pushary 1.9.75 → 1.9.77

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 +13 -0
  2. package/dist/{api-L3BIISGS.js → api-IGMCPM35.js} +10 -10
  3. package/dist/bin/pushary-bell-hook.js +26 -25
  4. package/dist/bin/pushary-bell.js +10 -10
  5. package/dist/bin/pushary-claude.js +27 -26
  6. package/dist/bin/pushary-clean.js +33 -33
  7. package/dist/bin/pushary-codex-bridge.js +21 -21
  8. package/dist/bin/pushary-codex-hook.js +31 -30
  9. package/dist/bin/pushary-codex-session.js +8 -8
  10. package/dist/bin/pushary-codex.js +13 -13
  11. package/dist/bin/pushary-connect.js +26 -26
  12. package/dist/bin/pushary-cowork.js +5 -5
  13. package/dist/bin/pushary-daemon.js +123 -66
  14. package/dist/bin/pushary-disconnect.js +25 -25
  15. package/dist/bin/pushary-display-hook.js +17 -16
  16. package/dist/bin/pushary-doctor.js +41 -41
  17. package/dist/bin/pushary-elicitation-hook.js +26 -25
  18. package/dist/bin/pushary-gemini-bridge.js +15 -15
  19. package/dist/bin/pushary-gemini-hook.js +28 -27
  20. package/dist/bin/pushary-hook.js +30 -29
  21. package/dist/bin/pushary-login.js +20 -20
  22. package/dist/bin/pushary-logout.js +24 -24
  23. package/dist/bin/pushary-mcp.js +11 -11
  24. package/dist/bin/pushary-mode.js +15 -15
  25. package/dist/bin/pushary-notification-hook.js +25 -24
  26. package/dist/bin/pushary-opencode-hook.js +27 -26
  27. package/dist/bin/pushary-permission-denied-hook.js +30 -29
  28. package/dist/bin/pushary-permission-hook.js +30 -29
  29. package/dist/bin/pushary-post-hook.js +25 -24
  30. package/dist/bin/pushary-prompt-hook.js +25 -24
  31. package/dist/bin/pushary-session-end-hook.js +27 -26
  32. package/dist/bin/pushary-session-start-hook.js +35 -34
  33. package/dist/bin/pushary-setup.js +50 -267
  34. package/dist/bin/pushary-stats.js +5 -5
  35. package/dist/bin/pushary-status.js +31 -31
  36. package/dist/bin/pushary-stop-hook.js +25 -24
  37. package/dist/bin/pushary-stopfailure-hook.js +25 -24
  38. package/dist/bin/pushary-suggestions.js +15 -15
  39. package/dist/bin/pushary-transcript-register.js +47 -18
  40. package/dist/bin/pushary-transcripts.js +19 -19
  41. package/dist/bin/pushary-upgrade.js +39 -39
  42. package/dist/bin/pushary-wait.js +15 -15
  43. package/dist/bin/pushary.js +7 -7
  44. package/dist/{chunk-JCZWWBBA.js → chunk-2IOQHB3C.js} +5 -5
  45. package/dist/{chunk-UIS332AZ.js → chunk-2OWG667E.js} +1 -1
  46. package/dist/{chunk-UEJTWQVF.js → chunk-2YRNRYZW.js} +1 -1
  47. package/dist/{chunk-UU3CDLIX.js → chunk-3UYMJZXV.js} +2 -2
  48. package/dist/{chunk-HXV2SY2V.js → chunk-4ZXFOT44.js} +2 -2
  49. package/dist/{chunk-L2FQBHIR.js → chunk-6WOG2SCC.js} +3 -2
  50. package/dist/{chunk-6BFLDHPM.js → chunk-6XOEYBWJ.js} +3 -3
  51. package/dist/{chunk-SOHJ37NA.js → chunk-7AQEWD2J.js} +3 -3
  52. package/dist/{chunk-VGHWK5Z2.js → chunk-7CU7OGWP.js} +263 -5
  53. package/dist/{chunk-GAAN3Y7V.js → chunk-7HYNAA6B.js} +1 -1
  54. package/dist/{chunk-EVBG5YUO.js → chunk-A3BF7SGM.js} +3 -3
  55. package/dist/{chunk-AUDYAVNM.js → chunk-ABAWD6QM.js} +3 -3
  56. package/dist/{chunk-Z7ORL6SC.js → chunk-BUBC4R5J.js} +2 -2
  57. package/dist/{chunk-RPKFXNOJ.js → chunk-CIMFZIGP.js} +2 -2
  58. package/dist/{chunk-SIJHPGUW.js → chunk-CKUFXQUJ.js} +3 -3
  59. package/dist/{chunk-P4TWTQ6V.js → chunk-CS5PJXL7.js} +6 -6
  60. package/dist/{chunk-LRYZXY4V.js → chunk-CU65AFT4.js} +1 -1
  61. package/dist/{chunk-M7BXAWUC.js → chunk-D5NLXF72.js} +3 -3
  62. package/dist/{chunk-LIDUPDEY.js → chunk-DYDA77FS.js} +4 -4
  63. package/dist/{chunk-4DH54LS7.js → chunk-EFHCTIFT.js} +1 -1
  64. package/dist/{chunk-EOGVWCLF.js → chunk-ESHMWZXX.js} +5 -5
  65. package/dist/{chunk-T6RWREWU.js → chunk-EV47UGDK.js} +1 -1
  66. package/dist/{chunk-UBANQ4EZ.js → chunk-FBLVK2WR.js} +13 -13
  67. package/dist/{chunk-FEC3JUIH.js → chunk-FXQCSU4L.js} +3 -3
  68. package/dist/{chunk-I73UQU7K.js → chunk-GI3PX2JU.js} +1 -1
  69. package/dist/{chunk-SFJOGZQV.js → chunk-GL34UITI.js} +3 -3
  70. package/dist/{chunk-4T25BBBO.js → chunk-GOJO2OLP.js} +4 -4
  71. package/dist/{chunk-32JJ5KV3.js → chunk-GV7FB5L3.js} +4 -4
  72. package/dist/{chunk-3SAF4QXU.js → chunk-H4GOJIOK.js} +4 -4
  73. package/dist/{chunk-QDUDXJAF.js → chunk-HGVUTO67.js} +3 -3
  74. package/dist/{chunk-53D6FFVE.js → chunk-IHT33GZN.js} +1 -1
  75. package/dist/{chunk-QRS7FQHE.js → chunk-INBG473K.js} +8 -2
  76. package/dist/{chunk-E5XYFIYL.js → chunk-J2NPFRDI.js} +6 -6
  77. package/dist/{chunk-DCHZ5JOI.js → chunk-JJPBWBAF.js} +6 -6
  78. package/dist/{chunk-DX65IDBI.js → chunk-JLYVYHD2.js} +1 -1
  79. package/dist/{chunk-DARPCQPY.js → chunk-K2MW33DG.js} +2 -2
  80. package/dist/{chunk-JFVLMWKY.js → chunk-KUI2KGRZ.js} +9 -9
  81. package/dist/{chunk-BGEUAJYL.js → chunk-LD5LSJ2B.js} +2 -2
  82. package/dist/{chunk-JOMG6J64.js → chunk-LIEPMKTX.js} +1 -1
  83. package/dist/chunk-MHUKYYYR.js +239 -0
  84. package/dist/{chunk-I6TIZMM3.js → chunk-MQBQRXTN.js} +5 -5
  85. package/dist/{chunk-DOG3BQFO.js → chunk-MRTGCXG4.js} +6 -6
  86. package/dist/{chunk-EH5OLIP2.js → chunk-NHIEDRZR.js} +236 -20
  87. package/dist/{chunk-PV5XEBFI.js → chunk-NNV2XS3Y.js} +6 -6
  88. package/dist/{chunk-WL3V2UV4.js → chunk-OJ4EEE2B.js} +6 -6
  89. package/dist/{chunk-D4X4BQVQ.js → chunk-OXA2P5NJ.js} +10 -10
  90. package/dist/{chunk-BRG4XS2H.js → chunk-P4IFREOO.js} +1 -1
  91. package/dist/{chunk-MD4UJICD.js → chunk-P7CGVHH5.js} +5 -5
  92. package/dist/{chunk-ZX3KEXO3.js → chunk-PAPUUZOP.js} +626 -164
  93. package/dist/{chunk-PGLNE556.js → chunk-PGZFXNRJ.js} +1 -1
  94. package/dist/{chunk-3PPL64AG.js → chunk-PKHNZTHY.js} +9 -4
  95. package/dist/{chunk-NCTABQMC.js → chunk-PUF3BL6C.js} +1 -1
  96. package/dist/{chunk-EM2RSWDA.js → chunk-PZWC3JXW.js} +5 -5
  97. package/dist/{chunk-2J4IVUEA.js → chunk-PZXQUV3P.js} +4 -4
  98. package/dist/{chunk-7O54GE5C.js → chunk-QBCV27CM.js} +3 -3
  99. package/dist/{chunk-SNUHX5GQ.js → chunk-QGOY3HNV.js} +6 -6
  100. package/dist/{chunk-7FNSQMQ2.js → chunk-QS2IKPQW.js} +4 -4
  101. package/dist/{chunk-CI6MDBQB.js → chunk-QVP6F6BX.js} +7 -7
  102. package/dist/{chunk-CLHRSK5L.js → chunk-RYDT6Y32.js} +4 -4
  103. package/dist/{chunk-HRRGIVIL.js → chunk-S2UHLCF5.js} +3 -3
  104. package/dist/{chunk-R7V6BPEV.js → chunk-SNHAXVCJ.js} +1 -1
  105. package/dist/{chunk-4GWKKUQ5.js → chunk-SUOW4HJB.js} +5 -5
  106. package/dist/{chunk-TPYZFYQ5.js → chunk-TK3XMPG2.js} +4 -4
  107. package/dist/{chunk-XO7LP3JC.js → chunk-UBQ3TG3B.js} +1 -1
  108. package/dist/{chunk-SHW6KK2K.js → chunk-UU2CQFSJ.js} +2 -2
  109. package/dist/{chunk-5EQOUZYE.js → chunk-UUX6Q3CB.js} +2 -2
  110. package/dist/{chunk-SONZCJY5.js → chunk-WFSE7NRM.js} +23 -6
  111. package/dist/{chunk-DUOBFECC.js → chunk-XYNQA6YC.js} +1 -1
  112. package/dist/{chunk-KDUWFGY2.js → chunk-Y5YJLBML.js} +4 -4
  113. package/dist/{chunk-2DB7TBA7.js → chunk-ZZFYV26G.js} +2 -2
  114. package/dist/{codex-app-cards-switch-CN3G7GZW.js → codex-app-cards-switch-LTZCEFYB.js} +11 -11
  115. package/dist/{codex-desktop-launch-GFZXCDSL.js → codex-desktop-launch-DBJBV66U.js} +1 -1
  116. package/dist/{codex-session-launch-4F2UGQYE.js → codex-session-launch-6RTU73OP.js} +18 -18
  117. package/dist/{config-BLHDUDCS.js → config-YREUMA6Z.js} +3 -3
  118. package/dist/{global-upgrade-IZEC22BQ.js → global-upgrade-EYLSJXXN.js} +10 -10
  119. package/dist/{identity-3HGTEP5Q.js → identity-WIM6NVLP.js} +4 -4
  120. package/dist/{live-capabilities-AZK664T7.js → live-capabilities-SU3BEP2Z.js} +5 -5
  121. package/dist/{paths-ROMZUSEX.js → paths-XV3FXWOM.js} +3 -3
  122. package/dist/{reapply-IJYRR4YK.js → reapply-Q7QC3MWL.js} +27 -27
  123. package/dist/src/index.js +29 -28
  124. package/dist/{upgrade-lock-FGEZPX56.js → upgrade-lock-A2ALHHUG.js} +4 -4
  125. package/dist/{usage-HCURE44F.js → usage-UNEATRXG.js} +3 -3
  126. package/package.json +1 -1
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  parseSseMessages
3
- } from "./chunk-32JJ5KV3.js";
3
+ } from "./chunk-GV7FB5L3.js";
4
4
  import {
5
5
  NPX_LATEST_COMMAND
6
- } from "./chunk-UIS332AZ.js";
6
+ } from "./chunk-2OWG667E.js";
7
7
 
8
8
  // src/mcp-stdio-bridge.ts
9
9
  var MCP_BRIDGE_BINARY = "pushary-mcp";
@@ -4,10 +4,10 @@ import {
4
4
  keyPrefix,
5
5
  readConfigFileKey,
6
6
  readMacAppKeyPrefix
7
- } from "./chunk-LIDUPDEY.js";
7
+ } from "./chunk-DYDA77FS.js";
8
8
  import {
9
9
  NPX_LATEST_COMMAND
10
- } from "./chunk-UIS332AZ.js";
10
+ } from "./chunk-2OWG667E.js";
11
11
 
12
12
  // src/key-signed-out.ts
13
13
  var VERDICT_BY_CHECK = {
@@ -19,7 +19,7 @@ var credentialSchema = external_exports.object({
19
19
  baseURL: external_exports.string().url(),
20
20
  keyScope: external_exports.literal("agent")
21
21
  });
22
- var consentSchema = credentialSchema.pick({ siteID: true, machineID: true, baseURL: true }).extend({ enabled: external_exports.literal(true), grantedAtMs: external_exports.number().int().positive() });
22
+ var consentSchema = credentialSchema.pick({ siteID: true, machineID: true, baseURL: true }).extend({ enabled: external_exports.literal(true), grantedAtMs: external_exports.number().int().positive(), includesEarlierConversation: external_exports.boolean().optional() });
23
23
  var readPositionSchema = external_exports.object({
24
24
  identity: external_exports.string().nullable(),
25
25
  offset: external_exports.number().int().nonnegative(),
@@ -58,7 +58,7 @@ var nativeTranscriptGrant = (home = homedir()) => {
58
58
  if (!account) return null;
59
59
  const consent = consentSchema.parse(ownerDocument(nativeConsentFile(home)));
60
60
  if (account.siteID !== consent.siteID || account.machineID !== consent.machineID || account.baseURL !== consent.baseURL) return null;
61
- return { account, grantedAtMs: consent.grantedAtMs };
61
+ return { account, grantedAtMs: consent.grantedAtMs, includesEarlierConversation: consent.includesEarlierConversation === true };
62
62
  } catch {
63
63
  return null;
64
64
  }
@@ -70,5 +70,6 @@ var macAppHistoryDecision = (home = homedir()) => {
70
70
 
71
71
  export {
72
72
  nativeBridgeAccount,
73
+ nativeTranscriptGrant,
73
74
  macAppHistoryDecision
74
75
  };
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  CODEX_HOOK_STATUS
3
- } from "./chunk-SHW6KK2K.js";
3
+ } from "./chunk-UU2CQFSJ.js";
4
4
  import {
5
5
  retryAgentCommand
6
- } from "./chunk-7O54GE5C.js";
6
+ } from "./chunk-QBCV27CM.js";
7
7
  import {
8
8
  NPX_LATEST_COMMAND
9
- } from "./chunk-UIS332AZ.js";
9
+ } from "./chunk-2OWG667E.js";
10
10
 
11
11
  // src/setup/codex-trust.ts
12
12
  var CODEX_TRUST_TELEMETRY = {
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  readExistingMachineId
3
- } from "./chunk-BGEUAJYL.js";
3
+ } from "./chunk-LD5LSJ2B.js";
4
4
  import {
5
5
  getBaseUrl
6
- } from "./chunk-53D6FFVE.js";
6
+ } from "./chunk-IHT33GZN.js";
7
7
  import {
8
8
  NetworkError
9
9
  } from "./chunk-PPY5N2E6.js";
@@ -13,7 +13,7 @@ import {
13
13
  import {
14
14
  clientContextHeaders,
15
15
  redactSecretsFromContent
16
- } from "./chunk-QRS7FQHE.js";
16
+ } from "./chunk-INBG473K.js";
17
17
 
18
18
  // src/cli/net.ts
19
19
  import { createRequire } from "module";
@@ -5,19 +5,19 @@ import {
5
5
  hermesAppWiringRecord,
6
6
  hermesConfigReceipt,
7
7
  hermesSetupReceipt
8
- } from "./chunk-2J4IVUEA.js";
8
+ } from "./chunk-PZXQUV3P.js";
9
9
  import {
10
10
  readJsonSafe,
11
11
  resolvePathBinary,
12
12
  writeJsonAtomic
13
- } from "./chunk-AUDYAVNM.js";
13
+ } from "./chunk-ABAWD6QM.js";
14
14
  import {
15
15
  isNewerVersion,
16
16
  isReleaseVersion
17
17
  } from "./chunk-QTOLNRET.js";
18
18
  import {
19
19
  isRecord
20
- } from "./chunk-QRS7FQHE.js";
20
+ } from "./chunk-INBG473K.js";
21
21
 
22
22
  // src/hermes/plugin-payload.ts
23
23
  import { createHash } from "crypto";
@@ -30,7 +30,7 @@ import os
30
30
  import threading
31
31
  from pathlib import Path
32
32
 
33
- from . import api, approval, host, identity, installation, schemas, tools
33
+ from . import api, approval, history, host, identity, installation, schemas, tools
34
34
 
35
35
  logger = logging.getLogger(__name__)
36
36
 
@@ -332,6 +332,7 @@ def _on_post_tool_call(tool_name, args, result, duration_ms=0, status=None, **kw
332
332
 
333
333
  def _on_session_start(session_id=None, **kwargs):
334
334
  identity.remember_session(session_id)
335
+ history.start_history(session_id, kwargs.get("parent_session_id"))
335
336
  _session_totals.update(tools=0, errors=0)
336
337
  _last_turn.update(completed=False, interrupted=False)
337
338
  _report_session_event("session_start", session_id, can_drain=True)
@@ -358,6 +359,7 @@ def _report_plugin_active_once(session_id):
358
359
 
359
360
  def _on_pre_llm_call(session_id=None, **kwargs):
360
361
  identity.remember_session(session_id)
362
+ history.start_history(session_id, kwargs.get("parent_session_id"))
361
363
  if not api.is_configured() or not identity.session_id():
362
364
  return None
363
365
  result = api.agent_event("user_prompt", identity.session_id(), can_drain=True)
@@ -402,6 +404,7 @@ def _on_session_end(session_id=None, completed=False, interrupted=False, failed=
402
404
 
403
405
 
404
406
  def _on_session_finalize(session_id=None, **kwargs):
407
+ history.stop_history(session_id or identity.session_id())
405
408
  _report_session_event("session_closed", session_id or identity.session_id())
406
409
  tool_count = _session_totals["tools"]
407
410
  error_count = _session_totals["errors"]
@@ -932,6 +935,261 @@ def get_end_user_decision(decision_id, wait_seconds=45):
932
935
  return _rest_call("GET", f"/decisions/{seg}", params={"wait": wait_seconds})
933
936
  ` },
934
937
  { path: "approval.py", contents: 'import time\n\nfrom . import api, identity\n\nHERMES_SHELL_TOOL = "terminal"\nDECISION_LINE_MAX = 500\nACTION_BODY_MAX = 4000\n\nPOLL_CEILING_SECONDS = 50.0\n\nCHOICE_LABELS = (\n ("once", "Allow once"),\n ("session", "Allow for this session"),\n ("always", "Always allow"),\n ("deny", "Deny"),\n)\n\n\nclass TransportUnavailable(Exception):\n pass\n\n\nQUESTION_COMMAND_MAX = 160\n\nPLUGIN_RULE_MARKER = "(plugin approval rule)"\n\n\ndef _offered(allowed):\n return [pair for pair in CHOICE_LABELS if pair[0] in set(allowed or ())]\n\n\ndef _command_head(command):\n return " ".join(str(command or "").split()[:2])[:80]\n\n\ndef _command_summary(command):\n collapsed = " ".join(str(command or "").split())\n if len(collapsed) <= QUESTION_COMMAND_MAX:\n return collapsed\n return collapsed[:QUESTION_COMMAND_MAX - 1] + "\u2026"\n\n\ndef _choice_from_answer(value, offered, uses_select):\n answer = str(value or "").strip()\n if uses_select:\n for choice, label in offered:\n if answer.lower() == label.lower():\n return choice\n return "deny"\n return "once" if api.is_affirmative(answer) else "deny"\n\n\ndef _subject(request):\n """What to put in the question.\n\n A dangerous-command approval carries the real command. A plugin rule\n escalated from pre_tool_call carries a synthetic display label instead, and\n the plugin\'s own message is the part worth reading, so use that.\n """\n command = str(request.command or "")\n if PLUGIN_RULE_MARKER in command:\n lines = str(request.description or "").strip().splitlines()\n if lines and lines[0].strip():\n return _command_summary(lines[0])\n return _command_summary(command)\n\n\ndef _open_question(request, offered, uses_select):\n command = str(request.command or "")\n return api.ask_user(\n question=f"Allow Hermes to run `{_subject(request)}`?",\n question_type="select" if uses_select else "confirm",\n options=[label for _, label in offered] if uses_select else None,\n context=str(request.description or "")[:500] or None,\n agent_name=identity.agent_name(),\n session_id=identity.session_id(),\n machine_id=identity.machine_id(),\n tool_name=HERMES_SHELL_TOOL,\n tool_target=_command_head(command),\n action=command[:DECISION_LINE_MAX] or None,\n action_body=command[:ACTION_BODY_MAX] or None,\n blocker=(\n f"Hermes flagged this command as {request.pattern_key}"[:DECISION_LINE_MAX]\n if request.pattern_key\n else None\n ),\n wait=False,\n )\n\n\ndef _stopped(result):\n return result.get("handoffAction") == "stop" or result.get("status") in ("cancelled", "unavailable", "stopped")\n\n\ndef _withdraw(correlation_id):\n try:\n cancelled = api.cancel_question(correlation_id)\n if _stopped(cancelled) or cancelled.get("error"):\n return {"handoffAction": "stop"}\n if cancelled.get("cancelled") is True:\n return {}\n answer = api.wait_for_answer(correlation_id, 1000)\n if answer.get("answered") or answer.get("status") in ("expired", "missing"):\n return answer\n except Exception:\n pass\n return {"handoffAction": "stop"}\n\n\ndef present(request):\n offered = _offered(request.allowed_choices)\n if not offered:\n return request.respond("deny")\n uses_select = [choice for choice, _ in offered] != ["once", "deny"]\n # Reserve time for withdrawal before the host discards a late decision.\n started_at = time.monotonic()\n deadline = started_at + max(float(request.timeout_seconds or 0) - 10, 0.0)\n created = _open_question(request, offered, uses_select)\n if created.get("answered"):\n return request.respond(_choice_from_answer(created.get("value"), offered, uses_select))\n if _stopped(created):\n return request.respond("deny")\n correlation_id = created.get("correlationId")\n if not correlation_id:\n raise TransportUnavailable("Pushary did not create an approval")\n\n policy_deadline = None\n window_ms = created.get("policyTimeoutMs")\n if created.get("deliveryMode") == "push_first" and isinstance(window_ms, (int, float)) and window_ms >= 0:\n policy_deadline = started_at + window_ms / 1000\n deadline = min(deadline, policy_deadline)\n handoff = created.get("noDevices") or created.get("suppressed") or created.get("status") in ("terminal", "notified")\n answer = created\n try:\n while not handoff and time.monotonic() < deadline:\n remaining = deadline - time.monotonic()\n answer = api.wait_for_answer(correlation_id, int(min(POLL_CEILING_SECONDS, max(1.0, remaining)) * 1000))\n if answer.get("answered"):\n return request.respond(_choice_from_answer(answer.get("value"), offered, uses_select))\n if _stopped(answer) or answer.get("error") or answer.get("status") in ("expired", "missing"):\n break\n # Some hosts/proxies return pending immediately instead of long-polling.\n time.sleep(min(0.1, max(0, deadline - time.monotonic())))\n except Exception:\n answer = {"handoffAction": "stop"}\n\n late = _withdraw(correlation_id)\n if _stopped(answer) or _stopped(late):\n return request.respond("deny")\n if late.get("answered"):\n return request.respond(_choice_from_answer(late.get("value"), offered, uses_select))\n if handoff or (policy_deadline is not None and time.monotonic() >= policy_deadline):\n # Only a fenced question may reach Hermes\'s configured builtin fallback.\n raise TransportUnavailable("Approval handed back to Hermes")\n return request.respond("deny")\n' },
938
+ { path: "history.py", contents: `from __future__ import annotations
939
+
940
+ import hashlib
941
+ import contextvars
942
+ import json
943
+ import logging
944
+ import math
945
+ import os
946
+ import re
947
+ import shutil
948
+ import subprocess
949
+ import tempfile
950
+ import threading
951
+ import time
952
+ from datetime import datetime, timezone
953
+ from pathlib import Path
954
+
955
+ from . import api
956
+
957
+ logger = logging.getLogger(__name__)
958
+ MAX_MESSAGES = 5000
959
+ MAX_SOURCE_BYTES = 20_000_000
960
+ POLL_SECONDS = 5
961
+ _workers: dict[tuple[str, str], tuple[threading.Event, threading.Thread]] = {}
962
+ _lock = threading.Lock()
963
+
964
+
965
+ def _text_blocks(content: object) -> list[dict[str, object]]:
966
+ if isinstance(content, str):
967
+ return [{"type": "text", "text": content}]
968
+ if not isinstance(content, list):
969
+ return []
970
+ return [{"type": "text", "text": block["text"]} for block in content
971
+ if isinstance(block, dict) and block.get("type") in ("text", "input_text", "output_text")
972
+ and isinstance(block.get("text"), str)]
973
+
974
+
975
+ def _tool_blocks(calls: object) -> list[dict[str, object]]:
976
+ blocks = []
977
+ if not isinstance(calls, list):
978
+ return blocks
979
+ for call in calls:
980
+ if not isinstance(call, dict) or not isinstance(call.get("id"), str):
981
+ continue
982
+ function = call.get("function")
983
+ if not isinstance(function, dict) or not isinstance(function.get("name"), str):
984
+ continue
985
+ arguments = function.get("arguments", {})
986
+ if isinstance(arguments, str):
987
+ arguments = json.loads(arguments)
988
+ if not isinstance(arguments, dict):
989
+ raise ValueError("Invalid Hermes tool arguments")
990
+ blocks.append({"type": "tool_use", "id": call["id"], "name": function["name"], "input": arguments})
991
+ return blocks
992
+
993
+
994
+ def normalize_messages(rows: list[object]) -> list[dict[str, object]]:
995
+ messages = []
996
+ for row in rows:
997
+ if not isinstance(row, dict):
998
+ raise ValueError("Invalid Hermes message")
999
+ if not (row.get("active") or row.get("compacted")) or row.get("display_kind") in ("hidden", "model_only"):
1000
+ continue
1001
+ role = row.get("role")
1002
+ if role not in ("user", "assistant", "tool") or row.get("_compressed_summary"):
1003
+ continue
1004
+ identity = row.get("id")
1005
+ stamp = row.get("timestamp")
1006
+ if not isinstance(identity, int) or isinstance(identity, bool) or identity <= 0:
1007
+ raise ValueError("Invalid Hermes message identity")
1008
+ if not isinstance(stamp, (int, float)) or not math.isfinite(stamp) or stamp <= 0:
1009
+ raise ValueError("Invalid Hermes message timestamp")
1010
+ content = _text_blocks(row.get("content"))
1011
+ if role == "assistant":
1012
+ content.extend(_tool_blocks(row.get("tool_calls")))
1013
+ elif role == "tool":
1014
+ call_id = row.get("tool_call_id")
1015
+ if not isinstance(call_id, str) or not call_id:
1016
+ continue
1017
+ content = [{"type": "tool_result", "tool_use_id": call_id, "content": content}]
1018
+ messages.append({
1019
+ "id": f"hermes:{identity}", "role": "user" if role == "tool" else role,
1020
+ "timestamp": datetime.fromtimestamp(stamp, timezone.utc).isoformat().replace("+00:00", "Z"),
1021
+ "content": content,
1022
+ })
1023
+ return messages
1024
+
1025
+
1026
+ def read_display_history(database_path: Path, session_id: str) -> list[dict[str, object]]:
1027
+ from hermes_state import SessionDB
1028
+
1029
+ database = SessionDB(db_path=database_path, read_only=True)
1030
+ try:
1031
+ connection = database._conn
1032
+ deadline = time.monotonic() + 2
1033
+ connection.set_progress_handler(lambda: int(time.monotonic() > deadline), 1000)
1034
+ connection.execute("BEGIN")
1035
+ if database.get_session(session_id) is None:
1036
+ raise ValueError("Hermes session is not in this profile")
1037
+ lineage = database._resume_lineage_ids(session_id)
1038
+ if not lineage or len(lineage) > 100:
1039
+ raise ValueError("Unsupported Hermes compression lineage")
1040
+ columns = [row[1] for row in connection.execute("PRAGMA table_info(messages)")]
1041
+ required = {"id", "session_id", "role", "content", "timestamp", "active", "compacted", "display_kind", "message_uid"}
1042
+ if not required.issubset(columns):
1043
+ raise ValueError("Unsupported Hermes history schema")
1044
+ identifiers = ['"' + column.replace('"', '""') + '"' for column in columns]
1045
+ row_bytes = " + ".join(f"COALESCE(length(CAST({column} AS BLOB)), 0)" for column in identifiers)
1046
+ placeholders = ",".join("?" for _ in lineage)
1047
+ count, size = connection.execute(
1048
+ f"SELECT COUNT(*), SUM({row_bytes}) FROM messages WHERE session_id IN ({placeholders})", lineage
1049
+ ).fetchone()
1050
+ if count > MAX_MESSAGES or (size or 0) > MAX_SOURCE_BYTES:
1051
+ raise ValueError("Hermes history exceeds the bounded reader limit")
1052
+ rows = database.get_messages(session_id, include_compacted=True, include_ancestors=True, limit=MAX_MESSAGES)
1053
+ return normalize_messages(rows)
1054
+ finally:
1055
+ database.close()
1056
+
1057
+
1058
+ def write_snapshot(path: Path, session_id: str, messages: list[dict[str, object]], error: str | None = None, completed: bool = False) -> None:
1059
+ value = {"version": 1, "sessionId": session_id, "updatedAtMs": int(time.time() * 1000), "messages": messages, "completed": completed}
1060
+ if error:
1061
+ value["error"] = error
1062
+ encoded = json.dumps(value, ensure_ascii=True, allow_nan=False).encode("utf-8")
1063
+ if len(encoded) > MAX_SOURCE_BYTES:
1064
+ raise ValueError("Hermes history export exceeds its size limit")
1065
+ path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
1066
+ descriptor, temporary = tempfile.mkstemp(dir=path.parent, prefix=".history-")
1067
+ try:
1068
+ with os.fdopen(descriptor, "wb") as handle:
1069
+ handle.write(encoded)
1070
+ handle.flush()
1071
+ os.fsync(handle.fileno())
1072
+ os.replace(temporary, path)
1073
+ finally:
1074
+ Path(temporary).unlink(missing_ok=True)
1075
+
1076
+
1077
+ def _registration_command(key: str) -> list[str] | None:
1078
+ root = Path.home() / ".pushary"
1079
+ try:
1080
+ if (root / "native-transcript-consent.json").exists():
1081
+ bridge = (root / "bin" / "pushary-bridge").resolve(strict=True)
1082
+ helper = str(bridge.parent / "pushary-native-transcripts")
1083
+ command = [helper, "register", "hermes"]
1084
+ else:
1085
+ helper = shutil.which("pushary-transcript-register")
1086
+ if helper is None:
1087
+ return None
1088
+ command = [helper, "hermes"]
1089
+ result = subprocess.run([helper, "authorize-export"],
1090
+ input=json.dumps({"apiKey": key, "baseUrl": api.BASE_URL}), text=True,
1091
+ stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, timeout=5, check=False,
1092
+ env={**os.environ, "PUSHARY_API_KEY": key, "PUSHARY_BASE_URL": api.BASE_URL})
1093
+ if result.returncode != 0:
1094
+ return None
1095
+ authorization = json.loads(result.stdout)
1096
+ if not isinstance(authorization, dict) or type(authorization.get("version")) is not int or authorization.get("version") != 1 or authorization.get("authorized") is not True:
1097
+ return None
1098
+ return command
1099
+ except (OSError, ValueError, subprocess.TimeoutExpired):
1100
+ return None
1101
+
1102
+
1103
+ def _export_loop(database_path: Path, session_id: str, key: str, command: list[str], stop: threading.Event) -> None:
1104
+ binding = hashlib.sha256((api.BASE_URL.rstrip("/") + "\\0" + key + "\\0" + str(database_path)).encode()).hexdigest()
1105
+ path = Path.home() / ".pushary" / "hermes-history" / binding / f"{session_id}.json"
1106
+ next_registration = 0
1107
+ warned = False
1108
+ revoked = False
1109
+ try:
1110
+ while True:
1111
+ if api._get_api_key() != key or _registration_command(key) != command:
1112
+ revoked = True
1113
+ break
1114
+ ending = stop.is_set()
1115
+ try:
1116
+ messages = read_display_history(database_path, session_id)
1117
+ write_snapshot(path, session_id, messages, completed=ending)
1118
+ warned = False
1119
+ except Exception:
1120
+ if not warned:
1121
+ logger.warning("[pushary] Hermes history source is unavailable; existing phone history is preserved")
1122
+ warned = True
1123
+ write_snapshot(path, session_id, [], error="source_unavailable")
1124
+ if ending or time.monotonic() >= next_registration:
1125
+ payload = {"session_id": session_id, "cwd": str(database_path.parent), "transcript_path": str(path),
1126
+ "hook_event_name": "SessionEnd" if ending else "SessionStart"}
1127
+ environment = {**os.environ, "PUSHARY_API_KEY": key, "PUSHARY_BASE_URL": api.BASE_URL}
1128
+ result = subprocess.run(command, input=json.dumps(payload), text=True, env=environment,
1129
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=5, check=False)
1130
+ if result.returncode != 0:
1131
+ logger.warning("[pushary] Hermes history reader could not register")
1132
+ next_registration = time.monotonic() + 60
1133
+ if ending:
1134
+ break
1135
+ stop.wait(POLL_SECONDS)
1136
+ except Exception:
1137
+ logger.warning("[pushary] Hermes history reader stopped; reopen the session to retry")
1138
+ finally:
1139
+ if revoked:
1140
+ path.unlink(missing_ok=True)
1141
+ with _lock:
1142
+ _workers.pop((str(database_path), session_id), None)
1143
+
1144
+
1145
+ def start_history(session_id: object, parent_session_id: object = None) -> None:
1146
+ if parent_session_id or not isinstance(session_id, str) or not re.fullmatch(r"[A-Za-z0-9_-]{1,128}", session_id):
1147
+ return
1148
+ try:
1149
+ from hermes_constants import get_hermes_home
1150
+
1151
+ key = api._get_api_key()
1152
+ command = _registration_command(key)
1153
+ if command is None:
1154
+ return
1155
+ database_path = (get_hermes_home() / "state.db").resolve()
1156
+ identity = (str(database_path), session_id)
1157
+ with _lock:
1158
+ if identity in _workers or len(_workers) >= 32:
1159
+ return
1160
+ stop = threading.Event()
1161
+ context = contextvars.copy_context()
1162
+ thread = threading.Thread(target=context.run, args=(_export_loop, database_path, session_id, key, command, stop),
1163
+ name="pushary-history", daemon=True)
1164
+ with _lock:
1165
+ if identity in _workers or len(_workers) >= 32:
1166
+ return
1167
+ _workers[identity] = (stop, thread)
1168
+ try:
1169
+ thread.start()
1170
+ except Exception:
1171
+ with _lock:
1172
+ _workers.pop(identity, None)
1173
+ raise
1174
+ except Exception:
1175
+ logger.warning("[pushary] Hermes history reader could not start")
1176
+
1177
+
1178
+ def stop_history(session_id: str) -> None:
1179
+ try:
1180
+ from hermes_constants import get_hermes_home
1181
+ database_path = str((get_hermes_home() / "state.db").resolve())
1182
+ except Exception:
1183
+ return
1184
+ with _lock:
1185
+ worker = _workers.get((database_path, session_id))
1186
+ if worker:
1187
+ stop, thread = worker
1188
+ stop.set()
1189
+ thread.join(timeout=8)
1190
+ if thread.is_alive():
1191
+ logger.warning("[pushary] Hermes final history export is still pending")
1192
+ ` },
935
1193
  { path: "host.py", contents: 'import os\n\nTRANSPORT_NAME = "pushary"\n\nHOOK_TIMEOUT_DEFAULT_SECONDS = 30.0\nHOOK_TIMEOUT_MAX_SECONDS = 600.0\n\nGATE_HEADROOM_SECONDS = 5.0\nGATE_MIN_SECONDS = 5.0\nGATE_MAX_SECONDS = 55.0\n\n# The last Hermes that runs a pre_tool_call callback to completion. Anything\n# after this abandons one that outlives plugins.hook_callback_timeout.\nUNBOUNDED_THROUGH_VERSION = (0, 20, 5)\n\n\ndef _config():\n try:\n from hermes_cli.config import load_config_readonly\n\n loaded = load_config_readonly()\n except Exception:\n return {}\n return loaded if isinstance(loaded, dict) else {}\n\n\ndef _section(node, *path):\n for key in path:\n node = node.get(key) if isinstance(node, dict) else None\n if node is None:\n return {}\n return node if isinstance(node, dict) else {}\n\n\ndef profile_secret(name):\n try:\n from agent.secret_scope import get_secret\n except Exception:\n return os.environ.get(name) or None\n try:\n return get_secret(name) or None\n except Exception:\n return None\n\n\ndef hermes_version_label():\n try:\n from importlib.metadata import version\n\n return version("hermes-cli")\n except Exception:\n try:\n import hermes_cli\n\n label = getattr(hermes_cli, "__version__", None)\n except Exception:\n return None\n return str(label) if label else None\n\n\ndef _hermes_version():\n """This Hermes as a comparable tuple, or None when it will not say."""\n raw = hermes_version_label()\n if not raw:\n return None\n parts = []\n for piece in str(raw).split(".")[:3]:\n digits = ""\n for char in piece:\n if not char.isdigit():\n break\n digits += char\n if not digits:\n return None\n parts.append(int(digits))\n while len(parts) < 3:\n parts.append(0)\n return tuple(parts)\n\n\ndef bounds_pre_tool_call():\n """Whether this Hermes abandons a slow pre_tool_call callback.\n\n The timeout was added after 0.20.5, which runs plugin hooks to completion.\n Clamping the wait on a host that would never have cut it off just shortens\n the window the user has to answer, for nothing.\n\n Unreadable means assume bounded. A wait that is shorter than it had to be\n costs the user seconds; a wait longer than the host allows costs them the\n answer they already gave.\n\n That rule is why the private symbol is not asked on its own. `hasattr` on\n `_HOOK_TIMEOUT_FAIL_CLOSED_HOOKS` cannot tell "this host runs hooks to\n completion" apart from "the private name moved", and those two answers want\n opposite behaviour: the first earns the full window, the second spends 55\n seconds against a budget of 30 and loses an answer the user already gave.\n A rename is the likelier of the two, because a private name is the one thing\n a host is free to change. So the symbol is taken as proof of bounded, and\n only a version this side of the timeout landing is taken as proof of the\n other. Anything unreadable falls to bounded.\n """\n try:\n from hermes_cli import plugins\n except Exception:\n return True\n if hasattr(plugins, "_HOOK_TIMEOUT_FAIL_CLOSED_HOOKS"):\n return True\n version = _hermes_version()\n if version is None:\n return True\n return version > UNBOUNDED_THROUGH_VERSION\n\n\ndef hook_callback_timeout_seconds():\n raw = _section(_config(), "plugins").get("hook_callback_timeout")\n if raw is None:\n return HOOK_TIMEOUT_DEFAULT_SECONDS\n try:\n timeout = float(raw)\n except (TypeError, ValueError):\n return HOOK_TIMEOUT_DEFAULT_SECONDS\n if timeout < 0:\n return HOOK_TIMEOUT_DEFAULT_SECONDS\n return min(timeout, HOOK_TIMEOUT_MAX_SECONDS)\n\n\ndef gate_budget_seconds(requested_seconds):\n if not bounds_pre_tool_call():\n return max(1.0, min(float(requested_seconds), GATE_MAX_SECONDS))\n timeout = hook_callback_timeout_seconds()\n ceiling = (\n GATE_MAX_SECONDS\n if timeout <= 0\n else max(GATE_MIN_SECONDS, timeout - GATE_HEADROOM_SECONDS)\n )\n return max(1.0, min(float(requested_seconds), ceiling, GATE_MAX_SECONDS))\n\n\ndef pushary_is_selected_transport():\n selected = _section(_config(), "security", "approval").get("transport")\n return str(selected or "").strip().lower() == TRANSPORT_NAME\n' },
936
1194
  { path: "identity.py", contents: 'import hashlib\nimport os\nimport re\nimport socket\nfrom pathlib import Path\n\nDEFAULT_AGENT_NAME = "Hermes"\nMAX_ID_LENGTH = 128\nSHARED_MACHINE_ID_FILE = Path.home() / ".pushary" / "machine-id"\nSHARED_MACHINE_ID_PATTERN = re.compile(r"^[A-Za-z0-9-]{1,128}$")\n\n_session_id = None\n\n\ndef agent_name():\n return os.environ.get("PUSHARY_AGENT_NAME", DEFAULT_AGENT_NAME)\n\n\ndef labelled_agent_name(requested):\n project = str(requested or "").partition(" - ")[2].strip()\n return f"{agent_name()} - {project}" if project else agent_name()\n\n\ndef shared_machine_id(path=None):\n try:\n value = (path or SHARED_MACHINE_ID_FILE).read_text(encoding="utf-8").strip()\n except OSError:\n return None\n return value if SHARED_MACHINE_ID_PATTERN.match(value) else None\n\n\ndef hostname_machine_id():\n try:\n host = socket.gethostname()\n except Exception:\n return None\n if not host:\n return None\n return hashlib.sha256(host.encode("utf-8")).hexdigest()[:8]\n\n\ndef machine_id():\n return shared_machine_id() or hostname_machine_id()\n\n\ndef remember_session(value):\n global _session_id\n if value:\n _session_id = str(value)[:MAX_ID_LENGTH]\n\n\ndef forget_session():\n global _session_id\n _session_id = None\n\n\ndef session_id():\n return _session_id\n' },
937
1195
  { path: "installation.py", contents: `import json
@@ -1063,7 +1321,7 @@ def record_plugin_load(approval_transport_registered, home=None, pid=None, now=N
1063
1321
  logger.debug("[pushary] could not record the plugin load: %s", exc)
1064
1322
  return None
1065
1323
  ` },
1066
- { path: "plugin.yaml", contents: "name: pushary\nversion: 0.8.1\ndescription: Push notifications and human-in-the-loop for Hermes Agent via Pushary\nauthor: RalphNex OU\nrequires_env:\n - name: PUSHARY_API_KEY\n description: Your Pushary API key\n url: https://pushary.com/sign-up?from=hermes\n secret: true\nprovides_tools:\n - pushary_remind\n - pushary_notify\n - pushary_ask\n - pushary_wait\n - pushary_cancel\n - pushary_propose_scope\n - pushary_enroll\n - pushary_ask_end_user\nprovides_hooks:\n - pre_llm_call\n - pre_tool_call\n - post_tool_call\n - on_session_start\n - on_session_end\n - on_session_finalize\nprovides_approval_transports:\n - pushary\n" },
1324
+ { path: "plugin.yaml", contents: "name: pushary\nversion: 0.9.0\ndescription: Push notifications and human-in-the-loop for Hermes Agent via Pushary\nauthor: RalphNex OU\nrequires_env:\n - name: PUSHARY_API_KEY\n description: Your Pushary API key\n url: https://pushary.com/sign-up?from=hermes\n secret: true\nprovides_tools:\n - pushary_remind\n - pushary_notify\n - pushary_ask\n - pushary_wait\n - pushary_cancel\n - pushary_propose_scope\n - pushary_enroll\n - pushary_ask_end_user\nprovides_hooks:\n - pre_llm_call\n - pre_tool_call\n - post_tool_call\n - on_session_start\n - on_session_end\n - on_session_finalize\nprovides_approval_transports:\n - pushary\n" },
1067
1325
  { path: "schemas.py", contents: `PUSHARY_REMIND = {
1068
1326
  "name": "pushary_remind",
1069
1327
  "description": "Schedule a one-time personal reminder on Pushary, even after Hermes exits. Use body plus in_minutes or at (ISO timestamp with explicit UTC offset, within 30 days). Never guess the user's timezone. Confirm the returned time. No arguments lists pending reminders; cancel_reminder_id cancels a scheduled one. Use Hermes cron for agent work; use this for reminding the operator.",
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isSafeReadOnlyCommand
3
- } from "./chunk-QRS7FQHE.js";
3
+ } from "./chunk-INBG473K.js";
4
4
 
5
5
  // src/ledger.ts
6
6
  import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync } from "fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SETUP_FLAGS
3
- } from "./chunk-NCTABQMC.js";
3
+ } from "./chunk-PUF3BL6C.js";
4
4
  import {
5
5
  parseFlags
6
6
  } from "./chunk-NMUI3ZWG.js";
@@ -9,7 +9,7 @@ import {
9
9
  } from "./chunk-PPY5N2E6.js";
10
10
  import {
11
11
  NPX_LATEST_COMMAND
12
- } from "./chunk-UIS332AZ.js";
12
+ } from "./chunk-2OWG667E.js";
13
13
  import {
14
14
  CLAUDE_CONNECTOR_GUIDE,
15
15
  CLAUDE_CONNECTOR_STEPS,
@@ -17,7 +17,7 @@ import {
17
17
  CLAUDE_CONNECTOR_URL,
18
18
  COWORK_CONNECTOR_LIMIT,
19
19
  COWORK_STANDING_INSTRUCTIONS
20
- } from "./chunk-QRS7FQHE.js";
20
+ } from "./chunk-INBG473K.js";
21
21
 
22
22
  // src/cli/cowork.ts
23
23
  import { readFileSync } from "fs";
@@ -443,9 +443,9 @@ var installGlobally = (options) => {
443
443
  }
444
444
  const started = (async () => {
445
445
  if (!npmIsOnPath()) throw new NpmMissingError();
446
- const { withUpgradeLock } = await import("./global-upgrade-IZEC22BQ.js");
447
- const { installWithUpgradeLock, ownsUpgradeLock } = await import("./upgrade-lock-FGEZPX56.js");
448
- const { autoUpdateLock } = await import("./paths-ROMZUSEX.js");
446
+ const { withUpgradeLock } = await import("./global-upgrade-EYLSJXXN.js");
447
+ const { installWithUpgradeLock, ownsUpgradeLock } = await import("./upgrade-lock-A2ALHHUG.js");
448
+ const { autoUpdateLock } = await import("./paths-XV3FXWOM.js");
449
449
  let result = "skipped";
450
450
  const install = async () => {
451
451
  const platform = process.platform;
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  autoUpdateLock
3
- } from "./chunk-2J4IVUEA.js";
3
+ } from "./chunk-PZXQUV3P.js";
4
4
  import {
5
5
  installPackageGlobally,
6
6
  npmErrorMessage,
7
7
  writeJsonAtomic
8
- } from "./chunk-AUDYAVNM.js";
8
+ } from "./chunk-ABAWD6QM.js";
9
9
 
10
10
  // src/upgrade-lock.ts
11
11
  import { fork } from "child_process";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  readTranscriptPreference
3
- } from "./chunk-LIDUPDEY.js";
3
+ } from "./chunk-DYDA77FS.js";
4
4
 
5
5
  // src/transcript-setting.ts
6
6
  var transcriptEnvironmentPreference = () => {
@@ -11,7 +11,7 @@ var transcriptEnvironmentPreference = () => {
11
11
  return null;
12
12
  };
13
13
  var configuredPreference = () => transcriptEnvironmentPreference() ?? readTranscriptPreference();
14
- var transcriptsEnabled = () => configuredPreference() ?? false;
14
+ var transcriptsEnabled = () => configuredPreference() ?? true;
15
15
  var transcriptPreferenceForSetup = (explicit = null) => explicit ?? configuredPreference() ?? true;
16
16
 
17
17
  export {
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  pusharyFetch
3
- } from "./chunk-SOHJ37NA.js";
3
+ } from "./chunk-7AQEWD2J.js";
4
4
  import {
5
5
  getBaseUrl
6
- } from "./chunk-53D6FFVE.js";
6
+ } from "./chunk-IHT33GZN.js";
7
7
  import {
8
8
  NetworkError
9
9
  } from "./chunk-PPY5N2E6.js";
@@ -13,7 +13,7 @@ import {
13
13
  import {
14
14
  isValidApiKey,
15
15
  normalizeKeyScope
16
- } from "./chunk-QRS7FQHE.js";
16
+ } from "./chunk-INBG473K.js";
17
17
 
18
18
  // src/session.ts
19
19
  var samePusharyAccount = (left, right) => left.kind === "ok" && right.kind === "ok" && left.identity.workspaceId !== "" && left.identity.workspaceId === right.identity.workspaceId && left.identity.siteId === right.identity.siteId;
@@ -2,31 +2,31 @@ import {
2
2
  SIGNED_OUT_ON_MAC_MESSAGE,
3
3
  describeSignedOutKey,
4
4
  readSignedOutDiagnosis
5
- } from "./chunk-HXV2SY2V.js";
5
+ } from "./chunk-4ZXFOT44.js";
6
6
  import {
7
7
  APPROVAL_RULES_PATH
8
- } from "./chunk-2DB7TBA7.js";
8
+ } from "./chunk-ZZFYV26G.js";
9
9
  import {
10
10
  createIo
11
11
  } from "./chunk-FO3MDD26.js";
12
12
  import {
13
13
  readKeySource
14
- } from "./chunk-LIDUPDEY.js";
14
+ } from "./chunk-DYDA77FS.js";
15
15
  import {
16
16
  absoluteUrl,
17
17
  checkApiKey,
18
18
  describeKeyCheck,
19
19
  keyCheckFromResponse
20
- } from "./chunk-SIJHPGUW.js";
20
+ } from "./chunk-CKUFXQUJ.js";
21
21
  import {
22
22
  getBaseUrl
23
- } from "./chunk-53D6FFVE.js";
23
+ } from "./chunk-IHT33GZN.js";
24
24
  import {
25
25
  EXIT
26
26
  } from "./chunk-KZERVKTD.js";
27
27
  import {
28
28
  KEY_SCOPE
29
- } from "./chunk-QRS7FQHE.js";
29
+ } from "./chunk-INBG473K.js";
30
30
 
31
31
  // src/cli/report-key.ts
32
32
  var { dim, yellow } = createIo();
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-KZERVKTD.js";
8
8
  import {
9
9
  redactSecretsFromContent
10
- } from "./chunk-QRS7FQHE.js";
10
+ } from "./chunk-INBG473K.js";
11
11
 
12
12
  // src/cli/json.ts
13
13
  var writeStdoutFully = (text) => new Promise((resolve, reject) => {
@@ -3,11 +3,11 @@ import {
3
3
  } from "./chunk-POFBE6ZY.js";
4
4
  import {
5
5
  autoUpdateState
6
- } from "./chunk-2J4IVUEA.js";
6
+ } from "./chunk-PZXQUV3P.js";
7
7
  import {
8
8
  readJsonSafe,
9
9
  writeJsonAtomic
10
- } from "./chunk-AUDYAVNM.js";
10
+ } from "./chunk-ABAWD6QM.js";
11
11
  import {
12
12
  CLI_PACKAGE_NAMES,
13
13
  POSIX_GLOBAL_ROOT_SEGMENTS,
@@ -17,7 +17,7 @@ import {
17
17
  } from "./chunk-QTOLNRET.js";
18
18
  import {
19
19
  isAutoUpdateResult
20
- } from "./chunk-QRS7FQHE.js";
20
+ } from "./chunk-INBG473K.js";
21
21
 
22
22
  // src/auto-update.ts
23
23
  import { spawn } from "child_process";
@@ -1,19 +1,19 @@
1
1
  import {
2
2
  resolveInstallSourceFor
3
- } from "./chunk-DARPCQPY.js";
3
+ } from "./chunk-K2MW33DG.js";
4
4
  import {
5
5
  backupFile,
6
6
  readJsonSafe,
7
7
  writeFileAtomic,
8
8
  writeJsonAtomic
9
- } from "./chunk-AUDYAVNM.js";
9
+ } from "./chunk-ABAWD6QM.js";
10
10
  import {
11
11
  configFilePath
12
- } from "./chunk-53D6FFVE.js";
12
+ } from "./chunk-IHT33GZN.js";
13
13
  import {
14
14
  API_KEY_PREFIX_PATTERN,
15
15
  isRecord
16
- } from "./chunk-QRS7FQHE.js";
16
+ } from "./chunk-INBG473K.js";
17
17
 
18
18
  // src/shell-alias.ts
19
19
  import { homedir } from "os";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  guardBinary
3
- } from "./chunk-2J4IVUEA.js";
3
+ } from "./chunk-PZXQUV3P.js";
4
4
 
5
5
  // src/bell/install.ts
6
6
  import { join } from "path";