@cephalization/phoenix-insight 0.3.0 ā 1.0.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 +195 -1
- package/dist/agent/index.js +9 -4
- package/dist/cli.js +172 -0
- package/dist/commands/index.js +1 -0
- package/dist/commands/report-tool.js +239 -0
- package/dist/config/schema.js +2 -2
- package/dist/modes/local.js +7 -0
- package/dist/modes/sandbox.js +8 -0
- package/dist/prompts/index.js +1 -1
- package/dist/prompts/system.js +10 -3
- package/dist/server/session.js +357 -0
- package/dist/server/ui.js +232 -0
- package/dist/server/websocket.js +212 -0
- package/dist/snapshot/spans.js +28 -4
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/ui/assets/code-block-F6WJLWQG-BTdTzfvl.js +154 -0
- package/dist/ui/assets/code-block-F6WJLWQG-BTdTzfvl.js.map +1 -0
- package/dist/ui/assets/index-CX8aDatf.css +1 -0
- package/dist/ui/assets/index-DjZuAW6Y.js +63 -0
- package/dist/ui/assets/index-DjZuAW6Y.js.map +1 -0
- package/dist/ui/assets/vendor-data-r1ZEkUds.js +40 -0
- package/dist/ui/assets/vendor-data-r1ZEkUds.js.map +1 -0
- package/dist/ui/assets/vendor-react-Cgg2GOmP.js +2 -0
- package/dist/ui/assets/vendor-react-Cgg2GOmP.js.map +1 -0
- package/dist/ui/assets/vendor-render-DoMl5bum.js +381 -0
- package/dist/ui/assets/vendor-render-DoMl5bum.js.map +1 -0
- package/dist/ui/assets/vendor-ui-Cg-YC4hK.js +46 -0
- package/dist/ui/assets/vendor-ui-Cg-YC4hK.js.map +1 -0
- package/dist/ui/index.html +18 -0
- package/dist/ui/vite.svg +1 -0
- package/package.json +13 -14
- package/src/agent/index.ts +0 -323
- package/src/cli.ts +0 -854
- package/src/commands/index.ts +0 -8
- package/src/commands/px-fetch-more-spans.ts +0 -174
- package/src/commands/px-fetch-more-trace.ts +0 -183
- package/src/config/index.ts +0 -225
- package/src/config/loader.ts +0 -173
- package/src/config/schema.ts +0 -66
- package/src/index.ts +0 -1
- package/src/modes/index.ts +0 -21
- package/src/modes/local.ts +0 -163
- package/src/modes/sandbox.ts +0 -144
- package/src/modes/types.ts +0 -31
- package/src/observability/index.ts +0 -90
- package/src/progress.ts +0 -239
- package/src/prompts/index.ts +0 -1
- package/src/prompts/system.ts +0 -31
- package/src/snapshot/client.ts +0 -129
- package/src/snapshot/context.ts +0 -587
- package/src/snapshot/datasets.ts +0 -132
- package/src/snapshot/experiments.ts +0 -246
- package/src/snapshot/index.ts +0 -403
- package/src/snapshot/projects.ts +0 -58
- package/src/snapshot/prompts.ts +0 -267
- package/src/snapshot/spans.ts +0 -142
- package/src/snapshot/utils.ts +0 -140
package/README.md
CHANGED
|
@@ -148,10 +148,33 @@ phoenix-insight --refresh "show me the latest errors"
|
|
|
148
148
|
phoenix-insight --no-stream "generate report" > report.txt
|
|
149
149
|
```
|
|
150
150
|
|
|
151
|
+
### Web UI
|
|
152
|
+
|
|
153
|
+
Start the web-based UI for a visual chat interface and structured report display:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
# Start web UI (opens browser automatically)
|
|
157
|
+
phoenix-insight ui
|
|
158
|
+
|
|
159
|
+
# Start on a custom port
|
|
160
|
+
phoenix-insight ui --port 8080
|
|
161
|
+
|
|
162
|
+
# Start without opening browser
|
|
163
|
+
phoenix-insight ui --no-open
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The web UI provides:
|
|
167
|
+
|
|
168
|
+
- **Split-pane interface**: Chat panel on the left, report display on the right
|
|
169
|
+
- **Real-time streaming**: See agent responses and tool usage as they happen
|
|
170
|
+
- **Structured reports**: AI-generated reports with tables, metrics, and formatted content
|
|
171
|
+
- **Session history**: Persist and browse previous conversations and reports
|
|
172
|
+
- **WebSocket connection**: Real-time bidirectional communication with the agent
|
|
173
|
+
|
|
151
174
|
### Observability
|
|
152
175
|
|
|
153
176
|
```bash
|
|
154
|
-
# Enable tracing of agent operations to Phoenix
|
|
177
|
+
# Enable tracing of agent operations to Phoenix (enabled by default)
|
|
155
178
|
phoenix-insight --trace "analyze performance"
|
|
156
179
|
```
|
|
157
180
|
|
|
@@ -398,6 +421,134 @@ phoenix-insight prune [options]
|
|
|
398
421
|
| ----------- | ---------------------------------------- | ------- | ------------------------------- |
|
|
399
422
|
| `--dry-run` | Preview what would be deleted without actually deleting | `false` | `phoenix-insight prune --dry-run` |
|
|
400
423
|
|
|
424
|
+
### UI Command
|
|
425
|
+
|
|
426
|
+
Starts a web-based UI for visual interaction with the Phoenix Insight agent.
|
|
427
|
+
|
|
428
|
+
```bash
|
|
429
|
+
phoenix-insight ui [options]
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
| Option | Description | Default | Example |
|
|
433
|
+
| --------------- | ------------------------------------------------ | ------- | -------------------------------- |
|
|
434
|
+
| `--port <n>` | Port to run the UI server on | `6007` | `phoenix-insight ui --port 8080` |
|
|
435
|
+
| `--no-open` | Do not automatically open browser | `false` | `phoenix-insight ui --no-open` |
|
|
436
|
+
|
|
437
|
+
The UI server provides:
|
|
438
|
+
|
|
439
|
+
- **HTTP server**: Serves the web UI on `http://localhost:6007` (configurable port)
|
|
440
|
+
- **WebSocket**: Real-time bidirectional communication at `/ws`
|
|
441
|
+
- **Session management**: Multiple concurrent sessions with conversation history
|
|
442
|
+
- **Report generation**: Agent can generate structured reports displayed in the UI
|
|
443
|
+
|
|
444
|
+
**Usage notes:**
|
|
445
|
+
|
|
446
|
+
- The server binds to localhost only (127.0.0.1) for security
|
|
447
|
+
- Press Ctrl+C to stop the server gracefully
|
|
448
|
+
- Configuration is loaded from the standard config file and environment variables
|
|
449
|
+
|
|
450
|
+
#### Web UI Features
|
|
451
|
+
|
|
452
|
+
The web UI provides a rich interface for interacting with Phoenix Insight:
|
|
453
|
+
|
|
454
|
+
**Split-pane Layout:**
|
|
455
|
+
- Left panel: Chat interface with message history
|
|
456
|
+
- Right panel: Structured report display
|
|
457
|
+
- Resizable panels with drag handle
|
|
458
|
+
- Responsive design: stacked tabs on mobile devices
|
|
459
|
+
|
|
460
|
+
**Chat Interface:**
|
|
461
|
+
- Real-time streaming of agent responses
|
|
462
|
+
- Markdown rendering optimized for streaming content
|
|
463
|
+
- Session history with dropdown to switch between conversations
|
|
464
|
+
- Create, switch, and delete sessions
|
|
465
|
+
- Visual indicators for agent tool usage (tool_call/tool_result events)
|
|
466
|
+
|
|
467
|
+
**Report Panel:**
|
|
468
|
+
- Structured reports generated by the agent using `generate_report` tool
|
|
469
|
+
- Components: Cards, Tables, Metrics, Alerts, Badges, Lists, Code blocks, and more
|
|
470
|
+
- Download reports as Markdown
|
|
471
|
+
- Browse and restore previous reports from history
|
|
472
|
+
|
|
473
|
+
**Connection Management:**
|
|
474
|
+
- Automatic reconnection with exponential backoff (via partysocket)
|
|
475
|
+
- Visual connection status indicator (green/yellow/red)
|
|
476
|
+
- Toast notifications for connection state changes
|
|
477
|
+
- Message buffering during temporary disconnections
|
|
478
|
+
|
|
479
|
+
**Data Persistence:**
|
|
480
|
+
- Sessions and reports stored in browser's IndexedDB
|
|
481
|
+
- Survives page refreshes and browser restarts
|
|
482
|
+
- Export reports as Markdown files
|
|
483
|
+
|
|
484
|
+
#### WebSocket Protocol
|
|
485
|
+
|
|
486
|
+
The UI communicates with the CLI server over WebSocket at the `/ws` endpoint. All messages are JSON-encoded.
|
|
487
|
+
|
|
488
|
+
**Client Messages (UI to Server):**
|
|
489
|
+
|
|
490
|
+
```typescript
|
|
491
|
+
// Send a query to the agent
|
|
492
|
+
{ type: "query", payload: { content: string, sessionId?: string } }
|
|
493
|
+
|
|
494
|
+
// Cancel the current query
|
|
495
|
+
{ type: "cancel", payload: { sessionId?: string } }
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
**Server Messages (Server to UI):**
|
|
499
|
+
|
|
500
|
+
```typescript
|
|
501
|
+
// Streaming text content from the agent
|
|
502
|
+
{ type: "text", payload: { content: string, sessionId: string } }
|
|
503
|
+
|
|
504
|
+
// Agent is calling a tool
|
|
505
|
+
{ type: "tool_call", payload: { toolName: string, args: unknown, sessionId: string } }
|
|
506
|
+
|
|
507
|
+
// Tool execution result
|
|
508
|
+
{ type: "tool_result", payload: { toolName: string, result: unknown, sessionId: string } }
|
|
509
|
+
|
|
510
|
+
// Agent generated a structured report
|
|
511
|
+
{ type: "report", payload: { content: UITree, sessionId: string } }
|
|
512
|
+
|
|
513
|
+
// Error occurred during processing
|
|
514
|
+
{ type: "error", payload: { message: string, sessionId?: string } }
|
|
515
|
+
|
|
516
|
+
// Agent finished processing the query
|
|
517
|
+
{ type: "done", payload: { sessionId: string } }
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
**Report Content Structure (UITree):**
|
|
521
|
+
|
|
522
|
+
The `report` message contains a `UITree` structure compatible with [json-render](https://github.com/vercel-labs/json-render):
|
|
523
|
+
|
|
524
|
+
```typescript
|
|
525
|
+
interface UITree {
|
|
526
|
+
root: string; // Key of the root element
|
|
527
|
+
elements: Record<string, UIElement>; // Flat map of all elements
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
interface UIElement {
|
|
531
|
+
key: string;
|
|
532
|
+
type: "Card" | "Text" | "Heading" | "List" | "Table" | "Metric" | "Badge" | "Alert" | "Separator" | "Code";
|
|
533
|
+
props: Record<string, unknown>; // Component-specific props
|
|
534
|
+
children?: string[]; // Keys of child elements
|
|
535
|
+
parentKey?: string;
|
|
536
|
+
}
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
**Example WebSocket Session:**
|
|
540
|
+
|
|
541
|
+
```
|
|
542
|
+
Client: {"type":"query","payload":{"content":"Show error patterns","sessionId":"session-1"}}
|
|
543
|
+
Server: {"type":"text","payload":{"content":"I'll analyze","sessionId":"session-1"}}
|
|
544
|
+
Server: {"type":"text","payload":{"content":" the error patterns...","sessionId":"session-1"}}
|
|
545
|
+
Server: {"type":"tool_call","payload":{"toolName":"bash","args":{"command":"grep -c 'error' spans.jsonl"},"sessionId":"session-1"}}
|
|
546
|
+
Server: {"type":"tool_result","payload":{"toolName":"bash","result":"42","sessionId":"session-1"}}
|
|
547
|
+
Server: {"type":"text","payload":{"content":"Found 42 errors.","sessionId":"session-1"}}
|
|
548
|
+
Server: {"type":"report","payload":{"content":{"root":"card-1","elements":{...}},"sessionId":"session-1"}}
|
|
549
|
+
Server: {"type":"done","payload":{"sessionId":"session-1"}}
|
|
550
|
+
```
|
|
551
|
+
|
|
401
552
|
### Help Command
|
|
402
553
|
|
|
403
554
|
Displays help information and available options.
|
|
@@ -691,6 +842,49 @@ pnpm test test/modes/sandbox.test.ts
|
|
|
691
842
|
pnpm typecheck
|
|
692
843
|
```
|
|
693
844
|
|
|
845
|
+
#### UI Integration Testing
|
|
846
|
+
|
|
847
|
+
The web UI has manual integration tests using [agent-browser](https://github.com/vercel-labs/agent-browser) for browser automation. These tests are NOT run in CI because they require a live Phoenix server with data.
|
|
848
|
+
|
|
849
|
+
**Prerequisites:**
|
|
850
|
+
|
|
851
|
+
1. Phoenix server running on `localhost:6006` with data
|
|
852
|
+
2. Run from the monorepo root
|
|
853
|
+
|
|
854
|
+
**Running UI tests:**
|
|
855
|
+
|
|
856
|
+
```bash
|
|
857
|
+
# From monorepo root - builds packages and runs UI integration tests
|
|
858
|
+
pnpm test:ui
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
The test:ui script will:
|
|
862
|
+
1. Build the UI package
|
|
863
|
+
2. Build the CLI package (which bundles the UI)
|
|
864
|
+
3. Expect a running Phoenix server at localhost:6006
|
|
865
|
+
4. Expect the UI server to be running at localhost:6007 (start with `phoenix-insight ui`)
|
|
866
|
+
5. Run browser automation tests that verify:
|
|
867
|
+
- Layout renders correctly (chat panel, report panel)
|
|
868
|
+
- Chat input is functional
|
|
869
|
+
- WebSocket connection works
|
|
870
|
+
- Session management works
|
|
871
|
+
- Report panel displays correctly
|
|
872
|
+
|
|
873
|
+
**Manual testing workflow:**
|
|
874
|
+
|
|
875
|
+
```bash
|
|
876
|
+
# Terminal 1: Start Phoenix
|
|
877
|
+
phoenix serve
|
|
878
|
+
|
|
879
|
+
# Terminal 2: Start the UI server
|
|
880
|
+
cd packages/cli && pnpm dev ui
|
|
881
|
+
|
|
882
|
+
# Terminal 3: Run UI tests
|
|
883
|
+
pnpm test:ui
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
**Note:** If tests skip with "Phoenix server not running" or "UI server not running", ensure both servers are accessible before running tests.
|
|
887
|
+
|
|
694
888
|
### Contributing & Releases
|
|
695
889
|
|
|
696
890
|
Contributions are welcome! This project uses [changesets](https://github.com/changesets/changesets) for version management and automated releases.
|
package/dist/agent/index.js
CHANGED
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
import { generateText, streamText, tool, stepCountIs, } from "ai";
|
|
5
5
|
import { anthropic } from "@ai-sdk/anthropic";
|
|
6
6
|
import { z } from "zod";
|
|
7
|
-
import {
|
|
7
|
+
import { getInsightSystemPrompt } from "../prompts/system.js";
|
|
8
8
|
import { fetchMoreSpans, fetchMoreTrace, } from "../commands/index.js";
|
|
9
|
-
import { PhoenixClientError } from "../snapshot/client.js";
|
|
10
9
|
/**
|
|
11
10
|
* Phoenix Insight Agent
|
|
12
11
|
*/
|
|
@@ -15,11 +14,16 @@ export class PhoenixInsightAgent {
|
|
|
15
14
|
client;
|
|
16
15
|
maxSteps;
|
|
17
16
|
tools = null;
|
|
17
|
+
additionalTools;
|
|
18
18
|
model = anthropic("claude-sonnet-4-5");
|
|
19
|
+
systemPrompt;
|
|
19
20
|
constructor(config) {
|
|
20
21
|
this.mode = config.mode;
|
|
21
22
|
this.client = config.client;
|
|
22
23
|
this.maxSteps = config.maxSteps || 25;
|
|
24
|
+
this.additionalTools = config.additionalTools || {};
|
|
25
|
+
// Generate the system prompt with the snapshot root path from the mode
|
|
26
|
+
this.systemPrompt = getInsightSystemPrompt(this.mode.getSnapshotRoot());
|
|
23
27
|
}
|
|
24
28
|
/**
|
|
25
29
|
* Initialize the agent tools
|
|
@@ -103,6 +107,7 @@ export class PhoenixInsightAgent {
|
|
|
103
107
|
bash: bashTool,
|
|
104
108
|
px_fetch_more_spans: pxFetchMoreSpans,
|
|
105
109
|
px_fetch_more_trace: pxFetchMoreTrace,
|
|
110
|
+
...this.additionalTools,
|
|
106
111
|
};
|
|
107
112
|
return this.tools;
|
|
108
113
|
}
|
|
@@ -120,7 +125,7 @@ export class PhoenixInsightAgent {
|
|
|
120
125
|
try {
|
|
121
126
|
const result = await generateText({
|
|
122
127
|
model: this.model,
|
|
123
|
-
system:
|
|
128
|
+
system: this.systemPrompt,
|
|
124
129
|
prompt: userQuery,
|
|
125
130
|
tools,
|
|
126
131
|
stopWhen: stepCountIs(this.maxSteps),
|
|
@@ -162,7 +167,7 @@ export class PhoenixInsightAgent {
|
|
|
162
167
|
try {
|
|
163
168
|
const result = streamText({
|
|
164
169
|
model: this.model,
|
|
165
|
-
system:
|
|
170
|
+
system: this.systemPrompt,
|
|
166
171
|
prompt: userQuery,
|
|
167
172
|
tools,
|
|
168
173
|
stopWhen: stepCountIs(this.maxSteps),
|
package/dist/cli.js
CHANGED
|
@@ -4,6 +4,7 @@ import * as readline from "node:readline";
|
|
|
4
4
|
import * as fs from "node:fs/promises";
|
|
5
5
|
import * as path from "node:path";
|
|
6
6
|
import * as os from "node:os";
|
|
7
|
+
import { exec } from "node:child_process";
|
|
7
8
|
import { createSandboxMode, createLocalMode } from "./modes/index.js";
|
|
8
9
|
import { createInsightAgent, runOneShotQuery } from "./agent/index.js";
|
|
9
10
|
import { createSnapshot, createIncrementalSnapshot, createPhoenixClient, PhoenixClientError, } from "./snapshot/index.js";
|
|
@@ -11,6 +12,9 @@ import { getLatestSnapshot, listSnapshots } from "./snapshot/utils.js";
|
|
|
11
12
|
import { AgentProgress } from "./progress.js";
|
|
12
13
|
import { initializeObservability, shutdownObservability, } from "./observability/index.js";
|
|
13
14
|
import { initializeConfig, getConfig } from "./config/index.js";
|
|
15
|
+
import { createUIServer } from "./server/ui.js";
|
|
16
|
+
import { createWebSocketServer } from "./server/websocket.js";
|
|
17
|
+
import { createSessionManager } from "./server/session.js";
|
|
14
18
|
// Version will be read from package.json during build
|
|
15
19
|
const VERSION = "0.0.1";
|
|
16
20
|
const program = new Command();
|
|
@@ -164,6 +168,9 @@ Examples:
|
|
|
164
168
|
$ phoenix-insight --local "Show me error patterns" # Local mode with persistence
|
|
165
169
|
$ phoenix-insight --local --stream "Analyze recent experiments" # Local mode with streaming
|
|
166
170
|
$ phoenix-insight --config ./my-config.json "Analyze traces" # Use custom config file
|
|
171
|
+
$ phoenix-insight ui # Start web UI on localhost:6007
|
|
172
|
+
$ phoenix-insight ui --port 8080 # Start web UI on custom port
|
|
173
|
+
$ phoenix-insight ui --no-open # Start web UI without opening browser
|
|
167
174
|
$ phoenix-insight help # Show this help message
|
|
168
175
|
`)
|
|
169
176
|
.hook("preAction", async (thisCommand) => {
|
|
@@ -324,6 +331,15 @@ program
|
|
|
324
331
|
process.exit(1);
|
|
325
332
|
}
|
|
326
333
|
});
|
|
334
|
+
// UI Command - starts web-based UI server
|
|
335
|
+
program
|
|
336
|
+
.command("ui")
|
|
337
|
+
.description("Start the web-based UI for interactive Phoenix analysis")
|
|
338
|
+
.option("--port <number>", "Port to run the UI server on", parseInt)
|
|
339
|
+
.option("--no-open", "Do not automatically open the browser")
|
|
340
|
+
.action(async (options) => {
|
|
341
|
+
await runUIServer(options);
|
|
342
|
+
});
|
|
327
343
|
program
|
|
328
344
|
.argument("[query]", "Query to run against Phoenix data")
|
|
329
345
|
.option("--sandbox", "Run in sandbox mode with in-memory filesystem (default)")
|
|
@@ -474,6 +490,162 @@ program
|
|
|
474
490
|
await shutdownObservability();
|
|
475
491
|
}
|
|
476
492
|
});
|
|
493
|
+
/**
|
|
494
|
+
* Open a URL in the default browser
|
|
495
|
+
*/
|
|
496
|
+
function openBrowser(url) {
|
|
497
|
+
const platform = process.platform;
|
|
498
|
+
let command;
|
|
499
|
+
if (platform === "darwin") {
|
|
500
|
+
command = `open "${url}"`;
|
|
501
|
+
}
|
|
502
|
+
else if (platform === "win32") {
|
|
503
|
+
command = `start "" "${url}"`;
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
// Linux and others - try xdg-open, fallback to sensible-browser
|
|
507
|
+
command = `xdg-open "${url}" || sensible-browser "${url}"`;
|
|
508
|
+
}
|
|
509
|
+
exec(command, (error) => {
|
|
510
|
+
if (error && process.env.DEBUG) {
|
|
511
|
+
console.warn(`Could not open browser: ${error.message}`);
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Run the UI server with WebSocket support for agent interaction
|
|
517
|
+
*/
|
|
518
|
+
async function runUIServer(options) {
|
|
519
|
+
const config = getConfig();
|
|
520
|
+
const port = options.port ?? 6007;
|
|
521
|
+
const shouldOpen = options.open !== false; // Default to opening browser
|
|
522
|
+
console.log("š Starting Phoenix Insight UI...\n");
|
|
523
|
+
// Initialize observability if trace is enabled in config
|
|
524
|
+
if (config.trace) {
|
|
525
|
+
initializeObservability({
|
|
526
|
+
enabled: true,
|
|
527
|
+
baseUrl: config.baseUrl,
|
|
528
|
+
apiKey: config.apiKey,
|
|
529
|
+
projectName: "phoenix-insight-ui",
|
|
530
|
+
debug: !!process.env.DEBUG,
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
try {
|
|
534
|
+
// Determine the execution mode - UI always uses local mode for persistence
|
|
535
|
+
const mode = await createLocalMode();
|
|
536
|
+
// Create Phoenix client
|
|
537
|
+
const client = createPhoenixClient({
|
|
538
|
+
baseURL: config.baseUrl,
|
|
539
|
+
apiKey: config.apiKey,
|
|
540
|
+
});
|
|
541
|
+
// Create snapshot with config values
|
|
542
|
+
const snapshotOptions = {
|
|
543
|
+
baseURL: config.baseUrl,
|
|
544
|
+
apiKey: config.apiKey,
|
|
545
|
+
spansPerProject: config.limit,
|
|
546
|
+
showProgress: true,
|
|
547
|
+
};
|
|
548
|
+
// Always use incremental snapshot for UI to reuse existing data
|
|
549
|
+
await createIncrementalSnapshot(mode, snapshotOptions);
|
|
550
|
+
console.log("\nā
Snapshot ready.\n");
|
|
551
|
+
// Create the UI HTTP server
|
|
552
|
+
const uiServer = await createUIServer({ port, host: "127.0.0.1" });
|
|
553
|
+
// Create the WebSocket server and attach to HTTP server
|
|
554
|
+
const sessionManager = createSessionManager({
|
|
555
|
+
mode,
|
|
556
|
+
client,
|
|
557
|
+
maxSteps: 25,
|
|
558
|
+
});
|
|
559
|
+
const wsServer = createWebSocketServer(uiServer.httpServer, {
|
|
560
|
+
path: "/ws",
|
|
561
|
+
onConnection: (ws) => {
|
|
562
|
+
if (process.env.DEBUG) {
|
|
563
|
+
console.log("WebSocket client connected");
|
|
564
|
+
}
|
|
565
|
+
},
|
|
566
|
+
onDisconnection: async (ws, code, reason) => {
|
|
567
|
+
if (process.env.DEBUG) {
|
|
568
|
+
console.log(`WebSocket client disconnected: ${code} ${reason}`);
|
|
569
|
+
}
|
|
570
|
+
// Clean up the session when client disconnects
|
|
571
|
+
await sessionManager.removeSession(ws);
|
|
572
|
+
},
|
|
573
|
+
onMessage: async (message, ws) => {
|
|
574
|
+
if (message.type === "query") {
|
|
575
|
+
const { content, sessionId: clientSessionId } = message.payload;
|
|
576
|
+
const sessionId = clientSessionId ?? `session-${Date.now()}`;
|
|
577
|
+
// Get or create session for this client
|
|
578
|
+
const session = sessionManager.getOrCreateSession(ws, sessionId, (msg) => wsServer.sendToClient(ws, msg));
|
|
579
|
+
// Execute the query (this is async but we don't await - let it stream)
|
|
580
|
+
session.executeQuery(content).catch((error) => {
|
|
581
|
+
console.error("Error executing query:", error);
|
|
582
|
+
wsServer.sendToClient(ws, {
|
|
583
|
+
type: "error",
|
|
584
|
+
payload: {
|
|
585
|
+
message: error instanceof Error
|
|
586
|
+
? error.message
|
|
587
|
+
: "An error occurred while executing the query",
|
|
588
|
+
sessionId,
|
|
589
|
+
},
|
|
590
|
+
});
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
else if (message.type === "cancel") {
|
|
594
|
+
const session = sessionManager.getSessionForClient(ws);
|
|
595
|
+
if (session) {
|
|
596
|
+
session.cancel();
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
},
|
|
600
|
+
onError: (error, ws) => {
|
|
601
|
+
console.error("WebSocket error:", error.message);
|
|
602
|
+
},
|
|
603
|
+
});
|
|
604
|
+
const url = `http://localhost:${uiServer.port}`;
|
|
605
|
+
console.log("š Phoenix Insight UI is running!");
|
|
606
|
+
console.log(` Local: ${url}`);
|
|
607
|
+
console.log("\nš” Press Ctrl+C to stop the server\n");
|
|
608
|
+
// Open browser if not disabled
|
|
609
|
+
if (shouldOpen) {
|
|
610
|
+
openBrowser(url);
|
|
611
|
+
}
|
|
612
|
+
// Handle graceful shutdown
|
|
613
|
+
let isShuttingDown = false;
|
|
614
|
+
const shutdown = async (signal) => {
|
|
615
|
+
if (isShuttingDown)
|
|
616
|
+
return;
|
|
617
|
+
isShuttingDown = true;
|
|
618
|
+
console.log(`\n\nš„ Received ${signal}, shutting down gracefully...`);
|
|
619
|
+
try {
|
|
620
|
+
// Close WebSocket connections first
|
|
621
|
+
await wsServer.close();
|
|
622
|
+
// Close the UI server
|
|
623
|
+
await uiServer.close();
|
|
624
|
+
// Clean up sessions
|
|
625
|
+
await sessionManager.cleanup();
|
|
626
|
+
// Clean up execution mode
|
|
627
|
+
await mode.cleanup();
|
|
628
|
+
// Shutdown observability if enabled
|
|
629
|
+
await shutdownObservability();
|
|
630
|
+
console.log("š Server stopped. Goodbye!");
|
|
631
|
+
process.exit(0);
|
|
632
|
+
}
|
|
633
|
+
catch (error) {
|
|
634
|
+
console.error("Error during shutdown:", error);
|
|
635
|
+
process.exit(1);
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
639
|
+
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
640
|
+
// Keep the process running
|
|
641
|
+
await new Promise(() => {
|
|
642
|
+
// This promise never resolves - server runs until SIGINT/SIGTERM
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
catch (error) {
|
|
646
|
+
handleError(error, "starting UI server");
|
|
647
|
+
}
|
|
648
|
+
}
|
|
477
649
|
async function runInteractiveMode() {
|
|
478
650
|
const config = getConfig();
|
|
479
651
|
console.log("š Phoenix Insight Interactive Mode");
|
package/dist/commands/index.js
CHANGED