@pushary/agent-hooks 1.8.1 → 1.8.3

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 (72) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/bin/pushary-bell-hook.js +12 -11
  3. package/dist/bin/pushary-bell.js +2 -2
  4. package/dist/bin/pushary-claude.js +16 -15
  5. package/dist/bin/pushary-clean.js +19 -18
  6. package/dist/bin/pushary-codex-bridge.js +14 -12
  7. package/dist/bin/pushary-codex-hook.js +19 -18
  8. package/dist/bin/pushary-codex.js +8 -8
  9. package/dist/bin/pushary-connect.js +7 -7
  10. package/dist/bin/pushary-daemon.js +92 -25
  11. package/dist/bin/pushary-disconnect.js +12 -11
  12. package/dist/bin/pushary-doctor.js +24 -21
  13. package/dist/bin/pushary-elicitation-hook.js +15 -14
  14. package/dist/bin/pushary-gemini-bridge.js +9 -6
  15. package/dist/bin/pushary-gemini-hook.js +17 -16
  16. package/dist/bin/pushary-hook.js +20 -19
  17. package/dist/bin/pushary-login.js +5 -5
  18. package/dist/bin/pushary-logout.js +1 -1
  19. package/dist/bin/pushary-mcp.js +6 -6
  20. package/dist/bin/pushary-mode.js +5 -5
  21. package/dist/bin/pushary-notification-hook.js +18 -17
  22. package/dist/bin/pushary-opencode-hook.js +16 -15
  23. package/dist/bin/pushary-permission-denied-hook.js +20 -19
  24. package/dist/bin/pushary-permission-hook.js +20 -19
  25. package/dist/bin/pushary-post-hook.js +18 -17
  26. package/dist/bin/pushary-prompt-hook.js +18 -17
  27. package/dist/bin/pushary-session-end-hook.js +18 -17
  28. package/dist/bin/pushary-session-start-hook.js +22 -21
  29. package/dist/bin/pushary-setup.js +37 -93
  30. package/dist/bin/pushary-status.js +11 -9
  31. package/dist/bin/pushary-stop-hook.js +18 -17
  32. package/dist/bin/pushary-stopfailure-hook.js +18 -17
  33. package/dist/bin/pushary-transcript-register.js +9 -8
  34. package/dist/bin/pushary-upgrade.js +219 -141
  35. package/dist/bin/pushary-wait.js +5 -5
  36. package/dist/bin/pushary.js +3 -3
  37. package/dist/{chunk-W2LJ4O3A.js → chunk-27A7L4DA.js} +1 -1
  38. package/dist/{chunk-I4HKMUZQ.js → chunk-34EQOFMH.js} +2 -2
  39. package/dist/{chunk-2SLCHCVN.js → chunk-4CW5EAOI.js} +1 -1
  40. package/dist/{chunk-F6NTWZZC.js → chunk-4DYHBXX4.js} +5 -5
  41. package/dist/{chunk-INSMZNS7.js → chunk-4SJIPHR3.js} +1 -1
  42. package/dist/chunk-6ICPEJY5.js +20 -0
  43. package/dist/{chunk-ZJYJAD52.js → chunk-6SS74UHS.js} +1 -1
  44. package/dist/{chunk-DVW2MYCM.js → chunk-6UDK5TY6.js} +1 -1
  45. package/dist/{chunk-MMVCI6DT.js → chunk-AKCE6ZRF.js} +3 -3
  46. package/dist/{chunk-HBWC3JVZ.js → chunk-APURTMSW.js} +2 -2
  47. package/dist/{chunk-QB6QYZ6B.js → chunk-CTNLP4A5.js} +4 -4
  48. package/dist/{chunk-GH6KGWMC.js → chunk-CXYKQAL2.js} +11 -9
  49. package/dist/{chunk-4A3UQERT.js → chunk-DMU5E6HJ.js} +1 -1
  50. package/dist/{chunk-FLQPM2GQ.js → chunk-E5JGQGLD.js} +2 -2
  51. package/dist/{chunk-JRQRNNGA.js → chunk-EJXEHNB5.js} +2 -2
  52. package/dist/{chunk-5DGNEUXK.js → chunk-FBTUF2PH.js} +1 -1
  53. package/dist/{chunk-EIGJYCOE.js → chunk-IEXWWAJY.js} +2 -2
  54. package/dist/{chunk-HQRCVEI2.js → chunk-MSMKCO4Y.js} +6 -6
  55. package/dist/{chunk-TKQ6LZQZ.js → chunk-OLYTGVCC.js} +5 -4
  56. package/dist/{chunk-ASWRMBOM.js → chunk-RIVNNBEK.js} +2 -1
  57. package/dist/{chunk-6TK4IY3A.js → chunk-SD6OLZJX.js} +3 -3
  58. package/dist/chunk-SOUJG6UY.js +198 -0
  59. package/dist/{chunk-KB2FTO4O.js → chunk-UXKXGRKG.js} +2 -2
  60. package/dist/{chunk-T55H6YEP.js → chunk-VF3X4MDB.js} +1 -1
  61. package/dist/{chunk-64H5IZZD.js → chunk-VI55VXDH.js} +1 -1
  62. package/dist/{chunk-NHCUXTEM.js → chunk-VUP5BMOD.js} +1 -1
  63. package/dist/{chunk-HOEB6AKG.js → chunk-XH7UNEHO.js} +1 -1
  64. package/dist/{chunk-LK2S5XIV.js → chunk-XNJ4MQF3.js} +2 -2
  65. package/dist/{chunk-HTEGMMT3.js → chunk-YLJ26RR2.js} +5 -18
  66. package/dist/{chunk-OCVVSRDA.js → chunk-YOYSXLUB.js} +2 -2
  67. package/dist/{chunk-GBNQXYN4.js → chunk-ZOKXZ3AS.js} +2 -2
  68. package/dist/{chunk-UPLV5AJF.js → chunk-ZR7LIQKO.js} +57 -5
  69. package/dist/{reapply-ICGTBHVE.js → reapply-DYOQAVUP.js} +8 -7
  70. package/dist/src/index.js +14 -13
  71. package/package.json +1 -1
  72. package/dist/chunk-VZMIHERK.js +0 -59
@@ -5,18 +5,19 @@ import {
5
5
  import {
6
6
  ensureCodexTrustAfterReapply,
7
7
  reapplyAllAgents
8
- } from "../chunk-GH6KGWMC.js";
9
- import "../chunk-ASWRMBOM.js";
10
- import "../chunk-W2LJ4O3A.js";
8
+ } from "../chunk-CXYKQAL2.js";
9
+ import "../chunk-27A7L4DA.js";
11
10
  import {
12
11
  waitForMachineReadiness
13
- } from "../chunk-TKQ6LZQZ.js";
14
- import "../chunk-JRQRNNGA.js";
12
+ } from "../chunk-OLYTGVCC.js";
13
+ import "../chunk-EJXEHNB5.js";
15
14
  import {
16
15
  detectProviderLiveCapabilities,
16
+ expectedLaunchCapabilities,
17
17
  hasSpawnProvider,
18
18
  resolveControlPolicy
19
- } from "../chunk-VZMIHERK.js";
19
+ } from "../chunk-SOUJG6UY.js";
20
+ import "../chunk-RIVNNBEK.js";
20
21
  import {
21
22
  AUTO_UPDATE_DISABLE_ENV,
22
23
  describeAutoUpdateCheck,
@@ -27,8 +28,8 @@ import {
27
28
  readAutoUpdateState,
28
29
  updateAutoUpdateState,
29
30
  upgradeEntryFor
30
- } from "../chunk-64H5IZZD.js";
31
- import "../chunk-OCVVSRDA.js";
31
+ } from "../chunk-VI55VXDH.js";
32
+ import "../chunk-YOYSXLUB.js";
32
33
  import {
33
34
  daemonHasRemoteProof,
34
35
  daemonServiceManagerAvailable,
@@ -39,9 +40,10 @@ import {
39
40
  replaceDaemonService,
40
41
  stopDaemonService,
41
42
  waitForDaemonRemoteProof
42
- } from "../chunk-EIGJYCOE.js";
43
- import "../chunk-HTEGMMT3.js";
43
+ } from "../chunk-IEXWWAJY.js";
44
+ import "../chunk-YLJ26RR2.js";
44
45
  import "../chunk-XHKBHWLX.js";
46
+ import "../chunk-6ICPEJY5.js";
45
47
  import "../chunk-VFFHQC7W.js";
46
48
  import "../chunk-POFBE6ZY.js";
47
49
  import {
@@ -50,7 +52,7 @@ import {
50
52
  import {
51
53
  readControlMode
52
54
  } from "../chunk-TJMAXQ5Q.js";
53
- import "../chunk-4A3UQERT.js";
55
+ import "../chunk-DMU5E6HJ.js";
54
56
  import {
55
57
  EXIT
56
58
  } from "../chunk-KZERVKTD.js";
@@ -71,8 +73,10 @@ import {
71
73
  readGlobalCliInstallAt,
72
74
  readGlobalPrefix,
73
75
  vscodePluginDir
74
- } from "../chunk-UPLV5AJF.js";
75
- import "../chunk-T7E75EKA.js";
76
+ } from "../chunk-ZR7LIQKO.js";
77
+ import {
78
+ writeJsonAtomic
79
+ } from "../chunk-T7E75EKA.js";
76
80
  import {
77
81
  getApiKey
78
82
  } from "../chunk-ON5JW2HP.js";
@@ -83,6 +87,7 @@ import {
83
87
  CANONICAL_PACKAGE,
84
88
  LEGACY_PACKAGE,
85
89
  commandsPackage,
90
+ getPackageVersion,
86
91
  globalUpgradeEntry,
87
92
  isNewerVersion,
88
93
  isReleaseVersion,
@@ -91,66 +96,38 @@ import {
91
96
  packageVersionAt
92
97
  } from "../chunk-5YY3IJT4.js";
93
98
 
94
- // bin/pushary-upgrade.ts
95
- import { existsSync as existsSync2 } from "fs";
96
- import { homedir } from "os";
97
- import { join } from "path";
98
-
99
- // src/global-upgrade.ts
100
- import { execFileSync as execFileSync2 } from "child_process";
101
- import { existsSync } from "fs";
102
-
103
- // src/auto-update-run.ts
104
- import { execFileSync } from "child_process";
105
- import { closeSync, mkdirSync, openSync, readFileSync, statSync, unlinkSync, writeSync } from "fs";
106
- import { dirname } from "path";
107
- var FINISH_UPGRADE_FLAG = "--finish-upgrade";
108
- var LOCK_STALE_MS = 10 * 60 * 1e3;
109
- 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;
110
112
  var FINISH_TIMEOUT_MS = 5 * 60 * 1e3;
111
- var LOG_LIMIT_BYTES = 256 * 1024;
112
- var UNREADABLE_INSTALLED_VERSION = "could not read the installed version";
113
- var UNREACHABLE_REGISTRY = "could not read the latest version from the npm registry";
114
- var MANUAL_UPGRADE_COMMAND = `${NPX_LATEST_COMMAND} upgrade`;
115
- var isAlreadyExists = (error) => error instanceof Error && "code" in error && error.code === "EEXIST";
116
- var createLock = (lockPath, now) => {
117
- const handle = openSync(lockPath, "wx");
118
- try {
119
- writeSync(handle, `${JSON.stringify({ pid: process.pid, at: new Date(now).toISOString() })}
120
- `);
121
- } finally {
122
- closeSync(handle);
123
- }
124
- };
125
- 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) => {
126
121
  try {
127
- createLock(lockPath, now);
128
- return "acquired";
129
- } catch (error) {
130
- if (isAlreadyExists(error)) return "held";
131
- throw error;
132
- }
133
- };
134
- var isStaleLock = (lockPath, now) => {
135
- const modifiedAt = statSync(lockPath, { throwIfNoEntry: false })?.mtimeMs;
136
- return modifiedAt !== void 0 && now - modifiedAt > LOCK_STALE_MS;
137
- };
138
- var takeOverLock = (lockPath, now) => {
139
- try {
140
- 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
+ };
141
129
  } catch {
142
- return "held";
143
- }
144
- return tryCreateLock(lockPath, now);
145
- };
146
- var isAbandonedLock = (evidence) => evidence.stale || evidence.holderPid !== void 0 && !evidence.holderAlive;
147
- var lockHolderPid = (lockPath) => {
148
- try {
149
- const holder = JSON.parse(readFileSync(lockPath, "utf8"));
150
- const pid = holder && typeof holder === "object" && "pid" in holder ? holder.pid : void 0;
151
- return typeof pid === "number" && Number.isInteger(pid) && pid > 0 ? pid : void 0;
152
- } catch {
153
- return void 0;
130
+ return { pid: void 0, usesKernelLock: false, installationPending: false };
154
131
  }
155
132
  };
156
133
  var isProcessAlive = (pid) => {
@@ -158,54 +135,138 @@ var isProcessAlive = (pid) => {
158
135
  process.kill(pid, 0);
159
136
  return true;
160
137
  } catch (error) {
161
- return error instanceof Error && "code" in error && error.code === "EPERM";
138
+ return !(error instanceof Error && "code" in error && error.code === "ESRCH");
162
139
  }
163
140
  };
164
- var readLockHolder = (lockPath, now, isAlive) => {
165
- const holderPid = lockHolderPid(lockPath);
166
- return {
167
- holderPid,
168
- holderAlive: holderPid !== void 0 && isAlive(holderPid),
169
- stale: isStaleLock(lockPath, now)
170
- };
141
+ var rejectConnection = (socket) => {
142
+ socket.destroy();
171
143
  };
172
- var acquireAutoUpdateLock = (lockPath, now) => {
173
- mkdirSync(dirname(lockPath), { recursive: true });
174
- if (tryCreateLock(lockPath, now) === "acquired") return "acquired";
175
- if (!isStaleLock(lockPath, now)) return "held";
176
- return takeOverLock(lockPath, now);
177
- };
178
- 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;
179
175
  try {
180
- unlinkSync(lockPath);
181
- } catch {
182
- 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) };
183
204
  }
184
205
  };
185
- var upgradeLockVerdict = (evidence) => {
186
- if (evidence.holderPid !== void 0 && evidence.holderPid === evidence.parentPid) return "inherited";
187
- if (isAbandonedLock(evidence)) return "take_over";
188
- return "held";
189
- };
190
- var claimUpgradeLockAt = (lockPath, request) => {
191
- const now = request.now ?? Date.now();
192
- mkdirSync(dirname(lockPath), { recursive: true });
193
- if (tryCreateLock(lockPath, now) === "acquired") return { kind: "acquired" };
194
- const holder = readLockHolder(lockPath, now, request.isAlive ?? isProcessAlive);
195
- const verdict = upgradeLockVerdict({ ...holder, parentPid: request.parentPid ?? process.ppid });
196
- if (verdict === "inherited") return { kind: "inherited" };
197
- if (verdict === "take_over" && takeOverLock(lockPath, now) === "acquired") return { kind: "acquired" };
198
- return { kind: "held", holderPid: holder.holderPid };
199
- };
200
- 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);
201
211
  try {
202
- return claimUpgradeLockAt(request.lockPath ?? autoUpdateLock(), request);
203
- } catch {
204
- return { kind: "unavailable" };
212
+ const record = readLockRecord(path);
213
+ if (record.pid === process.pid && !record.installationPending) unlinkSync(path);
214
+ } finally {
215
+ server.close();
205
216
  }
206
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`;
207
268
  var openAutoUpdateLog = (logPath) => {
208
- mkdirSync(dirname(logPath), { recursive: true });
269
+ mkdirSync2(dirname2(logPath), { recursive: true });
209
270
  const size = statSync(logPath, { throwIfNoEntry: false })?.size ?? 0;
210
271
  return openSync(logPath, size > LOG_LIMIT_BYTES ? "w" : "a");
211
272
  };
@@ -222,13 +283,15 @@ var appendAutoUpdateLog = (logPath, line) => {
222
283
  return;
223
284
  }
224
285
  };
225
- var finishInNewInstall = (request) => {
286
+ var finishInNewInstall = async (request) => {
226
287
  const handle = openAutoUpdateLog(request.logPath);
227
288
  try {
228
- execFileSync(process.execPath, [request.entry, FINISH_UPGRADE_FLAG], {
229
- stdio: ["ignore", handle, handle],
230
- env: { ...process.env, npm_config_prefix: request.prefix },
231
- 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 }
232
295
  });
233
296
  } finally {
234
297
  closeSync(handle);
@@ -236,11 +299,11 @@ var finishInNewInstall = (request) => {
236
299
  };
237
300
  var incompleteFinishDetail = (logPath) => `installed, but refreshing the agent configs did not finish cleanly; see ${logPath}`;
238
301
  var newMajorDetail = (latest) => `${latest} is a new major version; run ${MANUAL_UPGRADE_COMMAND}`;
239
- var finishInstalledUpgrade = (context, from, to) => {
302
+ var finishInstalledUpgrade = async (context, from, to) => {
240
303
  const pending = { result: "finish_pending", from, to, detail: incompleteFinishDetail(context.logPath) };
241
304
  recordAutoUpdateCheck(context.statePath, pending, new Date(context.now()).toISOString());
242
305
  try {
243
- 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 });
244
307
  return { result: "updated", from, to };
245
308
  } catch {
246
309
  return pending;
@@ -263,14 +326,13 @@ var checkAndInstall = async (context) => {
263
326
  }
264
327
  appendAutoUpdateLog(context.logPath, `${new Date(context.now()).toISOString()} installing ${context.location.packageName}@${latest} into ${context.location.prefix}`);
265
328
  try {
266
- installPackageGlobally({
329
+ installWithUpgradeLock({
267
330
  packageName: context.location.packageName,
268
331
  version: latest,
269
332
  prefix: context.location.prefix,
270
333
  timeoutMs: INSTALL_TIMEOUT_MS
271
- }, context.runNpm);
334
+ }, context.lockPath, context.runNpm);
272
335
  } catch (error) {
273
- if (pendingFinish) return finishInstalledUpgrade(context, previous.from, installed);
274
336
  return { result: "failed", from: installed, to: latest, detail: npmErrorMessage(error) };
275
337
  }
276
338
  return finishInstalledUpgrade(context, installed, latest);
@@ -292,9 +354,17 @@ var runAutoUpdate = async (run = {}) => {
292
354
  const now = run.now ?? Date.now;
293
355
  const lockPath = run.lockPath ?? autoUpdateLock();
294
356
  const logPath = run.logPath ?? autoUpdateLog();
295
- 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" };
296
365
  try {
297
366
  const check = await checkAndInstall({
367
+ lockPath,
298
368
  packageRoot,
299
369
  statePath: run.statePath ?? autoUpdateState(),
300
370
  location,
@@ -325,14 +395,12 @@ var readGlobalUpgradeSubject = (platform = process.platform) => {
325
395
  const prefix = readGlobalPrefix();
326
396
  return globalUpgradeSubject(prefix, prefix === void 0 ? { kind: "unavailable" } : readGlobalCliInstallAt(prefix, platform));
327
397
  };
328
- var finishWithThisNode = (entry) => {
329
- execFileSync2(process.execPath, [entry, FINISH_UPGRADE_FLAG], { stdio: "inherit" });
330
- };
331
- 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) => {
332
400
  const entry = globalUpgradeEntry(subject.prefix, subject.name, platform);
333
- if (!existsSync(entry)) return { kind: "entry_missing", entry };
401
+ if (!existsSync2(entry)) return { kind: "entry_missing", entry };
334
402
  try {
335
- runFinish(entry);
403
+ await runFinish(entry);
336
404
  return { kind: "finished" };
337
405
  } catch {
338
406
  return { kind: "failed" };
@@ -340,22 +408,30 @@ var finishGlobalUpgrade = (subject, platform = process.platform, runFinish = fin
340
408
  };
341
409
  var describeHeldUpgradeLock = (holderPid) => `Another Pushary upgrade is already running${holderPid === void 0 ? "" : ` (pid ${holderPid})`}. Try again when it has finished.`;
342
410
  var withUpgradeLock = async (run, request = {}) => {
343
- 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}` };
344
413
  if (claim.kind === "held") return { kind: "refused", exitCode: EXIT.FAILED, message: describeHeldUpgradeLock(claim.holderPid) };
345
414
  try {
346
415
  await run();
347
416
  } finally {
348
- if (claim.kind === "acquired") releaseAutoUpdateLock(request.lockPath ?? autoUpdateLock());
417
+ releaseAutoUpdateLock(request.lockPath ?? autoUpdateLock());
349
418
  }
350
419
  return { kind: "ran" };
351
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
+ };
352
428
  var describeUpgradeStart = (subject, latest) => `Upgrading ${subject.name} ${subject.version} -> ${latest ?? "latest"}`;
353
429
  var upgradeInstalledPackage = async (subject, effects) => {
354
430
  const pendingFinish = effects.hasPendingFinish(subject.version);
355
431
  const latest = await effects.fetchLatest(subject.name);
356
432
  const newerPublished = latest !== void 0 && isNewerVersion(latest, subject.version);
357
433
  if (pendingFinish && !newerPublished) {
358
- const finish = effects.finish(subject);
434
+ const finish = await effects.finish(subject);
359
435
  if (finish.kind !== "finished") return { kind: "pending_finish_failed", finish };
360
436
  effects.recordFinished();
361
437
  }
@@ -366,7 +442,7 @@ var upgradeInstalledPackage = async (subject, effects) => {
366
442
  } catch (error) {
367
443
  return { kind: "install_failed", detail: npmErrorMessage(error) };
368
444
  }
369
- return { kind: "installed", finish: effects.finish(subject) };
445
+ return { kind: "installed", finish: await effects.finish(subject) };
370
446
  };
371
447
 
372
448
  // bin/pushary-upgrade.ts
@@ -376,32 +452,32 @@ var detectInstallModes = () => {
376
452
  {
377
453
  label: "Cursor plugin",
378
454
  upgrade: "updates itself through Cursor",
379
- present: existsSync2(cursorPluginDir())
455
+ present: existsSync3(cursorPluginDir())
380
456
  },
381
457
  {
382
458
  label: "VS Code plugin",
383
459
  upgrade: `${NPX_LATEST_COMMAND} setup --agents vscode`,
384
- present: existsSync2(vscodePluginDir())
460
+ present: existsSync3(vscodePluginDir())
385
461
  },
386
462
  {
387
463
  label: "Codex config",
388
464
  upgrade: `${NPX_LATEST_COMMAND} setup --agents codex`,
389
- present: existsSync2(codexConfigToml())
465
+ present: existsSync3(codexConfigToml())
390
466
  },
391
467
  {
392
468
  label: "Claude Code config",
393
469
  upgrade: `${NPX_LATEST_COMMAND} setup --agents claude_code`,
394
- present: existsSync2(claudeJson())
470
+ present: existsSync3(claudeJson())
395
471
  },
396
472
  {
397
473
  label: "Gemini config",
398
474
  upgrade: `${NPX_LATEST_COMMAND} setup --agents gemini_cli`,
399
- present: existsSync2(geminiSettings())
475
+ present: existsSync3(geminiSettings())
400
476
  },
401
477
  {
402
478
  label: "Hermes plugin (pip)",
403
479
  upgrade: `${NPX_LATEST_COMMAND} setup --agents hermes`,
404
- present: existsSync2(join(homedir(), ".hermes"))
480
+ present: existsSync3(join2(homedir(), ".hermes"))
405
481
  }
406
482
  ];
407
483
  return candidates.filter((candidate) => candidate.present).map(({ label, upgrade }) => ({ label, upgrade }));
@@ -444,7 +520,7 @@ var finishUpgrade = async () => {
444
520
  const daemon = service.kind === "ok" ? await ensureDaemonRunning({ spawnDaemon: () => {
445
521
  } }) : null;
446
522
  const localProof = daemon?.kind === "running" || daemon?.kind === "started" ? await waitForDaemonRemoteProof() : null;
447
- const serverProof = localProof && daemonHasRemoteProof(localProof) ? await waitForMachineReadiness(apiKey, ["spawn", ...controlCapabilities]) : null;
523
+ const serverProof = localProof && daemonHasRemoteProof(localProof) ? await waitForMachineReadiness(apiKey, expectedLaunchCapabilities(configuredControlProviders)) : null;
448
524
  daemonReady = service.kind === "ok" && localProof !== null && daemonHasRemoteProof(localProof) && (serverProof?.kind === "ready" || serverProof?.kind === "unsupported");
449
525
  }
450
526
  } else if (controlMode === "on" && controlPolicy === "unsupported") {
@@ -551,7 +627,7 @@ var upgradeSubject = async (subject) => {
551
627
  updateAutoUpdateState(autoUpdateState(), { lastResult: "updated", detail: void 0, announced: false });
552
628
  },
553
629
  announce: (line) => console.log(line),
554
- install: (packageName) => installPackageGlobally({ packageName, version: LATEST_TAG, timeoutMs: 12e4 }),
630
+ install: (packageName) => installWithUpgradeLock({ packageName, version: LATEST_TAG, timeoutMs: 12e4 }),
555
631
  finish: (installedSubject) => finishGlobalUpgrade(installedSubject)
556
632
  });
557
633
  reportGlobalUpgrade(outcome);
@@ -569,15 +645,17 @@ var upgradeGlobalInstall = async () => {
569
645
  }
570
646
  await upgradeSubject(subject);
571
647
  };
572
- var runUnderUpgradeLock = async (run) => {
573
- const locked = await withUpgradeLock(run);
648
+ var reportUpgradeLock = (locked) => {
574
649
  if (locked.kind === "ran") return;
575
650
  console.error(locked.message);
576
651
  process.exitCode = locked.exitCode;
577
652
  };
578
653
  var main = async () => {
579
654
  if (process.argv.includes(FINISH_UPGRADE_FLAG)) {
580
- await runUnderUpgradeLock(finishUpgrade);
655
+ reportUpgradeLock(await finishUpgradeWithLock(finishUpgrade, {
656
+ installedVersion: getPackageVersion(),
657
+ statePath: autoUpdateState()
658
+ }));
581
659
  return;
582
660
  }
583
661
  const autoFlag = parseAutoUpdateFlag(process.argv.slice(2));
@@ -594,6 +672,6 @@ var main = async () => {
594
672
  await runAutomaticUpdate();
595
673
  return;
596
674
  }
597
- await runUnderUpgradeLock(upgradeGlobalInstall);
675
+ reportUpgradeLock(await withUpgradeLock(upgradeGlobalInstall));
598
676
  };
599
677
  main();
@@ -4,7 +4,7 @@ import {
4
4
  } from "../chunk-CVWG2N3Y.js";
5
5
  import {
6
6
  exitOnFailedResponse
7
- } from "../chunk-NHCUXTEM.js";
7
+ } from "../chunk-VUP5BMOD.js";
8
8
  import {
9
9
  createIo
10
10
  } from "../chunk-U4TMAANR.js";
@@ -14,16 +14,16 @@ import {
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-6UDK5TY6.js";
18
+ import "../chunk-4CW5EAOI.js";
19
+ import "../chunk-DMU5E6HJ.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-ZR7LIQKO.js";
27
27
  import "../chunk-T7E75EKA.js";
28
28
  import {
29
29
  getApiKey,
@@ -1,13 +1,13 @@
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-4CW5EAOI.js";
5
+ import "../chunk-DMU5E6HJ.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-ZR7LIQKO.js";
11
11
  import "../chunk-T7E75EKA.js";
12
12
  import "../chunk-ON5JW2HP.js";
13
13
  import {
@@ -6,7 +6,7 @@ import {
6
6
  codexHooksJsonIn,
7
7
  isPusharyCodexCommand,
8
8
  resolvePathBinary
9
- } from "./chunk-UPLV5AJF.js";
9
+ } from "./chunk-ZR7LIQKO.js";
10
10
  import {
11
11
  getPackageVersion
12
12
  } from "./chunk-5YY3IJT4.js";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  keyCheckFromResponse
3
- } from "./chunk-DVW2MYCM.js";
3
+ } from "./chunk-6UDK5TY6.js";
4
4
  import {
5
5
  installNetworkDefaults
6
- } from "./chunk-2SLCHCVN.js";
6
+ } from "./chunk-4CW5EAOI.js";
7
7
  import {
8
8
  getBaseUrl
9
9
  } from "./chunk-ON5JW2HP.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  readExistingMachineId
3
- } from "./chunk-4A3UQERT.js";
3
+ } from "./chunk-DMU5E6HJ.js";
4
4
  import {
5
5
  NetworkError
6
6
  } from "./chunk-7QLSKOSU.js";