deepline 0.3.294 → 0.3.296
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/bundling-sources/sdk/src/compat.ts +35 -7
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +108 -35
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-api-origin.ts +25 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +14 -1
- package/dist/cli/auth-JCGIDVDF.mjs +31 -0
- package/dist/cli/auth-QDWV3TXX.js +31 -0
- package/dist/cli/billing-OQBSIXRG.mjs +1749 -0
- package/dist/cli/billing-YBK3J4WP.js +1749 -0
- package/dist/cli/chunk-2C3AVPTY.js +5688 -0
- package/dist/cli/chunk-2MZ5EQZE.mjs +499 -0
- package/dist/cli/chunk-3KC45UVF.js +150 -0
- package/dist/cli/chunk-3XX5TEA4.js +15 -0
- package/dist/cli/chunk-4MBOJQPL.mjs +0 -0
- package/dist/cli/chunk-4Y6CG4GE.js +2240 -0
- package/dist/cli/chunk-4ZB3CJRD.js +1269 -0
- package/dist/cli/chunk-5JLQPVNX.js +3056 -0
- package/dist/cli/chunk-6A3TJUWC.js +76 -0
- package/dist/cli/chunk-6HPEDBSJ.js +1995 -0
- package/dist/cli/chunk-7D6QG5KF.js +890 -0
- package/dist/cli/chunk-7P5AA3SN.js +344 -0
- package/dist/cli/chunk-7UAO5MWV.mjs +190 -0
- package/dist/cli/chunk-A7427JJH.js +190 -0
- package/dist/cli/chunk-AQRTLPHZ.js +735 -0
- package/dist/cli/chunk-AYHLUHE5.js +52 -0
- package/dist/cli/chunk-BZ77OTRF.mjs +923 -0
- package/dist/cli/chunk-CBVIJSHF.mjs +560 -0
- package/dist/cli/chunk-COGTRVWW.js +30 -0
- package/dist/cli/chunk-DLIRSHV6.mjs +28 -0
- package/dist/cli/chunk-DYWHXO7O.mjs +34 -0
- package/dist/cli/chunk-EDWJY6T4.mjs +3056 -0
- package/dist/cli/chunk-EXMLR72R.mjs +0 -0
- package/dist/cli/chunk-G46HHFNB.mjs +1269 -0
- package/dist/cli/chunk-GAMFK2ZP.mjs +148 -0
- package/dist/cli/chunk-GLKUGBTP.mjs +2240 -0
- package/dist/cli/chunk-GONRLVV3.js +3440 -0
- package/dist/cli/chunk-GVUJJJ5G.mjs +76 -0
- package/dist/cli/chunk-H7HIDGZ7.mjs +150 -0
- package/dist/cli/chunk-HUZXWOSK.mjs +503 -0
- package/dist/cli/chunk-I5YZLGMG.js +1 -0
- package/dist/cli/chunk-ITOFSXIU.mjs +15 -0
- package/dist/cli/chunk-J2D3VDOU.mjs +5688 -0
- package/dist/cli/chunk-J5NA2TTX.js +503 -0
- package/dist/cli/chunk-JCS5TPNY.mjs +3440 -0
- package/dist/cli/chunk-JNNOCMQY.js +47 -0
- package/dist/cli/chunk-KRD6H754.mjs +30 -0
- package/dist/cli/chunk-L4HBNHFW.js +34 -0
- package/dist/cli/chunk-LA46MMTL.js +560 -0
- package/dist/cli/chunk-LIKFCE7Z.js +190 -0
- package/dist/cli/chunk-MIMASXO4.mjs +389 -0
- package/dist/cli/chunk-NPPJX3BL.mjs +1995 -0
- package/dist/cli/chunk-NTEZFZLE.mjs +890 -0
- package/dist/cli/chunk-NXDRDJ4K.js +389 -0
- package/dist/cli/chunk-NXGCO4JB.mjs +344 -0
- package/dist/cli/chunk-OG64RBAC.js +6 -0
- package/dist/cli/chunk-OWJZUE3Y.mjs +103 -0
- package/dist/cli/chunk-QSIO4TX2.mjs +190 -0
- package/dist/cli/chunk-RCUE5C5C.mjs +735 -0
- package/dist/cli/chunk-SDK3ZA2C.js +103 -0
- package/dist/cli/chunk-SFLARO4Y.mjs +47 -0
- package/dist/cli/chunk-TAC3JIFE.mjs +354 -0
- package/dist/cli/chunk-UC6GHEL4.js +499 -0
- package/dist/cli/chunk-UFS752TU.mjs +52 -0
- package/dist/cli/chunk-UNEMDKXN.js +1 -0
- package/dist/cli/chunk-WB6Z2Q44.mjs +1622 -0
- package/dist/cli/chunk-WJYPBHTE.js +148 -0
- package/dist/cli/chunk-WOHBA5PV.js +354 -0
- package/dist/cli/chunk-WZYKRD7T.mjs +6 -0
- package/dist/cli/chunk-XNYIZMNQ.js +28 -0
- package/dist/cli/chunk-ZDO3TO3M.js +1622 -0
- package/dist/cli/chunk-ZDOC4HFL.js +923 -0
- package/dist/cli/client-CZDA4JKO.mjs +20 -0
- package/dist/cli/client-IZGIN2ZA.js +20 -0
- package/dist/cli/csv-6KOMZA2H.mjs +585 -0
- package/dist/cli/csv-UTEQY3NA.js +585 -0
- package/dist/cli/db-D5EP6TKO.mjs +486 -0
- package/dist/cli/db-SIBLPIKC.js +486 -0
- package/dist/cli/executions-2PHT57IO.js +28 -0
- package/dist/cli/executions-DFWM2YYY.mjs +28 -0
- package/dist/cli/feedback-GZW37JMI.mjs +107 -0
- package/dist/cli/feedback-IQVWIJZ4.js +107 -0
- package/dist/cli/getting-started.js +18 -76
- package/dist/cli/getting-started.mjs +10 -37
- package/dist/cli/git-563GYLMA.js +23 -0
- package/dist/cli/git-B72Y4FFQ.mjs +23 -0
- package/dist/cli/index.js +291 -54294
- package/dist/cli/index.mjs +209 -54352
- package/dist/cli/legacy-noops-37F5MX2P.mjs +157 -0
- package/dist/cli/legacy-noops-IFGVDCOM.js +157 -0
- package/dist/cli/monitors-42H6LOVT.mjs +3251 -0
- package/dist/cli/monitors-GQCHXBXZ.js +3251 -0
- package/dist/cli/org-4GCPBTGR.js +849 -0
- package/dist/cli/org-JYZA3ZRJ.mjs +849 -0
- package/dist/cli/play-46ZJ4FMI.js +18439 -0
- package/dist/cli/play-Q2XV3FZZ.mjs +18439 -0
- package/dist/cli/providers-CTKQP45X.mjs +95 -0
- package/dist/cli/providers-PCNF5E3K.js +95 -0
- package/dist/cli/proxy-env-EBANZHPV.js +161 -0
- package/dist/cli/proxy-env-M4ZHXHRL.mjs +161 -0
- package/dist/cli/quickstart-FTHWRVWN.js +69 -0
- package/dist/cli/quickstart-X7EVVWRG.mjs +69 -0
- package/dist/cli/search-73YPOFDO.mjs +18 -0
- package/dist/cli/search-XA5UVC3G.js +18 -0
- package/dist/cli/secrets-OUMPT4FX.mjs +400 -0
- package/dist/cli/secrets-UOEVR73Q.js +400 -0
- package/dist/cli/sessions-CMKXCN5Y.js +15 -0
- package/dist/cli/sessions-ZIFGDZE7.mjs +15 -0
- package/dist/cli/settings-OO5R3G52.mjs +846 -0
- package/dist/cli/settings-ULZT2NIC.js +846 -0
- package/dist/cli/setup-6GZH52OZ.mjs +1257 -0
- package/dist/cli/setup-DHGERXPZ.js +1257 -0
- package/dist/cli/skills-Q7NUCS2W.js +28 -0
- package/dist/cli/skills-T3EY2656.mjs +28 -0
- package/dist/cli/tools-BIMOAOTX.mjs +2955 -0
- package/dist/cli/tools-ODXVCKO7.js +2955 -0
- package/dist/cli/update-UMB3ULBR.js +28 -0
- package/dist/cli/update-X4HX7Z3M.mjs +28 -0
- package/dist/cli/workflow-EAUFVPIP.mjs +1936 -0
- package/dist/cli/workflow-IVLCPFTG.js +1936 -0
- package/dist/helpers.js +6 -32
- package/dist/helpers.mjs +2 -0
- package/dist/index.js +1972 -12825
- package/dist/index.mjs +1910 -12672
- package/dist/install-integrity.json +110 -0
- package/dist/monitor-fleet-contract.js +31 -730
- package/dist/monitor-fleet-contract.mjs +18 -667
- package/dist/monitor-input-contract.js +10 -390
- package/dist/monitor-input-contract.mjs +7 -348
- package/dist/plays/bundle-play-file.mjs +13 -5178
- package/dist/plays/csv-data-input.js +5 -3132
- package/dist/plays/csv-data-input.mjs +5 -3105
- package/dist/plays/run-request.js +56 -664
- package/dist/plays/run-request.mjs +30 -588
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +5 -212
- package/dist/release.mjs +5 -185
- package/package.json +1 -1
|
@@ -0,0 +1,1749 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
var _chunk2C3AVPTYjs = require('./chunk-2C3AVPTY.js');
|
|
5
|
+
require('./chunk-LA46MMTL.js');
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
var _chunk7D6QG5KFjs = require('./chunk-7D6QG5KF.js');
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
var _chunk6HPEDBSJjs = require('./chunk-6HPEDBSJ.js');
|
|
17
|
+
require('./chunk-XNYIZMNQ.js');
|
|
18
|
+
require('./chunk-LIKFCE7Z.js');
|
|
19
|
+
require('./chunk-6A3TJUWC.js');
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
var _chunk7P5AA3SNjs = require('./chunk-7P5AA3SN.js');
|
|
23
|
+
require('./chunk-OG64RBAC.js');
|
|
24
|
+
require('./chunk-COGTRVWW.js');
|
|
25
|
+
|
|
26
|
+
// src/cli/commands/billing.ts
|
|
27
|
+
var _commander = require('commander');
|
|
28
|
+
var _crypto = require('crypto');
|
|
29
|
+
var _promises = require('fs/promises');
|
|
30
|
+
var _path = require('path');
|
|
31
|
+
var _sync = require('csv-stringify/sync');
|
|
32
|
+
var SUBSCRIPTION_STATUS_NEXT_COMMAND = "deepline billing subscription status --json";
|
|
33
|
+
var SUBSCRIPTION_CANCEL_PATH = "/api/v2/billing/subscription/cancel";
|
|
34
|
+
var BILLING_TOP_UP_PATH = "/api/v2/billing/top-up";
|
|
35
|
+
var DEFAULT_BALANCE_RECOVERY_CREDITS = 100;
|
|
36
|
+
function billingFailureFromError(error, options) {
|
|
37
|
+
if (error instanceof _chunk6HPEDBSJjs.AuthError) {
|
|
38
|
+
return {
|
|
39
|
+
exitCode: 3,
|
|
40
|
+
code: "AUTH_ERROR",
|
|
41
|
+
message: error.message,
|
|
42
|
+
next: "deepline auth status --json"
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (!(error instanceof _chunk7P5AA3SNjs.DeeplineError) || typeof error.statusCode !== "number") {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
if (error.statusCode === 404 || error.statusCode === 409) {
|
|
49
|
+
return {
|
|
50
|
+
exitCode: 4,
|
|
51
|
+
code: options.notFoundCode,
|
|
52
|
+
message: error.message,
|
|
53
|
+
next: SUBSCRIPTION_STATUS_NEXT_COMMAND
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (error.statusCode >= 500) {
|
|
57
|
+
return {
|
|
58
|
+
exitCode: 5,
|
|
59
|
+
code: "BILLING_SERVER_ERROR",
|
|
60
|
+
message: error.message,
|
|
61
|
+
next: SUBSCRIPTION_STATUS_NEXT_COMMAND
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
function reportBillingFailure(failure, options) {
|
|
67
|
+
const textLines = [failure.message];
|
|
68
|
+
if (failure.next) {
|
|
69
|
+
textLines.push(`Next: ${failure.next}`);
|
|
70
|
+
}
|
|
71
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
72
|
+
{
|
|
73
|
+
ok: false,
|
|
74
|
+
exitCode: failure.exitCode,
|
|
75
|
+
code: failure.code,
|
|
76
|
+
message: failure.message,
|
|
77
|
+
...failure.next ? { next: failure.next } : {}
|
|
78
|
+
},
|
|
79
|
+
{ json: options.json, text: `${textLines.join("\n")}
|
|
80
|
+
` }
|
|
81
|
+
);
|
|
82
|
+
process.exitCode = failure.exitCode;
|
|
83
|
+
}
|
|
84
|
+
function parseTopUpCredits(raw) {
|
|
85
|
+
const trimmed = String(_nullishCoalesce(raw, () => ( ""))).trim();
|
|
86
|
+
if (!/^\d+$/.test(trimmed)) return null;
|
|
87
|
+
const parsed = Number(trimmed);
|
|
88
|
+
if (!Number.isSafeInteger(parsed) || parsed <= 0) return null;
|
|
89
|
+
return parsed;
|
|
90
|
+
}
|
|
91
|
+
function topUpIdempotencyKey(raw) {
|
|
92
|
+
if (typeof raw === "string") {
|
|
93
|
+
const trimmed = raw.trim();
|
|
94
|
+
if (trimmed) return trimmed.slice(0, 120);
|
|
95
|
+
}
|
|
96
|
+
return `cli_topup:${Date.now()}:${_crypto.randomUUID.call(void 0, )}`;
|
|
97
|
+
}
|
|
98
|
+
function targetBillingIdempotencyKey(raw) {
|
|
99
|
+
return typeof raw === "string" ? raw : `cli_target_billing:${Date.now()}:${_crypto.randomUUID.call(void 0, )}`;
|
|
100
|
+
}
|
|
101
|
+
function checkoutCommandForCredits(credits) {
|
|
102
|
+
return `deepline billing checkout --credits ${credits} --no-open --json`;
|
|
103
|
+
}
|
|
104
|
+
function approvedTopUpCommandForCredits(credits) {
|
|
105
|
+
return `deepline billing top-up ${credits} --json`;
|
|
106
|
+
}
|
|
107
|
+
function balanceRecoveryForCredits(credits = DEFAULT_BALANCE_RECOVERY_CREDITS) {
|
|
108
|
+
return {
|
|
109
|
+
recommended_credits: credits,
|
|
110
|
+
requires_user_approval: true,
|
|
111
|
+
message: "Ask the user whether they want to add Deepline credits before taking payment-related action.",
|
|
112
|
+
top_up_command: approvedTopUpCommandForCredits(credits),
|
|
113
|
+
checkout_command: checkoutCommandForCredits(credits)
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function shellQuote(value) {
|
|
117
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
118
|
+
}
|
|
119
|
+
function topUpRetryCommand(credits, idempotencyKey) {
|
|
120
|
+
return `deepline billing top-up ${credits} --idempotency-key ${shellQuote(idempotencyKey)} --json`;
|
|
121
|
+
}
|
|
122
|
+
function apiErrorCode(error) {
|
|
123
|
+
const response = _optionalChain([error, 'access', _ => _.details, 'optionalAccess', _2 => _2.response]);
|
|
124
|
+
if (response && typeof response === "object" && !Array.isArray(response)) {
|
|
125
|
+
const code = response.code;
|
|
126
|
+
if (typeof code === "string" && code.trim()) {
|
|
127
|
+
return code.trim();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return _nullishCoalesce(error.code, () => ( "BILLING_API_ERROR"));
|
|
131
|
+
}
|
|
132
|
+
function billingTopUpFailureFromError(error, input) {
|
|
133
|
+
if (error instanceof _chunk6HPEDBSJjs.AuthError || error instanceof _chunk6HPEDBSJjs.ConfigError) {
|
|
134
|
+
return {
|
|
135
|
+
exitCode: 3,
|
|
136
|
+
code: "AUTH_ERROR",
|
|
137
|
+
message: error.message,
|
|
138
|
+
next: "deepline auth status --json"
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
if (!(error instanceof _chunk7P5AA3SNjs.DeeplineError) || typeof error.statusCode !== "number") {
|
|
142
|
+
if (error instanceof _chunk7P5AA3SNjs.DeeplineError) {
|
|
143
|
+
return {
|
|
144
|
+
exitCode: 5,
|
|
145
|
+
code: "TOP_UP_STATUS_UNKNOWN",
|
|
146
|
+
message: `${error.message} The top-up may have reached Deepline. Retry only with the same idempotency key to avoid a duplicate charge.`,
|
|
147
|
+
next: topUpRetryCommand(input.credits, input.idempotencyKey)
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
const code = apiErrorCode(error);
|
|
153
|
+
if (error.statusCode === 400) {
|
|
154
|
+
return {
|
|
155
|
+
exitCode: 2,
|
|
156
|
+
code: "INVALID_CREDITS",
|
|
157
|
+
message: error.message,
|
|
158
|
+
next: "deepline billing plans --json"
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
if (error.statusCode === 409) {
|
|
162
|
+
const checkoutNext = checkoutCommandForCredits(input.credits);
|
|
163
|
+
return {
|
|
164
|
+
exitCode: 4,
|
|
165
|
+
code: code === "requires_action" ? "PAYMENT_REQUIRES_ACTION" : "MISSING_PAYMENT_METHOD",
|
|
166
|
+
message: error.message,
|
|
167
|
+
next: checkoutNext
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
if (error.statusCode === 402) {
|
|
171
|
+
return {
|
|
172
|
+
exitCode: 5,
|
|
173
|
+
code: "PAYMENT_FAILED",
|
|
174
|
+
message: error.message,
|
|
175
|
+
next: checkoutCommandForCredits(input.credits)
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
if (error.statusCode >= 500) {
|
|
179
|
+
return {
|
|
180
|
+
exitCode: 5,
|
|
181
|
+
code: "BILLING_SERVER_ERROR",
|
|
182
|
+
message: `${error.message} Retry only with the same idempotency key to avoid a duplicate charge.`,
|
|
183
|
+
next: topUpRetryCommand(input.credits, input.idempotencyKey)
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
function normalizeUnixMilliseconds(value) {
|
|
189
|
+
const timestamp = Math.floor(value);
|
|
190
|
+
return Number.isFinite(timestamp) && timestamp >= 0 && Number.isFinite(new Date(timestamp).getTime()) ? timestamp : null;
|
|
191
|
+
}
|
|
192
|
+
function parseSince(raw) {
|
|
193
|
+
if (!_optionalChain([raw, 'optionalAccess', _3 => _3.trim, 'call', _4 => _4()])) return null;
|
|
194
|
+
const numeric = Number(raw);
|
|
195
|
+
const normalizedNumeric = normalizeUnixMilliseconds(numeric);
|
|
196
|
+
if (normalizedNumeric !== null) return normalizedNumeric;
|
|
197
|
+
const parsed = Date.parse(raw);
|
|
198
|
+
return normalizeUnixMilliseconds(parsed);
|
|
199
|
+
}
|
|
200
|
+
function humanize(value) {
|
|
201
|
+
return String(value || "").split("_").filter(Boolean).map((token) => _optionalChain([token, 'access', _5 => _5[0], 'optionalAccess', _6 => _6.toUpperCase, 'call', _7 => _7()]) + token.slice(1)).join(" ") || "Unknown";
|
|
202
|
+
}
|
|
203
|
+
function recentUsageLines(entries) {
|
|
204
|
+
if (entries.length === 0) {
|
|
205
|
+
return ["Recent activity: none yet"];
|
|
206
|
+
}
|
|
207
|
+
const lines = ["Recent activity:"];
|
|
208
|
+
for (const entry of entries) {
|
|
209
|
+
const op = `${humanize(entry.provider)} ${humanize(entry.operation)}`.trim();
|
|
210
|
+
const roughUsd = Number(entry.rough_usd_cost);
|
|
211
|
+
const credits = entry.credits !== null && entry.credits !== void 0 && String(entry.credits).trim().length > 0 && Number.isFinite(Number(entry.credits)) ? Number(entry.credits) : null;
|
|
212
|
+
const temporaryHold = entry.charge_state === "temporary_hold";
|
|
213
|
+
const pending = entry.charge_state === "pending" || entry.charge_finality === "pending";
|
|
214
|
+
const unknown = entry.charge_finality === "unknown" || credits === null;
|
|
215
|
+
const usdText = !pending && !unknown && entry.rough_usd_cost !== null && entry.rough_usd_cost !== void 0 && Number.isFinite(roughUsd) ? ` (\u2248$${roughUsd.toFixed(2)})` : "";
|
|
216
|
+
const charge = temporaryHold ? `${credits === null ? "unknown amount" : `${credits} cr`} held${usdText}, not final` : pending ? "final Deepline charge pending" : unknown ? "charge unknown" : entry.billing_mode === "no_bill" ? "free" : credits === 0 && (entry.charge_finality === "final" || entry.charge_state === "hold_release") ? "0 cr, final" : `${credits} cr${usdText}`;
|
|
217
|
+
const state = temporaryHold ? "temporary hold, final charge pending" : pending ? "no final charge recorded yet" : entry.status || "unknown";
|
|
218
|
+
const auditBits = [
|
|
219
|
+
entry.outcome ? `outcome=${entry.outcome}` : null,
|
|
220
|
+
entry.pricing_basis ? `priced_by=${entry.pricing_basis}` : null,
|
|
221
|
+
entry.run_id ? `run=${entry.run_id}` : null,
|
|
222
|
+
entry.play_run_id || entry.execution_run_id ? `play_run=${_nullishCoalesce(entry.play_run_id, () => ( entry.execution_run_id))}` : null,
|
|
223
|
+
entry.billing_run_id ? `billing_run=${entry.billing_run_id}` : null,
|
|
224
|
+
entry.workflow_id ? `workflow=${entry.workflow_id}` : null,
|
|
225
|
+
entry.step_alias ? `step=${entry.step_alias}` : null,
|
|
226
|
+
entry.request_id ? `request=${entry.request_id}` : null
|
|
227
|
+
].filter(Boolean);
|
|
228
|
+
lines.push(
|
|
229
|
+
`${op} | ${charge} | ${state}${auditBits.length > 0 ? ` | ${auditBits.join(" | ")}` : ""} | ${entry.created_at || "unknown"}`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
return lines;
|
|
233
|
+
}
|
|
234
|
+
function summarizeLedgerRows(summary, rows) {
|
|
235
|
+
const netDelta = rows.reduce((sum, row) => {
|
|
236
|
+
const value = Number.parseFloat(String(_nullishCoalesce(row.delta_credits, () => ( ""))).trim());
|
|
237
|
+
return Number.isFinite(value) ? sum + value : sum;
|
|
238
|
+
}, summary.net_delta_credits);
|
|
239
|
+
return {
|
|
240
|
+
row_count: summary.row_count + rows.length,
|
|
241
|
+
net_delta_credits: Math.round((netDelta + Number.EPSILON) * 100) / 100
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
function ledgerApiEntryToRow(entry) {
|
|
245
|
+
const metadata = typeof entry.metadata === "object" && entry.metadata !== null ? entry.metadata : {};
|
|
246
|
+
const audit = typeof entry.billing_audit === "object" && entry.billing_audit !== null ? entry.billing_audit : {};
|
|
247
|
+
return {
|
|
248
|
+
created_at: _nullishCoalesce(entry.created_at, () => ( "")),
|
|
249
|
+
delta_credits: _nullishCoalesce(entry.delta, () => ( "")),
|
|
250
|
+
reason: _nullishCoalesce(entry.reason, () => ( "")),
|
|
251
|
+
provider: _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(entry.provider, () => ( audit.provider)), () => ( metadata.provider)), () => ( "")),
|
|
252
|
+
operation: _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(entry.operation, () => ( audit.operation)), () => ( metadata.operation)), () => ( "")),
|
|
253
|
+
request_id: _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_nullishCoalesce(entry.request_id, () => ( audit.request_id)), () => ( metadata.requestId)), () => ( metadata.request_id)), () => ( "")),
|
|
254
|
+
run_id: _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_nullishCoalesce(entry.run_id, () => ( audit.run_id)), () => ( metadata.runId)), () => ( metadata.run_id)), () => ( "")),
|
|
255
|
+
billing_stage: _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(entry.billing_stage, () => ( audit.billing_stage)), () => ( metadata.billingStage)), () => ( "")),
|
|
256
|
+
charge_state: _nullishCoalesce(_nullishCoalesce(entry.charge_state, () => ( audit.charge_state)), () => ( "")),
|
|
257
|
+
billing_mode: _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_nullishCoalesce(entry.billing_mode, () => ( audit.billing_mode)), () => ( metadata.billingMode)), () => ( metadata.chargeMode)), () => ( "")),
|
|
258
|
+
pricing_model: _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(entry.pricing_model, () => ( audit.pricing_model)), () => ( metadata.pricingModel)), () => ( "")),
|
|
259
|
+
pricing_basis: _nullishCoalesce(_nullishCoalesce(entry.pricing_basis, () => ( audit.pricing_basis)), () => ( "")),
|
|
260
|
+
charge_credits: _nullishCoalesce(_nullishCoalesce(entry.charge_credits, () => ( audit.charge_credits)), () => ( "")),
|
|
261
|
+
outcome: _nullishCoalesce(_nullishCoalesce(entry.outcome, () => ( audit.outcome)), () => ( "")),
|
|
262
|
+
result_count: _nullishCoalesce(_nullishCoalesce(entry.result_count, () => ( audit.result_count)), () => ( "")),
|
|
263
|
+
deepline_rough_usd: _nullishCoalesce(entry.deepline_rough_usd, () => ( "")),
|
|
264
|
+
api_key_id: _nullishCoalesce(entry.api_key_id, () => ( "")),
|
|
265
|
+
stripe_session_id: _nullishCoalesce(entry.stripe_session_id, () => ( ""))
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function ledgerRowsToCsv(rows, header) {
|
|
269
|
+
return _sync.stringify.call(void 0, rows, {
|
|
270
|
+
header,
|
|
271
|
+
columns: [
|
|
272
|
+
"created_at",
|
|
273
|
+
"delta_credits",
|
|
274
|
+
"reason",
|
|
275
|
+
"provider",
|
|
276
|
+
"operation",
|
|
277
|
+
"request_id",
|
|
278
|
+
"run_id",
|
|
279
|
+
"billing_stage",
|
|
280
|
+
"charge_state",
|
|
281
|
+
"billing_mode",
|
|
282
|
+
"pricing_model",
|
|
283
|
+
"pricing_basis",
|
|
284
|
+
"charge_credits",
|
|
285
|
+
"outcome",
|
|
286
|
+
"result_count",
|
|
287
|
+
"deepline_rough_usd",
|
|
288
|
+
"api_key_id",
|
|
289
|
+
"stripe_session_id"
|
|
290
|
+
]
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
function defaultLedgerExportPath() {
|
|
294
|
+
return _path.resolve.call(void 0,
|
|
295
|
+
process.cwd(),
|
|
296
|
+
"deepline",
|
|
297
|
+
"data",
|
|
298
|
+
`billing-ledger-all-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.csv`
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
async function handleBalance(options) {
|
|
302
|
+
const { http } = _chunk7D6QG5KFjs.getAuthedHttpClient.call(void 0, );
|
|
303
|
+
const payload = await http.get(
|
|
304
|
+
"/api/v2/billing/balance"
|
|
305
|
+
);
|
|
306
|
+
const status = String(payload.balance_status || "");
|
|
307
|
+
const needsCredits = status === "no_billing" || status === "zero_balance" || status === "payment_method_only";
|
|
308
|
+
const recovery = needsCredits ? balanceRecoveryForCredits() : null;
|
|
309
|
+
const lines = [`Balance: ${_nullishCoalesce(payload.balance, () => ( "(unknown)"))} credits`];
|
|
310
|
+
const temporaryHolds = payload.temporary_hold_credits;
|
|
311
|
+
if (typeof temporaryHolds === "number" && temporaryHolds > 0) {
|
|
312
|
+
lines.push(`Open reservation exposure: ${temporaryHolds} credits.`);
|
|
313
|
+
} else if (temporaryHolds === null) {
|
|
314
|
+
lines.push("Temporary reservation status is temporarily unavailable.");
|
|
315
|
+
}
|
|
316
|
+
if (payload.temporary_hold_credits_truncated === true) {
|
|
317
|
+
lines.push("Temporary reservation total may be incomplete.");
|
|
318
|
+
}
|
|
319
|
+
if (typeof payload.estimated_hold_credits === "number") {
|
|
320
|
+
lines.push(
|
|
321
|
+
`Estimated holds: ${payload.estimated_hold_credits} credits \u2014 final usage is not recorded yet.`
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
if (typeof payload.incurred_unsettled_credits === "number") {
|
|
325
|
+
lines.push(
|
|
326
|
+
`Incurred, awaiting settlement: ${payload.incurred_unsettled_credits} credits.`
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
if (typeof payload.pending_wallet_debit_credits === "number") {
|
|
330
|
+
lines.push(
|
|
331
|
+
`Pending wallet debit: ${payload.pending_wallet_debit_credits} credits \u2014 excludes amounts already debited for funded holds.`
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
if (typeof payload.pending_wallet_refund_credits === "number" && payload.pending_wallet_refund_credits > 0) {
|
|
335
|
+
lines.push(
|
|
336
|
+
`Pending wallet refund: ${payload.pending_wallet_refund_credits} credits.`
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
if (payload.exposure_status === "partial") {
|
|
340
|
+
lines.push(
|
|
341
|
+
"Exposure amounts are lower bounds: some reservations are unpriced or the scan is incomplete."
|
|
342
|
+
);
|
|
343
|
+
} else if (payload.exposure_status !== "complete") {
|
|
344
|
+
lines.push(
|
|
345
|
+
"The estimated and incurred usage breakdown is temporarily unavailable."
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
if (status === "no_billing") {
|
|
349
|
+
lines.push(
|
|
350
|
+
"Billing: No billing account or payment method set up for this workspace."
|
|
351
|
+
);
|
|
352
|
+
} else if (status === "zero_balance") {
|
|
353
|
+
lines.push("Billing: This workspace has no Deepline credits available.");
|
|
354
|
+
} else if (status === "payment_method_only") {
|
|
355
|
+
lines.push(
|
|
356
|
+
"Billing: A payment method is saved, but this workspace has no Deepline credits available."
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
if (recovery) {
|
|
360
|
+
lines.push(recovery.message);
|
|
361
|
+
lines.push(`If the user approves, run: ${recovery.top_up_command}`);
|
|
362
|
+
lines.push(`If checkout is required, run: ${recovery.checkout_command}`);
|
|
363
|
+
}
|
|
364
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
365
|
+
{
|
|
366
|
+
...payload,
|
|
367
|
+
...recovery ? { recovery } : {},
|
|
368
|
+
render: { sections: [{ title: "billing balance", lines }] }
|
|
369
|
+
},
|
|
370
|
+
{ json: options.json }
|
|
371
|
+
);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
function quoteShellArgument(value) {
|
|
375
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
376
|
+
}
|
|
377
|
+
async function handleUsage(options) {
|
|
378
|
+
const { http } = _chunk7D6QG5KFjs.getAuthedHttpClient.call(void 0, );
|
|
379
|
+
const params = new URLSearchParams();
|
|
380
|
+
if (options.requestId !== void 0) {
|
|
381
|
+
const requestId = options.requestId.trim();
|
|
382
|
+
if (requestId.length === 0 || requestId.length > 200 || requestId !== options.requestId) {
|
|
383
|
+
reportBillingFailure(
|
|
384
|
+
{
|
|
385
|
+
exitCode: 2,
|
|
386
|
+
code: "INVALID_REQUEST_ID",
|
|
387
|
+
message: "--request-id must contain 1\u2013200 characters with no leading or trailing whitespace."
|
|
388
|
+
},
|
|
389
|
+
options
|
|
390
|
+
);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (options.offset || options.cursor || options.runId) {
|
|
394
|
+
reportBillingFailure(
|
|
395
|
+
{
|
|
396
|
+
exitCode: 2,
|
|
397
|
+
code: "CONFLICTING_USAGE_FILTERS",
|
|
398
|
+
message: "--request-id cannot be combined with --offset, --cursor, or --run-id."
|
|
399
|
+
},
|
|
400
|
+
options
|
|
401
|
+
);
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
params.set("request_id", requestId);
|
|
405
|
+
} else {
|
|
406
|
+
if (options.limit) params.set("recent_limit", options.limit);
|
|
407
|
+
if (options.offset) params.set("recent_offset", options.offset);
|
|
408
|
+
if (options.cursor) params.set("recent_cursor", options.cursor);
|
|
409
|
+
if (options.runId) params.set("run_id", options.runId);
|
|
410
|
+
}
|
|
411
|
+
const suffix = Array.from(params).length > 0 ? `?${params.toString()}` : "";
|
|
412
|
+
let payload;
|
|
413
|
+
try {
|
|
414
|
+
payload = await http.get(
|
|
415
|
+
`/api/v2/billing/usage${suffix}`
|
|
416
|
+
);
|
|
417
|
+
} catch (error) {
|
|
418
|
+
if (options.requestId && error instanceof _chunk7P5AA3SNjs.DeeplineError && error.statusCode === 404) {
|
|
419
|
+
reportBillingFailure(
|
|
420
|
+
{
|
|
421
|
+
exitCode: 4,
|
|
422
|
+
code: "USAGE_REQUEST_NOT_FOUND",
|
|
423
|
+
message: `No billing record for request_id ${options.requestId} in the active workspace.`,
|
|
424
|
+
next: "deepline billing usage --json"
|
|
425
|
+
},
|
|
426
|
+
options
|
|
427
|
+
);
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
throw error;
|
|
431
|
+
}
|
|
432
|
+
const usage = _nullishCoalesce(payload.usage, () => ( {}));
|
|
433
|
+
const quota = _nullishCoalesce(payload.quota, () => ( {}));
|
|
434
|
+
const recent = _nullishCoalesce(payload.recent, () => ( {}));
|
|
435
|
+
const last30Usage = _nullishCoalesce(usage.last_30_days_usage_credits, () => ( usage.month_spent_credits));
|
|
436
|
+
const temporaryHolds = Number(_nullishCoalesce(usage.temporary_hold_credits, () => ( 0)));
|
|
437
|
+
const nextPage = typeof recent.next_cursor === "string" && recent.next_cursor.length > 0 ? `deepline billing usage --cursor ${quoteShellArgument(recent.next_cursor)}${options.limit ? ` --limit ${options.limit}` : ""}${options.runId ? ` --run-id ${quoteShellArgument(options.runId)}` : ""} --json` : null;
|
|
438
|
+
const recentLines = recentUsageLines(
|
|
439
|
+
Array.isArray(recent.entries) ? recent.entries : []
|
|
440
|
+
);
|
|
441
|
+
const matchingActivity = typeof recent.total === "number" && Number.isFinite(recent.total) && recent.total >= 0 && typeof recent.total_is_exact === "boolean" ? recent.total_is_exact ? `Matching activity: ${recent.total}` : typeof recent.total_lower_bound === "number" && Number.isFinite(recent.total_lower_bound) && recent.total_lower_bound >= 0 ? `Matching activity: at least ${recent.total_lower_bound}` : `Matching activity: count not exact (reported ${recent.total})` : null;
|
|
442
|
+
const lines = options.requestId ? [`Charge lookup: ${options.requestId}`, ...recentLines] : [
|
|
443
|
+
`Balance: ${_nullishCoalesce(payload.balance, () => ( "(unknown)"))}`,
|
|
444
|
+
`Last 30 days usage: ${_nullishCoalesce(last30Usage, () => ( "(unknown)"))}`,
|
|
445
|
+
...Number.isFinite(temporaryHolds) && temporaryHolds > 0 ? [`Temporary holds: ${temporaryHolds}`] : [],
|
|
446
|
+
`Monthly usage cap: ${quota.enabled ? _nullishCoalesce(quota.monthly_credits_limit, () => ( "(unknown)")) : "off"}`,
|
|
447
|
+
...options.runId && recent.run_id ? [
|
|
448
|
+
`Run spend: ${_nullishCoalesce((_nullishCoalesce(payload.run, () => ( {}))).spent_credits, () => ( "(unknown)"))} Deepline Credits`
|
|
449
|
+
] : [],
|
|
450
|
+
...matchingActivity ? [matchingActivity] : [],
|
|
451
|
+
...recentLines,
|
|
452
|
+
...recent.has_more === true ? ["More activity pages are available."] : [],
|
|
453
|
+
...recent.incomplete === true ? ["Recent activity is incomplete; the matching count may be higher."] : [],
|
|
454
|
+
...nextPage ? [`Next page: ${nextPage}`] : [],
|
|
455
|
+
...!nextPage && (recent.has_more === true || recent.incomplete === true) ? [
|
|
456
|
+
"No continuation is available for this view. Use deepline billing ledger export all --json for the full ledger."
|
|
457
|
+
] : []
|
|
458
|
+
];
|
|
459
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
460
|
+
{
|
|
461
|
+
...payload,
|
|
462
|
+
recent: {
|
|
463
|
+
...recent,
|
|
464
|
+
is_truncated: recent.has_more === true || recent.incomplete === true
|
|
465
|
+
},
|
|
466
|
+
next: { page: nextPage },
|
|
467
|
+
render: { sections: [{ title: "billing usage", lines }] }
|
|
468
|
+
},
|
|
469
|
+
{ json: options.json }
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
async function handleLimit(options) {
|
|
473
|
+
const { http } = _chunk7D6QG5KFjs.getAuthedHttpClient.call(void 0, );
|
|
474
|
+
const payload = await http.get(
|
|
475
|
+
"/api/v2/billing/limit"
|
|
476
|
+
);
|
|
477
|
+
const lines = payload.enabled ? [
|
|
478
|
+
`Monthly usage cap: ${_nullishCoalesce(payload.monthly_credits_limit, () => ( "(unknown)"))}`,
|
|
479
|
+
"Enforcement: checked before new calls; in-flight work can exceed the cap."
|
|
480
|
+
] : ["Monthly usage cap: off"];
|
|
481
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
482
|
+
{
|
|
483
|
+
...payload,
|
|
484
|
+
render: { sections: [{ title: "billing limit", lines }] }
|
|
485
|
+
},
|
|
486
|
+
{ json: options.json }
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
async function handleSetLimit(credits, options) {
|
|
490
|
+
const { http } = _chunk7D6QG5KFjs.getAuthedHttpClient.call(void 0, );
|
|
491
|
+
const payload = await http.request("/api/v2/billing/limit", {
|
|
492
|
+
method: "PUT",
|
|
493
|
+
body: { monthly_credits_limit: Number.parseInt(credits, 10) }
|
|
494
|
+
});
|
|
495
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
496
|
+
{
|
|
497
|
+
...payload,
|
|
498
|
+
render: {
|
|
499
|
+
sections: [
|
|
500
|
+
{
|
|
501
|
+
title: "billing limit",
|
|
502
|
+
lines: [`Monthly usage cap set to ${credits} credits.`]
|
|
503
|
+
}
|
|
504
|
+
]
|
|
505
|
+
}
|
|
506
|
+
},
|
|
507
|
+
{ json: options.json }
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
async function handleLimitOff(options) {
|
|
511
|
+
const { http } = _chunk7D6QG5KFjs.getAuthedHttpClient.call(void 0, );
|
|
512
|
+
const payload = await http.request("/api/v2/billing/limit", {
|
|
513
|
+
method: "DELETE"
|
|
514
|
+
});
|
|
515
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
516
|
+
{
|
|
517
|
+
...payload,
|
|
518
|
+
render: {
|
|
519
|
+
sections: [
|
|
520
|
+
{
|
|
521
|
+
title: "billing limit",
|
|
522
|
+
lines: ["Monthly usage cap is now off."]
|
|
523
|
+
}
|
|
524
|
+
]
|
|
525
|
+
}
|
|
526
|
+
},
|
|
527
|
+
{ json: options.json }
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
async function exportLedger(http, outputPath, options) {
|
|
531
|
+
let summary = { row_count: 0, net_delta_credits: 0 };
|
|
532
|
+
let initialized = false;
|
|
533
|
+
let pageNumber = 0;
|
|
534
|
+
console.error(`Exporting billing ledger to ${outputPath}`);
|
|
535
|
+
for await (const entries of _chunk2C3AVPTYjs.iterateCursorPages.call(void 0, async (cursor) => {
|
|
536
|
+
pageNumber += 1;
|
|
537
|
+
console.error(
|
|
538
|
+
`Fetching page ${pageNumber}; ${summary.row_count} rows written.`
|
|
539
|
+
);
|
|
540
|
+
const params = new URLSearchParams({ limit: "5000" });
|
|
541
|
+
if (cursor) params.set("cursor", cursor);
|
|
542
|
+
if (options.sinceAt !== null)
|
|
543
|
+
params.set("since_at", String(options.sinceAt));
|
|
544
|
+
if (options.runId) params.set("run_id", options.runId);
|
|
545
|
+
if (options.operation) params.set("operation", options.operation);
|
|
546
|
+
const payload = await http.get(
|
|
547
|
+
`/api/v2/billing/ledger?${params.toString()}`
|
|
548
|
+
);
|
|
549
|
+
if (!Array.isArray(payload.entries) || typeof payload.has_more !== "boolean") {
|
|
550
|
+
throw new Error("Billing ledger response is missing entries");
|
|
551
|
+
}
|
|
552
|
+
return {
|
|
553
|
+
items: payload.entries,
|
|
554
|
+
hasMore: payload.has_more === true,
|
|
555
|
+
cursor: typeof payload.next_cursor === "string" ? payload.next_cursor : null
|
|
556
|
+
};
|
|
557
|
+
})) {
|
|
558
|
+
if (!initialized) {
|
|
559
|
+
await _promises.mkdir.call(void 0, _path.dirname.call(void 0, outputPath), { recursive: true });
|
|
560
|
+
await _promises.writeFile.call(void 0, outputPath, ledgerRowsToCsv([], true), "utf-8");
|
|
561
|
+
initialized = true;
|
|
562
|
+
}
|
|
563
|
+
const rows = entries.map(ledgerApiEntryToRow);
|
|
564
|
+
await _promises.appendFile.call(void 0, outputPath, ledgerRowsToCsv(rows, false), "utf-8");
|
|
565
|
+
summary = summarizeLedgerRows(summary, rows);
|
|
566
|
+
console.error(
|
|
567
|
+
`Wrote page ${pageNumber}; ${summary.row_count} rows written.`
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
console.error(`Export complete: ${summary.row_count} rows written.`);
|
|
571
|
+
return summary;
|
|
572
|
+
}
|
|
573
|
+
async function handleHistory(options) {
|
|
574
|
+
const { http } = _chunk7D6QG5KFjs.getAuthedHttpClient.call(void 0, );
|
|
575
|
+
const windows = {
|
|
576
|
+
"1d": 86400,
|
|
577
|
+
"1w": 604800,
|
|
578
|
+
"1m": 2592e3,
|
|
579
|
+
"1y": 31536e3
|
|
580
|
+
};
|
|
581
|
+
const sinceAt = Math.max(0, Math.floor(Date.now() / 1e3) - windows[options.time]) * 1e3;
|
|
582
|
+
const outputPath = _path.resolve.call(void 0,
|
|
583
|
+
process.cwd(),
|
|
584
|
+
"deepline",
|
|
585
|
+
"data",
|
|
586
|
+
_chunk7D6QG5KFjs.createTimestampedName.call(void 0, `billing-history-${options.time}`, "csv")
|
|
587
|
+
);
|
|
588
|
+
const summary = await exportLedger(http, outputPath, { sinceAt });
|
|
589
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
590
|
+
{
|
|
591
|
+
output_path: outputPath,
|
|
592
|
+
row_count: summary.row_count,
|
|
593
|
+
has_more: false,
|
|
594
|
+
is_truncated: false,
|
|
595
|
+
next_cursor: null,
|
|
596
|
+
time_window: options.time,
|
|
597
|
+
render: {
|
|
598
|
+
sections: [
|
|
599
|
+
{
|
|
600
|
+
title: "billing history",
|
|
601
|
+
lines: [
|
|
602
|
+
`Billing history written to ${outputPath}`,
|
|
603
|
+
`${summary.row_count} row(s) exported.`
|
|
604
|
+
]
|
|
605
|
+
}
|
|
606
|
+
]
|
|
607
|
+
},
|
|
608
|
+
local: { output_path: outputPath }
|
|
609
|
+
},
|
|
610
|
+
{ json: options.json }
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
async function handleLedgerExportAll(options) {
|
|
614
|
+
const sinceAt = parseSince(options.since);
|
|
615
|
+
if (options.since && sinceAt === null) {
|
|
616
|
+
reportBillingFailure(
|
|
617
|
+
{
|
|
618
|
+
exitCode: 2,
|
|
619
|
+
code: "INVALID_SINCE",
|
|
620
|
+
message: "--since must be an ISO-8601 timestamp or Unix milliseconds."
|
|
621
|
+
},
|
|
622
|
+
options
|
|
623
|
+
);
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
const { http } = _chunk7D6QG5KFjs.getAuthedHttpClient.call(void 0, );
|
|
627
|
+
const outputPath = options.output ? _path.resolve.call(void 0, String(options.output)) : defaultLedgerExportPath();
|
|
628
|
+
const summary = await exportLedger(http, outputPath, {
|
|
629
|
+
sinceAt,
|
|
630
|
+
runId: options.runId,
|
|
631
|
+
operation: options.operation
|
|
632
|
+
});
|
|
633
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
634
|
+
{
|
|
635
|
+
output_path: outputPath,
|
|
636
|
+
row_count: summary.row_count,
|
|
637
|
+
has_more: false,
|
|
638
|
+
is_truncated: false,
|
|
639
|
+
next_cursor: null,
|
|
640
|
+
net_delta_credits: summary.net_delta_credits,
|
|
641
|
+
scope: "workspace",
|
|
642
|
+
...options.runId ? { run_id: options.runId } : {},
|
|
643
|
+
...options.operation ? { operation: options.operation } : {},
|
|
644
|
+
...sinceAt !== null ? { since_at: sinceAt } : {},
|
|
645
|
+
render: {
|
|
646
|
+
sections: [
|
|
647
|
+
{
|
|
648
|
+
title: "billing ledger",
|
|
649
|
+
lines: [
|
|
650
|
+
`Billing ledger written to ${outputPath}`,
|
|
651
|
+
`${summary.row_count} row(s) exported for the active workspace${options.runId ? ` and run ${options.runId}` : ""}${options.operation ? ` and operation ${options.operation}` : ""}${sinceAt !== null ? ` since ${new Date(sinceAt).toISOString()}` : ""}.`,
|
|
652
|
+
`Net ledger delta: ${summary.net_delta_credits} Deepline Credits`
|
|
653
|
+
]
|
|
654
|
+
}
|
|
655
|
+
]
|
|
656
|
+
},
|
|
657
|
+
local: { output_path: outputPath }
|
|
658
|
+
},
|
|
659
|
+
{ json: options.json }
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
function planPriceText(priceUsd, priceInterval) {
|
|
663
|
+
if (priceUsd === null || priceUsd === void 0) {
|
|
664
|
+
return "no list price";
|
|
665
|
+
}
|
|
666
|
+
return `$${priceUsd}${priceInterval ? `/${priceInterval}` : ""}`;
|
|
667
|
+
}
|
|
668
|
+
function targetPlanLine(plan) {
|
|
669
|
+
return `${plan.name} (${plan.sku}) | ${planPriceText(plan.recurring_price_usd, plan.interval)} | ${plan.included_credits} included credits/month | $${plan.purchased_credit_price_usd}/purchased credit | ${plan.acquisition_enabled ? "available" : "unavailable"}`;
|
|
670
|
+
}
|
|
671
|
+
async function handlePlans(options) {
|
|
672
|
+
const client = new (0, _chunk2C3AVPTYjs.DeeplineClient)();
|
|
673
|
+
const payload = await client.billing.targetPlans();
|
|
674
|
+
const listedCurrentPlan = payload.plans.find(
|
|
675
|
+
(plan) => plan.sku === payload.current_plan_sku
|
|
676
|
+
);
|
|
677
|
+
const currentPlan = await _asyncNullishCoalesce(listedCurrentPlan, async () => ( (await client.billing.targetStatus()).plan));
|
|
678
|
+
const lines = [
|
|
679
|
+
`Current plan: ${currentPlan.name} (${currentPlan.sku}) | ${planPriceText(currentPlan.recurring_price_usd, currentPlan.interval)} | ${_nullishCoalesce(currentPlan.included_credits, () => ( "(unknown)"))} included credits/month`,
|
|
680
|
+
`Pending plan: ${_nullishCoalesce(payload.pending_plan_sku, () => ( "none"))}`,
|
|
681
|
+
`Plan changes: ${payload.acquisition_enabled ? "enabled" : "disabled"}`,
|
|
682
|
+
...payload.plans.length === 0 ? ["Plans: none available"] : ["Plans:", ...payload.plans.map(targetPlanLine)],
|
|
683
|
+
"Change plan: deepline billing change-plan <plan_sku> --json"
|
|
684
|
+
];
|
|
685
|
+
const legacyPayload = _chunk7D6QG5KFjs.shouldEmitJson.call(void 0, options.json) ? await client.billing.plans() : null;
|
|
686
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
687
|
+
{
|
|
688
|
+
..._nullishCoalesce(legacyPayload, () => ( payload)),
|
|
689
|
+
...legacyPayload ? { target_billing: payload } : {},
|
|
690
|
+
render: { sections: [{ title: "billing plans", lines }] }
|
|
691
|
+
},
|
|
692
|
+
{ json: options.json }
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
async function handleSubscribe(planVersionId, options) {
|
|
696
|
+
const { http } = _chunk7D6QG5KFjs.getAuthedHttpClient.call(void 0, );
|
|
697
|
+
const payload = await http.request(
|
|
698
|
+
"/api/v2/billing/subscription/checkout",
|
|
699
|
+
{
|
|
700
|
+
method: "POST",
|
|
701
|
+
body: { plan_version_id: planVersionId }
|
|
702
|
+
}
|
|
703
|
+
);
|
|
704
|
+
const url = String(payload.checkout_url || "");
|
|
705
|
+
if (!options.json && options.open !== false && url) _chunk7D6QG5KFjs.openInBrowser.call(void 0, url);
|
|
706
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
707
|
+
{
|
|
708
|
+
...payload,
|
|
709
|
+
render: {
|
|
710
|
+
sections: [
|
|
711
|
+
{
|
|
712
|
+
title: "billing subscribe",
|
|
713
|
+
lines: [url || "Subscription checkout session created."]
|
|
714
|
+
}
|
|
715
|
+
]
|
|
716
|
+
}
|
|
717
|
+
},
|
|
718
|
+
{ json: options.json }
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
async function handleSubscriptionStatus(options) {
|
|
722
|
+
const client = new (0, _chunk2C3AVPTYjs.DeeplineClient)();
|
|
723
|
+
const payload = await client.billing.subscription.status();
|
|
724
|
+
const pools = _nullishCoalesce(payload.credit_pools, () => ( []));
|
|
725
|
+
const lines = [
|
|
726
|
+
`Plan: ${_nullishCoalesce(payload.plan_name, () => ( "(unknown)"))} (${_nullishCoalesce(payload.plan_version_id, () => ( "(unknown)"))})`,
|
|
727
|
+
`Subscribed: ${payload.subscribed ? "yes" : "no"}`,
|
|
728
|
+
...payload.cancel_at_period_end ? [
|
|
729
|
+
`Cancellation scheduled: ends ${_nullishCoalesce(payload.current_period_end, () => ( "(unknown)"))} at period end (credits are kept)`
|
|
730
|
+
] : [],
|
|
731
|
+
`Price: ${planPriceText(payload.price_usd, payload.price_interval)}`,
|
|
732
|
+
`Monthly grant: ${_nullishCoalesce(payload.monthly_grant_credits, () => ( 0))} credits`,
|
|
733
|
+
`Pooled credits remaining: ${_nullishCoalesce(payload.pooled_credits_remaining, () => ( 0))}`,
|
|
734
|
+
...pools.length === 0 ? ["Credit pools: none"] : [
|
|
735
|
+
"Credit pools:",
|
|
736
|
+
...pools.map(
|
|
737
|
+
(pool) => `${_nullishCoalesce(pool.pool, () => ( "(unknown)"))} | ${_nullishCoalesce(pool.credits_remaining, () => ( 0))}/${_nullishCoalesce(pool.credits_granted, () => ( 0))} credits remaining | ${_nullishCoalesce(pool.source, () => ( "(unknown)"))}`
|
|
738
|
+
)
|
|
739
|
+
],
|
|
740
|
+
`Assigned by: ${_nullishCoalesce(payload.assigned_by, () => ( "default"))}`
|
|
741
|
+
];
|
|
742
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
743
|
+
{
|
|
744
|
+
...payload,
|
|
745
|
+
render: { sections: [{ title: "billing subscription", lines }] }
|
|
746
|
+
},
|
|
747
|
+
{ json: options.json }
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
async function handleSubscriptionCancel(options) {
|
|
751
|
+
const client = new (0, _chunk2C3AVPTYjs.DeeplineClient)();
|
|
752
|
+
const action = options.undo ? "undo_cancel" : "cancel";
|
|
753
|
+
if (options.dryRun) {
|
|
754
|
+
const status = await client.billing.subscription.status();
|
|
755
|
+
if (!status.subscribed) {
|
|
756
|
+
reportBillingFailure(
|
|
757
|
+
{
|
|
758
|
+
exitCode: 4,
|
|
759
|
+
code: "NO_ACTIVE_SUBSCRIPTION",
|
|
760
|
+
message: "This workspace has no active subscription to cancel.",
|
|
761
|
+
next: SUBSCRIPTION_STATUS_NEXT_COMMAND
|
|
762
|
+
},
|
|
763
|
+
options
|
|
764
|
+
);
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
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.";
|
|
768
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
769
|
+
{
|
|
770
|
+
ok: true,
|
|
771
|
+
dry_run: true,
|
|
772
|
+
action,
|
|
773
|
+
plan_version_id: status.plan_version_id,
|
|
774
|
+
plan_name: status.plan_name,
|
|
775
|
+
cancel_at_period_end: status.cancel_at_period_end,
|
|
776
|
+
current_period_end: status.current_period_end,
|
|
777
|
+
consequence,
|
|
778
|
+
planned_request: {
|
|
779
|
+
method: "POST",
|
|
780
|
+
path: SUBSCRIPTION_CANCEL_PATH,
|
|
781
|
+
body: { action }
|
|
782
|
+
},
|
|
783
|
+
render: {
|
|
784
|
+
sections: [
|
|
785
|
+
{
|
|
786
|
+
title: "billing subscription cancel (dry run)",
|
|
787
|
+
lines: [
|
|
788
|
+
`Plan: ${status.plan_name} (${status.plan_version_id})`,
|
|
789
|
+
`Planned request: POST ${SUBSCRIPTION_CANCEL_PATH} {"action":"${action}"}`,
|
|
790
|
+
consequence,
|
|
791
|
+
"No request was sent. Re-run without --dry-run to apply."
|
|
792
|
+
]
|
|
793
|
+
}
|
|
794
|
+
]
|
|
795
|
+
}
|
|
796
|
+
},
|
|
797
|
+
{ json: options.json }
|
|
798
|
+
);
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
let result;
|
|
802
|
+
try {
|
|
803
|
+
result = await client.billing.subscription.cancel({
|
|
804
|
+
undo: Boolean(options.undo)
|
|
805
|
+
});
|
|
806
|
+
} catch (error) {
|
|
807
|
+
const failure = billingFailureFromError(error, {
|
|
808
|
+
notFoundCode: "NO_ACTIVE_SUBSCRIPTION"
|
|
809
|
+
});
|
|
810
|
+
if (!failure) throw error;
|
|
811
|
+
reportBillingFailure(failure, options);
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
const lines = options.undo ? [
|
|
815
|
+
"Pending cancellation reversed; the subscription will renew normally.",
|
|
816
|
+
`Subscription: ${result.subscription_id}`
|
|
817
|
+
] : [
|
|
818
|
+
`Cancellation scheduled for subscription ${result.subscription_id}.`,
|
|
819
|
+
`The subscription stays active until ${_nullishCoalesce(result.current_period_end, () => ( "the end of the current billing period"))}, then cancels at period end.`,
|
|
820
|
+
"Remaining credits are yours to keep \u2014 cancellation never removes credits.",
|
|
821
|
+
"Undo with: deepline billing subscription cancel --undo"
|
|
822
|
+
];
|
|
823
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
824
|
+
{
|
|
825
|
+
ok: true,
|
|
826
|
+
...result,
|
|
827
|
+
render: {
|
|
828
|
+
sections: [{ title: "billing subscription cancel", lines }]
|
|
829
|
+
}
|
|
830
|
+
},
|
|
831
|
+
{ json: options.json }
|
|
832
|
+
);
|
|
833
|
+
}
|
|
834
|
+
function invoiceAmountText(amountCents, currency) {
|
|
835
|
+
const value = (Number(_nullishCoalesce(amountCents, () => ( 0))) / 100).toFixed(2);
|
|
836
|
+
return String(_nullishCoalesce(currency, () => ( "usd"))).toLowerCase() === "usd" ? `$${value}` : `${value} ${String(currency).toUpperCase()}`;
|
|
837
|
+
}
|
|
838
|
+
function invoiceDateText(createdAt) {
|
|
839
|
+
return String(_nullishCoalesce(createdAt, () => ( ""))).slice(0, 10);
|
|
840
|
+
}
|
|
841
|
+
function invoiceLine(entry, compact) {
|
|
842
|
+
const amount = invoiceAmountText(entry.amount_cents, entry.currency);
|
|
843
|
+
const link = _nullishCoalesce(entry.url, () => ( "(no link)"));
|
|
844
|
+
if (compact) {
|
|
845
|
+
return [
|
|
846
|
+
entry.id,
|
|
847
|
+
invoiceDateText(entry.created_at),
|
|
848
|
+
amount,
|
|
849
|
+
entry.status,
|
|
850
|
+
link
|
|
851
|
+
].join(" | ");
|
|
852
|
+
}
|
|
853
|
+
return [
|
|
854
|
+
invoiceDateText(entry.created_at),
|
|
855
|
+
entry.description,
|
|
856
|
+
amount,
|
|
857
|
+
entry.status,
|
|
858
|
+
link
|
|
859
|
+
].join(" | ");
|
|
860
|
+
}
|
|
861
|
+
async function handleInvoices(options) {
|
|
862
|
+
let limit;
|
|
863
|
+
if (options.limit !== void 0) {
|
|
864
|
+
limit = Number.parseInt(options.limit, 10);
|
|
865
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
|
|
866
|
+
reportBillingFailure(
|
|
867
|
+
{
|
|
868
|
+
exitCode: 2,
|
|
869
|
+
code: "INVALID_LIMIT",
|
|
870
|
+
message: "--limit must be an integer between 1 and 100."
|
|
871
|
+
},
|
|
872
|
+
options
|
|
873
|
+
);
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
const client = new (0, _chunk2C3AVPTYjs.DeeplineClient)();
|
|
878
|
+
let payload;
|
|
879
|
+
try {
|
|
880
|
+
payload = await client.billing.invoices.list(
|
|
881
|
+
limit === void 0 ? void 0 : { limit }
|
|
882
|
+
);
|
|
883
|
+
} catch (error) {
|
|
884
|
+
const failure = billingFailureFromError(error, {
|
|
885
|
+
notFoundCode: "NOT_FOUND"
|
|
886
|
+
});
|
|
887
|
+
if (!failure) throw error;
|
|
888
|
+
reportBillingFailure(failure, options);
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
const entries = Array.isArray(payload.entries) ? payload.entries : [];
|
|
892
|
+
const compact = Boolean(options.compact);
|
|
893
|
+
const header = compact ? "id | date | amount | status | link" : "date | description | amount | status | link";
|
|
894
|
+
const lines = entries.length === 0 ? ["No invoices or receipts yet."] : [header, ...entries.map((entry) => invoiceLine(entry, compact))];
|
|
895
|
+
const envelope = compact ? {
|
|
896
|
+
org_id: payload.org_id,
|
|
897
|
+
count: entries.length,
|
|
898
|
+
entries: entries.map((entry) => ({
|
|
899
|
+
id: entry.id,
|
|
900
|
+
date: invoiceDateText(entry.created_at),
|
|
901
|
+
amount: invoiceAmountText(entry.amount_cents, entry.currency),
|
|
902
|
+
status: entry.status,
|
|
903
|
+
url: entry.url
|
|
904
|
+
}))
|
|
905
|
+
} : { ...payload, count: entries.length };
|
|
906
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
907
|
+
{
|
|
908
|
+
...envelope,
|
|
909
|
+
render: { sections: [{ title: "billing invoices", lines }] }
|
|
910
|
+
},
|
|
911
|
+
{ json: options.json }
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
async function handleCheckout(options) {
|
|
915
|
+
const { http } = _chunk7D6QG5KFjs.getAuthedHttpClient.call(void 0, );
|
|
916
|
+
const payload = await http.post(
|
|
917
|
+
"/api/v2/billing/checkout",
|
|
918
|
+
{
|
|
919
|
+
...options.tier ? { tierId: options.tier } : {},
|
|
920
|
+
...options.credits ? { credits: Number.parseInt(options.credits, 10) } : {},
|
|
921
|
+
...options.discountCode ? { discountCode: options.discountCode } : {}
|
|
922
|
+
}
|
|
923
|
+
);
|
|
924
|
+
const url = String(payload.url || payload.checkout_url || "");
|
|
925
|
+
if (!options.json && !options.noOpen && url) _chunk7D6QG5KFjs.openInBrowser.call(void 0, url);
|
|
926
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
927
|
+
{
|
|
928
|
+
...payload,
|
|
929
|
+
render: {
|
|
930
|
+
sections: [
|
|
931
|
+
{
|
|
932
|
+
title: "billing checkout",
|
|
933
|
+
lines: [url || "Checkout session created."]
|
|
934
|
+
}
|
|
935
|
+
]
|
|
936
|
+
}
|
|
937
|
+
},
|
|
938
|
+
{ json: options.json }
|
|
939
|
+
);
|
|
940
|
+
}
|
|
941
|
+
async function handleTopUp(creditsRaw, options) {
|
|
942
|
+
const credits = parseTopUpCredits(creditsRaw);
|
|
943
|
+
if (credits === null) {
|
|
944
|
+
reportBillingFailure(
|
|
945
|
+
{
|
|
946
|
+
exitCode: 2,
|
|
947
|
+
code: "INVALID_CREDITS",
|
|
948
|
+
message: "<credits> must be a positive integer Deepline credit amount.",
|
|
949
|
+
next: "deepline billing plans --json"
|
|
950
|
+
},
|
|
951
|
+
options
|
|
952
|
+
);
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
const idempotencyKey = topUpIdempotencyKey(options.idempotencyKey);
|
|
956
|
+
if (options.dryRun) {
|
|
957
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
958
|
+
{
|
|
959
|
+
ok: true,
|
|
960
|
+
dry_run: true,
|
|
961
|
+
credits,
|
|
962
|
+
idempotency_key: idempotencyKey,
|
|
963
|
+
planned_request: {
|
|
964
|
+
method: "POST",
|
|
965
|
+
path: BILLING_TOP_UP_PATH,
|
|
966
|
+
body: { credits, idempotency_key: idempotencyKey }
|
|
967
|
+
},
|
|
968
|
+
render: {
|
|
969
|
+
sections: [
|
|
970
|
+
{
|
|
971
|
+
title: "billing top-up (dry run)",
|
|
972
|
+
lines: [
|
|
973
|
+
`Credits: ${credits}`,
|
|
974
|
+
`Idempotency key: ${idempotencyKey}`,
|
|
975
|
+
`Planned request: POST ${BILLING_TOP_UP_PATH}`,
|
|
976
|
+
"No payment request was sent. Re-run without --dry-run to apply."
|
|
977
|
+
]
|
|
978
|
+
}
|
|
979
|
+
]
|
|
980
|
+
}
|
|
981
|
+
},
|
|
982
|
+
{ json: options.json }
|
|
983
|
+
);
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
const client = new (0, _chunk2C3AVPTYjs.DeeplineClient)();
|
|
987
|
+
let payload;
|
|
988
|
+
try {
|
|
989
|
+
payload = await client.billing.topUpWithReceipt({
|
|
990
|
+
credits,
|
|
991
|
+
idempotencyKey
|
|
992
|
+
});
|
|
993
|
+
} catch (error) {
|
|
994
|
+
const failure = billingTopUpFailureFromError(error, {
|
|
995
|
+
credits,
|
|
996
|
+
idempotencyKey
|
|
997
|
+
});
|
|
998
|
+
if (!failure) throw error;
|
|
999
|
+
reportBillingFailure(failure, options);
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
if (payload.status === "pending") {
|
|
1003
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
1004
|
+
{
|
|
1005
|
+
ok: true,
|
|
1006
|
+
...payload,
|
|
1007
|
+
idempotency_key: idempotencyKey,
|
|
1008
|
+
next: topUpRetryCommand(credits, idempotencyKey),
|
|
1009
|
+
render: {
|
|
1010
|
+
sections: [
|
|
1011
|
+
{
|
|
1012
|
+
title: "billing top-up pending",
|
|
1013
|
+
lines: [
|
|
1014
|
+
`Requested: ${payload.requested_credits} Deepline Credits`,
|
|
1015
|
+
"Payment is still being confirmed. Credits have not been reported as added.",
|
|
1016
|
+
`Operation: ${payload.operation_id}`,
|
|
1017
|
+
`Retry with the same key: ${topUpRetryCommand(credits, idempotencyKey)}`
|
|
1018
|
+
]
|
|
1019
|
+
}
|
|
1020
|
+
]
|
|
1021
|
+
}
|
|
1022
|
+
},
|
|
1023
|
+
{ json: options.json }
|
|
1024
|
+
);
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
1027
|
+
const amountText = invoiceAmountText(payload.amount_cents, payload.currency);
|
|
1028
|
+
const appliedCredits = "applied_credits" in payload ? payload.applied_credits : payload.credits;
|
|
1029
|
+
const paymentMethod = "payment_method" in payload ? payload.payment_method : void 0;
|
|
1030
|
+
const paymentLabel = _nullishCoalesce(_optionalChain([paymentMethod, 'optionalAccess', _8 => _8.label]), () => ( [
|
|
1031
|
+
_optionalChain([paymentMethod, 'optionalAccess', _9 => _9.brand]) ? humanize(paymentMethod.brand) : null,
|
|
1032
|
+
_optionalChain([paymentMethod, 'optionalAccess', _10 => _10.last4]) ? `ending in ${paymentMethod.last4}` : null
|
|
1033
|
+
].filter(Boolean).join(" ")));
|
|
1034
|
+
const compactEnvelope = {
|
|
1035
|
+
ok: true,
|
|
1036
|
+
credits: appliedCredits,
|
|
1037
|
+
amount_cents: payload.amount_cents,
|
|
1038
|
+
currency: payload.currency,
|
|
1039
|
+
...typeof payload.balance === "number" ? { balance_credits: payload.balance } : {},
|
|
1040
|
+
..."operation_id" in payload ? {
|
|
1041
|
+
status: payload.status,
|
|
1042
|
+
operation_id: payload.operation_id,
|
|
1043
|
+
operation_state: payload.operation_state
|
|
1044
|
+
} : {},
|
|
1045
|
+
idempotency_key: idempotencyKey
|
|
1046
|
+
};
|
|
1047
|
+
const fullEnvelope = {
|
|
1048
|
+
...payload,
|
|
1049
|
+
ok: true,
|
|
1050
|
+
credits: appliedCredits,
|
|
1051
|
+
...typeof payload.balance === "number" ? { balance_credits: payload.balance } : {},
|
|
1052
|
+
idempotency_key: idempotencyKey,
|
|
1053
|
+
render: {
|
|
1054
|
+
sections: [
|
|
1055
|
+
{
|
|
1056
|
+
title: "billing top-up",
|
|
1057
|
+
lines: [
|
|
1058
|
+
`Added: ${appliedCredits} Deepline Credits`,
|
|
1059
|
+
`Charged: ${amountText}`,
|
|
1060
|
+
...typeof payload.balance === "number" ? [`Balance: ${payload.balance} Deepline Credits`] : ["Balance: unavailable; check deepline billing balance."],
|
|
1061
|
+
...paymentLabel ? [`Payment method: ${paymentLabel}`] : []
|
|
1062
|
+
]
|
|
1063
|
+
}
|
|
1064
|
+
]
|
|
1065
|
+
}
|
|
1066
|
+
};
|
|
1067
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0, options.compact ? compactEnvelope : fullEnvelope, {
|
|
1068
|
+
json: options.json
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
async function handleTargetStatus(options) {
|
|
1072
|
+
const client = new (0, _chunk2C3AVPTYjs.DeeplineClient)();
|
|
1073
|
+
const payload = await client.billing.targetStatus();
|
|
1074
|
+
const balanceCommand = "deepline billing balance --json";
|
|
1075
|
+
const recentChargesCommand = "deepline billing usage --limit 5 --json";
|
|
1076
|
+
const exactChargeCommand = "deepline billing usage --request-id <job_id> --json";
|
|
1077
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
1078
|
+
{
|
|
1079
|
+
ok: true,
|
|
1080
|
+
...payload,
|
|
1081
|
+
next: {
|
|
1082
|
+
balance: balanceCommand,
|
|
1083
|
+
recentCharges: recentChargesCommand,
|
|
1084
|
+
chargeByJobId: exactChargeCommand
|
|
1085
|
+
},
|
|
1086
|
+
render: {
|
|
1087
|
+
sections: [
|
|
1088
|
+
{
|
|
1089
|
+
title: "billing status",
|
|
1090
|
+
lines: [
|
|
1091
|
+
`Plan: ${payload.plan.name} (${payload.plan.sku})`,
|
|
1092
|
+
`State: ${payload.state}`,
|
|
1093
|
+
`Payment: ${payload.payment_state}`,
|
|
1094
|
+
`Automatic recharge: ${payload.recharge_state}`,
|
|
1095
|
+
`Balance: ${balanceCommand}`,
|
|
1096
|
+
`Recent charges: ${recentChargesCommand}`,
|
|
1097
|
+
`Charge for a tool job: ${exactChargeCommand}`
|
|
1098
|
+
]
|
|
1099
|
+
}
|
|
1100
|
+
]
|
|
1101
|
+
}
|
|
1102
|
+
},
|
|
1103
|
+
{ json: options.json }
|
|
1104
|
+
);
|
|
1105
|
+
}
|
|
1106
|
+
async function handleAutoRechargeStatus(options) {
|
|
1107
|
+
const payload = await new (0, _chunk2C3AVPTYjs.DeeplineClient)().billing.autoRecharge.get();
|
|
1108
|
+
const state = payload.available ? payload.enabled ? "on" : "off" : "unavailable";
|
|
1109
|
+
const lines = [
|
|
1110
|
+
`State: ${state}`,
|
|
1111
|
+
...payload.threshold_credits !== null ? [`Threshold: ${payload.threshold_credits} credits`] : [],
|
|
1112
|
+
...payload.refill_to_credits !== null ? [`Refill balance to: ${payload.refill_to_credits} credits`] : [],
|
|
1113
|
+
...payload.purchase_limit_credits != null ? [
|
|
1114
|
+
`Automatic credit purchase cap: ${payload.purchase_limit_credits} credits per rolling 30 days`
|
|
1115
|
+
] : []
|
|
1116
|
+
];
|
|
1117
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
1118
|
+
{
|
|
1119
|
+
ok: true,
|
|
1120
|
+
...payload,
|
|
1121
|
+
render: { sections: [{ title: "automatic recharge", lines }] }
|
|
1122
|
+
},
|
|
1123
|
+
{ json: options.json }
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
async function handleAutoRechargeSet(options) {
|
|
1127
|
+
const thresholdCredits = parseTopUpCredits(options.thresholdCredits);
|
|
1128
|
+
const refillToCredits = parseTopUpCredits(options.refillToCredits);
|
|
1129
|
+
const rollingLimitCents = options.purchaseLimitCents === void 0 ? void 0 : options.purchaseLimitCents === "none" ? null : Number(options.purchaseLimitCents);
|
|
1130
|
+
const rollingLimitCredits = options.purchaseLimitCredits === void 0 ? void 0 : options.purchaseLimitCredits === "none" ? null : Number(options.purchaseLimitCredits);
|
|
1131
|
+
if (rollingLimitCredits != null && (!Number.isSafeInteger(rollingLimitCredits) || rollingLimitCredits < 0)) {
|
|
1132
|
+
reportBillingFailure(
|
|
1133
|
+
{
|
|
1134
|
+
exitCode: 2,
|
|
1135
|
+
code: "INVALID_AUTO_RECHARGE_CONFIGURATION",
|
|
1136
|
+
message: "--purchase-limit-credits must be a nonnegative whole number of credits or none."
|
|
1137
|
+
},
|
|
1138
|
+
options
|
|
1139
|
+
);
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1142
|
+
if (rollingLimitCents !== void 0 && rollingLimitCents !== null && (!Number.isSafeInteger(rollingLimitCents) || rollingLimitCents <= 0)) {
|
|
1143
|
+
reportBillingFailure(
|
|
1144
|
+
{
|
|
1145
|
+
exitCode: 2,
|
|
1146
|
+
code: "INVALID_AUTO_RECHARGE_CONFIGURATION",
|
|
1147
|
+
message: "--purchase-limit-cents must be a positive whole number of USD cents or none."
|
|
1148
|
+
},
|
|
1149
|
+
options
|
|
1150
|
+
);
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
if (thresholdCredits === null || refillToCredits === null || refillToCredits <= thresholdCredits) {
|
|
1154
|
+
reportBillingFailure(
|
|
1155
|
+
{
|
|
1156
|
+
exitCode: 2,
|
|
1157
|
+
code: "INVALID_AUTO_RECHARGE_CONFIGURATION",
|
|
1158
|
+
message: "--threshold-credits and --refill-to-credits must be positive whole credits, and refill-to must be greater."
|
|
1159
|
+
},
|
|
1160
|
+
options
|
|
1161
|
+
);
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
const idempotencyKey = targetBillingIdempotencyKey(options.idempotencyKey);
|
|
1165
|
+
if (options.dryRun) {
|
|
1166
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
1167
|
+
{
|
|
1168
|
+
ok: true,
|
|
1169
|
+
dry_run: true,
|
|
1170
|
+
idempotency_key: idempotencyKey,
|
|
1171
|
+
planned_request: {
|
|
1172
|
+
method: "PUT",
|
|
1173
|
+
path: "/api/v2/billing/auto-recharge",
|
|
1174
|
+
body: {
|
|
1175
|
+
enabled: true,
|
|
1176
|
+
threshold_credits: thresholdCredits,
|
|
1177
|
+
refill_to_credits: refillToCredits,
|
|
1178
|
+
...rollingLimitCents !== void 0 ? { purchase_limit_cents: rollingLimitCents } : {},
|
|
1179
|
+
...rollingLimitCredits !== void 0 ? { purchase_limit_credits: rollingLimitCredits } : {}
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
},
|
|
1183
|
+
{ json: options.json }
|
|
1184
|
+
);
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1187
|
+
const payload = await new (0, _chunk2C3AVPTYjs.DeeplineClient)().billing.autoRecharge.update({
|
|
1188
|
+
enabled: true,
|
|
1189
|
+
thresholdCredits,
|
|
1190
|
+
refillToCredits,
|
|
1191
|
+
...rollingLimitCents !== void 0 ? { purchaseLimitCents: rollingLimitCents } : {},
|
|
1192
|
+
...rollingLimitCredits !== void 0 ? { purchaseLimitCredits: rollingLimitCredits } : {},
|
|
1193
|
+
idempotencyKey
|
|
1194
|
+
});
|
|
1195
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
1196
|
+
{ ok: true, idempotency_key: idempotencyKey, ...payload },
|
|
1197
|
+
{ json: options.json }
|
|
1198
|
+
);
|
|
1199
|
+
}
|
|
1200
|
+
async function handleAutoRechargeOff(options) {
|
|
1201
|
+
const idempotencyKey = targetBillingIdempotencyKey(options.idempotencyKey);
|
|
1202
|
+
if (options.dryRun) {
|
|
1203
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
1204
|
+
{
|
|
1205
|
+
ok: true,
|
|
1206
|
+
dry_run: true,
|
|
1207
|
+
idempotency_key: idempotencyKey,
|
|
1208
|
+
planned_request: {
|
|
1209
|
+
method: "PUT",
|
|
1210
|
+
path: "/api/v2/billing/auto-recharge",
|
|
1211
|
+
body: { enabled: false }
|
|
1212
|
+
}
|
|
1213
|
+
},
|
|
1214
|
+
{ json: options.json }
|
|
1215
|
+
);
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
const payload = await new (0, _chunk2C3AVPTYjs.DeeplineClient)().billing.autoRecharge.update({
|
|
1219
|
+
enabled: false,
|
|
1220
|
+
idempotencyKey
|
|
1221
|
+
});
|
|
1222
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
1223
|
+
{ ok: true, idempotency_key: idempotencyKey, ...payload },
|
|
1224
|
+
{ json: options.json }
|
|
1225
|
+
);
|
|
1226
|
+
}
|
|
1227
|
+
async function handleBuyCredits(creditsRaw, options) {
|
|
1228
|
+
const credits = parseTopUpCredits(creditsRaw);
|
|
1229
|
+
if (credits === null) {
|
|
1230
|
+
reportBillingFailure(
|
|
1231
|
+
{
|
|
1232
|
+
exitCode: 2,
|
|
1233
|
+
code: "INVALID_CREDITS",
|
|
1234
|
+
message: "<credits> must be a positive integer."
|
|
1235
|
+
},
|
|
1236
|
+
options
|
|
1237
|
+
);
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
const idempotencyKey = targetBillingIdempotencyKey(options.idempotencyKey);
|
|
1241
|
+
const payload = await new (0, _chunk2C3AVPTYjs.DeeplineClient)().billing.purchaseCredits({
|
|
1242
|
+
credits,
|
|
1243
|
+
idempotencyKey
|
|
1244
|
+
});
|
|
1245
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
1246
|
+
{ ok: true, idempotency_key: idempotencyKey, ...payload },
|
|
1247
|
+
{ json: options.json }
|
|
1248
|
+
);
|
|
1249
|
+
}
|
|
1250
|
+
async function handleTopUpRecover(creditsRaw, options) {
|
|
1251
|
+
const credits = parseTopUpCredits(creditsRaw);
|
|
1252
|
+
if (credits === null) {
|
|
1253
|
+
reportBillingFailure(
|
|
1254
|
+
{
|
|
1255
|
+
exitCode: 2,
|
|
1256
|
+
code: "INVALID_CREDITS",
|
|
1257
|
+
message: "<credits> must be a positive integer."
|
|
1258
|
+
},
|
|
1259
|
+
options
|
|
1260
|
+
);
|
|
1261
|
+
return;
|
|
1262
|
+
}
|
|
1263
|
+
const failure = !_optionalChain([options, 'access', _11 => _11.orgId, 'optionalAccess', _12 => _12.trim, 'call', _13 => _13()]) || options.orgId !== options.orgId.trim() ? {
|
|
1264
|
+
code: "INVALID_ORG_ID",
|
|
1265
|
+
message: "--org-id must be the original workspace ID without surrounding whitespace."
|
|
1266
|
+
} : !options.idempotencyKey || options.idempotencyKey.length > 200 || options.idempotencyKey !== options.idempotencyKey.trim() ? {
|
|
1267
|
+
code: "INVALID_BILLING_IDEMPOTENCY_KEY",
|
|
1268
|
+
message: "--idempotency-key must be the original key (1\u2013200 characters without surrounding whitespace)."
|
|
1269
|
+
} : null;
|
|
1270
|
+
if (failure) {
|
|
1271
|
+
reportBillingFailure(
|
|
1272
|
+
{
|
|
1273
|
+
exitCode: 2,
|
|
1274
|
+
...failure
|
|
1275
|
+
},
|
|
1276
|
+
options
|
|
1277
|
+
);
|
|
1278
|
+
return;
|
|
1279
|
+
}
|
|
1280
|
+
if (options.dryRun) {
|
|
1281
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
1282
|
+
{
|
|
1283
|
+
ok: true,
|
|
1284
|
+
dry_run: true,
|
|
1285
|
+
method: "POST",
|
|
1286
|
+
path: "/api/v2/billing/credit-purchases/recover",
|
|
1287
|
+
idempotency_key: options.idempotencyKey,
|
|
1288
|
+
body: { credits, org_id: options.orgId }
|
|
1289
|
+
},
|
|
1290
|
+
{ json: options.json }
|
|
1291
|
+
);
|
|
1292
|
+
return;
|
|
1293
|
+
}
|
|
1294
|
+
const payload = await new (0, _chunk2C3AVPTYjs.DeeplineClient)().billing.recoverCreditPurchase({
|
|
1295
|
+
credits,
|
|
1296
|
+
orgId: options.orgId,
|
|
1297
|
+
idempotencyKey: options.idempotencyKey
|
|
1298
|
+
});
|
|
1299
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
1300
|
+
{ ok: true, idempotency_key: options.idempotencyKey, ...payload },
|
|
1301
|
+
{ json: options.json }
|
|
1302
|
+
);
|
|
1303
|
+
}
|
|
1304
|
+
async function handleTargetPlan(planSku, options) {
|
|
1305
|
+
if (planSku !== "payg-v1" && planSku !== "builder-v1" && planSku !== "growth-v1" && // Retained Canary requests may replay their original operation. The API
|
|
1306
|
+
// rejects new acquisition even when a caller supplies an idempotency key.
|
|
1307
|
+
!(planSku === "growth-canary-v1" && options.idempotencyKey) && planSku !== "team-v1") {
|
|
1308
|
+
reportBillingFailure(
|
|
1309
|
+
{
|
|
1310
|
+
exitCode: 2,
|
|
1311
|
+
code: "INVALID_PLAN",
|
|
1312
|
+
message: "Plan must be payg-v1, builder-v1, growth-v1, or team-v1."
|
|
1313
|
+
},
|
|
1314
|
+
options
|
|
1315
|
+
);
|
|
1316
|
+
return;
|
|
1317
|
+
}
|
|
1318
|
+
const idempotencyKey = targetBillingIdempotencyKey(options.idempotencyKey);
|
|
1319
|
+
const payload = await new (0, _chunk2C3AVPTYjs.DeeplineClient)().billing.transitionPlan({
|
|
1320
|
+
action: "start_or_change",
|
|
1321
|
+
targetPlanSku: planSku,
|
|
1322
|
+
...options.billingEmail ? { billingEmail: options.billingEmail } : {},
|
|
1323
|
+
idempotencyKey
|
|
1324
|
+
});
|
|
1325
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
1326
|
+
{ ok: true, idempotency_key: idempotencyKey, ...payload },
|
|
1327
|
+
{ json: options.json }
|
|
1328
|
+
);
|
|
1329
|
+
}
|
|
1330
|
+
async function handleTargetPlanCancellation(options) {
|
|
1331
|
+
const idempotencyKey = targetBillingIdempotencyKey(options.idempotencyKey);
|
|
1332
|
+
const payload = await new (0, _chunk2C3AVPTYjs.DeeplineClient)().billing.transitionPlan({
|
|
1333
|
+
action: options.undo ? "undo_cancel" : "cancel",
|
|
1334
|
+
idempotencyKey
|
|
1335
|
+
});
|
|
1336
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
1337
|
+
{ ok: true, idempotency_key: idempotencyKey, ...payload },
|
|
1338
|
+
{ json: options.json }
|
|
1339
|
+
);
|
|
1340
|
+
}
|
|
1341
|
+
async function handleTargetPortal(options) {
|
|
1342
|
+
const payload = await new (0, _chunk2C3AVPTYjs.DeeplineClient)().billing.portalSession(
|
|
1343
|
+
options.billingAddress ? { flow: "customer_update" } : void 0
|
|
1344
|
+
);
|
|
1345
|
+
if (!options.json && !options.noOpen) _chunk7D6QG5KFjs.openInBrowser.call(void 0, payload.url);
|
|
1346
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0, { ok: true, ...payload }, { json: options.json });
|
|
1347
|
+
}
|
|
1348
|
+
async function handleRedeemCode(code, options) {
|
|
1349
|
+
const { http } = _chunk7D6QG5KFjs.getAuthedHttpClient.call(void 0, );
|
|
1350
|
+
const payload = await http.post(
|
|
1351
|
+
"/api/v2/billing/checkout",
|
|
1352
|
+
{
|
|
1353
|
+
discountCode: code
|
|
1354
|
+
}
|
|
1355
|
+
);
|
|
1356
|
+
const url = String(payload.url || payload.checkout_url || "");
|
|
1357
|
+
if (!options.json && !options.noOpen && url) _chunk7D6QG5KFjs.openInBrowser.call(void 0, url);
|
|
1358
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0,
|
|
1359
|
+
{
|
|
1360
|
+
...payload,
|
|
1361
|
+
render: {
|
|
1362
|
+
sections: [{ title: "billing code", lines: [url || "Code redeemed."] }]
|
|
1363
|
+
}
|
|
1364
|
+
},
|
|
1365
|
+
{ json: options.json }
|
|
1366
|
+
);
|
|
1367
|
+
}
|
|
1368
|
+
function registerBillingCommands(program) {
|
|
1369
|
+
const billing = program.command("billing").description("See your plan and credits, buy more, and manage billing.").addHelpText(
|
|
1370
|
+
"after",
|
|
1371
|
+
`
|
|
1372
|
+
Concepts:
|
|
1373
|
+
Billing commands show Deepline credits, not raw provider spend.
|
|
1374
|
+
Balance is shared by the workspace; concurrent work changes it. Use a job_id
|
|
1375
|
+
charge lookup to attribute one tool command instead of comparing balances.
|
|
1376
|
+
checkout/subscribe/redeem-code can open a browser unless --no-open is set.
|
|
1377
|
+
|
|
1378
|
+
Examples:
|
|
1379
|
+
# See where you stand
|
|
1380
|
+
deepline billing balance --json
|
|
1381
|
+
deepline billing usage --limit 20 --json
|
|
1382
|
+
deepline billing plans --json
|
|
1383
|
+
deepline billing subscription status --json
|
|
1384
|
+
|
|
1385
|
+
# Price a tool before execution; look up its exact charge afterward
|
|
1386
|
+
deepline tools describe <tool-id> --pricing-only
|
|
1387
|
+
deepline billing usage --request-id <job_id> --json
|
|
1388
|
+
|
|
1389
|
+
# Buy credits or a plan
|
|
1390
|
+
deepline billing top-up 1000 --json
|
|
1391
|
+
deepline billing checkout --credits 1000 --no-open --json
|
|
1392
|
+
deepline billing subscribe runtime-395-2026-07-v1 --no-open --json
|
|
1393
|
+
deepline billing redeem-code --code ABC123 --no-open --json
|
|
1394
|
+
|
|
1395
|
+
# Manage
|
|
1396
|
+
deepline billing subscription cancel --dry-run --json
|
|
1397
|
+
deepline billing set-limit 500 --json
|
|
1398
|
+
|
|
1399
|
+
# Records
|
|
1400
|
+
deepline billing invoices --limit 12 --json
|
|
1401
|
+
deepline billing history --time 1m --json
|
|
1402
|
+
deepline billing ledger export all --json
|
|
1403
|
+
`
|
|
1404
|
+
);
|
|
1405
|
+
billing.command("balance").description("Show current billing balance.").addHelpText(
|
|
1406
|
+
"after",
|
|
1407
|
+
`
|
|
1408
|
+
Notes:
|
|
1409
|
+
Read-only. Shows the current Deepline credit balance for the active workspace.
|
|
1410
|
+
The balance includes concurrent workspace activity, so a before/after difference
|
|
1411
|
+
cannot isolate one command. Use billing usage --request-id <job_id> --json.
|
|
1412
|
+
|
|
1413
|
+
Examples:
|
|
1414
|
+
deepline billing balance
|
|
1415
|
+
deepline billing balance --json
|
|
1416
|
+
`
|
|
1417
|
+
).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleBalance);
|
|
1418
|
+
billing.command("usage").description("Show current usage plus recent calls.").addHelpText(
|
|
1419
|
+
"after",
|
|
1420
|
+
`
|
|
1421
|
+
Notes:
|
|
1422
|
+
Read-only. Shows last-30-day Deepline credit usage plus a bounded recent-call
|
|
1423
|
+
page. Use --limit/--cursor to paginate the recent-call section.
|
|
1424
|
+
For one tool command, use the job_id returned by tools execute, or its next.billing
|
|
1425
|
+
command. Pending charges and temporary holds are not final spend; unknown means
|
|
1426
|
+
final charge evidence is unavailable. Workspace balance changes include other work.
|
|
1427
|
+
|
|
1428
|
+
Examples:
|
|
1429
|
+
deepline billing usage
|
|
1430
|
+
deepline billing usage --limit 50 --cursor <next_cursor> --json
|
|
1431
|
+
deepline billing usage --run-id play/example/run/20260630t120000-000-abcdef --json
|
|
1432
|
+
deepline billing usage --request-id <job_id> --json
|
|
1433
|
+
`
|
|
1434
|
+
).option("--limit <n>", "Recent-call page size").option("--offset <n>", "Recent-call offset").option(
|
|
1435
|
+
"--cursor <cursor>",
|
|
1436
|
+
"Recent-call cursor from the previous response"
|
|
1437
|
+
).option("--run-id <run_id>", "Show recent usage for one play run").option(
|
|
1438
|
+
"--request-id <job_id>",
|
|
1439
|
+
"Show the exact Deepline charge for one execution job_id"
|
|
1440
|
+
).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleUsage);
|
|
1441
|
+
billing.command("limit").description("Show configured monthly usage cap.").addHelpText(
|
|
1442
|
+
"after",
|
|
1443
|
+
`
|
|
1444
|
+
Notes:
|
|
1445
|
+
Read-only. Shows whether monthly workspace usage cap is enabled.
|
|
1446
|
+
New calls are checked against this usage cap, separately from wallet funding.
|
|
1447
|
+
|
|
1448
|
+
Examples:
|
|
1449
|
+
deepline billing limit
|
|
1450
|
+
deepline billing limit --json
|
|
1451
|
+
`
|
|
1452
|
+
).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleLimit);
|
|
1453
|
+
billing.command("set-limit").description("Set monthly usage cap.").addHelpText(
|
|
1454
|
+
"after",
|
|
1455
|
+
`
|
|
1456
|
+
Notes:
|
|
1457
|
+
Mutates workspace billing settings. The argument is a Deepline credit amount,
|
|
1458
|
+
not dollars and not provider credits. New calls are checked against the cap;
|
|
1459
|
+
in-flight work can still finish and exceed it. Buying credits does not reset it.
|
|
1460
|
+
|
|
1461
|
+
Examples:
|
|
1462
|
+
deepline billing set-limit 500
|
|
1463
|
+
deepline billing set-limit 500 --json
|
|
1464
|
+
`
|
|
1465
|
+
).argument("<credits>", "Monthly credits limit").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleSetLimit);
|
|
1466
|
+
billing.command("off").description("Disable monthly usage cap.").addHelpText(
|
|
1467
|
+
"after",
|
|
1468
|
+
`
|
|
1469
|
+
Notes:
|
|
1470
|
+
Mutates workspace billing settings by removing monthly Deepline usage cap.
|
|
1471
|
+
|
|
1472
|
+
Examples:
|
|
1473
|
+
deepline billing off
|
|
1474
|
+
deepline billing off --json
|
|
1475
|
+
`
|
|
1476
|
+
).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleLimitOff);
|
|
1477
|
+
billing.command("ledger").description("Inspect and export billing ledger rows.").addHelpText(
|
|
1478
|
+
"after",
|
|
1479
|
+
`
|
|
1480
|
+
Notes:
|
|
1481
|
+
Read-only. Exports every ledger row for the active workspace, including rows
|
|
1482
|
+
created without an API key.
|
|
1483
|
+
|
|
1484
|
+
Examples:
|
|
1485
|
+
deepline billing ledger export all
|
|
1486
|
+
deepline billing ledger export all --output ./ledger.csv
|
|
1487
|
+
deepline billing ledger export all --run-id play/demo/run/abc123
|
|
1488
|
+
deepline billing ledger export all --operation limadata_find_audience_identifiers --since 2026-09-01T00:00:00Z
|
|
1489
|
+
deepline billing ledger export all --json
|
|
1490
|
+
`
|
|
1491
|
+
).addCommand(
|
|
1492
|
+
new (0, _commander.Command)("export").description("Export billing ledger rows.").addHelpText(
|
|
1493
|
+
"after",
|
|
1494
|
+
`
|
|
1495
|
+
Examples:
|
|
1496
|
+
deepline billing ledger export all
|
|
1497
|
+
`
|
|
1498
|
+
).addCommand(
|
|
1499
|
+
new (0, _commander.Command)("all").description("Export all available billing ledger rows to CSV.").addHelpText(
|
|
1500
|
+
"after",
|
|
1501
|
+
`
|
|
1502
|
+
Notes:
|
|
1503
|
+
Pages through the workspace ledger, writes CSV locally,
|
|
1504
|
+
then reports the row count and the net delta computed from delta_credits.
|
|
1505
|
+
|
|
1506
|
+
Examples:
|
|
1507
|
+
deepline billing ledger export all
|
|
1508
|
+
deepline billing ledger export all --json
|
|
1509
|
+
`
|
|
1510
|
+
).option("--output <path>", "Write CSV to an explicit path").option("--run-id <run_id>", "Export ledger rows for one play run.").option("--operation <id>", "Export rows for one tool operation.").option(
|
|
1511
|
+
"--since <timestamp>",
|
|
1512
|
+
"Only export rows since ISO-8601 time or Unix milliseconds."
|
|
1513
|
+
).option(
|
|
1514
|
+
"--json",
|
|
1515
|
+
"Emit JSON output. Also automatic when stdout is piped"
|
|
1516
|
+
).action(handleLedgerExportAll)
|
|
1517
|
+
)
|
|
1518
|
+
);
|
|
1519
|
+
billing.command("history").description("Export billing ledger history to CSV.").addHelpText(
|
|
1520
|
+
"after",
|
|
1521
|
+
`
|
|
1522
|
+
Notes:
|
|
1523
|
+
Read-only server query that writes a local CSV file. With --json, stdout is a
|
|
1524
|
+
summary containing the output path, row count, and completeness metadata.
|
|
1525
|
+
Exports the complete window using bounded pages. For an exact start timestamp:
|
|
1526
|
+
deepline billing ledger export all --since <ISO-8601> --json
|
|
1527
|
+
|
|
1528
|
+
Examples:
|
|
1529
|
+
deepline billing history --time 1m
|
|
1530
|
+
deepline billing history --time 1y --json
|
|
1531
|
+
`
|
|
1532
|
+
).requiredOption("--time <window>", "Rolling time window: 1d, 1w, 1m, or 1y").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleHistory);
|
|
1533
|
+
billing.command("checkout").description(
|
|
1534
|
+
"Create a checkout session and optionally open it in your browser."
|
|
1535
|
+
).addHelpText(
|
|
1536
|
+
"after",
|
|
1537
|
+
`
|
|
1538
|
+
Notes:
|
|
1539
|
+
Creates a payment checkout session. Opens the checkout URL in a browser unless
|
|
1540
|
+
--no-open is set. Use --json for automation.
|
|
1541
|
+
|
|
1542
|
+
Examples:
|
|
1543
|
+
deepline billing checkout --tier pro
|
|
1544
|
+
deepline billing checkout --credits 1000 --no-open --json
|
|
1545
|
+
deepline billing checkout --credits 1000 --discount-code LAUNCH --no-open
|
|
1546
|
+
`
|
|
1547
|
+
).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);
|
|
1548
|
+
billing.command("top-up").description("Charge the saved payment method and add Deepline credits.").addHelpText(
|
|
1549
|
+
"after",
|
|
1550
|
+
`
|
|
1551
|
+
Notes:
|
|
1552
|
+
Mutates workspace billing by charging the saved payment method for the active
|
|
1553
|
+
workspace, then adding purchased Deepline credits. If no saved payment method
|
|
1554
|
+
is available, use the checkout command printed in the error response.
|
|
1555
|
+
--dry-run prints the planned request and idempotency key without charging.
|
|
1556
|
+
|
|
1557
|
+
Examples:
|
|
1558
|
+
deepline billing top-up 1000
|
|
1559
|
+
deepline billing top-up 1000 --json
|
|
1560
|
+
deepline billing top-up 1000 --dry-run --json
|
|
1561
|
+
deepline billing top-up 1000 --idempotency-key refill-2026-07-08 --json
|
|
1562
|
+
`
|
|
1563
|
+
).argument("<credits>", "Positive integer Deepline credit amount").option(
|
|
1564
|
+
"--idempotency-key <key>",
|
|
1565
|
+
"Stable retry key for the same intended top-up"
|
|
1566
|
+
).option("--dry-run", "Print the planned top-up without charging").option("--compact", "Keep only high-signal fields in JSON output").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleTopUp);
|
|
1567
|
+
billing.command("buy").description("Buy credits through the target billing contract.").argument("<credits>", "Positive integer Deepline credit amount").option("--idempotency-key <key>", "Stable retry key").option("--json", "Emit JSON output").action(handleBuyCredits);
|
|
1568
|
+
billing.command("top-up-recover").description(
|
|
1569
|
+
"Inspect or retire an original top-up intent without starting a payment."
|
|
1570
|
+
).argument("<credits>", "Original positive integer Deepline credit amount").requiredOption(
|
|
1571
|
+
"--org-id <original-org>",
|
|
1572
|
+
"Original workspace ID (must match the authenticated workspace)"
|
|
1573
|
+
).requiredOption(
|
|
1574
|
+
"--idempotency-key <original-key>",
|
|
1575
|
+
"Original purchase key; never generates a new key"
|
|
1576
|
+
).option("--dry-run", "Print the recovery request without changing state").option("--json", "Emit JSON output. Also automatic when stdout is piped").addHelpText(
|
|
1577
|
+
"after",
|
|
1578
|
+
"\nAn absent intent is durably retired; an existing operation is returned unchanged.\nThis command never starts a payment. Use the original amount, workspace, and key.\nExample:\n deepline billing top-up-recover 1000 --org-id org_original --idempotency-key original-purchase-key --json\n"
|
|
1579
|
+
).action(handleTopUpRecover);
|
|
1580
|
+
billing.command("status").description(
|
|
1581
|
+
"Show plan, payment, and recharge state; use usage for charges."
|
|
1582
|
+
).option("--json", "Emit JSON output").action(handleTargetStatus);
|
|
1583
|
+
billing.command("auto-recharge").description("Inspect and manage automatic Deepline credit recharge.").addHelpText(
|
|
1584
|
+
"after",
|
|
1585
|
+
`
|
|
1586
|
+
Examples:
|
|
1587
|
+
deepline billing auto-recharge status --json
|
|
1588
|
+
deepline billing auto-recharge set --threshold-credits 1000 --refill-to-credits 3500 --dry-run --json
|
|
1589
|
+
deepline billing auto-recharge off --dry-run --json
|
|
1590
|
+
`
|
|
1591
|
+
).addCommand(
|
|
1592
|
+
new (0, _commander.Command)("status").description("Show the canonical automatic recharge configuration.").option("--json", "Emit JSON output").action(handleAutoRechargeStatus)
|
|
1593
|
+
).addCommand(
|
|
1594
|
+
new (0, _commander.Command)("set").description(
|
|
1595
|
+
"Enable automatic recharge with a threshold and refill target."
|
|
1596
|
+
).requiredOption(
|
|
1597
|
+
"--threshold-credits <credits>",
|
|
1598
|
+
"Recharge when the balance reaches this amount"
|
|
1599
|
+
).requiredOption(
|
|
1600
|
+
"--refill-to-credits <credits>",
|
|
1601
|
+
"Recharge the balance to this amount"
|
|
1602
|
+
).addOption(
|
|
1603
|
+
new (0, _commander.Option)(
|
|
1604
|
+
"--purchase-limit-cents <cents>",
|
|
1605
|
+
"Deprecated: converted to credits"
|
|
1606
|
+
).hideHelp()
|
|
1607
|
+
).option(
|
|
1608
|
+
"--purchase-limit-credits <credits>",
|
|
1609
|
+
"Limit automatic credit purchases over rolling 30 days (or none to remove)"
|
|
1610
|
+
).option("--idempotency-key <key>", "Stable retry key").option("--dry-run", "Print the planned update without applying it").option("--json", "Emit JSON output").action(handleAutoRechargeSet)
|
|
1611
|
+
).addCommand(
|
|
1612
|
+
new (0, _commander.Command)("off").description(
|
|
1613
|
+
"Disable automatic recharge without clearing saved amounts."
|
|
1614
|
+
).option("--idempotency-key <key>", "Stable retry key").option("--dry-run", "Print the planned update without applying it").option("--json", "Emit JSON output").action(handleAutoRechargeOff)
|
|
1615
|
+
).addCommand(
|
|
1616
|
+
new (0, _commander.Command)("trigger").description(
|
|
1617
|
+
"Evaluate capped automatic recharge and resume any pending purchase."
|
|
1618
|
+
).option("--json", "Emit JSON output").action(async (options) => {
|
|
1619
|
+
const result = await new (0, _chunk2C3AVPTYjs.DeeplineClient)().billing.autoRecharge.trigger();
|
|
1620
|
+
_chunk7D6QG5KFjs.printCommandEnvelope.call(void 0, { ok: true, ...result }, { json: options.json });
|
|
1621
|
+
})
|
|
1622
|
+
);
|
|
1623
|
+
billing.command("change-plan").description("Start or change the target billing plan.").argument("<plan_sku>", "payg-v1, builder-v1, growth-v1, or team-v1").option(
|
|
1624
|
+
"--billing-email <email>",
|
|
1625
|
+
"Invoice recipient when the account has no billing email"
|
|
1626
|
+
).option("--idempotency-key <key>", "Stable retry key").option("--json", "Emit JSON output").action(handleTargetPlan);
|
|
1627
|
+
billing.command("cancel-plan").description("Cancel a target subscription at period end, or undo it.").option("--undo", "Undo a pending period-end cancellation").option("--idempotency-key <key>", "Stable retry key").option("--json", "Emit JSON output").action(handleTargetPlanCancellation);
|
|
1628
|
+
billing.command("portal").description("Open the Stripe-hosted billing recovery portal.").option("--billing-address", "Open the billing address editor directly").option("--no-open", "Print the URL without opening a browser").option("--json", "Emit JSON output").action(handleTargetPortal);
|
|
1629
|
+
billing.command("plans").description("Show published plans and the plan you are on.").addHelpText(
|
|
1630
|
+
"after",
|
|
1631
|
+
`
|
|
1632
|
+
Notes:
|
|
1633
|
+
Read-only. Shows the current target billing plan, pending plan, and every
|
|
1634
|
+
visible plan's recurring price, included credits, purchased-credit price,
|
|
1635
|
+
and current acquisition availability. All amounts are Deepline credits and
|
|
1636
|
+
Deepline-facing USD, never raw provider spend. Change to an available plan
|
|
1637
|
+
with: deepline billing change-plan <plan_sku>
|
|
1638
|
+
|
|
1639
|
+
Examples:
|
|
1640
|
+
deepline billing plans
|
|
1641
|
+
deepline billing plans --json
|
|
1642
|
+
`
|
|
1643
|
+
).option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handlePlans);
|
|
1644
|
+
billing.command("subscribe").description(
|
|
1645
|
+
"Start a subscription checkout for a plan and optionally open it in your browser."
|
|
1646
|
+
).addHelpText(
|
|
1647
|
+
"after",
|
|
1648
|
+
`
|
|
1649
|
+
Notes:
|
|
1650
|
+
Creates a Stripe subscription checkout session for the given plan version.
|
|
1651
|
+
Opens the checkout URL in a browser unless --no-open is set. Fails loudly
|
|
1652
|
+
with the server error when subscription checkout is not enabled or the plan
|
|
1653
|
+
is not open for subscription.
|
|
1654
|
+
|
|
1655
|
+
Examples:
|
|
1656
|
+
deepline billing subscribe runtime-395-2026-07-v1
|
|
1657
|
+
deepline billing subscribe runtime-395-2026-07-v1 --no-open --json
|
|
1658
|
+
`
|
|
1659
|
+
).argument("<plan_version_id>", "Legacy billing plan version id").option("--no-open", "Print the checkout URL without opening a browser").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(handleSubscribe);
|
|
1660
|
+
billing.command("subscription").description("Inspect and manage subscription state for the workspace.").addHelpText(
|
|
1661
|
+
"after",
|
|
1662
|
+
`
|
|
1663
|
+
Examples:
|
|
1664
|
+
deepline billing subscription status
|
|
1665
|
+
deepline billing subscription status --json
|
|
1666
|
+
deepline billing subscription cancel --dry-run
|
|
1667
|
+
deepline billing subscription cancel --json
|
|
1668
|
+
deepline billing subscription cancel --undo --json
|
|
1669
|
+
`
|
|
1670
|
+
).addCommand(
|
|
1671
|
+
new (0, _commander.Command)("status").description("Show active plan, subscription state, and credit pools.").addHelpText(
|
|
1672
|
+
"after",
|
|
1673
|
+
`
|
|
1674
|
+
Notes:
|
|
1675
|
+
Read-only. Shows the active plan, whether a Stripe subscription backs it,
|
|
1676
|
+
scheduled cancellation state, and remaining Deepline credits per grant pool.
|
|
1677
|
+
|
|
1678
|
+
Examples:
|
|
1679
|
+
deepline billing subscription status
|
|
1680
|
+
deepline billing subscription status --json
|
|
1681
|
+
`
|
|
1682
|
+
).option(
|
|
1683
|
+
"--json",
|
|
1684
|
+
"Emit JSON output. Also automatic when stdout is piped"
|
|
1685
|
+
).action(handleSubscriptionStatus)
|
|
1686
|
+
).addCommand(
|
|
1687
|
+
new (0, _commander.Command)("cancel").description("Cancel the subscription at period end. Credits are kept.").addHelpText(
|
|
1688
|
+
"after",
|
|
1689
|
+
`
|
|
1690
|
+
Notes:
|
|
1691
|
+
Mutates subscription state. Cancellation is always AT PERIOD END: the
|
|
1692
|
+
subscription stays active until the end of the paid cycle and remaining
|
|
1693
|
+
Deepline credits are kept \u2014 cancellation never removes credits. --undo
|
|
1694
|
+
reverses a pending cancellation before the period ends. --dry-run reads
|
|
1695
|
+
subscription status and prints the planned mutation without calling the
|
|
1696
|
+
cancel endpoint.
|
|
1697
|
+
|
|
1698
|
+
Exit codes:
|
|
1699
|
+
0 cancellation scheduled (or reversed with --undo)
|
|
1700
|
+
3 auth error
|
|
1701
|
+
4 no active subscription to cancel
|
|
1702
|
+
5 Stripe/server failure (the server message is preserved)
|
|
1703
|
+
|
|
1704
|
+
Examples:
|
|
1705
|
+
deepline billing subscription cancel --dry-run
|
|
1706
|
+
deepline billing subscription cancel
|
|
1707
|
+
deepline billing subscription cancel --undo
|
|
1708
|
+
deepline billing subscription cancel --json
|
|
1709
|
+
`
|
|
1710
|
+
).option("--undo", "Reverse a pending cancellation before period end").option(
|
|
1711
|
+
"--dry-run",
|
|
1712
|
+
"Print the planned cancellation without calling the cancel endpoint"
|
|
1713
|
+
).option(
|
|
1714
|
+
"--json",
|
|
1715
|
+
"Emit JSON output. Also automatic when stdout is piped"
|
|
1716
|
+
).action(handleSubscriptionCancel)
|
|
1717
|
+
);
|
|
1718
|
+
billing.command("invoices").description("List subscription invoices and credit purchase receipts.").addHelpText(
|
|
1719
|
+
"after",
|
|
1720
|
+
`
|
|
1721
|
+
Notes:
|
|
1722
|
+
Read-only. Shows customer-facing billing history from Stripe, newest first:
|
|
1723
|
+
subscription invoices plus one-time Deepline credit purchase receipts, with
|
|
1724
|
+
Stripe-hosted links. Amounts are what you paid \u2014 never provider spend.
|
|
1725
|
+
--compact keeps id/date/amount/status/url only.
|
|
1726
|
+
|
|
1727
|
+
Examples:
|
|
1728
|
+
deepline billing invoices
|
|
1729
|
+
deepline billing invoices --limit 12 --json
|
|
1730
|
+
deepline billing invoices --compact --json
|
|
1731
|
+
`
|
|
1732
|
+
).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);
|
|
1733
|
+
billing.command("redeem-code").description("Redeem a billing code.").addHelpText(
|
|
1734
|
+
"after",
|
|
1735
|
+
`
|
|
1736
|
+
Notes:
|
|
1737
|
+
Redeems a workspace billing code and may open a browser for completion unless
|
|
1738
|
+
--no-open is set.
|
|
1739
|
+
|
|
1740
|
+
Examples:
|
|
1741
|
+
deepline billing redeem-code --code ABC123
|
|
1742
|
+
deepline billing redeem-code --code ABC123 --no-open --json
|
|
1743
|
+
`
|
|
1744
|
+
).requiredOption("--code <code>", "Code to redeem").option("--no-open", "Do not open a browser").option("--json", "Emit JSON output. Also automatic when stdout is piped").action(({ code, ...options }) => handleRedeemCode(code, options));
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
|
|
1748
|
+
|
|
1749
|
+
exports.registerBillingCommands = registerBillingCommands; exports.targetBillingIdempotencyKey = targetBillingIdempotencyKey;
|