@hanzo/dev 3.0.8 → 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.
- package/package.json +6 -6
- package/postinstall.js +404 -190
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/dev",
|
|
3
|
-
"version": "3.0.
|
|
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.
|
|
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.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 {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
32
|
+
darwin: "apple-darwin",
|
|
33
|
+
linux: "unknown-linux-musl",
|
|
34
|
+
win32: "pc-windows-msvc",
|
|
19
35
|
};
|
|
20
36
|
|
|
21
37
|
const archMap = {
|
|
22
|
-
|
|
23
|
-
|
|
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 ===
|
|
37
|
-
base = process.env.LOCALAPPDATA || join(home,
|
|
38
|
-
} else if (plt ===
|
|
39
|
-
base = join(home,
|
|
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,
|
|
57
|
+
base = process.env.XDG_CACHE_HOME || join(home, ".cache");
|
|
42
58
|
}
|
|
43
|
-
const dir = join(base,
|
|
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 ?
|
|
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() !==
|
|
71
|
+
if (platform() !== "linux") return false;
|
|
56
72
|
try {
|
|
57
|
-
const ver = readFileSync(
|
|
58
|
-
return ver.includes(
|
|
59
|
-
} catch {
|
|
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(
|
|
65
|
-
|
|
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)
|
|
91
|
+
if (p.startsWith(mnt) && mnt.length > best.len)
|
|
92
|
+
best = { mount: mnt, type: typ, len: mnt.length };
|
|
72
93
|
}
|
|
73
|
-
return best.type ===
|
|
74
|
-
} catch {
|
|
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)) {
|
|
86
|
-
|
|
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 {
|
|
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)) {
|
|
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)) {
|
|
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 ===
|
|
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: [
|
|
161
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
120
162
|
shell: true,
|
|
121
|
-
})
|
|
163
|
+
})
|
|
164
|
+
.toString()
|
|
165
|
+
.trim();
|
|
122
166
|
} catch {
|
|
123
|
-
return
|
|
167
|
+
return "";
|
|
124
168
|
}
|
|
125
169
|
};
|
|
126
170
|
|
|
127
|
-
const prefixFromNpm = fromPrefix(tryExec(
|
|
171
|
+
const prefixFromNpm = fromPrefix(tryExec("npm prefix -g"));
|
|
128
172
|
if (prefixFromNpm) return prefixFromNpm;
|
|
129
173
|
|
|
130
|
-
const binFromNpm = tryExec(
|
|
174
|
+
const binFromNpm = tryExec("npm bin -g");
|
|
131
175
|
if (binFromNpm) return binFromNpm;
|
|
132
176
|
|
|
133
|
-
if (userAgent.includes(
|
|
134
|
-
const pnpmBin = tryExec(
|
|
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(
|
|
139
|
-
const yarnBin = tryExec(
|
|
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 = () =>
|
|
150
|
-
|
|
151
|
-
const
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (
|
|
157
|
-
|
|
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
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
273
|
+
req.on("error", (err) => {
|
|
274
|
+
try {
|
|
275
|
+
unlinkSync(dest);
|
|
276
|
+
} catch {}
|
|
277
|
+
reject(err);
|
|
278
|
+
});
|
|
212
279
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
280
|
+
req.setTimeout(120000, () => {
|
|
281
|
+
req.destroy(new Error("download timed out"));
|
|
282
|
+
});
|
|
283
|
+
};
|
|
217
284
|
|
|
218
|
-
|
|
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:
|
|
305
|
+
return { ok: false, reason: "empty or not a regular file" };
|
|
239
306
|
}
|
|
240
|
-
const fd = openSync(p,
|
|
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:
|
|
311
|
+
if (n < 2) return { ok: false, reason: "too short" };
|
|
245
312
|
const plt = platform();
|
|
246
|
-
if (plt ===
|
|
247
|
-
if (!(buf[0] === 0x4d && buf[1] === 0x5a))
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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 ===
|
|
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 =
|
|
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(
|
|
276
|
-
const isGlobal = process.env.npm_config_global ===
|
|
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() ===
|
|
280
|
-
const binaryExt = isWindows ?
|
|
363
|
+
const isWindows = platform() === "win32";
|
|
364
|
+
const binaryExt = isWindows ? ".exe" : "";
|
|
281
365
|
|
|
282
|
-
const binDir = join(__dirname,
|
|
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(
|
|
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 = [
|
|
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 = (() => {
|
|
308
|
-
|
|
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 {
|
|
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
|
|
422
|
+
if (isWindows) return "@hanzo/dev-win32-x64";
|
|
326
423
|
const plt = platform();
|
|
327
424
|
const cpu = arch();
|
|
328
|
-
if (plt ===
|
|
329
|
-
|
|
330
|
-
if (plt ===
|
|
331
|
-
if (plt ===
|
|
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,
|
|
446
|
+
const src = join(platformPkg.dir, "bin", devBinaryName);
|
|
347
447
|
if (!existsSync(src)) {
|
|
348
|
-
throw new Error(
|
|
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 = (() => {
|
|
353
|
-
|
|
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 {
|
|
467
|
+
try {
|
|
468
|
+
chmodSync(localPath, 0o755);
|
|
469
|
+
} catch {}
|
|
357
470
|
}
|
|
358
|
-
console.log(
|
|
471
|
+
console.log(
|
|
472
|
+
`✓ Installed ${devBinaryName} from ${platformPkg.name} (cached)`,
|
|
473
|
+
);
|
|
359
474
|
continue;
|
|
360
475
|
} catch (e) {
|
|
361
|
-
console.warn(
|
|
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 = (() => {
|
|
369
|
-
|
|
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(
|
|
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
|
|
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
|
|
515
|
+
let safeTempDir = needsIsolation
|
|
516
|
+
? join(tmpdir(), "hanzo", "dev", version)
|
|
517
|
+
: binDir;
|
|
386
518
|
if (needsIsolation) {
|
|
387
519
|
try {
|
|
388
|
-
if (!existsSync(safeTempDir))
|
|
520
|
+
if (!existsSync(safeTempDir))
|
|
521
|
+
mkdirSync(safeTempDir, { recursive: true });
|
|
389
522
|
} catch {
|
|
390
523
|
try {
|
|
391
524
|
safeTempDir = getCacheDir(version);
|
|
392
|
-
if (!existsSync(safeTempDir))
|
|
525
|
+
if (!existsSync(safeTempDir))
|
|
526
|
+
mkdirSync(safeTempDir, { recursive: true });
|
|
393
527
|
} catch {}
|
|
394
528
|
}
|
|
395
529
|
}
|
|
396
|
-
const tmpPath = join(
|
|
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 =
|
|
403
|
-
|
|
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 {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
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 {
|
|
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 {
|
|
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}'`, {
|
|
602
|
+
execSync(`zstd -d '${tmpPath}' -o '${downloadedPath}'`, {
|
|
603
|
+
stdio: "ignore",
|
|
604
|
+
shell: true,
|
|
605
|
+
});
|
|
429
606
|
} catch (e) {
|
|
430
|
-
try {
|
|
431
|
-
|
|
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 {
|
|
614
|
+
try {
|
|
615
|
+
unlinkSync(tmpPath);
|
|
616
|
+
} catch {}
|
|
434
617
|
} else {
|
|
435
618
|
try {
|
|
436
|
-
execSync(`tar -xzf '${tmpPath}' -C '${binDir}'`, {
|
|
619
|
+
execSync(`tar -xzf '${tmpPath}' -C '${binDir}'`, {
|
|
620
|
+
stdio: "ignore",
|
|
621
|
+
shell: true,
|
|
622
|
+
});
|
|
437
623
|
} catch (e) {
|
|
438
|
-
try {
|
|
624
|
+
try {
|
|
625
|
+
unlinkSync(tmpPath);
|
|
626
|
+
} catch {}
|
|
439
627
|
throw new Error(`failed to extract .tar.gz: ${e.message}`);
|
|
440
628
|
}
|
|
441
|
-
try {
|
|
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 {
|
|
640
|
+
try {
|
|
641
|
+
unlinkSync(extractedPath);
|
|
642
|
+
} catch {}
|
|
451
643
|
}
|
|
452
644
|
}
|
|
453
645
|
}
|
|
454
646
|
|
|
455
|
-
const valid = validateDownloadedBinary(
|
|
647
|
+
const valid = validateDownloadedBinary(
|
|
648
|
+
isWin ? cachePath : mirrorToLocal ? localPath : cachePath,
|
|
649
|
+
);
|
|
456
650
|
if (!valid.ok) {
|
|
457
|
-
try {
|
|
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(
|
|
663
|
+
console.log(
|
|
664
|
+
`✓ Installed ${devBinaryName}${isWin || !mirrorToLocal ? " (cached)" : ""}`,
|
|
665
|
+
);
|
|
466
666
|
if (!isWin && mirrorToLocal) {
|
|
467
|
-
try {
|
|
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 (
|
|
680
|
+
if (
|
|
681
|
+
existsSync(mainBinaryPath) ||
|
|
682
|
+
existsSync(
|
|
683
|
+
getCachedBinaryPath(version, targetTriple, platform() === "win32"),
|
|
684
|
+
)
|
|
685
|
+
) {
|
|
479
686
|
try {
|
|
480
|
-
const probePath = existsSync(mainBinaryPath)
|
|
687
|
+
const probePath = existsSync(mainBinaryPath)
|
|
688
|
+
? mainBinaryPath
|
|
689
|
+
: getCachedBinaryPath(version, targetTriple, platform() === "win32");
|
|
481
690
|
const stats = statSync(probePath);
|
|
482
|
-
if (!stats.size)
|
|
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(
|
|
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(
|
|
702
|
+
console.warn(" Try reinstalling or check your network/proxy settings.");
|
|
491
703
|
}
|
|
492
|
-
console.log(
|
|
704
|
+
console.log("✓ Installation complete!");
|
|
493
705
|
} else {
|
|
494
|
-
console.warn(
|
|
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(
|
|
723
|
+
runPostinstall().catch((error) => {
|
|
724
|
+
console.error("Installation failed:", error);
|
|
511
725
|
process.exit(1);
|
|
512
726
|
});
|
|
513
727
|
}
|