Files
dev/npm/scripts/preinstall.js
zeekay b31cb86dad chore: nothing in the root says whose agent this used to be
`codex-cli/` published `@hanzo/dev`, so the directory carrying our npm
package was named after someone else's product. It is `npm/`.
`patches/codex.json` is `patches/upstream.json` — it anchors edits against
upstream, and the file should say so rather than name one of them.

Deleted outright: `justfile`, whose first line was
`set working-directory := "codex-rs"` for a directory this repository has
never had; `announcement_tip.toml`, an example file for a TUI we do not
ship; and `.codex/` and `.code/`, which held upstream's own process skills
— babysit-pr, codex-bug, pushing-ci-changes — that were never ours.

Three branding leaks a real run turned up, none of which the help text
showed: `dev exec` announced "OpenAI Codex v0.7.0-dev.1" as its first
line, the status card was missed when the session header was anchored,
and both bubblewrap warnings said "Codex's Linux sandbox".

README: say what ships. It claimed the agent drives a browser, which is
the port that has not landed, and showed a screenshot of it doing so.
Both are gone until the bridge is back. The model count is "hundreds" —
`/v1/models` answers 458 today, an earlier line here said "over 500", and
hanzo.ai derives its own count precisely because typed ones drift.
Document the npm install beside the curl one; both give the same build.
2026-09-07 20:32:32 -07:00

70 lines
2.6 KiB
JavaScript

#!/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 dev-x86_64-pc-windows-msvc.exe /F');
}
// 2) Remove stale staging dirs from previous failed installs under the global
// @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, '@hanzo');
} 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('.dev-')) 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);