Preserve managed Git launchers across legacy ACP startup
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
22d61006b9
commit
0b9314cf3e
|
|
@ -47,7 +47,9 @@ Sandbox runs use the operating-system user's home directory. Both legacy
|
|||
adapters and the native runner enter the same host-owned lifecycle before
|
||||
dispatch. Local execution keeps its existing workspace and home behavior.
|
||||
Legacy ACP proxies use a private host staging directory while agent sessions start
|
||||
in the sandbox home. For API-key Codex ACP runs, the adapter writes the explicit
|
||||
in the sandbox home. When managed Git launchers are present, the remote agent
|
||||
starts without another login-shell profile pass, which could replace the assigned
|
||||
PATH before the agent starts. For API-key Codex ACP runs, the adapter writes the explicit
|
||||
key to an owner-only login file in the staging copy; host credentials stay unchanged.
|
||||
Per-run GitHub launchers declare their own CommonJS package scope so warm runs
|
||||
inside ES-module repositories can still execute Git and GitHub CLI commands. Native runner launches carry the validated scoped paths
|
||||
|
|
|
|||
|
|
@ -2277,7 +2277,9 @@ async function buildRuntime(input: {
|
|||
runtimeRootDir,
|
||||
adapterKey: input.engine.adapterType,
|
||||
command: "sh",
|
||||
args: ["-lc", `exec ${agentCommandShell}`],
|
||||
// The host has already projected the managed Git PATH. A login shell
|
||||
// can replace it from /etc/profile before the agent even starts.
|
||||
args: [env.PAPERCLIP_GITHUB_LAUNCHER_DIR ? "-c" : "-lc", `exec ${agentCommandShell}`],
|
||||
cwd: sessionCwd,
|
||||
env: launchEnv,
|
||||
timeoutSec,
|
||||
|
|
|
|||
|
|
@ -305,6 +305,34 @@ describe("ACPX engine startup characterization", () => {
|
|||
expect(bridgeExecEnv?.PAPERCLIP_SANDBOX_EXEC_CHANNEL).toBe("bridge");
|
||||
expect(bridgeExecEnv?.PAPERCLIP_API_KEY).toBeUndefined();
|
||||
});
|
||||
|
||||
it("keeps the managed Git executable first when the remote agent command starts", async () => {
|
||||
const { root, stateDir, localCwd, executionTarget } = await setupRemoteSandbox();
|
||||
const launcherDir = path.join(root, "managed-github");
|
||||
await fs.mkdir(launcherDir);
|
||||
await fs.writeFile(path.join(launcherDir, "git"), "#!/bin/sh\nprintf managed-git", { mode: 0o700 });
|
||||
const launchPath = `${launcherDir}:${path.dirname(process.execPath)}:/usr/bin:/bin`;
|
||||
let launchPayload: { command: string; args: string[]; env: Record<string, string> } | undefined;
|
||||
executionTarget.runner = createLocalSandboxRunner((input) => {
|
||||
const match = input.args?.[1]?.match(/PAPERCLIP_PROCESS_SESSION_COMMAND_B64='([^']+)'/);
|
||||
if (match) launchPayload = JSON.parse(Buffer.from(match[1]!, "base64").toString("utf8"));
|
||||
});
|
||||
await runExecutor({
|
||||
agent: "custom", agentCommand: "git", stateDir, cwd: localCwd,
|
||||
env: { PATH: launchPath, PAPERCLIP_GITHUB_LAUNCHER_DIR: launcherDir },
|
||||
}, { authToken: "test-run-jwt", executionTarget });
|
||||
expect(launchPayload).toBeDefined();
|
||||
expect(launchPayload!.env.PATH).toBe(launchPath);
|
||||
// Execute the actual launch payload. Checking only its env would miss a
|
||||
// login shell subsequently replacing PATH with the image's defaults.
|
||||
let stdout = "";
|
||||
const result = await runChildProcess("managed-git-launch", launchPayload!.command, launchPayload!.args, {
|
||||
cwd: root, env: launchPayload!.env, timeoutSec: 5, graceSec: 1,
|
||||
onLog: async (stream, chunk) => { if (stream === "stdout") stdout += chunk; },
|
||||
});
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(stdout).toBe("managed-git");
|
||||
});
|
||||
});
|
||||
|
||||
// Item 2: the 17 fingerprint fields folded into `configFingerprint`, and the
|
||||
|
|
|
|||
Loading…
Reference in New Issue