mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-13 04:02:28 +02:00
fix(computers): reserve lifecycle traffic capacity during workspace setup
Route project setup and file operations through ordinary SSH admission, retaining reserved control capacity for owned-port inspection and lifecycle operations. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
d3fc9db89e
commit
d4047f2fef
2 files changed
+38
-2
No files matched your search
@@ -19,6 +19,11 @@ import { afterEach, describe, it, expect, vi } from "vitest";
|
||||
import { boatBackend, createBoatTransportAdmission, desktopReadinessProgram, processProgram } from "./boat.js";
|
||||
import { buildGitAuthInvocation } from "../../../services/git-credentials.js";
|
||||
import type { ComputerRecord } from "../domain/ledger.js";
|
||||
const sshFactory = vi.hoisted(() => vi.fn());
|
||||
vi.mock("@paperclipai/adapter-utils/ssh", async importOriginal => ({
|
||||
...await importOriginal<typeof import("@paperclipai/adapter-utils/ssh")>(),
|
||||
createSshCommandManagedRuntimeRunner: sshFactory,
|
||||
}));
|
||||
const record: ComputerRecord = {
|
||||
id: "computer",
|
||||
companyId: "company",
|
||||
@@ -607,6 +612,34 @@ describe("Boat SSH admission", () => {
|
||||
await Promise.all(jobs);
|
||||
expect(runner.execute).toHaveBeenCalledTimes(12);
|
||||
});
|
||||
it("classifies checkout and file operations below reserved lifecycle capacity", async () => {
|
||||
const id = randomUUID();
|
||||
const scoped = { ...record, id, providerId: `bx_${id}` };
|
||||
const completions: Array<() => void> = [];
|
||||
const execute = vi.fn(async (_input: { stdin?: string }) => new Promise<typeof result>(resolve => {
|
||||
completions.push(() => resolve({ ...result, stdout: "{}" }));
|
||||
}));
|
||||
sshFactory.mockReturnValue({ execute });
|
||||
const backend = boatBackend(async () => "fixture-key", vi.fn(async () => json({
|
||||
hostKey: "ssh-ed25519 AAAA", sshEndpoint: "fixture.invalid:2222",
|
||||
})));
|
||||
const jobs = Array.from({ length: 8 }, (_, index) => backend.remote(scoped, {
|
||||
action: index % 2 ? "workspace" : "list", root: "/home/user/paperclip/company/project",
|
||||
}));
|
||||
await vi.waitFor(() => expect(execute).toHaveBeenCalledTimes(6));
|
||||
jobs.push(backend.retire(scoped, { id: "owner", generation: 1, kind: "runner", phase: "retiring", port: 43127,
|
||||
deadline: null, absoluteDeadline: null, process: null }));
|
||||
jobs.push(backend.remote(scoped, { action: "owned-port", port: 5173, ownerId: "owner" }));
|
||||
await vi.waitFor(() => expect(execute).toHaveBeenCalledTimes(8));
|
||||
const actions = execute.mock.calls.map(([input]) => JSON.parse(input.stdin!).action);
|
||||
expect(actions.filter(action => action === "workspace" || action === "list")).toHaveLength(6);
|
||||
expect(actions).toContain("retire"); expect(actions).toContain("owned-port");
|
||||
completions.splice(0).forEach(finish => finish());
|
||||
await vi.waitFor(() => expect(execute).toHaveBeenCalledTimes(10));
|
||||
completions.splice(0).forEach(finish => finish());
|
||||
await Promise.all(jobs);
|
||||
sshFactory.mockReset();
|
||||
});
|
||||
it("expires queued work without starting it and preserves the original remaining timeout", async () => {
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
|
||||
@@ -342,9 +342,10 @@ export function boatBackend(
|
||||
record: ComputerRecord,
|
||||
code: string,
|
||||
payload: unknown,
|
||||
control = true,
|
||||
) {
|
||||
const result = await (
|
||||
await runner(record, { control: true })
|
||||
await runner(record, { control })
|
||||
).execute({
|
||||
command: "python3",
|
||||
args: ["-c", code],
|
||||
@@ -550,7 +551,9 @@ print('{}')
|
||||
return { url: url.toString(), secretHeaders: { Cookie: cookie } };
|
||||
},
|
||||
async remote(record, input) {
|
||||
return execute(record, remoteProgram, input);
|
||||
// Git setup and file I/O may wait on locks or transfer substantial data.
|
||||
// Keep lifecycle capacity reserved; only the short owned-port probe uses it.
|
||||
return execute(record, remoteProgram, input, input.action === "owned-port");
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user