UNPKG

relay-companion

Version:

Install Relay for Claude Code, Cowork, and Codex, then sign in from the Relay pill.

1,192 lines (1,132 loc) • 49.6 kB
#!/usr/bin/env node "use strict"; // Deliberately stdlib-only. This is every byte npm executes before the person // has reviewed Relay: no package dependencies and no npm lifecycle scripts. const crypto = require("node:crypto"); const fs = require("node:fs"); const https = require("node:https"); const os = require("node:os"); const path = require("node:path"); const { spawnSync } = require("node:child_process"); const { pipeline } = require("node:stream/promises"); const { activateLinuxRuntimeServices, activateMacRuntimeServices, exactRuntimeHealth } = require("./runtime-health.cjs"); const { isTemporaryNodePath, relayOwnedNodePath } = require("./owned-node-runtime.cjs"); const { repairRuntimeExecutablePermissions, verifyRuntimeExecutables, } = require("./runtime-executables.cjs"); const RELEASE_ORIGIN = "https://api.sendrelays.com"; const RELEASE_BASE_PATH = "/v1/companion-releases"; const PACKAGE_NAME = "relay-companion"; const WINDOWS_RELAY_TASKS = ["Relay Companion Pill", "Relay Companion Daemon"]; const WINDOWS_STOP_RELAY_SERVICES_PS = [ "$p=Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object {", " $_.CommandLine -and ($_.CommandLine -match '[\\\\/]relay-companion[\\\\/]') -and (($_.CommandLine -match '[\\\\/]relay\\.js.*\\bdaemon\\b') -or ($_.CommandLine -match '[\\\\/]overlay[\\\\/]main\\.cjs') -or ($_.CommandLine -match '[\\\\/]mcp-broker-entry\\.js'))", "}; foreach($x in $p){ try { Invoke-CimMethod -InputObject $x -MethodName Terminate -ErrorAction Stop | Out-Null } catch {} }", ].join(" "); const packageJson = require("../package.json"); const trust = require("./trust.json"); const { RELEASE_ALGORITHM, verifyReleaseEnvelope } = require("./release-signature.cjs"); function fail(message) { throw new Error(message); } function releasePlatform(platform = process.platform, arch = process.arch) { const key = `${platform}-${arch}`; if ([ "darwin-arm64", "darwin-x64", "win32-x64", "win32-arm64", "linux-arm64", "linux-x64", ].includes(key)) return key; fail(`Relay supports 64-bit macOS, Windows, and Linux; this machine is ${key}.`); } function exactVersion(value) { const version = String(value || "").trim(); if (!/^\d+\.\d+\.\d+$/.test(version)) fail(`The bootstrap does not carry an exact release version (${version || "missing"}).`); return version; } function assertCompatibleNode(version = process.versions.node) { const match = /^(\d+)\.(\d+)\.(\d+)/.exec(String(version || "")); const parts = match ? match.slice(1).map(Number) : []; const supported = parts.length === 3 && (parts[0] > 22 || (parts[0] === 22 && parts[1] >= 12)); if (!supported) { fail(`Relay setup requires Node.js 22.12 or newer and will not switch runtimes automatically (found ${version || "unknown"}).`); } return true; } const VERSION_MANAGED_NODE_RE = /[\\/](Cellar[\\/]node|\.nvm[\\/]versions|\.n[\\/]versions|\.fnm|fnm[\\/]|\.volta|\.hermes[\\/]node)[\\/]/i; function stableNodePath(execPath = process.execPath, { platform = process.platform, env = process.env, existsSync = fs.existsSync, realpathSync = fs.realpathSync, spawnImpl = spawnSync, } = {}) { if (!execPath) return execPath; let realExec = execPath; try { realExec = realpathSync(execPath); } catch {} const volatile = VERSION_MANAGED_NODE_RE.test(execPath) || VERSION_MANAGED_NODE_RE.test(realExec) || isTemporaryNodePath(execPath, { platform, realpathSync }); if (!volatile) return execPath; const api = platform === "win32" ? path.win32 : path.posix; const candidates = platform === "win32" ? [api.join(env.ProgramFiles || "C:\\Program Files", "nodejs", "node.exe")] : ["/opt/homebrew/bin/node", "/usr/local/bin/node", "/usr/bin/node"]; for (const directory of String(env.PATH || "").split(platform === "win32" ? ";" : ":").filter(Boolean)) { candidates.push(api.join(directory, platform === "win32" ? "node.exe" : "node")); } for (const candidate of [...new Set(candidates)]) { try { if ( existsSync(candidate) && !isTemporaryNodePath(candidate, { platform, realpathSync }) && realpathSync(candidate) === realExec ) return candidate; } catch {} } for (const candidate of [...new Set(candidates)]) { try { if ( !existsSync(candidate) || VERSION_MANAGED_NODE_RE.test(candidate) || isTemporaryNodePath(candidate, { platform, realpathSync }) ) continue; const result = spawnImpl(candidate, ["-p", "process.versions.node"], { encoding: "utf8", timeout: 5000 }); if (!result?.error && result?.status === 0) { assertCompatibleNode(String(result.stdout || "").trim()); return candidate; } } catch {} } return execPath; } function durableNodePath(execPath = stableNodePath(), { platform = process.platform, runtimeRoot = path.join(os.homedir(), ".relay", "runtime"), fsImpl = fs, } = {}) { if (platform !== "linux" || !execPath) return execPath; let source = execPath; try { source = fsImpl.realpathSync(execPath); } catch {} if (!VERSION_MANAGED_NODE_RE.test(execPath) && !VERSION_MANAGED_NODE_RE.test(source)) return execPath; let bytes; try { bytes = fsImpl.readFileSync(source); } catch (error) { fail(`Relay could not preserve its Node runtime (${error?.message || error}).`); } const digest = crypto.createHash("sha256").update(bytes).digest("hex"); const directory = path.join(runtimeRoot, "node", digest); const destination = path.join(directory, "node"); try { const existing = fsImpl.readFileSync(destination); if (crypto.createHash("sha256").update(existing).digest("hex") === digest) return destination; } catch {} fsImpl.mkdirSync(directory, { recursive: true, mode: 0o700 }); const temporary = path.join(directory, `.node-${process.pid}-${crypto.randomBytes(6).toString("hex")}`); try { fsImpl.writeFileSync(temporary, bytes, { mode: 0o700, flag: "wx" }); fsImpl.chmodSync(temporary, 0o700); const copied = crypto.createHash("sha256").update(fsImpl.readFileSync(temporary)).digest("hex"); if (copied !== digest) fail("Relay's durable Node copy failed its integrity check."); fsImpl.rmSync(destination, { force: true }); fsImpl.renameSync(temporary, destination); } finally { try { fsImpl.rmSync(temporary, { force: true }); } catch {} } return destination; } function fetchBuffer(url, redirects = 0, maxBytes = 2 * 1024 * 1024) { return new Promise((resolve, reject) => { const parsed = new URL(url); if (parsed.protocol !== "https:") return reject(new Error(`Relay refused a non-HTTPS release URL: ${url}`)); const request = https.get(parsed, { headers: { "user-agent": `${PACKAGE_NAME}/${packageJson.version}` } }, (response) => { if ([301, 302, 303, 307, 308].includes(response.statusCode) && response.headers.location) { response.resume(); if (redirects >= 3) return reject(new Error("Too many release download redirects")); const next = new URL(response.headers.location, parsed); if (next.origin !== RELEASE_ORIGIN) return reject(new Error(`Relay refused a release redirect to ${next.origin}`)); return fetchBuffer(next, redirects + 1, maxBytes).then(resolve, reject); } if (response.statusCode !== 200) { response.resume(); return reject(new Error(`Release download returned HTTP ${response.statusCode}`)); } const chunks = []; let bytes = 0; response.on("data", (chunk) => { bytes += chunk.length; if (bytes > maxBytes) request.destroy(new Error("Release manifest exceeded the safety limit")); else chunks.push(chunk); }); response.on("end", () => resolve(Buffer.concat(chunks))); response.on("error", reject); }); request.setTimeout(60_000, () => request.destroy(new Error("Release download timed out"))); request.on("error", reject); }); } const MAX_ARTIFACT_BYTES = 750 * 1024 * 1024; const ARTIFACT_DOWNLOAD_ATTEMPTS = 6; class PermanentArtifactDownloadError extends Error {} function artifactResponseShape(response, offset, totalBytes) { const expectedBytes = totalBytes - offset; const contentLength = Number(response.headers["content-length"]); if (!Number.isSafeInteger(contentLength) || contentLength !== expectedBytes) { throw new PermanentArtifactDownloadError("Release artifact Content-Length does not match its signed manifest"); } if (offset === 0) { if (response.statusCode !== 200) { throw new PermanentArtifactDownloadError(`Release artifact returned HTTP ${response.statusCode}`); } return expectedBytes; } if (response.statusCode !== 206) { throw new PermanentArtifactDownloadError(`Release artifact resume returned HTTP ${response.statusCode}`); } const contentRange = String(response.headers["content-range"] || ""); const match = /^bytes (\d+)-(\d+)\/(\d+)$/.exec(contentRange); if ( !match || Number(match[1]) !== offset || Number(match[2]) !== totalBytes - 1 || Number(match[3]) !== totalBytes ) { throw new PermanentArtifactDownloadError("Release artifact resume returned an invalid Content-Range"); } return expectedBytes; } function downloadArtifactAttempt({ parsed, file, artifact, offset, get }) { return new Promise((resolve, reject) => { let complete = false; let responseStream = null; let responseDeadline = null; const finish = (error) => { if (complete) return; complete = true; if (responseDeadline) clearTimeout(responseDeadline); if (error) reject(error); else resolve(); }; const headers = { "user-agent": `${PACKAGE_NAME}/${packageJson.version}`, "accept-encoding": "identity", ...(offset > 0 ? { range: `bytes=${offset}-` } : {}), }; const request = get(parsed, { headers }, async (response) => { if (responseDeadline) clearTimeout(responseDeadline); responseStream = response; let expectedBytes; try { expectedBytes = artifactResponseShape(response, offset, artifact.bytes); } catch (error) { response.resume(); return finish(error); } let received = 0; response.on("data", (chunk) => { received += chunk.length; if (received > expectedBytes) { request.destroy(new PermanentArtifactDownloadError("Release artifact exceeded its signed size")); } }); try { if (fs.statSync(file).size !== offset) { throw new PermanentArtifactDownloadError("Relay artifact staging file changed during download"); } const output = fs.createWriteStream(file, { flags: "r+", start: offset, mode: 0o600 }); await pipeline(response, output); if (received !== expectedBytes) { throw new Error(`Relay artifact download ended early (${received} != ${expectedBytes}).`); } const fd = fs.openSync(file, "r"); try { fs.fsyncSync(fd); } finally { fs.closeSync(fd); } finish(); } catch (error) { finish(error); } }); // ClientRequest's socket timeout starts only after a socket is assigned. // Bound DNS, connection, TLS, and response-header setup separately so a // broken network cannot wedge setup before resumable transfer begins. responseDeadline = setTimeout( () => request.destroy(new Error("Release artifact connection timed out")), 30_000, ); request.setTimeout(60_000, () => request.destroy(new Error("Release artifact download stalled"))); request.on("error", (error) => { if (responseStream) responseStream.destroy(error); finish(error); }); }); } function sha512File(file) { return new Promise((resolve, reject) => { const hash = crypto.createHash("sha512"); const input = fs.createReadStream(file); input.on("data", (chunk) => hash.update(chunk)); input.on("error", reject); input.on("end", () => resolve(`sha512-${hash.digest("base64")}`)); }); } async function downloadVerifiedArtifact(url, file, artifact, options = {}) { const parsed = new URL(url); if (parsed.protocol !== "https:" || parsed.origin !== RELEASE_ORIGIN) { fail(`Relay refused an untrusted artifact origin: ${parsed.origin}`); } if (!Number.isSafeInteger(artifact.bytes) || artifact.bytes <= 0 || artifact.bytes > MAX_ARTIFACT_BYTES) { fail("Release artifact has an unsafe signed size"); } const get = options.get || https.get; const sleep = options.sleep || ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds))); const attempts = options.attempts || ARTIFACT_DOWNLOAD_ATTEMPTS; fs.closeSync(fs.openSync(file, "wx", 0o600)); let lastError; for (let attempt = 0; attempt < attempts; attempt += 1) { const offset = fs.statSync(file).size; if (offset > artifact.bytes) fail("Release artifact exceeded its signed size"); if (offset === artifact.bytes) break; try { await downloadArtifactAttempt({ parsed, file, artifact, offset, get }); } catch (error) { lastError = error; if (error instanceof PermanentArtifactDownloadError) throw error; if (attempt + 1 >= attempts) break; const backoff = Math.min(4_000, 250 * (2 ** attempt)); await sleep(backoff + Math.floor(Math.random() * 250)); } } const downloadedBytes = fs.statSync(file).size; if (downloadedBytes !== artifact.bytes) { throw new Error(`Relay artifact download failed after ${attempts} attempts (${downloadedBytes}/${artifact.bytes} bytes): ${lastError?.message || lastError || "incomplete response"}`); } const actual = await sha512File(file); if (actual.length !== artifact.sha512.length || !crypto.timingSafeEqual(Buffer.from(actual), Buffer.from(artifact.sha512))) { fs.rmSync(file, { force: true }); fail("Relay artifact checksum is invalid."); } } function parseSignedManifest(bytes, { version, platformKey, publicKeyPem, trustStore = trust }) { let envelope; try { envelope = JSON.parse(bytes.toString("utf8")); } catch { fail("Relay release manifest is not valid JSON."); } if (publicKeyPem) { trustStore = { schema: 2, activeKeyId: envelope?.keyId, keys: [{ keyId: envelope?.keyId, algorithm: RELEASE_ALGORITHM, publicKeyPem }], }; } else if (process.env.RELAY_ALLOW_DEV_RELEASE_KEY === "1" && process.env.RELAY_RELEASE_PUBLIC_KEY) { trustStore = { schema: 2, activeKeyId: envelope?.keyId, keys: [{ keyId: envelope?.keyId, algorithm: RELEASE_ALGORITHM, publicKeyPem: process.env.RELAY_RELEASE_PUBLIC_KEY }], }; } let payloadBytes; try { payloadBytes = verifyReleaseEnvelope(envelope, trustStore); } catch (error) { fail(error?.message || String(error)); } let payload; try { payload = JSON.parse(payloadBytes.toString("utf8")); } catch { fail("Relay signed release payload is invalid."); } if (payload?.product !== "Relay" || payload?.version !== version || !/^[0-9a-f]{40}$/.test(payload?.sourceSha || "")) { fail("Relay signed release identity does not match the requested exact version."); } const artifact = payload?.artifacts?.[platformKey]; if ( !artifact || !/^sha512-[A-Za-z0-9+/]+=*$/.test(artifact.sha512 || "") || !/^sha512-[A-Za-z0-9+/]+=*$/.test(artifact.dependencyLockSha512 || "") || !Number.isSafeInteger(artifact.bytes) ) { fail(`Relay's signed release does not contain a valid ${platformKey} artifact.`); } const url = new URL(artifact.url); if (url.origin !== RELEASE_ORIGIN || !url.pathname.startsWith(`${RELEASE_BASE_PATH}/v${version}/`)) { fail("Relay's signed artifact URL is not an exact-version branded release URL."); } return { payload, artifact }; } function tarInvocation({ archivePath, mode, destination, pathImpl = path }) { const cwd = pathImpl.dirname(archivePath); const archiveName = pathImpl.basename(archivePath); if (!archiveName || archiveName === "." || archiveName === pathImpl.sep) { fail("Relay runtime archive path is invalid."); } if (mode === "list-names") return { command: "tar", args: ["-tzf", archiveName], cwd }; if (mode === "list-details") return { command: "tar", args: ["-tvzf", archiveName], cwd }; if (mode === "extract") { const relativeDestination = pathImpl.relative(cwd, destination) || "."; if (pathImpl.isAbsolute(relativeDestination) || /^[A-Za-z]:/.test(relativeDestination)) { fail("Relay runtime archive and extraction directory must share a local volume."); } return { command: "tar", args: ["-xzf", archiveName, "-C", relativeDestination], cwd }; } fail("Relay runtime archive operation is invalid."); } function validateArchiveListing(archivePath) { const nameInvocation = tarInvocation({ archivePath, mode: "list-names" }); const detailInvocation = tarInvocation({ archivePath, mode: "list-details" }); const names = spawnSync(nameInvocation.command, nameInvocation.args, { cwd: nameInvocation.cwd, encoding: "utf8", windowsHide: true }); const details = spawnSync(detailInvocation.command, detailInvocation.args, { cwd: detailInvocation.cwd, encoding: "utf8", windowsHide: true }); if (names.error || names.status !== 0 || details.error || details.status !== 0) { fail(`Relay could not inspect its runtime archive: ${names.error?.message || details.error?.message || names.stderr || details.stderr}`); } validateArchiveEntries( String(names.stdout || "").split(/\r?\n/).filter(Boolean), String(details.stdout || "").split(/\r?\n/).filter(Boolean), ); } function validateArchiveEntries(names, details) { for (const name of names.map((value) => value.replaceAll("\\", "/"))) { if (name.startsWith("/") || /^[A-Za-z]:\//.test(name) || name.split("/").includes("..")) { fail(`Relay runtime archive contains an unsafe path: ${name}`); } } for (const line of details) { if (!/^[-d]/.test(line)) fail("Relay runtime archives may contain regular files and directories only."); } return true; } function rejectExtractedLinks(root) { for (const entry of fs.readdirSync(root, { withFileTypes: true })) { const absolute = path.join(root, entry.name); const stat = fs.lstatSync(absolute); if (stat.isSymbolicLink()) fail(`Relay runtime contains a link: ${absolute}`); if (stat.isDirectory()) rejectExtractedLinks(absolute); } } function withinRoot(root, candidate) { const resolvedRoot = path.resolve(root); const resolved = path.resolve(candidate); if (resolved === resolvedRoot || resolved.startsWith(`${resolvedRoot}${path.sep}`)) return true; // macOS exposes /tmp through /private/tmp. A reconstructed link's realpath // crosses that harmless system alias even though the lexical signed paths do // not. Compare real roots only as a second check for existing targets. try { const realRoot = fs.realpathSync(root); const realCandidate = fs.realpathSync(candidate); return realCandidate === realRoot || realCandidate.startsWith(`${realRoot}${path.sep}`); } catch { return false; } } function restoreRuntimeLinks(root) { const mapPath = path.join(root, "node_modules", PACKAGE_NAME, "runtime-links.json"); let document; try { document = JSON.parse(fs.readFileSync(mapPath, "utf8")); } catch { fail("Relay runtime is missing its signed internal-link map."); } if (document?.schema !== 1 || !Array.isArray(document.links) || document.links.length > 256) { fail("Relay runtime internal-link map is invalid."); } const records = new Map(); for (const item of document.links) { const linkPath = String(item?.path || ""); const target = String(item?.target || ""); const type = String(item?.type || ""); if ( !linkPath || linkPath.includes("\\") || linkPath.startsWith("/") || /^[A-Za-z]:/.test(linkPath) || linkPath.split("/").some((segment) => !segment || segment === "." || segment === "..") || !target || target.includes("\\") || target.startsWith("/") || /^[A-Za-z]:/.test(target) || !["file", "directory"].includes(type) ) { fail("Relay runtime internal-link map contains an unsafe entry."); } const absolute = path.resolve(root, ...linkPath.split("/")); const targetAbsolute = path.resolve(path.dirname(absolute), ...target.split("/")); if ( !withinRoot(root, absolute) || !withinRoot(root, targetAbsolute) || targetAbsolute === absolute || targetAbsolute.startsWith(`${absolute}${path.sep}`) || records.has(absolute) ) { fail("Relay runtime internal-link map escapes or duplicates its signed root."); } if (fs.existsSync(absolute)) fail("Relay runtime link destination was unexpectedly present in the archive."); records.set(absolute, { absolute, target, targetAbsolute, type }); } const ordered = []; const visiting = new Set(); const visited = new Set(); function visit(record) { if (visited.has(record.absolute)) return; if (visiting.has(record.absolute)) fail("Relay runtime internal-link map contains a cycle."); visiting.add(record.absolute); for (const dependency of records.values()) { if ( dependency.absolute !== record.absolute && (record.targetAbsolute === dependency.absolute || record.targetAbsolute.startsWith(`${dependency.absolute}${path.sep}`)) ) { visit(dependency); } } visiting.delete(record.absolute); visited.add(record.absolute); ordered.push(record); } for (const record of records.values()) visit(record); for (const record of ordered) { const targetStat = fs.statSync(record.targetAbsolute, { throwIfNoEntry: false }); if (!targetStat || (record.type === "directory" ? !targetStat.isDirectory() : !targetStat.isFile())) { const actualType = !targetStat ? "absent" : targetStat.isDirectory() ? "directory" : targetStat.isFile() ? "file" : "unsupported"; fail(`Relay runtime internal-link target is absent or has the wrong type (${path.relative(root, record.absolute)} -> ${record.target}; expected ${record.type}, found ${actualType}).`); } fs.symlinkSync( path.normalize(record.target), record.absolute, record.type === "directory" ? "dir" : "file", ); const realTarget = fs.realpathSync(record.absolute); if (!withinRoot(root, realTarget)) fail("Relay runtime reconstructed a link outside its signed root."); } return ordered.length; } function verifyExtractedRuntime(packageRoot, version, platformKey) { const required = ["package.json", path.join("bin", "relay.js"), path.join("src", "task-daemon.js"), path.join("overlay", "main.cjs")]; for (const relative of required) { if (!fs.statSync(path.join(packageRoot, relative), { throwIfNoEntry: false })?.isFile()) fail(`Relay runtime is missing ${relative}.`); } const manifest = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8")); if (manifest.name !== PACKAGE_NAME || manifest.version !== version) fail("Relay runtime package identity is invalid."); const permissions = repairRuntimeExecutablePermissions(packageRoot, { platform: platformKey }); if (!permissions.ok) { fail(`Relay runtime executable permission repair failed (${permissions.reason}${permissions.detail ? `: ${permissions.detail}` : ""}).`); } const bin = path.join(packageRoot, "bin", "relay.js"); if (!platformKey.startsWith("win32-")) { try { fs.accessSync(bin, fs.constants.X_OK); } catch { fail("Relay runtime CLI is not executable."); } } const executables = verifyRuntimeExecutables(packageRoot, { platform: platformKey }); if (!executables.ok) { fail(`Relay runtime executable verification failed (${executables.reason}${executables.detail ? `: ${executables.detail}` : ""}).`); } return { packageRoot, bin, electron: executables.electronPath, executablePaths: executables.paths.map((entry) => entry.path), repairedExecutableRoles: permissions.repaired, }; } function runtimeLayout(version, platformKey) { const root = path.join(os.homedir(), ".relay", "runtime"); const releasesDir = path.join(root, "releases"); const releaseId = `${version}-${platformKey}-${crypto.randomBytes(8).toString("hex")}`; const releaseRoot = path.join(releasesDir, releaseId); return { root, releasesDir, releaseId, releaseRoot, packageRoot: path.join(releaseRoot, "node_modules", PACKAGE_NAME), pointerPath: path.join(root, "current.json"), lockPath: path.join(root, "transaction.lock"), }; } function activeCanonicalCli({ homeDir = os.homedir(), platform = process.platform, readFileSync = fs.readFileSync, statSync = fs.statSync, } = {}) { const api = platform === "win32" ? path.win32 : path.posix; const runtimeRoot = api.join(homeDir, ".relay", "runtime"); let current; try { current = JSON.parse(readFileSync(api.join(runtimeRoot, "current.json"), "utf8")); } catch { return null; } if (current?.active !== true || current?.state !== "active" || !current.bin || !current.node) return null; const bin = api.resolve(String(current.bin)); const releasesRoot = api.resolve(api.join(runtimeRoot, "releases")); const relative = api.relative(releasesRoot, bin); if (!relative || relative.startsWith("..") || api.isAbsolute(relative)) return null; try { if (!statSync(bin).isFile() || !statSync(String(current.node)).isFile()) return null; } catch { return null; } return { bin, node: String(current.node), version: String(current.version || "") }; } function forwardActiveCanonicalCli(args = process.argv.slice(2), { findTarget = activeCanonicalCli, spawnImpl = spawnSync, } = {}) { const target = findTarget(); if (!target) return { forwarded: false }; const result = spawnImpl(target.node, [target.bin, ...args], { stdio: "inherit", windowsHide: true, env: process.env, }); if (result?.error) throw result.error; return { forwarded: true, status: Number.isInteger(result?.status) ? result.status : 1, target }; } function processAlive(pid) { if (!Number.isInteger(pid) || pid <= 0) return false; try { process.kill(pid, 0); return true; } catch (error) { return error?.code === "EPERM"; } } function acquireCanonicalLock(lockPath, { now = Date.now, staleAfterMs = 2 * 60 * 60_000, mkdirSync = fs.mkdirSync, readFileSync = fs.readFileSync, writeFileSync = fs.writeFileSync, rmSync = fs.rmSync, statSync = fs.statSync, existsSync = fs.existsSync, isProcessAlive = processAlive, } = {}) { fs.mkdirSync(path.dirname(lockPath), { recursive: true, mode: 0o700 }); const nonce = crypto.randomBytes(16).toString("hex"); const ownerPath = path.join(lockPath, "owner.json"); for (let attempt = 0; attempt < 2; attempt += 1) { try { mkdirSync(lockPath, { mode: 0o700 }); try { writeFileSync( ownerPath, `${JSON.stringify({ pid: process.pid, nonce, createdAt: now(), operation: "bootstrap-setup" })}\n`, { mode: 0o600 }, ); } catch (error) { try { rmSync(lockPath, { recursive: true, force: true }); } catch {} throw error; } return { release() { let owner = null; try { owner = JSON.parse(readFileSync(ownerPath, "utf8")); } catch {} if (owner?.nonce === nonce) rmSync(lockPath, { recursive: true, force: true }); }, }; } catch (error) { if (!existsSync(lockPath) || !["EEXIST", "ENOTEMPTY", "EPERM", "EACCES"].includes(error?.code)) throw error; let owner = null; try { owner = JSON.parse(readFileSync(ownerPath, "utf8")); } catch {} let observedAt = Number(owner?.createdAt || 0); if (!observedAt) { try { observedAt = Number(statSync(lockPath).mtimeMs || 0); } catch {} } const ownerDead = owner?.pid ? !isProcessAlive(Number(owner.pid)) : true; const stale = ownerDead && observedAt > 0 && now() - observedAt > staleAfterMs; if (!stale || attempt > 0) fail("Another verified Relay install or update is already in progress."); rmSync(lockPath, { recursive: true, force: true }); } } fail("Relay could not acquire its canonical install lock."); } function atomicWriteJson(file, value) { fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 }); const temporary = `${file}.${process.pid}.tmp`; fs.writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 }); fs.renameSync(temporary, file); } function atomicCreateText(file, value, mode = 0o600) { fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 }); const temporary = `${file}.${process.pid}.${crypto.randomBytes(6).toString("hex")}.tmp`; try { fs.writeFileSync(temporary, value, { mode, flag: "wx" }); fs.chmodSync(temporary, mode); // linkSync is the portable no-replace primitive Node exposes: unlike // renameSync it fails with EEXIST if another command appears after our // ownership preflight, so Relay can never win that race by overwriting it. fs.linkSync(temporary, file); } finally { try { fs.rmSync(temporary, { force: true }); } catch {} } } function shellSingleQuote(value) { return `'${String(value).replaceAll("'", `'"'"'`)}'`; } const CANONICAL_CLI_LAUNCHER_MARKER = "// relay-companion canonical CLI launcher v1"; const CANONICAL_CLI_SHIM_MARKER = "# relay-companion canonical CLI shim v1"; function canonicalCliLauncherSource(pointerPath) { return `#!/usr/bin/env node ${CANONICAL_CLI_LAUNCHER_MARKER} "use strict"; const fs = require("node:fs"); const path = require("node:path"); const { spawnSync } = require("node:child_process"); const pointerPath = ${JSON.stringify(pointerPath)}; let current; try { current = JSON.parse(fs.readFileSync(pointerPath, "utf8")); } catch { console.error("[relay] Relay is not installed. Run the exact setup command again."); process.exit(1); } const releases = path.resolve(path.dirname(pointerPath), "releases"); const bin = path.resolve(String(current?.bin || "")); const relative = path.relative(releases, bin); if (current?.active !== true || current?.state !== "active" || !relative || relative.startsWith("..") || path.isAbsolute(relative)) { console.error("[relay] Relay's active runtime pointer is invalid. Run the exact setup command again."); process.exit(1); } try { if (!fs.statSync(bin).isFile() || !fs.statSync(String(current.node || "")).isFile()) throw new Error("missing"); } catch { console.error("[relay] Relay's active runtime is missing. Run the exact setup command again."); process.exit(1); } const result = spawnSync(String(current.node), [bin, ...process.argv.slice(2)], { stdio: "inherit", env: process.env, windowsHide: true }); if (result.error) { console.error("[relay] " + (result.error.message || result.error)); process.exit(1); } process.exit(Number.isInteger(result.status) ? result.status : 1); `; } function canonicalCliShimSource(node, launcherPath) { return `#!/bin/sh\n${CANONICAL_CLI_SHIM_MARKER}\nexec ${shellSingleQuote(node)} ${shellSingleQuote(launcherPath)} "$@"\n`; } function canonicalCliPaths({ homeDir = os.homedir(), pointerPath = path.join(homeDir, ".relay", "runtime", "current.json"), } = {}) { const runtimeRoot = path.dirname(pointerPath); const binDir = path.join(homeDir, ".local", "bin"); return { pointerPath, runtimeRoot, launcherPath: path.join(runtimeRoot, "relay-cli.cjs"), binDir, shimPath: path.join(binDir, "relay"), }; } function isCanonicalCliShimSource(source, launcherPath) { const prefix = `#!/bin/sh\n${CANONICAL_CLI_SHIM_MARKER}\nexec `; const suffix = ` ${shellSingleQuote(launcherPath)} "$@"\n`; if (typeof source !== "string" || !source.startsWith(prefix) || !source.endsWith(suffix)) return false; const encodedNode = source.slice(prefix.length, -suffix.length); if (!encodedNode.startsWith("'") || !encodedNode.endsWith("'") || encodedNode.includes("\n") || encodedNode.includes("\r")) { return false; } const decodedNode = encodedNode.slice(1, -1).replaceAll(`'"'"'`, "'"); return shellSingleQuote(decodedNode) === encodedNode; } function installCanonicalCliLauncher(candidate, { platform = process.platform, homeDir = os.homedir(), env = process.env, pointerPath = path.join(homeDir, ".relay", "runtime", "current.json"), } = {}) { if (platform !== "linux") return { ok: true, skipped: true }; const { launcherPath, binDir, shimPath } = canonicalCliPaths({ homeDir, pointerPath }); const launcherSource = canonicalCliLauncherSource(pointerPath); const shimSource = canonicalCliShimSource(candidate.node, launcherPath); try { const existingLauncher = fs.existsSync(launcherPath) ? fs.readFileSync(launcherPath, "utf8") : null; const existingShim = fs.existsSync(shimPath) ? fs.readFileSync(shimPath, "utf8") : null; if (existingLauncher !== null && existingLauncher !== launcherSource) { return { ok: false, reason: "cli_launcher_collision", detail: `${launcherPath} already exists and is not Relay's generated launcher`, launcherPath, shimPath, }; } if (existingShim !== null && !isCanonicalCliShimSource(existingShim, launcherPath)) { return { ok: false, reason: "cli_shim_collision", detail: `${shimPath} already exists and is not Relay's generated command`, launcherPath, shimPath, }; } // Existing generated files deliberately remain byte-identical. The shim's // Node is itself a retained durable copy, while the launcher follows the // current pointer, so neither needs to be rewritten during an update. if (existingLauncher === null) atomicCreateText(launcherPath, launcherSource, 0o700); if (existingShim === null) atomicCreateText(shimPath, shimSource, 0o755); const pathAvailable = String(env.PATH || "").split(":").some((entry) => { try { return path.resolve(entry) === path.resolve(binDir); } catch { return false; } }); return { ok: true, launcherPath, shimPath, pathAvailable }; } catch (error) { return { ok: false, reason: "cli_launcher_install_failed", detail: error?.message || String(error), launcherPath, shimPath }; } } async function waitForRuntimeHealth(runtime, { platform = process.platform, healthCheck = exactRuntimeHealth, attempts = 60, intervalMs = 500, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)), } = {}) { let health = null; for (let attempt = 0; attempt < attempts; attempt += 1) { health = await healthCheck(runtime, { platform }); if (health?.ok) return health; if (attempt + 1 < attempts) await sleep(intervalMs); } return health || { ok: false, daemon: false, pill: false }; } // Scheduled Tasks launch through wscript and return before their real children. // Ending the task can therefore leave the old daemon/pill alive, where they win // the singleton race and make exact-root health reject an otherwise valid runtime. function stopPreviousWindowsRuntime({ platform = process.platform, spawnImpl = spawnSync } = {}) { if (platform !== "win32") return { ok: true, attempted: false, detail: "" }; for (const task of WINDOWS_RELAY_TASKS) { spawnImpl("schtasks.exe", ["/End", "/TN", task], { stdio: "ignore", windowsHide: true }); } const stopped = spawnImpl( "powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", WINDOWS_STOP_RELAY_SERVICES_PS], { encoding: "utf8", windowsHide: true }, ); return { ok: Boolean(stopped && !stopped.error && stopped.status === 0), attempted: true, detail: stopped?.error?.message || String(stopped?.stderr || stopped?.stdout || "").trim(), }; } const SETUP_COMPATIBILITY_VALUE_FLAGS = new Set([ "--code", "--api", "--web", "--open-relay", "--host", ]); const SAFE_SETUP_TOKEN = /^[A-Za-z0-9_-]{4,256}$/; /** * The signed-in website still emits the pre-pill setup contract while the * installer migration is in flight. Keep that compatibility surface narrow: * only the website's value-bearing arguments may cross the thin bootstrap into * the already-verified runtime, and endpoint overrides must be secure origins. */ function validateSetupCompatibilityArgs(argv = []) { if (!Array.isArray(argv) || argv.length === 0) return []; const args = argv.map((value) => String(value)); const values = new Map(); for (let index = 0; index < args.length; index += 2) { const flag = args[index]; const value = args[index + 1]; if (!SETUP_COMPATIBILITY_VALUE_FLAGS.has(flag)) { fail("Relay setup received an unsupported compatibility option."); } if (!value || value.startsWith("--")) { fail("Relay setup received an incomplete compatibility option."); } if (values.has(flag)) fail("Relay setup received a duplicate compatibility option."); values.set(flag, value); } const code = values.get("--code"); if (!code || !SAFE_SETUP_TOKEN.test(code)) fail("Relay setup received an invalid pairing code."); const openRelay = values.get("--open-relay"); if (openRelay && !SAFE_SETUP_TOKEN.test(openRelay)) fail("Relay setup received an invalid relay token."); const host = values.get("--host"); if (host && !openRelay) fail("Relay setup received a host without a relay token."); if (host && host !== "claude" && host !== "codex") fail("Relay setup received an invalid agent host."); for (const flag of ["--api", "--web"]) { const value = values.get(flag); if (!value) continue; let url; try { url = new URL(value); } catch { fail("Relay setup received an invalid service origin."); } const loopbackHttp = url.protocol === "http:" && ["localhost", "127.0.0.1", "::1"].includes(url.hostname); if ( (url.protocol !== "https:" && !loopbackHttp) || url.username || url.password || url.pathname !== "/" || url.search || url.hash ) { fail("Relay setup requires a secure service origin."); } } return args; } async function activateRuntime(layout, runtime, version, { platform = process.platform, homeDir = os.homedir(), spawnImpl = spawnSync, existsSync = fs.existsSync, readFileSync = fs.readFileSync, writePointer = atomicWriteJson, removePointer = (file) => fs.rmSync(file, { force: true }), healthCheck = exactRuntimeHealth, healthAttempts = 60, healthIntervalMs = 500, sleep, activateLinuxServices = activateLinuxRuntimeServices, activateMacServices = activateMacRuntimeServices, activationDeadlineMs = 90_000, now = Date.now, setupCompatibilityArgs = [], } = {}) { let previous = null; try { previous = JSON.parse(readFileSync(layout.pointerPath, "utf8")); } catch {} const preparedAt = Date.now(); const candidate = { schema: 1, active: true, state: "active", version, releaseId: layout.releaseId, releaseRoot: layout.releaseRoot, packageRoot: runtime.packageRoot, bin: runtime.bin, node: relayOwnedNodePath( durableNodePath(stableNodePath(), { platform, runtimeRoot: layout.root }), { platform, runtimeRoot: layout.root }, ), source: "signed-release-artifact", preparedAt, updatedAt: preparedAt, }; const journal = { schema: 1, active: false, state: "activating", candidate, previous: previous?.active === true ? previous : null, preparedAt, updatedAt: preparedAt, }; // The durable journal MUST precede every launcher/MCP/autostart mutation. // A crash can therefore be recovered; no process can mistake the candidate // for active before setup and service health have succeeded. writePointer(layout.pointerPath, journal); function commandOk(result) { return Boolean(result && !result.error && result.status === 0); } function rollback(activationFailure) { let target = null; let result = null; if (previous?.bin && existsSync(previous.bin)) { target = "previous runtime"; result = spawnImpl(candidate.node, [previous.bin, "repair-runtime", "--no-trampoline", "--claim"], { stdio: "ignore", windowsHide: true, }); } else { // A failed first install has no former canonical owner. The candidate's // uninstall removes the partial MCP/autostart writes setup may have made. target = "partial installation"; result = spawnImpl(candidate.node, [runtime.bin, "uninstall", "--no-trampoline"], { stdio: "ignore", windowsHide: true, }); } if (!commandOk(result)) { try { writePointer(layout.pointerPath, { ...journal, state: "recovery-required", failure: { reason: "activation-and-rollback-failed", detail: `${activationFailure}; could not restore ${target}`, }, updatedAt: Date.now(), }); } catch {} fail(`${activationFailure} Rollback failed; Relay left a recovery journal and did not claim the runtime active.`); } if (previous?.active === true) writePointer(layout.pointerPath, previous); else removePointer(layout.pointerPath); // Real runtime layouts place each immutable candidate directly beneath // releases/. Remove only that exact, now-unreferenced child after rollback; // recovery-required deliberately retains it above. if (layout.releasesDir) { const releasesDir = path.resolve(layout.releasesDir); const failedRoot = path.resolve(layout.releaseRoot); if (path.dirname(failedRoot) === releasesDir && failedRoot !== releasesDir) { try { fs.rmSync(failedRoot, { recursive: true, force: true }); } catch {} } } } const cliLauncher = installCanonicalCliLauncher(candidate, { platform, homeDir, pointerPath: layout.pointerPath, }); if (!cliLauncher.ok) { const message = `Relay could not install its durable command launcher (${cliLauncher.detail || cliLauncher.reason}).`; rollback(message); fail(message); } const stopped = stopPreviousWindowsRuntime({ platform, spawnImpl }); if (!stopped.ok) { const message = `Relay runtime activation could not stop the previous desktop services${stopped.detail ? ` (${stopped.detail})` : ""}.`; rollback(message); fail(message); } // On macOS/Linux setup owns registration, but the shared activation state // machine owns service restart. This keeps first-install MCP/autostart creation // in the candidate while bootstrap and the updater use the same exact-root handoff. const setupArgs = [runtime.bin, "setup", "--no-trampoline", "--claim", ...setupCompatibilityArgs]; if (platform === "darwin" || platform === "linux") setupArgs.push("--no-restart"); const result = spawnImpl(candidate.node, setupArgs, { stdio: "inherit", windowsHide: true, env: { ...process.env, RELAY_BOOTSTRAP_ACTIVATED: "1" }, }); if (!commandOk(result)) { const message = `Relay runtime activation failed${result?.status == null ? "" : ` (exit ${result.status})`}.`; rollback(message); fail(message); } let health = null; if (platform === "darwin") { const activated = await activateMacServices(runtime, { homeDir, platform, run: spawnImpl, healthCheck, activationDeadlineMs, now, ...(sleep ? { sleep } : {}), }); if (!activated?.ok) { const detail = activated?.detail ? ` (${activated.detail})` : ""; const message = `Relay runtime activation could not start the exact registered macOS services${detail}.`; rollback(message); fail(message); } health = activated.health; } else if (platform === "linux") { const activated = await activateLinuxServices(runtime, { homeDir, platform, run: spawnImpl, healthCheck, attempts: healthAttempts, ...(sleep ? { sleep } : {}), }); if (!activated?.ok) { const detail = activated?.detail ? ` (${activated.detail})` : ""; const message = `Relay runtime activation could not start the exact registered Linux services${detail}.`; rollback(message); fail(message); } health = activated.health; } else { health = await waitForRuntimeHealth(runtime, { healthCheck, attempts: healthAttempts, intervalMs: healthIntervalMs, ...(sleep ? { sleep } : {}), }); } if (!health?.ok) { const missing = [health?.daemon ? "" : "daemon", health?.pill ? "" : "pill"].filter(Boolean).join(" and "); const message = `Relay runtime activation failed exact-root health${missing ? ` (${missing} did not start)` : ""}.`; rollback(message); fail(message); } try { writePointer(layout.pointerPath, candidate); } catch (error) { rollback("Relay could not commit the verified runtime pointer."); throw error; } return { candidate, cliLauncher }; } async function setup(argv = []) { assertCompatibleNode(); const setupCompatibilityArgs = validateSetupCompatibilityArgs(argv); const version = exactVersion(packageJson.version); const platformKey = releasePlatform(); const layout = runtimeLayout(version, platformKey); const lock = acquireCanonicalLock(layout.lockPath); try { const runtime = await stageVerifiedRuntime({ version, platformKey, destination: layout.releaseRoot }); const activated = await activateRuntime(layout, runtime, version, { setupCompatibilityArgs }); if (setupCompatibilityArgs.includes("--code")) { console.log(`Relay ${version} is installed and paired. The Relay pill is open.`); } else { console.log(`Relay ${version} is installed. The Relay pill is open; sign in there to finish.`); } if (activated?.cliLauncher && !activated.cliLauncher.pathAvailable) { console.log(`Relay's command is installed at ${activated.cliLauncher.shimPath}. Open a new login session to add ~/.local/bin to PATH.`); } } finally { lock.release(); } } async function stageVerifiedRuntime({ version, platformKey = releasePlatform(), destination }) { const manifestUrl = `${RELEASE_ORIGIN}${RELEASE_BASE_PATH}/v${version}/manifest.json`; if (!destination) fail("Relay runtime destination is missing."); if (fs.existsSync(destination)) { fail("Relay refused to reuse a pre-existing runtime destination."); } const { artifact } = parseSignedManifest(await fetchBuffer(manifestUrl), { version, platformKey }); fs.mkdirSync(path.dirname(destination), { recursive: true, mode: 0o700 }); const staging = fs.mkdtempSync(path.join(path.dirname(destination), `.relay-download-${version}-`)); const archivePath = path.join(staging, "runtime.tar.gz"); const extracted = path.join(staging, "root"); try { fs.mkdirSync(extracted); await downloadVerifiedArtifact(artifact.url, archivePath, artifact); validateArchiveListing(archivePath); const unpackInvocation = tarInvocation({ archivePath, mode: "extract", destination: extracted }); const unpack = spawnSync(unpackInvocation.command, unpackInvocation.args, { cwd: unpackInvocation.cwd, encoding: "utf8", windowsHide: true, }); if (unpack.error || unpack.status !== 0) fail(`Relay could not unpack its verified runtime: ${unpack.error?.message || unpack.stderr}`); rejectExtractedLinks(extracted); restoreRuntimeLinks(extracted); verifyExtractedRuntime(path.join(extracted, "node_modules", PACKAGE_NAME), version, platformKey); if (fs.existsSync(destination)) fail("Relay runtime destination appeared during verification; refusing the race."); fs.renameSync(extracted, destination); return verifyExtractedRuntime(path.join(destination, "node_modules", PACKAGE_NAME), version, platformKey); } finally { fs.rmSync(staging, { recursive: true, force: true }); } } async function main() { const command = process.argv[2] || "help"; // Once the signed canonical runtime exists, the npm-global thin installer is // only a human-facing command shim. Hand every ordinary command to the active // runtime; keep `setup` local because an explicit exact-version npx install is // allowed to replace or repair the canonical runtime. if (command !== "setup") { const forwarded = forwardActiveCanonicalCli(); if (forwarded.forwarded) { process.exitCode = forwarded.status; return; } } if (command === "version" || process.argv.includes("--version")) return console.log(packageJson.version); if (command === "setup") return setup(process.argv.slice(3)); console.log([ "Relay secure setup", "", ` npx --yes ${PACKAGE_NAME}@${packageJson.version} setup`, "", "Downloads the exact signed Relay runtime, verifies it, installs Relay, and opens the signed-out pill.", ].join("\n")); } module.exports = { activeCanonicalCli, activateRuntime, acquireCanonicalLock, assertCompatibleNode, canonicalCliLauncherSource, canonicalCliPaths, canonicalCliShimSource, durableNodePath, downloadVerifiedArtifact, forwardActiveCanonicalCli, installCanonicalCliLauncher, isCanonicalCliShimSource, releasePlatform, parseSignedManifest, restoreRuntimeLinks, stageVerifiedRuntime, stableNodePath, tarInvocation, validateSetupCompatibilityArgs, validateArchiveEntries, verifyExtractedRuntime, waitForRuntimeHealth, }; if (require.main === module) { main().catch((error) => { console.error(`[relay] ${error instanceof Error ? error.message : error}`); process.exitCode = 1; }); }