From 29dab8fab2e3a8a3c73cd4129c9da1c13281373e Mon Sep 17 00:00:00 2001 From: Codex Date: Mon, 18 May 2026 16:20:11 +0000 Subject: [PATCH] fix: support ts backend core cli probes --- scripts/src/artifact-registry.ts | 6 +----- scripts/src/debug.ts | 29 +++++++++++++++++++++++++---- scripts/src/docker.ts | 12 +++++++++++- scripts/src/e2e.ts | 17 +++++++++++++++-- scripts/src/microservices.ts | 28 +++++++++++++++++++++++++--- scripts/src/ssh.ts | 23 ++++++++++++++++++----- 6 files changed, 95 insertions(+), 20 deletions(-) diff --git a/scripts/src/artifact-registry.ts b/scripts/src/artifact-registry.ts index 9b3475a9..24414ec8 100644 --- a/scripts/src/artifact-registry.ts +++ b/scripts/src/artifact-registry.ts @@ -763,11 +763,7 @@ async function deployBackendCoreNow(options: ArtifactRegistryOptions): Promise/dev/null 2>&1'; then", - " docker exec \"$cid\" backend-core --fetch-json http://127.0.0.1:8080/health --require-ok >/tmp/unidesk-backend-core-health.json", - "else", - " docker exec \"$cid\" bun -e \"fetch('http://127.0.0.1:8080/health').then(async r=>{console.log(await r.text()); process.exit(r.ok?0:1)}).catch(e=>{console.error(e); process.exit(1)})\" >/tmp/unidesk-backend-core-health.json", - "fi", + "docker exec \"$cid\" bun -e \"fetch('http://127.0.0.1:8080/health').then(async r=>{const text=await r.text(); console.log(text); process.exit(r.ok?0:1)}).catch(e=>{console.error(e); process.exit(1)})\" >/tmp/unidesk-backend-core-health.json", "cat /tmp/unidesk-backend-core-health.json", ].join("\n"); const deploy = runCommand(["bash", "-lc", composeLockScript(upScript)], repoRoot, { timeoutMs: Math.max(options.timeoutMs, 300_000) }); diff --git a/scripts/src/debug.ts b/scripts/src/debug.ts index aebc1860..d7735f13 100644 --- a/scripts/src/debug.ts +++ b/scripts/src/debug.ts @@ -22,9 +22,30 @@ async function readJson(url: string, init?: RequestInit): Promise { } } +function coreFetchCommand(path: string, init?: { method?: string; body?: unknown }): string[] { + const method = init?.method ?? "GET"; + const url = `http://127.0.0.1:8080${path}`; + const body = init?.body === undefined ? "" : JSON.stringify(init.body); + const script = [ + "set -euo pipefail", + "if command -v backend-core >/dev/null 2>&1; then", + ` exec backend-core --fetch-json ${shellQuote(url)} --method ${shellQuote(method)}${body.length > 0 ? ` --body-json ${shellQuote(body)}` : ""}`, + "fi", + `url=${shellQuote(url)}`, + `method=${shellQuote(method)}`, + `body=${shellQuote(body)}`, + "export url method body", + "bun -e 'const url=process.env.url; const method=process.env.method; const body=process.env.body; fetch(url,{method,body:body?body:undefined,headers:body?{\"content-type\":\"application/json\"}:undefined}).then(async r=>{const text=await r.text(); console.log(JSON.stringify({ok:r.ok,status:r.status,body:text?JSON.parse(text):null})); process.exit(r.ok?0:1);}).catch(e=>{console.error(e); process.exit(1);})'", + ].join("\n"); + return ["docker", "exec", "unidesk-backend-core", "sh", "-lc", script]; +} + +function shellQuote(value: string): string { + return `'${value.replace(/'/g, `'\\''`)}'`; +} + function coreInternalFetch(path: string, init?: { method?: string; body?: unknown }): unknown { - const command = ["docker", "exec", "unidesk-backend-core", "backend-core", "--fetch-json", `http://127.0.0.1:8080${path}`, "--method", init?.method ?? "GET"]; - if (init?.body !== undefined) command.push("--body-json", JSON.stringify(init.body)); + const command = coreFetchCommand(path, init); const result = runCommand(command, repoRoot); if (result.exitCode !== 0) { return { ok: false, exitCode: result.exitCode, stdoutTail: result.stdout.slice(-1200), stderrTail: result.stderr.slice(-1200) }; @@ -37,7 +58,7 @@ function coreInternalFetch(path: string, init?: { method?: string; body?: unknow } function coreDockerStatusSummary(): unknown { - const result = runCommand(["docker", "exec", "unidesk-backend-core", "backend-core", "--fetch-json", "http://127.0.0.1:8080/api/nodes/docker-status"], repoRoot); + const result = runCommand(coreFetchCommand("/api/nodes/docker-status"), repoRoot); if (result.exitCode !== 0) { return { ok: false, exitCode: result.exitCode, stdoutTail: result.stdout.slice(-1200), stderrTail: result.stderr.slice(-1200) }; } @@ -67,7 +88,7 @@ function coreDockerStatusSummary(): unknown { } function coreSystemStatusSummary(): unknown { - const result = runCommand(["docker", "exec", "unidesk-backend-core", "backend-core", "--fetch-json", "http://127.0.0.1:8080/api/nodes/system-status?limit=24"], repoRoot); + const result = runCommand(coreFetchCommand("/api/nodes/system-status?limit=24"), repoRoot); if (result.exitCode !== 0) { return { ok: false, exitCode: result.exitCode, stdoutTail: result.stdout.slice(-1200), stderrTail: result.stderr.slice(-1200) }; } diff --git a/scripts/src/docker.ts b/scripts/src/docker.ts index e01bcd78..469b10cd 100644 --- a/scripts/src/docker.ts +++ b/scripts/src/docker.ts @@ -400,7 +400,17 @@ async function probe(url: string): Promise { } function dockerExecJson(container: string, path: string): unknown { - const result = runCommand(["docker", "exec", container, "backend-core", "--fetch-json", `http://127.0.0.1:8080${path}`], repoRoot); + const url = `http://127.0.0.1:8080${path}`; + const script = [ + "set -euo pipefail", + "if command -v backend-core >/dev/null 2>&1; then", + ` exec backend-core --fetch-json ${shellQuote(url)}`, + "fi", + `url=${shellQuote(url)}`, + "export url", + "bun -e 'const url=process.env.url; fetch(url).then(async r=>{const text=await r.text(); console.log(JSON.stringify({ok:r.ok,status:r.status,body:text?JSON.parse(text):null})); process.exit(r.ok?0:1);}).catch(e=>{console.error(e); process.exit(1)})'", + ].join("\n"); + const result = runCommand(["docker", "exec", container, "sh", "-lc", script], repoRoot); if (result.exitCode !== 0) { return { ok: false, exitCode: result.exitCode, stdout: result.stdout.slice(-1200), stderr: result.stderr.slice(-1200) }; } diff --git a/scripts/src/e2e.ts b/scripts/src/e2e.ts index 7c97b604..533920e9 100644 --- a/scripts/src/e2e.ts +++ b/scripts/src/e2e.ts @@ -915,8 +915,21 @@ function runPsql(config: UniDeskConfig, sql: string): { ok: boolean; stdout: str } function dockerCoreJson(path: string, init?: { method?: string; body?: unknown }): unknown { - const command = ["docker", "exec", "unidesk-backend-core", "backend-core", "--fetch-json", `http://127.0.0.1:8080${path}`, "--method", init?.method ?? "GET"]; - if (init?.body !== undefined) command.push("--body-json", JSON.stringify(init.body)); + const method = init?.method ?? "GET"; + const body = init?.body === undefined ? "" : JSON.stringify(init.body); + const url = `http://127.0.0.1:8080${path}`; + const script = [ + "set -euo pipefail", + "if command -v backend-core >/dev/null 2>&1; then", + ` exec backend-core --fetch-json ${shellQuote(url)} --method ${shellQuote(method)}${body.length > 0 ? ` --body-json ${shellQuote(body)}` : ""}`, + "fi", + `url=${shellQuote(url)}`, + `method=${shellQuote(method)}`, + `body=${shellQuote(body)}`, + "export url method body", + "bun -e 'const url=process.env.url; const method=process.env.method; const body=process.env.body; fetch(url,{method,body:body?body:undefined,headers:body?{\"content-type\":\"application/json\"}:undefined}).then(async r=>{const text=await r.text(); console.log(JSON.stringify({ok:r.ok,status:r.status,body:text?JSON.parse(text):null})); process.exit(r.ok?0:1);}).catch(e=>{console.error(e); process.exit(1)})'", + ].join("\n"); + const command = ["docker", "exec", "unidesk-backend-core", "sh", "-lc", script]; const result = runCommand(command, repoRoot); if (result.exitCode !== 0) return { ok: false, exitCode: result.exitCode, stdout: result.stdout.slice(-1200), stderr: result.stderr.slice(-1200) }; try { diff --git a/scripts/src/microservices.ts b/scripts/src/microservices.ts index aa978bbb..c8b55beb 100644 --- a/scripts/src/microservices.ts +++ b/scripts/src/microservices.ts @@ -3,11 +3,33 @@ import { runCommand } from "./command"; import { type UniDeskConfig, repoRoot } from "./config"; import { jsonByteLength, previewJson } from "./preview"; +function shellQuote(value: string): string { + return `'${value.replace(/'/g, `'\\''`)}'`; +} + +function dockerCoreFetchCommand(path: string, init?: { method?: string; body?: unknown; maxResponseBytes?: number }): string[] { + const maxResponseBytes = Math.max(1024, Math.floor(init?.maxResponseBytes ?? 5_000_000)); + const method = init?.method ?? "GET"; + const body = init?.body === undefined ? "" : JSON.stringify(init.body); + const url = `http://127.0.0.1:8080${path}`; + const script = [ + "set -euo pipefail", + "if command -v backend-core >/dev/null 2>&1; then", + ` exec backend-core --fetch-json ${shellQuote(url)} --method ${shellQuote(method)} --max-response-bytes ${shellQuote(String(maxResponseBytes))}${body.length > 0 ? ` --body-json ${shellQuote(body)}` : ""}`, + "fi", + `url=${shellQuote(url)}`, + `method=${shellQuote(method)}`, + `max_bytes=${shellQuote(String(maxResponseBytes))}`, + `body=${shellQuote(body)}`, + "export url method body max_bytes", + "bun -e 'const url=process.env.url; const method=process.env.method; const body=process.env.body; const maxBytes=Number(process.env.max_bytes||\"5000000\"); fetch(url,{method,body:body?body:undefined,headers:body?{\"content-type\":\"application/json\"}:undefined}).then(async r=>{const text=await r.text(); const out={ok:r.ok,status:r.status,body:text?JSON.parse(text):null}; const json=JSON.stringify(out); if (Buffer.byteLength(json) > maxBytes) { console.error(\"response too large\"); process.exit(1); } console.log(json); process.exit(r.ok?0:1);}).catch(e=>{console.error(e); process.exit(1)})'", + ].join("\n"); + return ["docker", "exec", "unidesk-backend-core", "sh", "-lc", script]; +} + export function coreInternalFetch(path: string, init?: { method?: string; body?: unknown; maxResponseBytes?: number }): unknown { if (!path.startsWith("/")) throw new Error("core internal path must start with /"); - const maxResponseBytes = Math.max(1024, Math.floor(init?.maxResponseBytes ?? 5_000_000)); - const command = ["docker", "exec", "unidesk-backend-core", "backend-core", "--fetch-json", `http://127.0.0.1:8080${path}`, "--method", init?.method ?? "GET", "--max-response-bytes", String(maxResponseBytes)]; - if (init?.body !== undefined) command.push("--body-json", JSON.stringify(init.body)); + const command = dockerCoreFetchCommand(path, init); const result = runCommand(command, repoRoot); if (result.exitCode !== 0) { return { ok: false, exitCode: result.exitCode, stdoutTail: result.stdout.slice(-1200), stderrTail: result.stderr.slice(-1200) }; diff --git a/scripts/src/ssh.ts b/scripts/src/ssh.ts index c7e91697..df840f6f 100644 --- a/scripts/src/ssh.ts +++ b/scripts/src/ssh.ts @@ -661,8 +661,9 @@ export function wrapSshRemoteCommand(command: string | null): string { function brokerSource(): string { return String.raw` const open = JSON.parse(process.argv[2] || process.argv[1] || "{}"); -const token = process.env.PROVIDER_TOKEN || ""; -const url = "ws://backend-core:8080/ws/ssh?token=" + encodeURIComponent(token); +const token = process.env.PROVIDER_TOKEN || process.env.UNIDESK_PROVIDER_TOKEN || ""; +const baseUrl = process.env.UNIDESK_SSH_BROKER_URL || "ws://backend-core:8080/ws/ssh"; +const url = baseUrl + "?token=" + encodeURIComponent(token); const ws = new WebSocket(url); let exitCode = 255; let canSend = false; @@ -802,13 +803,25 @@ export async function runSsh(config: UniDeskConfig, providerId: string, args: st cols: size.cols, rows: size.rows, }; + const payloadJson = JSON.stringify(payload); + const encodedBrokerSource = Buffer.from(brokerSource(), "utf8").toString("base64"); + const script = [ + "set -euo pipefail", + `payload=${shellQuote(payloadJson)}`, + "if command -v backend-core >/dev/null 2>&1; then", + ' exec backend-core --ssh-broker "$payload"', + "fi", + `export UNIDESK_SSH_BROKER_URL=${shellQuote("ws://127.0.0.1:8080/ws/ssh")}`, + `printf %s ${shellQuote(encodedBrokerSource)} | base64 -d >/tmp/unidesk-ssh-broker.js`, + "exec bun /tmp/unidesk-ssh-broker.js \"$payload\"", + ].join("\n"); const child = spawn("docker", [ "exec", "-i", "unidesk-backend-core", - "backend-core", - "--ssh-broker", - JSON.stringify(payload), + "sh", + "-lc", + script, ], { cwd: repoRoot, stdio: ["pipe", "pipe", "pipe"],