failproofai 1.0.0-beta.7 → 1.0.0-beta.8
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.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/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 +10 -10
- 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/app/settings/page/server-reference-manifest.json +4 -4
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__06hexd0._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +3 -3
- 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]__0ommekk._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0spkm68._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1rv-sc9._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0c_qcgp._.js → [root-of-the-server]__1tkyag1._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1tyf0tc._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1u-wx42._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1v185mo._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_04mekdy._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_194yh9u._.js → _0i4l3_6._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0r-dsre._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0rjgfln._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0se484m._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0uz7o9y._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1fahg9e._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1w8sola._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1w_6p9g._.js +1 -1
- 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/src_hooks_builtin-policies_ts_09j2ndl._.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 +15 -15
- package/.next/standalone/.next/static/chunks/{2aej0vi1fm87m.js → 02vvywmww22zz.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3t1wr_kx6brwc.js → 0_-focvo4cuiv.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2bxld6-rym3ah.js → 1fwh-2un_l-eo.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2p2kz4aj1tooj.js → 1zrdsxe91qq6i.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0m-m7wgtxdoav.js → 2047vah_v4emu.js} +1 -1
- package/.next/standalone/.next/static/chunks/23hl78dzffun4.js +6 -0
- package/.next/standalone/.next/static/chunks/{11l6oh9cyb35a.js → 2bw0stk-s-1dd.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0gr76ps8_zm26.js → 2r_j5wrkbu5wm.js} +1 -1
- package/.next/standalone/.next/static/chunks/3m8onpqrjxzu5.js +1 -0
- package/.next/standalone/.next/static/chunks/{36pc3_8fpb6ic.js → 3yn1y6ckypqwp.js} +1 -1
- package/.next/standalone/package.json +5 -5
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +118 -1
- package/dist/cli.mjs +1166 -101
- package/dist/worker.mjs +77 -6
- package/package.json +5 -5
- package/scripts/repro-npm-install.sh +155 -0
- package/src/hooks/builtin-policies.ts +154 -2
- package/src/hooks/configure-wizard.ts +16 -6
- package/src/hooks/daemon-client.ts +35 -0
- package/src/hooks/daemon-service.ts +227 -22
- package/src/hooks/first-run-gate.ts +9 -1
- package/src/hooks/fp-reset.ts +25 -0
- package/src/hooks/uninstall-cli.ts +345 -0
- package/src/hooks/worker-server.ts +12 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kbc9bs._.js +0 -3
- package/.next/standalone/.next/static/chunks/0945ybz3fsvba.js +0 -1
- package/.next/standalone/.next/static/chunks/166f8qnh03jqc.js +0 -6
- /package/.next/standalone/.next/static/{LUaLkKg40nSkHrIajexIk → gOR8i03AeQm1VnIayDHVV}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{LUaLkKg40nSkHrIajexIk → gOR8i03AeQm1VnIayDHVV}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{LUaLkKg40nSkHrIajexIk → gOR8i03AeQm1VnIayDHVV}/_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 = "1.0.0-beta.
|
|
22
|
+
var version2 = "1.0.0-beta.8";
|
|
23
23
|
var init_package = () => {};
|
|
24
24
|
|
|
25
25
|
// src/posthog-key.ts
|
|
@@ -1454,6 +1454,7 @@ __export(exports_daemon_client, {
|
|
|
1454
1454
|
tryDaemonHook: () => tryDaemonHook,
|
|
1455
1455
|
isDaemonConfigured: () => isDaemonConfigured,
|
|
1456
1456
|
daemonSocketPresent: () => daemonSocketPresent,
|
|
1457
|
+
daemonAcceptsConnections: () => daemonAcceptsConnections,
|
|
1457
1458
|
attemptDaemonHook: () => attemptDaemonHook
|
|
1458
1459
|
});
|
|
1459
1460
|
import { createConnection } from "node:net";
|
|
@@ -1465,6 +1466,27 @@ function daemonSocketPresent() {
|
|
|
1465
1466
|
return false;
|
|
1466
1467
|
}
|
|
1467
1468
|
}
|
|
1469
|
+
async function daemonAcceptsConnections(timeoutMs = DAEMON_CONNECT_TIMEOUT_MS) {
|
|
1470
|
+
if (process.platform === "win32")
|
|
1471
|
+
return false;
|
|
1472
|
+
return new Promise((resolve2) => {
|
|
1473
|
+
let settled = false;
|
|
1474
|
+
const done = (v) => {
|
|
1475
|
+
if (settled)
|
|
1476
|
+
return;
|
|
1477
|
+
settled = true;
|
|
1478
|
+
clearTimeout(timer);
|
|
1479
|
+
socket.removeAllListeners();
|
|
1480
|
+
socket.destroy();
|
|
1481
|
+
resolve2(v);
|
|
1482
|
+
};
|
|
1483
|
+
const socket = createConnection({ path: socketPath() });
|
|
1484
|
+
const timer = setTimeout(() => done(false), timeoutMs);
|
|
1485
|
+
timer.unref?.();
|
|
1486
|
+
socket.on("connect", () => done(true));
|
|
1487
|
+
socket.on("error", () => done(false));
|
|
1488
|
+
});
|
|
1489
|
+
}
|
|
1468
1490
|
function socketPath() {
|
|
1469
1491
|
if (process.env.FAILPROOFAI_DAEMON_SOCKET)
|
|
1470
1492
|
return process.env.FAILPROOFAI_DAEMON_SOCKET;
|
|
@@ -2751,11 +2773,82 @@ function blockEnvFiles(ctx) {
|
|
|
2751
2773
|
}
|
|
2752
2774
|
return allow();
|
|
2753
2775
|
}
|
|
2776
|
+
function quoteAwareSegments(command) {
|
|
2777
|
+
const segments = [];
|
|
2778
|
+
let tokens = [];
|
|
2779
|
+
let token = "";
|
|
2780
|
+
let quote = null;
|
|
2781
|
+
const endToken = () => {
|
|
2782
|
+
if (token)
|
|
2783
|
+
tokens.push(token);
|
|
2784
|
+
token = "";
|
|
2785
|
+
};
|
|
2786
|
+
const endSegment = () => {
|
|
2787
|
+
endToken();
|
|
2788
|
+
if (tokens.length)
|
|
2789
|
+
segments.push(tokens);
|
|
2790
|
+
tokens = [];
|
|
2791
|
+
};
|
|
2792
|
+
for (let i = 0;i < command.length; i++) {
|
|
2793
|
+
const c = command[i];
|
|
2794
|
+
if (quote) {
|
|
2795
|
+
if (c === "\\" && quote === '"' && i + 1 < command.length) {
|
|
2796
|
+
token += command[++i];
|
|
2797
|
+
continue;
|
|
2798
|
+
}
|
|
2799
|
+
if (c === quote) {
|
|
2800
|
+
quote = null;
|
|
2801
|
+
continue;
|
|
2802
|
+
}
|
|
2803
|
+
token += c;
|
|
2804
|
+
continue;
|
|
2805
|
+
}
|
|
2806
|
+
if (c === "\\" && i + 1 < command.length) {
|
|
2807
|
+
token += command[++i];
|
|
2808
|
+
continue;
|
|
2809
|
+
}
|
|
2810
|
+
if (c === '"' || c === "'") {
|
|
2811
|
+
quote = c;
|
|
2812
|
+
continue;
|
|
2813
|
+
}
|
|
2814
|
+
if (/\s/.test(c)) {
|
|
2815
|
+
endToken();
|
|
2816
|
+
continue;
|
|
2817
|
+
}
|
|
2818
|
+
if (/[;&|\n\r(){}`]/.test(c)) {
|
|
2819
|
+
endSegment();
|
|
2820
|
+
continue;
|
|
2821
|
+
}
|
|
2822
|
+
token += c;
|
|
2823
|
+
}
|
|
2824
|
+
endSegment();
|
|
2825
|
+
return segments;
|
|
2826
|
+
}
|
|
2827
|
+
function namesElevation(command, depth = 0) {
|
|
2828
|
+
for (const tokens of quoteAwareSegments(command)) {
|
|
2829
|
+
let i = 0;
|
|
2830
|
+
while (i < tokens.length) {
|
|
2831
|
+
const token = tokens[i];
|
|
2832
|
+
const base = token.slice(token.lastIndexOf("/") + 1);
|
|
2833
|
+
if (base === "sudo" || base === "doas")
|
|
2834
|
+
return true;
|
|
2835
|
+
const isSkippable = ENV_ASSIGNMENT_RE.test(token) || token.startsWith("-") || token.startsWith(">") || token.startsWith("<") || RUNNER_OPERAND_RE.test(token) || COMMAND_PREFIX_TOKENS.has(base);
|
|
2836
|
+
if (!isSkippable)
|
|
2837
|
+
break;
|
|
2838
|
+
if (EVAL_FLAG_RE.test(token) && depth < 3 && i + 1 < tokens.length) {
|
|
2839
|
+
if (namesElevation(tokens[i + 1], depth + 1))
|
|
2840
|
+
return true;
|
|
2841
|
+
}
|
|
2842
|
+
i++;
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2845
|
+
return false;
|
|
2846
|
+
}
|
|
2754
2847
|
function blockSudo(ctx) {
|
|
2755
2848
|
if (ctx.toolName !== "Bash")
|
|
2756
2849
|
return allow();
|
|
2757
2850
|
const cmd = getCommand(ctx).trimStart();
|
|
2758
|
-
if (
|
|
2851
|
+
if (namesElevation(cmd)) {
|
|
2759
2852
|
const allowPatterns = ctx.params?.allowPatterns ?? [];
|
|
2760
2853
|
if (allowPatterns.some((p) => matchesAllowedPattern(cmd, p)))
|
|
2761
2854
|
return allow();
|
|
@@ -3515,7 +3608,7 @@ function registerBuiltinPolicies(enabledNames) {
|
|
|
3515
3608
|
}
|
|
3516
3609
|
}
|
|
3517
3610
|
}
|
|
3518
|
-
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,
|
|
3611
|
+
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, PS_ELEVATION_RE, RUNAS_RE, CURL_PIPE_SH_RE, SEGMENT_SEPARATORS, COMMAND_PREFIX_TOKENS, SELF_BINARY_TOKEN_RE, CONFIG_SUBCOMMAND_RE, PAUSE_FLAG_RE, ENV_ASSIGNMENT_RE, RUNNER_OPERAND_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, EVAL_FLAG_RE, 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;
|
|
3519
3612
|
var init_builtin_policies = __esm(() => {
|
|
3520
3613
|
init_hook_logger();
|
|
3521
3614
|
isClaudeInternalPath = isAgentInternalPath;
|
|
@@ -3552,7 +3645,6 @@ var init_builtin_policies = __esm(() => {
|
|
|
3552
3645
|
CMD_ECHO_ENV_RE = /echo\s+%[A-Za-z_]/i;
|
|
3553
3646
|
ENV_FILE_PATH_RE = /(?:^|[\\/])\.env(?:\.|$)/;
|
|
3554
3647
|
ENV_CMD_RE = /\.env(?:\b|\s|$|\.)/;
|
|
3555
|
-
SUDO_RE = /(?:^|;|&&|\|\|)\s*sudo\s/;
|
|
3556
3648
|
PS_ELEVATION_RE = /Start-Process\s+.*-Verb\s+RunAs/i;
|
|
3557
3649
|
RUNAS_RE = /(?:^|;|&&|\|\|)\s*runas\s/i;
|
|
3558
3650
|
CURL_PIPE_SH_RE = /(?:curl|wget)\s.*\|\s*(?:sh|bash|zsh|dash|ksh|csh|tcsh|fish|ash)\b/;
|
|
@@ -3638,6 +3730,7 @@ var init_builtin_policies = __esm(() => {
|
|
|
3638
3730
|
HELM_RE = /(?:^|[;\n]|&&|\|\|?|&)\s*helm(?:\s|$)/;
|
|
3639
3731
|
GH_PIPELINE_RE = /(?:^|[;\n]|&&|\|\|?|&)\s*gh\s+(?:workflow\s+(?:run|enable|disable)|run\s+(?:rerun|cancel)|pr\s+merge|release\s+(?:create|delete)|cache\s+delete|secret\s+(?:set|delete))\b/;
|
|
3640
3732
|
gitBranchCache = new Map;
|
|
3733
|
+
EVAL_FLAG_RE = /^-{1,2}c$/;
|
|
3641
3734
|
HOME_PREFIX_RE = /^(?:~[A-Za-z0-9_.-]*|\$HOME|\$\{HOME\})(?=$|\/)/;
|
|
3642
3735
|
RM_CMD_RE = /^(?:\/\S*\/)?rm$/;
|
|
3643
3736
|
FIND_CMD_RE = /^(?:\/\S*\/)?find$/;
|
|
@@ -10739,6 +10832,7 @@ __export(exports_daemon_client2, {
|
|
|
10739
10832
|
tryDaemonHook: () => tryDaemonHook2,
|
|
10740
10833
|
isDaemonConfigured: () => isDaemonConfigured2,
|
|
10741
10834
|
daemonSocketPresent: () => daemonSocketPresent2,
|
|
10835
|
+
daemonAcceptsConnections: () => daemonAcceptsConnections2,
|
|
10742
10836
|
attemptDaemonHook: () => attemptDaemonHook2
|
|
10743
10837
|
});
|
|
10744
10838
|
import { createConnection as createConnection2 } from "node:net";
|
|
@@ -10750,6 +10844,27 @@ function daemonSocketPresent2() {
|
|
|
10750
10844
|
return false;
|
|
10751
10845
|
}
|
|
10752
10846
|
}
|
|
10847
|
+
async function daemonAcceptsConnections2(timeoutMs = DAEMON_CONNECT_TIMEOUT_MS2) {
|
|
10848
|
+
if (process.platform === "win32")
|
|
10849
|
+
return false;
|
|
10850
|
+
return new Promise((resolve14) => {
|
|
10851
|
+
let settled = false;
|
|
10852
|
+
const done = (v) => {
|
|
10853
|
+
if (settled)
|
|
10854
|
+
return;
|
|
10855
|
+
settled = true;
|
|
10856
|
+
clearTimeout(timer);
|
|
10857
|
+
socket.removeAllListeners();
|
|
10858
|
+
socket.destroy();
|
|
10859
|
+
resolve14(v);
|
|
10860
|
+
};
|
|
10861
|
+
const socket = createConnection2({ path: socketPath2() });
|
|
10862
|
+
const timer = setTimeout(() => done(false), timeoutMs);
|
|
10863
|
+
timer.unref?.();
|
|
10864
|
+
socket.on("connect", () => done(true));
|
|
10865
|
+
socket.on("error", () => done(false));
|
|
10866
|
+
});
|
|
10867
|
+
}
|
|
10753
10868
|
function socketPath2() {
|
|
10754
10869
|
if (process.env.FAILPROOFAI_DAEMON_SOCKET)
|
|
10755
10870
|
return process.env.FAILPROOFAI_DAEMON_SOCKET;
|
|
@@ -10947,6 +11062,15 @@ function resolveCliCommand() {
|
|
|
10947
11062
|
function shellQuote(value) {
|
|
10948
11063
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
10949
11064
|
}
|
|
11065
|
+
function workerScriptPath() {
|
|
11066
|
+
if (process.env.FAILPROOFAI_WORKER_CMD)
|
|
11067
|
+
return null;
|
|
11068
|
+
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
|
|
11069
|
+
if (!packageRoot)
|
|
11070
|
+
return null;
|
|
11071
|
+
const workerScript = resolve14(packageRoot, "dist", "worker.mjs");
|
|
11072
|
+
return existsSync17(workerScript) ? workerScript : null;
|
|
11073
|
+
}
|
|
10950
11074
|
function assertUnitSafe(value, field) {
|
|
10951
11075
|
if (UNIT_UNSAFE_RE.test(value)) {
|
|
10952
11076
|
throw new Error(`refusing to write a service definition: ${field} contains a quote, backslash or newline ` + `(${JSON.stringify(value)}), which would inject directives into a root-owned unit file`);
|
|
@@ -11085,10 +11209,13 @@ function systemdUnitContents(binaryPath, workerCmd, cliCmd = null) {
|
|
|
11085
11209
|
const user = assertUnitSafe(serviceUser(), "User");
|
|
11086
11210
|
assertUnitSafe(binaryPath, "ExecStart");
|
|
11087
11211
|
assertUnitSafe(homedir19(), "HOME");
|
|
11212
|
+
const conditionPaths = [binaryPath, workerScriptPath()].filter((p) => typeof p === "string" && existsSync17(p));
|
|
11213
|
+
const conditionLines = conditionPaths.map((p) => `ConditionPathExists=${assertUnitSafe(p, "ConditionPathExists")}
|
|
11214
|
+
`).join("");
|
|
11088
11215
|
return `[Unit]
|
|
11089
11216
|
Description=failproofai background daemon (failproofaid) for ${user}
|
|
11090
11217
|
After=network.target
|
|
11091
|
-
|
|
11218
|
+
${conditionLines}
|
|
11092
11219
|
[Service]
|
|
11093
11220
|
Type=simple
|
|
11094
11221
|
User=${user}
|
|
@@ -11209,19 +11336,37 @@ async function installDaemonService() {
|
|
|
11209
11336
|
}
|
|
11210
11337
|
return { installed: true };
|
|
11211
11338
|
}
|
|
11212
|
-
async function
|
|
11339
|
+
async function probeDaemon() {
|
|
11213
11340
|
try {
|
|
11214
|
-
const { attemptDaemonHook: attemptDaemonHook3 } = await Promise.resolve().then(() => (init_daemon_client2(), exports_daemon_client2));
|
|
11215
|
-
const
|
|
11216
|
-
|
|
11217
|
-
|
|
11218
|
-
|
|
11219
|
-
|
|
11220
|
-
|
|
11341
|
+
const { attemptDaemonHook: attemptDaemonHook3, daemonAcceptsConnections: daemonAcceptsConnections3 } = await Promise.resolve().then(() => (init_daemon_client2(), exports_daemon_client2));
|
|
11342
|
+
const deadline = Date.now() + DAEMON_PROBE_READY_TIMEOUT_MS;
|
|
11343
|
+
let everConnected = false;
|
|
11344
|
+
for (;; ) {
|
|
11345
|
+
if (await daemonAcceptsConnections3()) {
|
|
11346
|
+
everConnected = true;
|
|
11347
|
+
const attempt = await attemptDaemonHook3({
|
|
11348
|
+
hookEvent: "SessionStart",
|
|
11349
|
+
cli: "claude",
|
|
11350
|
+
stdin: JSON.stringify({
|
|
11351
|
+
hook_event_name: "SessionStart",
|
|
11352
|
+
source: "failproofai-health-probe"
|
|
11353
|
+
})
|
|
11354
|
+
}, { responseTimeoutMs: DAEMON_PROBE_TIMEOUT_MS });
|
|
11355
|
+
if (attempt.ok || attempt.failure === "protocol-mismatch")
|
|
11356
|
+
return { ok: true };
|
|
11357
|
+
}
|
|
11358
|
+
if (Date.now() >= deadline)
|
|
11359
|
+
break;
|
|
11360
|
+
await new Promise((r) => setTimeout(r, DAEMON_PROBE_RETRY_MS));
|
|
11361
|
+
}
|
|
11362
|
+
return { ok: false, reason: everConnected ? "worker" : "unreachable" };
|
|
11221
11363
|
} catch {
|
|
11222
|
-
return false;
|
|
11364
|
+
return { ok: false, reason: "unreachable" };
|
|
11223
11365
|
}
|
|
11224
11366
|
}
|
|
11367
|
+
async function probeDaemonEndToEnd() {
|
|
11368
|
+
return (await probeDaemon()).ok;
|
|
11369
|
+
}
|
|
11225
11370
|
async function waitForDaemonRunning() {
|
|
11226
11371
|
const deadline = Date.now() + SERVICE_START_TIMEOUT_MS;
|
|
11227
11372
|
for (;; ) {
|
|
@@ -11406,6 +11551,16 @@ function daemonVersionSkew() {
|
|
|
11406
11551
|
return null;
|
|
11407
11552
|
return recorded === version2 ? null : { installed: recorded, expected: version2 };
|
|
11408
11553
|
}
|
|
11554
|
+
function inactiveLinuxStatus() {
|
|
11555
|
+
try {
|
|
11556
|
+
return interpretConditionResult(execFileSync3("systemctl", ["show", systemdUnitName(), "--property=ConditionResult", "--value"], { stdio: ["ignore", "pipe", "ignore"], timeout: SERVICE_CMD_TIMEOUT_MS }).toString());
|
|
11557
|
+
} catch {
|
|
11558
|
+
return "stopped";
|
|
11559
|
+
}
|
|
11560
|
+
}
|
|
11561
|
+
function interpretConditionResult(raw) {
|
|
11562
|
+
return raw.trim() === "no" ? "condition-failed" : "stopped";
|
|
11563
|
+
}
|
|
11409
11564
|
function daemonServiceStatus() {
|
|
11410
11565
|
if (!isDaemonSupportedPlatform())
|
|
11411
11566
|
return "unsupported-platform";
|
|
@@ -11417,9 +11572,9 @@ function daemonServiceStatus() {
|
|
|
11417
11572
|
stdio: ["ignore", "pipe", "ignore"],
|
|
11418
11573
|
timeout: SERVICE_CMD_TIMEOUT_MS
|
|
11419
11574
|
}).toString().trim();
|
|
11420
|
-
return out === "active" ? "running" :
|
|
11575
|
+
return out === "active" ? "running" : inactiveLinuxStatus();
|
|
11421
11576
|
} catch {
|
|
11422
|
-
return
|
|
11577
|
+
return inactiveLinuxStatus();
|
|
11423
11578
|
}
|
|
11424
11579
|
}
|
|
11425
11580
|
const plistPath = launchdPlistPath();
|
|
@@ -11440,7 +11595,7 @@ function daemonServiceStatus() {
|
|
|
11440
11595
|
return "stopped";
|
|
11441
11596
|
}
|
|
11442
11597
|
}
|
|
11443
|
-
var SERVICE_CMD_TIMEOUT_MS = 1e4, SERVICE_START_TIMEOUT_MS = 5000, SERVICE_START_POLL_MS = 100, SERVICE_SETTLE_MS = 750, SUDO_PROMPT_TIMEOUT_MS = 120000, UNIT_UNSAFE_RE, DAEMON_PROBE_TIMEOUT_MS = 5000;
|
|
11598
|
+
var SERVICE_CMD_TIMEOUT_MS = 1e4, SERVICE_START_TIMEOUT_MS = 5000, SERVICE_START_POLL_MS = 100, SERVICE_SETTLE_MS = 750, SUDO_PROMPT_TIMEOUT_MS = 120000, UNIT_UNSAFE_RE, DAEMON_PROBE_TIMEOUT_MS = 5000, DAEMON_PROBE_READY_TIMEOUT_MS = 1e4, DAEMON_PROBE_RETRY_MS = 150;
|
|
11444
11599
|
var init_daemon_service = __esm(() => {
|
|
11445
11600
|
init_hook_logger();
|
|
11446
11601
|
init_daemon_download();
|
|
@@ -11906,6 +12061,20 @@ async function healDaemonFlag() {
|
|
|
11906
12061
|
``
|
|
11907
12062
|
];
|
|
11908
12063
|
}
|
|
12064
|
+
if (status === "condition-failed") {
|
|
12065
|
+
updateConfig({ daemon: { configured: false } });
|
|
12066
|
+
return [
|
|
12067
|
+
`failproofaid is installed but cannot start — a file its service requires is`,
|
|
12068
|
+
`gone (most often because failproofai was removed with \`npm rm -g\`, which`,
|
|
12069
|
+
`deletes the worker but leaves the service behind). This machine was`,
|
|
12070
|
+
`configured to require the daemon, which denies every tool call, so that flag`,
|
|
12071
|
+
`is cleared; policies now evaluate in-process.`,
|
|
12072
|
+
``,
|
|
12073
|
+
`Run \`failproofai uninstall\` to remove the leftover service, or`,
|
|
12074
|
+
`\`failproofai config\` to rebuild it. \`${daemonStatusCommand()}\` names the missing path.`,
|
|
12075
|
+
``
|
|
12076
|
+
];
|
|
12077
|
+
}
|
|
11909
12078
|
if (status === "running" && !await probeDaemonEndToEnd()) {
|
|
11910
12079
|
updateConfig({ daemon: { configured: false } });
|
|
11911
12080
|
return [
|
|
@@ -12215,7 +12384,8 @@ var init_first_run_gate = __esm(() => {
|
|
|
12215
12384
|
FIRST_RUN_EXEMPT_SUBCOMMANDS = [
|
|
12216
12385
|
"config",
|
|
12217
12386
|
"policies",
|
|
12218
|
-
"policy"
|
|
12387
|
+
"policy",
|
|
12388
|
+
"uninstall"
|
|
12219
12389
|
];
|
|
12220
12390
|
});
|
|
12221
12391
|
|
|
@@ -21925,6 +22095,129 @@ This file can be committed to git — no machine-specific paths.`);
|
|
|
21925
22095
|
} catch {}
|
|
21926
22096
|
}
|
|
21927
22097
|
}
|
|
22098
|
+
async function removeHooks(policyNames, scope = "user", cwd, opts) {
|
|
22099
|
+
const configScope = scope === "all" ? "user" : scope;
|
|
22100
|
+
const selectedClis = opts?.cli && opts.cli.length > 0 ? [...new Set(opts.cli)] : ["claude"];
|
|
22101
|
+
if (opts?.removeCustomHooks) {
|
|
22102
|
+
const config = readScopedHooksConfig(configScope, cwd);
|
|
22103
|
+
delete config.customPoliciesPath;
|
|
22104
|
+
delete config.customPoliciesPaths;
|
|
22105
|
+
writeScopedHooksConfig(config, configScope, cwd);
|
|
22106
|
+
console.log("Custom hooks path cleared.");
|
|
22107
|
+
}
|
|
22108
|
+
if (policyNames && policyNames.length > 0 && !(policyNames.length === 1 && policyNames[0] === "all")) {
|
|
22109
|
+
validatePolicyNames(policyNames);
|
|
22110
|
+
const config = readScopedHooksConfig(configScope, cwd);
|
|
22111
|
+
const removeSet = new Set(policyNames);
|
|
22112
|
+
const remaining = config.enabledPolicies.filter((p) => !removeSet.has(p));
|
|
22113
|
+
const notEnabled = policyNames.filter((p) => !config.enabledPolicies.includes(p));
|
|
22114
|
+
if (notEnabled.length > 0) {
|
|
22115
|
+
console.log(`Warning: policy(ies) not currently enabled: ${notEnabled.join(", ")}`);
|
|
22116
|
+
}
|
|
22117
|
+
const { policyParams: existingParams, ...baseConfig } = config;
|
|
22118
|
+
const filteredParams = existingParams ? Object.fromEntries(Object.entries(existingParams).filter(([k]) => !removeSet.has(k))) : null;
|
|
22119
|
+
const updatedConfig = {
|
|
22120
|
+
...baseConfig,
|
|
22121
|
+
enabledPolicies: remaining,
|
|
22122
|
+
...filteredParams && Object.keys(filteredParams).length > 0 ? { policyParams: filteredParams } : {}
|
|
22123
|
+
};
|
|
22124
|
+
writeScopedHooksConfig(updatedConfig, configScope, cwd);
|
|
22125
|
+
try {
|
|
22126
|
+
const distinctId = getInstanceId2();
|
|
22127
|
+
const actuallyRemoved = policyNames.filter((p) => config.enabledPolicies.includes(p));
|
|
22128
|
+
await trackHookEvent2(distinctId, "hooks_removed", {
|
|
22129
|
+
scope,
|
|
22130
|
+
cli: selectedClis,
|
|
22131
|
+
removal_mode: opts?.betaOnly ? "beta_policies" : "policies",
|
|
22132
|
+
beta_only: opts?.betaOnly ?? false,
|
|
22133
|
+
policies_removed: actuallyRemoved,
|
|
22134
|
+
removed_count: actuallyRemoved.length,
|
|
22135
|
+
...opts?.source ? { source: opts.source } : {},
|
|
22136
|
+
platform: platform3(),
|
|
22137
|
+
arch: arch3(),
|
|
22138
|
+
os_release: release2(),
|
|
22139
|
+
hostname_hash: hashToId2(hostname3())
|
|
22140
|
+
});
|
|
22141
|
+
} catch {}
|
|
22142
|
+
console.log(`Disabled ${policyNames.length - notEnabled.length} policy(ies).`);
|
|
22143
|
+
console.log(`Remaining: ${remaining.length > 0 ? remaining.join(", ") : "(none)"}`);
|
|
22144
|
+
return;
|
|
22145
|
+
}
|
|
22146
|
+
const configBeforeRemoval = readScopedHooksConfig(configScope, cwd);
|
|
22147
|
+
let totalRemoved = 0;
|
|
22148
|
+
let nothingToReport = false;
|
|
22149
|
+
for (const cliId of selectedClis) {
|
|
22150
|
+
const integration = getIntegration(cliId);
|
|
22151
|
+
const scopesToRemove = scope === "all" ? [...integration.scopes] : integration.scopes.includes(scope) ? [scope] : [];
|
|
22152
|
+
for (const s of scopesToRemove) {
|
|
22153
|
+
const settingsPaths = settingsPathsFor(integration, s, cwd);
|
|
22154
|
+
const existing = settingsPaths.filter((p) => existsSync21(p));
|
|
22155
|
+
if (existing.length === 0) {
|
|
22156
|
+
if (scope !== "all" && selectedClis.length === 1) {
|
|
22157
|
+
console.log("No settings file found. Nothing to remove.");
|
|
22158
|
+
nothingToReport = true;
|
|
22159
|
+
}
|
|
22160
|
+
continue;
|
|
22161
|
+
}
|
|
22162
|
+
let removedHere = 0;
|
|
22163
|
+
for (const settingsPath of existing) {
|
|
22164
|
+
const removed = integration.removeHooksFromFile(settingsPath);
|
|
22165
|
+
removedHere += removed;
|
|
22166
|
+
if (removed > 0 && scope !== "all") {
|
|
22167
|
+
console.log(`Removed ${removed} failproofai hook(s) from ${integration.displayName} settings.`);
|
|
22168
|
+
console.log(`Settings: ${settingsPath}`);
|
|
22169
|
+
}
|
|
22170
|
+
}
|
|
22171
|
+
if (removedHere === 0 && scope !== "all" && selectedClis.length === 1) {
|
|
22172
|
+
console.log("No hooks found in settings. Nothing to remove.");
|
|
22173
|
+
nothingToReport = true;
|
|
22174
|
+
continue;
|
|
22175
|
+
}
|
|
22176
|
+
totalRemoved += removedHere;
|
|
22177
|
+
}
|
|
22178
|
+
}
|
|
22179
|
+
if (nothingToReport && totalRemoved === 0)
|
|
22180
|
+
return;
|
|
22181
|
+
if (scope === "all") {
|
|
22182
|
+
console.log(`Removed ${totalRemoved} failproofai hook(s) from all scopes.`);
|
|
22183
|
+
for (const cliId of selectedClis) {
|
|
22184
|
+
const integration = getIntegration(cliId);
|
|
22185
|
+
for (const s of integration.scopes) {
|
|
22186
|
+
for (const p of settingsPathsFor(integration, s, cwd)) {
|
|
22187
|
+
console.log(` ${integration.displayName} / ${s}: ${p}`);
|
|
22188
|
+
}
|
|
22189
|
+
}
|
|
22190
|
+
}
|
|
22191
|
+
}
|
|
22192
|
+
try {
|
|
22193
|
+
const distinctId = getInstanceId2();
|
|
22194
|
+
await trackHookEvent2(distinctId, "hooks_removed", {
|
|
22195
|
+
scope,
|
|
22196
|
+
cli: selectedClis,
|
|
22197
|
+
removal_mode: "hooks",
|
|
22198
|
+
policies_removed: configBeforeRemoval.enabledPolicies,
|
|
22199
|
+
removed_count: totalRemoved,
|
|
22200
|
+
...opts?.source ? { source: opts.source } : {},
|
|
22201
|
+
platform: platform3(),
|
|
22202
|
+
arch: arch3(),
|
|
22203
|
+
os_release: release2(),
|
|
22204
|
+
hostname_hash: hashToId2(hostname3())
|
|
22205
|
+
});
|
|
22206
|
+
} catch {}
|
|
22207
|
+
if (scope === "all") {
|
|
22208
|
+
for (const s of HOOK_SCOPES) {
|
|
22209
|
+
const existing = readScopedHooksConfig(s, cwd);
|
|
22210
|
+
if (existing.enabledPolicies.length > 0 || existing.customPoliciesPaths?.length || existing.customPoliciesPath || existing.policyParams) {
|
|
22211
|
+
const { customPoliciesPath: _drop, customPoliciesPaths: _dropMany, policyParams: _dropParams, ...rest } = existing;
|
|
22212
|
+
writeScopedHooksConfig({ ...rest, enabledPolicies: [] }, s, cwd);
|
|
22213
|
+
}
|
|
22214
|
+
}
|
|
22215
|
+
} else if (!HOOK_SCOPES.some((s) => hooksInstalledInSettings(s, cwd))) {
|
|
22216
|
+
const existing = readScopedHooksConfig(configScope, cwd);
|
|
22217
|
+
const { customPoliciesPath: _drop, customPoliciesPaths: _dropMany, policyParams: _dropParams, ...rest } = existing;
|
|
22218
|
+
writeScopedHooksConfig({ ...rest, enabledPolicies: [] }, configScope, cwd);
|
|
22219
|
+
}
|
|
22220
|
+
}
|
|
21928
22221
|
var VALID_POLICY_NAMES;
|
|
21929
22222
|
var init_manager = __esm(() => {
|
|
21930
22223
|
init_types();
|
|
@@ -25862,18 +26155,22 @@ Setup stopped before changing anything. Once that is fixed, re-run:
|
|
|
25862
26155
|
outro("Nothing was changed.", { ok: false }, stdout);
|
|
25863
26156
|
return { applied: false, abort: "daemon_failed" };
|
|
25864
26157
|
}
|
|
25865
|
-
|
|
25866
|
-
|
|
25867
|
-
|
|
25868
|
-
|
|
25869
|
-
|
|
25870
|
-
|
|
26158
|
+
const probe = await probeDaemon();
|
|
26159
|
+
if (!probe.ok) {
|
|
26160
|
+
hookLogWarn(`failproofaid was installed and running but did not answer a policy evaluation (${probe.reason})`);
|
|
26161
|
+
stdout.write((probe.reason === "worker" ? `
|
|
26162
|
+
failproofaid started but cannot evaluate policies — it is listening, and
|
|
26163
|
+
` + `its worker process could not be run.
|
|
26164
|
+
` : `
|
|
26165
|
+
failproofaid started but could not be reached on its socket.
|
|
26166
|
+
`) + `Setup stopped before changing anything, because a machine configured to
|
|
26167
|
+
require a daemon that cannot answer denies every tool call.
|
|
25871
26168
|
|
|
25872
26169
|
Check it with: ${daemonStatusCommand() ?? "systemctl status failproofaid"}
|
|
25873
26170
|
Then re-run: failproofai config
|
|
25874
26171
|
|
|
25875
26172
|
`);
|
|
25876
|
-
emit("configure_aborted", { reason:
|
|
26173
|
+
emit("configure_aborted", { reason: `daemon_not_answering_${probe.reason}` });
|
|
25877
26174
|
outro("Nothing was changed.", { ok: false }, stdout);
|
|
25878
26175
|
return { applied: false, abort: "daemon_failed" };
|
|
25879
26176
|
}
|
|
@@ -25998,55 +26295,735 @@ var init_configure_wizard = __esm(() => {
|
|
|
25998
26295
|
init_onboarding_lock();
|
|
25999
26296
|
});
|
|
26000
26297
|
|
|
26001
|
-
// src/hooks/
|
|
26002
|
-
var
|
|
26003
|
-
__export(
|
|
26004
|
-
|
|
26005
|
-
|
|
26006
|
-
installHooks: () => installHooks2,
|
|
26007
|
-
hooksInstalledInSettings: () => hooksInstalledInSettings2,
|
|
26008
|
-
getSettingsPath: () => getSettingsPath2
|
|
26298
|
+
// src/hooks/uninstall-cli.ts
|
|
26299
|
+
var exports_uninstall_cli = {};
|
|
26300
|
+
__export(exports_uninstall_cli, {
|
|
26301
|
+
serviceStateLabel: () => serviceStateLabel,
|
|
26302
|
+
runUninstallCommand: () => runUninstallCommand
|
|
26009
26303
|
});
|
|
26010
|
-
import {
|
|
26011
|
-
|
|
26012
|
-
|
|
26013
|
-
|
|
26014
|
-
|
|
26015
|
-
|
|
26016
|
-
|
|
26017
|
-
|
|
26018
|
-
|
|
26019
|
-
|
|
26020
|
-
|
|
26021
|
-
|
|
26022
|
-
return `{cwd}/.claude/settings.json`;
|
|
26023
|
-
case "local":
|
|
26024
|
-
return `{cwd}/.claude/settings.local.json`;
|
|
26304
|
+
import { existsSync as existsSync31, rmSync as rmSync10 } from "node:fs";
|
|
26305
|
+
function survey(cwd) {
|
|
26306
|
+
const clis = [];
|
|
26307
|
+
for (const id of listInstallableIds()) {
|
|
26308
|
+
for (const scope of ["user", "project", "local"]) {
|
|
26309
|
+
try {
|
|
26310
|
+
if (getIntegration(id).hooksInstalledInSettings(scope, cwd)) {
|
|
26311
|
+
clis.push(id);
|
|
26312
|
+
break;
|
|
26313
|
+
}
|
|
26314
|
+
} catch {}
|
|
26315
|
+
}
|
|
26025
26316
|
}
|
|
26026
|
-
|
|
26027
|
-
function resolveFailproofaiBinary2() {
|
|
26028
|
-
const override = process.env.FAILPROOFAI_BINARY_OVERRIDE;
|
|
26029
|
-
if (override && override.trim())
|
|
26030
|
-
return override.trim();
|
|
26317
|
+
let daemonConfigured = false;
|
|
26031
26318
|
try {
|
|
26032
|
-
|
|
26033
|
-
|
|
26034
|
-
|
|
26035
|
-
|
|
26036
|
-
|
|
26037
|
-
|
|
26038
|
-
|
|
26039
|
-
|
|
26040
|
-
|
|
26041
|
-
|
|
26042
|
-
const invalid = names.filter((n) => !VALID_POLICY_NAMES2.has(n));
|
|
26043
|
-
if (invalid.length > 0) {
|
|
26044
|
-
const validList = [...VALID_POLICY_NAMES2].join(", ");
|
|
26045
|
-
throw new CliError(`Unknown policy name(s): ${invalid.join(", ")}
|
|
26046
|
-
` + `Valid policies: ${validList}`);
|
|
26047
|
-
}
|
|
26319
|
+
daemonConfigured = readConfig().daemon.configured;
|
|
26320
|
+
} catch {}
|
|
26321
|
+
const servicePath = isDaemonSupportedPlatform() ? daemonServiceFilePath() : null;
|
|
26322
|
+
return {
|
|
26323
|
+
clis,
|
|
26324
|
+
servicePath,
|
|
26325
|
+
serviceInstalled: !!servicePath && existsSync31(servicePath),
|
|
26326
|
+
daemonConfigured,
|
|
26327
|
+
homeExists: existsSync31(failproofaiHome())
|
|
26328
|
+
};
|
|
26048
26329
|
}
|
|
26049
|
-
function
|
|
26330
|
+
async function runUninstallCommand(opts = {}) {
|
|
26331
|
+
const found = survey(opts.cwd);
|
|
26332
|
+
const lines = [];
|
|
26333
|
+
const nothingToDo = found.clis.length === 0 && !found.serviceInstalled && !found.daemonConfigured && !(opts.purge && found.homeExists);
|
|
26334
|
+
if (nothingToDo) {
|
|
26335
|
+
return {
|
|
26336
|
+
exitCode: 0,
|
|
26337
|
+
planLines: 0,
|
|
26338
|
+
purged: false,
|
|
26339
|
+
lines: [
|
|
26340
|
+
"Nothing to uninstall — no hook entries, no daemon service, and nothing configured.",
|
|
26341
|
+
...found.homeExists && !opts.purge ? [
|
|
26342
|
+
``,
|
|
26343
|
+
`${failproofaiHome()} still holds settings and audit history.`,
|
|
26344
|
+
`Remove it with \`failproofai uninstall --purge\`.`
|
|
26345
|
+
] : []
|
|
26346
|
+
]
|
|
26347
|
+
};
|
|
26348
|
+
}
|
|
26349
|
+
const plan = [];
|
|
26350
|
+
if (found.clis.length > 0) {
|
|
26351
|
+
plan.push(` • remove failproofai hook entries from ${found.clis.length} agent CLI${found.clis.length === 1 ? "" : "s"}: ` + found.clis.map((id) => getIntegration(id).displayName ?? id).join(", "));
|
|
26352
|
+
}
|
|
26353
|
+
if (found.daemonConfigured) {
|
|
26354
|
+
plan.push(` • stop requiring the daemon (policies go back to evaluating in-process)`);
|
|
26355
|
+
}
|
|
26356
|
+
if (found.serviceInstalled) {
|
|
26357
|
+
plan.push(` • stop, disable and delete the service at ${found.servicePath} — needs sudo`);
|
|
26358
|
+
}
|
|
26359
|
+
let purged = false;
|
|
26360
|
+
if (opts.purge && found.homeExists) {
|
|
26361
|
+
plan.push(` • delete ${failproofaiHome()} — settings, credentials, audit history and the daemon binary`);
|
|
26362
|
+
}
|
|
26363
|
+
lines.push("failproofai uninstall will:", ...plan);
|
|
26364
|
+
if (!opts.purge && found.homeExists) {
|
|
26365
|
+
lines.push(``, `${failproofaiHome()} is KEPT (settings and audit history survive a reinstall).`, `Add --purge to delete it too.`);
|
|
26366
|
+
}
|
|
26367
|
+
const planLines = lines.length;
|
|
26368
|
+
if (opts.dryRun) {
|
|
26369
|
+
lines.push(``, `--dry-run: nothing was changed.`);
|
|
26370
|
+
return { exitCode: 0, lines, planLines, purged: false };
|
|
26371
|
+
}
|
|
26372
|
+
if (!opts.yes) {
|
|
26373
|
+
if (!opts.confirm) {
|
|
26374
|
+
lines.push(``, `Refusing to uninstall without confirmation. Re-run with --yes.`);
|
|
26375
|
+
return { exitCode: 1, lines, planLines, purged: false };
|
|
26376
|
+
}
|
|
26377
|
+
if (!await opts.confirm(lines)) {
|
|
26378
|
+
return { exitCode: 1, planLines, purged: false, lines: [...lines, ``, `Cancelled — nothing was changed.`] };
|
|
26379
|
+
}
|
|
26380
|
+
}
|
|
26381
|
+
lines.push(``);
|
|
26382
|
+
const failures = [];
|
|
26383
|
+
if (found.daemonConfigured) {
|
|
26384
|
+
try {
|
|
26385
|
+
setDaemonConfigured(false);
|
|
26386
|
+
lines.push(`✓ policies now evaluate in-process (this machine no longer requires the daemon)`);
|
|
26387
|
+
} catch (err) {
|
|
26388
|
+
return {
|
|
26389
|
+
exitCode: 2,
|
|
26390
|
+
planLines,
|
|
26391
|
+
purged: false,
|
|
26392
|
+
lines: [
|
|
26393
|
+
...lines,
|
|
26394
|
+
`✗ could not update ${failproofaiHome()}/config.toml: ${err instanceof Error ? err.message : String(err)}`,
|
|
26395
|
+
``,
|
|
26396
|
+
`Stopped before touching the service. Removing it while this machine still`,
|
|
26397
|
+
`requires it would deny every tool call. Fix the file's permissions and re-run.`
|
|
26398
|
+
]
|
|
26399
|
+
};
|
|
26400
|
+
}
|
|
26401
|
+
}
|
|
26402
|
+
if (found.clis.length > 0) {
|
|
26403
|
+
try {
|
|
26404
|
+
await removeHooks(undefined, "all", opts.cwd, {
|
|
26405
|
+
cli: found.clis,
|
|
26406
|
+
removeCustomHooks: true,
|
|
26407
|
+
source: "uninstall_command"
|
|
26408
|
+
});
|
|
26409
|
+
lines.push(`✓ removed hook entries from ${found.clis.length} agent CLI${found.clis.length === 1 ? "" : "s"}`);
|
|
26410
|
+
} catch (err) {
|
|
26411
|
+
failures.push(`hook entries: ${err instanceof Error ? err.message : String(err)} ` + `(finish with \`failproofai policies --uninstall --scope all\`)`);
|
|
26412
|
+
}
|
|
26413
|
+
}
|
|
26414
|
+
if (found.serviceInstalled) {
|
|
26415
|
+
try {
|
|
26416
|
+
await uninstallDaemonService();
|
|
26417
|
+
if (found.servicePath && existsSync31(found.servicePath)) {
|
|
26418
|
+
failures.push(`the service at ${found.servicePath} is still there (most often: no sudo). ` + `Remove it with the commands below.`);
|
|
26419
|
+
} else {
|
|
26420
|
+
lines.push(`✓ stopped and removed the daemon service`);
|
|
26421
|
+
}
|
|
26422
|
+
} catch (err) {
|
|
26423
|
+
failures.push(`daemon service: ${err instanceof Error ? err.message : String(err)}`);
|
|
26424
|
+
}
|
|
26425
|
+
}
|
|
26426
|
+
if (opts.purge && found.homeExists) {
|
|
26427
|
+
try {
|
|
26428
|
+
rmSync10(failproofaiHome(), { recursive: true, force: true });
|
|
26429
|
+
purged = true;
|
|
26430
|
+
lines.push(`✓ deleted ${failproofaiHome()}`);
|
|
26431
|
+
} catch (err) {
|
|
26432
|
+
failures.push(`${failproofaiHome()}: ${err instanceof Error ? err.message : String(err)}`);
|
|
26433
|
+
}
|
|
26434
|
+
}
|
|
26435
|
+
if (failures.length > 0) {
|
|
26436
|
+
lines.push(``, `Not everything could be removed:`);
|
|
26437
|
+
for (const f of failures)
|
|
26438
|
+
lines.push(` ✗ ${f}`);
|
|
26439
|
+
if (found.servicePath && existsSync31(found.servicePath)) {
|
|
26440
|
+
lines.push(``, `To remove the service by hand:`, ...manualServiceRemoval(found.servicePath), ``, `(\`${daemonStatusCommand()}\` shows its current state.)`);
|
|
26441
|
+
}
|
|
26442
|
+
return { exitCode: 1, lines, planLines, purged };
|
|
26443
|
+
}
|
|
26444
|
+
lines.push(``, `Done. failproofai no longer enforces anything on this machine.`, ``, `The npm package itself is still installed — npm runs no uninstall script, which`, `is why this command exists. Remove it with:`, ` npm rm -g failproofai`);
|
|
26445
|
+
if (!opts.purge && found.homeExists) {
|
|
26446
|
+
lines.push(``, `${failproofaiHome()} was kept. Delete it with \`failproofai uninstall --purge\`.`);
|
|
26447
|
+
}
|
|
26448
|
+
return { exitCode: 0, lines, planLines, purged };
|
|
26449
|
+
}
|
|
26450
|
+
function manualServiceRemoval(servicePath) {
|
|
26451
|
+
if (process.platform === "darwin") {
|
|
26452
|
+
return [` sudo launchctl unload -w ${servicePath}`, ` sudo rm -f ${servicePath}`];
|
|
26453
|
+
}
|
|
26454
|
+
const unit = servicePath.split("/").pop() ?? servicePath;
|
|
26455
|
+
return [
|
|
26456
|
+
` sudo systemctl disable --now ${unit}`,
|
|
26457
|
+
` sudo rm -f ${servicePath}`,
|
|
26458
|
+
` sudo systemctl daemon-reload`
|
|
26459
|
+
];
|
|
26460
|
+
}
|
|
26461
|
+
function serviceStateLabel() {
|
|
26462
|
+
const status = daemonServiceStatus();
|
|
26463
|
+
return status === "condition-failed" ? "installed but skipped by systemd (a file it needs is missing)" : status;
|
|
26464
|
+
}
|
|
26465
|
+
var init_uninstall_cli = __esm(() => {
|
|
26466
|
+
init_fp_home();
|
|
26467
|
+
init_fp_config();
|
|
26468
|
+
init_daemon_service();
|
|
26469
|
+
init_integrations();
|
|
26470
|
+
init_manager();
|
|
26471
|
+
});
|
|
26472
|
+
|
|
26473
|
+
// src/hooks/tui.ts
|
|
26474
|
+
var exports_tui = {};
|
|
26475
|
+
__export(exports_tui, {
|
|
26476
|
+
truncate: () => truncate2,
|
|
26477
|
+
summarize: () => summarize2,
|
|
26478
|
+
selectOne: () => selectOne2,
|
|
26479
|
+
renderLaunchBanner: () => renderLaunchBanner2,
|
|
26480
|
+
renderBrandLogo: () => renderBrandLogo2,
|
|
26481
|
+
promptText: () => promptText2,
|
|
26482
|
+
paint: () => paint2,
|
|
26483
|
+
outro: () => outro2,
|
|
26484
|
+
multiSelect: () => multiSelect2,
|
|
26485
|
+
intro: () => intro2,
|
|
26486
|
+
ellipsize: () => ellipsize2,
|
|
26487
|
+
colorsEnabled: () => colorsEnabled2,
|
|
26488
|
+
brandAnsi: () => brandAnsi2,
|
|
26489
|
+
STEP_DONE: () => STEP_DONE2,
|
|
26490
|
+
STEP_ACTIVE: () => STEP_ACTIVE2,
|
|
26491
|
+
CHECK_ON: () => CHECK_ON2,
|
|
26492
|
+
CHECK_OFF: () => CHECK_OFF2,
|
|
26493
|
+
CARET: () => CARET2,
|
|
26494
|
+
BAR: () => BAR2,
|
|
26495
|
+
ANSI_RESET: () => ANSI_RESET2,
|
|
26496
|
+
ANSI_DIM: () => ANSI_DIM2,
|
|
26497
|
+
ANSI_BOLD: () => ANSI_BOLD2
|
|
26498
|
+
});
|
|
26499
|
+
import * as readline3 from "node:readline";
|
|
26500
|
+
function colorsEnabled2(out) {
|
|
26501
|
+
return !!out.isTTY && !process.env.NO_COLOR;
|
|
26502
|
+
}
|
|
26503
|
+
function brandAnsi2(role) {
|
|
26504
|
+
const h = HUES2[role];
|
|
26505
|
+
const code = truecolorEnabled2() ? `38;2;${h.rgb.join(";")}` : h.basic;
|
|
26506
|
+
return `${ESC2}[${code}m`;
|
|
26507
|
+
}
|
|
26508
|
+
function truecolorEnabled2() {
|
|
26509
|
+
return /truecolor|24bit/i.test(process.env.COLORTERM || "");
|
|
26510
|
+
}
|
|
26511
|
+
function paint2(on) {
|
|
26512
|
+
const tc = on && truecolorEnabled2();
|
|
26513
|
+
const mk = (h, bold = false) => (s) => {
|
|
26514
|
+
if (!on)
|
|
26515
|
+
return s;
|
|
26516
|
+
const code = tc ? `38;2;${h.rgb[0]};${h.rgb[1]};${h.rgb[2]}` : h.basic;
|
|
26517
|
+
return `${ESC2}[${bold ? "1;" : ""}${code}m${s}${ESC2}[0m`;
|
|
26518
|
+
};
|
|
26519
|
+
return {
|
|
26520
|
+
bold: (s) => on ? `${ESC2}[1m${s}${ESC2}[0m` : s,
|
|
26521
|
+
dim: mk(HUES2.dim),
|
|
26522
|
+
guide: mk(HUES2.guide),
|
|
26523
|
+
pink: mk(HUES2.pink),
|
|
26524
|
+
pinkBold: mk(HUES2.pink, true),
|
|
26525
|
+
softPink: mk(HUES2.logoPink),
|
|
26526
|
+
warn: mk(HUES2.warn)
|
|
26527
|
+
};
|
|
26528
|
+
}
|
|
26529
|
+
function renderLogo2(colorize) {
|
|
26530
|
+
const pad = ".".repeat(LOGO_GRID2[0]?.length ?? 0);
|
|
26531
|
+
const rgb = (ch) => ch === "t" ? LOGO_TEAL2 : ch === "p" ? LOGO_PINK2 : null;
|
|
26532
|
+
const lines = [];
|
|
26533
|
+
for (let r = 0;r < LOGO_GRID2.length; r += 2) {
|
|
26534
|
+
const top = LOGO_GRID2[r];
|
|
26535
|
+
const bot = LOGO_GRID2[r + 1] ?? pad;
|
|
26536
|
+
let line = "";
|
|
26537
|
+
for (let x = 0;x < top.length; x++) {
|
|
26538
|
+
const t = rgb(top[x]);
|
|
26539
|
+
const b = rgb(bot[x]);
|
|
26540
|
+
if (!t && !b) {
|
|
26541
|
+
line += " ";
|
|
26542
|
+
} else if (!colorize) {
|
|
26543
|
+
line += t && b ? "█" : t ? "▀" : "▄";
|
|
26544
|
+
} else if (t && b) {
|
|
26545
|
+
line += t === b ? `${ESC2}[38;2;${t.join(";")}m█${ESC2}[0m` : `${ESC2}[38;2;${t.join(";")};48;2;${b.join(";")}m▀${ESC2}[0m`;
|
|
26546
|
+
} else if (t) {
|
|
26547
|
+
line += `${ESC2}[38;2;${t.join(";")}m▀${ESC2}[0m`;
|
|
26548
|
+
} else {
|
|
26549
|
+
line += `${ESC2}[38;2;${b.join(";")}m▄${ESC2}[0m`;
|
|
26550
|
+
}
|
|
26551
|
+
}
|
|
26552
|
+
lines.push(line);
|
|
26553
|
+
}
|
|
26554
|
+
return lines;
|
|
26555
|
+
}
|
|
26556
|
+
function truncate2(line, width) {
|
|
26557
|
+
let visual = 0;
|
|
26558
|
+
let out = "";
|
|
26559
|
+
let i = 0;
|
|
26560
|
+
while (i < line.length) {
|
|
26561
|
+
if (line[i] === ESC2 && line[i + 1] === "[") {
|
|
26562
|
+
let j = i + 2;
|
|
26563
|
+
while (j < line.length && !/[A-Za-z]/.test(line[j]))
|
|
26564
|
+
j++;
|
|
26565
|
+
j++;
|
|
26566
|
+
out += line.slice(i, j);
|
|
26567
|
+
i = j;
|
|
26568
|
+
} else {
|
|
26569
|
+
if (visual >= width)
|
|
26570
|
+
break;
|
|
26571
|
+
out += line[i];
|
|
26572
|
+
visual++;
|
|
26573
|
+
i++;
|
|
26574
|
+
}
|
|
26575
|
+
}
|
|
26576
|
+
return out;
|
|
26577
|
+
}
|
|
26578
|
+
function ellipsize2(text, width) {
|
|
26579
|
+
if (width <= 0)
|
|
26580
|
+
return "";
|
|
26581
|
+
if (text.length <= width)
|
|
26582
|
+
return text;
|
|
26583
|
+
return text.slice(0, width - 1).trimEnd() + "…";
|
|
26584
|
+
}
|
|
26585
|
+
function summarize2(labels, noun = "selected") {
|
|
26586
|
+
if (labels.length === 0)
|
|
26587
|
+
return "none";
|
|
26588
|
+
if (labels.length <= 3)
|
|
26589
|
+
return labels.join(", ");
|
|
26590
|
+
const head = labels.slice(0, 3).join(", ");
|
|
26591
|
+
return `${labels.length} ${noun} · ${head} +${labels.length - 3}`;
|
|
26592
|
+
}
|
|
26593
|
+
function writeLines2(out, lines) {
|
|
26594
|
+
const cols = out.columns || 80;
|
|
26595
|
+
out.write(lines.map((l) => l === "" ? l : truncate2(l, cols)).join(`
|
|
26596
|
+
`) + `
|
|
26597
|
+
`);
|
|
26598
|
+
}
|
|
26599
|
+
function renderBrandLogo2(stdout = process.stdout) {
|
|
26600
|
+
const c2 = paint2(colorsEnabled2(stdout));
|
|
26601
|
+
const cols = stdout.columns || 80;
|
|
26602
|
+
if (cols < LOGO_MIN_COLS2) {
|
|
26603
|
+
return [`${c2.guide(FLOWER2)} fa${c2.pink("il")}proof ai ${c2.dim("· " + TAGLINE2)}`];
|
|
26604
|
+
}
|
|
26605
|
+
const tc = colorsEnabled2(stdout) && truecolorEnabled2();
|
|
26606
|
+
const lines = renderLogo2(tc).map((l) => ` ${l}`);
|
|
26607
|
+
lines.push("");
|
|
26608
|
+
lines.push(` fa${c2.pink("il")}proof ai`);
|
|
26609
|
+
lines.push(` ${c2.dim(TAGLINE2)}`);
|
|
26610
|
+
return lines;
|
|
26611
|
+
}
|
|
26612
|
+
function intro2(message, stdout = process.stdout) {
|
|
26613
|
+
if (!stdout.isTTY)
|
|
26614
|
+
return;
|
|
26615
|
+
const c2 = paint2(colorsEnabled2(stdout));
|
|
26616
|
+
const lines = ["", ...renderBrandLogo2(stdout)];
|
|
26617
|
+
lines.push(c2.dim(BAR2));
|
|
26618
|
+
lines.push(`${c2.guide(STEP_ACTIVE2)} ${c2.bold(message)}`);
|
|
26619
|
+
writeLines2(stdout, lines);
|
|
26620
|
+
}
|
|
26621
|
+
function renderLaunchBanner2(version3, stdout = process.stdout) {
|
|
26622
|
+
const c2 = paint2(colorsEnabled2(stdout));
|
|
26623
|
+
const row = (label, value) => ` ${c2.guide(label.padEnd(9))}${value}`;
|
|
26624
|
+
const header = stdout.isTTY ? renderBrandLogo2(stdout) : [" failproof ai", ` ${TAGLINE2}`];
|
|
26625
|
+
return [
|
|
26626
|
+
"",
|
|
26627
|
+
...header,
|
|
26628
|
+
"",
|
|
26629
|
+
row("version", c2.pink(version3)),
|
|
26630
|
+
row("star", c2.dim("https://github.com/failproofai/failproofai")),
|
|
26631
|
+
row("docs", c2.dim("https://docs.befailproof.ai/introduction")),
|
|
26632
|
+
row("discord", c2.dim("https://discord.befailproof.ai/")),
|
|
26633
|
+
""
|
|
26634
|
+
];
|
|
26635
|
+
}
|
|
26636
|
+
function outro2(message, opts = {}, stdout = process.stdout) {
|
|
26637
|
+
const c2 = paint2(colorsEnabled2(stdout));
|
|
26638
|
+
const ok = opts.ok !== false;
|
|
26639
|
+
if (!stdout.isTTY) {
|
|
26640
|
+
stdout.write(message + `
|
|
26641
|
+
`);
|
|
26642
|
+
return;
|
|
26643
|
+
}
|
|
26644
|
+
const end = ok ? c2.pink(BAR_END2) : c2.dim(BAR_END2);
|
|
26645
|
+
const text = ok ? c2.pink(message) : c2.dim(message);
|
|
26646
|
+
writeLines2(stdout, [c2.dim(BAR2), `${end} ${text}`]);
|
|
26647
|
+
}
|
|
26648
|
+
function repaint2(out, region, lines) {
|
|
26649
|
+
if (region.lastCount > 0)
|
|
26650
|
+
out.write(`${ESC2}[${region.lastCount}A${ESC2}[J`);
|
|
26651
|
+
writeLines2(out, lines);
|
|
26652
|
+
region.lastCount = lines.length;
|
|
26653
|
+
}
|
|
26654
|
+
function hideCursor2(out) {
|
|
26655
|
+
out.write(`${ESC2}[?25l`);
|
|
26656
|
+
}
|
|
26657
|
+
function showCursor2(out) {
|
|
26658
|
+
out.write(`${ESC2}[?25h`);
|
|
26659
|
+
}
|
|
26660
|
+
function nameWidth2(labels) {
|
|
26661
|
+
return Math.min(24, Math.max(6, ...labels.map((l) => l.length)));
|
|
26662
|
+
}
|
|
26663
|
+
function displayRows2(choices) {
|
|
26664
|
+
const rows = [];
|
|
26665
|
+
let lastSection;
|
|
26666
|
+
choices.forEach((choice, index) => {
|
|
26667
|
+
if (choice.section && choice.section !== lastSection) {
|
|
26668
|
+
lastSection = choice.section;
|
|
26669
|
+
rows.push({ kind: "header", text: choice.section });
|
|
26670
|
+
}
|
|
26671
|
+
rows.push({ kind: "item", index });
|
|
26672
|
+
});
|
|
26673
|
+
return rows;
|
|
26674
|
+
}
|
|
26675
|
+
function viewport2(rows, cursorRow, window) {
|
|
26676
|
+
if (rows.length <= window)
|
|
26677
|
+
return { start: 0, end: rows.length };
|
|
26678
|
+
let start = cursorRow - Math.floor(window / 2);
|
|
26679
|
+
start = Math.max(0, Math.min(start, rows.length - window));
|
|
26680
|
+
return { start, end: start + window };
|
|
26681
|
+
}
|
|
26682
|
+
function runPrompt2(p) {
|
|
26683
|
+
const { stdin, stdout, c: c2, choices } = p;
|
|
26684
|
+
const region = { lastCount: 0 };
|
|
26685
|
+
const nameCol = nameWidth2(choices.map((ch) => ch.label));
|
|
26686
|
+
let cursor2 = 0;
|
|
26687
|
+
const build = () => {
|
|
26688
|
+
const cols = stdout.columns || 80;
|
|
26689
|
+
const lines = [c2.dim(BAR2), `${c2.guide(STEP_ACTIVE2)} ${c2.bold(p.message)}`];
|
|
26690
|
+
for (const b of p.body ?? [])
|
|
26691
|
+
lines.push(`${c2.dim(BAR2)} ${c2.dim(b)}`);
|
|
26692
|
+
const rows = displayRows2(choices);
|
|
26693
|
+
let cursorRow = 0;
|
|
26694
|
+
rows.forEach((r, ri) => {
|
|
26695
|
+
if (r.kind === "item" && r.index === cursor2)
|
|
26696
|
+
cursorRow = ri;
|
|
26697
|
+
});
|
|
26698
|
+
const { start, end } = viewport2(rows, cursorRow, WINDOW3);
|
|
26699
|
+
const above = rows.slice(0, start).filter((r) => r.kind === "item").length;
|
|
26700
|
+
const below = rows.slice(end).filter((r) => r.kind === "item").length;
|
|
26701
|
+
if (above > 0)
|
|
26702
|
+
lines.push(`${c2.dim(BAR2)} ${c2.dim(`↑ ${above} more`)}`);
|
|
26703
|
+
const budget = Math.max(6, cols - nameCol - 10);
|
|
26704
|
+
for (let ri = start;ri < end; ri++) {
|
|
26705
|
+
const row = rows[ri];
|
|
26706
|
+
if (row.kind === "header") {
|
|
26707
|
+
lines.push(`${c2.dim(BAR2)} ${c2.dim(row.text)}`);
|
|
26708
|
+
} else {
|
|
26709
|
+
lines.push(`${c2.dim(BAR2)} ${p.renderRow(row.index, row.index === cursor2, budget)}`);
|
|
26710
|
+
}
|
|
26711
|
+
}
|
|
26712
|
+
if (below > 0)
|
|
26713
|
+
lines.push(`${c2.dim(BAR2)} ${c2.dim(`↓ ${below} more`)}`);
|
|
26714
|
+
const warn = p.warnLine?.();
|
|
26715
|
+
if (warn)
|
|
26716
|
+
lines.push(`${c2.dim(BAR2)} ${warn}`);
|
|
26717
|
+
lines.push(`${c2.dim(BAR2)} ${c2.dim(p.footer)}`);
|
|
26718
|
+
return lines;
|
|
26719
|
+
};
|
|
26720
|
+
const collapse = (result) => {
|
|
26721
|
+
repaint2(stdout, region, [
|
|
26722
|
+
c2.dim(BAR2),
|
|
26723
|
+
`${c2.dim(STEP_DONE2)} ${p.message}`,
|
|
26724
|
+
`${c2.dim(BAR2)} ${c2.dim(p.summaryFor(result))}`
|
|
26725
|
+
]);
|
|
26726
|
+
};
|
|
26727
|
+
return new Promise((resolve24) => {
|
|
26728
|
+
hideCursor2(stdout);
|
|
26729
|
+
repaint2(stdout, region, build());
|
|
26730
|
+
readline3.emitKeypressEvents(stdin);
|
|
26731
|
+
const wasRaw = stdin.isRaw;
|
|
26732
|
+
stdin.setRawMode?.(true);
|
|
26733
|
+
stdin.resume();
|
|
26734
|
+
const cleanup = () => {
|
|
26735
|
+
stdin.removeListener("keypress", onKey);
|
|
26736
|
+
stdin.setRawMode?.(wasRaw ?? false);
|
|
26737
|
+
stdin.pause();
|
|
26738
|
+
showCursor2(stdout);
|
|
26739
|
+
};
|
|
26740
|
+
const finish = (result) => {
|
|
26741
|
+
cleanup();
|
|
26742
|
+
collapse(result);
|
|
26743
|
+
resolve24(result);
|
|
26744
|
+
};
|
|
26745
|
+
function onKey(_s, key) {
|
|
26746
|
+
if (!key)
|
|
26747
|
+
return;
|
|
26748
|
+
if (key.ctrl && (key.name === "c" || key.name === "d") || key.name === "escape") {
|
|
26749
|
+
finish(null);
|
|
26750
|
+
} else if (key.name === "up") {
|
|
26751
|
+
cursor2 = cursor2 > 0 ? cursor2 - 1 : choices.length - 1;
|
|
26752
|
+
repaint2(stdout, region, build());
|
|
26753
|
+
} else if (key.name === "down") {
|
|
26754
|
+
cursor2 = cursor2 < choices.length - 1 ? cursor2 + 1 : 0;
|
|
26755
|
+
repaint2(stdout, region, build());
|
|
26756
|
+
} else {
|
|
26757
|
+
const outcome = p.onKey(key, cursor2);
|
|
26758
|
+
if (outcome === "redraw")
|
|
26759
|
+
repaint2(stdout, region, build());
|
|
26760
|
+
else if (outcome)
|
|
26761
|
+
finish(outcome.done);
|
|
26762
|
+
}
|
|
26763
|
+
}
|
|
26764
|
+
stdin.on("keypress", onKey);
|
|
26765
|
+
});
|
|
26766
|
+
}
|
|
26767
|
+
function selectOne2(opts) {
|
|
26768
|
+
const stdin = opts.stdin ?? process.stdin;
|
|
26769
|
+
const stdout = opts.stdout ?? process.stdout;
|
|
26770
|
+
const choices = opts.choices;
|
|
26771
|
+
if (!choices.length)
|
|
26772
|
+
return Promise.resolve(null);
|
|
26773
|
+
if (!stdin.isTTY || !stdout.isTTY) {
|
|
26774
|
+
return Promise.resolve(choices[0].value);
|
|
26775
|
+
}
|
|
26776
|
+
const c2 = paint2(colorsEnabled2(stdout));
|
|
26777
|
+
const nameCol = nameWidth2(choices.map((ch) => ch.label));
|
|
26778
|
+
return runPrompt2({
|
|
26779
|
+
stdin,
|
|
26780
|
+
stdout,
|
|
26781
|
+
message: opts.message,
|
|
26782
|
+
c: c2,
|
|
26783
|
+
body: opts.body,
|
|
26784
|
+
choices,
|
|
26785
|
+
renderRow: (index, active, budget) => {
|
|
26786
|
+
const choice = choices[index];
|
|
26787
|
+
const dot = active ? c2.pink(RADIO_ON2) : c2.dim(RADIO_OFF2);
|
|
26788
|
+
const rawLabel = choice.label.padEnd(nameCol);
|
|
26789
|
+
const label = active ? c2.pinkBold(rawLabel) : rawLabel;
|
|
26790
|
+
const hint = choice.hint ? ` ${c2.dim(ellipsize2(choice.hint, budget))}` : "";
|
|
26791
|
+
return `${dot} ${label}${hint}`;
|
|
26792
|
+
},
|
|
26793
|
+
footer: "↑/↓ navigate · enter to select · esc to cancel",
|
|
26794
|
+
onKey: (key, cursor2) => key.name === "return" ? { done: choices[cursor2].value } : undefined,
|
|
26795
|
+
summaryFor: (value) => value === null ? "cancelled" : choices.find((ch) => ch.value === value)?.label ?? String(value)
|
|
26796
|
+
});
|
|
26797
|
+
}
|
|
26798
|
+
function multiSelect2(opts) {
|
|
26799
|
+
const stdin = opts.stdin ?? process.stdin;
|
|
26800
|
+
const stdout = opts.stdout ?? process.stdout;
|
|
26801
|
+
const choices = opts.choices;
|
|
26802
|
+
const minSelected = opts.minSelected ?? 0;
|
|
26803
|
+
const noun = opts.summaryNoun ?? "selected";
|
|
26804
|
+
const checked = choices.map((ch) => ch.locked ? ch.checked ?? true : !!ch.checked);
|
|
26805
|
+
if (!stdin.isTTY || !stdout.isTTY) {
|
|
26806
|
+
return Promise.resolve(choices.filter((_, i) => checked[i]).map((ch) => ch.value));
|
|
26807
|
+
}
|
|
26808
|
+
const c2 = paint2(colorsEnabled2(stdout));
|
|
26809
|
+
const nameCol = nameWidth2(choices.map((ch) => ch.label));
|
|
26810
|
+
let warn = false;
|
|
26811
|
+
return runPrompt2({
|
|
26812
|
+
stdin,
|
|
26813
|
+
stdout,
|
|
26814
|
+
message: opts.message,
|
|
26815
|
+
c: c2,
|
|
26816
|
+
choices,
|
|
26817
|
+
renderRow: (index, active, budget) => {
|
|
26818
|
+
const choice = choices[index];
|
|
26819
|
+
const caret = active ? c2.pink(CARET2) : " ";
|
|
26820
|
+
const box = choice.locked ? checked[index] ? c2.guide(CHECK_ON2) : c2.dim(CHECK_OFF2) : checked[index] ? c2.pink(CHECK_ON2) : c2.dim(CHECK_OFF2);
|
|
26821
|
+
const rawLabel = choice.label.padEnd(nameCol);
|
|
26822
|
+
const label = active ? c2.pinkBold(rawLabel) : checked[index] ? rawLabel : c2.dim(rawLabel);
|
|
26823
|
+
const hint = choice.hint ? ` ${c2.dim(ellipsize2(choice.hint, budget))}` : "";
|
|
26824
|
+
return `${caret} ${box} ${label}${hint}`;
|
|
26825
|
+
},
|
|
26826
|
+
warnLine: () => warn ? c2.warn(`Select at least ${minSelected}.`) : null,
|
|
26827
|
+
footer: opts.hint ?? "↑/↓ move · space select · ctrl+a all · enter confirm",
|
|
26828
|
+
onKey: (key, cursor2) => {
|
|
26829
|
+
if (key.name === "space") {
|
|
26830
|
+
if (choices[cursor2]?.locked)
|
|
26831
|
+
return "redraw";
|
|
26832
|
+
checked[cursor2] = !checked[cursor2];
|
|
26833
|
+
warn = false;
|
|
26834
|
+
return "redraw";
|
|
26835
|
+
}
|
|
26836
|
+
if (key.ctrl && key.name === "a") {
|
|
26837
|
+
const allOn = choices.every((ch, i) => checked[i] || ch.locked);
|
|
26838
|
+
for (let i = 0;i < checked.length; i++) {
|
|
26839
|
+
checked[i] = choices[i]?.locked ? true : !allOn;
|
|
26840
|
+
}
|
|
26841
|
+
return "redraw";
|
|
26842
|
+
}
|
|
26843
|
+
if (key.name === "return") {
|
|
26844
|
+
const selected = choices.filter((_, i) => checked[i]).map((ch) => ch.value);
|
|
26845
|
+
const chosen = choices.filter((ch, i) => checked[i] && !ch.locked).length;
|
|
26846
|
+
if (chosen < minSelected) {
|
|
26847
|
+
warn = true;
|
|
26848
|
+
return "redraw";
|
|
26849
|
+
}
|
|
26850
|
+
return { done: selected };
|
|
26851
|
+
}
|
|
26852
|
+
return;
|
|
26853
|
+
},
|
|
26854
|
+
summaryFor: (values) => values === null ? "cancelled" : summarize2(choices.filter((ch) => values.includes(ch.value) && !ch.summaryExclude).map((ch) => ch.label), noun)
|
|
26855
|
+
});
|
|
26856
|
+
}
|
|
26857
|
+
function promptText2(opts) {
|
|
26858
|
+
const stdin = opts.stdin ?? process.stdin;
|
|
26859
|
+
const stdout = opts.stdout ?? process.stdout;
|
|
26860
|
+
const c2 = paint2(colorsEnabled2(stdout));
|
|
26861
|
+
const accept = (raw) => {
|
|
26862
|
+
const value = raw.trim() || opts.defaultValue || "";
|
|
26863
|
+
const error = opts.validate?.(value) ?? null;
|
|
26864
|
+
return error ? { ok: false, error } : { ok: true, value };
|
|
26865
|
+
};
|
|
26866
|
+
if (!stdin.isTTY) {
|
|
26867
|
+
return new Promise((resolve24) => {
|
|
26868
|
+
let buf = "";
|
|
26869
|
+
const onData = (chunk) => {
|
|
26870
|
+
buf += String(chunk);
|
|
26871
|
+
if (buf.includes(`
|
|
26872
|
+
`))
|
|
26873
|
+
done();
|
|
26874
|
+
};
|
|
26875
|
+
const done = () => {
|
|
26876
|
+
stdin.removeListener("data", onData);
|
|
26877
|
+
stdin.removeListener("end", done);
|
|
26878
|
+
const r = accept(buf.split(`
|
|
26879
|
+
`)[0] ?? "");
|
|
26880
|
+
resolve24(r.ok ? r.value : null);
|
|
26881
|
+
};
|
|
26882
|
+
stdin.on("data", onData);
|
|
26883
|
+
stdin.on("end", done);
|
|
26884
|
+
});
|
|
26885
|
+
}
|
|
26886
|
+
return new Promise((resolve24) => {
|
|
26887
|
+
let value = "";
|
|
26888
|
+
const draw = (error) => {
|
|
26889
|
+
const shown = opts.mask ? "•".repeat(value.length) : value;
|
|
26890
|
+
const hint = opts.hint ? ` ${c2.dim(opts.hint)}` : "";
|
|
26891
|
+
const err = error ? `
|
|
26892
|
+
${c2.warn(error)}` : "";
|
|
26893
|
+
stdout.write(`\r\x1B[2K${c2.bold(opts.message)} ${shown}${hint}${err}`);
|
|
26894
|
+
if (err)
|
|
26895
|
+
stdout.write("\x1B[1A");
|
|
26896
|
+
};
|
|
26897
|
+
draw();
|
|
26898
|
+
readline3.emitKeypressEvents(stdin);
|
|
26899
|
+
const wasRaw = stdin.isRaw;
|
|
26900
|
+
stdin.setRawMode?.(true);
|
|
26901
|
+
stdin.resume();
|
|
26902
|
+
const cleanup = () => {
|
|
26903
|
+
stdin.removeListener("keypress", onKey);
|
|
26904
|
+
stdin.setRawMode?.(wasRaw ?? false);
|
|
26905
|
+
stdin.pause();
|
|
26906
|
+
stdout.write(`
|
|
26907
|
+
`);
|
|
26908
|
+
};
|
|
26909
|
+
function onKey(str2, key) {
|
|
26910
|
+
if (!key)
|
|
26911
|
+
return;
|
|
26912
|
+
if (key.ctrl && (key.name === "c" || key.name === "d") || key.name === "escape") {
|
|
26913
|
+
cleanup();
|
|
26914
|
+
resolve24(null);
|
|
26915
|
+
return;
|
|
26916
|
+
}
|
|
26917
|
+
if (key.name === "return" || key.name === "enter") {
|
|
26918
|
+
const r = accept(value);
|
|
26919
|
+
if (r.ok) {
|
|
26920
|
+
cleanup();
|
|
26921
|
+
resolve24(r.value);
|
|
26922
|
+
} else {
|
|
26923
|
+
draw(r.error);
|
|
26924
|
+
}
|
|
26925
|
+
return;
|
|
26926
|
+
}
|
|
26927
|
+
if (key.name === "backspace") {
|
|
26928
|
+
value = value.slice(0, -1);
|
|
26929
|
+
draw();
|
|
26930
|
+
return;
|
|
26931
|
+
}
|
|
26932
|
+
if (str2 && !key.ctrl && !key.meta) {
|
|
26933
|
+
value += str2;
|
|
26934
|
+
draw();
|
|
26935
|
+
}
|
|
26936
|
+
}
|
|
26937
|
+
stdin.on("keypress", onKey);
|
|
26938
|
+
});
|
|
26939
|
+
}
|
|
26940
|
+
var ESC2 = "\x1B", BAR2 = "│", BAR_END2 = "└", STEP_ACTIVE2 = "◆", STEP_DONE2 = "◇", RADIO_ON2 = "●", RADIO_OFF2 = "○", CHECK_ON2 = "◼", CHECK_OFF2 = "◻", CARET2 = "❯", FLOWER2 = "❋", HUES2, ANSI_RESET2, ANSI_BOLD2, ANSI_DIM2, LOGO_MIN_COLS2 = 22, TAGLINE2 = "end-to-end failure layer for ai agents", LOGO_GRID2, LOGO_TEAL2, LOGO_PINK2, WINDOW3 = 8;
|
|
26941
|
+
var init_tui2 = __esm(() => {
|
|
26942
|
+
HUES2 = {
|
|
26943
|
+
guide: { rgb: [102, 209, 181], basic: "36" },
|
|
26944
|
+
pink: { rgb: [255, 46, 136], basic: "95" },
|
|
26945
|
+
logoPink: { rgb: [228, 88, 124], basic: "95" },
|
|
26946
|
+
warn: { rgb: [227, 179, 65], basic: "33" },
|
|
26947
|
+
dim: { rgb: [107, 118, 132], basic: "2" }
|
|
26948
|
+
};
|
|
26949
|
+
ANSI_RESET2 = `${ESC2}[0m`;
|
|
26950
|
+
ANSI_BOLD2 = `${ESC2}[1m`;
|
|
26951
|
+
ANSI_DIM2 = `${ESC2}[2m`;
|
|
26952
|
+
LOGO_GRID2 = [
|
|
26953
|
+
"...tt........",
|
|
26954
|
+
"..tttt.......",
|
|
26955
|
+
".tttttt..pppp",
|
|
26956
|
+
".tttttt..pppp",
|
|
26957
|
+
"..tttt...pppp",
|
|
26958
|
+
"...tt....pppp",
|
|
26959
|
+
".........pppp",
|
|
26960
|
+
".........pppp",
|
|
26961
|
+
"..pppp...pppp",
|
|
26962
|
+
"..pppp...pppp",
|
|
26963
|
+
"..pppp...pppp",
|
|
26964
|
+
".pppppp..pppp",
|
|
26965
|
+
".pppppp..pppp",
|
|
26966
|
+
".pppppp..pppp",
|
|
26967
|
+
".pppppp..pppp",
|
|
26968
|
+
"..pppp...pppp",
|
|
26969
|
+
"..ppppppppppp",
|
|
26970
|
+
"..ppppppppppp",
|
|
26971
|
+
"..ppppppppppp",
|
|
26972
|
+
"..ppppppppppp"
|
|
26973
|
+
];
|
|
26974
|
+
LOGO_TEAL2 = HUES2.guide.rgb;
|
|
26975
|
+
LOGO_PINK2 = HUES2.logoPink.rgb;
|
|
26976
|
+
});
|
|
26977
|
+
|
|
26978
|
+
// src/hooks/manager.ts
|
|
26979
|
+
var exports_manager = {};
|
|
26980
|
+
__export(exports_manager, {
|
|
26981
|
+
removeHooks: () => removeHooks2,
|
|
26982
|
+
listHooks: () => listHooks,
|
|
26983
|
+
installHooks: () => installHooks2,
|
|
26984
|
+
hooksInstalledInSettings: () => hooksInstalledInSettings2,
|
|
26985
|
+
getSettingsPath: () => getSettingsPath2
|
|
26986
|
+
});
|
|
26987
|
+
import { execSync as execSync7 } from "node:child_process";
|
|
26988
|
+
import { existsSync as existsSync32 } from "node:fs";
|
|
26989
|
+
import { resolve as resolve24, basename as basename6 } from "node:path";
|
|
26990
|
+
import { platform as platform5, arch as arch4, release as release3, hostname as hostname5 } from "node:os";
|
|
26991
|
+
function getSettingsPath2(scope, cwd) {
|
|
26992
|
+
return claudeCode.getSettingsPath(scope, cwd);
|
|
26993
|
+
}
|
|
26994
|
+
function scopeLabel2(scope) {
|
|
26995
|
+
switch (scope) {
|
|
26996
|
+
case "user":
|
|
26997
|
+
return `~/.claude/settings.json`;
|
|
26998
|
+
case "project":
|
|
26999
|
+
return `{cwd}/.claude/settings.json`;
|
|
27000
|
+
case "local":
|
|
27001
|
+
return `{cwd}/.claude/settings.local.json`;
|
|
27002
|
+
}
|
|
27003
|
+
}
|
|
27004
|
+
function resolveFailproofaiBinary2() {
|
|
27005
|
+
const override = process.env.FAILPROOFAI_BINARY_OVERRIDE;
|
|
27006
|
+
if (override && override.trim())
|
|
27007
|
+
return override.trim();
|
|
27008
|
+
try {
|
|
27009
|
+
const cmd = process.platform === "win32" ? "where failproofai" : "which failproofai";
|
|
27010
|
+
const result = execSync7(cmd, { encoding: "utf8" }).trim();
|
|
27011
|
+
return result.split(`
|
|
27012
|
+
`)[0].trim();
|
|
27013
|
+
} catch {
|
|
27014
|
+
throw new CliError(`failproofai binary not found in PATH.
|
|
27015
|
+
` + "Install it globally first: npm install -g failproofai");
|
|
27016
|
+
}
|
|
27017
|
+
}
|
|
27018
|
+
function validatePolicyNames2(names) {
|
|
27019
|
+
const invalid = names.filter((n) => !VALID_POLICY_NAMES2.has(n));
|
|
27020
|
+
if (invalid.length > 0) {
|
|
27021
|
+
const validList = [...VALID_POLICY_NAMES2].join(", ");
|
|
27022
|
+
throw new CliError(`Unknown policy name(s): ${invalid.join(", ")}
|
|
27023
|
+
` + `Valid policies: ${validList}`);
|
|
27024
|
+
}
|
|
27025
|
+
}
|
|
27026
|
+
function deduplicateScopes2(scopes, cwd) {
|
|
26050
27027
|
const seen = new Set;
|
|
26051
27028
|
return scopes.filter((s) => {
|
|
26052
27029
|
const p = getSettingsPath2(s, cwd);
|
|
@@ -26119,7 +27096,7 @@ async function installHooksImpl2(policyNames, scope = "user", cwd, includeBeta =
|
|
|
26119
27096
|
} else if (customPoliciesPath && (typeof customPoliciesPath === "string" || customPoliciesPath.length > 0)) {
|
|
26120
27097
|
const incoming = (typeof customPoliciesPath === "string" ? [customPoliciesPath] : customPoliciesPath).map((path3) => resolve24(path3));
|
|
26121
27098
|
const carried = (previousConfig.customPoliciesPaths ?? (previousConfig.customPoliciesPath ? [previousConfig.customPoliciesPath] : [])).map((path3) => resolve24(path3)).filter((path3) => {
|
|
26122
|
-
if (
|
|
27099
|
+
if (existsSync32(path3))
|
|
26123
27100
|
return true;
|
|
26124
27101
|
console.log(`Dropping custom policies path (file no longer exists): ${path3}`);
|
|
26125
27102
|
return false;
|
|
@@ -26255,7 +27232,7 @@ This file can be committed to git — no machine-specific paths.`);
|
|
|
26255
27232
|
} catch {}
|
|
26256
27233
|
}
|
|
26257
27234
|
}
|
|
26258
|
-
async function
|
|
27235
|
+
async function removeHooks2(policyNames, scope = "user", cwd, opts) {
|
|
26259
27236
|
const configScope = scope === "all" ? "user" : scope;
|
|
26260
27237
|
const selectedClis = opts?.cli && opts.cli.length > 0 ? [...new Set(opts.cli)] : ["claude"];
|
|
26261
27238
|
if (opts?.removeCustomHooks) {
|
|
@@ -26311,7 +27288,7 @@ async function removeHooks(policyNames, scope = "user", cwd, opts) {
|
|
|
26311
27288
|
const scopesToRemove = scope === "all" ? [...integration.scopes] : integration.scopes.includes(scope) ? [scope] : [];
|
|
26312
27289
|
for (const s of scopesToRemove) {
|
|
26313
27290
|
const settingsPaths = settingsPathsFor(integration, s, cwd);
|
|
26314
|
-
const existing = settingsPaths.filter((p) =>
|
|
27291
|
+
const existing = settingsPaths.filter((p) => existsSync32(p));
|
|
26315
27292
|
if (existing.length === 0) {
|
|
26316
27293
|
if (scope !== "all" && selectedClis.length === 1) {
|
|
26317
27294
|
console.log("No settings file found. Nothing to remove.");
|
|
@@ -26512,7 +27489,7 @@ Failproof AI Hook Policies
|
|
|
26512
27489
|
for (const path3 of explicitPaths) {
|
|
26513
27490
|
const absPath = resolve24(findProjectConfigDir(cwd ?? process.cwd()), path3);
|
|
26514
27491
|
console.log(` ${absPath}`);
|
|
26515
|
-
if (!
|
|
27492
|
+
if (!existsSync32(absPath)) {
|
|
26516
27493
|
console.log(` \x1B[31m✗ File not found: ${absPath}\x1B[0m`);
|
|
26517
27494
|
continue;
|
|
26518
27495
|
}
|
|
@@ -26609,7 +27586,7 @@ __export(exports_install_prompt, {
|
|
|
26609
27586
|
promptPolicySelection: () => promptPolicySelection2,
|
|
26610
27587
|
buildCliMenuOptions: () => buildCliMenuOptions
|
|
26611
27588
|
});
|
|
26612
|
-
import * as
|
|
27589
|
+
import * as readline4 from "node:readline";
|
|
26613
27590
|
async function resolveTargetClis(explicit, action = "install") {
|
|
26614
27591
|
const detected = explicit && explicit.length > 0 ? [] : detectInstalledClis();
|
|
26615
27592
|
const stdinIsTty = !!process.stdin.isTTY;
|
|
@@ -26716,13 +27693,13 @@ async function promptCliTargetSelection(detected, action = "install") {
|
|
|
26716
27693
|
let cursor2 = 0;
|
|
26717
27694
|
let lastLineCount = 0;
|
|
26718
27695
|
let cursorHidden = false;
|
|
26719
|
-
function
|
|
27696
|
+
function hideCursor3() {
|
|
26720
27697
|
if (!cursorHidden) {
|
|
26721
27698
|
process.stdout.write("\x1B[?25l");
|
|
26722
27699
|
cursorHidden = true;
|
|
26723
27700
|
}
|
|
26724
27701
|
}
|
|
26725
|
-
function
|
|
27702
|
+
function showCursor3() {
|
|
26726
27703
|
if (cursorHidden) {
|
|
26727
27704
|
process.stdout.write("\x1B[?25h");
|
|
26728
27705
|
cursorHidden = false;
|
|
@@ -26732,7 +27709,7 @@ async function promptCliTargetSelection(detected, action = "install") {
|
|
|
26732
27709
|
const heading = action === "uninstall" ? "Remove failproofai hooks" : "Install failproofai hooks";
|
|
26733
27710
|
function render() {
|
|
26734
27711
|
const cols = process.stdout.columns || 100;
|
|
26735
|
-
|
|
27712
|
+
hideCursor3();
|
|
26736
27713
|
const g = dim(BAR);
|
|
26737
27714
|
const lines = [g, `${teal(STEP_ACTIVE)} ${bold(heading)}`];
|
|
26738
27715
|
for (const row of buildDisplayRows()) {
|
|
@@ -26765,13 +27742,13 @@ async function promptCliTargetSelection(detected, action = "install") {
|
|
|
26765
27742
|
const itemCount = options.length;
|
|
26766
27743
|
return new Promise((resolve25) => {
|
|
26767
27744
|
render();
|
|
26768
|
-
|
|
27745
|
+
readline4.emitKeypressEvents(process.stdin);
|
|
26769
27746
|
const wasRaw = process.stdin.isRaw;
|
|
26770
27747
|
if (process.stdin.setRawMode)
|
|
26771
27748
|
process.stdin.setRawMode(true);
|
|
26772
27749
|
process.stdin.resume();
|
|
26773
27750
|
function cleanup() {
|
|
26774
|
-
|
|
27751
|
+
showCursor3();
|
|
26775
27752
|
process.stdin.removeListener("keypress", onKey);
|
|
26776
27753
|
if (process.stdin.setRawMode)
|
|
26777
27754
|
process.stdin.setRawMode(wasRaw ?? false);
|
|
@@ -26832,13 +27809,13 @@ async function promptPolicySelection2(preSelected, options = {}) {
|
|
|
26832
27809
|
let search = "";
|
|
26833
27810
|
let lastLineCount = 0;
|
|
26834
27811
|
let cursorHidden = false;
|
|
26835
|
-
function
|
|
27812
|
+
function hideCursor3() {
|
|
26836
27813
|
if (!cursorHidden) {
|
|
26837
27814
|
process.stdout.write("\x1B[?25l");
|
|
26838
27815
|
cursorHidden = true;
|
|
26839
27816
|
}
|
|
26840
27817
|
}
|
|
26841
|
-
function
|
|
27818
|
+
function showCursor3() {
|
|
26842
27819
|
if (cursorHidden) {
|
|
26843
27820
|
process.stdout.write("\x1B[?25h");
|
|
26844
27821
|
cursorHidden = false;
|
|
@@ -26885,7 +27862,7 @@ async function promptPolicySelection2(preSelected, options = {}) {
|
|
|
26885
27862
|
}
|
|
26886
27863
|
function render() {
|
|
26887
27864
|
const cols = process.stdout.columns || 100;
|
|
26888
|
-
|
|
27865
|
+
hideCursor3();
|
|
26889
27866
|
const filtered = getFiltered();
|
|
26890
27867
|
const shown = filtered.length;
|
|
26891
27868
|
if (shown > 0 && cursor2 >= shown)
|
|
@@ -26904,10 +27881,10 @@ async function promptPolicySelection2(preSelected, options = {}) {
|
|
|
26904
27881
|
for (let i = 0;i < WINDOW_SIZE + 1; i++)
|
|
26905
27882
|
lines.push(g);
|
|
26906
27883
|
} else {
|
|
26907
|
-
const
|
|
27884
|
+
const displayRows3 = buildDisplayRows(filtered);
|
|
26908
27885
|
let cursorDisplayRow = 0;
|
|
26909
|
-
for (let i = 0;i <
|
|
26910
|
-
const row =
|
|
27886
|
+
for (let i = 0;i < displayRows3.length; i++) {
|
|
27887
|
+
const row = displayRows3[i];
|
|
26911
27888
|
if (row.kind === "item" && row.filteredIndex === cursor2) {
|
|
26912
27889
|
cursorDisplayRow = i;
|
|
26913
27890
|
break;
|
|
@@ -26915,12 +27892,12 @@ async function promptPolicySelection2(preSelected, options = {}) {
|
|
|
26915
27892
|
}
|
|
26916
27893
|
let windowStart = cursorDisplayRow - Math.floor(WINDOW_SIZE / 2);
|
|
26917
27894
|
windowStart = Math.max(0, windowStart);
|
|
26918
|
-
windowStart = Math.min(windowStart, Math.max(0,
|
|
26919
|
-
const windowEnd = Math.min(
|
|
26920
|
-
const aboveItems =
|
|
27895
|
+
windowStart = Math.min(windowStart, Math.max(0, displayRows3.length - WINDOW_SIZE));
|
|
27896
|
+
const windowEnd = Math.min(displayRows3.length, windowStart + WINDOW_SIZE);
|
|
27897
|
+
const aboveItems = displayRows3.slice(0, windowStart).filter((r) => r.kind === "item").length;
|
|
26921
27898
|
lines.push(aboveItems > 0 ? `${g} ${dim(`↑ ${aboveItems} more`)}` : g);
|
|
26922
27899
|
for (let i = windowStart;i < windowEnd; i++) {
|
|
26923
|
-
const row =
|
|
27900
|
+
const row = displayRows3[i];
|
|
26924
27901
|
if (row.kind === "header") {
|
|
26925
27902
|
lines.push(`${g} ${dim(row.category.toUpperCase())} ${dim(`${row.enabledCount}/${row.totalCount}`)}`);
|
|
26926
27903
|
} else {
|
|
@@ -26937,7 +27914,7 @@ async function promptPolicySelection2(preSelected, options = {}) {
|
|
|
26937
27914
|
}
|
|
26938
27915
|
for (let i = windowEnd - windowStart;i < WINDOW_SIZE; i++)
|
|
26939
27916
|
lines.push(g);
|
|
26940
|
-
const belowItems =
|
|
27917
|
+
const belowItems = displayRows3.slice(windowEnd).filter((r) => r.kind === "item").length;
|
|
26941
27918
|
lines.push(belowItems > 0 ? `${g} ${dim(`↓ ${belowItems} more`)}` : g);
|
|
26942
27919
|
}
|
|
26943
27920
|
lines.push(g);
|
|
@@ -26953,7 +27930,7 @@ async function promptPolicySelection2(preSelected, options = {}) {
|
|
|
26953
27930
|
render();
|
|
26954
27931
|
process.stdin.setRawMode(true);
|
|
26955
27932
|
process.stdin.resume();
|
|
26956
|
-
|
|
27933
|
+
readline4.emitKeypressEvents(process.stdin);
|
|
26957
27934
|
function keypressHandler(_str, key) {
|
|
26958
27935
|
if (!key)
|
|
26959
27936
|
return;
|
|
@@ -27009,7 +27986,7 @@ ${dim(BAR)} ${dim(`${selected.length} selected`)}
|
|
|
27009
27986
|
}
|
|
27010
27987
|
}
|
|
27011
27988
|
function cleanup() {
|
|
27012
|
-
|
|
27989
|
+
showCursor3();
|
|
27013
27990
|
process.stdin.removeListener("keypress", keypressHandler);
|
|
27014
27991
|
process.stdin.setRawMode(false);
|
|
27015
27992
|
process.stdin.pause();
|
|
@@ -27647,7 +28624,7 @@ __export(exports_launch2, {
|
|
|
27647
28624
|
launch: () => launch2
|
|
27648
28625
|
});
|
|
27649
28626
|
import { spawn as spawn3 } from "child_process";
|
|
27650
|
-
import { realpathSync as realpathSync5, existsSync as
|
|
28627
|
+
import { realpathSync as realpathSync5, existsSync as existsSync33 } from "node:fs";
|
|
27651
28628
|
import { resolve as resolve25, dirname as dirname16 } from "node:path";
|
|
27652
28629
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
27653
28630
|
import { createInterface as createInterface2 } from "node:readline";
|
|
@@ -27674,7 +28651,7 @@ function launch2(mode) {
|
|
|
27674
28651
|
cmd = "node";
|
|
27675
28652
|
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve25(dirname16(realpathSync5(fileURLToPath3(import.meta.url))), "..");
|
|
27676
28653
|
const serverJsPath = resolve25(packageRoot, ".next/standalone/server.js");
|
|
27677
|
-
if (!
|
|
28654
|
+
if (!existsSync33(serverJsPath)) {
|
|
27678
28655
|
let shadowMessage = null;
|
|
27679
28656
|
try {
|
|
27680
28657
|
const diag = diagnoseShadow({ selfPackageRoot: packageRoot, selfVersion: version2 });
|
|
@@ -27774,7 +28751,7 @@ import { realpathSync as realpathSync6 } from "fs";
|
|
|
27774
28751
|
import { dirname as dirname17, resolve as resolve26 } from "path";
|
|
27775
28752
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
27776
28753
|
// package.json
|
|
27777
|
-
var version = "1.0.0-beta.
|
|
28754
|
+
var version = "1.0.0-beta.8";
|
|
27778
28755
|
|
|
27779
28756
|
// bin/failproofai.mjs
|
|
27780
28757
|
if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
|
|
@@ -27872,7 +28849,7 @@ if (hookIdx >= 0) {
|
|
|
27872
28849
|
}
|
|
27873
28850
|
}
|
|
27874
28851
|
async function runCli() {
|
|
27875
|
-
const SUBCOMMANDS = ["policies", "policy", "audit", "config"];
|
|
28852
|
+
const SUBCOMMANDS = ["policies", "policy", "audit", "config", "uninstall"];
|
|
27876
28853
|
if ((args.includes("--help") || args.includes("-h")) && !SUBCOMMANDS.includes(args[0])) {
|
|
27877
28854
|
const extraArgs = args.filter((a) => a !== "--help" && a !== "-h");
|
|
27878
28855
|
if (extraArgs.length > 0) {
|
|
@@ -27918,6 +28895,16 @@ COMMANDS
|
|
|
27918
28895
|
dashboard at http://localhost:8020/audit
|
|
27919
28896
|
audit --help, -h Show this help for the audit command
|
|
27920
28897
|
|
|
28898
|
+
uninstall Remove failproofai from this machine: hook
|
|
28899
|
+
entries from every agent CLI, and the daemon
|
|
28900
|
+
service. Run this BEFORE \`npm rm -g failproofai\`
|
|
28901
|
+
\u2014 npm runs no uninstall script, so removing the
|
|
28902
|
+
package alone leaves both behind.
|
|
28903
|
+
--purge Also delete ~/.failproofai (settings,
|
|
28904
|
+
credentials, audit history, daemon binary)
|
|
28905
|
+
--dry-run Show what would be removed, change nothing
|
|
28906
|
+
--yes, -y Skip the confirmation prompt
|
|
28907
|
+
|
|
27921
28908
|
--version, -v Print version and exit
|
|
27922
28909
|
--help, -h Show this help message
|
|
27923
28910
|
|
|
@@ -27995,6 +28982,84 @@ Run \`failproofai --help\` for usage.`);
|
|
|
27995
28982
|
await maybeFirstRunConfigure2({}, { postSetupAudit: args[0] !== "audit", force: layoutWasReset });
|
|
27996
28983
|
} catch {}
|
|
27997
28984
|
}
|
|
28985
|
+
if (args[0] === "uninstall") {
|
|
28986
|
+
const subArgs = args.slice(1);
|
|
28987
|
+
if (subArgs.includes("--help") || subArgs.includes("-h")) {
|
|
28988
|
+
console.log(`
|
|
28989
|
+
failproofai uninstall \u2014 remove failproofai from this machine
|
|
28990
|
+
|
|
28991
|
+
USAGE
|
|
28992
|
+
failproofai uninstall [--purge] [--dry-run] [--yes]
|
|
28993
|
+
|
|
28994
|
+
WHAT IT REMOVES
|
|
28995
|
+
\u2022 failproofai hook entries from every agent CLI that has them
|
|
28996
|
+
\u2022 the failproofaid daemon service (needs sudo)
|
|
28997
|
+
\u2022 the "require the daemon" flag \u2014 cleared FIRST, so a partial uninstall can
|
|
28998
|
+
never leave this machine denying every tool call
|
|
28999
|
+
|
|
29000
|
+
OPTIONS
|
|
29001
|
+
--purge Also delete ~/.failproofai \u2014 settings, credentials, audit
|
|
29002
|
+
history and the downloaded daemon binary. Off by default so a
|
|
29003
|
+
reinstall keeps your history.
|
|
29004
|
+
--dry-run Print what would be removed and change nothing.
|
|
29005
|
+
--yes, -y Skip the confirmation prompt. Required when there is no TTY.
|
|
29006
|
+
|
|
29007
|
+
WHY THIS EXISTS
|
|
29008
|
+
npm runs no uninstall script, so \`npm rm -g failproofai\` removes the package
|
|
29009
|
+
and leaves the hook entries and the service behind. Run this first, then:
|
|
29010
|
+
npm rm -g failproofai
|
|
29011
|
+
`);
|
|
29012
|
+
process.exit(0);
|
|
29013
|
+
}
|
|
29014
|
+
const KNOWN = new Set(["--purge", "--dry-run", "--yes", "-y"]);
|
|
29015
|
+
const unknown = subArgs.find((a) => !KNOWN.has(a));
|
|
29016
|
+
if (unknown) {
|
|
29017
|
+
throw new CliError3(`Unexpected argument: ${unknown}
|
|
29018
|
+
Run \`failproofai uninstall --help\` for usage.`);
|
|
29019
|
+
}
|
|
29020
|
+
lastSubcommand = "uninstall_command";
|
|
29021
|
+
const { runUninstallCommand: runUninstallCommand2 } = await Promise.resolve().then(() => (init_uninstall_cli(), exports_uninstall_cli));
|
|
29022
|
+
const purge = subArgs.includes("--purge");
|
|
29023
|
+
const dryRun = subArgs.includes("--dry-run");
|
|
29024
|
+
const yes = subArgs.includes("--yes") || subArgs.includes("-y");
|
|
29025
|
+
let planWasShown = false;
|
|
29026
|
+
const result = await runUninstallCommand2({
|
|
29027
|
+
purge,
|
|
29028
|
+
dryRun,
|
|
29029
|
+
yes,
|
|
29030
|
+
cwd: process.cwd(),
|
|
29031
|
+
confirm: process.stdin.isTTY ? async (planLines) => {
|
|
29032
|
+
planWasShown = true;
|
|
29033
|
+
const { selectOne: selectOne3 } = await Promise.resolve().then(() => (init_tui2(), exports_tui));
|
|
29034
|
+
const answer = await selectOne3({
|
|
29035
|
+
message: purge ? "Remove failproofai and DELETE ~/.failproofai?" : "Remove failproofai from this machine?",
|
|
29036
|
+
body: planLines,
|
|
29037
|
+
choices: [
|
|
29038
|
+
{ label: "No, cancel", value: false },
|
|
29039
|
+
{ label: purge ? "Yes, remove and purge" : "Yes, remove it", value: true }
|
|
29040
|
+
]
|
|
29041
|
+
});
|
|
29042
|
+
return answer === true;
|
|
29043
|
+
} : undefined
|
|
29044
|
+
});
|
|
29045
|
+
const skip = planWasShown ? result.planLines : 0;
|
|
29046
|
+
for (const line of result.lines.slice(skip)) {
|
|
29047
|
+
if (result.exitCode === 0)
|
|
29048
|
+
console.log(line);
|
|
29049
|
+
else
|
|
29050
|
+
console.error(line);
|
|
29051
|
+
}
|
|
29052
|
+
if (!result.purged) {
|
|
29053
|
+
await track("cli_uninstall_command", {
|
|
29054
|
+
ok: result.exitCode === 0,
|
|
29055
|
+
purge,
|
|
29056
|
+
dry_run: dryRun
|
|
29057
|
+
});
|
|
29058
|
+
}
|
|
29059
|
+
lastSubcommand = null;
|
|
29060
|
+
await exitAfterFlush(result.exitCode);
|
|
29061
|
+
return;
|
|
29062
|
+
}
|
|
27998
29063
|
if (args[0] === "policies") {
|
|
27999
29064
|
const subArgs = args.slice(1);
|
|
28000
29065
|
const isInstall = subArgs.includes("--install") || subArgs.includes("-i");
|
|
@@ -28124,7 +29189,7 @@ Run \`failproofai policies --help\` for usage.`);
|
|
|
28124
29189
|
}
|
|
28125
29190
|
if (isUninstall) {
|
|
28126
29191
|
lastSubcommand = "uninstall";
|
|
28127
|
-
const { removeHooks:
|
|
29192
|
+
const { removeHooks: removeHooks3 } = await Promise.resolve().then(() => (init_manager2(), exports_manager));
|
|
28128
29193
|
const { resolveTargetClis: resolveTargetClis2 } = await Promise.resolve().then(() => (init_install_prompt2(), exports_install_prompt));
|
|
28129
29194
|
const scopeIdx = subArgs.indexOf("--scope");
|
|
28130
29195
|
const scope = scopeIdx >= 0 ? subArgs[scopeIdx + 1] : "user";
|
|
@@ -28169,7 +29234,7 @@ Run \`failproofai policies --help\` for usage.`);
|
|
|
28169
29234
|
}
|
|
28170
29235
|
const policyNames = subArgs.filter((a, idx) => !a.startsWith("-") && !consumedIdxs.has(idx));
|
|
28171
29236
|
const cli = await resolveTargetClis2(cliFlagValues.length > 0 ? cliFlagValues : undefined, "uninstall");
|
|
28172
|
-
await
|
|
29237
|
+
await removeHooks3(policyNames.length > 0 ? policyNames : undefined, scope, undefined, { betaOnly, removeCustomHooks, cli });
|
|
28173
29238
|
await track("cli_uninstall_success", {
|
|
28174
29239
|
scope,
|
|
28175
29240
|
cli,
|
|
@@ -28301,8 +29366,8 @@ For multiple policies use \`failproofai policies --${action === "add" ? "install
|
|
|
28301
29366
|
include_beta: includeBeta
|
|
28302
29367
|
});
|
|
28303
29368
|
} else {
|
|
28304
|
-
const { removeHooks:
|
|
28305
|
-
await
|
|
29369
|
+
const { removeHooks: removeHooks3 } = await Promise.resolve().then(() => (init_manager2(), exports_manager));
|
|
29370
|
+
await removeHooks3([policyName], scope, undefined, { betaOnly: false, removeCustomHooks: false, cli });
|
|
28306
29371
|
await track("cli_policy_remove_success", {
|
|
28307
29372
|
scope,
|
|
28308
29373
|
cli,
|