pushary 1.9.73 → 1.9.76

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.
Files changed (127) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/{api-JVWXPKEW.js → api-YDCHRRA4.js} +10 -10
  3. package/dist/bin/pushary-bell-hook.js +27 -25
  4. package/dist/bin/pushary-bell.js +14 -14
  5. package/dist/bin/pushary-claude.js +55 -82
  6. package/dist/bin/pushary-clean.js +37 -37
  7. package/dist/bin/pushary-codex-bridge.js +24 -24
  8. package/dist/bin/pushary-codex-hook.js +36 -34
  9. package/dist/bin/pushary-codex-session.js +8 -8
  10. package/dist/bin/pushary-codex.js +15 -15
  11. package/dist/bin/pushary-connect.js +26 -26
  12. package/dist/bin/pushary-cowork.js +5 -5
  13. package/dist/bin/pushary-daemon.js +1293 -63
  14. package/dist/bin/pushary-disconnect.js +31 -31
  15. package/dist/bin/pushary-display-hook.d.ts +1 -0
  16. package/dist/bin/pushary-display-hook.js +58 -0
  17. package/dist/bin/pushary-doctor.js +50 -50
  18. package/dist/bin/pushary-elicitation-hook.js +29 -27
  19. package/dist/bin/pushary-gemini-bridge.js +16 -16
  20. package/dist/bin/pushary-gemini-hook.js +31 -29
  21. package/dist/bin/pushary-hook.js +31 -29
  22. package/dist/bin/pushary-login.js +20 -20
  23. package/dist/bin/pushary-logout.js +30 -30
  24. package/dist/bin/pushary-mcp.js +14 -14
  25. package/dist/bin/pushary-mode.js +18 -18
  26. package/dist/bin/pushary-notification-hook.js +26 -24
  27. package/dist/bin/pushary-opencode-hook.js +30 -28
  28. package/dist/bin/pushary-permission-denied-hook.js +31 -29
  29. package/dist/bin/pushary-permission-hook.js +31 -29
  30. package/dist/bin/pushary-post-hook.js +32 -29
  31. package/dist/bin/pushary-prompt-hook.js +32 -29
  32. package/dist/bin/pushary-session-end-hook.js +28 -26
  33. package/dist/bin/pushary-session-start-hook.js +36 -34
  34. package/dist/bin/pushary-setup.js +66 -282
  35. package/dist/bin/pushary-stats.js +6 -6
  36. package/dist/bin/pushary-status.js +31 -31
  37. package/dist/bin/pushary-stop-hook.js +32 -26
  38. package/dist/bin/pushary-stopfailure-hook.js +26 -24
  39. package/dist/bin/pushary-suggestions.js +18 -18
  40. package/dist/bin/pushary-transcript-register.js +18 -16
  41. package/dist/bin/pushary-transcripts.js +20 -20
  42. package/dist/bin/pushary-upgrade.js +45 -45
  43. package/dist/bin/pushary-wait.js +18 -18
  44. package/dist/bin/pushary.js +7 -7
  45. package/dist/{chunk-F3WI32HG.js → chunk-2BK2EQOB.js} +7 -7
  46. package/dist/{chunk-ZBZBYUNZ.js → chunk-2QFYEB3P.js} +5 -5
  47. package/dist/{chunk-UIF7HWHX.js → chunk-3YU6NIIL.js} +4 -4
  48. package/dist/{chunk-L2FQBHIR.js → chunk-47TIRGS5.js} +2 -2
  49. package/dist/{chunk-AXZ67XVN.js → chunk-4LX5QUFV.js} +1 -1
  50. package/dist/{chunk-YZ6DMGZZ.js → chunk-57MQNSM5.js} +1 -1
  51. package/dist/{chunk-BLX6KRK5.js → chunk-5FV472Q2.js} +1 -1
  52. package/dist/{chunk-VBSPP4QY.js → chunk-5KPMDLXB.js} +3 -3
  53. package/dist/{chunk-S63VIM7E.js → chunk-5M5F4IWR.js} +2 -2
  54. package/dist/{chunk-7FROPPIH.js → chunk-5NW7VM35.js} +2 -2
  55. package/dist/{chunk-EITOMPFB.js → chunk-74ONBRPF.js} +4 -4
  56. package/dist/{chunk-7S46LCKD.js → chunk-7KYQ24FI.js} +3 -2
  57. package/dist/{chunk-NGNBX3YH.js → chunk-7LLC4R66.js} +4 -4
  58. package/dist/{chunk-AC5TVOAH.js → chunk-7SFP6Y5U.js} +373 -93
  59. package/dist/{chunk-CMYFV3VC.js → chunk-7ZOWQBHQ.js} +5 -5
  60. package/dist/{chunk-2QB7HJVJ.js → chunk-B2PFCS72.js} +2 -2
  61. package/dist/{chunk-UUCRWEEU.js → chunk-BUR2LYQI.js} +2 -2
  62. package/dist/{chunk-W47XZJSH.js → chunk-C2AR7QXU.js} +2 -2
  63. package/dist/{chunk-FK3JSDH5.js → chunk-CHIOUBPB.js} +13 -13
  64. package/dist/{chunk-TTGBS5JB.js → chunk-CMNB4IZO.js} +3 -3
  65. package/dist/{chunk-4UIR25GA.js → chunk-CUD6SYDG.js} +1 -1
  66. package/dist/{chunk-WJJVCDEU.js → chunk-DB3NOEDW.js} +6 -6
  67. package/dist/{chunk-PGJMSREY.js → chunk-DZF2M3LM.js} +8 -8
  68. package/dist/{chunk-2NG3ANMT.js → chunk-E2VJ7B7D.js} +9 -9
  69. package/dist/{chunk-S7VMHSJW.js → chunk-E7U4YM4A.js} +5 -5
  70. package/dist/{chunk-XKQ7CQGG.js → chunk-F7R7M5MG.js} +1 -1
  71. package/dist/{chunk-FAKEKFCI.js → chunk-FAGQAR6I.js} +5 -3
  72. package/dist/{chunk-3JJI5O5N.js → chunk-GII565PB.js} +1 -1
  73. package/dist/{chunk-WXV7J3DG.js → chunk-GJVV2EUC.js} +3 -3
  74. package/dist/{chunk-Z7AIYWZ2.js → chunk-IRLHN5UP.js} +236 -20
  75. package/dist/{chunk-NJSW2REO.js → chunk-IRN6JLSM.js} +4 -4
  76. package/dist/{chunk-JC7XUZST.js → chunk-ITLWEGLE.js} +31 -5
  77. package/dist/{chunk-7M4AA7QF.js → chunk-JNV5LOZH.js} +4 -4
  78. package/dist/{chunk-IN3MPEFA.js → chunk-JZ5KV2FK.js} +5 -5
  79. package/dist/{chunk-QAWWTRDD.js → chunk-KGWLDNNT.js} +4 -4
  80. package/dist/{chunk-O4EAMVSC.js → chunk-KWGOKO4E.js} +5 -5
  81. package/dist/{chunk-KDQLKND5.js → chunk-M5VJ3HXB.js} +6 -6
  82. package/dist/{chunk-R5PZPROX.js → chunk-M7JFGDGP.js} +1 -1
  83. package/dist/{chunk-N4VRSFRZ.js → chunk-MAWC4PJV.js} +5 -5
  84. package/dist/chunk-MHUKYYYR.js +239 -0
  85. package/dist/{chunk-56GK65Z6.js → chunk-MMVIKXCP.js} +3 -3
  86. package/dist/{chunk-CGWUVEOB.js → chunk-MTRZQR2R.js} +3 -3
  87. package/dist/{chunk-JYOR57ZW.js → chunk-NFUV25JZ.js} +3 -3
  88. package/dist/{chunk-EUHTC7K5.js → chunk-NJTAUEW6.js} +1 -1
  89. package/dist/{chunk-2JFEMKDG.js → chunk-NQA56AMZ.js} +6 -6
  90. package/dist/{chunk-AJWGZJFS.js → chunk-NVLKH5FS.js} +131 -28
  91. package/dist/{chunk-2HFC4W6J.js → chunk-PPD64MZW.js} +4 -3
  92. package/dist/{chunk-3RAWW5DH.js → chunk-QUGP76DQ.js} +6 -6
  93. package/dist/{chunk-PPKIL4EL.js → chunk-R3HXGHRQ.js} +1 -1
  94. package/dist/{chunk-3QBKMSOZ.js → chunk-S7C5NY4R.js} +1 -1
  95. package/dist/{chunk-EUEGCEA7.js → chunk-SWRNJ3BP.js} +3 -3
  96. package/dist/{chunk-RZVWEDIS.js → chunk-T2EJ3H6Y.js} +3 -3
  97. package/dist/{chunk-DT6VCMXS.js → chunk-TFYFQM5C.js} +2 -2
  98. package/dist/{chunk-GBR6E7AY.js → chunk-UEY4SHY2.js} +1 -1
  99. package/dist/{chunk-TFDMJHAB.js → chunk-UMBVIFP5.js} +1 -1
  100. package/dist/{chunk-TQ2L3ECX.js → chunk-USMUW3LI.js} +2 -2
  101. package/dist/{chunk-VJUDBZGC.js → chunk-USZJJWZL.js} +2 -2
  102. package/dist/{chunk-4LAD5A2Q.js → chunk-VELPOV2F.js} +4 -4
  103. package/dist/{chunk-JNTYPMVU.js → chunk-VZSSL6US.js} +10 -10
  104. package/dist/{chunk-KSLBS3II.js → chunk-W5AUTVVL.js} +6 -5
  105. package/dist/{chunk-OJ3ZD5VX.js → chunk-WK2L3XVQ.js} +1 -1
  106. package/dist/{chunk-PM2MHBMF.js → chunk-WLHHDDVA.js} +9 -4
  107. package/dist/{chunk-U3EBM73X.js → chunk-WRCM4XGY.js} +2 -2
  108. package/dist/{chunk-SQ5XCMPB.js → chunk-XJ5DYPTM.js} +4 -4
  109. package/dist/{chunk-PNXNWZ5P.js → chunk-XLGD5LDG.js} +3 -3
  110. package/dist/{chunk-7RX5TOU3.js → chunk-Y2CYSVWU.js} +6 -6
  111. package/dist/{chunk-AVHL2ZTS.js → chunk-Y4OBAK4I.js} +459 -172
  112. package/dist/{chunk-JWBZUTYF.js → chunk-YDPGBBV7.js} +5 -5
  113. package/dist/{chunk-FB262EOM.js → chunk-YLB4AGZJ.js} +1 -1
  114. package/dist/{chunk-QMPM3CAN.js → chunk-ZY25SNFQ.js} +1 -1
  115. package/dist/{codex-app-cards-switch-M7VH7AZH.js → codex-app-cards-switch-3OUTABVU.js} +11 -11
  116. package/dist/{codex-desktop-launch-6DASTBBA.js → codex-desktop-launch-JH3E2CIA.js} +1 -1
  117. package/dist/{codex-session-launch-2XSMFA4K.js → codex-session-launch-GR363OQN.js} +18 -18
  118. package/dist/{config-7QQOIFTW.js → config-R43UFHX7.js} +3 -3
  119. package/dist/{global-upgrade-XAKX26AN.js → global-upgrade-73GP4MZM.js} +10 -10
  120. package/dist/{identity-7AS5MBNM.js → identity-CA7CZGDL.js} +4 -4
  121. package/dist/{live-capabilities-DV4F7MIB.js → live-capabilities-RCGMQIIZ.js} +5 -5
  122. package/dist/{paths-GSMXYKT5.js → paths-H7VNNB46.js} +3 -3
  123. package/dist/{reapply-MR7UWBXR.js → reapply-3UIA3CUO.js} +27 -27
  124. package/dist/src/index.js +32 -30
  125. package/dist/{upgrade-lock-6OTHLCBZ.js → upgrade-lock-ZTLX5PDF.js} +4 -4
  126. package/dist/{usage-H53PO3VQ.js → usage-C53NVWC2.js} +3 -3
  127. package/package.json +4 -2
@@ -1,26 +1,30 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  inspectConfiguredManagedProviders
4
- } from "../chunk-JNTYPMVU.js";
4
+ } from "../chunk-VZSSL6US.js";
5
5
  import {
6
6
  HEARTBEAT_INTERVAL_MS,
7
7
  sendHeartbeat
8
- } from "../chunk-ZBZBYUNZ.js";
9
- import "../chunk-VJUDBZGC.js";
8
+ } from "../chunk-2QFYEB3P.js";
9
+ import "../chunk-USZJJWZL.js";
10
10
  import {
11
11
  readSignedOutDiagnosis
12
- } from "../chunk-2QB7HJVJ.js";
13
- import "../chunk-FAKEKFCI.js";
12
+ } from "../chunk-B2PFCS72.js";
13
+ import {
14
+ HERMES_INSTALL_MARKER_FILE,
15
+ defaultHermesHome,
16
+ parseHermesInstallMarker
17
+ } from "../chunk-FAGQAR6I.js";
14
18
  import {
15
19
  writeHuman
16
20
  } from "../chunk-QCGNGEGE.js";
17
- import "../chunk-KSLBS3II.js";
18
- import "../chunk-EUEGCEA7.js";
19
- import "../chunk-W47XZJSH.js";
20
- import "../chunk-3RAWW5DH.js";
21
+ import "../chunk-W5AUTVVL.js";
22
+ import "../chunk-SWRNJ3BP.js";
23
+ import "../chunk-C2AR7QXU.js";
24
+ import "../chunk-QUGP76DQ.js";
21
25
  import {
22
26
  exitOnHelpFlag
23
- } from "../chunk-OJ3ZD5VX.js";
27
+ } from "../chunk-WK2L3XVQ.js";
24
28
  import {
25
29
  codexBinaryIsBroken,
26
30
  detectProviderLiveCapabilities,
@@ -29,8 +33,8 @@ import {
29
33
  launchableProviders,
30
34
  resolveControlPolicy,
31
35
  trackProviderInventory
32
- } from "../chunk-EITOMPFB.js";
33
- import "../chunk-JYOR57ZW.js";
36
+ } from "../chunk-74ONBRPF.js";
37
+ import "../chunk-NFUV25JZ.js";
34
38
  import {
35
39
  SESSION_REAP_SILENCE_MS,
36
40
  describeIdleWindow,
@@ -38,16 +42,17 @@ import {
38
42
  listProcessProofs,
39
43
  ownsLiveProcess,
40
44
  silentSpawnedProofs
41
- } from "../chunk-PNXNWZ5P.js";
42
- import "../chunk-TFDMJHAB.js";
45
+ } from "../chunk-XLGD5LDG.js";
46
+ import "../chunk-UMBVIFP5.js";
43
47
  import {
44
48
  readLocalAutoUpdateReport
45
- } from "../chunk-TTGBS5JB.js";
49
+ } from "../chunk-CMNB4IZO.js";
46
50
  import {
47
51
  startRegisteredTranscripts
48
- } from "../chunk-AC5TVOAH.js";
49
- import "../chunk-FB262EOM.js";
50
- import "../chunk-UIF7HWHX.js";
52
+ } from "../chunk-7SFP6Y5U.js";
53
+ import "../chunk-YLB4AGZJ.js";
54
+ import "../chunk-MHUKYYYR.js";
55
+ import "../chunk-3YU6NIIL.js";
51
56
  import {
52
57
  DAEMON_LEASE_RENEW_INTERVAL_MS,
53
58
  acquireDaemonLease,
@@ -64,52 +69,1273 @@ import {
64
69
  requestDaemonHandoff,
65
70
  stopDaemonRunning,
66
71
  writeDaemonNeedsLogin
67
- } from "../chunk-WJJVCDEU.js";
68
- import "../chunk-QAWWTRDD.js";
69
- import "../chunk-2HFC4W6J.js";
70
- import "../chunk-BLX6KRK5.js";
72
+ } from "../chunk-DB3NOEDW.js";
73
+ import {
74
+ hookOwnerOf
75
+ } from "../chunk-KGWLDNNT.js";
76
+ import "../chunk-PPD64MZW.js";
77
+ import "../chunk-5FV472Q2.js";
71
78
  import "../chunk-NMUI3ZWG.js";
72
- import "../chunk-JC7XUZST.js";
73
- import "../chunk-S63VIM7E.js";
79
+ import "../chunk-ITLWEGLE.js";
80
+ import "../chunk-XMSDA5WA.js";
81
+ import "../chunk-5M5F4IWR.js";
74
82
  import {
75
83
  spawnClaude
76
84
  } from "../chunk-XHKBHWLX.js";
77
85
  import {
78
86
  readControlMode
79
- } from "../chunk-NGNBX3YH.js";
80
- import "../chunk-AJWGZJFS.js";
81
- import "../chunk-QMPM3CAN.js";
82
- import "../chunk-CMYFV3VC.js";
87
+ } from "../chunk-7LLC4R66.js";
88
+ import {
89
+ pendingCommandReceipts,
90
+ removePendingCommand
91
+ } from "../chunk-NVLKH5FS.js";
92
+ import "../chunk-ZY25SNFQ.js";
93
+ import "../chunk-7ZOWQBHQ.js";
83
94
  import "../chunk-POFBE6ZY.js";
84
- import "../chunk-7M4AA7QF.js";
85
- import "../chunk-S7VMHSJW.js";
86
- import "../chunk-O4EAMVSC.js";
95
+ import "../chunk-JNV5LOZH.js";
96
+ import "../chunk-E7U4YM4A.js";
97
+ import "../chunk-KWGOKO4E.js";
87
98
  import {
88
99
  getMachineId
89
- } from "../chunk-UUCRWEEU.js";
90
- import "../chunk-PPY5N2E6.js";
100
+ } from "../chunk-BUR2LYQI.js";
101
+ import "../chunk-5NW7VM35.js";
91
102
  import {
92
- EXIT
93
- } from "../chunk-KZERVKTD.js";
94
- import "../chunk-7FROPPIH.js";
95
- import "../chunk-7S46LCKD.js";
103
+ claudeConfigDir,
104
+ codexHome
105
+ } from "../chunk-7KYQ24FI.js";
96
106
  import {
97
- resolveBinary
98
- } from "../chunk-RZVWEDIS.js";
107
+ resolveBinary,
108
+ writeJsonAtomicDurable
109
+ } from "../chunk-T2EJ3H6Y.js";
99
110
  import {
100
111
  getApiKey,
101
112
  getBaseUrl
102
- } from "../chunk-R5PZPROX.js";
103
- import "../chunk-PPKIL4EL.js";
113
+ } from "../chunk-M7JFGDGP.js";
114
+ import "../chunk-PPY5N2E6.js";
115
+ import {
116
+ EXIT
117
+ } from "../chunk-KZERVKTD.js";
118
+ import "../chunk-R3HXGHRQ.js";
104
119
  import "../chunk-QTOLNRET.js";
105
- import "../chunk-AVHL2ZTS.js";
120
+ import {
121
+ CLAUDE_INBOX,
122
+ CLAUDE_INBOX_MAX_SESSIONS,
123
+ CLAUDE_INBOX_RECEIVER,
124
+ CLAUDE_INBOX_SETTLEMENTS,
125
+ CLAUDE_WAKE,
126
+ CLAUDE_WAKE_API,
127
+ COMMAND_RECOVERY,
128
+ NATIVE_INPUT,
129
+ NativeInputAgent,
130
+ claudeInboxAction,
131
+ claudeWakeDecision,
132
+ claudeWakeWaitingOn,
133
+ hermesPendingAdmissionId,
134
+ hermesReplayAdmissionId,
135
+ isClaudeInboxReceipt,
136
+ isCodexInputReceipt,
137
+ isCodexQueuedReceipt,
138
+ isCommandAcknowledgementReceipt,
139
+ nativeIdentifier,
140
+ nativeInputAction,
141
+ nativeRecord,
142
+ parseClaudeInboxClaim,
143
+ parseClaudeInboxPoll,
144
+ parseClaudeInboxSession,
145
+ parseHermesAdmission,
146
+ parseHermesReplayCursor,
147
+ qualifyHermesReceiptMode
148
+ } from "../chunk-Y4OBAK4I.js";
106
149
  import "../chunk-MLKGABMK.js";
107
150
 
108
- // src/spawn-daemon.ts
109
- import { execFileSync, spawn } from "child_process";
110
- import { existsSync, statSync } from "fs";
151
+ // src/native-host-receiver.ts
152
+ import { join as join2 } from "path";
111
153
  import { homedir } from "os";
112
- import { basename, dirname, join } from "path";
154
+ import { readFileSync as readFileSync3 } from "fs";
155
+
156
+ // src/native-host.ts
157
+ import WebSocket from "ws";
158
+ import { createConnection } from "net";
159
+ import { createHash } from "crypto";
160
+ import {
161
+ readdirSync,
162
+ lstatSync,
163
+ realpathSync,
164
+ readFileSync,
165
+ mkdirSync,
166
+ openSync,
167
+ writeSync,
168
+ fsyncSync,
169
+ closeSync
170
+ } from "fs";
171
+ import { dirname, join, resolve } from "path";
172
+ var privateNode = (path, kind) => {
173
+ try {
174
+ const s = lstatSync(path);
175
+ return s.uid === process.getuid?.() && (s.mode & 63) === 0 && (kind === "socket" ? s.isSocket() : s.isDirectory());
176
+ } catch {
177
+ return false;
178
+ }
179
+ };
180
+ var ownerControlledDirectory = (path) => {
181
+ try {
182
+ const node = lstatSync(path);
183
+ return node.isDirectory() && node.uid === process.getuid?.() && (node.mode & 18) === 0;
184
+ } catch {
185
+ return false;
186
+ }
187
+ };
188
+ var syncDirectory = (path) => {
189
+ const fd = openSync(path, "r");
190
+ try {
191
+ fsyncSync(fd);
192
+ } finally {
193
+ closeSync(fd);
194
+ }
195
+ };
196
+ var Rpc = class _Rpc {
197
+ constructor(socket, timeout) {
198
+ this.socket = socket;
199
+ this.timeout = timeout;
200
+ socket.on("message", (data) => {
201
+ let v;
202
+ try {
203
+ v = JSON.parse(data.toString());
204
+ } catch {
205
+ socket.terminate();
206
+ return;
207
+ }
208
+ if (!nativeRecord(v) || typeof v.id !== "number") return;
209
+ const p = this.pending.get(v.id);
210
+ if (!p) return;
211
+ this.pending.delete(v.id);
212
+ clearTimeout(p.timer);
213
+ if (v.error) p.reject(new Error("native RPC refused"));
214
+ else p.resolve(v.result);
215
+ });
216
+ socket.on("close", () => this.fail());
217
+ socket.on("error", () => this.fail());
218
+ }
219
+ sequence = 0;
220
+ pending = /* @__PURE__ */ new Map();
221
+ fail() {
222
+ for (const p of this.pending.values()) {
223
+ clearTimeout(p.timer);
224
+ p.reject(new Error("native RPC disconnected"));
225
+ }
226
+ this.pending.clear();
227
+ }
228
+ static async connect(url, protocols, timeout) {
229
+ const socket = new WebSocket(url, [...protocols], {
230
+ handshakeTimeout: timeout,
231
+ maxPayload: NATIVE_INPUT.maxFrameBytes,
232
+ followRedirects: false
233
+ });
234
+ const rpc = new _Rpc(socket, timeout);
235
+ await new Promise((resolve3, reject) => {
236
+ socket.once("open", resolve3);
237
+ socket.once("error", reject);
238
+ });
239
+ if (protocols.length && socket.protocol !== protocols[0]) {
240
+ rpc.close();
241
+ throw new Error("native protocol mismatch");
242
+ }
243
+ return rpc;
244
+ }
245
+ request(method, params) {
246
+ const id = ++this.sequence;
247
+ return new Promise((resolve3, reject) => {
248
+ const timer = setTimeout(() => {
249
+ this.pending.delete(id);
250
+ reject(new Error("native RPC timeout"));
251
+ }, this.timeout);
252
+ this.pending.set(id, { resolve: resolve3, reject, timer });
253
+ this.socket.send(
254
+ JSON.stringify({ jsonrpc: "2.0", id, method, params }),
255
+ (error) => {
256
+ if (error) {
257
+ clearTimeout(timer);
258
+ this.pending.delete(id);
259
+ reject(error);
260
+ }
261
+ }
262
+ );
263
+ });
264
+ }
265
+ notify(method) {
266
+ this.socket.send(JSON.stringify({ method }));
267
+ }
268
+ close() {
269
+ this.fail();
270
+ this.socket.terminate();
271
+ }
272
+ };
273
+ var controlRequest = (path, request, timeout) => new Promise((resolve3, reject) => {
274
+ const s = createConnection(path);
275
+ let buffer = "";
276
+ const timer = setTimeout(() => {
277
+ s.destroy();
278
+ reject(new Error("native control timeout"));
279
+ }, timeout);
280
+ s.on("connect", () => s.write(JSON.stringify(request) + "\n"));
281
+ s.on("error", reject);
282
+ s.on("close", () => {
283
+ clearTimeout(timer);
284
+ reject(new Error("native control closed"));
285
+ });
286
+ s.on("data", (chunk) => {
287
+ buffer += chunk.toString();
288
+ if (buffer.length > 65536) {
289
+ s.destroy();
290
+ return;
291
+ }
292
+ if (!buffer.includes("\n")) return;
293
+ try {
294
+ resolve3(JSON.parse(buffer.slice(0, buffer.indexOf("\n"))));
295
+ } catch {
296
+ reject(new Error("invalid native control response"));
297
+ }
298
+ s.destroy();
299
+ });
300
+ });
301
+ var NativeHostClient = class {
302
+ constructor(options) {
303
+ this.options = options;
304
+ this.timeout = options.timeoutMs ?? NATIVE_INPUT.rpcTimeoutMs;
305
+ }
306
+ timeout;
307
+ async connect() {
308
+ if ((this.options.platform ?? process.platform) !== "darwin")
309
+ throw new Error("native host platform unqualified");
310
+ const home = resolve(this.options.home);
311
+ if (this.options.agent === "codex" /* codex */) {
312
+ const published = join(home, NATIVE_INPUT.codexSocket);
313
+ const path2 = realpathSync(published);
314
+ if (lstatSync(published).uid !== process.getuid?.() || !privateNode(join(home, "app-server-control"), "directory") || !privateNode(dirname(path2), "directory") || !privateNode(path2, "socket"))
315
+ throw new Error("native control unavailable");
316
+ const rpc = await Rpc.connect(`ws+unix://${path2}:/`, [], this.timeout);
317
+ try {
318
+ const v2 = await rpc.request("initialize", {
319
+ clientInfo: { name: "pushary_native_input", version: "1" },
320
+ capabilities: { experimentalApi: true }
321
+ });
322
+ if (!nativeRecord(v2) || v2.codexHome !== home || typeof v2.userAgent !== "string" || !v2.userAgent.includes(`/${NATIVE_INPUT.codexVersion} `) || v2.platformOs !== "macos")
323
+ throw new Error("unqualified native host");
324
+ rpc.notify("initialized");
325
+ return { rpc, profile: home };
326
+ } catch (e) {
327
+ rpc.close();
328
+ throw e;
329
+ }
330
+ }
331
+ const path = join(home, NATIVE_INPUT.hermesSocket);
332
+ if (!ownerControlledDirectory(home) || !privateNode(path, "socket"))
333
+ throw new Error("native control unavailable");
334
+ const v = await controlRequest(
335
+ path,
336
+ { protocol: 1, verb: "identify", id: 1 },
337
+ this.timeout
338
+ );
339
+ if (!nativeRecord(v)) throw new Error("invalid host");
340
+ const d = nativeRecord(v.result) ? v.result : v;
341
+ if (d.runtime_protocol !== 1 || d.state !== "ready" || !Array.isArray(d.served_profiles) || !nativeIdentifier(d.instance_id) || typeof d.api_origin !== "string")
342
+ throw new Error("unqualified native host");
343
+ const profiles = d.served_profiles.filter(
344
+ (p) => nativeRecord(p) && p.home === home
345
+ );
346
+ const profile = profiles[0];
347
+ if (profiles.length !== 1 || !nativeRecord(profile) || typeof profile.profile_id !== "string")
348
+ throw new Error("native profile mismatch");
349
+ const hermesReceiptMode = qualifyHermesReceiptMode(
350
+ d.code_sha,
351
+ d.capabilities
352
+ );
353
+ if (!hermesReceiptMode) throw new Error("native admission unavailable");
354
+ const url = new URL(d.api_origin);
355
+ if (url.protocol !== "http:" || url.hostname !== "127.0.0.1" || url.username || url.password || url.pathname !== "/")
356
+ throw new Error("nonlocal native host");
357
+ const t = await controlRequest(
358
+ path,
359
+ {
360
+ protocol: 1,
361
+ id: 1,
362
+ verb: "session-ticket",
363
+ params: {
364
+ profile_id: profile.profile_id,
365
+ instance_id: d.instance_id,
366
+ purpose: "interactive"
367
+ }
368
+ },
369
+ this.timeout
370
+ );
371
+ const ticket = nativeRecord(t) && nativeRecord(t.result) ? t.result : t;
372
+ if (!nativeRecord(ticket) || ticket.profile_id !== profile.profile_id || ticket.instance_id !== d.instance_id || ticket.runtime_protocol !== 1 || typeof ticket.ticket !== "string")
373
+ throw new Error("native ticket mismatch");
374
+ return {
375
+ rpc: await Rpc.connect(
376
+ `ws://${url.host}/api/ws`,
377
+ [
378
+ NATIVE_INPUT.hermesWebSocketProtocol,
379
+ NATIVE_INPUT.hermesTicketPrefix + ticket.ticket
380
+ ],
381
+ this.timeout
382
+ ),
383
+ profile: profile.profile_id,
384
+ hermesReceiptMode
385
+ };
386
+ }
387
+ async list(host) {
388
+ const { rpc } = host;
389
+ if (this.options.agent === "codex" /* codex */) {
390
+ const v2 = await rpc.request("thread/loaded/list", {
391
+ limit: NATIVE_INPUT.maxSessions
392
+ });
393
+ if (!nativeRecord(v2) || !Array.isArray(v2.data) || !v2.data.every(nativeIdentifier))
394
+ throw new Error("invalid loaded sessions");
395
+ const sessions = [];
396
+ for (const id of v2.data) {
397
+ const t = await this.thread(rpc, id, false);
398
+ if (t && typeof t.cwd === "string")
399
+ sessions.push({
400
+ sessionId: id,
401
+ cwd: t.cwd,
402
+ agent: this.options.agent
403
+ });
404
+ }
405
+ return sessions;
406
+ }
407
+ const v = await rpc.request("session.list", {
408
+ limit: NATIVE_INPUT.maxSessions
409
+ });
410
+ if (!nativeRecord(v) || v.scope !== "live" || !Array.isArray(v.sessions))
411
+ throw new Error("invalid live sessions");
412
+ return v.sessions.filter(
413
+ (row) => nativeRecord(row) && nativeIdentifier(row.session_id) && (row.source === "cli" || row.source === "local")
414
+ ).map((row) => ({
415
+ sessionId: String(row.session_id),
416
+ cwd: this.options.home,
417
+ agent: this.options.agent
418
+ }));
419
+ }
420
+ async sessions() {
421
+ let host;
422
+ try {
423
+ host = await this.connect();
424
+ return await this.list(host);
425
+ } catch (error) {
426
+ this.options.report?.(
427
+ error instanceof Error ? error.message : "native discovery failed"
428
+ );
429
+ return [];
430
+ } finally {
431
+ host?.rpc.close();
432
+ }
433
+ }
434
+ async thread(rpc, id, includeTurns = false) {
435
+ const v = await rpc.request("thread/read", { threadId: id, includeTurns });
436
+ return nativeRecord(v) && nativeRecord(v.thread) && v.thread.id === id ? v.thread : null;
437
+ }
438
+ async hasReceipt(rpc, sessionId, commandId) {
439
+ for (const method of [
440
+ "thread/queue/list" /* queueList */,
441
+ "thread/items/list" /* itemsList */
442
+ ]) {
443
+ let cursor;
444
+ for (let page = 0; page < NATIVE_INPUT.receiptMaxPages; page++) {
445
+ const result = await rpc.request(method, {
446
+ threadId: sessionId,
447
+ ...method === "thread/items/list" /* itemsList */ ? { sortDirection: "desc" } : {},
448
+ limit: NATIVE_INPUT.receiptPageSize,
449
+ ...cursor ? { cursor } : {}
450
+ });
451
+ if (!nativeRecord(result) || !Array.isArray(result.data))
452
+ throw new Error("invalid native receipt page");
453
+ if (method === "thread/queue/list" /* queueList */) {
454
+ if (result.data.some((row) => isCodexQueuedReceipt(row, commandId)))
455
+ return true;
456
+ } else {
457
+ const items = result.data.flatMap(
458
+ (row) => nativeRecord(row) && nativeRecord(row.item) ? [row.item] : []
459
+ );
460
+ if (isCodexInputReceipt(
461
+ { id: sessionId, turns: [{ items }] },
462
+ sessionId,
463
+ commandId
464
+ ))
465
+ return true;
466
+ }
467
+ if (typeof result.nextCursor !== "string" || !result.nextCursor || result.nextCursor === cursor)
468
+ break;
469
+ cursor = result.nextCursor;
470
+ }
471
+ }
472
+ return false;
473
+ }
474
+ async hasHermesReceipt(host, intent, snapshot) {
475
+ if (host.hermesReceiptMode === "submission" /* submission */) {
476
+ const receipt2 = parseHermesAdmission(
477
+ await host.rpc.request("prompt.receipt" /* receipt */, {
478
+ session_id: intent.sessionId,
479
+ submission_id: intent.commandId
480
+ }),
481
+ host.profile,
482
+ intent.sessionId
483
+ );
484
+ return receipt2 !== null && receipt2.status !== "unknown" /* unknown */;
485
+ }
486
+ let admissionId = hermesPendingAdmissionId(
487
+ snapshot,
488
+ host.profile,
489
+ intent.sessionId,
490
+ intent.commandId
491
+ );
492
+ if (!admissionId && intent.hermesCursor) {
493
+ const replay = await host.rpc.request("session.events.since" /* eventsSince */, {
494
+ session_id: intent.sessionId,
495
+ ...intent.hermesCursor
496
+ });
497
+ admissionId = hermesReplayAdmissionId(
498
+ replay,
499
+ intent.hermesCursor,
500
+ intent.sessionId,
501
+ intent.commandId
502
+ );
503
+ }
504
+ if (!admissionId) return false;
505
+ const receipt = parseHermesAdmission(
506
+ await host.rpc.request("prompt.receipt" /* receipt */, {
507
+ session_id: intent.sessionId,
508
+ admission_id: admissionId
509
+ }),
510
+ host.profile,
511
+ intent.sessionId
512
+ );
513
+ return receipt?.admissionId === admissionId && receipt.status !== "unknown" /* unknown */;
514
+ }
515
+ journalPath(sessionId, commandId) {
516
+ return join(
517
+ this.options.journalDir,
518
+ createHash("sha256").update(`${sessionId}\0${commandId}`).digest("hex") + ".json"
519
+ );
520
+ }
521
+ readIntent(sessionId, commandId) {
522
+ try {
523
+ const file = this.journalPath(sessionId, commandId);
524
+ const node = lstatSync(file);
525
+ if (!node.isFile() || node.uid !== process.getuid?.() || node.size > 4096 || (node.mode & 63) !== 0)
526
+ throw new Error("unsafe intent");
527
+ const v = JSON.parse(readFileSync(file, "utf8"));
528
+ if (!nativeRecord(v) || v.version !== NATIVE_INPUT.journalVersion || v.agent !== this.options.agent || v.sessionId !== sessionId || v.commandId !== commandId || v.home !== resolve(this.options.home) || typeof v.submittedAtMs !== "number" || !Number.isSafeInteger(v.submittedAtMs) || typeof v.accepted !== "boolean" || v.hermesCursor !== void 0 && !parseHermesReplayCursor(v.hermesCursor))
529
+ throw new Error("invalid intent");
530
+ const hermesCursor = parseHermesReplayCursor(v.hermesCursor);
531
+ return {
532
+ version: v.version,
533
+ agent: v.agent,
534
+ sessionId,
535
+ commandId,
536
+ home: v.home,
537
+ submittedAtMs: v.submittedAtMs,
538
+ accepted: v.accepted,
539
+ settled: v.settled === true,
540
+ ...hermesCursor ? { hermesCursor } : {}
541
+ };
542
+ } catch (e) {
543
+ if (e instanceof Error && "code" in e && e.code === "ENOENT") return null;
544
+ throw e;
545
+ }
546
+ }
547
+ pendingSettlements() {
548
+ try {
549
+ return readdirSync(this.options.journalDir).filter((file) => /^[0-9a-f]{64}\.json$/.test(file)).flatMap((file) => {
550
+ try {
551
+ const raw = JSON.parse(
552
+ readFileSync(join(this.options.journalDir, file), "utf8")
553
+ );
554
+ if (!nativeRecord(raw) || !nativeIdentifier(raw.sessionId) || !nativeIdentifier(raw.commandId))
555
+ return [];
556
+ const intent = this.readIntent(raw.sessionId, raw.commandId);
557
+ return intent?.accepted && !intent.settled ? [{ sessionId: intent.sessionId, commandId: intent.commandId }] : [];
558
+ } catch {
559
+ return [];
560
+ }
561
+ });
562
+ } catch {
563
+ return [];
564
+ }
565
+ }
566
+ confirmSettlement(sessionId, commandId) {
567
+ const intent = this.readIntent(sessionId, commandId);
568
+ if (intent?.accepted)
569
+ writeJsonAtomicDurable(
570
+ this.journalPath(sessionId, commandId),
571
+ { ...intent, settled: true },
572
+ 384
573
+ );
574
+ }
575
+ writeIntent(sessionId, commandId, hermesCursor) {
576
+ mkdirSync(this.options.journalDir, { recursive: true, mode: 448 });
577
+ const fd = openSync(this.journalPath(sessionId, commandId), "wx", 384);
578
+ try {
579
+ writeSync(
580
+ fd,
581
+ JSON.stringify({
582
+ version: NATIVE_INPUT.journalVersion,
583
+ agent: this.options.agent,
584
+ sessionId,
585
+ commandId,
586
+ home: resolve(this.options.home),
587
+ submittedAtMs: Date.now(),
588
+ accepted: false,
589
+ ...hermesCursor ? { hermesCursor } : {}
590
+ })
591
+ );
592
+ fsyncSync(fd);
593
+ } finally {
594
+ closeSync(fd);
595
+ }
596
+ syncDirectory(this.options.journalDir);
597
+ }
598
+ accept(intent) {
599
+ writeJsonAtomicDurable(
600
+ this.journalPath(intent.sessionId, intent.commandId),
601
+ { ...intent, accepted: true },
602
+ 384
603
+ );
604
+ return "accepted" /* accepted */;
605
+ }
606
+ async deliver(sessionId, command, signal) {
607
+ let intent;
608
+ try {
609
+ intent = this.readIntent(sessionId, command.id);
610
+ } catch {
611
+ return "outcome_unknown" /* outcomeUnknown */;
612
+ }
613
+ if (intent?.accepted) return "accepted" /* accepted */;
614
+ if (!intent && command.expiresAtMs <= Date.now())
615
+ return "expired" /* expired */;
616
+ let host;
617
+ let hermesCursor;
618
+ try {
619
+ host = await this.connect();
620
+ const { rpc } = host;
621
+ if (!(await this.list(host)).some((s) => s.sessionId === sessionId))
622
+ return intent ? "outcome_unknown" /* outcomeUnknown */ : "not_sent" /* notSent */;
623
+ if (this.options.agent === "codex" /* codex */) {
624
+ if (intent) {
625
+ if (await this.hasReceipt(rpc, sessionId, command.id))
626
+ return this.accept(intent);
627
+ } else {
628
+ const queue = await rpc.request("thread/queue/list" /* queueList */, {
629
+ threadId: sessionId,
630
+ limit: 1
631
+ });
632
+ if (!nativeRecord(queue) || !Array.isArray(queue.data))
633
+ throw new Error("native queue unavailable");
634
+ }
635
+ } else if (host.hermesReceiptMode === "submission" /* submission */) {
636
+ if (intent && await this.hasHermesReceipt(host, intent))
637
+ return this.accept(intent);
638
+ if (!intent) {
639
+ const snapshot = await rpc.request("session.resume" /* resume */, {
640
+ session_id: sessionId
641
+ });
642
+ if (!nativeRecord(snapshot) || snapshot.session_id !== sessionId)
643
+ throw new Error("native session mismatch");
644
+ }
645
+ } else {
646
+ const snapshot = await rpc.request("session.resume" /* resume */, {
647
+ session_id: sessionId
648
+ });
649
+ if (!nativeRecord(snapshot) || snapshot.session_id !== sessionId)
650
+ throw new Error("native session mismatch");
651
+ hermesCursor = parseHermesReplayCursor(snapshot) ?? void 0;
652
+ if (intent && await this.hasHermesReceipt(host, intent, snapshot))
653
+ return this.accept(intent);
654
+ if (!intent && !hermesCursor)
655
+ throw new Error("native replay cursor unavailable");
656
+ }
657
+ const action = nativeInputAction({
658
+ attempted: !!intent,
659
+ accepted: false,
660
+ nowMs: Date.now(),
661
+ expiresAtMs: Math.min(
662
+ command.expiresAtMs,
663
+ intent ? intent.submittedAtMs + NATIVE_INPUT.receiptWindowMs : Infinity
664
+ )
665
+ });
666
+ if (action === "expire" /* expire */) return "expired" /* expired */;
667
+ if (action === "outcome_unknown" /* outcomeUnknown */)
668
+ return "outcome_unknown" /* outcomeUnknown */;
669
+ if (action === "observe" /* observe */)
670
+ return "unconfirmed" /* unconfirmed */;
671
+ if (signal?.aborted) return "not_sent" /* notSent */;
672
+ if (!intent) {
673
+ this.writeIntent(sessionId, command.id, hermesCursor);
674
+ intent = this.readIntent(sessionId, command.id);
675
+ }
676
+ if (!intent) return "outcome_unknown" /* outcomeUnknown */;
677
+ if (this.options.agent === "hermes" /* hermes */) {
678
+ const receipt = parseHermesAdmission(
679
+ await rpc.request("prompt.submit" /* submit */, {
680
+ session_id: sessionId,
681
+ submission_id: command.id,
682
+ text: command.text
683
+ }),
684
+ host.profile,
685
+ sessionId
686
+ );
687
+ return receipt && receipt.status !== "unknown" /* unknown */ ? this.accept(intent) : "outcome_unknown" /* outcomeUnknown */;
688
+ }
689
+ const response = await rpc.request("thread/queue/add" /* queueAdd */, {
690
+ threadId: sessionId,
691
+ clientUserMessageId: command.id,
692
+ input: [{ type: "text", text: command.text, text_elements: [] }]
693
+ });
694
+ return nativeRecord(response) && isCodexQueuedReceipt(response.queuedSubmission, command.id) || await this.hasReceipt(rpc, sessionId, command.id) ? this.accept(intent) : "unconfirmed" /* unconfirmed */;
695
+ } catch (error) {
696
+ this.options.report?.(
697
+ error instanceof Error ? error.message : "native delivery failed"
698
+ );
699
+ return intent ? Date.now() >= Math.min(
700
+ command.expiresAtMs,
701
+ intent.submittedAtMs + NATIVE_INPUT.receiptWindowMs
702
+ ) ? "outcome_unknown" /* outcomeUnknown */ : "unconfirmed" /* unconfirmed */ : "not_sent" /* notSent */;
703
+ } finally {
704
+ host?.rpc.close();
705
+ }
706
+ }
707
+ };
708
+
709
+ // src/hook-ownership-file.ts
710
+ import { readFileSync as readFileSync2 } from "fs";
711
+ var readHookOwner = (file) => {
712
+ try {
713
+ const value = JSON.parse(readFileSync2(file, "utf8"));
714
+ return nativeRecord(value) ? hookOwnerOf(value) : null;
715
+ } catch (error) {
716
+ return error instanceof Error && "code" in error && error.code === "ENOENT" ? "none" : null;
717
+ }
718
+ };
719
+
720
+ // src/native-input-receiver.ts
721
+ import { basename } from "path";
722
+ var startNativeInputReceiver = (apiKey, machineId, options) => {
723
+ const client = options.client;
724
+ const baseUrl = options.baseUrl ?? getBaseUrl();
725
+ const abort = new AbortController();
726
+ const busy = /* @__PURE__ */ new Set();
727
+ const heartbeats = /* @__PURE__ */ new Map();
728
+ const settlements = /* @__PURE__ */ new Map();
729
+ let scanning = false;
730
+ let cursor = 0;
731
+ const post = async (path, body) => {
732
+ const response = await fetch(`${baseUrl}${path}`, {
733
+ method: "POST",
734
+ headers: {
735
+ "Content-Type": "application/json",
736
+ Authorization: `Bearer ${apiKey}`
737
+ },
738
+ body: JSON.stringify(body),
739
+ signal: AbortSignal.any([
740
+ abort.signal,
741
+ AbortSignal.timeout(CLAUDE_INBOX_RECEIVER.requestTimeoutMs)
742
+ ])
743
+ });
744
+ if (!response.ok) throw new Error(`native inbox HTTP ${response.status}`);
745
+ return response.json();
746
+ };
747
+ const settle = async (commandId) => {
748
+ const pending = settlements.get(commandId);
749
+ if (!pending) return false;
750
+ try {
751
+ const receipt = await post("/api/agent/command/ack", {
752
+ ...pending,
753
+ commandId,
754
+ machineId
755
+ });
756
+ if (!isCommandAcknowledgementReceipt(receipt))
757
+ throw new Error("native acknowledgement missing receipt");
758
+ client.confirmSettlement(pending.sessionId, commandId);
759
+ settlements.delete(commandId);
760
+ return true;
761
+ } catch (error) {
762
+ if (!abort.signal.aborted)
763
+ options.report?.(
764
+ error instanceof Error ? error.message : "native receipt recovery failed"
765
+ );
766
+ return false;
767
+ }
768
+ };
769
+ const receive = async (sessionId) => {
770
+ busy.add(sessionId);
771
+ try {
772
+ if ([...settlements.values()].some(
773
+ (receipt) => receipt.sessionId === sessionId
774
+ ))
775
+ return;
776
+ const raw = await post("/api/agent/command/native", {
777
+ operation: "claim" /* claim */,
778
+ sessionId,
779
+ machineId
780
+ });
781
+ const command = parseClaudeInboxClaim(raw);
782
+ if (!command || abort.signal.aborted) return;
783
+ const outcome = await client.deliver(sessionId, command, abort.signal);
784
+ const settlement = CLAUDE_INBOX_SETTLEMENTS[outcome];
785
+ if (abort.signal.aborted || !settlement) return;
786
+ settlements.set(command.id, { sessionId, ...settlement });
787
+ await settle(command.id);
788
+ } catch (error) {
789
+ if (!abort.signal.aborted)
790
+ options.report?.(
791
+ error instanceof Error ? error.message : "native inbox delivery failed"
792
+ );
793
+ } finally {
794
+ busy.delete(sessionId);
795
+ }
796
+ };
797
+ const tick = async () => {
798
+ if (scanning || abort.signal.aborted) return;
799
+ scanning = true;
800
+ try {
801
+ for (const receipt of client.pendingSettlements()) {
802
+ settlements.set(receipt.commandId, {
803
+ sessionId: receipt.sessionId,
804
+ state: "delivered"
805
+ });
806
+ }
807
+ for (const commandId of [...settlements.keys()]) await settle(commandId);
808
+ const sessions = (await client.sessions()).filter(options.ownsSession).slice(0, CLAUDE_INBOX_MAX_SESSIONS);
809
+ if (!sessions.length) return;
810
+ for (const session of sessions) {
811
+ if (Date.now() - (heartbeats.get(session.sessionId) ?? 0) < CLAUDE_INBOX_RECEIVER.heartbeatMs)
812
+ continue;
813
+ await post("/api/agent/event", {
814
+ event: "heartbeat",
815
+ agentType: options.agentType ?? "claude_code",
816
+ agentName: `${options.agentName ?? "Claude Code"} - ${basename(session.cwd)}`,
817
+ sessionId: session.sessionId,
818
+ machineId,
819
+ cwd: session.cwd,
820
+ sentAt: (/* @__PURE__ */ new Date()).toISOString(),
821
+ canDrainCommand: false
822
+ });
823
+ heartbeats.set(session.sessionId, Date.now());
824
+ }
825
+ const raw = await post("/api/agent/command/native", {
826
+ operation: "poll" /* poll */,
827
+ machineId,
828
+ sessionIds: sessions.map((session) => session.sessionId)
829
+ });
830
+ const pending = parseClaudeInboxPoll(raw);
831
+ if (!pending) throw new Error("native inbox protocol mismatch");
832
+ const rotated = [...pending.slice(cursor), ...pending.slice(0, cursor)];
833
+ cursor = pending.length ? (cursor + CLAUDE_INBOX_RECEIVER.maxConcurrent) % pending.length : 0;
834
+ for (const sessionId of rotated) {
835
+ if (abort.signal.aborted || busy.size >= CLAUDE_INBOX_RECEIVER.maxConcurrent)
836
+ break;
837
+ if (!busy.has(sessionId) && sessions.some((session) => session.sessionId === sessionId))
838
+ void receive(sessionId);
839
+ }
840
+ } catch (error) {
841
+ const message = error instanceof Error ? error.message : "native inbox polling failed";
842
+ if (!abort.signal.aborted) options.report?.(message);
843
+ } finally {
844
+ scanning = false;
845
+ }
846
+ };
847
+ const timer = setInterval(() => {
848
+ void tick();
849
+ }, options.pollMs ?? CLAUDE_INBOX_RECEIVER.pollMs);
850
+ timer.unref();
851
+ void tick();
852
+ return () => {
853
+ abort.abort();
854
+ clearInterval(timer);
855
+ };
856
+ };
857
+
858
+ // src/native-host-receiver.ts
859
+ var ownsHermes = (home) => {
860
+ try {
861
+ return parseHermesInstallMarker(
862
+ JSON.parse(
863
+ readFileSync3(
864
+ join2(home, "plugins", "pushary", HERMES_INSTALL_MARKER_FILE),
865
+ "utf8"
866
+ )
867
+ )
868
+ )?.installedBy === "cli";
869
+ } catch {
870
+ return false;
871
+ }
872
+ };
873
+ var startNativeHostReceivers = (apiKey, machineId, report) => {
874
+ const homes = {
875
+ ["codex" /* codex */]: codexHome(),
876
+ ["hermes" /* hermes */]: defaultHermesHome()
877
+ };
878
+ const stops = Object.values(NativeInputAgent).map((agent) => {
879
+ const home = homes[agent];
880
+ return startNativeInputReceiver(apiKey, machineId, {
881
+ client: new NativeHostClient({
882
+ agent,
883
+ home,
884
+ report,
885
+ journalDir: join2(homedir(), ".pushary", NATIVE_INPUT.journalDirectory)
886
+ }),
887
+ agentType: agent,
888
+ agentName: agent === "codex" /* codex */ ? "Codex" : "Hermes",
889
+ report,
890
+ ownsSession: () => agent === "codex" /* codex */ ? readHookOwner(join2(home, "hooks.json")) === "cli" : ownsHermes(home)
891
+ });
892
+ });
893
+ return () => {
894
+ for (const stop of stops) stop();
895
+ };
896
+ };
897
+
898
+ // src/hook-command-receipts.ts
899
+ var reconcileCommandReceipts = async (apiKey, machineId, agentType, baseUrl, signal) => {
900
+ const namespace = `${apiKey}:${machineId}:${agentType}`;
901
+ for (const { sessionId, command } of pendingCommandReceipts(namespace)) {
902
+ signal?.throwIfAborted();
903
+ const state = command.delivered ? "delivered" : command.attempted ? "uncertain" : "failed";
904
+ const response = await fetch(`${baseUrl}/api/agent/command/ack`, {
905
+ method: "POST",
906
+ headers: {
907
+ "Content-Type": "application/json",
908
+ Authorization: `Bearer ${apiKey}`
909
+ },
910
+ body: JSON.stringify({
911
+ commandId: command.id,
912
+ sessionId,
913
+ machineId,
914
+ state,
915
+ failureCode: command.attempted ? "delivery_unconfirmed" : "expired"
916
+ }),
917
+ signal: AbortSignal.any([
918
+ AbortSignal.timeout(COMMAND_RECOVERY.acknowledgementTimeoutMs),
919
+ ...signal ? [signal] : []
920
+ ])
921
+ });
922
+ if (response.ok) removePendingCommand(sessionId, command.id, namespace);
923
+ }
924
+ };
925
+
926
+ // src/claude-inbox-receiver.ts
927
+ import { join as join4, basename as basename2 } from "path";
928
+ import { homedir as homedir2 } from "os";
929
+
930
+ // src/claude-inbox.ts
931
+ import { createHash as createHash2, randomUUID } from "crypto";
932
+ import { execFileSync } from "child_process";
933
+ import {
934
+ closeSync as closeSync2,
935
+ fsyncSync as fsyncSync2,
936
+ lstatSync as lstatSync2,
937
+ mkdirSync as mkdirSync2,
938
+ openSync as openSync2,
939
+ readFileSync as readFileSync4,
940
+ readdirSync as readdirSync2,
941
+ readSync,
942
+ statSync,
943
+ writeFileSync,
944
+ unlinkSync
945
+ } from "fs";
946
+ import { dirname as dirname2, isAbsolute, join as join3, resolve as resolve2 } from "path";
947
+ import { Socket } from "net";
948
+ import { setTimeout as delay } from "timers/promises";
949
+ var object = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
950
+ var digest = (value) => createHash2("sha256").update(value).digest("hex");
951
+ var readObject = (file) => {
952
+ const stat = lstatSync2(file);
953
+ if (!stat.isFile() || stat.size > CLAUDE_INBOX.maxRecordBytes || stat.uid !== process.getuid?.())
954
+ throw new Error("Invalid inbox record");
955
+ return JSON.parse(readFileSync4(file, "utf8"));
956
+ };
957
+ var readKey = (file) => {
958
+ const value = readObject(file);
959
+ return object(value) && typeof value.peerToken === "string" && /^[0-9a-f]{32}$/.test(value.peerToken) && typeof value.procStart === "string" ? { peerToken: value.peerToken, procStart: value.procStart } : null;
960
+ };
961
+ var processStart = (pid) => {
962
+ if (process.platform !== "darwin") return null;
963
+ try {
964
+ return execFileSync("/bin/ps", ["-p", String(pid), "-o", "lstart="], {
965
+ encoding: "utf8",
966
+ env: { ...process.env, TZ: "UTC", LC_ALL: "C" },
967
+ timeout: CLAUDE_INBOX.connectTimeoutMs
968
+ }).trim();
969
+ } catch {
970
+ return null;
971
+ }
972
+ };
973
+ var transcriptPath = (config, session) => join3(
974
+ config,
975
+ "projects",
976
+ resolve2(session.cwd).replace(/[^a-zA-Z0-9-]/g, "-"),
977
+ `${session.sessionId}.jsonl`
978
+ );
979
+ var transcriptSize = (file) => {
980
+ try {
981
+ return statSync(file).size;
982
+ } catch {
983
+ return 0;
984
+ }
985
+ };
986
+ var receiptPresent = (attempt) => {
987
+ let fd;
988
+ try {
989
+ fd = openSync2(attempt.transcript, "r");
990
+ const buffer = Buffer.alloc(CLAUDE_INBOX.maxTranscriptReadBytes);
991
+ const count = readSync(fd, buffer, 0, buffer.length, attempt.offset);
992
+ for (const line of buffer.subarray(0, count).toString("utf8").split("\n")) {
993
+ try {
994
+ if (isClaudeInboxReceipt(
995
+ JSON.parse(line),
996
+ attempt.sessionId,
997
+ attempt.commandId
998
+ ))
999
+ return true;
1000
+ } catch {
1001
+ }
1002
+ }
1003
+ return false;
1004
+ } catch {
1005
+ return false;
1006
+ } finally {
1007
+ if (fd !== void 0) closeSync2(fd);
1008
+ }
1009
+ };
1010
+ var connectInbox = (path) => new Promise((resolveConnection) => {
1011
+ const socket = new Socket();
1012
+ const timer = setTimeout(failed, CLAUDE_INBOX.connectTimeoutMs);
1013
+ function failed() {
1014
+ clearTimeout(timer);
1015
+ socket.destroy();
1016
+ resolveConnection(null);
1017
+ }
1018
+ socket.once("error", failed);
1019
+ socket.connect(path, () => {
1020
+ clearTimeout(timer);
1021
+ socket.removeListener("error", failed);
1022
+ socket.on("error", () => {
1023
+ });
1024
+ resolveConnection(socket);
1025
+ });
1026
+ });
1027
+ var createAttempt = (file, attempt) => {
1028
+ mkdirSync2(dirname2(file), { recursive: true, mode: 448 });
1029
+ const fd = openSync2(file, "wx", 384);
1030
+ try {
1031
+ writeFileSync(fd, JSON.stringify(attempt));
1032
+ fsyncSync2(fd);
1033
+ } finally {
1034
+ closeSync2(fd);
1035
+ }
1036
+ const directory = openSync2(dirname2(file), "r");
1037
+ try {
1038
+ fsyncSync2(directory);
1039
+ } finally {
1040
+ closeSync2(directory);
1041
+ }
1042
+ return true;
1043
+ };
1044
+ var ClaudeInboxClient = class {
1045
+ options;
1046
+ constructor(options) {
1047
+ this.options = options;
1048
+ }
1049
+ sessions() {
1050
+ try {
1051
+ const sessions = readdirSync2(
1052
+ join3(this.options.configDir, "sessions")
1053
+ ).filter((file) => /^\d+\.json$/.test(file));
1054
+ return sessions.flatMap((file) => {
1055
+ try {
1056
+ const session = parseClaudeInboxSession(
1057
+ readObject(join3(this.options.configDir, "sessions", file))
1058
+ );
1059
+ if (!session || file !== `${session.pid}.json` || !isAbsolute(session.cwd) || !isAbsolute(session.messagingSocketPath))
1060
+ return [];
1061
+ if ((this.options.processStart ?? processStart)(session.pid) !== session.procStart)
1062
+ return [];
1063
+ const socket = lstatSync2(session.messagingSocketPath);
1064
+ if (!socket.isSocket() || socket.uid !== process.getuid?.()) return [];
1065
+ return this.key(session) ? [session] : [];
1066
+ } catch {
1067
+ return [];
1068
+ }
1069
+ });
1070
+ } catch {
1071
+ return [];
1072
+ }
1073
+ }
1074
+ key(session) {
1075
+ const canonicalSocket = session.messagingSocketPath.replace(
1076
+ /^\/private(?=\/(?:tmp|var|etc)\/)/,
1077
+ ""
1078
+ );
1079
+ const key = readKey(
1080
+ join3(
1081
+ this.options.configDir,
1082
+ "sessions",
1083
+ `${session.pid}.${digest(canonicalSocket)}.key`
1084
+ )
1085
+ );
1086
+ return key?.procStart === session.procStart ? key : null;
1087
+ }
1088
+ attempts(version) {
1089
+ const directory = join3(this.options.journalDir, version);
1090
+ mkdirSync2(directory, { recursive: true, mode: 448 });
1091
+ const files = readdirSync2(directory).filter((file) => /^\d{13}-[0-9a-f-]{36}\.json$/.test(file)).sort().reverse();
1092
+ const attempts = files.slice(0, CLAUDE_WAKE.retainedAttempts).map((name) => {
1093
+ const file = join3(directory, name);
1094
+ const value = readObject(file);
1095
+ if (!object(value) || value.version !== CLAUDE_INBOX.journalVersion || typeof value.sessionId !== "string" || typeof value.commandId !== "string" || typeof value.transcript !== "string" || !resolve2(value.transcript).startsWith(
1096
+ resolve2(this.options.configDir, "projects") + "/"
1097
+ ) || typeof value.offset !== "number" || !Number.isSafeInteger(value.offset) || value.offset < 0 || typeof value.accepted !== "boolean" || typeof value.submittedAtMs !== "number" || !Number.isSafeInteger(value.submittedAtMs))
1098
+ throw new Error("Invalid wake receipt");
1099
+ const attempt = {
1100
+ version: value.version,
1101
+ sessionId: value.sessionId,
1102
+ commandId: value.commandId,
1103
+ transcript: value.transcript,
1104
+ offset: value.offset,
1105
+ accepted: value.accepted,
1106
+ submittedAtMs: value.submittedAtMs
1107
+ };
1108
+ if (!attempt.accepted && receiptPresent(attempt)) {
1109
+ const accepted = { ...attempt, accepted: true };
1110
+ writeJsonAtomicDurable(file, accepted, 384);
1111
+ return { file, attempt: accepted };
1112
+ }
1113
+ return { file, attempt };
1114
+ });
1115
+ for (const name of files.slice(CLAUDE_WAKE.retainedAttempts))
1116
+ unlinkSync(join3(directory, name));
1117
+ return attempts;
1118
+ }
1119
+ async wake(sessionId, signal) {
1120
+ const matches = this.sessions().filter(
1121
+ (candidate) => candidate.sessionId === sessionId
1122
+ );
1123
+ const session = matches.length === 1 ? matches[0] : null;
1124
+ if (!session || signal?.aborted) return "wake_not_sent" /* notSent */;
1125
+ try {
1126
+ const attempts = this.attempts(session.version);
1127
+ const decision = claudeWakeDecision({
1128
+ sessionId,
1129
+ idle: session.status === "idle",
1130
+ nowMs: Date.now(),
1131
+ attempts: attempts.map((entry) => entry.attempt)
1132
+ });
1133
+ if (decision) return decision;
1134
+ const key = this.key(session);
1135
+ if (!key) return "wake_not_sent" /* notSent */;
1136
+ const socket = await connectInbox(session.messagingSocketPath);
1137
+ if (!socket) return "wake_not_sent" /* notSent */;
1138
+ if (signal?.aborted || (this.options.processStart ?? processStart)(session.pid) !== session.procStart) {
1139
+ socket.destroy();
1140
+ return "wake_not_sent" /* notSent */;
1141
+ }
1142
+ const commandId = randomUUID();
1143
+ const transcript = transcriptPath(this.options.configDir, session);
1144
+ const attempt = {
1145
+ version: CLAUDE_INBOX.journalVersion,
1146
+ sessionId,
1147
+ commandId,
1148
+ transcript,
1149
+ offset: transcriptSize(transcript),
1150
+ accepted: false,
1151
+ submittedAtMs: Date.now()
1152
+ };
1153
+ const file = join3(
1154
+ this.options.journalDir,
1155
+ session.version,
1156
+ `${attempt.submittedAtMs}-${commandId}.json`
1157
+ );
1158
+ try {
1159
+ createAttempt(file, attempt);
1160
+ } catch {
1161
+ socket.destroy();
1162
+ return "wake_not_sent" /* notSent */;
1163
+ }
1164
+ const frame = {
1165
+ type: "user",
1166
+ session_id: sessionId,
1167
+ uuid: commandId,
1168
+ msg_id: commandId,
1169
+ from: CLAUDE_INBOX.sender,
1170
+ priority: "next",
1171
+ message: { role: "user", content: CLAUDE_WAKE.message }
1172
+ };
1173
+ socket.end(
1174
+ JSON.stringify({ type: "auth", token: key.peerToken }) + "\n" + JSON.stringify(frame) + "\n"
1175
+ );
1176
+ socket.setTimeout(CLAUDE_INBOX.connectTimeoutMs, () => socket.destroy());
1177
+ const outcome = await this.observe(
1178
+ file,
1179
+ attempt,
1180
+ attempt.submittedAtMs + CLAUDE_WAKE.receiptWindowMs
1181
+ );
1182
+ return outcome === "accepted" /* accepted */ ? "wake_accepted" /* accepted */ : "wake_unconfirmed" /* unconfirmed */;
1183
+ } catch {
1184
+ return "wake_not_sent" /* notSent */;
1185
+ }
1186
+ }
1187
+ async observe(file, attempt, expiresAtMs) {
1188
+ const deliveryDeadline = Math.min(
1189
+ expiresAtMs,
1190
+ attempt.submittedAtMs + CLAUDE_INBOX.receiptWindowMs
1191
+ );
1192
+ if (claudeInboxAction({
1193
+ attempted: true,
1194
+ accepted: attempt.accepted,
1195
+ expiresAtMs: deliveryDeadline,
1196
+ nowMs: Date.now()
1197
+ }) === "acknowledge" /* acknowledge */)
1198
+ return "accepted" /* accepted */;
1199
+ const deadline = Math.min(
1200
+ deliveryDeadline,
1201
+ Date.now() + (this.options.receiptWindowMs ?? CLAUDE_INBOX.receiptWindowMs)
1202
+ );
1203
+ do {
1204
+ if (receiptPresent(attempt)) {
1205
+ try {
1206
+ writeJsonAtomicDurable(file, { ...attempt, accepted: true }, 384);
1207
+ } catch {
1208
+ return "unconfirmed" /* unconfirmed */;
1209
+ }
1210
+ return "accepted" /* accepted */;
1211
+ }
1212
+ if (Date.now() >= deadline) break;
1213
+ await delay(this.options.receiptPollMs ?? CLAUDE_INBOX.receiptPollMs);
1214
+ } while (Date.now() <= deadline);
1215
+ return deliveryDeadline <= Date.now() ? "outcome_unknown" /* outcomeUnknown */ : "unconfirmed" /* unconfirmed */;
1216
+ }
1217
+ };
1218
+
1219
+ // src/claude-inbox-receiver.ts
1220
+ var ownsSession = (session) => {
1221
+ const files = [
1222
+ join4(claudeConfigDir(), "settings.json"),
1223
+ join4(session.cwd, ".claude", "settings.json"),
1224
+ join4(session.cwd, ".claude", "settings.local.json")
1225
+ ];
1226
+ const owners = files.map(readHookOwner);
1227
+ return owners.includes("cli") && !owners.includes("app") && !owners.includes(null);
1228
+ };
1229
+ var startClaudeInboxReceiver = (apiKey, machineId, options = {}) => {
1230
+ const client = options.client ?? new ClaudeInboxClient({
1231
+ configDir: claudeConfigDir(),
1232
+ journalDir: join4(homedir2(), ".pushary", CLAUDE_WAKE.directory)
1233
+ });
1234
+ const baseUrl = options.baseUrl ?? getBaseUrl();
1235
+ const abort = new AbortController();
1236
+ let scanning = false;
1237
+ let cursor = 0;
1238
+ const heartbeats = /* @__PURE__ */ new Map();
1239
+ const post = async (body, path = CLAUDE_WAKE_API) => {
1240
+ const response = await fetch(`${baseUrl}${path}`, {
1241
+ method: "POST",
1242
+ headers: {
1243
+ "Content-Type": "application/json",
1244
+ Authorization: `Bearer ${apiKey}`
1245
+ },
1246
+ body: JSON.stringify(body),
1247
+ signal: AbortSignal.any([
1248
+ abort.signal,
1249
+ AbortSignal.timeout(CLAUDE_INBOX_RECEIVER.requestTimeoutMs)
1250
+ ])
1251
+ });
1252
+ if (!response.ok) throw new Error(`Claude wake HTTP ${response.status}`);
1253
+ return response.json();
1254
+ };
1255
+ const report = async (sessionId, waitingOn) => {
1256
+ await post({
1257
+ operation: "report" /* report */,
1258
+ sessionId,
1259
+ machineId,
1260
+ waitingOn
1261
+ });
1262
+ };
1263
+ const tick = async () => {
1264
+ if (scanning || abort.signal.aborted) return;
1265
+ scanning = true;
1266
+ try {
1267
+ await reconcileCommandReceipts(
1268
+ apiKey,
1269
+ machineId,
1270
+ "claude_code",
1271
+ baseUrl,
1272
+ abort.signal
1273
+ );
1274
+ const sessions = client.sessions().filter(options.ownsSession ?? ownsSession).slice(0, CLAUDE_INBOX_MAX_SESSIONS);
1275
+ if (!sessions.length) return;
1276
+ for (const session of sessions) {
1277
+ if (Date.now() - (heartbeats.get(session.sessionId) ?? 0) < CLAUDE_INBOX_RECEIVER.heartbeatMs)
1278
+ continue;
1279
+ await post(
1280
+ {
1281
+ event: "heartbeat",
1282
+ agentType: "claude_code",
1283
+ agentName: `Claude Code - ${basename2(session.cwd)}`,
1284
+ sessionId: session.sessionId,
1285
+ machineId,
1286
+ cwd: session.cwd,
1287
+ sentAt: (/* @__PURE__ */ new Date()).toISOString(),
1288
+ canDrainCommand: false,
1289
+ skipCommandDrain: true
1290
+ },
1291
+ "/api/agent/event"
1292
+ );
1293
+ heartbeats.set(session.sessionId, Date.now());
1294
+ }
1295
+ const raw = await post({
1296
+ operation: "poll" /* poll */,
1297
+ machineId,
1298
+ sessionIds: sessions.map((session) => session.sessionId)
1299
+ });
1300
+ const pending = parseClaudeInboxPoll(raw);
1301
+ if (!pending) throw new Error("Claude wake protocol mismatch");
1302
+ const rotated = [...pending.slice(cursor), ...pending.slice(0, cursor)];
1303
+ const batch = rotated.slice(0, CLAUDE_INBOX_RECEIVER.maxConcurrent);
1304
+ cursor = pending.length ? (cursor + batch.length) % pending.length : 0;
1305
+ await Promise.all(
1306
+ batch.map(async (sessionId) => {
1307
+ if (!sessions.some((session) => session.sessionId === sessionId) || abort.signal.aborted)
1308
+ return;
1309
+ await report(sessionId, "waking" /* waking */);
1310
+ const outcome = await client.wake(sessionId, abort.signal);
1311
+ await report(sessionId, claudeWakeWaitingOn(outcome));
1312
+ })
1313
+ );
1314
+ } catch (error) {
1315
+ if (!abort.signal.aborted)
1316
+ options.report?.(
1317
+ error instanceof Error ? error.message : "Claude wake failed"
1318
+ );
1319
+ } finally {
1320
+ scanning = false;
1321
+ }
1322
+ };
1323
+ const timer = setInterval(() => {
1324
+ void tick();
1325
+ }, options.pollMs ?? CLAUDE_INBOX_RECEIVER.pollMs);
1326
+ timer.unref();
1327
+ void tick();
1328
+ return () => {
1329
+ abort.abort();
1330
+ clearInterval(timer);
1331
+ };
1332
+ };
1333
+
1334
+ // src/spawn-daemon.ts
1335
+ import { execFileSync as execFileSync2, spawn } from "child_process";
1336
+ import { existsSync, statSync as statSync2 } from "fs";
1337
+ import { homedir as homedir3 } from "os";
1338
+ import { basename as basename3, dirname as dirname3, join as join5 } from "path";
113
1339
 
114
1340
  // src/stop-requests.ts
115
1341
  var STOP_CAPABILITY = "stop";
@@ -292,13 +1518,13 @@ var resolveSpawnDirectory = (requested, options = {}) => {
292
1518
  const fallbackDir = options.fallbackDir ?? process.cwd();
293
1519
  const value = requested?.trim();
294
1520
  if (!value) return { ok: true, cwd: fallbackDir };
295
- const homeDir = options.homeDir ?? homedir();
296
- const cwd = value === "~" ? homeDir : value.startsWith("~/") || value.startsWith("~\\") ? join(homeDir, value.slice(2)) : value;
1521
+ const homeDir = options.homeDir ?? homedir3();
1522
+ const cwd = value === "~" ? homeDir : value.startsWith("~/") || value.startsWith("~\\") ? join5(homeDir, value.slice(2)) : value;
297
1523
  if (!(options.exists ?? existsSync)(cwd)) {
298
1524
  return { ok: false, message: `Directory does not exist: ${value}` };
299
1525
  }
300
1526
  try {
301
- if (!(options.isDirectory ?? ((path) => statSync(path).isDirectory()))(cwd)) {
1527
+ if (!(options.isDirectory ?? ((path) => statSync2(path).isDirectory()))(cwd)) {
302
1528
  return { ok: false, message: `Not a directory: ${value}` };
303
1529
  }
304
1530
  } catch {
@@ -390,9 +1616,9 @@ var runSpawnDaemon = async () => {
390
1616
  describedProviders = description;
391
1617
  });
392
1618
  describedProviders = describeLaunchableProviders(inventory.current.capabilities);
393
- const siblingEntry = process.argv[1] ? join(dirname(process.argv[1]), "pushary.js") : "";
394
- const codexBridgeEntry = process.argv[1] ? join(dirname(process.argv[1]), "pushary-codex-bridge.js") : "pushary-codex-bridge";
395
- const geminiBridgeEntry = process.argv[1] ? join(dirname(process.argv[1]), "pushary-gemini-bridge.js") : "pushary-gemini-bridge";
1619
+ const siblingEntry = process.argv[1] ? join5(dirname3(process.argv[1]), "pushary.js") : "";
1620
+ const codexBridgeEntry = process.argv[1] ? join5(dirname3(process.argv[1]), "pushary-codex-bridge.js") : "pushary-codex-bridge";
1621
+ const geminiBridgeEntry = process.argv[1] ? join5(dirname3(process.argv[1]), "pushary-gemini-bridge.js") : "pushary-gemini-bridge";
396
1622
  const spawnDirect = siblingEntry.length > 0 && existsSync(siblingEntry);
397
1623
  const existing = readDaemonStatus();
398
1624
  let handoff;
@@ -492,7 +1718,7 @@ var runSpawnDaemon = async () => {
492
1718
  if (!plan) {
493
1719
  return Promise.reject(new Error(FLAG_SHAPED_PROMPT_MESSAGE));
494
1720
  }
495
- return new Promise((resolve, reject) => {
1721
+ return new Promise((resolve3, reject) => {
496
1722
  try {
497
1723
  const child = plan.direct ? spawn(plan.command, plan.args, { cwd, detached: true, stdio: "ignore", env }) : spawnClaude(plan.command, plan.args, { cwd, detached: true, stdio: "ignore", env });
498
1724
  let started = false;
@@ -521,8 +1747,8 @@ var runSpawnDaemon = async () => {
521
1747
  });
522
1748
  }
523
1749
  child.unref();
524
- log(`spawn ${req.id}: launched a phone-requested ${plan.agent} session in ${basename(cwd)} (pid ${child.pid ?? "unknown"})`);
525
- resolve(typeof child.pid === "number" ? child.pid : null);
1750
+ log(`spawn ${req.id}: launched a phone-requested ${plan.agent} session in ${basename3(cwd)} (pid ${child.pid ?? "unknown"})`);
1751
+ resolve3(typeof child.pid === "number" ? child.pid : null);
526
1752
  });
527
1753
  } catch (err) {
528
1754
  reject(err);
@@ -530,7 +1756,7 @@ var runSpawnDaemon = async () => {
530
1756
  });
531
1757
  };
532
1758
  const kill = process.platform === "win32" ? (pid) => {
533
- execFileSync("taskkill", windowsTaskkillArgs(pid), { stdio: "ignore" });
1759
+ execFileSync2("taskkill", windowsTaskkillArgs(pid), { stdio: "ignore" });
534
1760
  } : process.kill.bind(process);
535
1761
  const stopDeps = {
536
1762
  kill: (pid, signal) => {
@@ -542,7 +1768,7 @@ var runSpawnDaemon = async () => {
542
1768
  return launched || ownsLiveProcess(request);
543
1769
  },
544
1770
  selfPid: process.pid,
545
- sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
1771
+ sleep: (ms) => new Promise((resolve3) => setTimeout(resolve3, ms)),
546
1772
  isAlive: (pid) => {
547
1773
  try {
548
1774
  process.kill(pid, 0);
@@ -600,7 +1826,7 @@ var runSpawnDaemon = async () => {
600
1826
  completedStops.delete(acknowledgement.stopId);
601
1827
  break;
602
1828
  }
603
- if (attempt < 2) await new Promise((resolve) => setTimeout(resolve, 200 * (attempt + 1)));
1829
+ if (attempt < 2) await new Promise((resolve3) => setTimeout(resolve3, 200 * (attempt + 1)));
604
1830
  }
605
1831
  }
606
1832
  async function apply() {
@@ -743,8 +1969,12 @@ var runSpawnDaemon = async () => {
743
1969
  if (!stopped) timer = setTimeout(tick, jitter(nextDelay()));
744
1970
  };
745
1971
  const stopTranscripts = startRegisteredTranscripts(apiKey, machineId);
1972
+ const stopNativeHosts = startNativeHostReceivers(apiKey, machineId, log);
1973
+ const stopClaudeInbox = startClaudeInboxReceiver(apiKey, machineId, { report: log });
746
1974
  const teardown = () => {
747
1975
  stopTranscripts();
1976
+ stopClaudeInbox();
1977
+ stopNativeHosts();
748
1978
  if (stopped) return;
749
1979
  stopped = true;
750
1980
  if (timer) clearTimeout(timer);
@@ -767,7 +1997,7 @@ var runSpawnDaemon = async () => {
767
1997
  return;
768
1998
  }
769
1999
  const result = await sendHeartbeat(apiKey, {
770
- repoKey: basename(process.cwd()),
2000
+ repoKey: basename3(process.cwd()),
771
2001
  capabilities: [
772
2002
  ...launchAdvertisement(inventory.current.capabilities),
773
2003
  STOP_CAPABILITY,
@@ -799,24 +2029,24 @@ var runSpawnDaemon = async () => {
799
2029
  log(hasSpawnProvider(inventory.current.capabilities) ? `daemon online (pid ${process.pid}): this machine (${machineId}) can now be sent a new session from your phone. Ctrl-C to stop.` : `daemon online (pid ${process.pid}): no supported Claude, Codex, or Gemini CLI was found, so phone-start is unavailable. Ctrl-C to stop.`);
800
2030
  timer = setTimeout(tick, 0);
801
2031
  const signals = daemonSignals();
802
- await new Promise((resolve) => {
2032
+ await new Promise((resolve3) => {
803
2033
  const onSignal = () => finish();
804
2034
  for (const signal of signals) process.on(signal, onSignal);
805
2035
  finish = () => {
806
2036
  teardown();
807
2037
  for (const signal of signals) process.off(signal, onSignal);
808
- resolve();
2038
+ resolve3();
809
2039
  };
810
2040
  });
811
2041
  return 0;
812
2042
  };
813
2043
 
814
2044
  // src/daemon-sessions.ts
815
- import { execFileSync as execFileSync2 } from "child_process";
2045
+ import { execFileSync as execFileSync3 } from "child_process";
816
2046
  var readResidentSetKb = (pid) => {
817
2047
  if (process.platform === "win32") return null;
818
2048
  try {
819
- const output = execFileSync2("ps", ["-o", "rss=", "-p", String(pid)], {
2049
+ const output = execFileSync3("ps", ["-o", "rss=", "-p", String(pid)], {
820
2050
  encoding: "utf8",
821
2051
  stdio: ["ignore", "pipe", "ignore"]
822
2052
  });