@maxiaochao/pi-toolkit 0.1.3
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/CHANGELOG.md +17 -0
- package/README.md +7 -0
- package/extensions/cache-export/index.ts +94 -0
- package/extensions/cache-export/render.ts +570 -0
- package/extensions/cache-export/tau-assets.ts +420 -0
- package/extensions/cache-export/tests/run-tests.mjs +196 -0
- package/extensions/todo.ts +340 -0
- package/package.json +38 -0
package/CHANGELOG.md
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// pi-cache-dashboard extension entry.
|
|
2
|
+
// Registers a single command: /cache_export
|
|
3
|
+
// /cache_export -> writes a tau-style interactive cache dashboard
|
|
4
|
+
// HTML to ~/.cache/pi-cache-dashboard/<name>-<timestamp>.html
|
|
5
|
+
// /cache_export ./report.html -> writes to the given path instead
|
|
6
|
+
// /cache_export --open -> also open it in the browser
|
|
7
|
+
|
|
8
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
11
|
+
import { spawnSync } from "node:child_process";
|
|
12
|
+
import { parseEntries, buildCacheExportHtml } from "./render.ts";
|
|
13
|
+
|
|
14
|
+
const DEFAULT_DIR = join(homedir(), ".cache", "pi-cache-dashboard");
|
|
15
|
+
|
|
16
|
+
function isWsl() {
|
|
17
|
+
try {
|
|
18
|
+
return /microsoft/i.test(readFileSync("/proc/version", "utf8"));
|
|
19
|
+
} catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Open a file in the platform's default handler via spawnSync + argv arrays
|
|
25
|
+
// (no shell, so paths with $, quotes or backticks are safe). Returns true on success.
|
|
26
|
+
function openInBrowser(path) {
|
|
27
|
+
try {
|
|
28
|
+
let file = path;
|
|
29
|
+
if (isWsl()) {
|
|
30
|
+
// WSL has no Linux browser; convert the path and route through Windows.
|
|
31
|
+
const r = spawnSync("wslpath", ["-w", file], { timeout: 10000, encoding: "utf8" });
|
|
32
|
+
if (r.status !== 0 || !r.stdout || !r.stdout.trim()) return false;
|
|
33
|
+
file = r.stdout.trim();
|
|
34
|
+
const s = spawnSync("cmd.exe", ["/c", "start", "", file], { timeout: 15000 });
|
|
35
|
+
return s.status === 0;
|
|
36
|
+
}
|
|
37
|
+
if (process.platform === "darwin") {
|
|
38
|
+
return spawnSync("open", [file], { timeout: 15000 }).status === 0;
|
|
39
|
+
}
|
|
40
|
+
if (process.platform === "win32") {
|
|
41
|
+
return spawnSync("cmd", ["/c", "start", "", file], { timeout: 15000 }).status === 0;
|
|
42
|
+
}
|
|
43
|
+
return spawnSync("xdg-open", [file], { timeout: 15000 }).status === 0;
|
|
44
|
+
} catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function sanitizeName(s) {
|
|
50
|
+
return String(s || "session").replace(/[^a-zA-Z0-9._-]+/g, "-").slice(0, 60) || "session";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function defaultOutputPath(ctx) {
|
|
54
|
+
const info = ctx.sessionManager;
|
|
55
|
+
const sessionId = info.getSessionId ? info.getSessionId() : undefined;
|
|
56
|
+
const sessionFile = info.getSessionFile ? info.getSessionFile() : undefined;
|
|
57
|
+
const name = sessionFile ? sanitizeName(basename(sessionFile, ".jsonl")) : sanitizeName(sessionId || "session");
|
|
58
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19) + "Z";
|
|
59
|
+
return join(DEFAULT_DIR, `${name}-${stamp}.html`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export default function (pi) {
|
|
63
|
+
pi.registerCommand("cache_export", {
|
|
64
|
+
description: "Export a tau-style interactive cache dashboard (hit-rate charts, per-request table, events)",
|
|
65
|
+
handler: async (args, ctx) => {
|
|
66
|
+
const entries = ctx.sessionManager.getEntries();
|
|
67
|
+
if (!entries || entries.length === 0) {
|
|
68
|
+
ctx.ui.notify("No session entries to analyze.", "error");
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const tokens = String(args || "").trim().split(/\s+/).filter(Boolean);
|
|
73
|
+
const doOpen = tokens.includes("--open") || tokens.includes("-o");
|
|
74
|
+
const pathArg = tokens.find((t) => !t.startsWith("-"));
|
|
75
|
+
|
|
76
|
+
const data = parseEntries(entries);
|
|
77
|
+
if (data.requests.length === 0) {
|
|
78
|
+
ctx.ui.notify("No assistant requests with usage data yet.", "error");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const info = ctx.sessionManager;
|
|
82
|
+
const sessionId = info.getSessionId ? info.getSessionId() : undefined;
|
|
83
|
+
const outPath = resolve(pathArg || defaultOutputPath(ctx));
|
|
84
|
+
|
|
85
|
+
mkdirSync(dirname(outPath), { recursive: true });
|
|
86
|
+
writeFileSync(outPath, buildCacheExportHtml(data, { title: "cache export", sessionId }), "utf8");
|
|
87
|
+
ctx.ui.notify(`Cache dashboard: ${outPath}`, "success");
|
|
88
|
+
if (doOpen) {
|
|
89
|
+
const ok = openInBrowser(outPath);
|
|
90
|
+
if (!ok) ctx.ui.notify(`Could not auto-open; open manually: ${outPath}`, "warning");
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
}
|