@pushary/agent-hooks 0.89.3 → 0.89.5

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 (58) hide show
  1. package/README.md +40 -0
  2. package/dist/bin/pushary-bell-hook.js +3 -3
  3. package/dist/bin/pushary-bell.js +9 -10
  4. package/dist/bin/pushary-claude.js +62 -26
  5. package/dist/bin/pushary-clean.js +25 -7
  6. package/dist/bin/pushary-codex-bridge.js +4 -5
  7. package/dist/bin/pushary-codex-hook.js +8 -7
  8. package/dist/bin/pushary-codex.js +6 -4
  9. package/dist/bin/pushary-connect.js +4 -4
  10. package/dist/bin/pushary-daemon.js +155 -40
  11. package/dist/bin/pushary-disconnect.js +3 -4
  12. package/dist/bin/pushary-doctor.js +77 -39
  13. package/dist/bin/pushary-elicitation-hook.js +4 -2
  14. package/dist/bin/pushary-gemini-bridge.js +3 -4
  15. package/dist/bin/pushary-gemini-hook.js +6 -4
  16. package/dist/bin/pushary-hook.js +9 -7
  17. package/dist/bin/pushary-login.js +4 -4
  18. package/dist/bin/pushary-logout.js +5 -6
  19. package/dist/bin/pushary-mode.js +2 -2
  20. package/dist/bin/pushary-notification-hook.js +6 -4
  21. package/dist/bin/pushary-opencode-hook.js +14 -6
  22. package/dist/bin/pushary-permission-denied-hook.js +7 -5
  23. package/dist/bin/pushary-permission-hook.js +7 -5
  24. package/dist/bin/pushary-post-hook.js +6 -4
  25. package/dist/bin/pushary-prompt-hook.js +6 -4
  26. package/dist/bin/pushary-session-end-hook.js +6 -4
  27. package/dist/bin/pushary-session-start-hook.js +6 -4
  28. package/dist/bin/pushary-setup.js +125 -39
  29. package/dist/bin/pushary-stats.js +2 -2
  30. package/dist/bin/pushary-status.js +7 -6
  31. package/dist/bin/pushary-stop-hook.js +6 -4
  32. package/dist/bin/pushary-stopfailure-hook.js +6 -4
  33. package/dist/bin/pushary-suggestions.js +2 -2
  34. package/dist/bin/pushary-upgrade.js +110 -46
  35. package/dist/bin/pushary-wait.js +2 -2
  36. package/dist/bin/pushary.js +1 -1
  37. package/dist/chunk-2HYPZLYY.js +45 -0
  38. package/dist/{chunk-GOEXZ5QJ.js → chunk-3XTFX4CL.js} +10 -2
  39. package/dist/{chunk-ZYPS2XMT.js → chunk-4ZHQNBDP.js} +1 -1
  40. package/dist/{chunk-HUJAAWNL.js → chunk-GKAKAOKC.js} +10 -3
  41. package/dist/{chunk-LGXTBUT4.js → chunk-KOPUTJQC.js} +1 -0
  42. package/dist/{chunk-LALHWDWL.js → chunk-L3YUHEB6.js} +16 -4
  43. package/dist/{chunk-SQOFGPNX.js → chunk-LURY4WNF.js} +1 -1
  44. package/dist/{chunk-JT6TIBYH.js → chunk-MZBT2LXW.js} +4 -4
  45. package/dist/{chunk-3UBYTSCY.js → chunk-NFI2K2ZQ.js} +15 -7
  46. package/dist/chunk-NKHEHEUM.js +56 -0
  47. package/dist/{chunk-3O2XPYLF.js → chunk-OBI4YMCT.js} +1 -1
  48. package/dist/{chunk-LUCFN47K.js → chunk-QH4AA7QV.js} +1 -1
  49. package/dist/chunk-TRQ46MXR.js +648 -0
  50. package/dist/{chunk-VKVHY5HD.js → chunk-V37ZXMH3.js} +11 -3
  51. package/dist/{chunk-MHBC2XUP.js → chunk-VP7SDXYG.js} +1 -1
  52. package/dist/{chunk-B7JE4XHD.js → chunk-WQGMUIDW.js} +1 -1
  53. package/dist/src/index.js +7 -5
  54. package/package.json +1 -1
  55. package/dist/chunk-GIRT7677.js +0 -11
  56. package/dist/chunk-K42SZKYF.js +0 -343
  57. package/dist/chunk-NRGRUOAR.js +0 -25
  58. package/dist/chunk-RN3NOEJF.js +0 -9
@@ -4,8 +4,8 @@ import {
4
4
  } from "../chunk-VZKXBFUA.js";
5
5
  import {
6
6
  exitOnHelpFlag
7
- } from "../chunk-SQOFGPNX.js";
8
- import "../chunk-GOEXZ5QJ.js";
7
+ } from "../chunk-LURY4WNF.js";
8
+ import "../chunk-3XTFX4CL.js";
9
9
  import "../chunk-DINDL2CF.js";
10
10
  import "../chunk-KZERVKTD.js";
11
11
  import {
@@ -2,8 +2,9 @@
2
2
  import {
3
3
  describeLastSeen,
4
4
  listMachines
5
- } from "../chunk-MHBC2XUP.js";
6
- import "../chunk-RN3NOEJF.js";
5
+ } from "../chunk-VP7SDXYG.js";
6
+ import "../chunk-2HYPZLYY.js";
7
+ import "../chunk-V37ZXMH3.js";
7
8
  import {
8
9
  describeReach,
9
10
  fetchChannels,
@@ -19,17 +20,16 @@ import {
19
20
  } from "../chunk-5RUIFDTP.js";
20
21
  import {
21
22
  readKeySource
22
- } from "../chunk-LALHWDWL.js";
23
+ } from "../chunk-L3YUHEB6.js";
23
24
  import {
24
25
  rejectUnknownFlags
25
26
  } from "../chunk-GMXKITVA.js";
26
27
  import {
27
28
  exitOnHelpFlag
28
- } from "../chunk-SQOFGPNX.js";
29
- import "../chunk-GOEXZ5QJ.js";
29
+ } from "../chunk-LURY4WNF.js";
30
+ import "../chunk-3XTFX4CL.js";
30
31
  import "../chunk-BC3VCZ3E.js";
31
32
  import "../chunk-DINDL2CF.js";
32
- import "../chunk-6MTNS63X.js";
33
33
  import "../chunk-BSZYIAZL.js";
34
34
  import "../chunk-SAF6HGAA.js";
35
35
  import "../chunk-OFRNZWC4.js";
@@ -39,6 +39,7 @@ import {
39
39
  import {
40
40
  EXIT
41
41
  } from "../chunk-KZERVKTD.js";
42
+ import "../chunk-6MTNS63X.js";
42
43
  import "../chunk-2UMNXADU.js";
43
44
  import "../chunk-L4C2JXJS.js";
44
45
 
@@ -4,15 +4,17 @@ import {
4
4
  } from "../chunk-6CUUA7HO.js";
5
5
  import {
6
6
  handleStop
7
- } from "../chunk-3UBYTSCY.js";
8
- import "../chunk-HUJAAWNL.js";
9
- import "../chunk-LGXTBUT4.js";
10
- import "../chunk-RN3NOEJF.js";
7
+ } from "../chunk-NFI2K2ZQ.js";
8
+ import "../chunk-GKAKAOKC.js";
9
+ import "../chunk-KOPUTJQC.js";
10
+ import "../chunk-2HYPZLYY.js";
11
+ import "../chunk-V37ZXMH3.js";
11
12
  import "../chunk-BSZYIAZL.js";
12
13
  import "../chunk-SAF6HGAA.js";
13
14
  import "../chunk-OFRNZWC4.js";
14
15
  import "../chunk-7QLSKOSU.js";
15
16
  import "../chunk-KZERVKTD.js";
17
+ import "../chunk-6MTNS63X.js";
16
18
  import "../chunk-2UMNXADU.js";
17
19
  import "../chunk-L4C2JXJS.js";
18
20
 
@@ -4,15 +4,17 @@ import {
4
4
  } from "../chunk-6CUUA7HO.js";
5
5
  import {
6
6
  handleStopFailure
7
- } from "../chunk-3UBYTSCY.js";
8
- import "../chunk-HUJAAWNL.js";
9
- import "../chunk-LGXTBUT4.js";
10
- import "../chunk-RN3NOEJF.js";
7
+ } from "../chunk-NFI2K2ZQ.js";
8
+ import "../chunk-GKAKAOKC.js";
9
+ import "../chunk-KOPUTJQC.js";
10
+ import "../chunk-2HYPZLYY.js";
11
+ import "../chunk-V37ZXMH3.js";
11
12
  import "../chunk-BSZYIAZL.js";
12
13
  import "../chunk-SAF6HGAA.js";
13
14
  import "../chunk-OFRNZWC4.js";
14
15
  import "../chunk-7QLSKOSU.js";
15
16
  import "../chunk-KZERVKTD.js";
17
+ import "../chunk-6MTNS63X.js";
16
18
  import "../chunk-2UMNXADU.js";
17
19
  import "../chunk-L4C2JXJS.js";
18
20
 
@@ -7,8 +7,8 @@ import {
7
7
  } from "../chunk-GMXKITVA.js";
8
8
  import {
9
9
  exitOnHelpFlag
10
- } from "../chunk-SQOFGPNX.js";
11
- import "../chunk-GOEXZ5QJ.js";
10
+ } from "../chunk-LURY4WNF.js";
11
+ import "../chunk-3XTFX4CL.js";
12
12
  import "../chunk-DINDL2CF.js";
13
13
  import {
14
14
  UsageError
@@ -15,12 +15,29 @@ import "../chunk-ATBKJNWS.js";
15
15
  import {
16
16
  guardBinary
17
17
  } from "../chunk-AIGDBRIJ.js";
18
+ import {
19
+ daemonServiceManagerAvailable,
20
+ disableDaemonService,
21
+ ensureDaemonRunning,
22
+ hasDaemonServiceDefinition,
23
+ readDaemonStatus,
24
+ replaceDaemonService
25
+ } from "../chunk-TRQ46MXR.js";
26
+ import {
27
+ detectProviderLiveCapabilities,
28
+ hasSpawnProvider,
29
+ resolveControlPolicy
30
+ } from "../chunk-NKHEHEUM.js";
31
+ import {
32
+ needsShell
33
+ } from "../chunk-XHKBHWLX.js";
18
34
  import {
19
35
  execNpm,
20
36
  npmErrorMessage,
21
37
  resolveGlobalBinDir,
22
38
  resolveGlobalBinary
23
39
  } from "../chunk-PZA7G4CN.js";
40
+ import "../chunk-2HYPZLYY.js";
24
41
  import {
25
42
  CODEX_HOOK_BINARY,
26
43
  addCodexHookTrust,
@@ -35,19 +52,23 @@ import {
35
52
  hasCodexHooks,
36
53
  vscodePluginDir,
37
54
  vscodePluginMcp
38
- } from "../chunk-VKVHY5HD.js";
39
- import "../chunk-GIRT7677.js";
55
+ } from "../chunk-V37ZXMH3.js";
56
+ import {
57
+ readControlMode
58
+ } from "../chunk-L3YUHEB6.js";
40
59
  import {
41
60
  exitOnHelpFlag
42
- } from "../chunk-SQOFGPNX.js";
43
- import "../chunk-GOEXZ5QJ.js";
61
+ } from "../chunk-LURY4WNF.js";
62
+ import "../chunk-3XTFX4CL.js";
63
+ import "../chunk-BC3VCZ3E.js";
44
64
  import {
45
65
  isNewerVersion
46
66
  } from "../chunk-DINDL2CF.js";
67
+ import "../chunk-OFRNZWC4.js";
68
+ import "../chunk-KZERVKTD.js";
47
69
  import {
48
70
  readJsonSafe
49
71
  } from "../chunk-6MTNS63X.js";
50
- import "../chunk-KZERVKTD.js";
51
72
  import {
52
73
  getApiKey
53
74
  } from "../chunk-2UMNXADU.js";
@@ -57,6 +78,7 @@ import "../chunk-L4C2JXJS.js";
57
78
  import { existsSync as existsSync2 } from "fs";
58
79
  import { homedir as homedir2 } from "os";
59
80
  import { join as join2 } from "path";
81
+ import { execFileSync } from "child_process";
60
82
 
61
83
  // src/reapply.ts
62
84
  import { join, dirname } from "path";
@@ -84,7 +106,7 @@ var reapplyClaudeSettings = (apiKey, paths = {}) => {
84
106
  const settings = readJson(settingsPath);
85
107
  const claudeJson2 = readJson(claudeJsonPath);
86
108
  const configured = settings != null && hasPusharyClaudeHooks(settings) || claudeJson2 != null && hasPusharyMcp(claudeJson2);
87
- if (!configured) return { reapplied: false, hooks: false, mcp: false };
109
+ if (!configured) return { configured: false, reapplied: false, hooks: false, mcp: false };
88
110
  const settingsWritable = settings != null;
89
111
  const claudeJsonWritable = claudeJson2 != null;
90
112
  let hooks = false;
@@ -108,7 +130,7 @@ var reapplyClaudeSettings = (apiKey, paths = {}) => {
108
130
  } catch {
109
131
  }
110
132
  }
111
- return { reapplied: hooks || mcp, hooks, mcp };
133
+ return { configured: true, reapplied: hooks || mcp, hooks, mcp };
112
134
  };
113
135
  var readToml = (filePath) => {
114
136
  try {
@@ -124,7 +146,7 @@ var reapplyCodexSettings = (apiKey, paths = {}) => {
124
146
  const config = readToml(configPath);
125
147
  const hooksConfig = readJson(hooksPath);
126
148
  const configured = config != null && !!config.mcp_servers?.pushary || hooksConfig != null && hasCodexHooks(hooksConfig);
127
- if (!configured) return { reapplied: false, hooks: false, mcp: false };
149
+ if (!configured) return { configured: false, reapplied: false, hooks: false, mcp: false };
128
150
  const command = guardBinary(resolveGlobalBinary(CODEX_HOOK_BINARY) ?? CODEX_HOOK_BINARY);
129
151
  let hooks = false;
130
152
  let mcp = false;
@@ -147,14 +169,14 @@ var reapplyCodexSettings = (apiKey, paths = {}) => {
147
169
  } catch {
148
170
  }
149
171
  }
150
- return { reapplied: hooks || mcp, hooks, mcp };
172
+ return { configured: true, reapplied: hooks || mcp, hooks, mcp };
151
173
  };
152
174
  var reapplyGeminiSettings = (apiKey, paths = {}) => {
153
175
  const settingsPath = paths.settingsPath ?? join(homedir(), ".gemini", "settings.json");
154
176
  const settings = readJson(settingsPath);
155
- if (settings == null) return { reapplied: false, hooks: false, mcp: false };
177
+ if (settings == null) return { configured: false, reapplied: false, hooks: false, mcp: false };
156
178
  const configured = hasGeminiHooks(settings) || !!settings.mcpServers?.pushary;
157
- if (!configured) return { reapplied: false, hooks: false, mcp: false };
179
+ if (!configured) return { configured: false, reapplied: false, hooks: false, mcp: false };
158
180
  let hooks = false;
159
181
  let mcp = false;
160
182
  try {
@@ -167,20 +189,20 @@ var reapplyGeminiSettings = (apiKey, paths = {}) => {
167
189
  writeJson(settingsPath, settings);
168
190
  } catch {
169
191
  }
170
- return { reapplied: hooks || mcp, hooks, mcp };
192
+ return { configured: true, reapplied: hooks || mcp, hooks, mcp };
171
193
  };
172
194
  var reapplyPluginMcp = (mcpPath, apiKey) => {
173
- if (!apiKey) return { reapplied: false, hooks: false, mcp: false };
174
195
  const mcpConfig = readJson(mcpPath);
175
- if (mcpConfig == null) return { reapplied: false, hooks: false, mcp: false };
196
+ if (mcpConfig == null) return { configured: false, reapplied: false, hooks: false, mcp: false };
176
197
  const servers = mcpConfig.mcpServers;
177
- if (!servers?.pushary) return { reapplied: false, hooks: false, mcp: false };
198
+ if (!servers?.pushary) return { configured: false, reapplied: false, hooks: false, mcp: false };
199
+ if (!apiKey) return { configured: true, reapplied: false, hooks: false, mcp: false };
178
200
  try {
179
201
  addClaudeMcpServer(mcpConfig, apiKey);
180
202
  writeJson(mcpPath, mcpConfig);
181
- return { reapplied: true, hooks: false, mcp: true };
203
+ return { configured: true, reapplied: true, hooks: false, mcp: true };
182
204
  } catch {
183
- return { reapplied: false, hooks: false, mcp: false };
205
+ return { configured: true, reapplied: false, hooks: false, mcp: false };
184
206
  }
185
207
  };
186
208
  var reapplyCursorPlugin = (apiKey, paths = {}) => reapplyPluginMcp(
@@ -190,17 +212,17 @@ var reapplyCursorPlugin = (apiKey, paths = {}) => reapplyPluginMcp(
190
212
  var reapplyVsCodePlugin = (apiKey, paths = {}) => reapplyPluginMcp(paths.pluginMcpPath ?? vscodePluginMcp(), apiKey);
191
213
  var reapplyAllAgents = (apiKey) => {
192
214
  const runners = [
193
- { label: "Claude Code", run: () => reapplyClaudeSettings(apiKey) },
194
- { label: "Codex", run: () => reapplyCodexSettings(apiKey) },
195
- { label: "Gemini CLI", run: () => reapplyGeminiSettings(apiKey) },
196
- { label: "Cursor", run: () => reapplyCursorPlugin(apiKey) },
197
- { label: "VS Code", run: () => reapplyVsCodePlugin(apiKey) }
215
+ { id: "claude", label: "Claude Code", run: () => reapplyClaudeSettings(apiKey) },
216
+ { id: "codex", label: "Codex", run: () => reapplyCodexSettings(apiKey) },
217
+ { id: "gemini", label: "Gemini CLI", run: () => reapplyGeminiSettings(apiKey) },
218
+ { id: "cursor", label: "Cursor", run: () => reapplyCursorPlugin(apiKey) },
219
+ { id: "vscode", label: "VS Code", run: () => reapplyVsCodePlugin(apiKey) }
198
220
  ];
199
221
  const done = [];
200
222
  for (const runner of runners) {
201
223
  try {
202
224
  const result = runner.run();
203
- if (result.reapplied) done.push({ label: runner.label, result });
225
+ if (result.configured) done.push({ id: runner.id, label: runner.label, result });
204
226
  } catch {
205
227
  }
206
228
  }
@@ -263,7 +285,64 @@ var fetchLatestVersion = async () => {
263
285
  return void 0;
264
286
  }
265
287
  };
288
+ var finishUpgrade = async () => {
289
+ let apiKey;
290
+ try {
291
+ apiKey = getApiKey();
292
+ } catch {
293
+ apiKey = void 0;
294
+ }
295
+ const inspectedAgents = reapplyAllAgents(apiKey);
296
+ const refreshed = inspectedAgents.filter((agent) => agent.result.reapplied);
297
+ const configuredControlProviders = inspectedAgents.filter((agent) => agent.result.configured && (agent.id === "claude" || agent.id === "codex" || agent.id === "gemini")).map((agent) => agent.id);
298
+ const controlMode = readControlMode();
299
+ const daemonStatus = readDaemonStatus();
300
+ const serviceWasPresent = daemonStatus.kind !== "absent" || hasDaemonServiceDefinition();
301
+ const controlPolicy = resolveControlPolicy({
302
+ mode: controlMode,
303
+ providerAvailable: hasSpawnProvider(detectProviderLiveCapabilities(configuredControlProviders)),
304
+ managerAvailable: daemonServiceManagerAvailable(),
305
+ servicePresent: serviceWasPresent
306
+ });
307
+ let daemonReady = true;
308
+ if (controlPolicy === "disabled" && serviceWasPresent) {
309
+ daemonReady = (await disableDaemonService()).kind === "ok";
310
+ } else if (apiKey && controlPolicy === "enabled") {
311
+ const service = await replaceDaemonService();
312
+ const daemon = service.kind === "ok" ? await ensureDaemonRunning({ spawnDaemon: () => {
313
+ } }) : null;
314
+ daemonReady = service.kind === "ok" && (daemon?.kind === "running" || daemon?.kind === "started");
315
+ } else if (controlPolicy === "legacy" || controlMode === "on" && controlPolicy === "unsupported") {
316
+ daemonReady = false;
317
+ }
318
+ console.log();
319
+ if (refreshed.length > 0) {
320
+ console.log("Refreshed so the new hook set is active now:");
321
+ for (const agent of refreshed) {
322
+ const parts = [agent.result.hooks ? "hooks" : null, agent.result.mcp ? "MCP key" : null].filter(Boolean).join(" + ");
323
+ console.log(` ${agent.label.padEnd(14)} ${parts}`);
324
+ }
325
+ if (!apiKey) {
326
+ console.log();
327
+ console.log("No key was readable here, so MCP entries kept the key they already had.");
328
+ }
329
+ } else {
330
+ console.log("No agent config on this machine needed refreshing.");
331
+ }
332
+ console.log();
333
+ if (!daemonReady) {
334
+ console.error(controlPolicy === "disabled" ? "Upgrade installed, but the disabled background service could not be fully removed. Re-run setup with --control off." : controlPolicy === "legacy" ? "Upgrade installed, but a legacy background service cannot be safely adopted in this session. Run setup with --control off, or restore its provider and native service manager." : "Upgrade installed, but the background service did not start. Run: pushary daemon install");
335
+ process.exitCode = 1;
336
+ return;
337
+ }
338
+ console.log(controlPolicy === "disabled" ? "Done. Your hooks use the new global install; phone-start remains disabled." : controlPolicy === "unsupported" || controlPolicy === "preserve-absent" ? "Done. Your hooks use the new global install; no background service was added." : "Done. Your hooks and background service use the new global install.");
339
+ console.log("Verify with: pushary doctor");
340
+ };
266
341
  var main = async () => {
342
+ if (process.argv.includes("--finish-upgrade")) {
343
+ await finishUpgrade();
344
+ return;
345
+ }
267
346
  const installed = getInstalledVersion();
268
347
  if (!installed) {
269
348
  const found = detectInstallModes();
@@ -294,29 +373,14 @@ var main = async () => {
294
373
  console.error(`Upgrade failed: ${npmErrorMessage(err)}`);
295
374
  process.exit(1);
296
375
  }
297
- let apiKey;
298
- try {
299
- apiKey = getApiKey();
300
- } catch {
301
- apiKey = void 0;
302
- }
303
- const refreshed = reapplyAllAgents(apiKey);
304
- console.log();
305
- if (refreshed.length > 0) {
306
- console.log("Refreshed so the new hook set is active now:");
307
- for (const agent of refreshed) {
308
- const parts = [agent.result.hooks ? "hooks" : null, agent.result.mcp ? "MCP key" : null].filter(Boolean).join(" + ");
309
- console.log(` ${agent.label.padEnd(14)} ${parts}`);
310
- }
311
- if (!apiKey) {
312
- console.log();
313
- console.log("No key was readable here, so MCP entries kept the key they already had.");
314
- }
315
- } else {
316
- console.log("No agent config on this machine needed refreshing.");
376
+ const upgradedBinary = resolveGlobalBinary("pushary-upgrade");
377
+ if (!upgradedBinary) {
378
+ console.error("Upgrade installed, but the new pushary-upgrade binary could not be found on PATH.");
379
+ process.exit(1);
317
380
  }
318
- console.log();
319
- console.log("Done. Your hooks point at the global install.");
320
- console.log("Verify with: pushary doctor");
381
+ execFileSync(upgradedBinary, ["--finish-upgrade"], {
382
+ stdio: "inherit",
383
+ shell: needsShell(upgradedBinary)
384
+ });
321
385
  };
322
386
  main();
@@ -14,8 +14,8 @@ import {
14
14
  } from "../chunk-GMXKITVA.js";
15
15
  import {
16
16
  exitOnHelpFlag
17
- } from "../chunk-SQOFGPNX.js";
18
- import "../chunk-GOEXZ5QJ.js";
17
+ } from "../chunk-LURY4WNF.js";
18
+ import "../chunk-3XTFX4CL.js";
19
19
  import "../chunk-DINDL2CF.js";
20
20
  import "../chunk-SAF6HGAA.js";
21
21
  import {
@@ -3,7 +3,7 @@ import {
3
3
  COMMANDS,
4
4
  isCommandName,
5
5
  renderHelp
6
- } from "../chunk-GOEXZ5QJ.js";
6
+ } from "../chunk-3XTFX4CL.js";
7
7
  import {
8
8
  getPackageVersion
9
9
  } from "../chunk-DINDL2CF.js";
@@ -0,0 +1,45 @@
1
+ import {
2
+ pusharyDir
3
+ } from "./chunk-V37ZXMH3.js";
4
+ import {
5
+ writeFileAtomic
6
+ } from "./chunk-6MTNS63X.js";
7
+
8
+ // src/identity.ts
9
+ import { randomUUID } from "crypto";
10
+ import { mkdirSync, readFileSync, writeFileSync } from "fs";
11
+ import { dirname, join } from "path";
12
+ var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
13
+ var readMachineId = (filePath) => {
14
+ try {
15
+ const value = readFileSync(filePath, "utf8").trim();
16
+ return UUID.test(value) ? value : null;
17
+ } catch {
18
+ return null;
19
+ }
20
+ };
21
+ var getOrCreateMachineId = (options = {}) => {
22
+ const filePath = options.filePath ?? join(pusharyDir(), "machine-id");
23
+ const existing = readMachineId(filePath);
24
+ if (existing) return existing;
25
+ const id = (options.randomId ?? randomUUID)();
26
+ if (!UUID.test(id)) throw new Error("machine id generator returned an invalid UUID");
27
+ mkdirSync(dirname(filePath), { recursive: true });
28
+ try {
29
+ writeFileSync(filePath, `${id}
30
+ `, { flag: "wx", mode: 384 });
31
+ return id;
32
+ } catch (err) {
33
+ if (err.code !== "EEXIST") throw err;
34
+ }
35
+ const winner = readMachineId(filePath);
36
+ if (winner) return winner;
37
+ writeFileAtomic(filePath, `${id}
38
+ `, 384);
39
+ return id;
40
+ };
41
+ var getMachineId = (options = {}) => getOrCreateMachineId(options);
42
+
43
+ export {
44
+ getMachineId
45
+ };
@@ -19,8 +19,8 @@ var COMMAND_LIST = [
19
19
  module: "pushary-daemon",
20
20
  summary: "Keep this machine reachable so your phone can START a new session",
21
21
  detail: [
22
- "Leave it running in a project dir; it launches a headless",
23
- "'pushary claude --remote' when you send a prompt from the app."
22
+ "Install the native per-user service for restart-on-login and restart-on-failure.",
23
+ "A bare `pushary daemon` remains foreground mode for diagnostics."
24
24
  ]
25
25
  },
26
26
  {
@@ -114,6 +114,13 @@ var COMMAND_OPTIONS = {
114
114
  claude: [
115
115
  ["--remote", "Start headless and drive it entirely from your phone"],
116
116
  ["-p <prompt>", "Initial prompt"]
117
+ ],
118
+ daemon: [
119
+ ["install", "Install and start the native per-user service"],
120
+ ["start | stop | restart", "Control the installed service"],
121
+ ["status", "Show local daemon health and PID"],
122
+ ["uninstall", "Stop and remove the native service"],
123
+ ["foreground", "Run attached to this terminal for diagnostics"]
117
124
  ]
118
125
  };
119
126
  var SETUP_OPTIONS = [
@@ -125,6 +132,7 @@ var SETUP_OPTIONS = [
125
132
  ["--connect browser", "Approve in a browser tab instead. For a machine with no app"],
126
133
  ["--connect web", "Legacy browser subscribe page"],
127
134
  ["--connect none", "Skip the phone connect step, same as --skip-phone"],
135
+ ["--control auto|on|off", "Manage phone-start automatically (default), require it, or disable its background service"],
128
136
  ["--skip-phone", "Skip the phone connect step"],
129
137
  ["--verify roundtrip", "Send a real question and wait for you to answer it (default when a human is present)"],
130
138
  ["--verify push", "Only prove a notification was delivered. Automatic when nobody is at the terminal"],
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  pusharyDir
3
- } from "./chunk-VKVHY5HD.js";
3
+ } from "./chunk-V37ZXMH3.js";
4
4
  import {
5
5
  readJsonSafe
6
6
  } from "./chunk-6MTNS63X.js";
@@ -91,7 +91,11 @@ var readPendingCommand = (sessionId, namespace = "") => {
91
91
  unlinkSync(commandFile(sessionId, namespace));
92
92
  return null;
93
93
  }
94
- return { ...value, delivered: value.delivered === true };
94
+ return {
95
+ ...value,
96
+ delivered: value.delivered === true,
97
+ ...value.failed === true ? { failed: true } : {}
98
+ };
95
99
  } catch {
96
100
  return null;
97
101
  }
@@ -106,13 +110,13 @@ var savePendingCommand = (sessionId, command, namespace = "") => {
106
110
  return false;
107
111
  }
108
112
  };
109
- var markPendingCommandDelivered = (sessionId, id, namespace = "") => {
113
+ var updatePendingCommand = (sessionId, id, update, namespace = "") => {
110
114
  const command = readPendingCommand(sessionId, namespace);
111
115
  if (!command || command.id !== id) return false;
112
116
  const file = commandFile(sessionId, namespace);
113
117
  const temporary = `${file}.${process.pid}.tmp`;
114
118
  try {
115
- writeFileSync(temporary, JSON.stringify({ ...command, delivered: true }), { encoding: "utf-8", mode: 384 });
119
+ writeFileSync(temporary, JSON.stringify({ ...command, ...update }), { encoding: "utf-8", mode: 384 });
116
120
  renameSync(temporary, file);
117
121
  return true;
118
122
  } catch {
@@ -123,6 +127,8 @@ var markPendingCommandDelivered = (sessionId, id, namespace = "") => {
123
127
  return false;
124
128
  }
125
129
  };
130
+ var markPendingCommandDelivered = (sessionId, id, namespace = "") => updatePendingCommand(sessionId, id, { delivered: true, failed: void 0 }, namespace);
131
+ var markPendingCommandFailed = (sessionId, id, namespace = "") => updatePendingCommand(sessionId, id, { delivered: false, failed: true }, namespace);
126
132
  var removePendingCommand = (sessionId, id, namespace = "") => {
127
133
  if (readPendingCommand(sessionId, namespace)?.id !== id) return;
128
134
  try {
@@ -178,6 +184,7 @@ export {
178
184
  readPendingCommand,
179
185
  savePendingCommand,
180
186
  markPendingCommandDelivered,
187
+ markPendingCommandFailed,
181
188
  removePendingCommand,
182
189
  isDefaultSession,
183
190
  savePendingQuestion,
@@ -268,6 +268,7 @@ var sessionEndAction = (reason) => typeof reason === "string" && SESSION_END_ACT
268
268
  var taskTitleFrom = (prompt) => typeof prompt === "string" ? prompt.replace(/\s+/g, " ").trim().slice(0, TASK_TITLE_MAX_LENGTH) || void 0 : void 0;
269
269
 
270
270
  export {
271
+ HOOK_FALLBACK_MAX,
271
272
  describeToolCall,
272
273
  deriveToolTarget,
273
274
  parseApplyPatchOps,
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  resolveShellRc
3
3
  } from "./chunk-BC3VCZ3E.js";
4
+ import {
5
+ resolveInstallSourceFor
6
+ } from "./chunk-OFRNZWC4.js";
4
7
  import {
5
8
  backupFile,
6
9
  readJsonSafe,
7
10
  writeFileAtomic,
8
11
  writeJsonAtomic
9
12
  } from "./chunk-6MTNS63X.js";
10
- import {
11
- resolveInstallSourceFor
12
- } from "./chunk-OFRNZWC4.js";
13
13
  import {
14
14
  configFilePath
15
15
  } from "./chunk-2UMNXADU.js";
@@ -121,7 +121,12 @@ var writeKey = (apiKey, options = {}) => {
121
121
  }
122
122
  writeJsonAtomic(
123
123
  configPath,
124
- { ...current, apiKey, installSource: resolveInstallSourceFor(current) },
124
+ {
125
+ ...current,
126
+ apiKey,
127
+ installSource: resolveInstallSourceFor(current),
128
+ ...options.controlMode ? { controlMode: options.controlMode } : {}
129
+ },
125
130
  KEY_FILE_MODE
126
131
  );
127
132
  restrictFile(configPath);
@@ -177,6 +182,12 @@ var readConfigKey = (configPath) => {
177
182
  return key || null;
178
183
  };
179
184
  var readConfigFileKey = () => readConfigKey(configFilePath());
185
+ var readControlMode = () => {
186
+ const result = readJsonSafe(configFilePath());
187
+ if (result.kind !== "ok") return null;
188
+ const value = result.value.controlMode;
189
+ return value === "auto" || value === "on" || value === "off" ? value : null;
190
+ };
180
191
  var readKeySource = () => {
181
192
  const configPath = configFilePath();
182
193
  const envKey = process.env.PUSHARY_API_KEY?.trim() || null;
@@ -194,5 +205,6 @@ export {
194
205
  writeKey,
195
206
  clearKey,
196
207
  readConfigFileKey,
208
+ readControlMode,
197
209
  readKeySource
198
210
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  renderCommandHelp
3
- } from "./chunk-GOEXZ5QJ.js";
3
+ } from "./chunk-3XTFX4CL.js";
4
4
  import {
5
5
  getPackageVersion
6
6
  } from "./chunk-DINDL2CF.js";
@@ -17,7 +17,7 @@ import {
17
17
  readLastUserPrompt,
18
18
  repoKeyFor,
19
19
  throttlePass
20
- } from "./chunk-3UBYTSCY.js";
20
+ } from "./chunk-NFI2K2ZQ.js";
21
21
  import {
22
22
  DEFAULT_SESSION,
23
23
  askUser,
@@ -25,7 +25,7 @@ import {
25
25
  pollForAnswer,
26
26
  savePendingQuestion,
27
27
  sendNotification
28
- } from "./chunk-HUJAAWNL.js";
28
+ } from "./chunk-GKAKAOKC.js";
29
29
  import {
30
30
  deriveAction,
31
31
  deriveActionBody,
@@ -33,10 +33,10 @@ import {
33
33
  deriveToolTarget,
34
34
  describeToolCall,
35
35
  scopePathFor
36
- } from "./chunk-LGXTBUT4.js";
36
+ } from "./chunk-KOPUTJQC.js";
37
37
  import {
38
38
  getMachineId
39
- } from "./chunk-RN3NOEJF.js";
39
+ } from "./chunk-2HYPZLYY.js";
40
40
  import {
41
41
  getApiKey,
42
42
  getBaseUrl