@vantaloom/cli 0.15.35 → 0.15.37
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 +1 -1
- package/src/cli.mjs +528 -495
- package/src/lib/install.mjs +16 -3
- package/src/lib/mcp.mjs +264 -0
- package/src/lib/package.mjs +61 -56
package/src/cli.mjs
CHANGED
|
@@ -1,495 +1,528 @@
|
|
|
1
|
-
import {
|
|
2
|
-
existsSync,
|
|
3
|
-
mkdirSync,
|
|
4
|
-
mkdtempSync,
|
|
5
|
-
readFileSync,
|
|
6
|
-
} from "node:fs"
|
|
7
|
-
import os from "node:os"
|
|
8
|
-
import path from "node:path"
|
|
9
|
-
import {
|
|
10
|
-
cliRoot,
|
|
11
|
-
defaultNpmRegistry,
|
|
12
|
-
fallbackNpmRegistries,
|
|
13
|
-
} from "./lib/constants.mjs"
|
|
14
|
-
import {
|
|
15
|
-
platformId,
|
|
16
|
-
runtimePackageName,
|
|
17
|
-
safeDirectory,
|
|
18
|
-
defaultPrefix,
|
|
19
|
-
displayCommand,
|
|
20
|
-
readJSONIfExists,
|
|
21
|
-
normalizeRegistry,
|
|
22
|
-
packageBasename,
|
|
23
|
-
toCamel,
|
|
24
|
-
removeKnownPath,
|
|
25
|
-
run,
|
|
26
|
-
} from "./lib/platform.mjs"
|
|
27
|
-
import {
|
|
28
|
-
applyPackage,
|
|
29
|
-
findSourceRoot,
|
|
30
|
-
tryFindSourceRoot,
|
|
31
|
-
tryAssertSourceRoot,
|
|
32
|
-
readInstalledConfig,
|
|
33
|
-
writeRuntimePackageMetadata,
|
|
34
|
-
ensureInPath,
|
|
35
|
-
} from "./lib/install.mjs"
|
|
36
|
-
import { buildRuntimePackage } from "./lib/package.mjs"
|
|
37
|
-
import {
|
|
38
|
-
resolveNpmPackageWithFallback,
|
|
39
|
-
downloadNpmTarball,
|
|
40
|
-
findExtractedNpmPackage,
|
|
41
|
-
} from "./lib/registry.mjs"
|
|
42
|
-
import {
|
|
43
|
-
runCtl,
|
|
44
|
-
uninstallRuntime,
|
|
45
|
-
} from "./lib/lifecycle.mjs"
|
|
46
|
-
import {
|
|
47
|
-
runLogin,
|
|
48
|
-
runLogout,
|
|
49
|
-
localApiBase,
|
|
50
|
-
} from "./lib/auth.mjs"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (process.env.
|
|
58
|
-
if (process.env.npm_config_strict_ssl === "") return true
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
case "
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
case "
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
case "
|
|
137
|
-
|
|
138
|
-
return
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
console.log(`
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const
|
|
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
|
-
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
})
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
return
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
return
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
case "
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
case "
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
case "
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
case "
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
if (!
|
|
473
|
-
throw new Error(
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
console.log(
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
mkdirSync,
|
|
4
|
+
mkdtempSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
} from "node:fs"
|
|
7
|
+
import os from "node:os"
|
|
8
|
+
import path from "node:path"
|
|
9
|
+
import {
|
|
10
|
+
cliRoot,
|
|
11
|
+
defaultNpmRegistry,
|
|
12
|
+
fallbackNpmRegistries,
|
|
13
|
+
} from "./lib/constants.mjs"
|
|
14
|
+
import {
|
|
15
|
+
platformId,
|
|
16
|
+
runtimePackageName,
|
|
17
|
+
safeDirectory,
|
|
18
|
+
defaultPrefix,
|
|
19
|
+
displayCommand,
|
|
20
|
+
readJSONIfExists,
|
|
21
|
+
normalizeRegistry,
|
|
22
|
+
packageBasename,
|
|
23
|
+
toCamel,
|
|
24
|
+
removeKnownPath,
|
|
25
|
+
run,
|
|
26
|
+
} from "./lib/platform.mjs"
|
|
27
|
+
import {
|
|
28
|
+
applyPackage,
|
|
29
|
+
findSourceRoot,
|
|
30
|
+
tryFindSourceRoot,
|
|
31
|
+
tryAssertSourceRoot,
|
|
32
|
+
readInstalledConfig,
|
|
33
|
+
writeRuntimePackageMetadata,
|
|
34
|
+
ensureInPath,
|
|
35
|
+
} from "./lib/install.mjs"
|
|
36
|
+
import { buildRuntimePackage } from "./lib/package.mjs"
|
|
37
|
+
import {
|
|
38
|
+
resolveNpmPackageWithFallback,
|
|
39
|
+
downloadNpmTarball,
|
|
40
|
+
findExtractedNpmPackage,
|
|
41
|
+
} from "./lib/registry.mjs"
|
|
42
|
+
import {
|
|
43
|
+
runCtl,
|
|
44
|
+
uninstallRuntime,
|
|
45
|
+
} from "./lib/lifecycle.mjs"
|
|
46
|
+
import {
|
|
47
|
+
runLogin,
|
|
48
|
+
runLogout,
|
|
49
|
+
localApiBase,
|
|
50
|
+
} from "./lib/auth.mjs"
|
|
51
|
+
import { runMcp, mcpHint } from "./lib/mcp.mjs"
|
|
52
|
+
|
|
53
|
+
// Inherit strict-ssl=false from npm/npx config, or respect NODE_TLS_REJECT_UNAUTHORIZED.
|
|
54
|
+
// When npm runs us via npx with strict-ssl disabled, it sets npm_config_strict_ssl="false".
|
|
55
|
+
// Also check user .npmrc for strict-ssl=false (common in China behind proxies).
|
|
56
|
+
function shouldDisableTLS() {
|
|
57
|
+
if (process.env.NODE_TLS_REJECT_UNAUTHORIZED === "0") return true
|
|
58
|
+
if (process.env.npm_config_strict_ssl === "false") return true
|
|
59
|
+
if (process.env.npm_config_strict_ssl === "") return true
|
|
60
|
+
try {
|
|
61
|
+
const npmrcPath = path.join(os.homedir(), ".npmrc")
|
|
62
|
+
if (existsSync(npmrcPath)) {
|
|
63
|
+
const content = readFileSync(npmrcPath, "utf8")
|
|
64
|
+
if (/^\s*strict-ssl\s*=\s*false/m.test(content)) return true
|
|
65
|
+
}
|
|
66
|
+
} catch {}
|
|
67
|
+
return false
|
|
68
|
+
}
|
|
69
|
+
if (shouldDisableTLS()) {
|
|
70
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const cliVersion = readJSONIfExists(path.join(cliRoot, "package.json")).version ?? "unknown"
|
|
74
|
+
|
|
75
|
+
export async function main(argv) {
|
|
76
|
+
const command = argv[0] ?? "help"
|
|
77
|
+
// "mcp" 带子命令(status/path/print/install);其余命令的参数全是 --flag。
|
|
78
|
+
let rest = argv.slice(1)
|
|
79
|
+
let subcommand
|
|
80
|
+
if (command === "mcp" && rest[0] && !rest[0].startsWith("--")) {
|
|
81
|
+
subcommand = rest[0]
|
|
82
|
+
rest = rest.slice(1)
|
|
83
|
+
}
|
|
84
|
+
const options = parseOptions(rest)
|
|
85
|
+
|
|
86
|
+
// --no-strict-ssl flag disables TLS certificate verification for fetch calls
|
|
87
|
+
if (options.noStrictSsl) {
|
|
88
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (command === "install" || command === "update") {
|
|
92
|
+
console.log(`vantaloom-cli v${cliVersion} (${platformId()})`)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
switch (command) {
|
|
96
|
+
case "install":
|
|
97
|
+
if (options.package) {
|
|
98
|
+
await installFromPackage({ ...options, update: false })
|
|
99
|
+
} else if (shouldUseSourceInstall(options)) {
|
|
100
|
+
await installFromSource(options)
|
|
101
|
+
} else {
|
|
102
|
+
await syncFromNpmRegistry(options, "install")
|
|
103
|
+
}
|
|
104
|
+
return
|
|
105
|
+
case "update":
|
|
106
|
+
if (options.package) {
|
|
107
|
+
await installFromPackage({ ...options, update: true })
|
|
108
|
+
} else if (shouldUseSourceInstall(options)) {
|
|
109
|
+
await installFromSource({ ...options, update: true })
|
|
110
|
+
} else {
|
|
111
|
+
await syncFromNpmRegistry(options, "update")
|
|
112
|
+
}
|
|
113
|
+
return
|
|
114
|
+
case "package":
|
|
115
|
+
await packageRuntime(options)
|
|
116
|
+
return
|
|
117
|
+
case "platform":
|
|
118
|
+
console.log(platformId())
|
|
119
|
+
return
|
|
120
|
+
case "start":
|
|
121
|
+
case "stop":
|
|
122
|
+
case "restart":
|
|
123
|
+
case "status":
|
|
124
|
+
case "ports":
|
|
125
|
+
runCtl(command, options)
|
|
126
|
+
return
|
|
127
|
+
case "path":
|
|
128
|
+
printPaths(options)
|
|
129
|
+
return
|
|
130
|
+
case "uninstall":
|
|
131
|
+
await uninstallRuntime(options)
|
|
132
|
+
return
|
|
133
|
+
case "login":
|
|
134
|
+
await runLogin(options)
|
|
135
|
+
return
|
|
136
|
+
case "logout":
|
|
137
|
+
await runLogout(options)
|
|
138
|
+
return
|
|
139
|
+
case "bootstrap":
|
|
140
|
+
await runBootstrap(options)
|
|
141
|
+
return
|
|
142
|
+
case "mcp":
|
|
143
|
+
await runMcp(subcommand, options)
|
|
144
|
+
return
|
|
145
|
+
case "help":
|
|
146
|
+
case "-h":
|
|
147
|
+
case "--help":
|
|
148
|
+
printHelp()
|
|
149
|
+
return
|
|
150
|
+
default:
|
|
151
|
+
throw new Error(`unknown command "${command}"`)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function installFromSource(options) {
|
|
156
|
+
const sourceRoot = findSourceRoot(options.source)
|
|
157
|
+
const prefix = safeDirectory(options.prefix ?? defaultPrefix())
|
|
158
|
+
const buildRoot = path.join(sourceRoot, "artifacts", "local-install", platformId())
|
|
159
|
+
|
|
160
|
+
const { version } = await buildRuntimePackage(sourceRoot, buildRoot, {
|
|
161
|
+
buildWeb: options.buildWeb,
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
await applyPackage(buildRoot, prefix, {
|
|
165
|
+
noStart: options.noStart,
|
|
166
|
+
sourceRoot,
|
|
167
|
+
update: options.update,
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
console.log(`${options.update ? "updated" : "installed"} Vantaloom: ${prefix}`)
|
|
171
|
+
console.log(mcpHint(prefix))
|
|
172
|
+
console.log(`version: ${version}`)
|
|
173
|
+
ensureInPath(prefix)
|
|
174
|
+
console.log(`run: ${displayCommand(prefix)} status`)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function installFromPackage(options) {
|
|
178
|
+
const prefix = safeDirectory(options.prefix ?? defaultPrefix())
|
|
179
|
+
const packageRoot = safeDirectory(options.package)
|
|
180
|
+
const version = await applyPackage(packageRoot, prefix, {
|
|
181
|
+
noStart: options.noStart,
|
|
182
|
+
update: options.update,
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
console.log(`${options.update ? "updated" : "installed"} Vantaloom: ${prefix}`)
|
|
186
|
+
console.log(mcpHint(prefix))
|
|
187
|
+
console.log(`version: ${version}`)
|
|
188
|
+
ensureInPath(prefix)
|
|
189
|
+
console.log(`run: ${displayCommand(prefix)} status`)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async function packageRuntime(options) {
|
|
193
|
+
const sourceRoot = findSourceRoot(options.source)
|
|
194
|
+
const targetPlatform = options.target ?? platformId()
|
|
195
|
+
const packageRoot = safeDirectory(
|
|
196
|
+
options.output ?? path.join(sourceRoot, "artifacts", "packages", `vantaloom-${targetPlatform}`)
|
|
197
|
+
)
|
|
198
|
+
const { platform: builtPlatform, version } = await buildRuntimePackage(sourceRoot, packageRoot, {
|
|
199
|
+
buildWeb: options.buildWeb,
|
|
200
|
+
target: targetPlatform,
|
|
201
|
+
npmPackage: options.npmPackage,
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
if (options.npmPackage) {
|
|
205
|
+
writeRuntimePackageMetadata(packageRoot, sourceRoot, builtPlatform)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (options.archive) {
|
|
209
|
+
const archivePath = `${packageRoot}.tar.gz`
|
|
210
|
+
removeKnownPath(archivePath, path.dirname(archivePath))
|
|
211
|
+
run("tar", [
|
|
212
|
+
"-czf",
|
|
213
|
+
archivePath,
|
|
214
|
+
"-C",
|
|
215
|
+
path.dirname(packageRoot),
|
|
216
|
+
path.basename(packageRoot),
|
|
217
|
+
])
|
|
218
|
+
console.log(`archive: ${archivePath}`)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
console.log(`packaged Vantaloom: ${packageRoot}`)
|
|
222
|
+
console.log(`platform: ${builtPlatform}`)
|
|
223
|
+
console.log(`version: ${version}`)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async function syncFromNpmRegistry(options, action) {
|
|
227
|
+
const prefix = safeDirectory(options.prefix ?? defaultPrefix())
|
|
228
|
+
const installedConfig = readInstalledConfig(prefix)
|
|
229
|
+
// Always derive runtimePackage from current platform — never trust stale config
|
|
230
|
+
// from a different platform (e.g. win32 config baked into a darwin package).
|
|
231
|
+
// Only explicit --runtime-package flag can override.
|
|
232
|
+
const runtimePackage = options.runtimePackage || runtimePackageName(platformId())
|
|
233
|
+
const runtimeVersion = options.runtimeVersion || installedConfig.runtimeVersion || "latest"
|
|
234
|
+
const explicitRegistry = options.npmRegistry || installedConfig.npmRegistry
|
|
235
|
+
// Resolve against npmjs.org — the registry the runtime packages are published
|
|
236
|
+
// to. Ambient mirror config (npm_config_registry / .npmrc) is deliberately NOT
|
|
237
|
+
// adopted: mirrors lag on dist-tags (npmmirror still served "latest" = 0.6.12
|
|
238
|
+
// months after 0.15.x shipped), and trusting one turns an update into a silent
|
|
239
|
+
// rollback. Mirrors stay in the fallback chain for unreachable-network cases.
|
|
240
|
+
const registry = normalizeRegistry(explicitRegistry || defaultNpmRegistry)
|
|
241
|
+
const tempRoot = mkdtempSync(path.join(os.tmpdir(), "vantaloom-npm-"))
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
const extractRoot = path.join(tempRoot, "extract")
|
|
245
|
+
const archive = path.join(tempRoot, `${packageBasename(runtimePackage)}-${runtimeVersion}.tgz`)
|
|
246
|
+
mkdirSync(extractRoot, { recursive: true })
|
|
247
|
+
|
|
248
|
+
const resolved = await resolveNpmPackageWithFallback({
|
|
249
|
+
registries: explicitRegistry ? [registry] : [registry, ...fallbackNpmRegistries.map(normalizeRegistry)],
|
|
250
|
+
name: runtimePackage,
|
|
251
|
+
version: runtimeVersion,
|
|
252
|
+
})
|
|
253
|
+
assertNotDowngrade({ prefix, resolved, runtimePackage, options, action })
|
|
254
|
+
await downloadNpmTarball({
|
|
255
|
+
tarballUrl: resolved.tarball,
|
|
256
|
+
target: archive,
|
|
257
|
+
packageName: runtimePackage,
|
|
258
|
+
version: resolved.version,
|
|
259
|
+
})
|
|
260
|
+
run("tar", ["-xzf", archive, "-C", extractRoot])
|
|
261
|
+
|
|
262
|
+
const packageRoot = findExtractedNpmPackage(extractRoot)
|
|
263
|
+
const version = await applyPackage(packageRoot, prefix, {
|
|
264
|
+
noStart: options.noStart,
|
|
265
|
+
runtimePackage,
|
|
266
|
+
runtimeVersion: options.runtimeVersion ? resolved.version : "latest",
|
|
267
|
+
npmRegistry: resolved.registry,
|
|
268
|
+
update: action === "update",
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
console.log(`${action === "update" ? "updated" : "installed"} Vantaloom: ${prefix}`)
|
|
272
|
+
console.log(mcpHint(prefix))
|
|
273
|
+
console.log(`version: ${version}`)
|
|
274
|
+
console.log(`source: ${runtimePackage}@${resolved.version}`)
|
|
275
|
+
console.log(`registry: ${resolved.registry}`)
|
|
276
|
+
ensureInPath(prefix)
|
|
277
|
+
console.log(`run: ${displayCommand(prefix)} status`)
|
|
278
|
+
} finally {
|
|
279
|
+
removeKnownPath(tempRoot, os.tmpdir())
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// A registry mirror can answer "latest" with a version far older than the one
|
|
284
|
+
// already installed (npmmirror did exactly that: 0.6.12 long after 0.15.x
|
|
285
|
+
// shipped), turning an update into a silent rollback. Refuse it unless the
|
|
286
|
+
// caller asked for that exact version or opted in explicitly.
|
|
287
|
+
function assertNotDowngrade({ prefix, resolved, runtimePackage, options, action }) {
|
|
288
|
+
if (action !== "update" || options.runtimeVersion || options.allowDowngrade) {
|
|
289
|
+
return
|
|
290
|
+
}
|
|
291
|
+
const installed = readRuntimeVersion(prefix)
|
|
292
|
+
if (!isOlderVersion(resolved.version, installed)) {
|
|
293
|
+
return
|
|
294
|
+
}
|
|
295
|
+
throw new Error(
|
|
296
|
+
`${resolved.registry} offers ${runtimePackage}@${resolved.version}, older than the installed ${installed}.\n` +
|
|
297
|
+
` A mirror whose dist-tags lag behind npmjs.org is the usual cause.\n` +
|
|
298
|
+
` Retry with --npm-registry https://registry.npmjs.org/, or pass --allow-downgrade to install it anyway.`
|
|
299
|
+
)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Installed runtime version ("" when absent/unreadable — never block on it).
|
|
303
|
+
function readRuntimeVersion(prefix) {
|
|
304
|
+
try {
|
|
305
|
+
return readFileSync(path.join(prefix, "VERSION"), "utf8").trim()
|
|
306
|
+
} catch {
|
|
307
|
+
return ""
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// candidate < installed on the X.Y.Z triple (a -/+ suffix on patch is ignored).
|
|
312
|
+
// Non-semver on either side → false: never block a locally built or unversioned
|
|
313
|
+
// install from being replaced.
|
|
314
|
+
function isOlderVersion(candidate, installed) {
|
|
315
|
+
const parse = (value) => {
|
|
316
|
+
const parts = String(value).split(".")
|
|
317
|
+
if (parts.length < 3) {
|
|
318
|
+
return null
|
|
319
|
+
}
|
|
320
|
+
const triple = [parts[0], parts[1], parts.slice(2).join(".").split(/[-+]/)[0]].map(Number)
|
|
321
|
+
return triple.every(Number.isInteger) ? triple : null
|
|
322
|
+
}
|
|
323
|
+
const found = parse(candidate)
|
|
324
|
+
const current = parse(installed)
|
|
325
|
+
if (!found || !current) {
|
|
326
|
+
return false
|
|
327
|
+
}
|
|
328
|
+
for (let index = 0; index < 3; index += 1) {
|
|
329
|
+
if (found[index] !== current[index]) {
|
|
330
|
+
return found[index] < current[index]
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return false
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function shouldUseSourceInstall(options) {
|
|
337
|
+
return Boolean(options.local || options.source || options.buildWeb)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function printPaths(options) {
|
|
341
|
+
const sourceRoot = findSourceRoot(options.source)
|
|
342
|
+
const prefix = safeDirectory(options.prefix ?? defaultPrefix())
|
|
343
|
+
console.log(JSON.stringify({ sourceRoot, prefix }, null, 2))
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function parseOptions(args) {
|
|
347
|
+
const options = {}
|
|
348
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
349
|
+
const arg = args[index]
|
|
350
|
+
if (!arg.startsWith("--")) {
|
|
351
|
+
throw new Error(`unexpected argument "${arg}"`)
|
|
352
|
+
}
|
|
353
|
+
const [key, inlineValue] = arg.slice(2).split("=", 2)
|
|
354
|
+
switch (key) {
|
|
355
|
+
case "prefix":
|
|
356
|
+
case "source":
|
|
357
|
+
case "component":
|
|
358
|
+
case "package":
|
|
359
|
+
case "output":
|
|
360
|
+
case "runtime-package":
|
|
361
|
+
case "runtime-version":
|
|
362
|
+
case "npm-registry":
|
|
363
|
+
case "hub":
|
|
364
|
+
case "email":
|
|
365
|
+
case "password":
|
|
366
|
+
case "client":
|
|
367
|
+
case "api":
|
|
368
|
+
options[toCamel(key)] = inlineValue ?? args[++index]
|
|
369
|
+
if (!options[toCamel(key)]) {
|
|
370
|
+
throw new Error(`missing value for --${key}`)
|
|
371
|
+
}
|
|
372
|
+
break
|
|
373
|
+
case "build-web":
|
|
374
|
+
options.buildWeb = true
|
|
375
|
+
break
|
|
376
|
+
case "no-start":
|
|
377
|
+
options.noStart = true
|
|
378
|
+
break
|
|
379
|
+
case "archive":
|
|
380
|
+
options.archive = true
|
|
381
|
+
break
|
|
382
|
+
case "npm-package":
|
|
383
|
+
options.npmPackage = true
|
|
384
|
+
break
|
|
385
|
+
case "target":
|
|
386
|
+
options.target = inlineValue ?? args[++index]
|
|
387
|
+
if (!options.target) {
|
|
388
|
+
throw new Error("missing value for --target")
|
|
389
|
+
}
|
|
390
|
+
break
|
|
391
|
+
case "local":
|
|
392
|
+
options.local = true
|
|
393
|
+
break
|
|
394
|
+
case "skip-autostart":
|
|
395
|
+
options.skipAutostart = true
|
|
396
|
+
break
|
|
397
|
+
case "no-strict-ssl":
|
|
398
|
+
options.noStrictSsl = true
|
|
399
|
+
break
|
|
400
|
+
case "allow-downgrade":
|
|
401
|
+
options.allowDowngrade = true
|
|
402
|
+
break
|
|
403
|
+
case "force":
|
|
404
|
+
options.force = true
|
|
405
|
+
break
|
|
406
|
+
case "no-skill":
|
|
407
|
+
options.skill = false
|
|
408
|
+
break
|
|
409
|
+
default:
|
|
410
|
+
throw new Error(`unknown option --${key}`)
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return options
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function printHelp() {
|
|
417
|
+
console.log(`Vantaloom CLI
|
|
418
|
+
|
|
419
|
+
Usage:
|
|
420
|
+
vantaloom install [--prefix <dir>] [--runtime-version <version>] [--npm-registry <url>] [--package <dir>] [--no-start]
|
|
421
|
+
vantaloom install --local [--prefix <dir>] [--source <repo>] [--build-web] [--no-start]
|
|
422
|
+
vantaloom update [--prefix <dir>] [--runtime-version <version>] [--npm-registry <url>] [--no-start] [--allow-downgrade]
|
|
423
|
+
vantaloom update --local [--prefix <dir>] [--source <repo>] [--build-web] [--no-start]
|
|
424
|
+
vantaloom uninstall [--prefix <dir>]
|
|
425
|
+
vantaloom package [--source <repo>] [--output <dir>] [--build-web] [--archive] [--npm-package] [--target <platform>]
|
|
426
|
+
vantaloom start [--prefix <dir>] [--component all|api|agent|web]
|
|
427
|
+
vantaloom stop [--prefix <dir>] [--component all|api|agent|web]
|
|
428
|
+
vantaloom restart [--prefix <dir>] [--component all|api|agent|web]
|
|
429
|
+
vantaloom status [--prefix <dir>]
|
|
430
|
+
vantaloom ports [--prefix <dir>]
|
|
431
|
+
vantaloom path [--prefix <dir>] [--source <repo>]
|
|
432
|
+
vantaloom platform
|
|
433
|
+
vantaloom login [--hub <url>] [--email <email>] [--password <pw>] [--prefix <dir>]
|
|
434
|
+
vantaloom logout [--prefix <dir>]
|
|
435
|
+
vantaloom bootstrap [--hub <url>] --email <email> --password <pw> [--prefix <dir>] [--runtime-version <ver>] [--npm-registry <url>]
|
|
436
|
+
vantaloom mcp [status|path|print|install] [--client claude|cursor|codex|all] [--api <url>] [--force] [--no-skill] [--prefix <dir>]
|
|
437
|
+
|
|
438
|
+
Let another agent drive this machine (MCP bridge):
|
|
439
|
+
"vantaloom mcp install" registers the bundled MCP server (<prefix>/bin/vantaloom-mcp)
|
|
440
|
+
with Claude Code / Cursor / Codex and installs the vantaloom-fleet skill, so an
|
|
441
|
+
external agent can list your machines, run commands, open terminals and move
|
|
442
|
+
files across the mesh — a keyless SSH replacement. "vantaloom mcp" alone prints
|
|
443
|
+
the binary path and per-client registration status; "print" emits the config
|
|
444
|
+
snippets if you would rather paste them yourself. The MCP server is launched by
|
|
445
|
+
the agent client (independent of the Vantaloom runtime lifecycle) but needs the
|
|
446
|
+
local runtime to be running.
|
|
447
|
+
|
|
448
|
+
Hub login (for machines with no web access):
|
|
449
|
+
"vantaloom login" authenticates to the Hub from the terminal and joins this
|
|
450
|
+
machine to your workgroup — no browser needed. Email/password may be passed as
|
|
451
|
+
flags (for scripts) or entered interactively. The local runtime must be running
|
|
452
|
+
(run "vantaloom start" first).
|
|
453
|
+
|
|
454
|
+
Unattended bootstrap (one command for fresh hosts):
|
|
455
|
+
"vantaloom bootstrap" installs the runtime from npm, starts it, waits for it
|
|
456
|
+
to be ready, then logs in to the Hub — all in one step. Designed for ssh/scp
|
|
457
|
+
deployment scripts and cloud-init. Requires --email/--password (or env
|
|
458
|
+
VANTALOOM_HUB_EMAIL/VANTALOOM_HUB_PASSWORD) and --hub (or env
|
|
459
|
+
VANTALOOM_HUB_URL). The runtime auto-starts and reconnects on reboot.
|
|
460
|
+
`)
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// runBootstrap = install runtime (from npm) + wait ready + login Hub,一条命令完成无人值守部署。
|
|
464
|
+
// 面向 ssh/scp 远程部署脚本和 cloud-init 场景;用户在远程主机 npm i -g @vantaloom/cli 后
|
|
465
|
+
// 跑这条命令即可把机器加入 workgroup,无需进 UI。
|
|
466
|
+
async function runBootstrap(options) {
|
|
467
|
+
const prefix = safeDirectory(options.prefix ?? defaultPrefix())
|
|
468
|
+
const hubUrl = options.hub || process.env.VANTALOOM_HUB_URL || process.env.NEXT_PUBLIC_HUB_URL
|
|
469
|
+
const email = options.email || process.env.VANTALOOM_HUB_EMAIL
|
|
470
|
+
const password = options.password || process.env.VANTALOOM_HUB_PASSWORD
|
|
471
|
+
|
|
472
|
+
if (!hubUrl) {
|
|
473
|
+
throw new Error("bootstrap 需要 --hub <url>(或设置 VANTALOOM_HUB_URL 环境变量)")
|
|
474
|
+
}
|
|
475
|
+
if (!email || !password) {
|
|
476
|
+
throw new Error("bootstrap 需要 --email 和 --password(或设置 VANTALOOM_HUB_EMAIL/VANTALOOM_HUB_PASSWORD 环境变量)")
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// 1. 安装 runtime(从 npm 拉取,自动 start)
|
|
480
|
+
console.log(`vantaloom-cli v${cliVersion} (${platformId()})`)
|
|
481
|
+
console.log(`\n▸ 安装 runtime(从 npm)`)
|
|
482
|
+
const installOpts = { ...options, prefix }
|
|
483
|
+
// bootstrap 不允许 --no-start,runtime 必须起来才能 login
|
|
484
|
+
delete installOpts.noStart
|
|
485
|
+
await syncFromNpmRegistry(installOpts, "install")
|
|
486
|
+
|
|
487
|
+
// 2. 等待 runtime 就绪(轮询 /v1/hub/status)
|
|
488
|
+
const localApi = localApiBase(prefix)
|
|
489
|
+
console.log(`\n▸ 等待 runtime 就绪(${localApi})`)
|
|
490
|
+
let ready = false
|
|
491
|
+
for (let i = 0; i < 30; i++) {
|
|
492
|
+
await sleep(2000)
|
|
493
|
+
try {
|
|
494
|
+
const res = await fetch(`${localApi}/v1/hub/status`)
|
|
495
|
+
if (res.ok) {
|
|
496
|
+
ready = true
|
|
497
|
+
console.log(` ✓ runtime 就绪(第 ${i + 1} 次探测)`)
|
|
498
|
+
break
|
|
499
|
+
}
|
|
500
|
+
} catch {}
|
|
501
|
+
if (i % 5 === 0) {
|
|
502
|
+
console.log(` 等待中...(第 ${i + 1} 次)`)
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
if (!ready) {
|
|
506
|
+
throw new Error(`runtime 60s 内未就绪(${localApi} 不可达)。请手动检查 "vantaloom status" 后重试 "vantaloom login"。`)
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// 3. 登录 Hub + 注册机器 + 连接
|
|
510
|
+
console.log(`\n▸ 登录 Hub 并连接`)
|
|
511
|
+
await runLogin({
|
|
512
|
+
hub: hubUrl,
|
|
513
|
+
email,
|
|
514
|
+
password,
|
|
515
|
+
prefix,
|
|
516
|
+
})
|
|
517
|
+
|
|
518
|
+
console.log(`\n════════════════════════════════════════`)
|
|
519
|
+
console.log(` 无人值守部署完成!`)
|
|
520
|
+
console.log(` 安装目录: ${prefix}`)
|
|
521
|
+
console.log(` Hub: ${hubUrl}`)
|
|
522
|
+
console.log(` runtime 已启动并会在重启后自动恢复。`)
|
|
523
|
+
console.log(`════════════════════════════════════════`)
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function sleep(ms) {
|
|
527
|
+
return new Promise(resolve => setTimeout(resolve, ms))
|
|
528
|
+
}
|