@brainbase-labs/cli 0.47.0 → 0.48.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 +12 -16
- package/dist/index.js +305 -137
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -336,7 +336,7 @@ Playbook files carry a small YAML frontmatter (`title`, `description`) — the C
|
|
|
336
336
|
| `BRAINBASE_API_URL` | Legacy KLS host override. Control requests use `/api/cli`; it is also the fallback for model-proxy and registry traffic. |
|
|
337
337
|
| `BRAINBASE_PROXY_URL` | Override the model-proxy host used when enabling harness tracking. |
|
|
338
338
|
| `BRAINBASE_REGISTRY_URL` | Override the registry API host. |
|
|
339
|
-
| `BRAINBASE_TOKEN` | Use a PAT instead of the stored login session.
|
|
339
|
+
| `BRAINBASE_TOKEN` | Use a PAT instead of the stored login session. Outranks both the session and the PAT saved in `token.json`. See [Auth](#auth). |
|
|
340
340
|
| `BRAINBASE_HOME` | Where local state lives (default: `~/.brainbase`) |
|
|
341
341
|
|
|
342
342
|
Model-proxy and registry traffic fall back through `BRAINBASE_API_URL`,
|
|
@@ -344,30 +344,26 @@ the server captured at login, and finally `https://api.v1.brainbaselabs.com`.
|
|
|
344
344
|
|
|
345
345
|
## Auth
|
|
346
346
|
|
|
347
|
-
|
|
348
|
-
it talks to:
|
|
347
|
+
Every command tries three credentials, in this order:
|
|
349
348
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
| `benchmark` control-plane commands | `BRAINBASE_TOKEN`, then the session |
|
|
349
|
+
1. `BRAINBASE_TOKEN`
|
|
350
|
+
2. the `auth.json` login session, renewed first if it has expired
|
|
351
|
+
3. the PAT that `brainbase token create` saved in `~/.brainbase/token.json`,
|
|
352
|
+
when there is no session or it could not be renewed. A PAT minted by a
|
|
353
|
+
different account than the session is never used in its place.
|
|
356
354
|
|
|
357
|
-
`brainbase whoami` (or `--json`) reports which one is active.
|
|
355
|
+
`brainbase whoami` (or `--json`) reports which one is active. It renews an
|
|
356
|
+
expired session itself, so what it reports is what the next command will use.
|
|
358
357
|
|
|
359
|
-
**For CI:**
|
|
360
|
-
and control-plane commands never read that file — `brainbase agent push` needs
|
|
361
|
-
the token in `BRAINBASE_TOKEN`:
|
|
358
|
+
**For CI:** a runner has no `token.json`, so hand it the PAT as a secret:
|
|
362
359
|
|
|
363
360
|
```bash
|
|
364
361
|
export BRAINBASE_TOKEN=bbpat_…
|
|
365
362
|
brainbase agent push
|
|
366
363
|
```
|
|
367
364
|
|
|
368
|
-
Do not bake a `~/.brainbase` directory into a build image
|
|
369
|
-
`
|
|
370
|
-
failing once its session expires.
|
|
365
|
+
Do not bake a `~/.brainbase` directory into a build image. Ship
|
|
366
|
+
`BRAINBASE_TOKEN` as a secret instead.
|
|
371
367
|
|
|
372
368
|
An explicit PAT does not inherit routing from a stored login. Set
|
|
373
369
|
`BRAINBASE_CONTROL_PLANE_URL` when using a PAT against a non-default MAS host.
|
package/dist/index.js
CHANGED
|
@@ -36091,7 +36091,7 @@ function padStart(s, n) {
|
|
|
36091
36091
|
// package.json
|
|
36092
36092
|
var package_default = {
|
|
36093
36093
|
name: "@brainbase-labs/cli",
|
|
36094
|
-
version: "0.
|
|
36094
|
+
version: "0.48.0",
|
|
36095
36095
|
description: "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
|
|
36096
36096
|
type: "module",
|
|
36097
36097
|
bin: {
|
|
@@ -40994,7 +40994,8 @@ var AuthSessionSchema = exports_external.object({
|
|
|
40994
40994
|
supabase_url: exports_external.string().optional(),
|
|
40995
40995
|
supabase_anon_key: exports_external.string().optional(),
|
|
40996
40996
|
web_url: exports_external.string().optional(),
|
|
40997
|
-
authedAt: exports_external.string()
|
|
40997
|
+
authedAt: exports_external.string(),
|
|
40998
|
+
refresh_rejected: exports_external.object({ at: exports_external.string(), code: exports_external.string() }).optional()
|
|
40998
40999
|
});
|
|
40999
41000
|
function readAuth() {
|
|
41000
41001
|
if (!exists(AUTH_FILE))
|
|
@@ -41141,6 +41142,47 @@ function authStatus() {
|
|
|
41141
41142
|
return { ok: false, session: s, reason: "session expired" };
|
|
41142
41143
|
return { ok: true, session: s };
|
|
41143
41144
|
}
|
|
41145
|
+
function isSessionRefreshable(session) {
|
|
41146
|
+
return Boolean(session.refresh_token && session.supabase_url && session.supabase_anon_key && !session.refresh_rejected);
|
|
41147
|
+
}
|
|
41148
|
+
var lastRefreshFailure = null;
|
|
41149
|
+
function refreshFailureFor(session) {
|
|
41150
|
+
if (session.refresh_rejected) {
|
|
41151
|
+
return { kind: "rejected", detail: session.refresh_rejected.code };
|
|
41152
|
+
}
|
|
41153
|
+
if (lastRefreshFailure && lastRefreshFailure.refreshToken === session.refresh_token) {
|
|
41154
|
+
return lastRefreshFailure.failure;
|
|
41155
|
+
}
|
|
41156
|
+
return null;
|
|
41157
|
+
}
|
|
41158
|
+
function noteRefreshFailure(session, failure) {
|
|
41159
|
+
lastRefreshFailure = { refreshToken: session.refresh_token ?? "", failure };
|
|
41160
|
+
}
|
|
41161
|
+
async function rejectionCode(res) {
|
|
41162
|
+
if (![400, 401, 403].includes(res.status))
|
|
41163
|
+
return null;
|
|
41164
|
+
try {
|
|
41165
|
+
const body = await res.json();
|
|
41166
|
+
for (const key of ["error_code", "error"]) {
|
|
41167
|
+
const value = body[key];
|
|
41168
|
+
if (typeof value === "string" && value.trim())
|
|
41169
|
+
return value.trim();
|
|
41170
|
+
}
|
|
41171
|
+
} catch {}
|
|
41172
|
+
return null;
|
|
41173
|
+
}
|
|
41174
|
+
function markRefreshRejected(session, code) {
|
|
41175
|
+
withAuthLock(() => {
|
|
41176
|
+
const current = readAuth();
|
|
41177
|
+
if (!current || !isSameAuthIdentity(current, session) || current.access_token !== session.access_token || current.refresh_token !== session.refresh_token) {
|
|
41178
|
+
return;
|
|
41179
|
+
}
|
|
41180
|
+
writeAuthUnlocked({
|
|
41181
|
+
...current,
|
|
41182
|
+
refresh_rejected: { at: new Date().toISOString(), code }
|
|
41183
|
+
});
|
|
41184
|
+
});
|
|
41185
|
+
}
|
|
41144
41186
|
var refreshInFlight = null;
|
|
41145
41187
|
function validatedRefreshResult(expectedSession, result) {
|
|
41146
41188
|
const current = readAuth();
|
|
@@ -41162,10 +41204,11 @@ function adoptRefreshedLineage(session) {
|
|
|
41162
41204
|
return isAuthValid(current) ? current : null;
|
|
41163
41205
|
}
|
|
41164
41206
|
async function refreshSession(session) {
|
|
41165
|
-
if (!session
|
|
41207
|
+
if (!isSessionRefreshable(session))
|
|
41166
41208
|
return null;
|
|
41167
|
-
if (!session.supabase_url || !session.supabase_anon_key)
|
|
41209
|
+
if (!session.refresh_token || !session.supabase_url || !session.supabase_anon_key) {
|
|
41168
41210
|
return null;
|
|
41211
|
+
}
|
|
41169
41212
|
const releaseRefreshLock = acquireLock(REFRESH_LOCK_FILE, REFRESH_LOCK_TIMEOUT_MS);
|
|
41170
41213
|
if (!releaseRefreshLock) {
|
|
41171
41214
|
const adopted = adoptRefreshedLineage(session);
|
|
@@ -41173,12 +41216,24 @@ async function refreshSession(session) {
|
|
|
41173
41216
|
return adopted;
|
|
41174
41217
|
if (isAuthValid(session))
|
|
41175
41218
|
return session;
|
|
41219
|
+
noteRefreshFailure(session, {
|
|
41220
|
+
kind: "unavailable",
|
|
41221
|
+
detail: "timed out waiting for another command to renew it"
|
|
41222
|
+
});
|
|
41176
41223
|
throw new AuthRefreshLockTimeoutError;
|
|
41177
41224
|
}
|
|
41178
41225
|
try {
|
|
41179
41226
|
const adopted = adoptRefreshedLineage(session);
|
|
41180
41227
|
if (adopted !== undefined)
|
|
41181
41228
|
return adopted;
|
|
41229
|
+
const onDisk = readAuth();
|
|
41230
|
+
if (onDisk?.refresh_rejected) {
|
|
41231
|
+
noteRefreshFailure(session, {
|
|
41232
|
+
kind: "rejected",
|
|
41233
|
+
detail: onDisk.refresh_rejected.code
|
|
41234
|
+
});
|
|
41235
|
+
return null;
|
|
41236
|
+
}
|
|
41182
41237
|
const url = `${session.supabase_url.replace(/\/+$/, "")}/auth/v1/token?grant_type=refresh_token`;
|
|
41183
41238
|
let res;
|
|
41184
41239
|
try {
|
|
@@ -41192,19 +41247,43 @@ async function refreshSession(session) {
|
|
|
41192
41247
|
},
|
|
41193
41248
|
body: JSON.stringify({ refresh_token: session.refresh_token })
|
|
41194
41249
|
});
|
|
41195
|
-
} catch {
|
|
41250
|
+
} catch (error) {
|
|
41251
|
+
noteRefreshFailure(session, {
|
|
41252
|
+
kind: "unavailable",
|
|
41253
|
+
detail: `could not reach ${new URL(url).host}: ${error.message}`
|
|
41254
|
+
});
|
|
41196
41255
|
return null;
|
|
41197
41256
|
}
|
|
41198
|
-
if (!res.ok)
|
|
41257
|
+
if (!res.ok) {
|
|
41258
|
+
const code = await rejectionCode(res);
|
|
41259
|
+
if (code) {
|
|
41260
|
+
markRefreshRejected(session, code);
|
|
41261
|
+
noteRefreshFailure(session, { kind: "rejected", detail: code });
|
|
41262
|
+
} else {
|
|
41263
|
+
noteRefreshFailure(session, {
|
|
41264
|
+
kind: "unavailable",
|
|
41265
|
+
detail: `the auth server answered HTTP ${res.status}`
|
|
41266
|
+
});
|
|
41267
|
+
}
|
|
41199
41268
|
return null;
|
|
41269
|
+
}
|
|
41200
41270
|
let data;
|
|
41201
41271
|
try {
|
|
41202
41272
|
data = await res.json();
|
|
41203
41273
|
} catch {
|
|
41274
|
+
noteRefreshFailure(session, {
|
|
41275
|
+
kind: "unavailable",
|
|
41276
|
+
detail: "the auth server sent an unreadable response"
|
|
41277
|
+
});
|
|
41204
41278
|
return null;
|
|
41205
41279
|
}
|
|
41206
|
-
if (!data.access_token)
|
|
41280
|
+
if (!data.access_token) {
|
|
41281
|
+
noteRefreshFailure(session, {
|
|
41282
|
+
kind: "unavailable",
|
|
41283
|
+
detail: "the auth server sent no access token"
|
|
41284
|
+
});
|
|
41207
41285
|
return null;
|
|
41286
|
+
}
|
|
41208
41287
|
const now = Math.floor(Date.now() / 1000);
|
|
41209
41288
|
const expires_at = data.expires_at ?? (data.expires_in ? now + data.expires_in : undefined);
|
|
41210
41289
|
const updated = {
|
|
@@ -41224,6 +41303,7 @@ async function refreshSession(session) {
|
|
|
41224
41303
|
if (adopted2 !== undefined)
|
|
41225
41304
|
return adopted2;
|
|
41226
41305
|
writeAuthUnlocked(updated);
|
|
41306
|
+
lastRefreshFailure = null;
|
|
41227
41307
|
return updated;
|
|
41228
41308
|
});
|
|
41229
41309
|
} finally {
|
|
@@ -41263,7 +41343,7 @@ async function refreshSessionAfterUnauthorized(rejectedSession) {
|
|
|
41263
41343
|
return current;
|
|
41264
41344
|
}
|
|
41265
41345
|
const refreshed = await tryRefreshSession(rejectedSession);
|
|
41266
|
-
if (refreshed) {
|
|
41346
|
+
if (refreshed && refreshed.access_token !== rejectedSession.access_token) {
|
|
41267
41347
|
if (!isSameAuthIdentity(refreshed, rejectedSession)) {
|
|
41268
41348
|
throw new AuthSessionChangedError;
|
|
41269
41349
|
}
|
|
@@ -41276,8 +41356,21 @@ async function refreshSessionAfterUnauthorized(rejectedSession) {
|
|
|
41276
41356
|
if (latest.access_token !== rejectedSession.access_token && isAuthValid(latest)) {
|
|
41277
41357
|
return latest;
|
|
41278
41358
|
}
|
|
41359
|
+
expireRejectedAccessToken(rejectedSession);
|
|
41279
41360
|
return null;
|
|
41280
41361
|
}
|
|
41362
|
+
function expireRejectedAccessToken(session) {
|
|
41363
|
+
withAuthLock(() => {
|
|
41364
|
+
const current = readAuth();
|
|
41365
|
+
if (!current || !isSameAuthIdentity(current, session) || current.access_token !== session.access_token || !isAuthValid(current)) {
|
|
41366
|
+
return;
|
|
41367
|
+
}
|
|
41368
|
+
writeAuthUnlocked({
|
|
41369
|
+
...current,
|
|
41370
|
+
expires_at: Math.floor(Date.now() / 1000) - 1
|
|
41371
|
+
});
|
|
41372
|
+
});
|
|
41373
|
+
}
|
|
41281
41374
|
async function ensureFreshSession() {
|
|
41282
41375
|
const s = readAuth();
|
|
41283
41376
|
if (!s)
|
|
@@ -54875,7 +54968,8 @@ var StoredTokenSchema = exports_external.object({
|
|
|
54875
54968
|
token: exports_external.string(),
|
|
54876
54969
|
prefix: exports_external.string(),
|
|
54877
54970
|
name: exports_external.string().optional(),
|
|
54878
|
-
createdAt: exports_external.string()
|
|
54971
|
+
createdAt: exports_external.string(),
|
|
54972
|
+
user_id: exports_external.string().optional()
|
|
54879
54973
|
});
|
|
54880
54974
|
function withTokenLock(operation) {
|
|
54881
54975
|
const release = acquireLock(TOKEN_LOCK_FILE, TOKEN_LOCK_TIMEOUT_MS);
|
|
@@ -54897,7 +54991,7 @@ function readToken() {
|
|
|
54897
54991
|
return null;
|
|
54898
54992
|
}
|
|
54899
54993
|
}
|
|
54900
|
-
function writeToken(token, name) {
|
|
54994
|
+
function writeToken(token, name, userId) {
|
|
54901
54995
|
if (!isValidTokenFormat(token)) {
|
|
54902
54996
|
throw new Error("refusing to store token: invalid format");
|
|
54903
54997
|
}
|
|
@@ -54906,7 +55000,8 @@ function writeToken(token, name) {
|
|
|
54906
55000
|
token,
|
|
54907
55001
|
prefix: tokenPrefix(token),
|
|
54908
55002
|
name,
|
|
54909
|
-
createdAt: new Date().toISOString()
|
|
55003
|
+
createdAt: new Date().toISOString(),
|
|
55004
|
+
...userId ? { user_id: userId } : {}
|
|
54910
55005
|
};
|
|
54911
55006
|
withTokenLock(() => writeJsonAtomic(TOKEN_FILE, stored));
|
|
54912
55007
|
return stored;
|
|
@@ -54940,8 +55035,6 @@ function clearTokenIfMatches(prefix) {
|
|
|
54940
55035
|
}
|
|
54941
55036
|
|
|
54942
55037
|
// src/core/credentials.ts
|
|
54943
|
-
var REGISTRY_COMMANDS = "template, skill, token";
|
|
54944
|
-
var CONTROL_PLANE_COMMANDS = "agent, orchestration, link, unlink, sync, status, team";
|
|
54945
55038
|
var NOT_A_PAT = "(not a bbpat_… token)";
|
|
54946
55039
|
function maskPat(token) {
|
|
54947
55040
|
if (!isValidTokenFormat(token))
|
|
@@ -54954,58 +55047,89 @@ function readEnvPat() {
|
|
|
54954
55047
|
return null;
|
|
54955
55048
|
return { prefix: maskPat(value), malformed: !isValidTokenFormat(value) };
|
|
54956
55049
|
}
|
|
54957
|
-
function
|
|
54958
|
-
const
|
|
54959
|
-
|
|
54960
|
-
|
|
54961
|
-
|
|
54962
|
-
|
|
54963
|
-
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.";
|
|
54964
|
-
default: {
|
|
54965
|
-
const exhaustive = client;
|
|
54966
|
-
throw new Error(`unhandled credential client: ${String(exhaustive)}`);
|
|
54967
|
-
}
|
|
55050
|
+
function storedPatFor(session) {
|
|
55051
|
+
const stored = readToken();
|
|
55052
|
+
if (!stored)
|
|
55053
|
+
return null;
|
|
55054
|
+
if (session && stored.user_id && stored.user_id !== session.user_id) {
|
|
55055
|
+
return null;
|
|
54968
55056
|
}
|
|
55057
|
+
return stored;
|
|
54969
55058
|
}
|
|
54970
|
-
|
|
54971
|
-
|
|
54972
|
-
|
|
54973
|
-
|
|
54974
|
-
|
|
54975
|
-
|
|
55059
|
+
var announcedFallback = false;
|
|
55060
|
+
function announceStoredPatFallback(session, stored) {
|
|
55061
|
+
if (!session || announcedFallback)
|
|
55062
|
+
return;
|
|
55063
|
+
announcedFallback = true;
|
|
55064
|
+
console.error(`Login session ${sessionProblem(session)}; using the PAT saved in ${TOKEN_FILE} ` + `(${stored.prefix}…). Run \`brainbase login\` to switch back.`);
|
|
55065
|
+
}
|
|
55066
|
+
function sessionProblem(session) {
|
|
55067
|
+
const failure = refreshFailureFor(session);
|
|
55068
|
+
if (failure?.kind === "rejected") {
|
|
55069
|
+
return `expired and could not be renewed (the auth server rejected its refresh token: ${failure.detail})`;
|
|
55070
|
+
}
|
|
55071
|
+
if (failure?.kind === "unavailable") {
|
|
55072
|
+
return `expired and could not be renewed (${failure.detail})`;
|
|
55073
|
+
}
|
|
55074
|
+
if (!isSessionRefreshable(session)) {
|
|
55075
|
+
return "expired and cannot be renewed (no refresh token was saved with it)";
|
|
55076
|
+
}
|
|
55077
|
+
return "expired";
|
|
55078
|
+
}
|
|
55079
|
+
function noUsableCredentialMessage() {
|
|
55080
|
+
const session = readAuth();
|
|
55081
|
+
if (!session) {
|
|
55082
|
+
return "Not logged in. Run `brainbase login` to connect this device.";
|
|
55083
|
+
}
|
|
55084
|
+
const failure = refreshFailureFor(session);
|
|
55085
|
+
const retry = failure?.kind === "unavailable" ? " Check your connection and retry, or run `brainbase login`." : " Run `brainbase login` to sign in again.";
|
|
55086
|
+
const stored = readToken();
|
|
55087
|
+
const mismatch = stored && !storedPatFor(session) ? ` The PAT saved in ${TOKEN_FILE} belongs to a different account, so it was not used.` : "";
|
|
55088
|
+
return `Your login session ${sessionProblem(session)}.${retry}${mismatch}`;
|
|
54976
55089
|
}
|
|
54977
|
-
function withRejectedSessionHint(message, source
|
|
55090
|
+
function withRejectedSessionHint(message, source) {
|
|
54978
55091
|
if (source !== "session")
|
|
54979
55092
|
return message;
|
|
54980
|
-
|
|
54981
|
-
|
|
54982
|
-
|
|
54983
|
-
|
|
55093
|
+
const trimmed = message.trimEnd();
|
|
55094
|
+
const separator = /[.!?:]$/.test(trimmed) ? " " : ". ";
|
|
55095
|
+
const session = readAuth();
|
|
55096
|
+
const renewable = session ? isSessionRefreshable(session) : false;
|
|
55097
|
+
if (session && !renewable && storedPatFor(session)) {
|
|
55098
|
+
return `${trimmed}${separator}The login session could not be renewed; ` + `retry to use the PAT saved in ${TOKEN_FILE}, or run \`brainbase login\`.`;
|
|
55099
|
+
}
|
|
55100
|
+
return `${trimmed}${separator}The login session could not be renewed. Run \`brainbase login\` to sign in again.`;
|
|
54984
55101
|
}
|
|
54985
55102
|
function describeCredentials() {
|
|
54986
55103
|
const env3 = readEnvPat();
|
|
54987
55104
|
const status = authStatus();
|
|
54988
|
-
const
|
|
54989
|
-
const
|
|
55105
|
+
const storedToken = readToken();
|
|
55106
|
+
const storedUsable = storedToken ? storedPatFor(status.session) : null;
|
|
55107
|
+
const refreshable = Boolean(status.session && !status.ok && isSessionRefreshable(status.session));
|
|
55108
|
+
const failure = status.session ? refreshFailureFor(status.session) : null;
|
|
54990
55109
|
const session = status.session ? {
|
|
54991
55110
|
email: status.session.email ?? null,
|
|
54992
55111
|
userId: status.session.user_id,
|
|
54993
55112
|
expiresAt: status.session.expires_at ?? null,
|
|
54994
55113
|
expired: !status.ok,
|
|
54995
|
-
refreshable
|
|
55114
|
+
refreshable,
|
|
55115
|
+
refreshError: failure ? failure.kind === "rejected" ? `rejected: ${failure.detail}` : failure.detail : null
|
|
54996
55116
|
} : null;
|
|
54997
55117
|
let active = null;
|
|
54998
55118
|
if (env3)
|
|
54999
55119
|
active = "env_pat";
|
|
55000
|
-
else if (status.ok || refreshable)
|
|
55120
|
+
else if (status.ok || refreshable && !failure)
|
|
55001
55121
|
active = "session";
|
|
55002
|
-
else if (
|
|
55122
|
+
else if (storedUsable)
|
|
55003
55123
|
active = "stored_pat";
|
|
55004
55124
|
return {
|
|
55005
55125
|
active,
|
|
55006
55126
|
envPat: env3,
|
|
55007
55127
|
session,
|
|
55008
|
-
storedPat:
|
|
55128
|
+
storedPat: storedToken ? {
|
|
55129
|
+
name: storedToken.name ?? null,
|
|
55130
|
+
prefix: storedToken.prefix,
|
|
55131
|
+
usable: Boolean(storedUsable)
|
|
55132
|
+
} : null
|
|
55009
55133
|
};
|
|
55010
55134
|
}
|
|
55011
55135
|
|
|
@@ -55092,7 +55216,7 @@ function legacyScheduleError() {
|
|
|
55092
55216
|
function legacyAgentConfigError() {
|
|
55093
55217
|
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);
|
|
55094
55218
|
}
|
|
55095
|
-
async function resolveCredential(
|
|
55219
|
+
async function resolveCredential() {
|
|
55096
55220
|
const envToken = process.env.BRAINBASE_TOKEN;
|
|
55097
55221
|
if (envToken && envToken.trim()) {
|
|
55098
55222
|
return {
|
|
@@ -55101,43 +55225,39 @@ async function resolveCredential(storedPatHint = "control-plane") {
|
|
|
55101
55225
|
source: "env_pat"
|
|
55102
55226
|
};
|
|
55103
55227
|
}
|
|
55104
|
-
const
|
|
55105
|
-
|
|
55106
|
-
return {
|
|
55107
|
-
bearer: session.access_token,
|
|
55108
|
-
session,
|
|
55109
|
-
source: "session"
|
|
55110
|
-
};
|
|
55111
|
-
}
|
|
55112
|
-
const status = authStatus();
|
|
55113
|
-
const reason = status.reason ?? "not logged in";
|
|
55114
|
-
throw new ApiError(status.ok ? "CLI authentication changed while this command was starting; retry it" : storedPatHint ? withStoredPatHint(reason, storedPatHint) : reason, 401);
|
|
55115
|
-
}
|
|
55116
|
-
async function resolveMasCredential() {
|
|
55117
|
-
const configuredSession = readAuth();
|
|
55228
|
+
const configured = readAuth();
|
|
55229
|
+
let session;
|
|
55118
55230
|
try {
|
|
55119
|
-
|
|
55231
|
+
session = await ensureFreshSession();
|
|
55120
55232
|
} catch (error) {
|
|
55121
|
-
if (!(error instanceof
|
|
55233
|
+
if (!(error instanceof AuthRefreshLockTimeoutError))
|
|
55122
55234
|
throw error;
|
|
55123
|
-
|
|
55124
|
-
|
|
55125
|
-
|
|
55126
|
-
throw new ApiError(`${error.message}. ${storedPatIgnoredHint("managed-task")}`, 401);
|
|
55127
|
-
}
|
|
55235
|
+
const current2 = readAuth();
|
|
55236
|
+
const stored2 = storedPatFor(current2);
|
|
55237
|
+
if (!stored2 || (configured?.user_id ?? null) !== (current2?.user_id ?? null)) {
|
|
55128
55238
|
throw error;
|
|
55129
55239
|
}
|
|
55240
|
+
announceStoredPatFallback(current2, stored2);
|
|
55241
|
+
return { bearer: stored2.token, session: null, source: "stored_pat" };
|
|
55130
55242
|
}
|
|
55131
|
-
|
|
55132
|
-
if (storedToken) {
|
|
55243
|
+
if (session) {
|
|
55133
55244
|
return {
|
|
55134
|
-
bearer:
|
|
55135
|
-
session
|
|
55136
|
-
source: "
|
|
55245
|
+
bearer: session.access_token,
|
|
55246
|
+
session,
|
|
55247
|
+
source: "session"
|
|
55137
55248
|
};
|
|
55138
55249
|
}
|
|
55139
55250
|
const status = authStatus();
|
|
55140
|
-
|
|
55251
|
+
const current = readAuth();
|
|
55252
|
+
if (status.ok || (configured?.user_id ?? null) !== (current?.user_id ?? null)) {
|
|
55253
|
+
throw new ApiError("CLI authentication changed while this command was starting; retry it", 401);
|
|
55254
|
+
}
|
|
55255
|
+
const stored = storedPatFor(current);
|
|
55256
|
+
if (stored) {
|
|
55257
|
+
announceStoredPatFallback(current, stored);
|
|
55258
|
+
return { bearer: stored.token, session: null, source: "stored_pat" };
|
|
55259
|
+
}
|
|
55260
|
+
throw new ApiError(noUsableCredentialMessage(), 401);
|
|
55141
55261
|
}
|
|
55142
55262
|
function networkErrorCode(error) {
|
|
55143
55263
|
if (!error || typeof error !== "object")
|
|
@@ -55225,7 +55345,7 @@ async function request(pathname, init = {}) {
|
|
|
55225
55345
|
} catch {}
|
|
55226
55346
|
if (!res.ok) {
|
|
55227
55347
|
const message = apiErrorMessage(body, res.status);
|
|
55228
|
-
throw new ApiError(res.status === 401 ? withRejectedSessionHint(message, credential.source
|
|
55348
|
+
throw new ApiError(res.status === 401 ? withRejectedSessionHint(message, credential.source) : message, res.status, body);
|
|
55229
55349
|
}
|
|
55230
55350
|
return body;
|
|
55231
55351
|
}
|
|
@@ -55278,7 +55398,7 @@ function parseMasTaskCreateResponse(body) {
|
|
|
55278
55398
|
};
|
|
55279
55399
|
}
|
|
55280
55400
|
async function masRequest(pathname, init) {
|
|
55281
|
-
const credential = await
|
|
55401
|
+
const credential = await resolveCredential();
|
|
55282
55402
|
let currentSession = credential.session;
|
|
55283
55403
|
let ambiguousNetworkRetriesRemaining = 1;
|
|
55284
55404
|
let requestMayHaveSucceeded = false;
|
|
@@ -55369,7 +55489,7 @@ async function masRequest(pathname, init) {
|
|
|
55369
55489
|
}
|
|
55370
55490
|
if (!res.ok) {
|
|
55371
55491
|
const message = masApiErrorMessage(body, res.status);
|
|
55372
|
-
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
|
|
55492
|
+
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) : message, res.status, body);
|
|
55373
55493
|
}
|
|
55374
55494
|
return body;
|
|
55375
55495
|
}
|
|
@@ -55383,7 +55503,7 @@ function masNamedRecord(body, what, nameField) {
|
|
|
55383
55503
|
return { id: record.id, name };
|
|
55384
55504
|
}
|
|
55385
55505
|
async function masReadRequest(pathname, callerSignal) {
|
|
55386
|
-
const credential = await
|
|
55506
|
+
const credential = await resolveCredential();
|
|
55387
55507
|
let currentSession = credential.session;
|
|
55388
55508
|
let refreshSessionAvailable = credential.source === "session";
|
|
55389
55509
|
let res;
|
|
@@ -55421,7 +55541,7 @@ async function masReadRequest(pathname, callerSignal) {
|
|
|
55421
55541
|
} catch {}
|
|
55422
55542
|
if (!res.ok) {
|
|
55423
55543
|
const message = masApiErrorMessage(body, res.status);
|
|
55424
|
-
throw new ApiError(res.status === 401 ? withRejectedSessionHint(message, credential.source
|
|
55544
|
+
throw new ApiError(res.status === 401 ? withRejectedSessionHint(message, credential.source) : res.status === 404 && isUnroutedPath(body) ? `This control plane does not serve ${pathname} yet. Update the server, or use the web app.` : message, res.status, body);
|
|
55425
55545
|
}
|
|
55426
55546
|
return body;
|
|
55427
55547
|
}
|
|
@@ -55440,7 +55560,7 @@ function withRequestDeadline(callerSignal, timeoutMs) {
|
|
|
55440
55560
|
return combined.signal;
|
|
55441
55561
|
}
|
|
55442
55562
|
async function masResourceRequest(pathname, init = {}, onHeaders) {
|
|
55443
|
-
const credential = await
|
|
55563
|
+
const credential = await resolveCredential();
|
|
55444
55564
|
let currentSession = credential.session;
|
|
55445
55565
|
const res = await sendWithAuthRetry(credential.source === "session" ? currentSession : null, async (refreshed) => {
|
|
55446
55566
|
if (refreshed)
|
|
@@ -55462,7 +55582,7 @@ async function masResourceRequest(pathname, init = {}, onHeaders) {
|
|
|
55462
55582
|
} catch {}
|
|
55463
55583
|
if (!res.ok) {
|
|
55464
55584
|
const message = masApiErrorMessage(body, res.status);
|
|
55465
|
-
const failure = new ApiError(res.status === 401 ? withRejectedSessionHint(message, credential.source
|
|
55585
|
+
const failure = new ApiError(res.status === 401 ? withRejectedSessionHint(message, credential.source) : res.status === 404 && isUnroutedPath(body) ? `This control plane does not serve ${pathname} yet. Update the server, or use the web app.` : message, res.status, body);
|
|
55466
55586
|
const retryAfter = res.headers.get("Retry-After");
|
|
55467
55587
|
if (retryAfter)
|
|
55468
55588
|
failure.retryAfter = retryAfter;
|
|
@@ -55475,12 +55595,12 @@ async function masResourceRequest(pathname, init = {}, onHeaders) {
|
|
|
55475
55595
|
class StreamHostMovedError extends ApiError {
|
|
55476
55596
|
}
|
|
55477
55597
|
async function masStreamTarget(pathname) {
|
|
55478
|
-
const credential = await
|
|
55598
|
+
const credential = await resolveCredential();
|
|
55479
55599
|
const base2 = masApiBase(credential.session);
|
|
55480
55600
|
return {
|
|
55481
55601
|
url: `${base2}${pathname}`,
|
|
55482
55602
|
resolveBearer: async () => {
|
|
55483
|
-
const fresh = await
|
|
55603
|
+
const fresh = await resolveCredential();
|
|
55484
55604
|
const freshBase = masApiBase(fresh.session);
|
|
55485
55605
|
if (freshBase !== base2) {
|
|
55486
55606
|
throw new StreamHostMovedError(`The control plane moved from ${base2} to ${freshBase} while this stream was open, so the current credential was not sent. Re-run the command to follow against the new host.`);
|
|
@@ -55934,18 +56054,26 @@ async function resolveAuth() {
|
|
|
55934
56054
|
} catch (error) {
|
|
55935
56055
|
if (!(error instanceof AuthRefreshLockTimeoutError))
|
|
55936
56056
|
throw error;
|
|
55937
|
-
const
|
|
55938
|
-
|
|
56057
|
+
const session = readAuth();
|
|
56058
|
+
const pat2 = storedPatFor(session);
|
|
56059
|
+
if (pat2) {
|
|
56060
|
+
announceStoredPatFallback(session, pat2);
|
|
55939
56061
|
return patAuth(pat2.token);
|
|
56062
|
+
}
|
|
55940
56063
|
throw error;
|
|
55941
56064
|
}
|
|
55942
56065
|
if (freshSession)
|
|
55943
56066
|
return jwtAuth(freshSession);
|
|
55944
56067
|
const status = authStatus();
|
|
55945
|
-
|
|
55946
|
-
|
|
56068
|
+
if (status.ok) {
|
|
56069
|
+
throw new ApiError("CLI authentication changed while this command was starting; retry it", 401);
|
|
56070
|
+
}
|
|
56071
|
+
const pat = storedPatFor(status.session);
|
|
56072
|
+
if (pat) {
|
|
56073
|
+
announceStoredPatFallback(status.session, pat);
|
|
55947
56074
|
return patAuth(pat.token);
|
|
55948
|
-
|
|
56075
|
+
}
|
|
56076
|
+
throw new ApiError(noUsableCredentialMessage(), 401);
|
|
55949
56077
|
}
|
|
55950
56078
|
function mergeHeaders(auth, overrides) {
|
|
55951
56079
|
const headers = new Headers(auth);
|
|
@@ -63396,13 +63524,62 @@ async function runLogout() {
|
|
|
63396
63524
|
}
|
|
63397
63525
|
|
|
63398
63526
|
// src/cli/whoami.ts
|
|
63399
|
-
|
|
63527
|
+
var PAT_LOOKUP_TIMEOUT_MS = 5000;
|
|
63528
|
+
async function settleSession() {
|
|
63529
|
+
if (process.env.BRAINBASE_TOKEN?.trim())
|
|
63530
|
+
return;
|
|
63531
|
+
if (!readAuth())
|
|
63532
|
+
return;
|
|
63533
|
+
try {
|
|
63534
|
+
await ensureFreshSession();
|
|
63535
|
+
} catch {}
|
|
63536
|
+
}
|
|
63537
|
+
function activePat(report) {
|
|
63538
|
+
if (report.active === "env_pat") {
|
|
63539
|
+
return report.envPat?.malformed ? null : process.env.BRAINBASE_TOKEN?.trim() ?? null;
|
|
63540
|
+
}
|
|
63541
|
+
if (report.active === "stored_pat")
|
|
63542
|
+
return readToken()?.token ?? null;
|
|
63543
|
+
return null;
|
|
63544
|
+
}
|
|
63545
|
+
async function lookupPat(token) {
|
|
63546
|
+
let res;
|
|
63547
|
+
try {
|
|
63548
|
+
res = await fetch(`${baseUrl(null)}/v1/registry/me`, {
|
|
63549
|
+
headers: {
|
|
63550
|
+
Authorization: `Bearer ${token}`,
|
|
63551
|
+
"X-Auth-Source": "pat",
|
|
63552
|
+
Accept: "application/json"
|
|
63553
|
+
},
|
|
63554
|
+
signal: AbortSignal.timeout(PAT_LOOKUP_TIMEOUT_MS)
|
|
63555
|
+
});
|
|
63556
|
+
} catch {
|
|
63557
|
+
return { kind: "unknown" };
|
|
63558
|
+
}
|
|
63559
|
+
let body = null;
|
|
63560
|
+
try {
|
|
63561
|
+
body = await res.json();
|
|
63562
|
+
} catch {}
|
|
63563
|
+
const detail = body && typeof body === "object" && typeof body.detail === "string" ? body.detail : null;
|
|
63564
|
+
if (res.status === 401 && detail)
|
|
63565
|
+
return { kind: "rejected", message: detail };
|
|
63566
|
+
if (!res.ok || !body || typeof body !== "object")
|
|
63567
|
+
return { kind: "unknown" };
|
|
63568
|
+
const obj = body;
|
|
63569
|
+
return {
|
|
63570
|
+
kind: "ok",
|
|
63571
|
+
userId: typeof obj.id === "string" ? obj.id : null,
|
|
63572
|
+
email: typeof obj.email === "string" ? obj.email : null
|
|
63573
|
+
};
|
|
63574
|
+
}
|
|
63575
|
+
function usableForControlPlane(report, pat) {
|
|
63400
63576
|
switch (report.active) {
|
|
63401
63577
|
case "env_pat":
|
|
63402
|
-
return !report.envPat?.malformed;
|
|
63578
|
+
return !report.envPat?.malformed && pat.kind !== "rejected";
|
|
63579
|
+
case "stored_pat":
|
|
63580
|
+
return pat.kind !== "rejected";
|
|
63403
63581
|
case "session":
|
|
63404
63582
|
return true;
|
|
63405
|
-
case "stored_pat":
|
|
63406
63583
|
case null:
|
|
63407
63584
|
return false;
|
|
63408
63585
|
default: {
|
|
@@ -63411,41 +63588,44 @@ function usableForControlPlane(report) {
|
|
|
63411
63588
|
}
|
|
63412
63589
|
}
|
|
63413
63590
|
}
|
|
63414
|
-
function
|
|
63415
|
-
|
|
63416
|
-
|
|
63417
|
-
|
|
63591
|
+
function reasonFor(report, pat) {
|
|
63592
|
+
if (pat.kind === "rejected") {
|
|
63593
|
+
const which = report.active === "env_pat" ? "the PAT in BRAINBASE_TOKEN" : "the PAT saved in token.json";
|
|
63594
|
+
return `The server rejected ${which} (${pat.message}). ` + "Mint a new one with `brainbase token create`, or run `brainbase login`.";
|
|
63595
|
+
}
|
|
63418
63596
|
switch (report.active) {
|
|
63419
63597
|
case "env_pat":
|
|
63420
63598
|
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;
|
|
63421
63599
|
case "session":
|
|
63422
63600
|
return null;
|
|
63423
63601
|
case "stored_pat":
|
|
63424
|
-
return
|
|
63602
|
+
return report.session ? noUsableCredentialMessage() : null;
|
|
63425
63603
|
case null:
|
|
63426
|
-
return
|
|
63604
|
+
return noUsableCredentialMessage();
|
|
63427
63605
|
default: {
|
|
63428
63606
|
const exhaustive = report.active;
|
|
63429
63607
|
throw new Error(`unhandled credential: ${String(exhaustive)}`);
|
|
63430
63608
|
}
|
|
63431
63609
|
}
|
|
63432
63610
|
}
|
|
63433
|
-
function toJson(report, reason) {
|
|
63611
|
+
function toJson(report, pat, reason) {
|
|
63434
63612
|
const usingSession = report.active === "session";
|
|
63613
|
+
const patIdentity = pat.kind === "ok" ? pat : null;
|
|
63435
63614
|
return {
|
|
63436
63615
|
credential: report.active,
|
|
63437
|
-
authenticated: report.active !== null && !report.envPat?.malformed,
|
|
63438
|
-
control_plane: usableForControlPlane(report),
|
|
63439
|
-
email: usingSession ? report.session?.email ?? null : null,
|
|
63440
|
-
user_id: usingSession ? report.session?.userId ?? null : null,
|
|
63616
|
+
authenticated: report.active !== null && !report.envPat?.malformed && pat.kind !== "rejected",
|
|
63617
|
+
control_plane: usableForControlPlane(report, pat),
|
|
63618
|
+
email: usingSession ? report.session?.email ?? null : patIdentity?.email ?? null,
|
|
63619
|
+
user_id: usingSession ? report.session?.userId ?? null : patIdentity?.userId ?? null,
|
|
63441
63620
|
expires_at: usingSession ? report.session?.expiresAt ?? null : null,
|
|
63442
63621
|
token_prefix: report.active === "env_pat" ? report.envPat?.prefix ?? null : report.active === "stored_pat" ? report.storedPat?.prefix ?? null : null,
|
|
63443
63622
|
control_plane_url: controlPlaneBaseUrl(usingSession ? readAuth() : null),
|
|
63444
63623
|
session: report.session ? {
|
|
63445
63624
|
present: true,
|
|
63446
63625
|
expired: report.session.expired,
|
|
63447
|
-
refreshable: report.session.refreshable
|
|
63448
|
-
|
|
63626
|
+
refreshable: report.session.refreshable,
|
|
63627
|
+
refresh_error: report.session.refreshError
|
|
63628
|
+
} : { present: false, expired: false, refreshable: false, refresh_error: null },
|
|
63449
63629
|
stored_pat: report.storedPat ? { present: true, name: report.storedPat.name } : { present: false, name: null },
|
|
63450
63630
|
reason
|
|
63451
63631
|
};
|
|
@@ -63463,13 +63643,10 @@ function cardRows(report) {
|
|
|
63463
63643
|
}
|
|
63464
63644
|
break;
|
|
63465
63645
|
case "session":
|
|
63466
|
-
rows.push([
|
|
63467
|
-
"credential",
|
|
63468
|
-
report.session?.refreshable ? "login session (auth.json) — expired, renews on next use" : "login session (auth.json)"
|
|
63469
|
-
]);
|
|
63646
|
+
rows.push(["credential", "login session (auth.json)"]);
|
|
63470
63647
|
break;
|
|
63471
63648
|
case "stored_pat":
|
|
63472
|
-
rows.push(["credential", "
|
|
63649
|
+
rows.push(["credential", "saved PAT (token.json)"]);
|
|
63473
63650
|
rows.push([
|
|
63474
63651
|
"token",
|
|
63475
63652
|
report.storedPat?.name ? `${displayPrefix(report.storedPat.prefix)} (${report.storedPat.name})` : displayPrefix(report.storedPat?.prefix ?? "?")
|
|
@@ -63483,26 +63660,30 @@ function cardRows(report) {
|
|
|
63483
63660
|
}
|
|
63484
63661
|
}
|
|
63485
63662
|
if (report.session && report.active !== "session") {
|
|
63663
|
+
const state = !report.session.expired ? "not used, env PAT wins" : report.session.refreshError ? `expired, renewal failed: ${report.session.refreshError}` : "expired";
|
|
63486
63664
|
rows.push([
|
|
63487
63665
|
"session",
|
|
63488
|
-
`${report.session.email ?? report.session.userId} (${
|
|
63666
|
+
`${report.session.email ?? report.session.userId} (${state})`
|
|
63489
63667
|
]);
|
|
63490
63668
|
}
|
|
63491
63669
|
return rows;
|
|
63492
63670
|
}
|
|
63493
63671
|
async function runWhoami(args = {}) {
|
|
63672
|
+
await settleSession();
|
|
63494
63673
|
const report = describeCredentials();
|
|
63495
|
-
const
|
|
63496
|
-
|
|
63674
|
+
const token = activePat(report);
|
|
63675
|
+
const pat = token ? await lookupPat(token) : { kind: "unknown" };
|
|
63676
|
+
const reason = reasonFor(report, pat);
|
|
63677
|
+
process.exitCode = usableForControlPlane(report, pat) ? 0 : 1;
|
|
63497
63678
|
if (args.json) {
|
|
63498
|
-
console.log(JSON.stringify(toJson(report, reason), null, 2));
|
|
63679
|
+
console.log(JSON.stringify(toJson(report, pat, reason), null, 2));
|
|
63499
63680
|
return;
|
|
63500
63681
|
}
|
|
63501
63682
|
const signedIn = report.active !== null;
|
|
63502
63683
|
await showIdentityCard({
|
|
63503
63684
|
title: signedIn ? "WHOAMI" : "NOT SIGNED IN",
|
|
63504
63685
|
tone: process.exitCode === 0 ? "ok" : "warn",
|
|
63505
|
-
email: report.active === "session" ? report.session?.email ?? report.session?.userId ?? null : null,
|
|
63686
|
+
email: report.active === "session" ? report.session?.email ?? report.session?.userId ?? null : pat.kind === "ok" ? pat.email ?? pat.userId : null,
|
|
63506
63687
|
controlPlaneUrl: signedIn ? controlPlaneBaseUrl(report.active === "session" ? readAuth() : null) : null,
|
|
63507
63688
|
expiresAt: report.active === "session" ? report.session?.expiresAt : null,
|
|
63508
63689
|
rows: cardRows(report),
|
|
@@ -75158,22 +75339,13 @@ function TokenCreatedCard(props) {
|
|
|
75158
75339
|
children: [
|
|
75159
75340
|
"saved to ",
|
|
75160
75341
|
props.storedAt,
|
|
75161
|
-
" (mode 0600)
|
|
75162
|
-
REGISTRY_COMMANDS
|
|
75342
|
+
" (mode 0600), used whenever no login session is usable"
|
|
75163
75343
|
]
|
|
75164
75344
|
}, undefined, true, undefined, this),
|
|
75165
75345
|
/* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
|
|
75166
75346
|
dimColor: true,
|
|
75167
|
-
children:
|
|
75168
|
-
|
|
75169
|
-
" do ",
|
|
75170
|
-
/* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
|
|
75171
|
-
bold: true,
|
|
75172
|
-
children: "not"
|
|
75173
|
-
}, undefined, false, undefined, this),
|
|
75174
|
-
" read that file — export it instead:"
|
|
75175
|
-
]
|
|
75176
|
-
}, undefined, true, undefined, this)
|
|
75347
|
+
children: "for CI or another machine, export it instead:"
|
|
75348
|
+
}, undefined, false, undefined, this)
|
|
75177
75349
|
]
|
|
75178
75350
|
}, undefined, true, undefined, this)
|
|
75179
75351
|
]
|
|
@@ -75363,7 +75535,7 @@ function sessionWouldBeUsed() {
|
|
|
75363
75535
|
const { ok, session } = authStatus();
|
|
75364
75536
|
if (ok)
|
|
75365
75537
|
return true;
|
|
75366
|
-
return Boolean(session
|
|
75538
|
+
return Boolean(session && isSessionRefreshable(session));
|
|
75367
75539
|
}
|
|
75368
75540
|
function withLoginHint(error) {
|
|
75369
75541
|
if (!(error instanceof ApiError) || error.status !== 403)
|
|
@@ -75397,6 +75569,7 @@ async function runTokenCreate(args) {
|
|
|
75397
75569
|
} else {
|
|
75398
75570
|
scopes = args.scopes;
|
|
75399
75571
|
}
|
|
75572
|
+
const minter = readAuth()?.user_id;
|
|
75400
75573
|
const spinner2 = de();
|
|
75401
75574
|
spinner2.start("Creating token…");
|
|
75402
75575
|
let created;
|
|
@@ -75411,7 +75584,10 @@ async function runTokenCreate(args) {
|
|
|
75411
75584
|
}
|
|
75412
75585
|
spinner2.stop("Token created.");
|
|
75413
75586
|
try {
|
|
75414
|
-
|
|
75587
|
+
if (readAuth()?.user_id !== minter) {
|
|
75588
|
+
throw new Error("the login changed while it was being created");
|
|
75589
|
+
}
|
|
75590
|
+
writeToken(created.token, name, minter);
|
|
75415
75591
|
} catch (error) {
|
|
75416
75592
|
throw new Error(`Created token ${created.id}, but could not save it locally: ${error.message}
|
|
75417
75593
|
` + `This is the only time it is shown — copy it now:
|
|
@@ -96053,11 +96229,6 @@ var COMMANDS3 = [
|
|
|
96053
96229
|
"help",
|
|
96054
96230
|
"version"
|
|
96055
96231
|
];
|
|
96056
|
-
var STORED_PAT_COMMANDS = new Set([
|
|
96057
|
-
"template",
|
|
96058
|
-
"skill",
|
|
96059
|
-
"token"
|
|
96060
|
-
]);
|
|
96061
96232
|
var SUBCOMMAND_OWNED_FLAGS = {
|
|
96062
96233
|
token: ["--scope", "--name"]
|
|
96063
96234
|
};
|
|
@@ -96244,7 +96415,7 @@ function help() {
|
|
|
96244
96415
|
out.push(` ${import_picocolors80.default.dim("BRAINBASE_API_URL")} legacy KLS host override (uses /api/cli; proxy/registry fallback)`);
|
|
96245
96416
|
out.push(` ${import_picocolors80.default.dim("BRAINBASE_PROXY_URL")} override the model-proxy URL used by harness traffic (default https://api.v1.brainbaselabs.com)`);
|
|
96246
96417
|
out.push(` ${import_picocolors80.default.dim("BRAINBASE_REGISTRY_URL")} override the registry API URL (default https://api.v1.brainbaselabs.com)`);
|
|
96247
|
-
out.push(` ${import_picocolors80.default.dim("BRAINBASE_TOKEN")} long-lived CLI PAT; the
|
|
96418
|
+
out.push(` ${import_picocolors80.default.dim("BRAINBASE_TOKEN")} long-lived CLI PAT; outranks the login session and the PAT saved in token.json`);
|
|
96248
96419
|
out.push(` ${import_picocolors80.default.dim("BRAINBASE_SKIP_AUTH")} bypass the auth gate for development`);
|
|
96249
96420
|
out.push(` ${import_picocolors80.default.dim("BRAINBASE_NON_INTERACTIVE")} force non-interactive mode — skip/auto-default prompts (CI & agents)`);
|
|
96250
96421
|
out.push(` ${import_picocolors80.default.dim("BRAINBASE_RUN_ENTRYPOINT")} =1 → agent pull executes the agent entrypoint (sandbox boots; or pass --run-entrypoint)`);
|
|
@@ -96397,12 +96568,12 @@ async function requireAuth(cmd) {
|
|
|
96397
96568
|
if (envToken && envToken.trim())
|
|
96398
96569
|
return;
|
|
96399
96570
|
let status = authStatus();
|
|
96400
|
-
if (!status.ok && status.session
|
|
96571
|
+
if (!status.ok && status.session) {
|
|
96401
96572
|
let refreshed;
|
|
96402
96573
|
try {
|
|
96403
96574
|
refreshed = await tryRefreshSession(status.session);
|
|
96404
96575
|
} catch (error2) {
|
|
96405
|
-
if (error2 instanceof AuthRefreshLockTimeoutError &&
|
|
96576
|
+
if (error2 instanceof AuthRefreshLockTimeoutError && storedPatFor(status.session)) {
|
|
96406
96577
|
return;
|
|
96407
96578
|
}
|
|
96408
96579
|
throw error2;
|
|
@@ -96413,16 +96584,13 @@ async function requireAuth(cmd) {
|
|
|
96413
96584
|
}
|
|
96414
96585
|
if (status.ok)
|
|
96415
96586
|
return;
|
|
96416
|
-
if (
|
|
96587
|
+
if (storedPatFor(status.session))
|
|
96417
96588
|
return;
|
|
96418
96589
|
console.error("");
|
|
96419
96590
|
console.error(` ${brandTint("◆")} ${import_picocolors80.default.bold("brainbase")}`);
|
|
96420
96591
|
console.error("");
|
|
96421
96592
|
console.error(` ${import_picocolors80.default.red("✗")} You need to sign in to use ${import_picocolors80.default.bold("brainbase " + cmd)}.`);
|
|
96422
|
-
|
|
96423
|
-
console.error(` ${import_picocolors80.default.dim(status.reason)}`);
|
|
96424
|
-
console.error("");
|
|
96425
|
-
console.error(` Run ${import_picocolors80.default.cyan("brainbase login")} to connect this device.`);
|
|
96593
|
+
console.error(` ${import_picocolors80.default.dim(noUsableCredentialMessage())}`);
|
|
96426
96594
|
console.error("");
|
|
96427
96595
|
process14.exit(1);
|
|
96428
96596
|
}
|
|
@@ -96706,7 +96874,7 @@ ${didYouMean(cmd, COMMANDS3, "brainbase")}`);
|
|
|
96706
96874
|
} catch (err) {
|
|
96707
96875
|
console.error(import_picocolors80.default.red(`
|
|
96708
96876
|
${err.message}`));
|
|
96709
|
-
if (err instanceof ApiError && err.status === 401) {
|
|
96877
|
+
if (err instanceof ApiError && err.status === 401 && !err.message.includes("brainbase login")) {
|
|
96710
96878
|
console.error(` Run ${import_picocolors80.default.cyan("brainbase login")} to connect this device.`);
|
|
96711
96879
|
}
|
|
96712
96880
|
if (process14.env.BRAINBASE_DEBUG)
|