@pushary/agent-hooks 1.9.15 → 1.9.16

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 (94) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/bin/pushary-bell-hook.js +21 -21
  3. package/dist/bin/pushary-bell.js +9 -9
  4. package/dist/bin/pushary-claude.js +20 -20
  5. package/dist/bin/pushary-clean.js +26 -26
  6. package/dist/bin/pushary-codex-bridge.js +15 -15
  7. package/dist/bin/pushary-codex-hook.js +24 -24
  8. package/dist/bin/pushary-codex.js +10 -10
  9. package/dist/bin/pushary-connect.js +22 -22
  10. package/dist/bin/pushary-cowork.js +3 -3
  11. package/dist/bin/pushary-daemon.js +27 -27
  12. package/dist/bin/pushary-disconnect.js +21 -21
  13. package/dist/bin/pushary-doctor.js +30 -30
  14. package/dist/bin/pushary-elicitation-hook.js +21 -21
  15. package/dist/bin/pushary-gemini-bridge.js +11 -11
  16. package/dist/bin/pushary-gemini-hook.js +22 -22
  17. package/dist/bin/pushary-hook.js +24 -24
  18. package/dist/bin/pushary-login.js +12 -12
  19. package/dist/bin/pushary-logout.js +10 -10
  20. package/dist/bin/pushary-mcp.js +8 -8
  21. package/dist/bin/pushary-mode.js +12 -12
  22. package/dist/bin/pushary-notification-hook.js +20 -20
  23. package/dist/bin/pushary-opencode-hook.js +21 -21
  24. package/dist/bin/pushary-permission-denied-hook.js +23 -23
  25. package/dist/bin/pushary-permission-hook.js +23 -23
  26. package/dist/bin/pushary-post-hook.js +20 -20
  27. package/dist/bin/pushary-prompt-hook.js +20 -20
  28. package/dist/bin/pushary-session-end-hook.js +20 -20
  29. package/dist/bin/pushary-session-start-hook.js +24 -24
  30. package/dist/bin/pushary-setup.js +440 -114
  31. package/dist/bin/pushary-stats.js +3 -3
  32. package/dist/bin/pushary-status.js +24 -24
  33. package/dist/bin/pushary-stop-hook.js +20 -20
  34. package/dist/bin/pushary-stopfailure-hook.js +20 -20
  35. package/dist/bin/pushary-suggestions.js +12 -12
  36. package/dist/bin/pushary-transcript-register.js +13 -13
  37. package/dist/bin/pushary-transcripts.js +6 -6
  38. package/dist/bin/pushary-upgrade.js +27 -27
  39. package/dist/bin/pushary-wait.js +12 -12
  40. package/dist/bin/pushary.js +9 -6
  41. package/dist/{chunk-3FQCZSEQ.js → chunk-3DZG5EXY.js} +2 -2
  42. package/dist/{chunk-YOHPJNFW.js → chunk-4MOOSU5Y.js} +35 -24
  43. package/dist/{chunk-C2OUUGCW.js → chunk-4PQGFWPO.js} +1 -1
  44. package/dist/{chunk-HJL5RBOU.js → chunk-5ETPCIG4.js} +22 -3
  45. package/dist/{chunk-7QZZK2YU.js → chunk-65OI53N7.js} +5 -5
  46. package/dist/{chunk-COZE23R3.js → chunk-7BNU5AVD.js} +1 -1
  47. package/dist/{chunk-5BIMNGRQ.js → chunk-7DE5BZMB.js} +6 -6
  48. package/dist/{chunk-YL6HU2KF.js → chunk-7EBZRHIH.js} +4 -4
  49. package/dist/{chunk-PSWFYNOX.js → chunk-BARUIIUR.js} +2 -2
  50. package/dist/{chunk-Q4JD6WNB.js → chunk-COGQIULO.js} +1 -1
  51. package/dist/{chunk-2DCGUCPZ.js → chunk-CXXYHPN7.js} +1 -1
  52. package/dist/{chunk-6LORREOI.js → chunk-DMJPRZVJ.js} +1 -1
  53. package/dist/{chunk-5EWNGYFD.js → chunk-DUDI3Z7F.js} +2 -2
  54. package/dist/{chunk-VP6WDFVO.js → chunk-EDEIIKCV.js} +1 -1
  55. package/dist/{chunk-6WECKO24.js → chunk-EDXJEQCK.js} +3 -3
  56. package/dist/{chunk-U4TMAANR.js → chunk-FO3MDD26.js} +4 -4
  57. package/dist/{chunk-NDKFHLJI.js → chunk-HBDZYQZX.js} +3 -3
  58. package/dist/{chunk-NDZBDXU3.js → chunk-HDQAN2C3.js} +27 -8
  59. package/dist/{chunk-H5YC7H6V.js → chunk-HKSH4KWI.js} +3 -3
  60. package/dist/{chunk-OIESPH3R.js → chunk-ICEMM4H2.js} +9 -9
  61. package/dist/{chunk-IQCNOEQL.js → chunk-IHPSFUL2.js} +6 -6
  62. package/dist/{chunk-LJJWNHUW.js → chunk-ISJ5ROXM.js} +1 -1
  63. package/dist/{chunk-WJO7SENK.js → chunk-J36EWRVL.js} +5 -5
  64. package/dist/{chunk-HSVJ62BE.js → chunk-JF2XRQR2.js} +1 -1
  65. package/dist/{chunk-6MLSXBNR.js → chunk-JNZU2F5B.js} +1 -1
  66. package/dist/{chunk-5LQ7QUVF.js → chunk-JQC7ZFHM.js} +1 -1
  67. package/dist/{chunk-JEJSAI6Z.js → chunk-L52HDK35.js} +3 -3
  68. package/dist/{chunk-ZMUDVPOT.js → chunk-LJJLMU5B.js} +2 -2
  69. package/dist/{chunk-CU7Y7Y4L.js → chunk-LYEXTD64.js} +2 -2
  70. package/dist/{chunk-6GRUERVL.js → chunk-MN2QJA7K.js} +1 -1
  71. package/dist/{chunk-WBQK6KD5.js → chunk-NM6IHHRO.js} +7 -7
  72. package/dist/{chunk-3YCYTGGK.js → chunk-OEMDXBV6.js} +1 -1
  73. package/dist/{chunk-PNUWMEIY.js → chunk-OIAZ2QAC.js} +2 -1
  74. package/dist/{chunk-SDDKVY4I.js → chunk-OLJ6YZME.js} +4 -4
  75. package/dist/{chunk-HTO2XULO.js → chunk-PJNZJPPX.js} +1 -1
  76. package/dist/{chunk-ME55HQOI.js → chunk-PVOOKOKX.js} +3 -3
  77. package/dist/{chunk-3QXCTYMG.js → chunk-PYPZ3MYC.js} +2 -2
  78. package/dist/{chunk-TFV6RATK.js → chunk-PZ33VQ54.js} +4 -4
  79. package/dist/{chunk-57VUNV32.js → chunk-QINDG6HO.js} +1 -1
  80. package/dist/{chunk-7VOEZPJ4.js → chunk-QJYBHKHO.js} +1 -1
  81. package/dist/{chunk-TMQMUGSN.js → chunk-R2FHKTD3.js} +1 -1
  82. package/dist/{chunk-SYLJL63X.js → chunk-S2LU6HIZ.js} +3 -3
  83. package/dist/{chunk-VKTDXQGH.js → chunk-VKYDON4A.js} +1 -1
  84. package/dist/{chunk-JC3AGMOP.js → chunk-W2AUU35K.js} +1 -1
  85. package/dist/{chunk-D72OJS4S.js → chunk-WVSECXIM.js} +2 -2
  86. package/dist/{chunk-NA5D62OI.js → chunk-XIJMMY6M.js} +1 -1
  87. package/dist/{chunk-Y6KG5JZP.js → chunk-YHHUYJQN.js} +1 -1
  88. package/dist/{chunk-FZW7Q7GM.js → chunk-ZOMVMLMU.js} +8 -13
  89. package/dist/{global-upgrade-MOBLBPIJ.js → global-upgrade-4XWM5RXK.js} +5 -5
  90. package/dist/{paths-55SPLYW5.js → paths-2RE6MBKF.js} +1 -1
  91. package/dist/{reapply-SOIJJKGO.js → reapply-Y5PSM7U7.js} +17 -17
  92. package/dist/src/index.js +16 -16
  93. package/dist/{upgrade-lock-FER4DMDA.js → upgrade-lock-HVWZN4BZ.js} +2 -2
  94. package/package.json +1 -1
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runBrowserLogin
4
- } from "../chunk-6LORREOI.js";
4
+ } from "../chunk-DMJPRZVJ.js";
5
5
  import {
6
6
  coworkOutput,
7
7
  coworkSelection
8
- } from "../chunk-TMQMUGSN.js";
8
+ } from "../chunk-R2FHKTD3.js";
9
9
  import "../chunk-PUZZELE6.js";
10
10
  import {
11
11
  clearDeclinedAgent,
12
12
  declinedAgentForSetupChoice
13
- } from "../chunk-2DCGUCPZ.js";
13
+ } from "../chunk-CXXYHPN7.js";
14
14
  import {
15
15
  CURSOR_GATE_SCRIPT,
16
16
  describeVersionManagerPin,
@@ -20,17 +20,17 @@ import {
20
20
  codexTrustOutcome,
21
21
  describeCodexTrustConfirmation,
22
22
  needsCodexTrustStep
23
- } from "../chunk-CU7Y7Y4L.js";
23
+ } from "../chunk-LYEXTD64.js";
24
24
  import {
25
25
  writeStdoutFully
26
26
  } from "../chunk-EO23EUOK.js";
27
27
  import {
28
28
  ensureCodexTrustsPusharyHooks
29
- } from "../chunk-LJJWNHUW.js";
29
+ } from "../chunk-ISJ5ROXM.js";
30
30
  import {
31
31
  readManagedControlEvidence,
32
32
  waitForMachineReadiness
33
- } from "../chunk-7QZZK2YU.js";
33
+ } from "../chunk-65OI53N7.js";
34
34
  import {
35
35
  describeSetupSideEffects,
36
36
  installAgentSkill,
@@ -44,7 +44,7 @@ import {
44
44
  soleGateFile,
45
45
  soleGateReceipt,
46
46
  soleGateReceiptFile
47
- } from "../chunk-H5YC7H6V.js";
47
+ } from "../chunk-HKSH4KWI.js";
48
48
  import {
49
49
  HERMES_PIP_PACKAGE,
50
50
  describeDetection,
@@ -57,7 +57,7 @@ import {
57
57
  runHermesConfigScript,
58
58
  shortenHome,
59
59
  vscodeSettingsTargets
60
- } from "../chunk-ZMUDVPOT.js";
60
+ } from "../chunk-LJJLMU5B.js";
61
61
  import {
62
62
  AgentConfigError,
63
63
  FX_PUSHARY_TOOLS_RULE,
@@ -68,12 +68,14 @@ import {
68
68
  agentFailureWire,
69
69
  configFailureKind,
70
70
  configureFxSettings,
71
+ describeMacAccountMismatch,
71
72
  describedAgentFailure,
72
73
  failedAgentsContext,
73
74
  fxIgnoresInstructionsFile,
74
75
  fxInstructionsBody,
75
76
  fxPusharyEntry,
76
77
  inspectFxInstallation,
78
+ matchMacAccountStep,
77
79
  readFxProfileForSetup,
78
80
  renderAgentInstructions,
79
81
  renderProjectAgentInstructions,
@@ -81,10 +83,10 @@ import {
81
83
  withFxPusharyServer,
82
84
  writeFxProfile,
83
85
  writeInstructionBlock
84
- } from "../chunk-NDZBDXU3.js";
86
+ } from "../chunk-HDQAN2C3.js";
85
87
  import {
86
88
  MCP_BRIDGE_BINARY
87
- } from "../chunk-D72OJS4S.js";
89
+ } from "../chunk-WVSECXIM.js";
88
90
  import {
89
91
  checkCodexInstall,
90
92
  detectProviderLiveCapabilities,
@@ -92,14 +94,14 @@ import {
92
94
  hasSpawnProvider,
93
95
  nodeCodexInstallProbe,
94
96
  resolveControlPolicy
95
- } from "../chunk-JC3AGMOP.js";
97
+ } from "../chunk-W2AUU35K.js";
96
98
  import {
97
99
  detectClaudeVersion
98
100
  } from "../chunk-RIVNNBEK.js";
99
101
  import {
100
102
  describeAutoUpdateAnnouncement,
101
103
  takeAutoUpdateAnnouncement
102
- } from "../chunk-6MLSXBNR.js";
104
+ } from "../chunk-JNZU2F5B.js";
103
105
  import {
104
106
  DAEMON_COLD_START_TIMEOUT_MS,
105
107
  clearDaemonNeedsLogin,
@@ -113,29 +115,29 @@ import {
113
115
  replaceDaemonService,
114
116
  stopDaemonService,
115
117
  waitForDaemonRemoteProof
116
- } from "../chunk-TFV6RATK.js";
118
+ } from "../chunk-PZ33VQ54.js";
117
119
  import {
118
120
  transcriptPreferenceForSetup
119
- } from "../chunk-HSVJ62BE.js";
121
+ } from "../chunk-JF2XRQR2.js";
120
122
  import "../chunk-XHKBHWLX.js";
121
123
  import {
122
124
  BROWSER_CONNECT_COMMAND,
123
125
  confirmAppConnection,
124
126
  connectDevice,
125
127
  connectViaAppPairing,
126
- describeRoundTrip,
128
+ describeRoundTripOutcome,
127
129
  pairingEscapeApplies,
128
130
  printConnectInstructions,
129
131
  roundTripEvidence,
130
132
  verifyRoundTrip
131
- } from "../chunk-YOHPJNFW.js";
133
+ } from "../chunk-4MOOSU5Y.js";
132
134
  import "../chunk-3EGEA4KH.js";
133
135
  import {
134
136
  setHumanStream
135
137
  } from "../chunk-QCGNGEGE.js";
136
138
  import {
137
139
  readMacAppAgentKey
138
- } from "../chunk-6WECKO24.js";
140
+ } from "../chunk-EDXJEQCK.js";
139
141
  import {
140
142
  OPENCODE_HOOK_BINARY,
141
143
  addOpenCodeMcpServer,
@@ -151,12 +153,12 @@ import {
151
153
  openCodePluginDir,
152
154
  openCodePluginFile,
153
155
  openCodeWired
154
- } from "../chunk-NDKFHLJI.js";
156
+ } from "../chunk-HBDZYQZX.js";
155
157
  import {
156
158
  ansiPainter,
157
159
  createIo,
158
160
  useColor
159
- } from "../chunk-U4TMAANR.js";
161
+ } from "../chunk-FO3MDD26.js";
160
162
  import {
161
163
  addClaudeMcpServer,
162
164
  addCompatibleHooks,
@@ -173,7 +175,7 @@ import {
173
175
  hasCompatibleHooks,
174
176
  isClaudeMcpSnapshot,
175
177
  updateClaudeMcpSnapshot
176
- } from "../chunk-ME55HQOI.js";
178
+ } from "../chunk-PVOOKOKX.js";
177
179
  import {
178
180
  KEEP_HINT,
179
181
  captureHookTakeover,
@@ -186,19 +188,19 @@ import {
186
188
  mcpBearerKey,
187
189
  planHooks,
188
190
  pusharyHookExecutable
189
- } from "../chunk-FZW7Q7GM.js";
191
+ } from "../chunk-ZOMVMLMU.js";
190
192
  import {
191
193
  GEMINI_HOOK_BINARY,
192
194
  addGeminiHooks,
193
195
  addGeminiMcpServer
194
- } from "../chunk-VP6WDFVO.js";
196
+ } from "../chunk-EDEIIKCV.js";
195
197
  import {
196
198
  AGENT_CHOICES,
197
199
  effectiveControlMode,
198
200
  effectiveVerify,
199
201
  parseSetupOptions
200
202
  } from "../chunk-YKYFFCB5.js";
201
- import "../chunk-7VOEZPJ4.js";
203
+ import "../chunk-QJYBHKHO.js";
202
204
  import "../chunk-GMXKITVA.js";
203
205
  import {
204
206
  compareVersion,
@@ -216,28 +218,28 @@ import {
216
218
  recordMacAppKeyPrefix,
217
219
  resolveShellRc,
218
220
  writeKey
219
- } from "../chunk-5EWNGYFD.js";
221
+ } from "../chunk-DUDI3Z7F.js";
220
222
  import {
221
223
  reportEvent
222
- } from "../chunk-IQCNOEQL.js";
224
+ } from "../chunk-IHPSFUL2.js";
223
225
  import "../chunk-52JA2WTH.js";
224
- import "../chunk-YL6HU2KF.js";
226
+ import "../chunk-7EBZRHIH.js";
225
227
  import "../chunk-POFBE6ZY.js";
226
- import "../chunk-SYLJL63X.js";
228
+ import "../chunk-S2LU6HIZ.js";
227
229
  import {
228
230
  checkApiKey,
229
231
  describeKeyCheck,
230
232
  samePusharyAccount
231
- } from "../chunk-PSWFYNOX.js";
232
- import "../chunk-3FQCZSEQ.js";
233
- import "../chunk-NA5D62OI.js";
233
+ } from "../chunk-BARUIIUR.js";
234
+ import "../chunk-3DZG5EXY.js";
235
+ import "../chunk-XIJMMY6M.js";
234
236
  import {
235
237
  UsageError
236
238
  } from "../chunk-7QLSKOSU.js";
237
239
  import {
238
240
  EXIT
239
241
  } from "../chunk-KZERVKTD.js";
240
- import "../chunk-VKTDXQGH.js";
242
+ import "../chunk-VKYDON4A.js";
241
243
  import {
242
244
  NpmMissingError,
243
245
  addCodexHookTrust,
@@ -274,7 +276,7 @@ import {
274
276
  hermesConfigReceipt,
275
277
  hookTakeoverReceipt,
276
278
  installGlobally,
277
- npmErrorMessage,
279
+ npmInstallFailure,
278
280
  plannedGlobalPackage,
279
281
  pusharyConfigFile,
280
282
  readCodexMcpAuth,
@@ -282,18 +284,18 @@ import {
282
284
  resolveGlobalBinary,
283
285
  vscodePluginDir,
284
286
  vscodePluginMcp
285
- } from "../chunk-HJL5RBOU.js";
287
+ } from "../chunk-5ETPCIG4.js";
286
288
  import {
287
289
  backupFile,
288
290
  readJsonSafe,
289
291
  writeFileAtomic,
290
292
  writeJsonAtomic
291
293
  } from "../chunk-T7E75EKA.js";
292
- import "../chunk-HTO2XULO.js";
294
+ import "../chunk-PJNZJPPX.js";
293
295
  import {
294
296
  NPX_LATEST_COMMAND,
295
297
  renderCommandHelp
296
- } from "../chunk-PNUWMEIY.js";
298
+ } from "../chunk-OIAZ2QAC.js";
297
299
  import {
298
300
  HOOK_COMPATIBLE_PROFILES,
299
301
  PRETOOLUSE_GATED_TOOLS,
@@ -321,6 +323,270 @@ import { parse as parseTOML, stringify as stringifyTOML } from "smol-toml";
321
323
  // src/cli/exit.ts
322
324
  var isExitPromptError = (err) => err instanceof Error && (err.name === "ExitPromptError" || err.message.includes("User force closed") || /force closed the prompt/i.test(err.message));
323
325
 
326
+ // ../contracts/src/billing.ts
327
+ var PARTNER_SELF_SERVE_PLANS = ["partner_launch", "partner_growth", "partner_scale"];
328
+ var PARTNER_PLAN_TYPES = ["partner", ...PARTNER_SELF_SERVE_PLANS];
329
+ var PLAN_TYPES = ["free", "agent", "agent_pro", "team", "partner", "starter", "growth", "pro", "enterprise", ...PARTNER_SELF_SERVE_PLANS];
330
+ var PAID_PLAN_TYPES = ["agent", "agent_pro", "team", "partner", "starter", "growth", "pro", ...PARTNER_SELF_SERVE_PLANS];
331
+ var PLAN_CONFIGS = {
332
+ free: {
333
+ name: "Free",
334
+ price: 0,
335
+ priceId: null,
336
+ limits: {
337
+ subscribers: 100,
338
+ notifications: 500,
339
+ sites: 1,
340
+ members: 1,
341
+ campaigns: 25,
342
+ dataRetentionDays: 7
343
+ },
344
+ features: {
345
+ abTesting: false,
346
+ apiAccess: false,
347
+ prioritySupport: false
348
+ }
349
+ },
350
+ agent: {
351
+ name: "Agent",
352
+ price: 9.99,
353
+ priceId: null,
354
+ limits: {
355
+ subscribers: 3,
356
+ notifications: 5e3,
357
+ sites: 1,
358
+ members: 1,
359
+ campaigns: 0,
360
+ dataRetentionDays: 30
361
+ },
362
+ features: {
363
+ abTesting: false,
364
+ apiAccess: true,
365
+ prioritySupport: false
366
+ }
367
+ },
368
+ agent_pro: {
369
+ name: "Agent Pro",
370
+ price: 19.99,
371
+ priceId: null,
372
+ limits: {
373
+ subscribers: 5,
374
+ notifications: Infinity,
375
+ sites: 1,
376
+ members: 5,
377
+ campaigns: 0,
378
+ dataRetentionDays: 90
379
+ },
380
+ features: {
381
+ abTesting: false,
382
+ apiAccess: true,
383
+ prioritySupport: true
384
+ }
385
+ },
386
+ team: {
387
+ name: "Team",
388
+ price: 59.99,
389
+ priceId: null,
390
+ limits: {
391
+ subscribers: 25,
392
+ notifications: Infinity,
393
+ sites: 1,
394
+ members: 10,
395
+ campaigns: 0,
396
+ dataRetentionDays: 365
397
+ },
398
+ features: {
399
+ abTesting: false,
400
+ apiAccess: true,
401
+ prioritySupport: true
402
+ }
403
+ },
404
+ partner: {
405
+ name: "Partner",
406
+ price: 99,
407
+ priceId: null,
408
+ limits: {
409
+ subscribers: 25e3,
410
+ notifications: 1e5,
411
+ sites: 1,
412
+ members: 10,
413
+ campaigns: 0,
414
+ dataRetentionDays: 365
415
+ },
416
+ features: {
417
+ abTesting: false,
418
+ apiAccess: true,
419
+ prioritySupport: true
420
+ }
421
+ },
422
+ partner_launch: {
423
+ name: "Partner Launch",
424
+ price: 99,
425
+ priceId: null,
426
+ limits: {
427
+ subscribers: 5e3,
428
+ // Closed until request admission and fan-out accounting ship. Never sell
429
+ // human requests against the legacy notification-delivery counter.
430
+ notifications: 0,
431
+ humanDecisionRequests: 25e3,
432
+ sites: 1,
433
+ members: 3,
434
+ campaigns: 0,
435
+ dataRetentionDays: 90
436
+ },
437
+ features: { abTesting: false, apiAccess: true, prioritySupport: false }
438
+ },
439
+ partner_growth: {
440
+ name: "Partner Growth",
441
+ price: 299,
442
+ priceId: null,
443
+ limits: {
444
+ subscribers: 25e3,
445
+ // Closed until request admission and fan-out accounting ship. Never sell
446
+ // human requests against the legacy notification-delivery counter.
447
+ notifications: 0,
448
+ humanDecisionRequests: 1e5,
449
+ sites: 1,
450
+ members: 10,
451
+ campaigns: 0,
452
+ dataRetentionDays: 365
453
+ },
454
+ features: { abTesting: false, apiAccess: true, prioritySupport: true }
455
+ },
456
+ partner_scale: {
457
+ name: "Partner Scale",
458
+ price: 799,
459
+ priceId: null,
460
+ limits: {
461
+ subscribers: 1e5,
462
+ // Closed until request admission and fan-out accounting ship. Never sell
463
+ // human requests against the legacy notification-delivery counter.
464
+ notifications: 0,
465
+ humanDecisionRequests: 5e5,
466
+ sites: 1,
467
+ members: 25,
468
+ campaigns: 0,
469
+ dataRetentionDays: 730
470
+ },
471
+ features: { abTesting: false, apiAccess: true, prioritySupport: true }
472
+ },
473
+ starter: {
474
+ name: "Starter",
475
+ price: 29,
476
+ priceId: null,
477
+ limits: {
478
+ subscribers: 5e3,
479
+ notifications: 25e3,
480
+ sites: 3,
481
+ members: 5,
482
+ campaigns: 250,
483
+ dataRetentionDays: 30
484
+ },
485
+ features: {
486
+ abTesting: true,
487
+ apiAccess: true,
488
+ prioritySupport: false
489
+ }
490
+ },
491
+ growth: {
492
+ name: "Growth",
493
+ price: 99,
494
+ priceId: null,
495
+ limits: {
496
+ subscribers: 25e3,
497
+ notifications: 1e5,
498
+ sites: 10,
499
+ members: 15,
500
+ campaigns: Infinity,
501
+ dataRetentionDays: 90
502
+ },
503
+ features: {
504
+ abTesting: true,
505
+ apiAccess: true,
506
+ prioritySupport: true
507
+ }
508
+ },
509
+ pro: {
510
+ name: "Pro",
511
+ price: 149,
512
+ priceId: null,
513
+ limits: {
514
+ subscribers: 1e5,
515
+ notifications: 5e5,
516
+ sites: 25,
517
+ members: 50,
518
+ campaigns: Infinity,
519
+ dataRetentionDays: 180
520
+ },
521
+ features: {
522
+ abTesting: true,
523
+ apiAccess: true,
524
+ prioritySupport: true
525
+ }
526
+ },
527
+ enterprise: {
528
+ name: "Enterprise",
529
+ price: -1,
530
+ priceId: null,
531
+ limits: {
532
+ subscribers: Infinity,
533
+ notifications: Infinity,
534
+ sites: Infinity,
535
+ members: Infinity,
536
+ campaigns: Infinity,
537
+ dataRetentionDays: Infinity
538
+ },
539
+ features: {
540
+ abTesting: true,
541
+ apiAccess: true,
542
+ prioritySupport: true
543
+ }
544
+ }
545
+ };
546
+
547
+ // src/setup/key-account.ts
548
+ var agentConfigKeySource = (macAppInstalled) => macAppInstalled ? "mac-app" : "agent-settings";
549
+ var pickReusableKey = (candidates) => {
550
+ if (candidates.storedKey !== null && candidates.storedCheck !== null) {
551
+ return { source: "stored", key: candidates.storedKey, check: candidates.storedCheck };
552
+ }
553
+ if (candidates.envKey && candidates.envCheck !== null) {
554
+ return { source: "environment", key: candidates.envKey, check: candidates.envCheck };
555
+ }
556
+ if (candidates.agentConfigKey !== null && candidates.agentConfigCheck !== null) {
557
+ return { source: agentConfigKeySource(candidates.macAppInstalled), key: candidates.agentConfigKey, check: candidates.agentConfigCheck };
558
+ }
559
+ return null;
560
+ };
561
+ var REUSED_KEY_SOURCE_LABELS = {
562
+ stored: "the key saved on this machine",
563
+ environment: "the key in PUSHARY_API_KEY",
564
+ "mac-app": "the key Pushary for Mac is signed in with",
565
+ "agent-settings": "a key found in your agent settings"
566
+ };
567
+ var maskKey = (key) => `${key.slice(0, 8)}...${key.slice(-4)}`;
568
+ var describeFoundKey = (reusable) => `Found ${REUSED_KEY_SOURCE_LABELS[reusable.source]}: ${maskKey(reusable.key)}`;
569
+ var describeUsedKey = (reusable) => `Using ${REUSED_KEY_SOURCE_LABELS[reusable.source]}: ${maskKey(reusable.key)}`;
570
+ var keyReuseConfirmation = (context) => context.stdoutIsTty && !context.nonInteractive && !context.dryRun && context.canPairWithApp ? "ask" : "announce";
571
+ var accountHandleOf = (identity) => identity.siteSlug ? `@${identity.siteSlug}` : identity.siteName || null;
572
+ var keyAccountHandle = (check2) => check2.kind === "ok" ? accountHandleOf(check2.identity) : null;
573
+ var continueAsQuestion = (account) => account ? `Continue as ${account}?` : "Continue with this key?";
574
+ var WRONG_ACCOUNT_HINT = `Wrong account? Run ${NPX_LATEST_COMMAND} setup --connect app`;
575
+ var planNameOf = (plan) => {
576
+ const known = PLAN_TYPES.find((type) => type === plan);
577
+ return known ? PLAN_CONFIGS[known].name : null;
578
+ };
579
+ var describeSignedIn = (identity) => {
580
+ const account = accountHandleOf(identity) ?? "your account";
581
+ const planName = planNameOf(identity.plan);
582
+ return planName ? `Signed in to ${account} (${planName} plan)` : `Signed in to ${account}`;
583
+ };
584
+ var describeLockedKeyAccount = (key, account) => account ? `${account} has no active plan.` : `The key ${maskKey(key)} is on an account with no active plan.`;
585
+
586
+ // src/setup/service-notice.ts
587
+ var PHONE_START_OPT_OUT = "Turn it off with --control off.";
588
+ var describeServiceInstall = (servicePresent) => servicePresent ? `Updating the background service that lets your phone start agents. ${PHONE_START_OPT_OUT}` : `Installing a background service so your phone can start agents. ${PHONE_START_OPT_OUT}`;
589
+
324
590
  // src/setup/phone-check.ts
325
591
  var planPhoneCheck = (context) => {
326
592
  if (context.locked) return "locked";
@@ -334,6 +600,23 @@ var planPhoneCheck = (context) => {
334
600
 
335
601
  // src/setup/closing.ts
336
602
  var ACTIVITY_URL = "pushary.com/dashboard/agent/activity";
603
+ var TRANSCRIPT_SYNC_NOTICE = `Transcripts sync redacted and encrypted. Your phone can decrypt them; authorized Pushary compliance admins can recover them for documented legal obligations. Turn off: ${NPX_LATEST_COMMAND} setup --transcripts off`;
604
+ var PAIR_PHONE_APP_HINT = `${NPX_LATEST_COMMAND} setup --connect app pair the Pushary app on your phone`;
605
+ var RESTART_AGENTS_LINE = "Restart any open agent sessions so they pick up Pushary.";
606
+ var ANSWERING_DEVICES = {
607
+ mobile_app: "Your phone",
608
+ mobile_background: "Your phone",
609
+ mac_app: "Your Mac"
610
+ };
611
+ var SIMULATED_ANSWER_HEADLINE = "Agents configured. The test answer was simulated.";
612
+ var answerSeconds = (latencyMs) => Math.max(1, Math.round(latencyMs / 1e3));
613
+ var describeAnsweredHeadline = (answer) => {
614
+ if (!answer) return "You're set. The test question was answered.";
615
+ if (answer.answerSource === "sandbox") return SIMULATED_ANSWER_HEADLINE;
616
+ const device = answer.answerSource ? ANSWERING_DEVICES[answer.answerSource] : void 0;
617
+ const seconds = answerSeconds(answer.latencyMs);
618
+ return device ? `You're set. ${device} answered in ${seconds}s.` : `You're set. The test question was answered in ${seconds}s.`;
619
+ };
337
620
  var COMMAND_SURFACE = [
338
621
  { command: "pushary status", summary: "what this machine is connected to" },
339
622
  { command: "pushary mode", summary: "choose where approval requests appear" },
@@ -353,14 +636,13 @@ var nextSteps = (facts, reachable) => {
353
636
  lines.push("Transcripts: disabled on this machine.");
354
637
  lines.push("pushary setup --transcripts on enable encrypted transcript sync");
355
638
  } else {
356
- lines.push("Transcripts: Pushary-launched Claude, Codex and Gemini sessions sync redacted, encrypted content. Your phone can decrypt it; authorized Pushary compliance admins can recover it for documented legal obligations.");
357
- lines.push("pushary setup --transcripts off disable transcript sync on this machine");
639
+ lines.push(TRANSCRIPT_SYNC_NOTICE);
358
640
  }
359
641
  if (facts.completed.includes("claude_code")) {
360
642
  lines.push("pushary claude launch a managed Claude Code session");
361
643
  }
362
644
  if (!reachable) {
363
- lines.push("pushary pair pair a phone without re-running setup");
645
+ lines.push(PAIR_PHONE_APP_HINT);
364
646
  }
365
647
  lines.push("pushary doctor re-check everything");
366
648
  return lines;
@@ -415,8 +697,8 @@ var describeClosing = (facts) => {
415
697
  if (facts.roundTripAnswered === true) {
416
698
  return {
417
699
  ok: true,
418
- headline: "Agents configured. Synthetic answer returned.",
419
- next: ["Agent hook activation remains unverified.", ...nextSteps(facts, reachable), "", `Approvals: ${ACTIVITY_URL}`],
700
+ headline: describeAnsweredHeadline(facts.roundTripAnswer),
701
+ next: [RESTART_AGENTS_LINE, ...nextSteps(facts, reachable), "", `Approvals: ${ACTIVITY_URL}`],
420
702
  exitCode: EXIT.OK,
421
703
  reason: "synthetic-answer"
422
704
  };
@@ -473,13 +755,26 @@ var describeClosing = (facts) => {
473
755
  };
474
756
  var RETRY_AGENT_LABEL = "Retry just this agent:";
475
757
  var INSTALL_NODE_STEP = "Install the current Node.js LTS, which includes npm, then run setup again.";
476
- var nextStepFor = (failure) => failure.remedy === "install-node" ? { kind: "install-node", instruction: INSTALL_NODE_STEP } : { kind: "retry-agent", command: retryAgentCommand(failure.agent) };
758
+ var NPM_PREFIX_STEP = "npm can't install globally here. Use nvm, or run: npm config set prefix ~/.npm-global, then run setup again.";
759
+ var nextStepFor = (failure) => {
760
+ switch (failure.remedy) {
761
+ case "install-node":
762
+ return { kind: "install-node", instruction: INSTALL_NODE_STEP };
763
+ case "fix-npm-prefix":
764
+ return { kind: "fix-npm-prefix", instruction: NPM_PREFIX_STEP };
765
+ case "match-mac-account":
766
+ return { kind: "match-mac-account", instruction: matchMacAccountStep(failure.agent, null) };
767
+ case "retry-agent":
768
+ return { kind: "retry-agent", command: retryAgentCommand(failure.agent) };
769
+ }
770
+ };
477
771
  var describeAgentFailures = (failures, agentName) => failures.map((failure) => ({
478
772
  summary: `${agentName(failure.agent)}: ${failure.detail}`,
479
773
  next: nextStepFor(failure)
480
774
  }));
481
775
  var agentFailureFollowUps = (failures) => [
482
776
  ...failures.some((failure) => failure.remedy === "install-node") ? ["install-node"] : [],
777
+ ...failures.some((failure) => failure.remedy === "fix-npm-prefix") ? ["fix-npm-prefix"] : [],
483
778
  ...failures.some((failure) => failure.remedy === "retry-agent") ? ["run-doctor"] : []
484
779
  ];
485
780
  var unverifiedKeyClosingReason = (facts) => {
@@ -791,7 +1086,7 @@ var formatError = (err) => {
791
1086
  if (err instanceof Error) {
792
1087
  const errWithSignal = err;
793
1088
  if (errWithSignal.killed && errWithSignal.signal === "SIGTERM") {
794
- return "timed out \u2014 check your network connection";
1089
+ return "timed out, check your network connection";
795
1090
  }
796
1091
  return err.message;
797
1092
  }
@@ -823,7 +1118,7 @@ var installGlobally2 = async () => {
823
1118
  try {
824
1119
  await installGlobally({ version });
825
1120
  } catch (err) {
826
- throw err instanceof NpmMissingError ? err : new Error(npmErrorMessage(err));
1121
+ throw err instanceof NpmMissingError ? err : npmInstallFailure(err);
827
1122
  }
828
1123
  });
829
1124
  globalInstallReady = true;
@@ -1568,10 +1863,10 @@ var setupCustom = async (apiKey, context) => {
1568
1863
  console.log(` ${dim(`Use the key saved in ${PUSHARY_CONFIG_FILE} wherever`)} ${cyan("$PUSHARY_API_KEY")} ${dim("appears below.")}`);
1569
1864
  }
1570
1865
  console.log();
1571
- console.log(` ${dim("MCP clients (Windsurf, LangChain, custom) \u2014 add this server:")}`);
1866
+ console.log(` ${dim("For MCP clients (Windsurf, LangChain, custom), add this server:")}`);
1572
1867
  console.log(` ${cyan("https://pushary.com/api/mcp/mcp")} ${dim("with header")} ${cyan("Authorization: Bearer $PUSHARY_API_KEY")}`);
1573
1868
  console.log();
1574
- console.log(` ${dim("No-code / HTTP (n8n, Zapier, scripts) \u2014 ask a human and wait:")}`);
1869
+ console.log(` ${dim("For no-code or HTTP (n8n, Zapier, scripts), ask a human and wait:")}`);
1575
1870
  console.log(` ${dim("POST")} ${cyan("https://pushary.com/api/v1/server/ask")} ${dim("body")} ${cyan('{"question":"Approve?","type":"confirm","wait":true}')}`);
1576
1871
  console.log();
1577
1872
  console.log(` ${dim("Full guide:")} ${cyan("https://pushary.com/docs/agents/connect-any-agent")}`);
@@ -1870,7 +2165,7 @@ var offerProjectInstructions = async (agents, options) => {
1870
2165
  if (targets.length === 0 || !process.stdout.isTTY || options.nonInteractive) return;
1871
2166
  console.log();
1872
2167
  const wanted = await confirm({
1873
- message: "Add Pushary instructions to this project's CLAUDE.md/AGENTS.md so your whole team's agents can use phone approvals?",
2168
+ message: `Add a Pushary section to ${targets.map((target) => target.file).join(" and ")} in this folder? It tells agents here to ask through Pushary.`,
1874
2169
  default: false
1875
2170
  });
1876
2171
  if (!wanted) return;
@@ -1890,7 +2185,7 @@ var offerClaudeAlias = async (options) => {
1890
2185
  return;
1891
2186
  }
1892
2187
  const wanted = await confirm({
1893
- message: `Make plain ${bold("claude")} reachable from your phone by default? (adds an alias to ${basename(rc.path)}, reversible)`,
2188
+ message: `Start ${bold("claude")} through Pushary, so your phone can reach idle sessions? This adds an alias to ${basename(rc.path)}.`,
1894
2189
  default: false
1895
2190
  });
1896
2191
  if (!wanted) return;
@@ -1924,8 +2219,7 @@ var verifyKeyOrExit = async (apiKey, options, precomputed) => {
1924
2219
  process.exit(await abortWith("key-refused", report.exitCode, result.kind));
1925
2220
  }
1926
2221
  if (result.kind === "ok") {
1927
- const { siteSlug, siteName, plan } = result.identity;
1928
- console.log(` ${check} Key verified ${dim(`(${siteSlug || siteName} \xB7 plan ${plan})`)}`);
2222
+ console.log(` ${check} ${describeSignedIn(result.identity)}`);
1929
2223
  } else {
1930
2224
  console.log(` ${yellow("!")} ${report.message}`);
1931
2225
  for (const line of report.next) console.log(` ${dim(line)}`);
@@ -1954,6 +2248,14 @@ var usableStoredKey = async (apiKey) => {
1954
2248
  }
1955
2249
  return result.kind === "rejected" || result.kind === "malformed" ? null : result;
1956
2250
  };
2251
+ var acceptReusableKey = async (reusable, confirmation) => {
2252
+ if (confirmation === "announce") {
2253
+ console.log(` ${check} ${describeUsedKey(reusable)}`);
2254
+ return true;
2255
+ }
2256
+ console.log(` ${describeFoundKey(reusable)}`);
2257
+ return confirm({ message: continueAsQuestion(keyAccountHandle(reusable.check)), default: true });
2258
+ };
1957
2259
  var AGENT_NAMES = {
1958
2260
  claude_code: "Claude Code",
1959
2261
  codex: "Codex",
@@ -1980,6 +2282,22 @@ var AGENT_CAPABILITIES = {
1980
2282
  };
1981
2283
  var failedAgentNames = (failures) => failures.map((failure) => AGENT_NAMES[failure.agent]);
1982
2284
  var agentFailureNextLine = (next) => next.kind === "retry-agent" ? `${dim(RETRY_AGENT_LABEL)} ${cyan(next.command)}` : next.instruction;
2285
+ var NO_ANSWER_YET = "No answer yet";
2286
+ var roundTripAnswerOf = (outcome) => outcome?.kind === "answered" ? { latencyMs: outcome.latencyMs, answerSource: outcome.answerSource } : null;
2287
+ var roundTripSpinnerEnd = (outcome) => {
2288
+ const report = describeRoundTripOutcome(outcome);
2289
+ return report.ok ? { ok: true, label: report.message } : { ok: false, label: NO_ANSWER_YET };
2290
+ };
2291
+ var agentFailureFollowUpLine = (followUp) => {
2292
+ switch (followUp) {
2293
+ case "install-node":
2294
+ return INSTALL_NODE_STEP;
2295
+ case "fix-npm-prefix":
2296
+ return NPM_PREFIX_STEP;
2297
+ case "run-doctor":
2298
+ return `${dim("Run")} ${NPX_LATEST_COMMAND} doctor ${dim("for the reason, or send it to")} ${SUPPORT_EMAIL}`;
2299
+ }
2300
+ };
1983
2301
  var printAgentFailures = (failures) => {
1984
2302
  for (const failure of describeAgentFailures(failures, (agent) => AGENT_NAMES[agent])) {
1985
2303
  console.log(` ${yellow("!")} ${failure.summary}`);
@@ -2062,10 +2380,10 @@ var AGENT_TARGETS = {
2062
2380
  };
2063
2381
  var reportDryRun = (apiKey, agents, keyCheck, controlMode) => {
2064
2382
  console.log();
2065
- console.log(` ${bold("Dry run")} ${dim("\u2014 nothing below has been written")}`);
2383
+ console.log(` ${bold("Dry run.")} ${dim("Nothing below has been written.")}`);
2066
2384
  console.log();
2067
2385
  console.log(
2068
- apiKey ? ` ${dim("Key:")} ${dim(`${apiKey.slice(0, 8)}...${apiKey.slice(-4)}`)} ${dim(`(${keyCheck?.kind ?? "not verified"})`)}` : ` ${dim("Key:")} ${dim("none on this machine \u2014 a real run would acquire one")}`
2386
+ apiKey ? ` ${dim("Key:")} ${dim(`${apiKey.slice(0, 8)}...${apiKey.slice(-4)}`)} ${dim(`(${keyCheck?.kind ?? "not verified"})`)}` : ` ${dim("Key:")} ${dim("none on this machine. A real run would acquire one.")}`
2069
2387
  );
2070
2388
  console.log(` ${dim("Key store:")} ${PUSHARY_CONFIG_FILE}`);
2071
2389
  console.log(` ${dim("Control:")} ${controlMode}`);
@@ -2153,37 +2471,35 @@ var main = async () => {
2153
2471
  storedKey !== envKey ? await usableStoredKey(storedKey) : null;
2154
2472
  const appKey = ambientEligible && envCheck === null && storedCheck === null ? pickAppKey({ fromAgents: AGENT_CHOICES.map(appMcpKeyIfReadable), storedKey, envKey }) : null;
2155
2473
  const appKeyCheck = appKey === null ? null : await usableStoredKey(appKey);
2474
+ const appBridgeAvailable = macAppBridgeAvailable();
2475
+ const canPairWithApp = canShowPairingQr() && !options.skipPhone;
2476
+ const keyReuse = keyReuseConfirmation({
2477
+ stdoutIsTty: process.stdout.isTTY === true,
2478
+ nonInteractive: options.nonInteractive,
2479
+ dryRun: options.dryRun,
2480
+ canPairWithApp
2481
+ });
2482
+ const reusable = pickReusableKey({
2483
+ storedKey,
2484
+ storedCheck,
2485
+ envKey,
2486
+ envCheck,
2487
+ agentConfigKey: appKey,
2488
+ agentConfigCheck: appKeyCheck,
2489
+ macAppInstalled: appBridgeAvailable
2490
+ });
2491
+ const reuseAccepted = reusable !== null && await acceptReusableKey(reusable, keyReuse);
2492
+ const reuseDeclined = reusable !== null && !reuseAccepted;
2156
2493
  let storedKeyCheck = null;
2494
+ let pairedHandle = null;
2157
2495
  if (flagKey) {
2158
2496
  const masked = `${flagKey.slice(0, 8)}...${flagKey.slice(-4)}`;
2159
2497
  console.log(` ${check} Using API key: ${dim(masked)}`);
2160
2498
  console.log();
2161
2499
  trimmedKey = flagKey;
2162
- } else if (storedKey !== null && storedCheck !== null) {
2163
- const masked = `${storedKey.slice(0, 8)}...${storedKey.slice(-4)}`;
2164
- console.log(` ${check} Using the key already on this machine: ${dim(masked)}`);
2165
- console.log(` ${dim(PUSHARY_CONFIG_FILE)}`);
2166
- console.log();
2167
- trimmedKey = storedKey;
2168
- storedKeyCheck = storedCheck;
2169
- } else if (envKey && envCheck !== null) {
2170
- const masked = `${envKey.slice(0, 8)}...${envKey.slice(-4)}`;
2171
- console.log(` ${check} Found API key in environment: ${dim(masked)}`);
2172
- console.log();
2173
- const useExisting = options.nonInteractive || options.dryRun ? true : await confirm({ message: "Use this key?", default: true });
2174
- if (useExisting) {
2175
- trimmedKey = envKey;
2176
- storedKeyCheck = envCheck;
2177
- } else {
2178
- const apiKey = await input({ message: "API key:" });
2179
- trimmedKey = apiKey.trim();
2180
- }
2181
- } else if (appKey !== null && appKeyCheck !== null) {
2182
- const masked = `${appKey.slice(0, 8)}...${appKey.slice(-4)}`;
2183
- console.log(` ${check} Using the key the Pushary Mac app is signed in with: ${dim(masked)}`);
2184
- console.log();
2185
- trimmedKey = appKey;
2186
- storedKeyCheck = appKeyCheck;
2500
+ } else if (reusable !== null && reuseAccepted) {
2501
+ trimmedKey = reusable.key;
2502
+ storedKeyCheck = reusable.check;
2187
2503
  } else if (options.dryRun) {
2188
2504
  console.log(` ${dim("No key on this machine. A real run would connect one first.")}`);
2189
2505
  console.log();
@@ -2192,18 +2508,16 @@ var main = async () => {
2192
2508
  reportDryRun(null, plannedAgents, null, controlMode);
2193
2509
  await emitJson(options, { ok: true, dryRun: true, agents: plannedAgents, control: controlMode, keyCheck: "skipped", key: "none" });
2194
2510
  return;
2195
- } else if (connectMode === "app" && canShowPairingQr() && !options.skipPhone) {
2196
- console.log(` ${dim("Pairing with your Pushary app.")}`);
2197
- console.log(` ${dim("No app yet? Get it at")} ${cyan("https://pushary.com/download")}${dim(", this keeps waiting.")}`);
2198
- console.log(` ${dim("No phone? Ctrl-C, then:")} ${cyan(BROWSER_CONNECT_COMMAND)}`);
2511
+ } else if (connectMode === "app" && canPairWithApp) {
2512
+ if (reuseDeclined) console.log();
2199
2513
  markStep("pair");
2200
2514
  const previous = readKeySource();
2201
2515
  const computer = pairingComputerOf(hostname(), process.platform, detectedAgentChoices());
2202
2516
  const paired = await connectViaAppPairing({
2203
2517
  computer,
2204
- onCancelled: async () => {
2518
+ onCancelled: async (exitCode2) => {
2205
2519
  printPairingEscape();
2206
- await reportSetupAbort("pairing-cancelled", 130);
2520
+ await reportSetupAbort("pairing-cancelled", exitCode2);
2207
2521
  }
2208
2522
  });
2209
2523
  if (paired.kind === "failed") {
@@ -2212,8 +2526,9 @@ var main = async () => {
2212
2526
  }
2213
2527
  trimmedKey = paired.apiKey;
2214
2528
  keyFromPairing = true;
2215
- if (paired.handle) {
2216
- console.log(` ${check} Connected to ${cyan("@" + paired.handle)}`);
2529
+ pairedHandle = paired.handle ? `@${paired.handle}` : null;
2530
+ if (pairedHandle) {
2531
+ console.log(` ${check} Connected to ${cyan(pairedHandle)}`);
2217
2532
  }
2218
2533
  if (previous.key && previous.key !== trimmedKey) {
2219
2534
  console.log(` ${yellow("!")} The key this machine used before (${keyPrefix(previous.key)}) is still active.`);
@@ -2275,24 +2590,26 @@ var main = async () => {
2275
2590
  if (!options.dryRun) recordSetupKey(trimmedKey);
2276
2591
  markStep("verify-key");
2277
2592
  const keyCheck = await verifyKeyOrExit(trimmedKey, options, storedKeyCheck ?? void 0);
2278
- markStep("resolve-agents");
2279
- const agents = await resolveAgents(options);
2593
+ if (reuseAccepted && keyReuse === "announce") console.log(` ${dim(WRONG_ACCOUNT_HINT)}`);
2280
2594
  if (options.dryRun) {
2281
- reportDryRun(trimmedKey, agents, keyCheck, controlMode);
2282
- await emitJson(options, { ok: true, dryRun: true, agents, control: controlMode, keyCheck: keyCheck?.kind ?? "skipped" });
2595
+ markStep("resolve-agents");
2596
+ const plannedAgents = await resolveAgents(options);
2597
+ reportDryRun(trimmedKey, plannedAgents, keyCheck, controlMode);
2598
+ await emitJson(options, { ok: true, dryRun: true, agents: plannedAgents, control: controlMode, keyCheck: keyCheck?.kind ?? "skipped" });
2283
2599
  return;
2284
2600
  }
2285
2601
  markStep("save-key");
2286
2602
  const transcriptSync = transcriptPreferenceForSetup(options.transcripts);
2287
2603
  if (transcriptSync) {
2288
2604
  console.log();
2289
- console.log(" Transcript sync: enabling locally redacted, encrypted sync for Pushary-launched sessions. Your phone can decrypt it; authorized Pushary compliance admins can recover it for documented legal obligations. Disable with pushary setup --transcripts off.");
2605
+ console.log(` ${TRANSCRIPT_SYNC_NOTICE}`);
2290
2606
  }
2291
2607
  const rc = await saveApiKey(trimmedKey, controlMode, transcriptSync);
2292
2608
  if (trimmedKey === appKey) recordMacAppKeyPrefix(trimmedKey);
2293
2609
  if (keyCheck.kind === "ok") clearDaemonNeedsLogin();
2610
+ markStep("resolve-agents");
2611
+ const agents = await resolveAgents(options);
2294
2612
  const context = { rc };
2295
- const appBridgeAvailable = macAppBridgeAvailable();
2296
2613
  const accountChecks = /* @__PURE__ */ new Map();
2297
2614
  if (keyCheck) accountChecks.set(trimmedKey, Promise.resolve(keyCheck));
2298
2615
  const accountCheckFor = (key) => {
@@ -2302,15 +2619,20 @@ var main = async () => {
2302
2619
  accountChecks.set(key, pending);
2303
2620
  return pending;
2304
2621
  };
2305
- const appAccountMatches = async (agent) => {
2306
- const appKey2 = appWiringAccountKeyFor(agent);
2307
- if (!appKey2) return false;
2308
- if (appKey2 === trimmedKey) return true;
2622
+ const setupAccount = keyAccountHandle(keyCheck);
2623
+ const compareAppAccount = async (agent) => {
2624
+ const wiringKey = appWiringAccountKeyFor(agent);
2625
+ if (!wiringKey) return { matches: false, setupAccount, macAccount: null };
2626
+ if (wiringKey === trimmedKey) return { matches: true, setupAccount, macAccount: setupAccount };
2309
2627
  const [cliAccount, appAccount] = await Promise.all([
2310
2628
  accountCheckFor(trimmedKey),
2311
- accountCheckFor(appKey2)
2629
+ accountCheckFor(wiringKey)
2312
2630
  ]);
2313
- return samePusharyAccount(cliAccount, appAccount);
2631
+ return {
2632
+ matches: samePusharyAccount(cliAccount, appAccount),
2633
+ setupAccount: keyAccountHandle(cliAccount),
2634
+ macAccount: keyAccountHandle(appAccount)
2635
+ };
2314
2636
  };
2315
2637
  const staleEnvironmentKey = Boolean(envKey && envKey !== trimmedKey);
2316
2638
  if (envKey && staleEnvironmentKey) {
@@ -2335,14 +2657,19 @@ var main = async () => {
2335
2657
  const plan = planHooks({ owner: ownerBefore, takeOverHooks: options.takeOverHooks, nonInteractive: options.nonInteractive });
2336
2658
  const keepAppWiring = plan === "keep";
2337
2659
  if (keepAppWiring) {
2338
- if (!await appAccountMatches(agent)) {
2660
+ const account = await compareAppAccount(agent);
2661
+ if (!account.matches) {
2339
2662
  hookOwners.push({ agent, owner: "app", kept: true });
2340
- const failure = describedAgentFailure(agent, "app-account-mismatch");
2341
- failures.push(failure);
2663
+ failures.push(describedAgentFailure(agent, "app-account-mismatch"));
2664
+ const [summary, ...steps] = describeMacAccountMismatch({
2665
+ agent,
2666
+ agentName: AGENT_NAMES[agent],
2667
+ setupAccount: account.setupAccount,
2668
+ macAccount: account.macAccount
2669
+ });
2342
2670
  console.log(`
2343
- ${yellow("!")} ${AGENT_NAMES[agent]}: ${failure.detail}`);
2344
- console.log(` ${dim("Sign the Mac app into the same account and repair this agent, then re-run setup.")}`);
2345
- console.log(` ${dim("Or pass --take-over-hooks if you intentionally want the CLI to own these hooks.")}`);
2671
+ ${yellow("!")} ${summary}`);
2672
+ for (const step of steps) console.log(` ${dim(step)}`);
2346
2673
  continue;
2347
2674
  }
2348
2675
  console.log(`
@@ -2382,10 +2709,7 @@ var main = async () => {
2382
2709
  console.log();
2383
2710
  const configured = failedAgentsContext(completed.map((agent) => AGENT_NAMES[agent]));
2384
2711
  console.log(` ${yellow("!")} Failed: ${failedAgentNames(failures).join(", ")}${configured ? ` ${dim(configured)}` : ""}`);
2385
- for (const followUp of agentFailureFollowUps(failures)) {
2386
- if (followUp === "install-node") console.log(` ${INSTALL_NODE_STEP}`);
2387
- else console.log(` ${dim("Run")} ${NPX_LATEST_COMMAND} doctor ${dim("for the reason, or send it to")} ${SUPPORT_EMAIL}`);
2388
- }
2712
+ for (const followUp of agentFailureFollowUps(failures)) console.log(` ${agentFailureFollowUpLine(followUp)}`);
2389
2713
  }
2390
2714
  markStep("project-instructions");
2391
2715
  await offerProjectInstructions(agents, options);
@@ -2458,12 +2782,12 @@ var main = async () => {
2458
2782
  // being a thing they have to know to run.
2459
2783
  (phone?.reachesYou || phone?.outcome?.kind === "unattributable")) {
2460
2784
  console.log();
2461
- console.log(` ${dim("Test delivery and answer return. Agent hooks are a separate check.")}`);
2462
- await spinner("Waiting for you to tap the notification", async () => {
2463
- roundTrip = await verifyRoundTrip(trimmedKey);
2464
- });
2465
- const report = describeRoundTrip(roundTrip);
2466
- console.log(` ${report.ok ? check : yellow("!")} ${report.message}`);
2785
+ console.log(` ${dim("Answer the test question on your phone.")}`);
2786
+ const answer = verifyRoundTrip(trimmedKey);
2787
+ await spinner("Waiting for you to tap the notification", async () => roundTripSpinnerEnd(await answer));
2788
+ roundTrip = await answer;
2789
+ const report = describeRoundTripOutcome(roundTrip);
2790
+ if (!report.ok) console.log(` ${yellow("!")} ${report.message}`);
2467
2791
  for (const line of report.next) console.log(` ${dim(line)}`);
2468
2792
  } else if (verifyMode === "roundtrip" && keyCheck.kind === "ok") {
2469
2793
  console.log();
@@ -2472,7 +2796,7 @@ var main = async () => {
2472
2796
  console.log();
2473
2797
  if (keyCheck.kind === "locked" || keyCheck.kind === "unreachable") {
2474
2798
  const report = describeKeyCheck(keyCheck);
2475
- const next = [...report.next, "Finish activation: pushary setup (reuses the saved key)."];
2799
+ const next = keyCheck.kind === "locked" ? [...report.next, describeLockedKeyAccount(trimmedKey, pairedHandle), WRONG_ACCOUNT_HINT] : [...report.next, "Finish activation: pushary setup (reuses the saved key)."];
2476
2800
  const exitCode2 = failures.length > 0 || controlLifecycleFailure ? EXIT.PROBLEMS_FOUND : report.exitCode;
2477
2801
  printManualSteps();
2478
2802
  console.log(` ${yellow(bold(keyCheck.kind === "locked" ? "Agents configured, approvals inactive." : "Configuration saved, activation unverified."))}`);
@@ -2577,6 +2901,7 @@ var main = async () => {
2577
2901
  let controlFailureStage = null;
2578
2902
  let daemon = null;
2579
2903
  if (keyIsTrusted && controlPolicy === "enabled") {
2904
+ console.log(` ${dim(describeServiceInstall(servicePresent))}`);
2580
2905
  try {
2581
2906
  controlFailureStage = "package-install";
2582
2907
  await installGlobally2();
@@ -2632,7 +2957,8 @@ var main = async () => {
2632
2957
  transcriptSync,
2633
2958
  // null when the check did not run, so it changes nothing. Only an outright
2634
2959
  // `false` is allowed to hold back "Setup complete.".
2635
- roundTripAnswered: roundTrip === null ? null : roundTrip.kind === "answered" ? true : roundTrip.kind === "unanswered" ? false : null
2960
+ roundTripAnswered: roundTrip === null ? null : roundTrip.kind === "answered" ? true : roundTrip.kind === "unanswered" ? false : null,
2961
+ roundTripAnswer: roundTripAnswerOf(roundTrip)
2636
2962
  });
2637
2963
  console.log(` ${closing.ok ? green(bold(closing.headline)) : yellow(bold(closing.headline))}`);
2638
2964
  if (failures.length > 0) {