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.
@@ -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
+ }