@cephalization/phoenix-insight 0.4.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/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 +11 -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 -163
- package/src/snapshot/utils.ts +0 -140
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP server for Phoenix Insight UI.
|
|
3
|
+
* Serves the static build of @cephalization/phoenix-insight-ui package.
|
|
4
|
+
*
|
|
5
|
+
* Binds to localhost only for security (no external network access).
|
|
6
|
+
* Handles SPA routing by serving index.html for non-asset routes.
|
|
7
|
+
*/
|
|
8
|
+
import { createServer } from "node:http";
|
|
9
|
+
import { createReadStream, existsSync, statSync } from "node:fs";
|
|
10
|
+
import { extname, join, resolve } from "node:path";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// MIME Type Mapping
|
|
14
|
+
// ============================================================================
|
|
15
|
+
const MIME_TYPES = {
|
|
16
|
+
".html": "text/html; charset=utf-8",
|
|
17
|
+
".css": "text/css; charset=utf-8",
|
|
18
|
+
".js": "text/javascript; charset=utf-8",
|
|
19
|
+
".mjs": "text/javascript; charset=utf-8",
|
|
20
|
+
".json": "application/json; charset=utf-8",
|
|
21
|
+
".svg": "image/svg+xml",
|
|
22
|
+
".png": "image/png",
|
|
23
|
+
".jpg": "image/jpeg",
|
|
24
|
+
".jpeg": "image/jpeg",
|
|
25
|
+
".gif": "image/gif",
|
|
26
|
+
".ico": "image/x-icon",
|
|
27
|
+
".webp": "image/webp",
|
|
28
|
+
".woff": "font/woff",
|
|
29
|
+
".woff2": "font/woff2",
|
|
30
|
+
".ttf": "font/ttf",
|
|
31
|
+
".eot": "application/vnd.ms-fontobject",
|
|
32
|
+
".map": "application/json",
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Get MIME type for a file extension
|
|
36
|
+
*/
|
|
37
|
+
function getMimeType(filePath) {
|
|
38
|
+
const ext = extname(filePath).toLowerCase();
|
|
39
|
+
return MIME_TYPES[ext] ?? "application/octet-stream";
|
|
40
|
+
}
|
|
41
|
+
// ============================================================================
|
|
42
|
+
// Path Resolution
|
|
43
|
+
// ============================================================================
|
|
44
|
+
/**
|
|
45
|
+
* Resolve the UI package dist directory path.
|
|
46
|
+
*
|
|
47
|
+
* Resolution order:
|
|
48
|
+
* 1. Bundled dist/ui (copied during build, used for npm published package)
|
|
49
|
+
* 2. import.meta.resolve (finds package in node_modules, used for development)
|
|
50
|
+
* 3. Relative path fallback (for development before packages are linked)
|
|
51
|
+
*/
|
|
52
|
+
export function resolveUIDistPath() {
|
|
53
|
+
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
|
54
|
+
// First check for bundled UI dist (copied during CLI build)
|
|
55
|
+
// This is the path when installed from npm
|
|
56
|
+
const bundledPath = resolve(__dirname, "../ui");
|
|
57
|
+
if (existsSync(bundledPath) && existsSync(join(bundledPath, "index.html"))) {
|
|
58
|
+
return bundledPath;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
// Resolve the UI package's package.json using import.meta.resolve
|
|
62
|
+
// import.meta.resolve returns a file URL (file:///...)
|
|
63
|
+
const packageJsonUrl = import.meta.resolve("@cephalization/phoenix-insight-ui/package.json");
|
|
64
|
+
const packageJsonPath = fileURLToPath(packageJsonUrl);
|
|
65
|
+
const packageDir = resolve(packageJsonPath, "..");
|
|
66
|
+
return join(packageDir, "dist");
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Fallback: try to resolve relative to this file (for development)
|
|
70
|
+
return resolve(__dirname, "../../../ui/dist");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// ============================================================================
|
|
74
|
+
// Static File Server
|
|
75
|
+
// ============================================================================
|
|
76
|
+
/**
|
|
77
|
+
* Determine if a path should be served as a static asset.
|
|
78
|
+
* Asset paths have file extensions (e.g., .js, .css, .png).
|
|
79
|
+
* Non-asset paths (routes) should get the SPA index.html.
|
|
80
|
+
*/
|
|
81
|
+
function isAssetPath(urlPath) {
|
|
82
|
+
// Check for file extension in the last segment
|
|
83
|
+
const lastSegment = urlPath.split("/").pop() ?? "";
|
|
84
|
+
return lastSegment.includes(".");
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Sanitize URL path to prevent directory traversal attacks.
|
|
88
|
+
* Returns null if the path is invalid.
|
|
89
|
+
*/
|
|
90
|
+
function sanitizePath(urlPath, basePath) {
|
|
91
|
+
// Decode URI and normalize slashes
|
|
92
|
+
let decoded;
|
|
93
|
+
try {
|
|
94
|
+
decoded = decodeURIComponent(urlPath);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
// Remove query string and hash
|
|
100
|
+
const withoutQuery = decoded.split("?")[0] ?? decoded;
|
|
101
|
+
decoded = withoutQuery.split("#")[0] ?? withoutQuery;
|
|
102
|
+
// Resolve the full path
|
|
103
|
+
const fullPath = resolve(basePath, "." + decoded);
|
|
104
|
+
// Ensure the resolved path is within the base directory
|
|
105
|
+
if (!fullPath.startsWith(basePath)) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
return fullPath;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Create an HTTP server that serves the Phoenix Insight UI.
|
|
112
|
+
*
|
|
113
|
+
* Features:
|
|
114
|
+
* - Serves static files from the UI package dist directory
|
|
115
|
+
* - SPA fallback: non-asset routes serve index.html
|
|
116
|
+
* - Binds to localhost only (127.0.0.1) for security
|
|
117
|
+
* - Proper MIME types for all common web assets
|
|
118
|
+
*
|
|
119
|
+
* @param options - Server configuration options
|
|
120
|
+
* @returns Promise resolving to UIServer instance
|
|
121
|
+
*/
|
|
122
|
+
export function createUIServer(options = {}) {
|
|
123
|
+
const port = options.port ?? 6007;
|
|
124
|
+
const host = options.host ?? "127.0.0.1";
|
|
125
|
+
const distPath = options.distPath ?? resolveUIDistPath();
|
|
126
|
+
// Verify dist directory exists
|
|
127
|
+
if (!existsSync(distPath)) {
|
|
128
|
+
return Promise.reject(new Error(`UI dist directory not found at: ${distPath}\n` +
|
|
129
|
+
"Make sure to build the UI package first: pnpm --filter @cephalization/phoenix-insight-ui build"));
|
|
130
|
+
}
|
|
131
|
+
// Verify index.html exists
|
|
132
|
+
const indexPath = join(distPath, "index.html");
|
|
133
|
+
if (!existsSync(indexPath)) {
|
|
134
|
+
return Promise.reject(new Error(`UI index.html not found at: ${indexPath}\n` +
|
|
135
|
+
"Make sure to build the UI package first: pnpm --filter @cephalization/phoenix-insight-ui build"));
|
|
136
|
+
}
|
|
137
|
+
return new Promise((resolve, reject) => {
|
|
138
|
+
const httpServer = createServer((req, res) => {
|
|
139
|
+
const urlPath = req.url ?? "/";
|
|
140
|
+
// Sanitize the path to prevent directory traversal
|
|
141
|
+
let filePath = sanitizePath(urlPath, distPath);
|
|
142
|
+
if (!filePath) {
|
|
143
|
+
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
144
|
+
res.end("Bad Request");
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
// SPA fallback: if not an asset path, serve index.html
|
|
148
|
+
if (!isAssetPath(urlPath)) {
|
|
149
|
+
filePath = indexPath;
|
|
150
|
+
}
|
|
151
|
+
// Check if file exists
|
|
152
|
+
if (!existsSync(filePath)) {
|
|
153
|
+
// For missing assets, return 404
|
|
154
|
+
// For missing routes, serve index.html (SPA)
|
|
155
|
+
if (isAssetPath(urlPath)) {
|
|
156
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
157
|
+
res.end("Not Found");
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
filePath = indexPath;
|
|
161
|
+
}
|
|
162
|
+
// Get file stats
|
|
163
|
+
let stats;
|
|
164
|
+
try {
|
|
165
|
+
stats = statSync(filePath);
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
169
|
+
res.end("Internal Server Error");
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
// If it's a directory, serve index.html
|
|
173
|
+
if (stats.isDirectory()) {
|
|
174
|
+
filePath = indexPath;
|
|
175
|
+
try {
|
|
176
|
+
stats = statSync(filePath);
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
180
|
+
res.end("Internal Server Error");
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Set response headers
|
|
185
|
+
const mimeType = getMimeType(filePath);
|
|
186
|
+
res.writeHead(200, {
|
|
187
|
+
"Content-Type": mimeType,
|
|
188
|
+
"Content-Length": stats.size,
|
|
189
|
+
"Cache-Control": filePath === indexPath
|
|
190
|
+
? "no-cache" // Don't cache index.html for SPA
|
|
191
|
+
: "public, max-age=31536000", // Cache assets for 1 year
|
|
192
|
+
});
|
|
193
|
+
// Stream the file
|
|
194
|
+
const stream = createReadStream(filePath);
|
|
195
|
+
stream.pipe(res);
|
|
196
|
+
stream.on("error", () => {
|
|
197
|
+
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
198
|
+
res.end("Internal Server Error");
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
// Handle server errors
|
|
202
|
+
httpServer.on("error", (err) => {
|
|
203
|
+
reject(err);
|
|
204
|
+
});
|
|
205
|
+
// Start listening
|
|
206
|
+
httpServer.listen(port, host, () => {
|
|
207
|
+
// Get the actual assigned port (important when port 0 is specified)
|
|
208
|
+
const address = httpServer.address();
|
|
209
|
+
const actualPort = typeof address === "object" && address !== null
|
|
210
|
+
? address.port
|
|
211
|
+
: port;
|
|
212
|
+
resolve({
|
|
213
|
+
httpServer,
|
|
214
|
+
port: actualPort,
|
|
215
|
+
host,
|
|
216
|
+
distPath,
|
|
217
|
+
close() {
|
|
218
|
+
return new Promise((resolveClose, rejectClose) => {
|
|
219
|
+
httpServer.close((err) => {
|
|
220
|
+
if (err) {
|
|
221
|
+
rejectClose(err);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
resolveClose();
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket server for Phoenix Insight CLI.
|
|
3
|
+
* Provides bidirectional communication between the CLI agent and the web UI.
|
|
4
|
+
*
|
|
5
|
+
* Binds to localhost only for security (no external network access).
|
|
6
|
+
* Handles HTTP upgrade requests, manages client connections, and broadcasts messages.
|
|
7
|
+
*/
|
|
8
|
+
import { WebSocketServer, WebSocket } from "ws";
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Phoenix WebSocket Server
|
|
11
|
+
// ============================================================================
|
|
12
|
+
/**
|
|
13
|
+
* Phoenix WebSocket server wrapper providing typed message handling
|
|
14
|
+
* and connection management.
|
|
15
|
+
*/
|
|
16
|
+
export class PhoenixWebSocketServer {
|
|
17
|
+
wss = null;
|
|
18
|
+
clients = new Set();
|
|
19
|
+
options;
|
|
20
|
+
constructor(options = {}) {
|
|
21
|
+
this.options = {
|
|
22
|
+
path: "/ws",
|
|
23
|
+
onMessage: () => { },
|
|
24
|
+
onConnection: () => { },
|
|
25
|
+
onDisconnection: () => { },
|
|
26
|
+
onError: () => { },
|
|
27
|
+
...options,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Attach the WebSocket server to an existing HTTP server.
|
|
32
|
+
* Uses the upgrade event to handle WebSocket handshakes.
|
|
33
|
+
*/
|
|
34
|
+
attach(httpServer) {
|
|
35
|
+
if (this.wss) {
|
|
36
|
+
throw new Error("WebSocket server is already attached");
|
|
37
|
+
}
|
|
38
|
+
// Create WebSocket server with noServer mode to handle upgrade ourselves
|
|
39
|
+
this.wss = new WebSocketServer({ noServer: true });
|
|
40
|
+
// Handle HTTP upgrade requests
|
|
41
|
+
httpServer.on("upgrade", (request, socket, head) => {
|
|
42
|
+
this.handleUpgrade(request, socket, head);
|
|
43
|
+
});
|
|
44
|
+
// Set up WebSocket server event handlers
|
|
45
|
+
this.setupEventHandlers();
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Handle HTTP upgrade request for WebSocket connection.
|
|
49
|
+
* Only accepts connections on the configured path and from localhost.
|
|
50
|
+
*/
|
|
51
|
+
handleUpgrade(request, socket, head) {
|
|
52
|
+
if (!this.wss) {
|
|
53
|
+
socket.destroy();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
// Parse the request URL
|
|
57
|
+
const url = new URL(request.url ?? "/", `http://${request.headers.host}`);
|
|
58
|
+
// Only accept connections on the configured path
|
|
59
|
+
if (url.pathname !== this.options.path) {
|
|
60
|
+
socket.destroy();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
// Complete the WebSocket handshake
|
|
64
|
+
this.wss.handleUpgrade(request, socket, head, (ws) => {
|
|
65
|
+
this.wss?.emit("connection", ws, request);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Set up event handlers for the WebSocket server.
|
|
70
|
+
*/
|
|
71
|
+
setupEventHandlers() {
|
|
72
|
+
if (!this.wss)
|
|
73
|
+
return;
|
|
74
|
+
this.wss.on("connection", (ws) => {
|
|
75
|
+
this.clients.add(ws);
|
|
76
|
+
this.options.onConnection(ws);
|
|
77
|
+
ws.on("message", (data) => {
|
|
78
|
+
this.handleMessage(data, ws);
|
|
79
|
+
});
|
|
80
|
+
ws.on("close", (code, reason) => {
|
|
81
|
+
this.clients.delete(ws);
|
|
82
|
+
this.options.onDisconnection(ws, code, reason.toString());
|
|
83
|
+
});
|
|
84
|
+
ws.on("error", (error) => {
|
|
85
|
+
this.options.onError(error, ws);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
this.wss.on("error", (error) => {
|
|
89
|
+
this.options.onError(error);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Handle incoming message from a client.
|
|
94
|
+
* Parses JSON and validates message structure before calling handler.
|
|
95
|
+
*/
|
|
96
|
+
handleMessage(data, client) {
|
|
97
|
+
try {
|
|
98
|
+
const rawMessage = data.toString();
|
|
99
|
+
const parsed = JSON.parse(rawMessage);
|
|
100
|
+
// Basic validation of message structure
|
|
101
|
+
if (!parsed || typeof parsed !== "object") {
|
|
102
|
+
throw new Error("Invalid message structure: expected object");
|
|
103
|
+
}
|
|
104
|
+
const obj = parsed;
|
|
105
|
+
if (!("type" in obj) || typeof obj.type !== "string") {
|
|
106
|
+
throw new Error("Invalid message structure: missing type field");
|
|
107
|
+
}
|
|
108
|
+
const messageType = obj.type;
|
|
109
|
+
if (messageType !== "query" && messageType !== "cancel") {
|
|
110
|
+
throw new Error(`Unknown message type: ${messageType}`);
|
|
111
|
+
}
|
|
112
|
+
// Now we know this is a valid ClientMessage
|
|
113
|
+
const message = parsed;
|
|
114
|
+
this.options.onMessage(message, client);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
// Send error message back to the client
|
|
118
|
+
const errorMessage = {
|
|
119
|
+
type: "error",
|
|
120
|
+
payload: {
|
|
121
|
+
message: error instanceof Error
|
|
122
|
+
? error.message
|
|
123
|
+
: "Failed to parse message",
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
this.sendToClient(client, errorMessage);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Send a message to a specific client.
|
|
131
|
+
*/
|
|
132
|
+
sendToClient(client, message) {
|
|
133
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
134
|
+
client.send(JSON.stringify(message));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Broadcast a message to all connected clients.
|
|
139
|
+
*/
|
|
140
|
+
broadcast(message) {
|
|
141
|
+
const data = JSON.stringify(message);
|
|
142
|
+
for (const client of this.clients) {
|
|
143
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
144
|
+
client.send(data);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Broadcast a message to all clients with a specific session ID.
|
|
150
|
+
* This requires tracking session-to-client mapping externally.
|
|
151
|
+
* For now, it broadcasts to all clients (to be refined in cli-agent-session).
|
|
152
|
+
*/
|
|
153
|
+
broadcastToSession(sessionId, message) {
|
|
154
|
+
// For now, broadcast to all clients.
|
|
155
|
+
// Session-to-client mapping will be implemented in cli-agent-session task.
|
|
156
|
+
this.broadcast(message);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Get the number of connected clients.
|
|
160
|
+
*/
|
|
161
|
+
get clientCount() {
|
|
162
|
+
return this.clients.size;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Get all connected clients.
|
|
166
|
+
*/
|
|
167
|
+
getClients() {
|
|
168
|
+
return new Set(this.clients);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Close the WebSocket server and disconnect all clients.
|
|
172
|
+
*/
|
|
173
|
+
close() {
|
|
174
|
+
return new Promise((resolve, reject) => {
|
|
175
|
+
if (!this.wss) {
|
|
176
|
+
resolve();
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
// Close all client connections
|
|
180
|
+
for (const client of this.clients) {
|
|
181
|
+
client.close(1000, "Server shutting down");
|
|
182
|
+
}
|
|
183
|
+
this.clients.clear();
|
|
184
|
+
// Close the WebSocket server
|
|
185
|
+
this.wss.close((err) => {
|
|
186
|
+
this.wss = null;
|
|
187
|
+
if (err) {
|
|
188
|
+
reject(err);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
resolve();
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// ============================================================================
|
|
198
|
+
// Factory Function
|
|
199
|
+
// ============================================================================
|
|
200
|
+
/**
|
|
201
|
+
* Create and attach a WebSocket server to an HTTP server.
|
|
202
|
+
* The WebSocket server binds to localhost only (through the HTTP server).
|
|
203
|
+
*
|
|
204
|
+
* @param httpServer - HTTP server to attach WebSocket handling to
|
|
205
|
+
* @param options - WebSocket server options
|
|
206
|
+
* @returns PhoenixWebSocketServer instance
|
|
207
|
+
*/
|
|
208
|
+
export function createWebSocketServer(httpServer, options) {
|
|
209
|
+
const server = new PhoenixWebSocketServer(options);
|
|
210
|
+
server.attach(httpServer);
|
|
211
|
+
return server;
|
|
212
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/cli.ts","../src/index.ts","../src/progress.ts","../src/agent/index.ts","../src/commands/index.ts","../src/commands/px-fetch-more-spans.ts","../src/commands/px-fetch-more-trace.ts","../src/config/index.ts","../src/config/loader.ts","../src/config/schema.ts","../src/modes/index.ts","../src/modes/local.ts","../src/modes/sandbox.ts","../src/modes/types.ts","../src/observability/index.ts","../src/prompts/index.ts","../src/prompts/system.ts","../src/snapshot/client.ts","../src/snapshot/context.ts","../src/snapshot/datasets.ts","../src/snapshot/experiments.ts","../src/snapshot/index.ts","../src/snapshot/projects.ts","../src/snapshot/prompts.ts","../src/snapshot/spans.ts","../src/snapshot/utils.ts"],"version":"5.9.3"}
|
|
1
|
+
{"root":["../src/cli.ts","../src/index.ts","../src/progress.ts","../src/agent/index.ts","../src/commands/index.ts","../src/commands/px-fetch-more-spans.ts","../src/commands/px-fetch-more-trace.ts","../src/commands/report-tool.ts","../src/config/index.ts","../src/config/loader.ts","../src/config/schema.ts","../src/modes/index.ts","../src/modes/local.ts","../src/modes/sandbox.ts","../src/modes/types.ts","../src/observability/index.ts","../src/prompts/index.ts","../src/prompts/system.ts","../src/server/session.ts","../src/server/ui.ts","../src/server/websocket.ts","../src/snapshot/client.ts","../src/snapshot/context.ts","../src/snapshot/datasets.ts","../src/snapshot/experiments.ts","../src/snapshot/index.ts","../src/snapshot/projects.ts","../src/snapshot/prompts.ts","../src/snapshot/spans.ts","../src/snapshot/utils.ts"],"version":"5.9.3"}
|