@pushary/agent-hooks 0.63.2 → 0.64.1

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,66 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.64.1
4
+
5
+ ### Ctrl-C at the pairing QR printed nothing
6
+
7
+ The QR screen said "No app? Press Ctrl-C for the ways to finish without one", and
8
+ pressing it printed nothing at all. `connectViaAppPairing` handles SIGINT by
9
+ exiting the process itself, so control never returned to setup and the lines it
10
+ meant to show were unreachable on the one path that advertised them.
11
+
12
+ Setup now hands the escape to the pairing wait, which prints it inside the
13
+ handler before exiting, and the line on screen names the command directly instead
14
+ of telling you to interrupt the program to find out what it is.
15
+
16
+ The same two commands are printed when a pairing simply never completes, so both
17
+ ways out of the QR screen say the same thing.
18
+
19
+
20
+ ## 0.64.0
21
+
22
+ ### A typo on `--dry-run` ran a real clean
23
+
24
+ `pushary clean --yes --dryrun`, one missing hyphen, silently dropped the flag it
25
+ did not recognise and performed a full destructive clean: the stored key, the
26
+ Cursor plugin, every agent config, and the global package itself. It printed
27
+ "Clean complete." with the past tense throughout and exited `0`. The typo most
28
+ worth catching was the one on the flag that means "change nothing", sitting next
29
+ to the flag that means "do not ask me".
30
+
31
+ `clean` now rejects a flag it does not know and exits `2` before removing
32
+ anything. `--dry-run`, `--yes` and `-y` are unchanged.
33
+
34
+ ### Setup reuses the key this machine already has
35
+
36
+ `setup` read `PUSHARY_API_KEY` and never `~/.pushary/config.json`, so the key
37
+ `pushary login` had just written was invisible to it. `login` followed by `setup`
38
+ in the same shell ran a second browser login and minted a second key, and a
39
+ machine with no terminal that had been set up by `login` exited "no API key" with
40
+ a working key sitting on disk. The shell export made it work in a *new* shell,
41
+ which is what hid it.
42
+
43
+ Setup now reads the stored key, verifies it, and uses it. A key the server no
44
+ longer recognises falls through to a fresh sign-in rather than dead-ending, and a
45
+ server it cannot reach keeps the key rather than discarding it.
46
+
47
+ ### The Pushary app is the default connect path
48
+
49
+ An unflagged `setup` now pairs the app instead of sending you to the browser
50
+ subscribe page. `--connect browser` is the fallback for a machine with no app,
51
+ `--connect web` is the legacy page and keeps working exactly as before, and
52
+ `--connect none` skips the phone step like `--skip-phone`. `auto` and `pwa` are
53
+ accepted as input spellings.
54
+
55
+ Pairing only happens when there is a terminal to show the QR to and no usable key
56
+ already on the machine, so re-running setup to add an agent no longer asks you to
57
+ scan anything, and no longer mints a key you did not ask for. Typing
58
+ `--connect app` explicitly still pairs every time.
59
+
60
+ When pairing does not complete, setup now names the two ways to finish without
61
+ the app rather than only inviting you to try the QR again.
62
+
63
+
3
64
  ## 0.63.2
4
65
 
5
66
  ### Setup reports what happened, not only that it worked
@@ -29,15 +29,21 @@ import {
29
29
  import {
30
30
  removeClaudeAlias
31
31
  } from "../chunk-BC3VCZ3E.js";
32
+ import {
33
+ rejectUnknownFlags
34
+ } from "../chunk-GMXKITVA.js";
32
35
  import {
33
36
  execNpm
34
37
  } from "../chunk-MUEW424A.js";
35
38
  import {
36
39
  exitOnHelpFlag
37
- } from "../chunk-VYD27DKZ.js";
38
- import "../chunk-PPTC2QDB.js";
40
+ } from "../chunk-ATP6UQPH.js";
41
+ import "../chunk-JGH37QRB.js";
42
+ import "../chunk-7QLSKOSU.js";
39
43
  import "../chunk-DQAN3JQP.js";
40
- import "../chunk-KZERVKTD.js";
44
+ import {
45
+ EXIT
46
+ } from "../chunk-KZERVKTD.js";
41
47
 
42
48
  // bin/pushary-clean.ts
43
49
  import { existsSync, readFileSync, copyFileSync, readdirSync } from "fs";
@@ -149,6 +155,16 @@ var main = async () => {
149
155
  console.log(` ${bold("Pushary Clean")}`);
150
156
  console.log(` ${dim("Removes all Pushary configuration")}`);
151
157
  console.log();
158
+ try {
159
+ rejectUnknownFlags(
160
+ process.argv.slice(2).filter((a) => a !== "clean"),
161
+ ["--dry-run", "--yes", "-y", "--help", "-h"]
162
+ );
163
+ } catch (err) {
164
+ process.stderr.write(` ! ${err instanceof Error ? err.message : String(err)}
165
+ `);
166
+ process.exit(EXIT.USAGE);
167
+ }
152
168
  dryRun = process.argv.includes("--dry-run");
153
169
  if (dryRun) {
154
170
  console.log(` ${bold("Dry run")} ${dim("- nothing will be changed")}`);
@@ -6,11 +6,11 @@ import {
6
6
  confirmAppConnection,
7
7
  connectDevice,
8
8
  printConnectInstructions
9
- } from "../chunk-NGZMVCXN.js";
9
+ } from "../chunk-RASBS2CC.js";
10
10
  import "../chunk-3EGEA4KH.js";
11
11
  import {
12
12
  readKeySource
13
- } from "../chunk-COC6NPWG.js";
13
+ } from "../chunk-NHKWU7MN.js";
14
14
  import "../chunk-6MTNS63X.js";
15
15
  import {
16
16
  checkApiKey
@@ -18,14 +18,14 @@ import {
18
18
  import {
19
19
  createIo
20
20
  } from "../chunk-TX7KBKT7.js";
21
+ import "../chunk-BC3VCZ3E.js";
21
22
  import {
22
23
  parseFlags
23
24
  } from "../chunk-GMXKITVA.js";
24
- import "../chunk-BC3VCZ3E.js";
25
25
  import {
26
26
  exitOnHelpFlag
27
- } from "../chunk-VYD27DKZ.js";
28
- import "../chunk-PPTC2QDB.js";
27
+ } from "../chunk-ATP6UQPH.js";
28
+ import "../chunk-JGH37QRB.js";
29
29
  import "../chunk-BSZYIAZL.js";
30
30
  import "../chunk-SAF6HGAA.js";
31
31
  import {
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  HEARTBEAT_INTERVAL_MS,
4
4
  sendHeartbeat
5
- } from "../chunk-R22IAMSS.js";
5
+ } from "../chunk-YZT2CBC3.js";
6
6
  import {
7
7
  spawnClaude
8
8
  } from "../chunk-XHKBHWLX.js";
@@ -11,8 +11,8 @@ import {
11
11
  } from "../chunk-MUEW424A.js";
12
12
  import {
13
13
  exitOnHelpFlag
14
- } from "../chunk-VYD27DKZ.js";
15
- import "../chunk-PPTC2QDB.js";
14
+ } from "../chunk-ATP6UQPH.js";
15
+ import "../chunk-JGH37QRB.js";
16
16
  import {
17
17
  getMachineId
18
18
  } from "../chunk-RN3NOEJF.js";
@@ -34,12 +34,12 @@ import {
34
34
  describeReach,
35
35
  fetchChannels,
36
36
  reachVerdict
37
- } from "../chunk-NGZMVCXN.js";
37
+ } from "../chunk-RASBS2CC.js";
38
38
  import "../chunk-3EGEA4KH.js";
39
39
  import {
40
40
  keyPrefix,
41
41
  readKeySource
42
- } from "../chunk-COC6NPWG.js";
42
+ } from "../chunk-NHKWU7MN.js";
43
43
  import "../chunk-6MTNS63X.js";
44
44
  import {
45
45
  checkApiKey,
@@ -48,19 +48,19 @@ import {
48
48
  import {
49
49
  createIo
50
50
  } from "../chunk-TX7KBKT7.js";
51
+ import "../chunk-BC3VCZ3E.js";
51
52
  import {
52
53
  parseFlags
53
54
  } from "../chunk-GMXKITVA.js";
54
- import "../chunk-BC3VCZ3E.js";
55
55
  import {
56
56
  execNpm
57
57
  } from "../chunk-MUEW424A.js";
58
58
  import {
59
59
  exitOnHelpFlag
60
- } from "../chunk-VYD27DKZ.js";
60
+ } from "../chunk-ATP6UQPH.js";
61
61
  import {
62
62
  getPackageVersion
63
- } from "../chunk-PPTC2QDB.js";
63
+ } from "../chunk-JGH37QRB.js";
64
64
  import {
65
65
  readLedgerSummary
66
66
  } from "../chunk-WLGEY4UX.js";
@@ -102,12 +102,13 @@ var globalVersion = () => {
102
102
  };
103
103
  var collectBundle = async (options2 = {}) => {
104
104
  const now = options2.now ?? (() => /* @__PURE__ */ new Date());
105
+ const readGlobalVersion = options2.readGlobalVersion ?? globalVersion;
105
106
  const source = readKeySource();
106
107
  const identity = options2.identity ?? null;
107
108
  return {
108
109
  generatedAt: now().toISOString(),
109
110
  cliVersion: getPackageVersion(),
110
- globalVersion: globalVersion(),
111
+ globalVersion: readGlobalVersion(),
111
112
  node: process.version,
112
113
  platform: process.platform,
113
114
  arch: process.arch,
@@ -5,8 +5,8 @@ import {
5
5
  import "../chunk-CAJZAFVS.js";
6
6
  import {
7
7
  exitOnHelpFlag
8
- } from "../chunk-VYD27DKZ.js";
9
- import "../chunk-PPTC2QDB.js";
8
+ } from "../chunk-ATP6UQPH.js";
9
+ import "../chunk-JGH37QRB.js";
10
10
  import "../chunk-YHG74UFF.js";
11
11
  import "../chunk-5ZDYDU2Q.js";
12
12
  import "../chunk-WLGEY4UX.js";
@@ -6,7 +6,7 @@ import "../chunk-3EGEA4KH.js";
6
6
  import {
7
7
  readKeySource,
8
8
  writeKey
9
- } from "../chunk-COC6NPWG.js";
9
+ } from "../chunk-NHKWU7MN.js";
10
10
  import "../chunk-6MTNS63X.js";
11
11
  import {
12
12
  checkApiKey,
@@ -15,16 +15,16 @@ import {
15
15
  import {
16
16
  createIo
17
17
  } from "../chunk-TX7KBKT7.js";
18
+ import "../chunk-BC3VCZ3E.js";
18
19
  import {
19
20
  parseFlags
20
21
  } from "../chunk-GMXKITVA.js";
21
- import "../chunk-BC3VCZ3E.js";
22
22
  import {
23
23
  exitOnHelpFlag
24
- } from "../chunk-VYD27DKZ.js";
24
+ } from "../chunk-ATP6UQPH.js";
25
25
  import {
26
26
  getPackageVersion
27
- } from "../chunk-PPTC2QDB.js";
27
+ } from "../chunk-JGH37QRB.js";
28
28
  import "../chunk-SAF6HGAA.js";
29
29
  import {
30
30
  UsageError
@@ -8,19 +8,19 @@ import {
8
8
  import {
9
9
  clearKey,
10
10
  readKeySource
11
- } from "../chunk-COC6NPWG.js";
11
+ } from "../chunk-NHKWU7MN.js";
12
12
  import "../chunk-6MTNS63X.js";
13
13
  import {
14
14
  createIo
15
15
  } from "../chunk-TX7KBKT7.js";
16
+ import "../chunk-BC3VCZ3E.js";
16
17
  import {
17
18
  parseFlags
18
19
  } from "../chunk-GMXKITVA.js";
19
- import "../chunk-BC3VCZ3E.js";
20
20
  import {
21
21
  exitOnHelpFlag
22
- } from "../chunk-VYD27DKZ.js";
23
- import "../chunk-PPTC2QDB.js";
22
+ } from "../chunk-ATP6UQPH.js";
23
+ import "../chunk-JGH37QRB.js";
24
24
  import {
25
25
  UsageError
26
26
  } from "../chunk-7QLSKOSU.js";
@@ -11,8 +11,8 @@ import {
11
11
  } from "../chunk-GMXKITVA.js";
12
12
  import {
13
13
  exitOnHelpFlag
14
- } from "../chunk-VYD27DKZ.js";
15
- import "../chunk-PPTC2QDB.js";
14
+ } from "../chunk-ATP6UQPH.js";
15
+ import "../chunk-JGH37QRB.js";
16
16
  import "../chunk-SAF6HGAA.js";
17
17
  import {
18
18
  UsageError
@@ -45,15 +45,16 @@ import {
45
45
  connectViaAppPairing,
46
46
  printConnectInstructions,
47
47
  setHumanStream
48
- } from "../chunk-NGZMVCXN.js";
48
+ } from "../chunk-RASBS2CC.js";
49
49
  import "../chunk-3EGEA4KH.js";
50
50
  import {
51
51
  KEY_FILE_MODE,
52
52
  exportLine,
53
53
  keyPrefix,
54
+ readConfigFileKey,
54
55
  readKeySource,
55
56
  writeKey
56
- } from "../chunk-COC6NPWG.js";
57
+ } from "../chunk-NHKWU7MN.js";
57
58
  import {
58
59
  backupFile,
59
60
  readJsonSafe,
@@ -64,13 +65,13 @@ import {
64
65
  checkApiKey,
65
66
  describeKeyCheck
66
67
  } from "../chunk-G4TARJAY.js";
67
- import {
68
- parseFlags
69
- } from "../chunk-GMXKITVA.js";
70
68
  import {
71
69
  installClaudeAlias,
72
70
  resolveShellRc
73
71
  } from "../chunk-BC3VCZ3E.js";
72
+ import {
73
+ parseFlags
74
+ } from "../chunk-GMXKITVA.js";
74
75
  import {
75
76
  installGlobally,
76
77
  npmErrorMessage,
@@ -82,7 +83,7 @@ import {
82
83
  getPackageVersion,
83
84
  isNewerVersion,
84
85
  renderCommandHelp
85
- } from "../chunk-PPTC2QDB.js";
86
+ } from "../chunk-JGH37QRB.js";
86
87
  import {
87
88
  reportEvent
88
89
  } from "../chunk-5ZDYDU2Q.js";
@@ -234,6 +235,14 @@ var reportSetupOutcome = async (outcome) => {
234
235
 
235
236
  // src/setup/options.ts
236
237
  var AGENT_CHOICES = ["claude_code", "codex", "gemini_cli", "hermes", "cursor", "custom"];
238
+ var CONNECT_MODES = ["app", "browser", "web", "none"];
239
+ var CONNECT_ALIASES = { auto: "app", pwa: "web" };
240
+ var parseConnectMode = (raw) => {
241
+ const value = raw.trim().toLowerCase();
242
+ if (Object.hasOwn(CONNECT_ALIASES, value)) return CONNECT_ALIASES[value];
243
+ if (CONNECT_MODES.includes(value)) return value;
244
+ throw new UsageError(`--connect must be one of: ${CONNECT_MODES.join(", ")} (got ${raw})`);
245
+ };
237
246
  var SETUP_FLAGS = {
238
247
  bools: ["key-stdin", "skip-phone", "yes", "y", "json", "dry-run", "help", "h"],
239
248
  values: ["key", "connect", "agents"]
@@ -272,18 +281,19 @@ var parseSetupOptions = (input2) => {
272
281
  const agentsFlag = flags.values.get("agents");
273
282
  const agents = parseAgentSelection(agentsFlag, yes);
274
283
  const connectRaw = flags.values.get("connect");
275
- if (connectRaw !== void 0 && connectRaw !== "web" && connectRaw !== "app") {
276
- throw new UsageError(`--connect must be one of: web, app (got ${connectRaw})`);
277
- }
284
+ const connectMode = connectRaw === void 0 ? "app" : parseConnectMode(connectRaw);
278
285
  const key = flags.values.get("key")?.trim() || null;
279
286
  const keyStdin = flags.bools.has("key-stdin");
280
287
  if (key && keyStdin) {
281
288
  throw new UsageError("Pass either --key or --key-stdin, not both");
282
289
  }
283
290
  return {
284
- connectMode: connectRaw === "app" ? "app" : "web",
291
+ connectMode,
285
292
  connectExplicit: connectRaw !== void 0,
286
- skipPhone: flags.bools.has("skip-phone"),
293
+ // `--connect none` and `--skip-phone` are the same instruction, so they
294
+ // resolve to the same field rather than being checked separately at the two
295
+ // call sites that would inevitably drift apart.
296
+ skipPhone: flags.bools.has("skip-phone") || connectMode === "none",
287
297
  // Anything that means "there is nobody to answer a prompt". A non-TTY stdin
288
298
  // is included because that is the case that used to hang: it is better to
289
299
  // take the defaults and say so than to wait forever for a keypress.
@@ -942,14 +952,18 @@ var offerClaudeAlias = async (options) => {
942
952
  console.log(` ${check} ${dim("The")} ${cyan("claude")} ${dim("alias is already set.")}`);
943
953
  }
944
954
  };
945
- var verifyKeyOrExit = async (apiKey, options) => {
946
- const live = process.stdout.isTTY === true && !options.json;
955
+ var verifyKeyOrExit = async (apiKey, options, precomputed) => {
956
+ const live = process.stdout.isTTY === true && !options.json && precomputed === void 0;
947
957
  if (live) writeHuman(` ${dim("...")} Verifying your key with pushary.com`);
948
958
  let result;
949
- try {
950
- result = await checkApiKey(apiKey);
951
- } catch (err) {
952
- result = { kind: "unreachable", detail: formatError(err) };
959
+ if (precomputed !== void 0) {
960
+ result = precomputed;
961
+ } else {
962
+ try {
963
+ result = await checkApiKey(apiKey);
964
+ } catch (err) {
965
+ result = { kind: "unreachable", detail: formatError(err) };
966
+ }
953
967
  }
954
968
  if (live) writeHuman("\r\x1B[2K");
955
969
  const report = describeKeyCheck(result);
@@ -968,6 +982,28 @@ var verifyKeyOrExit = async (apiKey, options) => {
968
982
  }
969
983
  return result;
970
984
  };
985
+ var printPairingEscape = () => {
986
+ console.log();
987
+ console.log(` ${yellow("!")} No app? Two ways to finish without one:`);
988
+ console.log();
989
+ console.log(` ${cyan("npx @pushary/agent-hooks setup --connect browser")}`);
990
+ console.log(` ${dim("Approve in a browser tab. No phone needed.")}`);
991
+ console.log();
992
+ console.log(` ${cyan("npx @pushary/agent-hooks setup --key pk_...")}`);
993
+ console.log(` ${dim("Paste a key from")} ${cyan("pushary.com/dashboard/agent/settings")}`);
994
+ console.log();
995
+ console.log(` ${dim("Or get the app at")} ${cyan("https://pushary.com/download")} ${dim("and re-run setup.")}`);
996
+ console.log();
997
+ };
998
+ var usableStoredKey = async (apiKey) => {
999
+ let result;
1000
+ try {
1001
+ result = await checkApiKey(apiKey);
1002
+ } catch (err) {
1003
+ result = { kind: "unreachable", detail: formatError(err) };
1004
+ }
1005
+ return result.kind === "rejected" || result.kind === "malformed" ? null : result;
1006
+ };
971
1007
  var AGENT_NAMES = {
972
1008
  claude_code: "Claude Code",
973
1009
  codex: "Codex",
@@ -1108,20 +1144,41 @@ var main = async () => {
1108
1144
  const envKey = process.env.PUSHARY_API_KEY?.trim();
1109
1145
  let trimmedKey;
1110
1146
  let keyFromPairing = false;
1147
+ const storedKey = readConfigFileKey();
1148
+ const pairingRequested = connectMode === "app" && options.connectExplicit && !options.skipPhone;
1149
+ const storedCheck = !(flagKey && isValidApiKey(flagKey)) && !pairingRequested && !(envKey && isValidApiKey(envKey)) && storedKey !== null && isValidApiKey(storedKey) ? await usableStoredKey(storedKey) : null;
1150
+ let storedKeyCheck = null;
1111
1151
  if (flagKey && isValidApiKey(flagKey)) {
1112
1152
  const masked = `${flagKey.slice(0, 8)}...${flagKey.slice(-4)}`;
1113
1153
  console.log(` ${check} Using API key: ${dim(masked)}`);
1114
1154
  console.log();
1115
1155
  trimmedKey = flagKey;
1116
- } else if (connectMode === "app") {
1156
+ } else if (storedKey !== null && storedCheck !== null) {
1157
+ const masked = `${storedKey.slice(0, 8)}...${storedKey.slice(-4)}`;
1158
+ console.log(` ${check} Using the key already on this machine: ${dim(masked)}`);
1159
+ console.log(` ${dim(PUSHARY_CONFIG_FILE)}`);
1160
+ console.log();
1161
+ trimmedKey = storedKey;
1162
+ storedKeyCheck = storedCheck;
1163
+ } else if (!pairingRequested && envKey && isValidApiKey(envKey)) {
1164
+ const masked = `${envKey.slice(0, 8)}...${envKey.slice(-4)}`;
1165
+ console.log(` ${check} Found API key in environment: ${dim(masked)}`);
1166
+ console.log();
1167
+ const useExisting = options.nonInteractive ? true : await confirm({ message: "Use this key?", default: true });
1168
+ if (useExisting) {
1169
+ trimmedKey = envKey;
1170
+ } else {
1171
+ const apiKey = await input({ message: "API key:" });
1172
+ trimmedKey = apiKey.trim();
1173
+ }
1174
+ } else if (connectMode === "app" && canRunBrowserLogin() && !options.skipPhone) {
1117
1175
  console.log(` ${dim("Pairing with your Pushary app.")}`);
1176
+ console.log(` ${dim("No app? Ctrl-C, then:")} ${cyan("npx @pushary/agent-hooks setup --connect browser")}`);
1118
1177
  const previous = readKeySource();
1119
- const paired = await connectViaAppPairing();
1178
+ const paired = await connectViaAppPairing({ onCancelled: printPairingEscape });
1120
1179
  if (!paired) {
1121
- console.log();
1122
- console.log(` ${yellow("!")} Pairing didn't complete. Re-run setup when you're ready to scan.`);
1123
- console.log(` ${dim("No app yet? Get it at")} ${cyan("https://pushary.com/download")}`);
1124
- process.exit(1);
1180
+ printPairingEscape();
1181
+ process.exit(EXIT.NOT_CONFIGURED);
1125
1182
  }
1126
1183
  trimmedKey = paired.apiKey;
1127
1184
  keyFromPairing = true;
@@ -1133,17 +1190,6 @@ var main = async () => {
1133
1190
  console.log(` ${dim("Revoke it at")} ${cyan("pushary.com/dashboard/agent/settings")} ${dim("if nothing else uses it.")}`);
1134
1191
  }
1135
1192
  console.log();
1136
- } else if (envKey && isValidApiKey(envKey)) {
1137
- const masked = `${envKey.slice(0, 8)}...${envKey.slice(-4)}`;
1138
- console.log(` ${check} Found API key in environment: ${dim(masked)}`);
1139
- console.log();
1140
- const useExisting = options.nonInteractive ? true : await confirm({ message: "Use this key?", default: true });
1141
- if (useExisting) {
1142
- trimmedKey = envKey;
1143
- } else {
1144
- const apiKey = await input({ message: "API key:" });
1145
- trimmedKey = apiKey.trim();
1146
- }
1147
1193
  } else if (!canRunBrowserLogin()) {
1148
1194
  console.log(` ${yellow("!")} No API key, and no terminal to sign in from.`);
1149
1195
  console.log(` ${dim("Pass --key <pk_xxx.xxx>, or --key-stdin, or export PUSHARY_API_KEY.")}`);
@@ -1187,7 +1233,7 @@ var main = async () => {
1187
1233
  `);
1188
1234
  process.exit(EXIT.USAGE);
1189
1235
  }
1190
- const keyCheck = keyFromPairing ? null : await verifyKeyOrExit(trimmedKey, options);
1236
+ const keyCheck = keyFromPairing ? null : await verifyKeyOrExit(trimmedKey, options, storedKeyCheck ?? void 0);
1191
1237
  const agents = await resolveAgents(options);
1192
1238
  if (options.dryRun) {
1193
1239
  reportDryRun(trimmedKey, agents, keyCheck);
@@ -1238,8 +1284,10 @@ var main = async () => {
1238
1284
  console.log(` ${dim("Skipping the phone check (--skip-phone).")}`);
1239
1285
  } else if (!process.stdout.isTTY) {
1240
1286
  phone = await printConnectInstructions(trimmedKey);
1241
- } else if (connectMode === "app") {
1287
+ } else if (keyFromPairing) {
1242
1288
  phone = await confirmAppConnection(trimmedKey);
1289
+ } else if (connectMode === "app") {
1290
+ phone = await printConnectInstructions(trimmedKey);
1243
1291
  } else {
1244
1292
  phone = await connectDevice(trimmedKey, { showCapabilities: !options.connectExplicit });
1245
1293
  }
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  exitOnHelpFlag
4
- } from "../chunk-VYD27DKZ.js";
5
- import "../chunk-PPTC2QDB.js";
4
+ } from "../chunk-ATP6UQPH.js";
5
+ import "../chunk-JGH37QRB.js";
6
6
  import {
7
7
  readLedgerSummary
8
8
  } from "../chunk-WLGEY4UX.js";
@@ -2,16 +2,16 @@
2
2
  import {
3
3
  describeLastSeen,
4
4
  listMachines
5
- } from "../chunk-R22IAMSS.js";
5
+ } from "../chunk-YZT2CBC3.js";
6
6
  import {
7
7
  describeReach,
8
8
  fetchChannels,
9
9
  reachVerdict
10
- } from "../chunk-NGZMVCXN.js";
10
+ } from "../chunk-RASBS2CC.js";
11
11
  import "../chunk-3EGEA4KH.js";
12
12
  import {
13
13
  readKeySource
14
- } from "../chunk-COC6NPWG.js";
14
+ } from "../chunk-NHKWU7MN.js";
15
15
  import "../chunk-6MTNS63X.js";
16
16
  import {
17
17
  checkApiKey,
@@ -20,14 +20,14 @@ import {
20
20
  import {
21
21
  createIo
22
22
  } from "../chunk-TX7KBKT7.js";
23
+ import "../chunk-BC3VCZ3E.js";
23
24
  import {
24
25
  rejectUnknownFlags
25
26
  } from "../chunk-GMXKITVA.js";
26
- import "../chunk-BC3VCZ3E.js";
27
27
  import {
28
28
  exitOnHelpFlag
29
- } from "../chunk-VYD27DKZ.js";
30
- import "../chunk-PPTC2QDB.js";
29
+ } from "../chunk-ATP6UQPH.js";
30
+ import "../chunk-JGH37QRB.js";
31
31
  import "../chunk-BSZYIAZL.js";
32
32
  import "../chunk-SAF6HGAA.js";
33
33
  import {
@@ -7,8 +7,8 @@ import {
7
7
  } from "../chunk-GMXKITVA.js";
8
8
  import {
9
9
  exitOnHelpFlag
10
- } from "../chunk-VYD27DKZ.js";
11
- import "../chunk-PPTC2QDB.js";
10
+ } from "../chunk-ATP6UQPH.js";
11
+ import "../chunk-JGH37QRB.js";
12
12
  import {
13
13
  UsageError
14
14
  } from "../chunk-7QLSKOSU.js";
@@ -34,10 +34,10 @@ import {
34
34
  } from "../chunk-MUEW424A.js";
35
35
  import {
36
36
  exitOnHelpFlag
37
- } from "../chunk-VYD27DKZ.js";
37
+ } from "../chunk-ATP6UQPH.js";
38
38
  import {
39
39
  isNewerVersion
40
- } from "../chunk-PPTC2QDB.js";
40
+ } from "../chunk-JGH37QRB.js";
41
41
  import "../chunk-DQAN3JQP.js";
42
42
  import {
43
43
  getApiKey
@@ -14,8 +14,8 @@ import {
14
14
  } from "../chunk-GMXKITVA.js";
15
15
  import {
16
16
  exitOnHelpFlag
17
- } from "../chunk-VYD27DKZ.js";
18
- import "../chunk-PPTC2QDB.js";
17
+ } from "../chunk-ATP6UQPH.js";
18
+ import "../chunk-JGH37QRB.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-PPTC2QDB.js";
7
+ } from "../chunk-JGH37QRB.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-PPTC2QDB.js";
4
+ } from "./chunk-JGH37QRB.js";
5
5
  import {
6
6
  EXIT
7
7
  } from "./chunk-KZERVKTD.js";
@@ -105,8 +105,10 @@ var SETUP_OPTIONS = [
105
105
  ["--key-stdin", "Read the API key from the first line of stdin, so it never appears in argv or shell history"],
106
106
  ["--agents <list>", 'Comma-separated agent ids, or "auto" for everything detected, or "none" to save the key only'],
107
107
  ["--yes, -y", "Accept every default and ask nothing. Implies --agents auto unless --agents is given"],
108
- ["--connect app", "Pair the native Pushary app, no key to paste"],
109
- ["--connect web", "Connect your phone via the browser subscribe page (default)"],
108
+ ["--connect app", "Pair the native Pushary app, no key to paste (default)"],
109
+ ["--connect browser", "Approve in a browser tab instead. For a machine with no app"],
110
+ ["--connect web", "Legacy browser subscribe page"],
111
+ ["--connect none", "Skip the phone connect step, same as --skip-phone"],
110
112
  ["--skip-phone", "Skip the phone connect step"],
111
113
  ["--dry-run", "Print what would be written and change nothing"],
112
114
  ["--json", "Emit one machine-readable object instead of the human report"]
@@ -169,6 +169,7 @@ var readConfigKey = (configPath) => {
169
169
  const key = typeof result.value.apiKey === "string" ? result.value.apiKey.trim() : "";
170
170
  return key || null;
171
171
  };
172
+ var readConfigFileKey = () => readConfigKey(configFilePath());
172
173
  var readKeySource = () => {
173
174
  const configPath = configFilePath();
174
175
  const envKey = process.env.PUSHARY_API_KEY?.trim() || null;
@@ -185,5 +186,6 @@ export {
185
186
  keyPrefix,
186
187
  writeKey,
187
188
  clearKey,
189
+ readConfigFileKey,
188
190
  readKeySource
189
191
  };
@@ -494,7 +494,7 @@ var buildPairLinks = (pairId, publicKeyB64, baseUrl = apiBase()) => {
494
494
  deepLink: `pushary://pair?pk=${pk}&id=${id}`
495
495
  };
496
496
  };
497
- var connectViaAppPairing = async () => {
497
+ var connectViaAppPairing = async (options = {}) => {
498
498
  const keypair = generateKeypair();
499
499
  const pairId = await startPairing(keypair.publicKeyB64);
500
500
  if (!pairId) {
@@ -520,6 +520,7 @@ var connectViaAppPairing = async () => {
520
520
  const onInterrupt = () => {
521
521
  cancelled = true;
522
522
  stop(yellow("!"), "Cancelled");
523
+ options.onCancelled?.();
523
524
  void cancelPairing(pairId).finally(() => process.exit(EXIT_ABORTED));
524
525
  };
525
526
  process.once("SIGINT", onInterrupt);
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getPackageVersion
3
- } from "./chunk-PPTC2QDB.js";
3
+ } from "./chunk-JGH37QRB.js";
4
4
  import {
5
5
  getMachineId
6
6
  } from "./chunk-RN3NOEJF.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushary/agent-hooks",
3
- "version": "0.63.2",
3
+ "version": "0.64.1",
4
4
  "description": "Permission hooks for AI coding agents: route tool approvals through Pushary push notifications",
5
5
  "keywords": [
6
6
  "pushary",