@brainbase-labs/cli 0.16.1 → 0.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +6 -0
  2. package/dist/index.js +394 -143
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -22,6 +22,12 @@ brainbase template onboard <creator/slug> # install or refresh a template
22
22
 
23
23
  Run `brainbase help` to see every command.
24
24
 
25
+ ## Development
26
+
27
+ Use Bun 1.3.10 when building this repository. The generated
28
+ `dist/index.js` is committed and byte-checked in CI, and Bun patch releases
29
+ can produce different bundle output.
30
+
25
31
  ## Agent runtime configuration
26
32
 
27
33
  `brainbase.agent.yaml` can declare the provider and default model used by
package/dist/index.js CHANGED
@@ -16651,7 +16651,7 @@ Learn more about this warning here: https://reactjs.org/link/legacy-context`, so
16651
16651
 
16652
16652
  ` + ("" + errorBoundaryMessage);
16653
16653
  console["error"](combinedMessage);
16654
- }
16654
+ } else {}
16655
16655
  } catch (e2) {
16656
16656
  setTimeout(function() {
16657
16657
  throw e2;
@@ -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.16.1",
36011
+ version: "0.16.2",
36012
36012
  description: "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
36013
36013
  type: "module",
36014
36014
  bin: {
@@ -36407,6 +36407,17 @@ function writeJson(p, data) {
36407
36407
  fs2.writeFileSync(p, JSON.stringify(data, null, 2) + `
36408
36408
  `);
36409
36409
  }
36410
+ function writeJsonAtomic(p, data) {
36411
+ ensureDir(path2.dirname(p));
36412
+ const temporaryPath = `${p}.${process.pid}.${Math.random().toString(16).slice(2)}.tmp`;
36413
+ try {
36414
+ fs2.writeFileSync(temporaryPath, JSON.stringify(data, null, 2) + `
36415
+ `, { flag: "wx", mode: 384 });
36416
+ fs2.renameSync(temporaryPath, p);
36417
+ } finally {
36418
+ fs2.rmSync(temporaryPath, { force: true });
36419
+ }
36420
+ }
36410
36421
  function listDirs(dir) {
36411
36422
  if (!exists(dir))
36412
36423
  return [];
@@ -40874,7 +40885,14 @@ import fs7 from "node:fs";
40874
40885
  // src/core/auth.ts
40875
40886
  import path8 from "node:path";
40876
40887
  import fs6 from "node:fs";
40888
+ import { randomUUID } from "node:crypto";
40877
40889
  var AUTH_FILE = path8.join(BRAINBASE_HOME, "auth.json");
40890
+ var AUTH_LOCK_FILE = `${AUTH_FILE}.lock`;
40891
+ var AUTH_LOCK_TIMEOUT_MS = 5000;
40892
+ var REFRESH_LOCK_FILE = `${AUTH_FILE}.refresh.lock`;
40893
+ var REFRESH_LOCK_TIMEOUT_MS = 1e4;
40894
+ var REFRESH_REQUEST_TIMEOUT_MS = 5000;
40895
+ var lockWaiter = new Int32Array(new SharedArrayBuffer(4));
40878
40896
  var AuthSessionSchema = exports_external.object({
40879
40897
  schemaVersion: exports_external.literal(1),
40880
40898
  access_token: exports_external.string(),
@@ -40897,22 +40915,99 @@ function readAuth() {
40897
40915
  return null;
40898
40916
  }
40899
40917
  }
40900
- function writeAuth(s) {
40918
+ function acquireLock(lockFile, timeoutMs) {
40901
40919
  ensureDir(BRAINBASE_HOME);
40902
- writeJson(AUTH_FILE, s);
40920
+ const deadline = Date.now() + timeoutMs;
40921
+ const lockId = randomUUID();
40922
+ const owner = `${process.pid}:${lockId}`;
40923
+ const candidate = `${lockFile}.${lockId}.tmp`;
40924
+ while (true) {
40925
+ try {
40926
+ fs6.writeFileSync(candidate, owner, {
40927
+ encoding: "utf8",
40928
+ mode: 384,
40929
+ flag: "wx"
40930
+ });
40931
+ fs6.linkSync(candidate, lockFile);
40932
+ fs6.rmSync(candidate, { force: true });
40933
+ return () => {
40934
+ try {
40935
+ if (fs6.readFileSync(lockFile, "utf8") === owner) {
40936
+ fs6.rmSync(lockFile, { force: true });
40937
+ }
40938
+ } catch {}
40939
+ };
40940
+ } catch (error) {
40941
+ fs6.rmSync(candidate, { force: true });
40942
+ if (error.code !== "EEXIST")
40943
+ throw error;
40944
+ if (Date.now() >= deadline)
40945
+ return null;
40946
+ Atomics.wait(lockWaiter, 0, 0, 10);
40947
+ }
40948
+ }
40949
+ }
40950
+ function acquireAuthLock() {
40951
+ const release = acquireLock(AUTH_LOCK_FILE, AUTH_LOCK_TIMEOUT_MS);
40952
+ if (!release) {
40953
+ throw new Error(`Timed out waiting to update CLI authentication; if no other brainbase process is running, remove ${AUTH_LOCK_FILE}`);
40954
+ }
40955
+ return release;
40956
+ }
40957
+ function withAuthLock(operation) {
40958
+ const release = acquireAuthLock();
40959
+ try {
40960
+ return operation();
40961
+ } finally {
40962
+ release();
40963
+ }
40964
+ }
40965
+ function writeAuthUnlocked(s) {
40966
+ writeJsonAtomic(AUTH_FILE, s);
40903
40967
  try {
40904
40968
  fs6.chmodSync(AUTH_FILE, 384);
40905
40969
  } catch {}
40906
40970
  }
40971
+ function writeAuth(s) {
40972
+ withAuthLock(() => writeAuthUnlocked(s));
40973
+ }
40907
40974
  function clearAuth() {
40908
- if (exists(AUTH_FILE))
40909
- fs6.rmSync(AUTH_FILE);
40975
+ withAuthLock(() => {
40976
+ if (exists(AUTH_FILE))
40977
+ fs6.rmSync(AUTH_FILE);
40978
+ });
40910
40979
  }
40911
40980
  function isExpired(session) {
40912
40981
  if (!session.expires_at)
40913
40982
  return false;
40914
40983
  const now = Math.floor(Date.now() / 1000);
40915
- return session.expires_at <= now - 5;
40984
+ return session.expires_at <= now + 5;
40985
+ }
40986
+ function isNearExpiry(session, marginSeconds = 60) {
40987
+ if (!session.expires_at)
40988
+ return false;
40989
+ const now = Math.floor(Date.now() / 1000);
40990
+ return session.expires_at <= now + marginSeconds;
40991
+ }
40992
+ function isAuthValid(session) {
40993
+ return !!session && !isExpired(session);
40994
+ }
40995
+ function isSameAuthIdentity(a3, b3) {
40996
+ return a3.user_id === b3.user_id && (a3.server ?? null) === (b3.server ?? null) && (a3.control_plane_url ?? null) === (b3.control_plane_url ?? null) && (a3.supabase_url ?? null) === (b3.supabase_url ?? null);
40997
+ }
40998
+
40999
+ class AuthSessionChangedError extends Error {
41000
+ constructor() {
41001
+ super("CLI authentication changed while this command was running; retry it");
41002
+ this.name = "AuthSessionChangedError";
41003
+ }
41004
+ }
41005
+
41006
+ class AuthRefreshLockTimeoutError extends Error {
41007
+ constructor() {
41008
+ super(`Timed out waiting to refresh CLI authentication; retry, or if no other brainbase process is running, remove ${REFRESH_LOCK_FILE}`);
41009
+ this.name = "AuthRefreshLockTimeoutError";
41010
+ }
40916
41011
  }
40917
41012
  function authStatus() {
40918
41013
  const s = readAuth();
@@ -40922,52 +41017,157 @@ function authStatus() {
40922
41017
  return { ok: false, session: s, reason: "session expired" };
40923
41018
  return { ok: true, session: s };
40924
41019
  }
40925
- async function tryRefreshSession() {
40926
- const session = readAuth();
40927
- if (!session)
40928
- return null;
41020
+ var refreshInFlight = null;
41021
+ function validatedRefreshResult(expectedSession, result) {
41022
+ const current = readAuth();
41023
+ if (!current || !isSameAuthIdentity(current, expectedSession) || result && !isSameAuthIdentity(result, expectedSession)) {
41024
+ throw new AuthSessionChangedError;
41025
+ }
41026
+ if (result)
41027
+ return result;
41028
+ return isAuthValid(current) ? current : null;
41029
+ }
41030
+ function adoptRefreshedLineage(session) {
41031
+ const current = readAuth();
41032
+ if (!current || !isSameAuthIdentity(current, session)) {
41033
+ throw new AuthSessionChangedError;
41034
+ }
41035
+ const sameRefreshLineage = current.access_token === session.access_token && current.refresh_token === session.refresh_token;
41036
+ if (sameRefreshLineage)
41037
+ return;
41038
+ return isAuthValid(current) ? current : null;
41039
+ }
41040
+ async function refreshSession(session) {
40929
41041
  if (!session.refresh_token)
40930
41042
  return null;
40931
41043
  if (!session.supabase_url || !session.supabase_anon_key)
40932
41044
  return null;
40933
- const url = `${session.supabase_url.replace(/\/+$/, "")}/auth/v1/token?grant_type=refresh_token`;
40934
- let res;
41045
+ const releaseRefreshLock = acquireLock(REFRESH_LOCK_FILE, REFRESH_LOCK_TIMEOUT_MS);
41046
+ if (!releaseRefreshLock) {
41047
+ const adopted = adoptRefreshedLineage(session);
41048
+ if (adopted !== undefined)
41049
+ return adopted;
41050
+ if (isAuthValid(session))
41051
+ return session;
41052
+ throw new AuthRefreshLockTimeoutError;
41053
+ }
40935
41054
  try {
40936
- res = await fetch(url, {
40937
- method: "POST",
40938
- headers: {
40939
- "Content-Type": "application/json",
40940
- apikey: session.supabase_anon_key,
40941
- Authorization: `Bearer ${session.supabase_anon_key}`
40942
- },
40943
- body: JSON.stringify({ refresh_token: session.refresh_token })
41055
+ const adopted = adoptRefreshedLineage(session);
41056
+ if (adopted !== undefined)
41057
+ return adopted;
41058
+ const url = `${session.supabase_url.replace(/\/+$/, "")}/auth/v1/token?grant_type=refresh_token`;
41059
+ let res;
41060
+ try {
41061
+ res = await fetch(url, {
41062
+ method: "POST",
41063
+ signal: AbortSignal.timeout(REFRESH_REQUEST_TIMEOUT_MS),
41064
+ headers: {
41065
+ "Content-Type": "application/json",
41066
+ apikey: session.supabase_anon_key,
41067
+ Authorization: `Bearer ${session.supabase_anon_key}`
41068
+ },
41069
+ body: JSON.stringify({ refresh_token: session.refresh_token })
41070
+ });
41071
+ } catch {
41072
+ return null;
41073
+ }
41074
+ if (!res.ok)
41075
+ return null;
41076
+ let data;
41077
+ try {
41078
+ data = await res.json();
41079
+ } catch {
41080
+ return null;
41081
+ }
41082
+ if (!data.access_token)
41083
+ return null;
41084
+ const now = Math.floor(Date.now() / 1000);
41085
+ const expires_at = data.expires_at ?? (data.expires_in ? now + data.expires_in : undefined);
41086
+ const updated = {
41087
+ ...session,
41088
+ access_token: data.access_token,
41089
+ refresh_token: data.refresh_token ?? session.refresh_token,
41090
+ expires_at,
41091
+ user_id: data.user?.id ?? session.user_id,
41092
+ email: data.user?.email ?? session.email,
41093
+ authedAt: new Date().toISOString()
41094
+ };
41095
+ if (!isSameAuthIdentity(updated, session)) {
41096
+ throw new AuthSessionChangedError;
41097
+ }
41098
+ return withAuthLock(() => {
41099
+ const adopted2 = adoptRefreshedLineage(session);
41100
+ if (adopted2 !== undefined)
41101
+ return adopted2;
41102
+ writeAuthUnlocked(updated);
41103
+ return updated;
40944
41104
  });
40945
- } catch {
40946
- return null;
41105
+ } finally {
41106
+ releaseRefreshLock();
40947
41107
  }
40948
- if (!res.ok)
41108
+ }
41109
+ async function tryRefreshSession(expectedSession = readAuth()) {
41110
+ if (!expectedSession)
40949
41111
  return null;
40950
- let data;
41112
+ if (refreshInFlight) {
41113
+ if (!isSameAuthIdentity(refreshInFlight.session, expectedSession)) {
41114
+ throw new AuthSessionChangedError;
41115
+ }
41116
+ const result = await refreshInFlight.promise;
41117
+ return validatedRefreshResult(expectedSession, result);
41118
+ }
41119
+ const current = readAuth();
41120
+ if (!current || !isSameAuthIdentity(current, expectedSession)) {
41121
+ throw new AuthSessionChangedError;
41122
+ }
41123
+ const refresh = refreshSession(current);
41124
+ refreshInFlight = { session: current, promise: refresh };
40951
41125
  try {
40952
- data = await res.json();
40953
- } catch {
40954
- return null;
41126
+ const result = await refresh;
41127
+ return validatedRefreshResult(expectedSession, result);
41128
+ } finally {
41129
+ if (refreshInFlight?.promise === refresh)
41130
+ refreshInFlight = null;
40955
41131
  }
40956
- if (!data.access_token)
41132
+ }
41133
+ async function refreshSessionAfterUnauthorized(rejectedSession) {
41134
+ const current = readAuth();
41135
+ if (!current || !isSameAuthIdentity(current, rejectedSession)) {
41136
+ throw new AuthSessionChangedError;
41137
+ }
41138
+ if (current.access_token !== rejectedSession.access_token && isAuthValid(current)) {
41139
+ return current;
41140
+ }
41141
+ const refreshed = await tryRefreshSession(rejectedSession);
41142
+ if (refreshed) {
41143
+ if (!isSameAuthIdentity(refreshed, rejectedSession)) {
41144
+ throw new AuthSessionChangedError;
41145
+ }
41146
+ return refreshed;
41147
+ }
41148
+ const latest = readAuth();
41149
+ if (!latest || !isSameAuthIdentity(latest, rejectedSession)) {
41150
+ throw new AuthSessionChangedError;
41151
+ }
41152
+ if (latest.access_token !== rejectedSession.access_token && isAuthValid(latest)) {
41153
+ return latest;
41154
+ }
41155
+ return null;
41156
+ }
41157
+ async function ensureFreshSession() {
41158
+ const s = readAuth();
41159
+ if (!s)
40957
41160
  return null;
40958
- const now = Math.floor(Date.now() / 1000);
40959
- const expires_at = data.expires_at ?? (data.expires_in ? now + data.expires_in : undefined);
40960
- const updated = {
40961
- ...session,
40962
- access_token: data.access_token,
40963
- refresh_token: data.refresh_token ?? session.refresh_token,
40964
- expires_at,
40965
- user_id: data.user?.id ?? session.user_id,
40966
- email: data.user?.email ?? session.email,
40967
- authedAt: new Date().toISOString()
40968
- };
40969
- writeAuth(updated);
40970
- return updated;
41161
+ if (!isExpired(s) && !isNearExpiry(s))
41162
+ return s;
41163
+ const refreshed = await tryRefreshSession(s);
41164
+ if (refreshed)
41165
+ return refreshed;
41166
+ const current = readAuth();
41167
+ if (current && isSameAuthIdentity(current, s) && isAuthValid(current)) {
41168
+ return current;
41169
+ }
41170
+ return null;
40971
41171
  }
40972
41172
 
40973
41173
  // src/core/skill-marker-write.ts
@@ -53393,38 +53593,58 @@ function legacyScheduleError() {
53393
53593
  function legacyAgentConfigError() {
53394
53594
  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);
53395
53595
  }
53396
- function requireSession() {
53397
- const status = authStatus();
53398
- if (!status.ok || !status.session) {
53399
- throw new ApiError(status.reason ?? "not logged in", 401);
53400
- }
53401
- return status.session;
53402
- }
53403
- function resolveCredential() {
53596
+ async function resolveCredential() {
53404
53597
  const envToken = process.env.BRAINBASE_TOKEN;
53405
53598
  if (envToken && envToken.trim()) {
53406
- return { bearer: envToken.trim(), session: null };
53599
+ return {
53600
+ bearer: envToken.trim(),
53601
+ session: null,
53602
+ source: "env_pat"
53603
+ };
53407
53604
  }
53408
- const session = requireSession();
53409
- return { bearer: session.access_token, session };
53605
+ const session = await ensureFreshSession();
53606
+ if (session) {
53607
+ return {
53608
+ bearer: session.access_token,
53609
+ session,
53610
+ source: "session"
53611
+ };
53612
+ }
53613
+ const status = authStatus();
53614
+ throw new ApiError(status.ok ? "CLI authentication changed while this command was starting; retry it" : status.reason ?? "not logged in", 401);
53410
53615
  }
53411
- async function request(pathname, init = {}) {
53412
- const { bearer, session } = resolveCredential();
53413
- const url = `${apiBase(session)}${pathname}`;
53414
- const headers = {
53415
- Authorization: `Bearer ${bearer}`,
53416
- Accept: "application/json",
53417
- ...init.headers
53418
- };
53419
- if (init.body && !headers["Content-Type"]) {
53420
- headers["Content-Type"] = "application/json";
53616
+ async function sendRequest(url, init, bearer) {
53617
+ const headers = new Headers(init.headers);
53618
+ if (!headers.has("Authorization")) {
53619
+ headers.set("Authorization", `Bearer ${bearer}`);
53620
+ }
53621
+ if (!headers.has("Accept"))
53622
+ headers.set("Accept", "application/json");
53623
+ if (init.body && !headers.has("Content-Type")) {
53624
+ headers.set("Content-Type", "application/json");
53421
53625
  }
53422
- let res;
53423
53626
  try {
53424
- res = await fetch(url, { ...init, headers });
53627
+ return await fetch(url, { ...init, headers });
53425
53628
  } catch (err) {
53426
53629
  throw new ApiError(`Network error: ${err.message}`);
53427
53630
  }
53631
+ }
53632
+ async function sendWithAuthRetry(session, send) {
53633
+ const res = await send();
53634
+ if (res.status !== 401 || !session)
53635
+ return res;
53636
+ const refreshed = await refreshSessionAfterUnauthorized(session);
53637
+ if (!refreshed || refreshed.access_token === session.access_token) {
53638
+ return res;
53639
+ }
53640
+ if (res.body)
53641
+ res.body.cancel().catch(() => {});
53642
+ return await send(refreshed);
53643
+ }
53644
+ async function request(pathname, init = {}) {
53645
+ const credential = await resolveCredential();
53646
+ const retrySession = credential.source === "session" && !new Headers(init.headers).has("Authorization") ? credential.session : null;
53647
+ const res = await sendWithAuthRetry(retrySession, (refreshed) => sendRequest(`${apiBase(refreshed ?? credential.session)}${pathname}`, init, refreshed?.access_token ?? credential.bearer));
53428
53648
  const text2 = await res.text();
53429
53649
  let body = text2;
53430
53650
  try {
@@ -53541,6 +53761,9 @@ function proxyBaseUrl(session) {
53541
53761
  const envOverride = process.env.BRAINBASE_PROXY_URL || process.env.BRAINBASE_API_URL;
53542
53762
  if (envOverride)
53543
53763
  return envOverride.replace(/\/+$/, "");
53764
+ if (process.env.BRAINBASE_TOKEN?.trim()) {
53765
+ return DEFAULT_PROXY_BASE.replace(/\/+$/, "");
53766
+ }
53544
53767
  if (session?.server)
53545
53768
  return session.server.replace(/\/+$/, "");
53546
53769
  return DEFAULT_PROXY_BASE.replace(/\/+$/, "");
@@ -53618,61 +53841,89 @@ function registryHost() {
53618
53841
  const env3 = process.env.BRAINBASE_REGISTRY_URL || process.env.BRAINBASE_API_URL;
53619
53842
  if (env3)
53620
53843
  return env3.replace(/\/+$/, "");
53844
+ if (process.env.BRAINBASE_TOKEN?.trim())
53845
+ return DEFAULT_BASE;
53621
53846
  const s = readAuth();
53622
53847
  if (s?.server)
53623
53848
  return s.server.replace(/\/+$/, "");
53624
53849
  return DEFAULT_BASE.replace(/\/+$/, "");
53625
53850
  }
53626
- function authHeaders() {
53627
- const envToken = process.env.BRAINBASE_TOKEN;
53628
- if (envToken) {
53629
- return {
53630
- headers: {
53631
- Authorization: `Bearer ${envToken}`,
53632
- "X-Auth-Source": "pat"
53633
- },
53634
- session: null
53635
- };
53851
+ function patAuth(token) {
53852
+ return {
53853
+ headers: {
53854
+ Authorization: `Bearer ${token}`,
53855
+ "X-Auth-Source": "pat"
53856
+ },
53857
+ session: null
53858
+ };
53859
+ }
53860
+ function jwtAuth(session) {
53861
+ return {
53862
+ headers: {
53863
+ Authorization: `Bearer ${session.access_token}`,
53864
+ "X-Auth-Source": "jwt"
53865
+ },
53866
+ session
53867
+ };
53868
+ }
53869
+ async function resolveAuth() {
53870
+ const envToken = process.env.BRAINBASE_TOKEN?.trim();
53871
+ if (envToken)
53872
+ return patAuth(envToken);
53873
+ let freshSession;
53874
+ try {
53875
+ freshSession = await ensureFreshSession();
53876
+ } catch (error) {
53877
+ if (!(error instanceof AuthRefreshLockTimeoutError))
53878
+ throw error;
53879
+ const pat2 = readToken();
53880
+ if (pat2)
53881
+ return patAuth(pat2.token);
53882
+ throw error;
53636
53883
  }
53884
+ if (freshSession)
53885
+ return jwtAuth(freshSession);
53637
53886
  const status = authStatus();
53638
- if (status.ok && status.session) {
53639
- return {
53640
- headers: {
53641
- Authorization: `Bearer ${status.session.access_token}`,
53642
- "X-Auth-Source": "jwt"
53643
- },
53644
- session: status.session
53645
- };
53646
- }
53647
53887
  const pat = readToken();
53648
- if (pat) {
53649
- return {
53650
- headers: {
53651
- Authorization: `Bearer ${pat.token}`,
53652
- "X-Auth-Source": "pat"
53653
- },
53654
- session: null
53655
- };
53656
- }
53657
- throw new ApiError(status.reason ?? "not logged in", 401);
53888
+ if (pat)
53889
+ return patAuth(pat.token);
53890
+ throw new ApiError(status.ok ? "CLI authentication changed while this command was starting; retry it" : status.reason ?? "not logged in", 401);
53658
53891
  }
53659
- async function jsonRequest(pathname, init = {}) {
53660
- const { headers, session } = authHeaders();
53892
+ function mergeHeaders(auth, overrides) {
53893
+ const headers = new Headers(auth);
53894
+ new Headers(overrides).forEach((value, key2) => headers.set(key2, value));
53895
+ return headers;
53896
+ }
53897
+ async function sendRequest2(pathname, init, session) {
53661
53898
  const url = `${baseUrl(session)}${pathname}`;
53662
- const merged = {
53663
- ...headers,
53664
- Accept: "application/json",
53665
- ...init.headers ?? {}
53666
- };
53667
- if (init.body && !merged["Content-Type"] && typeof init.body === "string") {
53668
- merged["Content-Type"] = "application/json";
53669
- }
53670
- let res;
53671
53899
  try {
53672
- res = await fetch(url, { ...init, headers: merged });
53900
+ return await fetch(url, init);
53673
53901
  } catch (err) {
53674
53902
  throw new ApiError(`Network error: ${err.message}`);
53675
53903
  }
53904
+ }
53905
+ async function sendAuthenticatedRequest(pathname, init, auth) {
53906
+ return await sendRequest2(pathname, {
53907
+ ...init,
53908
+ headers: mergeHeaders(auth.headers, init.headers)
53909
+ }, auth.session);
53910
+ }
53911
+ async function registryFetch(pathname, init = {}) {
53912
+ if (new Headers(init.headers).has("Authorization")) {
53913
+ return await sendRequest2(pathname, init, readAuth());
53914
+ }
53915
+ const auth = await resolveAuth();
53916
+ const retrySession = auth.headers["X-Auth-Source"] === "jwt" ? auth.session : null;
53917
+ return await sendWithAuthRetry(retrySession, (refreshed) => sendAuthenticatedRequest(pathname, init, refreshed ? jwtAuth(refreshed) : auth));
53918
+ }
53919
+ async function jsonRequest(pathname, init = {}) {
53920
+ const headers = new Headers(init.headers);
53921
+ if (!headers.has("Accept"))
53922
+ headers.set("Accept", "application/json");
53923
+ if (init.body && !headers.has("Content-Type") && typeof init.body === "string") {
53924
+ headers.set("Content-Type", "application/json");
53925
+ }
53926
+ const res = await registryFetch(pathname, { ...init, headers });
53676
53927
  const text2 = await res.text();
53677
53928
  let body = text2;
53678
53929
  try {
@@ -53730,9 +53981,8 @@ var registryApi = {
53730
53981
  return jsonRequest(`/v1/registry/templates/${encodeURIComponent(creator)}/${encodeURIComponent(slug)}/versions/${encodeURIComponent(version)}`);
53731
53982
  },
53732
53983
  async getFile(creator, slug, version, relPath) {
53733
- const { headers, session } = authHeaders();
53734
- const url = `${baseUrl(session)}/v1/registry/templates/${encodeURIComponent(creator)}/${encodeURIComponent(slug)}/versions/${encodeURIComponent(version)}/files/${relPath}`;
53735
- const res = await fetch(url, { headers });
53984
+ const pathname = `/v1/registry/templates/${encodeURIComponent(creator)}/${encodeURIComponent(slug)}/versions/${encodeURIComponent(version)}/files/${relPath}`;
53985
+ const res = await registryFetch(pathname);
53736
53986
  if (!res.ok) {
53737
53987
  throw new ApiError(`HTTP ${res.status} reading file ${relPath}`, res.status);
53738
53988
  }
@@ -53740,9 +53990,8 @@ var registryApi = {
53740
53990
  return Buffer.from(ab);
53741
53991
  },
53742
53992
  async downloadTarball(creator, slug, version, destPath) {
53743
- const { headers, session } = authHeaders();
53744
- const url = `${baseUrl(session)}/v1/registry/templates/${encodeURIComponent(creator)}/${encodeURIComponent(slug)}/versions/${encodeURIComponent(version)}/tarball`;
53745
- const res = await fetch(url, { headers });
53993
+ const pathname = `/v1/registry/templates/${encodeURIComponent(creator)}/${encodeURIComponent(slug)}/versions/${encodeURIComponent(version)}/tarball`;
53994
+ const res = await registryFetch(pathname);
53746
53995
  if (!res.ok || !res.body) {
53747
53996
  throw new ApiError(`HTTP ${res.status} downloading tarball`, res.status);
53748
53997
  }
@@ -53768,8 +54017,7 @@ var registryApi = {
53768
54017
  });
53769
54018
  },
53770
54019
  async publishVersion(creator, slug, input) {
53771
- const { headers, session } = authHeaders();
53772
- const url = `${baseUrl(session)}/v1/registry/templates/${encodeURIComponent(creator)}/${encodeURIComponent(slug)}/versions`;
54020
+ const pathname = `/v1/registry/templates/${encodeURIComponent(creator)}/${encodeURIComponent(slug)}/versions`;
53773
54021
  const form = new FormData;
53774
54022
  form.set("version", input.version);
53775
54023
  form.set("source_harness", input.sourceHarness);
@@ -53777,16 +54025,11 @@ var registryApi = {
53777
54025
  form.set("manifest", new Blob([input.manifestJson], { type: "application/json" }), "manifest.json");
53778
54026
  const bytes = fs45.readFileSync(input.bundlePath);
53779
54027
  form.set("bundle", new Blob([bytes], { type: "application/gzip" }), "bundle.tgz");
53780
- let res;
53781
- try {
53782
- res = await fetch(url, {
53783
- method: "POST",
53784
- headers: { ...headers, Accept: "application/json" },
53785
- body: form
53786
- });
53787
- } catch (err) {
53788
- throw new ApiError(`Network error: ${err.message}`);
53789
- }
54028
+ const res = await registryFetch(pathname, {
54029
+ method: "POST",
54030
+ headers: { Accept: "application/json" },
54031
+ body: form
54032
+ });
53790
54033
  const text2 = await res.text();
53791
54034
  let body = text2;
53792
54035
  try {
@@ -58385,9 +58628,8 @@ var skillsApi = {
58385
58628
  return jsonRequest(`/v1/skill-registry/skills/${encodeURIComponent(creator)}/${encodeURIComponent(slug)}/versions/${encodeURIComponent(version)}`);
58386
58629
  },
58387
58630
  async getFile(creator, slug, version, relPath) {
58388
- const { headers, session } = authHeaders();
58389
- const url = `${baseUrl(session)}/v1/skill-registry/skills/${encodeURIComponent(creator)}/${encodeURIComponent(slug)}/versions/${encodeURIComponent(version)}/files/${relPath}`;
58390
- const res = await fetch(url, { headers });
58631
+ const pathname = `/v1/skill-registry/skills/${encodeURIComponent(creator)}/${encodeURIComponent(slug)}/versions/${encodeURIComponent(version)}/files/${relPath}`;
58632
+ const res = await registryFetch(pathname);
58391
58633
  if (!res.ok) {
58392
58634
  throw new ApiError(`HTTP ${res.status} reading file ${relPath}`, res.status);
58393
58635
  }
@@ -58395,9 +58637,8 @@ var skillsApi = {
58395
58637
  return Buffer.from(ab);
58396
58638
  },
58397
58639
  async downloadTarball(creator, slug, version, destPath) {
58398
- const { headers, session } = authHeaders();
58399
- const url = `${baseUrl(session)}/v1/skill-registry/skills/${encodeURIComponent(creator)}/${encodeURIComponent(slug)}/versions/${encodeURIComponent(version)}/tarball`;
58400
- const res = await fetch(url, { headers });
58640
+ const pathname = `/v1/skill-registry/skills/${encodeURIComponent(creator)}/${encodeURIComponent(slug)}/versions/${encodeURIComponent(version)}/tarball`;
58641
+ const res = await registryFetch(pathname);
58401
58642
  if (!res.ok || !res.body) {
58402
58643
  throw new ApiError(`HTTP ${res.status} downloading tarball`, res.status);
58403
58644
  }
@@ -58423,8 +58664,7 @@ var skillsApi = {
58423
58664
  });
58424
58665
  },
58425
58666
  async publishVersion(creator, slug, input) {
58426
- const { headers, session } = authHeaders();
58427
- const url = `${baseUrl(session)}/v1/skill-registry/skills/${encodeURIComponent(creator)}/${encodeURIComponent(slug)}/versions`;
58667
+ const pathname = `/v1/skill-registry/skills/${encodeURIComponent(creator)}/${encodeURIComponent(slug)}/versions`;
58428
58668
  const form = new FormData;
58429
58669
  form.set("version", input.version);
58430
58670
  form.set("source_harness", input.sourceHarness);
@@ -58432,16 +58672,11 @@ var skillsApi = {
58432
58672
  form.set("manifest", new Blob([input.manifestJson], { type: "application/json" }), "manifest.json");
58433
58673
  const bytes = fs52.readFileSync(input.bundlePath);
58434
58674
  form.set("bundle", new Blob([bytes], { type: "application/gzip" }), "bundle.tgz");
58435
- let res;
58436
- try {
58437
- res = await fetch(url, {
58438
- method: "POST",
58439
- headers: { ...headers, Accept: "application/json" },
58440
- body: form
58441
- });
58442
- } catch (err) {
58443
- throw new ApiError(`Network error: ${err.message}`);
58444
- }
58675
+ const res = await registryFetch(pathname, {
58676
+ method: "POST",
58677
+ headers: { Accept: "application/json" },
58678
+ body: form
58679
+ });
58445
58680
  const text2 = await res.text();
58446
58681
  let body = text2;
58447
58682
  try {
@@ -75811,6 +76046,12 @@ var PROTECTED = new Set([
75811
76046
  "status",
75812
76047
  "token"
75813
76048
  ]);
76049
+ var STORED_PAT_COMMANDS = new Set([
76050
+ "template",
76051
+ "skill",
76052
+ "publish",
76053
+ "token"
76054
+ ]);
75814
76055
  function help() {
75815
76056
  const out = [];
75816
76057
  out.push("");
@@ -75953,13 +76194,23 @@ async function requireAuth(cmd) {
75953
76194
  return;
75954
76195
  let status = authStatus();
75955
76196
  if (!status.ok && status.session?.refresh_token) {
75956
- const refreshed = await tryRefreshSession();
76197
+ let refreshed;
76198
+ try {
76199
+ refreshed = await tryRefreshSession(status.session);
76200
+ } catch (error2) {
76201
+ if (error2 instanceof AuthRefreshLockTimeoutError && STORED_PAT_COMMANDS.has(cmd) && readToken()) {
76202
+ return;
76203
+ }
76204
+ throw error2;
76205
+ }
75957
76206
  if (refreshed) {
75958
76207
  status = { ok: true, session: refreshed };
75959
76208
  }
75960
76209
  }
75961
76210
  if (status.ok)
75962
76211
  return;
76212
+ if (STORED_PAT_COMMANDS.has(cmd) && readToken())
76213
+ return;
75963
76214
  console.error("");
75964
76215
  console.error(` ${brandTint("◆")} ${import_picocolors43.default.bold("brainbase")}`);
75965
76216
  console.error("");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainbase-labs/cli",
3
- "version": "0.16.1",
3
+ "version": "0.16.2",
4
4
  "description": "Pack, share, and install agent templates across harnesses (Claude Code, Codex, ...).",
5
5
  "type": "module",
6
6
  "bin": {