@pushary/agent-hooks 1.9.53 → 1.9.55

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.
@@ -15,13 +15,15 @@ import {
15
15
  writeStdoutFully
16
16
  } from "../chunk-EO23EUOK.js";
17
17
  import {
18
+ describeAnswerTestPrompt,
18
19
  describeReach,
19
20
  describeRoundTrip,
20
21
  fetchChannels,
22
+ ownAppPlatforms,
21
23
  reachVerdict,
22
24
  roundTripEvidence,
23
25
  verifyRoundTrip
24
- } from "../chunk-XZRLFBEA.js";
26
+ } from "../chunk-LGTF7AZX.js";
25
27
  import "../chunk-3EGEA4KH.js";
26
28
  import {
27
29
  readMachineReadiness,
@@ -1373,6 +1375,7 @@ var main = async () => {
1373
1375
  console.log();
1374
1376
  console.log(` ${dim("Notification Delivery")}`);
1375
1377
  const probe = await fetchChannels(apiKey);
1378
+ const appPlatforms = probe.kind === "ok" ? ownAppPlatforms(probe.status) : [];
1376
1379
  if (probe.kind !== "ok") {
1377
1380
  check(
1378
1381
  false,
@@ -1420,9 +1423,11 @@ var main = async () => {
1420
1423
  console.log();
1421
1424
  console.log(` ${dim("Question Roundtrip")}`);
1422
1425
  console.log(` ${dim("\u2192")} Question sent, waiting for your answer...`);
1426
+ const answerStep = describeAnswerTestPrompt(appPlatforms).step;
1427
+ if (answerStep !== null) console.log(` ${dim(answerStep)}`);
1423
1428
  const outcome = await verifyRoundTrip(apiKey, "Pushary Doctor");
1424
1429
  roundTrip = outcome;
1425
- const report = describeRoundTrip(outcome);
1430
+ const report = describeRoundTrip(outcome, appPlatforms);
1426
1431
  check(report.ok, "Synthetic answer test", report.message);
1427
1432
  for (const line of report.next) console.log(` ${dim(line)}`);
1428
1433
  }
@@ -30,16 +30,19 @@ import {
30
30
  } from "../chunk-EO23EUOK.js";
31
31
  import {
32
32
  BROWSER_CONNECT_COMMAND,
33
+ SETUP_AGENT_NAME,
33
34
  confirmAppConnection,
34
35
  connectDevice,
35
36
  connectViaAppPairing,
37
+ describeAnswerTestPrompt,
36
38
  describeRoundTripOutcome,
39
+ notificationAnswerStep,
37
40
  pairingEscapeApplies,
38
41
  printConnectInstructions,
39
42
  roundTripEvidence,
40
43
  verifyRoundTrip,
41
44
  waitForYourPhone
42
- } from "../chunk-XZRLFBEA.js";
45
+ } from "../chunk-LGTF7AZX.js";
43
46
  import "../chunk-3EGEA4KH.js";
44
47
  import {
45
48
  readManagedControlEvidence,
@@ -55,7 +58,7 @@ import {
55
58
  } from "../chunk-EUW7JL2K.js";
56
59
  import {
57
60
  codexEngineBehindLauncher
58
- } from "../chunk-ABGN5RN2.js";
61
+ } from "../chunk-T5L5NWT3.js";
59
62
  import "../chunk-XCEN33UP.js";
60
63
  import {
61
64
  CURSOR_PLUGIN_BACKUP_INFIX,
@@ -244,7 +247,7 @@ import {
244
247
  CODEX_ENGINE_ENTRYPOINT,
245
248
  CODEX_ENGINE_MANIFEST,
246
249
  codexEngineInside
247
- } from "../chunk-36NGFHE4.js";
250
+ } from "../chunk-E46NE7FB.js";
248
251
  import {
249
252
  reportEvent
250
253
  } from "../chunk-U2L42GVI.js";
@@ -707,6 +710,12 @@ var fetchSetupWaitGuidance = async (apiKey) => {
707
710
 
708
711
  // src/setup/closing.ts
709
712
  var ACTIVITY_URL = "pushary.com/dashboard/agent/activity";
713
+ var CHECK_THE_NOTIFICATION_OPENED = "Check the notification opened something you could tap, then:";
714
+ var SEND_ANOTHER_TEST = "pushary doctor --roundtrip send another one";
715
+ var sendAnotherTestLines = (appPlatforms) => {
716
+ const step = notificationAnswerStep(appPlatforms);
717
+ return step === null ? [CHECK_THE_NOTIFICATION_OPENED, SEND_ANOTHER_TEST] : [SEND_ANOTHER_TEST, step];
718
+ };
710
719
  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`;
711
720
  var PAIR_PHONE_APP_HINT = `${NPX_LATEST_COMMAND} setup --connect app pair the Pushary app on your phone`;
712
721
  var RESTART_AGENTS_LINE = "Restart any open agent sessions so they pick up Pushary.";
@@ -856,8 +865,7 @@ var describeClosing = (facts) => {
856
865
  next: [
857
866
  "No answer returned from the synthetic test. Delivery acceptance does not prove a visible notification.",
858
867
  "",
859
- "Check the notification opened something you could tap, then:",
860
- "pushary doctor --roundtrip send another one",
868
+ ...sendAnotherTestLines(facts.appPlatforms ?? []),
861
869
  "",
862
870
  "If you simply were not looking, re-run setup or pass --verify push to skip this check."
863
871
  ],
@@ -1019,6 +1027,12 @@ var roundTripOutcome = (outcome) => {
1019
1027
  return null;
1020
1028
  }
1021
1029
  };
1030
+ var connectOutcomeOf = (roundTrip, phone) => {
1031
+ const fromRoundTrip = roundTripOutcome(roundTrip);
1032
+ if (fromRoundTrip !== null) return fromRoundTrip;
1033
+ if (phone === null) return "skipped";
1034
+ return phone.reachesYou ? "delivered" : "unreachable";
1035
+ };
1022
1036
  var currentStep = "start";
1023
1037
  var markStep = (step) => {
1024
1038
  currentStep = step;
@@ -1086,23 +1100,24 @@ var reportSetupOutcomeWith = async (deps, outcome) => emit(deps, {
1086
1100
  abortReason: null
1087
1101
  });
1088
1102
  var reportSetupOutcome = (outcome) => reportSetupOutcomeWith(defaultDeps, outcome);
1089
- var reportSetupAbortWith = async (deps, reason, exitCode, keyCheck = "unknown") => emit(deps, {
1090
- agents: [],
1103
+ var NOTHING_CONFIGURED = { completed: [], skipped: [], failures: [], connectOutcome: "skipped" };
1104
+ var reportSetupAbortWith = async (deps, reason, exitCode, keyCheck = "unknown", progress = NOTHING_CONFIGURED) => emit(deps, {
1105
+ agents: progress.completed,
1091
1106
  connectMode: recordedOptions?.connectMode ?? null,
1092
- connectOutcome: "skipped",
1093
- failedAgents: 0,
1094
- agentFailures: [],
1107
+ connectOutcome: progress.connectOutcome,
1108
+ failedAgents: progress.failures.length,
1109
+ agentFailures: agentFailureWire(progress.failures),
1095
1110
  closingReason: null,
1096
1111
  controlFailureStage: null,
1097
1112
  controlMissingPrerequisite: null,
1098
- skippedAgents: [],
1113
+ skippedAgents: progress.skipped,
1099
1114
  nonInteractive: recordedOptions?.nonInteractive ?? null,
1100
1115
  keyCheck,
1101
1116
  exitCode,
1102
1117
  abandonedAt: currentStep,
1103
1118
  abortReason: reason
1104
1119
  });
1105
- var reportSetupAbort = (reason, exitCode, keyCheck) => reportSetupAbortWith(defaultDeps, reason, exitCode, keyCheck);
1120
+ var reportSetupAbort = (reason, exitCode, keyCheck, progress) => reportSetupAbortWith(defaultDeps, reason, exitCode, keyCheck, progress);
1106
1121
 
1107
1122
  // src/setup/app-key.ts
1108
1123
  var pickAppKey = (candidates) => candidates.fromAgents.find(
@@ -1167,8 +1182,8 @@ var green = paint("\x1B[32m");
1167
1182
  var cyan = paint("\x1B[36m");
1168
1183
  var yellow = paint("\x1B[33m");
1169
1184
  var check = green("\u2713");
1170
- var abortWith = async (reason, code, keyCheck) => {
1171
- await reportSetupAbort(reason, code, keyCheck);
1185
+ var abortWith = async (reason, code, keyCheck, progress) => {
1186
+ await reportSetupAbort(reason, code, keyCheck, progress);
1172
1187
  if (jsonMode) {
1173
1188
  await writeStdoutFully(`${JSON.stringify({ ok: false, reason, exitCode: code, ...keyCheck ? { keyCheck } : {} }, null, 2)}
1174
1189
  `);
@@ -2474,6 +2489,8 @@ var AGENT_CAPABILITIES = {
2474
2489
  var failedAgentNames = (failures) => failures.map((failure) => AGENT_NAMES[failure.agent]);
2475
2490
  var agentFailureNextLine = (next) => next.kind === "retry-agent" ? `${dim(RETRY_AGENT_LABEL)} ${cyan(next.command)}` : next.instruction;
2476
2491
  var NO_ANSWER_YET = "No answer yet";
2492
+ var CONNECT_PHONE_STEP = "connect-phone";
2493
+ var ANSWER_TEST_STEP = "answer-test";
2477
2494
  var roundTripAnswerOf = (outcome) => outcome?.kind === "answered" ? { latencyMs: outcome.latencyMs, answerSource: outcome.answerSource } : null;
2478
2495
  var roundTripSpinnerEnd = (outcome) => {
2479
2496
  const report = describeRoundTripOutcome(outcome);
@@ -2914,7 +2931,7 @@ var main = async () => {
2914
2931
  console.log(` ${check} Phone-start background service disabled.`);
2915
2932
  }
2916
2933
  }
2917
- markStep("connect-phone");
2934
+ markStep(CONNECT_PHONE_STEP);
2918
2935
  if (keyCheck.kind === "ok") console.log(` ${dim(await fetchSetupWaitGuidance(trimmedKey))}`);
2919
2936
  const skipPhone = options.skipPhone;
2920
2937
  let phone = null;
@@ -2977,11 +2994,20 @@ var main = async () => {
2977
2994
  // being a thing they have to know to run.
2978
2995
  (phone?.reachesYou || phone?.outcome?.kind === "unattributable")) {
2979
2996
  console.log();
2997
+ const appPlatforms = phone?.appPlatforms ?? [];
2998
+ const prompt = describeAnswerTestPrompt(appPlatforms);
2980
2999
  console.log(` ${dim("Answer the test question on your phone.")}`);
2981
- const answer = verifyRoundTrip(trimmedKey);
2982
- await spinner("Waiting for you to tap the notification", async () => roundTripSpinnerEnd(await answer));
3000
+ if (prompt.step !== null) console.log(` ${dim(prompt.step)}`);
3001
+ markStep(ANSWER_TEST_STEP);
3002
+ const answer = verifyRoundTrip(trimmedKey, SETUP_AGENT_NAME, {
3003
+ interrupted: async (exitCode2) => {
3004
+ await abortWith("interrupted", exitCode2, keyCheck.kind, { completed, skipped, failures, connectOutcome: connectOutcomeOf(null, phone) });
3005
+ }
3006
+ });
3007
+ await spinner(prompt.waiting, async () => roundTripSpinnerEnd(await answer));
2983
3008
  roundTrip = await answer;
2984
- const report = describeRoundTripOutcome(roundTrip);
3009
+ markStep(CONNECT_PHONE_STEP);
3010
+ const report = describeRoundTripOutcome(roundTrip, appPlatforms);
2985
3011
  if (!report.ok) console.log(` ${yellow("!")} ${report.message}`);
2986
3012
  for (const line of report.next) console.log(` ${dim(line)}`);
2987
3013
  } else if (verifyMode === "roundtrip" && keyCheck.kind === "ok") {
@@ -3157,7 +3183,8 @@ var main = async () => {
3157
3183
  // null when the check did not run, so it changes nothing. Only an outright
3158
3184
  // `false` is allowed to hold back "Setup complete.".
3159
3185
  roundTripAnswered: roundTrip === null ? null : roundTrip.kind === "answered" ? true : roundTrip.kind === "unanswered" ? false : null,
3160
- roundTripAnswer: roundTripAnswerOf(roundTrip)
3186
+ roundTripAnswer: roundTripAnswerOf(roundTrip),
3187
+ appPlatforms: phone?.appPlatforms
3161
3188
  });
3162
3189
  console.log(` ${closing.ok ? green(bold(closing.headline)) : yellow(bold(closing.headline))}`);
3163
3190
  if (failures.length > 0) {
@@ -3186,7 +3213,7 @@ var main = async () => {
3186
3213
  // nothing produced it, because setup stopped at a delivered notification, so
3187
3214
  // the one value meaning "a human tapped" was never emitted by the command
3188
3215
  // whose job is to prove a human can.
3189
- connectOutcome: roundTripOutcome(roundTrip) ?? (phone === null ? "skipped" : phone.reachesYou ? "delivered" : "unreachable"),
3216
+ connectOutcome: connectOutcomeOf(roundTrip, phone),
3190
3217
  keyCheck: keyCheck?.kind ?? "skipped",
3191
3218
  exitCode
3192
3219
  });
@@ -3,7 +3,7 @@ import {
3
3
  describeReach,
4
4
  fetchChannels,
5
5
  reachVerdict
6
- } from "../chunk-XZRLFBEA.js";
6
+ } from "../chunk-LGTF7AZX.js";
7
7
  import "../chunk-3EGEA4KH.js";
8
8
  import {
9
9
  describeLastSeen,
@@ -4,6 +4,7 @@ import { rmSync } from "fs";
4
4
  import { join } from "path";
5
5
  var CODEX_APP_LAUNCH_VARIABLE = "CODEX_CLI_PATH";
6
6
  var CODEX_ENGINE_PENDING_VARIABLE = "PUSHARY_CODEX_ENGINE_PENDING";
7
+ var CODEX_APP_LAUNCH_AT_LOGIN_VERB = "codex-app-launch-at-login";
7
8
  var CODEX_ENGINE_DIRECTORY = "Contents/Resources/codex-cli";
8
9
  var CODEX_ENGINE_ENTRYPOINT = "bin/codex";
9
10
  var CODEX_ENGINE_MANIFEST = "codex-package.json";
@@ -60,6 +61,7 @@ var letCodexRunWithoutPushary = (app, engineArgs) => {
60
61
  export {
61
62
  CODEX_APP_LAUNCH_VARIABLE,
62
63
  CODEX_ENGINE_PENDING_VARIABLE,
64
+ CODEX_APP_LAUNCH_AT_LOGIN_VERB,
63
65
  CODEX_ENGINE_DIRECTORY,
64
66
  CODEX_ENGINE_ENTRYPOINT,
65
67
  CODEX_ENGINE_MANIFEST,
@@ -99,10 +99,12 @@ var describeJustPairedRepair = (verdict) => {
99
99
 
100
100
  // src/setup/verify.ts
101
101
  var plural2 = (count, one) => `${count} ${one}${count === 1 ? "" : "s"}`;
102
+ var SETUP_AGENT_NAME = "Pushary setup";
103
+ var ownAppPlatforms = (channels) => channels.yours?.appPlatforms ?? channels.appPlatforms;
102
104
  var describeRecipients = (push, channels) => {
103
105
  const parts = [];
104
106
  if (push.app > 0) {
105
- const platforms = channels.yours?.appPlatforms ?? channels.appPlatforms;
107
+ const platforms = ownAppPlatforms(channels);
106
108
  const suffix = platforms.length > 0 ? ` (${platforms.join(", ")})` : "";
107
109
  parts.push(`${plural2(push.app, "app device")}${suffix}`);
108
110
  }
@@ -131,7 +133,7 @@ var sendTestNotification = async (apiKey) => {
131
133
  const result = await callMcpTool(apiKey, "send_notification", {
132
134
  title: "Pushary is connected",
133
135
  body: "This setup notification reached you. Agent hooks still need a real-agent check.",
134
- agentName: "Pushary setup"
136
+ agentName: SETUP_AGENT_NAME
135
137
  }, { timeoutMs: 2e4 });
136
138
  const toCount = (value) => typeof value === "number" && Number.isFinite(value) ? value : 0;
137
139
  const app = toCount(result.delivery?.mobile?.recipients);
@@ -185,8 +187,10 @@ var describeVerifyOutcome = (outcome) => {
185
187
  };
186
188
  }
187
189
  };
188
- var ROUND_TRIP_QUESTION = "Pushary delivery test. Tap Yes or No to test returning an answer. This does not test your agent hooks.";
190
+ var ROUND_TRIP_QUESTION = "Pushary delivery test. Answer either way to test returning an answer. This does not test your agent hooks.";
189
191
  var ROUND_TRIP_TIMEOUT_MS = 55e3;
192
+ var reportNoInterrupt = async () => {
193
+ };
190
194
  var defaultAsk = (apiKey, agentName) => (
191
195
  // wait:false so the poll is ours: the blocking form returns on the SITE's
192
196
  // policy timeout, which a user may have set to seconds, and a setup check
@@ -218,10 +222,11 @@ var answeredRoundTrip = (answer, correlationId, latencyMs) => {
218
222
  ...isAnswerSource(answer.answerSource) ? { answerSource: answer.answerSource } : {}
219
223
  };
220
224
  };
221
- var verifyRoundTrip = async (apiKey, agentName = "Pushary setup", deps = {}) => {
225
+ var verifyRoundTrip = async (apiKey, agentName = SETUP_AGENT_NAME, deps = {}) => {
222
226
  const ask = deps.ask ?? defaultAsk;
223
227
  const wait = deps.wait ?? defaultWait;
224
228
  const cancel = deps.cancel ?? defaultCancel;
229
+ const interrupted = deps.interrupted ?? reportNoInterrupt;
225
230
  const now = deps.now ?? Date.now;
226
231
  const started = now();
227
232
  let asked;
@@ -237,8 +242,12 @@ var verifyRoundTrip = async (apiKey, agentName = "Pushary setup", deps = {}) =>
237
242
  let timer;
238
243
  try {
239
244
  await Promise.race([
240
- cancel(apiKey, asked.correlationId).catch(() => {
241
- }),
245
+ Promise.all([
246
+ cancel(apiKey, asked.correlationId).catch(() => {
247
+ }),
248
+ interrupted(code).catch(() => {
249
+ })
250
+ ]),
242
251
  new Promise((resolve) => {
243
252
  timer = setTimeout(resolve, 4e3);
244
253
  })
@@ -291,7 +300,30 @@ var describeAnswerSource = (source) => {
291
300
  return source ? `Answer source: ${source}. This was a synthetic test.` : "The answering surface was not reported. This was a synthetic test.";
292
301
  }
293
302
  };
294
- var describeRoundTripOutcome = (outcome) => {
303
+ var NOTIFICATION_ANSWER_STEPS = /* @__PURE__ */ new Map([
304
+ ["ios", "Press and hold the notification, then tap Approve."],
305
+ ["android", "Expand the notification, then tap Approve."]
306
+ ]);
307
+ var EITHER_PHONE_ANSWER_STEP = "Press and hold the notification on iPhone, or expand it on Android, then tap Approve.";
308
+ var notificationAnswerStep = (appPlatforms) => {
309
+ const platforms = [...new Set(appPlatforms)];
310
+ const [onlyPlatform] = platforms;
311
+ if (onlyPlatform === void 0) return null;
312
+ if (platforms.length > 1) return EITHER_PHONE_ANSWER_STEP;
313
+ return NOTIFICATION_ANSWER_STEPS.get(onlyPlatform) ?? EITHER_PHONE_ANSWER_STEP;
314
+ };
315
+ var WAITING_FOR_TAP = "Waiting for you to tap the notification";
316
+ var WAITING_FOR_ANSWER = "Waiting for your answer";
317
+ var describeAnswerTestPrompt = (appPlatforms) => {
318
+ const step = notificationAnswerStep(appPlatforms);
319
+ return { step, waiting: step === null ? WAITING_FOR_TAP : WAITING_FOR_ANSWER };
320
+ };
321
+ var CHECK_THE_NOTIFICATION_OPENED = "Check the notification actually opened something you could tap, then run pushary doctor --roundtrip.";
322
+ var unansweredNextStep = (appPlatforms) => {
323
+ const step = notificationAnswerStep(appPlatforms);
324
+ return step === null ? CHECK_THE_NOTIFICATION_OPENED : `To send another one, run pushary doctor --roundtrip. ${step}`;
325
+ };
326
+ var describeRoundTripOutcome = (outcome, appPlatforms = []) => {
295
327
  switch (outcome.kind) {
296
328
  case "answered":
297
329
  return {
@@ -305,7 +337,7 @@ var describeRoundTripOutcome = (outcome) => {
305
337
  message: "The question was sent and nothing came back.",
306
338
  next: [
307
339
  "A push that arrives and cannot be answered is the most common way this breaks, so this is worth resolving now.",
308
- "Check the notification actually opened something you could tap, then run pushary doctor --roundtrip."
340
+ unansweredNextStep(appPlatforms)
309
341
  ]
310
342
  };
311
343
  case "notify-only":
@@ -331,8 +363,8 @@ var describeRoundTripOutcome = (outcome) => {
331
363
  };
332
364
  }
333
365
  };
334
- var describeRoundTrip = (outcome) => {
335
- const report = describeRoundTripOutcome(outcome);
366
+ var describeRoundTrip = (outcome, appPlatforms = []) => {
367
+ const report = describeRoundTripOutcome(outcome, appPlatforms);
336
368
  return { ...report, next: [...report.next, "Agent hook activation remains unverified. Restart your agent and check one real approval."] };
337
369
  };
338
370
  var roundTripEvidence = (outcome) => ({
@@ -560,7 +592,7 @@ var sendAndConfirm = async (apiKey, channels) => {
560
592
  const report = describeVerifyOutcome(outcome);
561
593
  stopSend(report.ok ? check : yellow("!"), report.message);
562
594
  for (const line of report.next) console.log(` ${dim(line)}`);
563
- return { reachesYou: report.ok, outcome, detail: null };
595
+ return { reachesYou: report.ok, outcome, detail: null, appPlatforms: ownAppPlatforms(channels) };
564
596
  };
565
597
  var printCapabilityDelta = () => {
566
598
  console.log(` ${dim("Two ways to get approvals on your phone:")}`);
@@ -1028,7 +1060,11 @@ var waitForYourPhone = async (apiKey, options = {}) => {
1028
1060
  export {
1029
1061
  reachVerdict,
1030
1062
  describeReach,
1063
+ SETUP_AGENT_NAME,
1064
+ ownAppPlatforms,
1031
1065
  verifyRoundTrip,
1066
+ notificationAnswerStep,
1067
+ describeAnswerTestPrompt,
1032
1068
  describeRoundTripOutcome,
1033
1069
  describeRoundTrip,
1034
1070
  roundTripEvidence,
@@ -12,7 +12,7 @@ import {
12
12
  launcherIsGone,
13
13
  launcherWaitingOnThisProcess,
14
14
  tellTheLauncherTheEngineStarted
15
- } from "./chunk-36NGFHE4.js";
15
+ } from "./chunk-E46NE7FB.js";
16
16
 
17
17
  // src/codex-desktop-launch.ts
18
18
  import { spawn as spawn2, execFileSync } from "child_process";
@@ -695,7 +695,7 @@ var codexDesktopCommand = async (args, entry, readAccount) => {
695
695
  return runCodexDesktopProxy(engine2, args.slice(3), readAccount, { watchesForCards: layoutIsKnown });
696
696
  }
697
697
  if (args[0] === APP_CARDS_FLAG) {
698
- const { codexAppCardsCommandOnThisMac } = await import("./codex-app-cards-switch-RENOSSDD.js");
698
+ const { codexAppCardsCommandOnThisMac } = await import("./codex-app-cards-switch-P4RVJNRV.js");
699
699
  return codexAppCardsCommandOnThisMac(args.slice(1), entry, readAccount);
700
700
  }
701
701
  if (args.length > 2) throw new Error("Usage: pushary codex --desktop [path/to/Codex.app]");