apiblaze 0.12.0 → 0.14.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/dist/index.js +184 -62
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -51,6 +51,14 @@ var init_types = __esm({
|
|
|
51
51
|
});
|
|
52
52
|
|
|
53
53
|
// src/lib/auth.ts
|
|
54
|
+
var auth_exports = {};
|
|
55
|
+
__export(auth_exports, {
|
|
56
|
+
clearCredentials: () => clearCredentials,
|
|
57
|
+
getAccessToken: () => getAccessToken,
|
|
58
|
+
getApiblazeDir: () => getApiblazeDir,
|
|
59
|
+
loadCredentials: () => loadCredentials,
|
|
60
|
+
saveCredentials: () => saveCredentials
|
|
61
|
+
});
|
|
54
62
|
function saveCredentials(creds) {
|
|
55
63
|
fs.mkdirSync(APIBLAZE_DIR, { recursive: true });
|
|
56
64
|
fs.writeFileSync(CREDENTIALS_PATH, JSON.stringify(creds, null, 2), "utf-8");
|
|
@@ -82,6 +90,9 @@ function getAccessToken() {
|
|
|
82
90
|
}
|
|
83
91
|
return creds.accessToken;
|
|
84
92
|
}
|
|
93
|
+
function getApiblazeDir() {
|
|
94
|
+
return APIBLAZE_DIR;
|
|
95
|
+
}
|
|
85
96
|
var fs, os, path, APIBLAZE_DIR, CREDENTIALS_PATH;
|
|
86
97
|
var init_auth = __esm({
|
|
87
98
|
"src/lib/auth.ts"() {
|
|
@@ -232,6 +243,65 @@ var init_api = __esm({
|
|
|
232
243
|
}
|
|
233
244
|
});
|
|
234
245
|
|
|
246
|
+
// src/lib/team.ts
|
|
247
|
+
var team_exports = {};
|
|
248
|
+
__export(team_exports, {
|
|
249
|
+
resolveLinkedTeam: () => resolveLinkedTeam
|
|
250
|
+
});
|
|
251
|
+
async function resolveLinkedTeam(opts) {
|
|
252
|
+
let teams;
|
|
253
|
+
try {
|
|
254
|
+
teams = await getTeams();
|
|
255
|
+
} catch {
|
|
256
|
+
return opts.preferredId ? { teamId: opts.preferredId } : null;
|
|
257
|
+
}
|
|
258
|
+
if (teams.length === 0) return null;
|
|
259
|
+
const preferred = opts.preferredId ? teams.find((t) => t.teamId === opts.preferredId) : void 0;
|
|
260
|
+
if (preferred && !(opts.promptWhenMultiple && opts.interactive && teams.length > 1)) {
|
|
261
|
+
return { teamId: preferred.teamId, teamName: preferred.name };
|
|
262
|
+
}
|
|
263
|
+
if (preferred) {
|
|
264
|
+
const { default: inquirer3 } = await import("inquirer");
|
|
265
|
+
const { chosen: chosen2 } = await inquirer3.prompt([{
|
|
266
|
+
type: "list",
|
|
267
|
+
name: "chosen",
|
|
268
|
+
message: "Which team do you want to work in?",
|
|
269
|
+
default: preferred.teamId,
|
|
270
|
+
choices: teams.map((t) => ({ name: t.name, value: t.teamId }))
|
|
271
|
+
}]);
|
|
272
|
+
const picked2 = teams.find((t) => t.teamId === chosen2);
|
|
273
|
+
return { teamId: picked2.teamId, teamName: picked2.name };
|
|
274
|
+
}
|
|
275
|
+
if (opts.preferredId) {
|
|
276
|
+
console.log(import_chalk.default.yellow("\nYour previously linked team is no longer available."));
|
|
277
|
+
}
|
|
278
|
+
if (teams.length === 1) {
|
|
279
|
+
console.log(`${import_chalk.default.cyan("\u2192")} Linking to your team ${import_chalk.default.bold(teams[0].name)}.`);
|
|
280
|
+
return { teamId: teams[0].teamId, teamName: teams[0].name };
|
|
281
|
+
}
|
|
282
|
+
if (!opts.interactive) {
|
|
283
|
+
console.log(import_chalk.default.yellow(`Linking to "${teams[0].name}" \u2014 pass --team to choose another.`));
|
|
284
|
+
return { teamId: teams[0].teamId, teamName: teams[0].name };
|
|
285
|
+
}
|
|
286
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
287
|
+
const { chosen } = await inquirer2.prompt([{
|
|
288
|
+
type: "list",
|
|
289
|
+
name: "chosen",
|
|
290
|
+
message: "Which team do you want to link to?",
|
|
291
|
+
choices: teams.map((t) => ({ name: t.name, value: t.teamId }))
|
|
292
|
+
}]);
|
|
293
|
+
const picked = teams.find((t) => t.teamId === chosen);
|
|
294
|
+
return { teamId: picked.teamId, teamName: picked.name };
|
|
295
|
+
}
|
|
296
|
+
var import_chalk;
|
|
297
|
+
var init_team = __esm({
|
|
298
|
+
"src/lib/team.ts"() {
|
|
299
|
+
"use strict";
|
|
300
|
+
import_chalk = __toESM(require("chalk"));
|
|
301
|
+
init_api();
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
|
|
235
305
|
// src/lib/anon-cred.ts
|
|
236
306
|
var anon_cred_exports = {};
|
|
237
307
|
__export(anon_cred_exports, {
|
|
@@ -492,7 +562,7 @@ var import_commander = require("commander");
|
|
|
492
562
|
var import_chalk34 = __toESM(require("chalk"));
|
|
493
563
|
|
|
494
564
|
// package.json
|
|
495
|
-
var version = "0.
|
|
565
|
+
var version = "0.14.0";
|
|
496
566
|
|
|
497
567
|
// src/index.ts
|
|
498
568
|
init_types();
|
|
@@ -501,43 +571,7 @@ init_types();
|
|
|
501
571
|
var import_chalk2 = __toESM(require("chalk"));
|
|
502
572
|
var import_ora = __toESM(require("ora"));
|
|
503
573
|
init_auth();
|
|
504
|
-
|
|
505
|
-
// src/lib/team.ts
|
|
506
|
-
var import_chalk = __toESM(require("chalk"));
|
|
507
|
-
init_api();
|
|
508
|
-
async function resolveLinkedTeam(opts) {
|
|
509
|
-
let teams;
|
|
510
|
-
try {
|
|
511
|
-
teams = await getTeams();
|
|
512
|
-
} catch {
|
|
513
|
-
return opts.preferredId ? { teamId: opts.preferredId } : null;
|
|
514
|
-
}
|
|
515
|
-
if (teams.length === 0) return null;
|
|
516
|
-
const preferred = opts.preferredId ? teams.find((t) => t.teamId === opts.preferredId) : void 0;
|
|
517
|
-
if (preferred) return { teamId: preferred.teamId, teamName: preferred.name };
|
|
518
|
-
if (opts.preferredId) {
|
|
519
|
-
console.log(import_chalk.default.yellow("\nYour previously linked team is no longer available."));
|
|
520
|
-
}
|
|
521
|
-
if (teams.length === 1) {
|
|
522
|
-
console.log(`${import_chalk.default.cyan("\u2192")} Linking to your team ${import_chalk.default.bold(teams[0].name)}.`);
|
|
523
|
-
return { teamId: teams[0].teamId, teamName: teams[0].name };
|
|
524
|
-
}
|
|
525
|
-
if (!opts.interactive) {
|
|
526
|
-
console.log(import_chalk.default.yellow(`Linking to "${teams[0].name}" \u2014 pass --team to choose another.`));
|
|
527
|
-
return { teamId: teams[0].teamId, teamName: teams[0].name };
|
|
528
|
-
}
|
|
529
|
-
const { default: inquirer2 } = await import("inquirer");
|
|
530
|
-
const { chosen } = await inquirer2.prompt([{
|
|
531
|
-
type: "list",
|
|
532
|
-
name: "chosen",
|
|
533
|
-
message: "Which team do you want to link to?",
|
|
534
|
-
choices: teams.map((t) => ({ name: t.name, value: t.teamId }))
|
|
535
|
-
}]);
|
|
536
|
-
const picked = teams.find((t) => t.teamId === chosen);
|
|
537
|
-
return { teamId: picked.teamId, teamName: picked.name };
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
// src/commands/login.ts
|
|
574
|
+
init_team();
|
|
541
575
|
var DASHBOARD_BASE2 = "https://dashboard.apiblaze.com";
|
|
542
576
|
function openBrowser(url) {
|
|
543
577
|
const { exec } = require("child_process");
|
|
@@ -613,7 +647,12 @@ async function runLogin() {
|
|
|
613
647
|
}
|
|
614
648
|
let teamId = defaultTeamId ?? void 0;
|
|
615
649
|
let teamName;
|
|
616
|
-
const linked = await resolveLinkedTeam({
|
|
650
|
+
const linked = await resolveLinkedTeam({
|
|
651
|
+
preferredId: defaultTeamId ?? void 0,
|
|
652
|
+
interactive: !!process.stdin.isTTY,
|
|
653
|
+
// >1 team → ASK (JWT default is just the cursor). One team → link silently.
|
|
654
|
+
promptWhenMultiple: true
|
|
655
|
+
});
|
|
617
656
|
if (linked) {
|
|
618
657
|
teamId = linked.teamId;
|
|
619
658
|
teamName = linked.teamName;
|
|
@@ -838,6 +877,9 @@ function formatCapturedRequest(req, note) {
|
|
|
838
877
|
].join("\n");
|
|
839
878
|
}
|
|
840
879
|
|
|
880
|
+
// src/commands/dev.ts
|
|
881
|
+
init_team();
|
|
882
|
+
|
|
841
883
|
// src/lib/random-name.ts
|
|
842
884
|
var ADJECTIVES = [
|
|
843
885
|
"amber",
|
|
@@ -2729,10 +2771,12 @@ async function runTenantUse(query, opts) {
|
|
|
2729
2771
|
}
|
|
2730
2772
|
async function runTenantList(opts) {
|
|
2731
2773
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
2774
|
+
const limit = opts.limit ?? (opts.q ? "50" : void 0);
|
|
2775
|
+
const qs = `?detail=1${opts.q ? `&q=${encodeURIComponent(opts.q)}` : ""}${limit ? `&limit=${encodeURIComponent(limit)}` : ""}`;
|
|
2732
2776
|
const out = await admin({
|
|
2733
2777
|
method: "GET",
|
|
2734
|
-
path: `/teams/${encodeURIComponent(teamId)}/tenants
|
|
2735
|
-
summary: `List tenants for team ${teamName ?? teamId}`
|
|
2778
|
+
path: `/teams/${encodeURIComponent(teamId)}/tenants${qs}`,
|
|
2779
|
+
summary: opts.q ? `Search tenants matching "${opts.q}"` : `List tenants for team ${teamName ?? teamId}`
|
|
2736
2780
|
});
|
|
2737
2781
|
const tenants = out?.tenants ?? [];
|
|
2738
2782
|
if (opts.json) {
|
|
@@ -2740,7 +2784,7 @@ async function runTenantList(opts) {
|
|
|
2740
2784
|
return;
|
|
2741
2785
|
}
|
|
2742
2786
|
if (!tenants.length) {
|
|
2743
|
-
console.log(import_chalk23.default.yellow("No tenants."));
|
|
2787
|
+
console.log(import_chalk23.default.yellow(opts.q ? `No tenants matching "${opts.q}".` : "No tenants."));
|
|
2744
2788
|
return;
|
|
2745
2789
|
}
|
|
2746
2790
|
for (const t of tenants) {
|
|
@@ -2748,6 +2792,10 @@ async function runTenantList(opts) {
|
|
|
2748
2792
|
const display = typeof t === "string" ? "" : import_chalk23.default.dim(` ${t.display_name ?? ""}`);
|
|
2749
2793
|
console.log(` ${import_chalk23.default.bold(name)}${display}`);
|
|
2750
2794
|
}
|
|
2795
|
+
const total = out?.total ?? tenants.length;
|
|
2796
|
+
if (total > tenants.length) {
|
|
2797
|
+
console.log(import_chalk23.default.dim(` \u2026 showing ${tenants.length} of ${total} \u2014 narrow with --q <search>`));
|
|
2798
|
+
}
|
|
2751
2799
|
}
|
|
2752
2800
|
async function runTenantCreate(opts) {
|
|
2753
2801
|
if (!opts.name) {
|
|
@@ -2852,13 +2900,28 @@ var trailingComma = /\s*,\s*/;
|
|
|
2852
2900
|
var parseList = (s) => s.split(trailingComma).map((x) => x.trim()).filter(Boolean);
|
|
2853
2901
|
async function runTenantManage(query, opts) {
|
|
2854
2902
|
const { teamId } = await resolveTeam(opts.team);
|
|
2855
|
-
const slug = opts.tenant ??
|
|
2903
|
+
const slug = opts.tenant ?? await validScopedTenant(teamId, query) ?? await pickTenant(teamId, { message: "Manage which tenant?", initialQuery: query, allowCreate: true });
|
|
2856
2904
|
if (!slug) return;
|
|
2857
2905
|
await tenantHome(teamId, slug);
|
|
2858
2906
|
}
|
|
2859
|
-
function
|
|
2907
|
+
async function validScopedTenant(teamId, query) {
|
|
2860
2908
|
if (query) return void 0;
|
|
2861
|
-
|
|
2909
|
+
const creds = loadCredentials();
|
|
2910
|
+
const scoped = creds?.activeTenant;
|
|
2911
|
+
if (!scoped) return void 0;
|
|
2912
|
+
const out = await admin({
|
|
2913
|
+
method: "GET",
|
|
2914
|
+
path: `/teams/${encodeURIComponent(teamId)}/tenants?q=${encodeURIComponent(scoped)}&limit=15`,
|
|
2915
|
+
summary: `Verify tenant scope ${scoped}`
|
|
2916
|
+
}).catch(() => null);
|
|
2917
|
+
const names = (out?.tenants ?? []).map((t) => typeof t === "string" ? t : t.tenant_name);
|
|
2918
|
+
if (names.includes(scoped)) return scoped;
|
|
2919
|
+
const { saveCredentials: saveCredentials2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
|
|
2920
|
+
const next = { ...creds };
|
|
2921
|
+
delete next.activeTenant;
|
|
2922
|
+
saveCredentials2(next);
|
|
2923
|
+
console.log(import_chalk24.default.yellow(`Tenant scope "${scoped}" no longer exists in this team \u2014 cleared.`));
|
|
2924
|
+
return void 0;
|
|
2862
2925
|
}
|
|
2863
2926
|
async function tenantHome(teamId, tenant2) {
|
|
2864
2927
|
const { default: inquirer2 } = await import("inquirer");
|
|
@@ -5044,16 +5107,25 @@ async function opCall(call) {
|
|
|
5044
5107
|
function printResidue(report, applied) {
|
|
5045
5108
|
const up = report?.upstash ?? {};
|
|
5046
5109
|
const fga = report?.fga ?? {};
|
|
5110
|
+
const ghosts = report?.ghosts ?? {};
|
|
5047
5111
|
console.log(import_chalk33.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
|
|
5048
5112
|
console.log(import_chalk33.default.bold("\n Upstash"));
|
|
5049
5113
|
const orphans = up.orphans ?? [];
|
|
5050
5114
|
if (orphans.length === 0) console.log(import_chalk33.default.green(" no orphaned keys"));
|
|
5051
5115
|
for (const o of orphans) console.log(` ${import_chalk33.default.yellow(o.key)} ${import_chalk33.default.dim(`\u2014 ${o.reason}`)}`);
|
|
5052
5116
|
console.log(import_chalk33.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
|
|
5117
|
+
if (up.anon_wallet_detail) {
|
|
5118
|
+
const d = up.anon_wallet_detail;
|
|
5119
|
+
console.log(import_chalk33.default.dim(` anon wallets: ${d.count} ($${(d.total_cents / 100).toFixed(2)}), ${d.no_ttl} with NO TTL${d.no_ttl ? " \u26A0" : " (all self-expire)"}`));
|
|
5120
|
+
}
|
|
5121
|
+
if (up.keyspace_census) {
|
|
5122
|
+
const census = Object.entries(up.keyspace_census).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
|
|
5123
|
+
console.log(import_chalk33.default.dim(` keyspace: ${census}`));
|
|
5124
|
+
}
|
|
5053
5125
|
if (up.unknown?.length) console.log(import_chalk33.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
|
|
5054
5126
|
if (applied) console.log(` ${import_chalk33.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
|
|
5055
5127
|
for (const e of up.errors ?? []) console.log(import_chalk33.default.red(` error: ${e}`));
|
|
5056
|
-
console.log(import_chalk33.default.bold("\n OpenFGA / Neon"));
|
|
5128
|
+
console.log(import_chalk33.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
|
|
5057
5129
|
if (applied) {
|
|
5058
5130
|
const swept = fga?.swept ?? [];
|
|
5059
5131
|
if (swept.length === 0) console.log(import_chalk33.default.green(" no orphaned stores"));
|
|
@@ -5063,6 +5135,8 @@ function printResidue(report, applied) {
|
|
|
5063
5135
|
);
|
|
5064
5136
|
}
|
|
5065
5137
|
if (fga?.remaining) console.log(import_chalk33.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
|
|
5138
|
+
const st = fga?.side_tables;
|
|
5139
|
+
if (st) console.log(import_chalk33.default.dim(` Neon side-tables purged: ${st.soft_deleted_stores} store records, ${st.orphan_models} models, ${st.orphan_changelog} changelog rows${st.error ? ` (${st.error})` : ""}`));
|
|
5066
5140
|
} else {
|
|
5067
5141
|
const fgaOrphans = fga?.orphans ?? [];
|
|
5068
5142
|
if (fgaOrphans.length === 0) console.log(import_chalk33.default.green(" no orphaned stores"));
|
|
@@ -5071,8 +5145,26 @@ function printResidue(report, applied) {
|
|
|
5071
5145
|
console.log(` ${import_chalk33.default.yellow(s.store_id)} ${import_chalk33.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
|
|
5072
5146
|
}
|
|
5073
5147
|
console.log(import_chalk33.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
|
|
5148
|
+
const st = fga?.side_tables;
|
|
5149
|
+
if (st) console.log(import_chalk33.default.dim(` Neon side-table residue: ${st.soft_deleted_stores} soft-deleted store records, ${st.orphan_models} orphan models, ${st.orphan_changelog} orphan changelog rows`));
|
|
5074
5150
|
}
|
|
5075
5151
|
for (const e of fga?.errors ?? []) console.log(import_chalk33.default.red(` error: ${e}`));
|
|
5152
|
+
console.log(import_chalk33.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
|
|
5153
|
+
if (applied) {
|
|
5154
|
+
if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk33.default.green(" no ghost tuples"));
|
|
5155
|
+
else console.log(` ${import_chalk33.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk33.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
|
|
5156
|
+
} else {
|
|
5157
|
+
const n = ghosts?.ghost_count ?? 0;
|
|
5158
|
+
if (n === 0) console.log(import_chalk33.default.green(` no ghost tuples ${import_chalk33.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
|
|
5159
|
+
else {
|
|
5160
|
+
console.log(import_chalk33.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
|
|
5161
|
+
for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
|
|
5162
|
+
console.log(import_chalk33.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
|
|
5163
|
+
}
|
|
5164
|
+
if (n > 20) console.log(import_chalk33.default.dim(` \u2026 and ${n - 20} more`));
|
|
5165
|
+
}
|
|
5166
|
+
}
|
|
5167
|
+
for (const e of ghosts?.errors ?? []) console.log(import_chalk33.default.red(` error: ${e}`));
|
|
5076
5168
|
console.log();
|
|
5077
5169
|
}
|
|
5078
5170
|
async function runOp(sub, opts = {}) {
|
|
@@ -5090,8 +5182,9 @@ async function runOp(sub, opts = {}) {
|
|
|
5090
5182
|
console.log(import_chalk33.default.bold("\nOperator menu"));
|
|
5091
5183
|
console.log(` ${import_chalk33.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
|
|
5092
5184
|
console.log(` ${import_chalk33.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk33.default.dim("-y to skip")})`);
|
|
5093
|
-
console.log(` ${import_chalk33.default.cyan("apiblaze op credits")} list credit wallets
|
|
5094
|
-
`)
|
|
5185
|
+
console.log(` ${import_chalk33.default.cyan("apiblaze op credits")} list credit wallets`);
|
|
5186
|
+
console.log(import_chalk33.default.dim(` (to prune all non-CP data: run scripts/nuke-but-cp.sh --apply --sweep in the repo)
|
|
5187
|
+
`));
|
|
5095
5188
|
return;
|
|
5096
5189
|
}
|
|
5097
5190
|
case "residue": {
|
|
@@ -5104,15 +5197,20 @@ async function runOp(sub, opts = {}) {
|
|
|
5104
5197
|
const report = await opCall({ method: "GET", path: "/operator/external-residue", summary: "external residue report" });
|
|
5105
5198
|
const nUp = report?.upstash?.orphans?.length ?? 0;
|
|
5106
5199
|
const nFga = report?.fga?.orphans?.length ?? 0;
|
|
5200
|
+
const nGhost = report?.ghosts?.ghost_count ?? 0;
|
|
5201
|
+
const st = report?.fga?.side_tables ?? {};
|
|
5202
|
+
const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
|
|
5107
5203
|
printResidue(report, false);
|
|
5108
|
-
if (nUp + nFga === 0) {
|
|
5204
|
+
if (nUp + nFga + nGhost + nSide === 0) {
|
|
5109
5205
|
console.log(import_chalk33.default.green("Nothing to sweep."));
|
|
5110
5206
|
return;
|
|
5111
5207
|
}
|
|
5112
5208
|
if (!opts.yes) {
|
|
5113
5209
|
const readline2 = await import("readline/promises");
|
|
5114
5210
|
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
5115
|
-
const answer = await rl.question(
|
|
5211
|
+
const answer = await rl.question(
|
|
5212
|
+
import_chalk33.default.red(`Delete ${nUp} Upstash key(s) + ${nFga} OpenFGA store(s) + ${nGhost} ghost tuple(s) + ${nSide} Neon side-table row(s)? Type 'sweep' to confirm: `)
|
|
5213
|
+
);
|
|
5116
5214
|
rl.close();
|
|
5117
5215
|
if (answer.trim() !== "sweep") return void console.log(import_chalk33.default.dim("Aborted."));
|
|
5118
5216
|
}
|
|
@@ -5139,6 +5237,7 @@ async function runOp(sub, opts = {}) {
|
|
|
5139
5237
|
|
|
5140
5238
|
// src/index.ts
|
|
5141
5239
|
init_trace();
|
|
5240
|
+
init_auth();
|
|
5142
5241
|
var program = new import_commander.Command();
|
|
5143
5242
|
program.name("apiblaze").description("APIblaze CLI \u2014 create & manage API proxies and run dev tunnels").version(version).option("-v, --verbose", "Print the exact series of API calls each command makes (curl-equivalent you could run yourself)");
|
|
5144
5243
|
program.hook("preAction", () => {
|
|
@@ -5152,7 +5251,7 @@ function action(fn) {
|
|
|
5152
5251
|
renderTrace();
|
|
5153
5252
|
} catch (err) {
|
|
5154
5253
|
renderTrace();
|
|
5155
|
-
printError(err);
|
|
5254
|
+
await printError(err);
|
|
5156
5255
|
process.exit(1);
|
|
5157
5256
|
}
|
|
5158
5257
|
};
|
|
@@ -5161,7 +5260,7 @@ program.command("login").description("Authenticate with APIblaze").action(async
|
|
|
5161
5260
|
try {
|
|
5162
5261
|
await runLogin();
|
|
5163
5262
|
} catch (err) {
|
|
5164
|
-
printError(err);
|
|
5263
|
+
await printError(err);
|
|
5165
5264
|
process.exit(1);
|
|
5166
5265
|
}
|
|
5167
5266
|
});
|
|
@@ -5169,7 +5268,7 @@ program.command("create").description("Create a new API proxy (no login needed \
|
|
|
5169
5268
|
try {
|
|
5170
5269
|
await runCreate(opts);
|
|
5171
5270
|
} catch (err) {
|
|
5172
|
-
printError(err);
|
|
5271
|
+
await printError(err);
|
|
5173
5272
|
process.exit(1);
|
|
5174
5273
|
}
|
|
5175
5274
|
});
|
|
@@ -5194,7 +5293,7 @@ program.command("dev").description("Put your localhost behind a public URL (dev
|
|
|
5194
5293
|
}
|
|
5195
5294
|
await runDev({ port: resolved, captureFile: opts.captureFile });
|
|
5196
5295
|
} catch (err) {
|
|
5197
|
-
printError(err);
|
|
5296
|
+
await printError(err);
|
|
5198
5297
|
process.exit(1);
|
|
5199
5298
|
}
|
|
5200
5299
|
});
|
|
@@ -5207,7 +5306,7 @@ program.command("whoami").description("Show who you are \u2014 both API Producer
|
|
|
5207
5306
|
try {
|
|
5208
5307
|
await runWhoami(opts);
|
|
5209
5308
|
} catch (err) {
|
|
5210
|
-
printError(err);
|
|
5309
|
+
await printError(err);
|
|
5211
5310
|
process.exit(1);
|
|
5212
5311
|
}
|
|
5213
5312
|
});
|
|
@@ -5215,7 +5314,7 @@ program.command("claim").description("Claim your anonymous workspace into your a
|
|
|
5215
5314
|
try {
|
|
5216
5315
|
await runClaim(code, opts);
|
|
5217
5316
|
} catch (err) {
|
|
5218
|
-
printError(err);
|
|
5317
|
+
await printError(err);
|
|
5219
5318
|
process.exit(1);
|
|
5220
5319
|
}
|
|
5221
5320
|
});
|
|
@@ -5223,7 +5322,7 @@ program.command("team").description("Switch the active team").argument("[team]",
|
|
|
5223
5322
|
try {
|
|
5224
5323
|
await runTeam(team);
|
|
5225
5324
|
} catch (err) {
|
|
5226
|
-
printError(err);
|
|
5325
|
+
await printError(err);
|
|
5227
5326
|
process.exit(1);
|
|
5228
5327
|
}
|
|
5229
5328
|
});
|
|
@@ -5231,7 +5330,7 @@ program.command("projects").description("List the projects in your team").action
|
|
|
5231
5330
|
try {
|
|
5232
5331
|
await runProjects();
|
|
5233
5332
|
} catch (err) {
|
|
5234
|
-
printError(err);
|
|
5333
|
+
await printError(err);
|
|
5235
5334
|
process.exit(1);
|
|
5236
5335
|
}
|
|
5237
5336
|
});
|
|
@@ -5247,10 +5346,10 @@ domain.command("list").description("List custom domains for a proxy").argument("
|
|
|
5247
5346
|
domain.command("status").description("Check a custom domain's validation status").argument("<project>", "Project name or id").requiredOption("--id <domainId>", "Domain id (see `domain list`)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runDomainStatus(project, opts)));
|
|
5248
5347
|
domain.command("rm").description("Remove a custom domain").argument("<project>", "Project name or id").requiredOption("--id <domainId>", "Domain id (see `domain list`)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").action(action((project, opts) => runDomainRemove(project, opts)));
|
|
5249
5348
|
domain.command("set-base").description("Choose which version/environment your main URL serves").argument("<project>", "Project name or id").option("--env <env>", "Environment (default: prod)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").action(action((project, opts) => runDomainSetBase(project, opts)));
|
|
5250
|
-
var tenant = program.command("tenant").description("Manage tenants \u2014
|
|
5349
|
+
var tenant = program.command("tenant").description("Manage tenants \u2014 bare command opens the interactive picker (settings, app clients, providers)").action(action(() => runTenantManage(void 0, {})));
|
|
5251
5350
|
tenant.command("manage").description("Browse & edit one tenant: settings, login app clients, providers, issuers (search-first picker)").argument("[query]", "Search by tenant name/display name (omit to use your tenant scope or pick)").option("--tenant <slug>", "Exact tenant slug (skips the picker)").option("--team <id|name>", "Team (defaults to active team)").action(action((query, opts) => runTenantManage(query, opts)));
|
|
5252
5351
|
tenant.command("use").description("Set the sticky tenant scope for future commands (search-first; --clear to unset)").argument("[query]", "Search by tenant name/display name").option("--clear", "Clear the tenant scope").option("--team <id|name>", "Team (defaults to active team)").action(action((query, opts) => runTenantUse(query, opts)));
|
|
5253
|
-
tenant.command("list").description("List tenants in your team").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runTenantList(opts)));
|
|
5352
|
+
tenant.command("list").description("List tenants in your team (--q searches server-side)").option("--q <search>", "Filter by tenant name or display name").option("--limit <n>", "Page size (max 200)").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runTenantList(opts)));
|
|
5254
5353
|
tenant.command("create").description("Create a tenant in your team").requiredOption("--name <display>", "Display name").option("--slug <tenant_name>", "Explicit tenant slug (generated if omitted)").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runTenantCreate(opts)));
|
|
5255
5354
|
tenant.command("attach").description("Attach a tenant to a proxy").argument("<project>", "Project name or id").requiredOption("--tenant <slug>", "Tenant slug to attach").option("--auth-config <id>", "Auth config id to bind").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runTenantAttach(project, opts)));
|
|
5256
5355
|
tenant.command("delete").description("Delete a tenant (full cascade)").argument("<slug>", "Tenant slug to delete").option("--team <id|name>", "Team (defaults to active team)").option("-y, --yes", "Skip the confirmation prompt").action(action((slug, opts) => runTenantDelete(slug, opts)));
|
|
@@ -5313,10 +5412,33 @@ Examples:
|
|
|
5313
5412
|
$ npx apiblaze throttle myapi --rate 50 --verbose # configure + show the API call
|
|
5314
5413
|
$ npx apiblaze consumer login # act as a consumer of your API
|
|
5315
5414
|
`);
|
|
5316
|
-
function
|
|
5415
|
+
async function recoverStaleTeam() {
|
|
5416
|
+
try {
|
|
5417
|
+
const creds = loadCredentials();
|
|
5418
|
+
if (!creds?.teamId) return;
|
|
5419
|
+
const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
|
|
5420
|
+
const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
|
|
5421
|
+
if (!linked) {
|
|
5422
|
+
console.error(import_chalk34.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
|
|
5423
|
+
return;
|
|
5424
|
+
}
|
|
5425
|
+
if (linked.teamId === creds.teamId) return;
|
|
5426
|
+
const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
|
|
5427
|
+
delete next.activeTenant;
|
|
5428
|
+
saveCredentials(next);
|
|
5429
|
+
console.error(import_chalk34.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk34.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
|
|
5430
|
+
} catch {
|
|
5431
|
+
}
|
|
5432
|
+
}
|
|
5433
|
+
async function printError(err) {
|
|
5317
5434
|
if (err instanceof ApiError) {
|
|
5435
|
+
const data = err.body;
|
|
5436
|
+
const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
|
|
5318
5437
|
console.error(import_chalk34.default.red(`
|
|
5319
|
-
API error (${err.status}): ${err.message}`));
|
|
5438
|
+
API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
|
|
5439
|
+
if (err.status === 403 || err.status === 404) {
|
|
5440
|
+
await recoverStaleTeam();
|
|
5441
|
+
}
|
|
5320
5442
|
} else if (err instanceof Error) {
|
|
5321
5443
|
console.error(import_chalk34.default.red(`
|
|
5322
5444
|
Error: ${err.message}`));
|
package/package.json
CHANGED