@pushary/agent-hooks 0.40.0 → 0.42.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/dist/bin/pushary-claude.js +392 -8
- package/dist/bin/pushary-codex-hook.js +2 -2
- package/dist/bin/pushary-codex.js +2 -2
- package/dist/bin/pushary-gemini-hook.js +2 -2
- package/dist/bin/pushary-hook.js +3 -3
- package/dist/bin/pushary-notification-hook.js +2 -2
- package/dist/bin/pushary-permission-denied-hook.js +3 -3
- package/dist/bin/pushary-permission-hook.js +3 -3
- package/dist/bin/pushary-post-hook.js +2 -2
- package/dist/bin/pushary-prompt-hook.js +2 -2
- package/dist/bin/pushary-session-end-hook.js +2 -2
- package/dist/bin/pushary-session-start-hook.js +2 -2
- package/dist/bin/pushary-setup.js +2 -2
- package/dist/bin/pushary-stop-hook.js +2 -2
- package/dist/bin/pushary-stopfailure-hook.js +2 -2
- package/dist/bin/pushary.js +3 -1
- package/dist/{chunk-AUEPQATK.js → chunk-ETDXSKR5.js} +4 -3
- package/dist/{chunk-V2WKECMG.js → chunk-NRCQ5UUR.js} +1 -1
- package/dist/{chunk-7JSFIVNA.js → chunk-V2TSLWTV.js} +2 -2
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +3 -3
- package/package.json +2 -2
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
getPolicy,
|
|
13
13
|
resolvePolicy,
|
|
14
14
|
waitForAnswer
|
|
15
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-ETDXSKR5.js";
|
|
16
16
|
import "../chunk-DWED7BS3.js";
|
|
17
17
|
import "../chunk-Z5PL3K7C.js";
|
|
18
18
|
import {
|
|
@@ -46,8 +46,17 @@ var findClaudeBinary = () => {
|
|
|
46
46
|
return null;
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
-
// src/wrapper/
|
|
49
|
+
// src/wrapper/spawnClaude.ts
|
|
50
50
|
import { spawn } from "child_process";
|
|
51
|
+
var needsShell = (binary, platform = process.platform) => platform === "win32" && /\.(cmd|bat)$/i.test(binary);
|
|
52
|
+
var spawnClaude = (binary, args2, options) => {
|
|
53
|
+
if (needsShell(binary)) {
|
|
54
|
+
return spawn(`"${binary}"`, args2, { ...options, shell: true });
|
|
55
|
+
}
|
|
56
|
+
return spawn(binary, args2, options);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// src/wrapper/localPassthrough.ts
|
|
51
60
|
var SIGNAL_NUMBERS = {
|
|
52
61
|
SIGHUP: 1,
|
|
53
62
|
SIGINT: 2,
|
|
@@ -60,7 +69,7 @@ var runLocalPassthrough = (binary, args2) => {
|
|
|
60
69
|
return new Promise((resolve) => {
|
|
61
70
|
let child;
|
|
62
71
|
try {
|
|
63
|
-
child =
|
|
72
|
+
child = spawnClaude(binary, args2, {
|
|
64
73
|
stdio: "inherit",
|
|
65
74
|
env: { ...process.env, [WRAPPER_ACTIVE_ENV]: "1" }
|
|
66
75
|
});
|
|
@@ -119,12 +128,14 @@ var BoundedInputQueue = class {
|
|
|
119
128
|
closed = false;
|
|
120
129
|
cap;
|
|
121
130
|
onDrop;
|
|
131
|
+
coalesce;
|
|
122
132
|
pushedCount = 0;
|
|
123
133
|
deliveredCount = 0;
|
|
124
134
|
droppedCount = 0;
|
|
125
135
|
constructor(options = {}) {
|
|
126
136
|
this.cap = Math.max(1, options.cap ?? 32);
|
|
127
137
|
this.onDrop = options.onDrop;
|
|
138
|
+
this.coalesce = options.coalesce;
|
|
128
139
|
}
|
|
129
140
|
push(item) {
|
|
130
141
|
if (this.closed) return;
|
|
@@ -179,6 +190,11 @@ var BoundedInputQueue = class {
|
|
|
179
190
|
return {
|
|
180
191
|
next: () => {
|
|
181
192
|
if (this.buffer.length > 0) {
|
|
193
|
+
if (this.coalesce && this.buffer.length > 1) {
|
|
194
|
+
const items = this.buffer.splice(0, this.buffer.length);
|
|
195
|
+
this.deliveredCount += items.length;
|
|
196
|
+
return Promise.resolve({ value: this.coalesce(items), done: false });
|
|
197
|
+
}
|
|
182
198
|
const value = this.buffer.shift();
|
|
183
199
|
this.deliveredCount++;
|
|
184
200
|
return Promise.resolve({ value, done: false });
|
|
@@ -383,13 +399,28 @@ var startCommandPoller = (opts) => {
|
|
|
383
399
|
if (stopped) return;
|
|
384
400
|
timer = setTimeout(tick, jitter(delayMs));
|
|
385
401
|
};
|
|
402
|
+
const fetchMode = async () => {
|
|
403
|
+
if (!opts.fetchModeState || !opts.onModeState) return;
|
|
404
|
+
try {
|
|
405
|
+
const state = await opts.fetchModeState(opts.apiKey, opts.getSessionId?.());
|
|
406
|
+
try {
|
|
407
|
+
opts.onModeState(state);
|
|
408
|
+
} catch {
|
|
409
|
+
}
|
|
410
|
+
} catch {
|
|
411
|
+
}
|
|
412
|
+
};
|
|
386
413
|
const tick = async () => {
|
|
387
414
|
if (stopped) return;
|
|
388
415
|
const controller = new AbortController();
|
|
389
416
|
inflight = controller;
|
|
390
417
|
const timeout = setTimeout(() => controller.abort(), requestTimeout);
|
|
391
418
|
try {
|
|
392
|
-
const
|
|
419
|
+
const wantCommands = opts.shouldDrainCommands ? opts.shouldDrainCommands() : true;
|
|
420
|
+
const [command] = await Promise.all([
|
|
421
|
+
wantCommands ? drain(opts.apiKey, opts.getSessionId?.(), controller.signal) : Promise.resolve(null),
|
|
422
|
+
fetchMode()
|
|
423
|
+
]);
|
|
393
424
|
errorStreak = 0;
|
|
394
425
|
if (command) {
|
|
395
426
|
idleTicks = 0;
|
|
@@ -420,6 +451,286 @@ var startCommandPoller = (opts) => {
|
|
|
420
451
|
};
|
|
421
452
|
};
|
|
422
453
|
|
|
454
|
+
// src/wrapper/wsProtocol.ts
|
|
455
|
+
var PROTOCOL_VERSION = 1;
|
|
456
|
+
var TRANSCRIPT_TEXT_MAX = 4e3;
|
|
457
|
+
var encodeFrame = (frame) => JSON.stringify(frame);
|
|
458
|
+
var helloFrame = (fields) => ({ v: PROTOCOL_VERSION, t: "hello", ...fields });
|
|
459
|
+
var transcriptFrame = (seq, kind, text, meta) => ({
|
|
460
|
+
v: PROTOCOL_VERSION,
|
|
461
|
+
t: "transcript",
|
|
462
|
+
seq,
|
|
463
|
+
kind,
|
|
464
|
+
...text !== void 0 ? { text: text.slice(0, TRANSCRIPT_TEXT_MAX) } : {},
|
|
465
|
+
...meta ? { meta } : {}
|
|
466
|
+
});
|
|
467
|
+
var parse = (raw) => {
|
|
468
|
+
try {
|
|
469
|
+
const value = JSON.parse(raw);
|
|
470
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
471
|
+
const obj = value;
|
|
472
|
+
if (obj.v !== PROTOCOL_VERSION) return null;
|
|
473
|
+
return obj;
|
|
474
|
+
} catch {
|
|
475
|
+
return null;
|
|
476
|
+
}
|
|
477
|
+
};
|
|
478
|
+
var isString = (v) => typeof v === "string";
|
|
479
|
+
var isFiniteNumber = (v) => typeof v === "number" && Number.isFinite(v);
|
|
480
|
+
var decodeServerFrame = (raw) => {
|
|
481
|
+
const f = parse(raw);
|
|
482
|
+
if (!f) return null;
|
|
483
|
+
switch (f.t) {
|
|
484
|
+
case "welcome":
|
|
485
|
+
return isFiniteNumber(f.resumeFromSeq) ? { v: PROTOCOL_VERSION, t: "welcome", resumeFromSeq: f.resumeFromSeq } : null;
|
|
486
|
+
case "ping":
|
|
487
|
+
return { v: PROTOCOL_VERSION, t: "ping" };
|
|
488
|
+
case "pong":
|
|
489
|
+
return { v: PROTOCOL_VERSION, t: "pong" };
|
|
490
|
+
case "command":
|
|
491
|
+
return isString(f.id) && isString(f.text) ? { v: PROTOCOL_VERSION, t: "command", id: f.id, text: f.text } : null;
|
|
492
|
+
case "resume":
|
|
493
|
+
return isFiniteNumber(f.afterSeq) ? { v: PROTOCOL_VERSION, t: "resume", afterSeq: f.afterSeq } : null;
|
|
494
|
+
case "error":
|
|
495
|
+
if (isString(f.code) && isString(f.message)) {
|
|
496
|
+
return { v: PROTOCOL_VERSION, t: "error", code: f.code, message: f.message, fatal: f.fatal === true };
|
|
497
|
+
}
|
|
498
|
+
return null;
|
|
499
|
+
default:
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
// src/wrapper/relayClient.ts
|
|
505
|
+
var DEFAULT_HEARTBEAT_MS = 2e4;
|
|
506
|
+
var DEFAULT_PONG_TIMEOUT_MS = 1e4;
|
|
507
|
+
var DEFAULT_MAX_OUTBOUND = 128;
|
|
508
|
+
var DEFAULT_MAX_SEEN_COMMANDS = 256;
|
|
509
|
+
var defaultBackoff = (attempt) => Math.min(1e3 * 2 ** attempt, 3e4);
|
|
510
|
+
var defaultSocketFactory = (url) => {
|
|
511
|
+
const WS = globalThis.WebSocket;
|
|
512
|
+
if (!WS) throw new Error("no WebSocket in this runtime");
|
|
513
|
+
const socket = new WS(url);
|
|
514
|
+
return {
|
|
515
|
+
send: (data) => socket.send(data),
|
|
516
|
+
close: () => socket.close(),
|
|
517
|
+
onOpen: (cb) => socket.addEventListener("open", () => cb()),
|
|
518
|
+
onMessage: (cb) => socket.addEventListener("message", (event) => cb(String(event.data))),
|
|
519
|
+
onClose: (cb) => socket.addEventListener("close", () => cb()),
|
|
520
|
+
onError: (cb) => socket.addEventListener("error", (event) => cb(event))
|
|
521
|
+
};
|
|
522
|
+
};
|
|
523
|
+
var createRelayClient = (options) => {
|
|
524
|
+
const factory = options.socketFactory ?? defaultSocketFactory;
|
|
525
|
+
const heartbeatMs = options.heartbeatMs ?? DEFAULT_HEARTBEAT_MS;
|
|
526
|
+
const pongTimeoutMs = options.pongTimeoutMs ?? DEFAULT_PONG_TIMEOUT_MS;
|
|
527
|
+
const backoff = options.backoffMs ?? defaultBackoff;
|
|
528
|
+
const maxOutbound = Math.max(1, options.maxOutbound ?? DEFAULT_MAX_OUTBOUND);
|
|
529
|
+
const maxSeenCommands = Math.max(1, options.maxSeenCommands ?? DEFAULT_MAX_SEEN_COMMANDS);
|
|
530
|
+
let socket;
|
|
531
|
+
let isConnected = false;
|
|
532
|
+
let stopped = false;
|
|
533
|
+
let seq = 0;
|
|
534
|
+
let attempt = 0;
|
|
535
|
+
let reconnectTimer;
|
|
536
|
+
let heartbeatTimer;
|
|
537
|
+
let pongTimer;
|
|
538
|
+
const outbound = [];
|
|
539
|
+
const seenCommands = [];
|
|
540
|
+
const seenSet = /* @__PURE__ */ new Set();
|
|
541
|
+
const log = (message) => options.log?.(message);
|
|
542
|
+
const clearTimers = () => {
|
|
543
|
+
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
544
|
+
if (pongTimer) clearTimeout(pongTimer);
|
|
545
|
+
if (reconnectTimer) clearTimeout(reconnectTimer);
|
|
546
|
+
heartbeatTimer = void 0;
|
|
547
|
+
pongTimer = void 0;
|
|
548
|
+
reconnectTimer = void 0;
|
|
549
|
+
};
|
|
550
|
+
const rememberCommand = (id) => {
|
|
551
|
+
if (seenSet.has(id)) return false;
|
|
552
|
+
seenSet.add(id);
|
|
553
|
+
seenCommands.push(id);
|
|
554
|
+
while (seenCommands.length > maxSeenCommands) {
|
|
555
|
+
const evicted = seenCommands.shift();
|
|
556
|
+
if (evicted) seenSet.delete(evicted);
|
|
557
|
+
}
|
|
558
|
+
return true;
|
|
559
|
+
};
|
|
560
|
+
const sendFrame = (frame) => {
|
|
561
|
+
if (!socket || !isConnected) return;
|
|
562
|
+
try {
|
|
563
|
+
socket.send(encodeFrame(frame));
|
|
564
|
+
} catch {
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
const enqueueTranscript = (frame) => {
|
|
568
|
+
if (isConnected) {
|
|
569
|
+
sendFrame(frame);
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
outbound.push(frame);
|
|
573
|
+
while (outbound.length > maxOutbound) outbound.shift();
|
|
574
|
+
};
|
|
575
|
+
const flushOutbound = () => {
|
|
576
|
+
while (outbound.length > 0 && isConnected) {
|
|
577
|
+
const frame = outbound.shift();
|
|
578
|
+
if (frame) sendFrame(frame);
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
const armPong = () => {
|
|
582
|
+
if (pongTimer) clearTimeout(pongTimer);
|
|
583
|
+
pongTimer = setTimeout(() => {
|
|
584
|
+
log("[pushary] relay heartbeat timed out; reconnecting");
|
|
585
|
+
dropAndReconnect();
|
|
586
|
+
}, pongTimeoutMs);
|
|
587
|
+
};
|
|
588
|
+
const startHeartbeat = () => {
|
|
589
|
+
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
590
|
+
heartbeatTimer = setInterval(() => {
|
|
591
|
+
sendFrame({ v: PROTOCOL_VERSION, t: "ping" });
|
|
592
|
+
armPong();
|
|
593
|
+
}, heartbeatMs);
|
|
594
|
+
};
|
|
595
|
+
const onServerFrame = (raw) => {
|
|
596
|
+
const frame = decodeServerFrame(raw);
|
|
597
|
+
if (!frame) return;
|
|
598
|
+
switch (frame.t) {
|
|
599
|
+
case "welcome":
|
|
600
|
+
attempt = 0;
|
|
601
|
+
flushOutbound();
|
|
602
|
+
log("[pushary] relay connected");
|
|
603
|
+
break;
|
|
604
|
+
case "command":
|
|
605
|
+
sendFrame({ v: PROTOCOL_VERSION, t: "ack", id: frame.id });
|
|
606
|
+
if (rememberCommand(frame.id)) {
|
|
607
|
+
try {
|
|
608
|
+
options.onCommand(frame.text);
|
|
609
|
+
} catch {
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
break;
|
|
613
|
+
case "ping":
|
|
614
|
+
sendFrame({ v: PROTOCOL_VERSION, t: "pong" });
|
|
615
|
+
break;
|
|
616
|
+
case "pong":
|
|
617
|
+
if (pongTimer) clearTimeout(pongTimer);
|
|
618
|
+
pongTimer = void 0;
|
|
619
|
+
break;
|
|
620
|
+
case "resume":
|
|
621
|
+
log(`[pushary] relay signalled a gap; catch up after seq ${frame.afterSeq}`);
|
|
622
|
+
break;
|
|
623
|
+
case "error":
|
|
624
|
+
if (frame.fatal) {
|
|
625
|
+
log(`[pushary] relay fatal: ${frame.message}`);
|
|
626
|
+
fatal(frame.message);
|
|
627
|
+
} else {
|
|
628
|
+
log(`[pushary] relay error: ${frame.message}`);
|
|
629
|
+
}
|
|
630
|
+
break;
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
const dropSocket = () => {
|
|
634
|
+
isConnected = false;
|
|
635
|
+
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
636
|
+
if (pongTimer) clearTimeout(pongTimer);
|
|
637
|
+
heartbeatTimer = void 0;
|
|
638
|
+
pongTimer = void 0;
|
|
639
|
+
if (socket) {
|
|
640
|
+
try {
|
|
641
|
+
socket.close();
|
|
642
|
+
} catch {
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
socket = void 0;
|
|
646
|
+
};
|
|
647
|
+
const scheduleReconnect = () => {
|
|
648
|
+
if (stopped) return;
|
|
649
|
+
if (reconnectTimer) return;
|
|
650
|
+
const delay = backoff(attempt);
|
|
651
|
+
attempt++;
|
|
652
|
+
const jittered = Math.max(1, Math.round(delay * (0.85 + Math.random() * 0.3)));
|
|
653
|
+
reconnectTimer = setTimeout(() => {
|
|
654
|
+
reconnectTimer = void 0;
|
|
655
|
+
connect();
|
|
656
|
+
}, jittered);
|
|
657
|
+
};
|
|
658
|
+
const dropAndReconnect = () => {
|
|
659
|
+
dropSocket();
|
|
660
|
+
scheduleReconnect();
|
|
661
|
+
};
|
|
662
|
+
const fatal = (message) => {
|
|
663
|
+
stopped = true;
|
|
664
|
+
clearTimers();
|
|
665
|
+
dropSocket();
|
|
666
|
+
options.onFatal?.(message);
|
|
667
|
+
};
|
|
668
|
+
const connect = () => {
|
|
669
|
+
if (stopped) return;
|
|
670
|
+
let created;
|
|
671
|
+
try {
|
|
672
|
+
created = factory(options.url);
|
|
673
|
+
} catch (error) {
|
|
674
|
+
fatal(error instanceof Error ? error.message : String(error));
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
socket = created;
|
|
678
|
+
created.onOpen(() => {
|
|
679
|
+
isConnected = true;
|
|
680
|
+
sendFrame(
|
|
681
|
+
helloFrame({
|
|
682
|
+
apiKey: options.apiKey,
|
|
683
|
+
machineId: options.machineId,
|
|
684
|
+
agentType: options.agentType,
|
|
685
|
+
sessionId: options.getSessionId(),
|
|
686
|
+
lastSeq: seq
|
|
687
|
+
})
|
|
688
|
+
);
|
|
689
|
+
startHeartbeat();
|
|
690
|
+
});
|
|
691
|
+
created.onMessage((data) => onServerFrame(data));
|
|
692
|
+
created.onClose(() => {
|
|
693
|
+
if (stopped) return;
|
|
694
|
+
dropAndReconnect();
|
|
695
|
+
});
|
|
696
|
+
created.onError(() => {
|
|
697
|
+
});
|
|
698
|
+
};
|
|
699
|
+
return {
|
|
700
|
+
start() {
|
|
701
|
+
if (stopped) return;
|
|
702
|
+
connect();
|
|
703
|
+
},
|
|
704
|
+
connected() {
|
|
705
|
+
return isConnected;
|
|
706
|
+
},
|
|
707
|
+
sendTranscript(kind, text, meta) {
|
|
708
|
+
if (stopped) return;
|
|
709
|
+
seq++;
|
|
710
|
+
enqueueTranscript(transcriptFrame(seq, kind, text, meta));
|
|
711
|
+
},
|
|
712
|
+
reannounce() {
|
|
713
|
+
if (stopped || !isConnected) return;
|
|
714
|
+
sendFrame(
|
|
715
|
+
helloFrame({
|
|
716
|
+
apiKey: options.apiKey,
|
|
717
|
+
machineId: options.machineId,
|
|
718
|
+
agentType: options.agentType,
|
|
719
|
+
sessionId: options.getSessionId(),
|
|
720
|
+
lastSeq: seq
|
|
721
|
+
})
|
|
722
|
+
);
|
|
723
|
+
},
|
|
724
|
+
stop() {
|
|
725
|
+
if (stopped) return;
|
|
726
|
+
stopped = true;
|
|
727
|
+
sendFrame({ v: PROTOCOL_VERSION, t: "bye" });
|
|
728
|
+
clearTimers();
|
|
729
|
+
dropSocket();
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
};
|
|
733
|
+
|
|
423
734
|
// src/wrapper/remoteLoop.ts
|
|
424
735
|
var errMsg = (err) => err instanceof Error ? err.message : String(err);
|
|
425
736
|
var DEFAULT_MAX_RESTARTS = 5;
|
|
@@ -457,6 +768,7 @@ var runRemoteLoop = async (deps) => {
|
|
|
457
768
|
else deps.signal.addEventListener("abort", onAbort, { once: true });
|
|
458
769
|
try {
|
|
459
770
|
for await (const message of query) {
|
|
771
|
+
deps.onMessage?.(message);
|
|
460
772
|
if (message.type === "system" && message.subtype === "init" && typeof message.session_id === "string") {
|
|
461
773
|
resumeId = message.session_id;
|
|
462
774
|
deps.onSessionId?.(resumeId);
|
|
@@ -490,6 +802,10 @@ var resolveClaudeArgs = (argv) => {
|
|
|
490
802
|
const rest = argv.slice(2);
|
|
491
803
|
return rest[0] === "claude" ? rest.slice(1) : rest;
|
|
492
804
|
};
|
|
805
|
+
var extractRemoteFlag = (args2) => ({
|
|
806
|
+
remote: args2.includes("--remote"),
|
|
807
|
+
rest: args2.filter((arg) => arg !== "--remote")
|
|
808
|
+
});
|
|
493
809
|
var flagValue = (args2, index) => {
|
|
494
810
|
const inline = args2[index];
|
|
495
811
|
const eq = inline?.indexOf("=") ?? -1;
|
|
@@ -516,6 +832,27 @@ var parseRemoteArgs = (args2) => {
|
|
|
516
832
|
|
|
517
833
|
// src/wrapper/remoteMode.ts
|
|
518
834
|
var INPUT_QUEUE_CAP = 32;
|
|
835
|
+
var extractAssistantText = (message) => {
|
|
836
|
+
const content = message.message?.content;
|
|
837
|
+
if (typeof content === "string") return content || void 0;
|
|
838
|
+
if (Array.isArray(content)) {
|
|
839
|
+
const text = content.filter((block) => !!block && typeof block === "object").filter((block) => block.type === "text" && typeof block.text === "string").map((block) => block.text).join("");
|
|
840
|
+
return text || void 0;
|
|
841
|
+
}
|
|
842
|
+
return void 0;
|
|
843
|
+
};
|
|
844
|
+
var streamTranscript = (relay, message) => {
|
|
845
|
+
if (message.type === "assistant") {
|
|
846
|
+
const text = extractAssistantText(message);
|
|
847
|
+
if (text) relay.sendTranscript("assistant", text);
|
|
848
|
+
} else if (message.type === "result") {
|
|
849
|
+
relay.sendTranscript(
|
|
850
|
+
"turn_end",
|
|
851
|
+
void 0,
|
|
852
|
+
typeof message.total_cost_usd === "number" ? { cost: message.total_cost_usd } : void 0
|
|
853
|
+
);
|
|
854
|
+
}
|
|
855
|
+
};
|
|
519
856
|
var runRemoteMode = async (_binary, args2) => {
|
|
520
857
|
let apiKey;
|
|
521
858
|
try {
|
|
@@ -537,10 +874,14 @@ var runRemoteMode = async (_binary, args2) => {
|
|
|
537
874
|
const machineId = getMachineId();
|
|
538
875
|
const controller = new AbortController();
|
|
539
876
|
let sessionId;
|
|
877
|
+
let killed = false;
|
|
878
|
+
let totalCostUsd = 0;
|
|
540
879
|
const input = new BoundedInputQueue({
|
|
541
880
|
cap: INPUT_QUEUE_CAP,
|
|
542
881
|
onDrop: (_dropped, total) => process.stderr.write(`[pushary] input queue full; dropped ${total} stale instruction(s)
|
|
543
|
-
`)
|
|
882
|
+
`),
|
|
883
|
+
// Several instructions that piled up between turns are sent as one turn.
|
|
884
|
+
coalesce: (items) => userMessage(items.map((m) => m.message.content).join("\n\n"))
|
|
544
885
|
});
|
|
545
886
|
const approver = createRemoteApprover({
|
|
546
887
|
apiKey,
|
|
@@ -548,10 +889,41 @@ var runRemoteMode = async (_binary, args2) => {
|
|
|
548
889
|
projectName,
|
|
549
890
|
getSessionId: () => sessionId
|
|
550
891
|
});
|
|
892
|
+
const initialMode = await fetchModeState(apiKey).catch(() => null);
|
|
893
|
+
let relay;
|
|
894
|
+
if (initialMode?.relayUrl) {
|
|
895
|
+
relay = createRelayClient({
|
|
896
|
+
url: initialMode.relayUrl,
|
|
897
|
+
apiKey,
|
|
898
|
+
machineId,
|
|
899
|
+
agentType: "claude_code",
|
|
900
|
+
getSessionId: () => sessionId,
|
|
901
|
+
onCommand: (command) => input.push(userMessage(command)),
|
|
902
|
+
onFatal: (message) => process.stderr.write(`[pushary] relay unavailable (${message}); using polling
|
|
903
|
+
`),
|
|
904
|
+
log: (message) => process.stderr.write(`${message}
|
|
905
|
+
`)
|
|
906
|
+
});
|
|
907
|
+
relay.start();
|
|
908
|
+
}
|
|
551
909
|
const poller = startCommandPoller({
|
|
552
910
|
apiKey,
|
|
553
911
|
getSessionId: () => sessionId,
|
|
554
912
|
onCommand: (command) => input.push(userMessage(command)),
|
|
913
|
+
// While the relay socket owns command delivery, the poller must not also drain
|
|
914
|
+
// the single-use queue (that would deliver the command twice); it still reads
|
|
915
|
+
// mode/kill. When the socket is down or absent, the poller is the source.
|
|
916
|
+
shouldDrainCommands: () => !(relay?.connected() ?? false),
|
|
917
|
+
// Watch the kill switch: a phone "stop" interrupts the running turn (via the
|
|
918
|
+
// controller abort below), instead of only denying the next tool call.
|
|
919
|
+
fetchModeState,
|
|
920
|
+
onModeState: (state) => {
|
|
921
|
+
if (state.kill && !killed) {
|
|
922
|
+
killed = true;
|
|
923
|
+
process.stderr.write("[pushary] halted from Pushary \u2014 stopping the agent\n");
|
|
924
|
+
controller.abort();
|
|
925
|
+
}
|
|
926
|
+
},
|
|
555
927
|
log: (message) => process.stderr.write(`${message}
|
|
556
928
|
`)
|
|
557
929
|
});
|
|
@@ -574,16 +946,27 @@ var runRemoteMode = async (_binary, args2) => {
|
|
|
574
946
|
cwd: process.cwd(),
|
|
575
947
|
onSessionId: (id) => {
|
|
576
948
|
sessionId = id;
|
|
949
|
+
relay?.reannounce();
|
|
577
950
|
},
|
|
951
|
+
onCost: (usd) => {
|
|
952
|
+
totalCostUsd = usd;
|
|
953
|
+
},
|
|
954
|
+
// Stream a bounded live transcript to the phone (relay presence-gates it).
|
|
955
|
+
onMessage: relay ? (message) => streamTranscript(relay, message) : void 0,
|
|
578
956
|
log: (message) => process.stderr.write(`${message}
|
|
579
957
|
`)
|
|
580
958
|
});
|
|
581
959
|
return { implemented: true, exitCode: result.exitCode };
|
|
582
960
|
} finally {
|
|
583
961
|
for (const signal of signals) process.off(signal, onSignal);
|
|
962
|
+
relay?.stop();
|
|
584
963
|
poller.stop();
|
|
585
964
|
approver.teardown();
|
|
586
965
|
input.close();
|
|
966
|
+
if (totalCostUsd > 0) {
|
|
967
|
+
process.stderr.write(`[pushary] remote session cost: $${totalCostUsd.toFixed(4)}
|
|
968
|
+
`);
|
|
969
|
+
}
|
|
587
970
|
}
|
|
588
971
|
};
|
|
589
972
|
|
|
@@ -597,14 +980,15 @@ var runClaudeWrapper = async (args2) => {
|
|
|
597
980
|
return 127;
|
|
598
981
|
}
|
|
599
982
|
const nested = process.env[WRAPPER_ACTIVE_ENV] === "1";
|
|
600
|
-
|
|
983
|
+
const { remote: wantRemote, rest } = extractRemoteFlag(args2);
|
|
984
|
+
if (!nested && wantRemote) {
|
|
601
985
|
try {
|
|
602
|
-
const remote = await runRemoteMode(binary,
|
|
986
|
+
const remote = await runRemoteMode(binary, rest);
|
|
603
987
|
if (remote.implemented) return remote.exitCode ?? 0;
|
|
604
988
|
} catch {
|
|
605
989
|
}
|
|
606
990
|
}
|
|
607
|
-
return runLocalPassthrough(binary,
|
|
991
|
+
return runLocalPassthrough(binary, rest);
|
|
608
992
|
};
|
|
609
993
|
|
|
610
994
|
// bin/pushary-claude.ts
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
savePendingQuestion,
|
|
21
21
|
toCodexWire,
|
|
22
22
|
toPolicyLookup
|
|
23
|
-
} from "../chunk-
|
|
23
|
+
} from "../chunk-NRCQ5UUR.js";
|
|
24
24
|
import {
|
|
25
25
|
askUser,
|
|
26
26
|
cancelQuestion,
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
resolvePolicy,
|
|
35
35
|
sendNotification,
|
|
36
36
|
waitForAnswer
|
|
37
|
-
} from "../chunk-
|
|
37
|
+
} from "../chunk-ETDXSKR5.js";
|
|
38
38
|
import {
|
|
39
39
|
isGatingMoment,
|
|
40
40
|
recordKeylessMoment
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
reportEvent
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-NRCQ5UUR.js";
|
|
5
5
|
import {
|
|
6
6
|
askUser,
|
|
7
7
|
getMachineId,
|
|
8
8
|
waitForAnswer
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-ETDXSKR5.js";
|
|
10
10
|
import "../chunk-DWED7BS3.js";
|
|
11
11
|
import "../chunk-Z5PL3K7C.js";
|
|
12
12
|
import {
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
readLastUserPrompt,
|
|
13
13
|
reportEvent,
|
|
14
14
|
savePendingQuestion
|
|
15
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-NRCQ5UUR.js";
|
|
16
16
|
import {
|
|
17
17
|
askUser,
|
|
18
18
|
cancelQuestion,
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
resolvePolicy,
|
|
27
27
|
sendNotification,
|
|
28
28
|
waitForAnswer
|
|
29
|
-
} from "../chunk-
|
|
29
|
+
} from "../chunk-ETDXSKR5.js";
|
|
30
30
|
import {
|
|
31
31
|
isGatingMoment,
|
|
32
32
|
recordKeylessMoment
|
package/dist/bin/pushary-hook.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handlePreToolUse
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-V2TSLWTV.js";
|
|
5
5
|
import "../chunk-7EW3USQF.js";
|
|
6
6
|
import "../chunk-KQYIHZ5E.js";
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-NRCQ5UUR.js";
|
|
8
|
+
import "../chunk-ETDXSKR5.js";
|
|
9
9
|
import "../chunk-R5AJNXZS.js";
|
|
10
10
|
import "../chunk-DWED7BS3.js";
|
|
11
11
|
import "../chunk-Z5PL3K7C.js";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handleNotification
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-NRCQ5UUR.js";
|
|
5
|
+
import "../chunk-ETDXSKR5.js";
|
|
6
6
|
import "../chunk-DWED7BS3.js";
|
|
7
7
|
import "../chunk-Z5PL3K7C.js";
|
|
8
8
|
import "../chunk-NKXSILEW.js";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handlePermissionDenied
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-V2TSLWTV.js";
|
|
5
5
|
import "../chunk-7EW3USQF.js";
|
|
6
6
|
import "../chunk-KQYIHZ5E.js";
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-NRCQ5UUR.js";
|
|
8
|
+
import "../chunk-ETDXSKR5.js";
|
|
9
9
|
import "../chunk-R5AJNXZS.js";
|
|
10
10
|
import "../chunk-DWED7BS3.js";
|
|
11
11
|
import "../chunk-Z5PL3K7C.js";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handlePermissionRequest
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-V2TSLWTV.js";
|
|
5
5
|
import "../chunk-7EW3USQF.js";
|
|
6
6
|
import "../chunk-KQYIHZ5E.js";
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-NRCQ5UUR.js";
|
|
8
|
+
import "../chunk-ETDXSKR5.js";
|
|
9
9
|
import "../chunk-R5AJNXZS.js";
|
|
10
10
|
import "../chunk-DWED7BS3.js";
|
|
11
11
|
import "../chunk-Z5PL3K7C.js";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handlePostToolUse
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-NRCQ5UUR.js";
|
|
5
|
+
import "../chunk-ETDXSKR5.js";
|
|
6
6
|
import "../chunk-DWED7BS3.js";
|
|
7
7
|
import "../chunk-Z5PL3K7C.js";
|
|
8
8
|
import "../chunk-NKXSILEW.js";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handleUserPrompt
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-NRCQ5UUR.js";
|
|
5
|
+
import "../chunk-ETDXSKR5.js";
|
|
6
6
|
import "../chunk-DWED7BS3.js";
|
|
7
7
|
import "../chunk-Z5PL3K7C.js";
|
|
8
8
|
import "../chunk-NKXSILEW.js";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handleSessionEnd
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-NRCQ5UUR.js";
|
|
5
|
+
import "../chunk-ETDXSKR5.js";
|
|
6
6
|
import "../chunk-DWED7BS3.js";
|
|
7
7
|
import "../chunk-Z5PL3K7C.js";
|
|
8
8
|
import "../chunk-NKXSILEW.js";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handleSessionStart
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-NRCQ5UUR.js";
|
|
5
|
+
import "../chunk-ETDXSKR5.js";
|
|
6
6
|
import "../chunk-DWED7BS3.js";
|
|
7
7
|
import "../chunk-Z5PL3K7C.js";
|
|
8
8
|
import "../chunk-NKXSILEW.js";
|
|
@@ -24,8 +24,8 @@ import {
|
|
|
24
24
|
} from "../chunk-7EW3USQF.js";
|
|
25
25
|
import {
|
|
26
26
|
reportEvent
|
|
27
|
-
} from "../chunk-
|
|
28
|
-
import "../chunk-
|
|
27
|
+
} from "../chunk-NRCQ5UUR.js";
|
|
28
|
+
import "../chunk-ETDXSKR5.js";
|
|
29
29
|
import "../chunk-DWED7BS3.js";
|
|
30
30
|
import {
|
|
31
31
|
isValidApiKey
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handleStop
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-NRCQ5UUR.js";
|
|
5
|
+
import "../chunk-ETDXSKR5.js";
|
|
6
6
|
import "../chunk-DWED7BS3.js";
|
|
7
7
|
import "../chunk-Z5PL3K7C.js";
|
|
8
8
|
import "../chunk-NKXSILEW.js";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handleStopFailure
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-NRCQ5UUR.js";
|
|
5
|
+
import "../chunk-ETDXSKR5.js";
|
|
6
6
|
import "../chunk-DWED7BS3.js";
|
|
7
7
|
import "../chunk-Z5PL3K7C.js";
|
|
8
8
|
import "../chunk-NKXSILEW.js";
|
package/dist/bin/pushary.js
CHANGED
|
@@ -26,7 +26,8 @@ Pushary Agent Hooks
|
|
|
26
26
|
|
|
27
27
|
Commands:
|
|
28
28
|
setup Configure Claude Code, Codex, Gemini CLI, Hermes, or Cursor with Pushary
|
|
29
|
-
claude Run Claude Code through Pushary (
|
|
29
|
+
claude Run Claude Code through Pushary (transparent passthrough; add --remote to
|
|
30
|
+
drive it from your phone: reach and re-prompt it even while idle)
|
|
30
31
|
doctor Verify your Pushary installation is working
|
|
31
32
|
clean Remove all Pushary configuration (--yes for non-interactive)
|
|
32
33
|
mode Switch approval mode (push_only, push_first, terminal_only)
|
|
@@ -47,5 +48,6 @@ Usage:
|
|
|
47
48
|
npx @pushary/agent-hooks@latest doctor
|
|
48
49
|
npx @pushary/agent-hooks@latest mode push_only --for 30m
|
|
49
50
|
npx @pushary/agent-hooks@latest wait 45
|
|
51
|
+
pushary claude --remote -p "start the refactor" # drive from your phone (needs @anthropic-ai/claude-agent-sdk)
|
|
50
52
|
`);
|
|
51
53
|
}
|
|
@@ -158,16 +158,17 @@ var fetchModeState = async (apiKey, sessionId) => {
|
|
|
158
158
|
headers: { "Authorization": `Bearer ${apiKey}` },
|
|
159
159
|
signal: AbortSignal.timeout(3e3)
|
|
160
160
|
});
|
|
161
|
-
if (!response.ok) return { mode: null, kill: false, policyVersion: null };
|
|
161
|
+
if (!response.ok) return { mode: null, kill: false, policyVersion: null, relayUrl: null };
|
|
162
162
|
const data = await response.json();
|
|
163
163
|
const mode = data.override?.mode;
|
|
164
164
|
return {
|
|
165
165
|
mode: isApprovalMode(mode) ? mode : null,
|
|
166
166
|
kill: data.kill === true,
|
|
167
|
-
policyVersion: toPolicyVersion(data.policyVersion)
|
|
167
|
+
policyVersion: toPolicyVersion(data.policyVersion),
|
|
168
|
+
relayUrl: typeof data.relayUrl === "string" && data.relayUrl.length > 0 ? data.relayUrl : null
|
|
168
169
|
};
|
|
169
170
|
} catch {
|
|
170
|
-
return { mode: null, kill: false, policyVersion: null };
|
|
171
|
+
return { mode: null, kill: false, policyVersion: null, relayUrl: null };
|
|
171
172
|
}
|
|
172
173
|
};
|
|
173
174
|
var fetchModeOverride = async (apiKey) => (await fetchModeState(apiKey)).mode;
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
readLastUserPrompt,
|
|
12
12
|
savePendingQuestion,
|
|
13
13
|
throttlePass
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-NRCQ5UUR.js";
|
|
15
15
|
import {
|
|
16
16
|
askUser,
|
|
17
17
|
cancelQuestion,
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
resolvePolicy,
|
|
27
27
|
sendNotification,
|
|
28
28
|
waitForAnswer
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-ETDXSKR5.js";
|
|
30
30
|
import {
|
|
31
31
|
isGatingMoment,
|
|
32
32
|
recordKeylessMoment
|
package/dist/src/index.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ interface ModeState {
|
|
|
33
33
|
readonly mode: ApprovalMode | null;
|
|
34
34
|
readonly kill: boolean;
|
|
35
35
|
readonly policyVersion: string | null;
|
|
36
|
+
readonly relayUrl: string | null;
|
|
36
37
|
}
|
|
37
38
|
declare const fetchModeState: (apiKey: string, sessionId?: string) => Promise<ModeState>;
|
|
38
39
|
declare const fetchModeOverride: (apiKey: string) => Promise<ApprovalMode | null>;
|
package/dist/src/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
handlePreToolUse
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-V2TSLWTV.js";
|
|
4
4
|
import "../chunk-7EW3USQF.js";
|
|
5
5
|
import "../chunk-KQYIHZ5E.js";
|
|
6
6
|
import {
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
handlePostToolUse,
|
|
9
9
|
handleStop,
|
|
10
10
|
reportEvent
|
|
11
|
-
} from "../chunk-
|
|
11
|
+
} from "../chunk-NRCQ5UUR.js";
|
|
12
12
|
import {
|
|
13
13
|
askUser,
|
|
14
14
|
cancelQuestion,
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
getPolicy,
|
|
18
18
|
resolvePolicy,
|
|
19
19
|
waitForAnswer
|
|
20
|
-
} from "../chunk-
|
|
20
|
+
} from "../chunk-ETDXSKR5.js";
|
|
21
21
|
import "../chunk-R5AJNXZS.js";
|
|
22
22
|
import "../chunk-DWED7BS3.js";
|
|
23
23
|
import "../chunk-Z5PL3K7C.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pushary/agent-hooks",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.42.0",
|
|
4
4
|
"description": "Permission hooks for AI coding agents: route tool approvals through Pushary push notifications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pushary",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"scripts": {
|
|
73
73
|
"build": "node scripts/bundle-plugin.mjs && tsup",
|
|
74
74
|
"dev": "tsup --watch",
|
|
75
|
-
"test": "bun test src/api.test.ts && bun test src/claude-config.test.ts && bun test src/config.test.ts && bun test src/mcp-http.test.ts && bun test src/retry.test.ts && bun test src/usage.test.ts && bun test src/validate.test.ts && bun test src/policy.test.ts && bun test src/npm.test.ts && bun test src/identity.test.ts && bun test src/pending.test.ts && bun test src/events.test.ts && bun test src/describe.test.ts && bun test src/reapply.test.ts && bun test src/suggestions.test.ts && bun test src/safe-commands.test.ts && bun test src/hook.test.ts && bun test src/codex-adapter.test.ts && bun test src/codex-config.test.ts && bun test src/gemini-adapter.test.ts && bun test src/gemini-config.test.ts && bun test src/instruction-file.test.ts && bun test src/pairing.test.ts && bun test src/wait-ladder.test.ts && bun test src/ledger.test.ts && bun test src/wrapper/wrapper.test.ts && bun test src/wrapper/messageQueue.test.ts && bun test src/wrapper/approver.test.ts && bun test src/wrapper/remoteLoop.test.ts"
|
|
75
|
+
"test": "bun test src/api.test.ts && bun test src/claude-config.test.ts && bun test src/config.test.ts && bun test src/mcp-http.test.ts && bun test src/retry.test.ts && bun test src/usage.test.ts && bun test src/validate.test.ts && bun test src/policy.test.ts && bun test src/npm.test.ts && bun test src/identity.test.ts && bun test src/pending.test.ts && bun test src/events.test.ts && bun test src/describe.test.ts && bun test src/reapply.test.ts && bun test src/suggestions.test.ts && bun test src/safe-commands.test.ts && bun test src/hook.test.ts && bun test src/codex-adapter.test.ts && bun test src/codex-config.test.ts && bun test src/gemini-adapter.test.ts && bun test src/gemini-config.test.ts && bun test src/instruction-file.test.ts && bun test src/pairing.test.ts && bun test src/wait-ladder.test.ts && bun test src/ledger.test.ts && bun test src/wrapper/wrapper.test.ts && bun test src/wrapper/messageQueue.test.ts && bun test src/wrapper/approver.test.ts && bun test src/wrapper/remoteLoop.test.ts && bun test src/wrapper/spawnClaude.test.ts && bun test src/wrapper/wsProtocol.test.ts && bun test src/wrapper/relayClient.test.ts"
|
|
76
76
|
},
|
|
77
77
|
"dependencies": {
|
|
78
78
|
"@inquirer/prompts": "^8.4.2",
|