@repo-prism/dispatch-hub 1.1.11

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.
Files changed (67) hide show
  1. package/README.md +18 -0
  2. package/dist/auth.d.ts +5 -0
  3. package/dist/auth.d.ts.map +1 -0
  4. package/dist/auth.js +37 -0
  5. package/dist/auth.js.map +1 -0
  6. package/dist/bin.d.ts +3 -0
  7. package/dist/bin.d.ts.map +1 -0
  8. package/dist/bin.js +14 -0
  9. package/dist/bin.js.map +1 -0
  10. package/dist/dashboard/app.css +1 -0
  11. package/dist/dashboard/app.js +237 -0
  12. package/dist/dashboard/app.js.map +18 -0
  13. package/dist/dashboard/hub.css +707 -0
  14. package/dist/dashboard/index.html +13 -0
  15. package/dist/ensure.d.ts +27 -0
  16. package/dist/ensure.d.ts.map +1 -0
  17. package/dist/ensure.js +117 -0
  18. package/dist/ensure.js.map +1 -0
  19. package/dist/hub-record.d.ts +8 -0
  20. package/dist/hub-record.d.ts.map +1 -0
  21. package/dist/hub-record.js +25 -0
  22. package/dist/hub-record.js.map +1 -0
  23. package/dist/idle.d.ts +16 -0
  24. package/dist/idle.d.ts.map +1 -0
  25. package/dist/idle.js +33 -0
  26. package/dist/idle.js.map +1 -0
  27. package/dist/index.d.ts +12 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +11 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/json-file.d.ts +3 -0
  32. package/dist/json-file.d.ts.map +1 -0
  33. package/dist/json-file.js +19 -0
  34. package/dist/json-file.js.map +1 -0
  35. package/dist/notice.d.ts +11 -0
  36. package/dist/notice.d.ts.map +1 -0
  37. package/dist/notice.js +36 -0
  38. package/dist/notice.js.map +1 -0
  39. package/dist/notify.d.ts +7 -0
  40. package/dist/notify.d.ts.map +1 -0
  41. package/dist/notify.js +59 -0
  42. package/dist/notify.js.map +1 -0
  43. package/dist/paths.d.ts +17 -0
  44. package/dist/paths.d.ts.map +1 -0
  45. package/dist/paths.js +38 -0
  46. package/dist/paths.js.map +1 -0
  47. package/dist/registry.d.ts +8 -0
  48. package/dist/registry.d.ts.map +1 -0
  49. package/dist/registry.js +42 -0
  50. package/dist/registry.js.map +1 -0
  51. package/dist/server.d.ts +22 -0
  52. package/dist/server.d.ts.map +1 -0
  53. package/dist/server.js +264 -0
  54. package/dist/server.js.map +1 -0
  55. package/dist/snapshot.d.ts +7 -0
  56. package/dist/snapshot.d.ts.map +1 -0
  57. package/dist/snapshot.js +43 -0
  58. package/dist/snapshot.js.map +1 -0
  59. package/dist/types.d.ts +48 -0
  60. package/dist/types.d.ts.map +1 -0
  61. package/dist/types.js +13 -0
  62. package/dist/types.js.map +1 -0
  63. package/dist/watch.d.ts +22 -0
  64. package/dist/watch.d.ts.map +1 -0
  65. package/dist/watch.js +164 -0
  66. package/dist/watch.js.map +1 -0
  67. package/package.json +45 -0
package/dist/server.js ADDED
@@ -0,0 +1,264 @@
1
+ import { createServer, } from "node:http";
2
+ import { access } from "node:fs/promises";
3
+ import { createReadStream } from "node:fs";
4
+ import { dirname, extname, join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { createCursorWorkerPort, createDispatchRuntime, } from "@repo-prism/dispatch";
7
+ import { originAllowed, tokenFromRequest, tokensMatch } from "./auth.js";
8
+ import { createIdleTimer, IDLE_MS } from "./idle.js";
9
+ import { newHubToken, packageVersion, writeHubRecord } from "./hub-record.js";
10
+ import { formatJobFinishedNotice } from "./notice.js";
11
+ import { createOsNotifier } from "./notify.js";
12
+ import { dashboardUrl, hubPort } from "./paths.js";
13
+ import { dropMissingWorkspaces, registerWorkspace } from "./registry.js";
14
+ import { collectJobs, isInFlight, pathExists, watchWorkspaces, } from "./watch.js";
15
+ const MIME = {
16
+ ".html": "text/html; charset=utf-8",
17
+ ".js": "text/javascript; charset=utf-8",
18
+ ".css": "text/css; charset=utf-8",
19
+ ".png": "image/png",
20
+ ".map": "application/json",
21
+ };
22
+ function defaultAssetsDir() {
23
+ return join(dirname(fileURLToPath(import.meta.url)), "dashboard");
24
+ }
25
+ async function defaultControl(workspacePath, jobId, action) {
26
+ const runtime = createDispatchRuntime({
27
+ workspaceRoot: workspacePath,
28
+ worker: createCursorWorkerPort(),
29
+ });
30
+ return runtime.handle("job_control", { jobId, action });
31
+ }
32
+ export async function startHub(options = {}) {
33
+ const env = options.env ?? process.env;
34
+ const notify = options.notify ?? createOsNotifier();
35
+ const assetsDir = options.assetsDir ?? defaultAssetsDir();
36
+ const control = options.control ?? defaultControl;
37
+ const version = options.version ?? packageVersion();
38
+ const token = newHubToken();
39
+ const wantedPort = hubPort(env);
40
+ let workspaces = await dropMissingWorkspaces(pathExists, env);
41
+ const sse = new Set();
42
+ let jobs = [];
43
+ let server;
44
+ let closed = false;
45
+ let liveRecord = {
46
+ port: wantedPort,
47
+ pid: process.pid,
48
+ version,
49
+ token,
50
+ startedAt: new Date().toISOString(),
51
+ };
52
+ const broadcast = (event) => {
53
+ if (event.type === "snapshot")
54
+ jobs = [...event.jobs];
55
+ if (event.type === "job.updated") {
56
+ jobs = [
57
+ ...jobs.filter((row) => !(row.id === event.job.id &&
58
+ row.workspacePath === event.job.workspacePath)),
59
+ event.job,
60
+ ];
61
+ }
62
+ const payload = `data: ${JSON.stringify(event)}\n\n`;
63
+ for (const client of sse) {
64
+ try {
65
+ client.res.write(payload);
66
+ }
67
+ catch {
68
+ sse.delete(client);
69
+ }
70
+ }
71
+ };
72
+ const onEvent = (event) => {
73
+ broadcast(event);
74
+ if (event.type === "job.finished") {
75
+ void notify(formatJobFinishedNotice(event.job), dashboardUrl(liveRecord.port, liveRecord.token));
76
+ }
77
+ idle.touch();
78
+ };
79
+ const watcher = watchWorkspaces(() => workspaces, onEvent, {
80
+ pollMs: options.pollMs,
81
+ });
82
+ const idle = createIdleTimer({
83
+ idleMs: options.idleMs ?? IDLE_MS,
84
+ shouldExit: () => sse.size === 0 && !jobs.some(isInFlight),
85
+ onIdle: () => {
86
+ void close();
87
+ },
88
+ });
89
+ const close = async () => {
90
+ if (closed)
91
+ return;
92
+ closed = true;
93
+ idle.stop();
94
+ watcher.close();
95
+ for (const client of sse) {
96
+ try {
97
+ client.res.end();
98
+ }
99
+ catch {
100
+ /* ignore */
101
+ }
102
+ }
103
+ sse.clear();
104
+ await new Promise((resolve) => {
105
+ if (!server) {
106
+ resolve();
107
+ return;
108
+ }
109
+ server.close(() => resolve());
110
+ });
111
+ };
112
+ server = createServer((req, res) => {
113
+ void handleRequest(req, res);
114
+ });
115
+ const listen = await new Promise((resolve) => {
116
+ server.once("error", (error) => {
117
+ resolve({ ok: false, code: error.code ?? "ERROR" });
118
+ });
119
+ server.listen(wantedPort, "127.0.0.1", () => {
120
+ const address = server.address();
121
+ const port = address && typeof address === "object" ? address.port : wantedPort;
122
+ resolve({ ok: true, port });
123
+ });
124
+ });
125
+ if (!listen.ok) {
126
+ watcher.close();
127
+ idle.stop();
128
+ if (listen.code === "EADDRINUSE") {
129
+ return { alreadyRunning: true };
130
+ }
131
+ throw new Error(`prism-hub: listen failed (${listen.code})`);
132
+ }
133
+ liveRecord = { ...liveRecord, port: listen.port };
134
+ await writeHubRecord(liveRecord, env);
135
+ jobs = await collectJobs(workspaces);
136
+ broadcast({ type: "snapshot", jobs });
137
+ async function handleRequest(req, res) {
138
+ const host = req.headers.host ?? `127.0.0.1:${liveRecord.port}`;
139
+ const url = new URL(req.url ?? "/", `http://${host}`);
140
+ if (!originAllowed(req.headers.origin)) {
141
+ json(res, 403, { error: "origin not allowed" });
142
+ return;
143
+ }
144
+ if (url.pathname === "/api/healthz") {
145
+ json(res, 200, {
146
+ ok: true,
147
+ port: liveRecord.port,
148
+ pid: process.pid,
149
+ version,
150
+ workspaces: workspaces.length,
151
+ });
152
+ return;
153
+ }
154
+ const isApi = url.pathname.startsWith("/api/");
155
+ if (isApi && !tokensMatch(liveRecord.token, tokenFromRequest(req, url))) {
156
+ json(res, 401, { error: "unauthorized" });
157
+ return;
158
+ }
159
+ if (req.method === "GET" && url.pathname === "/api/jobs") {
160
+ json(res, 200, { jobs });
161
+ return;
162
+ }
163
+ if (req.method === "GET" && url.pathname === "/api/events") {
164
+ res.writeHead(200, {
165
+ "Content-Type": "text/event-stream",
166
+ "Cache-Control": "no-cache",
167
+ Connection: "keep-alive",
168
+ });
169
+ const client = { res };
170
+ sse.add(client);
171
+ idle.touch();
172
+ res.write(`data: ${JSON.stringify({ type: "snapshot", jobs })}\n\n`);
173
+ req.on("close", () => {
174
+ sse.delete(client);
175
+ });
176
+ return;
177
+ }
178
+ if (req.method === "POST" && url.pathname === "/api/workspaces") {
179
+ const body = await readBody(req);
180
+ const path = String(body.path ?? "").trim();
181
+ if (!path) {
182
+ json(res, 400, { error: "path required" });
183
+ return;
184
+ }
185
+ workspaces = await registerWorkspace(path, env);
186
+ void watcher.refresh();
187
+ json(res, 200, { workspaces });
188
+ return;
189
+ }
190
+ const controlMatch = /^\/api\/jobs\/([^/]+)\/control$/.exec(url.pathname);
191
+ if (req.method === "POST" && controlMatch) {
192
+ const jobId = decodeURIComponent(controlMatch[1] ?? "");
193
+ const body = await readBody(req);
194
+ const action = String(body.action ?? "").trim();
195
+ const workspace = String(body.workspace ?? "").trim() ||
196
+ jobs.find((job) => job.id === jobId)?.workspacePath;
197
+ if (!workspace || !action) {
198
+ json(res, 400, { error: "workspace and action required" });
199
+ return;
200
+ }
201
+ try {
202
+ const result = await control(workspace, jobId, action);
203
+ void watcher.refresh();
204
+ json(res, 200, result ?? { ok: true });
205
+ }
206
+ catch (cause) {
207
+ const message = cause instanceof Error ? cause.message : String(cause);
208
+ json(res, 500, { error: message });
209
+ }
210
+ return;
211
+ }
212
+ if (req.method === "GET" &&
213
+ (url.pathname === "/" || url.pathname === "/index.html")) {
214
+ await serveFile(res, join(assetsDir, "index.html"), "text/html; charset=utf-8");
215
+ return;
216
+ }
217
+ if (req.method === "GET" && url.pathname.startsWith("/assets/")) {
218
+ const relative = url.pathname.slice("/assets/".length);
219
+ if (relative.includes("..")) {
220
+ json(res, 400, { error: "bad path" });
221
+ return;
222
+ }
223
+ const file = join(assetsDir, relative);
224
+ await serveFile(res, file, MIME[extname(file)] ?? "application/octet-stream");
225
+ return;
226
+ }
227
+ json(res, 404, { error: "not found" });
228
+ }
229
+ return {
230
+ record: liveRecord,
231
+ url: dashboardUrl(liveRecord.port, liveRecord.token),
232
+ close,
233
+ };
234
+ }
235
+ function json(res, status, body) {
236
+ res.writeHead(status, { "Content-Type": "application/json; charset=utf-8" });
237
+ res.end(`${JSON.stringify(body)}\n`);
238
+ }
239
+ async function readBody(req) {
240
+ const chunks = [];
241
+ for await (const chunk of req) {
242
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
243
+ }
244
+ if (chunks.length === 0)
245
+ return {};
246
+ try {
247
+ return JSON.parse(Buffer.concat(chunks).toString("utf8"));
248
+ }
249
+ catch {
250
+ return {};
251
+ }
252
+ }
253
+ async function serveFile(res, path, mime) {
254
+ try {
255
+ await access(path);
256
+ }
257
+ catch {
258
+ json(res, 404, { error: "not found" });
259
+ return;
260
+ }
261
+ res.writeHead(200, { "Content-Type": mime });
262
+ createReadStream(path).pipe(res);
263
+ }
264
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { JobRecord } from "@repo-prism/dispatch";
2
+ import type { JobSnapshot } from "./types.js";
3
+ /** Chat-safe job row. Never includes a worktree path (ADR-0039). */
4
+ export declare function toSnapshot(job: JobRecord, workspacePath: string, now?: number): JobSnapshot;
5
+ export declare function snapshotKey(job: JobSnapshot): string;
6
+ export declare function finishedKey(job: JobSnapshot): string;
7
+ //# sourceMappingURL=snapshot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../src/snapshot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAQ9C,oEAAoE;AACpE,wBAAgB,UAAU,CACxB,GAAG,EAAE,SAAS,EACd,aAAa,EAAE,MAAM,EACrB,GAAG,SAAa,GACf,WAAW,CAoBb;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CASpD;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CAEpD"}
@@ -0,0 +1,43 @@
1
+ import { workspaceLabel } from "./registry.js";
2
+ function elapsedMs(createdAt, now) {
3
+ const started = Date.parse(createdAt);
4
+ if (!Number.isFinite(started))
5
+ return 0;
6
+ return Math.max(0, now - started);
7
+ }
8
+ /** Chat-safe job row. Never includes a worktree path (ADR-0039). */
9
+ export function toSnapshot(job, workspacePath, now = Date.now()) {
10
+ return {
11
+ id: job.id,
12
+ title: job.title,
13
+ status: job.status,
14
+ workspacePath,
15
+ workspaceLabel: workspaceLabel(workspacePath),
16
+ branch: job.branch,
17
+ lastActivity: job.lastActivity ?? "",
18
+ ...(job.resultSummary ? { resultSummary: job.resultSummary } : {}),
19
+ ...(job.errorMessage ? { errorMessage: job.errorMessage } : {}),
20
+ ...(job.verification ? { verification: job.verification } : {}),
21
+ ...(job.verificationDetail
22
+ ? { verificationDetail: job.verificationDetail }
23
+ : {}),
24
+ ...(job.commitSha ? { commitSha: job.commitSha } : {}),
25
+ createdAt: job.createdAt,
26
+ updatedAt: job.updatedAt,
27
+ elapsedMs: elapsedMs(job.createdAt, now),
28
+ };
29
+ }
30
+ export function snapshotKey(job) {
31
+ return [
32
+ job.id,
33
+ job.status,
34
+ job.lastActivity,
35
+ job.verification ?? "",
36
+ job.commitSha ?? "",
37
+ job.updatedAt,
38
+ ].join(":");
39
+ }
40
+ export function finishedKey(job) {
41
+ return `${job.workspacePath}:${job.id}:${job.status}:${job.updatedAt}`;
42
+ }
43
+ //# sourceMappingURL=snapshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../src/snapshot.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG/C,SAAS,SAAS,CAAC,SAAiB,EAAE,GAAW;IAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACtC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,CAAC,CAAC;IACxC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC;AACpC,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,UAAU,CACxB,GAAc,EACd,aAAqB,EACrB,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;IAEhB,OAAO;QACL,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,aAAa;QACb,cAAc,EAAE,cAAc,CAAC,aAAa,CAAC;QAC7C,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,YAAY,EAAE,GAAG,CAAC,YAAY,IAAI,EAAE;QACpC,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,GAAG,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClE,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/D,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/D,GAAG,CAAC,GAAG,CAAC,kBAAkB;YACxB,CAAC,CAAC,EAAE,kBAAkB,EAAE,GAAG,CAAC,kBAAkB,EAAE;YAChD,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtD,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC;KACzC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAgB;IAC1C,OAAO;QACL,GAAG,CAAC,EAAE;QACN,GAAG,CAAC,MAAM;QACV,GAAG,CAAC,YAAY;QAChB,GAAG,CAAC,YAAY,IAAI,EAAE;QACtB,GAAG,CAAC,SAAS,IAAI,EAAE;QACnB,GAAG,CAAC,SAAS;KACd,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAgB;IAC1C,OAAO,GAAG,GAAG,CAAC,aAAa,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;AACzE,CAAC"}
@@ -0,0 +1,48 @@
1
+ import type { JobRecord } from "@repo-prism/dispatch";
2
+ export type JobStatus = JobRecord["status"];
3
+ export type HubRecord = {
4
+ readonly port: number;
5
+ readonly pid: number;
6
+ readonly version: string;
7
+ readonly token: string;
8
+ readonly startedAt: string;
9
+ };
10
+ export type WorkspaceEntry = {
11
+ readonly path: string;
12
+ readonly label: string;
13
+ readonly lastSeenAt: string;
14
+ };
15
+ export type WorkspaceRegistry = {
16
+ readonly workspaces: readonly WorkspaceEntry[];
17
+ };
18
+ export type JobSnapshot = {
19
+ readonly id: string;
20
+ readonly title: string;
21
+ readonly status: JobStatus;
22
+ readonly workspacePath: string;
23
+ readonly workspaceLabel: string;
24
+ readonly branch: string;
25
+ readonly lastActivity: string;
26
+ readonly resultSummary?: string;
27
+ readonly errorMessage?: string;
28
+ readonly verification?: "passed" | "failed" | "skipped";
29
+ readonly verificationDetail?: string;
30
+ readonly commitSha?: string;
31
+ readonly createdAt: string;
32
+ readonly updatedAt: string;
33
+ readonly elapsedMs: number;
34
+ };
35
+ export type HubEvent = {
36
+ readonly type: "snapshot";
37
+ readonly jobs: readonly JobSnapshot[];
38
+ } | {
39
+ readonly type: "job.updated";
40
+ readonly job: JobSnapshot;
41
+ } | {
42
+ readonly type: "job.finished";
43
+ readonly job: JobSnapshot;
44
+ readonly notice: string;
45
+ };
46
+ export declare const IN_FLIGHT_STATUSES: readonly JobStatus[];
47
+ export declare const TERMINAL_STATUSES: readonly JobStatus[];
48
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE5C,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,UAAU,EAAE,SAAS,cAAc,EAAE,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,YAAY,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IACxD,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,QAAQ,GAChB;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,WAAW,EAAE,CAAA;CAAE,GACpE;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAA;CAAE,GAC3D;IACE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,CAAC;AAEN,eAAO,MAAM,kBAAkB,EAAE,SAAS,SAAS,EAMlD,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,SAAS,SAAS,EAIjD,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,13 @@
1
+ export const IN_FLIGHT_STATUSES = [
2
+ "ready",
3
+ "booting",
4
+ "running",
5
+ "waiting_on_you",
6
+ "blocked",
7
+ ];
8
+ export const TERMINAL_STATUSES = [
9
+ "done",
10
+ "error",
11
+ "cancelled",
12
+ ];
13
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAiDA,MAAM,CAAC,MAAM,kBAAkB,GAAyB;IACtD,OAAO;IACP,SAAS;IACT,SAAS;IACT,gBAAgB;IAChB,SAAS;CACV,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAyB;IACrD,MAAM;IACN,OAAO;IACP,WAAW;CACZ,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { type HubEvent, type JobSnapshot, type WorkspaceEntry } from "./types.js";
2
+ export type WatchEmit = (event: HubEvent) => void;
3
+ export type WorkspaceWatch = {
4
+ readonly refresh: () => Promise<void>;
5
+ readonly close: () => void;
6
+ readonly jobs: () => readonly JobSnapshot[];
7
+ };
8
+ declare function pathExists(path: string): Promise<boolean>;
9
+ export declare function isInFlight(job: JobSnapshot): boolean;
10
+ export declare function isTerminal(job: JobSnapshot): boolean;
11
+ export declare function collectJobs(workspaces: readonly WorkspaceEntry[], now?: number): Promise<JobSnapshot[]>;
12
+ /**
13
+ * Diff two snapshots. `job.finished` fires once per terminal transition
14
+ * (deduped by workspace+id+status+updatedAt).
15
+ */
16
+ export declare function diffJobs(previous: readonly JobSnapshot[], next: readonly JobSnapshot[], seenFinished: Set<string>): HubEvent[];
17
+ export declare function watchWorkspaces(getWorkspaces: () => readonly WorkspaceEntry[], emit: WatchEmit, options?: {
18
+ readonly debounceMs?: number | undefined;
19
+ readonly pollMs?: number | undefined;
20
+ }): WorkspaceWatch;
21
+ export { pathExists };
22
+ //# sourceMappingURL=watch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../src/watch.ts"],"names":[],"mappings":"AAMA,OAAO,EAGL,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,cAAc,EACpB,MAAM,YAAY,CAAC;AAKpB,MAAM,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;AAElD,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,SAAS,WAAW,EAAE,CAAC;CAC7C,CAAC;AAEF,iBAAe,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOxD;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAEpD;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAEpD;AAED,wBAAsB,WAAW,CAC/B,UAAU,EAAE,SAAS,cAAc,EAAE,EACrC,GAAG,SAAa,GACf,OAAO,CAAC,WAAW,EAAE,CAAC,CAaxB;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,SAAS,WAAW,EAAE,EAChC,IAAI,EAAE,SAAS,WAAW,EAAE,EAC5B,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,GACxB,QAAQ,EAAE,CA2BZ;AAED,wBAAgB,eAAe,CAC7B,aAAa,EAAE,MAAM,SAAS,cAAc,EAAE,EAC9C,IAAI,EAAE,SAAS,EACf,OAAO,GAAE;IACP,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC,GACL,cAAc,CAsFhB;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
package/dist/watch.js ADDED
@@ -0,0 +1,164 @@
1
+ import { access } from "node:fs/promises";
2
+ import { watch } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { reapJobs } from "@repo-prism/dispatch";
5
+ import { formatJobFinishedNotice } from "./notice.js";
6
+ import { finishedKey, snapshotKey, toSnapshot } from "./snapshot.js";
7
+ import { IN_FLIGHT_STATUSES, TERMINAL_STATUSES, } from "./types.js";
8
+ const DEBOUNCE_MS = 250;
9
+ const POLL_MS = 2_000;
10
+ async function pathExists(path) {
11
+ try {
12
+ await access(path);
13
+ return true;
14
+ }
15
+ catch {
16
+ return false;
17
+ }
18
+ }
19
+ export function isInFlight(job) {
20
+ return IN_FLIGHT_STATUSES.includes(job.status);
21
+ }
22
+ export function isTerminal(job) {
23
+ return TERMINAL_STATUSES.includes(job.status);
24
+ }
25
+ export async function collectJobs(workspaces, now = Date.now()) {
26
+ const jobs = [];
27
+ for (const entry of workspaces) {
28
+ try {
29
+ const records = await reapJobs(entry.path);
30
+ for (const record of records) {
31
+ jobs.push(toSnapshot(record, entry.path, now));
32
+ }
33
+ }
34
+ catch {
35
+ /* a missing dispatch dir is not a hub failure */
36
+ }
37
+ }
38
+ return jobs;
39
+ }
40
+ /**
41
+ * Diff two snapshots. `job.finished` fires once per terminal transition
42
+ * (deduped by workspace+id+status+updatedAt).
43
+ */
44
+ export function diffJobs(previous, next, seenFinished) {
45
+ const prevById = new Map(previous.map((job) => [`${job.workspacePath}:${job.id}`, job]));
46
+ const events = [];
47
+ for (const job of next) {
48
+ const key = `${job.workspacePath}:${job.id}`;
49
+ const before = prevById.get(key);
50
+ if (!before) {
51
+ events.push({ type: "job.updated", job });
52
+ }
53
+ else if (snapshotKey(before) !== snapshotKey(job)) {
54
+ events.push({ type: "job.updated", job });
55
+ }
56
+ if (isTerminal(job) && (!before || !isTerminal(before))) {
57
+ const finish = finishedKey(job);
58
+ if (!seenFinished.has(finish)) {
59
+ seenFinished.add(finish);
60
+ const copy = formatJobFinishedNotice(job);
61
+ events.push({
62
+ type: "job.finished",
63
+ job,
64
+ notice: `${copy.title}. ${copy.body}`,
65
+ });
66
+ }
67
+ }
68
+ }
69
+ return events;
70
+ }
71
+ export function watchWorkspaces(getWorkspaces, emit, options = {}) {
72
+ const debounceMs = options.debounceMs ?? DEBOUNCE_MS;
73
+ const pollMs = options.pollMs ?? POLL_MS;
74
+ let snapshots = [];
75
+ const seenFinished = new Set();
76
+ const watchers = new Map();
77
+ let timer;
78
+ let poll;
79
+ let closed = false;
80
+ const refresh = async () => {
81
+ if (closed)
82
+ return;
83
+ const next = await collectJobs(getWorkspaces());
84
+ const events = diffJobs(snapshots, next, seenFinished);
85
+ snapshots = next;
86
+ if (events.length > 0) {
87
+ emit({ type: "snapshot", jobs: next });
88
+ for (const event of events)
89
+ emit(event);
90
+ }
91
+ };
92
+ const schedule = () => {
93
+ if (timer)
94
+ clearTimeout(timer);
95
+ timer = setTimeout(() => {
96
+ void refresh();
97
+ }, debounceMs);
98
+ timer.unref?.();
99
+ };
100
+ const syncWatchers = () => {
101
+ const live = new Set(getWorkspaces().map((entry) => entry.path));
102
+ for (const [path, group] of watchers) {
103
+ if (live.has(path))
104
+ continue;
105
+ for (const watcher of group)
106
+ watcher.close();
107
+ watchers.delete(path);
108
+ }
109
+ for (const path of live) {
110
+ if (watchers.has(path))
111
+ continue;
112
+ const group = [];
113
+ const dispatch = join(path, ".prism", "dispatch");
114
+ const runs = join(dispatch, "runs");
115
+ for (const target of [dispatch, runs]) {
116
+ try {
117
+ const watcher = watch(target, { persistent: false }, () => schedule());
118
+ watcher.on("error", () => {
119
+ /* directory may not exist yet */
120
+ });
121
+ group.push(watcher);
122
+ }
123
+ catch {
124
+ /* watch is best-effort; poll covers it */
125
+ }
126
+ }
127
+ watchers.set(path, group);
128
+ }
129
+ };
130
+ void (async () => {
131
+ snapshots = await collectJobs(getWorkspaces());
132
+ for (const job of snapshots) {
133
+ if (isTerminal(job))
134
+ seenFinished.add(finishedKey(job));
135
+ }
136
+ emit({ type: "snapshot", jobs: snapshots });
137
+ syncWatchers();
138
+ if (closed)
139
+ return;
140
+ poll = setInterval(() => {
141
+ syncWatchers();
142
+ void refresh();
143
+ }, pollMs);
144
+ poll.unref?.();
145
+ })();
146
+ return {
147
+ refresh,
148
+ jobs: () => snapshots,
149
+ close: () => {
150
+ closed = true;
151
+ if (timer)
152
+ clearTimeout(timer);
153
+ if (poll)
154
+ clearInterval(poll);
155
+ for (const group of watchers.values()) {
156
+ for (const watcher of group)
157
+ watcher.close();
158
+ }
159
+ watchers.clear();
160
+ },
161
+ };
162
+ }
163
+ export { pathExists };
164
+ //# sourceMappingURL=watch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watch.js","sourceRoot":"","sources":["../src/watch.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@repo-prism/dispatch-hub",
3
+ "version": "1.1.11",
4
+ "type": "module",
5
+ "description": "Prism Hub — loopback dashboard and OS notifications for Dispatch jobs",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.js"
10
+ }
11
+ },
12
+ "bin": {
13
+ "prism-hub": "./dist/bin.js"
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc -p tsconfig.json && bun run scripts/build-dashboard.ts",
20
+ "start": "node dist/bin.js",
21
+ "typecheck": "tsc -p tsconfig.json --noEmit",
22
+ "test": "vitest run"
23
+ },
24
+ "dependencies": {
25
+ "@repo-prism/dispatch": "1.1.11"
26
+ },
27
+ "devDependencies": {
28
+ "@types/react": "^19.2.17",
29
+ "@types/react-dom": "^19.2.3",
30
+ "react": "^19.2.7",
31
+ "react-dom": "^19.2.7"
32
+ },
33
+ "license": "MIT",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/Shailesh200/prism.git"
37
+ },
38
+ "homepage": "https://www.prismhq.in",
39
+ "bugs": {
40
+ "url": "https://github.com/Shailesh200/prism/issues"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public"
44
+ }
45
+ }