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