mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-17 01:25:26 +02:00
fix: preserve saved setup and match sandbox runner targets
Share actual-target artifact selection across remote Codex setup and execution. Keep unchanged saved setup choices outside new-selection rules while retaining runtime and authentication validation. Update the served platform guide. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
665c155472
commit
e79254b2fa
10 files changed
+182
-30
No files matched your search
@@ -657,6 +657,78 @@ describe("agent routes adapter validation", () => {
|
||||
expect(res.status, JSON.stringify(res.body)).toBe(200);
|
||||
});
|
||||
|
||||
it.each(["pass", "fail"] as const)("tests saved legacy Codex on a disabled adapter and retains its %s runtime verdict", async (status) => {
|
||||
const adapterConfig = { model: "gpt-5.4", extraArgs: ["--legacy-setting"], env: { CODEX_HOME: "/saved-codex-home" } };
|
||||
mockAgentService.getById.mockResolvedValue({ ...(await mockAgentService.getById()), adapterConfig });
|
||||
mockAdapterPluginStore.getDisabledAdapterTypes.mockReturnValue(["codex_local"]);
|
||||
const probe = vi.fn(async () => ({ adapterType: "codex_local", status, testedAt: new Date(0).toISOString(),
|
||||
checks: status === "fail" ? [{ code: "adapter_auth_missing", level: "error" as const, message: "Sign in to the saved Codex account." }] : [] }));
|
||||
const { registerServerAdapter } = await import("../adapters/index.js");
|
||||
registerServerAdapter({ ...externalAdapter, type: "codex_local", testEnvironment: probe });
|
||||
try {
|
||||
const app = await createApp();
|
||||
const res = await requestApp(app, baseUrl => request(baseUrl)
|
||||
.post("/api/companies/company-1/adapters/codex_local/test-environment")
|
||||
.send({ agentId: "11111111-1111-4111-8111-111111111111", adapterConfig: {
|
||||
...adapterConfig, env: { CODEX_HOME: { type: "plain", value: "***REDACTED***" } },
|
||||
} }));
|
||||
expect(res.status, JSON.stringify(res.body)).toBe(200);
|
||||
expect(res.body).toMatchObject({ adapterType: "codex_local", status });
|
||||
expect(probe).toHaveBeenCalledOnce();
|
||||
expect(probe).toHaveBeenCalledWith(expect.objectContaining({ adapterType: "codex_local", config: adapterConfig }));
|
||||
expect(mockSecretService.normalizeAdapterConfigForPersistence).toHaveBeenCalledWith("company-1", adapterConfig, expect.objectContaining({ adapterType: "codex_local" }));
|
||||
expect(mockAgentService.create).not.toHaveBeenCalled();
|
||||
expect(mockAgentService.update).not.toHaveBeenCalled();
|
||||
} finally { await unregisterTestAdapter("codex_local"); }
|
||||
});
|
||||
|
||||
it.each(["draft", "explicit legacy", "explicit auto", "explicit native", "changed harness"] as const)("rejects a disabled Codex setup selection for %s", async (mode) => {
|
||||
mockAdapterPluginStore.getDisabledAdapterTypes.mockReturnValue(["codex_local"]);
|
||||
if (mode === "changed harness") mockAgentService.getById.mockResolvedValue({ ...(await mockAgentService.getById()), adapterType: "external_test" });
|
||||
const probe = vi.fn();
|
||||
const { registerServerAdapter } = await import("../adapters/index.js");
|
||||
registerServerAdapter({ ...externalAdapter, type: "codex_local", testEnvironment: probe });
|
||||
try {
|
||||
const runner = mode === "explicit legacy" ? "legacy" : mode === "explicit auto" ? "auto" : mode === "explicit native" ? "paperclip" : undefined;
|
||||
const app = await createApp();
|
||||
const res = await requestApp(app, baseUrl => request(baseUrl)
|
||||
.post("/api/companies/company-1/adapters/codex_local/test-environment")
|
||||
.send({ adapterConfig: {}, ...(mode === "draft" ? {} : { agentId: "11111111-1111-4111-8111-111111111111" }), ...(runner ? { runner } : {}) }));
|
||||
expect(res.status, JSON.stringify(res.body)).toBe(422);
|
||||
expect(res.body.details).toMatchObject({ code: "agent_runner_unavailable" });
|
||||
expect(probe).not.toHaveBeenCalled();
|
||||
expect(mockSecretService.normalizeAdapterConfigForPersistence).not.toHaveBeenCalled();
|
||||
} finally { await unregisterTestAdapter("codex_local"); }
|
||||
});
|
||||
|
||||
it("still rejects new Codex creation when its adapter is disabled", async () => {
|
||||
mockAdapterPluginStore.getDisabledAdapterTypes.mockReturnValue(["codex_local"]);
|
||||
const app = await createApp();
|
||||
const res = await requestApp(app, baseUrl => request(baseUrl)
|
||||
.post("/api/companies/company-1/agents").send({ name: "New Codex", adapterType: "codex_local" }));
|
||||
expect(res.status, JSON.stringify(res.body)).toBe(422);
|
||||
expect(res.body.details).toMatchObject({ code: "agent_runner_unavailable" });
|
||||
expect(mockAgentService.create).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("rechecks selection policy when a saved native adapter switches its underlying harness", async () => {
|
||||
mockAgentService.getById.mockResolvedValue({ ...(await mockAgentService.getById()), adapterType: "paperclip_runner", adapterConfig: { provider: "codex" } });
|
||||
mockInstanceSettingsService.getExperimental.mockResolvedValue({ enableNativeRunner: false });
|
||||
const probe = vi.fn();
|
||||
const { registerServerAdapter } = await import("../adapters/index.js");
|
||||
registerServerAdapter({ ...externalAdapter, type: "paperclip_runner", testEnvironment: probe });
|
||||
try {
|
||||
const app = await createApp();
|
||||
const res = await requestApp(app, baseUrl => request(baseUrl)
|
||||
.post("/api/companies/company-1/adapters/paperclip_runner/test-environment")
|
||||
.send({ agentId: "11111111-1111-4111-8111-111111111111", adapterConfig: { provider: "acpx", acpxAgent: "claude" } }));
|
||||
expect(res.status, JSON.stringify(res.body)).toBe(422);
|
||||
expect(res.body.details).toMatchObject({ code: "paperclip_runner_rollout_disabled" });
|
||||
expect(probe).not.toHaveBeenCalled();
|
||||
expect(mockSecretService.normalizeAdapterConfigForPersistence).not.toHaveBeenCalled();
|
||||
} finally { await unregisterTestAdapter("paperclip_runner"); }
|
||||
});
|
||||
|
||||
it("still creates an agent on an adapter that is registered and enabled", async () => {
|
||||
const { registerServerAdapter } = await import("../adapters/index.js");
|
||||
registerServerAdapter(externalAdapter);
|
||||
|
||||
@@ -3142,15 +3142,21 @@ export function agentRoutes(
|
||||
? restoreRedactedAgentEnv(inputAdapterConfig, savedAgent.adapterConfig)
|
||||
: inputAdapterConfig;
|
||||
}
|
||||
const selection = await resolveNewAgentRunnerForCompany(db, companyId, {
|
||||
adapterType: type, adapterConfig: adapterConfigForTest,
|
||||
runner: req.body.runner ?? (savedAgent && savedAgent.adapterType === type ? (type === "paperclip_runner" ? undefined : "legacy") : undefined),
|
||||
defaultEnvironmentId: requestedEnvironmentId,
|
||||
});
|
||||
const retainsSavedExecution = savedAgent && req.body.runner === undefined
|
||||
&& savedAgent.adapterType === type
|
||||
&& agentHarnessType(type, adapterConfigForTest) === agentHarnessType(savedAgent.adapterType, savedAgent.adapterConfig);
|
||||
// Preserve saved execution; the adapter probe below still validates runtime readiness.
|
||||
const selection = retainsSavedExecution
|
||||
? { adapterType: type, adapterConfig: adapterConfigForTest }
|
||||
: await resolveNewAgentRunnerForCompany(db, companyId, {
|
||||
adapterType: type, adapterConfig: adapterConfigForTest,
|
||||
runner: req.body.runner,
|
||||
defaultEnvironmentId: requestedEnvironmentId,
|
||||
});
|
||||
type = selection.adapterType;
|
||||
adapterConfigForTest = selection.adapterConfig;
|
||||
const adapter = requireServerAdapter(type);
|
||||
if (!savedAgent || req.body.runner !== undefined || type !== savedAgent.adapterType) await assertSelectableAdapterType(type, adapterConfigForTest);
|
||||
if (!retainsSavedExecution) await assertSelectableAdapterType(type, adapterConfigForTest);
|
||||
if (poolBinding?.mode === "router") {
|
||||
await validateManagedAgentBinding(req, companyId, savedAgentId ?? "", type, adapterConfigForTest, poolBinding, requestedEnvironmentId, false, !savedAgentId);
|
||||
res.json({ adapterType: type, status: "warn", testedAt: new Date().toISOString(), checks: [{ code: "ai_connection_pool_task_test_required", level: "warn", message: "Pool configuration is available. Run a task to verify the selected account and harness; this preview does not allocate a task or contact a provider." }] });
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
resolveRemotePaperclipRunnerBinary,
|
||||
} from "./native-codex-runner.js";
|
||||
|
||||
afterEach(() => vi.restoreAllMocks());
|
||||
afterEach(() => { vi.restoreAllMocks(); vi.unstubAllGlobals(); });
|
||||
|
||||
describe("installed runner selection", () => {
|
||||
it("uses the runner's platform-aware packaged selection", async () => {
|
||||
@@ -46,7 +46,7 @@ describe("installed runner selection", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("selected SSH daemon target", () => {
|
||||
describe("selected remote daemon target", () => {
|
||||
function header(target: string) {
|
||||
const bytes = Buffer.alloc(64);
|
||||
if (target.startsWith("darwin")) {
|
||||
@@ -72,6 +72,21 @@ describe("selected SSH daemon target", () => {
|
||||
expect(target.execute).toHaveBeenCalledExactlyOnceWith({ command: "sh", args: ["-c", "uname -s; uname -m"], cwd: "/selected-workspace", bypassSession: true, timeoutMs: 10_000 });
|
||||
} finally { await rm(root, { recursive: true, force: true }); }
|
||||
});
|
||||
it.each(["arm64", "x64"].flatMap(controllerArch => ["arm64", "x64"].map(targetArch => ({ controllerArch, targetArch }))))(
|
||||
"selects the actual Linux $targetArch sandbox bytes on a Mac $controllerArch controller", async ({ controllerArch, targetArch }) => {
|
||||
const root = await mkdtemp(join(tmpdir(), "paperclip-sandbox-daemon-"));
|
||||
const binary = join(root, "paperclip-runnerd");
|
||||
try {
|
||||
vi.stubGlobal("process", { ...process, platform: "darwin", arch: controllerArch });
|
||||
await writeFile(binary, header(`linux-${targetArch}`));
|
||||
const select = vi.spyOn(runner, "defaultCapabilityRunnerdBinary").mockReturnValue(binary);
|
||||
const target = remote(`Linux\n${targetArch === "arm64" ? "aarch64" : "x86_64"}\n`);
|
||||
expect(await resolveRemotePaperclipRunnerBinary({ runner: target, remoteCwd: "/sandbox-workspace" })).toBe(binary);
|
||||
expect(select).toHaveBeenCalledExactlyOnceWith("linux", targetArch);
|
||||
expect(target.execute).toHaveBeenCalledExactlyOnceWith({ command: "sh", args: ["-c", "uname -s; uname -m"], cwd: "/sandbox-workspace", bypassSession: true, timeoutMs: 10_000 });
|
||||
} finally { await rm(root, { recursive: true, force: true }); }
|
||||
},
|
||||
);
|
||||
it.each(["matching", "wrong-architecture", "malformed", "missing"])("verifies an explicit controller-owned remote artifact (%s)", async scenario => {
|
||||
const root = await mkdtemp(join(tmpdir(), "paperclip-ssh-daemon-"));
|
||||
const binary = join(root, "configured-runnerd");
|
||||
|
||||
@@ -208,7 +208,7 @@ export function resolvePaperclipRunnerBinary(
|
||||
);
|
||||
}
|
||||
|
||||
/** Select controller-owned bytes for the selected SSH host, never its controller. */
|
||||
/** Select controller-owned bytes for the actual remote host, never its controller. */
|
||||
export async function resolveRemotePaperclipRunnerBinary(input: {
|
||||
runner: CommandManagedRuntimeRunner;
|
||||
remoteCwd: string;
|
||||
@@ -220,7 +220,7 @@ export async function resolveRemotePaperclipRunnerBinary(input: {
|
||||
const platform = os === "Linux" ? "linux" : os === "Darwin" ? "darwin" : null;
|
||||
const architecture = arch === "x86_64" ? "x64" : arch === "aarch64" || arch === "arm64" ? "arm64" : null;
|
||||
if (probe.exitCode !== 0 || probe.timedOut || !platform || !architecture) {
|
||||
throw new Error("runner_remote_artifact_platform_mismatch: could not identify a supported remote OS and architecture; check the SSH host and retry");
|
||||
throw new Error("runner_remote_artifact_platform_mismatch: could not identify a supported remote OS and architecture; check the selected execution environment and retry");
|
||||
}
|
||||
const configured = input.configuredPath?.trim();
|
||||
if (configured && !isAbsolute(configured)) throw new Error("PAPERCLIP_RUNNER_REMOTE_BINARY_PATH must be an absolute path");
|
||||
|
||||
@@ -12218,10 +12218,8 @@ describe("runnerd provider runtime wiring", () => {
|
||||
// The mocked remote executes metadata probes; artifact staging only needs bytes.
|
||||
// Keep this regression independent of a locally compiled Rust runner binary.
|
||||
const controllerArtifact = join(isolatedStateDirectory, "paperclip-runnerd");
|
||||
if (needsReplacement || retained || image === "preinstalled-exact") {
|
||||
await writeFile(controllerArtifact, "fixture runner artifact");
|
||||
state.resolveRunnerBinary.mockReturnValueOnce(controllerArtifact);
|
||||
}
|
||||
await writeFile(controllerArtifact, "fixture runner artifact");
|
||||
state.resolveRemoteRunnerBinary.mockResolvedValue(controllerArtifact);
|
||||
const onLog = vi.fn(async () => undefined);
|
||||
const syncIn = vi.fn(async () => undefined);
|
||||
const remoteExecute = vi.fn(
|
||||
@@ -12430,21 +12428,27 @@ describe("runnerd provider runtime wiring", () => {
|
||||
|
||||
});
|
||||
|
||||
it.each(["explicit", "linux-arm64", "linux-x64"])("binds an SSH launch to the selected controller-owned runner artifact (%s)", async mode => {
|
||||
it.each(["ssh", "sandbox"].flatMap(transport => ["explicit", "linux-arm64", "linux-x64", "missing", "mismatch"].map(mode => ({ transport, mode }))))
|
||||
("binds a $transport launch to the selected controller-owned runner artifact ($mode)", async ({ transport, mode }) => {
|
||||
const remoteCwd = "/home/daytona/paperclip-workspace";
|
||||
const controllerArtifact = `/controller/artifacts/${mode}/paperclip-runnerd`;
|
||||
state.resolveRemoteRunnerBinary.mockResolvedValue(controllerArtifact);
|
||||
const explicit = mode === "explicit" || mode === "missing" || mode === "mismatch";
|
||||
const error = mode === "missing" ? "runner_remote_artifact_unavailable" : mode === "mismatch" ? "runner_remote_artifact_platform_mismatch" : null;
|
||||
if (error) state.resolveRemoteRunnerBinary.mockRejectedValue(new Error(error));
|
||||
else state.resolveRemoteRunnerBinary.mockResolvedValue(controllerArtifact);
|
||||
const remoteExecution = {
|
||||
...execution,
|
||||
binding: { ...execution.binding, runId: "run-remote-runner-artifact" },
|
||||
workspace: { ...execution.workspace, cwd: "/host/paperclip-workspace" },
|
||||
} as NativeExecutionInputV1;
|
||||
|
||||
await createRunnerdBackend({
|
||||
const backend = createRunnerdBackend({
|
||||
db: leaseDb(remoteExecution),
|
||||
execution: remoteExecution,
|
||||
runnerInstanceId: "runner",
|
||||
runnerExecutionTarget: {
|
||||
runnerExecutionTarget: transport === "sandbox" ? {
|
||||
kind: "remote", transport: "sandbox", providerKey: "test", remoteCwd, runner: { execute: vi.fn() },
|
||||
} : {
|
||||
kind: "remote",
|
||||
transport: "ssh",
|
||||
remoteCwd,
|
||||
@@ -12459,15 +12463,25 @@ describe("runnerd provider runtime wiring", () => {
|
||||
strictHostKeyChecking: true,
|
||||
},
|
||||
},
|
||||
...(mode === "explicit" ? { runnerRemoteBinaryPath: controllerArtifact } : {}),
|
||||
...(explicit ? { runnerRemoteBinaryPath: controllerArtifact } : {}),
|
||||
});
|
||||
|
||||
if (error) {
|
||||
await expect(backend).rejects.toThrow(error);
|
||||
expect(state.resolveRemoteRunnerBinary).toHaveBeenCalledWith(expect.objectContaining({ remoteCwd, configuredPath: controllerArtifact }));
|
||||
expect(state.resolveRunnerBinary).not.toHaveBeenCalled();
|
||||
expect(state.createBackend).not.toHaveBeenCalled();
|
||||
expect(state.createTransport).not.toHaveBeenCalled();
|
||||
return;
|
||||
}
|
||||
await backend;
|
||||
|
||||
state.createTransport.mockClear();
|
||||
state.createBackend.mock.calls.at(-1)![1].codexTransportFactory!();
|
||||
expect(state.createTransport).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ runnerBinary: controllerArtifact }),
|
||||
);
|
||||
expect(state.resolveRemoteRunnerBinary).toHaveBeenCalledWith(expect.objectContaining({ remoteCwd, configuredPath: mode === "explicit" ? controllerArtifact : undefined }));
|
||||
expect(state.resolveRemoteRunnerBinary).toHaveBeenCalledWith(expect.objectContaining({ remoteCwd, configuredPath: explicit ? controllerArtifact : undefined }));
|
||||
expect(state.resolveRunnerBinary).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
@@ -11551,7 +11551,7 @@ async function createRunnerdBackendWithinSessionClaim(
|
||||
// filesystem; the remote launcher separately owns the sandbox command path.
|
||||
// When an explicit remote artifact is configured, prepareRemoteRunner stages
|
||||
// these exact bytes at remoteBinary before launch.
|
||||
const targetMatchedCodexArtifact = remoteTarget?.transport === "ssh" && input.execution.provider.kind === "codex";
|
||||
const targetMatchedCodexArtifact = remoteTarget !== null && input.execution.provider.kind === "codex";
|
||||
const controllerRunnerBinary = targetMatchedCodexArtifact
|
||||
? await resolveRemotePaperclipRunnerBinary({ runner: remoteCommandRunner!, remoteCwd: remoteTarget.remoteCwd, configuredPath: input.runnerRemoteBinaryPath })
|
||||
: remoteTarget
|
||||
|
||||
@@ -35,6 +35,7 @@ describe("Codex selected native account verification", () => {
|
||||
vi.stubEnv("PAPERCLIP_RUNNER_REMOTE_BINARY_PATH", "");
|
||||
});
|
||||
afterEach(async () => {
|
||||
vi.unstubAllGlobals();
|
||||
vi.unstubAllEnvs();
|
||||
for (const [input] of nativeProbe.mock.calls) if (input.runtimeDirectory) await rm(input.runtimeDirectory, { recursive: true, force: true });
|
||||
});
|
||||
@@ -238,7 +239,12 @@ describe("Codex selected native account verification", () => {
|
||||
if (confirmed) await expect(stat(root)).rejects.toThrow();
|
||||
else expect((await stat(root)).isDirectory()).toBe(true);
|
||||
});
|
||||
it("uses the same preparation and controller PRP for a sandbox, without a guest provider pack", async () => {
|
||||
it.each(["arm64", "x64"].flatMap(controllerArch => ["arm64", "x64"].flatMap(targetArch => [false, true].map(explicit => ({ controllerArch, targetArch, explicit })))))
|
||||
("uses target $targetArch artifacts and controller PRP from Mac $controllerArch for sandbox setup (override=$explicit)", async ({ controllerArch, targetArch, explicit }) => {
|
||||
vi.stubGlobal("process", { ...process, platform: "darwin", arch: controllerArch });
|
||||
const selectedBinary = `/controller/artifacts/linux-${targetArch}/paperclip-runnerd`;
|
||||
selectedRemoteRunner.mockResolvedValue(selectedBinary);
|
||||
if (explicit) vi.stubEnv("PAPERCLIP_RUNNER_REMOTE_BINARY_PATH", selectedBinary);
|
||||
nativeProbe.mockImplementation(async input => {
|
||||
const registration = await input.transportOptions.controlPlaneRegistration({});
|
||||
expect(registration.connection.mode).toBe("connect");
|
||||
@@ -248,10 +254,23 @@ describe("Codex selected native account verification", () => {
|
||||
const target = { ...context.executionTarget, runner: { execute: sshExecute } };
|
||||
expect((await testNativeRunnerAuthentication({ ...context, executionTarget: target }, "codex", "gpt-6.1-sol")).status).toBe("pass");
|
||||
expect(sshRunner).not.toHaveBeenCalled();
|
||||
expect(nativeArtifacts).toHaveBeenCalledWith(expect.objectContaining({ target, runner: target.runner }));
|
||||
expect(selectedRemoteRunner).toHaveBeenCalledExactlyOnceWith({ runner: target.runner, remoteCwd: target.remoteCwd, configuredPath: explicit ? selectedBinary : null });
|
||||
expect(selectedRunner).not.toHaveBeenCalled();
|
||||
expect(nativeArtifacts).toHaveBeenCalledWith(expect.objectContaining({ target, runner: target.runner, controllerRunnerBinary: selectedBinary, targetPlatformArtifactVerified: true }));
|
||||
expect(nativeProbe.mock.calls[0][0].transportOptions.runnerBinary).toBe(selectedBinary);
|
||||
expect(nativeArtifacts.mock.results[0].value.prepare).toHaveBeenCalledWith("dial_wss");
|
||||
expect(execute).not.toHaveBeenCalled();
|
||||
});
|
||||
it.each(["runner_remote_artifact_unavailable", "runner_remote_artifact_platform_mismatch"])("fails sandbox %s before setup or a controller fallback", async code => {
|
||||
selectedRemoteRunner.mockRejectedValue(new Error(code));
|
||||
const target = { ...context.executionTarget, runner: { execute: sshExecute } };
|
||||
const result = await testNativeRunnerAuthentication({ ...context, executionTarget: target }, "codex", "gpt-6.1-sol");
|
||||
expect(result.status).toBe("fail");
|
||||
expect(result.checks[0].message).toContain(code);
|
||||
expect(selectedRunner).not.toHaveBeenCalled(); expect(nativeArtifacts).not.toHaveBeenCalled();
|
||||
expect(nativeProbe).not.toHaveBeenCalled(); expect(sshExecute).not.toHaveBeenCalled();
|
||||
expect(execute).not.toHaveBeenCalled();
|
||||
});
|
||||
it("prepares listener artifacts before acquiring sandbox ingress and owns its release", async () => {
|
||||
const order: string[] = [];
|
||||
const close = vi.fn();
|
||||
|
||||
@@ -63,11 +63,9 @@ async function probeRemoteNativeCodex(options: {
|
||||
const remoteCodexNpmSpec = process.env.PAPERCLIP_RUNNER_REMOTE_CODEX_NPM_SPEC?.trim() || null;
|
||||
const remoteCodexBinary = remoteCodexNpmSpec ? posix.join(remoteRoot, "harnesses", "codex", "node_modules", ".bin", "codex") : posix.join(remoteRoot, "bin", "codex");
|
||||
const explicitRunner = process.env.PAPERCLIP_RUNNER_REMOTE_BINARY_PATH?.trim() || null;
|
||||
const controllerRunnerBinary = target.transport === "ssh"
|
||||
? await resolveRemotePaperclipRunnerBinary({ runner, remoteCwd: target.remoteCwd, configuredPath: explicitRunner })
|
||||
: explicitRunner || resolvePaperclipRunnerBinary();
|
||||
const controllerRunnerBinary = await resolveRemotePaperclipRunnerBinary({ runner, remoteCwd: target.remoteCwd, configuredPath: explicitRunner });
|
||||
const artifacts = createRemoteNativeArtifactPreparation({ target, runner, remoteBinary, controllerRunnerBinary, remoteRuntimeRoot: remoteRoot, remoteCodexBinary,
|
||||
targetPlatformArtifactVerified: target.transport === "ssh",
|
||||
targetPlatformArtifactVerified: true,
|
||||
runnerRemoteBinaryPath: explicitRunner, runnerRemoteCodexPath: process.env.PAPERCLIP_RUNNER_REMOTE_CODEX_PATH?.trim(), runnerRemoteCodexNpmSpec: remoteCodexNpmSpec, model: options.model });
|
||||
const requireSuccess = (result: { timedOut: boolean; exitCode: number | null }, message: string) => {
|
||||
if (result.timedOut || result.exitCode !== 0) throw new Error(message);
|
||||
|
||||
Reference in new issue
Block a user