@pushary/agent-hooks 1.6.3 → 1.7.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/CHANGELOG.md +13 -0
- package/README.md +3 -2
- package/dist/bin/pushary-bell-hook.js +24 -22
- package/dist/bin/pushary-bell.js +11 -12
- package/dist/bin/pushary-claude.js +29 -28
- package/dist/bin/pushary-clean.js +99 -40
- package/dist/bin/pushary-codex-bridge.js +25 -25
- package/dist/bin/pushary-codex-hook.js +39 -34
- package/dist/bin/pushary-codex.js +13 -12
- package/dist/bin/pushary-connect.js +13 -12
- package/dist/bin/pushary-cowork.js +5 -6
- package/dist/bin/pushary-daemon.js +31 -32
- package/dist/bin/pushary-disconnect.js +22 -21
- package/dist/bin/pushary-doctor.js +98 -61
- package/dist/bin/pushary-elicitation-hook.js +26 -25
- package/dist/bin/pushary-gemini-bridge.js +12 -13
- package/dist/bin/pushary-gemini-hook.js +35 -30
- package/dist/bin/pushary-hook.js +38 -33
- package/dist/bin/pushary-login.js +13 -15
- package/dist/bin/pushary-logout.js +12 -13
- package/dist/bin/pushary-mcp.d.ts +1 -0
- package/dist/bin/pushary-mcp.js +56 -0
- package/dist/bin/pushary-mode.js +10 -8
- package/dist/bin/pushary-notification-hook.js +28 -27
- package/dist/bin/pushary-opencode-hook.js +34 -29
- package/dist/bin/pushary-permission-denied-hook.js +32 -31
- package/dist/bin/pushary-permission-hook.js +31 -30
- package/dist/bin/pushary-post-hook.js +28 -27
- package/dist/bin/pushary-prompt-hook.js +28 -27
- package/dist/bin/pushary-session-end-hook.js +28 -27
- package/dist/bin/pushary-session-start-hook.js +32 -31
- package/dist/bin/pushary-setup.js +222 -110
- package/dist/bin/pushary-stats.js +5 -6
- package/dist/bin/pushary-status.js +15 -16
- package/dist/bin/pushary-stop-hook.js +28 -27
- package/dist/bin/pushary-stopfailure-hook.js +28 -27
- package/dist/bin/pushary-suggestions.js +4 -4
- package/dist/bin/pushary-transcript-register.js +19 -20
- package/dist/bin/pushary-transcripts.js +7 -8
- package/dist/bin/pushary-upgrade.js +28 -28
- package/dist/bin/pushary-wait.js +10 -8
- package/dist/bin/pushary.js +9 -5
- package/dist/{chunk-4KSTLHWY.js → chunk-2C7CCWVX.js} +1 -1
- package/dist/{chunk-P4BNYJ5Q.js → chunk-2PS6QWDW.js} +2 -2
- package/dist/{chunk-ILGL4L3J.js → chunk-4F46VIEE.js} +8 -8
- package/dist/{chunk-UAV5MNKY.js → chunk-52NVBNJT.js} +3 -3
- package/dist/{chunk-GQPCPEG2.js → chunk-5HLTWFJ6.js} +1 -1
- package/dist/{chunk-EJ7ADMNM.js → chunk-6ADZQFVW.js} +1 -1
- package/dist/{chunk-QKOVVL7I.js → chunk-73T6MSU5.js} +7 -7
- package/dist/{chunk-YOCXPNQ5.js → chunk-7OS4IHGI.js} +2 -2
- package/dist/{chunk-BY2CRWBO.js → chunk-B234346F.js} +1 -1
- package/dist/{chunk-EA3IELHP.js → chunk-B3UXMSMW.js} +6 -2
- package/dist/{chunk-STOMNX4S.js → chunk-B3YQANFN.js} +1 -1
- package/dist/{chunk-JBU7XKY2.js → chunk-B76WVEDX.js} +1 -1
- package/dist/{chunk-WA7SQX6S.js → chunk-BBKNJJVX.js} +6 -6
- package/dist/{chunk-KNIIFNNQ.js → chunk-BH2SKSSK.js} +1 -1
- package/dist/{chunk-CTOK3AYA.js → chunk-BWYJ6AAF.js} +6 -7
- package/dist/{chunk-HCVAMXHT.js → chunk-CVALVGA7.js} +1 -1
- package/dist/{chunk-DX5FOQSO.js → chunk-D6BULEJW.js} +2 -2
- package/dist/{chunk-RJUHSFYE.js → chunk-DANOZVZK.js} +1 -1
- package/dist/{chunk-3BJQYXTL.js → chunk-DNJUSFBL.js} +13 -5
- package/dist/{chunk-57YDFAPP.js → chunk-DVQDPI3T.js} +8 -8
- package/dist/{chunk-BEHGVU4W.js → chunk-DZURML2D.js} +1 -1
- package/dist/{chunk-LDS4ERW4.js → chunk-EAQMWJPG.js} +10 -10
- package/dist/{chunk-LVHGSD5E.js → chunk-EGKXSQCY.js} +13 -7
- package/dist/{chunk-HHYVBHHC.js → chunk-HDRNWNVP.js} +6 -6
- package/dist/{chunk-JBXUKGZS.js → chunk-IJ6ECPA6.js} +15 -9
- package/dist/{chunk-NMRH3VX4.js → chunk-J2355HR7.js} +1 -1
- package/dist/{chunk-HETOKNIL.js → chunk-JNXVOU6Y.js} +10 -6
- package/dist/{chunk-COGHCL6K.js → chunk-JXUAXO74.js} +2 -2
- package/dist/chunk-KW2RRR7D.js +124 -0
- package/dist/{chunk-66H2QQAK.js → chunk-KXMZMZD3.js} +1 -1
- package/dist/{chunk-H3FEVIQD.js → chunk-KYQXUGH7.js} +3 -3
- package/dist/{chunk-P35IUFFR.js → chunk-L4H2Q2S4.js} +276 -19
- package/dist/{chunk-NKR4YDRP.js → chunk-LEZFOTMI.js} +1 -1
- package/dist/{chunk-WD6W7QDI.js → chunk-O2LEPIU6.js} +1 -1
- package/dist/chunk-POFBE6ZY.js +12 -0
- package/dist/{chunk-SND3C2AV.js → chunk-PQ4F64M2.js} +3 -3
- package/dist/{chunk-3UOC3GRO.js → chunk-PUDBG3GM.js} +1 -1
- package/dist/{chunk-YQMFGWBD.js → chunk-QXAK5RFU.js} +1 -1
- package/dist/{chunk-REYWOCQJ.js → chunk-R2XCLT6I.js} +4 -4
- package/dist/{chunk-NPGOQQCV.js → chunk-ROQK7IN3.js} +2 -2
- package/dist/{chunk-GU3EJCVV.js → chunk-S3UD4V6W.js} +39 -0
- package/dist/{chunk-ICGAIM2R.js → chunk-SQRWHALT.js} +2 -2
- package/dist/{chunk-JX67DVMY.js → chunk-UC65FJJT.js} +5 -6
- package/dist/{chunk-CWIARY6F.js → chunk-UOAQ6QMP.js} +2 -2
- package/dist/{chunk-OSJEMXSR.js → chunk-V4AQBAVG.js} +1 -1
- package/dist/chunk-V6QRSU2G.js +142 -0
- package/dist/{chunk-VDMX5EMU.js → chunk-VKVIAZQO.js} +1 -1
- package/dist/{chunk-BG6OCZ4N.js → chunk-WUNZAE7I.js} +5 -5
- package/dist/{chunk-TZ4KUNZG.js → chunk-X6KDKOV5.js} +3 -3
- package/dist/{chunk-LM2IOEDK.js → chunk-XNCMT7YY.js} +3 -1
- package/dist/{chunk-GHOB245Q.js → chunk-YROGUPOZ.js} +132 -30
- package/dist/chunk-YYINL576.js +120 -0
- package/dist/{chunk-57DDP4TB.js → chunk-ZQ7IDOKY.js} +3 -3
- package/dist/{reapply-IB7YE7BU.js → reapply-6PH3FMPX.js} +9 -10
- package/dist/src/index.js +20 -19
- package/package.json +2 -1
- package/dist/chunk-HWJW3CPO.js +0 -65
- package/dist/chunk-SAF6HGAA.js +0 -57
|
@@ -1,12 +1,244 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MCP_BRIDGE_BINARY
|
|
3
|
+
} from "./chunk-YYINL576.js";
|
|
1
4
|
import {
|
|
2
5
|
openCodeConfigDirFrom
|
|
3
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-B3YQANFN.js";
|
|
4
7
|
import {
|
|
5
8
|
codexHomeFrom
|
|
6
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-DNJUSFBL.js";
|
|
7
10
|
import {
|
|
8
11
|
HOOK_COMPATIBLE_PROFILES
|
|
9
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-YROGUPOZ.js";
|
|
13
|
+
|
|
14
|
+
// src/fx-config.ts
|
|
15
|
+
var FX_SERVER_NAME = "pushary";
|
|
16
|
+
var FX_OPERATION_TIMEOUT_MS = 75e3;
|
|
17
|
+
var FX_PROFILE_MAX_BYTES = 1024 * 1024 - 1;
|
|
18
|
+
var FX_SETTINGS_MAX_BYTES = 64 * 1024;
|
|
19
|
+
var FX_PUSHARY_TOOLS_RULE = "mcp_pushary_*";
|
|
20
|
+
var FX_TOOL_DISCOVERY_NOTE = 'In fx, these tools are named mcp_pushary_ask_user, mcp_pushary_send_notification, mcp_pushary_wait_for_answer and mcp_pushary_cancel_question. They are not loaded at the start of a turn: find them with capability_search (query "pushary") or select one with mcp_select_tool. When the user may be away from this terminal, use mcp_pushary_ask_user rather than your built-in question prompt.';
|
|
21
|
+
var ALLOW_ACTION = "allow";
|
|
22
|
+
var PERMISSION_KEY = "permission";
|
|
23
|
+
var WORKSPACES_KEY = "workspaces";
|
|
24
|
+
var CANONICAL_SERVERS_KEY = "mcp";
|
|
25
|
+
var ALIAS_SERVERS_KEY = "mcpServers";
|
|
26
|
+
var LOCAL_TRANSPORT = "local";
|
|
27
|
+
var SERVER_TRANSPORTS = /* @__PURE__ */ new Set(["local", "stdio", "http", "sse"]);
|
|
28
|
+
var STDIO_TRANSPORTS = /* @__PURE__ */ new Set(["local", "stdio"]);
|
|
29
|
+
var AMBIGUOUS_SERVER_KEYS = /* @__PURE__ */ new Set([
|
|
30
|
+
"mcpserver",
|
|
31
|
+
"mcpservers",
|
|
32
|
+
"modelcontextprotocol",
|
|
33
|
+
"modelcontextprotocolserver",
|
|
34
|
+
"modelcontextprotocolservers",
|
|
35
|
+
"servers"
|
|
36
|
+
]);
|
|
37
|
+
var PROFILE_SCAN_LIMIT = 64;
|
|
38
|
+
var PROFILE_KEY_BYTES_LIMIT = 128;
|
|
39
|
+
var FX_JSON_REFUSALS = {
|
|
40
|
+
"too-large": "is larger than fx reads",
|
|
41
|
+
"unparseable": "is not strict JSON (fx rejects comments, trailing commas and a byte-order mark)",
|
|
42
|
+
"duplicate-key": "repeats a key, which fx rejects",
|
|
43
|
+
"root-not-object": "is not a JSON object"
|
|
44
|
+
};
|
|
45
|
+
var FX_PROFILE_REFUSALS = {
|
|
46
|
+
...FX_JSON_REFUSALS,
|
|
47
|
+
"servers-not-object": 'has an "mcp" or "mcpServers" value that is not an object',
|
|
48
|
+
"ambiguous-server-key": "holds servers under a key fx does not read, so fx refuses to edit it too",
|
|
49
|
+
"too-many-keys": "has more top-level keys than fx checks for misplaced servers, so fx refuses to edit it too"
|
|
50
|
+
};
|
|
51
|
+
var FX_SETTINGS_REFUSALS = {
|
|
52
|
+
...FX_JSON_REFUSALS,
|
|
53
|
+
"permission-not-object": 'has a "permission" value that is not an object'
|
|
54
|
+
};
|
|
55
|
+
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
56
|
+
var stringTokenEnd = (text, start) => {
|
|
57
|
+
let index = start + 1;
|
|
58
|
+
while (index < text.length && text[index] !== '"') index += text[index] === "\\" ? 2 : 1;
|
|
59
|
+
return index;
|
|
60
|
+
};
|
|
61
|
+
var hasDuplicateObjectKeys = (json) => {
|
|
62
|
+
const scopes = [];
|
|
63
|
+
let expectingKey = false;
|
|
64
|
+
for (let index = 0; index < json.length; index += 1) {
|
|
65
|
+
const char = json[index];
|
|
66
|
+
if (char === '"') {
|
|
67
|
+
const end = stringTokenEnd(json, index);
|
|
68
|
+
const scope = scopes.at(-1);
|
|
69
|
+
if (expectingKey && scope) {
|
|
70
|
+
const key = JSON.parse(json.slice(index, end + 1));
|
|
71
|
+
if (typeof key === "string" && scope.has(key)) return true;
|
|
72
|
+
if (typeof key === "string") scope.add(key);
|
|
73
|
+
expectingKey = false;
|
|
74
|
+
}
|
|
75
|
+
index = end;
|
|
76
|
+
} else if (char === "{") {
|
|
77
|
+
scopes.push(/* @__PURE__ */ new Set());
|
|
78
|
+
expectingKey = true;
|
|
79
|
+
} else if (char === "[") {
|
|
80
|
+
scopes.push(null);
|
|
81
|
+
expectingKey = false;
|
|
82
|
+
} else if (char === "}" || char === "]") {
|
|
83
|
+
scopes.pop();
|
|
84
|
+
expectingKey = false;
|
|
85
|
+
} else if (char === ",") {
|
|
86
|
+
expectingKey = scopes.at(-1) instanceof Set;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
};
|
|
91
|
+
var normalizedServerKey = (key) => key.replace(/[^A-Za-z0-9]/g, "").toLowerCase();
|
|
92
|
+
var looksLikeServerEntry = (value) => {
|
|
93
|
+
const entry = asRecord(value);
|
|
94
|
+
if (!entry) return false;
|
|
95
|
+
return typeof entry.command === "string" || Array.isArray(entry.command) || typeof entry.url === "string" || typeof entry.type === "string" && SERVER_TRANSPORTS.has(entry.type);
|
|
96
|
+
};
|
|
97
|
+
var holdsServerEntries = (value) => Object.values(asRecord(value) ?? {}).some(looksLikeServerEntry);
|
|
98
|
+
var isServerMapKey = (key) => key === CANONICAL_SERVERS_KEY || key === ALIAS_SERVERS_KEY;
|
|
99
|
+
var exceedsScanLimit = (value) => Object.keys(asRecord(value) ?? {}).length > PROFILE_SCAN_LIMIT;
|
|
100
|
+
var isAmbiguousServerKey = (key, value) => {
|
|
101
|
+
if (isServerMapKey(key)) return false;
|
|
102
|
+
const scanned = Buffer.byteLength(key) > PROFILE_KEY_BYTES_LIMIT || AMBIGUOUS_SERVER_KEYS.has(normalizedServerKey(key));
|
|
103
|
+
return scanned && (exceedsScanLimit(value) || holdsServerEntries(value));
|
|
104
|
+
};
|
|
105
|
+
var misplacedServersRefusal = (profile) => {
|
|
106
|
+
if (exceedsScanLimit(profile)) return "too-many-keys";
|
|
107
|
+
return Object.keys(profile).some((key) => isAmbiguousServerKey(key, profile[key])) ? "ambiguous-server-key" : null;
|
|
108
|
+
};
|
|
109
|
+
var fxServersKeyOf = (profile) => !(CANONICAL_SERVERS_KEY in profile) && ALIAS_SERVERS_KEY in profile ? ALIAS_SERVERS_KEY : CANONICAL_SERVERS_KEY;
|
|
110
|
+
var refused = (reason) => ({ kind: "refused", reason });
|
|
111
|
+
var parseJson = (text) => {
|
|
112
|
+
try {
|
|
113
|
+
return { ok: true, value: JSON.parse(text) };
|
|
114
|
+
} catch {
|
|
115
|
+
return { ok: false };
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
var readFxJsonObject = (text, maxBytes) => {
|
|
119
|
+
if (Buffer.byteLength(text) > maxBytes) return { kind: "refused", reason: "too-large" };
|
|
120
|
+
if (!text.trim()) return { kind: "object", value: {} };
|
|
121
|
+
const parsed = parseJson(text);
|
|
122
|
+
if (!parsed.ok) return { kind: "refused", reason: "unparseable" };
|
|
123
|
+
if (hasDuplicateObjectKeys(text)) return { kind: "refused", reason: "duplicate-key" };
|
|
124
|
+
const value = asRecord(parsed.value);
|
|
125
|
+
return value ? { kind: "object", value } : { kind: "refused", reason: "root-not-object" };
|
|
126
|
+
};
|
|
127
|
+
var readFxProfile = (text) => {
|
|
128
|
+
const read = readFxJsonObject(text, FX_PROFILE_MAX_BYTES);
|
|
129
|
+
if (read.kind === "refused") return refused(read.reason);
|
|
130
|
+
const profile = read.value;
|
|
131
|
+
const serversKey = fxServersKeyOf(profile);
|
|
132
|
+
if (serversKey in profile && !asRecord(profile[serversKey])) return refused("servers-not-object");
|
|
133
|
+
const misplaced = misplacedServersRefusal(profile);
|
|
134
|
+
return misplaced ? refused(misplaced) : { kind: "profile", profile };
|
|
135
|
+
};
|
|
136
|
+
var fxPusharyEntry = (command) => ({
|
|
137
|
+
type: LOCAL_TRANSPORT,
|
|
138
|
+
command,
|
|
139
|
+
operation_timeout_ms: FX_OPERATION_TIMEOUT_MS
|
|
140
|
+
});
|
|
141
|
+
var withFxPusharyServer = (profile, entry) => {
|
|
142
|
+
const serversKey = fxServersKeyOf(profile);
|
|
143
|
+
return {
|
|
144
|
+
...profile,
|
|
145
|
+
[serversKey]: { ...asRecord(profile[serversKey]), [FX_SERVER_NAME]: { ...entry, command: [...entry.command] } }
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
var emptiedMapCanGo = (profile, serversKey) => serversKey === ALIAS_SERVERS_KEY || !(ALIAS_SERVERS_KEY in profile);
|
|
149
|
+
var withoutFxPusharyServer = (profile) => {
|
|
150
|
+
const serversKey = fxServersKeyOf(profile);
|
|
151
|
+
const servers = asRecord(profile[serversKey]);
|
|
152
|
+
if (!servers || !(FX_SERVER_NAME in servers)) return { profile, removed: false };
|
|
153
|
+
const remaining = Object.fromEntries(Object.entries(servers).filter(([name]) => name !== FX_SERVER_NAME));
|
|
154
|
+
const withoutServers = Object.fromEntries(Object.entries(profile).filter(([key]) => key !== serversKey));
|
|
155
|
+
const dropsMap = Object.keys(remaining).length === 0 && emptiedMapCanGo(profile, serversKey);
|
|
156
|
+
return {
|
|
157
|
+
profile: dropsMap ? withoutServers : { ...profile, [serversKey]: remaining },
|
|
158
|
+
removed: true
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
var namesBridge = (part) => typeof part === "string" && part.includes(MCP_BRIDGE_BINARY);
|
|
162
|
+
var commandNamesBridge = (command) => Array.isArray(command) ? command.some(namesBridge) : namesBridge(command);
|
|
163
|
+
var isFxPusharyEntry = (value) => {
|
|
164
|
+
const entry = asRecord(value);
|
|
165
|
+
if (!entry || entry.enabled === false) return false;
|
|
166
|
+
const transport = typeof entry.type === "string" ? entry.type : LOCAL_TRANSPORT;
|
|
167
|
+
return STDIO_TRANSPORTS.has(transport) && (commandNamesBridge(entry.command) || commandNamesBridge(entry.args));
|
|
168
|
+
};
|
|
169
|
+
var fxPusharyEntryOf = (profile) => asRecord(profile[fxServersKeyOf(profile)])?.[FX_SERVER_NAME];
|
|
170
|
+
var isBridgeTarget = (part) => namesBridge(part);
|
|
171
|
+
var fxBridgeTargetOf = (value) => {
|
|
172
|
+
const entry = asRecord(value);
|
|
173
|
+
const parts = [entry?.command, entry?.args].flatMap((part) => Array.isArray(part) ? part : [part]);
|
|
174
|
+
return parts.find(isBridgeTarget);
|
|
175
|
+
};
|
|
176
|
+
var fxFileText = (value, maxBytes) => {
|
|
177
|
+
const text = JSON.stringify(value);
|
|
178
|
+
return Buffer.byteLength(text) > maxBytes ? { kind: "refused", reason: "too-large" } : { kind: "text", text };
|
|
179
|
+
};
|
|
180
|
+
var fxProfileText = (profile) => fxFileText(profile, FX_PROFILE_MAX_BYTES);
|
|
181
|
+
var fxSettingsText = (settings) => fxFileText(settings, FX_SETTINGS_MAX_BYTES);
|
|
182
|
+
var readFxSettings = (text) => {
|
|
183
|
+
const read = readFxJsonObject(text, FX_SETTINGS_MAX_BYTES);
|
|
184
|
+
if (read.kind === "refused") return read;
|
|
185
|
+
const permission = read.value[PERMISSION_KEY];
|
|
186
|
+
if (permission !== void 0 && !asRecord(permission)) return { kind: "refused", reason: "permission-not-object" };
|
|
187
|
+
return { kind: "settings", settings: read.value };
|
|
188
|
+
};
|
|
189
|
+
var withPusharyToolsAllowed = (settings) => {
|
|
190
|
+
const permission = asRecord(settings[PERMISSION_KEY]) ?? {};
|
|
191
|
+
const existing = permission[FX_PUSHARY_TOOLS_RULE];
|
|
192
|
+
if (existing === ALLOW_ACTION) return { settings, change: "present" };
|
|
193
|
+
if (existing !== void 0) return { settings, change: "user-rule-kept" };
|
|
194
|
+
return {
|
|
195
|
+
settings: { ...settings, [PERMISSION_KEY]: { ...permission, [FX_PUSHARY_TOOLS_RULE]: ALLOW_ACTION } },
|
|
196
|
+
change: "added"
|
|
197
|
+
};
|
|
198
|
+
};
|
|
199
|
+
var withoutPusharyToolsAllowed = (settings) => {
|
|
200
|
+
const permission = asRecord(settings[PERMISSION_KEY]);
|
|
201
|
+
if (permission?.[FX_PUSHARY_TOOLS_RULE] !== ALLOW_ACTION) return { settings, removed: false };
|
|
202
|
+
const remaining = Object.fromEntries(Object.entries(permission).filter(([rule]) => rule !== FX_PUSHARY_TOOLS_RULE));
|
|
203
|
+
const withoutPermission = Object.fromEntries(Object.entries(settings).filter(([key]) => key !== PERMISSION_KEY));
|
|
204
|
+
return {
|
|
205
|
+
settings: Object.keys(remaining).length === 0 ? withoutPermission : { ...settings, [PERMISSION_KEY]: remaining },
|
|
206
|
+
removed: true
|
|
207
|
+
};
|
|
208
|
+
};
|
|
209
|
+
var pusharyToolsAllowed = (settings) => asRecord(settings[PERMISSION_KEY])?.[FX_PUSHARY_TOOLS_RULE] === ALLOW_ACTION;
|
|
210
|
+
var overridesPusharyRule = (workspace) => {
|
|
211
|
+
const permission = asRecord(asRecord(workspace)?.[PERMISSION_KEY]);
|
|
212
|
+
return permission !== void 0 && !(FX_PUSHARY_TOOLS_RULE in permission);
|
|
213
|
+
};
|
|
214
|
+
var workspacesOverridingPusharyRule = (settings) => Object.entries(asRecord(settings[WORKSPACES_KEY]) ?? {}).filter(([, workspace]) => overridesPusharyRule(workspace)).map(([path]) => path);
|
|
215
|
+
|
|
216
|
+
// src/fx-files.ts
|
|
217
|
+
import { existsSync, lstatSync, readFileSync, realpathSync } from "fs";
|
|
218
|
+
import { relative, isAbsolute } from "path";
|
|
219
|
+
var readFxFile = (path, read) => {
|
|
220
|
+
if (!existsSync(path)) return { kind: "missing" };
|
|
221
|
+
let text;
|
|
222
|
+
try {
|
|
223
|
+
text = readFileSync(path, "utf-8");
|
|
224
|
+
} catch {
|
|
225
|
+
return { kind: "unreadable" };
|
|
226
|
+
}
|
|
227
|
+
return read(text);
|
|
228
|
+
};
|
|
229
|
+
var isInside = (child, parent) => {
|
|
230
|
+
const path = relative(parent, child);
|
|
231
|
+
return path !== "" && !path.startsWith("..") && !isAbsolute(path);
|
|
232
|
+
};
|
|
233
|
+
var fxIgnoresInstructionsFile = (path, fxDir) => {
|
|
234
|
+
const link = lstatSync(path, { throwIfNoEntry: false });
|
|
235
|
+
if (!link?.isSymbolicLink()) return false;
|
|
236
|
+
try {
|
|
237
|
+
return !isInside(realpathSync(path), realpathSync(fxDir));
|
|
238
|
+
} catch {
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
};
|
|
10
242
|
|
|
11
243
|
// src/vscode-config.ts
|
|
12
244
|
import { homedir } from "os";
|
|
@@ -54,12 +286,12 @@ var blankJsonComments = (text) => {
|
|
|
54
286
|
}
|
|
55
287
|
return out;
|
|
56
288
|
};
|
|
57
|
-
var
|
|
289
|
+
var asRecord2 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
58
290
|
var parseJsonc = (text) => {
|
|
59
291
|
const blanked = blankJsonComments(text);
|
|
60
292
|
const withoutTrailingCommas = blanked.replace(/,(\s*[}\]])/g, "$1");
|
|
61
293
|
try {
|
|
62
|
-
return
|
|
294
|
+
return asRecord2(JSON.parse(withoutTrailingCommas));
|
|
63
295
|
} catch {
|
|
64
296
|
return void 0;
|
|
65
297
|
}
|
|
@@ -77,9 +309,9 @@ var registerPluginLocation = (existing, pluginDir) => {
|
|
|
77
309
|
}
|
|
78
310
|
const indent = detectIndent(existing);
|
|
79
311
|
try {
|
|
80
|
-
const parsed2 =
|
|
312
|
+
const parsed2 = asRecord2(JSON.parse(existing));
|
|
81
313
|
if (parsed2) {
|
|
82
|
-
const locations2 =
|
|
314
|
+
const locations2 = asRecord2(parsed2[PLUGIN_LOCATIONS_KEY]) ?? {};
|
|
83
315
|
if (locations2[pluginDir] === true) return { kind: "already" };
|
|
84
316
|
parsed2[PLUGIN_LOCATIONS_KEY] = { ...locations2, [pluginDir]: true };
|
|
85
317
|
return { kind: "merged", content: `${JSON.stringify(parsed2, null, indent)}
|
|
@@ -89,7 +321,7 @@ var registerPluginLocation = (existing, pluginDir) => {
|
|
|
89
321
|
}
|
|
90
322
|
const parsed = parseJsonc(existing);
|
|
91
323
|
if (!parsed) return { kind: "manual" };
|
|
92
|
-
const locations =
|
|
324
|
+
const locations = asRecord2(parsed[PLUGIN_LOCATIONS_KEY]);
|
|
93
325
|
if (locations?.[pluginDir] === true) return { kind: "already" };
|
|
94
326
|
if (parsed[PLUGIN_LOCATIONS_KEY] !== void 0) return { kind: "manual" };
|
|
95
327
|
const brace = rootBraceIndex(existing);
|
|
@@ -106,9 +338,9 @@ var unregisterPluginLocation = (existing, pluginDir) => {
|
|
|
106
338
|
if (existing === null || existing.trim() === "") return { kind: "absent" };
|
|
107
339
|
const indent = detectIndent(existing);
|
|
108
340
|
try {
|
|
109
|
-
const parsed2 =
|
|
341
|
+
const parsed2 = asRecord2(JSON.parse(existing));
|
|
110
342
|
if (parsed2) {
|
|
111
|
-
const locations2 =
|
|
343
|
+
const locations2 = asRecord2(parsed2[PLUGIN_LOCATIONS_KEY]);
|
|
112
344
|
if (!locations2 || !(pluginDir in locations2)) return { kind: "absent" };
|
|
113
345
|
const remaining = { ...locations2 };
|
|
114
346
|
delete remaining[pluginDir];
|
|
@@ -121,7 +353,7 @@ var unregisterPluginLocation = (existing, pluginDir) => {
|
|
|
121
353
|
}
|
|
122
354
|
const parsed = parseJsonc(existing);
|
|
123
355
|
if (!parsed) return { kind: "manual" };
|
|
124
|
-
const locations =
|
|
356
|
+
const locations = asRecord2(parsed[PLUGIN_LOCATIONS_KEY]);
|
|
125
357
|
if (!locations || !(pluginDir in locations)) return { kind: "absent" };
|
|
126
358
|
const line = new RegExp(
|
|
127
359
|
`\\n[ \\t]*${escapeRegExp(JSON.stringify(PLUGIN_LOCATIONS_KEY))}\\s*:\\s*\\{\\s*${escapeRegExp(JSON.stringify(pluginDir))}\\s*:\\s*true\\s*\\},?`
|
|
@@ -133,7 +365,7 @@ var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
|
133
365
|
var isPluginRegistered = (existing, pluginDir) => {
|
|
134
366
|
if (!existing) return false;
|
|
135
367
|
const parsed = parseJsonc(existing);
|
|
136
|
-
return
|
|
368
|
+
return asRecord2(parsed?.[PLUGIN_LOCATIONS_KEY])?.[pluginDir] === true;
|
|
137
369
|
};
|
|
138
370
|
var pluginLocationSnippet = (pluginDir) => `"${PLUGIN_LOCATIONS_KEY}": {
|
|
139
371
|
${JSON.stringify(pluginDir)}: true
|
|
@@ -156,7 +388,7 @@ var vscodeSettingsTargets = (exists, home, platform) => {
|
|
|
156
388
|
|
|
157
389
|
// src/setup/detect.ts
|
|
158
390
|
import { execSync } from "child_process";
|
|
159
|
-
import { existsSync } from "fs";
|
|
391
|
+
import { existsSync as existsSync2 } from "fs";
|
|
160
392
|
import { homedir as homedir2 } from "os";
|
|
161
393
|
import { dirname, join as join2 } from "path";
|
|
162
394
|
var whichCommand = () => process.platform === "win32" ? "where" : "which";
|
|
@@ -168,7 +400,7 @@ var binaryOnPath = (binary) => {
|
|
|
168
400
|
return false;
|
|
169
401
|
}
|
|
170
402
|
};
|
|
171
|
-
var defaultDeps = { onPath: binaryOnPath, exists:
|
|
403
|
+
var defaultDeps = { onPath: binaryOnPath, exists: existsSync2 };
|
|
172
404
|
var detectAgent = (probe, deps = defaultDeps) => {
|
|
173
405
|
const checked = [];
|
|
174
406
|
if (probe.binary) {
|
|
@@ -237,6 +469,11 @@ var agentProbes = (home = homedir2()) => ({
|
|
|
237
469
|
// never used.
|
|
238
470
|
configMarkers: [openCodeConfigDirFrom(home)]
|
|
239
471
|
},
|
|
472
|
+
fx: {
|
|
473
|
+
binary: null,
|
|
474
|
+
paths: [join2(home, ".fx", "settings.json"), join2(home, ".fx", "sessions"), join2(home, ".fx", "history.jsonl")],
|
|
475
|
+
configMarkers: []
|
|
476
|
+
},
|
|
240
477
|
...compatibleProbes(),
|
|
241
478
|
// "Other" is a set of printed instructions for any MCP or HTTP client. There is
|
|
242
479
|
// nothing to detect, and it must never be auto-selected.
|
|
@@ -266,7 +503,7 @@ var describeDetection = (result, home) => {
|
|
|
266
503
|
};
|
|
267
504
|
|
|
268
505
|
// src/instruction-file.ts
|
|
269
|
-
import { existsSync as
|
|
506
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync, mkdirSync, rmSync } from "fs";
|
|
270
507
|
import { dirname as dirname2 } from "path";
|
|
271
508
|
var PUSHARY_BEGIN = "<!-- BEGIN PUSHARY (managed by @pushary/agent-hooks; do not edit this block) -->";
|
|
272
509
|
var PUSHARY_END = "<!-- END PUSHARY -->";
|
|
@@ -296,10 +533,10 @@ ${body}
|
|
|
296
533
|
${PUSHARY_END}`;
|
|
297
534
|
var writeInstructionBlock = (filePath, body) => {
|
|
298
535
|
const dir = dirname2(filePath);
|
|
299
|
-
if (!
|
|
536
|
+
if (!existsSync3(dir)) mkdirSync(dir, { recursive: true });
|
|
300
537
|
let existing = "";
|
|
301
538
|
try {
|
|
302
|
-
existing =
|
|
539
|
+
existing = readFileSync2(filePath, "utf-8");
|
|
303
540
|
} catch {
|
|
304
541
|
}
|
|
305
542
|
const block = wrap(body);
|
|
@@ -317,7 +554,7 @@ var writeInstructionBlock = (filePath, body) => {
|
|
|
317
554
|
var removeInstructionBlock = (filePath) => {
|
|
318
555
|
let existing = "";
|
|
319
556
|
try {
|
|
320
|
-
existing =
|
|
557
|
+
existing = readFileSync2(filePath, "utf-8");
|
|
321
558
|
} catch {
|
|
322
559
|
return false;
|
|
323
560
|
}
|
|
@@ -334,13 +571,33 @@ var removeInstructionBlock = (filePath) => {
|
|
|
334
571
|
};
|
|
335
572
|
var hasInstructionBlock = (filePath) => {
|
|
336
573
|
try {
|
|
337
|
-
return
|
|
574
|
+
return readFileSync2(filePath, "utf-8").includes(PUSHARY_BEGIN);
|
|
338
575
|
} catch {
|
|
339
576
|
return false;
|
|
340
577
|
}
|
|
341
578
|
};
|
|
342
579
|
|
|
343
580
|
export {
|
|
581
|
+
FX_PUSHARY_TOOLS_RULE,
|
|
582
|
+
FX_TOOL_DISCOVERY_NOTE,
|
|
583
|
+
FX_PROFILE_REFUSALS,
|
|
584
|
+
FX_SETTINGS_REFUSALS,
|
|
585
|
+
readFxProfile,
|
|
586
|
+
fxPusharyEntry,
|
|
587
|
+
withFxPusharyServer,
|
|
588
|
+
withoutFxPusharyServer,
|
|
589
|
+
isFxPusharyEntry,
|
|
590
|
+
fxPusharyEntryOf,
|
|
591
|
+
fxBridgeTargetOf,
|
|
592
|
+
fxProfileText,
|
|
593
|
+
fxSettingsText,
|
|
594
|
+
readFxSettings,
|
|
595
|
+
withPusharyToolsAllowed,
|
|
596
|
+
withoutPusharyToolsAllowed,
|
|
597
|
+
pusharyToolsAllowed,
|
|
598
|
+
workspacesOverridingPusharyRule,
|
|
599
|
+
readFxFile,
|
|
600
|
+
fxIgnoresInstructionsFile,
|
|
344
601
|
registerPluginLocation,
|
|
345
602
|
unregisterPluginLocation,
|
|
346
603
|
isPluginRegistered,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// src/cli/env-switch.ts
|
|
2
|
+
var CLIENT_REPORT_DISABLE_ENV = "PUSHARY_DISABLE_CLIENT_REPORTS";
|
|
3
|
+
var NOT_DISABLED_VALUES = /* @__PURE__ */ new Set(["", "0"]);
|
|
4
|
+
var isDisabledByEnv = (env, name) => {
|
|
5
|
+
const value = env[name];
|
|
6
|
+
return value !== void 0 && !NOT_DISABLED_VALUES.has(value.trim());
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
CLIENT_REPORT_DISABLE_ENV,
|
|
11
|
+
isDisabledByEnv
|
|
12
|
+
};
|
|
@@ -2,17 +2,17 @@ import {
|
|
|
2
2
|
CLAUDE_HOOK_EVENTS,
|
|
3
3
|
GEMINI_HOOK_EVENTS,
|
|
4
4
|
hasGeminiHooks
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-ZQ7IDOKY.js";
|
|
6
6
|
import {
|
|
7
7
|
CODEX_HOOK_EVENTS,
|
|
8
8
|
hasCodexHooks,
|
|
9
9
|
holdBudgetOf
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-DNJUSFBL.js";
|
|
11
11
|
import {
|
|
12
12
|
CURSOR_GATE_EVENTS,
|
|
13
13
|
HOOK_LOCATOR_BINARY,
|
|
14
14
|
hookCommandHoldBudget
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-YROGUPOZ.js";
|
|
16
16
|
|
|
17
17
|
// src/diagnostics/wiring.ts
|
|
18
18
|
var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AGENT_CHOICES
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-CVALVGA7.js";
|
|
4
4
|
import {
|
|
5
5
|
isBridgeLocator
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-5HLTWFJ6.js";
|
|
7
7
|
import {
|
|
8
8
|
pusharyHookExecutable
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-PQ4F64M2.js";
|
|
10
10
|
import {
|
|
11
11
|
BELL_HOOK_BINARY
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-VKVIAZQO.js";
|
|
13
13
|
|
|
14
14
|
// src/setup/hook-ownership.ts
|
|
15
15
|
var commandsIn = (value) => {
|
|
@@ -1,3 +1,36 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NPX_LATEST_COMMAND
|
|
3
|
+
} from "./chunk-B3UXMSMW.js";
|
|
4
|
+
|
|
5
|
+
// src/setup/agent-failure.ts
|
|
6
|
+
var AgentConfigError = class extends Error {
|
|
7
|
+
kind;
|
|
8
|
+
filePath;
|
|
9
|
+
constructor(kind, filePath, message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "AgentConfigError";
|
|
12
|
+
this.kind = kind;
|
|
13
|
+
this.filePath = filePath;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var configFailureKind = (readFailure) => readFailure === "unparseable" ? "config-unparseable" : "config-unreadable";
|
|
17
|
+
var AGENT_FAILURE_DETAILS = {
|
|
18
|
+
"not-wired": "reported success but its config is not there.",
|
|
19
|
+
"app-account-mismatch": "Mac app hooks kept, but their Pushary account could not be matched to this setup."
|
|
20
|
+
};
|
|
21
|
+
var describedAgentFailure = (agent, kind) => ({
|
|
22
|
+
agent,
|
|
23
|
+
kind,
|
|
24
|
+
detail: AGENT_FAILURE_DETAILS[kind]
|
|
25
|
+
});
|
|
26
|
+
var agentFailureFromError = (agent, error, describeError) => ({
|
|
27
|
+
agent,
|
|
28
|
+
kind: error instanceof AgentConfigError ? error.kind : "installer-error",
|
|
29
|
+
detail: describeError(error)
|
|
30
|
+
});
|
|
31
|
+
var retryAgentCommand = (agent) => `${NPX_LATEST_COMMAND} setup --agents ${agent}`;
|
|
32
|
+
var agentFailureWire = (failures) => failures.map(({ agent, kind }) => ({ agent, kind }));
|
|
33
|
+
|
|
1
34
|
// src/diagnostics/service-runtime.ts
|
|
2
35
|
var unescapeXml = (value) => value.replaceAll(""", '"').replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">").replaceAll("&", "&");
|
|
3
36
|
var unquoteSystemd = (value) => value.replaceAll('\\"', '"').replaceAll("\\\\", "\\").replaceAll("%%", "%");
|
|
@@ -37,6 +70,12 @@ var versionManagerOf = (execPath) => {
|
|
|
37
70
|
var describeVersionManagerPin = (execPath, manager) => `The background service is pinned to ${execPath}, which ${manager} manages. Switching Node versions strands it; run pushary daemon install afterwards.`;
|
|
38
71
|
|
|
39
72
|
export {
|
|
73
|
+
AgentConfigError,
|
|
74
|
+
configFailureKind,
|
|
75
|
+
describedAgentFailure,
|
|
76
|
+
agentFailureFromError,
|
|
77
|
+
retryAgentCommand,
|
|
78
|
+
agentFailureWire,
|
|
40
79
|
readServiceExecPath,
|
|
41
80
|
versionManagerOf,
|
|
42
81
|
describeVersionManagerPin
|
|
@@ -3,11 +3,11 @@ import {
|
|
|
3
3
|
decodePublicKey,
|
|
4
4
|
generateSessionKey,
|
|
5
5
|
wrapSessionKey
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-BH2SKSSK.js";
|
|
7
7
|
import {
|
|
8
8
|
fetchTranscriptRecipients,
|
|
9
9
|
uploadTranscriptRecords
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-KXMZMZD3.js";
|
|
11
11
|
|
|
12
12
|
// src/live-session.ts
|
|
13
13
|
var MAX_TRANSCRIPT_QUEUE = 100;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
registerTranscript
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-EAQMWJPG.js";
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
} from "./chunk-
|
|
5
|
+
writeDegraded
|
|
6
|
+
} from "./chunk-V6QRSU2G.js";
|
|
7
7
|
|
|
8
8
|
// src/tool-hook-input.ts
|
|
9
9
|
var OPTIONAL_TEXT_FIELDS = ["hook_event_name", "session_id", "cwd", "transcript_path", "tool_use_id", "permission_mode"];
|
|
@@ -63,12 +63,11 @@ var matches = (rule, source, event) => {
|
|
|
63
63
|
var isAdmitted = (rules, source, event) => !rules.disabled.some((rule) => matches(rule, source, event));
|
|
64
64
|
var readHookInput = async (source, fallbackEvent = "", stdin = process.stdin, parseInput) => {
|
|
65
65
|
if (source === null) {
|
|
66
|
-
|
|
66
|
+
writeDegraded("hook", {
|
|
67
67
|
reason: "unknown_agent",
|
|
68
68
|
fallback: "stood aside, so nothing is gated or reported for this call",
|
|
69
69
|
event: fallbackEvent
|
|
70
|
-
})
|
|
71
|
-
`);
|
|
70
|
+
});
|
|
72
71
|
return null;
|
|
73
72
|
}
|
|
74
73
|
let raw = "";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
resolveInstallSourceFor
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ROQK7IN3.js";
|
|
4
4
|
import {
|
|
5
5
|
backupFile,
|
|
6
6
|
readJsonSafe,
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from "./chunk-T7E75EKA.js";
|
|
10
10
|
import {
|
|
11
11
|
configFilePath
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-V4AQBAVG.js";
|
|
13
13
|
|
|
14
14
|
// src/shell-alias.ts
|
|
15
15
|
import { homedir } from "os";
|