@insforge/cli 0.1.61 → 0.1.62

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 CHANGED
@@ -1237,7 +1237,7 @@ async function reportCliUsage(toolName, success, maxRetries = 1, explicitConfig)
1237
1237
 
1238
1238
  // src/lib/analytics.ts
1239
1239
  import { PostHog } from "posthog-node";
1240
- var POSTHOG_API_KEY = "phc_ueV1ii62wdBTkH7E70ugyeqHIHu8dFDdjs0qq3TZhJz";
1240
+ var POSTHOG_API_KEY = "";
1241
1241
  var POSTHOG_HOST = process.env.POSTHOG_HOST || "https://us.i.posthog.com";
1242
1242
  var client = null;
1243
1243
  function getClient() {
@@ -1269,6 +1269,17 @@ function trackDiagnose(subcommand, config) {
1269
1269
  oss_mode: config.project_id === FAKE_PROJECT_ID
1270
1270
  });
1271
1271
  }
1272
+ function trackPayments(subcommand, config, properties) {
1273
+ captureEvent(config.project_id, "cli_payments_invoked", {
1274
+ subcommand,
1275
+ project_id: config.project_id,
1276
+ project_name: config.project_name,
1277
+ org_id: config.org_id,
1278
+ region: config.region,
1279
+ oss_mode: config.project_id === FAKE_PROJECT_ID,
1280
+ ...properties
1281
+ });
1282
+ }
1272
1283
  async function shutdownAnalytics() {
1273
1284
  try {
1274
1285
  if (client) await client.shutdown();
@@ -1326,6 +1337,9 @@ ${err.nextActions}`;
1326
1337
  if (res.status === 404 && isRouteLevel404 && path5.startsWith("/api/compute")) {
1327
1338
  message = "Compute services are not available on this backend.\nSelf-hosted: upgrade your InsForge instance. Cloud: contact your InsForge admin to enable compute.";
1328
1339
  }
1340
+ if (res.status === 404 && isRouteLevel404 && path5.startsWith("/api/payments")) {
1341
+ message = "Payments are not available on this backend.\nSelf-hosted: upgrade your InsForge instance. Cloud/private preview: contact your InsForge admin to enable payments.";
1342
+ }
1329
1343
  if (res.status === 404 && isRouteLevel404 && path5 === "/api/database/migrations") {
1330
1344
  message = "Database migrations are not available on this backend.\nSelf-hosted: upgrade your InsForge instance. Cloud: contact your InsForge admin about database migration support.";
1331
1345
  }
@@ -5819,7 +5833,7 @@ function registerDiagnoseCommands(diagnoseCmd2) {
5819
5833
  const s = !json ? clack10.spinner() : null;
5820
5834
  s?.start("Collecting diagnostic data...");
5821
5835
  const data2 = await collectDiagnosticData(projectId, ossMode, apiUrl);
5822
- const cliVersion = "0.1.61";
5836
+ const cliVersion = "0.1.62";
5823
5837
  s?.stop("Data collected");
5824
5838
  if (!json) {
5825
5839
  console.log(`
@@ -6046,6 +6060,856 @@ function formatBytesCompact(bytes) {
6046
6060
  return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
6047
6061
  }
6048
6062
 
6063
+ // src/lib/api/payments.ts
6064
+ function withQuery(path5, params) {
6065
+ const query = new URLSearchParams();
6066
+ for (const [key, value] of Object.entries(params)) {
6067
+ if (value !== void 0) query.set(key, String(value));
6068
+ }
6069
+ const suffix = query.toString();
6070
+ return suffix ? `${path5}?${suffix}` : path5;
6071
+ }
6072
+ async function readJson(res) {
6073
+ return await res.json();
6074
+ }
6075
+ async function getPaymentsStatus() {
6076
+ return readJson(await ossFetch("/api/payments/status"));
6077
+ }
6078
+ async function getPaymentsConfig() {
6079
+ return readJson(await ossFetch("/api/payments/config"));
6080
+ }
6081
+ async function setStripeSecretKey(environment, secretKey) {
6082
+ return readJson(await ossFetch("/api/payments/config", {
6083
+ method: "POST",
6084
+ body: JSON.stringify({ environment, secretKey })
6085
+ }));
6086
+ }
6087
+ async function removeStripeSecretKey(environment) {
6088
+ return readJson(await ossFetch(`/api/payments/config/${encodeURIComponent(environment)}`, {
6089
+ method: "DELETE"
6090
+ }));
6091
+ }
6092
+ async function syncPayments(environment = "all") {
6093
+ return readJson(await ossFetch("/api/payments/sync", {
6094
+ method: "POST",
6095
+ body: JSON.stringify({ environment })
6096
+ }));
6097
+ }
6098
+ async function configurePaymentWebhook(environment) {
6099
+ return readJson(await ossFetch(
6100
+ `/api/payments/webhooks/${encodeURIComponent(environment)}/configure`,
6101
+ { method: "POST" }
6102
+ ));
6103
+ }
6104
+ async function listPaymentCatalog(environment) {
6105
+ return readJson(await ossFetch(withQuery("/api/payments/catalog", { environment })));
6106
+ }
6107
+ async function listPaymentProducts(environment) {
6108
+ return readJson(await ossFetch(withQuery("/api/payments/products", { environment })));
6109
+ }
6110
+ async function getPaymentProduct(environment, productId) {
6111
+ return readJson(await ossFetch(withQuery(
6112
+ `/api/payments/products/${encodeURIComponent(productId)}`,
6113
+ { environment }
6114
+ )));
6115
+ }
6116
+ async function createPaymentProduct(request) {
6117
+ return readJson(await ossFetch("/api/payments/products", {
6118
+ method: "POST",
6119
+ body: JSON.stringify(request)
6120
+ }));
6121
+ }
6122
+ async function updatePaymentProduct(productId, request) {
6123
+ return readJson(await ossFetch(`/api/payments/products/${encodeURIComponent(productId)}`, {
6124
+ method: "PATCH",
6125
+ body: JSON.stringify(request)
6126
+ }));
6127
+ }
6128
+ async function deletePaymentProduct(environment, productId) {
6129
+ return readJson(await ossFetch(withQuery(
6130
+ `/api/payments/products/${encodeURIComponent(productId)}`,
6131
+ { environment }
6132
+ ), { method: "DELETE" }));
6133
+ }
6134
+ async function listPaymentPrices(environment, stripeProductId) {
6135
+ return readJson(await ossFetch(withQuery("/api/payments/prices", {
6136
+ environment,
6137
+ stripeProductId
6138
+ })));
6139
+ }
6140
+ async function getPaymentPrice(environment, priceId) {
6141
+ return readJson(await ossFetch(withQuery(
6142
+ `/api/payments/prices/${encodeURIComponent(priceId)}`,
6143
+ { environment }
6144
+ )));
6145
+ }
6146
+ async function createPaymentPrice(request) {
6147
+ return readJson(await ossFetch("/api/payments/prices", {
6148
+ method: "POST",
6149
+ body: JSON.stringify(request)
6150
+ }));
6151
+ }
6152
+ async function updatePaymentPrice(priceId, request) {
6153
+ return readJson(await ossFetch(`/api/payments/prices/${encodeURIComponent(priceId)}`, {
6154
+ method: "PATCH",
6155
+ body: JSON.stringify(request)
6156
+ }));
6157
+ }
6158
+ async function archivePaymentPrice(environment, priceId) {
6159
+ return readJson(await ossFetch(withQuery(
6160
+ `/api/payments/prices/${encodeURIComponent(priceId)}`,
6161
+ { environment }
6162
+ ), { method: "DELETE" }));
6163
+ }
6164
+ async function listSubscriptions(request) {
6165
+ return readJson(await ossFetch(withQuery("/api/payments/subscriptions", request)));
6166
+ }
6167
+ async function listPaymentHistory(request) {
6168
+ return readJson(await ossFetch(withQuery("/api/payments/payment-history", request)));
6169
+ }
6170
+
6171
+ // src/commands/payments/utils.ts
6172
+ function parseEnvironment(value) {
6173
+ if (value === "test" || value === "live") return value;
6174
+ throw new CLIError('Environment must be "test" or "live".');
6175
+ }
6176
+ function parseEnvironmentOrAll(value) {
6177
+ if (value === "all") return value;
6178
+ return parseEnvironment(value);
6179
+ }
6180
+ function parseBooleanOption(value, flagName) {
6181
+ if (value === void 0) return void 0;
6182
+ const normalized = value.toLowerCase();
6183
+ if (normalized === "true") return true;
6184
+ if (normalized === "false") return false;
6185
+ throw new CLIError(`${flagName} must be "true" or "false".`);
6186
+ }
6187
+ function parseIntegerOption(value, flagName, options = {}) {
6188
+ if (value === void 0) return void 0;
6189
+ const parsed = Number.parseInt(value, 10);
6190
+ if (!Number.isInteger(parsed) || String(parsed) !== value.trim()) {
6191
+ throw new CLIError(`${flagName} must be an integer.`);
6192
+ }
6193
+ if (options.min !== void 0 && parsed < options.min) {
6194
+ throw new CLIError(`${flagName} must be at least ${options.min}.`);
6195
+ }
6196
+ if (options.max !== void 0 && parsed > options.max) {
6197
+ throw new CLIError(`${flagName} must be at most ${options.max}.`);
6198
+ }
6199
+ return parsed;
6200
+ }
6201
+ function parseMetadataOption(value) {
6202
+ if (value === void 0) return void 0;
6203
+ let parsed;
6204
+ try {
6205
+ parsed = JSON.parse(value);
6206
+ } catch {
6207
+ throw new CLIError("Invalid JSON for --metadata.");
6208
+ }
6209
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
6210
+ throw new CLIError("--metadata must be a JSON object.");
6211
+ }
6212
+ const metadata = {};
6213
+ for (const [key, raw] of Object.entries(parsed)) {
6214
+ if (typeof raw !== "string") {
6215
+ throw new CLIError(`Metadata value for "${key}" must be a string.`);
6216
+ }
6217
+ metadata[key] = raw;
6218
+ }
6219
+ return metadata;
6220
+ }
6221
+ function formatDate(value) {
6222
+ if (!value) return "-";
6223
+ const date = new Date(value);
6224
+ return Number.isNaN(date.getTime()) ? value : date.toLocaleString();
6225
+ }
6226
+ function formatAmount(amount, currency) {
6227
+ if (amount === null || amount === void 0) return "-";
6228
+ const code = currency?.toUpperCase();
6229
+ let fractionDigits = 2;
6230
+ if (code) {
6231
+ try {
6232
+ fractionDigits = new Intl.NumberFormat(void 0, {
6233
+ style: "currency",
6234
+ currency: code
6235
+ }).resolvedOptions().maximumFractionDigits;
6236
+ } catch {
6237
+ fractionDigits = 2;
6238
+ }
6239
+ }
6240
+ const divisor = 10 ** fractionDigits;
6241
+ return `${(amount / divisor).toFixed(fractionDigits)} ${code ?? ""}`.trim();
6242
+ }
6243
+ function formatRecurring(interval, intervalCount) {
6244
+ if (!interval) return "one-time";
6245
+ return `${intervalCount && intervalCount > 1 ? `${intervalCount} ` : ""}${interval}`;
6246
+ }
6247
+ async function trackPaymentUsage(subcommand, success, properties = {}) {
6248
+ try {
6249
+ try {
6250
+ const config = getProjectConfig();
6251
+ if (config) {
6252
+ trackPayments(subcommand, config, {
6253
+ success,
6254
+ ...properties
6255
+ });
6256
+ }
6257
+ } catch {
6258
+ }
6259
+ } finally {
6260
+ await shutdownAnalytics();
6261
+ }
6262
+ }
6263
+
6264
+ // src/commands/payments/catalog.ts
6265
+ function registerPaymentsCatalogCommand(paymentsCmd2) {
6266
+ paymentsCmd2.command("catalog").description("List mirrored Stripe products and prices").option("--environment <environment>", "Stripe environment: test or live").action(async (opts, cmd) => {
6267
+ const { json } = getRootOpts(cmd);
6268
+ try {
6269
+ const environment = opts.environment ? parseEnvironment(opts.environment) : void 0;
6270
+ await requireAuth();
6271
+ const data = await listPaymentCatalog(environment);
6272
+ if (json) {
6273
+ outputJson(data);
6274
+ } else {
6275
+ if (data.products.length === 0 && data.prices.length === 0) {
6276
+ console.log("No Stripe catalog records found.");
6277
+ await trackPaymentUsage("catalog", true, { environment });
6278
+ return;
6279
+ }
6280
+ if (data.products.length > 0) {
6281
+ console.log("Products");
6282
+ outputTable(
6283
+ ["Env", "Product ID", "Name", "Active", "Default Price"],
6284
+ data.products.map((product) => [
6285
+ product.environment,
6286
+ product.stripeProductId,
6287
+ product.name,
6288
+ product.active ? "Yes" : "No",
6289
+ product.defaultPriceId ?? "-"
6290
+ ])
6291
+ );
6292
+ }
6293
+ if (data.prices.length > 0) {
6294
+ console.log("Prices");
6295
+ outputTable(
6296
+ ["Env", "Price ID", "Product ID", "Amount", "Type", "Active", "Recurring"],
6297
+ data.prices.map((price) => [
6298
+ price.environment,
6299
+ price.stripePriceId,
6300
+ price.stripeProductId ?? "-",
6301
+ formatAmount(price.unitAmount, price.currency),
6302
+ price.type,
6303
+ price.active ? "Yes" : "No",
6304
+ formatRecurring(price.recurringInterval, price.recurringIntervalCount)
6305
+ ])
6306
+ );
6307
+ }
6308
+ }
6309
+ await trackPaymentUsage("catalog", true, { environment });
6310
+ } catch (err) {
6311
+ await trackPaymentUsage("catalog", false, { environment: opts.environment });
6312
+ handleError(err, json);
6313
+ }
6314
+ });
6315
+ }
6316
+
6317
+ // src/commands/payments/config.ts
6318
+ function outputConfigTable(data) {
6319
+ if (data.keys.length === 0) {
6320
+ console.log("No Stripe keys configured.");
6321
+ return;
6322
+ }
6323
+ outputTable(
6324
+ ["Env", "Configured", "Key"],
6325
+ data.keys.map((key) => [
6326
+ key.environment,
6327
+ key.hasKey ? "Yes" : "No",
6328
+ key.maskedKey ?? "-"
6329
+ ])
6330
+ );
6331
+ }
6332
+ function registerPaymentsConfigCommand(paymentsCmd2) {
6333
+ const configCmd = paymentsCmd2.command("config").description("Manage Stripe API keys for payments").action(async (_opts, cmd) => {
6334
+ const { json } = getRootOpts(cmd);
6335
+ try {
6336
+ await requireAuth();
6337
+ const data = await getPaymentsConfig();
6338
+ if (json) {
6339
+ outputJson(data);
6340
+ } else {
6341
+ outputConfigTable(data);
6342
+ }
6343
+ await trackPaymentUsage("config", true);
6344
+ } catch (err) {
6345
+ await trackPaymentUsage("config", false);
6346
+ handleError(err, json);
6347
+ }
6348
+ });
6349
+ configCmd.command("set <environment> [secretKey]").description("Configure a Stripe secret key for test or live payments").action(async (environmentValue, secretKeyValue, _opts, cmd) => {
6350
+ const { json } = getRootOpts(cmd);
6351
+ try {
6352
+ const environment = parseEnvironment(environmentValue);
6353
+ await requireAuth();
6354
+ let secretKey = secretKeyValue;
6355
+ if (!secretKey) {
6356
+ if (json) {
6357
+ throw new CLIError("Provide secretKey when using --json.");
6358
+ }
6359
+ const input = await password2({
6360
+ message: `Stripe ${environment} secret key`
6361
+ });
6362
+ if (isCancel2(input)) process.exit(0);
6363
+ secretKey = input;
6364
+ }
6365
+ const data = await setStripeSecretKey(environment, secretKey);
6366
+ if (json) {
6367
+ outputJson(data);
6368
+ } else {
6369
+ outputSuccess(`Stripe ${environment} key configured.`);
6370
+ }
6371
+ await trackPaymentUsage("config.set", true, { environment });
6372
+ } catch (err) {
6373
+ await trackPaymentUsage("config.set", false, { environment: environmentValue });
6374
+ handleError(err, json);
6375
+ }
6376
+ });
6377
+ configCmd.command("remove <environment>").alias("delete").description("Remove a configured Stripe secret key").action(async (environmentValue, _opts, cmd) => {
6378
+ const { json, yes } = getRootOpts(cmd);
6379
+ try {
6380
+ const environment = parseEnvironment(environmentValue);
6381
+ await requireAuth();
6382
+ if (json && !yes) {
6383
+ throw new CLIError("Use --yes with --json to remove a Stripe key non-interactively.");
6384
+ }
6385
+ if (!yes) {
6386
+ const confirm3 = await confirm2({
6387
+ message: `Remove Stripe ${environment} key? Payment sync and mutations for this environment will stop.`
6388
+ });
6389
+ if (isCancel2(confirm3) || !confirm3) process.exit(0);
6390
+ }
6391
+ const data = await removeStripeSecretKey(environment);
6392
+ if (json) {
6393
+ outputJson(data);
6394
+ } else {
6395
+ outputSuccess(`Stripe ${environment} key removed.`);
6396
+ }
6397
+ await trackPaymentUsage("config.remove", true, { environment });
6398
+ } catch (err) {
6399
+ await trackPaymentUsage("config.remove", false, { environment: environmentValue });
6400
+ handleError(err, json);
6401
+ }
6402
+ });
6403
+ }
6404
+
6405
+ // src/commands/payments/history.ts
6406
+ function registerPaymentsHistoryCommand(paymentsCmd2) {
6407
+ paymentsCmd2.command("history").description("List mirrored Stripe payment history").requiredOption("--environment <environment>", "Stripe environment: test or live").option("--subject-type <type>", "Filter by billing subject type").option("--subject-id <id>", "Filter by billing subject id").option("--limit <limit>", "Maximum rows to return (1-100)", "50").action(async (opts, cmd) => {
6408
+ const { json } = getRootOpts(cmd);
6409
+ try {
6410
+ const environment = parseEnvironment(opts.environment);
6411
+ const limit = parseIntegerOption(opts.limit, "--limit", { min: 1, max: 100 }) ?? 50;
6412
+ await requireAuth();
6413
+ const data = await listPaymentHistory({
6414
+ environment,
6415
+ limit,
6416
+ ...opts.subjectType !== void 0 ? { subjectType: opts.subjectType } : {},
6417
+ ...opts.subjectId !== void 0 ? { subjectId: opts.subjectId } : {}
6418
+ });
6419
+ if (json) {
6420
+ outputJson(data);
6421
+ } else if (data.paymentHistory.length === 0) {
6422
+ console.log("No Stripe payment history found.");
6423
+ } else {
6424
+ outputTable(
6425
+ ["Type", "Status", "Subject", "Amount", "Customer", "Stripe Object", "When"],
6426
+ data.paymentHistory.map((entry) => [
6427
+ entry.type,
6428
+ entry.status,
6429
+ entry.subjectType && entry.subjectId ? `${entry.subjectType}:${entry.subjectId}` : "-",
6430
+ formatAmount(entry.amount, entry.currency),
6431
+ entry.stripeCustomerId ?? "-",
6432
+ entry.stripeCheckoutSessionId ?? entry.stripeInvoiceId ?? entry.stripePaymentIntentId ?? entry.stripeRefundId ?? "-",
6433
+ formatDate(entry.paidAt ?? entry.failedAt ?? entry.refundedAt ?? entry.stripeCreatedAt)
6434
+ ])
6435
+ );
6436
+ }
6437
+ await trackPaymentUsage("history", true, { environment });
6438
+ } catch (err) {
6439
+ await trackPaymentUsage("history", false, { environment: opts.environment });
6440
+ handleError(err, json);
6441
+ }
6442
+ });
6443
+ }
6444
+
6445
+ // src/commands/payments/prices.ts
6446
+ function nullableString(value) {
6447
+ if (value === void 0) return void 0;
6448
+ return value === "null" ? null : value;
6449
+ }
6450
+ function parseRecurringInterval(value) {
6451
+ if (value === void 0) return void 0;
6452
+ if (value === "day" || value === "week" || value === "month" || value === "year") return value;
6453
+ throw new CLIError("--interval must be one of: day, week, month, year.");
6454
+ }
6455
+ function parseTaxBehavior(value) {
6456
+ if (value === void 0) return void 0;
6457
+ if (value === "exclusive" || value === "inclusive" || value === "unspecified") return value;
6458
+ throw new CLIError("--tax-behavior must be one of: exclusive, inclusive, unspecified.");
6459
+ }
6460
+ function outputPricesTable(prices) {
6461
+ if (prices.length === 0) {
6462
+ console.log("No Stripe prices found.");
6463
+ return;
6464
+ }
6465
+ outputTable(
6466
+ ["Env", "Price ID", "Product ID", "Amount", "Type", "Active", "Recurring", "Synced At"],
6467
+ prices.map((price) => [
6468
+ price.environment,
6469
+ price.stripePriceId,
6470
+ price.stripeProductId ?? "-",
6471
+ formatAmount(price.unitAmount, price.currency),
6472
+ price.type,
6473
+ price.active ? "Yes" : "No",
6474
+ formatRecurring(price.recurringInterval, price.recurringIntervalCount),
6475
+ formatDate(price.syncedAt)
6476
+ ])
6477
+ );
6478
+ }
6479
+ function registerPaymentsPricesCommand(paymentsCmd2) {
6480
+ const pricesCmd = paymentsCmd2.command("prices").description("Manage Stripe prices");
6481
+ pricesCmd.command("list").description("List mirrored Stripe prices").requiredOption("--environment <environment>", "Stripe environment: test or live").option("--product <productId>", "Filter by Stripe product id").action(async (opts, cmd) => {
6482
+ const { json } = getRootOpts(cmd);
6483
+ try {
6484
+ const environment = parseEnvironment(opts.environment);
6485
+ await requireAuth();
6486
+ const data = await listPaymentPrices(environment, opts.product);
6487
+ if (json) {
6488
+ outputJson(data);
6489
+ } else {
6490
+ outputPricesTable(data.prices);
6491
+ }
6492
+ await trackPaymentUsage("prices.list", true, { environment });
6493
+ } catch (err) {
6494
+ await trackPaymentUsage("prices.list", false, { environment: opts.environment });
6495
+ handleError(err, json);
6496
+ }
6497
+ });
6498
+ pricesCmd.command("get <priceId>").description("Show one Stripe price").requiredOption("--environment <environment>", "Stripe environment: test or live").action(async (priceId, opts, cmd) => {
6499
+ const { json } = getRootOpts(cmd);
6500
+ try {
6501
+ const environment = parseEnvironment(opts.environment);
6502
+ await requireAuth();
6503
+ const data = await getPaymentPrice(environment, priceId);
6504
+ if (json) {
6505
+ outputJson(data);
6506
+ } else {
6507
+ outputPricesTable([data.price]);
6508
+ }
6509
+ await trackPaymentUsage("prices.get", true, { environment });
6510
+ } catch (err) {
6511
+ await trackPaymentUsage("prices.get", false, { environment: opts.environment });
6512
+ handleError(err, json);
6513
+ }
6514
+ });
6515
+ pricesCmd.command("create").description("Create a Stripe one-time or recurring price").requiredOption("--environment <environment>", "Stripe environment: test or live").requiredOption("--product <productId>", "Stripe product id").requiredOption("--currency <currency>", "Three-letter currency code, e.g. usd").requiredOption("--unit-amount <amount>", "Unit amount in the smallest currency unit, e.g. cents").option("--interval <interval>", "Recurring interval: day, week, month, or year").option("--interval-count <count>", "Recurring interval count").option("--lookup-key <key>", 'Stripe lookup key, or "null"').option("--active <bool>", "Set active status (true/false)").option("--tax-behavior <behavior>", "exclusive, inclusive, or unspecified").option("--metadata <json>", "Metadata JSON object with string values").option("--idempotency-key <key>", "Caller-stable idempotency key").action(async (opts, cmd) => {
6516
+ const { json } = getRootOpts(cmd);
6517
+ try {
6518
+ const environment = parseEnvironment(opts.environment);
6519
+ await requireAuth();
6520
+ const interval = parseRecurringInterval(opts.interval);
6521
+ const intervalCount = parseIntegerOption(opts.intervalCount, "--interval-count", { min: 1 });
6522
+ if (!interval && intervalCount !== void 0) {
6523
+ throw new CLIError("Provide --interval when using --interval-count.");
6524
+ }
6525
+ const request = {
6526
+ environment,
6527
+ stripeProductId: opts.product,
6528
+ currency: opts.currency,
6529
+ unitAmount: parseIntegerOption(opts.unitAmount, "--unit-amount", { min: 0 }) ?? 0
6530
+ };
6531
+ const lookupKey = nullableString(opts.lookupKey);
6532
+ const active = parseBooleanOption(opts.active, "--active");
6533
+ const taxBehavior = parseTaxBehavior(opts.taxBehavior);
6534
+ const metadata = parseMetadataOption(opts.metadata);
6535
+ if (lookupKey !== void 0) request.lookupKey = lookupKey;
6536
+ if (active !== void 0) request.active = active;
6537
+ if (taxBehavior !== void 0) request.taxBehavior = taxBehavior;
6538
+ if (metadata !== void 0) request.metadata = metadata;
6539
+ if (opts.idempotencyKey !== void 0) request.idempotencyKey = opts.idempotencyKey;
6540
+ if (interval) {
6541
+ request.recurring = {
6542
+ interval,
6543
+ ...intervalCount !== void 0 ? { intervalCount } : {}
6544
+ };
6545
+ }
6546
+ const data = await createPaymentPrice(request);
6547
+ if (json) {
6548
+ outputJson(data);
6549
+ } else {
6550
+ outputSuccess(`Stripe price created: ${data.price.stripePriceId}`);
6551
+ }
6552
+ await trackPaymentUsage("prices.create", true, { environment });
6553
+ } catch (err) {
6554
+ await trackPaymentUsage("prices.create", false, { environment: opts.environment });
6555
+ handleError(err, json);
6556
+ }
6557
+ });
6558
+ pricesCmd.command("update <priceId>").description("Update a Stripe price").requiredOption("--environment <environment>", "Stripe environment: test or live").option("--active <bool>", "Set active status (true/false)").option("--lookup-key <key>", 'Stripe lookup key, or "null"').option("--tax-behavior <behavior>", "exclusive, inclusive, or unspecified").option("--metadata <json>", "Metadata JSON object with string values").action(async (priceId, opts, cmd) => {
6559
+ const { json } = getRootOpts(cmd);
6560
+ try {
6561
+ const environment = parseEnvironment(opts.environment);
6562
+ await requireAuth();
6563
+ const request = { environment };
6564
+ const active = parseBooleanOption(opts.active, "--active");
6565
+ const lookupKey = nullableString(opts.lookupKey);
6566
+ const taxBehavior = parseTaxBehavior(opts.taxBehavior);
6567
+ const metadata = parseMetadataOption(opts.metadata);
6568
+ if (active !== void 0) request.active = active;
6569
+ if (lookupKey !== void 0) request.lookupKey = lookupKey;
6570
+ if (taxBehavior !== void 0) request.taxBehavior = taxBehavior;
6571
+ if (metadata !== void 0) request.metadata = metadata;
6572
+ if (Object.keys(request).length === 1) {
6573
+ throw new CLIError("Provide at least one option to update (--active, --lookup-key, --tax-behavior, --metadata).");
6574
+ }
6575
+ const data = await updatePaymentPrice(priceId, request);
6576
+ if (json) {
6577
+ outputJson(data);
6578
+ } else {
6579
+ outputSuccess(`Stripe price updated: ${data.price.stripePriceId}`);
6580
+ }
6581
+ await trackPaymentUsage("prices.update", true, { environment });
6582
+ } catch (err) {
6583
+ await trackPaymentUsage("prices.update", false, { environment: opts.environment });
6584
+ handleError(err, json);
6585
+ }
6586
+ });
6587
+ pricesCmd.command("archive <priceId>").alias("delete").description("Archive a Stripe price").requiredOption("--environment <environment>", "Stripe environment: test or live").action(async (priceId, opts, cmd) => {
6588
+ const { json } = getRootOpts(cmd);
6589
+ try {
6590
+ const environment = parseEnvironment(opts.environment);
6591
+ await requireAuth();
6592
+ const data = await archivePaymentPrice(environment, priceId);
6593
+ if (json) {
6594
+ outputJson(data);
6595
+ } else {
6596
+ outputSuccess(`Stripe price archived: ${data.price.stripePriceId}`);
6597
+ }
6598
+ await trackPaymentUsage("prices.archive", true, { environment });
6599
+ } catch (err) {
6600
+ await trackPaymentUsage("prices.archive", false, { environment: opts.environment });
6601
+ handleError(err, json);
6602
+ }
6603
+ });
6604
+ }
6605
+
6606
+ // src/commands/payments/products.ts
6607
+ function nullableString2(value) {
6608
+ if (value === void 0) return void 0;
6609
+ return value === "null" ? null : value;
6610
+ }
6611
+ function outputProductsTable(products) {
6612
+ if (products.length === 0) {
6613
+ console.log("No Stripe products found.");
6614
+ return;
6615
+ }
6616
+ outputTable(
6617
+ ["Env", "Product ID", "Name", "Active", "Default Price", "Synced At"],
6618
+ products.map((product) => [
6619
+ product.environment,
6620
+ product.stripeProductId,
6621
+ product.name,
6622
+ product.active ? "Yes" : "No",
6623
+ product.defaultPriceId ?? "-",
6624
+ formatDate(product.syncedAt)
6625
+ ])
6626
+ );
6627
+ }
6628
+ function registerPaymentsProductsCommand(paymentsCmd2) {
6629
+ const productsCmd = paymentsCmd2.command("products").description("Manage Stripe products");
6630
+ productsCmd.command("list").description("List mirrored Stripe products").requiredOption("--environment <environment>", "Stripe environment: test or live").action(async (opts, cmd) => {
6631
+ const { json } = getRootOpts(cmd);
6632
+ try {
6633
+ const environment = parseEnvironment(opts.environment);
6634
+ await requireAuth();
6635
+ const data = await listPaymentProducts(environment);
6636
+ if (json) {
6637
+ outputJson(data);
6638
+ } else {
6639
+ outputProductsTable(data.products);
6640
+ }
6641
+ await trackPaymentUsage("products.list", true, { environment });
6642
+ } catch (err) {
6643
+ await trackPaymentUsage("products.list", false, { environment: opts.environment });
6644
+ handleError(err, json);
6645
+ }
6646
+ });
6647
+ productsCmd.command("get <productId>").description("Show one Stripe product and its prices").requiredOption("--environment <environment>", "Stripe environment: test or live").action(async (productId, opts, cmd) => {
6648
+ const { json } = getRootOpts(cmd);
6649
+ try {
6650
+ const environment = parseEnvironment(opts.environment);
6651
+ await requireAuth();
6652
+ const data = await getPaymentProduct(environment, productId);
6653
+ if (json) {
6654
+ outputJson(data);
6655
+ } else {
6656
+ outputProductsTable([data.product]);
6657
+ if (data.prices.length > 0) {
6658
+ console.log("Prices");
6659
+ outputTable(
6660
+ ["Price ID", "Amount", "Type", "Active", "Lookup Key"],
6661
+ data.prices.map((price) => [
6662
+ price.stripePriceId,
6663
+ formatAmount(price.unitAmount, price.currency),
6664
+ price.type,
6665
+ price.active ? "Yes" : "No",
6666
+ price.lookupKey ?? "-"
6667
+ ])
6668
+ );
6669
+ }
6670
+ }
6671
+ await trackPaymentUsage("products.get", true, { environment });
6672
+ } catch (err) {
6673
+ await trackPaymentUsage("products.get", false, { environment: opts.environment });
6674
+ handleError(err, json);
6675
+ }
6676
+ });
6677
+ productsCmd.command("create").description("Create a Stripe product").requiredOption("--environment <environment>", "Stripe environment: test or live").requiredOption("--name <name>", "Product name").option("--description <description>", 'Product description, or "null"').option("--active <bool>", "Set active status (true/false)").option("--metadata <json>", "Metadata JSON object with string values").option("--idempotency-key <key>", "Caller-stable idempotency key").action(async (opts, cmd) => {
6678
+ const { json } = getRootOpts(cmd);
6679
+ try {
6680
+ const environment = parseEnvironment(opts.environment);
6681
+ await requireAuth();
6682
+ const request = {
6683
+ environment,
6684
+ name: opts.name
6685
+ };
6686
+ const description = nullableString2(opts.description);
6687
+ const active = parseBooleanOption(opts.active, "--active");
6688
+ const metadata = parseMetadataOption(opts.metadata);
6689
+ if (description !== void 0) request.description = description;
6690
+ if (active !== void 0) request.active = active;
6691
+ if (metadata !== void 0) request.metadata = metadata;
6692
+ if (opts.idempotencyKey !== void 0) request.idempotencyKey = opts.idempotencyKey;
6693
+ const data = await createPaymentProduct(request);
6694
+ if (json) {
6695
+ outputJson(data);
6696
+ } else {
6697
+ outputSuccess(`Stripe product created: ${data.product.stripeProductId}`);
6698
+ }
6699
+ await trackPaymentUsage("products.create", true, { environment });
6700
+ } catch (err) {
6701
+ await trackPaymentUsage("products.create", false, { environment: opts.environment });
6702
+ handleError(err, json);
6703
+ }
6704
+ });
6705
+ productsCmd.command("update <productId>").description("Update a Stripe product").requiredOption("--environment <environment>", "Stripe environment: test or live").option("--name <name>", "Product name").option("--description <description>", 'Product description, or "null"').option("--active <bool>", "Set active status (true/false)").option("--metadata <json>", "Metadata JSON object with string values").action(async (productId, opts, cmd) => {
6706
+ const { json } = getRootOpts(cmd);
6707
+ try {
6708
+ const environment = parseEnvironment(opts.environment);
6709
+ await requireAuth();
6710
+ const request = { environment };
6711
+ const description = nullableString2(opts.description);
6712
+ const active = parseBooleanOption(opts.active, "--active");
6713
+ const metadata = parseMetadataOption(opts.metadata);
6714
+ if (opts.name !== void 0) request.name = opts.name;
6715
+ if (description !== void 0) request.description = description;
6716
+ if (active !== void 0) request.active = active;
6717
+ if (metadata !== void 0) request.metadata = metadata;
6718
+ if (Object.keys(request).length === 1) {
6719
+ throw new CLIError("Provide at least one option to update (--name, --description, --active, --metadata).");
6720
+ }
6721
+ const data = await updatePaymentProduct(productId, request);
6722
+ if (json) {
6723
+ outputJson(data);
6724
+ } else {
6725
+ outputSuccess(`Stripe product updated: ${data.product.stripeProductId}`);
6726
+ }
6727
+ await trackPaymentUsage("products.update", true, { environment });
6728
+ } catch (err) {
6729
+ await trackPaymentUsage("products.update", false, { environment: opts.environment });
6730
+ handleError(err, json);
6731
+ }
6732
+ });
6733
+ productsCmd.command("delete <productId>").description("Delete a Stripe product that has no prices").requiredOption("--environment <environment>", "Stripe environment: test or live").action(async (productId, opts, cmd) => {
6734
+ const { json, yes } = getRootOpts(cmd);
6735
+ try {
6736
+ const environment = parseEnvironment(opts.environment);
6737
+ await requireAuth();
6738
+ if (json && !yes) {
6739
+ throw new CLIError("Use --yes with --json to delete a Stripe product non-interactively.");
6740
+ }
6741
+ if (!yes) {
6742
+ const confirm3 = await confirm2({
6743
+ message: `Delete Stripe ${environment} product "${productId}"?`
6744
+ });
6745
+ if (isCancel2(confirm3) || !confirm3) process.exit(0);
6746
+ }
6747
+ const data = await deletePaymentProduct(environment, productId);
6748
+ if (json) {
6749
+ outputJson(data);
6750
+ } else {
6751
+ outputSuccess(`Stripe product deleted: ${data.stripeProductId}`);
6752
+ }
6753
+ await trackPaymentUsage("products.delete", true, { environment });
6754
+ } catch (err) {
6755
+ await trackPaymentUsage("products.delete", false, { environment: opts.environment });
6756
+ handleError(err, json);
6757
+ }
6758
+ });
6759
+ }
6760
+
6761
+ // src/commands/payments/status.ts
6762
+ function registerPaymentsStatusCommand(paymentsCmd2) {
6763
+ paymentsCmd2.command("status").description("Show Stripe payment connection, sync, and webhook status").action(async (_opts, cmd) => {
6764
+ const { json } = getRootOpts(cmd);
6765
+ try {
6766
+ await requireAuth();
6767
+ const data = await getPaymentsStatus();
6768
+ if (json) {
6769
+ outputJson(data);
6770
+ } else if (data.connections.length === 0) {
6771
+ console.log("No Stripe payment environments found.");
6772
+ } else {
6773
+ outputTable(
6774
+ ["Env", "Status", "Key", "Account", "Webhook", "Last Sync", "Synced At"],
6775
+ data.connections.map((connection) => [
6776
+ connection.environment,
6777
+ connection.status,
6778
+ connection.maskedKey ?? "-",
6779
+ connection.stripeAccountId ?? "-",
6780
+ connection.webhookEndpointId ? "Configured" : "-",
6781
+ connection.lastSyncStatus ?? "-",
6782
+ formatDate(connection.lastSyncedAt)
6783
+ ])
6784
+ );
6785
+ }
6786
+ await trackPaymentUsage("status", true);
6787
+ } catch (err) {
6788
+ await trackPaymentUsage("status", false);
6789
+ handleError(err, json);
6790
+ }
6791
+ });
6792
+ }
6793
+
6794
+ // src/commands/payments/subscriptions.ts
6795
+ function registerPaymentsSubscriptionsCommand(paymentsCmd2) {
6796
+ paymentsCmd2.command("subscriptions").description("List mirrored Stripe subscriptions").requiredOption("--environment <environment>", "Stripe environment: test or live").option("--subject-type <type>", "Filter by billing subject type").option("--subject-id <id>", "Filter by billing subject id").option("--limit <limit>", "Maximum rows to return (1-100)", "50").action(async (opts, cmd) => {
6797
+ const { json } = getRootOpts(cmd);
6798
+ try {
6799
+ const environment = parseEnvironment(opts.environment);
6800
+ const limit = parseIntegerOption(opts.limit, "--limit", { min: 1, max: 100 }) ?? 50;
6801
+ await requireAuth();
6802
+ const data = await listSubscriptions({
6803
+ environment,
6804
+ limit,
6805
+ ...opts.subjectType !== void 0 ? { subjectType: opts.subjectType } : {},
6806
+ ...opts.subjectId !== void 0 ? { subjectId: opts.subjectId } : {}
6807
+ });
6808
+ if (json) {
6809
+ outputJson(data);
6810
+ } else if (data.subscriptions.length === 0) {
6811
+ console.log("No Stripe subscriptions found.");
6812
+ } else {
6813
+ outputTable(
6814
+ ["Subscription ID", "Customer", "Subject", "Status", "Items", "Period End"],
6815
+ data.subscriptions.map((subscription) => [
6816
+ subscription.stripeSubscriptionId,
6817
+ subscription.stripeCustomerId,
6818
+ subscription.subjectType && subscription.subjectId ? `${subscription.subjectType}:${subscription.subjectId}` : "-",
6819
+ subscription.status,
6820
+ String(subscription.items?.length ?? 0),
6821
+ formatDate(subscription.currentPeriodEnd)
6822
+ ])
6823
+ );
6824
+ }
6825
+ await trackPaymentUsage("subscriptions", true, { environment });
6826
+ } catch (err) {
6827
+ await trackPaymentUsage("subscriptions", false, { environment: opts.environment });
6828
+ handleError(err, json);
6829
+ }
6830
+ });
6831
+ }
6832
+
6833
+ // src/commands/payments/sync.ts
6834
+ function registerPaymentsSyncCommand(paymentsCmd2) {
6835
+ paymentsCmd2.command("sync").description("Sync configured Stripe products, prices, and subscriptions").option("--environment <environment>", "Stripe environment: test, live, or all", "all").action(async (opts, cmd) => {
6836
+ const { json } = getRootOpts(cmd);
6837
+ try {
6838
+ const environment = parseEnvironmentOrAll(opts.environment);
6839
+ await requireAuth();
6840
+ const data = await syncPayments(environment);
6841
+ if (json) {
6842
+ outputJson(data);
6843
+ } else if (data.results.length === 0) {
6844
+ console.log("No configured Stripe environments to sync.");
6845
+ } else {
6846
+ outputTable(
6847
+ ["Env", "Status", "Products", "Prices", "Subscriptions", "Unmapped", "Synced At"],
6848
+ data.results.map((result) => [
6849
+ result.environment,
6850
+ result.connection.lastSyncStatus ?? result.connection.status,
6851
+ String(result.connection.lastSyncCounts.products ?? 0),
6852
+ String(result.connection.lastSyncCounts.prices ?? 0),
6853
+ String(result.subscriptions?.synced ?? 0),
6854
+ String(result.subscriptions?.unmapped ?? 0),
6855
+ formatDate(result.connection.lastSyncedAt)
6856
+ ])
6857
+ );
6858
+ outputSuccess("Stripe payments synced.");
6859
+ }
6860
+ await trackPaymentUsage("sync", true, { environment });
6861
+ } catch (err) {
6862
+ await trackPaymentUsage("sync", false, { environment: opts.environment });
6863
+ handleError(err, json);
6864
+ }
6865
+ });
6866
+ }
6867
+
6868
+ // src/commands/payments/webhooks.ts
6869
+ function registerPaymentsWebhooksCommand(paymentsCmd2) {
6870
+ const webhooksCmd = paymentsCmd2.command("webhooks").description("Manage InsForge-managed Stripe webhooks");
6871
+ webhooksCmd.command("configure <environment>").description("Create or recreate the managed Stripe webhook endpoint").action(async (environmentValue, _opts, cmd) => {
6872
+ const { json } = getRootOpts(cmd);
6873
+ try {
6874
+ const environment = parseEnvironment(environmentValue);
6875
+ await requireAuth();
6876
+ const data = await configurePaymentWebhook(environment);
6877
+ if (json) {
6878
+ outputJson(data);
6879
+ } else {
6880
+ outputTable(
6881
+ ["Env", "Webhook ID", "URL", "Configured At"],
6882
+ [[
6883
+ data.connection.environment,
6884
+ data.connection.webhookEndpointId ?? "-",
6885
+ data.connection.webhookEndpointUrl ?? "-",
6886
+ formatDate(data.connection.webhookConfiguredAt)
6887
+ ]]
6888
+ );
6889
+ outputSuccess(`Stripe ${environment} webhook configured.`);
6890
+ }
6891
+ await trackPaymentUsage("webhooks.configure", true, { environment });
6892
+ } catch (err) {
6893
+ await trackPaymentUsage("webhooks.configure", false, { environment: environmentValue });
6894
+ handleError(err, json);
6895
+ }
6896
+ });
6897
+ }
6898
+
6899
+ // src/commands/payments/index.ts
6900
+ function registerPaymentsCommands(paymentsCmd2) {
6901
+ paymentsCmd2.description("Manage Stripe payments");
6902
+ registerPaymentsStatusCommand(paymentsCmd2);
6903
+ registerPaymentsConfigCommand(paymentsCmd2);
6904
+ registerPaymentsSyncCommand(paymentsCmd2);
6905
+ registerPaymentsWebhooksCommand(paymentsCmd2);
6906
+ registerPaymentsCatalogCommand(paymentsCmd2);
6907
+ registerPaymentsProductsCommand(paymentsCmd2);
6908
+ registerPaymentsPricesCommand(paymentsCmd2);
6909
+ registerPaymentsSubscriptionsCommand(paymentsCmd2);
6910
+ registerPaymentsHistoryCommand(paymentsCmd2);
6911
+ }
6912
+
6049
6913
  // src/index.ts
6050
6914
  var __dirname = dirname(fileURLToPath(import.meta.url));
6051
6915
  var pkg = JSON.parse(readFileSync7(join13(__dirname, "../package.json"), "utf-8"));
@@ -6117,6 +6981,8 @@ registerLogsCommand(program);
6117
6981
  registerMetadataCommand(program);
6118
6982
  var diagnoseCmd = program.command("diagnose");
6119
6983
  registerDiagnoseCommands(diagnoseCmd);
6984
+ var paymentsCmd = program.command("payments").description("Manage Stripe payments");
6985
+ registerPaymentsCommands(paymentsCmd);
6120
6986
  var computeCmd = program.command("compute").description("Manage compute services (Docker containers on Fly.io)");
6121
6987
  registerComputeListCommand(computeCmd);
6122
6988
  registerComputeGetCommand(computeCmd);