@moxxy/plugin-channel-web 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/channel.d.ts +152 -0
- package/dist/channel.d.ts.map +1 -0
- package/dist/channel.js +595 -0
- package/dist/channel.js.map +1 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +171 -0
- package/dist/index.js.map +1 -0
- package/dist/projector.d.ts +17 -0
- package/dist/projector.d.ts.map +1 -0
- package/dist/projector.js +96 -0
- package/dist/projector.js.map +1 -0
- package/dist/protocol.d.ts +145 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +33 -0
- package/dist/protocol.js.map +1 -0
- package/dist/public/app.js +54 -0
- package/dist/public/index.html +178 -0
- package/dist/tunnel-settings.d.ts +17 -0
- package/dist/tunnel-settings.d.ts.map +1 -0
- package/dist/tunnel-settings.js +42 -0
- package/dist/tunnel-settings.js.map +1 -0
- package/package.json +71 -0
- package/src/channel.test.ts +514 -0
- package/src/channel.ts +669 -0
- package/src/discovery.test.ts +40 -0
- package/src/frontend/chat.test.ts +47 -0
- package/src/frontend/chat.tsx +138 -0
- package/src/frontend/index.html +178 -0
- package/src/frontend/main.tsx +87 -0
- package/src/frontend/render-diff.test.ts +86 -0
- package/src/frontend/render-diff.tsx +66 -0
- package/src/frontend/render.test.ts +166 -0
- package/src/frontend/render.tsx +274 -0
- package/src/frontend/socket.ts +212 -0
- package/src/frontend/url-safety.test.ts +0 -0
- package/src/frontend/url-safety.ts +33 -0
- package/src/frontend/view-store.test.ts +104 -0
- package/src/frontend/view-store.ts +120 -0
- package/src/index.ts +212 -0
- package/src/projector.test.ts +172 -0
- package/src/projector.ts +95 -0
- package/src/protocol.test.ts +59 -0
- package/src/protocol.ts +105 -0
- package/src/tunnel-settings.test.ts +64 -0
- package/src/tunnel-settings.ts +57 -0
- package/src/tunnel-tools.test.ts +120 -0
package/dist/channel.js
ADDED
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import { randomBytes } from 'node:crypto';
|
|
6
|
+
import { WebSocketServer } from 'ws';
|
|
7
|
+
import { createAllowListResolver } from '@moxxy/sdk';
|
|
8
|
+
import { bearerTokenMatches } from '@moxxy/sdk/server';
|
|
9
|
+
import { EventProjector } from './projector.js';
|
|
10
|
+
import { actionPrompt, clientFrameSchema } from './protocol.js';
|
|
11
|
+
/** Hard cap on an inbound WS frame; anything larger is garbage, not a prompt. */
|
|
12
|
+
const MAX_FRAME_BYTES = 256 * 1024;
|
|
13
|
+
/** Invalid-frame warnings are rate-limited to one per this window. */
|
|
14
|
+
const DROP_WARN_INTERVAL_MS = 10_000;
|
|
15
|
+
/**
|
|
16
|
+
* Cap on distinct NAMED screens kept for replay. Named views replace in place
|
|
17
|
+
* (bounded by an app's screen count) but we still LRU-evict so a pathological
|
|
18
|
+
* agent that mints unbounded distinct names can't grow the map without limit.
|
|
19
|
+
*/
|
|
20
|
+
const MAX_REPLAY_VIEWS = 32;
|
|
21
|
+
/**
|
|
22
|
+
* Replay-map slot shared by every UNNAMED view (latest wins). The leading
|
|
23
|
+
* newline guarantees no collision with an agent-chosen `<view name>` (view
|
|
24
|
+
* names can't contain control chars), so a real named screen never lands here.
|
|
25
|
+
*/
|
|
26
|
+
const UNNAMED_VIEW_KEY = '\n__current__';
|
|
27
|
+
function isAddrInUse(err) {
|
|
28
|
+
return (!!err &&
|
|
29
|
+
typeof err === 'object' &&
|
|
30
|
+
err.code === 'EADDRINUSE');
|
|
31
|
+
}
|
|
32
|
+
/** Run a command and collect its stdout. Empty string on any failure. */
|
|
33
|
+
async function captureStdout(cmd, args) {
|
|
34
|
+
const { spawn } = await import('node:child_process');
|
|
35
|
+
return await new Promise((resolve) => {
|
|
36
|
+
let out = '';
|
|
37
|
+
try {
|
|
38
|
+
const child = spawn(cmd, [...args], { stdio: ['ignore', 'pipe', 'ignore'] });
|
|
39
|
+
child.stdout.on('data', (b) => {
|
|
40
|
+
out += b.toString();
|
|
41
|
+
});
|
|
42
|
+
child.on('error', () => resolve(''));
|
|
43
|
+
child.on('close', () => resolve(out));
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
resolve('');
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/** PIDs actively LISTENing on a TCP port (lsof; empty on Windows / no lsof). */
|
|
51
|
+
async function pidsListeningOn(port) {
|
|
52
|
+
if (process.platform === 'win32')
|
|
53
|
+
return [];
|
|
54
|
+
const out = await captureStdout('lsof', ['-t', `-iTCP:${port}`, '-sTCP:LISTEN']);
|
|
55
|
+
const found = new Set();
|
|
56
|
+
for (const line of out.split('\n')) {
|
|
57
|
+
const n = parseInt(line.trim(), 10);
|
|
58
|
+
if (Number.isFinite(n) && n > 0)
|
|
59
|
+
found.add(n);
|
|
60
|
+
}
|
|
61
|
+
return [...found];
|
|
62
|
+
}
|
|
63
|
+
/** A PID's command line via `ps`. Empty when the process is gone / unknowable. */
|
|
64
|
+
async function pidCommand(pid) {
|
|
65
|
+
return (await captureStdout('ps', ['-p', String(pid), '-o', 'command='])).trim();
|
|
66
|
+
}
|
|
67
|
+
/** Identity gate: only ever signal processes that look like moxxy's own
|
|
68
|
+
* (CLI bin / `moxxy serve` daemon / desktop app). An unidentifiable
|
|
69
|
+
* command line fails the gate — never kill what we can't name. */
|
|
70
|
+
function looksLikeMoxxy(command) {
|
|
71
|
+
return command.length > 0 && /moxxy/i.test(command);
|
|
72
|
+
}
|
|
73
|
+
const realFreePortDeps = {
|
|
74
|
+
pidsListeningOn,
|
|
75
|
+
pidCommand,
|
|
76
|
+
kill: (pid, signal) => process.kill(pid, signal),
|
|
77
|
+
graceMs: 400,
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Free a TCP port ONLY if every process holding it is a moxxy process
|
|
81
|
+
* (stale `moxxy serve` leftovers — legitimate self-healing). Returns true
|
|
82
|
+
* when a kill was attempted. Anything else holding the port (the default,
|
|
83
|
+
* 4040, is also ngrok's local-UI port!) is left alone — the caller falls
|
|
84
|
+
* back to an ephemeral port instead. SIGTERM → grace → SIGKILL.
|
|
85
|
+
*/
|
|
86
|
+
export async function freeTcpPortIfMoxxy(port, logger, deps = realFreePortDeps) {
|
|
87
|
+
if (process.platform === 'win32')
|
|
88
|
+
return false;
|
|
89
|
+
const pids = (await deps.pidsListeningOn(port)).filter((pid) => pid !== process.pid);
|
|
90
|
+
if (pids.length === 0)
|
|
91
|
+
return false;
|
|
92
|
+
const holders = await Promise.all(pids.map(async (pid) => ({ pid, command: await deps.pidCommand(pid) })));
|
|
93
|
+
const foreign = holders.filter((h) => !looksLikeMoxxy(h.command));
|
|
94
|
+
if (foreign.length > 0) {
|
|
95
|
+
logger?.warn?.(`port ${port} is held by non-moxxy process(es); not killing them`, {
|
|
96
|
+
holders: foreign.map((h) => `${h.pid}: ${h.command || '<unknown command>'}`),
|
|
97
|
+
});
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
// Re-verify identity immediately before each signal. Between the `ps`
|
|
101
|
+
// snapshot above and the kill, a holder PID can exit and be reused by an
|
|
102
|
+
// unrelated process — so re-read its command and skip any whose name no
|
|
103
|
+
// longer looks like moxxy. Narrows (can't fully close — POSIX signalling
|
|
104
|
+
// is inherently racy) the TOCTOU on the identity gate.
|
|
105
|
+
let attempted = false;
|
|
106
|
+
for (const { pid } of holders) {
|
|
107
|
+
const command = await deps.pidCommand(pid);
|
|
108
|
+
if (!looksLikeMoxxy(command))
|
|
109
|
+
continue;
|
|
110
|
+
try {
|
|
111
|
+
// Log the exact command we judged moxxy-owned before signalling, so a
|
|
112
|
+
// mis-fire on a substring-collision process is auditable after the fact.
|
|
113
|
+
logger?.warn?.('freeing port: SIGTERM to apparent stale moxxy process', { pid, command });
|
|
114
|
+
deps.kill(pid, 'SIGTERM');
|
|
115
|
+
attempted = true;
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
/* may already be gone */
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (!attempted)
|
|
122
|
+
return false;
|
|
123
|
+
await new Promise((r) => setTimeout(r, deps.graceMs ?? 400));
|
|
124
|
+
for (const { pid } of holders) {
|
|
125
|
+
// Re-check identity again before escalating to SIGKILL.
|
|
126
|
+
try {
|
|
127
|
+
deps.kill(pid, 0);
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
continue; /* dead — nothing to escalate */
|
|
131
|
+
}
|
|
132
|
+
const command = await deps.pidCommand(pid);
|
|
133
|
+
if (!looksLikeMoxxy(command))
|
|
134
|
+
continue;
|
|
135
|
+
try {
|
|
136
|
+
logger?.warn?.('freeing port: SIGKILL to apparent stale moxxy process', { pid, command });
|
|
137
|
+
deps.kill(pid, 'SIGKILL');
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
/* dead */
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
/** Where `scripts/build-web.mjs` writes the browser bundle (relative to dist/channel.js). */
|
|
146
|
+
const PUBLIC_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), 'public');
|
|
147
|
+
/** The path the tunnel exposes us under (e.g. `/web`), or `''` for the root. */
|
|
148
|
+
export function basePathFromUrl(url) {
|
|
149
|
+
try {
|
|
150
|
+
const p = new URL(url).pathname;
|
|
151
|
+
return p === '/' ? '' : p.replace(/\/+$/, '');
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return '';
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/** Inject the base path into index.html so relative assets (`app.js`) resolve
|
|
158
|
+
* under it and the client builds the right WS URL — works at root or `/web`. */
|
|
159
|
+
export function injectBaseHtml(html, basePath) {
|
|
160
|
+
const inject = `<base href="${basePath}/" />` +
|
|
161
|
+
`<script>window.__MOXXY_BASE__=${JSON.stringify(basePath)}</script>`;
|
|
162
|
+
return html.replace('<!--moxxy:base-->', inject);
|
|
163
|
+
}
|
|
164
|
+
export class WebChannel {
|
|
165
|
+
name = 'web';
|
|
166
|
+
permissionResolver;
|
|
167
|
+
port;
|
|
168
|
+
host;
|
|
169
|
+
token;
|
|
170
|
+
logger;
|
|
171
|
+
getTunnel;
|
|
172
|
+
publishSurface;
|
|
173
|
+
publishControls;
|
|
174
|
+
server = null;
|
|
175
|
+
wss = null;
|
|
176
|
+
clients = new Set();
|
|
177
|
+
/**
|
|
178
|
+
* Built screens replayed to a newly-connected browser, keyed by logical
|
|
179
|
+
* `name`. UNNAMED views all share {@link UNNAMED_VIEW_KEY} (latest wins), so
|
|
180
|
+
* an agent that presents many unnamed views over a long session can't leak
|
|
181
|
+
* one ViewDoc per render. Named views LRU-evict at {@link MAX_REPLAY_VIEWS}.
|
|
182
|
+
*/
|
|
183
|
+
views = new Map();
|
|
184
|
+
unsubscribe = null;
|
|
185
|
+
session = null;
|
|
186
|
+
busy = false;
|
|
187
|
+
controller = null;
|
|
188
|
+
tunnel = null;
|
|
189
|
+
tunnelBase = null;
|
|
190
|
+
/** Public path the surface is served under (e.g. `/web` behind the relay; `''` local). */
|
|
191
|
+
basePath = '';
|
|
192
|
+
viewSeq = 0;
|
|
193
|
+
droppedFrames = 0;
|
|
194
|
+
lastDropWarnAt = 0;
|
|
195
|
+
constructor(opts = {}) {
|
|
196
|
+
this.port = opts.port ?? 4040;
|
|
197
|
+
this.host = opts.host ?? '127.0.0.1';
|
|
198
|
+
this.token = opts.authToken ?? randomBytes(16).toString('hex');
|
|
199
|
+
this.logger = opts.logger;
|
|
200
|
+
this.getTunnel = opts.getTunnel;
|
|
201
|
+
this.publishSurface = opts.publishSurface;
|
|
202
|
+
this.publishControls = opts.publishControls;
|
|
203
|
+
// The interactive surface is the gate; tools still need an upfront
|
|
204
|
+
// allow-list (no per-call clicker). Default to present_view + the read-only
|
|
205
|
+
// fetch tools so apps can pull REAL data out of the box. Extend via
|
|
206
|
+
// config.allowedTools. (When co-attached, the PRIMARY channel's resolver
|
|
207
|
+
// governs instead — e.g. the TUI prompts per tool.)
|
|
208
|
+
const allowed = opts.allowedTools && opts.allowedTools.length > 0
|
|
209
|
+
? [...opts.allowedTools]
|
|
210
|
+
: ['present_view', 'web_fetch', 'browser_session'];
|
|
211
|
+
this.permissionResolver = createAllowListResolver(allowed);
|
|
212
|
+
}
|
|
213
|
+
/** The local URL (token embedded). */
|
|
214
|
+
get url() {
|
|
215
|
+
return `http://${this.host}:${this.port}/?t=${this.token}`;
|
|
216
|
+
}
|
|
217
|
+
/** The URL to hand the user — the tunnel base if open, else local. */
|
|
218
|
+
get shareUrl() {
|
|
219
|
+
const base = this.tunnelBase ?? `http://${this.host}:${this.port}`;
|
|
220
|
+
return `${base}/?t=${this.token}`;
|
|
221
|
+
}
|
|
222
|
+
async start(startOpts) {
|
|
223
|
+
this.session = startOpts.session;
|
|
224
|
+
const projector = new EventProjector();
|
|
225
|
+
this.unsubscribe = startOpts.session.log.subscribe((event) => {
|
|
226
|
+
for (const frame of projector.project(event)) {
|
|
227
|
+
// Remember each screen so a browser that connects AFTER the agent built
|
|
228
|
+
// the app (the normal flow: build in TUI/Telegram → open the link) still
|
|
229
|
+
// sees it. Named screens replace in place; unnamed ones coalesce into a
|
|
230
|
+
// single latest-wins slot so the replay set stays bounded.
|
|
231
|
+
if (frame.kind === 'view')
|
|
232
|
+
this.rememberView(frame);
|
|
233
|
+
this.broadcast(frame);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
const server = createServer((req, res) => {
|
|
237
|
+
void this.handleHttp(req, res);
|
|
238
|
+
});
|
|
239
|
+
this.server = server;
|
|
240
|
+
// Bind FIRST: ws re-emits the http server's 'error' events on the
|
|
241
|
+
// WebSocketServer, so a WSS attached before a failed listen turns a
|
|
242
|
+
// recoverable EADDRINUSE into an unhandled 'error' → process crash.
|
|
243
|
+
await this.bindServerWithRetry(server);
|
|
244
|
+
// Validate the token at the handshake so a bad token is rejected with 401
|
|
245
|
+
// and the client never opens (the token is the only public-internet gate).
|
|
246
|
+
const wss = new WebSocketServer({
|
|
247
|
+
server,
|
|
248
|
+
// No fixed `path`: behind the relay the upgrade may arrive as `/ws` or
|
|
249
|
+
// `/<base>/ws`; verifyClient base-strips and checks it (plus the token).
|
|
250
|
+
// Frames past maxPayload are dropped at the socket layer (ws closes with
|
|
251
|
+
// 1009) instead of being buffered; onMessage applies a tighter cap.
|
|
252
|
+
maxPayload: 1024 * 1024,
|
|
253
|
+
verifyClient: (info) => {
|
|
254
|
+
const p = this.stripBase((info.req.url ?? '/').split('?')[0] ?? '/');
|
|
255
|
+
return p === '/ws' && this.validToken(info.req.url);
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
this.wss = wss;
|
|
259
|
+
wss.on('connection', (ws) => this.onConnection(ws));
|
|
260
|
+
// Never leave an EventEmitter 'error' unhandled — it would throw at the
|
|
261
|
+
// process level. Forwarded server errors after bind are log-and-survive.
|
|
262
|
+
wss.on('error', (err) => this.logger?.warn?.('web socket server error', { err: String(err) }));
|
|
263
|
+
await this.openTunnel();
|
|
264
|
+
this.publishSurface?.({ url: this.shareUrl, nextViewId: () => `v_srv_${++this.viewSeq}` });
|
|
265
|
+
this.publishControls?.({ retunnel: () => this.retunnel() });
|
|
266
|
+
const running = new Promise((resolve) => server.once('close', () => resolve()));
|
|
267
|
+
return { running, stop: () => this.stop() };
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Bind the HTTP server, with recovery if the port is already in use.
|
|
271
|
+
* A stale `moxxy serve` from a prior install often leaves 4040 bound
|
|
272
|
+
* even after its unix socket has been released — if (and only if) the
|
|
273
|
+
* holder is verifiably a moxxy process we kill it and retry. Anything
|
|
274
|
+
* else (ngrok's local UI also defaults to 4040) is never signalled;
|
|
275
|
+
* we bind an ephemeral port instead and log loudly which port the
|
|
276
|
+
* surface actually got (the share URL embeds the real port either way).
|
|
277
|
+
*/
|
|
278
|
+
async bindServerWithRetry(server) {
|
|
279
|
+
const tryListen = () => new Promise((resolve, reject) => {
|
|
280
|
+
const onError = (err) => {
|
|
281
|
+
server.off('listening', onListening);
|
|
282
|
+
reject(err);
|
|
283
|
+
};
|
|
284
|
+
const onListening = () => {
|
|
285
|
+
server.off('error', onError);
|
|
286
|
+
const addr = server.address();
|
|
287
|
+
if (addr && typeof addr === 'object')
|
|
288
|
+
this.port = addr.port;
|
|
289
|
+
this.logger?.info?.('web channel listening', { url: this.url });
|
|
290
|
+
resolve();
|
|
291
|
+
};
|
|
292
|
+
server.once('error', onError);
|
|
293
|
+
server.once('listening', onListening);
|
|
294
|
+
server.listen(this.port, this.host);
|
|
295
|
+
});
|
|
296
|
+
try {
|
|
297
|
+
await tryListen();
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
catch (err) {
|
|
301
|
+
if (!isAddrInUse(err))
|
|
302
|
+
throw err;
|
|
303
|
+
}
|
|
304
|
+
const requested = this.port;
|
|
305
|
+
const freed = await freeTcpPortIfMoxxy(requested, this.logger).catch(() => false);
|
|
306
|
+
if (freed) {
|
|
307
|
+
this.logger?.warn?.(`web channel port ${requested} was held by a stale moxxy process; freed it, retrying`);
|
|
308
|
+
try {
|
|
309
|
+
await tryListen();
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
catch (err) {
|
|
313
|
+
if (!isAddrInUse(err))
|
|
314
|
+
throw err;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// The holder is not ours to kill (or would not die) — take an
|
|
318
|
+
// ephemeral port. onListening reads back the real bound port, so
|
|
319
|
+
// this.url / shareUrl / the tunnel all carry it automatically.
|
|
320
|
+
this.port = 0;
|
|
321
|
+
await tryListen();
|
|
322
|
+
this.logger?.warn?.(`web channel port ${requested} was in use by another process; bound ephemeral port ${this.port} instead`, { requestedPort: requested, boundPort: this.port, url: this.url });
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* (Re-)open the tunnel via the active provider, closing any prior one FIRST so
|
|
326
|
+
* a switch never leaks a subprocess. Non-fatal: on failure (e.g. the relay
|
|
327
|
+
* not installed) we fall back to the local URL.
|
|
328
|
+
*/
|
|
329
|
+
async openTunnel() {
|
|
330
|
+
if (this.tunnel) {
|
|
331
|
+
try {
|
|
332
|
+
await this.tunnel.close();
|
|
333
|
+
}
|
|
334
|
+
catch {
|
|
335
|
+
/* ignore */
|
|
336
|
+
}
|
|
337
|
+
this.tunnel = null;
|
|
338
|
+
this.tunnelBase = null;
|
|
339
|
+
this.basePath = '';
|
|
340
|
+
}
|
|
341
|
+
const provider = this.getTunnel?.() ?? null;
|
|
342
|
+
if (!provider || provider.name === 'localhost')
|
|
343
|
+
return;
|
|
344
|
+
try {
|
|
345
|
+
// Route the preview under the `web` path so the relay can multiplex it
|
|
346
|
+
// alongside the mobile bridge under one uuid subdomain.
|
|
347
|
+
this.tunnel = await provider.open({ port: this.port, host: this.host, label: 'web' });
|
|
348
|
+
this.tunnelBase = this.tunnel.url;
|
|
349
|
+
this.basePath = basePathFromUrl(this.tunnel.url);
|
|
350
|
+
this.logger?.info?.('web surface tunnel open', { provider: provider.name, url: this.shareUrl });
|
|
351
|
+
}
|
|
352
|
+
catch (err) {
|
|
353
|
+
this.logger?.warn?.('web surface tunnel failed; using local URL', { provider: provider.name, err: String(err) });
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
/** Switch tunnels live (agent's web_set_tunnel) and republish the surface URL. */
|
|
357
|
+
async retunnel() {
|
|
358
|
+
await this.openTunnel();
|
|
359
|
+
this.publishSurface?.({ url: this.shareUrl, nextViewId: () => `v_srv_${++this.viewSeq}` });
|
|
360
|
+
return this.shareUrl;
|
|
361
|
+
}
|
|
362
|
+
async stop() {
|
|
363
|
+
this.publishSurface?.(null);
|
|
364
|
+
this.publishControls?.(null);
|
|
365
|
+
this.unsubscribe?.();
|
|
366
|
+
this.unsubscribe = null;
|
|
367
|
+
this.controller?.abort();
|
|
368
|
+
if (this.tunnel) {
|
|
369
|
+
try {
|
|
370
|
+
await this.tunnel.close();
|
|
371
|
+
}
|
|
372
|
+
catch {
|
|
373
|
+
/* ignore */
|
|
374
|
+
}
|
|
375
|
+
this.tunnel = null;
|
|
376
|
+
this.tunnelBase = null;
|
|
377
|
+
this.basePath = '';
|
|
378
|
+
}
|
|
379
|
+
for (const ws of this.clients) {
|
|
380
|
+
try {
|
|
381
|
+
ws.close();
|
|
382
|
+
}
|
|
383
|
+
catch {
|
|
384
|
+
/* ignore */
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
this.clients.clear();
|
|
388
|
+
await new Promise((resolve) => (this.wss ? this.wss.close(() => resolve()) : resolve()));
|
|
389
|
+
await new Promise((resolve) => (this.server ? this.server.close(() => resolve()) : resolve()));
|
|
390
|
+
}
|
|
391
|
+
validToken(reqUrl) {
|
|
392
|
+
try {
|
|
393
|
+
// Constant-time compare so the token isn't recoverable byte-by-byte via
|
|
394
|
+
// response timing (this is the only public-internet gate).
|
|
395
|
+
const presented = new URL(reqUrl ?? '/', 'http://localhost').searchParams.get('t');
|
|
396
|
+
return bearerTokenMatches(presented, this.token);
|
|
397
|
+
}
|
|
398
|
+
catch {
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
/** Strip the public base path (if any) so internal routing stays root-relative.
|
|
403
|
+
* Tolerant: handles both `/web/app.js` (relay didn't strip) and `/app.js`
|
|
404
|
+
* (relay stripped the first request line). */
|
|
405
|
+
stripBase(pathname) {
|
|
406
|
+
const b = this.basePath;
|
|
407
|
+
if (b && (pathname === b || pathname.startsWith(`${b}/`))) {
|
|
408
|
+
const rest = pathname.slice(b.length);
|
|
409
|
+
return rest === '' ? '/' : rest;
|
|
410
|
+
}
|
|
411
|
+
return pathname;
|
|
412
|
+
}
|
|
413
|
+
async handleHttp(req, res) {
|
|
414
|
+
const pathname = this.stripBase((req.url ?? '/').split('?')[0] ?? '/');
|
|
415
|
+
if (pathname === '/v1/health') {
|
|
416
|
+
res.writeHead(200, { 'content-type': 'application/json' });
|
|
417
|
+
res.end('{"status":"ok"}');
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
if (req.method === 'GET' && (pathname === '/' || pathname === '/index.html')) {
|
|
421
|
+
if (!this.validToken(req.url)) {
|
|
422
|
+
res.writeHead(401, { 'content-type': 'text/plain' });
|
|
423
|
+
res.end('unauthorized — open the tokenized URL the agent gave you');
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
await this.serveFile(res, 'index.html', 'text/html; charset=utf-8');
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
if (req.method === 'GET' && pathname === '/app.js') {
|
|
430
|
+
await this.serveFile(res, 'app.js', 'text/javascript; charset=utf-8');
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
res.writeHead(404, { 'content-type': 'text/plain' });
|
|
434
|
+
res.end('not found');
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Defense-in-depth headers for the internet-exposed surface (served over
|
|
438
|
+
* public tunnels). CSP contains any future renderer regression: scripts may
|
|
439
|
+
* only load from this origin (`'self'`) — no inline scripts — while the inline
|
|
440
|
+
* `<style>` block needs `style-src 'unsafe-inline'`; `connect-src` permits the
|
|
441
|
+
* same-origin WebSocket. `frame-ancestors 'none'` + `X-Frame-Options: DENY`
|
|
442
|
+
* stop the tokenized page from being framed (clickjacking); `Referrer-Policy:
|
|
443
|
+
* no-referrer` keeps the `?t` token out of the Referer header on agent-authored
|
|
444
|
+
* outbound links.
|
|
445
|
+
*/
|
|
446
|
+
securityHeaders() {
|
|
447
|
+
return {
|
|
448
|
+
'content-security-policy': "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; " +
|
|
449
|
+
"img-src 'self' data: https:; connect-src 'self' ws: wss:; " +
|
|
450
|
+
"base-uri 'none'; form-action 'none'; frame-ancestors 'none'",
|
|
451
|
+
'referrer-policy': 'no-referrer',
|
|
452
|
+
'x-content-type-options': 'nosniff',
|
|
453
|
+
'x-frame-options': 'DENY',
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
async serveFile(res, name, contentType) {
|
|
457
|
+
try {
|
|
458
|
+
let buf = await readFile(path.join(PUBLIC_DIR, name));
|
|
459
|
+
if (name === 'index.html') {
|
|
460
|
+
buf = Buffer.from(injectBaseHtml(buf.toString('utf8'), this.basePath), 'utf8');
|
|
461
|
+
}
|
|
462
|
+
res.writeHead(200, { 'content-type': contentType, ...this.securityHeaders() });
|
|
463
|
+
res.end(buf);
|
|
464
|
+
}
|
|
465
|
+
catch {
|
|
466
|
+
// Defense-in-depth headers are unconditional — they apply to the error
|
|
467
|
+
// response too, so a missing/unreadable bundle never serves a page
|
|
468
|
+
// without the clickjacking / referrer-token protections.
|
|
469
|
+
res.writeHead(500, { 'content-type': 'text/plain', ...this.securityHeaders() });
|
|
470
|
+
res.end('web surface bundle missing — run `pnpm --filter @moxxy/plugin-channel-web build`');
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Record a view frame for replay with a bounded footprint. Named views key
|
|
475
|
+
* by their name (re-render replaces in place) under an LRU bounded at
|
|
476
|
+
* {@link MAX_REPLAY_VIEWS}; unnamed views all collapse into a single
|
|
477
|
+
* latest-wins slot so an unbounded stream of unnamed renders can't leak.
|
|
478
|
+
*/
|
|
479
|
+
rememberView(frame) {
|
|
480
|
+
const key = frame.name ?? UNNAMED_VIEW_KEY;
|
|
481
|
+
// Re-insert at the tail to refresh LRU recency on re-render.
|
|
482
|
+
this.views.delete(key);
|
|
483
|
+
this.views.set(key, frame);
|
|
484
|
+
while (this.views.size > MAX_REPLAY_VIEWS) {
|
|
485
|
+
const oldest = this.views.keys().next().value;
|
|
486
|
+
if (oldest === undefined)
|
|
487
|
+
break;
|
|
488
|
+
this.views.delete(oldest);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
onConnection(ws) {
|
|
492
|
+
this.clients.add(ws);
|
|
493
|
+
ws.on('close', () => this.clients.delete(ws));
|
|
494
|
+
ws.on('message', (data) => this.onMessage(ws, data));
|
|
495
|
+
this.send(ws, { kind: 'hello' });
|
|
496
|
+
// Replay already-built screens so a browser opening the link AFTER the agent
|
|
497
|
+
// built the app sees it immediately (no "No view yet").
|
|
498
|
+
for (const frame of this.views.values())
|
|
499
|
+
this.send(ws, frame);
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Handle a browser → server frame. This is a trust boundary (tunnels put
|
|
503
|
+
* it on the public internet): every frame is schema-validated before any
|
|
504
|
+
* field access, and invalid ones are dropped — a thrown error in a ws
|
|
505
|
+
* 'message' listener escalates to a process-level uncaughtException.
|
|
506
|
+
*/
|
|
507
|
+
onMessage(ws, data) {
|
|
508
|
+
const raw = String(data);
|
|
509
|
+
if (raw.length > MAX_FRAME_BYTES) {
|
|
510
|
+
this.dropFrame('oversized frame');
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
let parsed;
|
|
514
|
+
try {
|
|
515
|
+
parsed = JSON.parse(raw);
|
|
516
|
+
}
|
|
517
|
+
catch {
|
|
518
|
+
this.dropFrame('invalid JSON');
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
const result = clientFrameSchema.safeParse(parsed);
|
|
522
|
+
if (!result.success) {
|
|
523
|
+
this.dropFrame('schema mismatch');
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
const frame = result.data;
|
|
527
|
+
if (frame.kind === 'prompt') {
|
|
528
|
+
if (frame.text.trim())
|
|
529
|
+
void this.drive(frame.text);
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
if (frame.kind === 'action') {
|
|
533
|
+
if (this.busy) {
|
|
534
|
+
this.send(ws, { kind: 'ack', actionId: frame.actionId, accepted: false, reason: 'busy' });
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
this.send(ws, { kind: 'ack', actionId: frame.actionId, accepted: true });
|
|
538
|
+
void this.drive(actionPrompt(frame.action, frame.formValues));
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
/** Count a dropped inbound frame; warn at most once per window (no log spam). */
|
|
542
|
+
dropFrame(reason) {
|
|
543
|
+
this.droppedFrames += 1;
|
|
544
|
+
const now = Date.now();
|
|
545
|
+
if (now - this.lastDropWarnAt < DROP_WARN_INTERVAL_MS)
|
|
546
|
+
return;
|
|
547
|
+
this.lastDropWarnAt = now;
|
|
548
|
+
this.logger?.warn?.('web channel dropped invalid client frame(s)', {
|
|
549
|
+
reason,
|
|
550
|
+
droppedTotal: this.droppedFrames,
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
async drive(prompt) {
|
|
554
|
+
if (!this.session || this.busy)
|
|
555
|
+
return;
|
|
556
|
+
this.busy = true;
|
|
557
|
+
this.controller = new AbortController();
|
|
558
|
+
try {
|
|
559
|
+
// Rendering happens via the log subscription; we only need to drain the
|
|
560
|
+
// iterator so the turn actually executes.
|
|
561
|
+
for await (const _event of this.session.runTurn(prompt, { signal: this.controller.signal })) {
|
|
562
|
+
void _event;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
catch (err) {
|
|
566
|
+
this.broadcast({ kind: 'status', turnId: '', phase: 'error', text: err instanceof Error ? err.message : String(err) });
|
|
567
|
+
}
|
|
568
|
+
finally {
|
|
569
|
+
this.busy = false;
|
|
570
|
+
this.controller = null;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
broadcast(frame) {
|
|
574
|
+
const s = JSON.stringify(frame);
|
|
575
|
+
for (const ws of this.clients) {
|
|
576
|
+
if (ws.readyState === ws.OPEN) {
|
|
577
|
+
try {
|
|
578
|
+
ws.send(s);
|
|
579
|
+
}
|
|
580
|
+
catch {
|
|
581
|
+
/* ignore */
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
send(ws, frame) {
|
|
587
|
+
try {
|
|
588
|
+
ws.send(JSON.stringify(frame));
|
|
589
|
+
}
|
|
590
|
+
catch {
|
|
591
|
+
/* ignore */
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
//# sourceMappingURL=channel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel.js","sourceRoot":"","sources":["../src/channel.ts"],"names":[],"mappings":"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"}
|