@vantaloom/runtime-linux-x64 0.1.5

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 (59) hide show
  1. package/README.md +3 -0
  2. package/VERSION +1 -0
  3. package/bin/vantaloom-agent +0 -0
  4. package/bin/vantaloom-api +0 -0
  5. package/bin/vantaloomctl +0 -0
  6. package/cli/README.md +12 -0
  7. package/cli/bin/vantaloom.mjs +9 -0
  8. package/cli/config.json +8 -0
  9. package/cli/package.json +22 -0
  10. package/cli/src/cli.mjs +878 -0
  11. package/manifest.json +12 -0
  12. package/package.json +27 -0
  13. package/web/404.html +1 -0
  14. package/web/__next.__PAGE__.txt +9 -0
  15. package/web/__next._full.txt +18 -0
  16. package/web/__next._head.txt +5 -0
  17. package/web/__next._index.txt +6 -0
  18. package/web/__next._tree.txt +2 -0
  19. package/web/_next/static/chunks/05de644ba01fa1d2.js +1 -0
  20. package/web/_next/static/chunks/2e32f8acb56ccf66.js +9 -0
  21. package/web/_next/static/chunks/67854c55d1bbbb98.js +58 -0
  22. package/web/_next/static/chunks/7dfeab42587bcc0e.js +1 -0
  23. package/web/_next/static/chunks/7fff36a233a41392.css +2 -0
  24. package/web/_next/static/chunks/9724f3a46d10c4e1.js +1 -0
  25. package/web/_next/static/chunks/a6dad97d9634a72d.js +1 -0
  26. package/web/_next/static/chunks/a6dad97d9634a72d.js.map +1 -0
  27. package/web/_next/static/chunks/b9cfbaaba1fd82be.js +1 -0
  28. package/web/_next/static/chunks/ba682d76034e0aae.js +5 -0
  29. package/web/_next/static/chunks/bb310e4bf0deb70d.js +1 -0
  30. package/web/_next/static/chunks/e1a58c067dbe2ad5.js +18 -0
  31. package/web/_next/static/chunks/eca64d281474b631.js +1 -0
  32. package/web/_next/static/chunks/f83db3c44102c97e.js +1 -0
  33. package/web/_next/static/chunks/turbopack-64a451a64512e5cf.js +4 -0
  34. package/web/_next/static/rDZJjFMnJIH0S-v3k-n2B/_buildManifest.js +11 -0
  35. package/web/_next/static/rDZJjFMnJIH0S-v3k-n2B/_clientMiddlewareManifest.json +1 -0
  36. package/web/_next/static/rDZJjFMnJIH0S-v3k-n2B/_ssgManifest.js +1 -0
  37. package/web/_not-found/__next._full.txt +14 -0
  38. package/web/_not-found/__next._head.txt +5 -0
  39. package/web/_not-found/__next._index.txt +6 -0
  40. package/web/_not-found/__next._not-found.__PAGE__.txt +5 -0
  41. package/web/_not-found/__next._not-found.txt +4 -0
  42. package/web/_not-found/__next._tree.txt +2 -0
  43. package/web/_not-found.html +1 -0
  44. package/web/_not-found.txt +14 -0
  45. package/web/index.html +31 -0
  46. package/web/index.txt +18 -0
  47. package/web/themes/jelly/bg1.png +0 -0
  48. package/web/themes/jelly/bg2.png +0 -0
  49. package/web/themes/jelly/bg3.png +0 -0
  50. package/web/themes/jelly/bg4.png +0 -0
  51. package/web/themes/jelly/bg5.png +0 -0
  52. package/web/themes/jelly/body-background.png +0 -0
  53. package/web/themes/jelly/card1.png +0 -0
  54. package/web/themes/jelly/card2.png +0 -0
  55. package/web/themes/jelly/card3.png +0 -0
  56. package/web/themes/jelly/card4.png +0 -0
  57. package/web/themes/jelly/card5.png +0 -0
  58. package/web/themes/jelly/card6.png +0 -0
  59. package/web/themes/jelly/welcome-card.png +0 -0
@@ -0,0 +1,878 @@
1
+ import { execFileSync, spawnSync } from "node:child_process"
2
+ import {
3
+ chmodSync,
4
+ existsSync,
5
+ mkdirSync,
6
+ mkdtempSync,
7
+ readdirSync,
8
+ readFileSync,
9
+ rmSync,
10
+ writeFileSync,
11
+ } from "node:fs"
12
+ import { cp, writeFile } from "node:fs/promises"
13
+ import os from "node:os"
14
+ import path from "node:path"
15
+ import { fileURLToPath } from "node:url"
16
+
17
+ const cliRoot = path.resolve(fileURLToPath(import.meta.url), "..", "..")
18
+ const repoCandidate = path.resolve(cliRoot, "..", "..")
19
+ const installedConfigPath = path.join(cliRoot, "config.json")
20
+ const defaultReleaseTag = "runtime-latest"
21
+ const defaultRepo = "Timefiles404/Vantaloom-next"
22
+ const defaultNpmRegistry = "https://registry.npmjs.org"
23
+
24
+ export async function main(argv) {
25
+ const command = argv[0] ?? "help"
26
+ const options = parseOptions(argv.slice(1))
27
+
28
+ switch (command) {
29
+ case "install":
30
+ if (options.package) {
31
+ await installFromPackage({ ...options, update: false })
32
+ } else if (shouldUseSourceInstall(options)) {
33
+ await installFromSource(options)
34
+ } else if (shouldUseReleaseSync(options)) {
35
+ await syncFromRelease(options, "install")
36
+ } else {
37
+ await syncFromNpmRegistry(options, "install")
38
+ }
39
+ return
40
+ case "update":
41
+ if (options.package) {
42
+ await installFromPackage({ ...options, update: true })
43
+ } else if (shouldUseSourceInstall(options)) {
44
+ await installFromSource({ ...options, update: true })
45
+ } else if (shouldUseReleaseSync(options)) {
46
+ await syncFromRelease(options, "update")
47
+ } else {
48
+ await syncFromNpmRegistry(options, "update")
49
+ }
50
+ return
51
+ case "package":
52
+ await packageRuntime(options)
53
+ return
54
+ case "platform":
55
+ console.log(platformId())
56
+ return
57
+ case "start":
58
+ case "stop":
59
+ case "restart":
60
+ case "status":
61
+ case "ports":
62
+ runCtl(command, options)
63
+ return
64
+ case "path":
65
+ printPaths(options)
66
+ return
67
+ case "help":
68
+ case "-h":
69
+ case "--help":
70
+ printHelp()
71
+ return
72
+ default:
73
+ throw new Error(`unknown command "${command}"`)
74
+ }
75
+ }
76
+
77
+ async function installFromSource(options) {
78
+ const sourceRoot = findSourceRoot(options.source)
79
+ const prefix = safeDirectory(options.prefix ?? defaultPrefix())
80
+ const buildRoot = path.join(sourceRoot, "artifacts", "local-install", platformId())
81
+
82
+ const { version } = await buildRuntimePackage(sourceRoot, buildRoot, {
83
+ buildWeb: options.buildWeb,
84
+ })
85
+
86
+ await applyPackage(buildRoot, prefix, {
87
+ noStart: options.noStart,
88
+ sourceRoot,
89
+ update: options.update,
90
+ })
91
+
92
+ console.log(`${options.update ? "updated" : "installed"} Vantaloom: ${prefix}`)
93
+ console.log(`version: ${version}`)
94
+ console.log(`run: ${displayCommand(prefix)} status`)
95
+ }
96
+
97
+ async function installFromPackage(options) {
98
+ const prefix = safeDirectory(options.prefix ?? defaultPrefix())
99
+ const packageRoot = safeDirectory(options.package)
100
+ const version = await applyPackage(packageRoot, prefix, {
101
+ noStart: options.noStart,
102
+ update: options.update,
103
+ })
104
+
105
+ console.log(`${options.update ? "updated" : "installed"} Vantaloom: ${prefix}`)
106
+ console.log(`version: ${version}`)
107
+ console.log(`run: ${displayCommand(prefix)} status`)
108
+ }
109
+
110
+ async function packageRuntime(options) {
111
+ const sourceRoot = findSourceRoot(options.source)
112
+ const packageRoot = safeDirectory(
113
+ options.output ?? path.join(sourceRoot, "artifacts", "packages", `vantaloom-${platformId()}`)
114
+ )
115
+ const { platform: builtPlatform, version } = await buildRuntimePackage(sourceRoot, packageRoot, {
116
+ buildWeb: options.buildWeb,
117
+ })
118
+
119
+ if (options.npmPackage) {
120
+ writeRuntimePackageMetadata(packageRoot, sourceRoot, builtPlatform)
121
+ }
122
+
123
+ if (options.archive) {
124
+ const archivePath = `${packageRoot}.tar.gz`
125
+ removeKnownPath(archivePath, path.dirname(archivePath))
126
+ run("tar", [
127
+ "-czf",
128
+ archivePath,
129
+ "-C",
130
+ path.dirname(packageRoot),
131
+ path.basename(packageRoot),
132
+ ])
133
+ console.log(`archive: ${archivePath}`)
134
+ }
135
+
136
+ console.log(`packaged Vantaloom: ${packageRoot}`)
137
+ console.log(`platform: ${builtPlatform}`)
138
+ console.log(`version: ${version}`)
139
+ }
140
+
141
+ async function buildRuntimePackage(sourceRoot, packageRoot, options) {
142
+ const version = gitVersion(sourceRoot)
143
+ const platform = platformId()
144
+ const buildBin = path.join(packageRoot, "bin")
145
+ const buildWeb = path.join(packageRoot, "web")
146
+
147
+ removeKnownPath(packageRoot, path.dirname(packageRoot))
148
+ mkdirSync(buildBin, { recursive: true })
149
+
150
+ buildGo(sourceRoot, buildBin, "vantaloom-api")
151
+ buildGo(sourceRoot, buildBin, "vantaloom-agent")
152
+ buildGo(sourceRoot, buildBin, "vantaloomctl")
153
+
154
+ if (options.buildWeb) {
155
+ runPnpm(["--filter", "vantaloom-app", "build"], { cwd: sourceRoot })
156
+ }
157
+
158
+ await copyStaticWeb(sourceRoot, buildWeb)
159
+ await copyCliDirectory(path.join(packageRoot, "cli"), sourceRoot, runtimeConfigFromSource(sourceRoot))
160
+ writeBuildManifest(packageRoot, version, platform)
161
+
162
+ return { platform, version }
163
+ }
164
+
165
+ async function applyPackage(packageRoot, prefix, options) {
166
+ assertRuntimePackage(packageRoot)
167
+
168
+ const existingConfig = readInstalledConfig(prefix)
169
+ const packageConfig = readJSONIfExists(path.join(packageRoot, "cli", "config.json"))
170
+ const mergedConfig = mergeRuntimeConfig(packageConfig, existingConfig, {
171
+ sourceRoot: options.sourceRoot,
172
+ })
173
+ const version = readVersion(packageRoot)
174
+
175
+ mkdirSync(prefix, { recursive: true })
176
+ const existingCtl = path.join(prefix, "bin", binaryName("vantaloomctl"))
177
+ if (existsSync(existingCtl)) {
178
+ spawnSync(existingCtl, ["stop", "--prefix", prefix], { stdio: "inherit" })
179
+ }
180
+
181
+ for (const name of ["bin", "web", "cli"]) {
182
+ removeKnownPath(path.join(prefix, name), prefix)
183
+ await cp(path.join(packageRoot, name), path.join(prefix, name), {
184
+ recursive: true,
185
+ force: true,
186
+ dereference: false,
187
+ })
188
+ }
189
+
190
+ await writeLauncher(prefix)
191
+ await writeText(path.join(prefix, "cli", "config.json"), `${JSON.stringify(mergedConfig, null, 2)}\n`)
192
+ await writeText(path.join(prefix, "VERSION"), `${version}\n`)
193
+ await cp(path.join(packageRoot, "manifest.json"), path.join(prefix, "manifest.json"), {
194
+ force: true,
195
+ })
196
+
197
+ run(path.join(prefix, "bin", binaryName("vantaloomctl")), [
198
+ "install",
199
+ "--prefix",
200
+ prefix,
201
+ "--version",
202
+ version,
203
+ ])
204
+
205
+ if (!options.noStart) {
206
+ run(path.join(prefix, "bin", binaryName("vantaloomctl")), [
207
+ "start",
208
+ "--prefix",
209
+ prefix,
210
+ ])
211
+ }
212
+
213
+ return version
214
+ }
215
+
216
+ async function syncFromNpmRegistry(options, action) {
217
+ const prefix = safeDirectory(options.prefix ?? defaultPrefix())
218
+ const installedConfig = readInstalledConfig(prefix)
219
+ const runtimePackage = options.runtimePackage || installedConfig.runtimePackage || runtimePackageName(platformId())
220
+ const runtimeVersion = options.runtimeVersion || installedConfig.runtimeVersion || "latest"
221
+ const registry = normalizeRegistry(options.npmRegistry || installedConfig.npmRegistry || defaultNpmRegistry)
222
+ const tempRoot = mkdtempSync(path.join(os.tmpdir(), "vantaloom-npm-"))
223
+
224
+ try {
225
+ const extractRoot = path.join(tempRoot, "extract")
226
+ const archive = path.join(tempRoot, `${packageBasename(runtimePackage)}-${runtimeVersion}.tgz`)
227
+ mkdirSync(extractRoot, { recursive: true })
228
+
229
+ const resolved = await resolveNpmPackage({ registry, name: runtimePackage, version: runtimeVersion })
230
+ await downloadNpmTarball({
231
+ tarballUrl: resolved.tarball,
232
+ target: archive,
233
+ packageName: runtimePackage,
234
+ version: resolved.version,
235
+ })
236
+ run("tar", ["-xzf", archive, "-C", extractRoot])
237
+
238
+ const packageRoot = findExtractedNpmPackage(extractRoot)
239
+ const version = await applyPackage(packageRoot, prefix, {
240
+ noStart: options.noStart,
241
+ runtimePackage,
242
+ runtimeVersion: options.runtimeVersion ? resolved.version : "latest",
243
+ npmRegistry: registry,
244
+ update: action === "update",
245
+ })
246
+
247
+ console.log(`${action === "update" ? "updated" : "installed"} Vantaloom: ${prefix}`)
248
+ console.log(`version: ${version}`)
249
+ console.log(`source: ${runtimePackage}@${resolved.version}`)
250
+ console.log(`run: ${displayCommand(prefix)} status`)
251
+ } finally {
252
+ removeKnownPath(tempRoot, os.tmpdir())
253
+ }
254
+ }
255
+
256
+ async function syncFromRelease(options, action) {
257
+ const prefix = safeDirectory(options.prefix ?? defaultPrefix())
258
+ const installedConfig = readInstalledConfig(prefix)
259
+ const sourceRoot = installedConfig.sourceRoot
260
+ ? tryAssertSourceRoot(installedConfig.sourceRoot)
261
+ : tryFindSourceRoot()
262
+ const sourceRemote = sourceRoot ? gitRemoteUrl(sourceRoot) : ""
263
+ const repo = options.repo || installedConfig.repo || inferGitHubRepo(options.remote ?? installedConfig.remote ?? sourceRemote) || defaultRepo
264
+ const releaseTag = options.releaseTag || installedConfig.releaseTag || defaultReleaseTag
265
+ const tempRoot = mkdtempSync(path.join(os.tmpdir(), "vantaloom-update-"))
266
+
267
+ try {
268
+ const extractRoot = path.join(tempRoot, "extract")
269
+ const archive = path.join(tempRoot, `vantaloom-${platformId()}.tar.gz`)
270
+ mkdirSync(extractRoot, { recursive: true })
271
+
272
+ await downloadReleaseAsset({
273
+ repo,
274
+ releaseTag,
275
+ assetName: `vantaloom-${platformId()}.tar.gz`,
276
+ target: archive,
277
+ token: options.githubToken,
278
+ })
279
+ run("tar", ["-xzf", archive, "-C", extractRoot])
280
+
281
+ const packageRoot = findExtractedPackage(extractRoot, platformId())
282
+ const version = await applyPackage(packageRoot, prefix, {
283
+ noStart: options.noStart,
284
+ sourceRoot: installedConfig.sourceRoot,
285
+ update: action === "update",
286
+ })
287
+
288
+ console.log(`${action === "update" ? "updated" : "installed"} Vantaloom: ${prefix}`)
289
+ console.log(`version: ${version}`)
290
+ console.log(`source: ${repo}@${releaseTag}`)
291
+ console.log(`run: ${displayCommand(prefix)} status`)
292
+ } finally {
293
+ removeKnownPath(tempRoot, os.tmpdir())
294
+ }
295
+ }
296
+
297
+ async function downloadReleaseAsset({ repo, releaseTag, assetName, target, token }) {
298
+ const releaseUrl = `https://api.github.com/repos/${repo}/releases/tags/${releaseTag}`
299
+ const headers = {
300
+ "User-Agent": "vantaloom-cli",
301
+ }
302
+ const githubToken = token || process.env.VANTALOOM_GITHUB_TOKEN || process.env.GITHUB_TOKEN
303
+ if (githubToken) {
304
+ headers.Authorization = `Bearer ${githubToken}`
305
+ }
306
+
307
+ const releaseResponse = await fetch(releaseUrl, { headers })
308
+ if (!releaseResponse.ok) {
309
+ const detail = await releaseResponse.text().catch(() => "")
310
+ const authHint = releaseResponse.status === 404 || releaseResponse.status === 403
311
+ ? " If the repository is private, set VANTALOOM_GITHUB_TOKEN to a GitHub token that can read releases."
312
+ : ""
313
+ throw new Error(`failed to inspect ${repo}@${releaseTag}: HTTP ${releaseResponse.status}.${authHint}${detail ? ` ${detail.slice(0, 200)}` : ""}`)
314
+ }
315
+
316
+ const release = await releaseResponse.json()
317
+ const asset = release.assets?.find((asset) => asset.name === assetName)
318
+ if (!asset) {
319
+ const names = release.assets?.map((asset) => asset.name).join(", ") || "none"
320
+ throw new Error(`missing release asset ${assetName} in ${repo}@${releaseTag}; available assets: ${names}`)
321
+ }
322
+
323
+ const response = await fetch(asset.url, {
324
+ headers: {
325
+ ...headers,
326
+ Accept: "application/octet-stream",
327
+ },
328
+ })
329
+ if (!response.ok) {
330
+ const detail = await response.text().catch(() => "")
331
+ const authHint = response.status === 404 || response.status === 403
332
+ ? " If the repository is private, set VANTALOOM_GITHUB_TOKEN to a GitHub token that can read releases."
333
+ : ""
334
+ throw new Error(`failed to download ${assetName} from ${repo}@${releaseTag}: HTTP ${response.status}.${authHint}${detail ? ` ${detail.slice(0, 200)}` : ""}`)
335
+ }
336
+
337
+ const buffer = Buffer.from(await response.arrayBuffer())
338
+ await writeFile(target, buffer)
339
+ }
340
+
341
+ async function resolveNpmPackage({ registry, name, version }) {
342
+ const metadataUrl = `${registry}/${encodeURIComponent(name).replace("%2F", "%2f")}`
343
+ const response = await fetch(metadataUrl, {
344
+ headers: {
345
+ Accept: "application/vnd.npm.install-v1+json",
346
+ "User-Agent": "vantaloom-cli",
347
+ },
348
+ })
349
+ if (!response.ok) {
350
+ const detail = await response.text().catch(() => "")
351
+ throw new Error(`failed to inspect npm package ${name}: HTTP ${response.status}${detail ? ` ${detail.slice(0, 200)}` : ""}`)
352
+ }
353
+
354
+ const metadata = await response.json()
355
+ const selectedVersion = metadata["dist-tags"]?.[version] ?? version
356
+ const packageVersion = metadata.versions?.[selectedVersion]
357
+ if (!packageVersion?.dist?.tarball) {
358
+ const available = Object.keys(metadata.versions ?? {}).slice(-8).join(", ") || "none"
359
+ throw new Error(`missing npm package ${name}@${version}; available versions: ${available}`)
360
+ }
361
+ return {
362
+ version: selectedVersion,
363
+ tarball: packageVersion.dist.tarball,
364
+ }
365
+ }
366
+
367
+ async function downloadNpmTarball({ tarballUrl, target, packageName, version }) {
368
+ const response = await fetch(tarballUrl, {
369
+ headers: {
370
+ "User-Agent": "vantaloom-cli",
371
+ },
372
+ })
373
+ if (!response.ok) {
374
+ const detail = await response.text().catch(() => "")
375
+ throw new Error(`failed to download ${packageName}@${version}: HTTP ${response.status}${detail ? ` ${detail.slice(0, 200)}` : ""}`)
376
+ }
377
+
378
+ const buffer = Buffer.from(await response.arrayBuffer())
379
+ await writeFile(target, buffer)
380
+ }
381
+
382
+ function shouldUseSourceInstall(options) {
383
+ return Boolean(options.local || options.source || options.buildWeb)
384
+ }
385
+
386
+ function shouldUseReleaseSync(options) {
387
+ return Boolean(options.repo || options.remote || options.releaseTag || options.githubToken)
388
+ }
389
+
390
+ function assertRuntimePackage(packageRoot) {
391
+ for (const name of ["bin", "web", "cli", "manifest.json"]) {
392
+ if (!existsSync(path.join(packageRoot, name))) {
393
+ throw new Error(`invalid Vantaloom package, missing ${name}: ${packageRoot}`)
394
+ }
395
+ }
396
+ }
397
+
398
+ function readVersion(packageRoot) {
399
+ const versionPath = path.join(packageRoot, "VERSION")
400
+ if (existsSync(versionPath)) {
401
+ return readFileSync(versionPath, "utf8").trim() || "dev"
402
+ }
403
+ const manifest = readJSONIfExists(path.join(packageRoot, "manifest.json"))
404
+ return manifest.version ?? "dev"
405
+ }
406
+
407
+ function readInstalledConfig(prefix) {
408
+ return readJSONIfExists(path.join(prefix, "cli", "config.json"))
409
+ }
410
+
411
+ function readJSONIfExists(filePath) {
412
+ if (!existsSync(filePath)) {
413
+ return {}
414
+ }
415
+ return JSON.parse(readFileSync(filePath, "utf8"))
416
+ }
417
+
418
+ function mergeRuntimeConfig(packageConfig, existingConfig, overrides) {
419
+ const merged = { ...packageConfig }
420
+ for (const key of ["sourceRoot", "remote", "repo", "releaseTag", "runtimePackage", "runtimeVersion", "npmRegistry"]) {
421
+ if (!merged[key] && existingConfig[key]) {
422
+ merged[key] = existingConfig[key]
423
+ }
424
+ }
425
+ if (overrides.sourceRoot) {
426
+ merged.sourceRoot = overrides.sourceRoot
427
+ }
428
+ if (overrides.runtimePackage) {
429
+ merged.runtimePackage = overrides.runtimePackage
430
+ }
431
+ if (overrides.runtimeVersion) {
432
+ merged.runtimeVersion = overrides.runtimeVersion
433
+ }
434
+ if (overrides.npmRegistry) {
435
+ merged.npmRegistry = overrides.npmRegistry
436
+ }
437
+ if (!merged.repo) {
438
+ merged.repo = defaultRepo
439
+ }
440
+ if (!merged.releaseTag) {
441
+ merged.releaseTag = defaultReleaseTag
442
+ }
443
+ if (!merged.runtimePackage) {
444
+ merged.runtimePackage = runtimePackageName(platformId())
445
+ }
446
+ if (!merged.runtimeVersion) {
447
+ merged.runtimeVersion = "latest"
448
+ }
449
+ if (!merged.npmRegistry) {
450
+ merged.npmRegistry = defaultNpmRegistry
451
+ }
452
+ return merged
453
+ }
454
+
455
+ function runtimeConfigFromSource(sourceRoot) {
456
+ const remote = gitRemoteUrl(sourceRoot)
457
+ const repo = inferGitHubRepo(remote) || defaultRepo
458
+ return {
459
+ ...(process.env.GITHUB_ACTIONS ? {} : { sourceRoot }),
460
+ ...(remote ? { remote } : {}),
461
+ repo,
462
+ releaseTag: defaultReleaseTag,
463
+ runtimePackage: runtimePackageName(platformId()),
464
+ runtimeVersion: "latest",
465
+ npmRegistry: defaultNpmRegistry,
466
+ }
467
+ }
468
+
469
+ function findExtractedPackage(extractRoot, platform) {
470
+ const expected = path.join(extractRoot, `vantaloom-${platform}`)
471
+ if (existsSync(expected)) {
472
+ return expected
473
+ }
474
+
475
+ const directories = readdirSync(extractRoot, { withFileTypes: true })
476
+ .filter((entry) => entry.isDirectory())
477
+ .map((entry) => path.join(extractRoot, entry.name))
478
+ if (directories.length === 1) {
479
+ return directories[0]
480
+ }
481
+
482
+ throw new Error(`could not find extracted Vantaloom package in ${extractRoot}`)
483
+ }
484
+
485
+ function findExtractedNpmPackage(extractRoot) {
486
+ const expected = path.join(extractRoot, "package")
487
+ if (existsSync(expected)) {
488
+ return expected
489
+ }
490
+
491
+ const directories = readdirSync(extractRoot, { withFileTypes: true })
492
+ .filter((entry) => entry.isDirectory())
493
+ .map((entry) => path.join(extractRoot, entry.name))
494
+ if (directories.length === 1) {
495
+ return directories[0]
496
+ }
497
+
498
+ throw new Error(`could not find extracted npm package in ${extractRoot}`)
499
+ }
500
+
501
+ function tryFindSourceRoot() {
502
+ try {
503
+ return findSourceRoot()
504
+ } catch {
505
+ return ""
506
+ }
507
+ }
508
+
509
+ function tryAssertSourceRoot(sourceRoot) {
510
+ try {
511
+ return assertSourceRoot(sourceRoot)
512
+ } catch {
513
+ return ""
514
+ }
515
+ }
516
+
517
+ function gitRemoteUrl(sourceRoot) {
518
+ const result = spawnSync("git", ["remote", "get-url", "origin"], {
519
+ cwd: sourceRoot,
520
+ encoding: "utf8",
521
+ })
522
+ if (result.status === 0) {
523
+ return result.stdout.trim()
524
+ }
525
+ return ""
526
+ }
527
+
528
+ function inferGitHubRepo(remote) {
529
+ if (!remote) {
530
+ return ""
531
+ }
532
+ const normalized = remote.replace(/\.git$/, "")
533
+ const httpsMatch = normalized.match(/github\.com[:/]([^/]+\/[^/]+)$/)
534
+ if (httpsMatch) {
535
+ return httpsMatch[1]
536
+ }
537
+ const sshMatch = normalized.match(/^[^:]+:([^/]+\/[^/]+)$/)
538
+ return sshMatch?.[1] ?? ""
539
+ }
540
+
541
+ async function copyStaticWeb(sourceRoot, buildWeb) {
542
+ const exportRoot = path.join(sourceRoot, "apps", "vantaloom", "out")
543
+ if (!existsSync(exportRoot)) {
544
+ throw new Error(
545
+ "missing Next static export output; let GitHub CI run production build, or pass --build-web for a local one-off build"
546
+ )
547
+ }
548
+
549
+ removeKnownPath(buildWeb, path.dirname(buildWeb))
550
+ await copyDir(exportRoot, buildWeb)
551
+ }
552
+
553
+ async function copyCliDirectory(target, sourceRoot, config) {
554
+ const sourceCliRoot = path.join(sourceRoot, "packages", "cli")
555
+ if (!existsSync(path.join(sourceCliRoot, "bin", "vantaloom.mjs"))) {
556
+ throw new Error(`missing source CLI package: ${sourceCliRoot}`)
557
+ }
558
+ removeKnownPath(target, path.dirname(target))
559
+ mkdirSync(target, { recursive: true })
560
+ await copyDir(sourceCliRoot, target)
561
+ await writeText(
562
+ path.join(target, "config.json"),
563
+ `${JSON.stringify(config, null, 2)}\n`
564
+ )
565
+ }
566
+
567
+ async function writeLauncher(prefix) {
568
+ if (process.platform === "win32") {
569
+ await writeText(
570
+ path.join(prefix, "vantaloom.cmd"),
571
+ `@echo off\r\nnode "%~dp0cli\\bin\\vantaloom.mjs" %*\r\n`
572
+ )
573
+ } else {
574
+ const launcher = `#!/usr/bin/env sh\nexec node "$(dirname "$0")/cli/bin/vantaloom.mjs" "$@"\n`
575
+ const launcherPath = path.join(prefix, "vantaloom")
576
+ await writeText(launcherPath, launcher)
577
+ chmodSync(launcherPath, 0o755)
578
+ }
579
+ }
580
+
581
+ function runCtl(command, options) {
582
+ const prefix = safeDirectory(options.prefix ?? defaultPrefix())
583
+ const ctl = path.join(prefix, "bin", binaryName("vantaloomctl"))
584
+ if (!existsSync(ctl)) {
585
+ throw new Error(`missing installed runtime at ${prefix}; run "vantaloom install" first`)
586
+ }
587
+
588
+ const args = [command, "--prefix", prefix]
589
+ if (options.component) {
590
+ args.push("--component", options.component)
591
+ }
592
+ run(ctl, args)
593
+ }
594
+
595
+ function printPaths(options) {
596
+ const sourceRoot = findSourceRoot(options.source)
597
+ const prefix = safeDirectory(options.prefix ?? defaultPrefix())
598
+ console.log(JSON.stringify({ sourceRoot, prefix }, null, 2))
599
+ }
600
+
601
+ function buildGo(sourceRoot, buildBin, name) {
602
+ run("go", [
603
+ "build",
604
+ "-o",
605
+ path.join(buildBin, binaryName(name)),
606
+ `./apps/api/cmd/${name}`,
607
+ ], { cwd: sourceRoot })
608
+ }
609
+
610
+ async function copyDir(source, destination, options = {}) {
611
+ if (!existsSync(source)) {
612
+ throw new Error(`missing source directory: ${source}`)
613
+ }
614
+ removeKnownPath(destination, path.dirname(destination))
615
+ mkdirSync(destination, { recursive: true })
616
+ await cp(source, destination, {
617
+ recursive: true,
618
+ force: true,
619
+ dereference: options.dereference ?? true,
620
+ })
621
+ }
622
+
623
+ function writeBuildManifest(buildRoot, version, platform) {
624
+ writeFileSync(path.join(buildRoot, "VERSION"), `${version}\n`)
625
+ writeFileSync(
626
+ path.join(buildRoot, "manifest.json"),
627
+ `${JSON.stringify(
628
+ {
629
+ name: "Vantaloom Local Runtime",
630
+ version,
631
+ platform,
632
+ updatedAt: new Date().toISOString(),
633
+ components: ["api", "agent", "web", "ctl"],
634
+ },
635
+ null,
636
+ 2
637
+ )}\n`
638
+ )
639
+ }
640
+
641
+ function writeRuntimePackageMetadata(packageRoot, sourceRoot, platform) {
642
+ const version = npmPackageVersion(sourceRoot)
643
+ const { os: runtimeOS, cpu } = parsePlatformId(platform)
644
+ const name = runtimePackageName(platform)
645
+ writeFileSync(
646
+ path.join(packageRoot, "package.json"),
647
+ `${JSON.stringify(
648
+ {
649
+ name,
650
+ version,
651
+ private: false,
652
+ description: `Vantaloom local runtime for ${platform}.`,
653
+ type: "module",
654
+ os: [runtimeOS],
655
+ cpu: [cpu],
656
+ files: ["bin", "web", "cli", "manifest.json", "VERSION", "README.md"],
657
+ publishConfig: {
658
+ access: "public",
659
+ },
660
+ engines: {
661
+ node: ">=20",
662
+ },
663
+ },
664
+ null,
665
+ 2
666
+ )}\n`
667
+ )
668
+ writeFileSync(
669
+ path.join(packageRoot, "README.md"),
670
+ `# ${name}\n\nPlatform runtime package for Vantaloom ${platform}. Install @vantaloom/cli instead of this package directly.\n`
671
+ )
672
+ }
673
+
674
+ function findSourceRoot(sourceOption) {
675
+ if (sourceOption) {
676
+ return assertSourceRoot(path.resolve(sourceOption))
677
+ }
678
+ if (process.env.VANTALOOM_SOURCE) {
679
+ return assertSourceRoot(path.resolve(process.env.VANTALOOM_SOURCE))
680
+ }
681
+ if (existsSync(installedConfigPath)) {
682
+ const config = JSON.parse(readFileSync(installedConfigPath, "utf8"))
683
+ if (config.sourceRoot) {
684
+ return assertSourceRoot(path.resolve(config.sourceRoot))
685
+ }
686
+ }
687
+ return assertSourceRoot(repoCandidate)
688
+ }
689
+
690
+ function npmPackageVersion(sourceRoot) {
691
+ const packageJSON = readJSONIfExists(path.join(sourceRoot, "packages", "cli", "package.json"))
692
+ return packageJSON.version ?? "0.0.0"
693
+ }
694
+
695
+ function assertSourceRoot(sourceRoot) {
696
+ if (!existsSync(path.join(sourceRoot, "apps", "api", "go.mod"))) {
697
+ throw new Error(`not a Vantaloom source root: ${sourceRoot}`)
698
+ }
699
+ return sourceRoot
700
+ }
701
+
702
+ function gitVersion(sourceRoot) {
703
+ const result = spawnSync("git", ["rev-parse", "--short", "HEAD"], {
704
+ cwd: sourceRoot,
705
+ encoding: "utf8",
706
+ })
707
+ if (result.status === 0) {
708
+ return result.stdout.trim() || "dev"
709
+ }
710
+ return "dev"
711
+ }
712
+
713
+ function parseOptions(args) {
714
+ const options = {}
715
+ for (let index = 0; index < args.length; index += 1) {
716
+ const arg = args[index]
717
+ if (!arg.startsWith("--")) {
718
+ throw new Error(`unexpected argument "${arg}"`)
719
+ }
720
+ const [key, inlineValue] = arg.slice(2).split("=", 2)
721
+ switch (key) {
722
+ case "prefix":
723
+ case "source":
724
+ case "component":
725
+ case "package":
726
+ case "output":
727
+ case "repo":
728
+ case "remote":
729
+ case "release-tag":
730
+ case "github-token":
731
+ case "runtime-package":
732
+ case "runtime-version":
733
+ case "npm-registry":
734
+ options[toCamel(key)] = inlineValue ?? args[++index]
735
+ if (!options[toCamel(key)]) {
736
+ throw new Error(`missing value for --${key}`)
737
+ }
738
+ break
739
+ case "build-web":
740
+ options.buildWeb = true
741
+ break
742
+ case "no-start":
743
+ options.noStart = true
744
+ break
745
+ case "archive":
746
+ options.archive = true
747
+ break
748
+ case "npm-package":
749
+ options.npmPackage = true
750
+ break
751
+ case "local":
752
+ options.local = true
753
+ break
754
+ default:
755
+ throw new Error(`unknown option --${key}`)
756
+ }
757
+ }
758
+ return options
759
+ }
760
+
761
+ function safeDirectory(value) {
762
+ const full = path.resolve(value)
763
+ const parsed = path.parse(full)
764
+ if (full === parsed.root) {
765
+ throw new Error(`refusing to operate on unsafe directory: ${value}`)
766
+ }
767
+ return full
768
+ }
769
+
770
+ function removeKnownPath(target, expectedParent) {
771
+ if (!existsSync(target)) {
772
+ return
773
+ }
774
+ const full = path.resolve(target)
775
+ const parent = path.resolve(expectedParent)
776
+ const prefix = parent.endsWith(path.sep) ? parent : `${parent}${path.sep}`
777
+ if (!full.startsWith(prefix)) {
778
+ throw new Error(`refusing to remove path outside expected parent: ${full}`)
779
+ }
780
+ rmSync(full, { recursive: true, force: true })
781
+ }
782
+
783
+ function run(command, args, options = {}) {
784
+ try {
785
+ execFileSync(command, args, { stdio: "inherit", ...options })
786
+ } catch (error) {
787
+ if (error && typeof error.status === "number") {
788
+ throw new Error(`${command} exited with ${error.status}`)
789
+ }
790
+ throw error
791
+ }
792
+ }
793
+
794
+ function runPnpm(args, options = {}) {
795
+ if (process.platform === "win32") {
796
+ run("cmd.exe", ["/d", "/s", "/c", "pnpm", ...args], options)
797
+ return
798
+ }
799
+ run("pnpm", args, options)
800
+ }
801
+
802
+ function binaryName(name) {
803
+ return process.platform === "win32" ? `${name}.exe` : name
804
+ }
805
+
806
+ function platformId() {
807
+ return `${process.platform}-${process.arch}`
808
+ }
809
+
810
+ function runtimePackageName(platform) {
811
+ return `@vantaloom/runtime-${platform}`
812
+ }
813
+
814
+ function parsePlatformId(platform) {
815
+ const parts = platform.split("-")
816
+ const cpu = parts.pop()
817
+ const runtimeOS = parts.join("-")
818
+ if (!runtimeOS || !cpu) {
819
+ throw new Error(`invalid platform id: ${platform}`)
820
+ }
821
+ return { os: runtimeOS, cpu }
822
+ }
823
+
824
+ function packageBasename(name) {
825
+ return name.split("/").pop() ?? name
826
+ }
827
+
828
+ function normalizeRegistry(value) {
829
+ return value.replace(/\/+$/, "")
830
+ }
831
+
832
+ function defaultPrefix() {
833
+ if (process.env.VANTALOOM_HOME) {
834
+ return process.env.VANTALOOM_HOME
835
+ }
836
+ if (process.platform === "win32") {
837
+ return "D:\\Vantaloom"
838
+ }
839
+ if (process.platform === "darwin") {
840
+ return path.join(os.homedir(), "Applications", "Vantaloom")
841
+ }
842
+ return path.join(os.homedir(), ".local", "vantaloom")
843
+ }
844
+
845
+ function displayCommand(prefix) {
846
+ if (process.platform === "win32") {
847
+ return path.join(prefix, "vantaloom.cmd")
848
+ }
849
+ return path.join(prefix, "vantaloom")
850
+ }
851
+
852
+ function toCamel(value) {
853
+ return value.replace(/-([a-z])/g, (_, char) => char.toUpperCase())
854
+ }
855
+
856
+ async function writeText(filePath, content) {
857
+ mkdirSync(path.dirname(filePath), { recursive: true })
858
+ await writeFile(filePath, content)
859
+ }
860
+
861
+ function printHelp() {
862
+ console.log(`Vantaloom CLI
863
+
864
+ Usage:
865
+ vantaloom install [--prefix <dir>] [--runtime-version <version>] [--npm-registry <url>] [--package <dir>] [--no-start]
866
+ vantaloom install --local [--prefix <dir>] [--source <repo>] [--build-web] [--no-start]
867
+ vantaloom update [--prefix <dir>] [--runtime-version <version>] [--npm-registry <url>] [--no-start]
868
+ vantaloom update --local [--prefix <dir>] [--source <repo>] [--build-web] [--no-start]
869
+ vantaloom package [--source <repo>] [--output <dir>] [--build-web] [--archive] [--npm-package]
870
+ vantaloom start [--prefix <dir>] [--component all|api|agent|web]
871
+ vantaloom stop [--prefix <dir>] [--component all|api|agent|web]
872
+ vantaloom restart [--prefix <dir>] [--component all|api|agent|web]
873
+ vantaloom status [--prefix <dir>]
874
+ vantaloom ports [--prefix <dir>]
875
+ vantaloom path [--prefix <dir>] [--source <repo>]
876
+ vantaloom platform
877
+ `)
878
+ }