mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-21 16:27:18 +02:00
Diagnose artifact upload phases with opt-in bounded records
This commit is contained in:
1 parent
086a9dbbd8
commit
4e489a1014
9 files changed
+226
-13
No files matched your search
@@ -1,3 +1,4 @@
|
||||
import { reportCommandPhase } from "@paperclipai/adapter-utils/command-diagnostics";
|
||||
import { generateKeyPairSync, randomUUID } from "node:crypto";
|
||||
import {
|
||||
createSshCommandManagedRuntimeRunner,
|
||||
@@ -85,6 +86,7 @@ export function createBoatTransportAdmission() {
|
||||
}
|
||||
const withStdinUploadSlot: NonNullable<CommandManagedRuntimeRunner["withStdinUploadSlot"]> = (operation, options) => {
|
||||
if (uploadQueue.length >= 128) return Promise.reject(new ComputerError("conflict", "Computer upload admission is busy"));
|
||||
reportCommandPhase(options.onPhase, "upload_queued");
|
||||
return new Promise((resolve, reject) => {
|
||||
const cleanup = () => { clearTimeout(timer); options.signal?.removeEventListener("abort", aborted); };
|
||||
const remove = (message: string) => {
|
||||
@@ -98,8 +100,10 @@ export function createBoatTransportAdmission() {
|
||||
const entry = { start: () => {
|
||||
cleanup();
|
||||
uploads++;
|
||||
reportCommandPhase(options.onPhase, "upload_admitted");
|
||||
void Promise.resolve().then(operation).then(resolve, reject).finally(() => {
|
||||
uploads--;
|
||||
reportCommandPhase(options.onPhase, "upload_released");
|
||||
drainUploads();
|
||||
});
|
||||
} };
|
||||
|
||||
@@ -116,6 +116,19 @@ function fixture() {
|
||||
};
|
||||
}
|
||||
describe("computer ownership", () => {
|
||||
it("forwards opt-in phases through the existing owner guard without serializing the observer", async () => {
|
||||
const f = fixture();
|
||||
const phases: string[] = [];
|
||||
vi.mocked(f.backend.runner).mockResolvedValue({ execute: async input => {
|
||||
expect(input.stdin).not.toContain("onPhase");
|
||||
await input.onPhase?.("ssh_spawned");
|
||||
return { exitCode: 0, signal: null, timedOut: false, stdout: "", stderr: "", pid: null, startedAt: "" };
|
||||
} });
|
||||
await f.attach();
|
||||
const binding = await f.admit();
|
||||
await binding.process.runner.execute({ command: "fixture", onPhase: phase => { phases.push(phase); } });
|
||||
expect(phases).toEqual(["owner_guard_start", "owner_guard_complete", "ssh_spawned"]);
|
||||
});
|
||||
it("rechecks owner authority after a shared upload slot wait before SSH dispatch", async () => {
|
||||
const f = fixture();
|
||||
const admission = createBoatTransportAdmission();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { reportCommandPhase } from "@paperclipai/adapter-utils/command-diagnostics";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import type { ComputerRepository, ComputerBackend } from "./ports.js";
|
||||
import {
|
||||
@@ -229,12 +230,14 @@ export function createComputerService(
|
||||
): CommandManagedRuntimeRunner => ({
|
||||
withStdinUploadSlot: (processScoped ? control : raw).withStdinUploadSlot,
|
||||
execute: async (input) => {
|
||||
reportCommandPhase(input.onPhase, "owner_guard_start");
|
||||
const current = await (processScoped
|
||||
? processScope()
|
||||
: scoped({ ...record, owner: ownerRef }));
|
||||
if ((!["active", "starting", "warm"].includes(current.owner.phase) || expired(current.owner, now())) &&
|
||||
!(processScoped && pinnedProcess && processControlAllowed(current.owner)))
|
||||
throw new ComputerError("conflict", "Computer owner is retired");
|
||||
reportCommandPhase(input.onPhase, "owner_guard_complete");
|
||||
// Start a command unit under the same owner slice while holding the remote tombstone lock.
|
||||
// The lock is released only after systemd knows the unit, so retirement cannot miss a delayed launch.
|
||||
return (processScoped ? control : raw).execute({
|
||||
@@ -320,6 +323,7 @@ finally:
|
||||
timeoutMs: input.timeoutMs,
|
||||
onLog: input.onLog,
|
||||
onSpawn: input.onSpawn,
|
||||
onPhase: input.onPhase,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user