opencode-dashboard 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +329 -0
- package/agents/orchestrator.md +99 -0
- package/agents/pipeline-builder.md +53 -0
- package/agents/pipeline-committer.md +78 -0
- package/agents/pipeline-refactor.md +58 -0
- package/agents/pipeline-reviewer.md +68 -0
- package/bin/cli.ts +332 -0
- package/commands/dashboard-start.md +5 -0
- package/commands/dashboard-status.md +5 -0
- package/commands/dashboard-stop.md +5 -0
- package/dist/assets/index-W-qyIr7d.js +134 -0
- package/dist/assets/index-mMdK5PVd.css +1 -0
- package/dist/index.html +13 -0
- package/package.json +82 -0
- package/plugin/index.ts +1441 -0
- package/server/PLUGIN_EVENTS.md +410 -0
- package/server/index.ts +55 -0
- package/server/pid.ts +140 -0
- package/server/routes.ts +520 -0
- package/server/sse.ts +196 -0
- package/server/state.ts +936 -0
- package/shared/types.ts +402 -0
package/shared/types.ts
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared TypeScript types for server, plugin, and React dashboard app.
|
|
3
|
+
*
|
|
4
|
+
* These types define the canonical data model used across all three components:
|
|
5
|
+
* - Plugin → Server: event payloads (POST /api/plugin/event)
|
|
6
|
+
* - Server → Dashboard: SSE events + REST API responses
|
|
7
|
+
* - Dashboard: internal state management
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// ============================================================
|
|
11
|
+
// Core Domain Types
|
|
12
|
+
// ============================================================
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Pipeline stage — dynamic string matching column IDs.
|
|
16
|
+
*
|
|
17
|
+
* Well-known values:
|
|
18
|
+
* - Status columns: "ready", "done", "error"
|
|
19
|
+
* - Agent columns: agent name (e.g., "orchestrator", "pipeline-builder")
|
|
20
|
+
*/
|
|
21
|
+
export type Stage = string;
|
|
22
|
+
|
|
23
|
+
/** Pipeline status */
|
|
24
|
+
export type PipelineStatus = "active" | "idle" | "done";
|
|
25
|
+
|
|
26
|
+
/** Bead status from `bd list --json` */
|
|
27
|
+
export type BdStatus = "open" | "in_progress" | "blocked" | "closed";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Agent type — dynamic string matching discovered agent names.
|
|
31
|
+
* No longer a fixed union; any agent name is valid.
|
|
32
|
+
*/
|
|
33
|
+
export type AgentType = string;
|
|
34
|
+
|
|
35
|
+
// ============================================================
|
|
36
|
+
// Raw Bead Record (from bd list --json)
|
|
37
|
+
// ============================================================
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Raw bead record as returned by `bd list --json`.
|
|
41
|
+
* Used in plugin event payloads and server state.
|
|
42
|
+
*/
|
|
43
|
+
export interface BeadRecord {
|
|
44
|
+
id: string;
|
|
45
|
+
title: string;
|
|
46
|
+
description: string;
|
|
47
|
+
status: string; // BdStatus, but left as string for forward compat
|
|
48
|
+
priority: number; // 0 (critical) to 4 (backlog)
|
|
49
|
+
issue_type: string; // bug, feature, task, epic, chore, decision
|
|
50
|
+
created_at: string; // ISO timestamp
|
|
51
|
+
updated_at: string; // ISO timestamp
|
|
52
|
+
closed_at?: string; // ISO timestamp (if closed)
|
|
53
|
+
close_reason?: string;
|
|
54
|
+
dependencies?: Array<{
|
|
55
|
+
type: string;
|
|
56
|
+
depends_on_id: string;
|
|
57
|
+
}>;
|
|
58
|
+
// Extra fields from bd list --json (not in plan, but present)
|
|
59
|
+
dependency_count?: number;
|
|
60
|
+
dependent_count?: number;
|
|
61
|
+
comment_count?: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ============================================================
|
|
65
|
+
// Bead Diff (used in plugin for snapshot diffing)
|
|
66
|
+
// ============================================================
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* A single diff entry between two bead snapshots.
|
|
70
|
+
*
|
|
71
|
+
* - `discovered`: bead exists in `next` but not `prev` (new bead appeared)
|
|
72
|
+
* - `changed`: bead exists in both but status/fields differ
|
|
73
|
+
* - `removed`: bead exists in `prev` but not `next` (bead deleted)
|
|
74
|
+
* - `error`: bead transitioned to an error state (e.g., blocked)
|
|
75
|
+
*/
|
|
76
|
+
export type BeadDiff =
|
|
77
|
+
| { type: "discovered"; bead: BeadRecord }
|
|
78
|
+
| { type: "changed"; bead: BeadRecord; prevStatus: string }
|
|
79
|
+
| { type: "removed"; beadId: string }
|
|
80
|
+
| { type: "error"; bead: BeadRecord; error: string };
|
|
81
|
+
|
|
82
|
+
// ============================================================
|
|
83
|
+
// Bead State (server-enriched, used in dashboard)
|
|
84
|
+
// ============================================================
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Bead state as tracked by the server, enriched with pipeline stage info.
|
|
88
|
+
* This is the canonical representation used in the dashboard UI.
|
|
89
|
+
*/
|
|
90
|
+
export interface BeadState {
|
|
91
|
+
id: string;
|
|
92
|
+
title: string;
|
|
93
|
+
description: string;
|
|
94
|
+
priority: number; // 0-4
|
|
95
|
+
issueType: string;
|
|
96
|
+
bdStatus: string; // BdStatus
|
|
97
|
+
stage: Stage;
|
|
98
|
+
stageStartedAt: number; // timestamp (ms)
|
|
99
|
+
claimedAt?: number; // when orchestrator claimed
|
|
100
|
+
completedAt?: number; // when bead was closed
|
|
101
|
+
agentSessionId?: string; // current child session working on it
|
|
102
|
+
error?: string; // error message if in error stage
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ============================================================
|
|
106
|
+
// Pipeline
|
|
107
|
+
// ============================================================
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* A pipeline represents one orchestrator session working through beads.
|
|
111
|
+
* Each project can have multiple pipelines (one per orchestrator session).
|
|
112
|
+
*/
|
|
113
|
+
export interface Pipeline {
|
|
114
|
+
id: string; // orchestrator session ID
|
|
115
|
+
title: string; // derived from first bead or session
|
|
116
|
+
status: PipelineStatus;
|
|
117
|
+
currentBeadId: string | null;
|
|
118
|
+
beads: Map<string, BeadState>;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ============================================================
|
|
122
|
+
// Project State
|
|
123
|
+
// ============================================================
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* State for a single connected project (one OpenCode instance).
|
|
127
|
+
* Identified by projectPath (working directory).
|
|
128
|
+
*/
|
|
129
|
+
export interface ProjectState {
|
|
130
|
+
projectPath: string; // e.g., "/Users/.../project-a"
|
|
131
|
+
projectName: string; // derived from directory basename
|
|
132
|
+
pluginId: string; // unique ID for this plugin connection
|
|
133
|
+
lastHeartbeat: number; // timestamp (ms)
|
|
134
|
+
connected: boolean; // is the plugin alive?
|
|
135
|
+
pipelines: Map<string, Pipeline>;
|
|
136
|
+
lastBeadSnapshot: BeadRecord[];
|
|
137
|
+
columns: ColumnConfig[]; // dynamic column layout from agent discovery
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ============================================================
|
|
141
|
+
// Dashboard State (top-level)
|
|
142
|
+
// ============================================================
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Top-level aggregated state across all connected projects.
|
|
146
|
+
* This is the canonical state maintained by the server.
|
|
147
|
+
*/
|
|
148
|
+
export interface DashboardState {
|
|
149
|
+
projects: Map<string, ProjectState>;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ============================================================
|
|
153
|
+
// Plugin Event Types
|
|
154
|
+
// ============================================================
|
|
155
|
+
|
|
156
|
+
/** All plugin event type strings */
|
|
157
|
+
export type PluginEventType =
|
|
158
|
+
| "bead:discovered"
|
|
159
|
+
| "bead:claimed"
|
|
160
|
+
| "bead:stage"
|
|
161
|
+
| "bead:done"
|
|
162
|
+
| "bead:error"
|
|
163
|
+
| "bead:changed"
|
|
164
|
+
| "bead:removed"
|
|
165
|
+
| "agent:active"
|
|
166
|
+
| "agent:idle"
|
|
167
|
+
| "beads:refreshed"
|
|
168
|
+
| "pipeline:started"
|
|
169
|
+
| "pipeline:done"
|
|
170
|
+
| "columns:update";
|
|
171
|
+
|
|
172
|
+
/** SSE event types sent from server to dashboard */
|
|
173
|
+
export type SSEEventType =
|
|
174
|
+
| PluginEventType
|
|
175
|
+
| "connected"
|
|
176
|
+
| "state:full"
|
|
177
|
+
| "project:connected"
|
|
178
|
+
| "project:disconnected";
|
|
179
|
+
|
|
180
|
+
// ============================================================
|
|
181
|
+
// Plugin Event Payloads
|
|
182
|
+
// ============================================================
|
|
183
|
+
|
|
184
|
+
/** Common fields present in all plugin event payloads */
|
|
185
|
+
interface BaseEventPayload {
|
|
186
|
+
projectPath: string;
|
|
187
|
+
timestamp: number;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** bead:discovered — new bead found in bd list --json */
|
|
191
|
+
export interface BeadDiscoveredPayload extends BaseEventPayload {
|
|
192
|
+
bead: BeadRecord;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** bead:claimed — bead status changed to in_progress */
|
|
196
|
+
export interface BeadClaimedPayload extends BaseEventPayload {
|
|
197
|
+
beadId: string;
|
|
198
|
+
bead: BeadRecord;
|
|
199
|
+
stage: string; // agent name that claimed it (e.g., "orchestrator")
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** bead:stage — bead moving to a new pipeline stage */
|
|
203
|
+
export interface BeadStagePayload extends BaseEventPayload {
|
|
204
|
+
beadId: string;
|
|
205
|
+
stage: string; // agent name (column ID)
|
|
206
|
+
agentSessionId?: string;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** bead:done — bead closed successfully */
|
|
210
|
+
export interface BeadDonePayload extends BaseEventPayload {
|
|
211
|
+
beadId: string;
|
|
212
|
+
bead: BeadRecord;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** bead:error — bead entered error state */
|
|
216
|
+
export interface BeadErrorPayload extends BaseEventPayload {
|
|
217
|
+
beadId: string;
|
|
218
|
+
bead?: BeadRecord;
|
|
219
|
+
error: string;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** bead:changed — bead status changed (not claimed/done/error) */
|
|
223
|
+
export interface BeadChangedPayload extends BaseEventPayload {
|
|
224
|
+
bead: BeadRecord;
|
|
225
|
+
prevStatus: string;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** bead:removed — bead disappeared from bd list --json */
|
|
229
|
+
export interface BeadRemovedPayload extends BaseEventPayload {
|
|
230
|
+
beadId: string;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** agent:active — child agent session created and mapped to pipeline stage */
|
|
234
|
+
export interface AgentActivePayload extends BaseEventPayload {
|
|
235
|
+
agent: string; // agent name
|
|
236
|
+
sessionId: string;
|
|
237
|
+
parentSessionId: string;
|
|
238
|
+
beadId: string;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** agent:idle — child agent session finished work */
|
|
242
|
+
export interface AgentIdlePayload extends BaseEventPayload {
|
|
243
|
+
agent: string; // agent name
|
|
244
|
+
sessionId: string;
|
|
245
|
+
beadId: string;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** beads:refreshed — summary after bead state refresh */
|
|
249
|
+
export interface BeadsRefreshedPayload extends BaseEventPayload {
|
|
250
|
+
beadCount: number;
|
|
251
|
+
changed: number;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** pipeline:started — new pipeline session detected */
|
|
255
|
+
export interface PipelineStartedPayload extends BaseEventPayload {
|
|
256
|
+
pipelineId: string;
|
|
257
|
+
title?: string;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** pipeline:done — all beads in pipeline completed */
|
|
261
|
+
export interface PipelineDonePayload extends BaseEventPayload {
|
|
262
|
+
pipelineId: string;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/** columns:update — plugin sends column configuration for the project */
|
|
266
|
+
export interface ColumnsUpdatePayload extends BaseEventPayload {
|
|
267
|
+
columns: ColumnConfig[];
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** Map of event type to its payload type */
|
|
271
|
+
export interface PluginEventPayloadMap {
|
|
272
|
+
"bead:discovered": BeadDiscoveredPayload;
|
|
273
|
+
"bead:claimed": BeadClaimedPayload;
|
|
274
|
+
"bead:stage": BeadStagePayload;
|
|
275
|
+
"bead:done": BeadDonePayload;
|
|
276
|
+
"bead:error": BeadErrorPayload;
|
|
277
|
+
"bead:changed": BeadChangedPayload;
|
|
278
|
+
"bead:removed": BeadRemovedPayload;
|
|
279
|
+
"agent:active": AgentActivePayload;
|
|
280
|
+
"agent:idle": AgentIdlePayload;
|
|
281
|
+
"beads:refreshed": BeadsRefreshedPayload;
|
|
282
|
+
"pipeline:started": PipelineStartedPayload;
|
|
283
|
+
"pipeline:done": PipelineDonePayload;
|
|
284
|
+
"columns:update": ColumnsUpdatePayload;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// ============================================================
|
|
288
|
+
// API Types (REST + SSE)
|
|
289
|
+
// ============================================================
|
|
290
|
+
|
|
291
|
+
/** POST /api/plugin/register request body */
|
|
292
|
+
export interface RegisterPluginRequest {
|
|
293
|
+
projectPath: string;
|
|
294
|
+
projectName: string;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/** POST /api/plugin/register response */
|
|
298
|
+
export interface RegisterPluginResponse {
|
|
299
|
+
pluginId: string;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/** POST /api/plugin/event request body */
|
|
303
|
+
export interface PluginEventRequest {
|
|
304
|
+
pluginId: string;
|
|
305
|
+
event: PluginEventType;
|
|
306
|
+
data?: Record<string, unknown>;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** POST /api/plugin/heartbeat request body */
|
|
310
|
+
export interface HeartbeatRequest {
|
|
311
|
+
pluginId: string;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// ============================================================
|
|
315
|
+
// SSE Event Payloads (server → dashboard)
|
|
316
|
+
// ============================================================
|
|
317
|
+
|
|
318
|
+
/** SSE connected event payload */
|
|
319
|
+
export interface SSEConnectedPayload {
|
|
320
|
+
message: string;
|
|
321
|
+
timestamp: number;
|
|
322
|
+
plugins: Array<{
|
|
323
|
+
pluginId: string;
|
|
324
|
+
projectPath: string;
|
|
325
|
+
projectName: string;
|
|
326
|
+
}>;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** SSE state:full event payload — serialized state snapshot */
|
|
330
|
+
export interface SSEStateFullPayload {
|
|
331
|
+
version: number;
|
|
332
|
+
savedAt: number;
|
|
333
|
+
projects: Array<
|
|
334
|
+
[
|
|
335
|
+
string,
|
|
336
|
+
{
|
|
337
|
+
projectPath: string;
|
|
338
|
+
projectName: string;
|
|
339
|
+
pluginId: string;
|
|
340
|
+
lastHeartbeat: number;
|
|
341
|
+
connected: boolean;
|
|
342
|
+
pipelines: Array<
|
|
343
|
+
[
|
|
344
|
+
string,
|
|
345
|
+
{
|
|
346
|
+
id: string;
|
|
347
|
+
title: string;
|
|
348
|
+
status: PipelineStatus;
|
|
349
|
+
currentBeadId: string | null;
|
|
350
|
+
beads: Array<[string, BeadState]>;
|
|
351
|
+
},
|
|
352
|
+
]
|
|
353
|
+
>;
|
|
354
|
+
lastBeadSnapshot: BeadRecord[];
|
|
355
|
+
columns: ColumnConfig[];
|
|
356
|
+
},
|
|
357
|
+
]
|
|
358
|
+
>;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/** SSE project:connected event payload */
|
|
362
|
+
export interface SSEProjectConnectedPayload {
|
|
363
|
+
pluginId: string;
|
|
364
|
+
projectPath: string;
|
|
365
|
+
projectName: string;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/** SSE project:disconnected event payload */
|
|
369
|
+
export interface SSEProjectDisconnectedPayload {
|
|
370
|
+
pluginId: string;
|
|
371
|
+
projectPath: string;
|
|
372
|
+
projectName: string;
|
|
373
|
+
reason: string;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// ============================================================
|
|
377
|
+
// Dashboard UI Types
|
|
378
|
+
// ============================================================
|
|
379
|
+
|
|
380
|
+
/** Connection status for the dashboard's SSE connection */
|
|
381
|
+
export type ConnectionStatus = "connecting" | "connected" | "reconnecting" | "disconnected";
|
|
382
|
+
|
|
383
|
+
/** Priority display configuration */
|
|
384
|
+
export interface PriorityConfig {
|
|
385
|
+
label: string;
|
|
386
|
+
colorClass: string; // Tailwind class
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Column configuration for the Kanban board.
|
|
391
|
+
*
|
|
392
|
+
* Columns are either status bookends ("ready", "done", "error") or
|
|
393
|
+
* agent columns (one per discovered agent). The plugin generates this
|
|
394
|
+
* config from agent discovery and sends it to the server.
|
|
395
|
+
*/
|
|
396
|
+
export interface ColumnConfig {
|
|
397
|
+
id: string; // column ID = stage value (e.g., "ready", "pipeline-builder")
|
|
398
|
+
label: string; // display name (e.g., "Ready", "Builder")
|
|
399
|
+
type: "status" | "agent"; // bookend vs agent column
|
|
400
|
+
color: string; // hex color for accent border (e.g., "#8b5cf6")
|
|
401
|
+
order: number; // position in board (0-based)
|
|
402
|
+
}
|