@insforge/cli 0.1.60 → 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/README.md +87 -1
- package/dist/index.js +938 -14
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { readFileSync as readFileSync7 } from "fs";
|
|
5
|
-
import { join as
|
|
5
|
+
import { join as join13, dirname } from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
import { Command } from "commander";
|
|
8
8
|
import * as clack11 from "@clack/prompts";
|
|
@@ -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 = "
|
|
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
|
}
|
|
@@ -4844,11 +4858,13 @@ function registerComputeLogsCommand(computeCmd2) {
|
|
|
4844
4858
|
}
|
|
4845
4859
|
|
|
4846
4860
|
// src/commands/compute/deploy.ts
|
|
4847
|
-
import { existsSync as
|
|
4848
|
-
import { join as
|
|
4861
|
+
import { existsSync as existsSync8 } from "fs";
|
|
4862
|
+
import { join as join12, resolve as resolve4 } from "path";
|
|
4849
4863
|
|
|
4850
4864
|
// src/lib/flyctl.ts
|
|
4851
4865
|
import { spawn, spawnSync } from "child_process";
|
|
4866
|
+
import { existsSync as existsSync7, writeFileSync as writeFileSync5, unlinkSync as unlinkSync2 } from "fs";
|
|
4867
|
+
import { join as join11 } from "path";
|
|
4852
4868
|
function ensureFlyctlAvailable() {
|
|
4853
4869
|
const r = spawnSync("flyctl", ["version"], {
|
|
4854
4870
|
encoding: "utf8",
|
|
@@ -4860,8 +4876,41 @@ function ensureFlyctlAvailable() {
|
|
|
4860
4876
|
);
|
|
4861
4877
|
}
|
|
4862
4878
|
}
|
|
4879
|
+
function ensureFlyTomlStub(opts) {
|
|
4880
|
+
const path5 = join11(opts.dir, "fly.toml");
|
|
4881
|
+
if (existsSync7(path5)) {
|
|
4882
|
+
return () => {
|
|
4883
|
+
};
|
|
4884
|
+
}
|
|
4885
|
+
const stub = `# Auto-generated by @insforge/cli for compute deploy. Safe to delete.
|
|
4886
|
+
app = "${opts.appId}"
|
|
4887
|
+
primary_region = "${opts.region}"
|
|
4888
|
+
|
|
4889
|
+
[build]
|
|
4890
|
+
|
|
4891
|
+
[http_service]
|
|
4892
|
+
internal_port = ${opts.port}
|
|
4893
|
+
force_https = true
|
|
4894
|
+
auto_stop_machines = false
|
|
4895
|
+
auto_start_machines = true
|
|
4896
|
+
min_machines_running = 0
|
|
4897
|
+
`;
|
|
4898
|
+
writeFileSync5(path5, stub, "utf8");
|
|
4899
|
+
return () => {
|
|
4900
|
+
try {
|
|
4901
|
+
unlinkSync2(path5);
|
|
4902
|
+
} catch {
|
|
4903
|
+
}
|
|
4904
|
+
};
|
|
4905
|
+
}
|
|
4863
4906
|
function flyctlBuildAndPush(opts) {
|
|
4864
4907
|
return new Promise((resolve5, reject) => {
|
|
4908
|
+
const cleanupStub = ensureFlyTomlStub({
|
|
4909
|
+
dir: opts.dir,
|
|
4910
|
+
appId: opts.appId,
|
|
4911
|
+
region: opts.region,
|
|
4912
|
+
port: opts.port
|
|
4913
|
+
});
|
|
4865
4914
|
const child = spawn(
|
|
4866
4915
|
"flyctl",
|
|
4867
4916
|
[
|
|
@@ -4893,9 +4942,11 @@ function flyctlBuildAndPush(opts) {
|
|
|
4893
4942
|
process.stderr.write(s);
|
|
4894
4943
|
});
|
|
4895
4944
|
child.on("error", (err) => {
|
|
4945
|
+
cleanupStub();
|
|
4896
4946
|
reject(new CLIError(`flyctl deploy could not start: ${err.message}`));
|
|
4897
4947
|
});
|
|
4898
4948
|
child.on("exit", (code) => {
|
|
4949
|
+
cleanupStub();
|
|
4899
4950
|
if (code !== 0) {
|
|
4900
4951
|
return reject(
|
|
4901
4952
|
new CLIError(`flyctl deploy --build-only failed (exit ${code}). See output above.`)
|
|
@@ -5003,8 +5054,8 @@ function registerComputeDeployCommand(computeCmd2) {
|
|
|
5003
5054
|
return;
|
|
5004
5055
|
}
|
|
5005
5056
|
const absDir = resolve4(dir);
|
|
5006
|
-
const dockerfilePath =
|
|
5007
|
-
if (!
|
|
5057
|
+
const dockerfilePath = join12(absDir, "Dockerfile");
|
|
5058
|
+
if (!existsSync8(dockerfilePath)) {
|
|
5008
5059
|
throw new CLIError(
|
|
5009
5060
|
`No Dockerfile at ${dockerfilePath}.
|
|
5010
5061
|
Either:
|
|
@@ -5046,12 +5097,33 @@ function registerComputeDeployCommand(computeCmd2) {
|
|
|
5046
5097
|
const tokenJson = await tokenRes.json();
|
|
5047
5098
|
const imageLabel = `cli-${Date.now()}`;
|
|
5048
5099
|
if (!json) outputInfo(`Building & pushing on Fly remote builder...`);
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5100
|
+
let imageRef;
|
|
5101
|
+
try {
|
|
5102
|
+
({ imageRef } = await flyctlBuildAndPush({
|
|
5103
|
+
dir: absDir,
|
|
5104
|
+
appId: flyAppId,
|
|
5105
|
+
imageLabel,
|
|
5106
|
+
token: tokenJson.token,
|
|
5107
|
+
region: opts.region,
|
|
5108
|
+
port
|
|
5109
|
+
}));
|
|
5110
|
+
} catch (buildErr) {
|
|
5111
|
+
if (!existing) {
|
|
5112
|
+
try {
|
|
5113
|
+
await ossFetch(`/api/compute/services/${encodeURIComponent(serviceId)}`, {
|
|
5114
|
+
method: "DELETE"
|
|
5115
|
+
});
|
|
5116
|
+
if (!json) outputInfo(`Rolled back service "${opts.name}" after build failure.`);
|
|
5117
|
+
} catch {
|
|
5118
|
+
if (!json) {
|
|
5119
|
+
outputInfo(
|
|
5120
|
+
`Build failed and rollback also failed. Run: npx @insforge/cli compute delete ${serviceId}`
|
|
5121
|
+
);
|
|
5122
|
+
}
|
|
5123
|
+
}
|
|
5124
|
+
}
|
|
5125
|
+
throw buildErr;
|
|
5126
|
+
}
|
|
5055
5127
|
if (!json) outputInfo("Launching machine...");
|
|
5056
5128
|
const updateBody = {
|
|
5057
5129
|
imageUrl: imageRef,
|
|
@@ -5761,7 +5833,7 @@ function registerDiagnoseCommands(diagnoseCmd2) {
|
|
|
5761
5833
|
const s = !json ? clack10.spinner() : null;
|
|
5762
5834
|
s?.start("Collecting diagnostic data...");
|
|
5763
5835
|
const data2 = await collectDiagnosticData(projectId, ossMode, apiUrl);
|
|
5764
|
-
const cliVersion = "0.1.
|
|
5836
|
+
const cliVersion = "0.1.62";
|
|
5765
5837
|
s?.stop("Data collected");
|
|
5766
5838
|
if (!json) {
|
|
5767
5839
|
console.log(`
|
|
@@ -5988,9 +6060,859 @@ function formatBytesCompact(bytes) {
|
|
|
5988
6060
|
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
5989
6061
|
}
|
|
5990
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
|
+
|
|
5991
6913
|
// src/index.ts
|
|
5992
6914
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
5993
|
-
var pkg = JSON.parse(readFileSync7(
|
|
6915
|
+
var pkg = JSON.parse(readFileSync7(join13(__dirname, "../package.json"), "utf-8"));
|
|
5994
6916
|
var INSFORGE_LOGO = `
|
|
5995
6917
|
\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
5996
6918
|
\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D
|
|
@@ -6059,6 +6981,8 @@ registerLogsCommand(program);
|
|
|
6059
6981
|
registerMetadataCommand(program);
|
|
6060
6982
|
var diagnoseCmd = program.command("diagnose");
|
|
6061
6983
|
registerDiagnoseCommands(diagnoseCmd);
|
|
6984
|
+
var paymentsCmd = program.command("payments").description("Manage Stripe payments");
|
|
6985
|
+
registerPaymentsCommands(paymentsCmd);
|
|
6062
6986
|
var computeCmd = program.command("compute").description("Manage compute services (Docker containers on Fly.io)");
|
|
6063
6987
|
registerComputeListCommand(computeCmd);
|
|
6064
6988
|
registerComputeGetCommand(computeCmd);
|