@insforge/cli 0.1.4 → 0.1.6

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";
@@ -135,6 +135,7 @@ function getRootOpts(cmd) {
135
135
  import { createServer } from "http";
136
136
  import { randomBytes, createHash } from "crypto";
137
137
  import { URL } from "url";
138
+ import * as clack from "@clack/prompts";
138
139
  var DEFAULT_CLIENT_ID = "clf_NK8cMUs41gm8ZcfdtSguVw";
139
140
  var OAUTH_SCOPES = "user:read organizations:read projects:read projects:write";
140
141
  function generatePKCE() {
@@ -242,14 +243,77 @@ function startCallbackServer() {
242
243
  }, 5 * 60 * 1e3).unref();
243
244
  });
244
245
  }
246
+ async function performOAuthLogin(apiUrl) {
247
+ const platformUrl = getPlatformApiUrl(apiUrl);
248
+ const config = getGlobalConfig();
249
+ const clientId = config.oauth_client_id ?? DEFAULT_CLIENT_ID;
250
+ const pkce = generatePKCE();
251
+ const state = generateState();
252
+ const { port, result, close } = await startCallbackServer();
253
+ const redirectUri = `http://127.0.0.1:${port}/callback`;
254
+ const authUrl = buildAuthorizeUrl({
255
+ platformUrl,
256
+ clientId,
257
+ redirectUri,
258
+ codeChallenge: pkce.code_challenge,
259
+ state,
260
+ scopes: OAUTH_SCOPES
261
+ });
262
+ clack.log.info("Opening browser for authentication...");
263
+ clack.log.info(`If browser doesn't open, visit:
264
+ ${authUrl}`);
265
+ try {
266
+ const open = (await import("open")).default;
267
+ await open(authUrl);
268
+ } catch {
269
+ clack.log.warn("Could not open browser. Please visit the URL above.");
270
+ }
271
+ const s = clack.spinner();
272
+ s.start("Waiting for authentication...");
273
+ try {
274
+ const callbackResult = await result;
275
+ close();
276
+ if (callbackResult.state !== state) {
277
+ s.stop("Authentication failed");
278
+ throw new Error("State mismatch. Possible CSRF attack.");
279
+ }
280
+ s.message("Exchanging authorization code...");
281
+ const tokens = await exchangeCodeForTokens({
282
+ platformUrl,
283
+ code: callbackResult.code,
284
+ redirectUri,
285
+ clientId,
286
+ codeVerifier: pkce.code_verifier
287
+ });
288
+ const creds = {
289
+ access_token: tokens.access_token,
290
+ refresh_token: tokens.refresh_token,
291
+ user: { id: "", name: "", email: "", avatar_url: null, email_verified: true }
292
+ };
293
+ saveCredentials(creds);
294
+ try {
295
+ const profile = await getProfile(apiUrl);
296
+ creds.user = profile;
297
+ saveCredentials(creds);
298
+ s.stop(`Authenticated as ${profile.email}`);
299
+ } catch {
300
+ s.stop("Authenticated successfully");
301
+ }
302
+ return creds;
303
+ } catch (err) {
304
+ close();
305
+ s.stop("Authentication failed");
306
+ throw err;
307
+ }
308
+ }
245
309
 
246
310
  // src/lib/credentials.ts
247
- function requireAuth() {
311
+ import * as clack2 from "@clack/prompts";
312
+ async function requireAuth(apiUrl) {
248
313
  const creds = getCredentials();
249
- if (!creds || !creds.access_token) {
250
- throw new AuthError();
251
- }
252
- return creds;
314
+ if (creds && creds.access_token) return creds;
315
+ clack2.log.info("You need to log in to continue.");
316
+ return await performOAuthLogin(apiUrl);
253
317
  }
254
318
  async function refreshAccessToken(apiUrl) {
255
319
  const creds = getCredentials();
@@ -371,7 +435,7 @@ function registerLoginCommand(program2) {
371
435
  if (opts.email) {
372
436
  await loginWithEmail(json, apiUrl);
373
437
  } else {
374
- await loginWithOAuth(json, apiUrl, opts.clientId);
438
+ await loginWithOAuth(json, apiUrl);
375
439
  }
376
440
  } catch (err) {
377
441
  if (err instanceof Error && err.message.includes("cancelled")) {
@@ -383,28 +447,28 @@ function registerLoginCommand(program2) {
383
447
  }
384
448
  async function loginWithEmail(json, apiUrl) {
385
449
  if (!json) {
386
- clack.intro("InsForge CLI");
450
+ clack3.intro("InsForge CLI");
387
451
  }
388
- const email = json ? process.env.INSFORGE_EMAIL : await clack.text({
452
+ const email = json ? process.env.INSFORGE_EMAIL : await clack3.text({
389
453
  message: "Email:",
390
454
  validate: (v) => v.includes("@") ? void 0 : "Please enter a valid email"
391
455
  });
392
- if (clack.isCancel(email)) {
393
- clack.cancel("Login cancelled.");
456
+ if (clack3.isCancel(email)) {
457
+ clack3.cancel("Login cancelled.");
394
458
  throw new Error("cancelled");
395
459
  }
396
- const password2 = json ? process.env.INSFORGE_PASSWORD : await clack.password({
460
+ const password2 = json ? process.env.INSFORGE_PASSWORD : await clack3.password({
397
461
  message: "Password:"
398
462
  });
399
- if (clack.isCancel(password2)) {
400
- clack.cancel("Login cancelled.");
463
+ if (clack3.isCancel(password2)) {
464
+ clack3.cancel("Login cancelled.");
401
465
  throw new Error("cancelled");
402
466
  }
403
467
  if (!email || !password2) {
404
468
  throw new Error("Email and password are required. Set INSFORGE_EMAIL and INSFORGE_PASSWORD environment variables for non-interactive mode.");
405
469
  }
406
470
  if (!json) {
407
- const s = clack.spinner();
471
+ const s = clack3.spinner();
408
472
  s.start("Authenticating...");
409
473
  const result = await login(email, password2, apiUrl);
410
474
  const creds = {
@@ -414,7 +478,7 @@ async function loginWithEmail(json, apiUrl) {
414
478
  };
415
479
  saveCredentials(creds);
416
480
  s.stop(`Authenticated as ${result.user.email}`);
417
- clack.outro("Done");
481
+ clack3.outro("Done");
418
482
  } else {
419
483
  const result = await login(email, password2, apiUrl);
420
484
  const creds = {
@@ -426,105 +490,15 @@ async function loginWithEmail(json, apiUrl) {
426
490
  console.log(JSON.stringify({ success: true, user: result.user }));
427
491
  }
428
492
  }
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
- });
493
+ async function loginWithOAuth(json, apiUrl) {
445
494
  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
- }
495
+ clack3.intro("InsForge CLI");
458
496
  }
497
+ const creds = await performOAuthLogin(apiUrl);
459
498
  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
- }
499
+ clack3.outro("Done");
497
500
  } 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
- }
501
+ console.log(JSON.stringify({ success: true, user: creds.user }));
528
502
  }
529
503
  }
530
504
 
@@ -572,7 +546,7 @@ function registerWhoamiCommand(program2) {
572
546
  program2.command("whoami").description("Show current authenticated user").action(async (_opts, cmd) => {
573
547
  const { json, apiUrl } = getRootOpts(cmd);
574
548
  try {
575
- requireAuth();
549
+ await requireAuth(apiUrl);
576
550
  const profile = await getProfile(apiUrl);
577
551
  if (json) {
578
552
  outputJson(profile);
@@ -592,7 +566,7 @@ function registerOrgsCommands(orgsCmd2) {
592
566
  orgsCmd2.command("list").description("List all organizations").action(async (_opts, cmd) => {
593
567
  const { json, apiUrl } = getRootOpts(cmd);
594
568
  try {
595
- requireAuth();
569
+ await requireAuth(apiUrl);
596
570
  const orgs = await listOrganizations(apiUrl);
597
571
  if (json) {
598
572
  outputJson(orgs);
@@ -617,12 +591,12 @@ function registerOrgsCommands(orgsCmd2) {
617
591
  }
618
592
 
619
593
  // src/commands/projects/list.ts
620
- import * as clack2 from "@clack/prompts";
594
+ import * as clack4 from "@clack/prompts";
621
595
  function registerProjectsCommands(projectsCmd2) {
622
596
  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
597
  const { json, apiUrl } = getRootOpts(cmd);
624
598
  try {
625
- requireAuth();
599
+ await requireAuth(apiUrl);
626
600
  let orgId = opts.orgId ?? getGlobalConfig().default_org_id;
627
601
  if (!orgId) {
628
602
  const orgs = await listOrganizations(apiUrl);
@@ -632,14 +606,14 @@ function registerProjectsCommands(projectsCmd2) {
632
606
  if (orgs.length === 1) {
633
607
  orgId = orgs[0].id;
634
608
  } else if (!json) {
635
- const selected = await clack2.select({
609
+ const selected = await clack4.select({
636
610
  message: "Select an organization:",
637
611
  options: orgs.map((o) => ({
638
612
  value: o.id,
639
613
  label: o.name
640
614
  }))
641
615
  });
642
- if (clack2.isCancel(selected)) {
616
+ if (clack4.isCancel(selected)) {
643
617
  process.exit(0);
644
618
  }
645
619
  orgId = selected;
@@ -667,23 +641,23 @@ function registerProjectsCommands(projectsCmd2) {
667
641
  }
668
642
 
669
643
  // src/commands/projects/link.ts
670
- import * as clack4 from "@clack/prompts";
644
+ import * as clack6 from "@clack/prompts";
671
645
 
672
646
  // src/lib/skills.ts
673
647
  import { exec } from "child_process";
674
648
  import { promisify } from "util";
675
- import * as clack3 from "@clack/prompts";
649
+ import * as clack5 from "@clack/prompts";
676
650
  var execAsync = promisify(exec);
677
651
  async function installSkills(json) {
678
652
  try {
679
- if (!json) clack3.log.info("Installing InsForge agent skills...");
653
+ if (!json) clack5.log.info("Installing InsForge agent skills...");
680
654
  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
655
  cwd: process.cwd(),
682
656
  timeout: 6e4
683
657
  });
684
- if (!json) clack3.log.success("InsForge agent skills installed.");
658
+ if (!json) clack5.log.success("InsForge agent skills installed.");
685
659
  } catch {
686
- if (!json) clack3.log.warn("Failed to install agent skills. You can run manually: npx skills add insforge/agent-skills");
660
+ if (!json) clack5.log.warn("Failed to install agent skills. You can run manually: npx skills add insforge/agent-skills");
687
661
  }
688
662
  }
689
663
 
@@ -695,7 +669,7 @@ function registerProjectLinkCommand(program2) {
695
669
  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
670
  const { json, apiUrl } = getRootOpts(cmd);
697
671
  try {
698
- requireAuth();
672
+ await requireAuth(apiUrl);
699
673
  let orgId = opts.orgId;
700
674
  let projectId = opts.projectId;
701
675
  if (!orgId) {
@@ -706,14 +680,14 @@ function registerProjectLinkCommand(program2) {
706
680
  if (json) {
707
681
  throw new CLIError("Specify --org-id in JSON mode.");
708
682
  }
709
- const selected = await clack4.select({
683
+ const selected = await clack6.select({
710
684
  message: "Select an organization:",
711
685
  options: orgs.map((o) => ({
712
686
  value: o.id,
713
687
  label: o.name
714
688
  }))
715
689
  });
716
- if (clack4.isCancel(selected)) process.exit(0);
690
+ if (clack6.isCancel(selected)) process.exit(0);
717
691
  orgId = selected;
718
692
  }
719
693
  const config = getGlobalConfig();
@@ -727,14 +701,14 @@ function registerProjectLinkCommand(program2) {
727
701
  if (json) {
728
702
  throw new CLIError("Specify --project-id in JSON mode.");
729
703
  }
730
- const selected = await clack4.select({
704
+ const selected = await clack6.select({
731
705
  message: "Select a project to link:",
732
706
  options: projects.map((p) => ({
733
707
  value: p.id,
734
708
  label: `${p.name} (${p.region}, ${p.status})`
735
709
  }))
736
710
  });
737
- if (clack4.isCancel(selected)) process.exit(0);
711
+ if (clack6.isCancel(selected)) process.exit(0);
738
712
  projectId = selected;
739
713
  }
740
714
  const [project, apiKey] = await Promise.all([
@@ -796,7 +770,7 @@ function registerDbCommands(dbCmd2) {
796
770
  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
771
  const { json } = getRootOpts(cmd);
798
772
  try {
799
- requireAuth();
773
+ await requireAuth();
800
774
  const endpoint = opts.unrestricted ? "/api/database/advance/rawsql/unrestricted" : "/api/database/advance/rawsql";
801
775
  const res = await ossFetch(endpoint, {
802
776
  method: "POST",
@@ -832,7 +806,7 @@ function registerDbTablesCommand(dbCmd2) {
832
806
  dbCmd2.command("tables").description("List all database tables").action(async (_opts, cmd) => {
833
807
  const { json } = getRootOpts(cmd);
834
808
  try {
835
- requireAuth();
809
+ await requireAuth();
836
810
  const res = await ossFetch("/api/database/tables");
837
811
  const tables = await res.json();
838
812
  if (json) {
@@ -871,7 +845,7 @@ function registerDbFunctionsCommand(dbCmd2) {
871
845
  dbCmd2.command("functions").description("List all database functions").action(async (_opts, cmd) => {
872
846
  const { json } = getRootOpts(cmd);
873
847
  try {
874
- requireAuth();
848
+ await requireAuth();
875
849
  const res = await ossFetch("/api/database/functions");
876
850
  const raw = await res.json();
877
851
  const functions = extractArray(raw);
@@ -911,7 +885,7 @@ function registerDbIndexesCommand(dbCmd2) {
911
885
  dbCmd2.command("indexes").description("List all database indexes").action(async (_opts, cmd) => {
912
886
  const { json } = getRootOpts(cmd);
913
887
  try {
914
- requireAuth();
888
+ await requireAuth();
915
889
  const res = await ossFetch("/api/database/indexes");
916
890
  const raw = await res.json();
917
891
  const indexes = extractArray2(raw);
@@ -957,7 +931,7 @@ function registerDbPoliciesCommand(dbCmd2) {
957
931
  dbCmd2.command("policies").description("List all RLS policies").action(async (_opts, cmd) => {
958
932
  const { json } = getRootOpts(cmd);
959
933
  try {
960
- requireAuth();
934
+ await requireAuth();
961
935
  const res = await ossFetch("/api/database/policies");
962
936
  const raw = await res.json();
963
937
  const policies = extractArray3(raw);
@@ -1004,7 +978,7 @@ function registerDbTriggersCommand(dbCmd2) {
1004
978
  dbCmd2.command("triggers").description("List all database triggers").action(async (_opts, cmd) => {
1005
979
  const { json } = getRootOpts(cmd);
1006
980
  try {
1007
- requireAuth();
981
+ await requireAuth();
1008
982
  const res = await ossFetch("/api/database/triggers");
1009
983
  const raw = await res.json();
1010
984
  const triggers = extractArray4(raw);
@@ -1038,7 +1012,7 @@ function registerDbRpcCommand(dbCmd2) {
1038
1012
  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
1013
  const { json } = getRootOpts(cmd);
1040
1014
  try {
1041
- requireAuth();
1015
+ await requireAuth();
1042
1016
  const body = opts.data ? JSON.stringify(JSON.parse(opts.data)) : void 0;
1043
1017
  const res = await ossFetch(`/api/database/rpc/${encodeURIComponent(functionName)}`, {
1044
1018
  method: body ? "POST" : "GET",
@@ -1062,7 +1036,7 @@ function registerDbExportCommand(dbCmd2) {
1062
1036
  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
1037
  const { json } = getRootOpts(cmd);
1064
1038
  try {
1065
- requireAuth();
1039
+ await requireAuth();
1066
1040
  const body = {
1067
1041
  format: opts.format,
1068
1042
  includeData: opts.data !== false
@@ -1117,7 +1091,7 @@ function registerDbImportCommand(dbCmd2) {
1117
1091
  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
1092
  const { json } = getRootOpts(cmd);
1119
1093
  try {
1120
- requireAuth();
1094
+ await requireAuth();
1121
1095
  const config = getProjectConfig();
1122
1096
  if (!config) throw new ProjectNotLinkedError();
1123
1097
  const fileContent = readFileSync2(file);
@@ -1155,7 +1129,7 @@ function registerRecordsCommands(recordsCmd2) {
1155
1129
  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
1130
  const { json } = getRootOpts(cmd);
1157
1131
  try {
1158
- requireAuth();
1132
+ await requireAuth();
1159
1133
  const params = new URLSearchParams();
1160
1134
  if (opts.select) params.set("select", opts.select);
1161
1135
  if (opts.filter) params.set(opts.filter.split("=")[0], opts.filter.split("=").slice(1).join("="));
@@ -1197,7 +1171,7 @@ function registerRecordsCreateCommand(recordsCmd2) {
1197
1171
  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
1172
  const { json } = getRootOpts(cmd);
1199
1173
  try {
1200
- requireAuth();
1174
+ await requireAuth();
1201
1175
  if (!opts.data) {
1202
1176
  throw new CLIError(`--data is required. Example: --data '{"name":"John"}'`);
1203
1177
  }
@@ -1233,7 +1207,7 @@ function registerRecordsUpdateCommand(recordsCmd2) {
1233
1207
  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
1208
  const { json } = getRootOpts(cmd);
1235
1209
  try {
1236
- requireAuth();
1210
+ await requireAuth();
1237
1211
  if (!opts.filter) {
1238
1212
  throw new CLIError("--filter is required to prevent accidental updates to all rows.");
1239
1213
  }
@@ -1274,7 +1248,7 @@ function registerRecordsDeleteCommand(recordsCmd2) {
1274
1248
  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
1249
  const { json } = getRootOpts(cmd);
1276
1250
  try {
1277
- requireAuth();
1251
+ await requireAuth();
1278
1252
  if (!opts.filter) {
1279
1253
  throw new CLIError("--filter is required to prevent accidental deletion of all rows.");
1280
1254
  }
@@ -1303,7 +1277,7 @@ function registerFunctionsCommands(functionsCmd2) {
1303
1277
  functionsCmd2.command("list").description("List all edge functions").action(async (_opts, cmd) => {
1304
1278
  const { json } = getRootOpts(cmd);
1305
1279
  try {
1306
- requireAuth();
1280
+ await requireAuth();
1307
1281
  const res = await ossFetch("/api/functions");
1308
1282
  const data = await res.json();
1309
1283
  const functions = data.functions ?? [];
@@ -1337,7 +1311,7 @@ function registerFunctionsDeployCommand(functionsCmd2) {
1337
1311
  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
1312
  const { json } = getRootOpts(cmd);
1339
1313
  try {
1340
- requireAuth();
1314
+ await requireAuth();
1341
1315
  const filePath = opts.file ?? join2(process.cwd(), "insforge", "functions", slug, "index.ts");
1342
1316
  if (!existsSync2(filePath)) {
1343
1317
  throw new CLIError(
@@ -1382,7 +1356,7 @@ function registerFunctionsInvokeCommand(functionsCmd2) {
1382
1356
  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
1357
  const { json } = getRootOpts(cmd);
1384
1358
  try {
1385
- requireAuth();
1359
+ await requireAuth();
1386
1360
  const config = getProjectConfig();
1387
1361
  if (!config) throw new ProjectNotLinkedError();
1388
1362
  const method = opts.method.toUpperCase();
@@ -1431,7 +1405,7 @@ function registerFunctionsCodeCommand(functionsCmd2) {
1431
1405
  functionsCmd2.command("code <slug>").description("Fetch and display the source code of an edge function").action(async (slug, _opts, cmd) => {
1432
1406
  const { json } = getRootOpts(cmd);
1433
1407
  try {
1434
- requireAuth();
1408
+ await requireAuth();
1435
1409
  const res = await ossFetch(`/api/functions/${encodeURIComponent(slug)}`);
1436
1410
  const fn = await res.json();
1437
1411
  if (json) {
@@ -1455,7 +1429,7 @@ function registerStorageBucketsCommand(storageCmd2) {
1455
1429
  storageCmd2.command("buckets").description("List all storage buckets").action(async (_opts, cmd) => {
1456
1430
  const { json } = getRootOpts(cmd);
1457
1431
  try {
1458
- requireAuth();
1432
+ await requireAuth();
1459
1433
  const res = await ossFetch("/api/storage/buckets");
1460
1434
  const raw = await res.json();
1461
1435
  let buckets;
@@ -1492,7 +1466,7 @@ function registerStorageUploadCommand(storageCmd2) {
1492
1466
  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
1467
  const { json } = getRootOpts(cmd);
1494
1468
  try {
1495
- requireAuth();
1469
+ await requireAuth();
1496
1470
  const config = getProjectConfig();
1497
1471
  if (!config) throw new ProjectNotLinkedError();
1498
1472
  if (!existsSync3(file)) {
@@ -1535,7 +1509,7 @@ function registerStorageDownloadCommand(storageCmd2) {
1535
1509
  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
1510
  const { json } = getRootOpts(cmd);
1537
1511
  try {
1538
- requireAuth();
1512
+ await requireAuth();
1539
1513
  const config = getProjectConfig();
1540
1514
  if (!config) throw new ProjectNotLinkedError();
1541
1515
  const bucketName = opts.bucket;
@@ -1568,7 +1542,7 @@ function registerStorageCreateBucketCommand(storageCmd2) {
1568
1542
  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
1543
  const { json } = getRootOpts(cmd);
1570
1544
  try {
1571
- requireAuth();
1545
+ await requireAuth();
1572
1546
  const isPublic = !opts.private;
1573
1547
  const res = await ossFetch("/api/storage/buckets", {
1574
1548
  method: "POST",
@@ -1587,17 +1561,17 @@ function registerStorageCreateBucketCommand(storageCmd2) {
1587
1561
  }
1588
1562
 
1589
1563
  // src/commands/storage/delete-bucket.ts
1590
- import * as clack5 from "@clack/prompts";
1564
+ import * as clack7 from "@clack/prompts";
1591
1565
  function registerStorageDeleteBucketCommand(storageCmd2) {
1592
1566
  storageCmd2.command("delete-bucket <name>").description("Delete a storage bucket and all its objects").action(async (name, _opts, cmd) => {
1593
1567
  const { json, yes } = getRootOpts(cmd);
1594
1568
  try {
1595
- requireAuth();
1569
+ await requireAuth();
1596
1570
  if (!yes && !json) {
1597
- const confirm4 = await clack5.confirm({
1571
+ const confirm5 = await clack7.confirm({
1598
1572
  message: `Delete bucket "${name}" and all its objects? This cannot be undone.`
1599
1573
  });
1600
- if (!confirm4 || clack5.isCancel(confirm4)) {
1574
+ if (!confirm5 || clack7.isCancel(confirm5)) {
1601
1575
  process.exit(0);
1602
1576
  }
1603
1577
  }
@@ -1626,7 +1600,7 @@ function registerStorageListObjectsCommand(storageCmd2) {
1626
1600
  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
1601
  const { json } = getRootOpts(cmd);
1628
1602
  try {
1629
- requireAuth();
1603
+ await requireAuth();
1630
1604
  const params = new URLSearchParams();
1631
1605
  params.set("limit", opts.limit);
1632
1606
  params.set("offset", opts.offset);
@@ -1677,7 +1651,7 @@ import { tmpdir } from "os";
1677
1651
  import { promisify as promisify2 } from "util";
1678
1652
  import * as fs from "fs/promises";
1679
1653
  import * as path from "path";
1680
- import * as clack6 from "@clack/prompts";
1654
+ import * as clack8 from "@clack/prompts";
1681
1655
  var execAsync2 = promisify2(exec2);
1682
1656
  function buildOssHost2(appkey, region) {
1683
1657
  return `https://${appkey}.${region}.insforge.app`;
@@ -1708,9 +1682,9 @@ function registerCreateCommand(program2) {
1708
1682
  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
1683
  const { json, apiUrl } = getRootOpts(cmd);
1710
1684
  try {
1711
- requireAuth();
1685
+ await requireAuth(apiUrl);
1712
1686
  if (!json) {
1713
- clack6.intro("Create a new InsForge project");
1687
+ clack8.intro("Create a new InsForge project");
1714
1688
  }
1715
1689
  let orgId = opts.orgId;
1716
1690
  if (!orgId) {
@@ -1721,14 +1695,14 @@ function registerCreateCommand(program2) {
1721
1695
  if (json) {
1722
1696
  throw new CLIError("Specify --org-id in JSON mode.");
1723
1697
  }
1724
- const selected = await clack6.select({
1698
+ const selected = await clack8.select({
1725
1699
  message: "Select an organization:",
1726
1700
  options: orgs.map((o) => ({
1727
1701
  value: o.id,
1728
1702
  label: o.name
1729
1703
  }))
1730
1704
  });
1731
- if (clack6.isCancel(selected)) process.exit(0);
1705
+ if (clack8.isCancel(selected)) process.exit(0);
1732
1706
  orgId = selected;
1733
1707
  }
1734
1708
  const globalConfig = getGlobalConfig();
@@ -1737,11 +1711,11 @@ function registerCreateCommand(program2) {
1737
1711
  let projectName = opts.name;
1738
1712
  if (!projectName) {
1739
1713
  if (json) throw new CLIError("--name is required in JSON mode.");
1740
- const name = await clack6.text({
1714
+ const name = await clack8.text({
1741
1715
  message: "Project name:",
1742
1716
  validate: (v) => v.length >= 2 ? void 0 : "Name must be at least 2 characters"
1743
1717
  });
1744
- if (clack6.isCancel(name)) process.exit(0);
1718
+ if (clack8.isCancel(name)) process.exit(0);
1745
1719
  projectName = name;
1746
1720
  }
1747
1721
  let template = opts.template;
@@ -1749,7 +1723,7 @@ function registerCreateCommand(program2) {
1749
1723
  if (json) {
1750
1724
  template = "empty";
1751
1725
  } else {
1752
- const selected = await clack6.select({
1726
+ const selected = await clack8.select({
1753
1727
  message: "Choose a starter template:",
1754
1728
  options: [
1755
1729
  { value: "react", label: "Web app template with React" },
@@ -1757,11 +1731,11 @@ function registerCreateCommand(program2) {
1757
1731
  { value: "empty", label: "Empty project" }
1758
1732
  ]
1759
1733
  });
1760
- if (clack6.isCancel(selected)) process.exit(0);
1734
+ if (clack8.isCancel(selected)) process.exit(0);
1761
1735
  template = selected;
1762
1736
  }
1763
1737
  }
1764
- const s = !json ? clack6.spinner() : null;
1738
+ const s = !json ? clack8.spinner() : null;
1765
1739
  s?.start("Creating project...");
1766
1740
  const project = await createProject(orgId, projectName, opts.region, apiUrl);
1767
1741
  s?.message("Waiting for project to become active...");
@@ -1789,7 +1763,7 @@ function registerCreateCommand(program2) {
1789
1763
  template
1790
1764
  });
1791
1765
  } else {
1792
- clack6.outro("Done! Run `npm install` to get started.");
1766
+ clack8.outro("Done! Run `npm install` to get started.");
1793
1767
  }
1794
1768
  } catch (err) {
1795
1769
  handleError(err, json);
@@ -1797,7 +1771,7 @@ function registerCreateCommand(program2) {
1797
1771
  });
1798
1772
  }
1799
1773
  async function downloadTemplate(framework, projectConfig, projectName, json, _apiUrl) {
1800
- const s = !json ? clack6.spinner() : null;
1774
+ const s = !json ? clack8.spinner() : null;
1801
1775
  s?.start("Downloading template...");
1802
1776
  try {
1803
1777
  const anonKey = await getAnonKey();
@@ -1827,8 +1801,8 @@ async function downloadTemplate(framework, projectConfig, projectName, json, _ap
1827
1801
  } catch (err) {
1828
1802
  s?.stop("Template download failed");
1829
1803
  if (!json) {
1830
- clack6.log.warn(`Failed to download template: ${err.message}`);
1831
- clack6.log.info("You can manually set up the template later.");
1804
+ clack8.log.warn(`Failed to download template: ${err.message}`);
1805
+ clack8.log.info("You can manually set up the template later.");
1832
1806
  }
1833
1807
  }
1834
1808
  }
@@ -1882,7 +1856,7 @@ function registerListCommand(program2) {
1882
1856
  program2.command("list").description("List all organizations and their projects").action(async (_opts, cmd) => {
1883
1857
  const { json, apiUrl } = getRootOpts(cmd);
1884
1858
  try {
1885
- requireAuth();
1859
+ await requireAuth(apiUrl);
1886
1860
  const orgs = await listOrganizations(apiUrl);
1887
1861
  if (orgs.length === 0) {
1888
1862
  if (json) {
@@ -1942,7 +1916,7 @@ function registerListCommand(program2) {
1942
1916
  // src/commands/deployments/deploy.ts
1943
1917
  import * as path2 from "path";
1944
1918
  import * as fs2 from "fs/promises";
1945
- import * as clack7 from "@clack/prompts";
1919
+ import * as clack9 from "@clack/prompts";
1946
1920
  import archiver from "archiver";
1947
1921
  var POLL_INTERVAL_MS = 5e3;
1948
1922
  var POLL_TIMEOUT_MS = 12e4;
@@ -1985,7 +1959,7 @@ function registerDeploymentsDeployCommand(deploymentsCmd2) {
1985
1959
  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
1960
  const { json } = getRootOpts(cmd);
1987
1961
  try {
1988
- requireAuth();
1962
+ await requireAuth();
1989
1963
  const config = getProjectConfig();
1990
1964
  if (!config) throw new ProjectNotLinkedError();
1991
1965
  const sourceDir = path2.resolve(directory ?? ".");
@@ -1993,7 +1967,7 @@ function registerDeploymentsDeployCommand(deploymentsCmd2) {
1993
1967
  if (!stats?.isDirectory()) {
1994
1968
  throw new CLIError(`"${sourceDir}" is not a valid directory.`);
1995
1969
  }
1996
- const s = !json ? clack7.spinner() : null;
1970
+ const s = !json ? clack9.spinner() : null;
1997
1971
  s?.start("Creating deployment...");
1998
1972
  const createRes = await ossFetch("/api/deployments", { method: "POST" });
1999
1973
  const { id: deploymentId, uploadUrl, uploadFields } = await createRes.json();
@@ -2069,18 +2043,18 @@ function registerDeploymentsDeployCommand(deploymentsCmd2) {
2069
2043
  } else {
2070
2044
  const liveUrl = deployment?.deploymentUrl ?? deployment?.url;
2071
2045
  if (liveUrl) {
2072
- clack7.log.success(`Live at: ${liveUrl}`);
2046
+ clack9.log.success(`Live at: ${liveUrl}`);
2073
2047
  }
2074
- clack7.log.info(`Deployment ID: ${deploymentId}`);
2048
+ clack9.log.info(`Deployment ID: ${deploymentId}`);
2075
2049
  }
2076
2050
  } else {
2077
2051
  s?.stop("Deployment is still building");
2078
2052
  if (json) {
2079
2053
  outputJson({ id: deploymentId, status: deployment?.status ?? "building", timedOut: true });
2080
2054
  } 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}`);
2055
+ clack9.log.info(`Deployment ID: ${deploymentId}`);
2056
+ clack9.log.warn("Deployment did not finish within 2 minutes.");
2057
+ clack9.log.info(`Check status with: insforge deployments status ${deploymentId}`);
2084
2058
  }
2085
2059
  }
2086
2060
  } catch (err) {
@@ -2094,7 +2068,7 @@ function registerDeploymentsListCommand(deploymentsCmd2) {
2094
2068
  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
2069
  const { json } = getRootOpts(cmd);
2096
2070
  try {
2097
- requireAuth();
2071
+ await requireAuth();
2098
2072
  if (!getProjectConfig()) throw new ProjectNotLinkedError();
2099
2073
  const res = await ossFetch(`/api/deployments?limit=${opts.limit}&offset=${opts.offset}`);
2100
2074
  const raw = await res.json();
@@ -2135,7 +2109,7 @@ function registerDeploymentsStatusCommand(deploymentsCmd2) {
2135
2109
  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
2110
  const { json } = getRootOpts(cmd);
2137
2111
  try {
2138
- requireAuth();
2112
+ await requireAuth();
2139
2113
  if (!getProjectConfig()) throw new ProjectNotLinkedError();
2140
2114
  if (opts.sync) {
2141
2115
  await ossFetch(`/api/deployments/${id}/sync`, { method: "POST" });
@@ -2166,18 +2140,18 @@ function registerDeploymentsStatusCommand(deploymentsCmd2) {
2166
2140
  }
2167
2141
 
2168
2142
  // src/commands/deployments/cancel.ts
2169
- import * as clack8 from "@clack/prompts";
2143
+ import * as clack10 from "@clack/prompts";
2170
2144
  function registerDeploymentsCancelCommand(deploymentsCmd2) {
2171
2145
  deploymentsCmd2.command("cancel <id>").description("Cancel a deployment").action(async (id, _opts, cmd) => {
2172
2146
  const { json, yes } = getRootOpts(cmd);
2173
2147
  try {
2174
- requireAuth();
2148
+ await requireAuth();
2175
2149
  if (!getProjectConfig()) throw new ProjectNotLinkedError();
2176
2150
  if (!yes && !json) {
2177
- const confirmed = await clack8.confirm({
2151
+ const confirmed = await clack10.confirm({
2178
2152
  message: `Cancel deployment ${id}?`
2179
2153
  });
2180
- if (clack8.isCancel(confirmed) || !confirmed) process.exit(0);
2154
+ if (clack10.isCancel(confirmed) || !confirmed) process.exit(0);
2181
2155
  }
2182
2156
  const res = await ossFetch(`/api/deployments/${id}/cancel`, { method: "POST" });
2183
2157
  const result = await res.json();
@@ -2208,7 +2182,7 @@ Examples:
2208
2182
  insforge docs storage rest-api Show REST API storage docs`).action(async (feature, language, _opts, cmd) => {
2209
2183
  const { json } = getRootOpts(cmd);
2210
2184
  try {
2211
- requireAuth();
2185
+ await requireAuth();
2212
2186
  if (!feature) {
2213
2187
  await listDocs(json);
2214
2188
  return;
@@ -2264,7 +2238,7 @@ function registerSecretsListCommand(secretsCmd2) {
2264
2238
  secretsCmd2.command("list").description("List secrets (metadata only, values are hidden)").option("--all", "Include inactive (deleted) secrets").action(async (opts, cmd) => {
2265
2239
  const { json } = getRootOpts(cmd);
2266
2240
  try {
2267
- requireAuth();
2241
+ await requireAuth();
2268
2242
  const res = await ossFetch("/api/secrets");
2269
2243
  const data = await res.json();
2270
2244
  let secrets = data.secrets ?? [];
@@ -2304,7 +2278,7 @@ function registerSecretsGetCommand(secretsCmd2) {
2304
2278
  secretsCmd2.command("get <key>").description("Get the decrypted value of a secret").action(async (key, _opts, cmd) => {
2305
2279
  const { json } = getRootOpts(cmd);
2306
2280
  try {
2307
- requireAuth();
2281
+ await requireAuth();
2308
2282
  const res = await ossFetch(`/api/secrets/${encodeURIComponent(key)}`);
2309
2283
  const data = await res.json();
2310
2284
  if (json) {
@@ -2323,7 +2297,7 @@ function registerSecretsAddCommand(secretsCmd2) {
2323
2297
  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
2298
  const { json } = getRootOpts(cmd);
2325
2299
  try {
2326
- requireAuth();
2300
+ await requireAuth();
2327
2301
  const body = { key, value };
2328
2302
  if (opts.reserved) body.isReserved = true;
2329
2303
  if (opts.expires) body.expiresAt = opts.expires;
@@ -2348,7 +2322,7 @@ function registerSecretsUpdateCommand(secretsCmd2) {
2348
2322
  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
2323
  const { json } = getRootOpts(cmd);
2350
2324
  try {
2351
- requireAuth();
2325
+ await requireAuth();
2352
2326
  const body = {};
2353
2327
  if (opts.value !== void 0) body.value = opts.value;
2354
2328
  if (opts.active !== void 0) body.isActive = opts.active === "true";
@@ -2374,17 +2348,17 @@ function registerSecretsUpdateCommand(secretsCmd2) {
2374
2348
  }
2375
2349
 
2376
2350
  // src/commands/secrets/delete.ts
2377
- import * as clack9 from "@clack/prompts";
2351
+ import * as clack11 from "@clack/prompts";
2378
2352
  function registerSecretsDeleteCommand(secretsCmd2) {
2379
2353
  secretsCmd2.command("delete <key>").description("Delete a secret").action(async (key, _opts, cmd) => {
2380
2354
  const { json, yes } = getRootOpts(cmd);
2381
2355
  try {
2382
- requireAuth();
2356
+ await requireAuth();
2383
2357
  if (!yes && !json) {
2384
- const confirm4 = await clack9.confirm({
2358
+ const confirm5 = await clack11.confirm({
2385
2359
  message: `Delete secret "${key}"? This cannot be undone.`
2386
2360
  });
2387
- if (!confirm4 || clack9.isCancel(confirm4)) {
2361
+ if (!confirm5 || clack11.isCancel(confirm5)) {
2388
2362
  process.exit(0);
2389
2363
  }
2390
2364
  }
@@ -2403,6 +2377,267 @@ function registerSecretsDeleteCommand(secretsCmd2) {
2403
2377
  });
2404
2378
  }
2405
2379
 
2380
+ // src/commands/schedules/list.ts
2381
+ function registerSchedulesListCommand(schedulesCmd2) {
2382
+ schedulesCmd2.command("list").description("List all schedules").action(async (_opts, cmd) => {
2383
+ const { json } = getRootOpts(cmd);
2384
+ try {
2385
+ await requireAuth();
2386
+ const res = await ossFetch("/api/schedules");
2387
+ const data = await res.json();
2388
+ const schedules = Array.isArray(data) ? data : data.schedules ?? [];
2389
+ if (json) {
2390
+ outputJson(schedules);
2391
+ } else {
2392
+ if (!schedules.length) {
2393
+ console.log("No schedules found.");
2394
+ return;
2395
+ }
2396
+ outputTable(
2397
+ ["ID", "Name", "Cron", "URL", "Method", "Active", "Next Run"],
2398
+ schedules.map((s) => [
2399
+ String(s.id ?? "-"),
2400
+ String(s.name ?? "-"),
2401
+ String(s.cronSchedule ?? "-"),
2402
+ String(s.functionUrl ?? "-"),
2403
+ String(s.httpMethod ?? "-"),
2404
+ s.isActive === false ? "No" : "Yes",
2405
+ s.nextRun ? new Date(String(s.nextRun)).toLocaleString() : "-"
2406
+ ])
2407
+ );
2408
+ }
2409
+ } catch (err) {
2410
+ handleError(err, json);
2411
+ }
2412
+ });
2413
+ }
2414
+
2415
+ // src/commands/schedules/get.ts
2416
+ function registerSchedulesGetCommand(schedulesCmd2) {
2417
+ schedulesCmd2.command("get <id>").description("Get schedule details").action(async (id, _opts, cmd) => {
2418
+ const { json } = getRootOpts(cmd);
2419
+ try {
2420
+ await requireAuth();
2421
+ const res = await ossFetch(`/api/schedules/${encodeURIComponent(id)}`);
2422
+ const data = await res.json();
2423
+ if (json) {
2424
+ outputJson(data);
2425
+ } else {
2426
+ console.log(`
2427
+ Name: ${data.name ?? "-"}`);
2428
+ console.log(` ID: ${data.id ?? "-"}`);
2429
+ console.log(` Cron: ${data.cronSchedule ?? "-"}`);
2430
+ console.log(` URL: ${data.functionUrl ?? "-"}`);
2431
+ console.log(` Method: ${data.httpMethod ?? "-"}`);
2432
+ console.log(` Active: ${data.isActive === false ? "No" : "Yes"}`);
2433
+ if (data.headers) console.log(` Headers: ${JSON.stringify(data.headers)}`);
2434
+ if (data.body) console.log(` Body: ${JSON.stringify(data.body)}`);
2435
+ console.log(` Next Run: ${data.nextRun ? new Date(String(data.nextRun)).toLocaleString() : "-"}`);
2436
+ console.log(` Created: ${data.createdAt ? new Date(String(data.createdAt)).toLocaleString() : "-"}`);
2437
+ console.log("");
2438
+ }
2439
+ } catch (err) {
2440
+ handleError(err, json);
2441
+ }
2442
+ });
2443
+ }
2444
+
2445
+ // src/commands/schedules/create.ts
2446
+ function registerSchedulesCreateCommand(schedulesCmd2) {
2447
+ 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) => {
2448
+ const { json } = getRootOpts(cmd);
2449
+ try {
2450
+ await requireAuth();
2451
+ const body = {
2452
+ name: opts.name,
2453
+ cronSchedule: opts.cron,
2454
+ functionUrl: opts.url,
2455
+ httpMethod: opts.method.toUpperCase()
2456
+ };
2457
+ if (opts.headers) {
2458
+ try {
2459
+ body.headers = JSON.parse(opts.headers);
2460
+ } catch {
2461
+ throw new CLIError("Invalid JSON for --headers");
2462
+ }
2463
+ }
2464
+ if (opts.body) {
2465
+ try {
2466
+ body.body = JSON.parse(opts.body);
2467
+ } catch {
2468
+ throw new CLIError("Invalid JSON for --body");
2469
+ }
2470
+ }
2471
+ const res = await ossFetch("/api/schedules", {
2472
+ method: "POST",
2473
+ body: JSON.stringify(body)
2474
+ });
2475
+ const data = await res.json();
2476
+ if (json) {
2477
+ outputJson(data);
2478
+ } else {
2479
+ outputSuccess(`Schedule "${opts.name}" created (ID: ${data.id ?? "unknown"}).`);
2480
+ }
2481
+ } catch (err) {
2482
+ handleError(err, json);
2483
+ }
2484
+ });
2485
+ }
2486
+
2487
+ // src/commands/schedules/update.ts
2488
+ function registerSchedulesUpdateCommand(schedulesCmd2) {
2489
+ 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) => {
2490
+ const { json } = getRootOpts(cmd);
2491
+ try {
2492
+ await requireAuth();
2493
+ const body = {};
2494
+ if (opts.name !== void 0) body.name = opts.name;
2495
+ if (opts.cron !== void 0) body.cronSchedule = opts.cron;
2496
+ if (opts.url !== void 0) body.functionUrl = opts.url;
2497
+ if (opts.method !== void 0) body.httpMethod = opts.method.toUpperCase();
2498
+ if (opts.active !== void 0) body.isActive = opts.active === "true";
2499
+ if (opts.headers !== void 0) {
2500
+ try {
2501
+ body.headers = JSON.parse(opts.headers);
2502
+ } catch {
2503
+ throw new CLIError("Invalid JSON for --headers");
2504
+ }
2505
+ }
2506
+ if (opts.body !== void 0) {
2507
+ try {
2508
+ body.body = JSON.parse(opts.body);
2509
+ } catch {
2510
+ throw new CLIError("Invalid JSON for --body");
2511
+ }
2512
+ }
2513
+ if (Object.keys(body).length === 0) {
2514
+ throw new CLIError("Provide at least one option to update (--name, --cron, --url, --method, --headers, --body, --active).");
2515
+ }
2516
+ const res = await ossFetch(`/api/schedules/${encodeURIComponent(id)}`, {
2517
+ method: "PATCH",
2518
+ body: JSON.stringify(body)
2519
+ });
2520
+ const data = await res.json();
2521
+ if (json) {
2522
+ outputJson(data);
2523
+ } else {
2524
+ outputSuccess(data.message ?? "Schedule updated.");
2525
+ }
2526
+ } catch (err) {
2527
+ handleError(err, json);
2528
+ }
2529
+ });
2530
+ }
2531
+
2532
+ // src/commands/schedules/delete.ts
2533
+ import * as clack12 from "@clack/prompts";
2534
+ function registerSchedulesDeleteCommand(schedulesCmd2) {
2535
+ schedulesCmd2.command("delete <id>").description("Delete a schedule").action(async (id, _opts, cmd) => {
2536
+ const { json, yes } = getRootOpts(cmd);
2537
+ try {
2538
+ await requireAuth();
2539
+ if (!yes && !json) {
2540
+ const confirm5 = await clack12.confirm({
2541
+ message: `Delete schedule "${id}"? This cannot be undone.`
2542
+ });
2543
+ if (!confirm5 || clack12.isCancel(confirm5)) {
2544
+ process.exit(0);
2545
+ }
2546
+ }
2547
+ const res = await ossFetch(`/api/schedules/${encodeURIComponent(id)}`, {
2548
+ method: "DELETE"
2549
+ });
2550
+ const data = await res.json();
2551
+ if (json) {
2552
+ outputJson(data);
2553
+ } else {
2554
+ outputSuccess(data.message ?? "Schedule deleted.");
2555
+ }
2556
+ } catch (err) {
2557
+ handleError(err, json);
2558
+ }
2559
+ });
2560
+ }
2561
+
2562
+ // src/commands/schedules/logs.ts
2563
+ function registerSchedulesLogsCommand(schedulesCmd2) {
2564
+ 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) => {
2565
+ const { json } = getRootOpts(cmd);
2566
+ try {
2567
+ await requireAuth();
2568
+ const limit = parseInt(opts.limit, 10) || 50;
2569
+ const offset = parseInt(opts.offset, 10) || 0;
2570
+ const res = await ossFetch(`/api/schedules/${encodeURIComponent(id)}/logs?limit=${limit}&offset=${offset}`);
2571
+ const data = await res.json();
2572
+ const logs = data.logs ?? [];
2573
+ if (json) {
2574
+ outputJson(data);
2575
+ } else {
2576
+ if (!logs.length) {
2577
+ console.log("No execution logs found.");
2578
+ return;
2579
+ }
2580
+ outputTable(
2581
+ ["Executed At", "Status", "Success", "Duration (ms)"],
2582
+ logs.map((l) => [
2583
+ l.executedAt ? new Date(String(l.executedAt)).toLocaleString() : "-",
2584
+ String(l.statusCode ?? "-"),
2585
+ l.success ? "Yes" : "No",
2586
+ String(l.durationMs ?? "-")
2587
+ ])
2588
+ );
2589
+ if (data.totalCount > offset + logs.length) {
2590
+ console.log(`
2591
+ Showing ${offset + 1}-${offset + logs.length} of ${data.totalCount}. Use --offset to paginate.`);
2592
+ }
2593
+ }
2594
+ } catch (err) {
2595
+ handleError(err, json);
2596
+ }
2597
+ });
2598
+ }
2599
+
2600
+ // src/commands/logs.ts
2601
+ var VALID_SOURCES = ["insforge.logs", "postgREST.logs", "postgres.logs", "function.logs"];
2602
+ var SOURCE_LOOKUP = new Map(VALID_SOURCES.map((s) => [s.toLowerCase(), s]));
2603
+ function registerLogsCommand(program2) {
2604
+ 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) => {
2605
+ const { json } = getRootOpts(cmd);
2606
+ try {
2607
+ await requireAuth();
2608
+ const resolved = SOURCE_LOOKUP.get(source.toLowerCase());
2609
+ if (!resolved) {
2610
+ console.error(`Invalid log source "${source}". Valid sources: ${VALID_SOURCES.join(", ")}`);
2611
+ process.exit(1);
2612
+ }
2613
+ const limit = parseInt(opts.limit, 10) || 20;
2614
+ const res = await ossFetch(`/api/logs/${encodeURIComponent(resolved)}?limit=${limit}`);
2615
+ const data = await res.json();
2616
+ if (json) {
2617
+ outputJson(data);
2618
+ } else {
2619
+ const logs = Array.isArray(data) ? data : data.logs;
2620
+ if (!Array.isArray(logs) || !logs.length) {
2621
+ console.log("No logs found.");
2622
+ return;
2623
+ }
2624
+ for (const entry of logs) {
2625
+ if (typeof entry === "string") {
2626
+ console.log(entry);
2627
+ } else {
2628
+ const e = entry;
2629
+ const ts = e.timestamp ?? e.time ?? "";
2630
+ const msg = e.message ?? e.msg ?? e.log ?? JSON.stringify(e);
2631
+ console.log(`${ts} ${msg}`);
2632
+ }
2633
+ }
2634
+ }
2635
+ } catch (err) {
2636
+ handleError(err, json);
2637
+ }
2638
+ });
2639
+ }
2640
+
2406
2641
  // src/index.ts
2407
2642
  var __dirname = dirname(fileURLToPath(import.meta.url));
2408
2643
  var pkg = JSON.parse(readFileSync5(join5(__dirname, "../package.json"), "utf-8"));
@@ -2476,5 +2711,13 @@ registerSecretsGetCommand(secretsCmd);
2476
2711
  registerSecretsAddCommand(secretsCmd);
2477
2712
  registerSecretsUpdateCommand(secretsCmd);
2478
2713
  registerSecretsDeleteCommand(secretsCmd);
2714
+ registerLogsCommand(program);
2715
+ var schedulesCmd = program.command("schedules").description("Manage scheduled tasks (cron jobs)");
2716
+ registerSchedulesListCommand(schedulesCmd);
2717
+ registerSchedulesGetCommand(schedulesCmd);
2718
+ registerSchedulesCreateCommand(schedulesCmd);
2719
+ registerSchedulesUpdateCommand(schedulesCmd);
2720
+ registerSchedulesDeleteCommand(schedulesCmd);
2721
+ registerSchedulesLogsCommand(schedulesCmd);
2479
2722
  program.parse();
2480
2723
  //# sourceMappingURL=index.js.map