failproofai 1.0.2-beta.5 → 1.0.2-beta.6
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 +6 -6
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- 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/_not-found/__PAGE__.segment.rsc +13 -13
- 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 +3 -3
- 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/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 +13 -13
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
- 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 +14 -14
- 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]__0o07qi9._.js +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1zkek3z._.js → [root-of-the-server]__1tg4pu6._.js} +1 -1
- package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +1 -1
- package/.next/standalone/.next/server/chunks/{_12moi5-._.js → _19uwam-._.js} +1 -1
- package/.next/standalone/.next/server/chunks/_1ek68ln._.js +7 -7
- package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +1 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013jr2b._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02npjtd._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__07gm3zl._.js +5 -5
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da85px._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ftmoxc._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0p-5p8u._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s740oi._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0stn0gx._.js → [root-of-the-server]__128se8m._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1p2otjt._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1km9-gn._.js → _0no96tu._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_1kesqs-._.js → _11fc-hb._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.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/app_settings_settings-client_tsx_20lq-mq._.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 +19 -19
- package/.next/standalone/.next/static/chunks/{0tyt-3a5rtzy8.js → 0931avh-06lj5.js} +1 -1
- package/.next/standalone/.next/static/chunks/{36-8i_vbkh85w.js → 0h-3zffoy-_ji.js} +1 -1
- package/.next/standalone/.next/static/chunks/{21r7iw3rmixf5.js → 0p84npzxdgpc5.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2hwc8e923g5df.js → 0sdgnflvw8kta.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2_27jraat038x.js → 0z7au3njlak0j.js} +1 -1
- package/.next/standalone/.next/static/chunks/{391tpdqcas7rq.js → 1id7o44jjegf0.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3_b73tc78jwsq.js → 276n-edifojbd.js} +2 -2
- package/.next/standalone/.next/static/chunks/{26lfjhb2kzzgb.js → 2jfskwd2u8o5r.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3jqm8ubpv3i-4.js → 2pxr7nr1nyotn.js} +1 -1
- package/.next/standalone/.next/static/chunks/{33np9nrh1mvj9.js → 3vu_7_eor1m-k.js} +1 -1
- package/.next/standalone/package.json +9 -9
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +93 -26
- package/dist/cli.mjs +791 -588
- package/dist/worker.mjs +2 -2
- package/package.json +9 -9
- package/src/audit/cli.ts +12 -0
- package/src/audit/index.ts +33 -2
- package/src/hooks/configure-wizard.ts +112 -18
- package/src/hooks/fp-reset.ts +1 -1
- package/src/hooks/handler.ts +13 -1
- package/src/hooks/manager.ts +19 -1
- package/src/hooks/onboarding-attempt.ts +4 -4
- package/src/hooks/pack-cli.ts +162 -21
- package/src/hooks/pack-store.ts +21 -8
- package/src/hooks/read-stdin.ts +78 -8
- /package/.next/standalone/.next/static/{59jsH11LoyXu9UBoHvgGM → mAYN9BOxWVFREnVp3yCHp}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{59jsH11LoyXu9UBoHvgGM → mAYN9BOxWVFREnVp3yCHp}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{59jsH11LoyXu9UBoHvgGM → mAYN9BOxWVFREnVp3yCHp}/_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.2-beta.
|
|
22
|
+
var version2 = "1.0.2-beta.6", package_default;
|
|
23
23
|
var init_package = __esm(() => {
|
|
24
24
|
package_default = {
|
|
25
25
|
name: "failproofai",
|
|
@@ -143,16 +143,16 @@ var init_package = __esm(() => {
|
|
|
143
143
|
sharp: "0.35.0"
|
|
144
144
|
},
|
|
145
145
|
optionalDependencies: {
|
|
146
|
-
"@failproofai/failproofaid-linux-x64": "1.0.2-beta.
|
|
147
|
-
"@failproofai/failproofaid-linux-arm64": "1.0.2-beta.
|
|
148
|
-
"@failproofai/failproofaid-darwin-x64": "1.0.2-beta.
|
|
149
|
-
"@failproofai/failproofaid-darwin-arm64": "1.0.2-beta.
|
|
146
|
+
"@failproofai/failproofaid-linux-x64": "1.0.2-beta.6",
|
|
147
|
+
"@failproofai/failproofaid-linux-arm64": "1.0.2-beta.6",
|
|
148
|
+
"@failproofai/failproofaid-darwin-x64": "1.0.2-beta.6",
|
|
149
|
+
"@failproofai/failproofaid-darwin-arm64": "1.0.2-beta.6"
|
|
150
150
|
},
|
|
151
151
|
failproofaidBinaries: {
|
|
152
|
-
"linux-x64": "
|
|
153
|
-
"linux-arm64": "
|
|
154
|
-
"darwin-x64": "
|
|
155
|
-
"darwin-arm64": "
|
|
152
|
+
"linux-x64": "1e883d50ea40dad46394a137fab9b8ecddb7b37064312af9ddf89cb3de5eb7b8",
|
|
153
|
+
"linux-arm64": "47a51a75a01c021417a4d67fb7eb5caa8fa9c46abca5ec85d23e8ddead1ebfdd",
|
|
154
|
+
"darwin-x64": "bbc4e714c1e961d53d7dd6c65b84dcc74b4e24439349ebdef3f7d8681ece1776",
|
|
155
|
+
"darwin-arm64": "ca6f6e688070a2d2a63fd333f7c3ac7c7d2bd991ca499cfced23b249e7ca684b"
|
|
156
156
|
}
|
|
157
157
|
};
|
|
158
158
|
});
|
|
@@ -937,35 +937,50 @@ var exports_read_stdin = {};
|
|
|
937
937
|
__export(exports_read_stdin, {
|
|
938
938
|
readStdinPayload: () => readStdinPayload
|
|
939
939
|
});
|
|
940
|
-
async function readStdinPayload(maxBytes = 1048576) {
|
|
940
|
+
async function readStdinPayload(maxBytes = 1048576, timeoutMs = STDIN_TIMEOUT_MS, stream = process.stdin) {
|
|
941
941
|
let payload = "";
|
|
942
942
|
let oversized = false;
|
|
943
943
|
let readError = false;
|
|
944
|
+
let timedOut = false;
|
|
945
|
+
if (stream.isTTY) {
|
|
946
|
+
return { payload: "", oversized: false, readError: false, timedOut: false };
|
|
947
|
+
}
|
|
948
|
+
let timer;
|
|
944
949
|
try {
|
|
945
950
|
payload = await new Promise((resolve2, reject) => {
|
|
946
951
|
const chunks = [];
|
|
947
952
|
let totalBytes = 0;
|
|
948
|
-
|
|
949
|
-
|
|
953
|
+
stream.setEncoding("utf8");
|
|
954
|
+
stream.on("data", (chunk) => {
|
|
950
955
|
totalBytes += Buffer.byteLength(chunk);
|
|
951
956
|
if (totalBytes > maxBytes) {
|
|
952
957
|
oversized = true;
|
|
953
|
-
|
|
958
|
+
stream.destroy?.();
|
|
954
959
|
resolve2("");
|
|
955
960
|
return;
|
|
956
961
|
}
|
|
957
962
|
chunks.push(chunk);
|
|
958
963
|
});
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
if (
|
|
964
|
+
stream.on("end", () => resolve2(chunks.join("")));
|
|
965
|
+
stream.on("error", reject);
|
|
966
|
+
if (stream.readableEnded)
|
|
962
967
|
resolve2("");
|
|
968
|
+
timer = setTimeout(() => {
|
|
969
|
+
timedOut = true;
|
|
970
|
+
stream.destroy?.();
|
|
971
|
+
resolve2("");
|
|
972
|
+
}, timeoutMs);
|
|
973
|
+
timer.unref?.();
|
|
963
974
|
});
|
|
964
975
|
} catch {
|
|
965
976
|
readError = true;
|
|
977
|
+
} finally {
|
|
978
|
+
if (timer)
|
|
979
|
+
clearTimeout(timer);
|
|
966
980
|
}
|
|
967
|
-
return { payload, oversized, readError };
|
|
981
|
+
return { payload, oversized, readError, timedOut };
|
|
968
982
|
}
|
|
983
|
+
var STDIN_TIMEOUT_MS = 1e4;
|
|
969
984
|
|
|
970
985
|
// src/hooks/types.ts
|
|
971
986
|
var HOOK_SCOPES, INTEGRATION_TYPES, CODEX_HOOK_SCOPES, CODEX_HOOK_EVENT_TYPES, CODEX_EVENT_MAP, CODEX_TOOL_MAP, HERMES_TOOL_MAP, HERMES_TOOL_INPUT_MAP, HERMES_HOOK_SCOPES, HERMES_HOOK_EVENT_TYPES, HERMES_EVENT_MAP, COPILOT_HOOK_SCOPES, COPILOT_HOOK_EVENT_TYPES, COPILOT_TOOL_MAP, COPILOT_TOOL_INPUT_MAP, CURSOR_HOOK_SCOPES, CURSOR_HOOK_EVENT_TYPES, CURSOR_EVENT_MAP, CURSOR_TOOL_MAP, OPENCODE_HOOK_SCOPES, OPENCODE_HOOK_EVENT_TYPES, OPENCODE_TOOL_MAP, OPENCODE_TOOL_INPUT_MAP, PI_HOOK_SCOPES, PI_HOOK_EVENT_TYPES, PI_EVENT_MAP, PI_TOOL_MAP, PI_TOOL_INPUT_MAP, OPENCLAW_HOOK_SCOPES, OPENCLAW_HOOK_EVENT_TYPES, OPENCLAW_EVENT_MAP, OPENCLAW_TOOL_MAP, OPENCLAW_TOOL_INPUT_MAP, FACTORY_HOOK_SCOPES, FACTORY_HOOK_EVENT_TYPES, FACTORY_TOOL_MAP, DEVIN_HOOK_SCOPES, DEVIN_HOOK_EVENT_TYPES, DEVIN_TOOL_MAP, ANTIGRAVITY_HOOK_SCOPES, ANTIGRAVITY_HOOK_EVENT_TYPES, ANTIGRAVITY_EVENT_MAP, ANTIGRAVITY_TOOL_MAP, ANTIGRAVITY_TOOL_INPUT_MAP, GOOSE_HOOK_SCOPES, GOOSE_HOOK_EVENT_TYPES, GOOSE_TOOL_MAP, GOOSE_TOOL_INPUT_MAP, HOOK_EVENT_TYPES, CLAUDE_INSTALL_EVENT_TYPES, FAILPROOFAI_HOOK_MARKER = "__failproofai_hook__";
|
|
@@ -9934,35 +9949,50 @@ var init_resolve_transcript_path = __esm(() => {
|
|
|
9934
9949
|
});
|
|
9935
9950
|
|
|
9936
9951
|
// src/hooks/read-stdin.ts
|
|
9937
|
-
async function readStdinPayload2(maxBytes = 1048576) {
|
|
9952
|
+
async function readStdinPayload2(maxBytes = 1048576, timeoutMs = STDIN_TIMEOUT_MS2, stream = process.stdin) {
|
|
9938
9953
|
let payload = "";
|
|
9939
9954
|
let oversized = false;
|
|
9940
9955
|
let readError = false;
|
|
9956
|
+
let timedOut = false;
|
|
9957
|
+
if (stream.isTTY) {
|
|
9958
|
+
return { payload: "", oversized: false, readError: false, timedOut: false };
|
|
9959
|
+
}
|
|
9960
|
+
let timer;
|
|
9941
9961
|
try {
|
|
9942
9962
|
payload = await new Promise((resolve11, reject) => {
|
|
9943
9963
|
const chunks = [];
|
|
9944
9964
|
let totalBytes = 0;
|
|
9945
|
-
|
|
9946
|
-
|
|
9965
|
+
stream.setEncoding("utf8");
|
|
9966
|
+
stream.on("data", (chunk) => {
|
|
9947
9967
|
totalBytes += Buffer.byteLength(chunk);
|
|
9948
9968
|
if (totalBytes > maxBytes) {
|
|
9949
9969
|
oversized = true;
|
|
9950
|
-
|
|
9970
|
+
stream.destroy?.();
|
|
9951
9971
|
resolve11("");
|
|
9952
9972
|
return;
|
|
9953
9973
|
}
|
|
9954
9974
|
chunks.push(chunk);
|
|
9955
9975
|
});
|
|
9956
|
-
|
|
9957
|
-
|
|
9958
|
-
if (
|
|
9976
|
+
stream.on("end", () => resolve11(chunks.join("")));
|
|
9977
|
+
stream.on("error", reject);
|
|
9978
|
+
if (stream.readableEnded)
|
|
9979
|
+
resolve11("");
|
|
9980
|
+
timer = setTimeout(() => {
|
|
9981
|
+
timedOut = true;
|
|
9982
|
+
stream.destroy?.();
|
|
9959
9983
|
resolve11("");
|
|
9984
|
+
}, timeoutMs);
|
|
9985
|
+
timer.unref?.();
|
|
9960
9986
|
});
|
|
9961
9987
|
} catch {
|
|
9962
9988
|
readError = true;
|
|
9989
|
+
} finally {
|
|
9990
|
+
if (timer)
|
|
9991
|
+
clearTimeout(timer);
|
|
9963
9992
|
}
|
|
9964
|
-
return { payload, oversized, readError };
|
|
9993
|
+
return { payload, oversized, readError, timedOut };
|
|
9965
9994
|
}
|
|
9995
|
+
var STDIN_TIMEOUT_MS2 = 1e4;
|
|
9966
9996
|
|
|
9967
9997
|
// src/hooks/cloud-managed-policies.ts
|
|
9968
9998
|
import { createHash as createHash3 } from "node:crypto";
|
|
@@ -12009,7 +12039,7 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
|
|
|
12009
12039
|
const legacyNames = activePause || packsInstalledHere ? [] : config.enabledPolicies;
|
|
12010
12040
|
registerBuiltinPolicies(activePause ? [] : legacyNames);
|
|
12011
12041
|
if (legacyNames.length > 0) {
|
|
12012
|
-
hookLogWarn(`enforcing ${legacyNames.length} policies from this build because no pack is installed — ` + `run \`failproofai policies add
|
|
12042
|
+
hookLogWarn(`enforcing ${legacyNames.length} policies from this build because no pack is installed — ` + `run \`failproofai policies add FailproofAI/policies\` to move them into a real pack`);
|
|
12013
12043
|
}
|
|
12014
12044
|
const enabledBuiltinNames = new Set(legacyNames);
|
|
12015
12045
|
let cloudManagedPolicies = [];
|
|
@@ -12251,6 +12281,14 @@ async function handleHookEvent(eventType, cli = "claude") {
|
|
|
12251
12281
|
error_type: "oversized"
|
|
12252
12282
|
});
|
|
12253
12283
|
}
|
|
12284
|
+
if (stdinRead.timedOut) {
|
|
12285
|
+
hookLogWarn(`stdin never closed for ${eventType}; evaluated nothing`);
|
|
12286
|
+
trackHookEvent2(getInstanceId2(), "hook_stdin_error", {
|
|
12287
|
+
event_type: eventType,
|
|
12288
|
+
cli,
|
|
12289
|
+
error_type: "timeout"
|
|
12290
|
+
});
|
|
12291
|
+
}
|
|
12254
12292
|
const result = await evaluateHookEvent(eventType, cli, stdinRead.payload);
|
|
12255
12293
|
try {
|
|
12256
12294
|
if (canonicalizeEventType(eventType, cli) === "SessionStart") {
|
|
@@ -14555,6 +14593,426 @@ var init_first_run_gate = __esm(() => {
|
|
|
14555
14593
|
];
|
|
14556
14594
|
});
|
|
14557
14595
|
|
|
14596
|
+
// src/hooks/pack-store.ts
|
|
14597
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
14598
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync16, readFileSync as readFileSync21, renameSync as renameSync8, rmSync as rmSync9, writeFileSync as writeFileSync14 } from "node:fs";
|
|
14599
|
+
import { dirname as dirname13, resolve as resolve20 } from "node:path";
|
|
14600
|
+
function baseUrl2() {
|
|
14601
|
+
return (process.env.FAILPROOFAI_PACK_BASE_URL || DEFAULT_BASE_URL2).replace(/\/+$/, "");
|
|
14602
|
+
}
|
|
14603
|
+
function parsePackSpec(source) {
|
|
14604
|
+
const raw = source.trim();
|
|
14605
|
+
if (raw.length === 0)
|
|
14606
|
+
throw new Error("pack source is empty");
|
|
14607
|
+
const url = raw.match(/^(?:https?:\/\/)?(?:www\.)?github\.com\/(.+)$/i);
|
|
14608
|
+
if (url) {
|
|
14609
|
+
const parts = url[1].replace(/\/+$/, "").split("/");
|
|
14610
|
+
const [owner, repo, ...rest] = parts;
|
|
14611
|
+
let tag2 = null;
|
|
14612
|
+
if (rest[0] === "releases" && (rest[1] === "tag" || rest[1] === "download")) {
|
|
14613
|
+
const tail = rest.slice(2);
|
|
14614
|
+
tag2 = (rest[1] === "download" ? tail.slice(0, -1) : tail).join("/") || null;
|
|
14615
|
+
} else if (rest[0] === "releases" && rest[1] === "latest") {
|
|
14616
|
+
tag2 = null;
|
|
14617
|
+
}
|
|
14618
|
+
return validated(owner, repo, tag2);
|
|
14619
|
+
}
|
|
14620
|
+
const withoutScheme = raw.replace(/^github:/i, "");
|
|
14621
|
+
const at2 = withoutScheme.lastIndexOf("@");
|
|
14622
|
+
const repoPart = at2 > 0 ? withoutScheme.slice(0, at2) : withoutScheme;
|
|
14623
|
+
const tag = at2 > 0 ? withoutScheme.slice(at2 + 1) : null;
|
|
14624
|
+
const slash = repoPart.indexOf("/");
|
|
14625
|
+
if (slash <= 0 || slash === repoPart.length - 1) {
|
|
14626
|
+
if (RETIRED_CORE_ALIASES.has(repoPart.trim().toLowerCase())) {
|
|
14627
|
+
throw new Error(`${JSON.stringify(source)} is no longer a pack name — ours is a pack like anyone else's now. Use ${CORE_SOURCE} instead.`);
|
|
14628
|
+
}
|
|
14629
|
+
throw new Error(`pack source must be owner/repo, owner/repo@tag, or a github.com URL — got ${JSON.stringify(source)}`);
|
|
14630
|
+
}
|
|
14631
|
+
return validated(repoPart.slice(0, slash), repoPart.slice(slash + 1), tag);
|
|
14632
|
+
}
|
|
14633
|
+
function validated(owner, repo, tag) {
|
|
14634
|
+
if (!owner || !OWNER_RE.test(owner))
|
|
14635
|
+
throw new Error(`unsafe owner ${JSON.stringify(owner)}`);
|
|
14636
|
+
if (!repo || !OWNER_RE.test(repo))
|
|
14637
|
+
throw new Error(`unsafe repo ${JSON.stringify(repo)}`);
|
|
14638
|
+
if (tag !== null && !TAG_RE.test(tag))
|
|
14639
|
+
throw new Error(`unsafe tag ${JSON.stringify(tag)}`);
|
|
14640
|
+
return { owner, repo, tag };
|
|
14641
|
+
}
|
|
14642
|
+
async function resolveLatestTag(spec) {
|
|
14643
|
+
const url = `${baseUrl2()}/${spec.owner}/${spec.repo}/releases/latest`;
|
|
14644
|
+
const response = await fetch(url, {
|
|
14645
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
14646
|
+
redirect: "manual"
|
|
14647
|
+
});
|
|
14648
|
+
const location = response.headers.get("location");
|
|
14649
|
+
if (!location) {
|
|
14650
|
+
throw new Error(`could not resolve the newest release of ${spec.owner}/${spec.repo} ` + `(GET ${url} returned ${response.status} with no redirect). That redirect is issued only ` + `for a published, non-prerelease release, so either the repository has no releases at all ` + `or every release it has is a prerelease or a draft. Name a tag explicitly.`);
|
|
14651
|
+
}
|
|
14652
|
+
const match = location.match(/\/releases\/tag\/(.+)$/);
|
|
14653
|
+
if (!match)
|
|
14654
|
+
throw new Error(`unexpected redirect from ${url}: ${location}`);
|
|
14655
|
+
const tag = decodeURIComponent(match[1]).replace(/\/+$/, "");
|
|
14656
|
+
if (!TAG_RE.test(tag))
|
|
14657
|
+
throw new Error(`unsafe tag ${JSON.stringify(tag)} from ${url}`);
|
|
14658
|
+
return tag;
|
|
14659
|
+
}
|
|
14660
|
+
function formatPackSpec(spec) {
|
|
14661
|
+
return `github:${spec.owner}/${spec.repo}@${spec.tag}`;
|
|
14662
|
+
}
|
|
14663
|
+
function packAssetUrl(spec, asset) {
|
|
14664
|
+
return `${baseUrl2()}/${spec.owner}/${spec.repo}/releases/download/${spec.tag}/${asset}`;
|
|
14665
|
+
}
|
|
14666
|
+
function packTagMatchesVersion(tag, version3) {
|
|
14667
|
+
const tail = tag.slice(tag.lastIndexOf("/") + 1);
|
|
14668
|
+
return tail === version3 || tail === `v${version3}`;
|
|
14669
|
+
}
|
|
14670
|
+
async function fetchBytes2(url) {
|
|
14671
|
+
const response = await fetch(url, {
|
|
14672
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
14673
|
+
redirect: "follow"
|
|
14674
|
+
});
|
|
14675
|
+
if (!response.ok)
|
|
14676
|
+
throw new Error(`GET ${url} returned ${response.status}`);
|
|
14677
|
+
const contentLength = response.headers.get("content-length");
|
|
14678
|
+
if (contentLength !== null) {
|
|
14679
|
+
const declared = Number(contentLength);
|
|
14680
|
+
if (Number.isFinite(declared) && declared > MAX_ARTIFACT_BYTES) {
|
|
14681
|
+
await response.body?.cancel();
|
|
14682
|
+
throw new Error(`${url} declares ${declared} bytes, over the ${MAX_ARTIFACT_BYTES} limit`);
|
|
14683
|
+
}
|
|
14684
|
+
}
|
|
14685
|
+
if (!response.body)
|
|
14686
|
+
return Buffer.alloc(0);
|
|
14687
|
+
const chunks = [];
|
|
14688
|
+
const reader = response.body.getReader();
|
|
14689
|
+
let total = 0;
|
|
14690
|
+
try {
|
|
14691
|
+
while (true) {
|
|
14692
|
+
const { done, value } = await reader.read();
|
|
14693
|
+
if (done)
|
|
14694
|
+
break;
|
|
14695
|
+
total += value.byteLength;
|
|
14696
|
+
if (total > MAX_ARTIFACT_BYTES) {
|
|
14697
|
+
await reader.cancel();
|
|
14698
|
+
throw new Error(`${url} is over the ${MAX_ARTIFACT_BYTES} byte limit`);
|
|
14699
|
+
}
|
|
14700
|
+
chunks.push(Buffer.from(value));
|
|
14701
|
+
}
|
|
14702
|
+
} finally {
|
|
14703
|
+
reader.releaseLock();
|
|
14704
|
+
}
|
|
14705
|
+
return Buffer.concat(chunks, total);
|
|
14706
|
+
}
|
|
14707
|
+
function digestFor2(manifest, assetName) {
|
|
14708
|
+
for (const line of manifest.split(`
|
|
14709
|
+
`)) {
|
|
14710
|
+
const match = line.trim().match(/^([0-9a-f]{64})\s+\*?(.+)$/i);
|
|
14711
|
+
if (match && match[2].trim() === assetName)
|
|
14712
|
+
return match[1].toLowerCase();
|
|
14713
|
+
}
|
|
14714
|
+
return null;
|
|
14715
|
+
}
|
|
14716
|
+
function sha256(bytes) {
|
|
14717
|
+
return createHash7("sha256").update(bytes).digest("hex");
|
|
14718
|
+
}
|
|
14719
|
+
function writeAtomic(target, bytes) {
|
|
14720
|
+
const tempPath = `${target}.${process.pid}.tmp`;
|
|
14721
|
+
try {
|
|
14722
|
+
mkdirSync16(dirname13(target), { recursive: true });
|
|
14723
|
+
writeFileSync14(tempPath, bytes);
|
|
14724
|
+
renameSync8(tempPath, target);
|
|
14725
|
+
} catch (err) {
|
|
14726
|
+
rmSync9(tempPath, { force: true });
|
|
14727
|
+
throw err;
|
|
14728
|
+
}
|
|
14729
|
+
}
|
|
14730
|
+
async function fetchPackPreview(source) {
|
|
14731
|
+
if (process.env.FAILPROOFAI_NO_DOWNLOAD === "1") {
|
|
14732
|
+
throw new Error("FAILPROOFAI_NO_DOWNLOAD is set, so nothing can be fetched to preview");
|
|
14733
|
+
}
|
|
14734
|
+
const parsed = parsePackSpec(source);
|
|
14735
|
+
const resolvedFromLatest = parsed.tag === null;
|
|
14736
|
+
const spec = parsed.tag !== null ? { ...parsed, tag: parsed.tag } : { ...parsed, tag: await resolveLatestTag(parsed) };
|
|
14737
|
+
const checksums = (await fetchBytes2(packAssetUrl(spec, PACK_CHECKSUMS_ASSET))).toString("utf8");
|
|
14738
|
+
const manifestBytes = await fetchBytes2(packAssetUrl(spec, PACK_MANIFEST_ASSET));
|
|
14739
|
+
const manifestDigest = digestFor2(checksums, PACK_MANIFEST_ASSET);
|
|
14740
|
+
if (!manifestDigest) {
|
|
14741
|
+
throw new Error(`${PACK_CHECKSUMS_ASSET} has no entry for ${PACK_MANIFEST_ASSET}`);
|
|
14742
|
+
}
|
|
14743
|
+
if (sha256(manifestBytes) !== manifestDigest) {
|
|
14744
|
+
throw new Error(`${PACK_MANIFEST_ASSET} failed integrity verification`);
|
|
14745
|
+
}
|
|
14746
|
+
let raw;
|
|
14747
|
+
try {
|
|
14748
|
+
raw = JSON.parse(manifestBytes.toString("utf8"));
|
|
14749
|
+
} catch (err) {
|
|
14750
|
+
throw new Error(`${PACK_MANIFEST_ASSET} is not valid JSON: ${errText2(err)}`);
|
|
14751
|
+
}
|
|
14752
|
+
if (!raw || typeof raw !== "object")
|
|
14753
|
+
throw new Error(`${PACK_MANIFEST_ASSET} is not an object`);
|
|
14754
|
+
const value = raw;
|
|
14755
|
+
const identity = parsePackIdentity(value);
|
|
14756
|
+
if (!Array.isArray(value.policies) || value.policies.length === 0) {
|
|
14757
|
+
throw new Error("pack manifest declares no policies");
|
|
14758
|
+
}
|
|
14759
|
+
return {
|
|
14760
|
+
id: identity.id,
|
|
14761
|
+
version: identity.version,
|
|
14762
|
+
effect: identity.effect,
|
|
14763
|
+
policies: value.policies.map((policy, i) => parsePackPolicy(identity.id, policy, i)),
|
|
14764
|
+
source: formatPackSpec(spec),
|
|
14765
|
+
resolvedFromLatest
|
|
14766
|
+
};
|
|
14767
|
+
}
|
|
14768
|
+
function priorRecordFor(id, sha2562) {
|
|
14769
|
+
const installed = readInstalledPacks().packs;
|
|
14770
|
+
return installed.find((p) => p.id === id) ?? installed.find((p) => p.sha256 === sha2562);
|
|
14771
|
+
}
|
|
14772
|
+
async function fetchPack(spec) {
|
|
14773
|
+
const checksums = (await fetchBytes2(packAssetUrl(spec, PACK_CHECKSUMS_ASSET))).toString("utf8");
|
|
14774
|
+
const manifestBytes = await fetchBytes2(packAssetUrl(spec, PACK_MANIFEST_ASSET));
|
|
14775
|
+
const manifestDigest = digestFor2(checksums, PACK_MANIFEST_ASSET);
|
|
14776
|
+
if (!manifestDigest)
|
|
14777
|
+
throw new Error(`${PACK_CHECKSUMS_ASSET} has no entry for ${PACK_MANIFEST_ASSET}`);
|
|
14778
|
+
if (sha256(manifestBytes) !== manifestDigest) {
|
|
14779
|
+
throw new Error(`${PACK_MANIFEST_ASSET} failed integrity verification`);
|
|
14780
|
+
}
|
|
14781
|
+
const artifact = await fetchBytes2(packAssetUrl(spec, PACK_ENTRY_ASSET));
|
|
14782
|
+
const entryDigest = digestFor2(checksums, PACK_ENTRY_ASSET);
|
|
14783
|
+
if (!entryDigest)
|
|
14784
|
+
throw new Error(`${PACK_CHECKSUMS_ASSET} has no entry for ${PACK_ENTRY_ASSET}`);
|
|
14785
|
+
const artifactDigest = sha256(artifact);
|
|
14786
|
+
if (artifactDigest !== entryDigest) {
|
|
14787
|
+
throw new Error(`${PACK_ENTRY_ASSET} failed integrity verification`);
|
|
14788
|
+
}
|
|
14789
|
+
let parsed;
|
|
14790
|
+
try {
|
|
14791
|
+
parsed = JSON.parse(manifestBytes.toString("utf8"));
|
|
14792
|
+
} catch (err) {
|
|
14793
|
+
throw new Error(`${PACK_MANIFEST_ASSET} is not valid JSON: ${errText2(err)}`);
|
|
14794
|
+
}
|
|
14795
|
+
if (!parsed || typeof parsed !== "object")
|
|
14796
|
+
throw new Error(`${PACK_MANIFEST_ASSET} is not an object`);
|
|
14797
|
+
const raw = parsed;
|
|
14798
|
+
const identity = parsePackIdentity(raw);
|
|
14799
|
+
if (!Array.isArray(raw.policies) || raw.policies.length === 0) {
|
|
14800
|
+
throw new Error("pack manifest declares no policies");
|
|
14801
|
+
}
|
|
14802
|
+
const policies = raw.policies.map((p, i) => parsePackPolicy(identity.id, p, i));
|
|
14803
|
+
return {
|
|
14804
|
+
id: identity.id,
|
|
14805
|
+
version: identity.version,
|
|
14806
|
+
policies,
|
|
14807
|
+
...raw.effect !== undefined ? { effect: identity.effect } : {},
|
|
14808
|
+
artifact,
|
|
14809
|
+
artifactDigest
|
|
14810
|
+
};
|
|
14811
|
+
}
|
|
14812
|
+
function slugifyCategory(category) {
|
|
14813
|
+
return category.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
14814
|
+
}
|
|
14815
|
+
function resolveSelection(policies, opts, previous, previouslyInstalled) {
|
|
14816
|
+
const available = policies.map((p) => p.name);
|
|
14817
|
+
if (opts?.all)
|
|
14818
|
+
return { enabled: null, reason: "all" };
|
|
14819
|
+
const picked = new Set;
|
|
14820
|
+
if (opts?.categories?.length) {
|
|
14821
|
+
const wanted = new Set(opts.categories.map(slugifyCategory));
|
|
14822
|
+
const known = new Map(policies.map((p) => [slugifyCategory(p.category), p.category]));
|
|
14823
|
+
const unknown = [...wanted].filter((c) => !known.has(c));
|
|
14824
|
+
if (unknown.length > 0) {
|
|
14825
|
+
throw new Error(`no such categor${unknown.length === 1 ? "y" : "ies"}: ${unknown.join(", ")} ` + `(has: ${[...new Set(policies.map((p) => slugifyCategory(p.category)))].join(", ")})`);
|
|
14826
|
+
}
|
|
14827
|
+
for (const p of policies)
|
|
14828
|
+
if (wanted.has(slugifyCategory(p.category)))
|
|
14829
|
+
picked.add(p.name);
|
|
14830
|
+
}
|
|
14831
|
+
if (opts?.only?.length) {
|
|
14832
|
+
const unknown = opts.only.filter((n) => !available.includes(n));
|
|
14833
|
+
if (unknown.length > 0) {
|
|
14834
|
+
throw new Error(`pack does not contain ${unknown.join(", ")}`);
|
|
14835
|
+
}
|
|
14836
|
+
for (const n of opts.only)
|
|
14837
|
+
picked.add(n);
|
|
14838
|
+
}
|
|
14839
|
+
if (opts?.only !== undefined || opts?.categories?.length) {
|
|
14840
|
+
return { enabled: available.filter((n) => picked.has(n)), reason: "selected" };
|
|
14841
|
+
}
|
|
14842
|
+
if (previouslyInstalled) {
|
|
14843
|
+
return { enabled: previous === null ? null : previous ?? null, reason: "carried" };
|
|
14844
|
+
}
|
|
14845
|
+
return { enabled: policies.filter((p) => p.defaultEnabled).map((p) => p.name), reason: "defaults" };
|
|
14846
|
+
}
|
|
14847
|
+
async function verifyArtifactRegisters(artifactPath, declared) {
|
|
14848
|
+
const { loadCustomHooks: loadCustomHooks2 } = await Promise.resolve().then(() => (init_custom_hooks_loader(), exports_custom_hooks_loader));
|
|
14849
|
+
let registered;
|
|
14850
|
+
try {
|
|
14851
|
+
registered = (await loadCustomHooks2(artifactPath, { strict: true })).map((h) => h.name);
|
|
14852
|
+
} catch (err) {
|
|
14853
|
+
throw new Error(`its artifact could not be loaded: ${err instanceof Error ? err.message : String(err)}`);
|
|
14854
|
+
}
|
|
14855
|
+
const missing = declared.filter((name) => !registered.includes(name));
|
|
14856
|
+
const extra = registered.filter((name) => !declared.includes(name));
|
|
14857
|
+
if (missing.length > 0) {
|
|
14858
|
+
throw new Error(`its manifest declares ${missing.join(", ")} but the artifact does not register ${missing.length === 1 ? "it" : "them"} — that would install as protection that never runs`);
|
|
14859
|
+
}
|
|
14860
|
+
if (extra.length > 0) {
|
|
14861
|
+
throw new Error(`its artifact registers undeclared ${extra.join(", ")} — a policy that enforces but ` + `appears in no listing cannot be reviewed or switched off`);
|
|
14862
|
+
}
|
|
14863
|
+
}
|
|
14864
|
+
async function checkPackArtifact(artifactPath, declared) {
|
|
14865
|
+
try {
|
|
14866
|
+
await verifyArtifactRegisters(artifactPath, declared);
|
|
14867
|
+
return null;
|
|
14868
|
+
} catch (err) {
|
|
14869
|
+
return err instanceof Error ? err.message : String(err);
|
|
14870
|
+
}
|
|
14871
|
+
}
|
|
14872
|
+
async function addPack(source, opts) {
|
|
14873
|
+
if (process.env.FAILPROOFAI_NO_DOWNLOAD) {
|
|
14874
|
+
throw new Error("pack downloads are disabled (FAILPROOFAI_NO_DOWNLOAD). Already-installed packs keep enforcing.");
|
|
14875
|
+
}
|
|
14876
|
+
const parsed = parsePackSpec(source);
|
|
14877
|
+
const spec = parsed.tag ? { ...parsed, tag: parsed.tag } : { ...parsed, tag: await resolveLatestTag(parsed) };
|
|
14878
|
+
const resolvedFromLatest = parsed.tag === null;
|
|
14879
|
+
const fetched = await fetchPack(spec);
|
|
14880
|
+
if (!packTagMatchesVersion(spec.tag, fetched.version)) {
|
|
14881
|
+
const fromTag = spec.tag.slice(spec.tag.lastIndexOf("/") + 1).replace(/^v/, "");
|
|
14882
|
+
const fix = `re-tag the release ${fetched.version} (a leading v is fine), or rebuild the pack with --version ${fromTag}`;
|
|
14883
|
+
throw new Error(`${fetched.id} was not installed: release tag ${spec.tag} does not match the version its manifest declares (${fetched.version}). ` + (resolvedFromLatest ? `That is the newest release of ${spec.owner}/${spec.repo}, so the release and the pack ` + `disagree at the source — ask the publisher to ${fix}.` : `Name the tag whose manifest says ${fetched.version}, or fix the pack: ${fix}.`));
|
|
14884
|
+
}
|
|
14885
|
+
const available = fetched.policies.map((p) => p.name);
|
|
14886
|
+
const prior = priorRecordFor(fetched.id, fetched.artifactDigest);
|
|
14887
|
+
const { enabled, reason } = resolveSelection(fetched.policies, opts, prior?.enabled ? prior.enabled.filter((n) => available.includes(n)) : prior?.enabled, Boolean(prior));
|
|
14888
|
+
const root = packsRoot();
|
|
14889
|
+
const artifactRel = `artifacts/${fetched.artifactDigest}.mjs`;
|
|
14890
|
+
const artifactAbs = resolve20(root, artifactRel);
|
|
14891
|
+
if (!existsSync25(artifactAbs))
|
|
14892
|
+
writeAtomic(artifactAbs, fetched.artifact);
|
|
14893
|
+
try {
|
|
14894
|
+
await verifyArtifactRegisters(artifactAbs, available);
|
|
14895
|
+
} catch (err) {
|
|
14896
|
+
throw new Error(`${fetched.id}@${fetched.version} was not installed: ${err instanceof Error ? err.message : String(err)}`);
|
|
14897
|
+
}
|
|
14898
|
+
const repoOf = (source2) => {
|
|
14899
|
+
const at2 = source2.lastIndexOf("@");
|
|
14900
|
+
return at2 > source2.indexOf(":") ? source2.slice(0, at2) : source2;
|
|
14901
|
+
};
|
|
14902
|
+
if (prior && !prior.source.startsWith("bundled:") && repoOf(prior.source) !== repoOf(formatPackSpec(spec))) {
|
|
14903
|
+
throw new Error(`pack id ${fetched.id} is already installed from ${prior.source}. Refusing to replace it with ${formatPackSpec(spec)} — remove it first if that is what you mean.`);
|
|
14904
|
+
}
|
|
14905
|
+
const record = {
|
|
14906
|
+
id: fetched.id,
|
|
14907
|
+
version: fetched.version,
|
|
14908
|
+
source: formatPackSpec(spec),
|
|
14909
|
+
entry: artifactRel,
|
|
14910
|
+
sha256: fetched.artifactDigest,
|
|
14911
|
+
policies: fetched.policies,
|
|
14912
|
+
...fetched.effect ? { effect: fetched.effect } : {},
|
|
14913
|
+
...enabled ? { enabled } : {},
|
|
14914
|
+
...opts?.clis ? { clis: opts.clis } : {}
|
|
14915
|
+
};
|
|
14916
|
+
const absorbed = upsertInstalled(record);
|
|
14917
|
+
return {
|
|
14918
|
+
replaced: absorbed,
|
|
14919
|
+
id: fetched.id,
|
|
14920
|
+
version: fetched.version,
|
|
14921
|
+
source: record.source,
|
|
14922
|
+
tag: spec.tag,
|
|
14923
|
+
resolvedFromLatest,
|
|
14924
|
+
enabled: enabled ?? available,
|
|
14925
|
+
available,
|
|
14926
|
+
selection: reason,
|
|
14927
|
+
categories: [...new Set(fetched.policies.map((p) => slugifyCategory(p.category)))],
|
|
14928
|
+
artifact: artifactAbs
|
|
14929
|
+
};
|
|
14930
|
+
}
|
|
14931
|
+
function setPackPolicyEnabled(packId, name, on) {
|
|
14932
|
+
const manifestPath = resolve20(packsRoot(), "installed.json");
|
|
14933
|
+
if (!existsSync25(manifestPath))
|
|
14934
|
+
return { ok: false, reason: "no packs are installed" };
|
|
14935
|
+
let raw;
|
|
14936
|
+
try {
|
|
14937
|
+
raw = JSON.parse(readFileSync21(manifestPath, "utf8"));
|
|
14938
|
+
} catch {
|
|
14939
|
+
return { ok: false, reason: `pack manifest at ${manifestPath} is unreadable` };
|
|
14940
|
+
}
|
|
14941
|
+
const packs = Array.isArray(raw.packs) ? raw.packs : [];
|
|
14942
|
+
const pack = packs.find((p) => p?.id === packId);
|
|
14943
|
+
if (!pack)
|
|
14944
|
+
return { ok: false, reason: `no installed pack with id ${packId}` };
|
|
14945
|
+
const declared = Array.isArray(pack.policies) ? pack.policies : [];
|
|
14946
|
+
const available = declared.map((p) => typeof p?.name === "string" ? p.name : null).filter((n) => n !== null);
|
|
14947
|
+
if (!available.includes(name)) {
|
|
14948
|
+
return { ok: false, reason: `pack ${packId} declares no policy named ${name}` };
|
|
14949
|
+
}
|
|
14950
|
+
const current = pack.enabled ?? available;
|
|
14951
|
+
const next = on ? [...new Set([...current, name])] : current.filter((n) => n !== name);
|
|
14952
|
+
pack.enabled = available.filter((n) => next.includes(n));
|
|
14953
|
+
writeAtomic(manifestPath, JSON.stringify({ schemaVersion: 1, packs }, null, 2) + `
|
|
14954
|
+
`);
|
|
14955
|
+
return { ok: true };
|
|
14956
|
+
}
|
|
14957
|
+
function removePack(id) {
|
|
14958
|
+
const manifestPath = resolve20(packsRoot(), "installed.json");
|
|
14959
|
+
if (!existsSync25(manifestPath))
|
|
14960
|
+
return false;
|
|
14961
|
+
let raw;
|
|
14962
|
+
try {
|
|
14963
|
+
raw = JSON.parse(readFileSync21(manifestPath, "utf8"));
|
|
14964
|
+
} catch {
|
|
14965
|
+
return false;
|
|
14966
|
+
}
|
|
14967
|
+
const packs = Array.isArray(raw.packs) ? raw.packs : [];
|
|
14968
|
+
const remaining = packs.filter((p) => p?.id !== id);
|
|
14969
|
+
if (remaining.length === packs.length)
|
|
14970
|
+
return false;
|
|
14971
|
+
writeAtomic(manifestPath, JSON.stringify({ schemaVersion: 1, packs: remaining }, null, 2) + `
|
|
14972
|
+
`);
|
|
14973
|
+
return true;
|
|
14974
|
+
}
|
|
14975
|
+
function upsertInstalled(record) {
|
|
14976
|
+
const manifestPath = resolve20(packsRoot(), "installed.json");
|
|
14977
|
+
let packs = [];
|
|
14978
|
+
if (existsSync25(manifestPath)) {
|
|
14979
|
+
try {
|
|
14980
|
+
const raw = JSON.parse(readFileSync21(manifestPath, "utf8"));
|
|
14981
|
+
if (Array.isArray(raw.packs))
|
|
14982
|
+
packs = raw.packs;
|
|
14983
|
+
} catch {
|
|
14984
|
+
throw new Error(`existing pack manifest at ${manifestPath} is unreadable; move it aside and re-add`);
|
|
14985
|
+
}
|
|
14986
|
+
}
|
|
14987
|
+
const absorbed = packs.filter((p) => p?.sha256 === record.sha256 && p?.id !== record.id).map((p) => p.id);
|
|
14988
|
+
if (!record.enabled) {
|
|
14989
|
+
const prior = packs.find((p) => absorbed.includes(p?.id));
|
|
14990
|
+
const carried = prior?.enabled?.filter((n) => record.policies?.some((rp) => rp?.name === n));
|
|
14991
|
+
if (carried && carried.length > 0)
|
|
14992
|
+
record.enabled = carried;
|
|
14993
|
+
}
|
|
14994
|
+
const remaining = packs.filter((p) => !absorbed.includes(p?.id));
|
|
14995
|
+
const idx = remaining.findIndex((p) => p?.id === record.id);
|
|
14996
|
+
if (idx >= 0)
|
|
14997
|
+
remaining[idx] = record;
|
|
14998
|
+
else
|
|
14999
|
+
remaining.push(record);
|
|
15000
|
+
writeAtomic(manifestPath, JSON.stringify({ schemaVersion: 1, packs: remaining }, null, 2) + `
|
|
15001
|
+
`);
|
|
15002
|
+
return absorbed;
|
|
15003
|
+
}
|
|
15004
|
+
function errText2(err) {
|
|
15005
|
+
return err instanceof Error ? err.message : String(err);
|
|
15006
|
+
}
|
|
15007
|
+
var DEFAULT_BASE_URL2 = "https://github.com", FETCH_TIMEOUT_MS = 30000, MAX_ARTIFACT_BYTES, PACK_MANIFEST_ASSET = "failproofai-pack.json", PACK_ENTRY_ASSET = "failproofai-pack.mjs", PACK_CHECKSUMS_ASSET = "SHA256SUMS", OWNER_RE, TAG_RE, CORE_SOURCE = "FailproofAI/policies", RETIRED_CORE_ALIASES;
|
|
15008
|
+
var init_pack_store = __esm(() => {
|
|
15009
|
+
init_pack_manifest();
|
|
15010
|
+
MAX_ARTIFACT_BYTES = 8 * 1024 * 1024;
|
|
15011
|
+
OWNER_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
15012
|
+
TAG_RE = /^[A-Za-z0-9][A-Za-z0-9._/-]{0,127}$/;
|
|
15013
|
+
RETIRED_CORE_ALIASES = new Set(["core", "failproofai", "official"]);
|
|
15014
|
+
});
|
|
15015
|
+
|
|
14558
15016
|
// src/hooks/tui.ts
|
|
14559
15017
|
import * as readline2 from "node:readline";
|
|
14560
15018
|
function colorsEnabled2(out) {
|
|
@@ -14824,7 +15282,7 @@ function runPrompt2(p) {
|
|
|
14824
15282
|
`${c.dim(BAR2)} ${c.dim(summary)}`
|
|
14825
15283
|
]);
|
|
14826
15284
|
};
|
|
14827
|
-
return new Promise((
|
|
15285
|
+
return new Promise((resolve21) => {
|
|
14828
15286
|
hideCursor2(stdout);
|
|
14829
15287
|
repaint2(stdout, region, build());
|
|
14830
15288
|
readline2.emitKeypressEvents(stdin);
|
|
@@ -14840,7 +15298,7 @@ function runPrompt2(p) {
|
|
|
14840
15298
|
const finish = (result) => {
|
|
14841
15299
|
cleanup();
|
|
14842
15300
|
collapse(result);
|
|
14843
|
-
|
|
15301
|
+
resolve21(result);
|
|
14844
15302
|
};
|
|
14845
15303
|
function onKey(_s, key) {
|
|
14846
15304
|
if (!key)
|
|
@@ -14970,7 +15428,7 @@ function promptText2(opts) {
|
|
|
14970
15428
|
return error ? { ok: false, error } : { ok: true, value };
|
|
14971
15429
|
};
|
|
14972
15430
|
if (!stdin.isTTY) {
|
|
14973
|
-
return new Promise((
|
|
15431
|
+
return new Promise((resolve21) => {
|
|
14974
15432
|
let buf = "";
|
|
14975
15433
|
const onData = (chunk) => {
|
|
14976
15434
|
buf += String(chunk);
|
|
@@ -14983,13 +15441,13 @@ function promptText2(opts) {
|
|
|
14983
15441
|
stdin.removeListener("end", done);
|
|
14984
15442
|
const r = accept(buf.split(`
|
|
14985
15443
|
`)[0] ?? "");
|
|
14986
|
-
|
|
15444
|
+
resolve21(r.ok ? r.value : null);
|
|
14987
15445
|
};
|
|
14988
15446
|
stdin.on("data", onData);
|
|
14989
15447
|
stdin.on("end", done);
|
|
14990
15448
|
});
|
|
14991
15449
|
}
|
|
14992
|
-
return new Promise((
|
|
15450
|
+
return new Promise((resolve21) => {
|
|
14993
15451
|
let value = "";
|
|
14994
15452
|
const draw = (error) => {
|
|
14995
15453
|
const cols = stdout.columns || 80;
|
|
@@ -15019,14 +15477,14 @@ ${opts.prefix ?? " "}${truncate2(c.warn(error), cols - 3)}` : "";
|
|
|
15019
15477
|
return;
|
|
15020
15478
|
if (key.ctrl && (key.name === "c" || key.name === "d") || key.name === "escape") {
|
|
15021
15479
|
cleanup();
|
|
15022
|
-
|
|
15480
|
+
resolve21(null);
|
|
15023
15481
|
return;
|
|
15024
15482
|
}
|
|
15025
15483
|
if (key.name === "return" || key.name === "enter") {
|
|
15026
15484
|
const r = accept(value);
|
|
15027
15485
|
if (r.ok) {
|
|
15028
15486
|
cleanup();
|
|
15029
|
-
|
|
15487
|
+
resolve21(r.value);
|
|
15030
15488
|
} else {
|
|
15031
15489
|
draw(r.error);
|
|
15032
15490
|
}
|
|
@@ -22376,31 +22834,31 @@ var init_dist = __esm(() => {
|
|
|
22376
22834
|
|
|
22377
22835
|
// src/hooks/integrations.ts
|
|
22378
22836
|
import { execSync as execSync5 } from "node:child_process";
|
|
22379
|
-
import { readFileSync as
|
|
22380
|
-
import { resolve as
|
|
22837
|
+
import { readFileSync as readFileSync22, writeFileSync as writeFileSync15, existsSync as existsSync26, mkdirSync as mkdirSync17, unlinkSync as unlinkSync3 } from "node:fs";
|
|
22838
|
+
import { resolve as resolve21, dirname as dirname14 } from "node:path";
|
|
22381
22839
|
import { fileURLToPath } from "node:url";
|
|
22382
22840
|
import { homedir as homedir23 } from "node:os";
|
|
22383
22841
|
function readJsonFile(path3) {
|
|
22384
|
-
if (!
|
|
22842
|
+
if (!existsSync26(path3))
|
|
22385
22843
|
return {};
|
|
22386
|
-
const raw =
|
|
22844
|
+
const raw = readFileSync22(path3, "utf8");
|
|
22387
22845
|
return JSON.parse(raw);
|
|
22388
22846
|
}
|
|
22389
22847
|
function writeJsonFile(path3, data) {
|
|
22390
|
-
|
|
22391
|
-
|
|
22848
|
+
mkdirSync17(dirname14(path3), { recursive: true });
|
|
22849
|
+
writeFileSync15(path3, JSON.stringify(data, null, 2) + `
|
|
22392
22850
|
`, "utf8");
|
|
22393
22851
|
}
|
|
22394
22852
|
function readYamlDoc(path3) {
|
|
22395
22853
|
try {
|
|
22396
|
-
return
|
|
22854
|
+
return existsSync26(path3) ? $parseDocument(readFileSync22(path3, "utf8")) : $parseDocument("");
|
|
22397
22855
|
} catch {
|
|
22398
22856
|
return $parseDocument("");
|
|
22399
22857
|
}
|
|
22400
22858
|
}
|
|
22401
22859
|
function writeYamlDoc(path3, doc) {
|
|
22402
|
-
|
|
22403
|
-
|
|
22860
|
+
mkdirSync17(dirname14(path3), { recursive: true });
|
|
22861
|
+
writeFileSync15(path3, doc.toString(), "utf8");
|
|
22404
22862
|
}
|
|
22405
22863
|
function isMarkedHook(hook) {
|
|
22406
22864
|
if (!hook || typeof hook !== "object")
|
|
@@ -22443,7 +22901,7 @@ function isMarkedCopilotHook(hook) {
|
|
|
22443
22901
|
return false;
|
|
22444
22902
|
}
|
|
22445
22903
|
function opencodePluginFilePath(settingsPath) {
|
|
22446
|
-
return
|
|
22904
|
+
return resolve21(dirname14(settingsPath), "plugins", "failproofai.mjs");
|
|
22447
22905
|
}
|
|
22448
22906
|
function buildOpenCodePluginShim(binaryPath, scope) {
|
|
22449
22907
|
const useNpx = scope === "project";
|
|
@@ -22658,8 +23116,8 @@ export default async function failproofaiPlugin({ client, directory }) {
|
|
|
22658
23116
|
function getPiExtensionPath() {
|
|
22659
23117
|
const fromEnv = process.env.FAILPROOFAI_PACKAGE_ROOT;
|
|
22660
23118
|
if (fromEnv)
|
|
22661
|
-
return
|
|
22662
|
-
return
|
|
23119
|
+
return resolve21(fromEnv, "pi-extension");
|
|
23120
|
+
return resolve21(fileURLToPath(import.meta.url), "..", "..", "..", "pi-extension");
|
|
22663
23121
|
}
|
|
22664
23122
|
function isFailproofaiPiEntry(source) {
|
|
22665
23123
|
if (typeof source !== "string")
|
|
@@ -22670,8 +23128,8 @@ function isFailproofaiPiEntry(source) {
|
|
|
22670
23128
|
}
|
|
22671
23129
|
function makePiProjectRelativeEntry(extPath) {
|
|
22672
23130
|
const cwd = process.cwd();
|
|
22673
|
-
const cwdResolved =
|
|
22674
|
-
const extResolved =
|
|
23131
|
+
const cwdResolved = resolve21(cwd);
|
|
23132
|
+
const extResolved = resolve21(extPath);
|
|
22675
23133
|
if (extResolved.startsWith(cwdResolved + "/") || extResolved === cwdResolved) {
|
|
22676
23134
|
const fromSettingsDir = "../" + extResolved.slice(cwdResolved.length + 1);
|
|
22677
23135
|
return fromSettingsDir;
|
|
@@ -22679,7 +23137,7 @@ function makePiProjectRelativeEntry(extPath) {
|
|
|
22679
23137
|
return extResolved;
|
|
22680
23138
|
}
|
|
22681
23139
|
function hermesConfigHasHooks(settingsPath) {
|
|
22682
|
-
if (!
|
|
23140
|
+
if (!existsSync26(settingsPath))
|
|
22683
23141
|
return false;
|
|
22684
23142
|
try {
|
|
22685
23143
|
const doc = readYamlDoc(settingsPath);
|
|
@@ -22695,13 +23153,13 @@ function hermesConfigHasHooks(settingsPath) {
|
|
|
22695
23153
|
return false;
|
|
22696
23154
|
}
|
|
22697
23155
|
function unhookedHermesProfiles() {
|
|
22698
|
-
return listHermesProfiles().filter((p) =>
|
|
23156
|
+
return listHermesProfiles().filter((p) => existsSync26(p.home)).filter((p) => !hermesConfigHasHooks(resolve21(p.home, "config.yaml"))).map((p) => p.name);
|
|
22699
23157
|
}
|
|
22700
23158
|
function getOpenClawPluginPath() {
|
|
22701
23159
|
const fromEnv = process.env.FAILPROOFAI_PACKAGE_ROOT;
|
|
22702
23160
|
if (fromEnv)
|
|
22703
|
-
return
|
|
22704
|
-
return
|
|
23161
|
+
return resolve21(fromEnv, "openclaw-plugin");
|
|
23162
|
+
return resolve21(fileURLToPath(import.meta.url), "..", "..", "..", "openclaw-plugin");
|
|
22705
23163
|
}
|
|
22706
23164
|
function isFailproofaiOpenClawPath(p) {
|
|
22707
23165
|
if (typeof p !== "string")
|
|
@@ -22738,14 +23196,14 @@ var init_integrations = __esm(() => {
|
|
|
22738
23196
|
scopes: HOOK_SCOPES,
|
|
22739
23197
|
eventTypes: CLAUDE_INSTALL_EVENT_TYPES,
|
|
22740
23198
|
getSettingsPath(scope, cwd) {
|
|
22741
|
-
const base = cwd ?
|
|
23199
|
+
const base = cwd ? resolve21(cwd) : process.cwd();
|
|
22742
23200
|
switch (scope) {
|
|
22743
23201
|
case "user":
|
|
22744
|
-
return
|
|
23202
|
+
return resolve21(homedir23(), ".claude", "settings.json");
|
|
22745
23203
|
case "project":
|
|
22746
|
-
return
|
|
23204
|
+
return resolve21(base, ".claude", "settings.json");
|
|
22747
23205
|
case "local":
|
|
22748
|
-
return
|
|
23206
|
+
return resolve21(base, ".claude", "settings.local.json");
|
|
22749
23207
|
}
|
|
22750
23208
|
},
|
|
22751
23209
|
readSettings(settingsPath) {
|
|
@@ -22841,7 +23299,7 @@ var init_integrations = __esm(() => {
|
|
|
22841
23299
|
},
|
|
22842
23300
|
hooksInstalledInSettings(scope, cwd) {
|
|
22843
23301
|
const settingsPath = this.getSettingsPath(scope, cwd);
|
|
22844
|
-
if (!
|
|
23302
|
+
if (!existsSync26(settingsPath))
|
|
22845
23303
|
return false;
|
|
22846
23304
|
try {
|
|
22847
23305
|
const settings = this.readSettings(settingsPath);
|
|
@@ -22870,14 +23328,14 @@ var init_integrations = __esm(() => {
|
|
|
22870
23328
|
scopes: CODEX_HOOK_SCOPES,
|
|
22871
23329
|
eventTypes: CODEX_HOOK_EVENT_TYPES,
|
|
22872
23330
|
getSettingsPath(scope, cwd) {
|
|
22873
|
-
const base = cwd ?
|
|
23331
|
+
const base = cwd ? resolve21(cwd) : process.cwd();
|
|
22874
23332
|
switch (scope) {
|
|
22875
23333
|
case "user":
|
|
22876
|
-
return
|
|
23334
|
+
return resolve21(homedir23(), ".codex", "hooks.json");
|
|
22877
23335
|
case "project":
|
|
22878
|
-
return
|
|
23336
|
+
return resolve21(base, ".codex", "hooks.json");
|
|
22879
23337
|
case "local":
|
|
22880
|
-
return
|
|
23338
|
+
return resolve21(base, ".codex", "hooks.json");
|
|
22881
23339
|
}
|
|
22882
23340
|
},
|
|
22883
23341
|
readSettings(settingsPath) {
|
|
@@ -22957,7 +23415,7 @@ var init_integrations = __esm(() => {
|
|
|
22957
23415
|
},
|
|
22958
23416
|
hooksInstalledInSettings(scope, cwd) {
|
|
22959
23417
|
const settingsPath = this.getSettingsPath(scope, cwd);
|
|
22960
|
-
if (!
|
|
23418
|
+
if (!existsSync26(settingsPath))
|
|
22961
23419
|
return false;
|
|
22962
23420
|
try {
|
|
22963
23421
|
const settings = this.readSettings(settingsPath);
|
|
@@ -22986,14 +23444,14 @@ var init_integrations = __esm(() => {
|
|
|
22986
23444
|
scopes: COPILOT_HOOK_SCOPES,
|
|
22987
23445
|
eventTypes: COPILOT_HOOK_EVENT_TYPES,
|
|
22988
23446
|
getSettingsPath(scope, cwd) {
|
|
22989
|
-
const base = cwd ?
|
|
23447
|
+
const base = cwd ? resolve21(cwd) : process.cwd();
|
|
22990
23448
|
switch (scope) {
|
|
22991
23449
|
case "user":
|
|
22992
|
-
return
|
|
23450
|
+
return resolve21(homedir23(), ".copilot", "hooks", "failproofai.json");
|
|
22993
23451
|
case "project":
|
|
22994
|
-
return
|
|
23452
|
+
return resolve21(base, ".github", "hooks", "failproofai.json");
|
|
22995
23453
|
case "local":
|
|
22996
|
-
return
|
|
23454
|
+
return resolve21(base, ".github", "hooks", "failproofai.json");
|
|
22997
23455
|
}
|
|
22998
23456
|
},
|
|
22999
23457
|
readSettings(settingsPath) {
|
|
@@ -23071,7 +23529,7 @@ var init_integrations = __esm(() => {
|
|
|
23071
23529
|
},
|
|
23072
23530
|
hooksInstalledInSettings(scope, cwd) {
|
|
23073
23531
|
const settingsPath = this.getSettingsPath(scope, cwd);
|
|
23074
|
-
if (!
|
|
23532
|
+
if (!existsSync26(settingsPath))
|
|
23075
23533
|
return false;
|
|
23076
23534
|
try {
|
|
23077
23535
|
const settings = this.readSettings(settingsPath);
|
|
@@ -23100,14 +23558,14 @@ var init_integrations = __esm(() => {
|
|
|
23100
23558
|
scopes: CURSOR_HOOK_SCOPES,
|
|
23101
23559
|
eventTypes: CURSOR_HOOK_EVENT_TYPES,
|
|
23102
23560
|
getSettingsPath(scope, cwd) {
|
|
23103
|
-
const base = cwd ?
|
|
23561
|
+
const base = cwd ? resolve21(cwd) : process.cwd();
|
|
23104
23562
|
switch (scope) {
|
|
23105
23563
|
case "user":
|
|
23106
|
-
return
|
|
23564
|
+
return resolve21(homedir23(), ".cursor", "hooks.json");
|
|
23107
23565
|
case "project":
|
|
23108
|
-
return
|
|
23566
|
+
return resolve21(base, ".cursor", "hooks.json");
|
|
23109
23567
|
case "local":
|
|
23110
|
-
return
|
|
23568
|
+
return resolve21(base, ".cursor", "hooks.json");
|
|
23111
23569
|
}
|
|
23112
23570
|
},
|
|
23113
23571
|
readSettings(settingsPath) {
|
|
@@ -23174,7 +23632,7 @@ var init_integrations = __esm(() => {
|
|
|
23174
23632
|
},
|
|
23175
23633
|
hooksInstalledInSettings(scope, cwd) {
|
|
23176
23634
|
const settingsPath = this.getSettingsPath(scope, cwd);
|
|
23177
|
-
if (!
|
|
23635
|
+
if (!existsSync26(settingsPath))
|
|
23178
23636
|
return false;
|
|
23179
23637
|
try {
|
|
23180
23638
|
const settings = this.readSettings(settingsPath);
|
|
@@ -23199,14 +23657,14 @@ var init_integrations = __esm(() => {
|
|
|
23199
23657
|
scopes: OPENCODE_HOOK_SCOPES,
|
|
23200
23658
|
eventTypes: OPENCODE_HOOK_EVENT_TYPES,
|
|
23201
23659
|
getSettingsPath(scope, cwd) {
|
|
23202
|
-
const base = cwd ?
|
|
23660
|
+
const base = cwd ? resolve21(cwd) : process.cwd();
|
|
23203
23661
|
switch (scope) {
|
|
23204
23662
|
case "user":
|
|
23205
|
-
return
|
|
23663
|
+
return resolve21(homedir23(), ".config", "opencode", "opencode.json");
|
|
23206
23664
|
case "project":
|
|
23207
|
-
return
|
|
23665
|
+
return resolve21(base, ".opencode", "opencode.json");
|
|
23208
23666
|
case "local":
|
|
23209
|
-
return
|
|
23667
|
+
return resolve21(base, ".opencode", "opencode.json");
|
|
23210
23668
|
}
|
|
23211
23669
|
},
|
|
23212
23670
|
readSettings(settingsPath) {
|
|
@@ -23217,7 +23675,7 @@ var init_integrations = __esm(() => {
|
|
|
23217
23675
|
},
|
|
23218
23676
|
buildHookEntry(_binaryPath, _eventType, scope) {
|
|
23219
23677
|
if (scope === "user") {
|
|
23220
|
-
const abs =
|
|
23678
|
+
const abs = resolve21(homedir23(), ".config", "opencode", "plugins", "failproofai.mjs");
|
|
23221
23679
|
return { spec: `file://${abs}`, [FAILPROOFAI_HOOK_MARKER]: true };
|
|
23222
23680
|
}
|
|
23223
23681
|
return { spec: OPENCODE_PLUGIN_REL_PATH, [FAILPROOFAI_HOOK_MARKER]: true };
|
|
@@ -23232,10 +23690,10 @@ var init_integrations = __esm(() => {
|
|
|
23232
23690
|
writeHookEntries(settings, binaryPath, scope) {
|
|
23233
23691
|
const s = settings;
|
|
23234
23692
|
const effectiveScope = scope ?? "project";
|
|
23235
|
-
const settingsPath = effectiveScope === "user" ?
|
|
23693
|
+
const settingsPath = effectiveScope === "user" ? resolve21(homedir23(), ".config", "opencode", "opencode.json") : resolve21(process.cwd(), ".opencode", "opencode.json");
|
|
23236
23694
|
const pluginPath = opencodePluginFilePath(settingsPath);
|
|
23237
|
-
|
|
23238
|
-
|
|
23695
|
+
mkdirSync17(dirname14(pluginPath), { recursive: true });
|
|
23696
|
+
writeFileSync15(pluginPath, buildOpenCodePluginShim(binaryPath, effectiveScope), "utf8");
|
|
23239
23697
|
if (!Array.isArray(s.plugin))
|
|
23240
23698
|
s.plugin = [];
|
|
23241
23699
|
const desired = effectiveScope === "user" ? `file://${pluginPath}` : OPENCODE_PLUGIN_REL_PATH;
|
|
@@ -23258,9 +23716,9 @@ var init_integrations = __esm(() => {
|
|
|
23258
23716
|
}
|
|
23259
23717
|
this.writeSettings(settingsPath, settings);
|
|
23260
23718
|
const pluginPath = opencodePluginFilePath(settingsPath);
|
|
23261
|
-
if (
|
|
23719
|
+
if (existsSync26(pluginPath)) {
|
|
23262
23720
|
try {
|
|
23263
|
-
const content =
|
|
23721
|
+
const content = readFileSync22(pluginPath, "utf8");
|
|
23264
23722
|
if (content.includes(FAILPROOFAI_HOOK_MARKER)) {
|
|
23265
23723
|
unlinkSync3(pluginPath);
|
|
23266
23724
|
if (removed === 0)
|
|
@@ -23272,7 +23730,7 @@ var init_integrations = __esm(() => {
|
|
|
23272
23730
|
},
|
|
23273
23731
|
hooksInstalledInSettings(scope, cwd) {
|
|
23274
23732
|
const settingsPath = this.getSettingsPath(scope, cwd);
|
|
23275
|
-
if (!
|
|
23733
|
+
if (!existsSync26(settingsPath))
|
|
23276
23734
|
return false;
|
|
23277
23735
|
try {
|
|
23278
23736
|
const settings = this.readSettings(settingsPath);
|
|
@@ -23282,9 +23740,9 @@ var init_integrations = __esm(() => {
|
|
|
23282
23740
|
if (!hasEntry)
|
|
23283
23741
|
return false;
|
|
23284
23742
|
const pluginPath = opencodePluginFilePath(settingsPath);
|
|
23285
|
-
if (!
|
|
23743
|
+
if (!existsSync26(pluginPath))
|
|
23286
23744
|
return false;
|
|
23287
|
-
const content =
|
|
23745
|
+
const content = readFileSync22(pluginPath, "utf8");
|
|
23288
23746
|
return content.includes(FAILPROOFAI_HOOK_MARKER);
|
|
23289
23747
|
} catch {
|
|
23290
23748
|
return false;
|
|
@@ -23300,14 +23758,14 @@ var init_integrations = __esm(() => {
|
|
|
23300
23758
|
scopes: PI_HOOK_SCOPES,
|
|
23301
23759
|
eventTypes: PI_HOOK_EVENT_TYPES,
|
|
23302
23760
|
getSettingsPath(scope, cwd) {
|
|
23303
|
-
const base = cwd ?
|
|
23761
|
+
const base = cwd ? resolve21(cwd) : process.cwd();
|
|
23304
23762
|
switch (scope) {
|
|
23305
23763
|
case "user":
|
|
23306
|
-
return
|
|
23764
|
+
return resolve21(homedir23(), ".pi", "agent", "settings.json");
|
|
23307
23765
|
case "project":
|
|
23308
|
-
return
|
|
23766
|
+
return resolve21(base, ".pi", "settings.json");
|
|
23309
23767
|
case "local":
|
|
23310
|
-
return
|
|
23768
|
+
return resolve21(base, ".pi", "settings.json");
|
|
23311
23769
|
}
|
|
23312
23770
|
},
|
|
23313
23771
|
readSettings(settingsPath) {
|
|
@@ -23349,7 +23807,7 @@ var init_integrations = __esm(() => {
|
|
|
23349
23807
|
}
|
|
23350
23808
|
},
|
|
23351
23809
|
removeHooksFromFile(settingsPath) {
|
|
23352
|
-
if (!
|
|
23810
|
+
if (!existsSync26(settingsPath))
|
|
23353
23811
|
return 0;
|
|
23354
23812
|
const settings = this.readSettings(settingsPath);
|
|
23355
23813
|
if (!Array.isArray(settings.packages))
|
|
@@ -23364,7 +23822,7 @@ var init_integrations = __esm(() => {
|
|
|
23364
23822
|
},
|
|
23365
23823
|
hooksInstalledInSettings(scope, cwd) {
|
|
23366
23824
|
const settingsPath = this.getSettingsPath(scope, cwd);
|
|
23367
|
-
if (!
|
|
23825
|
+
if (!existsSync26(settingsPath))
|
|
23368
23826
|
return false;
|
|
23369
23827
|
try {
|
|
23370
23828
|
const settings = this.readSettings(settingsPath);
|
|
@@ -23385,10 +23843,10 @@ var init_integrations = __esm(() => {
|
|
|
23385
23843
|
scopes: HERMES_HOOK_SCOPES,
|
|
23386
23844
|
eventTypes: HERMES_HOOK_EVENT_TYPES,
|
|
23387
23845
|
getSettingsPath() {
|
|
23388
|
-
return
|
|
23846
|
+
return resolve21(hermesRoot(), "config.yaml");
|
|
23389
23847
|
},
|
|
23390
23848
|
getSettingsPaths() {
|
|
23391
|
-
return listHermesProfiles().map((p) =>
|
|
23849
|
+
return listHermesProfiles().map((p) => resolve21(p.home, "config.yaml"));
|
|
23392
23850
|
},
|
|
23393
23851
|
readSettings(settingsPath) {
|
|
23394
23852
|
return readYamlDoc(settingsPath);
|
|
@@ -23423,7 +23881,7 @@ var init_integrations = __esm(() => {
|
|
|
23423
23881
|
doc.set("hooks_auto_accept", true);
|
|
23424
23882
|
},
|
|
23425
23883
|
removeHooksFromFile(settingsPath) {
|
|
23426
|
-
if (!
|
|
23884
|
+
if (!existsSync26(settingsPath))
|
|
23427
23885
|
return 0;
|
|
23428
23886
|
const doc = readYamlDoc(settingsPath);
|
|
23429
23887
|
const js = doc.toJS() ?? {};
|
|
@@ -23468,7 +23926,7 @@ var init_integrations = __esm(() => {
|
|
|
23468
23926
|
scopes: OPENCLAW_HOOK_SCOPES,
|
|
23469
23927
|
eventTypes: OPENCLAW_HOOK_EVENT_TYPES,
|
|
23470
23928
|
getSettingsPath() {
|
|
23471
|
-
return
|
|
23929
|
+
return resolve21(homedir23(), ".openclaw", "openclaw.json");
|
|
23472
23930
|
},
|
|
23473
23931
|
readSettings(settingsPath) {
|
|
23474
23932
|
return readJsonFile(settingsPath);
|
|
@@ -23513,7 +23971,7 @@ var init_integrations = __esm(() => {
|
|
|
23513
23971
|
hooks.allowConversationAccess = true;
|
|
23514
23972
|
},
|
|
23515
23973
|
removeHooksFromFile(settingsPath) {
|
|
23516
|
-
if (!
|
|
23974
|
+
if (!existsSync26(settingsPath))
|
|
23517
23975
|
return 0;
|
|
23518
23976
|
const settings = this.readSettings(settingsPath);
|
|
23519
23977
|
const plugins = settings.plugins;
|
|
@@ -23542,7 +24000,7 @@ var init_integrations = __esm(() => {
|
|
|
23542
24000
|
},
|
|
23543
24001
|
hooksInstalledInSettings(scope, cwd) {
|
|
23544
24002
|
const settingsPath = this.getSettingsPath(scope, cwd);
|
|
23545
|
-
if (!
|
|
24003
|
+
if (!existsSync26(settingsPath))
|
|
23546
24004
|
return false;
|
|
23547
24005
|
try {
|
|
23548
24006
|
const settings = this.readSettings(settingsPath);
|
|
@@ -23564,14 +24022,14 @@ var init_integrations = __esm(() => {
|
|
|
23564
24022
|
scopes: FACTORY_HOOK_SCOPES,
|
|
23565
24023
|
eventTypes: FACTORY_HOOK_EVENT_TYPES,
|
|
23566
24024
|
getSettingsPath(scope, cwd) {
|
|
23567
|
-
const base = cwd ?
|
|
24025
|
+
const base = cwd ? resolve21(cwd) : process.cwd();
|
|
23568
24026
|
switch (scope) {
|
|
23569
24027
|
case "user":
|
|
23570
|
-
return
|
|
24028
|
+
return resolve21(homedir23(), ".factory", "hooks.json");
|
|
23571
24029
|
case "project":
|
|
23572
|
-
return
|
|
24030
|
+
return resolve21(base, ".factory", "hooks.json");
|
|
23573
24031
|
case "local":
|
|
23574
|
-
return
|
|
24032
|
+
return resolve21(base, ".factory", "hooks.json");
|
|
23575
24033
|
}
|
|
23576
24034
|
},
|
|
23577
24035
|
readSettings(settingsPath) {
|
|
@@ -23639,7 +24097,7 @@ var init_integrations = __esm(() => {
|
|
|
23639
24097
|
},
|
|
23640
24098
|
hooksInstalledInSettings(scope, cwd) {
|
|
23641
24099
|
const settingsPath = this.getSettingsPath(scope, cwd);
|
|
23642
|
-
if (!
|
|
24100
|
+
if (!existsSync26(settingsPath))
|
|
23643
24101
|
return false;
|
|
23644
24102
|
try {
|
|
23645
24103
|
const settings = this.readSettings(settingsPath);
|
|
@@ -23666,14 +24124,14 @@ var init_integrations = __esm(() => {
|
|
|
23666
24124
|
scopes: DEVIN_HOOK_SCOPES,
|
|
23667
24125
|
eventTypes: DEVIN_HOOK_EVENT_TYPES,
|
|
23668
24126
|
getSettingsPath(scope, cwd) {
|
|
23669
|
-
const base = cwd ?
|
|
24127
|
+
const base = cwd ? resolve21(cwd) : process.cwd();
|
|
23670
24128
|
switch (scope) {
|
|
23671
24129
|
case "user":
|
|
23672
|
-
return
|
|
24130
|
+
return resolve21(homedir23(), ".config", "devin", "config.json");
|
|
23673
24131
|
case "project":
|
|
23674
|
-
return
|
|
24132
|
+
return resolve21(base, ".devin", "config.json");
|
|
23675
24133
|
case "local":
|
|
23676
|
-
return
|
|
24134
|
+
return resolve21(base, ".devin", "config.json");
|
|
23677
24135
|
}
|
|
23678
24136
|
},
|
|
23679
24137
|
readSettings(settingsPath) {
|
|
@@ -23745,7 +24203,7 @@ var init_integrations = __esm(() => {
|
|
|
23745
24203
|
},
|
|
23746
24204
|
hooksInstalledInSettings(scope, cwd) {
|
|
23747
24205
|
const settingsPath = this.getSettingsPath(scope, cwd);
|
|
23748
|
-
if (!
|
|
24206
|
+
if (!existsSync26(settingsPath))
|
|
23749
24207
|
return false;
|
|
23750
24208
|
try {
|
|
23751
24209
|
const settings = this.readSettings(settingsPath);
|
|
@@ -23775,14 +24233,14 @@ var init_integrations = __esm(() => {
|
|
|
23775
24233
|
scopes: ANTIGRAVITY_HOOK_SCOPES,
|
|
23776
24234
|
eventTypes: ANTIGRAVITY_HOOK_EVENT_TYPES,
|
|
23777
24235
|
getSettingsPath(scope, cwd) {
|
|
23778
|
-
const base = cwd ?
|
|
24236
|
+
const base = cwd ? resolve21(cwd) : process.cwd();
|
|
23779
24237
|
switch (scope) {
|
|
23780
24238
|
case "user":
|
|
23781
|
-
return
|
|
24239
|
+
return resolve21(homedir23(), ".gemini", "config", "hooks.json");
|
|
23782
24240
|
case "project":
|
|
23783
|
-
return
|
|
24241
|
+
return resolve21(base, ".agents", "hooks.json");
|
|
23784
24242
|
case "local":
|
|
23785
|
-
return
|
|
24243
|
+
return resolve21(base, ".agents", "hooks.json");
|
|
23786
24244
|
}
|
|
23787
24245
|
},
|
|
23788
24246
|
readSettings(settingsPath) {
|
|
@@ -23884,7 +24342,7 @@ var init_integrations = __esm(() => {
|
|
|
23884
24342
|
},
|
|
23885
24343
|
hooksInstalledInSettings(scope, cwd) {
|
|
23886
24344
|
const settingsPath = this.getSettingsPath(scope, cwd);
|
|
23887
|
-
if (!
|
|
24345
|
+
if (!existsSync26(settingsPath))
|
|
23888
24346
|
return false;
|
|
23889
24347
|
try {
|
|
23890
24348
|
const settings = this.readSettings(settingsPath);
|
|
@@ -23916,14 +24374,14 @@ var init_integrations = __esm(() => {
|
|
|
23916
24374
|
scopes: GOOSE_HOOK_SCOPES,
|
|
23917
24375
|
eventTypes: GOOSE_HOOK_EVENT_TYPES,
|
|
23918
24376
|
getSettingsPath(scope, cwd) {
|
|
23919
|
-
const base = cwd ?
|
|
24377
|
+
const base = cwd ? resolve21(cwd) : process.cwd();
|
|
23920
24378
|
switch (scope) {
|
|
23921
24379
|
case "user":
|
|
23922
|
-
return
|
|
24380
|
+
return resolve21(homedir23(), ".agents", "plugins", "failproofai", "hooks", "hooks.json");
|
|
23923
24381
|
case "project":
|
|
23924
|
-
return
|
|
24382
|
+
return resolve21(base, ".agents", "plugins", "failproofai", "hooks", "hooks.json");
|
|
23925
24383
|
case "local":
|
|
23926
|
-
return
|
|
24384
|
+
return resolve21(base, ".agents", "plugins", "failproofai", "hooks", "hooks.json");
|
|
23927
24385
|
}
|
|
23928
24386
|
},
|
|
23929
24387
|
readSettings(settingsPath) {
|
|
@@ -23990,7 +24448,7 @@ var init_integrations = __esm(() => {
|
|
|
23990
24448
|
},
|
|
23991
24449
|
hooksInstalledInSettings(scope, cwd) {
|
|
23992
24450
|
const settingsPath = this.getSettingsPath(scope, cwd);
|
|
23993
|
-
if (!
|
|
24451
|
+
if (!existsSync26(settingsPath))
|
|
23994
24452
|
return false;
|
|
23995
24453
|
try {
|
|
23996
24454
|
const settings = this.readSettings(settingsPath);
|
|
@@ -24179,7 +24637,7 @@ async function promptPolicySelection(preSelected, options = {}) {
|
|
|
24179
24637
|
`);
|
|
24180
24638
|
lastLineCount = lines.length;
|
|
24181
24639
|
}
|
|
24182
|
-
return new Promise((
|
|
24640
|
+
return new Promise((resolve22) => {
|
|
24183
24641
|
render();
|
|
24184
24642
|
process.stdin.setRawMode(true);
|
|
24185
24643
|
process.stdin.resume();
|
|
@@ -24217,7 +24675,7 @@ async function promptPolicySelection(preSelected, options = {}) {
|
|
|
24217
24675
|
${dim(STEP_DONE2)} Policies
|
|
24218
24676
|
${dim(BAR2)} ${dim(summary)}
|
|
24219
24677
|
`);
|
|
24220
|
-
|
|
24678
|
+
resolve22([...selected, ...carried]);
|
|
24221
24679
|
} else if (key.name === "escape") {
|
|
24222
24680
|
search = "";
|
|
24223
24681
|
cursor2 = 0;
|
|
@@ -24269,423 +24727,6 @@ var init_cli_error = __esm(() => {
|
|
|
24269
24727
|
};
|
|
24270
24728
|
});
|
|
24271
24729
|
|
|
24272
|
-
// src/hooks/pack-store.ts
|
|
24273
|
-
import { createHash as createHash7 } from "node:crypto";
|
|
24274
|
-
import { existsSync as existsSync26, mkdirSync as mkdirSync17, readFileSync as readFileSync22, renameSync as renameSync8, rmSync as rmSync9, writeFileSync as writeFileSync15 } from "node:fs";
|
|
24275
|
-
import { dirname as dirname14, resolve as resolve21 } from "node:path";
|
|
24276
|
-
function baseUrl2() {
|
|
24277
|
-
return (process.env.FAILPROOFAI_PACK_BASE_URL || DEFAULT_BASE_URL2).replace(/\/+$/, "");
|
|
24278
|
-
}
|
|
24279
|
-
function parsePackSpec(source) {
|
|
24280
|
-
const raw = source.trim();
|
|
24281
|
-
if (raw.length === 0)
|
|
24282
|
-
throw new Error("pack source is empty");
|
|
24283
|
-
const url = raw.match(/^(?:https?:\/\/)?(?:www\.)?github\.com\/(.+)$/i);
|
|
24284
|
-
if (url) {
|
|
24285
|
-
const parts = url[1].replace(/\/+$/, "").split("/");
|
|
24286
|
-
const [owner, repo, ...rest] = parts;
|
|
24287
|
-
let tag2 = null;
|
|
24288
|
-
if (rest[0] === "releases" && (rest[1] === "tag" || rest[1] === "download")) {
|
|
24289
|
-
const tail = rest.slice(2);
|
|
24290
|
-
tag2 = (rest[1] === "download" ? tail.slice(0, -1) : tail).join("/") || null;
|
|
24291
|
-
} else if (rest[0] === "releases" && rest[1] === "latest") {
|
|
24292
|
-
tag2 = null;
|
|
24293
|
-
}
|
|
24294
|
-
return validated(owner, repo, tag2);
|
|
24295
|
-
}
|
|
24296
|
-
const withoutScheme = raw.replace(/^github:/i, "");
|
|
24297
|
-
const at2 = withoutScheme.lastIndexOf("@");
|
|
24298
|
-
const repoPart = at2 > 0 ? withoutScheme.slice(0, at2) : withoutScheme;
|
|
24299
|
-
const tag = at2 > 0 ? withoutScheme.slice(at2 + 1) : null;
|
|
24300
|
-
const slash = repoPart.indexOf("/");
|
|
24301
|
-
if (slash <= 0 || slash === repoPart.length - 1) {
|
|
24302
|
-
throw new Error(`pack source must be owner/repo, owner/repo@tag, or a github.com URL — got ${JSON.stringify(source)}`);
|
|
24303
|
-
}
|
|
24304
|
-
return validated(repoPart.slice(0, slash), repoPart.slice(slash + 1), tag);
|
|
24305
|
-
}
|
|
24306
|
-
function validated(owner, repo, tag) {
|
|
24307
|
-
if (!owner || !OWNER_RE.test(owner))
|
|
24308
|
-
throw new Error(`unsafe owner ${JSON.stringify(owner)}`);
|
|
24309
|
-
if (!repo || !OWNER_RE.test(repo))
|
|
24310
|
-
throw new Error(`unsafe repo ${JSON.stringify(repo)}`);
|
|
24311
|
-
if (tag !== null && !TAG_RE.test(tag))
|
|
24312
|
-
throw new Error(`unsafe tag ${JSON.stringify(tag)}`);
|
|
24313
|
-
return { owner, repo, tag };
|
|
24314
|
-
}
|
|
24315
|
-
async function resolveLatestTag(spec) {
|
|
24316
|
-
const url = `${baseUrl2()}/${spec.owner}/${spec.repo}/releases/latest`;
|
|
24317
|
-
const response = await fetch(url, {
|
|
24318
|
-
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
24319
|
-
redirect: "manual"
|
|
24320
|
-
});
|
|
24321
|
-
const location = response.headers.get("location");
|
|
24322
|
-
if (!location) {
|
|
24323
|
-
throw new Error(`could not resolve the newest release of ${spec.owner}/${spec.repo} ` + `(GET ${url} returned ${response.status} with no redirect). That redirect is issued only ` + `for a published, non-prerelease release, so either the repository has no releases at all ` + `or every release it has is a prerelease or a draft. Name a tag explicitly.`);
|
|
24324
|
-
}
|
|
24325
|
-
const match = location.match(/\/releases\/tag\/(.+)$/);
|
|
24326
|
-
if (!match)
|
|
24327
|
-
throw new Error(`unexpected redirect from ${url}: ${location}`);
|
|
24328
|
-
const tag = decodeURIComponent(match[1]).replace(/\/+$/, "");
|
|
24329
|
-
if (!TAG_RE.test(tag))
|
|
24330
|
-
throw new Error(`unsafe tag ${JSON.stringify(tag)} from ${url}`);
|
|
24331
|
-
return tag;
|
|
24332
|
-
}
|
|
24333
|
-
function formatPackSpec(spec) {
|
|
24334
|
-
return `github:${spec.owner}/${spec.repo}@${spec.tag}`;
|
|
24335
|
-
}
|
|
24336
|
-
function packAssetUrl(spec, asset) {
|
|
24337
|
-
return `${baseUrl2()}/${spec.owner}/${spec.repo}/releases/download/${spec.tag}/${asset}`;
|
|
24338
|
-
}
|
|
24339
|
-
function packTagMatchesVersion(tag, version3) {
|
|
24340
|
-
const tail = tag.slice(tag.lastIndexOf("/") + 1);
|
|
24341
|
-
return tail === version3 || tail === `v${version3}`;
|
|
24342
|
-
}
|
|
24343
|
-
async function fetchBytes2(url) {
|
|
24344
|
-
const response = await fetch(url, {
|
|
24345
|
-
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
|
|
24346
|
-
redirect: "follow"
|
|
24347
|
-
});
|
|
24348
|
-
if (!response.ok)
|
|
24349
|
-
throw new Error(`GET ${url} returned ${response.status}`);
|
|
24350
|
-
const contentLength = response.headers.get("content-length");
|
|
24351
|
-
if (contentLength !== null) {
|
|
24352
|
-
const declared = Number(contentLength);
|
|
24353
|
-
if (Number.isFinite(declared) && declared > MAX_ARTIFACT_BYTES) {
|
|
24354
|
-
await response.body?.cancel();
|
|
24355
|
-
throw new Error(`${url} declares ${declared} bytes, over the ${MAX_ARTIFACT_BYTES} limit`);
|
|
24356
|
-
}
|
|
24357
|
-
}
|
|
24358
|
-
if (!response.body)
|
|
24359
|
-
return Buffer.alloc(0);
|
|
24360
|
-
const chunks = [];
|
|
24361
|
-
const reader = response.body.getReader();
|
|
24362
|
-
let total = 0;
|
|
24363
|
-
try {
|
|
24364
|
-
while (true) {
|
|
24365
|
-
const { done, value } = await reader.read();
|
|
24366
|
-
if (done)
|
|
24367
|
-
break;
|
|
24368
|
-
total += value.byteLength;
|
|
24369
|
-
if (total > MAX_ARTIFACT_BYTES) {
|
|
24370
|
-
await reader.cancel();
|
|
24371
|
-
throw new Error(`${url} is over the ${MAX_ARTIFACT_BYTES} byte limit`);
|
|
24372
|
-
}
|
|
24373
|
-
chunks.push(Buffer.from(value));
|
|
24374
|
-
}
|
|
24375
|
-
} finally {
|
|
24376
|
-
reader.releaseLock();
|
|
24377
|
-
}
|
|
24378
|
-
return Buffer.concat(chunks, total);
|
|
24379
|
-
}
|
|
24380
|
-
function digestFor2(manifest, assetName) {
|
|
24381
|
-
for (const line of manifest.split(`
|
|
24382
|
-
`)) {
|
|
24383
|
-
const match = line.trim().match(/^([0-9a-f]{64})\s+\*?(.+)$/i);
|
|
24384
|
-
if (match && match[2].trim() === assetName)
|
|
24385
|
-
return match[1].toLowerCase();
|
|
24386
|
-
}
|
|
24387
|
-
return null;
|
|
24388
|
-
}
|
|
24389
|
-
function sha256(bytes) {
|
|
24390
|
-
return createHash7("sha256").update(bytes).digest("hex");
|
|
24391
|
-
}
|
|
24392
|
-
function writeAtomic(target, bytes) {
|
|
24393
|
-
const tempPath = `${target}.${process.pid}.tmp`;
|
|
24394
|
-
try {
|
|
24395
|
-
mkdirSync17(dirname14(target), { recursive: true });
|
|
24396
|
-
writeFileSync15(tempPath, bytes);
|
|
24397
|
-
renameSync8(tempPath, target);
|
|
24398
|
-
} catch (err) {
|
|
24399
|
-
rmSync9(tempPath, { force: true });
|
|
24400
|
-
throw err;
|
|
24401
|
-
}
|
|
24402
|
-
}
|
|
24403
|
-
async function fetchPackPreview(source) {
|
|
24404
|
-
if (process.env.FAILPROOFAI_NO_DOWNLOAD === "1") {
|
|
24405
|
-
throw new Error("FAILPROOFAI_NO_DOWNLOAD is set, so nothing can be fetched to preview");
|
|
24406
|
-
}
|
|
24407
|
-
const parsed = parsePackSpec(source);
|
|
24408
|
-
const resolvedFromLatest = parsed.tag === null;
|
|
24409
|
-
const spec = parsed.tag !== null ? { ...parsed, tag: parsed.tag } : { ...parsed, tag: await resolveLatestTag(parsed) };
|
|
24410
|
-
const checksums = (await fetchBytes2(packAssetUrl(spec, PACK_CHECKSUMS_ASSET))).toString("utf8");
|
|
24411
|
-
const manifestBytes = await fetchBytes2(packAssetUrl(spec, PACK_MANIFEST_ASSET));
|
|
24412
|
-
const manifestDigest = digestFor2(checksums, PACK_MANIFEST_ASSET);
|
|
24413
|
-
if (!manifestDigest) {
|
|
24414
|
-
throw new Error(`${PACK_CHECKSUMS_ASSET} has no entry for ${PACK_MANIFEST_ASSET}`);
|
|
24415
|
-
}
|
|
24416
|
-
if (sha256(manifestBytes) !== manifestDigest) {
|
|
24417
|
-
throw new Error(`${PACK_MANIFEST_ASSET} failed integrity verification`);
|
|
24418
|
-
}
|
|
24419
|
-
let raw;
|
|
24420
|
-
try {
|
|
24421
|
-
raw = JSON.parse(manifestBytes.toString("utf8"));
|
|
24422
|
-
} catch (err) {
|
|
24423
|
-
throw new Error(`${PACK_MANIFEST_ASSET} is not valid JSON: ${errText2(err)}`);
|
|
24424
|
-
}
|
|
24425
|
-
if (!raw || typeof raw !== "object")
|
|
24426
|
-
throw new Error(`${PACK_MANIFEST_ASSET} is not an object`);
|
|
24427
|
-
const value = raw;
|
|
24428
|
-
const identity2 = parsePackIdentity(value);
|
|
24429
|
-
if (!Array.isArray(value.policies) || value.policies.length === 0) {
|
|
24430
|
-
throw new Error("pack manifest declares no policies");
|
|
24431
|
-
}
|
|
24432
|
-
return {
|
|
24433
|
-
id: identity2.id,
|
|
24434
|
-
version: identity2.version,
|
|
24435
|
-
effect: identity2.effect,
|
|
24436
|
-
policies: value.policies.map((policy, i) => parsePackPolicy(identity2.id, policy, i)),
|
|
24437
|
-
source: formatPackSpec(spec),
|
|
24438
|
-
resolvedFromLatest
|
|
24439
|
-
};
|
|
24440
|
-
}
|
|
24441
|
-
function priorRecordFor(id, sha2562) {
|
|
24442
|
-
const installed = readInstalledPacks().packs;
|
|
24443
|
-
return installed.find((p) => p.id === id) ?? installed.find((p) => p.sha256 === sha2562);
|
|
24444
|
-
}
|
|
24445
|
-
async function fetchPack(spec) {
|
|
24446
|
-
const checksums = (await fetchBytes2(packAssetUrl(spec, PACK_CHECKSUMS_ASSET))).toString("utf8");
|
|
24447
|
-
const manifestBytes = await fetchBytes2(packAssetUrl(spec, PACK_MANIFEST_ASSET));
|
|
24448
|
-
const manifestDigest = digestFor2(checksums, PACK_MANIFEST_ASSET);
|
|
24449
|
-
if (!manifestDigest)
|
|
24450
|
-
throw new Error(`${PACK_CHECKSUMS_ASSET} has no entry for ${PACK_MANIFEST_ASSET}`);
|
|
24451
|
-
if (sha256(manifestBytes) !== manifestDigest) {
|
|
24452
|
-
throw new Error(`${PACK_MANIFEST_ASSET} failed integrity verification`);
|
|
24453
|
-
}
|
|
24454
|
-
const artifact = await fetchBytes2(packAssetUrl(spec, PACK_ENTRY_ASSET));
|
|
24455
|
-
const entryDigest = digestFor2(checksums, PACK_ENTRY_ASSET);
|
|
24456
|
-
if (!entryDigest)
|
|
24457
|
-
throw new Error(`${PACK_CHECKSUMS_ASSET} has no entry for ${PACK_ENTRY_ASSET}`);
|
|
24458
|
-
const artifactDigest = sha256(artifact);
|
|
24459
|
-
if (artifactDigest !== entryDigest) {
|
|
24460
|
-
throw new Error(`${PACK_ENTRY_ASSET} failed integrity verification`);
|
|
24461
|
-
}
|
|
24462
|
-
let parsed;
|
|
24463
|
-
try {
|
|
24464
|
-
parsed = JSON.parse(manifestBytes.toString("utf8"));
|
|
24465
|
-
} catch (err) {
|
|
24466
|
-
throw new Error(`${PACK_MANIFEST_ASSET} is not valid JSON: ${errText2(err)}`);
|
|
24467
|
-
}
|
|
24468
|
-
if (!parsed || typeof parsed !== "object")
|
|
24469
|
-
throw new Error(`${PACK_MANIFEST_ASSET} is not an object`);
|
|
24470
|
-
const raw = parsed;
|
|
24471
|
-
const identity2 = parsePackIdentity(raw);
|
|
24472
|
-
if (!Array.isArray(raw.policies) || raw.policies.length === 0) {
|
|
24473
|
-
throw new Error("pack manifest declares no policies");
|
|
24474
|
-
}
|
|
24475
|
-
const policies = raw.policies.map((p, i) => parsePackPolicy(identity2.id, p, i));
|
|
24476
|
-
return {
|
|
24477
|
-
id: identity2.id,
|
|
24478
|
-
version: identity2.version,
|
|
24479
|
-
policies,
|
|
24480
|
-
...raw.effect !== undefined ? { effect: identity2.effect } : {},
|
|
24481
|
-
artifact,
|
|
24482
|
-
artifactDigest
|
|
24483
|
-
};
|
|
24484
|
-
}
|
|
24485
|
-
function slugifyCategory(category) {
|
|
24486
|
-
return category.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
24487
|
-
}
|
|
24488
|
-
function resolveSelection(policies, opts, previous, previouslyInstalled) {
|
|
24489
|
-
const available = policies.map((p) => p.name);
|
|
24490
|
-
if (opts?.all)
|
|
24491
|
-
return { enabled: null, reason: "all" };
|
|
24492
|
-
const picked = new Set;
|
|
24493
|
-
if (opts?.categories?.length) {
|
|
24494
|
-
const wanted = new Set(opts.categories.map(slugifyCategory));
|
|
24495
|
-
const known = new Map(policies.map((p) => [slugifyCategory(p.category), p.category]));
|
|
24496
|
-
const unknown = [...wanted].filter((c) => !known.has(c));
|
|
24497
|
-
if (unknown.length > 0) {
|
|
24498
|
-
throw new Error(`no such categor${unknown.length === 1 ? "y" : "ies"}: ${unknown.join(", ")} ` + `(has: ${[...new Set(policies.map((p) => slugifyCategory(p.category)))].join(", ")})`);
|
|
24499
|
-
}
|
|
24500
|
-
for (const p of policies)
|
|
24501
|
-
if (wanted.has(slugifyCategory(p.category)))
|
|
24502
|
-
picked.add(p.name);
|
|
24503
|
-
}
|
|
24504
|
-
if (opts?.only?.length) {
|
|
24505
|
-
const unknown = opts.only.filter((n) => !available.includes(n));
|
|
24506
|
-
if (unknown.length > 0) {
|
|
24507
|
-
throw new Error(`pack does not contain ${unknown.join(", ")}`);
|
|
24508
|
-
}
|
|
24509
|
-
for (const n of opts.only)
|
|
24510
|
-
picked.add(n);
|
|
24511
|
-
}
|
|
24512
|
-
if (opts?.only !== undefined || opts?.categories?.length) {
|
|
24513
|
-
return { enabled: available.filter((n) => picked.has(n)), reason: "selected" };
|
|
24514
|
-
}
|
|
24515
|
-
if (previouslyInstalled) {
|
|
24516
|
-
return { enabled: previous === null ? null : previous ?? null, reason: "carried" };
|
|
24517
|
-
}
|
|
24518
|
-
return { enabled: policies.filter((p) => p.defaultEnabled).map((p) => p.name), reason: "defaults" };
|
|
24519
|
-
}
|
|
24520
|
-
async function verifyArtifactRegisters(artifactPath, declared) {
|
|
24521
|
-
const { loadCustomHooks: loadCustomHooks2 } = await Promise.resolve().then(() => (init_custom_hooks_loader(), exports_custom_hooks_loader));
|
|
24522
|
-
let registered;
|
|
24523
|
-
try {
|
|
24524
|
-
registered = (await loadCustomHooks2(artifactPath, { strict: true })).map((h) => h.name);
|
|
24525
|
-
} catch (err) {
|
|
24526
|
-
throw new Error(`its artifact could not be loaded: ${err instanceof Error ? err.message : String(err)}`);
|
|
24527
|
-
}
|
|
24528
|
-
const missing = declared.filter((name) => !registered.includes(name));
|
|
24529
|
-
const extra = registered.filter((name) => !declared.includes(name));
|
|
24530
|
-
if (missing.length > 0) {
|
|
24531
|
-
throw new Error(`its manifest declares ${missing.join(", ")} but the artifact does not register ${missing.length === 1 ? "it" : "them"} — that would install as protection that never runs`);
|
|
24532
|
-
}
|
|
24533
|
-
if (extra.length > 0) {
|
|
24534
|
-
throw new Error(`its artifact registers undeclared ${extra.join(", ")} — a policy that enforces but ` + `appears in no listing cannot be reviewed or switched off`);
|
|
24535
|
-
}
|
|
24536
|
-
}
|
|
24537
|
-
async function checkPackArtifact(artifactPath, declared) {
|
|
24538
|
-
try {
|
|
24539
|
-
await verifyArtifactRegisters(artifactPath, declared);
|
|
24540
|
-
return null;
|
|
24541
|
-
} catch (err) {
|
|
24542
|
-
return err instanceof Error ? err.message : String(err);
|
|
24543
|
-
}
|
|
24544
|
-
}
|
|
24545
|
-
async function addPack(source, opts) {
|
|
24546
|
-
if (process.env.FAILPROOFAI_NO_DOWNLOAD) {
|
|
24547
|
-
throw new Error("pack downloads are disabled (FAILPROOFAI_NO_DOWNLOAD). Already-installed packs keep enforcing.");
|
|
24548
|
-
}
|
|
24549
|
-
const parsed = parsePackSpec(source);
|
|
24550
|
-
const spec = parsed.tag ? { ...parsed, tag: parsed.tag } : { ...parsed, tag: await resolveLatestTag(parsed) };
|
|
24551
|
-
const resolvedFromLatest = parsed.tag === null;
|
|
24552
|
-
const fetched = await fetchPack(spec);
|
|
24553
|
-
if (!packTagMatchesVersion(spec.tag, fetched.version)) {
|
|
24554
|
-
const fromTag = spec.tag.slice(spec.tag.lastIndexOf("/") + 1).replace(/^v/, "");
|
|
24555
|
-
const fix = `re-tag the release ${fetched.version} (a leading v is fine), or rebuild the pack with --version ${fromTag}`;
|
|
24556
|
-
throw new Error(`${fetched.id} was not installed: release tag ${spec.tag} does not match the version its manifest declares (${fetched.version}). ` + (resolvedFromLatest ? `That is the newest release of ${spec.owner}/${spec.repo}, so the release and the pack ` + `disagree at the source — ask the publisher to ${fix}.` : `Name the tag whose manifest says ${fetched.version}, or fix the pack: ${fix}.`));
|
|
24557
|
-
}
|
|
24558
|
-
const available = fetched.policies.map((p) => p.name);
|
|
24559
|
-
const prior = priorRecordFor(fetched.id, fetched.artifactDigest);
|
|
24560
|
-
const { enabled, reason } = resolveSelection(fetched.policies, opts, prior?.enabled ? prior.enabled.filter((n) => available.includes(n)) : prior?.enabled, Boolean(prior));
|
|
24561
|
-
const root = packsRoot();
|
|
24562
|
-
const artifactRel = `artifacts/${fetched.artifactDigest}.mjs`;
|
|
24563
|
-
const artifactAbs = resolve21(root, artifactRel);
|
|
24564
|
-
if (!existsSync26(artifactAbs))
|
|
24565
|
-
writeAtomic(artifactAbs, fetched.artifact);
|
|
24566
|
-
try {
|
|
24567
|
-
await verifyArtifactRegisters(artifactAbs, available);
|
|
24568
|
-
} catch (err) {
|
|
24569
|
-
throw new Error(`${fetched.id}@${fetched.version} was not installed: ${err instanceof Error ? err.message : String(err)}`);
|
|
24570
|
-
}
|
|
24571
|
-
const repoOf = (source2) => {
|
|
24572
|
-
const at2 = source2.lastIndexOf("@");
|
|
24573
|
-
return at2 > source2.indexOf(":") ? source2.slice(0, at2) : source2;
|
|
24574
|
-
};
|
|
24575
|
-
if (prior && !prior.source.startsWith("bundled:") && repoOf(prior.source) !== repoOf(formatPackSpec(spec))) {
|
|
24576
|
-
throw new Error(`pack id ${fetched.id} is already installed from ${prior.source}. Refusing to replace it with ${formatPackSpec(spec)} — remove it first if that is what you mean.`);
|
|
24577
|
-
}
|
|
24578
|
-
const record = {
|
|
24579
|
-
id: fetched.id,
|
|
24580
|
-
version: fetched.version,
|
|
24581
|
-
source: formatPackSpec(spec),
|
|
24582
|
-
entry: artifactRel,
|
|
24583
|
-
sha256: fetched.artifactDigest,
|
|
24584
|
-
policies: fetched.policies,
|
|
24585
|
-
...fetched.effect ? { effect: fetched.effect } : {},
|
|
24586
|
-
...enabled ? { enabled } : {},
|
|
24587
|
-
...opts?.clis ? { clis: opts.clis } : {}
|
|
24588
|
-
};
|
|
24589
|
-
const absorbed = upsertInstalled(record);
|
|
24590
|
-
return {
|
|
24591
|
-
replaced: absorbed,
|
|
24592
|
-
id: fetched.id,
|
|
24593
|
-
version: fetched.version,
|
|
24594
|
-
source: record.source,
|
|
24595
|
-
tag: spec.tag,
|
|
24596
|
-
resolvedFromLatest,
|
|
24597
|
-
enabled: enabled ?? available,
|
|
24598
|
-
available,
|
|
24599
|
-
selection: reason,
|
|
24600
|
-
categories: [...new Set(fetched.policies.map((p) => slugifyCategory(p.category)))],
|
|
24601
|
-
artifact: artifactAbs
|
|
24602
|
-
};
|
|
24603
|
-
}
|
|
24604
|
-
function setPackPolicyEnabled(packId, name, on) {
|
|
24605
|
-
const manifestPath = resolve21(packsRoot(), "installed.json");
|
|
24606
|
-
if (!existsSync26(manifestPath))
|
|
24607
|
-
return { ok: false, reason: "no packs are installed" };
|
|
24608
|
-
let raw;
|
|
24609
|
-
try {
|
|
24610
|
-
raw = JSON.parse(readFileSync22(manifestPath, "utf8"));
|
|
24611
|
-
} catch {
|
|
24612
|
-
return { ok: false, reason: `pack manifest at ${manifestPath} is unreadable` };
|
|
24613
|
-
}
|
|
24614
|
-
const packs = Array.isArray(raw.packs) ? raw.packs : [];
|
|
24615
|
-
const pack = packs.find((p) => p?.id === packId);
|
|
24616
|
-
if (!pack)
|
|
24617
|
-
return { ok: false, reason: `no installed pack with id ${packId}` };
|
|
24618
|
-
const declared = Array.isArray(pack.policies) ? pack.policies : [];
|
|
24619
|
-
const available = declared.map((p) => typeof p?.name === "string" ? p.name : null).filter((n) => n !== null);
|
|
24620
|
-
if (!available.includes(name)) {
|
|
24621
|
-
return { ok: false, reason: `pack ${packId} declares no policy named ${name}` };
|
|
24622
|
-
}
|
|
24623
|
-
const current = pack.enabled ?? available;
|
|
24624
|
-
const next = on ? [...new Set([...current, name])] : current.filter((n) => n !== name);
|
|
24625
|
-
pack.enabled = available.filter((n) => next.includes(n));
|
|
24626
|
-
writeAtomic(manifestPath, JSON.stringify({ schemaVersion: 1, packs }, null, 2) + `
|
|
24627
|
-
`);
|
|
24628
|
-
return { ok: true };
|
|
24629
|
-
}
|
|
24630
|
-
function removePack(id) {
|
|
24631
|
-
const manifestPath = resolve21(packsRoot(), "installed.json");
|
|
24632
|
-
if (!existsSync26(manifestPath))
|
|
24633
|
-
return false;
|
|
24634
|
-
let raw;
|
|
24635
|
-
try {
|
|
24636
|
-
raw = JSON.parse(readFileSync22(manifestPath, "utf8"));
|
|
24637
|
-
} catch {
|
|
24638
|
-
return false;
|
|
24639
|
-
}
|
|
24640
|
-
const packs = Array.isArray(raw.packs) ? raw.packs : [];
|
|
24641
|
-
const remaining = packs.filter((p) => p?.id !== id);
|
|
24642
|
-
if (remaining.length === packs.length)
|
|
24643
|
-
return false;
|
|
24644
|
-
writeAtomic(manifestPath, JSON.stringify({ schemaVersion: 1, packs: remaining }, null, 2) + `
|
|
24645
|
-
`);
|
|
24646
|
-
return true;
|
|
24647
|
-
}
|
|
24648
|
-
function upsertInstalled(record) {
|
|
24649
|
-
const manifestPath = resolve21(packsRoot(), "installed.json");
|
|
24650
|
-
let packs = [];
|
|
24651
|
-
if (existsSync26(manifestPath)) {
|
|
24652
|
-
try {
|
|
24653
|
-
const raw = JSON.parse(readFileSync22(manifestPath, "utf8"));
|
|
24654
|
-
if (Array.isArray(raw.packs))
|
|
24655
|
-
packs = raw.packs;
|
|
24656
|
-
} catch {
|
|
24657
|
-
throw new Error(`existing pack manifest at ${manifestPath} is unreadable; move it aside and re-add`);
|
|
24658
|
-
}
|
|
24659
|
-
}
|
|
24660
|
-
const absorbed = packs.filter((p) => p?.sha256 === record.sha256 && p?.id !== record.id).map((p) => p.id);
|
|
24661
|
-
if (!record.enabled) {
|
|
24662
|
-
const prior = packs.find((p) => absorbed.includes(p?.id));
|
|
24663
|
-
const carried = prior?.enabled?.filter((n) => record.policies?.some((rp) => rp?.name === n));
|
|
24664
|
-
if (carried && carried.length > 0)
|
|
24665
|
-
record.enabled = carried;
|
|
24666
|
-
}
|
|
24667
|
-
const remaining = packs.filter((p) => !absorbed.includes(p?.id));
|
|
24668
|
-
const idx = remaining.findIndex((p) => p?.id === record.id);
|
|
24669
|
-
if (idx >= 0)
|
|
24670
|
-
remaining[idx] = record;
|
|
24671
|
-
else
|
|
24672
|
-
remaining.push(record);
|
|
24673
|
-
writeAtomic(manifestPath, JSON.stringify({ schemaVersion: 1, packs: remaining }, null, 2) + `
|
|
24674
|
-
`);
|
|
24675
|
-
return absorbed;
|
|
24676
|
-
}
|
|
24677
|
-
function errText2(err) {
|
|
24678
|
-
return err instanceof Error ? err.message : String(err);
|
|
24679
|
-
}
|
|
24680
|
-
var DEFAULT_BASE_URL2 = "https://github.com", FETCH_TIMEOUT_MS = 30000, MAX_ARTIFACT_BYTES, PACK_MANIFEST_ASSET = "failproofai-pack.json", PACK_ENTRY_ASSET = "failproofai-pack.mjs", PACK_CHECKSUMS_ASSET = "SHA256SUMS", OWNER_RE, TAG_RE, CORE_SOURCE = "FailproofAI/policies", CORE_ALIASES;
|
|
24681
|
-
var init_pack_store = __esm(() => {
|
|
24682
|
-
init_pack_manifest();
|
|
24683
|
-
MAX_ARTIFACT_BYTES = 8 * 1024 * 1024;
|
|
24684
|
-
OWNER_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
24685
|
-
TAG_RE = /^[A-Za-z0-9][A-Za-z0-9._/-]{0,127}$/;
|
|
24686
|
-
CORE_ALIASES = new Set(["core", "failproofai", "official"]);
|
|
24687
|
-
});
|
|
24688
|
-
|
|
24689
24730
|
// src/hooks/manager.ts
|
|
24690
24731
|
import { execSync as execSync6 } from "node:child_process";
|
|
24691
24732
|
import { existsSync as existsSync27 } from "node:fs";
|
|
@@ -24951,7 +24992,7 @@ Validated ${validatedHooks.length} custom hook(s) from ${path3}: ${validatedHook
|
|
|
24951
24992
|
} catch (err) {
|
|
24952
24993
|
console.log(`
|
|
24953
24994
|
Warning: could not fetch the policy pack (${err instanceof Error ? err.message : String(err)}).
|
|
24954
|
-
` + `Run \`failproofai policies add
|
|
24995
|
+
` + `Run \`failproofai policies add FailproofAI/policies\` once you are online.`);
|
|
24955
24996
|
}
|
|
24956
24997
|
} else {
|
|
24957
24998
|
for (const name of fromPack) {
|
|
@@ -25690,7 +25731,6 @@ function blockerCleared(attempt, probe) {
|
|
|
25690
25731
|
return probe.cliVersion !== attempt.cliVersion;
|
|
25691
25732
|
case "unsupported_platform":
|
|
25692
25733
|
return probe.cliVersion !== attempt.cliVersion;
|
|
25693
|
-
case "not_a_tty":
|
|
25694
25734
|
case "running_as_sudo":
|
|
25695
25735
|
return true;
|
|
25696
25736
|
default:
|
|
@@ -25703,7 +25743,6 @@ function attemptHintLines(attempt) {
|
|
|
25703
25743
|
daemon_failed: "the failproofaid service could not be started",
|
|
25704
25744
|
cancelled: "it was cancelled",
|
|
25705
25745
|
unsupported_platform: "failproofaid does not run on this platform",
|
|
25706
|
-
not_a_tty: "there was no terminal to ask in",
|
|
25707
25746
|
running_as_sudo: "it was run under sudo"
|
|
25708
25747
|
};
|
|
25709
25748
|
const detail = why[attempt.reason] ?? "it did not finish";
|
|
@@ -27602,6 +27641,13 @@ async function runAuditInner(opts, startedAt) {
|
|
|
27602
27641
|
const sinceMs = parseSinceOpt(opts.since);
|
|
27603
27642
|
const userConfig = readMergedHooksConfig();
|
|
27604
27643
|
const enabledBuiltins = new Set((userConfig.enabledPolicies ?? []).map((n) => normalizePolicyName(n)));
|
|
27644
|
+
try {
|
|
27645
|
+
for (const pack of readInstalledPacks().packs) {
|
|
27646
|
+
const taken = pack.enabled ?? pack.policies.map((entry) => entry.name);
|
|
27647
|
+
for (const name of taken)
|
|
27648
|
+
enabledBuiltins.add(normalizePolicyName(name));
|
|
27649
|
+
}
|
|
27650
|
+
} catch {}
|
|
27605
27651
|
const allTranscripts = [];
|
|
27606
27652
|
for (const cli of clis) {
|
|
27607
27653
|
const adapter = ADAPTERS[cli];
|
|
@@ -27702,6 +27748,7 @@ var TRANSCRIPT_CONCURRENCY = 8;
|
|
|
27702
27748
|
var init_audit = __esm(() => {
|
|
27703
27749
|
init_builtin_policies();
|
|
27704
27750
|
init_hooks_config();
|
|
27751
|
+
init_pack_manifest();
|
|
27705
27752
|
init_types();
|
|
27706
27753
|
init_cli_adapters();
|
|
27707
27754
|
init_detectors();
|
|
@@ -29509,6 +29556,12 @@ async function runPostSetupAudit() {
|
|
|
29509
29556
|
printSummary(result);
|
|
29510
29557
|
process.stdout.write(`
|
|
29511
29558
|
`);
|
|
29559
|
+
if (!hasInstalledPacks()) {
|
|
29560
|
+
process.stdout.write(` ${c(DIM, "none of this is being enforced yet. take ours, or anyone's:")}
|
|
29561
|
+
` + ` ${c(CYAN, `failproofai policies add ${CORE_SOURCE}`)}
|
|
29562
|
+
|
|
29563
|
+
`);
|
|
29564
|
+
}
|
|
29512
29565
|
} finally {
|
|
29513
29566
|
attempt.lock.release();
|
|
29514
29567
|
}
|
|
@@ -29638,6 +29691,8 @@ Two audits write the same cache files, so this one won't start. Try again shortl
|
|
|
29638
29691
|
var DASHBOARD_PORT = 8020, EXIT_AUDIT_ALREADY_RUNNING = 75, AUDIT_STAGES, HELP_DESC_COL = 40, RESET, DIM, BOLD, PINK, GREEN, CYAN, SPINNER;
|
|
29639
29692
|
var init_cli = __esm(() => {
|
|
29640
29693
|
init_audit();
|
|
29694
|
+
init_pack_manifest();
|
|
29695
|
+
init_pack_store();
|
|
29641
29696
|
init_audit_lock();
|
|
29642
29697
|
init_dashboard_cache();
|
|
29643
29698
|
init_hook_telemetry2();
|
|
@@ -29967,17 +30022,11 @@ async function maybeFirstRunConfigure(io = {}, opts = {}) {
|
|
|
29967
30022
|
lock.release();
|
|
29968
30023
|
}
|
|
29969
30024
|
}
|
|
29970
|
-
async function runConfigureWizard(io = {}) {
|
|
30025
|
+
async function runConfigureWizard(io = {}, answers = {}) {
|
|
29971
30026
|
const stdin = io.stdin ?? process.stdin;
|
|
29972
30027
|
const stdout = io.stdout ?? process.stdout;
|
|
29973
30028
|
const cwd = process.cwd();
|
|
29974
|
-
|
|
29975
|
-
stdout.write(`failproofai config needs an interactive terminal.
|
|
29976
|
-
Use the flag form instead, e.g.:
|
|
29977
|
-
failproofai policies --install --scope user --cli claude
|
|
29978
|
-
`);
|
|
29979
|
-
return { applied: false };
|
|
29980
|
-
}
|
|
30029
|
+
const unattended = !stdin.isTTY || !stdout.isTTY;
|
|
29981
30030
|
if (typeof process.getuid === "function" && process.getuid() === 0 && process.env.SUDO_USER) {
|
|
29982
30031
|
stdout.write(`Run failproofai config as ${process.env.SUDO_USER}, not under sudo.
|
|
29983
30032
|
` + `Everything it configures is per-user — hooks, policies and the daemon's own
|
|
@@ -29985,7 +30034,7 @@ Use the flag form instead, e.g.:
|
|
|
29985
30034
|
` + `The one step that needs root (installing the service) asks for your password
|
|
29986
30035
|
on its own.
|
|
29987
30036
|
`);
|
|
29988
|
-
return { applied: false };
|
|
30037
|
+
return { applied: false, abort: "running_as_sudo" };
|
|
29989
30038
|
}
|
|
29990
30039
|
emit("configure_started", {});
|
|
29991
30040
|
if (!isDaemonSupportedPlatform()) {
|
|
@@ -30021,7 +30070,7 @@ ${process.platform} service target.
|
|
|
30021
30070
|
` : `Installing failproofaid — needs sudo once.
|
|
30022
30071
|
|
|
30023
30072
|
`);
|
|
30024
|
-
if (!primeElevation()) {
|
|
30073
|
+
if (!(unattended ? canElevate() : primeElevation())) {
|
|
30025
30074
|
stdout.write(`
|
|
30026
30075
|
Could not get root, so setup stopped before changing anything.
|
|
30027
30076
|
|
|
@@ -30038,7 +30087,7 @@ Could not get root, so setup stopped before changing anything.
|
|
|
30038
30087
|
` + `version — it cannot start a scheduled audit. Refreshing it needs root once.
|
|
30039
30088
|
|
|
30040
30089
|
`);
|
|
30041
|
-
if (!primeElevation()) {
|
|
30090
|
+
if (!(unattended ? canElevate() : primeElevation())) {
|
|
30042
30091
|
stdout.write(`
|
|
30043
30092
|
Could not get root, so the service definition was left as it is.
|
|
30044
30093
|
Everything else is set up as normal; scheduled audits stay off until it is
|
|
@@ -30066,7 +30115,7 @@ refreshed. Re-run \`failproofai config\` once you can use sudo.
|
|
|
30066
30115
|
const clis = [...clisSupportingScope(primaryScope)];
|
|
30067
30116
|
let connect = null;
|
|
30068
30117
|
{
|
|
30069
|
-
const choice = await selectOne2({
|
|
30118
|
+
const choice = unattended ? answers.token ? "key" : "local" : await selectOne2({
|
|
30070
30119
|
message: "Connect this machine to FailproofAI Cloud?",
|
|
30071
30120
|
body: [
|
|
30072
30121
|
" Sessions include prompts, file contents and command output.",
|
|
@@ -30095,7 +30144,12 @@ refreshed. Re-run \`failproofai config\` once you can use sudo.
|
|
|
30095
30144
|
let token = null;
|
|
30096
30145
|
let machineId = resolveMachineId();
|
|
30097
30146
|
let machineLabel2 = existing?.machineLabel ?? resolveMachineLabel();
|
|
30098
|
-
if (existing) {
|
|
30147
|
+
if (existing && unattended && !answers.url) {
|
|
30148
|
+
url = existing.url;
|
|
30149
|
+
token = answers.token ?? existing.token;
|
|
30150
|
+
machineId = answers.machineId ?? existing.machineId;
|
|
30151
|
+
machineLabel2 = answers.machineLabel ?? existing.machineLabel ?? machineLabel2;
|
|
30152
|
+
} else if (existing && !unattended) {
|
|
30099
30153
|
const reuse = await selectOne2({
|
|
30100
30154
|
message: `Use this machine's existing connection to ${existing.url}?`,
|
|
30101
30155
|
choices: [
|
|
@@ -30136,6 +30190,9 @@ Using ${url} (from FAILPROOFAI_CLOUD_URL).
|
|
|
30136
30190
|
url = cloudBaseFor(DEFAULT_INGEST_URL);
|
|
30137
30191
|
}
|
|
30138
30192
|
}
|
|
30193
|
+
if (token === null && unattended) {
|
|
30194
|
+
token = answers.token ?? null;
|
|
30195
|
+
}
|
|
30139
30196
|
if (token === null) {
|
|
30140
30197
|
token = await promptText2({
|
|
30141
30198
|
message: `API key for ${new URL(url).host}`,
|
|
@@ -30151,6 +30208,12 @@ Using ${url} (from FAILPROOFAI_CLOUD_URL).
|
|
|
30151
30208
|
stdout.write(`
|
|
30152
30209
|
Checking the key… `);
|
|
30153
30210
|
const probe = await validateIngestKey({ url: ingestUrlFor(url), key: token });
|
|
30211
|
+
if (!probe.ok && unattended) {
|
|
30212
|
+
stdout.write(`
|
|
30213
|
+
That key did not work: ${probe.reason}
|
|
30214
|
+
`);
|
|
30215
|
+
return { applied: false, abort: "cloud_unverified" };
|
|
30216
|
+
}
|
|
30154
30217
|
if (!probe.ok) {
|
|
30155
30218
|
stdout.write(`
|
|
30156
30219
|
That did not work: ${probe.reason}
|
|
@@ -30179,7 +30242,7 @@ That did not work: ${probe.reason}
|
|
|
30179
30242
|
}
|
|
30180
30243
|
}
|
|
30181
30244
|
}
|
|
30182
|
-
const decision = await selectOne2({
|
|
30245
|
+
const decision = unattended ? "apply" : await selectOne2({
|
|
30183
30246
|
message: "Ready to apply?",
|
|
30184
30247
|
body: reviewLines({
|
|
30185
30248
|
target,
|
|
@@ -30334,6 +30397,14 @@ Hooks keep enforcing; scheduled audits stay off until it is.
|
|
|
30334
30397
|
markLauncherSeen();
|
|
30335
30398
|
clearOnboardingAttempt();
|
|
30336
30399
|
outro2(buildCompletionSummary(policies.length, clis.length, customEnabled, daemonInstalled, connected), { ok: true }, stdout);
|
|
30400
|
+
if (policies.length === 0) {
|
|
30401
|
+
stdout.write(`
|
|
30402
|
+
Nothing is enforcing yet. Take ours, or anyone's:
|
|
30403
|
+
failproofai policies add ${CORE_SOURCE}
|
|
30404
|
+
failproofai policies show <owner>/<repo> (look first)
|
|
30405
|
+
|
|
30406
|
+
`);
|
|
30407
|
+
}
|
|
30337
30408
|
return {
|
|
30338
30409
|
applied: true,
|
|
30339
30410
|
target,
|
|
@@ -30346,6 +30417,7 @@ Hooks keep enforcing; scheduled audits stay off until it is.
|
|
|
30346
30417
|
}
|
|
30347
30418
|
var DIM_NOTE = "(auto-loaded)", MAX_SUMMARY_COLUMNS = 77, POLICY_LIST_INDENT;
|
|
30348
30419
|
var init_configure_wizard = __esm(() => {
|
|
30420
|
+
init_pack_store();
|
|
30349
30421
|
init_tui2();
|
|
30350
30422
|
init_collector_config();
|
|
30351
30423
|
init_integrations();
|
|
@@ -30641,7 +30713,8 @@ __export(exports_pack_cli, {
|
|
|
30641
30713
|
packAddSource: () => packAddSource,
|
|
30642
30714
|
runPackCommand: () => runPackCommand,
|
|
30643
30715
|
runPolicyPicker: () => runPolicyPicker,
|
|
30644
|
-
runPublishCommand: () => runPublishCommand
|
|
30716
|
+
runPublishCommand: () => runPublishCommand,
|
|
30717
|
+
selectionFromForTest: () => selectionFromForTest
|
|
30645
30718
|
});
|
|
30646
30719
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
30647
30720
|
import { homedir as homedir29, tmpdir as tmpdir3 } from "node:os";
|
|
@@ -30657,22 +30730,72 @@ function parseList(rest, flag) {
|
|
|
30657
30730
|
return [];
|
|
30658
30731
|
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
30659
30732
|
}
|
|
30733
|
+
function looksLikeCliName(token) {
|
|
30734
|
+
return !token.startsWith("-") && !token.includes("/");
|
|
30735
|
+
}
|
|
30736
|
+
function cliValueIndices(rest) {
|
|
30737
|
+
const taken = new Set;
|
|
30738
|
+
for (let i = 0;i < rest.length; i += 1) {
|
|
30739
|
+
if (rest[i] !== "--cli" && !rest[i].startsWith("--cli="))
|
|
30740
|
+
continue;
|
|
30741
|
+
if (rest[i].startsWith("--cli="))
|
|
30742
|
+
continue;
|
|
30743
|
+
for (let j = i + 1;j < rest.length && looksLikeCliName(rest[j]); j += 1) {
|
|
30744
|
+
taken.add(j);
|
|
30745
|
+
}
|
|
30746
|
+
}
|
|
30747
|
+
return taken;
|
|
30748
|
+
}
|
|
30660
30749
|
function packAddSource(rest) {
|
|
30661
|
-
const consumed =
|
|
30750
|
+
const consumed = cliValueIndices(rest);
|
|
30662
30751
|
for (let i = 0;i < rest.length; i += 1) {
|
|
30663
30752
|
if (VALUE_FLAGS.has(rest[i]))
|
|
30664
30753
|
consumed.add(i + 1);
|
|
30665
30754
|
}
|
|
30666
|
-
return rest.find((arg, index) => !arg.startsWith("
|
|
30755
|
+
return rest.find((arg, index) => !arg.startsWith("-") && !consumed.has(index));
|
|
30756
|
+
}
|
|
30757
|
+
function parseCliList(rest) {
|
|
30758
|
+
const idx = rest.findIndex((a) => a === "--cli" || a.startsWith("--cli="));
|
|
30759
|
+
if (idx === -1)
|
|
30760
|
+
return {};
|
|
30761
|
+
const values = [];
|
|
30762
|
+
if (rest[idx].startsWith("--cli=")) {
|
|
30763
|
+
values.push(...rest[idx].slice("--cli=".length).split(","));
|
|
30764
|
+
} else {
|
|
30765
|
+
for (let j = idx + 1;j < rest.length && looksLikeCliName(rest[j]); j += 1) {
|
|
30766
|
+
values.push(...rest[j].split(","));
|
|
30767
|
+
}
|
|
30768
|
+
}
|
|
30769
|
+
const names = values.map((v) => v.trim()).filter(Boolean);
|
|
30770
|
+
if (names.length === 0)
|
|
30771
|
+
return { clis: [] };
|
|
30772
|
+
const known = new Set(INTEGRATION_TYPES);
|
|
30773
|
+
const unknown = names.filter((n) => !known.has(n));
|
|
30774
|
+
if (unknown.length > 0) {
|
|
30775
|
+
const near = (bad) => INTEGRATION_TYPES.find((k) => k.startsWith(bad.slice(0, 3)) || bad.startsWith(k.slice(0, 3)));
|
|
30776
|
+
const hint = unknown.map((u) => near(u) ? `${u} (did you mean ${near(u)}?)` : u).join(", ");
|
|
30777
|
+
return {
|
|
30778
|
+
error: [
|
|
30779
|
+
`Not an agent: ${hint}`,
|
|
30780
|
+
`--cli takes any of: ${INTEGRATION_TYPES.join(", ")}`
|
|
30781
|
+
]
|
|
30782
|
+
};
|
|
30783
|
+
}
|
|
30784
|
+
return { clis: names };
|
|
30785
|
+
}
|
|
30786
|
+
function selectionFromForTest(rest) {
|
|
30787
|
+
return selectionFrom(rest);
|
|
30667
30788
|
}
|
|
30668
30789
|
function selectionFrom(rest) {
|
|
30669
30790
|
const only = parseList(rest, "--policy") ?? parseList(rest, "--only");
|
|
30670
30791
|
const categories = parseList(rest, "--category");
|
|
30671
|
-
const
|
|
30792
|
+
const parsedClis = parseCliList(rest);
|
|
30793
|
+
if ("error" in parsedClis)
|
|
30794
|
+
return { error: parsedClis.error };
|
|
30672
30795
|
return {
|
|
30673
30796
|
...only ? { only } : {},
|
|
30674
30797
|
...categories ? { categories } : {},
|
|
30675
|
-
...clis ? { clis } : {},
|
|
30798
|
+
...parsedClis.clis ? { clis: parsedClis.clis } : {},
|
|
30676
30799
|
...rest.includes("--all") ? { all: true } : {}
|
|
30677
30800
|
};
|
|
30678
30801
|
}
|
|
@@ -30728,6 +30851,18 @@ async function build(rest) {
|
|
|
30728
30851
|
"A pack entry calls customPolicies.add({ name, description, match, fn }) for each one."
|
|
30729
30852
|
]);
|
|
30730
30853
|
}
|
|
30854
|
+
const byName = new Map;
|
|
30855
|
+
for (const hook of hooks)
|
|
30856
|
+
byName.set(hook.name, (byName.get(hook.name) ?? 0) + 1);
|
|
30857
|
+
const duplicates = [...byName.entries()].filter(([, n]) => n > 1).map(([name]) => name);
|
|
30858
|
+
if (duplicates.length > 0) {
|
|
30859
|
+
return fail([
|
|
30860
|
+
duplicates.length === 1 ? `Two policies are called ${JSON.stringify(duplicates[0])}.` : `${duplicates.length} names are used by more than one policy: ${duplicates.join(", ")}.`,
|
|
30861
|
+
"A name is what --policy selects and what the picker toggles, so a pack",
|
|
30862
|
+
"cannot carry the same one twice — one of them would be unreachable.",
|
|
30863
|
+
"Rename or delete one of each pair."
|
|
30864
|
+
]);
|
|
30865
|
+
}
|
|
30731
30866
|
const policies = [];
|
|
30732
30867
|
for (const [index, hook] of hooks.entries()) {
|
|
30733
30868
|
const extra = hook;
|
|
@@ -30783,7 +30918,7 @@ async function runPolicyPicker(action, io = {}) {
|
|
|
30783
30918
|
return ok(stack2(emptyState2({
|
|
30784
30919
|
what: "No policies are installed yet.",
|
|
30785
30920
|
hint: "Take ours, or anyone's:",
|
|
30786
|
-
cmd: "failproofai policies add
|
|
30921
|
+
cmd: "failproofai policies add FailproofAI/policies"
|
|
30787
30922
|
}, opts), note2("Someone else's: failproofai policies add <owner>/<repo>", opts), note2("Look first: failproofai policies show <owner>/<repo>", opts)));
|
|
30788
30923
|
}
|
|
30789
30924
|
const rows3 = [];
|
|
@@ -31448,7 +31583,9 @@ async function pickFromSource(source, io) {
|
|
|
31448
31583
|
async function add(rest) {
|
|
31449
31584
|
const source = packAddSource(rest);
|
|
31450
31585
|
const selection = selectionFrom(rest);
|
|
31451
|
-
|
|
31586
|
+
if (selection.error)
|
|
31587
|
+
return fail(selection.error);
|
|
31588
|
+
const resolvedSource = source;
|
|
31452
31589
|
if (!resolvedSource) {
|
|
31453
31590
|
return fail(["Usage: failproofai policies add <source> [--policy a,b] [--category x,y] [--all]"]);
|
|
31454
31591
|
}
|
|
@@ -31630,7 +31767,15 @@ var init_pack_cli = __esm(() => {
|
|
|
31630
31767
|
init_pack_store();
|
|
31631
31768
|
init_custom_hooks_loader();
|
|
31632
31769
|
init_tui2();
|
|
31633
|
-
VALUE_FLAGS = new Set([
|
|
31770
|
+
VALUE_FLAGS = new Set([
|
|
31771
|
+
"--only",
|
|
31772
|
+
"--policy",
|
|
31773
|
+
"--category",
|
|
31774
|
+
"--cli",
|
|
31775
|
+
"--scope",
|
|
31776
|
+
"--custom",
|
|
31777
|
+
"-c"
|
|
31778
|
+
]);
|
|
31634
31779
|
PUBLISH_VALUE_FLAGS = new Set([
|
|
31635
31780
|
"--repo",
|
|
31636
31781
|
"--version",
|
|
@@ -33669,11 +33814,11 @@ var init_uninstall_cli = __esm(() => {
|
|
|
33669
33814
|
// src/hooks/pack-store.ts
|
|
33670
33815
|
var exports_pack_store = {};
|
|
33671
33816
|
__export(exports_pack_store, {
|
|
33672
|
-
CORE_ALIASES: () => CORE_ALIASES2,
|
|
33673
33817
|
CORE_SOURCE: () => CORE_SOURCE2,
|
|
33674
33818
|
PACK_CHECKSUMS_ASSET: () => PACK_CHECKSUMS_ASSET2,
|
|
33675
33819
|
PACK_ENTRY_ASSET: () => PACK_ENTRY_ASSET2,
|
|
33676
33820
|
PACK_MANIFEST_ASSET: () => PACK_MANIFEST_ASSET2,
|
|
33821
|
+
RETIRED_CORE_ALIASES: () => RETIRED_CORE_ALIASES2,
|
|
33677
33822
|
addPack: () => addPack2,
|
|
33678
33823
|
addPackFromSource: () => addPackFromSource,
|
|
33679
33824
|
checkPackArtifact: () => checkPackArtifact2,
|
|
@@ -33717,6 +33862,9 @@ function parsePackSpec2(source) {
|
|
|
33717
33862
|
const tag = at3 > 0 ? withoutScheme.slice(at3 + 1) : null;
|
|
33718
33863
|
const slash = repoPart.indexOf("/");
|
|
33719
33864
|
if (slash <= 0 || slash === repoPart.length - 1) {
|
|
33865
|
+
if (RETIRED_CORE_ALIASES2.has(repoPart.trim().toLowerCase())) {
|
|
33866
|
+
throw new Error(`${JSON.stringify(source)} is no longer a pack name — ours is a pack like anyone else's now. Use ${CORE_SOURCE2} instead.`);
|
|
33867
|
+
}
|
|
33720
33868
|
throw new Error(`pack source must be owner/repo, owner/repo@tag, or a github.com URL — got ${JSON.stringify(source)}`);
|
|
33721
33869
|
}
|
|
33722
33870
|
return validated2(repoPart.slice(0, slash), repoPart.slice(slash + 1), tag);
|
|
@@ -33819,8 +33967,7 @@ function writeAtomic2(target, bytes) {
|
|
|
33819
33967
|
}
|
|
33820
33968
|
}
|
|
33821
33969
|
async function addPackFromSource(source, opts = {}) {
|
|
33822
|
-
const
|
|
33823
|
-
const result = await addPack2(resolved2, opts);
|
|
33970
|
+
const result = await addPack2(source, opts);
|
|
33824
33971
|
return {
|
|
33825
33972
|
id: result.id,
|
|
33826
33973
|
version: result.version,
|
|
@@ -34111,13 +34258,13 @@ function upsertInstalled2(record) {
|
|
|
34111
34258
|
function errText3(err) {
|
|
34112
34259
|
return err instanceof Error ? err.message : String(err);
|
|
34113
34260
|
}
|
|
34114
|
-
var DEFAULT_BASE_URL3 = "https://github.com", FETCH_TIMEOUT_MS2 = 30000, MAX_ARTIFACT_BYTES2, PACK_MANIFEST_ASSET2 = "failproofai-pack.json", PACK_ENTRY_ASSET2 = "failproofai-pack.mjs", PACK_CHECKSUMS_ASSET2 = "SHA256SUMS", OWNER_RE2, TAG_RE2, CORE_SOURCE2 = "FailproofAI/policies",
|
|
34261
|
+
var DEFAULT_BASE_URL3 = "https://github.com", FETCH_TIMEOUT_MS2 = 30000, MAX_ARTIFACT_BYTES2, PACK_MANIFEST_ASSET2 = "failproofai-pack.json", PACK_ENTRY_ASSET2 = "failproofai-pack.mjs", PACK_CHECKSUMS_ASSET2 = "SHA256SUMS", OWNER_RE2, TAG_RE2, CORE_SOURCE2 = "FailproofAI/policies", RETIRED_CORE_ALIASES2;
|
|
34115
34262
|
var init_pack_store2 = __esm(() => {
|
|
34116
34263
|
init_pack_manifest();
|
|
34117
34264
|
MAX_ARTIFACT_BYTES2 = 8 * 1024 * 1024;
|
|
34118
34265
|
OWNER_RE2 = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
34119
34266
|
TAG_RE2 = /^[A-Za-z0-9][A-Za-z0-9._/-]{0,127}$/;
|
|
34120
|
-
|
|
34267
|
+
RETIRED_CORE_ALIASES2 = new Set(["core", "failproofai", "official"]);
|
|
34121
34268
|
});
|
|
34122
34269
|
|
|
34123
34270
|
// src/hooks/install-prompt.ts
|
|
@@ -34819,7 +34966,7 @@ Validated ${validatedHooks.length} custom hook(s) from ${path3}: ${validatedHook
|
|
|
34819
34966
|
} catch (err) {
|
|
34820
34967
|
console.log(`
|
|
34821
34968
|
Warning: could not fetch the policy pack (${err instanceof Error ? err.message : String(err)}).
|
|
34822
|
-
` + `Run \`failproofai policies add
|
|
34969
|
+
` + `Run \`failproofai policies add FailproofAI/policies\` once you are online.`);
|
|
34823
34970
|
}
|
|
34824
34971
|
} else {
|
|
34825
34972
|
for (const name of fromPack) {
|
|
@@ -35098,6 +35245,9 @@ async function listHooks(cwd) {
|
|
|
35098
35245
|
], opts) : nextStep2("failproofai config", config.enabledPolicies.length > 0 ? "These are configured but NOT installed — no hook is running them:" : "Nothing is set up yet. Get started with:", opts));
|
|
35099
35246
|
}
|
|
35100
35247
|
const footer = [note2(`Config: ${globalPolicyConfigFile()}`, opts)];
|
|
35248
|
+
if (packCount === 0) {
|
|
35249
|
+
footer.unshift(nextStep2(`failproofai policies add ${CORE_SOURCE}`, "Nothing is enforcing yet. Take ours, or anyone's:", opts), note2("Someone else's: failproofai policies add <owner>/<repo>", opts), note2("Look first: failproofai policies show <owner>/<repo>", opts));
|
|
35250
|
+
}
|
|
35101
35251
|
if (installedScopes.length > 1) {
|
|
35102
35252
|
footer.push(warning2([
|
|
35103
35253
|
`Hooks in multiple scopes (${installedScopes.join(", ")}).`,
|
|
@@ -35530,6 +35680,12 @@ async function runPostSetupAudit2() {
|
|
|
35530
35680
|
printSummary2(result);
|
|
35531
35681
|
process.stdout.write(`
|
|
35532
35682
|
`);
|
|
35683
|
+
if (!hasInstalledPacks()) {
|
|
35684
|
+
process.stdout.write(` ${c2(DIM2, "none of this is being enforced yet. take ours, or anyone's:")}
|
|
35685
|
+
` + ` ${c2(CYAN2, `failproofai policies add ${CORE_SOURCE}`)}
|
|
35686
|
+
|
|
35687
|
+
`);
|
|
35688
|
+
}
|
|
35533
35689
|
} finally {
|
|
35534
35690
|
attempt.lock.release();
|
|
35535
35691
|
}
|
|
@@ -35659,6 +35815,8 @@ Two audits write the same cache files, so this one won't start. Try again shortl
|
|
|
35659
35815
|
var DASHBOARD_PORT2 = 8020, EXIT_AUDIT_ALREADY_RUNNING2 = 75, AUDIT_STAGES2, HELP_DESC_COL2 = 40, RESET2, DIM2, BOLD2, PINK2, GREEN2, CYAN2, SPINNER2;
|
|
35660
35816
|
var init_cli2 = __esm(() => {
|
|
35661
35817
|
init_audit();
|
|
35818
|
+
init_pack_manifest();
|
|
35819
|
+
init_pack_store();
|
|
35662
35820
|
init_audit_lock();
|
|
35663
35821
|
init_dashboard_cache();
|
|
35664
35822
|
init_hook_telemetry2();
|
|
@@ -36148,7 +36306,7 @@ import { realpathSync as realpathSync6 } from "fs";
|
|
|
36148
36306
|
import { dirname as dirname27, resolve as resolve37 } from "path";
|
|
36149
36307
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
36150
36308
|
// package.json
|
|
36151
|
-
var version = "1.0.2-beta.
|
|
36309
|
+
var version = "1.0.2-beta.6";
|
|
36152
36310
|
|
|
36153
36311
|
// bin/failproofai.mjs
|
|
36154
36312
|
if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
|
|
@@ -36547,10 +36705,10 @@ Run \`failproofai backfill --help\` for usage.`);
|
|
|
36547
36705
|
failproofai pack \u2014 install policy packs published as GitHub releases
|
|
36548
36706
|
|
|
36549
36707
|
Usage:
|
|
36550
|
-
failproofai
|
|
36551
|
-
failproofai
|
|
36552
|
-
failproofai
|
|
36553
|
-
failproofai
|
|
36708
|
+
failproofai policies add FailproofAI/policies the Failproof AI policies
|
|
36709
|
+
failproofai policies add FailproofAI/policies --policy <a,b> just these
|
|
36710
|
+
failproofai policies add FailproofAI/policies --category <x,y> whole categories
|
|
36711
|
+
failproofai policies add FailproofAI/policies --all everything in it
|
|
36554
36712
|
failproofai pack list packs installed here
|
|
36555
36713
|
failproofai pack list <source> what a pack out there contains
|
|
36556
36714
|
failproofai pack add <source> [--policy <a,b>] [--category <x,y>] [--all]
|
|
@@ -36561,7 +36719,7 @@ A pack is one entry artifact plus a manifest, verified against the release's
|
|
|
36561
36719
|
SHA256SUMS at install time. The digest is recorded, and re-verified before every
|
|
36562
36720
|
import \u2014 so a pack cannot change under this machine after you installed it.
|
|
36563
36721
|
|
|
36564
|
-
<source> is
|
|
36722
|
+
<source> is any GitHub repo publishing a pack \u2014 ours included:
|
|
36565
36723
|
acme/support-agent newest release, pinned to its exact tag
|
|
36566
36724
|
acme/support-agent@v2.1.0 that release
|
|
36567
36725
|
github:acme/support-agent@v2.1.0 same, explicit
|
|
@@ -36583,7 +36741,7 @@ rest back on.
|
|
|
36583
36741
|
|
|
36584
36742
|
Examples:
|
|
36585
36743
|
failproofai pack add FailproofAI/policies
|
|
36586
|
-
failproofai
|
|
36744
|
+
failproofai policies add FailproofAI/policies --category sanitize,git
|
|
36587
36745
|
failproofai pack add github:acme/support-agent@v2.1.0 --only block-refunds
|
|
36588
36746
|
failproofai pack remove acme/support-agent
|
|
36589
36747
|
|
|
@@ -37093,7 +37251,7 @@ USAGE
|
|
|
37093
37251
|
failproofai policies add Pick from what is installed here
|
|
37094
37252
|
failproofai policies add <name> Turn one policy on
|
|
37095
37253
|
failproofai policies add <owner>/<repo> Install someone's pack
|
|
37096
|
-
failproofai policies add
|
|
37254
|
+
failproofai policies add FailproofAI/policies Our pack, fetched like any other
|
|
37097
37255
|
failproofai policies remove <name> Turn one policy off
|
|
37098
37256
|
failproofai policies remove <pack-id> Uninstall a pack
|
|
37099
37257
|
failproofai policies show <owner>/<repo> What a pack contains, before you take it
|
|
@@ -37126,7 +37284,7 @@ EXAMPLES
|
|
|
37126
37284
|
failproofai policies add
|
|
37127
37285
|
failproofai policies add block-sudo
|
|
37128
37286
|
failproofai policies add sanitize-api-keys --scope project
|
|
37129
|
-
failproofai policies add
|
|
37287
|
+
failproofai policies add FailproofAI/policies --category sanitize,git
|
|
37130
37288
|
failproofai policies add acme/deploy-guard --policy block-prod-deploy
|
|
37131
37289
|
failproofai policies show acme/deploy-guard
|
|
37132
37290
|
failproofai policies remove block-sudo
|
|
@@ -37139,11 +37297,22 @@ keep enforcing. FAILPROOFAI_PACK_BASE_URL points it all at a mirror.
|
|
|
37139
37297
|
}
|
|
37140
37298
|
const action = subArgs[0];
|
|
37141
37299
|
const rest = subArgs.slice(1);
|
|
37142
|
-
const
|
|
37300
|
+
const VALUE_TAKING = new Set(["--scope", "--policy", "--category", "--only", "--custom", "-c"]);
|
|
37301
|
+
const skip = new Set;
|
|
37302
|
+
for (let i = 0;i < rest.length; i += 1) {
|
|
37303
|
+
if (VALUE_TAKING.has(rest[i]))
|
|
37304
|
+
skip.add(i + 1);
|
|
37305
|
+
if (rest[i] !== "--cli")
|
|
37306
|
+
continue;
|
|
37307
|
+
for (let j = i + 1;j < rest.length && !rest[j].startsWith("-") && !rest[j].includes("/"); j += 1) {
|
|
37308
|
+
skip.add(j);
|
|
37309
|
+
}
|
|
37310
|
+
}
|
|
37311
|
+
const firstPositional = rest.find((a, i) => !a.startsWith("-") && !skip.has(i));
|
|
37143
37312
|
let looksLikeSource = !!firstPositional && (firstPositional.includes("/") || firstPositional.startsWith("github:"));
|
|
37144
37313
|
if (!looksLikeSource && firstPositional) {
|
|
37145
|
-
const {
|
|
37146
|
-
looksLikeSource =
|
|
37314
|
+
const { RETIRED_CORE_ALIASES: RETIRED_CORE_ALIASES3 } = await Promise.resolve().then(() => (init_pack_store2(), exports_pack_store));
|
|
37315
|
+
looksLikeSource = RETIRED_CORE_ALIASES3.has(firstPositional.toLowerCase());
|
|
37147
37316
|
}
|
|
37148
37317
|
if (action === "show" || looksLikeSource) {
|
|
37149
37318
|
if (action === "show" && !firstPositional) {
|
|
@@ -37322,8 +37491,8 @@ EXAMPLES
|
|
|
37322
37491
|
failproofai policies --uninstall --cli pi
|
|
37323
37492
|
failproofai policies -u
|
|
37324
37493
|
failproofai policies --uninstall --custom
|
|
37325
|
-
failproofai
|
|
37326
|
-
failproofai
|
|
37494
|
+
failproofai policies add FailproofAI/policies
|
|
37495
|
+
failproofai policies add FailproofAI/policies --category git,database
|
|
37327
37496
|
failproofai pack list acme/support-agent
|
|
37328
37497
|
failproofai pack build ./my-policies.mjs --id acme/support --version 1.0.0
|
|
37329
37498
|
`.trimStart());
|
|
@@ -37479,19 +37648,38 @@ Run \`failproofai policies --help\` for usage.`);
|
|
|
37479
37648
|
if (args[0] === "config") {
|
|
37480
37649
|
if (args.includes("--help") || args.includes("-h")) {
|
|
37481
37650
|
console.log(`
|
|
37482
|
-
failproofai config \u2014
|
|
37651
|
+
failproofai config \u2014 set this machine up
|
|
37483
37652
|
|
|
37484
37653
|
USAGE
|
|
37485
|
-
failproofai config Guided setup
|
|
37654
|
+
failproofai config Guided setup
|
|
37486
37655
|
failproofai configure Alias for config
|
|
37487
37656
|
failproofai setup Alias for config
|
|
37488
37657
|
|
|
37489
37658
|
WHAT IT DOES
|
|
37490
|
-
|
|
37491
|
-
|
|
37492
|
-
|
|
37493
|
-
|
|
37494
|
-
|
|
37659
|
+
Installs the failproofaid service (needs root once), wires hooks into every
|
|
37660
|
+
agent CLI it supports, and optionally connects this machine to Cloud. It
|
|
37661
|
+
chooses NO policies \u2014 take some with 'failproofai policies add <owner>/<repo>'.
|
|
37662
|
+
|
|
37663
|
+
WITH NO TERMINAL (CI, containers, an agent driving it)
|
|
37664
|
+
It just runs. There is nothing to confirm when nobody is watching, so it
|
|
37665
|
+
applies rather than asking \u2014 no flag needed.
|
|
37666
|
+
|
|
37667
|
+
failproofai config set up, stay local
|
|
37668
|
+
failproofai config --token <key> set up and connect to Cloud
|
|
37669
|
+
--url <url> somewhere other than app.befailproof.ai
|
|
37670
|
+
--machine-id <id> defaults to a stable per-machine key
|
|
37671
|
+
--machine-label <name> defaults to this host's name
|
|
37672
|
+
--no-transcripts decisions only, no session transcripts
|
|
37673
|
+
|
|
37674
|
+
The key can come from FAILPROOFAI_CLOUD_TOKEN instead of the command line,
|
|
37675
|
+
and the url from FAILPROOFAI_CLOUD_URL \u2014 the same variable the daemon reads.
|
|
37676
|
+
Prefer the environment: an argument is readable from 'ps' by every user on
|
|
37677
|
+
the box, and lands in shell history and CI logs.
|
|
37678
|
+
|
|
37679
|
+
Exit 1 if anything it was asked to do did not happen \u2014 including a key the
|
|
37680
|
+
server refused, and a machine that could not reach root. 'sudo' is never
|
|
37681
|
+
prompted for here; it either works without a password or you are told the
|
|
37682
|
+
exact commands to run.
|
|
37495
37683
|
|
|
37496
37684
|
FAILPROOF CLOUD
|
|
37497
37685
|
failproofai config --connect <url> --token <key> [--machine-id <id>]
|
|
@@ -37630,8 +37818,23 @@ PAUSING ENFORCEMENT (one session, always time-boxed)
|
|
|
37630
37818
|
await exitAfterFlush(result2.exitCode);
|
|
37631
37819
|
return;
|
|
37632
37820
|
}
|
|
37821
|
+
const valueFor = (flag) => {
|
|
37822
|
+
const i = args.indexOf(flag);
|
|
37823
|
+
if (i < 0)
|
|
37824
|
+
return;
|
|
37825
|
+
const v = args[i + 1];
|
|
37826
|
+
if (!v || v.startsWith("-"))
|
|
37827
|
+
throw new CliError3(`Missing value after ${flag}.`);
|
|
37828
|
+
return v;
|
|
37829
|
+
};
|
|
37633
37830
|
const { runConfigureWizard: runConfigureWizard2 } = await Promise.resolve().then(() => (init_configure_wizard(), exports_configure_wizard));
|
|
37634
|
-
const result = await runConfigureWizard2(
|
|
37831
|
+
const result = await runConfigureWizard2({}, {
|
|
37832
|
+
token: valueFor("--token") ?? process.env.FAILPROOFAI_CLOUD_TOKEN,
|
|
37833
|
+
url: valueFor("--url") ?? process.env.FAILPROOFAI_CLOUD_URL,
|
|
37834
|
+
machineId: valueFor("--machine-id"),
|
|
37835
|
+
machineLabel: valueFor("--machine-label"),
|
|
37836
|
+
noTranscripts: args.includes("--no-transcripts")
|
|
37837
|
+
});
|
|
37635
37838
|
await track("cli_configure_invoked", {
|
|
37636
37839
|
applied: result.applied,
|
|
37637
37840
|
target: result.target ?? null,
|