@vantaloom/runtime-darwin-arm64 0.12.49 → 0.12.50

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 (36) hide show
  1. package/VERSION +1 -1
  2. package/bin/vantaloom-agent +0 -0
  3. package/bin/vantaloom-api +0 -0
  4. package/bin/vantaloom-browser +0 -0
  5. package/bin/vantaloomctl +0 -0
  6. package/cli/package.json +1 -1
  7. package/cli/src/cli.mjs +460 -2100
  8. package/cli/src/lib/auth.mjs +155 -0
  9. package/cli/src/lib/constants.mjs +34 -0
  10. package/cli/src/lib/install.mjs +627 -0
  11. package/cli/src/lib/lifecycle.mjs +187 -0
  12. package/cli/src/lib/mesh.mjs +236 -0
  13. package/cli/src/lib/package.mjs +206 -0
  14. package/cli/src/lib/platform.mjs +165 -0
  15. package/cli/src/lib/registry.mjs +237 -0
  16. package/manifest.json +2 -2
  17. package/package.json +1 -1
  18. package/web/404.html +1 -1
  19. package/web/__next.__PAGE__.txt +1 -1
  20. package/web/__next._full.txt +1 -1
  21. package/web/__next._head.txt +1 -1
  22. package/web/__next._index.txt +1 -1
  23. package/web/__next._tree.txt +1 -1
  24. package/web/_not-found/__next._full.txt +1 -1
  25. package/web/_not-found/__next._head.txt +1 -1
  26. package/web/_not-found/__next._index.txt +1 -1
  27. package/web/_not-found/__next._not-found/__PAGE__.txt +1 -1
  28. package/web/_not-found/__next._not-found.txt +1 -1
  29. package/web/_not-found/__next._tree.txt +1 -1
  30. package/web/_not-found.html +1 -1
  31. package/web/_not-found.txt +1 -1
  32. package/web/index.html +1 -1
  33. package/web/index.txt +1 -1
  34. /package/web/_next/static/{jpoLF2PZ-dWfDd5NxkZG0 → fdvtf4murB4YOO6QLnYED}/_buildManifest.js +0 -0
  35. /package/web/_next/static/{jpoLF2PZ-dWfDd5NxkZG0 → fdvtf4murB4YOO6QLnYED}/_clientMiddlewareManifest.json +0 -0
  36. /package/web/_next/static/{jpoLF2PZ-dWfDd5NxkZG0 → fdvtf4murB4YOO6QLnYED}/_ssgManifest.js +0 -0
@@ -0,0 +1,187 @@
1
+ import {
2
+ existsSync,
3
+ mkdirSync,
4
+ rmSync,
5
+ writeFileSync,
6
+ } from "node:fs"
7
+ import { spawnSync } from "node:child_process"
8
+ import os from "node:os"
9
+ import path from "node:path"
10
+ import {
11
+ AUTOSTART_LABEL,
12
+ WINDOWS_RUN_KEY,
13
+ WINDOWS_RUN_VALUE,
14
+ } from "./constants.mjs"
15
+ import {
16
+ binaryName,
17
+ safeDirectory,
18
+ defaultPrefix,
19
+ run,
20
+ removeKnownPath,
21
+ } from "./platform.mjs"
22
+ import {
23
+ removeMeshService,
24
+ killTrayProcess,
25
+ } from "./mesh.mjs"
26
+
27
+ // ── Runtime login-autostart (no elevation) ──
28
+ // Start the local runtime (api/agent/web/tray) at login/boot using only
29
+ // per-user mechanisms — no UAC/sudo. This is separate from the privileged mesh
30
+ // service: the mesh sidecar autostarts via the OS service manager; this brings
31
+ // up the unprivileged runtime that joins the mesh and serves the local API.
32
+
33
+ export function enableRuntimeAutostart(prefix) {
34
+ try {
35
+ if (process.platform === "win32") {
36
+ // A .vbs launches the runtime with a hidden window (no console flash); the
37
+ // HKCU Run key runs it at login without elevation.
38
+ const launcher = path.join(prefix, "vantaloom.cmd")
39
+ const vbsPath = path.join(prefix, "autostart.vbs")
40
+ const vbs = `' Vantaloom runtime autostart (hidden)\r\nCreateObject("WScript.Shell").Run """${launcher}"" start", 0, False\r\n`
41
+ writeFileSync(vbsPath, vbs)
42
+ const result = spawnSync(
43
+ "powershell",
44
+ [
45
+ "-NoProfile",
46
+ "-NonInteractive",
47
+ "-Command",
48
+ `Set-ItemProperty -Path '${WINDOWS_RUN_KEY}' -Name '${WINDOWS_RUN_VALUE}' -Value 'wscript.exe "${vbsPath}"'`,
49
+ ],
50
+ { stdio: "ignore", windowsHide: true }
51
+ )
52
+ if (result.error || result.status !== 0) {
53
+ console.warn(" autostart: could not register login entry (HKCU Run)")
54
+ } else {
55
+ console.log(" autostart: enabled (login Run key)")
56
+ }
57
+ return
58
+ }
59
+ if (process.platform === "darwin") {
60
+ const launcher = path.join(prefix, "vantaloom")
61
+ const dir = path.join(os.homedir(), "Library", "LaunchAgents")
62
+ mkdirSync(dir, { recursive: true })
63
+ const plistPath = path.join(dir, `${AUTOSTART_LABEL}.plist`)
64
+ // RunAtLoad only (no KeepAlive): `vantaloom start` spawns detached and exits.
65
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
66
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
67
+ <plist version="1.0"><dict>
68
+ <key>Label</key><string>${AUTOSTART_LABEL}</string>
69
+ <key>ProgramArguments</key><array><string>${launcher}</string><string>start</string></array>
70
+ <key>RunAtLoad</key><true/>
71
+ </dict></plist>
72
+ `
73
+ writeFileSync(plistPath, plist)
74
+ spawnSync("launchctl", ["unload", plistPath], { stdio: "ignore", windowsHide: true })
75
+ spawnSync("launchctl", ["load", "-w", plistPath], { stdio: "ignore", windowsHide: true })
76
+ console.log(" autostart: enabled (LaunchAgent)")
77
+ return
78
+ }
79
+ // Linux: user systemd unit + linger (no root).
80
+ const launcher = path.join(prefix, "vantaloom")
81
+ const dir = path.join(os.homedir(), ".config", "systemd", "user")
82
+ mkdirSync(dir, { recursive: true })
83
+ const unit = `[Unit]
84
+ Description=Vantaloom local runtime
85
+ After=network-online.target
86
+ Wants=network-online.target
87
+
88
+ [Service]
89
+ Type=oneshot
90
+ RemainAfterExit=yes
91
+ ExecStart=${launcher} start
92
+ ExecStop=${launcher} stop
93
+
94
+ [Install]
95
+ WantedBy=default.target
96
+ `
97
+ writeFileSync(path.join(dir, "vantaloom-runtime.service"), unit)
98
+ spawnSync("loginctl", ["enable-linger"], { stdio: "ignore", windowsHide: true })
99
+ spawnSync("systemctl", ["--user", "daemon-reload"], { stdio: "ignore", windowsHide: true })
100
+ spawnSync("systemctl", ["--user", "enable", "vantaloom-runtime.service"], { stdio: "ignore", windowsHide: true })
101
+ console.log(" autostart: enabled (systemd user unit)")
102
+ } catch (error) {
103
+ console.warn(` autostart: could not enable (${error.message})`)
104
+ }
105
+ }
106
+
107
+ export function disableRuntimeAutostart() {
108
+ try {
109
+ if (process.platform === "win32") {
110
+ // Remove the Run-key entry; the autostart.vbs lives in the install dir and
111
+ // is deleted with it.
112
+ spawnSync(
113
+ "powershell",
114
+ ["-NoProfile", "-NonInteractive", "-Command", `Remove-ItemProperty -Path '${WINDOWS_RUN_KEY}' -Name '${WINDOWS_RUN_VALUE}' -ErrorAction SilentlyContinue`],
115
+ { stdio: "ignore", windowsHide: true }
116
+ )
117
+ return
118
+ }
119
+ if (process.platform === "darwin") {
120
+ const plistPath = path.join(os.homedir(), "Library", "LaunchAgents", `${AUTOSTART_LABEL}.plist`)
121
+ if (existsSync(plistPath)) {
122
+ spawnSync("launchctl", ["unload", "-w", plistPath], { stdio: "ignore", windowsHide: true })
123
+ rmSync(plistPath, { force: true })
124
+ }
125
+ return
126
+ }
127
+ const unit = path.join(os.homedir(), ".config", "systemd", "user", "vantaloom-runtime.service")
128
+ if (existsSync(unit)) {
129
+ spawnSync("systemctl", ["--user", "disable", "vantaloom-runtime.service"], { stdio: "ignore", windowsHide: true })
130
+ rmSync(unit, { force: true })
131
+ spawnSync("systemctl", ["--user", "daemon-reload"], { stdio: "ignore", windowsHide: true })
132
+ }
133
+ } catch (error) {
134
+ console.warn(` autostart: could not disable (${error.message})`)
135
+ }
136
+ }
137
+
138
+ // uninstallRuntime tears down a local install: stop the runtime, remove the
139
+ // privileged mesh service (elevated), then delete the install directory.
140
+ export async function uninstallRuntime(options) {
141
+ const prefix = safeDirectory(options.prefix ?? defaultPrefix())
142
+ if (!existsSync(prefix)) {
143
+ console.log(`Vantaloom is not installed at ${prefix}`)
144
+ return
145
+ }
146
+ console.log(`Uninstalling Vantaloom from ${prefix} ...`)
147
+
148
+ // 1. Stop the runtime (api/agent/web/tray). Best-effort.
149
+ const ctlBin = path.join(prefix, "bin", binaryName("vantaloomctl"))
150
+ if (existsSync(ctlBin)) {
151
+ spawnSync(ctlBin, ["stop", "--prefix", prefix], { stdio: "inherit", windowsHide: true })
152
+ }
153
+ killTrayProcess(prefix)
154
+
155
+ // 2. Remove the privileged mesh service (releases TUN adapter + file locks).
156
+ removeMeshService(prefix, options)
157
+
158
+ // 2b. Remove the login-autostart entry (lives outside the install dir).
159
+ disableRuntimeAutostart()
160
+
161
+ // 3. Delete the install directory.
162
+ try {
163
+ const parent = path.dirname(prefix)
164
+ removeKnownPath(prefix, parent)
165
+ console.log(`removed ${prefix}`)
166
+ } catch (error) {
167
+ console.warn(` could not remove ${prefix}: ${error.message}`)
168
+ console.warn(" some files may still be locked; re-run after closing Vantaloom processes.")
169
+ }
170
+
171
+ console.log("Vantaloom uninstalled.")
172
+ console.log("note: the install dir was left out of PATH edits; remove the bin/ entry from your shell profile if you added it.")
173
+ }
174
+
175
+ export function runCtl(command, options) {
176
+ const prefix = safeDirectory(options.prefix ?? defaultPrefix())
177
+ const ctl = path.join(prefix, "bin", binaryName("vantaloomctl"))
178
+ if (!existsSync(ctl)) {
179
+ throw new Error(`missing installed runtime at ${prefix}; run "vantaloom install" first`)
180
+ }
181
+
182
+ const args = [command, "--prefix", prefix]
183
+ if (options.component) {
184
+ args.push("--component", options.component)
185
+ }
186
+ run(ctl, args)
187
+ }
@@ -0,0 +1,236 @@
1
+ import {
2
+ existsSync,
3
+ readFileSync,
4
+ rmSync,
5
+ } from "node:fs"
6
+ import { spawnSync } from "node:child_process"
7
+ import path from "node:path"
8
+ import { WINDOWS_MESH_BINARIES } from "./constants.mjs"
9
+ import { binaryName, fileSha, psQuote } from "./platform.mjs"
10
+
11
+ // The privileged-mesh files that must NOT be needlessly overwritten on update:
12
+ // the service binary (vantaloom-mesh) and the EasyTier engine (easytier-core,
13
+ // the Linux setcap target), plus the Windows support DLLs the running service
14
+ // holds open. Leaving an unchanged copy in place keeps the service running and
15
+ // preserves its Linux file capability — so the update needs no elevation.
16
+ export function meshBinarySet(platform) {
17
+ return platform === "win32"
18
+ ? WINDOWS_MESH_BINARIES
19
+ : ["vantaloom-mesh", "easytier-core"]
20
+ }
21
+
22
+ // meshServiceRunning queries the OS service state via the (unprivileged) mesh
23
+ // `status` command. binDir is where to find the vantaloom-mesh binary to run.
24
+ // NOTE: mirrored in Go at apps/desktop/internal/runtime/
25
+ export function meshServiceRunning(binDir, platform) {
26
+ const exe = path.join(binDir, binaryName("vantaloom-mesh"))
27
+ if (!existsSync(exe)) return false
28
+ const r = spawnSync(exe, ["status"], { encoding: "utf8", windowsHide: true })
29
+ if (r.error || typeof r.stdout !== "string") return false
30
+ const out = r.stdout.toLowerCase()
31
+ if (platform === "win32") return out.includes("running")
32
+ return r.status === 0 && !out.includes("not loaded") && !out.includes("not installed")
33
+ }
34
+
35
+ // meshServiceNeedsApply reports whether the privileged service has to be
36
+ // (re)applied: true if the installed mesh binaries are missing/outdated, or if
37
+ // the service isn't currently up.
38
+ export function meshServiceNeedsApply(pkgBin, installBin, platform) {
39
+ // On Windows compare every lockable mesh file (incl. Packet.dll/WinDivert/wintun)
40
+ // so a missing or changed support DLL also triggers a (re)apply.
41
+ const names = platform === "win32"
42
+ ? WINDOWS_MESH_BINARIES
43
+ : [binaryName("vantaloom-mesh"), binaryName("easytier-core")]
44
+ for (const name of names) {
45
+ const installed = path.join(installBin, name)
46
+ if (!existsSync(installed)) return true
47
+ const staged = path.join(pkgBin, name)
48
+ if (existsSync(staged) && fileSha(staged) !== fileSha(installed)) return true
49
+ }
50
+ return !meshServiceRunning(pkgBin, platform)
51
+ }
52
+
53
+ // meshServiceRunningOrInstalled reports whether the Windows service exists at
54
+ // all (running or stopped), so uninstall only prompts for elevation when there
55
+ // is actually something to remove.
56
+ export function meshServiceRunningOrInstalled(binDir) {
57
+ const exe = path.join(binDir, "vantaloom-mesh.exe")
58
+ if (!existsSync(exe)) return false
59
+ const r = spawnSync(exe, ["status"], { encoding: "utf8", windowsHide: true })
60
+ if (r.error || typeof r.stdout !== "string") return false
61
+ return !r.stdout.toLowerCase().includes("not installed")
62
+ }
63
+
64
+ // ensureMeshService registers (or lock-safely updates) the privileged EasyTier
65
+ // sidecar service on Windows/macOS — the one elevation of the install on those
66
+ // platforms. Linux uses setcap instead (see ensureLinuxMeshCapabilities).
67
+ //
68
+ // To keep the source/dev rebuild loop frictionless it only elevates when the
69
+ // work is actually needed (service missing, or the mesh binaries changed) and,
70
+ // for source installs, defers to a printed one-liner unless --with-mesh is set.
71
+ // NOTE: mirrored in Go at apps/desktop/internal/runtime/
72
+ export function ensureMeshService(packageRoot, prefix, opts) {
73
+ const platform = process.platform
74
+ if (platform !== "win32" && platform !== "darwin") return // Linux: setcap path
75
+ if (opts.skipMesh) return
76
+
77
+ const pkgBin = path.join(packageRoot, "bin")
78
+ const meshExe = path.join(pkgBin, binaryName("vantaloom-mesh"))
79
+ if (!existsSync(meshExe)) return // sidecar not bundled
80
+
81
+ if (!meshServiceNeedsApply(pkgBin, path.join(prefix, "bin"), platform)) {
82
+ return // installed binaries current and service already managing them
83
+ }
84
+
85
+ if (opts.sourceInstall && !opts.withMesh) {
86
+ printMeshManualHint(prefix, platform)
87
+ return
88
+ }
89
+
90
+ if (platform === "win32") {
91
+ elevateWindowsMeshApply(pkgBin, prefix)
92
+ } else {
93
+ elevateDarwinMeshInstall(prefix)
94
+ }
95
+ }
96
+
97
+ // elevateWindowsMeshApply runs the lock-safe `apply` elevated (one UAC prompt).
98
+ // It launches from the package copy so it can overwrite the installed binary.
99
+ export function elevateWindowsMeshApply(pkgBin, prefix) {
100
+ const exe = path.join(pkgBin, "vantaloom-mesh.exe")
101
+ console.log(" mesh: registering privileged P2P service (UAC prompt) ...")
102
+ const argList = ["apply", "--pkg-bin", pkgBin, "--install-dir", prefix].map(psQuote).join(",")
103
+ const ps = `$ErrorActionPreference='Stop'; $p = Start-Process -FilePath ${psQuote(exe)} -ArgumentList ${argList} -Verb RunAs -Wait -PassThru; exit $p.ExitCode`
104
+ const r = spawnSync("powershell", ["-NoProfile", "-NonInteractive", "-Command", ps], { stdio: "inherit", windowsHide: true })
105
+ if (r.error || r.status !== 0) {
106
+ const installed = path.join(prefix, "bin", "vantaloom-mesh.exe")
107
+ console.warn(" mesh: service registration was cancelled or failed; P2P falls back to the Hub relay.")
108
+ console.warn(` enable it later (as Administrator): "${installed}" install --install-dir "${prefix}"`)
109
+ } else {
110
+ console.log(" mesh: privileged P2P service active")
111
+ }
112
+ }
113
+
114
+ // elevateDarwinMeshInstall registers the LaunchDaemon via sudo (one prompt). The
115
+ // runtime already copied the fresh binary into place; install reloads the daemon.
116
+ export function elevateDarwinMeshInstall(prefix) {
117
+ const exe = path.join(prefix, "bin", "vantaloom-mesh")
118
+ console.log(" mesh: registering privileged P2P service (sudo) ...")
119
+ const r = spawnSync("sudo", [exe, "install", "--install-dir", prefix], { stdio: "inherit", windowsHide: true })
120
+ if (r.error || r.status !== 0) {
121
+ console.warn(" mesh: LaunchDaemon registration failed; P2P falls back to the Hub relay.")
122
+ console.warn(` enable it later: sudo "${exe}" install --install-dir "${prefix}"`)
123
+ } else {
124
+ console.log(" mesh: privileged P2P service active")
125
+ }
126
+ }
127
+
128
+ export function printMeshManualHint(prefix, platform) {
129
+ console.log(" mesh: P2P service not auto-registered for source installs.")
130
+ if (platform === "win32") {
131
+ const exe = path.join(prefix, "bin", "vantaloom-mesh.exe")
132
+ console.log(` enable it once (as Administrator): "${exe}" install --install-dir "${prefix}"`)
133
+ console.log(" or re-run install/update with --with-mesh")
134
+ } else {
135
+ const exe = path.join(prefix, "bin", "vantaloom-mesh")
136
+ console.log(` enable it once: sudo "${exe}" install --install-dir "${prefix}"`)
137
+ console.log(" or re-run install/update with --with-mesh")
138
+ }
139
+ }
140
+
141
+ // ensureLinuxMeshCapabilities grants easytier-core the network capabilities it
142
+ // needs to create a TUN device, so the unprivileged Vantaloom runtime can bring
143
+ // up the EasyTier mesh without running as root. Re-run on every update because
144
+ // replacing the binary clears file capabilities. Non-fatal: if it can't elevate
145
+ // or setcap is missing, mesh just falls back to the Hub relay.
146
+ export function ensureLinuxMeshCapabilities(binDir) {
147
+ if (process.platform !== "linux") {
148
+ return
149
+ }
150
+ const core = path.join(binDir, "easytier-core")
151
+ if (!existsSync(core)) {
152
+ console.warn(" mesh: easytier-core not bundled; skipping TUN capability setup")
153
+ return
154
+ }
155
+ const caps = "cap_net_admin,cap_net_bind_service+ep"
156
+ // If the capability is already present (we kept an unchanged easytier-core in
157
+ // place above), there's nothing to do — and crucially, no sudo prompt. setcap
158
+ // only runs when the binary was actually replaced or never had the cap.
159
+ const have = spawnSync("getcap", [core], { encoding: "utf8", windowsHide: true })
160
+ if (
161
+ !have.error &&
162
+ typeof have.stdout === "string" &&
163
+ have.stdout.includes("cap_net_admin") &&
164
+ have.stdout.includes("cap_net_bind_service")
165
+ ) {
166
+ return
167
+ }
168
+ const isRoot = typeof process.getuid === "function" && process.getuid() === 0
169
+ console.log(` mesh: granting TUN capability to easytier-core${isRoot ? "" : " (sudo)"} ...`)
170
+ const result = isRoot
171
+ ? spawnSync("setcap", [caps, core], { stdio: "inherit", windowsHide: true })
172
+ : spawnSync("sudo", ["setcap", caps, core], { stdio: "inherit", windowsHide: true })
173
+ if (result.error || result.status !== 0) {
174
+ console.warn(
175
+ " mesh: could not set capabilities (P2P will fall back to the Hub relay).\n" +
176
+ ` enable it manually with: sudo setcap ${caps} ${core}\n` +
177
+ " (needs the 'setcap' tool, e.g. apt install libcap2-bin)"
178
+ )
179
+ }
180
+ }
181
+
182
+ export function removeMeshService(prefix, options) {
183
+ const platform = process.platform
184
+ if (platform !== "win32" && platform !== "darwin") return // Linux: nothing registered
185
+ const exe = path.join(prefix, "bin", binaryName("vantaloom-mesh"))
186
+ if (!existsSync(exe)) return
187
+ if (options.skipMesh) return
188
+
189
+ if (platform === "win32") {
190
+ if (!meshServiceRunningOrInstalled(path.join(prefix, "bin"))) return
191
+ console.log(" mesh: removing privileged P2P service (UAC prompt) ...")
192
+ const ps = `$ErrorActionPreference='Stop'; $p = Start-Process -FilePath ${psQuote(exe)} -ArgumentList 'uninstall' -Verb RunAs -Wait -PassThru; exit $p.ExitCode`
193
+ const r = spawnSync("powershell", ["-NoProfile", "-NonInteractive", "-Command", ps], { stdio: "inherit", windowsHide: true })
194
+ if (r.error || r.status !== 0) {
195
+ console.warn(` mesh: could not remove service; run manually (as Administrator): "${exe}" uninstall`)
196
+ }
197
+ } else {
198
+ console.log(" mesh: removing privileged P2P service (sudo) ...")
199
+ const r = spawnSync("sudo", [exe, "uninstall"], { stdio: "inherit", windowsHide: true })
200
+ if (r.error || r.status !== 0) {
201
+ console.warn(` mesh: could not remove service; run manually: sudo "${exe}" uninstall`)
202
+ }
203
+ }
204
+ }
205
+
206
+ export function killTrayProcess(prefix) {
207
+ if (process.platform === "win32") {
208
+ // Try PID file first (new tray versions write runtime/tray.pid).
209
+ const pidFile = path.join(prefix, "runtime", "tray.pid")
210
+ if (existsSync(pidFile)) {
211
+ const pid = readFileSync(pidFile, "utf8").trim()
212
+ if (pid) {
213
+ spawnSync("taskkill", ["/PID", pid, "/F"], { stdio: "ignore", windowsHide: true })
214
+ try { rmSync(pidFile, { force: true }) } catch {}
215
+ }
216
+ }
217
+ // Fallback: kill by image name if the binary is inside our prefix.
218
+ const result = spawnSync("tasklist", ["/FI", "IMAGENAME eq vantaloom-tray.exe", "/FO", "CSV", "/NH"], {
219
+ encoding: "utf8",
220
+ windowsHide: true,
221
+ })
222
+ if (result.stdout) {
223
+ for (const line of result.stdout.split("\n")) {
224
+ const match = line.match(/"vantaloom-tray\.exe","(\d+)"/)
225
+ if (match) {
226
+ spawnSync("taskkill", ["/PID", match[1], "/F"], { stdio: "ignore", windowsHide: true })
227
+ }
228
+ }
229
+ }
230
+ // Brief pause to let file handles release.
231
+ spawnSync("timeout", ["/t", "1", "/nobreak"], { stdio: "ignore", windowsHide: true })
232
+ } else {
233
+ // Unix: pkill by name (best-effort).
234
+ spawnSync("pkill", ["-f", "vantaloom-tray"], { stdio: "ignore", windowsHide: true })
235
+ }
236
+ }
@@ -0,0 +1,206 @@
1
+ import {
2
+ cpSync,
3
+ existsSync,
4
+ mkdirSync,
5
+ mkdtempSync,
6
+ rmSync,
7
+ writeFileSync,
8
+ } from "node:fs"
9
+ import { cp } from "node:fs/promises"
10
+ import os from "node:os"
11
+ import path from "node:path"
12
+ import { cliRoot } from "./constants.mjs"
13
+ import {
14
+ platformId,
15
+ parsePlatformId,
16
+ removeKnownPath,
17
+ run,
18
+ runNpm,
19
+ runPnpm,
20
+ } from "./platform.mjs"
21
+ import {
22
+ buildGo,
23
+ copyStaticWeb,
24
+ copyCliDirectory,
25
+ writeBuildManifest,
26
+ writeRuntimePackageMetadata,
27
+ runtimeConfigFromSource,
28
+ gitVersion,
29
+ } from "./install.mjs"
30
+
31
+ export async function buildRuntimePackage(sourceRoot, packageRoot, options) {
32
+ const version = gitVersion(sourceRoot)
33
+ const platform = options.target ?? platformId()
34
+ const buildBin = path.join(packageRoot, "bin")
35
+ const buildWeb = path.join(packageRoot, "web")
36
+
37
+ removeKnownPath(packageRoot, path.dirname(packageRoot))
38
+ mkdirSync(buildBin, { recursive: true })
39
+
40
+ buildGo(sourceRoot, buildBin, "vantaloom-api", platform)
41
+ buildGo(sourceRoot, buildBin, "vantaloom-agent", platform)
42
+ buildGo(sourceRoot, buildBin, "vantaloomctl", platform)
43
+ // Official "Vantaloom 浏览器" extension sidecar. Pure-Go (chromedp/CDP, no
44
+ // CGO), cross-compiles for every target. Ships dormant: it's only started by
45
+ // the api when the user installs+enables the browser extension (the heavy
46
+ // Chromium binary is downloaded on demand, NOT bundled here).
47
+ buildGo(sourceRoot, buildBin, "vantaloom-browser", platform)
48
+ // Privileged EasyTier sidecar (runs the mesh node as a service on Windows/macOS).
49
+ // Pure-Go (no CGO), so it cross-compiles cleanly for every target.
50
+ buildGo(sourceRoot, buildBin, "vantaloom-mesh", platform)
51
+ // The Windows system-tray app (vantaloom-tray) was removed — it crash-looped
52
+ // on some Windows 11 builds and got respawned until OOM. No longer built.
53
+
54
+ // Bundle the EasyTier mesh binaries (P2P virtual network) for the target platform.
55
+ await copyEasyTier(sourceRoot, buildBin, platform)
56
+
57
+ if (options.buildWeb) {
58
+ runPnpm(["--filter", "vantaloom-app", "build"], { cwd: sourceRoot })
59
+ }
60
+
61
+ await copyStaticWeb(sourceRoot, buildWeb)
62
+ await copyCliDirectory(path.join(packageRoot, "cli"), sourceRoot, runtimeConfigFromSource(sourceRoot))
63
+ // The Claude Code ACP adapter is NO LONGER bundled into the runtime package
64
+ // (it bloated every platform tarball by ~50-100MB). Instead the user installs
65
+ // it on demand from 设置 → CC 引擎 → "下载/安装适配器", which runs the
66
+ // `install-adapter` CLI command into <installDir>/adapters. Because applyPackage
67
+ // leaves a missing adapters/ dir untouched on update, the user-installed adapter
68
+ // survives runtime updates.
69
+ writeBuildManifest(packageRoot, version, platform)
70
+
71
+ return { platform, version }
72
+ }
73
+
74
+ // bundleAdapter installs the Claude Agent SDK into
75
+ // <packageRoot>/adapters/claude-sdk-bridge/node_modules and copies the bridge source
76
+ // (the thin node sidecar that runs the SDK's query() and pipes its native stream-json
77
+ // to the Go runtime). It also writes the reclaude shim the Go engine points the SDK's
78
+ // pathToClaudeCodeExecutable at. The SDK version is pinned in the bridge's own
79
+ // package.json (single source of truth); native optional deps (e.g. sharp) are
80
+ // selected per TARGET platform.
81
+ export async function bundleAdapter(packageRoot, options = {}) {
82
+ const adaptersRoot = path.join(packageRoot, "adapters")
83
+ const bridgeDest = path.join(adaptersRoot, "claude-sdk-bridge")
84
+ const bridgeSrc = path.join(cliRoot, "adapters", "claude-sdk-bridge")
85
+ const shimPath = path.join(adaptersRoot, "reclaude-shim.mjs")
86
+ const shimSource = `// Vantaloom reclaude shim: the Claude Agent SDK runs \`node <this>\` as the claude
87
+ // executable, so we re-exec the real reclaude binary (path in VANTALOOM_RECLAUDE_EXE)
88
+ // with the same args + inherited stdio. reclaude's banner goes to stderr, keeping the
89
+ // SDK's stdout protocol clean.
90
+ import { spawn } from "node:child_process";
91
+ const exe = process.env.VANTALOOM_RECLAUDE_EXE;
92
+ if (!exe) {
93
+ process.stderr.write("[reclaude-shim] VANTALOOM_RECLAUDE_EXE not set\\n");
94
+ process.exit(127);
95
+ }
96
+ const child = spawn(exe, process.argv.slice(2), { stdio: "inherit", windowsHide: true });
97
+ child.on("exit", (code, signal) => process.exit(code ?? (signal ? 1 : 0)));
98
+ child.on("error", (e) => {
99
+ process.stderr.write(\`[reclaude-shim] failed to launch reclaude: \${e.message}\\n\`);
100
+ process.exit(127);
101
+ });
102
+ `
103
+
104
+ if (!existsSync(path.join(bridgeSrc, "index.mjs"))) {
105
+ const msg = `claude-sdk-bridge source missing at ${bridgeSrc}`
106
+ if (options.skipAdapter) {
107
+ console.warn(` warning: skipping SDK bridge bundle (${msg})`)
108
+ return
109
+ }
110
+ throw new Error(msg)
111
+ }
112
+
113
+ // Resolve the SDK's platform-gated native optional deps (e.g. sharp's
114
+ // @img/sharp-<os>-<cpu>) for the TARGET platform, not the build host — otherwise a
115
+ // Windows-built bundle would ship the wrong native binary to the mac/linux packages.
116
+ const { os: targetOs, cpu: targetCpu } = parsePlatformId(options.platform ?? platformId())
117
+ console.log(` bundling Claude Agent SDK bridge (os=${targetOs} cpu=${targetCpu}) ...`)
118
+ const tmp = mkdtempSync(path.join(os.tmpdir(), "vantaloom-sdk-bridge-"))
119
+ try {
120
+ // Install the bridge's OWN dependencies (the SDK, version pinned in its
121
+ // package.json) into a temp prefix from the PUBLIC registry explicitly: the
122
+ // machine's global .npmrc points at npmmirror, which may not mirror this package.
123
+ cpSync(path.join(bridgeSrc, "package.json"), path.join(tmp, "package.json"))
124
+ const npmArgs = [
125
+ "install",
126
+ "--prefix",
127
+ tmp,
128
+ "--registry",
129
+ "https://registry.npmjs.org/",
130
+ "--no-audit",
131
+ "--no-fund",
132
+ "--omit=dev",
133
+ "--os",
134
+ targetOs,
135
+ "--cpu",
136
+ targetCpu,
137
+ ]
138
+ if (targetOs === "linux") {
139
+ npmArgs.push("--libc", "glibc")
140
+ }
141
+ runNpm(npmArgs)
142
+
143
+ const tmpModules = path.join(tmp, "node_modules")
144
+ if (!existsSync(tmpModules)) {
145
+ throw new Error(`SDK install produced no node_modules at ${tmpModules}`)
146
+ }
147
+
148
+ removeKnownPath(adaptersRoot, path.dirname(adaptersRoot))
149
+ mkdirSync(bridgeDest, { recursive: true })
150
+ // Bridge source: index.mjs (+ package.json, for reference/version).
151
+ cpSync(path.join(bridgeSrc, "index.mjs"), path.join(bridgeDest, "index.mjs"))
152
+ cpSync(path.join(bridgeSrc, "package.json"), path.join(bridgeDest, "package.json"))
153
+ // The SDK + its dependency tree.
154
+ cpSync(tmpModules, path.join(bridgeDest, "node_modules"), { recursive: true })
155
+
156
+ writeFileSync(shimPath, shimSource)
157
+ console.log(` bundled SDK bridge -> adapters/claude-sdk-bridge + reclaude-shim.mjs`)
158
+ } catch (error) {
159
+ if (options.skipAdapter) {
160
+ console.warn(` warning: skipping SDK bridge bundle (${error.message})`)
161
+ return
162
+ }
163
+ throw new Error(`failed to bundle SDK bridge: ${error.message}`)
164
+ } finally {
165
+ rmSync(tmp, { recursive: true, force: true })
166
+ }
167
+ }
168
+
169
+ // copyEasyTier bundles the vendored EasyTier binaries (easytier-core/easytier-cli,
170
+ // plus wintun.dll on Windows) for the target platform into the package bin/ dir.
171
+ export async function copyEasyTier(sourceRoot, buildBin, platform) {
172
+ const vendorMap = {
173
+ "win32-x64": "windows-x86_64",
174
+ "darwin-arm64": "macos-aarch64",
175
+ "linux-x64": "linux-x86_64",
176
+ }
177
+ const vendorName = vendorMap[platform]
178
+ if (!vendorName) {
179
+ console.warn(` warning: no EasyTier mapping for ${platform}; mesh disabled for this platform`)
180
+ return
181
+ }
182
+ const srcDir = path.join(sourceRoot, "vendor", "easytier", vendorName, `easytier-${vendorName}`)
183
+ if (!existsSync(srcDir)) {
184
+ console.warn(` warning: EasyTier binaries not found at ${srcDir}; run vendor download first`)
185
+ return
186
+ }
187
+ const isWin = platform.startsWith("win32")
188
+ // Packet.dll + WinDivert64.sys are REQUIRED: easytier-core.exe statically
189
+ // imports Packet.dll, so without it the process dies at launch with
190
+ // STATUS_DLL_NOT_FOUND (0xC0000135) before writing any log. wintun.dll is the
191
+ // TUN backend. easytier-cli.exe does not need Packet.dll (it still launches).
192
+ const files = isWin
193
+ ? ["easytier-core.exe", "easytier-cli.exe", "wintun.dll", "Packet.dll", "WinDivert64.sys"]
194
+ : ["easytier-core", "easytier-cli"]
195
+ let copied = 0
196
+ for (const f of files) {
197
+ const src = path.join(srcDir, f)
198
+ if (!existsSync(src)) {
199
+ console.warn(` warning: EasyTier file missing: ${f}`)
200
+ continue
201
+ }
202
+ await cp(src, path.join(buildBin, f), { force: true })
203
+ copied++
204
+ }
205
+ console.log(` bundled EasyTier ${vendorName} (${copied} files)`)
206
+ }