@pushary/agent-hooks 0.89.9 → 0.89.11
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/CHANGELOG.md +54 -0
- package/dist/bin/pushary-bell-hook.js +4 -4
- package/dist/bin/pushary-bell.js +7 -7
- package/dist/bin/pushary-claude.js +84 -36
- package/dist/bin/pushary-clean.js +52 -45
- package/dist/bin/pushary-codex-bridge.js +15 -9
- package/dist/bin/pushary-codex-hook.js +12 -8
- package/dist/bin/pushary-codex.js +7 -7
- package/dist/bin/pushary-connect.js +6 -6
- package/dist/bin/pushary-daemon.js +16 -16
- package/dist/bin/pushary-disconnect.js +34 -9
- package/dist/bin/pushary-doctor.js +19 -19
- package/dist/bin/pushary-elicitation-hook.js +4 -4
- package/dist/bin/pushary-gemini-bridge.js +15 -8
- package/dist/bin/pushary-gemini-hook.js +8 -8
- package/dist/bin/pushary-hook.js +11 -10
- package/dist/bin/pushary-login.js +4 -4
- package/dist/bin/pushary-logout.js +4 -4
- package/dist/bin/pushary-mode.js +3 -3
- package/dist/bin/pushary-notification-hook.js +7 -7
- package/dist/bin/pushary-opencode-hook.js +8 -8
- package/dist/bin/pushary-permission-denied-hook.js +11 -10
- package/dist/bin/pushary-permission-hook.js +11 -10
- package/dist/bin/pushary-post-hook.js +7 -7
- package/dist/bin/pushary-prompt-hook.js +7 -7
- package/dist/bin/pushary-session-end-hook.js +7 -7
- package/dist/bin/pushary-session-start-hook.js +7 -7
- package/dist/bin/pushary-setup.js +52 -54
- package/dist/bin/pushary-stats.js +2 -2
- package/dist/bin/pushary-status.js +8 -8
- package/dist/bin/pushary-stop-hook.js +7 -7
- package/dist/bin/pushary-stopfailure-hook.js +7 -7
- package/dist/bin/pushary-upgrade.js +15 -15
- package/dist/bin/pushary-wait.js +3 -3
- package/dist/{chunk-SS72Z7FV.js → chunk-253WC6O5.js} +1 -1
- package/dist/{chunk-T3LRWZKI.js → chunk-2LPEZYMC.js} +1 -1
- package/dist/{chunk-O5B7EK5U.js → chunk-34GN3IPX.js} +1 -1
- package/dist/{chunk-V6N4Q223.js → chunk-3CUDMTL4.js} +1 -1
- package/dist/{chunk-VZKXBFUA.js → chunk-3HFK3UXM.js} +1 -1
- package/dist/{chunk-543KNJKC.js → chunk-3SQYKLBW.js} +3 -3
- package/dist/{chunk-ATBKJNWS.js → chunk-6EKQZA6R.js} +5 -1
- package/dist/{chunk-NKHEHEUM.js → chunk-6XUABEX2.js} +1 -1
- package/dist/{chunk-I5L6SK5V.js → chunk-BLODMQKJ.js} +1 -1
- package/dist/{chunk-DKDDLF5P.js → chunk-DKWM5T3P.js} +1 -1
- package/dist/{chunk-4XZCDSNG.js → chunk-FXIL7RCL.js} +5 -5
- package/dist/{chunk-BAE45YLP.js → chunk-FZ73G2RO.js} +3 -3
- package/dist/{chunk-PMHTCDEO.js → chunk-IUN6VOKV.js} +1 -1
- package/dist/{chunk-OFRNZWC4.js → chunk-IUWRVTQK.js} +1 -1
- package/dist/{chunk-EJT2ER6G.js → chunk-J4K75V4D.js} +46 -35
- package/dist/{chunk-GKAKAOKC.js → chunk-JHJHCMAF.js} +65 -12
- package/dist/{chunk-J2JZJZKB.js → chunk-KVE5RCQL.js} +1 -1
- package/dist/{chunk-A4O5ROZK.js → chunk-MZGWKIUZ.js} +2 -2
- package/dist/chunk-NCVIWGVA.js +18 -0
- package/dist/{chunk-C623LWJR.js → chunk-OLAGKKSR.js} +1 -1
- package/dist/{chunk-EA3E74P2.js → chunk-PZRWFWSR.js} +6 -6
- package/dist/{chunk-VA7VUNRD.js → chunk-Q223OM5J.js} +58 -8
- package/dist/{chunk-GMI5QZLG.js → chunk-QA72MKIT.js} +5 -1
- package/dist/{chunk-AIGDBRIJ.js → chunk-SYUIXXQW.js} +1 -1
- package/dist/{chunk-TF247BET.js → chunk-TEZ2FZD6.js} +1 -1
- package/dist/{chunk-KOPUTJQC.js → chunk-TG3T3M4K.js} +1 -1
- package/dist/{chunk-FX5QSMPY.js → chunk-TRVIRETT.js} +1 -1
- package/dist/{chunk-U5TQCQ25.js → chunk-TY5HVMXT.js} +1 -1
- package/dist/chunk-UDBJHQMG.js +240 -0
- package/dist/{chunk-PZA7G4CN.js → chunk-X4ZUUFKV.js} +1 -0
- package/dist/{chunk-L4C2JXJS.js → chunk-X6RXX2NF.js} +5 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.js +11 -10
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.89.11
|
|
4
|
+
|
|
5
|
+
### Phone-spawned Codex and Gemini sessions record their conversation again
|
|
6
|
+
|
|
7
|
+
Both bridges relay their turns by default and stop only when told to. The
|
|
8
|
+
launchers told them to stop on every run: the value they passed was an equality
|
|
9
|
+
test against an environment variable nobody sets, which is false whenever it is
|
|
10
|
+
absent and never the "unset" the bridge was looking for. The relay was therefore
|
|
11
|
+
never built, and a session started from the phone had nothing to show but its
|
|
12
|
+
task title. The opt-out is now passed only when it is asked for.
|
|
13
|
+
|
|
14
|
+
### An agent standing by stops reading as working
|
|
15
|
+
|
|
16
|
+
`pushary claude` and the Gemini bridge reported that a session had started, that
|
|
17
|
+
it was still alive, and that it had ended, with nothing in between. Their
|
|
18
|
+
heartbeat kept resetting the clock that would otherwise have aged the session out
|
|
19
|
+
of "active", so the phone showed a working agent for one that had been waiting at
|
|
20
|
+
its prompt since its last reply. That is exactly when someone is looking at the
|
|
21
|
+
screen deciding whether to send it something. Both now report the turn boundary
|
|
22
|
+
they already detect, as the Codex bridge always has.
|
|
23
|
+
|
|
24
|
+
### Taking a session over from your phone says so, and is ready when you ask
|
|
25
|
+
|
|
26
|
+
An instruction from the phone hands the terminal to Pushary so it can drive an
|
|
27
|
+
agent that would otherwise sit idle. That has not changed. The handover is now
|
|
28
|
+
announced before the session is signalled, instead of the terminal appearing to
|
|
29
|
+
lose its agent for no visible reason. The Agent SDK is resolved when the session
|
|
30
|
+
starts rather than during the handover, so the wait is gone, and a machine that
|
|
31
|
+
cannot reach npm reports the instruction as undelivered instead of accepting it
|
|
32
|
+
and then dropping it. An instruction that is dropped now says it was.
|
|
33
|
+
|
|
34
|
+
## 0.89.10
|
|
35
|
+
|
|
36
|
+
### Phone control delivery is crash-safe
|
|
37
|
+
|
|
38
|
+
Phone instructions now use per-command receipts across polling and relay
|
|
39
|
+
delivery. A process restart acknowledges the recorded outcome instead of
|
|
40
|
+
silently losing or repeating an instruction, multiple queued commands no longer
|
|
41
|
+
overwrite each other, and expired receipts are pruned when new commands arrive.
|
|
42
|
+
|
|
43
|
+
### Setup and clean reverse only Pushary-owned changes
|
|
44
|
+
|
|
45
|
+
Setup records the Claude and Hermes configuration it replaces. Clean restores
|
|
46
|
+
that prior shape without deleting later user edits, and retains the receipt when
|
|
47
|
+
service removal, Hermes rollback, or plugin uninstall is incomplete so a retry
|
|
48
|
+
can finish safely. Installations created by older releases keep their compatible
|
|
49
|
+
best-effort cleanup path.
|
|
50
|
+
|
|
51
|
+
### Native permission modes keep their intended owner
|
|
52
|
+
|
|
53
|
+
Codex now reads the permission mode it already reports, and Claude
|
|
54
|
+
`PermissionRequest` remains answerable from Pushary after the agent itself asks
|
|
55
|
+
for a human. Kill switches and standing denies still take precedence.
|
|
56
|
+
|
|
3
57
|
## 0.89.9
|
|
4
58
|
|
|
5
59
|
### Setup proves managed control reaches the server
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handleBell
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-TRVIRETT.js";
|
|
5
5
|
import {
|
|
6
6
|
readHookInput
|
|
7
7
|
} from "../chunk-6CUUA7HO.js";
|
|
8
|
-
import "../chunk-
|
|
8
|
+
import "../chunk-3CUDMTL4.js";
|
|
9
9
|
import "../chunk-BC3VCZ3E.js";
|
|
10
|
-
import "../chunk-
|
|
10
|
+
import "../chunk-IUWRVTQK.js";
|
|
11
11
|
import "../chunk-XX2V4QS5.js";
|
|
12
12
|
import "../chunk-2UMNXADU.js";
|
|
13
|
-
import "../chunk-
|
|
13
|
+
import "../chunk-X6RXX2NF.js";
|
|
14
14
|
|
|
15
15
|
// bin/pushary-bell-hook.ts
|
|
16
16
|
var main = async () => {
|
package/dist/bin/pushary-bell.js
CHANGED
|
@@ -5,18 +5,18 @@ import {
|
|
|
5
5
|
liveAgentCount,
|
|
6
6
|
ring,
|
|
7
7
|
upgradeThreshold
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-TRVIRETT.js";
|
|
9
9
|
import {
|
|
10
10
|
guardBinary
|
|
11
|
-
} from "../chunk-
|
|
12
|
-
import "../chunk-
|
|
11
|
+
} from "../chunk-SYUIXXQW.js";
|
|
12
|
+
import "../chunk-X4ZUUFKV.js";
|
|
13
13
|
import {
|
|
14
14
|
claudeSettings
|
|
15
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-QA72MKIT.js";
|
|
16
16
|
import {
|
|
17
17
|
createIo
|
|
18
18
|
} from "../chunk-5RUIFDTP.js";
|
|
19
|
-
import "../chunk-
|
|
19
|
+
import "../chunk-3CUDMTL4.js";
|
|
20
20
|
import {
|
|
21
21
|
parseFlags
|
|
22
22
|
} from "../chunk-GMXKITVA.js";
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
import "../chunk-3XTFX4CL.js";
|
|
27
27
|
import "../chunk-BC3VCZ3E.js";
|
|
28
28
|
import "../chunk-DINDL2CF.js";
|
|
29
|
-
import "../chunk-
|
|
29
|
+
import "../chunk-IUWRVTQK.js";
|
|
30
30
|
import "../chunk-7QLSKOSU.js";
|
|
31
31
|
import {
|
|
32
32
|
EXIT
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
writeJsonAtomic
|
|
37
37
|
} from "../chunk-XX2V4QS5.js";
|
|
38
38
|
import "../chunk-2UMNXADU.js";
|
|
39
|
-
import "../chunk-
|
|
39
|
+
import "../chunk-X6RXX2NF.js";
|
|
40
40
|
|
|
41
41
|
// src/bell/install.ts
|
|
42
42
|
import { join } from "path";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ensureDaemonRunning
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-3SQYKLBW.js";
|
|
5
5
|
import {
|
|
6
6
|
buildTranscriptRecords,
|
|
7
7
|
decodePublicKey,
|
|
@@ -9,16 +9,16 @@ import {
|
|
|
9
9
|
generateSessionKey,
|
|
10
10
|
uploadTranscriptRecords,
|
|
11
11
|
wrapSessionKey
|
|
12
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-IUN6VOKV.js";
|
|
13
13
|
import {
|
|
14
14
|
startProcessOwnership,
|
|
15
15
|
withSpawnProcessOwnership
|
|
16
|
-
} from "../chunk-
|
|
16
|
+
} from "../chunk-TEZ2FZD6.js";
|
|
17
17
|
import {
|
|
18
18
|
needsShell,
|
|
19
19
|
spawnClaude
|
|
20
20
|
} from "../chunk-XHKBHWLX.js";
|
|
21
|
-
import "../chunk-
|
|
21
|
+
import "../chunk-X4ZUUFKV.js";
|
|
22
22
|
import {
|
|
23
23
|
isDeferAnswer
|
|
24
24
|
} from "../chunk-YHG74UFF.js";
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
getPolicy,
|
|
28
28
|
repoKeyFor,
|
|
29
29
|
reportEvent
|
|
30
|
-
} from "../chunk-
|
|
30
|
+
} from "../chunk-FXIL7RCL.js";
|
|
31
31
|
import {
|
|
32
32
|
DEFAULT_SESSION,
|
|
33
33
|
askUser,
|
|
@@ -35,23 +35,24 @@ import {
|
|
|
35
35
|
markPendingCommandDelivered,
|
|
36
36
|
markPendingCommandFailed,
|
|
37
37
|
readPendingCommand,
|
|
38
|
+
readPendingCommandById,
|
|
38
39
|
removePendingCommand,
|
|
39
40
|
savePendingCommand,
|
|
40
41
|
waitForAnswer
|
|
41
|
-
} from "../chunk-
|
|
42
|
+
} from "../chunk-JHJHCMAF.js";
|
|
42
43
|
import {
|
|
43
44
|
deriveToolTarget,
|
|
44
45
|
describeToolCall,
|
|
45
46
|
scopePathFor
|
|
46
|
-
} from "../chunk-
|
|
47
|
+
} from "../chunk-TG3T3M4K.js";
|
|
47
48
|
import {
|
|
48
49
|
getMachineId
|
|
49
|
-
} from "../chunk-
|
|
50
|
-
import "../chunk-
|
|
50
|
+
} from "../chunk-DKWM5T3P.js";
|
|
51
|
+
import "../chunk-QA72MKIT.js";
|
|
51
52
|
import "../chunk-DINDL2CF.js";
|
|
52
53
|
import "../chunk-BSZYIAZL.js";
|
|
53
54
|
import "../chunk-SAF6HGAA.js";
|
|
54
|
-
import "../chunk-
|
|
55
|
+
import "../chunk-IUWRVTQK.js";
|
|
55
56
|
import "../chunk-7QLSKOSU.js";
|
|
56
57
|
import "../chunk-KZERVKTD.js";
|
|
57
58
|
import "../chunk-XX2V4QS5.js";
|
|
@@ -62,7 +63,7 @@ import {
|
|
|
62
63
|
import {
|
|
63
64
|
KILL_REASON,
|
|
64
65
|
resolveGate
|
|
65
|
-
} from "../chunk-
|
|
66
|
+
} from "../chunk-X6RXX2NF.js";
|
|
66
67
|
|
|
67
68
|
// src/wrapper/claudeBinary.ts
|
|
68
69
|
import { statSync } from "fs";
|
|
@@ -341,9 +342,13 @@ var BoundedInputQueue = class {
|
|
|
341
342
|
}
|
|
342
343
|
// Drop every buffered item without closing the queue. Used when phone control
|
|
343
344
|
// turns out to be unavailable (offline SDK setup) so a pending instruction does
|
|
344
|
-
// not make the local leg switch again into a mode that cannot run.
|
|
345
|
+
// not make the local leg switch again into a mode that cannot run. Returns how
|
|
346
|
+
// many were dropped, because these are somebody's words and the caller has to
|
|
347
|
+
// be able to say they are gone.
|
|
345
348
|
clear() {
|
|
349
|
+
const dropped = this.buffer.length;
|
|
346
350
|
this.buffer.length = 0;
|
|
351
|
+
return dropped;
|
|
347
352
|
}
|
|
348
353
|
get isClosed() {
|
|
349
354
|
return this.closed;
|
|
@@ -664,20 +669,8 @@ var pollAgent = async (apiKey, sessionId, wantCommand, signal, baseUrl = getBase
|
|
|
664
669
|
try {
|
|
665
670
|
const sessionKey = sessionId || DEFAULT_SESSION;
|
|
666
671
|
const receiptNamespace = `${apiKey}:${machineId}:claude_code`;
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
method: "POST",
|
|
670
|
-
headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
|
|
671
|
-
body: JSON.stringify({
|
|
672
|
-
...sessionId ? { sessionId } : {},
|
|
673
|
-
...machineId ? { machineId } : {},
|
|
674
|
-
...wantCommand && !storedCommand ? {} : { drain: false },
|
|
675
|
-
...wantCommand && !storedCommand && machineId ? { ackMode: true } : {}
|
|
676
|
-
}),
|
|
677
|
-
signal
|
|
678
|
-
});
|
|
679
|
-
if (!response.ok) return { command: null, mode: null };
|
|
680
|
-
const data = await response.json();
|
|
672
|
+
let storedCommand = machineId ? readPendingCommand(sessionKey, receiptNamespace) : null;
|
|
673
|
+
let acknowledgedCommandId = null;
|
|
681
674
|
const acknowledge = async (id, state) => {
|
|
682
675
|
const persisted = state === "delivered" ? markPendingCommandDelivered(sessionKey, id, receiptNamespace) : markPendingCommandFailed(sessionKey, id, receiptNamespace);
|
|
683
676
|
if (!persisted) return false;
|
|
@@ -700,6 +693,29 @@ var pollAgent = async (apiKey, sessionId, wantCommand, signal, baseUrl = getBase
|
|
|
700
693
|
return false;
|
|
701
694
|
}
|
|
702
695
|
};
|
|
696
|
+
if (storedCommand?.delivered || storedCommand?.failed) {
|
|
697
|
+
const acknowledged = await acknowledge(
|
|
698
|
+
storedCommand.id,
|
|
699
|
+
storedCommand.failed ? "failed" : "delivered"
|
|
700
|
+
);
|
|
701
|
+
if (acknowledged) {
|
|
702
|
+
acknowledgedCommandId = storedCommand.id;
|
|
703
|
+
storedCommand = null;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
const response = await fetch(`${baseUrl}/api/agent/poll`, {
|
|
707
|
+
method: "POST",
|
|
708
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
|
|
709
|
+
body: JSON.stringify({
|
|
710
|
+
...sessionId ? { sessionId } : {},
|
|
711
|
+
...machineId ? { machineId } : {},
|
|
712
|
+
...wantCommand && !storedCommand ? {} : { drain: false },
|
|
713
|
+
...wantCommand && !storedCommand && machineId ? { ackMode: true } : {}
|
|
714
|
+
}),
|
|
715
|
+
signal
|
|
716
|
+
});
|
|
717
|
+
if (!response.ok) return { command: null, mode: null };
|
|
718
|
+
const data = await response.json();
|
|
703
719
|
const claimedCommand = (id, text) => ({
|
|
704
720
|
id,
|
|
705
721
|
text,
|
|
@@ -707,15 +723,10 @@ var pollAgent = async (apiKey, sessionId, wantCommand, signal, baseUrl = getBase
|
|
|
707
723
|
});
|
|
708
724
|
let command = typeof data.command === "string" && data.command.length > 0 ? data.command : null;
|
|
709
725
|
if (storedCommand) {
|
|
710
|
-
|
|
711
|
-
await acknowledge(storedCommand.id, storedCommand.failed ? "failed" : "delivered");
|
|
712
|
-
command = null;
|
|
713
|
-
} else {
|
|
714
|
-
command = claimedCommand(storedCommand.id, storedCommand.text);
|
|
715
|
-
}
|
|
726
|
+
command = storedCommand.delivered || storedCommand.failed ? null : claimedCommand(storedCommand.id, storedCommand.text);
|
|
716
727
|
} else if (data.command && typeof data.command === "object") {
|
|
717
728
|
const claimed = data.command;
|
|
718
|
-
if (typeof claimed.id === "string" && typeof claimed.text === "string") {
|
|
729
|
+
if (typeof claimed.id === "string" && typeof claimed.text === "string" && claimed.id !== acknowledgedCommandId) {
|
|
719
730
|
const saved = savePendingCommand(sessionKey, {
|
|
720
731
|
id: claimed.id,
|
|
721
732
|
text: claimed.text,
|
|
@@ -780,6 +791,7 @@ var decodeServerFrame = (raw) => {
|
|
|
780
791
|
var DEFAULT_HEARTBEAT_MS = 2e4;
|
|
781
792
|
var DEFAULT_PONG_TIMEOUT_MS = 1e4;
|
|
782
793
|
var DEFAULT_MAX_SEEN_COMMANDS = 256;
|
|
794
|
+
var COMMAND_RECEIPT_TTL_MS = 36e5;
|
|
783
795
|
var defaultBackoff = (attempt) => Math.min(1e3 * 2 ** attempt, 3e4);
|
|
784
796
|
var defaultSocketFactory = (url) => {
|
|
785
797
|
const WS = globalThis.WebSocket;
|
|
@@ -810,6 +822,17 @@ var createRelayClient = (options) => {
|
|
|
810
822
|
const seenCommands = [];
|
|
811
823
|
const seenSet = /* @__PURE__ */ new Set();
|
|
812
824
|
const log = (message) => options.log?.(message);
|
|
825
|
+
const receiptNamespace = `${options.apiKey}:${options.machineId}:${options.agentType}`;
|
|
826
|
+
const acknowledgeStored = (id) => {
|
|
827
|
+
const receipt = readPendingCommandById(
|
|
828
|
+
options.getSessionId() || DEFAULT_SESSION,
|
|
829
|
+
id,
|
|
830
|
+
receiptNamespace
|
|
831
|
+
);
|
|
832
|
+
if (!receipt || !receipt.delivered && !receipt.failed) return false;
|
|
833
|
+
sendFrame(receipt.failed ? { v: PROTOCOL_VERSION, t: "ack", id, state: "failed", failureCode: "consumer_rejected" } : { v: PROTOCOL_VERSION, t: "ack", id, state: "delivered" });
|
|
834
|
+
return true;
|
|
835
|
+
};
|
|
813
836
|
const clearTimers = () => {
|
|
814
837
|
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
815
838
|
if (pongTimer) clearTimeout(pongTimer);
|
|
@@ -870,12 +893,24 @@ var createRelayClient = (options) => {
|
|
|
870
893
|
sendFrame({ v: PROTOCOL_VERSION, t: "ack", id: frame.id, state: "delivered" });
|
|
871
894
|
break;
|
|
872
895
|
}
|
|
896
|
+
if (acknowledgeStored(frame.id)) break;
|
|
897
|
+
const sessionKey = options.getSessionId() || DEFAULT_SESSION;
|
|
898
|
+
if (!savePendingCommand(sessionKey, {
|
|
899
|
+
id: frame.id,
|
|
900
|
+
text: frame.text,
|
|
901
|
+
expiresAtMs: Date.now() + COMMAND_RECEIPT_TTL_MS,
|
|
902
|
+
delivered: false,
|
|
903
|
+
failed: true
|
|
904
|
+
}, receiptNamespace)) {
|
|
905
|
+
acknowledgeStored(frame.id);
|
|
906
|
+
break;
|
|
907
|
+
}
|
|
873
908
|
let accepted = false;
|
|
874
909
|
try {
|
|
875
910
|
accepted = options.onCommand(frame.text) !== false;
|
|
876
911
|
} catch {
|
|
877
912
|
}
|
|
878
|
-
if (accepted) {
|
|
913
|
+
if (accepted && markPendingCommandDelivered(sessionKey, frame.id, receiptNamespace)) {
|
|
879
914
|
rememberCommand(frame.id);
|
|
880
915
|
sendFrame({ v: PROTOCOL_VERSION, t: "ack", id: frame.id, state: "delivered" });
|
|
881
916
|
} else {
|
|
@@ -1097,7 +1132,12 @@ var runLocalLeg = (session, deps = {}) => {
|
|
|
1097
1132
|
const control = {
|
|
1098
1133
|
onPhoneCommand: () => {
|
|
1099
1134
|
if (!exitReason) exitReason = "switch";
|
|
1100
|
-
if (!legAbort.signal.aborted)
|
|
1135
|
+
if (!legAbort.signal.aborted) {
|
|
1136
|
+
process.stderr.write(
|
|
1137
|
+
"\n[pushary] instruction from your phone \u2014 taking this session over to run it (Ctrl-] hands the terminal back)\n"
|
|
1138
|
+
);
|
|
1139
|
+
legAbort.abort();
|
|
1140
|
+
}
|
|
1101
1141
|
},
|
|
1102
1142
|
stop: (reason) => {
|
|
1103
1143
|
if (!exitReason) exitReason = { type: "exit", code: reason === "signal" ? 130 : 0 };
|
|
@@ -1451,6 +1491,7 @@ var runDualMode = async (binary, args2, startingMode) => {
|
|
|
1451
1491
|
} else if (message.type === "result") {
|
|
1452
1492
|
if (typeof message.total_cost_usd === "number") totalCostUsd = message.total_cost_usd;
|
|
1453
1493
|
process.stderr.write("[pushary] turn complete \u2014 standing by for phone commands\n");
|
|
1494
|
+
void announcePresence("stop", "Turn complete");
|
|
1454
1495
|
}
|
|
1455
1496
|
};
|
|
1456
1497
|
const poller = startCommandPoller({
|
|
@@ -1515,6 +1556,7 @@ var runDualMode = async (binary, args2, startingMode) => {
|
|
|
1515
1556
|
void announcePresence("session_start", "Session ready \u2014 reachable from your phone");
|
|
1516
1557
|
ensurePresenceHeartbeat();
|
|
1517
1558
|
ensureTranscriptSync();
|
|
1559
|
+
void ensureSdk();
|
|
1518
1560
|
}
|
|
1519
1561
|
if (startingMode === "remote" && initialPrompt) input.push(userMessage(initialPrompt));
|
|
1520
1562
|
const exitCode = await driveModeLoop({
|
|
@@ -1524,13 +1566,19 @@ var runDualMode = async (binary, args2, startingMode) => {
|
|
|
1524
1566
|
const ready = await ensureSdk();
|
|
1525
1567
|
if (!ready) {
|
|
1526
1568
|
remoteAvailable = false;
|
|
1527
|
-
input.clear();
|
|
1569
|
+
const dropped = input.clear();
|
|
1528
1570
|
if (!noticedUnavailable) {
|
|
1529
1571
|
noticedUnavailable = true;
|
|
1530
1572
|
process.stderr.write(
|
|
1531
1573
|
"[pushary] could not start phone control (offline?); staying in the terminal. Relaunch when online to enable.\n"
|
|
1532
1574
|
);
|
|
1533
1575
|
}
|
|
1576
|
+
if (dropped > 0) {
|
|
1577
|
+
process.stderr.write(
|
|
1578
|
+
`[pushary] ${dropped} instruction(s) from your phone could not be delivered and were dropped.
|
|
1579
|
+
`
|
|
1580
|
+
);
|
|
1581
|
+
}
|
|
1534
1582
|
return "switch";
|
|
1535
1583
|
}
|
|
1536
1584
|
return runRemoteLeg(session, ready);
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
HERMES_CONFIG_SCRIPT,
|
|
4
|
+
resolveHermesPython,
|
|
5
|
+
runHermesPythonScript
|
|
6
|
+
} from "../chunk-UDBJHQMG.js";
|
|
2
7
|
import {
|
|
3
8
|
removeInstructionBlock,
|
|
4
9
|
shortenHome,
|
|
5
10
|
unregisterPluginLocation,
|
|
6
11
|
vscodeSettingsTargets
|
|
7
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-MZGWKIUZ.js";
|
|
8
13
|
import {
|
|
9
14
|
OPENCODE_PLUGIN_SIBLING,
|
|
10
15
|
OPENCODE_PLUGIN_SIBLING_CONTENTS,
|
|
@@ -14,25 +19,28 @@ import {
|
|
|
14
19
|
openCodePluginDir,
|
|
15
20
|
openCodePluginFile,
|
|
16
21
|
removeOpenCodeMcpServer
|
|
17
|
-
} from "../chunk-
|
|
22
|
+
} from "../chunk-34GN3IPX.js";
|
|
18
23
|
import {
|
|
24
|
+
isClaudeMcpSnapshot,
|
|
19
25
|
removeClaudeMcpServers,
|
|
20
26
|
removeGeminiSettings,
|
|
21
|
-
removePusharySettings
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
import "../chunk-
|
|
27
|
+
removePusharySettings,
|
|
28
|
+
restoreClaudeMcpServer
|
|
29
|
+
} from "../chunk-Q223OM5J.js";
|
|
30
|
+
import "../chunk-6EKQZA6R.js";
|
|
31
|
+
import "../chunk-SYUIXXQW.js";
|
|
25
32
|
import {
|
|
26
33
|
disableDaemonService
|
|
27
|
-
} from "../chunk-
|
|
34
|
+
} from "../chunk-3SQYKLBW.js";
|
|
28
35
|
import "../chunk-XHKBHWLX.js";
|
|
29
36
|
import {
|
|
30
37
|
execNpm
|
|
31
|
-
} from "../chunk-
|
|
32
|
-
import "../chunk-
|
|
33
|
-
import "../chunk-
|
|
38
|
+
} from "../chunk-X4ZUUFKV.js";
|
|
39
|
+
import "../chunk-TG3T3M4K.js";
|
|
40
|
+
import "../chunk-DKWM5T3P.js";
|
|
34
41
|
import {
|
|
35
42
|
claudeJson,
|
|
43
|
+
claudeMcpReceipt,
|
|
36
44
|
claudeSettings,
|
|
37
45
|
claudeSettingsLocal,
|
|
38
46
|
claudeSkillDir,
|
|
@@ -46,11 +54,12 @@ import {
|
|
|
46
54
|
cursorUserMcp,
|
|
47
55
|
geminiMd,
|
|
48
56
|
geminiSettings,
|
|
57
|
+
hermesConfigReceipt,
|
|
49
58
|
pusharyDir,
|
|
50
59
|
removeCodexHookTrust,
|
|
51
60
|
removeCodexHooks,
|
|
52
61
|
vscodePluginDir
|
|
53
|
-
} from "../chunk-
|
|
62
|
+
} from "../chunk-QA72MKIT.js";
|
|
54
63
|
import {
|
|
55
64
|
rejectUnknownFlags
|
|
56
65
|
} from "../chunk-GMXKITVA.js";
|
|
@@ -71,13 +80,12 @@ import "../chunk-2UMNXADU.js";
|
|
|
71
80
|
import {
|
|
72
81
|
CURSOR_GATE_EVENTS,
|
|
73
82
|
HOOK_LOCATOR_BINARY
|
|
74
|
-
} from "../chunk-
|
|
83
|
+
} from "../chunk-X6RXX2NF.js";
|
|
75
84
|
|
|
76
85
|
// bin/pushary-clean.ts
|
|
77
86
|
import { existsSync, readFileSync, copyFileSync, readdirSync } from "fs";
|
|
78
87
|
import { join } from "path";
|
|
79
88
|
import { homedir, tmpdir } from "os";
|
|
80
|
-
import { execSync as execSync2 } from "child_process";
|
|
81
89
|
import { confirm } from "@inquirer/prompts";
|
|
82
90
|
import { parse as parseTOML, stringify as stringifyTOML } from "smol-toml";
|
|
83
91
|
|
|
@@ -128,6 +136,8 @@ var incompleteClean = false;
|
|
|
128
136
|
var CLAUDE_SETTINGS = claudeSettings();
|
|
129
137
|
var CLAUDE_SETTINGS_LOCAL = claudeSettingsLocal();
|
|
130
138
|
var CLAUDE_JSON = claudeJson();
|
|
139
|
+
var CLAUDE_MCP_RECEIPT = claudeMcpReceipt();
|
|
140
|
+
var HERMES_CONFIG_RECEIPT = hermesConfigReceipt();
|
|
131
141
|
var SKILL_DIR = claudeSkillDir();
|
|
132
142
|
var CURSOR_MCP = cursorUserMcp();
|
|
133
143
|
var CURSOR_PLUGIN_DIR = cursorPluginDir();
|
|
@@ -140,22 +150,6 @@ var removalScope = () => ({
|
|
|
140
150
|
keepAppWiring: true,
|
|
141
151
|
keepMcpServer: macAppInstalled()
|
|
142
152
|
});
|
|
143
|
-
var resolveHermesPython = () => {
|
|
144
|
-
try {
|
|
145
|
-
const launcher = execSync2("command -v hermes", { encoding: "utf-8", stdio: "pipe", timeout: 5e3 }).trim();
|
|
146
|
-
if (launcher) {
|
|
147
|
-
const shebang = readFileSync(launcher, "utf-8").split("\n", 1)[0];
|
|
148
|
-
if (shebang.startsWith("#!")) {
|
|
149
|
-
const interpreter = shebang.slice(2).trim().split(/\s+/)[0];
|
|
150
|
-
if (interpreter && /python/i.test(interpreter) && existsSync(interpreter)) return interpreter;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
} catch {
|
|
154
|
-
}
|
|
155
|
-
const venvPython = join(homedir(), ".hermes", "hermes-agent", "venv", "bin", "python3");
|
|
156
|
-
if (existsSync(venvPython)) return venvPython;
|
|
157
|
-
return null;
|
|
158
|
-
};
|
|
159
153
|
var readJson = (path) => {
|
|
160
154
|
try {
|
|
161
155
|
return JSON.parse(readFileSync(path, "utf-8"));
|
|
@@ -305,12 +299,21 @@ var main = async () => {
|
|
|
305
299
|
if (keepShared) {
|
|
306
300
|
console.log(` ${skip} Claude Code MCP servers ${dim("(kept: the Mac app uses it)")}`);
|
|
307
301
|
} else if (claudeJson2) {
|
|
308
|
-
|
|
302
|
+
const rawReceipt = readJson(CLAUDE_MCP_RECEIPT);
|
|
303
|
+
const receipt = isClaudeMcpSnapshot(rawReceipt) ? rawReceipt : null;
|
|
304
|
+
if (existsSync(CLAUDE_MCP_RECEIPT) && !receipt) {
|
|
305
|
+
incompleteClean = true;
|
|
306
|
+
console.log(` ${skip} Claude Code MCP servers ${dim("(setup receipt is unreadable; left unchanged)")}`);
|
|
307
|
+
} else if (receipt && restoreClaudeMcpServer(claudeJson2, receipt) || !receipt && removeClaudeMcpServers(claudeJson2)) {
|
|
309
308
|
writeJson(CLAUDE_JSON, claudeJson2);
|
|
310
|
-
console.log(` ${check} Claude Code MCP servers ${dim("(removed from ~/.claude.json)")}`);
|
|
309
|
+
console.log(` ${check} Claude Code MCP servers ${dim(receipt ? "(previous entry restored)" : "(removed from ~/.claude.json)")}`);
|
|
311
310
|
} else {
|
|
312
|
-
|
|
311
|
+
if (receipt) incompleteClean = true;
|
|
312
|
+
console.log(` ${skip} Claude Code MCP servers ${dim(receipt ? "(changed after setup; left unchanged)" : "(no pushary entries)")}`);
|
|
313
313
|
}
|
|
314
|
+
} else if (existsSync(CLAUDE_MCP_RECEIPT)) {
|
|
315
|
+
incompleteClean = true;
|
|
316
|
+
console.log(` ${skip} Claude Code MCP servers ${dim("(config missing; setup receipt kept for retry)")}`);
|
|
314
317
|
} else {
|
|
315
318
|
console.log(` ${skip} Claude Code MCP servers ${dim("(not found)")}`);
|
|
316
319
|
}
|
|
@@ -487,19 +490,23 @@ var main = async () => {
|
|
|
487
490
|
}
|
|
488
491
|
const hermesPython = resolveHermesPython();
|
|
489
492
|
if (hermesPython) {
|
|
490
|
-
const
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
console.log(` ${check} Hermes config ${dim("(plugin disabled, clarify toolset restored)")}`);
|
|
494
|
-
} catch {
|
|
493
|
+
const configUpdated = dryRun ? (wouldNote("run ", "hermes plugin config restore"), true) : runHermesPythonScript(hermesPython, HERMES_CONFIG_SCRIPT, "clean", HERMES_CONFIG_RECEIPT);
|
|
494
|
+
if (!configUpdated) {
|
|
495
|
+
incompleteClean = true;
|
|
495
496
|
console.log(` ${skip} Hermes config ${dim("(could not update config.yaml)")}`);
|
|
497
|
+
} else {
|
|
498
|
+
console.log(` ${check} Hermes config ${dim("(setup-owned changes restored)")}`);
|
|
499
|
+
try {
|
|
500
|
+
guardedExec(`"${hermesPython}" -m pip uninstall -y hermes-plugin-pushary`, { stdio: "pipe", timeout: 6e4 }, "pip uninstall hermes-plugin-pushary");
|
|
501
|
+
console.log(` ${check} Hermes plugin ${dim("(pip uninstalled)")}`);
|
|
502
|
+
} catch {
|
|
503
|
+
incompleteClean = true;
|
|
504
|
+
console.log(` ${skip} Hermes plugin ${dim("(could not uninstall; setup receipt kept for retry)")}`);
|
|
505
|
+
}
|
|
496
506
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
} catch {
|
|
501
|
-
console.log(` ${skip} Hermes plugin ${dim("(not installed)")}`);
|
|
502
|
-
}
|
|
507
|
+
} else if (existsSync(HERMES_CONFIG_RECEIPT)) {
|
|
508
|
+
incompleteClean = true;
|
|
509
|
+
console.log(` ${skip} Hermes ${dim("(runtime not found; setup receipt kept for retry)")}`);
|
|
503
510
|
} else {
|
|
504
511
|
console.log(` ${skip} Hermes ${dim("(not found)")}`);
|
|
505
512
|
}
|
|
@@ -534,7 +541,7 @@ var main = async () => {
|
|
|
534
541
|
let removed = 0;
|
|
535
542
|
const kept = [];
|
|
536
543
|
for (const entry of readdirSync(PUSHARY_DIR)) {
|
|
537
|
-
if (keepShared && isAppOwned(entry)) {
|
|
544
|
+
if (keepShared && isAppOwned(entry) || entry === "setup" && (keepShared || incompleteClean)) {
|
|
538
545
|
kept.push(entry);
|
|
539
546
|
continue;
|
|
540
547
|
}
|
|
@@ -548,7 +555,7 @@ var main = async () => {
|
|
|
548
555
|
console.log(` ${skip} Local state ${dim("(nothing of ours in ~/.pushary)")}`);
|
|
549
556
|
}
|
|
550
557
|
if (kept.length > 0) {
|
|
551
|
-
console.log(` ${dim(`left ${kept.sort().join(", ")} for the Mac app`)}`);
|
|
558
|
+
console.log(` ${dim(`left ${kept.sort().join(", ")} for ${keepShared ? "the Mac app" : "a cleanup retry"}`)}`);
|
|
552
559
|
}
|
|
553
560
|
} else {
|
|
554
561
|
console.log(` ${skip} Local state ${dim("(~/.pushary not found)")}`);
|
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "../chunk-
|
|
2
|
+
import "../chunk-6XUABEX2.js";
|
|
3
3
|
import {
|
|
4
4
|
LiveTranscriptRelay,
|
|
5
5
|
liveSessionSleep,
|
|
6
6
|
postLiveSession,
|
|
7
7
|
postLiveSessionUntilAccepted,
|
|
8
8
|
startLiveSessionHeartbeat
|
|
9
|
-
} from "../chunk-
|
|
10
|
-
import "../chunk-
|
|
9
|
+
} from "../chunk-OLAGKKSR.js";
|
|
10
|
+
import "../chunk-IUN6VOKV.js";
|
|
11
11
|
import {
|
|
12
12
|
startProcessOwnership,
|
|
13
13
|
withSpawnProcessOwnership
|
|
14
|
-
} from "../chunk-
|
|
14
|
+
} from "../chunk-TEZ2FZD6.js";
|
|
15
15
|
import {
|
|
16
16
|
spawnClaude
|
|
17
17
|
} from "../chunk-XHKBHWLX.js";
|
|
18
18
|
import {
|
|
19
19
|
resolveBinary
|
|
20
|
-
} from "../chunk-
|
|
20
|
+
} from "../chunk-X4ZUUFKV.js";
|
|
21
21
|
import {
|
|
22
22
|
getMachineId
|
|
23
|
-
} from "../chunk-
|
|
24
|
-
import "../chunk-
|
|
23
|
+
} from "../chunk-DKWM5T3P.js";
|
|
24
|
+
import "../chunk-QA72MKIT.js";
|
|
25
25
|
import "../chunk-XX2V4QS5.js";
|
|
26
26
|
import {
|
|
27
27
|
getApiKey,
|
|
28
28
|
getBaseUrl
|
|
29
29
|
} from "../chunk-2UMNXADU.js";
|
|
30
|
-
import "../chunk-
|
|
30
|
+
import "../chunk-X6RXX2NF.js";
|
|
31
31
|
|
|
32
32
|
// bin/pushary-codex-bridge.ts
|
|
33
33
|
import { existsSync } from "fs";
|
|
@@ -371,7 +371,13 @@ var outcome = await withSpawnProcessOwnership(spawnId, () => runCodexBridge({
|
|
|
371
371
|
args: ["app-server", "--stdio"]
|
|
372
372
|
},
|
|
373
373
|
signal: abort.signal,
|
|
374
|
-
transcripts
|
|
374
|
+
// The bridge's own default is ON (`transcripts?: boolean`, and it opts out only
|
|
375
|
+
// on an explicit `false`), but `=== '1'` can never produce `undefined`, so an
|
|
376
|
+
// unset env var forced the opt-out on every spawn and the live relay was never
|
|
377
|
+
// built. Pass the opt-out only when the user asks for one. The relay stays a
|
|
378
|
+
// hard no-op until this account's phone has registered a public key, and what
|
|
379
|
+
// it sends is encrypted to that key on this machine.
|
|
380
|
+
...process.env.PUSHARY_TRANSCRIPTS === "0" ? { transcripts: false } : {}
|
|
375
381
|
}));
|
|
376
382
|
for (const signal of signals) process.off(signal, stop);
|
|
377
383
|
process.exit(outcome === "stopped" ? 0 : 1);
|