@pushary/agent-hooks 0.86.0 → 0.87.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-bell-hook.js +4 -4
- package/dist/bin/pushary-bell.js +6 -6
- package/dist/bin/pushary-claude.js +12 -10
- package/dist/bin/pushary-clean.js +6 -6
- package/dist/bin/pushary-codex-bridge.js +5 -5
- package/dist/bin/pushary-codex-hook.js +13 -29
- package/dist/bin/pushary-codex.js +6 -4
- package/dist/bin/pushary-connect.js +6 -6
- package/dist/bin/pushary-daemon.js +4 -4
- package/dist/bin/pushary-disconnect.js +22 -8
- package/dist/bin/pushary-doctor.js +21 -15
- package/dist/bin/pushary-elicitation-hook.d.ts +1 -0
- package/dist/bin/pushary-elicitation-hook.js +210 -0
- package/dist/bin/pushary-gemini-bridge.js +5 -5
- package/dist/bin/pushary-gemini-hook.js +10 -8
- package/dist/bin/pushary-hook.js +6 -5
- package/dist/bin/pushary-login.js +4 -4
- package/dist/bin/pushary-logout.js +4 -4
- package/dist/bin/pushary-mode.js +3 -3
- package/dist/bin/pushary-notification-hook.js +4 -3
- package/dist/bin/pushary-permission-denied-hook.js +6 -5
- package/dist/bin/pushary-permission-hook.js +6 -5
- package/dist/bin/pushary-post-hook.js +4 -3
- package/dist/bin/pushary-prompt-hook.js +4 -3
- package/dist/bin/pushary-session-end-hook.js +4 -3
- package/dist/bin/pushary-session-start-hook.js +4 -3
- package/dist/bin/pushary-setup.js +19 -15
- package/dist/bin/pushary-stats.js +2 -2
- package/dist/bin/pushary-status.js +5 -5
- package/dist/bin/pushary-stop-hook.js +4 -3
- package/dist/bin/pushary-stopfailure-hook.js +4 -3
- package/dist/bin/pushary-upgrade.js +8 -7
- package/dist/bin/pushary-wait.js +3 -3
- package/dist/{chunk-5EUZYWID.js → chunk-2T3GX7WG.js} +1 -1
- package/dist/{chunk-GK4CED6Z.js → chunk-5646VKR4.js} +7 -3
- package/dist/{chunk-MNQI6ZZU.js → chunk-7MBDFNUC.js} +1 -1
- package/dist/{chunk-CY4TZK5O.js → chunk-7YJS2VTG.js} +31 -4
- package/dist/{chunk-PZR4XMFU.js → chunk-A3PFIWWN.js} +1 -1
- package/dist/{chunk-2TZCZ7CL.js → chunk-A4I5JGCW.js} +1 -1
- package/dist/{chunk-VPJAAGD4.js → chunk-B3V2H6SA.js} +37 -17
- package/dist/{chunk-XPAGN3SC.js → chunk-ECEUMWBH.js} +1 -1
- package/dist/{chunk-2255DZ74.js → chunk-HAUTLCMA.js} +1 -1
- package/dist/{chunk-S3UD7O35.js → chunk-HJ44HIUW.js} +12 -28
- package/dist/{chunk-QW27ZFA4.js → chunk-IWL5LWIE.js} +1 -1
- package/dist/chunk-J3YDT4HM.js +128 -0
- package/dist/{chunk-N52CIK47.js → chunk-JAICP7BA.js} +1 -1
- package/dist/{chunk-HSGEEBBL.js → chunk-M7RYIZX6.js} +3 -3
- package/dist/{chunk-7G5VO76N.js → chunk-OT4YD54Q.js} +1 -1
- package/dist/{chunk-VZNDR3Z7.js → chunk-QBWYNVKY.js} +1 -1
- package/dist/{chunk-MTKR4YLW.js → chunk-SJVLFIYP.js} +1 -1
- package/dist/{chunk-33T5OOKP.js → chunk-V2XPF77V.js} +96 -161
- package/dist/{chunk-FOY5FISQ.js → chunk-WLLBFDVY.js} +1 -1
- package/dist/{chunk-Q7N3XGWM.js → chunk-XW5CHAQX.js} +2 -2
- package/dist/{chunk-7C5CJC3H.js → chunk-Y3UTTFTZ.js} +1 -1
- package/dist/{chunk-2C6B6V2U.js → chunk-ZZE6FN7T.js} +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +10 -8
- package/package.json +3 -1
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import {
|
|
2
|
+
callMcpTool
|
|
3
|
+
} from "./chunk-BSZYIAZL.js";
|
|
4
|
+
import {
|
|
5
|
+
installProxyDispatcher
|
|
6
|
+
} from "./chunk-SAF6HGAA.js";
|
|
7
|
+
|
|
8
|
+
// src/api.ts
|
|
9
|
+
import { randomUUID } from "crypto";
|
|
10
|
+
|
|
11
|
+
// src/validate.ts
|
|
12
|
+
var isPolicyConfig = (data) => {
|
|
13
|
+
if (!data || typeof data !== "object") return false;
|
|
14
|
+
const d = data;
|
|
15
|
+
return Array.isArray(d.policies) && typeof d.defaultTimeoutSeconds === "number" && typeof d.defaultTimeoutAction === "string";
|
|
16
|
+
};
|
|
17
|
+
var isAskUserResponse = (data) => {
|
|
18
|
+
if (!data || typeof data !== "object") return false;
|
|
19
|
+
const d = data;
|
|
20
|
+
return typeof d.correlationId === "string" && typeof d.status === "string";
|
|
21
|
+
};
|
|
22
|
+
var isWaitForAnswerResponse = (data) => {
|
|
23
|
+
if (!data || typeof data !== "object") return false;
|
|
24
|
+
const d = data;
|
|
25
|
+
return typeof d.answered === "boolean";
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// src/api.ts
|
|
29
|
+
installProxyDispatcher();
|
|
30
|
+
var askUser = async (apiKey, params, timeoutMs = 15e3) => {
|
|
31
|
+
const identified = params.toolUseId || params.requestId ? params : { ...params, requestId: randomUUID() };
|
|
32
|
+
const result = await callMcpTool(apiKey, "ask_user", { ...identified, wait: false }, { maxRetries: 3, timeoutMs });
|
|
33
|
+
if (!isAskUserResponse(result)) throw new Error("Invalid ask_user response");
|
|
34
|
+
return result;
|
|
35
|
+
};
|
|
36
|
+
var waitForAnswer = async (apiKey, correlationId, timeoutMs = 3e4) => {
|
|
37
|
+
const result = await callMcpTool(apiKey, "wait_for_answer", {
|
|
38
|
+
correlationId,
|
|
39
|
+
timeoutMs
|
|
40
|
+
}, { timeoutMs: timeoutMs + 1e4 });
|
|
41
|
+
if (!isWaitForAnswerResponse(result)) throw new Error("Invalid wait_for_answer response");
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
var pollForAnswer = async (apiKey, correlationId, deadlineMs, pollInterval = 2e3) => {
|
|
45
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
46
|
+
while (Date.now() < deadlineMs) {
|
|
47
|
+
const remaining = Math.min(Math.max(deadlineMs - Date.now(), 1e3), 3e4);
|
|
48
|
+
let answer;
|
|
49
|
+
try {
|
|
50
|
+
answer = await waitForAnswer(apiKey, correlationId, remaining);
|
|
51
|
+
} catch {
|
|
52
|
+
if (Date.now() + pollInterval >= deadlineMs) break;
|
|
53
|
+
await sleep(pollInterval);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (answer.answered) return answer;
|
|
57
|
+
if (Date.now() + pollInterval >= deadlineMs) break;
|
|
58
|
+
await sleep(pollInterval);
|
|
59
|
+
}
|
|
60
|
+
return { answered: false };
|
|
61
|
+
};
|
|
62
|
+
var cancelQuestion = async (apiKey, correlationId) => {
|
|
63
|
+
await callMcpTool(apiKey, "cancel_question", { correlationId });
|
|
64
|
+
};
|
|
65
|
+
var sendNotification = async (apiKey, params, timeoutMs = 15e3) => {
|
|
66
|
+
await callMcpTool(apiKey, "send_notification", { ...params }, { maxRetries: 3, timeoutMs });
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// src/pending.ts
|
|
70
|
+
import { join } from "path";
|
|
71
|
+
import { tmpdir } from "os";
|
|
72
|
+
import { existsSync, mkdirSync, writeFileSync, readdirSync, unlinkSync, rmSync, statSync } from "fs";
|
|
73
|
+
var PENDING_DIR = join(tmpdir(), "pushary-pending");
|
|
74
|
+
var DEFAULT_SESSION = "_no_session";
|
|
75
|
+
var GRACE_MS = 10 * 60 * 1e3;
|
|
76
|
+
var sanitize = (sessionId) => sessionId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 128) || DEFAULT_SESSION;
|
|
77
|
+
var dirFor = (sessionId) => join(PENDING_DIR, sanitize(sessionId));
|
|
78
|
+
var isDefaultSession = (sessionId) => sanitize(sessionId) === DEFAULT_SESSION;
|
|
79
|
+
var savePendingQuestion = (sessionId, correlationId) => {
|
|
80
|
+
const dir = dirFor(sessionId);
|
|
81
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
82
|
+
writeFileSync(join(dir, correlationId), "", "utf-8");
|
|
83
|
+
};
|
|
84
|
+
var listPendingQuestions = (sessionId) => {
|
|
85
|
+
const dir = dirFor(sessionId);
|
|
86
|
+
let files;
|
|
87
|
+
try {
|
|
88
|
+
files = readdirSync(dir);
|
|
89
|
+
} catch {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
if (!isDefaultSession(sessionId)) return files;
|
|
93
|
+
const cutoff = Date.now() - GRACE_MS;
|
|
94
|
+
return files.filter((name) => {
|
|
95
|
+
try {
|
|
96
|
+
return statSync(join(dir, name)).mtimeMs < cutoff;
|
|
97
|
+
} catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
var removePendingQuestion = (sessionId, correlationId) => {
|
|
103
|
+
try {
|
|
104
|
+
unlinkSync(join(dirFor(sessionId), correlationId));
|
|
105
|
+
} catch {
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
var removePendingSession = (sessionId) => {
|
|
109
|
+
try {
|
|
110
|
+
rmSync(dirFor(sessionId), { recursive: true, force: true });
|
|
111
|
+
} catch {
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export {
|
|
116
|
+
isPolicyConfig,
|
|
117
|
+
askUser,
|
|
118
|
+
waitForAnswer,
|
|
119
|
+
pollForAnswer,
|
|
120
|
+
cancelQuestion,
|
|
121
|
+
sendNotification,
|
|
122
|
+
DEFAULT_SESSION,
|
|
123
|
+
isDefaultSession,
|
|
124
|
+
savePendingQuestion,
|
|
125
|
+
listPendingQuestions,
|
|
126
|
+
removePendingQuestion,
|
|
127
|
+
removePendingSession
|
|
128
|
+
};
|
|
@@ -7,9 +7,9 @@ var HOOK_SHELL = "/bin/sh -c";
|
|
|
7
7
|
var LOCATE = 'B=$(command -v "$0") || exit 0; [ -f "$B" ] || exit 0; [ -x "$B" ] || exit 0';
|
|
8
8
|
var NODE_PRESENT = "command -v node >/dev/null 2>&1 || exit 0";
|
|
9
9
|
var HOOK_GUARD_SCRIPTS = {
|
|
10
|
-
native_binary: `${LOCATE}; exec "$B"`,
|
|
11
|
-
node_binary: `${LOCATE}; ${NODE_PRESENT}; exec "$B"`,
|
|
12
|
-
node_script: `[ -f "$0" ] || exit 0; ${NODE_PRESENT}; exec node "$0"`
|
|
10
|
+
native_binary: `${LOCATE}; exec "$B" "$@"`,
|
|
11
|
+
node_binary: `${LOCATE}; ${NODE_PRESENT}; exec "$B" "$@"`,
|
|
12
|
+
node_script: `[ -f "$0" ] || exit 0; ${NODE_PRESENT}; exec node "$0" "$@"`
|
|
13
13
|
};
|
|
14
14
|
var BINARY_GUARDS = {
|
|
15
15
|
native: "native_binary",
|
|
@@ -1,22 +1,30 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_SESSION,
|
|
3
|
+
cancelQuestion,
|
|
4
|
+
isDefaultSession,
|
|
5
|
+
isPolicyConfig,
|
|
6
|
+
listPendingQuestions,
|
|
7
|
+
removePendingQuestion,
|
|
8
|
+
removePendingSession,
|
|
9
|
+
sendNotification,
|
|
10
|
+
waitForAnswer
|
|
11
|
+
} from "./chunk-J3YDT4HM.js";
|
|
1
12
|
import {
|
|
2
13
|
getMachineId
|
|
3
14
|
} from "./chunk-RN3NOEJF.js";
|
|
4
15
|
import {
|
|
5
|
-
callMcpTool,
|
|
6
16
|
withRetry
|
|
7
17
|
} from "./chunk-BSZYIAZL.js";
|
|
8
|
-
import {
|
|
9
|
-
installProxyDispatcher
|
|
10
|
-
} from "./chunk-SAF6HGAA.js";
|
|
11
18
|
import {
|
|
12
19
|
reportedInstallSource
|
|
13
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-Y3UTTFTZ.js";
|
|
14
21
|
import {
|
|
15
22
|
getApiKey,
|
|
16
23
|
getBaseUrl
|
|
17
24
|
} from "./chunk-2UMNXADU.js";
|
|
18
25
|
import {
|
|
19
26
|
ACTION_BODY_MAX,
|
|
27
|
+
AGENT_LABELS_BY_TYPE,
|
|
20
28
|
DECISION_LINE_MAX,
|
|
21
29
|
FILE_TARGET_TOOLS,
|
|
22
30
|
gateOutputFor,
|
|
@@ -28,32 +36,13 @@ import {
|
|
|
28
36
|
redactSecretsDeep,
|
|
29
37
|
resolveAutoResolveOrigin,
|
|
30
38
|
resolvePolicy
|
|
31
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-7YJS2VTG.js";
|
|
32
40
|
|
|
33
41
|
// src/policy.ts
|
|
34
42
|
import { createHash } from "crypto";
|
|
35
43
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
36
44
|
import { join } from "path";
|
|
37
45
|
import { tmpdir } from "os";
|
|
38
|
-
|
|
39
|
-
// src/validate.ts
|
|
40
|
-
var isPolicyConfig = (data) => {
|
|
41
|
-
if (!data || typeof data !== "object") return false;
|
|
42
|
-
const d = data;
|
|
43
|
-
return Array.isArray(d.policies) && typeof d.defaultTimeoutSeconds === "number" && typeof d.defaultTimeoutAction === "string";
|
|
44
|
-
};
|
|
45
|
-
var isAskUserResponse = (data) => {
|
|
46
|
-
if (!data || typeof data !== "object") return false;
|
|
47
|
-
const d = data;
|
|
48
|
-
return typeof d.correlationId === "string" && typeof d.status === "string";
|
|
49
|
-
};
|
|
50
|
-
var isWaitForAnswerResponse = (data) => {
|
|
51
|
-
if (!data || typeof data !== "object") return false;
|
|
52
|
-
const d = data;
|
|
53
|
-
return typeof d.answered === "boolean";
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
// src/policy.ts
|
|
57
46
|
var CACHE_TTL_MS = 60 * 1e3;
|
|
58
47
|
var STALE_CACHE_MAX_MS = 12 * 60 * 60 * 1e3;
|
|
59
48
|
var policyRequestFor = (agentType) => ({
|
|
@@ -189,32 +178,8 @@ var fetchModeState = async (apiKey, sessionId, reuseWithinMs = 0) => {
|
|
|
189
178
|
};
|
|
190
179
|
var fetchModeOverride = async (apiKey) => (await fetchModeState(apiKey)).mode;
|
|
191
180
|
|
|
192
|
-
// src/api.ts
|
|
193
|
-
import { randomUUID } from "crypto";
|
|
194
|
-
installProxyDispatcher();
|
|
195
|
-
var askUser = async (apiKey, params, timeoutMs = 15e3) => {
|
|
196
|
-
const identified = params.toolUseId || params.requestId ? params : { ...params, requestId: randomUUID() };
|
|
197
|
-
const result = await callMcpTool(apiKey, "ask_user", { ...identified, wait: false }, { maxRetries: 3, timeoutMs });
|
|
198
|
-
if (!isAskUserResponse(result)) throw new Error("Invalid ask_user response");
|
|
199
|
-
return result;
|
|
200
|
-
};
|
|
201
|
-
var waitForAnswer = async (apiKey, correlationId, timeoutMs = 3e4) => {
|
|
202
|
-
const result = await callMcpTool(apiKey, "wait_for_answer", {
|
|
203
|
-
correlationId,
|
|
204
|
-
timeoutMs
|
|
205
|
-
}, { timeoutMs: timeoutMs + 1e4 });
|
|
206
|
-
if (!isWaitForAnswerResponse(result)) throw new Error("Invalid wait_for_answer response");
|
|
207
|
-
return result;
|
|
208
|
-
};
|
|
209
|
-
var cancelQuestion = async (apiKey, correlationId) => {
|
|
210
|
-
await callMcpTool(apiKey, "cancel_question", { correlationId });
|
|
211
|
-
};
|
|
212
|
-
var sendNotification = async (apiKey, params, timeoutMs = 15e3) => {
|
|
213
|
-
await callMcpTool(apiKey, "send_notification", { ...params }, { maxRetries: 3, timeoutMs });
|
|
214
|
-
};
|
|
215
|
-
|
|
216
181
|
// ../hook-mapping/src/describe.ts
|
|
217
|
-
import { isAbsolute, relative } from "path";
|
|
182
|
+
import { isAbsolute, relative, resolve } from "path";
|
|
218
183
|
var hookPrefixes = {
|
|
219
184
|
Bash: (input) => `bash: ${input.command ?? "(no command)"}`,
|
|
220
185
|
PowerShell: (input) => `powershell: ${input.command ?? "(no command)"}`,
|
|
@@ -292,6 +257,21 @@ var deriveReceiptCommandHead = (command) => {
|
|
|
292
257
|
return head ? redactSecrets(head).slice(0, TOOL_TARGET_MAX_LENGTH) : void 0;
|
|
293
258
|
};
|
|
294
259
|
var RECEIPT_TARGET_MAX_LENGTH = 256;
|
|
260
|
+
var parseApplyPatchOps = (command) => {
|
|
261
|
+
if (typeof command !== "string") return [];
|
|
262
|
+
const ops = [];
|
|
263
|
+
for (const line of command.split("\n")) {
|
|
264
|
+
const file = line.match(/^\*\*\*\s+(Add|Update|Delete) File:\s+(.+?)\s*$/);
|
|
265
|
+
if (file) {
|
|
266
|
+
ops.push({ op: file[1].toLowerCase(), path: file[2] });
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
const move = line.match(/^\*\*\*\s+Move to:\s+(.+?)\s*$/);
|
|
270
|
+
if (move) ops.push({ op: "move", path: move[1] });
|
|
271
|
+
}
|
|
272
|
+
return ops;
|
|
273
|
+
};
|
|
274
|
+
var parseApplyPatchFiles = (command) => [...new Set(parseApplyPatchOps(command).map((entry) => entry.path))];
|
|
295
275
|
var EXIT_CODE_FAILURE = /^Error: Exit code \d+/;
|
|
296
276
|
var isToolResultError = (toolResult) => {
|
|
297
277
|
try {
|
|
@@ -319,19 +299,34 @@ var relativizeReceiptPath = (filePath, cwd) => {
|
|
|
319
299
|
var deriveReceiptMeta = (toolName, toolInput, toolResult, cwd) => {
|
|
320
300
|
try {
|
|
321
301
|
const ok = !isToolResultError(toolResult);
|
|
322
|
-
if (
|
|
323
|
-
const
|
|
302
|
+
if (toolName === "apply_patch") {
|
|
303
|
+
const files = parseApplyPatchFiles(toolInput.command);
|
|
304
|
+
const target = files.length === 1 ? files[0] : toolInput.command;
|
|
305
|
+
if (typeof target !== "string" || !target) return void 0;
|
|
306
|
+
return { kind: "edit", target: redactSecrets(target).slice(0, RECEIPT_TARGET_MAX_LENGTH), ok };
|
|
307
|
+
}
|
|
308
|
+
const canonicalTool = {
|
|
309
|
+
run_shell_command: "Bash",
|
|
310
|
+
write_file: "Write",
|
|
311
|
+
replace: "Edit"
|
|
312
|
+
}[toolName] ?? toolName;
|
|
313
|
+
const canonicalInput = canonicalTool === toolName ? toolInput : {
|
|
314
|
+
...toolInput,
|
|
315
|
+
file_path: toolInput.file_path ?? toolInput.absolute_path ?? toolInput.path
|
|
316
|
+
};
|
|
317
|
+
if (FILE_TARGET_TOOLS.has(canonicalTool)) {
|
|
318
|
+
const filePath = canonicalInput.file_path;
|
|
324
319
|
if (typeof filePath !== "string" || !filePath) return void 0;
|
|
325
320
|
return {
|
|
326
|
-
kind:
|
|
321
|
+
kind: canonicalTool === "Write" ? "write" : "edit",
|
|
327
322
|
// A path is an unlikely place for a credential but not an impossible one
|
|
328
323
|
// (a token in a temp directory name), and this field is persisted.
|
|
329
324
|
target: redactSecrets(relativizeReceiptPath(filePath, cwd)).slice(0, RECEIPT_TARGET_MAX_LENGTH),
|
|
330
325
|
ok
|
|
331
326
|
};
|
|
332
327
|
}
|
|
333
|
-
if (
|
|
334
|
-
const head = deriveReceiptCommandHead(
|
|
328
|
+
if (canonicalTool === "Bash" || canonicalTool === "PowerShell" || canonicalTool === "Monitor") {
|
|
329
|
+
const head = deriveReceiptCommandHead(canonicalInput.command);
|
|
335
330
|
if (!head) return void 0;
|
|
336
331
|
return {
|
|
337
332
|
kind: head === "git commit" ? "commit" : "bash",
|
|
@@ -389,6 +384,13 @@ var deriveBlocker = (mode, reason) => {
|
|
|
389
384
|
|
|
390
385
|
// ../hook-mapping/src/map.ts
|
|
391
386
|
import { basename } from "path";
|
|
387
|
+
var AGENT_IDENTITIES = {
|
|
388
|
+
claude: { type: "claude_code", label: AGENT_LABELS_BY_TYPE.claude_code },
|
|
389
|
+
codex: { type: "codex", label: AGENT_LABELS_BY_TYPE.codex },
|
|
390
|
+
gemini: { type: "gemini_cli", label: AGENT_LABELS_BY_TYPE.gemini_cli },
|
|
391
|
+
cursor: { type: "cursor", label: AGENT_LABELS_BY_TYPE.cursor },
|
|
392
|
+
vscode: { type: "vscode", label: AGENT_LABELS_BY_TYPE.vscode }
|
|
393
|
+
};
|
|
392
394
|
var TASK_TITLE_MAX_LENGTH = 120;
|
|
393
395
|
var STOP_SUMMARY_MAX_LENGTH = 200;
|
|
394
396
|
var SESSION_END_ACTIONS = {
|
|
@@ -417,68 +419,22 @@ var sessionStartAction = (source) => source === "resume" ? "Session resumed" : "
|
|
|
417
419
|
var sessionEndAction = (reason) => typeof reason === "string" && SESSION_END_ACTIONS[reason] || "Session ended";
|
|
418
420
|
var taskTitleFrom = (prompt) => typeof prompt === "string" ? prompt.replace(/\s+/g, " ").trim().slice(0, TASK_TITLE_MAX_LENGTH) || void 0 : void 0;
|
|
419
421
|
|
|
420
|
-
// src/pending.ts
|
|
421
|
-
import { join as join2 } from "path";
|
|
422
|
-
import { tmpdir as tmpdir2 } from "os";
|
|
423
|
-
import { existsSync as existsSync2, mkdirSync, writeFileSync as writeFileSync2, readdirSync, unlinkSync, rmSync, statSync } from "fs";
|
|
424
|
-
var PENDING_DIR = join2(tmpdir2(), "pushary-pending");
|
|
425
|
-
var DEFAULT_SESSION = "_no_session";
|
|
426
|
-
var GRACE_MS = 10 * 60 * 1e3;
|
|
427
|
-
var sanitize = (sessionId) => sessionId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 128) || DEFAULT_SESSION;
|
|
428
|
-
var dirFor = (sessionId) => join2(PENDING_DIR, sanitize(sessionId));
|
|
429
|
-
var isDefaultSession = (sessionId) => sanitize(sessionId) === DEFAULT_SESSION;
|
|
430
|
-
var savePendingQuestion = (sessionId, correlationId) => {
|
|
431
|
-
const dir = dirFor(sessionId);
|
|
432
|
-
if (!existsSync2(dir)) mkdirSync(dir, { recursive: true });
|
|
433
|
-
writeFileSync2(join2(dir, correlationId), "", "utf-8");
|
|
434
|
-
};
|
|
435
|
-
var listPendingQuestions = (sessionId) => {
|
|
436
|
-
const dir = dirFor(sessionId);
|
|
437
|
-
let files;
|
|
438
|
-
try {
|
|
439
|
-
files = readdirSync(dir);
|
|
440
|
-
} catch {
|
|
441
|
-
return [];
|
|
442
|
-
}
|
|
443
|
-
if (!isDefaultSession(sessionId)) return files;
|
|
444
|
-
const cutoff = Date.now() - GRACE_MS;
|
|
445
|
-
return files.filter((name) => {
|
|
446
|
-
try {
|
|
447
|
-
return statSync(join2(dir, name)).mtimeMs < cutoff;
|
|
448
|
-
} catch {
|
|
449
|
-
return false;
|
|
450
|
-
}
|
|
451
|
-
});
|
|
452
|
-
};
|
|
453
|
-
var removePendingQuestion = (sessionId, correlationId) => {
|
|
454
|
-
try {
|
|
455
|
-
unlinkSync(join2(dirFor(sessionId), correlationId));
|
|
456
|
-
} catch {
|
|
457
|
-
}
|
|
458
|
-
};
|
|
459
|
-
var removePendingSession = (sessionId) => {
|
|
460
|
-
try {
|
|
461
|
-
rmSync(dirFor(sessionId), { recursive: true, force: true });
|
|
462
|
-
} catch {
|
|
463
|
-
}
|
|
464
|
-
};
|
|
465
|
-
|
|
466
422
|
// src/repo.ts
|
|
467
|
-
import { existsSync as
|
|
468
|
-
import { basename as basename2, dirname, isAbsolute as isAbsolute2, join as
|
|
423
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, statSync } from "fs";
|
|
424
|
+
import { basename as basename2, dirname, isAbsolute as isAbsolute2, join as join2, resolve as resolve2 } from "path";
|
|
469
425
|
var MAX_PARENT_WALK = 64;
|
|
470
426
|
var GIT_FILE_PREFIX = "gitdir:";
|
|
471
427
|
var findGitDir = (startDir) => {
|
|
472
|
-
let current =
|
|
428
|
+
let current = resolve2(startDir);
|
|
473
429
|
for (let depth = 0; depth < MAX_PARENT_WALK; depth += 1) {
|
|
474
|
-
const candidate =
|
|
430
|
+
const candidate = join2(current, ".git");
|
|
475
431
|
try {
|
|
476
|
-
if (
|
|
477
|
-
if (
|
|
432
|
+
if (existsSync2(candidate)) {
|
|
433
|
+
if (statSync(candidate).isDirectory()) return candidate;
|
|
478
434
|
const pointer = readFileSync2(candidate, "utf-8").trim();
|
|
479
435
|
if (pointer.startsWith(GIT_FILE_PREFIX)) {
|
|
480
436
|
const target = pointer.slice(GIT_FILE_PREFIX.length).trim();
|
|
481
|
-
return isAbsolute2(target) ? target :
|
|
437
|
+
return isAbsolute2(target) ? target : resolve2(current, target);
|
|
482
438
|
}
|
|
483
439
|
}
|
|
484
440
|
} catch {
|
|
@@ -491,7 +447,7 @@ var findGitDir = (startDir) => {
|
|
|
491
447
|
};
|
|
492
448
|
var configPathFor = (gitDir) => {
|
|
493
449
|
const index = gitDir.replace(/\\/g, "/").indexOf("/worktrees/");
|
|
494
|
-
return index === -1 ?
|
|
450
|
+
return index === -1 ? join2(gitDir, "config") : join2(gitDir.slice(0, index), "config");
|
|
495
451
|
};
|
|
496
452
|
var ORIGIN_SECTION = /^\[remote "origin"\]/;
|
|
497
453
|
var SECTION_HEADER = /^\[/;
|
|
@@ -499,7 +455,7 @@ var URL_LINE = /^url\s*=\s*(.+)$/;
|
|
|
499
455
|
var readOriginRemote = (gitDir) => {
|
|
500
456
|
try {
|
|
501
457
|
const configPath = configPathFor(gitDir);
|
|
502
|
-
if (!
|
|
458
|
+
if (!existsSync2(configPath)) return void 0;
|
|
503
459
|
let inOrigin = false;
|
|
504
460
|
for (const rawLine of readFileSync2(configPath, "utf-8").split("\n")) {
|
|
505
461
|
const line = rawLine.trim();
|
|
@@ -526,7 +482,7 @@ var deriveRepoKey = (cwd) => {
|
|
|
526
482
|
const root = gitDir.endsWith(".git") ? dirname(gitDir) : dir;
|
|
527
483
|
return localRepoKey(basename2(root));
|
|
528
484
|
}
|
|
529
|
-
return localRepoKey(basename2(
|
|
485
|
+
return localRepoKey(basename2(resolve2(dir)));
|
|
530
486
|
} catch {
|
|
531
487
|
return void 0;
|
|
532
488
|
}
|
|
@@ -548,9 +504,9 @@ var repoKeyFor = (cwd) => {
|
|
|
548
504
|
};
|
|
549
505
|
|
|
550
506
|
// src/usage.ts
|
|
551
|
-
import { closeSync, mkdirSync
|
|
552
|
-
import { join as
|
|
553
|
-
import { tmpdir as
|
|
507
|
+
import { closeSync, mkdirSync, openSync, readFileSync as readFileSync3, readSync, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
508
|
+
import { join as join3 } from "path";
|
|
509
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
554
510
|
var DEFAULT_PRICES = [
|
|
555
511
|
{ match: "opus-4-1", in: 15, out: 75 },
|
|
556
512
|
{ match: "opus-4-0", in: 15, out: 75 },
|
|
@@ -585,8 +541,8 @@ var estimateCostUsd = (usage, model) => {
|
|
|
585
541
|
const perTokenOut = price.out / 1e6;
|
|
586
542
|
return usage.inputTokens * perTokenIn + usage.outputTokens * perTokenOut + usage.cacheCreationTokens * perTokenIn * CACHE_WRITE_MULTIPLIER + usage.cacheReadTokens * perTokenIn * CACHE_READ_MULTIPLIER;
|
|
587
543
|
};
|
|
588
|
-
var stateDir = () => process.env.PUSHARY_USAGE_DIR?.trim() ||
|
|
589
|
-
var stateFile = (sessionId) =>
|
|
544
|
+
var stateDir = () => process.env.PUSHARY_USAGE_DIR?.trim() || join3(tmpdir2(), "pushary-usage");
|
|
545
|
+
var stateFile = (sessionId) => join3(stateDir(), sessionId.replace(/[^a-zA-Z0-9_-]/g, "_"));
|
|
590
546
|
var emptyState = () => ({ offset: 0, tokensIn: 0, tokensOut: 0, costUsd: 0, recentIds: [] });
|
|
591
547
|
var readState = (path) => {
|
|
592
548
|
try {
|
|
@@ -665,7 +621,7 @@ var extractUserText = (content) => {
|
|
|
665
621
|
};
|
|
666
622
|
var readLastUserPrompt = (transcriptPath) => {
|
|
667
623
|
try {
|
|
668
|
-
const size =
|
|
624
|
+
const size = statSync2(transcriptPath).size;
|
|
669
625
|
const start = Math.max(0, size - USER_PROMPT_TAIL_BYTES);
|
|
670
626
|
const lines = readRange(transcriptPath, start, size).toString("utf-8").split("\n");
|
|
671
627
|
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
@@ -688,7 +644,7 @@ var readLastUserPrompt = (transcriptPath) => {
|
|
|
688
644
|
};
|
|
689
645
|
var readNewUsage = (transcriptPath, sessionId) => {
|
|
690
646
|
try {
|
|
691
|
-
const size =
|
|
647
|
+
const size = statSync2(transcriptPath).size;
|
|
692
648
|
const path = stateFile(sessionId);
|
|
693
649
|
let state = readState(path);
|
|
694
650
|
if (size < state.offset) state = { ...emptyState(), recentIds: state.recentIds };
|
|
@@ -704,8 +660,8 @@ var readNewUsage = (transcriptPath, sessionId) => {
|
|
|
704
660
|
state.offset += lastNewline + 1;
|
|
705
661
|
}
|
|
706
662
|
}
|
|
707
|
-
|
|
708
|
-
|
|
663
|
+
mkdirSync(stateDir(), { recursive: true });
|
|
664
|
+
writeFileSync2(path, JSON.stringify(state), "utf-8");
|
|
709
665
|
if (state.tokensIn === 0 && state.tokensOut === 0) return null;
|
|
710
666
|
return {
|
|
711
667
|
tokensIn: state.tokensIn,
|
|
@@ -719,7 +675,7 @@ var readNewUsage = (transcriptPath, sessionId) => {
|
|
|
719
675
|
};
|
|
720
676
|
|
|
721
677
|
// src/codex-adapter.ts
|
|
722
|
-
import { resolve as
|
|
678
|
+
import { resolve as resolve3 } from "path";
|
|
723
679
|
var CODEX_AGENT = { type: "codex", label: "Codex" };
|
|
724
680
|
var codexAllow = () => ({ kind: "allow" });
|
|
725
681
|
var codexDeny = (reason) => ({ kind: "deny", reason });
|
|
@@ -733,21 +689,6 @@ var toCodexWire = (event, decision) => {
|
|
|
733
689
|
decision.kind === "deny" ? decision.reason : void 0
|
|
734
690
|
) ?? null;
|
|
735
691
|
};
|
|
736
|
-
var parseApplyPatchOps = (command) => {
|
|
737
|
-
if (typeof command !== "string") return [];
|
|
738
|
-
const ops = [];
|
|
739
|
-
for (const line of command.split("\n")) {
|
|
740
|
-
const file = line.match(/^\*\*\*\s+(Add|Update|Delete) File:\s+(.+?)\s*$/);
|
|
741
|
-
if (file) {
|
|
742
|
-
ops.push({ op: file[1].toLowerCase(), path: file[2] });
|
|
743
|
-
continue;
|
|
744
|
-
}
|
|
745
|
-
const move = line.match(/^\*\*\*\s+Move to:\s+(.+?)\s*$/);
|
|
746
|
-
if (move) ops.push({ op: "move", path: move[1] });
|
|
747
|
-
}
|
|
748
|
-
return ops;
|
|
749
|
-
};
|
|
750
|
-
var parseApplyPatchFiles = (command) => [...new Set(parseApplyPatchOps(command).map((entry) => entry.path))];
|
|
751
692
|
var describeApplyPatch = (command) => {
|
|
752
693
|
const ops = parseApplyPatchOps(command);
|
|
753
694
|
if (ops.length === 0) return null;
|
|
@@ -763,7 +704,7 @@ var toPolicyLookup = (toolName, toolInput, cwd) => {
|
|
|
763
704
|
const command = toolInput.command;
|
|
764
705
|
const files = parseApplyPatchFiles(command);
|
|
765
706
|
if (files.length === 1) {
|
|
766
|
-
return { tool: "Edit", input: { file_path: cwd ?
|
|
707
|
+
return { tool: "Edit", input: { file_path: cwd ? resolve3(cwd, files[0]) : files[0] } };
|
|
767
708
|
}
|
|
768
709
|
return { tool: "Edit", input: typeof command === "string" ? { file_path: command } : {} };
|
|
769
710
|
};
|
|
@@ -779,7 +720,7 @@ var toPolicyLookups = (toolName, toolInput, cwd) => {
|
|
|
779
720
|
}
|
|
780
721
|
return {
|
|
781
722
|
tool: "Edit",
|
|
782
|
-
inputs: files.map((file) => ({ file_path: cwd ?
|
|
723
|
+
inputs: files.map((file) => ({ file_path: cwd ? resolve3(cwd, file) : file }))
|
|
783
724
|
};
|
|
784
725
|
};
|
|
785
726
|
var permissionTimeoutDecision = (timeoutAction) => {
|
|
@@ -790,18 +731,18 @@ var permissionTimeoutDecision = (timeoutAction) => {
|
|
|
790
731
|
var preToolUseTimeoutDecision = (timeoutAction, denyReason = "No response within timeout") => timeoutAction === "deny" ? codexDeny(denyReason) : codexPass();
|
|
791
732
|
|
|
792
733
|
// src/throttle.ts
|
|
793
|
-
import { join as
|
|
794
|
-
import { tmpdir as
|
|
734
|
+
import { join as join4 } from "path";
|
|
735
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
795
736
|
import { createHash as createHash2 } from "crypto";
|
|
796
|
-
import { existsSync as
|
|
797
|
-
var THROTTLE_DIR =
|
|
798
|
-
var markerPath = (key) =>
|
|
737
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, statSync as statSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
738
|
+
var THROTTLE_DIR = join4(tmpdir3(), "pushary-throttle");
|
|
739
|
+
var markerPath = (key) => join4(THROTTLE_DIR, createHash2("sha256").update(key).digest("hex").slice(0, 16));
|
|
799
740
|
var throttlePass = (key, windowMs) => {
|
|
800
741
|
try {
|
|
801
742
|
const path = markerPath(key);
|
|
802
|
-
if (
|
|
803
|
-
|
|
804
|
-
|
|
743
|
+
if (existsSync3(path) && Date.now() - statSync3(path).mtimeMs < windowMs) return false;
|
|
744
|
+
mkdirSync2(THROTTLE_DIR, { recursive: true });
|
|
745
|
+
writeFileSync3(path, "", "utf-8");
|
|
805
746
|
return true;
|
|
806
747
|
} catch {
|
|
807
748
|
return false;
|
|
@@ -809,27 +750,27 @@ var throttlePass = (key, windowMs) => {
|
|
|
809
750
|
};
|
|
810
751
|
|
|
811
752
|
// src/events.ts
|
|
812
|
-
import { basename as basename3, join as
|
|
813
|
-
import { existsSync as
|
|
814
|
-
import { tmpdir as
|
|
815
|
-
var INTENT_DIR =
|
|
753
|
+
import { basename as basename3, join as join5 } from "path";
|
|
754
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, statSync as statSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
755
|
+
import { tmpdir as tmpdir4 } from "os";
|
|
756
|
+
var INTENT_DIR = join5(tmpdir4(), "pushary-intent");
|
|
816
757
|
var INTENT_MAX_BYTES = 2048;
|
|
817
758
|
var INTENT_GRACE_MS = 10 * 60 * 1e3;
|
|
818
759
|
var sanitizeSession = (sessionId) => sessionId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 128) || DEFAULT_SESSION;
|
|
819
|
-
var intentFile = (sessionId) =>
|
|
760
|
+
var intentFile = (sessionId) => join5(INTENT_DIR, sanitizeSession(sessionId));
|
|
820
761
|
var saveLastPrompt = (sessionId, prompt) => {
|
|
821
762
|
try {
|
|
822
763
|
const trimmed = prompt.replace(/\s+/g, " ").trim().slice(0, INTENT_MAX_BYTES);
|
|
823
764
|
if (!trimmed) return;
|
|
824
|
-
|
|
825
|
-
|
|
765
|
+
mkdirSync3(INTENT_DIR, { recursive: true });
|
|
766
|
+
writeFileSync4(intentFile(sessionId), trimmed, "utf-8");
|
|
826
767
|
} catch {
|
|
827
768
|
}
|
|
828
769
|
};
|
|
829
770
|
var readLastPrompt = (sessionId) => {
|
|
830
771
|
try {
|
|
831
772
|
const path = intentFile(sessionId);
|
|
832
|
-
if (isDefaultSession(sessionId) &&
|
|
773
|
+
if (isDefaultSession(sessionId) && statSync4(path).mtimeMs < Date.now() - INTENT_GRACE_MS) return void 0;
|
|
833
774
|
const value = readFileSync4(path, "utf-8").trim();
|
|
834
775
|
return value.length > 0 ? value : void 0;
|
|
835
776
|
} catch {
|
|
@@ -888,7 +829,7 @@ var POLICY_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
|
888
829
|
var readFreshCachedPolicy = (apiKey, agentType, sessionId) => {
|
|
889
830
|
const request = policyRequestFor(agentType);
|
|
890
831
|
const path = policyCacheFile(apiKey, request.agent, request.repoAware);
|
|
891
|
-
if (!
|
|
832
|
+
if (!existsSync4(path)) return null;
|
|
892
833
|
const cached = JSON.parse(readFileSync4(path, "utf-8"));
|
|
893
834
|
if (!isPolicyConfig(cached)) return null;
|
|
894
835
|
if ((cached._sessionId ?? null) !== (sessionId ?? null)) return null;
|
|
@@ -1250,18 +1191,12 @@ export {
|
|
|
1250
1191
|
getPolicy,
|
|
1251
1192
|
fetchModeState,
|
|
1252
1193
|
fetchModeOverride,
|
|
1253
|
-
askUser,
|
|
1254
|
-
waitForAnswer,
|
|
1255
|
-
cancelQuestion,
|
|
1256
|
-
sendNotification,
|
|
1257
1194
|
describeToolCall,
|
|
1258
1195
|
deriveToolTarget,
|
|
1259
1196
|
scopePathFor,
|
|
1260
1197
|
deriveActionBody,
|
|
1261
1198
|
deriveAction,
|
|
1262
1199
|
deriveBlocker,
|
|
1263
|
-
DEFAULT_SESSION,
|
|
1264
|
-
savePendingQuestion,
|
|
1265
1200
|
repoKeyFor,
|
|
1266
1201
|
throttlePass,
|
|
1267
1202
|
readLastUserPrompt,
|