@tarout/cli 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -0
- package/bin/tarout-mcp +3 -0
- package/dist/{api-SHKZO2SZ.js → api-QAKANRFX.js} +3 -2
- package/dist/billing-GUA4S2Y4.js +12 -0
- package/dist/chunk-5DAFGMBH.js +137 -0
- package/dist/{chunk-XSJKHLNL.js → chunk-BS6DFVSU.js} +116 -15
- package/dist/chunk-CJMIX35A.js +127 -0
- package/dist/{chunk-FS74WWHV.js → chunk-KL3JNPAY.js} +7 -2
- package/dist/{chunk-FKX4CRPL.js → chunk-NHNK5ZQ5.js} +6 -136
- package/dist/index.js +2365 -1641
- package/dist/mcp/stdio.d.ts +2 -0
- package/dist/mcp/stdio.js +55 -0
- package/dist/{prompts-WNFR34TG.js → prompts-QQ2FZKQT.js} +2 -2
- package/package.json +6 -4
- package/dist/billing-ENTHZKID.js +0 -11
- package/dist/chunk-J3H7LTFT.js +0 -68
package/README.md
CHANGED
|
@@ -39,6 +39,28 @@ tarout deploy --wait --source upload
|
|
|
39
39
|
# The first deploy prompts to create or link an app, and to create detected resources.
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
+
## Call any API (`tarout call`)
|
|
43
|
+
|
|
44
|
+
Beyond the curated commands, `tarout call` reaches **every** platform procedure
|
|
45
|
+
directly — the same control surface exposed via REST and MCP:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
tarout call --list # discover all callable procedures + type
|
|
49
|
+
tarout call application.create --input '{"name":"my-app"}' --json
|
|
50
|
+
tarout call deployment.all --input '{"applicationId":"app_123"}'
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## MCP (agent access)
|
|
54
|
+
|
|
55
|
+
Connect Claude Desktop / Cursor to Tarout via the bundled `tarout-mcp` bridge:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{ "mcpServers": { "tarout": { "command": "tarout-mcp" } } }
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Hosted agents can hit the Streamable-HTTP endpoint `/api/mcp` directly with an
|
|
62
|
+
`x-api-key` header. See [`docs/MCP.md`](../docs/MCP.md).
|
|
63
|
+
|
|
42
64
|
## Commands
|
|
43
65
|
|
|
44
66
|
### Authentication
|
package/bin/tarout-mcp
ADDED
|
@@ -2,8 +2,9 @@ import {
|
|
|
2
2
|
createApiClient,
|
|
3
3
|
getApiClient,
|
|
4
4
|
resetApiClient
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-NHNK5ZQ5.js";
|
|
6
|
+
import "./chunk-5DAFGMBH.js";
|
|
7
|
+
import "./chunk-KL3JNPAY.js";
|
|
7
8
|
export {
|
|
8
9
|
createApiClient,
|
|
9
10
|
getApiClient,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
pollCheckoutUntilTerminal,
|
|
3
|
+
registerBillingCommands
|
|
4
|
+
} from "./chunk-BS6DFVSU.js";
|
|
5
|
+
import "./chunk-NHNK5ZQ5.js";
|
|
6
|
+
import "./chunk-5DAFGMBH.js";
|
|
7
|
+
import "./chunk-CJMIX35A.js";
|
|
8
|
+
import "./chunk-KL3JNPAY.js";
|
|
9
|
+
export {
|
|
10
|
+
pollCheckoutUntilTerminal,
|
|
11
|
+
registerBillingCommands
|
|
12
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// src/lib/config.ts
|
|
2
|
+
import Conf from "conf";
|
|
3
|
+
import {
|
|
4
|
+
chmodSync,
|
|
5
|
+
existsSync,
|
|
6
|
+
mkdirSync,
|
|
7
|
+
readFileSync,
|
|
8
|
+
rmSync,
|
|
9
|
+
writeFileSync
|
|
10
|
+
} from "fs";
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
var config = new Conf({
|
|
13
|
+
projectName: "tarout",
|
|
14
|
+
configFileMode: 384,
|
|
15
|
+
defaults: {
|
|
16
|
+
currentProfile: "default",
|
|
17
|
+
profiles: {}
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
function getConfig() {
|
|
21
|
+
return config.store;
|
|
22
|
+
}
|
|
23
|
+
function getCurrentProfile() {
|
|
24
|
+
const cfg = getConfig();
|
|
25
|
+
return cfg.profiles[cfg.currentProfile] || null;
|
|
26
|
+
}
|
|
27
|
+
function setProfile(name, profile) {
|
|
28
|
+
config.set(`profiles.${name}`, profile);
|
|
29
|
+
}
|
|
30
|
+
function setCurrentProfile(name) {
|
|
31
|
+
config.set("currentProfile", name);
|
|
32
|
+
}
|
|
33
|
+
function clearConfig() {
|
|
34
|
+
config.clear();
|
|
35
|
+
}
|
|
36
|
+
function isLoggedIn() {
|
|
37
|
+
const profile = getCurrentProfile();
|
|
38
|
+
return profile !== null && !!profile.token || !!process.env.TAROUT_TOKEN;
|
|
39
|
+
}
|
|
40
|
+
function getToken() {
|
|
41
|
+
const profile = getCurrentProfile();
|
|
42
|
+
return profile?.token || process.env.TAROUT_TOKEN || null;
|
|
43
|
+
}
|
|
44
|
+
function getApiUrl() {
|
|
45
|
+
const profile = getCurrentProfile();
|
|
46
|
+
return profile?.apiUrl || process.env.TAROUT_API_URL || "https://tarout.sa";
|
|
47
|
+
}
|
|
48
|
+
function updateProfile(updates) {
|
|
49
|
+
const cfg = getConfig();
|
|
50
|
+
const currentProfileName = cfg.currentProfile;
|
|
51
|
+
const currentProfile = cfg.profiles[currentProfileName];
|
|
52
|
+
if (currentProfile) {
|
|
53
|
+
config.set(`profiles.${currentProfileName}`, {
|
|
54
|
+
...currentProfile,
|
|
55
|
+
...updates
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
var PROJECT_CONFIG_DIR = ".tarout";
|
|
60
|
+
var PROJECT_CONFIG_FILE = "project.json";
|
|
61
|
+
function chmodIfSupported(path, mode) {
|
|
62
|
+
try {
|
|
63
|
+
chmodSync(path, mode);
|
|
64
|
+
} catch {
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function getProjectConfigDir(basePath) {
|
|
68
|
+
const base = basePath || process.cwd();
|
|
69
|
+
return join(base, PROJECT_CONFIG_DIR);
|
|
70
|
+
}
|
|
71
|
+
function getProjectConfigPath(basePath) {
|
|
72
|
+
return join(getProjectConfigDir(basePath), PROJECT_CONFIG_FILE);
|
|
73
|
+
}
|
|
74
|
+
function isProjectLinked(basePath) {
|
|
75
|
+
return existsSync(getProjectConfigPath(basePath));
|
|
76
|
+
}
|
|
77
|
+
function getProjectConfig(basePath) {
|
|
78
|
+
const configPath = getProjectConfigPath(basePath);
|
|
79
|
+
if (!existsSync(configPath)) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const content = readFileSync(configPath, "utf-8");
|
|
84
|
+
return JSON.parse(content);
|
|
85
|
+
} catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function setProjectConfig(config2, basePath) {
|
|
90
|
+
const configDir = getProjectConfigDir(basePath);
|
|
91
|
+
const configPath = getProjectConfigPath(basePath);
|
|
92
|
+
if (!existsSync(configDir)) {
|
|
93
|
+
mkdirSync(configDir, { recursive: true, mode: 448 });
|
|
94
|
+
}
|
|
95
|
+
chmodIfSupported(configDir, 448);
|
|
96
|
+
writeFileSync(configPath, JSON.stringify(config2, null, 2), {
|
|
97
|
+
encoding: "utf-8",
|
|
98
|
+
mode: 384
|
|
99
|
+
});
|
|
100
|
+
chmodIfSupported(configPath, 384);
|
|
101
|
+
const gitignorePath = join(configDir, ".gitignore");
|
|
102
|
+
if (!existsSync(gitignorePath)) {
|
|
103
|
+
writeFileSync(
|
|
104
|
+
gitignorePath,
|
|
105
|
+
"# Ignore local tarout config\n*\n!.gitignore\n",
|
|
106
|
+
{ encoding: "utf-8", mode: 384 }
|
|
107
|
+
);
|
|
108
|
+
chmodIfSupported(gitignorePath, 384);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function removeProjectConfig(basePath) {
|
|
112
|
+
const configDir = getProjectConfigDir(basePath);
|
|
113
|
+
if (!existsSync(configDir)) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
rmSync(configDir, { recursive: true, force: true });
|
|
118
|
+
return true;
|
|
119
|
+
} catch {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export {
|
|
125
|
+
getCurrentProfile,
|
|
126
|
+
setProfile,
|
|
127
|
+
setCurrentProfile,
|
|
128
|
+
clearConfig,
|
|
129
|
+
isLoggedIn,
|
|
130
|
+
getToken,
|
|
131
|
+
getApiUrl,
|
|
132
|
+
updateProfile,
|
|
133
|
+
isProjectLinked,
|
|
134
|
+
getProjectConfig,
|
|
135
|
+
setProjectConfig,
|
|
136
|
+
removeProjectConfig
|
|
137
|
+
};
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AuthError,
|
|
3
3
|
getApiClient,
|
|
4
|
-
handleError
|
|
4
|
+
handleError
|
|
5
|
+
} from "./chunk-NHNK5ZQ5.js";
|
|
6
|
+
import {
|
|
5
7
|
isLoggedIn
|
|
6
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-5DAFGMBH.js";
|
|
7
9
|
import {
|
|
8
10
|
confirm,
|
|
9
11
|
select
|
|
10
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-CJMIX35A.js";
|
|
11
13
|
import {
|
|
12
14
|
ExitCode,
|
|
13
15
|
box,
|
|
@@ -20,9 +22,10 @@ import {
|
|
|
20
22
|
outputJsonLine,
|
|
21
23
|
shouldSkipConfirmation,
|
|
22
24
|
table
|
|
23
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-KL3JNPAY.js";
|
|
24
26
|
|
|
25
27
|
// src/commands/billing.ts
|
|
28
|
+
import { InvalidArgumentError } from "commander";
|
|
26
29
|
import open from "open";
|
|
27
30
|
|
|
28
31
|
// src/utils/spinner.ts
|
|
@@ -153,10 +156,22 @@ function registerBillingCommands(program) {
|
|
|
153
156
|
handleError(err);
|
|
154
157
|
}
|
|
155
158
|
});
|
|
156
|
-
billing.command("upgrade").argument("[plan]", "Plan key to switch to").description("Upgrade or change subscription plan").option(
|
|
159
|
+
billing.command("upgrade").argument("[plan]", "Plan key to switch to (alias: --plan)").description("Upgrade or change subscription plan").option(
|
|
160
|
+
"--plan <key>",
|
|
161
|
+
"Plan key (alias for the positional argument; useful for agent invocations)"
|
|
162
|
+
).option(
|
|
157
163
|
"-q, --quantity <n>",
|
|
158
164
|
"Plan quantity (for multi-slot plans)",
|
|
159
165
|
Number.parseInt
|
|
166
|
+
).option(
|
|
167
|
+
"--billing-period <period>",
|
|
168
|
+
"Billing period: monthly or yearly (yearly = 10\xD7 monthly, 2 months free)",
|
|
169
|
+
parseBillingPeriod
|
|
170
|
+
).option(
|
|
171
|
+
"--addon <key[:qty]>",
|
|
172
|
+
"Bundled addon to purchase with the plan change (repeatable, e.g. --addon db.standard:2)",
|
|
173
|
+
collectAddon,
|
|
174
|
+
[]
|
|
160
175
|
).option(
|
|
161
176
|
"-w, --wait",
|
|
162
177
|
"After hosted-checkout opens, poll status until the payment is confirmed"
|
|
@@ -172,7 +187,9 @@ function registerBillingCommands(program) {
|
|
|
172
187
|
try {
|
|
173
188
|
if (!isLoggedIn()) throw new AuthError();
|
|
174
189
|
const client = getApiClient();
|
|
175
|
-
let targetPlan = planKey;
|
|
190
|
+
let targetPlan = planKey || options.plan;
|
|
191
|
+
const billingPeriod = options.billingPeriod;
|
|
192
|
+
const addons = Array.isArray(options.addon) && options.addon.length > 0 ? options.addon : void 0;
|
|
176
193
|
if (!targetPlan) {
|
|
177
194
|
const _spinner = startSpinner("Fetching plans...");
|
|
178
195
|
const catalog = await client.subscription.getCatalog.query();
|
|
@@ -187,15 +204,30 @@ function registerBillingCommands(program) {
|
|
|
187
204
|
plans.map((p) => ({
|
|
188
205
|
name: `${p.planKey || p.key || p.name} ${p.priceHalalas ? `(${(p.priceHalalas / 100).toFixed(2)} SAR/mo)` : "(Free)"}`,
|
|
189
206
|
value: p.planKey || p.key || p.name
|
|
190
|
-
}))
|
|
207
|
+
})),
|
|
208
|
+
{
|
|
209
|
+
field: "plan",
|
|
210
|
+
flag: "--plan",
|
|
211
|
+
context: {
|
|
212
|
+
available: plans.map((p) => ({
|
|
213
|
+
key: p.planKey || p.key || p.name,
|
|
214
|
+
priceHalalas: p.priceHalalas ?? 0
|
|
215
|
+
}))
|
|
216
|
+
}
|
|
217
|
+
}
|
|
191
218
|
);
|
|
192
219
|
}
|
|
220
|
+
if (!targetPlan) {
|
|
221
|
+
throw new Error("No plan selected");
|
|
222
|
+
}
|
|
193
223
|
const _previewSpinner = startSpinner("Calculating change...");
|
|
194
224
|
let preview;
|
|
195
225
|
try {
|
|
196
226
|
preview = await client.subscription.previewPlanChange.query({
|
|
197
227
|
planKey: targetPlan,
|
|
198
|
-
planQuantity: options.quantity
|
|
228
|
+
planQuantity: options.quantity,
|
|
229
|
+
billingPeriod,
|
|
230
|
+
addons
|
|
199
231
|
});
|
|
200
232
|
succeedSpinner();
|
|
201
233
|
} catch {
|
|
@@ -206,6 +238,12 @@ function registerBillingCommands(program) {
|
|
|
206
238
|
log("");
|
|
207
239
|
log(`Plan: ${colors.cyan(targetPlan)}`);
|
|
208
240
|
if (options.quantity) log(`Quantity: ${options.quantity}`);
|
|
241
|
+
if (billingPeriod) log(`Billing period: ${billingPeriod}`);
|
|
242
|
+
if (addons && addons.length > 0) {
|
|
243
|
+
log(
|
|
244
|
+
`Addons: ${addons.map((a) => `${a.addonKey}\xD7${a.quantity}`).join(", ")}`
|
|
245
|
+
);
|
|
246
|
+
}
|
|
209
247
|
if (preview?.amountDue !== void 0) {
|
|
210
248
|
log(
|
|
211
249
|
`Amount due now: ${colors.bold(`${(preview.amountDue / 100).toFixed(2)} SAR`)}`
|
|
@@ -214,7 +252,18 @@ function registerBillingCommands(program) {
|
|
|
214
252
|
log("");
|
|
215
253
|
const confirmed = await confirm(
|
|
216
254
|
`Switch to plan "${targetPlan}"?`,
|
|
217
|
-
false
|
|
255
|
+
false,
|
|
256
|
+
{
|
|
257
|
+
field: "confirm_upgrade",
|
|
258
|
+
flag: "--yes",
|
|
259
|
+
context: {
|
|
260
|
+
plan: targetPlan,
|
|
261
|
+
quantity: options.quantity,
|
|
262
|
+
billingPeriod,
|
|
263
|
+
addons,
|
|
264
|
+
amountDueHalalas: preview?.amountDue
|
|
265
|
+
}
|
|
266
|
+
}
|
|
218
267
|
);
|
|
219
268
|
if (!confirmed) {
|
|
220
269
|
log("Cancelled.");
|
|
@@ -224,7 +273,9 @@ function registerBillingCommands(program) {
|
|
|
224
273
|
const _changeSpinner = startSpinner("Changing plan...");
|
|
225
274
|
const result = await client.subscription.changePlan.mutate({
|
|
226
275
|
planKey: targetPlan,
|
|
227
|
-
planQuantity: options.quantity
|
|
276
|
+
planQuantity: options.quantity,
|
|
277
|
+
billingPeriod,
|
|
278
|
+
addons
|
|
228
279
|
});
|
|
229
280
|
succeedSpinner("Plan changed!");
|
|
230
281
|
if (result?.applied) {
|
|
@@ -369,6 +420,14 @@ function registerBillingCommands(program) {
|
|
|
369
420
|
try {
|
|
370
421
|
if (!isLoggedIn()) throw new AuthError();
|
|
371
422
|
const client = getApiClient();
|
|
423
|
+
if (isJsonMode()) {
|
|
424
|
+
outputJsonLine({
|
|
425
|
+
type: "event",
|
|
426
|
+
event: "checkout_polling_started",
|
|
427
|
+
orderId,
|
|
428
|
+
timeoutSeconds: options.timeout
|
|
429
|
+
});
|
|
430
|
+
}
|
|
372
431
|
const final = await pollCheckoutUntilTerminal(client, orderId, {
|
|
373
432
|
timeoutMs: options.timeout * 1e3,
|
|
374
433
|
intervalMs: 4e3
|
|
@@ -414,7 +473,8 @@ function registerBillingCommands(program) {
|
|
|
414
473
|
log("");
|
|
415
474
|
const confirmed = await confirm(
|
|
416
475
|
"Are you sure you want to cancel your subscription?",
|
|
417
|
-
false
|
|
476
|
+
false,
|
|
477
|
+
{ field: "confirm_cancel", flag: "--yes" }
|
|
418
478
|
);
|
|
419
479
|
if (!confirmed) {
|
|
420
480
|
log("Cancelled.");
|
|
@@ -472,7 +532,12 @@ function registerBillingCommands(program) {
|
|
|
472
532
|
log("");
|
|
473
533
|
const confirmed = await confirm(
|
|
474
534
|
`Add addon "${addonKey}" \xD7 ${quantity}?`,
|
|
475
|
-
false
|
|
535
|
+
false,
|
|
536
|
+
{
|
|
537
|
+
field: "confirm_addon_add",
|
|
538
|
+
flag: "--yes",
|
|
539
|
+
context: { addonKey, quantity }
|
|
540
|
+
}
|
|
476
541
|
);
|
|
477
542
|
if (!confirmed) {
|
|
478
543
|
log("Cancelled.");
|
|
@@ -503,7 +568,15 @@ function registerBillingCommands(program) {
|
|
|
503
568
|
try {
|
|
504
569
|
if (!isLoggedIn()) throw new AuthError();
|
|
505
570
|
if (!shouldSkipConfirmation()) {
|
|
506
|
-
const confirmed = await confirm(
|
|
571
|
+
const confirmed = await confirm(
|
|
572
|
+
`Remove addon "${addonKey}"?`,
|
|
573
|
+
false,
|
|
574
|
+
{
|
|
575
|
+
field: "confirm_addon_remove",
|
|
576
|
+
flag: "--yes",
|
|
577
|
+
context: { addonKey }
|
|
578
|
+
}
|
|
579
|
+
);
|
|
507
580
|
if (!confirmed) {
|
|
508
581
|
log("Cancelled.");
|
|
509
582
|
return;
|
|
@@ -584,7 +657,11 @@ function registerBillingCommands(program) {
|
|
|
584
657
|
if (!shouldSkipConfirmation()) {
|
|
585
658
|
log(`
|
|
586
659
|
Purchase ${quantity}\xD7 ${colors.cyan(addonKey)}?`);
|
|
587
|
-
const confirmed = await confirm("Proceed?", false
|
|
660
|
+
const confirmed = await confirm("Proceed?", false, {
|
|
661
|
+
field: "confirm_addon_buy",
|
|
662
|
+
flag: "--yes",
|
|
663
|
+
context: { addonKey, quantity }
|
|
664
|
+
});
|
|
588
665
|
if (!confirmed) {
|
|
589
666
|
log("Cancelled.");
|
|
590
667
|
return;
|
|
@@ -614,7 +691,8 @@ Payment required: ${colors.cyan(result.paymentUrl)}
|
|
|
614
691
|
if (!shouldSkipConfirmation()) {
|
|
615
692
|
const confirmed = await confirm(
|
|
616
693
|
"Cancel the pending plan change?",
|
|
617
|
-
false
|
|
694
|
+
false,
|
|
695
|
+
{ field: "confirm_pending_cancel", flag: "--yes" }
|
|
618
696
|
);
|
|
619
697
|
if (!confirmed) {
|
|
620
698
|
log("Cancelled.");
|
|
@@ -867,6 +945,29 @@ async function pollCheckoutUntilTerminal(client, orderId, opts) {
|
|
|
867
945
|
failureReason: r.failureReason
|
|
868
946
|
};
|
|
869
947
|
}
|
|
948
|
+
function collectAddon(value, previous) {
|
|
949
|
+
const [rawKey, rawQty] = value.split(":");
|
|
950
|
+
const addonKey = (rawKey || "").trim();
|
|
951
|
+
if (!addonKey) {
|
|
952
|
+
throw new InvalidArgumentError(
|
|
953
|
+
`Invalid --addon value "${value}". Expected key[:qty] (e.g. db.standard:2).`
|
|
954
|
+
);
|
|
955
|
+
}
|
|
956
|
+
const quantity = rawQty === void 0 ? 1 : Number.parseInt(rawQty, 10);
|
|
957
|
+
if (!Number.isFinite(quantity) || quantity <= 0) {
|
|
958
|
+
throw new InvalidArgumentError(
|
|
959
|
+
`Invalid --addon quantity in "${value}". Expected a positive integer.`
|
|
960
|
+
);
|
|
961
|
+
}
|
|
962
|
+
return [...previous, { addonKey, quantity }];
|
|
963
|
+
}
|
|
964
|
+
function parseBillingPeriod(raw) {
|
|
965
|
+
const v = raw.toLowerCase();
|
|
966
|
+
if (v === "monthly" || v === "yearly") return v;
|
|
967
|
+
throw new InvalidArgumentError(
|
|
968
|
+
`Invalid --billing-period "${raw}". Expected "monthly" or "yearly".`
|
|
969
|
+
);
|
|
970
|
+
}
|
|
870
971
|
function formatSubStatus(status) {
|
|
871
972
|
const map = {
|
|
872
973
|
active: colors.success("active"),
|
|
@@ -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
|
|
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,
|