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:
@@ -3,6 +3,14 @@ edition = "2024"
|
||||
name = "code-cli"
|
||||
version = { workspace = true }
|
||||
|
||||
# `dev` is the product. It is what `hanzo code` launches (by bare name, off
|
||||
# PATH) and what the installer contract promises.
|
||||
[[bin]]
|
||||
name = "dev"
|
||||
path = "src/main.rs"
|
||||
|
||||
# `code` stays as an alias so existing installs keep working. It is the same
|
||||
# program — an alias, never the canonical name.
|
||||
[[bin]]
|
||||
name = "code"
|
||||
path = "src/main.rs"
|
||||
|
||||
@@ -70,7 +70,7 @@ pub fn read_api_key_from_stdin() -> String {
|
||||
|
||||
if stdin.is_terminal() {
|
||||
eprintln!(
|
||||
"--with-api-key expects the API key on stdin. Try piping it, e.g. `printenv OPENAI_API_KEY | codex login --with-api-key`."
|
||||
"--with-api-key expects the API key on stdin. Try piping it, e.g. `printenv OPENAI_API_KEY | dev login --with-api-key`."
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
+18
-18
@@ -35,7 +35,7 @@ mod mcp_cmd;
|
||||
|
||||
use crate::mcp_cmd::McpCli;
|
||||
|
||||
const CLI_COMMAND_NAME: &str = "code";
|
||||
const CLI_COMMAND_NAME: &str = "dev";
|
||||
pub(crate) const CODEX_SECURE_MODE_ENV_VAR: &str = "CODEX_SECURE_MODE";
|
||||
|
||||
/// As early as possible in the process lifecycle, apply hardening measures
|
||||
@@ -57,20 +57,20 @@ fn pre_main_hardening() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Codex CLI
|
||||
/// Hanzo Dev — the Hanzo coding agent.
|
||||
///
|
||||
/// If no subcommand is specified, options will be forwarded to the interactive CLI.
|
||||
#[derive(Debug, Parser)]
|
||||
#[clap(
|
||||
author,
|
||||
name = "code",
|
||||
name = "dev",
|
||||
version = code_version::version(),
|
||||
// If a sub‑command is given, ignore requirements of the default args.
|
||||
subcommand_negates_reqs = true,
|
||||
// The executable is sometimes invoked via a platform‑specific name like
|
||||
// `codex-x86_64-unknown-linux-musl`, but the help output should always use
|
||||
// the generic `code` command name that users run.
|
||||
bin_name = "code"
|
||||
// `dev-x86_64-unknown-linux-musl`, or via the `code` alias, but the help
|
||||
// output should always name the product: `dev`.
|
||||
bin_name = "dev"
|
||||
)]
|
||||
struct MultitoolCli {
|
||||
#[clap(flatten)]
|
||||
@@ -85,7 +85,7 @@ struct MultitoolCli {
|
||||
|
||||
#[derive(Debug, clap::Subcommand)]
|
||||
enum Subcommand {
|
||||
/// Run Codex non-interactively.
|
||||
/// Run Hanzo Dev non-interactively.
|
||||
#[clap(visible_alias = "e")]
|
||||
Exec(ExecCli),
|
||||
|
||||
@@ -95,11 +95,11 @@ enum Subcommand {
|
||||
/// Remove stored authentication credentials.
|
||||
Logout(LogoutCommand),
|
||||
|
||||
/// [experimental] Run Codex as an MCP server and manage MCP servers.
|
||||
/// [experimental] Run Hanzo Dev as an MCP server and manage MCP servers.
|
||||
#[clap(visible_alias = "acp")]
|
||||
Mcp(McpCli),
|
||||
|
||||
/// [experimental] Run the Codex MCP server (stdio transport).
|
||||
/// [experimental] Run the Hanzo Dev MCP server (stdio transport).
|
||||
McpServer,
|
||||
|
||||
/// [experimental] Run the app server.
|
||||
@@ -115,7 +115,7 @@ enum Subcommand {
|
||||
#[clap(hide = false)]
|
||||
OrderReplay(OrderReplayArgs),
|
||||
|
||||
/// Apply the latest diff produced by Codex agent as a `git apply` to your local working tree.
|
||||
/// Apply the latest diff produced by the agent as a `git apply` to your local working tree.
|
||||
#[clap(visible_alias = "a")]
|
||||
Apply(ApplyCommand),
|
||||
|
||||
@@ -187,7 +187,7 @@ struct LoginCommand {
|
||||
|
||||
#[arg(
|
||||
long = "with-api-key",
|
||||
help = "Read the API key from stdin (e.g. `printenv OPENAI_API_KEY | codex login --with-api-key`)"
|
||||
help = "Read the API key from stdin (e.g. `printenv OPENAI_API_KEY | dev login --with-api-key`)"
|
||||
)]
|
||||
with_api_key: bool,
|
||||
|
||||
@@ -254,7 +254,7 @@ struct OrderReplayArgs {
|
||||
struct PreviewArgs {
|
||||
/// Slug identifier (e.g., faster-downloads)
|
||||
slug: String,
|
||||
/// Optional owner/repo to override (defaults to just-every/code or $GITHUB_REPOSITORY)
|
||||
/// Optional owner/repo to override (defaults to hanzoai/dev or $GITHUB_REPOSITORY)
|
||||
#[arg(long = "repo", value_name = "OWNER/REPO")]
|
||||
repo: Option<String>,
|
||||
/// Output directory where the binary will be extracted
|
||||
@@ -373,7 +373,7 @@ async fn cli_main(code_linux_sandbox_exe: Option<PathBuf>) -> anyhow::Result<()>
|
||||
.await;
|
||||
} else if login_cli.api_key.is_some() {
|
||||
eprintln!(
|
||||
"The --api-key flag is no longer supported. Pipe the key instead, e.g. `printenv OPENAI_API_KEY | codex login --with-api-key`."
|
||||
"The --api-key flag is no longer supported. Pipe the key instead, e.g. `printenv OPENAI_API_KEY | dev login --with-api-key`."
|
||||
);
|
||||
std::process::exit(1);
|
||||
} else if login_cli.with_api_key {
|
||||
@@ -583,7 +583,7 @@ fn resolve_resume_path(session_id: Option<&str>, last: bool) -> anyhow::Result<O
|
||||
}
|
||||
|
||||
let code_home = code_core::config::find_code_home()
|
||||
.context("failed to locate Codex home directory")?;
|
||||
.context("failed to locate the Hanzo Dev home directory")?;
|
||||
|
||||
// Build the async work once, then execute it either on the existing
|
||||
// runtime (from a helper thread) or a fresh current-thread runtime.
|
||||
@@ -728,7 +728,7 @@ async fn preview_main(args: PreviewArgs) -> anyhow::Result<()> {
|
||||
let repo = args
|
||||
.repo
|
||||
.or_else(|| env::var("GITHUB_REPOSITORY").ok())
|
||||
.unwrap_or_else(|| "just-every/code".to_string());
|
||||
.unwrap_or_else(|| "hanzoai/dev".to_string());
|
||||
let (owner, name) = repo
|
||||
.split_once('/')
|
||||
.map(|(o, n)| (o.to_string(), n.to_string()))
|
||||
@@ -996,7 +996,7 @@ async fn doctor_main() -> anyhow::Result<()> {
|
||||
let bun_coder = format!("{}/coder", bun_bin);
|
||||
if coder_paths.iter().any(|p| p == &bun_coder) {
|
||||
println!("\nBun shim detected for 'coder': {}", bun_coder);
|
||||
println!("Suggestion: remove old Bun global with: bun remove -g @just-every/code");
|
||||
println!("Suggestion: remove old Bun global with: bun remove -g @hanzo/dev");
|
||||
}
|
||||
let bun_code = format!("{}/code", bun_bin);
|
||||
if code_paths.iter().any(|p| p == &bun_code) {
|
||||
@@ -1027,8 +1027,8 @@ async fn doctor_main() -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
println!("\nIf versions differ, remove older installs and keep one package manager:");
|
||||
println!(" - Bun: bun remove -g @just-every/code");
|
||||
println!(" - npm/pnpm: npm uninstall -g @just-every/code");
|
||||
println!(" - Bun: bun remove -g @hanzo/dev");
|
||||
println!(" - npm/pnpm: npm uninstall -g @hanzo/dev");
|
||||
println!(" - Homebrew: brew uninstall code");
|
||||
println!(" - Prefer using 'coder' to avoid conflicts with VS Code's 'code'.");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user