@loucompanion/forge-bridge 0.1.3-dev.d1ca67076aa9 → 0.1.3-dev.d46a8e73d03d

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +44 -0
  2. package/dist/bridge/bridge.base.d.ts +5 -1
  3. package/dist/bridge/bridge.service.d.ts +40 -2
  4. package/dist/bridge/bridge.service.js +580 -16
  5. package/dist/bridge/bridge.types.d.ts +114 -1
  6. package/dist/cli/cli.help.js +12 -0
  7. package/dist/cli/cli.service.d.ts +12 -0
  8. package/dist/cli/cli.service.js +171 -1
  9. package/dist/cli/local-config.d.ts +2 -0
  10. package/dist/cli/local-config.js +6 -2
  11. package/dist/sandbox/command-runner.d.ts +4 -0
  12. package/dist/sandbox/command-runner.js +47 -0
  13. package/dist/sandbox/contract.d.ts +5 -0
  14. package/dist/sandbox/contract.js +276 -0
  15. package/dist/sandbox/event-budget.d.ts +11 -0
  16. package/dist/sandbox/event-budget.js +27 -0
  17. package/dist/sandbox/image-policy.d.ts +35 -0
  18. package/dist/sandbox/image-policy.js +247 -0
  19. package/dist/sandbox/index.d.ts +5 -0
  20. package/dist/sandbox/index.js +5 -0
  21. package/dist/sandbox/lease-watchdog.d.ts +18 -0
  22. package/dist/sandbox/lease-watchdog.js +67 -0
  23. package/dist/sandbox/preflight.d.ts +40 -0
  24. package/dist/sandbox/preflight.js +256 -0
  25. package/dist/sandbox/process.d.ts +20 -0
  26. package/dist/sandbox/process.js +124 -0
  27. package/dist/sandbox/provider-auth/codex.d.ts +26 -0
  28. package/dist/sandbox/provider-auth/codex.js +212 -0
  29. package/dist/sandbox/repository-lock.d.ts +5 -0
  30. package/dist/sandbox/repository-lock.js +83 -0
  31. package/dist/sandbox/runtime.d.ts +234 -0
  32. package/dist/sandbox/runtime.js +1773 -0
  33. package/dist/sandbox/sandbox.types.d.ts +83 -0
  34. package/dist/sandbox/sandbox.types.js +1 -0
  35. package/dist/sandbox/worker-supervisor.d.ts +13 -0
  36. package/dist/sandbox/worker-supervisor.js +36 -0
  37. package/dist/sandbox/worker.d.ts +1 -0
  38. package/dist/sandbox/worker.js +228 -0
  39. package/dist/sandbox/workspace.service.d.ts +12 -0
  40. package/dist/sandbox/workspace.service.js +49 -0
  41. package/dist/session/phase-completion.artifacts.js +23 -2
  42. package/dist/session/provider-cli/provider-cli.base.d.ts +1 -0
  43. package/dist/session/provider-cli/provider-cli.deterministic.d.ts +2 -0
  44. package/dist/session/provider-cli/provider-cli.deterministic.js +20 -9
  45. package/dist/session/provider-cli/provider-cli.process.d.ts +1 -0
  46. package/dist/session/provider-cli/provider-cli.process.js +23 -1
  47. package/dist/session/provider-cli/provider-cli.prompts.d.ts +12 -1
  48. package/dist/session/provider-cli/provider-cli.prompts.js +119 -2
  49. package/dist/session/provider-cli/provider-cli.service.d.ts +4 -1
  50. package/dist/session/provider-cli/provider-cli.service.js +161 -43
  51. package/dist/session/provider-cli/provider-cli.types.d.ts +1 -0
  52. package/dist/session/session.service.js +34 -24
  53. package/dist/session/session.types.d.ts +52 -1
  54. package/dist/session/session.types.js +159 -3
  55. package/dist/shared/process/windows-launch.d.ts +1 -1
  56. package/dist/shared/process/windows-launch.js +6 -3
  57. package/dist/shared/provider-capabilities.d.ts +1 -0
  58. package/dist/shared/provider-capabilities.js +3 -0
  59. package/dist/shared/redaction/redaction.js +4 -2
  60. package/dist/version.d.ts +1 -1
  61. package/dist/version.js +1 -1
  62. package/dist/worktree/dependency-warmer.d.ts +11 -0
  63. package/dist/worktree/dependency-warmer.js +45 -1
  64. package/package.json +4 -2
@@ -0,0 +1,212 @@
1
+ import fs from "node:fs";
2
+ import fsp from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ const maxCredentialFileBytes = 1024 * 1024;
6
+ // Capability and heartbeat paths call doctor() synchronously; keep validation
7
+ // fresh enough for auth changes without rereading auth.json for every offer.
8
+ const authDoctorCacheTtlMs = 5_000;
9
+ let authDoctorCache = null;
10
+ export const codexSandboxProviderAuthAdapter = {
11
+ provider: "codex",
12
+ authKind: "bridge_cli",
13
+ containerMountPath: "/home/forge/.codex",
14
+ // Codex refreshes OAuth tokens in place, so the container mount is writable.
15
+ // The writable state is an attempt-scoped Docker volume copy only; no code path
16
+ // syncs modified container credentials back to the host ~/.codex directory.
17
+ writable: true,
18
+ doctor(environment = process.env) {
19
+ return codexAuthDoctor(environment);
20
+ },
21
+ async prepareAttemptAuth(environment = process.env) {
22
+ const doctor = codexAuthDoctor(environment);
23
+ if (!doctor.available || !doctor.authPath) {
24
+ throw new Error(`sandbox_auth_unavailable: ${doctor.message}`);
25
+ }
26
+ let content;
27
+ try {
28
+ content = await readValidatedCodexAuthFile(doctor.authPath);
29
+ }
30
+ catch (error) {
31
+ throw new Error(`sandbox_auth_unavailable: ${error instanceof Error ? error.message : "Codex Local CLI auth.json could not be validated."}`, { cause: error });
32
+ }
33
+ return {
34
+ files: [
35
+ {
36
+ name: "auth.json",
37
+ contentBase64: content.toString("base64")
38
+ }
39
+ ],
40
+ containerMountPath: this.containerMountPath,
41
+ writable: this.writable
42
+ };
43
+ }
44
+ };
45
+ export function sandboxProviderAuthAdapterFor(provider, authKind) {
46
+ if (authKind.trim().toLowerCase() !== "bridge_cli") {
47
+ return null;
48
+ }
49
+ return provider.trim().toLowerCase() === "codex"
50
+ ? codexSandboxProviderAuthAdapter
51
+ : null;
52
+ }
53
+ export function sandboxProviderAuthUnavailableMessage(provider, authKind) {
54
+ if (authKind.trim().toLowerCase() !== "bridge_cli") {
55
+ return null;
56
+ }
57
+ return provider.trim().toLowerCase() === "codex"
58
+ ? null
59
+ : `Provider ${provider} Local CLI auth is not portable into Docker yet.`;
60
+ }
61
+ function codexAuthDoctor(environment) {
62
+ const authPath = path.join(resolveCodexHome(environment), "auth.json");
63
+ const now = Date.now();
64
+ if (authDoctorCache?.authPath === authPath && authDoctorCache.expiresAt > now) {
65
+ return authDoctorCache.result;
66
+ }
67
+ let result;
68
+ try {
69
+ readValidatedCodexAuthFileSync(authPath);
70
+ result = {
71
+ available: true,
72
+ code: "ok",
73
+ message: "Codex Local CLI auth is portable into an attempt-scoped Docker volume.",
74
+ authPath
75
+ };
76
+ }
77
+ catch (error) {
78
+ result = unavailable(error instanceof Error ? error.message : "Codex Local CLI auth.json could not be validated.");
79
+ }
80
+ authDoctorCache = {
81
+ authPath,
82
+ expiresAt: now + authDoctorCacheTtlMs,
83
+ result
84
+ };
85
+ return result;
86
+ }
87
+ async function readValidatedCodexAuthFile(authPath) {
88
+ let pathStat;
89
+ try {
90
+ pathStat = await fsp.lstat(authPath);
91
+ }
92
+ catch {
93
+ throw new Error("Codex Local CLI auth is not available on the bridge host.");
94
+ }
95
+ validateCodexAuthStat(pathStat);
96
+ let handle;
97
+ try {
98
+ handle = await fsp.open(authPath, codexAuthOpenFlags());
99
+ }
100
+ catch (error) {
101
+ throw codexAuthOpenError(error);
102
+ }
103
+ try {
104
+ const openedStat = await handle.stat();
105
+ validateSameCodexAuthFile(pathStat, openedStat);
106
+ validateCodexAuthStat(openedStat);
107
+ const content = await handle.readFile();
108
+ const afterStat = await handle.stat();
109
+ validateSameCodexAuthFile(openedStat, afterStat);
110
+ validateCodexAuthStat(afterStat);
111
+ validateCodexAuthReadSize(content, openedStat, afterStat);
112
+ validateCodexAuthJson(content);
113
+ return content;
114
+ }
115
+ finally {
116
+ await handle.close();
117
+ }
118
+ }
119
+ function readValidatedCodexAuthFileSync(authPath) {
120
+ let pathStat;
121
+ try {
122
+ pathStat = fs.lstatSync(authPath);
123
+ }
124
+ catch {
125
+ throw new Error("Codex Local CLI auth is not available on the bridge host.");
126
+ }
127
+ validateCodexAuthStat(pathStat);
128
+ let fd;
129
+ try {
130
+ fd = fs.openSync(authPath, codexAuthOpenFlags());
131
+ }
132
+ catch (error) {
133
+ throw codexAuthOpenError(error);
134
+ }
135
+ try {
136
+ const openedStat = fs.fstatSync(fd);
137
+ validateSameCodexAuthFile(pathStat, openedStat);
138
+ validateCodexAuthStat(openedStat);
139
+ const content = fs.readFileSync(fd);
140
+ const afterStat = fs.fstatSync(fd);
141
+ validateSameCodexAuthFile(openedStat, afterStat);
142
+ validateCodexAuthStat(afterStat);
143
+ validateCodexAuthReadSize(content, openedStat, afterStat);
144
+ validateCodexAuthJson(content);
145
+ return content;
146
+ }
147
+ finally {
148
+ fs.closeSync(fd);
149
+ }
150
+ }
151
+ function codexAuthOpenFlags() {
152
+ return fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW;
153
+ }
154
+ function validateCodexAuthStat(stat) {
155
+ if (stat.isSymbolicLink()) {
156
+ throw new Error("Codex Local CLI auth.json must not be a symlink.");
157
+ }
158
+ if (!stat.isFile()) {
159
+ throw new Error("Codex Local CLI auth.json is not a regular file.");
160
+ }
161
+ if (stat.size <= 0) {
162
+ throw new Error("Codex Local CLI auth.json is empty.");
163
+ }
164
+ if (stat.size > maxCredentialFileBytes) {
165
+ throw new Error("Codex Local CLI auth.json exceeds the 1 MiB sandbox auth limit.");
166
+ }
167
+ }
168
+ function validateSameCodexAuthFile(before, after) {
169
+ if (before.dev !== 0 && before.ino !== 0 && after.dev !== 0 && after.ino !== 0
170
+ && (before.dev !== after.dev || before.ino !== after.ino)) {
171
+ throw new Error("Codex Local CLI auth.json changed while being copied.");
172
+ }
173
+ }
174
+ function validateCodexAuthReadSize(content, openedStat, afterStat) {
175
+ if (openedStat.size !== afterStat.size || content.length !== afterStat.size) {
176
+ throw new Error("Codex Local CLI auth.json changed while being copied.");
177
+ }
178
+ }
179
+ function validateCodexAuthJson(content) {
180
+ try {
181
+ JSON.parse(content.toString("utf8"));
182
+ }
183
+ catch {
184
+ throw new Error("Codex Local CLI auth.json is not valid JSON.");
185
+ }
186
+ }
187
+ function codexAuthOpenError(error) {
188
+ const code = typeof error === "object" && error !== null && "code" in error
189
+ ? error.code
190
+ : undefined;
191
+ if (code === "ELOOP") {
192
+ return new Error("Codex Local CLI auth.json must not be a symlink.");
193
+ }
194
+ if (code === "ENOENT") {
195
+ return new Error("Codex Local CLI auth is not available on the bridge host.");
196
+ }
197
+ if (code === "EACCES" || code === "EPERM") {
198
+ return new Error("Codex Local CLI auth.json could not be read.");
199
+ }
200
+ return error instanceof Error ? error : new Error("Codex Local CLI auth.json could not be read.");
201
+ }
202
+ function unavailable(message) {
203
+ return {
204
+ available: false,
205
+ code: "sandbox_auth_unavailable",
206
+ message
207
+ };
208
+ }
209
+ function resolveCodexHome(environment) {
210
+ const configured = environment.FORGE_CODEX_HOME?.trim();
211
+ return configured ? path.resolve(configured) : path.join(os.homedir(), ".codex");
212
+ }
@@ -0,0 +1,5 @@
1
+ export declare class RepositoryCoordinatorLock {
2
+ private readonly stateRoot;
3
+ constructor(stateRoot: string);
4
+ withLock<T>(repoPath: string, action: () => Promise<T>, signal?: AbortSignal): Promise<T>;
5
+ }
@@ -0,0 +1,83 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ export class RepositoryCoordinatorLock {
5
+ stateRoot;
6
+ constructor(stateRoot) {
7
+ this.stateRoot = stateRoot;
8
+ }
9
+ async withLock(repoPath, action, signal) {
10
+ const resolved = path.resolve(repoPath);
11
+ const key = createHash("sha256").update(resolved.toLowerCase()).digest("hex");
12
+ const root = path.join(this.stateRoot, "repo-locks");
13
+ const target = path.join(root, `${key}.lock`);
14
+ const token = randomUUID();
15
+ await fs.mkdir(root, { recursive: true, mode: 0o700 });
16
+ const started = Date.now();
17
+ while (true) {
18
+ if (signal?.aborted)
19
+ throw new Error("sandbox_import_cancelled: repository coordination was cancelled.");
20
+ try {
21
+ const handle = await fs.open(target, "wx", 0o600);
22
+ try {
23
+ await handle.writeFile(`${JSON.stringify({ schemaVersion: 1, token, pid: process.pid, repoPath: resolved, acquiredAt: new Date().toISOString() })}\n`);
24
+ await handle.sync();
25
+ }
26
+ finally {
27
+ await handle.close();
28
+ }
29
+ break;
30
+ }
31
+ catch (error) {
32
+ if (error.code !== "EEXIST")
33
+ throw error;
34
+ if (await staleLock(target)) {
35
+ await fs.rm(target, { force: true });
36
+ continue;
37
+ }
38
+ if (Date.now() - started >= 2 * 60_000) {
39
+ throw new Error("sandbox_import_unavailable: repository coordinator lock timed out.", { cause: error });
40
+ }
41
+ await abortableDelay(50, signal);
42
+ }
43
+ }
44
+ try {
45
+ return await action();
46
+ }
47
+ finally {
48
+ const owner = await readOwner(target);
49
+ if (owner?.token === token)
50
+ await fs.rm(target, { force: true });
51
+ }
52
+ }
53
+ }
54
+ async function staleLock(target) {
55
+ const stat = await fs.lstat(target).catch(() => null);
56
+ if (!stat || stat.isSymbolicLink() || !stat.isFile() || stat.size > 16 * 1024)
57
+ return true;
58
+ const owner = await readOwner(target);
59
+ if (!owner || typeof owner.pid !== "number" || !Number.isInteger(owner.pid) || owner.pid <= 0)
60
+ return true;
61
+ if (Date.now() - stat.mtimeMs > 10 * 60_000)
62
+ return true;
63
+ try {
64
+ process.kill(owner.pid, 0);
65
+ return false;
66
+ }
67
+ catch {
68
+ return true;
69
+ }
70
+ }
71
+ async function readOwner(target) {
72
+ return await fs.readFile(target, "utf8").then(value => JSON.parse(value)).catch(() => null);
73
+ }
74
+ async function abortableDelay(delayMs, signal) {
75
+ if (!signal)
76
+ return await new Promise(resolve => setTimeout(resolve, delayMs));
77
+ await new Promise((resolve, reject) => {
78
+ const timer = setTimeout(() => { signal.removeEventListener("abort", abort); resolve(); }, delayMs);
79
+ const abort = () => { clearTimeout(timer); reject(new Error("sandbox_import_cancelled: repository coordination was cancelled.")); };
80
+ signal.addEventListener("abort", abort, { once: true });
81
+ timer.unref?.();
82
+ });
83
+ }
@@ -0,0 +1,234 @@
1
+ import type { BridgeServiceConfig } from "../bridge/bridge.types.js";
2
+ import type { SessionEventSink } from "../session/session.base.js";
3
+ import { type RunOnceDispatchPacket, type RunOnceResultPacket } from "../session/session.types.js";
4
+ import type { SandboxService, ValidatedSandboxContract } from "./sandbox.types.js";
5
+ export interface SandboxRuntime {
6
+ runOnce(dispatch: RunOnceDispatchPacket, sink: SessionEventSink, signal?: AbortSignal): Promise<RunOnceResultPacket>;
7
+ reap(options?: SandboxReapOptions): Promise<void>;
8
+ pendingCleanupReports?(): Promise<readonly SandboxCleanupReport[]>;
9
+ acknowledgeCleanupReport?(reportId: string): Promise<void>;
10
+ updateLease(dispatchAttemptId: string, fencingToken: string, expiresAt: string): Promise<void>;
11
+ snapshot(): SandboxRuntimeSnapshot;
12
+ }
13
+ export interface SandboxReapOptions {
14
+ readonly diskLow?: boolean;
15
+ readonly attemptSnapshot?: readonly SandboxAttemptSnapshot[];
16
+ }
17
+ export interface SandboxAttemptSnapshot {
18
+ readonly dispatchAttemptId: string;
19
+ readonly fencingToken: string;
20
+ readonly runnerInstanceId: string;
21
+ readonly status: string;
22
+ readonly expiresAt: string;
23
+ }
24
+ export interface SandboxCleanupReport {
25
+ readonly reportId: string;
26
+ readonly source: "teardown" | "reaper";
27
+ readonly actions: readonly SandboxCleanupAction[];
28
+ }
29
+ export interface SandboxCleanupAction {
30
+ readonly dispatchAttemptId: string;
31
+ readonly fencingToken: string;
32
+ readonly runnerInstanceId: string;
33
+ readonly resourceType: "container" | "network" | "volume";
34
+ readonly resourceId: string;
35
+ readonly outcome: "removed" | "failed";
36
+ readonly message?: string | null;
37
+ readonly occurredAt: string;
38
+ }
39
+ export interface SandboxRuntimeSnapshot {
40
+ readonly starting: number;
41
+ readonly active: number;
42
+ readonly degraded: number;
43
+ }
44
+ export interface SandboxPublicationGrantRequest {
45
+ readonly dispatchAttemptId: string;
46
+ readonly leaseId: string;
47
+ readonly fencingToken: string;
48
+ readonly runnerInstanceId: string;
49
+ readonly grantId: string;
50
+ readonly branch: string;
51
+ readonly resultCommitSha: string;
52
+ }
53
+ export interface SandboxPublicationCredential {
54
+ readonly username: string;
55
+ readonly secret: string;
56
+ readonly remoteUrl: string;
57
+ readonly expiresAt: string;
58
+ }
59
+ export type SandboxPublicationGrantRedeemer = (request: SandboxPublicationGrantRequest, signal?: AbortSignal) => Promise<SandboxPublicationCredential>;
60
+ export declare class DockerSandboxRuntime implements SandboxRuntime {
61
+ private readonly config;
62
+ private readonly publicationGrantRedeemer?;
63
+ private readonly active;
64
+ private readonly degraded;
65
+ private readonly builds;
66
+ private readonly verifiedCatalogImages;
67
+ private buildTail;
68
+ constructor(config: BridgeServiceConfig, publicationGrantRedeemer?: SandboxPublicationGrantRedeemer | undefined);
69
+ runOnce(dispatch: RunOnceDispatchPacket, sink: SessionEventSink, signal?: AbortSignal): Promise<RunOnceResultPacket>;
70
+ updateLease(dispatchAttemptId: string, fencingToken: string, expiresAt: string): Promise<void>;
71
+ private pushLeaseUpdate;
72
+ snapshot(): SandboxRuntimeSnapshot;
73
+ reap(options?: SandboxReapOptions): Promise<void>;
74
+ pendingCleanupReports(): Promise<readonly SandboxCleanupReport[]>;
75
+ acknowledgeCleanupReport(reportId: string): Promise<void>;
76
+ private persistCleanupReport;
77
+ private cleanupReportPath;
78
+ private writeLedger;
79
+ private readLedgers;
80
+ private ledgerPath;
81
+ private removeLedger;
82
+ private buildImage;
83
+ private withBuildPermit;
84
+ private buildImageCore;
85
+ private runBuildStage;
86
+ private ensureBuildxBuilder;
87
+ private initializeVolumes;
88
+ private inspectContainerState;
89
+ private initializeServiceSecrets;
90
+ private initializeProviderCredentials;
91
+ private initializeEgressPolicy;
92
+ private startEgressProxy;
93
+ private startServices;
94
+ private verifyCatalogImage;
95
+ private waitForServiceHealthy;
96
+ private teardown;
97
+ private listResources;
98
+ private inspectResourceLabels;
99
+ private removeResource;
100
+ private writeImageCacheRecord;
101
+ private readImageCacheRecords;
102
+ private readImageCacheRecord;
103
+ private collectExpiredImages;
104
+ private purgeRestrictedAuditArtifacts;
105
+ private docker;
106
+ private dockerArgs;
107
+ }
108
+ export declare function publicationPushArgs(branch: string, resultCommitSha: string, inputCommitSha: string, remoteCommitSha: string, remote?: string): string[] | null;
109
+ export declare function validatePublicationCredential(credential: SandboxPublicationCredential, nowMs?: number): void;
110
+ export declare function publicationCredentialEnvironment(credential: SandboxPublicationCredential, environment?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
111
+ export interface RestrictedBuildLog {
112
+ readonly content: string;
113
+ readonly digest: string;
114
+ readonly byteCount: number;
115
+ readonly truncated: boolean;
116
+ }
117
+ export declare function restrictedBuildLog(source: string, exactSecrets?: readonly string[], maximumBytes?: number): RestrictedBuildLog;
118
+ export declare function validateGitObjectBudget(inventory: string, maximumObjectCount?: number, maximumExpandedBytes?: number): {
119
+ readonly objectCount: number;
120
+ readonly expandedBytes: number;
121
+ };
122
+ export declare function validateGitCommitDepth(value: string, maximumCommitDepth?: number): number;
123
+ export interface AuditArtifactRecord {
124
+ readonly key: string;
125
+ readonly lastModifiedMs: number;
126
+ }
127
+ export declare function selectAuditArtifactGcCandidates(records: readonly AuditArtifactRecord[], retentionCount: number, retentionTtlMs: number, protectedKeys?: ReadonlySet<string>, nowMs?: number): string[];
128
+ export declare function selectSandboxConfigRefs(executionPolicy: string, configuredSha: string | null | undefined, defaultBranch: string): {
129
+ readonly refs: readonly string[];
130
+ readonly approvedSha: string | null;
131
+ };
132
+ export declare function sandboxTimeLimits(environment?: NodeJS.ProcessEnv): {
133
+ readonly hardWallMs: number;
134
+ readonly idleMs: number;
135
+ };
136
+ export interface ImageCacheRecord {
137
+ readonly schemaVersion: 1;
138
+ readonly key: string;
139
+ readonly project: string;
140
+ readonly createdAt: string;
141
+ readonly lastUsedAt: string;
142
+ readonly projectTag: string;
143
+ readonly helperTag: string;
144
+ readonly overlayTag: string;
145
+ readonly imageDigest: string;
146
+ readonly helperImageDigest: string;
147
+ readonly policyDigest: string;
148
+ }
149
+ interface ResourceNames {
150
+ prefix: string;
151
+ container: string;
152
+ network: string;
153
+ egressNetwork: string;
154
+ workspaceVolume: string;
155
+ controlVolume: string;
156
+ secretsVolume: string;
157
+ credentialsVolume: string;
158
+ egressConfigVolume: string;
159
+ cacheVolumes: Record<string, string>;
160
+ }
161
+ interface SandboxRuntimeVolume {
162
+ readonly name: string;
163
+ readonly role: string;
164
+ }
165
+ interface ActiveSandbox {
166
+ dispatchAttemptId: string;
167
+ fencingToken: string;
168
+ expiresAt: string;
169
+ controller: AbortController;
170
+ phase: "starting" | "active";
171
+ ledger: SandboxLedger | null;
172
+ leaseControlKey: string;
173
+ }
174
+ export declare function assertSandboxImportCurrent(expected: Pick<ActiveSandbox, "dispatchAttemptId" | "fencingToken" | "expiresAt">, current: Pick<ActiveSandbox, "dispatchAttemptId" | "fencingToken"> | undefined, signal: AbortSignal, nowMs?: number): void;
175
+ interface SandboxLedger {
176
+ schemaVersion: 1;
177
+ dispatchAttemptId: string;
178
+ fencingToken: string;
179
+ runnerInstanceId: string;
180
+ expiresAt: string;
181
+ resources: ResourceNames;
182
+ labels: Record<string, string>;
183
+ includeCredentials?: boolean;
184
+ }
185
+ export declare function resourceNames(attemptHex: string, perAttemptCaches?: readonly string[]): ResourceNames;
186
+ export declare function dockerObjectId(output: string, kind: "container" | "network"): string;
187
+ export declare function sandboxRuntimeVolumes(resources: ResourceNames, includeEgressPolicy?: boolean, includeCredentials?: boolean): SandboxRuntimeVolume[];
188
+ export declare function sandboxRuntimeCleanupVolumeNames(resources: ResourceNames, includeCredentials?: boolean): string[];
189
+ export declare function mayReapManagedResource(labels: Readonly<Record<string, string>>, runnerId: string, liveAttempts: ReadonlySet<string>): boolean;
190
+ export declare function shouldReapSandboxLedger(ledger: Readonly<Pick<SandboxLedger, "dispatchAttemptId" | "fencingToken" | "runnerInstanceId" | "expiresAt">>, currentRunnerInstanceId: string, serverAttempt: SandboxAttemptSnapshot | undefined, snapshotAvailable: boolean, nowMs?: number): boolean;
191
+ export declare function prepareOverlayBuildRoot(overlayRoot: string): Promise<void>;
192
+ export declare function extractOciLayoutArchive(archivePath: string, destination: string, signal?: AbortSignal): Promise<void>;
193
+ export declare function ociLayoutContextRef(layoutPath: string): string;
194
+ export declare function formatOciExtractionFailure(result: {
195
+ readonly stdout: string;
196
+ readonly stderr: string;
197
+ readonly truncated: boolean;
198
+ }): string;
199
+ export declare function readOciLayoutImageDigest(layoutRoot: string): Promise<string>;
200
+ export declare function removeExtractedOciLayout(layoutRoot: string): Promise<void>;
201
+ export declare function ociLayoutScanTarget(layoutPath: string): string;
202
+ export declare function agentOverlayDockerignore(): string;
203
+ export declare function agentOverlayDockerfile(toolingImage: string, projectContextName: string, installMode: BridgePackageInstallMode, codexVersion: string): string;
204
+ export type BridgePackageInstallMode = "ci" | "install";
205
+ export declare function copyBridgePackageManifests(packageRoot: string, bridgeRoot: string): Promise<BridgePackageInstallMode>;
206
+ export declare function bridgePackageInstallCommand(mode: BridgePackageInstallMode, codexVersion: string): string;
207
+ export declare function buildxBuildArgs(builder: string, metadataFile: string, tail: readonly string[], environment?: NodeJS.ProcessEnv, options?: {
208
+ readonly load?: boolean;
209
+ }): string[];
210
+ export declare function buildResourcePolicy(environment?: NodeJS.ProcessEnv): {
211
+ readonly nofile: number;
212
+ readonly sharedMemory: string;
213
+ readonly builderPidsLimit: number;
214
+ };
215
+ export declare function dedicatedBuildxBuilderName(runnerId: string, dockerContext: string | null | undefined): string;
216
+ export declare function dedicatedBuildxBuilderContainerName(builder: string): string;
217
+ export declare function selectImageGcCandidates(records: readonly ImageCacheRecord[], keepPerProject: number, ttlMs: number, nowMs: number): ImageCacheRecord[];
218
+ export declare function workerCreateArgs(dispatch: RunOnceDispatchPacket, contract: ValidatedSandboxContract, resources: ResourceNames, labels: Record<string, string>, image: string, securityOptions?: readonly string[]): string[];
219
+ export declare function cacheVolumeArgs(contract: ValidatedSandboxContract, resources: ResourceNames): string[];
220
+ export declare function proxyEnvironmentArgs(dispatch: RunOnceDispatchPacket, contract: ValidatedSandboxContract, resources: ResourceNames): string[];
221
+ export declare function egressProxyConfig(allowedHosts: readonly string[]): string;
222
+ export declare function serviceContainerArgs(service: SandboxService, image: string, resources: ResourceNames, labels: Record<string, string>, securityOptions: readonly string[], environment?: NodeJS.ProcessEnv): string[];
223
+ export declare function resolveSandboxSecurityOptions(executionPolicy: string, environment?: NodeJS.ProcessEnv): string[];
224
+ export declare function networkCreateArgs(dispatch: RunOnceDispatchPacket, environment?: NodeJS.ProcessEnv): string[];
225
+ export declare function classifySandboxRuntimeFailure(value: string): {
226
+ readonly code: string;
227
+ readonly phase: string;
228
+ readonly message: string;
229
+ };
230
+ export declare function classifySandboxResourceFailure(value: string): {
231
+ readonly code: string;
232
+ readonly message: string;
233
+ } | null;
234
+ export {};