self-bench 0.3.3 → 0.3.5

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 (149) hide show
  1. package/Dockerfile.sandbox +1 -1
  2. package/README.md +20 -0
  3. package/bun.lock +48 -0
  4. package/compose.yaml +7 -1
  5. package/dist/agent-smoke-main.js +3 -2
  6. package/dist/agent-smoke-main.js.map +1 -1
  7. package/dist/agent-smoke.d.ts +2 -1
  8. package/dist/agent-smoke.d.ts.map +1 -1
  9. package/dist/agent-smoke.js +6 -1
  10. package/dist/agent-smoke.js.map +1 -1
  11. package/dist/api.d.ts +24 -0
  12. package/dist/api.d.ts.map +1 -1
  13. package/dist/api.js +25 -18
  14. package/dist/api.js.map +1 -1
  15. package/dist/build-metadata.js +1 -1
  16. package/dist/cli.js +67 -11
  17. package/dist/cli.js.map +1 -1
  18. package/dist/config.d.ts +36 -10
  19. package/dist/config.d.ts.map +1 -1
  20. package/dist/config.js +89 -15
  21. package/dist/config.js.map +1 -1
  22. package/dist/contracts.d.ts +22 -2
  23. package/dist/contracts.d.ts.map +1 -1
  24. package/dist/contracts.js +31 -6
  25. package/dist/contracts.js.map +1 -1
  26. package/dist/eval-main.js +3 -2
  27. package/dist/eval-main.js.map +1 -1
  28. package/dist/evaluate.d.ts +2 -1
  29. package/dist/evaluate.d.ts.map +1 -1
  30. package/dist/evaluate.js +2 -1
  31. package/dist/evaluate.js.map +1 -1
  32. package/dist/harbor-environment.d.ts +2 -0
  33. package/dist/harbor-environment.d.ts.map +1 -0
  34. package/dist/harbor-environment.js +15 -0
  35. package/dist/harbor-environment.js.map +1 -0
  36. package/dist/harbor-results.d.ts.map +1 -1
  37. package/dist/harbor-results.js +2 -0
  38. package/dist/harbor-results.js.map +1 -1
  39. package/dist/process.d.ts +2 -1
  40. package/dist/process.d.ts.map +1 -1
  41. package/dist/process.js.map +1 -1
  42. package/dist/providers.d.ts +8 -0
  43. package/dist/providers.d.ts.map +1 -0
  44. package/dist/providers.js +14 -0
  45. package/dist/providers.js.map +1 -0
  46. package/dist/reaudit-main.js +5 -5
  47. package/dist/reaudit-main.js.map +1 -1
  48. package/dist/repair-main.js +5 -5
  49. package/dist/repair-main.js.map +1 -1
  50. package/dist/sandbox-author.bundle.js +40 -6
  51. package/dist/sandbox-timeout.d.ts +11 -0
  52. package/dist/sandbox-timeout.d.ts.map +1 -0
  53. package/dist/sandbox-timeout.js +41 -0
  54. package/dist/sandbox-timeout.js.map +1 -0
  55. package/dist/sandbox-validation-repair.bundle.js +40 -6
  56. package/dist/sandbox.d.ts +2 -2
  57. package/dist/sandbox.d.ts.map +1 -1
  58. package/dist/sandbox.js +10 -3
  59. package/dist/sandbox.js.map +1 -1
  60. package/dist/temporal/activities.d.ts +2 -2
  61. package/dist/temporal/activities.d.ts.map +1 -1
  62. package/dist/temporal/activities.js +7 -1
  63. package/dist/temporal/activities.js.map +1 -1
  64. package/dist/temporal/worker-main.js +2 -2
  65. package/dist/temporal/worker-main.js.map +1 -1
  66. package/dist/terminal-prompts.d.ts +26 -0
  67. package/dist/terminal-prompts.d.ts.map +1 -0
  68. package/dist/terminal-prompts.js +137 -0
  69. package/dist/terminal-prompts.js.map +1 -0
  70. package/dist/terminal-reporter.d.ts +30 -0
  71. package/dist/terminal-reporter.d.ts.map +1 -0
  72. package/dist/terminal-reporter.js +90 -0
  73. package/dist/terminal-reporter.js.map +1 -0
  74. package/dist/validate-main.js +8 -2
  75. package/dist/validate-main.js.map +1 -1
  76. package/dist/vercel-cli.d.ts +64 -0
  77. package/dist/vercel-cli.d.ts.map +1 -0
  78. package/dist/vercel-cli.js +303 -0
  79. package/dist/vercel-cli.js.map +1 -0
  80. package/dist/vercel-command.d.ts +14 -0
  81. package/dist/vercel-command.d.ts.map +1 -0
  82. package/dist/vercel-command.js +93 -0
  83. package/dist/vercel-command.js.map +1 -0
  84. package/dist/vercel-executor.d.ts +14 -0
  85. package/dist/vercel-executor.d.ts.map +1 -0
  86. package/dist/vercel-executor.js +356 -0
  87. package/dist/vercel-executor.js.map +1 -0
  88. package/dist/vercel-fetch.d.ts +6 -0
  89. package/dist/vercel-fetch.d.ts.map +1 -0
  90. package/dist/vercel-fetch.js +39 -0
  91. package/dist/vercel-fetch.js.map +1 -0
  92. package/dist/vercel-profile.d.ts +34 -0
  93. package/dist/vercel-profile.d.ts.map +1 -0
  94. package/dist/vercel-profile.js +244 -0
  95. package/dist/vercel-profile.js.map +1 -0
  96. package/dist/vercel-runtime-image.d.ts +51 -0
  97. package/dist/vercel-runtime-image.d.ts.map +1 -0
  98. package/dist/vercel-runtime-image.js +123 -0
  99. package/dist/vercel-runtime-image.js.map +1 -0
  100. package/dist/vercel-setup-probe.d.ts +49 -0
  101. package/dist/vercel-setup-probe.d.ts.map +1 -0
  102. package/dist/vercel-setup-probe.js +151 -0
  103. package/dist/vercel-setup-probe.js.map +1 -0
  104. package/dist/vercel-setup.d.ts +42 -0
  105. package/dist/vercel-setup.d.ts.map +1 -0
  106. package/dist/vercel-setup.js +280 -0
  107. package/dist/vercel-setup.js.map +1 -0
  108. package/docs/evaluations.md +2 -0
  109. package/docs/operations.md +128 -6
  110. package/package.json +5 -1
  111. package/review/index.html +14 -0
  112. package/review/src/App.tsx +338 -0
  113. package/review/src/export-loader.test.ts +57 -0
  114. package/review/src/export-loader.ts +151 -0
  115. package/review/src/main.tsx +15 -0
  116. package/review/src/styles.css +162 -0
  117. package/review/src/types.ts +47 -0
  118. package/review/tsconfig.json +16 -0
  119. package/review/vite.config.ts +20 -0
  120. package/scripts/verify-package.ts +27 -3
  121. package/src/agent-smoke-main.ts +3 -2
  122. package/src/agent-smoke.ts +8 -2
  123. package/src/api.ts +31 -18
  124. package/src/cli.ts +72 -11
  125. package/src/config.ts +150 -25
  126. package/src/contracts.ts +33 -6
  127. package/src/eval-main.ts +3 -2
  128. package/src/evaluate.ts +5 -3
  129. package/src/harbor-environment.ts +17 -0
  130. package/src/harbor-results.ts +2 -0
  131. package/src/process.ts +3 -1
  132. package/src/providers.ts +22 -0
  133. package/src/reaudit-main.ts +8 -5
  134. package/src/repair-main.ts +8 -5
  135. package/src/sandbox-timeout.ts +56 -0
  136. package/src/sandbox.ts +15 -5
  137. package/src/temporal/activities.ts +9 -3
  138. package/src/temporal/worker-main.ts +2 -2
  139. package/src/terminal-prompts.ts +197 -0
  140. package/src/terminal-reporter.ts +126 -0
  141. package/src/validate-main.ts +9 -3
  142. package/src/vercel-cli.ts +403 -0
  143. package/src/vercel-command.ts +127 -0
  144. package/src/vercel-executor.ts +448 -0
  145. package/src/vercel-fetch.ts +45 -0
  146. package/src/vercel-profile.ts +319 -0
  147. package/src/vercel-runtime-image.ts +204 -0
  148. package/src/vercel-setup-probe.ts +229 -0
  149. package/src/vercel-setup.ts +419 -0
@@ -0,0 +1,162 @@
1
+ :root {
2
+ --bg: #f4f5f7;
3
+ --surface: #ffffff;
4
+ --border: #d8dce3;
5
+ --text: #1a1d23;
6
+ --muted: #6b7280;
7
+ --accent: #2563eb;
8
+ --danger: #dc2626;
9
+ --success: #059669;
10
+ --code-bg: #f1f3f5;
11
+ --font: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
12
+ --mono: "SF Mono", "Cascadia Code", "Fira Code", monospace;
13
+ }
14
+
15
+ @media (prefers-color-scheme: dark) {
16
+ :root {
17
+ --bg: #0d1117;
18
+ --surface: #161b22;
19
+ --border: #30363d;
20
+ --text: #e6edf3;
21
+ --muted: #8b949e;
22
+ --accent: #58a6ff;
23
+ --danger: #f85149;
24
+ --success: #3fb950;
25
+ --code-bg: #1c2128;
26
+ }
27
+ }
28
+
29
+ * { margin: 0; padding: 0; box-sizing: border-box; }
30
+
31
+ body {
32
+ font-family: var(--font);
33
+ background: var(--bg);
34
+ color: var(--text);
35
+ font-size: 14px;
36
+ line-height: 1.5;
37
+ }
38
+
39
+ .app-shell { min-height: 100vh; display: flex; flex-direction: column; }
40
+
41
+ .topbar {
42
+ display: flex; justify-content: space-between; align-items: center;
43
+ padding: 16px 24px; border-bottom: 1px solid var(--border);
44
+ background: var(--surface);
45
+ }
46
+
47
+ .eyebrow {
48
+ font-size: 11px; font-weight: 600; letter-spacing: 0.08em;
49
+ text-transform: uppercase; color: var(--muted);
50
+ }
51
+
52
+ .topbar h1 { font-size: 18px; font-weight: 600; margin-top: 2px; }
53
+ .topbar-actions { display: flex; gap: 8px; align-items: center; }
54
+ .token-input {
55
+ padding: 6px 10px; border: 1px solid var(--border); border-radius: 6px;
56
+ background: var(--surface); color: var(--text); font-size: 12px; width: 180px;
57
+ }
58
+
59
+ .error-banner {
60
+ padding: 12px 24px; background: var(--danger); color: #fff; font-size: 13px;
61
+ }
62
+
63
+ .workspace {
64
+ flex: 1; display: grid; grid-template-columns: 280px 1fr; gap: 16px;
65
+ padding: 16px 24px;
66
+ }
67
+
68
+ .surface { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; }
69
+
70
+ .run-list { padding: 16px; overflow-y: auto; max-height: calc(100vh - 100px); }
71
+
72
+ .section-heading {
73
+ display: flex; justify-content: space-between; align-items: baseline;
74
+ margin-bottom: 12px; padding-bottom: 8px; border-bottom: 1px solid var(--border);
75
+ }
76
+
77
+ .section-heading h2 { font-size: 13px; font-weight: 600; }
78
+ .section-heading span { font-size: 11px; color: var(--muted); }
79
+
80
+ .run-row {
81
+ width: 100%; display: flex; flex-direction: column; gap: 2px;
82
+ padding: 10px 12px; margin-bottom: 6px; border: 1px solid var(--border);
83
+ border-radius: 6px; background: var(--surface); cursor: pointer; text-align: left;
84
+ font: inherit; color: inherit;
85
+ }
86
+
87
+ .run-row:hover { border-color: var(--accent); }
88
+ .run-row.selected { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
89
+ .run-row strong { font-size: 13px; }
90
+ .run-row span { font-size: 11px; color: var(--muted); }
91
+ .run-row small { font-size: 10px; color: var(--muted); }
92
+
93
+ .content { display: flex; flex-direction: column; gap: 16px; }
94
+
95
+ .empty {
96
+ padding: 64px 32px; text-align: center; display: flex; flex-direction: column;
97
+ align-items: center; gap: 8px; min-height: 400px; justify-content: center;
98
+ }
99
+ .empty h2 { font-size: 16px; font-weight: 600; }
100
+ .empty p { color: var(--muted); }
101
+
102
+ .summary {
103
+ display: flex; justify-content: space-between; align-items: center;
104
+ padding: 16px 20px;
105
+ }
106
+ .summary h2 { font-size: 16px; font-weight: 600; }
107
+ .summary .muted { font-size: 12px; color: var(--muted); margin-top: 2px; }
108
+
109
+ .review-grid { display: grid; grid-template-columns: 240px 1fr; gap: 16px; }
110
+
111
+ .task-list { padding: 16px; max-height: calc(100vh - 280px); overflow-y: auto; }
112
+
113
+ .task-row {
114
+ width: 100%; display: flex; justify-content: space-between; align-items: center;
115
+ padding: 8px 10px; margin-bottom: 4px; border: 1px solid var(--border);
116
+ border-radius: 5px; background: var(--surface); cursor: pointer; text-align: left;
117
+ font: inherit; color: inherit; font-size: 12px;
118
+ }
119
+ .task-row:hover { border-color: var(--accent); }
120
+ .task-row.selected { border-color: var(--accent); }
121
+ .task-row.removed { opacity: 0.5; }
122
+ .task-row.removed strong { text-decoration: line-through; }
123
+ .task-row span { font-size: 10px; color: var(--muted); }
124
+
125
+ .task-detail { padding: 20px 24px; max-height: calc(100vh - 280px); overflow-y: auto; }
126
+
127
+ .detail-header {
128
+ display: flex; justify-content: space-between; align-items: flex-start;
129
+ margin-bottom: 20px; padding-bottom: 16px; border-bottom: 1px solid var(--border);
130
+ }
131
+ .detail-header h2 { font-size: 18px; font-weight: 600; }
132
+
133
+ .detail-block { margin-bottom: 20px; }
134
+ .detail-block h3 {
135
+ font-size: 12px; font-weight: 600; letter-spacing: 0.04em;
136
+ text-transform: uppercase; color: var(--muted); margin-bottom: 6px;
137
+ }
138
+ .detail-block pre {
139
+ padding: 12px 16px; background: var(--code-bg); border-radius: 6px;
140
+ font-family: var(--mono); font-size: 12px; white-space: pre-wrap;
141
+ word-break: break-all; max-height: 400px; overflow-y: auto; line-height: 1.6;
142
+ }
143
+ .patch-files {
144
+ display: grid; gap: 10px; padding: 8px; max-height: 600px; overflow: auto;
145
+ background: var(--code-bg); border-radius: 6px;
146
+ }
147
+ .patch-files > div { overflow: hidden; border: 1px solid var(--border); border-radius: 4px; }
148
+
149
+ .button {
150
+ padding: 7px 14px; border: 1px solid var(--border); border-radius: 6px;
151
+ background: var(--surface); color: var(--text); font-size: 12px; font-weight: 500;
152
+ cursor: pointer; font-family: var(--font);
153
+ }
154
+ .button:hover { border-color: var(--accent); color: var(--accent); }
155
+ .button.primary { background: var(--accent); color: #fff; border-color: var(--accent); }
156
+ .button.primary:hover { opacity: 0.9; color: #fff; }
157
+ .button.danger { color: var(--danger); border-color: var(--danger); }
158
+ .button.danger:hover { background: var(--danger); color: #fff; }
159
+ .button.undo { color: var(--success); border-color: var(--success); }
160
+ .button.undo:hover { background: var(--success); color: #fff; }
161
+
162
+ .muted { color: var(--muted); }
@@ -0,0 +1,47 @@
1
+ export interface RunSummary {
2
+ runId: string;
3
+ status: string;
4
+ startedAt: string;
5
+ closedAt?: string;
6
+ }
7
+
8
+ export interface TaskProgress {
9
+ taskId: string;
10
+ candidateId: string;
11
+ difficulty: "easy" | "medium" | "hard";
12
+ status: string;
13
+ reason?: string;
14
+ }
15
+
16
+ export interface RunStatus {
17
+ runId: string;
18
+ phase: string;
19
+ requested: number;
20
+ accepted: number;
21
+ rejected: number;
22
+ tasks: TaskProgress[];
23
+ export?: { uri: string; sizeBytes: number; contentType: string };
24
+ error?: string;
25
+ }
26
+
27
+ export interface ExportManifest {
28
+ schemaVersion: number;
29
+ runId: string;
30
+ candidateCounts: Record<string, number>;
31
+ repository: { url: string; commit: string };
32
+ version: Record<string, unknown>;
33
+ acceptedCount: number;
34
+ tasks: { taskId: string; sha256: string }[];
35
+ }
36
+
37
+ export interface ExportTask {
38
+ taskId: string;
39
+ files: Map<string, Uint8Array>;
40
+ textFiles: Map<string, string>;
41
+ }
42
+
43
+ export interface LoadedExport {
44
+ manifest: ExportManifest;
45
+ tasks: ExportTask[];
46
+ taskArchives: Map<string, Uint8Array>;
47
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "allowSyntheticDefaultImports": true,
4
+ "esModuleInterop": true,
5
+ "jsx": "react-jsx",
6
+ "lib": ["DOM", "DOM.Iterable", "ES2023"],
7
+ "module": "ESNext",
8
+ "moduleResolution": "Bundler",
9
+ "noEmit": true,
10
+ "skipLibCheck": true,
11
+ "strict": true,
12
+ "target": "ES2022",
13
+ "types": ["bun", "vite/client"]
14
+ },
15
+ "include": ["src", "vite.config.ts"]
16
+ }
@@ -0,0 +1,20 @@
1
+ import react from "@vitejs/plugin-react";
2
+ import { defineConfig } from "vite";
3
+
4
+ export default defineConfig({
5
+ root: "review",
6
+ plugins: [react()],
7
+ build: {
8
+ outDir: "../dist/review",
9
+ emptyOutDir: true,
10
+ },
11
+ server: {
12
+ allowedHosts: ["avyays-mac-mini.tailf3cee5.ts.net"],
13
+ proxy: {
14
+ "/v1": "http://127.0.0.1:8080",
15
+ },
16
+ },
17
+ preview: {
18
+ allowedHosts: ["avyays-mac-mini.tailf3cee5.ts.net"],
19
+ },
20
+ });
@@ -2,8 +2,14 @@ import { mkdtemp, readFile, rm } from "node:fs/promises";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join, resolve } from "node:path";
4
4
 
5
+ const options = process.argv.slice(2);
6
+ const dockerBuild = options.length === 1 && options[0] === "--docker-build";
7
+ if (options.length > 0 && !dockerBuild) {
8
+ throw new Error(`unknown verify-package argument: ${options.join(" ")}`);
9
+ }
5
10
  const root = resolve(import.meta.dir, "..");
6
11
  const temporary = await mkdtemp(join(tmpdir(), "self-bench-package-"));
12
+
7
13
  async function run(command: string, args: string[], cwd = root): Promise<string> {
8
14
  const child = Bun.spawn([command, ...args], {
9
15
  cwd,
@@ -42,15 +48,33 @@ try {
42
48
  if (!help.includes("self-bench up")) {
43
49
  throw new Error("installed self-bench did not print the expected CLI help");
44
50
  }
45
- for (const asset of ["compose.yaml", "Dockerfile", "Dockerfile.sandbox", "src/skills/selfbench/SKILL.md"]) {
46
- await readFile(join(installRoot, "node_modules", packageJson.name, asset));
51
+ const installedRoot = join(installRoot, "node_modules", packageJson.name);
52
+ for (const asset of [
53
+ ".dockerignore",
54
+ "compose.yaml",
55
+ "Dockerfile",
56
+ "Dockerfile.sandbox",
57
+ "review/index.html",
58
+ "review/src/App.tsx",
59
+ "review/vite.config.ts",
60
+ "src/extensions/authoring.ts",
61
+ "src/skills/selfbench/SKILL.md",
62
+ ]) {
63
+ await readFile(join(installedRoot, asset));
47
64
  }
48
65
  const installedPackage = JSON.parse(
49
- await readFile(join(installRoot, "node_modules", packageJson.name, "package.json"), "utf8"),
66
+ await readFile(join(installedRoot, "package.json"), "utf8"),
50
67
  ) as { name: string; version: string };
51
68
  if (installedPackage.name !== packageJson.name || installedPackage.version !== packageJson.version) {
52
69
  throw new Error("installed package metadata does not match the workspace package");
53
70
  }
71
+ if (dockerBuild) {
72
+ await run(
73
+ "docker",
74
+ ["build", "--target", "build", "--file", join(installedRoot, "Dockerfile"), installedRoot],
75
+ installedRoot,
76
+ );
77
+ }
54
78
  console.log(`verified ${installedPackage.name}@${installedPackage.version}`);
55
79
  } finally {
56
80
  await rm(temporary, { recursive: true, force: true });
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { parseArgs } from "node:util";
4
4
  import { smokeAllAdapters } from "./agent-smoke.js";
5
+ import { isHarborEnvironment } from "./providers.js";
5
6
 
6
7
  const parsed = parseArgs({
7
8
  options: {
@@ -27,8 +28,8 @@ Options:
27
28
  -h, --help Show this help`);
28
29
  process.exit(0);
29
30
  }
30
- const environment = parsed.values.environment;
31
- if (environment !== "docker" && environment !== "modal") {
31
+ const environment = parsed.values.environment ?? "modal";
32
+ if (!isHarborEnvironment(environment)) {
32
33
  throw new Error("--environment must be docker or modal");
33
34
  }
34
35
  const results = await smokeAllAdapters({
@@ -1,7 +1,9 @@
1
1
  import { mkdir, readFile, writeFile } from "node:fs/promises";
2
2
  import { join, resolve } from "node:path";
3
+ import { harborChildEnvironment } from "./harbor-environment.js";
3
4
  import { parallelMap } from "./parallel.js";
4
5
  import { runCommand } from "./process.js";
6
+ import type { HarborEnvironment } from "./providers.js";
5
7
 
6
8
  export const HARBOR_AGENT_ADAPTERS = [
7
9
  "oracle",
@@ -48,7 +50,7 @@ export interface AdapterSmokeOptions {
48
50
  readonly taskDirectory: string;
49
51
  readonly jobsDirectory: string;
50
52
  readonly harborPath?: string;
51
- readonly environment?: "docker" | "modal";
53
+ readonly environment?: HarborEnvironment;
52
54
  readonly concurrency?: number;
53
55
  }
54
56
 
@@ -99,7 +101,11 @@ export async function smokeAllAdapters(
99
101
  "--yes",
100
102
  "--quiet",
101
103
  ],
102
- { allowFailure: true, timeoutMs: 60 * 60 * 1000 },
104
+ {
105
+ allowFailure: true,
106
+ env: harborChildEnvironment(),
107
+ timeoutMs: 60 * 60 * 1000,
108
+ },
103
109
  );
104
110
  const summary: AdapterSmokeResult = {
105
111
  agent,
package/src/api.ts CHANGED
@@ -11,6 +11,7 @@ import type { SelfBenchConfig } from "./config.js";
11
11
  import {
12
12
  artifactRefSchema,
13
13
  type RunPhase,
14
+ type RunRequest,
14
15
  type RunStatus,
15
16
  repositoryRefSchema,
16
17
  runRequestSchema,
@@ -68,26 +69,10 @@ export async function startApi(config: SelfBenchConfig): Promise<() => Promise<v
68
69
  return;
69
70
  }
70
71
  if (request.method === "POST" && url.pathname === "/v1/runs") {
71
- const submission = submissionSchema.parse(
72
+ const workflowInput = buildRunRequest(
73
+ config,
72
74
  JSON.parse((await readBody(request)).toString("utf8")),
73
75
  );
74
- const workflowInput = runRequestSchema.parse({
75
- runId: submission.runId,
76
- repository: submission.repository,
77
- provenance: submission.provenance,
78
- candidateCounts: submission.candidateCounts,
79
- authoring: {
80
- provider: "openai-codex",
81
- model: submission.authoringModel,
82
- reasoningEffort: "high",
83
- },
84
- version: {
85
- selfbenchCommit: config.buildCommit ?? submission.selfbenchCommit,
86
- executionBackend: config.execution.kind,
87
- sandboxImage: config.execution.image,
88
- schema: 1,
89
- },
90
- });
91
76
  await client.workflow.start(selfBenchRunWorkflow, {
92
77
  workflowId: workflowInput.runId,
93
78
  taskQueue: config.temporal.taskQueue,
@@ -167,6 +152,34 @@ export async function startApi(config: SelfBenchConfig): Promise<() => Promise<v
167
152
  };
168
153
  }
169
154
 
155
+ export function buildRunRequest(
156
+ config: SelfBenchConfig,
157
+ submission: z.input<typeof submissionSchema>,
158
+ ): RunRequest {
159
+ const parsed = submissionSchema.parse(submission);
160
+ return runRequestSchema.parse({
161
+ runId: parsed.runId,
162
+ repository: parsed.repository,
163
+ provenance: parsed.provenance,
164
+ candidateCounts: parsed.candidateCounts,
165
+ authoring: {
166
+ provider: "openai-codex",
167
+ model: parsed.authoringModel,
168
+ reasoningEffort: "high",
169
+ },
170
+ version: {
171
+ selfbenchCommit: config.buildCommit ?? parsed.selfbenchCommit,
172
+ executionBackend: config.execution.kind,
173
+ harborEnvironment: config.harborEnvironment,
174
+ sandboxImage: config.execution.image,
175
+ ...(config.execution.kind === "vercel"
176
+ ? { sandboxTimeoutCapMs: config.execution.timeoutCapMs }
177
+ : {}),
178
+ schema: 1,
179
+ },
180
+ });
181
+ }
182
+
170
183
  async function queryStatus(
171
184
  handle: ReturnType<Client["workflow"]["getHandle"]>,
172
185
  ): Promise<RunStatus | object> {
package/src/cli.ts CHANGED
@@ -10,12 +10,20 @@ import type { ReadableStream as NodeReadableStream } from "node:stream/web";
10
10
  import { fileURLToPath } from "node:url";
11
11
  import { parseArgs } from "node:util";
12
12
  import { buildCommit } from "./build-metadata.js";
13
+ import { loadWorkerConfig } from "./config.js";
13
14
  import { runCommand } from "./process.js";
14
15
  import { collectGitHubPullRequestProvenance, collectRepositoryProvenance } from "./provenance.js";
16
+ import { isExecutionBackend, isHarborEnvironment, matchingHarborEnvironment } from "./providers.js";
15
17
  import { type PolledRunStatus, waitForRun } from "./run-wait.js";
18
+ import { SetupCanceledError } from "./terminal-prompts.js";
19
+ import { applyVercelProfile } from "./vercel-profile.js";
20
+ import { setupVercel } from "./vercel-setup.js";
16
21
 
17
22
  const [command, ...rest] = process.argv.slice(2);
18
23
  switch (command) {
24
+ case "setup":
25
+ await setup(rest);
26
+ break;
19
27
  case "up":
20
28
  await up(rest);
21
29
  break;
@@ -47,31 +55,74 @@ switch (command) {
47
55
  throw new Error(`unknown command: ${command}`);
48
56
  }
49
57
 
58
+ async function setup(args: string[]): Promise<void> {
59
+ const [provider, ...providerArgs] = args;
60
+ if (provider !== "vercel") {
61
+ fail("setup currently supports only: self-bench setup vercel");
62
+ }
63
+ const parsed = parseArgs({
64
+ args: providerArgs,
65
+ options: {
66
+ profile: { type: "string", default: "default" },
67
+ verbose: { type: "boolean", default: false },
68
+ },
69
+ strict: true,
70
+ });
71
+ try {
72
+ await setupVercel({
73
+ profileName: parsed.values.profile,
74
+ verbose: parsed.values.verbose,
75
+ });
76
+ } catch (error) {
77
+ if (error instanceof SetupCanceledError) {
78
+ process.exitCode = 130;
79
+ return;
80
+ }
81
+ throw error;
82
+ }
83
+ }
84
+
50
85
  async function up(args: string[]): Promise<void> {
51
86
  const parsed = parseArgs({
52
87
  args,
53
88
  options: {
54
89
  backend: { type: "string", default: "docker" },
90
+ "harbor-environment": { type: "string" },
55
91
  "modal-config": { type: "string" },
92
+ "vercel-profile": { type: "string" },
56
93
  },
57
94
  strict: true,
58
95
  });
59
- if (parsed.values.backend !== "docker" && parsed.values.backend !== "modal") {
60
- fail('--backend must be "docker" or "modal"');
96
+ if (!isExecutionBackend(parsed.values.backend)) {
97
+ fail('--backend must be "docker", "modal", or "vercel"');
61
98
  }
62
- if (parsed.values.backend === "docker" && parsed.values["modal-config"] !== undefined) {
63
- fail("--modal-config requires --backend modal");
99
+ if (
100
+ parsed.values["harbor-environment"] !== undefined &&
101
+ !isHarborEnvironment(parsed.values["harbor-environment"])
102
+ ) {
103
+ fail('--harbor-environment must be "docker" or "modal"');
64
104
  }
65
105
 
66
106
  const projectRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
67
107
  const composeFile = resolve(projectRoot, "compose.yaml");
68
108
  const backend = parsed.values.backend;
69
- const environment = {
109
+ const harborEnvironment =
110
+ parsed.values["harbor-environment"] ??
111
+ matchingHarborEnvironment(backend) ??
112
+ fail(`--harbor-environment is required with --backend ${backend}`);
113
+ const usesModal = backend === "modal" || harborEnvironment === "modal";
114
+ if (!usesModal && parsed.values["modal-config"] !== undefined) {
115
+ fail("--modal-config requires Modal generation or Harbor");
116
+ }
117
+ if (backend !== "vercel" && parsed.values["vercel-profile"] !== undefined) {
118
+ fail("--vercel-profile requires Vercel generation");
119
+ }
120
+ let environment: NodeJS.ProcessEnv = {
70
121
  ...process.env,
71
122
  SELFBENCH_BUILD_COMMIT: await resolveSelfBenchCommit(),
72
123
  SELFBENCH_EXECUTION_BACKEND: backend,
73
- SELFBENCH_HARBOR_ENVIRONMENT: backend,
74
- ...(backend === "modal"
124
+ SELFBENCH_HARBOR_ENVIRONMENT: harborEnvironment,
125
+ ...(usesModal
75
126
  ? {
76
127
  SELFBENCH_MODAL_CONFIG_PATH: resolve(
77
128
  parsed.values["modal-config"] ?? resolve(homedir(), ".modal.toml"),
@@ -79,6 +130,10 @@ async function up(args: string[]): Promise<void> {
79
130
  }
80
131
  : {}),
81
132
  };
133
+ if (backend === "vercel") {
134
+ environment = await applyVercelProfile(environment, parsed.values["vercel-profile"]);
135
+ loadWorkerConfig(environment);
136
+ }
82
137
 
83
138
  if (backend === "docker") {
84
139
  await runCommand(
@@ -97,7 +152,9 @@ async function up(args: string[]): Promise<void> {
97
152
  await runCommand("docker", ["compose", "--file", composeFile, "up", "-d", "--build"], {
98
153
  env: environment,
99
154
  });
100
- console.log(`SelfBench is running with the ${backend} backend at http://127.0.0.1:8080`);
155
+ console.log(
156
+ `SelfBench is running with ${backend} generation and ${harborEnvironment} Harbor at http://127.0.0.1:8080`,
157
+ );
101
158
  }
102
159
 
103
160
  async function down(): Promise<void> {
@@ -340,7 +397,9 @@ function printHelp(): void {
340
397
  console.log(`SelfBench creates durable tiered Harbor evaluations.
341
398
 
342
399
  Usage:
343
- self-bench up [--backend docker|modal] [--modal-config PATH]
400
+ self-bench setup vercel [--profile NAME] [--verbose]
401
+ self-bench up [--backend docker|modal|vercel] [--harbor-environment docker|modal]
402
+ [--modal-config PATH] [--vercel-profile NAME]
344
403
  self-bench down
345
404
  self-bench run --repo PATH [--easy-count N] [--medium-count N] [--hard-count N]
346
405
  [--model MODEL] [--run-id ID] [--wait] [--output OUTPUT.tar.gz]
@@ -349,8 +408,10 @@ Usage:
349
408
  self-bench download RUN_ID OUTPUT.tar.gz
350
409
  self-bench list
351
410
 
352
- The up command starts the local stack and configures both sandbox execution and Harbor validation for
353
- one backend. Modal uses ~/.modal.toml unless --modal-config overrides it.
411
+ The up command starts the local stack. Docker and Modal default Harbor to the matching backend; Vercel
412
+ requires --harbor-environment because Harbor does not support Vercel. Modal generation or Harbor uses
413
+ ~/.modal.toml unless --modal-config overrides it. Run self-bench setup vercel once to create or select a
414
+ project, publish the pinned runtime image, verify access, and save an owner-only local profile.
354
415
 
355
416
  The tier counts are candidate authoring budgets, not accepted-task targets. Rejected candidates are not
356
417
  replaced, and the export contains only accepted tasks. The run command performs only repository metadata