@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-
|
|
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
|
|
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-
|
|
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(
|
|
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(
|
|
836
|
+
await spinner(`Installing ${HERMES_PIP_PACKAGE} into Hermes\u2019 environment`, async () => {
|
|
830
837
|
ensurePip(python);
|
|
831
|
-
execSync2(`"${python}" -m pip install --upgrade
|
|
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
|
|
1954
|
-
if (!
|
|
1955
|
-
if (
|
|
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(
|
|
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,
|