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.
Files changed (138) hide show
  1. package/dist/bundling-sources/sdk/src/compat.ts +35 -7
  2. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  3. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +108 -35
  4. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/runtime-api-origin.ts +25 -0
  5. package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +14 -1
  6. package/dist/cli/auth-JCGIDVDF.mjs +31 -0
  7. package/dist/cli/auth-QDWV3TXX.js +31 -0
  8. package/dist/cli/billing-OQBSIXRG.mjs +1749 -0
  9. package/dist/cli/billing-YBK3J4WP.js +1749 -0
  10. package/dist/cli/chunk-2C3AVPTY.js +5688 -0
  11. package/dist/cli/chunk-2MZ5EQZE.mjs +499 -0
  12. package/dist/cli/chunk-3KC45UVF.js +150 -0
  13. package/dist/cli/chunk-3XX5TEA4.js +15 -0
  14. package/dist/cli/chunk-4MBOJQPL.mjs +0 -0
  15. package/dist/cli/chunk-4Y6CG4GE.js +2240 -0
  16. package/dist/cli/chunk-4ZB3CJRD.js +1269 -0
  17. package/dist/cli/chunk-5JLQPVNX.js +3056 -0
  18. package/dist/cli/chunk-6A3TJUWC.js +76 -0
  19. package/dist/cli/chunk-6HPEDBSJ.js +1995 -0
  20. package/dist/cli/chunk-7D6QG5KF.js +890 -0
  21. package/dist/cli/chunk-7P5AA3SN.js +344 -0
  22. package/dist/cli/chunk-7UAO5MWV.mjs +190 -0
  23. package/dist/cli/chunk-A7427JJH.js +190 -0
  24. package/dist/cli/chunk-AQRTLPHZ.js +735 -0
  25. package/dist/cli/chunk-AYHLUHE5.js +52 -0
  26. package/dist/cli/chunk-BZ77OTRF.mjs +923 -0
  27. package/dist/cli/chunk-CBVIJSHF.mjs +560 -0
  28. package/dist/cli/chunk-COGTRVWW.js +30 -0
  29. package/dist/cli/chunk-DLIRSHV6.mjs +28 -0
  30. package/dist/cli/chunk-DYWHXO7O.mjs +34 -0
  31. package/dist/cli/chunk-EDWJY6T4.mjs +3056 -0
  32. package/dist/cli/chunk-EXMLR72R.mjs +0 -0
  33. package/dist/cli/chunk-G46HHFNB.mjs +1269 -0
  34. package/dist/cli/chunk-GAMFK2ZP.mjs +148 -0
  35. package/dist/cli/chunk-GLKUGBTP.mjs +2240 -0
  36. package/dist/cli/chunk-GONRLVV3.js +3440 -0
  37. package/dist/cli/chunk-GVUJJJ5G.mjs +76 -0
  38. package/dist/cli/chunk-H7HIDGZ7.mjs +150 -0
  39. package/dist/cli/chunk-HUZXWOSK.mjs +503 -0
  40. package/dist/cli/chunk-I5YZLGMG.js +1 -0
  41. package/dist/cli/chunk-ITOFSXIU.mjs +15 -0
  42. package/dist/cli/chunk-J2D3VDOU.mjs +5688 -0
  43. package/dist/cli/chunk-J5NA2TTX.js +503 -0
  44. package/dist/cli/chunk-JCS5TPNY.mjs +3440 -0
  45. package/dist/cli/chunk-JNNOCMQY.js +47 -0
  46. package/dist/cli/chunk-KRD6H754.mjs +30 -0
  47. package/dist/cli/chunk-L4HBNHFW.js +34 -0
  48. package/dist/cli/chunk-LA46MMTL.js +560 -0
  49. package/dist/cli/chunk-LIKFCE7Z.js +190 -0
  50. package/dist/cli/chunk-MIMASXO4.mjs +389 -0
  51. package/dist/cli/chunk-NPPJX3BL.mjs +1995 -0
  52. package/dist/cli/chunk-NTEZFZLE.mjs +890 -0
  53. package/dist/cli/chunk-NXDRDJ4K.js +389 -0
  54. package/dist/cli/chunk-NXGCO4JB.mjs +344 -0
  55. package/dist/cli/chunk-OG64RBAC.js +6 -0
  56. package/dist/cli/chunk-OWJZUE3Y.mjs +103 -0
  57. package/dist/cli/chunk-QSIO4TX2.mjs +190 -0
  58. package/dist/cli/chunk-RCUE5C5C.mjs +735 -0
  59. package/dist/cli/chunk-SDK3ZA2C.js +103 -0
  60. package/dist/cli/chunk-SFLARO4Y.mjs +47 -0
  61. package/dist/cli/chunk-TAC3JIFE.mjs +354 -0
  62. package/dist/cli/chunk-UC6GHEL4.js +499 -0
  63. package/dist/cli/chunk-UFS752TU.mjs +52 -0
  64. package/dist/cli/chunk-UNEMDKXN.js +1 -0
  65. package/dist/cli/chunk-WB6Z2Q44.mjs +1622 -0
  66. package/dist/cli/chunk-WJYPBHTE.js +148 -0
  67. package/dist/cli/chunk-WOHBA5PV.js +354 -0
  68. package/dist/cli/chunk-WZYKRD7T.mjs +6 -0
  69. package/dist/cli/chunk-XNYIZMNQ.js +28 -0
  70. package/dist/cli/chunk-ZDO3TO3M.js +1622 -0
  71. package/dist/cli/chunk-ZDOC4HFL.js +923 -0
  72. package/dist/cli/client-CZDA4JKO.mjs +20 -0
  73. package/dist/cli/client-IZGIN2ZA.js +20 -0
  74. package/dist/cli/csv-6KOMZA2H.mjs +585 -0
  75. package/dist/cli/csv-UTEQY3NA.js +585 -0
  76. package/dist/cli/db-D5EP6TKO.mjs +486 -0
  77. package/dist/cli/db-SIBLPIKC.js +486 -0
  78. package/dist/cli/executions-2PHT57IO.js +28 -0
  79. package/dist/cli/executions-DFWM2YYY.mjs +28 -0
  80. package/dist/cli/feedback-GZW37JMI.mjs +107 -0
  81. package/dist/cli/feedback-IQVWIJZ4.js +107 -0
  82. package/dist/cli/getting-started.js +18 -76
  83. package/dist/cli/getting-started.mjs +10 -37
  84. package/dist/cli/git-563GYLMA.js +23 -0
  85. package/dist/cli/git-B72Y4FFQ.mjs +23 -0
  86. package/dist/cli/index.js +291 -54294
  87. package/dist/cli/index.mjs +209 -54352
  88. package/dist/cli/legacy-noops-37F5MX2P.mjs +157 -0
  89. package/dist/cli/legacy-noops-IFGVDCOM.js +157 -0
  90. package/dist/cli/monitors-42H6LOVT.mjs +3251 -0
  91. package/dist/cli/monitors-GQCHXBXZ.js +3251 -0
  92. package/dist/cli/org-4GCPBTGR.js +849 -0
  93. package/dist/cli/org-JYZA3ZRJ.mjs +849 -0
  94. package/dist/cli/play-46ZJ4FMI.js +18439 -0
  95. package/dist/cli/play-Q2XV3FZZ.mjs +18439 -0
  96. package/dist/cli/providers-CTKQP45X.mjs +95 -0
  97. package/dist/cli/providers-PCNF5E3K.js +95 -0
  98. package/dist/cli/proxy-env-EBANZHPV.js +161 -0
  99. package/dist/cli/proxy-env-M4ZHXHRL.mjs +161 -0
  100. package/dist/cli/quickstart-FTHWRVWN.js +69 -0
  101. package/dist/cli/quickstart-X7EVVWRG.mjs +69 -0
  102. package/dist/cli/search-73YPOFDO.mjs +18 -0
  103. package/dist/cli/search-XA5UVC3G.js +18 -0
  104. package/dist/cli/secrets-OUMPT4FX.mjs +400 -0
  105. package/dist/cli/secrets-UOEVR73Q.js +400 -0
  106. package/dist/cli/sessions-CMKXCN5Y.js +15 -0
  107. package/dist/cli/sessions-ZIFGDZE7.mjs +15 -0
  108. package/dist/cli/settings-OO5R3G52.mjs +846 -0
  109. package/dist/cli/settings-ULZT2NIC.js +846 -0
  110. package/dist/cli/setup-6GZH52OZ.mjs +1257 -0
  111. package/dist/cli/setup-DHGERXPZ.js +1257 -0
  112. package/dist/cli/skills-Q7NUCS2W.js +28 -0
  113. package/dist/cli/skills-T3EY2656.mjs +28 -0
  114. package/dist/cli/tools-BIMOAOTX.mjs +2955 -0
  115. package/dist/cli/tools-ODXVCKO7.js +2955 -0
  116. package/dist/cli/update-UMB3ULBR.js +28 -0
  117. package/dist/cli/update-X4HX7Z3M.mjs +28 -0
  118. package/dist/cli/workflow-EAUFVPIP.mjs +1936 -0
  119. package/dist/cli/workflow-IVLCPFTG.js +1936 -0
  120. package/dist/helpers.js +6 -32
  121. package/dist/helpers.mjs +2 -0
  122. package/dist/index.js +1972 -12825
  123. package/dist/index.mjs +1910 -12672
  124. package/dist/install-integrity.json +110 -0
  125. package/dist/monitor-fleet-contract.js +31 -730
  126. package/dist/monitor-fleet-contract.mjs +18 -667
  127. package/dist/monitor-input-contract.js +10 -390
  128. package/dist/monitor-input-contract.mjs +7 -348
  129. package/dist/plays/bundle-play-file.mjs +13 -5178
  130. package/dist/plays/csv-data-input.js +5 -3132
  131. package/dist/plays/csv-data-input.mjs +5 -3105
  132. package/dist/plays/run-request.js +56 -664
  133. package/dist/plays/run-request.mjs +30 -588
  134. package/dist/release.d.mts +1 -1
  135. package/dist/release.d.ts +1 -1
  136. package/dist/release.js +5 -212
  137. package/dist/release.mjs +5 -185
  138. 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;