failproofai 0.0.15 → 1.0.0-beta.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/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +9 -9
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +9 -9
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +9 -9
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0id_xf7._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__19120tr._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +4 -4
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0114ewg._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__06b4trb._.js → [root-of-the-server]__0259h38._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02r5bgf._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e8sjqm._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lkzqax._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qucxyj._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u02miy._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__170799-._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ath6v_._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zj-87g._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zqz4v8._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules_html-to-image_es_index_0hs_5mh.js → node_modules_html-to-image_es_index_1-upb14.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1ezd2jf._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +11 -11
- package/.next/standalone/.next/static/chunks/{1_v8b8mxhk25s.js → 0ip1n8xmgxarv.js} +1 -1
- package/.next/standalone/.next/static/chunks/{33rm-y-i4uzvv.js → 0u9hc6oxx-gpj.js} +1 -1
- package/.next/standalone/.next/static/chunks/{11qhtpk2cvoxv.js → 1d6ncspjagm53.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2f4mxqkkoa_7d.js → 1grd7y94-igz9.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3m8wmvlhsy1mx.js → 1s-_swlc80y43.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0kf9j0pf_j9-0.js → 29k6ef49zppxj.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2h7vw-ojgr9fz.js → 29ydr-8q0zis6.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1-ng45379-ztb.js → 2vr7aazw6k2-n.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2geh8s7d3ead-.js → 3wrgbih-516uj.js} +1 -1
- package/.next/standalone/Cargo.lock +428 -0
- package/.next/standalone/Cargo.toml +9 -0
- package/.next/standalone/crates/PROTOCOL.md +122 -0
- package/.next/standalone/crates/failproofaid/Cargo.toml +18 -0
- package/.next/standalone/crates/failproofaid/src/lock.rs +81 -0
- package/.next/standalone/crates/failproofaid/src/main.rs +83 -0
- package/.next/standalone/crates/failproofaid/src/paths.rs +172 -0
- package/.next/standalone/crates/failproofaid/src/server.rs +502 -0
- package/.next/standalone/crates/failproofaid/src/worker.rs +400 -0
- package/.next/standalone/crates/failproofaid/tests/daemon_e2e.rs +241 -0
- package/.next/standalone/crates/fpai-ipc/Cargo.toml +16 -0
- package/.next/standalone/crates/fpai-ipc/src/envelope.rs +175 -0
- package/.next/standalone/crates/fpai-ipc/src/framing.rs +177 -0
- package/.next/standalone/crates/fpai-ipc/src/lib.rs +10 -0
- package/.next/standalone/crates/fpai-ipc/src/peer.rs +80 -0
- package/.next/standalone/package.json +5 -3
- package/.next/standalone/rust-toolchain.toml +4 -0
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai-worker.mjs +51 -0
- package/bin/failproofai.mjs +68 -1
- package/bin/failproofaid-shim.mjs +67 -0
- package/dist/cli.mjs +1202 -624
- package/dist/worker.mjs +8441 -0
- package/package.json +5 -3
- package/src/hooks/builtin-policies.ts +37 -13
- package/src/hooks/configure-wizard.ts +73 -1
- package/src/hooks/daemon-client.ts +199 -0
- package/src/hooks/daemon-download.ts +152 -0
- package/src/hooks/daemon-service.ts +449 -0
- package/src/hooks/handler.ts +231 -195
- package/src/hooks/normalize-cli-payload.ts +61 -0
- package/src/hooks/policy-types.ts +15 -0
- package/src/hooks/read-stdin.ts +48 -0
- package/src/hooks/worker-server.ts +154 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1is3glr._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0fopait._.js +0 -3
- /package/.next/standalone/.next/static/{ePKMmXXfXxThNsxwv1EiF → 7Lxtd352rSaHB-pmm6G8M}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{ePKMmXXfXxThNsxwv1EiF → 7Lxtd352rSaHB-pmm6G8M}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{ePKMmXXfXxThNsxwv1EiF → 7Lxtd352rSaHB-pmm6G8M}/_ssgManifest.js +0 -0
package/dist/cli.mjs
CHANGED
|
@@ -19,7 +19,7 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
|
19
19
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
20
20
|
|
|
21
21
|
// package.json
|
|
22
|
-
var version2 = "0.0.
|
|
22
|
+
var version2 = "1.0.0-beta.0";
|
|
23
23
|
var init_package = () => {};
|
|
24
24
|
|
|
25
25
|
// src/posthog-key.ts
|
|
@@ -155,6 +155,383 @@ var init_telemetry_id = __esm(() => {
|
|
|
155
155
|
ID_FILE = path.join(ID_DIR, "instance-id");
|
|
156
156
|
});
|
|
157
157
|
|
|
158
|
+
// src/hooks/hook-logger.ts
|
|
159
|
+
import {
|
|
160
|
+
appendFileSync,
|
|
161
|
+
renameSync,
|
|
162
|
+
mkdirSync,
|
|
163
|
+
existsSync,
|
|
164
|
+
statSync
|
|
165
|
+
} from "node:fs";
|
|
166
|
+
import { join } from "node:path";
|
|
167
|
+
import { homedir } from "node:os";
|
|
168
|
+
function ensureResolved() {
|
|
169
|
+
if (resolved)
|
|
170
|
+
return;
|
|
171
|
+
resolved = true;
|
|
172
|
+
const rawLevel = (process.env.FAILPROOFAI_LOG_LEVEL ?? "").toLowerCase();
|
|
173
|
+
if (rawLevel === "info" || rawLevel === "warn" || rawLevel === "error") {
|
|
174
|
+
currentLevel = rawLevel;
|
|
175
|
+
}
|
|
176
|
+
const rawFile = (process.env.FAILPROOFAI_HOOK_LOG_FILE ?? "").trim();
|
|
177
|
+
if (rawFile) {
|
|
178
|
+
fileLoggingEnabled = true;
|
|
179
|
+
if (rawFile !== "1" && rawFile !== "true") {
|
|
180
|
+
logDir = rawFile;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function shouldEmit(level) {
|
|
185
|
+
ensureResolved();
|
|
186
|
+
return LEVEL_ORDER[level] >= LEVEL_ORDER[currentLevel];
|
|
187
|
+
}
|
|
188
|
+
function emitStderr(label, msg) {
|
|
189
|
+
process.stderr.write(`[failproofai:hook] ${label} ${msg}
|
|
190
|
+
`);
|
|
191
|
+
}
|
|
192
|
+
function ensureLogDir() {
|
|
193
|
+
if (!existsSync(logDir)) {
|
|
194
|
+
mkdirSync(logDir, { recursive: true });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function rotateIfNeeded(filePath) {
|
|
198
|
+
try {
|
|
199
|
+
const stats = statSync(filePath);
|
|
200
|
+
if (stats.size >= MAX_FILE_SIZE) {
|
|
201
|
+
const archiveName = `hooks-${Date.now()}.log`;
|
|
202
|
+
renameSync(filePath, join(logDir, archiveName));
|
|
203
|
+
}
|
|
204
|
+
} catch {}
|
|
205
|
+
}
|
|
206
|
+
function appendToFile(label, msg) {
|
|
207
|
+
if (!fileLoggingEnabled)
|
|
208
|
+
return;
|
|
209
|
+
try {
|
|
210
|
+
ensureLogDir();
|
|
211
|
+
const filePath = join(logDir, LOG_FILENAME);
|
|
212
|
+
rotateIfNeeded(filePath);
|
|
213
|
+
const timestamp = new Date().toISOString();
|
|
214
|
+
const line = `[${timestamp}] ${label} ${msg}
|
|
215
|
+
`;
|
|
216
|
+
appendFileSync(filePath, line, "utf-8");
|
|
217
|
+
} catch {}
|
|
218
|
+
}
|
|
219
|
+
function hookLogInfo(msg) {
|
|
220
|
+
if (!shouldEmit("info"))
|
|
221
|
+
return;
|
|
222
|
+
emitStderr("INFO", msg);
|
|
223
|
+
appendToFile("INFO", msg);
|
|
224
|
+
}
|
|
225
|
+
function hookLogWarn(msg) {
|
|
226
|
+
if (!shouldEmit("warn"))
|
|
227
|
+
return;
|
|
228
|
+
emitStderr("WARN", msg);
|
|
229
|
+
appendToFile("WARN", msg);
|
|
230
|
+
}
|
|
231
|
+
function hookLogError(msg) {
|
|
232
|
+
if (!shouldEmit("error"))
|
|
233
|
+
return;
|
|
234
|
+
emitStderr("ERROR", msg);
|
|
235
|
+
appendToFile("ERROR", msg);
|
|
236
|
+
}
|
|
237
|
+
var LEVEL_ORDER, MAX_FILE_SIZE, LOG_FILENAME = "hooks.log", DEFAULT_LOG_DIR, resolved = false, currentLevel = "warn", fileLoggingEnabled = false, logDir;
|
|
238
|
+
var init_hook_logger = __esm(() => {
|
|
239
|
+
LEVEL_ORDER = { info: 0, warn: 1, error: 2 };
|
|
240
|
+
MAX_FILE_SIZE = 512 * 1024;
|
|
241
|
+
DEFAULT_LOG_DIR = join(homedir(), ".failproofai", "logs");
|
|
242
|
+
logDir = DEFAULT_LOG_DIR;
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// src/hooks/hooks-config.ts
|
|
246
|
+
import { readFileSync, writeFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, statSync as statSync2 } from "node:fs";
|
|
247
|
+
import { resolve, dirname } from "node:path";
|
|
248
|
+
import { homedir as homedir2 } from "node:os";
|
|
249
|
+
function configuredCustomPolicyPaths(config) {
|
|
250
|
+
return config.customPoliciesPaths ?? (config.customPoliciesPath ? [config.customPoliciesPath] : []);
|
|
251
|
+
}
|
|
252
|
+
function readConfigAt(path2) {
|
|
253
|
+
if (!existsSync2(path2))
|
|
254
|
+
return {};
|
|
255
|
+
try {
|
|
256
|
+
const raw = readFileSync(path2, "utf8");
|
|
257
|
+
return JSON.parse(raw);
|
|
258
|
+
} catch (err) {
|
|
259
|
+
hookLogWarn(`failed to parse config at ${path2}: ${err instanceof Error ? err.message : String(err)}`);
|
|
260
|
+
return {};
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function findProjectConfigDir(start) {
|
|
264
|
+
const home = homedir2();
|
|
265
|
+
let dir = resolve(start);
|
|
266
|
+
while (dir !== home) {
|
|
267
|
+
const marker = resolve(dir, ".failproofai");
|
|
268
|
+
try {
|
|
269
|
+
if (statSync2(marker).isDirectory())
|
|
270
|
+
return dir;
|
|
271
|
+
} catch {}
|
|
272
|
+
const parent = dirname(dir);
|
|
273
|
+
if (parent === dir)
|
|
274
|
+
break;
|
|
275
|
+
dir = parent;
|
|
276
|
+
}
|
|
277
|
+
return resolve(start);
|
|
278
|
+
}
|
|
279
|
+
function readMergedHooksConfig(cwd) {
|
|
280
|
+
const base = findProjectConfigDir(cwd ?? process.cwd());
|
|
281
|
+
const projectPath = resolve(base, ".failproofai", "policies-config.json");
|
|
282
|
+
const localPath = resolve(base, ".failproofai", "policies-config.local.json");
|
|
283
|
+
const globalPath = resolve(homedir2(), ".failproofai", "policies-config.json");
|
|
284
|
+
const project = readConfigAt(projectPath);
|
|
285
|
+
const local = readConfigAt(localPath);
|
|
286
|
+
const global_ = readConfigAt(globalPath);
|
|
287
|
+
const enabledSet = new Set([
|
|
288
|
+
...project.enabledPolicies ?? [],
|
|
289
|
+
...local.enabledPolicies ?? [],
|
|
290
|
+
...global_.enabledPolicies ?? []
|
|
291
|
+
]);
|
|
292
|
+
const mergedParams = {};
|
|
293
|
+
for (const scope of [project, local, global_]) {
|
|
294
|
+
if (!scope.policyParams)
|
|
295
|
+
continue;
|
|
296
|
+
for (const [policyName, params] of Object.entries(scope.policyParams)) {
|
|
297
|
+
if (!(policyName in mergedParams)) {
|
|
298
|
+
mergedParams[policyName] = params;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
const customPoliciesPaths = [project, local, global_].map((scope) => scope.customPoliciesPaths !== undefined ? scope.customPoliciesPaths : scope.customPoliciesPath !== undefined ? [scope.customPoliciesPath] : undefined).find((paths) => paths !== undefined);
|
|
303
|
+
const disabledCustomPolicies = new Set([
|
|
304
|
+
...project.disabledCustomPolicies ?? [],
|
|
305
|
+
...local.disabledCustomPolicies ?? [],
|
|
306
|
+
...global_.disabledCustomPolicies ?? []
|
|
307
|
+
]);
|
|
308
|
+
const customPoliciesEnabled = project.customPoliciesEnabled ?? local.customPoliciesEnabled ?? global_.customPoliciesEnabled;
|
|
309
|
+
const llm = project.llm ?? local.llm ?? global_.llm;
|
|
310
|
+
return {
|
|
311
|
+
enabledPolicies: [...enabledSet],
|
|
312
|
+
...Object.keys(mergedParams).length > 0 ? { policyParams: mergedParams } : {},
|
|
313
|
+
...customPoliciesPaths !== undefined ? { customPoliciesPaths } : {},
|
|
314
|
+
...disabledCustomPolicies.size ? { disabledCustomPolicies: [...disabledCustomPolicies] } : {},
|
|
315
|
+
...customPoliciesEnabled !== undefined ? { customPoliciesEnabled } : {},
|
|
316
|
+
...llm !== undefined ? { llm } : {}
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
function getConfigPath() {
|
|
320
|
+
return resolve(homedir2(), ".failproofai", "policies-config.json");
|
|
321
|
+
}
|
|
322
|
+
function readHooksConfig() {
|
|
323
|
+
const configPath = getConfigPath();
|
|
324
|
+
if (!existsSync2(configPath)) {
|
|
325
|
+
return { enabledPolicies: [] };
|
|
326
|
+
}
|
|
327
|
+
try {
|
|
328
|
+
const raw = readFileSync(configPath, "utf8");
|
|
329
|
+
return JSON.parse(raw);
|
|
330
|
+
} catch (err) {
|
|
331
|
+
hookLogWarn(`failed to parse config at ${configPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
332
|
+
return { enabledPolicies: [] };
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
function getConfigPathForScope(scope, cwd) {
|
|
336
|
+
switch (scope) {
|
|
337
|
+
case "user":
|
|
338
|
+
return resolve(homedir2(), ".failproofai", "policies-config.json");
|
|
339
|
+
case "project":
|
|
340
|
+
return resolve(findProjectConfigDir(cwd ?? process.cwd()), ".failproofai", "policies-config.json");
|
|
341
|
+
case "local":
|
|
342
|
+
return resolve(findProjectConfigDir(cwd ?? process.cwd()), ".failproofai", "policies-config.local.json");
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
function readScopedHooksConfig(scope, cwd) {
|
|
346
|
+
const configPath = getConfigPathForScope(scope, cwd);
|
|
347
|
+
if (!existsSync2(configPath)) {
|
|
348
|
+
return { enabledPolicies: [] };
|
|
349
|
+
}
|
|
350
|
+
try {
|
|
351
|
+
const raw = readFileSync(configPath, "utf8");
|
|
352
|
+
return JSON.parse(raw);
|
|
353
|
+
} catch (err) {
|
|
354
|
+
hookLogWarn(`failed to parse config at ${configPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
355
|
+
return { enabledPolicies: [] };
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
function writeScopedHooksConfig(config, scope, cwd) {
|
|
359
|
+
const configPath = getConfigPathForScope(scope, cwd);
|
|
360
|
+
const dir = dirname(configPath);
|
|
361
|
+
if (!existsSync2(dir)) {
|
|
362
|
+
mkdirSync2(dir, { recursive: true });
|
|
363
|
+
}
|
|
364
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + `
|
|
365
|
+
`, "utf8");
|
|
366
|
+
}
|
|
367
|
+
function syncConventionPolicies(entries, scope, cwd) {
|
|
368
|
+
const configPath = getConfigPathForScope(scope, cwd);
|
|
369
|
+
if (!existsSync2(configPath) && entries.length === 0)
|
|
370
|
+
return false;
|
|
371
|
+
if (existsSync2(configPath)) {
|
|
372
|
+
try {
|
|
373
|
+
JSON.parse(readFileSync(configPath, "utf8"));
|
|
374
|
+
} catch {
|
|
375
|
+
hookLogWarn(`not recording convention policies: ${configPath} is not valid JSON — ` + `fix the syntax error and re-run. Nothing was written.`);
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
const config = readScopedHooksConfig(scope, cwd);
|
|
380
|
+
const sorted = [...entries].sort((a, b) => a.file.localeCompare(b.file));
|
|
381
|
+
const next = { ...config };
|
|
382
|
+
if (sorted.length === 0)
|
|
383
|
+
delete next.conventionPolicies;
|
|
384
|
+
else
|
|
385
|
+
next.conventionPolicies = sorted;
|
|
386
|
+
if (JSON.stringify(next.conventionPolicies ?? null) === JSON.stringify(config.conventionPolicies ?? null)) {
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
389
|
+
writeScopedHooksConfig(next, scope, cwd);
|
|
390
|
+
return true;
|
|
391
|
+
}
|
|
392
|
+
var init_hooks_config = __esm(() => {
|
|
393
|
+
init_hook_logger();
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
// src/hooks/daemon-client.ts
|
|
397
|
+
var exports_daemon_client = {};
|
|
398
|
+
__export(exports_daemon_client, {
|
|
399
|
+
tryDaemonHook: () => tryDaemonHook,
|
|
400
|
+
isDaemonConfigured: () => isDaemonConfigured
|
|
401
|
+
});
|
|
402
|
+
import { createConnection } from "node:net";
|
|
403
|
+
import { resolve as resolve2 } from "node:path";
|
|
404
|
+
import { homedir as homedir3 } from "node:os";
|
|
405
|
+
function socketPath() {
|
|
406
|
+
if (process.env.FAILPROOFAI_DAEMON_SOCKET)
|
|
407
|
+
return process.env.FAILPROOFAI_DAEMON_SOCKET;
|
|
408
|
+
return resolve2(homedir3(), ".failproofai", "run", "failproofaid.sock");
|
|
409
|
+
}
|
|
410
|
+
function isDaemonConfigured() {
|
|
411
|
+
try {
|
|
412
|
+
return readHooksConfig().daemonConfigured === true;
|
|
413
|
+
} catch {
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
function encodeFrame(value) {
|
|
418
|
+
const body = Buffer.from(JSON.stringify(value), "utf8");
|
|
419
|
+
const header = Buffer.alloc(4);
|
|
420
|
+
header.writeUInt32BE(body.length, 0);
|
|
421
|
+
return Buffer.concat([header, body]);
|
|
422
|
+
}
|
|
423
|
+
async function tryDaemonHook(req) {
|
|
424
|
+
if (process.platform === "win32")
|
|
425
|
+
return null;
|
|
426
|
+
return new Promise((resolvePromise) => {
|
|
427
|
+
let settled = false;
|
|
428
|
+
let timer;
|
|
429
|
+
const arm = (ms) => {
|
|
430
|
+
clearTimeout(timer);
|
|
431
|
+
timer = setTimeout(() => finish(null), ms);
|
|
432
|
+
timer.unref?.();
|
|
433
|
+
};
|
|
434
|
+
const finish = (result) => {
|
|
435
|
+
if (settled)
|
|
436
|
+
return;
|
|
437
|
+
settled = true;
|
|
438
|
+
clearTimeout(timer);
|
|
439
|
+
socket.removeAllListeners();
|
|
440
|
+
socket.destroy();
|
|
441
|
+
resolvePromise(result);
|
|
442
|
+
};
|
|
443
|
+
const socket = createConnection({ path: socketPath() });
|
|
444
|
+
arm(DAEMON_CONNECT_TIMEOUT_MS);
|
|
445
|
+
let recvBuf = Buffer.alloc(0);
|
|
446
|
+
let declaredLen = null;
|
|
447
|
+
socket.on("connect", () => {
|
|
448
|
+
arm(DAEMON_RESPONSE_TIMEOUT_MS);
|
|
449
|
+
socket.write(encodeFrame({
|
|
450
|
+
type: "hook",
|
|
451
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
452
|
+
hookEvent: req.hookEvent,
|
|
453
|
+
cli: req.cli,
|
|
454
|
+
stdin: req.stdin,
|
|
455
|
+
cwd: req.cwd
|
|
456
|
+
}));
|
|
457
|
+
});
|
|
458
|
+
socket.on("data", (chunk) => {
|
|
459
|
+
recvBuf = Buffer.concat([recvBuf, chunk]);
|
|
460
|
+
if (declaredLen === null) {
|
|
461
|
+
if (recvBuf.length < 4)
|
|
462
|
+
return;
|
|
463
|
+
declaredLen = recvBuf.readUInt32BE(0);
|
|
464
|
+
if (declaredLen > MAX_FRAME_LEN) {
|
|
465
|
+
finish(null);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
recvBuf = recvBuf.subarray(4);
|
|
469
|
+
}
|
|
470
|
+
if (recvBuf.length < declaredLen)
|
|
471
|
+
return;
|
|
472
|
+
const body = recvBuf.subarray(0, declaredLen);
|
|
473
|
+
let message;
|
|
474
|
+
try {
|
|
475
|
+
message = JSON.parse(body.toString("utf8"));
|
|
476
|
+
} catch {
|
|
477
|
+
finish(null);
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
if (message.protocolVersion !== PROTOCOL_VERSION) {
|
|
481
|
+
finish(null);
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
if (message.type === "hookResult" && typeof message.exitCode === "number" && typeof message.stdout === "string" && typeof message.stderr === "string") {
|
|
485
|
+
finish({ exitCode: message.exitCode, stdout: message.stdout, stderr: message.stderr });
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
finish(null);
|
|
489
|
+
});
|
|
490
|
+
socket.on("error", () => finish(null));
|
|
491
|
+
socket.on("close", () => finish(null));
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
var PROTOCOL_VERSION = 1, DAEMON_CONNECT_TIMEOUT_MS = 150, DAEMON_RESPONSE_TIMEOUT_MS = 30000, MAX_FRAME_LEN;
|
|
495
|
+
var init_daemon_client = __esm(() => {
|
|
496
|
+
init_hooks_config();
|
|
497
|
+
MAX_FRAME_LEN = 16 * 1024 * 1024;
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
// src/hooks/read-stdin.ts
|
|
501
|
+
var exports_read_stdin = {};
|
|
502
|
+
__export(exports_read_stdin, {
|
|
503
|
+
readStdinPayload: () => readStdinPayload
|
|
504
|
+
});
|
|
505
|
+
async function readStdinPayload(maxBytes = 1048576) {
|
|
506
|
+
let payload = "";
|
|
507
|
+
let oversized = false;
|
|
508
|
+
let readError = false;
|
|
509
|
+
try {
|
|
510
|
+
payload = await new Promise((resolve3, reject) => {
|
|
511
|
+
const chunks = [];
|
|
512
|
+
let totalBytes = 0;
|
|
513
|
+
process.stdin.setEncoding("utf8");
|
|
514
|
+
process.stdin.on("data", (chunk) => {
|
|
515
|
+
totalBytes += Buffer.byteLength(chunk);
|
|
516
|
+
if (totalBytes > maxBytes) {
|
|
517
|
+
oversized = true;
|
|
518
|
+
process.stdin.destroy();
|
|
519
|
+
resolve3("");
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
chunks.push(chunk);
|
|
523
|
+
});
|
|
524
|
+
process.stdin.on("end", () => resolve3(chunks.join("")));
|
|
525
|
+
process.stdin.on("error", reject);
|
|
526
|
+
if (process.stdin.readableEnded)
|
|
527
|
+
resolve3("");
|
|
528
|
+
});
|
|
529
|
+
} catch {
|
|
530
|
+
readError = true;
|
|
531
|
+
}
|
|
532
|
+
return { payload, oversized, readError };
|
|
533
|
+
}
|
|
534
|
+
|
|
158
535
|
// src/hooks/types.ts
|
|
159
536
|
var HOOK_SCOPES, INTEGRATION_TYPES, CODEX_HOOK_SCOPES, CODEX_HOOK_EVENT_TYPES, CODEX_EVENT_MAP, CODEX_TOOL_MAP, HERMES_TOOL_MAP, HERMES_TOOL_INPUT_MAP, HERMES_HOOK_SCOPES, HERMES_HOOK_EVENT_TYPES, HERMES_EVENT_MAP, COPILOT_HOOK_SCOPES, COPILOT_HOOK_EVENT_TYPES, COPILOT_TOOL_MAP, COPILOT_TOOL_INPUT_MAP, CURSOR_HOOK_SCOPES, CURSOR_HOOK_EVENT_TYPES, CURSOR_EVENT_MAP, CURSOR_TOOL_MAP, OPENCODE_HOOK_SCOPES, OPENCODE_HOOK_EVENT_TYPES, OPENCODE_TOOL_MAP, OPENCODE_TOOL_INPUT_MAP, PI_HOOK_SCOPES, PI_HOOK_EVENT_TYPES, PI_EVENT_MAP, PI_TOOL_MAP, PI_TOOL_INPUT_MAP, OPENCLAW_HOOK_SCOPES, OPENCLAW_HOOK_EVENT_TYPES, OPENCLAW_EVENT_MAP, OPENCLAW_TOOL_MAP, OPENCLAW_TOOL_INPUT_MAP, FACTORY_HOOK_SCOPES, FACTORY_HOOK_EVENT_TYPES, FACTORY_TOOL_MAP, DEVIN_HOOK_SCOPES, DEVIN_HOOK_EVENT_TYPES, DEVIN_TOOL_MAP, ANTIGRAVITY_HOOK_SCOPES, ANTIGRAVITY_HOOK_EVENT_TYPES, ANTIGRAVITY_EVENT_MAP, ANTIGRAVITY_TOOL_MAP, ANTIGRAVITY_TOOL_INPUT_MAP, GOOSE_HOOK_SCOPES, GOOSE_HOOK_EVENT_TYPES, GOOSE_TOOL_MAP, GOOSE_TOOL_INPUT_MAP, HOOK_EVENT_TYPES, CLAUDE_INSTALL_EVENT_TYPES, FAILPROOFAI_HOOK_MARKER = "__failproofai_hook__";
|
|
160
537
|
var init_types = __esm(() => {
|
|
@@ -578,243 +955,43 @@ var init_tool_name_canonicalize = __esm(() => {
|
|
|
578
955
|
init_types();
|
|
579
956
|
});
|
|
580
957
|
|
|
581
|
-
// src/hooks/
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
import { homedir } from "node:os";
|
|
591
|
-
function ensureResolved() {
|
|
592
|
-
if (resolved)
|
|
593
|
-
return;
|
|
594
|
-
resolved = true;
|
|
595
|
-
const rawLevel = (process.env.FAILPROOFAI_LOG_LEVEL ?? "").toLowerCase();
|
|
596
|
-
if (rawLevel === "info" || rawLevel === "warn" || rawLevel === "error") {
|
|
597
|
-
currentLevel = rawLevel;
|
|
598
|
-
}
|
|
599
|
-
const rawFile = (process.env.FAILPROOFAI_HOOK_LOG_FILE ?? "").trim();
|
|
600
|
-
if (rawFile) {
|
|
601
|
-
fileLoggingEnabled = true;
|
|
602
|
-
if (rawFile !== "1" && rawFile !== "true") {
|
|
603
|
-
logDir = rawFile;
|
|
958
|
+
// src/hooks/normalize-cli-payload.ts
|
|
959
|
+
function normalizeCliPayload(cli, parsed) {
|
|
960
|
+
if (cli === "antigravity") {
|
|
961
|
+
const tc = parsed.toolCall;
|
|
962
|
+
if (tc && typeof tc === "object") {
|
|
963
|
+
if (tc.name !== undefined)
|
|
964
|
+
parsed.tool_name = tc.name;
|
|
965
|
+
if (tc.args !== undefined)
|
|
966
|
+
parsed.tool_input = tc.args;
|
|
604
967
|
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
return LEVEL_ORDER[level] >= LEVEL_ORDER[currentLevel];
|
|
610
|
-
}
|
|
611
|
-
function emitStderr(label, msg) {
|
|
612
|
-
process.stderr.write(`[failproofai:hook] ${label} ${msg}
|
|
613
|
-
`);
|
|
614
|
-
}
|
|
615
|
-
function ensureLogDir() {
|
|
616
|
-
if (!existsSync(logDir)) {
|
|
617
|
-
mkdirSync(logDir, { recursive: true });
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
function rotateIfNeeded(filePath) {
|
|
621
|
-
try {
|
|
622
|
-
const stats = statSync(filePath);
|
|
623
|
-
if (stats.size >= MAX_FILE_SIZE) {
|
|
624
|
-
const archiveName = `hooks-${Date.now()}.log`;
|
|
625
|
-
renameSync(filePath, join(logDir, archiveName));
|
|
968
|
+
if (typeof parsed.conversationId === "string")
|
|
969
|
+
parsed.session_id = parsed.conversationId;
|
|
970
|
+
if (Array.isArray(parsed.workspacePaths) && typeof parsed.workspacePaths[0] === "string") {
|
|
971
|
+
parsed.cwd = parsed.workspacePaths[0];
|
|
626
972
|
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
function appendToFile(label, msg) {
|
|
630
|
-
if (!fileLoggingEnabled)
|
|
631
|
-
return;
|
|
632
|
-
try {
|
|
633
|
-
ensureLogDir();
|
|
634
|
-
const filePath = join(logDir, LOG_FILENAME);
|
|
635
|
-
rotateIfNeeded(filePath);
|
|
636
|
-
const timestamp = new Date().toISOString();
|
|
637
|
-
const line = `[${timestamp}] ${label} ${msg}
|
|
638
|
-
`;
|
|
639
|
-
appendFileSync(filePath, line, "utf-8");
|
|
640
|
-
} catch {}
|
|
641
|
-
}
|
|
642
|
-
function hookLogInfo(msg) {
|
|
643
|
-
if (!shouldEmit("info"))
|
|
644
|
-
return;
|
|
645
|
-
emitStderr("INFO", msg);
|
|
646
|
-
appendToFile("INFO", msg);
|
|
647
|
-
}
|
|
648
|
-
function hookLogWarn(msg) {
|
|
649
|
-
if (!shouldEmit("warn"))
|
|
650
|
-
return;
|
|
651
|
-
emitStderr("WARN", msg);
|
|
652
|
-
appendToFile("WARN", msg);
|
|
653
|
-
}
|
|
654
|
-
function hookLogError(msg) {
|
|
655
|
-
if (!shouldEmit("error"))
|
|
656
|
-
return;
|
|
657
|
-
emitStderr("ERROR", msg);
|
|
658
|
-
appendToFile("ERROR", msg);
|
|
659
|
-
}
|
|
660
|
-
var LEVEL_ORDER, MAX_FILE_SIZE, LOG_FILENAME = "hooks.log", DEFAULT_LOG_DIR, resolved = false, currentLevel = "warn", fileLoggingEnabled = false, logDir;
|
|
661
|
-
var init_hook_logger = __esm(() => {
|
|
662
|
-
LEVEL_ORDER = { info: 0, warn: 1, error: 2 };
|
|
663
|
-
MAX_FILE_SIZE = 512 * 1024;
|
|
664
|
-
DEFAULT_LOG_DIR = join(homedir(), ".failproofai", "logs");
|
|
665
|
-
logDir = DEFAULT_LOG_DIR;
|
|
666
|
-
});
|
|
667
|
-
|
|
668
|
-
// src/hooks/hooks-config.ts
|
|
669
|
-
import { readFileSync, writeFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, statSync as statSync2 } from "node:fs";
|
|
670
|
-
import { resolve, dirname } from "node:path";
|
|
671
|
-
import { homedir as homedir2 } from "node:os";
|
|
672
|
-
function configuredCustomPolicyPaths(config) {
|
|
673
|
-
return config.customPoliciesPaths ?? (config.customPoliciesPath ? [config.customPoliciesPath] : []);
|
|
674
|
-
}
|
|
675
|
-
function readConfigAt(path2) {
|
|
676
|
-
if (!existsSync2(path2))
|
|
677
|
-
return {};
|
|
678
|
-
try {
|
|
679
|
-
const raw = readFileSync(path2, "utf8");
|
|
680
|
-
return JSON.parse(raw);
|
|
681
|
-
} catch (err) {
|
|
682
|
-
hookLogWarn(`failed to parse config at ${path2}: ${err instanceof Error ? err.message : String(err)}`);
|
|
683
|
-
return {};
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
function findProjectConfigDir(start) {
|
|
687
|
-
const home = homedir2();
|
|
688
|
-
let dir = resolve(start);
|
|
689
|
-
while (dir !== home) {
|
|
690
|
-
const marker = resolve(dir, ".failproofai");
|
|
691
|
-
try {
|
|
692
|
-
if (statSync2(marker).isDirectory())
|
|
693
|
-
return dir;
|
|
694
|
-
} catch {}
|
|
695
|
-
const parent = dirname(dir);
|
|
696
|
-
if (parent === dir)
|
|
697
|
-
break;
|
|
698
|
-
dir = parent;
|
|
973
|
+
if (typeof parsed.transcriptPath === "string")
|
|
974
|
+
parsed.transcript_path = parsed.transcriptPath;
|
|
699
975
|
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
const base = findProjectConfigDir(cwd ?? process.cwd());
|
|
704
|
-
const projectPath = resolve(base, ".failproofai", "policies-config.json");
|
|
705
|
-
const localPath = resolve(base, ".failproofai", "policies-config.local.json");
|
|
706
|
-
const globalPath = resolve(homedir2(), ".failproofai", "policies-config.json");
|
|
707
|
-
const project = readConfigAt(projectPath);
|
|
708
|
-
const local = readConfigAt(localPath);
|
|
709
|
-
const global_ = readConfigAt(globalPath);
|
|
710
|
-
const enabledSet = new Set([
|
|
711
|
-
...project.enabledPolicies ?? [],
|
|
712
|
-
...local.enabledPolicies ?? [],
|
|
713
|
-
...global_.enabledPolicies ?? []
|
|
714
|
-
]);
|
|
715
|
-
const mergedParams = {};
|
|
716
|
-
for (const scope of [project, local, global_]) {
|
|
717
|
-
if (!scope.policyParams)
|
|
718
|
-
continue;
|
|
719
|
-
for (const [policyName, params] of Object.entries(scope.policyParams)) {
|
|
720
|
-
if (!(policyName in mergedParams)) {
|
|
721
|
-
mergedParams[policyName] = params;
|
|
722
|
-
}
|
|
976
|
+
if (cli === "copilot") {
|
|
977
|
+
if (typeof parsed.toolName === "string" && parsed.tool_name === undefined) {
|
|
978
|
+
parsed.tool_name = parsed.toolName;
|
|
723
979
|
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
...customPoliciesPaths !== undefined ? { customPoliciesPaths } : {},
|
|
737
|
-
...disabledCustomPolicies.size ? { disabledCustomPolicies: [...disabledCustomPolicies] } : {},
|
|
738
|
-
...customPoliciesEnabled !== undefined ? { customPoliciesEnabled } : {},
|
|
739
|
-
...llm !== undefined ? { llm } : {}
|
|
740
|
-
};
|
|
741
|
-
}
|
|
742
|
-
function getConfigPath() {
|
|
743
|
-
return resolve(homedir2(), ".failproofai", "policies-config.json");
|
|
744
|
-
}
|
|
745
|
-
function readHooksConfig() {
|
|
746
|
-
const configPath = getConfigPath();
|
|
747
|
-
if (!existsSync2(configPath)) {
|
|
748
|
-
return { enabledPolicies: [] };
|
|
749
|
-
}
|
|
750
|
-
try {
|
|
751
|
-
const raw = readFileSync(configPath, "utf8");
|
|
752
|
-
return JSON.parse(raw);
|
|
753
|
-
} catch (err) {
|
|
754
|
-
hookLogWarn(`failed to parse config at ${configPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
755
|
-
return { enabledPolicies: [] };
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
function getConfigPathForScope(scope, cwd) {
|
|
759
|
-
switch (scope) {
|
|
760
|
-
case "user":
|
|
761
|
-
return resolve(homedir2(), ".failproofai", "policies-config.json");
|
|
762
|
-
case "project":
|
|
763
|
-
return resolve(findProjectConfigDir(cwd ?? process.cwd()), ".failproofai", "policies-config.json");
|
|
764
|
-
case "local":
|
|
765
|
-
return resolve(findProjectConfigDir(cwd ?? process.cwd()), ".failproofai", "policies-config.local.json");
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
function readScopedHooksConfig(scope, cwd) {
|
|
769
|
-
const configPath = getConfigPathForScope(scope, cwd);
|
|
770
|
-
if (!existsSync2(configPath)) {
|
|
771
|
-
return { enabledPolicies: [] };
|
|
772
|
-
}
|
|
773
|
-
try {
|
|
774
|
-
const raw = readFileSync(configPath, "utf8");
|
|
775
|
-
return JSON.parse(raw);
|
|
776
|
-
} catch (err) {
|
|
777
|
-
hookLogWarn(`failed to parse config at ${configPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
778
|
-
return { enabledPolicies: [] };
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
function writeScopedHooksConfig(config, scope, cwd) {
|
|
782
|
-
const configPath = getConfigPathForScope(scope, cwd);
|
|
783
|
-
const dir = dirname(configPath);
|
|
784
|
-
if (!existsSync2(dir)) {
|
|
785
|
-
mkdirSync2(dir, { recursive: true });
|
|
786
|
-
}
|
|
787
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2) + `
|
|
788
|
-
`, "utf8");
|
|
789
|
-
}
|
|
790
|
-
function syncConventionPolicies(entries, scope, cwd) {
|
|
791
|
-
const configPath = getConfigPathForScope(scope, cwd);
|
|
792
|
-
if (!existsSync2(configPath) && entries.length === 0)
|
|
793
|
-
return false;
|
|
794
|
-
if (existsSync2(configPath)) {
|
|
795
|
-
try {
|
|
796
|
-
JSON.parse(readFileSync(configPath, "utf8"));
|
|
797
|
-
} catch {
|
|
798
|
-
hookLogWarn(`not recording convention policies: ${configPath} is not valid JSON — ` + `fix the syntax error and re-run. Nothing was written.`);
|
|
799
|
-
return false;
|
|
980
|
+
if (parsed.toolInput !== undefined && parsed.tool_input === undefined) {
|
|
981
|
+
parsed.tool_input = parsed.toolInput;
|
|
982
|
+
}
|
|
983
|
+
if (typeof parsed.sessionId === "string" && parsed.session_id === undefined) {
|
|
984
|
+
parsed.session_id = parsed.sessionId;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
if (cli === "goose") {
|
|
988
|
+
if (typeof parsed.working_dir === "string")
|
|
989
|
+
parsed.cwd = parsed.working_dir;
|
|
990
|
+
if (typeof parsed.event === "string" && parsed.hook_event_name === undefined) {
|
|
991
|
+
parsed.hook_event_name = parsed.event;
|
|
800
992
|
}
|
|
801
993
|
}
|
|
802
|
-
const config = readScopedHooksConfig(scope, cwd);
|
|
803
|
-
const sorted = [...entries].sort((a, b) => a.file.localeCompare(b.file));
|
|
804
|
-
const next = { ...config };
|
|
805
|
-
if (sorted.length === 0)
|
|
806
|
-
delete next.conventionPolicies;
|
|
807
|
-
else
|
|
808
|
-
next.conventionPolicies = sorted;
|
|
809
|
-
if (JSON.stringify(next.conventionPolicies ?? null) === JSON.stringify(config.conventionPolicies ?? null)) {
|
|
810
|
-
return false;
|
|
811
|
-
}
|
|
812
|
-
writeScopedHooksConfig(next, scope, cwd);
|
|
813
|
-
return true;
|
|
814
994
|
}
|
|
815
|
-
var init_hooks_config = __esm(() => {
|
|
816
|
-
init_hook_logger();
|
|
817
|
-
});
|
|
818
995
|
|
|
819
996
|
// src/hooks/policy-helpers.ts
|
|
820
997
|
function allow(reason) {
|
|
@@ -896,19 +1073,20 @@ function setAllPolicies(policies) {
|
|
|
896
1073
|
var REGISTRY_KEY = "__FAILPROOFAI_POLICY_REGISTRY__", INDEX_CACHE_KEY = "__FAILPROOFAI_POLICY_INDEX_CACHE__", DEFAULT_POLICY_NAMESPACE = "failproofai";
|
|
897
1074
|
|
|
898
1075
|
// src/hooks/builtin-policies.ts
|
|
899
|
-
import { resolve as
|
|
1076
|
+
import { resolve as resolve3, join as join2 } from "node:path";
|
|
1077
|
+
import { statSync as statSync3 } from "node:fs";
|
|
900
1078
|
import { readFile, writeFile } from "node:fs/promises";
|
|
901
1079
|
import { execSync as execSync2, execFileSync } from "node:child_process";
|
|
902
|
-
import { homedir as
|
|
1080
|
+
import { homedir as homedir4 } from "node:os";
|
|
903
1081
|
function isAgentInternalPath(resolved2) {
|
|
904
1082
|
const normResolved = resolved2.replaceAll("\\", "/");
|
|
905
1083
|
for (const dir of [".claude", ".codex", ".copilot", ".cursor", ".opencode", ".pi", ".gemini"]) {
|
|
906
|
-
const root = join2(
|
|
1084
|
+
const root = join2(homedir4(), dir).replaceAll("\\", "/");
|
|
907
1085
|
if (normResolved === root || normResolved.startsWith(root + "/"))
|
|
908
1086
|
return true;
|
|
909
1087
|
}
|
|
910
1088
|
for (const sub of [join2(".config", "opencode"), join2(".local", "share", "opencode")]) {
|
|
911
|
-
const root = join2(
|
|
1089
|
+
const root = join2(homedir4(), sub).replaceAll("\\", "/");
|
|
912
1090
|
if (normResolved === root || normResolved.startsWith(root + "/"))
|
|
913
1091
|
return true;
|
|
914
1092
|
}
|
|
@@ -954,17 +1132,30 @@ function getFilePath(ctx) {
|
|
|
954
1132
|
function parseArgvTokens(cmd) {
|
|
955
1133
|
return cmd.trim().split(/\s+/).map((t) => t.replace(/^['"]|['"]$/g, ""));
|
|
956
1134
|
}
|
|
1135
|
+
function statGitHeadMtimeMs(cwd) {
|
|
1136
|
+
try {
|
|
1137
|
+
return statSync3(join2(cwd, ".git", "HEAD")).mtimeMs;
|
|
1138
|
+
} catch {
|
|
1139
|
+
return null;
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
957
1142
|
function getCurrentBranch(cwd) {
|
|
958
1143
|
try {
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
1144
|
+
const headMtimeMs = statGitHeadMtimeMs(cwd);
|
|
1145
|
+
const cached = gitBranchCache.get(cwd);
|
|
1146
|
+
if (cached && headMtimeMs !== null && cached.headMtimeMs === headMtimeMs) {
|
|
1147
|
+
return cached.branch || null;
|
|
1148
|
+
}
|
|
1149
|
+
const branch = execSync2("git rev-parse --abbrev-ref HEAD", {
|
|
1150
|
+
cwd,
|
|
1151
|
+
encoding: "utf8",
|
|
1152
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1153
|
+
timeout: 3000
|
|
1154
|
+
}).trim();
|
|
1155
|
+
if (headMtimeMs !== null) {
|
|
1156
|
+
if (gitBranchCache.size >= GIT_BRANCH_CACHE_MAX_ENTRIES)
|
|
1157
|
+
gitBranchCache.clear();
|
|
1158
|
+
gitBranchCache.set(cwd, { branch, headMtimeMs });
|
|
968
1159
|
}
|
|
969
1160
|
return branch || null;
|
|
970
1161
|
} catch {
|
|
@@ -1233,7 +1424,7 @@ function blockPushMaster(ctx) {
|
|
|
1233
1424
|
}
|
|
1234
1425
|
function expandHomePrefix(path2) {
|
|
1235
1426
|
const m = path2.match(/^(?:~|\$HOME|\$\{HOME\})(?=$|\/)/);
|
|
1236
|
-
return m ?
|
|
1427
|
+
return m ? homedir4() + path2.slice(m[0].length) : path2;
|
|
1237
1428
|
}
|
|
1238
1429
|
function stripTrailingGlob(path2) {
|
|
1239
1430
|
return path2.replace(/\/\*$/, "").replace(/\/+$/, "");
|
|
@@ -1384,9 +1575,9 @@ function extractAbsolutePaths(command) {
|
|
|
1384
1575
|
while ((m = pathRe.exec(s)) !== null) {
|
|
1385
1576
|
let p = m[0];
|
|
1386
1577
|
if (p === "~")
|
|
1387
|
-
p =
|
|
1578
|
+
p = homedir4();
|
|
1388
1579
|
else if (p.startsWith("~/"))
|
|
1389
|
-
p = join2(
|
|
1580
|
+
p = join2(homedir4(), p.slice(2));
|
|
1390
1581
|
paths.push(p);
|
|
1391
1582
|
}
|
|
1392
1583
|
}
|
|
@@ -1428,7 +1619,7 @@ function blockReadOutsideCwd(ctx) {
|
|
|
1428
1619
|
const paths = extractAbsolutePaths(cmd);
|
|
1429
1620
|
const cwdWithSep2 = cwd.endsWith("/") ? cwd : cwd + "/";
|
|
1430
1621
|
for (const p of paths) {
|
|
1431
|
-
const resolved3 =
|
|
1622
|
+
const resolved3 = resolve3(cwd, p);
|
|
1432
1623
|
if (isClaudeSettingsFile(resolved3)) {
|
|
1433
1624
|
return deny(`Reading agent settings file blocked: ${resolved3}`);
|
|
1434
1625
|
}
|
|
@@ -1449,7 +1640,7 @@ function blockReadOutsideCwd(ctx) {
|
|
|
1449
1640
|
const target = filePath || searchPath;
|
|
1450
1641
|
if (!target)
|
|
1451
1642
|
return allow();
|
|
1452
|
-
const resolved2 =
|
|
1643
|
+
const resolved2 = resolve3(cwd, target);
|
|
1453
1644
|
if (isClaudeSettingsFile(resolved2)) {
|
|
1454
1645
|
return deny(`Reading agent settings file blocked: ${resolved2}`);
|
|
1455
1646
|
}
|
|
@@ -1940,7 +2131,7 @@ function registerBuiltinPolicies(enabledNames) {
|
|
|
1940
2131
|
}
|
|
1941
2132
|
}
|
|
1942
2133
|
}
|
|
1943
|
-
var isClaudeInternalPath, isClaudeSettingsFile, SHELL_OPERATORS, SHELL_METACHAR_RE, JWT_RE, API_KEY_PATTERNS, CONNECTION_STRING_RE, PRIVATE_KEY_RE, BEARER_TOKEN_RE, SQL_TOOL_RE, DESTRUCTIVE_SQL_RE, DELETE_NO_WHERE_RE, SQL_WHERE_RE, SCHEMA_ALTER_RE, PUBLISH_CMD_RE, ENV_PRINTENV_RE, ECHO_ENV_RE, EXPORT_RE, PS_ENV_VAR_RE, PS_CHILDITEM_ENV_RE, DOTNET_GETENV_RE, CMD_ECHO_ENV_RE, ENV_FILE_PATH_RE, ENV_CMD_RE, SUDO_RE, PS_ELEVATION_RE, RUNAS_RE, CURL_PIPE_SH_RE, PS_WEB_PIPE_RE, SHORT_FLAG_BUNDLE_RE, SAFE_FORCE_PREFIXES, SECRET_FILE_RE, SECRET_FILE_ID_RSA_RE, SECRET_FILE_CREDENTIALS_RE, GIT_COMMIT_MERGE_RE, FAILPROOFAI_CLI_RE, FAILPROOFAI_UNINSTALL_RE, GIT_AMEND_RE, GIT_STASH_DROP_RE, GIT_ADD_ALL_RE, NPM_GLOBAL_RE, YARN_GLOBAL_RE, PNPM_GLOBAL_RE, BUN_GLOBAL_RE, CARGO_INSTALL_RE, PIP_SYSTEM_RE, PKG_MANAGER_DETECTORS, NOHUP_RE, SCREEN_DETACH_RE, TMUX_DETACH_RE, DISOWN_RE, BACKGROUND_AMPERSAND_RE, KUBECTL_RE, TERRAFORM_RE, AWS_CLI_RE, GCLOUD_RE, AZ_CLI_RE, HELM_RE, GH_PIPELINE_RE, gitBranchCache, HOME_PREFIX_RE, RM_CMD_RE, FIND_CMD_RE, FIND_EXEC_RE, FIND_GLOBAL_OPT_RE, FIND_EXPR_START_RE, SCRATCH_ROOTS, CATASTROPHIC_DEPTH = 2, READ_LIKE_CMDS, TOOL_CALL_TRACKER_MAX_BYTES = 65536, SEGMENT_SPLIT_RE, BUILTIN_POLICIES;
|
|
2134
|
+
var isClaudeInternalPath, isClaudeSettingsFile, SHELL_OPERATORS, SHELL_METACHAR_RE, JWT_RE, API_KEY_PATTERNS, CONNECTION_STRING_RE, PRIVATE_KEY_RE, BEARER_TOKEN_RE, SQL_TOOL_RE, DESTRUCTIVE_SQL_RE, DELETE_NO_WHERE_RE, SQL_WHERE_RE, SCHEMA_ALTER_RE, PUBLISH_CMD_RE, ENV_PRINTENV_RE, ECHO_ENV_RE, EXPORT_RE, PS_ENV_VAR_RE, PS_CHILDITEM_ENV_RE, DOTNET_GETENV_RE, CMD_ECHO_ENV_RE, ENV_FILE_PATH_RE, ENV_CMD_RE, SUDO_RE, PS_ELEVATION_RE, RUNAS_RE, CURL_PIPE_SH_RE, PS_WEB_PIPE_RE, SHORT_FLAG_BUNDLE_RE, SAFE_FORCE_PREFIXES, SECRET_FILE_RE, SECRET_FILE_ID_RSA_RE, SECRET_FILE_CREDENTIALS_RE, GIT_COMMIT_MERGE_RE, FAILPROOFAI_CLI_RE, FAILPROOFAI_UNINSTALL_RE, GIT_AMEND_RE, GIT_STASH_DROP_RE, GIT_ADD_ALL_RE, NPM_GLOBAL_RE, YARN_GLOBAL_RE, PNPM_GLOBAL_RE, BUN_GLOBAL_RE, CARGO_INSTALL_RE, PIP_SYSTEM_RE, PKG_MANAGER_DETECTORS, NOHUP_RE, SCREEN_DETACH_RE, TMUX_DETACH_RE, DISOWN_RE, BACKGROUND_AMPERSAND_RE, KUBECTL_RE, TERRAFORM_RE, AWS_CLI_RE, GCLOUD_RE, AZ_CLI_RE, HELM_RE, GH_PIPELINE_RE, gitBranchCache, GIT_BRANCH_CACHE_MAX_ENTRIES = 500, HOME_PREFIX_RE, RM_CMD_RE, FIND_CMD_RE, FIND_EXEC_RE, FIND_GLOBAL_OPT_RE, FIND_EXPR_START_RE, SCRATCH_ROOTS, CATASTROPHIC_DEPTH = 2, READ_LIKE_CMDS, TOOL_CALL_TRACKER_MAX_BYTES = 65536, SEGMENT_SPLIT_RE, BUILTIN_POLICIES;
|
|
1944
2135
|
var init_builtin_policies = __esm(() => {
|
|
1945
2136
|
init_hook_logger();
|
|
1946
2137
|
isClaudeInternalPath = isAgentInternalPath;
|
|
@@ -3442,7 +3633,7 @@ var init_custom_hooks_registry = () => {};
|
|
|
3442
3633
|
|
|
3443
3634
|
// src/hooks/loader-utils.ts
|
|
3444
3635
|
import { readFile as readFile2, writeFile as writeFile2, unlink, access } from "fs/promises";
|
|
3445
|
-
import { resolve as
|
|
3636
|
+
import { resolve as resolve4, dirname as dirname2, relative } from "path";
|
|
3446
3637
|
import { pathToFileURL } from "url";
|
|
3447
3638
|
async function fileExists(path3) {
|
|
3448
3639
|
try {
|
|
@@ -3455,14 +3646,14 @@ async function fileExists(path3) {
|
|
|
3455
3646
|
async function findDistIndex() {
|
|
3456
3647
|
const distPath = process.env.FAILPROOFAI_DIST_PATH;
|
|
3457
3648
|
if (distPath) {
|
|
3458
|
-
const candidate =
|
|
3649
|
+
const candidate = resolve4(distPath, "index.js");
|
|
3459
3650
|
if (await fileExists(candidate))
|
|
3460
3651
|
return candidate;
|
|
3461
3652
|
}
|
|
3462
3653
|
const candidates = [
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3654
|
+
resolve4(dirname2(process.execPath), "..", "assets", "dist", "index.js"),
|
|
3655
|
+
resolve4(process.cwd(), "dist", "index.js"),
|
|
3656
|
+
resolve4(process.cwd(), "node_modules", "failproofai", "dist", "index.js")
|
|
3466
3657
|
];
|
|
3467
3658
|
for (const c of candidates) {
|
|
3468
3659
|
if (await fileExists(c))
|
|
@@ -3471,8 +3662,8 @@ async function findDistIndex() {
|
|
|
3471
3662
|
return null;
|
|
3472
3663
|
}
|
|
3473
3664
|
async function resolveLocalImport(fromDir, specifier) {
|
|
3474
|
-
const base =
|
|
3475
|
-
const candidates = [base, `${base}.js`, `${base}.mjs`, `${base}.ts`,
|
|
3665
|
+
const base = resolve4(fromDir, specifier);
|
|
3666
|
+
const candidates = [base, `${base}.js`, `${base}.mjs`, `${base}.ts`, resolve4(base, "index.js")];
|
|
3476
3667
|
for (const c of candidates) {
|
|
3477
3668
|
if (await fileExists(c))
|
|
3478
3669
|
return c;
|
|
@@ -3565,16 +3756,16 @@ var init_loader_utils = __esm(() => {
|
|
|
3565
3756
|
});
|
|
3566
3757
|
|
|
3567
3758
|
// src/hooks/custom-hooks-loader.ts
|
|
3568
|
-
import { resolve as
|
|
3759
|
+
import { resolve as resolve5, isAbsolute, basename } from "node:path";
|
|
3569
3760
|
import { existsSync as existsSync3, readdirSync } from "node:fs";
|
|
3570
3761
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
3571
|
-
import { homedir as
|
|
3762
|
+
import { homedir as homedir5 } from "node:os";
|
|
3572
3763
|
function discoverPolicyFiles(dir) {
|
|
3573
3764
|
if (!existsSync3(dir))
|
|
3574
3765
|
return [];
|
|
3575
3766
|
try {
|
|
3576
3767
|
const entries = readdirSync(dir, { withFileTypes: true });
|
|
3577
|
-
return entries.filter((e) => e.isFile() && CONVENTION_FILE_RE.test(e.name)).sort((a, b) => a.name.localeCompare(b.name)).map((e) =>
|
|
3768
|
+
return entries.filter((e) => e.isFile() && CONVENTION_FILE_RE.test(e.name)).sort((a, b) => a.name.localeCompare(b.name)).map((e) => resolve5(dir, e.name));
|
|
3578
3769
|
} catch {
|
|
3579
3770
|
return [];
|
|
3580
3771
|
}
|
|
@@ -3619,7 +3810,7 @@ async function loadSingleFile(absPath, opts) {
|
|
|
3619
3810
|
async function loadCustomHooks(customPoliciesPath, opts) {
|
|
3620
3811
|
if (!customPoliciesPath)
|
|
3621
3812
|
return [];
|
|
3622
|
-
const absPath = isAbsolute(customPoliciesPath) ? customPoliciesPath :
|
|
3813
|
+
const absPath = isAbsolute(customPoliciesPath) ? customPoliciesPath : resolve5(opts?.sessionCwd ?? process.cwd(), customPoliciesPath);
|
|
3623
3814
|
if (!existsSync3(absPath)) {
|
|
3624
3815
|
if (opts?.strict)
|
|
3625
3816
|
throw new Error(`Custom hooks file not found: ${absPath}`);
|
|
@@ -3650,7 +3841,7 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
|
|
|
3650
3841
|
const conventionEnabled = opts?.customPoliciesEnabled !== false;
|
|
3651
3842
|
const loadedPaths = new Set;
|
|
3652
3843
|
for (const customPoliciesPath of typeof customPoliciesPaths === "string" ? [customPoliciesPaths] : customPoliciesPaths ?? []) {
|
|
3653
|
-
const absPath =
|
|
3844
|
+
const absPath = resolve5(projectRoot, customPoliciesPath);
|
|
3654
3845
|
if (existsSync3(absPath)) {
|
|
3655
3846
|
if (!loadedPaths.has(absPath)) {
|
|
3656
3847
|
loadedPaths.add(absPath);
|
|
@@ -3665,7 +3856,7 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
|
|
|
3665
3856
|
}
|
|
3666
3857
|
}
|
|
3667
3858
|
const hooksBeforeConvention = getCustomHooks().length;
|
|
3668
|
-
const projectDir =
|
|
3859
|
+
const projectDir = resolve5(projectRoot, ".failproofai", "policies");
|
|
3669
3860
|
if (conventionEnabled)
|
|
3670
3861
|
warnSkippedPolicyFiles(projectDir, "project");
|
|
3671
3862
|
const projectFiles = conventionEnabled ? discoverPolicyFiles(projectDir).filter((f) => !loadedPaths.has(f)) : [];
|
|
@@ -3685,7 +3876,7 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
|
|
|
3685
3876
|
});
|
|
3686
3877
|
}
|
|
3687
3878
|
}
|
|
3688
|
-
const userDir =
|
|
3879
|
+
const userDir = resolve5(homedir5(), ".failproofai", "policies");
|
|
3689
3880
|
if (conventionEnabled && userDir !== projectDir)
|
|
3690
3881
|
warnSkippedPolicyFiles(userDir, "user");
|
|
3691
3882
|
const userFiles = conventionEnabled && userDir !== projectDir ? discoverPolicyFiles(userDir).filter((f) => !loadedPaths.has(f)) : [];
|
|
@@ -3750,11 +3941,11 @@ import {
|
|
|
3750
3941
|
readdirSync as readdirSync2,
|
|
3751
3942
|
mkdirSync as mkdirSync3,
|
|
3752
3943
|
existsSync as existsSync4,
|
|
3753
|
-
statSync as
|
|
3944
|
+
statSync as statSync4,
|
|
3754
3945
|
unlinkSync
|
|
3755
3946
|
} from "node:fs";
|
|
3756
3947
|
import { join as join3 } from "node:path";
|
|
3757
|
-
import { homedir as
|
|
3948
|
+
import { homedir as homedir6 } from "node:os";
|
|
3758
3949
|
function ensureDir() {
|
|
3759
3950
|
if (!existsSync4(storeDir)) {
|
|
3760
3951
|
mkdirSync3(storeDir, { recursive: true });
|
|
@@ -3772,7 +3963,7 @@ function acquireLock() {
|
|
|
3772
3963
|
if (e.code !== "EEXIST")
|
|
3773
3964
|
return;
|
|
3774
3965
|
try {
|
|
3775
|
-
const s =
|
|
3966
|
+
const s = statSync4(lockPath);
|
|
3776
3967
|
if (Date.now() - s.mtimeMs > LOCK_STALE_MS) {
|
|
3777
3968
|
writeFileSync2(lockPath, String(process.pid), "utf-8");
|
|
3778
3969
|
return;
|
|
@@ -3859,7 +4050,7 @@ function updateStats(entry) {
|
|
|
3859
4050
|
}
|
|
3860
4051
|
var PAGE_SIZE = 25, DEFAULT_STORE_DIR, CURRENT_FILE = "current.jsonl", COUNT_FILE = "current.count", STATS_FILE = "stats.json", LOCK_FILE = "current.lock", LOCK_STALE_MS = 2000, storeDir, rotateSeq = 0;
|
|
3861
4052
|
var init_hook_activity_store = __esm(() => {
|
|
3862
|
-
DEFAULT_STORE_DIR = join3(
|
|
4053
|
+
DEFAULT_STORE_DIR = join3(homedir6(), ".failproofai", "cache", "hook-activity");
|
|
3863
4054
|
storeDir = DEFAULT_STORE_DIR;
|
|
3864
4055
|
});
|
|
3865
4056
|
|
|
@@ -3909,10 +4100,10 @@ function runtimeCache(fn, revalidateSeconds, options) {
|
|
|
3909
4100
|
}
|
|
3910
4101
|
|
|
3911
4102
|
// lib/paths.ts
|
|
3912
|
-
import { homedir as
|
|
4103
|
+
import { homedir as homedir7 } from "os";
|
|
3913
4104
|
import { join as join4 } from "path";
|
|
3914
4105
|
function getDefaultClaudeProjectsPath() {
|
|
3915
|
-
return join4(
|
|
4106
|
+
return join4(homedir7(), ".claude", "projects");
|
|
3916
4107
|
}
|
|
3917
4108
|
function decodeFolderName(name) {
|
|
3918
4109
|
if (/^[A-Za-z]--/.test(name)) {
|
|
@@ -4006,7 +4197,7 @@ __export(exports_codex_projects, {
|
|
|
4006
4197
|
getCachedCodexProjects: () => getCachedCodexProjects
|
|
4007
4198
|
});
|
|
4008
4199
|
import { open as open2, readdir } from "node:fs/promises";
|
|
4009
|
-
import { homedir as
|
|
4200
|
+
import { homedir as homedir8 } from "node:os";
|
|
4010
4201
|
import { join as join5 } from "node:path";
|
|
4011
4202
|
async function safeReaddir(dir) {
|
|
4012
4203
|
try {
|
|
@@ -4185,7 +4376,7 @@ var CODEX_SESSIONS_ROOT, SESSION_ID_RE, FIRST_LINE_CHUNK_BYTES, cachedScan, getC
|
|
|
4185
4376
|
var init_codex_projects = __esm(() => {
|
|
4186
4377
|
init_paths();
|
|
4187
4378
|
init_logger();
|
|
4188
|
-
CODEX_SESSIONS_ROOT = join5(
|
|
4379
|
+
CODEX_SESSIONS_ROOT = join5(homedir8(), ".codex", "sessions");
|
|
4189
4380
|
SESSION_ID_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
|
|
4190
4381
|
FIRST_LINE_CHUNK_BYTES = 256 * 1024;
|
|
4191
4382
|
cachedScan = runtimeCache(scanCodexSessions, 30);
|
|
@@ -4205,10 +4396,10 @@ __export(exports_copilot_projects, {
|
|
|
4205
4396
|
getCachedCopilotProjects: () => getCachedCopilotProjects
|
|
4206
4397
|
});
|
|
4207
4398
|
import { readdir as readdir2, readFile as readFile3, stat as stat2 } from "node:fs/promises";
|
|
4208
|
-
import { homedir as
|
|
4399
|
+
import { homedir as homedir9 } from "node:os";
|
|
4209
4400
|
import { join as join6 } from "node:path";
|
|
4210
4401
|
function getCopilotSessionStateRoot() {
|
|
4211
|
-
return join6(process.env.COPILOT_HOME || join6(
|
|
4402
|
+
return join6(process.env.COPILOT_HOME || join6(homedir9(), ".copilot"), "session-state");
|
|
4212
4403
|
}
|
|
4213
4404
|
async function safeReaddir2(dir) {
|
|
4214
4405
|
try {
|
|
@@ -4353,10 +4544,10 @@ __export(exports_cursor_projects, {
|
|
|
4353
4544
|
getCachedCursorProjects: () => getCachedCursorProjects
|
|
4354
4545
|
});
|
|
4355
4546
|
import { readdir as readdir3, readFile as readFile4, stat as stat3 } from "node:fs/promises";
|
|
4356
|
-
import { homedir as
|
|
4547
|
+
import { homedir as homedir10 } from "node:os";
|
|
4357
4548
|
import { join as join7 } from "node:path";
|
|
4358
4549
|
function getCursorHome() {
|
|
4359
|
-
return process.env.CURSOR_HOME || join7(
|
|
4550
|
+
return process.env.CURSOR_HOME || join7(homedir10(), ".cursor");
|
|
4360
4551
|
}
|
|
4361
4552
|
async function safeReaddir3(dir) {
|
|
4362
4553
|
try {
|
|
@@ -4726,10 +4917,10 @@ __export(exports_pi_projects, {
|
|
|
4726
4917
|
getCachedPiProjects: () => getCachedPiProjects
|
|
4727
4918
|
});
|
|
4728
4919
|
import { readdir as readdir4, readFile as readFile5, stat as stat4 } from "node:fs/promises";
|
|
4729
|
-
import { homedir as
|
|
4920
|
+
import { homedir as homedir11 } from "node:os";
|
|
4730
4921
|
import { join as join8 } from "node:path";
|
|
4731
4922
|
function getPiSessionsRoot() {
|
|
4732
|
-
return process.env.PI_SESSIONS_DIR || join8(
|
|
4923
|
+
return process.env.PI_SESSIONS_DIR || join8(homedir11(), ".pi", "agent", "sessions");
|
|
4733
4924
|
}
|
|
4734
4925
|
async function safeReaddir4(dir) {
|
|
4735
4926
|
try {
|
|
@@ -4949,18 +5140,18 @@ var init_sqlite_reader = () => {};
|
|
|
4949
5140
|
|
|
4950
5141
|
// lib/hermes-profiles.ts
|
|
4951
5142
|
import { readdirSync as readdirSync3 } from "node:fs";
|
|
4952
|
-
import { homedir as
|
|
4953
|
-
import { basename as basename2, dirname as dirname3, join as join9, resolve as
|
|
5143
|
+
import { homedir as homedir12 } from "node:os";
|
|
5144
|
+
import { basename as basename2, dirname as dirname3, join as join9, resolve as resolve6 } from "node:path";
|
|
4954
5145
|
function hermesRoot() {
|
|
4955
5146
|
const env = (process.env.HERMES_HOME || "").trim();
|
|
4956
5147
|
if (env) {
|
|
4957
|
-
const home =
|
|
5148
|
+
const home = resolve6(env);
|
|
4958
5149
|
const parent = dirname3(home);
|
|
4959
5150
|
if (basename2(parent) === PROFILES_DIR)
|
|
4960
5151
|
return dirname3(parent);
|
|
4961
5152
|
return home;
|
|
4962
5153
|
}
|
|
4963
|
-
return join9(
|
|
5154
|
+
return join9(homedir12(), ".hermes");
|
|
4964
5155
|
}
|
|
4965
5156
|
function listHermesProfiles() {
|
|
4966
5157
|
const root = hermesRoot();
|
|
@@ -5331,11 +5522,11 @@ var init_hermes_projects = __esm(() => {
|
|
|
5331
5522
|
|
|
5332
5523
|
// lib/openclaw-sessions.ts
|
|
5333
5524
|
import { readFile as readFile6 } from "node:fs/promises";
|
|
5334
|
-
import { readdirSync as readdirSync4, statSync as
|
|
5335
|
-
import { homedir as
|
|
5525
|
+
import { readdirSync as readdirSync4, statSync as statSync5 } from "node:fs";
|
|
5526
|
+
import { homedir as homedir13 } from "node:os";
|
|
5336
5527
|
import { join as join11 } from "node:path";
|
|
5337
5528
|
function openclawHome() {
|
|
5338
|
-
return process.env.OPENCLAW_HOME || join11(
|
|
5529
|
+
return process.env.OPENCLAW_HOME || join11(homedir13(), ".openclaw");
|
|
5339
5530
|
}
|
|
5340
5531
|
function isPlainObject2(v) {
|
|
5341
5532
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
@@ -5480,7 +5671,7 @@ function listOpenClawTranscripts() {
|
|
|
5480
5671
|
continue;
|
|
5481
5672
|
const transcriptPath = join11(sessionsDir, file);
|
|
5482
5673
|
try {
|
|
5483
|
-
const st =
|
|
5674
|
+
const st = statSync5(transcriptPath);
|
|
5484
5675
|
out.push({ agentId, sessionId, transcriptPath, mtimeMs: st.mtimeMs, sizeBytes: st.size });
|
|
5485
5676
|
} catch {}
|
|
5486
5677
|
}
|
|
@@ -5700,11 +5891,11 @@ var init_openclaw_projects = __esm(() => {
|
|
|
5700
5891
|
|
|
5701
5892
|
// lib/factory-sessions.ts
|
|
5702
5893
|
import { readFile as readFile7 } from "node:fs/promises";
|
|
5703
|
-
import { readdirSync as readdirSync5, statSync as
|
|
5704
|
-
import { homedir as
|
|
5894
|
+
import { readdirSync as readdirSync5, statSync as statSync6 } from "node:fs";
|
|
5895
|
+
import { homedir as homedir14 } from "node:os";
|
|
5705
5896
|
import { join as join13 } from "node:path";
|
|
5706
5897
|
function factoryHome() {
|
|
5707
|
-
return process.env.FACTORY_HOME || join13(
|
|
5898
|
+
return process.env.FACTORY_HOME || join13(homedir14(), ".factory");
|
|
5708
5899
|
}
|
|
5709
5900
|
function factorySessionsRoot() {
|
|
5710
5901
|
return join13(factoryHome(), "sessions");
|
|
@@ -5868,7 +6059,7 @@ function listFactoryTranscripts() {
|
|
|
5868
6059
|
continue;
|
|
5869
6060
|
const transcriptPath = join13(projectPath, file);
|
|
5870
6061
|
try {
|
|
5871
|
-
const st =
|
|
6062
|
+
const st = statSync6(transcriptPath);
|
|
5872
6063
|
out.push({ projectName, cwd, sessionId, transcriptPath, mtimeMs: st.mtimeMs, sizeBytes: st.size });
|
|
5873
6064
|
} catch {}
|
|
5874
6065
|
}
|
|
@@ -5992,10 +6183,10 @@ var init_factory_projects = __esm(() => {
|
|
|
5992
6183
|
});
|
|
5993
6184
|
|
|
5994
6185
|
// lib/devin-sessions.ts
|
|
5995
|
-
import { homedir as
|
|
6186
|
+
import { homedir as homedir15 } from "node:os";
|
|
5996
6187
|
import { join as join14 } from "node:path";
|
|
5997
6188
|
function devinHome() {
|
|
5998
|
-
return process.env.DEVIN_HOME || join14(
|
|
6189
|
+
return process.env.DEVIN_HOME || join14(homedir15(), ".local", "share", "devin");
|
|
5999
6190
|
}
|
|
6000
6191
|
function devinDbPath() {
|
|
6001
6192
|
return process.env.DEVIN_DB_PATH || join14(devinHome(), "cli", "sessions.db");
|
|
@@ -6295,11 +6486,11 @@ var init_devin_projects = __esm(() => {
|
|
|
6295
6486
|
|
|
6296
6487
|
// lib/antigravity-sessions.ts
|
|
6297
6488
|
import { readFile as readFile8 } from "node:fs/promises";
|
|
6298
|
-
import { readdirSync as readdirSync6, statSync as
|
|
6299
|
-
import { homedir as
|
|
6489
|
+
import { readdirSync as readdirSync6, statSync as statSync7 } from "node:fs";
|
|
6490
|
+
import { homedir as homedir16 } from "node:os";
|
|
6300
6491
|
import { join as join15 } from "node:path";
|
|
6301
6492
|
function antigravityHome() {
|
|
6302
|
-
return process.env.ANTIGRAVITY_HOME || join15(
|
|
6493
|
+
return process.env.ANTIGRAVITY_HOME || join15(homedir16(), ".gemini", "antigravity-cli");
|
|
6303
6494
|
}
|
|
6304
6495
|
function antigravityBrainRoot() {
|
|
6305
6496
|
return join15(antigravityHome(), "brain");
|
|
@@ -6430,7 +6621,7 @@ function listAntigravityTranscripts() {
|
|
|
6430
6621
|
continue;
|
|
6431
6622
|
const transcriptPath = join15(root, conversationId, rel);
|
|
6432
6623
|
try {
|
|
6433
|
-
const st =
|
|
6624
|
+
const st = statSync7(transcriptPath);
|
|
6434
6625
|
if (!st.isFile())
|
|
6435
6626
|
continue;
|
|
6436
6627
|
out.push({ conversationId, transcriptPath, mtimeMs: st.mtimeMs, sizeBytes: st.size });
|
|
@@ -6443,7 +6634,7 @@ function findAntigravityTranscript(conversationId) {
|
|
|
6443
6634
|
return null;
|
|
6444
6635
|
const transcriptPath = join15(antigravityBrainRoot(), conversationId, antigravityTranscriptRelPath());
|
|
6445
6636
|
try {
|
|
6446
|
-
if (
|
|
6637
|
+
if (statSync7(transcriptPath).isFile())
|
|
6447
6638
|
return transcriptPath;
|
|
6448
6639
|
} catch {}
|
|
6449
6640
|
return null;
|
|
@@ -6626,13 +6817,13 @@ var init_antigravity_projects = __esm(() => {
|
|
|
6626
6817
|
});
|
|
6627
6818
|
|
|
6628
6819
|
// lib/goose-sessions.ts
|
|
6629
|
-
import { homedir as
|
|
6820
|
+
import { homedir as homedir17 } from "node:os";
|
|
6630
6821
|
import { join as join17 } from "node:path";
|
|
6631
6822
|
function gooseHome() {
|
|
6632
6823
|
if (process.env.GOOSE_HOME)
|
|
6633
6824
|
return process.env.GOOSE_HOME;
|
|
6634
6825
|
const xdg = process.env.XDG_DATA_HOME;
|
|
6635
|
-
return join17(xdg && xdg.length > 0 ? xdg : join17(
|
|
6826
|
+
return join17(xdg && xdg.length > 0 ? xdg : join17(homedir17(), ".local", "share"), "goose");
|
|
6636
6827
|
}
|
|
6637
6828
|
function gooseDbPath() {
|
|
6638
6829
|
return process.env.GOOSE_DB_PATH || join17(gooseHome(), "sessions", "sessions.db");
|
|
@@ -6903,7 +7094,7 @@ var init_goose_projects = __esm(() => {
|
|
|
6903
7094
|
|
|
6904
7095
|
// lib/projects.ts
|
|
6905
7096
|
import { readdir as readdir5, stat as stat5 } from "node:fs/promises";
|
|
6906
|
-
import { join as join18, resolve as
|
|
7097
|
+
import { join as join18, resolve as resolve7, sep } from "node:path";
|
|
6907
7098
|
async function getMtime(path3, label) {
|
|
6908
7099
|
try {
|
|
6909
7100
|
return (await stat5(path3)).mtime;
|
|
@@ -7055,8 +7246,8 @@ function resolveProjectPath(projectName) {
|
|
|
7055
7246
|
throw new RangeError("Empty project name");
|
|
7056
7247
|
if (/^[/\\]/.test(projectName))
|
|
7057
7248
|
throw new RangeError("Absolute project name");
|
|
7058
|
-
const projectsPath =
|
|
7059
|
-
const candidate =
|
|
7249
|
+
const projectsPath = resolve7(getClaudeProjectsPath());
|
|
7250
|
+
const candidate = resolve7(join18(projectsPath, projectName));
|
|
7060
7251
|
if (!candidate.startsWith(projectsPath + sep)) {
|
|
7061
7252
|
throw new RangeError("Project path escapes root");
|
|
7062
7253
|
}
|
|
@@ -7366,10 +7557,10 @@ var init_log_entries = __esm(() => {
|
|
|
7366
7557
|
});
|
|
7367
7558
|
|
|
7368
7559
|
// lib/codex-sessions.ts
|
|
7369
|
-
import { readFileSync as readFileSync5, readdirSync as readdirSync7, existsSync as existsSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, statSync as
|
|
7560
|
+
import { readFileSync as readFileSync5, readdirSync as readdirSync7, existsSync as existsSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, statSync as statSync8 } from "node:fs";
|
|
7370
7561
|
import { readFile as readFile10 } from "node:fs/promises";
|
|
7371
7562
|
import { dirname as dirname4, join as join21 } from "node:path";
|
|
7372
|
-
import { homedir as
|
|
7563
|
+
import { homedir as homedir18 } from "node:os";
|
|
7373
7564
|
function readCache() {
|
|
7374
7565
|
try {
|
|
7375
7566
|
if (!existsSync6(CACHE_PATH))
|
|
@@ -7402,7 +7593,7 @@ function findCodexTranscript(sessionId) {
|
|
|
7402
7593
|
const cached = cache[sessionId];
|
|
7403
7594
|
if (cached && existsSync6(cached))
|
|
7404
7595
|
return cached;
|
|
7405
|
-
const root = join21(
|
|
7596
|
+
const root = join21(homedir18(), ".codex", "sessions");
|
|
7406
7597
|
const today = new Date;
|
|
7407
7598
|
const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000);
|
|
7408
7599
|
const datedDirs = [today, yesterday].map((d) => {
|
|
@@ -7648,7 +7839,7 @@ async function getCodexSessionLog(sessionId) {
|
|
|
7648
7839
|
var CACHE_PATH, getCachedCodexSessionLog;
|
|
7649
7840
|
var init_codex_sessions = __esm(() => {
|
|
7650
7841
|
init_log_entries();
|
|
7651
|
-
CACHE_PATH = join21(
|
|
7842
|
+
CACHE_PATH = join21(homedir18(), ".failproofai", "cache", "codex-session-paths.json");
|
|
7652
7843
|
getCachedCodexSessionLog = runtimeCache((sessionId) => getCodexSessionLog(sessionId), 60, { maxSize: 50 });
|
|
7653
7844
|
});
|
|
7654
7845
|
|
|
@@ -7693,12 +7884,12 @@ var init_resolve_permission_mode = __esm(() => {
|
|
|
7693
7884
|
});
|
|
7694
7885
|
|
|
7695
7886
|
// lib/copilot-sessions.ts
|
|
7696
|
-
import { readFileSync as readFileSync7, readdirSync as readdirSync8, existsSync as existsSync7, statSync as
|
|
7887
|
+
import { readFileSync as readFileSync7, readdirSync as readdirSync8, existsSync as existsSync7, statSync as statSync9 } from "node:fs";
|
|
7697
7888
|
import { readFile as readFile11 } from "node:fs/promises";
|
|
7698
|
-
import { join as join22, resolve as
|
|
7699
|
-
import { homedir as
|
|
7889
|
+
import { join as join22, resolve as resolve8, sep as sep2 } from "node:path";
|
|
7890
|
+
import { homedir as homedir19 } from "node:os";
|
|
7700
7891
|
function getCopilotHome() {
|
|
7701
|
-
return process.env.COPILOT_HOME || join22(
|
|
7892
|
+
return process.env.COPILOT_HOME || join22(homedir19(), ".copilot");
|
|
7702
7893
|
}
|
|
7703
7894
|
function getCopilotSessionStateRoot2() {
|
|
7704
7895
|
return join22(getCopilotHome(), "session-state");
|
|
@@ -7706,8 +7897,8 @@ function getCopilotSessionStateRoot2() {
|
|
|
7706
7897
|
function getCopilotSessionDir(sessionId) {
|
|
7707
7898
|
if (!sessionId)
|
|
7708
7899
|
return null;
|
|
7709
|
-
const root =
|
|
7710
|
-
const candidate =
|
|
7900
|
+
const root = resolve8(getCopilotSessionStateRoot2());
|
|
7901
|
+
const candidate = resolve8(root, sessionId);
|
|
7711
7902
|
if (candidate === root || !candidate.startsWith(`${root}${sep2}`))
|
|
7712
7903
|
return null;
|
|
7713
7904
|
return candidate;
|
|
@@ -7910,18 +8101,18 @@ var init_copilot_sessions = __esm(() => {
|
|
|
7910
8101
|
});
|
|
7911
8102
|
|
|
7912
8103
|
// lib/cursor-sessions.ts
|
|
7913
|
-
import { readFileSync as readFileSync8, readdirSync as readdirSync9, existsSync as existsSync8, statSync as
|
|
8104
|
+
import { readFileSync as readFileSync8, readdirSync as readdirSync9, existsSync as existsSync8, statSync as statSync10 } from "node:fs";
|
|
7914
8105
|
import { readFile as readFile12 } from "node:fs/promises";
|
|
7915
|
-
import { basename as basename3, join as join23, resolve as
|
|
7916
|
-
import { homedir as
|
|
8106
|
+
import { basename as basename3, join as join23, resolve as resolve9, sep as sep3 } from "node:path";
|
|
8107
|
+
import { homedir as homedir20 } from "node:os";
|
|
7917
8108
|
function getCursorHome2() {
|
|
7918
|
-
return process.env.CURSOR_HOME || join23(
|
|
8109
|
+
return process.env.CURSOR_HOME || join23(homedir20(), ".cursor");
|
|
7919
8110
|
}
|
|
7920
8111
|
function getCursorSessionDir(sessionId) {
|
|
7921
8112
|
if (!sessionId)
|
|
7922
8113
|
return null;
|
|
7923
|
-
const home =
|
|
7924
|
-
const projectsRoot =
|
|
8114
|
+
const home = resolve9(getCursorHome2());
|
|
8115
|
+
const projectsRoot = resolve9(home, NEW_PROJECTS_DIR2);
|
|
7925
8116
|
let projectEntries = [];
|
|
7926
8117
|
try {
|
|
7927
8118
|
projectEntries = readdirSync9(projectsRoot, { withFileTypes: true });
|
|
@@ -7929,16 +8120,16 @@ function getCursorSessionDir(sessionId) {
|
|
|
7929
8120
|
for (const entry of projectEntries) {
|
|
7930
8121
|
if (!entry.isDirectory())
|
|
7931
8122
|
continue;
|
|
7932
|
-
const candidate =
|
|
7933
|
-
const transcriptRoot =
|
|
8123
|
+
const candidate = resolve9(projectsRoot, entry.name, NEW_AGENT_TRANSCRIPTS_DIR2, sessionId);
|
|
8124
|
+
const transcriptRoot = resolve9(projectsRoot, entry.name, NEW_AGENT_TRANSCRIPTS_DIR2);
|
|
7934
8125
|
if (candidate === transcriptRoot || !candidate.startsWith(`${transcriptRoot}${sep3}`))
|
|
7935
8126
|
continue;
|
|
7936
8127
|
if (existsSync8(candidate))
|
|
7937
8128
|
return candidate;
|
|
7938
8129
|
}
|
|
7939
8130
|
for (const sub of LEGACY_SESSION_ROOT_CANDIDATES2) {
|
|
7940
|
-
const root =
|
|
7941
|
-
const candidate =
|
|
8131
|
+
const root = resolve9(home, sub);
|
|
8132
|
+
const candidate = resolve9(root, sessionId);
|
|
7942
8133
|
if (candidate === root || !candidate.startsWith(`${root}${sep3}`))
|
|
7943
8134
|
continue;
|
|
7944
8135
|
if (existsSync8(candidate))
|
|
@@ -8166,12 +8357,12 @@ var init_cursor_sessions = __esm(() => {
|
|
|
8166
8357
|
});
|
|
8167
8358
|
|
|
8168
8359
|
// lib/pi-sessions.ts
|
|
8169
|
-
import { readFileSync as readFileSync9, readdirSync as readdirSync10, existsSync as existsSync9, statSync as
|
|
8360
|
+
import { readFileSync as readFileSync9, readdirSync as readdirSync10, existsSync as existsSync9, statSync as statSync11 } from "node:fs";
|
|
8170
8361
|
import { readFile as readFile13 } from "node:fs/promises";
|
|
8171
|
-
import { join as join24, resolve as
|
|
8172
|
-
import { homedir as
|
|
8362
|
+
import { join as join24, resolve as resolve10, sep as sep4 } from "node:path";
|
|
8363
|
+
import { homedir as homedir21 } from "node:os";
|
|
8173
8364
|
function getPiSessionStateRoot() {
|
|
8174
|
-
return process.env.PI_SESSIONS_DIR || join24(
|
|
8365
|
+
return process.env.PI_SESSIONS_DIR || join24(homedir21(), ".pi", "agent", "sessions");
|
|
8175
8366
|
}
|
|
8176
8367
|
function isSafeSessionId(sessionId) {
|
|
8177
8368
|
return UUID_RE.test(sessionId);
|
|
@@ -8179,7 +8370,7 @@ function isSafeSessionId(sessionId) {
|
|
|
8179
8370
|
function findPiTranscript(sessionId) {
|
|
8180
8371
|
if (!isSafeSessionId(sessionId))
|
|
8181
8372
|
return null;
|
|
8182
|
-
const root =
|
|
8373
|
+
const root = resolve10(getPiSessionStateRoot());
|
|
8183
8374
|
let cwdDirs;
|
|
8184
8375
|
try {
|
|
8185
8376
|
cwdDirs = readdirSync10(root);
|
|
@@ -8187,7 +8378,7 @@ function findPiTranscript(sessionId) {
|
|
|
8187
8378
|
return null;
|
|
8188
8379
|
}
|
|
8189
8380
|
for (const cwdDir of cwdDirs) {
|
|
8190
|
-
const cwdPath =
|
|
8381
|
+
const cwdPath = resolve10(root, cwdDir);
|
|
8191
8382
|
if (!cwdPath.startsWith(`${root}${sep4}`))
|
|
8192
8383
|
continue;
|
|
8193
8384
|
let files;
|
|
@@ -8200,7 +8391,7 @@ function findPiTranscript(sessionId) {
|
|
|
8200
8391
|
const m = SESSION_FILE_RE2.exec(f);
|
|
8201
8392
|
if (!m || m[1].toLowerCase() !== sessionId.toLowerCase())
|
|
8202
8393
|
continue;
|
|
8203
|
-
const candidate =
|
|
8394
|
+
const candidate = resolve10(cwdPath, f);
|
|
8204
8395
|
if (!candidate.startsWith(`${cwdPath}${sep4}`))
|
|
8205
8396
|
continue;
|
|
8206
8397
|
if (existsSync9(candidate))
|
|
@@ -8399,10 +8590,43 @@ var init_resolve_transcript_path = __esm(() => {
|
|
|
8399
8590
|
init_antigravity_sessions();
|
|
8400
8591
|
});
|
|
8401
8592
|
|
|
8593
|
+
// src/hooks/read-stdin.ts
|
|
8594
|
+
async function readStdinPayload2(maxBytes = 1048576) {
|
|
8595
|
+
let payload = "";
|
|
8596
|
+
let oversized = false;
|
|
8597
|
+
let readError = false;
|
|
8598
|
+
try {
|
|
8599
|
+
payload = await new Promise((resolve11, reject) => {
|
|
8600
|
+
const chunks = [];
|
|
8601
|
+
let totalBytes = 0;
|
|
8602
|
+
process.stdin.setEncoding("utf8");
|
|
8603
|
+
process.stdin.on("data", (chunk) => {
|
|
8604
|
+
totalBytes += Buffer.byteLength(chunk);
|
|
8605
|
+
if (totalBytes > maxBytes) {
|
|
8606
|
+
oversized = true;
|
|
8607
|
+
process.stdin.destroy();
|
|
8608
|
+
resolve11("");
|
|
8609
|
+
return;
|
|
8610
|
+
}
|
|
8611
|
+
chunks.push(chunk);
|
|
8612
|
+
});
|
|
8613
|
+
process.stdin.on("end", () => resolve11(chunks.join("")));
|
|
8614
|
+
process.stdin.on("error", reject);
|
|
8615
|
+
if (process.stdin.readableEnded)
|
|
8616
|
+
resolve11("");
|
|
8617
|
+
});
|
|
8618
|
+
} catch {
|
|
8619
|
+
readError = true;
|
|
8620
|
+
}
|
|
8621
|
+
return { payload, oversized, readError };
|
|
8622
|
+
}
|
|
8623
|
+
|
|
8402
8624
|
// src/hooks/handler.ts
|
|
8403
8625
|
var exports_handler = {};
|
|
8404
8626
|
__export(exports_handler, {
|
|
8405
|
-
handleHookEvent: () => handleHookEvent
|
|
8627
|
+
handleHookEvent: () => handleHookEvent,
|
|
8628
|
+
evaluateHookEvent: () => evaluateHookEvent,
|
|
8629
|
+
canonicalizeEventType: () => canonicalizeEventType
|
|
8406
8630
|
});
|
|
8407
8631
|
function canonicalizeEventType(raw, cli) {
|
|
8408
8632
|
if (cli === "codex") {
|
|
@@ -8437,95 +8661,23 @@ function canonicalizeEventType(raw, cli) {
|
|
|
8437
8661
|
}
|
|
8438
8662
|
return raw;
|
|
8439
8663
|
}
|
|
8440
|
-
async function
|
|
8664
|
+
async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts) {
|
|
8441
8665
|
const startTime = performance.now();
|
|
8442
8666
|
try {
|
|
8443
|
-
const MAX_STDIN_BYTES = 1048576;
|
|
8444
|
-
let payload = "";
|
|
8445
|
-
let stdinOversized = false;
|
|
8446
|
-
try {
|
|
8447
|
-
payload = await new Promise((resolve10, reject) => {
|
|
8448
|
-
const chunks = [];
|
|
8449
|
-
let totalBytes = 0;
|
|
8450
|
-
process.stdin.setEncoding("utf8");
|
|
8451
|
-
process.stdin.on("data", (chunk) => {
|
|
8452
|
-
totalBytes += Buffer.byteLength(chunk);
|
|
8453
|
-
if (totalBytes > MAX_STDIN_BYTES) {
|
|
8454
|
-
hookLogWarn(`stdin payload exceeds 1 MB for ${eventType}, discarding`);
|
|
8455
|
-
stdinOversized = true;
|
|
8456
|
-
process.stdin.destroy();
|
|
8457
|
-
resolve10("");
|
|
8458
|
-
return;
|
|
8459
|
-
}
|
|
8460
|
-
chunks.push(chunk);
|
|
8461
|
-
});
|
|
8462
|
-
process.stdin.on("end", () => resolve10(chunks.join("")));
|
|
8463
|
-
process.stdin.on("error", reject);
|
|
8464
|
-
if (process.stdin.readableEnded)
|
|
8465
|
-
resolve10("");
|
|
8466
|
-
});
|
|
8467
|
-
} catch (err) {
|
|
8468
|
-
hookLogWarn(`stdin read failed for ${eventType}`);
|
|
8469
|
-
trackHookEvent2(getInstanceId2(), "hook_stdin_error", {
|
|
8470
|
-
event_type: eventType,
|
|
8471
|
-
cli,
|
|
8472
|
-
error_type: err instanceof Error ? err.name : "unknown"
|
|
8473
|
-
});
|
|
8474
|
-
}
|
|
8475
|
-
if (stdinOversized) {
|
|
8476
|
-
trackHookEvent2(getInstanceId2(), "hook_stdin_error", {
|
|
8477
|
-
event_type: eventType,
|
|
8478
|
-
cli,
|
|
8479
|
-
error_type: "oversized"
|
|
8480
|
-
});
|
|
8481
|
-
}
|
|
8482
8667
|
let parsed = {};
|
|
8483
|
-
if (
|
|
8668
|
+
if (stdinPayload) {
|
|
8484
8669
|
try {
|
|
8485
|
-
parsed = JSON.parse(
|
|
8670
|
+
parsed = JSON.parse(stdinPayload);
|
|
8486
8671
|
} catch {
|
|
8487
|
-
hookLogWarn(`payload parse failed for ${eventType} (${
|
|
8672
|
+
hookLogWarn(`payload parse failed for ${eventType} (${stdinPayload.length} bytes)`);
|
|
8488
8673
|
trackHookEvent2(getInstanceId2(), "hook_payload_parse_error", {
|
|
8489
8674
|
event_type: eventType,
|
|
8490
8675
|
cli,
|
|
8491
|
-
payload_size:
|
|
8676
|
+
payload_size: stdinPayload.length
|
|
8492
8677
|
});
|
|
8493
8678
|
}
|
|
8494
8679
|
}
|
|
8495
|
-
|
|
8496
|
-
const tc = parsed.toolCall;
|
|
8497
|
-
if (tc && typeof tc === "object") {
|
|
8498
|
-
if (tc.name !== undefined)
|
|
8499
|
-
parsed.tool_name = tc.name;
|
|
8500
|
-
if (tc.args !== undefined)
|
|
8501
|
-
parsed.tool_input = tc.args;
|
|
8502
|
-
}
|
|
8503
|
-
if (typeof parsed.conversationId === "string")
|
|
8504
|
-
parsed.session_id = parsed.conversationId;
|
|
8505
|
-
if (Array.isArray(parsed.workspacePaths) && typeof parsed.workspacePaths[0] === "string") {
|
|
8506
|
-
parsed.cwd = parsed.workspacePaths[0];
|
|
8507
|
-
}
|
|
8508
|
-
if (typeof parsed.transcriptPath === "string")
|
|
8509
|
-
parsed.transcript_path = parsed.transcriptPath;
|
|
8510
|
-
}
|
|
8511
|
-
if (cli === "copilot") {
|
|
8512
|
-
if (typeof parsed.toolName === "string" && parsed.tool_name === undefined) {
|
|
8513
|
-
parsed.tool_name = parsed.toolName;
|
|
8514
|
-
}
|
|
8515
|
-
if (parsed.toolInput !== undefined && parsed.tool_input === undefined) {
|
|
8516
|
-
parsed.tool_input = parsed.toolInput;
|
|
8517
|
-
}
|
|
8518
|
-
if (typeof parsed.sessionId === "string" && parsed.session_id === undefined) {
|
|
8519
|
-
parsed.session_id = parsed.sessionId;
|
|
8520
|
-
}
|
|
8521
|
-
}
|
|
8522
|
-
if (cli === "goose") {
|
|
8523
|
-
if (typeof parsed.working_dir === "string")
|
|
8524
|
-
parsed.cwd = parsed.working_dir;
|
|
8525
|
-
if (typeof parsed.event === "string" && parsed.hook_event_name === undefined) {
|
|
8526
|
-
parsed.hook_event_name = parsed.event;
|
|
8527
|
-
}
|
|
8528
|
-
}
|
|
8680
|
+
normalizeCliPayload(cli, parsed);
|
|
8529
8681
|
const canonicalEventType = canonicalizeEventType(eventType, cli);
|
|
8530
8682
|
const rawToolName = parsed.tool_name;
|
|
8531
8683
|
const canonicalToolName = canonicalizeToolName(rawToolName, cli);
|
|
@@ -8541,82 +8693,86 @@ async function handleHookEvent(eventType, cli = "claude") {
|
|
|
8541
8693
|
const session = {
|
|
8542
8694
|
sessionId,
|
|
8543
8695
|
transcriptPath: resolveTranscriptPath(cli, parsed, sessionId),
|
|
8544
|
-
cwd: resolveCwd(cli, parsed),
|
|
8696
|
+
cwd: resolveCwd(cli, parsed) ?? opts?.fallbackCwd,
|
|
8545
8697
|
permissionMode: resolvePermissionMode(cli, parsed, sessionId),
|
|
8546
8698
|
hookEventName: parsed.hook_event_name,
|
|
8547
8699
|
rawHookEventName: eventType,
|
|
8548
8700
|
cli
|
|
8549
8701
|
};
|
|
8550
|
-
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
|
|
8557
|
-
|
|
8558
|
-
|
|
8559
|
-
|
|
8560
|
-
|
|
8561
|
-
|
|
8562
|
-
|
|
8563
|
-
|
|
8564
|
-
|
|
8565
|
-
const conventionScope = hook.__conventionScope;
|
|
8566
|
-
const isConvention = !!conventionScope;
|
|
8567
|
-
const prefix = isConvention ? `.failproofai-${conventionScope}` : "custom";
|
|
8568
|
-
const fn = async (ctx) => {
|
|
8569
|
-
try {
|
|
8570
|
-
const result2 = await Promise.race([
|
|
8571
|
-
hook.fn(ctx),
|
|
8572
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), 1e4))
|
|
8573
|
-
]);
|
|
8574
|
-
return result2;
|
|
8575
|
-
} catch (err) {
|
|
8576
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
8577
|
-
const isTimeout = msg === "timeout";
|
|
8578
|
-
hookLogWarn(`${prefix} hook "${hookName}" failed: ${msg}`);
|
|
8579
|
-
trackHookEvent2(getInstanceId2(), "custom_hook_error", {
|
|
8580
|
-
hook_name: hookName,
|
|
8581
|
-
error_type: isTimeout ? "timeout" : "exception",
|
|
8582
|
-
event_type: eventType,
|
|
8583
|
-
cli,
|
|
8584
|
-
is_convention_policy: isConvention,
|
|
8585
|
-
convention_scope: conventionScope ?? null
|
|
8586
|
-
});
|
|
8587
|
-
return { decision: "allow" };
|
|
8588
|
-
}
|
|
8589
|
-
};
|
|
8590
|
-
registerPolicy(`${prefix}/${hookName}`, hook.description ?? "", fn, hook.match ?? {}, -1);
|
|
8591
|
-
}
|
|
8592
|
-
if (customHooksList.length > 0) {
|
|
8593
|
-
trackHookEvent2(getInstanceId2(), "custom_hooks_loaded", {
|
|
8594
|
-
cli,
|
|
8595
|
-
custom_hooks_count: customHooksList.length,
|
|
8596
|
-
custom_hook_names: customHooksList.map((h) => h.name),
|
|
8597
|
-
event_types_covered: [...new Set(customHooksList.flatMap((h) => h.match?.events ?? []))]
|
|
8598
|
-
});
|
|
8599
|
-
}
|
|
8600
|
-
if (loadResult.conventionSources.length > 0) {
|
|
8601
|
-
trackHookEvent2(getInstanceId2(), "convention_policies_loaded", {
|
|
8602
|
-
event_type: canonicalEventType,
|
|
8603
|
-
cli,
|
|
8604
|
-
project_file_count: loadResult.conventionSources.filter((s) => s.scope === "project").length,
|
|
8605
|
-
user_file_count: loadResult.conventionSources.filter((s) => s.scope === "user").length,
|
|
8606
|
-
convention_hook_count: conventionHookNames.size,
|
|
8607
|
-
convention_hook_names: [...conventionHookNames]
|
|
8702
|
+
let config;
|
|
8703
|
+
let customHooksList = [];
|
|
8704
|
+
let conventionHookNames = new Set;
|
|
8705
|
+
if (opts?.forceDecision) {
|
|
8706
|
+
config = { enabledPolicies: [] };
|
|
8707
|
+
clearPolicies();
|
|
8708
|
+
const decision = opts.forceDecision;
|
|
8709
|
+
registerPolicy("failproofai/daemon-unreachable", "Fail-closed: the failproofaid daemon could not be reached for this daemon-configured scope.", async () => ({ decision: decision.decision, reason: decision.reason }), {});
|
|
8710
|
+
} else {
|
|
8711
|
+
config = readMergedHooksConfig(session.cwd);
|
|
8712
|
+
clearPolicies();
|
|
8713
|
+
registerBuiltinPolicies(config.enabledPolicies);
|
|
8714
|
+
const loadResult = await loadAllCustomHooks(config.customPoliciesPaths ?? config.customPoliciesPath, {
|
|
8715
|
+
sessionCwd: session.cwd,
|
|
8716
|
+
customPoliciesEnabled: config.customPoliciesEnabled
|
|
8608
8717
|
});
|
|
8718
|
+
customHooksList = loadResult.hooks;
|
|
8719
|
+
const disabledCustomPolicies = new Set(config.disabledCustomPolicies ?? []);
|
|
8720
|
+
conventionHookNames = new Set(loadResult.conventionSources.flatMap((s) => s.hookNames));
|
|
8721
|
+
for (const hook of customHooksList) {
|
|
8722
|
+
const policyId = hook.__policyId;
|
|
8723
|
+
if (policyId && disabledCustomPolicies.has(policyId))
|
|
8724
|
+
continue;
|
|
8725
|
+
const hookName = hook.name;
|
|
8726
|
+
const conventionScope = hook.__conventionScope;
|
|
8727
|
+
const isConvention = !!conventionScope;
|
|
8728
|
+
const prefix = isConvention ? `.failproofai-${conventionScope}` : "custom";
|
|
8729
|
+
const fn = async (ctx) => {
|
|
8730
|
+
try {
|
|
8731
|
+
const result2 = await Promise.race([
|
|
8732
|
+
hook.fn(ctx),
|
|
8733
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), 1e4))
|
|
8734
|
+
]);
|
|
8735
|
+
return result2;
|
|
8736
|
+
} catch (err) {
|
|
8737
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
8738
|
+
const isTimeout = msg === "timeout";
|
|
8739
|
+
hookLogWarn(`${prefix} hook "${hookName}" failed: ${msg}`);
|
|
8740
|
+
trackHookEvent2(getInstanceId2(), "custom_hook_error", {
|
|
8741
|
+
hook_name: hookName,
|
|
8742
|
+
error_type: isTimeout ? "timeout" : "exception",
|
|
8743
|
+
event_type: eventType,
|
|
8744
|
+
cli,
|
|
8745
|
+
is_convention_policy: isConvention,
|
|
8746
|
+
convention_scope: conventionScope ?? null
|
|
8747
|
+
});
|
|
8748
|
+
return { decision: "allow" };
|
|
8749
|
+
}
|
|
8750
|
+
};
|
|
8751
|
+
registerPolicy(`${prefix}/${hookName}`, hook.description ?? "", fn, hook.match ?? {}, -1);
|
|
8752
|
+
}
|
|
8753
|
+
if (customHooksList.length > 0) {
|
|
8754
|
+
trackHookEvent2(getInstanceId2(), "custom_hooks_loaded", {
|
|
8755
|
+
cli,
|
|
8756
|
+
custom_hooks_count: customHooksList.length,
|
|
8757
|
+
custom_hook_names: customHooksList.map((h) => h.name),
|
|
8758
|
+
event_types_covered: [...new Set(customHooksList.flatMap((h) => h.match?.events ?? []))]
|
|
8759
|
+
});
|
|
8760
|
+
}
|
|
8761
|
+
if (loadResult.conventionSources.length > 0) {
|
|
8762
|
+
trackHookEvent2(getInstanceId2(), "convention_policies_loaded", {
|
|
8763
|
+
event_type: canonicalEventType,
|
|
8764
|
+
cli,
|
|
8765
|
+
project_file_count: loadResult.conventionSources.filter((s) => s.scope === "project").length,
|
|
8766
|
+
user_file_count: loadResult.conventionSources.filter((s) => s.scope === "user").length,
|
|
8767
|
+
convention_hook_count: conventionHookNames.size,
|
|
8768
|
+
convention_hook_names: [...conventionHookNames]
|
|
8769
|
+
});
|
|
8770
|
+
}
|
|
8771
|
+
hookLogInfo(`event=${canonicalEventType} cli=${cli} policies=${config.enabledPolicies.length} custom=${customHooksList.length} convention=${conventionHookNames.size}`);
|
|
8609
8772
|
}
|
|
8610
|
-
hookLogInfo(`event=${canonicalEventType} cli=${cli} policies=${config.enabledPolicies.length} custom=${customHooksList.length} convention=${conventionHookNames.size}`);
|
|
8611
8773
|
const result = await evaluatePolicies(canonicalEventType, parsed, session, config);
|
|
8612
8774
|
const durationMs = Math.round(performance.now() - startTime);
|
|
8613
8775
|
hookLogInfo(`result=${result.decision} policy=${result.policyName ?? "none"} duration=${durationMs}ms`);
|
|
8614
|
-
if (result.stdout) {
|
|
8615
|
-
process.stdout.write(result.stdout);
|
|
8616
|
-
}
|
|
8617
|
-
if (result.stderr) {
|
|
8618
|
-
process.stderr.write(result.stderr);
|
|
8619
|
-
}
|
|
8620
8776
|
const matchedPolicies = getPoliciesForEvent(canonicalEventType, parsed.tool_name).map((p) => p.name);
|
|
8621
8777
|
const activityEntry = {
|
|
8622
8778
|
timestamp: Date.now(),
|
|
@@ -8648,7 +8804,7 @@ async function handleHookEvent(eventType, cli = "claude") {
|
|
|
8648
8804
|
const hasCustomParams = !isCustomHook && !isConventionPolicy && !!(result.policyName && config.policyParams?.[result.policyName]);
|
|
8649
8805
|
const paramKeysOverridden = hasCustomParams ? Object.keys(config.policyParams[result.policyName]) : [];
|
|
8650
8806
|
const distinctId = getInstanceId2();
|
|
8651
|
-
|
|
8807
|
+
const trackPromise = trackHookEvent2(distinctId, "hook_policy_triggered", {
|
|
8652
8808
|
event_type: canonicalEventType,
|
|
8653
8809
|
cli,
|
|
8654
8810
|
tool_name: parsed.tool_name ?? null,
|
|
@@ -8660,12 +8816,45 @@ async function handleHookEvent(eventType, cli = "claude") {
|
|
|
8660
8816
|
has_custom_params: hasCustomParams,
|
|
8661
8817
|
param_keys_overridden: paramKeysOverridden
|
|
8662
8818
|
});
|
|
8819
|
+
if (opts?.awaitTelemetryFlush ?? true) {
|
|
8820
|
+
await trackPromise;
|
|
8821
|
+
}
|
|
8663
8822
|
} catch {}
|
|
8664
8823
|
}
|
|
8665
|
-
return result.exitCode;
|
|
8824
|
+
return { exitCode: result.exitCode, stdout: result.stdout, stderr: result.stderr };
|
|
8666
8825
|
} finally {
|
|
8667
|
-
|
|
8826
|
+
if (opts?.awaitTelemetryFlush ?? true) {
|
|
8827
|
+
await flushHookTelemetry2();
|
|
8828
|
+
}
|
|
8829
|
+
}
|
|
8830
|
+
}
|
|
8831
|
+
async function handleHookEvent(eventType, cli = "claude") {
|
|
8832
|
+
const MAX_STDIN_BYTES = 1048576;
|
|
8833
|
+
const stdinRead = await readStdinPayload2(MAX_STDIN_BYTES);
|
|
8834
|
+
if (stdinRead.readError) {
|
|
8835
|
+
hookLogWarn(`stdin read failed for ${eventType}`);
|
|
8836
|
+
trackHookEvent2(getInstanceId2(), "hook_stdin_error", {
|
|
8837
|
+
event_type: eventType,
|
|
8838
|
+
cli,
|
|
8839
|
+
error_type: "unknown"
|
|
8840
|
+
});
|
|
8841
|
+
}
|
|
8842
|
+
if (stdinRead.oversized) {
|
|
8843
|
+
hookLogWarn(`stdin payload exceeds 1 MB for ${eventType}, discarding`);
|
|
8844
|
+
trackHookEvent2(getInstanceId2(), "hook_stdin_error", {
|
|
8845
|
+
event_type: eventType,
|
|
8846
|
+
cli,
|
|
8847
|
+
error_type: "oversized"
|
|
8848
|
+
});
|
|
8849
|
+
}
|
|
8850
|
+
const result = await evaluateHookEvent(eventType, cli, stdinRead.payload);
|
|
8851
|
+
if (result.stdout) {
|
|
8852
|
+
process.stdout.write(result.stdout);
|
|
8853
|
+
}
|
|
8854
|
+
if (result.stderr) {
|
|
8855
|
+
process.stderr.write(result.stderr);
|
|
8668
8856
|
}
|
|
8857
|
+
return result.exitCode;
|
|
8669
8858
|
}
|
|
8670
8859
|
var init_handler = __esm(() => {
|
|
8671
8860
|
init_types();
|
|
@@ -8686,13 +8875,13 @@ var init_handler = __esm(() => {
|
|
|
8686
8875
|
import { createHmac, randomUUID } from "node:crypto";
|
|
8687
8876
|
import { execSync as execSync4 } from "node:child_process";
|
|
8688
8877
|
import { readFileSync as readFileSync10, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5 } from "node:fs";
|
|
8689
|
-
import { homedir as
|
|
8878
|
+
import { homedir as homedir22, platform, arch, hostname, cpus } from "node:os";
|
|
8690
8879
|
import { join as join25 } from "node:path";
|
|
8691
8880
|
function hashToId3(raw) {
|
|
8692
8881
|
return createHmac("sha256", NAMESPACE3).update(raw).digest("hex");
|
|
8693
8882
|
}
|
|
8694
8883
|
function getInstanceId3() {
|
|
8695
|
-
const idDir = join25(
|
|
8884
|
+
const idDir = join25(homedir22(), ".failproofai");
|
|
8696
8885
|
const idFile = join25(idDir, "instance-id");
|
|
8697
8886
|
try {
|
|
8698
8887
|
const existing = readFileSync10(idFile, "utf-8").trim();
|
|
@@ -8727,7 +8916,7 @@ function getInstanceId3() {
|
|
|
8727
8916
|
try {
|
|
8728
8917
|
const sysProps = [
|
|
8729
8918
|
hostname(),
|
|
8730
|
-
|
|
8919
|
+
homedir22(),
|
|
8731
8920
|
platform(),
|
|
8732
8921
|
arch(),
|
|
8733
8922
|
cpus()[0]?.model ?? ""
|
|
@@ -8774,14 +8963,14 @@ __export(exports_install_check, {
|
|
|
8774
8963
|
checkHooks: () => checkHooks
|
|
8775
8964
|
});
|
|
8776
8965
|
import { existsSync as existsSync10, readFileSync as readFileSync11, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6 } from "node:fs";
|
|
8777
|
-
import { resolve as
|
|
8778
|
-
import { platform as platform2, arch as arch2, release, homedir as
|
|
8966
|
+
import { resolve as resolve11 } from "node:path";
|
|
8967
|
+
import { platform as platform2, arch as arch2, release, homedir as homedir23, hostname as hostname2 } from "node:os";
|
|
8779
8968
|
import { createHmac as createHmac2 } from "node:crypto";
|
|
8780
8969
|
function hashToId4(raw) {
|
|
8781
8970
|
return createHmac2("sha256", NAMESPACE4).update(raw).digest("hex");
|
|
8782
8971
|
}
|
|
8783
8972
|
function lastVersionPath() {
|
|
8784
|
-
return
|
|
8973
|
+
return resolve11(homedir23(), ".failproofai", "last-version");
|
|
8785
8974
|
}
|
|
8786
8975
|
function compareSemver(a, b) {
|
|
8787
8976
|
const parse = (v) => {
|
|
@@ -8828,7 +9017,7 @@ function compareSemver(a, b) {
|
|
|
8828
9017
|
return 0;
|
|
8829
9018
|
}
|
|
8830
9019
|
function checkHooks() {
|
|
8831
|
-
const hooksConfigPath =
|
|
9020
|
+
const hooksConfigPath = resolve11(homedir23(), ".failproofai", "policies-config.json");
|
|
8832
9021
|
if (!existsSync10(hooksConfigPath)) {
|
|
8833
9022
|
return { configured: false, registered: false, policyCount: 0 };
|
|
8834
9023
|
}
|
|
@@ -8842,7 +9031,7 @@ function checkHooks() {
|
|
|
8842
9031
|
return { configured: false, registered: false, policyCount: 0 };
|
|
8843
9032
|
}
|
|
8844
9033
|
const policyCount = config.enabledPolicies.length;
|
|
8845
|
-
const settingsPath =
|
|
9034
|
+
const settingsPath = resolve11(homedir23(), ".claude", "settings.json");
|
|
8846
9035
|
if (!existsSync10(settingsPath))
|
|
8847
9036
|
return { configured: true, registered: false, policyCount };
|
|
8848
9037
|
let settings;
|
|
@@ -8907,7 +9096,7 @@ async function maybeReportInstall(version3) {
|
|
|
8907
9096
|
enabled_policy_count: hooks.policyCount
|
|
8908
9097
|
}, { version: version3, timeoutMs: REPORT_TIMEOUT_MS }));
|
|
8909
9098
|
try {
|
|
8910
|
-
mkdirSync6(
|
|
9099
|
+
mkdirSync6(resolve11(homedir23(), ".failproofai"), { recursive: true });
|
|
8911
9100
|
writeFileSync5(versionFile, version3, "utf8");
|
|
8912
9101
|
} catch {}
|
|
8913
9102
|
await Promise.all(events.map((p) => p.catch(() => {})));
|
|
@@ -15911,9 +16100,9 @@ var init_dist = __esm(() => {
|
|
|
15911
16100
|
// src/hooks/integrations.ts
|
|
15912
16101
|
import { execSync as execSync5 } from "node:child_process";
|
|
15913
16102
|
import { readFileSync as readFileSync12, writeFileSync as writeFileSync6, existsSync as existsSync11, mkdirSync as mkdirSync7, unlinkSync as unlinkSync2 } from "node:fs";
|
|
15914
|
-
import { resolve as
|
|
16103
|
+
import { resolve as resolve12, dirname as dirname5 } from "node:path";
|
|
15915
16104
|
import { fileURLToPath } from "node:url";
|
|
15916
|
-
import { homedir as
|
|
16105
|
+
import { homedir as homedir24 } from "node:os";
|
|
15917
16106
|
function readJsonFile(path3) {
|
|
15918
16107
|
if (!existsSync11(path3))
|
|
15919
16108
|
return {};
|
|
@@ -15977,7 +16166,7 @@ function isMarkedCopilotHook(hook) {
|
|
|
15977
16166
|
return false;
|
|
15978
16167
|
}
|
|
15979
16168
|
function opencodePluginFilePath(settingsPath) {
|
|
15980
|
-
return
|
|
16169
|
+
return resolve12(dirname5(settingsPath), "plugins", "failproofai.mjs");
|
|
15981
16170
|
}
|
|
15982
16171
|
function buildOpenCodePluginShim(binaryPath, scope) {
|
|
15983
16172
|
const useNpx = scope === "project";
|
|
@@ -16192,8 +16381,8 @@ export default async function failproofaiPlugin({ client, directory }) {
|
|
|
16192
16381
|
function getPiExtensionPath() {
|
|
16193
16382
|
const fromEnv = process.env.FAILPROOFAI_PACKAGE_ROOT;
|
|
16194
16383
|
if (fromEnv)
|
|
16195
|
-
return
|
|
16196
|
-
return
|
|
16384
|
+
return resolve12(fromEnv, "pi-extension");
|
|
16385
|
+
return resolve12(fileURLToPath(import.meta.url), "..", "..", "..", "pi-extension");
|
|
16197
16386
|
}
|
|
16198
16387
|
function isFailproofaiPiEntry(source) {
|
|
16199
16388
|
if (typeof source !== "string")
|
|
@@ -16204,8 +16393,8 @@ function isFailproofaiPiEntry(source) {
|
|
|
16204
16393
|
}
|
|
16205
16394
|
function makePiProjectRelativeEntry(extPath) {
|
|
16206
16395
|
const cwd = process.cwd();
|
|
16207
|
-
const cwdResolved =
|
|
16208
|
-
const extResolved =
|
|
16396
|
+
const cwdResolved = resolve12(cwd);
|
|
16397
|
+
const extResolved = resolve12(extPath);
|
|
16209
16398
|
if (extResolved.startsWith(cwdResolved + "/") || extResolved === cwdResolved) {
|
|
16210
16399
|
const fromSettingsDir = "../" + extResolved.slice(cwdResolved.length + 1);
|
|
16211
16400
|
return fromSettingsDir;
|
|
@@ -16229,13 +16418,13 @@ function hermesConfigHasHooks(settingsPath) {
|
|
|
16229
16418
|
return false;
|
|
16230
16419
|
}
|
|
16231
16420
|
function unhookedHermesProfiles() {
|
|
16232
|
-
return listHermesProfiles().filter((p) => existsSync11(p.home)).filter((p) => !hermesConfigHasHooks(
|
|
16421
|
+
return listHermesProfiles().filter((p) => existsSync11(p.home)).filter((p) => !hermesConfigHasHooks(resolve12(p.home, "config.yaml"))).map((p) => p.name);
|
|
16233
16422
|
}
|
|
16234
16423
|
function getOpenClawPluginPath() {
|
|
16235
16424
|
const fromEnv = process.env.FAILPROOFAI_PACKAGE_ROOT;
|
|
16236
16425
|
if (fromEnv)
|
|
16237
|
-
return
|
|
16238
|
-
return
|
|
16426
|
+
return resolve12(fromEnv, "openclaw-plugin");
|
|
16427
|
+
return resolve12(fileURLToPath(import.meta.url), "..", "..", "..", "openclaw-plugin");
|
|
16239
16428
|
}
|
|
16240
16429
|
function isFailproofaiOpenClawPath(p) {
|
|
16241
16430
|
if (typeof p !== "string")
|
|
@@ -16272,14 +16461,14 @@ var init_integrations = __esm(() => {
|
|
|
16272
16461
|
scopes: HOOK_SCOPES,
|
|
16273
16462
|
eventTypes: CLAUDE_INSTALL_EVENT_TYPES,
|
|
16274
16463
|
getSettingsPath(scope, cwd) {
|
|
16275
|
-
const base = cwd ?
|
|
16464
|
+
const base = cwd ? resolve12(cwd) : process.cwd();
|
|
16276
16465
|
switch (scope) {
|
|
16277
16466
|
case "user":
|
|
16278
|
-
return
|
|
16467
|
+
return resolve12(homedir24(), ".claude", "settings.json");
|
|
16279
16468
|
case "project":
|
|
16280
|
-
return
|
|
16469
|
+
return resolve12(base, ".claude", "settings.json");
|
|
16281
16470
|
case "local":
|
|
16282
|
-
return
|
|
16471
|
+
return resolve12(base, ".claude", "settings.local.json");
|
|
16283
16472
|
}
|
|
16284
16473
|
},
|
|
16285
16474
|
readSettings(settingsPath) {
|
|
@@ -16404,14 +16593,14 @@ var init_integrations = __esm(() => {
|
|
|
16404
16593
|
scopes: CODEX_HOOK_SCOPES,
|
|
16405
16594
|
eventTypes: CODEX_HOOK_EVENT_TYPES,
|
|
16406
16595
|
getSettingsPath(scope, cwd) {
|
|
16407
|
-
const base = cwd ?
|
|
16596
|
+
const base = cwd ? resolve12(cwd) : process.cwd();
|
|
16408
16597
|
switch (scope) {
|
|
16409
16598
|
case "user":
|
|
16410
|
-
return
|
|
16599
|
+
return resolve12(homedir24(), ".codex", "hooks.json");
|
|
16411
16600
|
case "project":
|
|
16412
|
-
return
|
|
16601
|
+
return resolve12(base, ".codex", "hooks.json");
|
|
16413
16602
|
case "local":
|
|
16414
|
-
return
|
|
16603
|
+
return resolve12(base, ".codex", "hooks.json");
|
|
16415
16604
|
}
|
|
16416
16605
|
},
|
|
16417
16606
|
readSettings(settingsPath) {
|
|
@@ -16520,14 +16709,14 @@ var init_integrations = __esm(() => {
|
|
|
16520
16709
|
scopes: COPILOT_HOOK_SCOPES,
|
|
16521
16710
|
eventTypes: COPILOT_HOOK_EVENT_TYPES,
|
|
16522
16711
|
getSettingsPath(scope, cwd) {
|
|
16523
|
-
const base = cwd ?
|
|
16712
|
+
const base = cwd ? resolve12(cwd) : process.cwd();
|
|
16524
16713
|
switch (scope) {
|
|
16525
16714
|
case "user":
|
|
16526
|
-
return
|
|
16715
|
+
return resolve12(homedir24(), ".copilot", "hooks", "failproofai.json");
|
|
16527
16716
|
case "project":
|
|
16528
|
-
return
|
|
16717
|
+
return resolve12(base, ".github", "hooks", "failproofai.json");
|
|
16529
16718
|
case "local":
|
|
16530
|
-
return
|
|
16719
|
+
return resolve12(base, ".github", "hooks", "failproofai.json");
|
|
16531
16720
|
}
|
|
16532
16721
|
},
|
|
16533
16722
|
readSettings(settingsPath) {
|
|
@@ -16634,14 +16823,14 @@ var init_integrations = __esm(() => {
|
|
|
16634
16823
|
scopes: CURSOR_HOOK_SCOPES,
|
|
16635
16824
|
eventTypes: CURSOR_HOOK_EVENT_TYPES,
|
|
16636
16825
|
getSettingsPath(scope, cwd) {
|
|
16637
|
-
const base = cwd ?
|
|
16826
|
+
const base = cwd ? resolve12(cwd) : process.cwd();
|
|
16638
16827
|
switch (scope) {
|
|
16639
16828
|
case "user":
|
|
16640
|
-
return
|
|
16829
|
+
return resolve12(homedir24(), ".cursor", "hooks.json");
|
|
16641
16830
|
case "project":
|
|
16642
|
-
return
|
|
16831
|
+
return resolve12(base, ".cursor", "hooks.json");
|
|
16643
16832
|
case "local":
|
|
16644
|
-
return
|
|
16833
|
+
return resolve12(base, ".cursor", "hooks.json");
|
|
16645
16834
|
}
|
|
16646
16835
|
},
|
|
16647
16836
|
readSettings(settingsPath) {
|
|
@@ -16733,14 +16922,14 @@ var init_integrations = __esm(() => {
|
|
|
16733
16922
|
scopes: OPENCODE_HOOK_SCOPES,
|
|
16734
16923
|
eventTypes: OPENCODE_HOOK_EVENT_TYPES,
|
|
16735
16924
|
getSettingsPath(scope, cwd) {
|
|
16736
|
-
const base = cwd ?
|
|
16925
|
+
const base = cwd ? resolve12(cwd) : process.cwd();
|
|
16737
16926
|
switch (scope) {
|
|
16738
16927
|
case "user":
|
|
16739
|
-
return
|
|
16928
|
+
return resolve12(homedir24(), ".config", "opencode", "opencode.json");
|
|
16740
16929
|
case "project":
|
|
16741
|
-
return
|
|
16930
|
+
return resolve12(base, ".opencode", "opencode.json");
|
|
16742
16931
|
case "local":
|
|
16743
|
-
return
|
|
16932
|
+
return resolve12(base, ".opencode", "opencode.json");
|
|
16744
16933
|
}
|
|
16745
16934
|
},
|
|
16746
16935
|
readSettings(settingsPath) {
|
|
@@ -16751,7 +16940,7 @@ var init_integrations = __esm(() => {
|
|
|
16751
16940
|
},
|
|
16752
16941
|
buildHookEntry(_binaryPath, _eventType, scope) {
|
|
16753
16942
|
if (scope === "user") {
|
|
16754
|
-
const abs =
|
|
16943
|
+
const abs = resolve12(homedir24(), ".config", "opencode", "plugins", "failproofai.mjs");
|
|
16755
16944
|
return { spec: `file://${abs}`, [FAILPROOFAI_HOOK_MARKER]: true };
|
|
16756
16945
|
}
|
|
16757
16946
|
return { spec: OPENCODE_PLUGIN_REL_PATH, [FAILPROOFAI_HOOK_MARKER]: true };
|
|
@@ -16766,7 +16955,7 @@ var init_integrations = __esm(() => {
|
|
|
16766
16955
|
writeHookEntries(settings, binaryPath, scope) {
|
|
16767
16956
|
const s = settings;
|
|
16768
16957
|
const effectiveScope = scope ?? "project";
|
|
16769
|
-
const settingsPath = effectiveScope === "user" ?
|
|
16958
|
+
const settingsPath = effectiveScope === "user" ? resolve12(homedir24(), ".config", "opencode", "opencode.json") : resolve12(process.cwd(), ".opencode", "opencode.json");
|
|
16770
16959
|
const pluginPath = opencodePluginFilePath(settingsPath);
|
|
16771
16960
|
mkdirSync7(dirname5(pluginPath), { recursive: true });
|
|
16772
16961
|
writeFileSync6(pluginPath, buildOpenCodePluginShim(binaryPath, effectiveScope), "utf8");
|
|
@@ -16834,14 +17023,14 @@ var init_integrations = __esm(() => {
|
|
|
16834
17023
|
scopes: PI_HOOK_SCOPES,
|
|
16835
17024
|
eventTypes: PI_HOOK_EVENT_TYPES,
|
|
16836
17025
|
getSettingsPath(scope, cwd) {
|
|
16837
|
-
const base = cwd ?
|
|
17026
|
+
const base = cwd ? resolve12(cwd) : process.cwd();
|
|
16838
17027
|
switch (scope) {
|
|
16839
17028
|
case "user":
|
|
16840
|
-
return
|
|
17029
|
+
return resolve12(homedir24(), ".pi", "agent", "settings.json");
|
|
16841
17030
|
case "project":
|
|
16842
|
-
return
|
|
17031
|
+
return resolve12(base, ".pi", "settings.json");
|
|
16843
17032
|
case "local":
|
|
16844
|
-
return
|
|
17033
|
+
return resolve12(base, ".pi", "settings.json");
|
|
16845
17034
|
}
|
|
16846
17035
|
},
|
|
16847
17036
|
readSettings(settingsPath) {
|
|
@@ -16919,10 +17108,10 @@ var init_integrations = __esm(() => {
|
|
|
16919
17108
|
scopes: HERMES_HOOK_SCOPES,
|
|
16920
17109
|
eventTypes: HERMES_HOOK_EVENT_TYPES,
|
|
16921
17110
|
getSettingsPath() {
|
|
16922
|
-
return
|
|
17111
|
+
return resolve12(hermesRoot(), "config.yaml");
|
|
16923
17112
|
},
|
|
16924
17113
|
getSettingsPaths() {
|
|
16925
|
-
return listHermesProfiles().map((p) =>
|
|
17114
|
+
return listHermesProfiles().map((p) => resolve12(p.home, "config.yaml"));
|
|
16926
17115
|
},
|
|
16927
17116
|
readSettings(settingsPath) {
|
|
16928
17117
|
return readYamlDoc(settingsPath);
|
|
@@ -17002,7 +17191,7 @@ var init_integrations = __esm(() => {
|
|
|
17002
17191
|
scopes: OPENCLAW_HOOK_SCOPES,
|
|
17003
17192
|
eventTypes: OPENCLAW_HOOK_EVENT_TYPES,
|
|
17004
17193
|
getSettingsPath() {
|
|
17005
|
-
return
|
|
17194
|
+
return resolve12(homedir24(), ".openclaw", "openclaw.json");
|
|
17006
17195
|
},
|
|
17007
17196
|
readSettings(settingsPath) {
|
|
17008
17197
|
return readJsonFile(settingsPath);
|
|
@@ -17098,14 +17287,14 @@ var init_integrations = __esm(() => {
|
|
|
17098
17287
|
scopes: FACTORY_HOOK_SCOPES,
|
|
17099
17288
|
eventTypes: FACTORY_HOOK_EVENT_TYPES,
|
|
17100
17289
|
getSettingsPath(scope, cwd) {
|
|
17101
|
-
const base = cwd ?
|
|
17290
|
+
const base = cwd ? resolve12(cwd) : process.cwd();
|
|
17102
17291
|
switch (scope) {
|
|
17103
17292
|
case "user":
|
|
17104
|
-
return
|
|
17293
|
+
return resolve12(homedir24(), ".factory", "hooks.json");
|
|
17105
17294
|
case "project":
|
|
17106
|
-
return
|
|
17295
|
+
return resolve12(base, ".factory", "hooks.json");
|
|
17107
17296
|
case "local":
|
|
17108
|
-
return
|
|
17297
|
+
return resolve12(base, ".factory", "hooks.json");
|
|
17109
17298
|
}
|
|
17110
17299
|
},
|
|
17111
17300
|
readSettings(settingsPath) {
|
|
@@ -17200,14 +17389,14 @@ var init_integrations = __esm(() => {
|
|
|
17200
17389
|
scopes: DEVIN_HOOK_SCOPES,
|
|
17201
17390
|
eventTypes: DEVIN_HOOK_EVENT_TYPES,
|
|
17202
17391
|
getSettingsPath(scope, cwd) {
|
|
17203
|
-
const base = cwd ?
|
|
17392
|
+
const base = cwd ? resolve12(cwd) : process.cwd();
|
|
17204
17393
|
switch (scope) {
|
|
17205
17394
|
case "user":
|
|
17206
|
-
return
|
|
17395
|
+
return resolve12(homedir24(), ".config", "devin", "config.json");
|
|
17207
17396
|
case "project":
|
|
17208
|
-
return
|
|
17397
|
+
return resolve12(base, ".devin", "config.json");
|
|
17209
17398
|
case "local":
|
|
17210
|
-
return
|
|
17399
|
+
return resolve12(base, ".devin", "config.json");
|
|
17211
17400
|
}
|
|
17212
17401
|
},
|
|
17213
17402
|
readSettings(settingsPath) {
|
|
@@ -17309,14 +17498,14 @@ var init_integrations = __esm(() => {
|
|
|
17309
17498
|
scopes: ANTIGRAVITY_HOOK_SCOPES,
|
|
17310
17499
|
eventTypes: ANTIGRAVITY_HOOK_EVENT_TYPES,
|
|
17311
17500
|
getSettingsPath(scope, cwd) {
|
|
17312
|
-
const base = cwd ?
|
|
17501
|
+
const base = cwd ? resolve12(cwd) : process.cwd();
|
|
17313
17502
|
switch (scope) {
|
|
17314
17503
|
case "user":
|
|
17315
|
-
return
|
|
17504
|
+
return resolve12(homedir24(), ".gemini", "config", "hooks.json");
|
|
17316
17505
|
case "project":
|
|
17317
|
-
return
|
|
17506
|
+
return resolve12(base, ".agents", "hooks.json");
|
|
17318
17507
|
case "local":
|
|
17319
|
-
return
|
|
17508
|
+
return resolve12(base, ".agents", "hooks.json");
|
|
17320
17509
|
}
|
|
17321
17510
|
},
|
|
17322
17511
|
readSettings(settingsPath) {
|
|
@@ -17450,14 +17639,14 @@ var init_integrations = __esm(() => {
|
|
|
17450
17639
|
scopes: GOOSE_HOOK_SCOPES,
|
|
17451
17640
|
eventTypes: GOOSE_HOOK_EVENT_TYPES,
|
|
17452
17641
|
getSettingsPath(scope, cwd) {
|
|
17453
|
-
const base = cwd ?
|
|
17642
|
+
const base = cwd ? resolve12(cwd) : process.cwd();
|
|
17454
17643
|
switch (scope) {
|
|
17455
17644
|
case "user":
|
|
17456
|
-
return
|
|
17645
|
+
return resolve12(homedir24(), ".agents", "plugins", "failproofai", "hooks", "hooks.json");
|
|
17457
17646
|
case "project":
|
|
17458
|
-
return
|
|
17647
|
+
return resolve12(base, ".agents", "plugins", "failproofai", "hooks", "hooks.json");
|
|
17459
17648
|
case "local":
|
|
17460
|
-
return
|
|
17649
|
+
return resolve12(base, ".agents", "plugins", "failproofai", "hooks", "hooks.json");
|
|
17461
17650
|
}
|
|
17462
17651
|
},
|
|
17463
17652
|
readSettings(settingsPath) {
|
|
@@ -17792,7 +17981,7 @@ function runPrompt(p) {
|
|
|
17792
17981
|
`${c.dim(BAR)} ${c.dim(p.summaryFor(result))}`
|
|
17793
17982
|
]);
|
|
17794
17983
|
};
|
|
17795
|
-
return new Promise((
|
|
17984
|
+
return new Promise((resolve13) => {
|
|
17796
17985
|
hideCursor(stdout);
|
|
17797
17986
|
repaint(stdout, region, build());
|
|
17798
17987
|
readline.emitKeypressEvents(stdin);
|
|
@@ -17808,7 +17997,7 @@ function runPrompt(p) {
|
|
|
17808
17997
|
const finish = (result) => {
|
|
17809
17998
|
cleanup();
|
|
17810
17999
|
collapse(result);
|
|
17811
|
-
|
|
18000
|
+
resolve13(result);
|
|
17812
18001
|
};
|
|
17813
18002
|
function onKey(_s, key) {
|
|
17814
18003
|
if (!key)
|
|
@@ -18109,7 +18298,7 @@ async function promptPolicySelection(preSelected, options = {}) {
|
|
|
18109
18298
|
`);
|
|
18110
18299
|
lastLineCount = lines.length;
|
|
18111
18300
|
}
|
|
18112
|
-
return new Promise((
|
|
18301
|
+
return new Promise((resolve13) => {
|
|
18113
18302
|
render();
|
|
18114
18303
|
process.stdin.setRawMode(true);
|
|
18115
18304
|
process.stdin.resume();
|
|
@@ -18146,7 +18335,7 @@ async function promptPolicySelection(preSelected, options = {}) {
|
|
|
18146
18335
|
${dim(STEP_DONE)} Policies
|
|
18147
18336
|
${dim(BAR)} ${dim(`${selected.length} selected`)}
|
|
18148
18337
|
`);
|
|
18149
|
-
|
|
18338
|
+
resolve13(selected);
|
|
18150
18339
|
} else if (key.name === "escape") {
|
|
18151
18340
|
search = "";
|
|
18152
18341
|
cursor2 = 0;
|
|
@@ -18209,8 +18398,8 @@ __export(exports_manager, {
|
|
|
18209
18398
|
});
|
|
18210
18399
|
import { execSync as execSync6 } from "node:child_process";
|
|
18211
18400
|
import { existsSync as existsSync12 } from "node:fs";
|
|
18212
|
-
import { resolve as
|
|
18213
|
-
import { homedir as
|
|
18401
|
+
import { resolve as resolve13, basename as basename4 } from "node:path";
|
|
18402
|
+
import { homedir as homedir25, platform as platform3, arch as arch3, release as release2, hostname as hostname3 } from "node:os";
|
|
18214
18403
|
function getSettingsPath(scope, cwd) {
|
|
18215
18404
|
return claudeCode.getSettingsPath(scope, cwd);
|
|
18216
18405
|
}
|
|
@@ -18317,8 +18506,8 @@ async function installHooksImpl(policyNames, scope = "user", cwd, includeBeta =
|
|
|
18317
18506
|
delete configToWrite.customPoliciesPath;
|
|
18318
18507
|
delete configToWrite.customPoliciesPaths;
|
|
18319
18508
|
} else if (customPoliciesPath && (typeof customPoliciesPath === "string" || customPoliciesPath.length > 0)) {
|
|
18320
|
-
const incoming = (typeof customPoliciesPath === "string" ? [customPoliciesPath] : customPoliciesPath).map((path3) =>
|
|
18321
|
-
const carried = (previousConfig.customPoliciesPaths ?? (previousConfig.customPoliciesPath ? [previousConfig.customPoliciesPath] : [])).map((path3) =>
|
|
18509
|
+
const incoming = (typeof customPoliciesPath === "string" ? [customPoliciesPath] : customPoliciesPath).map((path3) => resolve13(path3));
|
|
18510
|
+
const carried = (previousConfig.customPoliciesPaths ?? (previousConfig.customPoliciesPath ? [previousConfig.customPoliciesPath] : [])).map((path3) => resolve13(path3)).filter((path3) => {
|
|
18322
18511
|
if (existsSync12(path3))
|
|
18323
18512
|
return true;
|
|
18324
18513
|
console.log(`Dropping custom policies path (file no longer exists): ${path3}`);
|
|
@@ -18710,7 +18899,7 @@ Failproof AI Hook Policies
|
|
|
18710
18899
|
console.log(`
|
|
18711
18900
|
── Custom Policies ───────────────────────`);
|
|
18712
18901
|
for (const path3 of explicitPaths) {
|
|
18713
|
-
const absPath =
|
|
18902
|
+
const absPath = resolve13(findProjectConfigDir(cwd ?? process.cwd()), path3);
|
|
18714
18903
|
console.log(` ${absPath}`);
|
|
18715
18904
|
if (!existsSync12(absPath)) {
|
|
18716
18905
|
console.log(` \x1B[31m✗ File not found: ${absPath}\x1B[0m`);
|
|
@@ -18731,8 +18920,8 @@ Failproof AI Hook Policies
|
|
|
18731
18920
|
console.log();
|
|
18732
18921
|
}
|
|
18733
18922
|
const base = findProjectConfigDir(cwd ?? process.cwd());
|
|
18734
|
-
const projectDir =
|
|
18735
|
-
const userDir =
|
|
18923
|
+
const projectDir = resolve13(base, ".failproofai", "policies");
|
|
18924
|
+
const userDir = resolve13(homedir25(), ".failproofai", "policies");
|
|
18736
18925
|
const sameDir = userDir === projectDir;
|
|
18737
18926
|
const conventionDirs = [
|
|
18738
18927
|
{ label: sameDir ? "Project + User" : "Project", dir: projectDir },
|
|
@@ -18962,7 +19151,7 @@ async function promptCliTargetSelection(detected, action = "install") {
|
|
|
18962
19151
|
lastLineCount = lines.length;
|
|
18963
19152
|
}
|
|
18964
19153
|
const itemCount = options.length;
|
|
18965
|
-
return new Promise((
|
|
19154
|
+
return new Promise((resolve14) => {
|
|
18966
19155
|
render();
|
|
18967
19156
|
readline3.emitKeypressEvents(process.stdin);
|
|
18968
19157
|
const wasRaw = process.stdin.isRaw;
|
|
@@ -18995,7 +19184,7 @@ async function promptCliTargetSelection(detected, action = "install") {
|
|
|
18995
19184
|
cleanup();
|
|
18996
19185
|
process.stdout.write(`
|
|
18997
19186
|
`);
|
|
18998
|
-
|
|
19187
|
+
resolve14(options[cursor2].value);
|
|
18999
19188
|
}
|
|
19000
19189
|
}
|
|
19001
19190
|
process.stdin.on("keypress", onKey);
|
|
@@ -19148,7 +19337,7 @@ async function promptPolicySelection2(preSelected, options = {}) {
|
|
|
19148
19337
|
`);
|
|
19149
19338
|
lastLineCount = lines.length;
|
|
19150
19339
|
}
|
|
19151
|
-
return new Promise((
|
|
19340
|
+
return new Promise((resolve14) => {
|
|
19152
19341
|
render();
|
|
19153
19342
|
process.stdin.setRawMode(true);
|
|
19154
19343
|
process.stdin.resume();
|
|
@@ -19185,7 +19374,7 @@ async function promptPolicySelection2(preSelected, options = {}) {
|
|
|
19185
19374
|
${dim(STEP_DONE)} Policies
|
|
19186
19375
|
${dim(BAR)} ${dim(`${selected.length} selected`)}
|
|
19187
19376
|
`);
|
|
19188
|
-
|
|
19377
|
+
resolve14(selected);
|
|
19189
19378
|
} else if (key.name === "escape") {
|
|
19190
19379
|
search = "";
|
|
19191
19380
|
cursor2 = 0;
|
|
@@ -19369,7 +19558,7 @@ import {
|
|
|
19369
19558
|
mkdirSync as mkdirSync8,
|
|
19370
19559
|
renameSync as renameSync3,
|
|
19371
19560
|
rmSync,
|
|
19372
|
-
statSync as
|
|
19561
|
+
statSync as statSync12,
|
|
19373
19562
|
writeFileSync as writeFileSync7
|
|
19374
19563
|
} from "node:fs";
|
|
19375
19564
|
import { dirname as dirname6 } from "node:path";
|
|
@@ -19384,12 +19573,12 @@ function writeJsonAtomically(filePath, value, opts = {}) {
|
|
|
19384
19573
|
try {
|
|
19385
19574
|
writeFileSync7(tmp, JSON.stringify(value, null, 2), { mode });
|
|
19386
19575
|
try {
|
|
19387
|
-
if ((
|
|
19576
|
+
if ((statSync12(tmp).mode & 63) !== 0)
|
|
19388
19577
|
chmodSync(tmp, mode);
|
|
19389
19578
|
} catch {}
|
|
19390
19579
|
renameSync3(tmp, filePath);
|
|
19391
19580
|
try {
|
|
19392
|
-
if ((
|
|
19581
|
+
if ((statSync12(filePath).mode & 63) !== 0)
|
|
19393
19582
|
chmodSync(filePath, mode);
|
|
19394
19583
|
} catch {}
|
|
19395
19584
|
} catch (err) {
|
|
@@ -19403,13 +19592,13 @@ var init_atomic_write = () => {};
|
|
|
19403
19592
|
|
|
19404
19593
|
// lib/auth/auth-store.ts
|
|
19405
19594
|
import { existsSync as existsSync14, readFileSync as readFileSync13, rmSync as rmSync2 } from "node:fs";
|
|
19406
|
-
import { homedir as
|
|
19595
|
+
import { homedir as homedir26 } from "node:os";
|
|
19407
19596
|
import { join as join26 } from "node:path";
|
|
19408
19597
|
function getAuthDir() {
|
|
19409
19598
|
const override = process.env.FAILPROOFAI_AUTH_DIR;
|
|
19410
19599
|
if (override)
|
|
19411
19600
|
return override;
|
|
19412
|
-
return join26(
|
|
19601
|
+
return join26(homedir26(), ".failproofai");
|
|
19413
19602
|
}
|
|
19414
19603
|
function getAuthFilePath() {
|
|
19415
19604
|
return join26(getAuthDir(), "auth.json");
|
|
@@ -19520,13 +19709,13 @@ function prompt(question, opts = {}) {
|
|
|
19520
19709
|
orig(s);
|
|
19521
19710
|
};
|
|
19522
19711
|
}
|
|
19523
|
-
return new Promise((
|
|
19712
|
+
return new Promise((resolve14) => {
|
|
19524
19713
|
rl.question(question, (answer) => {
|
|
19525
19714
|
rl.close();
|
|
19526
19715
|
if (opts.hidden && process.stdin.isTTY)
|
|
19527
19716
|
process.stdout.write(`
|
|
19528
19717
|
`);
|
|
19529
|
-
|
|
19718
|
+
resolve14(answer.trim());
|
|
19530
19719
|
});
|
|
19531
19720
|
});
|
|
19532
19721
|
}
|
|
@@ -19775,7 +19964,7 @@ EXAMPLES
|
|
|
19775
19964
|
});
|
|
19776
19965
|
|
|
19777
19966
|
// lib/claude-sessions.ts
|
|
19778
|
-
import { readdirSync as readdirSync11, statSync as
|
|
19967
|
+
import { readdirSync as readdirSync11, statSync as statSync13, existsSync as existsSync15 } from "node:fs";
|
|
19779
19968
|
import { join as join27, basename as basename5 } from "node:path";
|
|
19780
19969
|
function getClaudeProjectsRoot() {
|
|
19781
19970
|
return getClaudeProjectsPath();
|
|
@@ -19809,7 +19998,7 @@ function listClaudeTranscripts(project) {
|
|
|
19809
19998
|
continue;
|
|
19810
19999
|
const transcriptPath = join27(project.path, entry.name);
|
|
19811
20000
|
try {
|
|
19812
|
-
const s =
|
|
20001
|
+
const s = statSync13(transcriptPath);
|
|
19813
20002
|
out.push({
|
|
19814
20003
|
projectName: project.name,
|
|
19815
20004
|
cwd: project.cwd,
|
|
@@ -19836,7 +20025,7 @@ function listClaudeTranscripts(project) {
|
|
|
19836
20025
|
const agentId = sub.name.slice(0, -".jsonl".length);
|
|
19837
20026
|
const transcriptPath = join27(subDir, sub.name);
|
|
19838
20027
|
try {
|
|
19839
|
-
const s =
|
|
20028
|
+
const s = statSync13(transcriptPath);
|
|
19840
20029
|
out.push({
|
|
19841
20030
|
projectName: project.name,
|
|
19842
20031
|
cwd: project.cwd,
|
|
@@ -19980,7 +20169,7 @@ var init_claude = __esm(() => {
|
|
|
19980
20169
|
});
|
|
19981
20170
|
|
|
19982
20171
|
// src/audit/cli-adapters/codex.ts
|
|
19983
|
-
import { statSync as
|
|
20172
|
+
import { statSync as statSync14 } from "node:fs";
|
|
19984
20173
|
async function listCodexTranscriptMetadata(opts = {}) {
|
|
19985
20174
|
const projectFilter = opts.projects ? new Set(opts.projects) : null;
|
|
19986
20175
|
const sinceMs = opts.sinceMs ?? 0;
|
|
@@ -19997,7 +20186,7 @@ async function listCodexTranscriptMetadata(opts = {}) {
|
|
|
19997
20186
|
continue;
|
|
19998
20187
|
let sizeBytes = 0;
|
|
19999
20188
|
try {
|
|
20000
|
-
sizeBytes =
|
|
20189
|
+
sizeBytes = statSync14(s.path).size;
|
|
20001
20190
|
} catch {}
|
|
20002
20191
|
if (!s.sessionId)
|
|
20003
20192
|
continue;
|
|
@@ -20031,7 +20220,7 @@ var init_codex = __esm(() => {
|
|
|
20031
20220
|
});
|
|
20032
20221
|
|
|
20033
20222
|
// src/audit/cli-adapters/copilot.ts
|
|
20034
|
-
import { statSync as
|
|
20223
|
+
import { statSync as statSync15 } from "node:fs";
|
|
20035
20224
|
async function listCopilotTranscriptMetadata(opts = {}) {
|
|
20036
20225
|
const projectFilter = opts.projects ? new Set(opts.projects) : null;
|
|
20037
20226
|
const sinceMs = opts.sinceMs ?? 0;
|
|
@@ -20048,7 +20237,7 @@ async function listCopilotTranscriptMetadata(opts = {}) {
|
|
|
20048
20237
|
continue;
|
|
20049
20238
|
let sizeBytes = 0;
|
|
20050
20239
|
try {
|
|
20051
|
-
sizeBytes =
|
|
20240
|
+
sizeBytes = statSync15(s.path).size;
|
|
20052
20241
|
} catch {}
|
|
20053
20242
|
if (!s.sessionId)
|
|
20054
20243
|
continue;
|
|
@@ -20082,7 +20271,7 @@ var init_copilot = __esm(() => {
|
|
|
20082
20271
|
});
|
|
20083
20272
|
|
|
20084
20273
|
// src/audit/cli-adapters/cursor.ts
|
|
20085
|
-
import { statSync as
|
|
20274
|
+
import { statSync as statSync16 } from "node:fs";
|
|
20086
20275
|
async function listCursorTranscriptMetadata(opts = {}) {
|
|
20087
20276
|
const projectFilter = opts.projects ? new Set(opts.projects) : null;
|
|
20088
20277
|
const sinceMs = opts.sinceMs ?? 0;
|
|
@@ -20099,7 +20288,7 @@ async function listCursorTranscriptMetadata(opts = {}) {
|
|
|
20099
20288
|
continue;
|
|
20100
20289
|
let sizeBytes = 0;
|
|
20101
20290
|
try {
|
|
20102
|
-
sizeBytes =
|
|
20291
|
+
sizeBytes = statSync16(s.path).size;
|
|
20103
20292
|
} catch {}
|
|
20104
20293
|
if (!s.sessionId)
|
|
20105
20294
|
continue;
|
|
@@ -20345,7 +20534,7 @@ var init_opencode = __esm(() => {
|
|
|
20345
20534
|
});
|
|
20346
20535
|
|
|
20347
20536
|
// src/audit/cli-adapters/pi.ts
|
|
20348
|
-
import { statSync as
|
|
20537
|
+
import { statSync as statSync17 } from "node:fs";
|
|
20349
20538
|
async function listPiTranscriptMetadata(opts = {}) {
|
|
20350
20539
|
const projectFilter = opts.projects ? new Set(opts.projects) : null;
|
|
20351
20540
|
const sinceMs = opts.sinceMs ?? 0;
|
|
@@ -20362,7 +20551,7 @@ async function listPiTranscriptMetadata(opts = {}) {
|
|
|
20362
20551
|
continue;
|
|
20363
20552
|
let sizeBytes = 0;
|
|
20364
20553
|
try {
|
|
20365
|
-
sizeBytes =
|
|
20554
|
+
sizeBytes = statSync17(s.path).size;
|
|
20366
20555
|
} catch {}
|
|
20367
20556
|
if (!s.sessionId)
|
|
20368
20557
|
continue;
|
|
@@ -21092,7 +21281,7 @@ var init_features = __esm(() => {
|
|
|
21092
21281
|
import { createHash } from "node:crypto";
|
|
21093
21282
|
import { existsSync as existsSync16, mkdirSync as mkdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync8, chmodSync as chmodSync2 } from "node:fs";
|
|
21094
21283
|
import { join as join28 } from "node:path";
|
|
21095
|
-
import { homedir as
|
|
21284
|
+
import { homedir as homedir27 } from "node:os";
|
|
21096
21285
|
function getEngineVersion() {
|
|
21097
21286
|
if (cachedEngineVersion)
|
|
21098
21287
|
return cachedEngineVersion;
|
|
@@ -21110,7 +21299,7 @@ function getDetectorVersion() {
|
|
|
21110
21299
|
return cachedDetectorVersion;
|
|
21111
21300
|
}
|
|
21112
21301
|
function getCachePathFor(transcriptPath) {
|
|
21113
|
-
const root = join28(
|
|
21302
|
+
const root = join28(homedir27(), ".failproofai", "cache", "audit");
|
|
21114
21303
|
const key = createHash("sha1").update(transcriptPath).digest("hex");
|
|
21115
21304
|
return join28(root, `${key}.json`);
|
|
21116
21305
|
}
|
|
@@ -21145,7 +21334,7 @@ function writeCachedTranscriptResult(transcriptPath, mtimeMs, sizeBytes, result)
|
|
|
21145
21334
|
return;
|
|
21146
21335
|
const cachePath = getCachePathFor(transcriptPath);
|
|
21147
21336
|
try {
|
|
21148
|
-
mkdirSync9(join28(
|
|
21337
|
+
mkdirSync9(join28(homedir27(), ".failproofai", "cache", "audit"), { recursive: true });
|
|
21149
21338
|
const entry = {
|
|
21150
21339
|
schemaVersion: CACHE_SCHEMA_VERSION,
|
|
21151
21340
|
cachedAt: Date.now(),
|
|
@@ -21518,9 +21707,9 @@ var init_audit = __esm(() => {
|
|
|
21518
21707
|
|
|
21519
21708
|
// src/audit/dashboard-cache.ts
|
|
21520
21709
|
import { join as join29 } from "node:path";
|
|
21521
|
-
import { homedir as
|
|
21710
|
+
import { homedir as homedir28 } from "node:os";
|
|
21522
21711
|
function getCachePath() {
|
|
21523
|
-
return join29(
|
|
21712
|
+
return join29(homedir28(), ".failproofai", "audit-dashboard.json");
|
|
21524
21713
|
}
|
|
21525
21714
|
function writeDashboardCache(params, result) {
|
|
21526
21715
|
try {
|
|
@@ -21610,14 +21799,14 @@ var POLL_INTERVAL_MS = 150, POLL_TIMEOUT_MS = 30000;
|
|
|
21610
21799
|
var init_open_browser = () => {};
|
|
21611
21800
|
|
|
21612
21801
|
// scripts/parse-script-args.ts
|
|
21613
|
-
import { resolve as
|
|
21802
|
+
import { resolve as resolve14 } from "path";
|
|
21614
21803
|
function parseStringFlag(flagName, errorLabel, inlineValue, args, index, options) {
|
|
21615
21804
|
const raw = inlineValue ?? args[index + 1];
|
|
21616
21805
|
if (raw === undefined || inlineValue === null && raw.startsWith("-")) {
|
|
21617
21806
|
console.error(`Error: ${flagName} requires ${errorLabel}`);
|
|
21618
21807
|
process.exit(1);
|
|
21619
21808
|
}
|
|
21620
|
-
const value = options?.resolve ?
|
|
21809
|
+
const value = options?.resolve ? resolve14(raw) : raw;
|
|
21621
21810
|
return { value, spliceCount: inlineValue !== null ? 1 : 2 };
|
|
21622
21811
|
}
|
|
21623
21812
|
function parseScriptArgs(argv) {
|
|
@@ -21666,17 +21855,17 @@ var init_parse_script_args = () => {};
|
|
|
21666
21855
|
|
|
21667
21856
|
// scripts/install-diagnosis.mjs
|
|
21668
21857
|
import { existsSync as existsSync17, readFileSync as readFileSync15, realpathSync } from "node:fs";
|
|
21669
|
-
import { dirname as dirname7, resolve as
|
|
21670
|
-
import { homedir as
|
|
21858
|
+
import { dirname as dirname7, resolve as resolve15 } from "node:path";
|
|
21859
|
+
import { homedir as homedir29, platform as platform4 } from "node:os";
|
|
21671
21860
|
import { spawnSync } from "node:child_process";
|
|
21672
21861
|
function findPackageRoot(start) {
|
|
21673
21862
|
try {
|
|
21674
21863
|
let dir = realpathSync(start);
|
|
21675
|
-
if (existsSync17(dir) && !existsSync17(
|
|
21864
|
+
if (existsSync17(dir) && !existsSync17(resolve15(dir, "package.json"))) {
|
|
21676
21865
|
dir = dirname7(dir);
|
|
21677
21866
|
}
|
|
21678
21867
|
while (true) {
|
|
21679
|
-
const pkgPath =
|
|
21868
|
+
const pkgPath = resolve15(dir, "package.json");
|
|
21680
21869
|
if (existsSync17(pkgPath)) {
|
|
21681
21870
|
try {
|
|
21682
21871
|
const pkg = JSON.parse(readFileSync15(pkgPath, "utf8"));
|
|
@@ -21697,7 +21886,7 @@ function readPackageVersion(packageRoot) {
|
|
|
21697
21886
|
if (!packageRoot)
|
|
21698
21887
|
return null;
|
|
21699
21888
|
try {
|
|
21700
|
-
const pkg = JSON.parse(readFileSync15(
|
|
21889
|
+
const pkg = JSON.parse(readFileSync15(resolve15(packageRoot, "package.json"), "utf8"));
|
|
21701
21890
|
return typeof pkg.version === "string" ? pkg.version : null;
|
|
21702
21891
|
} catch {
|
|
21703
21892
|
return null;
|
|
@@ -21723,16 +21912,16 @@ function locateNpmGlobal() {
|
|
|
21723
21912
|
const root = (res.stdout || "").trim();
|
|
21724
21913
|
if (!root)
|
|
21725
21914
|
return null;
|
|
21726
|
-
const candidate =
|
|
21727
|
-
return existsSync17(
|
|
21915
|
+
const candidate = resolve15(root, PKG_NAME);
|
|
21916
|
+
return existsSync17(resolve15(candidate, "package.json")) ? candidate : null;
|
|
21728
21917
|
} catch {
|
|
21729
21918
|
return null;
|
|
21730
21919
|
}
|
|
21731
21920
|
}
|
|
21732
21921
|
function locateBunGlobal() {
|
|
21733
21922
|
try {
|
|
21734
|
-
const candidate =
|
|
21735
|
-
return existsSync17(
|
|
21923
|
+
const candidate = resolve15(homedir29(), ".bun", "install", "global", "node_modules", PKG_NAME);
|
|
21924
|
+
return existsSync17(resolve15(candidate, "package.json")) ? candidate : null;
|
|
21736
21925
|
} catch {
|
|
21737
21926
|
return null;
|
|
21738
21927
|
}
|
|
@@ -21740,8 +21929,8 @@ function locateBunGlobal() {
|
|
|
21740
21929
|
function buildRecommendation(pathFirstBin) {
|
|
21741
21930
|
if (!pathFirstBin)
|
|
21742
21931
|
return null;
|
|
21743
|
-
const bunBinPrefix =
|
|
21744
|
-
const bunGlobalPrefix =
|
|
21932
|
+
const bunBinPrefix = resolve15(homedir29(), ".bun", "bin") + "/";
|
|
21933
|
+
const bunGlobalPrefix = resolve15(homedir29(), ".bun", "install", "global") + "/";
|
|
21745
21934
|
const isBun = pathFirstBin.startsWith(bunBinPrefix) || pathFirstBin.startsWith(bunGlobalPrefix);
|
|
21746
21935
|
if (isBun) {
|
|
21747
21936
|
return `rm -f ~/.bun/bin/${PKG_NAME} && rm -rf ~/.bun/install/global/node_modules/${PKG_NAME}`;
|
|
@@ -21822,7 +22011,7 @@ __export(exports_launch, {
|
|
|
21822
22011
|
});
|
|
21823
22012
|
import { spawn as spawn2 } from "child_process";
|
|
21824
22013
|
import { realpathSync as realpathSync2, existsSync as existsSync18 } from "node:fs";
|
|
21825
|
-
import { resolve as
|
|
22014
|
+
import { resolve as resolve16, dirname as dirname8 } from "node:path";
|
|
21826
22015
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
21827
22016
|
import { createInterface as createInterface2 } from "node:readline";
|
|
21828
22017
|
function launch(mode) {
|
|
@@ -21837,8 +22026,8 @@ function launch(mode) {
|
|
|
21837
22026
|
process.env.PORT = port;
|
|
21838
22027
|
process.env.HOSTNAME = "0.0.0.0";
|
|
21839
22028
|
cmd = "node";
|
|
21840
|
-
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ??
|
|
21841
|
-
const serverJsPath =
|
|
22029
|
+
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve16(dirname8(realpathSync2(fileURLToPath2(import.meta.url))), "..");
|
|
22030
|
+
const serverJsPath = resolve16(packageRoot, ".next/standalone/server.js");
|
|
21842
22031
|
if (!existsSync18(serverJsPath)) {
|
|
21843
22032
|
let shadowMessage = null;
|
|
21844
22033
|
try {
|
|
@@ -22192,8 +22381,8 @@ WHAT IT DOES
|
|
|
22192
22381
|
// src/hooks/manager.ts
|
|
22193
22382
|
import { execSync as execSync7 } from "node:child_process";
|
|
22194
22383
|
import { existsSync as existsSync19 } from "node:fs";
|
|
22195
|
-
import { resolve as
|
|
22196
|
-
import { homedir as
|
|
22384
|
+
import { resolve as resolve17, basename as basename6 } from "node:path";
|
|
22385
|
+
import { homedir as homedir30, platform as platform5, arch as arch4, release as release3, hostname as hostname4 } from "node:os";
|
|
22197
22386
|
function getSettingsPath2(scope, cwd) {
|
|
22198
22387
|
return claudeCode.getSettingsPath(scope, cwd);
|
|
22199
22388
|
}
|
|
@@ -22300,8 +22489,8 @@ async function installHooksImpl2(policyNames, scope = "user", cwd, includeBeta =
|
|
|
22300
22489
|
delete configToWrite.customPoliciesPath;
|
|
22301
22490
|
delete configToWrite.customPoliciesPaths;
|
|
22302
22491
|
} else if (customPoliciesPath && (typeof customPoliciesPath === "string" || customPoliciesPath.length > 0)) {
|
|
22303
|
-
const incoming = (typeof customPoliciesPath === "string" ? [customPoliciesPath] : customPoliciesPath).map((path3) =>
|
|
22304
|
-
const carried = (previousConfig.customPoliciesPaths ?? (previousConfig.customPoliciesPath ? [previousConfig.customPoliciesPath] : [])).map((path3) =>
|
|
22492
|
+
const incoming = (typeof customPoliciesPath === "string" ? [customPoliciesPath] : customPoliciesPath).map((path3) => resolve17(path3));
|
|
22493
|
+
const carried = (previousConfig.customPoliciesPaths ?? (previousConfig.customPoliciesPath ? [previousConfig.customPoliciesPath] : [])).map((path3) => resolve17(path3)).filter((path3) => {
|
|
22305
22494
|
if (existsSync19(path3))
|
|
22306
22495
|
return true;
|
|
22307
22496
|
console.log(`Dropping custom policies path (file no longer exists): ${path3}`);
|
|
@@ -22498,6 +22687,314 @@ var init_policy_presets = __esm(() => {
|
|
|
22498
22687
|
];
|
|
22499
22688
|
});
|
|
22500
22689
|
|
|
22690
|
+
// src/hooks/daemon-download.ts
|
|
22691
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
22692
|
+
import { chmodSync as chmodSync3, existsSync as existsSync20, mkdirSync as mkdirSync10, renameSync as renameSync4, rmSync as rmSync3, writeFileSync as writeFileSync9 } from "node:fs";
|
|
22693
|
+
import { homedir as homedir31 } from "node:os";
|
|
22694
|
+
import { resolve as resolve18 } from "node:path";
|
|
22695
|
+
import { gunzipSync } from "node:zlib";
|
|
22696
|
+
function baseUrl() {
|
|
22697
|
+
return (process.env.FAILPROOFAI_DAEMON_BASE_URL || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
22698
|
+
}
|
|
22699
|
+
function daemonBinaryDir() {
|
|
22700
|
+
return resolve18(homedir31(), ".failproofai", "bin");
|
|
22701
|
+
}
|
|
22702
|
+
function installedBinaryPath(ver = version2) {
|
|
22703
|
+
return resolve18(daemonBinaryDir(), `failproofaid-${ver}`);
|
|
22704
|
+
}
|
|
22705
|
+
function daemonAssetUrl(key, ver = version2) {
|
|
22706
|
+
return `${baseUrl()}/v${ver}/failproofaid-${key}.gz`;
|
|
22707
|
+
}
|
|
22708
|
+
function checksumsUrl(ver = version2) {
|
|
22709
|
+
return `${baseUrl()}/v${ver}/SHA256SUMS`;
|
|
22710
|
+
}
|
|
22711
|
+
async function fetchBytes(url) {
|
|
22712
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS) });
|
|
22713
|
+
if (!response.ok)
|
|
22714
|
+
throw new Error(`GET ${url} returned ${response.status}`);
|
|
22715
|
+
return Buffer.from(await response.arrayBuffer());
|
|
22716
|
+
}
|
|
22717
|
+
function digestFor(manifest, assetName) {
|
|
22718
|
+
for (const line of manifest.split(`
|
|
22719
|
+
`)) {
|
|
22720
|
+
const match = line.trim().match(/^([0-9a-f]{64})\s+\*?(.+)$/i);
|
|
22721
|
+
if (match && match[2].trim() === assetName)
|
|
22722
|
+
return match[1].toLowerCase();
|
|
22723
|
+
}
|
|
22724
|
+
return null;
|
|
22725
|
+
}
|
|
22726
|
+
async function downloadFailproofaidBinary(key) {
|
|
22727
|
+
const target = installedBinaryPath();
|
|
22728
|
+
if (existsSync20(target))
|
|
22729
|
+
return { path: target };
|
|
22730
|
+
if (process.env.FAILPROOFAI_NO_DOWNLOAD) {
|
|
22731
|
+
return { error: "daemon downloads are disabled (FAILPROOFAI_NO_DOWNLOAD)" };
|
|
22732
|
+
}
|
|
22733
|
+
const assetName = `failproofaid-${key}.gz`;
|
|
22734
|
+
const tempPath = `${target}.${process.pid}.tmp`;
|
|
22735
|
+
try {
|
|
22736
|
+
const [asset, manifest] = await Promise.all([
|
|
22737
|
+
fetchBytes(daemonAssetUrl(key)),
|
|
22738
|
+
fetchBytes(checksumsUrl()).then((b) => b.toString("utf8"))
|
|
22739
|
+
]);
|
|
22740
|
+
const expected = digestFor(manifest, assetName);
|
|
22741
|
+
if (!expected) {
|
|
22742
|
+
return { error: `SHA256SUMS for v${version2} has no entry for ${assetName}` };
|
|
22743
|
+
}
|
|
22744
|
+
const actual = createHash2("sha256").update(asset).digest("hex");
|
|
22745
|
+
if (actual !== expected) {
|
|
22746
|
+
return { error: `checksum mismatch for ${assetName} (expected ${expected}, got ${actual})` };
|
|
22747
|
+
}
|
|
22748
|
+
const binary = gunzipSync(asset);
|
|
22749
|
+
mkdirSync10(daemonBinaryDir(), { recursive: true });
|
|
22750
|
+
writeFileSync9(tempPath, binary);
|
|
22751
|
+
chmodSync3(tempPath, 493);
|
|
22752
|
+
renameSync4(tempPath, target);
|
|
22753
|
+
return { path: target };
|
|
22754
|
+
} catch (err) {
|
|
22755
|
+
rmSync3(tempPath, { force: true });
|
|
22756
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
22757
|
+
return { error: `failed to download failproofaid v${version2} for ${key}: ${message}` };
|
|
22758
|
+
}
|
|
22759
|
+
}
|
|
22760
|
+
var DEFAULT_BASE_URL = "https://github.com/FailproofAI/failproofai/releases/download", DOWNLOAD_TIMEOUT_MS = 60000;
|
|
22761
|
+
var init_daemon_download = __esm(() => {
|
|
22762
|
+
init_package();
|
|
22763
|
+
});
|
|
22764
|
+
|
|
22765
|
+
// src/hooks/daemon-service.ts
|
|
22766
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync11, readFileSync as readFileSync16, writeFileSync as writeFileSync10, unlinkSync as unlinkSync3 } from "node:fs";
|
|
22767
|
+
import { homedir as homedir32 } from "node:os";
|
|
22768
|
+
import { resolve as resolve19, dirname as dirname9 } from "node:path";
|
|
22769
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
22770
|
+
function setDaemonConfigured(value) {
|
|
22771
|
+
const path3 = getConfigPathForScope("user");
|
|
22772
|
+
let config = {};
|
|
22773
|
+
try {
|
|
22774
|
+
if (existsSync21(path3))
|
|
22775
|
+
config = JSON.parse(readFileSync16(path3, "utf8"));
|
|
22776
|
+
} catch {
|
|
22777
|
+
return;
|
|
22778
|
+
}
|
|
22779
|
+
if (value)
|
|
22780
|
+
config.daemonConfigured = true;
|
|
22781
|
+
else
|
|
22782
|
+
delete config.daemonConfigured;
|
|
22783
|
+
try {
|
|
22784
|
+
mkdirSync11(dirname9(path3), { recursive: true });
|
|
22785
|
+
writeFileSync10(path3, JSON.stringify(config, null, 2) + `
|
|
22786
|
+
`, "utf8");
|
|
22787
|
+
} catch {}
|
|
22788
|
+
}
|
|
22789
|
+
function isDaemonSupportedPlatform() {
|
|
22790
|
+
return process.platform === "linux" || process.platform === "darwin";
|
|
22791
|
+
}
|
|
22792
|
+
function platformKey() {
|
|
22793
|
+
const os4 = process.platform === "linux" ? "linux" : process.platform === "darwin" ? "darwin" : null;
|
|
22794
|
+
const arch5 = process.arch === "x64" ? "x64" : process.arch === "arm64" ? "arm64" : null;
|
|
22795
|
+
if (!os4 || !arch5)
|
|
22796
|
+
return null;
|
|
22797
|
+
return `${os4}-${arch5}`;
|
|
22798
|
+
}
|
|
22799
|
+
function resolveFailproofaidBinaryPath() {
|
|
22800
|
+
if (process.env.FAILPROOFAI_DAEMON_BINARY)
|
|
22801
|
+
return process.env.FAILPROOFAI_DAEMON_BINARY;
|
|
22802
|
+
const downloaded = installedBinaryPath();
|
|
22803
|
+
if (existsSync21(downloaded))
|
|
22804
|
+
return downloaded;
|
|
22805
|
+
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
|
|
22806
|
+
if (packageRoot) {
|
|
22807
|
+
for (const profile of ["release", "debug"]) {
|
|
22808
|
+
const candidate = resolve19(packageRoot, "target", profile, "failproofaid");
|
|
22809
|
+
if (existsSync21(candidate))
|
|
22810
|
+
return candidate;
|
|
22811
|
+
}
|
|
22812
|
+
}
|
|
22813
|
+
return null;
|
|
22814
|
+
}
|
|
22815
|
+
async function ensureFailproofaidBinary() {
|
|
22816
|
+
const existing = resolveFailproofaidBinaryPath();
|
|
22817
|
+
if (existing)
|
|
22818
|
+
return { path: existing };
|
|
22819
|
+
const key = platformKey();
|
|
22820
|
+
if (!key) {
|
|
22821
|
+
return { reason: `failproofaid has no prebuilt binary for ${process.platform}/${process.arch}` };
|
|
22822
|
+
}
|
|
22823
|
+
const result = await downloadFailproofaidBinary(key);
|
|
22824
|
+
if (result.path)
|
|
22825
|
+
return { path: result.path };
|
|
22826
|
+
return { reason: result.error ?? "failproofaid binary could not be downloaded" };
|
|
22827
|
+
}
|
|
22828
|
+
function resolveWorkerCommand() {
|
|
22829
|
+
if (process.env.FAILPROOFAI_WORKER_CMD)
|
|
22830
|
+
return process.env.FAILPROOFAI_WORKER_CMD;
|
|
22831
|
+
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
|
|
22832
|
+
if (!packageRoot)
|
|
22833
|
+
return null;
|
|
22834
|
+
const workerScript = resolve19(packageRoot, "dist", "worker.mjs");
|
|
22835
|
+
if (!existsSync21(workerScript))
|
|
22836
|
+
return null;
|
|
22837
|
+
return `node ${workerScript}`;
|
|
22838
|
+
}
|
|
22839
|
+
function systemdUnitPath() {
|
|
22840
|
+
return resolve19(homedir32(), ".config", "systemd", "user", "failproofaid.service");
|
|
22841
|
+
}
|
|
22842
|
+
function launchdPlistPath() {
|
|
22843
|
+
return resolve19(homedir32(), "Library", "LaunchAgents", "ai.failproof.failproofaid.plist");
|
|
22844
|
+
}
|
|
22845
|
+
function daemonServiceFilePath() {
|
|
22846
|
+
if (!isDaemonSupportedPlatform())
|
|
22847
|
+
return null;
|
|
22848
|
+
return process.platform === "linux" ? systemdUnitPath() : launchdPlistPath();
|
|
22849
|
+
}
|
|
22850
|
+
function systemdUnitContents(binaryPath, workerCmd) {
|
|
22851
|
+
const envLine = workerCmd ? `Environment="FAILPROOFAI_WORKER_CMD=${workerCmd}"
|
|
22852
|
+
` : "";
|
|
22853
|
+
return `[Unit]
|
|
22854
|
+
Description=failproofai background daemon (failproofaid)
|
|
22855
|
+
After=network.target
|
|
22856
|
+
|
|
22857
|
+
[Service]
|
|
22858
|
+
Type=simple
|
|
22859
|
+
${envLine}ExecStart=${binaryPath}
|
|
22860
|
+
Restart=on-failure
|
|
22861
|
+
RestartSec=2
|
|
22862
|
+
|
|
22863
|
+
[Install]
|
|
22864
|
+
WantedBy=default.target
|
|
22865
|
+
`;
|
|
22866
|
+
}
|
|
22867
|
+
function escapeXml(s) {
|
|
22868
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
22869
|
+
}
|
|
22870
|
+
function launchdPlistContents(binaryPath, logDir2, workerCmd) {
|
|
22871
|
+
const envBlock = workerCmd ? ` <key>EnvironmentVariables</key>
|
|
22872
|
+
<dict>
|
|
22873
|
+
<key>FAILPROOFAI_WORKER_CMD</key>
|
|
22874
|
+
<string>${escapeXml(workerCmd)}</string>
|
|
22875
|
+
</dict>
|
|
22876
|
+
` : "";
|
|
22877
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
22878
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
22879
|
+
<plist version="1.0">
|
|
22880
|
+
<dict>
|
|
22881
|
+
<key>Label</key>
|
|
22882
|
+
<string>ai.failproof.failproofaid</string>
|
|
22883
|
+
<key>ProgramArguments</key>
|
|
22884
|
+
<array>
|
|
22885
|
+
<string>${escapeXml(binaryPath)}</string>
|
|
22886
|
+
</array>
|
|
22887
|
+
<key>RunAtLoad</key>
|
|
22888
|
+
<true/>
|
|
22889
|
+
<key>KeepAlive</key>
|
|
22890
|
+
<true/>
|
|
22891
|
+
${envBlock} <key>StandardOutPath</key>
|
|
22892
|
+
<string>${escapeXml(resolve19(logDir2, "failproofaid.log"))}</string>
|
|
22893
|
+
<key>StandardErrorPath</key>
|
|
22894
|
+
<string>${escapeXml(resolve19(logDir2, "failproofaid.err.log"))}</string>
|
|
22895
|
+
</dict>
|
|
22896
|
+
</plist>
|
|
22897
|
+
`;
|
|
22898
|
+
}
|
|
22899
|
+
async function installDaemonService() {
|
|
22900
|
+
if (!isDaemonSupportedPlatform()) {
|
|
22901
|
+
return { installed: false, reason: `failproofaid is not supported on ${process.platform} yet` };
|
|
22902
|
+
}
|
|
22903
|
+
const { path: binaryPath, reason: binaryReason } = await ensureFailproofaidBinary();
|
|
22904
|
+
if (!binaryPath) {
|
|
22905
|
+
return { installed: false, reason: binaryReason ?? "failproofaid binary not found for this platform" };
|
|
22906
|
+
}
|
|
22907
|
+
const workerCmd = resolveWorkerCommand();
|
|
22908
|
+
try {
|
|
22909
|
+
if (process.platform === "linux") {
|
|
22910
|
+
const unitPath = systemdUnitPath();
|
|
22911
|
+
mkdirSync11(dirname9(unitPath), { recursive: true });
|
|
22912
|
+
writeFileSync10(unitPath, systemdUnitContents(binaryPath, workerCmd), "utf8");
|
|
22913
|
+
execFileSync4("systemctl", ["--user", "daemon-reload"], {
|
|
22914
|
+
stdio: "ignore",
|
|
22915
|
+
timeout: SERVICE_CMD_TIMEOUT_MS
|
|
22916
|
+
});
|
|
22917
|
+
execFileSync4("systemctl", ["--user", "enable", "--now", "failproofaid.service"], {
|
|
22918
|
+
stdio: "ignore",
|
|
22919
|
+
timeout: SERVICE_CMD_TIMEOUT_MS
|
|
22920
|
+
});
|
|
22921
|
+
} else {
|
|
22922
|
+
const plistPath = launchdPlistPath();
|
|
22923
|
+
const logDir2 = resolve19(homedir32(), ".failproofai", "logs");
|
|
22924
|
+
mkdirSync11(dirname9(plistPath), { recursive: true });
|
|
22925
|
+
mkdirSync11(logDir2, { recursive: true });
|
|
22926
|
+
writeFileSync10(plistPath, launchdPlistContents(binaryPath, logDir2, workerCmd), "utf8");
|
|
22927
|
+
try {
|
|
22928
|
+
execFileSync4("launchctl", ["unload", plistPath], {
|
|
22929
|
+
stdio: "ignore",
|
|
22930
|
+
timeout: SERVICE_CMD_TIMEOUT_MS
|
|
22931
|
+
});
|
|
22932
|
+
} catch {}
|
|
22933
|
+
execFileSync4("launchctl", ["load", "-w", plistPath], {
|
|
22934
|
+
stdio: "ignore",
|
|
22935
|
+
timeout: SERVICE_CMD_TIMEOUT_MS
|
|
22936
|
+
});
|
|
22937
|
+
}
|
|
22938
|
+
} catch (err) {
|
|
22939
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
22940
|
+
hookLogWarn(`daemon service install failed: ${msg}`);
|
|
22941
|
+
return { installed: false, reason: msg };
|
|
22942
|
+
}
|
|
22943
|
+
if (!await waitForDaemonRunning()) {
|
|
22944
|
+
const reason = `failproofaid was installed but did not reach a running state within ${SERVICE_START_TIMEOUT_MS}ms (status: ${daemonServiceStatus()})`;
|
|
22945
|
+
hookLogWarn(`daemon service install failed: ${reason}`);
|
|
22946
|
+
return { installed: false, reason };
|
|
22947
|
+
}
|
|
22948
|
+
return { installed: true };
|
|
22949
|
+
}
|
|
22950
|
+
async function waitForDaemonRunning() {
|
|
22951
|
+
const deadline = Date.now() + SERVICE_START_TIMEOUT_MS;
|
|
22952
|
+
for (;; ) {
|
|
22953
|
+
if (daemonServiceStatus() === "running")
|
|
22954
|
+
break;
|
|
22955
|
+
if (Date.now() >= deadline)
|
|
22956
|
+
return false;
|
|
22957
|
+
await new Promise((r) => setTimeout(r, SERVICE_START_POLL_MS));
|
|
22958
|
+
}
|
|
22959
|
+
await new Promise((r) => setTimeout(r, SERVICE_SETTLE_MS));
|
|
22960
|
+
return daemonServiceStatus() === "running";
|
|
22961
|
+
}
|
|
22962
|
+
function daemonServiceStatus() {
|
|
22963
|
+
if (!isDaemonSupportedPlatform())
|
|
22964
|
+
return "unsupported-platform";
|
|
22965
|
+
if (process.platform === "linux") {
|
|
22966
|
+
if (!existsSync21(systemdUnitPath()))
|
|
22967
|
+
return "not-installed";
|
|
22968
|
+
try {
|
|
22969
|
+
const out = execFileSync4("systemctl", ["--user", "is-active", "failproofaid.service"], {
|
|
22970
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
22971
|
+
timeout: SERVICE_CMD_TIMEOUT_MS
|
|
22972
|
+
}).toString().trim();
|
|
22973
|
+
return out === "active" ? "running" : "stopped";
|
|
22974
|
+
} catch {
|
|
22975
|
+
return "stopped";
|
|
22976
|
+
}
|
|
22977
|
+
}
|
|
22978
|
+
const plistPath = launchdPlistPath();
|
|
22979
|
+
if (!existsSync21(plistPath))
|
|
22980
|
+
return "not-installed";
|
|
22981
|
+
try {
|
|
22982
|
+
const out = execFileSync4("launchctl", ["list"], {
|
|
22983
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
22984
|
+
timeout: SERVICE_CMD_TIMEOUT_MS
|
|
22985
|
+
}).toString();
|
|
22986
|
+
return out.includes("ai.failproof.failproofaid") ? "running" : "stopped";
|
|
22987
|
+
} catch {
|
|
22988
|
+
return "stopped";
|
|
22989
|
+
}
|
|
22990
|
+
}
|
|
22991
|
+
var SERVICE_CMD_TIMEOUT_MS = 1e4, SERVICE_START_TIMEOUT_MS = 5000, SERVICE_START_POLL_MS = 100, SERVICE_SETTLE_MS = 750;
|
|
22992
|
+
var init_daemon_service = __esm(() => {
|
|
22993
|
+
init_hook_logger();
|
|
22994
|
+
init_hooks_config();
|
|
22995
|
+
init_daemon_download();
|
|
22996
|
+
});
|
|
22997
|
+
|
|
22501
22998
|
// src/audit/cli.ts
|
|
22502
22999
|
var exports_cli3 = {};
|
|
22503
23000
|
__export(exports_cli3, {
|
|
@@ -22784,20 +23281,21 @@ __export(exports_configure_wizard, {
|
|
|
22784
23281
|
hasSeenLauncher: () => hasSeenLauncher,
|
|
22785
23282
|
describeCustomPolicies: () => describeCustomPolicies,
|
|
22786
23283
|
clisSupportingScope: () => clisSupportingScope,
|
|
23284
|
+
classifyDaemonInstallFailure: () => classifyDaemonInstallFailure,
|
|
22787
23285
|
buildScopeChoices: () => buildScopeChoices,
|
|
22788
23286
|
buildPresetChoices: () => buildPresetChoices,
|
|
22789
23287
|
buildAgentChoices: () => buildAgentChoices
|
|
22790
23288
|
});
|
|
22791
|
-
import { existsSync as
|
|
22792
|
-
import { homedir as
|
|
22793
|
-
import { resolve as
|
|
23289
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync12, writeFileSync as writeFileSync11, readFileSync as readFileSync17 } from "node:fs";
|
|
23290
|
+
import { homedir as homedir33 } from "node:os";
|
|
23291
|
+
import { resolve as resolve20, sep as sep5 } from "node:path";
|
|
22794
23292
|
async function emit(event, props) {
|
|
22795
23293
|
try {
|
|
22796
23294
|
await trackHookEvent2(getInstanceId2(), event, props);
|
|
22797
23295
|
} catch {}
|
|
22798
23296
|
}
|
|
22799
23297
|
function homeify(p) {
|
|
22800
|
-
const home =
|
|
23298
|
+
const home = homedir33();
|
|
22801
23299
|
if (p === home)
|
|
22802
23300
|
return "~";
|
|
22803
23301
|
if (p.startsWith(home + sep5))
|
|
@@ -22911,8 +23409,8 @@ function setCustomPoliciesEnabled(scope, cwd, enabled) {
|
|
|
22911
23409
|
const path3 = getConfigPathForScope(scope, cwd);
|
|
22912
23410
|
let config = {};
|
|
22913
23411
|
try {
|
|
22914
|
-
if (
|
|
22915
|
-
config = JSON.parse(
|
|
23412
|
+
if (existsSync22(path3))
|
|
23413
|
+
config = JSON.parse(readFileSync17(path3, "utf8"));
|
|
22916
23414
|
} catch {
|
|
22917
23415
|
return;
|
|
22918
23416
|
}
|
|
@@ -22921,18 +23419,37 @@ function setCustomPoliciesEnabled(scope, cwd, enabled) {
|
|
|
22921
23419
|
else
|
|
22922
23420
|
config.customPoliciesEnabled = false;
|
|
22923
23421
|
try {
|
|
22924
|
-
|
|
23422
|
+
writeFileSync11(path3, JSON.stringify(config, null, 2) + `
|
|
22925
23423
|
`, "utf8");
|
|
22926
23424
|
} catch {}
|
|
22927
23425
|
}
|
|
23426
|
+
function classifyDaemonInstallFailure(reason) {
|
|
23427
|
+
if (!reason)
|
|
23428
|
+
return "unknown";
|
|
23429
|
+
if (/not supported on/.test(reason))
|
|
23430
|
+
return "unsupported_platform";
|
|
23431
|
+
if (/no prebuilt binary for/.test(reason))
|
|
23432
|
+
return "unsupported_platform";
|
|
23433
|
+
if (/binary not found/.test(reason))
|
|
23434
|
+
return "binary_not_found";
|
|
23435
|
+
if (/checksum mismatch|has no entry for/.test(reason))
|
|
23436
|
+
return "checksum_mismatch";
|
|
23437
|
+
if (/downloads are disabled/.test(reason))
|
|
23438
|
+
return "downloads_disabled";
|
|
23439
|
+
if (/failed to download/.test(reason))
|
|
23440
|
+
return "download_failed";
|
|
23441
|
+
if (/did not reach a running state/.test(reason))
|
|
23442
|
+
return "did_not_start";
|
|
23443
|
+
return "service_manager_error";
|
|
23444
|
+
}
|
|
22928
23445
|
function describeCustomPolicies(cwd) {
|
|
22929
23446
|
const active = [];
|
|
22930
23447
|
const warnings = [];
|
|
22931
23448
|
const scopes = [];
|
|
22932
23449
|
let fileCount = 0;
|
|
22933
23450
|
const dirs = [
|
|
22934
|
-
{ dir:
|
|
22935
|
-
{ dir:
|
|
23451
|
+
{ dir: resolve20(cwd, ".failproofai", "policies"), label: "project" },
|
|
23452
|
+
{ dir: resolve20(homedir33(), ".failproofai", "policies"), label: "global" }
|
|
22936
23453
|
];
|
|
22937
23454
|
for (const { dir, label } of dirs) {
|
|
22938
23455
|
const found = discoverPolicyFiles(dir);
|
|
@@ -22942,7 +23459,7 @@ function describeCustomPolicies(cwd) {
|
|
|
22942
23459
|
fileCount += found.length;
|
|
22943
23460
|
}
|
|
22944
23461
|
for (const name of findSkippedPolicyFiles(dir)) {
|
|
22945
|
-
warnings.push(`! ${homeify(
|
|
23462
|
+
warnings.push(`! ${homeify(resolve20(dir, name))} is NOT loaded — rename to ` + `${name.replace(/\.(js|mjs|ts)$/, "-policies.$1")}`);
|
|
22946
23463
|
}
|
|
22947
23464
|
}
|
|
22948
23465
|
return { active, warnings, fileCount, scopes };
|
|
@@ -22955,6 +23472,9 @@ function reviewLines(state) {
|
|
|
22955
23472
|
lines.push(` Where : ${where}`);
|
|
22956
23473
|
lines.push(` Assistants : ${assistantNames.length ? summarize(assistantNames, "assistants") : "(none)"}`);
|
|
22957
23474
|
lines.push(` Policies : ${policies.length} enabled`);
|
|
23475
|
+
if (scope === "user" && isDaemonSupportedPlatform()) {
|
|
23476
|
+
lines.push(` Daemon : failproofaid will be installed/started as a background service`);
|
|
23477
|
+
}
|
|
22958
23478
|
const custom = describeCustomPolicies(cwd);
|
|
22959
23479
|
if (custom.active.length > 0) {
|
|
22960
23480
|
lines.push(` Custom : ${custom.active.join(" · ")} ${customEnabled === false ? "— DISABLED, will not load" : DIM_NOTE}`);
|
|
@@ -22970,18 +23490,23 @@ function reviewLines(state) {
|
|
|
22970
23490
|
}
|
|
22971
23491
|
}
|
|
22972
23492
|
lines.push(` ${homeify(getConfigPathForScope(scope, cwd))} ${policies.length} policies`);
|
|
23493
|
+
if (scope === "user" && isDaemonSupportedPlatform()) {
|
|
23494
|
+
const servicePath = daemonServiceFilePath();
|
|
23495
|
+
if (servicePath)
|
|
23496
|
+
lines.push(` ${homeify(servicePath)} failproofaid service`);
|
|
23497
|
+
}
|
|
22973
23498
|
return lines;
|
|
22974
23499
|
}
|
|
22975
23500
|
function firstRunMarkerPath() {
|
|
22976
|
-
return
|
|
23501
|
+
return resolve20(homedir33(), ".failproofai", ".launcher-configured");
|
|
22977
23502
|
}
|
|
22978
23503
|
function hasSeenLauncher() {
|
|
22979
|
-
return
|
|
23504
|
+
return existsSync22(firstRunMarkerPath());
|
|
22980
23505
|
}
|
|
22981
23506
|
function markLauncherSeen() {
|
|
22982
23507
|
try {
|
|
22983
|
-
|
|
22984
|
-
|
|
23508
|
+
mkdirSync12(resolve20(homedir33(), ".failproofai"), { recursive: true });
|
|
23509
|
+
writeFileSync11(firstRunMarkerPath(), "1", "utf8");
|
|
22985
23510
|
} catch {}
|
|
22986
23511
|
}
|
|
22987
23512
|
function anyHooksInstalledGlobally() {
|
|
@@ -23104,11 +23629,27 @@ Use the flag form instead, e.g.:
|
|
|
23104
23629
|
});
|
|
23105
23630
|
await installHooks2(policies, scope, cwd, false, "configure-wizard", undefined, false, clis, { replace: true, quiet: true });
|
|
23106
23631
|
setCustomPoliciesEnabled(scope, cwd, customEnabled);
|
|
23632
|
+
let daemonInstalled = false;
|
|
23633
|
+
if (scope === "user" && isDaemonSupportedPlatform()) {
|
|
23634
|
+
const daemonResult = await installDaemonService();
|
|
23635
|
+
if (daemonResult.installed) {
|
|
23636
|
+
setDaemonConfigured(true);
|
|
23637
|
+
daemonInstalled = true;
|
|
23638
|
+
} else {
|
|
23639
|
+
hookLogWarn(`failproofaid was not installed as a service: ${daemonResult.reason}`);
|
|
23640
|
+
}
|
|
23641
|
+
emit("configure_daemon_install", {
|
|
23642
|
+
installed: daemonResult.installed,
|
|
23643
|
+
reason: daemonResult.installed ? null : classifyDaemonInstallFailure(daemonResult.reason),
|
|
23644
|
+
platform: process.platform
|
|
23645
|
+
});
|
|
23646
|
+
}
|
|
23107
23647
|
await applied;
|
|
23108
23648
|
markLauncherSeen();
|
|
23109
23649
|
const customNote = customEnabled === true ? " + your custom policies" : customEnabled === false ? " · custom policies DISABLED" : "";
|
|
23650
|
+
const daemonNote = daemonInstalled ? " · background daemon enabled" : "";
|
|
23110
23651
|
const assistants = `${clis.length} assistant${clis.length === 1 ? "" : "s"}`;
|
|
23111
|
-
outro(`Setup complete — ${policies.length} policies${customNote} · ${assistants}`, { ok: true }, stdout);
|
|
23652
|
+
outro(`Setup complete — ${policies.length} policies${customNote} · ${assistants}${daemonNote}`, { ok: true }, stdout);
|
|
23112
23653
|
return { applied: true, scope, clis, policies };
|
|
23113
23654
|
}
|
|
23114
23655
|
var EVERYTHING = "__everything__", ALL_CLIS = "__all_clis__", CUSTOM = "__custom__", DIM_NOTE = "(auto-loaded)";
|
|
@@ -23122,6 +23663,8 @@ var init_configure_wizard = __esm(() => {
|
|
|
23122
23663
|
init_custom_hooks_loader();
|
|
23123
23664
|
init_hook_telemetry2();
|
|
23124
23665
|
init_telemetry_id2();
|
|
23666
|
+
init_daemon_service();
|
|
23667
|
+
init_hook_logger();
|
|
23125
23668
|
});
|
|
23126
23669
|
|
|
23127
23670
|
// scripts/launch.ts
|
|
@@ -23130,8 +23673,8 @@ __export(exports_launch2, {
|
|
|
23130
23673
|
launch: () => launch2
|
|
23131
23674
|
});
|
|
23132
23675
|
import { spawn as spawn3 } from "child_process";
|
|
23133
|
-
import { realpathSync as realpathSync3, existsSync as
|
|
23134
|
-
import { resolve as
|
|
23676
|
+
import { realpathSync as realpathSync3, existsSync as existsSync23 } from "node:fs";
|
|
23677
|
+
import { resolve as resolve21, dirname as dirname10 } from "node:path";
|
|
23135
23678
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
23136
23679
|
import { createInterface as createInterface3 } from "node:readline";
|
|
23137
23680
|
function launch2(mode) {
|
|
@@ -23146,9 +23689,9 @@ function launch2(mode) {
|
|
|
23146
23689
|
process.env.PORT = port;
|
|
23147
23690
|
process.env.HOSTNAME = "0.0.0.0";
|
|
23148
23691
|
cmd = "node";
|
|
23149
|
-
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ??
|
|
23150
|
-
const serverJsPath =
|
|
23151
|
-
if (!
|
|
23692
|
+
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve21(dirname10(realpathSync3(fileURLToPath3(import.meta.url))), "..");
|
|
23693
|
+
const serverJsPath = resolve21(packageRoot, ".next/standalone/server.js");
|
|
23694
|
+
if (!existsSync23(serverJsPath)) {
|
|
23152
23695
|
let shadowMessage = null;
|
|
23153
23696
|
try {
|
|
23154
23697
|
const diag = diagnoseShadow({ selfPackageRoot: packageRoot, selfVersion: version2 });
|
|
@@ -23243,17 +23786,17 @@ var init_cli_error2 = __esm(() => {
|
|
|
23243
23786
|
|
|
23244
23787
|
// bin/failproofai.mjs
|
|
23245
23788
|
import { realpathSync as realpathSync4 } from "fs";
|
|
23246
|
-
import { dirname as
|
|
23789
|
+
import { dirname as dirname11, resolve as resolve22 } from "path";
|
|
23247
23790
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
23248
23791
|
// package.json
|
|
23249
|
-
var version = "0.0.
|
|
23792
|
+
var version = "1.0.0-beta.0";
|
|
23250
23793
|
|
|
23251
23794
|
// bin/failproofai.mjs
|
|
23252
23795
|
if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
|
|
23253
|
-
process.env.FAILPROOFAI_PACKAGE_ROOT =
|
|
23796
|
+
process.env.FAILPROOFAI_PACKAGE_ROOT = resolve22(dirname11(realpathSync4(fileURLToPath4(import.meta.url))), "..");
|
|
23254
23797
|
}
|
|
23255
23798
|
if (!process.env.FAILPROOFAI_DIST_PATH) {
|
|
23256
|
-
process.env.FAILPROOFAI_DIST_PATH =
|
|
23799
|
+
process.env.FAILPROOFAI_DIST_PATH = resolve22(dirname11(realpathSync4(fileURLToPath4(import.meta.url))), "..", "dist");
|
|
23257
23800
|
}
|
|
23258
23801
|
var args = process.argv.slice(2);
|
|
23259
23802
|
if (args[0] === "p")
|
|
@@ -23275,6 +23818,18 @@ async function track(name, props) {
|
|
|
23275
23818
|
await _telemetry.trackHookEvent(_telemetry.getInstanceId(), name, props);
|
|
23276
23819
|
} catch {}
|
|
23277
23820
|
}
|
|
23821
|
+
async function exitAfterFlush(code) {
|
|
23822
|
+
const drain = (stream) => new Promise((resolveDrain) => {
|
|
23823
|
+
if (stream.writableLength === 0 || stream.destroyed)
|
|
23824
|
+
resolveDrain();
|
|
23825
|
+
else
|
|
23826
|
+
stream.write("", () => resolveDrain());
|
|
23827
|
+
});
|
|
23828
|
+
try {
|
|
23829
|
+
await Promise.all([drain(process.stdout), drain(process.stderr)]);
|
|
23830
|
+
} catch {}
|
|
23831
|
+
process.exit(code);
|
|
23832
|
+
}
|
|
23278
23833
|
var hookIdx = args.indexOf("--hook");
|
|
23279
23834
|
if (hookIdx >= 0) {
|
|
23280
23835
|
if (!args[hookIdx + 1]) {
|
|
@@ -23287,9 +23842,32 @@ if (hookIdx >= 0) {
|
|
|
23287
23842
|
const cliArg = cliIdx >= 0 ? args[cliIdx + 1] : undefined;
|
|
23288
23843
|
const cli = cliArg && (cliArg === "claude" || cliArg === "codex" || cliArg === "copilot" || cliArg === "cursor" || cliArg === "opencode" || cliArg === "pi" || cliArg === "hermes" || cliArg === "openclaw" || cliArg === "factory" || cliArg === "devin" || cliArg === "antigravity" || cliArg === "goose") ? cliArg : "claude";
|
|
23289
23844
|
try {
|
|
23845
|
+
const { isDaemonConfigured: isDaemonConfigured2, tryDaemonHook: tryDaemonHook2 } = await Promise.resolve().then(() => (init_daemon_client(), exports_daemon_client));
|
|
23846
|
+
if (isDaemonConfigured2()) {
|
|
23847
|
+
const { readStdinPayload: readStdinPayload3 } = await Promise.resolve().then(() => exports_read_stdin);
|
|
23848
|
+
const { evaluateHookEvent: evaluateHookEvent2 } = await Promise.resolve().then(() => (init_handler(), exports_handler));
|
|
23849
|
+
const stdinRead = await readStdinPayload3();
|
|
23850
|
+
const daemonResult = await tryDaemonHook2({
|
|
23851
|
+
hookEvent: eventType,
|
|
23852
|
+
cli,
|
|
23853
|
+
stdin: stdinRead.payload,
|
|
23854
|
+
cwd: process.cwd()
|
|
23855
|
+
});
|
|
23856
|
+
const result = daemonResult ?? await evaluateHookEvent2(eventType, cli, stdinRead.payload, {
|
|
23857
|
+
forceDecision: {
|
|
23858
|
+
decision: "deny",
|
|
23859
|
+
reason: "failproofaid could not be reached. This machine is configured to run hooks through it " + "\u2014 check the daemon (see `failproofai config`) rather than retrying blindly."
|
|
23860
|
+
}
|
|
23861
|
+
});
|
|
23862
|
+
if (result.stdout)
|
|
23863
|
+
process.stdout.write(result.stdout);
|
|
23864
|
+
if (result.stderr)
|
|
23865
|
+
process.stderr.write(result.stderr);
|
|
23866
|
+
await exitAfterFlush(result.exitCode);
|
|
23867
|
+
}
|
|
23290
23868
|
const { handleHookEvent: handleHookEvent2 } = await Promise.resolve().then(() => (init_handler(), exports_handler));
|
|
23291
23869
|
const exitCode = await handleHookEvent2(eventType, cli);
|
|
23292
|
-
|
|
23870
|
+
await exitAfterFlush(exitCode);
|
|
23293
23871
|
} catch (err) {
|
|
23294
23872
|
const msg = err instanceof Error ? err.message : String(err);
|
|
23295
23873
|
await track("hook_dispatch_error", {
|