@zephyrdeng/pi-review 0.7.0 → 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.
- package/README.md +51 -7
- package/README.zh-CN.md +45 -5
- package/dist/args.d.ts +4 -0
- package/dist/args.d.ts.map +1 -1
- package/dist/args.js +82 -5
- package/dist/args.js.map +1 -1
- package/dist/args.test.js +72 -1
- package/dist/args.test.js.map +1 -1
- package/dist/child-process.d.ts +6 -0
- package/dist/child-process.d.ts.map +1 -1
- package/dist/child-process.js +48 -0
- package/dist/child-process.js.map +1 -1
- package/dist/child-process.test.js +55 -1
- package/dist/child-process.test.js.map +1 -1
- package/dist/cli-exit.test.js +14 -5
- package/dist/cli-exit.test.js.map +1 -1
- package/dist/cli.js +8 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +13 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -5
- package/dist/index.js.map +1 -1
- package/dist/install-args.test.js +1 -1
- package/dist/install-args.test.js.map +1 -1
- package/dist/install.d.ts.map +1 -1
- package/dist/install.js +2 -3
- package/dist/install.js.map +1 -1
- package/dist/json-events.d.ts +34 -2
- package/dist/json-events.d.ts.map +1 -1
- package/dist/json-events.js +156 -49
- package/dist/json-events.js.map +1 -1
- package/dist/json-events.test.js +176 -4
- package/dist/json-events.test.js.map +1 -1
- package/dist/loop.d.ts +28 -2
- package/dist/loop.d.ts.map +1 -1
- package/dist/loop.js +45 -3
- package/dist/loop.js.map +1 -1
- package/dist/loop.test.js +24 -1
- package/dist/loop.test.js.map +1 -1
- package/dist/matcher.test.js +1 -1
- package/dist/matcher.test.js.map +1 -1
- package/dist/meta-footer.d.ts +4 -8
- package/dist/meta-footer.d.ts.map +1 -1
- package/dist/meta-footer.js +19 -5
- package/dist/meta-footer.js.map +1 -1
- package/dist/meta-footer.test.js +11 -4
- package/dist/meta-footer.test.js.map +1 -1
- package/dist/panel-aggregate.test.js +1 -1
- package/dist/panel-aggregate.test.js.map +1 -1
- package/dist/panel-cancellation.test.d.ts +2 -0
- package/dist/panel-cancellation.test.d.ts.map +1 -0
- package/dist/panel-cancellation.test.js +55 -0
- package/dist/panel-cancellation.test.js.map +1 -0
- package/dist/panel-cli-ui.test.d.ts +2 -0
- package/dist/panel-cli-ui.test.d.ts.map +1 -0
- package/dist/panel-cli-ui.test.js +91 -0
- package/dist/panel-cli-ui.test.js.map +1 -0
- package/dist/panel-cli.test.js +76 -27
- package/dist/panel-cli.test.js.map +1 -1
- package/dist/panel-config.d.ts +38 -0
- package/dist/panel-config.d.ts.map +1 -1
- package/dist/panel-config.js +137 -4
- package/dist/panel-config.js.map +1 -1
- package/dist/panel-config.test.js +84 -4
- package/dist/panel-config.test.js.map +1 -1
- package/dist/panel-loop.test.js +1 -1
- package/dist/panel-loop.test.js.map +1 -1
- package/dist/panel-tools.test.d.ts +2 -0
- package/dist/panel-tools.test.d.ts.map +1 -0
- package/dist/panel-tools.test.js +12 -0
- package/dist/panel-tools.test.js.map +1 -0
- package/dist/panel-ui.d.ts +20 -0
- package/dist/panel-ui.d.ts.map +1 -0
- package/dist/panel-ui.js +84 -0
- package/dist/panel-ui.js.map +1 -0
- package/dist/panel-ui.test.d.ts +2 -0
- package/dist/panel-ui.test.d.ts.map +1 -0
- package/dist/panel-ui.test.js +122 -0
- package/dist/panel-ui.test.js.map +1 -0
- package/dist/panel-usage.d.ts +6 -0
- package/dist/panel-usage.d.ts.map +1 -0
- package/dist/panel-usage.js +21 -0
- package/dist/panel-usage.js.map +1 -0
- package/dist/panel-view.d.ts +42 -0
- package/dist/panel-view.d.ts.map +1 -0
- package/dist/panel-view.js +124 -0
- package/dist/panel-view.js.map +1 -0
- package/dist/panel-view.test.d.ts +2 -0
- package/dist/panel-view.test.d.ts.map +1 -0
- package/dist/panel-view.test.js +112 -0
- package/dist/panel-view.test.js.map +1 -0
- package/dist/panel.d.ts +22 -2
- package/dist/panel.d.ts.map +1 -1
- package/dist/panel.js +184 -60
- package/dist/panel.js.map +1 -1
- package/dist/prompt.d.ts +2 -0
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +34 -3
- package/dist/prompt.js.map +1 -1
- package/dist/prompt.test.js +21 -2
- package/dist/prompt.test.js.map +1 -1
- package/dist/review-events.d.ts +70 -0
- package/dist/review-events.d.ts.map +1 -0
- package/dist/review-events.js +52 -0
- package/dist/review-events.js.map +1 -0
- package/dist/review-events.test.d.ts +2 -0
- package/dist/review-events.test.d.ts.map +1 -0
- package/dist/review-events.test.js +47 -0
- package/dist/review-events.test.js.map +1 -0
- package/dist/review-result.test.js +1 -1
- package/dist/review-result.test.js.map +1 -1
- package/dist/review.js +3 -3
- package/dist/review.js.map +1 -1
- package/dist/review.meta.test.js +1 -1
- package/dist/review.meta.test.js.map +1 -1
- package/dist/skill-protocol.test.js +1 -1
- package/dist/skill-protocol.test.js.map +1 -1
- package/dist/skill-update.test.d.ts +2 -0
- package/dist/skill-update.test.d.ts.map +1 -0
- package/dist/skill-update.test.js +23 -0
- package/dist/skill-update.test.js.map +1 -0
- package/dist/skill.d.ts +22 -0
- package/dist/skill.d.ts.map +1 -1
- package/dist/skill.js +109 -22
- package/dist/skill.js.map +1 -1
- package/dist/types.d.ts +28 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -1
- package/dist/ui-client.d.ts +13 -0
- package/dist/ui-client.d.ts.map +1 -0
- package/dist/ui-client.js +161 -0
- package/dist/ui-client.js.map +1 -0
- package/dist/ui-client.test.d.ts +2 -0
- package/dist/ui-client.test.d.ts.map +1 -0
- package/dist/ui-client.test.js +48 -0
- package/dist/ui-client.test.js.map +1 -0
- package/dist/ui-html.d.ts +7 -0
- package/dist/ui-html.d.ts.map +1 -0
- package/dist/ui-html.js +52 -0
- package/dist/ui-html.js.map +1 -0
- package/dist/ui-html.test.d.ts +2 -0
- package/dist/ui-html.test.d.ts.map +1 -0
- package/dist/ui-html.test.js +26 -0
- package/dist/ui-html.test.js.map +1 -0
- package/dist/ui-server-main.d.ts +3 -0
- package/dist/ui-server-main.d.ts.map +1 -0
- package/dist/ui-server-main.js +128 -0
- package/dist/ui-server-main.js.map +1 -0
- package/dist/ui-server-main.test.d.ts +2 -0
- package/dist/ui-server-main.test.d.ts.map +1 -0
- package/dist/ui-server-main.test.js +123 -0
- package/dist/ui-server-main.test.js.map +1 -0
- package/dist/ui-server.d.ts +42 -0
- package/dist/ui-server.d.ts.map +1 -0
- package/dist/ui-server.js +233 -0
- package/dist/ui-server.js.map +1 -0
- package/dist/ui-server.test.d.ts +2 -0
- package/dist/ui-server.test.d.ts.map +1 -0
- package/dist/ui-server.test.js +236 -0
- package/dist/ui-server.test.js.map +1 -0
- package/dist/update.d.ts.map +1 -1
- package/dist/update.js +48 -19
- package/dist/update.js.map +1 -1
- package/extensions/panel-tool.test.ts +388 -0
- package/extensions/panel-tool.ts +390 -0
- package/extensions/review.test.ts +79 -0
- package/extensions/review.ts +191 -45
- package/extensions/rv-completions.test.ts +177 -18
- package/extensions/rv-completions.ts +490 -60
- package/extensions/rv-interactive.test.ts +562 -0
- package/extensions/rv-interactive.ts +616 -0
- package/extensions/rv-locale.test.ts +12 -4
- package/extensions/rv-locale.ts +44 -11
- package/extensions/rv-model-picker.test.ts +172 -0
- package/extensions/rv-model-picker.ts +313 -0
- package/extensions/rv-model-priorities.test.ts +29 -9
- package/extensions/rv-model-priorities.ts +23 -8
- package/extensions/rv-prompts.test.ts +281 -2
- package/extensions/rv-prompts.ts +480 -32
- package/extensions/rv-resolve.test.ts +189 -0
- package/extensions/rv-resolve.ts +313 -0
- package/extensions/rv-semantic.test.ts +1 -1
- package/extensions/rv-semantic.ts +38 -4
- package/package.json +18 -6
- package/resources/review-presets.json +2 -2
- package/resources/rv-model-priorities.json +18 -9
- package/skills/pi-review/SKILL.md +84 -60
- package/skills/pi-review/references/codex-tools.md +18 -11
- package/skills/pi-review/references/model-selection.md +49 -28
- package/dist/extensions/review.d.ts +0 -2
- package/dist/extensions/review.js +0 -22
- package/dist/extensions/rv-prompts.d.ts +0 -19
- package/dist/extensions/rv-prompts.js +0 -132
package/dist/types.d.ts
CHANGED
|
@@ -29,8 +29,15 @@ export interface ParsedArgs {
|
|
|
29
29
|
stream: boolean;
|
|
30
30
|
/** When set, run the child in --mode json and stream its event log to this file path. */
|
|
31
31
|
progressLog?: string;
|
|
32
|
-
/** Review budget for the loop command. */
|
|
32
|
+
/** Review budget for the loop command (hard ceiling). */
|
|
33
33
|
maxRounds?: number;
|
|
34
|
+
/** True when the user explicitly passed --max-rounds (vs default). */
|
|
35
|
+
maxRoundsExplicit?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Loop stop goal. `clean` means keep reviewing until the gate is clean
|
|
38
|
+
* (or escalate / exhaust the hard max-rounds budget).
|
|
39
|
+
*/
|
|
40
|
+
until?: "clean";
|
|
34
41
|
continueHandle?: string;
|
|
35
42
|
model?: string;
|
|
36
43
|
provider?: string;
|
|
@@ -50,6 +57,19 @@ export interface ParsedArgs {
|
|
|
50
57
|
consensusModel?: string;
|
|
51
58
|
/** Panel: bounded reviewer concurrency. */
|
|
52
59
|
concurrency?: number;
|
|
60
|
+
/**
|
|
61
|
+
* Per-reviewer model overrides: `r1=provider/model` or `security=provider/model`.
|
|
62
|
+
* Generic panels use r1..rN; named panels use preset reviewer ids.
|
|
63
|
+
*/
|
|
64
|
+
reviewerModels?: string[];
|
|
65
|
+
/** Panel: normalized machine event stream for renderer adapters. */
|
|
66
|
+
outputFormat?: "events-jsonl";
|
|
67
|
+
/** Panel: start a loopback web dashboard for live progress (issue #4). Only "web" is supported. */
|
|
68
|
+
ui?: "web";
|
|
69
|
+
/** Panel: also write the dashboard URL to this file path (written atomically). */
|
|
70
|
+
uiUrlFile?: string;
|
|
71
|
+
/** Panel: override the dashboard's post-completion idle TTL, in seconds (default: 900). */
|
|
72
|
+
uiTtlSeconds?: number;
|
|
53
73
|
}
|
|
54
74
|
export interface ReviewPreset {
|
|
55
75
|
description: string;
|
|
@@ -82,10 +102,14 @@ export interface ReviewRunResult {
|
|
|
82
102
|
export interface SplitPayload {
|
|
83
103
|
fileRefs: string[];
|
|
84
104
|
userText: string;
|
|
105
|
+
/** Absolute/relative path targets that must be inspected with tools, not attached as Pi files. */
|
|
106
|
+
pathTargets?: string[];
|
|
107
|
+
/** File refs safe to pass as Pi `@file` attachments (never directories). */
|
|
108
|
+
attachableFileRefs?: string[];
|
|
85
109
|
}
|
|
86
110
|
export declare const CONSENSUS_POLICIES: readonly ["any", "quorum", "majority", "unanimous"];
|
|
87
111
|
export type ConsensusPolicy = (typeof CONSENSUS_POLICIES)[number];
|
|
88
|
-
/** Token usage accumulated across
|
|
112
|
+
/** Token usage accumulated across completed requests in one review run. */
|
|
89
113
|
export interface TokenUsage {
|
|
90
114
|
input: number;
|
|
91
115
|
output: number;
|
|
@@ -97,6 +121,8 @@ export interface TokenUsage {
|
|
|
97
121
|
}
|
|
98
122
|
/** Documented initial maximum number of reviewers in one panel. */
|
|
99
123
|
export declare const MAX_REVIEWERS = 8;
|
|
124
|
+
/** Hard capability boundary for every panel reviewer. */
|
|
125
|
+
export declare const PANEL_READ_ONLY_TOOLS: readonly ["read", "grep", "find", "ls"];
|
|
100
126
|
/** Default minimum agreement for a multi-reviewer quorum panel. */
|
|
101
127
|
export declare const DEFAULT_PANEL_MIN_AGREE = 2;
|
|
102
128
|
/** Confidence at or above which a semantic adjudicator match merges clusters. */
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,QAAQ,2EAA4E,CAAC;AAClG,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhD,eAAO,MAAM,eAAe,8DAA+D,CAAC;AAC5F,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC5B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,sBAAuB,SAAQ,WAAW;IACzD,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,eAAe,GAAG,iBAAiB,GAAG,QAAQ,CAAC;IACnG,gEAAgE;IAChE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,4EAA4E;IAC5E,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,kGAAkG;IAClG,MAAM,EAAE,OAAO,CAAC;IAChB,yFAAyF;IACzF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
CHANGED
|
@@ -6,6 +6,8 @@ export const REVIEW_STATUSES = ["clean", "has_findings", "needs_human", "blocked
|
|
|
6
6
|
export const CONSENSUS_POLICIES = ["any", "quorum", "majority", "unanimous"];
|
|
7
7
|
/** Documented initial maximum number of reviewers in one panel. */
|
|
8
8
|
export const MAX_REVIEWERS = 8;
|
|
9
|
+
/** Hard capability boundary for every panel reviewer. */
|
|
10
|
+
export const PANEL_READ_ONLY_TOOLS = ["read", "grep", "find", "ls"];
|
|
9
11
|
/** Default minimum agreement for a multi-reviewer quorum panel. */
|
|
10
12
|
export const DEFAULT_PANEL_MIN_AGREE = 2;
|
|
11
13
|
/** Confidence at or above which a semantic adjudicator match merges clusters. */
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,SAAS,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,SAAS,CAAU,CAAC;AAGlG,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,CAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,SAAS,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,SAAS,CAAU,CAAC;AAGlG,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,CAAU,CAAC;AAoH5F,8EAA8E;AAC9E,eAAe;AACf,8EAA8E;AAE9E,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAU,CAAC;AActF,mEAAmE;AACnE,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC;AAE/B,yDAAyD;AACzD,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAU,CAAC;AAE7E,mEAAmE;AACnE,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC;AAEzC,iFAAiF;AACjF,MAAM,CAAC,MAAM,mCAAmC,GAAG,GAAG,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { PanelReviewerView, PanelViewState, ReviewerViewStatus } from "./panel-view.js";
|
|
2
|
+
import type { FindingCluster, TokenUsage } from "./types.js";
|
|
3
|
+
export declare function formatDuration(ms: number | undefined): string;
|
|
4
|
+
export declare function formatUsage(usage: TokenUsage | undefined): string;
|
|
5
|
+
export declare function statusClass(status: ReviewerViewStatus): string;
|
|
6
|
+
/** Pure header summary line; kept separate from DOM writes so it is unit-testable. */
|
|
7
|
+
export declare function headerSummary(state: PanelViewState, now: number): string;
|
|
8
|
+
export declare function headerTitle(state: PanelViewState): string;
|
|
9
|
+
/** Pure reviewer detail line; excludes recent activity, which renders as its own block. */
|
|
10
|
+
export declare function reviewerDetailLine(reviewer: PanelReviewerView, now: number): string;
|
|
11
|
+
export declare function findingDetailLine(cluster: FindingCluster): string;
|
|
12
|
+
export declare function resultsSummary(meta: PanelViewState["meta"]): string;
|
|
13
|
+
//# sourceMappingURL=ui-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-client.d.ts","sourceRoot":"","sources":["../src/ui-client.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAE7F,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7D,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAO7D;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,CAGjE;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAE9D;AAED,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAUxE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAEzD;AAED,2FAA2F;AAC3F,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAanF;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAKjE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,GAAG,MAAM,CAGnE"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// Browser renderer for the loopback dashboard (issue #4). Loaded as a plain
|
|
2
|
+
// ES module; resolves its sibling reducer and events endpoint relative to its
|
|
3
|
+
// own script URL so the page needs no server-injected config. All dynamic
|
|
4
|
+
// content is written through textContent, never innerHTML.
|
|
5
|
+
import { createPanelViewState, reducePanelEvent } from "./panel-view.js";
|
|
6
|
+
export function formatDuration(ms) {
|
|
7
|
+
if (ms === undefined || ms < 0)
|
|
8
|
+
return "–";
|
|
9
|
+
const totalSeconds = Math.round(ms / 1000);
|
|
10
|
+
if (totalSeconds < 60)
|
|
11
|
+
return `${totalSeconds}s`;
|
|
12
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
13
|
+
const seconds = totalSeconds % 60;
|
|
14
|
+
return `${minutes}m ${seconds}s`;
|
|
15
|
+
}
|
|
16
|
+
export function formatUsage(usage) {
|
|
17
|
+
if (!usage)
|
|
18
|
+
return "– tokens";
|
|
19
|
+
return `${usage.totalTokens.toLocaleString("en-US")} tokens`;
|
|
20
|
+
}
|
|
21
|
+
export function statusClass(status) {
|
|
22
|
+
return `status status-${status}`;
|
|
23
|
+
}
|
|
24
|
+
/** Pure header summary line; kept separate from DOM writes so it is unit-testable. */
|
|
25
|
+
export function headerSummary(state, now) {
|
|
26
|
+
const elapsed = state.startedAt !== undefined ? formatDuration((state.completedAt ?? now) - state.startedAt) : "–";
|
|
27
|
+
const parts = [
|
|
28
|
+
state.mode,
|
|
29
|
+
state.phase,
|
|
30
|
+
`${state.aggregate.completed}/${state.aggregate.total} completed`,
|
|
31
|
+
elapsed,
|
|
32
|
+
formatUsage(state.aggregate.usage),
|
|
33
|
+
].filter((part) => Boolean(part));
|
|
34
|
+
return parts.join(" · ");
|
|
35
|
+
}
|
|
36
|
+
export function headerTitle(state) {
|
|
37
|
+
return state.target ? `pi-review panel — ${state.target}` : "pi-review panel";
|
|
38
|
+
}
|
|
39
|
+
/** Pure reviewer detail line; excludes recent activity, which renders as its own block. */
|
|
40
|
+
export function reviewerDetailLine(reviewer, now) {
|
|
41
|
+
const durationMs = reviewer.startedAt !== undefined ? (reviewer.completedAt ?? now) - reviewer.startedAt : undefined;
|
|
42
|
+
const activityAgeMs = reviewer.status === "running" && reviewer.activityAt !== undefined ? now - reviewer.activityAt : undefined;
|
|
43
|
+
return [
|
|
44
|
+
reviewer.model ?? "default model",
|
|
45
|
+
reviewer.thinking,
|
|
46
|
+
reviewer.activeTool ? `tool: ${reviewer.activeTool}` : undefined,
|
|
47
|
+
durationMs !== undefined ? formatDuration(durationMs) : undefined,
|
|
48
|
+
formatUsage(reviewer.usage),
|
|
49
|
+
activityAgeMs !== undefined ? `active ${formatDuration(activityAgeMs)} ago` : undefined,
|
|
50
|
+
]
|
|
51
|
+
.filter((part) => Boolean(part))
|
|
52
|
+
.join(" · ");
|
|
53
|
+
}
|
|
54
|
+
export function findingDetailLine(cluster) {
|
|
55
|
+
const support = cluster.supportingReviewerIds.length > 0
|
|
56
|
+
? `${cluster.supportCount} reviewer(s): ${cluster.supportingReviewerIds.join(", ")}`
|
|
57
|
+
: `${cluster.supportCount} reviewer(s)`;
|
|
58
|
+
return [cluster.severity, cluster.path, support].filter((part) => Boolean(part)).join(" · ");
|
|
59
|
+
}
|
|
60
|
+
export function resultsSummary(meta) {
|
|
61
|
+
if (!meta)
|
|
62
|
+
return "";
|
|
63
|
+
return `Result: ${meta.status} (${meta.panelHealth})`;
|
|
64
|
+
}
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// DOM glue — only executed inside a real browser.
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
function el(tag, opts = {}) {
|
|
69
|
+
const node = document.createElement(tag);
|
|
70
|
+
if (opts.className)
|
|
71
|
+
node.className = opts.className;
|
|
72
|
+
if (opts.text !== undefined)
|
|
73
|
+
node.textContent = opts.text;
|
|
74
|
+
return node;
|
|
75
|
+
}
|
|
76
|
+
function renderReviewerCard(reviewer, now) {
|
|
77
|
+
const card = el("article", { className: "reviewer-card" });
|
|
78
|
+
if (reviewer.status === "queued" || reviewer.status === "running") {
|
|
79
|
+
card.classList.add("is-loading");
|
|
80
|
+
}
|
|
81
|
+
const heading = el("h2");
|
|
82
|
+
const statusLabel = reviewer.status === "queued" ? "loading"
|
|
83
|
+
: reviewer.status === "running" ? "running"
|
|
84
|
+
: reviewer.status;
|
|
85
|
+
heading.append(`${reviewer.role} `, el("span", { className: statusClass(reviewer.status), text: statusLabel }));
|
|
86
|
+
card.append(heading, el("div", { className: "reviewer-detail", text: reviewerDetailLine(reviewer, now) }));
|
|
87
|
+
if (reviewer.recentActivity.length > 0) {
|
|
88
|
+
card.append(el("div", { className: "activity", text: reviewer.recentActivity.join("\n") }));
|
|
89
|
+
}
|
|
90
|
+
else if (reviewer.status === "queued") {
|
|
91
|
+
card.append(el("div", { className: "activity loading-line", text: "waiting to start…" }));
|
|
92
|
+
}
|
|
93
|
+
else if (reviewer.status === "running") {
|
|
94
|
+
card.append(el("div", { className: "activity loading-line", text: "working…" }));
|
|
95
|
+
}
|
|
96
|
+
if (reviewer.error) {
|
|
97
|
+
card.append(el("div", { className: "activity", text: `error: ${reviewer.error}` }));
|
|
98
|
+
}
|
|
99
|
+
return card;
|
|
100
|
+
}
|
|
101
|
+
function renderFindingCluster(cluster, advisory) {
|
|
102
|
+
const wrapper = el("div", { className: advisory ? "finding advisory" : "finding" });
|
|
103
|
+
wrapper.append(el("strong", { text: cluster.summary }), el("div", { text: findingDetailLine(cluster) }));
|
|
104
|
+
return wrapper;
|
|
105
|
+
}
|
|
106
|
+
function render(state) {
|
|
107
|
+
const now = Date.now();
|
|
108
|
+
const title = document.getElementById("run-title");
|
|
109
|
+
const meta = document.getElementById("run-meta");
|
|
110
|
+
if (title)
|
|
111
|
+
title.textContent = headerTitle(state);
|
|
112
|
+
if (meta)
|
|
113
|
+
meta.textContent = headerSummary(state, now);
|
|
114
|
+
const reviewers = document.getElementById("reviewers");
|
|
115
|
+
if (reviewers) {
|
|
116
|
+
reviewers.replaceChildren(...Object.values(state.reviewers).map((reviewer) => renderReviewerCard(reviewer, now)));
|
|
117
|
+
}
|
|
118
|
+
const results = document.getElementById("results");
|
|
119
|
+
if (results) {
|
|
120
|
+
results.replaceChildren();
|
|
121
|
+
if (state.meta) {
|
|
122
|
+
results.append(el("h2", { text: resultsSummary(state.meta) }));
|
|
123
|
+
if (state.meta.confirmedClusters.length > 0) {
|
|
124
|
+
results.append(el("h3", { text: "Confirmed findings" }));
|
|
125
|
+
results.append(...state.meta.confirmedClusters.map((cluster) => renderFindingCluster(cluster, false)));
|
|
126
|
+
}
|
|
127
|
+
if (state.meta.advisories.length > 0) {
|
|
128
|
+
results.append(el("h3", { text: "Advisories" }));
|
|
129
|
+
results.append(...state.meta.advisories.map((cluster) => renderFindingCluster(cluster, true)));
|
|
130
|
+
}
|
|
131
|
+
if (state.meta.confirmedClusters.length === 0 && state.meta.advisories.length === 0) {
|
|
132
|
+
results.append(el("p", { text: "No findings reported." }));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function bootstrap() {
|
|
138
|
+
let state = createPanelViewState();
|
|
139
|
+
render(state);
|
|
140
|
+
const eventsUrl = new URL("../events", import.meta.url).toString();
|
|
141
|
+
const source = new EventSource(eventsUrl);
|
|
142
|
+
source.onmessage = (message) => {
|
|
143
|
+
let event;
|
|
144
|
+
try {
|
|
145
|
+
event = JSON.parse(message.data);
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
state = reducePanelEvent(state, event);
|
|
151
|
+
render(state);
|
|
152
|
+
};
|
|
153
|
+
source.onerror = () => {
|
|
154
|
+
const meta = document.getElementById("run-meta");
|
|
155
|
+
if (meta)
|
|
156
|
+
meta.textContent = "reconnecting…";
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
if (typeof document !== "undefined")
|
|
160
|
+
bootstrap();
|
|
161
|
+
//# sourceMappingURL=ui-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-client.js","sourceRoot":"","sources":["../src/ui-client.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,8EAA8E;AAC9E,0EAA0E;AAC1E,2DAA2D;AAE3D,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAKzE,MAAM,UAAU,cAAc,CAAC,EAAsB;IACnD,IAAI,EAAE,KAAK,SAAS,IAAI,EAAE,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IAC3C,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;IAC3C,IAAI,YAAY,GAAG,EAAE;QAAE,OAAO,GAAG,YAAY,GAAG,CAAC;IACjD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,EAAE,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,YAAY,GAAG,EAAE,CAAC;IAClC,OAAO,GAAG,OAAO,KAAK,OAAO,GAAG,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAA6B;IACvD,IAAI,CAAC,KAAK;QAAE,OAAO,UAAU,CAAC;IAC9B,OAAO,GAAG,KAAK,CAAC,WAAW,CAAC,cAAc,CAAC,OAAO,CAAC,SAAS,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAA0B;IACpD,OAAO,iBAAiB,MAAM,EAAE,CAAC;AACnC,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,aAAa,CAAC,KAAqB,EAAE,GAAW;IAC9D,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,KAAK,CAAC,WAAW,IAAI,GAAG,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IACnH,MAAM,KAAK,GAAG;QACZ,KAAK,CAAC,IAAI;QACV,KAAK,CAAC,KAAK;QACX,GAAG,KAAK,CAAC,SAAS,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC,KAAK,YAAY;QACjE,OAAO;QACP,WAAW,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC;KACnC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAClD,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAqB;IAC/C,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,qBAAqB,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC;AAChF,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,kBAAkB,CAAC,QAA2B,EAAE,GAAW;IACzE,MAAM,UAAU,GAAG,QAAQ,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,IAAI,GAAG,CAAC,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IACrH,MAAM,aAAa,GAAG,QAAQ,CAAC,MAAM,KAAK,SAAS,IAAI,QAAQ,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IACjI,OAAO;QACL,QAAQ,CAAC,KAAK,IAAI,eAAe;QACjC,QAAQ,CAAC,QAAQ;QACjB,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,SAAS;QAChE,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS;QACjE,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC;QAC3B,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,cAAc,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;KACxF;SACE,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;SAC/C,IAAI,CAAC,KAAK,CAAC,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,OAAuB;IACvD,MAAM,OAAO,GAAG,OAAO,CAAC,qBAAqB,CAAC,MAAM,GAAG,CAAC;QACtD,CAAC,CAAC,GAAG,OAAO,CAAC,YAAY,iBAAiB,OAAO,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QACpF,CAAC,CAAC,GAAG,OAAO,CAAC,YAAY,cAAc,CAAC;IAC1C,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC/G,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAA4B;IACzD,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,OAAO,WAAW,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,WAAW,GAAG,CAAC;AACxD,CAAC;AAED,8EAA8E;AAC9E,kDAAkD;AAClD,8EAA8E;AAE9E,SAAS,EAAE,CACT,GAAM,EACN,OAA8C,EAAE;IAEhD,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACzC,IAAI,IAAI,CAAC,SAAS;QAAE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;IACpD,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;QAAE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC;IAC1D,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,kBAAkB,CAAC,QAA2B,EAAE,GAAW;IAClE,MAAM,IAAI,GAAG,EAAE,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC,CAAC;IAC3D,IAAI,QAAQ,CAAC,MAAM,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAClE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACnC,CAAC;IACD,MAAM,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACzB,MAAM,WAAW,GACf,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS;QACtC,CAAC,CAAC,QAAQ,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS;YACzC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;IACxB,OAAO,CAAC,MAAM,CACZ,GAAG,QAAQ,CAAC,IAAI,GAAG,EACnB,EAAE,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAC3E,CAAC;IACF,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,iBAAiB,EAAE,IAAI,EAAE,kBAAkB,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3G,IAAI,QAAQ,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAC9F,CAAC;SAAM,IAAI,QAAQ,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACxC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,uBAAuB,EAAE,IAAI,EAAE,mBAAmB,EAAE,CAAC,CAAC,CAAC;IAC5F,CAAC;SAAM,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QACzC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,uBAAuB,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;IACnF,CAAC;IACD,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;QACnB,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;IACtF,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,oBAAoB,CAAC,OAAuB,EAAE,QAAiB;IACtE,MAAM,OAAO,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;IACpF,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,iBAAiB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IACzG,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,MAAM,CAAC,KAAqB;IACnC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,KAAK,GAAG,QAAQ,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;IACnD,MAAM,IAAI,GAAG,QAAQ,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;IACjD,IAAI,KAAK;QAAE,KAAK,CAAC,WAAW,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IAClD,IAAI,IAAI;QAAE,IAAI,CAAC,WAAW,GAAG,aAAa,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAEvD,MAAM,SAAS,GAAG,QAAQ,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;IACvD,IAAI,SAAS,EAAE,CAAC;QACd,SAAS,CAAC,eAAe,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,kBAAkB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IACpH,CAAC;IAED,MAAM,OAAO,GAAG,QAAQ,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;IACnD,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CAAC,eAAe,EAAE,CAAC;QAC1B,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YACf,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;YAC/D,IAAI,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC5C,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,oBAAoB,EAAE,CAAC,CAAC,CAAC;gBACzD,OAAO,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,oBAAoB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;YACzG,CAAC;YACD,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACrC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC;gBACjD,OAAO,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,oBAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;YACjG,CAAC;YACD,IAAI,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACpF,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,uBAAuB,EAAE,CAAC,CAAC,CAAC;YAC7D,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,SAAS;IAChB,IAAI,KAAK,GAAG,oBAAoB,EAAE,CAAC;IACnC,MAAM,CAAC,KAAK,CAAC,CAAC;IAEd,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC;IACnE,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,CAAC,SAAS,GAAG,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,KAAkB,CAAC;QACvB,IAAI,CAAC;YACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,KAAK,GAAG,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,CAAC;IAChB,CAAC,CAAC;IACF,MAAM,CAAC,OAAO,GAAG,GAAG,EAAE;QACpB,MAAM,IAAI,GAAG,QAAQ,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QACjD,IAAI,IAAI;YAAE,IAAI,CAAC,WAAW,GAAG,eAAe,CAAC;IAC/C,CAAC,CAAC;AACJ,CAAC;AAED,IAAI,OAAO,QAAQ,KAAK,WAAW;IAAE,SAAS,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-client.test.d.ts","sourceRoot":"","sources":["../src/ui-client.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { test } from "vitest";
|
|
3
|
+
import { createPanelViewState, reducePanelEvent } from "./panel-view.js";
|
|
4
|
+
import { findingDetailLine, formatDuration, formatUsage, headerSummary, headerTitle, resultsSummary, reviewerDetailLine, statusClass, } from "./ui-client.js";
|
|
5
|
+
test("formatDuration renders seconds and minutes", () => {
|
|
6
|
+
assert.equal(formatDuration(undefined), "–");
|
|
7
|
+
assert.equal(formatDuration(-5), "–");
|
|
8
|
+
assert.equal(formatDuration(4200), "4s");
|
|
9
|
+
assert.equal(formatDuration(65000), "1m 5s");
|
|
10
|
+
});
|
|
11
|
+
test("formatUsage renders total tokens or a placeholder", () => {
|
|
12
|
+
assert.equal(formatUsage(undefined), "– tokens");
|
|
13
|
+
assert.equal(formatUsage({ input: 1, output: 1, cacheRead: 0, cacheWrite: 0, reasoning: 0, totalTokens: 1234 }), "1,234 tokens");
|
|
14
|
+
});
|
|
15
|
+
test("statusClass maps every reviewer status to a CSS class", () => {
|
|
16
|
+
assert.equal(statusClass("queued"), "status status-queued");
|
|
17
|
+
assert.equal(statusClass("failed"), "status status-failed");
|
|
18
|
+
});
|
|
19
|
+
function event(type, seq, payload) {
|
|
20
|
+
return { v: 1, runId: "run-1", seq, at: 1000 + seq, type, ...payload };
|
|
21
|
+
}
|
|
22
|
+
test("headerTitle and headerSummary reflect live reducer state", () => {
|
|
23
|
+
let state = createPanelViewState();
|
|
24
|
+
state = reducePanelEvent(state, event("panel.started", 1, { target: "@src", mode: "code", reviewers: [{ reviewerId: "r1", role: "Reviewer", model: null }] }));
|
|
25
|
+
assert.equal(headerTitle(state), "pi-review panel — @src");
|
|
26
|
+
assert.match(headerSummary(state, 2000), /code · running · 0\/1 completed/);
|
|
27
|
+
state = reducePanelEvent(state, event("reviewer.started", 2, { reviewerId: "r1" }));
|
|
28
|
+
state = reducePanelEvent(state, event("reviewer.completed", 3, {
|
|
29
|
+
reviewerId: "r1",
|
|
30
|
+
submission: { reviewerId: "r1", role: "Reviewer", model: null, durationMs: 10, result: { status: "clean", verdict: "approve", verdictSource: "parsed", findings: [], actionableCount: 0 } },
|
|
31
|
+
}));
|
|
32
|
+
assert.match(headerSummary(state, 2000), /1\/1 completed/);
|
|
33
|
+
});
|
|
34
|
+
test("reviewerDetailLine joins model, thinking, active tool, duration, usage, and activity age", () => {
|
|
35
|
+
assert.equal(reviewerDetailLine({
|
|
36
|
+
reviewerId: "r1", role: "Reviewer", model: "openai/gpt", thinking: "high", status: "running",
|
|
37
|
+
activeTool: "read", turns: 1, recentActivity: [], startedAt: 1000, activityAt: 4000,
|
|
38
|
+
usage: { input: 1, output: 1, cacheRead: 0, cacheWrite: 0, reasoning: 0, totalTokens: 500 },
|
|
39
|
+
}, 5000), "openai/gpt · high · tool: read · 4s · 500 tokens · active 1s ago");
|
|
40
|
+
assert.equal(reviewerDetailLine({ reviewerId: "r1", role: "Reviewer", model: null, status: "queued", turns: 0, recentActivity: [] }, 5000), "default model · – tokens");
|
|
41
|
+
assert.equal(reviewerDetailLine({ reviewerId: "r1", role: "Reviewer", model: null, status: "completed", turns: 0, recentActivity: [], startedAt: 1000, completedAt: 3000, activityAt: 3000 }, 9000), "default model · 2s · – tokens", "a completed reviewer's duration is fixed and does not keep growing with wall-clock time");
|
|
42
|
+
});
|
|
43
|
+
test("findingDetailLine includes provenance and resultsSummary formats meta text", () => {
|
|
44
|
+
assert.equal(findingDetailLine({ id: "C1", summary: "x", confirmed: true, supportCount: 2, actionableSupportCount: 2, supportingReviewerIds: ["r1", "r2"], sourceFindingIds: [], severity: "high", path: "src/x.ts" }), "high · src/x.ts · 2 reviewer(s): r1, r2");
|
|
45
|
+
assert.equal(findingDetailLine({ id: "C2", summary: "y", confirmed: false, supportCount: 1, actionableSupportCount: 1, supportingReviewerIds: [], sourceFindingIds: [] }), "1 reviewer(s)");
|
|
46
|
+
assert.equal(resultsSummary(undefined), "");
|
|
47
|
+
});
|
|
48
|
+
//# sourceMappingURL=ui-client.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-client.test.js","sourceRoot":"","sources":["../src/ui-client.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface DashboardHtmlOptions {
|
|
2
|
+
/** The capability-token path, e.g. "/run/<token>". Used to build absolute asset URLs. */
|
|
3
|
+
runPath: string;
|
|
4
|
+
title?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function renderDashboardHtml(opts: DashboardHtmlOptions): string;
|
|
7
|
+
//# sourceMappingURL=ui-html.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-html.d.ts","sourceRoot":"","sources":["../src/ui-html.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,oBAAoB;IACnC,yFAAyF;IACzF,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,oBAAoB,GAAG,MAAM,CA4CtE"}
|
package/dist/ui-html.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Pure HTML shell for the loopback dashboard (issue #4). No inline <script>;
|
|
2
|
+
// the page loads one CSP-safe module script that renders everything.
|
|
3
|
+
const HTML_ESCAPES = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" };
|
|
4
|
+
function escapeHtml(value) {
|
|
5
|
+
return value.replace(/[&<>"']/g, (ch) => HTML_ESCAPES[ch] ?? ch);
|
|
6
|
+
}
|
|
7
|
+
export function renderDashboardHtml(opts) {
|
|
8
|
+
const title = opts.title ?? "pi-review dashboard";
|
|
9
|
+
const scriptSrc = `${opts.runPath}/static/ui-client.js`;
|
|
10
|
+
return `<!doctype html>
|
|
11
|
+
<html lang="en">
|
|
12
|
+
<head>
|
|
13
|
+
<meta charset="utf-8" />
|
|
14
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
15
|
+
<title>${escapeHtml(title)}</title>
|
|
16
|
+
<style>
|
|
17
|
+
:root { color-scheme: light dark; }
|
|
18
|
+
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; margin: 0; padding: 1.5rem; background: Canvas; color: CanvasText; }
|
|
19
|
+
h1 { font-size: 1.15rem; margin: 0 0 0.25rem; }
|
|
20
|
+
header { margin-bottom: 1.25rem; }
|
|
21
|
+
#run-meta { opacity: 0.75; font-size: 0.85rem; }
|
|
22
|
+
.reviewer-card { border: 1px solid GrayText; border-radius: 8px; padding: 0.75rem 1rem; margin-bottom: 0.6rem; }
|
|
23
|
+
.reviewer-card h2 { margin: 0 0 0.35rem; font-size: 0.95rem; font-weight: 600; }
|
|
24
|
+
.status { display: inline-block; padding: 0.1rem 0.5rem; border-radius: 999px; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.02em; color: #fff; }
|
|
25
|
+
.status-queued { background: #6b7280; }
|
|
26
|
+
.status-running { background: #2563eb; animation: pulse 1.4s ease-in-out infinite; }
|
|
27
|
+
.status-completed { background: #16a34a; }
|
|
28
|
+
.status-failed, .status-cancelled { background: #dc2626; }
|
|
29
|
+
.reviewer-card.is-loading { border-color: #2563eb; }
|
|
30
|
+
.reviewer-detail { font-size: 0.85rem; opacity: 0.85; margin: 0.15rem 0; }
|
|
31
|
+
.activity { font-size: 0.8rem; opacity: 0.75; margin: 0.35rem 0 0; white-space: pre-wrap; }
|
|
32
|
+
.loading-line::before { content: "⏳ "; }
|
|
33
|
+
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }
|
|
34
|
+
.finding { border-left: 3px solid #dc2626; padding-left: 0.6rem; margin-bottom: 0.6rem; }
|
|
35
|
+
.finding.advisory { border-left-color: #d97706; }
|
|
36
|
+
section h2 { font-size: 1rem; margin: 1.25rem 0 0.5rem; }
|
|
37
|
+
section h3 { font-size: 0.9rem; margin: 0.75rem 0 0.4rem; }
|
|
38
|
+
</style>
|
|
39
|
+
</head>
|
|
40
|
+
<body>
|
|
41
|
+
<header>
|
|
42
|
+
<h1 id="run-title">pi-review panel</h1>
|
|
43
|
+
<div id="run-meta">connecting…</div>
|
|
44
|
+
</header>
|
|
45
|
+
<section id="reviewers" aria-label="Reviewers"></section>
|
|
46
|
+
<section id="results" aria-label="Results"></section>
|
|
47
|
+
<script type="module" src="${scriptSrc}"></script>
|
|
48
|
+
</body>
|
|
49
|
+
</html>
|
|
50
|
+
`;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=ui-html.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-html.js","sourceRoot":"","sources":["../src/ui-html.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,qEAAqE;AAErE,MAAM,YAAY,GAA2B,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;AAErH,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AACnE,CAAC;AAQD,MAAM,UAAU,mBAAmB,CAAC,IAA0B;IAC5D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,qBAAqB,CAAC;IAClD,MAAM,SAAS,GAAG,GAAG,IAAI,CAAC,OAAO,sBAAsB,CAAC;IACxD,OAAO;;;;;SAKA,UAAU,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BAgCG,SAAS;;;CAGrC,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-html.test.d.ts","sourceRoot":"","sources":["../src/ui-html.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { test } from "vitest";
|
|
3
|
+
import { renderDashboardHtml } from "./ui-html.js";
|
|
4
|
+
test("dashboard HTML references the client script under the run's own token path", () => {
|
|
5
|
+
const html = renderDashboardHtml({ runPath: "/run/abc123" });
|
|
6
|
+
assert.match(html, /<script type="module" src="\/run\/abc123\/static\/ui-client\.js"><\/script>/);
|
|
7
|
+
});
|
|
8
|
+
test("dashboard HTML has exactly one script tag and no inline event handlers", () => {
|
|
9
|
+
const html = renderDashboardHtml({ runPath: "/run/abc123" });
|
|
10
|
+
const scriptTags = html.match(/<script/g) ?? [];
|
|
11
|
+
assert.equal(scriptTags.length, 1);
|
|
12
|
+
assert.doesNotMatch(html, /\son\w+\s*=/i);
|
|
13
|
+
});
|
|
14
|
+
test("dashboard HTML escapes an untrusted title", () => {
|
|
15
|
+
const html = renderDashboardHtml({ runPath: "/run/abc123", title: '<img src=x onerror=alert(1)>' });
|
|
16
|
+
assert.doesNotMatch(html, /<img/);
|
|
17
|
+
assert.match(html, /<img src=x onerror=alert\(1\)>/);
|
|
18
|
+
});
|
|
19
|
+
test("dashboard HTML declares the render containers", () => {
|
|
20
|
+
const html = renderDashboardHtml({ runPath: "/run/abc123" });
|
|
21
|
+
assert.match(html, /id="run-title"/);
|
|
22
|
+
assert.match(html, /id="run-meta"/);
|
|
23
|
+
assert.match(html, /id="reviewers"/);
|
|
24
|
+
assert.match(html, /id="results"/);
|
|
25
|
+
});
|
|
26
|
+
//# sourceMappingURL=ui-html.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-html.test.js","sourceRoot":"","sources":["../src/ui-html.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEnD,IAAI,CAAC,4EAA4E,EAAE,GAAG,EAAE;IACtF,MAAM,IAAI,GAAG,mBAAmB,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC;IAC7D,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,6EAA6E,CAAC,CAAC;AACpG,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,wEAAwE,EAAE,GAAG,EAAE;IAClF,MAAM,IAAI,GAAG,mBAAmB,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC;IAC7D,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;IAChD,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACnC,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;AAC5C,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2CAA2C,EAAE,GAAG,EAAE;IACrD,MAAM,IAAI,GAAG,mBAAmB,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,8BAA8B,EAAE,CAAC,CAAC;IACpG,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAClC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,sCAAsC,CAAC,CAAC;AAC7D,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,+CAA+C,EAAE,GAAG,EAAE;IACzD,MAAM,IAAI,GAAG,mBAAmB,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC;IAC7D,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;IACrC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;IACpC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;IACrC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;AACrC,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-server-main.d.ts","sourceRoot":"","sources":["../src/ui-server-main.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Detached loopback dashboard server process (issue #4). panel-ui.ts spawns
|
|
3
|
+
// this via child_process.fork with a temporary IPC channel for the initial
|
|
4
|
+
// ready handshake, then fully detaches it: this process outlives the review
|
|
5
|
+
// process so a browser can reconnect/replay for the configured idle TTL.
|
|
6
|
+
// It never talks back to the parent after the handshake — it only tails the
|
|
7
|
+
// shared events.jsonl artifact on disk.
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import http from "node:http";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
import { createDashboardRequestListener, MAX_RUN_LIFETIME_MS } from "./ui-server.js";
|
|
13
|
+
import { renderDashboardHtml } from "./ui-html.js";
|
|
14
|
+
const TTL_CHECK_INTERVAL_MS = 30_000;
|
|
15
|
+
function readTextAsset(name) {
|
|
16
|
+
return fs.readFileSync(fileURLToPath(new URL(`./${name}`, import.meta.url)), "utf8");
|
|
17
|
+
}
|
|
18
|
+
function isRunComplete(eventsPath) {
|
|
19
|
+
let content;
|
|
20
|
+
try {
|
|
21
|
+
content = fs.readFileSync(eventsPath, "utf8");
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
for (const line of content.split("\n")) {
|
|
27
|
+
if (!line.includes("panel.completed"))
|
|
28
|
+
continue;
|
|
29
|
+
try {
|
|
30
|
+
if (JSON.parse(line).type === "panel.completed")
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
/* malformed/partial line; ignore */
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
function main() {
|
|
40
|
+
const [runDir, token, ttlMsArg, ttlCheckIntervalArg] = process.argv.slice(2);
|
|
41
|
+
if (!runDir || !token || !ttlMsArg || !Number.isFinite(Number(ttlMsArg))) {
|
|
42
|
+
process.stderr.write("pi-review-ui-server: expected <runDir> <token> <ttlMs> [ttlCheckIntervalMs]\n");
|
|
43
|
+
process.exitCode = 1;
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const ttlMs = Number(ttlMsArg);
|
|
47
|
+
// Test-only override; production always uses the default 30s cadence.
|
|
48
|
+
const ttlCheckIntervalMs = ttlCheckIntervalArg && Number.isFinite(Number(ttlCheckIntervalArg))
|
|
49
|
+
? Number(ttlCheckIntervalArg)
|
|
50
|
+
: TTL_CHECK_INTERVAL_MS;
|
|
51
|
+
const eventsPath = path.join(runDir, "events.jsonl");
|
|
52
|
+
let staticAssets;
|
|
53
|
+
let html;
|
|
54
|
+
try {
|
|
55
|
+
staticAssets = {
|
|
56
|
+
// panel-view.js's transitive runtime import (panel-usage.js) must be
|
|
57
|
+
// served too, or the browser's ES module graph fails to resolve it.
|
|
58
|
+
"panel-view.js": { contentType: "text/javascript; charset=utf-8", body: readTextAsset("panel-view.js") },
|
|
59
|
+
"panel-usage.js": { contentType: "text/javascript; charset=utf-8", body: readTextAsset("panel-usage.js") },
|
|
60
|
+
"ui-client.js": { contentType: "text/javascript; charset=utf-8", body: readTextAsset("ui-client.js") },
|
|
61
|
+
};
|
|
62
|
+
html = renderDashboardHtml({ runPath: `/run/${token}` });
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
process.send?.({ ready: false, error: error.message });
|
|
66
|
+
process.exitCode = 1;
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const startedAt = Date.now();
|
|
70
|
+
let completedAt;
|
|
71
|
+
let shuttingDown = false;
|
|
72
|
+
const server4 = http.createServer();
|
|
73
|
+
const server6 = http.createServer();
|
|
74
|
+
const ttlTimer = setInterval(() => {
|
|
75
|
+
const now = Date.now();
|
|
76
|
+
if (completedAt === undefined && isRunComplete(eventsPath))
|
|
77
|
+
completedAt = now;
|
|
78
|
+
if (completedAt !== undefined && now - completedAt > ttlMs) {
|
|
79
|
+
shutdown(0);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (now - startedAt > MAX_RUN_LIFETIME_MS)
|
|
83
|
+
shutdown(0);
|
|
84
|
+
}, ttlCheckIntervalMs);
|
|
85
|
+
function shutdown(exitCode) {
|
|
86
|
+
if (shuttingDown)
|
|
87
|
+
return;
|
|
88
|
+
shuttingDown = true;
|
|
89
|
+
clearInterval(ttlTimer);
|
|
90
|
+
for (const server of [server4, server6]) {
|
|
91
|
+
try {
|
|
92
|
+
server.closeAllConnections();
|
|
93
|
+
server.close();
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
/* already closed */
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
fs.rmSync(runDir, { recursive: true, force: true });
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
/* best-effort cleanup */
|
|
104
|
+
}
|
|
105
|
+
process.exit(exitCode);
|
|
106
|
+
}
|
|
107
|
+
process.on("SIGTERM", () => shutdown(0));
|
|
108
|
+
process.on("SIGINT", () => shutdown(0));
|
|
109
|
+
server4.once("error", (error) => {
|
|
110
|
+
process.send?.({ ready: false, error: error.message });
|
|
111
|
+
shutdown(1);
|
|
112
|
+
});
|
|
113
|
+
server4.listen(0, "127.0.0.1", () => {
|
|
114
|
+
const address = server4.address();
|
|
115
|
+
const port = typeof address === "object" && address ? address.port : 0;
|
|
116
|
+
const ctx = { token, port, eventsPath, html, staticAssets };
|
|
117
|
+
const listener = createDashboardRequestListener(ctx);
|
|
118
|
+
server4.on("request", listener);
|
|
119
|
+
// Best-effort IPv6 loopback: never widen to 0.0.0.0, and a bind failure
|
|
120
|
+
// here (dual-stack quirks, IPv6 disabled) must not affect the IPv4 path.
|
|
121
|
+
server6.on("error", () => { });
|
|
122
|
+
server6.on("request", listener);
|
|
123
|
+
server6.listen(port, "::1");
|
|
124
|
+
process.send?.({ ready: true, port });
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
main();
|
|
128
|
+
//# sourceMappingURL=ui-server-main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-server-main.js","sourceRoot":"","sources":["../src/ui-server-main.ts"],"names":[],"mappings":"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"}
|