@insforge/cli 0.1.48 → 0.1.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +260 -87
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -5,7 +5,166 @@ import { readFileSync as readFileSync7 } from "fs";
|
|
|
5
5
|
import { join as join9, dirname } from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
import { Command } from "commander";
|
|
8
|
-
import * as
|
|
8
|
+
import * as clack11 from "@clack/prompts";
|
|
9
|
+
|
|
10
|
+
// src/lib/prompts.ts
|
|
11
|
+
import * as readline from "readline";
|
|
12
|
+
import * as clack from "@clack/prompts";
|
|
13
|
+
var isInteractive = !!(process.stdin.isTTY && process.stdout.isTTY);
|
|
14
|
+
var LineReader = class {
|
|
15
|
+
constructor(input, output) {
|
|
16
|
+
this.output = output;
|
|
17
|
+
this.rl = readline.createInterface({ input });
|
|
18
|
+
this.rl.on("line", (line) => {
|
|
19
|
+
if (this.waiter) {
|
|
20
|
+
const w = this.waiter;
|
|
21
|
+
this.waiter = null;
|
|
22
|
+
w(line);
|
|
23
|
+
} else {
|
|
24
|
+
this.queue.push(line);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
this.rl.on("close", () => {
|
|
28
|
+
this.closed = true;
|
|
29
|
+
if (this.waiter) {
|
|
30
|
+
const w = this.waiter;
|
|
31
|
+
this.waiter = null;
|
|
32
|
+
w(null);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
queue = [];
|
|
37
|
+
waiter = null;
|
|
38
|
+
closed = false;
|
|
39
|
+
rl;
|
|
40
|
+
async readLine(prompt) {
|
|
41
|
+
this.output.write(prompt);
|
|
42
|
+
if (this.queue.length > 0) return this.queue.shift();
|
|
43
|
+
if (this.closed) return null;
|
|
44
|
+
return new Promise((resolve4) => {
|
|
45
|
+
this.waiter = resolve4;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
close() {
|
|
49
|
+
this.rl.close();
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var sharedReader = null;
|
|
53
|
+
function getReader() {
|
|
54
|
+
if (!sharedReader) {
|
|
55
|
+
sharedReader = new LineReader(process.stdin, process.stdout);
|
|
56
|
+
}
|
|
57
|
+
return sharedReader;
|
|
58
|
+
}
|
|
59
|
+
var CANCEL = /* @__PURE__ */ Symbol("prompt.cancel");
|
|
60
|
+
function isCancel2(v) {
|
|
61
|
+
return v === CANCEL || clack.isCancel(v);
|
|
62
|
+
}
|
|
63
|
+
async function text2(opts) {
|
|
64
|
+
if (isInteractive) {
|
|
65
|
+
const result = await clack.text({
|
|
66
|
+
message: opts.message,
|
|
67
|
+
initialValue: opts.initialValue,
|
|
68
|
+
placeholder: opts.placeholder,
|
|
69
|
+
validate: opts.validate
|
|
70
|
+
});
|
|
71
|
+
if (clack.isCancel(result)) return CANCEL;
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
return nonTtyText(opts);
|
|
75
|
+
}
|
|
76
|
+
async function select2(opts) {
|
|
77
|
+
if (isInteractive) {
|
|
78
|
+
const result = await clack.select({
|
|
79
|
+
message: opts.message,
|
|
80
|
+
options: opts.options,
|
|
81
|
+
initialValue: opts.initialValue
|
|
82
|
+
});
|
|
83
|
+
if (clack.isCancel(result)) return CANCEL;
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
return nonTtySelect(opts);
|
|
87
|
+
}
|
|
88
|
+
async function confirm2(opts) {
|
|
89
|
+
if (isInteractive) {
|
|
90
|
+
const result = await clack.confirm({
|
|
91
|
+
message: opts.message,
|
|
92
|
+
initialValue: opts.initialValue
|
|
93
|
+
});
|
|
94
|
+
if (clack.isCancel(result)) return CANCEL;
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
return nonTtyConfirm(opts);
|
|
98
|
+
}
|
|
99
|
+
async function password2(opts) {
|
|
100
|
+
if (isInteractive) {
|
|
101
|
+
const result = await clack.password({ message: opts.message });
|
|
102
|
+
if (clack.isCancel(result)) return CANCEL;
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
return nonTtyText({ message: opts.message, trim: false });
|
|
106
|
+
}
|
|
107
|
+
async function nonTtyText(opts, io = {}) {
|
|
108
|
+
const reader = io.reader ?? getReader();
|
|
109
|
+
const stderr = io.stderr ?? process.stderr;
|
|
110
|
+
const shouldTrim = opts.trim ?? true;
|
|
111
|
+
const defaultHint = opts.initialValue ? ` [${opts.initialValue}]` : "";
|
|
112
|
+
for (; ; ) {
|
|
113
|
+
const raw = await reader.readLine(`? ${opts.message}${defaultHint} `);
|
|
114
|
+
if (raw === null) return CANCEL;
|
|
115
|
+
const normalized = shouldTrim ? raw.trim() : raw;
|
|
116
|
+
const value = normalized === "" ? opts.initialValue ?? "" : normalized;
|
|
117
|
+
if (opts.validate) {
|
|
118
|
+
const err = opts.validate(value);
|
|
119
|
+
if (err) {
|
|
120
|
+
stderr.write(` ${err}
|
|
121
|
+
`);
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return value;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async function nonTtySelect(opts, io = {}) {
|
|
129
|
+
if (opts.options.length === 0) {
|
|
130
|
+
throw new Error(`No options available for prompt "${opts.message}".`);
|
|
131
|
+
}
|
|
132
|
+
const reader = io.reader ?? getReader();
|
|
133
|
+
const stdout = io.stdout ?? process.stdout;
|
|
134
|
+
const stderr = io.stderr ?? process.stderr;
|
|
135
|
+
stdout.write(`? ${opts.message}
|
|
136
|
+
`);
|
|
137
|
+
opts.options.forEach((o, i) => {
|
|
138
|
+
const hint = o.hint ? ` \u2014 ${o.hint}` : "";
|
|
139
|
+
stdout.write(` ${i + 1}) ${o.label}${hint}
|
|
140
|
+
`);
|
|
141
|
+
});
|
|
142
|
+
for (; ; ) {
|
|
143
|
+
const raw = await reader.readLine(`Enter number [1-${opts.options.length}]: `);
|
|
144
|
+
if (raw === null) return CANCEL;
|
|
145
|
+
const n = Number.parseInt(raw.trim(), 10);
|
|
146
|
+
if (Number.isInteger(n) && n >= 1 && n <= opts.options.length) {
|
|
147
|
+
return opts.options[n - 1].value;
|
|
148
|
+
}
|
|
149
|
+
stderr.write(` Please enter a number between 1 and ${opts.options.length}.
|
|
150
|
+
`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async function nonTtyConfirm(opts, io = {}) {
|
|
154
|
+
const reader = io.reader ?? getReader();
|
|
155
|
+
const stderr = io.stderr ?? process.stderr;
|
|
156
|
+
const defaultHint = opts.initialValue === true ? " [Y/n]" : opts.initialValue === false ? " [y/N]" : " [y/n]";
|
|
157
|
+
for (; ; ) {
|
|
158
|
+
const raw = await reader.readLine(`? ${opts.message}${defaultHint} `);
|
|
159
|
+
if (raw === null) return CANCEL;
|
|
160
|
+
const answer = raw.trim().toLowerCase();
|
|
161
|
+
if (answer === "" && opts.initialValue !== void 0) return opts.initialValue;
|
|
162
|
+
if (answer === "y" || answer === "yes") return true;
|
|
163
|
+
if (answer === "n" || answer === "no") return false;
|
|
164
|
+
stderr.write(` Please answer y or n.
|
|
165
|
+
`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
9
168
|
|
|
10
169
|
// src/lib/config.ts
|
|
11
170
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
@@ -87,7 +246,7 @@ function getAccessToken() {
|
|
|
87
246
|
}
|
|
88
247
|
|
|
89
248
|
// src/commands/login.ts
|
|
90
|
-
import * as
|
|
249
|
+
import * as clack4 from "@clack/prompts";
|
|
91
250
|
|
|
92
251
|
// src/lib/errors.ts
|
|
93
252
|
var CLIError = class extends Error {
|
|
@@ -179,7 +338,8 @@ function getRootOpts(cmd) {
|
|
|
179
338
|
import { createServer } from "http";
|
|
180
339
|
import { randomBytes, createHash } from "crypto";
|
|
181
340
|
import { URL as URL2 } from "url";
|
|
182
|
-
import * as
|
|
341
|
+
import * as clack2 from "@clack/prompts";
|
|
342
|
+
import pc from "picocolors";
|
|
183
343
|
var DEFAULT_CLIENT_ID = "clf_NK8cMUs41gm8ZcfdtSguVw";
|
|
184
344
|
var OAUTH_SCOPES = "user:read organizations:read projects:read projects:write";
|
|
185
345
|
function generatePKCE() {
|
|
@@ -315,25 +475,35 @@ async function performOAuthLogin(apiUrl) {
|
|
|
315
475
|
state,
|
|
316
476
|
scopes: OAUTH_SCOPES
|
|
317
477
|
});
|
|
318
|
-
|
|
319
|
-
|
|
478
|
+
if (isInteractive) {
|
|
479
|
+
clack2.log.info("Opening browser for authentication...");
|
|
480
|
+
clack2.log.info(`If browser doesn't open, visit:
|
|
320
481
|
${authUrl}`);
|
|
482
|
+
} else {
|
|
483
|
+
process.stderr.write(`
|
|
484
|
+
To sign in, open this URL in your browser:
|
|
485
|
+
|
|
486
|
+
${pc.cyan(pc.underline(authUrl))}
|
|
487
|
+
|
|
488
|
+
`);
|
|
489
|
+
}
|
|
321
490
|
try {
|
|
322
491
|
const open = (await import("open")).default;
|
|
323
492
|
await open(authUrl);
|
|
324
493
|
} catch {
|
|
325
|
-
|
|
494
|
+
if (isInteractive) clack2.log.warn("Could not open browser. Please visit the URL above.");
|
|
326
495
|
}
|
|
327
|
-
const s =
|
|
328
|
-
s
|
|
496
|
+
const s = isInteractive ? clack2.spinner() : null;
|
|
497
|
+
s?.start("Waiting for authentication...");
|
|
498
|
+
if (!isInteractive) process.stderr.write("Waiting for authentication...\n");
|
|
329
499
|
try {
|
|
330
500
|
const callbackResult = await result;
|
|
331
501
|
close();
|
|
332
502
|
if (callbackResult.state !== state) {
|
|
333
|
-
s
|
|
503
|
+
s?.stop("Authentication failed");
|
|
334
504
|
throw new Error("State mismatch. Possible CSRF attack.");
|
|
335
505
|
}
|
|
336
|
-
s
|
|
506
|
+
s?.message("Exchanging authorization code...");
|
|
337
507
|
const tokens = await exchangeCodeForTokens({
|
|
338
508
|
platformUrl,
|
|
339
509
|
code: callbackResult.code,
|
|
@@ -351,20 +521,24 @@ ${authUrl}`);
|
|
|
351
521
|
const profile = await getProfile(apiUrl);
|
|
352
522
|
creds.user = profile;
|
|
353
523
|
saveCredentials(creds);
|
|
354
|
-
s
|
|
524
|
+
s?.stop(`Authenticated as ${profile.email}`);
|
|
525
|
+
if (!isInteractive) process.stderr.write(`Authenticated as ${profile.email}
|
|
526
|
+
`);
|
|
355
527
|
} catch {
|
|
356
|
-
s
|
|
528
|
+
s?.stop("Authenticated successfully");
|
|
529
|
+
if (!isInteractive) process.stderr.write("Authenticated successfully\n");
|
|
357
530
|
}
|
|
358
531
|
return creds;
|
|
359
532
|
} catch (err) {
|
|
360
533
|
close();
|
|
361
|
-
s
|
|
534
|
+
s?.stop("Authentication failed");
|
|
535
|
+
if (!isInteractive) process.stderr.write("Authentication failed\n");
|
|
362
536
|
throw err;
|
|
363
537
|
}
|
|
364
538
|
}
|
|
365
539
|
|
|
366
540
|
// src/lib/credentials.ts
|
|
367
|
-
import * as
|
|
541
|
+
import * as clack3 from "@clack/prompts";
|
|
368
542
|
async function requireAuth(apiUrl, allowOssBypass = true) {
|
|
369
543
|
const projConfig = getProjectConfig();
|
|
370
544
|
if (allowOssBypass && projConfig?.project_id === FAKE_PROJECT_ID) {
|
|
@@ -382,16 +556,16 @@ async function requireAuth(apiUrl, allowOssBypass = true) {
|
|
|
382
556
|
}
|
|
383
557
|
const creds = getCredentials();
|
|
384
558
|
if (creds && creds.access_token) return creds;
|
|
385
|
-
|
|
559
|
+
clack3.log.info("You need to log in to continue.");
|
|
386
560
|
for (; ; ) {
|
|
387
561
|
try {
|
|
388
562
|
return await performOAuthLogin(apiUrl);
|
|
389
563
|
} catch (err) {
|
|
390
564
|
if (!process.stdout.isTTY) throw err;
|
|
391
565
|
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
392
|
-
|
|
393
|
-
const retry = await
|
|
394
|
-
if (
|
|
566
|
+
clack3.log.error(`Login failed: ${msg}`);
|
|
567
|
+
const retry = await confirm2({ message: "Would you like to try again?" });
|
|
568
|
+
if (isCancel2(retry) || !retry) {
|
|
395
569
|
throw new AuthError("Authentication required. Run `npx @insforge/cli login` to authenticate.");
|
|
396
570
|
}
|
|
397
571
|
}
|
|
@@ -421,7 +595,7 @@ async function refreshAccessToken(apiUrl) {
|
|
|
421
595
|
return data.access_token;
|
|
422
596
|
} catch {
|
|
423
597
|
if (process.stdout.isTTY) {
|
|
424
|
-
|
|
598
|
+
clack3.log.warn("Session expired. Please log in again.");
|
|
425
599
|
const newCreds = await performOAuthLogin(apiUrl);
|
|
426
600
|
return newCreds.access_token;
|
|
427
601
|
}
|
|
@@ -477,12 +651,12 @@ async function platformFetch(path5, options = {}, apiUrl) {
|
|
|
477
651
|
}
|
|
478
652
|
return res;
|
|
479
653
|
}
|
|
480
|
-
async function login(email,
|
|
654
|
+
async function login(email, password3, apiUrl) {
|
|
481
655
|
const baseUrl = getPlatformApiUrl(apiUrl);
|
|
482
656
|
const res = await fetch(`${baseUrl}/auth/v1/login`, {
|
|
483
657
|
method: "POST",
|
|
484
658
|
headers: { "Content-Type": "application/json" },
|
|
485
|
-
body: JSON.stringify({ email, password:
|
|
659
|
+
body: JSON.stringify({ email, password: password3 })
|
|
486
660
|
});
|
|
487
661
|
if (!res.ok) {
|
|
488
662
|
const err = await res.json().catch(() => ({}));
|
|
@@ -618,30 +792,30 @@ function registerLoginCommand(program2) {
|
|
|
618
792
|
}
|
|
619
793
|
async function loginWithEmail(json, apiUrl) {
|
|
620
794
|
if (!json) {
|
|
621
|
-
|
|
795
|
+
clack4.intro("InsForge CLI");
|
|
622
796
|
}
|
|
623
|
-
const email = json ? process.env.INSFORGE_EMAIL : await
|
|
797
|
+
const email = json ? process.env.INSFORGE_EMAIL : await text2({
|
|
624
798
|
message: "Email:",
|
|
625
799
|
validate: (v) => v.includes("@") ? void 0 : "Please enter a valid email"
|
|
626
800
|
});
|
|
627
|
-
if (
|
|
628
|
-
|
|
801
|
+
if (isCancel2(email)) {
|
|
802
|
+
clack4.cancel("Login cancelled.");
|
|
629
803
|
throw new Error("cancelled");
|
|
630
804
|
}
|
|
631
|
-
const
|
|
805
|
+
const password3 = json ? process.env.INSFORGE_PASSWORD : await password2({
|
|
632
806
|
message: "Password:"
|
|
633
807
|
});
|
|
634
|
-
if (
|
|
635
|
-
|
|
808
|
+
if (isCancel2(password3)) {
|
|
809
|
+
clack4.cancel("Login cancelled.");
|
|
636
810
|
throw new Error("cancelled");
|
|
637
811
|
}
|
|
638
|
-
if (!email || !
|
|
812
|
+
if (!email || !password3) {
|
|
639
813
|
throw new Error("Email and password are required. Set INSFORGE_EMAIL and INSFORGE_PASSWORD environment variables for non-interactive mode.");
|
|
640
814
|
}
|
|
641
815
|
if (!json) {
|
|
642
|
-
const s =
|
|
816
|
+
const s = clack4.spinner();
|
|
643
817
|
s.start("Authenticating...");
|
|
644
|
-
const result = await login(email,
|
|
818
|
+
const result = await login(email, password3, apiUrl);
|
|
645
819
|
const creds = {
|
|
646
820
|
access_token: result.token,
|
|
647
821
|
refresh_token: result._refreshToken ?? "",
|
|
@@ -649,9 +823,9 @@ async function loginWithEmail(json, apiUrl) {
|
|
|
649
823
|
};
|
|
650
824
|
saveCredentials(creds);
|
|
651
825
|
s.stop(`Authenticated as ${result.user.email}`);
|
|
652
|
-
|
|
826
|
+
clack4.outro("Done");
|
|
653
827
|
} else {
|
|
654
|
-
const result = await login(email,
|
|
828
|
+
const result = await login(email, password3, apiUrl);
|
|
655
829
|
const creds = {
|
|
656
830
|
access_token: result.token,
|
|
657
831
|
refresh_token: result._refreshToken ?? "",
|
|
@@ -663,11 +837,11 @@ async function loginWithEmail(json, apiUrl) {
|
|
|
663
837
|
}
|
|
664
838
|
async function loginWithOAuth(json, apiUrl) {
|
|
665
839
|
if (!json) {
|
|
666
|
-
|
|
840
|
+
clack4.intro("InsForge CLI");
|
|
667
841
|
}
|
|
668
842
|
const creds = await performOAuthLogin(apiUrl);
|
|
669
843
|
if (!json) {
|
|
670
|
-
|
|
844
|
+
clack4.outro("Done");
|
|
671
845
|
} else {
|
|
672
846
|
console.log(JSON.stringify({ success: true, user: creds.user }));
|
|
673
847
|
}
|
|
@@ -762,7 +936,6 @@ function registerOrgsCommands(orgsCmd2) {
|
|
|
762
936
|
}
|
|
763
937
|
|
|
764
938
|
// src/commands/projects/list.ts
|
|
765
|
-
import * as clack4 from "@clack/prompts";
|
|
766
939
|
function registerProjectsCommands(projectsCmd2) {
|
|
767
940
|
projectsCmd2.command("list").description("List all projects in an organization").option("--org-id <id>", "Organization ID (uses default if not specified)").action(async (opts, cmd) => {
|
|
768
941
|
const { json, apiUrl } = getRootOpts(cmd);
|
|
@@ -777,14 +950,14 @@ function registerProjectsCommands(projectsCmd2) {
|
|
|
777
950
|
if (orgs.length === 1) {
|
|
778
951
|
orgId = orgs[0].id;
|
|
779
952
|
} else if (!json) {
|
|
780
|
-
const selected = await
|
|
953
|
+
const selected = await select2({
|
|
781
954
|
message: "Select an organization:",
|
|
782
955
|
options: orgs.map((o) => ({
|
|
783
956
|
value: o.id,
|
|
784
957
|
label: o.name
|
|
785
958
|
}))
|
|
786
959
|
});
|
|
787
|
-
if (
|
|
960
|
+
if (isCancel2(selected)) {
|
|
788
961
|
process.exit(0);
|
|
789
962
|
}
|
|
790
963
|
orgId = selected;
|
|
@@ -817,6 +990,7 @@ import { promisify as promisify3 } from "util";
|
|
|
817
990
|
import * as fs4 from "fs/promises";
|
|
818
991
|
import * as path4 from "path";
|
|
819
992
|
import * as clack8 from "@clack/prompts";
|
|
993
|
+
import pc2 from "picocolors";
|
|
820
994
|
|
|
821
995
|
// src/lib/skills.ts
|
|
822
996
|
import { exec } from "child_process";
|
|
@@ -1382,14 +1556,14 @@ function registerCreateCommand(program2) {
|
|
|
1382
1556
|
if (json) {
|
|
1383
1557
|
throw new CLIError("Multiple organizations found. Specify --org-id.");
|
|
1384
1558
|
}
|
|
1385
|
-
const selected = await
|
|
1559
|
+
const selected = await select2({
|
|
1386
1560
|
message: "Select an organization:",
|
|
1387
1561
|
options: orgs.map((o) => ({
|
|
1388
1562
|
value: o.id,
|
|
1389
1563
|
label: o.name
|
|
1390
1564
|
}))
|
|
1391
1565
|
});
|
|
1392
|
-
if (
|
|
1566
|
+
if (isCancel2(selected)) process.exit(0);
|
|
1393
1567
|
orgId = selected;
|
|
1394
1568
|
}
|
|
1395
1569
|
}
|
|
@@ -1400,12 +1574,12 @@ function registerCreateCommand(program2) {
|
|
|
1400
1574
|
if (!projectName) {
|
|
1401
1575
|
if (json) throw new CLIError("--name is required in JSON mode.");
|
|
1402
1576
|
const defaultName = getDefaultProjectName();
|
|
1403
|
-
const name = await
|
|
1577
|
+
const name = await text2({
|
|
1404
1578
|
message: "Project name:",
|
|
1405
1579
|
...defaultName ? { initialValue: defaultName } : {},
|
|
1406
1580
|
validate: (v) => v.length >= 2 ? void 0 : "Name must be at least 2 characters"
|
|
1407
1581
|
});
|
|
1408
|
-
if (
|
|
1582
|
+
if (isCancel2(name)) process.exit(0);
|
|
1409
1583
|
projectName = name;
|
|
1410
1584
|
}
|
|
1411
1585
|
projectName = path3.basename(projectName).replace(/[^a-zA-Z0-9._-]/g, "-").replace(/\.+/g, ".");
|
|
@@ -1421,21 +1595,21 @@ function registerCreateCommand(program2) {
|
|
|
1421
1595
|
if (json) {
|
|
1422
1596
|
template = "empty";
|
|
1423
1597
|
} else {
|
|
1424
|
-
const approach = await
|
|
1598
|
+
const approach = await select2({
|
|
1425
1599
|
message: "How would you like to start?",
|
|
1426
1600
|
options: [
|
|
1427
1601
|
{ value: "blank", label: "Blank project", hint: "Start from scratch with .env.local ready" },
|
|
1428
1602
|
{ value: "template", label: "Start from a template", hint: "Pre-built starter apps" }
|
|
1429
1603
|
]
|
|
1430
1604
|
});
|
|
1431
|
-
if (
|
|
1605
|
+
if (isCancel2(approach)) process.exit(0);
|
|
1432
1606
|
captureEvent(orgId, "create_approach_selected", {
|
|
1433
1607
|
approach
|
|
1434
1608
|
});
|
|
1435
1609
|
if (approach === "blank") {
|
|
1436
1610
|
template = "empty";
|
|
1437
1611
|
} else {
|
|
1438
|
-
const selected = await
|
|
1612
|
+
const selected = await select2({
|
|
1439
1613
|
message: "Choose a starter template:",
|
|
1440
1614
|
options: [
|
|
1441
1615
|
{ value: "react", label: "Web app template with React" },
|
|
@@ -1446,7 +1620,7 @@ function registerCreateCommand(program2) {
|
|
|
1446
1620
|
{ value: "todo", label: "Todo app with Next.js" }
|
|
1447
1621
|
]
|
|
1448
1622
|
});
|
|
1449
|
-
if (
|
|
1623
|
+
if (isCancel2(selected)) process.exit(0);
|
|
1450
1624
|
template = selected;
|
|
1451
1625
|
}
|
|
1452
1626
|
}
|
|
@@ -1462,7 +1636,7 @@ function registerCreateCommand(program2) {
|
|
|
1462
1636
|
if (hasTemplate) {
|
|
1463
1637
|
dirName = projectName;
|
|
1464
1638
|
if (!json) {
|
|
1465
|
-
const inputDir = await
|
|
1639
|
+
const inputDir = await text2({
|
|
1466
1640
|
message: "Directory name:",
|
|
1467
1641
|
initialValue: projectName,
|
|
1468
1642
|
validate: (v) => {
|
|
@@ -1472,7 +1646,7 @@ function registerCreateCommand(program2) {
|
|
|
1472
1646
|
return void 0;
|
|
1473
1647
|
}
|
|
1474
1648
|
});
|
|
1475
|
-
if (
|
|
1649
|
+
if (isCancel2(inputDir)) process.exit(0);
|
|
1476
1650
|
dirName = path3.basename(inputDir).replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
1477
1651
|
}
|
|
1478
1652
|
if (!dirName || dirName === "." || dirName === "..") {
|
|
@@ -1560,10 +1734,10 @@ function registerCreateCommand(program2) {
|
|
|
1560
1734
|
}
|
|
1561
1735
|
let liveUrl = null;
|
|
1562
1736
|
if (templateDownloaded && !json) {
|
|
1563
|
-
const shouldDeploy = await
|
|
1737
|
+
const shouldDeploy = await confirm2({
|
|
1564
1738
|
message: "Would you like to deploy now?"
|
|
1565
1739
|
});
|
|
1566
|
-
if (!
|
|
1740
|
+
if (!isCancel2(shouldDeploy) && shouldDeploy) {
|
|
1567
1741
|
try {
|
|
1568
1742
|
const envVars = await readEnvFile(process.cwd());
|
|
1569
1743
|
const startBody = {};
|
|
@@ -1825,14 +1999,14 @@ function registerProjectLinkCommand(program2) {
|
|
|
1825
1999
|
if (json) {
|
|
1826
2000
|
throw new CLIError("Multiple organizations found. Specify --org-id.");
|
|
1827
2001
|
}
|
|
1828
|
-
const selected = await
|
|
2002
|
+
const selected = await select2({
|
|
1829
2003
|
message: "Select an organization:",
|
|
1830
2004
|
options: orgs.map((o) => ({
|
|
1831
2005
|
value: o.id,
|
|
1832
2006
|
label: o.name
|
|
1833
2007
|
}))
|
|
1834
2008
|
});
|
|
1835
|
-
if (
|
|
2009
|
+
if (isCancel2(selected)) process.exit(0);
|
|
1836
2010
|
orgId = selected;
|
|
1837
2011
|
}
|
|
1838
2012
|
}
|
|
@@ -1847,14 +2021,14 @@ function registerProjectLinkCommand(program2) {
|
|
|
1847
2021
|
if (json) {
|
|
1848
2022
|
throw new CLIError("Specify --project-id in JSON mode.");
|
|
1849
2023
|
}
|
|
1850
|
-
const selected = await
|
|
2024
|
+
const selected = await select2({
|
|
1851
2025
|
message: "Select a project to link:",
|
|
1852
2026
|
options: projects.map((p) => ({
|
|
1853
2027
|
value: p.id,
|
|
1854
2028
|
label: `${p.name} (${p.region}, ${p.status})`
|
|
1855
2029
|
}))
|
|
1856
2030
|
});
|
|
1857
|
-
if (
|
|
2031
|
+
if (isCancel2(selected)) process.exit(0);
|
|
1858
2032
|
projectId = selected;
|
|
1859
2033
|
}
|
|
1860
2034
|
let project;
|
|
@@ -1905,7 +2079,7 @@ function registerProjectLinkCommand(program2) {
|
|
|
1905
2079
|
}
|
|
1906
2080
|
let dirName = project.name;
|
|
1907
2081
|
if (!json) {
|
|
1908
|
-
const inputDir = await
|
|
2082
|
+
const inputDir = await text2({
|
|
1909
2083
|
message: "Directory name:",
|
|
1910
2084
|
initialValue: project.name,
|
|
1911
2085
|
validate: (v) => {
|
|
@@ -1915,7 +2089,7 @@ function registerProjectLinkCommand(program2) {
|
|
|
1915
2089
|
return void 0;
|
|
1916
2090
|
}
|
|
1917
2091
|
});
|
|
1918
|
-
if (
|
|
2092
|
+
if (isCancel2(inputDir)) process.exit(0);
|
|
1919
2093
|
dirName = path4.basename(inputDir).replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
1920
2094
|
}
|
|
1921
2095
|
if (!dirName || dirName === "." || dirName === "..") {
|
|
@@ -1953,11 +2127,14 @@ function registerProjectLinkCommand(program2) {
|
|
|
1953
2127
|
await reportCliUsage("cli.link", true, 6, projectConfig);
|
|
1954
2128
|
if (!json) {
|
|
1955
2129
|
const dashboardUrl = `${getFrontendUrl()}/dashboard/project/${project.id}`;
|
|
1956
|
-
clack8.log.step(`Dashboard: ${dashboardUrl}`);
|
|
2130
|
+
clack8.log.step(`Dashboard: ${pc2.underline(dashboardUrl)}`);
|
|
1957
2131
|
if (templateDownloaded) {
|
|
1958
|
-
const
|
|
1959
|
-
|
|
1960
|
-
|
|
2132
|
+
const runCommand = `${pc2.cyan("cd")} ${pc2.green(dirName)} ${pc2.dim("&&")} ${pc2.cyan("npm run dev")}`;
|
|
2133
|
+
const steps = [
|
|
2134
|
+
`${pc2.bold("1.")} ${runCommand}`,
|
|
2135
|
+
`${pc2.bold("2.")} Open ${pc2.cyan("Claude Code")} or ${pc2.cyan("Cursor")} and prompt your agent to add more features`
|
|
2136
|
+
];
|
|
2137
|
+
clack8.note(steps.join("\n"), "What's next");
|
|
1961
2138
|
} else {
|
|
1962
2139
|
clack8.log.warn("Template download failed. You can retry or set up manually.");
|
|
1963
2140
|
}
|
|
@@ -2586,8 +2763,8 @@ function registerFunctionsInvokeCommand(functionsCmd2) {
|
|
|
2586
2763
|
console.log(JSON.stringify(data, null, 2));
|
|
2587
2764
|
}
|
|
2588
2765
|
} else {
|
|
2589
|
-
const
|
|
2590
|
-
console.log(
|
|
2766
|
+
const text3 = await res.text();
|
|
2767
|
+
console.log(text3);
|
|
2591
2768
|
}
|
|
2592
2769
|
if (status >= 400) {
|
|
2593
2770
|
throw new CLIError(`HTTP ${status}`, 1, "HTTP_ERROR");
|
|
@@ -2630,10 +2807,10 @@ function registerFunctionsDeleteCommand(functionsCmd2) {
|
|
|
2630
2807
|
try {
|
|
2631
2808
|
await requireAuth();
|
|
2632
2809
|
if (!yes && !json) {
|
|
2633
|
-
const confirmed = await
|
|
2810
|
+
const confirmed = await confirm2({
|
|
2634
2811
|
message: `Delete function "${slug}"? This cannot be undone.`
|
|
2635
2812
|
});
|
|
2636
|
-
if (
|
|
2813
|
+
if (isCancel2(confirmed) || !confirmed) {
|
|
2637
2814
|
clack9.log.info("Cancelled.");
|
|
2638
2815
|
return;
|
|
2639
2816
|
}
|
|
@@ -2792,17 +2969,16 @@ function registerStorageCreateBucketCommand(storageCmd2) {
|
|
|
2792
2969
|
}
|
|
2793
2970
|
|
|
2794
2971
|
// src/commands/storage/delete-bucket.ts
|
|
2795
|
-
import * as clack10 from "@clack/prompts";
|
|
2796
2972
|
function registerStorageDeleteBucketCommand(storageCmd2) {
|
|
2797
2973
|
storageCmd2.command("delete-bucket <name>").description("Delete a storage bucket and all its objects").action(async (name, _opts, cmd) => {
|
|
2798
2974
|
const { json, yes } = getRootOpts(cmd);
|
|
2799
2975
|
try {
|
|
2800
2976
|
await requireAuth();
|
|
2801
2977
|
if (!yes && !json) {
|
|
2802
|
-
const
|
|
2978
|
+
const confirm3 = await confirm2({
|
|
2803
2979
|
message: `Delete bucket "${name}" and all its objects? This cannot be undone.`
|
|
2804
2980
|
});
|
|
2805
|
-
if (
|
|
2981
|
+
if (isCancel2(confirm3) || !confirm3) {
|
|
2806
2982
|
process.exit(0);
|
|
2807
2983
|
}
|
|
2808
2984
|
}
|
|
@@ -3057,7 +3233,6 @@ function registerDeploymentsStatusCommand(deploymentsCmd2) {
|
|
|
3057
3233
|
}
|
|
3058
3234
|
|
|
3059
3235
|
// src/commands/deployments/cancel.ts
|
|
3060
|
-
import * as clack11 from "@clack/prompts";
|
|
3061
3236
|
function registerDeploymentsCancelCommand(deploymentsCmd2) {
|
|
3062
3237
|
deploymentsCmd2.command("cancel <id>").description("Cancel a deployment").action(async (id, _opts, cmd) => {
|
|
3063
3238
|
const { json, yes } = getRootOpts(cmd);
|
|
@@ -3065,10 +3240,10 @@ function registerDeploymentsCancelCommand(deploymentsCmd2) {
|
|
|
3065
3240
|
await requireAuth();
|
|
3066
3241
|
if (!getProjectConfig()) throw new ProjectNotLinkedError();
|
|
3067
3242
|
if (!yes && !json) {
|
|
3068
|
-
const confirmed = await
|
|
3243
|
+
const confirmed = await confirm2({
|
|
3069
3244
|
message: `Cancel deployment ${id}?`
|
|
3070
3245
|
});
|
|
3071
|
-
if (
|
|
3246
|
+
if (isCancel2(confirmed) || !confirmed) process.exit(0);
|
|
3072
3247
|
}
|
|
3073
3248
|
const res = await ossFetch(`/api/deployments/${id}/cancel`, { method: "POST" });
|
|
3074
3249
|
const result = await res.json();
|
|
@@ -3348,17 +3523,16 @@ function registerSecretsUpdateCommand(secretsCmd2) {
|
|
|
3348
3523
|
}
|
|
3349
3524
|
|
|
3350
3525
|
// src/commands/secrets/delete.ts
|
|
3351
|
-
import * as clack12 from "@clack/prompts";
|
|
3352
3526
|
function registerSecretsDeleteCommand(secretsCmd2) {
|
|
3353
3527
|
secretsCmd2.command("delete <key>").description("Delete a secret").action(async (key, _opts, cmd) => {
|
|
3354
3528
|
const { json, yes } = getRootOpts(cmd);
|
|
3355
3529
|
try {
|
|
3356
3530
|
await requireAuth();
|
|
3357
3531
|
if (!yes && !json) {
|
|
3358
|
-
const
|
|
3532
|
+
const confirm3 = await confirm2({
|
|
3359
3533
|
message: `Delete secret "${key}"? This cannot be undone.`
|
|
3360
3534
|
});
|
|
3361
|
-
if (
|
|
3535
|
+
if (isCancel2(confirm3) || !confirm3) {
|
|
3362
3536
|
process.exit(0);
|
|
3363
3537
|
}
|
|
3364
3538
|
}
|
|
@@ -3534,17 +3708,16 @@ function registerSchedulesUpdateCommand(schedulesCmd2) {
|
|
|
3534
3708
|
}
|
|
3535
3709
|
|
|
3536
3710
|
// src/commands/schedules/delete.ts
|
|
3537
|
-
import * as clack13 from "@clack/prompts";
|
|
3538
3711
|
function registerSchedulesDeleteCommand(schedulesCmd2) {
|
|
3539
3712
|
schedulesCmd2.command("delete <id>").description("Delete a schedule").action(async (id, _opts, cmd) => {
|
|
3540
3713
|
const { json, yes } = getRootOpts(cmd);
|
|
3541
3714
|
try {
|
|
3542
3715
|
await requireAuth();
|
|
3543
3716
|
if (!yes && !json) {
|
|
3544
|
-
const
|
|
3717
|
+
const confirm3 = await confirm2({
|
|
3545
3718
|
message: `Delete schedule "${id}"? This cannot be undone.`
|
|
3546
3719
|
});
|
|
3547
|
-
if (
|
|
3720
|
+
if (isCancel2(confirm3) || !confirm3) {
|
|
3548
3721
|
process.exit(0);
|
|
3549
3722
|
}
|
|
3550
3723
|
}
|
|
@@ -4175,7 +4348,7 @@ function formatSize2(gb) {
|
|
|
4175
4348
|
|
|
4176
4349
|
// src/commands/diagnose/index.ts
|
|
4177
4350
|
import * as os from "os";
|
|
4178
|
-
import * as
|
|
4351
|
+
import * as clack10 from "@clack/prompts";
|
|
4179
4352
|
|
|
4180
4353
|
// src/commands/diagnose/metrics.ts
|
|
4181
4354
|
var METRIC_LABELS = {
|
|
@@ -4712,10 +4885,10 @@ function registerDiagnoseCommands(diagnoseCmd2) {
|
|
|
4712
4885
|
if (question.length === 0 || question.length > 2e3) {
|
|
4713
4886
|
throw new CLIError("Question must be between 1 and 2000 characters.");
|
|
4714
4887
|
}
|
|
4715
|
-
const s = !json ?
|
|
4888
|
+
const s = !json ? clack10.spinner() : null;
|
|
4716
4889
|
s?.start("Collecting diagnostic data...");
|
|
4717
4890
|
const data2 = await collectDiagnosticData(projectId, ossMode, apiUrl);
|
|
4718
|
-
const cliVersion = "0.1.
|
|
4891
|
+
const cliVersion = "0.1.50";
|
|
4719
4892
|
s?.stop("Data collected");
|
|
4720
4893
|
if (!json) {
|
|
4721
4894
|
console.log(`
|
|
@@ -4831,7 +5004,7 @@ function registerDiagnoseCommands(diagnoseCmd2) {
|
|
|
4831
5004
|
outputJson({ sessionId, events: jsonEvents });
|
|
4832
5005
|
}
|
|
4833
5006
|
if (!json && sessionId) {
|
|
4834
|
-
const ratingChoice = await
|
|
5007
|
+
const ratingChoice = await select2({
|
|
4835
5008
|
message: "Was this analysis helpful?",
|
|
4836
5009
|
options: [
|
|
4837
5010
|
{ value: "skip", label: "Skip", hint: "no rating" },
|
|
@@ -4840,7 +5013,7 @@ function registerDiagnoseCommands(diagnoseCmd2) {
|
|
|
4840
5013
|
{ value: "incorrect", label: "Incorrect", hint: "diagnosis was wrong or misleading" }
|
|
4841
5014
|
]
|
|
4842
5015
|
});
|
|
4843
|
-
if (!
|
|
5016
|
+
if (!isCancel2(ratingChoice) && ratingChoice !== "skip") {
|
|
4844
5017
|
try {
|
|
4845
5018
|
await rateDiagnosticSession(
|
|
4846
5019
|
sessionId,
|
|
@@ -4848,9 +5021,9 @@ function registerDiagnoseCommands(diagnoseCmd2) {
|
|
|
4848
5021
|
void 0,
|
|
4849
5022
|
apiUrl
|
|
4850
5023
|
);
|
|
4851
|
-
|
|
5024
|
+
clack10.log.success("Thanks for your feedback!");
|
|
4852
5025
|
} catch {
|
|
4853
|
-
|
|
5026
|
+
clack10.log.warn("Failed to submit rating.");
|
|
4854
5027
|
}
|
|
4855
5028
|
}
|
|
4856
5029
|
}
|
|
@@ -5045,7 +5218,7 @@ async function showInteractiveMenu() {
|
|
|
5045
5218
|
} catch {
|
|
5046
5219
|
}
|
|
5047
5220
|
console.log(INSFORGE_LOGO);
|
|
5048
|
-
|
|
5221
|
+
clack11.intro(`InsForge CLI v${pkg.version}`);
|
|
5049
5222
|
const options = [];
|
|
5050
5223
|
if (!isLoggedIn) {
|
|
5051
5224
|
options.push({ value: "login", label: "Log in to InsForge" });
|
|
@@ -5061,12 +5234,12 @@ async function showInteractiveMenu() {
|
|
|
5061
5234
|
{ value: "docs", label: "View documentation" },
|
|
5062
5235
|
{ value: "help", label: "Show all commands" }
|
|
5063
5236
|
);
|
|
5064
|
-
const action = await
|
|
5237
|
+
const action = await select2({
|
|
5065
5238
|
message: "What would you like to do?",
|
|
5066
5239
|
options
|
|
5067
5240
|
});
|
|
5068
|
-
if (
|
|
5069
|
-
|
|
5241
|
+
if (isCancel2(action)) {
|
|
5242
|
+
clack11.cancel("Bye!");
|
|
5070
5243
|
process.exit(0);
|
|
5071
5244
|
}
|
|
5072
5245
|
switch (action) {
|