import { createHash } from "node:crypto"; import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import { dirname } from "node:path"; import type { UniDeskConfig } from "./config"; import { rootPath } from "./config"; import { runCommand, type CommandResult } from "./command"; import { resolveEgressProxySourceRef, type MasterShadowsocksSourceSpec } from "./egress-proxy-sources"; import type { RenderedCliResult } from "./output"; import { shQuote } from "./platform-infra-public-service"; import { fingerprintSecretValues, readEnvSourceFile, requiredEnvValue } from "./secrets"; import { transHostProxyEnvSummary } from "./trans-host-proxy"; const configPath = "config/platform-infra/host-proxy.yaml"; interface HostProxyConfig { defaults: { targetId: string }; server: HostProxyServer; sources: Record; targets: Record; } interface HostProxyServer { id: string; enabled: boolean; benchmarkRef: string; implementationRef: string; sourceConfigRef: string; sourceRef: string; sourceKey: string; sourceFingerprint: string; masterShadowsocks: MasterShadowsocksSourceSpec; composeFile: string; serviceName: string; containerName: string; image: string; configPath: string; listenHost: string; listenPort: number; health: { mode: "tcp"; host: string; port: number }; fingerprint: string; } interface HostProxySource { id: string; sourceType: "benchmark-validated-master-shadowsocks"; serverRef: string; benchmarkRef: string; implementationRef: string; sourceConfigRef: string; sourceRef: string; sourceKey: string; sourceFingerprint: string; masterShadowsocks: MasterShadowsocksSourceSpec; client: HostProxyClient; proxyUrl: string; externalProbeUrl: string; fingerprint: string; } interface HostProxyClient { mode: "trans-static-binary"; upstreamUrl: string; version: string; archiveCachePath: string; archiveSha256: string; archiveInstallPath: string; binaryMember: string; binaryCachePath: string; binarySha256: string; installPath: string; configPath: string; unitPath: string; serviceName: string; listenHost: string; listenPort: number; podAccess: HostProxyPodAccess | null; clashApiListen: string; healthUrl: string; } interface HostProxyPodAccess { enabled: boolean; listenHost: string; listenPort: number; proxyUrl: string; } interface HostProxyTarget { id: string; route: string; enabled: boolean; sourceRef: string; source: HostProxySource; env: { httpProxy: string; httpsProxy: string; allProxy: string; noProxy: string[]; }; files: { envFile: string; profile: string; apt: string; dockerSystemdDropIn: string; k3sSystemdDropIn: string; }; apply: { reloadSystemd: boolean; restartDocker: boolean; restartK3s: boolean; }; } interface HostProxySecretMaterial { serverConfigJson: string; clientConfigJson: string; fingerprint: string; sourcePath: string; valuesPrinted: false; } type HostProxyAction = "plan" | "apply" | "status"; interface HostProxyOptions { action: HostProxyAction; targetId: string; confirm: boolean; dryRun: boolean; } export async function runPlatformInfraHostProxyCommand(_config: UniDeskConfig, args: string[]): Promise> { const options = parseOptions(args); const config = readHostProxyConfig(); const target = resolveTarget(config, options.targetId); if (options.action === "plan") return renderPlan(plan(config.server, target)); if (options.action === "status") return renderStatus(status(config.server, target)); if (!options.confirm || options.dryRun) { return renderPlan({ ...plan(config.server, target), mode: "dry-run", mutation: false, next: { apply: `bun scripts/cli.ts platform-infra egress-proxy host apply --target ${target.id} --confirm`, status: `bun scripts/cli.ts platform-infra egress-proxy host status --target ${target.id}`, }, }); } return renderApply(apply(config.server, target)); } function parseOptions(args: string[]): HostProxyOptions { const actionRaw = args[0] ?? "plan"; if (actionRaw !== "plan" && actionRaw !== "apply" && actionRaw !== "status") { throw new Error("platform-infra egress-proxy host usage: host plan|apply|status --target JD01 [--dry-run|--confirm]"); } const targetId = option(args, "--target"); const confirm = args.includes("--confirm"); const dryRun = args.includes("--dry-run") || !confirm; if (confirm && args.includes("--dry-run")) throw new Error("host apply accepts only one of --confirm or --dry-run"); return { action: actionRaw, targetId: targetId ?? readHostProxyConfig().defaults.targetId, confirm, dryRun }; } function readHostProxyConfig(): HostProxyConfig { const root = record(Bun.YAML.parse(readFileSync(rootPath(configPath), "utf8")), configPath); if (root.kind !== "platform-infra-host-proxy") throw new Error(`${configPath}.kind must be platform-infra-host-proxy`); const defaultsRaw = record(root.defaults, `${configPath}.defaults`); const server = parseServer(record(root.server, `${configPath}.server`), `${configPath}.server`); const sources = Object.fromEntries(Object.entries(record(root.sources, `${configPath}.sources`)).map(([id, value]) => { const source = parseSource(id, record(value, `${configPath}.sources.${id}`)); if (source.serverRef !== `server.${server.id}`) throw new Error(`${configPath}.sources.${id}.serverRef must be server.${server.id}`); return [id, source]; })); const targets: Record = {}; for (const [id, value] of Object.entries(record(root.targets, `${configPath}.targets`))) { targets[id] = parseTarget(id, record(value, `${configPath}.targets.${id}`), sources); } return { defaults: { targetId: stringField(defaultsRaw, "targetId", `${configPath}.defaults`) }, server, sources, targets, }; } function parseServer(raw: Record, label: string): HostProxyServer { const sourceConfigRef = stringField(raw, "sourceConfigRef", label); const resolved = resolveEgressProxySourceRef(sourceConfigRef, `${label}.sourceConfigRef`); if (resolved.sourceType !== "master-shadowsocks" || resolved.masterShadowsocks === null) { throw new Error(`${label}.sourceConfigRef must reference a master-shadowsocks source`); } const healthRaw = record(raw.health, `${label}.health`); const server = { id: stringField(raw, "id", label), enabled: booleanField(raw, "enabled", label), benchmarkRef: stringField(raw, "benchmarkRef", label), implementationRef: stringField(raw, "implementationRef", label), sourceConfigRef, sourceRef: resolved.sourceRef, sourceKey: resolved.sourceKey, sourceFingerprint: resolved.fingerprint, masterShadowsocks: resolved.masterShadowsocks, composeFile: repoYamlPathField(raw, "composeFile", label), serviceName: stringField(raw, "serviceName", label), containerName: stringField(raw, "containerName", label), image: imageField(raw, "image", label), configPath: absolutePathField(raw, "configPath", label), listenHost: hostField(raw, "listenHost", label), listenPort: portField(raw, "listenPort", label), health: { mode: enumField(healthRaw, "mode", `${label}.health`, ["tcp"] as const), host: hostField(healthRaw, "host", `${label}.health`), port: portField(healthRaw, "port", `${label}.health`), }, fingerprint: "", }; return { ...server, fingerprint: fingerprint({ benchmarkRef: server.benchmarkRef, implementationRef: server.implementationRef, sourceConfigRef, sourceFingerprint: server.sourceFingerprint, composeFile: server.composeFile, serviceName: server.serviceName, containerName: server.containerName, configPath: server.configPath, listenHost: server.listenHost, listenPort: server.listenPort, health: server.health, }), }; } function parseSource(id: string, raw: Record): HostProxySource { const sourceType = enumField(raw, "sourceType", `${configPath}.sources.${id}`, ["benchmark-validated-master-shadowsocks"] as const); const sourceConfigRef = stringField(raw, "sourceConfigRef", `${configPath}.sources.${id}`); const resolved = resolveEgressProxySourceRef(sourceConfigRef, `${configPath}.sources.${id}.sourceConfigRef`); if (resolved.sourceType !== "master-shadowsocks" || resolved.masterShadowsocks === null) { throw new Error(`${configPath}.sources.${id}.sourceConfigRef must reference a master-shadowsocks source`); } const client = clientSpec(record(raw.client, `${configPath}.sources.${id}.client`), `${configPath}.sources.${id}.client`); const proxyUrl = urlField(raw, "proxyUrl", `${configPath}.sources.${id}`); const expectedProxyUrl = `http://${client.listenHost}:${client.listenPort}`; if (proxyUrl !== expectedProxyUrl) throw new Error(`${configPath}.sources.${id}.proxyUrl must be ${expectedProxyUrl}`); const source = { id, sourceType, serverRef: stringField(raw, "serverRef", `${configPath}.sources.${id}`), benchmarkRef: stringField(raw, "benchmarkRef", `${configPath}.sources.${id}`), implementationRef: stringField(raw, "implementationRef", `${configPath}.sources.${id}`), sourceConfigRef, sourceRef: resolved.sourceRef, sourceKey: resolved.sourceKey, sourceFingerprint: resolved.fingerprint, masterShadowsocks: resolved.masterShadowsocks, client, proxyUrl, externalProbeUrl: urlField(raw, "externalProbeUrl", `${configPath}.sources.${id}`), fingerprint: "", }; return { ...source, fingerprint: fingerprint({ sourceType, benchmarkRef: source.benchmarkRef, implementationRef: source.implementationRef, sourceConfigRef, sourceFingerprint: source.sourceFingerprint, client, proxyUrl, externalProbeUrl: source.externalProbeUrl, }), }; } function clientSpec(raw: Record, label: string): HostProxyClient { const mode = enumField(raw, "mode", label, ["trans-static-binary"] as const); const podAccess = raw.podAccess === undefined ? null : podAccessSpec(record(raw.podAccess, `${label}.podAccess`), `${label}.podAccess`); const client = { mode, upstreamUrl: httpsUrlField(raw, "upstreamUrl", label), version: stringField(raw, "version", label), archiveCachePath: repoStatePathField(raw, "archiveCachePath", label), archiveSha256: sha256Field(raw, "archiveSha256", label), archiveInstallPath: absolutePathField(raw, "archiveInstallPath", label), binaryMember: relativePathField(raw, "binaryMember", label), binaryCachePath: repoStatePathField(raw, "binaryCachePath", label), binarySha256: sha256Field(raw, "binarySha256", label), installPath: absolutePathField(raw, "installPath", label), configPath: absolutePathField(raw, "configPath", label), unitPath: absolutePathField(raw, "unitPath", label), serviceName: stringField(raw, "serviceName", label), listenHost: hostField(raw, "listenHost", label), listenPort: portField(raw, "listenPort", label), podAccess, clashApiListen: listenAddressField(raw, "clashApiListen", label), healthUrl: urlField(raw, "healthUrl", label), }; if (client.listenHost !== "127.0.0.1") throw new Error(`${label}.listenHost must stay 127.0.0.1 for host-local bootstrap proxy`); if (client.unitPath !== `/etc/systemd/system/${client.serviceName}.service`) throw new Error(`${label}.unitPath must match serviceName`); return client; } function podAccessSpec(raw: Record, label: string): HostProxyPodAccess { const enabled = booleanField(raw, "enabled", label); const listenHost = hostField(raw, "listenHost", label); const listenPort = portField(raw, "listenPort", label); const proxyUrl = urlField(raw, "proxyUrl", label); const expectedProxyUrl = `http://${listenHost}:${listenPort}`; if (proxyUrl !== expectedProxyUrl) throw new Error(`${label}.proxyUrl must be ${expectedProxyUrl}`); if (listenHost === "0.0.0.0" || listenHost === "127.0.0.1") { throw new Error(`${label}.listenHost must be a pod-reachable private host address, not ${listenHost}`); } if (!privateIpv4(listenHost)) throw new Error(`${label}.listenHost must be a private IPv4 address`); return { enabled, listenHost, listenPort, proxyUrl }; } function parseTarget(id: string, raw: Record, sources: Record): HostProxyTarget { const sourceRef = stringField(raw, "sourceRef", `${configPath}.targets.${id}`); if (!sourceRef.startsWith("sources.")) throw new Error(`${configPath}.targets.${id}.sourceRef must use sources.`); const sourceId = sourceRef.slice("sources.".length); const source = sources[sourceId]; if (source === undefined) throw new Error(`${configPath}.targets.${id}.sourceRef points to missing source ${sourceId}`); const envRaw = record(raw.env, `${configPath}.targets.${id}.env`); const filesRaw = record(raw.files, `${configPath}.targets.${id}.files`); const applyRaw = record(raw.apply, `${configPath}.targets.${id}.apply`); const noProxy = stringArrayField(envRaw, "noProxy", `${configPath}.targets.${id}.env`); if (!noProxy.includes("hyueapi.com") || !noProxy.includes(".hyueapi.com")) { throw new Error(`${configPath}.targets.${id}.env.noProxy must preserve hyueapi.com and .hyueapi.com`); } const target: HostProxyTarget = { id, route: routeField(raw, "route", `${configPath}.targets.${id}`), enabled: booleanField(raw, "enabled", `${configPath}.targets.${id}`), sourceRef, source, env: { httpProxy: urlField(envRaw, "httpProxy", `${configPath}.targets.${id}.env`), httpsProxy: urlField(envRaw, "httpsProxy", `${configPath}.targets.${id}.env`), allProxy: urlField(envRaw, "allProxy", `${configPath}.targets.${id}.env`), noProxy, }, files: { envFile: absolutePathField(filesRaw, "envFile", `${configPath}.targets.${id}.files`), profile: absolutePathField(filesRaw, "profile", `${configPath}.targets.${id}.files`), apt: absolutePathField(filesRaw, "apt", `${configPath}.targets.${id}.files`), dockerSystemdDropIn: absolutePathField(filesRaw, "dockerSystemdDropIn", `${configPath}.targets.${id}.files`), k3sSystemdDropIn: absolutePathField(filesRaw, "k3sSystemdDropIn", `${configPath}.targets.${id}.files`), }, apply: { reloadSystemd: booleanField(applyRaw, "reloadSystemd", `${configPath}.targets.${id}.apply`), restartDocker: booleanField(applyRaw, "restartDocker", `${configPath}.targets.${id}.apply`), restartK3s: booleanField(applyRaw, "restartK3s", `${configPath}.targets.${id}.apply`), }, }; if (target.env.httpProxy !== source.proxyUrl || target.env.httpsProxy !== source.proxyUrl || target.env.allProxy !== source.proxyUrl) { throw new Error(`${configPath}.targets.${id}.env proxy URLs must match ${sourceRef}.proxyUrl`); } return target; } function resolveTarget(config: HostProxyConfig, targetId: string): HostProxyTarget { const target = config.targets[targetId]; if (target === undefined) throw new Error(`${configPath}.targets.${targetId} is not defined`); return target; } function plan(server: HostProxyServer, target: HostProxyTarget): Record { const client = target.source.client; return { ok: true, command: "platform-infra egress-proxy host plan", mode: "plan", mutation: false, server: serverSummary(server), target: targetSummary(target), artifact: artifactSummary(client), files: target.files, apply: target.apply, env: { proxyUrl: target.env.httpProxy, noProxyCount: target.env.noProxy.length, noProxyIncludesHyueapi: true }, valuesPrinted: false, next: { dryRun: `bun scripts/cli.ts platform-infra egress-proxy host apply --target ${target.id} --dry-run`, apply: `bun scripts/cli.ts platform-infra egress-proxy host apply --target ${target.id} --confirm`, status: `bun scripts/cli.ts platform-infra egress-proxy host status --target ${target.id}`, }, }; } function apply(server: HostProxyServer, target: HostProxyTarget): Record { const material = proxySecretMaterial(server, target.source); const serverApply = applyMasterProxyServer(server, material); const artifact = prepareClientArtifact(target.source.client); const remotePrepare = runTrans(target.route, remotePrepareScript(target.source.client), 30); const remoteArchive = remotePrepare.exitCode === 0 ? runTrans(target.route, remoteArchiveStatusScript(target.source.client), 30) : remotePrepare; const remoteArchiveParsed = record(parseJson(remoteArchive.stdout) ?? {}); const upload = remotePrepare.exitCode === 0 && remoteArchive.exitCode === 0 && remoteArchiveParsed.archiveSha256Ok === true ? skippedCommandResult(["bun", "scripts/ssh-cli.ts", "ssh", target.route, "upload", rootPath(target.source.client.archiveCachePath), target.source.client.archiveInstallPath], "remote archive already matches YAML sha256") : remotePrepare.exitCode === 0 ? runTransUpload(target.route, rootPath(target.source.client.archiveCachePath), target.source.client.archiveInstallPath, 1_800_000) : remotePrepare; const remoteApply = upload.exitCode === 0 ? runTrans(target.route, remoteApplyScript(target, material.clientConfigJson), 60) : upload; const parsed = parseJson(remoteApply.stdout); return { ok: serverApply.ok === true && artifact.ok === true && remotePrepare.exitCode === 0 && upload.exitCode === 0 && remoteApply.exitCode === 0 && record(parsed).ok !== false, command: "platform-infra egress-proxy host apply", mode: "apply", mutation: serverApply.ok === true && remoteApply.exitCode === 0, server: serverSummary(server), serverApply, target: targetSummary(target), artifact, distribution: { mode: target.source.client.mode, artifact: "archive", prepare: compactLocalResult(remotePrepare), remoteArchive: compactLocalResult(remoteArchive), upload: compactLocalResult(upload), }, remote: parsed ?? { stdoutPreview: remoteApply.stdout.slice(0, 2000) }, stderrTail: remoteApply.stderr.slice(-2000), valuesPrinted: false, next: { status: `bun scripts/cli.ts platform-infra egress-proxy host status --target ${target.id}` }, }; } function status(server: HostProxyServer, target: HostProxyTarget): Record { const serverStatus = masterProxyServerStatus(server); const result = runTrans(target.route, remoteStatusScript(target), 45); const parsed = parseJson(result.stdout); return { ok: serverStatus.ok === true && result.exitCode === 0 && record(parsed).ok !== false, command: "platform-infra egress-proxy host status", mode: "status", mutation: false, server: serverSummary(server), serverStatus, target: targetSummary(target), remote: parsed ?? { stdoutPreview: result.stdout.slice(0, 2000) }, stderrTail: result.stderr.slice(-2000), valuesPrinted: false, }; } function proxySecretMaterial(server: HostProxyServer, source: HostProxySource): HostProxySecretMaterial { const envSource = readEnvSourceFile({ root: rootPath(".state", "secrets"), sourceRef: source.sourceRef, missingMessage: (sourcePath) => `host proxy requires ${sourcePath} with ${source.sourceKey}`, }); const password = requiredEnvValue(envSource.values, source.sourceKey, source.sourceRef); const serverConfigJson = renderMasterShadowsocksServerConfig(server, password); const clientConfigJson = renderSingBoxClientConfig(source, password); return { serverConfigJson, clientConfigJson, sourcePath: envSource.sourcePathRedacted, fingerprint: fingerprintSecretValues({ password, serverConfigJson, clientConfigJson }, ["password", "serverConfigJson", "clientConfigJson"]), valuesPrinted: false, }; } function renderMasterShadowsocksServerConfig(server: HostProxyServer, password: string): string { return `${JSON.stringify({ server: server.listenHost, server_port: server.listenPort, password, method: server.masterShadowsocks.method, timeout: 300, mode: "tcp_only", }, null, 2)}\n`; } function renderSingBoxClientConfig(source: HostProxySource, password: string): string { const client = source.client; const inbounds = [ { type: "mixed", tag: "mixed-host-local", listen: client.listenHost, listen_port: client.listenPort }, ...(client.podAccess?.enabled ? [{ type: "mixed", tag: "mixed-pod-access", listen: client.podAccess.listenHost, listen_port: client.podAccess.listenPort }] : []), ]; return `${JSON.stringify({ log: { level: "info", timestamp: true }, experimental: { clash_api: { external_controller: client.clashApiListen } }, inbounds, outbounds: [ { type: "shadowsocks", tag: "master-vpn", server: source.masterShadowsocks.serverHost, server_port: source.masterShadowsocks.serverPort, method: source.masterShadowsocks.method, password, }, { type: "direct", tag: "direct" }, { type: "block", tag: "block" }, ], route: { rules: [ { ip_is_private: true, outbound: "direct" }, { domain_suffix: ["cluster.local", "svc"], outbound: "direct" }, ], final: "master-vpn", }, }, null, 2)}\n`; } function applyMasterProxyServer(server: HostProxyServer, material: HostProxySecretMaterial): Record { mkdirSync(dirname(server.configPath), { recursive: true }); writeFileSync(server.configPath, material.serverConfigJson, "utf8"); chmodSync(server.configPath, 0o600); const compose = runCommand(["docker", "compose", "-f", rootPath(server.composeFile), "up", "-d", server.serviceName], rootPath(), { timeoutMs: 60_000 }); const health = masterProxyTcpHealth(server); const inspect = runCommand(["docker", "inspect", server.containerName, "--format", "{{.State.Status}} {{.State.Running}} {{.Config.Image}}"], rootPath(), { timeoutMs: 15_000 }); const existingHealthy = compose.exitCode !== 0 && /container name .* already in use|Conflict\. The container name/i.test(compose.stderr) && inspect.exitCode === 0 && /\brunning true\b/.test(inspect.stdout.trim()) && health.ok === true; return { ok: server.enabled && (compose.exitCode === 0 || existingHealthy) && health.ok, enabled: server.enabled, benchmarkRef: server.benchmarkRef, implementationRef: server.implementationRef, sourceConfigRef: server.sourceConfigRef, sourceFingerprint: server.sourceFingerprint, materialFingerprint: material.fingerprint, compose: compactLocalResult(compose), existingHealthy, inspect: compactLocalResult(inspect), health, valuesPrinted: false, }; } function masterProxyServerStatus(server: HostProxyServer): Record { const inspect = runCommand(["docker", "inspect", server.containerName, "--format", "{{.State.Status}} {{.State.Running}} {{.Config.Image}}"], rootPath(), { timeoutMs: 15_000 }); const health = masterProxyTcpHealth(server); return { ok: server.enabled && inspect.exitCode === 0 && health.ok, enabled: server.enabled, benchmarkRef: server.benchmarkRef, implementationRef: server.implementationRef, sourceConfigRef: server.sourceConfigRef, sourceFingerprint: server.sourceFingerprint, container: inspect.exitCode === 0 ? inspect.stdout.trim() : "missing", inspect: compactLocalResult(inspect), health, valuesPrinted: false, }; } function prepareClientArtifact(client: HostProxyClient): Record { const binaryPath = rootPath(client.binaryCachePath); const archivePath = rootPath(client.archiveCachePath); let archiveAction = "existing"; let binaryAction = "existing"; if (!existsSync(binaryPath) || sha256File(binaryPath) !== client.binarySha256) { if (!existsSync(archivePath) || sha256File(archivePath) !== client.archiveSha256) { mkdirSync(dirname(archivePath), { recursive: true }); const download = runCommand(["curl", "-fL", "--connect-timeout", "15", "--max-time", "240", "-o", archivePath, client.upstreamUrl], rootPath(), { timeoutMs: 300_000 }); if (download.exitCode !== 0) return { ok: false, action: "download", result: compactLocalResult(download), valuesPrinted: false }; archiveAction = "downloaded"; } if (sha256File(archivePath) !== client.archiveSha256) { return { ok: false, action: archiveAction, reason: "archive-sha256-mismatch", path: client.archiveCachePath, valuesPrinted: false }; } mkdirSync(dirname(binaryPath), { recursive: true }); const extract = runCommand(["bash", "-lc", `tar -xOf ${shQuote(archivePath)} ${shQuote(client.binaryMember)} > ${shQuote(binaryPath)} && chmod 0755 ${shQuote(binaryPath)}`], rootPath(), { timeoutMs: 120_000 }); if (extract.exitCode !== 0) return { ok: false, action: "extract", result: compactLocalResult(extract), valuesPrinted: false }; binaryAction = "extracted"; } const binarySha = sha256File(binaryPath); return { ok: binarySha === client.binarySha256, mode: client.mode, version: client.version, archiveCachePath: client.archiveCachePath, binaryCachePath: client.binaryCachePath, archiveAction, binaryAction, binarySha256: binarySha, installPath: client.installPath, valuesPrinted: false, }; } function remotePrepareScript(client: HostProxyClient): string { return ` set -eu mkdir -p ${shQuote(dirname(client.installPath))} ${shQuote(dirname(client.configPath))} ${shQuote(dirname(client.unitPath))} ${shQuote(dirname(client.archiveInstallPath))} `; } function remoteArchiveStatusScript(client: HostProxyClient): string { return ` set -eu archive_sha="$(sha256sum ${shQuote(client.archiveInstallPath)} 2>/dev/null | awk '{print $1}' || true)" python3 - "$archive_sha" <<'PY' import json import sys archive_sha = sys.argv[1] payload = { "ok": True, "archivePresent": bool(archive_sha), "archiveSha256Ok": archive_sha == "${client.archiveSha256}", "archiveSha256Prefix": archive_sha[:12] if archive_sha else "", "valuesPrinted": False, } print(json.dumps(payload, ensure_ascii=False, indent=2)) PY `; } function remoteApplyScript(target: HostProxyTarget, clientConfigJson: string): string { const client = target.source.client; const envFile = renderEnvFile(target); const profile = renderProfile(target); const apt = renderApt(target); const dockerDropIn = renderSystemdDropIn(target.files.envFile); const k3sDropIn = renderSystemdDropIn(target.files.envFile); const unit = renderClientUnit(client); const daemonReload = target.apply.reloadSystemd ? "systemctl daemon-reload >/dev/null 2>&1 || true" : ": # reloadSystemd disabled by YAML"; const restartDocker = target.apply.restartDocker ? "nohup sh -c 'sleep 1; systemctl restart docker >/tmp/unidesk-host-proxy-docker-restart.out 2>/tmp/unidesk-host-proxy-docker-restart.err || true' >/dev/null 2>&1 &" : ": # restartDocker disabled by YAML"; const restartK3s = target.apply.restartK3s ? "nohup sh -c 'sleep 1; systemctl restart k3s >/tmp/unidesk-host-proxy-k3s-restart.out 2>/tmp/unidesk-host-proxy-k3s-restart.err || true' >/dev/null 2>&1 &" : ": # restartK3s disabled by YAML"; return ` set -eu write_b64() { path="$1" mode="$2" data="$3" mkdir -p "$(dirname "$path")" tmp="$path.tmp.$$" printf '%s' "$data" | base64 -d >"$tmp" chmod "$mode" "$tmp" mv "$tmp" "$path" } write_b64 ${shQuote(client.configPath)} 0600 ${shQuote(b64(clientConfigJson))} write_b64 ${shQuote(client.unitPath)} 0644 ${shQuote(b64(unit))} write_b64 ${shQuote(target.files.envFile)} 0644 ${shQuote(b64(envFile))} write_b64 ${shQuote(target.files.profile)} 0644 ${shQuote(b64(profile))} write_b64 ${shQuote(target.files.apt)} 0644 ${shQuote(b64(apt))} write_b64 ${shQuote(target.files.dockerSystemdDropIn)} 0644 ${shQuote(b64(dockerDropIn))} write_b64 ${shQuote(target.files.k3sSystemdDropIn)} 0644 ${shQuote(b64(k3sDropIn))} archive_sha="$(sha256sum ${shQuote(client.archiveInstallPath)} 2>/dev/null | awk '{print $1}' || true)" if [ "$archive_sha" != ${shQuote(client.archiveSha256)} ]; then printf 'archive sha256 mismatch\\n' >&2 exit 23 fi tmp_bin="${client.installPath}.tmp.$$" tar -xOf ${shQuote(client.archiveInstallPath)} ${shQuote(client.binaryMember)} >"$tmp_bin" chmod 0755 "$tmp_bin" binary_sha="$(sha256sum "$tmp_bin" | awk '{print $1}')" if [ "$binary_sha" != ${shQuote(client.binarySha256)} ]; then rm -f "$tmp_bin" printf 'binary sha256 mismatch\\n' >&2 exit 24 fi mv -f "$tmp_bin" ${shQuote(client.installPath)} chmod 0755 ${shQuote(client.installPath)} ${daemonReload} systemctl enable --now ${shQuote(client.serviceName)} >/tmp/unidesk-host-proxy-systemctl.out 2>/tmp/unidesk-host-proxy-systemctl.err || true systemctl restart ${shQuote(client.serviceName)} >/tmp/unidesk-host-proxy-restart.out 2>/tmp/unidesk-host-proxy-restart.err || true sleep 2 ${remoteStatusProbeShell(target)} ${restartDocker} ${restartK3s} `; } function remoteStatusScript(target: HostProxyTarget): string { return ` set -eu ${remoteStatusProbeShell(target)} `; } function remoteStatusProbeShell(target: HostProxyTarget): string { const client = target.source.client; const podAccess = client.podAccess; const podAccessShell = podAccess?.enabled ? ` pod_probe_rc=0 pod_probe_status="$(HTTP_PROXY=${shQuote(podAccess.proxyUrl)} HTTPS_PROXY=${shQuote(podAccess.proxyUrl)} ALL_PROXY=${shQuote(podAccess.proxyUrl)} NO_PROXY=${shQuote(target.env.noProxy.join(","))} curl -fsS -o /dev/null -w '%{http_code}' --max-time 20 ${shQuote(target.source.externalProbeUrl)} 2>/tmp/unidesk-host-proxy-pod-probe.err)" || pod_probe_rc=$? ` : ` pod_probe_rc=0 pod_probe_status="skipped" `; return ` exists() { [ -f "$1" ] && printf present || printf missing; } contains() { grep -F "$2" "$1" >/dev/null 2>&1 && printf true || printf false; } service_active="$(systemctl is-active ${shQuote(client.serviceName)} 2>/dev/null || true)" binary_sha="$(sha256sum ${shQuote(client.installPath)} 2>/dev/null | awk '{print $1}' || true)" archive_sha="$(sha256sum ${shQuote(client.archiveInstallPath)} 2>/dev/null | awk '{print $1}' || true)" health_rc=0 curl -fsS --max-time 5 ${shQuote(client.healthUrl)} >/tmp/unidesk-host-proxy-health.out 2>/tmp/unidesk-host-proxy-health.err || health_rc=$? probe_rc=0 probe_status="$(HTTP_PROXY=${shQuote(target.env.httpProxy)} HTTPS_PROXY=${shQuote(target.env.httpsProxy)} ALL_PROXY=${shQuote(target.env.allProxy)} NO_PROXY=${shQuote(target.env.noProxy.join(","))} curl -fsS -o /dev/null -w '%{http_code}' --max-time 20 ${shQuote(target.source.externalProbeUrl)} 2>/tmp/unidesk-host-proxy-probe.err)" || probe_rc=$? ${podAccessShell} python3 - "$health_rc" "$probe_rc" "$probe_status" "$(exists ${shQuote(target.files.envFile)})" "$(exists ${shQuote(target.files.profile)})" "$(exists ${shQuote(target.files.apt)})" "$(exists ${shQuote(target.files.dockerSystemdDropIn)})" "$(exists ${shQuote(target.files.k3sSystemdDropIn)})" "$(contains ${shQuote(target.files.envFile)} hyueapi.com)" "$(contains ${shQuote(target.files.envFile)} .hyueapi.com)" "$service_active" "$binary_sha" "$archive_sha" ${shQuote(podAccess?.enabled ? "true" : "false")} "$pod_probe_rc" "$pod_probe_status" <<'PY' import json import sys health_rc = int(sys.argv[1]) probe_rc = int(sys.argv[2]) service_active = sys.argv[11] binary_sha = sys.argv[12] archive_sha = sys.argv[13] pod_access_enabled = sys.argv[14] == "true" pod_probe_rc = int(sys.argv[15]) payload = { "ok": health_rc == 0 and probe_rc == 0 and (not pod_access_enabled or pod_probe_rc == 0) and service_active == "active" and binary_sha == "${client.binarySha256}" and archive_sha == "${client.archiveSha256}" and all(value == "present" for value in sys.argv[4:9]) and sys.argv[9] == "true" and sys.argv[10] == "true", "target": "${target.id}", "route": "${target.route}", "sourceRef": "${target.sourceRef}", "sourceFingerprint": "${target.source.fingerprint}", "client": { "mode": "${client.mode}", "service": "${client.serviceName}", "serviceActive": service_active, "binarySha256Ok": binary_sha == "${client.binarySha256}", "archiveSha256Ok": archive_sha == "${client.archiveSha256}", "listen": "${client.listenHost}:${client.listenPort}", "podAccess": { "enabled": pod_access_enabled, "listen": "${podAccess?.enabled ? `${podAccess.listenHost}:${podAccess.listenPort}` : ""}", "proxyUrl": "${podAccess?.enabled ? podAccess.proxyUrl : ""}", }, }, "files": { "envFile": sys.argv[4], "profile": sys.argv[5], "apt": sys.argv[6], "dockerSystemdDropIn": sys.argv[7], "k3sSystemdDropIn": sys.argv[8], }, "noProxy": {"hyueapi": sys.argv[9] == "true", "wildcardHyueapi": sys.argv[10] == "true"}, "health": {"exitCode": health_rc, "url": "${client.healthUrl}"}, "externalProbe": {"exitCode": probe_rc, "status": sys.argv[3], "url": "${target.source.externalProbeUrl}"}, "podAccessProbe": {"enabled": pod_access_enabled, "exitCode": pod_probe_rc, "status": sys.argv[16], "url": "${target.source.externalProbeUrl}"}, "valuesPrinted": False, } print(json.dumps(payload, ensure_ascii=False, indent=2)) sys.exit(0 if payload["ok"] else 1) PY `; } function renderClientUnit(client: HostProxyClient): string { return [ "[Unit]", "Description=UniDesk host egress proxy client", "After=network-online.target", "Wants=network-online.target", "", "[Service]", "Type=simple", `ExecStart=${client.installPath} run -c ${client.configPath}`, "Restart=always", "RestartSec=5", "LimitNOFILE=1048576", "NoNewPrivileges=true", "", "[Install]", "WantedBy=multi-user.target", "", ].join("\n"); } function renderEnvFile(target: HostProxyTarget): string { const noProxy = target.env.noProxy.join(","); return [ "# Generated by: bun scripts/cli.ts platform-infra egress-proxy host apply", "# Source: config/platform-infra/host-proxy.yaml", `HTTP_PROXY=${target.env.httpProxy}`, `HTTPS_PROXY=${target.env.httpsProxy}`, `ALL_PROXY=${target.env.allProxy}`, `NO_PROXY=${noProxy}`, `http_proxy=${target.env.httpProxy}`, `https_proxy=${target.env.httpsProxy}`, `all_proxy=${target.env.allProxy}`, `no_proxy=${noProxy}`, "", ].join("\n"); } function renderProfile(target: HostProxyTarget): string { return renderEnvFile(target) .split("\n") .filter((line) => line.length > 0 && !line.startsWith("#")) .map((line) => `export ${line}`) .join("\n") + "\n"; } function renderApt(target: HostProxyTarget): string { return [ `Acquire::http::Proxy "${target.env.httpProxy}";`, `Acquire::https::Proxy "${target.env.httpsProxy}";`, "", ].join("\n"); } function renderSystemdDropIn(envFile: string): string { return ["[Service]", `EnvironmentFile=-${envFile}`, ""].join("\n"); } function serverSummary(server: HostProxyServer): Record { return { id: server.id, enabled: server.enabled, benchmarkRef: server.benchmarkRef, implementationRef: server.implementationRef, sourceConfigRef: server.sourceConfigRef, sourceFingerprint: server.sourceFingerprint, composeFile: server.composeFile, serviceName: server.serviceName, containerName: server.containerName, image: server.image, listen: `${server.listenHost}:${server.listenPort}`, health: `${server.health.host}:${server.health.port}`, fingerprint: server.fingerprint, valuesPrinted: false, }; } function targetSummary(target: HostProxyTarget): Record { const client = target.source.client; return { id: target.id, route: target.route, enabled: target.enabled, sourceRef: target.sourceRef, sourceType: target.source.sourceType, sourceFingerprint: target.source.fingerprint, proxyUrl: target.source.proxyUrl, benchmarkRef: target.source.benchmarkRef, implementationRef: target.source.implementationRef, client: { mode: client.mode, version: client.version, installPath: client.installPath, configPath: client.configPath, unitPath: client.unitPath, serviceName: client.serviceName, listen: `${client.listenHost}:${client.listenPort}`, }, trans: { hostProxyEnv: transHostProxyEnvSummary(target.id), }, }; } function artifactSummary(client: HostProxyClient): Record { return { mode: client.mode, version: client.version, upstreamUrl: client.upstreamUrl, archiveCachePath: client.archiveCachePath, archiveSha256: client.archiveSha256, archiveInstallPath: client.archiveInstallPath, binaryCachePath: client.binaryCachePath, binarySha256: client.binarySha256, installPath: client.installPath, }; } function masterProxyTcpHealth(server: HostProxyServer): Record { const result = runCommand(["bash", "-lc", `timeout 5 bash -lc ${shQuote(` { return { exitCode: result.exitCode, timedOut: result.timedOut, stdoutTail: result.stdout.slice(-1000), stderrTail: result.stderr.slice(-1000), }; } function renderPlan(result: Record): RenderedCliResult { const server = record(result.server); const target = record(result.target); const env = record(result.env); const artifact = record(result.artifact); const next = record(result.next); const lines = [ "PLATFORM-INFRA HOST PROXY PLAN", ...table(["SERVER", "LISTEN", "BENCHMARK", "SOURCE"], [[text(server.id), text(server.listen), text(server.benchmarkRef), text(server.sourceConfigRef)]]), "", ...table(["TARGET", "ROUTE", "SOURCE", "PROXY"], [[text(target.id), text(target.route), text(target.sourceRef), text(target.proxyUrl)]]), "", ...table(["CLIENT_MODE", "VERSION", "INSTALL", "SHA256"], [[text(artifact.mode), text(artifact.version), text(artifact.installPath), shortSha(text(artifact.binarySha256))]]), "", ...table(["PROXY", "NO_PROXY_COUNT", "HYUEAPI", "VALUES"], [[text(env.proxyUrl), text(env.noProxyCount), "preserved", "printed=false"]]), "", ...transHostProxyEnvTable(target), "", "NEXT", ` dry-run: ${text(next.dryRun, "")}`, ` apply: ${text(next.apply, "")}`, ` status: ${text(next.status, "")}`, "", "Disclosure: Secret material and generated proxy config are not printed.", ]; return { ok: result.ok !== false, command: text(result.command, "platform-infra egress-proxy host plan"), renderedText: lines.join("\n"), contentType: "text/plain" }; } function renderApply(result: Record): RenderedCliResult { const server = record(result.server); const serverApply = record(result.serverApply); const serverHealth = record(serverApply.health); const target = record(result.target); const artifact = record(result.artifact); const distribution = record(result.distribution); const remote = record(result.remote); const client = record(remote.client); const podProbe = record(remote.podAccessProbe); const probe = record(remote.externalProbe); const lines = [ "PLATFORM-INFRA HOST PROXY APPLY", ...table(["SERVER", "OK", "LISTEN", "BENCHMARK"], [[text(server.id), text(serverApply.ok), text(server.listen), text(server.benchmarkRef)]]), "", ...table(["SERVER_HEALTH", "CLIENT_BINARY", "DISTRIBUTION"], [[text(serverHealth.ok), text(artifact.binaryAction), text(distribution.mode)]]), "", ...table(["TARGET", "ROUTE", "OK", "CLIENT"], [[text(target.id), text(target.route), result.ok === false ? "false" : "true", text(client.service)]]), "", ...table(["SERVICE_ACTIVE", "BINARY_SHA", "HOST_PROBE", "POD_PROBE", "VALUES"], [[text(client.serviceActive), text(client.binarySha256Ok), text(probe.status), text(podProbe.status, "skipped"), "printed=false"]]), "", ...transHostProxyEnvTable(target), "", `NEXT\n ${text(record(result.next).status, "")}`, ]; return { ok: result.ok !== false, command: "platform-infra egress-proxy host apply", renderedText: lines.join("\n"), contentType: "text/plain" }; } function renderStatus(result: Record): RenderedCliResult { const server = record(result.server); const serverStatus = record(result.serverStatus); const target = record(result.target); const remote = record(result.remote); const client = record(remote.client); const files = record(remote.files); const noProxy = record(remote.noProxy); const health = record(remote.health); const probe = record(remote.externalProbe); const podProbe = record(remote.podAccessProbe); const lines = [ "PLATFORM-INFRA HOST PROXY STATUS", ...table(["SERVER", "OK", "LISTEN", "CONTAINER"], [[text(server.id), text(serverStatus.ok), text(server.listen), text(serverStatus.container)]]), "", ...table(["TARGET", "ROUTE", "OK", "CLIENT"], [[text(target.id), text(target.route), result.ok === false ? "false" : "true", text(client.service)]]), "", ...table(["SERVICE_ACTIVE", "BINARY_SHA", "HEALTH_EXIT", "HOST_PROBE", "POD_PROBE"], [[text(client.serviceActive), text(client.binarySha256Ok), text(health.exitCode), text(probe.status), text(podProbe.status, "skipped")]]), "", ...table(["ENV", "PROFILE", "APT", "DOCKER_DROPIN", "K3S_DROPIN"], [[text(files.envFile), text(files.profile), text(files.apt), text(files.dockerSystemdDropIn), text(files.k3sSystemdDropIn)]]), "", ...table(["NO_PROXY_HYUEAPI", "NO_PROXY_WILDCARD", "VALUES"], [[text(noProxy.hyueapi), text(noProxy.wildcardHyueapi), "printed=false"]]), "", ...transHostProxyEnvTable(target), "", "Disclosure: file content, Secret values and generated proxy config are not printed.", ]; return { ok: result.ok !== false, command: "platform-infra egress-proxy host status", renderedText: lines.join("\n"), contentType: "text/plain" }; } function transHostProxyEnvTable(target: Record): string[] { const trans = record(target.trans); const rule = record(trans.hostProxyEnv); return table( ["TRANS_PROXY_ENV", "ENV_REF", "APPLY_TO", "ENV_FILE"], [[text(rule.enabled, "false"), text(rule.envFileRef, ""), text(rule.applyTo, "disabled"), text(rule.envFile, "")]], ); } function option(args: string[], name: string): string | null { const index = args.indexOf(name); if (index === -1) return null; const value = args[index + 1]; if (value === undefined || value.startsWith("--")) throw new Error(`${name} requires a value`); return value; } function parseJson(value: string): unknown { const trimmed = value.trim(); if (trimmed.length === 0) return null; try { return JSON.parse(trimmed) as unknown; } catch { const start = trimmed.indexOf("{"); const end = trimmed.lastIndexOf("}"); if (start >= 0 && end > start) { try { return JSON.parse(trimmed.slice(start, end + 1)) as unknown; } catch {} } } return null; } function record(value: unknown, _label = "value"): Record { if (typeof value !== "object" || value === null || Array.isArray(value)) return {}; return value as Record; } function stringField(obj: Record, key: string, label: string): string { const value = obj[key]; if (typeof value !== "string" || value.trim().length === 0) throw new Error(`${label}.${key} must be a non-empty string`); return value.trim(); } function routeField(obj: Record, key: string, label: string): string { const value = stringField(obj, key, label); if (!/^[A-Za-z0-9:_./-]+$/u.test(value) || value.includes("..")) throw new Error(`${label}.${key} has an unsupported route format`); return value; } function repoYamlPathField(obj: Record, key: string, label: string): string { const value = stringField(obj, key, label); if (value.startsWith("/") || value.includes("..") || !value.startsWith("config/") || !value.endsWith(".yaml")) { throw new Error(`${label}.${key} must be a repo-relative config/*.yaml path without ..`); } return value; } function repoStatePathField(obj: Record, key: string, label: string): string { const value = stringField(obj, key, label); if (value.startsWith("/") || value.includes("..") || !value.startsWith(".state/")) { throw new Error(`${label}.${key} must be a repo-relative .state/ path without ..`); } return value; } function relativePathField(obj: Record, key: string, label: string): string { const value = stringField(obj, key, label); if (value.startsWith("/") || value.includes("..")) throw new Error(`${label}.${key} must be a relative path without ..`); return value; } function absolutePathField(obj: Record, key: string, label: string): string { const value = stringField(obj, key, label); if (!value.startsWith("/") || value.includes("..")) throw new Error(`${label}.${key} must be an absolute path without ..`); return value; } function booleanField(obj: Record, key: string, label: string): boolean { const value = obj[key]; if (typeof value !== "boolean") throw new Error(`${label}.${key} must be a boolean`); return value; } function stringArrayField(obj: Record, key: string, label: string): string[] { const value = obj[key]; if (!Array.isArray(value) || !value.every((item) => typeof item === "string" && item.trim().length > 0)) { throw new Error(`${label}.${key} must be an array of non-empty strings`); } return value.map((item) => item.trim()); } function enumField(obj: Record, key: string, label: string, values: T): T[number] { const value = stringField(obj, key, label); if (!(values as readonly string[]).includes(value)) throw new Error(`${label}.${key} must be one of ${values.join(", ")}`); return value as T[number]; } function urlField(obj: Record, key: string, label: string): string { const value = stringField(obj, key, label); const url = new URL(value); if (url.protocol !== "http:" && url.protocol !== "https:") throw new Error(`${label}.${key} must use http:// or https://`); if (url.username || url.password || url.hash) throw new Error(`${label}.${key} must not include credentials or hash`); return value; } function httpsUrlField(obj: Record, key: string, label: string): string { const value = urlField(obj, key, label); if (!value.startsWith("https://")) throw new Error(`${label}.${key} must use https://`); return value; } function hostField(obj: Record, key: string, label: string): string { const value = stringField(obj, key, label); if (!/^[A-Za-z0-9._:-]+$/u.test(value)) throw new Error(`${label}.${key} has an unsupported host/listen format`); return value; } function privateIpv4(value: string): boolean { const parts = value.split(".").map((part) => Number(part)); if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) return false; return parts[0] === 10 || (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) || (parts[0] === 192 && parts[1] === 168); } function portField(obj: Record, key: string, label: string): number { const value = obj[key]; if (typeof value !== "number" || !Number.isInteger(value) || value < 1 || value > 65535) throw new Error(`${label}.${key} must be a TCP port`); return value; } function imageField(obj: Record, key: string, label: string): string { const value = stringField(obj, key, label); if (!/^[A-Za-z0-9._:/@+-]+$/u.test(value) || value.includes("..")) throw new Error(`${label}.${key} has an unsupported image reference format`); return value; } function listenAddressField(obj: Record, key: string, label: string): string { const value = stringField(obj, key, label); const [host, portRaw, extra] = value.split(":"); if (extra !== undefined || host === undefined || portRaw === undefined || host.length === 0) throw new Error(`${label}.${key} must be host:port`); const port = Number(portRaw); if (!/^[A-Za-z0-9._-]+$/u.test(host) || !Number.isInteger(port) || port < 1 || port > 65535) throw new Error(`${label}.${key} must be host:port`); return value; } function sha256Field(obj: Record, key: string, label: string): string { const value = stringField(obj, key, label).toLowerCase(); if (!/^[0-9a-f]{64}$/u.test(value)) throw new Error(`${label}.${key} must be a sha256 hex digest`); return value; } function b64(value: string): string { return Buffer.from(value, "utf8").toString("base64"); } function fingerprint(value: unknown): string { return `sha256:${createHash("sha256").update(JSON.stringify(value)).digest("hex").slice(0, 16)}`; } function text(value: unknown, fallback = "-"): string { if (typeof value === "string") return value; if (typeof value === "number" || typeof value === "boolean") return String(value); return fallback; } function shortSha(value: string): string { return /^[0-9a-f]{64}$/u.test(value) ? value.slice(0, 12) : value; } function table(headers: string[], rows: string[][]): string[] { const widths = headers.map((header, index) => Math.max(header.length, ...rows.map((row) => (row[index] ?? "").length))); const format = (row: string[]) => row.map((cell, index) => (cell ?? "").padEnd(widths[index] ?? 0)).join(" ").trimEnd(); return [format(headers), format(headers.map((header, index) => "-".repeat(widths[index] ?? header.length))), ...rows.map(format)]; }