@blackbelt-technology/pi-agent-dashboard 0.2.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/AGENTS.md +342 -0
- package/README.md +619 -0
- package/docs/architecture.md +646 -0
- package/package.json +92 -0
- package/packages/extension/package.json +33 -0
- package/packages/extension/src/__tests__/ask-user-tool.test.ts +85 -0
- package/packages/extension/src/__tests__/command-handler.test.ts +712 -0
- package/packages/extension/src/__tests__/connection.test.ts +344 -0
- package/packages/extension/src/__tests__/credentials-updated.test.ts +26 -0
- package/packages/extension/src/__tests__/dev-build.test.ts +79 -0
- package/packages/extension/src/__tests__/event-forwarder.test.ts +89 -0
- package/packages/extension/src/__tests__/git-info.test.ts +112 -0
- package/packages/extension/src/__tests__/git-link-builder.test.ts +102 -0
- package/packages/extension/src/__tests__/openspec-activity-detector.test.ts +232 -0
- package/packages/extension/src/__tests__/openspec-poller.test.ts +119 -0
- package/packages/extension/src/__tests__/process-metrics.test.ts +47 -0
- package/packages/extension/src/__tests__/process-scanner.test.ts +202 -0
- package/packages/extension/src/__tests__/prompt-expander.test.ts +54 -0
- package/packages/extension/src/__tests__/server-auto-start.test.ts +167 -0
- package/packages/extension/src/__tests__/server-launcher.test.ts +44 -0
- package/packages/extension/src/__tests__/server-probe.test.ts +25 -0
- package/packages/extension/src/__tests__/session-switch.test.ts +139 -0
- package/packages/extension/src/__tests__/session-sync.test.ts +55 -0
- package/packages/extension/src/__tests__/source-detector.test.ts +73 -0
- package/packages/extension/src/__tests__/stats-extractor.test.ts +92 -0
- package/packages/extension/src/__tests__/ui-proxy.test.ts +583 -0
- package/packages/extension/src/__tests__/watchdog.test.ts +161 -0
- package/packages/extension/src/ask-user-tool.ts +63 -0
- package/packages/extension/src/bridge-context.ts +64 -0
- package/packages/extension/src/bridge.ts +926 -0
- package/packages/extension/src/command-handler.ts +538 -0
- package/packages/extension/src/connection.ts +204 -0
- package/packages/extension/src/dev-build.ts +39 -0
- package/packages/extension/src/event-forwarder.ts +40 -0
- package/packages/extension/src/flow-event-wiring.ts +102 -0
- package/packages/extension/src/git-info.ts +65 -0
- package/packages/extension/src/git-link-builder.ts +112 -0
- package/packages/extension/src/model-tracker.ts +56 -0
- package/packages/extension/src/pi-env.d.ts +23 -0
- package/packages/extension/src/process-metrics.ts +70 -0
- package/packages/extension/src/process-scanner.ts +396 -0
- package/packages/extension/src/prompt-expander.ts +87 -0
- package/packages/extension/src/provider-register.ts +276 -0
- package/packages/extension/src/server-auto-start.ts +87 -0
- package/packages/extension/src/server-launcher.ts +82 -0
- package/packages/extension/src/server-probe.ts +33 -0
- package/packages/extension/src/session-sync.ts +154 -0
- package/packages/extension/src/source-detector.ts +26 -0
- package/packages/extension/src/ui-proxy.ts +269 -0
- package/packages/extension/tsconfig.json +11 -0
- package/packages/server/package.json +37 -0
- package/packages/server/src/__tests__/auth-plugin.test.ts +117 -0
- package/packages/server/src/__tests__/auth.test.ts +224 -0
- package/packages/server/src/__tests__/auto-attach.test.ts +246 -0
- package/packages/server/src/__tests__/auto-resume.test.ts +135 -0
- package/packages/server/src/__tests__/auto-shutdown.test.ts +136 -0
- package/packages/server/src/__tests__/browse-endpoint.test.ts +104 -0
- package/packages/server/src/__tests__/bulk-archive-handler.test.ts +15 -0
- package/packages/server/src/__tests__/cli-parse.test.ts +73 -0
- package/packages/server/src/__tests__/client-discovery.test.ts +39 -0
- package/packages/server/src/__tests__/config-api.test.ts +104 -0
- package/packages/server/src/__tests__/cors.test.ts +48 -0
- package/packages/server/src/__tests__/directory-service.test.ts +240 -0
- package/packages/server/src/__tests__/editor-detection.test.ts +60 -0
- package/packages/server/src/__tests__/editor-endpoints.test.ts +26 -0
- package/packages/server/src/__tests__/editor-manager.test.ts +73 -0
- package/packages/server/src/__tests__/editor-registry.test.ts +151 -0
- package/packages/server/src/__tests__/event-status-extraction-flow.test.ts +55 -0
- package/packages/server/src/__tests__/event-status-extraction.test.ts +58 -0
- package/packages/server/src/__tests__/extension-register.test.ts +61 -0
- package/packages/server/src/__tests__/file-endpoint.test.ts +49 -0
- package/packages/server/src/__tests__/force-kill-handler.test.ts +109 -0
- package/packages/server/src/__tests__/git-operations.test.ts +251 -0
- package/packages/server/src/__tests__/headless-pid-registry.test.ts +233 -0
- package/packages/server/src/__tests__/headless-shutdown-fallback.test.ts +109 -0
- package/packages/server/src/__tests__/health-endpoint.test.ts +35 -0
- package/packages/server/src/__tests__/heartbeat-ack.test.ts +63 -0
- package/packages/server/src/__tests__/json-store.test.ts +70 -0
- package/packages/server/src/__tests__/localhost-guard.test.ts +149 -0
- package/packages/server/src/__tests__/memory-event-store.test.ts +260 -0
- package/packages/server/src/__tests__/memory-session-manager.test.ts +80 -0
- package/packages/server/src/__tests__/meta-persistence.test.ts +107 -0
- package/packages/server/src/__tests__/migrate-persistence.test.ts +180 -0
- package/packages/server/src/__tests__/npm-search-proxy.test.ts +153 -0
- package/packages/server/src/__tests__/oauth-callback-server.test.ts +165 -0
- package/packages/server/src/__tests__/openspec-archive.test.ts +87 -0
- package/packages/server/src/__tests__/package-manager-wrapper.test.ts +163 -0
- package/packages/server/src/__tests__/package-routes.test.ts +172 -0
- package/packages/server/src/__tests__/pending-fork-registry.test.ts +69 -0
- package/packages/server/src/__tests__/pending-load-manager.test.ts +144 -0
- package/packages/server/src/__tests__/pending-resume-registry.test.ts +130 -0
- package/packages/server/src/__tests__/pi-resource-scanner.test.ts +235 -0
- package/packages/server/src/__tests__/preferences-store.test.ts +108 -0
- package/packages/server/src/__tests__/process-manager.test.ts +184 -0
- package/packages/server/src/__tests__/provider-auth-handlers.test.ts +93 -0
- package/packages/server/src/__tests__/provider-auth-routes.test.ts +143 -0
- package/packages/server/src/__tests__/provider-auth-storage.test.ts +114 -0
- package/packages/server/src/__tests__/resolve-path.test.ts +38 -0
- package/packages/server/src/__tests__/ring-buffer.test.ts +45 -0
- package/packages/server/src/__tests__/server-pid.test.ts +89 -0
- package/packages/server/src/__tests__/session-api.test.ts +244 -0
- package/packages/server/src/__tests__/session-diff.test.ts +138 -0
- package/packages/server/src/__tests__/session-file-dedup.test.ts +102 -0
- package/packages/server/src/__tests__/session-file-reader.test.ts +85 -0
- package/packages/server/src/__tests__/session-lifecycle-logging.test.ts +138 -0
- package/packages/server/src/__tests__/session-order-manager.test.ts +135 -0
- package/packages/server/src/__tests__/session-ordering-integration.test.ts +102 -0
- package/packages/server/src/__tests__/session-scanner.test.ts +199 -0
- package/packages/server/src/__tests__/shutdown-endpoint.test.ts +42 -0
- package/packages/server/src/__tests__/skip-wipe.test.ts +123 -0
- package/packages/server/src/__tests__/sleep-aware-heartbeat.test.ts +126 -0
- package/packages/server/src/__tests__/smoke-integration.test.ts +175 -0
- package/packages/server/src/__tests__/spa-fallback.test.ts +68 -0
- package/packages/server/src/__tests__/subscription-handler.test.ts +155 -0
- package/packages/server/src/__tests__/terminal-gateway.test.ts +61 -0
- package/packages/server/src/__tests__/terminal-manager.test.ts +257 -0
- package/packages/server/src/__tests__/trusted-networks-config.test.ts +84 -0
- package/packages/server/src/__tests__/tunnel.test.ts +206 -0
- package/packages/server/src/__tests__/ws-ping-pong.test.ts +112 -0
- package/packages/server/src/auth-plugin.ts +302 -0
- package/packages/server/src/auth.ts +323 -0
- package/packages/server/src/browse.ts +55 -0
- package/packages/server/src/browser-gateway.ts +495 -0
- package/packages/server/src/browser-handlers/directory-handler.ts +137 -0
- package/packages/server/src/browser-handlers/handler-context.ts +45 -0
- package/packages/server/src/browser-handlers/session-action-handler.ts +271 -0
- package/packages/server/src/browser-handlers/session-meta-handler.ts +95 -0
- package/packages/server/src/browser-handlers/subscription-handler.ts +154 -0
- package/packages/server/src/browser-handlers/terminal-handler.ts +37 -0
- package/packages/server/src/cli.ts +347 -0
- package/packages/server/src/config-api.ts +130 -0
- package/packages/server/src/directory-service.ts +162 -0
- package/packages/server/src/editor-detection.ts +60 -0
- package/packages/server/src/editor-manager.ts +352 -0
- package/packages/server/src/editor-proxy.ts +134 -0
- package/packages/server/src/editor-registry.ts +108 -0
- package/packages/server/src/event-status-extraction.ts +131 -0
- package/packages/server/src/event-wiring.ts +589 -0
- package/packages/server/src/extension-register.ts +92 -0
- package/packages/server/src/git-operations.ts +200 -0
- package/packages/server/src/headless-pid-registry.ts +207 -0
- package/packages/server/src/idle-timer.ts +61 -0
- package/packages/server/src/json-store.ts +32 -0
- package/packages/server/src/localhost-guard.ts +117 -0
- package/packages/server/src/memory-event-store.ts +193 -0
- package/packages/server/src/memory-session-manager.ts +123 -0
- package/packages/server/src/meta-persistence.ts +64 -0
- package/packages/server/src/migrate-persistence.ts +195 -0
- package/packages/server/src/npm-search-proxy.ts +143 -0
- package/packages/server/src/oauth-callback-server.ts +177 -0
- package/packages/server/src/openspec-archive.ts +60 -0
- package/packages/server/src/package-manager-wrapper.ts +200 -0
- package/packages/server/src/pending-fork-registry.ts +53 -0
- package/packages/server/src/pending-load-manager.ts +110 -0
- package/packages/server/src/pending-resume-registry.ts +69 -0
- package/packages/server/src/pi-gateway.ts +419 -0
- package/packages/server/src/pi-resource-scanner.ts +369 -0
- package/packages/server/src/preferences-store.ts +116 -0
- package/packages/server/src/process-manager.ts +311 -0
- package/packages/server/src/provider-auth-handlers.ts +438 -0
- package/packages/server/src/provider-auth-storage.ts +200 -0
- package/packages/server/src/resolve-path.ts +12 -0
- package/packages/server/src/routes/editor-routes.ts +86 -0
- package/packages/server/src/routes/file-routes.ts +116 -0
- package/packages/server/src/routes/git-routes.ts +89 -0
- package/packages/server/src/routes/openspec-routes.ts +99 -0
- package/packages/server/src/routes/package-routes.ts +172 -0
- package/packages/server/src/routes/provider-auth-routes.ts +244 -0
- package/packages/server/src/routes/provider-routes.ts +101 -0
- package/packages/server/src/routes/route-deps.ts +23 -0
- package/packages/server/src/routes/session-routes.ts +91 -0
- package/packages/server/src/routes/system-routes.ts +271 -0
- package/packages/server/src/server-pid.ts +84 -0
- package/packages/server/src/server.ts +554 -0
- package/packages/server/src/session-api.ts +330 -0
- package/packages/server/src/session-bootstrap.ts +80 -0
- package/packages/server/src/session-diff.ts +178 -0
- package/packages/server/src/session-discovery.ts +134 -0
- package/packages/server/src/session-file-reader.ts +135 -0
- package/packages/server/src/session-order-manager.ts +73 -0
- package/packages/server/src/session-scanner.ts +233 -0
- package/packages/server/src/session-stats-reader.ts +99 -0
- package/packages/server/src/terminal-gateway.ts +51 -0
- package/packages/server/src/terminal-manager.ts +241 -0
- package/packages/server/src/tunnel.ts +329 -0
- package/packages/server/tsconfig.json +11 -0
- package/packages/shared/package.json +15 -0
- package/packages/shared/src/__tests__/config.test.ts +358 -0
- package/packages/shared/src/__tests__/deriveChangeState.test.ts +95 -0
- package/packages/shared/src/__tests__/mdns-discovery.test.ts +80 -0
- package/packages/shared/src/__tests__/protocol.test.ts +243 -0
- package/packages/shared/src/__tests__/resolve-jiti.test.ts +17 -0
- package/packages/shared/src/__tests__/server-identity.test.ts +73 -0
- package/packages/shared/src/__tests__/session-meta.test.ts +125 -0
- package/packages/shared/src/archive-types.ts +11 -0
- package/packages/shared/src/browser-protocol.ts +534 -0
- package/packages/shared/src/config.ts +245 -0
- package/packages/shared/src/diff-types.ts +41 -0
- package/packages/shared/src/editor-types.ts +18 -0
- package/packages/shared/src/mdns-discovery.ts +248 -0
- package/packages/shared/src/openspec-activity-detector.ts +109 -0
- package/packages/shared/src/openspec-poller.ts +96 -0
- package/packages/shared/src/protocol.ts +369 -0
- package/packages/shared/src/resolve-jiti.ts +43 -0
- package/packages/shared/src/rest-api.ts +255 -0
- package/packages/shared/src/server-identity.ts +51 -0
- package/packages/shared/src/session-meta.ts +86 -0
- package/packages/shared/src/state-replay.ts +174 -0
- package/packages/shared/src/stats-extractor.ts +54 -0
- package/packages/shared/src/terminal-types.ts +18 -0
- package/packages/shared/src/types.ts +351 -0
- package/packages/shared/tsconfig.json +8 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import type {
|
|
3
|
+
ExtensionToServerMessage,
|
|
4
|
+
ServerToExtensionMessage,
|
|
5
|
+
} from "../protocol.js";
|
|
6
|
+
import type {
|
|
7
|
+
ServerToBrowserMessage,
|
|
8
|
+
BrowserToServerMessage,
|
|
9
|
+
} from "../browser-protocol.js";
|
|
10
|
+
import type {
|
|
11
|
+
DashboardSession,
|
|
12
|
+
DashboardEvent,
|
|
13
|
+
SessionSource,
|
|
14
|
+
SessionStatus,
|
|
15
|
+
CommandInfo,
|
|
16
|
+
ApiResponse,
|
|
17
|
+
} from "../types.js";
|
|
18
|
+
|
|
19
|
+
describe("Protocol message serialization round-trip", () => {
|
|
20
|
+
it("should serialize/deserialize extension→server messages", () => {
|
|
21
|
+
const messages: ExtensionToServerMessage[] = [
|
|
22
|
+
{
|
|
23
|
+
type: "session_register",
|
|
24
|
+
sessionId: "s1",
|
|
25
|
+
cwd: "/home/user/project",
|
|
26
|
+
source: "tui",
|
|
27
|
+
model: "claude-sonnet-4-20250514",
|
|
28
|
+
thinkingLevel: "medium",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
type: "session_unregister",
|
|
32
|
+
sessionId: "s1",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
type: "session_heartbeat",
|
|
36
|
+
sessionId: "s1",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
type: "event_forward",
|
|
40
|
+
sessionId: "s1",
|
|
41
|
+
event: {
|
|
42
|
+
eventType: "message_update",
|
|
43
|
+
timestamp: Date.now(),
|
|
44
|
+
data: { text: "Hello" },
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
type: "commands_list",
|
|
49
|
+
sessionId: "s1",
|
|
50
|
+
commands: [
|
|
51
|
+
{ name: "test", description: "Run tests", source: "extension" },
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
type: "extension_ui_request",
|
|
56
|
+
sessionId: "s1",
|
|
57
|
+
requestId: "req-1",
|
|
58
|
+
method: "confirm",
|
|
59
|
+
params: { title: "Allow?", message: "Delete files?" },
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
for (const msg of messages) {
|
|
65
|
+
const json = JSON.stringify(msg);
|
|
66
|
+
const parsed = JSON.parse(json) as ExtensionToServerMessage;
|
|
67
|
+
expect(parsed).toEqual(msg);
|
|
68
|
+
expect(parsed.type).toBe(msg.type);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("should serialize/deserialize server→extension messages", () => {
|
|
73
|
+
const messages: ServerToExtensionMessage[] = [
|
|
74
|
+
{
|
|
75
|
+
type: "send_prompt",
|
|
76
|
+
sessionId: "s1",
|
|
77
|
+
text: "Hello agent",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
type: "abort",
|
|
81
|
+
sessionId: "s1",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
type: "request_commands",
|
|
85
|
+
sessionId: "s1",
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
type: "request_state_sync",
|
|
89
|
+
sessionId: "s1",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
type: "extension_ui_response",
|
|
93
|
+
sessionId: "s1",
|
|
94
|
+
requestId: "req-1",
|
|
95
|
+
result: { confirmed: true },
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
type: "extension_ui_response",
|
|
99
|
+
sessionId: "s1",
|
|
100
|
+
requestId: "req-2",
|
|
101
|
+
cancelled: true,
|
|
102
|
+
},
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
for (const msg of messages) {
|
|
106
|
+
const json = JSON.stringify(msg);
|
|
107
|
+
const parsed = JSON.parse(json) as ServerToExtensionMessage;
|
|
108
|
+
expect(parsed).toEqual(msg);
|
|
109
|
+
expect(parsed.type).toBe(msg.type);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("should serialize/deserialize server→browser messages", () => {
|
|
114
|
+
const messages: ServerToBrowserMessage[] = [
|
|
115
|
+
{
|
|
116
|
+
type: "session_added",
|
|
117
|
+
session: {
|
|
118
|
+
id: "s1",
|
|
119
|
+
cwd: "/project",
|
|
120
|
+
source: "tui",
|
|
121
|
+
status: "active",
|
|
122
|
+
model: "claude-sonnet-4-20250514",
|
|
123
|
+
thinkingLevel: "medium",
|
|
124
|
+
startedAt: Date.now(),
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
type: "session_updated",
|
|
129
|
+
sessionId: "s1",
|
|
130
|
+
updates: { status: "streaming" },
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
type: "session_removed",
|
|
134
|
+
sessionId: "s1",
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
type: "event",
|
|
138
|
+
sessionId: "s1",
|
|
139
|
+
seq: 42,
|
|
140
|
+
event: {
|
|
141
|
+
eventType: "message_update",
|
|
142
|
+
timestamp: Date.now(),
|
|
143
|
+
data: { text: "hi" },
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
type: "event_replay",
|
|
148
|
+
sessionId: "s1",
|
|
149
|
+
events: [],
|
|
150
|
+
isLast: true,
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
type: "commands_list",
|
|
154
|
+
sessionId: "s1",
|
|
155
|
+
commands: [],
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
type: "extension_ui_request",
|
|
159
|
+
sessionId: "s1",
|
|
160
|
+
requestId: "req-2",
|
|
161
|
+
method: "notify",
|
|
162
|
+
params: { message: "done", level: "info" },
|
|
163
|
+
},
|
|
164
|
+
];
|
|
165
|
+
|
|
166
|
+
for (const msg of messages) {
|
|
167
|
+
const json = JSON.stringify(msg);
|
|
168
|
+
const parsed = JSON.parse(json) as ServerToBrowserMessage;
|
|
169
|
+
expect(parsed).toEqual(msg);
|
|
170
|
+
expect(parsed.type).toBe(msg.type);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("should serialize/deserialize browser→server messages", () => {
|
|
175
|
+
const messages: BrowserToServerMessage[] = [
|
|
176
|
+
{
|
|
177
|
+
type: "subscribe",
|
|
178
|
+
sessionId: "s1",
|
|
179
|
+
lastSeq: 0,
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
type: "unsubscribe",
|
|
183
|
+
sessionId: "s1",
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
type: "send_prompt",
|
|
187
|
+
sessionId: "s1",
|
|
188
|
+
text: "Hello",
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
type: "abort",
|
|
192
|
+
sessionId: "s1",
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
type: "request_commands",
|
|
196
|
+
sessionId: "s1",
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
type: "fetch_content",
|
|
200
|
+
sessionId: "s1",
|
|
201
|
+
seq: 42,
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
type: "extension_ui_response",
|
|
205
|
+
sessionId: "s1",
|
|
206
|
+
requestId: "req-1",
|
|
207
|
+
result: { value: "Option A" },
|
|
208
|
+
},
|
|
209
|
+
];
|
|
210
|
+
|
|
211
|
+
for (const msg of messages) {
|
|
212
|
+
const json = JSON.stringify(msg);
|
|
213
|
+
const parsed = JSON.parse(json) as BrowserToServerMessage;
|
|
214
|
+
expect(parsed).toEqual(msg);
|
|
215
|
+
expect(parsed.type).toBe(msg.type);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
describe("Shared data model types", () => {
|
|
221
|
+
it("should have correct SessionSource values", () => {
|
|
222
|
+
const sources: SessionSource[] = ["tui", "zed", "tmux", "dashboard", "unknown"];
|
|
223
|
+
expect(sources).toHaveLength(5);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it("should have correct SessionStatus values", () => {
|
|
227
|
+
const statuses: SessionStatus[] = ["active", "streaming", "ended"];
|
|
228
|
+
expect(statuses).toHaveLength(3);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it("should construct valid ApiResponse", () => {
|
|
232
|
+
const success: ApiResponse<{ id: string }> = {
|
|
233
|
+
success: true,
|
|
234
|
+
data: { id: "123" },
|
|
235
|
+
};
|
|
236
|
+
const error: ApiResponse = {
|
|
237
|
+
success: false,
|
|
238
|
+
error: "Not found",
|
|
239
|
+
};
|
|
240
|
+
expect(success.success).toBe(true);
|
|
241
|
+
expect(error.success).toBe(false);
|
|
242
|
+
});
|
|
243
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
|
|
3
|
+
describe("resolveJitiImport", () => {
|
|
4
|
+
it("throws with clear error when pi-coding-agent is not resolvable", async () => {
|
|
5
|
+
// In test context (vitest, not inside pi's jiti loader),
|
|
6
|
+
// peer deps are not resolvable — should throw
|
|
7
|
+
const { resolveJitiImport } = await import("../resolve-jiti.js");
|
|
8
|
+
|
|
9
|
+
expect(() => resolveJitiImport()).toThrow("Cannot find pi's TypeScript loader");
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("error message mentions pi-coding-agent", async () => {
|
|
13
|
+
const { resolveJitiImport } = await import("../resolve-jiti.js");
|
|
14
|
+
|
|
15
|
+
expect(() => resolveJitiImport()).toThrow("pi-coding-agent");
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { isDashboardRunning } from "../server-identity.js";
|
|
3
|
+
|
|
4
|
+
describe("isDashboardRunning", () => {
|
|
5
|
+
const originalFetch = globalThis.fetch;
|
|
6
|
+
|
|
7
|
+
afterEach(() => {
|
|
8
|
+
globalThis.fetch = originalFetch;
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("returns running: true with pid when health endpoint responds correctly", async () => {
|
|
12
|
+
globalThis.fetch = vi.fn().mockResolvedValue({
|
|
13
|
+
ok: true,
|
|
14
|
+
json: () => Promise.resolve({ ok: true, pid: 12345, uptime: 60 }),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const result = await isDashboardRunning(8000);
|
|
18
|
+
expect(result).toEqual({ running: true, pid: 12345 });
|
|
19
|
+
expect(globalThis.fetch).toHaveBeenCalledWith(
|
|
20
|
+
"http://localhost:8000/api/health",
|
|
21
|
+
expect.objectContaining({ signal: expect.any(AbortSignal) }),
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("returns portConflict when port returns non-ok HTTP status", async () => {
|
|
26
|
+
globalThis.fetch = vi.fn().mockResolvedValue({
|
|
27
|
+
ok: false,
|
|
28
|
+
status: 404,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const result = await isDashboardRunning(8000);
|
|
32
|
+
expect(result).toEqual({ running: false, portConflict: true });
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("returns portConflict when response is HTTP 200 but not dashboard format", async () => {
|
|
36
|
+
globalThis.fetch = vi.fn().mockResolvedValue({
|
|
37
|
+
ok: true,
|
|
38
|
+
json: () => Promise.resolve({ status: "ok", service: "nginx" }),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const result = await isDashboardRunning(8000);
|
|
42
|
+
expect(result).toEqual({ running: false, portConflict: true });
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("returns running: false when connection refused (nothing running)", async () => {
|
|
46
|
+
globalThis.fetch = vi.fn().mockRejectedValue(new Error("ECONNREFUSED"));
|
|
47
|
+
|
|
48
|
+
const result = await isDashboardRunning(8000);
|
|
49
|
+
expect(result).toEqual({ running: false });
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("returns running: false when request times out", async () => {
|
|
53
|
+
const abortError = new Error("aborted");
|
|
54
|
+
abortError.name = "AbortError";
|
|
55
|
+
globalThis.fetch = vi.fn().mockRejectedValue(abortError);
|
|
56
|
+
|
|
57
|
+
const result = await isDashboardRunning(8000);
|
|
58
|
+
expect(result).toEqual({ running: false });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("uses custom host when provided", async () => {
|
|
62
|
+
globalThis.fetch = vi.fn().mockResolvedValue({
|
|
63
|
+
ok: true,
|
|
64
|
+
json: () => Promise.resolve({ ok: true, pid: 999 }),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
await isDashboardRunning(8000, "192.168.1.10");
|
|
68
|
+
expect(globalThis.fetch).toHaveBeenCalledWith(
|
|
69
|
+
"http://192.168.1.10:8000/api/health",
|
|
70
|
+
expect.any(Object),
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import { metaPath, readSessionMeta, writeSessionMeta, mergeSessionMeta } from "../session-meta.js";
|
|
6
|
+
|
|
7
|
+
describe("session-meta", () => {
|
|
8
|
+
let tmpDir: string;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "session-meta-test-"));
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe("metaPath", () => {
|
|
19
|
+
it("should derive .meta.json from .jsonl path", () => {
|
|
20
|
+
const sessionFile = "/home/user/.pi/sessions/cwd/2026-01-01T00-00-00-000Z_abc123.jsonl";
|
|
21
|
+
expect(metaPath(sessionFile)).toBe(
|
|
22
|
+
"/home/user/.pi/sessions/cwd/2026-01-01T00-00-00-000Z_abc123.meta.json"
|
|
23
|
+
);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe("writeSessionMeta / readSessionMeta", () => {
|
|
28
|
+
it("should write and read meta", () => {
|
|
29
|
+
const sessionFile = path.join(tmpDir, "test-session.jsonl");
|
|
30
|
+
writeSessionMeta(sessionFile, { source: "dashboard" });
|
|
31
|
+
const meta = readSessionMeta(sessionFile);
|
|
32
|
+
expect(meta).toEqual({ source: "dashboard" });
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("should return undefined for missing meta file", () => {
|
|
36
|
+
const sessionFile = path.join(tmpDir, "nonexistent.jsonl");
|
|
37
|
+
expect(readSessionMeta(sessionFile)).toBeUndefined();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("should return undefined for invalid JSON", () => {
|
|
41
|
+
const sessionFile = path.join(tmpDir, "bad.jsonl");
|
|
42
|
+
fs.writeFileSync(path.join(tmpDir, "bad.meta.json"), "not json");
|
|
43
|
+
expect(readSessionMeta(sessionFile)).toBeUndefined();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should write and read expanded fields", () => {
|
|
47
|
+
const sessionFile = path.join(tmpDir, "expanded.jsonl");
|
|
48
|
+
writeSessionMeta(sessionFile, {
|
|
49
|
+
source: "dashboard",
|
|
50
|
+
name: "General",
|
|
51
|
+
attachedProposal: "my-change",
|
|
52
|
+
hidden: false,
|
|
53
|
+
cwd: "/Users/test/project",
|
|
54
|
+
status: "ended",
|
|
55
|
+
startedAt: 1000,
|
|
56
|
+
endedAt: 2000,
|
|
57
|
+
model: "anthropic/claude-sonnet-4-20250514",
|
|
58
|
+
thinkingLevel: "medium",
|
|
59
|
+
tokensIn: 100,
|
|
60
|
+
tokensOut: 200,
|
|
61
|
+
cacheRead: 300,
|
|
62
|
+
cacheWrite: 400,
|
|
63
|
+
cost: 1.5,
|
|
64
|
+
contextTokens: 5000,
|
|
65
|
+
contextWindow: 200000,
|
|
66
|
+
firstMessage: "Hello",
|
|
67
|
+
cachedAt: 3000,
|
|
68
|
+
});
|
|
69
|
+
const meta = readSessionMeta(sessionFile);
|
|
70
|
+
expect(meta?.name).toBe("General");
|
|
71
|
+
expect(meta?.cost).toBe(1.5);
|
|
72
|
+
expect(meta?.cachedAt).toBe(3000);
|
|
73
|
+
expect(meta?.hidden).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("should read minimal meta (backward compat)", () => {
|
|
77
|
+
const sessionFile = path.join(tmpDir, "minimal.jsonl");
|
|
78
|
+
writeSessionMeta(sessionFile, { source: "dashboard" });
|
|
79
|
+
const meta = readSessionMeta(sessionFile);
|
|
80
|
+
expect(meta).toEqual({ source: "dashboard" });
|
|
81
|
+
expect(meta?.name).toBeUndefined();
|
|
82
|
+
expect(meta?.cost).toBeUndefined();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("should use atomic write (tmp + rename)", () => {
|
|
86
|
+
const sessionFile = path.join(tmpDir, "atomic.jsonl");
|
|
87
|
+
writeSessionMeta(sessionFile, { source: "dashboard" });
|
|
88
|
+
// tmp file should not remain
|
|
89
|
+
const tmpFile = metaPath(sessionFile) + ".tmp";
|
|
90
|
+
expect(fs.existsSync(tmpFile)).toBe(false);
|
|
91
|
+
expect(fs.existsSync(metaPath(sessionFile))).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe("mergeSessionMeta", () => {
|
|
96
|
+
it("should merge new fields into existing meta", () => {
|
|
97
|
+
const sessionFile = path.join(tmpDir, "merge.jsonl");
|
|
98
|
+
writeSessionMeta(sessionFile, { source: "dashboard", name: "Old" });
|
|
99
|
+
mergeSessionMeta(sessionFile, { name: "New", cost: 5.0 });
|
|
100
|
+
const meta = readSessionMeta(sessionFile);
|
|
101
|
+
expect(meta?.source).toBe("dashboard");
|
|
102
|
+
expect(meta?.name).toBe("New");
|
|
103
|
+
expect(meta?.cost).toBe(5.0);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("should create file if it does not exist", () => {
|
|
107
|
+
const sessionFile = path.join(tmpDir, "new-merge.jsonl");
|
|
108
|
+
mergeSessionMeta(sessionFile, { hidden: true, cost: 1.0 });
|
|
109
|
+
const meta = readSessionMeta(sessionFile);
|
|
110
|
+
expect(meta?.hidden).toBe(true);
|
|
111
|
+
expect(meta?.cost).toBe(1.0);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("should preserve unknown fields", () => {
|
|
115
|
+
const sessionFile = path.join(tmpDir, "unknown.jsonl");
|
|
116
|
+
// Write a file with an unknown field
|
|
117
|
+
const p = metaPath(sessionFile);
|
|
118
|
+
fs.writeFileSync(p, JSON.stringify({ source: "dashboard", customField: 42 }) + "\n");
|
|
119
|
+
mergeSessionMeta(sessionFile, { name: "Test" });
|
|
120
|
+
const raw = JSON.parse(fs.readFileSync(p, "utf-8"));
|
|
121
|
+
expect(raw.customField).toBe(42);
|
|
122
|
+
expect(raw.name).toBe("Test");
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type for OpenSpec archive entries. Shared between server and client.
|
|
3
|
+
*/
|
|
4
|
+
export interface ArchiveEntry {
|
|
5
|
+
/** Full directory name, e.g. "2026-03-27-openspec-artifact-reader" */
|
|
6
|
+
name: string;
|
|
7
|
+
/** Date extracted from the prefix, e.g. "2026-03-27" */
|
|
8
|
+
date: string;
|
|
9
|
+
/** Detected artifacts with status "done" */
|
|
10
|
+
artifacts: { id: string; status: "done" }[];
|
|
11
|
+
}
|