@insforge/cli 0.1.49 → 0.1.51-dev.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/dist/index.js CHANGED
@@ -2,10 +2,169 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { readFileSync as readFileSync7 } from "fs";
5
- import { join as join9, dirname } from "path";
5
+ import { join as join10, dirname } from "path";
6
6
  import { fileURLToPath } from "url";
7
7
  import { Command } from "commander";
8
- import * as clack15 from "@clack/prompts";
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 clack3 from "@clack/prompts";
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 clack from "@clack/prompts";
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
- clack.log.info("Opening browser for authentication...");
319
- clack.log.info(`If browser doesn't open, visit:
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
- clack.log.warn("Could not open browser. Please visit the URL above.");
494
+ if (isInteractive) clack2.log.warn("Could not open browser. Please visit the URL above.");
326
495
  }
327
- const s = clack.spinner();
328
- s.start("Waiting for authentication...");
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.stop("Authentication failed");
503
+ s?.stop("Authentication failed");
334
504
  throw new Error("State mismatch. Possible CSRF attack.");
335
505
  }
336
- s.message("Exchanging authorization code...");
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.stop(`Authenticated as ${profile.email}`);
524
+ s?.stop(`Authenticated as ${profile.email}`);
525
+ if (!isInteractive) process.stderr.write(`Authenticated as ${profile.email}
526
+ `);
355
527
  } catch {
356
- s.stop("Authenticated successfully");
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.stop("Authentication failed");
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 clack2 from "@clack/prompts";
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
- clack2.log.info("You need to log in to continue.");
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
- clack2.log.error(`Login failed: ${msg}`);
393
- const retry = await clack2.confirm({ message: "Would you like to try again?" });
394
- if (clack2.isCancel(retry) || !retry) {
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
- clack2.log.warn("Session expired. Please log in again.");
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, password2, apiUrl) {
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: password2 })
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
- clack3.intro("InsForge CLI");
795
+ clack4.intro("InsForge CLI");
622
796
  }
623
- const email = json ? process.env.INSFORGE_EMAIL : await clack3.text({
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 (clack3.isCancel(email)) {
628
- clack3.cancel("Login cancelled.");
801
+ if (isCancel2(email)) {
802
+ clack4.cancel("Login cancelled.");
629
803
  throw new Error("cancelled");
630
804
  }
631
- const password2 = json ? process.env.INSFORGE_PASSWORD : await clack3.password({
805
+ const password3 = json ? process.env.INSFORGE_PASSWORD : await password2({
632
806
  message: "Password:"
633
807
  });
634
- if (clack3.isCancel(password2)) {
635
- clack3.cancel("Login cancelled.");
808
+ if (isCancel2(password3)) {
809
+ clack4.cancel("Login cancelled.");
636
810
  throw new Error("cancelled");
637
811
  }
638
- if (!email || !password2) {
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 = clack3.spinner();
816
+ const s = clack4.spinner();
643
817
  s.start("Authenticating...");
644
- const result = await login(email, password2, apiUrl);
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
- clack3.outro("Done");
826
+ clack4.outro("Done");
653
827
  } else {
654
- const result = await login(email, password2, apiUrl);
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
- clack3.intro("InsForge CLI");
840
+ clack4.intro("InsForge CLI");
667
841
  }
668
842
  const creds = await performOAuthLogin(apiUrl);
669
843
  if (!json) {
670
- clack3.outro("Done");
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 clack4.select({
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 (clack4.isCancel(selected)) {
960
+ if (isCancel2(selected)) {
788
961
  process.exit(0);
789
962
  }
790
963
  orgId = selected;
@@ -817,7 +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";
820
- import pc from "picocolors";
993
+ import pc2 from "picocolors";
821
994
 
822
995
  // src/lib/skills.ts
823
996
  import { exec } from "child_process";
@@ -950,7 +1123,7 @@ async function reportCliUsage(toolName, success, maxRetries = 1, explicitConfig)
950
1123
 
951
1124
  // src/lib/analytics.ts
952
1125
  import { PostHog } from "posthog-node";
953
- var POSTHOG_API_KEY = "phc_ueV1ii62wdBTkH7E70ugyeqHIHu8dFDdjs0qq3TZhJz";
1126
+ var POSTHOG_API_KEY = "";
954
1127
  var POSTHOG_HOST = process.env.POSTHOG_HOST || "https://us.i.posthog.com";
955
1128
  var client = null;
956
1129
  function getClient() {
@@ -1074,10 +1247,13 @@ async function readEnvFile(cwd) {
1074
1247
  // src/commands/deployments/deploy.ts
1075
1248
  import * as path2 from "path";
1076
1249
  import * as fs2 from "fs/promises";
1250
+ import { createReadStream } from "fs";
1251
+ import { createHash as createHash2 } from "crypto";
1077
1252
  import * as clack6 from "@clack/prompts";
1078
1253
  import archiver from "archiver";
1079
1254
  var POLL_INTERVAL_MS = 5e3;
1080
1255
  var POLL_TIMEOUT_MS = 3e5;
1256
+ var DIRECT_UPLOAD_CONCURRENCY = 8;
1081
1257
  var EXCLUDE_PATTERNS = [
1082
1258
  "node_modules",
1083
1259
  ".git",
@@ -1105,6 +1281,12 @@ var EXCLUDE_PATTERNS = [
1105
1281
  "skills",
1106
1282
  "coverage"
1107
1283
  ];
1284
+ var DirectDeploymentUnsupportedError = class extends Error {
1285
+ constructor() {
1286
+ super("Direct deployment endpoints are not available on this backend");
1287
+ this.name = "DirectDeploymentUnsupportedError";
1288
+ }
1289
+ };
1108
1290
  function shouldExclude(name) {
1109
1291
  const normalized = name.replace(/\\/g, "/");
1110
1292
  for (const pattern of EXCLUDE_PATTERNS) {
@@ -1115,6 +1297,56 @@ function shouldExclude(name) {
1115
1297
  if (normalized.endsWith(".log")) return true;
1116
1298
  return false;
1117
1299
  }
1300
+ function isInsforgeCloudOssHost(ossHost) {
1301
+ try {
1302
+ return new URL(ossHost).hostname.endsWith(".insforge.app");
1303
+ } catch {
1304
+ return false;
1305
+ }
1306
+ }
1307
+ function normalizeRelativePath(sourceDir, absolutePath) {
1308
+ return path2.relative(sourceDir, absolutePath).split(path2.sep).join("/").replace(/\\/g, "/");
1309
+ }
1310
+ async function hashFile(filePath) {
1311
+ const hash = createHash2("sha1");
1312
+ let size = 0;
1313
+ for await (const chunk of createReadStream(filePath)) {
1314
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
1315
+ size += buffer.length;
1316
+ hash.update(buffer);
1317
+ }
1318
+ return { sha: hash.digest("hex"), size };
1319
+ }
1320
+ async function collectDeploymentFiles(sourceDir) {
1321
+ const files = [];
1322
+ async function walk(currentDir) {
1323
+ const entries = await fs2.readdir(currentDir, { withFileTypes: true });
1324
+ entries.sort((a, b) => a.name.localeCompare(b.name));
1325
+ for (const entry of entries) {
1326
+ const absolutePath = path2.join(currentDir, entry.name);
1327
+ const normalizedPath = normalizeRelativePath(sourceDir, absolutePath);
1328
+ if (!normalizedPath || shouldExclude(normalizedPath)) {
1329
+ continue;
1330
+ }
1331
+ if (entry.isDirectory()) {
1332
+ await walk(absolutePath);
1333
+ continue;
1334
+ }
1335
+ if (!entry.isFile()) {
1336
+ continue;
1337
+ }
1338
+ const { sha, size } = await hashFile(absolutePath);
1339
+ files.push({
1340
+ absolutePath,
1341
+ path: normalizedPath,
1342
+ sha,
1343
+ size
1344
+ });
1345
+ }
1346
+ }
1347
+ await walk(sourceDir);
1348
+ return files;
1349
+ }
1118
1350
  async function createZipBuffer(sourceDir) {
1119
1351
  return new Promise((resolve4, reject) => {
1120
1352
  const archive = archiver("zip", { zlib: { level: 9 } });
@@ -1129,40 +1361,84 @@ async function createZipBuffer(sourceDir) {
1129
1361
  void archive.finalize();
1130
1362
  });
1131
1363
  }
1132
- async function deployProject(opts) {
1133
- const { sourceDir, startBody = {}, spinner: s } = opts;
1134
- s?.start("Creating deployment...");
1135
- const createRes = await ossFetch("/api/deployments", { method: "POST" });
1136
- const { id: deploymentId, uploadUrl, uploadFields } = await createRes.json();
1137
- s?.message("Compressing source files...");
1138
- const zipBuffer = await createZipBuffer(sourceDir);
1139
- s?.message("Uploading...");
1140
- const formData = new FormData();
1141
- for (const [key, value] of Object.entries(uploadFields)) {
1142
- formData.append(key, value);
1364
+ async function runWithConcurrency(items, concurrency, worker) {
1365
+ let nextIndex = 0;
1366
+ async function runWorker() {
1367
+ while (nextIndex < items.length) {
1368
+ const index = nextIndex;
1369
+ nextIndex += 1;
1370
+ await worker(items[index], index);
1371
+ }
1143
1372
  }
1144
- formData.append(
1145
- "file",
1146
- new Blob([zipBuffer], { type: "application/zip" }),
1147
- "deployment.zip"
1148
- );
1149
- const uploadRes = await fetch(uploadUrl, { method: "POST", body: formData });
1150
- if (!uploadRes.ok) {
1151
- const uploadErr = await uploadRes.text();
1152
- throw new CLIError(`Failed to upload: ${uploadErr}`);
1373
+ const workerCount = Math.min(concurrency, items.length);
1374
+ await Promise.all(Array.from({ length: workerCount }, () => runWorker()));
1375
+ }
1376
+ async function createDirectDeploymentSession(config, files) {
1377
+ const url = `${config.oss_host}/api/deployments/direct`;
1378
+ let response;
1379
+ try {
1380
+ response = await fetch(url, {
1381
+ method: "POST",
1382
+ headers: {
1383
+ "Content-Type": "application/json",
1384
+ Authorization: `Bearer ${config.api_key}`
1385
+ },
1386
+ body: JSON.stringify({ files })
1387
+ });
1388
+ } catch (error) {
1389
+ throw new CLIError(formatFetchError(error, url));
1153
1390
  }
1154
- s?.message("Starting deployment...");
1155
- const startRes = await ossFetch(`/api/deployments/${deploymentId}/start`, {
1391
+ if (response.status === 404) {
1392
+ throw new DirectDeploymentUnsupportedError();
1393
+ }
1394
+ if (!response.ok) {
1395
+ const err = await response.json().catch(() => ({}));
1396
+ let message = err.message ?? err.error ?? `OSS request failed: ${response.status}`;
1397
+ if (err.nextActions) {
1398
+ message += `
1399
+ ${err.nextActions}`;
1400
+ }
1401
+ throw new CLIError(message);
1402
+ }
1403
+ const payload = await response.json();
1404
+ if (!payload.id || !Array.isArray(payload.files)) {
1405
+ throw new CLIError("Unexpected response from direct deployment create endpoint.");
1406
+ }
1407
+ return payload;
1408
+ }
1409
+ async function uploadDirectDeploymentFile(deploymentId, manifestFile, localFile) {
1410
+ const requestInit = {
1411
+ method: "PUT",
1412
+ headers: {
1413
+ "Content-Type": "application/octet-stream",
1414
+ "Content-Length": String(localFile.size)
1415
+ },
1416
+ body: createReadStream(localFile.absolutePath),
1417
+ duplex: "half"
1418
+ };
1419
+ await ossFetch(
1420
+ `/api/deployments/${encodeURIComponent(deploymentId)}/files/${encodeURIComponent(manifestFile.fileId)}/content`,
1421
+ requestInit
1422
+ );
1423
+ }
1424
+ async function startDirectDeployment(deploymentId, startBody) {
1425
+ const response = await ossFetch(`/api/deployments/${encodeURIComponent(deploymentId)}/start`, {
1156
1426
  method: "POST",
1427
+ headers: { "Content-Type": "application/json" },
1157
1428
  body: JSON.stringify(startBody)
1158
1429
  });
1159
- await startRes.json();
1160
- s?.message("Building and deploying...");
1430
+ await response.json();
1431
+ }
1432
+ async function pollDeployment(deploymentId, spinner7, syncBeforeRead) {
1433
+ spinner7?.message("Building and deploying...");
1161
1434
  const startTime = Date.now();
1162
1435
  let deployment = null;
1163
1436
  while (Date.now() - startTime < POLL_TIMEOUT_MS) {
1164
- await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
1437
+ await new Promise((resolve4) => setTimeout(resolve4, POLL_INTERVAL_MS));
1165
1438
  try {
1439
+ if (syncBeforeRead) {
1440
+ await ossFetch(`/api/deployments/${deploymentId}/sync`, { method: "POST" });
1441
+ }
1166
1442
  const statusRes = await ossFetch(`/api/deployments/${deploymentId}`);
1167
1443
  deployment = await statusRes.json();
1168
1444
  const status = deployment.status.toUpperCase();
@@ -1170,11 +1446,13 @@ async function deployProject(opts) {
1170
1446
  break;
1171
1447
  }
1172
1448
  if (status === "ERROR" || status === "CANCELED") {
1173
- s?.stop("Deployment failed");
1174
- throw new CLIError(getDeploymentError(deployment.metadata) ?? `Deployment failed with status: ${deployment.status}`);
1449
+ spinner7?.stop("Deployment failed");
1450
+ throw new CLIError(
1451
+ getDeploymentError(deployment.metadata) ?? `Deployment failed with status: ${deployment.status}`
1452
+ );
1175
1453
  }
1176
1454
  const elapsed = Math.round((Date.now() - startTime) / 1e3);
1177
- s?.message(`Building and deploying... (${elapsed}s, status: ${deployment.status})`);
1455
+ spinner7?.message(`Building and deploying... (${elapsed}s, status: ${deployment.status})`);
1178
1456
  } catch (err) {
1179
1457
  if (err instanceof CLIError) throw err;
1180
1458
  }
@@ -1183,8 +1461,78 @@ async function deployProject(opts) {
1183
1461
  const liveUrl = isReady ? deployment?.url ?? null : null;
1184
1462
  return { deploymentId, deployment, isReady, liveUrl };
1185
1463
  }
1464
+ async function deployProjectDirect(opts, config) {
1465
+ const { sourceDir, startBody = {}, spinner: spinner7 } = opts;
1466
+ spinner7?.start("Scanning source files...");
1467
+ const localFiles = await collectDeploymentFiles(sourceDir);
1468
+ if (localFiles.length === 0) {
1469
+ throw new CLIError("No deployable files found in the source directory.");
1470
+ }
1471
+ spinner7?.message("Creating deployment...");
1472
+ const createResult = await createDirectDeploymentSession(
1473
+ config,
1474
+ localFiles.map(({ path: relativePath, sha, size }) => ({ path: relativePath, sha, size }))
1475
+ );
1476
+ const localFileByPath = new Map(localFiles.map((file) => [file.path, file]));
1477
+ const pendingFiles = createResult.files.filter((file) => !file.uploadedAt);
1478
+ spinner7?.message(`Uploading ${pendingFiles.length} file${pendingFiles.length === 1 ? "" : "s"}...`);
1479
+ await runWithConcurrency(pendingFiles, DIRECT_UPLOAD_CONCURRENCY, async (manifestFile) => {
1480
+ const localFile = localFileByPath.get(manifestFile.path);
1481
+ if (!localFile) {
1482
+ throw new CLIError(`Backend returned an unknown file path: ${manifestFile.path}`);
1483
+ }
1484
+ if (localFile.sha !== manifestFile.sha || localFile.size !== manifestFile.size) {
1485
+ throw new CLIError(`Backend file metadata mismatch for: ${manifestFile.path}`);
1486
+ }
1487
+ await uploadDirectDeploymentFile(createResult.id, manifestFile, localFile);
1488
+ });
1489
+ spinner7?.message("Starting deployment...");
1490
+ await startDirectDeployment(createResult.id, startBody);
1491
+ return await pollDeployment(createResult.id, spinner7, !isInsforgeCloudOssHost(config.oss_host));
1492
+ }
1493
+ async function deployProjectLegacy(opts) {
1494
+ const { sourceDir, startBody = {}, spinner: spinner7 } = opts;
1495
+ spinner7?.message("Creating deployment...");
1496
+ const createRes = await ossFetch("/api/deployments", { method: "POST" });
1497
+ const { id: deploymentId, uploadUrl, uploadFields } = await createRes.json();
1498
+ spinner7?.message("Compressing source files...");
1499
+ const zipBuffer = await createZipBuffer(sourceDir);
1500
+ spinner7?.message("Uploading...");
1501
+ const formData = new FormData();
1502
+ for (const [key, value] of Object.entries(uploadFields)) {
1503
+ formData.append(key, value);
1504
+ }
1505
+ formData.append("file", new Blob([zipBuffer], { type: "application/zip" }), "deployment.zip");
1506
+ const uploadRes = await fetch(uploadUrl, { method: "POST", body: formData });
1507
+ if (!uploadRes.ok) {
1508
+ const uploadErr = await uploadRes.text();
1509
+ throw new CLIError(`Failed to upload: ${uploadErr}`);
1510
+ }
1511
+ spinner7?.message("Starting deployment...");
1512
+ const startRes = await ossFetch(`/api/deployments/${deploymentId}/start`, {
1513
+ method: "POST",
1514
+ body: JSON.stringify(startBody)
1515
+ });
1516
+ await startRes.json();
1517
+ return await pollDeployment(deploymentId, spinner7, false);
1518
+ }
1519
+ async function deployProject(opts) {
1520
+ const config = getProjectConfig();
1521
+ if (!config) {
1522
+ throw new ProjectNotLinkedError();
1523
+ }
1524
+ try {
1525
+ return await deployProjectDirect(opts, config);
1526
+ } catch (error) {
1527
+ if (!(error instanceof DirectDeploymentUnsupportedError)) {
1528
+ throw error;
1529
+ }
1530
+ opts.spinner?.message("Direct deployment is not available on this backend. Falling back to the legacy zip upload flow...");
1531
+ return await deployProjectLegacy(opts);
1532
+ }
1533
+ }
1186
1534
  function registerDeploymentsDeployCommand(deploymentsCmd2) {
1187
- deploymentsCmd2.command("deploy [directory]").description("Deploy a frontend project to Vercel").option("--env <vars>", `Environment variables as JSON (e.g. '{"KEY":"value"}')`).option("--meta <meta>", "Deployment metadata as JSON").action(async (directory, opts, cmd) => {
1535
+ deploymentsCmd2.command("deploy [directory]").description("Deploy a frontend project to Vercel").option("--env <vars>", 'Environment variables as JSON (e.g. {"KEY":"value"})').option("--meta <meta>", "Deployment metadata as JSON").action(async (directory, opts, cmd) => {
1188
1536
  const { json } = getRootOpts(cmd);
1189
1537
  try {
1190
1538
  await requireAuth();
@@ -1197,17 +1545,24 @@ function registerDeploymentsDeployCommand(deploymentsCmd2) {
1197
1545
  }
1198
1546
  const dirName = path2.basename(sourceDir);
1199
1547
  if (EXCLUDE_PATTERNS.includes(dirName)) {
1200
- throw new CLIError(`"${dirName}" is an excluded directory and cannot be used as a deploy source. Please specify your project root or output directory instead.`);
1548
+ throw new CLIError(
1549
+ `"${dirName}" is an excluded directory and cannot be used as a deploy source. Please specify your project root or output directory instead.`
1550
+ );
1201
1551
  }
1202
- const s = !json ? clack6.spinner() : null;
1552
+ const spinner7 = !json ? clack6.spinner() : null;
1203
1553
  const startBody = {};
1204
1554
  if (opts.env) {
1205
1555
  try {
1206
1556
  const parsed = JSON.parse(opts.env);
1207
1557
  if (Array.isArray(parsed)) {
1208
1558
  startBody.envVars = parsed;
1559
+ } else if (parsed && typeof parsed === "object") {
1560
+ startBody.envVars = Object.entries(parsed).map(([key, value]) => ({
1561
+ key,
1562
+ value: String(value)
1563
+ }));
1209
1564
  } else {
1210
- startBody.envVars = Object.entries(parsed).map(([key, value]) => ({ key, value }));
1565
+ throw new CLIError("Invalid --env JSON. Expected an object or array.");
1211
1566
  }
1212
1567
  } catch {
1213
1568
  throw new CLIError("Invalid --env JSON.");
@@ -1220,9 +1575,9 @@ function registerDeploymentsDeployCommand(deploymentsCmd2) {
1220
1575
  throw new CLIError("Invalid --meta JSON.");
1221
1576
  }
1222
1577
  }
1223
- const result = await deployProject({ sourceDir, startBody, spinner: s });
1578
+ const result = await deployProject({ sourceDir, startBody, spinner: spinner7 });
1224
1579
  if (result.isReady) {
1225
- s?.stop("Deployment complete");
1580
+ spinner7?.stop("Deployment complete");
1226
1581
  if (json) {
1227
1582
  outputJson(result.deployment);
1228
1583
  } else {
@@ -1232,9 +1587,13 @@ function registerDeploymentsDeployCommand(deploymentsCmd2) {
1232
1587
  clack6.log.info(`Deployment ID: ${result.deploymentId}`);
1233
1588
  }
1234
1589
  } else {
1235
- s?.stop("Deployment is still building");
1590
+ spinner7?.stop("Deployment is still building");
1236
1591
  if (json) {
1237
- outputJson({ id: result.deploymentId, status: result.deployment?.status ?? "building", timedOut: true });
1592
+ outputJson({
1593
+ id: result.deploymentId,
1594
+ status: result.deployment?.status ?? "building",
1595
+ timedOut: true
1596
+ });
1238
1597
  } else {
1239
1598
  clack6.log.info(`Deployment ID: ${result.deploymentId}`);
1240
1599
  clack6.log.warn("Deployment did not finish within 5 minutes.");
@@ -1383,14 +1742,14 @@ function registerCreateCommand(program2) {
1383
1742
  if (json) {
1384
1743
  throw new CLIError("Multiple organizations found. Specify --org-id.");
1385
1744
  }
1386
- const selected = await clack7.select({
1745
+ const selected = await select2({
1387
1746
  message: "Select an organization:",
1388
1747
  options: orgs.map((o) => ({
1389
1748
  value: o.id,
1390
1749
  label: o.name
1391
1750
  }))
1392
1751
  });
1393
- if (clack7.isCancel(selected)) process.exit(0);
1752
+ if (isCancel2(selected)) process.exit(0);
1394
1753
  orgId = selected;
1395
1754
  }
1396
1755
  }
@@ -1401,12 +1760,12 @@ function registerCreateCommand(program2) {
1401
1760
  if (!projectName) {
1402
1761
  if (json) throw new CLIError("--name is required in JSON mode.");
1403
1762
  const defaultName = getDefaultProjectName();
1404
- const name = await clack7.text({
1763
+ const name = await text2({
1405
1764
  message: "Project name:",
1406
1765
  ...defaultName ? { initialValue: defaultName } : {},
1407
1766
  validate: (v) => v.length >= 2 ? void 0 : "Name must be at least 2 characters"
1408
1767
  });
1409
- if (clack7.isCancel(name)) process.exit(0);
1768
+ if (isCancel2(name)) process.exit(0);
1410
1769
  projectName = name;
1411
1770
  }
1412
1771
  projectName = path3.basename(projectName).replace(/[^a-zA-Z0-9._-]/g, "-").replace(/\.+/g, ".");
@@ -1422,21 +1781,21 @@ function registerCreateCommand(program2) {
1422
1781
  if (json) {
1423
1782
  template = "empty";
1424
1783
  } else {
1425
- const approach = await clack7.select({
1784
+ const approach = await select2({
1426
1785
  message: "How would you like to start?",
1427
1786
  options: [
1428
1787
  { value: "blank", label: "Blank project", hint: "Start from scratch with .env.local ready" },
1429
1788
  { value: "template", label: "Start from a template", hint: "Pre-built starter apps" }
1430
1789
  ]
1431
1790
  });
1432
- if (clack7.isCancel(approach)) process.exit(0);
1791
+ if (isCancel2(approach)) process.exit(0);
1433
1792
  captureEvent(orgId, "create_approach_selected", {
1434
1793
  approach
1435
1794
  });
1436
1795
  if (approach === "blank") {
1437
1796
  template = "empty";
1438
1797
  } else {
1439
- const selected = await clack7.select({
1798
+ const selected = await select2({
1440
1799
  message: "Choose a starter template:",
1441
1800
  options: [
1442
1801
  { value: "react", label: "Web app template with React" },
@@ -1447,7 +1806,7 @@ function registerCreateCommand(program2) {
1447
1806
  { value: "todo", label: "Todo app with Next.js" }
1448
1807
  ]
1449
1808
  });
1450
- if (clack7.isCancel(selected)) process.exit(0);
1809
+ if (isCancel2(selected)) process.exit(0);
1451
1810
  template = selected;
1452
1811
  }
1453
1812
  }
@@ -1463,7 +1822,7 @@ function registerCreateCommand(program2) {
1463
1822
  if (hasTemplate) {
1464
1823
  dirName = projectName;
1465
1824
  if (!json) {
1466
- const inputDir = await clack7.text({
1825
+ const inputDir = await text2({
1467
1826
  message: "Directory name:",
1468
1827
  initialValue: projectName,
1469
1828
  validate: (v) => {
@@ -1473,7 +1832,7 @@ function registerCreateCommand(program2) {
1473
1832
  return void 0;
1474
1833
  }
1475
1834
  });
1476
- if (clack7.isCancel(inputDir)) process.exit(0);
1835
+ if (isCancel2(inputDir)) process.exit(0);
1477
1836
  dirName = path3.basename(inputDir).replace(/[^a-zA-Z0-9._-]/g, "-");
1478
1837
  }
1479
1838
  if (!dirName || dirName === "." || dirName === "..") {
@@ -1561,10 +1920,10 @@ function registerCreateCommand(program2) {
1561
1920
  }
1562
1921
  let liveUrl = null;
1563
1922
  if (templateDownloaded && !json) {
1564
- const shouldDeploy = await clack7.confirm({
1923
+ const shouldDeploy = await confirm2({
1565
1924
  message: "Would you like to deploy now?"
1566
1925
  });
1567
- if (!clack7.isCancel(shouldDeploy) && shouldDeploy) {
1926
+ if (!isCancel2(shouldDeploy) && shouldDeploy) {
1568
1927
  try {
1569
1928
  const envVars = await readEnvFile(process.cwd());
1570
1929
  const startBody = {};
@@ -1826,14 +2185,14 @@ function registerProjectLinkCommand(program2) {
1826
2185
  if (json) {
1827
2186
  throw new CLIError("Multiple organizations found. Specify --org-id.");
1828
2187
  }
1829
- const selected = await clack8.select({
2188
+ const selected = await select2({
1830
2189
  message: "Select an organization:",
1831
2190
  options: orgs.map((o) => ({
1832
2191
  value: o.id,
1833
2192
  label: o.name
1834
2193
  }))
1835
2194
  });
1836
- if (clack8.isCancel(selected)) process.exit(0);
2195
+ if (isCancel2(selected)) process.exit(0);
1837
2196
  orgId = selected;
1838
2197
  }
1839
2198
  }
@@ -1848,14 +2207,14 @@ function registerProjectLinkCommand(program2) {
1848
2207
  if (json) {
1849
2208
  throw new CLIError("Specify --project-id in JSON mode.");
1850
2209
  }
1851
- const selected = await clack8.select({
2210
+ const selected = await select2({
1852
2211
  message: "Select a project to link:",
1853
2212
  options: projects.map((p) => ({
1854
2213
  value: p.id,
1855
2214
  label: `${p.name} (${p.region}, ${p.status})`
1856
2215
  }))
1857
2216
  });
1858
- if (clack8.isCancel(selected)) process.exit(0);
2217
+ if (isCancel2(selected)) process.exit(0);
1859
2218
  projectId = selected;
1860
2219
  }
1861
2220
  let project;
@@ -1906,7 +2265,7 @@ function registerProjectLinkCommand(program2) {
1906
2265
  }
1907
2266
  let dirName = project.name;
1908
2267
  if (!json) {
1909
- const inputDir = await clack8.text({
2268
+ const inputDir = await text2({
1910
2269
  message: "Directory name:",
1911
2270
  initialValue: project.name,
1912
2271
  validate: (v) => {
@@ -1916,7 +2275,7 @@ function registerProjectLinkCommand(program2) {
1916
2275
  return void 0;
1917
2276
  }
1918
2277
  });
1919
- if (clack8.isCancel(inputDir)) process.exit(0);
2278
+ if (isCancel2(inputDir)) process.exit(0);
1920
2279
  dirName = path4.basename(inputDir).replace(/[^a-zA-Z0-9._-]/g, "-");
1921
2280
  }
1922
2281
  if (!dirName || dirName === "." || dirName === "..") {
@@ -1954,12 +2313,12 @@ function registerProjectLinkCommand(program2) {
1954
2313
  await reportCliUsage("cli.link", true, 6, projectConfig);
1955
2314
  if (!json) {
1956
2315
  const dashboardUrl = `${getFrontendUrl()}/dashboard/project/${project.id}`;
1957
- clack8.log.step(`Dashboard: ${pc.underline(dashboardUrl)}`);
2316
+ clack8.log.step(`Dashboard: ${pc2.underline(dashboardUrl)}`);
1958
2317
  if (templateDownloaded) {
1959
- const runCommand = `${pc.cyan("cd")} ${pc.green(dirName)} ${pc.dim("&&")} ${pc.cyan("npm run dev")}`;
2318
+ const runCommand = `${pc2.cyan("cd")} ${pc2.green(dirName)} ${pc2.dim("&&")} ${pc2.cyan("npm run dev")}`;
1960
2319
  const steps = [
1961
- `${pc.bold("1.")} ${runCommand}`,
1962
- `${pc.bold("2.")} Open ${pc.cyan("Claude Code")} or ${pc.cyan("Cursor")} and prompt your agent to add more features`
2320
+ `${pc2.bold("1.")} ${runCommand}`,
2321
+ `${pc2.bold("2.")} Open ${pc2.cyan("Claude Code")} or ${pc2.cyan("Cursor")} and prompt your agent to add more features`
1963
2322
  ];
1964
2323
  clack8.note(steps.join("\n"), "What's next");
1965
2324
  } else {
@@ -2493,17 +2852,17 @@ function registerFunctionsCommands(functionsCmd2) {
2493
2852
 
2494
2853
  // src/commands/functions/deploy.ts
2495
2854
  import { readFileSync as readFileSync4, existsSync as existsSync3 } from "fs";
2496
- import { join as join6 } from "path";
2855
+ import { join as join7 } from "path";
2497
2856
  function registerFunctionsDeployCommand(functionsCmd2) {
2498
2857
  functionsCmd2.command("deploy <slug>").description("Deploy an edge function (create or update)").option("--file <path>", "Path to the function source file").option("--name <name>", "Function display name").option("--description <desc>", "Function description").action(async (slug, opts, cmd) => {
2499
2858
  const { json } = getRootOpts(cmd);
2500
2859
  try {
2501
2860
  await requireAuth();
2502
- const filePath = opts.file ?? join6(process.cwd(), "insforge", "functions", slug, "index.ts");
2861
+ const filePath = opts.file ?? join7(process.cwd(), "insforge", "functions", slug, "index.ts");
2503
2862
  if (!existsSync3(filePath)) {
2504
2863
  throw new CLIError(
2505
2864
  `Source file not found: ${filePath}
2506
- Specify --file <path> or create ${join6("insforge", "functions", slug, "index.ts")}`
2865
+ Specify --file <path> or create ${join7("insforge", "functions", slug, "index.ts")}`
2507
2866
  );
2508
2867
  }
2509
2868
  const code = readFileSync4(filePath, "utf-8");
@@ -2590,8 +2949,8 @@ function registerFunctionsInvokeCommand(functionsCmd2) {
2590
2949
  console.log(JSON.stringify(data, null, 2));
2591
2950
  }
2592
2951
  } else {
2593
- const text4 = await res.text();
2594
- console.log(text4);
2952
+ const text3 = await res.text();
2953
+ console.log(text3);
2595
2954
  }
2596
2955
  if (status >= 400) {
2597
2956
  throw new CLIError(`HTTP ${status}`, 1, "HTTP_ERROR");
@@ -2634,10 +2993,10 @@ function registerFunctionsDeleteCommand(functionsCmd2) {
2634
2993
  try {
2635
2994
  await requireAuth();
2636
2995
  if (!yes && !json) {
2637
- const confirmed = await clack9.confirm({
2996
+ const confirmed = await confirm2({
2638
2997
  message: `Delete function "${slug}"? This cannot be undone.`
2639
2998
  });
2640
- if (clack9.isCancel(confirmed) || !confirmed) {
2999
+ if (isCancel2(confirmed) || !confirmed) {
2641
3000
  clack9.log.info("Cancelled.");
2642
3001
  return;
2643
3002
  }
@@ -2737,7 +3096,7 @@ function registerStorageUploadCommand(storageCmd2) {
2737
3096
 
2738
3097
  // src/commands/storage/download.ts
2739
3098
  import { writeFileSync as writeFileSync3 } from "fs";
2740
- import { join as join7, basename as basename6 } from "path";
3099
+ import { join as join8, basename as basename6 } from "path";
2741
3100
  function registerStorageDownloadCommand(storageCmd2) {
2742
3101
  storageCmd2.command("download <objectKey>").description("Download a file from a storage bucket").requiredOption("--bucket <name>", "Source bucket name").option("--output <path>", "Output file path (defaults to current directory)").action(async (objectKey, opts, cmd) => {
2743
3102
  const { json } = getRootOpts(cmd);
@@ -2757,7 +3116,7 @@ function registerStorageDownloadCommand(storageCmd2) {
2757
3116
  throw new CLIError(err.error ?? `Download failed: ${res.status}`);
2758
3117
  }
2759
3118
  const buffer = Buffer.from(await res.arrayBuffer());
2760
- const outputPath = opts.output ?? join7(process.cwd(), basename6(objectKey));
3119
+ const outputPath = opts.output ?? join8(process.cwd(), basename6(objectKey));
2761
3120
  writeFileSync3(outputPath, buffer);
2762
3121
  if (json) {
2763
3122
  outputJson({ success: true, path: outputPath, size: buffer.length });
@@ -2796,17 +3155,16 @@ function registerStorageCreateBucketCommand(storageCmd2) {
2796
3155
  }
2797
3156
 
2798
3157
  // src/commands/storage/delete-bucket.ts
2799
- import * as clack10 from "@clack/prompts";
2800
3158
  function registerStorageDeleteBucketCommand(storageCmd2) {
2801
3159
  storageCmd2.command("delete-bucket <name>").description("Delete a storage bucket and all its objects").action(async (name, _opts, cmd) => {
2802
3160
  const { json, yes } = getRootOpts(cmd);
2803
3161
  try {
2804
3162
  await requireAuth();
2805
3163
  if (!yes && !json) {
2806
- const confirm8 = await clack10.confirm({
3164
+ const confirm3 = await confirm2({
2807
3165
  message: `Delete bucket "${name}" and all its objects? This cannot be undone.`
2808
3166
  });
2809
- if (!confirm8 || clack10.isCancel(confirm8)) {
3167
+ if (isCancel2(confirm3) || !confirm3) {
2810
3168
  process.exit(0);
2811
3169
  }
2812
3170
  }
@@ -3061,7 +3419,6 @@ function registerDeploymentsStatusCommand(deploymentsCmd2) {
3061
3419
  }
3062
3420
 
3063
3421
  // src/commands/deployments/cancel.ts
3064
- import * as clack11 from "@clack/prompts";
3065
3422
  function registerDeploymentsCancelCommand(deploymentsCmd2) {
3066
3423
  deploymentsCmd2.command("cancel <id>").description("Cancel a deployment").action(async (id, _opts, cmd) => {
3067
3424
  const { json, yes } = getRootOpts(cmd);
@@ -3069,10 +3426,10 @@ function registerDeploymentsCancelCommand(deploymentsCmd2) {
3069
3426
  await requireAuth();
3070
3427
  if (!getProjectConfig()) throw new ProjectNotLinkedError();
3071
3428
  if (!yes && !json) {
3072
- const confirmed = await clack11.confirm({
3429
+ const confirmed = await confirm2({
3073
3430
  message: `Cancel deployment ${id}?`
3074
3431
  });
3075
- if (clack11.isCancel(confirmed) || !confirmed) process.exit(0);
3432
+ if (isCancel2(confirmed) || !confirmed) process.exit(0);
3076
3433
  }
3077
3434
  const res = await ossFetch(`/api/deployments/${id}/cancel`, { method: "POST" });
3078
3435
  const result = await res.json();
@@ -3352,17 +3709,16 @@ function registerSecretsUpdateCommand(secretsCmd2) {
3352
3709
  }
3353
3710
 
3354
3711
  // src/commands/secrets/delete.ts
3355
- import * as clack12 from "@clack/prompts";
3356
3712
  function registerSecretsDeleteCommand(secretsCmd2) {
3357
3713
  secretsCmd2.command("delete <key>").description("Delete a secret").action(async (key, _opts, cmd) => {
3358
3714
  const { json, yes } = getRootOpts(cmd);
3359
3715
  try {
3360
3716
  await requireAuth();
3361
3717
  if (!yes && !json) {
3362
- const confirm8 = await clack12.confirm({
3718
+ const confirm3 = await confirm2({
3363
3719
  message: `Delete secret "${key}"? This cannot be undone.`
3364
3720
  });
3365
- if (!confirm8 || clack12.isCancel(confirm8)) {
3721
+ if (isCancel2(confirm3) || !confirm3) {
3366
3722
  process.exit(0);
3367
3723
  }
3368
3724
  }
@@ -3538,17 +3894,16 @@ function registerSchedulesUpdateCommand(schedulesCmd2) {
3538
3894
  }
3539
3895
 
3540
3896
  // src/commands/schedules/delete.ts
3541
- import * as clack13 from "@clack/prompts";
3542
3897
  function registerSchedulesDeleteCommand(schedulesCmd2) {
3543
3898
  schedulesCmd2.command("delete <id>").description("Delete a schedule").action(async (id, _opts, cmd) => {
3544
3899
  const { json, yes } = getRootOpts(cmd);
3545
3900
  try {
3546
3901
  await requireAuth();
3547
3902
  if (!yes && !json) {
3548
- const confirm8 = await clack13.confirm({
3903
+ const confirm3 = await confirm2({
3549
3904
  message: `Delete schedule "${id}"? This cannot be undone.`
3550
3905
  });
3551
- if (!confirm8 || clack13.isCancel(confirm8)) {
3906
+ if (isCancel2(confirm3) || !confirm3) {
3552
3907
  process.exit(0);
3553
3908
  }
3554
3909
  }
@@ -3869,10 +4224,10 @@ function registerComputeLogsCommand(computeCmd2) {
3869
4224
 
3870
4225
  // src/commands/compute/deploy.ts
3871
4226
  import { existsSync as existsSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4, unlinkSync as unlinkSync2, renameSync } from "fs";
3872
- import { join as join8 } from "path";
4227
+ import { join as join9 } from "path";
3873
4228
  import { execSync, spawn } from "child_process";
3874
4229
  function parseFlyToml(dir) {
3875
- const tomlPath = join8(dir, "fly.toml");
4230
+ const tomlPath = join9(dir, "fly.toml");
3876
4231
  if (!existsSync5(tomlPath)) return {};
3877
4232
  const content = readFileSync6(tomlPath, "utf-8");
3878
4233
  const config = {};
@@ -3944,7 +4299,7 @@ function registerComputeDeployCommand(computeCmd2) {
3944
4299
  checkFlyctl();
3945
4300
  const flyToken = getFlyToken();
3946
4301
  const dir = directory ?? process.cwd();
3947
- const dockerfilePath = join8(dir, "Dockerfile");
4302
+ const dockerfilePath = join9(dir, "Dockerfile");
3948
4303
  if (!existsSync5(dockerfilePath)) {
3949
4304
  throw new CLIError(`No Dockerfile found in ${dir}`);
3950
4305
  }
@@ -3990,8 +4345,8 @@ function registerComputeDeployCommand(computeCmd2) {
3990
4345
  serviceId = service.id;
3991
4346
  flyAppId = service.flyAppId;
3992
4347
  }
3993
- const existingTomlPath = join8(dir, "fly.toml");
3994
- const backupTomlPath = join8(dir, "fly.toml.insforge-backup");
4348
+ const existingTomlPath = join9(dir, "fly.toml");
4349
+ const backupTomlPath = join9(dir, "fly.toml.insforge-backup");
3995
4350
  let hadExistingToml = false;
3996
4351
  if (existsSync5(existingTomlPath)) {
3997
4352
  hadExistingToml = true;
@@ -4179,7 +4534,7 @@ function formatSize2(gb) {
4179
4534
 
4180
4535
  // src/commands/diagnose/index.ts
4181
4536
  import * as os from "os";
4182
- import * as clack14 from "@clack/prompts";
4537
+ import * as clack10 from "@clack/prompts";
4183
4538
 
4184
4539
  // src/commands/diagnose/metrics.ts
4185
4540
  var METRIC_LABELS = {
@@ -4716,10 +5071,10 @@ function registerDiagnoseCommands(diagnoseCmd2) {
4716
5071
  if (question.length === 0 || question.length > 2e3) {
4717
5072
  throw new CLIError("Question must be between 1 and 2000 characters.");
4718
5073
  }
4719
- const s = !json ? clack14.spinner() : null;
5074
+ const s = !json ? clack10.spinner() : null;
4720
5075
  s?.start("Collecting diagnostic data...");
4721
5076
  const data2 = await collectDiagnosticData(projectId, ossMode, apiUrl);
4722
- const cliVersion = "0.1.49";
5077
+ const cliVersion = "0.1.51-dev.0";
4723
5078
  s?.stop("Data collected");
4724
5079
  if (!json) {
4725
5080
  console.log(`
@@ -4835,7 +5190,7 @@ function registerDiagnoseCommands(diagnoseCmd2) {
4835
5190
  outputJson({ sessionId, events: jsonEvents });
4836
5191
  }
4837
5192
  if (!json && sessionId) {
4838
- const ratingChoice = await clack14.select({
5193
+ const ratingChoice = await select2({
4839
5194
  message: "Was this analysis helpful?",
4840
5195
  options: [
4841
5196
  { value: "skip", label: "Skip", hint: "no rating" },
@@ -4844,7 +5199,7 @@ function registerDiagnoseCommands(diagnoseCmd2) {
4844
5199
  { value: "incorrect", label: "Incorrect", hint: "diagnosis was wrong or misleading" }
4845
5200
  ]
4846
5201
  });
4847
- if (!clack14.isCancel(ratingChoice) && ratingChoice !== "skip") {
5202
+ if (!isCancel2(ratingChoice) && ratingChoice !== "skip") {
4848
5203
  try {
4849
5204
  await rateDiagnosticSession(
4850
5205
  sessionId,
@@ -4852,9 +5207,9 @@ function registerDiagnoseCommands(diagnoseCmd2) {
4852
5207
  void 0,
4853
5208
  apiUrl
4854
5209
  );
4855
- clack14.log.success("Thanks for your feedback!");
5210
+ clack10.log.success("Thanks for your feedback!");
4856
5211
  } catch {
4857
- clack14.log.warn("Failed to submit rating.");
5212
+ clack10.log.warn("Failed to submit rating.");
4858
5213
  }
4859
5214
  }
4860
5215
  }
@@ -4947,7 +5302,7 @@ function formatBytesCompact(bytes) {
4947
5302
 
4948
5303
  // src/index.ts
4949
5304
  var __dirname = dirname(fileURLToPath(import.meta.url));
4950
- var pkg = JSON.parse(readFileSync7(join9(__dirname, "../package.json"), "utf-8"));
5305
+ var pkg = JSON.parse(readFileSync7(join10(__dirname, "../package.json"), "utf-8"));
4951
5306
  var INSFORGE_LOGO = `
4952
5307
  \u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
4953
5308
  \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D
@@ -5049,7 +5404,7 @@ async function showInteractiveMenu() {
5049
5404
  } catch {
5050
5405
  }
5051
5406
  console.log(INSFORGE_LOGO);
5052
- clack15.intro(`InsForge CLI v${pkg.version}`);
5407
+ clack11.intro(`InsForge CLI v${pkg.version}`);
5053
5408
  const options = [];
5054
5409
  if (!isLoggedIn) {
5055
5410
  options.push({ value: "login", label: "Log in to InsForge" });
@@ -5065,12 +5420,12 @@ async function showInteractiveMenu() {
5065
5420
  { value: "docs", label: "View documentation" },
5066
5421
  { value: "help", label: "Show all commands" }
5067
5422
  );
5068
- const action = await clack15.select({
5423
+ const action = await select2({
5069
5424
  message: "What would you like to do?",
5070
5425
  options
5071
5426
  });
5072
- if (clack15.isCancel(action)) {
5073
- clack15.cancel("Bye!");
5427
+ if (isCancel2(action)) {
5428
+ clack11.cancel("Bye!");
5074
5429
  process.exit(0);
5075
5430
  }
5076
5431
  switch (action) {