@insforge/cli 0.1.5 → 0.1.7

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
@@ -7,7 +7,7 @@ import { fileURLToPath } from "url";
7
7
  import { Command } from "commander";
8
8
 
9
9
  // src/commands/login.ts
10
- import * as clack from "@clack/prompts";
10
+ import * as clack3 from "@clack/prompts";
11
11
 
12
12
  // src/lib/config.ts
13
13
  import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
@@ -17,6 +17,7 @@ var GLOBAL_DIR = join(homedir(), ".insforge");
17
17
  var CREDENTIALS_FILE = join(GLOBAL_DIR, "credentials.json");
18
18
  var CONFIG_FILE = join(GLOBAL_DIR, "config.json");
19
19
  var DEFAULT_PLATFORM_URL = "https://api.insforge.dev";
20
+ var DEFAULT_FRONTEND_URL = "https://insforge.dev";
20
21
  function ensureGlobalDir() {
21
22
  if (!existsSync(GLOBAL_DIR)) {
22
23
  mkdirSync(GLOBAL_DIR, { recursive: true });
@@ -78,6 +79,9 @@ function saveProjectConfig(config) {
78
79
  function getPlatformApiUrl(override) {
79
80
  return process.env.INSFORGE_API_URL ?? override ?? getGlobalConfig().platform_api_url ?? DEFAULT_PLATFORM_URL;
80
81
  }
82
+ function getFrontendUrl() {
83
+ return process.env.INSFORGE_FRONTEND_URL ?? DEFAULT_FRONTEND_URL;
84
+ }
81
85
  function getAccessToken() {
82
86
  return process.env.INSFORGE_ACCESS_TOKEN ?? getCredentials()?.access_token ?? null;
83
87
  }
@@ -135,6 +139,7 @@ function getRootOpts(cmd) {
135
139
  import { createServer } from "http";
136
140
  import { randomBytes, createHash } from "crypto";
137
141
  import { URL } from "url";
142
+ import * as clack from "@clack/prompts";
138
143
  var DEFAULT_CLIENT_ID = "clf_NK8cMUs41gm8ZcfdtSguVw";
139
144
  var OAUTH_SCOPES = "user:read organizations:read projects:read projects:write";
140
145
  function generatePKCE() {
@@ -242,14 +247,77 @@ function startCallbackServer() {
242
247
  }, 5 * 60 * 1e3).unref();
243
248
  });
244
249
  }
250
+ async function performOAuthLogin(apiUrl) {
251
+ const platformUrl = getPlatformApiUrl(apiUrl);
252
+ const config = getGlobalConfig();
253
+ const clientId = config.oauth_client_id ?? DEFAULT_CLIENT_ID;
254
+ const pkce = generatePKCE();
255
+ const state = generateState();
256
+ const { port, result, close } = await startCallbackServer();
257
+ const redirectUri = `http://127.0.0.1:${port}/callback`;
258
+ const authUrl = buildAuthorizeUrl({
259
+ platformUrl,
260
+ clientId,
261
+ redirectUri,
262
+ codeChallenge: pkce.code_challenge,
263
+ state,
264
+ scopes: OAUTH_SCOPES
265
+ });
266
+ clack.log.info("Opening browser for authentication...");
267
+ clack.log.info(`If browser doesn't open, visit:
268
+ ${authUrl}`);
269
+ try {
270
+ const open = (await import("open")).default;
271
+ await open(authUrl);
272
+ } catch {
273
+ clack.log.warn("Could not open browser. Please visit the URL above.");
274
+ }
275
+ const s = clack.spinner();
276
+ s.start("Waiting for authentication...");
277
+ try {
278
+ const callbackResult = await result;
279
+ close();
280
+ if (callbackResult.state !== state) {
281
+ s.stop("Authentication failed");
282
+ throw new Error("State mismatch. Possible CSRF attack.");
283
+ }
284
+ s.message("Exchanging authorization code...");
285
+ const tokens = await exchangeCodeForTokens({
286
+ platformUrl,
287
+ code: callbackResult.code,
288
+ redirectUri,
289
+ clientId,
290
+ codeVerifier: pkce.code_verifier
291
+ });
292
+ const creds = {
293
+ access_token: tokens.access_token,
294
+ refresh_token: tokens.refresh_token,
295
+ user: { id: "", name: "", email: "", avatar_url: null, email_verified: true }
296
+ };
297
+ saveCredentials(creds);
298
+ try {
299
+ const profile = await getProfile(apiUrl);
300
+ creds.user = profile;
301
+ saveCredentials(creds);
302
+ s.stop(`Authenticated as ${profile.email}`);
303
+ } catch {
304
+ s.stop("Authenticated successfully");
305
+ }
306
+ return creds;
307
+ } catch (err) {
308
+ close();
309
+ s.stop("Authentication failed");
310
+ throw err;
311
+ }
312
+ }
245
313
 
246
314
  // src/lib/credentials.ts
247
- function requireAuth() {
315
+ import * as clack2 from "@clack/prompts";
316
+ async function requireAuth(apiUrl) {
248
317
  const creds = getCredentials();
249
- if (!creds || !creds.access_token) {
250
- throw new AuthError();
251
- }
252
- return creds;
318
+ if (creds && creds.access_token) return creds;
319
+ clack2.log.info("You need to log in to continue.");
320
+ return await performOAuthLogin(apiUrl);
253
321
  }
254
322
  async function refreshAccessToken(apiUrl) {
255
323
  const creds = getCredentials();
@@ -371,7 +439,7 @@ function registerLoginCommand(program2) {
371
439
  if (opts.email) {
372
440
  await loginWithEmail(json, apiUrl);
373
441
  } else {
374
- await loginWithOAuth(json, apiUrl, opts.clientId);
442
+ await loginWithOAuth(json, apiUrl);
375
443
  }
376
444
  } catch (err) {
377
445
  if (err instanceof Error && err.message.includes("cancelled")) {
@@ -383,28 +451,28 @@ function registerLoginCommand(program2) {
383
451
  }
384
452
  async function loginWithEmail(json, apiUrl) {
385
453
  if (!json) {
386
- clack.intro("InsForge CLI");
454
+ clack3.intro("InsForge CLI");
387
455
  }
388
- const email = json ? process.env.INSFORGE_EMAIL : await clack.text({
456
+ const email = json ? process.env.INSFORGE_EMAIL : await clack3.text({
389
457
  message: "Email:",
390
458
  validate: (v) => v.includes("@") ? void 0 : "Please enter a valid email"
391
459
  });
392
- if (clack.isCancel(email)) {
393
- clack.cancel("Login cancelled.");
460
+ if (clack3.isCancel(email)) {
461
+ clack3.cancel("Login cancelled.");
394
462
  throw new Error("cancelled");
395
463
  }
396
- const password2 = json ? process.env.INSFORGE_PASSWORD : await clack.password({
464
+ const password2 = json ? process.env.INSFORGE_PASSWORD : await clack3.password({
397
465
  message: "Password:"
398
466
  });
399
- if (clack.isCancel(password2)) {
400
- clack.cancel("Login cancelled.");
467
+ if (clack3.isCancel(password2)) {
468
+ clack3.cancel("Login cancelled.");
401
469
  throw new Error("cancelled");
402
470
  }
403
471
  if (!email || !password2) {
404
472
  throw new Error("Email and password are required. Set INSFORGE_EMAIL and INSFORGE_PASSWORD environment variables for non-interactive mode.");
405
473
  }
406
474
  if (!json) {
407
- const s = clack.spinner();
475
+ const s = clack3.spinner();
408
476
  s.start("Authenticating...");
409
477
  const result = await login(email, password2, apiUrl);
410
478
  const creds = {
@@ -414,7 +482,7 @@ async function loginWithEmail(json, apiUrl) {
414
482
  };
415
483
  saveCredentials(creds);
416
484
  s.stop(`Authenticated as ${result.user.email}`);
417
- clack.outro("Done");
485
+ clack3.outro("Done");
418
486
  } else {
419
487
  const result = await login(email, password2, apiUrl);
420
488
  const creds = {
@@ -426,105 +494,15 @@ async function loginWithEmail(json, apiUrl) {
426
494
  console.log(JSON.stringify({ success: true, user: result.user }));
427
495
  }
428
496
  }
429
- async function loginWithOAuth(json, apiUrl, clientIdOverride) {
430
- const platformUrl = getPlatformApiUrl(apiUrl);
431
- const config = getGlobalConfig();
432
- const clientId = clientIdOverride ?? config.oauth_client_id ?? DEFAULT_CLIENT_ID;
433
- const pkce = generatePKCE();
434
- const state = generateState();
435
- const { port, result, close } = await startCallbackServer();
436
- const redirectUri = `http://127.0.0.1:${port}/callback`;
437
- const authUrl = buildAuthorizeUrl({
438
- platformUrl,
439
- clientId,
440
- redirectUri,
441
- codeChallenge: pkce.code_challenge,
442
- state,
443
- scopes: OAUTH_SCOPES
444
- });
497
+ async function loginWithOAuth(json, apiUrl) {
445
498
  if (!json) {
446
- clack.intro("InsForge CLI");
447
- clack.log.info("Opening browser for authentication...");
448
- clack.log.info(`If browser doesn't open, visit:
449
- ${authUrl}`);
450
- }
451
- try {
452
- const open = (await import("open")).default;
453
- await open(authUrl);
454
- } catch {
455
- if (!json) {
456
- clack.log.warn(`Could not open browser. Please visit the URL above.`);
457
- }
499
+ clack3.intro("InsForge CLI");
458
500
  }
501
+ const creds = await performOAuthLogin(apiUrl);
459
502
  if (!json) {
460
- const s = clack.spinner();
461
- s.start("Waiting for authentication...");
462
- try {
463
- const callbackResult = await result;
464
- close();
465
- if (callbackResult.state !== state) {
466
- s.stop("Authentication failed");
467
- throw new Error("State mismatch. Possible CSRF attack.");
468
- }
469
- s.message("Exchanging authorization code...");
470
- const tokens = await exchangeCodeForTokens({
471
- platformUrl,
472
- code: callbackResult.code,
473
- redirectUri,
474
- clientId,
475
- codeVerifier: pkce.code_verifier
476
- });
477
- const creds = {
478
- access_token: tokens.access_token,
479
- refresh_token: tokens.refresh_token,
480
- user: { id: "", name: "", email: "", avatar_url: null, email_verified: true }
481
- };
482
- saveCredentials(creds);
483
- try {
484
- const profile = await getProfile(apiUrl);
485
- creds.user = profile;
486
- saveCredentials(creds);
487
- s.stop(`Authenticated as ${profile.email}`);
488
- } catch {
489
- s.stop("Authenticated successfully");
490
- }
491
- clack.outro("Done");
492
- } catch (err) {
493
- close();
494
- s.stop("Authentication failed");
495
- throw err;
496
- }
503
+ clack3.outro("Done");
497
504
  } else {
498
- try {
499
- const callbackResult = await result;
500
- close();
501
- if (callbackResult.state !== state) {
502
- throw new Error("State mismatch.");
503
- }
504
- const tokens = await exchangeCodeForTokens({
505
- platformUrl,
506
- code: callbackResult.code,
507
- redirectUri,
508
- clientId,
509
- codeVerifier: pkce.code_verifier
510
- });
511
- const creds = {
512
- access_token: tokens.access_token,
513
- refresh_token: tokens.refresh_token,
514
- user: { id: "", name: "", email: "", avatar_url: null, email_verified: true }
515
- };
516
- saveCredentials(creds);
517
- try {
518
- const profile = await getProfile(apiUrl);
519
- creds.user = profile;
520
- saveCredentials(creds);
521
- } catch {
522
- }
523
- console.log(JSON.stringify({ success: true, user: creds.user }));
524
- } catch (err) {
525
- close();
526
- throw err;
527
- }
505
+ console.log(JSON.stringify({ success: true, user: creds.user }));
528
506
  }
529
507
  }
530
508
 
@@ -572,7 +550,7 @@ function registerWhoamiCommand(program2) {
572
550
  program2.command("whoami").description("Show current authenticated user").action(async (_opts, cmd) => {
573
551
  const { json, apiUrl } = getRootOpts(cmd);
574
552
  try {
575
- requireAuth();
553
+ await requireAuth(apiUrl);
576
554
  const profile = await getProfile(apiUrl);
577
555
  if (json) {
578
556
  outputJson(profile);
@@ -592,7 +570,7 @@ function registerOrgsCommands(orgsCmd2) {
592
570
  orgsCmd2.command("list").description("List all organizations").action(async (_opts, cmd) => {
593
571
  const { json, apiUrl } = getRootOpts(cmd);
594
572
  try {
595
- requireAuth();
573
+ await requireAuth(apiUrl);
596
574
  const orgs = await listOrganizations(apiUrl);
597
575
  if (json) {
598
576
  outputJson(orgs);
@@ -617,12 +595,12 @@ function registerOrgsCommands(orgsCmd2) {
617
595
  }
618
596
 
619
597
  // src/commands/projects/list.ts
620
- import * as clack2 from "@clack/prompts";
598
+ import * as clack4 from "@clack/prompts";
621
599
  function registerProjectsCommands(projectsCmd2) {
622
600
  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) => {
623
601
  const { json, apiUrl } = getRootOpts(cmd);
624
602
  try {
625
- requireAuth();
603
+ await requireAuth(apiUrl);
626
604
  let orgId = opts.orgId ?? getGlobalConfig().default_org_id;
627
605
  if (!orgId) {
628
606
  const orgs = await listOrganizations(apiUrl);
@@ -632,14 +610,14 @@ function registerProjectsCommands(projectsCmd2) {
632
610
  if (orgs.length === 1) {
633
611
  orgId = orgs[0].id;
634
612
  } else if (!json) {
635
- const selected = await clack2.select({
613
+ const selected = await clack4.select({
636
614
  message: "Select an organization:",
637
615
  options: orgs.map((o) => ({
638
616
  value: o.id,
639
617
  label: o.name
640
618
  }))
641
619
  });
642
- if (clack2.isCancel(selected)) {
620
+ if (clack4.isCancel(selected)) {
643
621
  process.exit(0);
644
622
  }
645
623
  orgId = selected;
@@ -667,23 +645,23 @@ function registerProjectsCommands(projectsCmd2) {
667
645
  }
668
646
 
669
647
  // src/commands/projects/link.ts
670
- import * as clack4 from "@clack/prompts";
648
+ import * as clack6 from "@clack/prompts";
671
649
 
672
650
  // src/lib/skills.ts
673
651
  import { exec } from "child_process";
674
652
  import { promisify } from "util";
675
- import * as clack3 from "@clack/prompts";
653
+ import * as clack5 from "@clack/prompts";
676
654
  var execAsync = promisify(exec);
677
655
  async function installSkills(json) {
678
656
  try {
679
- if (!json) clack3.log.info("Installing InsForge agent skills...");
657
+ if (!json) clack5.log.info("Installing InsForge agent skills...");
680
658
  await execAsync("npx skills add insforge/agent-skills -y -a antigravity -a augment -a claude-code -a cline -a codex -a cursor -a gemini-cli -a github-copilot -a kilo -a qoder -a qwen-code -a roo -a trae -a windsurf", {
681
659
  cwd: process.cwd(),
682
660
  timeout: 6e4
683
661
  });
684
- if (!json) clack3.log.success("InsForge agent skills installed.");
662
+ if (!json) clack5.log.success("InsForge agent skills installed.");
685
663
  } catch {
686
- if (!json) clack3.log.warn("Failed to install agent skills. You can run manually: npx skills add insforge/agent-skills");
664
+ if (!json) clack5.log.warn("Failed to install agent skills. You can run manually: npx skills add insforge/agent-skills");
687
665
  }
688
666
  }
689
667
 
@@ -695,7 +673,7 @@ function registerProjectLinkCommand(program2) {
695
673
  program2.command("link").description("Link current directory to an InsForge project").option("--project-id <id>", "Project ID to link").option("--org-id <id>", "Organization ID").action(async (opts, cmd) => {
696
674
  const { json, apiUrl } = getRootOpts(cmd);
697
675
  try {
698
- requireAuth();
676
+ await requireAuth(apiUrl);
699
677
  let orgId = opts.orgId;
700
678
  let projectId = opts.projectId;
701
679
  if (!orgId) {
@@ -706,14 +684,14 @@ function registerProjectLinkCommand(program2) {
706
684
  if (json) {
707
685
  throw new CLIError("Specify --org-id in JSON mode.");
708
686
  }
709
- const selected = await clack4.select({
687
+ const selected = await clack6.select({
710
688
  message: "Select an organization:",
711
689
  options: orgs.map((o) => ({
712
690
  value: o.id,
713
691
  label: o.name
714
692
  }))
715
693
  });
716
- if (clack4.isCancel(selected)) process.exit(0);
694
+ if (clack6.isCancel(selected)) process.exit(0);
717
695
  orgId = selected;
718
696
  }
719
697
  const config = getGlobalConfig();
@@ -727,14 +705,14 @@ function registerProjectLinkCommand(program2) {
727
705
  if (json) {
728
706
  throw new CLIError("Specify --project-id in JSON mode.");
729
707
  }
730
- const selected = await clack4.select({
708
+ const selected = await clack6.select({
731
709
  message: "Select a project to link:",
732
710
  options: projects.map((p) => ({
733
711
  value: p.id,
734
712
  label: `${p.name} (${p.region}, ${p.status})`
735
713
  }))
736
714
  });
737
- if (clack4.isCancel(selected)) process.exit(0);
715
+ if (clack6.isCancel(selected)) process.exit(0);
738
716
  projectId = selected;
739
717
  }
740
718
  const [project, apiKey] = await Promise.all([
@@ -796,7 +774,7 @@ function registerDbCommands(dbCmd2) {
796
774
  dbCmd2.command("query <sql>").description("Execute a SQL query against the database").option("--unrestricted", "Use unrestricted mode (allows system table access)").action(async (sql, opts, cmd) => {
797
775
  const { json } = getRootOpts(cmd);
798
776
  try {
799
- requireAuth();
777
+ await requireAuth();
800
778
  const endpoint = opts.unrestricted ? "/api/database/advance/rawsql/unrestricted" : "/api/database/advance/rawsql";
801
779
  const res = await ossFetch(endpoint, {
802
780
  method: "POST",
@@ -832,7 +810,7 @@ function registerDbTablesCommand(dbCmd2) {
832
810
  dbCmd2.command("tables").description("List all database tables").action(async (_opts, cmd) => {
833
811
  const { json } = getRootOpts(cmd);
834
812
  try {
835
- requireAuth();
813
+ await requireAuth();
836
814
  const res = await ossFetch("/api/database/tables");
837
815
  const tables = await res.json();
838
816
  if (json) {
@@ -871,7 +849,7 @@ function registerDbFunctionsCommand(dbCmd2) {
871
849
  dbCmd2.command("functions").description("List all database functions").action(async (_opts, cmd) => {
872
850
  const { json } = getRootOpts(cmd);
873
851
  try {
874
- requireAuth();
852
+ await requireAuth();
875
853
  const res = await ossFetch("/api/database/functions");
876
854
  const raw = await res.json();
877
855
  const functions = extractArray(raw);
@@ -911,7 +889,7 @@ function registerDbIndexesCommand(dbCmd2) {
911
889
  dbCmd2.command("indexes").description("List all database indexes").action(async (_opts, cmd) => {
912
890
  const { json } = getRootOpts(cmd);
913
891
  try {
914
- requireAuth();
892
+ await requireAuth();
915
893
  const res = await ossFetch("/api/database/indexes");
916
894
  const raw = await res.json();
917
895
  const indexes = extractArray2(raw);
@@ -957,7 +935,7 @@ function registerDbPoliciesCommand(dbCmd2) {
957
935
  dbCmd2.command("policies").description("List all RLS policies").action(async (_opts, cmd) => {
958
936
  const { json } = getRootOpts(cmd);
959
937
  try {
960
- requireAuth();
938
+ await requireAuth();
961
939
  const res = await ossFetch("/api/database/policies");
962
940
  const raw = await res.json();
963
941
  const policies = extractArray3(raw);
@@ -1004,7 +982,7 @@ function registerDbTriggersCommand(dbCmd2) {
1004
982
  dbCmd2.command("triggers").description("List all database triggers").action(async (_opts, cmd) => {
1005
983
  const { json } = getRootOpts(cmd);
1006
984
  try {
1007
- requireAuth();
985
+ await requireAuth();
1008
986
  const res = await ossFetch("/api/database/triggers");
1009
987
  const raw = await res.json();
1010
988
  const triggers = extractArray4(raw);
@@ -1038,7 +1016,7 @@ function registerDbRpcCommand(dbCmd2) {
1038
1016
  dbCmd2.command("rpc <functionName>").description("Call a database function via RPC").option("--data <json>", "JSON body to pass as function parameters").action(async (functionName, opts, cmd) => {
1039
1017
  const { json } = getRootOpts(cmd);
1040
1018
  try {
1041
- requireAuth();
1019
+ await requireAuth();
1042
1020
  const body = opts.data ? JSON.stringify(JSON.parse(opts.data)) : void 0;
1043
1021
  const res = await ossFetch(`/api/database/rpc/${encodeURIComponent(functionName)}`, {
1044
1022
  method: body ? "POST" : "GET",
@@ -1062,7 +1040,7 @@ function registerDbExportCommand(dbCmd2) {
1062
1040
  dbCmd2.command("export").description("Export database schema and/or data").option("--format <format>", "Export format: sql or json", "sql").option("--tables <tables>", "Comma-separated list of tables to export (default: all)").option("--no-data", "Exclude table data (schema only)").option("--include-functions", "Include database functions").option("--include-sequences", "Include sequences").option("--include-views", "Include views").option("--row-limit <n>", "Maximum rows per table").option("-o, --output <file>", "Output file path (default: stdout)").action(async (opts, cmd) => {
1063
1041
  const { json } = getRootOpts(cmd);
1064
1042
  try {
1065
- requireAuth();
1043
+ await requireAuth();
1066
1044
  const body = {
1067
1045
  format: opts.format,
1068
1046
  includeData: opts.data !== false
@@ -1117,7 +1095,7 @@ function registerDbImportCommand(dbCmd2) {
1117
1095
  dbCmd2.command("import <file>").description("Import database from a local SQL file").option("--truncate", "Truncate existing tables before import").action(async (file, opts, cmd) => {
1118
1096
  const { json } = getRootOpts(cmd);
1119
1097
  try {
1120
- requireAuth();
1098
+ await requireAuth();
1121
1099
  const config = getProjectConfig();
1122
1100
  if (!config) throw new ProjectNotLinkedError();
1123
1101
  const fileContent = readFileSync2(file);
@@ -1155,7 +1133,7 @@ function registerRecordsCommands(recordsCmd2) {
1155
1133
  recordsCmd2.command("list <table>").description("List records from a table").option("--select <columns>", "Columns to select (comma-separated)").option("--filter <filter>", 'Filter expression (e.g. "name=eq.John")').option("--order <order>", 'Order by (e.g. "created_at.desc")').option("--limit <n>", "Limit number of records", parseInt).option("--offset <n>", "Offset for pagination", parseInt).action(async (table, opts, cmd) => {
1156
1134
  const { json } = getRootOpts(cmd);
1157
1135
  try {
1158
- requireAuth();
1136
+ await requireAuth();
1159
1137
  const params = new URLSearchParams();
1160
1138
  if (opts.select) params.set("select", opts.select);
1161
1139
  if (opts.filter) params.set(opts.filter.split("=")[0], opts.filter.split("=").slice(1).join("="));
@@ -1197,7 +1175,7 @@ function registerRecordsCreateCommand(recordsCmd2) {
1197
1175
  recordsCmd2.command("create <table>").description("Create record(s) in a table").option("--data <json>", "JSON data to insert (object or array of objects)").action(async (table, opts, cmd) => {
1198
1176
  const { json } = getRootOpts(cmd);
1199
1177
  try {
1200
- requireAuth();
1178
+ await requireAuth();
1201
1179
  if (!opts.data) {
1202
1180
  throw new CLIError(`--data is required. Example: --data '{"name":"John"}'`);
1203
1181
  }
@@ -1233,7 +1211,7 @@ function registerRecordsUpdateCommand(recordsCmd2) {
1233
1211
  recordsCmd2.command("update <table>").description("Update records in a table matching a filter").option("--filter <filter>", 'Filter expression (e.g. "id=eq.123")').option("--data <json>", "JSON data to update").action(async (table, opts, cmd) => {
1234
1212
  const { json } = getRootOpts(cmd);
1235
1213
  try {
1236
- requireAuth();
1214
+ await requireAuth();
1237
1215
  if (!opts.filter) {
1238
1216
  throw new CLIError("--filter is required to prevent accidental updates to all rows.");
1239
1217
  }
@@ -1274,7 +1252,7 @@ function registerRecordsDeleteCommand(recordsCmd2) {
1274
1252
  recordsCmd2.command("delete <table>").description("Delete records from a table matching a filter").option("--filter <filter>", 'Filter expression (e.g. "id=eq.123")').action(async (table, opts, cmd) => {
1275
1253
  const { json } = getRootOpts(cmd);
1276
1254
  try {
1277
- requireAuth();
1255
+ await requireAuth();
1278
1256
  if (!opts.filter) {
1279
1257
  throw new CLIError("--filter is required to prevent accidental deletion of all rows.");
1280
1258
  }
@@ -1303,7 +1281,7 @@ function registerFunctionsCommands(functionsCmd2) {
1303
1281
  functionsCmd2.command("list").description("List all edge functions").action(async (_opts, cmd) => {
1304
1282
  const { json } = getRootOpts(cmd);
1305
1283
  try {
1306
- requireAuth();
1284
+ await requireAuth();
1307
1285
  const res = await ossFetch("/api/functions");
1308
1286
  const data = await res.json();
1309
1287
  const functions = data.functions ?? [];
@@ -1337,7 +1315,7 @@ function registerFunctionsDeployCommand(functionsCmd2) {
1337
1315
  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) => {
1338
1316
  const { json } = getRootOpts(cmd);
1339
1317
  try {
1340
- requireAuth();
1318
+ await requireAuth();
1341
1319
  const filePath = opts.file ?? join2(process.cwd(), "insforge", "functions", slug, "index.ts");
1342
1320
  if (!existsSync2(filePath)) {
1343
1321
  throw new CLIError(
@@ -1382,7 +1360,7 @@ function registerFunctionsInvokeCommand(functionsCmd2) {
1382
1360
  functionsCmd2.command("invoke <slug>").description("Invoke an edge function").option("--data <json>", "JSON body to send to the function").option("--method <method>", "HTTP method (GET, POST, PUT, PATCH, DELETE)", "POST").action(async (slug, opts, cmd) => {
1383
1361
  const { json } = getRootOpts(cmd);
1384
1362
  try {
1385
- requireAuth();
1363
+ await requireAuth();
1386
1364
  const config = getProjectConfig();
1387
1365
  if (!config) throw new ProjectNotLinkedError();
1388
1366
  const method = opts.method.toUpperCase();
@@ -1431,7 +1409,7 @@ function registerFunctionsCodeCommand(functionsCmd2) {
1431
1409
  functionsCmd2.command("code <slug>").description("Fetch and display the source code of an edge function").action(async (slug, _opts, cmd) => {
1432
1410
  const { json } = getRootOpts(cmd);
1433
1411
  try {
1434
- requireAuth();
1412
+ await requireAuth();
1435
1413
  const res = await ossFetch(`/api/functions/${encodeURIComponent(slug)}`);
1436
1414
  const fn = await res.json();
1437
1415
  if (json) {
@@ -1455,7 +1433,7 @@ function registerStorageBucketsCommand(storageCmd2) {
1455
1433
  storageCmd2.command("buckets").description("List all storage buckets").action(async (_opts, cmd) => {
1456
1434
  const { json } = getRootOpts(cmd);
1457
1435
  try {
1458
- requireAuth();
1436
+ await requireAuth();
1459
1437
  const res = await ossFetch("/api/storage/buckets");
1460
1438
  const raw = await res.json();
1461
1439
  let buckets;
@@ -1492,7 +1470,7 @@ function registerStorageUploadCommand(storageCmd2) {
1492
1470
  storageCmd2.command("upload <file>").description("Upload a file to a storage bucket").requiredOption("--bucket <name>", "Target bucket name").option("--key <objectKey>", "Object key (defaults to filename)").action(async (file, opts, cmd) => {
1493
1471
  const { json } = getRootOpts(cmd);
1494
1472
  try {
1495
- requireAuth();
1473
+ await requireAuth();
1496
1474
  const config = getProjectConfig();
1497
1475
  if (!config) throw new ProjectNotLinkedError();
1498
1476
  if (!existsSync3(file)) {
@@ -1535,7 +1513,7 @@ function registerStorageDownloadCommand(storageCmd2) {
1535
1513
  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) => {
1536
1514
  const { json } = getRootOpts(cmd);
1537
1515
  try {
1538
- requireAuth();
1516
+ await requireAuth();
1539
1517
  const config = getProjectConfig();
1540
1518
  if (!config) throw new ProjectNotLinkedError();
1541
1519
  const bucketName = opts.bucket;
@@ -1568,7 +1546,7 @@ function registerStorageCreateBucketCommand(storageCmd2) {
1568
1546
  storageCmd2.command("create-bucket <name>").description("Create a new storage bucket").option("--public", "Make the bucket publicly accessible (default)").option("--private", "Make the bucket private").action(async (name, opts, cmd) => {
1569
1547
  const { json } = getRootOpts(cmd);
1570
1548
  try {
1571
- requireAuth();
1549
+ await requireAuth();
1572
1550
  const isPublic = !opts.private;
1573
1551
  const res = await ossFetch("/api/storage/buckets", {
1574
1552
  method: "POST",
@@ -1587,17 +1565,17 @@ function registerStorageCreateBucketCommand(storageCmd2) {
1587
1565
  }
1588
1566
 
1589
1567
  // src/commands/storage/delete-bucket.ts
1590
- import * as clack5 from "@clack/prompts";
1568
+ import * as clack7 from "@clack/prompts";
1591
1569
  function registerStorageDeleteBucketCommand(storageCmd2) {
1592
1570
  storageCmd2.command("delete-bucket <name>").description("Delete a storage bucket and all its objects").action(async (name, _opts, cmd) => {
1593
1571
  const { json, yes } = getRootOpts(cmd);
1594
1572
  try {
1595
- requireAuth();
1573
+ await requireAuth();
1596
1574
  if (!yes && !json) {
1597
- const confirm5 = await clack5.confirm({
1575
+ const confirm6 = await clack7.confirm({
1598
1576
  message: `Delete bucket "${name}" and all its objects? This cannot be undone.`
1599
1577
  });
1600
- if (!confirm5 || clack5.isCancel(confirm5)) {
1578
+ if (!confirm6 || clack7.isCancel(confirm6)) {
1601
1579
  process.exit(0);
1602
1580
  }
1603
1581
  }
@@ -1626,7 +1604,7 @@ function registerStorageListObjectsCommand(storageCmd2) {
1626
1604
  storageCmd2.command("list-objects <bucket>").description("List objects in a storage bucket").option("--limit <n>", "Maximum number of objects to return", "100").option("--offset <n>", "Number of objects to skip", "0").option("--prefix <prefix>", "Filter objects by key prefix").option("--search <term>", "Search objects by key (partial match)").option("--sort <field>", "Sort by field: key, size, uploadedAt (default: key)").action(async (bucket, opts, cmd) => {
1627
1605
  const { json } = getRootOpts(cmd);
1628
1606
  try {
1629
- requireAuth();
1607
+ await requireAuth();
1630
1608
  const params = new URLSearchParams();
1631
1609
  params.set("limit", opts.limit);
1632
1610
  params.set("offset", opts.offset);
@@ -1675,9 +1653,166 @@ function registerStorageListObjectsCommand(storageCmd2) {
1675
1653
  import { exec as exec2 } from "child_process";
1676
1654
  import { tmpdir } from "os";
1677
1655
  import { promisify as promisify2 } from "util";
1678
- import * as fs from "fs/promises";
1656
+ import * as fs2 from "fs/promises";
1657
+ import * as path2 from "path";
1658
+ import * as clack9 from "@clack/prompts";
1659
+
1660
+ // src/commands/deployments/deploy.ts
1679
1661
  import * as path from "path";
1680
- import * as clack6 from "@clack/prompts";
1662
+ import * as fs from "fs/promises";
1663
+ import * as clack8 from "@clack/prompts";
1664
+ import archiver from "archiver";
1665
+ var POLL_INTERVAL_MS = 5e3;
1666
+ var POLL_TIMEOUT_MS = 12e4;
1667
+ var EXCLUDE_PATTERNS = [
1668
+ "node_modules",
1669
+ ".git",
1670
+ ".next",
1671
+ ".env",
1672
+ ".env.local",
1673
+ "dist",
1674
+ "build",
1675
+ ".DS_Store",
1676
+ ".insforge"
1677
+ ];
1678
+ function shouldExclude(name) {
1679
+ const normalized = name.replace(/\\/g, "/");
1680
+ for (const pattern of EXCLUDE_PATTERNS) {
1681
+ if (normalized === pattern || normalized.startsWith(pattern + "/") || normalized.endsWith("/" + pattern) || normalized.includes("/" + pattern + "/")) {
1682
+ return true;
1683
+ }
1684
+ }
1685
+ if (normalized.endsWith(".log")) return true;
1686
+ return false;
1687
+ }
1688
+ async function createZipBuffer(sourceDir) {
1689
+ return new Promise((resolve2, reject) => {
1690
+ const archive = archiver("zip", { zlib: { level: 9 } });
1691
+ const chunks = [];
1692
+ archive.on("data", (chunk) => chunks.push(chunk));
1693
+ archive.on("end", () => resolve2(Buffer.concat(chunks)));
1694
+ archive.on("error", (err) => reject(err));
1695
+ archive.directory(sourceDir, false, (entry) => {
1696
+ if (shouldExclude(entry.name)) return false;
1697
+ return entry;
1698
+ });
1699
+ void archive.finalize();
1700
+ });
1701
+ }
1702
+ async function deployProject(opts) {
1703
+ const { sourceDir, startBody = {}, spinner: s } = opts;
1704
+ s?.start("Creating deployment...");
1705
+ const createRes = await ossFetch("/api/deployments", { method: "POST" });
1706
+ const { id: deploymentId, uploadUrl, uploadFields } = await createRes.json();
1707
+ s?.message("Compressing source files...");
1708
+ const zipBuffer = await createZipBuffer(sourceDir);
1709
+ s?.message("Uploading...");
1710
+ const formData = new FormData();
1711
+ for (const [key, value] of Object.entries(uploadFields)) {
1712
+ formData.append(key, value);
1713
+ }
1714
+ formData.append(
1715
+ "file",
1716
+ new Blob([zipBuffer], { type: "application/zip" }),
1717
+ "deployment.zip"
1718
+ );
1719
+ const uploadRes = await fetch(uploadUrl, { method: "POST", body: formData });
1720
+ if (!uploadRes.ok) {
1721
+ const uploadErr = await uploadRes.text();
1722
+ throw new CLIError(`Failed to upload: ${uploadErr}`);
1723
+ }
1724
+ s?.message("Starting deployment...");
1725
+ const startRes = await ossFetch(`/api/deployments/${deploymentId}/start`, {
1726
+ method: "POST",
1727
+ body: JSON.stringify(startBody)
1728
+ });
1729
+ await startRes.json();
1730
+ s?.message("Building and deploying...");
1731
+ const startTime = Date.now();
1732
+ let deployment = null;
1733
+ while (Date.now() - startTime < POLL_TIMEOUT_MS) {
1734
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
1735
+ try {
1736
+ const statusRes = await ossFetch(`/api/deployments/${deploymentId}`);
1737
+ deployment = await statusRes.json();
1738
+ if (deployment.status === "ready" || deployment.status === "READY") {
1739
+ break;
1740
+ }
1741
+ if (deployment.status === "error" || deployment.status === "ERROR" || deployment.status === "canceled") {
1742
+ s?.stop("Deployment failed");
1743
+ throw new CLIError(deployment.error ?? `Deployment failed with status: ${deployment.status}`);
1744
+ }
1745
+ const elapsed = Math.round((Date.now() - startTime) / 1e3);
1746
+ s?.message(`Building and deploying... (${elapsed}s, status: ${deployment.status})`);
1747
+ } catch (err) {
1748
+ if (err instanceof CLIError) throw err;
1749
+ }
1750
+ }
1751
+ const isReady = deployment?.status === "ready" || deployment?.status === "READY";
1752
+ const liveUrl = isReady ? deployment?.deploymentUrl ?? deployment?.url ?? null : null;
1753
+ return { deploymentId, deployment, isReady, liveUrl };
1754
+ }
1755
+ function registerDeploymentsDeployCommand(deploymentsCmd2) {
1756
+ 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) => {
1757
+ const { json } = getRootOpts(cmd);
1758
+ try {
1759
+ await requireAuth();
1760
+ const config = getProjectConfig();
1761
+ if (!config) throw new ProjectNotLinkedError();
1762
+ const sourceDir = path.resolve(directory ?? ".");
1763
+ const stats = await fs.stat(sourceDir).catch(() => null);
1764
+ if (!stats?.isDirectory()) {
1765
+ throw new CLIError(`"${sourceDir}" is not a valid directory.`);
1766
+ }
1767
+ const s = !json ? clack8.spinner() : null;
1768
+ const startBody = {};
1769
+ if (opts.env) {
1770
+ try {
1771
+ const parsed = JSON.parse(opts.env);
1772
+ if (Array.isArray(parsed)) {
1773
+ startBody.envVars = parsed;
1774
+ } else {
1775
+ startBody.envVars = Object.entries(parsed).map(([key, value]) => ({ key, value }));
1776
+ }
1777
+ } catch {
1778
+ throw new CLIError("Invalid --env JSON.");
1779
+ }
1780
+ }
1781
+ if (opts.meta) {
1782
+ try {
1783
+ startBody.meta = JSON.parse(opts.meta);
1784
+ } catch {
1785
+ throw new CLIError("Invalid --meta JSON.");
1786
+ }
1787
+ }
1788
+ const result = await deployProject({ sourceDir, startBody, spinner: s });
1789
+ if (result.isReady) {
1790
+ s?.stop("Deployment complete");
1791
+ if (json) {
1792
+ outputJson(result.deployment);
1793
+ } else {
1794
+ if (result.liveUrl) {
1795
+ clack8.log.success(`Live at: ${result.liveUrl}`);
1796
+ }
1797
+ clack8.log.info(`Deployment ID: ${result.deploymentId}`);
1798
+ }
1799
+ } else {
1800
+ s?.stop("Deployment is still building");
1801
+ if (json) {
1802
+ outputJson({ id: result.deploymentId, status: result.deployment?.status ?? "building", timedOut: true });
1803
+ } else {
1804
+ clack8.log.info(`Deployment ID: ${result.deploymentId}`);
1805
+ clack8.log.warn("Deployment did not finish within 2 minutes.");
1806
+ clack8.log.info(`Check status with: insforge deployments status ${result.deploymentId}`);
1807
+ }
1808
+ }
1809
+ } catch (err) {
1810
+ handleError(err, json);
1811
+ }
1812
+ });
1813
+ }
1814
+
1815
+ // src/commands/create.ts
1681
1816
  var execAsync2 = promisify2(exec2);
1682
1817
  function buildOssHost2(appkey, region) {
1683
1818
  return `https://${appkey}.${region}.insforge.app`;
@@ -1692,15 +1827,15 @@ async function waitForProjectActive(projectId, apiUrl, timeoutMs = 12e4) {
1692
1827
  throw new CLIError("Project creation timed out. Check the dashboard for status.");
1693
1828
  }
1694
1829
  async function copyDir(src, dest) {
1695
- const entries = await fs.readdir(src, { withFileTypes: true });
1830
+ const entries = await fs2.readdir(src, { withFileTypes: true });
1696
1831
  for (const entry of entries) {
1697
- const srcPath = path.join(src, entry.name);
1698
- const destPath = path.join(dest, entry.name);
1832
+ const srcPath = path2.join(src, entry.name);
1833
+ const destPath = path2.join(dest, entry.name);
1699
1834
  if (entry.isDirectory()) {
1700
- await fs.mkdir(destPath, { recursive: true });
1835
+ await fs2.mkdir(destPath, { recursive: true });
1701
1836
  await copyDir(srcPath, destPath);
1702
1837
  } else {
1703
- await fs.copyFile(srcPath, destPath);
1838
+ await fs2.copyFile(srcPath, destPath);
1704
1839
  }
1705
1840
  }
1706
1841
  }
@@ -1708,9 +1843,9 @@ function registerCreateCommand(program2) {
1708
1843
  program2.command("create").description("Create a new InsForge project").option("--name <name>", "Project name").option("--org-id <id>", "Organization ID").option("--region <region>", "Deployment region (us-east, us-west, eu-central, ap-southeast)").option("--template <template>", "Template to use: react, nextjs, or empty").action(async (opts, cmd) => {
1709
1844
  const { json, apiUrl } = getRootOpts(cmd);
1710
1845
  try {
1711
- requireAuth();
1846
+ await requireAuth(apiUrl);
1712
1847
  if (!json) {
1713
- clack6.intro("Create a new InsForge project");
1848
+ clack9.intro("Create a new InsForge project");
1714
1849
  }
1715
1850
  let orgId = opts.orgId;
1716
1851
  if (!orgId) {
@@ -1721,14 +1856,14 @@ function registerCreateCommand(program2) {
1721
1856
  if (json) {
1722
1857
  throw new CLIError("Specify --org-id in JSON mode.");
1723
1858
  }
1724
- const selected = await clack6.select({
1859
+ const selected = await clack9.select({
1725
1860
  message: "Select an organization:",
1726
1861
  options: orgs.map((o) => ({
1727
1862
  value: o.id,
1728
1863
  label: o.name
1729
1864
  }))
1730
1865
  });
1731
- if (clack6.isCancel(selected)) process.exit(0);
1866
+ if (clack9.isCancel(selected)) process.exit(0);
1732
1867
  orgId = selected;
1733
1868
  }
1734
1869
  const globalConfig = getGlobalConfig();
@@ -1737,11 +1872,11 @@ function registerCreateCommand(program2) {
1737
1872
  let projectName = opts.name;
1738
1873
  if (!projectName) {
1739
1874
  if (json) throw new CLIError("--name is required in JSON mode.");
1740
- const name = await clack6.text({
1875
+ const name = await clack9.text({
1741
1876
  message: "Project name:",
1742
1877
  validate: (v) => v.length >= 2 ? void 0 : "Name must be at least 2 characters"
1743
1878
  });
1744
- if (clack6.isCancel(name)) process.exit(0);
1879
+ if (clack9.isCancel(name)) process.exit(0);
1745
1880
  projectName = name;
1746
1881
  }
1747
1882
  let template = opts.template;
@@ -1749,7 +1884,7 @@ function registerCreateCommand(program2) {
1749
1884
  if (json) {
1750
1885
  template = "empty";
1751
1886
  } else {
1752
- const selected = await clack6.select({
1887
+ const selected = await clack9.select({
1753
1888
  message: "Choose a starter template:",
1754
1889
  options: [
1755
1890
  { value: "react", label: "Web app template with React" },
@@ -1757,11 +1892,11 @@ function registerCreateCommand(program2) {
1757
1892
  { value: "empty", label: "Empty project" }
1758
1893
  ]
1759
1894
  });
1760
- if (clack6.isCancel(selected)) process.exit(0);
1895
+ if (clack9.isCancel(selected)) process.exit(0);
1761
1896
  template = selected;
1762
1897
  }
1763
1898
  }
1764
- const s = !json ? clack6.spinner() : null;
1899
+ const s = !json ? clack9.spinner() : null;
1765
1900
  s?.start("Creating project...");
1766
1901
  const project = await createProject(orgId, projectName, opts.region, apiUrl);
1767
1902
  s?.message("Waiting for project to become active...");
@@ -1778,18 +1913,68 @@ function registerCreateCommand(program2) {
1778
1913
  };
1779
1914
  saveProjectConfig(projectConfig);
1780
1915
  s?.stop(`Project "${project.name}" created and linked`);
1781
- if (template !== "empty") {
1916
+ const hasTemplate = template !== "empty";
1917
+ if (hasTemplate) {
1782
1918
  await downloadTemplate(template, projectConfig, projectName, json, apiUrl);
1783
1919
  }
1784
1920
  await installSkills(json);
1921
+ if (hasTemplate) {
1922
+ const installSpinner = !json ? clack9.spinner() : null;
1923
+ installSpinner?.start("Installing dependencies...");
1924
+ try {
1925
+ await execAsync2("npm install", { cwd: process.cwd(), maxBuffer: 10 * 1024 * 1024 });
1926
+ installSpinner?.stop("Dependencies installed");
1927
+ } catch (err) {
1928
+ installSpinner?.stop("Failed to install dependencies");
1929
+ if (!json) {
1930
+ clack9.log.warn(`npm install failed: ${err.message}`);
1931
+ clack9.log.info("Run `npm install` manually to install dependencies.");
1932
+ }
1933
+ }
1934
+ }
1935
+ let liveUrl = null;
1936
+ if (hasTemplate && !json) {
1937
+ const shouldDeploy = await clack9.confirm({
1938
+ message: "Would you like to deploy now?"
1939
+ });
1940
+ if (!clack9.isCancel(shouldDeploy) && shouldDeploy) {
1941
+ try {
1942
+ const deploySpinner = clack9.spinner();
1943
+ const result = await deployProject({
1944
+ sourceDir: process.cwd(),
1945
+ spinner: deploySpinner
1946
+ });
1947
+ if (result.isReady) {
1948
+ deploySpinner.stop("Deployment complete");
1949
+ liveUrl = result.liveUrl;
1950
+ } else {
1951
+ deploySpinner.stop("Deployment is still building");
1952
+ clack9.log.info(`Deployment ID: ${result.deploymentId}`);
1953
+ clack9.log.warn("Deployment did not finish within 2 minutes.");
1954
+ clack9.log.info(`Check status with: insforge deployments status ${result.deploymentId}`);
1955
+ }
1956
+ } catch (err) {
1957
+ clack9.log.warn(`Deploy failed: ${err.message}`);
1958
+ }
1959
+ }
1960
+ }
1961
+ const dashboardUrl = `${getFrontendUrl()}/dashboard/project/${project.id}`;
1785
1962
  if (json) {
1786
1963
  outputJson({
1787
1964
  success: true,
1788
1965
  project: { id: project.id, name: project.name, appkey: project.appkey, region: project.region },
1789
- template
1966
+ template,
1967
+ urls: {
1968
+ dashboard: dashboardUrl,
1969
+ ...liveUrl ? { liveSite: liveUrl } : {}
1970
+ }
1790
1971
  });
1791
1972
  } else {
1792
- clack6.outro("Done! Run `npm install` to get started.");
1973
+ clack9.log.step(`Dashboard: ${dashboardUrl}`);
1974
+ if (liveUrl) {
1975
+ clack9.log.success(`Live site: ${liveUrl}`);
1976
+ }
1977
+ clack9.outro("Done!");
1793
1978
  }
1794
1979
  } catch (err) {
1795
1980
  handleError(err, json);
@@ -1797,7 +1982,7 @@ function registerCreateCommand(program2) {
1797
1982
  });
1798
1983
  }
1799
1984
  async function downloadTemplate(framework, projectConfig, projectName, json, _apiUrl) {
1800
- const s = !json ? clack6.spinner() : null;
1985
+ const s = !json ? clack9.spinner() : null;
1801
1986
  s?.start("Downloading template...");
1802
1987
  try {
1803
1988
  const anonKey = await getAnonKey();
@@ -1806,9 +1991,9 @@ async function downloadTemplate(framework, projectConfig, projectName, json, _ap
1806
1991
  }
1807
1992
  const tempDir = tmpdir();
1808
1993
  const targetDir = projectName;
1809
- const templatePath = path.join(tempDir, targetDir);
1994
+ const templatePath = path2.join(tempDir, targetDir);
1810
1995
  try {
1811
- await fs.rm(templatePath, { recursive: true, force: true });
1996
+ await fs2.rm(templatePath, { recursive: true, force: true });
1812
1997
  } catch {
1813
1998
  }
1814
1999
  const frame = framework === "nextjs" ? "nextjs" : "react";
@@ -1821,14 +2006,14 @@ async function downloadTemplate(framework, projectConfig, projectName, json, _ap
1821
2006
  s?.message("Copying template files...");
1822
2007
  const cwd = process.cwd();
1823
2008
  await copyDir(templatePath, cwd);
1824
- await fs.rm(templatePath, { recursive: true, force: true }).catch(() => {
2009
+ await fs2.rm(templatePath, { recursive: true, force: true }).catch(() => {
1825
2010
  });
1826
2011
  s?.stop("Template files downloaded");
1827
2012
  } catch (err) {
1828
2013
  s?.stop("Template download failed");
1829
2014
  if (!json) {
1830
- clack6.log.warn(`Failed to download template: ${err.message}`);
1831
- clack6.log.info("You can manually set up the template later.");
2015
+ clack9.log.warn(`Failed to download template: ${err.message}`);
2016
+ clack9.log.info("You can manually set up the template later.");
1832
2017
  }
1833
2018
  }
1834
2019
  }
@@ -1882,7 +2067,7 @@ function registerListCommand(program2) {
1882
2067
  program2.command("list").description("List all organizations and their projects").action(async (_opts, cmd) => {
1883
2068
  const { json, apiUrl } = getRootOpts(cmd);
1884
2069
  try {
1885
- requireAuth();
2070
+ await requireAuth(apiUrl);
1886
2071
  const orgs = await listOrganizations(apiUrl);
1887
2072
  if (orgs.length === 0) {
1888
2073
  if (json) {
@@ -1939,162 +2124,12 @@ function registerListCommand(program2) {
1939
2124
  });
1940
2125
  }
1941
2126
 
1942
- // src/commands/deployments/deploy.ts
1943
- import * as path2 from "path";
1944
- import * as fs2 from "fs/promises";
1945
- import * as clack7 from "@clack/prompts";
1946
- import archiver from "archiver";
1947
- var POLL_INTERVAL_MS = 5e3;
1948
- var POLL_TIMEOUT_MS = 12e4;
1949
- var EXCLUDE_PATTERNS = [
1950
- "node_modules",
1951
- ".git",
1952
- ".next",
1953
- ".env",
1954
- ".env.local",
1955
- "dist",
1956
- "build",
1957
- ".DS_Store",
1958
- ".insforge"
1959
- ];
1960
- function shouldExclude(name) {
1961
- const normalized = name.replace(/\\/g, "/");
1962
- for (const pattern of EXCLUDE_PATTERNS) {
1963
- if (normalized === pattern || normalized.startsWith(pattern + "/") || normalized.endsWith("/" + pattern) || normalized.includes("/" + pattern + "/")) {
1964
- return true;
1965
- }
1966
- }
1967
- if (normalized.endsWith(".log")) return true;
1968
- return false;
1969
- }
1970
- async function createZipBuffer(sourceDir) {
1971
- return new Promise((resolve2, reject) => {
1972
- const archive = archiver("zip", { zlib: { level: 9 } });
1973
- const chunks = [];
1974
- archive.on("data", (chunk) => chunks.push(chunk));
1975
- archive.on("end", () => resolve2(Buffer.concat(chunks)));
1976
- archive.on("error", (err) => reject(err));
1977
- archive.directory(sourceDir, false, (entry) => {
1978
- if (shouldExclude(entry.name)) return false;
1979
- return entry;
1980
- });
1981
- void archive.finalize();
1982
- });
1983
- }
1984
- function registerDeploymentsDeployCommand(deploymentsCmd2) {
1985
- 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) => {
1986
- const { json } = getRootOpts(cmd);
1987
- try {
1988
- requireAuth();
1989
- const config = getProjectConfig();
1990
- if (!config) throw new ProjectNotLinkedError();
1991
- const sourceDir = path2.resolve(directory ?? ".");
1992
- const stats = await fs2.stat(sourceDir).catch(() => null);
1993
- if (!stats?.isDirectory()) {
1994
- throw new CLIError(`"${sourceDir}" is not a valid directory.`);
1995
- }
1996
- const s = !json ? clack7.spinner() : null;
1997
- s?.start("Creating deployment...");
1998
- const createRes = await ossFetch("/api/deployments", { method: "POST" });
1999
- const { id: deploymentId, uploadUrl, uploadFields } = await createRes.json();
2000
- s?.message("Compressing source files...");
2001
- const zipBuffer = await createZipBuffer(sourceDir);
2002
- s?.message("Uploading...");
2003
- const formData = new FormData();
2004
- for (const [key, value] of Object.entries(uploadFields)) {
2005
- formData.append(key, value);
2006
- }
2007
- formData.append(
2008
- "file",
2009
- new Blob([zipBuffer], { type: "application/zip" }),
2010
- "deployment.zip"
2011
- );
2012
- const uploadRes = await fetch(uploadUrl, { method: "POST", body: formData });
2013
- if (!uploadRes.ok) {
2014
- const uploadErr = await uploadRes.text();
2015
- throw new CLIError(`Failed to upload: ${uploadErr}`);
2016
- }
2017
- s?.message("Starting deployment...");
2018
- const startBody = {};
2019
- if (opts.env) {
2020
- try {
2021
- const parsed = JSON.parse(opts.env);
2022
- if (Array.isArray(parsed)) {
2023
- startBody.envVars = parsed;
2024
- } else {
2025
- startBody.envVars = Object.entries(parsed).map(([key, value]) => ({ key, value }));
2026
- }
2027
- } catch {
2028
- throw new CLIError("Invalid --env JSON.");
2029
- }
2030
- }
2031
- if (opts.meta) {
2032
- try {
2033
- startBody.meta = JSON.parse(opts.meta);
2034
- } catch {
2035
- throw new CLIError("Invalid --meta JSON.");
2036
- }
2037
- }
2038
- const startRes = await ossFetch(`/api/deployments/${deploymentId}/start`, {
2039
- method: "POST",
2040
- body: JSON.stringify(startBody)
2041
- });
2042
- await startRes.json();
2043
- s?.message("Building and deploying...");
2044
- const startTime = Date.now();
2045
- let deployment = null;
2046
- while (Date.now() - startTime < POLL_TIMEOUT_MS) {
2047
- await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
2048
- try {
2049
- const statusRes = await ossFetch(`/api/deployments/${deploymentId}`);
2050
- deployment = await statusRes.json();
2051
- if (deployment.status === "ready" || deployment.status === "READY") {
2052
- break;
2053
- }
2054
- if (deployment.status === "error" || deployment.status === "ERROR" || deployment.status === "canceled") {
2055
- s?.stop("Deployment failed");
2056
- throw new CLIError(deployment.error ?? `Deployment failed with status: ${deployment.status}`);
2057
- }
2058
- const elapsed = Math.round((Date.now() - startTime) / 1e3);
2059
- s?.message(`Building and deploying... (${elapsed}s, status: ${deployment.status})`);
2060
- } catch (err) {
2061
- if (err instanceof CLIError) throw err;
2062
- }
2063
- }
2064
- const isReady = deployment?.status === "ready" || deployment?.status === "READY";
2065
- if (isReady) {
2066
- s?.stop("Deployment complete");
2067
- if (json) {
2068
- outputJson(deployment);
2069
- } else {
2070
- const liveUrl = deployment?.deploymentUrl ?? deployment?.url;
2071
- if (liveUrl) {
2072
- clack7.log.success(`Live at: ${liveUrl}`);
2073
- }
2074
- clack7.log.info(`Deployment ID: ${deploymentId}`);
2075
- }
2076
- } else {
2077
- s?.stop("Deployment is still building");
2078
- if (json) {
2079
- outputJson({ id: deploymentId, status: deployment?.status ?? "building", timedOut: true });
2080
- } else {
2081
- clack7.log.info(`Deployment ID: ${deploymentId}`);
2082
- clack7.log.warn("Deployment did not finish within 2 minutes.");
2083
- clack7.log.info(`Check status with: insforge deployments status ${deploymentId}`);
2084
- }
2085
- }
2086
- } catch (err) {
2087
- handleError(err, json);
2088
- }
2089
- });
2090
- }
2091
-
2092
2127
  // src/commands/deployments/list.ts
2093
2128
  function registerDeploymentsListCommand(deploymentsCmd2) {
2094
2129
  deploymentsCmd2.command("list").description("List all deployments").option("--limit <n>", "Limit number of results", "20").option("--offset <n>", "Offset for pagination", "0").action(async (opts, cmd) => {
2095
2130
  const { json } = getRootOpts(cmd);
2096
2131
  try {
2097
- requireAuth();
2132
+ await requireAuth();
2098
2133
  if (!getProjectConfig()) throw new ProjectNotLinkedError();
2099
2134
  const res = await ossFetch(`/api/deployments?limit=${opts.limit}&offset=${opts.offset}`);
2100
2135
  const raw = await res.json();
@@ -2135,7 +2170,7 @@ function registerDeploymentsStatusCommand(deploymentsCmd2) {
2135
2170
  deploymentsCmd2.command("status <id>").description("Get deployment details and sync status from Vercel").option("--sync", "Sync status from Vercel before showing").action(async (id, opts, cmd) => {
2136
2171
  const { json } = getRootOpts(cmd);
2137
2172
  try {
2138
- requireAuth();
2173
+ await requireAuth();
2139
2174
  if (!getProjectConfig()) throw new ProjectNotLinkedError();
2140
2175
  if (opts.sync) {
2141
2176
  await ossFetch(`/api/deployments/${id}/sync`, { method: "POST" });
@@ -2166,18 +2201,18 @@ function registerDeploymentsStatusCommand(deploymentsCmd2) {
2166
2201
  }
2167
2202
 
2168
2203
  // src/commands/deployments/cancel.ts
2169
- import * as clack8 from "@clack/prompts";
2204
+ import * as clack10 from "@clack/prompts";
2170
2205
  function registerDeploymentsCancelCommand(deploymentsCmd2) {
2171
2206
  deploymentsCmd2.command("cancel <id>").description("Cancel a deployment").action(async (id, _opts, cmd) => {
2172
2207
  const { json, yes } = getRootOpts(cmd);
2173
2208
  try {
2174
- requireAuth();
2209
+ await requireAuth();
2175
2210
  if (!getProjectConfig()) throw new ProjectNotLinkedError();
2176
2211
  if (!yes && !json) {
2177
- const confirmed = await clack8.confirm({
2212
+ const confirmed = await clack10.confirm({
2178
2213
  message: `Cancel deployment ${id}?`
2179
2214
  });
2180
- if (clack8.isCancel(confirmed) || !confirmed) process.exit(0);
2215
+ if (clack10.isCancel(confirmed) || !confirmed) process.exit(0);
2181
2216
  }
2182
2217
  const res = await ossFetch(`/api/deployments/${id}/cancel`, { method: "POST" });
2183
2218
  const result = await res.json();
@@ -2208,7 +2243,7 @@ Examples:
2208
2243
  insforge docs storage rest-api Show REST API storage docs`).action(async (feature, language, _opts, cmd) => {
2209
2244
  const { json } = getRootOpts(cmd);
2210
2245
  try {
2211
- requireAuth();
2246
+ await requireAuth();
2212
2247
  if (!feature) {
2213
2248
  await listDocs(json);
2214
2249
  return;
@@ -2264,7 +2299,7 @@ function registerSecretsListCommand(secretsCmd2) {
2264
2299
  secretsCmd2.command("list").description("List secrets (metadata only, values are hidden)").option("--all", "Include inactive (deleted) secrets").action(async (opts, cmd) => {
2265
2300
  const { json } = getRootOpts(cmd);
2266
2301
  try {
2267
- requireAuth();
2302
+ await requireAuth();
2268
2303
  const res = await ossFetch("/api/secrets");
2269
2304
  const data = await res.json();
2270
2305
  let secrets = data.secrets ?? [];
@@ -2304,7 +2339,7 @@ function registerSecretsGetCommand(secretsCmd2) {
2304
2339
  secretsCmd2.command("get <key>").description("Get the decrypted value of a secret").action(async (key, _opts, cmd) => {
2305
2340
  const { json } = getRootOpts(cmd);
2306
2341
  try {
2307
- requireAuth();
2342
+ await requireAuth();
2308
2343
  const res = await ossFetch(`/api/secrets/${encodeURIComponent(key)}`);
2309
2344
  const data = await res.json();
2310
2345
  if (json) {
@@ -2323,7 +2358,7 @@ function registerSecretsAddCommand(secretsCmd2) {
2323
2358
  secretsCmd2.command("add <key> <value>").description("Create a new secret").option("--reserved", "Mark secret as protected from deletion").option("--expires <date>", "Expiration date (ISO 8601 format)").action(async (key, value, opts, cmd) => {
2324
2359
  const { json } = getRootOpts(cmd);
2325
2360
  try {
2326
- requireAuth();
2361
+ await requireAuth();
2327
2362
  const body = { key, value };
2328
2363
  if (opts.reserved) body.isReserved = true;
2329
2364
  if (opts.expires) body.expiresAt = opts.expires;
@@ -2348,7 +2383,7 @@ function registerSecretsUpdateCommand(secretsCmd2) {
2348
2383
  secretsCmd2.command("update <key>").description("Update an existing secret").option("--value <value>", "New secret value").option("--active <bool>", "Set active status (true/false)").option("--reserved <bool>", "Set reserved status (true/false)").option("--expires <date>", 'Expiration date (ISO 8601, or "null" to remove)').action(async (key, opts, cmd) => {
2349
2384
  const { json } = getRootOpts(cmd);
2350
2385
  try {
2351
- requireAuth();
2386
+ await requireAuth();
2352
2387
  const body = {};
2353
2388
  if (opts.value !== void 0) body.value = opts.value;
2354
2389
  if (opts.active !== void 0) body.isActive = opts.active === "true";
@@ -2374,17 +2409,17 @@ function registerSecretsUpdateCommand(secretsCmd2) {
2374
2409
  }
2375
2410
 
2376
2411
  // src/commands/secrets/delete.ts
2377
- import * as clack9 from "@clack/prompts";
2412
+ import * as clack11 from "@clack/prompts";
2378
2413
  function registerSecretsDeleteCommand(secretsCmd2) {
2379
2414
  secretsCmd2.command("delete <key>").description("Delete a secret").action(async (key, _opts, cmd) => {
2380
2415
  const { json, yes } = getRootOpts(cmd);
2381
2416
  try {
2382
- requireAuth();
2417
+ await requireAuth();
2383
2418
  if (!yes && !json) {
2384
- const confirm5 = await clack9.confirm({
2419
+ const confirm6 = await clack11.confirm({
2385
2420
  message: `Delete secret "${key}"? This cannot be undone.`
2386
2421
  });
2387
- if (!confirm5 || clack9.isCancel(confirm5)) {
2422
+ if (!confirm6 || clack11.isCancel(confirm6)) {
2388
2423
  process.exit(0);
2389
2424
  }
2390
2425
  }
@@ -2408,7 +2443,7 @@ function registerSchedulesListCommand(schedulesCmd2) {
2408
2443
  schedulesCmd2.command("list").description("List all schedules").action(async (_opts, cmd) => {
2409
2444
  const { json } = getRootOpts(cmd);
2410
2445
  try {
2411
- requireAuth();
2446
+ await requireAuth();
2412
2447
  const res = await ossFetch("/api/schedules");
2413
2448
  const data = await res.json();
2414
2449
  const schedules = Array.isArray(data) ? data : data.schedules ?? [];
@@ -2443,7 +2478,7 @@ function registerSchedulesGetCommand(schedulesCmd2) {
2443
2478
  schedulesCmd2.command("get <id>").description("Get schedule details").action(async (id, _opts, cmd) => {
2444
2479
  const { json } = getRootOpts(cmd);
2445
2480
  try {
2446
- requireAuth();
2481
+ await requireAuth();
2447
2482
  const res = await ossFetch(`/api/schedules/${encodeURIComponent(id)}`);
2448
2483
  const data = await res.json();
2449
2484
  if (json) {
@@ -2473,7 +2508,7 @@ function registerSchedulesCreateCommand(schedulesCmd2) {
2473
2508
  schedulesCmd2.command("create").description("Create a new schedule").requiredOption("--name <name>", "Schedule name").requiredOption("--cron <expression>", "Cron expression (5-field format)").requiredOption("--url <url>", "URL to invoke").requiredOption("--method <method>", "HTTP method (GET, POST, PUT, PATCH, DELETE)").option("--headers <json>", "HTTP headers as JSON").option("--body <json>", "Request body as JSON").action(async (opts, cmd) => {
2474
2509
  const { json } = getRootOpts(cmd);
2475
2510
  try {
2476
- requireAuth();
2511
+ await requireAuth();
2477
2512
  const body = {
2478
2513
  name: opts.name,
2479
2514
  cronSchedule: opts.cron,
@@ -2515,7 +2550,7 @@ function registerSchedulesUpdateCommand(schedulesCmd2) {
2515
2550
  schedulesCmd2.command("update <id>").description("Update a schedule").option("--name <name>", "New schedule name").option("--cron <expression>", "New cron expression").option("--url <url>", "New URL to invoke").option("--method <method>", "New HTTP method").option("--headers <json>", "New HTTP headers as JSON").option("--body <json>", "New request body as JSON").option("--active <bool>", "Enable/disable schedule (true/false)").action(async (id, opts, cmd) => {
2516
2551
  const { json } = getRootOpts(cmd);
2517
2552
  try {
2518
- requireAuth();
2553
+ await requireAuth();
2519
2554
  const body = {};
2520
2555
  if (opts.name !== void 0) body.name = opts.name;
2521
2556
  if (opts.cron !== void 0) body.cronSchedule = opts.cron;
@@ -2556,17 +2591,17 @@ function registerSchedulesUpdateCommand(schedulesCmd2) {
2556
2591
  }
2557
2592
 
2558
2593
  // src/commands/schedules/delete.ts
2559
- import * as clack10 from "@clack/prompts";
2594
+ import * as clack12 from "@clack/prompts";
2560
2595
  function registerSchedulesDeleteCommand(schedulesCmd2) {
2561
2596
  schedulesCmd2.command("delete <id>").description("Delete a schedule").action(async (id, _opts, cmd) => {
2562
2597
  const { json, yes } = getRootOpts(cmd);
2563
2598
  try {
2564
- requireAuth();
2599
+ await requireAuth();
2565
2600
  if (!yes && !json) {
2566
- const confirm5 = await clack10.confirm({
2601
+ const confirm6 = await clack12.confirm({
2567
2602
  message: `Delete schedule "${id}"? This cannot be undone.`
2568
2603
  });
2569
- if (!confirm5 || clack10.isCancel(confirm5)) {
2604
+ if (!confirm6 || clack12.isCancel(confirm6)) {
2570
2605
  process.exit(0);
2571
2606
  }
2572
2607
  }
@@ -2590,7 +2625,7 @@ function registerSchedulesLogsCommand(schedulesCmd2) {
2590
2625
  schedulesCmd2.command("logs <id>").description("Get execution logs for a schedule").option("--limit <n>", "Max logs to return (default: 50, max: 100)", "50").option("--offset <n>", "Pagination offset", "0").action(async (id, opts, cmd) => {
2591
2626
  const { json } = getRootOpts(cmd);
2592
2627
  try {
2593
- requireAuth();
2628
+ await requireAuth();
2594
2629
  const limit = parseInt(opts.limit, 10) || 50;
2595
2630
  const offset = parseInt(opts.offset, 10) || 0;
2596
2631
  const res = await ossFetch(`/api/schedules/${encodeURIComponent(id)}/logs?limit=${limit}&offset=${offset}`);
@@ -2630,7 +2665,7 @@ function registerLogsCommand(program2) {
2630
2665
  program2.command("logs <source>").description("Fetch backend container logs (insforge.logs | postgREST.logs | postgres.logs | function.logs)").option("--limit <n>", "Number of log entries to return", "20").action(async (source, opts, cmd) => {
2631
2666
  const { json } = getRootOpts(cmd);
2632
2667
  try {
2633
- requireAuth();
2668
+ await requireAuth();
2634
2669
  const resolved = SOURCE_LOOKUP.get(source.toLowerCase());
2635
2670
  if (!resolved) {
2636
2671
  console.error(`Invalid log source "${source}". Valid sources: ${VALID_SOURCES.join(", ")}`);