self-bench 0.3.4 → 0.3.6
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.
- package/README.md +16 -14
- package/biome.json +7 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +7 -7
- package/dist/api.js.map +1 -1
- package/dist/build-metadata.js +1 -1
- package/dist/cli.js +75 -15
- package/dist/cli.js.map +1 -1
- package/dist/config.js +1 -1
- package/dist/contracts.d.ts +1 -0
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +6 -5
- package/dist/contracts.js.map +1 -1
- package/dist/extensions/discovery.d.ts.map +1 -1
- package/dist/extensions/discovery.js +12 -5
- package/dist/extensions/discovery.js.map +1 -1
- package/dist/harbor-task.d.ts.map +1 -1
- package/dist/harbor-task.js +3 -1
- package/dist/harbor-task.js.map +1 -1
- package/dist/project-paths.d.ts +2 -0
- package/dist/project-paths.d.ts.map +1 -0
- package/dist/project-paths.js +15 -0
- package/dist/project-paths.js.map +1 -0
- package/dist/provenance-associations.d.ts +49 -0
- package/dist/provenance-associations.d.ts.map +1 -0
- package/dist/provenance-associations.js +220 -0
- package/dist/provenance-associations.js.map +1 -0
- package/dist/provenance.d.ts +34 -13
- package/dist/provenance.d.ts.map +1 -1
- package/dist/provenance.js +81 -9
- package/dist/provenance.js.map +1 -1
- package/dist/reaudit-main.js +3 -3
- package/dist/reaudit-main.js.map +1 -1
- package/dist/repair-main.js +4 -4
- package/dist/repair-main.js.map +1 -1
- package/dist/{sandbox.d.ts → sandbox/contracts.d.ts} +5 -3
- package/dist/sandbox/contracts.d.ts.map +1 -0
- package/dist/sandbox/contracts.js +9 -0
- package/dist/sandbox/contracts.js.map +1 -0
- package/dist/sandbox/index.d.ts +5 -0
- package/dist/sandbox/index.d.ts.map +1 -0
- package/dist/{sandbox.js → sandbox/index.js} +6 -5
- package/dist/sandbox/index.js.map +1 -0
- package/dist/sandbox/programs/author.d.ts +3 -0
- package/dist/sandbox/programs/author.d.ts.map +1 -0
- package/dist/{sandbox-author.js → sandbox/programs/author.js} +2 -2
- package/dist/sandbox/programs/author.js.map +1 -0
- package/dist/sandbox/programs/prepare-repair.d.ts +7 -0
- package/dist/sandbox/programs/prepare-repair.d.ts.map +1 -0
- package/dist/sandbox/programs/prepare-repair.js +30 -0
- package/dist/sandbox/programs/prepare-repair.js.map +1 -0
- package/dist/sandbox/programs/repair.d.ts +3 -0
- package/dist/sandbox/programs/repair.d.ts.map +1 -0
- package/dist/{sandbox-repair.js → sandbox/programs/repair.js} +7 -28
- package/dist/sandbox/programs/repair.js.map +1 -0
- package/dist/sandbox/programs/review.d.ts +2 -0
- package/dist/sandbox/programs/review.d.ts.map +1 -0
- package/dist/{sandbox-review.js → sandbox/programs/review.js} +2 -2
- package/dist/sandbox/programs/review.js.map +1 -0
- package/dist/sandbox/programs/validation-repair.d.ts +3 -0
- package/dist/sandbox/programs/validation-repair.d.ts.map +1 -0
- package/dist/{sandbox-validation-repair.js → sandbox/programs/validation-repair.js} +7 -28
- package/dist/sandbox/programs/validation-repair.js.map +1 -0
- package/dist/{docker-executor.d.ts → sandbox/providers/docker/executor.d.ts} +3 -3
- package/dist/sandbox/providers/docker/executor.d.ts.map +1 -0
- package/dist/{docker-executor.js → sandbox/providers/docker/executor.js} +2 -2
- package/dist/sandbox/providers/docker/executor.js.map +1 -0
- package/dist/{modal-executor.d.ts → sandbox/providers/modal/executor.d.ts} +3 -3
- package/dist/sandbox/providers/modal/executor.d.ts.map +1 -0
- package/dist/{modal-executor.js → sandbox/providers/modal/executor.js} +50 -19
- package/dist/sandbox/providers/modal/executor.js.map +1 -0
- package/dist/{vercel-command.d.ts → sandbox/providers/vercel/command.d.ts} +3 -3
- package/dist/sandbox/providers/vercel/command.d.ts.map +1 -0
- package/dist/{vercel-command.js → sandbox/providers/vercel/command.js} +3 -3
- package/dist/sandbox/providers/vercel/command.js.map +1 -0
- package/dist/{vercel-executor.d.ts → sandbox/providers/vercel/executor.d.ts} +3 -3
- package/dist/sandbox/providers/vercel/executor.d.ts.map +1 -0
- package/dist/{vercel-executor.js → sandbox/providers/vercel/executor.js} +5 -5
- package/dist/sandbox/providers/vercel/executor.js.map +1 -0
- package/dist/{vercel-fetch.d.ts → sandbox/providers/vercel/fetch.d.ts} +1 -1
- package/dist/sandbox/providers/vercel/fetch.d.ts.map +1 -0
- package/dist/{vercel-fetch.js → sandbox/providers/vercel/fetch.js} +1 -1
- package/dist/sandbox/providers/vercel/fetch.js.map +1 -0
- package/dist/{sandbox-timeout.d.ts → sandbox/timeout.d.ts} +2 -2
- package/dist/sandbox/timeout.d.ts.map +1 -0
- package/dist/{sandbox-timeout.js → sandbox/timeout.js} +1 -1
- package/dist/sandbox/timeout.js.map +1 -0
- package/dist/sandbox-author.bundle.js +11 -8
- package/dist/sandbox-repair.bundle.js +43 -35
- package/dist/sandbox-review.bundle.js +2 -2
- package/dist/sandbox-validation-repair.bundle.js +50 -41
- package/dist/{vercel-cli.d.ts → setup/vercel/cli.d.ts} +2 -2
- package/dist/setup/vercel/cli.d.ts.map +1 -0
- package/dist/{vercel-cli.js → setup/vercel/cli.js} +2 -2
- package/dist/setup/vercel/cli.js.map +1 -0
- package/dist/setup/vercel/index.d.ts +4 -0
- package/dist/setup/vercel/index.d.ts.map +1 -0
- package/dist/setup/vercel/index.js +3 -0
- package/dist/setup/vercel/index.js.map +1 -0
- package/dist/{vercel-setup-probe.d.ts → setup/vercel/probe.d.ts} +2 -2
- package/dist/setup/vercel/probe.d.ts.map +1 -0
- package/dist/{vercel-setup-probe.js → setup/vercel/probe.js} +2 -2
- package/dist/setup/vercel/probe.js.map +1 -0
- package/dist/{vercel-profile.d.ts → setup/vercel/profile.d.ts} +1 -1
- package/dist/setup/vercel/profile.d.ts.map +1 -0
- package/dist/{vercel-profile.js → setup/vercel/profile.js} +2 -2
- package/dist/setup/vercel/profile.js.map +1 -0
- package/dist/{vercel-runtime-image.d.ts → setup/vercel/runtime-image.d.ts} +5 -5
- package/dist/setup/vercel/runtime-image.d.ts.map +1 -0
- package/dist/{vercel-runtime-image.js → setup/vercel/runtime-image.js} +2 -2
- package/dist/setup/vercel/runtime-image.js.map +1 -0
- package/dist/{vercel-setup.d.ts → setup/vercel/setup.d.ts} +8 -8
- package/dist/setup/vercel/setup.d.ts.map +1 -0
- package/dist/{vercel-setup.js → setup/vercel/setup.js} +8 -8
- package/dist/setup/vercel/setup.js.map +1 -0
- package/dist/temporal/activities.d.ts +12 -0
- package/dist/temporal/activities.d.ts.map +1 -1
- package/dist/temporal/activities.js +80 -33
- package/dist/temporal/activities.js.map +1 -1
- package/dist/temporal/workflow.d.ts +1 -1
- package/dist/temporal/workflow.d.ts.map +1 -1
- package/dist/temporal/workflow.js +90 -65
- package/dist/temporal/workflow.js.map +1 -1
- package/docs/operations.md +39 -5
- package/docs/task-construction.md +3 -1
- package/package.json +7 -5
- package/review/index.html +14 -0
- package/review/src/App.tsx +338 -0
- package/review/src/export-loader.test.ts +57 -0
- package/review/src/export-loader.ts +151 -0
- package/review/src/main.tsx +15 -0
- package/review/src/styles.css +162 -0
- package/review/src/types.ts +47 -0
- package/review/tsconfig.json +16 -0
- package/review/vite.config.ts +20 -0
- package/scripts/build-sandbox-programs.ts +22 -0
- package/scripts/verify-package.ts +52 -7
- package/scripts/write-build-metadata.ts +1 -1
- package/src/api.ts +7 -6
- package/src/cli.ts +99 -15
- package/src/config.ts +1 -1
- package/src/contracts.ts +7 -5
- package/src/extensions/discovery.ts +14 -5
- package/src/harbor-task.ts +6 -1
- package/src/project-paths.ts +15 -0
- package/src/provenance-associations.ts +311 -0
- package/src/provenance.ts +132 -28
- package/src/reaudit-main.ts +3 -3
- package/src/repair-main.ts +4 -4
- package/src/{sandbox.ts → sandbox/contracts.ts} +11 -20
- package/src/sandbox/index.ts +23 -0
- package/src/{sandbox-author.ts → sandbox/programs/author.ts} +1 -1
- package/src/sandbox/programs/prepare-repair.ts +44 -0
- package/src/{sandbox-repair.ts → sandbox/programs/repair.ts} +7 -31
- package/src/{sandbox-review.ts → sandbox/programs/review.ts} +1 -1
- package/src/{sandbox-validation-repair.ts → sandbox/programs/validation-repair.ts} +6 -30
- package/src/{docker-executor.ts → sandbox/providers/docker/executor.ts} +3 -3
- package/src/{modal-executor.ts → sandbox/providers/modal/executor.ts} +66 -25
- package/src/{vercel-command.ts → sandbox/providers/vercel/command.ts} +3 -3
- package/src/{vercel-executor.ts → sandbox/providers/vercel/executor.ts} +5 -5
- package/src/{sandbox-timeout.ts → sandbox/timeout.ts} +1 -1
- package/src/{vercel-cli.ts → setup/vercel/cli.ts} +1 -1
- package/src/setup/vercel/index.ts +3 -0
- package/src/{vercel-setup-probe.ts → setup/vercel/probe.ts} +5 -2
- package/src/{vercel-profile.ts → setup/vercel/profile.ts} +4 -1
- package/src/{vercel-runtime-image.ts → setup/vercel/runtime-image.ts} +5 -5
- package/src/{vercel-setup.ts → setup/vercel/setup.ts} +8 -8
- package/src/temporal/activities.ts +164 -63
- package/src/temporal/workflow.ts +120 -71
- package/tsconfig.json +1 -1
- package/dist/docker-executor.d.ts.map +0 -1
- package/dist/docker-executor.js.map +0 -1
- package/dist/modal-executor.d.ts.map +0 -1
- package/dist/modal-executor.js.map +0 -1
- package/dist/sandbox-author.d.ts +0 -3
- package/dist/sandbox-author.d.ts.map +0 -1
- package/dist/sandbox-author.js.map +0 -1
- package/dist/sandbox-repair.d.ts +0 -3
- package/dist/sandbox-repair.d.ts.map +0 -1
- package/dist/sandbox-repair.js.map +0 -1
- package/dist/sandbox-review.d.ts +0 -2
- package/dist/sandbox-review.d.ts.map +0 -1
- package/dist/sandbox-review.js.map +0 -1
- package/dist/sandbox-timeout.d.ts.map +0 -1
- package/dist/sandbox-timeout.js.map +0 -1
- package/dist/sandbox-validation-repair.d.ts +0 -3
- package/dist/sandbox-validation-repair.d.ts.map +0 -1
- package/dist/sandbox-validation-repair.js.map +0 -1
- package/dist/sandbox.d.ts.map +0 -1
- package/dist/sandbox.js.map +0 -1
- package/dist/vercel-cli.d.ts.map +0 -1
- package/dist/vercel-cli.js.map +0 -1
- package/dist/vercel-command.d.ts.map +0 -1
- package/dist/vercel-command.js.map +0 -1
- package/dist/vercel-executor.d.ts.map +0 -1
- package/dist/vercel-executor.js.map +0 -1
- package/dist/vercel-fetch.d.ts.map +0 -1
- package/dist/vercel-fetch.js.map +0 -1
- package/dist/vercel-profile.d.ts.map +0 -1
- package/dist/vercel-profile.js.map +0 -1
- package/dist/vercel-runtime-image.d.ts.map +0 -1
- package/dist/vercel-runtime-image.js.map +0 -1
- package/dist/vercel-setup-probe.d.ts.map +0 -1
- package/dist/vercel-setup-probe.js.map +0 -1
- package/dist/vercel-setup.d.ts.map +0 -1
- package/dist/vercel-setup.js.map +0 -1
- /package/src/{vercel-fetch.ts → sandbox/providers/vercel/fetch.ts} +0 -0
package/src/harbor-task.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { runCommand } from "./process.js";
|
|
|
6
6
|
import { patchPaths } from "./repair.js";
|
|
7
7
|
|
|
8
8
|
const HARBOR_SCHEMA_VERSION = "1.4";
|
|
9
|
-
const COMPILER_REVISION =
|
|
9
|
+
const COMPILER_REVISION = 24;
|
|
10
10
|
|
|
11
11
|
export interface AuthoredTaskFiles {
|
|
12
12
|
readonly definition: TaskDefinition;
|
|
@@ -70,6 +70,10 @@ export async function compileHarborTask(
|
|
|
70
70
|
await Promise.all([
|
|
71
71
|
cp(snapshot, join(environment, "repo.tar.gz")),
|
|
72
72
|
cp(snapshot, join(tests, "repo.tar.gz")),
|
|
73
|
+
writeFile(
|
|
74
|
+
join(outputDirectory, "definition.json"),
|
|
75
|
+
`${JSON.stringify(task.definition, null, 2)}\n`,
|
|
76
|
+
),
|
|
73
77
|
writeFile(join(outputDirectory, "instruction.md"), `${task.definition.prompt.trim()}\n`),
|
|
74
78
|
writeFile(join(solution, "gold.patch"), task.goldPatch),
|
|
75
79
|
writeFile(join(solution, "solve.sh"), solutionScript()),
|
|
@@ -127,6 +131,7 @@ export async function refreshHarborTask(
|
|
|
127
131
|
const preinstallGoldDependencies = dependencySetupPatch.length > 0;
|
|
128
132
|
await writeFile(join(outputDirectory, "tests/test.sh"), testScript(definition, testPatch));
|
|
129
133
|
await Promise.all([
|
|
134
|
+
writeFile(join(outputDirectory, "definition.json"), `${JSON.stringify(definition, null, 2)}\n`),
|
|
130
135
|
writeFile(join(outputDirectory, "task.toml"), taskToml(definition)),
|
|
131
136
|
writeFile(join(outputDirectory, "environment/Dockerfile"), agentDockerfile(definition)),
|
|
132
137
|
writeFile(
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { dirname, join, parse } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
export function projectRoot(moduleUrl: string): string {
|
|
6
|
+
let directory = dirname(fileURLToPath(moduleUrl));
|
|
7
|
+
const root = parse(directory).root;
|
|
8
|
+
while (directory !== root) {
|
|
9
|
+
if (existsSync(join(directory, "package.json"))) {
|
|
10
|
+
return directory;
|
|
11
|
+
}
|
|
12
|
+
directory = dirname(directory);
|
|
13
|
+
}
|
|
14
|
+
throw new Error(`could not find package.json above ${fileURLToPath(moduleUrl)}`);
|
|
15
|
+
}
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { assertPullRequestBelongsToRepository, githubRepository } from "./github.js";
|
|
4
|
+
import { sha256 } from "./hash.js";
|
|
5
|
+
import { runCommand } from "./process.js";
|
|
6
|
+
import type {
|
|
7
|
+
LocalSessionMetadata,
|
|
8
|
+
ProvenanceMessage,
|
|
9
|
+
SessionProvenanceFormat,
|
|
10
|
+
} from "./provenance.js";
|
|
11
|
+
|
|
12
|
+
const localSourceTypeSchema = z.enum(["pi", "claude-code", "codex"]);
|
|
13
|
+
type LocalSourceType = z.infer<typeof localSourceTypeSchema>;
|
|
14
|
+
|
|
15
|
+
const associationMessageSchema = z
|
|
16
|
+
.object({
|
|
17
|
+
sourceType: localSourceTypeSchema,
|
|
18
|
+
sessionId: z.string().min(1),
|
|
19
|
+
messageIndex: z.number().int().nonnegative(),
|
|
20
|
+
contentSha256: z.string().regex(/^[0-9a-f]{64}$/),
|
|
21
|
+
})
|
|
22
|
+
.strict();
|
|
23
|
+
|
|
24
|
+
export const provenanceAssociationManifestSchema = z
|
|
25
|
+
.object({
|
|
26
|
+
schemaVersion: z.literal(1),
|
|
27
|
+
repository: z.string().regex(/^[^/]+\/[^/]+$/),
|
|
28
|
+
sourcePr: z.number().int().positive(),
|
|
29
|
+
sourceUrl: z.string().url(),
|
|
30
|
+
messages: z.array(associationMessageSchema).min(1),
|
|
31
|
+
})
|
|
32
|
+
.strict();
|
|
33
|
+
|
|
34
|
+
export type ProvenanceAssociationManifest = z.infer<typeof provenanceAssociationManifestSchema>;
|
|
35
|
+
|
|
36
|
+
export interface AssociationSessionSummary {
|
|
37
|
+
readonly selector: string;
|
|
38
|
+
readonly sourceType: LocalSourceType;
|
|
39
|
+
readonly sessionId: string;
|
|
40
|
+
readonly messageCount: number;
|
|
41
|
+
readonly modifiedAt?: string;
|
|
42
|
+
readonly paths?: readonly string[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface MergedPullRequest {
|
|
46
|
+
readonly sourcePr: number;
|
|
47
|
+
readonly sourceUrl: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function associationSessionSummaries(
|
|
51
|
+
messages: readonly ProvenanceMessage[],
|
|
52
|
+
metadata: readonly LocalSessionMetadata[] = [],
|
|
53
|
+
): AssociationSessionSummary[] {
|
|
54
|
+
const metadataBySelector = new Map<string, { modifiedAt: string; paths: Set<string> }>();
|
|
55
|
+
for (const session of metadata) {
|
|
56
|
+
if (!isLocalSourceType(session.sourceType)) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const selector = sessionSelector(session.sourceType, session.sessionId);
|
|
60
|
+
const existing = metadataBySelector.get(selector);
|
|
61
|
+
metadataBySelector.set(selector, {
|
|
62
|
+
modifiedAt:
|
|
63
|
+
!existing || session.modifiedAt > existing.modifiedAt
|
|
64
|
+
? session.modifiedAt
|
|
65
|
+
: existing.modifiedAt,
|
|
66
|
+
paths: new Set([...(existing?.paths ?? []), session.path]),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const counts = new Map<string, AssociationSessionSummary>();
|
|
71
|
+
for (const message of messages) {
|
|
72
|
+
if (!isLocalSourceType(message.sourceType)) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const selector = sessionSelector(message.sourceType, message.sessionId);
|
|
76
|
+
const existing = counts.get(selector);
|
|
77
|
+
const sessionMetadata = metadataBySelector.get(selector);
|
|
78
|
+
counts.set(selector, {
|
|
79
|
+
selector,
|
|
80
|
+
sourceType: message.sourceType,
|
|
81
|
+
sessionId: message.sessionId,
|
|
82
|
+
messageCount: (existing?.messageCount ?? 0) + 1,
|
|
83
|
+
...(sessionMetadata
|
|
84
|
+
? {
|
|
85
|
+
modifiedAt: sessionMetadata.modifiedAt,
|
|
86
|
+
paths: [...sessionMetadata.paths].sort(),
|
|
87
|
+
}
|
|
88
|
+
: {}),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return [...counts.values()].sort(
|
|
92
|
+
(left, right) =>
|
|
93
|
+
(right.modifiedAt ?? "").localeCompare(left.modifiedAt ?? "") ||
|
|
94
|
+
left.selector.localeCompare(right.selector),
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function createProvenanceAssociationManifest(input: {
|
|
99
|
+
readonly repositoryUrl: string;
|
|
100
|
+
readonly pullRequest: MergedPullRequest;
|
|
101
|
+
readonly messages: readonly ProvenanceMessage[];
|
|
102
|
+
readonly sessionSelectors: readonly string[];
|
|
103
|
+
}): ProvenanceAssociationManifest {
|
|
104
|
+
const repository = githubRepository(input.repositoryUrl);
|
|
105
|
+
assertPullRequestBelongsToRepository(
|
|
106
|
+
input.repositoryUrl,
|
|
107
|
+
input.pullRequest.sourceUrl,
|
|
108
|
+
input.pullRequest.sourcePr,
|
|
109
|
+
);
|
|
110
|
+
if (input.sessionSelectors.length === 0) {
|
|
111
|
+
throw new Error("at least one --session is required to create an association manifest");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const selected = new Set(input.sessionSelectors.map(parseSessionSelector));
|
|
115
|
+
if (selected.size !== input.sessionSelectors.length) {
|
|
116
|
+
throw new Error("each --session selector must be unique");
|
|
117
|
+
}
|
|
118
|
+
const available = new Set(
|
|
119
|
+
associationSessionSummaries(input.messages).map(({ selector }) => selector),
|
|
120
|
+
);
|
|
121
|
+
for (const selector of selected) {
|
|
122
|
+
if (!available.has(selector)) {
|
|
123
|
+
throw new Error(`local session ${selector} was not found for this repository`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const messageKeys = new Set<string>();
|
|
128
|
+
const messages = input.messages
|
|
129
|
+
.filter(
|
|
130
|
+
(message): message is ProvenanceMessage & { sourceType: LocalSourceType } =>
|
|
131
|
+
isLocalSourceType(message.sourceType) &&
|
|
132
|
+
selected.has(sessionSelector(message.sourceType, message.sessionId)),
|
|
133
|
+
)
|
|
134
|
+
.map((message) => {
|
|
135
|
+
const key = messageKey(message.sourceType, message.sessionId, message.messageIndex);
|
|
136
|
+
if (messageKeys.has(key)) {
|
|
137
|
+
throw new Error(`local provenance contains duplicate message identity ${key}`);
|
|
138
|
+
}
|
|
139
|
+
messageKeys.add(key);
|
|
140
|
+
return {
|
|
141
|
+
sourceType: message.sourceType,
|
|
142
|
+
sessionId: message.sessionId,
|
|
143
|
+
messageIndex: message.messageIndex,
|
|
144
|
+
contentSha256: sha256(message.content),
|
|
145
|
+
};
|
|
146
|
+
})
|
|
147
|
+
.sort(compareAssociationMessages);
|
|
148
|
+
|
|
149
|
+
return provenanceAssociationManifestSchema.parse({
|
|
150
|
+
schemaVersion: 1,
|
|
151
|
+
repository,
|
|
152
|
+
sourcePr: input.pullRequest.sourcePr,
|
|
153
|
+
sourceUrl: input.pullRequest.sourceUrl,
|
|
154
|
+
messages,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export async function writeProvenanceAssociationManifest(
|
|
159
|
+
path: string,
|
|
160
|
+
manifest: ProvenanceAssociationManifest,
|
|
161
|
+
): Promise<void> {
|
|
162
|
+
await writeFile(path, `${JSON.stringify(manifest, null, 2)}\n`, { flag: "wx", mode: 0o600 });
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export async function applyProvenanceAssociationManifests(
|
|
166
|
+
messages: readonly ProvenanceMessage[],
|
|
167
|
+
repositoryUrl: string,
|
|
168
|
+
paths: readonly string[],
|
|
169
|
+
): Promise<ProvenanceMessage[]> {
|
|
170
|
+
if (paths.length === 0) {
|
|
171
|
+
return [...messages];
|
|
172
|
+
}
|
|
173
|
+
const repository = githubRepository(repositoryUrl);
|
|
174
|
+
const manifests = await Promise.all(
|
|
175
|
+
paths.map(async (path) => {
|
|
176
|
+
const value = JSON.parse(await readFile(path, "utf8"));
|
|
177
|
+
return { path, manifest: provenanceAssociationManifestSchema.parse(value) };
|
|
178
|
+
}),
|
|
179
|
+
);
|
|
180
|
+
const messageByKey = new Map<string, ProvenanceMessage>();
|
|
181
|
+
for (const message of messages) {
|
|
182
|
+
const key = messageKey(message.sourceType, message.sessionId, message.messageIndex);
|
|
183
|
+
if (messageByKey.has(key)) {
|
|
184
|
+
throw new Error(`local provenance contains duplicate message identity ${key}`);
|
|
185
|
+
}
|
|
186
|
+
messageByKey.set(key, message);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const associations = new Map<string, MergedPullRequest>();
|
|
190
|
+
for (const { path, manifest } of manifests) {
|
|
191
|
+
if (manifest.repository.toLowerCase() !== repository) {
|
|
192
|
+
throw new Error(
|
|
193
|
+
`association manifest ${path} belongs to ${manifest.repository}, not ${repository}`,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
assertPullRequestBelongsToRepository(repositoryUrl, manifest.sourceUrl, manifest.sourcePr);
|
|
197
|
+
for (const selector of new Set(
|
|
198
|
+
manifest.messages.map((message) => sessionSelector(message.sourceType, message.sessionId)),
|
|
199
|
+
)) {
|
|
200
|
+
const expected = manifest.messages
|
|
201
|
+
.filter((message) => sessionSelector(message.sourceType, message.sessionId) === selector)
|
|
202
|
+
.sort(compareAssociationMessages);
|
|
203
|
+
const current = messages
|
|
204
|
+
.filter(
|
|
205
|
+
(message): message is ProvenanceMessage & { sourceType: LocalSourceType } =>
|
|
206
|
+
isLocalSourceType(message.sourceType) &&
|
|
207
|
+
sessionSelector(message.sourceType, message.sessionId) === selector,
|
|
208
|
+
)
|
|
209
|
+
.map((message) => ({
|
|
210
|
+
sourceType: message.sourceType,
|
|
211
|
+
sessionId: message.sessionId,
|
|
212
|
+
messageIndex: message.messageIndex,
|
|
213
|
+
contentSha256: sha256(message.content),
|
|
214
|
+
}))
|
|
215
|
+
.sort(compareAssociationMessages);
|
|
216
|
+
if (JSON.stringify(current) !== JSON.stringify(expected)) {
|
|
217
|
+
throw new Error(`association manifest ${path} does not match local session ${selector}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
for (const reference of manifest.messages) {
|
|
221
|
+
const key = messageKey(reference.sourceType, reference.sessionId, reference.messageIndex);
|
|
222
|
+
const message = messageByKey.get(key);
|
|
223
|
+
if (!message || sha256(message.content) !== reference.contentSha256) {
|
|
224
|
+
throw new Error(`association manifest ${path} does not match local message ${key}`);
|
|
225
|
+
}
|
|
226
|
+
if (associations.has(key)) {
|
|
227
|
+
throw new Error(`local message ${key} is associated more than once`);
|
|
228
|
+
}
|
|
229
|
+
associations.set(key, {
|
|
230
|
+
sourcePr: manifest.sourcePr,
|
|
231
|
+
sourceUrl: manifest.sourceUrl,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return messages.map((message) => {
|
|
237
|
+
const association = associations.get(
|
|
238
|
+
messageKey(message.sourceType, message.sessionId, message.messageIndex),
|
|
239
|
+
);
|
|
240
|
+
return association ? { ...message, ...association } : message;
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export async function resolveMergedPullRequest(
|
|
245
|
+
repositoryUrl: string,
|
|
246
|
+
sourcePr: number,
|
|
247
|
+
): Promise<MergedPullRequest> {
|
|
248
|
+
const repository = githubRepository(repositoryUrl);
|
|
249
|
+
const result = await runCommand("gh", [
|
|
250
|
+
"pr",
|
|
251
|
+
"view",
|
|
252
|
+
String(sourcePr),
|
|
253
|
+
"--repo",
|
|
254
|
+
repository,
|
|
255
|
+
"--json",
|
|
256
|
+
"number,url,state,mergedAt",
|
|
257
|
+
]);
|
|
258
|
+
const parsed = z
|
|
259
|
+
.object({
|
|
260
|
+
number: z.number().int().positive(),
|
|
261
|
+
url: z.string().url(),
|
|
262
|
+
state: z.string(),
|
|
263
|
+
mergedAt: z.string().nullable(),
|
|
264
|
+
})
|
|
265
|
+
.parse(JSON.parse(result.stdout));
|
|
266
|
+
if (parsed.number !== sourcePr) {
|
|
267
|
+
throw new Error(`GitHub returned pull request ${parsed.number}; expected ${sourcePr}`);
|
|
268
|
+
}
|
|
269
|
+
assertPullRequestBelongsToRepository(repositoryUrl, parsed.url, sourcePr);
|
|
270
|
+
if (parsed.state !== "MERGED" || !parsed.mergedAt) {
|
|
271
|
+
throw new Error(`pull request ${repository}#${sourcePr} is not merged`);
|
|
272
|
+
}
|
|
273
|
+
return { sourcePr, sourceUrl: parsed.url };
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function parseSessionSelector(value: string): string {
|
|
277
|
+
const separator = value.indexOf(":");
|
|
278
|
+
if (separator < 1 || separator === value.length - 1) {
|
|
279
|
+
throw new Error(`invalid session selector ${JSON.stringify(value)}; expected TYPE:SESSION_ID`);
|
|
280
|
+
}
|
|
281
|
+
const sourceType = value.slice(0, separator);
|
|
282
|
+
localSourceTypeSchema.parse(sourceType);
|
|
283
|
+
return sessionSelector(sourceType as LocalSourceType, value.slice(separator + 1));
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function sessionSelector(sourceType: LocalSourceType, sessionId: string): string {
|
|
287
|
+
return `${sourceType}:${sessionId}`;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function messageKey(
|
|
291
|
+
sourceType: SessionProvenanceFormat | "github-pull-request",
|
|
292
|
+
sessionId: string,
|
|
293
|
+
messageIndex: number,
|
|
294
|
+
): string {
|
|
295
|
+
return `${sourceType}:${sessionId}:${messageIndex}`;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function compareAssociationMessages(
|
|
299
|
+
left: z.infer<typeof associationMessageSchema>,
|
|
300
|
+
right: z.infer<typeof associationMessageSchema>,
|
|
301
|
+
): number {
|
|
302
|
+
return (
|
|
303
|
+
left.sourceType.localeCompare(right.sourceType) ||
|
|
304
|
+
left.sessionId.localeCompare(right.sessionId) ||
|
|
305
|
+
left.messageIndex - right.messageIndex
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function isLocalSourceType(value: string): value is LocalSourceType {
|
|
310
|
+
return value === "pi" || value === "claude-code" || value === "codex";
|
|
311
|
+
}
|
package/src/provenance.ts
CHANGED
|
@@ -1,23 +1,50 @@
|
|
|
1
|
-
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
-
import { basename, join } from "node:path";
|
|
1
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
|
+
import { basename, join, relative } from "node:path";
|
|
3
|
+
import { z } from "zod";
|
|
3
4
|
import { assertPullRequestBelongsToRepository, githubRepository } from "./github.js";
|
|
4
5
|
import { runCommand } from "./process.js";
|
|
5
6
|
|
|
6
7
|
export type SessionProvenanceFormat = "codex" | "claude-code" | "pi" | "generic";
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
const provenanceMessageBaseSchema = z.object({
|
|
10
|
+
sessionId: z.string().min(1),
|
|
11
|
+
messageIndex: z.number().int().nonnegative(),
|
|
12
|
+
content: z.string().min(1),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const localProvenanceMessageSchema = provenanceMessageBaseSchema
|
|
16
|
+
.extend({
|
|
17
|
+
sourceType: z.enum(["codex", "claude-code", "pi", "generic"]),
|
|
18
|
+
sourcePr: z.number().int().positive().optional(),
|
|
19
|
+
sourceUrl: z.string().url().optional(),
|
|
20
|
+
})
|
|
21
|
+
.refine(
|
|
22
|
+
(message) => (message.sourcePr === undefined) === (message.sourceUrl === undefined),
|
|
23
|
+
"sourcePr and sourceUrl must be supplied together",
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
export const provenanceMessageSchema = z.union([
|
|
27
|
+
localProvenanceMessageSchema,
|
|
28
|
+
provenanceMessageBaseSchema.extend({
|
|
29
|
+
sourceType: z.literal("github-pull-request"),
|
|
30
|
+
sourcePr: z.number().int().positive(),
|
|
31
|
+
sourceUrl: z.string().url(),
|
|
32
|
+
}),
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
export type ProvenanceMessage = z.infer<typeof provenanceMessageSchema>;
|
|
36
|
+
|
|
37
|
+
export interface LocalSessionMetadata {
|
|
38
|
+
readonly sourceType: SessionProvenanceFormat;
|
|
9
39
|
readonly sessionId: string;
|
|
10
|
-
readonly
|
|
11
|
-
readonly
|
|
40
|
+
readonly path: string;
|
|
41
|
+
readonly modifiedAt: string;
|
|
12
42
|
}
|
|
13
43
|
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
readonly sourcePr: number;
|
|
19
|
-
readonly sourceUrl: string;
|
|
20
|
-
});
|
|
44
|
+
export interface RepositoryProvenanceCollection {
|
|
45
|
+
readonly messages: readonly ProvenanceMessage[];
|
|
46
|
+
readonly sessions: readonly LocalSessionMetadata[];
|
|
47
|
+
}
|
|
21
48
|
|
|
22
49
|
interface JsonRecord {
|
|
23
50
|
readonly [key: string]: unknown;
|
|
@@ -51,9 +78,19 @@ export async function collectRepositoryProvenance(
|
|
|
51
78
|
repositoryPath: string,
|
|
52
79
|
homeDirectory: string,
|
|
53
80
|
): Promise<ProvenanceMessage[]> {
|
|
81
|
+
return [
|
|
82
|
+
...(await collectRepositoryProvenanceWithMetadata(repositoryPath, homeDirectory)).messages,
|
|
83
|
+
];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function collectRepositoryProvenanceWithMetadata(
|
|
87
|
+
repositoryPath: string,
|
|
88
|
+
homeDirectory: string,
|
|
89
|
+
): Promise<RepositoryProvenanceCollection> {
|
|
54
90
|
const worktrees = await repositoryWorktrees(repositoryPath);
|
|
55
91
|
const encodedWorktrees = worktrees.flatMap((path) => [path, encodeSessionPath(path)]);
|
|
56
92
|
const messages: ProvenanceMessage[] = [];
|
|
93
|
+
const sessions: LocalSessionMetadata[] = [];
|
|
57
94
|
|
|
58
95
|
for (const [format, relativeRoot] of SOURCE_ROOTS) {
|
|
59
96
|
const root = join(homeDirectory, relativeRoot);
|
|
@@ -65,29 +102,54 @@ export async function collectRepositoryProvenance(
|
|
|
65
102
|
) {
|
|
66
103
|
continue;
|
|
67
104
|
}
|
|
68
|
-
|
|
105
|
+
const extracted = extractProvenanceMessages(raw, format, basename(path));
|
|
106
|
+
if (extracted.length === 0) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
messages.push(...extracted);
|
|
110
|
+
const file = await stat(path).catch(() => undefined);
|
|
111
|
+
if (file) {
|
|
112
|
+
const sessionIds = new Set(extracted.map((message) => message.sessionId));
|
|
113
|
+
for (const sessionId of sessionIds) {
|
|
114
|
+
sessions.push({
|
|
115
|
+
sourceType: format,
|
|
116
|
+
sessionId,
|
|
117
|
+
path: displaySessionPath(path, homeDirectory),
|
|
118
|
+
modifiedAt: file.mtime.toISOString(),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
69
122
|
}
|
|
70
123
|
}
|
|
71
124
|
|
|
72
|
-
return deduplicateMessages(messages);
|
|
125
|
+
return { messages: deduplicateMessages(messages), sessions };
|
|
73
126
|
}
|
|
74
127
|
|
|
75
128
|
export async function collectGitHubPullRequestProvenance(
|
|
76
129
|
repositoryUrl: string,
|
|
130
|
+
token?: string,
|
|
131
|
+
signal?: AbortSignal,
|
|
77
132
|
): Promise<ProvenanceMessage[]> {
|
|
78
133
|
const repository = githubRepository(repositoryUrl);
|
|
79
|
-
const result = await runCommand(
|
|
80
|
-
"
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
134
|
+
const result = await runCommand(
|
|
135
|
+
"gh",
|
|
136
|
+
[
|
|
137
|
+
"pr",
|
|
138
|
+
"list",
|
|
139
|
+
"--repo",
|
|
140
|
+
repository,
|
|
141
|
+
"--state",
|
|
142
|
+
"merged",
|
|
143
|
+
"--limit",
|
|
144
|
+
String(GITHUB_PULL_REQUEST_LIMIT),
|
|
145
|
+
"--json",
|
|
146
|
+
"number,title,body,url,author,isDraft,additions,deletions,changedFiles",
|
|
147
|
+
],
|
|
148
|
+
{
|
|
149
|
+
env: token ? { ...process.env, GH_TOKEN: token } : process.env,
|
|
150
|
+
...(signal ? { signal } : {}),
|
|
151
|
+
},
|
|
152
|
+
);
|
|
91
153
|
return extractGitHubPullRequestProvenance(result.stdout, repositoryUrl);
|
|
92
154
|
}
|
|
93
155
|
|
|
@@ -130,19 +192,55 @@ export function extractGitHubPullRequestProvenance(
|
|
|
130
192
|
return messages;
|
|
131
193
|
}
|
|
132
194
|
|
|
195
|
+
export function combineRunProvenance(
|
|
196
|
+
repositoryUrl: string,
|
|
197
|
+
local: readonly ProvenanceMessage[],
|
|
198
|
+
github: readonly ProvenanceMessage[],
|
|
199
|
+
): ProvenanceMessage[] {
|
|
200
|
+
const explicitlyAssociatedPrs = new Set<number>();
|
|
201
|
+
for (const message of local) {
|
|
202
|
+
if (message.sourcePr === undefined || message.sourceUrl === undefined) {
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
assertPullRequestBelongsToRepository(repositoryUrl, message.sourceUrl, message.sourcePr);
|
|
206
|
+
explicitlyAssociatedPrs.add(message.sourcePr);
|
|
207
|
+
}
|
|
208
|
+
return [
|
|
209
|
+
...local,
|
|
210
|
+
...github.filter(
|
|
211
|
+
(message) =>
|
|
212
|
+
message.sourceType !== "github-pull-request" ||
|
|
213
|
+
!explicitlyAssociatedPrs.has(message.sourcePr),
|
|
214
|
+
),
|
|
215
|
+
];
|
|
216
|
+
}
|
|
217
|
+
|
|
133
218
|
export function assertProvenanceMatchesPullRequest(
|
|
134
219
|
message: ProvenanceMessage,
|
|
135
220
|
sourcePr: number,
|
|
136
221
|
sourceUrl: string,
|
|
222
|
+
provenance: readonly ProvenanceMessage[] = [message],
|
|
137
223
|
): void {
|
|
138
224
|
if (
|
|
139
|
-
message.
|
|
225
|
+
(message.sourcePr !== undefined || message.sourceUrl !== undefined) &&
|
|
140
226
|
(message.sourcePr !== sourcePr || message.sourceUrl !== sourceUrl)
|
|
141
227
|
) {
|
|
142
228
|
throw new Error(
|
|
143
229
|
`pull request ${sourceUrl}#${sourcePr} does not match provenance ${message.sourceUrl}#${message.sourcePr}`,
|
|
144
230
|
);
|
|
145
231
|
}
|
|
232
|
+
const hasExplicitLocalAssociation = provenance.some(
|
|
233
|
+
(item) => item.sourceType !== "github-pull-request" && item.sourcePr === sourcePr,
|
|
234
|
+
);
|
|
235
|
+
if (
|
|
236
|
+
hasExplicitLocalAssociation &&
|
|
237
|
+
message.sourceType !== "github-pull-request" &&
|
|
238
|
+
(message.sourcePr !== sourcePr || message.sourceUrl !== sourceUrl)
|
|
239
|
+
) {
|
|
240
|
+
throw new Error(
|
|
241
|
+
`pull request ${sourceUrl}#${sourcePr} has explicit local provenance; unbound local provenance cannot be selected`,
|
|
242
|
+
);
|
|
243
|
+
}
|
|
146
244
|
}
|
|
147
245
|
|
|
148
246
|
export function extractProvenanceMessages(
|
|
@@ -418,13 +516,19 @@ async function listJsonFiles(root: string): Promise<string[]> {
|
|
|
418
516
|
const entries = await readdir(root, { recursive: true, withFileTypes: true }).catch(() => []);
|
|
419
517
|
return entries
|
|
420
518
|
.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl"))
|
|
421
|
-
.map((entry) => join(entry.parentPath, entry.name))
|
|
519
|
+
.map((entry) => join(entry.parentPath, entry.name))
|
|
520
|
+
.sort();
|
|
422
521
|
}
|
|
423
522
|
|
|
424
523
|
function encodeSessionPath(path: string): string {
|
|
425
524
|
return path.replaceAll("/", "-");
|
|
426
525
|
}
|
|
427
526
|
|
|
527
|
+
function displaySessionPath(path: string, homeDirectory: string): string {
|
|
528
|
+
const fromHome = relative(homeDirectory, path);
|
|
529
|
+
return fromHome && !fromHome.startsWith("..") ? `~/${fromHome}` : path;
|
|
530
|
+
}
|
|
531
|
+
|
|
428
532
|
function deduplicateMessages(messages: readonly ProvenanceMessage[]): ProvenanceMessage[] {
|
|
429
533
|
const seen = new Set<string>();
|
|
430
534
|
return messages.filter((message) => {
|
package/src/reaudit-main.ts
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import { access, mkdir, mkdtemp, readdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { basename, dirname, join, resolve } from "node:path";
|
|
6
|
-
import { fileURLToPath } from "node:url";
|
|
7
6
|
import { parseArgs } from "node:util";
|
|
8
7
|
import {
|
|
9
8
|
COUPLING_REVIEW_MODEL,
|
|
@@ -19,7 +18,8 @@ import {
|
|
|
19
18
|
} from "./coupling.js";
|
|
20
19
|
import { parallelMap } from "./parallel.js";
|
|
21
20
|
import { runCommand } from "./process.js";
|
|
22
|
-
import {
|
|
21
|
+
import { projectRoot } from "./project-paths.js";
|
|
22
|
+
import { createSandboxExecutor, type SandboxExecutor } from "./sandbox/index.js";
|
|
23
23
|
import { loadPiModelAuth } from "./subscription-auth.js";
|
|
24
24
|
|
|
25
25
|
const parsed = parseArgs({
|
|
@@ -182,7 +182,7 @@ async function auditTask(
|
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
function assetRoot(): string {
|
|
185
|
-
return
|
|
185
|
+
return projectRoot(import.meta.url);
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
function positiveInteger(value: string | undefined, label: string): number {
|
package/src/repair-main.ts
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
import { access, cp, mkdir, mkdtemp, readdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
|
-
import { basename,
|
|
6
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { basename, join, resolve } from "node:path";
|
|
7
6
|
import { parseArgs } from "node:util";
|
|
8
7
|
import { defaultStandaloneConcurrency, loadWorkerConfig } from "./config.js";
|
|
9
8
|
import { parallelMap } from "./parallel.js";
|
|
10
9
|
import { runCommand } from "./process.js";
|
|
11
|
-
import {
|
|
10
|
+
import { projectRoot } from "./project-paths.js";
|
|
11
|
+
import { createSandboxExecutor } from "./sandbox/index.js";
|
|
12
12
|
import { loadCodexModelAuth } from "./subscription-auth.js";
|
|
13
13
|
|
|
14
14
|
const parsed = parseArgs({
|
|
@@ -189,7 +189,7 @@ function reviewReport(value: unknown): { readonly tasks: readonly ReviewTask[] }
|
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
function assetRoot(): string {
|
|
192
|
-
return
|
|
192
|
+
return projectRoot(import.meta.url);
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
function positiveInteger(value: string | undefined, label: string): number {
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import type { SelfBenchWorkerConfig } from "./config.js";
|
|
2
|
-
import { DockerSandboxExecutor } from "./docker-executor.js";
|
|
3
|
-
import { ModalSandboxExecutor } from "./modal-executor.js";
|
|
4
|
-
import { TimeoutCappedSandboxExecutor } from "./sandbox-timeout.js";
|
|
5
|
-
import { VercelSandboxExecutor } from "./vercel-executor.js";
|
|
6
|
-
|
|
7
1
|
export interface SandboxFile {
|
|
8
2
|
readonly path: string;
|
|
9
3
|
readonly contents: Uint8Array | string;
|
|
@@ -41,21 +35,18 @@ export interface SandboxResult {
|
|
|
41
35
|
readonly outputs: Readonly<Record<string, Uint8Array>>;
|
|
42
36
|
}
|
|
43
37
|
|
|
38
|
+
export class SandboxExecutionError extends Error {
|
|
39
|
+
constructor(
|
|
40
|
+
message: string,
|
|
41
|
+
readonly result: SandboxResult,
|
|
42
|
+
options?: ErrorOptions,
|
|
43
|
+
) {
|
|
44
|
+
super(message, options);
|
|
45
|
+
this.name = "SandboxExecutionError";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
44
49
|
export interface SandboxExecutor {
|
|
45
50
|
run(request: SandboxRequest, options?: SandboxRunOptions): Promise<SandboxResult>;
|
|
46
51
|
close(): void;
|
|
47
52
|
}
|
|
48
|
-
|
|
49
|
-
export function createSandboxExecutor(config: SelfBenchWorkerConfig["execution"]): SandboxExecutor {
|
|
50
|
-
switch (config.kind) {
|
|
51
|
-
case "docker":
|
|
52
|
-
return new DockerSandboxExecutor(config);
|
|
53
|
-
case "modal":
|
|
54
|
-
return new ModalSandboxExecutor(config);
|
|
55
|
-
case "vercel":
|
|
56
|
-
return new TimeoutCappedSandboxExecutor(
|
|
57
|
-
new VercelSandboxExecutor(config),
|
|
58
|
-
config.timeoutCapMs,
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
}
|