deepline 0.1.103 → 0.1.105
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/cli/index.js +577 -16
- package/dist/cli/index.mjs +577 -16
- package/dist/index.d.mts +184 -1
- package/dist/index.d.ts +184 -1
- package/dist/index.js +135 -9
- package/dist/index.mjs +135 -9
- package/dist/repo/sdk/src/client.ts +312 -6
- package/dist/repo/sdk/src/http.ts +12 -1
- package/dist/repo/sdk/src/index.ts +6 -0
- package/dist/repo/sdk/src/release.ts +5 -2
- package/dist/repo/shared_libs/play-runtime/csv-rename.ts +26 -0
- package/dist/repo/shared_libs/play-runtime/providers.ts +2 -2
- package/dist/repo/shared_libs/play-runtime/scheduler-backend.ts +2 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -238,10 +238,13 @@ var SDK_RELEASE = {
|
|
|
238
238
|
// preflight (existence, data rows, quotes, duplicate headers), HTML error
|
|
239
239
|
// scrubbing, and word-boundary watch truncation.
|
|
240
240
|
// 0.1.103 ships the refined SDK CLI command surface.
|
|
241
|
-
|
|
241
|
+
// 0.1.104 ships postgres_fast suspension/billing parity and runtime worker hardening.
|
|
242
|
+
// 0.1.105 ships the billing catalog surface: billing plans, subscribe,
|
|
243
|
+
// subscription status/cancel, invoices, and the client.billing namespace.
|
|
244
|
+
version: "0.1.105",
|
|
242
245
|
apiContract: "2026-06-dataset-column-cell-stale-hard-cutover",
|
|
243
246
|
supportPolicy: {
|
|
244
|
-
latest: "0.1.
|
|
247
|
+
latest: "0.1.105",
|
|
245
248
|
minimumSupported: "0.1.53",
|
|
246
249
|
deprecatedBelow: "0.1.53"
|
|
247
250
|
}
|
|
@@ -372,7 +375,7 @@ var HttpClient = class {
|
|
|
372
375
|
signal: controller.signal
|
|
373
376
|
});
|
|
374
377
|
clearTimeout(timeoutId);
|
|
375
|
-
if (response.status === 401 || response.status === 403) {
|
|
378
|
+
if (response.status === 401 || response.status === 403 && !options?.forbiddenAsApiError) {
|
|
376
379
|
throw new AuthError();
|
|
377
380
|
}
|
|
378
381
|
if (response.status === 429) {
|
|
@@ -1464,6 +1467,9 @@ var INCLUDE_TOOL_METADATA_HEADER = "x-deepline-include-tool-metadata";
|
|
|
1464
1467
|
var EXECUTE_RESPONSE_CONTRACT_HEADER = "x-deepline-execute-response-contract";
|
|
1465
1468
|
var V2_EXECUTE_RESPONSE_CONTRACT = "v2-tool-response";
|
|
1466
1469
|
var COMPILE_MANIFEST_RETRY_DELAYS_MS = [250, 1e3];
|
|
1470
|
+
var REGISTER_PLAY_ARTIFACTS_COMPILE_CONCURRENCY = 3;
|
|
1471
|
+
var REGISTER_PLAY_ARTIFACTS_MAX_BATCH_COUNT = 3;
|
|
1472
|
+
var REGISTER_PLAY_ARTIFACTS_MAX_BATCH_BYTES = 25e5;
|
|
1467
1473
|
function sleep2(ms) {
|
|
1468
1474
|
return new Promise((resolve16) => setTimeout(resolve16, ms));
|
|
1469
1475
|
}
|
|
@@ -1476,6 +1482,45 @@ function isTransientCompileManifestError(error) {
|
|
|
1476
1482
|
message
|
|
1477
1483
|
);
|
|
1478
1484
|
}
|
|
1485
|
+
async function mapWithConcurrency(items, concurrency, mapper) {
|
|
1486
|
+
const results = new Array(items.length);
|
|
1487
|
+
let nextIndex = 0;
|
|
1488
|
+
const workerCount = Math.min(Math.max(1, concurrency), items.length);
|
|
1489
|
+
await Promise.all(
|
|
1490
|
+
Array.from({ length: workerCount }, async () => {
|
|
1491
|
+
for (; ; ) {
|
|
1492
|
+
const index = nextIndex;
|
|
1493
|
+
nextIndex += 1;
|
|
1494
|
+
if (index >= items.length) {
|
|
1495
|
+
return;
|
|
1496
|
+
}
|
|
1497
|
+
results[index] = await mapper(items[index], index);
|
|
1498
|
+
}
|
|
1499
|
+
})
|
|
1500
|
+
);
|
|
1501
|
+
return results;
|
|
1502
|
+
}
|
|
1503
|
+
function jsonUtf8Bytes(value) {
|
|
1504
|
+
return new TextEncoder().encode(JSON.stringify(value)).length;
|
|
1505
|
+
}
|
|
1506
|
+
function chunkRegisterPlayArtifacts(artifacts) {
|
|
1507
|
+
const chunks = [];
|
|
1508
|
+
let current = [];
|
|
1509
|
+
for (const artifact of artifacts) {
|
|
1510
|
+
const candidate = [...current, artifact];
|
|
1511
|
+
const candidateTooLarge = candidate.length > REGISTER_PLAY_ARTIFACTS_MAX_BATCH_COUNT || jsonUtf8Bytes({ artifacts: candidate }) > REGISTER_PLAY_ARTIFACTS_MAX_BATCH_BYTES;
|
|
1512
|
+
if (current.length > 0 && candidateTooLarge) {
|
|
1513
|
+
chunks.push(current);
|
|
1514
|
+
current = [artifact];
|
|
1515
|
+
} else {
|
|
1516
|
+
current = candidate;
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
if (current.length > 0) {
|
|
1520
|
+
chunks.push(current);
|
|
1521
|
+
}
|
|
1522
|
+
return chunks;
|
|
1523
|
+
}
|
|
1479
1524
|
var RUN_LOGS_PAGE_LIMIT = 1e3;
|
|
1480
1525
|
function isRecord2(value) {
|
|
1481
1526
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
@@ -1643,6 +1688,8 @@ var DeeplineClient = class {
|
|
|
1643
1688
|
config;
|
|
1644
1689
|
/** Canonical run lifecycle namespace backed by `/api/v2/runs`. */
|
|
1645
1690
|
runs;
|
|
1691
|
+
/** Billing namespace: subscription status/cancel and invoice history. */
|
|
1692
|
+
billing;
|
|
1646
1693
|
/**
|
|
1647
1694
|
* Create a low-level SDK client.
|
|
1648
1695
|
*
|
|
@@ -1663,6 +1710,16 @@ var DeeplineClient = class {
|
|
|
1663
1710
|
exportDatasetRows: (input2) => this.getPlaySheetRows(input2),
|
|
1664
1711
|
stop: (runId, options2) => this.stopRun(runId, options2)
|
|
1665
1712
|
};
|
|
1713
|
+
this.billing = {
|
|
1714
|
+
plans: () => this.getBillingPlans(),
|
|
1715
|
+
subscription: {
|
|
1716
|
+
status: () => this.getBillingSubscriptionStatus(),
|
|
1717
|
+
cancel: (options2) => this.cancelBillingSubscription(options2)
|
|
1718
|
+
},
|
|
1719
|
+
invoices: {
|
|
1720
|
+
list: (options2) => this.listBillingInvoices(options2)
|
|
1721
|
+
}
|
|
1722
|
+
};
|
|
1666
1723
|
}
|
|
1667
1724
|
/** The resolved base URL this client is targeting (e.g. `"http://localhost:3000"`). */
|
|
1668
1725
|
get baseUrl() {
|
|
@@ -2031,8 +2088,13 @@ var DeeplineClient = class {
|
|
|
2031
2088
|
* first when a compiler manifest is not already supplied.
|
|
2032
2089
|
*/
|
|
2033
2090
|
async registerPlayArtifacts(artifacts) {
|
|
2034
|
-
|
|
2035
|
-
|
|
2091
|
+
if (artifacts.length === 0) {
|
|
2092
|
+
return this.http.post("/api/v2/plays/artifacts", { artifacts });
|
|
2093
|
+
}
|
|
2094
|
+
const compiledArtifacts = await mapWithConcurrency(
|
|
2095
|
+
artifacts,
|
|
2096
|
+
REGISTER_PLAY_ARTIFACTS_COMPILE_CONCURRENCY,
|
|
2097
|
+
async (artifact) => ({
|
|
2036
2098
|
...artifact,
|
|
2037
2099
|
compilerManifest: artifact.compilerManifest ?? await this.compilePlayManifest({
|
|
2038
2100
|
name: artifact.name,
|
|
@@ -2040,11 +2102,20 @@ var DeeplineClient = class {
|
|
|
2040
2102
|
sourceFiles: artifact.sourceFiles,
|
|
2041
2103
|
artifact: artifact.artifact
|
|
2042
2104
|
})
|
|
2043
|
-
})
|
|
2105
|
+
})
|
|
2044
2106
|
);
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2107
|
+
const responses = [];
|
|
2108
|
+
for (const chunk of chunkRegisterPlayArtifacts(compiledArtifacts)) {
|
|
2109
|
+
responses.push(
|
|
2110
|
+
await this.http.post("/api/v2/plays/artifacts", {
|
|
2111
|
+
artifacts: chunk
|
|
2112
|
+
})
|
|
2113
|
+
);
|
|
2114
|
+
}
|
|
2115
|
+
return {
|
|
2116
|
+
success: responses.every((response) => response.success),
|
|
2117
|
+
artifacts: responses.flatMap((response) => response.artifacts)
|
|
2118
|
+
};
|
|
2048
2119
|
}
|
|
2049
2120
|
/**
|
|
2050
2121
|
* Compile a bundled play artifact into the server-side compiler manifest.
|
|
@@ -3017,6 +3088,61 @@ var DeeplineClient = class {
|
|
|
3017
3088
|
// ——————————————————————————————————————————————————————————
|
|
3018
3089
|
// Health
|
|
3019
3090
|
// ——————————————————————————————————————————————————————————
|
|
3091
|
+
/**
|
|
3092
|
+
* Published plans plus the caller's active plan: prices, monthly grant
|
|
3093
|
+
* credits, rollover policy, and which plans are open for subscription.
|
|
3094
|
+
* Prefer `client.billing.plans()`.
|
|
3095
|
+
*
|
|
3096
|
+
* @returns Snake_case catalog from `GET /api/v2/billing/catalog/current`
|
|
3097
|
+
*/
|
|
3098
|
+
async getBillingPlans() {
|
|
3099
|
+
return this.http.get("/api/v2/billing/catalog/current");
|
|
3100
|
+
}
|
|
3101
|
+
/**
|
|
3102
|
+
* Subscription state for the active workspace: active plan, whether a
|
|
3103
|
+
* Stripe subscription backs it, renewal/cancellation facts, and remaining
|
|
3104
|
+
* Deepline credit pools. Prefer `client.billing.subscription.status()`.
|
|
3105
|
+
*
|
|
3106
|
+
* @returns Snake_case subscription status from `GET /api/v2/billing/subscription/status`
|
|
3107
|
+
*/
|
|
3108
|
+
async getBillingSubscriptionStatus() {
|
|
3109
|
+
return this.http.get(
|
|
3110
|
+
"/api/v2/billing/subscription/status"
|
|
3111
|
+
);
|
|
3112
|
+
}
|
|
3113
|
+
/**
|
|
3114
|
+
* Schedule subscription cancellation at period end, or reverse a pending
|
|
3115
|
+
* cancellation with `{ undo: true }`. The customer keeps the cycle they
|
|
3116
|
+
* paid for and every remaining credit — cancellation never claws back
|
|
3117
|
+
* credits. Prefer `client.billing.subscription.cancel(...)`.
|
|
3118
|
+
*
|
|
3119
|
+
* @throws {@link DeeplineError} with `statusCode: 409` when the workspace
|
|
3120
|
+
* has no active subscription, and `statusCode: 502` when Stripe rejects
|
|
3121
|
+
* the update (the server message is preserved).
|
|
3122
|
+
*/
|
|
3123
|
+
async cancelBillingSubscription(options) {
|
|
3124
|
+
return this.http.post(
|
|
3125
|
+
"/api/v2/billing/subscription/cancel",
|
|
3126
|
+
{ action: options?.undo ? "undo_cancel" : "cancel" }
|
|
3127
|
+
);
|
|
3128
|
+
}
|
|
3129
|
+
/**
|
|
3130
|
+
* Customer-facing billing history: subscription invoices plus one-time
|
|
3131
|
+
* credit purchase receipts, newest first, with Stripe-hosted links.
|
|
3132
|
+
* Prefer `client.billing.invoices.list(...)`.
|
|
3133
|
+
*
|
|
3134
|
+
* @param options.limit - Maximum entries to return (server clamps to 1–100, default 24).
|
|
3135
|
+
*/
|
|
3136
|
+
async listBillingInvoices(options) {
|
|
3137
|
+
const params = new URLSearchParams();
|
|
3138
|
+
if (options?.limit !== void 0) {
|
|
3139
|
+
params.set("limit", String(options.limit));
|
|
3140
|
+
}
|
|
3141
|
+
const suffix = Array.from(params).length > 0 ? `?${params.toString()}` : "";
|
|
3142
|
+
return this.http.get(
|
|
3143
|
+
`/api/v2/billing/invoices${suffix}`
|
|
3144
|
+
);
|
|
3145
|
+
}
|
|
3020
3146
|
/**
|
|
3021
3147
|
* Check API connectivity and server health.
|
|
3022
3148
|
*
|
|
@@ -4141,6 +4267,56 @@ var import_commander = require("commander");
|
|
|
4141
4267
|
var import_promises2 = require("fs/promises");
|
|
4142
4268
|
var import_node_path5 = require("path");
|
|
4143
4269
|
var import_sync3 = require("csv-stringify/sync");
|
|
4270
|
+
var SUBSCRIPTION_STATUS_NEXT_COMMAND = "deepline billing subscription status --json";
|
|
4271
|
+
var SUBSCRIPTION_CANCEL_PATH = "/api/v2/billing/subscription/cancel";
|
|
4272
|
+
function billingFailureFromError(error, options) {
|
|
4273
|
+
if (error instanceof AuthError) {
|
|
4274
|
+
return {
|
|
4275
|
+
exitCode: 3,
|
|
4276
|
+
code: "AUTH_ERROR",
|
|
4277
|
+
message: error.message,
|
|
4278
|
+
next: "deepline auth status --json"
|
|
4279
|
+
};
|
|
4280
|
+
}
|
|
4281
|
+
if (!(error instanceof DeeplineError) || typeof error.statusCode !== "number") {
|
|
4282
|
+
return null;
|
|
4283
|
+
}
|
|
4284
|
+
if (error.statusCode === 404 || error.statusCode === 409) {
|
|
4285
|
+
return {
|
|
4286
|
+
exitCode: 4,
|
|
4287
|
+
code: options.notFoundCode,
|
|
4288
|
+
message: error.message,
|
|
4289
|
+
next: SUBSCRIPTION_STATUS_NEXT_COMMAND
|
|
4290
|
+
};
|
|
4291
|
+
}
|
|
4292
|
+
if (error.statusCode >= 500) {
|
|
4293
|
+
return {
|
|
4294
|
+
exitCode: 5,
|
|
4295
|
+
code: "BILLING_SERVER_ERROR",
|
|
4296
|
+
message: error.message,
|
|
4297
|
+
next: SUBSCRIPTION_STATUS_NEXT_COMMAND
|
|
4298
|
+
};
|
|
4299
|
+
}
|
|
4300
|
+
return null;
|
|
4301
|
+
}
|
|
4302
|
+
function reportBillingFailure(failure, options) {
|
|
4303
|
+
const textLines = [failure.message];
|
|
4304
|
+
if (failure.next) {
|
|
4305
|
+
textLines.push(`Next: ${failure.next}`);
|
|
4306
|
+
}
|
|
4307
|
+
printCommandEnvelope(
|
|
4308
|
+
{
|
|
4309
|
+
ok: false,
|
|
4310
|
+
exitCode: failure.exitCode,
|
|
4311
|
+
code: failure.code,
|
|
4312
|
+
message: failure.message,
|
|
4313
|
+
...failure.next ? { next: failure.next } : {}
|
|
4314
|
+
},
|
|
4315
|
+
{ json: options.json, text: `${textLines.join("\n")}
|
|
4316
|
+
` }
|
|
4317
|
+
);
|
|
4318
|
+
process.exitCode = failure.exitCode;
|
|
4319
|
+
}
|
|
4144
4320
|
function humanize(value) {
|
|
4145
4321
|
return String(value || "").split("_").filter(Boolean).map((token) => token[0]?.toUpperCase() + token.slice(1)).join(" ") || "Unknown";
|
|
4146
4322
|
}
|
|
@@ -4416,6 +4592,273 @@ async function handleLedgerExportAll(options) {
|
|
|
4416
4592
|
{ json: options.json }
|
|
4417
4593
|
);
|
|
4418
4594
|
}
|
|
4595
|
+
function planPriceText(priceUsd, priceInterval) {
|
|
4596
|
+
if (priceUsd === null || priceUsd === void 0) {
|
|
4597
|
+
return "no list price";
|
|
4598
|
+
}
|
|
4599
|
+
return `$${priceUsd}${priceInterval ? `/${priceInterval}` : ""}`;
|
|
4600
|
+
}
|
|
4601
|
+
function planRolloverText(rollover) {
|
|
4602
|
+
const mode = rollover?.mode ?? "none";
|
|
4603
|
+
if (mode === "none") return "no rollover";
|
|
4604
|
+
if (mode === "bounded") {
|
|
4605
|
+
return `rollover up to ${rollover?.max_credits ?? "(unknown)"} credits`;
|
|
4606
|
+
}
|
|
4607
|
+
return `rollover ${mode}`;
|
|
4608
|
+
}
|
|
4609
|
+
async function handlePlans(options) {
|
|
4610
|
+
const { http } = getAuthedHttpClient();
|
|
4611
|
+
const payload = await http.get(
|
|
4612
|
+
"/api/v2/billing/catalog/current"
|
|
4613
|
+
);
|
|
4614
|
+
const activePlan = payload.active_plan ?? {};
|
|
4615
|
+
const plans = Array.isArray(payload.plans) ? payload.plans : [];
|
|
4616
|
+
const metrics = Array.isArray(payload.metrics) ? payload.metrics : [];
|
|
4617
|
+
const lines = [
|
|
4618
|
+
`Catalog: ${payload.catalog_id ?? "(unknown)"} (version ${payload.catalog_version ?? "(unknown)"})`,
|
|
4619
|
+
`Active plan: ${activePlan.public_name ?? "(unknown)"} (${activePlan.plan_version_id ?? "(unknown)"})`,
|
|
4620
|
+
`Assigned by: ${activePlan.assigned_by ?? "default"} | subscription: ${activePlan.has_subscription ? "yes" : "no"}`,
|
|
4621
|
+
...plans.length === 0 ? ["Plans: none published"] : [
|
|
4622
|
+
"Plans:",
|
|
4623
|
+
...plans.map(
|
|
4624
|
+
(plan) => `${plan.public_name ?? "(unknown)"} (${plan.plan_version_id ?? "(unknown)"}) | ${planPriceText(plan.price_usd, plan.price_interval)} | ${plan.monthly_grant_credits ?? 0} credits/cycle | ${planRolloverText(plan.rollover)} | ${plan.acquirable ? "acquirable" : "not acquirable"}`
|
|
4625
|
+
)
|
|
4626
|
+
],
|
|
4627
|
+
...metrics.length === 0 ? ["Metrics: none"] : [
|
|
4628
|
+
"Metrics:",
|
|
4629
|
+
...metrics.map(
|
|
4630
|
+
(metric) => `${metric.id ?? "(unknown)"} | ${metric.name ?? "(unknown)"}`
|
|
4631
|
+
)
|
|
4632
|
+
]
|
|
4633
|
+
];
|
|
4634
|
+
printCommandEnvelope(
|
|
4635
|
+
{
|
|
4636
|
+
...payload,
|
|
4637
|
+
render: { sections: [{ title: "billing plans", lines }] }
|
|
4638
|
+
},
|
|
4639
|
+
{ json: options.json }
|
|
4640
|
+
);
|
|
4641
|
+
}
|
|
4642
|
+
async function handleSubscribe(planVersionId, options) {
|
|
4643
|
+
const { http } = getAuthedHttpClient();
|
|
4644
|
+
const payload = await http.request(
|
|
4645
|
+
"/api/v2/billing/subscription/checkout",
|
|
4646
|
+
{
|
|
4647
|
+
method: "POST",
|
|
4648
|
+
body: { plan_version_id: planVersionId },
|
|
4649
|
+
forbiddenAsApiError: true
|
|
4650
|
+
}
|
|
4651
|
+
);
|
|
4652
|
+
const url = String(payload.checkout_url || "");
|
|
4653
|
+
if (!options.json && options.open !== false && url) openInBrowser(url);
|
|
4654
|
+
printCommandEnvelope(
|
|
4655
|
+
{
|
|
4656
|
+
...payload,
|
|
4657
|
+
render: {
|
|
4658
|
+
sections: [
|
|
4659
|
+
{
|
|
4660
|
+
title: "billing subscribe",
|
|
4661
|
+
lines: [url || "Subscription checkout session created."]
|
|
4662
|
+
}
|
|
4663
|
+
]
|
|
4664
|
+
}
|
|
4665
|
+
},
|
|
4666
|
+
{ json: options.json }
|
|
4667
|
+
);
|
|
4668
|
+
}
|
|
4669
|
+
async function handleSubscriptionStatus(options) {
|
|
4670
|
+
const client2 = new DeeplineClient();
|
|
4671
|
+
const payload = await client2.billing.subscription.status();
|
|
4672
|
+
const pools = payload.credit_pools ?? [];
|
|
4673
|
+
const lines = [
|
|
4674
|
+
`Plan: ${payload.plan_name ?? "(unknown)"} (${payload.plan_version_id ?? "(unknown)"})`,
|
|
4675
|
+
`Subscribed: ${payload.subscribed ? "yes" : "no"}`,
|
|
4676
|
+
...payload.cancel_at_period_end ? [
|
|
4677
|
+
`Cancellation scheduled: ends ${payload.current_period_end ?? "(unknown)"} at period end (credits are kept)`
|
|
4678
|
+
] : [],
|
|
4679
|
+
`Price: ${planPriceText(payload.price_usd, payload.price_interval)}`,
|
|
4680
|
+
`Monthly grant: ${payload.monthly_grant_credits ?? 0} credits`,
|
|
4681
|
+
`Pooled credits remaining: ${payload.pooled_credits_remaining ?? 0}`,
|
|
4682
|
+
...pools.length === 0 ? ["Credit pools: none"] : [
|
|
4683
|
+
"Credit pools:",
|
|
4684
|
+
...pools.map(
|
|
4685
|
+
(pool) => `${pool.pool ?? "(unknown)"} | ${pool.credits_remaining ?? 0}/${pool.credits_granted ?? 0} credits remaining | ${pool.source ?? "(unknown)"}`
|
|
4686
|
+
)
|
|
4687
|
+
],
|
|
4688
|
+
`Assigned by: ${payload.assigned_by ?? "default"}`
|
|
4689
|
+
];
|
|
4690
|
+
printCommandEnvelope(
|
|
4691
|
+
{
|
|
4692
|
+
...payload,
|
|
4693
|
+
render: { sections: [{ title: "billing subscription", lines }] }
|
|
4694
|
+
},
|
|
4695
|
+
{ json: options.json }
|
|
4696
|
+
);
|
|
4697
|
+
}
|
|
4698
|
+
async function handleSubscriptionCancel(options) {
|
|
4699
|
+
const client2 = new DeeplineClient();
|
|
4700
|
+
const action = options.undo ? "undo_cancel" : "cancel";
|
|
4701
|
+
if (options.dryRun) {
|
|
4702
|
+
const status = await client2.billing.subscription.status();
|
|
4703
|
+
if (!status.subscribed) {
|
|
4704
|
+
reportBillingFailure(
|
|
4705
|
+
{
|
|
4706
|
+
exitCode: 4,
|
|
4707
|
+
code: "NO_ACTIVE_SUBSCRIPTION",
|
|
4708
|
+
message: "This workspace has no active subscription to cancel.",
|
|
4709
|
+
next: SUBSCRIPTION_STATUS_NEXT_COMMAND
|
|
4710
|
+
},
|
|
4711
|
+
options
|
|
4712
|
+
);
|
|
4713
|
+
return;
|
|
4714
|
+
}
|
|
4715
|
+
const consequence = action === "cancel" ? `Would schedule cancellation at period end${status.current_period_end ? ` (${status.current_period_end})` : ""}. The subscription stays active until then and remaining credits are kept.` : "Would reverse the pending cancellation; the subscription would renew normally.";
|
|
4716
|
+
printCommandEnvelope(
|
|
4717
|
+
{
|
|
4718
|
+
ok: true,
|
|
4719
|
+
dry_run: true,
|
|
4720
|
+
action,
|
|
4721
|
+
plan_version_id: status.plan_version_id,
|
|
4722
|
+
plan_name: status.plan_name,
|
|
4723
|
+
cancel_at_period_end: status.cancel_at_period_end,
|
|
4724
|
+
current_period_end: status.current_period_end,
|
|
4725
|
+
consequence,
|
|
4726
|
+
planned_request: {
|
|
4727
|
+
method: "POST",
|
|
4728
|
+
path: SUBSCRIPTION_CANCEL_PATH,
|
|
4729
|
+
body: { action }
|
|
4730
|
+
},
|
|
4731
|
+
render: {
|
|
4732
|
+
sections: [
|
|
4733
|
+
{
|
|
4734
|
+
title: "billing subscription cancel (dry run)",
|
|
4735
|
+
lines: [
|
|
4736
|
+
`Plan: ${status.plan_name} (${status.plan_version_id})`,
|
|
4737
|
+
`Planned request: POST ${SUBSCRIPTION_CANCEL_PATH} {"action":"${action}"}`,
|
|
4738
|
+
consequence,
|
|
4739
|
+
"No request was sent. Re-run without --dry-run to apply."
|
|
4740
|
+
]
|
|
4741
|
+
}
|
|
4742
|
+
]
|
|
4743
|
+
}
|
|
4744
|
+
},
|
|
4745
|
+
{ json: options.json }
|
|
4746
|
+
);
|
|
4747
|
+
return;
|
|
4748
|
+
}
|
|
4749
|
+
let result;
|
|
4750
|
+
try {
|
|
4751
|
+
result = await client2.billing.subscription.cancel({
|
|
4752
|
+
undo: Boolean(options.undo)
|
|
4753
|
+
});
|
|
4754
|
+
} catch (error) {
|
|
4755
|
+
const failure = billingFailureFromError(error, {
|
|
4756
|
+
notFoundCode: "NO_ACTIVE_SUBSCRIPTION"
|
|
4757
|
+
});
|
|
4758
|
+
if (!failure) throw error;
|
|
4759
|
+
reportBillingFailure(failure, options);
|
|
4760
|
+
return;
|
|
4761
|
+
}
|
|
4762
|
+
const lines = options.undo ? [
|
|
4763
|
+
"Pending cancellation reversed; the subscription will renew normally.",
|
|
4764
|
+
`Subscription: ${result.subscription_id}`
|
|
4765
|
+
] : [
|
|
4766
|
+
`Cancellation scheduled for subscription ${result.subscription_id}.`,
|
|
4767
|
+
`The subscription stays active until ${result.current_period_end ?? "the end of the current billing period"}, then cancels at period end.`,
|
|
4768
|
+
"Remaining credits are yours to keep \u2014 cancellation never removes credits.",
|
|
4769
|
+
"Undo with: deepline billing subscription cancel --undo"
|
|
4770
|
+
];
|
|
4771
|
+
printCommandEnvelope(
|
|
4772
|
+
{
|
|
4773
|
+
ok: true,
|
|
4774
|
+
...result,
|
|
4775
|
+
render: {
|
|
4776
|
+
sections: [{ title: "billing subscription cancel", lines }]
|
|
4777
|
+
}
|
|
4778
|
+
},
|
|
4779
|
+
{ json: options.json }
|
|
4780
|
+
);
|
|
4781
|
+
}
|
|
4782
|
+
function invoiceAmountText(amountCents, currency) {
|
|
4783
|
+
const value = (Number(amountCents ?? 0) / 100).toFixed(2);
|
|
4784
|
+
return String(currency ?? "usd").toLowerCase() === "usd" ? `$${value}` : `${value} ${String(currency).toUpperCase()}`;
|
|
4785
|
+
}
|
|
4786
|
+
function invoiceDateText(createdAt) {
|
|
4787
|
+
return String(createdAt ?? "").slice(0, 10);
|
|
4788
|
+
}
|
|
4789
|
+
function invoiceLine(entry, compact) {
|
|
4790
|
+
const amount = invoiceAmountText(entry.amount_cents, entry.currency);
|
|
4791
|
+
const link = entry.url ?? "(no link)";
|
|
4792
|
+
if (compact) {
|
|
4793
|
+
return [
|
|
4794
|
+
entry.id,
|
|
4795
|
+
invoiceDateText(entry.created_at),
|
|
4796
|
+
amount,
|
|
4797
|
+
entry.status,
|
|
4798
|
+
link
|
|
4799
|
+
].join(" | ");
|
|
4800
|
+
}
|
|
4801
|
+
return [
|
|
4802
|
+
invoiceDateText(entry.created_at),
|
|
4803
|
+
entry.description,
|
|
4804
|
+
amount,
|
|
4805
|
+
entry.status,
|
|
4806
|
+
link
|
|
4807
|
+
].join(" | ");
|
|
4808
|
+
}
|
|
4809
|
+
async function handleInvoices(options) {
|
|
4810
|
+
let limit;
|
|
4811
|
+
if (options.limit !== void 0) {
|
|
4812
|
+
limit = Number.parseInt(options.limit, 10);
|
|
4813
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
|
|
4814
|
+
reportBillingFailure(
|
|
4815
|
+
{
|
|
4816
|
+
exitCode: 2,
|
|
4817
|
+
code: "INVALID_LIMIT",
|
|
4818
|
+
message: "--limit must be an integer between 1 and 100."
|
|
4819
|
+
},
|
|
4820
|
+
options
|
|
4821
|
+
);
|
|
4822
|
+
return;
|
|
4823
|
+
}
|
|
4824
|
+
}
|
|
4825
|
+
const client2 = new DeeplineClient();
|
|
4826
|
+
let payload;
|
|
4827
|
+
try {
|
|
4828
|
+
payload = await client2.billing.invoices.list(
|
|
4829
|
+
limit === void 0 ? void 0 : { limit }
|
|
4830
|
+
);
|
|
4831
|
+
} catch (error) {
|
|
4832
|
+
const failure = billingFailureFromError(error, {
|
|
4833
|
+
notFoundCode: "NOT_FOUND"
|
|
4834
|
+
});
|
|
4835
|
+
if (!failure) throw error;
|
|
4836
|
+
reportBillingFailure(failure, options);
|
|
4837
|
+
return;
|
|
4838
|
+
}
|
|
4839
|
+
const entries = Array.isArray(payload.entries) ? payload.entries : [];
|
|
4840
|
+
const compact = Boolean(options.compact);
|
|
4841
|
+
const header = compact ? "id | date | amount | status | link" : "date | description | amount | status | link";
|
|
4842
|
+
const lines = entries.length === 0 ? ["No invoices or receipts yet."] : [header, ...entries.map((entry) => invoiceLine(entry, compact))];
|
|
4843
|
+
const envelope = compact ? {
|
|
4844
|
+
org_id: payload.org_id,
|
|
4845
|
+
count: entries.length,
|
|
4846
|
+
entries: entries.map((entry) => ({
|
|
4847
|
+
id: entry.id,
|
|
4848
|
+
date: invoiceDateText(entry.created_at),
|
|
4849
|
+
amount: invoiceAmountText(entry.amount_cents, entry.currency),
|
|
4850
|
+
status: entry.status,
|
|
4851
|
+
url: entry.url
|
|
4852
|
+
}))
|
|
4853
|
+
} : { ...payload, count: entries.length };
|
|
4854
|
+
printCommandEnvelope(
|
|
4855
|
+
{
|
|
4856
|
+
...envelope,
|
|
4857
|
+
render: { sections: [{ title: "billing invoices", lines }] }
|
|
4858
|
+
},
|
|
4859
|
+
{ json: options.json }
|
|
4860
|
+
);
|
|
4861
|
+
}
|
|
4419
4862
|
async function handleCheckout(options) {
|
|
4420
4863
|
const { http } = getAuthedHttpClient();
|
|
4421
4864
|
const payload = await http.post(
|
|
@@ -4464,19 +4907,33 @@ async function handleRedeemCode(code, options) {
|
|
|
4464
4907
|
);
|
|
4465
4908
|
}
|
|
4466
4909
|
function registerBillingCommands(program) {
|
|
4467
|
-
const billing = program.command("billing").description("
|
|
4910
|
+
const billing = program.command("billing").description("See your plan and credits, buy more, and manage billing.").addHelpText(
|
|
4468
4911
|
"after",
|
|
4469
4912
|
`
|
|
4470
4913
|
Concepts:
|
|
4471
4914
|
Billing commands show Deepline credits, not raw provider spend.
|
|
4472
|
-
|
|
4473
|
-
a browser unless --no-open is set.
|
|
4915
|
+
checkout/subscribe/redeem-code can open a browser unless --no-open is set.
|
|
4474
4916
|
|
|
4475
4917
|
Examples:
|
|
4918
|
+
# See where you stand
|
|
4476
4919
|
deepline billing balance --json
|
|
4477
4920
|
deepline billing usage --limit 20 --json
|
|
4478
|
-
deepline billing
|
|
4921
|
+
deepline billing plans --json
|
|
4922
|
+
deepline billing subscription status --json
|
|
4923
|
+
|
|
4924
|
+
# Buy credits or a plan
|
|
4479
4925
|
deepline billing checkout --credits 1000 --no-open --json
|
|
4926
|
+
deepline billing subscribe runtime-395-2026-07-v1 --no-open --json
|
|
4927
|
+
deepline billing redeem-code --code ABC123 --no-open --json
|
|
4928
|
+
|
|
4929
|
+
# Manage
|
|
4930
|
+
deepline billing subscription cancel --dry-run --json
|
|
4931
|
+
deepline billing set-limit 500 --json
|
|
4932
|
+
|
|
4933
|
+
# Records
|
|
4934
|
+
deepline billing invoices --limit 12 --json
|
|
4935
|
+
deepline billing history --time 1m --json
|
|
4936
|
+
deepline billing ledger export all --json
|
|
4480
4937
|
`
|
|
4481
4938
|
);
|
|
4482
4939
|
billing.command("balance").description("Show current billing balance.").addHelpText(
|
|
@@ -4602,6 +5059,110 @@ Examples:
|
|
|
4602
5059
|
deepline billing checkout --credits 1000 --discount-code LAUNCH --no-open
|
|
4603
5060
|
`
|
|
4604
5061
|
).option("--tier <tierId>", "Named pricing tier").option("--credits <credits>", "Custom credit amount").option("--discount-code <code>", "Apply a discount code").option("--no-open", "Print the checkout URL without opening a browser").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleCheckout);
|
|
5062
|
+
billing.command("plans").description("Show published plans and the plan you are on.").addHelpText(
|
|
5063
|
+
"after",
|
|
5064
|
+
`
|
|
5065
|
+
Notes:
|
|
5066
|
+
Read-only. Answers "what plans exist and what am I on": each plan's price,
|
|
5067
|
+
monthly grant credits, rollover policy, and whether it is open for
|
|
5068
|
+
subscription, plus the catalog's usage metrics. All amounts are Deepline
|
|
5069
|
+
credits and Deepline-facing USD, never raw provider spend. Subscribe to a
|
|
5070
|
+
listed plan with: deepline billing subscribe <plan_version_id>
|
|
5071
|
+
|
|
5072
|
+
Examples:
|
|
5073
|
+
deepline billing plans
|
|
5074
|
+
deepline billing plans --json
|
|
5075
|
+
`
|
|
5076
|
+
).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handlePlans);
|
|
5077
|
+
billing.command("subscribe").description(
|
|
5078
|
+
"Start a subscription checkout for a plan and optionally open it in your browser."
|
|
5079
|
+
).addHelpText(
|
|
5080
|
+
"after",
|
|
5081
|
+
`
|
|
5082
|
+
Notes:
|
|
5083
|
+
Creates a Stripe subscription checkout session for the given plan version.
|
|
5084
|
+
Opens the checkout URL in a browser unless --no-open is set. Fails loudly
|
|
5085
|
+
with the server error when subscription checkout is not enabled or the plan
|
|
5086
|
+
is not open for subscription.
|
|
5087
|
+
|
|
5088
|
+
Examples:
|
|
5089
|
+
deepline billing subscribe runtime-395-2026-07-v1
|
|
5090
|
+
deepline billing subscribe runtime-395-2026-07-v1 --no-open --json
|
|
5091
|
+
`
|
|
5092
|
+
).argument("<plan_version_id>", "Plan version id from `billing plans`").option("--no-open", "Print the checkout URL without opening a browser").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleSubscribe);
|
|
5093
|
+
billing.command("subscription").description("Inspect and manage subscription state for the workspace.").addHelpText(
|
|
5094
|
+
"after",
|
|
5095
|
+
`
|
|
5096
|
+
Examples:
|
|
5097
|
+
deepline billing subscription status
|
|
5098
|
+
deepline billing subscription status --json
|
|
5099
|
+
deepline billing subscription cancel --dry-run
|
|
5100
|
+
deepline billing subscription cancel --json
|
|
5101
|
+
deepline billing subscription cancel --undo --json
|
|
5102
|
+
`
|
|
5103
|
+
).addCommand(
|
|
5104
|
+
new import_commander.Command("status").description("Show active plan, subscription state, and credit pools.").addHelpText(
|
|
5105
|
+
"after",
|
|
5106
|
+
`
|
|
5107
|
+
Notes:
|
|
5108
|
+
Read-only. Shows the active plan, whether a Stripe subscription backs it,
|
|
5109
|
+
scheduled cancellation state, and remaining Deepline credits per grant pool.
|
|
5110
|
+
|
|
5111
|
+
Examples:
|
|
5112
|
+
deepline billing subscription status
|
|
5113
|
+
deepline billing subscription status --json
|
|
5114
|
+
`
|
|
5115
|
+
).option(
|
|
5116
|
+
"--json",
|
|
5117
|
+
"Emit JSON output. Also automatic when stdout is piped"
|
|
5118
|
+
).action(handleSubscriptionStatus)
|
|
5119
|
+
).addCommand(
|
|
5120
|
+
new import_commander.Command("cancel").description("Cancel the subscription at period end. Credits are kept.").addHelpText(
|
|
5121
|
+
"after",
|
|
5122
|
+
`
|
|
5123
|
+
Notes:
|
|
5124
|
+
Mutates subscription state. Cancellation is always AT PERIOD END: the
|
|
5125
|
+
subscription stays active until the end of the paid cycle and remaining
|
|
5126
|
+
Deepline credits are kept \u2014 cancellation never removes credits. --undo
|
|
5127
|
+
reverses a pending cancellation before the period ends. --dry-run reads
|
|
5128
|
+
subscription status and prints the planned mutation without calling the
|
|
5129
|
+
cancel endpoint.
|
|
5130
|
+
|
|
5131
|
+
Exit codes:
|
|
5132
|
+
0 cancellation scheduled (or reversed with --undo)
|
|
5133
|
+
3 auth error
|
|
5134
|
+
4 no active subscription to cancel
|
|
5135
|
+
5 Stripe/server failure (the server message is preserved)
|
|
5136
|
+
|
|
5137
|
+
Examples:
|
|
5138
|
+
deepline billing subscription cancel --dry-run
|
|
5139
|
+
deepline billing subscription cancel
|
|
5140
|
+
deepline billing subscription cancel --undo
|
|
5141
|
+
deepline billing subscription cancel --json
|
|
5142
|
+
`
|
|
5143
|
+
).option("--undo", "Reverse a pending cancellation before period end").option(
|
|
5144
|
+
"--dry-run",
|
|
5145
|
+
"Print the planned cancellation without calling the cancel endpoint"
|
|
5146
|
+
).option(
|
|
5147
|
+
"--json",
|
|
5148
|
+
"Emit JSON output. Also automatic when stdout is piped"
|
|
5149
|
+
).action(handleSubscriptionCancel)
|
|
5150
|
+
);
|
|
5151
|
+
billing.command("invoices").description("List subscription invoices and credit purchase receipts.").addHelpText(
|
|
5152
|
+
"after",
|
|
5153
|
+
`
|
|
5154
|
+
Notes:
|
|
5155
|
+
Read-only. Shows customer-facing billing history from Stripe, newest first:
|
|
5156
|
+
subscription invoices plus one-time Deepline credit purchase receipts, with
|
|
5157
|
+
Stripe-hosted links. Amounts are what you paid \u2014 never provider spend.
|
|
5158
|
+
--compact keeps id/date/amount/status/url only.
|
|
5159
|
+
|
|
5160
|
+
Examples:
|
|
5161
|
+
deepline billing invoices
|
|
5162
|
+
deepline billing invoices --limit 12 --json
|
|
5163
|
+
deepline billing invoices --compact --json
|
|
5164
|
+
`
|
|
5165
|
+
).option("--limit <n>", "Maximum entries to return (1-100, default 24)").option("--compact", "Keep only id, date, amount, status, and url").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleInvoices);
|
|
4605
5166
|
billing.command("redeem-code").description("Redeem a billing code.").addHelpText(
|
|
4606
5167
|
"after",
|
|
4607
5168
|
`
|
|
@@ -6922,7 +7483,7 @@ var PLAY_RUNTIME_PROVIDERS = {
|
|
|
6922
7483
|
runner: PLAY_RUNTIME_BACKENDS.daytona,
|
|
6923
7484
|
dedup: PLAY_DEDUP_BACKENDS.durableObject,
|
|
6924
7485
|
artifactKind: PLAY_ARTIFACT_KINDS.cjsNode20,
|
|
6925
|
-
label: "
|
|
7486
|
+
label: "BETA: Postgres Scheduler + warm sandbox runner + DO dedup"
|
|
6926
7487
|
},
|
|
6927
7488
|
postgres_fast_sandbox: {
|
|
6928
7489
|
id: PLAY_RUNTIME_PROVIDER_IDS.postgresFastSandbox,
|
|
@@ -6930,7 +7491,7 @@ var PLAY_RUNTIME_PROVIDERS = {
|
|
|
6930
7491
|
runner: PLAY_RUNTIME_BACKENDS.daytona,
|
|
6931
7492
|
dedup: PLAY_DEDUP_BACKENDS.durableObject,
|
|
6932
7493
|
artifactKind: PLAY_ARTIFACT_KINDS.cjsNode20,
|
|
6933
|
-
label: "
|
|
7494
|
+
label: "BETA: Postgres Scheduler + warm sandbox runner + DO dedup"
|
|
6934
7495
|
},
|
|
6935
7496
|
postgres_fast_workers: {
|
|
6936
7497
|
id: PLAY_RUNTIME_PROVIDER_IDS.postgresFastWorkers,
|
|
@@ -12160,7 +12721,7 @@ function writeStartedPlayRun(input2) {
|
|
|
12160
12721
|
);
|
|
12161
12722
|
}
|
|
12162
12723
|
function parsePlayRunOptions(args) {
|
|
12163
|
-
const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--profile <id>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--profile <id>] [--live|--latest|--revision-id <id>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--no-open] [--json] [--full] [--<input> value]\n Unknown --<input> value flags, such as --limit 5, are passed into play input.\nRun `deepline plays run --help` for idempotency, tool call id, and ctx.dataset guidance.";
|
|
12724
|
+
const usage = "Usage: deepline plays run <play-name> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run <play-file.ts> [--input '{...}'] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run --file <play-file.ts> [--input '{...}'] [--profile <id>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--full] [--<input> value]\n deepline plays run --name <name> [--input '{...}'] [--profile <id>] [--live|--latest|--revision-id <id>] [--no-wait] [--tail-timeout-ms 30000] [--force] [--no-open] [--json] [--full] [--<input> value]\n --profile defaults to workers_edge; postgres_fast is BETA (opt-in per run, never the default).\n Unknown --<input> value flags, such as --limit 5, are passed into play input.\nRun `deepline plays run --help` for idempotency, tool call id, and ctx.dataset guidance.";
|
|
12164
12725
|
let filePath = null;
|
|
12165
12726
|
let playName = null;
|
|
12166
12727
|
let input2 = null;
|