agent-yes 1.96.0 → 1.98.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.
@@ -0,0 +1,449 @@
1
+ function W() {
2
+ return crypto.randomUUID();
3
+ }
4
+ var E = 1e4,
5
+ T = 1e4;
6
+ class U {
7
+ opts;
8
+ peerId;
9
+ ws = null;
10
+ closed = !1;
11
+ reconnectDelay = 1000;
12
+ reconnectTimer = null;
13
+ heartbeat = null;
14
+ stableTimer = null;
15
+ openedAt = 0;
16
+ constructor(q) {
17
+ this.opts = q;
18
+ this.peerId = q.peerId ?? W();
19
+ }
20
+ connect() {
21
+ ((this.closed = !1), this.attachWakeListeners(), this.open());
22
+ }
23
+ onWake = () => {
24
+ if (this.closed) return;
25
+ let q = this.ws?.readyState;
26
+ if (q === 1) return;
27
+ if (q === 0) {
28
+ try {
29
+ this.ws?.close();
30
+ } catch {}
31
+ return;
32
+ }
33
+ if (this.reconnectTimer != null) (this.clearReconnectTimer(), this.open());
34
+ };
35
+ attachWakeListeners() {
36
+ globalThis.document?.addEventListener("visibilitychange", this.onWake);
37
+ let z = globalThis.window;
38
+ (z?.addEventListener("focus", this.onWake), z?.addEventListener("online", this.onWake));
39
+ }
40
+ detachWakeListeners() {
41
+ globalThis.document?.removeEventListener("visibilitychange", this.onWake);
42
+ let z = globalThis.window;
43
+ (z?.removeEventListener("focus", this.onWake), z?.removeEventListener("online", this.onWake));
44
+ }
45
+ roomUrl() {
46
+ return `${this.opts.url.replace(/\/+$/, "")}/room/${encodeURIComponent(this.opts.token)}`;
47
+ }
48
+ open() {
49
+ let q = new WebSocket(this.roomUrl());
50
+ this.ws = q;
51
+ let z = setTimeout(() => {
52
+ if (q.readyState === 0)
53
+ try {
54
+ q.close();
55
+ } catch {}
56
+ }, T);
57
+ ((q.onopen = () => {
58
+ (clearTimeout(z),
59
+ (this.openedAt = Date.now()),
60
+ this.clearStableTimer(),
61
+ (this.stableTimer = setTimeout(() => {
62
+ this.reconnectDelay = 1000;
63
+ }, E)));
64
+ let K = {
65
+ type: "hello",
66
+ role: this.opts.role,
67
+ peerId: this.peerId,
68
+ ...(this.opts.meta ? { meta: this.opts.meta } : {}),
69
+ };
70
+ (q.send(JSON.stringify(K)), this.startHeartbeat(), this.opts.onOpen?.());
71
+ }),
72
+ (q.onmessage = (K) => {
73
+ let Q;
74
+ try {
75
+ Q = JSON.parse(String(K.data));
76
+ } catch {
77
+ return;
78
+ }
79
+ if (Q.type === "peers") this.opts.onPeers?.(Q.peers);
80
+ else if (Q.type === "signal") this.opts.onSignal?.(Q.from, Q.data);
81
+ }),
82
+ (q.onclose = (K) => {
83
+ (clearTimeout(z), this.clearStableTimer(), this.stopHeartbeat());
84
+ let Q = this.openedAt ? Date.now() - this.openedAt : 0;
85
+ if (
86
+ ((this.openedAt = 0),
87
+ this.opts.onClose?.({ code: K?.code ?? 0, reason: K?.reason ?? "", ms: Q }),
88
+ !this.closed)
89
+ )
90
+ this.scheduleReconnect();
91
+ }),
92
+ (q.onerror = () => {
93
+ try {
94
+ q.close();
95
+ } catch {}
96
+ }));
97
+ }
98
+ startHeartbeat() {
99
+ (this.stopHeartbeat(),
100
+ (this.heartbeat = setInterval(() => {
101
+ try {
102
+ this.ws?.send(JSON.stringify({ type: "ping" }));
103
+ } catch {}
104
+ }, 1e4)));
105
+ }
106
+ stopHeartbeat() {
107
+ if (this.heartbeat != null) (clearInterval(this.heartbeat), (this.heartbeat = null));
108
+ }
109
+ clearStableTimer() {
110
+ if (this.stableTimer != null) (clearTimeout(this.stableTimer), (this.stableTimer = null));
111
+ }
112
+ scheduleReconnect() {
113
+ let q = this.reconnectDelay;
114
+ ((this.reconnectDelay = Math.min(q * 2, 15000)),
115
+ this.clearReconnectTimer(),
116
+ (this.reconnectTimer = setTimeout(() => {
117
+ if (((this.reconnectTimer = null), !this.closed)) this.open();
118
+ }, q)));
119
+ }
120
+ clearReconnectTimer() {
121
+ if (this.reconnectTimer != null)
122
+ (clearTimeout(this.reconnectTimer), (this.reconnectTimer = null));
123
+ }
124
+ sendSignal(q, z) {
125
+ let K = { type: "signal", to: q, data: z };
126
+ this.ws?.send(JSON.stringify(K));
127
+ }
128
+ updateMeta(q) {
129
+ if (((this.opts.meta = q), this.ws?.readyState === 1)) {
130
+ let z = { type: "meta", meta: q };
131
+ this.ws.send(JSON.stringify(z));
132
+ }
133
+ }
134
+ close() {
135
+ ((this.closed = !0),
136
+ this.detachWakeListeners(),
137
+ this.clearReconnectTimer(),
138
+ this.stopHeartbeat(),
139
+ this.clearStableTimer());
140
+ try {
141
+ this.ws?.close();
142
+ } catch {}
143
+ }
144
+ }
145
+ var A = ["stun:stun.l.google.com:19302", "stun:stun1.l.google.com:19302"],
146
+ _ = "codehost";
147
+ class B {
148
+ opts;
149
+ pc;
150
+ channel = null;
151
+ constructor(q) {
152
+ this.opts = q;
153
+ ((this.pc = new RTCPeerConnection({ iceServers: A.map((z) => ({ urls: z })) })),
154
+ (this.pc.onicecandidate = (z) => {
155
+ if (z.candidate)
156
+ this.opts.sendSignal({
157
+ kind: "candidate",
158
+ candidate: z.candidate.candidate,
159
+ mid: z.candidate.sdpMid ?? "0",
160
+ });
161
+ }),
162
+ (this.pc.onconnectionstatechange = () => {
163
+ this.opts.onState?.(this.pc.connectionState);
164
+ }));
165
+ }
166
+ async start() {
167
+ let q = this.pc.createDataChannel(_, { ordered: !0 });
168
+ ((q.binaryType = "arraybuffer"),
169
+ (this.channel = q),
170
+ (q.onopen = () => this.opts.onOpen?.(q)),
171
+ (q.onclose = () => this.opts.onClose?.()));
172
+ let z = await this.pc.createOffer();
173
+ (await this.pc.setLocalDescription(z),
174
+ this.opts.sendSignal({ kind: "offer", type: "offer", sdp: z.sdp ?? "" }));
175
+ }
176
+ async handleSignal(q) {
177
+ let z = q;
178
+ if (!z || typeof z !== "object") return;
179
+ if (z.kind === "answer") await this.pc.setRemoteDescription({ type: "answer", sdp: z.sdp });
180
+ else if (z.kind === "candidate")
181
+ try {
182
+ await this.pc.addIceCandidate({ candidate: z.candidate, sdpMid: z.mid });
183
+ } catch (K) {
184
+ console.error("[rtc] addIceCandidate failed:", K);
185
+ }
186
+ }
187
+ get dataChannel() {
188
+ return this.channel;
189
+ }
190
+ close() {
191
+ try {
192
+ this.channel?.close();
193
+ } catch {}
194
+ try {
195
+ this.pc.close();
196
+ } catch {}
197
+ }
198
+ }
199
+ var F = new TextEncoder(),
200
+ x = new TextDecoder();
201
+ function G(q, z, K) {
202
+ let Q = K?.byteLength ?? 0,
203
+ X = new Uint8Array(5 + Q);
204
+ if (((X[0] = q), new DataView(X.buffer).setUint32(1, z >>> 0, !1), K && Q)) X.set(K, 5);
205
+ return X;
206
+ }
207
+ function P(q, z, K) {
208
+ return G(q, z, F.encode(JSON.stringify(K)));
209
+ }
210
+ function J(q) {
211
+ let z = q instanceof Uint8Array ? q : new Uint8Array(q),
212
+ K = z[0],
213
+ Q = new DataView(z.buffer, z.byteOffset, z.byteLength).getUint32(1, !1),
214
+ X = z.subarray(5);
215
+ return { op: K, streamId: Q, payload: X };
216
+ }
217
+ function H(q) {
218
+ return JSON.parse(x.decode(q));
219
+ }
220
+ function L(q) {
221
+ return x.decode(q);
222
+ }
223
+ function* S(q) {
224
+ for (let z = 0; z < q.byteLength; z += 16379) yield q.slice(z, Math.min(z + 16379, q.byteLength));
225
+ }
226
+ function C(q) {
227
+ if (q.length === 1) return q[0];
228
+ let z = q.reduce((X, Z) => X + Z.byteLength, 0),
229
+ K = new Uint8Array(z),
230
+ Q = 0;
231
+ for (let X of q) (K.set(X, Q), (Q += X.byteLength));
232
+ return K;
233
+ }
234
+ function* M(q, z, K) {
235
+ let Q = 0;
236
+ while (K.byteLength - Q > 16379) (yield G(13, z, K.subarray(Q, Q + 16379)), (Q += 16379));
237
+ yield G(q, z, K.subarray(Q));
238
+ }
239
+ class k {
240
+ pending = new Map();
241
+ cont(q, z) {
242
+ let K = this.pending.get(q);
243
+ if (K) K.push(z.slice());
244
+ else this.pending.set(q, [z.slice()]);
245
+ }
246
+ finish(q, z) {
247
+ let K = this.pending.get(q);
248
+ if (!K) return z;
249
+ return (this.pending.delete(q), K.push(z), C(K));
250
+ }
251
+ drop(q) {
252
+ this.pending.delete(q);
253
+ }
254
+ }
255
+ class N {
256
+ channel;
257
+ nextStreamId = 1;
258
+ https = new Map();
259
+ wss = new Map();
260
+ wsRx = new k();
261
+ textEncoder = new TextEncoder();
262
+ constructor(q) {
263
+ this.channel = q;
264
+ ((q.binaryType = "arraybuffer"), q.addEventListener("message", (z) => this.onFrame(z.data)));
265
+ }
266
+ allocId() {
267
+ let q = this.nextStreamId;
268
+ return ((this.nextStreamId = (this.nextStreamId + 1) >>> 0 || 1), q);
269
+ }
270
+ onFrame(q) {
271
+ if (typeof q === "string") return;
272
+ let { op: z, streamId: K, payload: Q } = J(q);
273
+ switch (z) {
274
+ case 4:
275
+ this.https.get(K)?.onHead(H(Q));
276
+ break;
277
+ case 5:
278
+ this.https.get(K)?.onBody(Q.slice());
279
+ break;
280
+ case 6:
281
+ (this.https.get(K)?.onEnd(), this.https.delete(K));
282
+ break;
283
+ case 12: {
284
+ let X = this.https.get(K);
285
+ if (X) (X.onError(H(Q).message), this.https.delete(K));
286
+ break;
287
+ }
288
+ case 8: {
289
+ let X = H(Q);
290
+ this.wss.get(K)?.onOpenAck(X.ok, X.protocol);
291
+ break;
292
+ }
293
+ case 13:
294
+ this.wsRx.cont(K, Q);
295
+ break;
296
+ case 9:
297
+ this.wss.get(K)?.onText(L(this.wsRx.finish(K, Q)));
298
+ break;
299
+ case 10:
300
+ this.wss.get(K)?.onBin(this.wsRx.finish(K, Q).slice());
301
+ break;
302
+ case 11: {
303
+ let X = H(Q);
304
+ (this.wsRx.drop(K),
305
+ this.wss.get(K)?.onClose(X.code ?? 1000, X.reason ?? ""),
306
+ this.wss.delete(K));
307
+ break;
308
+ }
309
+ }
310
+ }
311
+ fetch(q, z, K, Q) {
312
+ let X = this.allocId();
313
+ return new Promise((Z, V) => {
314
+ let D = null,
315
+ $ = null,
316
+ j = new ReadableStream({
317
+ start: (Y) => {
318
+ $ = Y;
319
+ },
320
+ });
321
+ if (
322
+ (this.https.set(X, {
323
+ onHead: (Y) => {
324
+ ((D = Y),
325
+ Z(
326
+ new Response(j, {
327
+ status: Y.status === 204 || Y.status === 304 ? Y.status : Y.status,
328
+ statusText: Y.statusText,
329
+ headers: Y.headers,
330
+ }),
331
+ ));
332
+ },
333
+ onBody: (Y) => {
334
+ try {
335
+ $?.enqueue(Y);
336
+ } catch {}
337
+ },
338
+ onEnd: () => {
339
+ try {
340
+ $?.close();
341
+ } catch {}
342
+ if (!D) V(Error("stream ended before head"));
343
+ },
344
+ onError: (Y) => {
345
+ try {
346
+ $?.error(Error(Y));
347
+ } catch {}
348
+ if (!D) V(Error(Y));
349
+ },
350
+ }),
351
+ this.send(P(1, X, { method: q, path: z, headers: K })),
352
+ Q && Q.byteLength)
353
+ )
354
+ for (let Y of S(Q)) this.send(G(2, X, Y));
355
+ this.send(G(3, X));
356
+ });
357
+ }
358
+ openWs(q, z, K) {
359
+ let Q = this.allocId();
360
+ return (
361
+ this.wss.set(Q, K),
362
+ this.send(P(7, Q, { path: q, protocols: z })),
363
+ {
364
+ sendText: (X) => {
365
+ for (let Z of M(9, Q, this.textEncoder.encode(X))) this.send(Z);
366
+ },
367
+ sendBin: (X) => {
368
+ for (let Z of M(10, Q, X)) this.send(Z);
369
+ },
370
+ close: (X, Z) => {
371
+ (this.send(P(11, Q, { code: X, reason: Z })), this.wss.delete(Q));
372
+ },
373
+ }
374
+ );
375
+ }
376
+ send(q) {
377
+ if (this.channel.readyState === "open") {
378
+ let z = new Uint8Array(q.byteLength);
379
+ (z.set(q), this.channel.send(z.buffer));
380
+ }
381
+ }
382
+ get ready() {
383
+ return this.channel.readyState === "open";
384
+ }
385
+ }
386
+ var w = "wss://signal.codehost.dev";
387
+ class R {
388
+ peers = [];
389
+ signaling;
390
+ rtcs = new Map();
391
+ tunnels = new Map();
392
+ closed = !1;
393
+ constructor(q) {
394
+ ((this.signaling = new U({
395
+ url: q.signalUrl ?? w,
396
+ token: q.token,
397
+ role: "viewer",
398
+ onOpen: () => q.onStatus?.(!0),
399
+ onClose: () => q.onStatus?.(!1),
400
+ onPeers: (z) => {
401
+ ((this.peers = z.filter((K) => K.role === "server")), q.onPeers?.(this.peers));
402
+ },
403
+ onSignal: (z, K) => void this.rtcs.get(z)?.handleSignal(K),
404
+ })),
405
+ this.signaling.connect());
406
+ }
407
+ async fetch(q, z, K, Q = {}) {
408
+ let X = await this.dial(q),
409
+ Z = typeof Q.body === "string" ? new TextEncoder().encode(Q.body) : Q.body;
410
+ return X.fetch(z, K, Q.headers ?? {}, Z);
411
+ }
412
+ dial(q) {
413
+ let z = this.tunnels.get(q);
414
+ if (z) return z;
415
+ let K = () => {
416
+ (this.tunnels.delete(q), this.rtcs.get(q)?.close(), this.rtcs.delete(q));
417
+ },
418
+ Q = new Promise((X, Z) => {
419
+ let V = setTimeout(() => {
420
+ (K(), Z(Error("dial timed out")));
421
+ }, 15000),
422
+ D = new B({
423
+ sendSignal: ($) => this.signaling.sendSignal(q, $),
424
+ onOpen: ($) => {
425
+ (clearTimeout(V), X(new N($)));
426
+ },
427
+ onClose: K,
428
+ onState: ($) => {
429
+ if ($ === "failed" || $ === "disconnected") K();
430
+ },
431
+ });
432
+ (this.rtcs.set(q, D),
433
+ D.start().catch(($) => {
434
+ (clearTimeout(V), K(), Z($));
435
+ }));
436
+ });
437
+ return (this.tunnels.set(q, Q), Q.catch(() => this.tunnels.delete(q)), Q);
438
+ }
439
+ close() {
440
+ if (this.closed) return;
441
+ this.closed = !0;
442
+ for (let q of this.rtcs.values()) q.close();
443
+ (this.rtcs.clear(), this.tunnels.clear(), this.signaling.close());
444
+ }
445
+ }
446
+ function n(q) {
447
+ return new R(q);
448
+ }
449
+ export { n as joinRoom, w as DEFAULT_SIGNAL_URL, R as CodehostRoom };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-yes",
3
- "version": "1.96.0",
3
+ "version": "1.98.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",
@@ -54,7 +54,9 @@
54
54
  "scripts",
55
55
  "ts/*.ts",
56
56
  "!dist/**/*.map",
57
- "dist/**/*.js"
57
+ "dist/**/*.js",
58
+ "lab/ui/index.html",
59
+ "lab/ui/room-client.js"
58
60
  ],
59
61
  "type": "module",
60
62
  "module": "ts/index.ts",
package/ts/index.ts CHANGED
@@ -5,6 +5,7 @@ import path from "path";
5
5
  import DIE from "phpdie";
6
6
  import sflow from "sflow";
7
7
  import { XtermProxy } from "./xterm-proxy.ts";
8
+ import { agentYesHome } from "./agentYesHome.ts";
8
9
  import {
9
10
  extractSessionId,
10
11
  getSessionForCwd,
@@ -643,11 +644,26 @@ export default async function agentYes({
643
644
  return pendingExitCode.resolve(exitCode);
644
645
  });
645
646
 
647
+ // Record the agent's current PTY size to ~/.agent-yes/ptysize/<pid> so `ay serve`
648
+ // / the web console can render the existing buffer at the agent's real width
649
+ // before adapting. Mirrors the Rust runtime (rs/src/pty_spawner.rs).
650
+ const writeCurrentPtysize = (cols: number, rows: number) => {
651
+ const dir = path.join(agentYesHome(), "ptysize");
652
+ void mkdir(dir, { recursive: true })
653
+ .then(() => writeFile(path.join(dir, String(process.pid)), `${cols} ${rows}\n`))
654
+ .catch(() => null);
655
+ };
656
+ {
657
+ const { cols, rows } = getTerminalDimensions();
658
+ writeCurrentPtysize(cols, rows);
659
+ }
660
+
646
661
  // when current tty resized, resize both pty and xterm proxy
647
662
  process.stdout.on("resize", () => {
648
663
  const { cols, rows } = getTerminalDimensions();
649
664
  shell.resize(cols, rows);
650
665
  xtermProxy.resize(cols, rows);
666
+ writeCurrentPtysize(cols, rows);
651
667
  });
652
668
 
653
669
  const isStillWorkingQ = () => {