perf(adapter-utils): batch remote GitHub launcher staging (#16552)

Stage the nine managed GitHub launcher files through one bounded control command. Preserve per-file locks, content verification, atomic replacement and control-channel dispatch.

Real Daytona staging decreased from about one second to 0.24–0.34 seconds. Full CI, E2E, Superagent and Apex 5/5 passed on the reviewed head.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip authored and GitHub committed 2026-10-16 04:59:21 -05:00
1 parent 8f1cb11a03
commit 8556539364
6 files changed
+282 -171

No files matched your search

+11
View File
@@ -1810,6 +1810,17 @@ versions across directories. With these artifacts preinstalled, startup links
and verifies them without uploading a binary or installing packages. Deploy
the updated sandbox image with the matching runner qualification changes.
### Remote GitHub launcher staging
Managed GitHub launchers stage their nine text files in one bounded control
command. Each file retains its content-hash check, process lock, upload check,
and atomic rename. The command runs outside the persistent agent session and
stops at the first failed file. Earlier files can remain after a later failure;
the launcher environment is returned only after the whole command succeeds.
The batch permits at most 16 files and 1 MiB of shell input. Launcher staging
keeps its 15-second deadline. Run `packages/adapter-utils/src/github-launcher-environment.test.ts`
and `packages/adapter-utils/src/sandbox-callback-bridge.test.ts` for these contracts.
### Native runner restart recovery
Custom source-mode HTTP launchers should create their listener with
+12 -16
View File
@@ -46,6 +46,7 @@ import {
startSandboxCallbackBridgeServer,
startSandboxCallbackBridgeWorker,
syncRemoteTextFileWithHashSkip,
syncRemoteTextFilesWithHashSkip,
syncSandboxCallbackBridgeEntrypoint,
} from "./sandbox-callback-bridge.js";
import {
@@ -1770,22 +1771,17 @@ export async function prepareGitHubOperationLaunchers(input: {
]);
if (remote) {
const runner = adapterExecutionTargetCommandRunner(remote);
const entries = Object.entries(files);
const width = runner.allowConcurrentSyncOperations === true ? 3 : 1;
for (let offset = 0; offset < entries.length; offset += width) {
const results = await Promise.allSettled(entries.slice(offset, offset + width).map(([program, body]) =>
syncRemoteTextFileWithHashSkip({
runner, remoteCwd: remote.remoteCwd, remoteDir: directory,
remotePath: path.posix.join(directory, program), body,
label: "GitHub operation launcher", action: "stage GitHub operation launcher",
lockDir: path.posix.join(directory, `.${program}.lock`),
timeoutMs: 15_000, shellCommand: adapterExecutionTargetShellCommand(remote),
})));
const failure = results.find((result) => result.status === "rejected");
if (failure?.status === "rejected") throw failure.reason;
}
const permissions = await runner.execute({ command: "sh", args: ["-c", `chmod 700 ${shellQuote(directory)}/git ${shellQuote(directory)}/gh && mkdir -p ${shellQuote(configDirectory)}`], cwd: remote.remoteCwd, timeoutMs: 15_000 });
if (permissions.exitCode !== 0) throw new Error("Could not prepare managed GitHub launchers");
await syncRemoteTextFilesWithHashSkip({
runner, remoteCwd: remote.remoteCwd,
files: Object.entries(files).map(([program, body]) => ({
remoteDir: directory, remotePath: path.posix.join(directory, program), body,
label: "GitHub operation launcher", action: "stage GitHub operation launcher",
lockDir: path.posix.join(directory, `.${program}.lock`),
executable: program === "git" || program === "gh",
})),
ensureDirectories: [configDirectory], action: "stage GitHub operation launchers",
timeoutMs: 15_000, shellCommand: adapterExecutionTargetShellCommand(remote),
});
} else {
await fs.mkdir(directory, { recursive: true, mode: 0o700 });
await fs.mkdir(configDirectory, { recursive: true, mode: 0o700 });
@@ -1,6 +1,6 @@
import { execFile } from "node:child_process";
import { watch } from "node:fs";
import { mkdtemp, mkdir, readFile, realpath, rm, writeFile } from "node:fs/promises";
import { mkdtemp, mkdir, readFile, readdir, realpath, rm, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { promisify } from "node:util";
@@ -8,6 +8,7 @@ import { afterEach, describe, expect, it, vi } from "vitest";
import * as ssh from "./ssh.js";
import type { CommandManagedRuntimeRunner } from "./command-managed-runtime.js";
import { githubBrokerEnvironment } from "./github-launcher.js";
import { syncRemoteTextFilesWithHashSkip } from "./sandbox-callback-bridge.js";
import {
ensureAdapterExecutionTargetCommandResolvable,
prepareGitHubOperationLaunchers,
@@ -68,7 +69,20 @@ async function sandbox(layout: string, concurrent?: boolean) {
}
describe("managed GitHub launcher environment", () => {
it("bounds independent remote launcher uploads and preserves the staged environment", async () => {
it("stages the complete remote launcher set in one control command", async () => {
const fixture = await sandbox("tools with 'quotes'/bin", true);
const env = await prepareGitHubOperationLaunchers({
runId: "single-control-stage", target: fixture.target, cwd: fixture.root,
env: githubBrokerEnvironment({ PATH: fixture.remotePath }, { url: "", token: "" }),
});
expect(fixture.runner.execute).toHaveBeenCalledTimes(1);
expect(fixture.runner.execute.mock.calls[0]![0].bypassSession).toBe(true);
const result = await fixture.runner.execute({ command: "sh", args: ["-c", "git; gh"], env });
expect(result.exitCode, result.stderr).toBe(0);
expect(result.stdout).toBe("git started\ngh started\n");
});
it("uses one bounded control command and preserves the staged environment", async () => {
const fixture = await sandbox("tools with 'quotes'/bin", true);
const execute = fixture.runner.execute.getMockImplementation()!;
let release!: () => void;
@@ -91,13 +105,13 @@ describe("managed GitHub launcher environment", () => {
env: githubBrokerEnvironment({ PATH: fixture.remotePath }, { url: "", token: "" }),
});
try {
await vi.waitFor(() => expect(started).toBe(3));
expect(active).toBe(3);
await vi.waitFor(() => expect(started).toBe(1));
expect(active).toBe(1);
} finally { release(); await preparation; }
const env = await preparation;
expect(maximum).toBe(3);
expect(maximum).toBe(1);
expect(active).toBe(0);
expect(started).toBe(9);
expect(started).toBe(1);
expect(env.PATH).toBe(`${env.PAPERCLIP_GITHUB_LAUNCHER_DIR}:${fixture.remotePath}`);
const command = await fixture.runner.execute({ command: "bash", args: ["--noprofile", "--norc", "-c", "git; gh"], env });
expect(command.exitCode, command.stderr).toBe(0);
@@ -112,21 +126,18 @@ describe("managed GitHub launcher environment", () => {
it("settles failed upload batches before rejecting and starting no later files", async () => {
const fixture = await sandbox("usr/bin", true);
const execute = fixture.runner.execute.getMockImplementation()!;
let release!: () => void;
const gate = new Promise<void>((resolve) => { release = resolve; });
let started = 0;
let active = 0;
let settled = false;
fixture.runner.execute.mockImplementation(async (input) => {
if (!input.stdin) return execute(input);
const index = started++;
started += 1;
active += 1;
try {
if (index === 0) return { exitCode: 42, signal: null, timedOut: false, pid: null,
startedAt: new Date().toISOString(), stdout: "", stderr: "fixture upload failed" };
await gate;
return await execute(input);
return { exitCode: 42, signal: null, timedOut: false, pid: null,
startedAt: new Date().toISOString(), stdout: "", stderr: "fixture upload failed" };
} finally { active -= 1; }
});
const preparation = prepareGitHubOperationLaunchers({
@@ -134,13 +145,13 @@ describe("managed GitHub launcher environment", () => {
});
const outcome = preparation.then(() => { settled = true; return null; }, (error: unknown) => { settled = true; return error; });
try {
await vi.waitFor(() => expect(started).toBe(3));
await vi.waitFor(() => expect(started).toBe(1));
expect(settled).toBe(false);
expect(active).toBe(2);
expect(active).toBe(1);
} finally { release(); await outcome; }
expect(await outcome).toBeInstanceOf(Error);
expect(String(await outcome)).toContain("stage GitHub operation launcher");
expect(started).toBe(3);
expect(started).toBe(1);
expect(active).toBe(0);
expect(fixture.runner.execute.mock.calls.some(([input]) => input.args?.join(" ").startsWith("chmod 700"))).toBe(false);
});
@@ -171,7 +182,59 @@ describe("managed GitHub launcher environment", () => {
cwd: fixture.root, env: { PATH: fixture.remotePath } });
expect(maximum).toBe(1);
expect(active).toBe(0);
expect(fixture.runner.execute).toHaveBeenCalledTimes(10);
expect(fixture.runner.execute).toHaveBeenCalledTimes(1);
});
it("rejects corrupt batch input before replacing that file or staging later files", async () => {
const fixture = await sandbox("usr/bin");
const execute = fixture.runner.execute.getMockImplementation()!;
const directory = path.join(fixture.root, "batch");
const files = ["first", "corrupt", "later"].map(name => ({
remoteDir: directory, remotePath: path.join(directory, name), body: name + "\n",
label: name, action: "stage fixture", lockDir: path.join(directory, "." + name + ".lock"),
}));
fixture.runner.execute.mockImplementation(input => execute({ ...input,
stdin: input.stdin?.replace(Buffer.from("corrupt\n").toString("base64"), Buffer.from("invalid\n").toString("base64")),
}));
await expect(syncRemoteTextFilesWithHashSkip({ runner: fixture.runner, remoteCwd: fixture.root,
files, action: "stage fixtures" })).rejects.toThrow("sha mismatch");
expect(await readFile(path.join(directory, "first"), "utf8")).toBe("first\n");
expect(await readdir(directory)).toEqual(["first"]);
});
it("hash-skips a complete batch and repairs a missing executable bit", async () => {
const fixture = await sandbox("usr/bin");
const directory = path.join(fixture.root, "batch 'quoted'");
const files = [{ remoteDir: directory, remotePath: path.join(directory, "entry"), body: "#!/bin/sh\nprintf done\n",
label: "entry", action: "stage fixture", lockDir: path.join(directory, ".entry.lock"), executable: true }];
const input = { runner: fixture.runner, remoteCwd: fixture.root, files, action: "stage fixtures" };
expect((await syncRemoteTextFilesWithHashSkip(input))[0]?.uploaded).toBe(true);
const firstHash = (await syncRemoteTextFilesWithHashSkip(input))[0]?.sha256;
await fixture.runner.execute({ command: "chmod", args: ["600", files[0]!.remotePath] });
const restored = await syncRemoteTextFilesWithHashSkip(input);
expect(restored).toEqual([{ uploaded: false, sha256: firstHash }]);
const result = await fixture.runner.execute({ command: files[0]!.remotePath });
expect(result.exitCode, result.stderr).toBe(0);
expect(result.stdout).toBe("done");
expect(await readdir(directory)).toEqual(["entry"]);
});
it.each([0, 17])("rejects an invalid batch size before remote work (%s)", async count => {
const fixture = await sandbox("usr/bin");
const file = { remoteDir: fixture.root, remotePath: path.join(fixture.root, "entry"), body: "entry",
label: "entry", action: "stage fixture", lockDir: path.join(fixture.root, ".entry.lock") };
await expect(syncRemoteTextFilesWithHashSkip({ runner: fixture.runner, remoteCwd: fixture.root,
files: Array.from({ length: count }, () => file), action: "stage fixtures" })).rejects.toThrow("1 to 16 files");
expect(fixture.runner.execute).not.toHaveBeenCalled();
});
it("rejects an oversized batch before remote work", async () => {
const fixture = await sandbox("usr/bin");
await expect(syncRemoteTextFilesWithHashSkip({ runner: fixture.runner, remoteCwd: fixture.root,
files: [{ remoteDir: fixture.root, remotePath: path.join(fixture.root, "entry"), body: "x".repeat(1024 * 1024),
label: "entry", action: "stage fixture", lockDir: path.join(fixture.root, ".entry.lock") }],
action: "stage fixtures" })).rejects.toThrow("1 MiB");
expect(fixture.runner.execute).not.toHaveBeenCalled();
});
// The Daytona native directory oracle uses Linux inotify. Run its real
@@ -0,0 +1,36 @@
function shellQuote(value: string) {
return `'${value.replace(/'/g, `'"'"'`)}'`;
}
export function buildRemotePidLockAcquireScript(lockDirExpr: string, timeoutMessage: string): string[] {
return [
"attempts=0",
`while ! mkdir ${lockDirExpr} 2>/dev/null; do`,
" holder_pid=\"\"",
` if [ -s ${lockDirExpr}/pid ]; then`,
` holder_pid="$(cat ${lockDirExpr}/pid 2>/dev/null || true)"`,
" fi",
" if [ -n \"$holder_pid\" ] && ! kill -0 \"$holder_pid\" 2>/dev/null; then",
` rm -rf ${lockDirExpr}`,
" continue",
" fi",
" attempts=$((attempts + 1))",
" if [ \"$attempts\" -ge 600 ]; then",
` echo ${shellQuote(timeoutMessage)} >&2`,
" exit 1",
" fi",
" sleep 0.05",
"done",
`printf '%s\\n' "$$" > ${lockDirExpr}/pid`,
];
}
export function buildRemotePidLockCleanupScript(lockDirExpr: string, cleanupLines: string[]): string[] {
return [
"cleanup() {",
...cleanupLines.map((line) => ` ${line}`),
` rm -rf ${lockDirExpr}`,
"}",
"trap cleanup EXIT INT TERM",
];
}
@@ -0,0 +1,137 @@
import { createHash, randomUUID } from "node:crypto";
import type { CommandManagedRuntimeRunner } from "./command-managed-runtime.js";
import type { RunProcessResult } from "./server-utils.js";
import { preferredShellForSandbox } from "./sandbox-shell.js";
import { buildRemotePidLockAcquireScript, buildRemotePidLockCleanupScript } from "./remote-pid-lock.js";
export interface RemoteTextFileSyncFile {
remoteDir: string;
remotePath: string;
body: string;
label: string;
action: string;
lockDir: string;
executable?: boolean;
}
export interface RemoteTextFileSyncInput extends RemoteTextFileSyncFile {
runner: CommandManagedRuntimeRunner;
remoteCwd: string;
timeoutMs?: number | null;
shellCommand?: "bash" | "sh" | null;
}
export interface RemoteTextFileBatchInput {
runner: CommandManagedRuntimeRunner;
remoteCwd: string;
files: RemoteTextFileSyncFile[];
ensureDirectories?: string[];
action: string;
timeoutMs?: number | null;
shellCommand?: "bash" | "sh" | null;
}
interface RemoteTextSyncExecution {
runShell(runner: CommandManagedRuntimeRunner, cwd: string, script: string, timeoutMs: number,
shellCommand: "bash" | "sh", stdin?: string): Promise<RunProcessResult>;
check(action: string, result: RunProcessResult): RunProcessResult;
}
function shellQuote(value: string) {
return `'${value.replace(/'/g, `'"'"'`)}'`;
}
function timeout(value: number | null | undefined) {
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : 30_000;
}
function program(input: RemoteTextFileSyncFile) {
const remotePartial = `${input.remotePath}.partial`;
const remoteUploadPath = `${input.remotePath}.paperclip-upload.b64`;
const base64Body = Buffer.from(input.body, "utf8").toString("base64");
const sha256 = createHash("sha256").update(input.body, "utf8").digest("hex");
const script = [
"set -eu",
`remote_dir=${shellQuote(input.remoteDir)}`,
`remote_path=${shellQuote(input.remotePath)}`,
`remote_partial=${shellQuote(remotePartial)}`,
`remote_upload=${shellQuote(remoteUploadPath)}`,
`lock_dir=${shellQuote(input.lockDir)}`,
`expected_sha=${shellQuote(sha256)}`,
"hash_file() {",
" if command -v sha256sum >/dev/null 2>&1; then",
" sha256sum \"$1\" | awk '{print $1}'",
" return 0",
" fi",
" if command -v shasum >/dev/null 2>&1; then",
" shasum -a 256 \"$1\" | awk '{print $1}'",
" return 0",
" fi",
" return 127",
"}",
"mkdir -p \"$remote_dir\"",
...buildRemotePidLockAcquireScript("\"$lock_dir\"", `Timed out acquiring ${input.label} upload lock.`),
...buildRemotePidLockCleanupScript("\"$lock_dir\"", [
"rm -f \"$remote_upload\" \"$remote_partial\"",
]),
"current_sha=\"\"",
"if [ -f \"$remote_path\" ]; then",
" current_sha=\"$(hash_file \"$remote_path\" 2>/dev/null)\" || current_sha=\"\"",
"fi",
"if [ -n \"$current_sha\" ] && [ \"$current_sha\" = \"$expected_sha\" ]; then",
...(input.executable ? [' chmod 700 "$remote_path"'] : []),
" printf '{\"uploaded\":false}\\n'",
" exit 0",
"fi",
"rm -f \"$remote_upload\" \"$remote_partial\"",
"cat > \"$remote_upload\"",
"base64 -d < \"$remote_upload\" > \"$remote_partial\"",
"if partial_sha=\"$(hash_file \"$remote_partial\" 2>/dev/null)\"; then",
" if [ \"$partial_sha\" != \"$expected_sha\" ]; then",
` echo ${shellQuote(`${input.label} upload sha mismatch.`)} >&2`,
" exit 1",
" fi",
"else",
` echo ${shellQuote(`${input.label} sha verify skipped: no sha256sum/shasum on remote.`)} >&2`,
"fi",
...(input.executable ? ['chmod 700 "$remote_partial"'] : []),
"mv \"$remote_partial\" \"$remote_path\"",
"printf '{\"uploaded\":true}\\n'",
].join("\n");
return { script, base64Body, sha256 };
}
export async function syncRemoteTextFile(input: RemoteTextFileSyncInput, execution: RemoteTextSyncExecution) {
const prepared = program(input);
const result = await execution.runShell(input.runner, input.remoteCwd, prepared.script, timeout(input.timeoutMs),
preferredShellForSandbox(input.shellCommand), prepared.base64Body);
execution.check(input.action, result);
let uploaded = false;
try { uploaded = JSON.parse(result.stdout.trim())?.uploaded === true; }
catch (error) {
throw new Error(`${input.label} sync wrote invalid result JSON: ${error instanceof Error ? error.message : String(error)}`);
}
return { uploaded, sha256: prepared.sha256 };
}
export async function syncRemoteTextFiles(input: RemoteTextFileBatchInput, execution: RemoteTextSyncExecution) {
if (input.files.length === 0 || input.files.length > 16) throw new Error("Remote text sync requires 1 to 16 files");
const shell = preferredShellForSandbox(input.shellCommand);
const programs = input.files.map(program);
const script = ["set -eu", ...programs.map((file, index) => {
const delimiter = `PAPERCLIP_TEXT_${index}_${randomUUID().replaceAll("-", "")}`;
return `${shellQuote(shell)} -c ${shellQuote(file.script)} <<'${delimiter}'\n${file.base64Body}\n${delimiter}`;
}), ...(input.ensureDirectories?.length ? [`mkdir -p -- ${input.ensureDirectories.map(shellQuote).join(" ")}`] : [])].join("\n") + "\n";
if (Buffer.byteLength(script, "utf8") > 1024 * 1024) throw new Error("Remote text sync batch exceeds 1 MiB");
const result = await execution.runShell(input.runner, input.remoteCwd, `exec ${shell} -s`, timeout(input.timeoutMs), shell, script);
execution.check(input.action, result);
try {
const results: unknown[] = result.stdout.trim().split("\n").map(line => JSON.parse(line));
if (results.length !== programs.length || results.some(row => !row || typeof row !== "object" || typeof (row as { uploaded?: unknown }).uploaded !== "boolean")) {
throw new Error("Invalid file result count or upload state");
}
return programs.map((file, index) => ({ uploaded: (results[index] as { uploaded: boolean }).uploaded, sha256: file.sha256 }));
} catch (error) {
throw new Error(`Remote text sync batch wrote invalid result JSON: ${error instanceof Error ? error.message : String(error)}`);
}
}
@@ -1,3 +1,5 @@
import { buildRemotePidLockAcquireScript, buildRemotePidLockCleanupScript } from "./remote-pid-lock.js";
import { syncRemoteTextFile, syncRemoteTextFiles, type RemoteTextFileSyncInput, type RemoteTextFileBatchInput } from "./remote-text-file-sync.js";
import { createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
import { promises as fs } from "node:fs";
import http2 from "node:http2";
@@ -414,39 +416,6 @@ async function pathExists(filePath: string): Promise<boolean> {
return await fs.stat(filePath).then(() => true).catch(() => false);
}
function buildRemotePidLockAcquireScript(lockDirExpr: string, timeoutMessage: string): string[] {
return [
"attempts=0",
`while ! mkdir ${lockDirExpr} 2>/dev/null; do`,
" holder_pid=\"\"",
` if [ -s ${lockDirExpr}/pid ]; then`,
` holder_pid="$(cat ${lockDirExpr}/pid 2>/dev/null || true)"`,
" fi",
" if [ -n \"$holder_pid\" ] && ! kill -0 \"$holder_pid\" 2>/dev/null; then",
` rm -rf ${lockDirExpr}`,
" continue",
" fi",
" attempts=$((attempts + 1))",
" if [ \"$attempts\" -ge 600 ]; then",
` echo ${shellQuote(timeoutMessage)} >&2`,
" exit 1",
" fi",
" sleep 0.05",
"done",
`printf '%s\\n' "$$" > ${lockDirExpr}/pid`,
];
}
function buildRemotePidLockCleanupScript(lockDirExpr: string, cleanupLines: string[]): string[] {
return [
"cleanup() {",
...cleanupLines.map((line) => ` ${line}`),
` rm -rf ${lockDirExpr}`,
"}",
"trap cleanup EXIT INT TERM",
];
}
export function createSandboxCallbackBridgeToken(bytes = DEFAULT_BRIDGE_TOKEN_BYTES): string {
return randomBytes(bytes).toString("base64url");
}
@@ -1671,113 +1640,12 @@ export async function startSandboxCallbackBridgeWorker(input: {
};
}
/**
* Content-hash-skip write of a Paperclip-authored text file into the sandbox, in
* a SINGLE remote exec. The body's sha256 is computed on the host; the one shell
* round-trip skips the write entirely when the remote file already hashes to the
* same value (warm start — 0 write execs), otherwise it uploads (base64 over
* stdin), verifies the decoded bytes, and atomically renames into place. A
* PID-liveness lock serializes concurrent writers to the same path and the
* verify step guards against a torn upload.
*
* Fail loudly: a non-zero remote exit (surfaced by `requireSuccessfulResult`) or
* malformed result JSON throws rather than silently re-uploading and masking a
* failed check. The only intentional degradation is when the remote has neither
* `sha256sum` nor `shasum` — then the skip cannot be proven and we conservatively
* re-upload (and the post-upload verify is best-effort, as noted inline).
*/
export async function syncRemoteTextFileWithHashSkip(input: {
runner: CommandManagedRuntimeRunner;
remoteCwd: string;
remoteDir: string;
remotePath: string;
body: string;
// Human-readable noun phrase used in fail-loud messages, e.g.
// "Sandbox callback bridge entrypoint" / "Process session remote script".
label: string;
// Short action label for `requireSuccessfulResult`, e.g.
// "sync sandbox callback bridge entrypoint".
action: string;
lockDir: string;
timeoutMs?: number | null;
shellCommand?: "bash" | "sh" | null;
}): Promise<{ uploaded: boolean; sha256: string }> {
const timeoutMs = normalizeTimeoutMs(input.timeoutMs, DEFAULT_BRIDGE_RESPONSE_TIMEOUT_MS);
const shellCommand = preferredShellForSandbox(input.shellCommand);
const remotePartial = `${input.remotePath}.partial`;
const remoteUploadPath = `${input.remotePath}.paperclip-upload.b64`;
const base64Body = toBuffer(Buffer.from(input.body, "utf8")).toString("base64");
const sha256 = createHash("sha256").update(input.body, "utf8").digest("hex");
export async function syncRemoteTextFileWithHashSkip(input: RemoteTextFileSyncInput) {
return syncRemoteTextFile(input, { runShell, check: requireSuccessfulResult });
}
const syncResult = await runShell(
input.runner,
input.remoteCwd,
[
"set -eu",
`remote_dir=${shellQuote(input.remoteDir)}`,
`remote_path=${shellQuote(input.remotePath)}`,
`remote_partial=${shellQuote(remotePartial)}`,
`remote_upload=${shellQuote(remoteUploadPath)}`,
`lock_dir=${shellQuote(input.lockDir)}`,
`expected_sha=${shellQuote(sha256)}`,
"hash_file() {",
" if command -v sha256sum >/dev/null 2>&1; then",
" sha256sum \"$1\" | awk '{print $1}'",
" return 0",
" fi",
" if command -v shasum >/dev/null 2>&1; then",
" shasum -a 256 \"$1\" | awk '{print $1}'",
" return 0",
" fi",
" return 127",
"}",
"mkdir -p \"$remote_dir\"",
...buildRemotePidLockAcquireScript("\"$lock_dir\"", `Timed out acquiring ${input.label} upload lock.`),
...buildRemotePidLockCleanupScript("\"$lock_dir\"", [
"rm -f \"$remote_upload\" \"$remote_partial\"",
]),
"current_sha=\"\"",
"if [ -f \"$remote_path\" ]; then",
" current_sha=\"$(hash_file \"$remote_path\" 2>/dev/null)\" || current_sha=\"\"",
"fi",
"if [ -n \"$current_sha\" ] && [ \"$current_sha\" = \"$expected_sha\" ]; then",
" printf '{\"uploaded\":false}\\n'",
" exit 0",
"fi",
"rm -f \"$remote_upload\" \"$remote_partial\"",
"cat > \"$remote_upload\"",
"base64 -d < \"$remote_upload\" > \"$remote_partial\"",
// Verify upload integrity. If neither sha256sum nor shasum is on PATH
// (minimal Alpine/scratch images), surface the missing-tool error
// instead of a misleading "sha mismatch" — the verify step is then
// best-effort and we trust base64-decode + atomic rename below.
"if partial_sha=\"$(hash_file \"$remote_partial\" 2>/dev/null)\"; then",
" if [ \"$partial_sha\" != \"$expected_sha\" ]; then",
` echo ${shellQuote(`${input.label} upload sha mismatch.`)} >&2`,
" exit 1",
" fi",
"else",
` echo ${shellQuote(`${input.label} sha verify skipped: no sha256sum/shasum on remote.`)} >&2`,
"fi",
"mv \"$remote_partial\" \"$remote_path\"",
"printf '{\"uploaded\":true}\\n'",
].join("\n"),
timeoutMs,
shellCommand,
base64Body,
);
requireSuccessfulResult(input.action, syncResult);
let uploaded = false;
try {
uploaded = JSON.parse(syncResult.stdout.trim())?.uploaded === true;
} catch (error) {
throw new Error(
`${input.label} sync wrote invalid result JSON: ${error instanceof Error ? error.message : String(error)}`,
);
}
return { uploaded, sha256 };
export async function syncRemoteTextFilesWithHashSkip(input: RemoteTextFileBatchInput) {
return syncRemoteTextFiles(input, { runShell, check: requireSuccessfulResult });
}
export async function syncSandboxCallbackBridgeEntrypoint(input: {