@tarout/cli 0.2.1 → 0.2.2

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.
@@ -2,8 +2,8 @@ import {
2
2
  createApiClient,
3
3
  getApiClient,
4
4
  resetApiClient
5
- } from "./chunk-FKX4CRPL.js";
6
- import "./chunk-FS74WWHV.js";
5
+ } from "./chunk-7YS2WBLB.js";
6
+ import "./chunk-KL3JNPAY.js";
7
7
  export {
8
8
  createApiClient,
9
9
  getApiClient,
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  pollCheckoutUntilTerminal,
3
3
  registerBillingCommands
4
- } from "./chunk-XSJKHLNL.js";
5
- import "./chunk-FKX4CRPL.js";
6
- import "./chunk-J3H7LTFT.js";
7
- import "./chunk-FS74WWHV.js";
4
+ } from "./chunk-5XBVQICT.js";
5
+ import "./chunk-7YS2WBLB.js";
6
+ import "./chunk-CJMIX35A.js";
7
+ import "./chunk-KL3JNPAY.js";
8
8
  export {
9
9
  pollCheckoutUntilTerminal,
10
10
  registerBillingCommands
@@ -3,11 +3,11 @@ import {
3
3
  getApiClient,
4
4
  handleError,
5
5
  isLoggedIn
6
- } from "./chunk-FKX4CRPL.js";
6
+ } from "./chunk-7YS2WBLB.js";
7
7
  import {
8
8
  confirm,
9
9
  select
10
- } from "./chunk-J3H7LTFT.js";
10
+ } from "./chunk-CJMIX35A.js";
11
11
  import {
12
12
  ExitCode,
13
13
  box,
@@ -20,9 +20,10 @@ import {
20
20
  outputJsonLine,
21
21
  shouldSkipConfirmation,
22
22
  table
23
- } from "./chunk-FS74WWHV.js";
23
+ } from "./chunk-KL3JNPAY.js";
24
24
 
25
25
  // src/commands/billing.ts
26
+ import { InvalidArgumentError } from "commander";
26
27
  import open from "open";
27
28
 
28
29
  // src/utils/spinner.ts
@@ -153,10 +154,22 @@ function registerBillingCommands(program) {
153
154
  handleError(err);
154
155
  }
155
156
  });
156
- billing.command("upgrade").argument("[plan]", "Plan key to switch to").description("Upgrade or change subscription plan").option(
157
+ billing.command("upgrade").argument("[plan]", "Plan key to switch to (alias: --plan)").description("Upgrade or change subscription plan").option(
158
+ "--plan <key>",
159
+ "Plan key (alias for the positional argument; useful for agent invocations)"
160
+ ).option(
157
161
  "-q, --quantity <n>",
158
162
  "Plan quantity (for multi-slot plans)",
159
163
  Number.parseInt
164
+ ).option(
165
+ "--billing-period <period>",
166
+ "Billing period: monthly or yearly (yearly = 10\xD7 monthly, 2 months free)",
167
+ parseBillingPeriod
168
+ ).option(
169
+ "--addon <key[:qty]>",
170
+ "Bundled addon to purchase with the plan change (repeatable, e.g. --addon db.standard:2)",
171
+ collectAddon,
172
+ []
160
173
  ).option(
161
174
  "-w, --wait",
162
175
  "After hosted-checkout opens, poll status until the payment is confirmed"
@@ -172,7 +185,9 @@ function registerBillingCommands(program) {
172
185
  try {
173
186
  if (!isLoggedIn()) throw new AuthError();
174
187
  const client = getApiClient();
175
- let targetPlan = planKey;
188
+ let targetPlan = planKey || options.plan;
189
+ const billingPeriod = options.billingPeriod;
190
+ const addons = Array.isArray(options.addon) && options.addon.length > 0 ? options.addon : void 0;
176
191
  if (!targetPlan) {
177
192
  const _spinner = startSpinner("Fetching plans...");
178
193
  const catalog = await client.subscription.getCatalog.query();
@@ -187,15 +202,30 @@ function registerBillingCommands(program) {
187
202
  plans.map((p) => ({
188
203
  name: `${p.planKey || p.key || p.name} ${p.priceHalalas ? `(${(p.priceHalalas / 100).toFixed(2)} SAR/mo)` : "(Free)"}`,
189
204
  value: p.planKey || p.key || p.name
190
- }))
205
+ })),
206
+ {
207
+ field: "plan",
208
+ flag: "--plan",
209
+ context: {
210
+ available: plans.map((p) => ({
211
+ key: p.planKey || p.key || p.name,
212
+ priceHalalas: p.priceHalalas ?? 0
213
+ }))
214
+ }
215
+ }
191
216
  );
192
217
  }
218
+ if (!targetPlan) {
219
+ throw new Error("No plan selected");
220
+ }
193
221
  const _previewSpinner = startSpinner("Calculating change...");
194
222
  let preview;
195
223
  try {
196
224
  preview = await client.subscription.previewPlanChange.query({
197
225
  planKey: targetPlan,
198
- planQuantity: options.quantity
226
+ planQuantity: options.quantity,
227
+ billingPeriod,
228
+ addons
199
229
  });
200
230
  succeedSpinner();
201
231
  } catch {
@@ -206,6 +236,12 @@ function registerBillingCommands(program) {
206
236
  log("");
207
237
  log(`Plan: ${colors.cyan(targetPlan)}`);
208
238
  if (options.quantity) log(`Quantity: ${options.quantity}`);
239
+ if (billingPeriod) log(`Billing period: ${billingPeriod}`);
240
+ if (addons && addons.length > 0) {
241
+ log(
242
+ `Addons: ${addons.map((a) => `${a.addonKey}\xD7${a.quantity}`).join(", ")}`
243
+ );
244
+ }
209
245
  if (preview?.amountDue !== void 0) {
210
246
  log(
211
247
  `Amount due now: ${colors.bold(`${(preview.amountDue / 100).toFixed(2)} SAR`)}`
@@ -214,7 +250,18 @@ function registerBillingCommands(program) {
214
250
  log("");
215
251
  const confirmed = await confirm(
216
252
  `Switch to plan "${targetPlan}"?`,
217
- false
253
+ false,
254
+ {
255
+ field: "confirm_upgrade",
256
+ flag: "--yes",
257
+ context: {
258
+ plan: targetPlan,
259
+ quantity: options.quantity,
260
+ billingPeriod,
261
+ addons,
262
+ amountDueHalalas: preview?.amountDue
263
+ }
264
+ }
218
265
  );
219
266
  if (!confirmed) {
220
267
  log("Cancelled.");
@@ -224,7 +271,9 @@ function registerBillingCommands(program) {
224
271
  const _changeSpinner = startSpinner("Changing plan...");
225
272
  const result = await client.subscription.changePlan.mutate({
226
273
  planKey: targetPlan,
227
- planQuantity: options.quantity
274
+ planQuantity: options.quantity,
275
+ billingPeriod,
276
+ addons
228
277
  });
229
278
  succeedSpinner("Plan changed!");
230
279
  if (result?.applied) {
@@ -369,6 +418,14 @@ function registerBillingCommands(program) {
369
418
  try {
370
419
  if (!isLoggedIn()) throw new AuthError();
371
420
  const client = getApiClient();
421
+ if (isJsonMode()) {
422
+ outputJsonLine({
423
+ type: "event",
424
+ event: "checkout_polling_started",
425
+ orderId,
426
+ timeoutSeconds: options.timeout
427
+ });
428
+ }
372
429
  const final = await pollCheckoutUntilTerminal(client, orderId, {
373
430
  timeoutMs: options.timeout * 1e3,
374
431
  intervalMs: 4e3
@@ -414,7 +471,8 @@ function registerBillingCommands(program) {
414
471
  log("");
415
472
  const confirmed = await confirm(
416
473
  "Are you sure you want to cancel your subscription?",
417
- false
474
+ false,
475
+ { field: "confirm_cancel", flag: "--yes" }
418
476
  );
419
477
  if (!confirmed) {
420
478
  log("Cancelled.");
@@ -472,7 +530,12 @@ function registerBillingCommands(program) {
472
530
  log("");
473
531
  const confirmed = await confirm(
474
532
  `Add addon "${addonKey}" \xD7 ${quantity}?`,
475
- false
533
+ false,
534
+ {
535
+ field: "confirm_addon_add",
536
+ flag: "--yes",
537
+ context: { addonKey, quantity }
538
+ }
476
539
  );
477
540
  if (!confirmed) {
478
541
  log("Cancelled.");
@@ -503,7 +566,15 @@ function registerBillingCommands(program) {
503
566
  try {
504
567
  if (!isLoggedIn()) throw new AuthError();
505
568
  if (!shouldSkipConfirmation()) {
506
- const confirmed = await confirm(`Remove addon "${addonKey}"?`, false);
569
+ const confirmed = await confirm(
570
+ `Remove addon "${addonKey}"?`,
571
+ false,
572
+ {
573
+ field: "confirm_addon_remove",
574
+ flag: "--yes",
575
+ context: { addonKey }
576
+ }
577
+ );
507
578
  if (!confirmed) {
508
579
  log("Cancelled.");
509
580
  return;
@@ -584,7 +655,11 @@ function registerBillingCommands(program) {
584
655
  if (!shouldSkipConfirmation()) {
585
656
  log(`
586
657
  Purchase ${quantity}\xD7 ${colors.cyan(addonKey)}?`);
587
- const confirmed = await confirm("Proceed?", false);
658
+ const confirmed = await confirm("Proceed?", false, {
659
+ field: "confirm_addon_buy",
660
+ flag: "--yes",
661
+ context: { addonKey, quantity }
662
+ });
588
663
  if (!confirmed) {
589
664
  log("Cancelled.");
590
665
  return;
@@ -614,7 +689,8 @@ Payment required: ${colors.cyan(result.paymentUrl)}
614
689
  if (!shouldSkipConfirmation()) {
615
690
  const confirmed = await confirm(
616
691
  "Cancel the pending plan change?",
617
- false
692
+ false,
693
+ { field: "confirm_pending_cancel", flag: "--yes" }
618
694
  );
619
695
  if (!confirmed) {
620
696
  log("Cancelled.");
@@ -867,6 +943,29 @@ async function pollCheckoutUntilTerminal(client, orderId, opts) {
867
943
  failureReason: r.failureReason
868
944
  };
869
945
  }
946
+ function collectAddon(value, previous) {
947
+ const [rawKey, rawQty] = value.split(":");
948
+ const addonKey = (rawKey || "").trim();
949
+ if (!addonKey) {
950
+ throw new InvalidArgumentError(
951
+ `Invalid --addon value "${value}". Expected key[:qty] (e.g. db.standard:2).`
952
+ );
953
+ }
954
+ const quantity = rawQty === void 0 ? 1 : Number.parseInt(rawQty, 10);
955
+ if (!Number.isFinite(quantity) || quantity <= 0) {
956
+ throw new InvalidArgumentError(
957
+ `Invalid --addon quantity in "${value}". Expected a positive integer.`
958
+ );
959
+ }
960
+ return [...previous, { addonKey, quantity }];
961
+ }
962
+ function parseBillingPeriod(raw) {
963
+ const v = raw.toLowerCase();
964
+ if (v === "monthly" || v === "yearly") return v;
965
+ throw new InvalidArgumentError(
966
+ `Invalid --billing-period "${raw}". Expected "monthly" or "yearly".`
967
+ );
968
+ }
870
969
  function formatSubStatus(status) {
871
970
  const map = {
872
971
  active: colors.success("active"),
@@ -5,7 +5,7 @@ import {
5
5
  isJsonMode,
6
6
  jsonError,
7
7
  outputJson
8
- } from "./chunk-FS74WWHV.js";
8
+ } from "./chunk-KL3JNPAY.js";
9
9
 
10
10
  // src/lib/api.ts
11
11
  import { createTRPCProxyClient, httpBatchLink } from "@trpc/client";
@@ -0,0 +1,127 @@
1
+ import {
2
+ ExitCode,
3
+ exit,
4
+ isJsonMode,
5
+ isNonInteractiveMode,
6
+ outputNeedsInput
7
+ } from "./chunk-KL3JNPAY.js";
8
+
9
+ // src/utils/prompts.ts
10
+ import inquirer from "inquirer";
11
+ function emitNeedsInputAndExit(descriptor, rest) {
12
+ outputNeedsInput({ ...descriptor, ...rest });
13
+ exit(ExitCode.NEEDS_INPUT);
14
+ }
15
+ function shouldEmitNeedsInput() {
16
+ return isJsonMode() || isNonInteractiveMode();
17
+ }
18
+ function emitUnannotatedPromptError(kind, question) {
19
+ outputNeedsInput({
20
+ field: "unannotated_prompt",
21
+ kind,
22
+ question,
23
+ flag: "--yes",
24
+ context: {
25
+ hint: "This prompt site does not yet expose a CLI flag. Re-invoke with --yes (for confirms) or provide the value as a positional/flag argument, or file an issue to ask for explicit flag support for this command."
26
+ }
27
+ });
28
+ exit(ExitCode.NEEDS_INPUT);
29
+ }
30
+ async function confirm(message, defaultValue = false, descriptor) {
31
+ if (descriptor && shouldEmitNeedsInput()) {
32
+ emitNeedsInputAndExit(descriptor, {
33
+ kind: "confirm",
34
+ question: message,
35
+ default: defaultValue
36
+ });
37
+ }
38
+ if (!descriptor && shouldEmitNeedsInput()) {
39
+ emitUnannotatedPromptError("confirm", message);
40
+ }
41
+ const { confirmed } = await inquirer.prompt([
42
+ {
43
+ type: "confirm",
44
+ name: "confirmed",
45
+ message,
46
+ default: defaultValue
47
+ }
48
+ ]);
49
+ return confirmed;
50
+ }
51
+ async function input(message, defaultValue, descriptor) {
52
+ if (descriptor && shouldEmitNeedsInput()) {
53
+ emitNeedsInputAndExit(descriptor, {
54
+ kind: "input",
55
+ question: message,
56
+ default: defaultValue
57
+ });
58
+ }
59
+ if (!descriptor && shouldEmitNeedsInput()) {
60
+ emitUnannotatedPromptError("input", message);
61
+ }
62
+ const { value } = await inquirer.prompt([
63
+ {
64
+ type: "input",
65
+ name: "value",
66
+ message,
67
+ default: defaultValue
68
+ }
69
+ ]);
70
+ return value;
71
+ }
72
+ async function select(message, choices, descriptor) {
73
+ if (descriptor && shouldEmitNeedsInput()) {
74
+ emitNeedsInputAndExit(descriptor, {
75
+ kind: "select",
76
+ question: message,
77
+ choices: choices.map((c) => ({ label: c.name, value: c.value }))
78
+ });
79
+ }
80
+ if (!descriptor && shouldEmitNeedsInput()) {
81
+ emitUnannotatedPromptError("select", message);
82
+ }
83
+ const { value } = await inquirer.prompt([
84
+ {
85
+ type: "list",
86
+ name: "value",
87
+ message,
88
+ choices
89
+ }
90
+ ]);
91
+ return value;
92
+ }
93
+ async function password(message, descriptor) {
94
+ if (descriptor && shouldEmitNeedsInput()) {
95
+ emitNeedsInputAndExit(
96
+ { ...descriptor, sensitive: true },
97
+ { kind: "password", question: message }
98
+ );
99
+ }
100
+ if (!descriptor && shouldEmitNeedsInput()) {
101
+ emitUnannotatedPromptError("password", message);
102
+ }
103
+ const { value } = await inquirer.prompt([
104
+ {
105
+ type: "password",
106
+ name: "value",
107
+ message,
108
+ mask: "*"
109
+ }
110
+ ]);
111
+ return value;
112
+ }
113
+ async function promptOrEmit(req, fallback) {
114
+ if (shouldEmitNeedsInput()) {
115
+ outputNeedsInput(req);
116
+ exit(ExitCode.NEEDS_INPUT);
117
+ }
118
+ return fallback();
119
+ }
120
+
121
+ export {
122
+ confirm,
123
+ input,
124
+ select,
125
+ password,
126
+ promptOrEmit
127
+ };
@@ -43,7 +43,7 @@ function jsonError(code, message, suggestions, details) {
43
43
  };
44
44
  }
45
45
  function outputJson(response) {
46
- console.log(JSON.stringify(response, null, 2));
46
+ console.log(JSON.stringify(response));
47
47
  }
48
48
 
49
49
  // src/lib/output.ts
@@ -52,7 +52,8 @@ var globalOptions = {
52
52
  quiet: false,
53
53
  verbose: false,
54
54
  noColor: false,
55
- yes: false
55
+ yes: false,
56
+ nonInteractive: false
56
57
  };
57
58
  function setGlobalOptions(options) {
58
59
  globalOptions = { ...globalOptions, ...options };
@@ -63,6 +64,9 @@ function isJsonMode() {
63
64
  function shouldSkipConfirmation() {
64
65
  return globalOptions.yes;
65
66
  }
67
+ function isNonInteractiveMode() {
68
+ return globalOptions.nonInteractive;
69
+ }
66
70
  function c(colorFn, str) {
67
71
  return globalOptions.noColor ? str : colorFn(str);
68
72
  }
@@ -183,6 +187,7 @@ export {
183
187
  setGlobalOptions,
184
188
  isJsonMode,
185
189
  shouldSkipConfirmation,
190
+ isNonInteractiveMode,
186
191
  colors,
187
192
  getStatusBadge,
188
193
  log,