@cephalization/phoenix-insight 1.0.3 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,6 +6,25 @@ Phoenix Insight brings AI-powered analysis to your [Phoenix](https://github.com/
6
6
 
7
7
  This filesystem-native approach provides transparency that traditional APIs can't match. Every query the agent runs is visible and reproducible. You can inspect the exact files it reads, copy its commands, and run them yourself. The data is just files, and the analysis is just bash, making AI-driven observability debuggable, auditable, and extensible with any tool in your Unix toolkit.
8
8
 
9
+ ## Requirements
10
+
11
+ - **Node.js v22 or newer** - Required for the CLI to run
12
+ - **Anthropic API key** - Required for the AI agent
13
+
14
+ Set your Anthropic API key before running:
15
+
16
+ ```bash
17
+ export ANTHROPIC_API_KEY=sk-ant-api03-...
18
+ ```
19
+
20
+ Or add it to your shell profile for persistence:
21
+
22
+ ```bash
23
+ echo 'export ANTHROPIC_API_KEY=sk-ant-api03-...' >> ~/.zshrc # or ~/.bashrc
24
+ ```
25
+
26
+ You can get an API key from [console.anthropic.com](https://console.anthropic.com/).
27
+
9
28
  ## Installation
10
29
 
11
30
  ```bash
package/dist/cli.js CHANGED
@@ -2709,6 +2709,7 @@ Make sure to build the UI package first: pnpm --filter @cephalization/phoenix-in
2709
2709
  );
2710
2710
  }
2711
2711
  return new Promise((resolve2, reject) => {
2712
+ const activeConnections = /* @__PURE__ */ new Set();
2712
2713
  const httpServer = createServer((req, res) => {
2713
2714
  const urlPath = req.url ?? "/";
2714
2715
  let filePath = sanitizePath(urlPath, distPath);
@@ -2760,6 +2761,12 @@ Make sure to build the UI package first: pnpm --filter @cephalization/phoenix-in
2760
2761
  res.end("Internal Server Error");
2761
2762
  });
2762
2763
  });
2764
+ httpServer.on("connection", (socket) => {
2765
+ activeConnections.add(socket);
2766
+ socket.on("close", () => {
2767
+ activeConnections.delete(socket);
2768
+ });
2769
+ });
2763
2770
  httpServer.on("error", (err) => {
2764
2771
  reject(err);
2765
2772
  });
@@ -2781,6 +2788,12 @@ Make sure to build the UI package first: pnpm --filter @cephalization/phoenix-in
2781
2788
  }
2782
2789
  });
2783
2790
  });
2791
+ },
2792
+ forceClose() {
2793
+ for (const socket of activeConnections) {
2794
+ socket.destroy();
2795
+ }
2796
+ activeConnections.clear();
2784
2797
  }
2785
2798
  });
2786
2799
  });
@@ -2955,6 +2968,19 @@ var PhoenixWebSocketServer = class {
2955
2968
  });
2956
2969
  });
2957
2970
  }
2971
+ /**
2972
+ * Force terminate all WebSocket connections immediately.
2973
+ * Use this when graceful close doesn't complete in time.
2974
+ */
2975
+ forceClose() {
2976
+ if (!this.wss) {
2977
+ return;
2978
+ }
2979
+ for (const client of this.clients) {
2980
+ client.terminate();
2981
+ }
2982
+ this.clients.clear();
2983
+ }
2958
2984
  };
2959
2985
  function createWebSocketServer(httpServer, options) {
2960
2986
  const server = new PhoenixWebSocketServer(options);
@@ -3317,6 +3343,26 @@ function formatBashCommand(command) {
3317
3343
  return firstLine.substring(0, 80) + (firstLine.length > 80 ? "..." : "");
3318
3344
  }
3319
3345
  }
3346
+ function ensureAnthropicApiKey() {
3347
+ if (!process.env.ANTHROPIC_API_KEY) {
3348
+ console.error(
3349
+ "\n\u274C Error: Missing ANTHROPIC_API_KEY environment variable\n"
3350
+ );
3351
+ console.error("The Anthropic API key is required to run the AI agent.\n");
3352
+ console.error("To fix this, set the environment variable:\n");
3353
+ console.error(" export ANTHROPIC_API_KEY=sk-ant-api03-...\n");
3354
+ console.error(
3355
+ "Or add it to your shell profile (~/.zshrc, ~/.bashrc, etc.):\n"
3356
+ );
3357
+ console.error(
3358
+ " echo 'export ANTHROPIC_API_KEY=sk-ant-api03-...' >> ~/.zshrc\n"
3359
+ );
3360
+ console.error(
3361
+ "You can get an API key from: https://console.anthropic.com/\n"
3362
+ );
3363
+ process.exit(1);
3364
+ }
3365
+ }
3320
3366
  function handleError(error, context) {
3321
3367
  console.error(`
3322
3368
  \u274C Error ${context}:`);
@@ -3401,16 +3447,17 @@ Configuration:
3401
3447
  Set PHOENIX_INSIGHT_CONFIG env var to override the default config location.
3402
3448
 
3403
3449
  Examples:
3404
- $ phoenix-insight # Start interactive mode
3405
- $ phoenix-insight "What are the slowest traces?" # Single query (sandbox mode)
3406
- $ phoenix-insight --interactive # Explicitly start interactive mode
3407
- $ phoenix-insight --local "Show me error patterns" # Local mode with persistence
3408
- $ phoenix-insight --local --stream "Analyze recent experiments" # Local mode with streaming
3409
- $ phoenix-insight --config ./my-config.json "Analyze traces" # Use custom config file
3410
- $ phoenix-insight ui # Start web UI on localhost:6007
3411
- $ phoenix-insight ui --port 8080 # Start web UI on custom port
3412
- $ phoenix-insight ui --no-open # Start web UI without opening browser
3413
- $ phoenix-insight help # Show this help message
3450
+ $ phoenix-insight # Start interactive mode
3451
+ $ phoenix-insight "What are the slowest traces?" # Single query (sandbox mode)
3452
+ $ phoenix-insight --interactive # Explicitly start interactive mode
3453
+ $ phoenix-insight --local "Show me error patterns" # Local mode with persistence
3454
+ $ phoenix-insight --local --stream "Analyze recent experiments" # Local mode with streaming
3455
+ $ phoenix-insight --config ./my-config.json "Analyze traces" # Use custom config file
3456
+ $ phoenix-insight ui # Start web UI on localhost:6007
3457
+ $ phoenix-insight ui --port 8080 # Start web UI on custom port
3458
+ $ phoenix-insight ui --no-open # Start web UI without opening browser
3459
+ $ opencode run "Analyze my spans" -f $(pxi snapshot latest)/_context.md # Analyze phoenix data with OpenCode agent
3460
+ $ phoenix-insight help # Show this help message
3414
3461
  `
3415
3462
  ).hook("preAction", async (thisCommand) => {
3416
3463
  const opts = thisCommand.opts();
@@ -3560,6 +3607,7 @@ program.argument("[query]", "Query to run against Phoenix data").option(
3560
3607
  await runInteractiveMode();
3561
3608
  return;
3562
3609
  }
3610
+ ensureAnthropicApiKey();
3563
3611
  if (config.trace) {
3564
3612
  initializeObservability({
3565
3613
  enabled: true,
@@ -3682,6 +3730,7 @@ function openBrowser(url) {
3682
3730
  });
3683
3731
  }
3684
3732
  async function runUIServer(options) {
3733
+ ensureAnthropicApiKey();
3685
3734
  const config = getConfig();
3686
3735
  const port = options.port ?? 6007;
3687
3736
  const shouldOpen = options.open !== false;
@@ -3766,21 +3815,30 @@ async function runUIServer(options) {
3766
3815
  openBrowser(url);
3767
3816
  }
3768
3817
  let isShuttingDown = false;
3818
+ const SHUTDOWN_TIMEOUT_MS = 3e3;
3769
3819
  const shutdown = async (signal) => {
3770
3820
  if (isShuttingDown) return;
3771
3821
  isShuttingDown = true;
3772
3822
  console.log(`
3773
3823
 
3774
3824
  \u{1F4E5} Received ${signal}, shutting down gracefully...`);
3825
+ const forceExitTimeout = setTimeout(() => {
3826
+ console.log("\u23F1\uFE0F Shutdown timeout reached, forcing exit...");
3827
+ wsServer.forceClose();
3828
+ uiServer.forceClose();
3829
+ process.exit(0);
3830
+ }, SHUTDOWN_TIMEOUT_MS);
3775
3831
  try {
3776
3832
  await wsServer.close();
3777
3833
  await uiServer.close();
3778
3834
  await sessionManager.cleanup();
3779
3835
  await mode.cleanup();
3780
3836
  await shutdownObservability();
3837
+ clearTimeout(forceExitTimeout);
3781
3838
  console.log("\u{1F44B} Server stopped. Goodbye!");
3782
3839
  process.exit(0);
3783
3840
  } catch (error) {
3841
+ clearTimeout(forceExitTimeout);
3784
3842
  console.error("Error during shutdown:", error);
3785
3843
  process.exit(1);
3786
3844
  }
@@ -3794,6 +3852,7 @@ async function runUIServer(options) {
3794
3852
  }
3795
3853
  }
3796
3854
  async function runInteractiveMode() {
3855
+ ensureAnthropicApiKey();
3797
3856
  const config = getConfig();
3798
3857
  console.log("\u{1F680} Phoenix Insight Interactive Mode");
3799
3858
  console.log(