postgresai 0.11.0-alpha.9 → 0.12.0-alpha.13

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.
@@ -45,6 +45,9 @@ const util_1 = require("util");
45
45
  const readline = __importStar(require("readline"));
46
46
  const http = __importStar(require("https"));
47
47
  const url_1 = require("url");
48
+ const mcp_server_1 = require("../lib/mcp-server");
49
+ const issues_1 = require("../lib/issues");
50
+ const util_2 = require("../lib/util");
48
51
  const execPromise = (0, util_1.promisify)(child_process_1.exec);
49
52
  const execFilePromise = (0, util_1.promisify)(child_process_1.execFile);
50
53
  /**
@@ -122,10 +125,11 @@ async function runCompose(args) {
122
125
  program.command("help", { isDefault: true }).description("show help").action(() => {
123
126
  program.outputHelp();
124
127
  });
125
- // Service lifecycle
126
- program
128
+ // Monitoring services management
129
+ const mon = program.command("mon").description("monitoring services management");
130
+ mon
127
131
  .command("quickstart")
128
- .description("complete setup (generate config, start services)")
132
+ .description("complete setup (generate config, start monitoring services)")
129
133
  .option("--demo", "demo mode", false)
130
134
  .action(async () => {
131
135
  const code1 = await runCompose(["run", "--rm", "sources-generator"]);
@@ -137,31 +141,25 @@ program
137
141
  if (code2 !== 0)
138
142
  process.exitCode = code2;
139
143
  });
140
- program
141
- .command("install")
142
- .description("prepare project (no-op in repo checkout)")
143
- .action(async () => {
144
- console.log("Project files present; nothing to install.");
145
- });
146
- program
144
+ mon
147
145
  .command("start")
148
- .description("start services")
146
+ .description("start monitoring services")
149
147
  .action(async () => {
150
148
  const code = await runCompose(["up", "-d"]);
151
149
  if (code !== 0)
152
150
  process.exitCode = code;
153
151
  });
154
- program
152
+ mon
155
153
  .command("stop")
156
- .description("stop services")
154
+ .description("stop monitoring services")
157
155
  .action(async () => {
158
156
  const code = await runCompose(["down"]);
159
157
  if (code !== 0)
160
158
  process.exitCode = code;
161
159
  });
162
- program
160
+ mon
163
161
  .command("restart [service]")
164
- .description("restart all services or specific service")
162
+ .description("restart all monitoring services or specific service")
165
163
  .action(async (service) => {
166
164
  const args = ["restart"];
167
165
  if (service)
@@ -170,19 +168,19 @@ program
170
168
  if (code !== 0)
171
169
  process.exitCode = code;
172
170
  });
173
- program
171
+ mon
174
172
  .command("status")
175
- .description("show service status")
173
+ .description("show monitoring services status")
176
174
  .action(async () => {
177
175
  const code = await runCompose(["ps"]);
178
176
  if (code !== 0)
179
177
  process.exitCode = code;
180
178
  });
181
- program
179
+ mon
182
180
  .command("logs [service]")
183
181
  .option("-f, --follow", "follow logs", false)
184
182
  .option("--tail <lines>", "number of lines to show from the end of logs", "all")
185
- .description("show logs for all or specific service")
183
+ .description("show logs for all or specific monitoring service")
186
184
  .action(async (service, opts) => {
187
185
  const args = ["logs"];
188
186
  if (opts.follow)
@@ -195,9 +193,9 @@ program
195
193
  if (code !== 0)
196
194
  process.exitCode = code;
197
195
  });
198
- program
196
+ mon
199
197
  .command("health")
200
- .description("health check")
198
+ .description("health check for monitoring services")
201
199
  .option("--wait <seconds>", "wait time in seconds for services to become healthy", parseInt, 0)
202
200
  .action(async (opts) => {
203
201
  const services = [
@@ -246,9 +244,9 @@ program
246
244
  process.exitCode = 1;
247
245
  }
248
246
  });
249
- program
247
+ mon
250
248
  .command("config")
251
- .description("show configuration")
249
+ .description("show monitoring services configuration")
252
250
  .action(async () => {
253
251
  const { fs, projectDir, composeFile, instancesFile } = resolvePaths();
254
252
  console.log(`Project Directory: ${projectDir}`);
@@ -262,17 +260,17 @@ program
262
260
  console.log();
263
261
  }
264
262
  });
265
- program
263
+ mon
266
264
  .command("update-config")
267
- .description("apply configuration (generate sources)")
265
+ .description("apply monitoring services configuration (generate sources)")
268
266
  .action(async () => {
269
267
  const code = await runCompose(["run", "--rm", "sources-generator"]);
270
268
  if (code !== 0)
271
269
  process.exitCode = code;
272
270
  });
273
- program
271
+ mon
274
272
  .command("update")
275
- .description("update project")
273
+ .description("update monitoring stack")
276
274
  .action(async () => {
277
275
  console.log("Updating PostgresAI monitoring stack...\n");
278
276
  try {
@@ -299,8 +297,8 @@ program
299
297
  const code = await runCompose(["pull"]);
300
298
  if (code === 0) {
301
299
  console.log("\n✓ Update completed successfully");
302
- console.log("\nTo apply updates, restart services:");
303
- console.log(" postgres-ai restart");
300
+ console.log("\nTo apply updates, restart monitoring services:");
301
+ console.log(" postgres-ai mon restart");
304
302
  }
305
303
  else {
306
304
  console.error("\n✗ Docker image update failed");
@@ -313,9 +311,9 @@ program
313
311
  process.exitCode = 1;
314
312
  }
315
313
  });
316
- program
314
+ mon
317
315
  .command("reset [service]")
318
- .description("reset all or specific service")
316
+ .description("reset all or specific monitoring service")
319
317
  .action(async (service) => {
320
318
  const rl = readline.createInterface({
321
319
  input: process.stdin,
@@ -376,9 +374,9 @@ program
376
374
  process.exitCode = 1;
377
375
  }
378
376
  });
379
- program
377
+ mon
380
378
  .command("clean")
381
- .description("cleanup artifacts")
379
+ .description("cleanup monitoring services artifacts")
382
380
  .action(async () => {
383
381
  console.log("Cleaning up Docker resources...\n");
384
382
  try {
@@ -409,26 +407,27 @@ program
409
407
  process.exitCode = 1;
410
408
  }
411
409
  });
412
- program
410
+ mon
413
411
  .command("shell <service>")
414
- .description("open service shell")
412
+ .description("open shell to monitoring service")
415
413
  .action(async (service) => {
416
414
  const code = await runCompose(["exec", service, "/bin/sh"]);
417
415
  if (code !== 0)
418
416
  process.exitCode = code;
419
417
  });
420
- program
418
+ mon
421
419
  .command("check")
422
- .description("system readiness check")
420
+ .description("monitoring services system readiness check")
423
421
  .action(async () => {
424
422
  const code = await runCompose(["ps"]);
425
423
  if (code !== 0)
426
424
  process.exitCode = code;
427
425
  });
428
- // Instance management
429
- program
430
- .command("list-instances")
431
- .description("list instances")
426
+ // Monitoring targets (databases to monitor)
427
+ const targets = mon.command("targets").description("manage databases to monitor");
428
+ targets
429
+ .command("list")
430
+ .description("list monitoring target databases")
432
431
  .action(async () => {
433
432
  const instancesPath = path.resolve(process.cwd(), "instances.yml");
434
433
  if (!fs.existsSync(instancesPath)) {
@@ -440,29 +439,29 @@ program
440
439
  const content = fs.readFileSync(instancesPath, "utf8");
441
440
  const instances = yaml.load(content);
442
441
  if (!instances || !Array.isArray(instances) || instances.length === 0) {
443
- console.log("No instances configured");
442
+ console.log("No monitoring targets configured");
444
443
  console.log("");
445
- console.log("To add an instance:");
446
- console.log(" postgres-ai add-instance <connection-string> <name>");
444
+ console.log("To add a monitoring target:");
445
+ console.log(" postgres-ai mon targets add <connection-string> <name>");
447
446
  console.log("");
448
447
  console.log("Example:");
449
- console.log(" postgres-ai add-instance 'postgresql://user:pass@host:5432/db' my-db");
448
+ console.log(" postgres-ai mon targets add 'postgresql://user:pass@host:5432/db' my-db");
450
449
  return;
451
450
  }
452
451
  // Filter out demo placeholder
453
452
  const filtered = instances.filter((inst) => inst.name && inst.name !== "target-database");
454
453
  if (filtered.length === 0) {
455
- console.log("No instances configured");
454
+ console.log("No monitoring targets configured");
456
455
  console.log("");
457
- console.log("To add an instance:");
458
- console.log(" postgres-ai add-instance <connection-string> <name>");
456
+ console.log("To add a monitoring target:");
457
+ console.log(" postgres-ai mon targets add <connection-string> <name>");
459
458
  console.log("");
460
459
  console.log("Example:");
461
- console.log(" postgres-ai add-instance 'postgresql://user:pass@host:5432/db' my-db");
460
+ console.log(" postgres-ai mon targets add 'postgresql://user:pass@host:5432/db' my-db");
462
461
  return;
463
462
  }
464
463
  for (const inst of filtered) {
465
- console.log(`Instance: ${inst.name}`);
464
+ console.log(`Target: ${inst.name}`);
466
465
  }
467
466
  }
468
467
  catch (err) {
@@ -471,9 +470,9 @@ program
471
470
  process.exitCode = 1;
472
471
  }
473
472
  });
474
- program
475
- .command("add-instance [connStr] [name]")
476
- .description("add instance")
473
+ targets
474
+ .command("add [connStr] [name]")
475
+ .description("add monitoring target database")
477
476
  .action(async (connStr, name) => {
478
477
  const file = path.resolve(process.cwd(), "instances.yml");
479
478
  if (!connStr) {
@@ -498,7 +497,7 @@ program
498
497
  if (Array.isArray(instances)) {
499
498
  const exists = instances.some((inst) => inst.name === instanceName);
500
499
  if (exists) {
501
- console.error(`Instance '${instanceName}' already exists`);
500
+ console.error(`Monitoring target '${instanceName}' already exists`);
502
501
  process.exitCode = 1;
503
502
  return;
504
503
  }
@@ -509,7 +508,7 @@ program
509
508
  // If YAML parsing fails, fall back to simple check
510
509
  const content = fs.existsSync(file) ? fs.readFileSync(file, "utf8") : "";
511
510
  if (new RegExp(`^- name: ${instanceName}$`, "m").test(content)) {
512
- console.error(`Instance '${instanceName}' already exists`);
511
+ console.error(`Monitoring target '${instanceName}' already exists`);
513
512
  process.exitCode = 1;
514
513
  return;
515
514
  }
@@ -518,11 +517,11 @@ program
518
517
  const body = `- name: ${instanceName}\n conn_str: ${connStr}\n preset_metrics: full\n custom_metrics:\n is_enabled: true\n group: default\n custom_tags:\n env: production\n cluster: default\n node_name: ${instanceName}\n sink_type: ~sink_type~\n`;
519
518
  const content = fs.existsSync(file) ? fs.readFileSync(file, "utf8") : "";
520
519
  fs.appendFileSync(file, (content && !/\n$/.test(content) ? "\n" : "") + body, "utf8");
521
- console.log(`Instance '${instanceName}' added`);
520
+ console.log(`Monitoring target '${instanceName}' added`);
522
521
  });
523
- program
524
- .command("remove-instance <name>")
525
- .description("remove instance")
522
+ targets
523
+ .command("remove <name>")
524
+ .description("remove monitoring target database")
526
525
  .action(async (name) => {
527
526
  const file = path.resolve(process.cwd(), "instances.yml");
528
527
  if (!fs.existsSync(file)) {
@@ -540,12 +539,12 @@ program
540
539
  }
541
540
  const filtered = instances.filter((inst) => inst.name !== name);
542
541
  if (filtered.length === instances.length) {
543
- console.error(`Instance '${name}' not found`);
542
+ console.error(`Monitoring target '${name}' not found`);
544
543
  process.exitCode = 1;
545
544
  return;
546
545
  }
547
546
  fs.writeFileSync(file, yaml.dump(filtered), "utf8");
548
- console.log(`Instance '${name}' removed`);
547
+ console.log(`Monitoring target '${name}' removed`);
549
548
  }
550
549
  catch (err) {
551
550
  const message = err instanceof Error ? err.message : String(err);
@@ -553,9 +552,9 @@ program
553
552
  process.exitCode = 1;
554
553
  }
555
554
  });
556
- program
557
- .command("test-instance <name>")
558
- .description("test instance connectivity")
555
+ targets
556
+ .command("test <name>")
557
+ .description("test monitoring target database connectivity")
559
558
  .action(async (name) => {
560
559
  const instancesPath = path.resolve(process.cwd(), "instances.yml");
561
560
  if (!fs.existsSync(instancesPath)) {
@@ -573,16 +572,16 @@ program
573
572
  }
574
573
  const instance = instances.find((inst) => inst.name === name);
575
574
  if (!instance) {
576
- console.error(`Instance '${name}' not found`);
575
+ console.error(`Monitoring target '${name}' not found`);
577
576
  process.exitCode = 1;
578
577
  return;
579
578
  }
580
579
  if (!instance.conn_str) {
581
- console.error(`Connection string not found for instance '${name}'`);
580
+ console.error(`Connection string not found for monitoring target '${name}'`);
582
581
  process.exitCode = 1;
583
582
  return;
584
583
  }
585
- console.log(`Testing connection to '${name}'...`);
584
+ console.log(`Testing connection to monitoring target '${name}'...`);
586
585
  const { stdout, stderr } = await execFilePromise("psql", [instance.conn_str, "-c", "SELECT version();", "--no-psqlrc"], { timeout: 10000, env: { ...process.env, PAGER: 'cat' } });
587
586
  console.log(`✓ Connection successful`);
588
587
  console.log(stdout.trim());
@@ -606,8 +605,8 @@ program
606
605
  // Generate PKCE parameters
607
606
  const params = pkce.generatePKCEParams();
608
607
  const rootOpts = program.opts();
609
- const apiBaseUrl = (rootOpts.apiBaseUrl || process.env.PGAI_API_BASE_URL || "https://postgres.ai/api/general/").replace(/\/$/, "");
610
- const uiBaseUrl = (rootOpts.uiBaseUrl || process.env.PGAI_UI_BASE_URL || "https://console.postgres.ai").replace(/\/$/, "");
608
+ const cfg = config.readConfig();
609
+ const { apiBaseUrl, uiBaseUrl } = (0, util_2.resolveBaseUrls)(rootOpts, cfg);
611
610
  if (opts.debug) {
612
611
  console.log(`Debug: Resolved API base URL: ${apiBaseUrl}`);
613
612
  console.log(`Debug: Resolved UI base URL: ${uiBaseUrl}`);
@@ -616,7 +615,7 @@ program
616
615
  // Step 1: Start local callback server FIRST to get actual port
617
616
  console.log("Starting local callback server...");
618
617
  const requestedPort = opts.port || 0; // 0 = OS assigns available port
619
- const callbackServer = authServer.createCallbackServer(requestedPort, params.state, 300000);
618
+ const callbackServer = authServer.createCallbackServer(requestedPort, params.state, 120000); // 2 minute timeout
620
619
  // Wait a bit for server to start and get port
621
620
  await new Promise(resolve => setTimeout(resolve, 100));
622
621
  const actualPort = callbackServer.getPort();
@@ -649,10 +648,19 @@ program
649
648
  res.on("end", async () => {
650
649
  if (res.statusCode !== 200) {
651
650
  console.error(`Failed to initialize auth session: ${res.statusCode}`);
652
- console.error(data);
651
+ // Check if response is HTML (common for 404 pages)
652
+ if (data.trim().startsWith("<!") || data.trim().startsWith("<html")) {
653
+ console.error("Error: Received HTML response instead of JSON. This usually means:");
654
+ console.error(" 1. The API endpoint URL is incorrect");
655
+ console.error(" 2. The endpoint does not exist (404)");
656
+ console.error(`\nAPI URL attempted: ${initUrl.toString()}`);
657
+ console.error("\nPlease verify the --api-base-url parameter.");
658
+ }
659
+ else {
660
+ console.error(data);
661
+ }
653
662
  callbackServer.server.close();
654
- process.exitCode = 1;
655
- return;
663
+ process.exit(1);
656
664
  }
657
665
  // Step 3: Open browser
658
666
  const authUrl = `${uiBaseUrl}/cli/auth?state=${encodeURIComponent(params.state)}&code_challenge=${encodeURIComponent(params.codeChallenge)}&code_challenge_method=S256&redirect_uri=${encodeURIComponent(redirectUri)}`;
@@ -668,8 +676,18 @@ program
668
676
  (0, child_process_1.spawn)(openCommand, [authUrl], { detached: true, stdio: "ignore" }).unref();
669
677
  // Step 4: Wait for callback
670
678
  console.log("Waiting for authorization...");
679
+ console.log("(Press Ctrl+C to cancel)\n");
680
+ // Handle Ctrl+C gracefully
681
+ const cancelHandler = () => {
682
+ console.log("\n\nAuthentication cancelled by user.");
683
+ callbackServer.server.close();
684
+ process.exit(130); // Standard exit code for SIGINT
685
+ };
686
+ process.on("SIGINT", cancelHandler);
671
687
  try {
672
688
  const { code } = await callbackServer.promise;
689
+ // Remove the cancel handler after successful auth
690
+ process.off("SIGINT", cancelHandler);
673
691
  // Step 5: Exchange code for token
674
692
  console.log("\nExchanging authorization code for API token...");
675
693
  const exchangeData = JSON.stringify({
@@ -685,19 +703,29 @@ program
685
703
  "Content-Length": Buffer.byteLength(exchangeData),
686
704
  },
687
705
  }, (exchangeRes) => {
688
- let exchangeData = "";
689
- exchangeRes.on("data", (chunk) => (exchangeData += chunk));
706
+ let exchangeBody = "";
707
+ exchangeRes.on("data", (chunk) => (exchangeBody += chunk));
690
708
  exchangeRes.on("end", () => {
691
709
  if (exchangeRes.statusCode !== 200) {
692
710
  console.error(`Failed to exchange code for token: ${exchangeRes.statusCode}`);
693
- console.error(exchangeData);
694
- process.exitCode = 1;
711
+ // Check if response is HTML (common for 404 pages)
712
+ if (exchangeBody.trim().startsWith("<!") || exchangeBody.trim().startsWith("<html")) {
713
+ console.error("Error: Received HTML response instead of JSON. This usually means:");
714
+ console.error(" 1. The API endpoint URL is incorrect");
715
+ console.error(" 2. The endpoint does not exist (404)");
716
+ console.error(`\nAPI URL attempted: ${exchangeUrl.toString()}`);
717
+ console.error("\nPlease verify the --api-base-url parameter.");
718
+ }
719
+ else {
720
+ console.error(exchangeBody);
721
+ }
722
+ process.exit(1);
695
723
  return;
696
724
  }
697
725
  try {
698
- const result = JSON.parse(exchangeData);
699
- const apiToken = result.api_token;
700
- const orgId = result.org_id;
726
+ const result = JSON.parse(exchangeBody);
727
+ const apiToken = result.api_token || result?.[0]?.result?.api_token; // There is a bug with PostgREST Caching that may return an array, not single object, it's a workaround to support both cases.
728
+ const orgId = result.org_id || result?.[0]?.result?.org_id; // There is a bug with PostgREST Caching that may return an array, not single object, it's a workaround to support both cases.
701
729
  // Step 6: Save token to config
702
730
  config.writeConfig({
703
731
  apiKey: apiToken,
@@ -708,32 +736,43 @@ program
708
736
  console.log(`API key saved to: ${config.getConfigPath()}`);
709
737
  console.log(`Organization ID: ${orgId}`);
710
738
  console.log(`\nYou can now use the CLI without specifying an API key.`);
739
+ process.exit(0);
711
740
  }
712
741
  catch (err) {
713
742
  const message = err instanceof Error ? err.message : String(err);
714
743
  console.error(`Failed to parse response: ${message}`);
715
- process.exitCode = 1;
744
+ process.exit(1);
716
745
  }
717
746
  });
718
747
  });
719
748
  exchangeReq.on("error", (err) => {
720
749
  console.error(`Exchange request failed: ${err.message}`);
721
- process.exitCode = 1;
750
+ process.exit(1);
722
751
  });
723
752
  exchangeReq.write(exchangeData);
724
753
  exchangeReq.end();
725
754
  }
726
755
  catch (err) {
756
+ // Remove the cancel handler in error case too
757
+ process.off("SIGINT", cancelHandler);
727
758
  const message = err instanceof Error ? err.message : String(err);
728
- console.error(`\nAuthentication failed: ${message}`);
729
- process.exitCode = 1;
759
+ // Provide more helpful error messages
760
+ if (message.includes("timeout")) {
761
+ console.error(`\nAuthentication timed out.`);
762
+ console.error(`This usually means you closed the browser window without completing authentication.`);
763
+ console.error(`Please try again and complete the authentication flow.`);
764
+ }
765
+ else {
766
+ console.error(`\nAuthentication failed: ${message}`);
767
+ }
768
+ process.exit(1);
730
769
  }
731
770
  });
732
771
  });
733
772
  initReq.on("error", (err) => {
734
773
  console.error(`Failed to connect to API: ${err.message}`);
735
774
  callbackServer.server.close();
736
- process.exitCode = 1;
775
+ process.exit(1);
737
776
  });
738
777
  initReq.write(initData);
739
778
  initReq.end();
@@ -741,7 +780,7 @@ program
741
780
  catch (err) {
742
781
  const message = err instanceof Error ? err.message : String(err);
743
782
  console.error(`Authentication error: ${message}`);
744
- process.exitCode = 1;
783
+ process.exit(1);
745
784
  }
746
785
  });
747
786
  program
@@ -761,15 +800,8 @@ program
761
800
  console.log(`\nTo authenticate, run: pgai auth`);
762
801
  return;
763
802
  }
764
- const mask = (k) => {
765
- if (k.length <= 8)
766
- return "****";
767
- if (k.length <= 16)
768
- return `${k.slice(0, 4)}${"*".repeat(k.length - 8)}${k.slice(-4)}`;
769
- // For longer keys, show more of the beginning to help identify them
770
- return `${k.slice(0, Math.min(12, k.length - 8))}${"*".repeat(Math.max(4, k.length - 16))}${k.slice(-4)}`;
771
- };
772
- console.log(`Current API key: ${mask(cfg.apiKey)}`);
803
+ const { maskSecret } = require("../lib/util");
804
+ console.log(`Current API key: ${maskSecret(cfg.apiKey)}`);
773
805
  if (cfg.orgId) {
774
806
  console.log(`Organization ID: ${cfg.orgId}`);
775
807
  }
@@ -811,9 +843,9 @@ program
811
843
  console.log("API key removed");
812
844
  console.log(`\nTo authenticate again, run: pgai auth`);
813
845
  });
814
- program
846
+ mon
815
847
  .command("generate-grafana-password")
816
- .description("generate Grafana password")
848
+ .description("generate Grafana password for monitoring services")
817
849
  .action(async () => {
818
850
  const cfgPath = path.resolve(process.cwd(), ".pgwatch-config");
819
851
  try {
@@ -846,8 +878,8 @@ program
846
878
  console.log(" URL: http://localhost:3000");
847
879
  console.log(" Username: monitor");
848
880
  console.log(` Password: ${newPassword}`);
849
- console.log("\nRestart Grafana to apply:");
850
- console.log(" postgres-ai restart grafana");
881
+ console.log("\nReset Grafana to apply new password:");
882
+ console.log(" postgres-ai mon reset grafana");
851
883
  }
852
884
  catch (error) {
853
885
  const message = error instanceof Error ? error.message : String(error);
@@ -856,13 +888,13 @@ program
856
888
  process.exitCode = 1;
857
889
  }
858
890
  });
859
- program
891
+ mon
860
892
  .command("show-grafana-credentials")
861
- .description("show Grafana credentials")
893
+ .description("show Grafana credentials for monitoring services")
862
894
  .action(async () => {
863
895
  const cfgPath = path.resolve(process.cwd(), ".pgwatch-config");
864
896
  if (!fs.existsSync(cfgPath)) {
865
- console.error("Configuration file not found. Run 'quickstart' first.");
897
+ console.error("Configuration file not found. Run 'postgres-ai mon quickstart' first.");
866
898
  process.exitCode = 1;
867
899
  return;
868
900
  }
@@ -893,5 +925,48 @@ program
893
925
  console.log(` Password: ${password}`);
894
926
  console.log("");
895
927
  });
928
+ // Issues management
929
+ const issues = program.command("issues").description("issues management");
930
+ issues
931
+ .command("list")
932
+ .description("list issues")
933
+ .option("--debug", "enable debug output")
934
+ .action(async (opts) => {
935
+ try {
936
+ const rootOpts = program.opts();
937
+ const cfg = config.readConfig();
938
+ const { apiKey } = getConfig(rootOpts);
939
+ if (!apiKey) {
940
+ console.error("API key is required. Run 'pgai auth' first or set --api-key.");
941
+ process.exitCode = 1;
942
+ return;
943
+ }
944
+ const { apiBaseUrl } = (0, util_2.resolveBaseUrls)(rootOpts, cfg);
945
+ const result = await (0, issues_1.fetchIssues)({ apiKey, apiBaseUrl, debug: !!opts.debug });
946
+ if (typeof result === "string") {
947
+ process.stdout.write(result);
948
+ if (!/\n$/.test(result))
949
+ console.log();
950
+ }
951
+ else {
952
+ console.log(JSON.stringify(result, null, 2));
953
+ }
954
+ }
955
+ catch (err) {
956
+ const message = err instanceof Error ? err.message : String(err);
957
+ console.error(message);
958
+ process.exitCode = 1;
959
+ }
960
+ });
961
+ // MCP server
962
+ const mcp = program.command("mcp").description("MCP server integration");
963
+ mcp
964
+ .command("start")
965
+ .description("start MCP stdio server")
966
+ .option("--debug", "enable debug output")
967
+ .action(async (opts) => {
968
+ const rootOpts = program.opts();
969
+ await (0, mcp_server_1.startMcpServer)(rootOpts, { debug: !!opts.debug });
970
+ });
896
971
  program.parseAsync(process.argv);
897
972
  //# sourceMappingURL=postgres-ai.js.map