failproofai 1.0.6-beta.1 → 1.0.7-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js +4 -4
- 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 +4 -4
- 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 +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- 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 +8 -7
- 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 +7 -5
- 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 +15 -15
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js +6 -6
- 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 +13 -11
- 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 +8 -7
- 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 +7 -7
- 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 +7 -6
- 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 +41 -8
- package/.next/standalone/.next/server/app/settings/page.js +12 -9
- 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/{[externals]__1msfs-h._.js → [externals]__19_pzeq._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__1_bftcl._.js → [externals]__1j-zsg5._.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]__1_r2rbg._.js → [root-of-the-server]__1bf34x4._.js} +5 -7
- package/.next/standalone/.next/server/chunks/_09dz7xv._.js +21 -21
- package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1ek68ln._.js +16 -16
- package/.next/standalone/.next/server/chunks/{_1c3k-8x._.js → _1q5i8mb._.js} +2 -2
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +2 -2
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +2 -2
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_01frwmb._.js +5 -0
- package/.next/standalone/.next/server/chunks/src_hooks_18qtd42._.js +3 -0
- package/.next/standalone/.next/server/chunks/src_hooks_custom-hooks-loader_ts_0lnb3n3._.js +4 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01bmjsj._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__04usis8._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__02npjtd._.js → [root-of-the-server]__056wjo4._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0576iuy._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__059yza8._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0eip4_k._.js +22 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0p-5p8u._.js → [root-of-the-server]__0n0xg95._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qcb0mg._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qxnccm._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__013jr2b._.js → [root-of-the-server]__0rwtwpm._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0s_yomn._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0soxz2z._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0yrsbd_._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0da85px._.js → [root-of-the-server]__11mayhe._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__13d-wb6._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1dinjii._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0cxe_2_._.js → [root-of-the-server]__1mf3zp6._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1x3j9mj._.js → [root-of-the-server]__1p28m0u._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0ftmoxc._.js → [root-of-the-server]__1pprgri._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1p2otjt._.js → [root-of-the-server]__1q4p5b8._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1qiz0e4._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1v185mo._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_042cgl1._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0bqoto4._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_0qlygw6._.js → _0cf1olg._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_0uyu3jf._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_1_ddpm3._.js → _135-c3y._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1feuvhb._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/{_1es2j7i._.js → _1gb0ifp._.js} +5 -5
- package/.next/standalone/.next/server/chunks/ssr/{_1_qswah._.js → _1q46vxx._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_0h_douw._.js → _1w_5l7t._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1zopuov._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +4 -4
- package/.next/standalone/.next/server/chunks/ssr/app_actions_get-scheduled-audit_ts_0ei9sni._.js +3 -0
- 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 +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_settings_02tf1h4._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/lib_utils_ts_0az0sfq._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules_next_dist_0drixxt._.js → node_modules_next_dist_0w6mzq5._.js} +4 -4
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_18_d8l1._.js +151 -0
- package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_095a_79._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_15t8kqj._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_18k8rl0._.js +12 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1fm2w5z._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1j0zy3v._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_pack-cli_ts_0t7me65._.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 +56 -23
- package/.next/standalone/.next/static/chunks/{06fjx8-0_qufs.js → 00i9thnpht4l1.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3ht0xsco46pul.js → 0289oapk24zql.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1qf2uiipatr80.js → 0pyy9m5cm8zku.js} +1 -1
- package/.next/standalone/.next/static/chunks/15heikmy3yjzc.js +1 -0
- package/.next/standalone/.next/static/chunks/285spx855h_3r.css +2 -0
- package/.next/standalone/.next/static/chunks/{02vwr5js1x28j.js → 2plg9qae1yejv.js} +1 -1
- package/.next/standalone/.next/static/chunks/2qv4hshejedtx.css +1 -0
- package/.next/standalone/.next/static/chunks/{2x776s0x3n_tu.js → 2tqszh84mp_2l.js} +1 -1
- package/.next/standalone/.next/static/chunks/34jpdb2xjm-x7.js +1 -0
- package/.next/standalone/.next/static/chunks/3cdkxrukvlvul.js +1 -0
- package/.next/standalone/.next/static/chunks/3dnd-_29uiaj3.js +6 -0
- package/.next/standalone/.next/static/chunks/{0y227_ohdqwvn.js → 3vp7c3knkh8mr.js} +1 -1
- package/.next/standalone/.next/static/chunks/{27_bwbg8sv6vj.js → 3vwnzfx9q80yl.js} +1 -1
- package/.next/standalone/app/actions/get-jev-config.ts +409 -0
- package/.next/standalone/app/actions/pack-actions.ts +12 -0
- package/.next/standalone/app/actions/update-jev-config.ts +420 -0
- package/.next/standalone/app/components/jev-notices.tsx +96 -0
- package/.next/standalone/app/policies/hooks-client.tsx +9 -0
- package/.next/standalone/app/settings/jev-panel.tsx +469 -0
- package/.next/standalone/app/settings/page.tsx +20 -1
- package/.next/standalone/app/settings/settings-client.tsx +27 -1
- package/.next/standalone/app/settings/settings.css +79 -0
- package/.next/standalone/fp-cloud-cli/CHANGELOG.md +51 -3
- package/.next/standalone/fp-cloud-cli/README.md +1 -1
- package/.next/standalone/fp-cloud-cli/fp_cli/_version.py +1 -1
- package/.next/standalone/fp-cloud-cli/fp_cli/client.py +44 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/incidents_cmds.py +226 -5
- package/.next/standalone/fp-cloud-cli/fp_cli/models.py +11 -0
- package/.next/standalone/fp-cloud-cli/fp_cli/output.py +68 -1
- package/.next/standalone/fp-cloud-cli/pyproject.toml +1 -1
- package/.next/standalone/fp-cloud-cli/skill/SKILL.md +17 -2
- package/.next/standalone/fp-cloud-cli/skill/references/commands.md +7 -4
- package/.next/standalone/fp-cloud-cli/tests/test_alerting.py +184 -0
- package/.next/standalone/fp-cloud-cli/uv.lock +3 -3
- package/.next/standalone/package.json +11 -11
- package/.next/standalone/sdk/python/CHANGELOG.md +7 -0
- package/.next/standalone/sdk/python/failproofai_sdk/_version.py +1 -1
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/__init__.py +2 -0
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/authoring.py +24 -1
- package/.next/standalone/sdk/python/failproofai_sdk/evaluator/runtime.py +41 -6
- package/.next/standalone/sdk/python/tests/test_evaluator_runtime.py +88 -0
- package/.next/standalone/sdk/python/uv.lock +15 -15
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +115 -2
- package/dist/cli.mjs +10982 -3787
- package/dist/index.js +19 -1
- package/dist/worker.mjs +13761 -7834
- package/package.json +11 -11
- package/pi-extension/index.ts +11 -0
- package/scripts/build-policy-pack.mjs +39 -2
- package/src/hooks/builtin-policies.ts +21 -3
- package/src/hooks/cloud-enrollment-cli.ts +1 -1
- package/src/hooks/cloud-managed-policies.ts +22 -0
- package/src/hooks/custom-hooks-loader.ts +45 -7
- package/src/hooks/custom-hooks-registry.ts +45 -1
- package/src/hooks/daemon-client.ts +128 -50
- package/src/hooks/daemon-service.ts +30 -0
- package/src/hooks/effective-reviewers.ts +79 -0
- package/src/hooks/first-run-gate.ts +5 -0
- package/src/hooks/fp-home.ts +23 -0
- package/src/hooks/handler.ts +265 -6
- package/src/hooks/hook-activity-store.ts +105 -1
- package/src/hooks/hook-telemetry.ts +41 -0
- package/src/hooks/integrations.ts +3 -2
- package/src/hooks/jev-activity.ts +385 -0
- package/src/hooks/jev-cli.ts +1193 -0
- package/src/hooks/loader-utils.ts +6 -0
- package/src/hooks/manager.ts +4 -4
- package/src/hooks/pack-cli.ts +237 -11
- package/src/hooks/pack-manifest.ts +479 -7
- package/src/hooks/pack-store.ts +156 -10
- package/src/hooks/policy-authority.ts +333 -0
- package/src/hooks/policy-catalog.ts +65 -0
- package/src/hooks/policy-evaluator.ts +940 -796
- package/src/hooks/policy-registry.ts +25 -0
- package/src/hooks/policy-reviewability.ts +229 -0
- package/src/hooks/policy-types.ts +126 -0
- package/src/hooks/semantic/combine.ts +541 -0
- package/src/hooks/semantic/compile.ts +176 -0
- package/src/hooks/semantic/decide.ts +392 -0
- package/src/hooks/semantic/envelope.ts +1296 -0
- package/src/hooks/semantic/evaluator.ts +547 -0
- package/src/hooks/semantic/facts.ts +292 -0
- package/src/hooks/semantic/intent.ts +1190 -0
- package/src/hooks/semantic/jev-client.ts +643 -0
- package/src/hooks/semantic/jev-config.ts +594 -0
- package/src/hooks/semantic/jev-review.ts +374 -0
- package/src/hooks/semantic/jev-stats.ts +289 -0
- package/src/hooks/semantic/jev-throttle.ts +421 -0
- package/src/hooks/semantic/pack-policies.ts +251 -0
- package/src/hooks/semantic/policies.ts +596 -0
- package/src/hooks/semantic/precondition-names.ts +60 -0
- package/src/hooks/semantic/preconditions.ts +58 -0
- package/src/hooks/semantic/redact.ts +2910 -0
- package/src/hooks/semantic/types.ts +145 -0
- package/src/hooks/semver-precedence.ts +128 -0
- package/src/hooks/worker-server.ts +119 -26
- package/src/index.ts +6 -0
- package/.next/standalone/.next/server/chunks/src_hooks_0iu54mz._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0-_ki57._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__01wy8d-._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0cg-bgc._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0cpu_mj._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u3w0ll._.js +0 -22
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__17d_ffl._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__19d9tgz._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ctpynv._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1jiwfsj._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1phc187._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_06imw3p._.js +0 -5
- package/.next/standalone/.next/server/chunks/ssr/_1u8-lu2._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1cv9_c4._.js +0 -10
- package/.next/standalone/.next/static/chunks/0cbvytf8epn34.js +0 -1
- package/.next/standalone/.next/static/chunks/0qmsunv04x4na.css +0 -2
- package/.next/standalone/.next/static/chunks/1p4xauf-zu6zk.js +0 -1
- package/.next/standalone/.next/static/chunks/1uc_s_dftgxiq.js +0 -6
- package/.next/standalone/.next/static/chunks/2k9f4tyv04809.css +0 -1
- package/.next/standalone/.next/static/chunks/2lvr2pt70qn14.js +0 -1
- /package/.next/standalone/.next/static/{QCNYz2yCxxdeggg1k0iEq → qbVI48kgBOf9CTOKcFIk2}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{QCNYz2yCxxdeggg1k0iEq → qbVI48kgBOf9CTOKcFIk2}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{QCNYz2yCxxdeggg1k0iEq → qbVI48kgBOf9CTOKcFIk2}/_ssgManifest.js +0 -0
|
@@ -191,7 +191,13 @@ export async function createEsmShim(
|
|
|
191
191
|
const shimCode = [
|
|
192
192
|
`import _cjs from '${distUrl}';`,
|
|
193
193
|
`export const customPolicies = _cjs.customPolicies;`,
|
|
194
|
+
// Every name a policy file may import has to be listed HERE as well as in
|
|
195
|
+
// `src/index.ts`: this shim is an ESM module, so importing a name it does not
|
|
196
|
+
// declare is a link-time error, not an undefined at runtime. A pack entry
|
|
197
|
+
// calling `semanticPolicies.add` would fail to load entirely.
|
|
198
|
+
`export const semanticPolicies = _cjs.semanticPolicies;`,
|
|
194
199
|
`export const getCustomHooks = _cjs.getCustomHooks;`,
|
|
200
|
+
`export const getSemanticRegistrations = _cjs.getSemanticRegistrations;`,
|
|
195
201
|
`export const clearCustomHooks = _cjs.clearCustomHooks;`,
|
|
196
202
|
`export const allow = _cjs.allow;`,
|
|
197
203
|
`export const deny = _cjs.deny;`,
|
package/src/hooks/manager.ts
CHANGED
|
@@ -29,7 +29,7 @@ import { CORE_SOURCE, addPack, setPackPolicyEnabled } from "./pack-store";
|
|
|
29
29
|
import type { ResolvedPack } from "./pack-manifest";
|
|
30
30
|
import { hasInstalledPacks, readInstalledPacks } from "./pack-manifest";
|
|
31
31
|
import { packPolicyParamKey } from "./policy-evaluator";
|
|
32
|
-
import {
|
|
32
|
+
import { probeDaemonPolicyEvaluation } from "./daemon-service";
|
|
33
33
|
import {
|
|
34
34
|
chip,
|
|
35
35
|
note,
|
|
@@ -477,12 +477,12 @@ async function installHooksImpl(
|
|
|
477
477
|
const daemonRequiredBy = selectedIntegrations
|
|
478
478
|
.map(({ integration }) => integration)
|
|
479
479
|
.filter((integration) => integration.requiresHealthyDaemon);
|
|
480
|
-
if (daemonRequiredBy.length > 0 && !(await
|
|
480
|
+
if (daemonRequiredBy.length > 0 && !(await probeDaemonPolicyEvaluation())) {
|
|
481
481
|
const names = daemonRequiredBy.map((integration) => integration.displayName).join(", ");
|
|
482
482
|
const verb = daemonRequiredBy.length === 1 ? "requires" : "require";
|
|
483
483
|
throw new CliError(
|
|
484
|
-
`${names} ${verb} a
|
|
485
|
-
"Run `failproofai config` to install
|
|
484
|
+
`${names} ${verb} a compatible failproofaid daemon with native policy evaluation before FailproofAI enforcement can be enabled.\n` +
|
|
485
|
+
"Run `failproofai config` to install or update the daemon, then retry.",
|
|
486
486
|
);
|
|
487
487
|
}
|
|
488
488
|
|
package/src/hooks/pack-cli.ts
CHANGED
|
@@ -13,7 +13,20 @@ import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "nod
|
|
|
13
13
|
import { INTEGRATION_TYPES } from "./types";
|
|
14
14
|
import { PACK_COMMIT_RE, PACK_VERSION_RE } from "./pack-manifest";
|
|
15
15
|
import { detectInstalledClis } from "./integrations";
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
packParamsProblem,
|
|
18
|
+
parsePackIdentity,
|
|
19
|
+
parsePackPolicy,
|
|
20
|
+
parsePackSemanticPolicy,
|
|
21
|
+
readInstalledPacks,
|
|
22
|
+
type SemanticManifestEntry,
|
|
23
|
+
} from "./pack-manifest";
|
|
24
|
+
import { parseSemver } from "./semver-precedence";
|
|
25
|
+
// The build step measures a pack's question set against the envelope's own
|
|
26
|
+
// budget, which means reaching the semantic side. This is a CLI module — loaded
|
|
27
|
+
// by `failproofai publish`, never by a hook — so the rule that keeps those
|
|
28
|
+
// modules off an unconfigured machine's hook path does not apply here.
|
|
29
|
+
import { MAX_PACK_QUESTION_CHARS, questionChars } from "./semantic/pack-policies";
|
|
17
30
|
import {
|
|
18
31
|
AmbiguousPackId,
|
|
19
32
|
PACK_CHECKSUMS_ASSET,
|
|
@@ -30,6 +43,9 @@ import {
|
|
|
30
43
|
} from "./pack-store";
|
|
31
44
|
import type { PolicyEffect } from "./cloud-managed-policies";
|
|
32
45
|
import { loadCustomHooks } from "./custom-hooks-loader";
|
|
46
|
+
import { getSemanticRegistrations } from "./custom-hooks-registry";
|
|
47
|
+
import { authorityFieldsOf, authorityProblem } from "./policy-authority";
|
|
48
|
+
import type { SemanticPolicyDeclaration } from "./policy-types";
|
|
33
49
|
import type { MultiChoice, TTYIn, TTYOut } from "./tui";
|
|
34
50
|
import {
|
|
35
51
|
chip,
|
|
@@ -294,12 +310,23 @@ async function build(rest: string[]): Promise<PackCliResult> {
|
|
|
294
310
|
const version = flag("version");
|
|
295
311
|
const commit = flag("commit");
|
|
296
312
|
const effect = flag("effect") ?? "enforce";
|
|
313
|
+
const minCliVersion = flag("min-cli-version");
|
|
297
314
|
const outDir = resolve(flag("out") ?? "dist-pack");
|
|
298
315
|
|
|
299
316
|
if (!entry || !id || !version) {
|
|
300
317
|
return fail([
|
|
301
318
|
"Usage: failproofai publish <entry.mjs> --repo <owner>/<repo> --version <version>",
|
|
302
|
-
" [--out <dir>] [--effect enforce|observe]",
|
|
319
|
+
" [--out <dir>] [--effect enforce|observe] [--min-cli-version <version>]",
|
|
320
|
+
]);
|
|
321
|
+
}
|
|
322
|
+
// Refused here rather than written and refused by every machine that installs
|
|
323
|
+
// it. A `minCliVersion` nobody can order is a requirement nobody can check,
|
|
324
|
+
// and the loader treats one as grounds to refuse the whole pack — so a typo
|
|
325
|
+
// published here is a pack that installs nowhere.
|
|
326
|
+
if (minCliVersion !== undefined && parseSemver(minCliVersion) === null) {
|
|
327
|
+
return fail([
|
|
328
|
+
`--min-cli-version ${JSON.stringify(minCliVersion)} is not a version that can be compared.`,
|
|
329
|
+
"It must be plain semver — 1.0.7, or 1.0.7-beta.0 for a pre-release.",
|
|
303
330
|
]);
|
|
304
331
|
}
|
|
305
332
|
let identity: { id: string; version: string; effect: PolicyEffect; commit?: string };
|
|
@@ -324,15 +351,25 @@ async function build(rest: string[]): Promise<PackCliResult> {
|
|
|
324
351
|
}
|
|
325
352
|
|
|
326
353
|
let hooks;
|
|
354
|
+
let semanticDeclarations: SemanticPolicyDeclaration[];
|
|
327
355
|
try {
|
|
328
356
|
hooks = await loadCustomHooks(entryPath, { strict: true });
|
|
357
|
+
// Read AFTER the load, from the same registration pass `clearCustomHooks`
|
|
358
|
+
// resets: a semantic policy has no `fn`, so it never travels back through
|
|
359
|
+
// `loadCustomHooks`'s return value.
|
|
360
|
+
semanticDeclarations = [...getSemanticRegistrations()];
|
|
329
361
|
} catch (err) {
|
|
330
362
|
return fail([`Could not load ${entryPath}: ${err instanceof Error ? err.message : String(err)}`]);
|
|
331
363
|
}
|
|
332
|
-
|
|
364
|
+
// BOTH registries, or a pack that ships only Jev questions — a legitimate
|
|
365
|
+
// thing to publish, since the regex floor may already be somebody else's pack
|
|
366
|
+
// — is refused for registering nothing.
|
|
367
|
+
if (hooks.length === 0 && semanticDeclarations.length === 0) {
|
|
333
368
|
return fail([
|
|
334
369
|
`${entryPath} registered no policies.`,
|
|
335
|
-
"A pack entry calls customPolicies.add({ name, description, match, fn }) for each one
|
|
370
|
+
"A pack entry calls customPolicies.add({ name, description, match, fn }) for each one,",
|
|
371
|
+
"and semanticPolicies.add({ name, title, appliesTo, mode, userCanOverride, probes, guidance })",
|
|
372
|
+
"for each Jev question set.",
|
|
336
373
|
]);
|
|
337
374
|
}
|
|
338
375
|
|
|
@@ -359,6 +396,66 @@ async function build(rest: string[]): Promise<PackCliResult> {
|
|
|
359
396
|
]);
|
|
360
397
|
}
|
|
361
398
|
|
|
399
|
+
// The pack's Jev question sets, validated with the LOADER's own rules — the
|
|
400
|
+
// same deal `parsePackPolicy` gets above, so a semantic policy that could
|
|
401
|
+
// never load fails here, where the author can fix it.
|
|
402
|
+
const semantic: SemanticManifestEntry[] = [];
|
|
403
|
+
for (const [index, declaration] of semanticDeclarations.entries()) {
|
|
404
|
+
try {
|
|
405
|
+
const parsed = parsePackSemanticPolicy(identity.id, declaration, index);
|
|
406
|
+
// Same reason two regex policies cannot share a name, one layer over: the
|
|
407
|
+
// answer map is keyed `<policy>.<probe>`, so a duplicate would overwrite
|
|
408
|
+
// the first policy's questions in the compiled request.
|
|
409
|
+
if (semantic.some((s) => s.name === parsed.name)) {
|
|
410
|
+
throw new Error(`two semantic policies are called ${JSON.stringify(parsed.name)}`);
|
|
411
|
+
}
|
|
412
|
+
semantic.push(parsed);
|
|
413
|
+
} catch (err) {
|
|
414
|
+
return fail([err instanceof Error ? err.message : String(err)]);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// The question budget, checked against what one Jev request can carry. A pack
|
|
419
|
+
// over it installs and then has its overflow policies dropped one by one on
|
|
420
|
+
// the user's machine, in manifest order — a pack that enforces less than it
|
|
421
|
+
// says, which is the failure this whole lane is built to avoid.
|
|
422
|
+
const questionCost = semantic.reduce((total, entry) => total + questionChars(entry), 0);
|
|
423
|
+
if (questionCost > MAX_PACK_QUESTION_CHARS) {
|
|
424
|
+
return fail([
|
|
425
|
+
`This pack's ${semantic.length} semantic policies compile to ${questionCost} characters of questions, ` +
|
|
426
|
+
`over the ${MAX_PACK_QUESTION_CHARS} one Jev request has room for.`,
|
|
427
|
+
"Shorten the probe instructions and criteria, or ship fewer policies per pack.",
|
|
428
|
+
]);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// An authority declaration the manifest would lose, or that registration
|
|
432
|
+
// would quietly downgrade to hard, is refused HERE. Every machine that
|
|
433
|
+
// installs the pack would otherwise make the policy hard with only a warning
|
|
434
|
+
// in its own log — the author's decision, silently not taking effect — while
|
|
435
|
+
// at build time nothing is installed yet and refusing costs nobody anything.
|
|
436
|
+
// The same rule `scripts/build-policy-pack.mjs` applies to the core pack.
|
|
437
|
+
//
|
|
438
|
+
// Judged against the reviewers the PACK SHIPS WITH, not this build's: a pack
|
|
439
|
+
// that carries both tiers replaces the compiled semantic set on every machine
|
|
440
|
+
// that installs it, so `reviewedBy: ["its-own-check"]` is exactly right and
|
|
441
|
+
// the builtin list would call it a name "this build does not have". A pack
|
|
442
|
+
// with no semantic entries still answers to the builtin set, which is what its
|
|
443
|
+
// machines will be running.
|
|
444
|
+
const reviewers = semantic.length > 0 ? new Set(semantic.map((s) => s.name)) : undefined;
|
|
445
|
+
const authorityProblems = hooks.flatMap((hook) => {
|
|
446
|
+
const problem = authorityProblem({ authority: hook.authority, reviewedBy: hook.reviewedBy }, reviewers);
|
|
447
|
+
return problem ? [` ${hook.name}: ${problem}`] : [];
|
|
448
|
+
});
|
|
449
|
+
if (authorityProblems.length > 0) {
|
|
450
|
+
return fail([
|
|
451
|
+
authorityProblems.length === 1
|
|
452
|
+
? "One policy declares an authority this build cannot publish:"
|
|
453
|
+
: `${authorityProblems.length} policies declare an authority this build cannot publish:`,
|
|
454
|
+
...authorityProblems,
|
|
455
|
+
'Fix the declaration, or leave authority out and the policy is "hard". See https://docs.befailproof.ai/policies/authority',
|
|
456
|
+
]);
|
|
457
|
+
}
|
|
458
|
+
|
|
362
459
|
const policies: unknown[] = [];
|
|
363
460
|
for (const [index, hook] of hooks.entries()) {
|
|
364
461
|
// `category` and `defaultEnabled` are pack-manifest fields a plain custom
|
|
@@ -367,13 +464,36 @@ async function build(rest: string[]): Promise<PackCliResult> {
|
|
|
367
464
|
// FALSE: a pack's declared defaults are what `pack add` switches on with no
|
|
368
465
|
// flags, and switching on a stranger's every policy unattended is the
|
|
369
466
|
// installer opinion this lane already refused once.
|
|
370
|
-
const extra = hook as unknown as { category?: unknown; defaultEnabled?: unknown };
|
|
467
|
+
const extra = hook as unknown as { category?: unknown; defaultEnabled?: unknown; params?: unknown };
|
|
468
|
+
// `params` is read off the registration for the same reason, and leaving it
|
|
469
|
+
// out was not cosmetic: `registerPolicy` takes a pack policy's schema from the
|
|
470
|
+
// MANIFEST by name, so a pack published without it evaluates every one of its
|
|
471
|
+
// policies with `ctx.params = {}` — which discards the user's own configured
|
|
472
|
+
// values too, not just the defaults. Of the 38 builtins, 19 carry a schema:
|
|
473
|
+
// `prefer-package-manager` would have been inert, and `block-sudo`'s
|
|
474
|
+
// `allowPatterns`, `block-rm-rf`'s and `block-read-outside-cwd`'s `allowPaths`
|
|
475
|
+
// would all have stopped working — every one of them failing silently
|
|
476
|
+
// STRICTER, which is the direction users work around instead of reporting.
|
|
477
|
+
const paramsProblem = packParamsProblem(extra.params);
|
|
478
|
+
if (paramsProblem) {
|
|
479
|
+
return fail([
|
|
480
|
+
`${hook.name} declares a params schema this build cannot publish: ${paramsProblem}`,
|
|
481
|
+
"A machine reads the schema from the manifest, so an unusable one is a policy",
|
|
482
|
+
"that quietly runs without its parameters. Fix the declaration, or leave params out.",
|
|
483
|
+
]);
|
|
484
|
+
}
|
|
371
485
|
const candidate = {
|
|
372
486
|
name: hook.name,
|
|
373
487
|
description: hook.description ?? "",
|
|
374
488
|
category: typeof extra.category === "string" && extra.category ? extra.category : "General",
|
|
375
489
|
defaultEnabled: extra.defaultEnabled === true,
|
|
376
490
|
match: hook.match ?? {},
|
|
491
|
+
...(extra.params !== undefined ? { params: extra.params } : {}),
|
|
492
|
+
// Whether Jev may clear this policy's verdict. A pack's MANIFEST is what
|
|
493
|
+
// a machine reads it from, so a declaration left on the registration
|
|
494
|
+
// alone would be published as nothing — silently hard. Checked above, so
|
|
495
|
+
// everything declared here is shape-valid; absent stays absent.
|
|
496
|
+
...authorityFieldsOf(hook as unknown as Record<string, unknown>),
|
|
377
497
|
};
|
|
378
498
|
try {
|
|
379
499
|
policies.push(parsePackPolicy(identity.id, candidate, index));
|
|
@@ -386,14 +506,21 @@ async function build(rest: string[]): Promise<PackCliResult> {
|
|
|
386
506
|
// The manifest is hashed and the hash is the pin, so every byte in here is
|
|
387
507
|
// part of what a machine verifies — a field carrying "there was nothing to
|
|
388
508
|
// say" earns none of that cost. Readers already treat absence as ordinary.
|
|
509
|
+
//
|
|
510
|
+
// `minCliVersion` and `semantic` follow the same rule, and for `semantic` it is
|
|
511
|
+
// load-bearing rather than tidy: an EMPTY array would still be "a pack that
|
|
512
|
+
// declares semantic entries" to a careless reader, and the replacement rule
|
|
513
|
+
// turns that into "this pack replaced the compiled-in set with nothing".
|
|
389
514
|
const manifest =
|
|
390
515
|
JSON.stringify(
|
|
391
516
|
{
|
|
392
517
|
id: identity.id,
|
|
393
518
|
version: identity.version,
|
|
519
|
+
...(minCliVersion ? { minCliVersion } : {}),
|
|
394
520
|
effect: identity.effect,
|
|
395
521
|
...(identity.commit ? { commit: identity.commit } : {}),
|
|
396
522
|
policies,
|
|
523
|
+
...(semantic.length > 0 ? { semantic } : {}),
|
|
397
524
|
},
|
|
398
525
|
null,
|
|
399
526
|
2,
|
|
@@ -413,6 +540,16 @@ async function build(rest: string[]): Promise<PackCliResult> {
|
|
|
413
540
|
const on = policies.filter((p) => (p as { defaultEnabled?: boolean }).defaultEnabled).length;
|
|
414
541
|
return ok([
|
|
415
542
|
`Built ${identity.id}@${identity.version} — ${policies.length} policies, ${on} on by default.`,
|
|
543
|
+
// Said separately, and said at all, because this half replaces the compiled-in
|
|
544
|
+
// semantic set on every machine that installs the pack. An author who did not
|
|
545
|
+
// mean to ship it should find that out here.
|
|
546
|
+
...(semantic.length > 0
|
|
547
|
+
? [
|
|
548
|
+
` ${semantic.length} semantic ${semantic.length === 1 ? "policy" : "policies"} for Jev ` +
|
|
549
|
+
`(${questionCost} characters of questions), replacing this build's own set where it installs.`,
|
|
550
|
+
]
|
|
551
|
+
: []),
|
|
552
|
+
...(minCliVersion ? [` Requires failproofai ${minCliVersion} or newer.`] : []),
|
|
416
553
|
` ${outDir}/${PACK_MANIFEST_ASSET}`,
|
|
417
554
|
` ${outDir}/${PACK_ENTRY_ASSET}`,
|
|
418
555
|
` ${outDir}/${PACK_CHECKSUMS_ASSET}`,
|
|
@@ -999,7 +1136,13 @@ function inferTaggedVersion(entryPath: string, sources?: string[]): string | nul
|
|
|
999
1136
|
* Identified by CONTENT, not by filename. A name convention would either miss
|
|
1000
1137
|
* `guards.mjs` or match an unrelated `policies.mjs` that configures something
|
|
1001
1138
|
* else entirely; a file that imports `failproofai` and calls
|
|
1002
|
-
* `customPolicies.add` is a policy file whatever
|
|
1139
|
+
* `customPolicies.add` — or `semanticPolicies.add` — is a policy file whatever
|
|
1140
|
+
* it is called.
|
|
1141
|
+
*
|
|
1142
|
+
* Both registries count, and leaving one out was not a cosmetic gap: a file that
|
|
1143
|
+
* registers only Jev questions was undiscoverable, so `failproofai publish` in
|
|
1144
|
+
* its own directory reported no policy file at all rather than reaching the
|
|
1145
|
+
* "registered no policies" check and explaining itself.
|
|
1003
1146
|
*
|
|
1004
1147
|
* Returns ALL of them. Splitting policies across files is the normal thing to
|
|
1005
1148
|
* do past about three, and they are one pack — so several files is an answer,
|
|
@@ -1008,8 +1151,11 @@ function inferTaggedVersion(entryPath: string, sources?: string[]): string | nul
|
|
|
1008
1151
|
*
|
|
1009
1152
|
* Non-recursive on purpose. Walking the tree finds fixtures, examples and
|
|
1010
1153
|
* anything vendored, and publishing those is the failure this avoids.
|
|
1154
|
+
*
|
|
1155
|
+
* Takes the directory rather than reading `process.cwd()` so the discovery rule
|
|
1156
|
+
* can be tested against a real folder without a `chdir`.
|
|
1011
1157
|
*/
|
|
1012
|
-
function findEntry(dir: string): string[] {
|
|
1158
|
+
export function findEntry(dir: string): string[] {
|
|
1013
1159
|
let names: string[];
|
|
1014
1160
|
try {
|
|
1015
1161
|
names = readdirSync(dir);
|
|
@@ -1025,7 +1171,10 @@ function findEntry(dir: string): string[] {
|
|
|
1025
1171
|
// would be the expensive half of this scan.
|
|
1026
1172
|
if (statSync(full).size > 512 * 1024) continue;
|
|
1027
1173
|
const text = readFileSync(full, "utf8");
|
|
1028
|
-
if (
|
|
1174
|
+
if (
|
|
1175
|
+
/from\s+["']failproofai["']/.test(text) &&
|
|
1176
|
+
/(?:customPolicies|semanticPolicies)\s*\.\s*add\s*\(/.test(text)
|
|
1177
|
+
) {
|
|
1029
1178
|
found.push(full);
|
|
1030
1179
|
}
|
|
1031
1180
|
} catch {
|
|
@@ -1727,7 +1876,7 @@ function bundleEntry(
|
|
|
1727
1876
|
*/
|
|
1728
1877
|
const PUBLISH_VALUE_FLAGS = new Set([
|
|
1729
1878
|
"--repo", "--version", "--id", "--tag", "--notes", "--out", "--effect", "--entry", "--init",
|
|
1730
|
-
"--commit",
|
|
1879
|
+
"--commit", "--min-cli-version",
|
|
1731
1880
|
]);
|
|
1732
1881
|
function publishEntryArg(rest: string[]): string | undefined {
|
|
1733
1882
|
const consumed = new Set<number>();
|
|
@@ -2129,6 +2278,7 @@ async function publish(rest: string[]): Promise<PackCliResult> {
|
|
|
2129
2278
|
...(provenance && !provenance.dirty ? ["--commit", provenance.sha] : []),
|
|
2130
2279
|
...outFlagFrom(rest),
|
|
2131
2280
|
...effectFlagFrom(rest),
|
|
2281
|
+
...minCliVersionFlagFrom(rest),
|
|
2132
2282
|
]);
|
|
2133
2283
|
if (built.exitCode !== 0) return built;
|
|
2134
2284
|
|
|
@@ -2292,6 +2442,18 @@ function effectFlagFrom(rest: string[]): string[] {
|
|
|
2292
2442
|
if (i === -1) return [];
|
|
2293
2443
|
return rest[i].includes("=") ? [rest[i]] : [rest[i], rest[i + 1]];
|
|
2294
2444
|
}
|
|
2445
|
+
/**
|
|
2446
|
+
* Same again for `--min-cli-version`, and it has to be forwarded explicitly
|
|
2447
|
+
* because `publish` hands `build` a list it assembles rather than its own
|
|
2448
|
+
* `rest`. Left off, the flag parsed, validated, refused an uncomparable value —
|
|
2449
|
+
* and then never reached the manifest, so the field the whole ordering
|
|
2450
|
+
* constraint rests on was silently absent from the published artifact.
|
|
2451
|
+
*/
|
|
2452
|
+
function minCliVersionFlagFrom(rest: string[]): string[] {
|
|
2453
|
+
const i = rest.findIndex((a) => a === "--min-cli-version" || a.startsWith("--min-cli-version="));
|
|
2454
|
+
if (i === -1) return [];
|
|
2455
|
+
return rest[i].includes("=") ? [rest[i]] : [rest[i], rest[i + 1]];
|
|
2456
|
+
}
|
|
2295
2457
|
|
|
2296
2458
|
/** `failproofai publish` — exported for bin/failproofai.mjs and the tests. */
|
|
2297
2459
|
export async function runPublishCommand(rest: string[]): Promise<PackCliResult> {
|
|
@@ -2345,11 +2507,35 @@ async function pickClis(
|
|
|
2345
2507
|
* Returns null when the user cancelled, which must not be confused with an
|
|
2346
2508
|
* empty selection: one means "install nothing", the other means "do nothing".
|
|
2347
2509
|
*/
|
|
2510
|
+
/**
|
|
2511
|
+
* How a pack's semantic half is named to a person: "3 Jev checks", never "3
|
|
2512
|
+
* semantic policies".
|
|
2513
|
+
*
|
|
2514
|
+
* They are not policies in the sense every other number on these screens uses —
|
|
2515
|
+
* nothing switches them on or off, `--policy` cannot name one, and they do not
|
|
2516
|
+
* appear in `failproofai policies`. Calling them policies would make every count
|
|
2517
|
+
* beside them read as the same kind of thing.
|
|
2518
|
+
*/
|
|
2519
|
+
function semanticPhrase(count: number): string {
|
|
2520
|
+
return `${count} Jev ${count === 1 ? "check" : "checks"}`;
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2348
2523
|
async function pickFromSource(
|
|
2349
2524
|
source: string,
|
|
2350
2525
|
io: { stdin: TTYIn; stdout: TTYOut },
|
|
2351
2526
|
): Promise<string[] | null> {
|
|
2352
2527
|
const preview = await fetchPackPreview(source);
|
|
2528
|
+
// Nothing to pick. A pack of Jev questions alone has no selectable policies —
|
|
2529
|
+
// its semantic set is not switchable per entry — so the picker would draw an
|
|
2530
|
+
// empty list and ask a question with no answers. `[]` rather than `null`: the
|
|
2531
|
+
// user cancelled nothing, there was simply nothing to choose.
|
|
2532
|
+
if (preview.policies.length === 0) {
|
|
2533
|
+
io.stdout.write(
|
|
2534
|
+
`\n ${preview.id}@${preview.version} carries no selectable policies` +
|
|
2535
|
+
`${preview.semantic.length > 0 ? `, only ${semanticPhrase(preview.semantic.length)}` : ""}.\n\n`,
|
|
2536
|
+
);
|
|
2537
|
+
return [];
|
|
2538
|
+
}
|
|
2353
2539
|
const categories = [...new Set(preview.policies.map((p) => p.category))];
|
|
2354
2540
|
const rows: MultiChoice<string>[] = [];
|
|
2355
2541
|
for (const category of categories) {
|
|
@@ -2364,6 +2550,15 @@ async function pickFromSource(
|
|
|
2364
2550
|
}
|
|
2365
2551
|
}
|
|
2366
2552
|
const on = preview.policies.filter((p) => p.defaultEnabled).length;
|
|
2553
|
+
// Said BEFORE the list, because the list cannot say it: a pack's Jev checks are
|
|
2554
|
+
// not rows here, they arrive whole, and they replace the ones this build ships.
|
|
2555
|
+
// Somebody ticking boxes should know that is part of what they are agreeing to.
|
|
2556
|
+
if (preview.semantic.length > 0) {
|
|
2557
|
+
io.stdout.write(
|
|
2558
|
+
`\n This pack also brings ${semanticPhrase(preview.semantic.length)}, which are not selectable —\n` +
|
|
2559
|
+
` they replace the ones this build ships with. See: failproofai policies show ${source}\n\n`,
|
|
2560
|
+
);
|
|
2561
|
+
}
|
|
2367
2562
|
const picked = await multiSelect<string>({
|
|
2368
2563
|
message: `${preview.id}@${preview.version} — which of these should be on?`,
|
|
2369
2564
|
choices: rows,
|
|
@@ -2480,9 +2675,25 @@ async function add(rest: string[]): Promise<PackCliResult> {
|
|
|
2480
2675
|
// rendered as if something had gone missing.
|
|
2481
2676
|
lines.push(
|
|
2482
2677
|
result.enabled.length === 0
|
|
2483
|
-
?
|
|
2678
|
+
? // "enforcing nothing" is only true when the pack brought nothing else.
|
|
2679
|
+
// A pack of Jev checks alone enforces through the semantic tier with no
|
|
2680
|
+
// regex policy enabled at all, and telling that user their install does
|
|
2681
|
+
// nothing is worse than saying nothing.
|
|
2682
|
+
` enabled (0/${result.available.length}, ${why}): none` +
|
|
2683
|
+
(result.semantic > 0 ? "" : " — the pack is installed and enforcing nothing")
|
|
2484
2684
|
: ` enabled (${result.enabled.length}/${result.available.length}, ${why}): ${summarise(result.enabled)}`,
|
|
2485
2685
|
);
|
|
2686
|
+
// Counted separately, and without an on/off ratio, because there is none:
|
|
2687
|
+
// a pack's Jev checks arrive whole and REPLACE the ones this build ships.
|
|
2688
|
+
// Silence here was the shape of the original bug — the manifest's semantic
|
|
2689
|
+
// half was fetched, verified and then never written to `installed.json`, so
|
|
2690
|
+
// the half somebody installed did nothing and nothing said so.
|
|
2691
|
+
if (result.semantic > 0) {
|
|
2692
|
+
lines.push(
|
|
2693
|
+
` ${semanticPhrase(result.semantic)} for Jev, replacing this build's own set. ` +
|
|
2694
|
+
"They apply only where you configured Jev (`failproofai jev status`).",
|
|
2695
|
+
);
|
|
2696
|
+
}
|
|
2486
2697
|
|
|
2487
2698
|
if (skipped.length > 0) {
|
|
2488
2699
|
lines.push(` not enabled (${skipped.length}): ${summarise(skipped)}`);
|
|
@@ -2774,7 +2985,8 @@ async function listRemote(source: string): Promise<PackCliResult> {
|
|
|
2774
2985
|
stack(
|
|
2775
2986
|
title(
|
|
2776
2987
|
`${preview.id}@${preview.version}`,
|
|
2777
|
-
`${preview.policies.length} policies · ${categories.length} categories
|
|
2988
|
+
`${preview.policies.length} policies · ${categories.length} categories` +
|
|
2989
|
+
(preview.semantic.length > 0 ? ` · ${semanticPhrase(preview.semantic.length)}` : ""),
|
|
2778
2990
|
opts,
|
|
2779
2991
|
),
|
|
2780
2992
|
note(
|
|
@@ -2782,6 +2994,20 @@ async function listRemote(source: string): Promise<PackCliResult> {
|
|
|
2782
2994
|
(preview.effect === "observe" ? " This pack OBSERVES — it records and blocks nothing." : ""),
|
|
2783
2995
|
opts,
|
|
2784
2996
|
),
|
|
2997
|
+
// The half that is not in the table below, said in words. It is not
|
|
2998
|
+
// selectable, so it has no rows; and it REPLACES this build's own question
|
|
2999
|
+
// set rather than adding to it, which is the part worth knowing before
|
|
3000
|
+
// installing rather than after.
|
|
3001
|
+
preview.semantic.length > 0
|
|
3002
|
+
? note(
|
|
3003
|
+
`It also carries ${semanticPhrase(preview.semantic.length)} — ${preview.semantic
|
|
3004
|
+
.map((s) => s.name)
|
|
3005
|
+
.join(", ")}. They are not selectable, they apply only where you configured Jev, and they ` +
|
|
3006
|
+
"replace the checks this build ships with.",
|
|
3007
|
+
opts,
|
|
3008
|
+
)
|
|
3009
|
+
: null,
|
|
3010
|
+
preview.minCliVersion ? note(`Requires failproofai ${preview.minCliVersion} or newer.`, opts) : null,
|
|
2785
3011
|
preview.resolvedFromLatest ? note(`Newest release: ${preview.source}`, opts) : null,
|
|
2786
3012
|
table({ head: ["", "", ""], rows }, opts),
|
|
2787
3013
|
nextStep(`failproofai policies add ${source}`, "Install the defaults with:", opts),
|