@nebulr-group/bridge-cli 0.1.4 → 0.4.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -4
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/app.command.d.ts.map +1 -1
- package/dist/commands/app.command.js +2 -0
- package/dist/commands/app.command.js.map +1 -1
- package/dist/commands/flag-init.command.d.ts +13 -0
- package/dist/commands/flag-init.command.d.ts.map +1 -0
- package/dist/commands/flag-init.command.js +353 -0
- package/dist/commands/flag-init.command.js.map +1 -0
- package/dist/commands/flag.command.d.ts +92 -0
- package/dist/commands/flag.command.d.ts.map +1 -1
- package/dist/commands/flag.command.js +784 -25
- package/dist/commands/flag.command.js.map +1 -1
- package/dist/commands/guide.command.d.ts +21 -0
- package/dist/commands/guide.command.d.ts.map +1 -1
- package/dist/commands/guide.command.js +297 -21
- package/dist/commands/guide.command.js.map +1 -1
- package/dist/commands/integrate.command.js +3 -3
- package/dist/commands/integrate.command.js.map +1 -1
- package/dist/commands/ops.command.d.ts +17 -0
- package/dist/commands/ops.command.d.ts.map +1 -0
- package/dist/commands/ops.command.js +129 -0
- package/dist/commands/ops.command.js.map +1 -0
- package/dist/commands/plan.command.d.ts +31 -0
- package/dist/commands/plan.command.d.ts.map +1 -1
- package/dist/commands/plan.command.js +182 -1
- package/dist/commands/plan.command.js.map +1 -1
- package/dist/commands/stripe.command.d.ts +3 -0
- package/dist/commands/stripe.command.d.ts.map +1 -0
- package/dist/commands/stripe.command.js +43 -0
- package/dist/commands/stripe.command.js.map +1 -0
- package/dist/output.d.ts +8 -0
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +10 -0
- package/dist/output.js.map +1 -1
- package/dist/prompts/architecture.md +560 -0
- package/dist/prompts/auth-master-integration-prompt.md +251 -0
- package/dist/prompts/billing/master.md +269 -0
- package/dist/prompts/flags/master.md +215 -0
- package/dist/prompts/integration-success.md +107 -0
- package/package.json +4 -3
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { DEFAULT_BASE_URL } from '../config.js';
|
|
2
|
+
import { isExpired, readCredentials } from '../credentials.js';
|
|
3
|
+
import { outputError, outputSuccess } from '../output.js';
|
|
4
|
+
export function registerOpsCommands(program) {
|
|
5
|
+
const ops = program
|
|
6
|
+
.command('ops')
|
|
7
|
+
.description('Operator / platform-internal commands');
|
|
8
|
+
ops
|
|
9
|
+
.command('reconcile')
|
|
10
|
+
.description('Trigger Bridge↔Stripe usage reconciliation for a workspace (Billing 2.0 US-18). ' +
|
|
11
|
+
'Diffs Bridge raw events against Stripe usage records and replays the missing ones.')
|
|
12
|
+
.requiredOption('--workspace <id>', 'Workspace (tenant) id to reconcile')
|
|
13
|
+
.option('--metric <metric>', 'Scope to one metric instead of all metered metrics')
|
|
14
|
+
.option('--json', 'Emit raw JSON instead of the human-readable table')
|
|
15
|
+
.action(async (opts) => {
|
|
16
|
+
try {
|
|
17
|
+
const result = await callReconcile(opts.workspace, opts.metric);
|
|
18
|
+
if (opts.json) {
|
|
19
|
+
outputSuccess(result);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
// Default: render a friendly table; still go through outputSuccess so
|
|
23
|
+
// the success envelope + exit code stay consistent with the rest of the
|
|
24
|
+
// CLI. The pretty table is appended to stderr so machine parsers that
|
|
25
|
+
// only watch stdout get the JSON.
|
|
26
|
+
printReconciliationTable(result);
|
|
27
|
+
outputSuccess(result);
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
outputError(err);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
async function callReconcile(workspaceId, metric) {
|
|
35
|
+
const { apiKey, baseUrl } = resolveCredentials();
|
|
36
|
+
const path = metric
|
|
37
|
+
? `/v1/admin/reconcile/${encodeURIComponent(workspaceId)}/${encodeURIComponent(metric)}`
|
|
38
|
+
: `/v1/admin/reconcile/${encodeURIComponent(workspaceId)}`;
|
|
39
|
+
const url = `${baseUrl.replace(/\/$/, '')}${path}`;
|
|
40
|
+
const res = await fetch(url, {
|
|
41
|
+
method: 'POST',
|
|
42
|
+
headers: {
|
|
43
|
+
'content-type': 'application/json',
|
|
44
|
+
'x-api-key': apiKey,
|
|
45
|
+
},
|
|
46
|
+
body: '{}',
|
|
47
|
+
});
|
|
48
|
+
if (!res.ok) {
|
|
49
|
+
let body;
|
|
50
|
+
try {
|
|
51
|
+
body = await res.json();
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
body = await res.text().catch(() => '');
|
|
55
|
+
}
|
|
56
|
+
const message = (body && typeof body === 'object' && 'message' in body
|
|
57
|
+
? String(body.message)
|
|
58
|
+
: '') || `HTTP ${res.status}`;
|
|
59
|
+
const err = new Error(message);
|
|
60
|
+
err.status = res.status;
|
|
61
|
+
err.body = body;
|
|
62
|
+
throw err;
|
|
63
|
+
}
|
|
64
|
+
return (await res.json());
|
|
65
|
+
}
|
|
66
|
+
function resolveCredentials() {
|
|
67
|
+
const envKey = process.env.BRIDGE_API_KEY?.trim();
|
|
68
|
+
const envBase = process.env.BRIDGE_BASE_URL?.trim();
|
|
69
|
+
const creds = safeReadCredentials();
|
|
70
|
+
if (creds && !isExpired(creds)) {
|
|
71
|
+
return {
|
|
72
|
+
apiKey: creds.apiKey,
|
|
73
|
+
baseUrl: envBase && envBase.length > 0 ? envBase : creds.baseUrl,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (envKey && envKey.length > 0) {
|
|
77
|
+
return {
|
|
78
|
+
apiKey: envKey,
|
|
79
|
+
baseUrl: envBase && envBase.length > 0 ? envBase : DEFAULT_BASE_URL,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
throw new Error('Not logged in. Run `bridge auth login`.');
|
|
83
|
+
}
|
|
84
|
+
function safeReadCredentials() {
|
|
85
|
+
try {
|
|
86
|
+
return readCredentials();
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function printReconciliationTable(result) {
|
|
93
|
+
const lines = [];
|
|
94
|
+
lines.push('');
|
|
95
|
+
lines.push(`workspace=${result.workspaceId} app=${result.appId}`);
|
|
96
|
+
if (result.metrics.length === 0) {
|
|
97
|
+
lines.push(' (no metered metrics reconciled)');
|
|
98
|
+
process.stderr.write(lines.join('\n') + '\n');
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const header = [
|
|
102
|
+
'metric'.padEnd(28),
|
|
103
|
+
'status'.padEnd(14),
|
|
104
|
+
'bridge'.padStart(7),
|
|
105
|
+
'provider'.padStart(9),
|
|
106
|
+
'missing'.padStart(8),
|
|
107
|
+
'healed'.padStart(7),
|
|
108
|
+
'attempts'.padStart(9),
|
|
109
|
+
].join(' ');
|
|
110
|
+
lines.push(header);
|
|
111
|
+
lines.push('-'.repeat(header.length));
|
|
112
|
+
for (const m of result.metrics) {
|
|
113
|
+
lines.push([
|
|
114
|
+
m.metric.padEnd(28).slice(0, 28),
|
|
115
|
+
m.status.padEnd(14),
|
|
116
|
+
String(m.bridgeCount).padStart(7),
|
|
117
|
+
String(m.providerCount).padStart(9),
|
|
118
|
+
String(m.missing).padStart(8),
|
|
119
|
+
String(m.healed).padStart(7),
|
|
120
|
+
String(m.attempts).padStart(9),
|
|
121
|
+
].join(' '));
|
|
122
|
+
if (m.failed.length > 0) {
|
|
123
|
+
lines.push(` failed: ${m.failed.slice(0, 3).join('; ')}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
lines.push('');
|
|
127
|
+
process.stderr.write(lines.join('\n') + '\n');
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=ops.command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ops.command.js","sourceRoot":"","sources":["../../src/commands/ops.command.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,34 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
export type QuotaPolicy = 'hard' | 'metered';
|
|
3
|
+
export interface Quota {
|
|
4
|
+
metric: string;
|
|
5
|
+
limit: number;
|
|
6
|
+
policy: QuotaPolicy;
|
|
7
|
+
}
|
|
8
|
+
export declare function validateQuotaEntry(entry: {
|
|
9
|
+
metric?: string;
|
|
10
|
+
limit?: number;
|
|
11
|
+
policy?: string;
|
|
12
|
+
}): Quota;
|
|
13
|
+
/** Upsert a quota by metric (replace if present, append otherwise). Pure. */
|
|
14
|
+
export declare function upsertQuota(quotas: Quota[], entry: Quota): Quota[];
|
|
15
|
+
export type RecurrenceInterval = 'day' | 'week' | 'month' | 'year';
|
|
16
|
+
export interface PlanPriceInput {
|
|
17
|
+
currency: string;
|
|
18
|
+
recurrenceInterval: RecurrenceInterval;
|
|
19
|
+
amount: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Validate + normalise one price entry from CLI flags. A plan can carry several
|
|
23
|
+
* prices (e.g. monthly + yearly); each is set independently and identified by
|
|
24
|
+
* its currency + interval. `--interval` is always required.
|
|
25
|
+
*/
|
|
26
|
+
export declare function buildPriceEntry(opts: {
|
|
27
|
+
amount?: number;
|
|
28
|
+
currency?: string;
|
|
29
|
+
interval?: string;
|
|
30
|
+
}): PlanPriceInput;
|
|
31
|
+
/** Upsert a price by (currency + interval): replace if present, append otherwise. Pure. */
|
|
32
|
+
export declare function upsertPrice(prices: PlanPriceInput[], entry: PlanPriceInput): PlanPriceInput[];
|
|
2
33
|
export declare function registerPlanCommands(program: Command): void;
|
|
3
34
|
//# sourceMappingURL=plan.command.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan.command.d.ts","sourceRoot":"","sources":["../../src/commands/plan.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"plan.command.d.ts","sourceRoot":"","sources":["../../src/commands/plan.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,CAAC;AAE7C,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,WAAW,CAAC;CACrB;AAID,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,KAAK,CAsBR;AAED,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,GAAG,KAAK,EAAE,CAElE;AAID,MAAM,MAAM,kBAAkB,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAQnE,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,cAAc,CAcjB;AAED,2FAA2F;AAC3F,wBAAgB,WAAW,CACzB,MAAM,EAAE,cAAc,EAAE,EACxB,KAAK,EAAE,cAAc,GACpB,cAAc,EAAE,CAWlB;AAcD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAyD3D"}
|
|
@@ -1,5 +1,70 @@
|
|
|
1
1
|
import { getManagementClient } from '../config.js';
|
|
2
2
|
import { outputSuccess, outputError } from '../output.js';
|
|
3
|
+
const QUOTA_POLICIES = ['hard', 'metered'];
|
|
4
|
+
export function validateQuotaEntry(entry) {
|
|
5
|
+
const metric = (entry.metric ?? '').trim();
|
|
6
|
+
if (!metric)
|
|
7
|
+
throw new Error('--metric is required and must be non-empty.');
|
|
8
|
+
const limit = entry.limit;
|
|
9
|
+
if (typeof limit !== 'number' ||
|
|
10
|
+
!Number.isFinite(limit) ||
|
|
11
|
+
!Number.isInteger(limit) ||
|
|
12
|
+
limit < 0) {
|
|
13
|
+
throw new Error(`--limit must be an integer >= 0, got "${entry.limit}".`);
|
|
14
|
+
}
|
|
15
|
+
const policy = entry.policy;
|
|
16
|
+
if (!QUOTA_POLICIES.includes(policy)) {
|
|
17
|
+
throw new Error(`--policy must be one of ${QUOTA_POLICIES.join(', ')}, got "${entry.policy}".`);
|
|
18
|
+
}
|
|
19
|
+
return { metric, limit, policy };
|
|
20
|
+
}
|
|
21
|
+
/** Upsert a quota by metric (replace if present, append otherwise). Pure. */
|
|
22
|
+
export function upsertQuota(quotas, entry) {
|
|
23
|
+
return [...quotas.filter((q) => q.metric !== entry.metric), entry];
|
|
24
|
+
}
|
|
25
|
+
const VALID_INTERVALS = [
|
|
26
|
+
'day',
|
|
27
|
+
'week',
|
|
28
|
+
'month',
|
|
29
|
+
'year',
|
|
30
|
+
];
|
|
31
|
+
/**
|
|
32
|
+
* Validate + normalise one price entry from CLI flags. A plan can carry several
|
|
33
|
+
* prices (e.g. monthly + yearly); each is set independently and identified by
|
|
34
|
+
* its currency + interval. `--interval` is always required.
|
|
35
|
+
*/
|
|
36
|
+
export function buildPriceEntry(opts) {
|
|
37
|
+
const amount = opts.amount;
|
|
38
|
+
if (typeof amount !== 'number' || !Number.isFinite(amount) || amount < 0) {
|
|
39
|
+
throw new Error(`--amount must be a number >= 0, got "${opts.amount}".`);
|
|
40
|
+
}
|
|
41
|
+
const interval = (opts.interval ?? '');
|
|
42
|
+
if (!VALID_INTERVALS.includes(interval)) {
|
|
43
|
+
throw new Error(`--interval is required and must be one of ${VALID_INTERVALS.join(', ')}, got "${opts.interval}".`);
|
|
44
|
+
}
|
|
45
|
+
const currency = (opts.currency ?? 'USD').trim().toUpperCase();
|
|
46
|
+
if (!currency)
|
|
47
|
+
throw new Error('--currency must be non-empty.');
|
|
48
|
+
return { amount, currency, recurrenceInterval: interval };
|
|
49
|
+
}
|
|
50
|
+
/** Upsert a price by (currency + interval): replace if present, append otherwise. Pure. */
|
|
51
|
+
export function upsertPrice(prices, entry) {
|
|
52
|
+
return [
|
|
53
|
+
...prices.filter((p) => !(p.currency === entry.currency &&
|
|
54
|
+
p.recurrenceInterval === entry.recurrenceInterval)),
|
|
55
|
+
entry,
|
|
56
|
+
];
|
|
57
|
+
}
|
|
58
|
+
/** Fetch a plan + its quotas/prices (read defensively — list() may omit them). */
|
|
59
|
+
async function getPlan(key) {
|
|
60
|
+
const plans = (await getManagementClient().plans.list());
|
|
61
|
+
const plan = plans.find((p) => p.key === key);
|
|
62
|
+
if (!plan)
|
|
63
|
+
throw new Error(`Plan not found: ${key}`);
|
|
64
|
+
const quotas = (plan.quotas ?? []);
|
|
65
|
+
const prices = (plan.prices ?? []);
|
|
66
|
+
return { plan, quotas, prices };
|
|
67
|
+
}
|
|
3
68
|
export function registerPlanCommands(program) {
|
|
4
69
|
const plan = program.command('plan').description('Manage subscription plans');
|
|
5
70
|
plan.command('list')
|
|
@@ -12,8 +77,20 @@ export function registerPlanCommands(program) {
|
|
|
12
77
|
outputError(err);
|
|
13
78
|
}
|
|
14
79
|
});
|
|
80
|
+
plan.command('get')
|
|
81
|
+
.description('Get a plan by key (includes usage quotas)')
|
|
82
|
+
.argument('<key>', 'Plan key')
|
|
83
|
+
.action(async (key) => {
|
|
84
|
+
try {
|
|
85
|
+
const { plan: found, quotas } = await getPlan(key);
|
|
86
|
+
outputSuccess({ ...found, quotas });
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
outputError(err);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
15
92
|
plan.command('create')
|
|
16
|
-
.description('Create a new plan')
|
|
93
|
+
.description('Create a new plan (no prices — add them with `plan price set`)')
|
|
17
94
|
.requiredOption('--key <key>', 'Plan key')
|
|
18
95
|
.requiredOption('--name <name>', 'Plan name')
|
|
19
96
|
.option('--description <desc>', 'Description')
|
|
@@ -27,6 +104,9 @@ export function registerPlanCommands(program) {
|
|
|
27
104
|
description: opts.description,
|
|
28
105
|
trial: opts.trial,
|
|
29
106
|
trialDays: opts.trialDays,
|
|
107
|
+
// Start with no prices so the server doesn't apply its default
|
|
108
|
+
// placeholder price; add prices explicitly via `plan price set`.
|
|
109
|
+
prices: [],
|
|
30
110
|
}));
|
|
31
111
|
}
|
|
32
112
|
catch (err) {
|
|
@@ -48,5 +128,106 @@ export function registerPlanCommands(program) {
|
|
|
48
128
|
outputError(err);
|
|
49
129
|
}
|
|
50
130
|
});
|
|
131
|
+
registerPriceCommands(plan);
|
|
132
|
+
registerQuotaCommands(plan);
|
|
133
|
+
}
|
|
134
|
+
// ── plan price (recurring prices) ────────────────────────────────────────────
|
|
135
|
+
function registerPriceCommands(plan) {
|
|
136
|
+
const price = plan
|
|
137
|
+
.command('price')
|
|
138
|
+
.description("Manage a plan's recurring prices (one per currency + interval)");
|
|
139
|
+
price.command('set')
|
|
140
|
+
.description('Add or update a price on a plan (idempotent by currency + interval)')
|
|
141
|
+
.argument('<key>', 'Plan key')
|
|
142
|
+
.requiredOption('--amount <amount>', 'Price amount (>= 0)', parseFloat)
|
|
143
|
+
.requiredOption('--interval <interval>', `Billing interval: ${VALID_INTERVALS.join(' | ')}`)
|
|
144
|
+
.option('--currency <currency>', 'Currency (default USD)', 'USD')
|
|
145
|
+
.action(async (key, opts) => {
|
|
146
|
+
try {
|
|
147
|
+
const entry = buildPriceEntry({
|
|
148
|
+
amount: opts.amount,
|
|
149
|
+
interval: opts.interval,
|
|
150
|
+
currency: opts.currency,
|
|
151
|
+
});
|
|
152
|
+
const { prices } = await getPlan(key);
|
|
153
|
+
const next = upsertPrice(prices, entry);
|
|
154
|
+
outputSuccess(await getManagementClient().plans.update(key, { prices: next }));
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
outputError(err);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
price.command('rm')
|
|
161
|
+
.description('Remove a price from a plan by interval (+ currency)')
|
|
162
|
+
.argument('<key>', 'Plan key')
|
|
163
|
+
.requiredOption('--interval <interval>', `Billing interval: ${VALID_INTERVALS.join(' | ')}`)
|
|
164
|
+
.option('--currency <currency>', 'Currency (default USD)', 'USD')
|
|
165
|
+
.action(async (key, opts) => {
|
|
166
|
+
try {
|
|
167
|
+
const currency = String(opts.currency ?? 'USD').trim().toUpperCase();
|
|
168
|
+
const interval = opts.interval;
|
|
169
|
+
const { prices } = await getPlan(key);
|
|
170
|
+
const next = prices.filter((p) => !(p.currency === currency && p.recurrenceInterval === interval));
|
|
171
|
+
if (next.length === prices.length) {
|
|
172
|
+
throw new Error(`No ${currency} ${interval} price on plan "${key}".`);
|
|
173
|
+
}
|
|
174
|
+
outputSuccess(await getManagementClient().plans.update(key, { prices: next }));
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
outputError(err);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
// ── plan quota (usage caps) ──────────────────────────────────────────────────
|
|
182
|
+
function registerQuotaCommands(plan) {
|
|
183
|
+
const quota = plan
|
|
184
|
+
.command('quota')
|
|
185
|
+
.description('Manage a plan\'s usage quotas (hard | metered caps)');
|
|
186
|
+
quota.command('list')
|
|
187
|
+
.description('List the usage quotas on a plan')
|
|
188
|
+
.argument('<key>', 'Plan key')
|
|
189
|
+
.action(async (key) => {
|
|
190
|
+
try {
|
|
191
|
+
const { quotas } = await getPlan(key);
|
|
192
|
+
outputSuccess(quotas);
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
outputError(err);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
quota.command('set')
|
|
199
|
+
.description('Add or update a usage quota on a plan')
|
|
200
|
+
.argument('<key>', 'Plan key')
|
|
201
|
+
.requiredOption('--metric <metric>', 'Metric key (e.g. num.clicks)')
|
|
202
|
+
.requiredOption('--limit <n>', 'Limit (integer >= 0)', parseInt)
|
|
203
|
+
.requiredOption('--policy <policy>', `Cap policy: ${QUOTA_POLICIES.join(' | ')}`)
|
|
204
|
+
.action(async (key, opts) => {
|
|
205
|
+
try {
|
|
206
|
+
const entry = validateQuotaEntry({ metric: opts.metric, limit: opts.limit, policy: opts.policy });
|
|
207
|
+
const { quotas } = await getPlan(key);
|
|
208
|
+
const next = upsertQuota(quotas, entry);
|
|
209
|
+
outputSuccess(await getManagementClient().plans.update(key, { quotas: next }));
|
|
210
|
+
}
|
|
211
|
+
catch (err) {
|
|
212
|
+
outputError(err);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
quota.command('rm')
|
|
216
|
+
.description('Remove a usage quota from a plan')
|
|
217
|
+
.argument('<key>', 'Plan key')
|
|
218
|
+
.requiredOption('--metric <metric>', 'Metric key to remove')
|
|
219
|
+
.action(async (key, opts) => {
|
|
220
|
+
try {
|
|
221
|
+
const { quotas } = await getPlan(key);
|
|
222
|
+
const next = quotas.filter((q) => q.metric !== opts.metric);
|
|
223
|
+
if (next.length === quotas.length) {
|
|
224
|
+
throw new Error(`No quota for metric "${opts.metric}" on plan "${key}".`);
|
|
225
|
+
}
|
|
226
|
+
outputSuccess(await getManagementClient().plans.update(key, { quotas: next }));
|
|
227
|
+
}
|
|
228
|
+
catch (err) {
|
|
229
|
+
outputError(err);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
51
232
|
}
|
|
52
233
|
//# sourceMappingURL=plan.command.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan.command.js","sourceRoot":"","sources":["../../src/commands/plan.command.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"plan.command.js","sourceRoot":"","sources":["../../src/commands/plan.command.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stripe.command.d.ts","sourceRoot":"","sources":["../../src/commands/stripe.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAmC7D"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { getManagementClient } from '../config.js';
|
|
2
|
+
import { outputSuccess, outputError } from '../output.js';
|
|
3
|
+
export function registerStripeCommands(program) {
|
|
4
|
+
const stripe = program.command('stripe').description('Manage Stripe integration');
|
|
5
|
+
stripe.command('status')
|
|
6
|
+
.description('Show whether Stripe credentials are configured')
|
|
7
|
+
.action(async () => {
|
|
8
|
+
try {
|
|
9
|
+
const state = await getManagementClient().app.getCredentialsState();
|
|
10
|
+
outputSuccess({
|
|
11
|
+
connected: state.hasStripeCredentials,
|
|
12
|
+
message: state.hasStripeCredentials
|
|
13
|
+
? 'Stripe is connected'
|
|
14
|
+
: 'Stripe is not connected — run: bridge stripe connect --secret-key sk_... --publishable-key pk_...',
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
outputError(err);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
stripe.command('connect')
|
|
22
|
+
.description('Set Stripe API credentials')
|
|
23
|
+
.requiredOption('--secret-key <key>', 'Stripe secret key (sk_live_... or sk_test_...)')
|
|
24
|
+
.requiredOption('--publishable-key <key>', 'Stripe publishable key (pk_live_... or pk_test_...)')
|
|
25
|
+
.action(async (opts) => {
|
|
26
|
+
try {
|
|
27
|
+
const state = await getManagementClient().app.updateCredentials({
|
|
28
|
+
stripeSecretKey: opts.secretKey,
|
|
29
|
+
stripePublicKey: opts.publishableKey,
|
|
30
|
+
});
|
|
31
|
+
outputSuccess({
|
|
32
|
+
connected: state.hasStripeCredentials,
|
|
33
|
+
message: state.hasStripeCredentials
|
|
34
|
+
? 'Stripe connected successfully'
|
|
35
|
+
: 'Credentials saved but Stripe did not confirm — check your keys and try again',
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
outputError(err);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=stripe.command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stripe.command.js","sourceRoot":"","sources":["../../src/commands/stripe.command.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE1D,MAAM,UAAU,sBAAsB,CAAC,OAAgB;IACrD,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,2BAA2B,CAAC,CAAC;IAElF,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;SACrB,WAAW,CAAC,gDAAgD,CAAC;SAC7D,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,mBAAmB,EAAE,CAAC,GAAG,CAAC,mBAAmB,EAAE,CAAC;YACpE,aAAa,CAAC;gBACZ,SAAS,EAAE,KAAK,CAAC,oBAAoB;gBACrC,OAAO,EAAE,KAAK,CAAC,oBAAoB;oBACjC,CAAC,CAAC,qBAAqB;oBACvB,CAAC,CAAC,mGAAmG;aACxG,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEL,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC;SACtB,WAAW,CAAC,4BAA4B,CAAC;SACzC,cAAc,CAAC,oBAAoB,EAAE,gDAAgD,CAAC;SACtF,cAAc,CAAC,yBAAyB,EAAE,qDAAqD,CAAC;SAChG,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,mBAAmB,EAAE,CAAC,GAAG,CAAC,iBAAiB,CAAC;gBAC9D,eAAe,EAAE,IAAI,CAAC,SAAS;gBAC/B,eAAe,EAAE,IAAI,CAAC,cAAc;aACrC,CAAC,CAAC;YACH,aAAa,CAAC;gBACZ,SAAS,EAAE,KAAK,CAAC,oBAAoB;gBACrC,OAAO,EAAE,KAAK,CAAC,oBAAoB;oBACjC,CAAC,CAAC,+BAA+B;oBACjC,CAAC,CAAC,8EAA8E;aACnF,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QAAC,CAAC;IACrC,CAAC,CAAC,CAAC;AACP,CAAC"}
|
package/dist/output.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
export declare function outputSuccess(data: unknown): void;
|
|
2
|
+
/**
|
|
3
|
+
* Emit a prompt body verbatim to stdout — used by `bridge guide …` commands
|
|
4
|
+
* whose output is intended to be read by a human or pasted to an AI agent.
|
|
5
|
+
* Wrapping the markdown in a JSON envelope makes it unreadable in a terminal
|
|
6
|
+
* and forces scripts to parse JSON to recover the original text. Use the
|
|
7
|
+
* `--json` flag on each guide command if the envelope is needed for tooling.
|
|
8
|
+
*/
|
|
9
|
+
export declare function outputPrompt(content: string): void;
|
|
2
10
|
export declare function outputError(error: unknown): void;
|
|
3
11
|
//# sourceMappingURL=output.d.ts.map
|
package/dist/output.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAGA,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAGjD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CA2BhD"}
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAGA,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAGjD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAElD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CA2BhD"}
|
package/dist/output.js
CHANGED
|
@@ -4,6 +4,16 @@ export function outputSuccess(data) {
|
|
|
4
4
|
const output = { success: true, data };
|
|
5
5
|
process.stdout.write(JSON.stringify(output, null, 2) + '\n');
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* Emit a prompt body verbatim to stdout — used by `bridge guide …` commands
|
|
9
|
+
* whose output is intended to be read by a human or pasted to an AI agent.
|
|
10
|
+
* Wrapping the markdown in a JSON envelope makes it unreadable in a terminal
|
|
11
|
+
* and forces scripts to parse JSON to recover the original text. Use the
|
|
12
|
+
* `--json` flag on each guide command if the envelope is needed for tooling.
|
|
13
|
+
*/
|
|
14
|
+
export function outputPrompt(content) {
|
|
15
|
+
process.stdout.write(content.endsWith('\n') ? content : content + '\n');
|
|
16
|
+
}
|
|
7
17
|
export function outputError(error) {
|
|
8
18
|
let code = 'UNKNOWN_ERROR';
|
|
9
19
|
let message = 'An unknown error occurred';
|
package/dist/output.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,UAAU,aAAa,CAAC,IAAa;IACzC,MAAM,MAAM,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACvC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,IAAI,GAAG,eAAe,CAAC;IAC3B,IAAI,OAAO,GAAG,2BAA2B,CAAC;IAC1C,IAAI,OAAO,GAAY,SAAS,CAAC;IACjC,IAAI,QAAQ,GAAG,CAAC,CAAC;IAEjB,IAAI,KAAK,YAAY,WAAW,EAAE,CAAC;QACjC,IAAI,GAAG,cAAc,CAAC;QACtB,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QACxB,QAAQ,GAAG,CAAC,CAAC;IACf,CAAC;SAAM,IAAI,KAAK,YAAY,SAAS,EAAE,CAAC;QACtC,IAAI,GAAG,QAAQ,KAAK,CAAC,MAAM,EAAE,CAAC;QAC9B,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QACxB,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC;QACrB,QAAQ,GAAG,KAAK,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAEvC,iCAAiC;QACjC,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,IAAI,aAAa,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YAChF,IAAI,GAAI,KAAK,CAAC,IAA+B,CAAC,WAAW,CAAC;QAC5D,CAAC;IACH,CAAC;SAAM,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAClC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC1B,CAAC;IAED,MAAM,MAAM,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IAC3G,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC7D,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC;AAC9B,CAAC"}
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,UAAU,aAAa,CAAC,IAAa;IACzC,MAAM,MAAM,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACvC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;AAC1E,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,IAAI,GAAG,eAAe,CAAC;IAC3B,IAAI,OAAO,GAAG,2BAA2B,CAAC;IAC1C,IAAI,OAAO,GAAY,SAAS,CAAC;IACjC,IAAI,QAAQ,GAAG,CAAC,CAAC;IAEjB,IAAI,KAAK,YAAY,WAAW,EAAE,CAAC;QACjC,IAAI,GAAG,cAAc,CAAC;QACtB,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QACxB,QAAQ,GAAG,CAAC,CAAC;IACf,CAAC;SAAM,IAAI,KAAK,YAAY,SAAS,EAAE,CAAC;QACtC,IAAI,GAAG,QAAQ,KAAK,CAAC,MAAM,EAAE,CAAC;QAC9B,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QACxB,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC;QACrB,QAAQ,GAAG,KAAK,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAEvC,iCAAiC;QACjC,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,IAAI,aAAa,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YAChF,IAAI,GAAI,KAAK,CAAC,IAA+B,CAAC,WAAW,CAAC;QAC5D,CAAC;IACH,CAAC;SAAM,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAClC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC1B,CAAC;IAED,MAAM,MAAM,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IAC3G,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC7D,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC;AAC9B,CAAC"}
|