@pushary/agent-hooks 0.49.1 → 0.51.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.
- package/dist/bin/pushary-claude.js +227 -32
- package/dist/bin/pushary-clean.js +3 -3
- package/dist/bin/pushary-codex-hook.js +5 -5
- package/dist/bin/pushary-codex.js +2 -2
- package/dist/bin/pushary-daemon.js +22 -3
- package/dist/bin/pushary-doctor.js +3 -3
- package/dist/bin/pushary-gemini-hook.js +4 -4
- package/dist/bin/pushary-hook.js +5 -5
- package/dist/bin/pushary-notification-hook.js +2 -2
- package/dist/bin/pushary-permission-denied-hook.js +5 -5
- package/dist/bin/pushary-permission-hook.js +5 -5
- package/dist/bin/pushary-post-hook.js +2 -2
- package/dist/bin/pushary-prompt-hook.js +2 -2
- package/dist/bin/pushary-session-end-hook.js +2 -2
- package/dist/bin/pushary-session-start-hook.js +2 -2
- package/dist/bin/pushary-setup.js +4 -4
- package/dist/bin/pushary-stats.js +2 -2
- package/dist/bin/pushary-stop-hook.js +2 -2
- package/dist/bin/pushary-stopfailure-hook.js +2 -2
- package/dist/bin/pushary-upgrade.js +2 -2
- package/dist/{chunk-O5MFSRWV.js → chunk-BEK4SJNX.js} +27 -11
- package/dist/chunk-CJBVT33U.js +595 -0
- package/dist/{chunk-7EW3USQF.js → chunk-K74PMS6N.js} +1 -1
- package/dist/{chunk-R5AJNXZS.js → chunk-LRGSMQH2.js} +1 -1
- package/dist/{chunk-SVVEC4QV.js → chunk-ORKNGSJI.js} +1 -1
- package/dist/{chunk-OQMEGREX.js → chunk-ZIQKFF4S.js} +86 -23
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.js +5 -5
- package/package.json +4 -3
- package/dist/chunk-Z5PL3K7C.js +0 -322
|
@@ -16,12 +16,14 @@ import {
|
|
|
16
16
|
reportEvent,
|
|
17
17
|
resolvePolicy,
|
|
18
18
|
waitForAnswer
|
|
19
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-BEK4SJNX.js";
|
|
20
20
|
import {
|
|
21
21
|
getMachineId
|
|
22
22
|
} from "../chunk-RN3NOEJF.js";
|
|
23
23
|
import "../chunk-DWED7BS3.js";
|
|
24
|
-
import
|
|
24
|
+
import {
|
|
25
|
+
redactSecrets
|
|
26
|
+
} from "../chunk-CJBVT33U.js";
|
|
25
27
|
import {
|
|
26
28
|
getApiKey,
|
|
27
29
|
getBaseUrl
|
|
@@ -63,7 +65,7 @@ var SIGNAL_NUMBERS = {
|
|
|
63
65
|
};
|
|
64
66
|
var FORWARDED_SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
65
67
|
var runLocalPassthrough = (binary, args2) => {
|
|
66
|
-
return new Promise((
|
|
68
|
+
return new Promise((resolve2) => {
|
|
67
69
|
let child;
|
|
68
70
|
try {
|
|
69
71
|
child = spawnClaude(binary, args2, {
|
|
@@ -71,7 +73,7 @@ var runLocalPassthrough = (binary, args2) => {
|
|
|
71
73
|
env: { ...process.env, [WRAPPER_ACTIVE_ENV]: "1" }
|
|
72
74
|
});
|
|
73
75
|
} catch {
|
|
74
|
-
|
|
76
|
+
resolve2(127);
|
|
75
77
|
return;
|
|
76
78
|
}
|
|
77
79
|
const forward = (signal) => {
|
|
@@ -86,13 +88,13 @@ var runLocalPassthrough = (binary, args2) => {
|
|
|
86
88
|
};
|
|
87
89
|
child.on("error", () => {
|
|
88
90
|
cleanup();
|
|
89
|
-
|
|
91
|
+
resolve2(127);
|
|
90
92
|
});
|
|
91
93
|
child.on("exit", (code, signal) => {
|
|
92
94
|
cleanup();
|
|
93
|
-
if (typeof code === "number")
|
|
94
|
-
else if (signal)
|
|
95
|
-
else
|
|
95
|
+
if (typeof code === "number") resolve2(code);
|
|
96
|
+
else if (signal) resolve2(128 + (SIGNAL_NUMBERS[signal] ?? 0));
|
|
97
|
+
else resolve2(0);
|
|
96
98
|
});
|
|
97
99
|
});
|
|
98
100
|
};
|
|
@@ -100,11 +102,194 @@ var runLocalPassthrough = (binary, args2) => {
|
|
|
100
102
|
// src/wrapper/dualMode.ts
|
|
101
103
|
import { basename } from "path";
|
|
102
104
|
|
|
105
|
+
// src/transcript-sync.ts
|
|
106
|
+
import { homedir } from "os";
|
|
107
|
+
import { join as join2, resolve } from "path";
|
|
108
|
+
import { existsSync, readFileSync } from "fs";
|
|
109
|
+
|
|
110
|
+
// src/transcript-capture.ts
|
|
111
|
+
import { createHash } from "crypto";
|
|
112
|
+
|
|
113
|
+
// src/crypto.ts
|
|
114
|
+
import nacl from "tweetnacl";
|
|
115
|
+
var EPH_PUB_LEN = nacl.box.publicKeyLength;
|
|
116
|
+
var BOX_NONCE_LEN = nacl.box.nonceLength;
|
|
117
|
+
var SECRETBOX_NONCE_LEN = nacl.secretbox.nonceLength;
|
|
118
|
+
var SESSION_KEY_LEN = nacl.secretbox.keyLength;
|
|
119
|
+
var toBase64 = (bytes) => Buffer.from(bytes).toString("base64");
|
|
120
|
+
var fromBase64 = (value) => new Uint8Array(Buffer.from(value, "base64"));
|
|
121
|
+
var utf8Encode = (text) => new Uint8Array(Buffer.from(text, "utf-8"));
|
|
122
|
+
var decodePublicKey = (value) => fromBase64(value);
|
|
123
|
+
var generateSessionKey = () => nacl.randomBytes(SESSION_KEY_LEN);
|
|
124
|
+
var wrapSessionKey = (sessionKey, accountPublicKey) => {
|
|
125
|
+
const ephemeral = nacl.box.keyPair();
|
|
126
|
+
const nonce = nacl.randomBytes(BOX_NONCE_LEN);
|
|
127
|
+
const box = nacl.box(sessionKey, nonce, accountPublicKey, ephemeral.secretKey);
|
|
128
|
+
const bundle = new Uint8Array(EPH_PUB_LEN + BOX_NONCE_LEN + box.length);
|
|
129
|
+
bundle.set(ephemeral.publicKey, 0);
|
|
130
|
+
bundle.set(nonce, EPH_PUB_LEN);
|
|
131
|
+
bundle.set(box, EPH_PUB_LEN + BOX_NONCE_LEN);
|
|
132
|
+
return toBase64(bundle);
|
|
133
|
+
};
|
|
134
|
+
var encryptRecord = (plaintext, sessionKey) => {
|
|
135
|
+
const nonce = nacl.randomBytes(SECRETBOX_NONCE_LEN);
|
|
136
|
+
const box = nacl.secretbox(utf8Encode(plaintext), nonce, sessionKey);
|
|
137
|
+
const bundle = new Uint8Array(SECRETBOX_NONCE_LEN + box.length);
|
|
138
|
+
bundle.set(nonce, 0);
|
|
139
|
+
bundle.set(box, SECRETBOX_NONCE_LEN);
|
|
140
|
+
return toBase64(bundle);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// src/transcript-capture.ts
|
|
144
|
+
var INTERNAL_TYPES = /* @__PURE__ */ new Set([
|
|
145
|
+
"file-history-snapshot",
|
|
146
|
+
"change",
|
|
147
|
+
"queue-operation",
|
|
148
|
+
"summary"
|
|
149
|
+
]);
|
|
150
|
+
var redactDeep = (value) => {
|
|
151
|
+
if (typeof value === "string") return redactSecrets(value);
|
|
152
|
+
if (Array.isArray(value)) return value.map(redactDeep);
|
|
153
|
+
if (value && typeof value === "object") {
|
|
154
|
+
const out = {};
|
|
155
|
+
for (const [key, inner] of Object.entries(value)) {
|
|
156
|
+
out[key] = redactDeep(inner);
|
|
157
|
+
}
|
|
158
|
+
return out;
|
|
159
|
+
}
|
|
160
|
+
return value;
|
|
161
|
+
};
|
|
162
|
+
var localIdFor = (parsed, rawLine) => typeof parsed.uuid === "string" && parsed.uuid.length > 0 ? parsed.uuid : createHash("sha256").update(rawLine).digest("hex").slice(0, 32);
|
|
163
|
+
var buildTranscriptRecords = (lines, sessionKey, seen = /* @__PURE__ */ new Set()) => {
|
|
164
|
+
const records = [];
|
|
165
|
+
const nextSeen = new Set(seen);
|
|
166
|
+
for (const line of lines) {
|
|
167
|
+
const trimmed = line.trim();
|
|
168
|
+
if (!trimmed) continue;
|
|
169
|
+
let parsed;
|
|
170
|
+
try {
|
|
171
|
+
parsed = JSON.parse(trimmed);
|
|
172
|
+
} catch {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const type = typeof parsed.type === "string" ? parsed.type : "";
|
|
176
|
+
if (INTERNAL_TYPES.has(type)) continue;
|
|
177
|
+
const localId = localIdFor(parsed, trimmed);
|
|
178
|
+
if (nextSeen.has(localId)) continue;
|
|
179
|
+
nextSeen.add(localId);
|
|
180
|
+
const content = encryptRecord(JSON.stringify(redactDeep(parsed)), sessionKey);
|
|
181
|
+
records.push({ localId, content });
|
|
182
|
+
}
|
|
183
|
+
return { records, seen: nextSeen };
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// src/transcript-api.ts
|
|
187
|
+
var fetchAccountPublicKey = async (apiKey) => {
|
|
188
|
+
try {
|
|
189
|
+
const res = await fetch(`${getBaseUrl()}/api/agent/encryption-account`, {
|
|
190
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
191
|
+
signal: AbortSignal.timeout(5e3)
|
|
192
|
+
});
|
|
193
|
+
if (!res.ok) return null;
|
|
194
|
+
const data = await res.json();
|
|
195
|
+
return typeof data.publicKey === "string" && data.publicKey.length > 0 ? data.publicKey : null;
|
|
196
|
+
} catch {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
var uploadTranscriptRecords = async (apiKey, params) => {
|
|
201
|
+
try {
|
|
202
|
+
const res = await fetch(`${getBaseUrl()}/api/agent/transcript`, {
|
|
203
|
+
method: "POST",
|
|
204
|
+
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
|
|
205
|
+
body: JSON.stringify(params),
|
|
206
|
+
signal: AbortSignal.timeout(15e3)
|
|
207
|
+
});
|
|
208
|
+
return res.ok;
|
|
209
|
+
} catch {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// src/transcript-sync.ts
|
|
215
|
+
var transcriptsEnabled = () => process.env.PUSHARY_TRANSCRIPTS === "1";
|
|
216
|
+
var claudeTranscriptPath = (cwd, sessionId) => {
|
|
217
|
+
const projectId = resolve(cwd).replace(/[^a-zA-Z0-9-]/g, "-");
|
|
218
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR || join2(homedir(), ".claude");
|
|
219
|
+
return join2(configDir, "projects", projectId, `${sessionId}.jsonl`);
|
|
220
|
+
};
|
|
221
|
+
var createSyncState = () => ({
|
|
222
|
+
sessionKey: null,
|
|
223
|
+
wrappedKey: null,
|
|
224
|
+
seen: /* @__PURE__ */ new Set(),
|
|
225
|
+
keyUploaded: false
|
|
226
|
+
});
|
|
227
|
+
var syncTick = async (lines, state, uploader, sessionId, machineId) => {
|
|
228
|
+
let sessionKey = state.sessionKey;
|
|
229
|
+
let wrappedKey = state.wrappedKey;
|
|
230
|
+
if (!sessionKey) {
|
|
231
|
+
const publicKey = await uploader.fetchAccountPublicKey();
|
|
232
|
+
if (!publicKey) return state;
|
|
233
|
+
sessionKey = generateSessionKey();
|
|
234
|
+
wrappedKey = wrapSessionKey(sessionKey, decodePublicKey(publicKey));
|
|
235
|
+
}
|
|
236
|
+
const built = buildTranscriptRecords(lines, sessionKey, state.seen);
|
|
237
|
+
if (built.records.length === 0) {
|
|
238
|
+
return { sessionKey, wrappedKey, seen: built.seen, keyUploaded: state.keyUploaded };
|
|
239
|
+
}
|
|
240
|
+
const ok = await uploader.uploadRecords({
|
|
241
|
+
sessionId,
|
|
242
|
+
machineId,
|
|
243
|
+
dataEncryptionKey: state.keyUploaded ? void 0 : wrappedKey ?? void 0,
|
|
244
|
+
records: built.records
|
|
245
|
+
});
|
|
246
|
+
return {
|
|
247
|
+
sessionKey,
|
|
248
|
+
wrappedKey,
|
|
249
|
+
seen: ok ? built.seen : state.seen,
|
|
250
|
+
keyUploaded: state.keyUploaded || ok
|
|
251
|
+
};
|
|
252
|
+
};
|
|
253
|
+
var readLines = (path) => {
|
|
254
|
+
try {
|
|
255
|
+
return existsSync(path) ? readFileSync(path, "utf-8").split("\n") : [];
|
|
256
|
+
} catch {
|
|
257
|
+
return [];
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
var startTranscriptSync = (opts) => {
|
|
261
|
+
const uploader = {
|
|
262
|
+
fetchAccountPublicKey: () => fetchAccountPublicKey(opts.apiKey),
|
|
263
|
+
uploadRecords: (params) => uploadTranscriptRecords(opts.apiKey, params)
|
|
264
|
+
};
|
|
265
|
+
const path = claudeTranscriptPath(opts.cwd, opts.sessionId);
|
|
266
|
+
let state = createSyncState();
|
|
267
|
+
let running = true;
|
|
268
|
+
let inFlight = false;
|
|
269
|
+
const tick = async () => {
|
|
270
|
+
if (!running || inFlight) return;
|
|
271
|
+
inFlight = true;
|
|
272
|
+
try {
|
|
273
|
+
state = await syncTick(readLines(path), state, uploader, opts.sessionId, opts.machineId);
|
|
274
|
+
} catch {
|
|
275
|
+
} finally {
|
|
276
|
+
inFlight = false;
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
const timer = setInterval(() => void tick(), opts.intervalMs ?? 3e3);
|
|
280
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
281
|
+
void tick();
|
|
282
|
+
return () => {
|
|
283
|
+
running = false;
|
|
284
|
+
clearInterval(timer);
|
|
285
|
+
};
|
|
286
|
+
};
|
|
287
|
+
|
|
103
288
|
// src/wrapper/sdkLoader.ts
|
|
104
289
|
import { spawn } from "child_process";
|
|
105
|
-
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
106
|
-
import { homedir } from "os";
|
|
107
|
-
import { join as
|
|
290
|
+
import { existsSync as existsSync2, mkdirSync, writeFileSync } from "fs";
|
|
291
|
+
import { homedir as homedir2 } from "os";
|
|
292
|
+
import { join as join3 } from "path";
|
|
108
293
|
import { pathToFileURL } from "url";
|
|
109
294
|
var userMessage = (text) => ({
|
|
110
295
|
type: "user",
|
|
@@ -114,8 +299,8 @@ var userMessage = (text) => ({
|
|
|
114
299
|
var SDK_VERSION = "0.3.207";
|
|
115
300
|
var SDK_PACKAGE = "@anthropic-ai/claude-agent-sdk";
|
|
116
301
|
var SDK_SPEC = `${SDK_PACKAGE}@${SDK_VERSION}`;
|
|
117
|
-
var sdkCacheDir = () => process.env.PUSHARY_REMOTE_SDK_DIR?.trim() ||
|
|
118
|
-
var cachedSdkEntry = () =>
|
|
302
|
+
var sdkCacheDir = () => process.env.PUSHARY_REMOTE_SDK_DIR?.trim() || join3(homedir2(), ".pushary", "remote-sdk");
|
|
303
|
+
var cachedSdkEntry = () => join3(sdkCacheDir(), "node_modules", "@anthropic-ai", "claude-agent-sdk", "sdk.mjs");
|
|
119
304
|
var importSdk = async (specifierOrUrl) => {
|
|
120
305
|
try {
|
|
121
306
|
const mod = await import(specifierOrUrl);
|
|
@@ -130,7 +315,7 @@ var loadClaudeSdk = async () => {
|
|
|
130
315
|
const fromResolution = await importSdk(SDK_PACKAGE);
|
|
131
316
|
if (fromResolution) return fromResolution;
|
|
132
317
|
const entry = cachedSdkEntry();
|
|
133
|
-
if (
|
|
318
|
+
if (existsSync2(entry)) return importSdk(pathToFileURL(entry).href);
|
|
134
319
|
return null;
|
|
135
320
|
};
|
|
136
321
|
var ensureClaudeSdk = async (log) => {
|
|
@@ -139,8 +324,8 @@ var ensureClaudeSdk = async (log) => {
|
|
|
139
324
|
const dir = sdkCacheDir();
|
|
140
325
|
try {
|
|
141
326
|
mkdirSync(dir, { recursive: true });
|
|
142
|
-
const pkgJson =
|
|
143
|
-
if (!
|
|
327
|
+
const pkgJson = join3(dir, "package.json");
|
|
328
|
+
if (!existsSync2(pkgJson)) {
|
|
144
329
|
writeFileSync(pkgJson, `${JSON.stringify({ name: "pushary-remote-sdk", private: true })}
|
|
145
330
|
`);
|
|
146
331
|
}
|
|
@@ -149,14 +334,14 @@ var ensureClaudeSdk = async (log) => {
|
|
|
149
334
|
}
|
|
150
335
|
log?.("[pushary] setting up phone control (one-time, fetching the Claude Agent SDK)...");
|
|
151
336
|
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
152
|
-
const installed = await new Promise((
|
|
337
|
+
const installed = await new Promise((resolve2) => {
|
|
153
338
|
const child = spawn(
|
|
154
339
|
npmCmd,
|
|
155
340
|
["install", "--no-optional", "--no-audit", "--no-fund", "--loglevel=error", SDK_SPEC],
|
|
156
341
|
{ cwd: dir, stdio: "ignore", shell: process.platform === "win32" }
|
|
157
342
|
);
|
|
158
|
-
child.on("error", () =>
|
|
159
|
-
child.on("close", (code) =>
|
|
343
|
+
child.on("error", () => resolve2(false));
|
|
344
|
+
child.on("close", (code) => resolve2(code === 0));
|
|
160
345
|
});
|
|
161
346
|
if (!installed) {
|
|
162
347
|
log?.(`[pushary] could not auto-install the SDK. Install it manually: npm i -g ${SDK_SPEC}`);
|
|
@@ -185,10 +370,10 @@ var BoundedInputQueue = class {
|
|
|
185
370
|
if (this.closed) return;
|
|
186
371
|
this.pushedCount++;
|
|
187
372
|
if (this.waiter) {
|
|
188
|
-
const
|
|
373
|
+
const resolve2 = this.waiter;
|
|
189
374
|
this.waiter = void 0;
|
|
190
375
|
this.deliveredCount++;
|
|
191
|
-
|
|
376
|
+
resolve2({ value: item, done: false });
|
|
192
377
|
return;
|
|
193
378
|
}
|
|
194
379
|
this.buffer.push(item);
|
|
@@ -205,9 +390,9 @@ var BoundedInputQueue = class {
|
|
|
205
390
|
this.closed = true;
|
|
206
391
|
this.buffer.length = 0;
|
|
207
392
|
if (this.waiter) {
|
|
208
|
-
const
|
|
393
|
+
const resolve2 = this.waiter;
|
|
209
394
|
this.waiter = void 0;
|
|
210
|
-
|
|
395
|
+
resolve2({ value: void 0, done: true });
|
|
211
396
|
}
|
|
212
397
|
}
|
|
213
398
|
// End the CURRENT consumer without closing the queue, so a fresh query can
|
|
@@ -216,9 +401,9 @@ var BoundedInputQueue = class {
|
|
|
216
401
|
// instead of hanging on a promise that would otherwise never settle.
|
|
217
402
|
detachConsumer() {
|
|
218
403
|
if (this.waiter) {
|
|
219
|
-
const
|
|
404
|
+
const resolve2 = this.waiter;
|
|
220
405
|
this.waiter = void 0;
|
|
221
|
-
|
|
406
|
+
resolve2({ value: void 0, done: true });
|
|
222
407
|
}
|
|
223
408
|
}
|
|
224
409
|
// Drop every buffered item without closing the queue. Used when phone control
|
|
@@ -257,8 +442,8 @@ var BoundedInputQueue = class {
|
|
|
257
442
|
new Error("BoundedInputQueue: concurrent consumers are not supported")
|
|
258
443
|
);
|
|
259
444
|
}
|
|
260
|
-
return new Promise((
|
|
261
|
-
this.waiter =
|
|
445
|
+
return new Promise((resolve2) => {
|
|
446
|
+
this.waiter = resolve2;
|
|
262
447
|
});
|
|
263
448
|
},
|
|
264
449
|
return: () => {
|
|
@@ -300,8 +485,8 @@ var createRemoteApprover = (deps) => {
|
|
|
300
485
|
const deadline = now() + windowMs;
|
|
301
486
|
while (now() < deadline && !entry.controller.signal.aborted) {
|
|
302
487
|
const remaining = Math.min(Math.max(deadline - now(), 1e3), 3e4);
|
|
303
|
-
const abortPromise = new Promise((
|
|
304
|
-
entry.controller.signal.addEventListener("abort", () =>
|
|
488
|
+
const abortPromise = new Promise((resolve2) => {
|
|
489
|
+
entry.controller.signal.addEventListener("abort", () => resolve2({ answered: false, aborted: true }), {
|
|
305
490
|
once: true
|
|
306
491
|
});
|
|
307
492
|
});
|
|
@@ -343,7 +528,7 @@ var createRemoteApprover = (deps) => {
|
|
|
343
528
|
const sessionId = deps.getSessionId?.();
|
|
344
529
|
const modeState = await fetchModeState2(deps.apiKey, sessionId);
|
|
345
530
|
if (modeState.kill) return deny("Stopped by user \u2014 this agent was halted from Pushary");
|
|
346
|
-
const policyConfig = await getPolicy2(deps.apiKey, modeState.policyVersion);
|
|
531
|
+
const policyConfig = await getPolicy2(deps.apiKey, modeState.policyVersion, "claude_code");
|
|
347
532
|
const policy = resolvePolicy2(policyConfig, toolName, modeState.mode, input);
|
|
348
533
|
if (policy.timeoutSeconds === 0 && policy.timeoutAction === "approve") return allow(input);
|
|
349
534
|
if (policy.timeoutSeconds === 0 && policy.timeoutAction === "deny") {
|
|
@@ -878,7 +1063,7 @@ var nativeArgs = (session) => {
|
|
|
878
1063
|
};
|
|
879
1064
|
var runLocalLeg = (session, deps = {}) => {
|
|
880
1065
|
const spawn2 = deps.spawn ?? spawnClaude;
|
|
881
|
-
return new Promise((
|
|
1066
|
+
return new Promise((resolve2) => {
|
|
882
1067
|
const legAbort = new AbortController();
|
|
883
1068
|
let exitReason = null;
|
|
884
1069
|
let settled = false;
|
|
@@ -886,7 +1071,7 @@ var runLocalLeg = (session, deps = {}) => {
|
|
|
886
1071
|
if (settled) return;
|
|
887
1072
|
settled = true;
|
|
888
1073
|
session.setActiveLeg(null);
|
|
889
|
-
|
|
1074
|
+
resolve2(exitReason ?? { type: "exit", code: 0 });
|
|
890
1075
|
};
|
|
891
1076
|
const control = {
|
|
892
1077
|
onPhoneCommand: () => {
|
|
@@ -1183,12 +1368,20 @@ var runDualMode = async (binary, args2, startingMode) => {
|
|
|
1183
1368
|
}).catch(() => {
|
|
1184
1369
|
});
|
|
1185
1370
|
};
|
|
1371
|
+
let stopTranscript;
|
|
1372
|
+
let transcriptStarted = false;
|
|
1373
|
+
const ensureTranscriptSync = () => {
|
|
1374
|
+
if (transcriptStarted || !transcriptsEnabled() || !sessionId) return;
|
|
1375
|
+
transcriptStarted = true;
|
|
1376
|
+
stopTranscript = startTranscriptSync({ apiKey, sessionId, cwd: process.cwd(), machineId });
|
|
1377
|
+
};
|
|
1186
1378
|
const setSessionId = (id) => {
|
|
1187
1379
|
const isNew = sessionId !== id;
|
|
1188
1380
|
sessionId = id;
|
|
1189
1381
|
sessionCreated = true;
|
|
1190
1382
|
relay?.reannounce();
|
|
1191
1383
|
if (isNew) void announcePresence("session_start", "Remote session ready \u2014 reachable from your phone");
|
|
1384
|
+
ensureTranscriptSync();
|
|
1192
1385
|
};
|
|
1193
1386
|
const onPhoneCommand = (command) => {
|
|
1194
1387
|
if (!remoteAvailable) {
|
|
@@ -1271,6 +1464,7 @@ var runDualMode = async (binary, args2, startingMode) => {
|
|
|
1271
1464
|
"[pushary] reachable from your phone \u2014 send an instruction from the app when this goes idle (Ctrl-] keeps the terminal).\n"
|
|
1272
1465
|
);
|
|
1273
1466
|
void announcePresence("session_start", "Session ready \u2014 reachable from your phone");
|
|
1467
|
+
ensureTranscriptSync();
|
|
1274
1468
|
}
|
|
1275
1469
|
if (startingMode === "remote" && initialPrompt) input.push(userMessage(initialPrompt));
|
|
1276
1470
|
const exitCode = await driveModeLoop({
|
|
@@ -1300,6 +1494,7 @@ var runDualMode = async (binary, args2, startingMode) => {
|
|
|
1300
1494
|
process.removeListener("exit", restoreStdin);
|
|
1301
1495
|
relay?.stop();
|
|
1302
1496
|
poller.stop();
|
|
1497
|
+
stopTranscript?.();
|
|
1303
1498
|
approver.teardown();
|
|
1304
1499
|
input.close();
|
|
1305
1500
|
stdin.dispose();
|
|
@@ -6,15 +6,15 @@ import {
|
|
|
6
6
|
removeCodexHooks,
|
|
7
7
|
removeGeminiSettings,
|
|
8
8
|
removeInstructionBlock
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-ORKNGSJI.js";
|
|
10
10
|
import {
|
|
11
11
|
execNpm
|
|
12
12
|
} from "../chunk-J7JWI3KU.js";
|
|
13
13
|
import {
|
|
14
14
|
removeClaudeMcpServers,
|
|
15
15
|
removePusharySettings
|
|
16
|
-
} from "../chunk-
|
|
17
|
-
import "../chunk-
|
|
16
|
+
} from "../chunk-K74PMS6N.js";
|
|
17
|
+
import "../chunk-CJBVT33U.js";
|
|
18
18
|
|
|
19
19
|
// bin/pushary-clean.ts
|
|
20
20
|
import { existsSync, readFileSync, writeFileSync, rmSync, readdirSync } from "fs";
|
|
@@ -31,21 +31,21 @@ import {
|
|
|
31
31
|
toCodexWire,
|
|
32
32
|
toPolicyLookup,
|
|
33
33
|
waitForAnswer
|
|
34
|
-
} from "../chunk-
|
|
34
|
+
} from "../chunk-BEK4SJNX.js";
|
|
35
35
|
import {
|
|
36
36
|
getMachineId
|
|
37
37
|
} from "../chunk-RN3NOEJF.js";
|
|
38
38
|
import {
|
|
39
39
|
isGatingMoment,
|
|
40
40
|
recordKeylessMoment
|
|
41
|
-
} from "../chunk-
|
|
41
|
+
} from "../chunk-LRGSMQH2.js";
|
|
42
42
|
import "../chunk-DWED7BS3.js";
|
|
43
43
|
import {
|
|
44
44
|
DECISION_LINE_MAX,
|
|
45
45
|
effectiveWaitSeconds,
|
|
46
46
|
hookWaitClamped,
|
|
47
47
|
hookWaitDeadline
|
|
48
|
-
} from "../chunk-
|
|
48
|
+
} from "../chunk-CJBVT33U.js";
|
|
49
49
|
import {
|
|
50
50
|
getApiKey
|
|
51
51
|
} from "../chunk-NKXSILEW.js";
|
|
@@ -170,7 +170,7 @@ var decidePermissionRequest = async (input) => {
|
|
|
170
170
|
const modeState = await fetchModeState(apiKey, input.session_id);
|
|
171
171
|
if (modeState.kill) return codexDeny(KILL_REASON);
|
|
172
172
|
const lookup = toPolicyLookup(input.tool_name ?? "", input.tool_input ?? {}, input.cwd);
|
|
173
|
-
const policy = await getPolicy(apiKey, modeState.policyVersion);
|
|
173
|
+
const policy = await getPolicy(apiKey, modeState.policyVersion, "codex");
|
|
174
174
|
const toolPolicy = resolvePolicy(policy, lookup.tool, modeState.mode, lookup.input);
|
|
175
175
|
if (toolPolicy.timeoutSeconds === 0 && toolPolicy.timeoutAction === "approve") return codexAllow();
|
|
176
176
|
if (toolPolicy.timeoutSeconds === 0 && toolPolicy.timeoutAction === "deny") {
|
|
@@ -229,7 +229,7 @@ var decidePreToolUse = async (input) => {
|
|
|
229
229
|
const modeState = await fetchModeState(apiKey, input.session_id);
|
|
230
230
|
if (modeState.kill) return codexDeny(KILL_REASON);
|
|
231
231
|
const lookup = toPolicyLookup(input.tool_name ?? "", input.tool_input ?? {}, input.cwd);
|
|
232
|
-
const policy = await getPolicy(apiKey, modeState.policyVersion);
|
|
232
|
+
const policy = await getPolicy(apiKey, modeState.policyVersion, "codex");
|
|
233
233
|
const toolPolicy = resolvePolicy(policy, lookup.tool, modeState.mode, lookup.input);
|
|
234
234
|
if (toolPolicy.timeoutSeconds === 0 && toolPolicy.timeoutAction === "approve") return codexPass();
|
|
235
235
|
if (toolPolicy.timeoutSeconds === 0 && toolPolicy.timeoutAction === "deny") {
|
|
@@ -3,12 +3,12 @@ import {
|
|
|
3
3
|
askUser,
|
|
4
4
|
reportEvent,
|
|
5
5
|
waitForAnswer
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-BEK4SJNX.js";
|
|
7
7
|
import {
|
|
8
8
|
getMachineId
|
|
9
9
|
} from "../chunk-RN3NOEJF.js";
|
|
10
10
|
import "../chunk-DWED7BS3.js";
|
|
11
|
-
import "../chunk-
|
|
11
|
+
import "../chunk-CJBVT33U.js";
|
|
12
12
|
import {
|
|
13
13
|
getApiKey
|
|
14
14
|
} from "../chunk-NKXSILEW.js";
|
|
@@ -26,6 +26,18 @@ var MAX_ERROR_STREAK = 6;
|
|
|
26
26
|
var REQUEST_TIMEOUT_MS = 1e4;
|
|
27
27
|
var MAX_SPAWNS_PER_WINDOW = 5;
|
|
28
28
|
var SPAWN_WINDOW_MS = 6e4;
|
|
29
|
+
var NATIVE_AGENT_BINARIES = { codex: "codex", gemini: "gemini" };
|
|
30
|
+
var buildSpawnLaunch = (req, opts) => {
|
|
31
|
+
const agent = req.agent === "codex" || req.agent === "gemini" ? req.agent : "claude";
|
|
32
|
+
const model = typeof req.model === "string" && req.model.length > 0 ? ["--model", req.model] : [];
|
|
33
|
+
if (agent === "claude") {
|
|
34
|
+
const claudeArgs = ["claude", "--remote", ...model, "-p", req.prompt];
|
|
35
|
+
return opts.spawnDirect ? { direct: true, command: opts.execPath, args: [opts.siblingEntry, ...claudeArgs], agent } : { direct: false, command: opts.pusharyBin, args: claudeArgs, agent };
|
|
36
|
+
}
|
|
37
|
+
const command = opts.resolveNative(NATIVE_AGENT_BINARIES[agent]);
|
|
38
|
+
const args = agent === "codex" ? ["exec", ...model, req.prompt] : [...model, "-p", req.prompt];
|
|
39
|
+
return { direct: false, command, args, agent };
|
|
40
|
+
};
|
|
29
41
|
var spawnRateAllows = (timestamps, now, maxPerWindow = MAX_SPAWNS_PER_WINDOW, windowMs = SPAWN_WINDOW_MS) => {
|
|
30
42
|
while (timestamps.length > 0 && now - timestamps[0] > windowMs) timestamps.shift();
|
|
31
43
|
return timestamps.length < maxPerWindow;
|
|
@@ -55,13 +67,20 @@ var runSpawnDaemon = async () => {
|
|
|
55
67
|
const launch = (req) => {
|
|
56
68
|
spawnTimestamps.push(Date.now());
|
|
57
69
|
const cwd = req.cwd && existsSync(req.cwd) ? req.cwd : process.cwd();
|
|
58
|
-
const
|
|
70
|
+
const env = { ...process.env, PUSHARY_SPAWN_ID: req.id };
|
|
71
|
+
const plan = buildSpawnLaunch(req, {
|
|
72
|
+
spawnDirect,
|
|
73
|
+
execPath: process.execPath,
|
|
74
|
+
siblingEntry,
|
|
75
|
+
pusharyBin,
|
|
76
|
+
resolveNative: (name) => resolveGlobalBinary(name) ?? name
|
|
77
|
+
});
|
|
59
78
|
try {
|
|
60
|
-
const child =
|
|
79
|
+
const child = plan.direct ? spawn(plan.command, plan.args, { cwd, detached: true, stdio: "ignore", env }) : spawnClaude(plan.command, plan.args, { cwd, detached: true, stdio: "ignore", env });
|
|
61
80
|
child.on("error", (err) => stderr(`[pushary] could not launch session: ${err.message}
|
|
62
81
|
`));
|
|
63
82
|
child.unref();
|
|
64
|
-
stderr(`[pushary] launched a phone-requested session in ${basename(cwd)}
|
|
83
|
+
stderr(`[pushary] launched a phone-requested ${plan.agent} session in ${basename(cwd)}
|
|
65
84
|
`);
|
|
66
85
|
} catch (err) {
|
|
67
86
|
stderr(`[pushary] could not launch session: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -11,18 +11,18 @@ import {
|
|
|
11
11
|
missingGeminiHookEvents,
|
|
12
12
|
readCodexMcpAuth,
|
|
13
13
|
untrustedCodexHookEvents
|
|
14
|
-
} from "../chunk-
|
|
14
|
+
} from "../chunk-ORKNGSJI.js";
|
|
15
15
|
import {
|
|
16
16
|
execNpm
|
|
17
17
|
} from "../chunk-J7JWI3KU.js";
|
|
18
18
|
import {
|
|
19
19
|
readLedgerSummary
|
|
20
|
-
} from "../chunk-
|
|
20
|
+
} from "../chunk-LRGSMQH2.js";
|
|
21
21
|
import {
|
|
22
22
|
callMcpTool,
|
|
23
23
|
sendMcpRequest
|
|
24
24
|
} from "../chunk-DWED7BS3.js";
|
|
25
|
-
import "../chunk-
|
|
25
|
+
import "../chunk-CJBVT33U.js";
|
|
26
26
|
import {
|
|
27
27
|
getBaseUrl
|
|
28
28
|
} from "../chunk-NKXSILEW.js";
|
|
@@ -23,21 +23,21 @@ import {
|
|
|
23
23
|
savePendingQuestion,
|
|
24
24
|
sendNotification,
|
|
25
25
|
waitForAnswer
|
|
26
|
-
} from "../chunk-
|
|
26
|
+
} from "../chunk-BEK4SJNX.js";
|
|
27
27
|
import {
|
|
28
28
|
getMachineId
|
|
29
29
|
} from "../chunk-RN3NOEJF.js";
|
|
30
30
|
import {
|
|
31
31
|
isGatingMoment,
|
|
32
32
|
recordKeylessMoment
|
|
33
|
-
} from "../chunk-
|
|
33
|
+
} from "../chunk-LRGSMQH2.js";
|
|
34
34
|
import "../chunk-DWED7BS3.js";
|
|
35
35
|
import {
|
|
36
36
|
DECISION_LINE_MAX,
|
|
37
37
|
effectiveWaitSeconds,
|
|
38
38
|
hookWaitClamped,
|
|
39
39
|
hookWaitDeadline
|
|
40
|
-
} from "../chunk-
|
|
40
|
+
} from "../chunk-CJBVT33U.js";
|
|
41
41
|
import {
|
|
42
42
|
getApiKey
|
|
43
43
|
} from "../chunk-NKXSILEW.js";
|
|
@@ -209,7 +209,7 @@ var decideBeforeTool = async (input) => {
|
|
|
209
209
|
const modeState = await fetchModeState(apiKey, input.session_id);
|
|
210
210
|
if (modeState.kill) return geminiDeny(KILL_REASON);
|
|
211
211
|
const lookup = toGeminiPolicyLookup(input.tool_name ?? "", input.tool_input ?? {});
|
|
212
|
-
const policy = await getPolicy(apiKey, modeState.policyVersion);
|
|
212
|
+
const policy = await getPolicy(apiKey, modeState.policyVersion, "gemini_cli");
|
|
213
213
|
const toolPolicy = resolvePolicy(policy, lookup.tool, modeState.mode, lookup.input);
|
|
214
214
|
if (toolPolicy.timeoutSeconds === 0 && toolPolicy.timeoutAction === "approve") return geminiAllow();
|
|
215
215
|
if (toolPolicy.timeoutSeconds === 0 && toolPolicy.timeoutAction === "deny") {
|
package/dist/bin/pushary-hook.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handlePreToolUse
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-ZIQKFF4S.js";
|
|
5
|
+
import "../chunk-K74PMS6N.js";
|
|
6
6
|
import "../chunk-KQYIHZ5E.js";
|
|
7
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-BEK4SJNX.js";
|
|
8
8
|
import "../chunk-RN3NOEJF.js";
|
|
9
|
-
import "../chunk-
|
|
9
|
+
import "../chunk-LRGSMQH2.js";
|
|
10
10
|
import "../chunk-DWED7BS3.js";
|
|
11
|
-
import "../chunk-
|
|
11
|
+
import "../chunk-CJBVT33U.js";
|
|
12
12
|
import "../chunk-NKXSILEW.js";
|
|
13
13
|
|
|
14
14
|
// bin/pushary-hook.ts
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handleNotification
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-BEK4SJNX.js";
|
|
5
5
|
import "../chunk-RN3NOEJF.js";
|
|
6
6
|
import "../chunk-DWED7BS3.js";
|
|
7
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-CJBVT33U.js";
|
|
8
8
|
import "../chunk-NKXSILEW.js";
|
|
9
9
|
|
|
10
10
|
// bin/pushary-notification-hook.ts
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handlePermissionDenied
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-ZIQKFF4S.js";
|
|
5
|
+
import "../chunk-K74PMS6N.js";
|
|
6
6
|
import "../chunk-KQYIHZ5E.js";
|
|
7
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-BEK4SJNX.js";
|
|
8
8
|
import "../chunk-RN3NOEJF.js";
|
|
9
|
-
import "../chunk-
|
|
9
|
+
import "../chunk-LRGSMQH2.js";
|
|
10
10
|
import "../chunk-DWED7BS3.js";
|
|
11
|
-
import "../chunk-
|
|
11
|
+
import "../chunk-CJBVT33U.js";
|
|
12
12
|
import "../chunk-NKXSILEW.js";
|
|
13
13
|
|
|
14
14
|
// bin/pushary-permission-denied-hook.ts
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handlePermissionRequest
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-ZIQKFF4S.js";
|
|
5
|
+
import "../chunk-K74PMS6N.js";
|
|
6
6
|
import "../chunk-KQYIHZ5E.js";
|
|
7
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-BEK4SJNX.js";
|
|
8
8
|
import "../chunk-RN3NOEJF.js";
|
|
9
|
-
import "../chunk-
|
|
9
|
+
import "../chunk-LRGSMQH2.js";
|
|
10
10
|
import "../chunk-DWED7BS3.js";
|
|
11
|
-
import "../chunk-
|
|
11
|
+
import "../chunk-CJBVT33U.js";
|
|
12
12
|
import "../chunk-NKXSILEW.js";
|
|
13
13
|
|
|
14
14
|
// bin/pushary-permission-hook.ts
|