@brainbase-labs/cli 0.16.1 → 0.16.3
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 +6 -0
- package/dist/index.js +439 -149
- 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.
|
|
36011
|
+
version: "0.16.3",
|
|
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
|
|
40918
|
+
function acquireLock(lockFile, timeoutMs) {
|
|
40901
40919
|
ensureDir(BRAINBASE_HOME);
|
|
40902
|
-
|
|
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
|
-
|
|
40909
|
-
|
|
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
|
|
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
|
-
|
|
40926
|
-
|
|
40927
|
-
|
|
40928
|
-
|
|
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
|
|
40934
|
-
|
|
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
|
-
|
|
40937
|
-
|
|
40938
|
-
|
|
40939
|
-
|
|
40940
|
-
|
|
40941
|
-
|
|
40942
|
-
|
|
40943
|
-
|
|
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
|
-
}
|
|
40946
|
-
|
|
41105
|
+
} finally {
|
|
41106
|
+
releaseRefreshLock();
|
|
40947
41107
|
}
|
|
40948
|
-
|
|
41108
|
+
}
|
|
41109
|
+
async function tryRefreshSession(expectedSession = readAuth()) {
|
|
41110
|
+
if (!expectedSession)
|
|
40949
41111
|
return null;
|
|
40950
|
-
|
|
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
|
-
|
|
40953
|
-
|
|
40954
|
-
|
|
41126
|
+
const result = await refresh;
|
|
41127
|
+
return validatedRefreshResult(expectedSession, result);
|
|
41128
|
+
} finally {
|
|
41129
|
+
if (refreshInFlight?.promise === refresh)
|
|
41130
|
+
refreshInFlight = null;
|
|
41131
|
+
}
|
|
41132
|
+
}
|
|
41133
|
+
async function refreshSessionAfterUnauthorized(rejectedSession) {
|
|
41134
|
+
const current = readAuth();
|
|
41135
|
+
if (!current || !isSameAuthIdentity(current, rejectedSession)) {
|
|
41136
|
+
throw new AuthSessionChangedError;
|
|
40955
41137
|
}
|
|
40956
|
-
if (
|
|
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
|
-
|
|
40959
|
-
|
|
40960
|
-
const
|
|
40961
|
-
|
|
40962
|
-
|
|
40963
|
-
|
|
40964
|
-
|
|
40965
|
-
|
|
40966
|
-
|
|
40967
|
-
|
|
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
|
|
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 {
|
|
53599
|
+
return {
|
|
53600
|
+
bearer: envToken.trim(),
|
|
53601
|
+
session: null,
|
|
53602
|
+
source: "env_pat"
|
|
53603
|
+
};
|
|
53604
|
+
}
|
|
53605
|
+
const session = await ensureFreshSession();
|
|
53606
|
+
if (session) {
|
|
53607
|
+
return {
|
|
53608
|
+
bearer: session.access_token,
|
|
53609
|
+
session,
|
|
53610
|
+
source: "session"
|
|
53611
|
+
};
|
|
53407
53612
|
}
|
|
53408
|
-
const
|
|
53409
|
-
|
|
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
|
|
53412
|
-
const
|
|
53413
|
-
|
|
53414
|
-
|
|
53415
|
-
|
|
53416
|
-
|
|
53417
|
-
|
|
53418
|
-
|
|
53419
|
-
|
|
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
|
-
|
|
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
|
|
53627
|
-
|
|
53628
|
-
|
|
53629
|
-
|
|
53630
|
-
|
|
53631
|
-
|
|
53632
|
-
|
|
53633
|
-
|
|
53634
|
-
|
|
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
|
-
|
|
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
|
-
|
|
53660
|
-
const
|
|
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
|
-
|
|
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
|
|
53734
|
-
const
|
|
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
|
|
53744
|
-
const
|
|
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
|
|
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
|
-
|
|
53781
|
-
|
|
53782
|
-
|
|
53783
|
-
|
|
53784
|
-
|
|
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
|
|
58389
|
-
const
|
|
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
|
|
58399
|
-
const
|
|
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
|
|
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
|
-
|
|
58436
|
-
|
|
58437
|
-
|
|
58438
|
-
|
|
58439
|
-
|
|
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 {
|
|
@@ -60827,7 +61062,8 @@ var CapabilitiesSchema = exports_external.object({
|
|
|
60827
61062
|
memory: exports_external.boolean().optional(),
|
|
60828
61063
|
browser: exports_external.boolean().optional(),
|
|
60829
61064
|
slack: exports_external.boolean().optional(),
|
|
60830
|
-
meeting: exports_external.boolean().optional()
|
|
61065
|
+
meeting: exports_external.boolean().optional(),
|
|
61066
|
+
github: exports_external.boolean().optional()
|
|
60831
61067
|
});
|
|
60832
61068
|
var EvalOutputShape = exports_external.enum(["binary", "rating", "classification"]);
|
|
60833
61069
|
var EVAL_SLUG_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
@@ -61874,6 +62110,39 @@ function buildMeetingMcpComponent(scope) {
|
|
|
61874
62110
|
};
|
|
61875
62111
|
}
|
|
61876
62112
|
|
|
62113
|
+
// src/core/github-mcp.ts
|
|
62114
|
+
var DEFAULT_GITHUB_MCP_BASE = "https://brainbase-github-mcp.onrender.com";
|
|
62115
|
+
var GITHUB_MCP_SLUG = "brainbase-github";
|
|
62116
|
+
function githubMcpBaseUrl() {
|
|
62117
|
+
const envOverride = process.env.BRAINBASE_GITHUB_MCP_URL;
|
|
62118
|
+
if (envOverride)
|
|
62119
|
+
return envOverride.replace(/\/+$/, "");
|
|
62120
|
+
return DEFAULT_GITHUB_MCP_BASE;
|
|
62121
|
+
}
|
|
62122
|
+
function githubMcpUrl() {
|
|
62123
|
+
return `${githubMcpBaseUrl()}/t/\${BRAINBASE_THREAD_ID}/mcp`;
|
|
62124
|
+
}
|
|
62125
|
+
function githubMcpPayload() {
|
|
62126
|
+
return {
|
|
62127
|
+
url: githubMcpUrl(),
|
|
62128
|
+
headers: {
|
|
62129
|
+
Authorization: "Bearer ${BRAINBASE_TOKEN}"
|
|
62130
|
+
}
|
|
62131
|
+
};
|
|
62132
|
+
}
|
|
62133
|
+
function buildGithubMcpComponent(scope) {
|
|
62134
|
+
return {
|
|
62135
|
+
type: "mcp",
|
|
62136
|
+
slug: GITHUB_MCP_SLUG,
|
|
62137
|
+
scope,
|
|
62138
|
+
rootDir: "",
|
|
62139
|
+
description: "This agent's GitHub connector (review pull requests, comment on PRs " + "and issues). Scoped to this agent only.",
|
|
62140
|
+
meta: { mcp: githubMcpPayload() },
|
|
62141
|
+
payload: githubMcpPayload(),
|
|
62142
|
+
checksum: "builtin:brainbase-github:v1"
|
|
62143
|
+
};
|
|
62144
|
+
}
|
|
62145
|
+
|
|
61877
62146
|
// src/core/browser-mcp.ts
|
|
61878
62147
|
var DEFAULT_BROWSER_MCP_BASE = "https://brainbase-browser-mcp.onrender.com";
|
|
61879
62148
|
var BROWSER_MCP_SLUG = "brainbase-browser";
|
|
@@ -61913,7 +62182,8 @@ function capabilitiesFromAgent(agent) {
|
|
|
61913
62182
|
memory: agent.memory_enabled !== false,
|
|
61914
62183
|
browser: agent.browser_enabled !== false,
|
|
61915
62184
|
slack: agent.slack_connected === true,
|
|
61916
|
-
meeting: agent.meeting_connected === true
|
|
62185
|
+
meeting: agent.meeting_connected === true,
|
|
62186
|
+
github: agent.github_connected === true
|
|
61917
62187
|
};
|
|
61918
62188
|
}
|
|
61919
62189
|
function capabilitiesFromManifest(manifest) {
|
|
@@ -61922,7 +62192,8 @@ function capabilitiesFromManifest(manifest) {
|
|
|
61922
62192
|
memory: c2.memory !== false,
|
|
61923
62193
|
browser: c2.browser !== false,
|
|
61924
62194
|
slack: c2.slack === true,
|
|
61925
|
-
meeting: c2.meeting === true
|
|
62195
|
+
meeting: c2.meeting === true,
|
|
62196
|
+
github: c2.github === true
|
|
61926
62197
|
};
|
|
61927
62198
|
}
|
|
61928
62199
|
function resolveBuiltinMcps(input) {
|
|
@@ -61932,7 +62203,8 @@ function resolveBuiltinMcps(input) {
|
|
|
61932
62203
|
{ slug: MEMORY_MCP_SLUG, enabled: caps.memory, build: buildMemoryMcpComponent },
|
|
61933
62204
|
{ slug: BROWSER_MCP_SLUG, enabled: caps.browser, build: buildBrowserMcpComponent },
|
|
61934
62205
|
{ slug: SLACK_MCP_SLUG, enabled: caps.slack, build: buildSlackMcpComponent },
|
|
61935
|
-
{ slug: MEETING_MCP_SLUG, enabled: caps.meeting, build: buildMeetingMcpComponent }
|
|
62206
|
+
{ slug: MEETING_MCP_SLUG, enabled: caps.meeting, build: buildMeetingMcpComponent },
|
|
62207
|
+
{ slug: GITHUB_MCP_SLUG, enabled: caps.github, build: buildGithubMcpComponent }
|
|
61936
62208
|
];
|
|
61937
62209
|
const install = [];
|
|
61938
62210
|
const removeSlugs = [];
|
|
@@ -63490,7 +63762,8 @@ function mergeManifest(cwd2, prev, cloud, cloudAgent, harness) {
|
|
|
63490
63762
|
memory: caps.memory,
|
|
63491
63763
|
browser: caps.browser,
|
|
63492
63764
|
slack: caps.slack,
|
|
63493
|
-
meeting: caps.meeting
|
|
63765
|
+
meeting: caps.meeting,
|
|
63766
|
+
github: caps.github
|
|
63494
63767
|
}
|
|
63495
63768
|
};
|
|
63496
63769
|
}
|
|
@@ -65510,7 +65783,8 @@ function buildManifestFromCloud(cloud, agent) {
|
|
|
65510
65783
|
memory: caps.memory,
|
|
65511
65784
|
browser: caps.browser,
|
|
65512
65785
|
slack: caps.slack,
|
|
65513
|
-
meeting: caps.meeting
|
|
65786
|
+
meeting: caps.meeting,
|
|
65787
|
+
github: caps.github
|
|
65514
65788
|
}
|
|
65515
65789
|
};
|
|
65516
65790
|
}
|
|
@@ -75811,6 +76085,12 @@ var PROTECTED = new Set([
|
|
|
75811
76085
|
"status",
|
|
75812
76086
|
"token"
|
|
75813
76087
|
]);
|
|
76088
|
+
var STORED_PAT_COMMANDS = new Set([
|
|
76089
|
+
"template",
|
|
76090
|
+
"skill",
|
|
76091
|
+
"publish",
|
|
76092
|
+
"token"
|
|
76093
|
+
]);
|
|
75814
76094
|
function help() {
|
|
75815
76095
|
const out = [];
|
|
75816
76096
|
out.push("");
|
|
@@ -75953,13 +76233,23 @@ async function requireAuth(cmd) {
|
|
|
75953
76233
|
return;
|
|
75954
76234
|
let status = authStatus();
|
|
75955
76235
|
if (!status.ok && status.session?.refresh_token) {
|
|
75956
|
-
|
|
76236
|
+
let refreshed;
|
|
76237
|
+
try {
|
|
76238
|
+
refreshed = await tryRefreshSession(status.session);
|
|
76239
|
+
} catch (error2) {
|
|
76240
|
+
if (error2 instanceof AuthRefreshLockTimeoutError && STORED_PAT_COMMANDS.has(cmd) && readToken()) {
|
|
76241
|
+
return;
|
|
76242
|
+
}
|
|
76243
|
+
throw error2;
|
|
76244
|
+
}
|
|
75957
76245
|
if (refreshed) {
|
|
75958
76246
|
status = { ok: true, session: refreshed };
|
|
75959
76247
|
}
|
|
75960
76248
|
}
|
|
75961
76249
|
if (status.ok)
|
|
75962
76250
|
return;
|
|
76251
|
+
if (STORED_PAT_COMMANDS.has(cmd) && readToken())
|
|
76252
|
+
return;
|
|
75963
76253
|
console.error("");
|
|
75964
76254
|
console.error(` ${brandTint("◆")} ${import_picocolors43.default.bold("brainbase")}`);
|
|
75965
76255
|
console.error("");
|