self-bench 0.3.2 → 0.3.4
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/.dockerignore +0 -1
- package/Dockerfile +2 -1
- package/Dockerfile.sandbox +1 -1
- package/README.md +114 -170
- package/bun.lock +60 -12
- package/compose.yaml +11 -5
- package/dist/agent-smoke-main.js +3 -2
- package/dist/agent-smoke-main.js.map +1 -1
- package/dist/agent-smoke.d.ts +2 -1
- package/dist/agent-smoke.d.ts.map +1 -1
- package/dist/agent-smoke.js +6 -1
- package/dist/agent-smoke.js.map +1 -1
- package/dist/api.d.ts +24 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +65 -20
- package/dist/api.js.map +1 -1
- package/dist/artifacts.d.ts +0 -1
- package/dist/artifacts.d.ts.map +1 -1
- package/dist/artifacts.js +0 -3
- package/dist/artifacts.js.map +1 -1
- package/dist/build-metadata.js +1 -1
- package/dist/cli.js +75 -11
- package/dist/cli.js.map +1 -1
- package/dist/codex-review.d.ts +2 -1
- package/dist/codex-review.d.ts.map +1 -1
- package/dist/codex-review.js +26 -22
- package/dist/codex-review.js.map +1 -1
- package/dist/config.d.ts +36 -11
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +89 -17
- package/dist/config.js.map +1 -1
- package/dist/contracts.d.ts +23 -3
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +31 -6
- package/dist/contracts.js.map +1 -1
- package/dist/eval-main.js +5 -4
- package/dist/eval-main.js.map +1 -1
- package/dist/evaluate.d.ts +2 -1
- package/dist/evaluate.d.ts.map +1 -1
- package/dist/evaluate.js +14 -8
- package/dist/evaluate.js.map +1 -1
- package/dist/harbor-environment.d.ts +2 -0
- package/dist/harbor-environment.d.ts.map +1 -0
- package/dist/harbor-environment.js +15 -0
- package/dist/harbor-environment.js.map +1 -0
- package/dist/harbor-results.d.ts.map +1 -1
- package/dist/harbor-results.js +2 -0
- package/dist/harbor-results.js.map +1 -1
- package/dist/process.d.ts +2 -1
- package/dist/process.d.ts.map +1 -1
- package/dist/process.js.map +1 -1
- package/dist/provenance.d.ts +0 -1
- package/dist/provenance.d.ts.map +1 -1
- package/dist/provenance.js.map +1 -1
- package/dist/providers.d.ts +8 -0
- package/dist/providers.d.ts.map +1 -0
- package/dist/providers.js +14 -0
- package/dist/providers.js.map +1 -0
- package/dist/reaudit-main.js +14 -11
- package/dist/reaudit-main.js.map +1 -1
- package/dist/repair-main.js +12 -9
- package/dist/repair-main.js.map +1 -1
- package/dist/review/assets/index-CZ3G759O.js +1568 -0
- package/dist/review/assets/index-DgSLzX8l.css +1 -0
- package/dist/review/assets/{mojo-Blby8dQK.js → mojo-DJz3ZmWd.js} +1 -1
- package/dist/review/assets/typst-BUadGCkm.js +1 -0
- package/dist/review/index.html +2 -2
- package/dist/sandbox-author.bundle.js +40 -6
- package/dist/sandbox-repair.bundle.js +8 -7
- package/dist/sandbox-repair.js +7 -7
- package/dist/sandbox-repair.js.map +1 -1
- package/dist/sandbox-review.bundle.js +29 -25
- package/dist/sandbox-review.js +5 -3
- package/dist/sandbox-review.js.map +1 -1
- package/dist/sandbox-timeout.d.ts +11 -0
- package/dist/sandbox-timeout.d.ts.map +1 -0
- package/dist/sandbox-timeout.js +41 -0
- package/dist/sandbox-timeout.js.map +1 -0
- package/dist/sandbox-validation-repair.bundle.js +49 -15
- package/dist/sandbox-validation-repair.js +11 -9
- package/dist/sandbox-validation-repair.js.map +1 -1
- package/dist/sandbox.d.ts +2 -2
- package/dist/sandbox.d.ts.map +1 -1
- package/dist/sandbox.js +10 -3
- package/dist/sandbox.js.map +1 -1
- package/dist/subscription-auth.d.ts +12 -1
- package/dist/subscription-auth.d.ts.map +1 -1
- package/dist/subscription-auth.js +16 -16
- package/dist/subscription-auth.js.map +1 -1
- package/dist/{activities.d.ts → temporal/activities.d.ts} +3 -4
- package/dist/temporal/activities.d.ts.map +1 -0
- package/dist/{activities.js → temporal/activities.js} +51 -34
- package/dist/temporal/activities.js.map +1 -0
- package/dist/{temporal.d.ts → temporal/connection.d.ts} +2 -2
- package/dist/temporal/connection.d.ts.map +1 -0
- package/dist/{temporal.js → temporal/connection.js} +1 -1
- package/dist/temporal/connection.js.map +1 -0
- package/dist/{worker-main.d.ts.map → temporal/worker-main.d.ts.map} +1 -1
- package/dist/{worker-main.js → temporal/worker-main.js} +6 -10
- package/dist/temporal/worker-main.js.map +1 -0
- package/dist/{workflow.d.ts → temporal/workflow.d.ts} +1 -1
- package/dist/temporal/workflow.d.ts.map +1 -0
- package/dist/temporal/workflow.js.map +1 -0
- package/dist/terminal-prompts.d.ts +26 -0
- package/dist/terminal-prompts.d.ts.map +1 -0
- package/dist/terminal-prompts.js +137 -0
- package/dist/terminal-prompts.js.map +1 -0
- package/dist/terminal-reporter.d.ts +30 -0
- package/dist/terminal-reporter.d.ts.map +1 -0
- package/dist/terminal-reporter.js +90 -0
- package/dist/terminal-reporter.js.map +1 -0
- package/dist/validate-main.js +8 -2
- package/dist/validate-main.js.map +1 -1
- package/dist/vercel-cli.d.ts +64 -0
- package/dist/vercel-cli.d.ts.map +1 -0
- package/dist/vercel-cli.js +303 -0
- package/dist/vercel-cli.js.map +1 -0
- package/dist/vercel-command.d.ts +14 -0
- package/dist/vercel-command.d.ts.map +1 -0
- package/dist/vercel-command.js +93 -0
- package/dist/vercel-command.js.map +1 -0
- package/dist/vercel-executor.d.ts +14 -0
- package/dist/vercel-executor.d.ts.map +1 -0
- package/dist/vercel-executor.js +356 -0
- package/dist/vercel-executor.js.map +1 -0
- package/dist/vercel-fetch.d.ts +6 -0
- package/dist/vercel-fetch.d.ts.map +1 -0
- package/dist/vercel-fetch.js +39 -0
- package/dist/vercel-fetch.js.map +1 -0
- package/dist/vercel-profile.d.ts +34 -0
- package/dist/vercel-profile.d.ts.map +1 -0
- package/dist/vercel-profile.js +244 -0
- package/dist/vercel-profile.js.map +1 -0
- package/dist/vercel-runtime-image.d.ts +51 -0
- package/dist/vercel-runtime-image.d.ts.map +1 -0
- package/dist/vercel-runtime-image.js +123 -0
- package/dist/vercel-runtime-image.js.map +1 -0
- package/dist/vercel-setup-probe.d.ts +49 -0
- package/dist/vercel-setup-probe.d.ts.map +1 -0
- package/dist/vercel-setup-probe.js +151 -0
- package/dist/vercel-setup-probe.js.map +1 -0
- package/dist/vercel-setup.d.ts +42 -0
- package/dist/vercel-setup.d.ts.map +1 -0
- package/dist/vercel-setup.js +280 -0
- package/dist/vercel-setup.js.map +1 -0
- package/docs/evaluations.md +15 -12
- package/docs/operations.md +134 -21
- package/package.json +8 -6
- package/src/agent-smoke-main.ts +3 -2
- package/src/agent-smoke.ts +8 -2
- package/src/api.ts +71 -20
- package/src/artifacts.ts +0 -4
- package/src/cli.ts +81 -11
- package/src/codex-review.ts +63 -54
- package/src/config.ts +150 -28
- package/src/contracts.ts +33 -11
- package/src/eval-main.ts +5 -4
- package/src/evaluate.ts +18 -11
- package/src/harbor-environment.ts +17 -0
- package/src/harbor-results.ts +2 -0
- package/src/process.ts +3 -1
- package/src/provenance.ts +0 -1
- package/src/providers.ts +22 -0
- package/src/reaudit-main.ts +17 -11
- package/src/repair-main.ts +15 -9
- package/src/sandbox-repair.ts +7 -11
- package/src/sandbox-review.ts +5 -3
- package/src/sandbox-timeout.ts +56 -0
- package/src/sandbox-validation-repair.ts +13 -10
- package/src/sandbox.ts +15 -5
- package/src/subscription-auth.ts +30 -17
- package/src/{activities.ts → temporal/activities.ts} +53 -42
- package/src/{temporal.ts → temporal/connection.ts} +1 -1
- package/src/{worker-main.ts → temporal/worker-main.ts} +6 -10
- package/src/{workflow.ts → temporal/workflow.ts} +10 -10
- package/src/terminal-prompts.ts +197 -0
- package/src/terminal-reporter.ts +126 -0
- package/src/validate-main.ts +9 -3
- package/src/vercel-cli.ts +403 -0
- package/src/vercel-command.ts +127 -0
- package/src/vercel-executor.ts +448 -0
- package/src/vercel-fetch.ts +45 -0
- package/src/vercel-profile.ts +319 -0
- package/src/vercel-runtime-image.ts +204 -0
- package/src/vercel-setup-probe.ts +229 -0
- package/src/vercel-setup.ts +419 -0
- package/dist/activities.d.ts.map +0 -1
- package/dist/activities.js.map +0 -1
- package/dist/extensions/review.d.ts +0 -3
- package/dist/extensions/review.d.ts.map +0 -1
- package/dist/extensions/review.js +0 -44
- package/dist/extensions/review.js.map +0 -1
- package/dist/review/assets/index-Dq-6DFzI.css +0 -1
- package/dist/review/assets/index-DurW_RD7.js +0 -1567
- package/dist/review/assets/typst-DHCkPAjA.js +0 -1
- package/dist/temporal.d.ts.map +0 -1
- package/dist/temporal.js.map +0 -1
- package/dist/worker-main.js.map +0 -1
- package/dist/workflow.d.ts.map +0 -1
- package/dist/workflow.js.map +0 -1
- package/src/extensions/review.ts +0 -54
- /package/dist/{worker-main.d.ts → temporal/worker-main.d.ts} +0 -0
- /package/dist/{workflow.js → temporal/workflow.js} +0 -0
package/src/codex-review.ts
CHANGED
|
@@ -28,45 +28,54 @@ export const couplingReviewSchema = z.object({
|
|
|
28
28
|
export type CouplingReview = z.infer<typeof couplingReviewSchema>;
|
|
29
29
|
|
|
30
30
|
export async function reviewCouplingWithCodex(input: {
|
|
31
|
-
readonly
|
|
31
|
+
readonly apiKey?: string;
|
|
32
|
+
readonly authJson?: string;
|
|
32
33
|
readonly prompt: string;
|
|
33
34
|
readonly signal?: AbortSignal;
|
|
34
35
|
readonly fetch?: typeof fetch;
|
|
35
36
|
}): Promise<CouplingReview> {
|
|
36
|
-
const
|
|
37
|
-
|
|
37
|
+
const subscription =
|
|
38
|
+
!input.apiKey && input.authJson ? parseCredential(input.authJson) : undefined;
|
|
39
|
+
if (!input.apiKey && !subscription) {
|
|
40
|
+
throw new Error("OpenAI model authentication is required");
|
|
41
|
+
}
|
|
38
42
|
const request = input.fetch ?? fetch;
|
|
39
|
-
const response = await request(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
const response = await request(
|
|
44
|
+
subscription
|
|
45
|
+
? "https://chatgpt.com/backend-api/codex/responses"
|
|
46
|
+
: "https://api.openai.com/v1/responses",
|
|
47
|
+
{
|
|
48
|
+
method: "POST",
|
|
49
|
+
headers: {
|
|
50
|
+
Authorization: `Bearer ${subscription?.access ?? input.apiKey}`,
|
|
51
|
+
...(subscription ? { "ChatGPT-Account-Id": accountIdFromToken(subscription.access) } : {}),
|
|
52
|
+
Originator: "selfbench",
|
|
53
|
+
"OpenAI-Beta": "responses=experimental",
|
|
54
|
+
Accept: "text/event-stream",
|
|
55
|
+
"Content-Type": "application/json",
|
|
56
|
+
},
|
|
57
|
+
body: JSON.stringify({
|
|
58
|
+
model: COUPLING_REVIEW_MODEL,
|
|
59
|
+
store: false,
|
|
60
|
+
stream: true,
|
|
61
|
+
instructions:
|
|
62
|
+
"You are an independent benchmark-quality reviewer. Analyze the supplied evidence and call submit_review exactly once.",
|
|
63
|
+
input: [
|
|
64
|
+
{
|
|
65
|
+
role: "user",
|
|
66
|
+
content: [{ type: "input_text", text: input.prompt }],
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
reasoning: { effort: "high", summary: "auto" },
|
|
70
|
+
text: { verbosity: "low" },
|
|
71
|
+
include: ["reasoning.encrypted_content"],
|
|
72
|
+
tools: [reviewTool()],
|
|
73
|
+
tool_choice: { type: "function", name: "submit_review" },
|
|
74
|
+
parallel_tool_calls: false,
|
|
75
|
+
}),
|
|
76
|
+
...(input.signal ? { signal: input.signal } : {}),
|
|
48
77
|
},
|
|
49
|
-
|
|
50
|
-
model: COUPLING_REVIEW_MODEL,
|
|
51
|
-
store: false,
|
|
52
|
-
stream: true,
|
|
53
|
-
instructions:
|
|
54
|
-
"You are an independent benchmark-quality reviewer. Analyze the supplied evidence and call submit_review exactly once.",
|
|
55
|
-
input: [
|
|
56
|
-
{
|
|
57
|
-
role: "user",
|
|
58
|
-
content: [{ type: "input_text", text: input.prompt }],
|
|
59
|
-
},
|
|
60
|
-
],
|
|
61
|
-
reasoning: { effort: "high", summary: "auto" },
|
|
62
|
-
text: { verbosity: "low" },
|
|
63
|
-
include: ["reasoning.encrypted_content"],
|
|
64
|
-
tools: [reviewTool()],
|
|
65
|
-
tool_choice: { type: "function", name: "submit_review" },
|
|
66
|
-
parallel_tool_calls: false,
|
|
67
|
-
}),
|
|
68
|
-
...(input.signal ? { signal: input.signal } : {}),
|
|
69
|
-
});
|
|
78
|
+
);
|
|
70
79
|
const responseText = await response.text();
|
|
71
80
|
if (!response.ok) {
|
|
72
81
|
throw new Error(
|
|
@@ -76,6 +85,27 @@ export async function reviewCouplingWithCodex(input: {
|
|
|
76
85
|
return parseCodexReviewEvents(responseText);
|
|
77
86
|
}
|
|
78
87
|
|
|
88
|
+
function parseCredential(value: string): { access: string } {
|
|
89
|
+
const parsed = JSON.parse(value) as unknown;
|
|
90
|
+
const credential = isRecord(parsed) ? parsed["openai-codex"] : undefined;
|
|
91
|
+
if (!isRecord(credential) || typeof credential.access !== "string") {
|
|
92
|
+
throw new Error("Pi auth does not contain an OpenAI Codex subscription access token");
|
|
93
|
+
}
|
|
94
|
+
return { access: credential.access };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function accountIdFromToken(token: string): string {
|
|
98
|
+
const payload = JSON.parse(
|
|
99
|
+
Buffer.from(token.split(".")[1] ?? "", "base64url").toString("utf8"),
|
|
100
|
+
) as unknown;
|
|
101
|
+
const auth = isRecord(payload) ? payload["https://api.openai.com/auth"] : undefined;
|
|
102
|
+
const accountId = isRecord(auth) ? auth.chatgpt_account_id : undefined;
|
|
103
|
+
if (typeof accountId !== "string" || !accountId) {
|
|
104
|
+
throw new Error("OpenAI Codex subscription token has no ChatGPT account ID");
|
|
105
|
+
}
|
|
106
|
+
return accountId;
|
|
107
|
+
}
|
|
108
|
+
|
|
79
109
|
export function parseCodexReviewEvents(value: string): CouplingReview {
|
|
80
110
|
for (const line of value.split("\n")) {
|
|
81
111
|
if (!line.startsWith("data: ")) {
|
|
@@ -148,27 +178,6 @@ ${goldPatch}
|
|
|
148
178
|
`;
|
|
149
179
|
}
|
|
150
180
|
|
|
151
|
-
function parseCredential(value: string): { access: string } {
|
|
152
|
-
const parsed = JSON.parse(value) as unknown;
|
|
153
|
-
const credential = isRecord(parsed) ? parsed["openai-codex"] : undefined;
|
|
154
|
-
if (!isRecord(credential) || typeof credential.access !== "string") {
|
|
155
|
-
throw new Error("Pi auth does not contain an OpenAI Codex subscription access token");
|
|
156
|
-
}
|
|
157
|
-
return { access: credential.access };
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function accountIdFromToken(token: string): string {
|
|
161
|
-
const payload = JSON.parse(
|
|
162
|
-
Buffer.from(token.split(".")[1] ?? "", "base64url").toString("utf8"),
|
|
163
|
-
) as unknown;
|
|
164
|
-
const auth = isRecord(payload) ? payload["https://api.openai.com/auth"] : undefined;
|
|
165
|
-
const accountId = isRecord(auth) ? auth.chatgpt_account_id : undefined;
|
|
166
|
-
if (typeof accountId !== "string" || !accountId) {
|
|
167
|
-
throw new Error("OpenAI Codex subscription token has no ChatGPT account ID");
|
|
168
|
-
}
|
|
169
|
-
return accountId;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
181
|
function reviewTool(): Record<string, unknown> {
|
|
173
182
|
return {
|
|
174
183
|
type: "function",
|
package/src/config.ts
CHANGED
|
@@ -1,20 +1,37 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import {
|
|
3
|
+
EXECUTION_BACKENDS,
|
|
4
|
+
type ExecutionBackend,
|
|
5
|
+
HARBOR_ENVIRONMENTS,
|
|
6
|
+
type HarborEnvironment,
|
|
7
|
+
matchingHarborEnvironment,
|
|
8
|
+
} from "./providers.js";
|
|
9
|
+
import { parseSandboxTimeoutCapText, STANDARD_VERCEL_TIMEOUT_CAP_MS } from "./sandbox-timeout.js";
|
|
10
|
+
|
|
11
|
+
const emptyStringAsUndefined = (value: unknown): unknown =>
|
|
12
|
+
typeof value === "string" && value.trim() === "" ? undefined : value;
|
|
2
13
|
|
|
3
14
|
const environmentSchema = z.object({
|
|
4
15
|
SELFBENCH_API_HOST: z.string().default("127.0.0.1"),
|
|
5
16
|
SELFBENCH_API_PORT: z.coerce.number().int().min(1).max(65535).default(8080),
|
|
6
|
-
SELFBENCH_API_URL: z.string().url().default("http://127.0.0.1:8080"),
|
|
7
17
|
SELFBENCH_API_TOKEN: z.string().min(1).optional(),
|
|
8
18
|
SELFBENCH_ARTIFACT_BACKEND: z.enum(["local", "gcs"]).default("local"),
|
|
9
19
|
SELFBENCH_ARTIFACT_DIR: z.string().default(".selfbench/artifacts"),
|
|
10
20
|
SELFBENCH_GCS_BUCKET: z.string().optional(),
|
|
11
21
|
SELFBENCH_GCS_PREFIX: z.string().default("selfbench"),
|
|
12
|
-
SELFBENCH_EXECUTION_BACKEND: z.enum(
|
|
22
|
+
SELFBENCH_EXECUTION_BACKEND: z.enum(EXECUTION_BACKENDS).default("docker"),
|
|
13
23
|
SELFBENCH_DOCKER_IMAGE: z.string().default("selfbench-sandbox:local"),
|
|
14
|
-
SELFBENCH_HARBOR_ENVIRONMENT: z.
|
|
24
|
+
SELFBENCH_HARBOR_ENVIRONMENT: z.preprocess(
|
|
25
|
+
emptyStringAsUndefined,
|
|
26
|
+
z.enum(HARBOR_ENVIRONMENTS).optional(),
|
|
27
|
+
),
|
|
15
28
|
SELFBENCH_MODAL_APP: z.string().default("selfbench"),
|
|
16
29
|
SELFBENCH_MODAL_ENVIRONMENT: z.string().optional(),
|
|
17
30
|
SELFBENCH_MODAL_IMAGE: z.string().default("node:22-bookworm"),
|
|
31
|
+
SELFBENCH_VERCEL_IMAGE: z.preprocess(emptyStringAsUndefined, z.string().trim().min(1).optional()),
|
|
32
|
+
// Keep provider-specific validation in the Vercel branch so a stale Vercel
|
|
33
|
+
// variable cannot break an otherwise unrelated Docker or Modal worker.
|
|
34
|
+
SELFBENCH_VERCEL_TIMEOUT_CAP: z.preprocess(emptyStringAsUndefined, z.string().optional()),
|
|
18
35
|
SELFBENCH_BUILD_COMMIT: z.preprocess(
|
|
19
36
|
(value) => (value === "" ? undefined : value),
|
|
20
37
|
z
|
|
@@ -22,7 +39,10 @@ const environmentSchema = z.object({
|
|
|
22
39
|
.regex(/^[0-9a-f]{40}$/i)
|
|
23
40
|
.optional(),
|
|
24
41
|
),
|
|
25
|
-
SELFBENCH_ACTIVITY_CONCURRENCY: z.
|
|
42
|
+
SELFBENCH_ACTIVITY_CONCURRENCY: z.preprocess(
|
|
43
|
+
emptyStringAsUndefined,
|
|
44
|
+
z.coerce.number().int().min(1).max(100).optional(),
|
|
45
|
+
),
|
|
26
46
|
SELFBENCH_TASK_QUEUE: z.string().default("selfbench-dev"),
|
|
27
47
|
SELFBENCH_TEMPORAL_ADDRESS: z.string().default("127.0.0.1:7233"),
|
|
28
48
|
SELFBENCH_TEMPORAL_NAMESPACE: z.string().default("default"),
|
|
@@ -33,25 +53,48 @@ const environmentSchema = z.object({
|
|
|
33
53
|
.default(false),
|
|
34
54
|
});
|
|
35
55
|
|
|
56
|
+
export interface VercelCredentials {
|
|
57
|
+
readonly token: string;
|
|
58
|
+
readonly teamId: string;
|
|
59
|
+
readonly projectId: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
type ExecutionConfig =
|
|
63
|
+
| { readonly kind: "docker"; readonly image: string }
|
|
64
|
+
| {
|
|
65
|
+
readonly kind: "modal";
|
|
66
|
+
readonly app: string;
|
|
67
|
+
readonly environment?: string;
|
|
68
|
+
readonly image: string;
|
|
69
|
+
}
|
|
70
|
+
| { readonly kind: "vercel"; readonly image: string; readonly timeoutCapMs: number };
|
|
71
|
+
|
|
72
|
+
type WorkerExecutionConfig =
|
|
73
|
+
| Exclude<ExecutionConfig, { readonly kind: "vercel" }>
|
|
74
|
+
| {
|
|
75
|
+
readonly kind: "vercel";
|
|
76
|
+
readonly image: string;
|
|
77
|
+
readonly timeoutCapMs: number;
|
|
78
|
+
readonly credentials: VercelCredentials;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const DEFAULT_ACTIVITY_CONCURRENCY = {
|
|
82
|
+
docker: 1,
|
|
83
|
+
modal: 20,
|
|
84
|
+
vercel: 4,
|
|
85
|
+
} as const satisfies Record<ExecutionBackend, number>;
|
|
86
|
+
|
|
36
87
|
export interface SelfBenchConfig {
|
|
37
88
|
readonly apiHost: string;
|
|
38
89
|
readonly apiPort: number;
|
|
39
|
-
readonly apiUrl: string;
|
|
40
90
|
readonly apiToken?: string;
|
|
41
91
|
readonly buildCommit?: string;
|
|
42
92
|
readonly activityConcurrency: number;
|
|
43
93
|
readonly artifact:
|
|
44
94
|
| { readonly kind: "local"; readonly directory: string }
|
|
45
95
|
| { readonly kind: "gcs"; readonly bucket: string; readonly prefix: string };
|
|
46
|
-
readonly execution:
|
|
47
|
-
|
|
48
|
-
| {
|
|
49
|
-
readonly kind: "modal";
|
|
50
|
-
readonly app: string;
|
|
51
|
-
readonly environment?: string;
|
|
52
|
-
readonly image: string;
|
|
53
|
-
};
|
|
54
|
-
readonly harborEnvironment: "docker" | "modal";
|
|
96
|
+
readonly execution: ExecutionConfig;
|
|
97
|
+
readonly harborEnvironment: HarborEnvironment;
|
|
55
98
|
readonly temporal: {
|
|
56
99
|
readonly address: string;
|
|
57
100
|
readonly namespace: string;
|
|
@@ -61,6 +104,14 @@ export interface SelfBenchConfig {
|
|
|
61
104
|
};
|
|
62
105
|
}
|
|
63
106
|
|
|
107
|
+
export interface SelfBenchWorkerConfig extends Omit<SelfBenchConfig, "execution"> {
|
|
108
|
+
readonly execution: WorkerExecutionConfig;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function isDigestPinnedOciImage(image: string): boolean {
|
|
112
|
+
return /^[^@\s]+@sha256:[0-9a-f]{64}$/i.test(image);
|
|
113
|
+
}
|
|
114
|
+
|
|
64
115
|
export function loadConfig(environment: NodeJS.ProcessEnv = process.env): SelfBenchConfig {
|
|
65
116
|
const value = environmentSchema.parse(environment);
|
|
66
117
|
if (
|
|
@@ -78,30 +129,56 @@ export function loadConfig(environment: NodeJS.ProcessEnv = process.env): SelfBe
|
|
|
78
129
|
}
|
|
79
130
|
: { kind: "local" as const, directory: value.SELFBENCH_ARTIFACT_DIR };
|
|
80
131
|
|
|
132
|
+
const harborEnvironment =
|
|
133
|
+
value.SELFBENCH_HARBOR_ENVIRONMENT ??
|
|
134
|
+
matchingHarborEnvironment(value.SELFBENCH_EXECUTION_BACKEND) ??
|
|
135
|
+
fail(
|
|
136
|
+
`SELFBENCH_HARBOR_ENVIRONMENT is required for ${value.SELFBENCH_EXECUTION_BACKEND} execution`,
|
|
137
|
+
);
|
|
138
|
+
let execution: ExecutionConfig;
|
|
139
|
+
switch (value.SELFBENCH_EXECUTION_BACKEND) {
|
|
140
|
+
case "docker":
|
|
141
|
+
execution = { kind: "docker", image: value.SELFBENCH_DOCKER_IMAGE };
|
|
142
|
+
break;
|
|
143
|
+
case "modal":
|
|
144
|
+
execution = {
|
|
145
|
+
kind: "modal",
|
|
146
|
+
app: value.SELFBENCH_MODAL_APP,
|
|
147
|
+
image: value.SELFBENCH_MODAL_IMAGE,
|
|
148
|
+
...(value.SELFBENCH_MODAL_ENVIRONMENT
|
|
149
|
+
? { environment: value.SELFBENCH_MODAL_ENVIRONMENT }
|
|
150
|
+
: {}),
|
|
151
|
+
};
|
|
152
|
+
break;
|
|
153
|
+
case "vercel": {
|
|
154
|
+
const image =
|
|
155
|
+
value.SELFBENCH_VERCEL_IMAGE ??
|
|
156
|
+
fail("SELFBENCH_VERCEL_IMAGE is required for Vercel execution");
|
|
157
|
+
if (!isDigestPinnedOciImage(image)) {
|
|
158
|
+
fail("SELFBENCH_VERCEL_IMAGE must be pinned by sha256 digest");
|
|
159
|
+
}
|
|
160
|
+
execution = {
|
|
161
|
+
kind: "vercel",
|
|
162
|
+
image,
|
|
163
|
+
timeoutCapMs: vercelTimeoutCap(value.SELFBENCH_VERCEL_TIMEOUT_CAP),
|
|
164
|
+
};
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
81
169
|
return {
|
|
82
170
|
apiHost: value.SELFBENCH_API_HOST,
|
|
83
171
|
apiPort: value.SELFBENCH_API_PORT,
|
|
84
|
-
apiUrl: value.SELFBENCH_API_URL,
|
|
85
172
|
...(value.SELFBENCH_API_TOKEN ? { apiToken: value.SELFBENCH_API_TOKEN } : {}),
|
|
86
173
|
...(value.SELFBENCH_BUILD_COMMIT
|
|
87
174
|
? { buildCommit: value.SELFBENCH_BUILD_COMMIT.toLowerCase() }
|
|
88
175
|
: {}),
|
|
89
176
|
activityConcurrency:
|
|
90
177
|
value.SELFBENCH_ACTIVITY_CONCURRENCY ??
|
|
91
|
-
|
|
178
|
+
DEFAULT_ACTIVITY_CONCURRENCY[value.SELFBENCH_EXECUTION_BACKEND],
|
|
92
179
|
artifact,
|
|
93
|
-
harborEnvironment
|
|
94
|
-
execution
|
|
95
|
-
value.SELFBENCH_EXECUTION_BACKEND === "modal"
|
|
96
|
-
? {
|
|
97
|
-
kind: "modal",
|
|
98
|
-
app: value.SELFBENCH_MODAL_APP,
|
|
99
|
-
image: value.SELFBENCH_MODAL_IMAGE,
|
|
100
|
-
...(value.SELFBENCH_MODAL_ENVIRONMENT
|
|
101
|
-
? { environment: value.SELFBENCH_MODAL_ENVIRONMENT }
|
|
102
|
-
: {}),
|
|
103
|
-
}
|
|
104
|
-
: { kind: "docker", image: value.SELFBENCH_DOCKER_IMAGE },
|
|
180
|
+
harborEnvironment,
|
|
181
|
+
execution,
|
|
105
182
|
temporal: {
|
|
106
183
|
address: value.SELFBENCH_TEMPORAL_ADDRESS,
|
|
107
184
|
namespace: value.SELFBENCH_TEMPORAL_NAMESPACE,
|
|
@@ -112,6 +189,51 @@ export function loadConfig(environment: NodeJS.ProcessEnv = process.env): SelfBe
|
|
|
112
189
|
};
|
|
113
190
|
}
|
|
114
191
|
|
|
192
|
+
export function loadWorkerConfig(
|
|
193
|
+
environment: NodeJS.ProcessEnv = process.env,
|
|
194
|
+
): SelfBenchWorkerConfig {
|
|
195
|
+
const config = loadConfig(environment);
|
|
196
|
+
if (config.execution.kind !== "vercel") {
|
|
197
|
+
return { ...config, execution: config.execution };
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
...config,
|
|
201
|
+
execution: {
|
|
202
|
+
...config.execution,
|
|
203
|
+
credentials: vercelCredentials(environment),
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function defaultStandaloneConcurrency(
|
|
209
|
+
config: Pick<SelfBenchConfig, "activityConcurrency" | "execution">,
|
|
210
|
+
existingDefault: number,
|
|
211
|
+
): number {
|
|
212
|
+
return config.execution.kind === "vercel" ? config.activityConcurrency : existingDefault;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function vercelCredentials(environment: NodeJS.ProcessEnv): VercelCredentials {
|
|
216
|
+
const token = environment.VERCEL_TOKEN?.trim();
|
|
217
|
+
const teamId = environment.VERCEL_TEAM_ID?.trim();
|
|
218
|
+
const projectId = environment.VERCEL_PROJECT_ID?.trim();
|
|
219
|
+
if (!token || !teamId || !projectId) {
|
|
220
|
+
fail("VERCEL_TOKEN, VERCEL_TEAM_ID, and VERCEL_PROJECT_ID are required for Vercel execution");
|
|
221
|
+
}
|
|
222
|
+
return { token, teamId, projectId };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function vercelTimeoutCap(value: string | undefined): number {
|
|
226
|
+
if (value === undefined) {
|
|
227
|
+
return STANDARD_VERCEL_TIMEOUT_CAP_MS;
|
|
228
|
+
}
|
|
229
|
+
return z
|
|
230
|
+
.number()
|
|
231
|
+
.int()
|
|
232
|
+
.min(100)
|
|
233
|
+
.max(STANDARD_VERCEL_TIMEOUT_CAP_MS)
|
|
234
|
+
.parse(parseSandboxTimeoutCapText(value));
|
|
235
|
+
}
|
|
236
|
+
|
|
115
237
|
function fail(message: string): never {
|
|
116
238
|
throw new Error(message);
|
|
117
239
|
}
|
package/src/contracts.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { EXECUTION_BACKENDS, HARBOR_ENVIRONMENTS, matchingHarborEnvironment } from "./providers.js";
|
|
2
3
|
|
|
3
4
|
export const commitSchema = z.string().regex(/^[0-9a-f]{40}$/i, "expected a full commit SHA");
|
|
4
5
|
|
|
@@ -34,6 +35,37 @@ const candidateCountsSchema = z
|
|
|
34
35
|
message: "at most 100 candidates may be requested",
|
|
35
36
|
});
|
|
36
37
|
|
|
38
|
+
const runVersionSchema = z
|
|
39
|
+
.object({
|
|
40
|
+
selfbenchCommit: commitSchema,
|
|
41
|
+
executionBackend: z.enum(EXECUTION_BACKENDS),
|
|
42
|
+
harborEnvironment: z.enum(HARBOR_ENVIRONMENTS).optional(),
|
|
43
|
+
sandboxImage: z.string().min(1),
|
|
44
|
+
sandboxTimeoutCapMs: z.number().int().min(100).optional(),
|
|
45
|
+
schema: z.literal(1),
|
|
46
|
+
})
|
|
47
|
+
.transform((version, context) => {
|
|
48
|
+
const harborEnvironment =
|
|
49
|
+
version.harborEnvironment ?? matchingHarborEnvironment(version.executionBackend);
|
|
50
|
+
if (!harborEnvironment) {
|
|
51
|
+
context.addIssue({
|
|
52
|
+
code: "custom",
|
|
53
|
+
message: `harborEnvironment is required for ${version.executionBackend} execution`,
|
|
54
|
+
path: ["harborEnvironment"],
|
|
55
|
+
});
|
|
56
|
+
return z.NEVER;
|
|
57
|
+
}
|
|
58
|
+
if (version.sandboxTimeoutCapMs !== undefined && version.executionBackend !== "vercel") {
|
|
59
|
+
context.addIssue({
|
|
60
|
+
code: "custom",
|
|
61
|
+
message: "sandboxTimeoutCapMs is only valid for Vercel execution",
|
|
62
|
+
path: ["sandboxTimeoutCapMs"],
|
|
63
|
+
});
|
|
64
|
+
return z.NEVER;
|
|
65
|
+
}
|
|
66
|
+
return { ...version, harborEnvironment };
|
|
67
|
+
});
|
|
68
|
+
|
|
37
69
|
export const runRequestSchema = z.object({
|
|
38
70
|
runId: z.string().regex(/^[a-z0-9][a-z0-9-]{2,62}$/),
|
|
39
71
|
repository: repositoryRefSchema,
|
|
@@ -44,12 +76,7 @@ export const runRequestSchema = z.object({
|
|
|
44
76
|
model: z.string().min(1),
|
|
45
77
|
reasoningEffort: z.literal("high"),
|
|
46
78
|
}),
|
|
47
|
-
version:
|
|
48
|
-
selfbenchCommit: commitSchema,
|
|
49
|
-
executionBackend: z.enum(["docker", "modal"]),
|
|
50
|
-
sandboxImage: z.string().min(1),
|
|
51
|
-
schema: z.literal(1),
|
|
52
|
-
}),
|
|
79
|
+
version: runVersionSchema,
|
|
53
80
|
});
|
|
54
81
|
|
|
55
82
|
export type RunRequest = z.infer<typeof runRequestSchema>;
|
|
@@ -130,15 +157,10 @@ export type RunPhase =
|
|
|
130
157
|
| "queued"
|
|
131
158
|
| "discovering"
|
|
132
159
|
| "authoring"
|
|
133
|
-
| "validating"
|
|
134
|
-
| "reviewing"
|
|
135
|
-
| "repairing"
|
|
136
|
-
| "auditing"
|
|
137
160
|
| "exporting"
|
|
138
161
|
| "complete"
|
|
139
162
|
| "blocked"
|
|
140
163
|
| "failed"
|
|
141
|
-
| "cancelling"
|
|
142
164
|
| "cancelled";
|
|
143
165
|
|
|
144
166
|
export interface TaskProgress {
|
package/src/eval-main.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { parseArgs } from "node:util";
|
|
4
4
|
import { MATRIX_MODELS, type MatrixModel, runMatrix } from "./evaluate.js";
|
|
5
|
+
import { isHarborEnvironment } from "./providers.js";
|
|
5
6
|
|
|
6
7
|
const parsed = parseArgs({
|
|
7
8
|
options: {
|
|
@@ -18,7 +19,7 @@ const parsed = parseArgs({
|
|
|
18
19
|
strict: true,
|
|
19
20
|
});
|
|
20
21
|
if (parsed.values.help) {
|
|
21
|
-
console.log(`Run Harbor tasks through the fixed Codex
|
|
22
|
+
console.log(`Run Harbor tasks through the fixed Codex model matrix.
|
|
22
23
|
|
|
23
24
|
Usage:
|
|
24
25
|
self-bench-eval (--export FILE.tar.gz | --tasks DIRECTORY) --jobs DIRECTORY [options]
|
|
@@ -29,12 +30,12 @@ Options:
|
|
|
29
30
|
--environment docker|modal Execution environment (default: modal)
|
|
30
31
|
--concurrency N Concurrent trials (default: 3)
|
|
31
32
|
--model MODEL Run only this model; may be repeated
|
|
32
|
-
--auth FILE Codex ChatGPT auth.json
|
|
33
|
+
--auth FILE Optional Codex ChatGPT auth.json fallback
|
|
33
34
|
-h, --help Show this help`);
|
|
34
35
|
process.exit(0);
|
|
35
36
|
}
|
|
36
|
-
const environment = parsed.values.environment;
|
|
37
|
-
if (environment
|
|
37
|
+
const environment = parsed.values.environment ?? "modal";
|
|
38
|
+
if (!isHarborEnvironment(environment)) {
|
|
38
39
|
throw new Error("--environment must be docker or modal");
|
|
39
40
|
}
|
|
40
41
|
const concurrency = positiveInteger(parsed.values.concurrency, "--concurrency");
|
package/src/evaluate.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { cp, mkdir, mkdtemp, readdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
2
2
|
import { homedir, tmpdir } from "node:os";
|
|
3
3
|
import { basename, join, resolve } from "node:path";
|
|
4
|
+
import { harborChildEnvironment } from "./harbor-environment.js";
|
|
4
5
|
import { archiveIncompleteHarborJob, tryReadHarborJobResult } from "./harbor-results.js";
|
|
5
6
|
import { parallelMap } from "./parallel.js";
|
|
6
7
|
import { runCommand } from "./process.js";
|
|
7
|
-
import {
|
|
8
|
+
import type { HarborEnvironment } from "./providers.js";
|
|
9
|
+
import { assertCodexSubscriptionAuth, openAiApiKey } from "./subscription-auth.js";
|
|
8
10
|
|
|
9
11
|
export const MATRIX_MODELS = ["gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"] as const;
|
|
10
12
|
export type MatrixModel = (typeof MATRIX_MODELS)[number];
|
|
@@ -15,7 +17,7 @@ export interface MatrixOptions {
|
|
|
15
17
|
readonly tasksPath?: string;
|
|
16
18
|
readonly jobsDirectory: string;
|
|
17
19
|
readonly harborPath?: string;
|
|
18
|
-
readonly environment?:
|
|
20
|
+
readonly environment?: HarborEnvironment;
|
|
19
21
|
readonly concurrency?: number;
|
|
20
22
|
readonly authPath?: string;
|
|
21
23
|
readonly models?: readonly MatrixModel[];
|
|
@@ -40,8 +42,11 @@ export async function runMatrix(options: MatrixOptions): Promise<readonly Matrix
|
|
|
40
42
|
const tasksDirectory = join(root, "tasks");
|
|
41
43
|
await mkdir(root, { recursive: true });
|
|
42
44
|
const taskDirectories = await resolveMatrixTasks(options, tasksDirectory);
|
|
45
|
+
const apiKey = openAiApiKey();
|
|
43
46
|
const authPath = resolve(options.authPath ?? join(homedir(), ".codex/auth.json"));
|
|
44
|
-
|
|
47
|
+
if (!apiKey) {
|
|
48
|
+
await assertSubscriptionAuth(authPath);
|
|
49
|
+
}
|
|
45
50
|
const models = options.models ?? MATRIX_MODELS;
|
|
46
51
|
if (models.length < 1) {
|
|
47
52
|
throw new Error("provide at least one evaluation model");
|
|
@@ -55,7 +60,7 @@ export async function runMatrix(options: MatrixOptions): Promise<readonly Matrix
|
|
|
55
60
|
jobsDirectory: root,
|
|
56
61
|
harborPath: options.harborPath ?? "harbor",
|
|
57
62
|
environment: options.environment ?? "modal",
|
|
58
|
-
authPath,
|
|
63
|
+
...(!apiKey ? { authPath } : {}),
|
|
59
64
|
...item,
|
|
60
65
|
});
|
|
61
66
|
completed += 1;
|
|
@@ -69,7 +74,7 @@ export async function runMatrix(options: MatrixOptions): Promise<readonly Matrix
|
|
|
69
74
|
schemaVersion: 1,
|
|
70
75
|
agent: "codex",
|
|
71
76
|
agentVersion: CODEX_VERSION,
|
|
72
|
-
auth: "codex-subscription",
|
|
77
|
+
auth: apiKey ? "openai-api-key" : "codex-subscription",
|
|
73
78
|
reasoningEffort: "high",
|
|
74
79
|
models,
|
|
75
80
|
taskCount: taskDirectories.length,
|
|
@@ -111,8 +116,8 @@ async function runTrial(input: {
|
|
|
111
116
|
readonly model: MatrixModel;
|
|
112
117
|
readonly jobsDirectory: string;
|
|
113
118
|
readonly harborPath: string;
|
|
114
|
-
readonly environment:
|
|
115
|
-
readonly authPath
|
|
119
|
+
readonly environment: HarborEnvironment;
|
|
120
|
+
readonly authPath?: string;
|
|
116
121
|
}): Promise<MatrixTrialSummary> {
|
|
117
122
|
const taskId = basename(input.taskDirectory);
|
|
118
123
|
const jobName = `${taskId}-${input.model}`.toLowerCase().replace(/[^a-z0-9_.-]/g, "-");
|
|
@@ -122,10 +127,12 @@ async function runTrial(input: {
|
|
|
122
127
|
}
|
|
123
128
|
await archiveIncompleteHarborJob(input.jobsDirectory, jobName);
|
|
124
129
|
|
|
125
|
-
const environment =
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
130
|
+
const environment = harborChildEnvironment();
|
|
131
|
+
if (input.authPath) {
|
|
132
|
+
delete environment.OPENAI_API_KEY;
|
|
133
|
+
environment.CODEX_FORCE_AUTH_JSON = "1";
|
|
134
|
+
environment.CODEX_AUTH_JSON_PATH = input.authPath;
|
|
135
|
+
}
|
|
129
136
|
let result: Awaited<ReturnType<typeof runCommand>>;
|
|
130
137
|
try {
|
|
131
138
|
result = await runCommand(
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const VERCEL_CONTROL_CREDENTIALS = [
|
|
2
|
+
"VERCEL_AUTH_TOKEN",
|
|
3
|
+
"VERCEL_TOKEN",
|
|
4
|
+
"VERCEL_TEAM_ID",
|
|
5
|
+
"VERCEL_PROJECT_ID",
|
|
6
|
+
"VERCEL_OIDC_TOKEN",
|
|
7
|
+
] as const;
|
|
8
|
+
|
|
9
|
+
export function harborChildEnvironment(
|
|
10
|
+
environment: NodeJS.ProcessEnv = process.env,
|
|
11
|
+
): NodeJS.ProcessEnv {
|
|
12
|
+
const child = { ...environment };
|
|
13
|
+
for (const key of VERCEL_CONTROL_CREDENTIALS) {
|
|
14
|
+
delete child[key];
|
|
15
|
+
}
|
|
16
|
+
return child;
|
|
17
|
+
}
|
package/src/harbor-results.ts
CHANGED
|
@@ -20,6 +20,8 @@ const infrastructurePatterns = [
|
|
|
20
20
|
/modal.*(?:unavailable|timed out|timeout)/i,
|
|
21
21
|
/image build for im-[a-z0-9]+ failed/i,
|
|
22
22
|
/all predefined address pools have been fully subnetted/i,
|
|
23
|
+
/mounts denied/i,
|
|
24
|
+
/network-policy[\s\S]*could not process rule/i,
|
|
23
25
|
];
|
|
24
26
|
|
|
25
27
|
class IncompleteHarborJobError extends Error {}
|
package/src/process.ts
CHANGED
|
@@ -6,6 +6,8 @@ export interface CommandResult {
|
|
|
6
6
|
readonly exitCode: number;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
export type CommandOutputHandler = (stream: "stdout" | "stderr", chunk: Uint8Array) => void;
|
|
10
|
+
|
|
9
11
|
export interface CommandOptions {
|
|
10
12
|
readonly cwd?: string;
|
|
11
13
|
readonly env?: NodeJS.ProcessEnv;
|
|
@@ -14,7 +16,7 @@ export interface CommandOptions {
|
|
|
14
16
|
readonly inactivityTimeoutMs?: number;
|
|
15
17
|
readonly allowFailure?: boolean;
|
|
16
18
|
readonly signal?: AbortSignal;
|
|
17
|
-
readonly onOutput?:
|
|
19
|
+
readonly onOutput?: CommandOutputHandler;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
export class InactivityTimeoutError extends Error {
|
package/src/provenance.ts
CHANGED
|
@@ -4,7 +4,6 @@ import { assertPullRequestBelongsToRepository, githubRepository } from "./github
|
|
|
4
4
|
import { runCommand } from "./process.js";
|
|
5
5
|
|
|
6
6
|
export type SessionProvenanceFormat = "codex" | "claude-code" | "pi" | "generic";
|
|
7
|
-
export type ProvenanceFormat = SessionProvenanceFormat | "github-pull-request";
|
|
8
7
|
|
|
9
8
|
interface ProvenanceMessageBase {
|
|
10
9
|
readonly sessionId: string;
|
package/src/providers.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const EXECUTION_BACKENDS = ["docker", "modal", "vercel"] as const;
|
|
2
|
+
export type ExecutionBackend = (typeof EXECUTION_BACKENDS)[number];
|
|
3
|
+
|
|
4
|
+
export const HARBOR_ENVIRONMENTS = ["docker", "modal"] as const;
|
|
5
|
+
export type HarborEnvironment = (typeof HARBOR_ENVIRONMENTS)[number];
|
|
6
|
+
|
|
7
|
+
const executionBackends: ReadonlySet<string> = new Set(EXECUTION_BACKENDS);
|
|
8
|
+
const harborEnvironments: ReadonlySet<string> = new Set(HARBOR_ENVIRONMENTS);
|
|
9
|
+
|
|
10
|
+
export function isExecutionBackend(value: string): value is ExecutionBackend {
|
|
11
|
+
return executionBackends.has(value);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function isHarborEnvironment(value: string): value is HarborEnvironment {
|
|
15
|
+
return harborEnvironments.has(value);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function matchingHarborEnvironment(
|
|
19
|
+
backend: ExecutionBackend,
|
|
20
|
+
): HarborEnvironment | undefined {
|
|
21
|
+
return isHarborEnvironment(backend) ? backend : undefined;
|
|
22
|
+
}
|