opencode-dashboard 0.1.0 → 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/README.md CHANGED
@@ -41,6 +41,8 @@ npx opencode-dashboard start
41
41
 
42
42
  The dashboard will be available at `http://localhost:3333`.
43
43
 
44
+ After the first activation, the dashboard **auto-starts on subsequent OpenCode sessions** — you don't need to run `/dashboard-start` again. This works across terminals: if you open a second OpenCode session while the dashboard is already running, it connects automatically.
45
+
44
46
  ### Check Status
45
47
 
46
48
  ```
@@ -65,6 +67,8 @@ Or via CLI:
65
67
  npx opencode-dashboard stop
66
68
  ```
67
69
 
70
+ This also **disables auto-start** — the dashboard will stay dormant on future sessions until you explicitly run `/dashboard-start` again.
71
+
68
72
  ### Custom Port
69
73
 
70
74
  ```bash
@@ -90,7 +94,7 @@ OpenCode Plugin (plugin/index.ts)
90
94
  ```
91
95
 
92
96
  1. **Plugin** -- An OpenCode plugin that hooks into agent lifecycle events, discovers configured agents, tracks bead state via `bd list --json`, and pushes structured events to the server. Registers custom tools (`dashboard_start`, `dashboard_stop`, `dashboard_status`, `dashboard_open`) for controlling the dashboard from within OpenCode.
93
- 2. **Server** -- A Bun HTTP server that aggregates state from connected plugins, persists it to disk, serves the dashboard frontend, and broadcasts updates to browser clients via Server-Sent Events (SSE).
97
+ 2. **Server** -- A Bun HTTP server that aggregates state from connected plugins, persists it to disk, serves the dashboard frontend, and broadcasts updates to browser clients via Server-Sent Events (SSE). The server automatically shuts down after 5 minutes of inactivity (no connected plugins or browser clients) to avoid leaving orphaned processes. Multiple OpenCode sessions share a single server instance — it only shuts down when all sessions have disconnected.
94
98
  3. **Dashboard** -- A React SPA that renders a dynamic Kanban board with real-time updates, animated card transitions, and connection resilience.
95
99
 
96
100
  ### Kanban Columns
@@ -220,6 +224,7 @@ bun test
220
224
  | `server/state.test.ts` | Server state manager (event processing, persistence) |
221
225
  | `server/routes.test.ts` | HTTP route handlers (register, event, heartbeat) |
222
226
  | `server/sse.test.ts` | SSE client management and broadcasting |
227
+ | `server/pid.test.ts` | PID file and autostart marker management |
223
228
  | `server/diffBeadState.test.ts` | Bead snapshot diffing algorithm |
224
229
 
225
230
  ### Building
@@ -235,6 +240,7 @@ bun run build:check # Run TypeScript type checking
235
240
  |----------|---------|-------------|
236
241
  | `DASHBOARD_PORT` | `3333` | Port for the dashboard server |
237
242
  | `DASHBOARD_DEBUG` | (unset) | Set to `1` to enable verbose plugin logging to stderr |
243
+ | `DASHBOARD_IDLE_TIMEOUT_MS` | `300000` (5 min) | Idle auto-shutdown timeout in milliseconds. Set to `0` to disable |
238
244
 
239
245
  ## Project Structure
240
246
 
@@ -324,6 +330,19 @@ The server saves state to `server/.dashboard-state.json`. Delete this file to st
324
330
  rm server/.dashboard-state.json
325
331
  ```
326
332
 
333
+ ### Dashboard keeps auto-starting (or won't auto-start)
334
+
335
+ The plugin stores an autostart marker at `~/.cache/opencode/opencode-dashboard.autostart`. To reset auto-start behavior:
336
+
337
+ ```bash
338
+ # Disable auto-start
339
+ rm ~/.cache/opencode/opencode-dashboard.autostart
340
+
341
+ # Or just run /dashboard-stop — it clears the marker for you
342
+ ```
343
+
344
+ If the dashboard *should* auto-start but isn't, run `/dashboard-start` once to re-create the marker.
345
+
327
346
  ## License
328
347
 
329
348
  [MIT](LICENSE)
package/bin/cli.ts CHANGED
@@ -20,7 +20,8 @@
20
20
  import { join } from "path";
21
21
  import { readdir, copyFile, mkdir, stat } from "fs/promises";
22
22
  import { createInterface } from "readline";
23
- import { readPid, removePid, isServerRunning } from "../server/pid";
23
+ import { readPid, removePid, isServerRunning, openLogFile, getLogFilePath } from "../server/pid";
24
+ import { closeSync } from "fs";
24
25
 
25
26
  // ─── Constants ─────────────────────────────────────────────────
26
27
 
@@ -110,16 +111,27 @@ async function cmdStart(args: string[]): Promise<void> {
110
111
  // Use Bun.which to find bun reliably (process.execPath may not always be bun)
111
112
  const bunPath = Bun.which("bun") ?? process.execPath;
112
113
 
114
+ let logFd: number | undefined;
115
+ try {
116
+ logFd = openLogFile();
117
+ } catch {
118
+ // If log file can't be opened, fall back to ignore
119
+ }
120
+
113
121
  try {
114
122
  const proc = Bun.spawn([bunPath, "run", SERVER_ENTRY], {
115
123
  detached: true,
116
- stdio: ["ignore", "ignore", "ignore"],
124
+ stdio: ["ignore", logFd ?? "ignore", logFd ?? "ignore"],
117
125
  env: { ...process.env, DASHBOARD_PORT: String(port) },
118
126
  });
119
127
  proc.unref();
120
128
  } catch (err: any) {
121
129
  console.error(`Failed to start server: ${err?.message ?? err}`);
122
130
  process.exit(1);
131
+ } finally {
132
+ if (logFd !== undefined) {
133
+ try { closeSync(logFd); } catch {}
134
+ }
123
135
  }
124
136
 
125
137
  // Poll for readiness
@@ -128,12 +140,14 @@ async function cmdStart(args: string[]): Promise<void> {
128
140
  await Bun.sleep(SPAWN_POLL_INTERVAL_MS);
129
141
  if (await checkHealth(port)) {
130
142
  console.log(`Dashboard running at http://localhost:${port}`);
143
+ console.log(` Logs: ${getLogFilePath()}`);
131
144
  return;
132
145
  }
133
146
  }
134
147
 
135
148
  console.error(`Server failed to start within ${SPAWN_TIMEOUT_MS / 1000}s.`);
136
149
  console.error(`Check if port ${port} is already in use.`);
150
+ console.error(` Logs: ${getLogFilePath()}`);
137
151
  process.exit(1);
138
152
  }
139
153