agentchannel 0.5.1 → 0.5.3
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/dist/web.js +210 -110
- package/dist/web.js.map +1 -1
- package/package.json +1 -1
package/dist/web.js
CHANGED
|
@@ -2,80 +2,152 @@ import { createServer } from "node:http";
|
|
|
2
2
|
import mqtt from "mqtt";
|
|
3
3
|
import { deriveKey, hashRoom, decrypt } from "./crypto.js";
|
|
4
4
|
const MAX_HISTORY = 200;
|
|
5
|
-
const CSS = `*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
|
|
6
|
-
:root{--bg-primary:#1a1a2e;--bg-secondary:#16213e;--bg-hover:#1f2b47;--text-primary:#e0e0e0;--text-secondary:#8892a4;--text-muted:#565f73;--mention-bg:rgba(88,133,240,0.15);--mention-text:#7cacf8;--mention-border:rgba(88,133,240,0.3);--system-text:#565f73;--channel-bg:rgba(77,186,135,0.12);--channel-text:#4dba87;--channel-border:rgba(77,186,135,0.25);--divider:rgba(255,255,255,0.06);--font-sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;--radius-sm:4px;--radius-lg:12px}
|
|
7
|
-
html{font-size:15px;-webkit-font-smoothing:antialiased}
|
|
8
|
-
body{font-family:var(--font-sans);background:var(--bg-primary);color:var(--text-primary);line-height:1.6;min-height:100vh}
|
|
9
|
-
.chat-container{max-width:820px;margin:0 auto;display:flex;flex-direction:column;min-height:100vh}
|
|
10
|
-
.chat-header{padding:20px 20px 16px;border-bottom:1px solid var(--divider);display:flex;align-items:baseline;gap:12px;position:sticky;top:0;background:var(--bg-primary);z-index:10}
|
|
11
|
-
.chat-title{font-size:1.15rem;font-weight:700;letter-spacing:-0.01em}
|
|
12
|
-
.chat-status{font-size:0.8rem;color:var(--text-muted)}
|
|
13
|
-
.chat-status.connected{color:#4ade80}
|
|
14
|
-
.message-list{padding:8px 0;flex:1}
|
|
15
|
-
.message{display:flex;padding:2px 20px;transition:background-color 0.1s ease}
|
|
16
|
-
.message:hover{background:var(--bg-hover)}
|
|
17
|
-
.message__gutter{width:40px;flex-shrink:0;margin-right:12px;display:flex;align-items:flex-start;justify-content:center;padding-top:4px}
|
|
18
|
-
.message__avatar{width:36px;height:36px;border-radius:var(--radius-lg);background:var(--avatar-color,#7c6bf0);color:#fff;font-weight:700;font-size:0.85rem;display:flex;align-items:center;justify-content:center;user-select:none}
|
|
19
|
-
.message__body{flex:1;min-width:0}
|
|
20
|
-
.message--first{padding-top:10px;margin-top:4px}
|
|
21
|
-
.message--first+.message--first{border-top:1px solid var(--divider)}
|
|
22
|
-
.message--system+.message--first,.message--first:first-child{border-top:none}
|
|
23
|
-
.message__header{display:flex;align-items:baseline;gap:8px;margin-bottom:2px;flex-wrap:wrap}
|
|
24
|
-
.message__sender{font-weight:700;font-size:0.935rem;color:var(--sender-color,#7c6bf0)}
|
|
25
|
-
.message__channel{display:inline-block;font-size:0.72rem;font-weight:600;padding:1px 7px;border-radius:9999px;background:var(--channel-bg);color:var(--channel-text);border:1px solid var(--channel-border);white-space:nowrap}
|
|
26
|
-
.message__time{font-size:0.72rem;color:var(--text-muted);font-variant-numeric:tabular-nums;white-space:nowrap}
|
|
27
|
-
.message__time--hover{opacity:0;font-size:0.68rem;transition:opacity 0.15s ease;padding-top:4px}
|
|
28
|
-
.message--grouped:hover .message__time--hover{opacity:1}
|
|
29
|
-
.message__content{font-size:0.935rem;line-height:1.65;word-wrap:break-word}
|
|
30
|
-
.message--grouped{padding-top:1px;padding-bottom:1px}
|
|
31
|
-
.mention{display:inline;background:var(--mention-bg);color:var(--mention-text);padding:1px 5px;border-radius:var(--radius-sm);font-weight:600;font-size:0.9rem;border:1px solid var(--mention-border);white-space:nowrap}
|
|
32
|
-
.message--system{padding-top:6px;padding-bottom:6px}
|
|
33
|
-
.message--system .message__body{display:flex;align-items:center;gap:10px;padding-left:52px}
|
|
34
|
-
.message__system-text{font-size:0.82rem;color:var(--system-text);font-style:italic}
|
|
35
|
-
.message--system:hover{background:transparent}
|
|
36
|
-
::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:rgba(255,255,255,0.1);border-radius:3px}
|
|
37
|
-
@media(max-width:600px){html{font-size:14px}.message{padding-left:12px;padding-right:12px}.message__gutter{width:32px;margin-right:10px}.message__avatar{width:30px;height:30px;font-size:0.75rem}.message--system .message__body{padding-left:42px}}`;
|
|
38
5
|
const HTML = `<!DOCTYPE html>
|
|
39
6
|
<html lang="en">
|
|
40
7
|
<head>
|
|
41
8
|
<meta charset="utf-8">
|
|
42
9
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
43
10
|
<title>AgentChannel</title>
|
|
44
|
-
<style
|
|
11
|
+
<style>
|
|
12
|
+
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
|
|
13
|
+
|
|
14
|
+
:root {
|
|
15
|
+
--bg: #ffffff;
|
|
16
|
+
--bg-alt: #f7f7f8;
|
|
17
|
+
--bg-sidebar: #f7f7f8;
|
|
18
|
+
--bg-bubble: #f7f7f8;
|
|
19
|
+
--bg-bubble-self: #ececf1;
|
|
20
|
+
--bg-hover: rgba(0,0,0,0.02);
|
|
21
|
+
--text: #0d0d0d;
|
|
22
|
+
--text-secondary: #6b6c7b;
|
|
23
|
+
--text-muted: #acacbe;
|
|
24
|
+
--text-sidebar: #6b6c7b;
|
|
25
|
+
--text-sidebar-active: #0d0d0d;
|
|
26
|
+
--mention-bg: rgba(59,130,246,0.08);
|
|
27
|
+
--mention-text: #2563eb;
|
|
28
|
+
--border: #e5e5e5;
|
|
29
|
+
--accent: #0d0d0d;
|
|
30
|
+
--sidebar-active: #ececf1;
|
|
31
|
+
--font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@media (prefers-color-scheme: dark) {
|
|
35
|
+
:root {
|
|
36
|
+
--bg: #212121;
|
|
37
|
+
--bg-alt: #2f2f2f;
|
|
38
|
+
--bg-sidebar: #171717;
|
|
39
|
+
--bg-bubble: #2f2f2f;
|
|
40
|
+
--bg-bubble-self: #303030;
|
|
41
|
+
--bg-hover: rgba(255,255,255,0.02);
|
|
42
|
+
--text: #ececec;
|
|
43
|
+
--text-secondary: #9b9b9b;
|
|
44
|
+
--text-muted: #666;
|
|
45
|
+
--text-sidebar: #9b9b9b;
|
|
46
|
+
--text-sidebar-active: #ececec;
|
|
47
|
+
--mention-bg: rgba(137,180,250,0.12);
|
|
48
|
+
--mention-text: #89b4fa;
|
|
49
|
+
--border: #383838;
|
|
50
|
+
--accent: #ececec;
|
|
51
|
+
--sidebar-active: #2f2f2f;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
html { font-size: 16px; -webkit-font-smoothing: antialiased; }
|
|
56
|
+
body { font-family: var(--font); background: var(--bg); color: var(--text); height: 100vh; overflow: hidden; }
|
|
57
|
+
|
|
58
|
+
.app { display: flex; height: 100vh; }
|
|
59
|
+
|
|
60
|
+
/* Sidebar */
|
|
61
|
+
.sidebar { width: 260px; background: var(--bg-sidebar); border-right: 1px solid var(--border); display: flex; flex-direction: column; flex-shrink: 0; }
|
|
62
|
+
.sidebar__header { padding: 20px; font-size: 0.875rem; font-weight: 600; color: var(--text-secondary); letter-spacing: 0.05em; text-transform: uppercase; }
|
|
63
|
+
.sidebar__channels { flex: 1; padding: 0 8px; overflow-y: auto; }
|
|
64
|
+
.sidebar__channel { display: flex; align-items: center; padding: 10px 12px; border-radius: 8px; cursor: pointer; color: var(--text-sidebar); font-size: 0.9rem; transition: all 0.15s; margin-bottom: 2px; }
|
|
65
|
+
.sidebar__channel:hover { background: var(--bg-hover); }
|
|
66
|
+
.sidebar__channel.active { background: var(--sidebar-active); color: var(--text-sidebar-active); font-weight: 600; }
|
|
67
|
+
.sidebar__channel .icon { width: 20px; margin-right: 10px; font-size: 1.1rem; text-align: center; }
|
|
68
|
+
.sidebar__status { padding: 16px 20px; font-size: 0.75rem; color: var(--text-muted); border-top: 1px solid var(--border); }
|
|
69
|
+
.sidebar__status.connected { color: #22c55e; }
|
|
70
|
+
|
|
71
|
+
/* Main */
|
|
72
|
+
.main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
|
|
73
|
+
.main__header { padding: 16px 24px; border-bottom: 1px solid var(--border); font-weight: 600; font-size: 1rem; display: flex; align-items: center; gap: 8px; }
|
|
74
|
+
.main__header .channel-name { color: var(--text); }
|
|
75
|
+
.main__header .channel-desc { color: var(--text-muted); font-weight: 400; font-size: 0.85rem; }
|
|
76
|
+
|
|
77
|
+
/* Messages */
|
|
78
|
+
.messages { flex: 1; overflow-y: auto; padding: 24px 0; }
|
|
79
|
+
.messages__inner { max-width: 768px; margin: 0 auto; padding: 0 24px; }
|
|
80
|
+
|
|
81
|
+
.conversation { margin-bottom: 24px; }
|
|
82
|
+
.conversation__header { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 4px; }
|
|
83
|
+
.conversation__avatar { width: 36px; height: 36px; border-radius: 50%; background: var(--accent); color: var(--bg); font-weight: 700; font-size: 0.8rem; display: flex; align-items: center; justify-content: center; flex-shrink: 0; margin-top: 2px; }
|
|
84
|
+
.conversation__meta { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
|
|
85
|
+
.conversation__sender { font-weight: 600; font-size: 0.9rem; }
|
|
86
|
+
.conversation__channel { font-size: 0.75rem; color: var(--text-muted); }
|
|
87
|
+
.conversation__time { font-size: 0.75rem; color: var(--text-muted); }
|
|
88
|
+
|
|
89
|
+
.conversation__bubble { margin-left: 52px; padding: 12px 16px; background: var(--bg-bubble); border-radius: 12px; font-size: 0.938rem; line-height: 1.7; color: var(--text); word-wrap: break-word; margin-bottom: 6px; }
|
|
90
|
+
.conversation__bubble--grouped { margin-top: 2px; }
|
|
91
|
+
|
|
92
|
+
.mention { background: var(--mention-bg); color: var(--mention-text); padding: 1px 4px; border-radius: 4px; font-weight: 600; font-size: 0.875rem; }
|
|
93
|
+
|
|
94
|
+
.system-msg { text-align: center; font-size: 0.8rem; color: var(--text-muted); padding: 8px 0; }
|
|
95
|
+
|
|
96
|
+
.empty { display: flex; align-items: center; justify-content: center; height: 100%; color: var(--text-muted); font-size: 0.9rem; }
|
|
97
|
+
|
|
98
|
+
::-webkit-scrollbar { width: 6px; }
|
|
99
|
+
::-webkit-scrollbar-track { background: transparent; }
|
|
100
|
+
::-webkit-scrollbar-thumb { background: rgba(128,128,128,0.15); border-radius: 3px; }
|
|
101
|
+
|
|
102
|
+
@media (max-width: 700px) {
|
|
103
|
+
.sidebar { width: 0; display: none; }
|
|
104
|
+
.messages__inner { padding: 0 16px; }
|
|
105
|
+
.conversation__bubble { margin-left: 0; }
|
|
106
|
+
}
|
|
107
|
+
</style>
|
|
45
108
|
</head>
|
|
46
109
|
<body>
|
|
47
|
-
<div class="
|
|
48
|
-
<
|
|
49
|
-
<
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
|
|
110
|
+
<div class="app">
|
|
111
|
+
<div class="sidebar">
|
|
112
|
+
<div class="sidebar__header">Channels</div>
|
|
113
|
+
<div class="sidebar__channels" id="channel-list"></div>
|
|
114
|
+
<div class="sidebar__status" id="status">connecting...</div>
|
|
115
|
+
</div>
|
|
116
|
+
<div class="main">
|
|
117
|
+
<div class="main__header">
|
|
118
|
+
<span class="channel-name" id="header-name"># all</span>
|
|
119
|
+
<span class="channel-desc" id="header-desc">All channels</span>
|
|
120
|
+
</div>
|
|
121
|
+
<div class="messages" id="messages-scroll">
|
|
122
|
+
<div class="messages__inner" id="messages">
|
|
123
|
+
<div class="empty">Waiting for messages...</div>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
53
127
|
</div>
|
|
54
128
|
<script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
|
|
55
129
|
<script>
|
|
56
130
|
const CONFIG = __CONFIG__;
|
|
57
|
-
const COLORS = ["#
|
|
131
|
+
const COLORS = ["#6366f1","#ec4899","#14b8a6","#f59e0b","#3b82f6","#ef4444","#8b5cf6","#22c55e"];
|
|
58
132
|
const senderColors = {};
|
|
59
|
-
let
|
|
133
|
+
let activeChannel = "all";
|
|
134
|
+
const allMessages = [];
|
|
60
135
|
|
|
61
136
|
const encoder = new TextEncoder();
|
|
62
137
|
const decoder = new TextDecoder();
|
|
63
138
|
|
|
64
|
-
function
|
|
65
|
-
if (!senderColors[name])
|
|
66
|
-
const idx = Object.keys(senderColors).length % COLORS.length;
|
|
67
|
-
senderColors[name] = COLORS[idx];
|
|
68
|
-
}
|
|
139
|
+
function getColor(name) {
|
|
140
|
+
if (!senderColors[name]) senderColors[name] = COLORS[Object.keys(senderColors).length % COLORS.length];
|
|
69
141
|
return senderColors[name];
|
|
70
142
|
}
|
|
71
143
|
|
|
72
|
-
async function deriveKey(
|
|
73
|
-
const km = await crypto.subtle.importKey("raw",
|
|
144
|
+
async function deriveKey(s) {
|
|
145
|
+
const km = await crypto.subtle.importKey("raw",encoder.encode(s),"PBKDF2",false,["deriveKey"]);
|
|
74
146
|
return crypto.subtle.deriveKey({name:"PBKDF2",salt:encoder.encode("agentchannel-v1"),iterations:100000,hash:"SHA-256"},km,{name:"AES-GCM",length:256},false,["encrypt","decrypt"]);
|
|
75
147
|
}
|
|
76
148
|
|
|
77
|
-
async function hashRoom(
|
|
78
|
-
const h = await crypto.subtle.digest("SHA-256",
|
|
149
|
+
async function hashRoom(c) {
|
|
150
|
+
const h = await crypto.subtle.digest("SHA-256",encoder.encode(c));
|
|
79
151
|
return Array.from(new Uint8Array(h)).map(b=>b.toString(16).padStart(2,"0")).join("").slice(0,16);
|
|
80
152
|
}
|
|
81
153
|
|
|
@@ -86,80 +158,112 @@ async function decrypt(payload, key) {
|
|
|
86
158
|
const tag = Uint8Array.from(atob(p.tag),c=>c.charCodeAt(0));
|
|
87
159
|
const combined = new Uint8Array(data.length+tag.length);
|
|
88
160
|
combined.set(data); combined.set(tag, data.length);
|
|
89
|
-
|
|
90
|
-
return decoder.decode(pt);
|
|
161
|
+
return decoder.decode(await crypto.subtle.decrypt({name:"AES-GCM",iv},key,combined));
|
|
91
162
|
}
|
|
92
163
|
|
|
93
164
|
const msgsEl = document.getElementById("messages");
|
|
94
|
-
const
|
|
165
|
+
const scrollEl = document.getElementById("messages-scroll");
|
|
166
|
+
const headerName = document.getElementById("header-name");
|
|
167
|
+
const headerDesc = document.getElementById("header-desc");
|
|
95
168
|
|
|
96
|
-
function
|
|
169
|
+
function esc(s) { return s.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"); }
|
|
170
|
+
function mentions(t) { return esc(t).replace(/@(\\w+)/g,'<span class="mention">@$1</span>'); }
|
|
97
171
|
|
|
98
|
-
function
|
|
99
|
-
|
|
100
|
-
}
|
|
172
|
+
function render() {
|
|
173
|
+
const filtered = activeChannel === "all" ? allMessages : allMessages.filter(m => m.channel === activeChannel);
|
|
174
|
+
if (!filtered.length) { msgsEl.innerHTML = '<div class="empty">No messages yet</div>'; return; }
|
|
101
175
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
div.className = "message message--system";
|
|
106
|
-
const time = new Date(msg.timestamp).toLocaleTimeString([], {hour:"2-digit",minute:"2-digit"});
|
|
107
|
-
div.innerHTML = '<div class="message__body"><time class="message__time">'+time+'</time><span class="message__system-text">'+escapeHtml(msg.content)+'</span></div>';
|
|
108
|
-
msgsEl.appendChild(div);
|
|
109
|
-
lastSender = null;
|
|
110
|
-
} else {
|
|
111
|
-
const isGrouped = lastSender === msg.sender;
|
|
112
|
-
const color = getSenderColor(msg.sender);
|
|
113
|
-
const time = new Date(msg.timestamp).toLocaleTimeString([], {hour:"2-digit",minute:"2-digit"});
|
|
114
|
-
const div = document.createElement("div");
|
|
176
|
+
let html = "";
|
|
177
|
+
let lastSender = null;
|
|
178
|
+
let lastChannel = null;
|
|
115
179
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
div.innerHTML = '<div class="message__gutter"><div class="message__avatar" style="--avatar-color:'+color+'">'+msg.sender[0].toUpperCase()+'</div></div><div class="message__body"><div class="message__header"><span class="message__sender" style="--sender-color:'+color+'">@'+escapeHtml(msg.sender)+'</span><span class="message__channel">#'+escapeHtml(channel)+'</span><time class="message__time">'+time+'</time></div><div class="message__content">'+highlightMentions(msg.content)+'</div></div>';
|
|
180
|
+
for (const msg of filtered) {
|
|
181
|
+
if (msg.type === "system") {
|
|
182
|
+
html += '<div class="system-msg">' + esc(msg.content) + '</div>';
|
|
183
|
+
lastSender = null;
|
|
184
|
+
continue;
|
|
122
185
|
}
|
|
123
|
-
|
|
186
|
+
|
|
187
|
+
const time = new Date(msg.timestamp).toLocaleTimeString([], {hour:"2-digit", minute:"2-digit"});
|
|
188
|
+
const color = getColor(msg.sender);
|
|
189
|
+
const isGrouped = lastSender === msg.sender && lastChannel === msg.channel;
|
|
190
|
+
|
|
191
|
+
if (!isGrouped) {
|
|
192
|
+
html += '<div class="conversation">';
|
|
193
|
+
html += '<div class="conversation__header">';
|
|
194
|
+
html += '<div class="conversation__avatar" style="background:'+color+'">'+msg.sender[0].toUpperCase()+'</div>';
|
|
195
|
+
html += '<div class="conversation__meta">';
|
|
196
|
+
html += '<span class="conversation__sender" style="color:'+color+'">@'+esc(msg.sender)+'</span>';
|
|
197
|
+
if (activeChannel === "all") html += '<span class="conversation__channel">#'+esc(msg.channel)+'</span>';
|
|
198
|
+
html += '<span class="conversation__time">'+time+'</span>';
|
|
199
|
+
html += '</div></div>';
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
html += '<div class="conversation__bubble'+(isGrouped?' conversation__bubble--grouped':'')+'">' + mentions(msg.content) + '</div>';
|
|
203
|
+
|
|
204
|
+
if (!isGrouped) {
|
|
205
|
+
// Close conversation div will be handled by next non-grouped or end
|
|
206
|
+
}
|
|
207
|
+
|
|
124
208
|
lastSender = msg.sender;
|
|
209
|
+
lastChannel = msg.channel;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
msgsEl.innerHTML = html;
|
|
213
|
+
scrollEl.scrollTop = scrollEl.scrollHeight;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function renderSidebar() {
|
|
217
|
+
const el = document.getElementById("channel-list");
|
|
218
|
+
el.innerHTML = "";
|
|
219
|
+
const items = [{id:"all",label:"All channels",icon:"#"}];
|
|
220
|
+
for (const ch of CONFIG.channels) items.push({id:ch.channel,label:ch.channel,icon:"#"});
|
|
221
|
+
|
|
222
|
+
for (const item of items) {
|
|
223
|
+
const div = document.createElement("div");
|
|
224
|
+
div.className = "sidebar__channel"+(activeChannel===item.id?" active":"");
|
|
225
|
+
div.innerHTML = '<span class="icon">'+item.icon+'</span>'+esc(item.label);
|
|
226
|
+
div.onclick = () => {
|
|
227
|
+
activeChannel = item.id;
|
|
228
|
+
headerName.textContent = "# " + item.label;
|
|
229
|
+
headerDesc.textContent = item.id === "all" ? "All channels" : "";
|
|
230
|
+
renderSidebar();
|
|
231
|
+
render();
|
|
232
|
+
};
|
|
233
|
+
el.appendChild(div);
|
|
125
234
|
}
|
|
126
|
-
msgsEl.scrollTop = msgsEl.scrollHeight;
|
|
127
235
|
}
|
|
128
236
|
|
|
129
237
|
async function init() {
|
|
130
|
-
|
|
238
|
+
renderSidebar();
|
|
239
|
+
|
|
131
240
|
try {
|
|
132
241
|
const res = await fetch("/api/history");
|
|
133
242
|
const history = await res.json();
|
|
134
|
-
for (const
|
|
243
|
+
for (const m of history) allMessages.push(m);
|
|
244
|
+
render();
|
|
135
245
|
} catch(e) {}
|
|
136
246
|
|
|
137
247
|
const channels = {};
|
|
138
|
-
for (const ch of CONFIG.channels) {
|
|
139
|
-
channels[ch.channel] = {key: await deriveKey(ch.key), hash: await hashRoom(ch.key), name: ch.channel};
|
|
140
|
-
}
|
|
248
|
+
for (const ch of CONFIG.channels) channels[ch.channel] = {key:await deriveKey(ch.key),hash:await hashRoom(ch.key),name:ch.channel};
|
|
141
249
|
|
|
142
250
|
const client = mqtt.connect("wss://broker.emqx.io:8084/mqtt");
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (msg.sender !== CONFIG.name && Notification.permission === "granted") {
|
|
160
|
-
new Notification("AgentChannel #"+ch.name, {body:"@"+msg.sender+": "+msg.content});
|
|
161
|
-
}
|
|
162
|
-
} catch(e) {}
|
|
251
|
+
const statusEl = document.getElementById("status");
|
|
252
|
+
client.on("connect",()=>{statusEl.textContent="connected";statusEl.className="sidebar__status connected";for(const ch of Object.values(channels))client.subscribe("agentchannel/"+ch.hash+"/messages")});
|
|
253
|
+
client.on("close",()=>{statusEl.textContent="disconnected";statusEl.className="sidebar__status"});
|
|
254
|
+
|
|
255
|
+
if(Notification.permission==="default")Notification.requestPermission();
|
|
256
|
+
|
|
257
|
+
client.on("message",async(topic,payload)=>{
|
|
258
|
+
for(const ch of Object.values(channels)){
|
|
259
|
+
if(topic==="agentchannel/"+ch.hash+"/messages"){
|
|
260
|
+
try{
|
|
261
|
+
const msg=JSON.parse(await decrypt(payload.toString(),ch.key));
|
|
262
|
+
msg.channel=ch.name;
|
|
263
|
+
allMessages.push(msg);
|
|
264
|
+
render();
|
|
265
|
+
if(msg.sender!==CONFIG.name&&Notification.permission==="granted")new Notification("#"+ch.name+" @"+msg.sender,{body:msg.content});
|
|
266
|
+
}catch(e){}
|
|
163
267
|
}
|
|
164
268
|
}
|
|
165
269
|
});
|
|
@@ -176,12 +280,10 @@ export function startWebUI(config, port = 3456) {
|
|
|
176
280
|
key: deriveKey(ch.key),
|
|
177
281
|
hash: hashRoom(ch.key),
|
|
178
282
|
}));
|
|
179
|
-
// Server-side MQTT to collect history
|
|
180
283
|
const mqttClient = mqtt.connect("mqtt://broker.emqx.io:1883");
|
|
181
284
|
mqttClient.on("connect", () => {
|
|
182
|
-
for (const cs of channelStates)
|
|
285
|
+
for (const cs of channelStates)
|
|
183
286
|
mqttClient.subscribe(`agentchannel/${cs.hash}/messages`);
|
|
184
|
-
}
|
|
185
287
|
});
|
|
186
288
|
mqttClient.on("message", (_topic, payload) => {
|
|
187
289
|
for (const cs of channelStates) {
|
|
@@ -195,9 +297,7 @@ export function startWebUI(config, port = 3456) {
|
|
|
195
297
|
if (history.length > MAX_HISTORY)
|
|
196
298
|
history.shift();
|
|
197
299
|
}
|
|
198
|
-
catch {
|
|
199
|
-
// ignore
|
|
200
|
-
}
|
|
300
|
+
catch { }
|
|
201
301
|
}
|
|
202
302
|
}
|
|
203
303
|
});
|
package/dist/web.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web.js","sourceRoot":"","sources":["../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAG3D,MAAM,WAAW,GAAG,GAAG,CAAC;AAExB,MAAM,
|
|
1
|
+
{"version":3,"file":"web.js","sourceRoot":"","sources":["../src/web.ts"],"names":[],"mappings":"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"}
|