agent-rooms 0.1.5 → 0.1.7
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 +75 -55
- package/dist/cli.js +11 -11
- package/dist/cli.js.map +1 -1
- package/dist/commands/dashboard.js +38 -0
- package/dist/commands/dashboard.js.map +1 -0
- package/dist/commands/init.js +32 -23
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/watch.js +28 -123
- package/dist/commands/watch.js.map +1 -1
- package/dist/dashboard/readiness.js +319 -0
- package/dist/dashboard/readiness.js.map +1 -0
- package/dist/dashboard/server.js +322 -0
- package/dist/dashboard/server.js.map +1 -0
- package/dist/dashboard/ui.js +270 -0
- package/dist/dashboard/ui.js.map +1 -0
- package/dist/hosts.js +24 -6
- package/dist/hosts.js.map +1 -1
- package/dist/listener.js +280 -0
- package/dist/listener.js.map +1 -0
- package/dist/skill.js +40 -0
- package/dist/skill.js.map +1 -0
- package/dist/socket.js +20 -8
- package/dist/socket.js.map +1 -1
- package/package.json +6 -4
- package/skill/agent-rooms/AGENTS.md +40 -0
- package/skill/agent-rooms/SKILL.md +90 -0
- package/skill/agent-rooms/references/etiquette.md +49 -0
- package/skill/agent-rooms/references/tools.md +102 -0
- package/skill/agent-rooms/references/troubleshooting.md +50 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
|
+
import { createServer } from "node:http";
|
|
4
|
+
import { platform } from "node:os";
|
|
5
|
+
import { resolve } from "node:path";
|
|
6
|
+
import { pairPoll, pairStart } from "../api.js";
|
|
7
|
+
import { loadConfig, saveConfig, upsertBinding } from "../config.js";
|
|
8
|
+
import { getAdapter } from "../hosts.js";
|
|
9
|
+
import { ListenerRuntime } from "../listener.js";
|
|
10
|
+
import { installBundledSkill } from "../skill.js";
|
|
11
|
+
import { buildReadiness } from "./readiness.js";
|
|
12
|
+
import { dashboardHtml } from "./ui.js";
|
|
13
|
+
export class DashboardController {
|
|
14
|
+
apiBaseOverride;
|
|
15
|
+
runtime;
|
|
16
|
+
activity = [];
|
|
17
|
+
pendingPair = null;
|
|
18
|
+
subscribers = new Set();
|
|
19
|
+
constructor(apiBaseOverride) {
|
|
20
|
+
this.apiBaseOverride = apiBaseOverride;
|
|
21
|
+
this.runtime = new ListenerRuntime({
|
|
22
|
+
apiBase: apiBaseOverride,
|
|
23
|
+
onEvent: (event) => this.addActivity(event)
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
snapshot() {
|
|
27
|
+
return {
|
|
28
|
+
readiness: buildReadiness(this.runtime.snapshot(), this.apiBaseOverride),
|
|
29
|
+
activity: [...this.activity],
|
|
30
|
+
pendingPair: this.pendingPair
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
subscribe(fn) {
|
|
34
|
+
this.subscribers.add(fn);
|
|
35
|
+
return () => this.subscribers.delete(fn);
|
|
36
|
+
}
|
|
37
|
+
async close() {
|
|
38
|
+
await this.runtime.stop();
|
|
39
|
+
}
|
|
40
|
+
async action(input) {
|
|
41
|
+
let message = "Done.";
|
|
42
|
+
let command;
|
|
43
|
+
switch (input.action) {
|
|
44
|
+
case "pair.start": {
|
|
45
|
+
const apiBase = this.apiBase();
|
|
46
|
+
const start = await pairStart(apiBase, platform());
|
|
47
|
+
this.pendingPair = start;
|
|
48
|
+
openExternal(start.verification_url);
|
|
49
|
+
message = `Pairing started. Code: ${start.user_code}`;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
case "pair.poll": {
|
|
53
|
+
message = await this.pollPair();
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
case "host.installMcp": {
|
|
57
|
+
const host = this.requireHost(input.host);
|
|
58
|
+
const spec = host.buildMcpAdd(this.apiBase());
|
|
59
|
+
command = `${spec.command} ${spec.args.join(" ")}`;
|
|
60
|
+
const result = runCommand(spec.command, spec.args);
|
|
61
|
+
message = result.ok || /already (exists|configured|registered)/i.test(result.output)
|
|
62
|
+
? `MCP install command finished for ${host.name}.`
|
|
63
|
+
: `MCP install exited ${result.status ?? "?"}: ${result.output || "no output"}`;
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
case "host.installSkill": {
|
|
67
|
+
const host = this.requireHost(input.host);
|
|
68
|
+
const result = installBundledSkill(host);
|
|
69
|
+
message = result.destination ? `${result.message} ${result.destination}` : result.message;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
case "listener.start": {
|
|
73
|
+
await this.runtime.start();
|
|
74
|
+
message = "Listener started.";
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
case "listener.stop": {
|
|
78
|
+
await this.runtime.stop();
|
|
79
|
+
message = "Listener stopped.";
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
case "config.binding.save": {
|
|
83
|
+
message = this.saveBinding(input.payload ?? {});
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
case "open.url": {
|
|
87
|
+
const url = String(input.payload?.url ?? "");
|
|
88
|
+
if (!/^https?:\/\//.test(url))
|
|
89
|
+
throw new Error("Invalid URL.");
|
|
90
|
+
openExternal(url);
|
|
91
|
+
message = `Opened ${url}`;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case "manual.command": {
|
|
95
|
+
command = String(input.payload?.command ?? "");
|
|
96
|
+
message = "Manual command shown.";
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
default:
|
|
100
|
+
throw new Error(`Unknown action: ${input.action}`);
|
|
101
|
+
}
|
|
102
|
+
this.addActivity({ at: Date.now(), level: "info", message, type: input.action });
|
|
103
|
+
const snapshot = this.snapshot();
|
|
104
|
+
this.notify();
|
|
105
|
+
return { ok: true, message, snapshot, command };
|
|
106
|
+
}
|
|
107
|
+
apiBase() {
|
|
108
|
+
return this.apiBaseOverride || loadConfig().apiBase;
|
|
109
|
+
}
|
|
110
|
+
async pollPair() {
|
|
111
|
+
if (!this.pendingPair) {
|
|
112
|
+
return "No active pairing request.";
|
|
113
|
+
}
|
|
114
|
+
const poll = await pairPoll(this.apiBase(), this.pendingPair.device_code);
|
|
115
|
+
if ("device_id" in poll) {
|
|
116
|
+
const config = loadConfig();
|
|
117
|
+
saveConfig({ ...config, apiBase: this.apiBase(), device: { id: poll.device_id, token: poll.refresh_credential } });
|
|
118
|
+
this.pendingPair = null;
|
|
119
|
+
return "Device paired.";
|
|
120
|
+
}
|
|
121
|
+
if (poll.status === "expired") {
|
|
122
|
+
this.pendingPair = null;
|
|
123
|
+
return "Pairing code expired. Start pairing again.";
|
|
124
|
+
}
|
|
125
|
+
return `Still waiting for approval. Code: ${this.pendingPair.user_code}`;
|
|
126
|
+
}
|
|
127
|
+
saveBinding(payload) {
|
|
128
|
+
const agent = requiredString(payload.agent, "agent");
|
|
129
|
+
const workspace = resolve(requiredString(payload.workspace, "workspace"));
|
|
130
|
+
const host = normalizeHost(requiredString(payload.host, "host"));
|
|
131
|
+
if (!getAdapter(host)) {
|
|
132
|
+
throw new Error(`Unsupported host: ${host}`);
|
|
133
|
+
}
|
|
134
|
+
const rooms = parseRooms(requiredString(payload.rooms, "rooms"));
|
|
135
|
+
if (rooms.length === 0) {
|
|
136
|
+
throw new Error("At least one room id is required.");
|
|
137
|
+
}
|
|
138
|
+
const config = loadConfig();
|
|
139
|
+
const next = upsertBinding({ ...config, apiBase: this.apiBase() }, { agent, workspace, host, rooms });
|
|
140
|
+
saveConfig(next);
|
|
141
|
+
return `Bound ${agent} to ${workspace}.`;
|
|
142
|
+
}
|
|
143
|
+
requireHost(host) {
|
|
144
|
+
const adapter = getAdapter(host ?? "unknown");
|
|
145
|
+
if (!adapter) {
|
|
146
|
+
throw new Error(`Unsupported host: ${String(host)}`);
|
|
147
|
+
}
|
|
148
|
+
return adapter;
|
|
149
|
+
}
|
|
150
|
+
addActivity(event) {
|
|
151
|
+
this.activity.push(event);
|
|
152
|
+
if (this.activity.length > 200)
|
|
153
|
+
this.activity = this.activity.slice(-200);
|
|
154
|
+
this.notify();
|
|
155
|
+
}
|
|
156
|
+
notify() {
|
|
157
|
+
for (const fn of this.subscribers)
|
|
158
|
+
fn();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
export async function serveDashboard(options = {}) {
|
|
162
|
+
const token = randomBytes(24).toString("base64url");
|
|
163
|
+
const controller = new DashboardController(options.apiBase);
|
|
164
|
+
const clients = new Set();
|
|
165
|
+
const host = options.host || "127.0.0.1";
|
|
166
|
+
const port = options.port ?? 0;
|
|
167
|
+
const server = createServer((req, res) => {
|
|
168
|
+
void routeRequest({ req, res, token, controller, clients });
|
|
169
|
+
});
|
|
170
|
+
server.headersTimeout = 60_000;
|
|
171
|
+
server.requestTimeout = 120_000;
|
|
172
|
+
controller.subscribe(() => {
|
|
173
|
+
const data = `data: ${JSON.stringify(controller.snapshot())}\n\n`;
|
|
174
|
+
for (const client of clients)
|
|
175
|
+
client.write(data);
|
|
176
|
+
});
|
|
177
|
+
await new Promise((resolveListen, reject) => {
|
|
178
|
+
server.once("error", reject);
|
|
179
|
+
server.listen(port, host, () => {
|
|
180
|
+
server.off("error", reject);
|
|
181
|
+
resolveListen();
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
const address = server.address();
|
|
185
|
+
const actualPort = typeof address === "object" && address ? address.port : port;
|
|
186
|
+
const url = `http://${host}:${actualPort}/?token=${encodeURIComponent(token)}`;
|
|
187
|
+
if (options.open !== false) {
|
|
188
|
+
openExternal(url);
|
|
189
|
+
}
|
|
190
|
+
return { server, controller, url, token };
|
|
191
|
+
}
|
|
192
|
+
async function routeRequest(input) {
|
|
193
|
+
const { req, res, token, controller, clients } = input;
|
|
194
|
+
const url = new URL(req.url || "/", "http://127.0.0.1");
|
|
195
|
+
if (!validHost(req)) {
|
|
196
|
+
sendText(res, 403, "Blocked a request from outside this machine.");
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
if (url.pathname === "/" && req.method === "GET") {
|
|
200
|
+
if (!authorized(req, url, token))
|
|
201
|
+
return sendText(res, 403, "Missing or invalid dashboard token.");
|
|
202
|
+
sendHtml(res, dashboardHtml());
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (url.pathname === "/api/readiness" && req.method === "GET") {
|
|
206
|
+
if (!authorized(req, url, token))
|
|
207
|
+
return sendJson(res, 403, { error: "Missing or invalid dashboard token." });
|
|
208
|
+
sendJson(res, 200, controller.snapshot());
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (url.pathname === "/api/events" && req.method === "GET") {
|
|
212
|
+
if (!authorized(req, url, token))
|
|
213
|
+
return sendText(res, 403, "Missing or invalid dashboard token.");
|
|
214
|
+
res.writeHead(200, {
|
|
215
|
+
"content-type": "text/event-stream",
|
|
216
|
+
"cache-control": "no-cache, no-transform",
|
|
217
|
+
connection: "keep-alive",
|
|
218
|
+
"x-accel-buffering": "no"
|
|
219
|
+
});
|
|
220
|
+
res.write(`data: ${JSON.stringify(controller.snapshot())}\n\n`);
|
|
221
|
+
clients.add(res);
|
|
222
|
+
req.on("close", () => clients.delete(res));
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (url.pathname === "/api/actions" && req.method === "POST") {
|
|
226
|
+
if (!authorized(req, url, token))
|
|
227
|
+
return sendJson(res, 403, { error: "Missing or invalid dashboard token." });
|
|
228
|
+
try {
|
|
229
|
+
const body = await readBody(req);
|
|
230
|
+
const result = await controller.action(JSON.parse(body || "{}"));
|
|
231
|
+
sendJson(res, 200, result);
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
sendJson(res, 400, { error: error.message, snapshot: controller.snapshot() });
|
|
235
|
+
}
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
sendJson(res, 404, { error: "Not found." });
|
|
239
|
+
}
|
|
240
|
+
function authorized(req, url, token) {
|
|
241
|
+
return req.headers["x-agent-rooms-token"] === token || url.searchParams.get("token") === token;
|
|
242
|
+
}
|
|
243
|
+
function validHost(req) {
|
|
244
|
+
const raw = req.headers.host;
|
|
245
|
+
if (!raw)
|
|
246
|
+
return false;
|
|
247
|
+
const host = raw.toLowerCase();
|
|
248
|
+
return host.startsWith("127.0.0.1:") || host === "127.0.0.1" || host.startsWith("localhost:") || host === "localhost";
|
|
249
|
+
}
|
|
250
|
+
function sendHtml(res, html) {
|
|
251
|
+
res.writeHead(200, {
|
|
252
|
+
"content-type": "text/html; charset=utf-8",
|
|
253
|
+
"cache-control": "no-store"
|
|
254
|
+
});
|
|
255
|
+
res.end(html);
|
|
256
|
+
}
|
|
257
|
+
function sendText(res, status, text) {
|
|
258
|
+
res.writeHead(status, {
|
|
259
|
+
"content-type": "text/plain; charset=utf-8",
|
|
260
|
+
"cache-control": "no-store"
|
|
261
|
+
});
|
|
262
|
+
res.end(text);
|
|
263
|
+
}
|
|
264
|
+
function sendJson(res, status, body) {
|
|
265
|
+
res.writeHead(status, {
|
|
266
|
+
"content-type": "application/json; charset=utf-8",
|
|
267
|
+
"cache-control": "no-store"
|
|
268
|
+
});
|
|
269
|
+
res.end(JSON.stringify(body));
|
|
270
|
+
}
|
|
271
|
+
function readBody(req) {
|
|
272
|
+
return new Promise((resolveBody, reject) => {
|
|
273
|
+
let body = "";
|
|
274
|
+
req.setEncoding("utf8");
|
|
275
|
+
req.on("data", (chunk) => {
|
|
276
|
+
body += chunk;
|
|
277
|
+
if (body.length > 1_000_000) {
|
|
278
|
+
reject(new Error("Request body too large."));
|
|
279
|
+
req.destroy();
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
req.on("end", () => resolveBody(body));
|
|
283
|
+
req.on("error", reject);
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
function runCommand(command, args) {
|
|
287
|
+
const result = spawnSync(command, args, {
|
|
288
|
+
encoding: "utf8",
|
|
289
|
+
timeout: 30_000,
|
|
290
|
+
shell: process.platform === "win32",
|
|
291
|
+
stdio: "pipe"
|
|
292
|
+
});
|
|
293
|
+
return {
|
|
294
|
+
ok: result.status === 0,
|
|
295
|
+
status: result.status,
|
|
296
|
+
output: `${result.stdout ?? ""}${result.stderr ?? ""}`.trim().slice(0, 4000)
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
function openExternal(target) {
|
|
300
|
+
const child = process.platform === "win32"
|
|
301
|
+
? spawn("cmd", ["/c", "start", "", target], { detached: true, stdio: "ignore", windowsHide: true })
|
|
302
|
+
: process.platform === "darwin"
|
|
303
|
+
? spawn("open", [target], { detached: true, stdio: "ignore" })
|
|
304
|
+
: spawn("xdg-open", [target], { detached: true, stdio: "ignore" });
|
|
305
|
+
child.unref();
|
|
306
|
+
}
|
|
307
|
+
function requiredString(value, field) {
|
|
308
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
309
|
+
throw new Error(`${field} is required.`);
|
|
310
|
+
}
|
|
311
|
+
return value.trim();
|
|
312
|
+
}
|
|
313
|
+
function normalizeHost(value) {
|
|
314
|
+
const host = value.trim().toLowerCase();
|
|
315
|
+
if (host === "claude")
|
|
316
|
+
return "claude_code";
|
|
317
|
+
return host;
|
|
318
|
+
}
|
|
319
|
+
function parseRooms(value) {
|
|
320
|
+
return value.split(/[,\s]+/).map((item) => item.trim()).filter(Boolean);
|
|
321
|
+
}
|
|
322
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/dashboard/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
export function dashboardHtml() {
|
|
2
|
+
return String.raw `<!doctype html>
|
|
3
|
+
<html lang="en">
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="utf-8" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
+
<title>Agent Rooms Control</title>
|
|
8
|
+
<style>
|
|
9
|
+
:root {
|
|
10
|
+
color-scheme: light;
|
|
11
|
+
--bg: #f6f5f2;
|
|
12
|
+
--panel: #ffffff;
|
|
13
|
+
--ink: #181715;
|
|
14
|
+
--muted: #69645c;
|
|
15
|
+
--line: #ded8ce;
|
|
16
|
+
--green: #207a4c;
|
|
17
|
+
--amber: #9a6417;
|
|
18
|
+
--red: #a33a32;
|
|
19
|
+
--blue: #2364a8;
|
|
20
|
+
--gold: #a97913;
|
|
21
|
+
--shadow: 0 14px 35px rgba(24, 23, 21, 0.08);
|
|
22
|
+
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
23
|
+
}
|
|
24
|
+
* { box-sizing: border-box; }
|
|
25
|
+
body { margin: 0; background: var(--bg); color: var(--ink); }
|
|
26
|
+
button, input, select { font: inherit; }
|
|
27
|
+
.shell { min-height: 100vh; display: grid; grid-template-columns: 250px 1fr; }
|
|
28
|
+
aside { border-right: 1px solid var(--line); padding: 24px 18px; background: #fbfaf8; }
|
|
29
|
+
main { padding: 26px; max-width: 1180px; width: 100%; }
|
|
30
|
+
.brand { font-size: 15px; font-weight: 800; letter-spacing: 0; margin-bottom: 4px; }
|
|
31
|
+
.sub { color: var(--muted); font-size: 13px; line-height: 1.4; margin-bottom: 22px; }
|
|
32
|
+
.nav { display: grid; gap: 6px; }
|
|
33
|
+
.nav button { border: 0; background: transparent; text-align: left; padding: 10px 11px; border-radius: 8px; color: var(--muted); cursor: pointer; }
|
|
34
|
+
.nav button.active { background: #ece7dd; color: var(--ink); font-weight: 700; }
|
|
35
|
+
.top { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; margin-bottom: 18px; }
|
|
36
|
+
h1 { font-size: 25px; margin: 0 0 6px; letter-spacing: 0; }
|
|
37
|
+
.meta { color: var(--muted); font-size: 13px; }
|
|
38
|
+
.banner { background: var(--panel); border: 1px solid var(--line); border-left: 5px solid var(--blue); box-shadow: var(--shadow); border-radius: 8px; padding: 15px 16px; display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 18px; }
|
|
39
|
+
.banner.ready { border-left-color: var(--green); }
|
|
40
|
+
.banner strong { display: block; margin-bottom: 3px; }
|
|
41
|
+
.grid { display: grid; gap: 16px; }
|
|
42
|
+
.two { grid-template-columns: minmax(0, 1.2fr) minmax(320px, .8fr); align-items: start; }
|
|
43
|
+
.panel { background: var(--panel); border: 1px solid var(--line); border-radius: 8px; box-shadow: var(--shadow); }
|
|
44
|
+
.panel h2 { font-size: 16px; padding: 15px 16px 0; margin: 0; }
|
|
45
|
+
.step { display: grid; grid-template-columns: 26px minmax(0, 1fr) auto; gap: 12px; padding: 13px 16px; border-top: 1px solid var(--line); align-items: center; }
|
|
46
|
+
.step:first-of-type { border-top: 0; }
|
|
47
|
+
.step.done { color: var(--muted); padding-top: 9px; padding-bottom: 9px; }
|
|
48
|
+
.step.frontier { background: #f2f7fb; outline: 2px solid rgba(35, 100, 168, .18); outline-offset: -2px; }
|
|
49
|
+
.dot { width: 14px; height: 14px; border-radius: 50%; margin-top: 3px; background: var(--muted); }
|
|
50
|
+
.ready .dot { background: var(--green); }
|
|
51
|
+
.action_needed .dot { background: var(--amber); }
|
|
52
|
+
.blocked .dot { background: var(--red); }
|
|
53
|
+
.working .dot { background: var(--blue); animation: pulse 1.2s infinite; }
|
|
54
|
+
.attention .dot { background: var(--gold); }
|
|
55
|
+
.absent .dot { background: #a7a29a; }
|
|
56
|
+
@keyframes pulse { 0%,100% { opacity: .45; } 50% { opacity: 1; } }
|
|
57
|
+
.label { font-weight: 750; margin-bottom: 3px; }
|
|
58
|
+
.hint { color: var(--muted); font-size: 13px; line-height: 1.4; }
|
|
59
|
+
.btn { border: 1px solid #25211b; background: #25211b; color: white; border-radius: 7px; min-height: 36px; padding: 0 12px; cursor: pointer; white-space: nowrap; }
|
|
60
|
+
.btn.secondary { background: #fff; color: var(--ink); border-color: var(--line); }
|
|
61
|
+
.btn.gold { background: var(--gold); border-color: var(--gold); }
|
|
62
|
+
.btn:disabled { opacity: .45; cursor: not-allowed; }
|
|
63
|
+
.kv { display: grid; gap: 9px; padding: 15px 16px; }
|
|
64
|
+
.row { display: flex; align-items: center; justify-content: space-between; gap: 12px; border-top: 1px solid var(--line); padding: 11px 16px; }
|
|
65
|
+
.row:first-child { border-top: 0; }
|
|
66
|
+
.pill { border: 1px solid var(--line); border-radius: 999px; padding: 3px 8px; font-size: 12px; color: var(--muted); white-space: nowrap; }
|
|
67
|
+
.pill.ready { color: var(--green); border-color: rgba(32,122,76,.25); background: rgba(32,122,76,.08); }
|
|
68
|
+
.pill.warn { color: var(--amber); border-color: rgba(154,100,23,.25); background: rgba(154,100,23,.08); }
|
|
69
|
+
.pill.bad { color: var(--red); border-color: rgba(163,58,50,.25); background: rgba(163,58,50,.08); }
|
|
70
|
+
.activity { max-height: 460px; overflow: auto; padding: 8px 0; }
|
|
71
|
+
.event { padding: 10px 16px; border-top: 1px solid var(--line); font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 12px; color: #37332d; }
|
|
72
|
+
.event:first-child { border-top: 0; }
|
|
73
|
+
.form { display: grid; gap: 12px; padding: 16px; }
|
|
74
|
+
label { display: grid; gap: 5px; font-size: 13px; color: var(--muted); }
|
|
75
|
+
input, select { width: 100%; border: 1px solid var(--line); border-radius: 7px; padding: 10px 11px; background: #fff; color: var(--ink); }
|
|
76
|
+
pre { margin: 0; padding: 14px 16px; overflow: auto; border-top: 1px solid var(--line); font-size: 12px; background: #fbfaf8; }
|
|
77
|
+
.hidden { display: none !important; }
|
|
78
|
+
.toast { position: fixed; right: 18px; bottom: 18px; width: min(560px, calc(100vw - 36px)); background: #25211b; color: white; border-radius: 8px; padding: 12px 14px; box-shadow: var(--shadow); font-size: 13px; white-space: pre-wrap; }
|
|
79
|
+
@media (max-width: 850px) {
|
|
80
|
+
.shell { grid-template-columns: 1fr; }
|
|
81
|
+
aside { border-right: 0; border-bottom: 1px solid var(--line); }
|
|
82
|
+
main { padding: 18px; }
|
|
83
|
+
.two { grid-template-columns: 1fr; }
|
|
84
|
+
.top, .banner, .row { flex-direction: column; align-items: stretch; }
|
|
85
|
+
.step { grid-template-columns: 22px minmax(0, 1fr); }
|
|
86
|
+
.step .btn { grid-column: 2; justify-self: start; }
|
|
87
|
+
}
|
|
88
|
+
</style>
|
|
89
|
+
</head>
|
|
90
|
+
<body>
|
|
91
|
+
<div id="app"></div>
|
|
92
|
+
<script>
|
|
93
|
+
const token = new URLSearchParams(location.search).get("token") || "";
|
|
94
|
+
let snapshot = null;
|
|
95
|
+
let active = "home";
|
|
96
|
+
let toastTimer = null;
|
|
97
|
+
const app = document.getElementById("app");
|
|
98
|
+
|
|
99
|
+
function api(path, options = {}) {
|
|
100
|
+
return fetch(path, {
|
|
101
|
+
...options,
|
|
102
|
+
headers: { "content-type": "application/json", "x-agent-rooms-token": token, ...(options.headers || {}) }
|
|
103
|
+
}).then(async (res) => {
|
|
104
|
+
const text = await res.text();
|
|
105
|
+
const body = text ? JSON.parse(text) : {};
|
|
106
|
+
if (!res.ok) throw new Error(body.error || res.statusText);
|
|
107
|
+
return body;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function load() {
|
|
112
|
+
snapshot = await api("/api/readiness");
|
|
113
|
+
render();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function act(action, payload = {}) {
|
|
117
|
+
if (action.id === "config.binding.focus") {
|
|
118
|
+
active = "agents";
|
|
119
|
+
render();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (action.manual) {
|
|
123
|
+
showToast(action.command || "Manual step required.");
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
const result = await api("/api/actions", { method: "POST", body: JSON.stringify({ action: action.id, host: action.host, payload: { ...(action.payload || {}), ...payload } }) });
|
|
128
|
+
snapshot = result.snapshot;
|
|
129
|
+
if (result.command) showToast(result.command);
|
|
130
|
+
else if (result.message) showToast(result.message);
|
|
131
|
+
render();
|
|
132
|
+
} catch (err) {
|
|
133
|
+
showToast("Action failed: " + err.message);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function statusText(status) {
|
|
138
|
+
return {
|
|
139
|
+
ready: "READY",
|
|
140
|
+
action_needed: "ACTION NEEDED",
|
|
141
|
+
blocked: "BLOCKED",
|
|
142
|
+
working: "WORKING",
|
|
143
|
+
attention: "ATTENTION",
|
|
144
|
+
absent: "NOT AVAILABLE"
|
|
145
|
+
}[status] || status;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function render() {
|
|
149
|
+
if (!token) {
|
|
150
|
+
app.innerHTML = "<main><h1>Blocked</h1><p>Missing dashboard token.</p></main>";
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const r = snapshot && snapshot.readiness;
|
|
154
|
+
if (!r) {
|
|
155
|
+
app.innerHTML = "<main><h1>Agent Rooms Control</h1><p>Loading local state...</p></main>";
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
app.innerHTML = '<div class="shell"><aside>' + nav() + '</aside><main>' + page(r) + '</main></div>' + '<div id="toast" class="toast hidden"></div>';
|
|
159
|
+
bind();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function nav() {
|
|
163
|
+
return '<div class="brand">Agent Rooms Control</div><div class="sub">Local dashboard on 127.0.0.1. Buttons are driven by detected machine state.</div><div class="nav">' +
|
|
164
|
+
["home:Readiness", "agents:Agents", "activity:Activity", "advanced:Advanced"].map((item) => {
|
|
165
|
+
const [id, label] = item.split(":");
|
|
166
|
+
return '<button data-tab="' + id + '" class="' + (active === id ? "active" : "") + '">' + label + '</button>';
|
|
167
|
+
}).join("") + '</div>';
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function page(r) {
|
|
171
|
+
if (active === "agents") return agentsPage(r);
|
|
172
|
+
if (active === "activity") return activityPage();
|
|
173
|
+
if (active === "advanced") return advancedPage(r);
|
|
174
|
+
return homePage(r);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function header(title, r) {
|
|
178
|
+
return '<div class="top"><div><h1>' + title + '</h1><div class="meta">API ' + esc(r.apiBase) + ' - config ' + esc(r.configPath) + '</div></div><button class="btn secondary" data-refresh>Re-detect</button></div>';
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function homePage(r) {
|
|
182
|
+
const next = r.nextAction;
|
|
183
|
+
const pair = snapshot.pendingPair
|
|
184
|
+
? '<div class="banner"><div><strong>Approve this device: ' + esc(snapshot.pendingPair.user_code) + '</strong><div class="meta">The approval page was opened. After approving it in Agent Rooms, check status here.</div></div><button class="btn" data-action="' + encodeURIComponent(JSON.stringify({ id: "pair.poll", label: "Check approval" })) + '">Check approval</button></div>'
|
|
185
|
+
: "";
|
|
186
|
+
const banner = next
|
|
187
|
+
? '<div class="banner"><div><strong>Next: ' + esc(next.label) + '</strong><div class="meta">' + esc(next.hint) + '</div></div>' + actionButton(next.action) + '</div>'
|
|
188
|
+
: '<div class="banner ready"><div><strong>All required steps are ready.</strong><div class="meta">Open Activity to watch wakes and errors.</div></div></div>';
|
|
189
|
+
return header("Readiness", r) + pair + banner + '<div class="grid two"><section class="panel"><h2>Path</h2>' + r.steps.map(stepHtml).join("") + '</section>' + sideStatus(r) + '</div>';
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function stepHtml(step) {
|
|
193
|
+
const cls = "step " + step.status + (snapshot.readiness.nextAction && snapshot.readiness.nextAction.id === step.id ? " frontier" : "") + (step.status === "ready" ? " done" : "");
|
|
194
|
+
return '<div class="' + cls + '"><div class="dot"></div><div><div class="label">' + esc(step.label) + ' <span class="pill">' + statusText(step.status) + '</span></div><div class="hint">' + esc(step.hint) + '</div></div>' + actionButton(step.action) + '</div>';
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function sideStatus(r) {
|
|
198
|
+
return '<section class="panel"><h2>Local Truth</h2>' +
|
|
199
|
+
'<div class="row"><span>Device</span><span class="pill ' + (r.device.paired ? "ready" : "warn") + '">' + (r.device.paired ? "PAIRED" : "NOT PAIRED") + '</span></div>' +
|
|
200
|
+
'<div class="row"><span>Listener</span><span class="pill ' + (r.listener.running ? "ready" : "warn") + '">' + (r.listener.running ? "RUNNING" : "STOPPED") + '</span></div>' +
|
|
201
|
+
Object.values(r.hosts).map((h) => '<div class="row"><span>' + esc(h.label) + '<br><span class="meta">' + esc(h.version || h.detail) + '</span></span><span class="pill ' + (h.detected ? "ready" : "bad") + '">' + (h.detected ? "DETECTED" : "MISSING") + '</span></div>').join("") +
|
|
202
|
+
'<h2>Diagnose</h2>' + r.diagnostics.map((d) => '<div class="row"><span><strong>' + esc(d.symptom) + '</strong><br><span class="meta">' + esc(d.cause) + '</span></span>' + actionButton(d.action, "secondary") + '</div>').join("") +
|
|
203
|
+
'</section>';
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function agentsPage(r) {
|
|
207
|
+
return header("Agents", r) + '<div class="grid two"><section class="panel"><h2>Bindings</h2>' +
|
|
208
|
+
(r.agents.length ? r.agents.map((a) => '<div class="row"><span><strong>' + esc(a.agent) + '</strong><br><span class="meta">' + esc(a.host) + ' - ' + esc(a.workspace) + '<br>rooms: ' + esc(a.rooms.join(", ")) + '</span></span><span class="pill ' + (a.workspaceExists ? "ready" : "warn") + '">' + (a.workspaceExists ? "READY" : "PATH MISSING") + '</span></div>').join("") : '<div class="kv meta">No bindings yet.</div>') +
|
|
209
|
+
'</section><section class="panel"><h2>Add / Replace Binding</h2><form class="form" id="bindingForm">' +
|
|
210
|
+
'<label>Agent plate<input name="agent" placeholder="BRNL-AGT-XXXXXXXX" required /></label>' +
|
|
211
|
+
'<label>Room id(s)<input name="rooms" placeholder="room_1, room_2" required /></label>' +
|
|
212
|
+
'<label>Workspace path<input name="workspace" placeholder="D:\\\\your-project" required /></label>' +
|
|
213
|
+
'<label>Host<select name="host"><option value="claude_code">Claude Code</option><option value="codex">Codex</option></select></label>' +
|
|
214
|
+
'<button class="btn" type="submit">Save binding</button></form></section></div>';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function activityPage() {
|
|
218
|
+
const rows = (snapshot.activity || []).slice().reverse().map((e) => '<div class="event">[' + new Date(e.at).toLocaleTimeString() + '] ' + esc(e.level.toUpperCase()) + ' ' + esc(e.message) + '</div>').join("");
|
|
219
|
+
return header("Activity", snapshot.readiness) + '<section class="panel"><h2>Live feed</h2><div class="activity">' + (rows || '<div class="event">No events yet.</div>') + '</div></section>';
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function advancedPage(r) {
|
|
223
|
+
return header("Advanced", r) + '<div class="grid two"><section class="panel"><h2>Commands</h2><pre>' + esc(r.advanced.commands.join("\n")) + '</pre></section><section class="panel"><h2>Raw Config</h2><pre>' + esc(JSON.stringify(r.advanced.rawConfig, null, 2)) + '</pre></section></div>';
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function actionButton(action, style = "") {
|
|
227
|
+
if (!action) return "";
|
|
228
|
+
return '<button class="btn ' + style + '" data-action="' + encodeURIComponent(JSON.stringify(action)) + '">' + esc(action.label) + '</button>';
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function bind() {
|
|
232
|
+
document.querySelectorAll("[data-tab]").forEach((btn) => btn.addEventListener("click", () => { active = btn.dataset.tab; render(); }));
|
|
233
|
+
document.querySelectorAll("[data-refresh]").forEach((btn) => btn.addEventListener("click", load));
|
|
234
|
+
document.querySelectorAll("[data-action]").forEach((btn) => btn.addEventListener("click", () => act(JSON.parse(decodeURIComponent(btn.dataset.action)))));
|
|
235
|
+
const form = document.getElementById("bindingForm");
|
|
236
|
+
if (form) form.addEventListener("submit", (event) => {
|
|
237
|
+
event.preventDefault();
|
|
238
|
+
const data = new FormData(form);
|
|
239
|
+
act({ id: "config.binding.save", label: "Save binding" }, {
|
|
240
|
+
agent: String(data.get("agent") || ""),
|
|
241
|
+
rooms: String(data.get("rooms") || ""),
|
|
242
|
+
workspace: String(data.get("workspace") || ""),
|
|
243
|
+
host: String(data.get("host") || "claude_code")
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function showToast(message) {
|
|
249
|
+
const el = document.getElementById("toast");
|
|
250
|
+
if (!el) return;
|
|
251
|
+
el.textContent = message;
|
|
252
|
+
el.classList.remove("hidden");
|
|
253
|
+
clearTimeout(toastTimer);
|
|
254
|
+
toastTimer = setTimeout(() => el.classList.add("hidden"), 9000);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function esc(value) {
|
|
258
|
+
return String(value ?? "").replace(/[&<>"']/g, (ch) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[ch]));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
window.addEventListener("focus", load);
|
|
262
|
+
setInterval(load, 10000);
|
|
263
|
+
const events = new EventSource("/api/events?token=" + encodeURIComponent(token));
|
|
264
|
+
events.onmessage = (event) => { snapshot = JSON.parse(event.data); render(); };
|
|
265
|
+
load().catch((err) => { app.innerHTML = "<main><h1>Unable to load</h1><p>" + esc(err.message) + "</p></main>"; });
|
|
266
|
+
</script>
|
|
267
|
+
</body>
|
|
268
|
+
</html>`;
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=ui.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../../src/dashboard/ui.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,aAAa;IAC3B,OAAO,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA0QX,CAAC;AACT,CAAC"}
|