@pushary/agent-hooks 0.40.0 → 0.43.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 +467 -17
- 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
|
});
|
|
@@ -95,15 +104,24 @@ var runLocalPassthrough = (binary, args2) => {
|
|
|
95
104
|
import { basename } from "path";
|
|
96
105
|
|
|
97
106
|
// src/wrapper/sdkLoader.ts
|
|
107
|
+
import { spawn as spawn2 } from "child_process";
|
|
108
|
+
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
109
|
+
import { homedir } from "os";
|
|
110
|
+
import { join as join2 } from "path";
|
|
111
|
+
import { pathToFileURL } from "url";
|
|
98
112
|
var userMessage = (text) => ({
|
|
99
113
|
type: "user",
|
|
100
114
|
message: { role: "user", content: text },
|
|
101
115
|
parent_tool_use_id: null
|
|
102
116
|
});
|
|
103
|
-
var
|
|
104
|
-
|
|
117
|
+
var SDK_VERSION = "0.3.207";
|
|
118
|
+
var SDK_PACKAGE = "@anthropic-ai/claude-agent-sdk";
|
|
119
|
+
var SDK_SPEC = `${SDK_PACKAGE}@${SDK_VERSION}`;
|
|
120
|
+
var sdkCacheDir = () => process.env.PUSHARY_REMOTE_SDK_DIR?.trim() || join2(homedir(), ".pushary", "remote-sdk");
|
|
121
|
+
var cachedSdkEntry = () => join2(sdkCacheDir(), "node_modules", "@anthropic-ai", "claude-agent-sdk", "sdk.mjs");
|
|
122
|
+
var importSdk = async (specifierOrUrl) => {
|
|
105
123
|
try {
|
|
106
|
-
const mod = await import(
|
|
124
|
+
const mod = await import(specifierOrUrl);
|
|
107
125
|
if (typeof mod.query === "function") return mod;
|
|
108
126
|
if (mod.default && typeof mod.default.query === "function") return mod.default;
|
|
109
127
|
return null;
|
|
@@ -111,6 +129,44 @@ var loadClaudeSdk = async () => {
|
|
|
111
129
|
return null;
|
|
112
130
|
}
|
|
113
131
|
};
|
|
132
|
+
var loadClaudeSdk = async () => {
|
|
133
|
+
const fromResolution = await importSdk(SDK_PACKAGE);
|
|
134
|
+
if (fromResolution) return fromResolution;
|
|
135
|
+
const entry = cachedSdkEntry();
|
|
136
|
+
if (existsSync(entry)) return importSdk(pathToFileURL(entry).href);
|
|
137
|
+
return null;
|
|
138
|
+
};
|
|
139
|
+
var ensureClaudeSdk = async (log) => {
|
|
140
|
+
const present = await loadClaudeSdk();
|
|
141
|
+
if (present) return present;
|
|
142
|
+
const dir = sdkCacheDir();
|
|
143
|
+
try {
|
|
144
|
+
mkdirSync(dir, { recursive: true });
|
|
145
|
+
const pkgJson = join2(dir, "package.json");
|
|
146
|
+
if (!existsSync(pkgJson)) {
|
|
147
|
+
writeFileSync(pkgJson, `${JSON.stringify({ name: "pushary-remote-sdk", private: true })}
|
|
148
|
+
`);
|
|
149
|
+
}
|
|
150
|
+
} catch {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
log?.("[pushary] setting up remote mode (one-time, fetching the Claude Agent SDK)...");
|
|
154
|
+
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
155
|
+
const installed = await new Promise((resolve) => {
|
|
156
|
+
const child = spawn2(
|
|
157
|
+
npmCmd,
|
|
158
|
+
["install", "--no-optional", "--no-audit", "--no-fund", "--loglevel=error", SDK_SPEC],
|
|
159
|
+
{ cwd: dir, stdio: "ignore", shell: process.platform === "win32" }
|
|
160
|
+
);
|
|
161
|
+
child.on("error", () => resolve(false));
|
|
162
|
+
child.on("close", (code) => resolve(code === 0));
|
|
163
|
+
});
|
|
164
|
+
if (!installed) {
|
|
165
|
+
log?.(`[pushary] could not auto-install the SDK. Install it manually: npm i -g ${SDK_SPEC}`);
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
return loadClaudeSdk();
|
|
169
|
+
};
|
|
114
170
|
|
|
115
171
|
// src/wrapper/messageQueue.ts
|
|
116
172
|
var BoundedInputQueue = class {
|
|
@@ -119,12 +175,14 @@ var BoundedInputQueue = class {
|
|
|
119
175
|
closed = false;
|
|
120
176
|
cap;
|
|
121
177
|
onDrop;
|
|
178
|
+
coalesce;
|
|
122
179
|
pushedCount = 0;
|
|
123
180
|
deliveredCount = 0;
|
|
124
181
|
droppedCount = 0;
|
|
125
182
|
constructor(options = {}) {
|
|
126
183
|
this.cap = Math.max(1, options.cap ?? 32);
|
|
127
184
|
this.onDrop = options.onDrop;
|
|
185
|
+
this.coalesce = options.coalesce;
|
|
128
186
|
}
|
|
129
187
|
push(item) {
|
|
130
188
|
if (this.closed) return;
|
|
@@ -179,6 +237,11 @@ var BoundedInputQueue = class {
|
|
|
179
237
|
return {
|
|
180
238
|
next: () => {
|
|
181
239
|
if (this.buffer.length > 0) {
|
|
240
|
+
if (this.coalesce && this.buffer.length > 1) {
|
|
241
|
+
const items = this.buffer.splice(0, this.buffer.length);
|
|
242
|
+
this.deliveredCount += items.length;
|
|
243
|
+
return Promise.resolve({ value: this.coalesce(items), done: false });
|
|
244
|
+
}
|
|
182
245
|
const value = this.buffer.shift();
|
|
183
246
|
this.deliveredCount++;
|
|
184
247
|
return Promise.resolve({ value, done: false });
|
|
@@ -383,13 +446,28 @@ var startCommandPoller = (opts) => {
|
|
|
383
446
|
if (stopped) return;
|
|
384
447
|
timer = setTimeout(tick, jitter(delayMs));
|
|
385
448
|
};
|
|
449
|
+
const fetchMode = async () => {
|
|
450
|
+
if (!opts.fetchModeState || !opts.onModeState) return;
|
|
451
|
+
try {
|
|
452
|
+
const state = await opts.fetchModeState(opts.apiKey, opts.getSessionId?.());
|
|
453
|
+
try {
|
|
454
|
+
opts.onModeState(state);
|
|
455
|
+
} catch {
|
|
456
|
+
}
|
|
457
|
+
} catch {
|
|
458
|
+
}
|
|
459
|
+
};
|
|
386
460
|
const tick = async () => {
|
|
387
461
|
if (stopped) return;
|
|
388
462
|
const controller = new AbortController();
|
|
389
463
|
inflight = controller;
|
|
390
464
|
const timeout = setTimeout(() => controller.abort(), requestTimeout);
|
|
391
465
|
try {
|
|
392
|
-
const
|
|
466
|
+
const wantCommands = opts.shouldDrainCommands ? opts.shouldDrainCommands() : true;
|
|
467
|
+
const [command] = await Promise.all([
|
|
468
|
+
wantCommands ? drain(opts.apiKey, opts.getSessionId?.(), controller.signal) : Promise.resolve(null),
|
|
469
|
+
fetchMode()
|
|
470
|
+
]);
|
|
393
471
|
errorStreak = 0;
|
|
394
472
|
if (command) {
|
|
395
473
|
idleTicks = 0;
|
|
@@ -420,6 +498,286 @@ var startCommandPoller = (opts) => {
|
|
|
420
498
|
};
|
|
421
499
|
};
|
|
422
500
|
|
|
501
|
+
// src/wrapper/wsProtocol.ts
|
|
502
|
+
var PROTOCOL_VERSION = 1;
|
|
503
|
+
var TRANSCRIPT_TEXT_MAX = 4e3;
|
|
504
|
+
var encodeFrame = (frame) => JSON.stringify(frame);
|
|
505
|
+
var helloFrame = (fields) => ({ v: PROTOCOL_VERSION, t: "hello", ...fields });
|
|
506
|
+
var transcriptFrame = (seq, kind, text, meta) => ({
|
|
507
|
+
v: PROTOCOL_VERSION,
|
|
508
|
+
t: "transcript",
|
|
509
|
+
seq,
|
|
510
|
+
kind,
|
|
511
|
+
...text !== void 0 ? { text: text.slice(0, TRANSCRIPT_TEXT_MAX) } : {},
|
|
512
|
+
...meta ? { meta } : {}
|
|
513
|
+
});
|
|
514
|
+
var parse = (raw) => {
|
|
515
|
+
try {
|
|
516
|
+
const value = JSON.parse(raw);
|
|
517
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
518
|
+
const obj = value;
|
|
519
|
+
if (obj.v !== PROTOCOL_VERSION) return null;
|
|
520
|
+
return obj;
|
|
521
|
+
} catch {
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
var isString = (v) => typeof v === "string";
|
|
526
|
+
var isFiniteNumber = (v) => typeof v === "number" && Number.isFinite(v);
|
|
527
|
+
var decodeServerFrame = (raw) => {
|
|
528
|
+
const f = parse(raw);
|
|
529
|
+
if (!f) return null;
|
|
530
|
+
switch (f.t) {
|
|
531
|
+
case "welcome":
|
|
532
|
+
return isFiniteNumber(f.resumeFromSeq) ? { v: PROTOCOL_VERSION, t: "welcome", resumeFromSeq: f.resumeFromSeq } : null;
|
|
533
|
+
case "ping":
|
|
534
|
+
return { v: PROTOCOL_VERSION, t: "ping" };
|
|
535
|
+
case "pong":
|
|
536
|
+
return { v: PROTOCOL_VERSION, t: "pong" };
|
|
537
|
+
case "command":
|
|
538
|
+
return isString(f.id) && isString(f.text) ? { v: PROTOCOL_VERSION, t: "command", id: f.id, text: f.text } : null;
|
|
539
|
+
case "resume":
|
|
540
|
+
return isFiniteNumber(f.afterSeq) ? { v: PROTOCOL_VERSION, t: "resume", afterSeq: f.afterSeq } : null;
|
|
541
|
+
case "error":
|
|
542
|
+
if (isString(f.code) && isString(f.message)) {
|
|
543
|
+
return { v: PROTOCOL_VERSION, t: "error", code: f.code, message: f.message, fatal: f.fatal === true };
|
|
544
|
+
}
|
|
545
|
+
return null;
|
|
546
|
+
default:
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
// src/wrapper/relayClient.ts
|
|
552
|
+
var DEFAULT_HEARTBEAT_MS = 2e4;
|
|
553
|
+
var DEFAULT_PONG_TIMEOUT_MS = 1e4;
|
|
554
|
+
var DEFAULT_MAX_OUTBOUND = 128;
|
|
555
|
+
var DEFAULT_MAX_SEEN_COMMANDS = 256;
|
|
556
|
+
var defaultBackoff = (attempt) => Math.min(1e3 * 2 ** attempt, 3e4);
|
|
557
|
+
var defaultSocketFactory = (url) => {
|
|
558
|
+
const WS = globalThis.WebSocket;
|
|
559
|
+
if (!WS) throw new Error("no WebSocket in this runtime");
|
|
560
|
+
const socket = new WS(url);
|
|
561
|
+
return {
|
|
562
|
+
send: (data) => socket.send(data),
|
|
563
|
+
close: () => socket.close(),
|
|
564
|
+
onOpen: (cb) => socket.addEventListener("open", () => cb()),
|
|
565
|
+
onMessage: (cb) => socket.addEventListener("message", (event) => cb(String(event.data))),
|
|
566
|
+
onClose: (cb) => socket.addEventListener("close", () => cb()),
|
|
567
|
+
onError: (cb) => socket.addEventListener("error", (event) => cb(event))
|
|
568
|
+
};
|
|
569
|
+
};
|
|
570
|
+
var createRelayClient = (options) => {
|
|
571
|
+
const factory = options.socketFactory ?? defaultSocketFactory;
|
|
572
|
+
const heartbeatMs = options.heartbeatMs ?? DEFAULT_HEARTBEAT_MS;
|
|
573
|
+
const pongTimeoutMs = options.pongTimeoutMs ?? DEFAULT_PONG_TIMEOUT_MS;
|
|
574
|
+
const backoff = options.backoffMs ?? defaultBackoff;
|
|
575
|
+
const maxOutbound = Math.max(1, options.maxOutbound ?? DEFAULT_MAX_OUTBOUND);
|
|
576
|
+
const maxSeenCommands = Math.max(1, options.maxSeenCommands ?? DEFAULT_MAX_SEEN_COMMANDS);
|
|
577
|
+
let socket;
|
|
578
|
+
let isConnected = false;
|
|
579
|
+
let stopped = false;
|
|
580
|
+
let seq = 0;
|
|
581
|
+
let attempt = 0;
|
|
582
|
+
let reconnectTimer;
|
|
583
|
+
let heartbeatTimer;
|
|
584
|
+
let pongTimer;
|
|
585
|
+
const outbound = [];
|
|
586
|
+
const seenCommands = [];
|
|
587
|
+
const seenSet = /* @__PURE__ */ new Set();
|
|
588
|
+
const log = (message) => options.log?.(message);
|
|
589
|
+
const clearTimers = () => {
|
|
590
|
+
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
591
|
+
if (pongTimer) clearTimeout(pongTimer);
|
|
592
|
+
if (reconnectTimer) clearTimeout(reconnectTimer);
|
|
593
|
+
heartbeatTimer = void 0;
|
|
594
|
+
pongTimer = void 0;
|
|
595
|
+
reconnectTimer = void 0;
|
|
596
|
+
};
|
|
597
|
+
const rememberCommand = (id) => {
|
|
598
|
+
if (seenSet.has(id)) return false;
|
|
599
|
+
seenSet.add(id);
|
|
600
|
+
seenCommands.push(id);
|
|
601
|
+
while (seenCommands.length > maxSeenCommands) {
|
|
602
|
+
const evicted = seenCommands.shift();
|
|
603
|
+
if (evicted) seenSet.delete(evicted);
|
|
604
|
+
}
|
|
605
|
+
return true;
|
|
606
|
+
};
|
|
607
|
+
const sendFrame = (frame) => {
|
|
608
|
+
if (!socket || !isConnected) return;
|
|
609
|
+
try {
|
|
610
|
+
socket.send(encodeFrame(frame));
|
|
611
|
+
} catch {
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
const enqueueTranscript = (frame) => {
|
|
615
|
+
if (isConnected) {
|
|
616
|
+
sendFrame(frame);
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
outbound.push(frame);
|
|
620
|
+
while (outbound.length > maxOutbound) outbound.shift();
|
|
621
|
+
};
|
|
622
|
+
const flushOutbound = () => {
|
|
623
|
+
while (outbound.length > 0 && isConnected) {
|
|
624
|
+
const frame = outbound.shift();
|
|
625
|
+
if (frame) sendFrame(frame);
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
const armPong = () => {
|
|
629
|
+
if (pongTimer) clearTimeout(pongTimer);
|
|
630
|
+
pongTimer = setTimeout(() => {
|
|
631
|
+
log("[pushary] relay heartbeat timed out; reconnecting");
|
|
632
|
+
dropAndReconnect();
|
|
633
|
+
}, pongTimeoutMs);
|
|
634
|
+
};
|
|
635
|
+
const startHeartbeat = () => {
|
|
636
|
+
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
637
|
+
heartbeatTimer = setInterval(() => {
|
|
638
|
+
sendFrame({ v: PROTOCOL_VERSION, t: "ping" });
|
|
639
|
+
armPong();
|
|
640
|
+
}, heartbeatMs);
|
|
641
|
+
};
|
|
642
|
+
const onServerFrame = (raw) => {
|
|
643
|
+
const frame = decodeServerFrame(raw);
|
|
644
|
+
if (!frame) return;
|
|
645
|
+
switch (frame.t) {
|
|
646
|
+
case "welcome":
|
|
647
|
+
attempt = 0;
|
|
648
|
+
flushOutbound();
|
|
649
|
+
log("[pushary] relay connected");
|
|
650
|
+
break;
|
|
651
|
+
case "command":
|
|
652
|
+
sendFrame({ v: PROTOCOL_VERSION, t: "ack", id: frame.id });
|
|
653
|
+
if (rememberCommand(frame.id)) {
|
|
654
|
+
try {
|
|
655
|
+
options.onCommand(frame.text);
|
|
656
|
+
} catch {
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
break;
|
|
660
|
+
case "ping":
|
|
661
|
+
sendFrame({ v: PROTOCOL_VERSION, t: "pong" });
|
|
662
|
+
break;
|
|
663
|
+
case "pong":
|
|
664
|
+
if (pongTimer) clearTimeout(pongTimer);
|
|
665
|
+
pongTimer = void 0;
|
|
666
|
+
break;
|
|
667
|
+
case "resume":
|
|
668
|
+
log(`[pushary] relay signalled a gap; catch up after seq ${frame.afterSeq}`);
|
|
669
|
+
break;
|
|
670
|
+
case "error":
|
|
671
|
+
if (frame.fatal) {
|
|
672
|
+
log(`[pushary] relay fatal: ${frame.message}`);
|
|
673
|
+
fatal(frame.message);
|
|
674
|
+
} else {
|
|
675
|
+
log(`[pushary] relay error: ${frame.message}`);
|
|
676
|
+
}
|
|
677
|
+
break;
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
const dropSocket = () => {
|
|
681
|
+
isConnected = false;
|
|
682
|
+
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
683
|
+
if (pongTimer) clearTimeout(pongTimer);
|
|
684
|
+
heartbeatTimer = void 0;
|
|
685
|
+
pongTimer = void 0;
|
|
686
|
+
if (socket) {
|
|
687
|
+
try {
|
|
688
|
+
socket.close();
|
|
689
|
+
} catch {
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
socket = void 0;
|
|
693
|
+
};
|
|
694
|
+
const scheduleReconnect = () => {
|
|
695
|
+
if (stopped) return;
|
|
696
|
+
if (reconnectTimer) return;
|
|
697
|
+
const delay = backoff(attempt);
|
|
698
|
+
attempt++;
|
|
699
|
+
const jittered = Math.max(1, Math.round(delay * (0.85 + Math.random() * 0.3)));
|
|
700
|
+
reconnectTimer = setTimeout(() => {
|
|
701
|
+
reconnectTimer = void 0;
|
|
702
|
+
connect();
|
|
703
|
+
}, jittered);
|
|
704
|
+
};
|
|
705
|
+
const dropAndReconnect = () => {
|
|
706
|
+
dropSocket();
|
|
707
|
+
scheduleReconnect();
|
|
708
|
+
};
|
|
709
|
+
const fatal = (message) => {
|
|
710
|
+
stopped = true;
|
|
711
|
+
clearTimers();
|
|
712
|
+
dropSocket();
|
|
713
|
+
options.onFatal?.(message);
|
|
714
|
+
};
|
|
715
|
+
const connect = () => {
|
|
716
|
+
if (stopped) return;
|
|
717
|
+
let created;
|
|
718
|
+
try {
|
|
719
|
+
created = factory(options.url);
|
|
720
|
+
} catch (error) {
|
|
721
|
+
fatal(error instanceof Error ? error.message : String(error));
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
socket = created;
|
|
725
|
+
created.onOpen(() => {
|
|
726
|
+
isConnected = true;
|
|
727
|
+
sendFrame(
|
|
728
|
+
helloFrame({
|
|
729
|
+
apiKey: options.apiKey,
|
|
730
|
+
machineId: options.machineId,
|
|
731
|
+
agentType: options.agentType,
|
|
732
|
+
sessionId: options.getSessionId(),
|
|
733
|
+
lastSeq: seq
|
|
734
|
+
})
|
|
735
|
+
);
|
|
736
|
+
startHeartbeat();
|
|
737
|
+
});
|
|
738
|
+
created.onMessage((data) => onServerFrame(data));
|
|
739
|
+
created.onClose(() => {
|
|
740
|
+
if (stopped) return;
|
|
741
|
+
dropAndReconnect();
|
|
742
|
+
});
|
|
743
|
+
created.onError(() => {
|
|
744
|
+
});
|
|
745
|
+
};
|
|
746
|
+
return {
|
|
747
|
+
start() {
|
|
748
|
+
if (stopped) return;
|
|
749
|
+
connect();
|
|
750
|
+
},
|
|
751
|
+
connected() {
|
|
752
|
+
return isConnected;
|
|
753
|
+
},
|
|
754
|
+
sendTranscript(kind, text, meta) {
|
|
755
|
+
if (stopped) return;
|
|
756
|
+
seq++;
|
|
757
|
+
enqueueTranscript(transcriptFrame(seq, kind, text, meta));
|
|
758
|
+
},
|
|
759
|
+
reannounce() {
|
|
760
|
+
if (stopped || !isConnected) return;
|
|
761
|
+
sendFrame(
|
|
762
|
+
helloFrame({
|
|
763
|
+
apiKey: options.apiKey,
|
|
764
|
+
machineId: options.machineId,
|
|
765
|
+
agentType: options.agentType,
|
|
766
|
+
sessionId: options.getSessionId(),
|
|
767
|
+
lastSeq: seq
|
|
768
|
+
})
|
|
769
|
+
);
|
|
770
|
+
},
|
|
771
|
+
stop() {
|
|
772
|
+
if (stopped) return;
|
|
773
|
+
stopped = true;
|
|
774
|
+
sendFrame({ v: PROTOCOL_VERSION, t: "bye" });
|
|
775
|
+
clearTimers();
|
|
776
|
+
dropSocket();
|
|
777
|
+
}
|
|
778
|
+
};
|
|
779
|
+
};
|
|
780
|
+
|
|
423
781
|
// src/wrapper/remoteLoop.ts
|
|
424
782
|
var errMsg = (err) => err instanceof Error ? err.message : String(err);
|
|
425
783
|
var DEFAULT_MAX_RESTARTS = 5;
|
|
@@ -441,7 +799,8 @@ var runRemoteLoop = async (deps) => {
|
|
|
441
799
|
permissionMode: deps.permissionMode,
|
|
442
800
|
canUseTool: deps.canUseTool,
|
|
443
801
|
env: deps.env,
|
|
444
|
-
cwd: deps.cwd
|
|
802
|
+
cwd: deps.cwd,
|
|
803
|
+
pathToClaudeCodeExecutable: deps.pathToClaudeCodeExecutable
|
|
445
804
|
}
|
|
446
805
|
});
|
|
447
806
|
} catch (err) {
|
|
@@ -457,6 +816,7 @@ var runRemoteLoop = async (deps) => {
|
|
|
457
816
|
else deps.signal.addEventListener("abort", onAbort, { once: true });
|
|
458
817
|
try {
|
|
459
818
|
for await (const message of query) {
|
|
819
|
+
deps.onMessage?.(message);
|
|
460
820
|
if (message.type === "system" && message.subtype === "init" && typeof message.session_id === "string") {
|
|
461
821
|
resumeId = message.session_id;
|
|
462
822
|
deps.onSessionId?.(resumeId);
|
|
@@ -490,6 +850,10 @@ var resolveClaudeArgs = (argv) => {
|
|
|
490
850
|
const rest = argv.slice(2);
|
|
491
851
|
return rest[0] === "claude" ? rest.slice(1) : rest;
|
|
492
852
|
};
|
|
853
|
+
var extractRemoteFlag = (args2) => ({
|
|
854
|
+
remote: args2.includes("--remote"),
|
|
855
|
+
rest: args2.filter((arg) => arg !== "--remote")
|
|
856
|
+
});
|
|
493
857
|
var flagValue = (args2, index) => {
|
|
494
858
|
const inline = args2[index];
|
|
495
859
|
const eq = inline?.indexOf("=") ?? -1;
|
|
@@ -516,7 +880,37 @@ var parseRemoteArgs = (args2) => {
|
|
|
516
880
|
|
|
517
881
|
// src/wrapper/remoteMode.ts
|
|
518
882
|
var INPUT_QUEUE_CAP = 32;
|
|
519
|
-
var
|
|
883
|
+
var extractAssistantText = (message) => {
|
|
884
|
+
const content = message.message?.content;
|
|
885
|
+
if (typeof content === "string") return content || void 0;
|
|
886
|
+
if (Array.isArray(content)) {
|
|
887
|
+
const text = content.filter((block) => !!block && typeof block === "object").filter((block) => block.type === "text" && typeof block.text === "string").map((block) => block.text).join("");
|
|
888
|
+
return text || void 0;
|
|
889
|
+
}
|
|
890
|
+
return void 0;
|
|
891
|
+
};
|
|
892
|
+
var echoLocal = (message) => {
|
|
893
|
+
if (message.type === "assistant") {
|
|
894
|
+
const text = extractAssistantText(message);
|
|
895
|
+
if (text) process.stdout.write(`${text}
|
|
896
|
+
`);
|
|
897
|
+
} else if (message.type === "result") {
|
|
898
|
+
process.stderr.write("[pushary] turn complete \u2014 standing by for phone commands (Ctrl-C to exit)\n");
|
|
899
|
+
}
|
|
900
|
+
};
|
|
901
|
+
var streamTranscript = (relay, message) => {
|
|
902
|
+
if (message.type === "assistant") {
|
|
903
|
+
const text = extractAssistantText(message);
|
|
904
|
+
if (text) relay.sendTranscript("assistant", text);
|
|
905
|
+
} else if (message.type === "result") {
|
|
906
|
+
relay.sendTranscript(
|
|
907
|
+
"turn_end",
|
|
908
|
+
void 0,
|
|
909
|
+
typeof message.total_cost_usd === "number" ? { cost: message.total_cost_usd } : void 0
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
};
|
|
913
|
+
var runRemoteMode = async (binary, args2) => {
|
|
520
914
|
let apiKey;
|
|
521
915
|
try {
|
|
522
916
|
apiKey = getApiKey();
|
|
@@ -526,21 +920,24 @@ var runRemoteMode = async (_binary, args2) => {
|
|
|
526
920
|
);
|
|
527
921
|
return { implemented: false };
|
|
528
922
|
}
|
|
529
|
-
const sdk = await
|
|
923
|
+
const sdk = await ensureClaudeSdk((message) => process.stderr.write(`${message}
|
|
924
|
+
`));
|
|
530
925
|
if (!sdk) {
|
|
531
|
-
process.stderr.write(
|
|
532
|
-
"[pushary] remote mode needs the Claude Agent SDK, which is not installed. Enable it with:\n npm i -g @anthropic-ai/claude-agent-sdk@0.3.207\nRunning the normal passthrough for now.\n"
|
|
533
|
-
);
|
|
926
|
+
process.stderr.write("[pushary] remote mode is unavailable; running the normal passthrough.\n");
|
|
534
927
|
return { implemented: false };
|
|
535
928
|
}
|
|
536
929
|
const projectName = basename(process.cwd());
|
|
537
930
|
const machineId = getMachineId();
|
|
538
931
|
const controller = new AbortController();
|
|
539
932
|
let sessionId;
|
|
933
|
+
let killed = false;
|
|
934
|
+
let totalCostUsd = 0;
|
|
540
935
|
const input = new BoundedInputQueue({
|
|
541
936
|
cap: INPUT_QUEUE_CAP,
|
|
542
937
|
onDrop: (_dropped, total) => process.stderr.write(`[pushary] input queue full; dropped ${total} stale instruction(s)
|
|
543
|
-
`)
|
|
938
|
+
`),
|
|
939
|
+
// Several instructions that piled up between turns are sent as one turn.
|
|
940
|
+
coalesce: (items) => userMessage(items.map((m) => m.message.content).join("\n\n"))
|
|
544
941
|
});
|
|
545
942
|
const approver = createRemoteApprover({
|
|
546
943
|
apiKey,
|
|
@@ -548,10 +945,41 @@ var runRemoteMode = async (_binary, args2) => {
|
|
|
548
945
|
projectName,
|
|
549
946
|
getSessionId: () => sessionId
|
|
550
947
|
});
|
|
948
|
+
const initialMode = await fetchModeState(apiKey).catch(() => null);
|
|
949
|
+
let relay;
|
|
950
|
+
if (initialMode?.relayUrl) {
|
|
951
|
+
relay = createRelayClient({
|
|
952
|
+
url: initialMode.relayUrl,
|
|
953
|
+
apiKey,
|
|
954
|
+
machineId,
|
|
955
|
+
agentType: "claude_code",
|
|
956
|
+
getSessionId: () => sessionId,
|
|
957
|
+
onCommand: (command) => input.push(userMessage(command)),
|
|
958
|
+
onFatal: (message) => process.stderr.write(`[pushary] relay unavailable (${message}); using polling
|
|
959
|
+
`),
|
|
960
|
+
log: (message) => process.stderr.write(`${message}
|
|
961
|
+
`)
|
|
962
|
+
});
|
|
963
|
+
relay.start();
|
|
964
|
+
}
|
|
551
965
|
const poller = startCommandPoller({
|
|
552
966
|
apiKey,
|
|
553
967
|
getSessionId: () => sessionId,
|
|
554
968
|
onCommand: (command) => input.push(userMessage(command)),
|
|
969
|
+
// While the relay socket owns command delivery, the poller must not also drain
|
|
970
|
+
// the single-use queue (that would deliver the command twice); it still reads
|
|
971
|
+
// mode/kill. When the socket is down or absent, the poller is the source.
|
|
972
|
+
shouldDrainCommands: () => !(relay?.connected() ?? false),
|
|
973
|
+
// Watch the kill switch: a phone "stop" interrupts the running turn (via the
|
|
974
|
+
// controller abort below), instead of only denying the next tool call.
|
|
975
|
+
fetchModeState,
|
|
976
|
+
onModeState: (state) => {
|
|
977
|
+
if (state.kill && !killed) {
|
|
978
|
+
killed = true;
|
|
979
|
+
process.stderr.write("[pushary] halted from Pushary \u2014 stopping the agent\n");
|
|
980
|
+
controller.abort();
|
|
981
|
+
}
|
|
982
|
+
},
|
|
555
983
|
log: (message) => process.stderr.write(`${message}
|
|
556
984
|
`)
|
|
557
985
|
});
|
|
@@ -572,8 +1000,24 @@ var runRemoteMode = async (_binary, args2) => {
|
|
|
572
1000
|
// recursion guard so a wrapper-spawned claude cannot re-enter the wrapper.
|
|
573
1001
|
env: { ...process.env, [WRAPPER_ACTIVE_ENV]: "1" },
|
|
574
1002
|
cwd: process.cwd(),
|
|
1003
|
+
// Run the user's OWN claude, not the SDK's bundled copy, so whatever login
|
|
1004
|
+
// already works for `claude` works here with no extra setup. On Windows a
|
|
1005
|
+
// `.cmd`/`.bat` shim can't be spawned directly by the SDK, so there we let
|
|
1006
|
+
// the SDK use its bundled binary (Windows reads creds from a plain file, so
|
|
1007
|
+
// there is no keychain-ACL mismatch to work around).
|
|
1008
|
+
pathToClaudeCodeExecutable: needsShell(binary) ? void 0 : binary,
|
|
575
1009
|
onSessionId: (id) => {
|
|
576
1010
|
sessionId = id;
|
|
1011
|
+
relay?.reannounce();
|
|
1012
|
+
},
|
|
1013
|
+
onCost: (usd) => {
|
|
1014
|
+
totalCostUsd = usd;
|
|
1015
|
+
},
|
|
1016
|
+
// Always echo the turn to the local terminal; also stream a bounded live
|
|
1017
|
+
// transcript to the phone when the relay is up (it presence-gates it).
|
|
1018
|
+
onMessage: (message) => {
|
|
1019
|
+
echoLocal(message);
|
|
1020
|
+
if (relay) streamTranscript(relay, message);
|
|
577
1021
|
},
|
|
578
1022
|
log: (message) => process.stderr.write(`${message}
|
|
579
1023
|
`)
|
|
@@ -581,9 +1025,14 @@ var runRemoteMode = async (_binary, args2) => {
|
|
|
581
1025
|
return { implemented: true, exitCode: result.exitCode };
|
|
582
1026
|
} finally {
|
|
583
1027
|
for (const signal of signals) process.off(signal, onSignal);
|
|
1028
|
+
relay?.stop();
|
|
584
1029
|
poller.stop();
|
|
585
1030
|
approver.teardown();
|
|
586
1031
|
input.close();
|
|
1032
|
+
if (totalCostUsd > 0) {
|
|
1033
|
+
process.stderr.write(`[pushary] remote session cost: $${totalCostUsd.toFixed(4)}
|
|
1034
|
+
`);
|
|
1035
|
+
}
|
|
587
1036
|
}
|
|
588
1037
|
};
|
|
589
1038
|
|
|
@@ -597,14 +1046,15 @@ var runClaudeWrapper = async (args2) => {
|
|
|
597
1046
|
return 127;
|
|
598
1047
|
}
|
|
599
1048
|
const nested = process.env[WRAPPER_ACTIVE_ENV] === "1";
|
|
600
|
-
|
|
1049
|
+
const { remote: wantRemote, rest } = extractRemoteFlag(args2);
|
|
1050
|
+
if (!nested && wantRemote) {
|
|
601
1051
|
try {
|
|
602
|
-
const remote = await runRemoteMode(binary,
|
|
1052
|
+
const remote = await runRemoteMode(binary, rest);
|
|
603
1053
|
if (remote.implemented) return remote.exitCode ?? 0;
|
|
604
1054
|
} catch {
|
|
605
1055
|
}
|
|
606
1056
|
}
|
|
607
|
-
return runLocalPassthrough(binary,
|
|
1057
|
+
return runLocalPassthrough(binary, rest);
|
|
608
1058
|
};
|
|
609
1059
|
|
|
610
1060
|
// 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 (uses your existing Claude login; sets up on first run)
|
|
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.43.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",
|