@hanzo/dev 3.0.11 → 3.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +493 -36
- package/bin/{dev.js → coder.js} +95 -38
- package/bin/codex.js +197 -0
- package/package.json +11 -11
- package/postinstall.js +501 -442
- package/scripts/preinstall.js +5 -5
- package/scripts/windows-cleanup.ps1 +7 -6
package/bin/{dev.js → coder.js}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Unified entry point for the
|
|
2
|
+
// Unified entry point for the Code CLI (fork of OpenAI Codex).
|
|
3
3
|
|
|
4
4
|
import path from "path";
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
@@ -14,6 +14,10 @@ const __dirname = path.dirname(__filename);
|
|
|
14
14
|
|
|
15
15
|
const { platform, arch } = process;
|
|
16
16
|
|
|
17
|
+
// Important: Never delegate to another system's `code` binary (e.g., VS Code).
|
|
18
|
+
// When users run via `npx @just-every/code`, we must always execute our
|
|
19
|
+
// packaged native binary by absolute path to avoid PATH collisions.
|
|
20
|
+
|
|
17
21
|
function isWSL() {
|
|
18
22
|
if (platform !== "linux") return false;
|
|
19
23
|
try {
|
|
@@ -70,10 +74,11 @@ if (!targetTriple) {
|
|
|
70
74
|
throw new Error(`Unsupported platform: ${platform} (${arch})`);
|
|
71
75
|
}
|
|
72
76
|
|
|
73
|
-
//
|
|
74
|
-
let binaryPath = path.join(__dirname, "..", "bin", `
|
|
75
|
-
let legacyBinaryPath = path.join(__dirname, "..", "bin", `
|
|
77
|
+
// Prefer new 'code-*' binary names; fall back to legacy 'coder-*' if missing.
|
|
78
|
+
let binaryPath = path.join(__dirname, "..", "bin", `code-${targetTriple}`);
|
|
79
|
+
let legacyBinaryPath = path.join(__dirname, "..", "bin", `coder-${targetTriple}`);
|
|
76
80
|
|
|
81
|
+
// --- Bootstrap helper (runs if the binary is missing, e.g. Bun blocked postinstall) ---
|
|
77
82
|
import { existsSync, chmodSync, statSync, openSync, readSync, closeSync, mkdirSync, copyFileSync, readFileSync, unlinkSync, createWriteStream } from "fs";
|
|
78
83
|
|
|
79
84
|
const validateBinary = (p) => {
|
|
@@ -116,14 +121,16 @@ const getCacheDir = (version) => {
|
|
|
116
121
|
} else {
|
|
117
122
|
base = process.env.XDG_CACHE_HOME || path.join(home, ".cache");
|
|
118
123
|
}
|
|
119
|
-
const dir = path.join(base, "
|
|
124
|
+
const dir = path.join(base, "just-every", "code", version);
|
|
120
125
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
121
126
|
return dir;
|
|
122
127
|
};
|
|
123
128
|
|
|
124
129
|
const getCachedBinaryPath = (version) => {
|
|
130
|
+
// targetTriple already includes the proper extension on Windows ("...msvc.exe").
|
|
131
|
+
// Do not append another suffix; just use the exact targetTriple-derived name.
|
|
125
132
|
const cacheDir = getCacheDir(version);
|
|
126
|
-
return path.join(cacheDir, `
|
|
133
|
+
return path.join(cacheDir, `code-${targetTriple}`);
|
|
127
134
|
};
|
|
128
135
|
|
|
129
136
|
let lastBootstrapError = null;
|
|
@@ -132,6 +139,7 @@ const httpsDownload = (url, dest) => new Promise((resolve, reject) => {
|
|
|
132
139
|
const req = httpsGet(url, (res) => {
|
|
133
140
|
const status = res.statusCode || 0;
|
|
134
141
|
if (status >= 300 && status < 400 && res.headers.location) {
|
|
142
|
+
// follow one redirect recursively
|
|
135
143
|
return resolve(httpsDownload(res.headers.location, dest));
|
|
136
144
|
}
|
|
137
145
|
if (status !== 200) {
|
|
@@ -156,17 +164,19 @@ const httpsDownload = (url, dest) => new Promise((resolve, reject) => {
|
|
|
156
164
|
|
|
157
165
|
const tryBootstrapBinary = async () => {
|
|
158
166
|
try {
|
|
167
|
+
// 1) Read our published version
|
|
159
168
|
const pkg = JSON.parse(readFileSync(path.join(__dirname, "..", "package.json"), "utf8"));
|
|
160
169
|
const version = pkg.version;
|
|
161
170
|
|
|
162
171
|
const binDir = path.join(__dirname, "..", "bin");
|
|
163
172
|
if (!existsSync(binDir)) mkdirSync(binDir, { recursive: true });
|
|
164
173
|
|
|
165
|
-
// Fast path: user cache
|
|
174
|
+
// 2) Fast path: user cache
|
|
166
175
|
const cachePath = getCachedBinaryPath(version);
|
|
167
176
|
if (existsSync(cachePath)) {
|
|
168
177
|
const v = validateBinary(cachePath);
|
|
169
178
|
if (v.ok) {
|
|
179
|
+
// Prefer running directly from cache; mirror into node_modules on Unix
|
|
170
180
|
if (platform !== "win32") {
|
|
171
181
|
copyFileSync(cachePath, binaryPath);
|
|
172
182
|
try { chmodSync(binaryPath, 0o755); } catch {}
|
|
@@ -175,25 +185,26 @@ const tryBootstrapBinary = async () => {
|
|
|
175
185
|
}
|
|
176
186
|
}
|
|
177
187
|
|
|
178
|
-
// Try platform package (if present)
|
|
188
|
+
// 3) Try platform package (if present)
|
|
179
189
|
try {
|
|
180
190
|
const req = (await import("module")).createRequire(import.meta.url);
|
|
181
191
|
const name = (() => {
|
|
182
|
-
if (platform === "win32") return "@
|
|
192
|
+
if (platform === "win32") return "@just-every/code-win32-x64"; // may be unpublished; falls through
|
|
183
193
|
const plt = nodePlatform();
|
|
184
194
|
const cpu = nodeArch();
|
|
185
|
-
if (plt === "darwin" && cpu === "arm64") return "@
|
|
186
|
-
if (plt === "darwin" && cpu === "x64") return "@
|
|
187
|
-
if (plt === "linux" && cpu === "x64") return "@
|
|
188
|
-
if (plt === "linux" && cpu === "arm64") return "@
|
|
195
|
+
if (plt === "darwin" && cpu === "arm64") return "@just-every/code-darwin-arm64";
|
|
196
|
+
if (plt === "darwin" && cpu === "x64") return "@just-every/code-darwin-x64";
|
|
197
|
+
if (plt === "linux" && cpu === "x64") return "@just-every/code-linux-x64-musl";
|
|
198
|
+
if (plt === "linux" && cpu === "arm64") return "@just-every/code-linux-arm64-musl";
|
|
189
199
|
return null;
|
|
190
200
|
})();
|
|
191
201
|
if (name) {
|
|
192
202
|
try {
|
|
193
203
|
const pkgJson = req.resolve(`${name}/package.json`);
|
|
194
204
|
const pkgDir = path.dirname(pkgJson);
|
|
195
|
-
const src = path.join(pkgDir, "bin", `
|
|
205
|
+
const src = path.join(pkgDir, "bin", `code-${targetTriple}`);
|
|
196
206
|
if (existsSync(src)) {
|
|
207
|
+
// Always ensure cache has the binary; on Unix mirror into node_modules
|
|
197
208
|
copyFileSync(src, cachePath);
|
|
198
209
|
if (platform !== "win32") {
|
|
199
210
|
copyFileSync(cachePath, binaryPath);
|
|
@@ -205,22 +216,21 @@ const tryBootstrapBinary = async () => {
|
|
|
205
216
|
}
|
|
206
217
|
} catch { /* ignore */ }
|
|
207
218
|
|
|
208
|
-
// Download from GitHub release
|
|
219
|
+
// 4) Download from GitHub release
|
|
209
220
|
const isWin = platform === "win32";
|
|
210
|
-
// Use 'code-*' binary names since that's what the release produces
|
|
211
|
-
const binaryName = `code-${targetTriple}`;
|
|
212
221
|
const archiveName = isWin
|
|
213
|
-
?
|
|
214
|
-
: (() => { try { execSync("zstd --version", { stdio: "ignore", shell: true }); return
|
|
215
|
-
const url = `https://github.com/
|
|
222
|
+
? `code-${targetTriple}.zip`
|
|
223
|
+
: (() => { try { execSync("zstd --version", { stdio: "ignore", shell: true }); return `code-${targetTriple}.zst`; } catch { return `code-${targetTriple}.tar.gz`; } })();
|
|
224
|
+
const url = `https://github.com/just-every/code/releases/download/v${version}/${archiveName}`;
|
|
216
225
|
const tmp = path.join(binDir, `.${archiveName}.part`);
|
|
217
226
|
return httpsDownload(url, tmp)
|
|
218
227
|
.then(() => {
|
|
219
228
|
if (isWin) {
|
|
229
|
+
// Extract zip with robust fallbacks and use a safe temp dir, then move to cache
|
|
220
230
|
try {
|
|
221
|
-
const sysRoot = process.env.SystemRoot || process.env.windir || 'C
|
|
231
|
+
const sysRoot = process.env.SystemRoot || process.env.windir || 'C:\\\Windows';
|
|
222
232
|
const psFull = path.join(sysRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe');
|
|
223
|
-
const unzipDest = getCacheDir(version);
|
|
233
|
+
const unzipDest = getCacheDir(version); // extract directly to cache location
|
|
224
234
|
const psCmd = `Expand-Archive -Path '${tmp}' -DestinationPath '${unzipDest}' -Force`;
|
|
225
235
|
let ok = false;
|
|
226
236
|
try { execSync(`"${psFull}" -NoProfile -NonInteractive -Command "${psCmd}"`, { stdio: 'ignore' }); ok = true; } catch {}
|
|
@@ -241,7 +251,11 @@ const tryBootstrapBinary = async () => {
|
|
|
241
251
|
try { unlinkSync(tmp); } catch {}
|
|
242
252
|
}
|
|
243
253
|
}
|
|
244
|
-
|
|
254
|
+
// On Windows, the file was extracted directly into the cache dir
|
|
255
|
+
if (platform === "win32") {
|
|
256
|
+
// Ensure the expected filename exists in cache; Expand-Archive extracts exact name
|
|
257
|
+
// No action required here; validation occurs below against cachePath
|
|
258
|
+
} else {
|
|
245
259
|
try { copyFileSync(binaryPath, cachePath); } catch {}
|
|
246
260
|
}
|
|
247
261
|
|
|
@@ -256,7 +270,7 @@ const tryBootstrapBinary = async () => {
|
|
|
256
270
|
}
|
|
257
271
|
};
|
|
258
272
|
|
|
259
|
-
// If missing, attempt to bootstrap into place
|
|
273
|
+
// If missing, attempt to bootstrap into place (helps when Bun blocks postinstall)
|
|
260
274
|
let binaryReady = existsSync(binaryPath) || existsSync(legacyBinaryPath);
|
|
261
275
|
if (!binaryReady) {
|
|
262
276
|
let runtimePostinstallError = null;
|
|
@@ -273,6 +287,7 @@ if (!binaryReady) {
|
|
|
273
287
|
if (runtimePostinstallError && !lastBootstrapError) {
|
|
274
288
|
lastBootstrapError = runtimePostinstallError;
|
|
275
289
|
}
|
|
290
|
+
// retry legacy name in case archive provided coder-*
|
|
276
291
|
if (existsSync(legacyBinaryPath) && !existsSync(binaryPath)) {
|
|
277
292
|
binaryPath = legacyBinaryPath;
|
|
278
293
|
}
|
|
@@ -295,14 +310,17 @@ try {
|
|
|
295
310
|
// ignore
|
|
296
311
|
}
|
|
297
312
|
|
|
313
|
+
// Check if binary exists and try to fix permissions if needed
|
|
314
|
+
// fs imports are above; keep for readability if tree-shaken by bundlers
|
|
298
315
|
import { spawnSync } from "child_process";
|
|
299
316
|
if (existsSync(binaryPath)) {
|
|
300
317
|
try {
|
|
318
|
+
// Ensure binary is executable on Unix-like systems
|
|
301
319
|
if (platform !== "win32") {
|
|
302
320
|
chmodSync(binaryPath, 0o755);
|
|
303
321
|
}
|
|
304
322
|
} catch (e) {
|
|
305
|
-
// Ignore permission errors
|
|
323
|
+
// Ignore permission errors, will be caught below if it's a real problem
|
|
306
324
|
}
|
|
307
325
|
} else {
|
|
308
326
|
console.error(`Binary not found: ${binaryPath}`);
|
|
@@ -311,69 +329,97 @@ if (existsSync(binaryPath)) {
|
|
|
311
329
|
console.error(`Bootstrap error: ${msg}`);
|
|
312
330
|
}
|
|
313
331
|
console.error(`Please try reinstalling the package:`);
|
|
314
|
-
console.error(` npm uninstall -g @
|
|
315
|
-
console.error(` npm install -g @
|
|
332
|
+
console.error(` npm uninstall -g @just-every/code`);
|
|
333
|
+
console.error(` npm install -g @just-every/code`);
|
|
316
334
|
if (isWSL()) {
|
|
317
335
|
console.error("Detected WSL. Install inside WSL (Ubuntu) separately:");
|
|
318
|
-
console.error(" npx -y @
|
|
336
|
+
console.error(" npx -y @just-every/code@latest (run inside WSL)");
|
|
319
337
|
console.error("If installed globally on Windows, those binaries are not usable from WSL.");
|
|
320
338
|
}
|
|
321
339
|
process.exit(1);
|
|
322
340
|
}
|
|
323
341
|
|
|
342
|
+
// Lightweight header validation to provide clearer errors before spawn
|
|
343
|
+
// Reuse the validateBinary helper defined above in the bootstrap section.
|
|
344
|
+
|
|
324
345
|
const validation = validateBinary(binaryPath);
|
|
325
346
|
if (!validation.ok) {
|
|
326
347
|
console.error(`The native binary at ${binaryPath} appears invalid: ${validation.reason}`);
|
|
327
348
|
console.error("This can happen if the download failed or was modified by antivirus/proxy.");
|
|
328
349
|
console.error("Please try reinstalling:");
|
|
329
|
-
console.error(" npm uninstall -g @
|
|
330
|
-
console.error(" npm install -g @
|
|
350
|
+
console.error(" npm uninstall -g @just-every/code");
|
|
351
|
+
console.error(" npm install -g @just-every/code");
|
|
331
352
|
if (platform === "win32") {
|
|
332
353
|
console.error("If the issue persists, clear npm cache and disable antivirus temporarily:");
|
|
333
354
|
console.error(" npm cache clean --force");
|
|
334
355
|
}
|
|
335
356
|
if (isWSL()) {
|
|
336
357
|
console.error("Detected WSL. Ensure you install/run inside WSL, not Windows:");
|
|
337
|
-
console.error(" npx -y @
|
|
358
|
+
console.error(" npx -y @just-every/code@latest (inside WSL)");
|
|
338
359
|
}
|
|
339
360
|
process.exit(1);
|
|
340
361
|
}
|
|
341
362
|
|
|
342
|
-
// If running under npx/npm, emit a concise notice
|
|
363
|
+
// If running under npx/npm, emit a concise notice about which binary path is used
|
|
343
364
|
try {
|
|
344
365
|
const ua = process.env.npm_config_user_agent || "";
|
|
345
366
|
const isNpx = ua.includes("npx");
|
|
346
367
|
if (isNpx && process.stderr && process.stderr.isTTY) {
|
|
347
|
-
|
|
368
|
+
// Best-effort discovery of another 'code' on PATH for user clarity
|
|
369
|
+
let otherCode = "";
|
|
370
|
+
try {
|
|
371
|
+
const cmd = process.platform === "win32" ? "where code" : "command -v code || which code || true";
|
|
372
|
+
const out = spawnSync(process.platform === "win32" ? "cmd" : "bash", [
|
|
373
|
+
process.platform === "win32" ? "/c" : "-lc",
|
|
374
|
+
cmd,
|
|
375
|
+
], { encoding: "utf8" });
|
|
376
|
+
const line = (out.stdout || "").split(/\r?\n/).map((s) => s.trim()).filter(Boolean)[0];
|
|
377
|
+
if (line && !line.includes("@just-every/code")) {
|
|
378
|
+
otherCode = line;
|
|
379
|
+
}
|
|
380
|
+
} catch {}
|
|
381
|
+
if (otherCode) {
|
|
382
|
+
console.error(`@just-every/code: running bundled binary -> ${binaryPath}`);
|
|
383
|
+
console.error(`Note: a different 'code' exists at ${otherCode}; not delegating.`);
|
|
384
|
+
} else {
|
|
385
|
+
console.error(`@just-every/code: running bundled binary -> ${binaryPath}`);
|
|
386
|
+
}
|
|
348
387
|
}
|
|
349
388
|
} catch {}
|
|
350
389
|
|
|
390
|
+
// Use an asynchronous spawn instead of spawnSync so that Node is able to
|
|
391
|
+
// respond to signals (e.g. Ctrl-C / SIGINT) while the native binary is
|
|
392
|
+
// executing. This allows us to forward those signals to the child process
|
|
393
|
+
// and guarantees that when either the child terminates or the parent
|
|
394
|
+
// receives a fatal signal, both processes exit in a predictable manner.
|
|
351
395
|
const { spawn } = await import("child_process");
|
|
352
396
|
|
|
353
|
-
|
|
397
|
+
// Make the resolved native binary path visible to spawned agents/subprocesses.
|
|
398
|
+
process.env.CODE_BINARY_PATH = binaryPath;
|
|
354
399
|
|
|
355
400
|
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
356
401
|
stdio: "inherit",
|
|
357
|
-
env: { ...process.env,
|
|
402
|
+
env: { ...process.env, CODER_MANAGED_BY_NPM: "1", CODEX_MANAGED_BY_NPM: "1", CODE_BINARY_PATH: binaryPath },
|
|
358
403
|
});
|
|
359
404
|
|
|
360
405
|
child.on("error", (err) => {
|
|
406
|
+
// Typically triggered when the binary is missing or not executable.
|
|
361
407
|
const code = err && err.code;
|
|
362
408
|
if (code === 'EACCES') {
|
|
363
409
|
console.error(`Permission denied: ${binaryPath}`);
|
|
364
410
|
console.error(`Try running: chmod +x "${binaryPath}"`);
|
|
365
|
-
console.error(`Or reinstall the package with: npm install -g @
|
|
411
|
+
console.error(`Or reinstall the package with: npm install -g @just-every/code`);
|
|
366
412
|
} else if (code === 'EFTYPE' || code === 'ENOEXEC') {
|
|
367
413
|
console.error(`Failed to execute native binary: ${binaryPath}`);
|
|
368
414
|
console.error("The file may be corrupt or of the wrong type. Reinstall usually fixes this:");
|
|
369
|
-
console.error(" npm uninstall -g @
|
|
415
|
+
console.error(" npm uninstall -g @just-every/code && npm install -g @just-every/code");
|
|
370
416
|
if (platform === 'win32') {
|
|
371
417
|
console.error("On Windows, ensure the .exe downloaded correctly (proxy/AV can interfere).");
|
|
372
418
|
console.error("Try clearing cache: npm cache clean --force");
|
|
373
419
|
}
|
|
374
420
|
if (isWSL()) {
|
|
375
421
|
console.error("Detected WSL. Windows binaries cannot be executed from WSL.");
|
|
376
|
-
console.error("Install inside WSL and run there: npx -y @
|
|
422
|
+
console.error("Install inside WSL and run there: npx -y @just-every/code@latest");
|
|
377
423
|
}
|
|
378
424
|
} else {
|
|
379
425
|
console.error(err);
|
|
@@ -381,6 +427,10 @@ child.on("error", (err) => {
|
|
|
381
427
|
process.exit(1);
|
|
382
428
|
});
|
|
383
429
|
|
|
430
|
+
// Forward common termination signals to the child so that it shuts down
|
|
431
|
+
// gracefully. In the handler we temporarily disable the default behavior of
|
|
432
|
+
// exiting immediately; once the child has been signaled we simply wait for
|
|
433
|
+
// its exit event which will in turn terminate the parent (see below).
|
|
384
434
|
const forwardSignal = (signal) => {
|
|
385
435
|
if (child.killed) {
|
|
386
436
|
return;
|
|
@@ -396,6 +446,11 @@ const forwardSignal = (signal) => {
|
|
|
396
446
|
process.on(sig, () => forwardSignal(sig));
|
|
397
447
|
});
|
|
398
448
|
|
|
449
|
+
// When the child exits, mirror its termination reason in the parent so that
|
|
450
|
+
// shell scripts and other tooling observe the correct exit status.
|
|
451
|
+
// Wrap the lifetime of the child process in a Promise so that we can await
|
|
452
|
+
// its termination in a structured way. The Promise resolves with an object
|
|
453
|
+
// describing how the child exited: either via exit code or due to a signal.
|
|
399
454
|
const childResult = await new Promise((resolve) => {
|
|
400
455
|
child.on("exit", (code, signal) => {
|
|
401
456
|
if (signal) {
|
|
@@ -407,6 +462,8 @@ const childResult = await new Promise((resolve) => {
|
|
|
407
462
|
});
|
|
408
463
|
|
|
409
464
|
if (childResult.type === "signal") {
|
|
465
|
+
// Re-emit the same signal so that the parent terminates with the expected
|
|
466
|
+
// semantics (this also sets the correct exit code of 128 + n).
|
|
410
467
|
process.kill(process.pid, childResult.signal);
|
|
411
468
|
} else {
|
|
412
469
|
process.exit(childResult.exitCode);
|
package/bin/codex.js
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Unified entry point for the Codex CLI.
|
|
3
|
+
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { existsSync } from "fs";
|
|
6
|
+
import { createRequire } from "node:module";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
|
|
10
|
+
// __dirname equivalent in ESM
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = path.dirname(__filename);
|
|
13
|
+
const require = createRequire(import.meta.url);
|
|
14
|
+
|
|
15
|
+
const PLATFORM_PACKAGE_BY_TARGET = {
|
|
16
|
+
"x86_64-unknown-linux-musl": "@openai/codex-linux-x64",
|
|
17
|
+
"aarch64-unknown-linux-musl": "@openai/codex-linux-arm64",
|
|
18
|
+
"x86_64-apple-darwin": "@openai/codex-darwin-x64",
|
|
19
|
+
"aarch64-apple-darwin": "@openai/codex-darwin-arm64",
|
|
20
|
+
"x86_64-pc-windows-msvc": "@openai/codex-win32-x64",
|
|
21
|
+
"aarch64-pc-windows-msvc": "@openai/codex-win32-arm64",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const { platform, arch } = process;
|
|
25
|
+
|
|
26
|
+
let targetTriple = null;
|
|
27
|
+
switch (platform) {
|
|
28
|
+
case "linux":
|
|
29
|
+
case "android":
|
|
30
|
+
switch (arch) {
|
|
31
|
+
case "x64":
|
|
32
|
+
targetTriple = "x86_64-unknown-linux-musl";
|
|
33
|
+
break;
|
|
34
|
+
case "arm64":
|
|
35
|
+
targetTriple = "aarch64-unknown-linux-musl";
|
|
36
|
+
break;
|
|
37
|
+
default:
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
break;
|
|
41
|
+
case "darwin":
|
|
42
|
+
switch (arch) {
|
|
43
|
+
case "x64":
|
|
44
|
+
targetTriple = "x86_64-apple-darwin";
|
|
45
|
+
break;
|
|
46
|
+
case "arm64":
|
|
47
|
+
targetTriple = "aarch64-apple-darwin";
|
|
48
|
+
break;
|
|
49
|
+
default:
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
break;
|
|
53
|
+
case "win32":
|
|
54
|
+
switch (arch) {
|
|
55
|
+
case "x64":
|
|
56
|
+
targetTriple = "x86_64-pc-windows-msvc";
|
|
57
|
+
break;
|
|
58
|
+
case "arm64":
|
|
59
|
+
targetTriple = "aarch64-pc-windows-msvc";
|
|
60
|
+
break;
|
|
61
|
+
default:
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
break;
|
|
65
|
+
default:
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!targetTriple) {
|
|
70
|
+
throw new Error(`Unsupported platform: ${platform} (${arch})`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const platformPackage = PLATFORM_PACKAGE_BY_TARGET[targetTriple];
|
|
74
|
+
if (!platformPackage) {
|
|
75
|
+
throw new Error(`Unsupported target triple: ${targetTriple}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const codexBinaryName = process.platform === "win32" ? "codex.exe" : "codex";
|
|
79
|
+
const localVendorRoot = path.join(__dirname, "..", "vendor");
|
|
80
|
+
const localBinaryPath = path.join(
|
|
81
|
+
localVendorRoot,
|
|
82
|
+
targetTriple,
|
|
83
|
+
"codex",
|
|
84
|
+
codexBinaryName,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
let vendorRoot;
|
|
88
|
+
try {
|
|
89
|
+
const packageJsonPath = require.resolve(`${platformPackage}/package.json`);
|
|
90
|
+
vendorRoot = path.join(path.dirname(packageJsonPath), "vendor");
|
|
91
|
+
} catch {
|
|
92
|
+
if (existsSync(localBinaryPath)) {
|
|
93
|
+
vendorRoot = localVendorRoot;
|
|
94
|
+
} else {
|
|
95
|
+
const packageManager = detectPackageManager();
|
|
96
|
+
const updateCommand =
|
|
97
|
+
packageManager === "bun"
|
|
98
|
+
? "bun install -g @openai/codex@latest"
|
|
99
|
+
: "npm install -g @openai/codex@latest";
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Missing optional dependency ${platformPackage}. Reinstall Codex: ${updateCommand}`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!vendorRoot) {
|
|
107
|
+
const packageManager = detectPackageManager();
|
|
108
|
+
const updateCommand =
|
|
109
|
+
packageManager === "bun"
|
|
110
|
+
? "bun install -g @openai/codex@latest"
|
|
111
|
+
: "npm install -g @openai/codex@latest";
|
|
112
|
+
throw new Error(
|
|
113
|
+
`Missing optional dependency ${platformPackage}. Reinstall Codex: ${updateCommand}`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const archRoot = path.join(vendorRoot, targetTriple);
|
|
118
|
+
const binaryPath = path.join(archRoot, "codex", codexBinaryName);
|
|
119
|
+
|
|
120
|
+
// Use an asynchronous spawn instead of spawnSync so that Node is able to
|
|
121
|
+
// respond to signals (e.g. Ctrl-C / SIGINT) while the native binary is
|
|
122
|
+
// executing. This allows us to forward those signals to the child process
|
|
123
|
+
// and guarantees that when either the child terminates or the parent
|
|
124
|
+
// receives a fatal signal, both processes exit in a predictable manner.
|
|
125
|
+
|
|
126
|
+
function getUpdatedPath(newDirs) {
|
|
127
|
+
const pathSep = process.platform === "win32" ? ";" : ":";
|
|
128
|
+
const existingPath = process.env.PATH || "";
|
|
129
|
+
const updatedPath = [
|
|
130
|
+
...newDirs,
|
|
131
|
+
...existingPath.split(pathSep).filter(Boolean),
|
|
132
|
+
].join(pathSep);
|
|
133
|
+
return updatedPath;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const additionalDirs = [];
|
|
137
|
+
const pathDir = path.join(archRoot, "path");
|
|
138
|
+
if (existsSync(pathDir)) {
|
|
139
|
+
additionalDirs.push(pathDir);
|
|
140
|
+
}
|
|
141
|
+
const updatedPath = getUpdatedPath(additionalDirs);
|
|
142
|
+
|
|
143
|
+
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
144
|
+
stdio: "inherit",
|
|
145
|
+
env: { ...process.env, PATH: updatedPath, CODEX_MANAGED_BY_NPM: "1" },
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
child.on("error", (err) => {
|
|
149
|
+
// Typically triggered when the binary is missing or not executable.
|
|
150
|
+
// Re-throwing here will terminate the parent with a non-zero exit code
|
|
151
|
+
// while still printing a helpful stack trace.
|
|
152
|
+
// eslint-disable-next-line no-console
|
|
153
|
+
console.error(err);
|
|
154
|
+
process.exit(1);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Forward common termination signals to the child so that it shuts down
|
|
158
|
+
// gracefully. In the handler we temporarily disable the default behavior of
|
|
159
|
+
// exiting immediately; once the child has been signaled we simply wait for
|
|
160
|
+
// its exit event which will in turn terminate the parent (see below).
|
|
161
|
+
const forwardSignal = (signal) => {
|
|
162
|
+
if (child.killed) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
child.kill(signal);
|
|
167
|
+
} catch {
|
|
168
|
+
/* ignore */
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
["SIGINT", "SIGTERM", "SIGHUP"].forEach((sig) => {
|
|
173
|
+
process.on(sig, () => forwardSignal(sig));
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// When the child exits, mirror its termination reason in the parent so that
|
|
177
|
+
// shell scripts and other tooling observe the correct exit status.
|
|
178
|
+
// Wrap the lifetime of the child process in a Promise so that we can await
|
|
179
|
+
// its termination in a structured way. The Promise resolves with an object
|
|
180
|
+
// describing how the child exited: either via exit code or due to a signal.
|
|
181
|
+
const childResult = await new Promise((resolve) => {
|
|
182
|
+
child.on("exit", (code, signal) => {
|
|
183
|
+
if (signal) {
|
|
184
|
+
resolve({ type: "signal", signal });
|
|
185
|
+
} else {
|
|
186
|
+
resolve({ type: "code", exitCode: code ?? 1 });
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
if (childResult.type === "signal") {
|
|
192
|
+
// Re-emit the same signal so that the parent terminates with the expected
|
|
193
|
+
// semantics (this also sets the correct exit code of 128 + n).
|
|
194
|
+
process.kill(process.pid, childResult.signal);
|
|
195
|
+
} else {
|
|
196
|
+
process.exit(childResult.exitCode);
|
|
197
|
+
}
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/dev",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.12",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "Lightweight coding agent that runs in your terminal - fork of OpenAI Codex",
|
|
6
6
|
"bin": {
|
|
7
|
-
"
|
|
8
|
-
"hanzo": "bin/dev.js"
|
|
7
|
+
"coder": "bin/coder.js"
|
|
9
8
|
},
|
|
10
9
|
"type": "module",
|
|
11
10
|
"engines": {
|
|
12
11
|
"node": ">=16"
|
|
13
12
|
},
|
|
14
13
|
"files": [
|
|
15
|
-
"bin/
|
|
14
|
+
"bin/coder.js",
|
|
15
|
+
"bin/codex.js",
|
|
16
16
|
"postinstall.js",
|
|
17
17
|
"scripts/preinstall.js",
|
|
18
18
|
"scripts/windows-cleanup.ps1",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"scripts": {
|
|
22
22
|
"preinstall": "node scripts/preinstall.js",
|
|
23
23
|
"postinstall": "node postinstall.js",
|
|
24
|
-
"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
|
|
24
|
+
"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...'); }\""
|
|
25
25
|
},
|
|
26
26
|
"repository": {
|
|
27
27
|
"type": "git",
|
|
@@ -35,10 +35,10 @@
|
|
|
35
35
|
"prettier": "^3.3.3"
|
|
36
36
|
},
|
|
37
37
|
"optionalDependencies": {
|
|
38
|
-
"@hanzo/dev-darwin-arm64": "3.0.
|
|
39
|
-
"@hanzo/dev-darwin-x64": "3.0.
|
|
40
|
-
"@hanzo/dev-linux-x64-musl": "3.0.
|
|
41
|
-
"@hanzo/dev-linux-arm64-musl": "3.0.
|
|
42
|
-
"@hanzo/dev-win32-x64": "3.0.
|
|
38
|
+
"@hanzo/dev-darwin-arm64": "3.0.12",
|
|
39
|
+
"@hanzo/dev-darwin-x64": "3.0.12",
|
|
40
|
+
"@hanzo/dev-linux-x64-musl": "3.0.12",
|
|
41
|
+
"@hanzo/dev-linux-arm64-musl": "3.0.12",
|
|
42
|
+
"@hanzo/dev-win32-x64": "3.0.12"
|
|
43
43
|
}
|
|
44
44
|
}
|