@ai-setting/roy-plugin-task-show 0.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 +263 -0
- package/dist/collector.d.ts +104 -0
- package/dist/collector.d.ts.map +1 -0
- package/dist/collector.js +247 -0
- package/dist/collector.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +107 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +332 -0
- package/dist/plugin.js.map +1 -0
- package/dist/server.d.ts +79 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +570 -0
- package/dist/server.js.map +1 -0
- package/dist/types.d.ts +96 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +20 -0
- package/dist/types.js.map +1 -0
- package/dist/url-injector.d.ts +53 -0
- package/dist/url-injector.d.ts.map +1 -0
- package/dist/url-injector.js +69 -0
- package/dist/url-injector.js.map +1 -0
- package/package.json +70 -0
- package/plugin.json +62 -0
- package/public/app.js +133 -0
- package/public/index.html +34 -0
- package/public/style.css +240 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Tiny standalone HTTP server for the visualization frontend.
|
|
3
|
+
*
|
|
4
|
+
* We deliberately avoid Express/Fastify to keep the dependency tree of this
|
|
5
|
+
* plugin at zero. Routes:
|
|
6
|
+
* GET / → index of recent task sessions
|
|
7
|
+
* GET /task/:taskId → detail page (mermaid flow + tables)
|
|
8
|
+
* GET /api/sessions → JSON list of sessions
|
|
9
|
+
* GET /api/sessions/:taskId → JSON detail of one session
|
|
10
|
+
* GET /static/* → static frontend assets (CSS/JS)
|
|
11
|
+
*
|
|
12
|
+
* The server is started/stopped via `TaskShowServer.start()` /
|
|
13
|
+
* `.stop()`. Multiple instances cooperate by random port fallback — if the
|
|
14
|
+
* configured port is busy we let the OS pick a free one and log the actual
|
|
15
|
+
* port.
|
|
16
|
+
*/
|
|
17
|
+
import * as fs from "node:fs";
|
|
18
|
+
import * as path from "node:path";
|
|
19
|
+
import * as http from "node:http";
|
|
20
|
+
import { fileURLToPath } from "node:url";
|
|
21
|
+
const PUBLIC_DIR = "public";
|
|
22
|
+
/** Cached __dirname equivalent for ESM. */
|
|
23
|
+
function getDirname(metaUrl) {
|
|
24
|
+
if (metaUrl)
|
|
25
|
+
return path.dirname(fileURLToPath(metaUrl));
|
|
26
|
+
return typeof __dirname !== "undefined" ? __dirname : process.cwd();
|
|
27
|
+
}
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// TaskShowServer
|
|
30
|
+
// ============================================================================
|
|
31
|
+
export class TaskShowServer {
|
|
32
|
+
server = null;
|
|
33
|
+
cfg;
|
|
34
|
+
collector;
|
|
35
|
+
publicRoot;
|
|
36
|
+
logger;
|
|
37
|
+
constructor(opts) {
|
|
38
|
+
this.cfg = opts.cfg;
|
|
39
|
+
this.collector = opts.collector;
|
|
40
|
+
this.publicRoot = resolvePublicDir(opts.publicDir ?? PUBLIC_DIR, opts.metaUrl);
|
|
41
|
+
this.logger = opts.logger ?? console;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Start the listener. The promise resolves once the server is bound — but
|
|
45
|
+
* the caller can race against `getInfo()` immediately afterwards.
|
|
46
|
+
*/
|
|
47
|
+
start() {
|
|
48
|
+
if (this.server) {
|
|
49
|
+
throw new Error("TaskShowServer already started");
|
|
50
|
+
}
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
const requestListener = (req, res) => {
|
|
53
|
+
this.handle(req, res).catch((err) => {
|
|
54
|
+
this.logger.error(`Unhandled error in HTTP handler: ${err}`);
|
|
55
|
+
try {
|
|
56
|
+
res.statusCode = 500;
|
|
57
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
58
|
+
res.end("Internal Server Error");
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// ignore — socket may already be closed
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
const desiredPort = this.cfg.port;
|
|
66
|
+
const server = http.createServer(requestListener);
|
|
67
|
+
server.on("error", (err) => {
|
|
68
|
+
if (err.code === "EADDRINUSE") {
|
|
69
|
+
this.logger.warn(`Port ${desiredPort} busy — falling back to an ephemeral port`);
|
|
70
|
+
// Retry on an OS-assigned port.
|
|
71
|
+
server.listen(0, this.cfg.host, () => {
|
|
72
|
+
const addr = server.address();
|
|
73
|
+
if (typeof addr === "string" || addr === null) {
|
|
74
|
+
reject(new Error("Failed to determine assigned port"));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
finish(server, addr.port, true);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
reject(err);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
server.listen(desiredPort, this.cfg.host, () => {
|
|
85
|
+
const addr = server.address();
|
|
86
|
+
if (typeof addr === "string" || addr === null) {
|
|
87
|
+
reject(new Error("Failed to determine assigned port"));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
finish(server, addr.port, false);
|
|
91
|
+
});
|
|
92
|
+
const finish = (srv, port, portRewritten) => {
|
|
93
|
+
this.server = srv;
|
|
94
|
+
const url = `http://${this.cfg.host === "0.0.0.0" ? "localhost" : this.cfg.host}:${port}/`;
|
|
95
|
+
this.logger.info(`HTTP service listening on ${url}`);
|
|
96
|
+
resolve({ host: this.cfg.host, port, url, server: srv, portRewritten });
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Read-only accessor used by the plugin entry point to compute the per-task
|
|
102
|
+
* URL after the server has been bound.
|
|
103
|
+
*/
|
|
104
|
+
getInfo() {
|
|
105
|
+
if (!this.server)
|
|
106
|
+
return null;
|
|
107
|
+
const addr = this.server.address();
|
|
108
|
+
if (typeof addr === "string" || addr === null)
|
|
109
|
+
return null;
|
|
110
|
+
return {
|
|
111
|
+
host: this.cfg.host,
|
|
112
|
+
port: addr.port,
|
|
113
|
+
url: `http://${this.cfg.host === "0.0.0.0" ? "localhost" : this.cfg.host}:${addr.port}/`,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Stop the listener; safe to call multiple times.
|
|
118
|
+
*/
|
|
119
|
+
stop() {
|
|
120
|
+
return new Promise((resolve) => {
|
|
121
|
+
if (!this.server) {
|
|
122
|
+
resolve();
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const s = this.server;
|
|
126
|
+
this.server = null;
|
|
127
|
+
s.close(() => resolve());
|
|
128
|
+
// Best-effort: tell outstanding sockets to close.
|
|
129
|
+
try {
|
|
130
|
+
// @ts-ignore — `closeAllConnections` is Node 18.2+
|
|
131
|
+
if (typeof s.closeAllConnections === "function") {
|
|
132
|
+
s.closeAllConnections();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// ignore
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
// -----------------------------------------------------------------------
|
|
141
|
+
// HTTP routing
|
|
142
|
+
// -----------------------------------------------------------------------
|
|
143
|
+
async handle(req, res) {
|
|
144
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
145
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
146
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
147
|
+
this.text(res, 405, "Method Not Allowed");
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const pathname = url.pathname;
|
|
151
|
+
// /api/sessions
|
|
152
|
+
if (pathname === "/api/sessions") {
|
|
153
|
+
this.json(res, 200, this.collector.listSessions());
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
// /api/sessions/:taskId
|
|
157
|
+
const apiMatch = pathname.match(/^\/api\/sessions\/([^/]+)$/);
|
|
158
|
+
if (apiMatch) {
|
|
159
|
+
const id = decodeURIComponent(apiMatch[1]);
|
|
160
|
+
const numericId = Number(id);
|
|
161
|
+
if (!Number.isFinite(numericId)) {
|
|
162
|
+
this.text(res, 400, `Invalid task id: ${id}`);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const session = this.collector.getSession(numericId);
|
|
166
|
+
if (!session) {
|
|
167
|
+
this.text(res, 404, `Task ${numericId} not found`);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
this.json(res, 200, session);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
// /task/:taskId
|
|
174
|
+
const taskMatch = pathname.match(/^\/task\/([^/]+)$/);
|
|
175
|
+
if (taskMatch) {
|
|
176
|
+
const id = decodeURIComponent(taskMatch[1]);
|
|
177
|
+
const numericId = Number(id);
|
|
178
|
+
if (!Number.isFinite(numericId)) {
|
|
179
|
+
this.text(res, 400, `Invalid task id: ${id}`);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const session = this.collector.getSession(numericId);
|
|
183
|
+
if (!session) {
|
|
184
|
+
this.sendNotFound(res, numericId);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
this.sendHtml(res, 200, renderTaskPage(session));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
// /static/... (or root with index.html)
|
|
191
|
+
if (pathname === "/" || pathname === "/index.html") {
|
|
192
|
+
this.sendIndex(res);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const staticMatch = pathname.match(/^\/static\/(.+)$/);
|
|
196
|
+
if (staticMatch) {
|
|
197
|
+
this.serveStatic(res, staticMatch[1]);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
this.text(res, 404, "Not Found");
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Render the index page (lists known tasks).
|
|
204
|
+
*/
|
|
205
|
+
sendIndex(res) {
|
|
206
|
+
const sessions = this.collector.listSessions();
|
|
207
|
+
this.sendHtml(res, 200, renderIndexPage(sessions));
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Send a 404 page that still includes nav + helpful hint.
|
|
211
|
+
*/
|
|
212
|
+
sendNotFound(res, taskId) {
|
|
213
|
+
this.sendHtml(res, 200, `<!DOCTYPE html><html><head><meta charset="utf-8"><title>Task #${taskId} not found</title></head>` +
|
|
214
|
+
`<body><h1>Task #${taskId} not found</h1>` +
|
|
215
|
+
`<p>The plugin has not recorded any tool calls for this task yet (or it was evicted).</p>` +
|
|
216
|
+
`<p><a href="/">Back to index</a></p></body></html>`);
|
|
217
|
+
}
|
|
218
|
+
serveStatic(res, relPath) {
|
|
219
|
+
// Normalize against directory traversal
|
|
220
|
+
const safe = path
|
|
221
|
+
.normalize(relPath)
|
|
222
|
+
.replace(/^(\.\.[\/\\])+/, "");
|
|
223
|
+
const target = path.join(this.publicRoot, safe);
|
|
224
|
+
if (!target.startsWith(this.publicRoot)) {
|
|
225
|
+
this.text(res, 403, "Forbidden");
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
fs.readFile(target, (err, data) => {
|
|
229
|
+
if (err) {
|
|
230
|
+
this.text(res, 404, `Not Found: ${relPath}`);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const mime = guessMime(relPath);
|
|
234
|
+
res.statusCode = 200;
|
|
235
|
+
res.setHeader("Content-Type", mime);
|
|
236
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
237
|
+
res.end(data);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
json(res, status, body) {
|
|
241
|
+
const payload = JSON.stringify(body, null, 2);
|
|
242
|
+
res.statusCode = status;
|
|
243
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
244
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
245
|
+
res.end(payload);
|
|
246
|
+
}
|
|
247
|
+
text(res, status, body) {
|
|
248
|
+
res.statusCode = status;
|
|
249
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
250
|
+
res.end(body);
|
|
251
|
+
}
|
|
252
|
+
sendHtml(res, status, html) {
|
|
253
|
+
res.statusCode = status;
|
|
254
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
255
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
256
|
+
res.end(html);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// ============================================================================
|
|
260
|
+
// Rendering (single-file frontend, no external JS bundles)
|
|
261
|
+
// ============================================================================
|
|
262
|
+
/**
|
|
263
|
+
* Escape user-controlled strings before injecting them into HTML.
|
|
264
|
+
* Anything showing up here goes through JSON.stringify as well.
|
|
265
|
+
*/
|
|
266
|
+
function htmlEscape(input) {
|
|
267
|
+
if (input === undefined || input === null)
|
|
268
|
+
return "";
|
|
269
|
+
const s = typeof input === "string" ? input : JSON.stringify(input);
|
|
270
|
+
return s
|
|
271
|
+
.replace(/&/g, "&")
|
|
272
|
+
.replace(/</g, "<")
|
|
273
|
+
.replace(/>/g, ">")
|
|
274
|
+
.replace(/"/g, """)
|
|
275
|
+
.replace(/'/g, "'");
|
|
276
|
+
}
|
|
277
|
+
function fmtTs(ms) {
|
|
278
|
+
try {
|
|
279
|
+
return new Date(ms).toISOString().replace("T", " ").slice(0, 19);
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
return String(ms);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/** Censor obviously sensitive args (API keys, passwords). */
|
|
286
|
+
function redactArgs(args) {
|
|
287
|
+
const clone = {};
|
|
288
|
+
for (const [k, v] of Object.entries(args ?? {})) {
|
|
289
|
+
const lk = k.toLowerCase();
|
|
290
|
+
if (/(api[_-]?key|token|password|pass|secret)/.test(lk)) {
|
|
291
|
+
clone[k] = "***REDACTED***";
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
clone[k] = v;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return clone;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Build the per-task HTML page. The page relies on mermaid.js loaded from a
|
|
301
|
+
* CDN so we don't need to ship a vendored copy.
|
|
302
|
+
*/
|
|
303
|
+
function renderTaskPage(session) {
|
|
304
|
+
const statusBadge = session.status;
|
|
305
|
+
const totalDuration = session.toolCalls.reduce((acc, c) => acc + c.durationMs, 0);
|
|
306
|
+
const toolStats = aggregateStats(session.toolCalls);
|
|
307
|
+
const mermaid = buildMermaidDiagram(session);
|
|
308
|
+
const table = renderToolCallsTable(session);
|
|
309
|
+
const prettyArgs = renderPrettyJson(redactSessionArgs(session.toolCalls));
|
|
310
|
+
return /* html */ `<!DOCTYPE html>
|
|
311
|
+
<html lang="en">
|
|
312
|
+
<head>
|
|
313
|
+
<meta charset="utf-8">
|
|
314
|
+
<title>Task #${session.taskId} — visualization</title>
|
|
315
|
+
<link rel="stylesheet" href="/static/style.css">
|
|
316
|
+
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
|
317
|
+
</head>
|
|
318
|
+
<body>
|
|
319
|
+
<header class="topbar">
|
|
320
|
+
<a href="/" class="back">← All tasks</a>
|
|
321
|
+
<h1>Task #${session.taskId} <span class="badge badge-${statusBadge}">${htmlEscape(statusBadge)}</span></h1>
|
|
322
|
+
<p class="meta">
|
|
323
|
+
Started: ${htmlEscape(fmtTs(session.startedAt))}
|
|
324
|
+
${session.endedAt ? ` · Ended: ${htmlEscape(fmtTs(session.endedAt))}` : " · still running"}
|
|
325
|
+
· ${session.toolCalls.length} tool calls · total ~${totalDuration} ms
|
|
326
|
+
</p>
|
|
327
|
+
${session.title ? `<p class="task-title">${htmlEscape(session.title)}</p>` : ""}
|
|
328
|
+
</header>
|
|
329
|
+
|
|
330
|
+
<section class="panel">
|
|
331
|
+
<h2>📈 Tool frequency</h2>
|
|
332
|
+
<table class="stats">
|
|
333
|
+
<thead><tr><th>Tool</th><th>Calls</th><th>Success</th><th>Fail</th><th>Total ms</th><th>Avg ms</th></tr></thead>
|
|
334
|
+
<tbody>
|
|
335
|
+
${Object.entries(toolStats).map(([name, s]) => `
|
|
336
|
+
<tr>
|
|
337
|
+
<td><code>${htmlEscape(name)}</code></td>
|
|
338
|
+
<td>${s.count}</td>
|
|
339
|
+
<td>${s.success}</td>
|
|
340
|
+
<td>${s.fail}</td>
|
|
341
|
+
<td>${s.totalMs}</td>
|
|
342
|
+
<td>${s.count ? Math.round(s.totalMs / s.count) : 0}</td>
|
|
343
|
+
</tr>`).join("")}
|
|
344
|
+
</tbody>
|
|
345
|
+
</table>
|
|
346
|
+
</section>
|
|
347
|
+
|
|
348
|
+
<section class="panel">
|
|
349
|
+
<h2>🧭 Tool call flow (mermaid)</h2>
|
|
350
|
+
<div class="mermaid" id="mermaid">
|
|
351
|
+
${mermaid}
|
|
352
|
+
</div>
|
|
353
|
+
<p class="hint">Hover a node to inspect the parameters/result preview. Click to expand the JSON.</p>
|
|
354
|
+
</section>
|
|
355
|
+
|
|
356
|
+
<section class="panel">
|
|
357
|
+
<h2>📜 Tool calls (chronological)</h2>
|
|
358
|
+
${table}
|
|
359
|
+
</section>
|
|
360
|
+
|
|
361
|
+
<section class="panel">
|
|
362
|
+
<h2>🔍 Full payload (JSON)</h2>
|
|
363
|
+
<details>
|
|
364
|
+
<summary>Show / hide raw arguments & output previews</summary>
|
|
365
|
+
<pre class="json">${htmlEscape(prettyArgs)}</pre>
|
|
366
|
+
</details>
|
|
367
|
+
</section>
|
|
368
|
+
|
|
369
|
+
<script>
|
|
370
|
+
// Mermaid boot
|
|
371
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
372
|
+
if (window.mermaid) {
|
|
373
|
+
window.mermaid.initialize({ startOnLoad: true, securityLevel: 'loose', theme: 'default' });
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
</script>
|
|
377
|
+
<script src="/static/app.js"></script>
|
|
378
|
+
</body>
|
|
379
|
+
</html>`;
|
|
380
|
+
}
|
|
381
|
+
function renderToolCallsTable(session) {
|
|
382
|
+
if (session.toolCalls.length === 0) {
|
|
383
|
+
return `<p class="empty">No tool calls have been recorded yet for this task.</p>`;
|
|
384
|
+
}
|
|
385
|
+
const rows = session.toolCalls
|
|
386
|
+
.map((call) => {
|
|
387
|
+
const argsJson = JSON.stringify(redactArgs(call.args)).slice(0, 240);
|
|
388
|
+
const preview = call.error
|
|
389
|
+
? (call.error.slice(0, 200) + (call.error.length > 200 ? "…" : ""))
|
|
390
|
+
: (call.outputPreview.slice(0, 240) + (call.outputPreview.length > 240 ? "…" : ""));
|
|
391
|
+
const status = call.success ? "ok" : "fail";
|
|
392
|
+
const attach = call.hasAttachment ? " <span class=\"attach\">📎</span>" : "";
|
|
393
|
+
return `
|
|
394
|
+
<tr class="row-${status}" data-args="${htmlEscape(argsJson)}">
|
|
395
|
+
<td class="seq">${call.sequence}</td>
|
|
396
|
+
<td><code>${htmlEscape(call.toolName)}</code>${attach}</td>
|
|
397
|
+
<td class="status status-${status}">${status}</td>
|
|
398
|
+
<td>${call.durationMs}</td>
|
|
399
|
+
<td>${htmlEscape(fmtTs(call.timestamp))}</td>
|
|
400
|
+
<td class="args">${htmlEscape(argsJson)}</td>
|
|
401
|
+
<td class="result">${htmlEscape(preview)}</td>
|
|
402
|
+
</tr>`;
|
|
403
|
+
})
|
|
404
|
+
.join("");
|
|
405
|
+
return `
|
|
406
|
+
<table class="toolcalls">
|
|
407
|
+
<thead>
|
|
408
|
+
<tr>
|
|
409
|
+
<th>#</th><th>Tool</th><th>Status</th><th>ms</th><th>Time</th><th>Args</th><th>Result preview</th>
|
|
410
|
+
</tr>
|
|
411
|
+
</thead>
|
|
412
|
+
<tbody>${rows}</tbody>
|
|
413
|
+
</table>`;
|
|
414
|
+
}
|
|
415
|
+
function renderIndexPage(sessions) {
|
|
416
|
+
const row = sessions.length === 0
|
|
417
|
+
? `<p class="empty">No tasks recorded yet. Trigger a tool call (e.g. send a message in <code>interactive</code> mode) and refresh this page.</p>`
|
|
418
|
+
: sessions
|
|
419
|
+
.map((s) => {
|
|
420
|
+
const tools = new Set(s.toolCalls.map((c) => c.toolName));
|
|
421
|
+
const totalMs = s.toolCalls.reduce((a, c) => a + c.durationMs, 0);
|
|
422
|
+
return `
|
|
423
|
+
<tr>
|
|
424
|
+
<td><a href="/task/${s.taskId}">#${s.taskId}</a></td>
|
|
425
|
+
<td><span class="badge badge-${s.status}">${htmlEscape(s.status)}</span></td>
|
|
426
|
+
<td>${s.toolCalls.length} calls · ${totalMs} ms</td>
|
|
427
|
+
<td>${htmlEscape(s.title || "(no title)")}</td>
|
|
428
|
+
<td>${htmlEscape(fmtTs(s.startedAt))}</td>
|
|
429
|
+
<td class="tools">${Array.from(tools).map((t) => `<code>${htmlEscape(t)}</code>`).join(" ")}</td>
|
|
430
|
+
</tr>`;
|
|
431
|
+
})
|
|
432
|
+
.join("");
|
|
433
|
+
return /* html */ `<!DOCTYPE html>
|
|
434
|
+
<html lang="en">
|
|
435
|
+
<head>
|
|
436
|
+
<meta charset="utf-8">
|
|
437
|
+
<title>roy-plugin-task-show · index</title>
|
|
438
|
+
<link rel="stylesheet" href="/static/style.css">
|
|
439
|
+
</head>
|
|
440
|
+
<body>
|
|
441
|
+
<header class="topbar">
|
|
442
|
+
<h1>📊 roy-plugin-task-show</h1>
|
|
443
|
+
<p class="meta">${sessions.length} task session(s) collected this run</p>
|
|
444
|
+
</header>
|
|
445
|
+
<section class="panel">
|
|
446
|
+
<table class="sessions">
|
|
447
|
+
<thead>
|
|
448
|
+
<tr><th>Task ID</th><th>Status</th><th>Stats</th><th>Title</th><th>Started</th><th>Tools</th></tr>
|
|
449
|
+
</thead>
|
|
450
|
+
<tbody>${row}</tbody>
|
|
451
|
+
</table>
|
|
452
|
+
</section>
|
|
453
|
+
<section class="panel">
|
|
454
|
+
<h2>How to read this page</h2>
|
|
455
|
+
<ul>
|
|
456
|
+
<li>Click a task ID to open the mermaid flow + tool call table for that task.</li>
|
|
457
|
+
<li>The plugin appends a 🔗 URL to the <strong>last</strong> tool result when the task transitions to <code>completed</code> / <code>failed</code>.</li>
|
|
458
|
+
<li>Sessions are kept in memory only. Restart the host process to clear all data.</li>
|
|
459
|
+
</ul>
|
|
460
|
+
</section>
|
|
461
|
+
</body>
|
|
462
|
+
</html>`;
|
|
463
|
+
}
|
|
464
|
+
function buildMermaidDiagram(session) {
|
|
465
|
+
// Mermaid `flowchart TD` with one node per tool call. We use `subgraph` to
|
|
466
|
+
// group all calls under the task so multiple sessions on one page stay
|
|
467
|
+
// visually separated.
|
|
468
|
+
const lines = [];
|
|
469
|
+
lines.push(`flowchart TD`);
|
|
470
|
+
lines.push(` classDef ok fill:#dcfce7,stroke:#16a34a,color:#064e3b`);
|
|
471
|
+
lines.push(` classDef fail fill:#fee2e2,stroke:#dc2626,color:#7f1d1d`);
|
|
472
|
+
lines.push(` classDef runtime fill:#dbeafe,stroke:#2563eb,color:#1e3a8a`);
|
|
473
|
+
lines.push(` Start([Task #${session.taskId} start]):::runtime`);
|
|
474
|
+
let prev = "Start";
|
|
475
|
+
if (session.toolCalls.length === 0) {
|
|
476
|
+
lines.push(` End([end]):::runtime`);
|
|
477
|
+
lines.push(` Start --> End`);
|
|
478
|
+
return lines.join("\n");
|
|
479
|
+
}
|
|
480
|
+
session.toolCalls.forEach((call) => {
|
|
481
|
+
const nodeId = `n${call.sequence}`;
|
|
482
|
+
const label = `${call.sequence}. ${escapeMermaidLabel(call.toolName)}${call.hasAttachment ? " 📎" : ""}`;
|
|
483
|
+
lines.push(` ${nodeId}["${label}<br/><small>${call.durationMs}ms · ${call.success ? "ok" : "FAIL"}</small>"]`);
|
|
484
|
+
if (call.success) {
|
|
485
|
+
lines.push(` class ${nodeId} ok`);
|
|
486
|
+
}
|
|
487
|
+
else {
|
|
488
|
+
lines.push(` class ${nodeId} fail`);
|
|
489
|
+
}
|
|
490
|
+
lines.push(` ${prev} --> ${nodeId}`);
|
|
491
|
+
prev = nodeId;
|
|
492
|
+
});
|
|
493
|
+
const status = session.status ?? "running";
|
|
494
|
+
const endLabel = status === "failed" ? `❌ Task ${status}` : `✅ Task ${status}`;
|
|
495
|
+
lines.push(` End([${endLabel}]):::runtime`);
|
|
496
|
+
lines.push(` ${prev} --> End`);
|
|
497
|
+
return lines.join("\n");
|
|
498
|
+
}
|
|
499
|
+
function escapeMermaidLabel(s) {
|
|
500
|
+
// Mermaid is sensitive to a few chars. We mostly limit to ASCII and escape
|
|
501
|
+
// brackets/quotes so node labels render safely.
|
|
502
|
+
return s.replace(/[<>"#]/g, "").replace(/[^A-Za-z0-9_.\-]/g, "_");
|
|
503
|
+
}
|
|
504
|
+
function aggregateStats(toolCalls) {
|
|
505
|
+
const out = {};
|
|
506
|
+
for (const c of toolCalls) {
|
|
507
|
+
const cur = out[c.toolName] ?? { count: 0, success: 0, fail: 0, totalMs: 0 };
|
|
508
|
+
cur.count += 1;
|
|
509
|
+
if (c.success)
|
|
510
|
+
cur.success += 1;
|
|
511
|
+
else
|
|
512
|
+
cur.fail += 1;
|
|
513
|
+
cur.totalMs += c.durationMs;
|
|
514
|
+
out[c.toolName] = cur;
|
|
515
|
+
}
|
|
516
|
+
return out;
|
|
517
|
+
}
|
|
518
|
+
function renderPrettyJson(sessions) {
|
|
519
|
+
return JSON.stringify(sessions, null, 2);
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Strip extreme detail when showing per-task JSON dump. We keep the args
|
|
523
|
+
* (redacted) and the output preview.
|
|
524
|
+
*/
|
|
525
|
+
function redactSessionArgs(calls) {
|
|
526
|
+
return calls.map((c) => ({
|
|
527
|
+
sequence: c.sequence,
|
|
528
|
+
toolName: c.toolName,
|
|
529
|
+
args: redactArgs(c.args),
|
|
530
|
+
outputPreview: c.outputPreview,
|
|
531
|
+
durationMs: c.durationMs,
|
|
532
|
+
timestamp: c.timestamp,
|
|
533
|
+
}));
|
|
534
|
+
}
|
|
535
|
+
// ============================================================================
|
|
536
|
+
// Helpers: MIME + path resolution
|
|
537
|
+
// ============================================================================
|
|
538
|
+
function guessMime(relPath) {
|
|
539
|
+
const ext = path.extname(relPath).toLowerCase();
|
|
540
|
+
switch (ext) {
|
|
541
|
+
case ".css":
|
|
542
|
+
return "text/css; charset=utf-8";
|
|
543
|
+
case ".js":
|
|
544
|
+
return "application/javascript; charset=utf-8";
|
|
545
|
+
case ".json":
|
|
546
|
+
return "application/json; charset=utf-8";
|
|
547
|
+
case ".svg":
|
|
548
|
+
return "image/svg+xml";
|
|
549
|
+
case ".png":
|
|
550
|
+
return "image/png";
|
|
551
|
+
case ".ico":
|
|
552
|
+
return "image/x-icon";
|
|
553
|
+
default:
|
|
554
|
+
return "text/plain; charset=utf-8";
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
function resolvePublicDir(input, metaUrl) {
|
|
558
|
+
// Try as-is (absolute or relative to cwd)
|
|
559
|
+
const abs = path.isAbsolute(input) ? input : path.resolve(process.cwd(), input);
|
|
560
|
+
if (fs.existsSync(path.join(abs, "index.html")) || fs.existsSync(path.join(abs, "style.css"))) {
|
|
561
|
+
return abs;
|
|
562
|
+
}
|
|
563
|
+
// Fallback: look for /dist/../public
|
|
564
|
+
const here = getDirname(metaUrl);
|
|
565
|
+
const guess = path.resolve(here, "..", "public");
|
|
566
|
+
if (fs.existsSync(path.join(guess, "style.css")))
|
|
567
|
+
return guess;
|
|
568
|
+
return abs; // may not exist yet — handler will 404 on missing files
|
|
569
|
+
}
|
|
570
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|