@pushary/agent-hooks 0.61.0 → 0.62.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.
Files changed (45) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/dist/bin/pushary-claude.js +4 -4
  3. package/dist/bin/pushary-clean.js +82 -35
  4. package/dist/bin/pushary-codex-hook.js +4 -4
  5. package/dist/bin/pushary-codex.js +3 -3
  6. package/dist/bin/pushary-connect.js +7 -6
  7. package/dist/bin/pushary-daemon.js +37 -5
  8. package/dist/bin/pushary-doctor.js +114 -34
  9. package/dist/bin/pushary-gemini-hook.js +4 -4
  10. package/dist/bin/pushary-hook.js +5 -5
  11. package/dist/bin/pushary-login.js +5 -4
  12. package/dist/bin/pushary-logout.js +52 -9
  13. package/dist/bin/pushary-mode.js +4 -4
  14. package/dist/bin/pushary-notification-hook.js +2 -2
  15. package/dist/bin/pushary-permission-denied-hook.js +3 -3
  16. package/dist/bin/pushary-permission-hook.js +3 -3
  17. package/dist/bin/pushary-post-hook.js +2 -2
  18. package/dist/bin/pushary-prompt-hook.js +2 -2
  19. package/dist/bin/pushary-session-end-hook.js +2 -2
  20. package/dist/bin/pushary-session-start-hook.js +2 -2
  21. package/dist/bin/pushary-setup.js +97 -155
  22. package/dist/bin/pushary-stats.js +2 -2
  23. package/dist/bin/pushary-status.js +6 -5
  24. package/dist/bin/pushary-stop-hook.js +2 -2
  25. package/dist/bin/pushary-stopfailure-hook.js +2 -2
  26. package/dist/bin/pushary-suggestions.js +2 -2
  27. package/dist/bin/pushary-upgrade.js +189 -18
  28. package/dist/bin/pushary-wait.js +4 -4
  29. package/dist/bin/pushary.js +1 -1
  30. package/dist/{chunk-GX64YGU3.js → chunk-3O27ZSRE.js} +25 -0
  31. package/dist/{chunk-HI4AGGE6.js → chunk-5SO3CEGJ.js} +4 -4
  32. package/dist/chunk-6MTNS63X.js +55 -0
  33. package/dist/{chunk-B26DNXCA.js → chunk-COC6NPWG.js} +13 -59
  34. package/dist/chunk-E2U35RLD.js +108 -0
  35. package/dist/chunk-ER657KRJ.js +168 -0
  36. package/dist/{chunk-HUJQSP4F.js → chunk-IIZZYUWK.js} +1 -1
  37. package/dist/chunk-N7XJ4L2W.js +79 -0
  38. package/dist/{chunk-R6C4USIV.js → chunk-ONVEYEVR.js} +2 -2
  39. package/dist/{chunk-SML23YOT.js → chunk-SP7OZXCQ.js} +10 -2
  40. package/dist/chunk-UXXRRXUS.js +93 -0
  41. package/dist/{chunk-UKNAAVEE.js → chunk-YJ7YZRVV.js} +1 -1
  42. package/dist/src/index.js +3 -3
  43. package/package.json +1 -1
  44. package/dist/chunk-S4TFBJ5O.js +0 -339
  45. package/dist/{chunk-VFBYRR2N.js → chunk-HIIBSYFJ.js} +3 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,81 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.62.1
4
+
5
+ ### `pushary clean --dry-run` was not a dry run
6
+
7
+ In 0.62.0 the flag ran four real operations while printing "would":
8
+
9
+ - uninstalled the global `@pushary/agent-hooks` package
10
+ - rewrote your shell profile to remove the `claude` alias
11
+ - stripped the Pushary block from `~/.codex/AGENTS.md`
12
+ - stripped it from `~/.gemini/GEMINI.md`
13
+
14
+ If you ran `pushary clean --dry-run` on 0.62.0 and the CLI then seemed to
15
+ disappear, that is why. Reinstall with `npm i -g @pushary/agent-hooks`, and check
16
+ your shell profile and those two files against version control if you keep them
17
+ there.
18
+
19
+ ### `pushary upgrade` could destroy `~/.claude.json`
20
+
21
+ `upgrade` treated an unparseable config as an empty one and wrote it back, so a
22
+ truncated `~/.claude.json` was replaced with nothing but the Pushary MCP entry,
23
+ losing every project, MCP server and permission rule in it. A half-written
24
+ `~/.claude.json` is what an interrupted write leaves behind, so this needed only
25
+ one earlier crash to line up.
26
+
27
+ A config that cannot be parsed is now left untouched, and the other agent files
28
+ are still refreshed around it.
29
+
30
+
31
+ ## 0.62.0
32
+
33
+ ### `pushary clean` could leave your API key on disk
34
+
35
+ If `CODEX_HOME` was set, `clean` looked for Codex's `config.toml` in `~/.codex`
36
+ and never found the real one. It printed `Codex config (not found)`, exited `0`,
37
+ and left the key in the file. A user who ran `clean` to remove their key was told
38
+ it had worked.
39
+
40
+ The same fault ran through the rest of the Codex path. With `CODEX_HOME` set:
41
+
42
+ - `setup` wrote the MCP server, the trust hashes and the notify entry to
43
+ `~/.codex/config.toml` while writing `hooks.json`, `AGENTS.md` and the skill to
44
+ the real `CODEX_HOME`, splitting the install across two directories
45
+ - `setup --dry-run` named the relocated path, so the preview and the real run
46
+ disagreed about where the key would land
47
+ - `doctor` read the relocated config and reported the key missing
48
+ - `logout` treated an exported-but-empty `CODEX_HOME` as a path relative to the
49
+ current directory
50
+
51
+ If you use `CODEX_HOME`, run `pushary clean --dry-run` to see what is still
52
+ there, and check `$CODEX_HOME/config.toml` for a key you thought was gone.
53
+
54
+ ### New
55
+
56
+ - `pushary clean --dry-run` lists everything it would remove and changes nothing
57
+ - `pushary doctor --bundle` writes a redacted diagnostics file for a support
58
+ thread. Keys, your home directory and the machine name are stripped.
59
+ - `pushary logout --revoke` kills the key server-side as well as locally, so it
60
+ stops working everywhere rather than only on this machine
61
+
62
+ ### Changed
63
+
64
+ - `pushary upgrade` now refreshes Codex, Gemini CLI and Cursor, not only Claude
65
+ Code, and names the agents it actually touched. It matters most for Codex,
66
+ whose hooks are trusted by hash: a refreshed hook command without a refreshed
67
+ trust entry is a hook Codex silently declines to run.
68
+ - `pushary upgrade` with no global install now names the install modes it can
69
+ see and the command that updates each, instead of one generic line and exit 0
70
+ - `pushary daemon` stops on a rejected key or a closed plan and exits `4`
71
+ (`UNAUTHENTICATED`). It used to treat that like a network blip, back off to a
72
+ two-minute poll and sit there under an `online` banner, so a revoked key left a
73
+ daemon that looked healthy and would never launch anything again.
74
+ - Ctrl-C during app pairing now cancels the pairing on the server, so a QR still
75
+ on your screen stops being claimable immediately instead of staying live for
76
+ the rest of its five minutes
77
+ - `setup` prints the command surface when it finishes
78
+
3
79
  ## 0.61.0
4
80
 
5
81
  The largest release since the CLI shipped. Setup no longer asks you to paste an
@@ -17,14 +17,14 @@ import {
17
17
  reportEvent,
18
18
  resolvePolicy,
19
19
  waitForAnswer
20
- } from "../chunk-VFBYRR2N.js";
21
- import {
22
- getMachineId
23
- } from "../chunk-RN3NOEJF.js";
20
+ } from "../chunk-HIIBSYFJ.js";
24
21
  import "../chunk-HNTKTK5B.js";
25
22
  import {
26
23
  redactSecrets
27
24
  } from "../chunk-DQAN3JQP.js";
25
+ import {
26
+ getMachineId
27
+ } from "../chunk-RN3NOEJF.js";
28
28
  import {
29
29
  getApiKey,
30
30
  getBaseUrl
@@ -4,10 +4,18 @@ import {
4
4
  removePusharySettings
5
5
  } from "../chunk-CAJZAFVS.js";
6
6
  import {
7
- removeCodexHooks,
8
- removeGeminiSettings,
9
7
  removeInstructionBlock
10
- } from "../chunk-S4TFBJ5O.js";
8
+ } from "../chunk-N7XJ4L2W.js";
9
+ import {
10
+ removeGeminiSettings
11
+ } from "../chunk-E2U35RLD.js";
12
+ import {
13
+ codexAgentsMd,
14
+ codexConfigToml,
15
+ codexHooksJson,
16
+ codexSkillDir,
17
+ removeCodexHooks
18
+ } from "../chunk-ER657KRJ.js";
11
19
  import {
12
20
  removeClaudeAlias
13
21
  } from "../chunk-BC3VCZ3E.js";
@@ -16,8 +24,8 @@ import {
16
24
  } from "../chunk-MUEW424A.js";
17
25
  import {
18
26
  exitOnHelpFlag
19
- } from "../chunk-HUJQSP4F.js";
20
- import "../chunk-SML23YOT.js";
27
+ } from "../chunk-IIZZYUWK.js";
28
+ import "../chunk-SP7OZXCQ.js";
21
29
  import "../chunk-DQAN3JQP.js";
22
30
 
23
31
  // bin/pushary-clean.ts
@@ -66,8 +74,38 @@ var readJson = (path) => {
66
74
  return null;
67
75
  }
68
76
  };
77
+ var dryRun = false;
78
+ var did = (past) => dryRun ? `(would ${past === "cleaned" ? "clean" : past === "removed" ? "remove" : past === "uninstalled" ? "uninstall" : past})` : `(${past})`;
79
+ var wouldNote = (action, path) => {
80
+ console.log(` ${dim("would")} ${action} ${path}`);
81
+ };
82
+ var guardedWrite = (path, contents) => {
83
+ if (dryRun) return wouldNote("rewrite", path);
84
+ writeFileSync(path, contents, "utf-8");
85
+ };
86
+ var guardedRemove = (path, options = {}) => {
87
+ if (dryRun) return wouldNote("remove ", path);
88
+ rmSync(path, { recursive: true, force: options.force });
89
+ };
90
+ var guardedExec = (command, options, describe) => {
91
+ if (dryRun) return wouldNote("run ", describe);
92
+ execSync(command, options);
93
+ };
94
+ var guardedInstructionBlockRemoval = (path) => {
95
+ if (!dryRun) return removeInstructionBlock(path);
96
+ if (!existsSync(path)) return false;
97
+ const hasBlock = readFileSync(path, "utf-8").includes("pushary");
98
+ if (hasBlock) wouldNote("rewrite", path);
99
+ return hasBlock;
100
+ };
101
+ var guardedAliasRemoval = () => {
102
+ if (!dryRun) return removeClaudeAlias();
103
+ const shells = [".zshrc", ".bashrc", ".bash_profile", ".profile"].map((name) => join(homedir(), name)).filter((path) => existsSync(path) && readFileSync(path, "utf-8").includes("pushary claude"));
104
+ for (const path of shells) wouldNote("rewrite", path);
105
+ return shells;
106
+ };
69
107
  var writeJson = (path, data) => {
70
- writeFileSync(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
108
+ guardedWrite(path, JSON.stringify(data, null, 2) + "\n");
71
109
  };
72
110
  var cleanSettingsFile = (path, label) => {
73
111
  const data = readJson(path);
@@ -78,7 +116,7 @@ var cleanSettingsFile = (path, label) => {
78
116
  const changed = removePusharySettings(data);
79
117
  if (changed) {
80
118
  writeJson(path, data);
81
- console.log(` ${check} ${label} ${dim("(cleaned)")}`);
119
+ console.log(` ${check} ${label} ${dim(did("cleaned"))}`);
82
120
  } else {
83
121
  console.log(` ${skip} ${label} ${dim("(no pushary entries)")}`);
84
122
  }
@@ -88,7 +126,12 @@ var main = async () => {
88
126
  console.log(` ${bold("Pushary Clean")}`);
89
127
  console.log(` ${dim("Removes all Pushary configuration")}`);
90
128
  console.log();
91
- const assumeYes = process.argv.includes("--yes") || process.argv.includes("-y");
129
+ dryRun = process.argv.includes("--dry-run");
130
+ if (dryRun) {
131
+ console.log(` ${bold("Dry run")} ${dim("- nothing will be changed")}`);
132
+ console.log();
133
+ }
134
+ const assumeYes = dryRun || process.argv.includes("--yes") || process.argv.includes("-y");
92
135
  if (!assumeYes && !process.stdin.isTTY) {
93
136
  console.log(` ${yellow("!")} Non-interactive shell. Re-run with --yes to confirm removal.`);
94
137
  process.exit(1);
@@ -118,7 +161,7 @@ var main = async () => {
118
161
  if (mcpServers?.pushary) {
119
162
  delete mcpServers.pushary;
120
163
  writeJson(CURSOR_MCP, cursorData);
121
- console.log(` ${check} Cursor MCP config ${dim("(cleaned)")}`);
164
+ console.log(` ${check} Cursor MCP config ${dim(did("cleaned"))}`);
122
165
  } else {
123
166
  console.log(` ${skip} Cursor MCP config ${dim("(no pushary entries)")}`);
124
167
  }
@@ -126,7 +169,7 @@ var main = async () => {
126
169
  console.log(` ${skip} Cursor MCP config ${dim("(not found)")}`);
127
170
  }
128
171
  if (existsSync(CURSOR_PLUGIN_DIR)) {
129
- rmSync(CURSOR_PLUGIN_DIR, { recursive: true });
172
+ guardedRemove(CURSOR_PLUGIN_DIR);
130
173
  console.log(` ${check} Cursor plugin ${dim("(removed from ~/.cursor/plugins/local)")}`);
131
174
  } else {
132
175
  console.log(` ${skip} Cursor plugin ${dim("(not installed)")}`);
@@ -149,12 +192,12 @@ var main = async () => {
149
192
  console.log(` ${skip} Cursor gate ${dim("(no hooks.json)")}`);
150
193
  }
151
194
  if (existsSync(SKILL_DIR)) {
152
- rmSync(SKILL_DIR, { recursive: true });
153
- console.log(` ${check} Skill directory ${dim("(removed)")}`);
195
+ guardedRemove(SKILL_DIR);
196
+ console.log(` ${check} Skill directory ${dim(did("removed"))}`);
154
197
  } else {
155
198
  console.log(` ${skip} Skill directory ${dim("(not found)")}`);
156
199
  }
157
- const codexConfig = join(homedir(), ".codex", "config.toml");
200
+ const codexConfig = codexConfigToml();
158
201
  try {
159
202
  const config = readFileSync(codexConfig, "utf-8");
160
203
  const hadPushary = config.includes("pushary");
@@ -168,19 +211,19 @@ var main = async () => {
168
211
  parsed.notify = parsed.notify.filter((n) => typeof n !== "string" || !n.includes("pushary-codex"));
169
212
  if (parsed.notify.length === 0) delete parsed.notify;
170
213
  }
171
- writeFileSync(codexConfig, stringifyTOML(parsed), "utf-8");
172
- console.log(` ${check} Codex config ${dim("(cleaned)")}`);
214
+ guardedWrite(codexConfig, stringifyTOML(parsed));
215
+ console.log(` ${check} Codex config ${dim(did("cleaned"))}`);
173
216
  } else {
174
217
  console.log(` ${skip} Codex config ${dim("(no pushary entries)")}`);
175
218
  }
176
219
  } catch {
177
220
  console.log(` ${skip} Codex config ${dim("(not found)")}`);
178
221
  }
179
- const codexHooksJson = join(homedir(), ".codex", "hooks.json");
180
- const codexHooksData = readJson(codexHooksJson);
222
+ const codexHooksJson2 = codexHooksJson();
223
+ const codexHooksData = readJson(codexHooksJson2);
181
224
  if (codexHooksData) {
182
225
  if (removeCodexHooks(codexHooksData)) {
183
- writeJson(codexHooksJson, codexHooksData);
226
+ writeJson(codexHooksJson2, codexHooksData);
184
227
  console.log(` ${check} Codex hooks ${dim("(removed from ~/.codex/hooks.json)")}`);
185
228
  } else {
186
229
  console.log(` ${skip} Codex hooks ${dim("(no pushary entries)")}`);
@@ -188,16 +231,16 @@ var main = async () => {
188
231
  } else {
189
232
  console.log(` ${skip} Codex hooks ${dim("(not found)")}`);
190
233
  }
191
- const codexAgentsMd = join(homedir(), ".codex", "AGENTS.md");
192
- if (removeInstructionBlock(codexAgentsMd)) {
234
+ const codexAgentsMd2 = codexAgentsMd();
235
+ if (guardedInstructionBlockRemoval(codexAgentsMd2)) {
193
236
  console.log(` ${check} Codex AGENTS.md ${dim("(removed Pushary block)")}`);
194
237
  } else {
195
238
  console.log(` ${skip} Codex AGENTS.md ${dim("(no pushary block)")}`);
196
239
  }
197
- const codexSkillDir = join(homedir(), ".codex", "skills", "pushary");
198
- if (existsSync(codexSkillDir)) {
199
- rmSync(codexSkillDir, { recursive: true });
200
- console.log(` ${check} Codex skill directory ${dim("(removed)")}`);
240
+ const codexSkillDir2 = codexSkillDir();
241
+ if (existsSync(codexSkillDir2)) {
242
+ guardedRemove(codexSkillDir2);
243
+ console.log(` ${check} Codex skill directory ${dim(did("removed"))}`);
201
244
  } else {
202
245
  console.log(` ${skip} Codex skill directory ${dim("(not found)")}`);
203
246
  }
@@ -206,7 +249,7 @@ var main = async () => {
206
249
  if (geminiSettings) {
207
250
  if (removeGeminiSettings(geminiSettings)) {
208
251
  writeJson(geminiSettingsPath, geminiSettings);
209
- console.log(` ${check} Gemini CLI settings ${dim("(cleaned)")}`);
252
+ console.log(` ${check} Gemini CLI settings ${dim(did("cleaned"))}`);
210
253
  } else {
211
254
  console.log(` ${skip} Gemini CLI settings ${dim("(no pushary entries)")}`);
212
255
  }
@@ -214,7 +257,7 @@ var main = async () => {
214
257
  console.log(` ${skip} Gemini CLI settings ${dim("(not found)")}`);
215
258
  }
216
259
  const geminiMd = join(homedir(), ".gemini", "GEMINI.md");
217
- if (removeInstructionBlock(geminiMd)) {
260
+ if (guardedInstructionBlockRemoval(geminiMd)) {
218
261
  console.log(` ${check} Gemini GEMINI.md ${dim("(removed Pushary block)")}`);
219
262
  } else {
220
263
  console.log(` ${skip} Gemini GEMINI.md ${dim("(no pushary block)")}`);
@@ -223,13 +266,13 @@ var main = async () => {
223
266
  if (hermesPython) {
224
267
  const snippet = 'from hermes_cli.config import load_config, save_config; c = load_config(); p = c.get("plugins") if isinstance(c.get("plugins"), dict) else {}; e = p.get("enabled") if isinstance(p.get("enabled"), list) else []; p["enabled"] = [x for x in e if x != "pushary"]; c["plugins"] = p; a = c.get("agent") if isinstance(c.get("agent"), dict) else {}; d = a.get("disabled_toolsets") if isinstance(a.get("disabled_toolsets"), list) else []; a["disabled_toolsets"] = [x for x in d if x != "clarify"]; c["agent"] = a; save_config(c)';
225
268
  try {
226
- execSync(`"${hermesPython}" -c '${snippet}'`, { stdio: "pipe", timeout: 15e3 });
269
+ guardedExec(`"${hermesPython}" -c '${snippet}'`, { stdio: "pipe", timeout: 15e3 }, "hermes plugin config update");
227
270
  console.log(` ${check} Hermes config ${dim("(plugin disabled, clarify toolset restored)")}`);
228
271
  } catch {
229
272
  console.log(` ${skip} Hermes config ${dim("(could not update config.yaml)")}`);
230
273
  }
231
274
  try {
232
- execSync(`"${hermesPython}" -m pip uninstall -y hermes-plugin-pushary`, { stdio: "pipe", timeout: 6e4 });
275
+ guardedExec(`"${hermesPython}" -m pip uninstall -y hermes-plugin-pushary`, { stdio: "pipe", timeout: 6e4 }, "pip uninstall hermes-plugin-pushary");
233
276
  console.log(` ${check} Hermes plugin ${dim("(pip uninstalled)")}`);
234
277
  } catch {
235
278
  console.log(` ${skip} Hermes plugin ${dim("(not installed)")}`);
@@ -247,8 +290,8 @@ var main = async () => {
247
290
  const cleaned = lines.filter((l) => !isOurExport(l)).join("\n");
248
291
  if (cleaned !== content) {
249
292
  const backup = `${shellFile}.pushary-backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
250
- copyFileSync(shellFile, backup);
251
- writeFileSync(shellFile, cleaned, "utf-8");
293
+ if (!dryRun) copyFileSync(shellFile, backup);
294
+ guardedWrite(shellFile, cleaned);
252
295
  console.log(` ${check} ${shellFile.split("/").pop()} ${dim(`(removed the export, backup at ${backup})`)}`);
253
296
  }
254
297
  for (const line of declined) {
@@ -258,14 +301,14 @@ var main = async () => {
258
301
  } catch {
259
302
  }
260
303
  }
261
- const aliasRemovedFrom = removeClaudeAlias();
304
+ const aliasRemovedFrom = guardedAliasRemoval();
262
305
  if (aliasRemovedFrom.length > 0) {
263
306
  console.log(` ${check} claude alias ${dim(`(removed from ${aliasRemovedFrom.map((p) => p.split("/").pop()).join(", ")})`)}`);
264
307
  } else {
265
308
  console.log(` ${skip} claude alias ${dim("(not set)")}`);
266
309
  }
267
310
  if (existsSync(PUSHARY_DIR)) {
268
- rmSync(PUSHARY_DIR, { recursive: true });
311
+ guardedRemove(PUSHARY_DIR);
269
312
  console.log(` ${check} Local state ${dim("(~/.pushary removed: stored API key, ledger)")}`);
270
313
  } else {
271
314
  console.log(` ${skip} Local state ${dim("(~/.pushary not found)")}`);
@@ -275,7 +318,7 @@ var main = async () => {
275
318
  for (const f of readdirSync(tmpdir())) {
276
319
  if (!f.startsWith("pushary-")) continue;
277
320
  try {
278
- rmSync(join(tmpdir(), f), { recursive: true, force: true });
321
+ guardedRemove(join(tmpdir(), f), { force: true });
279
322
  swept++;
280
323
  } catch {
281
324
  }
@@ -284,8 +327,12 @@ var main = async () => {
284
327
  } catch {
285
328
  }
286
329
  try {
287
- execNpm("uninstall -g --no-workspaces @pushary/agent-hooks", { stdio: "ignore", timeout: 3e4 });
288
- console.log(` ${check} Global package ${dim("(uninstalled)")}`);
330
+ if (dryRun) {
331
+ wouldNote("run ", "npm uninstall -g @pushary/agent-hooks");
332
+ } else {
333
+ execNpm("uninstall -g --no-workspaces @pushary/agent-hooks", { stdio: "ignore", timeout: 3e4 });
334
+ }
335
+ console.log(` ${check} Global package ${dim(did("uninstalled"))}`);
289
336
  } catch {
290
337
  console.log(` ${skip} Global package ${dim("(not installed)")}`);
291
338
  }
@@ -31,10 +31,7 @@ import {
31
31
  toCodexWire,
32
32
  toPolicyLookup,
33
33
  waitForAnswer
34
- } from "../chunk-VFBYRR2N.js";
35
- import {
36
- getMachineId
37
- } from "../chunk-RN3NOEJF.js";
34
+ } from "../chunk-HIIBSYFJ.js";
38
35
  import {
39
36
  isGatingMoment,
40
37
  recordKeylessMoment
@@ -46,6 +43,9 @@ import {
46
43
  hookWaitClamped,
47
44
  hookWaitDeadline
48
45
  } from "../chunk-DQAN3JQP.js";
46
+ import {
47
+ getMachineId
48
+ } from "../chunk-RN3NOEJF.js";
49
49
  import {
50
50
  getApiKey
51
51
  } from "../chunk-2UMNXADU.js";
@@ -3,12 +3,12 @@ import {
3
3
  askUser,
4
4
  reportEvent,
5
5
  waitForAnswer
6
- } from "../chunk-VFBYRR2N.js";
6
+ } from "../chunk-HIIBSYFJ.js";
7
+ import "../chunk-HNTKTK5B.js";
8
+ import "../chunk-DQAN3JQP.js";
7
9
  import {
8
10
  getMachineId
9
11
  } from "../chunk-RN3NOEJF.js";
10
- import "../chunk-HNTKTK5B.js";
11
- import "../chunk-DQAN3JQP.js";
12
12
  import {
13
13
  getApiKey
14
14
  } from "../chunk-2UMNXADU.js";
@@ -1,22 +1,23 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  printKeyCheck
4
- } from "../chunk-R6C4USIV.js";
4
+ } from "../chunk-ONVEYEVR.js";
5
5
  import {
6
6
  confirmAppConnection,
7
7
  connectDevice,
8
8
  printConnectInstructions
9
- } from "../chunk-GX64YGU3.js";
9
+ } from "../chunk-3O27ZSRE.js";
10
10
  import "../chunk-3EGEA4KH.js";
11
11
  import {
12
12
  parseFlags
13
13
  } from "../chunk-N4DA4CJB.js";
14
14
  import {
15
15
  readKeySource
16
- } from "../chunk-B26DNXCA.js";
16
+ } from "../chunk-COC6NPWG.js";
17
+ import "../chunk-6MTNS63X.js";
17
18
  import {
18
19
  checkApiKey
19
- } from "../chunk-UKNAAVEE.js";
20
+ } from "../chunk-YJ7YZRVV.js";
20
21
  import {
21
22
  createIo
22
23
  } from "../chunk-TX7KBKT7.js";
@@ -26,10 +27,10 @@ import {
26
27
  import "../chunk-BC3VCZ3E.js";
27
28
  import {
28
29
  exitOnHelpFlag
29
- } from "../chunk-HUJQSP4F.js";
30
+ } from "../chunk-IIZZYUWK.js";
30
31
  import {
31
32
  EXIT
32
- } from "../chunk-SML23YOT.js";
33
+ } from "../chunk-SP7OZXCQ.js";
33
34
  import "../chunk-HNTKTK5B.js";
34
35
  import "../chunk-DQAN3JQP.js";
35
36
  import "../chunk-2UMNXADU.js";
@@ -7,8 +7,10 @@ import {
7
7
  } from "../chunk-MUEW424A.js";
8
8
  import {
9
9
  exitOnHelpFlag
10
- } from "../chunk-HUJQSP4F.js";
11
- import "../chunk-SML23YOT.js";
10
+ } from "../chunk-IIZZYUWK.js";
11
+ import {
12
+ EXIT
13
+ } from "../chunk-SP7OZXCQ.js";
12
14
  import {
13
15
  getMachineId
14
16
  } from "../chunk-RN3NOEJF.js";
@@ -30,6 +32,19 @@ var MAX_ERROR_STREAK = 6;
30
32
  var REQUEST_TIMEOUT_MS = 1e4;
31
33
  var MAX_SPAWNS_PER_WINDOW = 5;
32
34
  var SPAWN_WINDOW_MS = 6e4;
35
+ var DrainError = class extends Error {
36
+ constructor(status) {
37
+ super(`drain ${status}`);
38
+ this.status = status;
39
+ this.name = "DrainError";
40
+ }
41
+ };
42
+ var isAuthFailure = (err) => {
43
+ if (typeof err !== "object" || err === null) return false;
44
+ const candidate = err;
45
+ if (candidate.name !== "DrainError") return false;
46
+ return candidate.status === 401 || candidate.status === 403;
47
+ };
33
48
  var NATIVE_AGENT_BINARIES = { codex: "codex", gemini: "gemini" };
34
49
  var buildSpawnLaunch = (req, opts) => {
35
50
  const agent = req.agent === "codex" || req.agent === "gemini" ? req.agent : "claude";
@@ -68,6 +83,9 @@ var runSpawnDaemon = async () => {
68
83
  const spawnTimestamps = [];
69
84
  let timer;
70
85
  let inflight;
86
+ let fatalCode = null;
87
+ let finish = () => {
88
+ };
71
89
  const launch = (req) => {
72
90
  spawnTimestamps.push(Date.now());
73
91
  const cwd = req.cwd && existsSync(req.cwd) ? req.cwd : process.cwd();
@@ -98,7 +116,7 @@ var runSpawnDaemon = async () => {
98
116
  body: JSON.stringify({ machineId }),
99
117
  signal
100
118
  });
101
- if (!res.ok) throw new Error(`drain ${res.status}`);
119
+ if (!res.ok) throw new DrainError(res.status);
102
120
  const data = await res.json();
103
121
  const spawn2 = data.spawn;
104
122
  return spawn2 && typeof spawn2.prompt === "string" && typeof spawn2.id === "string" ? spawn2 : null;
@@ -126,7 +144,16 @@ var runSpawnDaemon = async () => {
126
144
  } else {
127
145
  idleTicks++;
128
146
  }
129
- } catch {
147
+ } catch (err) {
148
+ if (isAuthFailure(err)) {
149
+ const status = err.status;
150
+ fatalCode = EXIT.UNAUTHENTICATED;
151
+ stderr(
152
+ status === 401 ? "[pushary] daemon stopping: this machine's key was rejected. Run `pushary login` to sign in again.\n" : "[pushary] daemon stopping: this workspace has no active plan, so the queue is closed. Pick one at pushary.com/dashboard/settings?tab=billing\n"
153
+ );
154
+ finish();
155
+ return;
156
+ }
130
157
  errorStreak = Math.min(errorStreak + 1, MAX_ERROR_STREAK);
131
158
  } finally {
132
159
  clearTimeout(timeout);
@@ -152,8 +179,13 @@ var runSpawnDaemon = async () => {
152
179
  resolve();
153
180
  };
154
181
  for (const signal of signals) process.on(signal, onSignal);
182
+ finish = () => {
183
+ teardown();
184
+ for (const signal of signals) process.off(signal, onSignal);
185
+ resolve();
186
+ };
155
187
  });
156
- return 0;
188
+ return fatalCode ?? 0;
157
189
  };
158
190
 
159
191
  // bin/pushary-daemon.ts