From 3a7d8f947eff319b4d383ee0eca3f5fac58d7631 Mon Sep 17 00:00:00 2001 From: Zach Kelling Date: Tue, 3 Nov 2026 19:40:59 -0800 Subject: [PATCH] feat(npm): host the unified `hanzo` package beside @hanzo/cli github.com/hanzo-js/hanzo answers 404 to anonymous reads (and its own Actions never start), so no runner can fetch it. The `hanzo` front door now lives at npm/hanzo in this repo, its repository field points here, and publish-npm.yml publishes @hanzo/cli then hanzo in one run. --- .github/workflows/publish-npm.yml | 19 ++------ npm/hanzo/README.md | 42 ++++++++++++++++++ npm/hanzo/ai.cjs | 1 + npm/hanzo/ai.d.ts | 1 + npm/hanzo/ai.mjs | 1 + npm/hanzo/bin/hanzo.js | 60 ++++++++++++++++++++++++++ npm/hanzo/cloud.cjs | 1 + npm/hanzo/cloud.d.ts | 1 + npm/hanzo/cloud.mjs | 1 + npm/hanzo/package.json | 72 +++++++++++++++++++++++++++++++ npm/hanzo/ui.cjs | 1 + npm/hanzo/ui.d.ts | 1 + npm/hanzo/ui.mjs | 1 + 13 files changed, 187 insertions(+), 15 deletions(-) create mode 100644 npm/hanzo/README.md create mode 100644 npm/hanzo/ai.cjs create mode 100644 npm/hanzo/ai.d.ts create mode 100644 npm/hanzo/ai.mjs create mode 100755 npm/hanzo/bin/hanzo.js create mode 100644 npm/hanzo/cloud.cjs create mode 100644 npm/hanzo/cloud.d.ts create mode 100644 npm/hanzo/cloud.mjs create mode 100644 npm/hanzo/package.json create mode 100644 npm/hanzo/ui.cjs create mode 100644 npm/hanzo/ui.d.ts create mode 100644 npm/hanzo/ui.mjs diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml index 6eb3a7284..ed536c7d9 100644 --- a/.github/workflows/publish-npm.yml +++ b/.github/workflows/publish-npm.yml @@ -22,19 +22,8 @@ jobs: steps: - uses: actions/checkout@v4 - # The unified `hanzo` front door (hanzo-js/hanzo) ships beside @hanzo/cli: - # it depends on it, so the two go out together. Fetched as a public - # tarball: this run's token is scoped to hanzoai/cli and answers 404 for - # another org's repo (checkout), and a bare `git clone` on the runner - # prompts for credentials. codeload needs neither. - - name: The hanzo package source - run: | - set -euo pipefail - mkdir -p .hanzo-pkg - curl -fsSL https://codeload.github.com/hanzo-js/hanzo/tar.gz/refs/heads/main \ - | tar -xz -C .hanzo-pkg --strip-components=1 - node -p 'const p=require("./.hanzo-pkg/package.json"); p.name+"@"+p.version' - + # The unified `hanzo` front door lives beside @hanzo/cli in npm/hanzo and + # depends on it, so one run publishes both, @hanzo/cli first. - uses: actions/setup-node@v4 with: node-version: '22' @@ -54,7 +43,7 @@ jobs: echo "publishing @hanzo/cli@$NEW (current on npm: $OLD)" echo "cli=true" >> "$GITHUB_OUTPUT" fi - HNEW=$(node -p 'require(process.argv[1]).version' "$GITHUB_WORKSPACE/.hanzo-pkg/package.json") + HNEW=$(node -p 'require(process.argv[1]).version' "$GITHUB_WORKSPACE/npm/hanzo/package.json") HOLD=$(npm view hanzo version --registry https://registry.npmjs.org 2>/dev/null || echo 0.0.0) if curl -fsSL "https://registry.npmjs.org/hanzo/$HNEW" >/dev/null 2>&1; then echo "npm already serves hanzo@$HNEW" @@ -89,7 +78,7 @@ jobs: - name: Publish hanzo if: steps.ahead.outputs.hanzo == 'true' - working-directory: .hanzo-pkg + working-directory: npm/hanzo env: NPM_CONFIG_USERCONFIG: ${{ runner.temp }}/.npmrc run: npm publish --access public --ignore-scripts --no-provenance --registry https://registry.npmjs.org diff --git a/npm/hanzo/README.md b/npm/hanzo/README.md new file mode 100644 index 000000000..e10bc02ea --- /dev/null +++ b/npm/hanzo/README.md @@ -0,0 +1,42 @@ +# hanzo + +One lightweight install for the entire Hanzo stack — CLI, client SDK, `dev` coding agent, `mcp` server, `engine` (high-performance local inference), and `node`. + +```sh +npm install -g hanzo # unified CLI + lazy-loaded stack (cli, dev, mcp, engine, node) +npm install hanzo # TypeScript client SDK (hanzo, hanzo/ai, hanzo/ui) +``` + +```js +import { Configuration, AdminApi } from 'hanzo' // the Open AI Cloud +import { Agent } from 'hanzo/ai' // agents and inference +import { Button } from 'hanzo/ui' // the component library +``` + +```sh +hanzo --version +hanzo dev # coding agent + MCP (fetched lazily on first run) +hanzo mcp # Hanzo MCP server (fetched lazily on first run) +hanzo engine serve # high-perf local LLM inference (fetched lazily on first run) +hanzo node up # Hanzo L1 chain node (hanzod) +``` + +## What it is + +A lightweight front door that installs fast and fetches heavy native binaries lazily on first use: + +| surface | comes from | what it is | +|---|---|---| +| `hanzo` (import) | `hanzoai` | the full Open AI Cloud client, generated from `hanzo-inc/cloud` | +| `hanzo/ai` (import) | `@hanzo/ai` | the AI and agents library | +| `hanzo/ui` (import) | `@hanzo/ui` | the component library (optional peer dependency) | +| `hanzo` (command) | `@hanzo/cli` | the Rust CLI (`hanzoai/cli`), fetched for your platform on install | +| `hanzo dev` / `hanzo-dev` | `hanzoai/dev` | terminal AI coding agent, fetched lazily on first run | +| `hanzo mcp` / `hanzo-mcp` | `hanzoai/mcp` | Model Context Protocol server, fetched lazily on first run | +| `hanzo engine` / `hanzo-engine` | `hanzoai/engine` | high-performance local inference engine, fetched lazily on first run | +| `hanzo node` / `hanzo-node` | `hanzoai/node` | Hanzo network validator/chain node (`hanzod`), fetched lazily on first run | + +Nothing is vendored and nothing is reimplemented here — there is one downloader, +one generated client and one agents library, each in the package that owns it. +Depending on any of them directly still works; `hanzo` is the single name you +reach for so you never have to remember which `@hanzo/*` package to install. diff --git a/npm/hanzo/ai.cjs b/npm/hanzo/ai.cjs new file mode 100644 index 000000000..71a468488 --- /dev/null +++ b/npm/hanzo/ai.cjs @@ -0,0 +1 @@ +module.exports = require("@hanzo/ai"); diff --git a/npm/hanzo/ai.d.ts b/npm/hanzo/ai.d.ts new file mode 100644 index 000000000..cc1b3ab52 --- /dev/null +++ b/npm/hanzo/ai.d.ts @@ -0,0 +1 @@ +export * from "@hanzo/ai"; diff --git a/npm/hanzo/ai.mjs b/npm/hanzo/ai.mjs new file mode 100644 index 000000000..cc1b3ab52 --- /dev/null +++ b/npm/hanzo/ai.mjs @@ -0,0 +1 @@ +export * from "@hanzo/ai"; diff --git a/npm/hanzo/bin/hanzo.js b/npm/hanzo/bin/hanzo.js new file mode 100755 index 000000000..3a9dbbe83 --- /dev/null +++ b/npm/hanzo/bin/hanzo.js @@ -0,0 +1,60 @@ +#!/usr/bin/env node +// Hand the real binary the argv it was given and get out of the way. +// +// ONE HOP, NOT TWO. Loading `@hanzo/cli/bin/hanzo.js` in-process via `require` +// runs the launcher inside THIS Node process (no extra Node wrapper between the +// terminal and the native binary), while ensuring `cliDir/bin` is prepended to +// `PATH` (handling Windows `Path` case-insensitively) and companion binaries +// (`dev`, `hanzo-mcp`, `hanzo-engine`, `hanzod`, `hanzo-vm`, `zt`) install +// lazily on first use. +import { spawnSync } from "node:child_process"; +import { existsSync } from "node:fs"; +import { createRequire } from "node:module"; +import { delimiter, dirname, join } from "node:path"; + +const require = createRequire(import.meta.url); + +let cliDir; +try { + cliDir = dirname(require.resolve("@hanzo/cli/package.json")); +} catch { + console.error( + "hanzo: @hanzo/cli is not installed. It is a dependency of this package, so\n" + + "reinstall with `npm install -g hanzo` (or `npm install hanzo` in a project)." + ); + process.exit(1); +} + +const binDir = join(cliDir, "bin"); +const pathKey = + Object.keys(process.env).find((key) => key.toLowerCase() === "path") || "PATH"; +const currentPath = process.env[pathKey] || ""; +if (!currentPath.split(delimiter).includes(binDir)) { + process.env[pathKey] = binDir + (currentPath ? delimiter + currentPath : ""); +} + +const cliLauncher = join(binDir, "hanzo.js"); +if (existsSync(cliLauncher)) { + require(cliLauncher); +} else { + const bin = join(binDir, process.platform === "win32" ? "hanzo.exe" : "hanzo"); + if (!existsSync(bin)) { + const installScript = join(cliDir, "install.js"); + if (existsSync(installScript)) { + spawnSync(process.execPath, [installScript], { stdio: "inherit", env: process.env }); + } + } + if (!existsSync(bin)) { + console.error( + "hanzo: the binary is missing. @hanzo/cli's install script fetches it;\n" + + "if that was skipped (--ignore-scripts) run `node " + join(cliDir, "install.js") + "`." + ); + process.exit(1); + } + const { status, signal } = spawnSync(bin, process.argv.slice(2), { + stdio: "inherit", + env: process.env, + }); + if (signal) process.kill(process.pid, signal); + process.exit(status ?? 1); +} diff --git a/npm/hanzo/cloud.cjs b/npm/hanzo/cloud.cjs new file mode 100644 index 000000000..b65ccd09c --- /dev/null +++ b/npm/hanzo/cloud.cjs @@ -0,0 +1 @@ +module.exports = require("hanzoai"); diff --git a/npm/hanzo/cloud.d.ts b/npm/hanzo/cloud.d.ts new file mode 100644 index 000000000..f814b650a --- /dev/null +++ b/npm/hanzo/cloud.d.ts @@ -0,0 +1 @@ +export * from "hanzoai"; diff --git a/npm/hanzo/cloud.mjs b/npm/hanzo/cloud.mjs new file mode 100644 index 000000000..f814b650a --- /dev/null +++ b/npm/hanzo/cloud.mjs @@ -0,0 +1 @@ +export * from "hanzoai"; diff --git a/npm/hanzo/package.json b/npm/hanzo/package.json new file mode 100644 index 000000000..c8407a122 --- /dev/null +++ b/npm/hanzo/package.json @@ -0,0 +1,72 @@ +{ + "name": "hanzo", + "version": "8.5.909", + "description": "Hanzo — unified CLI, coding agent (dev), MCP server, local inference engine, node, and TypeScript client in one lightweight install.", + "license": "BSD-3-Clause", + "repository": { + "type": "git", + "url": "git+https://github.com/hanzoai/cli.git", + "directory": "npm/hanzo" + }, + "homepage": "https://hanzo.ai", + "bin": { + "hanzo": "bin/hanzo.js", + "hanzo-dev": "bin/hanzo.js", + "hanzo-mcp": "bin/hanzo.js", + "hanzo-engine": "bin/hanzo.js", + "hanzo-node": "bin/hanzo.js" + }, + "type": "module", + "types": "./cloud.d.ts", + "exports": { + ".": { + "types": "./cloud.d.ts", + "import": "./cloud.mjs", + "require": "./cloud.cjs" + }, + "./ai": { + "types": "./ai.d.ts", + "import": "./ai.mjs", + "require": "./ai.cjs" + }, + "./ui": { + "types": "./ui.d.ts", + "import": "./ui.mjs", + "require": "./ui.cjs" + }, + "./package.json": "./package.json" + }, + "files": [ + "bin", + "cloud.mjs", + "cloud.cjs", + "cloud.d.ts", + "ai.mjs", + "ai.cjs", + "ai.d.ts", + "ui.mjs", + "ui.cjs", + "ui.d.ts", + "README.md" + ], + "dependencies": { + "@hanzo/ai": "^0.7.0", + "@hanzo/cli": "^8.5.910", + "hanzoai": "^8.5.914" + }, + "peerDependencies": { + "@hanzo/ui": ">=8.0.0", + "react": ">=18.0.0" + }, + "peerDependenciesMeta": { + "@hanzo/ui": { + "optional": true + }, + "react": { + "optional": true + } + }, + "engines": { + "node": ">=18" + } +} diff --git a/npm/hanzo/ui.cjs b/npm/hanzo/ui.cjs new file mode 100644 index 000000000..b53d5dcfc --- /dev/null +++ b/npm/hanzo/ui.cjs @@ -0,0 +1 @@ +module.exports = require("@hanzo/ui"); diff --git a/npm/hanzo/ui.d.ts b/npm/hanzo/ui.d.ts new file mode 100644 index 000000000..7deb57543 --- /dev/null +++ b/npm/hanzo/ui.d.ts @@ -0,0 +1 @@ +export * from "@hanzo/ui"; diff --git a/npm/hanzo/ui.mjs b/npm/hanzo/ui.mjs new file mode 100644 index 000000000..7deb57543 --- /dev/null +++ b/npm/hanzo/ui.mjs @@ -0,0 +1 @@ +export * from "@hanzo/ui";