@pushary/agent-hooks 1.8.2 → 1.9.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.
Files changed (93) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/bin/pushary-bell-hook.js +23 -23
  3. package/dist/bin/pushary-bell.js +9 -9
  4. package/dist/bin/pushary-claude.js +26 -26
  5. package/dist/bin/pushary-clean.js +28 -28
  6. package/dist/bin/pushary-codex-bridge.js +18 -18
  7. package/dist/bin/pushary-codex-hook.js +31 -31
  8. package/dist/bin/pushary-codex.js +13 -13
  9. package/dist/bin/pushary-connect.js +13 -13
  10. package/dist/bin/pushary-cowork.js +5 -5
  11. package/dist/bin/pushary-daemon.js +43 -40
  12. package/dist/bin/pushary-disconnect.js +18 -18
  13. package/dist/bin/pushary-doctor.js +30 -30
  14. package/dist/bin/pushary-elicitation-hook.js +25 -25
  15. package/dist/bin/pushary-gemini-bridge.js +13 -13
  16. package/dist/bin/pushary-gemini-hook.js +28 -28
  17. package/dist/bin/pushary-hook.js +32 -32
  18. package/dist/bin/pushary-login.js +12 -12
  19. package/dist/bin/pushary-logout.js +7 -7
  20. package/dist/bin/pushary-mcp.js +9 -9
  21. package/dist/bin/pushary-mode.js +9 -9
  22. package/dist/bin/pushary-notification-hook.js +28 -28
  23. package/dist/bin/pushary-opencode-hook.js +27 -27
  24. package/dist/bin/pushary-permission-denied-hook.js +32 -32
  25. package/dist/bin/pushary-permission-hook.js +31 -31
  26. package/dist/bin/pushary-post-hook.js +28 -28
  27. package/dist/bin/pushary-prompt-hook.js +28 -28
  28. package/dist/bin/pushary-session-end-hook.js +28 -28
  29. package/dist/bin/pushary-session-start-hook.js +31 -31
  30. package/dist/bin/pushary-setup.js +53 -38
  31. package/dist/bin/pushary-stats.js +5 -5
  32. package/dist/bin/pushary-status.js +15 -15
  33. package/dist/bin/pushary-stop-hook.js +28 -28
  34. package/dist/bin/pushary-stopfailure-hook.js +28 -28
  35. package/dist/bin/pushary-suggestions.js +4 -4
  36. package/dist/bin/pushary-transcript-register.js +19 -19
  37. package/dist/bin/pushary-transcripts.js +7 -7
  38. package/dist/bin/pushary-upgrade.js +223 -147
  39. package/dist/bin/pushary-wait.js +9 -9
  40. package/dist/bin/pushary.js +6 -6
  41. package/dist/{chunk-NHCUXTEM.js → chunk-2V64X5RE.js} +1 -1
  42. package/dist/{chunk-ZJYJAD52.js → chunk-3OFHFVPX.js} +1 -1
  43. package/dist/{chunk-F6NTWZZC.js → chunk-42E4DE6C.js} +9 -9
  44. package/dist/{chunk-INSMZNS7.js → chunk-4GMZOGH4.js} +10 -7
  45. package/dist/{chunk-5YY3IJT4.js → chunk-4PCF34AB.js} +17 -0
  46. package/dist/{chunk-7U7ZWUTR.js → chunk-4ZXYWEEH.js} +1 -1
  47. package/dist/{chunk-DVW2MYCM.js → chunk-75CEJ4WP.js} +3 -3
  48. package/dist/{chunk-DXBS2EL7.js → chunk-7LLNRCOK.js} +1 -1
  49. package/dist/{chunk-I3XLHSOC.js → chunk-7TQQSQZ7.js} +6 -6
  50. package/dist/{chunk-KB2FTO4O.js → chunk-BG4QCOHQ.js} +4 -4
  51. package/dist/{chunk-XQZEIWAV.js → chunk-CFAD2LTH.js} +54 -6
  52. package/dist/{chunk-MOTNSXC3.js → chunk-CKPDSFY3.js} +5 -5
  53. package/dist/{chunk-I4HKMUZQ.js → chunk-CPBRXBLG.js} +3 -3
  54. package/dist/{chunk-ON5JW2HP.js → chunk-EG46RJRF.js} +1 -1
  55. package/dist/{chunk-DLEKCAQL.js → chunk-ET6QSY3H.js} +1 -1
  56. package/dist/{chunk-NOLPURUI.js → chunk-HHNFGWR5.js} +1 -1
  57. package/dist/{chunk-NNWQ5VDB.js → chunk-HNMMKFYN.js} +10 -10
  58. package/dist/{chunk-N6JJJTPH.js → chunk-HQWPMVR4.js} +3 -3
  59. package/dist/{chunk-UMMCHS6P.js → chunk-HSM5FIA3.js} +1 -1
  60. package/dist/{chunk-7ITOAMCD.js → chunk-IJJ3HHFT.js} +1 -1
  61. package/dist/{chunk-CHMOVJ2M.js → chunk-IQUBCAEK.js} +1 -1
  62. package/dist/{chunk-HVDMGDYV.js → chunk-JOQEPWDH.js} +2 -2
  63. package/dist/{chunk-W2LJ4O3A.js → chunk-JW7DKTT6.js} +2 -2
  64. package/dist/{chunk-CB23AO4D.js → chunk-K75TP4JY.js} +2 -2
  65. package/dist/{chunk-LK2S5XIV.js → chunk-K7DY2DQN.js} +6 -6
  66. package/dist/{chunk-HMNR7B2F.js → chunk-KETBIX7I.js} +1 -1
  67. package/dist/{chunk-EIGJYCOE.js → chunk-LQESQPIT.js} +5 -5
  68. package/dist/{chunk-T55H6YEP.js → chunk-MBNMO5IR.js} +2 -2
  69. package/dist/{chunk-72WOCX22.js → chunk-MBVV3RX3.js} +1 -1
  70. package/dist/{chunk-URNXU3FO.js → chunk-MGXXQHCM.js} +1 -1
  71. package/dist/{chunk-644AEX4T.js → chunk-MQJWO3AA.js} +3 -3
  72. package/dist/{chunk-XEGVVU5H.js → chunk-NAJ3UV4E.js} +3 -3
  73. package/dist/{chunk-YNCGXHM4.js → chunk-OLG6FJV2.js} +4 -4
  74. package/dist/{chunk-EESPY5DG.js → chunk-OXQKXZJ3.js} +2 -2
  75. package/dist/{chunk-64H5IZZD.js → chunk-P25O4K3J.js} +2 -2
  76. package/dist/{chunk-2SLCHCVN.js → chunk-P6VDRQF3.js} +3 -3
  77. package/dist/{chunk-UPLV5AJF.js → chunk-R76C43H7.js} +58 -6
  78. package/dist/{chunk-HOEB6AKG.js → chunk-RZXQFAWR.js} +1 -1
  79. package/dist/{chunk-35X5UDML.js → chunk-SDSKTRIV.js} +1 -1
  80. package/dist/{chunk-MYHG7PX2.js → chunk-TUZ7WKOF.js} +1 -1
  81. package/dist/{chunk-5Y2DY6X4.js → chunk-US3RDXK7.js} +2 -2
  82. package/dist/{chunk-4A3UQERT.js → chunk-VV4SBPCQ.js} +1 -1
  83. package/dist/{chunk-VFFHQC7W.js → chunk-VYNTRHB6.js} +1 -1
  84. package/dist/{chunk-DUA5O6X5.js → chunk-WFVNXGZ6.js} +8 -8
  85. package/dist/{chunk-BDMC5H5L.js → chunk-XGS2DHGF.js} +2 -2
  86. package/dist/{chunk-B3UVRGYP.js → chunk-XXVMUMKT.js} +5 -5
  87. package/dist/{chunk-S2UNQQQS.js → chunk-YFS4HYCI.js} +3 -3
  88. package/dist/{chunk-5DGNEUXK.js → chunk-YJZDIFON.js} +2 -2
  89. package/dist/{chunk-MMVCI6DT.js → chunk-ZMVLKPT7.js} +5 -5
  90. package/dist/{chunk-TJMAXQ5Q.js → chunk-ZRHA4PGN.js} +2 -2
  91. package/dist/{reapply-3UERAKQN.js → reapply-FYCVOWCW.js} +8 -8
  92. package/dist/src/index.js +20 -20
  93. package/package.json +1 -1
@@ -1,22 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  describeCodexTrustConfirmation
4
- } from "../chunk-DXBS2EL7.js";
4
+ } from "../chunk-7LLNRCOK.js";
5
5
  import {
6
6
  ensureCodexTrustAfterReapply,
7
7
  reapplyAllAgents
8
- } from "../chunk-B3UVRGYP.js";
9
- import "../chunk-W2LJ4O3A.js";
8
+ } from "../chunk-XXVMUMKT.js";
9
+ import "../chunk-JW7DKTT6.js";
10
10
  import {
11
11
  waitForMachineReadiness
12
- } from "../chunk-I3XLHSOC.js";
13
- import "../chunk-644AEX4T.js";
12
+ } from "../chunk-7TQQSQZ7.js";
13
+ import "../chunk-MQJWO3AA.js";
14
14
  import {
15
15
  detectProviderLiveCapabilities,
16
16
  expectedLaunchCapabilities,
17
17
  hasSpawnProvider,
18
18
  resolveControlPolicy
19
- } from "../chunk-XQZEIWAV.js";
19
+ } from "../chunk-CFAD2LTH.js";
20
20
  import "../chunk-RIVNNBEK.js";
21
21
  import {
22
22
  AUTO_UPDATE_DISABLE_ENV,
@@ -28,8 +28,8 @@ import {
28
28
  readAutoUpdateState,
29
29
  updateAutoUpdateState,
30
30
  upgradeEntryFor
31
- } from "../chunk-64H5IZZD.js";
32
- import "../chunk-XEGVVU5H.js";
31
+ } from "../chunk-P25O4K3J.js";
32
+ import "../chunk-NAJ3UV4E.js";
33
33
  import {
34
34
  daemonHasRemoteProof,
35
35
  daemonServiceManagerAvailable,
@@ -40,23 +40,23 @@ import {
40
40
  replaceDaemonService,
41
41
  stopDaemonService,
42
42
  waitForDaemonRemoteProof
43
- } from "../chunk-EIGJYCOE.js";
44
- import "../chunk-S2UNQQQS.js";
43
+ } from "../chunk-LQESQPIT.js";
44
+ import "../chunk-YFS4HYCI.js";
45
45
  import "../chunk-XHKBHWLX.js";
46
46
  import "../chunk-6ICPEJY5.js";
47
- import "../chunk-VFFHQC7W.js";
47
+ import "../chunk-VYNTRHB6.js";
48
48
  import "../chunk-POFBE6ZY.js";
49
49
  import {
50
50
  exitOnHelpFlag
51
- } from "../chunk-CB23AO4D.js";
51
+ } from "../chunk-K75TP4JY.js";
52
52
  import {
53
53
  readControlMode
54
- } from "../chunk-TJMAXQ5Q.js";
55
- import "../chunk-4A3UQERT.js";
54
+ } from "../chunk-ZRHA4PGN.js";
55
+ import "../chunk-VV4SBPCQ.js";
56
56
  import {
57
57
  EXIT
58
58
  } from "../chunk-KZERVKTD.js";
59
- import "../chunk-5Y2DY6X4.js";
59
+ import "../chunk-US3RDXK7.js";
60
60
  import {
61
61
  LATEST_TAG,
62
62
  autoUpdateLock,
@@ -73,86 +73,61 @@ import {
73
73
  readGlobalCliInstallAt,
74
74
  readGlobalPrefix,
75
75
  vscodePluginDir
76
- } from "../chunk-UPLV5AJF.js";
77
- import "../chunk-T7E75EKA.js";
76
+ } from "../chunk-R76C43H7.js";
77
+ import {
78
+ writeJsonAtomic
79
+ } from "../chunk-T7E75EKA.js";
78
80
  import {
79
81
  getApiKey
80
- } from "../chunk-ON5JW2HP.js";
82
+ } from "../chunk-EG46RJRF.js";
81
83
  import {
82
84
  NPX_LATEST_COMMAND
83
- } from "../chunk-35X5UDML.js";
85
+ } from "../chunk-SDSKTRIV.js";
84
86
  import {
85
87
  CANONICAL_PACKAGE,
86
88
  LEGACY_PACKAGE,
87
89
  commandsPackage,
90
+ getPackageVersion,
88
91
  globalUpgradeEntry,
89
92
  isNewerVersion,
90
93
  isReleaseVersion,
91
94
  isSameMajorVersion,
92
95
  ownPackageRoot,
93
96
  packageVersionAt
94
- } from "../chunk-5YY3IJT4.js";
95
-
96
- // bin/pushary-upgrade.ts
97
- import { existsSync as existsSync2 } from "fs";
98
- import { homedir } from "os";
99
- import { join } from "path";
97
+ } from "../chunk-4PCF34AB.js";
100
98
 
101
- // src/global-upgrade.ts
102
- import { execFileSync as execFileSync2 } from "child_process";
103
- import { existsSync } from "fs";
104
-
105
- // src/auto-update-run.ts
106
- import { execFileSync } from "child_process";
107
- import { closeSync, mkdirSync, openSync, readFileSync, statSync, unlinkSync, writeSync } from "fs";
108
- import { dirname } from "path";
109
- var FINISH_UPGRADE_FLAG = "--finish-upgrade";
110
- var LOCK_STALE_MS = 10 * 60 * 1e3;
111
- var INSTALL_TIMEOUT_MS = 12e4;
99
+ // src/upgrade-lock.ts
100
+ import { fork } from "child_process";
101
+ import { createHash } from "crypto";
102
+ import { existsSync, mkdirSync, readFileSync, realpathSync, unlinkSync } from "fs";
103
+ import { createServer, Server } from "net";
104
+ import { basename, dirname, join } from "path";
105
+ var LOCK_HOST = "127.0.0.1";
106
+ var LOCK_PORT_START = 49152;
107
+ var LOCK_PORT_COUNT = 16384;
108
+ var INHERITED_LOCK_ENV = "PUSHARY_UPGRADE_LOCK";
109
+ var LOCK_READY = "pushary-upgrade-lock-ready";
110
+ var LOCK_HANDLE = "pushary-upgrade-lock-handle";
111
+ var HANDOFF_TIMEOUT_MS = 1e4;
112
112
  var FINISH_TIMEOUT_MS = 5 * 60 * 1e3;
113
- var LOG_LIMIT_BYTES = 256 * 1024;
114
- var UNREADABLE_INSTALLED_VERSION = "could not read the installed version";
115
- var UNREACHABLE_REGISTRY = "could not read the latest version from the npm registry";
116
- var MANUAL_UPGRADE_COMMAND = `${NPX_LATEST_COMMAND} upgrade`;
117
- var isAlreadyExists = (error) => error instanceof Error && "code" in error && error.code === "EEXIST";
118
- var createLock = (lockPath, now) => {
119
- const handle = openSync(lockPath, "wx");
120
- try {
121
- writeSync(handle, `${JSON.stringify({ pid: process.pid, at: new Date(now).toISOString() })}
122
- `);
123
- } finally {
124
- closeSync(handle);
125
- }
126
- };
127
- var tryCreateLock = (lockPath, now) => {
113
+ var KERNEL_LOCK_FORMAT = "kernel-v1";
114
+ var heldLocks = /* @__PURE__ */ new Map();
115
+ var canonicalLockPath = (path) => {
116
+ mkdirSync(dirname(path), { recursive: true });
117
+ return join(realpathSync(dirname(path)), basename(path));
118
+ };
119
+ var lockPort = (path) => LOCK_PORT_START + createHash("sha256").update(path).digest().readUInt32BE(0) % LOCK_PORT_COUNT;
120
+ var readLockRecord = (path) => {
128
121
  try {
129
- createLock(lockPath, now);
130
- return "acquired";
131
- } catch (error) {
132
- if (isAlreadyExists(error)) return "held";
133
- throw error;
134
- }
135
- };
136
- var isStaleLock = (lockPath, now) => {
137
- const modifiedAt = statSync(lockPath, { throwIfNoEntry: false })?.mtimeMs;
138
- return modifiedAt !== void 0 && now - modifiedAt > LOCK_STALE_MS;
139
- };
140
- var takeOverLock = (lockPath, now) => {
141
- try {
142
- unlinkSync(lockPath);
122
+ const value = JSON.parse(readFileSync(path, "utf8"));
123
+ const pid = value && typeof value === "object" && "pid" in value ? value.pid : void 0;
124
+ return {
125
+ pid: typeof pid === "number" && Number.isInteger(pid) && pid > 0 ? pid : void 0,
126
+ installationPending: value !== null && typeof value === "object" && "installationPending" in value && value.installationPending === true,
127
+ usesKernelLock: value !== null && typeof value === "object" && "format" in value && value.format === KERNEL_LOCK_FORMAT
128
+ };
143
129
  } catch {
144
- return "held";
145
- }
146
- return tryCreateLock(lockPath, now);
147
- };
148
- var isAbandonedLock = (evidence) => evidence.stale || evidence.holderPid !== void 0 && !evidence.holderAlive;
149
- var lockHolderPid = (lockPath) => {
150
- try {
151
- const holder = JSON.parse(readFileSync(lockPath, "utf8"));
152
- const pid = holder && typeof holder === "object" && "pid" in holder ? holder.pid : void 0;
153
- return typeof pid === "number" && Number.isInteger(pid) && pid > 0 ? pid : void 0;
154
- } catch {
155
- return void 0;
130
+ return { pid: void 0, usesKernelLock: false, installationPending: false };
156
131
  }
157
132
  };
158
133
  var isProcessAlive = (pid) => {
@@ -160,54 +135,138 @@ var isProcessAlive = (pid) => {
160
135
  process.kill(pid, 0);
161
136
  return true;
162
137
  } catch (error) {
163
- return error instanceof Error && "code" in error && error.code === "EPERM";
138
+ return !(error instanceof Error && "code" in error && error.code === "ESRCH");
164
139
  }
165
140
  };
166
- var readLockHolder = (lockPath, now, isAlive) => {
167
- const holderPid = lockHolderPid(lockPath);
168
- return {
169
- holderPid,
170
- holderAlive: holderPid !== void 0 && isAlive(holderPid),
171
- stale: isStaleLock(lockPath, now)
172
- };
141
+ var rejectConnection = (socket) => {
142
+ socket.destroy();
173
143
  };
174
- var acquireAutoUpdateLock = (lockPath, now) => {
175
- mkdirSync(dirname(lockPath), { recursive: true });
176
- if (tryCreateLock(lockPath, now) === "acquired") return "acquired";
177
- if (!isStaleLock(lockPath, now)) return "held";
178
- return takeOverLock(lockPath, now);
179
- };
180
- var releaseAutoUpdateLock = (lockPath) => {
144
+ var inheritLock = (path) => new Promise((resolve) => {
145
+ const done = (claim) => {
146
+ clearTimeout(timer);
147
+ process.off("message", receive);
148
+ process.off("disconnect", disconnected);
149
+ resolve(claim);
150
+ };
151
+ const disconnected = () => done({ kind: "unavailable", reason: "Upgrade parent disconnected before transferring its lock" });
152
+ const receive = (message, handle) => {
153
+ if (message !== LOCK_HANDLE || !(handle instanceof Server)) return;
154
+ const address = handle.address();
155
+ if (address === null || typeof address === "string" || address.port !== lockPort(path) || address.address !== LOCK_HOST) {
156
+ handle.close();
157
+ done({ kind: "unavailable", reason: "Invalid inherited upgrade lock" });
158
+ return;
159
+ }
160
+ handle.on("connection", rejectConnection);
161
+ heldLocks.set(path, handle);
162
+ done({ kind: "inherited" });
163
+ process.disconnect?.();
164
+ };
165
+ const timer = setTimeout(() => done({ kind: "unavailable", reason: "Upgrade lock handoff timed out" }), HANDOFF_TIMEOUT_MS);
166
+ process.on("message", receive);
167
+ process.once("disconnect", disconnected);
168
+ if (!process.send || !process.connected) return disconnected();
169
+ process.send(LOCK_READY, (error) => {
170
+ if (error) disconnected();
171
+ });
172
+ });
173
+ var claimUpgradeLock = async (request = {}) => {
174
+ let server;
181
175
  try {
182
- unlinkSync(lockPath);
183
- } catch {
184
- return;
176
+ const path = canonicalLockPath(request.lockPath ?? autoUpdateLock());
177
+ const inheritedPath = process.env[INHERITED_LOCK_ENV];
178
+ delete process.env[INHERITED_LOCK_ENV];
179
+ if (inheritedPath === path && !heldLocks.has(path)) return await inheritLock(path);
180
+ server = createServer(rejectConnection);
181
+ const listener = server;
182
+ await new Promise((resolve, reject) => {
183
+ listener.once("error", reject);
184
+ listener.listen({ host: LOCK_HOST, port: lockPort(path), exclusive: true }, resolve);
185
+ });
186
+ const record = readLockRecord(path);
187
+ if (record.installationPending) {
188
+ server.close();
189
+ return { kind: "unavailable", reason: `An earlier installation did not finish safely. Stop its npm processes (or restart the computer), then remove ${path} and retry the upgrade.` };
190
+ }
191
+ if (existsSync(path) && !record.usesKernelLock && (record.pid === void 0 || (request.isAlive ?? isProcessAlive)(record.pid))) {
192
+ server.close();
193
+ return { kind: "held", holderPid: record.pid };
194
+ }
195
+ writeJsonAtomic(path, { pid: process.pid, format: KERNEL_LOCK_FORMAT }, 384);
196
+ heldLocks.set(path, server);
197
+ return { kind: "acquired" };
198
+ } catch (error) {
199
+ server?.close();
200
+ if (error instanceof Error && "code" in error && error.code === "EADDRINUSE") {
201
+ return { kind: "held", holderPid: readLockRecord(request.lockPath ?? autoUpdateLock()).pid };
202
+ }
203
+ return { kind: "unavailable", reason: npmErrorMessage(error) };
185
204
  }
186
205
  };
187
- var upgradeLockVerdict = (evidence) => {
188
- if (evidence.holderPid !== void 0 && evidence.holderPid === evidence.parentPid) return "inherited";
189
- if (isAbandonedLock(evidence)) return "take_over";
190
- return "held";
191
- };
192
- var claimUpgradeLockAt = (lockPath, request) => {
193
- const now = request.now ?? Date.now();
194
- mkdirSync(dirname(lockPath), { recursive: true });
195
- if (tryCreateLock(lockPath, now) === "acquired") return { kind: "acquired" };
196
- const holder = readLockHolder(lockPath, now, request.isAlive ?? isProcessAlive);
197
- const verdict = upgradeLockVerdict({ ...holder, parentPid: request.parentPid ?? process.ppid });
198
- if (verdict === "inherited") return { kind: "inherited" };
199
- if (verdict === "take_over" && takeOverLock(lockPath, now) === "acquired") return { kind: "acquired" };
200
- return { kind: "held", holderPid: holder.holderPid };
201
- };
202
- var claimUpgradeLock = (request = {}) => {
206
+ var releaseAutoUpdateLock = (lockPath) => {
207
+ const path = canonicalLockPath(lockPath);
208
+ const server = heldLocks.get(path);
209
+ if (!server) return;
210
+ heldLocks.delete(path);
203
211
  try {
204
- return claimUpgradeLockAt(request.lockPath ?? autoUpdateLock(), request);
205
- } catch {
206
- return { kind: "unavailable" };
212
+ const record = readLockRecord(path);
213
+ if (record.pid === process.pid && !record.installationPending) unlinkSync(path);
214
+ } finally {
215
+ server.close();
207
216
  }
208
217
  };
218
+ var finishWithUpgradeLock = (request) => new Promise((resolve, reject) => {
219
+ const path = canonicalLockPath(request.lockPath ?? autoUpdateLock());
220
+ const server = heldLocks.get(path);
221
+ if (!server) return reject(new Error("Cannot finish an upgrade without its lock"));
222
+ const child = fork(request.entry, [...request.args], {
223
+ execArgv: [],
224
+ stdio: request.stdio ?? ["inherit", "inherit", "inherit", "ipc"],
225
+ env: { ...process.env, ...request.env, [INHERITED_LOCK_ENV]: path }
226
+ });
227
+ const timer = setTimeout(() => child.kill("SIGKILL"), FINISH_TIMEOUT_MS);
228
+ child.on("message", (message) => {
229
+ if (message === LOCK_READY) child.send(LOCK_HANDLE, server, (error) => {
230
+ if (error) child.kill();
231
+ });
232
+ });
233
+ child.once("error", (error) => {
234
+ clearTimeout(timer);
235
+ reject(error);
236
+ });
237
+ child.once("exit", (code, signal) => {
238
+ clearTimeout(timer);
239
+ if (code === 0) resolve();
240
+ else reject(new Error(`Upgrade finish exited with ${signal ?? code}`));
241
+ });
242
+ });
243
+ var installWithUpgradeLock = (request, lockPath = autoUpdateLock(), runNpm) => {
244
+ const path = canonicalLockPath(lockPath);
245
+ if (!heldLocks.has(path)) throw new Error("Cannot install an upgrade without its lock");
246
+ writeJsonAtomic(path, { pid: process.pid, format: KERNEL_LOCK_FORMAT, installationPending: true }, 384);
247
+ installPackageGlobally(request, runNpm);
248
+ writeJsonAtomic(path, { pid: process.pid, format: KERNEL_LOCK_FORMAT }, 384);
249
+ };
250
+
251
+ // bin/pushary-upgrade.ts
252
+ import { existsSync as existsSync3 } from "fs";
253
+ import { homedir } from "os";
254
+ import { join as join2 } from "path";
255
+
256
+ // src/global-upgrade.ts
257
+ import { existsSync as existsSync2 } from "fs";
258
+
259
+ // src/auto-update-run.ts
260
+ import { closeSync, mkdirSync as mkdirSync2, openSync, statSync, writeSync } from "fs";
261
+ import { dirname as dirname2 } from "path";
262
+ var FINISH_UPGRADE_FLAG = "--finish-upgrade";
263
+ var INSTALL_TIMEOUT_MS = 12e4;
264
+ var LOG_LIMIT_BYTES = 256 * 1024;
265
+ var UNREADABLE_INSTALLED_VERSION = "could not read the installed version";
266
+ var UNREACHABLE_REGISTRY = "could not read the latest version from the npm registry";
267
+ var MANUAL_UPGRADE_COMMAND = `${NPX_LATEST_COMMAND} upgrade`;
209
268
  var openAutoUpdateLog = (logPath) => {
210
- mkdirSync(dirname(logPath), { recursive: true });
269
+ mkdirSync2(dirname2(logPath), { recursive: true });
211
270
  const size = statSync(logPath, { throwIfNoEntry: false })?.size ?? 0;
212
271
  return openSync(logPath, size > LOG_LIMIT_BYTES ? "w" : "a");
213
272
  };
@@ -224,13 +283,15 @@ var appendAutoUpdateLog = (logPath, line) => {
224
283
  return;
225
284
  }
226
285
  };
227
- var finishInNewInstall = (request) => {
286
+ var finishInNewInstall = async (request) => {
228
287
  const handle = openAutoUpdateLog(request.logPath);
229
288
  try {
230
- execFileSync(process.execPath, [request.entry, FINISH_UPGRADE_FLAG], {
231
- stdio: ["ignore", handle, handle],
232
- env: { ...process.env, npm_config_prefix: request.prefix },
233
- timeout: FINISH_TIMEOUT_MS
289
+ await finishWithUpgradeLock({
290
+ entry: request.entry,
291
+ args: [FINISH_UPGRADE_FLAG],
292
+ lockPath: request.lockPath,
293
+ stdio: ["ignore", handle, handle, "ipc"],
294
+ env: { npm_config_prefix: request.prefix }
234
295
  });
235
296
  } finally {
236
297
  closeSync(handle);
@@ -238,11 +299,11 @@ var finishInNewInstall = (request) => {
238
299
  };
239
300
  var incompleteFinishDetail = (logPath) => `installed, but refreshing the agent configs did not finish cleanly; see ${logPath}`;
240
301
  var newMajorDetail = (latest) => `${latest} is a new major version; run ${MANUAL_UPGRADE_COMMAND}`;
241
- var finishInstalledUpgrade = (context, from, to) => {
302
+ var finishInstalledUpgrade = async (context, from, to) => {
242
303
  const pending = { result: "finish_pending", from, to, detail: incompleteFinishDetail(context.logPath) };
243
304
  recordAutoUpdateCheck(context.statePath, pending, new Date(context.now()).toISOString());
244
305
  try {
245
- context.finish({ entry: upgradeEntryFor(context.location), prefix: context.location.prefix, logPath: context.logPath });
306
+ await context.finish({ lockPath: context.lockPath, entry: upgradeEntryFor(context.location), prefix: context.location.prefix, logPath: context.logPath });
246
307
  return { result: "updated", from, to };
247
308
  } catch {
248
309
  return pending;
@@ -265,14 +326,13 @@ var checkAndInstall = async (context) => {
265
326
  }
266
327
  appendAutoUpdateLog(context.logPath, `${new Date(context.now()).toISOString()} installing ${context.location.packageName}@${latest} into ${context.location.prefix}`);
267
328
  try {
268
- installPackageGlobally({
329
+ installWithUpgradeLock({
269
330
  packageName: context.location.packageName,
270
331
  version: latest,
271
332
  prefix: context.location.prefix,
272
333
  timeoutMs: INSTALL_TIMEOUT_MS
273
- }, context.runNpm);
334
+ }, context.lockPath, context.runNpm);
274
335
  } catch (error) {
275
- if (pendingFinish) return finishInstalledUpgrade(context, previous.from, installed);
276
336
  return { result: "failed", from: installed, to: latest, detail: npmErrorMessage(error) };
277
337
  }
278
338
  return finishInstalledUpgrade(context, installed, latest);
@@ -294,9 +354,17 @@ var runAutoUpdate = async (run = {}) => {
294
354
  const now = run.now ?? Date.now;
295
355
  const lockPath = run.lockPath ?? autoUpdateLock();
296
356
  const logPath = run.logPath ?? autoUpdateLog();
297
- if (acquireAutoUpdateLock(lockPath, now()) === "held") return { kind: "locked" };
357
+ const claim = await claimUpgradeLock({ lockPath });
358
+ if (claim.kind === "unavailable") {
359
+ const at = new Date(now()).toISOString();
360
+ const state = recordAutoUpdateCheck(run.statePath ?? autoUpdateState(), { result: "failed", detail: claim.reason }, at);
361
+ appendAutoUpdateLog(logPath, `${at} ${describeAutoUpdateCheck(state)}`);
362
+ return { kind: "recorded", state };
363
+ }
364
+ if (claim.kind !== "acquired") return { kind: "locked" };
298
365
  try {
299
366
  const check = await checkAndInstall({
367
+ lockPath,
300
368
  packageRoot,
301
369
  statePath: run.statePath ?? autoUpdateState(),
302
370
  location,
@@ -327,14 +395,12 @@ var readGlobalUpgradeSubject = (platform = process.platform) => {
327
395
  const prefix = readGlobalPrefix();
328
396
  return globalUpgradeSubject(prefix, prefix === void 0 ? { kind: "unavailable" } : readGlobalCliInstallAt(prefix, platform));
329
397
  };
330
- var finishWithThisNode = (entry) => {
331
- execFileSync2(process.execPath, [entry, FINISH_UPGRADE_FLAG], { stdio: "inherit" });
332
- };
333
- var finishGlobalUpgrade = (subject, platform = process.platform, runFinish = finishWithThisNode) => {
398
+ var finishWithThisNode = (entry) => finishWithUpgradeLock({ entry, args: [FINISH_UPGRADE_FLAG] });
399
+ var finishGlobalUpgrade = async (subject, platform = process.platform, runFinish = finishWithThisNode) => {
334
400
  const entry = globalUpgradeEntry(subject.prefix, subject.name, platform);
335
- if (!existsSync(entry)) return { kind: "entry_missing", entry };
401
+ if (!existsSync2(entry)) return { kind: "entry_missing", entry };
336
402
  try {
337
- runFinish(entry);
403
+ await runFinish(entry);
338
404
  return { kind: "finished" };
339
405
  } catch {
340
406
  return { kind: "failed" };
@@ -342,22 +408,30 @@ var finishGlobalUpgrade = (subject, platform = process.platform, runFinish = fin
342
408
  };
343
409
  var describeHeldUpgradeLock = (holderPid) => `Another Pushary upgrade is already running${holderPid === void 0 ? "" : ` (pid ${holderPid})`}. Try again when it has finished.`;
344
410
  var withUpgradeLock = async (run, request = {}) => {
345
- const claim = claimUpgradeLock(request);
411
+ const claim = await claimUpgradeLock(request);
412
+ if (claim.kind === "unavailable") return { kind: "refused", exitCode: EXIT.FAILED, message: `Could not lock the Pushary upgrade: ${claim.reason}` };
346
413
  if (claim.kind === "held") return { kind: "refused", exitCode: EXIT.FAILED, message: describeHeldUpgradeLock(claim.holderPid) };
347
414
  try {
348
415
  await run();
349
416
  } finally {
350
- if (claim.kind === "acquired") releaseAutoUpdateLock(request.lockPath ?? autoUpdateLock());
417
+ releaseAutoUpdateLock(request.lockPath ?? autoUpdateLock());
351
418
  }
352
419
  return { kind: "ran" };
353
420
  };
421
+ var finishUpgradeWithLock = async (run, request) => {
422
+ const outcome = await withUpgradeLock(run, request);
423
+ if (outcome.kind === "refused") {
424
+ updateAutoUpdateState(request.statePath, { lastResult: "finish_pending", to: request.installedVersion, detail: outcome.message });
425
+ }
426
+ return outcome;
427
+ };
354
428
  var describeUpgradeStart = (subject, latest) => `Upgrading ${subject.name} ${subject.version} -> ${latest ?? "latest"}`;
355
429
  var upgradeInstalledPackage = async (subject, effects) => {
356
430
  const pendingFinish = effects.hasPendingFinish(subject.version);
357
431
  const latest = await effects.fetchLatest(subject.name);
358
432
  const newerPublished = latest !== void 0 && isNewerVersion(latest, subject.version);
359
433
  if (pendingFinish && !newerPublished) {
360
- const finish = effects.finish(subject);
434
+ const finish = await effects.finish(subject);
361
435
  if (finish.kind !== "finished") return { kind: "pending_finish_failed", finish };
362
436
  effects.recordFinished();
363
437
  }
@@ -368,7 +442,7 @@ var upgradeInstalledPackage = async (subject, effects) => {
368
442
  } catch (error) {
369
443
  return { kind: "install_failed", detail: npmErrorMessage(error) };
370
444
  }
371
- return { kind: "installed", finish: effects.finish(subject) };
445
+ return { kind: "installed", finish: await effects.finish(subject) };
372
446
  };
373
447
 
374
448
  // bin/pushary-upgrade.ts
@@ -378,32 +452,32 @@ var detectInstallModes = () => {
378
452
  {
379
453
  label: "Cursor plugin",
380
454
  upgrade: "updates itself through Cursor",
381
- present: existsSync2(cursorPluginDir())
455
+ present: existsSync3(cursorPluginDir())
382
456
  },
383
457
  {
384
458
  label: "VS Code plugin",
385
459
  upgrade: `${NPX_LATEST_COMMAND} setup --agents vscode`,
386
- present: existsSync2(vscodePluginDir())
460
+ present: existsSync3(vscodePluginDir())
387
461
  },
388
462
  {
389
463
  label: "Codex config",
390
464
  upgrade: `${NPX_LATEST_COMMAND} setup --agents codex`,
391
- present: existsSync2(codexConfigToml())
465
+ present: existsSync3(codexConfigToml())
392
466
  },
393
467
  {
394
468
  label: "Claude Code config",
395
469
  upgrade: `${NPX_LATEST_COMMAND} setup --agents claude_code`,
396
- present: existsSync2(claudeJson())
470
+ present: existsSync3(claudeJson())
397
471
  },
398
472
  {
399
473
  label: "Gemini config",
400
474
  upgrade: `${NPX_LATEST_COMMAND} setup --agents gemini_cli`,
401
- present: existsSync2(geminiSettings())
475
+ present: existsSync3(geminiSettings())
402
476
  },
403
477
  {
404
478
  label: "Hermes plugin (pip)",
405
479
  upgrade: `${NPX_LATEST_COMMAND} setup --agents hermes`,
406
- present: existsSync2(join(homedir(), ".hermes"))
480
+ present: existsSync3(join2(homedir(), ".hermes"))
407
481
  }
408
482
  ];
409
483
  return candidates.filter((candidate) => candidate.present).map(({ label, upgrade }) => ({ label, upgrade }));
@@ -553,7 +627,7 @@ var upgradeSubject = async (subject) => {
553
627
  updateAutoUpdateState(autoUpdateState(), { lastResult: "updated", detail: void 0, announced: false });
554
628
  },
555
629
  announce: (line) => console.log(line),
556
- install: (packageName) => installPackageGlobally({ packageName, version: LATEST_TAG, timeoutMs: 12e4 }),
630
+ install: (packageName) => installWithUpgradeLock({ packageName, version: LATEST_TAG, timeoutMs: 12e4 }),
557
631
  finish: (installedSubject) => finishGlobalUpgrade(installedSubject)
558
632
  });
559
633
  reportGlobalUpgrade(outcome);
@@ -571,15 +645,17 @@ var upgradeGlobalInstall = async () => {
571
645
  }
572
646
  await upgradeSubject(subject);
573
647
  };
574
- var runUnderUpgradeLock = async (run) => {
575
- const locked = await withUpgradeLock(run);
648
+ var reportUpgradeLock = (locked) => {
576
649
  if (locked.kind === "ran") return;
577
650
  console.error(locked.message);
578
651
  process.exitCode = locked.exitCode;
579
652
  };
580
653
  var main = async () => {
581
654
  if (process.argv.includes(FINISH_UPGRADE_FLAG)) {
582
- await runUnderUpgradeLock(finishUpgrade);
655
+ reportUpgradeLock(await finishUpgradeWithLock(finishUpgrade, {
656
+ installedVersion: getPackageVersion(),
657
+ statePath: autoUpdateState()
658
+ }));
583
659
  return;
584
660
  }
585
661
  const autoFlag = parseAutoUpdateFlag(process.argv.slice(2));
@@ -596,6 +672,6 @@ var main = async () => {
596
672
  await runAutomaticUpdate();
597
673
  return;
598
674
  }
599
- await runUnderUpgradeLock(upgradeGlobalInstall);
675
+ reportUpgradeLock(await withUpgradeLock(upgradeGlobalInstall));
600
676
  };
601
677
  main();
@@ -4,33 +4,33 @@ import {
4
4
  } from "../chunk-CVWG2N3Y.js";
5
5
  import {
6
6
  exitOnFailedResponse
7
- } from "../chunk-NHCUXTEM.js";
7
+ } from "../chunk-2V64X5RE.js";
8
8
  import {
9
9
  createIo
10
10
  } from "../chunk-U4TMAANR.js";
11
11
  import {
12
12
  exitOnHelpFlag
13
- } from "../chunk-CB23AO4D.js";
13
+ } from "../chunk-K75TP4JY.js";
14
14
  import {
15
15
  rejectUnknownFlags
16
16
  } from "../chunk-GMXKITVA.js";
17
- import "../chunk-DVW2MYCM.js";
18
- import "../chunk-2SLCHCVN.js";
19
- import "../chunk-4A3UQERT.js";
17
+ import "../chunk-75CEJ4WP.js";
18
+ import "../chunk-P6VDRQF3.js";
19
+ import "../chunk-VV4SBPCQ.js";
20
20
  import {
21
21
  UsageError
22
22
  } from "../chunk-7QLSKOSU.js";
23
23
  import {
24
24
  EXIT
25
25
  } from "../chunk-KZERVKTD.js";
26
- import "../chunk-UPLV5AJF.js";
26
+ import "../chunk-R76C43H7.js";
27
27
  import "../chunk-T7E75EKA.js";
28
28
  import {
29
29
  getApiKey,
30
30
  getBaseUrl
31
- } from "../chunk-ON5JW2HP.js";
32
- import "../chunk-35X5UDML.js";
33
- import "../chunk-5YY3IJT4.js";
31
+ } from "../chunk-EG46RJRF.js";
32
+ import "../chunk-SDSKTRIV.js";
33
+ import "../chunk-4PCF34AB.js";
34
34
 
35
35
  // bin/pushary-wait.ts
36
36
  exitOnHelpFlag("wait");
@@ -1,23 +1,23 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  installNetworkDefaults
4
- } from "../chunk-2SLCHCVN.js";
5
- import "../chunk-4A3UQERT.js";
4
+ } from "../chunk-P6VDRQF3.js";
5
+ import "../chunk-VV4SBPCQ.js";
6
6
  import "../chunk-7QLSKOSU.js";
7
7
  import {
8
8
  EXIT
9
9
  } from "../chunk-KZERVKTD.js";
10
- import "../chunk-UPLV5AJF.js";
10
+ import "../chunk-R76C43H7.js";
11
11
  import "../chunk-T7E75EKA.js";
12
- import "../chunk-ON5JW2HP.js";
12
+ import "../chunk-EG46RJRF.js";
13
13
  import {
14
14
  COMMANDS,
15
15
  isCommandName,
16
16
  renderHelp
17
- } from "../chunk-35X5UDML.js";
17
+ } from "../chunk-SDSKTRIV.js";
18
18
  import {
19
19
  getPackageVersion
20
- } from "../chunk-5YY3IJT4.js";
20
+ } from "../chunk-4PCF34AB.js";
21
21
 
22
22
  // src/cli/runtime.ts
23
23
  var supportsRuntime = (version) => {
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  describeKeyCheck,
6
6
  keyCheckFromResponse
7
- } from "./chunk-DVW2MYCM.js";
7
+ } from "./chunk-75CEJ4WP.js";
8
8
  import {
9
9
  EXIT
10
10
  } from "./chunk-KZERVKTD.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  pusharyDir
3
- } from "./chunk-UPLV5AJF.js";
3
+ } from "./chunk-R76C43H7.js";
4
4
  import {
5
5
  readJsonSafe
6
6
  } from "./chunk-T7E75EKA.js";