@hanzo/dev 3.0.9 → 3.0.10

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.
Files changed (2) hide show
  1. package/package.json +6 -6
  2. package/postinstall.js +404 -190
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanzo/dev",
3
- "version": "3.0.9",
3
+ "version": "3.0.10",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Hanzo AI coding assistant - intelligent CLI for developers",
6
6
  "bin": {
@@ -35,10 +35,10 @@
35
35
  "prettier": "^3.3.3"
36
36
  },
37
37
  "optionalDependencies": {
38
- "@hanzo/dev-darwin-arm64": "3.0.9",
39
- "@hanzo/dev-darwin-x64": "3.0.9",
40
- "@hanzo/dev-linux-x64-musl": "3.0.9",
41
- "@hanzo/dev-linux-arm64-musl": "3.0.9",
42
- "@hanzo/dev-win32-x64": "3.0.9"
38
+ "@hanzo/dev-darwin-arm64": "3.0.10",
39
+ "@hanzo/dev-darwin-x64": "3.0.10",
40
+ "@hanzo/dev-linux-x64-musl": "3.0.10",
41
+ "@hanzo/dev-linux-arm64-musl": "3.0.10",
42
+ "@hanzo/dev-win32-x64": "3.0.10"
43
43
  }
44
44
  }
package/postinstall.js CHANGED
@@ -1,26 +1,42 @@
1
1
  #!/usr/bin/env node
2
2
  // Postinstall script for @hanzo/dev
3
3
 
4
- import { existsSync, mkdirSync, createWriteStream, chmodSync, readFileSync, readSync, writeFileSync, unlinkSync, statSync, openSync, closeSync, copyFileSync, fsyncSync, renameSync, realpathSync } from 'fs';
5
- import { join, dirname, resolve } from 'path';
6
- import { fileURLToPath } from 'url';
7
- import { get } from 'https';
8
- import { platform, arch, tmpdir } from 'os';
9
- import { execSync } from 'child_process';
10
- import { createRequire } from 'module';
4
+ import {
5
+ existsSync,
6
+ mkdirSync,
7
+ createWriteStream,
8
+ chmodSync,
9
+ readFileSync,
10
+ readSync,
11
+ writeFileSync,
12
+ unlinkSync,
13
+ statSync,
14
+ openSync,
15
+ closeSync,
16
+ copyFileSync,
17
+ fsyncSync,
18
+ renameSync,
19
+ realpathSync,
20
+ } from "fs";
21
+ import { join, dirname, resolve } from "path";
22
+ import { fileURLToPath } from "url";
23
+ import { get } from "https";
24
+ import { platform, arch, tmpdir } from "os";
25
+ import { execSync } from "child_process";
26
+ import { createRequire } from "module";
11
27
 
12
28
  const __dirname = dirname(fileURLToPath(import.meta.url));
13
29
 
14
30
  function getTargetTriple() {
15
31
  const platformMap = {
16
- 'darwin': 'apple-darwin',
17
- 'linux': 'unknown-linux-musl',
18
- 'win32': 'pc-windows-msvc'
32
+ darwin: "apple-darwin",
33
+ linux: "unknown-linux-musl",
34
+ win32: "pc-windows-msvc",
19
35
  };
20
36
 
21
37
  const archMap = {
22
- 'x64': 'x86_64',
23
- 'arm64': 'aarch64'
38
+ x64: "x86_64",
39
+ arm64: "aarch64",
24
40
  };
25
41
 
26
42
  const rustArch = archMap[arch()] || arch();
@@ -31,47 +47,54 @@ function getTargetTriple() {
31
47
 
32
48
  function getCacheDir(version) {
33
49
  const plt = platform();
34
- const home = process.env.HOME || process.env.USERPROFILE || '';
35
- let base = '';
36
- if (plt === 'win32') {
37
- base = process.env.LOCALAPPDATA || join(home, 'AppData', 'Local');
38
- } else if (plt === 'darwin') {
39
- base = join(home, 'Library', 'Caches');
50
+ const home = process.env.HOME || process.env.USERPROFILE || "";
51
+ let base = "";
52
+ if (plt === "win32") {
53
+ base = process.env.LOCALAPPDATA || join(home, "AppData", "Local");
54
+ } else if (plt === "darwin") {
55
+ base = join(home, "Library", "Caches");
40
56
  } else {
41
- base = process.env.XDG_CACHE_HOME || join(home, '.cache');
57
+ base = process.env.XDG_CACHE_HOME || join(home, ".cache");
42
58
  }
43
- const dir = join(base, 'hanzo', 'dev', version);
59
+ const dir = join(base, "hanzo", "dev", version);
44
60
  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
45
61
  return dir;
46
62
  }
47
63
 
48
64
  function getCachedBinaryPath(version, targetTriple, isWindows) {
49
- const ext = isWindows ? '.exe' : '';
65
+ const ext = isWindows ? ".exe" : "";
50
66
  const cacheDir = getCacheDir(version);
51
67
  return join(cacheDir, `dev-${targetTriple}${ext}`);
52
68
  }
53
69
 
54
70
  function isWSL() {
55
- if (platform() !== 'linux') return false;
71
+ if (platform() !== "linux") return false;
56
72
  try {
57
- const ver = readFileSync('/proc/version', 'utf8').toLowerCase();
58
- return ver.includes('microsoft') || !!process.env.WSL_DISTRO_NAME;
59
- } catch { return false; }
73
+ const ver = readFileSync("/proc/version", "utf8").toLowerCase();
74
+ return ver.includes("microsoft") || !!process.env.WSL_DISTRO_NAME;
75
+ } catch {
76
+ return false;
77
+ }
60
78
  }
61
79
 
62
80
  function isPathOnWindowsFs(p) {
63
81
  try {
64
- const mounts = readFileSync('/proc/mounts', 'utf8').split(/\n/).filter(Boolean);
65
- let best = { mount: '/', type: 'unknown', len: 1 };
82
+ const mounts = readFileSync("/proc/mounts", "utf8")
83
+ .split(/\n/)
84
+ .filter(Boolean);
85
+ let best = { mount: "/", type: "unknown", len: 1 };
66
86
  for (const line of mounts) {
67
- const parts = line.split(' ');
87
+ const parts = line.split(" ");
68
88
  if (parts.length < 3) continue;
69
89
  const mnt = parts[1];
70
90
  const typ = parts[2];
71
- if (p.startsWith(mnt) && mnt.length > best.len) best = { mount: mnt, type: typ, len: mnt.length };
91
+ if (p.startsWith(mnt) && mnt.length > best.len)
92
+ best = { mount: mnt, type: typ, len: mnt.length };
72
93
  }
73
- return best.type === 'drvfs' || best.type === 'cifs';
74
- } catch { return false; }
94
+ return best.type === "drvfs" || best.type === "cifs";
95
+ } catch {
96
+ return false;
97
+ }
75
98
  }
76
99
 
77
100
  async function writeCacheAtomic(srcPath, cachePath) {
@@ -82,141 +105,185 @@ async function writeCacheAtomic(srcPath, cachePath) {
82
105
  }
83
106
  } catch {}
84
107
  const dir = dirname(cachePath);
85
- if (!existsSync(dir)) { try { mkdirSync(dir, { recursive: true }); } catch {} }
86
- const tmp = cachePath + '.tmp-' + Math.random().toString(36).slice(2, 8);
108
+ if (!existsSync(dir)) {
109
+ try {
110
+ mkdirSync(dir, { recursive: true });
111
+ } catch {}
112
+ }
113
+ const tmp = cachePath + ".tmp-" + Math.random().toString(36).slice(2, 8);
87
114
  copyFileSync(srcPath, tmp);
88
- try { const fd = openSync(tmp, 'r'); try { fsyncSync(fd); } finally { closeSync(fd); } } catch {}
115
+ try {
116
+ const fd = openSync(tmp, "r");
117
+ try {
118
+ fsyncSync(fd);
119
+ } finally {
120
+ closeSync(fd);
121
+ }
122
+ } catch {}
89
123
  const delays = [100, 200, 400, 800, 1200, 1600];
90
124
  for (let i = 0; i < delays.length; i++) {
91
125
  try {
92
- if (existsSync(cachePath)) { try { unlinkSync(cachePath); } catch {} }
126
+ if (existsSync(cachePath)) {
127
+ try {
128
+ unlinkSync(cachePath);
129
+ } catch {}
130
+ }
93
131
  renameSync(tmp, cachePath);
94
132
  return;
95
133
  } catch {
96
- await new Promise(r => setTimeout(r, delays[i]));
134
+ await new Promise((r) => setTimeout(r, delays[i]));
97
135
  }
98
136
  }
99
- if (existsSync(cachePath)) { try { unlinkSync(cachePath); } catch {} }
137
+ if (existsSync(cachePath)) {
138
+ try {
139
+ unlinkSync(cachePath);
140
+ } catch {}
141
+ }
100
142
  renameSync(tmp, cachePath);
101
143
  }
102
144
 
103
145
  function resolveGlobalBinDir() {
104
146
  const plt = platform();
105
- const userAgent = process.env.npm_config_user_agent || '';
147
+ const userAgent = process.env.npm_config_user_agent || "";
106
148
 
107
149
  const fromPrefix = (prefixPath) => {
108
- if (!prefixPath) return '';
109
- return plt === 'win32' ? prefixPath : join(prefixPath, 'bin');
150
+ if (!prefixPath) return "";
151
+ return plt === "win32" ? prefixPath : join(prefixPath, "bin");
110
152
  };
111
153
 
112
- const prefixEnv = process.env.npm_config_prefix || process.env.PREFIX || '';
154
+ const prefixEnv = process.env.npm_config_prefix || process.env.PREFIX || "";
113
155
  const direct = fromPrefix(prefixEnv);
114
156
  if (direct) return direct;
115
157
 
116
158
  const tryExec = (command) => {
117
159
  try {
118
160
  return execSync(command, {
119
- stdio: ['ignore', 'pipe', 'ignore'],
161
+ stdio: ["ignore", "pipe", "ignore"],
120
162
  shell: true,
121
- }).toString().trim();
163
+ })
164
+ .toString()
165
+ .trim();
122
166
  } catch {
123
- return '';
167
+ return "";
124
168
  }
125
169
  };
126
170
 
127
- const prefixFromNpm = fromPrefix(tryExec('npm prefix -g'));
171
+ const prefixFromNpm = fromPrefix(tryExec("npm prefix -g"));
128
172
  if (prefixFromNpm) return prefixFromNpm;
129
173
 
130
- const binFromNpm = tryExec('npm bin -g');
174
+ const binFromNpm = tryExec("npm bin -g");
131
175
  if (binFromNpm) return binFromNpm;
132
176
 
133
- if (userAgent.includes('pnpm')) {
134
- const pnpmBin = tryExec('pnpm bin --global');
177
+ if (userAgent.includes("pnpm")) {
178
+ const pnpmBin = tryExec("pnpm bin --global");
135
179
  if (pnpmBin) return pnpmBin;
136
180
  }
137
181
 
138
- if (userAgent.includes('yarn')) {
139
- const yarnBin = tryExec('yarn global bin');
182
+ if (userAgent.includes("yarn")) {
183
+ const yarnBin = tryExec("yarn global bin");
140
184
  if (yarnBin) return yarnBin;
141
185
  }
142
186
 
143
- return '';
187
+ return "";
144
188
  }
145
189
 
146
190
  async function downloadBinary(url, dest, maxRedirects = 5, maxRetries = 3) {
147
- const sleep = (ms) => new Promise(r => setTimeout(r, ms));
148
-
149
- const doAttempt = () => new Promise((resolve, reject) => {
150
- const attempt = (currentUrl, redirectsLeft) => {
151
- const req = get(currentUrl, (response) => {
152
- const status = response.statusCode || 0;
153
- const location = response.headers.location;
154
-
155
- if ((status === 301 || status === 302 || status === 303 || status === 307 || status === 308) && location) {
156
- if (redirectsLeft <= 0) {
157
- reject(new Error(`Too many redirects while downloading ${currentUrl}`));
191
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
192
+
193
+ const doAttempt = () =>
194
+ new Promise((resolve, reject) => {
195
+ const attempt = (currentUrl, redirectsLeft) => {
196
+ const req = get(currentUrl, (response) => {
197
+ const status = response.statusCode || 0;
198
+ const location = response.headers.location;
199
+
200
+ if (
201
+ (status === 301 ||
202
+ status === 302 ||
203
+ status === 303 ||
204
+ status === 307 ||
205
+ status === 308) &&
206
+ location
207
+ ) {
208
+ if (redirectsLeft <= 0) {
209
+ reject(
210
+ new Error(`Too many redirects while downloading ${currentUrl}`),
211
+ );
212
+ return;
213
+ }
214
+ attempt(location, redirectsLeft - 1);
158
215
  return;
159
216
  }
160
- attempt(location, redirectsLeft - 1);
161
- return;
162
- }
163
217
 
164
- if (status === 200) {
165
- const expected = parseInt(response.headers['content-length'] || '0', 10) || 0;
166
- let bytes = 0;
167
- let timer;
168
- const timeoutMs = 30000;
169
-
170
- const resetTimer = () => {
171
- if (timer) clearTimeout(timer);
172
- timer = setTimeout(() => {
173
- req.destroy(new Error('download stalled'));
174
- }, timeoutMs);
175
- };
176
-
177
- resetTimer();
178
- response.on('data', (chunk) => {
179
- bytes += chunk.length;
218
+ if (status === 200) {
219
+ const expected =
220
+ parseInt(response.headers["content-length"] || "0", 10) || 0;
221
+ let bytes = 0;
222
+ let timer;
223
+ const timeoutMs = 30000;
224
+
225
+ const resetTimer = () => {
226
+ if (timer) clearTimeout(timer);
227
+ timer = setTimeout(() => {
228
+ req.destroy(new Error("download stalled"));
229
+ }, timeoutMs);
230
+ };
231
+
180
232
  resetTimer();
181
- });
182
-
183
- const file = createWriteStream(dest);
184
- response.pipe(file);
185
- file.on('finish', () => {
186
- if (timer) clearTimeout(timer);
187
- file.close();
188
- if (expected && bytes !== expected) {
189
- try { unlinkSync(dest); } catch {}
190
- reject(new Error(`incomplete download: got ${bytes} of ${expected} bytes`));
191
- } else if (bytes === 0) {
192
- try { unlinkSync(dest); } catch {}
193
- reject(new Error('empty download'));
194
- } else {
195
- resolve();
196
- }
197
- });
198
- file.on('error', (err) => {
199
- if (timer) clearTimeout(timer);
200
- try { unlinkSync(dest); } catch {}
201
- reject(err);
202
- });
203
- } else {
204
- reject(new Error(`Failed to download: HTTP ${status}`));
205
- }
206
- });
233
+ response.on("data", (chunk) => {
234
+ bytes += chunk.length;
235
+ resetTimer();
236
+ });
237
+
238
+ const file = createWriteStream(dest);
239
+ response.pipe(file);
240
+ file.on("finish", () => {
241
+ if (timer) clearTimeout(timer);
242
+ file.close();
243
+ if (expected && bytes !== expected) {
244
+ try {
245
+ unlinkSync(dest);
246
+ } catch {}
247
+ reject(
248
+ new Error(
249
+ `incomplete download: got ${bytes} of ${expected} bytes`,
250
+ ),
251
+ );
252
+ } else if (bytes === 0) {
253
+ try {
254
+ unlinkSync(dest);
255
+ } catch {}
256
+ reject(new Error("empty download"));
257
+ } else {
258
+ resolve();
259
+ }
260
+ });
261
+ file.on("error", (err) => {
262
+ if (timer) clearTimeout(timer);
263
+ try {
264
+ unlinkSync(dest);
265
+ } catch {}
266
+ reject(err);
267
+ });
268
+ } else {
269
+ reject(new Error(`Failed to download: HTTP ${status}`));
270
+ }
271
+ });
207
272
 
208
- req.on('error', (err) => {
209
- try { unlinkSync(dest); } catch {}
210
- reject(err);
211
- });
273
+ req.on("error", (err) => {
274
+ try {
275
+ unlinkSync(dest);
276
+ } catch {}
277
+ reject(err);
278
+ });
212
279
 
213
- req.setTimeout(120000, () => {
214
- req.destroy(new Error('download timed out'));
215
- });
216
- };
280
+ req.setTimeout(120000, () => {
281
+ req.destroy(new Error("download timed out"));
282
+ });
283
+ };
217
284
 
218
- attempt(url, maxRedirects);
219
- });
285
+ attempt(url, maxRedirects);
286
+ });
220
287
 
221
288
  let attemptNum = 0;
222
289
  while (true) {
@@ -235,22 +302,38 @@ function validateDownloadedBinary(p) {
235
302
  try {
236
303
  const st = statSync(p);
237
304
  if (!st.isFile() || st.size === 0) {
238
- return { ok: false, reason: 'empty or not a regular file' };
305
+ return { ok: false, reason: "empty or not a regular file" };
239
306
  }
240
- const fd = openSync(p, 'r');
307
+ const fd = openSync(p, "r");
241
308
  try {
242
309
  const buf = Buffer.alloc(4);
243
310
  const n = readSync(fd, buf, 0, 4, 0);
244
- if (n < 2) return { ok: false, reason: 'too short' };
311
+ if (n < 2) return { ok: false, reason: "too short" };
245
312
  const plt = platform();
246
- if (plt === 'win32') {
247
- if (!(buf[0] === 0x4d && buf[1] === 0x5a)) return { ok: false, reason: 'invalid PE header (missing MZ)' };
248
- } else if (plt === 'linux' || plt === 'android') {
249
- if (!(buf[0] === 0x7f && buf[1] === 0x45 && buf[2] === 0x4c && buf[3] === 0x46)) return { ok: false, reason: 'invalid ELF header' };
250
- } else if (plt === 'darwin') {
251
- const isMachO = (buf[0] === 0xcf && buf[1] === 0xfa && buf[2] === 0xed && buf[3] === 0xfe) ||
252
- (buf[0] === 0xca && buf[1] === 0xfe && buf[2] === 0xba && buf[3] === 0xbe);
253
- if (!isMachO) return { ok: false, reason: 'invalid Mach-O header' };
313
+ if (plt === "win32") {
314
+ if (!(buf[0] === 0x4d && buf[1] === 0x5a))
315
+ return { ok: false, reason: "invalid PE header (missing MZ)" };
316
+ } else if (plt === "linux" || plt === "android") {
317
+ if (
318
+ !(
319
+ buf[0] === 0x7f &&
320
+ buf[1] === 0x45 &&
321
+ buf[2] === 0x4c &&
322
+ buf[3] === 0x46
323
+ )
324
+ )
325
+ return { ok: false, reason: "invalid ELF header" };
326
+ } else if (plt === "darwin") {
327
+ const isMachO =
328
+ (buf[0] === 0xcf &&
329
+ buf[1] === 0xfa &&
330
+ buf[2] === 0xed &&
331
+ buf[3] === 0xfe) ||
332
+ (buf[0] === 0xca &&
333
+ buf[1] === 0xfe &&
334
+ buf[2] === 0xba &&
335
+ buf[3] === 0xbe);
336
+ if (!isMachO) return { ok: false, reason: "invalid Mach-O header" };
254
337
  }
255
338
  return { ok: true };
256
339
  } finally {
@@ -263,32 +346,35 @@ function validateDownloadedBinary(p) {
263
346
 
264
347
  export async function runPostinstall(options = {}) {
265
348
  const { skipGlobalAlias = false, invokedByRuntime = false } = options;
266
- if (process.env.DEV_POSTINSTALL_DRY_RUN === '1') {
349
+ if (process.env.DEV_POSTINSTALL_DRY_RUN === "1") {
267
350
  return { skipped: true };
268
351
  }
269
352
 
270
353
  if (invokedByRuntime) {
271
- process.env.DEV_RUNTIME_POSTINSTALL = process.env.DEV_RUNTIME_POSTINSTALL || '1';
354
+ process.env.DEV_RUNTIME_POSTINSTALL =
355
+ process.env.DEV_RUNTIME_POSTINSTALL || "1";
272
356
  }
273
357
 
274
- const ua = process.env.npm_config_user_agent || '';
275
- const isNpx = ua.includes('npx');
276
- const isGlobal = process.env.npm_config_global === 'true';
358
+ const ua = process.env.npm_config_user_agent || "";
359
+ const isNpx = ua.includes("npx");
360
+ const isGlobal = process.env.npm_config_global === "true";
277
361
 
278
362
  const targetTriple = getTargetTriple();
279
- const isWindows = platform() === 'win32';
280
- const binaryExt = isWindows ? '.exe' : '';
363
+ const isWindows = platform() === "win32";
364
+ const binaryExt = isWindows ? ".exe" : "";
281
365
 
282
- const binDir = join(__dirname, 'bin');
366
+ const binDir = join(__dirname, "bin");
283
367
  if (!existsSync(binDir)) {
284
368
  mkdirSync(binDir, { recursive: true });
285
369
  }
286
370
 
287
- const packageJson = JSON.parse(readFileSync(join(__dirname, 'package.json'), 'utf8'));
371
+ const packageJson = JSON.parse(
372
+ readFileSync(join(__dirname, "package.json"), "utf8"),
373
+ );
288
374
  const version = packageJson.version;
289
375
 
290
376
  // The release produces 'code-*' binaries; we'll download and rename to 'dev-*'
291
- const binaries = ['code'];
377
+ const binaries = ["code"];
292
378
 
293
379
  console.log(`Installing @hanzo/dev v${version} for ${targetTriple}...`);
294
380
 
@@ -304,11 +390,22 @@ export async function runPostinstall(options = {}) {
304
390
  const valid = validateDownloadedBinary(cachePath);
305
391
  if (valid.ok) {
306
392
  const wsl = isWSL();
307
- const binDirReal = (() => { try { return realpathSync(binDir); } catch { return binDir; } })();
308
- const mirrorToLocal = !(isWindows || (wsl && isPathOnWindowsFs(binDirReal)));
393
+ const binDirReal = (() => {
394
+ try {
395
+ return realpathSync(binDir);
396
+ } catch {
397
+ return binDir;
398
+ }
399
+ })();
400
+ const mirrorToLocal = !(
401
+ isWindows ||
402
+ (wsl && isPathOnWindowsFs(binDirReal))
403
+ );
309
404
  if (mirrorToLocal) {
310
405
  copyFileSync(cachePath, localPath);
311
- try { chmodSync(localPath, 0o755); } catch {}
406
+ try {
407
+ chmodSync(localPath, 0o755);
408
+ } catch {}
312
409
  }
313
410
  console.log(`✓ ${devBinaryName} ready from user cache`);
314
411
  continue;
@@ -322,13 +419,16 @@ export async function runPostinstall(options = {}) {
322
419
  const require = createRequire(import.meta.url);
323
420
  const platformPkg = (() => {
324
421
  const name = (() => {
325
- if (isWindows) return '@hanzo/dev-win32-x64';
422
+ if (isWindows) return "@hanzo/dev-win32-x64";
326
423
  const plt = platform();
327
424
  const cpu = arch();
328
- if (plt === 'darwin' && cpu === 'arm64') return '@hanzo/dev-darwin-arm64';
329
- if (plt === 'darwin' && cpu === 'x64') return '@hanzo/dev-darwin-x64';
330
- if (plt === 'linux' && cpu === 'x64') return '@hanzo/dev-linux-x64-musl';
331
- if (plt === 'linux' && cpu === 'arm64') return '@hanzo/dev-linux-arm64-musl';
425
+ if (plt === "darwin" && cpu === "arm64")
426
+ return "@hanzo/dev-darwin-arm64";
427
+ if (plt === "darwin" && cpu === "x64") return "@hanzo/dev-darwin-x64";
428
+ if (plt === "linux" && cpu === "x64")
429
+ return "@hanzo/dev-linux-x64-musl";
430
+ if (plt === "linux" && cpu === "arm64")
431
+ return "@hanzo/dev-linux-arm64-musl";
332
432
  return null;
333
433
  })();
334
434
  if (!name) return null;
@@ -343,81 +443,155 @@ export async function runPostinstall(options = {}) {
343
443
 
344
444
  if (platformPkg) {
345
445
  try {
346
- const src = join(platformPkg.dir, 'bin', devBinaryName);
446
+ const src = join(platformPkg.dir, "bin", devBinaryName);
347
447
  if (!existsSync(src)) {
348
- throw new Error(`platform package missing binary: ${platformPkg.name}`);
448
+ throw new Error(
449
+ `platform package missing binary: ${platformPkg.name}`,
450
+ );
349
451
  }
350
452
  await writeCacheAtomic(src, cachePath);
351
453
  const wsl = isWSL();
352
- const binDirReal = (() => { try { return realpathSync(binDir); } catch { return binDir; } })();
353
- const mirrorToLocal = !(isWindows || (wsl && isPathOnWindowsFs(binDirReal)));
454
+ const binDirReal = (() => {
455
+ try {
456
+ return realpathSync(binDir);
457
+ } catch {
458
+ return binDir;
459
+ }
460
+ })();
461
+ const mirrorToLocal = !(
462
+ isWindows ||
463
+ (wsl && isPathOnWindowsFs(binDirReal))
464
+ );
354
465
  if (mirrorToLocal) {
355
466
  copyFileSync(cachePath, localPath);
356
- try { chmodSync(localPath, 0o755); } catch {}
467
+ try {
468
+ chmodSync(localPath, 0o755);
469
+ } catch {}
357
470
  }
358
- console.log(`✓ Installed ${devBinaryName} from ${platformPkg.name} (cached)`);
471
+ console.log(
472
+ `✓ Installed ${devBinaryName} from ${platformPkg.name} (cached)`,
473
+ );
359
474
  continue;
360
475
  } catch (e) {
361
- console.warn(`⚠ Failed platform package install (${e.message}), falling back to GitHub download`);
476
+ console.warn(
477
+ `⚠ Failed platform package install (${e.message}), falling back to GitHub download`,
478
+ );
362
479
  }
363
480
  }
364
481
 
365
482
  // Download from GitHub release
366
483
  const isWin = isWindows;
367
484
  const detectedWSL = isWSL();
368
- const binDirReal = (() => { try { return realpathSync(binDir); } catch { return binDir; } })();
369
- const mirrorToLocal = !(isWin || (detectedWSL && isPathOnWindowsFs(binDirReal)));
485
+ const binDirReal = (() => {
486
+ try {
487
+ return realpathSync(binDir);
488
+ } catch {
489
+ return binDir;
490
+ }
491
+ })();
492
+ const mirrorToLocal = !(
493
+ isWin ||
494
+ (detectedWSL && isPathOnWindowsFs(binDirReal))
495
+ );
370
496
  let useZst = false;
371
497
  if (!isWin) {
372
498
  try {
373
- execSync('zstd --version', { stdio: 'ignore', shell: true });
499
+ execSync("zstd --version", { stdio: "ignore", shell: true });
374
500
  useZst = true;
375
501
  } catch {
376
502
  useZst = false;
377
503
  }
378
504
  }
379
- const archiveName = isWin ? `${binaryName}.zip` : (useZst ? `${binaryName}.zst` : `${binaryName}.tar.gz`);
505
+ const archiveName = isWin
506
+ ? `${binaryName}.zip`
507
+ : useZst
508
+ ? `${binaryName}.zst`
509
+ : `${binaryName}.tar.gz`;
380
510
  const downloadUrl = `https://github.com/hanzoai/dev/releases/download/v${version}/${archiveName}`;
381
511
 
382
512
  console.log(`Downloading ${archiveName}...`);
383
513
  try {
384
514
  const needsIsolation = isWin || (!isWin && !mirrorToLocal);
385
- let safeTempDir = needsIsolation ? join(tmpdir(), 'hanzo', 'dev', version) : binDir;
515
+ let safeTempDir = needsIsolation
516
+ ? join(tmpdir(), "hanzo", "dev", version)
517
+ : binDir;
386
518
  if (needsIsolation) {
387
519
  try {
388
- if (!existsSync(safeTempDir)) mkdirSync(safeTempDir, { recursive: true });
520
+ if (!existsSync(safeTempDir))
521
+ mkdirSync(safeTempDir, { recursive: true });
389
522
  } catch {
390
523
  try {
391
524
  safeTempDir = getCacheDir(version);
392
- if (!existsSync(safeTempDir)) mkdirSync(safeTempDir, { recursive: true });
525
+ if (!existsSync(safeTempDir))
526
+ mkdirSync(safeTempDir, { recursive: true });
393
527
  } catch {}
394
528
  }
395
529
  }
396
- const tmpPath = join(needsIsolation ? safeTempDir : binDir, `.${archiveName}.part`);
530
+ const tmpPath = join(
531
+ needsIsolation ? safeTempDir : binDir,
532
+ `.${archiveName}.part`,
533
+ );
397
534
  await downloadBinary(downloadUrl, tmpPath);
398
535
 
399
536
  if (isWin) {
400
537
  const unzipDest = safeTempDir;
401
538
  try {
402
- const sysRoot = process.env.SystemRoot || process.env.windir || 'C:\\Windows';
403
- const psFull = join(sysRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe');
539
+ const sysRoot =
540
+ process.env.SystemRoot || process.env.windir || "C:\\Windows";
541
+ const psFull = join(
542
+ sysRoot,
543
+ "System32",
544
+ "WindowsPowerShell",
545
+ "v1.0",
546
+ "powershell.exe",
547
+ );
404
548
  const psCmd = `Expand-Archive -Path '${tmpPath}' -DestinationPath '${unzipDest}' -Force`;
405
549
  let ok = false;
406
- try { execSync(`"${psFull}" -NoProfile -NonInteractive -Command "${psCmd}"`, { stdio: 'ignore' }); ok = true; } catch {}
407
- if (!ok) { try { execSync(`powershell -NoProfile -NonInteractive -Command "${psCmd}"`, { stdio: 'ignore' }); ok = true; } catch {} }
408
- if (!ok) { try { execSync(`pwsh -NoProfile -NonInteractive -Command "${psCmd}"`, { stdio: 'ignore' }); ok = true; } catch {} }
409
- if (!ok) { execSync(`tar -xf "${tmpPath}" -C "${unzipDest}"`, { stdio: 'ignore', shell: true }); }
550
+ try {
551
+ execSync(
552
+ `"${psFull}" -NoProfile -NonInteractive -Command "${psCmd}"`,
553
+ { stdio: "ignore" },
554
+ );
555
+ ok = true;
556
+ } catch {}
557
+ if (!ok) {
558
+ try {
559
+ execSync(
560
+ `powershell -NoProfile -NonInteractive -Command "${psCmd}"`,
561
+ { stdio: "ignore" },
562
+ );
563
+ ok = true;
564
+ } catch {}
565
+ }
566
+ if (!ok) {
567
+ try {
568
+ execSync(`pwsh -NoProfile -NonInteractive -Command "${psCmd}"`, {
569
+ stdio: "ignore",
570
+ });
571
+ ok = true;
572
+ } catch {}
573
+ }
574
+ if (!ok) {
575
+ execSync(`tar -xf "${tmpPath}" -C "${unzipDest}"`, {
576
+ stdio: "ignore",
577
+ shell: true,
578
+ });
579
+ }
410
580
  } catch (e) {
411
581
  throw new Error(`failed to unzip archive: ${e.message}`);
412
582
  } finally {
413
- try { unlinkSync(tmpPath); } catch {}
583
+ try {
584
+ unlinkSync(tmpPath);
585
+ } catch {}
414
586
  }
415
587
  try {
416
588
  const extractedPath = join(unzipDest, binaryName);
417
589
  // Rename code-* to dev-* in cache
418
590
  const devCachePath = cachePath;
419
591
  await writeCacheAtomic(extractedPath, devCachePath);
420
- try { unlinkSync(extractedPath); } catch {}
592
+ try {
593
+ unlinkSync(extractedPath);
594
+ } catch {}
421
595
  } catch (e) {
422
596
  throw new Error(`failed to move binary to cache: ${e.message}`);
423
597
  }
@@ -425,20 +599,36 @@ export async function runPostinstall(options = {}) {
425
599
  const downloadedPath = join(binDir, binaryName);
426
600
  if (useZst) {
427
601
  try {
428
- execSync(`zstd -d '${tmpPath}' -o '${downloadedPath}'`, { stdio: 'ignore', shell: true });
602
+ execSync(`zstd -d '${tmpPath}' -o '${downloadedPath}'`, {
603
+ stdio: "ignore",
604
+ shell: true,
605
+ });
429
606
  } catch (e) {
430
- try { unlinkSync(tmpPath); } catch {}
431
- throw new Error(`failed to decompress .zst (need zstd CLI): ${e.message}`);
607
+ try {
608
+ unlinkSync(tmpPath);
609
+ } catch {}
610
+ throw new Error(
611
+ `failed to decompress .zst (need zstd CLI): ${e.message}`,
612
+ );
432
613
  }
433
- try { unlinkSync(tmpPath); } catch {}
614
+ try {
615
+ unlinkSync(tmpPath);
616
+ } catch {}
434
617
  } else {
435
618
  try {
436
- execSync(`tar -xzf '${tmpPath}' -C '${binDir}'`, { stdio: 'ignore', shell: true });
619
+ execSync(`tar -xzf '${tmpPath}' -C '${binDir}'`, {
620
+ stdio: "ignore",
621
+ shell: true,
622
+ });
437
623
  } catch (e) {
438
- try { unlinkSync(tmpPath); } catch {}
624
+ try {
625
+ unlinkSync(tmpPath);
626
+ } catch {}
439
627
  throw new Error(`failed to extract .tar.gz: ${e.message}`);
440
628
  }
441
- try { unlinkSync(tmpPath); } catch {}
629
+ try {
630
+ unlinkSync(tmpPath);
631
+ } catch {}
442
632
  }
443
633
  // Rename code-* to dev-*
444
634
  if (existsSync(downloadedPath)) {
@@ -447,14 +637,22 @@ export async function runPostinstall(options = {}) {
447
637
  } else {
448
638
  const extractedPath = downloadedPath;
449
639
  await writeCacheAtomic(extractedPath, cachePath);
450
- try { unlinkSync(extractedPath); } catch {}
640
+ try {
641
+ unlinkSync(extractedPath);
642
+ } catch {}
451
643
  }
452
644
  }
453
645
  }
454
646
 
455
- const valid = validateDownloadedBinary(isWin ? cachePath : (mirrorToLocal ? localPath : cachePath));
647
+ const valid = validateDownloadedBinary(
648
+ isWin ? cachePath : mirrorToLocal ? localPath : cachePath,
649
+ );
456
650
  if (!valid.ok) {
457
- try { (isWin || !mirrorToLocal) ? unlinkSync(cachePath) : unlinkSync(localPath); } catch {}
651
+ try {
652
+ isWin || !mirrorToLocal
653
+ ? unlinkSync(cachePath)
654
+ : unlinkSync(localPath);
655
+ } catch {}
458
656
  throw new Error(`invalid binary (${valid.reason})`);
459
657
  }
460
658
 
@@ -462,9 +660,13 @@ export async function runPostinstall(options = {}) {
462
660
  chmodSync(localPath, 0o755);
463
661
  }
464
662
 
465
- console.log(`✓ Installed ${devBinaryName}${(isWin || !mirrorToLocal) ? ' (cached)' : ''}`);
663
+ console.log(
664
+ `✓ Installed ${devBinaryName}${isWin || !mirrorToLocal ? " (cached)" : ""}`,
665
+ );
466
666
  if (!isWin && mirrorToLocal) {
467
- try { await writeCacheAtomic(localPath, cachePath); } catch {}
667
+ try {
668
+ await writeCacheAtomic(localPath, cachePath);
669
+ } catch {}
468
670
  }
469
671
  } catch (error) {
470
672
  console.error(`✗ Failed to install ${devBinaryName}: ${error.message}`);
@@ -475,23 +677,35 @@ export async function runPostinstall(options = {}) {
475
677
  const mainBinary = `dev-${targetTriple}${binaryExt}`;
476
678
  const mainBinaryPath = join(binDir, mainBinary);
477
679
 
478
- if (existsSync(mainBinaryPath) || existsSync(getCachedBinaryPath(version, targetTriple, platform() === 'win32'))) {
680
+ if (
681
+ existsSync(mainBinaryPath) ||
682
+ existsSync(
683
+ getCachedBinaryPath(version, targetTriple, platform() === "win32"),
684
+ )
685
+ ) {
479
686
  try {
480
- const probePath = existsSync(mainBinaryPath) ? mainBinaryPath : getCachedBinaryPath(version, targetTriple, platform() === 'win32');
687
+ const probePath = existsSync(mainBinaryPath)
688
+ ? mainBinaryPath
689
+ : getCachedBinaryPath(version, targetTriple, platform() === "win32");
481
690
  const stats = statSync(probePath);
482
- if (!stats.size) throw new Error('binary is empty (download likely failed)');
691
+ if (!stats.size)
692
+ throw new Error("binary is empty (download likely failed)");
483
693
  const valid = validateDownloadedBinary(probePath);
484
694
  if (!valid.ok) {
485
695
  console.warn(`⚠ Main dev binary appears invalid: ${valid.reason}`);
486
- console.warn(' Try reinstalling or check your network/proxy settings.');
696
+ console.warn(
697
+ " Try reinstalling or check your network/proxy settings.",
698
+ );
487
699
  }
488
700
  } catch (e) {
489
701
  console.warn(`⚠ Main dev binary appears invalid: ${e.message}`);
490
- console.warn(' Try reinstalling or check your network/proxy settings.');
702
+ console.warn(" Try reinstalling or check your network/proxy settings.");
491
703
  }
492
- console.log('✓ Installation complete!');
704
+ console.log("✓ Installation complete!");
493
705
  } else {
494
- console.warn('⚠ Main dev binary not found. You may need to build from source.');
706
+ console.warn(
707
+ "⚠ Main dev binary not found. You may need to build from source.",
708
+ );
495
709
  }
496
710
  }
497
711
 
@@ -506,8 +720,8 @@ function isExecutedDirectly() {
506
720
  }
507
721
 
508
722
  if (isExecutedDirectly()) {
509
- runPostinstall().catch(error => {
510
- console.error('Installation failed:', error);
723
+ runPostinstall().catch((error) => {
724
+ console.error("Installation failed:", error);
511
725
  process.exit(1);
512
726
  });
513
727
  }