@pushary/agent-hooks 0.89.13 → 0.90.1
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/CHANGELOG.md +73 -0
- package/data/SKILL.md +1 -1
- package/data/cursor-plugin/skills/pushary/SKILL.md +1 -1
- package/data/vscode-plugin/skills/pushary/SKILL.md +1 -1
- package/dist/bin/pushary-bell-hook.js +4 -4
- package/dist/bin/pushary-bell.js +8 -8
- package/dist/bin/pushary-claude.js +72 -21
- package/dist/bin/pushary-clean.js +16 -42
- package/dist/bin/pushary-codex-bridge.js +37 -12
- package/dist/bin/pushary-codex-hook.js +45 -12
- package/dist/bin/pushary-codex.js +7 -7
- package/dist/bin/pushary-connect.js +11 -10
- package/dist/bin/pushary-daemon.js +187 -63
- package/dist/bin/pushary-disconnect.js +8 -8
- package/dist/bin/pushary-doctor.js +108 -25
- package/dist/bin/pushary-elicitation-hook.js +4 -4
- package/dist/bin/pushary-gemini-bridge.js +33 -11
- package/dist/bin/pushary-gemini-hook.js +21 -10
- package/dist/bin/pushary-hook.js +13 -13
- package/dist/bin/pushary-login.js +18 -7
- package/dist/bin/pushary-logout.js +6 -6
- package/dist/bin/pushary-mode.js +28 -21
- package/dist/bin/pushary-notification-hook.js +7 -7
- package/dist/bin/pushary-opencode-hook.js +8 -8
- package/dist/bin/pushary-permission-denied-hook.js +11 -11
- package/dist/bin/pushary-permission-hook.js +11 -11
- package/dist/bin/pushary-post-hook.js +14 -8
- package/dist/bin/pushary-prompt-hook.js +7 -7
- package/dist/bin/pushary-session-end-hook.js +7 -7
- package/dist/bin/pushary-session-start-hook.js +7 -7
- package/dist/bin/pushary-setup.js +168 -45
- package/dist/bin/pushary-stats.js +4 -4
- package/dist/bin/pushary-status.js +11 -10
- package/dist/bin/pushary-stop-hook.js +14 -8
- package/dist/bin/pushary-stopfailure-hook.js +7 -7
- package/dist/bin/pushary-suggestions.js +2 -2
- package/dist/bin/pushary-transcripts.js +6 -6
- package/dist/bin/pushary-upgrade.js +15 -15
- package/dist/bin/pushary-wait.js +5 -5
- package/dist/bin/pushary.js +1 -1
- package/dist/{chunk-B2HND22S.js → chunk-2LAHXHBR.js} +1 -1
- package/dist/{chunk-GMJDQDXU.js → chunk-43GMYSS4.js} +1 -0
- package/dist/{chunk-OX35BSPV.js → chunk-4LOPSGLP.js} +70 -11
- package/dist/{chunk-JTBDBKHU.js → chunk-54ELC7D7.js} +1 -1
- package/dist/{chunk-C56VHSXU.js → chunk-5NAZ7SFS.js} +1 -1
- package/dist/{chunk-4IPSMEGT.js → chunk-7FE2KABY.js} +12 -2
- package/dist/{chunk-RAEPAUNJ.js → chunk-7TPQEWUZ.js} +1 -1
- package/dist/{chunk-ZO43JTT3.js → chunk-AJYV3VGU.js} +1 -1
- package/dist/{chunk-QFJZHVVL.js → chunk-AZPIQG4G.js} +35 -1
- package/dist/{chunk-5MNTWAAJ.js → chunk-B6H2KAQC.js} +29 -22
- package/dist/{chunk-MNTZKFZI.js → chunk-BQBEH6TV.js} +5 -5
- package/dist/{chunk-WHDSJ6PD.js → chunk-BVBEURWJ.js} +1 -1
- package/dist/{chunk-ZWEKONU6.js → chunk-CELX75MW.js} +1 -1
- package/dist/{chunk-ZP3SNUP7.js → chunk-CNPAT3HL.js} +3 -2
- package/dist/{chunk-YXT55PYF.js → chunk-DYP6SMOR.js} +1 -1
- package/dist/chunk-DZDI62LY.js +145 -0
- package/dist/{chunk-VF3RTVWY.js → chunk-EZIEXHYM.js} +15 -7
- package/dist/{chunk-7JZCADBR.js → chunk-F2ICEWZL.js} +101 -19
- package/dist/{chunk-JHJHCMAF.js → chunk-F7OURW3C.js} +24 -6
- package/dist/{chunk-VASZ6TWW.js → chunk-FQL5E6NG.js} +2 -2
- package/dist/{chunk-JVLB5CAJ.js → chunk-GL3FFM7A.js} +7 -4
- package/dist/{chunk-5YEF55AX.js → chunk-H2QZRQR2.js} +1 -1
- package/dist/{chunk-S4BLNILH.js → chunk-I463AEG3.js} +1 -1
- package/dist/{chunk-ANMHIZZY.js → chunk-JW4KO5AF.js} +1 -1
- package/dist/{chunk-3YGGEDJK.js → chunk-KUZWXPUS.js} +3 -2
- package/dist/{chunk-N4QJ3CLX.js → chunk-L4E2BIJ2.js} +80 -7
- package/dist/{chunk-NBDU3R3G.js → chunk-OSALSIUF.js} +1 -1
- package/dist/{chunk-KNLKHJZR.js → chunk-PYA4FLKI.js} +32 -21
- package/dist/chunk-QCGNGEGE.js +15 -0
- package/dist/chunk-S6LYP7QA.js +127 -0
- package/dist/{chunk-M3UABYRT.js → chunk-UZNKVRLE.js} +1 -1
- package/dist/{chunk-FOGW6O2Y.js → chunk-VHWCVD42.js} +1 -1
- package/dist/{chunk-QINYXLA5.js → chunk-VNMKIJZB.js} +15 -2
- package/dist/{chunk-CBGGUEKE.js → chunk-Y6BWPERV.js} +90 -7
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +11 -11
- package/package.json +1 -1
- package/dist/chunk-MSTHT2M7.js +0 -72
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
decodePublicKey,
|
|
4
4
|
generateSessionKey,
|
|
5
5
|
wrapSessionKey
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-7FE2KABY.js";
|
|
7
7
|
import {
|
|
8
8
|
fetchTranscriptRecipients,
|
|
9
9
|
uploadTranscriptRecords
|
|
@@ -55,9 +55,10 @@ var postLiveSessionUntilAccepted = async (options, path, body) => {
|
|
|
55
55
|
}
|
|
56
56
|
throw new Error("Live session stopped before it linked");
|
|
57
57
|
};
|
|
58
|
-
var startLiveSessionHeartbeat = (options, body, intervalMs = LIVE_SESSION_HEARTBEAT_MS) => {
|
|
58
|
+
var startLiveSessionHeartbeat = (options, body, intervalMs = LIVE_SESSION_HEARTBEAT_MS, onBeat) => {
|
|
59
59
|
const timer = setInterval(() => {
|
|
60
60
|
if (!options.signal.aborted) {
|
|
61
|
+
onBeat?.();
|
|
61
62
|
void postLiveSession(options, "/api/agent/event", body).catch(() => {
|
|
62
63
|
});
|
|
63
64
|
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CLAUDE_HOOK_EVENTS,
|
|
3
|
+
GEMINI_HOOK_EVENTS,
|
|
4
|
+
hasGeminiHooks
|
|
5
|
+
} from "./chunk-GL3FFM7A.js";
|
|
6
|
+
import {
|
|
7
|
+
CODEX_HOOK_EVENTS,
|
|
8
|
+
hasCodexHooks
|
|
9
|
+
} from "./chunk-KUZWXPUS.js";
|
|
10
|
+
import {
|
|
11
|
+
CURSOR_GATE_EVENTS,
|
|
12
|
+
HOOK_LOCATOR_BINARY
|
|
13
|
+
} from "./chunk-VNMKIJZB.js";
|
|
14
|
+
|
|
15
|
+
// src/diagnostics/wiring.ts
|
|
16
|
+
var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
17
|
+
var CLAUDE_HOOK_BINARY = /pushary-[a-z-]*hook/;
|
|
18
|
+
var EXACT_HOOK_BINARY = /^pushary-[a-z-]*(hook|gate)$/;
|
|
19
|
+
var pusharyHookExecutable = (command) => {
|
|
20
|
+
const tokens = command.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
|
|
21
|
+
for (const raw of tokens.reverse()) {
|
|
22
|
+
const token = raw[0] === '"' && raw.at(-1) === '"' || raw[0] === "'" && raw.at(-1) === "'" ? raw.slice(1, -1) : raw;
|
|
23
|
+
const name = token.split(/[/\\]/).pop()?.replace(/\.(cmd|exe|mjs)$/i, "") ?? "";
|
|
24
|
+
if (name === HOOK_LOCATOR_BINARY || EXACT_HOOK_BINARY.test(name)) return token;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
};
|
|
28
|
+
var claudeWired = ({ claudeJson, settings, skillExists }) => {
|
|
29
|
+
if (record(record(claudeJson)?.mcpServers)?.pushary) return true;
|
|
30
|
+
const s = record(settings);
|
|
31
|
+
if (s) {
|
|
32
|
+
const hooks = JSON.stringify(s.hooks ?? {});
|
|
33
|
+
if (CLAUDE_HOOK_BINARY.test(hooks) || hooks.includes(HOOK_LOCATOR_BINARY)) return true;
|
|
34
|
+
const allow = record(s.permissions)?.allow;
|
|
35
|
+
if (Array.isArray(allow) && allow.some((rule) => String(rule).includes("mcp__pushary__"))) return true;
|
|
36
|
+
}
|
|
37
|
+
return skillExists;
|
|
38
|
+
};
|
|
39
|
+
var codexWired = ({ config, hooks, skillExists }) => {
|
|
40
|
+
if (record(record(config)?.mcp_servers)?.pushary) return true;
|
|
41
|
+
const h = record(hooks);
|
|
42
|
+
if (h && hasCodexHooks(h)) return true;
|
|
43
|
+
return skillExists;
|
|
44
|
+
};
|
|
45
|
+
var geminiWired = ({ settings }) => {
|
|
46
|
+
const s = record(settings);
|
|
47
|
+
if (!s) return false;
|
|
48
|
+
if (record(s.mcpServers)?.pushary) return true;
|
|
49
|
+
return hasGeminiHooks(s);
|
|
50
|
+
};
|
|
51
|
+
var cursorWired = ({ userHooks, mcp, pluginDirExists }) => {
|
|
52
|
+
if (pluginDirExists) return true;
|
|
53
|
+
if (record(record(mcp)?.mcpServers)?.pushary) return true;
|
|
54
|
+
const hooks = record(record(userHooks)?.hooks);
|
|
55
|
+
if (!hooks) return false;
|
|
56
|
+
return Object.values(hooks).some((entries) => Array.isArray(entries) && entries.some((entry) => pusharyHookExecutable(String(record(entry)?.command ?? "")) !== null));
|
|
57
|
+
};
|
|
58
|
+
var cursorMcpAuthorization = (pluginMcp, userMcp) => {
|
|
59
|
+
const pluginServers = record(record(pluginMcp)?.mcpServers);
|
|
60
|
+
const usePlugin = pluginServers !== null && Object.hasOwn(pluginServers, "pushary");
|
|
61
|
+
const servers = usePlugin ? pluginServers : record(record(userMcp)?.mcpServers);
|
|
62
|
+
const headers = record(record(servers?.pushary)?.headers);
|
|
63
|
+
return {
|
|
64
|
+
authorization: typeof headers?.Authorization === "string" ? headers.Authorization : null,
|
|
65
|
+
source: usePlugin ? "plugin mcp.json" : "~/.cursor/mcp.json"
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
var LONG_HOLD_SECONDS = 600;
|
|
69
|
+
var toSeconds = (timeout, unit) => {
|
|
70
|
+
if (typeof timeout !== "number" || !Number.isFinite(timeout)) return null;
|
|
71
|
+
return unit === "milliseconds" ? timeout / 1e3 : timeout;
|
|
72
|
+
};
|
|
73
|
+
var registeredHooks = (entries, shape, unit) => {
|
|
74
|
+
if (!Array.isArray(entries)) return [];
|
|
75
|
+
return entries.flatMap((entry, entryIndex) => {
|
|
76
|
+
const container = record(entry);
|
|
77
|
+
if (!container) return [];
|
|
78
|
+
const hooks = shape === "nested" ? Array.isArray(container.hooks) ? container.hooks : [] : [container];
|
|
79
|
+
return hooks.flatMap((hook, hookIndex) => {
|
|
80
|
+
const command = record(hook)?.command;
|
|
81
|
+
if (typeof command !== "string" || command.trim() === "") return [];
|
|
82
|
+
return [{
|
|
83
|
+
command,
|
|
84
|
+
timeoutSeconds: toSeconds(record(hook)?.timeout, unit),
|
|
85
|
+
order: [entryIndex, hookIndex]
|
|
86
|
+
}];
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
var runsBefore = (a, b) => a[0] < b[0] || a[0] === b[0] && a[1] < b[1];
|
|
91
|
+
var holdersOnEvent = (event, hooks) => {
|
|
92
|
+
const ours = hooks.find((hook) => pusharyHookExecutable(hook.command) !== null);
|
|
93
|
+
if (!ours) return [];
|
|
94
|
+
return hooks.filter((hook) => pusharyHookExecutable(hook.command) === null).map((hook) => ({
|
|
95
|
+
event,
|
|
96
|
+
command: hook.command,
|
|
97
|
+
timeoutSeconds: hook.timeoutSeconds,
|
|
98
|
+
position: runsBefore(hook.order, ours.order) ? "before" : "after"
|
|
99
|
+
}));
|
|
100
|
+
};
|
|
101
|
+
var otherHolders = (hooks, events, shape, unit) => {
|
|
102
|
+
const table = record(hooks);
|
|
103
|
+
if (!table) return [];
|
|
104
|
+
return events.flatMap((event) => holdersOnEvent(event, registeredHooks(table[event], shape, unit)));
|
|
105
|
+
};
|
|
106
|
+
var approvalEvents = (table) => table.filter((hook) => hook.delivery === "approval").map((hook) => hook.event);
|
|
107
|
+
var CLAUDE_GATE_EVENTS = approvalEvents(CLAUDE_HOOK_EVENTS);
|
|
108
|
+
var CODEX_GATE_EVENTS = approvalEvents(CODEX_HOOK_EVENTS);
|
|
109
|
+
var GEMINI_GATE_EVENTS = approvalEvents(GEMINI_HOOK_EVENTS);
|
|
110
|
+
var claudeOtherHolders = (settings) => otherHolders(record(settings)?.hooks, CLAUDE_GATE_EVENTS, "nested", "seconds");
|
|
111
|
+
var codexOtherHolders = (hooks) => otherHolders(record(hooks)?.hooks, CODEX_GATE_EVENTS, "nested", "seconds");
|
|
112
|
+
var geminiOtherHolders = (settings) => otherHolders(record(settings)?.hooks, GEMINI_GATE_EVENTS, "nested", "milliseconds");
|
|
113
|
+
var cursorOtherHolders = (userHooks) => otherHolders(record(userHooks)?.hooks, CURSOR_GATE_EVENTS, "flat", "seconds");
|
|
114
|
+
var holdsLong = (holder) => holder.timeoutSeconds !== null && holder.timeoutSeconds > LONG_HOLD_SECONDS;
|
|
115
|
+
var SHELLS = /* @__PURE__ */ new Set(["/bin/sh", "/bin/bash", "/bin/zsh", "/usr/bin/env"]);
|
|
116
|
+
var holderExecutable = (command) => {
|
|
117
|
+
const pathLike = command.match(/[^\s"']*[/\\][^\s"']*/g) ?? [];
|
|
118
|
+
const target = pathLike.find((token) => !SHELLS.has(token));
|
|
119
|
+
return target ?? command.trim().split(/\s+/)[0] ?? command;
|
|
120
|
+
};
|
|
121
|
+
var describeHoldTimeout = (holder) => {
|
|
122
|
+
const seconds = holder.timeoutSeconds;
|
|
123
|
+
if (seconds === null) return "no timeout set, so the agent default applies";
|
|
124
|
+
if (seconds >= 3600 && seconds % 3600 === 0) return `${seconds / 3600}h timeout`;
|
|
125
|
+
if (seconds >= 60 && seconds % 60 === 0) return `${seconds / 60}m timeout`;
|
|
126
|
+
return `${seconds}s timeout`;
|
|
127
|
+
};
|
|
128
|
+
var describeOtherHolder = (holder) => `another hook on ${holder.event}: ${holderExecutable(holder.command)} (${describeHoldTimeout(holder)}, runs ${holder.position} ours)`;
|
|
129
|
+
var describeHoldSentence = (agentLabel) => `${agentLabel} waits for every hook on an event, so the terminal cannot move until that one returns, however fast your phone answered.`;
|
|
130
|
+
|
|
131
|
+
export {
|
|
132
|
+
pusharyHookExecutable,
|
|
133
|
+
claudeWired,
|
|
134
|
+
codexWired,
|
|
135
|
+
geminiWired,
|
|
136
|
+
cursorWired,
|
|
137
|
+
cursorMcpAuthorization,
|
|
138
|
+
claudeOtherHolders,
|
|
139
|
+
codexOtherHolders,
|
|
140
|
+
geminiOtherHolders,
|
|
141
|
+
cursorOtherHolders,
|
|
142
|
+
holdsLong,
|
|
143
|
+
describeOtherHolder,
|
|
144
|
+
describeHoldSentence
|
|
145
|
+
};
|
|
@@ -6,10 +6,14 @@ import {
|
|
|
6
6
|
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
|
+
var CURSOR_NO_OPINION = { permission: "ask" };
|
|
10
|
+
var shellDoubleQuoted = (text) => `"${text.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
11
|
+
var CURSOR_MISS = `{ printf %s ${shellDoubleQuoted(JSON.stringify(CURSOR_NO_OPINION))}; exit 0; }`;
|
|
9
12
|
var HOOK_GUARD_SCRIPTS = {
|
|
10
13
|
native_binary: `${LOCATE}; exec "$B" "$@"`,
|
|
11
14
|
node_binary: `${LOCATE}; ${NODE_PRESENT}; exec "$B" "$@"`,
|
|
12
|
-
node_script: `[ -f "$0" ] || exit 0; ${NODE_PRESENT}; exec node "$0" "$@"
|
|
15
|
+
node_script: `[ -f "$0" ] || exit 0; ${NODE_PRESENT}; exec node "$0" "$@"`,
|
|
16
|
+
cursor_gate_script: `[ -f "$0" ] || ${CURSOR_MISS}; command -v node >/dev/null 2>&1 || ${CURSOR_MISS}; exec node "$0" "$@"`
|
|
13
17
|
};
|
|
14
18
|
var BINARY_GUARDS = {
|
|
15
19
|
native: "native_binary",
|
|
@@ -19,21 +23,25 @@ var guardKindOf = (target) => {
|
|
|
19
23
|
switch (target.kind) {
|
|
20
24
|
case "node":
|
|
21
25
|
return "node_script";
|
|
26
|
+
case "cursor_gate":
|
|
27
|
+
return "cursor_gate_script";
|
|
22
28
|
case "binary":
|
|
23
29
|
return BINARY_GUARDS[target.runtime];
|
|
24
30
|
}
|
|
25
31
|
};
|
|
32
|
+
var targetPath = (target) => target.kind === "binary" ? target.path : target.script;
|
|
33
|
+
var runsThroughNode = (target) => target.kind !== "binary";
|
|
26
34
|
var guardHookCommand = (target, platform = process.platform) => {
|
|
27
|
-
const path = quoteIfNeeded(target
|
|
28
|
-
if (platform === "win32") return target
|
|
35
|
+
const path = quoteIfNeeded(targetPath(target));
|
|
36
|
+
if (platform === "win32") return runsThroughNode(target) ? `node ${path}` : path;
|
|
29
37
|
return `${HOOK_SHELL} '${HOOK_GUARD_SCRIPTS[guardKindOf(target)]}' ${path}`;
|
|
30
38
|
};
|
|
31
39
|
var guardBinary = (path, platform) => guardHookCommand({ kind: "binary", path, runtime: "node" }, platform);
|
|
32
|
-
var
|
|
40
|
+
var guardCursorGate = (script, platform) => guardHookCommand({ kind: "cursor_gate", script }, platform);
|
|
33
41
|
var guardedArgv = (target, args = [], platform = process.platform) => {
|
|
34
|
-
const path = target
|
|
42
|
+
const path = targetPath(target);
|
|
35
43
|
if (platform === "win32") {
|
|
36
|
-
return target
|
|
44
|
+
return runsThroughNode(target) ? ["node", path, ...args] : [path, ...args];
|
|
37
45
|
}
|
|
38
46
|
const [shell, ...shellArgs] = HOOK_SHELL.split(/\s+/);
|
|
39
47
|
return [shell, ...shellArgs, HOOK_GUARD_SCRIPTS[guardKindOf(target)], path, ...args];
|
|
@@ -41,6 +49,6 @@ var guardedArgv = (target, args = [], platform = process.platform) => {
|
|
|
41
49
|
|
|
42
50
|
export {
|
|
43
51
|
guardBinary,
|
|
44
|
-
|
|
52
|
+
guardCursorGate,
|
|
45
53
|
guardedArgv
|
|
46
54
|
};
|
|
@@ -7,10 +7,10 @@ import {
|
|
|
7
7
|
} from "./chunk-L4ZCFMWW.js";
|
|
8
8
|
import {
|
|
9
9
|
getMachineId
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-54ELC7D7.js";
|
|
11
11
|
import {
|
|
12
12
|
pusharyDir
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-KUZWXPUS.js";
|
|
14
14
|
import {
|
|
15
15
|
getPackageVersion
|
|
16
16
|
} from "./chunk-DINDL2CF.js";
|
|
@@ -383,18 +383,32 @@ var manageDaemonService = (action, options = {}) => {
|
|
|
383
383
|
}
|
|
384
384
|
return applyDaemonService(action, definition, { run: options.run });
|
|
385
385
|
};
|
|
386
|
+
var nominalDaemonService = (options) => buildDaemonService({
|
|
387
|
+
platform: options.platform ?? process.platform,
|
|
388
|
+
homeDir: options.homeDir ?? homedir(),
|
|
389
|
+
stateDir: options.stateDir ?? pusharyDir(),
|
|
390
|
+
execPath: options.execPath ?? process.execPath,
|
|
391
|
+
daemonEntry: options.daemonEntry ?? process.execPath,
|
|
392
|
+
pathEnv: options.pathEnv ?? process.env.PATH ?? "",
|
|
393
|
+
uid: options.uid ?? process.getuid?.()
|
|
394
|
+
});
|
|
386
395
|
var hasDaemonServiceDefinition = (options = {}) => {
|
|
387
|
-
const definition =
|
|
388
|
-
platform: options.platform ?? process.platform,
|
|
389
|
-
homeDir: options.homeDir ?? homedir(),
|
|
390
|
-
stateDir: options.stateDir ?? pusharyDir(),
|
|
391
|
-
execPath: options.execPath ?? process.execPath,
|
|
392
|
-
daemonEntry: options.daemonEntry ?? process.execPath,
|
|
393
|
-
pathEnv: options.pathEnv ?? process.env.PATH ?? "",
|
|
394
|
-
uid: options.uid ?? process.getuid?.()
|
|
395
|
-
});
|
|
396
|
+
const definition = nominalDaemonService(options);
|
|
396
397
|
return definition !== null && existsSync(definition.definitionPath);
|
|
397
398
|
};
|
|
399
|
+
var daemonServiceDefinitionPath = (options = {}) => nominalDaemonService(options)?.definitionPath ?? null;
|
|
400
|
+
var readInstalledDaemonService = (options = {}) => {
|
|
401
|
+
const definition = nominalDaemonService(options);
|
|
402
|
+
if (!definition) return null;
|
|
403
|
+
try {
|
|
404
|
+
return {
|
|
405
|
+
path: definition.definitionPath,
|
|
406
|
+
contents: readFileSync(definition.definitionPath, definition.encoding ?? "utf-8")
|
|
407
|
+
};
|
|
408
|
+
} catch {
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
};
|
|
398
412
|
var spawnDetachedDaemon = (plan, options = {}) => {
|
|
399
413
|
const spawnImpl = options.spawnImpl ?? spawnClaude;
|
|
400
414
|
const child = spawnImpl(plan.command, plan.args, {
|
|
@@ -418,13 +432,19 @@ var processIsAlive = (pid) => {
|
|
|
418
432
|
};
|
|
419
433
|
var isString = (value) => typeof value === "string" && value.length > 0;
|
|
420
434
|
var isTimestamp = (value) => isString(value) && Number.isFinite(Date.parse(value));
|
|
435
|
+
var daemonConfigurationId = (apiKey) => createHash("sha256").update(apiKey).digest("hex").slice(0, 16);
|
|
421
436
|
var currentConfigurationId = () => {
|
|
422
437
|
try {
|
|
423
|
-
return
|
|
438
|
+
return daemonConfigurationId(getApiKey());
|
|
424
439
|
} catch {
|
|
425
440
|
return null;
|
|
426
441
|
}
|
|
427
442
|
};
|
|
443
|
+
var parseDaemonLastError = (value) => {
|
|
444
|
+
if (typeof value !== "object" || value === null) return null;
|
|
445
|
+
const error = value;
|
|
446
|
+
return isString(error.message) && isTimestamp(error.at) ? { message: error.message, at: error.at } : null;
|
|
447
|
+
};
|
|
428
448
|
var parseDaemonState = (value) => {
|
|
429
449
|
if (typeof value !== "object" || value === null) return null;
|
|
430
450
|
const state = value;
|
|
@@ -438,7 +458,8 @@ var parseDaemonState = (value) => {
|
|
|
438
458
|
startedAt: state.startedAt,
|
|
439
459
|
updatedAt: state.updatedAt,
|
|
440
460
|
lastSuccessfulHeartbeatAt: state.lastSuccessfulHeartbeatAt,
|
|
441
|
-
lastSuccessfulControlAt: state.lastSuccessfulControlAt ?? null
|
|
461
|
+
lastSuccessfulControlAt: state.lastSuccessfulControlAt ?? null,
|
|
462
|
+
lastError: parseDaemonLastError(state.lastError)
|
|
442
463
|
};
|
|
443
464
|
};
|
|
444
465
|
var parseLockOwner = (value) => {
|
|
@@ -461,16 +482,17 @@ var readDaemonStatus = (options = {}) => {
|
|
|
461
482
|
startedAt: null,
|
|
462
483
|
updatedAt: null,
|
|
463
484
|
lastSuccessfulHeartbeatAt: null,
|
|
464
|
-
lastSuccessfulControlAt: null
|
|
485
|
+
lastSuccessfulControlAt: null,
|
|
486
|
+
lastError: null
|
|
465
487
|
};
|
|
466
488
|
}
|
|
467
489
|
const value = state.kind === "ok" ? parseDaemonState(state.value) : null;
|
|
468
490
|
if (value) {
|
|
469
491
|
const alive = (options.isProcessAlive ?? processIsAlive)(value.pid);
|
|
470
492
|
const fresh = (options.now ?? Date.now)() - Date.parse(value.updatedAt) <= LOCAL_LEASE_TIMEOUT_MS;
|
|
471
|
-
const
|
|
493
|
+
const expectedConfigurationId2 = options.configurationId === void 0 ? currentConfigurationId() : options.configurationId;
|
|
472
494
|
return {
|
|
473
|
-
kind: !alive || !fresh ? "unresponsive" : value.version !== installedVersion ? "version-mismatch" :
|
|
495
|
+
kind: !alive || !fresh ? "unresponsive" : value.version !== installedVersion ? "version-mismatch" : expectedConfigurationId2 !== null && value.configurationId !== null && value.configurationId !== expectedConfigurationId2 ? "configuration-mismatch" : "running",
|
|
474
496
|
installedVersion,
|
|
475
497
|
machineId: value.machineId,
|
|
476
498
|
pid: value.pid,
|
|
@@ -479,7 +501,8 @@ var readDaemonStatus = (options = {}) => {
|
|
|
479
501
|
startedAt: value.startedAt,
|
|
480
502
|
updatedAt: value.updatedAt,
|
|
481
503
|
lastSuccessfulHeartbeatAt: value.lastSuccessfulHeartbeatAt,
|
|
482
|
-
lastSuccessfulControlAt: value.lastSuccessfulControlAt
|
|
504
|
+
lastSuccessfulControlAt: value.lastSuccessfulControlAt,
|
|
505
|
+
lastError: value.lastError
|
|
483
506
|
};
|
|
484
507
|
}
|
|
485
508
|
return {
|
|
@@ -492,7 +515,8 @@ var readDaemonStatus = (options = {}) => {
|
|
|
492
515
|
startedAt: null,
|
|
493
516
|
updatedAt: null,
|
|
494
517
|
lastSuccessfulHeartbeatAt: null,
|
|
495
|
-
lastSuccessfulControlAt: null
|
|
518
|
+
lastSuccessfulControlAt: null,
|
|
519
|
+
lastError: null
|
|
496
520
|
};
|
|
497
521
|
};
|
|
498
522
|
var safeUnlink = (path) => {
|
|
@@ -561,7 +585,8 @@ var acquireDaemonLease = (options = {}) => {
|
|
|
561
585
|
startedAt,
|
|
562
586
|
updatedAt: startedAt,
|
|
563
587
|
lastSuccessfulHeartbeatAt: null,
|
|
564
|
-
lastSuccessfulControlAt: null
|
|
588
|
+
lastSuccessfulControlAt: null,
|
|
589
|
+
lastError: null
|
|
565
590
|
};
|
|
566
591
|
try {
|
|
567
592
|
writeJsonAtomic(statePath(dir), state, 384);
|
|
@@ -605,6 +630,14 @@ var acquireDaemonLease = (options = {}) => {
|
|
|
605
630
|
lastSuccessfulControlAt: timestamp
|
|
606
631
|
});
|
|
607
632
|
},
|
|
633
|
+
recordError: (message, at = now()) => {
|
|
634
|
+
const timestamp = new Date(at).toISOString();
|
|
635
|
+
updateState({
|
|
636
|
+
...currentState,
|
|
637
|
+
updatedAt: timestamp,
|
|
638
|
+
lastError: { message: message.slice(0, 240), at: timestamp }
|
|
639
|
+
});
|
|
640
|
+
},
|
|
608
641
|
shouldYield: () => {
|
|
609
642
|
if (!isOwner()) return true;
|
|
610
643
|
const target = readJsonSafe(targetPath(dir));
|
|
@@ -670,6 +703,44 @@ var stopDaemonRunning = async (options = {}) => {
|
|
|
670
703
|
safeUnlink(lockPath(dir));
|
|
671
704
|
return { kind: "stopped" };
|
|
672
705
|
};
|
|
706
|
+
var needsLoginPath = (dir) => join(dir, "daemon-needs-login.json");
|
|
707
|
+
var isLoginReason = (value) => value === "key_rejected" || value === "plan_inactive";
|
|
708
|
+
var parseDaemonNeedsLogin = (value) => {
|
|
709
|
+
if (typeof value !== "object" || value === null) return null;
|
|
710
|
+
const record = value;
|
|
711
|
+
if (!isLoginReason(record.reason) || !isTimestamp(record.at)) return null;
|
|
712
|
+
if (!(record.configurationId === null || isString(record.configurationId))) return null;
|
|
713
|
+
return { reason: record.reason, at: record.at, configurationId: record.configurationId };
|
|
714
|
+
};
|
|
715
|
+
var expectedConfigurationId = (options) => options.configurationId === void 0 ? currentConfigurationId() : options.configurationId;
|
|
716
|
+
var writeDaemonNeedsLogin = (reason, options = {}) => {
|
|
717
|
+
const record = {
|
|
718
|
+
reason,
|
|
719
|
+
at: new Date((options.now ?? Date.now)()).toISOString(),
|
|
720
|
+
configurationId: expectedConfigurationId(options)
|
|
721
|
+
};
|
|
722
|
+
writeJsonAtomic(needsLoginPath(options.dir ?? pusharyDir()), record, 384);
|
|
723
|
+
return record;
|
|
724
|
+
};
|
|
725
|
+
var readDaemonNeedsLogin = (options = {}) => {
|
|
726
|
+
const stored = readJsonSafe(needsLoginPath(options.dir ?? pusharyDir()));
|
|
727
|
+
const record = stored.kind === "ok" ? parseDaemonNeedsLogin(stored.value) : null;
|
|
728
|
+
if (!record) return { kind: "none" };
|
|
729
|
+
return record.configurationId === expectedConfigurationId(options) ? { kind: "blocked", record } : { kind: "stale", record };
|
|
730
|
+
};
|
|
731
|
+
var clearDaemonNeedsLogin = (options = {}) => {
|
|
732
|
+
const path = needsLoginPath(options.dir ?? pusharyDir());
|
|
733
|
+
if (!existsSync(path)) return false;
|
|
734
|
+
safeUnlink(path);
|
|
735
|
+
return true;
|
|
736
|
+
};
|
|
737
|
+
var describeDaemonLoginBlock = (record) => record.reason === "key_rejected" ? `this machine's key was rejected at ${record.at}; run \`pushary login\` to sign in again` : `this workspace had no active plan at ${record.at}; pick one at pushary.com/dashboard/settings?tab=billing, then run \`pushary login\``;
|
|
738
|
+
var recoverDaemonAfterLogin = (options = {}) => {
|
|
739
|
+
const cleared = clearDaemonNeedsLogin(options);
|
|
740
|
+
if (!cleared || !hasDaemonServiceDefinition(options)) return { cleared, restarted: false, detail: null };
|
|
741
|
+
const started = manageDaemonService("start", options);
|
|
742
|
+
return started.kind === "ok" ? { cleared, restarted: true, detail: null } : { cleared, restarted: false, detail: started.detail };
|
|
743
|
+
};
|
|
673
744
|
var deactivateDaemonService = async (action, options) => {
|
|
674
745
|
const platform = options.platform ?? process.platform;
|
|
675
746
|
const supported = ["darwin", "linux", "win32"].includes(platform);
|
|
@@ -966,6 +1037,10 @@ var ensureDaemonRunning = async (options = {}) => {
|
|
|
966
1037
|
return { kind: "not-configured", status: initial, detail: "Pushary is not configured on this machine" };
|
|
967
1038
|
}
|
|
968
1039
|
if (initial.kind === "running") return { kind: "running", status: initial };
|
|
1040
|
+
const loginBlock = readDaemonNeedsLogin(options);
|
|
1041
|
+
if (loginBlock.kind === "blocked") {
|
|
1042
|
+
return { kind: "unavailable", status: initial, detail: describeDaemonLoginBlock(loginBlock.record) };
|
|
1043
|
+
}
|
|
969
1044
|
let handoffStatus;
|
|
970
1045
|
try {
|
|
971
1046
|
handoffStatus = await requestDaemonHandoff(initial, options);
|
|
@@ -1020,10 +1095,17 @@ export {
|
|
|
1020
1095
|
parseDaemonAction,
|
|
1021
1096
|
manageDaemonService,
|
|
1022
1097
|
hasDaemonServiceDefinition,
|
|
1098
|
+
daemonServiceDefinitionPath,
|
|
1099
|
+
readInstalledDaemonService,
|
|
1023
1100
|
readDaemonStatus,
|
|
1024
1101
|
acquireDaemonLease,
|
|
1025
1102
|
requestDaemonHandoff,
|
|
1026
1103
|
stopDaemonRunning,
|
|
1104
|
+
writeDaemonNeedsLogin,
|
|
1105
|
+
readDaemonNeedsLogin,
|
|
1106
|
+
clearDaemonNeedsLogin,
|
|
1107
|
+
describeDaemonLoginBlock,
|
|
1108
|
+
recoverDaemonAfterLogin,
|
|
1027
1109
|
stopDaemonService,
|
|
1028
1110
|
disableDaemonService,
|
|
1029
1111
|
replaceDaemonService,
|
|
@@ -72,13 +72,13 @@ import { homedir, tmpdir } from "os";
|
|
|
72
72
|
import { createHash } from "crypto";
|
|
73
73
|
import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, renameSync, unlinkSync, rmSync, statSync } from "fs";
|
|
74
74
|
var PENDING_DIR = join(tmpdir(), "pushary-pending");
|
|
75
|
-
var
|
|
75
|
+
var commandRoot = () => process.env.PUSHARY_COMMAND_DIR?.trim() || join(homedir(), ".pushary", "run", "commands");
|
|
76
76
|
var DEFAULT_SESSION = "_no_session";
|
|
77
77
|
var GRACE_MS = 10 * 60 * 1e3;
|
|
78
78
|
var sanitize = (sessionId) => sessionId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 128) || DEFAULT_SESSION;
|
|
79
79
|
var dirFor = (sessionId) => join(PENDING_DIR, sanitize(sessionId));
|
|
80
80
|
var commandDirFor = (sessionId, namespace = "") => join(
|
|
81
|
-
|
|
81
|
+
commandRoot(),
|
|
82
82
|
createHash("sha256").update(namespace).digest("hex").slice(0, 16),
|
|
83
83
|
sanitize(sessionId)
|
|
84
84
|
);
|
|
@@ -122,12 +122,12 @@ var pruneExpiredCommands = (dir) => {
|
|
|
122
122
|
var pruneExpiredCommandStore = () => {
|
|
123
123
|
let namespaces;
|
|
124
124
|
try {
|
|
125
|
-
namespaces = readdirSync(
|
|
125
|
+
namespaces = readdirSync(commandRoot(), { withFileTypes: true });
|
|
126
126
|
} catch {
|
|
127
127
|
return;
|
|
128
128
|
}
|
|
129
129
|
for (const namespace of namespaces.filter((entry) => entry.isDirectory())) {
|
|
130
|
-
const namespaceDir = join(
|
|
130
|
+
const namespaceDir = join(commandRoot(), namespace.name);
|
|
131
131
|
let sessions;
|
|
132
132
|
try {
|
|
133
133
|
sessions = readdirSync(namespaceDir, { withFileTypes: true });
|
|
@@ -189,10 +189,27 @@ var removePendingCommand = (sessionId, id, namespace = "") => {
|
|
|
189
189
|
}
|
|
190
190
|
};
|
|
191
191
|
var isDefaultSession = (sessionId) => sanitize(sessionId) === DEFAULT_SESSION;
|
|
192
|
-
var savePendingQuestion = (sessionId, correlationId) => {
|
|
192
|
+
var savePendingQuestion = (sessionId, correlationId, toolUseId) => {
|
|
193
193
|
const dir = dirFor(sessionId);
|
|
194
194
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
195
|
-
writeFileSync(join(dir, correlationId), "", "utf-8");
|
|
195
|
+
writeFileSync(join(dir, correlationId), toolUseId ?? "", "utf-8");
|
|
196
|
+
};
|
|
197
|
+
var listPendingQuestionsForToolUse = (sessionId, toolUseId) => {
|
|
198
|
+
if (!toolUseId) return [];
|
|
199
|
+
const dir = dirFor(sessionId);
|
|
200
|
+
let files;
|
|
201
|
+
try {
|
|
202
|
+
files = readdirSync(dir);
|
|
203
|
+
} catch {
|
|
204
|
+
return [];
|
|
205
|
+
}
|
|
206
|
+
return files.filter((name) => {
|
|
207
|
+
try {
|
|
208
|
+
return readFileSync(join(dir, name), "utf-8") === toolUseId;
|
|
209
|
+
} catch {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
});
|
|
196
213
|
};
|
|
197
214
|
var listPendingQuestions = (sessionId) => {
|
|
198
215
|
const dir = dirFor(sessionId);
|
|
@@ -241,6 +258,7 @@ export {
|
|
|
241
258
|
removePendingCommand,
|
|
242
259
|
isDefaultSession,
|
|
243
260
|
savePendingQuestion,
|
|
261
|
+
listPendingQuestionsForToolUse,
|
|
244
262
|
listPendingQuestions,
|
|
245
263
|
removePendingQuestion,
|
|
246
264
|
removePendingSession
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PRETOOLUSE_GATED_TOOLS,
|
|
3
3
|
PRETOOLUSE_PHONE_ANSWERED_TOOLS
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-2LAHXHBR.js";
|
|
5
5
|
import {
|
|
6
6
|
guardBinary
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-EZIEXHYM.js";
|
|
8
8
|
import {
|
|
9
9
|
isProtectedByScope
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-KUZWXPUS.js";
|
|
11
11
|
import {
|
|
12
12
|
HOOK_BUDGETS,
|
|
13
13
|
HOOK_LOCATOR_BINARY
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-VNMKIJZB.js";
|
|
15
15
|
|
|
16
16
|
// src/claude-config.ts
|
|
17
17
|
import { join } from "path";
|
|
@@ -234,6 +234,7 @@ var GEMINI_HOOK_EVENTS = [
|
|
|
234
234
|
{ event: "BeforeTool", matcher: "run_shell_command|write_file|replace", delivery: "approval", timeoutMs: HOOK_BUDGETS.gemini.budgetSeconds * 1e3 },
|
|
235
235
|
{ event: "AfterTool", matcher: "run_shell_command|write_file|replace", delivery: "telemetry", timeoutMs: 1e4 },
|
|
236
236
|
{ event: "BeforeAgent", delivery: "telemetry", timeoutMs: 1e4 },
|
|
237
|
+
{ event: "AfterAgent", delivery: "telemetry", timeoutMs: 1e4 },
|
|
237
238
|
{ event: "SessionStart", delivery: "telemetry", timeoutMs: 1e4 },
|
|
238
239
|
{ event: "SessionEnd", delivery: "telemetry", timeoutMs: 1e4 }
|
|
239
240
|
];
|
|
@@ -330,6 +331,7 @@ var missingGeminiHookEvents = (settings) => {
|
|
|
330
331
|
export {
|
|
331
332
|
parseVersion,
|
|
332
333
|
compareVersion,
|
|
334
|
+
CLAUDE_HOOK_EVENTS,
|
|
333
335
|
addClaudeMcpServer,
|
|
334
336
|
isClaudeMcpSnapshot,
|
|
335
337
|
captureClaudeMcpServer,
|
|
@@ -340,6 +342,7 @@ export {
|
|
|
340
342
|
addPusharyHooks,
|
|
341
343
|
removePusharySettings,
|
|
342
344
|
GEMINI_HOOK_BINARY,
|
|
345
|
+
GEMINI_HOOK_EVENTS,
|
|
343
346
|
addGeminiMcpServer,
|
|
344
347
|
addGeminiHooks,
|
|
345
348
|
removeGeminiSettings,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
HOOK_BUDGETS,
|
|
3
3
|
HOOK_LOCATOR_BINARY
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-VNMKIJZB.js";
|
|
5
5
|
|
|
6
6
|
// src/removal-scope.ts
|
|
7
7
|
var namesAppLocator = (command) => command.includes(HOOK_LOCATOR_BINARY);
|
|
@@ -29,7 +29,7 @@ var codexHooksJson = () => join(codexHome(), "hooks.json");
|
|
|
29
29
|
var codexAgentsMd = () => join(codexHome(), "AGENTS.md");
|
|
30
30
|
var codexSkillDir = () => join(codexHome(), "skills", "pushary");
|
|
31
31
|
var CODEX_HOOK_EVENTS = [
|
|
32
|
-
{ event: "PermissionRequest",
|
|
32
|
+
{ event: "PermissionRequest", delivery: "approval", timeout: HOOK_BUDGETS.codex.budgetSeconds, statusMessage: "Waiting for your phone" },
|
|
33
33
|
{ event: "PreToolUse", matcher: "Bash|apply_patch", delivery: "approval", timeout: HOOK_BUDGETS.codex.budgetSeconds, statusMessage: "Checking Pushary policy" },
|
|
34
34
|
{ event: "PostToolUse", matcher: "Bash|apply_patch", delivery: "telemetry", timeout: 10 },
|
|
35
35
|
{ event: "UserPromptSubmit", delivery: "telemetry", timeout: 10 },
|
|
@@ -286,6 +286,7 @@ export {
|
|
|
286
286
|
codexHooksJson,
|
|
287
287
|
codexAgentsMd,
|
|
288
288
|
codexSkillDir,
|
|
289
|
+
CODEX_HOOK_EVENTS,
|
|
289
290
|
addCodexMcpServer,
|
|
290
291
|
readCodexMcpAuth,
|
|
291
292
|
addCodexHooks,
|