Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions apps/server/src/computer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -459,13 +459,30 @@ export class ComputerService {
active.token !== lease.token ||
active.stopping ||
active.expiresAt <= Date.now()
)
return this.db.put(owner, this.receipts, {
) {
const stopped: ComputerCommand = {
...command,
status: "interrupted",
stderr: "Stopped before execution",
completedAt: new Date().toISOString(),
});
};
const preserved = await this.db.compareAndSwap<ComputerCommand>(
owner,
this.receipts,
id,
{ status: "running" },
{
status: "interrupted",
stderr: stopped.stderr,
completedAt: stopped.completedAt,
},
);
return (
preserved ?? (await this.db.get<ComputerCommand>(owner, this.receipts, id)) ?? stopped
);
}
const receipt = await this.db.get<ComputerCommand>(owner, this.receipts, id);
if (receipt && receipt.status !== "running") return receipt;
let result: DockerResult;
try {
result = await session.exec(args.command, cwd, options.signal);
Expand Down
46 changes: 46 additions & 0 deletions tests/computer-e2b-desktop.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { createHash } from "node:crypto";
import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
Expand Down Expand Up @@ -1193,3 +1194,48 @@ test("commands and files reuse the verified inspection instead of finding twice"
assert.equal(f.calls.infos - infos, 1);
}
});

test("desktop command cannot bypass an interrupted receipt after lease validation", async () => {
const f = fake({ boxes: [{}] });
const { computer, owner } = service(f);
const cmdId = createHash("sha256").update("computer-command:desktop-dead-row-case").digest("hex");
const originalGet = db.get.bind(db);
let intercepted = false;
db.get = (async (o: string, kind: string, id: string) => {
if (!intercepted && kind === "computer-state" && id === "lease") {
const lease = await originalGet<{ token: string }>(o, kind, id);
const row = await originalGet(o, "computer-desktop-commands", cmdId);
if (lease && row) {
intercepted = true;
await db.compareAndSwap(
o,
"computer-desktop-commands",
cmdId,
{ status: "running" },
{
status: "interrupted",
completedAt: new Date().toISOString(),
stderr:
"Execution was interrupted. Its outcome is unknown; inspect files before running it again.",
},
);
}
}
return originalGet(o, kind, id);
}) as Store["get"];
try {
const receipt = await computer.execute(
owner,
{ command: "touch /workspace/should-not-run" },
{ idempotencyKey: "desktop-dead-row-case" },
);
assert.equal(receipt.status, "interrupted");
assert.equal(f.calls.run.length, 0, "desktop command must not be dispatched");
assert.equal(
(await db.get<ComputerCommand>(owner, "computer-desktop-commands", cmdId))?.status,
"interrupted",
);
} finally {
db.get = originalGet;
}
});
97 changes: 97 additions & 0 deletions tests/computer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,100 @@ test("an expired lease from a dead executor recovers as interrupted without repl
assert.ok(!f.calls.some((call) => call.args[0] === "exec"));
assert.equal((await service.start("owner")).status, "running");
});

test("a Stop quarantine that lands before the lease check keeps its record", async () => {
const f = fixture();
const service = new ComputerService(db, config, f.runner);
const cmdId = createHash("sha256").update("computer-command:quarantine-case").digest("hex");
const originalGet = db.get.bind(db);
let intercepted = false;
db.get = (async (o: string, kind: string, id: string) => {
if (!intercepted && kind === "computer-state" && id === "lease") {
const lease = await originalGet<{ token: string }>(o, kind, id);
const row = await originalGet(o, "computer-commands", cmdId);
if (lease && row) {
intercepted = true;
await db.compareAndSwap(
o,
kind,
id,
{ token: lease.token, stopping: false },
{
stopping: true,
stopInFlight: true,
stopAttempt: "stop-attempt-1",
stopConfirmed: false,
expiresAt: Date.now() + 180000,
},
);
await db.compareAndSwap(
o,
"computer-commands",
cmdId,
{ status: "running" },
{
status: "interrupted",
completedAt: new Date().toISOString(),
stderr: "Stopped by the user. Inspect the workspace before repeating this command.",
},
);
}
}
return originalGet(o, kind, id);
}) as Store["get"];
try {
const receipt = await service.execute(
"owner",
{ command: "sleep 30" },
{ idempotencyKey: "quarantine-case" },
);
assert.equal(receipt.status, "interrupted");
assert.match(receipt.stderr, /Stopped by the user/);
assert.ok(!f.calls.some((call) => call.args[0] === "exec"));
} finally {
db.get = originalGet;
await db.remove("owner", "computer-state", "lease");
}
});

test("a command marked dead after the lease check is never executed", async () => {
const f = fixture({ command: async () => ok("should never run") });
const service = new ComputerService(db, config, f.runner);
const cmdId = createHash("sha256").update("computer-command:dead-row-case").digest("hex");
const originalGet = db.get.bind(db);
let intercepted = false;
db.get = (async (o: string, kind: string, id: string) => {
if (!intercepted && kind === "computer-state" && id === "lease") {
const lease = await originalGet<{ token: string }>(o, kind, id);
const row = await originalGet(o, "computer-commands", cmdId);
if (lease && row) {
intercepted = true;
await db.compareAndSwap(
o,
"computer-commands",
cmdId,
{ status: "running" },
{
status: "interrupted",
completedAt: new Date().toISOString(),
stderr:
"Execution was interrupted. Its outcome is unknown; inspect files before running it again.",
},
);
}
}
return originalGet(o, kind, id);
}) as Store["get"];
try {
const receipt = await service.execute(
"owner",
{ command: "touch /tmp/should-not-run" },
{ idempotencyKey: "dead-row-case" },
);
assert.equal(receipt.status, "interrupted");
assert.equal(receipt.stdout, "");
assert.ok(!f.calls.some((call) => call.args[0] === "exec"), "docker exec must not run");
} finally {
db.get = originalGet;
}
});
Loading