fix(worker): make user-box lifecycle concurrency-safe
This commit is contained in:
@@ -16,6 +16,7 @@ import { api } from '@spoon/backend/convex/_generated/api.js';
|
||||
|
||||
import type { NormalizedAgentEvent } from './agent-events';
|
||||
import type { OpenCodeSession } from './opencode-session';
|
||||
import type { BoxHandle } from './user-container';
|
||||
import { normalizeCodexJsonLine } from './agent-events';
|
||||
import { prepareCodexWorkspaceFiles } from './codex-runtime';
|
||||
import { env } from './env';
|
||||
@@ -41,11 +42,7 @@ import {
|
||||
stopWorkspaceContainer,
|
||||
streamExecInContainer,
|
||||
} from './runtime/docker';
|
||||
import {
|
||||
acquireUserBox,
|
||||
releaseUserBox,
|
||||
runningBoxUsernames,
|
||||
} from './user-container';
|
||||
import { acquireUserBox, runningBoxUsernames } from './user-container';
|
||||
import { fetchUserEnvironment, materializeUserHome } from './user-environment';
|
||||
|
||||
type Claim = {
|
||||
@@ -111,6 +108,7 @@ type ActiveWorkspace = {
|
||||
repoDir: string;
|
||||
// Phase 2: the per-user box container this thread execs into.
|
||||
boxName: string;
|
||||
boxHandle: BoxHandle;
|
||||
githubToken: string;
|
||||
redact: (value: string) => string;
|
||||
runtimeMode?: 'opencode_server' | 'codex_exec' | 'legacy_cli';
|
||||
@@ -1323,7 +1321,7 @@ const runClaim = async (claim: Claim) => {
|
||||
...claim.secrets.map((secret) => secret.value),
|
||||
].filter(Boolean);
|
||||
const redact = createRedactor(secretValues);
|
||||
let acquiredBoxUser: string | undefined;
|
||||
let acquiredBoxHandle: BoxHandle | undefined;
|
||||
try {
|
||||
if ((claim.job.runtime ?? 'opencode') !== 'opencode') {
|
||||
throw new Error('Legacy OpenAI direct jobs are no longer supported.');
|
||||
@@ -1354,12 +1352,13 @@ const runClaim = async (claim: Claim) => {
|
||||
|
||||
// Start (or reuse) the persistent per-user box that this thread — and the
|
||||
// terminal — exec into. It mounts the home, so the clone below is visible.
|
||||
const boxName = await acquireUserBox({
|
||||
const boxHandle = await acquireUserBox({
|
||||
username,
|
||||
workdir: homeDir,
|
||||
containerHome,
|
||||
});
|
||||
acquiredBoxUser = username;
|
||||
acquiredBoxHandle = boxHandle;
|
||||
const boxName = boxHandle.boxName;
|
||||
|
||||
const repoDir = await cloneRepository({
|
||||
workdir: checkoutParent,
|
||||
@@ -1381,6 +1380,7 @@ const runClaim = async (claim: Claim) => {
|
||||
containerRepo,
|
||||
repoDir,
|
||||
boxName,
|
||||
boxHandle,
|
||||
githubToken,
|
||||
redact,
|
||||
};
|
||||
@@ -1460,7 +1460,7 @@ const runClaim = async (claim: Claim) => {
|
||||
).catch((stopError: unknown) => {
|
||||
console.error(stopError);
|
||||
});
|
||||
if (acquiredBoxUser) releaseUserBox(acquiredBoxUser);
|
||||
acquiredBoxHandle?.release();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1883,7 +1883,7 @@ export const openWorkspacePullRequest = async (jobId: string) => {
|
||||
activeWorkspaces.delete(jobId);
|
||||
// The persistent per-user home + ~/Code checkouts survive across sessions;
|
||||
// release the box (reaped once no other thread/terminal holds it).
|
||||
releaseUserBox(workspace.username);
|
||||
workspace.boxHandle.release();
|
||||
return {
|
||||
pullRequestUrl: pullRequest.html_url,
|
||||
pullRequestNumber: pullRequest.number,
|
||||
@@ -1900,7 +1900,7 @@ export const stopWorkspace = async (jobId: string) => {
|
||||
activeWorkspaces.delete(jobId);
|
||||
// The persistent per-user home + ~/Code checkouts survive across sessions;
|
||||
// release the box (reaped once no other thread/terminal holds it).
|
||||
releaseUserBox(workspace.username);
|
||||
workspace.boxHandle.release();
|
||||
return { success: true };
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user