pushary 1.8.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 (186) hide show
  1. package/CHANGELOG.md +1943 -0
  2. package/LICENSE +21 -0
  3. package/README.md +330 -0
  4. package/data/SKILL.md +591 -0
  5. package/data/cowork/SKILL.md +77 -0
  6. package/data/cursor-plugin/.cursor-plugin/plugin.json +25 -0
  7. package/data/cursor-plugin/CHANGELOG.md +70 -0
  8. package/data/cursor-plugin/CONTRIBUTING.md +40 -0
  9. package/data/cursor-plugin/LICENSE +21 -0
  10. package/data/cursor-plugin/README.md +113 -0
  11. package/data/cursor-plugin/SECURITY.md +33 -0
  12. package/data/cursor-plugin/assets/logo.png +0 -0
  13. package/data/cursor-plugin/commands/notify-when-done.md +14 -0
  14. package/data/cursor-plugin/commands/pushary-test.md +13 -0
  15. package/data/cursor-plugin/hooks/hooks.json +75 -0
  16. package/data/cursor-plugin/mcp.json +11 -0
  17. package/data/cursor-plugin/rules/pushary.mdc +40 -0
  18. package/data/cursor-plugin/scripts/pushary-gate.mjs +724 -0
  19. package/data/cursor-plugin/scripts/pushary-gate.test.mjs +137 -0
  20. package/data/cursor-plugin/scripts/redaction.mjs +51 -0
  21. package/data/cursor-plugin/skills/pushary/SKILL.md +584 -0
  22. package/data/vscode-plugin/.claude-plugin/plugin.json +28 -0
  23. package/data/vscode-plugin/.mcp.json +11 -0
  24. package/data/vscode-plugin/CHANGELOG.md +49 -0
  25. package/data/vscode-plugin/CONTRIBUTING.md +40 -0
  26. package/data/vscode-plugin/LICENSE +21 -0
  27. package/data/vscode-plugin/README.md +128 -0
  28. package/data/vscode-plugin/SECURITY.md +56 -0
  29. package/data/vscode-plugin/assets/logo.png +0 -0
  30. package/data/vscode-plugin/commands/notify-when-done.md +14 -0
  31. package/data/vscode-plugin/commands/pushary-test.md +13 -0
  32. package/data/vscode-plugin/hooks/hooks.json +60 -0
  33. package/data/vscode-plugin/scripts/pushary-gate.mjs +762 -0
  34. package/data/vscode-plugin/scripts/redaction.mjs +51 -0
  35. package/data/vscode-plugin/skills/pushary/SKILL.md +584 -0
  36. package/dist/bin/pushary-bell-hook.d.ts +1 -0
  37. package/dist/bin/pushary-bell-hook.js +46 -0
  38. package/dist/bin/pushary-bell.d.ts +1 -0
  39. package/dist/bin/pushary-bell.js +146 -0
  40. package/dist/bin/pushary-claude.d.ts +1 -0
  41. package/dist/bin/pushary-claude.js +1916 -0
  42. package/dist/bin/pushary-clean.d.ts +1 -0
  43. package/dist/bin/pushary-clean.js +1300 -0
  44. package/dist/bin/pushary-codex-bridge.d.ts +1 -0
  45. package/dist/bin/pushary-codex-bridge.js +599 -0
  46. package/dist/bin/pushary-codex-hook.d.ts +1 -0
  47. package/dist/bin/pushary-codex-hook.js +886 -0
  48. package/dist/bin/pushary-codex.d.ts +1 -0
  49. package/dist/bin/pushary-codex.js +125 -0
  50. package/dist/bin/pushary-connect.d.ts +1 -0
  51. package/dist/bin/pushary-connect.js +100 -0
  52. package/dist/bin/pushary-cowork.d.ts +1 -0
  53. package/dist/bin/pushary-cowork.js +36 -0
  54. package/dist/bin/pushary-daemon-supervisor.d.ts +1 -0
  55. package/dist/bin/pushary-daemon-supervisor.js +27 -0
  56. package/dist/bin/pushary-daemon.d.ts +1 -0
  57. package/dist/bin/pushary-daemon.js +878 -0
  58. package/dist/bin/pushary-disconnect.d.ts +1 -0
  59. package/dist/bin/pushary-disconnect.js +264 -0
  60. package/dist/bin/pushary-doctor.d.ts +1 -0
  61. package/dist/bin/pushary-doctor.js +1433 -0
  62. package/dist/bin/pushary-elicitation-hook.d.ts +1 -0
  63. package/dist/bin/pushary-elicitation-hook.js +149 -0
  64. package/dist/bin/pushary-gemini-bridge.d.ts +1 -0
  65. package/dist/bin/pushary-gemini-bridge.js +339 -0
  66. package/dist/bin/pushary-gemini-hook.d.ts +1 -0
  67. package/dist/bin/pushary-gemini-hook.js +381 -0
  68. package/dist/bin/pushary-hook.d.ts +1 -0
  69. package/dist/bin/pushary-hook.js +95 -0
  70. package/dist/bin/pushary-login.d.ts +1 -0
  71. package/dist/bin/pushary-login.js +198 -0
  72. package/dist/bin/pushary-logout.d.ts +1 -0
  73. package/dist/bin/pushary-logout.js +147 -0
  74. package/dist/bin/pushary-mcp.d.ts +1 -0
  75. package/dist/bin/pushary-mcp.js +114 -0
  76. package/dist/bin/pushary-mode.d.ts +1 -0
  77. package/dist/bin/pushary-mode.js +130 -0
  78. package/dist/bin/pushary-notification-hook.d.ts +1 -0
  79. package/dist/bin/pushary-notification-hook.js +56 -0
  80. package/dist/bin/pushary-opencode-hook.d.ts +1 -0
  81. package/dist/bin/pushary-opencode-hook.js +338 -0
  82. package/dist/bin/pushary-permission-denied-hook.d.ts +1 -0
  83. package/dist/bin/pushary-permission-denied-hook.js +60 -0
  84. package/dist/bin/pushary-permission-hook.d.ts +1 -0
  85. package/dist/bin/pushary-permission-hook.js +65 -0
  86. package/dist/bin/pushary-post-hook.d.ts +1 -0
  87. package/dist/bin/pushary-post-hook.js +70 -0
  88. package/dist/bin/pushary-prompt-hook.d.ts +1 -0
  89. package/dist/bin/pushary-prompt-hook.js +61 -0
  90. package/dist/bin/pushary-session-end-hook.d.ts +1 -0
  91. package/dist/bin/pushary-session-end-hook.js +59 -0
  92. package/dist/bin/pushary-session-start-hook.d.ts +1 -0
  93. package/dist/bin/pushary-session-start-hook.js +72 -0
  94. package/dist/bin/pushary-setup.d.ts +1 -0
  95. package/dist/bin/pushary-setup.js +2660 -0
  96. package/dist/bin/pushary-stats.d.ts +1 -0
  97. package/dist/bin/pushary-stats.js +44 -0
  98. package/dist/bin/pushary-status.d.ts +1 -0
  99. package/dist/bin/pushary-status.js +262 -0
  100. package/dist/bin/pushary-stop-hook.d.ts +1 -0
  101. package/dist/bin/pushary-stop-hook.js +63 -0
  102. package/dist/bin/pushary-stopfailure-hook.d.ts +1 -0
  103. package/dist/bin/pushary-stopfailure-hook.js +56 -0
  104. package/dist/bin/pushary-suggestions.d.ts +1 -0
  105. package/dist/bin/pushary-suggestions.js +100 -0
  106. package/dist/bin/pushary-transcript-register.d.ts +1 -0
  107. package/dist/bin/pushary-transcript-register.js +40 -0
  108. package/dist/bin/pushary-transcripts.d.ts +1 -0
  109. package/dist/bin/pushary-transcripts.js +77 -0
  110. package/dist/bin/pushary-upgrade.d.ts +1 -0
  111. package/dist/bin/pushary-upgrade.js +599 -0
  112. package/dist/bin/pushary-wait.d.ts +1 -0
  113. package/dist/bin/pushary-wait.js +122 -0
  114. package/dist/bin/pushary.d.ts +1 -0
  115. package/dist/bin/pushary.js +74 -0
  116. package/dist/chunk-2ABTSGFT.js +173 -0
  117. package/dist/chunk-2OY3ZZ5N.js +124 -0
  118. package/dist/chunk-3EGEA4KH.js +44 -0
  119. package/dist/chunk-3EVPNIBE.js +185 -0
  120. package/dist/chunk-5OP5MQI7.js +45 -0
  121. package/dist/chunk-6J2JC2RT.js +149 -0
  122. package/dist/chunk-7QLSKOSU.js +19 -0
  123. package/dist/chunk-7Z7Q4GGS.js +88 -0
  124. package/dist/chunk-A2A2YTMG.js +835 -0
  125. package/dist/chunk-AHV4LNB5.js +377 -0
  126. package/dist/chunk-AXPCESYO.js +197 -0
  127. package/dist/chunk-C2WCPBF7.js +86 -0
  128. package/dist/chunk-CJVVKOSR.js +73 -0
  129. package/dist/chunk-CULUZTWQ.js +2117 -0
  130. package/dist/chunk-CVQYGMSR.js +142 -0
  131. package/dist/chunk-CVWG2N3Y.js +16 -0
  132. package/dist/chunk-D2FX3EPW.js +15 -0
  133. package/dist/chunk-DB3ONZB4.js +44 -0
  134. package/dist/chunk-DE4Y6TCC.js +160 -0
  135. package/dist/chunk-E4FJTUH4.js +249 -0
  136. package/dist/chunk-EPCXUP3P.js +41 -0
  137. package/dist/chunk-EX3GAPA4.js +44 -0
  138. package/dist/chunk-EYK3GSRH.js +143 -0
  139. package/dist/chunk-GMXKITVA.js +70 -0
  140. package/dist/chunk-GPEGOGRJ.js +49 -0
  141. package/dist/chunk-GU3EJCVV.js +43 -0
  142. package/dist/chunk-HQ6G3B5R.js +59 -0
  143. package/dist/chunk-HZVVNGEW.js +22 -0
  144. package/dist/chunk-IPVKH2ST.js +231 -0
  145. package/dist/chunk-J5TIV3J6.js +238 -0
  146. package/dist/chunk-JU6W3XAU.js +64 -0
  147. package/dist/chunk-JYG4B33G.js +227 -0
  148. package/dist/chunk-JZTHGAPA.js +503 -0
  149. package/dist/chunk-KGDWDSNL.js +102 -0
  150. package/dist/chunk-KVQBWI5T.js +99 -0
  151. package/dist/chunk-KZERVKTD.js +16 -0
  152. package/dist/chunk-LXQ5FBBD.js +47 -0
  153. package/dist/chunk-M5ICYGHT.js +138 -0
  154. package/dist/chunk-M5JDZ45W.js +943 -0
  155. package/dist/chunk-MDEPU45E.js +1226 -0
  156. package/dist/chunk-MPWXGONV.js +157 -0
  157. package/dist/chunk-N6IOSZOE.js +185 -0
  158. package/dist/chunk-NGDD6TXY.js +192 -0
  159. package/dist/chunk-NQTHFF4W.js +2830 -0
  160. package/dist/chunk-OKXS7WDZ.js +68 -0
  161. package/dist/chunk-OLQRRFBX.js +21 -0
  162. package/dist/chunk-P7WQEVWA.js +307 -0
  163. package/dist/chunk-POFBE6ZY.js +12 -0
  164. package/dist/chunk-PQ4K74WL.js +30 -0
  165. package/dist/chunk-PUZZELE6.js +37 -0
  166. package/dist/chunk-Q7E4I6UA.js +767 -0
  167. package/dist/chunk-QCGNGEGE.js +15 -0
  168. package/dist/chunk-R7437YI3.js +151 -0
  169. package/dist/chunk-RHR6NARM.js +305 -0
  170. package/dist/chunk-SAXBC4AZ.js +60 -0
  171. package/dist/chunk-SGZJ5LYH.js +76 -0
  172. package/dist/chunk-SIFVFP6T.js +70 -0
  173. package/dist/chunk-SLFABHW3.js +150 -0
  174. package/dist/chunk-T7E75EKA.js +67 -0
  175. package/dist/chunk-TY4JT7W3.js +38 -0
  176. package/dist/chunk-U4TMAANR.js +111 -0
  177. package/dist/chunk-UI36QSBI.js +177 -0
  178. package/dist/chunk-VQKRG3AC.js +339 -0
  179. package/dist/chunk-WE2J62NC.js +1131 -0
  180. package/dist/chunk-XHKBHWLX.js +14 -0
  181. package/dist/chunk-ZSZSSJCV.js +275 -0
  182. package/dist/chunk-ZVSKMPBZ.js +135 -0
  183. package/dist/reapply-CW3MA66V.js +34 -0
  184. package/dist/src/index.d.ts +204 -0
  185. package/dist/src/index.js +64 -0
  186. package/package.json +108 -0
@@ -0,0 +1,14 @@
1
+ // src/wrapper/spawnClaude.ts
2
+ import { spawn } from "child_process";
3
+ var needsShell = (binary, platform = process.platform) => platform === "win32" && /\.(cmd|bat)$/i.test(binary);
4
+ var spawnClaude = (binary, args, options) => {
5
+ if (needsShell(binary)) {
6
+ return spawn(`"${binary}"`, args, { ...options, shell: true });
7
+ }
8
+ return spawn(binary, args, options);
9
+ };
10
+
11
+ export {
12
+ needsShell,
13
+ spawnClaude
14
+ };
@@ -0,0 +1,275 @@
1
+ import {
2
+ pusharyDir
3
+ } from "./chunk-A2A2YTMG.js";
4
+ import {
5
+ readJsonSafe
6
+ } from "./chunk-T7E75EKA.js";
7
+
8
+ // src/process-ownership.ts
9
+ import { createHash, randomUUID } from "crypto";
10
+ import { mkdirSync, readdirSync, statSync, unlinkSync, writeFileSync } from "fs";
11
+ import { createConnection, createServer } from "net";
12
+ import { join } from "path";
13
+ var MAX_PROOF_BYTES = 1024;
14
+ var ownershipDir = (options) => options.dir ?? join(pusharyDir(), "processes");
15
+ var proofPath = (dir, sessionId) => join(dir, `${createHash("sha256").update(sessionId).digest("hex")}.json`);
16
+ var isOptionalTimestamp = (value) => value === void 0 || typeof value === "string" && Number.isFinite(Date.parse(value));
17
+ var isProof = (value) => {
18
+ if (!value || typeof value !== "object") return false;
19
+ const proof = value;
20
+ return typeof proof.sessionId === "string" && Number.isInteger(proof.pid) && proof.pid > 1 && Number.isInteger(proof.port) && proof.port > 0 && proof.port <= 65535 && typeof proof.token === "string" && proof.token.length > 0 && (proof.spawnId === void 0 || typeof proof.spawnId === "string") && isOptionalTimestamp(proof.startedAt) && isOptionalTimestamp(proof.heartbeatAt);
21
+ };
22
+ var removeProof = (path, token) => {
23
+ const current = readJsonSafe(path);
24
+ if (current.kind !== "ok" || current.value.token !== token) return;
25
+ try {
26
+ unlinkSync(path);
27
+ } catch {
28
+ }
29
+ };
30
+ var processIsAlive = (pid) => {
31
+ try {
32
+ process.kill(pid, 0);
33
+ return true;
34
+ } catch (error) {
35
+ return error.code === "EPERM";
36
+ }
37
+ };
38
+ var fileTimes = (path) => {
39
+ try {
40
+ const stat = statSync(path);
41
+ return { createdAt: stat.birthtimeMs || stat.mtimeMs, modifiedAt: stat.mtimeMs };
42
+ } catch {
43
+ return { createdAt: 0, modifiedAt: 0 };
44
+ }
45
+ };
46
+ var parseTimestamp = (value) => {
47
+ if (value === void 0) return null;
48
+ const parsed = Date.parse(value);
49
+ return Number.isFinite(parsed) ? parsed : null;
50
+ };
51
+ var proofEntry = (proof, path) => {
52
+ const times = fileTimes(path);
53
+ const recordedStart = parseTimestamp(proof.startedAt);
54
+ const startedAt = recordedStart ?? times.createdAt;
55
+ return {
56
+ sessionId: proof.sessionId,
57
+ pid: proof.pid,
58
+ spawnId: proof.spawnId ?? null,
59
+ startedAt,
60
+ lastSeenAt: parseTimestamp(proof.heartbeatAt) ?? recordedStart ?? times.modifiedAt,
61
+ alive: processIsAlive(proof.pid)
62
+ };
63
+ };
64
+ var safeUnlink = (path) => {
65
+ try {
66
+ unlinkSync(path);
67
+ } catch {
68
+ }
69
+ };
70
+ var listProcessProofs = (options = {}) => {
71
+ const dir = ownershipDir(options);
72
+ const entries = [];
73
+ try {
74
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
75
+ if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
76
+ const path = join(dir, entry.name);
77
+ const stored = readJsonSafe(path);
78
+ if (stored.kind !== "ok" || !isProof(stored.value)) {
79
+ if (options.reapDead) safeUnlink(path);
80
+ continue;
81
+ }
82
+ const listed = proofEntry(stored.value, path);
83
+ if (!listed.alive && options.reapDead) safeUnlink(path);
84
+ entries.push(listed);
85
+ }
86
+ } catch {
87
+ }
88
+ return entries;
89
+ };
90
+ var pruneProofs = (dir) => {
91
+ listProcessProofs({ dir, reapDead: true });
92
+ };
93
+ var wasSpawned = (entry) => entry.spawnId !== null;
94
+ var silentSpawnedProofs = (entries, now, silenceMs) => {
95
+ const freshestByPid = /* @__PURE__ */ new Map();
96
+ for (const entry of entries) {
97
+ freshestByPid.set(entry.pid, Math.max(freshestByPid.get(entry.pid) ?? 0, entry.lastSeenAt));
98
+ }
99
+ const reported = /* @__PURE__ */ new Set();
100
+ return entries.filter((entry) => {
101
+ if (!entry.alive || !wasSpawned(entry) || reported.has(entry.pid)) return false;
102
+ if (now - (freshestByPid.get(entry.pid) ?? 0) < silenceMs) return false;
103
+ reported.add(entry.pid);
104
+ return true;
105
+ });
106
+ };
107
+ var startProcessOwnership = (identity, options = {}) => {
108
+ const dir = ownershipDir(options);
109
+ const path = proofPath(dir, identity.sessionId);
110
+ const token = randomUUID();
111
+ let stopped = false;
112
+ let proof = null;
113
+ let settleReady = () => {
114
+ };
115
+ const server = createServer((socket) => {
116
+ let request = "";
117
+ socket.setTimeout(500, () => socket.destroy());
118
+ socket.on("error", () => {
119
+ });
120
+ socket.on("data", (chunk) => {
121
+ request += String(chunk);
122
+ if (!proof || request.length > token.length) socket.destroy();
123
+ else if (request.length === token.length) {
124
+ if (request === token) socket.end(JSON.stringify(proof));
125
+ else socket.destroy();
126
+ }
127
+ });
128
+ });
129
+ server.maxConnections = 4;
130
+ const ready = new Promise((resolve) => {
131
+ settleReady = resolve;
132
+ const resolveOnce = () => resolve();
133
+ server.once("error", resolveOnce);
134
+ server.listen(0, "127.0.0.1", () => {
135
+ server.off("error", resolveOnce);
136
+ server.on("error", () => {
137
+ });
138
+ if (stopped) {
139
+ server.close();
140
+ resolve();
141
+ return;
142
+ }
143
+ const address = server.address();
144
+ if (!address || typeof address === "string") {
145
+ server.close();
146
+ resolve();
147
+ return;
148
+ }
149
+ try {
150
+ proof = { ...identity, port: address.port, token, startedAt: (/* @__PURE__ */ new Date()).toISOString() };
151
+ mkdirSync(dir, { recursive: true, mode: 448 });
152
+ pruneProofs(dir);
153
+ writeFileSync(path, JSON.stringify(proof), {
154
+ encoding: "utf8",
155
+ mode: 384,
156
+ flag: options.exclusive ? "wx" : "w"
157
+ });
158
+ resolve();
159
+ } catch {
160
+ server.close();
161
+ resolve();
162
+ }
163
+ });
164
+ });
165
+ const cleanup = () => removeProof(path, token);
166
+ process.once("exit", cleanup);
167
+ return {
168
+ ready,
169
+ touch(at = Date.now()) {
170
+ if (stopped || !proof) return;
171
+ proof = { ...proof, heartbeatAt: new Date(at).toISOString() };
172
+ try {
173
+ writeFileSync(path, JSON.stringify(proof), { encoding: "utf8", mode: 384 });
174
+ } catch {
175
+ }
176
+ },
177
+ stop() {
178
+ if (stopped) return;
179
+ stopped = true;
180
+ settleReady();
181
+ process.off("exit", cleanup);
182
+ server.close();
183
+ cleanup();
184
+ }
185
+ };
186
+ };
187
+ var withSpawnProcessOwnership = async (spawnId, run, options = {}) => {
188
+ if (!spawnId) return run();
189
+ const sessionId = `spawn:${spawnId}`;
190
+ const ownership = startProcessOwnership({
191
+ sessionId,
192
+ pid: process.pid,
193
+ spawnId
194
+ }, { ...options, exclusive: true });
195
+ await ownership.ready;
196
+ if (!await ownsLiveProcess({ sessionId, pid: process.pid, spawnId }, options)) {
197
+ ownership.stop();
198
+ throw new Error("Could not establish spawned process ownership");
199
+ }
200
+ try {
201
+ return await run();
202
+ } finally {
203
+ ownership.stop();
204
+ }
205
+ };
206
+ var ownsLiveProcess = async (request, options = {}) => {
207
+ const stored = readJsonSafe(proofPath(ownershipDir(options), request.sessionId));
208
+ if (stored.kind !== "ok" || !isProof(stored.value)) return false;
209
+ const proof = stored.value;
210
+ if (proof.sessionId !== request.sessionId || proof.pid !== request.pid) return false;
211
+ if (request.spawnId && proof.spawnId !== request.spawnId) return false;
212
+ return verifyProof(proof, options);
213
+ };
214
+ var findLiveProcessBySpawnId = async (spawnId, options = {}) => {
215
+ try {
216
+ const dir = ownershipDir(options);
217
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
218
+ if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
219
+ const stored = readJsonSafe(join(dir, entry.name));
220
+ if (stored.kind !== "ok" || !isProof(stored.value) || stored.value.spawnId !== spawnId) continue;
221
+ if (await verifyProof(stored.value, options)) return stored.value.pid;
222
+ }
223
+ } catch {
224
+ }
225
+ return null;
226
+ };
227
+ var verifyProof = (proof, options) => new Promise((resolve) => {
228
+ let settled = false;
229
+ let raw = "";
230
+ const socket = createConnection({ host: "127.0.0.1", port: proof.port });
231
+ const finish = (owned) => {
232
+ if (settled) return;
233
+ settled = true;
234
+ socket.destroy();
235
+ resolve(owned);
236
+ };
237
+ socket.setTimeout(options.connectTimeoutMs ?? 500, () => finish(false));
238
+ socket.on("error", () => finish(false));
239
+ socket.on("connect", () => socket.write(proof.token));
240
+ socket.on("data", (chunk) => {
241
+ raw += String(chunk);
242
+ if (raw.length > MAX_PROOF_BYTES) finish(false);
243
+ });
244
+ socket.on("end", () => {
245
+ try {
246
+ const live = JSON.parse(raw);
247
+ finish(isProof(live) && live.token === proof.token && live.sessionId === proof.sessionId && live.pid === proof.pid && live.spawnId === proof.spawnId);
248
+ } catch {
249
+ finish(false);
250
+ }
251
+ });
252
+ });
253
+
254
+ // src/session-idle.ts
255
+ var SESSION_IDLE_TIMEOUT_MS = 10 * 6e4;
256
+ var SESSION_REAP_GRACE_MS = 5 * 6e4;
257
+ var SESSION_REAP_SILENCE_MS = SESSION_IDLE_TIMEOUT_MS + SESSION_REAP_GRACE_MS;
258
+ var idleDeadlinePassed = (idleSince, now, timeoutMs = SESSION_IDLE_TIMEOUT_MS) => idleSince !== null && now - idleSince >= timeoutMs;
259
+ var describeIdleWindow = (timeoutMs) => {
260
+ const minutes = Math.round(timeoutMs / 6e4);
261
+ return minutes >= 1 ? `${minutes} minute${minutes === 1 ? "" : "s"}` : `${Math.round(timeoutMs / 1e3)}s`;
262
+ };
263
+
264
+ export {
265
+ listProcessProofs,
266
+ silentSpawnedProofs,
267
+ startProcessOwnership,
268
+ withSpawnProcessOwnership,
269
+ ownsLiveProcess,
270
+ findLiveProcessBySpawnId,
271
+ SESSION_IDLE_TIMEOUT_MS,
272
+ SESSION_REAP_SILENCE_MS,
273
+ idleDeadlinePassed,
274
+ describeIdleWindow
275
+ };
@@ -0,0 +1,135 @@
1
+ import {
2
+ parseFlags
3
+ } from "./chunk-GMXKITVA.js";
4
+ import {
5
+ UsageError
6
+ } from "./chunk-7QLSKOSU.js";
7
+ import {
8
+ SETUP_AGENT_CHOICES,
9
+ SETUP_CONNECT_MODES,
10
+ isSetupConnectMode,
11
+ isValidApiKey
12
+ } from "./chunk-NQTHFF4W.js";
13
+
14
+ // src/setup/options.ts
15
+ var AGENT_CHOICES = SETUP_AGENT_CHOICES;
16
+ var CONNECT_ALIASES = { auto: "app", pwa: "web" };
17
+ var parseConnectMode = (raw) => {
18
+ const value = raw.trim().toLowerCase();
19
+ if (Object.hasOwn(CONNECT_ALIASES, value)) return CONNECT_ALIASES[value];
20
+ if (isSetupConnectMode(value)) return value;
21
+ throw new UsageError(`--connect must be one of: ${SETUP_CONNECT_MODES.join(", ")} (got ${raw})`);
22
+ };
23
+ var VERIFY_MODES = ["roundtrip", "push", "none"];
24
+ var parseVerifyMode = (raw) => {
25
+ const value = raw.trim().toLowerCase();
26
+ if (VERIFY_MODES.includes(value)) return value;
27
+ throw new UsageError(`--verify must be one of: ${VERIFY_MODES.join(", ")} (got ${raw})`);
28
+ };
29
+ var CONTROL_MODES = ["auto", "on", "off"];
30
+ var parseControlMode = (raw) => {
31
+ const value = raw.trim().toLowerCase();
32
+ if (CONTROL_MODES.includes(value)) return value;
33
+ throw new UsageError(`--control must be one of: ${CONTROL_MODES.join(", ")} (got ${raw})`);
34
+ };
35
+ var effectiveVerify = (options, context) => {
36
+ if (options.verifyExplicit) return options.verify;
37
+ if (options.verify === "roundtrip") return "roundtrip";
38
+ return context.paired ? "roundtrip" : options.verify;
39
+ };
40
+ var SETUP_FLAGS = {
41
+ bools: ["key-stdin", "skip-phone", "yes", "y", "json", "dry-run", "take-over-hooks", "help", "h"],
42
+ values: ["key", "connect", "agents", "verify", "control", "transcripts"]
43
+ };
44
+ var effectiveControlMode = (options, stored, configPresent) => options.controlExplicit ? options.control : stored ?? (configPresent ? null : options.control);
45
+ var isAgentChoice = (value) => AGENT_CHOICES.includes(value);
46
+ var parseAgentSelection = (raw, yes) => {
47
+ if (raw === void 0) {
48
+ return yes ? { kind: "auto" } : { kind: "ask" };
49
+ }
50
+ const value = raw.trim().toLowerCase();
51
+ if (value === "auto") return { kind: "auto" };
52
+ if (value === "none") return { kind: "none" };
53
+ const parts = value.split(",").map((part) => part.trim()).filter((part) => part !== "");
54
+ if (parts.length === 0) throw new UsageError("--agents needs a value");
55
+ const unknown = parts.filter((part) => !isAgentChoice(part));
56
+ if (unknown.length > 0) {
57
+ throw new UsageError(
58
+ `--agents does not know: ${unknown.join(", ")}. Use auto, none, or any of: ${AGENT_CHOICES.join(", ")}`
59
+ );
60
+ }
61
+ return { kind: "explicit", agents: [...new Set(parts)] };
62
+ };
63
+ var parseSetupOptions = (input) => {
64
+ const flags = parseFlags(input.argv, SETUP_FLAGS);
65
+ const stray = [
66
+ ...flags.command !== void 0 && flags.command !== "setup" ? [flags.command] : [],
67
+ ...flags.rest
68
+ ];
69
+ if (stray.length > 0) {
70
+ throw new UsageError(
71
+ `setup takes no positional arguments (got ${stray.join(", ")}). Did you mean --agents ${stray[0]}?`
72
+ );
73
+ }
74
+ const yes = flags.bools.has("yes") || flags.bools.has("y");
75
+ const help = flags.bools.has("help") || flags.bools.has("h");
76
+ const agentsFlag = flags.values.get("agents");
77
+ const agents = parseAgentSelection(agentsFlag, yes);
78
+ const connectRaw = flags.values.get("connect");
79
+ const connectMode = connectRaw === void 0 ? "app" : parseConnectMode(connectRaw);
80
+ const verifyRaw = flags.values.get("verify");
81
+ const controlRaw = flags.values.get("control");
82
+ const transcriptsRaw = flags.values.get("transcripts")?.trim().toLowerCase();
83
+ if (transcriptsRaw !== void 0 && transcriptsRaw !== "on" && transcriptsRaw !== "off") {
84
+ throw new UsageError(`--transcripts must be one of: on, off (got ${transcriptsRaw})`);
85
+ }
86
+ const nonInteractive = yes || agentsFlag !== void 0 || flags.bools.has("json") || !input.stdinIsTty;
87
+ const verify = verifyRaw !== void 0 ? parseVerifyMode(verifyRaw) : nonInteractive ? "push" : "roundtrip";
88
+ const key = flags.values.get("key")?.trim() || null;
89
+ const keyStdin = flags.bools.has("key-stdin");
90
+ if (key && keyStdin) {
91
+ throw new UsageError("Pass either --key or --key-stdin, not both");
92
+ }
93
+ if (key && !isValidApiKey(key)) {
94
+ throw new UsageError("--key is not a valid API key. Expected the pk_xxx.xxx shape from pushary.com/dashboard/agent/settings");
95
+ }
96
+ return {
97
+ connectMode,
98
+ connectExplicit: connectRaw !== void 0,
99
+ // `--connect none` and `--skip-phone` are the same instruction, so they
100
+ // resolve to the same field rather than being checked separately at the two
101
+ // call sites that would inevitably drift apart.
102
+ skipPhone: flags.bools.has("skip-phone") || connectMode === "none",
103
+ // Anything that means "there is nobody to answer a prompt". A non-TTY stdin
104
+ // is included because that is the case that used to hang: it is better to
105
+ // take the defaults and say so than to wait forever for a keypress.
106
+ //
107
+ // --json counts. Under it stdout carries exactly one object and every human
108
+ // line is redirected to stderr, so a prompt has nowhere to render: the
109
+ // question goes to stderr while the run blocks on a keypress the caller
110
+ // cannot see it is waiting for, and whatever is reading stdout waits forever
111
+ // for an object that never arrives. Asking for machine-readable output is
112
+ // itself a statement that no human is watching.
113
+ nonInteractive,
114
+ json: flags.bools.has("json"),
115
+ dryRun: flags.bools.has("dry-run"),
116
+ help,
117
+ agents,
118
+ key,
119
+ keyStdin,
120
+ verify,
121
+ verifyExplicit: verifyRaw !== void 0,
122
+ control: controlRaw === void 0 ? "auto" : parseControlMode(controlRaw),
123
+ controlExplicit: controlRaw !== void 0,
124
+ transcripts: transcriptsRaw === void 0 ? null : transcriptsRaw === "on",
125
+ takeOverHooks: flags.bools.has("take-over-hooks")
126
+ };
127
+ };
128
+
129
+ export {
130
+ AGENT_CHOICES,
131
+ effectiveVerify,
132
+ SETUP_FLAGS,
133
+ effectiveControlMode,
134
+ parseSetupOptions
135
+ };
@@ -0,0 +1,34 @@
1
+ import {
2
+ ensureCodexTrustAfterReapply,
3
+ inspectConfiguredManagedProviders,
4
+ reapplyAllAgents,
5
+ reapplyClaudeForVersion,
6
+ reapplyClaudeSettings,
7
+ reapplyCodexSettings,
8
+ reapplyCursorPlugin,
9
+ reapplyGeminiSettings,
10
+ reapplyVsCodePlugin,
11
+ repairCodexHolds
12
+ } from "./chunk-VQKRG3AC.js";
13
+ import "./chunk-SIFVFP6T.js";
14
+ import "./chunk-RHR6NARM.js";
15
+ import "./chunk-AXPCESYO.js";
16
+ import "./chunk-6J2JC2RT.js";
17
+ import "./chunk-3EVPNIBE.js";
18
+ import "./chunk-XHKBHWLX.js";
19
+ import "./chunk-EPCXUP3P.js";
20
+ import "./chunk-A2A2YTMG.js";
21
+ import "./chunk-T7E75EKA.js";
22
+ import "./chunk-NQTHFF4W.js";
23
+ export {
24
+ ensureCodexTrustAfterReapply,
25
+ inspectConfiguredManagedProviders,
26
+ reapplyAllAgents,
27
+ reapplyClaudeForVersion,
28
+ reapplyClaudeSettings,
29
+ reapplyCodexSettings,
30
+ reapplyCursorPlugin,
31
+ reapplyGeminiSettings,
32
+ reapplyVsCodePlugin,
33
+ repairCodexHolds
34
+ };
@@ -0,0 +1,204 @@
1
+ import { CanonicalGateInputs, ReceiptMeta } from '@pushary/hook-mapping';
2
+ import { ClaudePreToolUseOutput, SetupOutcomeBeacon, MachineDecisionOrigin, SettledOutcome, AgentQuestion, PolicyConfig, ApprovalMode, ModeState } from '@pushary/contracts';
3
+ export { ApprovalMode, ModeState, PolicyConfig, ToolPolicy, resolvePolicy } from '@pushary/contracts';
4
+
5
+ interface ToolHookInput {
6
+ readonly hook_event_name?: string;
7
+ readonly tool_name: string;
8
+ readonly tool_input: Record<string, unknown>;
9
+ readonly session_id?: string;
10
+ readonly cwd?: string;
11
+ readonly transcript_path?: string;
12
+ readonly tool_use_id?: string;
13
+ readonly permission_mode?: string;
14
+ }
15
+
16
+ type HookOutput = ClaudePreToolUseOutput;
17
+ declare const handlePreToolUse: (input: ToolHookInput, canonical?: CanonicalGateInputs | null) => Promise<HookOutput | undefined>;
18
+
19
+ interface UsageReport {
20
+ readonly eventId: string;
21
+ readonly reportedAt: number;
22
+ readonly tokensIn: number;
23
+ readonly tokensOut: number;
24
+ readonly costUsd: number;
25
+ readonly deltaUsd: number;
26
+ }
27
+
28
+ type DecisionSource = 'policy_auto' | 'human' | 'terminal';
29
+ interface AgentIdentity {
30
+ readonly type: string;
31
+ readonly label: string;
32
+ }
33
+ interface AgentEvent extends Partial<Omit<SetupOutcomeBeacon, 'agents'>> {
34
+ occurredAt?: string;
35
+ toolUseId?: string;
36
+ event: string;
37
+ agentType: string;
38
+ agentName?: string;
39
+ action?: string;
40
+ machineId?: string;
41
+ repoKey?: string;
42
+ sessionId?: string;
43
+ parentSessionId?: string;
44
+ error?: string;
45
+ taskTitle?: string;
46
+ decisionSource?: DecisionSource;
47
+ decisionOrigin?: MachineDecisionOrigin;
48
+ decisionOutcome?: SettledOutcome;
49
+ toolName?: string;
50
+ toolTarget?: string;
51
+ agents?: readonly string[];
52
+ meta?: ReceiptMeta;
53
+ usage?: UsageReport;
54
+ pid?: number;
55
+ }
56
+ interface ReportEventOptions {
57
+ signal?: AbortSignal;
58
+ maxAttempts?: number;
59
+ timeoutMs?: number;
60
+ /**
61
+ * Total wall clock this call may spend, acknowledge round trip included.
62
+ *
63
+ * Deliberately not `timeoutMs`. That option doubles as the "I am a side report"
64
+ * signal (`clientCanDrain` below is keyed on its absence), so a drain-carrying
65
+ * caller that set it to bound its time would silently stop delivering the
66
+ * instructions a person sent from their phone. This bounds the clock and says
67
+ * nothing about draining.
68
+ *
69
+ * Without it, a single report could spend `timeoutMs ?? 10_000` on the event
70
+ * POST and then a further `timeoutMs ?? 10_000` acknowledging a pending command,
71
+ * which is twice the ceiling the hook was given and, on Codex, twice what the
72
+ * agent will wait before killing the process mid-flight.
73
+ */
74
+ budgetMs?: number;
75
+ /**
76
+ * Tells the server not to pop a queued instruction for this report.
77
+ *
78
+ * `canDrainCommand: false` is not enough. The route's first disjunct is
79
+ * `event === "session_end" && consumesDrainedCommand(agentType)`, which never
80
+ * reads that flag, so any `session_end` from an agent whose Stop carries text
81
+ * back pops the queue destructively. A subagent's stop is a `session_end` and
82
+ * its caller throws the response away, which is a queued instruction destroyed
83
+ * rather than delivered. `skipCommandDrain` is the route's own first check.
84
+ */
85
+ skipDrain?: boolean;
86
+ requireOk?: boolean;
87
+ /**
88
+ * Authenticate with this key instead of the ambient one.
89
+ *
90
+ * getApiKey() reads the environment and ~/.pushary/config.json, so it can only
91
+ * ever speak for a key that is already installed. setup resolves a key minutes
92
+ * before it writes one, and its abort beacons run inside that window: without
93
+ * an override they post as nobody, so the runs that fail before saveApiKey are
94
+ * exactly the runs that cannot report failing.
95
+ */
96
+ apiKey?: string;
97
+ }
98
+ interface EventResponse {
99
+ readonly ok?: boolean;
100
+ readonly pendingCommand?: string;
101
+ readonly pendingCommandId?: string;
102
+ readonly pendingCommandExpiresAtMs?: number;
103
+ }
104
+ declare const reportEvent: (event: AgentEvent, options?: ReportEventOptions) => Promise<EventResponse | null>;
105
+ declare const handlePostToolUse: (input: {
106
+ tool_name: string;
107
+ tool_input: Record<string, unknown>;
108
+ tool_response?: unknown;
109
+ tool_result?: unknown;
110
+ cwd?: string;
111
+ session_id?: string;
112
+ agent_id?: string;
113
+ transcript_path?: string;
114
+ tool_use_id?: string;
115
+ }, agent?: AgentIdentity) => Promise<string | undefined>;
116
+ interface StopHookOutput {
117
+ readonly decision: 'block';
118
+ readonly reason: string;
119
+ }
120
+ declare const handleStop: (input: {
121
+ cwd?: string;
122
+ session_id?: string;
123
+ agent_id?: string;
124
+ stop_hook_active?: boolean;
125
+ transcript_path?: string;
126
+ last_assistant_message?: string;
127
+ background_tasks?: unknown;
128
+ session_crons?: unknown;
129
+ }, agent?: AgentIdentity) => Promise<StopHookOutput | undefined>;
130
+ declare const handleNotification: (input: {
131
+ message?: string;
132
+ title?: string;
133
+ type?: string;
134
+ notification_type?: string;
135
+ hook_event_name?: string;
136
+ cwd?: string;
137
+ session_id?: string;
138
+ }, agent?: AgentIdentity) => Promise<void>;
139
+
140
+ interface AskUserParams {
141
+ question: string;
142
+ type?: 'confirm' | 'select' | 'input';
143
+ options?: string[];
144
+ questions?: AgentQuestion[];
145
+ context?: string;
146
+ agentName?: string;
147
+ sessionId?: string;
148
+ machineId?: string;
149
+ toolName?: string;
150
+ toolTarget?: string;
151
+ toolPath?: string;
152
+ repoKey?: string;
153
+ intent?: string;
154
+ action?: string;
155
+ blocker?: string;
156
+ actionBody?: string;
157
+ scopePath?: string;
158
+ toolUseId?: string;
159
+ waitEndsAt?: string;
160
+ requestId?: string;
161
+ sessionToolAware?: true;
162
+ }
163
+ interface AskUserResponse {
164
+ correlationId: string;
165
+ status: string;
166
+ handoffAction?: string;
167
+ delivery?: {
168
+ web: number;
169
+ mobile: number;
170
+ };
171
+ suppressed?: boolean;
172
+ noDevices?: boolean;
173
+ answered?: boolean;
174
+ value?: string;
175
+ note?: string;
176
+ waitEndsAt?: string;
177
+ answerUrl?: string;
178
+ }
179
+ interface AnswerResult {
180
+ status?: string;
181
+ handoffAction?: string;
182
+ answered: boolean;
183
+ value?: string;
184
+ note?: string;
185
+ }
186
+ declare const askUser: (apiKey: string, params: AskUserParams, timeoutMs?: number) => Promise<AskUserResponse>;
187
+ declare const waitForAnswer: (apiKey: string, correlationId: string, timeoutMs?: number, signal?: AbortSignal) => Promise<AnswerResult>;
188
+ declare const cancelQuestion: (apiKey: string, correlationId: string, signal?: AbortSignal) => Promise<{
189
+ cancelled?: boolean;
190
+ status?: string;
191
+ handoffAction?: string;
192
+ }>;
193
+
194
+ declare const getPolicy: (apiKey: string, expectedVersion?: string | null, agent?: string, repoAware?: boolean, sessionId?: string) => Promise<PolicyConfig>;
195
+
196
+ declare const fetchModeState: (apiKey: string, sessionId?: string, reuseWithinMs?: number, signal?: AbortSignal) => Promise<ModeState>;
197
+ declare const fetchModeOverride: (apiKey: string) => Promise<ApprovalMode | null>;
198
+
199
+ declare const getApiKey: () => string;
200
+ declare const getBaseUrl: () => string;
201
+
202
+ declare const fetchAccountPublicKey: (apiKey: string, baseUrl?: string) => Promise<string | null>;
203
+
204
+ export { askUser, cancelQuestion, fetchAccountPublicKey, fetchModeOverride, fetchModeState, getApiKey, getBaseUrl, getPolicy, handleNotification, handlePostToolUse, handlePreToolUse, handleStop, reportEvent, waitForAnswer };