failproofai 1.0.0-beta.7 → 1.0.0-beta.9
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]__0c_qcgp._.js → [root-of-the-server]__0abann8._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0f9901b._.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]__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/_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/{_194yh9u._.js → _1t3vvw3._.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/{2p2kz4aj1tooj.js → 0jibhdbjojuq5.js} +1 -1
- package/.next/standalone/.next/static/chunks/{36pc3_8fpb6ic.js → 0qubxfmqjdk8-.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0m-m7wgtxdoav.js → 159mxiauw9a8_.js} +1 -1
- package/.next/standalone/.next/static/chunks/{11l6oh9cyb35a.js → 1xxzquhtvg72x.js} +1 -1
- package/.next/standalone/.next/static/chunks/270jpx60b2kuj.js +1 -0
- package/.next/standalone/.next/static/chunks/{2aej0vi1fm87m.js → 2f295j8d9ta-h.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2bxld6-rym3ah.js → 2g7p545cy5458.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0gr76ps8_zm26.js → 2nsm1u-n43hfa.js} +1 -1
- package/.next/standalone/.next/static/chunks/3940dugfmx1ai.js +6 -0
- package/.next/standalone/.next/static/chunks/{3t1wr_kx6brwc.js → 3dpc4c0l_hf32.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 +1199 -98
- 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/collector-config.ts +18 -2
- package/src/hooks/configure-wizard.ts +65 -34
- 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-home.ts +23 -3
- package/src/hooks/fp-reset.ts +137 -5
- 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 → biwmgAwO6vVswC0z-soDU}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{LUaLkKg40nSkHrIajexIk → biwmgAwO6vVswC0z-soDU}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{LUaLkKg40nSkHrIajexIk → biwmgAwO6vVswC0z-soDU}/_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.9";
|
|
23
23
|
var init_package = () => {};
|
|
24
24
|
|
|
25
25
|
// src/posthog-key.ts
|
|
@@ -1007,7 +1007,8 @@ function resettablePaths() {
|
|
|
1007
1007
|
legacy.launcherMarker(),
|
|
1008
1008
|
legacy.lastVersion(),
|
|
1009
1009
|
legacy.auditDashboard(),
|
|
1010
|
-
legacy.
|
|
1010
|
+
legacy.auditCacheDir(),
|
|
1011
|
+
legacy.codexSessionPaths(),
|
|
1011
1012
|
legacy.spoolDir(),
|
|
1012
1013
|
legacy.failedDir(),
|
|
1013
1014
|
legacy.collectorHealth(),
|
|
@@ -1015,12 +1016,10 @@ function resettablePaths() {
|
|
|
1015
1016
|
legacy.cloudManagedPolicies(),
|
|
1016
1017
|
localPoliciesDir(),
|
|
1017
1018
|
cloudPoliciesDir(),
|
|
1018
|
-
at("cursors"),
|
|
1019
1019
|
at("state"),
|
|
1020
1020
|
configFile(),
|
|
1021
1021
|
credentialsFile(),
|
|
1022
1022
|
auditDir(),
|
|
1023
|
-
hookActivityDir(),
|
|
1024
1023
|
customAgentsDir(),
|
|
1025
1024
|
logsDir()
|
|
1026
1025
|
];
|
|
@@ -1454,6 +1453,7 @@ __export(exports_daemon_client, {
|
|
|
1454
1453
|
tryDaemonHook: () => tryDaemonHook,
|
|
1455
1454
|
isDaemonConfigured: () => isDaemonConfigured,
|
|
1456
1455
|
daemonSocketPresent: () => daemonSocketPresent,
|
|
1456
|
+
daemonAcceptsConnections: () => daemonAcceptsConnections,
|
|
1457
1457
|
attemptDaemonHook: () => attemptDaemonHook
|
|
1458
1458
|
});
|
|
1459
1459
|
import { createConnection } from "node:net";
|
|
@@ -1465,6 +1465,27 @@ function daemonSocketPresent() {
|
|
|
1465
1465
|
return false;
|
|
1466
1466
|
}
|
|
1467
1467
|
}
|
|
1468
|
+
async function daemonAcceptsConnections(timeoutMs = DAEMON_CONNECT_TIMEOUT_MS) {
|
|
1469
|
+
if (process.platform === "win32")
|
|
1470
|
+
return false;
|
|
1471
|
+
return new Promise((resolve2) => {
|
|
1472
|
+
let settled = false;
|
|
1473
|
+
const done = (v) => {
|
|
1474
|
+
if (settled)
|
|
1475
|
+
return;
|
|
1476
|
+
settled = true;
|
|
1477
|
+
clearTimeout(timer);
|
|
1478
|
+
socket.removeAllListeners();
|
|
1479
|
+
socket.destroy();
|
|
1480
|
+
resolve2(v);
|
|
1481
|
+
};
|
|
1482
|
+
const socket = createConnection({ path: socketPath() });
|
|
1483
|
+
const timer = setTimeout(() => done(false), timeoutMs);
|
|
1484
|
+
timer.unref?.();
|
|
1485
|
+
socket.on("connect", () => done(true));
|
|
1486
|
+
socket.on("error", () => done(false));
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1468
1489
|
function socketPath() {
|
|
1469
1490
|
if (process.env.FAILPROOFAI_DAEMON_SOCKET)
|
|
1470
1491
|
return process.env.FAILPROOFAI_DAEMON_SOCKET;
|
|
@@ -2751,11 +2772,82 @@ function blockEnvFiles(ctx) {
|
|
|
2751
2772
|
}
|
|
2752
2773
|
return allow();
|
|
2753
2774
|
}
|
|
2775
|
+
function quoteAwareSegments(command) {
|
|
2776
|
+
const segments = [];
|
|
2777
|
+
let tokens = [];
|
|
2778
|
+
let token = "";
|
|
2779
|
+
let quote = null;
|
|
2780
|
+
const endToken = () => {
|
|
2781
|
+
if (token)
|
|
2782
|
+
tokens.push(token);
|
|
2783
|
+
token = "";
|
|
2784
|
+
};
|
|
2785
|
+
const endSegment = () => {
|
|
2786
|
+
endToken();
|
|
2787
|
+
if (tokens.length)
|
|
2788
|
+
segments.push(tokens);
|
|
2789
|
+
tokens = [];
|
|
2790
|
+
};
|
|
2791
|
+
for (let i = 0;i < command.length; i++) {
|
|
2792
|
+
const c = command[i];
|
|
2793
|
+
if (quote) {
|
|
2794
|
+
if (c === "\\" && quote === '"' && i + 1 < command.length) {
|
|
2795
|
+
token += command[++i];
|
|
2796
|
+
continue;
|
|
2797
|
+
}
|
|
2798
|
+
if (c === quote) {
|
|
2799
|
+
quote = null;
|
|
2800
|
+
continue;
|
|
2801
|
+
}
|
|
2802
|
+
token += c;
|
|
2803
|
+
continue;
|
|
2804
|
+
}
|
|
2805
|
+
if (c === "\\" && i + 1 < command.length) {
|
|
2806
|
+
token += command[++i];
|
|
2807
|
+
continue;
|
|
2808
|
+
}
|
|
2809
|
+
if (c === '"' || c === "'") {
|
|
2810
|
+
quote = c;
|
|
2811
|
+
continue;
|
|
2812
|
+
}
|
|
2813
|
+
if (/\s/.test(c)) {
|
|
2814
|
+
endToken();
|
|
2815
|
+
continue;
|
|
2816
|
+
}
|
|
2817
|
+
if (/[;&|\n\r(){}`]/.test(c)) {
|
|
2818
|
+
endSegment();
|
|
2819
|
+
continue;
|
|
2820
|
+
}
|
|
2821
|
+
token += c;
|
|
2822
|
+
}
|
|
2823
|
+
endSegment();
|
|
2824
|
+
return segments;
|
|
2825
|
+
}
|
|
2826
|
+
function namesElevation(command, depth = 0) {
|
|
2827
|
+
for (const tokens of quoteAwareSegments(command)) {
|
|
2828
|
+
let i = 0;
|
|
2829
|
+
while (i < tokens.length) {
|
|
2830
|
+
const token = tokens[i];
|
|
2831
|
+
const base = token.slice(token.lastIndexOf("/") + 1);
|
|
2832
|
+
if (base === "sudo" || base === "doas")
|
|
2833
|
+
return true;
|
|
2834
|
+
const isSkippable = ENV_ASSIGNMENT_RE.test(token) || token.startsWith("-") || token.startsWith(">") || token.startsWith("<") || RUNNER_OPERAND_RE.test(token) || COMMAND_PREFIX_TOKENS.has(base);
|
|
2835
|
+
if (!isSkippable)
|
|
2836
|
+
break;
|
|
2837
|
+
if (EVAL_FLAG_RE.test(token) && depth < 3 && i + 1 < tokens.length) {
|
|
2838
|
+
if (namesElevation(tokens[i + 1], depth + 1))
|
|
2839
|
+
return true;
|
|
2840
|
+
}
|
|
2841
|
+
i++;
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
return false;
|
|
2845
|
+
}
|
|
2754
2846
|
function blockSudo(ctx) {
|
|
2755
2847
|
if (ctx.toolName !== "Bash")
|
|
2756
2848
|
return allow();
|
|
2757
2849
|
const cmd = getCommand(ctx).trimStart();
|
|
2758
|
-
if (
|
|
2850
|
+
if (namesElevation(cmd)) {
|
|
2759
2851
|
const allowPatterns = ctx.params?.allowPatterns ?? [];
|
|
2760
2852
|
if (allowPatterns.some((p) => matchesAllowedPattern(cmd, p)))
|
|
2761
2853
|
return allow();
|
|
@@ -3515,7 +3607,7 @@ function registerBuiltinPolicies(enabledNames) {
|
|
|
3515
3607
|
}
|
|
3516
3608
|
}
|
|
3517
3609
|
}
|
|
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,
|
|
3610
|
+
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
3611
|
var init_builtin_policies = __esm(() => {
|
|
3520
3612
|
init_hook_logger();
|
|
3521
3613
|
isClaudeInternalPath = isAgentInternalPath;
|
|
@@ -3552,7 +3644,6 @@ var init_builtin_policies = __esm(() => {
|
|
|
3552
3644
|
CMD_ECHO_ENV_RE = /echo\s+%[A-Za-z_]/i;
|
|
3553
3645
|
ENV_FILE_PATH_RE = /(?:^|[\\/])\.env(?:\.|$)/;
|
|
3554
3646
|
ENV_CMD_RE = /\.env(?:\b|\s|$|\.)/;
|
|
3555
|
-
SUDO_RE = /(?:^|;|&&|\|\|)\s*sudo\s/;
|
|
3556
3647
|
PS_ELEVATION_RE = /Start-Process\s+.*-Verb\s+RunAs/i;
|
|
3557
3648
|
RUNAS_RE = /(?:^|;|&&|\|\|)\s*runas\s/i;
|
|
3558
3649
|
CURL_PIPE_SH_RE = /(?:curl|wget)\s.*\|\s*(?:sh|bash|zsh|dash|ksh|csh|tcsh|fish|ash)\b/;
|
|
@@ -3638,6 +3729,7 @@ var init_builtin_policies = __esm(() => {
|
|
|
3638
3729
|
HELM_RE = /(?:^|[;\n]|&&|\|\|?|&)\s*helm(?:\s|$)/;
|
|
3639
3730
|
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
3731
|
gitBranchCache = new Map;
|
|
3732
|
+
EVAL_FLAG_RE = /^-{1,2}c$/;
|
|
3641
3733
|
HOME_PREFIX_RE = /^(?:~[A-Za-z0-9_.-]*|\$HOME|\$\{HOME\})(?=$|\/)/;
|
|
3642
3734
|
RM_CMD_RE = /^(?:\/\S*\/)?rm$/;
|
|
3643
3735
|
FIND_CMD_RE = /^(?:\/\S*\/)?find$/;
|
|
@@ -10739,6 +10831,7 @@ __export(exports_daemon_client2, {
|
|
|
10739
10831
|
tryDaemonHook: () => tryDaemonHook2,
|
|
10740
10832
|
isDaemonConfigured: () => isDaemonConfigured2,
|
|
10741
10833
|
daemonSocketPresent: () => daemonSocketPresent2,
|
|
10834
|
+
daemonAcceptsConnections: () => daemonAcceptsConnections2,
|
|
10742
10835
|
attemptDaemonHook: () => attemptDaemonHook2
|
|
10743
10836
|
});
|
|
10744
10837
|
import { createConnection as createConnection2 } from "node:net";
|
|
@@ -10750,6 +10843,27 @@ function daemonSocketPresent2() {
|
|
|
10750
10843
|
return false;
|
|
10751
10844
|
}
|
|
10752
10845
|
}
|
|
10846
|
+
async function daemonAcceptsConnections2(timeoutMs = DAEMON_CONNECT_TIMEOUT_MS2) {
|
|
10847
|
+
if (process.platform === "win32")
|
|
10848
|
+
return false;
|
|
10849
|
+
return new Promise((resolve14) => {
|
|
10850
|
+
let settled = false;
|
|
10851
|
+
const done = (v) => {
|
|
10852
|
+
if (settled)
|
|
10853
|
+
return;
|
|
10854
|
+
settled = true;
|
|
10855
|
+
clearTimeout(timer);
|
|
10856
|
+
socket.removeAllListeners();
|
|
10857
|
+
socket.destroy();
|
|
10858
|
+
resolve14(v);
|
|
10859
|
+
};
|
|
10860
|
+
const socket = createConnection2({ path: socketPath2() });
|
|
10861
|
+
const timer = setTimeout(() => done(false), timeoutMs);
|
|
10862
|
+
timer.unref?.();
|
|
10863
|
+
socket.on("connect", () => done(true));
|
|
10864
|
+
socket.on("error", () => done(false));
|
|
10865
|
+
});
|
|
10866
|
+
}
|
|
10753
10867
|
function socketPath2() {
|
|
10754
10868
|
if (process.env.FAILPROOFAI_DAEMON_SOCKET)
|
|
10755
10869
|
return process.env.FAILPROOFAI_DAEMON_SOCKET;
|
|
@@ -10947,6 +11061,15 @@ function resolveCliCommand() {
|
|
|
10947
11061
|
function shellQuote(value) {
|
|
10948
11062
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
10949
11063
|
}
|
|
11064
|
+
function workerScriptPath() {
|
|
11065
|
+
if (process.env.FAILPROOFAI_WORKER_CMD)
|
|
11066
|
+
return null;
|
|
11067
|
+
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT;
|
|
11068
|
+
if (!packageRoot)
|
|
11069
|
+
return null;
|
|
11070
|
+
const workerScript = resolve14(packageRoot, "dist", "worker.mjs");
|
|
11071
|
+
return existsSync17(workerScript) ? workerScript : null;
|
|
11072
|
+
}
|
|
10950
11073
|
function assertUnitSafe(value, field) {
|
|
10951
11074
|
if (UNIT_UNSAFE_RE.test(value)) {
|
|
10952
11075
|
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 +11208,13 @@ function systemdUnitContents(binaryPath, workerCmd, cliCmd = null) {
|
|
|
11085
11208
|
const user = assertUnitSafe(serviceUser(), "User");
|
|
11086
11209
|
assertUnitSafe(binaryPath, "ExecStart");
|
|
11087
11210
|
assertUnitSafe(homedir19(), "HOME");
|
|
11211
|
+
const conditionPaths = [binaryPath, workerScriptPath()].filter((p) => typeof p === "string" && existsSync17(p));
|
|
11212
|
+
const conditionLines = conditionPaths.map((p) => `ConditionPathExists=${assertUnitSafe(p, "ConditionPathExists")}
|
|
11213
|
+
`).join("");
|
|
11088
11214
|
return `[Unit]
|
|
11089
11215
|
Description=failproofai background daemon (failproofaid) for ${user}
|
|
11090
11216
|
After=network.target
|
|
11091
|
-
|
|
11217
|
+
${conditionLines}
|
|
11092
11218
|
[Service]
|
|
11093
11219
|
Type=simple
|
|
11094
11220
|
User=${user}
|
|
@@ -11209,19 +11335,37 @@ async function installDaemonService() {
|
|
|
11209
11335
|
}
|
|
11210
11336
|
return { installed: true };
|
|
11211
11337
|
}
|
|
11212
|
-
async function
|
|
11338
|
+
async function probeDaemon() {
|
|
11213
11339
|
try {
|
|
11214
|
-
const { attemptDaemonHook: attemptDaemonHook3 } = await Promise.resolve().then(() => (init_daemon_client2(), exports_daemon_client2));
|
|
11215
|
-
const
|
|
11216
|
-
|
|
11217
|
-
|
|
11218
|
-
|
|
11219
|
-
|
|
11220
|
-
|
|
11340
|
+
const { attemptDaemonHook: attemptDaemonHook3, daemonAcceptsConnections: daemonAcceptsConnections3 } = await Promise.resolve().then(() => (init_daemon_client2(), exports_daemon_client2));
|
|
11341
|
+
const deadline = Date.now() + DAEMON_PROBE_READY_TIMEOUT_MS;
|
|
11342
|
+
let everConnected = false;
|
|
11343
|
+
for (;; ) {
|
|
11344
|
+
if (await daemonAcceptsConnections3()) {
|
|
11345
|
+
everConnected = true;
|
|
11346
|
+
const attempt = await attemptDaemonHook3({
|
|
11347
|
+
hookEvent: "SessionStart",
|
|
11348
|
+
cli: "claude",
|
|
11349
|
+
stdin: JSON.stringify({
|
|
11350
|
+
hook_event_name: "SessionStart",
|
|
11351
|
+
source: "failproofai-health-probe"
|
|
11352
|
+
})
|
|
11353
|
+
}, { responseTimeoutMs: DAEMON_PROBE_TIMEOUT_MS });
|
|
11354
|
+
if (attempt.ok || attempt.failure === "protocol-mismatch")
|
|
11355
|
+
return { ok: true };
|
|
11356
|
+
}
|
|
11357
|
+
if (Date.now() >= deadline)
|
|
11358
|
+
break;
|
|
11359
|
+
await new Promise((r) => setTimeout(r, DAEMON_PROBE_RETRY_MS));
|
|
11360
|
+
}
|
|
11361
|
+
return { ok: false, reason: everConnected ? "worker" : "unreachable" };
|
|
11221
11362
|
} catch {
|
|
11222
|
-
return false;
|
|
11363
|
+
return { ok: false, reason: "unreachable" };
|
|
11223
11364
|
}
|
|
11224
11365
|
}
|
|
11366
|
+
async function probeDaemonEndToEnd() {
|
|
11367
|
+
return (await probeDaemon()).ok;
|
|
11368
|
+
}
|
|
11225
11369
|
async function waitForDaemonRunning() {
|
|
11226
11370
|
const deadline = Date.now() + SERVICE_START_TIMEOUT_MS;
|
|
11227
11371
|
for (;; ) {
|
|
@@ -11406,6 +11550,16 @@ function daemonVersionSkew() {
|
|
|
11406
11550
|
return null;
|
|
11407
11551
|
return recorded === version2 ? null : { installed: recorded, expected: version2 };
|
|
11408
11552
|
}
|
|
11553
|
+
function inactiveLinuxStatus() {
|
|
11554
|
+
try {
|
|
11555
|
+
return interpretConditionResult(execFileSync3("systemctl", ["show", systemdUnitName(), "--property=ConditionResult", "--value"], { stdio: ["ignore", "pipe", "ignore"], timeout: SERVICE_CMD_TIMEOUT_MS }).toString());
|
|
11556
|
+
} catch {
|
|
11557
|
+
return "stopped";
|
|
11558
|
+
}
|
|
11559
|
+
}
|
|
11560
|
+
function interpretConditionResult(raw) {
|
|
11561
|
+
return raw.trim() === "no" ? "condition-failed" : "stopped";
|
|
11562
|
+
}
|
|
11409
11563
|
function daemonServiceStatus() {
|
|
11410
11564
|
if (!isDaemonSupportedPlatform())
|
|
11411
11565
|
return "unsupported-platform";
|
|
@@ -11417,9 +11571,9 @@ function daemonServiceStatus() {
|
|
|
11417
11571
|
stdio: ["ignore", "pipe", "ignore"],
|
|
11418
11572
|
timeout: SERVICE_CMD_TIMEOUT_MS
|
|
11419
11573
|
}).toString().trim();
|
|
11420
|
-
return out === "active" ? "running" :
|
|
11574
|
+
return out === "active" ? "running" : inactiveLinuxStatus();
|
|
11421
11575
|
} catch {
|
|
11422
|
-
return
|
|
11576
|
+
return inactiveLinuxStatus();
|
|
11423
11577
|
}
|
|
11424
11578
|
}
|
|
11425
11579
|
const plistPath = launchdPlistPath();
|
|
@@ -11440,7 +11594,7 @@ function daemonServiceStatus() {
|
|
|
11440
11594
|
return "stopped";
|
|
11441
11595
|
}
|
|
11442
11596
|
}
|
|
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;
|
|
11597
|
+
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
11598
|
var init_daemon_service = __esm(() => {
|
|
11445
11599
|
init_hook_logger();
|
|
11446
11600
|
init_daemon_download();
|
|
@@ -11800,11 +11954,12 @@ var init_handler = __esm(() => {
|
|
|
11800
11954
|
var exports_fp_reset = {};
|
|
11801
11955
|
__export(exports_fp_reset, {
|
|
11802
11956
|
resetHome: () => resetHome,
|
|
11957
|
+
migrateHookActivity: () => migrateHookActivity,
|
|
11803
11958
|
migrateConventionPolicies: () => migrateConventionPolicies,
|
|
11804
11959
|
layoutWarningForHook: () => layoutWarningForHook2,
|
|
11805
11960
|
checkLayoutForCli: () => checkLayoutForCli
|
|
11806
11961
|
});
|
|
11807
|
-
import { existsSync as existsSync18, mkdirSync as mkdirSync10, readdirSync as readdirSync13, renameSync as renameSync5, rmSync as rmSync6 } from "node:fs";
|
|
11962
|
+
import { copyFileSync, existsSync as existsSync18, mkdirSync as mkdirSync10, readdirSync as readdirSync13, renameSync as renameSync5, rmSync as rmSync6 } from "node:fs";
|
|
11808
11963
|
import { resolve as resolve15 } from "node:path";
|
|
11809
11964
|
function migrateConventionPolicies() {
|
|
11810
11965
|
const from = policiesDir();
|
|
@@ -11829,8 +11984,45 @@ function migrateConventionPolicies() {
|
|
|
11829
11984
|
} catch {}
|
|
11830
11985
|
return moved.sort((a, b) => a.localeCompare(b));
|
|
11831
11986
|
}
|
|
11987
|
+
function migrateHookActivity() {
|
|
11988
|
+
const from = legacy.hookActivityDir();
|
|
11989
|
+
if (!existsSync18(from))
|
|
11990
|
+
return [];
|
|
11991
|
+
const moved = [];
|
|
11992
|
+
try {
|
|
11993
|
+
const entries = readdirSync13(from, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl"));
|
|
11994
|
+
if (entries.length === 0)
|
|
11995
|
+
return [];
|
|
11996
|
+
const to = hookActivityDir();
|
|
11997
|
+
mkdirSync10(to, { recursive: true });
|
|
11998
|
+
const stamp2 = Date.now();
|
|
11999
|
+
let seq = 0;
|
|
12000
|
+
for (const entry of entries) {
|
|
12001
|
+
const source = resolve15(from, entry.name);
|
|
12002
|
+
let name = entry.name === "current.jsonl" ? `page-${stamp2}-${seq++}.jsonl` : entry.name;
|
|
12003
|
+
let target = resolve15(to, name);
|
|
12004
|
+
while (existsSync18(target)) {
|
|
12005
|
+
name = `page-${stamp2}-${seq++}.jsonl`;
|
|
12006
|
+
target = resolve15(to, name);
|
|
12007
|
+
}
|
|
12008
|
+
try {
|
|
12009
|
+
renameSync5(source, target);
|
|
12010
|
+
moved.push(name);
|
|
12011
|
+
} catch (err) {
|
|
12012
|
+
if (err?.code === "EXDEV") {
|
|
12013
|
+
try {
|
|
12014
|
+
copyFileSync(source, target);
|
|
12015
|
+
moved.push(name);
|
|
12016
|
+
} catch {}
|
|
12017
|
+
}
|
|
12018
|
+
}
|
|
12019
|
+
}
|
|
12020
|
+
} catch {}
|
|
12021
|
+
return moved.sort((a, b) => a.localeCompare(b));
|
|
12022
|
+
}
|
|
11832
12023
|
function resetHome(from) {
|
|
11833
12024
|
const migrated = migrateConventionPolicies();
|
|
12025
|
+
const activity = migrateHookActivity();
|
|
11834
12026
|
const removed = [];
|
|
11835
12027
|
for (const path3 of resettablePaths()) {
|
|
11836
12028
|
if (!existsSync18(path3))
|
|
@@ -11841,7 +12033,7 @@ function resetHome(from) {
|
|
|
11841
12033
|
} catch {}
|
|
11842
12034
|
}
|
|
11843
12035
|
writeVersionFile();
|
|
11844
|
-
return { removed, migrated, from };
|
|
12036
|
+
return { removed, migrated, activity, from };
|
|
11845
12037
|
}
|
|
11846
12038
|
async function checkLayoutForCli() {
|
|
11847
12039
|
const state = detectLayout();
|
|
@@ -11860,21 +12052,25 @@ async function checkLayoutForCli() {
|
|
|
11860
12052
|
};
|
|
11861
12053
|
}
|
|
11862
12054
|
if (state.kind === "stale") {
|
|
11863
|
-
const { removed, migrated } = resetHome(state.found);
|
|
12055
|
+
const { removed, migrated, activity } = resetHome(state.found);
|
|
11864
12056
|
return {
|
|
11865
12057
|
state,
|
|
11866
12058
|
fatal: false,
|
|
11867
12059
|
didReset: true,
|
|
11868
12060
|
lines: [
|
|
11869
12061
|
`failproofai reorganised ${failproofaiHome()} in this version.`,
|
|
11870
|
-
`Removed ${removed.length} item(s) from the old layout — policy config
|
|
11871
|
-
`
|
|
12062
|
+
`Removed ${removed.length} item(s) from the old layout — policy config and`,
|
|
12063
|
+
`audit cache. Your daemon binary and decision history were kept.`,
|
|
11872
12064
|
...migrated.length > 0 ? [
|
|
11873
12065
|
``,
|
|
11874
12066
|
`Kept your own policy file(s) and moved them to where this version`,
|
|
11875
12067
|
`loads them (${customPoliciesDir()}):`,
|
|
11876
12068
|
...migrated.map((name) => ` ${name}`)
|
|
11877
12069
|
] : [],
|
|
12070
|
+
...activity.length > 0 ? [
|
|
12071
|
+
``,
|
|
12072
|
+
`Carried ${activity.length} page(s) of decision history into ${hookActivityDir()}.`
|
|
12073
|
+
] : [],
|
|
11878
12074
|
``,
|
|
11879
12075
|
`Run \`failproofai config\` to set up again.`
|
|
11880
12076
|
]
|
|
@@ -11906,6 +12102,20 @@ async function healDaemonFlag() {
|
|
|
11906
12102
|
``
|
|
11907
12103
|
];
|
|
11908
12104
|
}
|
|
12105
|
+
if (status === "condition-failed") {
|
|
12106
|
+
updateConfig({ daemon: { configured: false } });
|
|
12107
|
+
return [
|
|
12108
|
+
`failproofaid is installed but cannot start — a file its service requires is`,
|
|
12109
|
+
`gone (most often because failproofai was removed with \`npm rm -g\`, which`,
|
|
12110
|
+
`deletes the worker but leaves the service behind). This machine was`,
|
|
12111
|
+
`configured to require the daemon, which denies every tool call, so that flag`,
|
|
12112
|
+
`is cleared; policies now evaluate in-process.`,
|
|
12113
|
+
``,
|
|
12114
|
+
`Run \`failproofai uninstall\` to remove the leftover service, or`,
|
|
12115
|
+
`\`failproofai config\` to rebuild it. \`${daemonStatusCommand()}\` names the missing path.`,
|
|
12116
|
+
``
|
|
12117
|
+
];
|
|
12118
|
+
}
|
|
11909
12119
|
if (status === "running" && !await probeDaemonEndToEnd()) {
|
|
11910
12120
|
updateConfig({ daemon: { configured: false } });
|
|
11911
12121
|
return [
|
|
@@ -12215,7 +12425,8 @@ var init_first_run_gate = __esm(() => {
|
|
|
12215
12425
|
FIRST_RUN_EXEMPT_SUBCOMMANDS = [
|
|
12216
12426
|
"config",
|
|
12217
12427
|
"policies",
|
|
12218
|
-
"policy"
|
|
12428
|
+
"policy",
|
|
12429
|
+
"uninstall"
|
|
12219
12430
|
];
|
|
12220
12431
|
});
|
|
12221
12432
|
|
|
@@ -12787,7 +12998,7 @@ async function validateIngestKey(cred, fetchImpl = fetch, timeoutMs = 1e4) {
|
|
|
12787
12998
|
clearTimeout(timer);
|
|
12788
12999
|
}
|
|
12789
13000
|
}
|
|
12790
|
-
var DEFAULT_INGEST_URL = "https://
|
|
13001
|
+
var DEFAULT_INGEST_URL = "https://app.befailproof.ai/v1/events";
|
|
12791
13002
|
var init_collector_config = __esm(() => {
|
|
12792
13003
|
init_fp_home();
|
|
12793
13004
|
init_fp_config();
|
|
@@ -21925,6 +22136,129 @@ This file can be committed to git — no machine-specific paths.`);
|
|
|
21925
22136
|
} catch {}
|
|
21926
22137
|
}
|
|
21927
22138
|
}
|
|
22139
|
+
async function removeHooks(policyNames, scope = "user", cwd, opts) {
|
|
22140
|
+
const configScope = scope === "all" ? "user" : scope;
|
|
22141
|
+
const selectedClis = opts?.cli && opts.cli.length > 0 ? [...new Set(opts.cli)] : ["claude"];
|
|
22142
|
+
if (opts?.removeCustomHooks) {
|
|
22143
|
+
const config = readScopedHooksConfig(configScope, cwd);
|
|
22144
|
+
delete config.customPoliciesPath;
|
|
22145
|
+
delete config.customPoliciesPaths;
|
|
22146
|
+
writeScopedHooksConfig(config, configScope, cwd);
|
|
22147
|
+
console.log("Custom hooks path cleared.");
|
|
22148
|
+
}
|
|
22149
|
+
if (policyNames && policyNames.length > 0 && !(policyNames.length === 1 && policyNames[0] === "all")) {
|
|
22150
|
+
validatePolicyNames(policyNames);
|
|
22151
|
+
const config = readScopedHooksConfig(configScope, cwd);
|
|
22152
|
+
const removeSet = new Set(policyNames);
|
|
22153
|
+
const remaining = config.enabledPolicies.filter((p) => !removeSet.has(p));
|
|
22154
|
+
const notEnabled = policyNames.filter((p) => !config.enabledPolicies.includes(p));
|
|
22155
|
+
if (notEnabled.length > 0) {
|
|
22156
|
+
console.log(`Warning: policy(ies) not currently enabled: ${notEnabled.join(", ")}`);
|
|
22157
|
+
}
|
|
22158
|
+
const { policyParams: existingParams, ...baseConfig } = config;
|
|
22159
|
+
const filteredParams = existingParams ? Object.fromEntries(Object.entries(existingParams).filter(([k]) => !removeSet.has(k))) : null;
|
|
22160
|
+
const updatedConfig = {
|
|
22161
|
+
...baseConfig,
|
|
22162
|
+
enabledPolicies: remaining,
|
|
22163
|
+
...filteredParams && Object.keys(filteredParams).length > 0 ? { policyParams: filteredParams } : {}
|
|
22164
|
+
};
|
|
22165
|
+
writeScopedHooksConfig(updatedConfig, configScope, cwd);
|
|
22166
|
+
try {
|
|
22167
|
+
const distinctId = getInstanceId2();
|
|
22168
|
+
const actuallyRemoved = policyNames.filter((p) => config.enabledPolicies.includes(p));
|
|
22169
|
+
await trackHookEvent2(distinctId, "hooks_removed", {
|
|
22170
|
+
scope,
|
|
22171
|
+
cli: selectedClis,
|
|
22172
|
+
removal_mode: opts?.betaOnly ? "beta_policies" : "policies",
|
|
22173
|
+
beta_only: opts?.betaOnly ?? false,
|
|
22174
|
+
policies_removed: actuallyRemoved,
|
|
22175
|
+
removed_count: actuallyRemoved.length,
|
|
22176
|
+
...opts?.source ? { source: opts.source } : {},
|
|
22177
|
+
platform: platform3(),
|
|
22178
|
+
arch: arch3(),
|
|
22179
|
+
os_release: release2(),
|
|
22180
|
+
hostname_hash: hashToId2(hostname3())
|
|
22181
|
+
});
|
|
22182
|
+
} catch {}
|
|
22183
|
+
console.log(`Disabled ${policyNames.length - notEnabled.length} policy(ies).`);
|
|
22184
|
+
console.log(`Remaining: ${remaining.length > 0 ? remaining.join(", ") : "(none)"}`);
|
|
22185
|
+
return;
|
|
22186
|
+
}
|
|
22187
|
+
const configBeforeRemoval = readScopedHooksConfig(configScope, cwd);
|
|
22188
|
+
let totalRemoved = 0;
|
|
22189
|
+
let nothingToReport = false;
|
|
22190
|
+
for (const cliId of selectedClis) {
|
|
22191
|
+
const integration = getIntegration(cliId);
|
|
22192
|
+
const scopesToRemove = scope === "all" ? [...integration.scopes] : integration.scopes.includes(scope) ? [scope] : [];
|
|
22193
|
+
for (const s of scopesToRemove) {
|
|
22194
|
+
const settingsPaths = settingsPathsFor(integration, s, cwd);
|
|
22195
|
+
const existing = settingsPaths.filter((p) => existsSync21(p));
|
|
22196
|
+
if (existing.length === 0) {
|
|
22197
|
+
if (scope !== "all" && selectedClis.length === 1) {
|
|
22198
|
+
console.log("No settings file found. Nothing to remove.");
|
|
22199
|
+
nothingToReport = true;
|
|
22200
|
+
}
|
|
22201
|
+
continue;
|
|
22202
|
+
}
|
|
22203
|
+
let removedHere = 0;
|
|
22204
|
+
for (const settingsPath of existing) {
|
|
22205
|
+
const removed = integration.removeHooksFromFile(settingsPath);
|
|
22206
|
+
removedHere += removed;
|
|
22207
|
+
if (removed > 0 && scope !== "all") {
|
|
22208
|
+
console.log(`Removed ${removed} failproofai hook(s) from ${integration.displayName} settings.`);
|
|
22209
|
+
console.log(`Settings: ${settingsPath}`);
|
|
22210
|
+
}
|
|
22211
|
+
}
|
|
22212
|
+
if (removedHere === 0 && scope !== "all" && selectedClis.length === 1) {
|
|
22213
|
+
console.log("No hooks found in settings. Nothing to remove.");
|
|
22214
|
+
nothingToReport = true;
|
|
22215
|
+
continue;
|
|
22216
|
+
}
|
|
22217
|
+
totalRemoved += removedHere;
|
|
22218
|
+
}
|
|
22219
|
+
}
|
|
22220
|
+
if (nothingToReport && totalRemoved === 0)
|
|
22221
|
+
return;
|
|
22222
|
+
if (scope === "all") {
|
|
22223
|
+
console.log(`Removed ${totalRemoved} failproofai hook(s) from all scopes.`);
|
|
22224
|
+
for (const cliId of selectedClis) {
|
|
22225
|
+
const integration = getIntegration(cliId);
|
|
22226
|
+
for (const s of integration.scopes) {
|
|
22227
|
+
for (const p of settingsPathsFor(integration, s, cwd)) {
|
|
22228
|
+
console.log(` ${integration.displayName} / ${s}: ${p}`);
|
|
22229
|
+
}
|
|
22230
|
+
}
|
|
22231
|
+
}
|
|
22232
|
+
}
|
|
22233
|
+
try {
|
|
22234
|
+
const distinctId = getInstanceId2();
|
|
22235
|
+
await trackHookEvent2(distinctId, "hooks_removed", {
|
|
22236
|
+
scope,
|
|
22237
|
+
cli: selectedClis,
|
|
22238
|
+
removal_mode: "hooks",
|
|
22239
|
+
policies_removed: configBeforeRemoval.enabledPolicies,
|
|
22240
|
+
removed_count: totalRemoved,
|
|
22241
|
+
...opts?.source ? { source: opts.source } : {},
|
|
22242
|
+
platform: platform3(),
|
|
22243
|
+
arch: arch3(),
|
|
22244
|
+
os_release: release2(),
|
|
22245
|
+
hostname_hash: hashToId2(hostname3())
|
|
22246
|
+
});
|
|
22247
|
+
} catch {}
|
|
22248
|
+
if (scope === "all") {
|
|
22249
|
+
for (const s of HOOK_SCOPES) {
|
|
22250
|
+
const existing = readScopedHooksConfig(s, cwd);
|
|
22251
|
+
if (existing.enabledPolicies.length > 0 || existing.customPoliciesPaths?.length || existing.customPoliciesPath || existing.policyParams) {
|
|
22252
|
+
const { customPoliciesPath: _drop, customPoliciesPaths: _dropMany, policyParams: _dropParams, ...rest } = existing;
|
|
22253
|
+
writeScopedHooksConfig({ ...rest, enabledPolicies: [] }, s, cwd);
|
|
22254
|
+
}
|
|
22255
|
+
}
|
|
22256
|
+
} else if (!HOOK_SCOPES.some((s) => hooksInstalledInSettings(s, cwd))) {
|
|
22257
|
+
const existing = readScopedHooksConfig(configScope, cwd);
|
|
22258
|
+
const { customPoliciesPath: _drop, customPoliciesPaths: _dropMany, policyParams: _dropParams, ...rest } = existing;
|
|
22259
|
+
writeScopedHooksConfig({ ...rest, enabledPolicies: [] }, configScope, cwd);
|
|
22260
|
+
}
|
|
22261
|
+
}
|
|
21928
22262
|
var VALID_POLICY_NAMES;
|
|
21929
22263
|
var init_manager = __esm(() => {
|
|
21930
22264
|
init_types();
|
|
@@ -25743,31 +26077,26 @@ refreshed. Re-run \`failproofai config\` once you can use sudo.
|
|
|
25743
26077
|
}
|
|
25744
26078
|
}
|
|
25745
26079
|
if (url === null) {
|
|
25746
|
-
const
|
|
25747
|
-
|
|
25748
|
-
|
|
25749
|
-
|
|
25750
|
-
|
|
25751
|
-
|
|
25752
|
-
|
|
25753
|
-
|
|
25754
|
-
|
|
25755
|
-
|
|
25756
|
-
});
|
|
25757
|
-
if (entered === null)
|
|
25758
|
-
return cancel();
|
|
25759
|
-
const validated = validateCloudUrl(cloudBaseFor(entered));
|
|
25760
|
-
if (!validated.ok) {
|
|
26080
|
+
const override = process.env.FAILPROOFAI_CLOUD_URL?.trim();
|
|
26081
|
+
if (override) {
|
|
26082
|
+
const validated = validateCloudUrl(cloudBaseFor(override));
|
|
26083
|
+
if (!validated.ok) {
|
|
26084
|
+
stdout.write(`
|
|
26085
|
+
FAILPROOFAI_CLOUD_URL is set to "${override}", which cannot be used: ${validated.reason}
|
|
26086
|
+
`);
|
|
26087
|
+
return cancel();
|
|
26088
|
+
}
|
|
26089
|
+
url = validated.url;
|
|
25761
26090
|
stdout.write(`
|
|
25762
|
-
${
|
|
26091
|
+
Using ${url} (from FAILPROOFAI_CLOUD_URL).
|
|
25763
26092
|
`);
|
|
25764
|
-
|
|
26093
|
+
} else {
|
|
26094
|
+
url = cloudBaseFor(DEFAULT_INGEST_URL);
|
|
25765
26095
|
}
|
|
25766
|
-
url = validated.url;
|
|
25767
26096
|
}
|
|
25768
26097
|
if (token === null) {
|
|
25769
26098
|
token = await promptText({
|
|
25770
|
-
message:
|
|
26099
|
+
message: `API key for ${new URL(url).host}`,
|
|
25771
26100
|
hint: "needs events:add · policies:pull enables managed policy too",
|
|
25772
26101
|
mask: true,
|
|
25773
26102
|
validate: (v) => v.length >= 8 ? null : "that looks too short to be a key",
|
|
@@ -25862,18 +26191,22 @@ Setup stopped before changing anything. Once that is fixed, re-run:
|
|
|
25862
26191
|
outro("Nothing was changed.", { ok: false }, stdout);
|
|
25863
26192
|
return { applied: false, abort: "daemon_failed" };
|
|
25864
26193
|
}
|
|
25865
|
-
|
|
25866
|
-
|
|
25867
|
-
|
|
25868
|
-
|
|
25869
|
-
|
|
25870
|
-
|
|
26194
|
+
const probe = await probeDaemon();
|
|
26195
|
+
if (!probe.ok) {
|
|
26196
|
+
hookLogWarn(`failproofaid was installed and running but did not answer a policy evaluation (${probe.reason})`);
|
|
26197
|
+
stdout.write((probe.reason === "worker" ? `
|
|
26198
|
+
failproofaid started but cannot evaluate policies — it is listening, and
|
|
26199
|
+
` + `its worker process could not be run.
|
|
26200
|
+
` : `
|
|
26201
|
+
failproofaid started but could not be reached on its socket.
|
|
26202
|
+
`) + `Setup stopped before changing anything, because a machine configured to
|
|
26203
|
+
require a daemon that cannot answer denies every tool call.
|
|
25871
26204
|
|
|
25872
26205
|
Check it with: ${daemonStatusCommand() ?? "systemctl status failproofaid"}
|
|
25873
26206
|
Then re-run: failproofai config
|
|
25874
26207
|
|
|
25875
26208
|
`);
|
|
25876
|
-
emit("configure_aborted", { reason:
|
|
26209
|
+
emit("configure_aborted", { reason: `daemon_not_answering_${probe.reason}` });
|
|
25877
26210
|
outro("Nothing was changed.", { ok: false }, stdout);
|
|
25878
26211
|
return { applied: false, abort: "daemon_failed" };
|
|
25879
26212
|
}
|
|
@@ -25998,21 +26331,701 @@ var init_configure_wizard = __esm(() => {
|
|
|
25998
26331
|
init_onboarding_lock();
|
|
25999
26332
|
});
|
|
26000
26333
|
|
|
26001
|
-
// src/hooks/
|
|
26002
|
-
var
|
|
26003
|
-
__export(
|
|
26004
|
-
|
|
26005
|
-
|
|
26006
|
-
installHooks: () => installHooks2,
|
|
26007
|
-
hooksInstalledInSettings: () => hooksInstalledInSettings2,
|
|
26008
|
-
getSettingsPath: () => getSettingsPath2
|
|
26334
|
+
// src/hooks/uninstall-cli.ts
|
|
26335
|
+
var exports_uninstall_cli = {};
|
|
26336
|
+
__export(exports_uninstall_cli, {
|
|
26337
|
+
serviceStateLabel: () => serviceStateLabel,
|
|
26338
|
+
runUninstallCommand: () => runUninstallCommand
|
|
26009
26339
|
});
|
|
26010
|
-
import {
|
|
26011
|
-
|
|
26012
|
-
|
|
26013
|
-
|
|
26014
|
-
|
|
26015
|
-
|
|
26340
|
+
import { existsSync as existsSync31, rmSync as rmSync10 } from "node:fs";
|
|
26341
|
+
function survey(cwd) {
|
|
26342
|
+
const clis = [];
|
|
26343
|
+
for (const id of listInstallableIds()) {
|
|
26344
|
+
for (const scope of ["user", "project", "local"]) {
|
|
26345
|
+
try {
|
|
26346
|
+
if (getIntegration(id).hooksInstalledInSettings(scope, cwd)) {
|
|
26347
|
+
clis.push(id);
|
|
26348
|
+
break;
|
|
26349
|
+
}
|
|
26350
|
+
} catch {}
|
|
26351
|
+
}
|
|
26352
|
+
}
|
|
26353
|
+
let daemonConfigured = false;
|
|
26354
|
+
try {
|
|
26355
|
+
daemonConfigured = readConfig().daemon.configured;
|
|
26356
|
+
} catch {}
|
|
26357
|
+
const servicePath = isDaemonSupportedPlatform() ? daemonServiceFilePath() : null;
|
|
26358
|
+
return {
|
|
26359
|
+
clis,
|
|
26360
|
+
servicePath,
|
|
26361
|
+
serviceInstalled: !!servicePath && existsSync31(servicePath),
|
|
26362
|
+
daemonConfigured,
|
|
26363
|
+
homeExists: existsSync31(failproofaiHome())
|
|
26364
|
+
};
|
|
26365
|
+
}
|
|
26366
|
+
async function runUninstallCommand(opts = {}) {
|
|
26367
|
+
const found = survey(opts.cwd);
|
|
26368
|
+
const lines = [];
|
|
26369
|
+
const nothingToDo = found.clis.length === 0 && !found.serviceInstalled && !found.daemonConfigured && !(opts.purge && found.homeExists);
|
|
26370
|
+
if (nothingToDo) {
|
|
26371
|
+
return {
|
|
26372
|
+
exitCode: 0,
|
|
26373
|
+
planLines: 0,
|
|
26374
|
+
purged: false,
|
|
26375
|
+
lines: [
|
|
26376
|
+
"Nothing to uninstall — no hook entries, no daemon service, and nothing configured.",
|
|
26377
|
+
...found.homeExists && !opts.purge ? [
|
|
26378
|
+
``,
|
|
26379
|
+
`${failproofaiHome()} still holds settings and audit history.`,
|
|
26380
|
+
`Remove it with \`failproofai uninstall --purge\`.`
|
|
26381
|
+
] : []
|
|
26382
|
+
]
|
|
26383
|
+
};
|
|
26384
|
+
}
|
|
26385
|
+
const plan = [];
|
|
26386
|
+
if (found.clis.length > 0) {
|
|
26387
|
+
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(", "));
|
|
26388
|
+
}
|
|
26389
|
+
if (found.daemonConfigured) {
|
|
26390
|
+
plan.push(` • stop requiring the daemon (policies go back to evaluating in-process)`);
|
|
26391
|
+
}
|
|
26392
|
+
if (found.serviceInstalled) {
|
|
26393
|
+
plan.push(` • stop, disable and delete the service at ${found.servicePath} — needs sudo`);
|
|
26394
|
+
}
|
|
26395
|
+
let purged = false;
|
|
26396
|
+
if (opts.purge && found.homeExists) {
|
|
26397
|
+
plan.push(` • delete ${failproofaiHome()} — settings, credentials, audit history and the daemon binary`);
|
|
26398
|
+
}
|
|
26399
|
+
lines.push("failproofai uninstall will:", ...plan);
|
|
26400
|
+
if (!opts.purge && found.homeExists) {
|
|
26401
|
+
lines.push(``, `${failproofaiHome()} is KEPT (settings and audit history survive a reinstall).`, `Add --purge to delete it too.`);
|
|
26402
|
+
}
|
|
26403
|
+
const planLines = lines.length;
|
|
26404
|
+
if (opts.dryRun) {
|
|
26405
|
+
lines.push(``, `--dry-run: nothing was changed.`);
|
|
26406
|
+
return { exitCode: 0, lines, planLines, purged: false };
|
|
26407
|
+
}
|
|
26408
|
+
if (!opts.yes) {
|
|
26409
|
+
if (!opts.confirm) {
|
|
26410
|
+
lines.push(``, `Refusing to uninstall without confirmation. Re-run with --yes.`);
|
|
26411
|
+
return { exitCode: 1, lines, planLines, purged: false };
|
|
26412
|
+
}
|
|
26413
|
+
if (!await opts.confirm(lines)) {
|
|
26414
|
+
return { exitCode: 1, planLines, purged: false, lines: [...lines, ``, `Cancelled — nothing was changed.`] };
|
|
26415
|
+
}
|
|
26416
|
+
}
|
|
26417
|
+
lines.push(``);
|
|
26418
|
+
const failures = [];
|
|
26419
|
+
if (found.daemonConfigured) {
|
|
26420
|
+
try {
|
|
26421
|
+
setDaemonConfigured(false);
|
|
26422
|
+
lines.push(`✓ policies now evaluate in-process (this machine no longer requires the daemon)`);
|
|
26423
|
+
} catch (err) {
|
|
26424
|
+
return {
|
|
26425
|
+
exitCode: 2,
|
|
26426
|
+
planLines,
|
|
26427
|
+
purged: false,
|
|
26428
|
+
lines: [
|
|
26429
|
+
...lines,
|
|
26430
|
+
`✗ could not update ${failproofaiHome()}/config.toml: ${err instanceof Error ? err.message : String(err)}`,
|
|
26431
|
+
``,
|
|
26432
|
+
`Stopped before touching the service. Removing it while this machine still`,
|
|
26433
|
+
`requires it would deny every tool call. Fix the file's permissions and re-run.`
|
|
26434
|
+
]
|
|
26435
|
+
};
|
|
26436
|
+
}
|
|
26437
|
+
}
|
|
26438
|
+
if (found.clis.length > 0) {
|
|
26439
|
+
try {
|
|
26440
|
+
await removeHooks(undefined, "all", opts.cwd, {
|
|
26441
|
+
cli: found.clis,
|
|
26442
|
+
removeCustomHooks: true,
|
|
26443
|
+
source: "uninstall_command"
|
|
26444
|
+
});
|
|
26445
|
+
lines.push(`✓ removed hook entries from ${found.clis.length} agent CLI${found.clis.length === 1 ? "" : "s"}`);
|
|
26446
|
+
} catch (err) {
|
|
26447
|
+
failures.push(`hook entries: ${err instanceof Error ? err.message : String(err)} ` + `(finish with \`failproofai policies --uninstall --scope all\`)`);
|
|
26448
|
+
}
|
|
26449
|
+
}
|
|
26450
|
+
if (found.serviceInstalled) {
|
|
26451
|
+
try {
|
|
26452
|
+
await uninstallDaemonService();
|
|
26453
|
+
if (found.servicePath && existsSync31(found.servicePath)) {
|
|
26454
|
+
failures.push(`the service at ${found.servicePath} is still there (most often: no sudo). ` + `Remove it with the commands below.`);
|
|
26455
|
+
} else {
|
|
26456
|
+
lines.push(`✓ stopped and removed the daemon service`);
|
|
26457
|
+
}
|
|
26458
|
+
} catch (err) {
|
|
26459
|
+
failures.push(`daemon service: ${err instanceof Error ? err.message : String(err)}`);
|
|
26460
|
+
}
|
|
26461
|
+
}
|
|
26462
|
+
if (opts.purge && found.homeExists) {
|
|
26463
|
+
try {
|
|
26464
|
+
rmSync10(failproofaiHome(), { recursive: true, force: true });
|
|
26465
|
+
purged = true;
|
|
26466
|
+
lines.push(`✓ deleted ${failproofaiHome()}`);
|
|
26467
|
+
} catch (err) {
|
|
26468
|
+
failures.push(`${failproofaiHome()}: ${err instanceof Error ? err.message : String(err)}`);
|
|
26469
|
+
}
|
|
26470
|
+
}
|
|
26471
|
+
if (failures.length > 0) {
|
|
26472
|
+
lines.push(``, `Not everything could be removed:`);
|
|
26473
|
+
for (const f of failures)
|
|
26474
|
+
lines.push(` ✗ ${f}`);
|
|
26475
|
+
if (found.servicePath && existsSync31(found.servicePath)) {
|
|
26476
|
+
lines.push(``, `To remove the service by hand:`, ...manualServiceRemoval(found.servicePath), ``, `(\`${daemonStatusCommand()}\` shows its current state.)`);
|
|
26477
|
+
}
|
|
26478
|
+
return { exitCode: 1, lines, planLines, purged };
|
|
26479
|
+
}
|
|
26480
|
+
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`);
|
|
26481
|
+
if (!opts.purge && found.homeExists) {
|
|
26482
|
+
lines.push(``, `${failproofaiHome()} was kept. Delete it with \`failproofai uninstall --purge\`.`);
|
|
26483
|
+
}
|
|
26484
|
+
return { exitCode: 0, lines, planLines, purged };
|
|
26485
|
+
}
|
|
26486
|
+
function manualServiceRemoval(servicePath) {
|
|
26487
|
+
if (process.platform === "darwin") {
|
|
26488
|
+
return [` sudo launchctl unload -w ${servicePath}`, ` sudo rm -f ${servicePath}`];
|
|
26489
|
+
}
|
|
26490
|
+
const unit = servicePath.split("/").pop() ?? servicePath;
|
|
26491
|
+
return [
|
|
26492
|
+
` sudo systemctl disable --now ${unit}`,
|
|
26493
|
+
` sudo rm -f ${servicePath}`,
|
|
26494
|
+
` sudo systemctl daemon-reload`
|
|
26495
|
+
];
|
|
26496
|
+
}
|
|
26497
|
+
function serviceStateLabel() {
|
|
26498
|
+
const status = daemonServiceStatus();
|
|
26499
|
+
return status === "condition-failed" ? "installed but skipped by systemd (a file it needs is missing)" : status;
|
|
26500
|
+
}
|
|
26501
|
+
var init_uninstall_cli = __esm(() => {
|
|
26502
|
+
init_fp_home();
|
|
26503
|
+
init_fp_config();
|
|
26504
|
+
init_daemon_service();
|
|
26505
|
+
init_integrations();
|
|
26506
|
+
init_manager();
|
|
26507
|
+
});
|
|
26508
|
+
|
|
26509
|
+
// src/hooks/tui.ts
|
|
26510
|
+
var exports_tui = {};
|
|
26511
|
+
__export(exports_tui, {
|
|
26512
|
+
truncate: () => truncate2,
|
|
26513
|
+
summarize: () => summarize2,
|
|
26514
|
+
selectOne: () => selectOne2,
|
|
26515
|
+
renderLaunchBanner: () => renderLaunchBanner2,
|
|
26516
|
+
renderBrandLogo: () => renderBrandLogo2,
|
|
26517
|
+
promptText: () => promptText2,
|
|
26518
|
+
paint: () => paint2,
|
|
26519
|
+
outro: () => outro2,
|
|
26520
|
+
multiSelect: () => multiSelect2,
|
|
26521
|
+
intro: () => intro2,
|
|
26522
|
+
ellipsize: () => ellipsize2,
|
|
26523
|
+
colorsEnabled: () => colorsEnabled2,
|
|
26524
|
+
brandAnsi: () => brandAnsi2,
|
|
26525
|
+
STEP_DONE: () => STEP_DONE2,
|
|
26526
|
+
STEP_ACTIVE: () => STEP_ACTIVE2,
|
|
26527
|
+
CHECK_ON: () => CHECK_ON2,
|
|
26528
|
+
CHECK_OFF: () => CHECK_OFF2,
|
|
26529
|
+
CARET: () => CARET2,
|
|
26530
|
+
BAR: () => BAR2,
|
|
26531
|
+
ANSI_RESET: () => ANSI_RESET2,
|
|
26532
|
+
ANSI_DIM: () => ANSI_DIM2,
|
|
26533
|
+
ANSI_BOLD: () => ANSI_BOLD2
|
|
26534
|
+
});
|
|
26535
|
+
import * as readline3 from "node:readline";
|
|
26536
|
+
function colorsEnabled2(out) {
|
|
26537
|
+
return !!out.isTTY && !process.env.NO_COLOR;
|
|
26538
|
+
}
|
|
26539
|
+
function brandAnsi2(role) {
|
|
26540
|
+
const h = HUES2[role];
|
|
26541
|
+
const code = truecolorEnabled2() ? `38;2;${h.rgb.join(";")}` : h.basic;
|
|
26542
|
+
return `${ESC2}[${code}m`;
|
|
26543
|
+
}
|
|
26544
|
+
function truecolorEnabled2() {
|
|
26545
|
+
return /truecolor|24bit/i.test(process.env.COLORTERM || "");
|
|
26546
|
+
}
|
|
26547
|
+
function paint2(on) {
|
|
26548
|
+
const tc = on && truecolorEnabled2();
|
|
26549
|
+
const mk = (h, bold = false) => (s) => {
|
|
26550
|
+
if (!on)
|
|
26551
|
+
return s;
|
|
26552
|
+
const code = tc ? `38;2;${h.rgb[0]};${h.rgb[1]};${h.rgb[2]}` : h.basic;
|
|
26553
|
+
return `${ESC2}[${bold ? "1;" : ""}${code}m${s}${ESC2}[0m`;
|
|
26554
|
+
};
|
|
26555
|
+
return {
|
|
26556
|
+
bold: (s) => on ? `${ESC2}[1m${s}${ESC2}[0m` : s,
|
|
26557
|
+
dim: mk(HUES2.dim),
|
|
26558
|
+
guide: mk(HUES2.guide),
|
|
26559
|
+
pink: mk(HUES2.pink),
|
|
26560
|
+
pinkBold: mk(HUES2.pink, true),
|
|
26561
|
+
softPink: mk(HUES2.logoPink),
|
|
26562
|
+
warn: mk(HUES2.warn)
|
|
26563
|
+
};
|
|
26564
|
+
}
|
|
26565
|
+
function renderLogo2(colorize) {
|
|
26566
|
+
const pad = ".".repeat(LOGO_GRID2[0]?.length ?? 0);
|
|
26567
|
+
const rgb = (ch) => ch === "t" ? LOGO_TEAL2 : ch === "p" ? LOGO_PINK2 : null;
|
|
26568
|
+
const lines = [];
|
|
26569
|
+
for (let r = 0;r < LOGO_GRID2.length; r += 2) {
|
|
26570
|
+
const top = LOGO_GRID2[r];
|
|
26571
|
+
const bot = LOGO_GRID2[r + 1] ?? pad;
|
|
26572
|
+
let line = "";
|
|
26573
|
+
for (let x = 0;x < top.length; x++) {
|
|
26574
|
+
const t = rgb(top[x]);
|
|
26575
|
+
const b = rgb(bot[x]);
|
|
26576
|
+
if (!t && !b) {
|
|
26577
|
+
line += " ";
|
|
26578
|
+
} else if (!colorize) {
|
|
26579
|
+
line += t && b ? "█" : t ? "▀" : "▄";
|
|
26580
|
+
} else if (t && b) {
|
|
26581
|
+
line += t === b ? `${ESC2}[38;2;${t.join(";")}m█${ESC2}[0m` : `${ESC2}[38;2;${t.join(";")};48;2;${b.join(";")}m▀${ESC2}[0m`;
|
|
26582
|
+
} else if (t) {
|
|
26583
|
+
line += `${ESC2}[38;2;${t.join(";")}m▀${ESC2}[0m`;
|
|
26584
|
+
} else {
|
|
26585
|
+
line += `${ESC2}[38;2;${b.join(";")}m▄${ESC2}[0m`;
|
|
26586
|
+
}
|
|
26587
|
+
}
|
|
26588
|
+
lines.push(line);
|
|
26589
|
+
}
|
|
26590
|
+
return lines;
|
|
26591
|
+
}
|
|
26592
|
+
function truncate2(line, width) {
|
|
26593
|
+
let visual = 0;
|
|
26594
|
+
let out = "";
|
|
26595
|
+
let i = 0;
|
|
26596
|
+
while (i < line.length) {
|
|
26597
|
+
if (line[i] === ESC2 && line[i + 1] === "[") {
|
|
26598
|
+
let j = i + 2;
|
|
26599
|
+
while (j < line.length && !/[A-Za-z]/.test(line[j]))
|
|
26600
|
+
j++;
|
|
26601
|
+
j++;
|
|
26602
|
+
out += line.slice(i, j);
|
|
26603
|
+
i = j;
|
|
26604
|
+
} else {
|
|
26605
|
+
if (visual >= width)
|
|
26606
|
+
break;
|
|
26607
|
+
out += line[i];
|
|
26608
|
+
visual++;
|
|
26609
|
+
i++;
|
|
26610
|
+
}
|
|
26611
|
+
}
|
|
26612
|
+
return out;
|
|
26613
|
+
}
|
|
26614
|
+
function ellipsize2(text, width) {
|
|
26615
|
+
if (width <= 0)
|
|
26616
|
+
return "";
|
|
26617
|
+
if (text.length <= width)
|
|
26618
|
+
return text;
|
|
26619
|
+
return text.slice(0, width - 1).trimEnd() + "…";
|
|
26620
|
+
}
|
|
26621
|
+
function summarize2(labels, noun = "selected") {
|
|
26622
|
+
if (labels.length === 0)
|
|
26623
|
+
return "none";
|
|
26624
|
+
if (labels.length <= 3)
|
|
26625
|
+
return labels.join(", ");
|
|
26626
|
+
const head = labels.slice(0, 3).join(", ");
|
|
26627
|
+
return `${labels.length} ${noun} · ${head} +${labels.length - 3}`;
|
|
26628
|
+
}
|
|
26629
|
+
function writeLines2(out, lines) {
|
|
26630
|
+
const cols = out.columns || 80;
|
|
26631
|
+
out.write(lines.map((l) => l === "" ? l : truncate2(l, cols)).join(`
|
|
26632
|
+
`) + `
|
|
26633
|
+
`);
|
|
26634
|
+
}
|
|
26635
|
+
function renderBrandLogo2(stdout = process.stdout) {
|
|
26636
|
+
const c2 = paint2(colorsEnabled2(stdout));
|
|
26637
|
+
const cols = stdout.columns || 80;
|
|
26638
|
+
if (cols < LOGO_MIN_COLS2) {
|
|
26639
|
+
return [`${c2.guide(FLOWER2)} fa${c2.pink("il")}proof ai ${c2.dim("· " + TAGLINE2)}`];
|
|
26640
|
+
}
|
|
26641
|
+
const tc = colorsEnabled2(stdout) && truecolorEnabled2();
|
|
26642
|
+
const lines = renderLogo2(tc).map((l) => ` ${l}`);
|
|
26643
|
+
lines.push("");
|
|
26644
|
+
lines.push(` fa${c2.pink("il")}proof ai`);
|
|
26645
|
+
lines.push(` ${c2.dim(TAGLINE2)}`);
|
|
26646
|
+
return lines;
|
|
26647
|
+
}
|
|
26648
|
+
function intro2(message, stdout = process.stdout) {
|
|
26649
|
+
if (!stdout.isTTY)
|
|
26650
|
+
return;
|
|
26651
|
+
const c2 = paint2(colorsEnabled2(stdout));
|
|
26652
|
+
const lines = ["", ...renderBrandLogo2(stdout)];
|
|
26653
|
+
lines.push(c2.dim(BAR2));
|
|
26654
|
+
lines.push(`${c2.guide(STEP_ACTIVE2)} ${c2.bold(message)}`);
|
|
26655
|
+
writeLines2(stdout, lines);
|
|
26656
|
+
}
|
|
26657
|
+
function renderLaunchBanner2(version3, stdout = process.stdout) {
|
|
26658
|
+
const c2 = paint2(colorsEnabled2(stdout));
|
|
26659
|
+
const row = (label, value) => ` ${c2.guide(label.padEnd(9))}${value}`;
|
|
26660
|
+
const header = stdout.isTTY ? renderBrandLogo2(stdout) : [" failproof ai", ` ${TAGLINE2}`];
|
|
26661
|
+
return [
|
|
26662
|
+
"",
|
|
26663
|
+
...header,
|
|
26664
|
+
"",
|
|
26665
|
+
row("version", c2.pink(version3)),
|
|
26666
|
+
row("star", c2.dim("https://github.com/failproofai/failproofai")),
|
|
26667
|
+
row("docs", c2.dim("https://docs.befailproof.ai/introduction")),
|
|
26668
|
+
row("discord", c2.dim("https://discord.befailproof.ai/")),
|
|
26669
|
+
""
|
|
26670
|
+
];
|
|
26671
|
+
}
|
|
26672
|
+
function outro2(message, opts = {}, stdout = process.stdout) {
|
|
26673
|
+
const c2 = paint2(colorsEnabled2(stdout));
|
|
26674
|
+
const ok = opts.ok !== false;
|
|
26675
|
+
if (!stdout.isTTY) {
|
|
26676
|
+
stdout.write(message + `
|
|
26677
|
+
`);
|
|
26678
|
+
return;
|
|
26679
|
+
}
|
|
26680
|
+
const end = ok ? c2.pink(BAR_END2) : c2.dim(BAR_END2);
|
|
26681
|
+
const text = ok ? c2.pink(message) : c2.dim(message);
|
|
26682
|
+
writeLines2(stdout, [c2.dim(BAR2), `${end} ${text}`]);
|
|
26683
|
+
}
|
|
26684
|
+
function repaint2(out, region, lines) {
|
|
26685
|
+
if (region.lastCount > 0)
|
|
26686
|
+
out.write(`${ESC2}[${region.lastCount}A${ESC2}[J`);
|
|
26687
|
+
writeLines2(out, lines);
|
|
26688
|
+
region.lastCount = lines.length;
|
|
26689
|
+
}
|
|
26690
|
+
function hideCursor2(out) {
|
|
26691
|
+
out.write(`${ESC2}[?25l`);
|
|
26692
|
+
}
|
|
26693
|
+
function showCursor2(out) {
|
|
26694
|
+
out.write(`${ESC2}[?25h`);
|
|
26695
|
+
}
|
|
26696
|
+
function nameWidth2(labels) {
|
|
26697
|
+
return Math.min(24, Math.max(6, ...labels.map((l) => l.length)));
|
|
26698
|
+
}
|
|
26699
|
+
function displayRows2(choices) {
|
|
26700
|
+
const rows = [];
|
|
26701
|
+
let lastSection;
|
|
26702
|
+
choices.forEach((choice, index) => {
|
|
26703
|
+
if (choice.section && choice.section !== lastSection) {
|
|
26704
|
+
lastSection = choice.section;
|
|
26705
|
+
rows.push({ kind: "header", text: choice.section });
|
|
26706
|
+
}
|
|
26707
|
+
rows.push({ kind: "item", index });
|
|
26708
|
+
});
|
|
26709
|
+
return rows;
|
|
26710
|
+
}
|
|
26711
|
+
function viewport2(rows, cursorRow, window) {
|
|
26712
|
+
if (rows.length <= window)
|
|
26713
|
+
return { start: 0, end: rows.length };
|
|
26714
|
+
let start = cursorRow - Math.floor(window / 2);
|
|
26715
|
+
start = Math.max(0, Math.min(start, rows.length - window));
|
|
26716
|
+
return { start, end: start + window };
|
|
26717
|
+
}
|
|
26718
|
+
function runPrompt2(p) {
|
|
26719
|
+
const { stdin, stdout, c: c2, choices } = p;
|
|
26720
|
+
const region = { lastCount: 0 };
|
|
26721
|
+
const nameCol = nameWidth2(choices.map((ch) => ch.label));
|
|
26722
|
+
let cursor2 = 0;
|
|
26723
|
+
const build = () => {
|
|
26724
|
+
const cols = stdout.columns || 80;
|
|
26725
|
+
const lines = [c2.dim(BAR2), `${c2.guide(STEP_ACTIVE2)} ${c2.bold(p.message)}`];
|
|
26726
|
+
for (const b of p.body ?? [])
|
|
26727
|
+
lines.push(`${c2.dim(BAR2)} ${c2.dim(b)}`);
|
|
26728
|
+
const rows = displayRows2(choices);
|
|
26729
|
+
let cursorRow = 0;
|
|
26730
|
+
rows.forEach((r, ri) => {
|
|
26731
|
+
if (r.kind === "item" && r.index === cursor2)
|
|
26732
|
+
cursorRow = ri;
|
|
26733
|
+
});
|
|
26734
|
+
const { start, end } = viewport2(rows, cursorRow, WINDOW3);
|
|
26735
|
+
const above = rows.slice(0, start).filter((r) => r.kind === "item").length;
|
|
26736
|
+
const below = rows.slice(end).filter((r) => r.kind === "item").length;
|
|
26737
|
+
if (above > 0)
|
|
26738
|
+
lines.push(`${c2.dim(BAR2)} ${c2.dim(`↑ ${above} more`)}`);
|
|
26739
|
+
const budget = Math.max(6, cols - nameCol - 10);
|
|
26740
|
+
for (let ri = start;ri < end; ri++) {
|
|
26741
|
+
const row = rows[ri];
|
|
26742
|
+
if (row.kind === "header") {
|
|
26743
|
+
lines.push(`${c2.dim(BAR2)} ${c2.dim(row.text)}`);
|
|
26744
|
+
} else {
|
|
26745
|
+
lines.push(`${c2.dim(BAR2)} ${p.renderRow(row.index, row.index === cursor2, budget)}`);
|
|
26746
|
+
}
|
|
26747
|
+
}
|
|
26748
|
+
if (below > 0)
|
|
26749
|
+
lines.push(`${c2.dim(BAR2)} ${c2.dim(`↓ ${below} more`)}`);
|
|
26750
|
+
const warn = p.warnLine?.();
|
|
26751
|
+
if (warn)
|
|
26752
|
+
lines.push(`${c2.dim(BAR2)} ${warn}`);
|
|
26753
|
+
lines.push(`${c2.dim(BAR2)} ${c2.dim(p.footer)}`);
|
|
26754
|
+
return lines;
|
|
26755
|
+
};
|
|
26756
|
+
const collapse = (result) => {
|
|
26757
|
+
repaint2(stdout, region, [
|
|
26758
|
+
c2.dim(BAR2),
|
|
26759
|
+
`${c2.dim(STEP_DONE2)} ${p.message}`,
|
|
26760
|
+
`${c2.dim(BAR2)} ${c2.dim(p.summaryFor(result))}`
|
|
26761
|
+
]);
|
|
26762
|
+
};
|
|
26763
|
+
return new Promise((resolve24) => {
|
|
26764
|
+
hideCursor2(stdout);
|
|
26765
|
+
repaint2(stdout, region, build());
|
|
26766
|
+
readline3.emitKeypressEvents(stdin);
|
|
26767
|
+
const wasRaw = stdin.isRaw;
|
|
26768
|
+
stdin.setRawMode?.(true);
|
|
26769
|
+
stdin.resume();
|
|
26770
|
+
const cleanup = () => {
|
|
26771
|
+
stdin.removeListener("keypress", onKey);
|
|
26772
|
+
stdin.setRawMode?.(wasRaw ?? false);
|
|
26773
|
+
stdin.pause();
|
|
26774
|
+
showCursor2(stdout);
|
|
26775
|
+
};
|
|
26776
|
+
const finish = (result) => {
|
|
26777
|
+
cleanup();
|
|
26778
|
+
collapse(result);
|
|
26779
|
+
resolve24(result);
|
|
26780
|
+
};
|
|
26781
|
+
function onKey(_s, key) {
|
|
26782
|
+
if (!key)
|
|
26783
|
+
return;
|
|
26784
|
+
if (key.ctrl && (key.name === "c" || key.name === "d") || key.name === "escape") {
|
|
26785
|
+
finish(null);
|
|
26786
|
+
} else if (key.name === "up") {
|
|
26787
|
+
cursor2 = cursor2 > 0 ? cursor2 - 1 : choices.length - 1;
|
|
26788
|
+
repaint2(stdout, region, build());
|
|
26789
|
+
} else if (key.name === "down") {
|
|
26790
|
+
cursor2 = cursor2 < choices.length - 1 ? cursor2 + 1 : 0;
|
|
26791
|
+
repaint2(stdout, region, build());
|
|
26792
|
+
} else {
|
|
26793
|
+
const outcome = p.onKey(key, cursor2);
|
|
26794
|
+
if (outcome === "redraw")
|
|
26795
|
+
repaint2(stdout, region, build());
|
|
26796
|
+
else if (outcome)
|
|
26797
|
+
finish(outcome.done);
|
|
26798
|
+
}
|
|
26799
|
+
}
|
|
26800
|
+
stdin.on("keypress", onKey);
|
|
26801
|
+
});
|
|
26802
|
+
}
|
|
26803
|
+
function selectOne2(opts) {
|
|
26804
|
+
const stdin = opts.stdin ?? process.stdin;
|
|
26805
|
+
const stdout = opts.stdout ?? process.stdout;
|
|
26806
|
+
const choices = opts.choices;
|
|
26807
|
+
if (!choices.length)
|
|
26808
|
+
return Promise.resolve(null);
|
|
26809
|
+
if (!stdin.isTTY || !stdout.isTTY) {
|
|
26810
|
+
return Promise.resolve(choices[0].value);
|
|
26811
|
+
}
|
|
26812
|
+
const c2 = paint2(colorsEnabled2(stdout));
|
|
26813
|
+
const nameCol = nameWidth2(choices.map((ch) => ch.label));
|
|
26814
|
+
return runPrompt2({
|
|
26815
|
+
stdin,
|
|
26816
|
+
stdout,
|
|
26817
|
+
message: opts.message,
|
|
26818
|
+
c: c2,
|
|
26819
|
+
body: opts.body,
|
|
26820
|
+
choices,
|
|
26821
|
+
renderRow: (index, active, budget) => {
|
|
26822
|
+
const choice = choices[index];
|
|
26823
|
+
const dot = active ? c2.pink(RADIO_ON2) : c2.dim(RADIO_OFF2);
|
|
26824
|
+
const rawLabel = choice.label.padEnd(nameCol);
|
|
26825
|
+
const label = active ? c2.pinkBold(rawLabel) : rawLabel;
|
|
26826
|
+
const hint = choice.hint ? ` ${c2.dim(ellipsize2(choice.hint, budget))}` : "";
|
|
26827
|
+
return `${dot} ${label}${hint}`;
|
|
26828
|
+
},
|
|
26829
|
+
footer: "↑/↓ navigate · enter to select · esc to cancel",
|
|
26830
|
+
onKey: (key, cursor2) => key.name === "return" ? { done: choices[cursor2].value } : undefined,
|
|
26831
|
+
summaryFor: (value) => value === null ? "cancelled" : choices.find((ch) => ch.value === value)?.label ?? String(value)
|
|
26832
|
+
});
|
|
26833
|
+
}
|
|
26834
|
+
function multiSelect2(opts) {
|
|
26835
|
+
const stdin = opts.stdin ?? process.stdin;
|
|
26836
|
+
const stdout = opts.stdout ?? process.stdout;
|
|
26837
|
+
const choices = opts.choices;
|
|
26838
|
+
const minSelected = opts.minSelected ?? 0;
|
|
26839
|
+
const noun = opts.summaryNoun ?? "selected";
|
|
26840
|
+
const checked = choices.map((ch) => ch.locked ? ch.checked ?? true : !!ch.checked);
|
|
26841
|
+
if (!stdin.isTTY || !stdout.isTTY) {
|
|
26842
|
+
return Promise.resolve(choices.filter((_, i) => checked[i]).map((ch) => ch.value));
|
|
26843
|
+
}
|
|
26844
|
+
const c2 = paint2(colorsEnabled2(stdout));
|
|
26845
|
+
const nameCol = nameWidth2(choices.map((ch) => ch.label));
|
|
26846
|
+
let warn = false;
|
|
26847
|
+
return runPrompt2({
|
|
26848
|
+
stdin,
|
|
26849
|
+
stdout,
|
|
26850
|
+
message: opts.message,
|
|
26851
|
+
c: c2,
|
|
26852
|
+
choices,
|
|
26853
|
+
renderRow: (index, active, budget) => {
|
|
26854
|
+
const choice = choices[index];
|
|
26855
|
+
const caret = active ? c2.pink(CARET2) : " ";
|
|
26856
|
+
const box = choice.locked ? checked[index] ? c2.guide(CHECK_ON2) : c2.dim(CHECK_OFF2) : checked[index] ? c2.pink(CHECK_ON2) : c2.dim(CHECK_OFF2);
|
|
26857
|
+
const rawLabel = choice.label.padEnd(nameCol);
|
|
26858
|
+
const label = active ? c2.pinkBold(rawLabel) : checked[index] ? rawLabel : c2.dim(rawLabel);
|
|
26859
|
+
const hint = choice.hint ? ` ${c2.dim(ellipsize2(choice.hint, budget))}` : "";
|
|
26860
|
+
return `${caret} ${box} ${label}${hint}`;
|
|
26861
|
+
},
|
|
26862
|
+
warnLine: () => warn ? c2.warn(`Select at least ${minSelected}.`) : null,
|
|
26863
|
+
footer: opts.hint ?? "↑/↓ move · space select · ctrl+a all · enter confirm",
|
|
26864
|
+
onKey: (key, cursor2) => {
|
|
26865
|
+
if (key.name === "space") {
|
|
26866
|
+
if (choices[cursor2]?.locked)
|
|
26867
|
+
return "redraw";
|
|
26868
|
+
checked[cursor2] = !checked[cursor2];
|
|
26869
|
+
warn = false;
|
|
26870
|
+
return "redraw";
|
|
26871
|
+
}
|
|
26872
|
+
if (key.ctrl && key.name === "a") {
|
|
26873
|
+
const allOn = choices.every((ch, i) => checked[i] || ch.locked);
|
|
26874
|
+
for (let i = 0;i < checked.length; i++) {
|
|
26875
|
+
checked[i] = choices[i]?.locked ? true : !allOn;
|
|
26876
|
+
}
|
|
26877
|
+
return "redraw";
|
|
26878
|
+
}
|
|
26879
|
+
if (key.name === "return") {
|
|
26880
|
+
const selected = choices.filter((_, i) => checked[i]).map((ch) => ch.value);
|
|
26881
|
+
const chosen = choices.filter((ch, i) => checked[i] && !ch.locked).length;
|
|
26882
|
+
if (chosen < minSelected) {
|
|
26883
|
+
warn = true;
|
|
26884
|
+
return "redraw";
|
|
26885
|
+
}
|
|
26886
|
+
return { done: selected };
|
|
26887
|
+
}
|
|
26888
|
+
return;
|
|
26889
|
+
},
|
|
26890
|
+
summaryFor: (values) => values === null ? "cancelled" : summarize2(choices.filter((ch) => values.includes(ch.value) && !ch.summaryExclude).map((ch) => ch.label), noun)
|
|
26891
|
+
});
|
|
26892
|
+
}
|
|
26893
|
+
function promptText2(opts) {
|
|
26894
|
+
const stdin = opts.stdin ?? process.stdin;
|
|
26895
|
+
const stdout = opts.stdout ?? process.stdout;
|
|
26896
|
+
const c2 = paint2(colorsEnabled2(stdout));
|
|
26897
|
+
const accept = (raw) => {
|
|
26898
|
+
const value = raw.trim() || opts.defaultValue || "";
|
|
26899
|
+
const error = opts.validate?.(value) ?? null;
|
|
26900
|
+
return error ? { ok: false, error } : { ok: true, value };
|
|
26901
|
+
};
|
|
26902
|
+
if (!stdin.isTTY) {
|
|
26903
|
+
return new Promise((resolve24) => {
|
|
26904
|
+
let buf = "";
|
|
26905
|
+
const onData = (chunk) => {
|
|
26906
|
+
buf += String(chunk);
|
|
26907
|
+
if (buf.includes(`
|
|
26908
|
+
`))
|
|
26909
|
+
done();
|
|
26910
|
+
};
|
|
26911
|
+
const done = () => {
|
|
26912
|
+
stdin.removeListener("data", onData);
|
|
26913
|
+
stdin.removeListener("end", done);
|
|
26914
|
+
const r = accept(buf.split(`
|
|
26915
|
+
`)[0] ?? "");
|
|
26916
|
+
resolve24(r.ok ? r.value : null);
|
|
26917
|
+
};
|
|
26918
|
+
stdin.on("data", onData);
|
|
26919
|
+
stdin.on("end", done);
|
|
26920
|
+
});
|
|
26921
|
+
}
|
|
26922
|
+
return new Promise((resolve24) => {
|
|
26923
|
+
let value = "";
|
|
26924
|
+
const draw = (error) => {
|
|
26925
|
+
const shown = opts.mask ? "•".repeat(value.length) : value;
|
|
26926
|
+
const hint = opts.hint ? ` ${c2.dim(opts.hint)}` : "";
|
|
26927
|
+
const err = error ? `
|
|
26928
|
+
${c2.warn(error)}` : "";
|
|
26929
|
+
stdout.write(`\r\x1B[2K${c2.bold(opts.message)} ${shown}${hint}${err}`);
|
|
26930
|
+
if (err)
|
|
26931
|
+
stdout.write("\x1B[1A");
|
|
26932
|
+
};
|
|
26933
|
+
draw();
|
|
26934
|
+
readline3.emitKeypressEvents(stdin);
|
|
26935
|
+
const wasRaw = stdin.isRaw;
|
|
26936
|
+
stdin.setRawMode?.(true);
|
|
26937
|
+
stdin.resume();
|
|
26938
|
+
const cleanup = () => {
|
|
26939
|
+
stdin.removeListener("keypress", onKey);
|
|
26940
|
+
stdin.setRawMode?.(wasRaw ?? false);
|
|
26941
|
+
stdin.pause();
|
|
26942
|
+
stdout.write(`
|
|
26943
|
+
`);
|
|
26944
|
+
};
|
|
26945
|
+
function onKey(str2, key) {
|
|
26946
|
+
if (!key)
|
|
26947
|
+
return;
|
|
26948
|
+
if (key.ctrl && (key.name === "c" || key.name === "d") || key.name === "escape") {
|
|
26949
|
+
cleanup();
|
|
26950
|
+
resolve24(null);
|
|
26951
|
+
return;
|
|
26952
|
+
}
|
|
26953
|
+
if (key.name === "return" || key.name === "enter") {
|
|
26954
|
+
const r = accept(value);
|
|
26955
|
+
if (r.ok) {
|
|
26956
|
+
cleanup();
|
|
26957
|
+
resolve24(r.value);
|
|
26958
|
+
} else {
|
|
26959
|
+
draw(r.error);
|
|
26960
|
+
}
|
|
26961
|
+
return;
|
|
26962
|
+
}
|
|
26963
|
+
if (key.name === "backspace") {
|
|
26964
|
+
value = value.slice(0, -1);
|
|
26965
|
+
draw();
|
|
26966
|
+
return;
|
|
26967
|
+
}
|
|
26968
|
+
if (str2 && !key.ctrl && !key.meta) {
|
|
26969
|
+
value += str2;
|
|
26970
|
+
draw();
|
|
26971
|
+
}
|
|
26972
|
+
}
|
|
26973
|
+
stdin.on("keypress", onKey);
|
|
26974
|
+
});
|
|
26975
|
+
}
|
|
26976
|
+
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;
|
|
26977
|
+
var init_tui2 = __esm(() => {
|
|
26978
|
+
HUES2 = {
|
|
26979
|
+
guide: { rgb: [102, 209, 181], basic: "36" },
|
|
26980
|
+
pink: { rgb: [255, 46, 136], basic: "95" },
|
|
26981
|
+
logoPink: { rgb: [228, 88, 124], basic: "95" },
|
|
26982
|
+
warn: { rgb: [227, 179, 65], basic: "33" },
|
|
26983
|
+
dim: { rgb: [107, 118, 132], basic: "2" }
|
|
26984
|
+
};
|
|
26985
|
+
ANSI_RESET2 = `${ESC2}[0m`;
|
|
26986
|
+
ANSI_BOLD2 = `${ESC2}[1m`;
|
|
26987
|
+
ANSI_DIM2 = `${ESC2}[2m`;
|
|
26988
|
+
LOGO_GRID2 = [
|
|
26989
|
+
"...tt........",
|
|
26990
|
+
"..tttt.......",
|
|
26991
|
+
".tttttt..pppp",
|
|
26992
|
+
".tttttt..pppp",
|
|
26993
|
+
"..tttt...pppp",
|
|
26994
|
+
"...tt....pppp",
|
|
26995
|
+
".........pppp",
|
|
26996
|
+
".........pppp",
|
|
26997
|
+
"..pppp...pppp",
|
|
26998
|
+
"..pppp...pppp",
|
|
26999
|
+
"..pppp...pppp",
|
|
27000
|
+
".pppppp..pppp",
|
|
27001
|
+
".pppppp..pppp",
|
|
27002
|
+
".pppppp..pppp",
|
|
27003
|
+
".pppppp..pppp",
|
|
27004
|
+
"..pppp...pppp",
|
|
27005
|
+
"..ppppppppppp",
|
|
27006
|
+
"..ppppppppppp",
|
|
27007
|
+
"..ppppppppppp",
|
|
27008
|
+
"..ppppppppppp"
|
|
27009
|
+
];
|
|
27010
|
+
LOGO_TEAL2 = HUES2.guide.rgb;
|
|
27011
|
+
LOGO_PINK2 = HUES2.logoPink.rgb;
|
|
27012
|
+
});
|
|
27013
|
+
|
|
27014
|
+
// src/hooks/manager.ts
|
|
27015
|
+
var exports_manager = {};
|
|
27016
|
+
__export(exports_manager, {
|
|
27017
|
+
removeHooks: () => removeHooks2,
|
|
27018
|
+
listHooks: () => listHooks,
|
|
27019
|
+
installHooks: () => installHooks2,
|
|
27020
|
+
hooksInstalledInSettings: () => hooksInstalledInSettings2,
|
|
27021
|
+
getSettingsPath: () => getSettingsPath2
|
|
27022
|
+
});
|
|
27023
|
+
import { execSync as execSync7 } from "node:child_process";
|
|
27024
|
+
import { existsSync as existsSync32 } from "node:fs";
|
|
27025
|
+
import { resolve as resolve24, basename as basename6 } from "node:path";
|
|
27026
|
+
import { platform as platform5, arch as arch4, release as release3, hostname as hostname5 } from "node:os";
|
|
27027
|
+
function getSettingsPath2(scope, cwd) {
|
|
27028
|
+
return claudeCode.getSettingsPath(scope, cwd);
|
|
26016
27029
|
}
|
|
26017
27030
|
function scopeLabel2(scope) {
|
|
26018
27031
|
switch (scope) {
|
|
@@ -26119,7 +27132,7 @@ async function installHooksImpl2(policyNames, scope = "user", cwd, includeBeta =
|
|
|
26119
27132
|
} else if (customPoliciesPath && (typeof customPoliciesPath === "string" || customPoliciesPath.length > 0)) {
|
|
26120
27133
|
const incoming = (typeof customPoliciesPath === "string" ? [customPoliciesPath] : customPoliciesPath).map((path3) => resolve24(path3));
|
|
26121
27134
|
const carried = (previousConfig.customPoliciesPaths ?? (previousConfig.customPoliciesPath ? [previousConfig.customPoliciesPath] : [])).map((path3) => resolve24(path3)).filter((path3) => {
|
|
26122
|
-
if (
|
|
27135
|
+
if (existsSync32(path3))
|
|
26123
27136
|
return true;
|
|
26124
27137
|
console.log(`Dropping custom policies path (file no longer exists): ${path3}`);
|
|
26125
27138
|
return false;
|
|
@@ -26255,7 +27268,7 @@ This file can be committed to git — no machine-specific paths.`);
|
|
|
26255
27268
|
} catch {}
|
|
26256
27269
|
}
|
|
26257
27270
|
}
|
|
26258
|
-
async function
|
|
27271
|
+
async function removeHooks2(policyNames, scope = "user", cwd, opts) {
|
|
26259
27272
|
const configScope = scope === "all" ? "user" : scope;
|
|
26260
27273
|
const selectedClis = opts?.cli && opts.cli.length > 0 ? [...new Set(opts.cli)] : ["claude"];
|
|
26261
27274
|
if (opts?.removeCustomHooks) {
|
|
@@ -26311,7 +27324,7 @@ async function removeHooks(policyNames, scope = "user", cwd, opts) {
|
|
|
26311
27324
|
const scopesToRemove = scope === "all" ? [...integration.scopes] : integration.scopes.includes(scope) ? [scope] : [];
|
|
26312
27325
|
for (const s of scopesToRemove) {
|
|
26313
27326
|
const settingsPaths = settingsPathsFor(integration, s, cwd);
|
|
26314
|
-
const existing = settingsPaths.filter((p) =>
|
|
27327
|
+
const existing = settingsPaths.filter((p) => existsSync32(p));
|
|
26315
27328
|
if (existing.length === 0) {
|
|
26316
27329
|
if (scope !== "all" && selectedClis.length === 1) {
|
|
26317
27330
|
console.log("No settings file found. Nothing to remove.");
|
|
@@ -26512,7 +27525,7 @@ Failproof AI Hook Policies
|
|
|
26512
27525
|
for (const path3 of explicitPaths) {
|
|
26513
27526
|
const absPath = resolve24(findProjectConfigDir(cwd ?? process.cwd()), path3);
|
|
26514
27527
|
console.log(` ${absPath}`);
|
|
26515
|
-
if (!
|
|
27528
|
+
if (!existsSync32(absPath)) {
|
|
26516
27529
|
console.log(` \x1B[31m✗ File not found: ${absPath}\x1B[0m`);
|
|
26517
27530
|
continue;
|
|
26518
27531
|
}
|
|
@@ -26609,7 +27622,7 @@ __export(exports_install_prompt, {
|
|
|
26609
27622
|
promptPolicySelection: () => promptPolicySelection2,
|
|
26610
27623
|
buildCliMenuOptions: () => buildCliMenuOptions
|
|
26611
27624
|
});
|
|
26612
|
-
import * as
|
|
27625
|
+
import * as readline4 from "node:readline";
|
|
26613
27626
|
async function resolveTargetClis(explicit, action = "install") {
|
|
26614
27627
|
const detected = explicit && explicit.length > 0 ? [] : detectInstalledClis();
|
|
26615
27628
|
const stdinIsTty = !!process.stdin.isTTY;
|
|
@@ -26716,13 +27729,13 @@ async function promptCliTargetSelection(detected, action = "install") {
|
|
|
26716
27729
|
let cursor2 = 0;
|
|
26717
27730
|
let lastLineCount = 0;
|
|
26718
27731
|
let cursorHidden = false;
|
|
26719
|
-
function
|
|
27732
|
+
function hideCursor3() {
|
|
26720
27733
|
if (!cursorHidden) {
|
|
26721
27734
|
process.stdout.write("\x1B[?25l");
|
|
26722
27735
|
cursorHidden = true;
|
|
26723
27736
|
}
|
|
26724
27737
|
}
|
|
26725
|
-
function
|
|
27738
|
+
function showCursor3() {
|
|
26726
27739
|
if (cursorHidden) {
|
|
26727
27740
|
process.stdout.write("\x1B[?25h");
|
|
26728
27741
|
cursorHidden = false;
|
|
@@ -26732,7 +27745,7 @@ async function promptCliTargetSelection(detected, action = "install") {
|
|
|
26732
27745
|
const heading = action === "uninstall" ? "Remove failproofai hooks" : "Install failproofai hooks";
|
|
26733
27746
|
function render() {
|
|
26734
27747
|
const cols = process.stdout.columns || 100;
|
|
26735
|
-
|
|
27748
|
+
hideCursor3();
|
|
26736
27749
|
const g = dim(BAR);
|
|
26737
27750
|
const lines = [g, `${teal(STEP_ACTIVE)} ${bold(heading)}`];
|
|
26738
27751
|
for (const row of buildDisplayRows()) {
|
|
@@ -26765,13 +27778,13 @@ async function promptCliTargetSelection(detected, action = "install") {
|
|
|
26765
27778
|
const itemCount = options.length;
|
|
26766
27779
|
return new Promise((resolve25) => {
|
|
26767
27780
|
render();
|
|
26768
|
-
|
|
27781
|
+
readline4.emitKeypressEvents(process.stdin);
|
|
26769
27782
|
const wasRaw = process.stdin.isRaw;
|
|
26770
27783
|
if (process.stdin.setRawMode)
|
|
26771
27784
|
process.stdin.setRawMode(true);
|
|
26772
27785
|
process.stdin.resume();
|
|
26773
27786
|
function cleanup() {
|
|
26774
|
-
|
|
27787
|
+
showCursor3();
|
|
26775
27788
|
process.stdin.removeListener("keypress", onKey);
|
|
26776
27789
|
if (process.stdin.setRawMode)
|
|
26777
27790
|
process.stdin.setRawMode(wasRaw ?? false);
|
|
@@ -26832,13 +27845,13 @@ async function promptPolicySelection2(preSelected, options = {}) {
|
|
|
26832
27845
|
let search = "";
|
|
26833
27846
|
let lastLineCount = 0;
|
|
26834
27847
|
let cursorHidden = false;
|
|
26835
|
-
function
|
|
27848
|
+
function hideCursor3() {
|
|
26836
27849
|
if (!cursorHidden) {
|
|
26837
27850
|
process.stdout.write("\x1B[?25l");
|
|
26838
27851
|
cursorHidden = true;
|
|
26839
27852
|
}
|
|
26840
27853
|
}
|
|
26841
|
-
function
|
|
27854
|
+
function showCursor3() {
|
|
26842
27855
|
if (cursorHidden) {
|
|
26843
27856
|
process.stdout.write("\x1B[?25h");
|
|
26844
27857
|
cursorHidden = false;
|
|
@@ -26885,7 +27898,7 @@ async function promptPolicySelection2(preSelected, options = {}) {
|
|
|
26885
27898
|
}
|
|
26886
27899
|
function render() {
|
|
26887
27900
|
const cols = process.stdout.columns || 100;
|
|
26888
|
-
|
|
27901
|
+
hideCursor3();
|
|
26889
27902
|
const filtered = getFiltered();
|
|
26890
27903
|
const shown = filtered.length;
|
|
26891
27904
|
if (shown > 0 && cursor2 >= shown)
|
|
@@ -26904,10 +27917,10 @@ async function promptPolicySelection2(preSelected, options = {}) {
|
|
|
26904
27917
|
for (let i = 0;i < WINDOW_SIZE + 1; i++)
|
|
26905
27918
|
lines.push(g);
|
|
26906
27919
|
} else {
|
|
26907
|
-
const
|
|
27920
|
+
const displayRows3 = buildDisplayRows(filtered);
|
|
26908
27921
|
let cursorDisplayRow = 0;
|
|
26909
|
-
for (let i = 0;i <
|
|
26910
|
-
const row =
|
|
27922
|
+
for (let i = 0;i < displayRows3.length; i++) {
|
|
27923
|
+
const row = displayRows3[i];
|
|
26911
27924
|
if (row.kind === "item" && row.filteredIndex === cursor2) {
|
|
26912
27925
|
cursorDisplayRow = i;
|
|
26913
27926
|
break;
|
|
@@ -26915,12 +27928,12 @@ async function promptPolicySelection2(preSelected, options = {}) {
|
|
|
26915
27928
|
}
|
|
26916
27929
|
let windowStart = cursorDisplayRow - Math.floor(WINDOW_SIZE / 2);
|
|
26917
27930
|
windowStart = Math.max(0, windowStart);
|
|
26918
|
-
windowStart = Math.min(windowStart, Math.max(0,
|
|
26919
|
-
const windowEnd = Math.min(
|
|
26920
|
-
const aboveItems =
|
|
27931
|
+
windowStart = Math.min(windowStart, Math.max(0, displayRows3.length - WINDOW_SIZE));
|
|
27932
|
+
const windowEnd = Math.min(displayRows3.length, windowStart + WINDOW_SIZE);
|
|
27933
|
+
const aboveItems = displayRows3.slice(0, windowStart).filter((r) => r.kind === "item").length;
|
|
26921
27934
|
lines.push(aboveItems > 0 ? `${g} ${dim(`↑ ${aboveItems} more`)}` : g);
|
|
26922
27935
|
for (let i = windowStart;i < windowEnd; i++) {
|
|
26923
|
-
const row =
|
|
27936
|
+
const row = displayRows3[i];
|
|
26924
27937
|
if (row.kind === "header") {
|
|
26925
27938
|
lines.push(`${g} ${dim(row.category.toUpperCase())} ${dim(`${row.enabledCount}/${row.totalCount}`)}`);
|
|
26926
27939
|
} else {
|
|
@@ -26937,7 +27950,7 @@ async function promptPolicySelection2(preSelected, options = {}) {
|
|
|
26937
27950
|
}
|
|
26938
27951
|
for (let i = windowEnd - windowStart;i < WINDOW_SIZE; i++)
|
|
26939
27952
|
lines.push(g);
|
|
26940
|
-
const belowItems =
|
|
27953
|
+
const belowItems = displayRows3.slice(windowEnd).filter((r) => r.kind === "item").length;
|
|
26941
27954
|
lines.push(belowItems > 0 ? `${g} ${dim(`↓ ${belowItems} more`)}` : g);
|
|
26942
27955
|
}
|
|
26943
27956
|
lines.push(g);
|
|
@@ -26953,7 +27966,7 @@ async function promptPolicySelection2(preSelected, options = {}) {
|
|
|
26953
27966
|
render();
|
|
26954
27967
|
process.stdin.setRawMode(true);
|
|
26955
27968
|
process.stdin.resume();
|
|
26956
|
-
|
|
27969
|
+
readline4.emitKeypressEvents(process.stdin);
|
|
26957
27970
|
function keypressHandler(_str, key) {
|
|
26958
27971
|
if (!key)
|
|
26959
27972
|
return;
|
|
@@ -27009,7 +28022,7 @@ ${dim(BAR)} ${dim(`${selected.length} selected`)}
|
|
|
27009
28022
|
}
|
|
27010
28023
|
}
|
|
27011
28024
|
function cleanup() {
|
|
27012
|
-
|
|
28025
|
+
showCursor3();
|
|
27013
28026
|
process.stdin.removeListener("keypress", keypressHandler);
|
|
27014
28027
|
process.stdin.setRawMode(false);
|
|
27015
28028
|
process.stdin.pause();
|
|
@@ -27647,7 +28660,7 @@ __export(exports_launch2, {
|
|
|
27647
28660
|
launch: () => launch2
|
|
27648
28661
|
});
|
|
27649
28662
|
import { spawn as spawn3 } from "child_process";
|
|
27650
|
-
import { realpathSync as realpathSync5, existsSync as
|
|
28663
|
+
import { realpathSync as realpathSync5, existsSync as existsSync33 } from "node:fs";
|
|
27651
28664
|
import { resolve as resolve25, dirname as dirname16 } from "node:path";
|
|
27652
28665
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
27653
28666
|
import { createInterface as createInterface2 } from "node:readline";
|
|
@@ -27674,7 +28687,7 @@ function launch2(mode) {
|
|
|
27674
28687
|
cmd = "node";
|
|
27675
28688
|
const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve25(dirname16(realpathSync5(fileURLToPath3(import.meta.url))), "..");
|
|
27676
28689
|
const serverJsPath = resolve25(packageRoot, ".next/standalone/server.js");
|
|
27677
|
-
if (!
|
|
28690
|
+
if (!existsSync33(serverJsPath)) {
|
|
27678
28691
|
let shadowMessage = null;
|
|
27679
28692
|
try {
|
|
27680
28693
|
const diag = diagnoseShadow({ selfPackageRoot: packageRoot, selfVersion: version2 });
|
|
@@ -27774,7 +28787,7 @@ import { realpathSync as realpathSync6 } from "fs";
|
|
|
27774
28787
|
import { dirname as dirname17, resolve as resolve26 } from "path";
|
|
27775
28788
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
27776
28789
|
// package.json
|
|
27777
|
-
var version = "1.0.0-beta.
|
|
28790
|
+
var version = "1.0.0-beta.9";
|
|
27778
28791
|
|
|
27779
28792
|
// bin/failproofai.mjs
|
|
27780
28793
|
if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
|
|
@@ -27872,7 +28885,7 @@ if (hookIdx >= 0) {
|
|
|
27872
28885
|
}
|
|
27873
28886
|
}
|
|
27874
28887
|
async function runCli() {
|
|
27875
|
-
const SUBCOMMANDS = ["policies", "policy", "audit", "config"];
|
|
28888
|
+
const SUBCOMMANDS = ["policies", "policy", "audit", "config", "uninstall"];
|
|
27876
28889
|
if ((args.includes("--help") || args.includes("-h")) && !SUBCOMMANDS.includes(args[0])) {
|
|
27877
28890
|
const extraArgs = args.filter((a) => a !== "--help" && a !== "-h");
|
|
27878
28891
|
if (extraArgs.length > 0) {
|
|
@@ -27918,6 +28931,16 @@ COMMANDS
|
|
|
27918
28931
|
dashboard at http://localhost:8020/audit
|
|
27919
28932
|
audit --help, -h Show this help for the audit command
|
|
27920
28933
|
|
|
28934
|
+
uninstall Remove failproofai from this machine: hook
|
|
28935
|
+
entries from every agent CLI, and the daemon
|
|
28936
|
+
service. Run this BEFORE \`npm rm -g failproofai\`
|
|
28937
|
+
\u2014 npm runs no uninstall script, so removing the
|
|
28938
|
+
package alone leaves both behind.
|
|
28939
|
+
--purge Also delete ~/.failproofai (settings,
|
|
28940
|
+
credentials, audit history, daemon binary)
|
|
28941
|
+
--dry-run Show what would be removed, change nothing
|
|
28942
|
+
--yes, -y Skip the confirmation prompt
|
|
28943
|
+
|
|
27921
28944
|
--version, -v Print version and exit
|
|
27922
28945
|
--help, -h Show this help message
|
|
27923
28946
|
|
|
@@ -27995,6 +29018,84 @@ Run \`failproofai --help\` for usage.`);
|
|
|
27995
29018
|
await maybeFirstRunConfigure2({}, { postSetupAudit: args[0] !== "audit", force: layoutWasReset });
|
|
27996
29019
|
} catch {}
|
|
27997
29020
|
}
|
|
29021
|
+
if (args[0] === "uninstall") {
|
|
29022
|
+
const subArgs = args.slice(1);
|
|
29023
|
+
if (subArgs.includes("--help") || subArgs.includes("-h")) {
|
|
29024
|
+
console.log(`
|
|
29025
|
+
failproofai uninstall \u2014 remove failproofai from this machine
|
|
29026
|
+
|
|
29027
|
+
USAGE
|
|
29028
|
+
failproofai uninstall [--purge] [--dry-run] [--yes]
|
|
29029
|
+
|
|
29030
|
+
WHAT IT REMOVES
|
|
29031
|
+
\u2022 failproofai hook entries from every agent CLI that has them
|
|
29032
|
+
\u2022 the failproofaid daemon service (needs sudo)
|
|
29033
|
+
\u2022 the "require the daemon" flag \u2014 cleared FIRST, so a partial uninstall can
|
|
29034
|
+
never leave this machine denying every tool call
|
|
29035
|
+
|
|
29036
|
+
OPTIONS
|
|
29037
|
+
--purge Also delete ~/.failproofai \u2014 settings, credentials, audit
|
|
29038
|
+
history and the downloaded daemon binary. Off by default so a
|
|
29039
|
+
reinstall keeps your history.
|
|
29040
|
+
--dry-run Print what would be removed and change nothing.
|
|
29041
|
+
--yes, -y Skip the confirmation prompt. Required when there is no TTY.
|
|
29042
|
+
|
|
29043
|
+
WHY THIS EXISTS
|
|
29044
|
+
npm runs no uninstall script, so \`npm rm -g failproofai\` removes the package
|
|
29045
|
+
and leaves the hook entries and the service behind. Run this first, then:
|
|
29046
|
+
npm rm -g failproofai
|
|
29047
|
+
`);
|
|
29048
|
+
process.exit(0);
|
|
29049
|
+
}
|
|
29050
|
+
const KNOWN = new Set(["--purge", "--dry-run", "--yes", "-y"]);
|
|
29051
|
+
const unknown = subArgs.find((a) => !KNOWN.has(a));
|
|
29052
|
+
if (unknown) {
|
|
29053
|
+
throw new CliError3(`Unexpected argument: ${unknown}
|
|
29054
|
+
Run \`failproofai uninstall --help\` for usage.`);
|
|
29055
|
+
}
|
|
29056
|
+
lastSubcommand = "uninstall_command";
|
|
29057
|
+
const { runUninstallCommand: runUninstallCommand2 } = await Promise.resolve().then(() => (init_uninstall_cli(), exports_uninstall_cli));
|
|
29058
|
+
const purge = subArgs.includes("--purge");
|
|
29059
|
+
const dryRun = subArgs.includes("--dry-run");
|
|
29060
|
+
const yes = subArgs.includes("--yes") || subArgs.includes("-y");
|
|
29061
|
+
let planWasShown = false;
|
|
29062
|
+
const result = await runUninstallCommand2({
|
|
29063
|
+
purge,
|
|
29064
|
+
dryRun,
|
|
29065
|
+
yes,
|
|
29066
|
+
cwd: process.cwd(),
|
|
29067
|
+
confirm: process.stdin.isTTY ? async (planLines) => {
|
|
29068
|
+
planWasShown = true;
|
|
29069
|
+
const { selectOne: selectOne3 } = await Promise.resolve().then(() => (init_tui2(), exports_tui));
|
|
29070
|
+
const answer = await selectOne3({
|
|
29071
|
+
message: purge ? "Remove failproofai and DELETE ~/.failproofai?" : "Remove failproofai from this machine?",
|
|
29072
|
+
body: planLines,
|
|
29073
|
+
choices: [
|
|
29074
|
+
{ label: "No, cancel", value: false },
|
|
29075
|
+
{ label: purge ? "Yes, remove and purge" : "Yes, remove it", value: true }
|
|
29076
|
+
]
|
|
29077
|
+
});
|
|
29078
|
+
return answer === true;
|
|
29079
|
+
} : undefined
|
|
29080
|
+
});
|
|
29081
|
+
const skip = planWasShown ? result.planLines : 0;
|
|
29082
|
+
for (const line of result.lines.slice(skip)) {
|
|
29083
|
+
if (result.exitCode === 0)
|
|
29084
|
+
console.log(line);
|
|
29085
|
+
else
|
|
29086
|
+
console.error(line);
|
|
29087
|
+
}
|
|
29088
|
+
if (!result.purged) {
|
|
29089
|
+
await track("cli_uninstall_command", {
|
|
29090
|
+
ok: result.exitCode === 0,
|
|
29091
|
+
purge,
|
|
29092
|
+
dry_run: dryRun
|
|
29093
|
+
});
|
|
29094
|
+
}
|
|
29095
|
+
lastSubcommand = null;
|
|
29096
|
+
await exitAfterFlush(result.exitCode);
|
|
29097
|
+
return;
|
|
29098
|
+
}
|
|
27998
29099
|
if (args[0] === "policies") {
|
|
27999
29100
|
const subArgs = args.slice(1);
|
|
28000
29101
|
const isInstall = subArgs.includes("--install") || subArgs.includes("-i");
|
|
@@ -28124,7 +29225,7 @@ Run \`failproofai policies --help\` for usage.`);
|
|
|
28124
29225
|
}
|
|
28125
29226
|
if (isUninstall) {
|
|
28126
29227
|
lastSubcommand = "uninstall";
|
|
28127
|
-
const { removeHooks:
|
|
29228
|
+
const { removeHooks: removeHooks3 } = await Promise.resolve().then(() => (init_manager2(), exports_manager));
|
|
28128
29229
|
const { resolveTargetClis: resolveTargetClis2 } = await Promise.resolve().then(() => (init_install_prompt2(), exports_install_prompt));
|
|
28129
29230
|
const scopeIdx = subArgs.indexOf("--scope");
|
|
28130
29231
|
const scope = scopeIdx >= 0 ? subArgs[scopeIdx + 1] : "user";
|
|
@@ -28169,7 +29270,7 @@ Run \`failproofai policies --help\` for usage.`);
|
|
|
28169
29270
|
}
|
|
28170
29271
|
const policyNames = subArgs.filter((a, idx) => !a.startsWith("-") && !consumedIdxs.has(idx));
|
|
28171
29272
|
const cli = await resolveTargetClis2(cliFlagValues.length > 0 ? cliFlagValues : undefined, "uninstall");
|
|
28172
|
-
await
|
|
29273
|
+
await removeHooks3(policyNames.length > 0 ? policyNames : undefined, scope, undefined, { betaOnly, removeCustomHooks, cli });
|
|
28173
29274
|
await track("cli_uninstall_success", {
|
|
28174
29275
|
scope,
|
|
28175
29276
|
cli,
|
|
@@ -28301,8 +29402,8 @@ For multiple policies use \`failproofai policies --${action === "add" ? "install
|
|
|
28301
29402
|
include_beta: includeBeta
|
|
28302
29403
|
});
|
|
28303
29404
|
} else {
|
|
28304
|
-
const { removeHooks:
|
|
28305
|
-
await
|
|
29405
|
+
const { removeHooks: removeHooks3 } = await Promise.resolve().then(() => (init_manager2(), exports_manager));
|
|
29406
|
+
await removeHooks3([policyName], scope, undefined, { betaOnly: false, removeCustomHooks: false, cli });
|
|
28306
29407
|
await track("cli_policy_remove_success", {
|
|
28307
29408
|
scope,
|
|
28308
29409
|
cli,
|