@pushary/agent-hooks 0.90.2 → 0.91.0

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,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.91.0
4
+
5
+ ### Setup adopts the key the Mac app is already signed in with
6
+
7
+ The Mac app authenticates through Clerk and writes its API key into every agent
8
+ config it wires, but never into `~/.pushary/config.json`. Setup's key ladder did
9
+ not look there, so a Mac the app had already connected still fell through to the
10
+ pairing QR and minted a second key for an account that was already live.
11
+
12
+ The ladder gains one rung between the environment and pairing: a key the Mac app
13
+ wrote, probed against the server exactly like the stored and environment rungs
14
+ are, and skipped whenever `--key` was passed or `--connect app` was typed. Both
15
+ of those still mean what they meant.
16
+
3
17
  ## 0.90.2
4
18
 
5
19
  ### The Mac app and CLI coexist without a hook takeover
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  HERMES_CONFIG_SCRIPT,
4
+ HERMES_PIP_PACKAGE,
4
5
  createGuards,
5
6
  hookOwnerOf,
6
7
  isHookTakeoverReceipt,
@@ -8,7 +9,7 @@ import {
8
9
  restoreHookTakeover,
9
10
  restoreMcpServer,
10
11
  runHermesPythonScript
11
- } from "../chunk-ATUUQFDH.js";
12
+ } from "../chunk-B65JGBIZ.js";
12
13
  import {
13
14
  removeInstructionBlock,
14
15
  shortenHome,
@@ -574,7 +575,7 @@ var main = async () => {
574
575
  } else {
575
576
  console.log(` ${check} Hermes config ${dim("(setup-owned changes restored)")}`);
576
577
  try {
577
- guardedExec(`"${hermesPython}" -m pip uninstall -y hermes-plugin-pushary`, { stdio: "pipe", timeout: 6e4 }, "pip uninstall hermes-plugin-pushary");
578
+ guardedExec(`"${hermesPython}" -m pip uninstall -y ${HERMES_PIP_PACKAGE}`, { stdio: "pipe", timeout: 6e4 }, `pip uninstall ${HERMES_PIP_PACKAGE}`);
578
579
  console.log(` ${check} Hermes plugin ${dim("(pip uninstalled)")}`);
579
580
  } catch {
580
581
  incompleteClean = true;
@@ -13,7 +13,9 @@ import {
13
13
  waitForMachineReadiness
14
14
  } from "../chunk-YTD6PJM7.js";
15
15
  import {
16
+ AGENT_CHOICES,
16
17
  HERMES_CONFIG_SCRIPT,
18
+ HERMES_PIP_PACKAGE,
17
19
  KEEP_HINT,
18
20
  captureHookTakeover,
19
21
  captureMcpServer,
@@ -29,7 +31,7 @@ import {
29
31
  planHooks,
30
32
  resolveHermesPython,
31
33
  runHermesPythonScript
32
- } from "../chunk-ATUUQFDH.js";
34
+ } from "../chunk-B65JGBIZ.js";
33
35
  import {
34
36
  describeDetection,
35
37
  detectAllAgents,
@@ -553,6 +555,11 @@ var reportSetupAbortWith = async (deps, reason, exitCode, keyCheck = "unknown")
553
555
  });
554
556
  var reportSetupAbort = (reason, exitCode, keyCheck) => reportSetupAbortWith(defaultDeps, reason, exitCode, keyCheck);
555
557
 
558
+ // src/setup/app-key.ts
559
+ var pickAppKey = (candidates) => candidates.fromAgents.find(
560
+ (candidate) => typeof candidate === "string" && candidate !== candidates.storedKey && candidate !== candidates.envKey && isValidApiKey(candidate)
561
+ ) ?? null;
562
+
556
563
  // bin/pushary-setup.ts
557
564
  var CLAUDE_SETTINGS = claudeSettings();
558
565
  var CLAUDE_JSON = claudeJson();
@@ -819,16 +826,16 @@ var setupHermes = async (_apiKey) => {
819
826
  const python = resolveHermesPython();
820
827
  if (!python) {
821
828
  console.log(` ${yellow("!")} Could not locate the Python that Hermes runs in. Skipping.`);
822
- noteManual("Install the Hermes plugin by hand: <hermes-python> -m pip install hermes-plugin-pushary");
829
+ noteManual(`Install the Hermes plugin by hand: <hermes-python> -m pip install ${HERMES_PIP_PACKAGE}`);
823
830
  return "skipped";
824
831
  }
825
832
  const existingReceipt = readJsonSafe(HERMES_CONFIG_RECEIPT);
826
833
  if (existingReceipt.kind !== "missing" && (existingReceipt.kind !== "ok" || !isHermesConfigReceipt(existingReceipt.value))) {
827
834
  throw new Error(`Cannot safely configure Hermes: ${HERMES_CONFIG_RECEIPT} is unreadable or invalid.`);
828
835
  }
829
- await spinner("Installing hermes-plugin-pushary into Hermes\u2019 environment", async () => {
836
+ await spinner(`Installing ${HERMES_PIP_PACKAGE} into Hermes\u2019 environment`, async () => {
830
837
  ensurePip(python);
831
- execSync2(`"${python}" -m pip install --upgrade hermes-plugin-pushary`, { stdio: "pipe", timeout: 18e4 });
838
+ execSync2(`"${python}" -m pip install --upgrade ${HERMES_PIP_PACKAGE}`, { stdio: "pipe", timeout: 18e4 });
832
839
  });
833
840
  let pluginEnabled = false;
834
841
  await spinner("Enabling plugin + routing questions to push", async () => {
@@ -1471,6 +1478,13 @@ var appMcpKeyFor = (agent) => {
1471
1478
  const file = agent === "claude_code" ? CLAUDE_JSON : agent === "gemini_cli" ? GEMINI_SETTINGS : agent === "cursor" ? cursorUserMcp() : agent === "vscode" ? VSCODE_PLUGIN_MCP : null;
1472
1479
  return file ? mcpBearerKey(readAgentJson(file), "mcpServers", "headers") : null;
1473
1480
  };
1481
+ var appMcpKeyIfReadable = (agent) => {
1482
+ try {
1483
+ return appMcpKeyFor(agent);
1484
+ } catch {
1485
+ return null;
1486
+ }
1487
+ };
1474
1488
  var captureTakeoverReceipt = (agent) => {
1475
1489
  const hookFile = HOOK_FILES[agent]?.();
1476
1490
  if (!hookFile) return;
@@ -1806,6 +1820,8 @@ var main = async () => {
1806
1820
  const envCheck = ambientEligible && envKey && isValidApiKey(envKey) ? await usableStoredKey(envKey) : null;
1807
1821
  const storedCheck = ambientEligible && envCheck === null && storedKey !== null && isValidApiKey(storedKey) && // login writes both, so without this the same key is probed twice.
1808
1822
  storedKey !== envKey ? await usableStoredKey(storedKey) : null;
1823
+ const appKey = ambientEligible && envCheck === null && storedCheck === null ? pickAppKey({ fromAgents: AGENT_CHOICES.map(appMcpKeyIfReadable), storedKey, envKey }) : null;
1824
+ const appKeyCheck = appKey === null ? null : await usableStoredKey(appKey);
1809
1825
  let storedKeyCheck = null;
1810
1826
  if (flagKey) {
1811
1827
  const masked = `${flagKey.slice(0, 8)}...${flagKey.slice(-4)}`;
@@ -1831,6 +1847,12 @@ var main = async () => {
1831
1847
  const apiKey = await input({ message: "API key:" });
1832
1848
  trimmedKey = apiKey.trim();
1833
1849
  }
1850
+ } else if (appKey !== null && appKeyCheck !== null) {
1851
+ const masked = `${appKey.slice(0, 8)}...${appKey.slice(-4)}`;
1852
+ console.log(` ${check} Using the key the Pushary Mac app is signed in with: ${dim(masked)}`);
1853
+ console.log();
1854
+ trimmedKey = appKey;
1855
+ storedKeyCheck = appKeyCheck;
1834
1856
  } else if (options.dryRun) {
1835
1857
  console.log(` ${dim("No key on this machine. A real run would connect one first.")}`);
1836
1858
  console.log();
@@ -1950,12 +1972,12 @@ var main = async () => {
1950
1972
  return pending;
1951
1973
  };
1952
1974
  const appAccountMatches = async (agent) => {
1953
- const appKey = appMcpKeyFor(agent);
1954
- if (!appKey) return false;
1955
- if (appKey === trimmedKey) return true;
1975
+ const appKey2 = appMcpKeyFor(agent);
1976
+ if (!appKey2) return false;
1977
+ if (appKey2 === trimmedKey) return true;
1956
1978
  const [cliAccount, appAccount] = await Promise.all([
1957
1979
  accountCheckFor(trimmedKey),
1958
- accountCheckFor(appKey)
1980
+ accountCheckFor(appKey2)
1959
1981
  ]);
1960
1982
  return samePusharyAccount(cliAccount, appAccount);
1961
1983
  };
@@ -31,6 +31,7 @@ var isHermesConfigReceipt = (value) => {
31
31
  if (!isRecord(value)) return false;
32
32
  return ["security", "approval", "transport", "fallback", "plugins", "enabled", "agent", "disabled"].every((key) => isSnapshot(value[key])) && typeof value.pushary_enabled === "boolean" && typeof value.clarify_disabled === "boolean";
33
33
  };
34
+ var HERMES_PIP_PACKAGE = "hermes-plugin-pushary";
34
35
  var HERMES_CONFIG_SCRIPT = String.raw`
35
36
  import json, os, sys
36
37
  from hermes_cli.config import load_config, save_config
@@ -514,11 +515,13 @@ var describeHookOwnership = (entries, names) => {
514
515
 
515
516
  export {
516
517
  isHermesConfigReceipt,
518
+ HERMES_PIP_PACKAGE,
517
519
  HERMES_CONFIG_SCRIPT,
518
520
  resolveHermesPython,
519
521
  runHermesPythonScript,
520
522
  createGuards,
521
523
  describeSetupSideEffects,
524
+ AGENT_CHOICES,
522
525
  effectiveVerify,
523
526
  effectiveControlMode,
524
527
  parseSetupOptions,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushary/agent-hooks",
3
- "version": "0.90.2",
3
+ "version": "0.91.0",
4
4
  "description": "Permission hooks for AI coding agents: route tool approvals through Pushary push notifications",
5
5
  "keywords": [
6
6
  "pushary",