agent-comms 1.4.2 → 1.5.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.
Files changed (39) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +2 -2
  3. package/dist/bridges/claude-code/channel.js +1 -1
  4. package/dist/bridges/claude-code/channel.js.map +1 -1
  5. package/dist/bridges/codex/tool.js +1 -1
  6. package/dist/bridges/codex/tool.js.map +1 -1
  7. package/dist/bridges/mcp/index.js +1 -1
  8. package/dist/bridges/mcp/index.js.map +1 -1
  9. package/dist/bridges/pi/index.js +1 -1
  10. package/dist/bridges/pi/index.js.map +1 -1
  11. package/dist/bridges/user/cli.d.ts +12 -0
  12. package/dist/bridges/user/cli.d.ts.map +1 -0
  13. package/dist/bridges/user/cli.js +77 -0
  14. package/dist/bridges/user/cli.js.map +1 -0
  15. package/dist/bridges/user/controller.d.ts +39 -0
  16. package/dist/bridges/user/controller.d.ts.map +1 -0
  17. package/dist/bridges/user/controller.js +185 -0
  18. package/dist/bridges/user/controller.js.map +1 -0
  19. package/dist/bridges/user/tui.d.ts +21 -0
  20. package/dist/bridges/user/tui.d.ts.map +1 -0
  21. package/dist/bridges/user/tui.js +237 -0
  22. package/dist/bridges/user/tui.js.map +1 -0
  23. package/dist/bridges/user/web/index.html.d.ts +8 -0
  24. package/dist/bridges/user/web/index.html.d.ts.map +1 -0
  25. package/dist/bridges/user/web/index.html.js +386 -0
  26. package/dist/bridges/user/web/index.html.js.map +1 -0
  27. package/dist/bridges/user/web/server.d.ts +19 -0
  28. package/dist/bridges/user/web/server.d.ts.map +1 -0
  29. package/dist/bridges/user/web/server.js +277 -0
  30. package/dist/bridges/user/web/server.js.map +1 -0
  31. package/dist/cli.d.ts +8 -1
  32. package/dist/cli.d.ts.map +1 -1
  33. package/dist/cli.js +65 -4
  34. package/dist/cli.js.map +1 -1
  35. package/dist/core/store.d.ts +2 -2
  36. package/dist/core/store.d.ts.map +1 -1
  37. package/dist/core/store.js +4 -4
  38. package/dist/core/store.js.map +1 -1
  39. package/package.json +6 -4
@@ -0,0 +1,237 @@
1
+ /**
2
+ * TUI — terminal chat interface using readline + ANSI escape codes.
3
+ *
4
+ * Commands:
5
+ * /join <room> Join or switch to a room
6
+ * /leave [room] Leave the current (or specified) room
7
+ * /rooms List all rooms
8
+ * /agents List all agents
9
+ * /read [room] Read messages in current (or specified) room
10
+ * /dm <agent> <msg> Send a direct message
11
+ * /create <name> Create a public room
12
+ * /invite <room> <agent> Invite an agent to a room
13
+ * /kick <room> <agent> Kick an agent from a room
14
+ * /destroy <room> Destroy a room
15
+ * /help Show commands
16
+ * /quit Exit
17
+ *
18
+ * Anything without / is sent to the current room.
19
+ */
20
+ import * as readline from "node:readline";
21
+ import { ChatController } from "./controller.js";
22
+ // ANSI helpers
23
+ const BOLD = "\x1b[1m";
24
+ const DIM = "\x1b[2m";
25
+ const CYAN = "\x1b[36m";
26
+ const GREEN = "\x1b[32m";
27
+ const YELLOW = "\x1b[33m";
28
+ const RED = "\x1b[31m";
29
+ const MAGENTA = "\x1b[35m";
30
+ const RESET = "\x1b[0m";
31
+ const CLEAR_LINE = "\r\x1b[2K";
32
+ function prompt(controller) {
33
+ const room = controller.activeRoom;
34
+ return room ? `${GREEN}${room}${RESET} > ` : `${DIM}>${RESET} `;
35
+ }
36
+ export async function runTui(userName) {
37
+ const controller = new ChatController(userName);
38
+ await controller.init();
39
+ console.log(`${CYAN}Connected as ${BOLD}${userName} (user)${RESET} [${controller.agentId}]`);
40
+ console.log(`${DIM}Type /help for commands. Anything else goes to the current room.${RESET}\n`);
41
+ const rl = readline.createInterface({
42
+ input: process.stdin,
43
+ output: process.stdout,
44
+ });
45
+ // Delivery event handler — print above the prompt
46
+ function onMessage(event) {
47
+ const line = formatForTerminal(event);
48
+ process.stdout.write(`${CLEAR_LINE}${line}\n${prompt(controller)}`);
49
+ }
50
+ controller.on("message", onMessage);
51
+ function onError(err) {
52
+ process.stdout.write(`${CLEAR_LINE}${RED}Error: ${err.message}${RESET}\n${prompt(controller)}`);
53
+ }
54
+ controller.on("error", onError);
55
+ // Input loop
56
+ async function handleInput(input) {
57
+ const trimmed = input.trim();
58
+ if (trimmed.length === 0)
59
+ return;
60
+ if (trimmed.startsWith("/")) {
61
+ await handleCommand(trimmed, controller);
62
+ }
63
+ else if (controller.activeRoom) {
64
+ const result = await controller.sendToCurrentRoom(trimmed);
65
+ if (result.isError) {
66
+ process.stdout.write(`${RED}${result.content}${RESET}\n`);
67
+ }
68
+ }
69
+ else {
70
+ process.stdout.write(`${YELLOW}No active room. /join a room first or use /dm.${RESET}\n`);
71
+ }
72
+ }
73
+ function ask() {
74
+ rl.question(prompt(controller), (input) => {
75
+ void handleInput(input).then(() => {
76
+ ask();
77
+ });
78
+ });
79
+ }
80
+ ask();
81
+ // Graceful shutdown
82
+ rl.on("close", () => {
83
+ controller.off("message", onMessage);
84
+ controller.off("error", onError);
85
+ void controller.shutdown().then(() => process.exit(0));
86
+ });
87
+ }
88
+ // ---------------------------------------------------------------------------
89
+ // Command handler
90
+ // ---------------------------------------------------------------------------
91
+ async function handleCommand(input, c) {
92
+ const parts = input.slice(1).split(/\s+/);
93
+ const cmd = parts[0]?.toLowerCase();
94
+ const arg1 = parts[1];
95
+ const arg2 = parts.slice(2).join(" ");
96
+ switch (cmd) {
97
+ case "join": {
98
+ if (!arg1) {
99
+ process.stdout.write(`${YELLOW}Usage: /join <room>${RESET}\n`);
100
+ return;
101
+ }
102
+ const result = await c.switchRoom(arg1);
103
+ printResult(result);
104
+ if (!result.isError) {
105
+ const read = await c.readRoom();
106
+ if (!read.isError && read.content !== "No messages.") {
107
+ process.stdout.write(`${DIM}${read.content}${RESET}\n`);
108
+ }
109
+ }
110
+ return;
111
+ }
112
+ case "leave": {
113
+ const result = await c.leaveRoom(arg1);
114
+ printResult(result);
115
+ return;
116
+ }
117
+ case "rooms": {
118
+ const result = await c.listRooms();
119
+ printResult(result);
120
+ return;
121
+ }
122
+ case "agents": {
123
+ const result = await c.listAgents();
124
+ printResult(result);
125
+ return;
126
+ }
127
+ case "read": {
128
+ const result = await c.readRoom(arg1);
129
+ printResult(result);
130
+ return;
131
+ }
132
+ case "dm": {
133
+ if (!arg1 || !arg2) {
134
+ process.stdout.write(`${YELLOW}Usage: /dm <agent> <message>${RESET}\n`);
135
+ return;
136
+ }
137
+ const result = await c.dm(arg1, arg2);
138
+ printResult(result);
139
+ return;
140
+ }
141
+ case "create": {
142
+ if (!arg1) {
143
+ process.stdout.write(`${YELLOW}Usage: /create <name>${RESET}\n`);
144
+ return;
145
+ }
146
+ const result = await c.createRoom(arg1, "public", arg2);
147
+ printResult(result);
148
+ return;
149
+ }
150
+ case "invite": {
151
+ const agentId = parts[2];
152
+ if (!arg1 || !agentId) {
153
+ process.stdout.write(`${YELLOW}Usage: /invite <room> <agent>${RESET}\n`);
154
+ return;
155
+ }
156
+ const result = await c.invite(arg1, agentId);
157
+ printResult(result);
158
+ return;
159
+ }
160
+ case "kick": {
161
+ const agentId = parts[2];
162
+ if (!arg1 || !agentId) {
163
+ process.stdout.write(`${YELLOW}Usage: /kick <room> <agent>${RESET}\n`);
164
+ return;
165
+ }
166
+ const result = await c.kick(arg1, agentId);
167
+ printResult(result);
168
+ return;
169
+ }
170
+ case "destroy": {
171
+ if (!arg1) {
172
+ process.stdout.write(`${YELLOW}Usage: /destroy <room>${RESET}\n`);
173
+ return;
174
+ }
175
+ const result = await c.destroyRoom(arg1);
176
+ printResult(result);
177
+ return;
178
+ }
179
+ case "help":
180
+ process.stdout.write(`${CYAN}Commands:${RESET}
181
+ ${GREEN}/join${RESET} <room> Join or switch to a room
182
+ ${GREEN}/leave${RESET} [room] Leave current (or specified) room
183
+ ${GREEN}/rooms${RESET} List all rooms
184
+ ${GREEN}/agents${RESET} List all agents
185
+ ${GREEN}/read${RESET} [room] Read messages in room
186
+ ${GREEN}/dm${RESET} <agent> <msg> Send a direct message
187
+ ${GREEN}/create${RESET} <name> Create a public room
188
+ ${GREEN}/invite${RESET} <room> <id> Invite agent to room
189
+ ${GREEN}/kick${RESET} <room> <id> Kick agent from room
190
+ ${GREEN}/destroy${RESET} <room> Destroy a room
191
+ ${GREEN}/help${RESET} Show this help
192
+ ${GREEN}/quit${RESET} Exit
193
+ \n${DIM}Anything without / is sent to the current room.${RESET}\n`);
194
+ return;
195
+ case "quit":
196
+ process.stdout.write(`${DIM}Goodbye!${RESET}\n`);
197
+ process.exit(0);
198
+ default:
199
+ process.stdout.write(`${YELLOW}Unknown command: /${String(cmd)}. Type /help for commands.${RESET}\n`);
200
+ }
201
+ }
202
+ // ---------------------------------------------------------------------------
203
+ // Formatting
204
+ // ---------------------------------------------------------------------------
205
+ function printResult(result) {
206
+ if (result.isError) {
207
+ process.stdout.write(`${RED}${result.content}${RESET}\n`);
208
+ }
209
+ else {
210
+ process.stdout.write(`${result.content}\n`);
211
+ }
212
+ }
213
+ function formatForTerminal(event) {
214
+ switch (event.type) {
215
+ case "room_message":
216
+ return `${BOLD}[${event.message.room}] ${event.message.from}:${RESET} ${event.message.content}`;
217
+ case "dm":
218
+ return `${MAGENTA}DM from ${event.message.from}:${RESET} ${event.message.content}`;
219
+ case "member_joined":
220
+ return `${GREEN}→ ${event.agent} joined ${event.room}${RESET}`;
221
+ case "member_left":
222
+ return `${YELLOW}← ${event.agent} left ${event.room}${RESET}`;
223
+ case "member_status":
224
+ return `${CYAN}● ${event.agent} is now ${event.status} in ${event.room}${RESET}`;
225
+ case "delivery_status":
226
+ return `${DIM}✓ Message ${event.messageId} ${event.status} by ${event.agent}${RESET}`;
227
+ case "room_invite":
228
+ return `${CYAN}_invite to ${event.room} from ${event.from}${RESET}`;
229
+ case "room_members": {
230
+ const names = event.members
231
+ .map((m) => `${m.name} (${m.status})`)
232
+ .join(", ");
233
+ return `${DIM}Members of ${event.room}: ${names}${RESET}`;
234
+ }
235
+ }
236
+ }
237
+ //# sourceMappingURL=tui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tui.js","sourceRoot":"","sources":["../../../src/bridges/user/tui.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Frontend HTML — single-page chat UI served as a string constant.
3
+ *
4
+ * Vanilla JS + CSS, no build step, no framework. Communicates via
5
+ * REST API + WebSocket.
6
+ */
7
+ export declare const FRONTEND_HTML = "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Agent Comms</title>\n<style>\n :root {\n --bg: #1a1a2e;\n --surface: #16213e;\n --border: #0f3460;\n --text: #e4e4e4;\n --dim: #888;\n --accent: #00b4d8;\n --green: #06d6a0;\n --red: #ef476f;\n --yellow: #ffd166;\n --purple: #b5838d;\n }\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n background: var(--bg);\n color: var(--text);\n height: 100vh;\n display: flex;\n }\n #sidebar {\n width: 260px;\n background: var(--surface);\n border-right: 1px solid var(--border);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n #sidebar h2 {\n padding: 12px 16px;\n font-size: 14px;\n color: var(--accent);\n border-bottom: 1px solid var(--border);\n }\n .sidebar-section {\n flex: 1;\n overflow-y: auto;\n padding: 8px;\n }\n .sidebar-section h3 {\n font-size: 11px;\n text-transform: uppercase;\n color: var(--dim);\n padding: 8px 8px 4px;\n }\n .room-item, .agent-item {\n padding: 6px 10px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 13px;\n display: flex;\n align-items: center;\n gap: 6px;\n }\n .room-item:hover, .agent-item:hover { background: rgba(255,255,255,0.05); }\n .room-item.active { background: var(--border); }\n .status-dot {\n width: 8px; height: 8px;\n border-radius: 50%;\n display: inline-block;\n }\n .status-dot.active { background: var(--green); }\n .status-dot.idle { background: var(--yellow); }\n .status-dot.busy { background: var(--red); }\n .status-dot.offline { background: var(--dim); }\n #main {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n #header {\n padding: 12px 20px;\n border-bottom: 1px solid var(--border);\n font-size: 15px;\n font-weight: 600;\n background: var(--surface);\n }\n #messages {\n flex: 1;\n overflow-y: auto;\n padding: 16px 20px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n .msg {\n font-size: 13px;\n line-height: 1.5;\n }\n .msg .sender { font-weight: 600; color: var(--accent); }\n .msg .time { color: var(--dim); font-size: 11px; margin-left: 8px; }\n .msg.system { color: var(--dim); font-style: italic; }\n .msg.status { color: var(--yellow); font-size: 12px; }\n .msg.dm { color: var(--purple); }\n .msg .dm-badge {\n background: var(--purple);\n color: #fff;\n font-size: 10px;\n padding: 1px 5px;\n border-radius: 3px;\n font-weight: 600;\n }\n #input-bar {\n display: flex;\n padding: 12px 20px;\n gap: 8px;\n background: var(--surface);\n border-top: 1px solid var(--border);\n }\n #input {\n flex: 1;\n background: var(--bg);\n border: 1px solid var(--border);\n border-radius: 6px;\n padding: 8px 12px;\n color: var(--text);\n font-size: 13px;\n outline: none;\n }\n #input:focus { border-color: var(--accent); }\n #send-btn {\n background: var(--accent);\n border: none;\n border-radius: 6px;\n padding: 8px 16px;\n color: #fff;\n font-weight: 600;\n cursor: pointer;\n }\n #send-btn:hover { opacity: 0.9; }\n #empty-state {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--dim);\n font-size: 14px;\n }\n</style>\n</head>\n<body>\n\n<div id=\"sidebar\">\n <h2>Agent Comms</h2>\n <div class=\"sidebar-section\">\n <h3>Rooms</h3>\n <div id=\"room-list\"></div>\n <h3>Agents</h3>\n <div id=\"agent-list\"></div>\n </div>\n</div>\n\n<div id=\"main\">\n <div id=\"header\">Select a room</div>\n <div id=\"messages\"></div>\n <div id=\"input-bar\">\n <input id=\"input\" type=\"text\" placeholder=\"Type a message or /command...\" autocomplete=\"off\" />\n <button id=\"send-btn\">Send</button>\n </div>\n</div>\n\n<script>\nconst $ = (s) => document.querySelector(s);\nconst messagesEl = $('#messages');\nconst inputEl = $('#input');\nconst headerEl = $('#header');\nconst roomListEl = $('#room-list');\nconst agentListEl = $('#agent-list');\n\nlet currentRoom = null;\nlet ws = null;\n\n// Connect WebSocket\nfunction connect() {\n const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';\n ws = new WebSocket(proto + '//' + location.host);\n\n ws.onopen = () => { addSystem('Connected to mesh'); };\n ws.onclose = () => { addSystem('Disconnected \u2014 reconnecting...'); setTimeout(connect, 3000); };\n ws.onerror = () => {};\n\n ws.onmessage = (e) => {\n const frame = JSON.parse(e.data);\n if (frame.type === 'delivery') handleDelivery(frame.event);\n if (frame.type === 'result') addSystem(frame.result.content);\n if (frame.type === 'error') addSystem('Error: ' + frame.message);\n if (frame.type === 'state') {\n renderAgents(frame.agents);\n renderRooms(frame.rooms);\n }\n };\n}\n\nfunction sendAction(params) {\n if (ws && ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify(params));\n }\n}\n\n// Delivery event handling\nfunction handleDelivery(event) {\n switch (event.type) {\n case 'room_message':\n if (!currentRoom || event.message.room !== currentRoom) return;\n addMessage(event.message.from, event.message.content, event.message.timestamp);\n break;\n case 'dm':\n addDm(event.message.from, event.message.content, event.message.timestamp);\n break;\n case 'member_joined':\n addSystem(event.agent + ' joined ' + event.room);\n refreshState();\n break;\n case 'member_left':\n addSystem(event.agent + ' left ' + event.room);\n refreshState();\n break;\n case 'member_status':\n addStatus(event.agent + ' is now ' + event.status + ' in ' + event.room);\n refreshState();\n break;\n case 'delivery_status':\n addStatus('Message ' + event.messageId + ' ' + event.status + ' by ' + event.agent);\n break;\n case 'room_members':\n if (currentRoom === event.room) {\n addSystem('Members: ' + event.members.map(m => m.name + ' (' + m.status + ')').join(', '));\n }\n break;\n case 'room_invite':\n addSystem('Invited to ' + event.room + ' by ' + event.from);\n break;\n }\n}\n\n// UI rendering\nfunction addMessage(sender, content, timestamp) {\n const time = (timestamp || new Date().toISOString()).slice(11, 19);\n const div = document.createElement('div');\n div.className = 'msg';\n div.innerHTML = '<span class=\"sender\">' + esc(sender) + '</span><span class=\"time\">' + time + '</span>: ' + esc(content);\n messagesEl.appendChild(div);\n messagesEl.scrollTop = messagesEl.scrollHeight;\n}\n\nfunction addDm(sender, content, timestamp) {\n const time = (timestamp || new Date().toISOString()).slice(11, 19);\n const div = document.createElement('div');\n div.className = 'msg dm';\n div.innerHTML = '<span class=\"dm-badge\">DM</span> <span class=\"sender\">' + esc(sender) + '</span><span class=\"time\">' + time + '</span>: ' + esc(content);\n messagesEl.appendChild(div);\n messagesEl.scrollTop = messagesEl.scrollHeight;\n}\n\nfunction addSystem(text) {\n const div = document.createElement('div');\n div.className = 'msg system';\n div.textContent = text;\n messagesEl.appendChild(div);\n messagesEl.scrollTop = messagesEl.scrollHeight;\n}\n\nfunction addStatus(text) {\n const div = document.createElement('div');\n div.className = 'msg status';\n div.textContent = text;\n messagesEl.appendChild(div);\n messagesEl.scrollTop = messagesEl.scrollHeight;\n}\n\nfunction clearMessages() {\n messagesEl.innerHTML = '';\n}\n\nfunction esc(s) {\n const d = document.createElement('div');\n d.textContent = s;\n return d.innerHTML;\n}\n\n// State refresh\nasync function refreshState() {\n const [agentsRes, roomsRes] = await Promise.all([\n fetch('/api/agents'), fetch('/api/rooms')\n ]);\n renderAgents(await agentsRes.json());\n renderRooms(await roomsRes.json());\n}\n\nfunction renderAgents(agents) {\n agentListEl.innerHTML = '';\n for (const a of agents) {\n const div = document.createElement('div');\n div.className = 'agent-item';\n const dot = a.status || 'offline';\n div.innerHTML = '<span class=\"status-dot ' + dot + '\"></span> ' + esc(a.name);\n agentListEl.appendChild(div);\n }\n}\n\nfunction renderRooms(rooms) {\n roomListEl.innerHTML = '';\n for (const r of rooms) {\n const div = document.createElement('div');\n div.className = 'room-item' + (currentRoom === r.id ? ' active' : '');\n const joined = r.members.length;\n div.innerHTML = r.type.charAt(0).toUpperCase() + ' ' + esc(r.name) + ' <span style=\"color:var(--dim)\">(' + joined + ')</span>';\n div.onclick = () => joinRoom(r.id);\n roomListEl.appendChild(div);\n }\n}\n\nasync function joinRoom(roomId) {\n currentRoom = roomId;\n headerEl.textContent = roomId;\n clearMessages();\n\n // Highlight in sidebar\n document.querySelectorAll('.room-item').forEach(el => el.classList.remove('active'));\n sendAction({ action: 'join_room', room: roomId });\n\n // Load history\n const res = await fetch('/api/rooms/' + encodeURIComponent(roomId) + '/messages');\n const messages = await res.json();\n for (const m of messages) {\n addMessage(m.from, m.content, m.timestamp);\n }\n addSystem('Joined ' + roomId);\n inputEl.focus();\n}\n\n// Input handling\nfunction handleInput() {\n const text = inputEl.value.trim();\n if (!text) return;\n inputEl.value = '';\n\n if (text.startsWith('/')) {\n const parts = text.slice(1).split(/\\s+/);\n const cmd = parts[0].toLowerCase();\n switch (cmd) {\n case 'join': sendAction({ action: 'join_room', room: parts[1] }); break;\n case 'leave': sendAction({ action: 'leave_room', room: parts[1] || currentRoom }); break;\n case 'rooms': refreshState(); break;\n case 'agents': refreshState(); break;\n case 'dm': sendAction({ action: 'dm', target: parts[1], content: parts.slice(2).join(' ') }); break;\n case 'create': sendAction({ action: 'create_room', name: parts[1], type: 'public' }); break;\n case 'destroy': sendAction({ action: 'destroy_room', room: parts[1] }); break;\n case 'help':\n addSystem('Commands: /join, /leave, /rooms, /agents, /dm, /create, /destroy, /help');\n break;\n default:\n addSystem('Unknown command: /' + cmd);\n }\n } else if (currentRoom) {\n sendAction({ action: 'send', target: currentRoom, content: text });\n } else {\n addSystem('Join a room first (click one in the sidebar)');\n }\n}\n\n$('#send-btn').onclick = handleInput;\ninputEl.addEventListener('keydown', (e) => { if (e.key === 'Enter') handleInput(); });\n\n// Boot\nconnect();\nrefreshState();\ninputEl.focus();\n</script>\n</body>\n</html>";
8
+ //# sourceMappingURL=index.html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.html.d.ts","sourceRoot":"","sources":["../../../../src/bridges/user/web/index.html.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,aAAa,2xVA0XlB,CAAC"}
@@ -0,0 +1,386 @@
1
+ /**
2
+ * Frontend HTML — single-page chat UI served as a string constant.
3
+ *
4
+ * Vanilla JS + CSS, no build step, no framework. Communicates via
5
+ * REST API + WebSocket.
6
+ */
7
+ export const FRONTEND_HTML = `<!DOCTYPE html>
8
+ <html lang="en">
9
+ <head>
10
+ <meta charset="utf-8">
11
+ <meta name="viewport" content="width=device-width, initial-scale=1">
12
+ <title>Agent Comms</title>
13
+ <style>
14
+ :root {
15
+ --bg: #1a1a2e;
16
+ --surface: #16213e;
17
+ --border: #0f3460;
18
+ --text: #e4e4e4;
19
+ --dim: #888;
20
+ --accent: #00b4d8;
21
+ --green: #06d6a0;
22
+ --red: #ef476f;
23
+ --yellow: #ffd166;
24
+ --purple: #b5838d;
25
+ }
26
+ * { margin: 0; padding: 0; box-sizing: border-box; }
27
+ body {
28
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
29
+ background: var(--bg);
30
+ color: var(--text);
31
+ height: 100vh;
32
+ display: flex;
33
+ }
34
+ #sidebar {
35
+ width: 260px;
36
+ background: var(--surface);
37
+ border-right: 1px solid var(--border);
38
+ display: flex;
39
+ flex-direction: column;
40
+ overflow: hidden;
41
+ }
42
+ #sidebar h2 {
43
+ padding: 12px 16px;
44
+ font-size: 14px;
45
+ color: var(--accent);
46
+ border-bottom: 1px solid var(--border);
47
+ }
48
+ .sidebar-section {
49
+ flex: 1;
50
+ overflow-y: auto;
51
+ padding: 8px;
52
+ }
53
+ .sidebar-section h3 {
54
+ font-size: 11px;
55
+ text-transform: uppercase;
56
+ color: var(--dim);
57
+ padding: 8px 8px 4px;
58
+ }
59
+ .room-item, .agent-item {
60
+ padding: 6px 10px;
61
+ border-radius: 6px;
62
+ cursor: pointer;
63
+ font-size: 13px;
64
+ display: flex;
65
+ align-items: center;
66
+ gap: 6px;
67
+ }
68
+ .room-item:hover, .agent-item:hover { background: rgba(255,255,255,0.05); }
69
+ .room-item.active { background: var(--border); }
70
+ .status-dot {
71
+ width: 8px; height: 8px;
72
+ border-radius: 50%;
73
+ display: inline-block;
74
+ }
75
+ .status-dot.active { background: var(--green); }
76
+ .status-dot.idle { background: var(--yellow); }
77
+ .status-dot.busy { background: var(--red); }
78
+ .status-dot.offline { background: var(--dim); }
79
+ #main {
80
+ flex: 1;
81
+ display: flex;
82
+ flex-direction: column;
83
+ overflow: hidden;
84
+ }
85
+ #header {
86
+ padding: 12px 20px;
87
+ border-bottom: 1px solid var(--border);
88
+ font-size: 15px;
89
+ font-weight: 600;
90
+ background: var(--surface);
91
+ }
92
+ #messages {
93
+ flex: 1;
94
+ overflow-y: auto;
95
+ padding: 16px 20px;
96
+ display: flex;
97
+ flex-direction: column;
98
+ gap: 4px;
99
+ }
100
+ .msg {
101
+ font-size: 13px;
102
+ line-height: 1.5;
103
+ }
104
+ .msg .sender { font-weight: 600; color: var(--accent); }
105
+ .msg .time { color: var(--dim); font-size: 11px; margin-left: 8px; }
106
+ .msg.system { color: var(--dim); font-style: italic; }
107
+ .msg.status { color: var(--yellow); font-size: 12px; }
108
+ .msg.dm { color: var(--purple); }
109
+ .msg .dm-badge {
110
+ background: var(--purple);
111
+ color: #fff;
112
+ font-size: 10px;
113
+ padding: 1px 5px;
114
+ border-radius: 3px;
115
+ font-weight: 600;
116
+ }
117
+ #input-bar {
118
+ display: flex;
119
+ padding: 12px 20px;
120
+ gap: 8px;
121
+ background: var(--surface);
122
+ border-top: 1px solid var(--border);
123
+ }
124
+ #input {
125
+ flex: 1;
126
+ background: var(--bg);
127
+ border: 1px solid var(--border);
128
+ border-radius: 6px;
129
+ padding: 8px 12px;
130
+ color: var(--text);
131
+ font-size: 13px;
132
+ outline: none;
133
+ }
134
+ #input:focus { border-color: var(--accent); }
135
+ #send-btn {
136
+ background: var(--accent);
137
+ border: none;
138
+ border-radius: 6px;
139
+ padding: 8px 16px;
140
+ color: #fff;
141
+ font-weight: 600;
142
+ cursor: pointer;
143
+ }
144
+ #send-btn:hover { opacity: 0.9; }
145
+ #empty-state {
146
+ flex: 1;
147
+ display: flex;
148
+ align-items: center;
149
+ justify-content: center;
150
+ color: var(--dim);
151
+ font-size: 14px;
152
+ }
153
+ </style>
154
+ </head>
155
+ <body>
156
+
157
+ <div id="sidebar">
158
+ <h2>Agent Comms</h2>
159
+ <div class="sidebar-section">
160
+ <h3>Rooms</h3>
161
+ <div id="room-list"></div>
162
+ <h3>Agents</h3>
163
+ <div id="agent-list"></div>
164
+ </div>
165
+ </div>
166
+
167
+ <div id="main">
168
+ <div id="header">Select a room</div>
169
+ <div id="messages"></div>
170
+ <div id="input-bar">
171
+ <input id="input" type="text" placeholder="Type a message or /command..." autocomplete="off" />
172
+ <button id="send-btn">Send</button>
173
+ </div>
174
+ </div>
175
+
176
+ <script>
177
+ const $ = (s) => document.querySelector(s);
178
+ const messagesEl = $('#messages');
179
+ const inputEl = $('#input');
180
+ const headerEl = $('#header');
181
+ const roomListEl = $('#room-list');
182
+ const agentListEl = $('#agent-list');
183
+
184
+ let currentRoom = null;
185
+ let ws = null;
186
+
187
+ // Connect WebSocket
188
+ function connect() {
189
+ const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
190
+ ws = new WebSocket(proto + '//' + location.host);
191
+
192
+ ws.onopen = () => { addSystem('Connected to mesh'); };
193
+ ws.onclose = () => { addSystem('Disconnected — reconnecting...'); setTimeout(connect, 3000); };
194
+ ws.onerror = () => {};
195
+
196
+ ws.onmessage = (e) => {
197
+ const frame = JSON.parse(e.data);
198
+ if (frame.type === 'delivery') handleDelivery(frame.event);
199
+ if (frame.type === 'result') addSystem(frame.result.content);
200
+ if (frame.type === 'error') addSystem('Error: ' + frame.message);
201
+ if (frame.type === 'state') {
202
+ renderAgents(frame.agents);
203
+ renderRooms(frame.rooms);
204
+ }
205
+ };
206
+ }
207
+
208
+ function sendAction(params) {
209
+ if (ws && ws.readyState === WebSocket.OPEN) {
210
+ ws.send(JSON.stringify(params));
211
+ }
212
+ }
213
+
214
+ // Delivery event handling
215
+ function handleDelivery(event) {
216
+ switch (event.type) {
217
+ case 'room_message':
218
+ if (!currentRoom || event.message.room !== currentRoom) return;
219
+ addMessage(event.message.from, event.message.content, event.message.timestamp);
220
+ break;
221
+ case 'dm':
222
+ addDm(event.message.from, event.message.content, event.message.timestamp);
223
+ break;
224
+ case 'member_joined':
225
+ addSystem(event.agent + ' joined ' + event.room);
226
+ refreshState();
227
+ break;
228
+ case 'member_left':
229
+ addSystem(event.agent + ' left ' + event.room);
230
+ refreshState();
231
+ break;
232
+ case 'member_status':
233
+ addStatus(event.agent + ' is now ' + event.status + ' in ' + event.room);
234
+ refreshState();
235
+ break;
236
+ case 'delivery_status':
237
+ addStatus('Message ' + event.messageId + ' ' + event.status + ' by ' + event.agent);
238
+ break;
239
+ case 'room_members':
240
+ if (currentRoom === event.room) {
241
+ addSystem('Members: ' + event.members.map(m => m.name + ' (' + m.status + ')').join(', '));
242
+ }
243
+ break;
244
+ case 'room_invite':
245
+ addSystem('Invited to ' + event.room + ' by ' + event.from);
246
+ break;
247
+ }
248
+ }
249
+
250
+ // UI rendering
251
+ function addMessage(sender, content, timestamp) {
252
+ const time = (timestamp || new Date().toISOString()).slice(11, 19);
253
+ const div = document.createElement('div');
254
+ div.className = 'msg';
255
+ div.innerHTML = '<span class="sender">' + esc(sender) + '</span><span class="time">' + time + '</span>: ' + esc(content);
256
+ messagesEl.appendChild(div);
257
+ messagesEl.scrollTop = messagesEl.scrollHeight;
258
+ }
259
+
260
+ function addDm(sender, content, timestamp) {
261
+ const time = (timestamp || new Date().toISOString()).slice(11, 19);
262
+ const div = document.createElement('div');
263
+ div.className = 'msg dm';
264
+ div.innerHTML = '<span class="dm-badge">DM</span> <span class="sender">' + esc(sender) + '</span><span class="time">' + time + '</span>: ' + esc(content);
265
+ messagesEl.appendChild(div);
266
+ messagesEl.scrollTop = messagesEl.scrollHeight;
267
+ }
268
+
269
+ function addSystem(text) {
270
+ const div = document.createElement('div');
271
+ div.className = 'msg system';
272
+ div.textContent = text;
273
+ messagesEl.appendChild(div);
274
+ messagesEl.scrollTop = messagesEl.scrollHeight;
275
+ }
276
+
277
+ function addStatus(text) {
278
+ const div = document.createElement('div');
279
+ div.className = 'msg status';
280
+ div.textContent = text;
281
+ messagesEl.appendChild(div);
282
+ messagesEl.scrollTop = messagesEl.scrollHeight;
283
+ }
284
+
285
+ function clearMessages() {
286
+ messagesEl.innerHTML = '';
287
+ }
288
+
289
+ function esc(s) {
290
+ const d = document.createElement('div');
291
+ d.textContent = s;
292
+ return d.innerHTML;
293
+ }
294
+
295
+ // State refresh
296
+ async function refreshState() {
297
+ const [agentsRes, roomsRes] = await Promise.all([
298
+ fetch('/api/agents'), fetch('/api/rooms')
299
+ ]);
300
+ renderAgents(await agentsRes.json());
301
+ renderRooms(await roomsRes.json());
302
+ }
303
+
304
+ function renderAgents(agents) {
305
+ agentListEl.innerHTML = '';
306
+ for (const a of agents) {
307
+ const div = document.createElement('div');
308
+ div.className = 'agent-item';
309
+ const dot = a.status || 'offline';
310
+ div.innerHTML = '<span class="status-dot ' + dot + '"></span> ' + esc(a.name);
311
+ agentListEl.appendChild(div);
312
+ }
313
+ }
314
+
315
+ function renderRooms(rooms) {
316
+ roomListEl.innerHTML = '';
317
+ for (const r of rooms) {
318
+ const div = document.createElement('div');
319
+ div.className = 'room-item' + (currentRoom === r.id ? ' active' : '');
320
+ const joined = r.members.length;
321
+ div.innerHTML = r.type.charAt(0).toUpperCase() + ' ' + esc(r.name) + ' <span style="color:var(--dim)">(' + joined + ')</span>';
322
+ div.onclick = () => joinRoom(r.id);
323
+ roomListEl.appendChild(div);
324
+ }
325
+ }
326
+
327
+ async function joinRoom(roomId) {
328
+ currentRoom = roomId;
329
+ headerEl.textContent = roomId;
330
+ clearMessages();
331
+
332
+ // Highlight in sidebar
333
+ document.querySelectorAll('.room-item').forEach(el => el.classList.remove('active'));
334
+ sendAction({ action: 'join_room', room: roomId });
335
+
336
+ // Load history
337
+ const res = await fetch('/api/rooms/' + encodeURIComponent(roomId) + '/messages');
338
+ const messages = await res.json();
339
+ for (const m of messages) {
340
+ addMessage(m.from, m.content, m.timestamp);
341
+ }
342
+ addSystem('Joined ' + roomId);
343
+ inputEl.focus();
344
+ }
345
+
346
+ // Input handling
347
+ function handleInput() {
348
+ const text = inputEl.value.trim();
349
+ if (!text) return;
350
+ inputEl.value = '';
351
+
352
+ if (text.startsWith('/')) {
353
+ const parts = text.slice(1).split(/\\s+/);
354
+ const cmd = parts[0].toLowerCase();
355
+ switch (cmd) {
356
+ case 'join': sendAction({ action: 'join_room', room: parts[1] }); break;
357
+ case 'leave': sendAction({ action: 'leave_room', room: parts[1] || currentRoom }); break;
358
+ case 'rooms': refreshState(); break;
359
+ case 'agents': refreshState(); break;
360
+ case 'dm': sendAction({ action: 'dm', target: parts[1], content: parts.slice(2).join(' ') }); break;
361
+ case 'create': sendAction({ action: 'create_room', name: parts[1], type: 'public' }); break;
362
+ case 'destroy': sendAction({ action: 'destroy_room', room: parts[1] }); break;
363
+ case 'help':
364
+ addSystem('Commands: /join, /leave, /rooms, /agents, /dm, /create, /destroy, /help');
365
+ break;
366
+ default:
367
+ addSystem('Unknown command: /' + cmd);
368
+ }
369
+ } else if (currentRoom) {
370
+ sendAction({ action: 'send', target: currentRoom, content: text });
371
+ } else {
372
+ addSystem('Join a room first (click one in the sidebar)');
373
+ }
374
+ }
375
+
376
+ $('#send-btn').onclick = handleInput;
377
+ inputEl.addEventListener('keydown', (e) => { if (e.key === 'Enter') handleInput(); });
378
+
379
+ // Boot
380
+ connect();
381
+ refreshState();
382
+ inputEl.focus();
383
+ </script>
384
+ </body>
385
+ </html>`;
386
+ //# sourceMappingURL=index.html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.html.js","sourceRoot":"","sources":["../../../../src/bridges/user/web/index.html.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA0XrB,CAAC"}