@pushary/agent-hooks 0.62.2 → 0.62.4

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,63 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.62.4
4
+
5
+ ### `pushary clean` crashed partway through on Node 24
6
+
7
+ `clean` threw `ERR_INVALID_ARG_TYPE: The "options.force" property must be of
8
+ type boolean` and died mid-run, after cleaning some agents and before touching
9
+ the rest. Introduced in 0.62.0 by the `--dry-run` work, which started passing
10
+ `force: undefined` to `rmSync` on every call that omitted the option. Older Node
11
+ ignored that; Node 24 rejects it.
12
+
13
+ If a clean died on you, re-run it. It is safe to run repeatedly and picks up
14
+ whatever is left.
15
+
16
+ The three mutation primitives behind `--dry-run` now live in `src/cli/dry-run.ts`
17
+ instead of inside the command file. Both `--dry-run` defects that reached users
18
+ were in those few lines, and both got past tests that could only read the source
19
+ as text, because code in `bin/` runs at import and cannot be called by a test.
20
+ They are ordinary functions with ordinary tests now.
21
+
22
+ ### The authorize page's code input overflowed its card
23
+
24
+ The eight character slots were a fixed width, so they added up to 488px inside a
25
+ 384px card and spilled out of both edges. They now share the row and shrink to
26
+ fit, down to a 320px phone.
27
+
28
+
29
+ ## 0.62.3
30
+
31
+ ### `setup --connect app --key ...` discarded your key
32
+
33
+ Pairing was checked before the supplied key, so passing `--key` or `--key-stdin`
34
+ alongside `--connect app` silently threw that key away and minted a new one. The
35
+ key you named stayed live on the account with nothing pointing at it. An
36
+ explicitly supplied key now wins, and pairing is only the key source when there
37
+ is no key to use.
38
+
39
+ Pairing still mints a key each time it runs, which is inherent to the protocol.
40
+ When that replaces a key this machine was already using, setup now says so and
41
+ names the old key, rather than leaving you to find it in the dashboard later. It
42
+ does not revoke it for you: the same key may be in use on another machine.
43
+
44
+ ### The update banner offered downgrades
45
+
46
+ Setup compared its version to the registry with `!==`, so it announced an update
47
+ whenever the two merely differed. A machine running a build ahead of npm was told
48
+ `Update available: 0.62.3 -> 0.62.2` and pointed at a command that would have
49
+ downgraded it. It now prompts only when the registry is genuinely ahead, using
50
+ the same comparison `pushary upgrade` already used. There is one implementation
51
+ of that comparison now instead of two.
52
+
53
+ ### A reused key skipped its own verification
54
+
55
+ Setup skipped the server key check whenever `--connect app` was passed, on the
56
+ assumption the key had just been minted. That is only true when pairing actually
57
+ produced it, so a key reused on an `--connect app` run was never verified. The
58
+ skip now follows where the key came from.
59
+
60
+
3
61
  ## 0.62.2
4
62
 
5
63
  ### An ignored `--dry-run` made real changes
@@ -24,18 +24,38 @@ import {
24
24
  } from "../chunk-MUEW424A.js";
25
25
  import {
26
26
  exitOnHelpFlag
27
- } from "../chunk-WXFEK7KD.js";
28
- import "../chunk-7OXIC7E3.js";
27
+ } from "../chunk-674M3JQF.js";
28
+ import "../chunk-KVUBLB6K.js";
29
29
  import "../chunk-DQAN3JQP.js";
30
30
  import "../chunk-KZERVKTD.js";
31
31
 
32
32
  // bin/pushary-clean.ts
33
- import { existsSync, readFileSync, writeFileSync, copyFileSync, rmSync, readdirSync } from "fs";
33
+ import { existsSync, readFileSync, copyFileSync, readdirSync } from "fs";
34
34
  import { join } from "path";
35
35
  import { homedir, tmpdir } from "os";
36
- import { execSync } from "child_process";
36
+ import { execSync as execSync2 } from "child_process";
37
37
  import { confirm } from "@inquirer/prompts";
38
38
  import { parse as parseTOML, stringify as stringifyTOML } from "smol-toml";
39
+
40
+ // src/cli/dry-run.ts
41
+ import { execSync } from "child_process";
42
+ import { rmSync, writeFileSync } from "fs";
43
+ var createGuards = (dryRun2, note) => ({
44
+ write: (path, contents) => {
45
+ if (dryRun2) return note("rewrite", path);
46
+ writeFileSync(path, contents, "utf-8");
47
+ },
48
+ remove: (path, options = {}) => {
49
+ if (dryRun2) return note("remove ", path);
50
+ rmSync(path, { recursive: true, force: options.force ?? false });
51
+ },
52
+ exec: (command, options, describe) => {
53
+ if (dryRun2) return note("run ", describe);
54
+ execSync(command, options);
55
+ }
56
+ });
57
+
58
+ // bin/pushary-clean.ts
39
59
  exitOnHelpFlag("clean");
40
60
  var dim = (s) => `\x1B[2m${s}\x1B[0m`;
41
61
  var bold = (s) => `\x1B[1m${s}\x1B[0m`;
@@ -54,7 +74,7 @@ var PUSHARY_DIR = join(homedir(), ".pushary");
54
74
  var SHELL_FILES = [".zshrc", ".zprofile", ".bashrc", ".bash_profile"].map((f) => join(homedir(), f));
55
75
  var resolveHermesPython = () => {
56
76
  try {
57
- const launcher = execSync("command -v hermes", { encoding: "utf-8", stdio: "pipe", timeout: 5e3 }).trim();
77
+ const launcher = execSync2("command -v hermes", { encoding: "utf-8", stdio: "pipe", timeout: 5e3 }).trim();
58
78
  if (launcher) {
59
79
  const shebang = readFileSync(launcher, "utf-8").split("\n", 1)[0];
60
80
  if (shebang.startsWith("#!")) {
@@ -80,18 +100,10 @@ var did = (past) => dryRun ? `(would ${past === "cleaned" ? "clean" : past === "
80
100
  var wouldNote = (action, path) => {
81
101
  console.log(` ${dim("would")} ${action} ${path}`);
82
102
  };
83
- var guardedWrite = (path, contents) => {
84
- if (dryRun) return wouldNote("rewrite", path);
85
- writeFileSync(path, contents, "utf-8");
86
- };
87
- var guardedRemove = (path, options = {}) => {
88
- if (dryRun) return wouldNote("remove ", path);
89
- rmSync(path, { recursive: true, force: options.force });
90
- };
91
- var guardedExec = (command, options, describe) => {
92
- if (dryRun) return wouldNote("run ", describe);
93
- execSync(command, options);
94
- };
103
+ var guards = createGuards(dryRun, wouldNote);
104
+ var guardedWrite = (path, contents) => guards.write(path, contents);
105
+ var guardedRemove = (path, options) => guards.remove(path, options);
106
+ var guardedExec = (command, options, describe) => guards.exec(command, options ?? {}, describe);
95
107
  var guardedInstructionBlockRemoval = (path) => {
96
108
  if (!dryRun) return removeInstructionBlock(path);
97
109
  if (!existsSync(path)) return false;
@@ -24,8 +24,8 @@ import {
24
24
  import "../chunk-BC3VCZ3E.js";
25
25
  import {
26
26
  exitOnHelpFlag
27
- } from "../chunk-WXFEK7KD.js";
28
- import "../chunk-7OXIC7E3.js";
27
+ } from "../chunk-674M3JQF.js";
28
+ import "../chunk-KVUBLB6K.js";
29
29
  import "../chunk-BSZYIAZL.js";
30
30
  import "../chunk-SAF6HGAA.js";
31
31
  import {
@@ -7,8 +7,8 @@ import {
7
7
  } from "../chunk-MUEW424A.js";
8
8
  import {
9
9
  exitOnHelpFlag
10
- } from "../chunk-WXFEK7KD.js";
11
- import "../chunk-7OXIC7E3.js";
10
+ } from "../chunk-674M3JQF.js";
11
+ import "../chunk-KVUBLB6K.js";
12
12
  import {
13
13
  getMachineId
14
14
  } from "../chunk-RN3NOEJF.js";
@@ -50,10 +50,10 @@ import {
50
50
  } from "../chunk-MUEW424A.js";
51
51
  import {
52
52
  exitOnHelpFlag
53
- } from "../chunk-WXFEK7KD.js";
53
+ } from "../chunk-674M3JQF.js";
54
54
  import {
55
55
  getPackageVersion
56
- } from "../chunk-7OXIC7E3.js";
56
+ } from "../chunk-KVUBLB6K.js";
57
57
  import {
58
58
  readLedgerSummary
59
59
  } from "../chunk-WLGEY4UX.js";
@@ -5,8 +5,8 @@ import {
5
5
  import "../chunk-CAJZAFVS.js";
6
6
  import {
7
7
  exitOnHelpFlag
8
- } from "../chunk-WXFEK7KD.js";
9
- import "../chunk-7OXIC7E3.js";
8
+ } from "../chunk-674M3JQF.js";
9
+ import "../chunk-KVUBLB6K.js";
10
10
  import "../chunk-YHG74UFF.js";
11
11
  import "../chunk-5ZDYDU2Q.js";
12
12
  import "../chunk-WLGEY4UX.js";
@@ -21,10 +21,10 @@ import {
21
21
  import "../chunk-BC3VCZ3E.js";
22
22
  import {
23
23
  exitOnHelpFlag
24
- } from "../chunk-WXFEK7KD.js";
24
+ } from "../chunk-674M3JQF.js";
25
25
  import {
26
26
  getPackageVersion
27
- } from "../chunk-7OXIC7E3.js";
27
+ } from "../chunk-KVUBLB6K.js";
28
28
  import "../chunk-SAF6HGAA.js";
29
29
  import {
30
30
  UsageError
@@ -16,8 +16,8 @@ import {
16
16
  import "../chunk-BC3VCZ3E.js";
17
17
  import {
18
18
  exitOnHelpFlag
19
- } from "../chunk-WXFEK7KD.js";
20
- import "../chunk-7OXIC7E3.js";
19
+ } from "../chunk-674M3JQF.js";
20
+ import "../chunk-KVUBLB6K.js";
21
21
  import {
22
22
  UsageError
23
23
  } from "../chunk-7QLSKOSU.js";
@@ -11,8 +11,8 @@ import {
11
11
  } from "../chunk-GMXKITVA.js";
12
12
  import {
13
13
  exitOnHelpFlag
14
- } from "../chunk-WXFEK7KD.js";
15
- import "../chunk-7OXIC7E3.js";
14
+ } from "../chunk-674M3JQF.js";
15
+ import "../chunk-KVUBLB6K.js";
16
16
  import "../chunk-SAF6HGAA.js";
17
17
  import {
18
18
  UsageError
@@ -40,6 +40,8 @@ import "../chunk-3EGEA4KH.js";
40
40
  import {
41
41
  KEY_FILE_MODE,
42
42
  exportLine,
43
+ keyPrefix,
44
+ readKeySource,
43
45
  writeKey
44
46
  } from "../chunk-COC6NPWG.js";
45
47
  import {
@@ -68,8 +70,9 @@ import {
68
70
  } from "../chunk-MUEW424A.js";
69
71
  import {
70
72
  getPackageVersion,
73
+ isNewerVersion,
71
74
  renderCommandHelp
72
- } from "../chunk-7OXIC7E3.js";
75
+ } from "../chunk-KVUBLB6K.js";
73
76
  import {
74
77
  reportEvent
75
78
  } from "../chunk-5ZDYDU2Q.js";
@@ -385,7 +388,7 @@ var checkForUpdates = async (current) => {
385
388
  });
386
389
  const data = await res.json();
387
390
  const latest = data.version;
388
- if (latest && latest !== current) {
391
+ if (latest && isNewerVersion(latest, current)) {
389
392
  console.log(` ${yellow("!")} Update available: ${dim(current)} \u2192 ${green(latest)}`);
390
393
  console.log(` ${dim("Run:")} npx @pushary/agent-hooks@latest upgrade`);
391
394
  console.log();
@@ -1065,8 +1068,15 @@ var main = async () => {
1065
1068
  const flagKey = await readKeyFromStdin(options) ?? options.key ?? void 0;
1066
1069
  const envKey = process.env.PUSHARY_API_KEY?.trim();
1067
1070
  let trimmedKey;
1068
- if (connectMode === "app") {
1071
+ let keyFromPairing = false;
1072
+ if (flagKey && isValidApiKey(flagKey)) {
1073
+ const masked = `${flagKey.slice(0, 8)}...${flagKey.slice(-4)}`;
1074
+ console.log(` ${check} Using API key: ${dim(masked)}`);
1075
+ console.log();
1076
+ trimmedKey = flagKey;
1077
+ } else if (connectMode === "app") {
1069
1078
  console.log(` ${dim("Pairing with your Pushary app.")}`);
1079
+ const previous = readKeySource();
1070
1080
  const paired = await connectViaAppPairing();
1071
1081
  if (!paired) {
1072
1082
  console.log();
@@ -1075,15 +1085,15 @@ var main = async () => {
1075
1085
  process.exit(1);
1076
1086
  }
1077
1087
  trimmedKey = paired.apiKey;
1088
+ keyFromPairing = true;
1078
1089
  if (paired.handle) {
1079
1090
  console.log(` ${check} Connected to ${cyan("@" + paired.handle)}`);
1080
1091
  }
1092
+ if (previous.key && previous.key !== trimmedKey) {
1093
+ console.log(` ${yellow("!")} The key this machine used before (${keyPrefix(previous.key)}) is still active.`);
1094
+ console.log(` ${dim("Revoke it at")} ${cyan("pushary.com/dashboard/agent/settings")} ${dim("if nothing else uses it.")}`);
1095
+ }
1081
1096
  console.log();
1082
- } else if (flagKey && isValidApiKey(flagKey)) {
1083
- const masked = `${flagKey.slice(0, 8)}...${flagKey.slice(-4)}`;
1084
- console.log(` ${check} Using API key: ${dim(masked)}`);
1085
- console.log();
1086
- trimmedKey = flagKey;
1087
1097
  } else if (envKey && isValidApiKey(envKey)) {
1088
1098
  const masked = `${envKey.slice(0, 8)}...${envKey.slice(-4)}`;
1089
1099
  console.log(` ${check} Found API key in environment: ${dim(masked)}`);
@@ -1138,7 +1148,7 @@ var main = async () => {
1138
1148
  `);
1139
1149
  process.exit(EXIT.USAGE);
1140
1150
  }
1141
- const keyCheck = connectMode === "app" ? null : await verifyKeyOrExit(trimmedKey, options);
1151
+ const keyCheck = keyFromPairing ? null : await verifyKeyOrExit(trimmedKey, options);
1142
1152
  const agents = await resolveAgents(options);
1143
1153
  if (options.dryRun) {
1144
1154
  reportDryRun(trimmedKey, agents, keyCheck);
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  exitOnHelpFlag
4
- } from "../chunk-WXFEK7KD.js";
5
- import "../chunk-7OXIC7E3.js";
4
+ } from "../chunk-674M3JQF.js";
5
+ import "../chunk-KVUBLB6K.js";
6
6
  import {
7
7
  readLedgerSummary
8
8
  } from "../chunk-WLGEY4UX.js";
@@ -22,8 +22,8 @@ import {
22
22
  import "../chunk-BC3VCZ3E.js";
23
23
  import {
24
24
  exitOnHelpFlag
25
- } from "../chunk-WXFEK7KD.js";
26
- import "../chunk-7OXIC7E3.js";
25
+ } from "../chunk-674M3JQF.js";
26
+ import "../chunk-KVUBLB6K.js";
27
27
  import "../chunk-BSZYIAZL.js";
28
28
  import "../chunk-SAF6HGAA.js";
29
29
  import {
@@ -7,8 +7,8 @@ import {
7
7
  } from "../chunk-GMXKITVA.js";
8
8
  import {
9
9
  exitOnHelpFlag
10
- } from "../chunk-WXFEK7KD.js";
11
- import "../chunk-7OXIC7E3.js";
10
+ } from "../chunk-674M3JQF.js";
11
+ import "../chunk-KVUBLB6K.js";
12
12
  import {
13
13
  UsageError
14
14
  } from "../chunk-7QLSKOSU.js";
@@ -31,8 +31,10 @@ import {
31
31
  } from "../chunk-MUEW424A.js";
32
32
  import {
33
33
  exitOnHelpFlag
34
- } from "../chunk-WXFEK7KD.js";
35
- import "../chunk-7OXIC7E3.js";
34
+ } from "../chunk-674M3JQF.js";
35
+ import {
36
+ isNewerVersion
37
+ } from "../chunk-KVUBLB6K.js";
36
38
  import "../chunk-DQAN3JQP.js";
37
39
  import {
38
40
  getApiKey
@@ -239,15 +241,6 @@ var fetchLatestVersion = async () => {
239
241
  return void 0;
240
242
  }
241
243
  };
242
- var isNewer = (a, b) => {
243
- const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
244
- const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
245
- for (let i = 0; i < 3; i++) {
246
- if ((pa[i] ?? 0) > (pb[i] ?? 0)) return true;
247
- if ((pa[i] ?? 0) < (pb[i] ?? 0)) return false;
248
- }
249
- return false;
250
- };
251
244
  var main = async () => {
252
245
  const installed = getInstalledVersion();
253
246
  if (!installed) {
@@ -268,7 +261,7 @@ var main = async () => {
268
261
  return;
269
262
  }
270
263
  const latest = await fetchLatestVersion();
271
- if (latest && !isNewer(latest, installed)) {
264
+ if (latest && !isNewerVersion(latest, installed)) {
272
265
  console.log(`Already on the latest version (${installed}).`);
273
266
  return;
274
267
  }
@@ -14,8 +14,8 @@ import {
14
14
  } from "../chunk-GMXKITVA.js";
15
15
  import {
16
16
  exitOnHelpFlag
17
- } from "../chunk-WXFEK7KD.js";
18
- import "../chunk-7OXIC7E3.js";
17
+ } from "../chunk-674M3JQF.js";
18
+ import "../chunk-KVUBLB6K.js";
19
19
  import "../chunk-SAF6HGAA.js";
20
20
  import {
21
21
  UsageError
@@ -4,7 +4,7 @@ import {
4
4
  getPackageVersion,
5
5
  isCommandName,
6
6
  renderHelp
7
- } from "../chunk-7OXIC7E3.js";
7
+ } from "../chunk-KVUBLB6K.js";
8
8
  import {
9
9
  installProxyDispatcher
10
10
  } from "../chunk-SAF6HGAA.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getPackageVersion,
3
3
  renderCommandHelp
4
- } from "./chunk-7OXIC7E3.js";
4
+ } from "./chunk-KVUBLB6K.js";
5
5
  import {
6
6
  EXIT
7
7
  } from "./chunk-KZERVKTD.js";
@@ -200,11 +200,21 @@ var getPackageVersion = () => {
200
200
  return "0.0.0";
201
201
  }
202
202
  };
203
+ var isNewerVersion = (candidate, current) => {
204
+ const a = candidate.split(".").map((part) => parseInt(part, 10) || 0);
205
+ const b = current.split(".").map((part) => parseInt(part, 10) || 0);
206
+ for (let i = 0; i < 3; i++) {
207
+ if ((a[i] ?? 0) > (b[i] ?? 0)) return true;
208
+ if ((a[i] ?? 0) < (b[i] ?? 0)) return false;
209
+ }
210
+ return false;
211
+ };
203
212
 
204
213
  export {
205
214
  COMMANDS,
206
215
  isCommandName,
207
216
  renderHelp,
208
217
  renderCommandHelp,
209
- getPackageVersion
218
+ getPackageVersion,
219
+ isNewerVersion
210
220
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushary/agent-hooks",
3
- "version": "0.62.2",
3
+ "version": "0.62.4",
4
4
  "description": "Permission hooks for AI coding agents: route tool approvals through Pushary push notifications",
5
5
  "keywords": [
6
6
  "pushary",