@brainbase-labs/cli 0.22.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -5
- package/dist/index.js +958 -80
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -36008,7 +36008,7 @@ function padStart(s, n) {
|
|
|
36008
36008
|
// package.json
|
|
36009
36009
|
var package_default = {
|
|
36010
36010
|
name: "@brainbase-labs/cli",
|
|
36011
|
-
version: "0.
|
|
36011
|
+
version: "0.24.0",
|
|
36012
36012
|
description: "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
|
|
36013
36013
|
type: "module",
|
|
36014
36014
|
bin: {
|
|
@@ -53931,6 +53931,76 @@ function clearTokenIfMatches(prefix) {
|
|
|
53931
53931
|
});
|
|
53932
53932
|
}
|
|
53933
53933
|
|
|
53934
|
+
// src/core/credentials.ts
|
|
53935
|
+
var REGISTRY_COMMANDS = "template, skill, token";
|
|
53936
|
+
var CONTROL_PLANE_COMMANDS = "agent, orchestration, link, unlink, sync, status, team";
|
|
53937
|
+
var NOT_A_PAT = "(not a bbpat_… token)";
|
|
53938
|
+
function maskPat(token) {
|
|
53939
|
+
if (!isValidTokenFormat(token))
|
|
53940
|
+
return NOT_A_PAT;
|
|
53941
|
+
return token.slice(0, TOKEN_PREFIX_LEN);
|
|
53942
|
+
}
|
|
53943
|
+
function readEnvPat() {
|
|
53944
|
+
const value = process.env.BRAINBASE_TOKEN?.trim();
|
|
53945
|
+
if (!value)
|
|
53946
|
+
return null;
|
|
53947
|
+
return { prefix: maskPat(value), malformed: !isValidTokenFormat(value) };
|
|
53948
|
+
}
|
|
53949
|
+
function storedPatIgnoredHint(client) {
|
|
53950
|
+
const setEnv = `Set BRAINBASE_TOKEN to that token's value to authenticate with it here, ` + "or run `brainbase login` to refresh the session";
|
|
53951
|
+
switch (client) {
|
|
53952
|
+
case "control-plane":
|
|
53953
|
+
return `The PAT saved in ${TOKEN_FILE} is never used by control-plane ` + `commands (${CONTROL_PLANE_COMMANDS}). ${setEnv}.`;
|
|
53954
|
+
case "managed-task":
|
|
53955
|
+
return `The PAT saved in ${TOKEN_FILE} is not used while a login session is ` + `configured. ${setEnv} — or \`brainbase logout\`, which leaves the ` + "PAT as the only credential.";
|
|
53956
|
+
default: {
|
|
53957
|
+
const exhaustive = client;
|
|
53958
|
+
throw new Error(`unhandled credential client: ${String(exhaustive)}`);
|
|
53959
|
+
}
|
|
53960
|
+
}
|
|
53961
|
+
}
|
|
53962
|
+
function withStoredPatHint(reason, client) {
|
|
53963
|
+
if (!readToken())
|
|
53964
|
+
return reason;
|
|
53965
|
+
const trimmed = reason.trimEnd();
|
|
53966
|
+
const separator = /[.!?:]$/.test(trimmed) ? " " : ". ";
|
|
53967
|
+
return `${trimmed}${separator}${storedPatIgnoredHint(client)}`;
|
|
53968
|
+
}
|
|
53969
|
+
function withRejectedSessionHint(message, source, client) {
|
|
53970
|
+
if (source !== "session")
|
|
53971
|
+
return message;
|
|
53972
|
+
return withStoredPatHint(message, client);
|
|
53973
|
+
}
|
|
53974
|
+
function isRefreshable(session) {
|
|
53975
|
+
return Boolean(session.refresh_token && session.supabase_url && session.supabase_anon_key);
|
|
53976
|
+
}
|
|
53977
|
+
function describeCredentials() {
|
|
53978
|
+
const env3 = readEnvPat();
|
|
53979
|
+
const status = authStatus();
|
|
53980
|
+
const stored = readToken();
|
|
53981
|
+
const refreshable = Boolean(status.session && !status.ok && isRefreshable(status.session));
|
|
53982
|
+
const session = status.session ? {
|
|
53983
|
+
email: status.session.email ?? null,
|
|
53984
|
+
userId: status.session.user_id,
|
|
53985
|
+
expiresAt: status.session.expires_at ?? null,
|
|
53986
|
+
expired: !status.ok,
|
|
53987
|
+
refreshable
|
|
53988
|
+
} : null;
|
|
53989
|
+
let active = null;
|
|
53990
|
+
if (env3)
|
|
53991
|
+
active = "env_pat";
|
|
53992
|
+
else if (status.ok || refreshable)
|
|
53993
|
+
active = "session";
|
|
53994
|
+
else if (stored)
|
|
53995
|
+
active = "stored_pat";
|
|
53996
|
+
return {
|
|
53997
|
+
active,
|
|
53998
|
+
envPat: env3,
|
|
53999
|
+
session,
|
|
54000
|
+
storedPat: stored ? { name: stored.name ?? null, prefix: stored.prefix } : null
|
|
54001
|
+
};
|
|
54002
|
+
}
|
|
54003
|
+
|
|
53934
54004
|
// src/core/api.ts
|
|
53935
54005
|
var DEFAULT_CONTROL_PLANE_BASE = "https://api.brainbaselabs.com";
|
|
53936
54006
|
var DEFAULT_PROXY_BASE = "https://api.v1.brainbaselabs.com";
|
|
@@ -54005,7 +54075,7 @@ function legacyScheduleError() {
|
|
|
54005
54075
|
function legacyAgentConfigError() {
|
|
54006
54076
|
return new ApiError("Declarative machine/model config requires the MAS control plane. Set BRAINBASE_CONTROL_PLANE_URL or unset the legacy BRAINBASE_API_URL override.", 400);
|
|
54007
54077
|
}
|
|
54008
|
-
async function resolveCredential() {
|
|
54078
|
+
async function resolveCredential(storedPatHint = "control-plane") {
|
|
54009
54079
|
const envToken = process.env.BRAINBASE_TOKEN;
|
|
54010
54080
|
if (envToken && envToken.trim()) {
|
|
54011
54081
|
return {
|
|
@@ -54023,19 +54093,20 @@ async function resolveCredential() {
|
|
|
54023
54093
|
};
|
|
54024
54094
|
}
|
|
54025
54095
|
const status = authStatus();
|
|
54026
|
-
|
|
54096
|
+
const reason = status.reason ?? "not logged in";
|
|
54097
|
+
throw new ApiError(status.ok ? "CLI authentication changed while this command was starting; retry it" : storedPatHint ? withStoredPatHint(reason, storedPatHint) : reason, 401);
|
|
54027
54098
|
}
|
|
54028
54099
|
async function resolveMasCredential() {
|
|
54029
54100
|
const configuredSession = readAuth();
|
|
54030
54101
|
try {
|
|
54031
|
-
return await resolveCredential();
|
|
54102
|
+
return await resolveCredential(null);
|
|
54032
54103
|
} catch (error) {
|
|
54033
54104
|
if (!(error instanceof ApiError && error.status === 401)) {
|
|
54034
54105
|
throw error;
|
|
54035
54106
|
}
|
|
54036
54107
|
if (configuredSession || readAuth()) {
|
|
54037
54108
|
if (readToken()) {
|
|
54038
|
-
throw new ApiError(`${error.message}.
|
|
54109
|
+
throw new ApiError(`${error.message}. ${storedPatIgnoredHint("managed-task")}`, 401);
|
|
54039
54110
|
}
|
|
54040
54111
|
throw error;
|
|
54041
54112
|
}
|
|
@@ -54134,7 +54205,8 @@ async function request(pathname, init = {}) {
|
|
|
54134
54205
|
body = text2 ? JSON.parse(text2) : null;
|
|
54135
54206
|
} catch {}
|
|
54136
54207
|
if (!res.ok) {
|
|
54137
|
-
|
|
54208
|
+
const message = apiErrorMessage(body, res.status);
|
|
54209
|
+
throw new ApiError(res.status === 401 ? withRejectedSessionHint(message, credential.source, "control-plane") : message, res.status, body);
|
|
54138
54210
|
}
|
|
54139
54211
|
return body;
|
|
54140
54212
|
}
|
|
@@ -54277,7 +54349,7 @@ async function masRequest(pathname, init) {
|
|
|
54277
54349
|
}
|
|
54278
54350
|
if (!res.ok) {
|
|
54279
54351
|
const message = masApiErrorMessage(body, res.status);
|
|
54280
|
-
throw new ApiError(res.status >= 500 ? `${message} Task creation may still be processing; check your tasks before running this command again.` : message, res.status, body);
|
|
54352
|
+
throw new ApiError(res.status >= 500 ? `${message} Task creation may still be processing; check your tasks before running this command again.` : res.status === 401 ? withRejectedSessionHint(message, credential.source, "managed-task") : message, res.status, body);
|
|
54281
54353
|
}
|
|
54282
54354
|
return body;
|
|
54283
54355
|
}
|
|
@@ -61658,12 +61730,13 @@ async function runLogin(_cwd, args) {
|
|
|
61658
61730
|
// src/ui/ink/IdentityCard.tsx
|
|
61659
61731
|
var jsx_dev_runtime15 = __toESM(require_jsx_dev_runtime(), 1);
|
|
61660
61732
|
function IdentityCard(props) {
|
|
61733
|
+
const hasRows = Boolean(props.rows?.length || props.controlPlaneUrl || props.expiresAt);
|
|
61661
61734
|
return /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(Card, {
|
|
61662
61735
|
title: props.title,
|
|
61663
61736
|
tone: props.tone ?? "info",
|
|
61664
61737
|
children: [
|
|
61665
61738
|
props.email && /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(Box_default, {
|
|
61666
|
-
marginBottom:
|
|
61739
|
+
marginBottom: hasRows ? 1 : 0,
|
|
61667
61740
|
children: /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(Text, {
|
|
61668
61741
|
bold: true,
|
|
61669
61742
|
children: props.email
|
|
@@ -61672,6 +61745,20 @@ function IdentityCard(props) {
|
|
|
61672
61745
|
/* @__PURE__ */ jsx_dev_runtime15.jsxDEV(Box_default, {
|
|
61673
61746
|
flexDirection: "column",
|
|
61674
61747
|
children: [
|
|
61748
|
+
props.rows?.map(([label, value]) => /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(Box_default, {
|
|
61749
|
+
children: [
|
|
61750
|
+
/* @__PURE__ */ jsx_dev_runtime15.jsxDEV(Box_default, {
|
|
61751
|
+
width: 15,
|
|
61752
|
+
children: /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(Text, {
|
|
61753
|
+
dimColor: true,
|
|
61754
|
+
children: label
|
|
61755
|
+
}, undefined, false, undefined, this)
|
|
61756
|
+
}, undefined, false, undefined, this),
|
|
61757
|
+
/* @__PURE__ */ jsx_dev_runtime15.jsxDEV(Text, {
|
|
61758
|
+
children: value
|
|
61759
|
+
}, undefined, false, undefined, this)
|
|
61760
|
+
]
|
|
61761
|
+
}, label, true, undefined, this)),
|
|
61675
61762
|
props.controlPlaneUrl && /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(Box_default, {
|
|
61676
61763
|
children: [
|
|
61677
61764
|
/* @__PURE__ */ jsx_dev_runtime15.jsxDEV(Box_default, {
|
|
@@ -61739,22 +61826,117 @@ async function runLogout() {
|
|
|
61739
61826
|
}
|
|
61740
61827
|
|
|
61741
61828
|
// src/cli/whoami.ts
|
|
61742
|
-
|
|
61743
|
-
|
|
61744
|
-
|
|
61745
|
-
|
|
61746
|
-
|
|
61747
|
-
|
|
61748
|
-
|
|
61749
|
-
|
|
61750
|
-
|
|
61829
|
+
function usableForControlPlane(report) {
|
|
61830
|
+
switch (report.active) {
|
|
61831
|
+
case "env_pat":
|
|
61832
|
+
return !report.envPat?.malformed;
|
|
61833
|
+
case "session":
|
|
61834
|
+
return true;
|
|
61835
|
+
case "stored_pat":
|
|
61836
|
+
case null:
|
|
61837
|
+
return false;
|
|
61838
|
+
default: {
|
|
61839
|
+
const exhaustive = report.active;
|
|
61840
|
+
throw new Error(`unhandled credential: ${String(exhaustive)}`);
|
|
61841
|
+
}
|
|
61751
61842
|
}
|
|
61843
|
+
}
|
|
61844
|
+
function exitCodeFor(report) {
|
|
61845
|
+
return usableForControlPlane(report) ? 0 : 1;
|
|
61846
|
+
}
|
|
61847
|
+
function reasonFor(report) {
|
|
61848
|
+
switch (report.active) {
|
|
61849
|
+
case "env_pat":
|
|
61850
|
+
return report.envPat?.malformed ? "BRAINBASE_TOKEN is set to something that is not a `bbpat_…` token, " + "and it outranks every other credential — so every request will fail " + "until it is corrected or unset. Mint one with `brainbase token create`." : null;
|
|
61851
|
+
case "session":
|
|
61852
|
+
return null;
|
|
61853
|
+
case "stored_pat":
|
|
61854
|
+
return `The stored PAT authenticates registry commands (${REGISTRY_COMMANDS}) only. ` + `Set BRAINBASE_TOKEN to its value for ${CONTROL_PLANE_COMMANDS}, ` + "or run `brainbase login`.";
|
|
61855
|
+
case null:
|
|
61856
|
+
return report.session?.expired ? "Session expired. Run `brainbase login` to connect this device." : "Not logged in. Run `brainbase login` to connect this device.";
|
|
61857
|
+
default: {
|
|
61858
|
+
const exhaustive = report.active;
|
|
61859
|
+
throw new Error(`unhandled credential: ${String(exhaustive)}`);
|
|
61860
|
+
}
|
|
61861
|
+
}
|
|
61862
|
+
}
|
|
61863
|
+
function toJson(report, reason) {
|
|
61864
|
+
const usingSession = report.active === "session";
|
|
61865
|
+
return {
|
|
61866
|
+
credential: report.active,
|
|
61867
|
+
authenticated: report.active !== null && !report.envPat?.malformed,
|
|
61868
|
+
control_plane: usableForControlPlane(report),
|
|
61869
|
+
email: usingSession ? report.session?.email ?? null : null,
|
|
61870
|
+
user_id: usingSession ? report.session?.userId ?? null : null,
|
|
61871
|
+
expires_at: usingSession ? report.session?.expiresAt ?? null : null,
|
|
61872
|
+
token_prefix: report.active === "env_pat" ? report.envPat?.prefix ?? null : report.active === "stored_pat" ? report.storedPat?.prefix ?? null : null,
|
|
61873
|
+
control_plane_url: controlPlaneBaseUrl(usingSession ? readAuth() : null),
|
|
61874
|
+
session: report.session ? {
|
|
61875
|
+
present: true,
|
|
61876
|
+
expired: report.session.expired,
|
|
61877
|
+
refreshable: report.session.refreshable
|
|
61878
|
+
} : { present: false, expired: false, refreshable: false },
|
|
61879
|
+
stored_pat: report.storedPat ? { present: true, name: report.storedPat.name } : { present: false, name: null },
|
|
61880
|
+
reason
|
|
61881
|
+
};
|
|
61882
|
+
}
|
|
61883
|
+
function displayPrefix(prefix) {
|
|
61884
|
+
return prefix === NOT_A_PAT ? prefix : `${prefix}…`;
|
|
61885
|
+
}
|
|
61886
|
+
function cardRows(report) {
|
|
61887
|
+
const rows = [];
|
|
61888
|
+
switch (report.active) {
|
|
61889
|
+
case "env_pat":
|
|
61890
|
+
rows.push(["credential", "BRAINBASE_TOKEN (env PAT)"]);
|
|
61891
|
+
if (report.envPat) {
|
|
61892
|
+
rows.push(["token", displayPrefix(report.envPat.prefix)]);
|
|
61893
|
+
}
|
|
61894
|
+
break;
|
|
61895
|
+
case "session":
|
|
61896
|
+
rows.push([
|
|
61897
|
+
"credential",
|
|
61898
|
+
report.session?.refreshable ? "login session (auth.json) — expired, renews on next use" : "login session (auth.json)"
|
|
61899
|
+
]);
|
|
61900
|
+
break;
|
|
61901
|
+
case "stored_pat":
|
|
61902
|
+
rows.push(["credential", "stored PAT (token.json)"]);
|
|
61903
|
+
rows.push([
|
|
61904
|
+
"token",
|
|
61905
|
+
report.storedPat?.name ? `${displayPrefix(report.storedPat.prefix)} (${report.storedPat.name})` : displayPrefix(report.storedPat?.prefix ?? "?")
|
|
61906
|
+
]);
|
|
61907
|
+
break;
|
|
61908
|
+
case null:
|
|
61909
|
+
break;
|
|
61910
|
+
default: {
|
|
61911
|
+
const exhaustive = report.active;
|
|
61912
|
+
throw new Error(`unhandled credential: ${String(exhaustive)}`);
|
|
61913
|
+
}
|
|
61914
|
+
}
|
|
61915
|
+
if (report.session && report.active !== "session") {
|
|
61916
|
+
rows.push([
|
|
61917
|
+
"session",
|
|
61918
|
+
`${report.session.email ?? report.session.userId} (${report.session.expired ? "expired" : "not used — env PAT wins"})`
|
|
61919
|
+
]);
|
|
61920
|
+
}
|
|
61921
|
+
return rows;
|
|
61922
|
+
}
|
|
61923
|
+
async function runWhoami(args = {}) {
|
|
61924
|
+
const report = describeCredentials();
|
|
61925
|
+
const reason = reasonFor(report);
|
|
61926
|
+
process.exitCode = exitCodeFor(report);
|
|
61927
|
+
if (args.json) {
|
|
61928
|
+
console.log(JSON.stringify(toJson(report, reason), null, 2));
|
|
61929
|
+
return;
|
|
61930
|
+
}
|
|
61931
|
+
const signedIn = report.active !== null;
|
|
61752
61932
|
await showIdentityCard({
|
|
61753
|
-
title: "WHOAMI",
|
|
61754
|
-
tone: "ok",
|
|
61755
|
-
email: session.email ?? session
|
|
61756
|
-
controlPlaneUrl: controlPlaneBaseUrl(session),
|
|
61757
|
-
expiresAt: session.
|
|
61933
|
+
title: signedIn ? "WHOAMI" : "NOT SIGNED IN",
|
|
61934
|
+
tone: process.exitCode === 0 ? "ok" : "warn",
|
|
61935
|
+
email: report.active === "session" ? report.session?.email ?? report.session?.userId ?? null : null,
|
|
61936
|
+
controlPlaneUrl: signedIn ? controlPlaneBaseUrl(report.active === "session" ? readAuth() : null) : null,
|
|
61937
|
+
expiresAt: report.active === "session" ? report.session?.expiresAt : null,
|
|
61938
|
+
rows: cardRows(report),
|
|
61939
|
+
message: reason ?? undefined
|
|
61758
61940
|
});
|
|
61759
61941
|
}
|
|
61760
61942
|
|
|
@@ -68922,14 +69104,6 @@ function TokenCreatedCard(props) {
|
|
|
68922
69104
|
tone: "warn",
|
|
68923
69105
|
subtitle: "shown ONCE — copy it now",
|
|
68924
69106
|
children: [
|
|
68925
|
-
/* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
|
|
68926
|
-
marginBottom: 1,
|
|
68927
|
-
children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
|
|
68928
|
-
bold: true,
|
|
68929
|
-
color: "yellow",
|
|
68930
|
-
children: props.token
|
|
68931
|
-
}, undefined, false, undefined, this)
|
|
68932
|
-
}, undefined, false, undefined, this),
|
|
68933
69107
|
/* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Box_default, {
|
|
68934
69108
|
flexDirection: "column",
|
|
68935
69109
|
marginBottom: 1,
|
|
@@ -68990,20 +69164,22 @@ function TokenCreatedCard(props) {
|
|
|
68990
69164
|
/* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
|
|
68991
69165
|
dimColor: true,
|
|
68992
69166
|
children: [
|
|
68993
|
-
"saved
|
|
69167
|
+
"saved to ",
|
|
68994
69168
|
props.storedAt,
|
|
68995
|
-
" (mode 0600)"
|
|
69169
|
+
" (mode 0600) — covers ",
|
|
69170
|
+
REGISTRY_COMMANDS
|
|
68996
69171
|
]
|
|
68997
69172
|
}, undefined, true, undefined, this),
|
|
68998
69173
|
/* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
|
|
68999
69174
|
dimColor: true,
|
|
69000
69175
|
children: [
|
|
69001
|
-
|
|
69176
|
+
CONTROL_PLANE_COMMANDS,
|
|
69177
|
+
" do ",
|
|
69002
69178
|
/* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
|
|
69003
69179
|
bold: true,
|
|
69004
|
-
children: "
|
|
69180
|
+
children: "not"
|
|
69005
69181
|
}, undefined, false, undefined, this),
|
|
69006
|
-
"
|
|
69182
|
+
" read that file — export it instead:"
|
|
69007
69183
|
]
|
|
69008
69184
|
}, undefined, true, undefined, this)
|
|
69009
69185
|
]
|
|
@@ -69012,9 +69188,12 @@ function TokenCreatedCard(props) {
|
|
|
69012
69188
|
}, undefined, true, undefined, this);
|
|
69013
69189
|
}
|
|
69014
69190
|
async function showTokenCreatedCard(props) {
|
|
69191
|
+
const { token, ...card } = props;
|
|
69015
69192
|
await renderStatic(/* @__PURE__ */ jsx_dev_runtime17.jsxDEV(TokenCreatedCard, {
|
|
69016
|
-
...
|
|
69193
|
+
...card
|
|
69017
69194
|
}, undefined, false, undefined, this));
|
|
69195
|
+
console.log(` export BRAINBASE_TOKEN=${token}`);
|
|
69196
|
+
console.log("");
|
|
69018
69197
|
}
|
|
69019
69198
|
function TokenListCard(props) {
|
|
69020
69199
|
if (props.active.length === 0) {
|
|
@@ -69180,11 +69359,32 @@ function isAllowedScope(value) {
|
|
|
69180
69359
|
function isExpired2(token) {
|
|
69181
69360
|
return Boolean(token.expires_at && Date.parse(token.expires_at) <= Date.now());
|
|
69182
69361
|
}
|
|
69362
|
+
function isJwtRequired(body) {
|
|
69363
|
+
if (!body || typeof body !== "object")
|
|
69364
|
+
return false;
|
|
69365
|
+
const detail = body.detail;
|
|
69366
|
+
if (!detail || typeof detail !== "object")
|
|
69367
|
+
return false;
|
|
69368
|
+
return detail.code === "jwt_required";
|
|
69369
|
+
}
|
|
69370
|
+
function sessionWouldBeUsed() {
|
|
69371
|
+
const { ok, session } = authStatus();
|
|
69372
|
+
if (ok)
|
|
69373
|
+
return true;
|
|
69374
|
+
return Boolean(session?.refresh_token && session.supabase_url && session.supabase_anon_key);
|
|
69375
|
+
}
|
|
69183
69376
|
function withLoginHint(error) {
|
|
69184
|
-
if (error instanceof ApiError
|
|
69185
|
-
return
|
|
69186
|
-
|
|
69187
|
-
|
|
69377
|
+
if (!(error instanceof ApiError) || error.status !== 403)
|
|
69378
|
+
return error;
|
|
69379
|
+
if (!isJwtRequired(error.body))
|
|
69380
|
+
return error;
|
|
69381
|
+
if (!process.env.BRAINBASE_TOKEN?.trim()) {
|
|
69382
|
+
if (sessionWouldBeUsed() || !readToken())
|
|
69383
|
+
return error;
|
|
69384
|
+
return new Error("Managing tokens needs a logged-in session; the stored PAT cannot. " + "Run `brainbase login` and try again.");
|
|
69385
|
+
}
|
|
69386
|
+
const lead = "Managing tokens needs a logged-in session, and BRAINBASE_TOKEN is set — " + "that PAT is being used instead.";
|
|
69387
|
+
return new Error(sessionWouldBeUsed() ? `${lead} Run \`unset BRAINBASE_TOKEN\` and try again.` : `${lead} Run \`unset BRAINBASE_TOKEN\`, then \`brainbase login\`.`);
|
|
69188
69388
|
}
|
|
69189
69389
|
async function runTokenCreate(args) {
|
|
69190
69390
|
banner("token create — make a long-lived CLI key");
|
|
@@ -69207,10 +69407,16 @@ async function runTokenCreate(args) {
|
|
|
69207
69407
|
}
|
|
69208
69408
|
const spinner = de();
|
|
69209
69409
|
spinner.start("Creating token…");
|
|
69210
|
-
|
|
69211
|
-
|
|
69212
|
-
|
|
69213
|
-
|
|
69410
|
+
let created;
|
|
69411
|
+
try {
|
|
69412
|
+
created = await registryApi.createCliToken({
|
|
69413
|
+
name,
|
|
69414
|
+
scopes
|
|
69415
|
+
});
|
|
69416
|
+
} catch (error) {
|
|
69417
|
+
spinner.stop("Could not create token.");
|
|
69418
|
+
throw withLoginHint(error);
|
|
69419
|
+
}
|
|
69214
69420
|
spinner.stop("Token created.");
|
|
69215
69421
|
try {
|
|
69216
69422
|
writeToken(created.token, name);
|
|
@@ -69231,7 +69437,12 @@ async function runTokenCreate(args) {
|
|
|
69231
69437
|
}
|
|
69232
69438
|
async function runTokenList() {
|
|
69233
69439
|
banner("tokens");
|
|
69234
|
-
|
|
69440
|
+
let tokens;
|
|
69441
|
+
try {
|
|
69442
|
+
tokens = await registryApi.listCliTokens();
|
|
69443
|
+
} catch (error) {
|
|
69444
|
+
throw withLoginHint(error);
|
|
69445
|
+
}
|
|
69235
69446
|
const active = tokens.filter((t) => !t.revoked_at);
|
|
69236
69447
|
const revoked = tokens.filter((t) => t.revoked_at);
|
|
69237
69448
|
const local = readToken();
|
|
@@ -69354,7 +69565,7 @@ async function runTokenRevoke(args) {
|
|
|
69354
69565
|
}
|
|
69355
69566
|
throw new Error(`The server reported no active token with id ${args.id}, but it listed one a moment ago. ` + "The local token has been left alone, since that key may still work. " + "If this server predates `DELETE /v1/registry/cli-tokens/{id}`, revoke from the web app instead.");
|
|
69356
69567
|
}
|
|
69357
|
-
throw error;
|
|
69568
|
+
throw withLoginHint(error);
|
|
69358
69569
|
}
|
|
69359
69570
|
reconcileDeadToken(target, `Revoked ${import_picocolors48.default.bold(target.name)}.`);
|
|
69360
69571
|
}
|
|
@@ -78261,7 +78472,9 @@ import crypto6 from "node:crypto";
|
|
|
78261
78472
|
import fs81 from "node:fs";
|
|
78262
78473
|
import os16 from "node:os";
|
|
78263
78474
|
import path88 from "node:path";
|
|
78475
|
+
import { Readable, Transform as Transform2 } from "node:stream";
|
|
78264
78476
|
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
78477
|
+
import { createGunzip, createInflateRaw } from "node:zlib";
|
|
78265
78478
|
var SCHEMA_VERSION = "1";
|
|
78266
78479
|
var SHA256_RE = /^[a-f0-9]{64}$/i;
|
|
78267
78480
|
var ID_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/;
|
|
@@ -78269,6 +78482,9 @@ var ENV_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
|
78269
78482
|
var MAX_SPEC_BYTES = 20 * 1024 * 1024;
|
|
78270
78483
|
var MAX_FINAL_OUTPUT_BYTES = 10 * 1024 * 1024;
|
|
78271
78484
|
var MAX_TRAJECTORY_BYTES = 100 * 1024 * 1024;
|
|
78485
|
+
var MAX_REMOTE_INPUT_BYTES = 2 * 1024 * 1024 * 1024;
|
|
78486
|
+
var MAX_ARCHIVE_EXTRACTED_BYTES = 2 * 1024 * 1024 * 1024;
|
|
78487
|
+
var MAX_ARCHIVE_SCAN_BYTES = 2 * 1024 * 1024 * 1024;
|
|
78272
78488
|
var RESERVED_WORKSPACE_PATHS = new Set([
|
|
78273
78489
|
".brainbase",
|
|
78274
78490
|
".git",
|
|
@@ -78296,21 +78512,51 @@ var NonSecretEnvironmentValueSchema = exports_external.object({
|
|
|
78296
78512
|
sensitive: exports_external.literal(false)
|
|
78297
78513
|
}).strict();
|
|
78298
78514
|
var RootRelativePathSchema = exports_external.string().min(1).max(1024);
|
|
78515
|
+
var ArchivePathSchema = RootRelativePathSchema.refine((value) => {
|
|
78516
|
+
try {
|
|
78517
|
+
return safeRelPath(value) === value;
|
|
78518
|
+
} catch {
|
|
78519
|
+
return false;
|
|
78520
|
+
}
|
|
78521
|
+
}, {
|
|
78522
|
+
message: "must be a normalized safe relative path"
|
|
78523
|
+
});
|
|
78299
78524
|
var BudgetSchema = exports_external.object({
|
|
78300
78525
|
timeout_ms: exports_external.number().int().min(100).max(3600000),
|
|
78301
78526
|
max_output_bytes: exports_external.number().int().min(1024).max(100 * 1024 * 1024)
|
|
78302
78527
|
}).strict();
|
|
78303
|
-
var
|
|
78528
|
+
var MaterialBaseSchema = {
|
|
78304
78529
|
id: IdSchema,
|
|
78305
|
-
kind: exports_external.enum(["file", "tar_gz"]),
|
|
78306
78530
|
source: RootRelativePathSchema,
|
|
78531
|
+
download_url_env: EnvNameSchema.optional(),
|
|
78307
78532
|
destination: RootRelativePathSchema,
|
|
78308
78533
|
sha256: Sha256Schema,
|
|
78309
78534
|
size_bytes: exports_external.number().int().min(0).max(2 * 1024 * 1024 * 1024),
|
|
78310
|
-
mode: exports_external.number().int().min(0).max(511).optional()
|
|
78535
|
+
mode: exports_external.number().int().min(0).max(511).optional()
|
|
78536
|
+
};
|
|
78537
|
+
var ExpandableMaterialSchema = {
|
|
78311
78538
|
max_unpacked_bytes: exports_external.number().int().min(1).max(2 * 1024 * 1024 * 1024).optional(),
|
|
78312
78539
|
max_file_count: exports_external.number().int().min(1).max(1e5).optional()
|
|
78313
|
-
}
|
|
78540
|
+
};
|
|
78541
|
+
var MaterialSchema = exports_external.discriminatedUnion("kind", [
|
|
78542
|
+
exports_external.object({
|
|
78543
|
+
...MaterialBaseSchema,
|
|
78544
|
+
...ExpandableMaterialSchema,
|
|
78545
|
+
kind: exports_external.literal("file")
|
|
78546
|
+
}).strict(),
|
|
78547
|
+
exports_external.object({
|
|
78548
|
+
...MaterialBaseSchema,
|
|
78549
|
+
...ExpandableMaterialSchema,
|
|
78550
|
+
kind: exports_external.literal("tar_gz")
|
|
78551
|
+
}).strict(),
|
|
78552
|
+
exports_external.object({
|
|
78553
|
+
...MaterialBaseSchema,
|
|
78554
|
+
kind: exports_external.literal("archive_file"),
|
|
78555
|
+
archive_path: ArchivePathSchema,
|
|
78556
|
+
file_sha256: Sha256Schema,
|
|
78557
|
+
file_size_bytes: exports_external.number().int().min(0).max(2 * 1024 * 1024 * 1024)
|
|
78558
|
+
}).strict()
|
|
78559
|
+
]);
|
|
78314
78560
|
var CommandSchema = exports_external.object({
|
|
78315
78561
|
id: IdSchema,
|
|
78316
78562
|
argv: exports_external.array(exports_external.string().min(1)).min(1).max(128),
|
|
@@ -78377,6 +78623,7 @@ var EvaluatorSchema = exports_external.discriminatedUnion("type", [
|
|
|
78377
78623
|
]);
|
|
78378
78624
|
var EvidenceFileSchema = exports_external.object({
|
|
78379
78625
|
source: RootRelativePathSchema,
|
|
78626
|
+
download_url_env: EnvNameSchema.optional(),
|
|
78380
78627
|
sha256: Sha256Schema,
|
|
78381
78628
|
size_bytes: exports_external.number().int().min(0).max(2 * 1024 * 1024 * 1024)
|
|
78382
78629
|
}).strict();
|
|
@@ -78421,6 +78668,35 @@ var BenchmarkSpecSchema = exports_external.discriminatedUnion("phase", [
|
|
|
78421
78668
|
}
|
|
78422
78669
|
}
|
|
78423
78670
|
const declaredSecrets = new Set(value.secret_env);
|
|
78671
|
+
const remoteInputs = value.phase === "hydrate" ? value.materials : [
|
|
78672
|
+
value.evidence.final_output,
|
|
78673
|
+
value.evidence.trajectory,
|
|
78674
|
+
...value.references
|
|
78675
|
+
];
|
|
78676
|
+
const remoteUrlEnvNames = new Set(remoteInputs.flatMap((input) => input.download_url_env ? [input.download_url_env] : []));
|
|
78677
|
+
const uniqueRemoteInputs = new Map;
|
|
78678
|
+
for (const input of remoteInputs) {
|
|
78679
|
+
if (!input.download_url_env)
|
|
78680
|
+
continue;
|
|
78681
|
+
uniqueRemoteInputs.set([input.source, input.sha256, input.size_bytes].join("\x00"), input.size_bytes);
|
|
78682
|
+
}
|
|
78683
|
+
const remoteInputBytes = [...uniqueRemoteInputs.values()].reduce((total, size2) => total + size2, 0);
|
|
78684
|
+
if (remoteInputBytes > MAX_REMOTE_INPUT_BYTES) {
|
|
78685
|
+
context.addIssue({
|
|
78686
|
+
code: exports_external.ZodIssueCode.custom,
|
|
78687
|
+
path: [value.phase === "hydrate" ? "materials" : "references"],
|
|
78688
|
+
message: "remote inputs exceed the aggregate download byte limit"
|
|
78689
|
+
});
|
|
78690
|
+
}
|
|
78691
|
+
for (const name of remoteUrlEnvNames) {
|
|
78692
|
+
if (Object.hasOwn(value.environment, name)) {
|
|
78693
|
+
context.addIssue({
|
|
78694
|
+
code: exports_external.ZodIssueCode.custom,
|
|
78695
|
+
path: ["environment", name],
|
|
78696
|
+
message: "signed download URLs must be supplied through the process environment"
|
|
78697
|
+
});
|
|
78698
|
+
}
|
|
78699
|
+
}
|
|
78424
78700
|
const commands = value.phase === "hydrate" ? value.setup_commands.map((command, index) => ({
|
|
78425
78701
|
command,
|
|
78426
78702
|
path: ["setup_commands", index, "secret_env"]
|
|
@@ -78430,6 +78706,13 @@ var BenchmarkSpecSchema = exports_external.discriminatedUnion("phase", [
|
|
|
78430
78706
|
}] : []);
|
|
78431
78707
|
for (const { command, path: issuePath } of commands) {
|
|
78432
78708
|
for (const name of command.secret_env) {
|
|
78709
|
+
if (remoteUrlEnvNames.has(name)) {
|
|
78710
|
+
context.addIssue({
|
|
78711
|
+
code: exports_external.ZodIssueCode.custom,
|
|
78712
|
+
path: issuePath,
|
|
78713
|
+
message: `signed download URL environment variables cannot be exposed to commands: ${name}`
|
|
78714
|
+
});
|
|
78715
|
+
}
|
|
78433
78716
|
if (!declaredSecrets.has(name)) {
|
|
78434
78717
|
context.addIssue({
|
|
78435
78718
|
code: exports_external.ZodIssueCode.custom,
|
|
@@ -78456,6 +78739,15 @@ var BenchmarkSpecSchema = exports_external.discriminatedUnion("phase", [
|
|
|
78456
78739
|
});
|
|
78457
78740
|
}
|
|
78458
78741
|
}
|
|
78742
|
+
const materials = value.phase === "hydrate" ? value.materials : value.references;
|
|
78743
|
+
const archiveOutputBytes = materials.reduce((total, material) => total + (material.kind === "archive_file" ? material.file_size_bytes : 0), 0);
|
|
78744
|
+
if (archiveOutputBytes > MAX_ARCHIVE_EXTRACTED_BYTES) {
|
|
78745
|
+
context.addIssue({
|
|
78746
|
+
code: exports_external.ZodIssueCode.custom,
|
|
78747
|
+
path: [value.phase === "hydrate" ? "materials" : "references"],
|
|
78748
|
+
message: "archive-file materials exceed the aggregate extracted byte limit"
|
|
78749
|
+
});
|
|
78750
|
+
}
|
|
78459
78751
|
if (value.phase === "evaluate" && value.evaluators.filter((evaluator) => evaluator.type === "sandbox_command").length > 1) {
|
|
78460
78752
|
context.addIssue({
|
|
78461
78753
|
code: exports_external.ZodIssueCode.custom,
|
|
@@ -78469,6 +78761,10 @@ var BENCHMARK_CAPABILITIES = {
|
|
|
78469
78761
|
cli_version: VERSION,
|
|
78470
78762
|
schema_versions: [SCHEMA_VERSION],
|
|
78471
78763
|
phases: ["hydrate", "evaluate"],
|
|
78764
|
+
features: [
|
|
78765
|
+
"remote_input_references_v1",
|
|
78766
|
+
"archive_file_materials_v1"
|
|
78767
|
+
],
|
|
78472
78768
|
evaluator_types: [
|
|
78473
78769
|
"output_assertion",
|
|
78474
78770
|
"trajectory_assertion",
|
|
@@ -78492,6 +78788,11 @@ class BenchmarkPhaseError extends Error {
|
|
|
78492
78788
|
this.name = "BenchmarkPhaseError";
|
|
78493
78789
|
}
|
|
78494
78790
|
}
|
|
78791
|
+
var ZIP_EOCD_SIGNATURE = 101010256;
|
|
78792
|
+
var ZIP_CENTRAL_SIGNATURE = 33639248;
|
|
78793
|
+
var ZIP_LOCAL_SIGNATURE = 67324752;
|
|
78794
|
+
var MAX_ZIP_EOCD_BYTES = 65535 + 22;
|
|
78795
|
+
var MAX_ARCHIVE_MEMBERS = 1e5;
|
|
78495
78796
|
function nowIso() {
|
|
78496
78797
|
return new Date().toISOString();
|
|
78497
78798
|
}
|
|
@@ -78754,10 +79055,516 @@ async function verifyInput(stagingRoot, material) {
|
|
|
78754
79055
|
if (actual !== material.sha256) {
|
|
78755
79056
|
throw new BenchmarkPhaseError("digest_mismatch", `checksum mismatch for ${material.source}`, { expected: material.sha256, actual });
|
|
78756
79057
|
}
|
|
79058
|
+
return {
|
|
79059
|
+
source,
|
|
79060
|
+
sha256: actual,
|
|
79061
|
+
size: opened.stat.size,
|
|
79062
|
+
mode: opened.stat.mode & 511
|
|
79063
|
+
};
|
|
78757
79064
|
} finally {
|
|
78758
79065
|
fs81.closeSync(opened.fd);
|
|
78759
79066
|
}
|
|
78760
|
-
|
|
79067
|
+
}
|
|
79068
|
+
function inputCacheKey(input) {
|
|
79069
|
+
return [
|
|
79070
|
+
input.source,
|
|
79071
|
+
input.sha256,
|
|
79072
|
+
input.size_bytes
|
|
79073
|
+
].join(":");
|
|
79074
|
+
}
|
|
79075
|
+
async function cachedVerifiedInput(stagingRoot, input, context) {
|
|
79076
|
+
const cacheKey = inputCacheKey(input);
|
|
79077
|
+
const cached2 = context.verifiedInputs.get(cacheKey);
|
|
79078
|
+
if (cached2)
|
|
79079
|
+
return cached2;
|
|
79080
|
+
const verified = await verifyInput(stagingRoot, input);
|
|
79081
|
+
context.verifiedInputs.set(cacheKey, verified);
|
|
79082
|
+
return verified;
|
|
79083
|
+
}
|
|
79084
|
+
function stagedInputRecord(stagingRoot, verified) {
|
|
79085
|
+
return {
|
|
79086
|
+
root: "staging",
|
|
79087
|
+
path: path88.relative(stagingRoot, verified.source).replace(/\\/g, "/"),
|
|
79088
|
+
sha256: verified.sha256,
|
|
79089
|
+
size: verified.size,
|
|
79090
|
+
mode: verified.mode,
|
|
79091
|
+
kind: "file"
|
|
79092
|
+
};
|
|
79093
|
+
}
|
|
79094
|
+
function remoteUrlEnvironmentNames(spec) {
|
|
79095
|
+
const inputs = spec.phase === "hydrate" ? spec.materials : [
|
|
79096
|
+
spec.evidence.final_output,
|
|
79097
|
+
spec.evidence.trajectory,
|
|
79098
|
+
...spec.references
|
|
79099
|
+
];
|
|
79100
|
+
return new Set(inputs.flatMap((input) => input.download_url_env ? [input.download_url_env] : []));
|
|
79101
|
+
}
|
|
79102
|
+
function removeRemoteHydrationInputs(spec) {
|
|
79103
|
+
const removedSources = new Set;
|
|
79104
|
+
for (const material of spec.materials) {
|
|
79105
|
+
if (!material.download_url_env || removedSources.has(material.source))
|
|
79106
|
+
continue;
|
|
79107
|
+
const relative = safeRelPath(material.source);
|
|
79108
|
+
const source = path88.resolve(spec.staging_root, relative);
|
|
79109
|
+
if (!isWithin(spec.staging_root, source)) {
|
|
79110
|
+
throw new BenchmarkPhaseError("unsafe_path", `source escapes staging root: ${material.source}`);
|
|
79111
|
+
}
|
|
79112
|
+
assertNoSymlinkTraversal(spec.staging_root, relative);
|
|
79113
|
+
try {
|
|
79114
|
+
fs81.rmSync(source, { force: true });
|
|
79115
|
+
} catch {
|
|
79116
|
+
throw new BenchmarkPhaseError("staging_cleanup_failed", `downloaded benchmark input could not be removed: ${material.source}`);
|
|
79117
|
+
}
|
|
79118
|
+
removedSources.add(material.source);
|
|
79119
|
+
}
|
|
79120
|
+
}
|
|
79121
|
+
async function downloadInputReference(stagingRoot, input, context) {
|
|
79122
|
+
const cacheKey = inputCacheKey(input);
|
|
79123
|
+
const cached2 = context.verifiedInputs.get(cacheKey);
|
|
79124
|
+
if (cached2)
|
|
79125
|
+
return cached2;
|
|
79126
|
+
const envName = input.download_url_env;
|
|
79127
|
+
if (!envName) {
|
|
79128
|
+
return await cachedVerifiedInput(stagingRoot, input, context);
|
|
79129
|
+
}
|
|
79130
|
+
const relative = safeRelPath(input.source);
|
|
79131
|
+
const destination = path88.resolve(stagingRoot, relative);
|
|
79132
|
+
if (!isWithin(stagingRoot, destination)) {
|
|
79133
|
+
throw new BenchmarkPhaseError("unsafe_path", `source escapes staging root: ${input.source}`);
|
|
79134
|
+
}
|
|
79135
|
+
assertNoSymlinkTraversal(stagingRoot, relative);
|
|
79136
|
+
try {
|
|
79137
|
+
fs81.lstatSync(destination);
|
|
79138
|
+
return await cachedVerifiedInput(stagingRoot, input, context);
|
|
79139
|
+
} catch (error2) {
|
|
79140
|
+
if (error2.code !== "ENOENT")
|
|
79141
|
+
throw error2;
|
|
79142
|
+
}
|
|
79143
|
+
const rawUrl = process.env[envName];
|
|
79144
|
+
if (!rawUrl) {
|
|
79145
|
+
throw new BenchmarkPhaseError("missing_environment", `required signed download URL environment variable is missing: ${envName}`);
|
|
79146
|
+
}
|
|
79147
|
+
let url2;
|
|
79148
|
+
try {
|
|
79149
|
+
url2 = new URL(rawUrl);
|
|
79150
|
+
} catch {
|
|
79151
|
+
throw new BenchmarkPhaseError("invalid_download_url", `signed download URL environment variable is invalid: ${envName}`);
|
|
79152
|
+
}
|
|
79153
|
+
if (url2.protocol !== "https:" || url2.username || url2.password) {
|
|
79154
|
+
throw new BenchmarkPhaseError("invalid_download_url", `signed download URL environment variable must contain an HTTPS URL without credentials: ${envName}`);
|
|
79155
|
+
}
|
|
79156
|
+
const remainingMs = context.deadline - Date.now();
|
|
79157
|
+
if (remainingMs <= 0) {
|
|
79158
|
+
throw new BenchmarkPhaseError("phase_timeout", "phase budget expired");
|
|
79159
|
+
}
|
|
79160
|
+
fs81.mkdirSync(path88.dirname(destination), { recursive: true, mode: 448 });
|
|
79161
|
+
assertNoSymlinkTraversal(stagingRoot, relative);
|
|
79162
|
+
assertWritableDestination(stagingRoot, relative);
|
|
79163
|
+
const temporary = `${destination}.${process.pid}.${crypto6.randomBytes(6).toString("hex")}.download`;
|
|
79164
|
+
const controller = new AbortController;
|
|
79165
|
+
const timer = setTimeout(() => controller.abort(), remainingMs);
|
|
79166
|
+
let descriptor;
|
|
79167
|
+
try {
|
|
79168
|
+
let response;
|
|
79169
|
+
try {
|
|
79170
|
+
response = await fetch(url2, {
|
|
79171
|
+
redirect: "error",
|
|
79172
|
+
signal: controller.signal
|
|
79173
|
+
});
|
|
79174
|
+
} catch {
|
|
79175
|
+
if (controller.signal.aborted || Date.now() >= context.deadline) {
|
|
79176
|
+
throw new BenchmarkPhaseError("phase_timeout", "phase budget expired");
|
|
79177
|
+
}
|
|
79178
|
+
throw new BenchmarkPhaseError("download_failed", `signed input download failed for ${input.source}`);
|
|
79179
|
+
}
|
|
79180
|
+
if (response.redirected || response.status >= 300 && response.status < 400) {
|
|
79181
|
+
throw new BenchmarkPhaseError("download_redirect_rejected", `signed input download redirected for ${input.source}`);
|
|
79182
|
+
}
|
|
79183
|
+
if (!response.ok || !response.body) {
|
|
79184
|
+
throw new BenchmarkPhaseError("download_failed", `signed input download failed for ${input.source}`, { status: response.status });
|
|
79185
|
+
}
|
|
79186
|
+
const declaredLength = response.headers.get("content-length");
|
|
79187
|
+
if (declaredLength !== null && /^\d+$/.test(declaredLength) && Number(declaredLength) > input.size_bytes) {
|
|
79188
|
+
throw new BenchmarkPhaseError("size_mismatch", `size mismatch for ${input.source}`, { expected: input.size_bytes, actual: Number(declaredLength) });
|
|
79189
|
+
}
|
|
79190
|
+
descriptor = fs81.openSync(temporary, "wx", 384);
|
|
79191
|
+
const reader = response.body.getReader();
|
|
79192
|
+
let actual;
|
|
79193
|
+
let size2 = 0;
|
|
79194
|
+
try {
|
|
79195
|
+
const hash = crypto6.createHash("sha256");
|
|
79196
|
+
while (true) {
|
|
79197
|
+
const { done, value } = await reader.read();
|
|
79198
|
+
if (done)
|
|
79199
|
+
break;
|
|
79200
|
+
assertBudget(context);
|
|
79201
|
+
size2 += value.byteLength;
|
|
79202
|
+
if (size2 > input.size_bytes) {
|
|
79203
|
+
throw new BenchmarkPhaseError("size_mismatch", `size mismatch for ${input.source}`, { expected: input.size_bytes, actual: size2 });
|
|
79204
|
+
}
|
|
79205
|
+
hash.update(value);
|
|
79206
|
+
let offset = 0;
|
|
79207
|
+
while (offset < value.byteLength) {
|
|
79208
|
+
offset += fs81.writeSync(descriptor, value, offset, value.byteLength - offset);
|
|
79209
|
+
}
|
|
79210
|
+
}
|
|
79211
|
+
actual = hash.digest("hex");
|
|
79212
|
+
} finally {
|
|
79213
|
+
try {
|
|
79214
|
+
await reader.cancel();
|
|
79215
|
+
} catch {}
|
|
79216
|
+
}
|
|
79217
|
+
if (size2 !== input.size_bytes) {
|
|
79218
|
+
throw new BenchmarkPhaseError("size_mismatch", `size mismatch for ${input.source}`, { expected: input.size_bytes, actual: size2 });
|
|
79219
|
+
}
|
|
79220
|
+
if (actual !== input.sha256) {
|
|
79221
|
+
throw new BenchmarkPhaseError("digest_mismatch", `checksum mismatch for ${input.source}`, { expected: input.sha256, actual });
|
|
79222
|
+
}
|
|
79223
|
+
fs81.fsyncSync(descriptor);
|
|
79224
|
+
fs81.closeSync(descriptor);
|
|
79225
|
+
descriptor = undefined;
|
|
79226
|
+
assertNoSymlinkTraversal(stagingRoot, relative);
|
|
79227
|
+
if (fs81.existsSync(destination)) {
|
|
79228
|
+
return await cachedVerifiedInput(stagingRoot, input, context);
|
|
79229
|
+
}
|
|
79230
|
+
fs81.renameSync(temporary, destination);
|
|
79231
|
+
return await cachedVerifiedInput(stagingRoot, input, context);
|
|
79232
|
+
} catch (error2) {
|
|
79233
|
+
if (!(error2 instanceof BenchmarkPhaseError) && (controller.signal.aborted || Date.now() >= context.deadline)) {
|
|
79234
|
+
throw new BenchmarkPhaseError("phase_timeout", "phase budget expired");
|
|
79235
|
+
}
|
|
79236
|
+
if (error2 instanceof BenchmarkPhaseError)
|
|
79237
|
+
throw error2;
|
|
79238
|
+
throw new BenchmarkPhaseError("download_failed", `signed input download failed for ${input.source}`);
|
|
79239
|
+
} finally {
|
|
79240
|
+
clearTimeout(timer);
|
|
79241
|
+
controller.abort();
|
|
79242
|
+
if (descriptor !== undefined)
|
|
79243
|
+
fs81.closeSync(descriptor);
|
|
79244
|
+
fs81.rmSync(temporary, { force: true });
|
|
79245
|
+
}
|
|
79246
|
+
}
|
|
79247
|
+
function readExactly(descriptor, length, position) {
|
|
79248
|
+
const buffer = Buffer.alloc(length);
|
|
79249
|
+
let offset = 0;
|
|
79250
|
+
while (offset < length) {
|
|
79251
|
+
const count = fs81.readSync(descriptor, buffer, offset, length - offset, position + offset);
|
|
79252
|
+
if (count === 0) {
|
|
79253
|
+
throw new BenchmarkPhaseError("invalid_archive", "archive ended unexpectedly");
|
|
79254
|
+
}
|
|
79255
|
+
offset += count;
|
|
79256
|
+
}
|
|
79257
|
+
return buffer;
|
|
79258
|
+
}
|
|
79259
|
+
function decodeZipPath(value, utf8) {
|
|
79260
|
+
if (!utf8 && value.some((byte) => byte >= 128)) {
|
|
79261
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP members with non-UTF-8 names are unsupported");
|
|
79262
|
+
}
|
|
79263
|
+
try {
|
|
79264
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(value);
|
|
79265
|
+
} catch {
|
|
79266
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP member name is not valid UTF-8");
|
|
79267
|
+
}
|
|
79268
|
+
}
|
|
79269
|
+
function findZipMembers(archivePath, requested, context) {
|
|
79270
|
+
const descriptor = fs81.openSync(archivePath, "r");
|
|
79271
|
+
try {
|
|
79272
|
+
assertBudget(context);
|
|
79273
|
+
const archiveSize = fs81.fstatSync(descriptor).size;
|
|
79274
|
+
const tailSize = Math.min(archiveSize, MAX_ZIP_EOCD_BYTES);
|
|
79275
|
+
const tailOffset = archiveSize - tailSize;
|
|
79276
|
+
const tail2 = readExactly(descriptor, tailSize, tailOffset);
|
|
79277
|
+
let eocdOffset = -1;
|
|
79278
|
+
for (let offset2 = tail2.length - 22;offset2 >= 0; offset2 -= 1) {
|
|
79279
|
+
assertBudget(context);
|
|
79280
|
+
if (tail2.readUInt32LE(offset2) === ZIP_EOCD_SIGNATURE) {
|
|
79281
|
+
const commentLength = tail2.readUInt16LE(offset2 + 20);
|
|
79282
|
+
if (offset2 + 22 + commentLength === tail2.length) {
|
|
79283
|
+
eocdOffset = offset2;
|
|
79284
|
+
break;
|
|
79285
|
+
}
|
|
79286
|
+
}
|
|
79287
|
+
}
|
|
79288
|
+
if (eocdOffset < 0) {
|
|
79289
|
+
throw new BenchmarkPhaseError("invalid_archive", "invalid ZIP archive");
|
|
79290
|
+
}
|
|
79291
|
+
const diskNumber = tail2.readUInt16LE(eocdOffset + 4);
|
|
79292
|
+
const directoryDisk = tail2.readUInt16LE(eocdOffset + 6);
|
|
79293
|
+
const diskEntries = tail2.readUInt16LE(eocdOffset + 8);
|
|
79294
|
+
const totalEntries = tail2.readUInt16LE(eocdOffset + 10);
|
|
79295
|
+
const directorySize = tail2.readUInt32LE(eocdOffset + 12);
|
|
79296
|
+
const directoryOffset = tail2.readUInt32LE(eocdOffset + 16);
|
|
79297
|
+
if (diskNumber !== 0 || directoryDisk !== 0 || diskEntries !== totalEntries) {
|
|
79298
|
+
throw new BenchmarkPhaseError("invalid_archive", "multi-disk ZIP archives are unsupported");
|
|
79299
|
+
}
|
|
79300
|
+
if (totalEntries === 65535 || directorySize === 4294967295 || directoryOffset === 4294967295) {
|
|
79301
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP64 archives are unsupported");
|
|
79302
|
+
}
|
|
79303
|
+
if (totalEntries > MAX_ARCHIVE_MEMBERS || directoryOffset + directorySize > tailOffset + eocdOffset) {
|
|
79304
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP central directory is invalid");
|
|
79305
|
+
}
|
|
79306
|
+
let offset = directoryOffset;
|
|
79307
|
+
const found = new Map;
|
|
79308
|
+
for (let index = 0;index < totalEntries; index += 1) {
|
|
79309
|
+
assertBudget(context);
|
|
79310
|
+
const header = readExactly(descriptor, 46, offset);
|
|
79311
|
+
if (header.readUInt32LE(0) !== ZIP_CENTRAL_SIGNATURE) {
|
|
79312
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP central directory is invalid");
|
|
79313
|
+
}
|
|
79314
|
+
const flags = header.readUInt16LE(8);
|
|
79315
|
+
const compressionMethod = header.readUInt16LE(10);
|
|
79316
|
+
const compressedSize = header.readUInt32LE(20);
|
|
79317
|
+
const uncompressedSize = header.readUInt32LE(24);
|
|
79318
|
+
const nameLength = header.readUInt16LE(28);
|
|
79319
|
+
const extraLength = header.readUInt16LE(30);
|
|
79320
|
+
const commentLength = header.readUInt16LE(32);
|
|
79321
|
+
const diskStart = header.readUInt16LE(34);
|
|
79322
|
+
const externalAttributes = header.readUInt32LE(38);
|
|
79323
|
+
const localHeaderOffset = header.readUInt32LE(42);
|
|
79324
|
+
const recordSize = 46 + nameLength + extraLength + commentLength;
|
|
79325
|
+
if (offset + recordSize > directoryOffset + directorySize || compressedSize === 4294967295 || uncompressedSize === 4294967295 || localHeaderOffset === 4294967295 || diskStart !== 0) {
|
|
79326
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP member metadata is invalid");
|
|
79327
|
+
}
|
|
79328
|
+
const name = decodeZipPath(readExactly(descriptor, nameLength, offset + 46), (flags & 2048) !== 0);
|
|
79329
|
+
const material = requested.get(name);
|
|
79330
|
+
if (material) {
|
|
79331
|
+
if (found.has(name)) {
|
|
79332
|
+
throw new BenchmarkPhaseError("invalid_archive", `archive member is duplicated: ${name}`);
|
|
79333
|
+
}
|
|
79334
|
+
const madeBy = header.readUInt16LE(4) >> 8;
|
|
79335
|
+
const unixMode = externalAttributes >>> 16;
|
|
79336
|
+
const fileType = unixMode & 61440;
|
|
79337
|
+
if (name.endsWith("/") || (externalAttributes & 16) !== 0 || madeBy === 3 && fileType !== 0 && fileType !== 32768) {
|
|
79338
|
+
throw new BenchmarkPhaseError("invalid_archive", `archive member must be a regular file: ${name}`);
|
|
79339
|
+
}
|
|
79340
|
+
if ((flags & 1) !== 0 || compressionMethod !== 0 && compressionMethod !== 8) {
|
|
79341
|
+
throw new BenchmarkPhaseError("invalid_archive", `archive member uses unsupported ZIP features: ${name}`);
|
|
79342
|
+
}
|
|
79343
|
+
if (uncompressedSize !== material.file_size_bytes) {
|
|
79344
|
+
throw new BenchmarkPhaseError("size_mismatch", `size mismatch for archive member ${name}`, { expected: material.file_size_bytes, actual: uncompressedSize });
|
|
79345
|
+
}
|
|
79346
|
+
const localHeader = readExactly(descriptor, 30, localHeaderOffset);
|
|
79347
|
+
if (localHeader.readUInt32LE(0) !== ZIP_LOCAL_SIGNATURE || localHeader.readUInt16LE(6) !== flags || localHeader.readUInt16LE(8) !== compressionMethod) {
|
|
79348
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP local header is invalid");
|
|
79349
|
+
}
|
|
79350
|
+
const localNameLength = localHeader.readUInt16LE(26);
|
|
79351
|
+
const localExtraLength = localHeader.readUInt16LE(28);
|
|
79352
|
+
const localName = decodeZipPath(readExactly(descriptor, localNameLength, localHeaderOffset + 30), (flags & 2048) !== 0);
|
|
79353
|
+
const dataOffset = localHeaderOffset + 30 + localNameLength + localExtraLength;
|
|
79354
|
+
if (localName !== name || dataOffset + compressedSize > directoryOffset) {
|
|
79355
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP member bounds are invalid");
|
|
79356
|
+
}
|
|
79357
|
+
found.set(name, {
|
|
79358
|
+
compressionMethod,
|
|
79359
|
+
compressedSize,
|
|
79360
|
+
dataOffset
|
|
79361
|
+
});
|
|
79362
|
+
}
|
|
79363
|
+
offset += recordSize;
|
|
79364
|
+
}
|
|
79365
|
+
if (offset !== directoryOffset + directorySize) {
|
|
79366
|
+
throw new BenchmarkPhaseError("invalid_archive", "ZIP central directory is invalid");
|
|
79367
|
+
}
|
|
79368
|
+
for (const memberPath of requested.keys()) {
|
|
79369
|
+
if (!found.has(memberPath)) {
|
|
79370
|
+
throw new BenchmarkPhaseError("missing_input", `archive member does not exist: ${memberPath}`);
|
|
79371
|
+
}
|
|
79372
|
+
}
|
|
79373
|
+
return found;
|
|
79374
|
+
} finally {
|
|
79375
|
+
fs81.closeSync(descriptor);
|
|
79376
|
+
}
|
|
79377
|
+
}
|
|
79378
|
+
async function writeVerifiedArchiveMember(source, destination, material, context) {
|
|
79379
|
+
fs81.mkdirSync(path88.dirname(destination), { recursive: true, mode: 448 });
|
|
79380
|
+
const temporary = `${destination}.${process.pid}.${crypto6.randomBytes(6).toString("hex")}.tmp`;
|
|
79381
|
+
const descriptor = fs81.openSync(temporary, "wx", 384);
|
|
79382
|
+
const hash = crypto6.createHash("sha256");
|
|
79383
|
+
let size2 = 0;
|
|
79384
|
+
try {
|
|
79385
|
+
for await (const value of source) {
|
|
79386
|
+
assertBudget(context);
|
|
79387
|
+
const chunk2 = Buffer.isBuffer(value) ? value : Buffer.from(value);
|
|
79388
|
+
size2 += chunk2.length;
|
|
79389
|
+
if (size2 > material.file_size_bytes) {
|
|
79390
|
+
throw new BenchmarkPhaseError("size_mismatch", `size mismatch for archive member ${material.archive_path}`, { expected: material.file_size_bytes, actual: size2 });
|
|
79391
|
+
}
|
|
79392
|
+
hash.update(chunk2);
|
|
79393
|
+
let offset = 0;
|
|
79394
|
+
while (offset < chunk2.length) {
|
|
79395
|
+
offset += fs81.writeSync(descriptor, chunk2, offset, chunk2.length - offset);
|
|
79396
|
+
}
|
|
79397
|
+
}
|
|
79398
|
+
if (size2 !== material.file_size_bytes) {
|
|
79399
|
+
throw new BenchmarkPhaseError("size_mismatch", `size mismatch for archive member ${material.archive_path}`, { expected: material.file_size_bytes, actual: size2 });
|
|
79400
|
+
}
|
|
79401
|
+
const actual = hash.digest("hex");
|
|
79402
|
+
if (actual !== material.file_sha256) {
|
|
79403
|
+
throw new BenchmarkPhaseError("digest_mismatch", `checksum mismatch for archive member ${material.archive_path}`, { expected: material.file_sha256, actual });
|
|
79404
|
+
}
|
|
79405
|
+
fs81.fsyncSync(descriptor);
|
|
79406
|
+
fs81.closeSync(descriptor);
|
|
79407
|
+
fs81.renameSync(temporary, destination);
|
|
79408
|
+
} catch (error2) {
|
|
79409
|
+
if (error2 instanceof BenchmarkPhaseError)
|
|
79410
|
+
throw error2;
|
|
79411
|
+
throw new BenchmarkPhaseError("invalid_archive", `archive member could not be extracted: ${material.archive_path}`);
|
|
79412
|
+
} finally {
|
|
79413
|
+
try {
|
|
79414
|
+
fs81.closeSync(descriptor);
|
|
79415
|
+
} catch {}
|
|
79416
|
+
fs81.rmSync(temporary, { force: true });
|
|
79417
|
+
}
|
|
79418
|
+
}
|
|
79419
|
+
function isZipArchive(archivePath) {
|
|
79420
|
+
const descriptor = fs81.openSync(archivePath, "r");
|
|
79421
|
+
try {
|
|
79422
|
+
const header = Buffer.alloc(4);
|
|
79423
|
+
return fs81.readSync(descriptor, header, 0, header.length, 0) === header.length && [
|
|
79424
|
+
ZIP_LOCAL_SIGNATURE,
|
|
79425
|
+
ZIP_EOCD_SIGNATURE,
|
|
79426
|
+
134695760
|
|
79427
|
+
].includes(header.readUInt32LE(0));
|
|
79428
|
+
} finally {
|
|
79429
|
+
fs81.closeSync(descriptor);
|
|
79430
|
+
}
|
|
79431
|
+
}
|
|
79432
|
+
function isGzipArchive(archivePath) {
|
|
79433
|
+
const descriptor = fs81.openSync(archivePath, "r");
|
|
79434
|
+
try {
|
|
79435
|
+
const header = Buffer.alloc(2);
|
|
79436
|
+
return fs81.readSync(descriptor, header, 0, header.length, 0) === header.length && header[0] === 31 && header[1] === 139;
|
|
79437
|
+
} finally {
|
|
79438
|
+
fs81.closeSync(descriptor);
|
|
79439
|
+
}
|
|
79440
|
+
}
|
|
79441
|
+
function archiveSourceCacheKey(material) {
|
|
79442
|
+
return [
|
|
79443
|
+
material.source,
|
|
79444
|
+
material.sha256,
|
|
79445
|
+
material.size_bytes,
|
|
79446
|
+
material.download_url_env ?? ""
|
|
79447
|
+
].join("\x00");
|
|
79448
|
+
}
|
|
79449
|
+
function archiveMemberCacheKey(material) {
|
|
79450
|
+
return [
|
|
79451
|
+
archiveSourceCacheKey(material),
|
|
79452
|
+
material.archive_path,
|
|
79453
|
+
material.file_sha256,
|
|
79454
|
+
material.file_size_bytes
|
|
79455
|
+
].join("\x00");
|
|
79456
|
+
}
|
|
79457
|
+
function archiveScanBudget(context) {
|
|
79458
|
+
let scanned = 0;
|
|
79459
|
+
return new Transform2({
|
|
79460
|
+
transform(chunk2, _encoding, callback) {
|
|
79461
|
+
try {
|
|
79462
|
+
assertBudget(context);
|
|
79463
|
+
scanned += chunk2.length;
|
|
79464
|
+
if (scanned > MAX_ARCHIVE_SCAN_BYTES) {
|
|
79465
|
+
throw new BenchmarkPhaseError("invalid_archive", "archive expands beyond the supported scan limit");
|
|
79466
|
+
}
|
|
79467
|
+
callback(null, chunk2);
|
|
79468
|
+
} catch (error2) {
|
|
79469
|
+
callback(error2);
|
|
79470
|
+
}
|
|
79471
|
+
}
|
|
79472
|
+
});
|
|
79473
|
+
}
|
|
79474
|
+
async function extractArchiveMembers(archivePath, outputRoot, materials, context) {
|
|
79475
|
+
const requested = new Map;
|
|
79476
|
+
for (const material of materials) {
|
|
79477
|
+
let memberPath;
|
|
79478
|
+
try {
|
|
79479
|
+
memberPath = safeRelPath(material.archive_path);
|
|
79480
|
+
} catch {
|
|
79481
|
+
throw new BenchmarkPhaseError("unsafe_path", "archive member path is unsafe");
|
|
79482
|
+
}
|
|
79483
|
+
const existing = requested.get(memberPath);
|
|
79484
|
+
if (existing && (existing.file_sha256 !== material.file_sha256 || existing.file_size_bytes !== material.file_size_bytes)) {
|
|
79485
|
+
throw new BenchmarkPhaseError("invalid_archive", `archive member has conflicting declarations: ${memberPath}`);
|
|
79486
|
+
}
|
|
79487
|
+
requested.set(memberPath, material);
|
|
79488
|
+
}
|
|
79489
|
+
const extracted = new Map;
|
|
79490
|
+
if (isZipArchive(archivePath)) {
|
|
79491
|
+
const indexed = findZipMembers(archivePath, requested, context);
|
|
79492
|
+
for (const [memberPath, material] of requested) {
|
|
79493
|
+
assertBudget(context);
|
|
79494
|
+
const destination = path88.resolve(outputRoot, memberPath);
|
|
79495
|
+
if (!isWithin(outputRoot, destination)) {
|
|
79496
|
+
throw new BenchmarkPhaseError("unsafe_path", `archive member escapes output root: ${memberPath}`);
|
|
79497
|
+
}
|
|
79498
|
+
const member = indexed.get(memberPath);
|
|
79499
|
+
const compressed = member.compressedSize === 0 ? Readable.from([]) : fs81.createReadStream(archivePath, {
|
|
79500
|
+
start: member.dataOffset,
|
|
79501
|
+
end: member.dataOffset + member.compressedSize - 1
|
|
79502
|
+
});
|
|
79503
|
+
const contents = member.compressionMethod === 8 ? compressed.pipe(createInflateRaw()) : compressed;
|
|
79504
|
+
await writeVerifiedArchiveMember(contents, destination, material, context);
|
|
79505
|
+
extracted.set(memberPath, destination);
|
|
79506
|
+
}
|
|
79507
|
+
return extracted;
|
|
79508
|
+
}
|
|
79509
|
+
const matches2 = new Map;
|
|
79510
|
+
let validationError;
|
|
79511
|
+
const unpack = co({
|
|
79512
|
+
cwd: outputRoot,
|
|
79513
|
+
strict: true,
|
|
79514
|
+
preserveOwner: false,
|
|
79515
|
+
filter: (entryPath, entryAny) => {
|
|
79516
|
+
assertBudget(context);
|
|
79517
|
+
const material = requested.get(entryPath);
|
|
79518
|
+
if (!material)
|
|
79519
|
+
return false;
|
|
79520
|
+
const count = (matches2.get(entryPath) ?? 0) + 1;
|
|
79521
|
+
matches2.set(entryPath, count);
|
|
79522
|
+
const entry = entryAny;
|
|
79523
|
+
if (count > 1) {
|
|
79524
|
+
validationError = new BenchmarkPhaseError("invalid_archive", `archive member is duplicated: ${entryPath}`);
|
|
79525
|
+
return false;
|
|
79526
|
+
}
|
|
79527
|
+
if (!["File", "OldFile", "ContiguousFile"].includes(entry.type)) {
|
|
79528
|
+
validationError = new BenchmarkPhaseError("invalid_archive", `archive member must be a regular file: ${entryPath}`);
|
|
79529
|
+
return false;
|
|
79530
|
+
}
|
|
79531
|
+
if (entry.size !== material.file_size_bytes) {
|
|
79532
|
+
validationError = new BenchmarkPhaseError("size_mismatch", `size mismatch for archive member ${entryPath}`, { expected: material.file_size_bytes, actual: entry.size });
|
|
79533
|
+
return false;
|
|
79534
|
+
}
|
|
79535
|
+
return true;
|
|
79536
|
+
}
|
|
79537
|
+
});
|
|
79538
|
+
const archive = fs81.createReadStream(archivePath);
|
|
79539
|
+
const scanBudget = archiveScanBudget(context);
|
|
79540
|
+
try {
|
|
79541
|
+
if (isGzipArchive(archivePath)) {
|
|
79542
|
+
await pipeline2(archive, createGunzip(), scanBudget, unpack);
|
|
79543
|
+
} else {
|
|
79544
|
+
await pipeline2(archive, scanBudget, unpack);
|
|
79545
|
+
}
|
|
79546
|
+
} catch (error2) {
|
|
79547
|
+
if (validationError)
|
|
79548
|
+
throw validationError;
|
|
79549
|
+
if (error2 instanceof BenchmarkPhaseError)
|
|
79550
|
+
throw error2;
|
|
79551
|
+
throw new BenchmarkPhaseError("invalid_archive", "archive members could not be extracted");
|
|
79552
|
+
}
|
|
79553
|
+
if (validationError)
|
|
79554
|
+
throw validationError;
|
|
79555
|
+
for (const [memberPath, material] of requested) {
|
|
79556
|
+
if (!matches2.has(memberPath)) {
|
|
79557
|
+
throw new BenchmarkPhaseError("missing_input", `archive member does not exist: ${memberPath}`);
|
|
79558
|
+
}
|
|
79559
|
+
const verified = await verifyInput(outputRoot, {
|
|
79560
|
+
source: memberPath,
|
|
79561
|
+
sha256: material.file_sha256,
|
|
79562
|
+
size_bytes: material.file_size_bytes
|
|
79563
|
+
});
|
|
79564
|
+
assertBudget(context);
|
|
79565
|
+
extracted.set(memberPath, verified.source);
|
|
79566
|
+
}
|
|
79567
|
+
return extracted;
|
|
78761
79568
|
}
|
|
78762
79569
|
async function atomicCopy(source, destination, mode, sourceRoot) {
|
|
78763
79570
|
fs81.mkdirSync(path88.dirname(destination), { recursive: true });
|
|
@@ -78806,21 +79613,31 @@ async function recordFile(root, filePath, rootName, kind = "file") {
|
|
|
78806
79613
|
fs81.closeSync(opened.fd);
|
|
78807
79614
|
}
|
|
78808
79615
|
}
|
|
78809
|
-
async function copyMaterial(material, sourceRoot, destinationRoot, destinationRootName, protectWorkspace) {
|
|
78810
|
-
const
|
|
79616
|
+
async function copyMaterial(material, sourceRoot, destinationRoot, destinationRootName, protectWorkspace, context) {
|
|
79617
|
+
const verifiedSource = material.kind === "archive_file" ? undefined : await cachedVerifiedInput(sourceRoot, material, context);
|
|
79618
|
+
const source = material.kind === "archive_file" ? context.preparedArchiveFiles.get(archiveMemberCacheKey(material)) : verifiedSource?.source;
|
|
79619
|
+
if (!source) {
|
|
79620
|
+
throw new BenchmarkPhaseError("invalid_archive", `archive member was not prepared: ${material.id}`);
|
|
79621
|
+
}
|
|
78811
79622
|
const destinationRel = protectWorkspace ? workspaceRel(material.destination, material.kind === "tar_gz") : normalizedRootRelative(material.destination);
|
|
78812
79623
|
if (!protectWorkspace && destinationRel.toLowerCase() === ".brainbase-benchmark-owner.json") {
|
|
78813
79624
|
throw new BenchmarkPhaseError("reserved_path", "benchmark input cannot replace the phase ownership marker");
|
|
78814
79625
|
}
|
|
78815
|
-
if (material.kind === "file") {
|
|
79626
|
+
if (material.kind === "file" || material.kind === "archive_file") {
|
|
78816
79627
|
if (destinationRel === ".") {
|
|
78817
79628
|
throw new BenchmarkPhaseError("unsafe_path", "file destination cannot be a directory root");
|
|
78818
79629
|
}
|
|
78819
79630
|
assertWritableDestination(destinationRoot, destinationRel);
|
|
78820
79631
|
const destination = path88.resolve(destinationRoot, destinationRel);
|
|
78821
|
-
|
|
79632
|
+
if (material.kind === "file") {
|
|
79633
|
+
await atomicCopy(source, destination, material.mode ?? verifiedSource?.mode, sourceRoot);
|
|
79634
|
+
} else {
|
|
79635
|
+
await atomicCopy(source, destination, material.mode, path88.dirname(source));
|
|
79636
|
+
}
|
|
78822
79637
|
const record3 = await recordFile(destinationRoot, destination, destinationRootName);
|
|
78823
|
-
|
|
79638
|
+
const expectedSha256 = material.kind === "archive_file" ? material.file_sha256 : material.sha256;
|
|
79639
|
+
const expectedSize = material.kind === "archive_file" ? material.file_size_bytes : material.size_bytes;
|
|
79640
|
+
if (record3.sha256 !== expectedSha256 || record3.size !== expectedSize) {
|
|
78824
79641
|
throw new BenchmarkPhaseError("evidence_tampered", `material changed while it was copied: ${material.source}`);
|
|
78825
79642
|
}
|
|
78826
79643
|
return [record3];
|
|
@@ -78861,17 +79678,39 @@ async function copyMaterial(material, sourceRoot, destinationRoot, destinationRo
|
|
|
78861
79678
|
fs81.rmSync(temporary, { recursive: true, force: true });
|
|
78862
79679
|
}
|
|
78863
79680
|
}
|
|
78864
|
-
async function preflightMaterial(material, sourceRoot, destinationRoot, protectWorkspace) {
|
|
78865
|
-
const source = await
|
|
79681
|
+
async function preflightMaterial(material, materials, sourceRoot, destinationRoot, protectWorkspace, context) {
|
|
79682
|
+
const source = (await cachedVerifiedInput(sourceRoot, material, context)).source;
|
|
78866
79683
|
const destinationRel = protectWorkspace ? workspaceRel(material.destination, material.kind === "tar_gz") : normalizedRootRelative(material.destination);
|
|
78867
79684
|
if (!protectWorkspace && destinationRel.toLowerCase() === ".brainbase-benchmark-owner.json") {
|
|
78868
79685
|
throw new BenchmarkPhaseError("reserved_path", "benchmark input cannot replace the phase ownership marker");
|
|
78869
79686
|
}
|
|
78870
|
-
if (material.kind === "file") {
|
|
79687
|
+
if (material.kind === "file" || material.kind === "archive_file") {
|
|
78871
79688
|
if (destinationRel === ".") {
|
|
78872
79689
|
throw new BenchmarkPhaseError("unsafe_path", "file destination cannot be a directory root");
|
|
78873
79690
|
}
|
|
78874
79691
|
assertWritableDestination(destinationRoot, destinationRel);
|
|
79692
|
+
if (material.kind === "archive_file") {
|
|
79693
|
+
const cacheKey = archiveMemberCacheKey(material);
|
|
79694
|
+
let extracted = context.preparedArchiveFiles.get(cacheKey);
|
|
79695
|
+
if (!extracted) {
|
|
79696
|
+
const temporary2 = fs81.mkdtempSync(path88.join(os16.tmpdir(), "brainbase-benchmark-member-"));
|
|
79697
|
+
context.temporaryRoots.add(temporary2);
|
|
79698
|
+
const archiveKey = archiveSourceCacheKey(material);
|
|
79699
|
+
const related = materials.filter((candidate) => candidate.kind === "archive_file" && archiveSourceCacheKey(candidate) === archiveKey);
|
|
79700
|
+
const extractedMembers = await extractArchiveMembers(source, temporary2, related, context);
|
|
79701
|
+
for (const candidate of related) {
|
|
79702
|
+
const prepared = extractedMembers.get(safeRelPath(candidate.archive_path));
|
|
79703
|
+
if (!prepared) {
|
|
79704
|
+
throw new BenchmarkPhaseError("missing_input", `archive member does not exist: ${candidate.archive_path}`);
|
|
79705
|
+
}
|
|
79706
|
+
context.preparedArchiveFiles.set(archiveMemberCacheKey(candidate), prepared);
|
|
79707
|
+
}
|
|
79708
|
+
extracted = context.preparedArchiveFiles.get(cacheKey);
|
|
79709
|
+
}
|
|
79710
|
+
if (!extracted) {
|
|
79711
|
+
throw new BenchmarkPhaseError("missing_input", `archive member does not exist: ${material.archive_path}`);
|
|
79712
|
+
}
|
|
79713
|
+
}
|
|
78875
79714
|
return [destinationRel];
|
|
78876
79715
|
}
|
|
78877
79716
|
const temporary = fs81.mkdtempSync(path88.join(os16.tmpdir(), "brainbase-benchmark-preflight-"));
|
|
@@ -78939,14 +79778,18 @@ function prepareOwnedDirectory(root, role, spec) {
|
|
|
78939
79778
|
}
|
|
78940
79779
|
function buildEnvironment(spec, secretNames, additions = {}) {
|
|
78941
79780
|
const env3 = {};
|
|
79781
|
+
const remoteUrlEnvNames = remoteUrlEnvironmentNames(spec);
|
|
78942
79782
|
for (const name of BASE_ENV_NAMES) {
|
|
78943
|
-
if (process.env[name] !== undefined)
|
|
79783
|
+
if (!remoteUrlEnvNames.has(name) && process.env[name] !== undefined) {
|
|
78944
79784
|
env3[name] = process.env[name];
|
|
79785
|
+
}
|
|
78945
79786
|
}
|
|
78946
79787
|
for (const [name, declared] of Object.entries(spec.environment)) {
|
|
78947
79788
|
env3[name] = declared.value;
|
|
78948
79789
|
}
|
|
78949
79790
|
for (const name of secretNames) {
|
|
79791
|
+
if (remoteUrlEnvNames.has(name))
|
|
79792
|
+
continue;
|
|
78950
79793
|
const value = process.env[name];
|
|
78951
79794
|
if (value === undefined) {
|
|
78952
79795
|
throw new BenchmarkPhaseError("missing_environment", `required environment variable is missing: ${name}`);
|
|
@@ -78958,7 +79801,11 @@ function buildEnvironment(spec, secretNames, additions = {}) {
|
|
|
78958
79801
|
}
|
|
78959
79802
|
function redactCommandOutput(data, spec) {
|
|
78960
79803
|
let value = data.toString("utf8");
|
|
78961
|
-
|
|
79804
|
+
const sensitiveNames = new Set([
|
|
79805
|
+
...spec.secret_env,
|
|
79806
|
+
...remoteUrlEnvironmentNames(spec)
|
|
79807
|
+
]);
|
|
79808
|
+
for (const name of sensitiveNames) {
|
|
78962
79809
|
const secret = process.env[name];
|
|
78963
79810
|
if (secret)
|
|
78964
79811
|
value = value.split(secret).join("[REDACTED]");
|
|
@@ -79128,16 +79975,19 @@ async function executeHydrate(spec, context) {
|
|
|
79128
79975
|
const plannedDestinations = [];
|
|
79129
79976
|
for (const material of spec.materials) {
|
|
79130
79977
|
assertBudget(context);
|
|
79131
|
-
|
|
79132
|
-
|
|
79133
|
-
|
|
79978
|
+
const verified = await downloadInputReference(spec.staging_root, material, context);
|
|
79979
|
+
plannedDestinations.push(...await preflightMaterial(material, spec.materials, spec.staging_root, spec.workspace_root, true, context));
|
|
79980
|
+
const record3 = stagedInputRecord(spec.staging_root, verified);
|
|
79981
|
+
if (!context.inputs.some((input) => input.path === record3.path && input.sha256 === record3.sha256 && input.size === record3.size)) {
|
|
79982
|
+
context.inputs.push(record3);
|
|
79983
|
+
}
|
|
79134
79984
|
}
|
|
79135
79985
|
validateDestinationGraph(plannedDestinations);
|
|
79136
79986
|
for (const material of spec.materials) {
|
|
79137
79987
|
assertBudget(context);
|
|
79138
79988
|
const started = Date.now();
|
|
79139
79989
|
try {
|
|
79140
|
-
const records = await copyMaterial(material, spec.staging_root, spec.workspace_root, "workspace", true);
|
|
79990
|
+
const records = await copyMaterial(material, spec.staging_root, spec.workspace_root, "workspace", true, context);
|
|
79141
79991
|
outputs.push(...records);
|
|
79142
79992
|
context.outputs.push(...records);
|
|
79143
79993
|
context.steps.push({
|
|
@@ -79211,6 +80061,7 @@ async function executeHydrate(spec, context) {
|
|
|
79211
80061
|
}
|
|
79212
80062
|
outputs.splice(0, outputs.length, ...finalOutputs);
|
|
79213
80063
|
context.outputs.splice(0, context.outputs.length, ...finalOutputs);
|
|
80064
|
+
removeRemoteHydrationInputs(spec);
|
|
79214
80065
|
assertBudget(context);
|
|
79215
80066
|
return outputs;
|
|
79216
80067
|
}
|
|
@@ -79416,6 +80267,8 @@ async function evaluateOne(evaluator, spec, finalOutput, trajectory, frozenEvide
|
|
|
79416
80267
|
};
|
|
79417
80268
|
}
|
|
79418
80269
|
async function executeEvaluate(spec, context) {
|
|
80270
|
+
await downloadInputReference(spec.staging_root, spec.evidence.final_output, context);
|
|
80271
|
+
await downloadInputReference(spec.staging_root, spec.evidence.trajectory, context);
|
|
79419
80272
|
const finalOutput = await readEvidence(spec.staging_root, spec.evidence.final_output);
|
|
79420
80273
|
const trajectoryEvidence = await readEvidence(spec.staging_root, spec.evidence.trajectory);
|
|
79421
80274
|
context.inputs.push(finalOutput.record, trajectoryEvidence.record);
|
|
@@ -79429,9 +80282,12 @@ async function executeEvaluate(spec, context) {
|
|
|
79429
80282
|
const plannedReferences = [];
|
|
79430
80283
|
for (const reference of spec.references) {
|
|
79431
80284
|
assertBudget(context);
|
|
79432
|
-
|
|
79433
|
-
|
|
79434
|
-
|
|
80285
|
+
const verified = await downloadInputReference(spec.staging_root, reference, context);
|
|
80286
|
+
plannedReferences.push(...await preflightMaterial(reference, spec.references, spec.staging_root, spec.tests_root, false, context));
|
|
80287
|
+
const record3 = stagedInputRecord(spec.staging_root, verified);
|
|
80288
|
+
if (!context.inputs.some((input) => input.path === record3.path && input.sha256 === record3.sha256 && input.size === record3.size)) {
|
|
80289
|
+
context.inputs.push(record3);
|
|
80290
|
+
}
|
|
79435
80291
|
}
|
|
79436
80292
|
validateDestinationGraph(plannedReferences);
|
|
79437
80293
|
assertBudget(context);
|
|
@@ -79496,7 +80352,7 @@ async function executeEvaluate(spec, context) {
|
|
|
79496
80352
|
}
|
|
79497
80353
|
await verifyRecordsUnchanged(manifest, spec);
|
|
79498
80354
|
for (const reference of spec.references) {
|
|
79499
|
-
const referenceOutputs = await copyMaterial(reference, spec.staging_root, spec.tests_root, "tests", false);
|
|
80355
|
+
const referenceOutputs = await copyMaterial(reference, spec.staging_root, spec.tests_root, "tests", false, context);
|
|
79500
80356
|
outputs.push(...referenceOutputs);
|
|
79501
80357
|
context.outputs.push(...referenceOutputs);
|
|
79502
80358
|
}
|
|
@@ -79646,19 +80502,26 @@ function prepareBenchmarkPhaseInvocation(specPathInput, resultPathInput, expecte
|
|
|
79646
80502
|
spec_digest: digest,
|
|
79647
80503
|
timeout_ms: spec.budget.timeout_ms
|
|
79648
80504
|
};
|
|
80505
|
+
let cachedResult;
|
|
79649
80506
|
if (fs81.existsSync(resultPath)) {
|
|
79650
80507
|
try {
|
|
79651
80508
|
const cached2 = JSON.parse(fs81.readFileSync(resultPath, "utf8"));
|
|
79652
80509
|
if (cached2.schema_version === SCHEMA_VERSION && cached2.phase === spec.phase && cached2.attempt_id === spec.attempt_id && cached2.phase_id === spec.phase_id && cached2.spec_digest === digest && cached2.status === "succeeded") {
|
|
79653
|
-
|
|
79654
|
-
ok: true,
|
|
79655
|
-
invocation,
|
|
79656
|
-
spec_bytes: bytes,
|
|
79657
|
-
cached_result: cached2
|
|
79658
|
-
};
|
|
80510
|
+
cachedResult = cached2;
|
|
79659
80511
|
}
|
|
79660
80512
|
} catch {}
|
|
79661
80513
|
}
|
|
80514
|
+
if (cachedResult) {
|
|
80515
|
+
if (spec.phase === "hydrate") {
|
|
80516
|
+
removeRemoteHydrationInputs(spec);
|
|
80517
|
+
}
|
|
80518
|
+
return {
|
|
80519
|
+
ok: true,
|
|
80520
|
+
invocation,
|
|
80521
|
+
spec_bytes: bytes,
|
|
80522
|
+
cached_result: cachedResult
|
|
80523
|
+
};
|
|
80524
|
+
}
|
|
79662
80525
|
return { ok: true, invocation, spec_bytes: bytes };
|
|
79663
80526
|
} catch (error2) {
|
|
79664
80527
|
return {
|
|
@@ -79755,14 +80618,21 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
|
|
|
79755
80618
|
throw new BenchmarkPhaseError("invalid_result_path", `result path must be ${expectedResultPath}`);
|
|
79756
80619
|
}
|
|
79757
80620
|
resultPathValidated = true;
|
|
80621
|
+
let cachedResult;
|
|
79758
80622
|
if (fs81.existsSync(resultPath)) {
|
|
79759
80623
|
try {
|
|
79760
80624
|
const cached2 = JSON.parse(fs81.readFileSync(resultPath, "utf8"));
|
|
79761
80625
|
if (cached2.schema_version === SCHEMA_VERSION && cached2.phase === spec.phase && cached2.attempt_id === spec.attempt_id && cached2.phase_id === spec.phase_id && cached2.spec_digest === digest && cached2.status === "succeeded") {
|
|
79762
|
-
|
|
80626
|
+
cachedResult = cached2;
|
|
79763
80627
|
}
|
|
79764
80628
|
} catch {}
|
|
79765
80629
|
}
|
|
80630
|
+
if (cachedResult) {
|
|
80631
|
+
if (spec.phase === "hydrate") {
|
|
80632
|
+
removeRemoteHydrationInputs(spec);
|
|
80633
|
+
}
|
|
80634
|
+
return { exitCode: 0, result: cachedResult };
|
|
80635
|
+
}
|
|
79766
80636
|
context = {
|
|
79767
80637
|
deadline: started + spec.budget.timeout_ms,
|
|
79768
80638
|
remainingOutputBytes: spec.budget.max_output_bytes,
|
|
@@ -79770,7 +80640,10 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
|
|
|
79770
80640
|
evaluators: [],
|
|
79771
80641
|
inputs: [],
|
|
79772
80642
|
outputs: [],
|
|
79773
|
-
logsOwned: false
|
|
80643
|
+
logsOwned: false,
|
|
80644
|
+
verifiedInputs: new Map,
|
|
80645
|
+
preparedArchiveFiles: new Map,
|
|
80646
|
+
temporaryRoots: new Set
|
|
79774
80647
|
};
|
|
79775
80648
|
if (spec.phase === "hydrate") {
|
|
79776
80649
|
outputs = await executeHydrate(spec, context);
|
|
@@ -79790,6 +80663,11 @@ async function runBenchmarkPhase(specPathInput, resultPathInput, expectedPhase,
|
|
|
79790
80663
|
evaluators = context.evaluators;
|
|
79791
80664
|
}
|
|
79792
80665
|
}
|
|
80666
|
+
if (context) {
|
|
80667
|
+
for (const temporary of context.temporaryRoots) {
|
|
80668
|
+
fs81.rmSync(temporary, { recursive: true, force: true });
|
|
80669
|
+
}
|
|
80670
|
+
}
|
|
79793
80671
|
const result2 = {
|
|
79794
80672
|
schema_version: SCHEMA_VERSION,
|
|
79795
80673
|
cli_version: VERSION,
|
|
@@ -80201,7 +81079,7 @@ function help() {
|
|
|
80201
81079
|
out.push("");
|
|
80202
81080
|
out.push(` ${import_picocolors52.default.cyan("login")} ${import_picocolors52.default.dim(" open the web app and connect this device")}`);
|
|
80203
81081
|
out.push(` ${import_picocolors52.default.cyan("logout")} ${import_picocolors52.default.dim(" clear the local session")}`);
|
|
80204
|
-
out.push(` ${import_picocolors52.default.cyan("whoami")}
|
|
81082
|
+
out.push(` ${import_picocolors52.default.cyan("whoami")} ${import_picocolors52.default.dim("[--json]")} ${import_picocolors52.default.dim(" show which credential is in use and what it covers")}`);
|
|
80205
81083
|
out.push("");
|
|
80206
81084
|
out.push(divider("DISCOVERY"));
|
|
80207
81085
|
out.push("");
|
|
@@ -80304,7 +81182,7 @@ function help() {
|
|
|
80304
81182
|
out.push(` ${import_picocolors52.default.dim("BRAINBASE_API_URL")} legacy KLS host override (uses /api/cli; proxy/registry fallback)`);
|
|
80305
81183
|
out.push(` ${import_picocolors52.default.dim("BRAINBASE_PROXY_URL")} override the model-proxy URL used by harness traffic (default https://api.v1.brainbaselabs.com)`);
|
|
80306
81184
|
out.push(` ${import_picocolors52.default.dim("BRAINBASE_REGISTRY_URL")} override the registry API URL (default https://api.v1.brainbaselabs.com)`);
|
|
80307
|
-
out.push(` ${import_picocolors52.default.dim("BRAINBASE_TOKEN")} long-lived CLI PAT (
|
|
81185
|
+
out.push(` ${import_picocolors52.default.dim("BRAINBASE_TOKEN")} long-lived CLI PAT; the only PAT control-plane commands accept (token.json is not read there)`);
|
|
80308
81186
|
out.push(` ${import_picocolors52.default.dim("BRAINBASE_SKIP_AUTH")} bypass the auth gate for development`);
|
|
80309
81187
|
out.push(` ${import_picocolors52.default.dim("BRAINBASE_NON_INTERACTIVE")} force non-interactive mode — skip/auto-default prompts (CI & agents)`);
|
|
80310
81188
|
out.push(` ${import_picocolors52.default.dim("BRAINBASE_RUN_ENTRYPOINT")} =1 → agent pull executes the agent entrypoint (sandbox boots; or pass --run-entrypoint)`);
|
|
@@ -80556,7 +81434,7 @@ async function main() {
|
|
|
80556
81434
|
break;
|
|
80557
81435
|
}
|
|
80558
81436
|
case "whoami": {
|
|
80559
|
-
await runWhoami();
|
|
81437
|
+
await runWhoami({ json: jsonFlag });
|
|
80560
81438
|
break;
|
|
80561
81439
|
}
|
|
80562
81440
|
case "template": {
|