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:
2026-08-01 14:05:00 -07:00
co-authored by Hanzo Dev
parent d3a9c6aa46
commit d3d1dfa1d3
49 changed files with 660 additions and 2180 deletions
-297
View File
@@ -1,297 +0,0 @@
# CODE
&ensp;
<p align="center">
<img src="docs/logo.png" alt="Code Logo" width="400">
</p>
&ensp;
**Code** is a fast, local coding agent for your terminal. It's a community-driven fork of `openai/codex` focused on real developer ergonomics: Browser integration, multi-agents, theming, and reasoning control — all while staying compatible with upstream.
&ensp;
## Why Code
- 🌐 **Browser Integration** - CDP support, headless browsing, screenshots
- 📝 **Diff Viewer** - Side-by-side diffs with syntax highlighting
- 🤖 **Multi-Agent Commands** - /plan, /solve, /code with agent panels
- 🎨 **Theme System** - /themes with live preview and accessibility
- 🧠 **Reasoning Control** - /reasoning for dynamic effort adjustment
- 🔌 **MCP support** – Extend with filesystem, DBs, APIs, or your own tools.
- 🔒 **Safety modes** – Read-only, approvals, and workspace sandboxing.
- 🔁 **Backwards compatible** – Reads both `~/.code/*` (primary) and legacy `~/.codex/*`; writes only to `~/.code/*`
&ensp;
| <img src="docs/screenshots/simple.png" alt="Simple interface" width="100%"><br>Simple interface | <img src="docs/screenshots/diff.png" alt="Unified diff viewer" width="100%"><br>Unified diffs |
|:--:|:--:|
| <br><img src="docs/screenshots/browser.png" alt="Browser control" width="100%"><br>Browser control | <br><img src="docs/screenshots/agents.png" alt="Assist with Claude & Gemini" width="100%"><br>Assist with Claude & Gemini |
|:--:|:--:|
&ensp;
## Quickstart
### Run
```bash
npx -y @just-every/code
```
### Install & Run
```bash
npm install -g @just-every/code
code // or `coder` if you're using VS Code
```
Note: If another tool already provides a `code` command (e.g. VS Code), our CLI is also installed as `coder`. Use `coder` to avoid conflicts.
**Authenticate** (one of the following):
- **Sign in with ChatGPT** (Plus/Pro/Team; uses models available to your plan)
- Run `code` and pick "Sign in with ChatGPT"
- Stores creds locally at `~/.code/auth.json` (still reads legacy `~/.codex/auth.json` if present)
- **API key** (usage-based)
- Set `export OPENAI_API_KEY=xyz` and run `code`
### Install Claude & Gemini (optional)
Code supports orchestrating other AI CLI tools. Install these and config to use alongside Code.
```bash
npm install -g @anthropic-ai/claude-code @google/gemini-cli && claude "Just checking you're working! Let me know how I can exit." && gemini -i "Just checking you're working! Let me know how I can exit."
```
&ensp;
## Commands
### Browser
```bash
# Connect code to external Chrome browser (running CDP)
/chrome # Connect with auto-detect port
/chrome 9222 # Connect to specific port
# Switch to internal browser mode
/browser # Use internal headless browser
/browser https://example.com # Open URL in internal browser
```
### Agents
```bash
# Plan code changes (Claude, Gemini and GPT-5 consensus)
# All agents review task and create a consolidated plan
/plan "Stop the AI from ordering pizza at 3AM"
# Solve complex problems (Claude, Gemini and GPT-5 race)
# Fastest preferred (see https://arxiv.org/abs/2505.17813)
/solve "Why does deleting one user drop the whole database?"
# Write code! (Claude, Gemini and GPT-5 consensus)
# Creates multiple worktrees then implements the optimal solution
/code "Show dark mode when I feel cranky"
```
### General
```bash
# Try a new theme!
/themes
# Change reasoning level
/reasoning low|medium|high
# Switch models or effort presets
/model
# Start new conversation
/new
```
## CLI reference
```shell
code [options] [prompt]
Options:
--model <name> Override the model (gpt-5, claude-opus, etc.)
--read-only Prevent file modifications
--no-approval Skip approval prompts (use with caution)
--config <key=val> Override config values
--oss Use local open source models
--sandbox <mode> Set sandbox level (read-only, workspace-write, etc.)
--help Show help information
--debug Log API requests and responses to file
--version Show version number
```
&ensp;
## Memory & project docs
Code can remember context across sessions:
1. **Create an `AGENTS.md` or `CLAUDE.md` file** in your project root:
```markdown
# Project Context
This is a React TypeScript application with:
- Authentication via JWT
- PostgreSQL database
- Express.js backend
## Key files:
- `/src/auth/` - Authentication logic
- `/src/api/` - API client code
- `/server/` - Backend services
```
2. **Session memory**: Code maintains conversation history
3. **Codebase analysis**: Automatically understands project structure
&ensp;
## Non-interactive / CI mode
For automation and CI/CD:
```shell
# Run a specific task
code --no-approval "run tests and fix any failures"
# Generate reports
code --read-only "analyze code quality and generate report"
# Batch processing
code --config output_format=json "list all TODO comments"
```
&ensp;
## Model Context Protocol (MCP)
Code supports MCP for extended capabilities:
- **File operations**: Advanced file system access
- **Database connections**: Query and modify databases
- **API integrations**: Connect to external services
- **Custom tools**: Build your own extensions
Configure MCP in `~/.code/config.toml` (legacy `~/.codex/config.toml` is still read if present). Define each server under a named table like `[mcp_servers.<name>]` (this maps to the JSON `mcpServers` object used by other clients):
```toml
[mcp_servers.filesystem]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/project"]
```
&ensp;
## Configuration
Main config file: `~/.code/config.toml`
> [!NOTE]
> Code reads from both `~/.code/` and `~/.codex/` for backwards compatibility, but it only writes updates to `~/.code/`. If you switch back to Codex and it fails to start, remove `~/.codex/config.toml`. If Code appears to miss settings after upgrading, copy your legacy `~/.codex/config.toml` into `~/.code/`.
```toml
# Model settings
model = "gpt-5"
model_provider = "openai"
# Behavior
approval_policy = "on-request" # untrusted | on-failure | on-request | never
model_reasoning_effort = "medium" # low | medium | high
sandbox_mode = "workspace-write"
# UI preferences see THEME_CONFIG.md
[tui.theme]
name = "light-photon"
# Add config for specific models
[profiles.gpt-5]
model = "gpt-5"
model_provider = "openai"
approval_policy = "never"
model_reasoning_effort = "high"
model_reasoning_summary = "detailed"
```
### Environment variables
- `CODEX_HOME`: Override config directory location
- `OPENAI_API_KEY`: Use API key instead of ChatGPT auth
- `OPENAI_BASE_URL`: Use alternative API endpoints
- `OPENAI_WIRE_API`: Force the built-in OpenAI provider to use `chat` or `responses` wiring
&ensp;
## FAQ
**How is this different from the original?**
> This fork adds browser integration, multi-agent commands (`/plan`, `/solve`, `/code`), theme system, and enhanced reasoning controls while maintaining full compatibility.
**Can I use my existing Codex configuration?**
> Yes. Code reads from both `~/.code/` (primary) and legacy `~/.codex/` directories. We only write to `~/.code/`, so Codex will keep running if you switch back; copy or remove legacy files if you notice conflicts.
**Does this work with ChatGPT Plus?**
> Absolutely. Use the same "Sign in with ChatGPT" flow as the original.
**Is my data secure?**
> Yes. Authentication stays on your machine, and we don't proxy your credentials or conversations.
&ensp;
## Contributing
We welcome contributions! This fork maintains compatibility with upstream while adding community-requested features.
### Development workflow
```bash
# Clone and setup
git clone https://github.com/just-every/code.git
cd code
npm install
# Build (use fast build for development)
./build-fast.sh
# Run locally
./codex-rs/target/dev-fast/code
```
### Opening a pull request
1. Fork the repository
2. Create a feature branch: `git checkout -b feature/amazing-feature`
3. Make your changes
4. Run tests: `cargo test`
5. Build successfully: `./build-fast.sh`
6. Submit a pull request
&ensp;
## Legal & Use
### License & attribution
- This project is a community fork of `openai/codex` under **Apache-2.0**. We preserve upstream LICENSE and NOTICE files.
- **Code** is **not** affiliated with, sponsored by, or endorsed by OpenAI.
### Your responsibilities
Using OpenAI, Anthropic or Google services through Code means you agree to **their Terms and policies**. In particular:
- **Don't** programmatically scrape/extract content outside intended flows.
- **Don't** bypass or interfere with rate limits, quotas, or safety mitigations.
- Use your **own** account; don't share or rotate accounts to evade limits.
- If you configure other model providers, you're responsible for their terms.
### Privacy
- Your auth file lives at `~/.code/auth.json` (legacy `~/.codex/auth.json` is still read).
- Inputs/outputs you send to AI providers are handled under their Terms and Privacy Policy; consult those documents (and any org-level data-sharing settings).
### Subject to change
AI providers can change eligibility, limits, models, or authentication flows. Code supports **both** ChatGPT sign-in and API-key modes so you can pick what fits (local/hobby vs CI/automation).
&ensp;
## License
Apache 2.0 - See [LICENSE](LICENSE) file for details.
This project is a community fork of the original Codex CLI. We maintain compatibility while adding enhanced features requested by the developer community.
&ensp;
---
**Need help?** Open an issue on [GitHub](https://github.com/just-every/code/issues) or check our documentation.
-452
View File
@@ -1,452 +0,0 @@
#!/usr/bin/env node
// Unified entry point for the Code CLI (fork of OpenAI Codex).
import path from "path";
import { fileURLToPath } from "url";
import { platform as nodePlatform, arch as nodeArch } from "os";
import { execSync } from "child_process";
import { get as httpsGet } from "https";
// __dirname equivalent in ESM
const __filename = fileURLToPath(import.meta.url);
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
// packaged native binary by absolute path to avoid PATH collisions.
const isWSL = () => {
if (platform !== "linux") return false;
try {
const txt = readFileSync("/proc/version", "utf8").toLowerCase();
return txt.includes("microsoft");
} catch {
return false;
}
};
let targetTriple = null;
switch (platform) {
case "linux":
case "android":
switch (arch) {
case "x64":
targetTriple = "x86_64-unknown-linux-musl";
break;
case "arm64":
targetTriple = "aarch64-unknown-linux-musl";
break;
default:
break;
}
break;
case "darwin":
switch (arch) {
case "x64":
targetTriple = "x86_64-apple-darwin";
break;
case "arm64":
targetTriple = "aarch64-apple-darwin";
break;
default:
break;
}
break;
case "win32":
switch (arch) {
case "x64":
targetTriple = "x86_64-pc-windows-msvc.exe";
break;
case "arm64":
// We do not build this today, fall through...
default:
break;
}
break;
default:
break;
}
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}`);
let legacyBinaryPath = path.join(__dirname, "..", "bin", `coder-${targetTriple}`);
// --- Bootstrap helper (runs if the binary is missing, e.g. Bun blocked postinstall) ---
import { existsSync, chmodSync, statSync, openSync, readSync, closeSync, mkdirSync, copyFileSync, readFileSync, unlinkSync, createWriteStream } from "fs";
const validateBinary = (p) => {
try {
const st = statSync(p);
if (!st.isFile() || st.size === 0) {
return { ok: false, reason: "empty or not a regular file" };
}
const fd = openSync(p, "r");
try {
const buf = Buffer.alloc(4);
const n = readSync(fd, buf, 0, 4, 0);
if (n < 2) return { ok: false, reason: "too short" };
if (platform === "win32") {
if (!(buf[0] === 0x4d && buf[1] === 0x5a)) return { ok: false, reason: "invalid PE header (missing MZ)" };
} else if (platform === "linux" || platform === "android") {
if (!(buf[0] === 0x7f && buf[1] === 0x45 && buf[2] === 0x4c && buf[3] === 0x46)) return { ok: false, reason: "invalid ELF header" };
} else if (platform === "darwin") {
const isMachO = (buf[0] === 0xcf && buf[1] === 0xfa && buf[2] === 0xed && buf[3] === 0xfe) ||
(buf[0] === 0xca && buf[1] === 0xfe && buf[2] === 0xba && buf[3] === 0xbe);
if (!isMachO) return { ok: false, reason: "invalid Mach-O header" };
}
} finally {
closeSync(fd);
}
return { ok: true };
} catch (e) {
return { ok: false, reason: e.message };
}
};
const getCacheDir = (version) => {
const plt = nodePlatform();
const home = process.env.HOME || process.env.USERPROFILE || "";
let base = "";
if (plt === "win32") {
base = process.env.LOCALAPPDATA || path.join(home, "AppData", "Local");
} else if (plt === "darwin") {
base = path.join(home, "Library", "Caches");
} else {
base = process.env.XDG_CACHE_HOME || path.join(home, ".cache");
}
const dir = path.join(base, "just-every", "code", version);
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
return dir;
};
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}`);
};
let lastBootstrapError = null;
const httpsDownload = (url, dest) => new Promise((resolve, reject) => {
const req = httpsGet(url, (res) => {
const status = res.statusCode || 0;
if (status >= 300 && status < 400 && res.headers.location) {
// follow one redirect recursively
return resolve(httpsDownload(res.headers.location, dest));
}
if (status !== 200) {
return reject(new Error(`HTTP ${status}`));
}
const out = createWriteStream(dest);
res.pipe(out);
out.on("finish", () => out.close(resolve));
out.on("error", (e) => {
try { unlinkSync(dest); } catch {}
reject(e);
});
});
req.on("error", (e) => {
try { unlinkSync(dest); } catch {}
reject(e);
});
req.setTimeout(120000, () => {
req.destroy(new Error("download timed out"));
});
});
const tryBootstrapBinary = async () => {
try {
// 1) Read our published version
const pkg = JSON.parse(readFileSync(path.join(__dirname, "..", "package.json"), "utf8"));
const version = pkg.version;
const binDir = path.join(__dirname, "..", "bin");
if (!existsSync(binDir)) mkdirSync(binDir, { recursive: true });
// 2) Fast path: user cache
const cachePath = getCachedBinaryPath(version);
if (existsSync(cachePath)) {
const v = validateBinary(cachePath);
if (v.ok) {
// Prefer running directly from cache; mirror into node_modules on Unix
if (platform !== "win32") {
copyFileSync(cachePath, binaryPath);
try { chmodSync(binaryPath, 0o755); } catch {}
}
return true;
}
}
// 3) Try platform package (if present)
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
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";
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}`);
if (existsSync(src)) {
// Always ensure cache has the binary; on Unix mirror into node_modules
copyFileSync(src, cachePath);
if (platform !== "win32") {
copyFileSync(cachePath, binaryPath);
try { chmodSync(binaryPath, 0o755); } catch {}
}
return true;
}
} catch { /* ignore and fall back */ }
}
} catch { /* ignore */ }
// 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}`;
const tmp = path.join(binDir, `.${archiveName}.part`);
return httpsDownload(url, tmp)
.then(() => {
if (isWin) {
// Extract zip with robust fallbacks and use a safe temp dir, then move to cache
try {
const sysRoot = process.env.SystemRoot || process.env.windir || 'C:\\\Windows';
const psFull = path.join(sysRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe');
const unzipDest = getCacheDir(version); // extract directly to cache location
const psCmd = `Expand-Archive -Path '${tmp}' -DestinationPath '${unzipDest}' -Force`;
let ok = false;
try { execSync(`"${psFull}" -NoProfile -NonInteractive -Command "${psCmd}"`, { stdio: 'ignore' }); ok = true; } catch {}
if (!ok) { try { execSync(`powershell -NoProfile -NonInteractive -Command "${psCmd}"`, { stdio: 'ignore' }); ok = true; } catch {} }
if (!ok) { try { execSync(`pwsh -NoProfile -NonInteractive -Command "${psCmd}"`, { stdio: 'ignore' }); ok = true; } catch {} }
if (!ok) { execSync(`tar -xf "${tmp}" -C "${unzipDest}"`, { stdio: 'ignore', shell: true }); }
} catch (e) {
throw new Error(`failed to unzip: ${e.message}`);
} finally { try { unlinkSync(tmp); } catch {} }
} else {
if (archiveName.endsWith(".zst")) {
try { execSync(`zstd -d '${tmp}' -o '${binaryPath}'`, { stdio: 'ignore', shell: true }); }
catch (e) { try { unlinkSync(tmp); } catch {}; throw new Error(`failed to decompress zst: ${e.message}`); }
try { unlinkSync(tmp); } catch {}
} else {
try { execSync(`tar -xzf '${tmp}' -C '${binDir}'`, { stdio: 'ignore', shell: true }); }
catch (e) { try { unlinkSync(tmp); } catch {}; throw new Error(`failed to extract tar.gz: ${e.message}`); }
try { unlinkSync(tmp); } catch {}
}
}
// On Windows, the file was extracted directly into the cache dir
if (platform === "win32") {
// Ensure the expected filename exists in cache; Expand-Archive extracts exact name
// No action required here; validation occurs below against cachePath
} else {
try { copyFileSync(binaryPath, cachePath); } catch {}
}
const v = validateBinary(platform === "win32" ? cachePath : binaryPath);
if (!v.ok) throw new Error(`invalid binary (${v.reason})`);
if (platform !== "win32") try { chmodSync(binaryPath, 0o755); } catch {}
return true;
})
.catch((e) => { lastBootstrapError = e; return false; });
} catch {
return false;
}
};
// If missing, attempt to bootstrap into place (helps when Bun blocks postinstall)
if (!existsSync(binaryPath) && !existsSync(legacyBinaryPath)) {
const ok = await tryBootstrapBinary();
if (!ok) {
// retry legacy name in case archive provided coder-*
if (existsSync(legacyBinaryPath) && !existsSync(binaryPath)) {
binaryPath = legacyBinaryPath;
}
}
}
// Prefer cached binary when available
try {
const pkg = JSON.parse(readFileSync(path.join(__dirname, "..", "package.json"), "utf8"));
const version = pkg.version;
const cached = getCachedBinaryPath(version);
const v = existsSync(cached) ? validateBinary(cached) : { ok: false };
if (v.ok) {
binaryPath = cached;
} else if (!existsSync(binaryPath) && existsSync(legacyBinaryPath)) {
binaryPath = legacyBinaryPath;
}
} catch {
// ignore
}
// Check if binary exists and try to fix permissions if needed
// fs imports are above; keep for readability if tree-shaken by bundlers
import { spawnSync } from "child_process";
if (existsSync(binaryPath)) {
try {
// Ensure binary is executable on Unix-like systems
if (platform !== "win32") {
chmodSync(binaryPath, 0o755);
}
} catch (e) {
// Ignore permission errors, will be caught below if it's a real problem
}
} else {
console.error(`Binary not found: ${binaryPath}`);
if (lastBootstrapError) {
const msg = (lastBootstrapError && (lastBootstrapError.message || String(lastBootstrapError))) || 'unknown bootstrap error';
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`);
if (isWSL()) {
console.error("Detected WSL. Install inside WSL (Ubuntu) separately:");
console.error(" npx -y @just-every/code@latest (run inside WSL)");
console.error("If installed globally on Windows, those binaries are not usable from WSL.");
}
process.exit(1);
}
// Lightweight header validation to provide clearer errors before spawn
// Reuse the validateBinary helper defined above in the bootstrap section.
const validation = validateBinary(binaryPath);
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");
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)");
}
process.exit(1);
}
// If running under npx/npm, emit a concise notice about which binary path is used
try {
const ua = process.env.npm_config_user_agent || "";
const isNpx = ua.includes("npx");
if (isNpx && process.stderr && process.stderr.isTTY) {
// Best-effort discovery of another 'code' on PATH for user clarity
let otherCode = "";
try {
const cmd = process.platform === "win32" ? "where code" : "command -v code || which code || true";
const out = spawnSync(process.platform === "win32" ? "cmd" : "bash", [
process.platform === "win32" ? "/c" : "-lc",
cmd,
], { encoding: "utf8" });
const line = (out.stdout || "").split(/\r?\n/).map((s) => s.trim()).filter(Boolean)[0];
if (line && !line.includes("@just-every/code")) {
otherCode = line;
}
} catch {}
if (otherCode) {
console.error(`@just-every/code: 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}`);
}
}
} catch {}
// Use an asynchronous spawn instead of spawnSync so that Node is able to
// respond to signals (e.g. Ctrl-C / SIGINT) while the native binary is
// executing. This allows us to forward those signals to the child process
// and guarantees that when either the child terminates or the parent
// receives a fatal signal, both processes exit in a predictable manner.
const { spawn } = await import("child_process");
const child = spawn(binaryPath, process.argv.slice(2), {
stdio: "inherit",
env: { ...process.env, CODER_MANAGED_BY_NPM: "1", CODEX_MANAGED_BY_NPM: "1" },
});
child.on("error", (err) => {
// Typically triggered when the binary is missing or not executable.
const code = err && err.code;
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`);
} 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");
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");
}
} else {
console.error(err);
}
process.exit(1);
});
// Forward common termination signals to the child so that it shuts down
// gracefully. In the handler we temporarily disable the default behavior of
// exiting immediately; once the child has been signaled we simply wait for
// its exit event which will in turn terminate the parent (see below).
const forwardSignal = (signal) => {
if (child.killed) {
return;
}
try {
child.kill(signal);
} catch {
/* ignore */
}
};
["SIGINT", "SIGTERM", "SIGHUP"].forEach((sig) => {
process.on(sig, () => forwardSignal(sig));
});
// When the child exits, mirror its termination reason in the parent so that
// shell scripts and other tooling observe the correct exit status.
// Wrap the lifetime of the child process in a Promise so that we can await
// its termination in a structured way. The Promise resolves with an object
// describing how the child exited: either via exit code or due to a signal.
const childResult = await new Promise((resolve) => {
child.on("exit", (code, signal) => {
if (signal) {
resolve({ type: "signal", signal });
} else {
resolve({ type: "code", exitCode: code ?? 1 });
}
});
});
if (childResult.type === "signal") {
// Re-emit the same signal so that the parent terminates with the expected
// semantics (this also sets the correct exit code of 128 + n).
process.kill(process.pid, childResult.signal);
} else {
process.exit(childResult.exitCode);
}
-144
View File
@@ -1,144 +0,0 @@
#!/usr/bin/env node
// Unified entry point for the Codex CLI.
import { existsSync } from "fs";
import path from "path";
import { fileURLToPath } from "url";
// __dirname equivalent in ESM
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const { platform, arch } = process;
let targetTriple = null;
switch (platform) {
case "linux":
case "android":
switch (arch) {
case "x64":
targetTriple = "x86_64-unknown-linux-musl";
break;
case "arm64":
targetTriple = "aarch64-unknown-linux-musl";
break;
default:
break;
}
break;
case "darwin":
switch (arch) {
case "x64":
targetTriple = "x86_64-apple-darwin";
break;
case "arm64":
targetTriple = "aarch64-apple-darwin";
break;
default:
break;
}
break;
case "win32":
switch (arch) {
case "x64":
targetTriple = "x86_64-pc-windows-msvc";
break;
case "arm64":
targetTriple = "aarch64-pc-windows-msvc";
break;
default:
break;
}
break;
default:
break;
}
if (!targetTriple) {
throw new Error(`Unsupported platform: ${platform} (${arch})`);
}
const vendorRoot = path.join(__dirname, "..", "vendor");
const archRoot = path.join(vendorRoot, targetTriple);
const codexBinaryName = process.platform === "win32" ? "codex.exe" : "codex";
const binaryPath = path.join(archRoot, "codex", codexBinaryName);
// Use an asynchronous spawn instead of spawnSync so that Node is able to
// respond to signals (e.g. Ctrl-C / SIGINT) while the native binary is
// executing. This allows us to forward those signals to the child process
// and guarantees that when either the child terminates or the parent
// receives a fatal signal, both processes exit in a predictable manner.
const { spawn } = await import("child_process");
function getUpdatedPath(newDirs) {
const pathSep = process.platform === "win32" ? ";" : ":";
const existingPath = process.env.PATH || "";
const updatedPath = [
...newDirs,
...existingPath.split(pathSep).filter(Boolean),
].join(pathSep);
return updatedPath;
}
const additionalDirs = [];
const pathDir = path.join(archRoot, "path");
if (existsSync(pathDir)) {
additionalDirs.push(pathDir);
}
const updatedPath = getUpdatedPath(additionalDirs);
const child = spawn(binaryPath, process.argv.slice(2), {
stdio: "inherit",
env: { ...process.env, PATH: updatedPath, CODEX_MANAGED_BY_NPM: "1" },
});
child.on("error", (err) => {
// Typically triggered when the binary is missing or not executable.
// Re-throwing here will terminate the parent with a non-zero exit code
// while still printing a helpful stack trace.
// eslint-disable-next-line no-console
console.error(err);
process.exit(1);
});
// Forward common termination signals to the child so that it shuts down
// gracefully. In the handler we temporarily disable the default behavior of
// exiting immediately; once the child has been signaled we simply wait for
// its exit event which will in turn terminate the parent (see below).
const forwardSignal = (signal) => {
if (child.killed) {
return;
}
try {
child.kill(signal);
} catch {
/* ignore */
}
};
["SIGINT", "SIGTERM", "SIGHUP"].forEach((sig) => {
process.on(sig, () => forwardSignal(sig));
});
// When the child exits, mirror its termination reason in the parent so that
// shell scripts and other tooling observe the correct exit status.
// Wrap the lifetime of the child process in a Promise so that we can await
// its termination in a structured way. The Promise resolves with an object
// describing how the child exited: either via exit code or due to a signal.
const childResult = await new Promise((resolve) => {
child.on("exit", (code, signal) => {
if (signal) {
resolve({ type: "signal", signal });
} else {
resolve({ type: "code", exitCode: code ?? 1 });
}
});
});
if (childResult.type === "signal") {
// Re-emit the same signal so that the parent terminates with the expected
// semantics (this also sets the correct exit code of 128 + n).
process.kill(process.pid, childResult.signal);
} else {
process.exit(childResult.exitCode);
}
-44
View File
@@ -1,44 +0,0 @@
{
"name": "@just-every/code",
"version": "0.2.175",
"license": "Apache-2.0",
"description": "Lightweight coding agent that runs in your terminal - fork of OpenAI Codex",
"bin": {
"coder": "bin/coder.js"
},
"type": "module",
"engines": {
"node": ">=20"
},
"files": [
"bin/coder.js",
"bin/codex.js",
"postinstall.js",
"scripts/preinstall.js",
"scripts/windows-cleanup.ps1",
"dist"
],
"scripts": {
"preinstall": "node scripts/preinstall.js",
"postinstall": "node postinstall.js",
"prepublishOnly": "node -e \"const fs=require('fs'),path=require('path'); const repoGit=path.join(__dirname,'..','.git'); const inCi=process.env.GITHUB_ACTIONS==='true'||process.env.CI==='true'; if(fs.existsSync(repoGit) && !inCi){ console.error('Refusing to publish from codex-cli. Publishing happens via release.yml.'); process.exit(1);} else { console.log(inCi ? 'CI publish detected.' : 'Publishing staged package...'); }\""
},
"repository": {
"type": "git",
"url": "git+https://github.com/just-every/code.git"
},
"publishConfig": {
"access": "public"
},
"dependencies": {},
"devDependencies": {
"prettier": "^3.3.3"
},
"optionalDependencies": {
"@just-every/code-darwin-arm64": "0.2.175",
"@just-every/code-darwin-x64": "0.2.175",
"@just-every/code-linux-x64-musl": "0.2.175",
"@just-every/code-linux-arm64-musl": "0.2.175",
"@just-every/code-win32-x64": "0.2.175"
}
}
-766
View File
@@ -1,766 +0,0 @@
#!/usr/bin/env node
// Non-functional change to trigger release workflow
import { existsSync, mkdirSync, createWriteStream, chmodSync, readFileSync, readSync, writeFileSync, unlinkSync, statSync, openSync, closeSync, copyFileSync, fsyncSync, renameSync, realpathSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import { get } from 'https';
import { platform, arch, tmpdir } from 'os';
import { execSync } from 'child_process';
import { createRequire } from 'module';
const __dirname = dirname(fileURLToPath(import.meta.url));
// Map Node.js platform/arch to Rust target triples
function getTargetTriple() {
const platformMap = {
'darwin': 'apple-darwin',
'linux': 'unknown-linux-musl', // Default to musl for better compatibility
'win32': 'pc-windows-msvc'
};
const archMap = {
'x64': 'x86_64',
'arm64': 'aarch64'
};
const rustArch = archMap[arch()] || arch();
const rustPlatform = platformMap[platform()] || platform();
return `${rustArch}-${rustPlatform}`;
}
// Resolve a persistent user cache directory for binaries so that repeated
// npx installs can reuse a previously downloaded artifact and skip work.
function getCacheDir(version) {
const plt = platform();
const home = process.env.HOME || process.env.USERPROFILE || '';
let base = '';
if (plt === 'win32') {
base = process.env.LOCALAPPDATA || join(home, 'AppData', 'Local');
} else if (plt === 'darwin') {
base = join(home, 'Library', 'Caches');
} else {
base = process.env.XDG_CACHE_HOME || join(home, '.cache');
}
const dir = join(base, 'just-every', 'code', version);
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
return dir;
}
function getCachedBinaryPath(version, targetTriple, isWindows) {
const ext = isWindows ? '.exe' : '';
const cacheDir = getCacheDir(version);
return join(cacheDir, `code-${targetTriple}${ext}`);
}
const CODE_SHIM_SIGNATURES = [
'@just-every/code',
'bin/coder.js',
'$(dirname "$0")/coder',
'%~dp0coder'
];
function shimContentsLookOurs(contents) {
return CODE_SHIM_SIGNATURES.some(sig => contents.includes(sig));
}
function looksLikeOurCodeShim(path) {
try {
const contents = readFileSync(path, 'utf8');
return shimContentsLookOurs(contents);
} catch {
return false;
}
}
function isWSL() {
if (platform() !== 'linux') return false;
try {
const ver = readFileSync('/proc/version', 'utf8').toLowerCase();
return ver.includes('microsoft') || !!process.env.WSL_DISTRO_NAME;
} catch { return false; }
}
function isPathOnWindowsFs(p) {
try {
const mounts = readFileSync('/proc/mounts', 'utf8').split(/\n/).filter(Boolean);
let best = { mount: '/', type: 'unknown', len: 1 };
for (const line of mounts) {
const parts = line.split(' ');
if (parts.length < 3) continue;
const mnt = parts[1];
const typ = parts[2];
if (p.startsWith(mnt) && mnt.length > best.len) best = { mount: mnt, type: typ, len: mnt.length };
}
return best.type === 'drvfs' || best.type === 'cifs';
} catch { return false; }
}
async function writeCacheAtomic(srcPath, cachePath) {
try {
if (existsSync(cachePath)) {
const ok = validateDownloadedBinary(cachePath).ok;
if (ok) return;
}
} catch {}
const dir = dirname(cachePath);
if (!existsSync(dir)) { try { mkdirSync(dir, { recursive: true }); } catch {} }
const tmp = cachePath + '.tmp-' + Math.random().toString(36).slice(2, 8);
copyFileSync(srcPath, tmp);
try { const fd = openSync(tmp, 'r'); try { fsyncSync(fd); } finally { closeSync(fd); } } catch {}
// Retry with exponential backoff up to ~1.6s total
const delays = [100, 200, 400, 800, 1200, 1600];
for (let i = 0; i < delays.length; i++) {
try {
if (existsSync(cachePath)) { try { unlinkSync(cachePath); } catch {} }
renameSync(tmp, cachePath);
return;
} catch {
await new Promise(r => setTimeout(r, delays[i]));
}
}
if (existsSync(cachePath)) { try { unlinkSync(cachePath); } catch {} }
renameSync(tmp, cachePath);
}
function resolveGlobalBinDir() {
const plt = platform();
const userAgent = process.env.npm_config_user_agent || '';
const fromPrefix = (prefixPath) => {
if (!prefixPath) return '';
return plt === 'win32' ? prefixPath : join(prefixPath, 'bin');
};
const prefixEnv = process.env.npm_config_prefix || process.env.PREFIX || '';
const direct = fromPrefix(prefixEnv);
if (direct) return direct;
const tryExec = (command) => {
try {
return execSync(command, {
stdio: ['ignore', 'pipe', 'ignore'],
shell: true,
}).toString().trim();
} catch {
return '';
}
};
const prefixFromNpm = fromPrefix(tryExec('npm prefix -g'));
if (prefixFromNpm) return prefixFromNpm;
const binFromNpm = tryExec('npm bin -g');
if (binFromNpm) return binFromNpm;
if (userAgent.includes('pnpm')) {
const pnpmBin = tryExec('pnpm bin --global');
if (pnpmBin) return pnpmBin;
const pnpmPrefix = fromPrefix(tryExec('pnpm env get prefix'));
if (pnpmPrefix) return pnpmPrefix;
}
if (userAgent.includes('yarn')) {
const yarnBin = tryExec('yarn global bin');
if (yarnBin) return yarnBin;
}
return '';
}
async function downloadBinary(url, dest, maxRedirects = 5, maxRetries = 3) {
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
const doAttempt = () => new Promise((resolve, reject) => {
const attempt = (currentUrl, redirectsLeft) => {
const req = get(currentUrl, (response) => {
const status = response.statusCode || 0;
const location = response.headers.location;
if ((status === 301 || status === 302 || status === 303 || status === 307 || status === 308) && location) {
if (redirectsLeft <= 0) {
reject(new Error(`Too many redirects while downloading ${currentUrl}`));
return;
}
attempt(location, redirectsLeft - 1);
return;
}
if (status === 200) {
const expected = parseInt(response.headers['content-length'] || '0', 10) || 0;
let bytes = 0;
let timer;
const timeoutMs = 30000; // 30s inactivity timeout
const resetTimer = () => {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
req.destroy(new Error('download stalled'));
}, timeoutMs);
};
resetTimer();
response.on('data', (chunk) => {
bytes += chunk.length;
resetTimer();
});
const file = createWriteStream(dest);
response.pipe(file);
file.on('finish', () => {
if (timer) clearTimeout(timer);
file.close();
if (expected && bytes !== expected) {
try { unlinkSync(dest); } catch {}
reject(new Error(`incomplete download: got ${bytes} of ${expected} bytes`));
} else if (bytes === 0) {
try { unlinkSync(dest); } catch {}
reject(new Error('empty download'));
} else {
resolve();
}
});
file.on('error', (err) => {
if (timer) clearTimeout(timer);
try { unlinkSync(dest); } catch {}
reject(err);
});
} else {
reject(new Error(`Failed to download: HTTP ${status}`));
}
});
req.on('error', (err) => {
try { unlinkSync(dest); } catch {}
reject(err);
});
// Absolute request timeout to avoid hanging forever
req.setTimeout(120000, () => {
req.destroy(new Error('download timed out'));
});
};
attempt(url, maxRedirects);
});
let attemptNum = 0;
while (true) {
try {
return await doAttempt();
} catch (e) {
attemptNum += 1;
if (attemptNum > maxRetries) throw e;
const backoff = Math.min(2000, 200 * attemptNum);
await sleep(backoff);
}
}
}
function validateDownloadedBinary(p) {
try {
const st = statSync(p);
if (!st.isFile() || st.size === 0) {
return { ok: false, reason: 'empty or not a regular file' };
}
const fd = openSync(p, 'r');
try {
const buf = Buffer.alloc(4);
const n = readSync(fd, buf, 0, 4, 0);
if (n < 2) return { ok: false, reason: 'too short' };
const plt = platform();
if (plt === 'win32') {
if (!(buf[0] === 0x4d && buf[1] === 0x5a)) return { ok: false, reason: 'invalid PE header (missing MZ)' };
} else if (plt === 'linux' || plt === 'android') {
if (!(buf[0] === 0x7f && buf[1] === 0x45 && buf[2] === 0x4c && buf[3] === 0x46)) return { ok: false, reason: 'invalid ELF header' };
} else if (plt === 'darwin') {
const isMachO = (buf[0] === 0xcf && buf[1] === 0xfa && buf[2] === 0xed && buf[3] === 0xfe) ||
(buf[0] === 0xca && buf[1] === 0xfe && buf[2] === 0xba && buf[3] === 0xbe);
if (!isMachO) return { ok: false, reason: 'invalid Mach-O header' };
}
return { ok: true };
} finally {
closeSync(fd);
}
} catch (e) {
return { ok: false, reason: e.message };
}
}
async function main() {
// Detect potential PATH conflict with an existing `code` command (e.g., VS Code)
// Only relevant for global installs; skip for npx/local installs to keep postinstall fast.
const ua = process.env.npm_config_user_agent || '';
const isNpx = ua.includes('npx');
const isGlobal = process.env.npm_config_global === 'true';
if (isGlobal && !isNpx) {
try {
const whichCmd = process.platform === 'win32' ? 'where code' : 'command -v code || which code || true';
const resolved = execSync(whichCmd, { stdio: ['ignore', 'pipe', 'ignore'], shell: process.platform !== 'win32' }).toString().split(/\r?\n/).filter(Boolean)[0];
if (resolved) {
let contents = '';
try {
contents = readFileSync(resolved, 'utf8');
} catch {
contents = '';
}
const looksLikeOurs = shimContentsLookOurs(contents);
if (!looksLikeOurs) {
console.warn('[notice] Found an existing `code` on PATH at:');
console.warn(` ${resolved}`);
console.warn('[notice] We will still install our CLI, also available as `coder`.');
console.warn(' If `code` runs another tool, prefer using: coder');
console.warn(' Or run our CLI explicitly via: npx -y @just-every/code');
}
}
} catch {
// Ignore detection failures; proceed with install.
}
}
const targetTriple = getTargetTriple();
const isWindows = platform() === 'win32';
const binaryExt = isWindows ? '.exe' : '';
const binDir = join(__dirname, 'bin');
if (!existsSync(binDir)) {
mkdirSync(binDir, { recursive: true });
}
// Get package version - use readFileSync for compatibility
const packageJson = JSON.parse(readFileSync(join(__dirname, 'package.json'), 'utf8'));
const version = packageJson.version;
// Download only the primary binary; we'll create wrappers for legacy names.
const binaries = ['code'];
console.log(`Installing @just-every/code v${version} for ${targetTriple}...`);
for (const binary of binaries) {
const binaryName = `${binary}-${targetTriple}${binaryExt}`;
const localPath = join(binDir, binaryName);
const cachePath = getCachedBinaryPath(version, targetTriple, isWindows);
// On Windows we avoid placing the executable inside node_modules to prevent
// EBUSY/EPERM during global upgrades when the binary is in use.
// We treat the user cache path as the canonical home of the native binary.
// For macOS/Linux we keep previous behavior and also place a copy in binDir
// for convenience.
// Fast path: if a valid cached binary exists for this version+triple, reuse it.
try {
if (existsSync(cachePath)) {
const valid = validateDownloadedBinary(cachePath);
if (valid.ok) {
// Avoid mirroring into node_modules on Windows or WSL-on-NTFS.
const wsl = isWSL();
const binDirReal = (() => { try { return realpathSync(binDir); } catch { return binDir; } })();
const mirrorToLocal = !(isWindows || (wsl && isPathOnWindowsFs(binDirReal)));
if (mirrorToLocal) {
copyFileSync(cachePath, localPath);
try { chmodSync(localPath, 0o755); } catch {}
}
console.log(`✓ ${binaryName} ready from user cache`);
continue; // next binary
}
}
} catch {
// Ignore cache errors and fall through to normal paths
}
// First try platform package via npm optionalDependencies (fast path on npm CDN).
const require = createRequire(import.meta.url);
const platformPkg = (() => {
const name = (() => {
if (isWindows) return '@just-every/code-win32-x64';
const plt = platform();
const cpu = arch();
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';
return null;
})();
if (!name) return null;
try {
const pkgJsonPath = require.resolve(`${name}/package.json`);
const pkgDir = dirname(pkgJsonPath);
return { name, dir: pkgDir };
} catch {
return null;
}
})();
if (platformPkg) {
try {
// Expect binary inside platform package bin directory
const src = join(platformPkg.dir, 'bin', binaryName);
if (!existsSync(src)) {
throw new Error(`platform package missing binary: ${platformPkg.name}`);
}
// Populate cache first (canonical location) atomically
await writeCacheAtomic(src, cachePath);
// Mirror into local bin only on Unix-like filesystems (not Windows/WSL-on-NTFS)
const wsl = isWSL();
const binDirReal = (() => { try { return realpathSync(binDir); } catch { return binDir; } })();
const mirrorToLocal = !(isWindows || (wsl && isPathOnWindowsFs(binDirReal)));
if (mirrorToLocal) {
copyFileSync(cachePath, localPath);
try { chmodSync(localPath, 0o755); } catch {}
}
console.log(`✓ Installed ${binaryName} from ${platformPkg.name} (cached)`);
continue; // next binary
} catch (e) {
console.warn(`⚠ Failed platform package install (${e.message}), falling back to GitHub download`);
}
}
// Decide archive format per OS with fallback on macOS/Linux:
// - Windows: .zip
// - macOS/Linux: prefer .zst if `zstd` CLI is available; otherwise use .tar.gz
const isWin = isWindows;
const isWSL = (() => {
if (platform() !== 'linux') return false;
try {
const ver = readFileSync('/proc/version', 'utf8').toLowerCase();
return ver.includes('microsoft') || !!process.env.WSL_DISTRO_NAME;
} catch { return false; }
})();
const binDirReal = (() => { try { return realpathSync(binDir); } catch { return binDir; } })();
const mirrorToLocal = !(isWin || (isWSL && isPathOnWindowsFs(binDirReal)));
let useZst = false;
if (!isWin) {
try {
execSync('zstd --version', { stdio: 'ignore', shell: true });
useZst = true;
} catch {
useZst = false;
}
}
const archiveName = isWin ? `${binaryName}.zip` : (useZst ? `${binaryName}.zst` : `${binaryName}.tar.gz`);
const downloadUrl = `https://github.com/just-every/code/releases/download/v${version}/${archiveName}`;
console.log(`Downloading ${archiveName}...`);
try {
const needsIsolation = isWin || (!isWin && !mirrorToLocal); // Windows or WSL-on-NTFS
let safeTempDir = needsIsolation ? join(tmpdir(), 'just-every', 'code', version) : binDir;
// Ensure staging dir exists; if tmp fails (permissions/space), fall back to user cache.
if (needsIsolation) {
try {
if (!existsSync(safeTempDir)) mkdirSync(safeTempDir, { recursive: true });
} catch {
try {
safeTempDir = getCacheDir(version);
if (!existsSync(safeTempDir)) mkdirSync(safeTempDir, { recursive: true });
} catch {}
}
}
const tmpPath = join(needsIsolation ? safeTempDir : binDir, `.${archiveName}.part`);
await downloadBinary(downloadUrl, tmpPath);
if (isWin) {
// Unzip to a temp directory, then move into the per-user cache.
const unzipDest = safeTempDir;
try {
const sysRoot = process.env.SystemRoot || process.env.windir || 'C:\\Windows';
const psFull = join(sysRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe');
const psCmd = `Expand-Archive -Path '${tmpPath}' -DestinationPath '${unzipDest}' -Force`;
let ok = false;
// Attempt full-path powershell.exe
try { execSync(`"${psFull}" -NoProfile -NonInteractive -Command "${psCmd}"`, { stdio: 'ignore' }); ok = true; } catch {}
// Fallback to powershell in PATH
if (!ok) { try { execSync(`powershell -NoProfile -NonInteractive -Command "${psCmd}"`, { stdio: 'ignore' }); ok = true; } catch {} }
// Fallback to pwsh (PowerShell 7)
if (!ok) { try { execSync(`pwsh -NoProfile -NonInteractive -Command "${psCmd}"`, { stdio: 'ignore' }); ok = true; } catch {} }
// Final fallback: bsdtar can extract .zip
if (!ok) { execSync(`tar -xf "${tmpPath}" -C "${unzipDest}"`, { stdio: 'ignore', shell: true }); }
} catch (e) {
throw new Error(`failed to unzip archive: ${e.message}`);
} finally {
try { unlinkSync(tmpPath); } catch {}
}
// Move the extracted file from temp to cache; do not leave a copy in node_modules
try {
const extractedPath = join(unzipDest, binaryName);
await writeCacheAtomic(extractedPath, cachePath);
try { unlinkSync(extractedPath); } catch {}
} catch (e) {
throw new Error(`failed to move binary to cache: ${e.message}`);
}
} else {
if (useZst) {
// Decompress .zst via system zstd
try {
const outPath = mirrorToLocal ? localPath : join(safeTempDir, binaryName);
execSync(`zstd -d '${tmpPath}' -o '${outPath}'`, { stdio: 'ignore', shell: true });
} catch (e) {
try { unlinkSync(tmpPath); } catch {}
throw new Error(`failed to decompress .zst (need zstd CLI): ${e.message}`);
}
try { unlinkSync(tmpPath); } catch {}
} else {
// Extract .tar.gz using system tar
try {
const dest = mirrorToLocal ? binDir : safeTempDir;
execSync(`tar -xzf '${tmpPath}' -C '${dest}'`, { stdio: 'ignore', shell: true });
} catch (e) {
try { unlinkSync(tmpPath); } catch {}
throw new Error(`failed to extract .tar.gz: ${e.message}`);
}
try { unlinkSync(tmpPath); } catch {}
}
if (!mirrorToLocal) {
try {
const extractedPath = join(safeTempDir, binaryName);
await writeCacheAtomic(extractedPath, cachePath);
try { unlinkSync(extractedPath); } catch {}
} catch (e) {
throw new Error(`failed to move binary to cache: ${e.message}`);
}
}
}
// Validate header to avoid corrupt binaries causing spawn EFTYPE/ENOEXEC
const valid = validateDownloadedBinary(isWin ? cachePath : (mirrorToLocal ? localPath : cachePath));
if (!valid.ok) {
try { (isWin || !mirrorToLocal) ? unlinkSync(cachePath) : unlinkSync(localPath); } catch {}
throw new Error(`invalid binary (${valid.reason})`);
}
// Make executable on Unix-like systems
if (!isWin && mirrorToLocal) {
chmodSync(localPath, 0o755);
}
console.log(`✓ Installed ${binaryName}${(isWin || !mirrorToLocal) ? ' (cached)' : ''}`);
// Ensure persistent cache holds the binary (already true for Windows path)
if (!isWin && mirrorToLocal) {
try { await writeCacheAtomic(localPath, cachePath); } catch {}
}
} catch (error) {
console.error(`✗ Failed to install ${binaryName}: ${error.message}`);
console.error(` Downloaded from: ${downloadUrl}`);
// Continue with other binaries even if one fails
}
}
// Create platform-specific symlink/copy for main binary
const mainBinary = `code-${targetTriple}${binaryExt}`;
const mainBinaryPath = join(binDir, mainBinary);
if (existsSync(mainBinaryPath) || existsSync(getCachedBinaryPath(version, targetTriple, platform() === 'win32'))) {
try {
const probePath = existsSync(mainBinaryPath) ? mainBinaryPath : getCachedBinaryPath(version, targetTriple, platform() === 'win32');
const stats = statSync(probePath);
if (!stats.size) throw new Error('binary is empty (download likely failed)');
const valid = validateDownloadedBinary(probePath);
if (!valid.ok) {
console.warn(`⚠ Main code binary appears invalid: ${valid.reason}`);
console.warn(' Try reinstalling or check your network/proxy settings.');
}
} catch (e) {
console.warn(`⚠ Main code binary appears invalid: ${e.message}`);
console.warn(' Try reinstalling or check your network/proxy settings.');
}
console.log('Setting up main code binary...');
// On Windows, we can't use symlinks easily, so update the JS wrapper
// On Unix, the JS wrapper will find the correct binary
console.log('✓ Installation complete!');
} else {
console.warn('⚠ Main code binary not found. You may need to build from source.');
}
// Handle collisions (e.g., VS Code) and add wrappers. We no longer publish a
// `code` bin in package.json. Instead, for global installs we create a `code`
// wrapper only when there is no conflicting `code` earlier on PATH. This avoids
// hijacking the VS Code CLI while still giving users a friendly name when safe.
// For upgrades from older versions that published a `code` bin, we also remove
// our old shim if a conflict is detected.
if (isGlobal && !isNpx) try {
const isTTY = process.stdout && process.stdout.isTTY;
const isWindows = platform() === 'win32';
const ua = process.env.npm_config_user_agent || '';
const isBun = ua.includes('bun') || !!process.env.BUN_INSTALL;
const installedCmds = new Set(['coder']); // global install always exposes coder via package manager
const skippedCmds = [];
// Helper to resolve all 'code' on PATH
const resolveAllOnPath = () => {
try {
if (isWindows) {
const out = execSync('where code', { stdio: ['ignore', 'pipe', 'ignore'] }).toString();
return out.split(/\r?\n/).map(s => s.trim()).filter(Boolean);
}
let out = '';
try {
out = execSync('bash -lc "which -a code 2>/dev/null"', { stdio: ['ignore', 'pipe', 'ignore'] }).toString();
} catch {
try {
out = execSync('command -v code || true', { stdio: ['ignore', 'pipe', 'ignore'] }).toString();
} catch { out = ''; }
}
return out.split(/\r?\n/).map(s => s.trim()).filter(Boolean);
} catch {
return [];
}
};
if (isBun) {
// Bun creates shims for every bin; if another 'code' exists elsewhere on PATH, remove Bun's shim
let bunBin = '';
try {
const home = process.env.HOME || process.env.USERPROFILE || '';
const bunBase = process.env.BUN_INSTALL || join(home, '.bun');
bunBin = join(bunBase, 'bin');
} catch {}
const bunShim = join(bunBin || '', isWindows ? 'code.cmd' : 'code');
const candidates = resolveAllOnPath();
const other = candidates.find(p => p && (!bunBin || !p.startsWith(bunBin)));
if (other && existsSync(bunShim)) {
try {
unlinkSync(bunShim);
console.log(`✓ Skipped global 'code' shim under Bun (existing: ${other})`);
skippedCmds.push({ name: 'code', reason: `existing: ${other}` });
} catch (e) {
console.log(`⚠ Could not remove Bun shim '${bunShim}': ${e.message}`);
}
} else if (!other) {
// No conflict: create a wrapper that forwards to `coder`
try {
const wrapperPath = bunShim;
if (isWindows) {
const content = `@echo off\r\n"%~dp0coder" %*\r\n`;
writeFileSync(wrapperPath, content);
} else {
const content = `#!/bin/sh\nexec "$(dirname \"$0\")/coder" "$@"\n`;
writeFileSync(wrapperPath, content);
chmodSync(wrapperPath, 0o755);
}
console.log("✓ Created 'code' wrapper -> coder (bun)");
installedCmds.add('code');
} catch (e) {
console.log(`⚠ Failed to create 'code' wrapper (bun): ${e.message}`);
}
}
// Print summary for Bun
const list = Array.from(installedCmds).sort().join(', ');
console.log(`Commands installed (bun): ${list}`);
if (skippedCmds.length) {
for (const s of skippedCmds) console.error(`Commands skipped: ${s.name} (${s.reason})`);
console.error('→ Use `coder` to run this tool.');
}
// Final friendly usage hint
if (installedCmds.has('code')) {
console.log("Use 'code' to launch Code.");
} else {
console.log("Use 'coder' to launch Code.");
}
} else {
// npm/pnpm/yarn path
const globalBin = resolveGlobalBinDir();
const ourShim = globalBin ? join(globalBin, isWindows ? 'code.cmd' : 'code') : '';
const candidates = resolveAllOnPath();
const others = candidates.filter(p => p && (!ourShim || p !== ourShim));
const ourShimExists = ourShim && existsSync(ourShim);
const shimLooksOurs = ourShimExists && looksLikeOurCodeShim(ourShim);
const conflictPaths = [
...others,
...(ourShimExists && !shimLooksOurs ? [ourShim] : []),
];
const collision = conflictPaths.length > 0;
const ensureWrapper = (name, args) => {
if (!globalBin) return;
try {
const wrapperPath = join(globalBin, isWindows ? `${name}.cmd` : name);
if (isWindows) {
const content = `@echo off\r\n"%~dp0${collision ? 'coder' : 'code'}" ${args} %*\r\n`;
writeFileSync(wrapperPath, content);
} else {
const content = `#!/bin/sh\nexec "$(dirname \"$0\")/${collision ? 'coder' : 'code'}" ${args} "$@"\n`;
writeFileSync(wrapperPath, content);
chmodSync(wrapperPath, 0o755);
}
console.log(`✓ Created wrapper '${name}' -> ${collision ? 'coder' : 'code'} ${args}`);
installedCmds.add(name);
} catch (e) {
console.log(`⚠ Failed to create '${name}' wrapper: ${e.message}`);
}
};
// Always create legacy wrappers so existing scripts keep working
ensureWrapper('code-tui', '');
ensureWrapper('code-exec', 'exec');
if (collision) {
console.error('⚠ Detected existing `code` on PATH:');
for (const p of conflictPaths) console.error(` - ${p}`);
if (globalBin) {
try {
if (ourShimExists) {
if (shimLooksOurs && others.length > 0) {
unlinkSync(ourShim);
console.error(`✓ Removed global 'code' shim (ours) at ${ourShim}`);
const reason = others[0] || ourShim;
skippedCmds.push({ name: 'code', reason: `existing: ${reason}` });
} else if (!shimLooksOurs) {
console.error(`✓ Skipped global 'code' shim (different CLI at ${ourShim})`);
const reason = conflictPaths[0] || ourShim;
skippedCmds.push({ name: 'code', reason: `existing: ${reason}` });
}
} else {
const reason = conflictPaths[0] || 'another command on PATH';
skippedCmds.push({ name: 'code', reason: `existing: ${reason}` });
}
} catch (e) {
console.error(`⚠ Could not remove npm shim '${ourShim}': ${e.message}`);
}
console.error('→ Use `coder` to run this tool.');
} else {
console.log('Note: could not determine npm global bin; skipping alias creation.');
}
} else {
// No collision; ensure a 'code' wrapper exists forwarding to 'coder'
if (globalBin) {
try {
const content = isWindows
? `@echo off\r\n"%~dp0coder" %*\r\n`
: `#!/bin/sh\nexec "$(dirname \"$0\")/coder" "$@"\n`;
writeFileSync(ourShim, content);
if (!isWindows) chmodSync(ourShim, 0o755);
console.log("✓ Created 'code' wrapper -> coder");
installedCmds.add('code');
} catch (e) {
console.log(`⚠ Failed to create 'code' wrapper: ${e.message}`);
}
}
}
// Print summary for npm/pnpm/yarn
const list = Array.from(installedCmds).sort().join(', ');
console.log(`Commands installed: ${list}`);
if (skippedCmds.length) {
for (const s of skippedCmds) console.log(`Commands skipped: ${s.name} (${s.reason})`);
}
// Final friendly usage hint
if (installedCmds.has('code')) {
console.log("Use 'code' to launch Code.");
} else {
console.log("Use 'coder' to launch Code.");
}
}
} catch {
// non-fatal
}
}
main().catch(error => {
console.error('Installation failed:', error);
process.exit(1);
});
@@ -1,69 +0,0 @@
#!/usr/bin/env node
// Windows-friendly preinstall: proactively free file locks from prior installs
// so npm/yarn/pnpm can stage the new package. No-ops on non-Windows.
import { platform } from 'os';
import { execSync } from 'child_process';
import { existsSync, readdirSync, rmSync, readFileSync, statSync } from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
function isWSL() {
if (platform() !== 'linux') return false;
try {
const rel = readFileSync('/proc/version', 'utf8').toLowerCase();
return rel.includes('microsoft') || !!process.env.WSL_DISTRO_NAME;
} catch { return false; }
}
const isWin = platform() === 'win32';
const wsl = isWSL();
const isWinLike = isWin || wsl;
// Scope: only run for global installs, unless explicitly forced. Allow opt-out.
const isGlobal = process.env.npm_config_global === 'true';
const force = process.env.CODE_FORCE_PREINSTALL === '1';
const skip = process.env.CODE_SKIP_PREINSTALL === '1';
if (!isWinLike || skip || (!isGlobal && !force)) process.exit(0);
function tryExec(cmd, opts = {}) {
try { execSync(cmd, { stdio: ['ignore', 'ignore', 'ignore'], shell: true, ...opts }); } catch { /* ignore */ }
}
// 1) Stop our native binary if it is holding locks. Avoid killing unrelated tools.
// Only available on native Windows; skip entirely on WSL to avoid noise.
if (isWin) {
tryExec('taskkill /IM code-x86_64-pc-windows-msvc.exe /F');
}
// 2) Remove stale staging dirs from previous failed installs under the global
// @just-every scope, which npm will reuse (e.g., .code-XXXXX). Remove only
// old entries and never the current staging or live package.
try {
let scopeDir = '';
try {
const root = execSync('npm root -g', { stdio: ['ignore', 'pipe', 'ignore'], shell: true }).toString().trim();
scopeDir = path.join(root, '@just-every');
} catch {
// Fall back to guessing from this script location: <staging>\..\..\
const here = path.resolve(path.dirname(fileURLToPath(import.meta.url)));
scopeDir = path.resolve(here, '..');
}
if (existsSync(scopeDir)) {
const now = Date.now();
const maxAgeMs = 2 * 60 * 60 * 1000; // 2 hours
const currentDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
for (const name of readdirSync(scopeDir)) {
if (!name.startsWith('.code-')) continue;
const p = path.join(scopeDir, name);
if (path.resolve(p) === currentDir) continue; // never remove our current dir
try {
const st = statSync(p);
const age = now - st.mtimeMs;
if (age > maxAgeMs) rmSync(p, { recursive: true, force: true });
} catch { /* ignore */ }
}
}
} catch { /* ignore */ }
process.exit(0);
@@ -1,32 +0,0 @@
<#
Helper to recover from EBUSY/EPERM during global npm upgrades on Windows.
Closes running processes and removes stale package folders.
Usage (PowerShell):
Set-ExecutionPolicy -Scope Process Bypass -Force
./codex-cli/scripts/windows-cleanup.ps1
#>
$ErrorActionPreference = 'SilentlyContinue'
Write-Host "Stopping running Code/Coder processes..."
taskkill /IM code-x86_64-pc-windows-msvc.exe /F 2>$null | Out-Null
taskkill /IM code.exe /F 2>$null | Out-Null
taskkill /IM coder.exe /F 2>$null | Out-Null
Write-Host "Removing old global package (if present)..."
$npmRoot = (& npm root -g).Trim()
$pkgPath = Join-Path $npmRoot "@just-every\code"
if (Test-Path $pkgPath) {
try { Remove-Item -LiteralPath $pkgPath -Recurse -Force -ErrorAction Stop } catch {}
}
Write-Host "Removing temp staging directories (if present)..."
Get-ChildItem -LiteralPath (Join-Path $npmRoot "@just-every") -Force -ErrorAction SilentlyContinue |
Where-Object { $_.Name -like '.code-*' } |
ForEach-Object {
try { Remove-Item -LiteralPath $_.FullName -Recurse -Force -ErrorAction Stop } catch {}
}
Write-Host "Cleanup complete. You can now run: npm install -g @just-every/code@latest"
+27 -27
View File
@@ -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);
+9 -9
View File
@@ -1,10 +1,10 @@
{
"name": "@just-every/code",
"name": "@hanzo/dev",
"version": "0.2.188",
"license": "Apache-2.0",
"description": "Lightweight coding agent that runs in your terminal - fork of OpenAI Codex",
"description": "Local AI coding agent for your terminal, powered by Hanzo AI",
"bin": {
"coder": "bin/coder.js"
"dev": "bin/coder.js"
},
"type": "module",
"engines": {
@@ -25,7 +25,7 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/just-every/code.git"
"url": "git+https://github.com/hanzoai/dev.git"
},
"publishConfig": {
"access": "public"
@@ -35,10 +35,10 @@
"prettier": "^3.3.3"
},
"optionalDependencies": {
"@just-every/code-darwin-arm64": "0.2.188",
"@just-every/code-darwin-x64": "0.2.188",
"@just-every/code-linux-x64-musl": "0.2.188",
"@just-every/code-linux-arm64-musl": "0.2.188",
"@just-every/code-win32-x64": "0.2.188"
"@hanzo/dev-darwin-arm64": "0.2.188",
"@hanzo/dev-darwin-x64": "0.2.188",
"@hanzo/dev-linux-x64-musl": "0.2.188",
"@hanzo/dev-linux-arm64-musl": "0.2.188",
"@hanzo/dev-win32-x64": "0.2.188"
}
}
+54 -59
View File
@@ -43,7 +43,7 @@ function getCacheDir(version) {
} else {
base = process.env.XDG_CACHE_HOME || join(home, '.cache');
}
const dir = join(base, 'just-every', 'code', version);
const dir = join(base, 'hanzoai', 'dev', version);
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
return dir;
}
@@ -51,21 +51,22 @@ function getCacheDir(version) {
function getCachedBinaryPath(version, targetTriple, isWindows) {
const ext = isWindows ? '.exe' : '';
const cacheDir = getCacheDir(version);
return join(cacheDir, `code-${targetTriple}${ext}`);
return join(cacheDir, `dev-${targetTriple}${ext}`);
}
const CODE_SHIM_SIGNATURES = [
'@just-every/code',
'bin/coder.js',
'$(dirname "$0")/coder',
'%~dp0coder'
// Substrings that appear only in shims we wrote ourselves. Used before removing
// or replacing a `code` on PATH, so a third party's `code` is never touched.
const SHIM_SIGNATURES = [
'@hanzo/dev',
'$(dirname "$0")/dev',
'%~dp0dev'
];
function shimContentsLookOurs(contents) {
return CODE_SHIM_SIGNATURES.some(sig => contents.includes(sig));
return SHIM_SIGNATURES.some(sig => contents.includes(sig));
}
function looksLikeOurCodeShim(path) {
function looksLikeOurShim(path) {
try {
const contents = readFileSync(path, 'utf8');
return shimContentsLookOurs(contents);
@@ -309,9 +310,9 @@ async function main() {
if (!looksLikeOurs) {
console.warn('[notice] Found an existing `code` on PATH at:');
console.warn(` ${resolved}`);
console.warn('[notice] We will still install our CLI, also available as `coder`.');
console.warn(' If `code` runs another tool, prefer using: coder');
console.warn(' Or run our CLI explicitly via: npx -y @just-every/code');
console.warn('[notice] We will still install our CLI, also available as `dev`.');
console.warn(' If `code` runs another tool, prefer using: dev');
console.warn(' Or run our CLI explicitly via: npx -y @hanzo/dev');
}
}
} catch {
@@ -333,9 +334,9 @@ async function main() {
const version = packageJson.version;
// Download only the primary binary; we'll create wrappers for legacy names.
const binaries = ['code'];
console.log(`Installing @just-every/code v${version} for ${targetTriple}...`);
const binaries = ['dev'];
console.log(`Installing @hanzo/dev v${version} for ${targetTriple}...`);
for (const binary of binaries) {
const binaryName = `${binary}-${targetTriple}${binaryExt}`;
@@ -373,13 +374,13 @@ async function main() {
const require = createRequire(import.meta.url);
const platformPkg = (() => {
const name = (() => {
if (isWindows) return '@just-every/code-win32-x64';
if (isWindows) return '@hanzo/dev-win32-x64';
const plt = platform();
const cpu = arch();
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) return null;
@@ -420,15 +421,8 @@ async function main() {
// - Windows: .zip
// - macOS/Linux: prefer .zst if `zstd` CLI is available; otherwise use .tar.gz
const isWin = isWindows;
const isWSL = (() => {
if (platform() !== 'linux') return false;
try {
const ver = readFileSync('/proc/version', 'utf8').toLowerCase();
return ver.includes('microsoft') || !!process.env.WSL_DISTRO_NAME;
} catch { return false; }
})();
const binDirReal = (() => { try { return realpathSync(binDir); } catch { return binDir; } })();
const mirrorToLocal = !(isWin || (isWSL && isPathOnWindowsFs(binDirReal)));
const mirrorToLocal = !(isWin || (isWSL() && isPathOnWindowsFs(binDirReal)));
let useZst = false;
if (!isWin) {
try {
@@ -439,12 +433,12 @@ async function main() {
}
}
const archiveName = isWin ? `${binaryName}.zip` : (useZst ? `${binaryName}.zst` : `${binaryName}.tar.gz`);
const downloadUrl = `https://github.com/just-every/code/releases/download/v${version}/${archiveName}`;
const downloadUrl = `https://github.com/hanzoai/dev/releases/download/v${version}/${archiveName}`;
console.log(`Downloading ${archiveName}...`);
try {
const needsIsolation = isWin || (!isWin && !mirrorToLocal); // Windows or WSL-on-NTFS
let safeTempDir = needsIsolation ? join(tmpdir(), 'just-every', 'code', version) : binDir;
let safeTempDir = needsIsolation ? join(tmpdir(), 'hanzoai', 'dev', version) : binDir;
// Ensure staging dir exists; if tmp fails (permissions/space), fall back to user cache.
if (needsIsolation) {
try {
@@ -547,7 +541,7 @@ async function main() {
}
// Create platform-specific symlink/copy for main binary
const mainBinary = `code-${targetTriple}${binaryExt}`;
const mainBinary = `dev-${targetTriple}${binaryExt}`;
const mainBinaryPath = join(binDir, mainBinary);
if (existsSync(mainBinaryPath) || existsSync(getCachedBinaryPath(version, targetTriple, platform() === 'win32'))) {
@@ -557,26 +551,25 @@ async function main() {
if (!stats.size) throw new Error('binary is empty (download likely failed)');
const valid = validateDownloadedBinary(probePath);
if (!valid.ok) {
console.warn(`⚠ Main code binary appears invalid: ${valid.reason}`);
console.warn(`⚠ Main dev binary appears invalid: ${valid.reason}`);
console.warn(' Try reinstalling or check your network/proxy settings.');
}
} catch (e) {
console.warn(`⚠ Main code binary appears invalid: ${e.message}`);
console.warn(`⚠ Main dev binary appears invalid: ${e.message}`);
console.warn(' Try reinstalling or check your network/proxy settings.');
}
console.log('Setting up main code binary...');
console.log('Setting up main dev binary...');
// On Windows, we can't use symlinks easily, so update the JS wrapper
// On Unix, the JS wrapper will find the correct binary
console.log('✓ Installation complete!');
} else {
console.warn('⚠ Main code binary not found. You may need to build from source.');
console.warn('⚠ Main dev binary not found. You may need to build from source.');
}
// Handle collisions (e.g., VS Code) and add wrappers. We no longer publish a
// `code` bin in package.json. Instead, for global installs we create a `code`
// wrapper only when there is no conflicting `code` earlier on PATH. This avoids
// hijacking the VS Code CLI while still giving users a friendly name when safe.
// `dev` is the only bin in package.json, so the package manager always creates
// it and every wrapper below forwards to it. `code` is an alias we create only
// when nothing else owns that name on PATH, so we never hijack another CLI.
// For upgrades from older versions that published a `code` bin, we also remove
// our old shim if a conflict is detected.
if (isGlobal && !isNpx) try {
@@ -585,7 +578,7 @@ async function main() {
const ua = process.env.npm_config_user_agent || '';
const isBun = ua.includes('bun') || !!process.env.BUN_INSTALL;
const installedCmds = new Set(['coder']); // global install always exposes coder via package manager
const installedCmds = new Set(['dev']); // global install always exposes dev via package manager
const skippedCmds = [];
// Helper to resolve all 'code' on PATH
@@ -630,18 +623,18 @@ async function main() {
console.log(`⚠ Could not remove Bun shim '${bunShim}': ${e.message}`);
}
} else if (!other) {
// No conflict: create a wrapper that forwards to `coder`
// No conflict: create a wrapper that forwards to `dev`
try {
const wrapperPath = bunShim;
if (isWindows) {
const content = `@echo off\r\n"%~dp0coder" %*\r\n`;
const content = `@echo off\r\n"%~dp0dev" %*\r\n`;
writeFileSync(wrapperPath, content);
} else {
const content = `#!/bin/sh\nexec "$(dirname \"$0\")/coder" "$@"\n`;
const content = `#!/bin/sh\nexec "$(dirname \"$0\")/dev" "$@"\n`;
writeFileSync(wrapperPath, content);
chmodSync(wrapperPath, 0o755);
}
console.log("✓ Created 'code' wrapper -> coder (bun)");
console.log("✓ Created 'code' wrapper -> dev (bun)");
installedCmds.add('code');
} catch (e) {
console.log(`⚠ Failed to create 'code' wrapper (bun): ${e.message}`);
@@ -653,13 +646,13 @@ async function main() {
console.log(`Commands installed (bun): ${list}`);
if (skippedCmds.length) {
for (const s of skippedCmds) console.error(`Commands skipped: ${s.name} (${s.reason})`);
console.error('→ Use `coder` to run this tool.');
console.error('→ Use `dev` to run this tool.');
}
// Final friendly usage hint
if (installedCmds.has('code')) {
console.log("Use 'code' to launch Code.");
console.log("Use 'code' to launch Hanzo Dev.");
} else {
console.log("Use 'coder' to launch Code.");
console.log("Use 'dev' to launch Hanzo Dev.");
}
} else {
// npm/pnpm/yarn path
@@ -668,7 +661,7 @@ async function main() {
const candidates = resolveAllOnPath();
const others = candidates.filter(p => p && (!ourShim || p !== ourShim));
const ourShimExists = ourShim && existsSync(ourShim);
const shimLooksOurs = ourShimExists && looksLikeOurCodeShim(ourShim);
const shimLooksOurs = ourShimExists && looksLikeOurShim(ourShim);
const conflictPaths = [
...others,
...(ourShimExists && !shimLooksOurs ? [ourShim] : []),
@@ -680,21 +673,23 @@ async function main() {
try {
const wrapperPath = join(globalBin, isWindows ? `${name}.cmd` : name);
if (isWindows) {
const content = `@echo off\r\n"%~dp0${collision ? 'coder' : 'code'}" ${args} %*\r\n`;
const content = `@echo off\r\n"%~dp0dev" ${args} %*\r\n`;
writeFileSync(wrapperPath, content);
} else {
const content = `#!/bin/sh\nexec "$(dirname \"$0\")/${collision ? 'coder' : 'code'}" ${args} "$@"\n`;
const content = `#!/bin/sh\nexec "$(dirname \"$0\")/dev" ${args} "$@"\n`;
writeFileSync(wrapperPath, content);
chmodSync(wrapperPath, 0o755);
}
console.log(`✓ Created wrapper '${name}' -> ${collision ? 'coder' : 'code'} ${args}`);
console.log(`✓ Created wrapper '${name}' -> dev ${args}`);
installedCmds.add(name);
} catch (e) {
console.log(`⚠ Failed to create '${name}' wrapper: ${e.message}`);
}
};
// Always create legacy wrappers so existing scripts keep working
// Always create legacy wrappers so existing scripts keep working.
// These are distinct commands and keep their own names; only their target
// changed from `code` to `dev`.
ensureWrapper('code-tui', '');
ensureWrapper('code-exec', 'exec');
@@ -721,20 +716,20 @@ async function main() {
} catch (e) {
console.error(`⚠ Could not remove npm shim '${ourShim}': ${e.message}`);
}
console.error('→ Use `coder` to run this tool.');
console.error('→ Use `dev` to run this tool.');
} else {
console.log('Note: could not determine npm global bin; skipping alias creation.');
}
} else {
// No collision; ensure a 'code' wrapper exists forwarding to 'coder'
// No collision; ensure a 'code' wrapper exists forwarding to 'dev'
if (globalBin) {
try {
const content = isWindows
? `@echo off\r\n"%~dp0coder" %*\r\n`
: `#!/bin/sh\nexec "$(dirname \"$0\")/coder" "$@"\n`;
? `@echo off\r\n"%~dp0dev" %*\r\n`
: `#!/bin/sh\nexec "$(dirname \"$0\")/dev" "$@"\n`;
writeFileSync(ourShim, content);
if (!isWindows) chmodSync(ourShim, 0o755);
console.log("✓ Created 'code' wrapper -> coder");
console.log("✓ Created 'code' wrapper -> dev");
installedCmds.add('code');
} catch (e) {
console.log(`⚠ Failed to create 'code' wrapper: ${e.message}`);
@@ -750,9 +745,9 @@ async function main() {
}
// Final friendly usage hint
if (installedCmds.has('code')) {
console.log("Use 'code' to launch Code.");
console.log("Use 'code' to launch Hanzo Dev.");
} else {
console.log("Use 'coder' to launch Code.");
console.log("Use 'dev' to launch Hanzo Dev.");
}
}
} catch {
+12 -16
View File
@@ -70,22 +70,18 @@ ARTIFACTS_DIR="$(mktemp -d)"
trap 'rm -rf "$ARTIFACTS_DIR"' EXIT
# NB: The GitHub CLI `gh` must be installed and authenticated.
gh run download --dir "$ARTIFACTS_DIR" --repo just-every/code "$WORKFLOW_ID"
gh run download --dir "$ARTIFACTS_DIR" --repo hanzoai/dev "$WORKFLOW_ID"
# x64 Linux
zstd -d "$ARTIFACTS_DIR/x86_64-unknown-linux-musl/code-x86_64-unknown-linux-musl.zst" \
-o "$BIN_DIR/code-x86_64-unknown-linux-musl"
# ARM64 Linux
zstd -d "$ARTIFACTS_DIR/aarch64-unknown-linux-musl/code-aarch64-unknown-linux-musl.zst" \
-o "$BIN_DIR/code-aarch64-unknown-linux-musl"
# x64 macOS
zstd -d "$ARTIFACTS_DIR/x86_64-apple-darwin/code-x86_64-apple-darwin.zst" \
-o "$BIN_DIR/code-x86_64-apple-darwin"
# ARM64 macOS
zstd -d "$ARTIFACTS_DIR/aarch64-apple-darwin/code-aarch64-apple-darwin.zst" \
-o "$BIN_DIR/code-aarch64-apple-darwin"
# x64 Windows
zstd -d "$ARTIFACTS_DIR/x86_64-pc-windows-msvc/code-x86_64-pc-windows-msvc.exe.zst" \
-o "$BIN_DIR/code-x86_64-pc-windows-msvc.exe"
# Each build job uploads its artifact as `binaries-<target>` containing
# `dev-<target>[.exe].zst`.
for target in \
x86_64-unknown-linux-musl \
aarch64-unknown-linux-musl \
x86_64-apple-darwin \
aarch64-apple-darwin \
x86_64-pc-windows-msvc.exe
do
zstd -d "$ARTIFACTS_DIR/binaries-${target%.exe}/dev-$target.zst" -o "$BIN_DIR/dev-$target"
done
echo "Installed native dependencies into $BIN_DIR"
+4 -4
View File
@@ -33,17 +33,17 @@ function tryExec(cmd, opts = {}) {
// 1) Stop our native binary if it is holding locks. Avoid killing unrelated tools.
// Only available on native Windows; skip entirely on WSL to avoid noise.
if (isWin) {
tryExec('taskkill /IM code-x86_64-pc-windows-msvc.exe /F');
tryExec('taskkill /IM dev-x86_64-pc-windows-msvc.exe /F');
}
// 2) Remove stale staging dirs from previous failed installs under the global
// @just-every scope, which npm will reuse (e.g., .code-XXXXX). Remove only
// @hanzo scope, which npm will reuse (e.g., .dev-XXXXX). Remove only
// old entries and never the current staging or live package.
try {
let scopeDir = '';
try {
const root = execSync('npm root -g', { stdio: ['ignore', 'pipe', 'ignore'], shell: true }).toString().trim();
scopeDir = path.join(root, '@just-every');
scopeDir = path.join(root, '@hanzo');
} catch {
// Fall back to guessing from this script location: <staging>\..\..\
const here = path.resolve(path.dirname(fileURLToPath(import.meta.url)));
@@ -54,7 +54,7 @@ try {
const maxAgeMs = 2 * 60 * 60 * 1000; // 2 hours
const currentDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
for (const name of readdirSync(scopeDir)) {
if (!name.startsWith('.code-')) continue;
if (!name.startsWith('.dev-')) continue;
const p = path.join(scopeDir, name);
if (path.resolve(p) === currentDir) continue; // never remove our current dir
try {
+7 -8
View File
@@ -9,24 +9,23 @@ Usage (PowerShell):
$ErrorActionPreference = 'SilentlyContinue'
Write-Host "Stopping running Code/Coder processes..."
taskkill /IM code-x86_64-pc-windows-msvc.exe /F 2>$null | Out-Null
taskkill /IM code.exe /F 2>$null | Out-Null
taskkill /IM coder.exe /F 2>$null | Out-Null
Write-Host "Stopping running Hanzo Dev processes..."
taskkill /IM dev-x86_64-pc-windows-msvc.exe /F 2>$null | Out-Null
taskkill /IM dev.exe /F 2>$null | Out-Null
Write-Host "Removing old global package (if present)..."
$npmRoot = (& npm root -g).Trim()
$pkgPath = Join-Path $npmRoot "@just-every\code"
$pkgPath = Join-Path $npmRoot "@hanzo\dev"
if (Test-Path $pkgPath) {
try { Remove-Item -LiteralPath $pkgPath -Recurse -Force -ErrorAction Stop } catch {}
}
Write-Host "Removing temp staging directories (if present)..."
Get-ChildItem -LiteralPath (Join-Path $npmRoot "@just-every") -Force -ErrorAction SilentlyContinue |
Where-Object { $_.Name -like '.code-*' } |
Get-ChildItem -LiteralPath (Join-Path $npmRoot "@hanzo") -Force -ErrorAction SilentlyContinue |
Where-Object { $_.Name -like '.dev-*' } |
ForEach-Object {
try { Remove-Item -LiteralPath $_.FullName -Recurse -Force -ErrorAction Stop } catch {}
}
Write-Host "Cleanup complete. You can now run: npm install -g @just-every/code@latest"
Write-Host "Cleanup complete. You can now run: npm install -g @hanzo/dev@latest"