@brainbase-labs/cli 0.22.0 → 0.23.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 +260 -49
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -144,7 +144,7 @@ Playbook files carry a small YAML frontmatter (`title`, `description`) — the C
|
|
|
144
144
|
| `BRAINBASE_API_URL` | Legacy KLS host override. Control requests use `/api/cli`; it is also the fallback for model-proxy and registry traffic. |
|
|
145
145
|
| `BRAINBASE_PROXY_URL` | Override the model-proxy host used when enabling harness tracking. |
|
|
146
146
|
| `BRAINBASE_REGISTRY_URL` | Override the registry API host. |
|
|
147
|
-
| `BRAINBASE_TOKEN` | Use a PAT instead of the stored login session |
|
|
147
|
+
| `BRAINBASE_TOKEN` | Use a PAT instead of the stored login session. The only way a PAT authenticates a control-plane command — see [Auth](#auth). |
|
|
148
148
|
| `BRAINBASE_HOME` | Where local state lives (default: `~/.brainbase`) |
|
|
149
149
|
|
|
150
150
|
Model-proxy and registry traffic fall back through `BRAINBASE_API_URL`,
|
|
@@ -152,11 +152,29 @@ the server captured at login, and finally `https://api.v1.brainbaselabs.com`.
|
|
|
152
152
|
|
|
153
153
|
## Auth
|
|
154
154
|
|
|
155
|
-
|
|
155
|
+
Three credentials, and which ones a command considers depends on the service
|
|
156
|
+
it talks to:
|
|
156
157
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
| Commands | Credentials tried, in order |
|
|
159
|
+
|-|-|
|
|
160
|
+
| `agent`, `orchestration`, `link`, `unlink`, `sync`, `status`, `team` | `BRAINBASE_TOKEN`, then the `auth.json` session |
|
|
161
|
+
| `template`, `skill`, `token` | `BRAINBASE_TOKEN`, then the session, then `token.json` |
|
|
162
|
+
| `task create` | `BRAINBASE_TOKEN`, then the session, then `token.json` — the last only when no session is configured |
|
|
163
|
+
|
|
164
|
+
`brainbase whoami` (or `--json`) reports which one is active.
|
|
165
|
+
|
|
166
|
+
**For CI:** `brainbase token create` saves the PAT to `~/.brainbase/token.json`,
|
|
167
|
+
and control-plane commands never read that file — `brainbase agent push` needs
|
|
168
|
+
the token in `BRAINBASE_TOKEN`:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
export BRAINBASE_TOKEN=bbpat_…
|
|
172
|
+
brainbase agent push
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Do not bake a `~/.brainbase` directory into a build image: a leftover
|
|
176
|
+
`auth.json` outranks the stored PAT, and every control-plane command starts
|
|
177
|
+
failing once its session expires.
|
|
160
178
|
|
|
161
179
|
An explicit PAT does not inherit routing from a stored login. Set
|
|
162
180
|
`BRAINBASE_CONTROL_PLANE_URL` when using a PAT against a non-default MAS host.
|
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.23.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
|
+
}
|
|
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;
|
|
61751
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
|
}
|
|
@@ -80201,7 +80412,7 @@ function help() {
|
|
|
80201
80412
|
out.push("");
|
|
80202
80413
|
out.push(` ${import_picocolors52.default.cyan("login")} ${import_picocolors52.default.dim(" open the web app and connect this device")}`);
|
|
80203
80414
|
out.push(` ${import_picocolors52.default.cyan("logout")} ${import_picocolors52.default.dim(" clear the local session")}`);
|
|
80204
|
-
out.push(` ${import_picocolors52.default.cyan("whoami")}
|
|
80415
|
+
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
80416
|
out.push("");
|
|
80206
80417
|
out.push(divider("DISCOVERY"));
|
|
80207
80418
|
out.push("");
|
|
@@ -80304,7 +80515,7 @@ function help() {
|
|
|
80304
80515
|
out.push(` ${import_picocolors52.default.dim("BRAINBASE_API_URL")} legacy KLS host override (uses /api/cli; proxy/registry fallback)`);
|
|
80305
80516
|
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
80517
|
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 (
|
|
80518
|
+
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
80519
|
out.push(` ${import_picocolors52.default.dim("BRAINBASE_SKIP_AUTH")} bypass the auth gate for development`);
|
|
80309
80520
|
out.push(` ${import_picocolors52.default.dim("BRAINBASE_NON_INTERACTIVE")} force non-interactive mode — skip/auto-default prompts (CI & agents)`);
|
|
80310
80521
|
out.push(` ${import_picocolors52.default.dim("BRAINBASE_RUN_ENTRYPOINT")} =1 → agent pull executes the agent entrypoint (sandbox boots; or pass --run-entrypoint)`);
|
|
@@ -80556,7 +80767,7 @@ async function main() {
|
|
|
80556
80767
|
break;
|
|
80557
80768
|
}
|
|
80558
80769
|
case "whoami": {
|
|
80559
|
-
await runWhoami();
|
|
80770
|
+
await runWhoami({ json: jsonFlag });
|
|
80560
80771
|
break;
|
|
80561
80772
|
}
|
|
80562
80773
|
case "template": {
|