agent-yes 1.200.0 → 1.202.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/default.config.yaml +8 -8
- package/dist/{SUPPORTED_CLIS-Dt-oEBTl.js → SUPPORTED_CLIS-Cc4YcXL_.js} +2 -2
- package/dist/SUPPORTED_CLIS-ChdPabFX.js +9 -0
- package/dist/{agentShare-D-9ppbuL.js → agentShare-e4ErqbAZ.js} +2 -2
- package/dist/cli.js +4 -4
- package/dist/index.js +2 -2
- package/dist/{notifyDaemon-CQZ_wPhN.js → notifyDaemon-Bo-RiQ2N.js} +2 -2
- package/dist/{rustBinary-C5SrmIUM.js → rustBinary-Dd7-HOFy.js} +2 -2
- package/dist/{schedule-lPUt6BkE.js → schedule-Hu3krhNK.js} +4 -4
- package/dist/{serve-DlUJ7Hg9.js → serve-D4B43UQC.js} +12 -12
- package/dist/{setup-Dalhb8_0.js → setup-hUonzP6V.js} +2 -2
- package/dist/{subcommands-g1kLml5I.js → subcommands-DQmW41_L.js} +1 -1
- package/dist/{subcommands-DvXyUjWT.js → subcommands-DbiyHGKA.js} +53 -11
- package/dist/{ts-Oyau4Eap.js → ts--ZOQIaTr.js} +2 -2
- package/dist/{versionChecker-CeLrr-kQ.js → versionChecker-D2egG8Wk.js} +2 -2
- package/dist/{ws-DZDjlG3o.js → ws-CygS4IEs.js} +2 -2
- package/lab/ui/console-logic.js +32 -2
- package/lab/ui/index.html +99 -24
- package/lab/ui/qrcode.js +548 -565
- package/lab/ui/room-client.js +70 -91
- package/lab/ui/sw.js +17 -7
- package/package.json +7 -7
- package/scripts/build-rgui.ts +3 -1
- package/ts/agentShare.spec.ts +5 -3
- package/ts/badges.spec.ts +84 -1
- package/ts/badges.ts +69 -5
- package/ts/notifyDaemon.spec.ts +2 -8
- package/ts/notifyDaemon.ts +32 -33
- package/ts/notifyInbox.spec.ts +2 -1
- package/ts/notifyRouter.spec.ts +7 -11
- package/ts/notifyStore.spec.ts +4 -1
- package/ts/notifyStore.ts +18 -14
- package/ts/serve.spec.ts +4 -6
- package/ts/serve.ts +69 -17
- package/ts/subcommands.ts +46 -18
- package/ts/ws.spec.ts +31 -7
- package/ts/ws.ts +15 -4
- package/dist/SUPPORTED_CLIS-D8PoWxsA.js +0 -9
package/lab/ui/room-client.js
CHANGED
|
@@ -32,11 +32,9 @@ class SignalingClient {
|
|
|
32
32
|
this.open();
|
|
33
33
|
}
|
|
34
34
|
onWake = () => {
|
|
35
|
-
if (this.closed)
|
|
36
|
-
return;
|
|
35
|
+
if (this.closed) return;
|
|
37
36
|
const state = this.ws?.readyState;
|
|
38
|
-
if (state === 1)
|
|
39
|
-
return;
|
|
37
|
+
if (state === 1) return;
|
|
40
38
|
if (state === 0) {
|
|
41
39
|
try {
|
|
42
40
|
this.ws?.close();
|
|
@@ -92,7 +90,7 @@ class SignalingClient {
|
|
|
92
90
|
type: "hello",
|
|
93
91
|
role: this.opts.role,
|
|
94
92
|
peerId: this.peerId,
|
|
95
|
-
...this.opts.meta ? { meta: this.opts.meta } : {}
|
|
93
|
+
...(this.opts.meta ? { meta: this.opts.meta } : {}),
|
|
96
94
|
};
|
|
97
95
|
ws.send(JSON.stringify(hello));
|
|
98
96
|
this.startHeartbeat();
|
|
@@ -105,10 +103,8 @@ class SignalingClient {
|
|
|
105
103
|
} catch {
|
|
106
104
|
return;
|
|
107
105
|
}
|
|
108
|
-
if (msg.type === "peers")
|
|
109
|
-
|
|
110
|
-
else if (msg.type === "signal")
|
|
111
|
-
this.opts.onSignal?.(msg.from, msg.data);
|
|
106
|
+
if (msg.type === "peers") this.opts.onPeers?.(msg.peers, msg.now);
|
|
107
|
+
else if (msg.type === "signal") this.opts.onSignal?.(msg.from, msg.data);
|
|
112
108
|
};
|
|
113
109
|
ws.onclose = (ev) => {
|
|
114
110
|
clearTimeout(connectTimer);
|
|
@@ -117,8 +113,7 @@ class SignalingClient {
|
|
|
117
113
|
const ms = this.openedAt ? Date.now() - this.openedAt : 0;
|
|
118
114
|
this.openedAt = 0;
|
|
119
115
|
this.opts.onClose?.({ code: ev?.code ?? 0, reason: ev?.reason ?? "", ms });
|
|
120
|
-
if (!this.closed)
|
|
121
|
-
this.scheduleReconnect();
|
|
116
|
+
if (!this.closed) this.scheduleReconnect();
|
|
122
117
|
};
|
|
123
118
|
ws.onerror = () => {
|
|
124
119
|
try {
|
|
@@ -156,8 +151,7 @@ class SignalingClient {
|
|
|
156
151
|
this.clearReconnectTimer();
|
|
157
152
|
this.reconnectTimer = setTimeout(() => {
|
|
158
153
|
this.reconnectTimer = null;
|
|
159
|
-
if (this.closed)
|
|
160
|
-
return;
|
|
154
|
+
if (this.closed) return;
|
|
161
155
|
if (this.hidden()) {
|
|
162
156
|
this.dormant = true;
|
|
163
157
|
return;
|
|
@@ -196,10 +190,7 @@ class SignalingClient {
|
|
|
196
190
|
}
|
|
197
191
|
|
|
198
192
|
// src/shared/rtc.ts
|
|
199
|
-
var ICE_SERVERS = [
|
|
200
|
-
"stun:stun.l.google.com:19302",
|
|
201
|
-
"stun:stun1.l.google.com:19302"
|
|
202
|
-
];
|
|
193
|
+
var ICE_SERVERS = ["stun:stun.l.google.com:19302", "stun:stun1.l.google.com:19302"];
|
|
203
194
|
var CHANNEL_LABEL = "codehost";
|
|
204
195
|
var BULK_CHANNEL_LABEL = "codehost-bulk";
|
|
205
196
|
|
|
@@ -212,14 +203,14 @@ class RtcClient {
|
|
|
212
203
|
constructor(opts) {
|
|
213
204
|
this.opts = opts;
|
|
214
205
|
this.pc = new RTCPeerConnection({
|
|
215
|
-
iceServers: ICE_SERVERS.map((urls) => ({ urls }))
|
|
206
|
+
iceServers: ICE_SERVERS.map((urls) => ({ urls })),
|
|
216
207
|
});
|
|
217
208
|
this.pc.onicecandidate = (ev) => {
|
|
218
209
|
if (ev.candidate) {
|
|
219
210
|
this.opts.sendSignal({
|
|
220
211
|
kind: "candidate",
|
|
221
212
|
candidate: ev.candidate.candidate,
|
|
222
|
-
mid: ev.candidate.sdpMid ?? "0"
|
|
213
|
+
mid: ev.candidate.sdpMid ?? "0",
|
|
223
214
|
});
|
|
224
215
|
}
|
|
225
216
|
};
|
|
@@ -242,8 +233,7 @@ class RtcClient {
|
|
|
242
233
|
}
|
|
243
234
|
async handleSignal(data) {
|
|
244
235
|
const sig = data;
|
|
245
|
-
if (!sig || typeof sig !== "object")
|
|
246
|
-
return;
|
|
236
|
+
if (!sig || typeof sig !== "object") return;
|
|
247
237
|
if (sig.kind === "answer") {
|
|
248
238
|
await this.pc.setRemoteDescription({ type: "answer", sdp: sig.sdp });
|
|
249
239
|
} else if (sig.kind === "candidate") {
|
|
@@ -265,29 +255,29 @@ class RtcClient {
|
|
|
265
255
|
const stats = await this.pc.getStats();
|
|
266
256
|
let pairId = null;
|
|
267
257
|
stats.forEach((s) => {
|
|
268
|
-
if (s.type === "transport" && s.selectedCandidatePairId)
|
|
269
|
-
pairId = s.selectedCandidatePairId;
|
|
258
|
+
if (s.type === "transport" && s.selectedCandidatePairId) pairId = s.selectedCandidatePairId;
|
|
270
259
|
});
|
|
271
260
|
let pair = null;
|
|
272
261
|
stats.forEach((s) => {
|
|
273
|
-
if (
|
|
262
|
+
if (
|
|
263
|
+
pairId
|
|
264
|
+
? s.id === pairId
|
|
265
|
+
: s.type === "candidate-pair" && s.state === "succeeded" && s.nominated
|
|
266
|
+
) {
|
|
274
267
|
pair = s;
|
|
275
268
|
}
|
|
276
269
|
});
|
|
277
|
-
if (!pair)
|
|
278
|
-
return null;
|
|
270
|
+
if (!pair) return null;
|
|
279
271
|
const { localCandidateId, remoteCandidateId } = pair;
|
|
280
272
|
let lan = true;
|
|
281
273
|
let found = 0;
|
|
282
274
|
stats.forEach((s) => {
|
|
283
275
|
if (s.id === localCandidateId || s.id === remoteCandidateId) {
|
|
284
276
|
found++;
|
|
285
|
-
if (s.candidateType !== "host")
|
|
286
|
-
lan = false;
|
|
277
|
+
if (s.candidateType !== "host") lan = false;
|
|
287
278
|
}
|
|
288
279
|
});
|
|
289
|
-
if (found < 2)
|
|
290
|
-
return null;
|
|
280
|
+
if (found < 2) return null;
|
|
291
281
|
return lan ? "lan" : "p2p";
|
|
292
282
|
} catch {
|
|
293
283
|
return null;
|
|
@@ -310,15 +300,14 @@ class RtcClient {
|
|
|
310
300
|
var FRAME_HEADER = 5;
|
|
311
301
|
var MAX_FRAME = 64 * 1024;
|
|
312
302
|
var MAX_CHUNK = MAX_FRAME - FRAME_HEADER;
|
|
313
|
-
var enc = new TextEncoder;
|
|
314
|
-
var dec = new TextDecoder;
|
|
303
|
+
var enc = new TextEncoder();
|
|
304
|
+
var dec = new TextDecoder();
|
|
315
305
|
function encodeFrame(op, streamId, payload) {
|
|
316
306
|
const len = payload?.byteLength ?? 0;
|
|
317
307
|
const buf = new Uint8Array(5 + len);
|
|
318
308
|
buf[0] = op;
|
|
319
309
|
new DataView(buf.buffer).setUint32(1, streamId >>> 0, false);
|
|
320
|
-
if (payload && len)
|
|
321
|
-
buf.set(payload, 5);
|
|
310
|
+
if (payload && len) buf.set(payload, 5);
|
|
322
311
|
return buf;
|
|
323
312
|
}
|
|
324
313
|
function encodeJson(op, streamId, obj) {
|
|
@@ -338,13 +327,12 @@ function payloadText(payload) {
|
|
|
338
327
|
return dec.decode(payload);
|
|
339
328
|
}
|
|
340
329
|
function* chunk(body) {
|
|
341
|
-
for (let off = 0;off < body.byteLength; off += MAX_CHUNK) {
|
|
330
|
+
for (let off = 0; off < body.byteLength; off += MAX_CHUNK) {
|
|
342
331
|
yield body.slice(off, Math.min(off + MAX_CHUNK, body.byteLength));
|
|
343
332
|
}
|
|
344
333
|
}
|
|
345
334
|
function concatBytes(parts) {
|
|
346
|
-
if (parts.length === 1)
|
|
347
|
-
return parts[0];
|
|
335
|
+
if (parts.length === 1) return parts[0];
|
|
348
336
|
const total = parts.reduce((n, p) => n + p.byteLength, 0);
|
|
349
337
|
const out = new Uint8Array(total);
|
|
350
338
|
let off = 0;
|
|
@@ -364,18 +352,15 @@ function* wsMessageFrames(terminal, streamId, payload) {
|
|
|
364
352
|
}
|
|
365
353
|
|
|
366
354
|
class WsReassembler {
|
|
367
|
-
pending = new Map;
|
|
355
|
+
pending = new Map();
|
|
368
356
|
cont(streamId, payload) {
|
|
369
357
|
const buf = this.pending.get(streamId);
|
|
370
|
-
if (buf)
|
|
371
|
-
|
|
372
|
-
else
|
|
373
|
-
this.pending.set(streamId, [payload.slice()]);
|
|
358
|
+
if (buf) buf.push(payload.slice());
|
|
359
|
+
else this.pending.set(streamId, [payload.slice()]);
|
|
374
360
|
}
|
|
375
361
|
finish(streamId, payload) {
|
|
376
362
|
const buf = this.pending.get(streamId);
|
|
377
|
-
if (!buf)
|
|
378
|
-
return payload;
|
|
363
|
+
if (!buf) return payload;
|
|
379
364
|
this.pending.delete(streamId);
|
|
380
365
|
buf.push(payload);
|
|
381
366
|
return concatBytes(buf);
|
|
@@ -390,11 +375,11 @@ class TunnelClient {
|
|
|
390
375
|
transport;
|
|
391
376
|
bulk;
|
|
392
377
|
nextStreamId = 1;
|
|
393
|
-
https = new Map;
|
|
394
|
-
httpLane = new Map;
|
|
395
|
-
wss = new Map;
|
|
396
|
-
wsRx = new WsReassembler;
|
|
397
|
-
textEncoder = new TextEncoder;
|
|
378
|
+
https = new Map();
|
|
379
|
+
httpLane = new Map();
|
|
380
|
+
wss = new Map();
|
|
381
|
+
wsRx = new WsReassembler();
|
|
382
|
+
textEncoder = new TextEncoder();
|
|
398
383
|
constructor(transport, bulk = null) {
|
|
399
384
|
this.transport = transport;
|
|
400
385
|
this.bulk = bulk;
|
|
@@ -405,8 +390,7 @@ class TunnelClient {
|
|
|
405
390
|
}
|
|
406
391
|
failLane(lane, interactive) {
|
|
407
392
|
for (const [streamId, waiter] of [...this.https]) {
|
|
408
|
-
if ((this.httpLane.get(streamId) ?? this.transport) !== lane)
|
|
409
|
-
continue;
|
|
393
|
+
if ((this.httpLane.get(streamId) ?? this.transport) !== lane) continue;
|
|
410
394
|
this.https.delete(streamId);
|
|
411
395
|
this.httpLane.delete(streamId);
|
|
412
396
|
waiter.onError("tunnel closed");
|
|
@@ -421,7 +405,7 @@ class TunnelClient {
|
|
|
421
405
|
}
|
|
422
406
|
allocId() {
|
|
423
407
|
const id = this.nextStreamId;
|
|
424
|
-
this.nextStreamId = this.nextStreamId + 1 >>> 0 || 1;
|
|
408
|
+
this.nextStreamId = (this.nextStreamId + 1) >>> 0 || 1;
|
|
425
409
|
return id;
|
|
426
410
|
}
|
|
427
411
|
onFrame(data) {
|
|
@@ -478,9 +462,12 @@ class TunnelClient {
|
|
|
478
462
|
const stream = new ReadableStream({
|
|
479
463
|
start: (c) => {
|
|
480
464
|
controller = c;
|
|
481
|
-
}
|
|
465
|
+
},
|
|
482
466
|
});
|
|
483
|
-
const reqHeaders =
|
|
467
|
+
const reqHeaders =
|
|
468
|
+
typeof DecompressionStream !== "undefined"
|
|
469
|
+
? { ...headers, "x-codehost-accept-gzip": "1" }
|
|
470
|
+
: headers;
|
|
484
471
|
this.https.set(streamId, {
|
|
485
472
|
onHead: (h) => {
|
|
486
473
|
head = h;
|
|
@@ -491,11 +478,13 @@ class TunnelClient {
|
|
|
491
478
|
resHeaders.delete("content-encoding");
|
|
492
479
|
resHeaders.delete("content-length");
|
|
493
480
|
}
|
|
494
|
-
resolve(
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
481
|
+
resolve(
|
|
482
|
+
new Response(bodyStream, {
|
|
483
|
+
status: h.status === 204 || h.status === 304 ? h.status : h.status,
|
|
484
|
+
statusText: h.statusText,
|
|
485
|
+
headers: resHeaders,
|
|
486
|
+
}),
|
|
487
|
+
);
|
|
499
488
|
},
|
|
500
489
|
onBody: (b) => {
|
|
501
490
|
try {
|
|
@@ -506,20 +495,21 @@ class TunnelClient {
|
|
|
506
495
|
try {
|
|
507
496
|
controller?.close();
|
|
508
497
|
} catch {}
|
|
509
|
-
if (!head)
|
|
510
|
-
reject(new Error("stream ended before head"));
|
|
498
|
+
if (!head) reject(new Error("stream ended before head"));
|
|
511
499
|
},
|
|
512
500
|
onError: (msg) => {
|
|
513
501
|
try {
|
|
514
502
|
controller?.error(new Error(msg));
|
|
515
503
|
} catch {}
|
|
516
|
-
if (!head)
|
|
517
|
-
|
|
518
|
-
}
|
|
504
|
+
if (!head) reject(new Error(msg));
|
|
505
|
+
},
|
|
519
506
|
});
|
|
520
507
|
const lane = this.bulk?.isOpen() ? this.bulk : this.transport;
|
|
521
508
|
this.httpLane.set(streamId, lane);
|
|
522
|
-
this.sendOn(
|
|
509
|
+
this.sendOn(
|
|
510
|
+
lane,
|
|
511
|
+
encodeJson(1 /* HttpReq */, streamId, { method, path, headers: reqHeaders }),
|
|
512
|
+
);
|
|
523
513
|
if (body && body.byteLength) {
|
|
524
514
|
for (const part of chunk(body))
|
|
525
515
|
this.sendOn(lane, encodeFrame(2 /* HttpReqBody */, streamId, part));
|
|
@@ -537,21 +527,19 @@ class TunnelClient {
|
|
|
537
527
|
this.send(f);
|
|
538
528
|
},
|
|
539
529
|
sendBin: (data) => {
|
|
540
|
-
for (const f of wsMessageFrames(10 /* WsBin */, streamId, data))
|
|
541
|
-
this.send(f);
|
|
530
|
+
for (const f of wsMessageFrames(10 /* WsBin */, streamId, data)) this.send(f);
|
|
542
531
|
},
|
|
543
532
|
close: (code, reason) => {
|
|
544
533
|
this.send(encodeJson(11 /* WsClose */, streamId, { code, reason }));
|
|
545
534
|
this.wss.delete(streamId);
|
|
546
|
-
}
|
|
535
|
+
},
|
|
547
536
|
};
|
|
548
537
|
}
|
|
549
538
|
send(frame) {
|
|
550
539
|
this.sendOn(this.transport, frame);
|
|
551
540
|
}
|
|
552
541
|
sendOn(t, frame) {
|
|
553
|
-
if (t.isOpen())
|
|
554
|
-
t.send(frame);
|
|
542
|
+
if (t.isOpen()) t.send(frame);
|
|
555
543
|
}
|
|
556
544
|
get ready() {
|
|
557
545
|
return this.transport.isOpen();
|
|
@@ -575,14 +563,13 @@ function rtcDataChannelTransport(channel) {
|
|
|
575
563
|
},
|
|
576
564
|
onFrame(cb) {
|
|
577
565
|
channel.addEventListener("message", (ev) => {
|
|
578
|
-
if (typeof ev.data === "string")
|
|
579
|
-
return;
|
|
566
|
+
if (typeof ev.data === "string") return;
|
|
580
567
|
cb(new Uint8Array(ev.data));
|
|
581
568
|
});
|
|
582
569
|
},
|
|
583
570
|
onClose(cb) {
|
|
584
571
|
channel.addEventListener("close", cb);
|
|
585
|
-
}
|
|
572
|
+
},
|
|
586
573
|
};
|
|
587
574
|
}
|
|
588
575
|
|
|
@@ -600,9 +587,9 @@ var DIAL_FAIL_COOLDOWN_MS = 1e4;
|
|
|
600
587
|
class CodehostRoom {
|
|
601
588
|
peers = [];
|
|
602
589
|
signaling;
|
|
603
|
-
rtcs = new Map;
|
|
604
|
-
tunnels = new Map;
|
|
605
|
-
dialFailedAt = new Map;
|
|
590
|
+
rtcs = new Map();
|
|
591
|
+
tunnels = new Map();
|
|
592
|
+
dialFailedAt = new Map();
|
|
606
593
|
closed = false;
|
|
607
594
|
constructor(opts) {
|
|
608
595
|
this.signaling = new SignalingClient({
|
|
@@ -615,7 +602,7 @@ class CodehostRoom {
|
|
|
615
602
|
this.peers = peers.filter((p) => p.role === "server");
|
|
616
603
|
opts.onPeers?.(this.peers);
|
|
617
604
|
},
|
|
618
|
-
onSignal: (from, data) => void this.rtcs.get(from)?.handleSignal(data)
|
|
605
|
+
onSignal: (from, data) => void this.rtcs.get(from)?.handleSignal(data),
|
|
619
606
|
});
|
|
620
607
|
this.signaling.connect();
|
|
621
608
|
}
|
|
@@ -630,8 +617,7 @@ class CodehostRoom {
|
|
|
630
617
|
}
|
|
631
618
|
dial(peerId) {
|
|
632
619
|
const existing = this.tunnels.get(peerId);
|
|
633
|
-
if (existing)
|
|
634
|
-
return existing;
|
|
620
|
+
if (existing) return existing;
|
|
635
621
|
const failedAt = this.dialFailedAt.get(peerId);
|
|
636
622
|
if (failedAt != null && Date.now() - failedAt < DIAL_FAIL_COOLDOWN_MS) {
|
|
637
623
|
return Promise.reject(new Error("dial failed recently; cooling down"));
|
|
@@ -655,9 +641,8 @@ class CodehostRoom {
|
|
|
655
641
|
},
|
|
656
642
|
onClose: drop,
|
|
657
643
|
onState: (state) => {
|
|
658
|
-
if (state === "failed" || state === "disconnected")
|
|
659
|
-
|
|
660
|
-
}
|
|
644
|
+
if (state === "failed" || state === "disconnected") drop();
|
|
645
|
+
},
|
|
661
646
|
});
|
|
662
647
|
this.rtcs.set(peerId, rtc);
|
|
663
648
|
rtc.start().catch((err) => {
|
|
@@ -674,11 +659,9 @@ class CodehostRoom {
|
|
|
674
659
|
return dialing;
|
|
675
660
|
}
|
|
676
661
|
close() {
|
|
677
|
-
if (this.closed)
|
|
678
|
-
return;
|
|
662
|
+
if (this.closed) return;
|
|
679
663
|
this.closed = true;
|
|
680
|
-
for (const rtc of this.rtcs.values())
|
|
681
|
-
rtc.close();
|
|
664
|
+
for (const rtc of this.rtcs.values()) rtc.close();
|
|
682
665
|
this.rtcs.clear();
|
|
683
666
|
this.tunnels.clear();
|
|
684
667
|
this.signaling.close();
|
|
@@ -687,8 +670,4 @@ class CodehostRoom {
|
|
|
687
670
|
function joinRoom(opts) {
|
|
688
671
|
return new CodehostRoom(opts);
|
|
689
672
|
}
|
|
690
|
-
export {
|
|
691
|
-
joinRoom,
|
|
692
|
-
DEFAULT_SIGNAL_URL,
|
|
693
|
-
CodehostRoom
|
|
694
|
-
};
|
|
673
|
+
export { joinRoom, DEFAULT_SIGNAL_URL, CodehostRoom };
|
package/lab/ui/sw.js
CHANGED
|
@@ -13,7 +13,9 @@ const CACHE = "agent-yes-w-v3";
|
|
|
13
13
|
// request to the controlling page (which does), streaming the response back.
|
|
14
14
|
// Scope-relative so it works at both /w/ (agent-yes.com) and / (ay serve --http).
|
|
15
15
|
const BASE = new URL("./", self.location.href).pathname; // "/w/" or "/"
|
|
16
|
-
const PREVIEW = new RegExp(
|
|
16
|
+
const PREVIEW = new RegExp(
|
|
17
|
+
"^" + BASE.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "p/([^/]+)/(\\d{1,5})(/.*)?$",
|
|
18
|
+
);
|
|
17
19
|
|
|
18
20
|
async function pickClient() {
|
|
19
21
|
const all = await self.clients.matchAll({ type: "window", includeUncontrolled: true });
|
|
@@ -53,8 +55,10 @@ async function proxyPreview(request, src, port, rest, allowInject) {
|
|
|
53
55
|
mc.port1.onmessage = (e2) => {
|
|
54
56
|
const m2 = e2.data;
|
|
55
57
|
if (m2.type === "body") chunks.push(new Uint8Array(m2.chunk));
|
|
56
|
-
else if (m2.type === "end")
|
|
57
|
-
|
|
58
|
+
else if (m2.type === "end")
|
|
59
|
+
resolve(injectBootstrap(chunks, msg, headers, src, Number(port)));
|
|
60
|
+
else if (m2.type === "error")
|
|
61
|
+
resolve(new Response("preview error: " + m2.message, { status: 502 }));
|
|
58
62
|
};
|
|
59
63
|
return;
|
|
60
64
|
}
|
|
@@ -71,7 +75,15 @@ async function proxyPreview(request, src, port, rest, allowInject) {
|
|
|
71
75
|
resolve(new Response(stream, { status: msg.status, statusText: msg.statusText, headers }));
|
|
72
76
|
};
|
|
73
77
|
client.postMessage(
|
|
74
|
-
{
|
|
78
|
+
{
|
|
79
|
+
type: "ay-preview-fetch",
|
|
80
|
+
src,
|
|
81
|
+
port: Number(port),
|
|
82
|
+
method: request.method,
|
|
83
|
+
path: rest,
|
|
84
|
+
headers,
|
|
85
|
+
body,
|
|
86
|
+
},
|
|
75
87
|
[mc.port2, ...(body ? [body.buffer] : [])],
|
|
76
88
|
);
|
|
77
89
|
});
|
|
@@ -99,9 +111,7 @@ function injectBootstrap(chunks, msg, headers, src, port) {
|
|
|
99
111
|
JSON.stringify(port) +
|
|
100
112
|
");}catch(e){}})();</" +
|
|
101
113
|
"script>";
|
|
102
|
-
const html = /<head[^>]*>/i.test(raw)
|
|
103
|
-
? raw.replace(/<head[^>]*>/i, (m) => m + boot)
|
|
104
|
-
: boot + raw;
|
|
114
|
+
const html = /<head[^>]*>/i.test(raw) ? raw.replace(/<head[^>]*>/i, (m) => m + boot) : boot + raw;
|
|
105
115
|
headers.delete("content-security-policy");
|
|
106
116
|
headers.delete("content-security-policy-report-only");
|
|
107
117
|
headers.delete("content-length");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-yes",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.202.0",
|
|
4
4
|
"description": "A wrapper tool that automates interactions with various AI CLI tools by automatically handling common prompts and responses.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -33,7 +33,9 @@
|
|
|
33
33
|
"amp-yes": "./dist/amp-yes.js",
|
|
34
34
|
"auggie-yes": "./dist/auggie-yes.js",
|
|
35
35
|
"ay": "./dist/agent-yes.js",
|
|
36
|
+
"bash-yes": "./dist/bash-yes.js",
|
|
36
37
|
"claude-yes": "./dist/claude-yes.js",
|
|
38
|
+
"cmd-yes": "./dist/cmd-yes.js",
|
|
37
39
|
"codex-yes": "./dist/codex-yes.js",
|
|
38
40
|
"copilot-yes": "./dist/copilot-yes.js",
|
|
39
41
|
"cursor-yes": "./dist/cursor-yes.js",
|
|
@@ -45,10 +47,8 @@
|
|
|
45
47
|
"openrouter-yes": "./dist/openrouter-yes.js",
|
|
46
48
|
"orcy": "./dist/orcy.js",
|
|
47
49
|
"pi-yes": "./dist/pi-yes.js",
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"cmd-yes": "./dist/cmd-yes.js",
|
|
51
|
-
"powershell-yes": "./dist/powershell-yes.js"
|
|
50
|
+
"powershell-yes": "./dist/powershell-yes.js",
|
|
51
|
+
"qwen-yes": "./dist/qwen-yes.js"
|
|
52
52
|
},
|
|
53
53
|
"directories": {
|
|
54
54
|
"doc": "docs"
|
|
@@ -137,6 +137,7 @@
|
|
|
137
137
|
"@types/yargs": "^17.0.35",
|
|
138
138
|
"@typescript/native-preview": "^7.0.0-dev.20260124.1",
|
|
139
139
|
"@vitest/coverage-v8": "4.1.0",
|
|
140
|
+
"codehost": "^0.32.0",
|
|
140
141
|
"husky": "^9.1.7",
|
|
141
142
|
"lint-staged": "^16.2.7",
|
|
142
143
|
"node-pty": "^1.1.0",
|
|
@@ -150,8 +151,7 @@
|
|
|
150
151
|
"tsdown": "^0.20.3",
|
|
151
152
|
"vitest": "4.1.0",
|
|
152
153
|
"ws": "^8.20.0",
|
|
153
|
-
"zod": "^3.23.0"
|
|
154
|
-
"codehost": "^0.32.0"
|
|
154
|
+
"zod": "^3.23.0"
|
|
155
155
|
},
|
|
156
156
|
"peerDependencies": {
|
|
157
157
|
"node-pty": "latest",
|
package/scripts/build-rgui.ts
CHANGED
|
@@ -45,7 +45,9 @@ if (!existsSync(path.join(RGUI_DIR, "node_modules/d3-selection"))) {
|
|
|
45
45
|
await $`bun install --cwd ${RGUI_DIR}`;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
const outdir = process.argv[2]
|
|
48
|
+
const outdir = process.argv[2]
|
|
49
|
+
? path.resolve(process.argv[2])
|
|
50
|
+
: path.join(root, "lab/ui/rgui/dist");
|
|
49
51
|
await mkdir(outdir, { recursive: true });
|
|
50
52
|
|
|
51
53
|
const result = await Bun.build({
|
package/ts/agentShare.spec.ts
CHANGED
|
@@ -147,9 +147,11 @@ describe("scopedFetch — /api/ls/subscribe remove + teardown", () => {
|
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
it("forwards removes only for pids it forwarded (sibling removals stay hidden)", async () => {
|
|
150
|
-
const res = await scopedFetch(
|
|
151
|
-
|
|
152
|
-
|
|
150
|
+
const res = await scopedFetch(
|
|
151
|
+
SHARED,
|
|
152
|
+
removalInner(),
|
|
153
|
+
"r",
|
|
154
|
+
)(new Request("http://ay.local/api/ls/subscribe"));
|
|
153
155
|
const events = (await sse(res))
|
|
154
156
|
.split("\n\n")
|
|
155
157
|
.filter((e) => e.startsWith("data:"))
|
package/ts/badges.spec.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
badgeDef,
|
|
4
|
+
badgeLabel,
|
|
5
|
+
BADGE_DEFS,
|
|
6
|
+
matchBadges,
|
|
7
|
+
TYPING_BADGE,
|
|
8
|
+
type BadgeDef,
|
|
9
|
+
} from "./badges.ts";
|
|
3
10
|
|
|
4
11
|
describe("matchBadges", () => {
|
|
5
12
|
it("matches goal-active when the /goal status line is on screen", () => {
|
|
@@ -78,6 +85,61 @@ describe("matchBadges", () => {
|
|
|
78
85
|
});
|
|
79
86
|
});
|
|
80
87
|
|
|
88
|
+
describe("dynamic footer counters", () => {
|
|
89
|
+
// Real footer lines captured from live agents (`ay tail`), singular and plural.
|
|
90
|
+
it("matches the shell counter and carries the footer text on the wire", () => {
|
|
91
|
+
expect(
|
|
92
|
+
matchBadges([
|
|
93
|
+
" ⏸ manual mode on · 1 shell · ctrl+t to hide tasks · ← for agents · ↓ to manage",
|
|
94
|
+
]),
|
|
95
|
+
).toEqual(["shells:1 shell"]);
|
|
96
|
+
expect(matchBadges([" ⏸ manual mode on · 4 shells · ↓ to manage"])).toEqual([
|
|
97
|
+
"shells:4 shells",
|
|
98
|
+
]);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("matches the monitor counter", () => {
|
|
102
|
+
expect(
|
|
103
|
+
matchBadges([" ⏸ manual mode on · 3 monitors · esc to interrupt · ↓ to manage"]),
|
|
104
|
+
).toEqual(["monitors:3 monitors"]);
|
|
105
|
+
expect(matchBadges([" ⏸ manual mode on · 1 monitor · ↓ to manage"])).toEqual([
|
|
106
|
+
"monitors:1 monitor",
|
|
107
|
+
]);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("matches the background-agents counter (← N agents) at end of line", () => {
|
|
111
|
+
expect(matchBadges([" ⏸ manual mode on · ? for shortcuts · ← 3 agents"])).toEqual([
|
|
112
|
+
"bg-agents:3 agents",
|
|
113
|
+
]);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("does not light bg-agents on the plain '← for agents' hint (no count)", () => {
|
|
117
|
+
expect(matchBadges([" ⏸ manual mode on · ctrl+t to hide tasks · ← for agents"])).toEqual([]);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("matches the PR chip", () => {
|
|
121
|
+
expect(matchBadges([" ⏸ manual mode on · PR #310"])).toEqual(["pr:PR #310"]);
|
|
122
|
+
expect(matchBadges([" ⏸ manual mode on · PR #229 · esc to interrupt"])).toEqual([
|
|
123
|
+
"pr:PR #229",
|
|
124
|
+
]);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("is anchored to footer chrome — prose mentions don't match", () => {
|
|
128
|
+
expect(
|
|
129
|
+
matchBadges([
|
|
130
|
+
"I opened 4 shells and 2 monitors while checking PR #310 today",
|
|
131
|
+
"there are 3 agents running",
|
|
132
|
+
]),
|
|
133
|
+
).toEqual([]);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("can light several counters on one footer line", () => {
|
|
137
|
+
expect(
|
|
138
|
+
matchBadges([" ⏸ manual mode on · 2 shells · 3 monitors · PR #12 · ← 5 agents"]),
|
|
139
|
+
).toEqual(["shells:2 shells", "monitors:3 monitors", "bg-agents:5 agents", "pr:PR #12"]);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
81
143
|
describe("badgeDef", () => {
|
|
82
144
|
it("looks up a built-in definition by id", () => {
|
|
83
145
|
expect(badgeDef("goal-active")).toBe(BADGE_DEFS[0]);
|
|
@@ -87,12 +149,33 @@ describe("badgeDef", () => {
|
|
|
87
149
|
expect(badgeDef("does-not-exist")).toBeUndefined();
|
|
88
150
|
});
|
|
89
151
|
|
|
152
|
+
it("resolves a dynamic id by the part before the ':'", () => {
|
|
153
|
+
expect(badgeDef("shells:4 shells")?.id).toBe("shells");
|
|
154
|
+
});
|
|
155
|
+
|
|
90
156
|
it("resolves the time-derived typing badge even though it isn't in BADGE_DEFS", () => {
|
|
91
157
|
expect(badgeDef("typing")).toBe(TYPING_BADGE);
|
|
92
158
|
expect(BADGE_DEFS).not.toContain(TYPING_BADGE);
|
|
93
159
|
});
|
|
94
160
|
});
|
|
95
161
|
|
|
162
|
+
describe("badgeLabel", () => {
|
|
163
|
+
it("returns the static label for a plain id", () => {
|
|
164
|
+
expect(badgeLabel("goal-active")).toBe("goal");
|
|
165
|
+
expect(badgeLabel("typing")).toBe("typing");
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("substitutes the captured footer text into a dynamic label", () => {
|
|
169
|
+
expect(badgeLabel("shells:1 shell")).toBe("1 shell");
|
|
170
|
+
expect(badgeLabel("shells:4 shells")).toBe("4 shells");
|
|
171
|
+
expect(badgeLabel("pr:PR #310")).toBe("PR #310");
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("falls back to the raw id for an unknown badge", () => {
|
|
175
|
+
expect(badgeLabel("some-future-flag")).toBe("some-future-flag");
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
96
179
|
describe("TYPING_BADGE", () => {
|
|
97
180
|
it("is never produced by screen matching (its pattern can't match)", () => {
|
|
98
181
|
// Presence is derived from the stdin-activity marker, not the rendered
|