postgresai 0.11.0-alpha.11 → 0.11.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.
package/README.md CHANGED
@@ -88,6 +88,31 @@ postgres-ai mon check # System readiness check
88
88
  postgres-ai mon shell <service> # Open shell to monitoring service
89
89
  ```
90
90
 
91
+ ### MCP server (`mcp` group)
92
+
93
+ ```bash
94
+ pgai mcp start # Start MCP stdio server exposing list_issues tool
95
+ ```
96
+
97
+ Cursor configuration example (Settings → MCP):
98
+
99
+ ```json
100
+ {
101
+ "mcpServers": {
102
+ "PostgresAI": {
103
+ "command": "pgai",
104
+ "args": ["mcp", "start"],
105
+ "env": {
106
+ "PGAI_API_BASE_URL": "https://postgres.ai/api/general/"
107
+ }
108
+ }
109
+ }
110
+ }
111
+ ```
112
+
113
+ Tools exposed:
114
+ - list_issues: returns the same JSON as `pgai issues list`.
115
+
91
116
  #### Grafana management
92
117
  ```bash
93
118
  postgres-ai mon generate-grafana-password # Generate new Grafana password
@@ -117,6 +142,20 @@ API key resolution order:
117
142
  3. User config file (`~/.config/postgresai/config.json`)
118
143
  4. Legacy project config (`.pgwatch-config`)
119
144
 
145
+ Base URL resolution order:
146
+ - API base URL (`apiBaseUrl`):
147
+ 1. Command line option (`--api-base-url`)
148
+ 2. Environment variable (`PGAI_API_BASE_URL`)
149
+ 3. User config file `baseUrl` (`~/.config/postgresai/config.json`)
150
+ 4. Default: `https://postgres.ai/api/general/`
151
+ - UI base URL (`uiBaseUrl`):
152
+ 1. Command line option (`--ui-base-url`)
153
+ 2. Environment variable (`PGAI_UI_BASE_URL`)
154
+ 3. Default: `https://console.postgres.ai`
155
+
156
+ Normalization:
157
+ - A single trailing `/` is removed to ensure consistent path joining.
158
+
120
159
  ### Environment variables
121
160
 
122
161
  - `PGAI_API_KEY` - API key for PostgresAI services
@@ -12,6 +12,9 @@ import { promisify } from "util";
12
12
  import * as readline from "readline";
13
13
  import * as http from "https";
14
14
  import { URL } from "url";
15
+ import { startMcpServer } from "../lib/mcp-server";
16
+ import { fetchIssues } from "../lib/issues";
17
+ import { resolveBaseUrls } from "../lib/util";
15
18
 
16
19
  const execPromise = promisify(exec);
17
20
  const execFilePromise = promisify(execFile);
@@ -672,9 +675,8 @@ program
672
675
  const params = pkce.generatePKCEParams();
673
676
 
674
677
  const rootOpts = program.opts<CliOptions>();
675
-
676
- const apiBaseUrl = (rootOpts.apiBaseUrl || process.env.PGAI_API_BASE_URL || "https://postgres.ai/api/general/").replace(/\/$/, "");
677
- const uiBaseUrl = (rootOpts.uiBaseUrl || process.env.PGAI_UI_BASE_URL || "https://console.postgres.ai").replace(/\/$/, "");
678
+ const cfg = config.readConfig();
679
+ const { apiBaseUrl, uiBaseUrl } = resolveBaseUrls(rootOpts, cfg);
678
680
 
679
681
  if (opts.debug) {
680
682
  console.log(`Debug: Resolved API base URL: ${apiBaseUrl}`);
@@ -685,7 +687,7 @@ program
685
687
  // Step 1: Start local callback server FIRST to get actual port
686
688
  console.log("Starting local callback server...");
687
689
  const requestedPort = opts.port || 0; // 0 = OS assigns available port
688
- const callbackServer = authServer.createCallbackServer(requestedPort, params.state, 300000);
690
+ const callbackServer = authServer.createCallbackServer(requestedPort, params.state, 120000); // 2 minute timeout
689
691
 
690
692
  // Wait a bit for server to start and get port
691
693
  await new Promise(resolve => setTimeout(resolve, 100));
@@ -727,10 +729,20 @@ program
727
729
  res.on("end", async () => {
728
730
  if (res.statusCode !== 200) {
729
731
  console.error(`Failed to initialize auth session: ${res.statusCode}`);
730
- console.error(data);
732
+
733
+ // Check if response is HTML (common for 404 pages)
734
+ if (data.trim().startsWith("<!") || data.trim().startsWith("<html")) {
735
+ console.error("Error: Received HTML response instead of JSON. This usually means:");
736
+ console.error(" 1. The API endpoint URL is incorrect");
737
+ console.error(" 2. The endpoint does not exist (404)");
738
+ console.error(`\nAPI URL attempted: ${initUrl.toString()}`);
739
+ console.error("\nPlease verify the --api-base-url parameter.");
740
+ } else {
741
+ console.error(data);
742
+ }
743
+
731
744
  callbackServer.server.close();
732
- process.exitCode = 1;
733
- return;
745
+ process.exit(1);
734
746
  }
735
747
 
736
748
  // Step 3: Open browser
@@ -751,9 +763,22 @@ program
751
763
 
752
764
  // Step 4: Wait for callback
753
765
  console.log("Waiting for authorization...");
766
+ console.log("(Press Ctrl+C to cancel)\n");
767
+
768
+ // Handle Ctrl+C gracefully
769
+ const cancelHandler = () => {
770
+ console.log("\n\nAuthentication cancelled by user.");
771
+ callbackServer.server.close();
772
+ process.exit(130); // Standard exit code for SIGINT
773
+ };
774
+ process.on("SIGINT", cancelHandler);
775
+
754
776
  try {
755
777
  const { code } = await callbackServer.promise;
756
778
 
779
+ // Remove the cancel handler after successful auth
780
+ process.off("SIGINT", cancelHandler);
781
+
757
782
  // Step 5: Exchange code for token
758
783
  console.log("\nExchanging authorization code for API token...");
759
784
  const exchangeData = JSON.stringify({
@@ -761,7 +786,6 @@ program
761
786
  code_verifier: params.codeVerifier,
762
787
  state: params.state,
763
788
  });
764
-
765
789
  const exchangeUrl = new URL(`${apiBaseUrl}/rpc/oauth_token_exchange`);
766
790
  const exchangeReq = http.request(
767
791
  exchangeUrl,
@@ -773,20 +797,31 @@ program
773
797
  },
774
798
  },
775
799
  (exchangeRes) => {
776
- let exchangeData = "";
777
- exchangeRes.on("data", (chunk) => (exchangeData += chunk));
800
+ let exchangeBody = "";
801
+ exchangeRes.on("data", (chunk) => (exchangeBody += chunk));
778
802
  exchangeRes.on("end", () => {
779
803
  if (exchangeRes.statusCode !== 200) {
780
804
  console.error(`Failed to exchange code for token: ${exchangeRes.statusCode}`);
781
- console.error(exchangeData);
782
- process.exitCode = 1;
805
+
806
+ // Check if response is HTML (common for 404 pages)
807
+ if (exchangeBody.trim().startsWith("<!") || exchangeBody.trim().startsWith("<html")) {
808
+ console.error("Error: Received HTML response instead of JSON. This usually means:");
809
+ console.error(" 1. The API endpoint URL is incorrect");
810
+ console.error(" 2. The endpoint does not exist (404)");
811
+ console.error(`\nAPI URL attempted: ${exchangeUrl.toString()}`);
812
+ console.error("\nPlease verify the --api-base-url parameter.");
813
+ } else {
814
+ console.error(exchangeBody);
815
+ }
816
+
817
+ process.exit(1);
783
818
  return;
784
819
  }
785
820
 
786
821
  try {
787
- const result = JSON.parse(exchangeData);
788
- const apiToken = result.api_token;
789
- const orgId = result.org_id;
822
+ const result = JSON.parse(exchangeBody);
823
+ 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.
824
+ 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.
790
825
 
791
826
  // Step 6: Save token to config
792
827
  config.writeConfig({
@@ -799,10 +834,11 @@ program
799
834
  console.log(`API key saved to: ${config.getConfigPath()}`);
800
835
  console.log(`Organization ID: ${orgId}`);
801
836
  console.log(`\nYou can now use the CLI without specifying an API key.`);
837
+ process.exit(0);
802
838
  } catch (err) {
803
839
  const message = err instanceof Error ? err.message : String(err);
804
840
  console.error(`Failed to parse response: ${message}`);
805
- process.exitCode = 1;
841
+ process.exit(1);
806
842
  }
807
843
  });
808
844
  }
@@ -810,16 +846,28 @@ program
810
846
 
811
847
  exchangeReq.on("error", (err: Error) => {
812
848
  console.error(`Exchange request failed: ${err.message}`);
813
- process.exitCode = 1;
849
+ process.exit(1);
814
850
  });
815
851
 
816
852
  exchangeReq.write(exchangeData);
817
853
  exchangeReq.end();
818
854
 
819
855
  } catch (err) {
856
+ // Remove the cancel handler in error case too
857
+ process.off("SIGINT", cancelHandler);
858
+
820
859
  const message = err instanceof Error ? err.message : String(err);
821
- console.error(`\nAuthentication failed: ${message}`);
822
- process.exitCode = 1;
860
+
861
+ // Provide more helpful error messages
862
+ if (message.includes("timeout")) {
863
+ console.error(`\nAuthentication timed out.`);
864
+ console.error(`This usually means you closed the browser window without completing authentication.`);
865
+ console.error(`Please try again and complete the authentication flow.`);
866
+ } else {
867
+ console.error(`\nAuthentication failed: ${message}`);
868
+ }
869
+
870
+ process.exit(1);
823
871
  }
824
872
  });
825
873
  }
@@ -828,7 +876,7 @@ program
828
876
  initReq.on("error", (err: Error) => {
829
877
  console.error(`Failed to connect to API: ${err.message}`);
830
878
  callbackServer.server.close();
831
- process.exitCode = 1;
879
+ process.exit(1);
832
880
  });
833
881
 
834
882
  initReq.write(initData);
@@ -837,7 +885,7 @@ program
837
885
  } catch (err) {
838
886
  const message = err instanceof Error ? err.message : String(err);
839
887
  console.error(`Authentication error: ${message}`);
840
- process.exitCode = 1;
888
+ process.exit(1);
841
889
  }
842
890
  });
843
891
 
@@ -859,13 +907,8 @@ program
859
907
  console.log(`\nTo authenticate, run: pgai auth`);
860
908
  return;
861
909
  }
862
- const mask = (k: string): string => {
863
- if (k.length <= 8) return "****";
864
- if (k.length <= 16) return `${k.slice(0, 4)}${"*".repeat(k.length - 8)}${k.slice(-4)}`;
865
- // For longer keys, show more of the beginning to help identify them
866
- return `${k.slice(0, Math.min(12, k.length - 8))}${"*".repeat(Math.max(4, k.length - 16))}${k.slice(-4)}`;
867
- };
868
- console.log(`Current API key: ${mask(cfg.apiKey)}`);
910
+ const { maskSecret } = require("../lib/util");
911
+ console.log(`Current API key: ${maskSecret(cfg.apiKey)}`);
869
912
  if (cfg.orgId) {
870
913
  console.log(`Organization ID: ${cfg.orgId}`);
871
914
  }
@@ -953,8 +996,8 @@ mon
953
996
  console.log(" URL: http://localhost:3000");
954
997
  console.log(" Username: monitor");
955
998
  console.log(` Password: ${newPassword}`);
956
- console.log("\nRestart Grafana to apply:");
957
- console.log(" postgres-ai mon restart grafana");
999
+ console.log("\nReset Grafana to apply new password:");
1000
+ console.log(" postgres-ai mon reset grafana");
958
1001
  } catch (error) {
959
1002
  const message = error instanceof Error ? error.message : String(error);
960
1003
  console.error(`Failed to generate password: ${message}`);
@@ -1002,5 +1045,51 @@ mon
1002
1045
  console.log("");
1003
1046
  });
1004
1047
 
1048
+ // Issues management
1049
+ const issues = program.command("issues").description("issues management");
1050
+
1051
+ issues
1052
+ .command("list")
1053
+ .description("list issues")
1054
+ .option("--debug", "enable debug output")
1055
+ .action(async (opts: { debug?: boolean }) => {
1056
+ try {
1057
+ const rootOpts = program.opts<CliOptions>();
1058
+ const cfg = config.readConfig();
1059
+ const { apiKey } = getConfig(rootOpts);
1060
+ if (!apiKey) {
1061
+ console.error("API key is required. Run 'pgai auth' first or set --api-key.");
1062
+ process.exitCode = 1;
1063
+ return;
1064
+ }
1065
+
1066
+ const { apiBaseUrl } = resolveBaseUrls(rootOpts, cfg);
1067
+
1068
+ const result = await fetchIssues({ apiKey, apiBaseUrl, debug: !!opts.debug });
1069
+ if (typeof result === "string") {
1070
+ process.stdout.write(result);
1071
+ if (!/\n$/.test(result)) console.log();
1072
+ } else {
1073
+ console.log(JSON.stringify(result, null, 2));
1074
+ }
1075
+ } catch (err) {
1076
+ const message = err instanceof Error ? err.message : String(err);
1077
+ console.error(message);
1078
+ process.exitCode = 1;
1079
+ }
1080
+ });
1081
+
1082
+ // MCP server
1083
+ const mcp = program.command("mcp").description("MCP server integration");
1084
+
1085
+ mcp
1086
+ .command("start")
1087
+ .description("start MCP stdio server")
1088
+ .option("--debug", "enable debug output")
1089
+ .action(async (opts: { debug?: boolean }) => {
1090
+ const rootOpts = program.opts<CliOptions>();
1091
+ await startMcpServer(rootOpts, { debug: !!opts.debug });
1092
+ });
1093
+
1005
1094
  program.parseAsync(process.argv);
1006
1095
 
@@ -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
  /**
@@ -602,8 +605,8 @@ program
602
605
  // Generate PKCE parameters
603
606
  const params = pkce.generatePKCEParams();
604
607
  const rootOpts = program.opts();
605
- const apiBaseUrl = (rootOpts.apiBaseUrl || process.env.PGAI_API_BASE_URL || "https://postgres.ai/api/general/").replace(/\/$/, "");
606
- 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);
607
610
  if (opts.debug) {
608
611
  console.log(`Debug: Resolved API base URL: ${apiBaseUrl}`);
609
612
  console.log(`Debug: Resolved UI base URL: ${uiBaseUrl}`);
@@ -612,7 +615,7 @@ program
612
615
  // Step 1: Start local callback server FIRST to get actual port
613
616
  console.log("Starting local callback server...");
614
617
  const requestedPort = opts.port || 0; // 0 = OS assigns available port
615
- const callbackServer = authServer.createCallbackServer(requestedPort, params.state, 300000);
618
+ const callbackServer = authServer.createCallbackServer(requestedPort, params.state, 120000); // 2 minute timeout
616
619
  // Wait a bit for server to start and get port
617
620
  await new Promise(resolve => setTimeout(resolve, 100));
618
621
  const actualPort = callbackServer.getPort();
@@ -645,10 +648,19 @@ program
645
648
  res.on("end", async () => {
646
649
  if (res.statusCode !== 200) {
647
650
  console.error(`Failed to initialize auth session: ${res.statusCode}`);
648
- 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
+ }
649
662
  callbackServer.server.close();
650
- process.exitCode = 1;
651
- return;
663
+ process.exit(1);
652
664
  }
653
665
  // Step 3: Open browser
654
666
  const authUrl = `${uiBaseUrl}/cli/auth?state=${encodeURIComponent(params.state)}&code_challenge=${encodeURIComponent(params.codeChallenge)}&code_challenge_method=S256&redirect_uri=${encodeURIComponent(redirectUri)}`;
@@ -664,8 +676,18 @@ program
664
676
  (0, child_process_1.spawn)(openCommand, [authUrl], { detached: true, stdio: "ignore" }).unref();
665
677
  // Step 4: Wait for callback
666
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);
667
687
  try {
668
688
  const { code } = await callbackServer.promise;
689
+ // Remove the cancel handler after successful auth
690
+ process.off("SIGINT", cancelHandler);
669
691
  // Step 5: Exchange code for token
670
692
  console.log("\nExchanging authorization code for API token...");
671
693
  const exchangeData = JSON.stringify({
@@ -681,19 +703,29 @@ program
681
703
  "Content-Length": Buffer.byteLength(exchangeData),
682
704
  },
683
705
  }, (exchangeRes) => {
684
- let exchangeData = "";
685
- exchangeRes.on("data", (chunk) => (exchangeData += chunk));
706
+ let exchangeBody = "";
707
+ exchangeRes.on("data", (chunk) => (exchangeBody += chunk));
686
708
  exchangeRes.on("end", () => {
687
709
  if (exchangeRes.statusCode !== 200) {
688
710
  console.error(`Failed to exchange code for token: ${exchangeRes.statusCode}`);
689
- console.error(exchangeData);
690
- 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);
691
723
  return;
692
724
  }
693
725
  try {
694
- const result = JSON.parse(exchangeData);
695
- const apiToken = result.api_token;
696
- 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.
697
729
  // Step 6: Save token to config
698
730
  config.writeConfig({
699
731
  apiKey: apiToken,
@@ -704,32 +736,43 @@ program
704
736
  console.log(`API key saved to: ${config.getConfigPath()}`);
705
737
  console.log(`Organization ID: ${orgId}`);
706
738
  console.log(`\nYou can now use the CLI without specifying an API key.`);
739
+ process.exit(0);
707
740
  }
708
741
  catch (err) {
709
742
  const message = err instanceof Error ? err.message : String(err);
710
743
  console.error(`Failed to parse response: ${message}`);
711
- process.exitCode = 1;
744
+ process.exit(1);
712
745
  }
713
746
  });
714
747
  });
715
748
  exchangeReq.on("error", (err) => {
716
749
  console.error(`Exchange request failed: ${err.message}`);
717
- process.exitCode = 1;
750
+ process.exit(1);
718
751
  });
719
752
  exchangeReq.write(exchangeData);
720
753
  exchangeReq.end();
721
754
  }
722
755
  catch (err) {
756
+ // Remove the cancel handler in error case too
757
+ process.off("SIGINT", cancelHandler);
723
758
  const message = err instanceof Error ? err.message : String(err);
724
- console.error(`\nAuthentication failed: ${message}`);
725
- 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);
726
769
  }
727
770
  });
728
771
  });
729
772
  initReq.on("error", (err) => {
730
773
  console.error(`Failed to connect to API: ${err.message}`);
731
774
  callbackServer.server.close();
732
- process.exitCode = 1;
775
+ process.exit(1);
733
776
  });
734
777
  initReq.write(initData);
735
778
  initReq.end();
@@ -737,7 +780,7 @@ program
737
780
  catch (err) {
738
781
  const message = err instanceof Error ? err.message : String(err);
739
782
  console.error(`Authentication error: ${message}`);
740
- process.exitCode = 1;
783
+ process.exit(1);
741
784
  }
742
785
  });
743
786
  program
@@ -757,15 +800,8 @@ program
757
800
  console.log(`\nTo authenticate, run: pgai auth`);
758
801
  return;
759
802
  }
760
- const mask = (k) => {
761
- if (k.length <= 8)
762
- return "****";
763
- if (k.length <= 16)
764
- return `${k.slice(0, 4)}${"*".repeat(k.length - 8)}${k.slice(-4)}`;
765
- // For longer keys, show more of the beginning to help identify them
766
- return `${k.slice(0, Math.min(12, k.length - 8))}${"*".repeat(Math.max(4, k.length - 16))}${k.slice(-4)}`;
767
- };
768
- console.log(`Current API key: ${mask(cfg.apiKey)}`);
803
+ const { maskSecret } = require("../lib/util");
804
+ console.log(`Current API key: ${maskSecret(cfg.apiKey)}`);
769
805
  if (cfg.orgId) {
770
806
  console.log(`Organization ID: ${cfg.orgId}`);
771
807
  }
@@ -842,8 +878,8 @@ mon
842
878
  console.log(" URL: http://localhost:3000");
843
879
  console.log(" Username: monitor");
844
880
  console.log(` Password: ${newPassword}`);
845
- console.log("\nRestart Grafana to apply:");
846
- console.log(" postgres-ai mon restart grafana");
881
+ console.log("\nReset Grafana to apply new password:");
882
+ console.log(" postgres-ai mon reset grafana");
847
883
  }
848
884
  catch (error) {
849
885
  const message = error instanceof Error ? error.message : String(error);
@@ -889,5 +925,48 @@ mon
889
925
  console.log(` Password: ${password}`);
890
926
  console.log("");
891
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
+ });
892
971
  program.parseAsync(process.argv);
893
972
  //# sourceMappingURL=postgres-ai.js.map