@zephyrdeng/pi-review 0.7.1 → 0.8.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.
Files changed (176) hide show
  1. package/README.md +37 -8
  2. package/README.zh-CN.md +31 -5
  3. package/dist/args.d.ts +4 -0
  4. package/dist/args.d.ts.map +1 -1
  5. package/dist/args.js +61 -4
  6. package/dist/args.js.map +1 -1
  7. package/dist/args.test.js +62 -1
  8. package/dist/args.test.js.map +1 -1
  9. package/dist/child-process.d.ts +3 -0
  10. package/dist/child-process.d.ts.map +1 -1
  11. package/dist/child-process.js +27 -5
  12. package/dist/child-process.js.map +1 -1
  13. package/dist/child-process.test.js +42 -1
  14. package/dist/child-process.test.js.map +1 -1
  15. package/dist/cli-exit.test.js +14 -5
  16. package/dist/cli-exit.test.js.map +1 -1
  17. package/dist/cli.js +8 -3
  18. package/dist/cli.js.map +1 -1
  19. package/dist/index.d.ts +7 -5
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +5 -4
  22. package/dist/index.js.map +1 -1
  23. package/dist/install-args.test.js +1 -1
  24. package/dist/install-args.test.js.map +1 -1
  25. package/dist/install.d.ts.map +1 -1
  26. package/dist/install.js +2 -3
  27. package/dist/install.js.map +1 -1
  28. package/dist/json-events.d.ts +6 -0
  29. package/dist/json-events.d.ts.map +1 -1
  30. package/dist/json-events.js +48 -6
  31. package/dist/json-events.js.map +1 -1
  32. package/dist/json-events.test.js +20 -1
  33. package/dist/json-events.test.js.map +1 -1
  34. package/dist/loop.d.ts +28 -2
  35. package/dist/loop.d.ts.map +1 -1
  36. package/dist/loop.js +45 -3
  37. package/dist/loop.js.map +1 -1
  38. package/dist/loop.test.js +24 -1
  39. package/dist/loop.test.js.map +1 -1
  40. package/dist/matcher.test.js +1 -1
  41. package/dist/matcher.test.js.map +1 -1
  42. package/dist/meta-footer.d.ts +4 -8
  43. package/dist/meta-footer.d.ts.map +1 -1
  44. package/dist/meta-footer.js +19 -5
  45. package/dist/meta-footer.js.map +1 -1
  46. package/dist/meta-footer.test.js +11 -4
  47. package/dist/meta-footer.test.js.map +1 -1
  48. package/dist/panel-aggregate.test.js +1 -1
  49. package/dist/panel-aggregate.test.js.map +1 -1
  50. package/dist/panel-cancellation.test.js +33 -2
  51. package/dist/panel-cancellation.test.js.map +1 -1
  52. package/dist/panel-cli-ui.test.d.ts +2 -0
  53. package/dist/panel-cli-ui.test.d.ts.map +1 -0
  54. package/dist/panel-cli-ui.test.js +91 -0
  55. package/dist/panel-cli-ui.test.js.map +1 -0
  56. package/dist/panel-cli.test.js +37 -39
  57. package/dist/panel-cli.test.js.map +1 -1
  58. package/dist/panel-config.d.ts +38 -0
  59. package/dist/panel-config.d.ts.map +1 -1
  60. package/dist/panel-config.js +135 -2
  61. package/dist/panel-config.js.map +1 -1
  62. package/dist/panel-config.test.js +79 -2
  63. package/dist/panel-config.test.js.map +1 -1
  64. package/dist/panel-loop.test.js +1 -1
  65. package/dist/panel-loop.test.js.map +1 -1
  66. package/dist/panel-tools.test.js +1 -1
  67. package/dist/panel-tools.test.js.map +1 -1
  68. package/dist/panel-ui.d.ts +20 -0
  69. package/dist/panel-ui.d.ts.map +1 -0
  70. package/dist/panel-ui.js +84 -0
  71. package/dist/panel-ui.js.map +1 -0
  72. package/dist/panel-ui.test.d.ts +2 -0
  73. package/dist/panel-ui.test.d.ts.map +1 -0
  74. package/dist/panel-ui.test.js +122 -0
  75. package/dist/panel-ui.test.js.map +1 -0
  76. package/dist/panel-view.d.ts +1 -0
  77. package/dist/panel-view.d.ts.map +1 -1
  78. package/dist/panel-view.js +40 -6
  79. package/dist/panel-view.js.map +1 -1
  80. package/dist/panel-view.test.js +68 -1
  81. package/dist/panel-view.test.js.map +1 -1
  82. package/dist/panel.d.ts +11 -1
  83. package/dist/panel.d.ts.map +1 -1
  84. package/dist/panel.js +67 -21
  85. package/dist/panel.js.map +1 -1
  86. package/dist/prompt.d.ts +2 -0
  87. package/dist/prompt.d.ts.map +1 -1
  88. package/dist/prompt.js +34 -3
  89. package/dist/prompt.js.map +1 -1
  90. package/dist/prompt.test.js +21 -2
  91. package/dist/prompt.test.js.map +1 -1
  92. package/dist/review-events.test.js +1 -1
  93. package/dist/review-events.test.js.map +1 -1
  94. package/dist/review-result.test.js +1 -1
  95. package/dist/review-result.test.js.map +1 -1
  96. package/dist/review.js +3 -3
  97. package/dist/review.js.map +1 -1
  98. package/dist/review.meta.test.js +1 -1
  99. package/dist/review.meta.test.js.map +1 -1
  100. package/dist/skill-protocol.test.js +1 -1
  101. package/dist/skill-protocol.test.js.map +1 -1
  102. package/dist/skill-update.test.d.ts +2 -0
  103. package/dist/skill-update.test.d.ts.map +1 -0
  104. package/dist/skill-update.test.js +23 -0
  105. package/dist/skill-update.test.js.map +1 -0
  106. package/dist/skill.d.ts +22 -0
  107. package/dist/skill.d.ts.map +1 -1
  108. package/dist/skill.js +109 -22
  109. package/dist/skill.js.map +1 -1
  110. package/dist/types.d.ts +23 -1
  111. package/dist/types.d.ts.map +1 -1
  112. package/dist/types.js.map +1 -1
  113. package/dist/ui-client.d.ts +13 -0
  114. package/dist/ui-client.d.ts.map +1 -0
  115. package/dist/ui-client.js +161 -0
  116. package/dist/ui-client.js.map +1 -0
  117. package/dist/ui-client.test.d.ts +2 -0
  118. package/dist/ui-client.test.d.ts.map +1 -0
  119. package/dist/ui-client.test.js +48 -0
  120. package/dist/ui-client.test.js.map +1 -0
  121. package/dist/ui-html.d.ts +7 -0
  122. package/dist/ui-html.d.ts.map +1 -0
  123. package/dist/ui-html.js +52 -0
  124. package/dist/ui-html.js.map +1 -0
  125. package/dist/ui-html.test.d.ts +2 -0
  126. package/dist/ui-html.test.d.ts.map +1 -0
  127. package/dist/ui-html.test.js +26 -0
  128. package/dist/ui-html.test.js.map +1 -0
  129. package/dist/ui-server-main.d.ts +3 -0
  130. package/dist/ui-server-main.d.ts.map +1 -0
  131. package/dist/ui-server-main.js +128 -0
  132. package/dist/ui-server-main.js.map +1 -0
  133. package/dist/ui-server-main.test.d.ts +2 -0
  134. package/dist/ui-server-main.test.d.ts.map +1 -0
  135. package/dist/ui-server-main.test.js +123 -0
  136. package/dist/ui-server-main.test.js.map +1 -0
  137. package/dist/ui-server.d.ts +42 -0
  138. package/dist/ui-server.d.ts.map +1 -0
  139. package/dist/ui-server.js +233 -0
  140. package/dist/ui-server.js.map +1 -0
  141. package/dist/ui-server.test.d.ts +2 -0
  142. package/dist/ui-server.test.d.ts.map +1 -0
  143. package/dist/ui-server.test.js +236 -0
  144. package/dist/ui-server.test.js.map +1 -0
  145. package/dist/update.d.ts.map +1 -1
  146. package/dist/update.js +48 -19
  147. package/dist/update.js.map +1 -1
  148. package/extensions/panel-tool.test.ts +366 -8
  149. package/extensions/panel-tool.ts +289 -68
  150. package/extensions/review.test.ts +79 -0
  151. package/extensions/review.ts +188 -44
  152. package/extensions/rv-completions.test.ts +177 -18
  153. package/extensions/rv-completions.ts +490 -60
  154. package/extensions/rv-interactive.test.ts +562 -0
  155. package/extensions/rv-interactive.ts +616 -0
  156. package/extensions/rv-locale.test.ts +12 -4
  157. package/extensions/rv-locale.ts +44 -11
  158. package/extensions/rv-model-picker.test.ts +172 -0
  159. package/extensions/rv-model-picker.ts +313 -0
  160. package/extensions/rv-model-priorities.test.ts +29 -9
  161. package/extensions/rv-model-priorities.ts +23 -8
  162. package/extensions/rv-prompts.test.ts +274 -2
  163. package/extensions/rv-prompts.ts +479 -33
  164. package/extensions/rv-resolve.test.ts +189 -0
  165. package/extensions/rv-resolve.ts +313 -0
  166. package/extensions/rv-semantic.test.ts +1 -1
  167. package/extensions/rv-semantic.ts +38 -4
  168. package/package.json +7 -4
  169. package/resources/rv-model-priorities.json +18 -9
  170. package/skills/pi-review/SKILL.md +84 -60
  171. package/skills/pi-review/references/codex-tools.md +18 -11
  172. package/skills/pi-review/references/model-selection.md +49 -28
  173. package/dist/extensions/review.d.ts +0 -2
  174. package/dist/extensions/review.js +0 -22
  175. package/dist/extensions/rv-prompts.d.ts +0 -19
  176. package/dist/extensions/rv-prompts.js +0 -132
@@ -0,0 +1,123 @@
1
+ import assert from "node:assert/strict";
2
+ import fs from "node:fs";
3
+ import http from "node:http";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { fork } from "node:child_process";
7
+ import { fileURLToPath } from "node:url";
8
+ import { test, afterEach } from "vitest";
9
+ // These tests exercise the real detached server process, so they need a
10
+ // fresh `dist/ui-server-main.js` (the module reads its sibling static assets
11
+ // from its own compiled location). `npm test` runs `npm run build` first;
12
+ // run `npm run build` before invoking this file standalone.
13
+ function distEntry() {
14
+ return fileURLToPath(new URL("../dist/ui-server-main.js", import.meta.url));
15
+ }
16
+ function httpGet(port, requestPath, headers = {}) {
17
+ return new Promise((resolve, reject) => {
18
+ const req = http.request({ host: "127.0.0.1", port, path: requestPath, headers }, (res) => {
19
+ let body = "";
20
+ res.on("data", (c) => (body += c));
21
+ res.on("end", () => resolve({ status: res.statusCode ?? 0, headers: res.headers, body }));
22
+ });
23
+ req.on("error", reject);
24
+ req.end();
25
+ });
26
+ }
27
+ let activeChild;
28
+ let activeDir;
29
+ afterEach(() => {
30
+ if (activeChild && activeChild.exitCode === null && !activeChild.killed)
31
+ activeChild.kill("SIGKILL");
32
+ activeChild = undefined;
33
+ if (activeDir) {
34
+ fs.rmSync(activeDir, { recursive: true, force: true });
35
+ activeDir = undefined;
36
+ }
37
+ });
38
+ async function spawnServer(opts = {}) {
39
+ const entry = distEntry();
40
+ assert.ok(fs.existsSync(entry), "dist/ui-server-main.js is missing — run `npm run build` before this test");
41
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-review-ui-main-test-"));
42
+ activeDir = dir;
43
+ const eventsPath = path.join(dir, "events.jsonl");
44
+ const token = `test-token-${Math.random().toString(36).slice(2)}`;
45
+ fs.writeFileSync(eventsPath, opts.preCompleted
46
+ ? `${JSON.stringify({ v: 1, runId: "r1", seq: 1, at: 1, type: "panel.completed", meta: { status: "clean" } })}\n`
47
+ : "");
48
+ const args = [dir, token, String(opts.ttlMs ?? 900_000), String(opts.ttlCheckIntervalMs ?? 30_000)];
49
+ const child = fork(entry, args, { stdio: ["ignore", "pipe", "pipe", "ipc"] });
50
+ activeChild = child;
51
+ const ready = await new Promise((resolve, reject) => {
52
+ const timer = setTimeout(() => reject(new Error("server did not send a ready message in time")), 5000);
53
+ child.once("message", (msg) => {
54
+ clearTimeout(timer);
55
+ resolve(msg);
56
+ });
57
+ child.once("error", (error) => {
58
+ clearTimeout(timer);
59
+ reject(error);
60
+ });
61
+ child.once("exit", (code) => {
62
+ clearTimeout(timer);
63
+ reject(new Error(`server exited early with code ${code}`));
64
+ });
65
+ });
66
+ assert.equal(ready.ready, true, ready.error);
67
+ return { child, port: ready.port, token, dir, eventsPath };
68
+ }
69
+ test("ui-server-main serves the dashboard page, static assets, and SSE stream once ready", async () => {
70
+ const { port, token } = await spawnServer();
71
+ const page = await httpGet(port, `/run/${token}`, { Host: `127.0.0.1:${port}` });
72
+ assert.equal(page.status, 200);
73
+ assert.match(page.body, /pi-review panel/);
74
+ const clientJs = await httpGet(port, `/run/${token}/static/ui-client.js`, { Host: `127.0.0.1:${port}` });
75
+ assert.equal(clientJs.status, 200);
76
+ assert.match(clientJs.body, /EventSource/);
77
+ const reducerJs = await httpGet(port, `/run/${token}/static/panel-view.js`, { Host: `127.0.0.1:${port}` });
78
+ assert.equal(reducerJs.status, 200);
79
+ assert.match(reducerJs.body, /reducePanelEvent/);
80
+ }, 10_000);
81
+ test("every static asset's transitive relative imports are also served (the browser module graph resolves)", async () => {
82
+ const { port, token } = await spawnServer();
83
+ const entryPoints = ["ui-client.js", "panel-view.js"];
84
+ const seen = new Set();
85
+ const pending = [...entryPoints];
86
+ while (pending.length > 0) {
87
+ const name = pending.pop();
88
+ if (seen.has(name))
89
+ continue;
90
+ seen.add(name);
91
+ const res = await httpGet(port, `/run/${token}/static/${name}`, { Host: `127.0.0.1:${port}` });
92
+ assert.equal(res.status, 200, `expected /static/${name} to be served (imported by the module graph)`);
93
+ for (const match of res.body.matchAll(/from\s+"\.\/([^"]+)"/g))
94
+ pending.push(match[1]);
95
+ }
96
+ assert.ok(seen.size >= 3, "expected to discover panel-view.js's transitive import too");
97
+ }, 10_000);
98
+ test("ui-server-main rejects a wrong capability token even once ready", async () => {
99
+ const { port, token } = await spawnServer();
100
+ const res = await httpGet(port, `/run/${token}-wrong`, { Host: `127.0.0.1:${port}` });
101
+ assert.equal(res.status, 404);
102
+ }, 10_000);
103
+ test("ui-server-main removes the run directory on SIGTERM", async () => {
104
+ const { child, dir } = await spawnServer();
105
+ assert.ok(fs.existsSync(dir));
106
+ const exited = new Promise((resolve) => child.once("exit", () => resolve()));
107
+ child.kill("SIGTERM");
108
+ await exited;
109
+ assert.equal(fs.existsSync(dir), false);
110
+ }, 10_000);
111
+ test("ui-server-main self-terminates after the idle TTL once the run has completed", async () => {
112
+ const { child, dir } = await spawnServer({ preCompleted: true, ttlMs: 30, ttlCheckIntervalMs: 20 });
113
+ const exit = await new Promise((resolve, reject) => {
114
+ const timer = setTimeout(() => reject(new Error("server did not self-terminate within the TTL window")), 3000);
115
+ child.once("exit", (code) => {
116
+ clearTimeout(timer);
117
+ resolve({ code });
118
+ });
119
+ });
120
+ assert.equal(exit.code, 0);
121
+ assert.equal(fs.existsSync(dir), false);
122
+ }, 10_000);
123
+ //# sourceMappingURL=ui-server-main.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui-server-main.test.js","sourceRoot":"","sources":["../src/ui-server-main.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ /** Default idle TTL (seconds) the dashboard stays up after the run completes. */
3
+ export declare const DEFAULT_UI_TTL_SECONDS = 900;
4
+ /** Hard safeguard so an orphaned dashboard process never runs forever, even if the run never completes. */
5
+ export declare const MAX_RUN_LIFETIME_MS: number;
6
+ /** High-entropy per-run capability token embedded in the dashboard URL path. */
7
+ export declare function generateCapabilityToken(): string;
8
+ /** Constant-time token comparison; a length mismatch alone must not leak timing. */
9
+ export declare function isValidToken(candidate: string | undefined, expected: string): boolean;
10
+ /** Reject every Host header except loopback names bound to the server's own port. */
11
+ export declare function isAllowedHost(hostHeader: string | undefined, port: number): boolean;
12
+ /** Allow a missing Origin (same-origin GET/EventSource often omit it) or an exact loopback match. */
13
+ export declare function isAllowedOrigin(originHeader: string | undefined, port: number): boolean;
14
+ /** Restrictive headers on every response: no remote assets, no framing, no caching of sensitive state. */
15
+ export declare function securityHeaders(): Record<string, string>;
16
+ /** Create the per-run temp directory with owner-only permissions where supported. */
17
+ export declare function createRunDirectory(runId: string, baseDir?: string): string;
18
+ /** Write the dashboard URL via temp-file + rename so readers never see a partial write. */
19
+ export declare function writeUrlFileAtomic(filePath: string, url: string): void;
20
+ /** One SSE frame: `id:` enables browser Last-Event-ID replay on reconnect. */
21
+ export declare function formatSseEvent(seq: number, event: unknown): string;
22
+ /** Split newline-delimited text into complete lines plus a leftover partial line for the next chunk. */
23
+ export declare function splitCompleteLines(buffered: string, chunk: string): {
24
+ lines: string[];
25
+ leftover: string;
26
+ };
27
+ export interface DashboardAsset {
28
+ contentType: string;
29
+ body: string;
30
+ }
31
+ export interface DashboardServerContext {
32
+ token: string;
33
+ port: number;
34
+ eventsPath: string;
35
+ html: string;
36
+ staticAssets: Record<string, DashboardAsset>;
37
+ /** Test-only: shrink the tail-poll interval below the 200ms production default. */
38
+ pollIntervalMs?: number;
39
+ }
40
+ /** Build the dashboard's HTTP request handler. All routes live under /run/:token for one auth boundary. */
41
+ export declare function createDashboardRequestListener(ctx: DashboardServerContext): (req: IncomingMessage, res: ServerResponse) => void;
42
+ //# sourceMappingURL=ui-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui-server.d.ts","sourceRoot":"","sources":["../src/ui-server.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE,iFAAiF;AACjF,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,2GAA2G;AAC3G,eAAO,MAAM,mBAAmB,QAAqB,CAAC;AAEtD,gFAAgF;AAChF,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD;AAED,oFAAoF;AACpF,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAMrF;AAID,qFAAqF;AACrF,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAsBnF;AAED,qGAAqG;AACrG,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAIvF;AAED,0GAA0G;AAC1G,wBAAgB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CASxD;AAED,qFAAqF;AACrF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,MAAoB,GAAG,MAAM,CAKvF;AAED,2FAA2F;AAC3F,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAItE;AAED,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAElE;AAED,wGAAwG;AACxG,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAKzG;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC7C,mFAAmF;IACnF,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AA4FD,2GAA2G;AAC3G,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,sBAAsB,GAC1B,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,KAAK,IAAI,CA+CrD"}
@@ -0,0 +1,233 @@
1
+ // Loopback dashboard HTTP/SSE server (issue #4). Binds only to loopback
2
+ // addresses, protects every run with a high-entropy capability token, and
3
+ // streams the normalized ReviewEvent v1 JSONL artifact so a browser can
4
+ // replay/reconnect without touching the review process directly.
5
+ import crypto from "node:crypto";
6
+ import fs from "node:fs";
7
+ import os from "node:os";
8
+ import path from "node:path";
9
+ /** Default idle TTL (seconds) the dashboard stays up after the run completes. */
10
+ export const DEFAULT_UI_TTL_SECONDS = 900;
11
+ /** Hard safeguard so an orphaned dashboard process never runs forever, even if the run never completes. */
12
+ export const MAX_RUN_LIFETIME_MS = 2 * 60 * 60 * 1000;
13
+ /** High-entropy per-run capability token embedded in the dashboard URL path. */
14
+ export function generateCapabilityToken() {
15
+ return crypto.randomBytes(32).toString("base64url");
16
+ }
17
+ /** Constant-time token comparison; a length mismatch alone must not leak timing. */
18
+ export function isValidToken(candidate, expected) {
19
+ if (!candidate)
20
+ return false;
21
+ const a = Buffer.from(candidate);
22
+ const b = Buffer.from(expected);
23
+ if (a.length !== b.length)
24
+ return false;
25
+ return crypto.timingSafeEqual(a, b);
26
+ }
27
+ const LOOPBACK_HOSTNAMES = new Set(["127.0.0.1", "localhost", "[::1]"]);
28
+ /** Reject every Host header except loopback names bound to the server's own port. */
29
+ export function isAllowedHost(hostHeader, port) {
30
+ if (!hostHeader)
31
+ return false;
32
+ let hostname;
33
+ let portPart;
34
+ if (hostHeader.startsWith("[")) {
35
+ const closeIndex = hostHeader.indexOf("]");
36
+ if (closeIndex === -1)
37
+ return false;
38
+ hostname = hostHeader.slice(0, closeIndex + 1);
39
+ const rest = hostHeader.slice(closeIndex + 1);
40
+ portPart = rest.startsWith(":") ? rest.slice(1) : undefined;
41
+ }
42
+ else {
43
+ const colonIndex = hostHeader.lastIndexOf(":");
44
+ if (colonIndex === -1) {
45
+ hostname = hostHeader;
46
+ portPart = undefined;
47
+ }
48
+ else {
49
+ hostname = hostHeader.slice(0, colonIndex);
50
+ portPart = hostHeader.slice(colonIndex + 1);
51
+ }
52
+ }
53
+ if (!LOOPBACK_HOSTNAMES.has(hostname.toLowerCase()))
54
+ return false;
55
+ return portPart === String(port);
56
+ }
57
+ /** Allow a missing Origin (same-origin GET/EventSource often omit it) or an exact loopback match. */
58
+ export function isAllowedOrigin(originHeader, port) {
59
+ if (originHeader === undefined)
60
+ return true;
61
+ const allowed = new Set([`http://127.0.0.1:${port}`, `http://localhost:${port}`, `http://[::1]:${port}`]);
62
+ return allowed.has(originHeader);
63
+ }
64
+ /** Restrictive headers on every response: no remote assets, no framing, no caching of sensitive state. */
65
+ export function securityHeaders() {
66
+ return {
67
+ "Content-Security-Policy": "default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self'; img-src 'self'; base-uri 'none'; frame-ancestors 'none'; form-action 'none'",
68
+ "X-Frame-Options": "DENY",
69
+ "X-Content-Type-Options": "nosniff",
70
+ "Referrer-Policy": "no-referrer",
71
+ "Cache-Control": "no-store",
72
+ };
73
+ }
74
+ /** Create the per-run temp directory with owner-only permissions where supported. */
75
+ export function createRunDirectory(runId, baseDir = os.tmpdir()) {
76
+ const dir = path.join(baseDir, "pi-review-ui", runId);
77
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
78
+ fs.chmodSync(dir, 0o700);
79
+ return dir;
80
+ }
81
+ /** Write the dashboard URL via temp-file + rename so readers never see a partial write. */
82
+ export function writeUrlFileAtomic(filePath, url) {
83
+ const tmpPath = `${filePath}.tmp-${process.pid}`;
84
+ fs.writeFileSync(tmpPath, `${url}\n`, { mode: 0o600 });
85
+ fs.renameSync(tmpPath, filePath);
86
+ }
87
+ /** One SSE frame: `id:` enables browser Last-Event-ID replay on reconnect. */
88
+ export function formatSseEvent(seq, event) {
89
+ return `id: ${seq}\ndata: ${JSON.stringify(event)}\n\n`;
90
+ }
91
+ /** Split newline-delimited text into complete lines plus a leftover partial line for the next chunk. */
92
+ export function splitCompleteLines(buffered, chunk) {
93
+ const combined = buffered + chunk;
94
+ const lines = combined.split("\n");
95
+ const leftover = lines.pop() ?? "";
96
+ return { lines: lines.filter((line) => line.length > 0), leftover };
97
+ }
98
+ function safePathname(rawUrl) {
99
+ try {
100
+ return new URL(rawUrl ?? "/", "http://internal").pathname;
101
+ }
102
+ catch {
103
+ return "/";
104
+ }
105
+ }
106
+ function writeStatus(res, status, headers, body) {
107
+ res.writeHead(status, { ...securityHeaders(), "Content-Type": "text/plain; charset=utf-8", ...headers });
108
+ res.end(body);
109
+ }
110
+ function serveEvents(req, res, ctx) {
111
+ res.writeHead(200, {
112
+ ...securityHeaders(),
113
+ "Content-Type": "text/event-stream; charset=utf-8",
114
+ Connection: "keep-alive",
115
+ });
116
+ res.write(":ok\n\n");
117
+ const lastEventIdHeader = req.headers["last-event-id"];
118
+ const initialLastSeq = typeof lastEventIdHeader === "string" && /^\d+$/.test(lastEventIdHeader) ? Number(lastEventIdHeader) : 0;
119
+ let lastSentSeq = initialLastSeq;
120
+ let leftover = "";
121
+ const sendLine = (line) => {
122
+ let parsed;
123
+ try {
124
+ parsed = JSON.parse(line);
125
+ }
126
+ catch {
127
+ return;
128
+ }
129
+ if (!parsed || typeof parsed.seq !== "number" || parsed.seq <= lastSentSeq)
130
+ return;
131
+ lastSentSeq = parsed.seq;
132
+ res.write(formatSseEvent(parsed.seq, parsed));
133
+ };
134
+ const consume = (chunk) => {
135
+ const { lines, leftover: rest } = splitCompleteLines(leftover, chunk);
136
+ leftover = rest;
137
+ for (const line of lines)
138
+ sendLine(line);
139
+ };
140
+ let initial = "";
141
+ try {
142
+ initial = fs.readFileSync(ctx.eventsPath, "utf8");
143
+ }
144
+ catch {
145
+ initial = "";
146
+ }
147
+ let offset = Buffer.byteLength(initial, "utf8");
148
+ consume(initial);
149
+ let stopped = false;
150
+ const pollIntervalMs = ctx.pollIntervalMs ?? 200;
151
+ const poll = setInterval(() => {
152
+ if (stopped)
153
+ return;
154
+ fs.stat(ctx.eventsPath, (err, stat) => {
155
+ if (err || stopped || stat.size <= offset)
156
+ return;
157
+ const stream = fs.createReadStream(ctx.eventsPath, { start: offset, end: stat.size - 1, encoding: "utf8" });
158
+ let buf = "";
159
+ stream.on("data", (c) => {
160
+ buf += c;
161
+ });
162
+ stream.on("end", () => {
163
+ offset = stat.size;
164
+ if (buf)
165
+ consume(buf);
166
+ });
167
+ });
168
+ }, pollIntervalMs);
169
+ const heartbeat = setInterval(() => {
170
+ try {
171
+ res.write(":hb\n\n");
172
+ }
173
+ catch {
174
+ /* connection already gone; cleanup runs on the close event */
175
+ }
176
+ }, 15000);
177
+ const cleanup = () => {
178
+ if (stopped)
179
+ return;
180
+ stopped = true;
181
+ clearInterval(poll);
182
+ clearInterval(heartbeat);
183
+ };
184
+ req.on("close", cleanup);
185
+ res.on("close", cleanup);
186
+ }
187
+ /** Build the dashboard's HTTP request handler. All routes live under /run/:token for one auth boundary. */
188
+ export function createDashboardRequestListener(ctx) {
189
+ return (req, res) => {
190
+ if (!isAllowedHost(req.headers.host, ctx.port)) {
191
+ writeStatus(res, 400, {}, "bad host");
192
+ return;
193
+ }
194
+ if (!isAllowedOrigin(req.headers.origin, ctx.port)) {
195
+ writeStatus(res, 403, {}, "bad origin");
196
+ return;
197
+ }
198
+ const method = req.method ?? "GET";
199
+ if (method !== "GET" && method !== "HEAD") {
200
+ writeStatus(res, 405, { Allow: "GET, HEAD" }, "method not allowed");
201
+ return;
202
+ }
203
+ const pathname = safePathname(req.url);
204
+ const match = pathname.match(/^\/run\/([^/]+)((?:\/.*)?)$/);
205
+ if (!match || !isValidToken(match[1], ctx.token)) {
206
+ writeStatus(res, 404, {}, "not found");
207
+ return;
208
+ }
209
+ const rest = match[2] || "/";
210
+ if (rest === "/" || rest === "") {
211
+ res.writeHead(200, { ...securityHeaders(), "Content-Type": "text/html; charset=utf-8" });
212
+ res.end(ctx.html);
213
+ return;
214
+ }
215
+ if (rest === "/events") {
216
+ serveEvents(req, res, ctx);
217
+ return;
218
+ }
219
+ const staticMatch = rest.match(/^\/static\/([^/]+)$/);
220
+ if (staticMatch) {
221
+ const asset = ctx.staticAssets[staticMatch[1]];
222
+ if (!asset) {
223
+ writeStatus(res, 404, {}, "not found");
224
+ return;
225
+ }
226
+ res.writeHead(200, { ...securityHeaders(), "Content-Type": asset.contentType });
227
+ res.end(asset.body);
228
+ return;
229
+ }
230
+ writeStatus(res, 404, {}, "not found");
231
+ };
232
+ }
233
+ //# sourceMappingURL=ui-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui-server.js","sourceRoot":"","sources":["../src/ui-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ui-server.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui-server.test.d.ts","sourceRoot":"","sources":["../src/ui-server.test.ts"],"names":[],"mappings":""}