Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 4 additions & 15 deletions .github/workflows/publish-npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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"
Expand Down Expand Up @@ -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
42 changes: 42 additions & 0 deletions npm/hanzo/README.md
Original file line number Diff line number Diff line change
@@ -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 <id> # 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.
1 change: 1 addition & 0 deletions npm/hanzo/ai.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require("@hanzo/ai");
1 change: 1 addition & 0 deletions npm/hanzo/ai.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "@hanzo/ai";
1 change: 1 addition & 0 deletions npm/hanzo/ai.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "@hanzo/ai";
60 changes: 60 additions & 0 deletions npm/hanzo/bin/hanzo.js
Original file line number Diff line number Diff line change
@@ -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);
}
1 change: 1 addition & 0 deletions npm/hanzo/cloud.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require("hanzoai");
1 change: 1 addition & 0 deletions npm/hanzo/cloud.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "hanzoai";
1 change: 1 addition & 0 deletions npm/hanzo/cloud.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "hanzoai";
72 changes: 72 additions & 0 deletions npm/hanzo/package.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
1 change: 1 addition & 0 deletions npm/hanzo/ui.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require("@hanzo/ui");
1 change: 1 addition & 0 deletions npm/hanzo/ui.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "@hanzo/ui";
1 change: 1 addition & 0 deletions npm/hanzo/ui.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "@hanzo/ui";
Loading