self-bench 0.3.5 → 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.
Files changed (198) hide show
  1. package/README.md +16 -14
  2. package/biome.json +7 -1
  3. package/dist/api.d.ts.map +1 -1
  4. package/dist/api.js +7 -7
  5. package/dist/api.js.map +1 -1
  6. package/dist/build-metadata.js +1 -1
  7. package/dist/cli.js +75 -15
  8. package/dist/cli.js.map +1 -1
  9. package/dist/config.js +1 -1
  10. package/dist/contracts.d.ts +1 -0
  11. package/dist/contracts.d.ts.map +1 -1
  12. package/dist/contracts.js +6 -5
  13. package/dist/contracts.js.map +1 -1
  14. package/dist/extensions/discovery.d.ts.map +1 -1
  15. package/dist/extensions/discovery.js +12 -5
  16. package/dist/extensions/discovery.js.map +1 -1
  17. package/dist/harbor-task.d.ts.map +1 -1
  18. package/dist/harbor-task.js +3 -1
  19. package/dist/harbor-task.js.map +1 -1
  20. package/dist/project-paths.d.ts +2 -0
  21. package/dist/project-paths.d.ts.map +1 -0
  22. package/dist/project-paths.js +15 -0
  23. package/dist/project-paths.js.map +1 -0
  24. package/dist/provenance-associations.d.ts +49 -0
  25. package/dist/provenance-associations.d.ts.map +1 -0
  26. package/dist/provenance-associations.js +220 -0
  27. package/dist/provenance-associations.js.map +1 -0
  28. package/dist/provenance.d.ts +34 -13
  29. package/dist/provenance.d.ts.map +1 -1
  30. package/dist/provenance.js +81 -9
  31. package/dist/provenance.js.map +1 -1
  32. package/dist/reaudit-main.js +3 -3
  33. package/dist/reaudit-main.js.map +1 -1
  34. package/dist/repair-main.js +4 -4
  35. package/dist/repair-main.js.map +1 -1
  36. package/dist/{sandbox.d.ts → sandbox/contracts.d.ts} +5 -3
  37. package/dist/sandbox/contracts.d.ts.map +1 -0
  38. package/dist/sandbox/contracts.js +9 -0
  39. package/dist/sandbox/contracts.js.map +1 -0
  40. package/dist/sandbox/index.d.ts +5 -0
  41. package/dist/sandbox/index.d.ts.map +1 -0
  42. package/dist/{sandbox.js → sandbox/index.js} +6 -5
  43. package/dist/sandbox/index.js.map +1 -0
  44. package/dist/sandbox/programs/author.d.ts +3 -0
  45. package/dist/sandbox/programs/author.d.ts.map +1 -0
  46. package/dist/{sandbox-author.js → sandbox/programs/author.js} +2 -2
  47. package/dist/sandbox/programs/author.js.map +1 -0
  48. package/dist/sandbox/programs/prepare-repair.d.ts +7 -0
  49. package/dist/sandbox/programs/prepare-repair.d.ts.map +1 -0
  50. package/dist/sandbox/programs/prepare-repair.js +30 -0
  51. package/dist/sandbox/programs/prepare-repair.js.map +1 -0
  52. package/dist/sandbox/programs/repair.d.ts +3 -0
  53. package/dist/sandbox/programs/repair.d.ts.map +1 -0
  54. package/dist/{sandbox-repair.js → sandbox/programs/repair.js} +7 -28
  55. package/dist/sandbox/programs/repair.js.map +1 -0
  56. package/dist/sandbox/programs/review.d.ts +2 -0
  57. package/dist/sandbox/programs/review.d.ts.map +1 -0
  58. package/dist/{sandbox-review.js → sandbox/programs/review.js} +2 -2
  59. package/dist/sandbox/programs/review.js.map +1 -0
  60. package/dist/sandbox/programs/validation-repair.d.ts +3 -0
  61. package/dist/sandbox/programs/validation-repair.d.ts.map +1 -0
  62. package/dist/{sandbox-validation-repair.js → sandbox/programs/validation-repair.js} +7 -28
  63. package/dist/sandbox/programs/validation-repair.js.map +1 -0
  64. package/dist/{docker-executor.d.ts → sandbox/providers/docker/executor.d.ts} +3 -3
  65. package/dist/sandbox/providers/docker/executor.d.ts.map +1 -0
  66. package/dist/{docker-executor.js → sandbox/providers/docker/executor.js} +2 -2
  67. package/dist/sandbox/providers/docker/executor.js.map +1 -0
  68. package/dist/{modal-executor.d.ts → sandbox/providers/modal/executor.d.ts} +3 -3
  69. package/dist/sandbox/providers/modal/executor.d.ts.map +1 -0
  70. package/dist/{modal-executor.js → sandbox/providers/modal/executor.js} +50 -19
  71. package/dist/sandbox/providers/modal/executor.js.map +1 -0
  72. package/dist/{vercel-command.d.ts → sandbox/providers/vercel/command.d.ts} +3 -3
  73. package/dist/sandbox/providers/vercel/command.d.ts.map +1 -0
  74. package/dist/{vercel-command.js → sandbox/providers/vercel/command.js} +3 -3
  75. package/dist/sandbox/providers/vercel/command.js.map +1 -0
  76. package/dist/{vercel-executor.d.ts → sandbox/providers/vercel/executor.d.ts} +3 -3
  77. package/dist/sandbox/providers/vercel/executor.d.ts.map +1 -0
  78. package/dist/{vercel-executor.js → sandbox/providers/vercel/executor.js} +5 -5
  79. package/dist/sandbox/providers/vercel/executor.js.map +1 -0
  80. package/dist/{vercel-fetch.d.ts → sandbox/providers/vercel/fetch.d.ts} +1 -1
  81. package/dist/sandbox/providers/vercel/fetch.d.ts.map +1 -0
  82. package/dist/{vercel-fetch.js → sandbox/providers/vercel/fetch.js} +1 -1
  83. package/dist/sandbox/providers/vercel/fetch.js.map +1 -0
  84. package/dist/{sandbox-timeout.d.ts → sandbox/timeout.d.ts} +2 -2
  85. package/dist/sandbox/timeout.d.ts.map +1 -0
  86. package/dist/{sandbox-timeout.js → sandbox/timeout.js} +1 -1
  87. package/dist/sandbox/timeout.js.map +1 -0
  88. package/dist/sandbox-author.bundle.js +11 -8
  89. package/dist/sandbox-repair.bundle.js +43 -35
  90. package/dist/sandbox-review.bundle.js +2 -2
  91. package/dist/sandbox-validation-repair.bundle.js +50 -41
  92. package/dist/{vercel-cli.d.ts → setup/vercel/cli.d.ts} +2 -2
  93. package/dist/setup/vercel/cli.d.ts.map +1 -0
  94. package/dist/{vercel-cli.js → setup/vercel/cli.js} +2 -2
  95. package/dist/setup/vercel/cli.js.map +1 -0
  96. package/dist/setup/vercel/index.d.ts +4 -0
  97. package/dist/setup/vercel/index.d.ts.map +1 -0
  98. package/dist/setup/vercel/index.js +3 -0
  99. package/dist/setup/vercel/index.js.map +1 -0
  100. package/dist/{vercel-setup-probe.d.ts → setup/vercel/probe.d.ts} +2 -2
  101. package/dist/setup/vercel/probe.d.ts.map +1 -0
  102. package/dist/{vercel-setup-probe.js → setup/vercel/probe.js} +2 -2
  103. package/dist/setup/vercel/probe.js.map +1 -0
  104. package/dist/{vercel-profile.d.ts → setup/vercel/profile.d.ts} +1 -1
  105. package/dist/setup/vercel/profile.d.ts.map +1 -0
  106. package/dist/{vercel-profile.js → setup/vercel/profile.js} +2 -2
  107. package/dist/setup/vercel/profile.js.map +1 -0
  108. package/dist/{vercel-runtime-image.d.ts → setup/vercel/runtime-image.d.ts} +5 -5
  109. package/dist/setup/vercel/runtime-image.d.ts.map +1 -0
  110. package/dist/{vercel-runtime-image.js → setup/vercel/runtime-image.js} +2 -2
  111. package/dist/setup/vercel/runtime-image.js.map +1 -0
  112. package/dist/{vercel-setup.d.ts → setup/vercel/setup.d.ts} +8 -8
  113. package/dist/setup/vercel/setup.d.ts.map +1 -0
  114. package/dist/{vercel-setup.js → setup/vercel/setup.js} +8 -8
  115. package/dist/setup/vercel/setup.js.map +1 -0
  116. package/dist/temporal/activities.d.ts +12 -0
  117. package/dist/temporal/activities.d.ts.map +1 -1
  118. package/dist/temporal/activities.js +80 -33
  119. package/dist/temporal/activities.js.map +1 -1
  120. package/dist/temporal/workflow.d.ts +1 -1
  121. package/dist/temporal/workflow.d.ts.map +1 -1
  122. package/dist/temporal/workflow.js +90 -65
  123. package/dist/temporal/workflow.js.map +1 -1
  124. package/docs/operations.md +39 -5
  125. package/docs/task-construction.md +3 -1
  126. package/package.json +5 -5
  127. package/scripts/build-sandbox-programs.ts +22 -0
  128. package/scripts/verify-package.ts +25 -4
  129. package/scripts/write-build-metadata.ts +1 -1
  130. package/src/api.ts +7 -6
  131. package/src/cli.ts +99 -15
  132. package/src/config.ts +1 -1
  133. package/src/contracts.ts +7 -5
  134. package/src/extensions/discovery.ts +14 -5
  135. package/src/harbor-task.ts +6 -1
  136. package/src/project-paths.ts +15 -0
  137. package/src/provenance-associations.ts +311 -0
  138. package/src/provenance.ts +132 -28
  139. package/src/reaudit-main.ts +3 -3
  140. package/src/repair-main.ts +4 -4
  141. package/src/{sandbox.ts → sandbox/contracts.ts} +11 -20
  142. package/src/sandbox/index.ts +23 -0
  143. package/src/{sandbox-author.ts → sandbox/programs/author.ts} +1 -1
  144. package/src/sandbox/programs/prepare-repair.ts +44 -0
  145. package/src/{sandbox-repair.ts → sandbox/programs/repair.ts} +7 -31
  146. package/src/{sandbox-review.ts → sandbox/programs/review.ts} +1 -1
  147. package/src/{sandbox-validation-repair.ts → sandbox/programs/validation-repair.ts} +6 -30
  148. package/src/{docker-executor.ts → sandbox/providers/docker/executor.ts} +3 -3
  149. package/src/{modal-executor.ts → sandbox/providers/modal/executor.ts} +66 -25
  150. package/src/{vercel-command.ts → sandbox/providers/vercel/command.ts} +3 -3
  151. package/src/{vercel-executor.ts → sandbox/providers/vercel/executor.ts} +5 -5
  152. package/src/{sandbox-timeout.ts → sandbox/timeout.ts} +1 -1
  153. package/src/{vercel-cli.ts → setup/vercel/cli.ts} +1 -1
  154. package/src/setup/vercel/index.ts +3 -0
  155. package/src/{vercel-setup-probe.ts → setup/vercel/probe.ts} +5 -2
  156. package/src/{vercel-profile.ts → setup/vercel/profile.ts} +4 -1
  157. package/src/{vercel-runtime-image.ts → setup/vercel/runtime-image.ts} +5 -5
  158. package/src/{vercel-setup.ts → setup/vercel/setup.ts} +8 -8
  159. package/src/temporal/activities.ts +164 -63
  160. package/src/temporal/workflow.ts +120 -71
  161. package/tsconfig.json +1 -1
  162. package/dist/docker-executor.d.ts.map +0 -1
  163. package/dist/docker-executor.js.map +0 -1
  164. package/dist/modal-executor.d.ts.map +0 -1
  165. package/dist/modal-executor.js.map +0 -1
  166. package/dist/sandbox-author.d.ts +0 -3
  167. package/dist/sandbox-author.d.ts.map +0 -1
  168. package/dist/sandbox-author.js.map +0 -1
  169. package/dist/sandbox-repair.d.ts +0 -3
  170. package/dist/sandbox-repair.d.ts.map +0 -1
  171. package/dist/sandbox-repair.js.map +0 -1
  172. package/dist/sandbox-review.d.ts +0 -2
  173. package/dist/sandbox-review.d.ts.map +0 -1
  174. package/dist/sandbox-review.js.map +0 -1
  175. package/dist/sandbox-timeout.d.ts.map +0 -1
  176. package/dist/sandbox-timeout.js.map +0 -1
  177. package/dist/sandbox-validation-repair.d.ts +0 -3
  178. package/dist/sandbox-validation-repair.d.ts.map +0 -1
  179. package/dist/sandbox-validation-repair.js.map +0 -1
  180. package/dist/sandbox.d.ts.map +0 -1
  181. package/dist/sandbox.js.map +0 -1
  182. package/dist/vercel-cli.d.ts.map +0 -1
  183. package/dist/vercel-cli.js.map +0 -1
  184. package/dist/vercel-command.d.ts.map +0 -1
  185. package/dist/vercel-command.js.map +0 -1
  186. package/dist/vercel-executor.d.ts.map +0 -1
  187. package/dist/vercel-executor.js.map +0 -1
  188. package/dist/vercel-fetch.d.ts.map +0 -1
  189. package/dist/vercel-fetch.js.map +0 -1
  190. package/dist/vercel-profile.d.ts.map +0 -1
  191. package/dist/vercel-profile.js.map +0 -1
  192. package/dist/vercel-runtime-image.d.ts.map +0 -1
  193. package/dist/vercel-runtime-image.js.map +0 -1
  194. package/dist/vercel-setup-probe.d.ts.map +0 -1
  195. package/dist/vercel-setup-probe.js.map +0 -1
  196. package/dist/vercel-setup.d.ts.map +0 -1
  197. package/dist/vercel-setup.js.map +0 -1
  198. /package/src/{vercel-fetch.ts → sandbox/providers/vercel/fetch.ts} +0 -0
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
- interface ProvenanceMessageBase {
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 messageIndex: number;
11
- readonly content: string;
40
+ readonly path: string;
41
+ readonly modifiedAt: string;
12
42
  }
13
43
 
14
- export type ProvenanceMessage =
15
- | (ProvenanceMessageBase & { readonly sourceType: SessionProvenanceFormat })
16
- | (ProvenanceMessageBase & {
17
- readonly sourceType: "github-pull-request";
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
- messages.push(...extractProvenanceMessages(raw, format, basename(path)));
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("gh", [
80
- "pr",
81
- "list",
82
- "--repo",
83
- repository,
84
- "--state",
85
- "merged",
86
- "--limit",
87
- String(GITHUB_PULL_REQUEST_LIMIT),
88
- "--json",
89
- "number,title,body,url,author,isDraft,additions,deletions,changedFiles",
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.sourceType === "github-pull-request" &&
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) => {
@@ -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 { createSandboxExecutor, type SandboxExecutor } from "./sandbox.js";
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 resolve(dirname(fileURLToPath(import.meta.url)), "..");
185
+ return projectRoot(import.meta.url);
186
186
  }
187
187
 
188
188
  function positiveInteger(value: string | undefined, label: string): number {
@@ -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, dirname, join, resolve } from "node:path";
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 { createSandboxExecutor } from "./sandbox.js";
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 resolve(dirname(fileURLToPath(import.meta.url)), "..");
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
- }
@@ -0,0 +1,23 @@
1
+ import type { SelfBenchWorkerConfig } from "../config.js";
2
+ import { DockerSandboxExecutor } from "./providers/docker/executor.js";
3
+ import { ModalSandboxExecutor } from "./providers/modal/executor.js";
4
+ import { VercelSandboxExecutor } from "./providers/vercel/executor.js";
5
+ import { TimeoutCappedSandboxExecutor } from "./timeout.js";
6
+
7
+ export * from "./contracts.js";
8
+
9
+ import type { SandboxExecutor } from "./contracts.js";
10
+
11
+ export function createSandboxExecutor(config: SelfBenchWorkerConfig["execution"]): SandboxExecutor {
12
+ switch (config.kind) {
13
+ case "docker":
14
+ return new DockerSandboxExecutor(config);
15
+ case "modal":
16
+ return new ModalSandboxExecutor(config);
17
+ case "vercel":
18
+ return new TimeoutCappedSandboxExecutor(
19
+ new VercelSandboxExecutor(config),
20
+ config.timeoutCapMs,
21
+ );
22
+ }
23
+ }
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { readdir } from "node:fs/promises";
4
4
  import { join } from "node:path";
5
- import { compileHarborTask } from "./harbor-task.js";
5
+ import { compileHarborTask } from "../../harbor-task.js";
6
6
 
7
7
  const [tasksRoot, repositoryDirectory, outputDirectory] = process.argv.slice(2);
8
8
  if (!tasksRoot || !repositoryDirectory || !outputDirectory) {
@@ -0,0 +1,44 @@
1
+ import { access, mkdir } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { runCommand } from "../../process.js";
4
+
5
+ export interface PreparedRepairTask {
6
+ readonly extractedDirectory: string;
7
+ readonly taskDirectory: string;
8
+ readonly repositoryDirectory: string;
9
+ }
10
+
11
+ export async function prepareRepairTask(
12
+ archivePath: string,
13
+ workDirectory = "/work",
14
+ ): Promise<PreparedRepairTask> {
15
+ const extractedDirectory = join(workDirectory, "task");
16
+ const repositoryDirectory = join(workDirectory, "repo");
17
+ await Promise.all([mkdir(extractedDirectory, { recursive: true }), mkdir(repositoryDirectory)]);
18
+ await runCommand("tar", ["-xzf", archivePath, "-C", extractedDirectory]);
19
+ const taskDirectory = await access(join(extractedDirectory, "instruction.md")).then(
20
+ () => extractedDirectory,
21
+ () => join(extractedDirectory, "harbor-task"),
22
+ );
23
+
24
+ await runCommand("tar", [
25
+ "-xzf",
26
+ join(taskDirectory, "tests/repo.tar.gz"),
27
+ "-C",
28
+ repositoryDirectory,
29
+ ]);
30
+ await runCommand("git", ["-C", repositoryDirectory, "init", "-q"]);
31
+ await runCommand("git", ["-C", repositoryDirectory, "config", "user.name", "SelfBench"]);
32
+ await runCommand("git", ["-C", repositoryDirectory, "config", "user.email", "selfbench@local"]);
33
+ await runCommand("git", ["-C", repositoryDirectory, "add", "-A"]);
34
+ await runCommand("git", ["-C", repositoryDirectory, "commit", "-qm", "base"]);
35
+ await runCommand("git", [
36
+ "-C",
37
+ repositoryDirectory,
38
+ "apply",
39
+ join(taskDirectory, "tests/test.patch"),
40
+ ]);
41
+ await runCommand("git", ["-C", repositoryDirectory, "add", "-N", "--all"]);
42
+
43
+ return { extractedDirectory, taskDirectory, repositoryDirectory };
44
+ }
@@ -1,25 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { access, chmod, mkdir, readFile, writeFile } from "node:fs/promises";
3
+ import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
4
4
  import { homedir, tmpdir } from "node:os";
5
5
  import { join } from "node:path";
6
- import { sha256 } from "./hash.js";
7
- import { runCommand } from "./process.js";
8
- import { assertRepairPaths, patchPaths, repairPrompt } from "./repair.js";
6
+ import { sha256 } from "../../hash.js";
7
+ import { runCommand } from "../../process.js";
8
+ import { assertRepairPaths, patchPaths, repairPrompt } from "../../repair.js";
9
+ import { prepareRepairTask } from "./prepare-repair.js";
9
10
 
10
11
  const [archivePath, reviewPath, outputArchive, outputReport] = process.argv.slice(2);
11
12
  if (!archivePath || !reviewPath || !outputArchive || !outputReport) {
12
13
  throw new Error("usage: sandbox-repair TASK.tar.gz REVIEW.json OUTPUT.tar.gz OUTPUT-REPORT.json");
13
14
  }
14
15
 
15
- const extractedDirectory = "/work/task";
16
- const repositoryDirectory = "/work/repo";
17
- await Promise.all([mkdir(extractedDirectory, { recursive: true }), mkdir(repositoryDirectory)]);
18
- await runCommand("tar", ["-xzf", archivePath, "-C", extractedDirectory]);
19
- const taskDirectory = await access(join(extractedDirectory, "instruction.md")).then(
20
- () => extractedDirectory,
21
- () => join(extractedDirectory, "harbor-task"),
22
- );
16
+ const { extractedDirectory, taskDirectory, repositoryDirectory } =
17
+ await prepareRepairTask(archivePath);
23
18
 
24
19
  const [instruction, originalPatch, review] = await Promise.all([
25
20
  readFile(join(taskDirectory, "instruction.md"), "utf8"),
@@ -31,25 +26,6 @@ const manifest = JSON.parse(await readFile(manifestPath, "utf8")) as Record<stri
31
26
  const taskId = typeof manifest.taskId === "string" ? manifest.taskId : "unknown-task";
32
27
  const allowedPaths = patchPaths(originalPatch);
33
28
 
34
- await runCommand("tar", [
35
- "-xzf",
36
- join(taskDirectory, "tests/repo.tar.gz"),
37
- "-C",
38
- repositoryDirectory,
39
- ]);
40
- await runCommand("git", ["-C", repositoryDirectory, "init", "-q"]);
41
- await runCommand("git", ["-C", repositoryDirectory, "config", "user.name", "SelfBench"]);
42
- await runCommand("git", ["-C", repositoryDirectory, "config", "user.email", "selfbench@local"]);
43
- await runCommand("git", ["-C", repositoryDirectory, "add", "-A"]);
44
- await runCommand("git", ["-C", repositoryDirectory, "commit", "-qm", "base"]);
45
- await runCommand("git", [
46
- "-C",
47
- repositoryDirectory,
48
- "apply",
49
- join(taskDirectory, "tests/test.patch"),
50
- ]);
51
- await runCommand("git", ["-C", repositoryDirectory, "add", "-N", "--all"]);
52
-
53
29
  const codexAuth = process.env.SELFBENCH_CODEX_AUTH_JSON;
54
30
  const apiKey = process.env.OPENAI_API_KEY;
55
31
  if (!apiKey && !codexAuth) {
@@ -1,5 +1,5 @@
1
1
  import { readFile, writeFile } from "node:fs/promises";
2
- import { reviewCouplingWithCodex } from "./codex-review.js";
2
+ import { reviewCouplingWithCodex } from "../../codex-review.js";
3
3
 
4
4
  const apiKey = process.env.OPENAI_API_KEY;
5
5
  const authJson = process.env.SELFBENCH_PI_AUTH_JSON;
@@ -1,15 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { access, chmod, mkdir, readFile, writeFile } from "node:fs/promises";
3
+ import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
4
4
  import { homedir, tmpdir } from "node:os";
5
5
  import { join } from "node:path";
6
- import { taskDefinitionSchema } from "./contracts.js";
7
- import { runCommand } from "./process.js";
6
+ import { taskDefinitionSchema } from "../../contracts.js";
7
+ import { runCommand } from "../../process.js";
8
8
  import {
9
9
  assertValidationRepair,
10
10
  validationRepairPaths,
11
11
  validationRepairPrompt,
12
- } from "./validation-repair.js";
12
+ } from "../../validation-repair.js";
13
+ import { prepareRepairTask } from "./prepare-repair.js";
13
14
 
14
15
  const [archivePath, definitionPath, diagnosticsPath, outputDefinition, outputPatch, outputReport] =
15
16
  process.argv.slice(2);
@@ -26,14 +27,7 @@ if (
26
27
  );
27
28
  }
28
29
 
29
- const extractedDirectory = "/work/task";
30
- const repositoryDirectory = "/work/repo";
31
- await Promise.all([mkdir(extractedDirectory, { recursive: true }), mkdir(repositoryDirectory)]);
32
- await runCommand("tar", ["-xzf", archivePath, "-C", extractedDirectory]);
33
- const taskDirectory = await access(join(extractedDirectory, "instruction.md")).then(
34
- () => extractedDirectory,
35
- () => join(extractedDirectory, "harbor-task"),
36
- );
30
+ const { taskDirectory, repositoryDirectory } = await prepareRepairTask(archivePath);
37
31
  const [instruction, originalPatch, diagnostics, originalDefinitionBytes] = await Promise.all([
38
32
  readFile(join(taskDirectory, "instruction.md"), "utf8"),
39
33
  readFile(join(taskDirectory, "tests/test.patch"), "utf8"),
@@ -43,24 +37,6 @@ const [instruction, originalPatch, diagnostics, originalDefinitionBytes] = await
43
37
  const originalDefinition = taskDefinitionSchema.parse(JSON.parse(originalDefinitionBytes));
44
38
  const allowedPaths = validationRepairPaths(originalPatch);
45
39
 
46
- await runCommand("tar", [
47
- "-xzf",
48
- join(taskDirectory, "tests/repo.tar.gz"),
49
- "-C",
50
- repositoryDirectory,
51
- ]);
52
- await runCommand("git", ["-C", repositoryDirectory, "init", "-q"]);
53
- await runCommand("git", ["-C", repositoryDirectory, "config", "user.name", "SelfBench"]);
54
- await runCommand("git", ["-C", repositoryDirectory, "config", "user.email", "selfbench@local"]);
55
- await runCommand("git", ["-C", repositoryDirectory, "add", "-A"]);
56
- await runCommand("git", ["-C", repositoryDirectory, "commit", "-qm", "base"]);
57
- await runCommand("git", [
58
- "-C",
59
- repositoryDirectory,
60
- "apply",
61
- join(taskDirectory, "tests/test.patch"),
62
- ]);
63
- await runCommand("git", ["-C", repositoryDirectory, "add", "-N", "--all"]);
64
40
  await Promise.all([
65
41
  writeFile("/work/definition.json", `${JSON.stringify(originalDefinition, null, 2)}\n`),
66
42
  writeFile("/work/gold.patch", await readFile(join(taskDirectory, "solution/gold.patch"))),
@@ -1,14 +1,14 @@
1
1
  import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
2
2
  import { tmpdir } from "node:os";
3
3
  import { dirname, join, relative, resolve } from "node:path";
4
- import type { SelfBenchConfig } from "./config.js";
5
- import { runCommand } from "./process.js";
4
+ import type { SelfBenchConfig } from "../../../config.js";
5
+ import { runCommand } from "../../../process.js";
6
6
  import type {
7
7
  SandboxExecutor,
8
8
  SandboxRequest,
9
9
  SandboxResult,
10
10
  SandboxRunOptions,
11
- } from "./sandbox.js";
11
+ } from "../../contracts.js";
12
12
 
13
13
  export class DockerSandboxExecutor implements SandboxExecutor {
14
14
  readonly #config: Extract<SelfBenchConfig["execution"], { kind: "docker" }>;