@pushary/agent-hooks 1.9.58 → 1.9.60

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 (126) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/data/cursor-plugin/.cursor-plugin/plugin.json +5 -1
  3. package/data/cursor-plugin/CHANGELOG.md +4 -0
  4. package/dist/{api-WVXBM4MR.js → api-ASQA5PZM.js} +11 -11
  5. package/dist/bin/pushary-bell-hook.js +29 -29
  6. package/dist/bin/pushary-bell.js +12 -12
  7. package/dist/bin/pushary-claude.js +31 -30
  8. package/dist/bin/pushary-clean.js +58 -53
  9. package/dist/bin/pushary-codex-bridge.js +22 -22
  10. package/dist/bin/pushary-codex-hook.js +34 -34
  11. package/dist/bin/pushary-codex-session.js +8 -8
  12. package/dist/bin/pushary-codex.js +15 -15
  13. package/dist/bin/pushary-connect.js +29 -29
  14. package/dist/bin/pushary-cowork.js +7 -7
  15. package/dist/bin/pushary-daemon.js +39 -39
  16. package/dist/bin/pushary-disconnect.js +42 -29
  17. package/dist/bin/pushary-doctor.js +51 -47
  18. package/dist/bin/pushary-elicitation-hook.js +29 -29
  19. package/dist/bin/pushary-gemini-bridge.js +15 -15
  20. package/dist/bin/pushary-gemini-hook.js +31 -31
  21. package/dist/bin/pushary-hook.js +33 -33
  22. package/dist/bin/pushary-login.js +22 -19
  23. package/dist/bin/pushary-logout.js +36 -31
  24. package/dist/bin/pushary-mcp.js +12 -12
  25. package/dist/bin/pushary-mode.js +41 -55
  26. package/dist/bin/pushary-notification-hook.js +28 -28
  27. package/dist/bin/pushary-opencode-hook.js +30 -30
  28. package/dist/bin/pushary-permission-denied-hook.js +33 -33
  29. package/dist/bin/pushary-permission-hook.js +32 -32
  30. package/dist/bin/pushary-post-hook.js +28 -28
  31. package/dist/bin/pushary-prompt-hook.js +28 -28
  32. package/dist/bin/pushary-session-end-hook.js +28 -28
  33. package/dist/bin/pushary-session-start-hook.js +35 -35
  34. package/dist/bin/pushary-setup.js +49 -46
  35. package/dist/bin/pushary-stats.js +5 -5
  36. package/dist/bin/pushary-status.js +41 -36
  37. package/dist/bin/pushary-stop-hook.js +28 -28
  38. package/dist/bin/pushary-stopfailure-hook.js +28 -28
  39. package/dist/bin/pushary-suggestions.js +33 -39
  40. package/dist/bin/pushary-transcript-register.js +20 -20
  41. package/dist/bin/pushary-transcripts.js +33 -20
  42. package/dist/bin/pushary-upgrade.js +71 -50
  43. package/dist/bin/pushary-wait.js +36 -42
  44. package/dist/bin/pushary.js +8 -8
  45. package/dist/{chunk-JSQOHMNJ.js → chunk-2AELPDMT.js} +1 -1
  46. package/dist/{chunk-WVP6UQG3.js → chunk-2QREHU4O.js} +11 -4
  47. package/dist/{chunk-QTCFEKLJ.js → chunk-33OOEVPG.js} +1 -1
  48. package/dist/{chunk-QDFLBTUP.js → chunk-4MUFZU2G.js} +1 -1
  49. package/dist/{chunk-5DZHKNBU.js → chunk-4MWRR5IT.js} +8 -7
  50. package/dist/{chunk-XEJV3QSS.js → chunk-4PZXI7WU.js} +3 -3
  51. package/dist/{chunk-UMVVQXAC.js → chunk-56JCQQLS.js} +2 -2
  52. package/dist/{chunk-JZKEC4XJ.js → chunk-5RU35K57.js} +3 -12
  53. package/dist/{chunk-HRHAZ3GE.js → chunk-625DZLT5.js} +6 -6
  54. package/dist/{chunk-3KOPY2UQ.js → chunk-6LOQE4J4.js} +3 -3
  55. package/dist/{chunk-K5CAURU5.js → chunk-6MC2XXFD.js} +4 -4
  56. package/dist/{chunk-MYBCXDIL.js → chunk-6MJWYYAS.js} +62 -49
  57. package/dist/chunk-7BHI3PZN.js +41 -0
  58. package/dist/{chunk-C3IWX6KU.js → chunk-7JXJJNQ5.js} +1 -1
  59. package/dist/{chunk-CK5BCFQF.js → chunk-7WFRY6OL.js} +2 -2
  60. package/dist/{chunk-4YNNAF53.js → chunk-ADR3KHOC.js} +2 -2
  61. package/dist/{chunk-XTZHUIGJ.js → chunk-ATKN4BJT.js} +1 -1
  62. package/dist/{chunk-GZL3KEPC.js → chunk-AXJLJJ3M.js} +147 -136
  63. package/dist/{chunk-LVDC3RUE.js → chunk-BG4Y64FW.js} +1 -1
  64. package/dist/{chunk-KSVCCSAO.js → chunk-CDQHQAS7.js} +9 -7
  65. package/dist/{chunk-TN74MUMB.js → chunk-CEQ2AZH4.js} +13 -13
  66. package/dist/{chunk-X6F4BQEE.js → chunk-CSOROFJY.js} +5 -5
  67. package/dist/{chunk-CZULJ647.js → chunk-DJ7EZIQA.js} +3 -3
  68. package/dist/{chunk-IUOUNRLH.js → chunk-ELLJPP6O.js} +2 -2
  69. package/dist/{chunk-5SFM6XB4.js → chunk-ERK24P76.js} +12 -2
  70. package/dist/{chunk-DQ565WFA.js → chunk-EZHY65QA.js} +21 -11
  71. package/dist/{chunk-4LUPZ347.js → chunk-FDWMG6NY.js} +2 -2
  72. package/dist/{chunk-S5LX25XE.js → chunk-FUZ5VYWD.js} +1 -1
  73. package/dist/{chunk-N3NSWWNU.js → chunk-GAMVAORR.js} +1 -1
  74. package/dist/{chunk-QP3XPS5U.js → chunk-HC3FHDKB.js} +6 -6
  75. package/dist/{chunk-ATUZX4K7.js → chunk-IF26ZGZ7.js} +3 -3
  76. package/dist/{chunk-CSQJCXPX.js → chunk-J4KQL7YZ.js} +5 -5
  77. package/dist/{chunk-M7NX25ZA.js → chunk-K4MBKXAE.js} +1 -1
  78. package/dist/{chunk-PZOFPAPG.js → chunk-L3AFVFGQ.js} +2 -2
  79. package/dist/{chunk-S54YLXWW.js → chunk-METMK3L4.js} +4 -4
  80. package/dist/{chunk-ZEHUWE3X.js → chunk-MUNL23WE.js} +6 -6
  81. package/dist/{chunk-3TJLPABQ.js → chunk-NKLNU577.js} +5 -5
  82. package/dist/{chunk-GMXKITVA.js → chunk-NMUI3ZWG.js} +1 -1
  83. package/dist/{chunk-IJ6XKKQW.js → chunk-O6IUZGWV.js} +1 -1
  84. package/dist/chunk-PPY5N2E6.js +58 -0
  85. package/dist/{chunk-XWY4UW62.js → chunk-PUHP4IBA.js} +4 -4
  86. package/dist/{chunk-T2AC5VFQ.js → chunk-QGSHV3WO.js} +5 -5
  87. package/dist/{chunk-RGQEULXD.js → chunk-QLMOU57V.js} +28 -17
  88. package/dist/{chunk-FAGDOPQI.js → chunk-QUBIEZXC.js} +3 -3
  89. package/dist/{chunk-XFUKCBPU.js → chunk-RLVJ6TVL.js} +23 -23
  90. package/dist/{chunk-VPZ5K72D.js → chunk-RPPXQNIA.js} +1 -1
  91. package/dist/{chunk-MCHOBQHE.js → chunk-S54C6TQC.js} +7 -6
  92. package/dist/{chunk-ZL3UR6M5.js → chunk-SEILUNWB.js} +3 -3
  93. package/dist/{chunk-4JJ3T7IE.js → chunk-SOCJ2N3E.js} +6 -6
  94. package/dist/{chunk-JYXIARGW.js → chunk-SVQWHV65.js} +1 -1
  95. package/dist/{chunk-T2BEIPK2.js → chunk-U7NR4JVD.js} +4 -4
  96. package/dist/{chunk-LVWXRXT7.js → chunk-USE5IEEW.js} +1 -1
  97. package/dist/{chunk-OHD35NPC.js → chunk-V57XLKZL.js} +2 -2
  98. package/dist/{chunk-KIH5BDH5.js → chunk-VIJIG5YL.js} +2 -2
  99. package/dist/{chunk-RF74KBF5.js → chunk-W2IVTHIG.js} +9 -9
  100. package/dist/{chunk-WXZN5CS3.js → chunk-WFSGCFXQ.js} +2 -2
  101. package/dist/{chunk-IRZ5FVWG.js → chunk-WN3RACVD.js} +3 -3
  102. package/dist/{chunk-3WNOO7BM.js → chunk-WXPVVXBU.js} +4 -4
  103. package/dist/{chunk-HUWLEVL2.js → chunk-WXZHQ3TT.js} +5 -5
  104. package/dist/chunk-X3YKQJCZ.js +36 -0
  105. package/dist/{chunk-I43PY5G3.js → chunk-XEJOPVCG.js} +1 -1
  106. package/dist/{chunk-ECJJKDP7.js → chunk-XV6S5PWW.js} +3 -3
  107. package/dist/{chunk-ZJNU32VE.js → chunk-Y5V4PA2Z.js} +155 -37
  108. package/dist/{chunk-7RZO6VWD.js → chunk-Y5VKZNFN.js} +2 -2
  109. package/dist/{chunk-VLJ5C6IT.js → chunk-YGE6ML3O.js} +4 -4
  110. package/dist/{chunk-XXNT4FGA.js → chunk-YPD3MTFU.js} +1 -1
  111. package/dist/{chunk-KW6EYFZV.js → chunk-ZGOJLNKS.js} +4 -4
  112. package/dist/{codex-app-cards-switch-TVKXJWXE.js → codex-app-cards-switch-ZO76D2NY.js} +11 -11
  113. package/dist/{codex-desktop-launch-ROTJEEGF.js → codex-desktop-launch-MJT5E3O3.js} +1 -1
  114. package/dist/{codex-session-launch-VL6ZQA3N.js → codex-session-launch-PTFCVIC7.js} +19 -19
  115. package/dist/{config-SYLUYZQE.js → config-SXV6XNVU.js} +3 -3
  116. package/dist/{global-upgrade-IPWV5PTM.js → global-upgrade-ETDP32LO.js} +9 -9
  117. package/dist/{identity-ARPAUNGN.js → identity-OOLIOSMR.js} +4 -4
  118. package/dist/{live-capabilities-EWTRY2HA.js → live-capabilities-2XIFA7YO.js} +4 -4
  119. package/dist/{paths-IZTNMJAQ.js → paths-IIOQZESZ.js} +3 -3
  120. package/dist/{reapply-ILKOYYKA.js → reapply-KFAJSKOQ.js} +28 -28
  121. package/dist/src/index.js +22 -22
  122. package/dist/{upgrade-lock-ML73QXIG.js → upgrade-lock-T6ILR644.js} +4 -4
  123. package/dist/{usage-OUV76AIY.js → usage-6TN7FVWS.js} +3 -3
  124. package/package.json +1 -1
  125. package/dist/chunk-7QLSKOSU.js +0 -19
  126. package/dist/chunk-EO23EUOK.js +0 -14
@@ -1,3 +1,132 @@
1
+ // ../contracts/src/json-record.ts
2
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3
+
4
+ // ../contracts/src/agent-questions.ts
5
+ var MAX_AGENT_QUESTIONS = 4;
6
+ var MIN_AGENT_QUESTION_OPTIONS = 2;
7
+ var MAX_AGENT_QUESTION_OPTIONS = 4;
8
+ var AGENT_QUESTION_HEADER_MAX = 40;
9
+ var AGENT_QUESTION_OPTION_MAX = 100;
10
+ var AGENT_QUESTION_DESCRIPTION_MAX = 500;
11
+ var agentQuestionOptionIn = (raw) => {
12
+ if (!isRecord(raw) || typeof raw.label !== "string" || raw.label.length === 0) return [];
13
+ return [{
14
+ label: raw.label,
15
+ ...typeof raw.description === "string" ? { description: raw.description } : {}
16
+ }];
17
+ };
18
+ var agentQuestionFieldsIn = (raw) => {
19
+ if (!isRecord(raw) || typeof raw.question !== "string") return void 0;
20
+ return {
21
+ question: raw.question,
22
+ ...typeof raw.header === "string" ? { header: raw.header } : {},
23
+ multiSelect: raw.multiSelect === true,
24
+ ...typeof raw.allowOther === "boolean" ? { allowOther: raw.allowOther } : {},
25
+ options: Array.isArray(raw.options) ? raw.options.flatMap(agentQuestionOptionIn) : []
26
+ };
27
+ };
28
+ var parseAgentQuestion = (raw) => {
29
+ const question = agentQuestionFieldsIn(raw);
30
+ return question && question.options.length >= MIN_AGENT_QUESTION_OPTIONS ? question : void 0;
31
+ };
32
+ var parseAgentQuestions = (toolInput) => {
33
+ const questions = toolInput.questions;
34
+ if (!Array.isArray(questions) || questions.length === 0) return void 0;
35
+ if (questions.length > MAX_AGENT_QUESTIONS) return void 0;
36
+ const parsed = [];
37
+ for (const raw of questions) {
38
+ const one = parseAgentQuestion(raw);
39
+ if (!one || parsed.some((question) => question.question === one.question)) return void 0;
40
+ parsed.push(one);
41
+ }
42
+ return parsed;
43
+ };
44
+ var isTypedAnswerQuestion = (question) => question.options.length === 0;
45
+ var typingCanAnswer = (question) => question.multiSelect === false && question.allowOther !== false;
46
+ var optionCountCanBeAnswered = (question, optionCount) => optionCount === 0 ? typingCanAnswer(question) : optionCount >= MIN_AGENT_QUESTION_OPTIONS && optionCount <= MAX_AGENT_QUESTION_OPTIONS;
47
+ var isAgentQuestionSet = (value) => {
48
+ if (!Array.isArray(value) || value.length === 0 || value.length > 4) return false;
49
+ const questions = /* @__PURE__ */ new Set();
50
+ for (const item of value) {
51
+ if (!isRecord(item)) return false;
52
+ const question = item;
53
+ if (typeof question.question !== "string" || question.question.length === 0 || questions.has(question.question) || question.header !== void 0 && typeof question.header !== "string" || typeof question.multiSelect !== "boolean" || question.allowOther !== void 0 && typeof question.allowOther !== "boolean" || !Array.isArray(question.options) || !optionCountCanBeAnswered(question, question.options.length)) return false;
54
+ const labels = /* @__PURE__ */ new Set();
55
+ for (const option of question.options) {
56
+ if (!option || typeof option !== "object" || Array.isArray(option)) return false;
57
+ const record3 = option;
58
+ const label = record3.label;
59
+ if (typeof label !== "string" || label.length === 0 || labels.has(label) || record3.description !== void 0 && typeof record3.description !== "string") return false;
60
+ labels.add(label);
61
+ }
62
+ questions.add(question.question);
63
+ }
64
+ return true;
65
+ };
66
+ var parseAgentQuestionAnswers = (questions, value) => {
67
+ let parsed;
68
+ try {
69
+ parsed = JSON.parse(value);
70
+ } catch {
71
+ return null;
72
+ }
73
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
74
+ const answers = parsed;
75
+ if (Object.keys(answers).length !== questions.length) return null;
76
+ for (const question of questions) {
77
+ const answer = answers[question.question];
78
+ if (typeof answer !== "string" || answer.trim().length === 0) return null;
79
+ const labels = new Set(question.options.map((option) => option.label));
80
+ if (!question.multiSelect) {
81
+ if (question.allowOther === false && !labels.has(answer)) return null;
82
+ continue;
83
+ }
84
+ try {
85
+ const selected = JSON.parse(answer);
86
+ if (!Array.isArray(selected)) {
87
+ if (question.allowOther === false) return null;
88
+ continue;
89
+ }
90
+ if (selected.length === 0 || new Set(selected).size !== selected.length || !selected.every((label) => typeof label === "string" && labels.has(label))) return null;
91
+ } catch {
92
+ if (question.allowOther === false) return null;
93
+ }
94
+ }
95
+ return answers;
96
+ };
97
+
98
+ // ../contracts/src/codex-question.ts
99
+ var CODEX_QUESTION_TOOL = "request_user_input";
100
+ var CODEX_ASYNC_QUESTION_TOOL = "request_user_input_async";
101
+ var CODEX_QUESTION_TOOLS = [CODEX_QUESTION_TOOL, CODEX_ASYNC_QUESTION_TOOL];
102
+ var CODEX_QUESTION_TOOL_NAMES = new Set(CODEX_QUESTION_TOOLS);
103
+ var isCodexQuestionTool = (toolName) => typeof toolName === "string" && CODEX_QUESTION_TOOL_NAMES.has(toolName);
104
+ var noOptionsWhenAbsent = (options) => options ?? [];
105
+ var optionsNamedByLabel = (options) => Array.isArray(options) ? options.map((label) => ({ label })) : options;
106
+ var offersOnlyItsOptions = (raw, options) => raw.isOther === false && Array.isArray(options) && options.length > 0;
107
+ var codexQuestionFields = (raw, toolName) => {
108
+ if (!isRecord(raw) || raw.isSecret === true) return void 0;
109
+ const options = noOptionsWhenAbsent(raw.options);
110
+ if (toolName === CODEX_ASYNC_QUESTION_TOOL) {
111
+ return { question: raw.title, options: optionsNamedByLabel(options), multiSelect: false };
112
+ }
113
+ return { ...raw, options, multiSelect: raw.multiSelect ?? false, ...offersOnlyItsOptions(raw, options) ? { allowOther: false } : {} };
114
+ };
115
+ var agentQuestionsOf = (questions) => questions.flatMap((question) => agentQuestionFieldsIn(question) ?? []);
116
+ var parseCodexQuestions = (toolName, toolInput) => {
117
+ const raw = Array.isArray(toolInput.questions) ? toolInput.questions : [];
118
+ const questions = raw.map((question) => codexQuestionFields(question, toolName));
119
+ return isAgentQuestionSet(questions) ? agentQuestionsOf(questions) : void 0;
120
+ };
121
+ var CODEX_QUESTION_ANSWERED_IN_PUSHARY = "The user already answered this question in Pushary, so it was not shown again";
122
+ var CODEX_QUESTION_CONTINUE = "Continue with this answer and do not ask it again";
123
+ var quotedAnswerPair = ([question, answer]) => `${JSON.stringify(question)}: ${JSON.stringify(answer)}`;
124
+ var codexQuestionAnswerReason = (answers) => {
125
+ const pairs = Object.entries(answers);
126
+ const answered = pairs.length === 1 ? `Their answer: ${JSON.stringify(pairs[0]?.[1] ?? "")}` : `Their answers: ${pairs.map(quotedAnswerPair).join("; ")}`;
127
+ return `${CODEX_QUESTION_ANSWERED_IN_PUSHARY}. ${answered}. ${CODEX_QUESTION_CONTINUE}`;
128
+ };
129
+
1
130
  // ../contracts/src/claude-connector.ts
2
131
  var CLAUDE_CONNECTOR_URL = "https://pushary.com/api/mcp/mcp";
3
132
  var CLAUDE_CONNECTOR_GUIDE = "https://pushary.com/docs/agents/guides/claude-desktop";
@@ -255,6 +384,13 @@ var AGENTS = {
255
384
  enforcementDetail: "Shell, edits and MCP calls through native PreToolUse hooks. Grok's own permission checks still apply after a phone approval.",
256
385
  capabilityNote: "Grok fails open if it kills a hook or cannot run it. Setup leaves Grok's permission mode unchanged. Native question and plan dialogs stay in Grok; proactive phone questions use Pushary MCP."
257
386
  },
387
+ grok_bot: {
388
+ ...defaults,
389
+ label: "Grok Bot",
390
+ connectVia: "Grok Bot app plugin (remote MCP connector)",
391
+ setupGuide: "[Grok Bot setup](/docs/agents/guides/grok-bot)",
392
+ capabilityNote: "Grok Bot keeps its own approval cards, and Pushary cannot stop a Bot in the middle of a task. A question nobody answers expires after about ten minutes and is never treated as a yes."
393
+ },
258
394
  windsurf: {
259
395
  ...defaults,
260
396
  label: "Windsurf",
@@ -310,9 +446,6 @@ var normalizeAgentId = (value, accepted) => {
310
446
  return accepted.find((candidate) => names.includes(candidate)) ?? null;
311
447
  };
312
448
 
313
- // ../contracts/src/json-record.ts
314
- var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
315
-
316
449
  // ../contracts/src/grok.ts
317
450
  var GROK_FIELD_ALIASES = {
318
451
  sessionId: "session_id",
@@ -2884,115 +3017,6 @@ var TIMEOUT_ACTION_THAT_REFUSES = "deny";
2884
3017
  var timeoutActionWithoutLocalPrompt = ({ mode, timeoutAction }) => mode !== "push_only" && timeoutAction === "approve" ? TIMEOUT_ACTION_THAT_REFUSES : timeoutAction;
2885
3018
  var TIMEOUT_ACTIONS = ["approve", "deny", "escalate", "wait"];
2886
3019
  var isTimeoutAction = (value) => typeof value === "string" && TIMEOUT_ACTIONS.includes(value);
2887
- var MAX_AGENT_QUESTIONS = 4;
2888
- var MIN_AGENT_QUESTION_OPTIONS = 2;
2889
- var MAX_AGENT_QUESTION_OPTIONS = 4;
2890
- var AGENT_QUESTION_HEADER_MAX = 40;
2891
- var AGENT_QUESTION_OPTION_MAX = 100;
2892
- var AGENT_QUESTION_DESCRIPTION_MAX = 500;
2893
- var agentQuestionOptionIn = (raw) => {
2894
- if (!isRecord(raw) || typeof raw.label !== "string" || raw.label.length === 0) return [];
2895
- return [{
2896
- label: raw.label,
2897
- ...typeof raw.description === "string" ? { description: raw.description } : {}
2898
- }];
2899
- };
2900
- var agentQuestionFieldsIn = (raw) => {
2901
- if (!isRecord(raw) || typeof raw.question !== "string") return void 0;
2902
- return {
2903
- question: raw.question,
2904
- ...typeof raw.header === "string" ? { header: raw.header } : {},
2905
- multiSelect: raw.multiSelect === true,
2906
- ...typeof raw.allowOther === "boolean" ? { allowOther: raw.allowOther } : {},
2907
- options: Array.isArray(raw.options) ? raw.options.flatMap(agentQuestionOptionIn) : []
2908
- };
2909
- };
2910
- var parseAgentQuestion = (raw) => {
2911
- const question = agentQuestionFieldsIn(raw);
2912
- return question && question.options.length >= MIN_AGENT_QUESTION_OPTIONS ? question : void 0;
2913
- };
2914
- var parseAgentQuestions = (toolInput) => {
2915
- const questions = toolInput.questions;
2916
- if (!Array.isArray(questions) || questions.length === 0) return void 0;
2917
- if (questions.length > MAX_AGENT_QUESTIONS) return void 0;
2918
- const parsed = [];
2919
- for (const raw of questions) {
2920
- const one = parseAgentQuestion(raw);
2921
- if (!one || parsed.some((question) => question.question === one.question)) return void 0;
2922
- parsed.push(one);
2923
- }
2924
- return parsed;
2925
- };
2926
- var isTypedAnswerQuestion = (question) => question.options.length === 0;
2927
- var typingCanAnswer = (question) => question.multiSelect === false && question.allowOther !== false;
2928
- var optionCountCanBeAnswered = (question, optionCount) => optionCount === 0 ? typingCanAnswer(question) : optionCount >= MIN_AGENT_QUESTION_OPTIONS && optionCount <= MAX_AGENT_QUESTION_OPTIONS;
2929
- var isAgentQuestionSet = (value) => {
2930
- if (!Array.isArray(value) || value.length === 0 || value.length > 4) return false;
2931
- const questions = /* @__PURE__ */ new Set();
2932
- for (const item of value) {
2933
- if (!isRecord(item)) return false;
2934
- const question = item;
2935
- if (typeof question.question !== "string" || question.question.length === 0 || questions.has(question.question) || question.header !== void 0 && typeof question.header !== "string" || typeof question.multiSelect !== "boolean" || question.allowOther !== void 0 && typeof question.allowOther !== "boolean" || !Array.isArray(question.options) || !optionCountCanBeAnswered(question, question.options.length)) return false;
2936
- const labels = /* @__PURE__ */ new Set();
2937
- for (const option of question.options) {
2938
- if (!option || typeof option !== "object" || Array.isArray(option)) return false;
2939
- const record3 = option;
2940
- const label = record3.label;
2941
- if (typeof label !== "string" || label.length === 0 || labels.has(label) || record3.description !== void 0 && typeof record3.description !== "string") return false;
2942
- labels.add(label);
2943
- }
2944
- questions.add(question.question);
2945
- }
2946
- return true;
2947
- };
2948
- var parseAgentQuestionAnswers = (questions, value) => {
2949
- let parsed;
2950
- try {
2951
- parsed = JSON.parse(value);
2952
- } catch {
2953
- return null;
2954
- }
2955
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
2956
- const answers = parsed;
2957
- if (Object.keys(answers).length !== questions.length) return null;
2958
- for (const question of questions) {
2959
- const answer = answers[question.question];
2960
- if (typeof answer !== "string" || answer.trim().length === 0) return null;
2961
- const labels = new Set(question.options.map((option) => option.label));
2962
- if (!question.multiSelect) {
2963
- if (question.allowOther === false && !labels.has(answer)) return null;
2964
- continue;
2965
- }
2966
- try {
2967
- const selected = JSON.parse(answer);
2968
- if (!Array.isArray(selected)) {
2969
- if (question.allowOther === false) return null;
2970
- continue;
2971
- }
2972
- if (selected.length === 0 || new Set(selected).size !== selected.length || !selected.every((label) => typeof label === "string" && labels.has(label))) return null;
2973
- } catch {
2974
- if (question.allowOther === false) return null;
2975
- }
2976
- }
2977
- return answers;
2978
- };
2979
- var noOptionsWhenAbsent = (options) => options ?? [];
2980
- var optionsNamedByLabel = (options) => Array.isArray(options) ? options.map((label) => ({ label })) : options;
2981
- var offersOnlyItsOptions = (raw, options) => raw.isOther === false && Array.isArray(options) && options.length > 0;
2982
- var codexQuestionFields = (raw, toolName) => {
2983
- if (!isRecord(raw) || raw.isSecret === true) return void 0;
2984
- const options = noOptionsWhenAbsent(raw.options);
2985
- if (toolName === CODEX_ASYNC_QUESTION_TOOL) {
2986
- return { question: raw.title, options: optionsNamedByLabel(options), multiSelect: false };
2987
- }
2988
- return { ...raw, options, multiSelect: raw.multiSelect ?? false, ...offersOnlyItsOptions(raw, options) ? { allowOther: false } : {} };
2989
- };
2990
- var agentQuestionsOf = (questions) => questions.flatMap((question) => agentQuestionFieldsIn(question) ?? []);
2991
- var parseCodexQuestions = (toolName, toolInput) => {
2992
- const raw = Array.isArray(toolInput.questions) ? toolInput.questions : [];
2993
- const questions = raw.map((question) => codexQuestionFields(question, toolName));
2994
- return isAgentQuestionSet(questions) ? agentQuestionsOf(questions) : void 0;
2995
- };
2996
3020
  var typedTextAsAnswers = (questions, text2) => {
2997
3021
  const [only, ...others] = questions;
2998
3022
  if (!only || others.length > 0 || !isTypedAnswerQuestion(only)) return null;
@@ -3004,14 +3028,6 @@ var isLoneTypedAnswerQuestion = (questions) => questions.length === 1 && questio
3004
3028
  var questionsShownAsASet = (questions) => isLoneTypedAnswerQuestion(questions) ? void 0 : questions;
3005
3029
  var agentQuestionContext = (header, project) => header ? `${header}: your agent is asking in ${project}` : `Your agent is asking in ${project}`;
3006
3030
  var answersToQuestionsAsAsked = (questions, answer) => questionsShownAsASet(questions) ? parseAgentQuestionAnswers(questions, answer) : typedTextAsAnswers(questions, answer);
3007
- var CODEX_QUESTION_ANSWERED_IN_PUSHARY = "The user already answered this question in Pushary, so it was not shown again";
3008
- var CODEX_QUESTION_CONTINUE = "Continue with this answer and do not ask it again";
3009
- var quotedAnswerPair = ([question, answer]) => `${JSON.stringify(question)}: ${JSON.stringify(answer)}`;
3010
- var codexQuestionAnswerReason = (answers) => {
3011
- const pairs = Object.entries(answers);
3012
- const answered = pairs.length === 1 ? `Their answer: ${JSON.stringify(pairs[0]?.[1] ?? "")}` : `Their answers: ${pairs.map(quotedAnswerPair).join("; ")}`;
3013
- return `${CODEX_QUESTION_ANSWERED_IN_PUSHARY}. ${answered}. ${CODEX_QUESTION_CONTINUE}`;
3014
- };
3015
3031
  var INSTALL_SOURCES = [
3016
3032
  /** `npx pushary@latest ...`, the documented one-liner. */
3017
3033
  "npx",
@@ -5644,11 +5660,6 @@ var AGENT_QUESTION_STRATEGY = {
5644
5660
  };
5645
5661
  var UPDATED_INPUT_QUESTION_SOURCES = /* @__PURE__ */ new Set(["claude", "qoder", "codebuddy", "droid"]);
5646
5662
  var GROK_NATIVE_DIALOG_TOOLS = /* @__PURE__ */ new Set(["ask_user_question", "exit_plan_mode"]);
5647
- var CODEX_QUESTION_TOOL = "request_user_input";
5648
- var CODEX_ASYNC_QUESTION_TOOL = "request_user_input_async";
5649
- var CODEX_QUESTION_TOOLS = [CODEX_QUESTION_TOOL, CODEX_ASYNC_QUESTION_TOOL];
5650
- var CODEX_QUESTION_TOOL_NAMES = new Set(CODEX_QUESTION_TOOLS);
5651
- var isCodexQuestionTool = (toolName) => typeof toolName === "string" && CODEX_QUESTION_TOOL_NAMES.has(toolName);
5652
5663
  var isCodexQuestionCall = (source, event, toolName) => source === "codex" && event === "PreToolUse" && isCodexQuestionTool(toolName);
5653
5664
  var codexQuestionGoesToPushary = (verdict) => verdict.kind === "gate" && !approvalWaitsAtTheAgentsPrompt(verdict.policy.mode);
5654
5665
  function agentQuestionStrategy(source, event, toolName) {
@@ -6179,6 +6190,17 @@ var hookActivationResult = (report) => {
6179
6190
  };
6180
6191
 
6181
6192
  export {
6193
+ isRecord,
6194
+ AGENT_QUESTION_HEADER_MAX,
6195
+ AGENT_QUESTION_OPTION_MAX,
6196
+ AGENT_QUESTION_DESCRIPTION_MAX,
6197
+ parseAgentQuestions,
6198
+ isTypedAnswerQuestion,
6199
+ parseAgentQuestionAnswers,
6200
+ CODEX_QUESTION_TOOLS,
6201
+ isCodexQuestionTool,
6202
+ parseCodexQuestions,
6203
+ codexQuestionAnswerReason,
6182
6204
  CLAUDE_CONNECTOR_URL,
6183
6205
  CLAUDE_CONNECTOR_GUIDE,
6184
6206
  COWORK_CONNECTOR_LIMIT,
@@ -6193,7 +6215,6 @@ export {
6193
6215
  MANAGED_PROVIDERS,
6194
6216
  DISCONNECT_AGENT_IDS,
6195
6217
  normalizeAgentId,
6196
- isRecord,
6197
6218
  GROK_UNREADABLE_CALL,
6198
6219
  isGrokHookPayload,
6199
6220
  isGrokSessionEndStop,
@@ -6254,18 +6275,10 @@ export {
6254
6275
  approvalModeWithoutLocalPrompt,
6255
6276
  timeoutActionWithoutLocalPrompt,
6256
6277
  isTimeoutAction,
6257
- AGENT_QUESTION_HEADER_MAX,
6258
- AGENT_QUESTION_OPTION_MAX,
6259
- AGENT_QUESTION_DESCRIPTION_MAX,
6260
- parseAgentQuestions,
6261
- isTypedAnswerQuestion,
6262
- parseAgentQuestionAnswers,
6263
- parseCodexQuestions,
6264
6278
  plainAskOf,
6265
6279
  questionsShownAsASet,
6266
6280
  agentQuestionContext,
6267
6281
  answersToQuestionsAsAsked,
6268
- codexQuestionAnswerReason,
6269
6282
  normalizeInstallSource,
6270
6283
  HOOK_BUDGETS,
6271
6284
  HOOK_HOLD_BUDGET_ENV,
@@ -6309,8 +6322,6 @@ export {
6309
6322
  shouldDeferToNativeMode,
6310
6323
  AGENT_QUESTION_TOOL,
6311
6324
  AGENT_QUESTION_STRATEGY,
6312
- CODEX_QUESTION_TOOLS,
6313
- isCodexQuestionTool,
6314
6325
  codexQuestionGoesToPushary,
6315
6326
  agentQuestionStrategy,
6316
6327
  questionHandedToTheTerminal,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  NPX_LATEST_COMMAND
3
- } from "./chunk-5SFM6XB4.js";
3
+ } from "./chunk-ERK24P76.js";
4
4
 
5
5
  // src/config.ts
6
6
  import { readFileSync } from "fs";
@@ -2,23 +2,24 @@ import {
2
2
  CLAUDE_HOOK_EVENTS,
3
3
  GEMINI_HOOK_EVENTS,
4
4
  hasGeminiHooks
5
- } from "./chunk-ATUZX4K7.js";
5
+ } from "./chunk-IF26ZGZ7.js";
6
6
  import {
7
7
  AGENT_CHOICES
8
- } from "./chunk-ECJJKDP7.js";
8
+ } from "./chunk-XV6S5PWW.js";
9
9
  import {
10
10
  BELL_HOOK_BINARY
11
- } from "./chunk-QDFLBTUP.js";
11
+ } from "./chunk-4MUFZU2G.js";
12
12
  import {
13
13
  CODEX_HOOK_EVENTS,
14
+ HOOK_SHELL,
14
15
  hasCodexHooks,
15
16
  holdBudgetOf
16
- } from "./chunk-MYBCXDIL.js";
17
+ } from "./chunk-6MJWYYAS.js";
17
18
  import {
18
19
  CURSOR_GATE_EVENTS,
19
20
  HOOK_LOCATOR_BINARY,
20
21
  hookCommandHoldBudget
21
- } from "./chunk-GZL3KEPC.js";
22
+ } from "./chunk-AXJLJJ3M.js";
22
23
 
23
24
  // src/diagnostics/bridge.ts
24
25
  import { execFileSync } from "child_process";
@@ -101,9 +102,10 @@ var record = (value) => value && typeof value === "object" && !Array.isArray(val
101
102
  var CLAUDE_HOOK_BINARY = /pushary-[a-z-]*hook/;
102
103
  var EXACT_HOOK_BINARY = /^pushary-[a-z-]*(hook|gate)$/;
103
104
  var pusharyHookExecutable = (command) => {
104
- const tokens = command.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
105
+ const tokens = command.match(/"(?:\\.|[^"\\])*"|'[^']*'|\S+/g) ?? [];
105
106
  for (const raw of tokens.reverse()) {
106
- const token = raw[0] === '"' && raw.at(-1) === '"' || raw[0] === "'" && raw.at(-1) === "'" ? raw.slice(1, -1) : raw;
107
+ const unquoted = raw[0] === '"' && raw.at(-1) === '"' || raw[0] === "'" && raw.at(-1) === "'" ? raw.slice(1, -1) : raw;
108
+ const token = command.startsWith(`${HOOK_SHELL} `) && raw[0] === '"' ? unquoted.replace(/\\(["\\$`])/g, "$1") : unquoted;
107
109
  const name = token.split(/[/\\]/).pop()?.replace(/\.(cmd|exe|mjs)$/i, "") ?? "";
108
110
  if (name === HOOK_LOCATOR_BINARY || EXACT_HOOK_BINARY.test(name)) return token;
109
111
  }
@@ -6,38 +6,39 @@ import {
6
6
  } from "./chunk-QW3FRUFB.js";
7
7
  import {
8
8
  ensureDaemonRunning
9
- } from "./chunk-QP3XPS5U.js";
9
+ } from "./chunk-HC3FHDKB.js";
10
10
  import {
11
11
  hookOwnerOf
12
- } from "./chunk-KSVCCSAO.js";
12
+ } from "./chunk-CDQHQAS7.js";
13
13
  import {
14
14
  buildTranscriptRecords,
15
15
  decodePublicKey,
16
16
  generateSessionKey,
17
17
  wrapSessionKey
18
- } from "./chunk-JSQOHMNJ.js";
18
+ } from "./chunk-2AELPDMT.js";
19
19
  import {
20
20
  fetchTranscriptRecipients,
21
21
  uploadTranscriptRecords
22
- } from "./chunk-N3NSWWNU.js";
22
+ } from "./chunk-GAMVAORR.js";
23
23
  import {
24
24
  transcriptEnvironmentPreference,
25
25
  transcriptsEnabled
26
- } from "./chunk-C3IWX6KU.js";
26
+ } from "./chunk-7JXJJNQ5.js";
27
27
  import {
28
28
  readTranscriptPreference
29
- } from "./chunk-IRZ5FVWG.js";
29
+ } from "./chunk-WN3RACVD.js";
30
30
  import {
31
+ claudeConfigDir,
31
32
  codexHome,
32
33
  pusharyDir
33
- } from "./chunk-MYBCXDIL.js";
34
+ } from "./chunk-6MJWYYAS.js";
34
35
  import {
35
36
  writeJsonAtomic
36
37
  } from "./chunk-QENTGE75.js";
37
38
  import {
38
39
  getApiKey,
39
40
  getBaseUrl
40
- } from "./chunk-LVDC3RUE.js";
41
+ } from "./chunk-BG4Y64FW.js";
41
42
 
42
43
  // src/transcript-registration.ts
43
44
  import { createHash } from "crypto";
@@ -92,7 +93,6 @@ var transcriptLinesSeenLive = (lines, epochStartedAtMs, startedBeforeEpoch) => l
92
93
  });
93
94
 
94
95
  // src/transcript-sync.ts
95
- import { homedir as homedir2 } from "os";
96
96
  import { join as join2, resolve } from "path";
97
97
  import { closeSync, existsSync as existsSync2, fstatSync, openSync, readFileSync as readFileSync2, readSync, statSync as statSync2 } from "fs";
98
98
 
@@ -165,7 +165,7 @@ var sweepStaleSessionKeys = (now = Date.now(), dir = sessionDir()) => {
165
165
  // src/transcript-sync.ts
166
166
  var claudeTranscriptPath = (cwd, sessionId) => {
167
167
  const projectId = resolve(cwd).replace(/[^a-zA-Z0-9-]/g, "-");
168
- const configDir = process.env.CLAUDE_CONFIG_DIR || join2(homedir2(), ".claude");
168
+ const configDir = claudeConfigDir();
169
169
  return join2(configDir, "projects", projectId, `${sessionId}.jsonl`);
170
170
  };
171
171
  var createSyncState = () => ({
@@ -446,7 +446,7 @@ var startTranscriptSync = (opts) => {
446
446
  };
447
447
 
448
448
  // src/transcript-registration.ts
449
- import { homedir as homedir3 } from "os";
449
+ import { homedir as homedir2 } from "os";
450
450
  var defaultRegistryDirectory = () => process.env.PUSHARY_TRANSCRIPT_REGISTRY_DIR || join3(pusharyDir(), "transcript-capture");
451
451
  var cliRegistry = (apiKey = getApiKey()) => ({ apiKey, baseUrl: getBaseUrl(), directory: defaultRegistryDirectory() });
452
452
  var accountFor = (registry) => createHash("sha256").update(`${registry.baseUrl}\0${registry.apiKey}`).digest("hex");
@@ -466,7 +466,7 @@ var registeredTranscriptEnabled = (apiKey, sessionId) => {
466
466
  }
467
467
  };
468
468
  var macOwnsClaudeHooks = (cwd) => [
469
- join3(process.env.CLAUDE_CONFIG_DIR || join3(homedir3(), ".claude"), "settings.json"),
469
+ join3(claudeConfigDir(), "settings.json"),
470
470
  join3(cwd, ".claude", "settings.json"),
471
471
  join3(cwd, ".claude", "settings.local.json")
472
472
  ].some((path) => {
@@ -478,7 +478,7 @@ var macOwnsClaudeHooks = (cwd) => [
478
478
  });
479
479
  var TRANSCRIPT_ROOT_NAME = { claude: "projects", codex: "sessions" };
480
480
  var transcriptRoots = (source) => {
481
- const home = homedir3();
481
+ const home = homedir2();
482
482
  if (source === "codex") return [join3(codexHome(), TRANSCRIPT_ROOT_NAME.codex)];
483
483
  const configured = process.env.CLAUDE_CONFIG_DIR;
484
484
  return [join3(configured || join3(home, ".claude"), TRANSCRIPT_ROOT_NAME.claude), join3(home, ".claude", TRANSCRIPT_ROOT_NAME.claude)];
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  APPROVAL_SETTINGS_PATH
3
- } from "./chunk-VLJ5C6IT.js";
3
+ } from "./chunk-YGE6ML3O.js";
4
4
  import {
5
5
  readMacAppAgentKey
6
- } from "./chunk-T2BEIPK2.js";
6
+ } from "./chunk-U7NR4JVD.js";
7
7
  import {
8
8
  findExportedKeyLines,
9
9
  isAdoptedMacAppKey,
@@ -13,13 +13,13 @@ import {
13
13
  readConfigFileKey,
14
14
  readKeySource,
15
15
  readMacAppKeyPrefix
16
- } from "./chunk-IRZ5FVWG.js";
16
+ } from "./chunk-WN3RACVD.js";
17
17
  import {
18
18
  absoluteUrl
19
- } from "./chunk-S54YLXWW.js";
19
+ } from "./chunk-METMK3L4.js";
20
20
  import {
21
21
  isValidApiKey
22
- } from "./chunk-GZL3KEPC.js";
22
+ } from "./chunk-AXJLJJ3M.js";
23
23
 
24
24
  // src/key-revocation.ts
25
25
  var SELF_REVOKE_PATH = "/api/v1/server/keys/self/revoke";
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  callMcpTool
3
- } from "./chunk-ZL3UR6M5.js";
3
+ } from "./chunk-SEILUNWB.js";
4
4
  import {
5
5
  NPX_LATEST_COMMAND
6
- } from "./chunk-5SFM6XB4.js";
6
+ } from "./chunk-ERK24P76.js";
7
7
  import {
8
8
  classifyConfirmResponse,
9
9
  isAnswerSource
10
- } from "./chunk-GZL3KEPC.js";
10
+ } from "./chunk-AXJLJJ3M.js";
11
11
 
12
12
  // src/reach.ts
13
13
  var reachVerdict = (channels) => {
@@ -3,11 +3,11 @@ import {
3
3
  decodePublicKey,
4
4
  generateSessionKey,
5
5
  wrapSessionKey
6
- } from "./chunk-JSQOHMNJ.js";
6
+ } from "./chunk-2AELPDMT.js";
7
7
  import {
8
8
  fetchTranscriptRecipients,
9
9
  uploadTranscriptRecords
10
- } from "./chunk-N3NSWWNU.js";
10
+ } from "./chunk-GAMVAORR.js";
11
11
 
12
12
  // src/live-session.ts
13
13
  var MAX_TRANSCRIPT_QUEUE = 100;
@@ -2,7 +2,7 @@ import {
2
2
  DISCONNECT_AGENT_IDS,
3
3
  SETUP_AGENT_CHOICES,
4
4
  isApprovalMode
5
- } from "./chunk-GZL3KEPC.js";
5
+ } from "./chunk-AXJLJJ3M.js";
6
6
 
7
7
  // src/cli/mode-request.ts
8
8
  var parseDuration = (value) => {
@@ -41,7 +41,7 @@ var COMMAND_LIST = [
41
41
  module: "pushary-codex-session",
42
42
  summary: "Experimental Codex extras that ordinary setup does not turn on",
43
43
  detail: [
44
- "Use --app-cards on|off|status so the Codex app also asks Pushary before it uses an app on this Mac.",
44
+ "Use --app-cards on|off|status so the Codex app also asks Pushary before it uses an app on this Mac, or before its browser opens a site, downloads or uploads.",
45
45
  "Use --desktop [app path] to do the same for one launch. Quit Codex first; a running app is not attached.",
46
46
  "Use --cwd <directory> to start a managed Codex terminal session in a project."
47
47
  ],
@@ -146,6 +146,16 @@ var resolveCommandName = (token) => {
146
146
  };
147
147
  var isInvocationOf = (token, name) => resolveCommandName(token) === name;
148
148
  var COMMAND_OPTIONS = {
149
+ login: [
150
+ ["--with-token", "Read an API key from stdin"],
151
+ ["--no-browser", "Print the sign-in link without opening a browser"],
152
+ ["--json", "Emit the result as one object instead of the screen"],
153
+ ["--force", "Start a new sign-in instead of reusing the saved key"]
154
+ ],
155
+ pair: [
156
+ ["--app", "Pair with the Pushary app"],
157
+ ["--browser", "Pair through a browser (default)"]
158
+ ],
149
159
  cowork: [
150
160
  ["--open", "Also open the setup guide in your browser"],
151
161
  ["--instructions", "Print standing instructions to paste into Cowork"],
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  SubscriptionRequiredError,
3
3
  callMcpTool
4
- } from "./chunk-ZL3UR6M5.js";
4
+ } from "./chunk-SEILUNWB.js";
5
5
  import {
6
6
  CLIENT_REPORT_DISABLE_ENV,
7
7
  isDisabledByEnv
@@ -9,14 +9,14 @@ import {
9
9
  import {
10
10
  installNetworkDefaults,
11
11
  isNetworkFailure
12
- } from "./chunk-5DZHKNBU.js";
12
+ } from "./chunk-4MWRR5IT.js";
13
13
  import {
14
14
  clientReportClaims
15
- } from "./chunk-MYBCXDIL.js";
15
+ } from "./chunk-6MJWYYAS.js";
16
16
  import {
17
17
  getApiKey,
18
18
  getBaseUrl
19
- } from "./chunk-LVDC3RUE.js";
19
+ } from "./chunk-BG4Y64FW.js";
20
20
  import {
21
21
  ASK_AGENT_NAME_MAX,
22
22
  CLIENT_REPORT_PATH,
@@ -24,7 +24,7 @@ import {
24
24
  GATE_PERMISSION_MODE_MAX_LENGTH,
25
25
  degradedLine,
26
26
  truncateLine
27
- } from "./chunk-GZL3KEPC.js";
27
+ } from "./chunk-AXJLJJ3M.js";
28
28
 
29
29
  // src/api.ts
30
30
  import { randomUUID } from "crypto";
@@ -114,6 +114,7 @@ var claimReportWindow = (claimsDir, key, now) => {
114
114
 
115
115
  // src/client-report.ts
116
116
  var CLIENT_REPORT_TIMEOUT_MS = 1e3;
117
+ var CLIENT_REPORT_MAX_ATTEMPTS = 2;
117
118
  var readApiKey = () => {
118
119
  try {
119
120
  return getApiKey();
@@ -147,12 +148,21 @@ var createClientErrorReporter = (deps) => {
147
148
  const apiKey = deps.apiKey();
148
149
  const now = deps.now();
149
150
  if (!apiKey || !claimReport(reportKey(event), now)) return;
150
- await deps.fetchImpl(`${deps.baseUrl()}${CLIENT_REPORT_PATH}`, {
151
- method: "POST",
152
- headers: { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json" },
153
- body: JSON.stringify({ ...event, occurredAt: new Date(now).toISOString() }),
154
- signal: AbortSignal.timeout(CLIENT_REPORT_TIMEOUT_MS)
155
- });
151
+ const signal = AbortSignal.timeout(CLIENT_REPORT_TIMEOUT_MS);
152
+ for (let attempt = 0; attempt < CLIENT_REPORT_MAX_ATTEMPTS && !signal.aborted; attempt++) {
153
+ try {
154
+ const response = await deps.fetchImpl(`${deps.baseUrl()}${CLIENT_REPORT_PATH}`, {
155
+ method: "POST",
156
+ headers: { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json" },
157
+ body: JSON.stringify({ ...event, occurredAt: new Date(now).toISOString() }),
158
+ signal
159
+ });
160
+ await response.body?.cancel();
161
+ if (response.ok || response.status < 500 && response.status !== 429) return;
162
+ } catch {
163
+ if (signal.aborted) return;
164
+ }
165
+ }
156
166
  } catch {
157
167
  return;
158
168
  }