mirror of
https://github.com/hanzoai/dev.git
synced 2026-10-25 03:32:32 +00:00
dev is ours: the Hanzo Cloud by default, a binary named dev, our namespaces
hanzoai/dev went public defaulting to someone else's cloud. Four things were
wrong, and all four now have a test that fails if they come back.
DEFAULT PROVIDER AND MODEL. built_in_model_providers() gains `hanzo` -- the
Hanzo Cloud at api.hanzo.ai/v1, Responses wire, bearer from HANZO_USER_KEY --
and it is the default. The default model is zen5-coder. This was not cosmetic:
the old default is not in the gateway catalog, so it 400s against our own
cloud. Measured, not assumed:
POST api.hanzo.ai/v1/responses model=gpt-5-codex -> 400 "model is not
available. Use GET /v1/models to list available models"
POST api.hanzo.ai/v1/responses model=zen5-coder -> 200, clean output
Our own CLI already documented the bug from the other side -- dev.rs says
"Name the model too -- codex's built-in default is not in the gateway catalog
and would 400" and passes `-c model=` on every launch to work around it. It
also asserts that on the native gateway NO model_provider override is needed,
which is only true once `hanzo` is the built-in default. It is now.
zen5-coder over enso because dev is a coding agent and because enso returns
system-prompt text on the Responses wire that dev actually speaks; zen5-coder
returns the answer. Model ids were read from GET /v1/models with a bearer, not
guessed. Other providers stay fully configurable -- this is the default, not
the only choice.
THE LOGIN COMMAND. Messages said `code login` / `codex login`. The real command
is `hanzo auth login` (`hanzo login` is not a command -- it is swallowed as an
AI task). It now lives once, as auth::LOGIN_COMMAND, so no message can drift
into naming a command that does not exist. References to this binary's own
subcommand became `dev login`.
A BINARY NAMED dev. HEAD built `code` and `codex` and nothing named `dev`, so a
release cut today would ship no `dev` -- and `hanzo code` launches a bare `dev`
off PATH, so every new install would break. `dev` is now the canonical bin,
declared first; `code` remains as an alias to the same program, never the
reverse. The CLI identifies as `dev` / "Hanzo Dev", not "OpenAI Codex".
OUR NAMESPACES. The pipeline published to @just-every/* on npm and pushed
formulae to just-every/homebrew-tap. Both of ours already exist and are already
populated at v0.6.91 -- @hanzo/dev plus all five @hanzo/dev-<target> packages,
and hanzoai/homebrew-tap's Formula/hanzo-dev.rb, which installs `dev` and
expects assets named dev-<triple>. Nothing here is squatted; this is our own
distribution, reclaimed. Assets are renamed to match the formula.
Two live supply-chain bugs fell out of that, both worse than a naming problem:
postinstall.js (runs on every npm install) and the tui auto-updater fetched
from a third party's releases -- the updater also told our users to
`npm install -g @just-every/code@latest`. Worse, a shadowed `isWSL` const put
the two fast paths in the temporal dead zone, so their catch blocks swallowed
the error and EVERY install fell through to that download: the five published
platform packages were never used, and the third-party fetch was not an edge
case but the only path that ran. Fixed and exercised in a sandbox across all
three paths. codex-cli/.pack/ -- a committed `npm pack` artifact CI
regenerates, and the reason the bad URL had two homes -- is now untracked.
DURABILITY, because upstream-merge.yml merges openai/codex every 30 minutes.
A one-time edit to a default is a suggestion that survives until the next
merge. Two mechanisms:
- .github/merge-policy.json prefer_ours_globs listed only codex-rs/** while
the workspace that SHIPS is code-rs/**, so it protected nothing that
ships. The identity-bearing code-rs files are now listed.
- code-rs/core/tests/hanzo_identity.rs asserts all four properties -- the
provider, a Zen default, a bin named dev, and no third-party publish
target. Upstream has no such file, so a merge cannot conflict with it or
carry the guard away with the thing it guards. Policy can be edited;
a red build cannot be ignored.
The gate is not decorative: it failed on the un-retargeted pipeline
("release.yml still references `@just-every/`") and went green only when the
pipeline was actually fixed.
Verified: cargo build --bin dev clean; hanzo_identity 4/4; code-core lib
332 passed, 1 failed -- git_info::test_collect_git_info_with_remote, which
fails identically on a clean origin/main checkout here because this machine
has a global [email protected]:.insteadof rewrite. Pre-existing and
environmental, not from this change.
$ dev exec "say OK" # fresh profile, no config
Hanzo Dev v0.0.0
model: zen5-coder
provider: hanzo
Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
+27
-27
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
// Unified entry point for the Code CLI (fork of OpenAI Codex).
|
||||
// Unified entry point for the Code CLI.
|
||||
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
@@ -14,7 +14,7 @@ const __dirname = path.dirname(__filename);
|
||||
const { platform, arch } = process;
|
||||
|
||||
// Important: Never delegate to another system's `code` binary (e.g., VS Code).
|
||||
// When users run via `npx @just-every/code`, we must always execute our
|
||||
// When users run via `npx @hanzo/dev`, we must always execute our
|
||||
// packaged native binary by absolute path to avoid PATH collisions.
|
||||
|
||||
const isWSL = () => {
|
||||
@@ -73,8 +73,8 @@ if (!targetTriple) {
|
||||
throw new Error(`Unsupported platform: ${platform} (${arch})`);
|
||||
}
|
||||
|
||||
// Prefer new 'code-*' binary names; fall back to legacy 'coder-*' if missing.
|
||||
let binaryPath = path.join(__dirname, "..", "bin", `code-${targetTriple}`);
|
||||
// Prefer new 'dev-*' binary names; fall back to legacy 'coder-*' if missing.
|
||||
let binaryPath = path.join(__dirname, "..", "bin", `dev-${targetTriple}`);
|
||||
let legacyBinaryPath = path.join(__dirname, "..", "bin", `coder-${targetTriple}`);
|
||||
|
||||
// --- Bootstrap helper (runs if the binary is missing, e.g. Bun blocked postinstall) ---
|
||||
@@ -120,7 +120,7 @@ const getCacheDir = (version) => {
|
||||
} else {
|
||||
base = process.env.XDG_CACHE_HOME || path.join(home, ".cache");
|
||||
}
|
||||
const dir = path.join(base, "just-every", "code", version);
|
||||
const dir = path.join(base, "hanzoai", "dev", version);
|
||||
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
||||
return dir;
|
||||
};
|
||||
@@ -129,7 +129,7 @@ const getCachedBinaryPath = (version) => {
|
||||
// targetTriple already includes the proper extension on Windows ("...msvc.exe").
|
||||
// Do not append another suffix; just use the exact targetTriple-derived name.
|
||||
const cacheDir = getCacheDir(version);
|
||||
return path.join(cacheDir, `code-${targetTriple}`);
|
||||
return path.join(cacheDir, `dev-${targetTriple}`);
|
||||
};
|
||||
|
||||
let lastBootstrapError = null;
|
||||
@@ -188,20 +188,20 @@ const tryBootstrapBinary = async () => {
|
||||
try {
|
||||
const req = (await import("module")).createRequire(import.meta.url);
|
||||
const name = (() => {
|
||||
if (platform === "win32") return "@just-every/code-win32-x64"; // may be unpublished; falls through
|
||||
if (platform === "win32") return "@hanzo/dev-win32-x64"; // may be unpublished; falls through
|
||||
const plt = nodePlatform();
|
||||
const cpu = nodeArch();
|
||||
if (plt === "darwin" && cpu === "arm64") return "@just-every/code-darwin-arm64";
|
||||
if (plt === "darwin" && cpu === "x64") return "@just-every/code-darwin-x64";
|
||||
if (plt === "linux" && cpu === "x64") return "@just-every/code-linux-x64-musl";
|
||||
if (plt === "linux" && cpu === "arm64") return "@just-every/code-linux-arm64-musl";
|
||||
if (plt === "darwin" && cpu === "arm64") return "@hanzo/dev-darwin-arm64";
|
||||
if (plt === "darwin" && cpu === "x64") return "@hanzo/dev-darwin-x64";
|
||||
if (plt === "linux" && cpu === "x64") return "@hanzo/dev-linux-x64-musl";
|
||||
if (plt === "linux" && cpu === "arm64") return "@hanzo/dev-linux-arm64-musl";
|
||||
return null;
|
||||
})();
|
||||
if (name) {
|
||||
try {
|
||||
const pkgJson = req.resolve(`${name}/package.json`);
|
||||
const pkgDir = path.dirname(pkgJson);
|
||||
const src = path.join(pkgDir, "bin", `code-${targetTriple}`);
|
||||
const src = path.join(pkgDir, "bin", `dev-${targetTriple}`);
|
||||
if (existsSync(src)) {
|
||||
// Always ensure cache has the binary; on Unix mirror into node_modules
|
||||
copyFileSync(src, cachePath);
|
||||
@@ -218,9 +218,9 @@ const tryBootstrapBinary = async () => {
|
||||
// 4) Download from GitHub release
|
||||
const isWin = platform === "win32";
|
||||
const archiveName = isWin
|
||||
? `code-${targetTriple}.zip`
|
||||
: (() => { try { execSync("zstd --version", { stdio: "ignore", shell: true }); return `code-${targetTriple}.zst`; } catch { return `code-${targetTriple}.tar.gz`; } })();
|
||||
const url = `https://github.com/just-every/code/releases/download/v${version}/${archiveName}`;
|
||||
? `dev-${targetTriple}.zip`
|
||||
: (() => { try { execSync("zstd --version", { stdio: "ignore", shell: true }); return `dev-${targetTriple}.zst`; } catch { return `dev-${targetTriple}.tar.gz`; } })();
|
||||
const url = `https://github.com/hanzoai/dev/releases/download/v${version}/${archiveName}`;
|
||||
const tmp = path.join(binDir, `.${archiveName}.part`);
|
||||
return httpsDownload(url, tmp)
|
||||
.then(() => {
|
||||
@@ -314,11 +314,11 @@ if (existsSync(binaryPath)) {
|
||||
console.error(`Bootstrap error: ${msg}`);
|
||||
}
|
||||
console.error(`Please try reinstalling the package:`);
|
||||
console.error(` npm uninstall -g @just-every/code`);
|
||||
console.error(` npm install -g @just-every/code`);
|
||||
console.error(` npm uninstall -g @hanzo/dev`);
|
||||
console.error(` npm install -g @hanzo/dev`);
|
||||
if (isWSL()) {
|
||||
console.error("Detected WSL. Install inside WSL (Ubuntu) separately:");
|
||||
console.error(" npx -y @just-every/code@latest (run inside WSL)");
|
||||
console.error(" npx -y @hanzo/dev@latest (run inside WSL)");
|
||||
console.error("If installed globally on Windows, those binaries are not usable from WSL.");
|
||||
}
|
||||
process.exit(1);
|
||||
@@ -332,15 +332,15 @@ if (!validation.ok) {
|
||||
console.error(`The native binary at ${binaryPath} appears invalid: ${validation.reason}`);
|
||||
console.error("This can happen if the download failed or was modified by antivirus/proxy.");
|
||||
console.error("Please try reinstalling:");
|
||||
console.error(" npm uninstall -g @just-every/code");
|
||||
console.error(" npm install -g @just-every/code");
|
||||
console.error(" npm uninstall -g @hanzo/dev");
|
||||
console.error(" npm install -g @hanzo/dev");
|
||||
if (platform === "win32") {
|
||||
console.error("If the issue persists, clear npm cache and disable antivirus temporarily:");
|
||||
console.error(" npm cache clean --force");
|
||||
}
|
||||
if (isWSL()) {
|
||||
console.error("Detected WSL. Ensure you install/run inside WSL, not Windows:");
|
||||
console.error(" npx -y @just-every/code@latest (inside WSL)");
|
||||
console.error(" npx -y @hanzo/dev@latest (inside WSL)");
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -359,15 +359,15 @@ try {
|
||||
cmd,
|
||||
], { encoding: "utf8" });
|
||||
const line = (out.stdout || "").split(/\r?\n/).map((s) => s.trim()).filter(Boolean)[0];
|
||||
if (line && !line.includes("@just-every/code")) {
|
||||
if (line && !line.includes("@hanzo/dev")) {
|
||||
otherCode = line;
|
||||
}
|
||||
} catch {}
|
||||
if (otherCode) {
|
||||
console.error(`@just-every/code: running bundled binary -> ${binaryPath}`);
|
||||
console.error(`@hanzo/dev: running bundled binary -> ${binaryPath}`);
|
||||
console.error(`Note: a different 'code' exists at ${otherCode}; not delegating.`);
|
||||
} else {
|
||||
console.error(`@just-every/code: running bundled binary -> ${binaryPath}`);
|
||||
console.error(`@hanzo/dev: running bundled binary -> ${binaryPath}`);
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
@@ -390,18 +390,18 @@ child.on("error", (err) => {
|
||||
if (code === 'EACCES') {
|
||||
console.error(`Permission denied: ${binaryPath}`);
|
||||
console.error(`Try running: chmod +x "${binaryPath}"`);
|
||||
console.error(`Or reinstall the package with: npm install -g @just-every/code`);
|
||||
console.error(`Or reinstall the package with: npm install -g @hanzo/dev`);
|
||||
} else if (code === 'EFTYPE' || code === 'ENOEXEC') {
|
||||
console.error(`Failed to execute native binary: ${binaryPath}`);
|
||||
console.error("The file may be corrupt or of the wrong type. Reinstall usually fixes this:");
|
||||
console.error(" npm uninstall -g @just-every/code && npm install -g @just-every/code");
|
||||
console.error(" npm uninstall -g @hanzo/dev && npm install -g @hanzo/dev");
|
||||
if (platform === 'win32') {
|
||||
console.error("On Windows, ensure the .exe downloaded correctly (proxy/AV can interfere).");
|
||||
console.error("Try clearing cache: npm cache clean --force");
|
||||
}
|
||||
if (isWSL()) {
|
||||
console.error("Detected WSL. Windows binaries cannot be executed from WSL.");
|
||||
console.error("Install inside WSL and run there: npx -y @just-every/code@latest");
|
||||
console.error("Install inside WSL and run there: npx -y @hanzo/dev@latest");
|
||||
}
|
||||
} else {
|
||||
console.error(err);
|
||||
|
||||
Reference in New Issue
Block a user