pushary 1.9.102 → 1.9.103

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.103
4
+
5
+ - The Hermes plugin Pushary installs is now 1.0.1. When it hands a Hermes session to Pushary's history helper, the helper now gets only what it reads: the home folder, the search path, the temporary folder and locale, the Pushary settings it uses, and the Pushary key Hermes holds. Hermes' other provider keys no longer reach it. Checking whether history is allowed sends the key on standard input only.
6
+
3
7
  ## 1.9.102
4
8
 
5
9
  - The Hermes plugin Pushary installs is now 1.0.0. In Hermes Desktop, a Pushary chip in the status bar shows the approvals waiting on your phone, and you can answer them there too. Only one answer counts: a Hermes Desktop answer is used only when Pushary confirms that answer is what withdrew the question, and otherwise the panel says the approval had already closed. The chip also lists how recent approvals ended and where they were answered. Enable Pushary under Capabilities, Plugins in Hermes Desktop to see it.
@@ -53,7 +53,7 @@ import {
53
53
  defaultHermesHome,
54
54
  hermesReceiptPaths,
55
55
  removeHermesWiring
56
- } from "../chunk-PPWEBIKL.js";
56
+ } from "../chunk-COBOR3ST.js";
57
57
  import "../chunk-QCGNGEGE.js";
58
58
  import {
59
59
  planClineRemoval,
@@ -814,7 +814,7 @@ var decidePreToolUse = async (input) => {
814
814
  };
815
815
  var repairCodexHoldsQuietly = async () => {
816
816
  try {
817
- const { repairCodexHolds } = await import("../reapply-3LBUA5EV.js");
817
+ const { repairCodexHolds } = await import("../reapply-LDCMRN6Q.js");
818
818
  repairCodexHolds();
819
819
  } catch {
820
820
  return;
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  inspectConfiguredManagedProviders
4
- } from "../chunk-MA65OP6M.js";
4
+ } from "../chunk-RMFL62HR.js";
5
5
  import {
6
6
  HEARTBEAT_INTERVAL_MS,
7
7
  sendHeartbeat
@@ -14,7 +14,7 @@ import {
14
14
  HERMES_INSTALL_MARKER_FILE,
15
15
  defaultHermesHome,
16
16
  parseHermesInstallMarker
17
- } from "../chunk-PPWEBIKL.js";
17
+ } from "../chunk-COBOR3ST.js";
18
18
  import {
19
19
  writeHuman
20
20
  } from "../chunk-QCGNGEGE.js";
@@ -75,7 +75,7 @@ import {
75
75
  routesHermesQuestions,
76
76
  runsAnOlderPlugin,
77
77
  shippedHermesPlugin
78
- } from "../chunk-PPWEBIKL.js";
78
+ } from "../chunk-COBOR3ST.js";
79
79
  import "../chunk-QCGNGEGE.js";
80
80
  import {
81
81
  FX_PROFILE_REFUSALS,
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  reapplyClaudeForVersion
4
- } from "../chunk-MA65OP6M.js";
4
+ } from "../chunk-RMFL62HR.js";
5
5
  import "../chunk-RVSRPDRF.js";
6
- import "../chunk-PPWEBIKL.js";
6
+ import "../chunk-COBOR3ST.js";
7
7
  import "../chunk-SXAZPWL7.js";
8
8
  import "../chunk-JECH4NJL.js";
9
9
  import "../chunk-AZSOH2SF.js";
@@ -136,7 +136,7 @@ import {
136
136
  routesHermesQuestions,
137
137
  runsAnOlderPlugin,
138
138
  shippedHermesPlugin
139
- } from "../chunk-PPWEBIKL.js";
139
+ } from "../chunk-COBOR3ST.js";
140
140
  import {
141
141
  setHumanStream
142
142
  } from "../chunk-QCGNGEGE.js";
@@ -28,7 +28,7 @@ import {
28
28
  import {
29
29
  ensureCodexTrustAfterReapply,
30
30
  reapplyAllAgents
31
- } from "../chunk-MA65OP6M.js";
31
+ } from "../chunk-RMFL62HR.js";
32
32
  import {
33
33
  HERMES_RESTART_COMMAND,
34
34
  describeCodexTrustConfirmation
@@ -42,7 +42,7 @@ import {
42
42
  readMacAppAgentKey
43
43
  } from "../chunk-JKOAAIJD.js";
44
44
  import "../chunk-WYJ6QM7K.js";
45
- import "../chunk-PPWEBIKL.js";
45
+ import "../chunk-COBOR3ST.js";
46
46
  import {
47
47
  FX_RELOAD_AFTER_BRIDGE,
48
48
  fxHttpEntryKeptNote,
@@ -1311,7 +1311,7 @@ def _await_decision(request, created, correlation_id: str, offered, uses_select:
1311
1311
  return _decided(request, correlation_id, "deny", None)
1312
1312
  ` },
1313
1313
  { path: "cancellation.py", contents: 'from __future__ import annotations\n\nfrom typing import Literal, Mapping, Tuple\n\nCancellation = Literal[\n "withdrawn_by_this_call",\n "cancelled_unproven",\n "already_cancelled",\n "not_pending",\n "unreachable",\n]\n\nWITHDRAWN_BY_THIS_CALL: Cancellation = "withdrawn_by_this_call"\nCANCELLED_UNPROVEN: Cancellation = "cancelled_unproven"\nALREADY_CANCELLED: Cancellation = "already_cancelled"\nNOT_PENDING: Cancellation = "not_pending"\nUNREACHABLE: Cancellation = "unreachable"\nCANCELLATIONS: Tuple[Cancellation, ...] = (\n WITHDRAWN_BY_THIS_CALL,\n CANCELLED_UNPROVEN,\n ALREADY_CANCELLED,\n NOT_PENDING,\n UNREACHABLE,\n)\n\nUNAVAILABLE_STATUS = "unavailable"\nSTOP_HANDOFF = "stop"\n\n\ndef classify_cancellation(response: object) -> Cancellation:\n if not isinstance(response, Mapping):\n return UNREACHABLE\n fields: Mapping[object, object] = response\n cancelled = fields.get("cancelled")\n by_this_call = fields.get("cancelledByThisCall")\n if (\n not isinstance(cancelled, bool)\n or fields.get("status") == UNAVAILABLE_STATUS\n or fields.get("handoffAction") == STOP_HANDOFF\n or (by_this_call is not None and not isinstance(by_this_call, bool))\n or (by_this_call is True and not cancelled)\n ):\n return UNREACHABLE\n if not cancelled:\n return NOT_PENDING\n if by_this_call is None:\n return CANCELLED_UNPROVEN\n return WITHDRAWN_BY_THIS_CALL if by_this_call else ALREADY_CANCELLED\n' },
1314
- { path: "dashboard/manifest.json", contents: '{\n "name": "pushary",\n "label": "Pushary",\n "description": "Approvals Pushary is carrying to your phone, answerable from Hermes Desktop",\n "version": "1.0.0",\n "tab": { "hidden": true },\n "entry": "web.js",\n "api": "plugin_api.py"\n}\n' },
1314
+ { path: "dashboard/manifest.json", contents: '{\n "name": "pushary",\n "label": "Pushary",\n "description": "Approvals Pushary is carrying to your phone, answerable from Hermes Desktop",\n "version": "1.0.1",\n "tab": { "hidden": true },\n "entry": "web.js",\n "api": "plugin_api.py"\n}\n' },
1315
1315
  { path: "dashboard/plugin_api.py", contents: `import importlib
1316
1316
  import sys
1317
1317
  import time
@@ -1590,6 +1590,11 @@ SNAPSHOT_PRUNE_INTERVAL_SECONDS = 60 * 60
1590
1590
  SOURCE_UNAVAILABLE = "source_unavailable"
1591
1591
  HISTORY_NOT_ALLOWED_WARNING = ("[pushary] Hermes session history is not saved on this computer; it needs "
1592
1592
  "\`npx pushary@latest setup\` with the key Hermes uses, or Session history turned on in the Pushary Mac app")
1593
+ HELPER_ENVIRONMENT_NAMES = frozenset({
1594
+ "HOME", "PATH", "TMPDIR", "LANG", "LC_ALL", "LC_CTYPE",
1595
+ "SYSTEMROOT", "PATHEXT", "COMSPEC", "USERPROFILE", "APPDATA", "LOCALAPPDATA", "TEMP", "TMP",
1596
+ "PUSHARY_BASE_URL", "PUSHARY_API_URL", "PUSHARY_CONFIG_FILE", "PUSHARY_TRANSCRIPTS", "PUSHARY_TRANSCRIPT_REGISTRY_DIR",
1597
+ })
1593
1598
 
1594
1599
 
1595
1600
  class _Authorization(NamedTuple):
@@ -1717,6 +1722,14 @@ def write_snapshot(path: Path, session_id: str, messages: list[dict[str, object]
1717
1722
  Path(temporary).unlink(missing_ok=True)
1718
1723
 
1719
1724
 
1725
+ def _helper_environment() -> dict[str, str]:
1726
+ return {name: value for name, value in os.environ.items() if name in HELPER_ENVIRONMENT_NAMES}
1727
+
1728
+
1729
+ def _registration_environment(key: str) -> dict[str, str]:
1730
+ return {**_helper_environment(), "PUSHARY_API_KEY": key, "PUSHARY_BASE_URL": api.BASE_URL}
1731
+
1732
+
1720
1733
  def _registration_command(key: str) -> list[str] | None:
1721
1734
  root = Path.home() / ".pushary"
1722
1735
  try:
@@ -1731,7 +1744,7 @@ def _registration_command(key: str) -> list[str] | None:
1731
1744
  command = [helper, "hermes"]
1732
1745
  result = subprocess.run([helper, "authorize-export"],
1733
1746
  input=json.dumps({"apiKey": key, "baseUrl": api.BASE_URL}), text=True,
1734
- stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, timeout=5, check=False)
1747
+ env=_helper_environment(), stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, timeout=5, check=False)
1735
1748
  if result.returncode != 0:
1736
1749
  return None
1737
1750
  authorization = json.loads(result.stdout)
@@ -1885,8 +1898,7 @@ def _export_loop(database_path: Path, session_id: str, key: str, command: list[s
1885
1898
  if ending or time.monotonic() >= next_registration:
1886
1899
  payload = {"session_id": session_id, "cwd": str(database_path.parent), "transcript_path": str(path),
1887
1900
  "hook_event_name": "SessionEnd" if ending else "SessionStart"}
1888
- environment = {**os.environ, "PUSHARY_API_KEY": key, "PUSHARY_BASE_URL": api.BASE_URL}
1889
- result = subprocess.run(command, input=json.dumps(payload), text=True, env=environment,
1901
+ result = subprocess.run(command, input=json.dumps(payload), text=True, env=_registration_environment(key),
1890
1902
  stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=5, check=False)
1891
1903
  if result.returncode != 0:
1892
1904
  logger.warning("[pushary] Hermes history reader could not register")
@@ -2084,7 +2096,7 @@ def record_plugin_load(approval_transport_registered, home=None, pid=None, now=N
2084
2096
  logger.debug("[pushary] could not record the plugin load: %s", exc)
2085
2097
  return None
2086
2098
  ` },
2087
- { path: "plugin.yaml", contents: 'name: pushary\nversion: 1.0.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\nconfig_schema:\n api_key:\n type: secret\n env: PUSHARY_API_KEY\n label: Pushary API key\n description: "Get one at pushary.com/sign-up?from=hermes"\n required: 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\n - post_approval_response\nprovides_approval_transports:\n - pushary\n' },
2099
+ { path: "plugin.yaml", contents: 'name: pushary\nversion: 1.0.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\nconfig_schema:\n api_key:\n type: secret\n env: PUSHARY_API_KEY\n label: Pushary API key\n description: "Get one at pushary.com/sign-up?from=hermes"\n required: 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\n - post_approval_response\nprovides_approval_transports:\n - pushary\n' },
2088
2100
  { path: "schemas.py", contents: `PUSHARY_REMIND = {
2089
2101
  "name": "pushary_remind",
2090
2102
  "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.",
@@ -7,7 +7,7 @@ import {
7
7
  installHermesPlugin,
8
8
  refreshHermesPluginFiles,
9
9
  shippedHermesPlugin
10
- } from "./chunk-PPWEBIKL.js";
10
+ } from "./chunk-COBOR3ST.js";
11
11
  import {
12
12
  fxIgnoresInstructionsFile,
13
13
  fxInstructionsBody,
@@ -12,9 +12,9 @@ import {
12
12
  reapplyOpenCodePlugin,
13
13
  reapplyVsCodePlugin,
14
14
  repairCodexHolds
15
- } from "./chunk-MA65OP6M.js";
15
+ } from "./chunk-RMFL62HR.js";
16
16
  import "./chunk-RVSRPDRF.js";
17
- import "./chunk-PPWEBIKL.js";
17
+ import "./chunk-COBOR3ST.js";
18
18
  import "./chunk-SXAZPWL7.js";
19
19
  import "./chunk-JECH4NJL.js";
20
20
  import "./chunk-AZSOH2SF.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pushary",
3
- "version": "1.9.102",
3
+ "version": "1.9.103",
4
4
  "description": "Human-in-the-loop (HITL) for AI agents: ask a person for approvals or answers by push notification. One command connects Claude Code, Codex, Cursor, Gemini CLI and more.",
5
5
  "keywords": [
6
6
  "pushary",