busa-cli 0.9.12 → 0.9.13
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/dist/{chunk-DRYP6RY3.js → chunk-LRUXSAKS.js} +126 -47
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
|
@@ -775,11 +775,10 @@ var BUSABASE_CLI_CLIENT_ID = "busabase-cli";
|
|
|
775
775
|
|
|
776
776
|
// src/login.ts
|
|
777
777
|
import { DEFAULT_BASE_URL, normalizeBaseUrl } from "busabase-sdk";
|
|
778
|
-
var CLI_CLIENT_PLATFORM = "cli";
|
|
779
778
|
var OAUTH_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
780
779
|
var EXPIRES_AT_KEY = "BUSABASE_TOKEN_EXPIRES_AT";
|
|
781
|
-
var
|
|
782
|
-
var AUTO_REFRESH_THRESHOLD_MS =
|
|
780
|
+
var REFRESH_TOKEN_KEY = "BUSABASE_REFRESH_TOKEN";
|
|
781
|
+
var AUTO_REFRESH_THRESHOLD_MS = 5 * 60 * 1e3;
|
|
783
782
|
var DEFAULT_LOCAL_URL = "http://localhost:15419";
|
|
784
783
|
var base64url = (buffer) => buffer.toString("base64url");
|
|
785
784
|
var isInteractive = () => Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
@@ -828,7 +827,7 @@ async function verifyAuth(baseUrl, token) {
|
|
|
828
827
|
});
|
|
829
828
|
if (res.status === 401) {
|
|
830
829
|
throw new Error(
|
|
831
|
-
`The credential was rejected (401) by ${baseUrl}. If it's an API key, check it in Dashboard \u2192 Settings \u2192 API Keys; if it's
|
|
830
|
+
`The credential was rejected (401) by ${baseUrl}. If it's an API key, check it in Dashboard \u2192 Settings \u2192 API Keys; if it's an OAuth login, run \`busabase-cli login\` again.`
|
|
832
831
|
);
|
|
833
832
|
}
|
|
834
833
|
if (!res.ok) {
|
|
@@ -857,6 +856,7 @@ async function loopbackOauthLogin(baseUrl, useBrowser) {
|
|
|
857
856
|
const error = url.searchParams.get("error");
|
|
858
857
|
const returnedState = url.searchParams.get("state");
|
|
859
858
|
const returnedCode = url.searchParams.get("code");
|
|
859
|
+
const returnedIssuer = url.searchParams.get("iss");
|
|
860
860
|
if (error) {
|
|
861
861
|
respond("Sign-in failed", "You can close this tab and return to the terminal.");
|
|
862
862
|
cleanup();
|
|
@@ -869,6 +869,20 @@ async function loopbackOauthLogin(baseUrl, useBrowser) {
|
|
|
869
869
|
reject(new Error("OAuth state mismatch \u2014 the callback did not match this request."));
|
|
870
870
|
return;
|
|
871
871
|
}
|
|
872
|
+
let issuerMatches = false;
|
|
873
|
+
try {
|
|
874
|
+
issuerMatches = Boolean(
|
|
875
|
+
returnedIssuer && new URL(returnedIssuer).origin === new URL(baseUrl).origin
|
|
876
|
+
);
|
|
877
|
+
} catch {
|
|
878
|
+
issuerMatches = false;
|
|
879
|
+
}
|
|
880
|
+
if (!issuerMatches) {
|
|
881
|
+
respond("Sign-in failed", "Authorization server mismatch. Close this tab and try again.");
|
|
882
|
+
cleanup();
|
|
883
|
+
reject(new Error("OAuth authorization server issuer mismatch."));
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
872
886
|
respond("Signed in \u2713", "You can close this tab and return to the terminal.");
|
|
873
887
|
const address = server.address();
|
|
874
888
|
cleanup();
|
|
@@ -892,7 +906,8 @@ async function loopbackOauthLogin(baseUrl, useBrowser) {
|
|
|
892
906
|
const authorizeUrl = new URL(`${baseUrl}/api/oauth/authorize`);
|
|
893
907
|
authorizeUrl.searchParams.set("response_type", "code");
|
|
894
908
|
authorizeUrl.searchParams.set("client_id", BUSABASE_CLI_CLIENT_ID);
|
|
895
|
-
authorizeUrl.searchParams.set("
|
|
909
|
+
authorizeUrl.searchParams.set("resource", `${baseUrl}/api/v1`);
|
|
910
|
+
authorizeUrl.searchParams.set("scope", "api");
|
|
896
911
|
authorizeUrl.searchParams.set("code_challenge", codeChallenge);
|
|
897
912
|
authorizeUrl.searchParams.set("code_challenge_method", "S256");
|
|
898
913
|
authorizeUrl.searchParams.set("redirect_uri", redirect);
|
|
@@ -913,15 +928,14 @@ async function loopbackOauthLogin(baseUrl, useBrowser) {
|
|
|
913
928
|
);
|
|
914
929
|
const tokenRes = await fetch(`${baseUrl}/api/oauth/token`, {
|
|
915
930
|
method: "POST",
|
|
916
|
-
headers: { "content-type": "application/
|
|
917
|
-
body:
|
|
931
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
932
|
+
body: new URLSearchParams({
|
|
918
933
|
grant_type: "authorization_code",
|
|
919
934
|
client_id: BUSABASE_CLI_CLIENT_ID,
|
|
920
|
-
client_platform: CLI_CLIENT_PLATFORM,
|
|
921
935
|
code,
|
|
922
936
|
code_verifier: codeVerifier,
|
|
923
937
|
redirect_uri: redirectUri,
|
|
924
|
-
|
|
938
|
+
resource: `${baseUrl}/api/v1`
|
|
925
939
|
})
|
|
926
940
|
});
|
|
927
941
|
if (!tokenRes.ok) {
|
|
@@ -929,9 +943,13 @@ async function loopbackOauthLogin(baseUrl, useBrowser) {
|
|
|
929
943
|
throw new Error(`Token exchange failed (HTTP ${tokenRes.status})${text ? `: ${text}` : ""}`);
|
|
930
944
|
}
|
|
931
945
|
const payload = await tokenRes.json();
|
|
932
|
-
const token = payload.
|
|
946
|
+
const token = payload.access_token;
|
|
933
947
|
if (!token) throw new Error("Token exchange returned no credential.");
|
|
934
|
-
return
|
|
948
|
+
return {
|
|
949
|
+
token,
|
|
950
|
+
refreshToken: payload.refresh_token,
|
|
951
|
+
expiresAt: typeof payload.expires_in === "number" ? new Date(Date.now() + payload.expires_in * 1e3).toISOString() : void 0
|
|
952
|
+
};
|
|
935
953
|
}
|
|
936
954
|
var delay = (durationMs) => new Promise((resolve) => setTimeout(resolve, durationMs));
|
|
937
955
|
async function readDeviceResponse(response) {
|
|
@@ -1036,15 +1054,26 @@ async function deviceLogin(baseUrl, useBrowser) {
|
|
|
1036
1054
|
}
|
|
1037
1055
|
throw new Error("The device code expired. Run `busabase-cli login` to request a new one.");
|
|
1038
1056
|
}
|
|
1039
|
-
async function refreshToken(baseUrl,
|
|
1040
|
-
const res = await fetch(`${baseUrl}/api/oauth/
|
|
1057
|
+
async function refreshToken(baseUrl, refreshToken2) {
|
|
1058
|
+
const res = await fetch(`${baseUrl}/api/oauth/token`, {
|
|
1041
1059
|
method: "POST",
|
|
1042
|
-
headers: {
|
|
1060
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
1061
|
+
body: new URLSearchParams({
|
|
1062
|
+
grant_type: "refresh_token",
|
|
1063
|
+
refresh_token: refreshToken2,
|
|
1064
|
+
client_id: BUSABASE_CLI_CLIENT_ID,
|
|
1065
|
+
resource: `${baseUrl}/api/v1`
|
|
1066
|
+
})
|
|
1043
1067
|
});
|
|
1044
|
-
if (res.status === 401) throw new Error("SESSION_EXPIRED");
|
|
1068
|
+
if (res.status === 400 || res.status === 401) throw new Error("SESSION_EXPIRED");
|
|
1045
1069
|
if (!res.ok) throw new Error(`Refresh failed (HTTP ${res.status}) from ${baseUrl}.`);
|
|
1046
1070
|
const payload = await res.json();
|
|
1047
|
-
|
|
1071
|
+
if (!payload.access_token) throw new Error("Refresh response had no access token");
|
|
1072
|
+
return {
|
|
1073
|
+
token: payload.access_token,
|
|
1074
|
+
refreshToken: payload.refresh_token,
|
|
1075
|
+
expiresAt: typeof payload.expires_in === "number" ? new Date(Date.now() + payload.expires_in * 1e3).toISOString() : void 0
|
|
1076
|
+
};
|
|
1048
1077
|
}
|
|
1049
1078
|
async function pickSpaceId(verify, preselected) {
|
|
1050
1079
|
if (preselected) return preselected;
|
|
@@ -1144,6 +1173,7 @@ async function runLogin(options) {
|
|
|
1144
1173
|
let token;
|
|
1145
1174
|
let expiresAt;
|
|
1146
1175
|
let apiKeyExpiresAt;
|
|
1176
|
+
let refreshTokenValue;
|
|
1147
1177
|
if (method === "device") {
|
|
1148
1178
|
const result = await deviceLogin(baseUrl, options.browser);
|
|
1149
1179
|
token = result.token;
|
|
@@ -1152,6 +1182,7 @@ async function runLogin(options) {
|
|
|
1152
1182
|
const result = await loopbackOauthLogin(baseUrl, options.browser);
|
|
1153
1183
|
token = result.token;
|
|
1154
1184
|
expiresAt = result.expiresAt;
|
|
1185
|
+
refreshTokenValue = result.refreshToken;
|
|
1155
1186
|
apiKeyExpiresAt = result.apiKeyExpiresAt;
|
|
1156
1187
|
} else {
|
|
1157
1188
|
if (!apiKey) {
|
|
@@ -1169,14 +1200,15 @@ async function runLogin(options) {
|
|
|
1169
1200
|
BUSABASE_API_KEY: token,
|
|
1170
1201
|
BUSABASE_SPACE_ID: spaceId ?? null,
|
|
1171
1202
|
// Clear any stale expiry when switching to an API key.
|
|
1172
|
-
[EXPIRES_AT_KEY]: expiresAt ?? null
|
|
1203
|
+
[EXPIRES_AT_KEY]: expiresAt ?? null,
|
|
1204
|
+
[REFRESH_TOKEN_KEY]: refreshTokenValue ?? null
|
|
1173
1205
|
});
|
|
1174
1206
|
say("");
|
|
1175
1207
|
say(`\u2713 Signed in and saved to ${dotEnvPath()}`);
|
|
1176
1208
|
return {
|
|
1177
1209
|
status: "signed in",
|
|
1178
1210
|
method: method === "loopback-oauth" ? "oauth" : method,
|
|
1179
|
-
credentialType: method === "device" ? "api_key" : expiresAt ? "
|
|
1211
|
+
credentialType: method === "device" ? "api_key" : expiresAt ? "oauth" : "api_key",
|
|
1180
1212
|
user: verify.user?.email ?? verify.user?.name ?? verify.user?.id ?? "(unknown)",
|
|
1181
1213
|
space: spaceId ?? "(server default)",
|
|
1182
1214
|
createdSpace: String(Boolean(verify.createdSpace)),
|
|
@@ -1191,29 +1223,34 @@ async function runRefresh(options) {
|
|
|
1191
1223
|
const token = options.apiKey;
|
|
1192
1224
|
if (!token) throw new Error("Not signed in \u2014 run `busabase-cli login` first.");
|
|
1193
1225
|
const file = loadDotEnvFile();
|
|
1194
|
-
const
|
|
1195
|
-
if (!
|
|
1226
|
+
const isSavedExpiringOAuthToken = file.BUSABASE_API_KEY === token && Boolean(file.BUSABASE_TOKEN_EXPIRES_AT);
|
|
1227
|
+
if (!isSavedExpiringOAuthToken) {
|
|
1196
1228
|
return {
|
|
1197
1229
|
status: "nothing to refresh",
|
|
1198
|
-
detail: "This credential is an API key and cannot be refreshed. Only OAuth
|
|
1230
|
+
detail: "This credential is an API key and cannot be refreshed. Only OAuth token sets refresh."
|
|
1199
1231
|
};
|
|
1200
1232
|
}
|
|
1233
|
+
const savedRefreshToken = file[REFRESH_TOKEN_KEY];
|
|
1234
|
+
if (!savedRefreshToken) throw new Error("OAuth refresh token is missing \u2014 sign in again.");
|
|
1201
1235
|
let refreshed;
|
|
1202
1236
|
try {
|
|
1203
|
-
refreshed = await refreshToken(baseUrl,
|
|
1237
|
+
refreshed = await refreshToken(baseUrl, savedRefreshToken);
|
|
1204
1238
|
} catch (error) {
|
|
1205
1239
|
if (error instanceof Error && error.message === "SESSION_EXPIRED") {
|
|
1206
1240
|
throw new Error(
|
|
1207
|
-
"Your
|
|
1241
|
+
"Your OAuth authorization has expired \u2014 run `busabase-cli login` to sign in again."
|
|
1208
1242
|
);
|
|
1209
1243
|
}
|
|
1210
1244
|
throw error;
|
|
1211
1245
|
}
|
|
1212
1246
|
writeDotEnvFile({
|
|
1213
1247
|
BUSABASE_API_KEY: refreshed.token,
|
|
1214
|
-
[EXPIRES_AT_KEY]: refreshed.expiresAt ?? null
|
|
1248
|
+
[EXPIRES_AT_KEY]: refreshed.expiresAt ?? null,
|
|
1249
|
+
[REFRESH_TOKEN_KEY]: refreshed.refreshToken ?? null
|
|
1215
1250
|
});
|
|
1216
|
-
say(
|
|
1251
|
+
say(
|
|
1252
|
+
`\u2713 OAuth token refreshed${refreshed.expiresAt ? ` \u2014 valid until ${refreshed.expiresAt}` : ""}`
|
|
1253
|
+
);
|
|
1217
1254
|
return {
|
|
1218
1255
|
status: "refreshed",
|
|
1219
1256
|
expiresAt: refreshed.expiresAt ?? "(unknown)",
|
|
@@ -1221,22 +1258,26 @@ async function runRefresh(options) {
|
|
|
1221
1258
|
};
|
|
1222
1259
|
}
|
|
1223
1260
|
async function maybeAutoRefresh(baseUrl, apiKey) {
|
|
1224
|
-
if (!apiKey) return;
|
|
1261
|
+
if (!apiKey) return void 0;
|
|
1225
1262
|
const file = loadDotEnvFile();
|
|
1226
|
-
if (file.BUSABASE_API_KEY !== apiKey) return;
|
|
1263
|
+
if (file.BUSABASE_API_KEY !== apiKey) return void 0;
|
|
1227
1264
|
const expiresRaw = file[EXPIRES_AT_KEY];
|
|
1228
|
-
if (!expiresRaw) return;
|
|
1265
|
+
if (!expiresRaw) return void 0;
|
|
1229
1266
|
const expiresAt = Date.parse(expiresRaw);
|
|
1230
|
-
if (Number.isNaN(expiresAt)) return;
|
|
1231
|
-
if (expiresAt
|
|
1232
|
-
if (expiresAt - Date.now() > AUTO_REFRESH_THRESHOLD_MS) return;
|
|
1267
|
+
if (Number.isNaN(expiresAt)) return void 0;
|
|
1268
|
+
if (expiresAt - Date.now() > AUTO_REFRESH_THRESHOLD_MS) return void 0;
|
|
1233
1269
|
try {
|
|
1234
|
-
const
|
|
1270
|
+
const savedRefreshToken = file[REFRESH_TOKEN_KEY];
|
|
1271
|
+
if (!savedRefreshToken) return void 0;
|
|
1272
|
+
const refreshed = await refreshToken(normalizeBaseUrl(baseUrl), savedRefreshToken);
|
|
1235
1273
|
writeDotEnvFile({
|
|
1236
1274
|
BUSABASE_API_KEY: refreshed.token,
|
|
1237
|
-
[EXPIRES_AT_KEY]: refreshed.expiresAt ?? null
|
|
1275
|
+
[EXPIRES_AT_KEY]: refreshed.expiresAt ?? null,
|
|
1276
|
+
[REFRESH_TOKEN_KEY]: refreshed.refreshToken ?? null
|
|
1238
1277
|
});
|
|
1278
|
+
return refreshed.token;
|
|
1239
1279
|
} catch {
|
|
1280
|
+
return void 0;
|
|
1240
1281
|
}
|
|
1241
1282
|
}
|
|
1242
1283
|
function assertCredentialNotExpired(apiKey) {
|
|
@@ -1248,29 +1289,36 @@ function assertCredentialNotExpired(apiKey) {
|
|
|
1248
1289
|
const expiresAt = Date.parse(expiresRaw);
|
|
1249
1290
|
if (!Number.isNaN(expiresAt) && expiresAt <= Date.now()) {
|
|
1250
1291
|
throw new Error(
|
|
1251
|
-
"Your saved
|
|
1292
|
+
"Your saved OAuth login has expired. Run `busabase-cli login` to sign in again."
|
|
1252
1293
|
);
|
|
1253
1294
|
}
|
|
1254
1295
|
}
|
|
1255
1296
|
async function runLogout(options) {
|
|
1256
1297
|
const baseUrl = normalizeBaseUrl(options.baseUrl);
|
|
1257
1298
|
const token = options.apiKey;
|
|
1258
|
-
let revoked = "no
|
|
1299
|
+
let revoked = "no OAuth token to revoke";
|
|
1259
1300
|
const file = loadDotEnvFile();
|
|
1260
|
-
if (token &&
|
|
1301
|
+
if (token && file.BUSABASE_API_KEY === token && file[EXPIRES_AT_KEY]) {
|
|
1261
1302
|
try {
|
|
1303
|
+
const tokenToRevoke = file[REFRESH_TOKEN_KEY] ?? token;
|
|
1262
1304
|
const res = await fetch(`${baseUrl}/api/oauth/revoke`, {
|
|
1263
1305
|
method: "POST",
|
|
1264
|
-
headers: {
|
|
1306
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
1307
|
+
body: new URLSearchParams({ token: tokenToRevoke })
|
|
1265
1308
|
});
|
|
1266
|
-
revoked = res.ok ? "
|
|
1309
|
+
revoked = res.ok ? "OAuth token family revoked" : `revoke returned HTTP ${res.status}`;
|
|
1267
1310
|
} catch {
|
|
1268
1311
|
revoked = "revoke request failed (cleared locally anyway)";
|
|
1269
1312
|
}
|
|
1270
1313
|
} else if (token) {
|
|
1271
1314
|
revoked = "API key cleared locally (revoke it in the dashboard to disable it)";
|
|
1272
1315
|
}
|
|
1273
|
-
writeDotEnvFile({
|
|
1316
|
+
writeDotEnvFile({
|
|
1317
|
+
BUSABASE_API_KEY: null,
|
|
1318
|
+
BUSABASE_SPACE_ID: null,
|
|
1319
|
+
[EXPIRES_AT_KEY]: null,
|
|
1320
|
+
[REFRESH_TOKEN_KEY]: null
|
|
1321
|
+
});
|
|
1274
1322
|
say(`\u2713 Cleared the saved credential from ${dotEnvPath()}`);
|
|
1275
1323
|
return { status: "signed out", detail: revoked, config: dotEnvPath() };
|
|
1276
1324
|
}
|
|
@@ -1656,6 +1704,15 @@ var folderNodeType = {
|
|
|
1656
1704
|
operations: []
|
|
1657
1705
|
};
|
|
1658
1706
|
|
|
1707
|
+
// ../../packages/busabase-contract/src/domains/html/definition.ts
|
|
1708
|
+
var htmlNodeType = {
|
|
1709
|
+
type: "html",
|
|
1710
|
+
label: "HTML",
|
|
1711
|
+
icon: "code-xml",
|
|
1712
|
+
capabilities: { hasDetail: true, creatable: true },
|
|
1713
|
+
operations: []
|
|
1714
|
+
};
|
|
1715
|
+
|
|
1659
1716
|
// ../../packages/busabase-contract/src/domains/skill/definition.ts
|
|
1660
1717
|
var skillNodeType = makeFileTreeNodeType({
|
|
1661
1718
|
type: "skill",
|
|
@@ -1666,6 +1723,24 @@ var skillNodeType = makeFileTreeNodeType({
|
|
|
1666
1723
|
entryFile: "SKILL.md"
|
|
1667
1724
|
});
|
|
1668
1725
|
|
|
1726
|
+
// ../../packages/busabase-contract/src/domains/whiteboard/definition.ts
|
|
1727
|
+
var whiteboardNodeType = {
|
|
1728
|
+
type: "whiteboard",
|
|
1729
|
+
label: "Whiteboard",
|
|
1730
|
+
icon: "pen-tool",
|
|
1731
|
+
capabilities: { hasDetail: true, creatable: true },
|
|
1732
|
+
operations: []
|
|
1733
|
+
};
|
|
1734
|
+
|
|
1735
|
+
// ../../packages/busabase-contract/src/domains/workflow/definition.ts
|
|
1736
|
+
var workflowNodeType = {
|
|
1737
|
+
type: "workflow",
|
|
1738
|
+
label: "Workflow",
|
|
1739
|
+
icon: "workflow",
|
|
1740
|
+
capabilities: { hasDetail: true, creatable: true },
|
|
1741
|
+
operations: []
|
|
1742
|
+
};
|
|
1743
|
+
|
|
1669
1744
|
// ../../packages/busabase-contract/src/domains/registry.ts
|
|
1670
1745
|
var GENERIC_NODE_OPERATIONS = [
|
|
1671
1746
|
{
|
|
@@ -1693,7 +1768,10 @@ var BUILTIN_NODE_TYPES = [
|
|
|
1693
1768
|
driveNodeType,
|
|
1694
1769
|
airappNodeType,
|
|
1695
1770
|
fileNodeType,
|
|
1696
|
-
docNodeType
|
|
1771
|
+
docNodeType,
|
|
1772
|
+
whiteboardNodeType,
|
|
1773
|
+
workflowNodeType,
|
|
1774
|
+
htmlNodeType
|
|
1697
1775
|
];
|
|
1698
1776
|
for (const definition of BUILTIN_NODE_TYPES) {
|
|
1699
1777
|
registerNodeType(definition);
|
|
@@ -4477,9 +4555,10 @@ function runAction(state, handler) {
|
|
|
4477
4555
|
return async (_opts, cmd) => {
|
|
4478
4556
|
const opts = cmd.optsWithGlobals();
|
|
4479
4557
|
const config = resolveConfig(opts);
|
|
4558
|
+
const refreshedToken = await maybeAutoRefresh(config.baseUrl, config.apiKey);
|
|
4559
|
+
if (refreshedToken) config.apiKey = refreshedToken;
|
|
4480
4560
|
state.config = config;
|
|
4481
4561
|
assertCredentialNotExpired(config.apiKey);
|
|
4482
|
-
await maybeAutoRefresh(config.baseUrl, config.apiKey);
|
|
4483
4562
|
const client = createBusabaseClient(config);
|
|
4484
4563
|
const result = await handler(client, opts, config);
|
|
4485
4564
|
console.log(render(result, config.output));
|
|
@@ -4489,9 +4568,10 @@ function runArgAction(state, handler) {
|
|
|
4489
4568
|
return async (arg, _opts, cmd) => {
|
|
4490
4569
|
const opts = cmd.optsWithGlobals();
|
|
4491
4570
|
const config = resolveConfig(opts);
|
|
4571
|
+
const refreshedToken = await maybeAutoRefresh(config.baseUrl, config.apiKey);
|
|
4572
|
+
if (refreshedToken) config.apiKey = refreshedToken;
|
|
4492
4573
|
state.config = config;
|
|
4493
4574
|
assertCredentialNotExpired(config.apiKey);
|
|
4494
|
-
await maybeAutoRefresh(config.baseUrl, config.apiKey);
|
|
4495
4575
|
const client = createBusabaseClient(config);
|
|
4496
4576
|
const result = await handler(arg, client, opts, config);
|
|
4497
4577
|
console.log(render(result, config.output));
|
|
@@ -4564,7 +4644,7 @@ function registerGeneratedCommands(program, state) {
|
|
|
4564
4644
|
const getGroup = (displayName, tag) => program.commands.find((c2) => c2.name() === displayName) ?? program.command(displayName).description(tag ?? `${displayName} endpoints`);
|
|
4565
4645
|
const addLeaf = (parent, navPath, procKey, proc) => {
|
|
4566
4646
|
const { route, inputSchema } = proc["~orpc"];
|
|
4567
|
-
const name =
|
|
4647
|
+
const name = [...navPath.slice(1), procKey].map(kebab).join("-");
|
|
4568
4648
|
if (parent.commands.some((c2) => c2.name() === name)) return;
|
|
4569
4649
|
const fields = inputFields(inputSchema);
|
|
4570
4650
|
const leaf = parent.command(name).description(route.summary ?? `${route.method} ${route.path}`);
|
|
@@ -4622,7 +4702,6 @@ OpenAPI: ${route.method} ${route.path}`);
|
|
|
4622
4702
|
const id = [...navPath, key].join(".");
|
|
4623
4703
|
if (isProcedure(child)) {
|
|
4624
4704
|
if (GENERATED_SKIP.has(id)) continue;
|
|
4625
|
-
if (navPath.length > 1) continue;
|
|
4626
4705
|
const parent = navPath.length ? getGroup(kebab(navPath[0]), child["~orpc"].route.tags?.[0]) : program;
|
|
4627
4706
|
addLeaf(parent, navPath, key, child);
|
|
4628
4707
|
} else if (child && typeof child === "object") {
|
|
@@ -4677,7 +4756,7 @@ function buildProgram(state = {}) {
|
|
|
4677
4756
|
runAction(state, (_client, _opts, config) => rawFetch(config, "GET", "/api/v1/openapi.json"))
|
|
4678
4757
|
);
|
|
4679
4758
|
addGlobalFlags(program.command("whoami")).description("Active space, user, and membership").action(runAction(state, (client) => client.auth.verify()));
|
|
4680
|
-
addGlobalFlags(program.command("login")).description("Connect the CLI to Busabase \u2014 Personal/local, Cloud, or self-hosted").option("--device-code", "force device authorization and skip the method prompt").option("--oauth", "legacy same-machine OAuth callback flow").option("--no-browser", "print the sign-in URL instead of opening a browser").option("--refresh", "
|
|
4759
|
+
addGlobalFlags(program.command("login")).description("Connect the CLI to Busabase \u2014 Personal/local, Cloud, or self-hosted").option("--device-code", "force device authorization and skip the method prompt").option("--oauth", "legacy same-machine OAuth callback flow").option("--no-browser", "print the sign-in URL instead of opening a browser").option("--refresh", "rotate the saved OAuth token set (no browser, no re-login)").addHelpText(
|
|
4681
4760
|
"after",
|
|
4682
4761
|
`
|
|
4683
4762
|
Busabase is an approval-first database and knowledge base for AI agents: agents
|
|
@@ -4697,7 +4776,7 @@ The flags below skip the menu (handy for scripts / CI):
|
|
|
4697
4776
|
busabase-cli login --oauth # legacy same-machine callback
|
|
4698
4777
|
busabase-cli login --api-key sk_\u2026 # Cloud API key (headless/CI)
|
|
4699
4778
|
busabase-cli login --base-url http://localhost:15419 # connect to a local server (no auth)
|
|
4700
|
-
busabase-cli login --refresh #
|
|
4779
|
+
busabase-cli login --refresh # rotate the OAuth token set (auto-runs too)`
|
|
4701
4780
|
).action(async (_opts, cmd) => {
|
|
4702
4781
|
const opts = cmd.optsWithGlobals();
|
|
4703
4782
|
const config = resolveConfig(opts);
|
|
@@ -4712,7 +4791,7 @@ The flags below skip the menu (handy for scripts / CI):
|
|
|
4712
4791
|
});
|
|
4713
4792
|
console.log(render(summary, config.output));
|
|
4714
4793
|
});
|
|
4715
|
-
addGlobalFlags(program.command("logout")).description("Revoke the saved OAuth
|
|
4794
|
+
addGlobalFlags(program.command("logout")).description("Revoke the saved OAuth token family (if any) and clear ~/.busabase/.env").action(async (_opts, cmd) => {
|
|
4716
4795
|
const opts = cmd.optsWithGlobals();
|
|
4717
4796
|
const config = resolveConfig(opts);
|
|
4718
4797
|
state.config = config;
|
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "busa-cli",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.13",
|
|
4
4
|
"description": "Command-line client for the Busabase OpenAPI REST API. Talks to a local or remote `busabase server`. Short-name alias for busabase-cli.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/busabase/busabase/tree/main/apps/busabase-cli",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@zip.js/zip.js": "^2.8.20",
|
|
35
35
|
"busabase-package": "workspace:*",
|
|
36
|
-
"busabase-sdk": "0.9.
|
|
36
|
+
"busabase-sdk": "0.9.13",
|
|
37
37
|
"commander": "^14.0.0",
|
|
38
38
|
"tar-stream": "^3.1.7",
|
|
39
39
|
"yaml": "^2.9.0",
|