@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.
- package/VERSION +1 -1
- package/bin/vantaloom-agent +0 -0
- package/bin/vantaloom-api +0 -0
- package/bin/vantaloom-browser +0 -0
- package/bin/vantaloomctl +0 -0
- package/cli/package.json +1 -1
- package/cli/src/cli.mjs +460 -2100
- package/cli/src/lib/auth.mjs +155 -0
- package/cli/src/lib/constants.mjs +34 -0
- package/cli/src/lib/install.mjs +627 -0
- package/cli/src/lib/lifecycle.mjs +187 -0
- package/cli/src/lib/mesh.mjs +236 -0
- package/cli/src/lib/package.mjs +206 -0
- package/cli/src/lib/platform.mjs +165 -0
- package/cli/src/lib/registry.mjs +237 -0
- package/manifest.json +2 -2
- package/package.json +1 -1
- package/web/404.html +1 -1
- package/web/__next.__PAGE__.txt +1 -1
- package/web/__next._full.txt +1 -1
- package/web/__next._head.txt +1 -1
- package/web/__next._index.txt +1 -1
- package/web/__next._tree.txt +1 -1
- package/web/_not-found/__next._full.txt +1 -1
- package/web/_not-found/__next._head.txt +1 -1
- package/web/_not-found/__next._index.txt +1 -1
- package/web/_not-found/__next._not-found/__PAGE__.txt +1 -1
- package/web/_not-found/__next._not-found.txt +1 -1
- package/web/_not-found/__next._tree.txt +1 -1
- package/web/_not-found.html +1 -1
- package/web/_not-found.txt +1 -1
- package/web/index.html +1 -1
- package/web/index.txt +1 -1
- /package/web/_next/static/{jpoLF2PZ-dWfDd5NxkZG0 → fdvtf4murB4YOO6QLnYED}/_buildManifest.js +0 -0
- /package/web/_next/static/{jpoLF2PZ-dWfDd5NxkZG0 → fdvtf4murB4YOO6QLnYED}/_clientMiddlewareManifest.json +0 -0
- /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
|
+
}
|