@hanzo/dev 0.6.92 → 0.6.93

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/bin/coder.js CHANGED
@@ -6,6 +6,7 @@ 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";
9
10
 
10
11
  // __dirname equivalent in ESM
11
12
  const __filename = fileURLToPath(import.meta.url);
@@ -17,7 +18,7 @@ const { platform, arch } = process;
17
18
  // When users run via `npx @hanzo/dev`, we must always execute our
18
19
  // packaged native binary by absolute path to avoid PATH collisions.
19
20
 
20
- const isWSL = () => {
21
+ function isWSL() {
21
22
  if (platform !== "linux") return false;
22
23
  try {
23
24
  const txt = readFileSync("/proc/version", "utf8").toLowerCase();
@@ -25,7 +26,7 @@ const isWSL = () => {
25
26
  } catch {
26
27
  return false;
27
28
  }
28
- };
29
+ }
29
30
 
30
31
  let targetTriple = null;
31
32
  switch (platform) {
@@ -270,12 +271,26 @@ const tryBootstrapBinary = async () => {
270
271
  };
271
272
 
272
273
  // 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;
274
+ let binaryReady = existsSync(binaryPath) || existsSync(legacyBinaryPath);
275
+ if (!binaryReady) {
276
+ let runtimePostinstallError = null;
277
+ try {
278
+ await runPostinstall({ invokedByRuntime: true, skipGlobalAlias: true });
279
+ } catch (err) {
280
+ runtimePostinstallError = err;
281
+ }
282
+
283
+ binaryReady = existsSync(binaryPath) || existsSync(legacyBinaryPath);
284
+ if (!binaryReady) {
285
+ const ok = await tryBootstrapBinary();
286
+ if (!ok) {
287
+ if (runtimePostinstallError && !lastBootstrapError) {
288
+ lastBootstrapError = runtimePostinstallError;
289
+ }
290
+ // retry legacy name in case archive provided coder-*
291
+ if (existsSync(legacyBinaryPath) && !existsSync(binaryPath)) {
292
+ binaryPath = legacyBinaryPath;
293
+ }
279
294
  }
280
295
  }
281
296
  }
@@ -379,9 +394,12 @@ try {
379
394
  // receives a fatal signal, both processes exit in a predictable manner.
380
395
  const { spawn } = await import("child_process");
381
396
 
397
+ // Make the resolved native binary path visible to spawned agents/subprocesses.
398
+ process.env.CODE_BINARY_PATH = binaryPath;
399
+
382
400
  const child = spawn(binaryPath, process.argv.slice(2), {
383
401
  stdio: "inherit",
384
- env: { ...process.env, CODER_MANAGED_BY_NPM: "1", CODEX_MANAGED_BY_NPM: "1" },
402
+ env: { ...process.env, CODER_MANAGED_BY_NPM: "1", CODEX_MANAGED_BY_NPM: "1", CODE_BINARY_PATH: binaryPath },
385
403
  });
386
404
 
387
405
  child.on("error", (err) => {
package/bin/codex.js CHANGED
@@ -3,12 +3,24 @@
3
3
 
4
4
  import { spawn } from "node:child_process";
5
5
  import { existsSync } from "fs";
6
+ import { createRequire } from "node:module";
6
7
  import path from "path";
7
8
  import { fileURLToPath } from "url";
8
9
 
9
10
  // __dirname equivalent in ESM
10
11
  const __filename = fileURLToPath(import.meta.url);
11
12
  const __dirname = path.dirname(__filename);
13
+ const require = createRequire(import.meta.url);
14
+ const codexPackageRoot = realpathSync(path.join(__dirname, ".."));
15
+
16
+ const PLATFORM_PACKAGE_BY_TARGET = {
17
+ "x86_64-unknown-linux-musl": "@openai/codex-linux-x64",
18
+ "aarch64-unknown-linux-musl": "@openai/codex-linux-arm64",
19
+ "x86_64-apple-darwin": "@openai/codex-darwin-x64",
20
+ "aarch64-apple-darwin": "@openai/codex-darwin-arm64",
21
+ "x86_64-pc-windows-msvc": "@openai/codex-win32-x64",
22
+ "aarch64-pc-windows-msvc": "@openai/codex-win32-arm64",
23
+ };
12
24
 
13
25
  const { platform, arch } = process;
14
26
 
@@ -59,9 +71,53 @@ if (!targetTriple) {
59
71
  throw new Error(`Unsupported platform: ${platform} (${arch})`);
60
72
  }
61
73
 
62
- const vendorRoot = path.join(__dirname, "..", "vendor");
63
- const archRoot = path.join(vendorRoot, targetTriple);
74
+ const platformPackage = PLATFORM_PACKAGE_BY_TARGET[targetTriple];
75
+ if (!platformPackage) {
76
+ throw new Error(`Unsupported target triple: ${targetTriple}`);
77
+ }
78
+
64
79
  const codexBinaryName = process.platform === "win32" ? "codex.exe" : "codex";
80
+ const localVendorRoot = path.join(__dirname, "..", "vendor");
81
+ const localBinaryPath = path.join(
82
+ localVendorRoot,
83
+ targetTriple,
84
+ "codex",
85
+ codexBinaryName,
86
+ );
87
+
88
+ let vendorRoot;
89
+ try {
90
+ const packageJsonPath = require.resolve(`${platformPackage}/package.json`);
91
+ vendorRoot = path.join(path.dirname(packageJsonPath), "vendor");
92
+ } catch {
93
+ if (existsSync(localBinaryPath)) {
94
+ vendorRoot = localVendorRoot;
95
+ } else {
96
+ const packageManager = detectPackageManager();
97
+ const updateCommand =
98
+ packageManager === "bun"
99
+ ? "bun install -g @openai/codex@latest"
100
+ : "npm install -g @openai/codex@latest";
101
+ throw new Error(
102
+ `Missing optional dependency ${platformPackage}. Reinstall Codex: ${updateCommand}`,
103
+ );
104
+ }
105
+ }
106
+
107
+ if (!vendorRoot) {
108
+ const packageManager = detectPackageManager();
109
+ const updateCommand =
110
+ packageManager === "bun"
111
+ ? "bun install -g @openai/codex@latest"
112
+ : packageManager === "pnpm"
113
+ ? "pnpm add -g @openai/codex@latest"
114
+ : "npm install -g @openai/codex@latest";
115
+ throw new Error(
116
+ `Missing optional dependency ${platformPackage}. Reinstall Codex: ${updateCommand}`,
117
+ );
118
+ }
119
+
120
+ const archRoot = path.join(vendorRoot, targetTriple);
65
121
  const binaryPath = path.join(archRoot, "codex", codexBinaryName);
66
122
 
67
123
  // Use an asynchronous spawn instead of spawnSync so that Node is able to
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanzo/dev",
3
- "version": "0.6.92",
3
+ "version": "0.6.93",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Local AI coding agent for your terminal, powered by Hanzo AI",
6
6
  "bin": {
@@ -35,10 +35,10 @@
35
35
  "prettier": "^3.3.3"
36
36
  },
37
37
  "optionalDependencies": {
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"
38
+ "@hanzo/dev-darwin-arm64": "0.6.93",
39
+ "@hanzo/dev-darwin-x64": "0.6.93",
40
+ "@hanzo/dev-linux-x64-musl": "0.6.93",
41
+ "@hanzo/dev-linux-arm64-musl": "0.6.93",
42
+ "@hanzo/dev-win32-x64": "0.6.93"
43
43
  }
44
44
  }
package/postinstall.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // Non-functional change to trigger release workflow
3
3
 
4
4
  import { existsSync, mkdirSync, createWriteStream, chmodSync, readFileSync, readSync, writeFileSync, unlinkSync, statSync, openSync, closeSync, copyFileSync, fsyncSync, renameSync, realpathSync } from 'fs';
5
- import { join, dirname } from 'path';
5
+ import { join, dirname, resolve } from 'path';
6
6
  import { fileURLToPath } from 'url';
7
7
  import { get } from 'https';
8
8
  import { platform, arch, tmpdir } from 'os';
@@ -289,13 +289,21 @@ function validateDownloadedBinary(p) {
289
289
  }
290
290
  }
291
291
 
292
- async function main() {
292
+ export async function runPostinstall(options = {}) {
293
+ const { skipGlobalAlias = false, invokedByRuntime = false } = options;
294
+ if (process.env.CODE_POSTINSTALL_DRY_RUN === '1') {
295
+ return { skipped: true };
296
+ }
297
+
298
+ if (invokedByRuntime) {
299
+ process.env.CODE_RUNTIME_POSTINSTALL = process.env.CODE_RUNTIME_POSTINSTALL || '1';
300
+ }
293
301
  // Detect potential PATH conflict with an existing `code` command (e.g., VS Code)
294
302
  // Only relevant for global installs; skip for npx/local installs to keep postinstall fast.
295
303
  const ua = process.env.npm_config_user_agent || '';
296
304
  const isNpx = ua.includes('npx');
297
305
  const isGlobal = process.env.npm_config_global === 'true';
298
- if (isGlobal && !isNpx) {
306
+ if (!skipGlobalAlias && isGlobal && !isNpx) {
299
307
  try {
300
308
  const whichCmd = process.platform === 'win32' ? 'where code' : 'command -v code || which code || true';
301
309
  const resolved = execSync(whichCmd, { stdio: ['ignore', 'pipe', 'ignore'], shell: process.platform !== 'win32' }).toString().split(/\r?\n/).filter(Boolean)[0];
@@ -421,8 +429,15 @@ async function main() {
421
429
  // - Windows: .zip
422
430
  // - macOS/Linux: prefer .zst if `zstd` CLI is available; otherwise use .tar.gz
423
431
  const isWin = isWindows;
432
+ const detectedWSL = (() => {
433
+ if (platform() !== 'linux') return false;
434
+ try {
435
+ const ver = readFileSync('/proc/version', 'utf8').toLowerCase();
436
+ return ver.includes('microsoft') || !!process.env.WSL_DISTRO_NAME;
437
+ } catch { return false; }
438
+ })();
424
439
  const binDirReal = (() => { try { return realpathSync(binDir); } catch { return binDir; } })();
425
- const mirrorToLocal = !(isWin || (isWSL() && isPathOnWindowsFs(binDirReal)));
440
+ const mirrorToLocal = !(isWin || (detectedWSL && isPathOnWindowsFs(binDirReal)));
426
441
  let useZst = false;
427
442
  if (!isWin) {
428
443
  try {
@@ -755,7 +770,19 @@ async function main() {
755
770
  }
756
771
  }
757
772
 
758
- main().catch(error => {
759
- console.error('Installation failed:', error);
760
- process.exit(1);
761
- });
773
+ function isExecutedDirectly() {
774
+ const entry = process.argv[1];
775
+ if (!entry) return false;
776
+ try {
777
+ return resolve(entry) === fileURLToPath(import.meta.url);
778
+ } catch {
779
+ return false;
780
+ }
781
+ }
782
+
783
+ if (isExecutedDirectly()) {
784
+ runPostinstall().catch(error => {
785
+ console.error('Installation failed:', error);
786
+ process.exit(1);
787
+ });
788
+ }