failproofai 1.0.12-beta.0 → 1.0.12-beta.1
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 +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
- 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 +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_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 +22 -22
- 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 +1 -1
- 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 +3 -3
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +3 -3
- 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 +4 -4
- 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 +8 -8
- package/.next/standalone/.next/server/app/settings/page.js +7 -7
- 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]__0-ha5ydqq0mws._.js → [externals]__0vt9i6fqinjne._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__20xe05hm5u8_1._.js → [root-of-the-server]__0f_4g-vazrvxv._.js} +1 -1
- package/.next/standalone/.next/server/chunks/_0hf5q4xzyg40j._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0tovk6q0miud6._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0trp3ycqv17cl._.js +1 -1
- package/.next/standalone/.next/server/chunks/_19011-u44143j._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1ek68lnh2daur._.js +1 -1
- package/.next/standalone/.next/server/chunks/{_0kc-piux3hady._.js → _1y590mjzpkbos._.js} +2 -2
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4vkpxxu7._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_0_em82s16zfym._.js +5 -0
- package/.next/standalone/.next/server/chunks/src_hooks_builtin-policies_ts_1yq5ybgx1v98e._.js +3 -0
- package/.next/standalone/.next/server/chunks/src_hooks_custom-hooks-loader_ts_0lnb3n3h94wnn._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_daemon-client_ts_0il_yu1zc0291._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn_4twyh._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0588st-tpiuia._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__08ch3staq3ejw._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0vcyntk4snf0f._.js → [root-of-the-server]__0b78b0j8iyv6l._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0bigvy9xmv2ka._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h86bl2qf0k_o._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1_xbg2kx30wp-._.js → [root-of-the-server]__0naevg4bcoh54._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pia5h3p8re4c._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1-ey9fgesfbc0._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__15usvs-ms-0hf._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__16gb3_92zvk5x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1avi2kypusyb_._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1r7drlqli-ado._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0eoura3_9gjb-._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0pedwblqdoh58._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_128ebxr83nk6g._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_1gb0ifpkowwub._.js → _1a3tubds7s7ow._.js} +10 -9
- package/.next/standalone/.next/server/chunks/ssr/_1djqs9i27e4kg._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo5_vm5f.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_actions_get-scheduled-audit_ts_0ei9snixb5fd-._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47q0xq5w._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x33jqb6._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpcz0uxur._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_settings_02tf1h47jmhhd._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{node_modules_html-to-image_es_index_00jxj8foxqz5t.js → node_modules_html-to-image_es_index_1i8oid1gc7s1f.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/package_json_[json]_cjs_013tntikdft59._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_06kzv9ddabn3_._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_0agdmptetetdt._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_0mdwgjisv4fub._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_1cv9_c4l1tzrm._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_daemon-client_ts_0k41_i7jg6ndv._.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/preview-props.json +3 -3
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +23 -23
- package/.next/standalone/.next/static/chunks/{3tn1bzk094e8e.js → 0_zfad4co-t2z.js} +1 -1
- package/.next/standalone/.next/static/chunks/0jutamwqz8pg5.js +1 -0
- package/.next/standalone/.next/static/chunks/{2meqb9kygrncb.js → 114hbolvrv94e.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2znt1784j9gd9.js → 18433qz-8njv3.js} +1 -1
- package/.next/standalone/.next/static/chunks/1b32-afqacaen.js +1 -0
- package/.next/standalone/.next/static/chunks/{13jv-xvabeb62.js → 1p-hsi58i-6h0.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0pjxnp927y7_j.js → 2-tf6_7u623kd.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2qz5piiir-qqm.js → 24w3x2da9sk49.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0a-ccsp3xmypz.js → 3-pku9qwv1yfu.js} +1 -1
- package/.next/standalone/.next/static/chunks/42e8zab314654.js +1 -0
- package/.next/standalone/fp-cloud-cli/CHANGELOG.md +41 -0
- package/.next/standalone/fp-cloud-cli/README.md +42 -1
- package/.next/standalone/fp-cloud-cli/fp_cli/client.py +117 -12
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/fleet_cmds.py +285 -19
- package/.next/standalone/fp-cloud-cli/fp_cli/commands/policies_cmds.py +84 -18
- package/.next/standalone/fp-cloud-cli/fp_cli/enforcement.py +170 -6
- package/.next/standalone/fp-cloud-cli/fp_cli/models.py +89 -6
- package/.next/standalone/fp-cloud-cli/fp_cli/output.py +259 -21
- package/.next/standalone/fp-cloud-cli/fp_cli/policy_check.py +8 -5
- package/.next/standalone/fp-cloud-cli/skill/SKILL.md +2 -2
- package/.next/standalone/fp-cloud-cli/skill/references/commands.md +17 -9
- package/.next/standalone/fp-cloud-cli/tests/test_cloud_jev.py +692 -0
- package/.next/standalone/fp-cloud-cli/tests/test_enforcement_logic.py +46 -0
- package/.next/standalone/fp-cloud-cli/tests/test_output.py +23 -0
- package/.next/standalone/hermes-plugin/README.md +6 -4
- package/.next/standalone/hermes-plugin/__init__.py +16 -0
- package/.next/standalone/hermes-plugin/client.py +5 -1
- package/.next/standalone/hermes-plugin/plugin.yaml +1 -1
- package/.next/standalone/package.json +9 -9
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +17 -1
- package/dist/cli.mjs +2930 -1747
- package/dist/worker.mjs +1450 -145
- package/hermes-plugin/README.md +6 -4
- package/hermes-plugin/__init__.py +16 -0
- package/hermes-plugin/client.py +5 -1
- package/hermes-plugin/plugin.yaml +1 -1
- package/package.json +9 -9
- package/src/audit/open-browser.ts +1 -19
- package/src/hooks/agent-roster.ts +172 -0
- package/src/hooks/agent-targets.ts +60 -0
- package/src/hooks/cloud-connection.ts +97 -27
- package/src/hooks/cloud-enrollment-cli.ts +7 -4
- package/src/hooks/cloud-introspect.ts +2 -5
- package/src/hooks/cloud-managed-policies.ts +380 -20
- package/src/hooks/cloud-policy-errors.ts +481 -0
- package/src/hooks/config-status.ts +1 -1
- package/src/hooks/configure-wizard.ts +15 -311
- package/src/hooks/custom-hooks-loader.ts +26 -3
- package/src/hooks/daemon-client.ts +9 -4
- package/src/hooks/daemon-service.ts +2 -1
- package/src/hooks/effective-reviewers.ts +22 -1
- package/src/hooks/fp-home.ts +6 -0
- package/src/hooks/handler.ts +235 -18
- package/src/hooks/health-probe.ts +15 -0
- package/src/hooks/integrations.ts +18 -11
- package/src/hooks/jev-cli.ts +197 -0
- package/src/hooks/jev-cloud-connection.ts +92 -0
- package/src/hooks/manager.ts +122 -16
- package/src/hooks/pack-failclosed.ts +2 -0
- package/src/hooks/pack-manifest.ts +8 -1
- package/src/hooks/policy-evaluator.ts +5 -1
- package/src/hooks/policy-registry.ts +3 -2
- package/src/hooks/policy-reviewability.ts +21 -5
- package/src/hooks/semantic/cloud-jev-health.ts +227 -0
- package/src/hooks/semantic/cloud-jev.ts +677 -0
- package/src/hooks/semantic/jev-client.ts +18 -1
- package/src/hooks/semantic/jev-config.ts +173 -3
- package/src/hooks/semantic/jev-review.ts +129 -10
- package/src/hooks/semantic/pack-policies.ts +2 -1
- package/src/hooks/semantic/types.ts +8 -0
- package/src/hooks/session-pause-cli.ts +1 -1
- package/src/hooks/tui.ts +0 -271
- package/src/hooks/worker-server.ts +5 -1
- package/.next/standalone/.next/server/chunks/src_hooks_1aveq0uhs2t1b._.js +0 -5
- package/.next/standalone/.next/server/chunks/src_hooks_1eem5a7cyqlb2._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ts0fg5yih1c1._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_1ve8y151ua7qx._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_effective-reviewers_ts_1h4wtvo55j16x._.js +0 -5
- package/.next/standalone/.next/static/chunks/2ank2u3apy8m6.js +0 -1
- package/.next/standalone/.next/static/chunks/2ohktp_-lkv3x.js +0 -1
- package/.next/standalone/.next/static/chunks/2py0eldhwz8ay.js +0 -1
- package/src/hooks/cloud-login.ts +0 -245
- /package/.next/standalone/.next/static/{SNr7o_-G4kNBQTR-iLZMM → xq8UgF5yFxty1VWxY2hyn}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{SNr7o_-G4kNBQTR-iLZMM → xq8UgF5yFxty1VWxY2hyn}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{SNr7o_-G4kNBQTR-iLZMM → xq8UgF5yFxty1VWxY2hyn}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{"use strict";(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,64237,e=>{var t=e.i(43476),s=e.i(71645),a=e.i(12344),n=e.i(29054);let l=/^[^\s@]+@[^\s@]+\.[^\s@]+$/;function i(e){if((0,n.c)(e))return"request timed out. check your network and try again.";let t=e instanceof Error?e.message:String(e);return`network error: ${t}`}e.s(["f",0,function({open:e,headline:r="where to route the reminder?",subhead:o="we'll send a one-time code to confirm.",onClose:c,onAuthed:d,source:u="unknown"}){let{capture:h}=(0,a.usePostHog)(),[m,f]=(0,s.useState)({kind:"email",error:null}),[v,b]=(0,s.useState)(!1),p=(0,s.useRef)(!1),x=(0,s.useRef)(null),j=(0,s.useRef)(null);(0,s.useEffect)(()=>{e&&(f({kind:"email",error:null}),b(!1),p.current=!1,h("audit_auth_dialog_opened",{source:u}))},[h,e,u]);let w=(0,s.useRef)(!1);(0,s.useEffect)(()=>{if(e){w.current=!0;return}w.current&&!p.current&&h("audit_auth_dialog_dismissed",{source:u,step:m.kind}),w.current=!1},[h,e,u,m.kind]),(0,s.useEffect)(()=>{if(!e)return;let t=setTimeout(()=>{"email"===m.kind?x.current?.focus():"code"===m.kind&&j.current?.focus()},50);return()=>clearTimeout(t)},[e,m.kind]),(0,s.useEffect)(()=>{if(!e)return;let t=e=>{"Escape"!==e.key||v||c()};return window.addEventListener("keydown",t),()=>window.removeEventListener("keydown",t)},[e,v,c]);let g="code"===m.kind&&m.resendIn>0;(0,s.useEffect)(()=>{if(!g)return;let e=setInterval(()=>{f(e=>"code"===e.kind?{...e,resendIn:Math.max(0,e.resendIn-1)}:e)},1e3);return()=>clearInterval(e)},[g]);let y=(0,s.useCallback)(async(e,t={})=>{let{isResend:s=!1}=t,a=e=>{s?f(t=>"code"===t.kind?{...t,error:e}:t):f({kind:"email",error:e})};b(!0);try{let t=await (0,n.q)("/api/auth/login-request",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({email:e})}),s=await t.json().catch(()=>({}));if(!t.ok){let e=s.message??"could not send code.";"rate_limited"===s.code&&void 0!==s.retry_after_secs?e=`too many tries. wait ${s.retry_after_secs}s and try again.`:"upstream_unreachable"===s.code&&(e="api-server unreachable. check your network."),a(e);return}f({kind:"code",email:e,error:null,expiresIn:s.expires_in??600,resendIn:s.resend_available_in??30})}catch(e){a(i(e))}finally{b(!1)}},[]),k=(0,s.useCallback)(async(e,t)=>{b(!0);try{let s=await (0,n.q)("/api/auth/login-verify",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({email:e,code:t})}),a=await s.json().catch(()=>({}));if(!s.ok||!a.authenticated||!a.user){let e=a.message??"invalid code.";"invalid_code"===a.code&&(e="wrong code, or it expired. try again."),f(t=>"code"===t.kind?{...t,error:e}:t);return}p.current=!0,h("audit_auth_dialog_succeeded",{source:u}),f({kind:"done",user:a.user}),d(a.user)}catch(t){let e=i(t);f(t=>"code"===t.kind?{...t,error:e}:t)}finally{b(!1)}},[h,d,u]),N=(0,s.useCallback)(async e=>{if(e.preventDefault(),v||"email"!==m.kind)return;let t=String(new FormData(e.currentTarget).get("email")??"").trim().toLowerCase();l.test(t)?await y(t):f({kind:"email",error:"that doesn't look like an email."})},[v,m,y]),C=(0,s.useCallback)(async e=>{if(e.preventDefault(),v||"code"!==m.kind)return;let t=String(new FormData(e.currentTarget).get("code")??"").trim();t.length<4||t.length>12?f(e=>"code"===e.kind?{...e,error:"code is 4–12 characters."}:e):await k(m.email,t)},[v,m,k]),S=(0,s.useCallback)(async()=>{"code"!==m.kind||m.resendIn>0||v||await y(m.email,{isResend:!0})},[m,v,y]);return e?(0,t.jsx)("div",{className:"auth-dialog-backdrop",role:"dialog","aria-modal":"true","aria-labelledby":"auth-dialog-title",onClick:e=>{v||e.target!==e.currentTarget||c()},children:(0,t.jsxs)("div",{className:"auth-dialog",children:[(0,t.jsx)("button",{type:"button",className:"auth-close",onClick:c,disabled:v,"aria-label":"close",children:"×"}),(0,t.jsx)("h2",{id:"auth-dialog-title",className:"auth-headline",children:r}),"email"===m.kind&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("p",{className:"auth-sub",children:o}),(0,t.jsxs)("form",{onSubmit:N,className:"auth-form",children:[(0,t.jsx)("input",{ref:x,id:"auth-dialog-email",name:"email",type:"email",autoComplete:"email",inputMode:"email",spellCheck:!1,placeholder:"you@yourdomain.com",disabled:v,className:"auth-input",required:!0}),m.error&&(0,t.jsx)("div",{className:"auth-error",children:m.error}),(0,t.jsxs)("div",{className:"auth-actions",children:[(0,t.jsx)("button",{type:"submit",className:"auth-btn primary",disabled:v,children:v?"sending…":"send code"}),(0,t.jsx)("button",{type:"button",className:"auth-btn",onClick:c,disabled:v,children:"cancel"})]})]})]}),"code"===m.kind&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("p",{className:"auth-sub",children:["code sent to ",(0,t.jsx)("span",{className:"auth-email",children:m.email}),". expires in ",Math.ceil(m.expiresIn/60)," min."]}),(0,t.jsxs)("form",{onSubmit:C,className:"auth-form",children:[(0,t.jsx)("input",{ref:j,id:"auth-dialog-code",name:"code",type:"text",inputMode:"numeric",autoComplete:"one-time-code",spellCheck:!1,placeholder:"123456",disabled:v,className:"auth-input auth-input-code",maxLength:12,required:!0}),m.error&&(0,t.jsx)("div",{className:"auth-error",children:m.error}),(0,t.jsxs)("div",{className:"auth-actions",children:[(0,t.jsx)("button",{type:"submit",className:"auth-btn primary",disabled:v,children:v?"verifying…":"verify"}),(0,t.jsx)("button",{type:"button",className:"auth-btn",onClick:S,disabled:v||m.resendIn>0,children:m.resendIn>0?`resend in ${m.resendIn}s`:"resend code"})]}),(0,t.jsx)("button",{type:"button",className:"auth-back",onClick:()=>f({kind:"email",error:null}),disabled:v,children:"← use a different email"})]})]}),"done"===m.kind&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("p",{className:"auth-sub",children:[(0,t.jsx)("span",{className:"auth-ok",children:"✓"})," signed in as"," ",(0,t.jsx)("span",{className:"auth-email",children:m.user.email}),"."]}),(0,t.jsx)("div",{className:"auth-actions",children:(0,t.jsx)("button",{type:"button",className:"auth-btn primary",onClick:c,children:"continue"})})]})]})}):null}])},86616,e=>{var t=e.i(29054);class s extends Error{kind;constructor(e,t){super(t),this.kind=e,this.name="RerunError"}}async function a(e){try{let a=await (0,t.q)("/api/audit/run",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({cli:e.cli.length>0?e.cli:void 0,since:"all"===e.since?void 0:e.since,noCache:!!e.noCache||void 0})});if(!a.ok&&409!==a.status){let e=await a.text().catch(()=>"");throw console.error("audit run failed:",a.status,e),new s("post_failed",`audit run failed (${a.status})`)}}catch(e){if(e instanceof s)throw e;throw console.error("audit run request failed:",e),new s((0,t.c)(e)?"timeout":"network","audit run request failed")}let a=0;for(;;){await new Promise(e=>setTimeout(e,1e3));let e=null;try{let s=await (0,t.q)("/api/audit/status",{cache:"no-store"});s.ok&&(e=await s.json())}catch{}if(null===e){if(++a>=10)throw new s("network","audit status poll lost the server");continue}if(a=0,!e.running){if(e.error)throw new s("post_failed",e.error);return}}}e.s(["t",0,s,"s",0,a])},2917,e=>{var t=e.i(43476),s=e.i(71645),a=e.i(95187);let n=(0,a.createServerReference)("00c117e2f6723aaec427f79646a93b7db2e2c1fc33",a.callServer,void 0,a.findSourceMapURL,"getJevSettingsAction"),l=(0,a.createServerReference)("000f25cb570f9ee8b2f54da19de57f679d6e17a052",a.callServer,void 0,a.findSourceMapURL,"removeJevConfigAction"),i=(0,a.createServerReference)("40efd95eef98ad185ef9d139a21ca7d1d6905e34d5",a.callServer,void 0,a.findSourceMapURL,"saveJevConfigAction"),r=(0,a.createServerReference)("4051bcb104f743e8e533ce34b83cc84eca2f875736",a.callServer,void 0,a.findSourceMapURL,"setJevModeAction");var o=e.i(55574);let c=[{value:"typesafe",label:"typesafe"},{value:"openrouter",label:"openrouter"},{value:"vercel",label:"vercel ai gateway"},{value:"cloudflare",label:"cloudflare workers ai"},{value:"custom",label:"custom endpoint"}],d=[{value:"enforce",label:"enforce — jev's verdict counts"},{value:"observe",label:"observe — log only, regex decides"},{value:"off",label:"off — keep this config, don't ask jev"}],u="failproofai";function h(e){return{provider:e.provider&&e.provider!==u?e.provider:"typesafe",baseUrl:e.baseUrl,accountId:e.accountId,mode:e.mode}}function m({id:e,label:s,hint:a,children:n}){return(0,t.jsxs)("div",{className:"set-field",children:[(0,t.jsx)("label",{className:"set-field-label",htmlFor:e,children:s}),n,a?(0,t.jsx)("span",{className:"set-hint",children:a}):null]})}e.s(["default",0,function({initial:e}){var a,f,v;let b,[p,x]=(0,s.useState)(e),[j,w]=(0,s.useState)(e?h(e):{provider:"typesafe",baseUrl:"",accountId:"",mode:"enforce"}),[g,y]=(0,s.useState)(""),[k,N]=(0,s.useState)(!1),[C,S]=(0,s.useState)(null),[M,A]=(0,s.useState)(!1),$=(0,s.useRef)(!0),I=(0,s.useRef)(!1),E=(0,s.useCallback)(async()=>{try{let e=await n();if(!$.current)return;x(e),I.current||w(h(e))}catch{}},[]);(0,s.useEffect)(()=>($.current=!0,E(),()=>{$.current=!1}),[E]),(0,s.useEffect)(()=>{let e=()=>void E();return window.addEventListener("focus",e),document.addEventListener("visibilitychange",e),()=>{window.removeEventListener("focus",e),document.removeEventListener("visibilitychange",e)}},[E]);let R=(0,s.useCallback)((e,t)=>{I.current=!0,w(s=>({...s,[e]:t}))},[]),_=(0,s.useCallback)(async()=>{N(!0),S(null);try{let e=await i({provider:j.provider,baseUrl:j.baseUrl,accountId:j.accountId,mode:j.mode,token:g});if(!e.ok){S(e.problem),A(!0===e.needsToken);return}I.current=!1,A(!1),y(""),x(e.view),w(h(e.view)),(0,o.toast)(e.view.on?"jev is on.":"saved.")}catch{S("could not save that.")}finally{N(!1)}},[j,g]),F=(0,s.useCallback)(async e=>{N(!0),S(null);try{let t=await r(e);if(!t.ok)return void S(t.problem);I.current=!1,x(t.view),w(h(t.view)),(0,o.toast)("off"===e?"jev is off. hooks run the regex policies.":`jev is on, in ${e} mode.`)}catch{S("could not switch that.")}finally{N(!1)}},[]),D=(0,s.useCallback)(async()=>{N(!0),S(null),A(!1);try{let e=await l();if(!e.ok)return void S(e.problem);I.current=!1,y(""),x(e.view),w(h(e.view)),(0,o.toast)("jev is off. hooks run the regex policies.")}catch{S("could not turn that off.")}finally{N(!1)}},[]),L=null!==p&&"absent"!==p.status,U=p?.provider===u,T=p?.token,q=M?"needed for this save — type the token for this endpoint":T?"cloud"===T.source?"from this machine's FailproofAI Cloud connection":"env"===T.source?"configured, read from the environment — leave blank to keep it":"configured — leave blank to keep it":p?.status==="key-missing"?"read from the environment, which is not set in this shell — leave blank to keep it that way":"sent as a bearer token; stored owner-only at 0600";return(0,t.jsxs)("div",{className:"set-cell set-jev",children:[(0,t.jsx)("h2",{className:"set-cell-title",children:"jev · semantic evaluation"}),(0,t.jsx)("div",{className:"set-row set-row-main",children:(0,t.jsxs)("div",{className:"set-row-copy",children:[(0,t.jsx)("span",{className:p?.on?"set-strong":"set-dim",children:null===p?"reading…":p.on?`on. every judged command is also asked of ${(a=p.provider)===u?"FailproofAI Cloud":a??""}.`:"off"===p.status?"off — switched off. hooks run the regex policies exactly as before.":"not-connected"===p.status?"off — this machine is not connected to FailproofAI Cloud. hooks run the regex policies.":"key-lacks-jev"===p.status?"off — no jev key is stored for this machine's FailproofAI Cloud connection. hooks run the regex policies.":"key-missing"===p.status?"off for this dashboard — FAILPROOFAI_JEV_API_KEY is not set in its environment. hooks run the regex policies wherever it is unset, including under the daemon; a session that sets it still consults jev.":"off. hooks run the regex policies exactly as before."}),(0,t.jsx)("span",{className:"set-dim",children:p?.on?"observe"===p.mode?"observe: jev's answers are logged, the regex result is what gets enforced.":"enforce: jev's answers can clear a reviewable deny.":U?"through FailproofAI Cloud, on your org's plan — no endpoint or token of your own.":"a second opinion on the commands the regex policies flag. needs your own endpoint and token."})]})}),p&&(0,t.jsx)("dl",{className:"set-how-list set-jev-cloud",children:(0,t.jsxs)("div",{className:"set-how-row",children:[(0,t.jsx)("dt",{className:"set-how-label",children:"FailproofAI Cloud connection"}),(0,t.jsx)("dd",{className:"set-how-body",children:function(e){if(!e||!e.connected)return e?.jev==="yes"?"a jev key is stored, but no connection is":"not connected";let t=e.org??e.host??"connected",s="yes"===e.jev?"key carries jev":"refused"===e.jev?"jev key refused — credentials.json is not owner-only":"no jev key stored";return`connected to ${t} \xb7 ${s}`}(p.cloud)})]})}),p&&L&&(0,t.jsxs)("dl",{className:"set-how-list",children:[U&&(0,t.jsxs)("div",{className:"set-how-row",children:[(0,t.jsx)("dt",{className:"set-how-label",children:"provider"}),(0,t.jsx)("dd",{className:"set-how-body",children:p.cloud?.host?`FailproofAI Cloud \xb7 ${p.cloud.host}`:"FailproofAI Cloud"})]}),(0,t.jsxs)("div",{className:"set-how-row",children:[(0,t.jsx)("dt",{className:"set-how-label",children:"model"}),(0,t.jsx)("dd",{className:"set-how-body",children:function(e){switch(e.kind){case"id":return e.id;case"withheld":return"set to something that is not a model id — not shown here, in case it is a key";default:return"the provider's default"}}(p.model)})]}),(0,t.jsxs)("div",{className:"set-how-row",children:[(0,t.jsx)("dt",{className:"set-how-label",children:"token"}),(0,t.jsx)("dd",{className:"set-how-body",children:T?"cloud"===T.source?"FailproofAI Cloud connection":"env"===T.source?"from FAILPROOFAI_JEV_API_KEY":"configured":U?p.cloud?.connected?"none stored for this connection":"none — this machine is not connected":"none stored"})]}),p.reviewable&&(0,t.jsxs)("div",{className:"set-how-row",children:[(0,t.jsx)("dt",{className:"set-how-label",children:"reviewable"}),(0,t.jsx)("dd",{className:"set-how-body",children:p.reviewable.summary})]}),p.stats&&p.stats.total>0&&(0,t.jsxs)("div",{className:"set-how-row",children:[(0,t.jsx)("dt",{className:"set-how-label",children:"fallbacks"}),(0,t.jsx)("dd",{className:"set-how-body",children:`${(f=p.stats.fallbackRate,`${Math.round(100*f)}%`)} of ${p.stats.total} calls in the last ${Number.isInteger(b=(v=p.stats.windowMs)/36e5)&&b>=1?b%24==0?`${b/24}d`:`${b}h`:`${Math.max(1,Math.round(v/6e4))}m`} fell back to the regex engine.`})]})]}),p?.problem&&(0,t.jsxs)("p",{className:"set-warn",children:[p.problem,p.fix?` — ${p.fix}`:""]}),p?.reviewable?.problem&&(0,t.jsx)("p",{className:"set-warn",children:p.reviewable.problem}),(0,t.jsx)("div",{className:"set-rule"}),U?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("div",{className:"set-jev-fields",children:(0,t.jsx)(m,{id:"jev-cloud-mode",label:"mode",hint:"the endpoint and key come from this machine's FailproofAI Cloud connection",children:(0,t.jsx)("select",{id:"jev-cloud-mode",className:"set-select",value:p?.mode==="enforce"?"enforce":"observe",disabled:k||p?.mode==="off",onChange:e=>void F("enforce"===e.target.value?"enforce":"observe"),children:d.filter(e=>"off"!==e.value).map(e=>(0,t.jsx)("option",{value:e.value,children:e.label},e.value))})})}),C&&(0,t.jsx)("p",{className:"set-warn",children:C}),(0,t.jsx)("div",{className:"set-actions set-jev-actions",children:(0,t.jsx)("button",{type:"button",className:"btn btn-press",disabled:k,onClick:()=>void F(p?.mode==="off"?"observe":"off"),children:k?"[ switching… ]":p?.mode==="off"?"[ turn jev on ]":"[ turn jev off ]"})})]}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("div",{className:"set-jev-fields",children:[(0,t.jsx)(m,{id:"jev-provider",label:"provider",children:(0,t.jsx)("select",{id:"jev-provider",className:"set-select",value:j.provider,disabled:k,onChange:e=>R("provider",e.target.value),children:c.map(e=>(0,t.jsx)("option",{value:e.value,children:e.label},e.value))})}),(0,t.jsx)(m,{id:"jev-url",label:"endpoint url",hint:["custom"===j.provider?"required — https, or http to localhost in observe mode":"leave blank for the provider's own api",p?.baseUrlQueryWithheld?"the stored url's query string is not shown here — leave the url as it is to keep it":null].filter(Boolean).join(" · "),children:(0,t.jsx)("input",{id:"jev-url",type:"text",className:"set-text",inputMode:"url",autoComplete:"off",spellCheck:!1,placeholder:"custom"===j.provider?"https://…":"the provider's own api",value:j.baseUrl,disabled:k,onChange:e=>R("baseUrl",e.target.value)})}),"cloudflare"===j.provider&&(0,t.jsx)(m,{id:"jev-account",label:"account id",hint:"32 lowercase hex characters",children:(0,t.jsx)("input",{id:"jev-account",type:"text",className:"set-text",autoComplete:"off",spellCheck:!1,value:j.accountId,disabled:k,onChange:e=>R("accountId",e.target.value)})}),(0,t.jsx)(m,{id:"jev-token",label:"token",hint:q,children:(0,t.jsx)("input",{id:"jev-token",type:"password",className:"set-text",autoComplete:"off",spellCheck:!1,placeholder:T?"•••• kept":"",value:g,disabled:k,"aria-invalid":M||void 0,onChange:e=>{I.current=!0,y(e.target.value)}})}),(0,t.jsx)(m,{id:"jev-mode",label:"mode",children:(0,t.jsx)("select",{id:"jev-mode",className:"set-select",value:j.mode,disabled:k,onChange:e=>R("mode",e.target.value),children:d.map(e=>(0,t.jsx)("option",{value:e.value,children:e.label},e.value))})})]}),C&&(0,t.jsx)("p",{className:"set-warn",children:C}),(0,t.jsxs)("div",{className:"set-actions set-jev-actions",children:[(0,t.jsx)("button",{type:"button",className:"btn btn-press",disabled:k,onClick:()=>void _(),children:k?"[ saving… ]":L?"[ save changes ]":"[ turn jev on ]"}),L&&(0,t.jsx)("button",{type:"button",className:"set-link",disabled:k,onClick:()=>void D(),children:"turn jev off"})]})]})]})}],2917)},93237,e=>{var t=e.i(43476),s=e.i(71645),a=e.i(95187);let n=(0,a.createServerReference)("0001953c5af243a11547577ef82018061ce3259b2e",a.callServer,void 0,a.findSourceMapURL,"getScheduledAuditAction"),l=(0,a.createServerReference)("40c9442f8916b211fed78371a2bff660dea301ef3c",a.callServer,void 0,a.findSourceMapURL,"setAutoAuditAction"),i=(0,a.createServerReference)("406b9805673a46697a80acba35ac964f3ef5e62a4f",a.callServer,void 0,a.findSourceMapURL,"setAuditIntervalAction");var r=e.i(86616),o=e.i(64237),c=e.i(55574),d=e.i(34713);function u(e){return new Date(e).toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}function h(e,t){let s=e-t;if(s<=0)return"now";let a=Math.floor(s/864e5),n=Math.floor(s%864e5/36e5);return a>0?n>0?`${a}d ${n}h`:`${a}d`:n>0?`${n}h`:`${Math.max(1,Math.floor(s/6e4))}m`}function m(e){return e.toLocaleString("en-US")}function f({label:e,value:s,sub:a,tone:n}){return(0,t.jsxs)("div",{className:"set-stat",children:[(0,t.jsx)("div",{className:"set-stat-label",children:e}),(0,t.jsx)("div",{className:`set-stat-value${n?` ${n}`:""}`,children:s}),(0,t.jsx)("div",{className:"set-stat-sub",children:a??" "})]})}function v({lastRunAtMs:e,nextDueAtMs:s,now:a}){if(null==e||null==s||s<=e)return null;let n=Math.min(100,Math.max(0,(a-e)/(s-e)*100));return(0,t.jsxs)("div",{className:"tape","aria-hidden":"true",children:[(0,t.jsxs)("div",{className:"tape-rail",children:[(0,t.jsx)("div",{className:"tape-fill",style:{width:`${n}%`}}),(0,t.jsx)("div",{className:"tape-now",style:{left:`${n}%`}})]}),(0,t.jsxs)("div",{className:"tape-ends",children:[(0,t.jsxs)("span",{children:["last scan · ",u(e)]}),(0,t.jsxs)("span",{className:"tape-next",children:["next · ",h(s,a)]})]})]})}function b({enabled:e,onChange:s,disabled:a,label:n}){return(0,t.jsx)("button",{type:"button",role:"switch","aria-checked":e,"aria-label":n,disabled:a,onClick:s,className:"sw","data-on":e?"true":"false",children:(0,t.jsx)("span",{className:"sw-knob"})})}let p=[{label:"reads",body:"every session transcript on disk — your prompts, the files your agents read and wrote, and command output."},{label:"runs",body:"entirely on this machine. the transcripts never leave it."},{label:"sends",body:"counts, redacted examples, and this machine's name — and only when a scan finds something harmful."}];e.s(["default",0,function({initial:e,jevPanel:a}){let[x,j]=(0,s.useState)(e),[w,g]=(0,s.useState)(e?.auto??!1),[y,k]=(0,s.useState)(e?.intervalDays??7),[N,C]=(0,s.useState)(!1),[S,M]=(0,s.useState)(!1),[A,$]=(0,s.useState)(!1),[I,E]=(0,s.useState)(null===e),[R,_]=(0,s.useState)(0),F=(0,s.useRef)(!0),D=(0,s.useRef)(null!==e),L=(0,s.useCallback)(async()=>{try{let e=await n();if(!F.current)return;j(e),D.current=!0,g(e.auto),k(e.intervalDays),_(Date.now()),E(!1)}catch{F.current&&!D.current&&E(!0)}},[]);(0,s.useEffect)(()=>(F.current=!0,L(),()=>{F.current=!1}),[L]),(0,s.useEffect)(()=>{let e=()=>void L();return window.addEventListener("focus",e),document.addEventListener("visibilitychange",e),()=>{window.removeEventListener("focus",e),document.removeEventListener("visibilitychange",e)}},[L]);let U=x?.signedInAs??null,T=x?.daemon==="running",q=x?.daemon==="unsupported-platform",O=x?.schedule??null,P=x?.lastScan??null,J=O?.lastExitCode!=null&&0!==O.lastExitCode&&75!==O.lastExitCode,B=(0,s.useCallback)(async()=>{C(!0);try{let e=await l(!0);if(!e.ok){if(g(!1),"unreachable"===e.reason)return void(0,c.toast)("could not reach the server. check your connection and try again.");await L(),$(!0),(0,c.toast)("that sign-in expired. one more code and it's on.");return}g(e.auto),(0,c.toast)("scheduled audits on."),await L()}catch{g(!1),(0,c.toast)("could not turn that on.")}finally{C(!1)}},[L]),K=(0,s.useCallback)(async()=>{if(w){C(!0);try{let e=await l(!1);e.ok&&g(e.auto),(0,c.toast)("scheduled audits off."),await L()}catch{(0,c.toast)("could not turn that off.")}finally{C(!1)}return}U?await B():$(!0)},[w,B,L,U]),V=(0,s.useCallback)(async e=>{C(!0);try{let t=await i(e);k(t.intervalDays),j(e=>e?{...e,intervalDays:t.intervalDays}:e),(0,c.toast)(`scanning every ${t.intervalDays} day${1===t.intervalDays?"":"s"}.`)}catch{k(x?.intervalDays??7),(0,c.toast)("could not save that.")}finally{C(!1)}},[x?.intervalDays]),Y=(0,s.useCallback)(async()=>{C(!0);try{await fetch("/api/auth/logout",{method:"POST"}),(0,c.toast)("signed out. scans continue; digests pause."),await L()}catch{(0,c.toast)("could not sign out.")}finally{C(!1)}},[L]),H=(0,s.useCallback)(async()=>{if(!S){M(!0);try{await (0,r.s)({cli:[],since:"all",noCache:!1}),(0,c.toast)("scan complete."),await L()}catch(e){(0,c.toast)(e instanceof r.t&&"timeout"===e.kind?"the scan is taking a while — it will finish in the background.":"the scan could not be completed.")}finally{M(!1)}}},[L,S]),Q=(0,s.useCallback)(async e=>{$(!1),await L(),await B()},[B,L]),W=x?T?{value:"running",sub:null!=x.daemonStartedAtMs&&R>0?`up ${function(e){let t=Math.floor(e/864e5);if(t>0)return`${t}d`;let s=Math.floor(e/36e5);return s>0?`${s}h`:`${Math.max(1,Math.floor(e/6e4))}m`}(Math.max(0,R-x.daemonStartedAtMs))}`:"",tone:"ok"}:q?{value:"unavailable",sub:"not on this platform"}:"not-installed"===x.daemon?{value:"not installed",sub:"run failproofai config",tone:"warn"}:"unknown"===x.daemon?{value:"unknown",sub:"could not read it"}:{value:"stopped",sub:"run failproofai config",tone:"warn"}:{value:"…",sub:"reading"},z=w?O?.nextDueAtMs==null?{value:"pending",sub:"after the next scan"}:{value:R>0?h(O.nextDueAtMs,R):"—",sub:u(O.nextDueAtMs)}:{value:"off",sub:"nothing scheduled"},G=P?{value:(0,d.g)(Date.parse(P.finishedAt)),sub:null!=P.sessionsScanned?`${m(P.sessionsScanned)} sessions`:"scanned"}:{value:"none yet",sub:"run one below"},X=P?null==P.findings?{value:"—",sub:"unreadable"}:{value:m(P.findings),sub:"this scan",...P.findings>0?{tone:"warn"}:{}}:{value:"—",sub:"no scan yet"};return(0,t.jsxs)("main",{className:"report",children:[(0,t.jsxs)("section",{className:"section set-sec",children:[(0,t.jsxs)("div",{className:"set-mast",children:[(0,t.jsx)("h1",{className:"set-title",children:"settings"}),(0,t.jsx)("p",{className:"set-lede",children:"keeping watch, so you don't have to."})]}),I?(0,t.jsxs)("div",{className:"set-console",children:[(0,t.jsx)("div",{className:"set-cell",children:(0,t.jsxs)("p",{className:"set-warn",children:["could not read this machine's settings. check that the dashboard can reach ",(0,t.jsx)("code",{children:"~/.failproofai/config.json"}),"."]})}),a]}):(0,t.jsxs)("div",{className:"set-console",children:[(0,t.jsxs)("div",{className:"set-stats",children:[(0,t.jsx)(f,{label:"daemon",...W}),(0,t.jsx)(f,{label:"next scan",...z}),(0,t.jsx)(f,{label:"last scan",...G}),(0,t.jsx)(f,{label:"findings",...X})]}),(0,t.jsxs)("div",{className:"set-cols",children:[(0,t.jsxs)("div",{className:"set-cell set-config",children:[(0,t.jsx)("h2",{className:"set-cell-title",children:"scheduled audit"}),(0,t.jsxs)("div",{className:"set-row set-row-main",children:[(0,t.jsx)(b,{enabled:w,disabled:N||null===x,onChange:()=>void K(),label:w?"turn off scheduled audits":"turn on scheduled audits"}),(0,t.jsxs)("div",{className:"set-row-copy",children:[(0,t.jsx)("span",{className:"set-strong",children:w?"scanning on a schedule.":"scan on a schedule."}),(0,t.jsx)("span",{className:"set-dim",children:w?"you'll get an email only when a scan finds something.":"off. nothing runs and nothing is sent."})]})]}),(0,t.jsxs)("div",{className:"set-row set-row-interval",children:[(0,t.jsx)("span",{className:"set-dim",children:"scan every"}),(0,t.jsx)("input",{type:"number",className:"set-num",min:1,max:90,value:y,disabled:N||null===x,"aria-label":"days between scheduled scans",onChange:e=>k(Number(e.target.value)),onBlur:e=>{let t=Number(e.target.value);Number.isFinite(t)?t!==x?.intervalDays&&V(t):k(x?.intervalDays??7)}}),(0,t.jsx)("span",{className:"set-dim",children:"days"}),(0,t.jsxs)("span",{className:"set-hint",children:[1,"–",90]})]}),(0,t.jsx)("div",{className:"set-rule"}),(0,t.jsx)("div",{className:"set-row set-row-identity",children:U?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("span",{className:"set-dim",children:"reports go to"}),(0,t.jsx)("span",{className:"set-email",children:U.email}),(0,t.jsx)("button",{type:"button",className:"set-link",disabled:N,onClick:()=>void Y(),children:"sign out"})]}):w?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("span",{className:"set-warn",children:"signed out — scans continue, digests are paused."}),(0,t.jsx)("button",{type:"button",className:"set-link",disabled:N,onClick:()=>$(!0),children:"sign in to resume"})]}):(0,t.jsx)("span",{className:"set-dim",children:"turning this on asks for an email, so there is somewhere to send the report."})}),w&&x&&!T&&(0,t.jsx)("p",{className:"set-warn",children:q?"the background service isn't available on this platform, so scheduled scans can't run here. you can still run one now.":"not-installed"===x.daemon?"the background service isn't installed, so nothing will run on the timer. install it with `failproofai config`.":"the background service is stopped, so nothing will run until it starts. repair it with `failproofai config`."}),J&&(0,t.jsxs)("p",{className:"set-warn",children:["the last scheduled scan exited ",O?.lastExitCode,". the next one will still run."]}),(0,t.jsx)("div",{className:"set-actions",children:(0,t.jsx)("button",{type:"button",className:"btn btn-press",disabled:S,onClick:()=>void H(),children:S?"[ scanning… ]":"[ run a scan now ]"})})]}),(0,t.jsxs)("div",{className:"set-cell set-how",children:[(0,t.jsx)("h2",{className:"set-cell-title",children:"how it works"}),(0,t.jsx)("dl",{className:"set-how-list",children:p.map(e=>(0,t.jsxs)("div",{className:"set-how-row",children:[(0,t.jsx)("dt",{className:"set-how-label",children:e.label}),(0,t.jsx)("dd",{className:"set-how-body",children:e.body})]},e.label))})]})]}),a]}),w&&R>0&&(0,t.jsx)(v,{lastRunAtMs:O?.lastRunAtMs??null,nextDueAtMs:O?.nextDueAtMs??null,now:R})]}),(0,t.jsx)(o.f,{open:A,source:"settings_scheduled_audit",headline:"where should the report go?",subhead:"we'll send a one-time code to confirm.",onClose:()=>$(!1),onAuthed:e=>void Q(e)})]})}],93237)},29054,e=>{e.s(["q",0,function(e,t={},s=15e3){let a=AbortSignal.timeout(s),n=t.signal?AbortSignal.any([t.signal,a]):a;return fetch(e,{...t,signal:n})},"c",0,function(e){if(null===e||"object"!=typeof e)return!1;let t=e.name;return"AbortError"===t||"TimeoutError"===t}])},34713,e=>{e.s(["M",0,function(e){if(e<1e3)return`${e}ms`;let t=Math.round(e/100);if(t<600)return`${(t/10).toFixed(1)}s`;let s=Math.round(e/1e3);if(s<3600){let e=Math.floor(s/60);return`${e}m ${s%60}s`}let a=Math.round(e/6e4),n=Math.floor(a/60);return`${n}h ${a%60}m`},"g",0,function(e){let t=Date.now()-e;return t<6e4?`${Math.max(1,Math.floor(t/1e3))}s ago`:t<36e5?`${Math.floor(t/6e4)}m ago`:t<864e5?`${Math.floor(t/36e5)}h ago`:`${Math.floor(t/864e5)}d ago`}])}])})();
|
|
1
|
+
(()=>{"use strict";(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,64237,e=>{var t=e.i(43476),s=e.i(71645),a=e.i(12344),n=e.i(29054);let l=/^[^\s@]+@[^\s@]+\.[^\s@]+$/;function i(e){if((0,n.c)(e))return"request timed out. check your network and try again.";let t=e instanceof Error?e.message:String(e);return`network error: ${t}`}e.s(["f",0,function({open:e,headline:r="where to route the reminder?",subhead:o="we'll send a one-time code to confirm.",onClose:c,onAuthed:d,source:u="unknown"}){let{capture:h}=(0,a.usePostHog)(),[m,f]=(0,s.useState)({kind:"email",error:null}),[v,b]=(0,s.useState)(!1),p=(0,s.useRef)(!1),x=(0,s.useRef)(null),j=(0,s.useRef)(null);(0,s.useEffect)(()=>{e&&(f({kind:"email",error:null}),b(!1),p.current=!1,h("audit_auth_dialog_opened",{source:u}))},[h,e,u]);let w=(0,s.useRef)(!1);(0,s.useEffect)(()=>{if(e){w.current=!0;return}w.current&&!p.current&&h("audit_auth_dialog_dismissed",{source:u,step:m.kind}),w.current=!1},[h,e,u,m.kind]),(0,s.useEffect)(()=>{if(!e)return;let t=setTimeout(()=>{"email"===m.kind?x.current?.focus():"code"===m.kind&&j.current?.focus()},50);return()=>clearTimeout(t)},[e,m.kind]),(0,s.useEffect)(()=>{if(!e)return;let t=e=>{"Escape"!==e.key||v||c()};return window.addEventListener("keydown",t),()=>window.removeEventListener("keydown",t)},[e,v,c]);let g="code"===m.kind&&m.resendIn>0;(0,s.useEffect)(()=>{if(!g)return;let e=setInterval(()=>{f(e=>"code"===e.kind?{...e,resendIn:Math.max(0,e.resendIn-1)}:e)},1e3);return()=>clearInterval(e)},[g]);let y=(0,s.useCallback)(async(e,t={})=>{let{isResend:s=!1}=t,a=e=>{s?f(t=>"code"===t.kind?{...t,error:e}:t):f({kind:"email",error:e})};b(!0);try{let t=await (0,n.q)("/api/auth/login-request",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({email:e})}),s=await t.json().catch(()=>({}));if(!t.ok){let e=s.message??"could not send code.";"rate_limited"===s.code&&void 0!==s.retry_after_secs?e=`too many tries. wait ${s.retry_after_secs}s and try again.`:"upstream_unreachable"===s.code&&(e="api-server unreachable. check your network."),a(e);return}f({kind:"code",email:e,error:null,expiresIn:s.expires_in??600,resendIn:s.resend_available_in??30})}catch(e){a(i(e))}finally{b(!1)}},[]),k=(0,s.useCallback)(async(e,t)=>{b(!0);try{let s=await (0,n.q)("/api/auth/login-verify",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({email:e,code:t})}),a=await s.json().catch(()=>({}));if(!s.ok||!a.authenticated||!a.user){let e=a.message??"invalid code.";"invalid_code"===a.code&&(e="wrong code, or it expired. try again."),f(t=>"code"===t.kind?{...t,error:e}:t);return}p.current=!0,h("audit_auth_dialog_succeeded",{source:u}),f({kind:"done",user:a.user}),d(a.user)}catch(t){let e=i(t);f(t=>"code"===t.kind?{...t,error:e}:t)}finally{b(!1)}},[h,d,u]),N=(0,s.useCallback)(async e=>{if(e.preventDefault(),v||"email"!==m.kind)return;let t=String(new FormData(e.currentTarget).get("email")??"").trim().toLowerCase();l.test(t)?await y(t):f({kind:"email",error:"that doesn't look like an email."})},[v,m,y]),C=(0,s.useCallback)(async e=>{if(e.preventDefault(),v||"code"!==m.kind)return;let t=String(new FormData(e.currentTarget).get("code")??"").trim();t.length<4||t.length>12?f(e=>"code"===e.kind?{...e,error:"code is 4–12 characters."}:e):await k(m.email,t)},[v,m,k]),S=(0,s.useCallback)(async()=>{"code"!==m.kind||m.resendIn>0||v||await y(m.email,{isResend:!0})},[m,v,y]);return e?(0,t.jsx)("div",{className:"auth-dialog-backdrop",role:"dialog","aria-modal":"true","aria-labelledby":"auth-dialog-title",onClick:e=>{v||e.target!==e.currentTarget||c()},children:(0,t.jsxs)("div",{className:"auth-dialog",children:[(0,t.jsx)("button",{type:"button",className:"auth-close",onClick:c,disabled:v,"aria-label":"close",children:"×"}),(0,t.jsx)("h2",{id:"auth-dialog-title",className:"auth-headline",children:r}),"email"===m.kind&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("p",{className:"auth-sub",children:o}),(0,t.jsxs)("form",{onSubmit:N,className:"auth-form",children:[(0,t.jsx)("input",{ref:x,id:"auth-dialog-email",name:"email",type:"email",autoComplete:"email",inputMode:"email",spellCheck:!1,placeholder:"you@yourdomain.com",disabled:v,className:"auth-input",required:!0}),m.error&&(0,t.jsx)("div",{className:"auth-error",children:m.error}),(0,t.jsxs)("div",{className:"auth-actions",children:[(0,t.jsx)("button",{type:"submit",className:"auth-btn primary",disabled:v,children:v?"sending…":"send code"}),(0,t.jsx)("button",{type:"button",className:"auth-btn",onClick:c,disabled:v,children:"cancel"})]})]})]}),"code"===m.kind&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("p",{className:"auth-sub",children:["code sent to ",(0,t.jsx)("span",{className:"auth-email",children:m.email}),". expires in ",Math.ceil(m.expiresIn/60)," min."]}),(0,t.jsxs)("form",{onSubmit:C,className:"auth-form",children:[(0,t.jsx)("input",{ref:j,id:"auth-dialog-code",name:"code",type:"text",inputMode:"numeric",autoComplete:"one-time-code",spellCheck:!1,placeholder:"123456",disabled:v,className:"auth-input auth-input-code",maxLength:12,required:!0}),m.error&&(0,t.jsx)("div",{className:"auth-error",children:m.error}),(0,t.jsxs)("div",{className:"auth-actions",children:[(0,t.jsx)("button",{type:"submit",className:"auth-btn primary",disabled:v,children:v?"verifying…":"verify"}),(0,t.jsx)("button",{type:"button",className:"auth-btn",onClick:S,disabled:v||m.resendIn>0,children:m.resendIn>0?`resend in ${m.resendIn}s`:"resend code"})]}),(0,t.jsx)("button",{type:"button",className:"auth-back",onClick:()=>f({kind:"email",error:null}),disabled:v,children:"← use a different email"})]})]}),"done"===m.kind&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("p",{className:"auth-sub",children:[(0,t.jsx)("span",{className:"auth-ok",children:"✓"})," signed in as"," ",(0,t.jsx)("span",{className:"auth-email",children:m.user.email}),"."]}),(0,t.jsx)("div",{className:"auth-actions",children:(0,t.jsx)("button",{type:"button",className:"auth-btn primary",onClick:c,children:"continue"})})]})]})}):null}])},86616,e=>{var t=e.i(29054);class s extends Error{kind;constructor(e,t){super(t),this.kind=e,this.name="RerunError"}}async function a(e){try{let a=await (0,t.q)("/api/audit/run",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({cli:e.cli.length>0?e.cli:void 0,since:"all"===e.since?void 0:e.since,noCache:!!e.noCache||void 0})});if(!a.ok&&409!==a.status){let e=await a.text().catch(()=>"");throw console.error("audit run failed:",a.status,e),new s("post_failed",`audit run failed (${a.status})`)}}catch(e){if(e instanceof s)throw e;throw console.error("audit run request failed:",e),new s((0,t.c)(e)?"timeout":"network","audit run request failed")}let a=0;for(;;){await new Promise(e=>setTimeout(e,1e3));let e=null;try{let s=await (0,t.q)("/api/audit/status",{cache:"no-store"});s.ok&&(e=await s.json())}catch{}if(null===e){if(++a>=10)throw new s("network","audit status poll lost the server");continue}if(a=0,!e.running){if(e.error)throw new s("post_failed",e.error);return}}}e.s(["t",0,s,"s",0,a])},2917,e=>{var t=e.i(43476),s=e.i(71645),a=e.i(95187);let n=(0,a.createServerReference)("001e76cc3ece7218b24b9e6382ab3e016aeb9f2290",a.callServer,void 0,a.findSourceMapURL,"getJevSettingsAction"),l=(0,a.createServerReference)("0063958edce5309d23c8b62f3cb34558c89b70dded",a.callServer,void 0,a.findSourceMapURL,"removeJevConfigAction"),i=(0,a.createServerReference)("40c1e5828b89f194396a4edc086ed817d4931d5ac8",a.callServer,void 0,a.findSourceMapURL,"saveJevConfigAction"),r=(0,a.createServerReference)("40c96ce60ae14f95409628d3eeef0c1ffff3b426bd",a.callServer,void 0,a.findSourceMapURL,"setJevModeAction");var o=e.i(55574);let c=[{value:"typesafe",label:"typesafe"},{value:"openrouter",label:"openrouter"},{value:"vercel",label:"vercel ai gateway"},{value:"cloudflare",label:"cloudflare workers ai"},{value:"custom",label:"custom endpoint"}],d=[{value:"enforce",label:"enforce — jev's verdict counts"},{value:"observe",label:"observe — log only, regex decides"},{value:"off",label:"off — keep this config, don't ask jev"}],u="failproofai";function h(e){return{provider:e.provider&&e.provider!==u?e.provider:"typesafe",baseUrl:e.baseUrl,accountId:e.accountId,mode:e.mode}}function m({id:e,label:s,hint:a,children:n}){return(0,t.jsxs)("div",{className:"set-field",children:[(0,t.jsx)("label",{className:"set-field-label",htmlFor:e,children:s}),n,a?(0,t.jsx)("span",{className:"set-hint",children:a}):null]})}e.s(["default",0,function({initial:e}){var a,f,v;let b,[p,x]=(0,s.useState)(e),[j,w]=(0,s.useState)(e?h(e):{provider:"typesafe",baseUrl:"",accountId:"",mode:"enforce"}),[g,y]=(0,s.useState)(""),[k,N]=(0,s.useState)(!1),[C,S]=(0,s.useState)(null),[M,A]=(0,s.useState)(!1),$=(0,s.useRef)(!0),I=(0,s.useRef)(!1),E=(0,s.useCallback)(async()=>{try{let e=await n();if(!$.current)return;x(e),I.current||w(h(e))}catch{}},[]);(0,s.useEffect)(()=>($.current=!0,E(),()=>{$.current=!1}),[E]),(0,s.useEffect)(()=>{let e=()=>void E();return window.addEventListener("focus",e),document.addEventListener("visibilitychange",e),()=>{window.removeEventListener("focus",e),document.removeEventListener("visibilitychange",e)}},[E]);let R=(0,s.useCallback)((e,t)=>{I.current=!0,w(s=>({...s,[e]:t}))},[]),_=(0,s.useCallback)(async()=>{N(!0),S(null);try{let e=await i({provider:j.provider,baseUrl:j.baseUrl,accountId:j.accountId,mode:j.mode,token:g});if(!e.ok){S(e.problem),A(!0===e.needsToken);return}I.current=!1,A(!1),y(""),x(e.view),w(h(e.view)),(0,o.toast)(e.view.on?"jev is on.":"saved.")}catch{S("could not save that.")}finally{N(!1)}},[j,g]),F=(0,s.useCallback)(async e=>{N(!0),S(null);try{let t=await r(e);if(!t.ok)return void S(t.problem);I.current=!1,x(t.view),w(h(t.view)),(0,o.toast)("off"===e?"jev is off. hooks run the regex policies.":`jev is on, in ${e} mode.`)}catch{S("could not switch that.")}finally{N(!1)}},[]),D=(0,s.useCallback)(async()=>{N(!0),S(null),A(!1);try{let e=await l();if(!e.ok)return void S(e.problem);I.current=!1,y(""),x(e.view),w(h(e.view)),(0,o.toast)("jev is off. hooks run the regex policies.")}catch{S("could not turn that off.")}finally{N(!1)}},[]),L=null!==p&&"absent"!==p.status,U=p?.provider===u,T=p?.token,q=M?"needed for this save — type the token for this endpoint":T?"cloud"===T.source?"from this machine's FailproofAI Cloud connection":"env"===T.source?"configured, read from the environment — leave blank to keep it":"configured — leave blank to keep it":p?.status==="key-missing"?"read from the environment, which is not set in this shell — leave blank to keep it that way":"sent as a bearer token; stored owner-only at 0600";return(0,t.jsxs)("div",{className:"set-cell set-jev",children:[(0,t.jsx)("h2",{className:"set-cell-title",children:"jev · semantic evaluation"}),(0,t.jsx)("div",{className:"set-row set-row-main",children:(0,t.jsxs)("div",{className:"set-row-copy",children:[(0,t.jsx)("span",{className:p?.on?"set-strong":"set-dim",children:null===p?"reading…":p.on?`on. every judged command is also asked of ${(a=p.provider)===u?"FailproofAI Cloud":a??""}.`:"off"===p.status?"off — switched off. hooks run the regex policies exactly as before.":"not-connected"===p.status?"off — this machine is not connected to FailproofAI Cloud. hooks run the regex policies.":"key-lacks-jev"===p.status?"off — no jev key is stored for this machine's FailproofAI Cloud connection. hooks run the regex policies.":"key-missing"===p.status?"off for this dashboard — FAILPROOFAI_JEV_API_KEY is not set in its environment. hooks run the regex policies wherever it is unset, including under the daemon; a session that sets it still consults jev.":"off. hooks run the regex policies exactly as before."}),(0,t.jsx)("span",{className:"set-dim",children:p?.on?"observe"===p.mode?"observe: jev's answers are logged, the regex result is what gets enforced.":"enforce: jev's answers can clear a reviewable deny.":U?"through FailproofAI Cloud, on your org's plan — no endpoint or token of your own.":"a second opinion on the commands the regex policies flag. needs your own endpoint and token."})]})}),p&&(0,t.jsx)("dl",{className:"set-how-list set-jev-cloud",children:(0,t.jsxs)("div",{className:"set-how-row",children:[(0,t.jsx)("dt",{className:"set-how-label",children:"FailproofAI Cloud connection"}),(0,t.jsx)("dd",{className:"set-how-body",children:function(e){if(!e||!e.connected)return e?.jev==="yes"?"a jev key is stored, but no connection is":"not connected";let t=e.org??e.host??"connected",s="yes"===e.jev?"key carries jev":"refused"===e.jev?"jev key refused — credentials.json is not owner-only":"no jev key stored";return`connected to ${t} \xb7 ${s}`}(p.cloud)})]})}),p&&L&&(0,t.jsxs)("dl",{className:"set-how-list",children:[U&&(0,t.jsxs)("div",{className:"set-how-row",children:[(0,t.jsx)("dt",{className:"set-how-label",children:"provider"}),(0,t.jsx)("dd",{className:"set-how-body",children:p.cloud?.host?`FailproofAI Cloud \xb7 ${p.cloud.host}`:"FailproofAI Cloud"})]}),(0,t.jsxs)("div",{className:"set-how-row",children:[(0,t.jsx)("dt",{className:"set-how-label",children:"model"}),(0,t.jsx)("dd",{className:"set-how-body",children:function(e){switch(e.kind){case"id":return e.id;case"withheld":return"set to something that is not a model id — not shown here, in case it is a key";default:return"the provider's default"}}(p.model)})]}),(0,t.jsxs)("div",{className:"set-how-row",children:[(0,t.jsx)("dt",{className:"set-how-label",children:"token"}),(0,t.jsx)("dd",{className:"set-how-body",children:T?"cloud"===T.source?"FailproofAI Cloud connection":"env"===T.source?"from FAILPROOFAI_JEV_API_KEY":"configured":U?p.cloud?.connected?"none stored for this connection":"none — this machine is not connected":"none stored"})]}),p.reviewable&&(0,t.jsxs)("div",{className:"set-how-row",children:[(0,t.jsx)("dt",{className:"set-how-label",children:"reviewable"}),(0,t.jsx)("dd",{className:"set-how-body",children:p.reviewable.summary})]}),p.stats&&p.stats.total>0&&(0,t.jsxs)("div",{className:"set-how-row",children:[(0,t.jsx)("dt",{className:"set-how-label",children:"fallbacks"}),(0,t.jsx)("dd",{className:"set-how-body",children:`${(f=p.stats.fallbackRate,`${Math.round(100*f)}%`)} of ${p.stats.total} calls in the last ${Number.isInteger(b=(v=p.stats.windowMs)/36e5)&&b>=1?b%24==0?`${b/24}d`:`${b}h`:`${Math.max(1,Math.round(v/6e4))}m`} fell back to the regex engine.`})]})]}),p?.problem&&(0,t.jsxs)("p",{className:"set-warn",children:[p.problem,p.fix?` — ${p.fix}`:""]}),p?.reviewable?.problem&&(0,t.jsx)("p",{className:"set-warn",children:p.reviewable.problem}),(0,t.jsx)("div",{className:"set-rule"}),U?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("div",{className:"set-jev-fields",children:(0,t.jsx)(m,{id:"jev-cloud-mode",label:"mode",hint:"the endpoint and key come from this machine's FailproofAI Cloud connection",children:(0,t.jsx)("select",{id:"jev-cloud-mode",className:"set-select",value:p?.mode==="enforce"?"enforce":"observe",disabled:k||p?.mode==="off",onChange:e=>void F("enforce"===e.target.value?"enforce":"observe"),children:d.filter(e=>"off"!==e.value).map(e=>(0,t.jsx)("option",{value:e.value,children:e.label},e.value))})})}),C&&(0,t.jsx)("p",{className:"set-warn",children:C}),(0,t.jsx)("div",{className:"set-actions set-jev-actions",children:(0,t.jsx)("button",{type:"button",className:"btn btn-press",disabled:k,onClick:()=>void F(p?.mode==="off"?"observe":"off"),children:k?"[ switching… ]":p?.mode==="off"?"[ turn jev on ]":"[ turn jev off ]"})})]}):(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("div",{className:"set-jev-fields",children:[(0,t.jsx)(m,{id:"jev-provider",label:"provider",children:(0,t.jsx)("select",{id:"jev-provider",className:"set-select",value:j.provider,disabled:k,onChange:e=>R("provider",e.target.value),children:c.map(e=>(0,t.jsx)("option",{value:e.value,children:e.label},e.value))})}),(0,t.jsx)(m,{id:"jev-url",label:"endpoint url",hint:["custom"===j.provider?"required — https, or http to localhost in observe mode":"leave blank for the provider's own api",p?.baseUrlQueryWithheld?"the stored url's query string is not shown here — leave the url as it is to keep it":null].filter(Boolean).join(" · "),children:(0,t.jsx)("input",{id:"jev-url",type:"text",className:"set-text",inputMode:"url",autoComplete:"off",spellCheck:!1,placeholder:"custom"===j.provider?"https://…":"the provider's own api",value:j.baseUrl,disabled:k,onChange:e=>R("baseUrl",e.target.value)})}),"cloudflare"===j.provider&&(0,t.jsx)(m,{id:"jev-account",label:"account id",hint:"32 lowercase hex characters",children:(0,t.jsx)("input",{id:"jev-account",type:"text",className:"set-text",autoComplete:"off",spellCheck:!1,value:j.accountId,disabled:k,onChange:e=>R("accountId",e.target.value)})}),(0,t.jsx)(m,{id:"jev-token",label:"token",hint:q,children:(0,t.jsx)("input",{id:"jev-token",type:"password",className:"set-text",autoComplete:"off",spellCheck:!1,placeholder:T?"•••• kept":"",value:g,disabled:k,"aria-invalid":M||void 0,onChange:e=>{I.current=!0,y(e.target.value)}})}),(0,t.jsx)(m,{id:"jev-mode",label:"mode",children:(0,t.jsx)("select",{id:"jev-mode",className:"set-select",value:j.mode,disabled:k,onChange:e=>R("mode",e.target.value),children:d.map(e=>(0,t.jsx)("option",{value:e.value,children:e.label},e.value))})})]}),C&&(0,t.jsx)("p",{className:"set-warn",children:C}),(0,t.jsxs)("div",{className:"set-actions set-jev-actions",children:[(0,t.jsx)("button",{type:"button",className:"btn btn-press",disabled:k,onClick:()=>void _(),children:k?"[ saving… ]":L?"[ save changes ]":"[ turn jev on ]"}),L&&(0,t.jsx)("button",{type:"button",className:"set-link",disabled:k,onClick:()=>void D(),children:"turn jev off"})]})]})]})}],2917)},93237,e=>{var t=e.i(43476),s=e.i(71645),a=e.i(95187);let n=(0,a.createServerReference)("0042ee3f3518fc9ad9f544ba4f9df7e68d57ce0a04",a.callServer,void 0,a.findSourceMapURL,"getScheduledAuditAction"),l=(0,a.createServerReference)("40552ac1d76f989b4b1d866d70394d9192459bbde0",a.callServer,void 0,a.findSourceMapURL,"setAutoAuditAction"),i=(0,a.createServerReference)("4065281afd273e7a92cb535aa8aa5369bb864fc84e",a.callServer,void 0,a.findSourceMapURL,"setAuditIntervalAction");var r=e.i(86616),o=e.i(64237),c=e.i(55574),d=e.i(34713);function u(e){return new Date(e).toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}function h(e,t){let s=e-t;if(s<=0)return"now";let a=Math.floor(s/864e5),n=Math.floor(s%864e5/36e5);return a>0?n>0?`${a}d ${n}h`:`${a}d`:n>0?`${n}h`:`${Math.max(1,Math.floor(s/6e4))}m`}function m(e){return e.toLocaleString("en-US")}function f({label:e,value:s,sub:a,tone:n}){return(0,t.jsxs)("div",{className:"set-stat",children:[(0,t.jsx)("div",{className:"set-stat-label",children:e}),(0,t.jsx)("div",{className:`set-stat-value${n?` ${n}`:""}`,children:s}),(0,t.jsx)("div",{className:"set-stat-sub",children:a??" "})]})}function v({lastRunAtMs:e,nextDueAtMs:s,now:a}){if(null==e||null==s||s<=e)return null;let n=Math.min(100,Math.max(0,(a-e)/(s-e)*100));return(0,t.jsxs)("div",{className:"tape","aria-hidden":"true",children:[(0,t.jsxs)("div",{className:"tape-rail",children:[(0,t.jsx)("div",{className:"tape-fill",style:{width:`${n}%`}}),(0,t.jsx)("div",{className:"tape-now",style:{left:`${n}%`}})]}),(0,t.jsxs)("div",{className:"tape-ends",children:[(0,t.jsxs)("span",{children:["last scan · ",u(e)]}),(0,t.jsxs)("span",{className:"tape-next",children:["next · ",h(s,a)]})]})]})}function b({enabled:e,onChange:s,disabled:a,label:n}){return(0,t.jsx)("button",{type:"button",role:"switch","aria-checked":e,"aria-label":n,disabled:a,onClick:s,className:"sw","data-on":e?"true":"false",children:(0,t.jsx)("span",{className:"sw-knob"})})}let p=[{label:"reads",body:"every session transcript on disk — your prompts, the files your agents read and wrote, and command output."},{label:"runs",body:"entirely on this machine. the transcripts never leave it."},{label:"sends",body:"counts, redacted examples, and this machine's name — and only when a scan finds something harmful."}];e.s(["default",0,function({initial:e,jevPanel:a}){let[x,j]=(0,s.useState)(e),[w,g]=(0,s.useState)(e?.auto??!1),[y,k]=(0,s.useState)(e?.intervalDays??7),[N,C]=(0,s.useState)(!1),[S,M]=(0,s.useState)(!1),[A,$]=(0,s.useState)(!1),[I,E]=(0,s.useState)(null===e),[R,_]=(0,s.useState)(0),F=(0,s.useRef)(!0),D=(0,s.useRef)(null!==e),L=(0,s.useCallback)(async()=>{try{let e=await n();if(!F.current)return;j(e),D.current=!0,g(e.auto),k(e.intervalDays),_(Date.now()),E(!1)}catch{F.current&&!D.current&&E(!0)}},[]);(0,s.useEffect)(()=>(F.current=!0,L(),()=>{F.current=!1}),[L]),(0,s.useEffect)(()=>{let e=()=>void L();return window.addEventListener("focus",e),document.addEventListener("visibilitychange",e),()=>{window.removeEventListener("focus",e),document.removeEventListener("visibilitychange",e)}},[L]);let U=x?.signedInAs??null,T=x?.daemon==="running",q=x?.daemon==="unsupported-platform",O=x?.schedule??null,P=x?.lastScan??null,J=O?.lastExitCode!=null&&0!==O.lastExitCode&&75!==O.lastExitCode,B=(0,s.useCallback)(async()=>{C(!0);try{let e=await l(!0);if(!e.ok){if(g(!1),"unreachable"===e.reason)return void(0,c.toast)("could not reach the server. check your connection and try again.");await L(),$(!0),(0,c.toast)("that sign-in expired. one more code and it's on.");return}g(e.auto),(0,c.toast)("scheduled audits on."),await L()}catch{g(!1),(0,c.toast)("could not turn that on.")}finally{C(!1)}},[L]),K=(0,s.useCallback)(async()=>{if(w){C(!0);try{let e=await l(!1);e.ok&&g(e.auto),(0,c.toast)("scheduled audits off."),await L()}catch{(0,c.toast)("could not turn that off.")}finally{C(!1)}return}U?await B():$(!0)},[w,B,L,U]),V=(0,s.useCallback)(async e=>{C(!0);try{let t=await i(e);k(t.intervalDays),j(e=>e?{...e,intervalDays:t.intervalDays}:e),(0,c.toast)(`scanning every ${t.intervalDays} day${1===t.intervalDays?"":"s"}.`)}catch{k(x?.intervalDays??7),(0,c.toast)("could not save that.")}finally{C(!1)}},[x?.intervalDays]),Y=(0,s.useCallback)(async()=>{C(!0);try{await fetch("/api/auth/logout",{method:"POST"}),(0,c.toast)("signed out. scans continue; digests pause."),await L()}catch{(0,c.toast)("could not sign out.")}finally{C(!1)}},[L]),H=(0,s.useCallback)(async()=>{if(!S){M(!0);try{await (0,r.s)({cli:[],since:"all",noCache:!1}),(0,c.toast)("scan complete."),await L()}catch(e){(0,c.toast)(e instanceof r.t&&"timeout"===e.kind?"the scan is taking a while — it will finish in the background.":"the scan could not be completed.")}finally{M(!1)}}},[L,S]),Q=(0,s.useCallback)(async e=>{$(!1),await L(),await B()},[B,L]),W=x?T?{value:"running",sub:null!=x.daemonStartedAtMs&&R>0?`up ${function(e){let t=Math.floor(e/864e5);if(t>0)return`${t}d`;let s=Math.floor(e/36e5);return s>0?`${s}h`:`${Math.max(1,Math.floor(e/6e4))}m`}(Math.max(0,R-x.daemonStartedAtMs))}`:"",tone:"ok"}:q?{value:"unavailable",sub:"not on this platform"}:"not-installed"===x.daemon?{value:"not installed",sub:"run failproofai config",tone:"warn"}:"unknown"===x.daemon?{value:"unknown",sub:"could not read it"}:{value:"stopped",sub:"run failproofai config",tone:"warn"}:{value:"…",sub:"reading"},z=w?O?.nextDueAtMs==null?{value:"pending",sub:"after the next scan"}:{value:R>0?h(O.nextDueAtMs,R):"—",sub:u(O.nextDueAtMs)}:{value:"off",sub:"nothing scheduled"},G=P?{value:(0,d.g)(Date.parse(P.finishedAt)),sub:null!=P.sessionsScanned?`${m(P.sessionsScanned)} sessions`:"scanned"}:{value:"none yet",sub:"run one below"},X=P?null==P.findings?{value:"—",sub:"unreadable"}:{value:m(P.findings),sub:"this scan",...P.findings>0?{tone:"warn"}:{}}:{value:"—",sub:"no scan yet"};return(0,t.jsxs)("main",{className:"report",children:[(0,t.jsxs)("section",{className:"section set-sec",children:[(0,t.jsxs)("div",{className:"set-mast",children:[(0,t.jsx)("h1",{className:"set-title",children:"settings"}),(0,t.jsx)("p",{className:"set-lede",children:"keeping watch, so you don't have to."})]}),I?(0,t.jsxs)("div",{className:"set-console",children:[(0,t.jsx)("div",{className:"set-cell",children:(0,t.jsxs)("p",{className:"set-warn",children:["could not read this machine's settings. check that the dashboard can reach ",(0,t.jsx)("code",{children:"~/.failproofai/config.json"}),"."]})}),a]}):(0,t.jsxs)("div",{className:"set-console",children:[(0,t.jsxs)("div",{className:"set-stats",children:[(0,t.jsx)(f,{label:"daemon",...W}),(0,t.jsx)(f,{label:"next scan",...z}),(0,t.jsx)(f,{label:"last scan",...G}),(0,t.jsx)(f,{label:"findings",...X})]}),(0,t.jsxs)("div",{className:"set-cols",children:[(0,t.jsxs)("div",{className:"set-cell set-config",children:[(0,t.jsx)("h2",{className:"set-cell-title",children:"scheduled audit"}),(0,t.jsxs)("div",{className:"set-row set-row-main",children:[(0,t.jsx)(b,{enabled:w,disabled:N||null===x,onChange:()=>void K(),label:w?"turn off scheduled audits":"turn on scheduled audits"}),(0,t.jsxs)("div",{className:"set-row-copy",children:[(0,t.jsx)("span",{className:"set-strong",children:w?"scanning on a schedule.":"scan on a schedule."}),(0,t.jsx)("span",{className:"set-dim",children:w?"you'll get an email only when a scan finds something.":"off. nothing runs and nothing is sent."})]})]}),(0,t.jsxs)("div",{className:"set-row set-row-interval",children:[(0,t.jsx)("span",{className:"set-dim",children:"scan every"}),(0,t.jsx)("input",{type:"number",className:"set-num",min:1,max:90,value:y,disabled:N||null===x,"aria-label":"days between scheduled scans",onChange:e=>k(Number(e.target.value)),onBlur:e=>{let t=Number(e.target.value);Number.isFinite(t)?t!==x?.intervalDays&&V(t):k(x?.intervalDays??7)}}),(0,t.jsx)("span",{className:"set-dim",children:"days"}),(0,t.jsxs)("span",{className:"set-hint",children:[1,"–",90]})]}),(0,t.jsx)("div",{className:"set-rule"}),(0,t.jsx)("div",{className:"set-row set-row-identity",children:U?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("span",{className:"set-dim",children:"reports go to"}),(0,t.jsx)("span",{className:"set-email",children:U.email}),(0,t.jsx)("button",{type:"button",className:"set-link",disabled:N,onClick:()=>void Y(),children:"sign out"})]}):w?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)("span",{className:"set-warn",children:"signed out — scans continue, digests are paused."}),(0,t.jsx)("button",{type:"button",className:"set-link",disabled:N,onClick:()=>$(!0),children:"sign in to resume"})]}):(0,t.jsx)("span",{className:"set-dim",children:"turning this on asks for an email, so there is somewhere to send the report."})}),w&&x&&!T&&(0,t.jsx)("p",{className:"set-warn",children:q?"the background service isn't available on this platform, so scheduled scans can't run here. you can still run one now.":"not-installed"===x.daemon?"the background service isn't installed, so nothing will run on the timer. install it with `failproofai config`.":"the background service is stopped, so nothing will run until it starts. repair it with `failproofai config`."}),J&&(0,t.jsxs)("p",{className:"set-warn",children:["the last scheduled scan exited ",O?.lastExitCode,". the next one will still run."]}),(0,t.jsx)("div",{className:"set-actions",children:(0,t.jsx)("button",{type:"button",className:"btn btn-press",disabled:S,onClick:()=>void H(),children:S?"[ scanning… ]":"[ run a scan now ]"})})]}),(0,t.jsxs)("div",{className:"set-cell set-how",children:[(0,t.jsx)("h2",{className:"set-cell-title",children:"how it works"}),(0,t.jsx)("dl",{className:"set-how-list",children:p.map(e=>(0,t.jsxs)("div",{className:"set-how-row",children:[(0,t.jsx)("dt",{className:"set-how-label",children:e.label}),(0,t.jsx)("dd",{className:"set-how-body",children:e.body})]},e.label))})]})]}),a]}),w&&R>0&&(0,t.jsx)(v,{lastRunAtMs:O?.lastRunAtMs??null,nextDueAtMs:O?.nextDueAtMs??null,now:R})]}),(0,t.jsx)(o.f,{open:A,source:"settings_scheduled_audit",headline:"where should the report go?",subhead:"we'll send a one-time code to confirm.",onClose:()=>$(!1),onAuthed:e=>void Q(e)})]})}],93237)},29054,e=>{e.s(["q",0,function(e,t={},s=15e3){let a=AbortSignal.timeout(s),n=t.signal?AbortSignal.any([t.signal,a]):a;return fetch(e,{...t,signal:n})},"c",0,function(e){if(null===e||"object"!=typeof e)return!1;let t=e.name;return"AbortError"===t||"TimeoutError"===t}])},34713,e=>{e.s(["M",0,function(e){if(e<1e3)return`${e}ms`;let t=Math.round(e/100);if(t<600)return`${(t/10).toFixed(1)}s`;let s=Math.round(e/1e3);if(s<3600){let e=Math.floor(s/60);return`${e}m ${s%60}s`}let a=Math.round(e/6e4),n=Math.floor(a/60);return`${n}h ${a%60}m`},"g",0,function(e){let t=Date.now()-e;return t<6e4?`${Math.max(1,Math.floor(t/1e3))}s ago`:t<36e5?`${Math.floor(t/6e4)}m ago`:t<864e5?`${Math.floor(t/36e5)}h ago`:`${Math.floor(t/864e5)}d ago`}])}])})();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(()=>{"use strict";(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,
|
|
1
|
+
(()=>{"use strict";(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,32344,e=>{var t=e.i(95187);let a=(0,t.createServerReference)("601da5259db8771c5daa055322e9f6abf5b570520a",t.callServer,void 0,t.findSourceMapURL,"searchHookActivityAction");e.s(["f",0,a])},73520,e=>{var t=e.i(43476),a=e.i(24071),r=e.i(67927);e.s(["f",0,function({currentPage:e,totalPages:l,onPageChange:o}){if(l<=1)return null;let n=e=>`px-3 py-2 text-sm rounded-md transition-colors flex items-center gap-1 bg-muted text-muted-foreground ${e?"opacity-50 cursor-not-allowed":"hover:bg-muted/80"}`;return(0,t.jsxs)("div",{className:"flex items-center justify-center gap-2 py-4",children:[(0,t.jsxs)("button",{onClick:()=>o(e-1),disabled:1===e,className:n(1===e),"aria-label":"Previous page",children:[(0,t.jsx)(a.f,{className:"w-4 h-4"}),(0,t.jsx)("span",{className:"hidden sm:inline",children:"Previous"})]}),(0,t.jsx)("div",{className:"flex items-center gap-1",children:(()=>{if(l<=7)return Array.from({length:l},(e,t)=>t+1);let t=[1],a=Math.max(2,e-1),r=Math.min(l-1,e+1);e<=3&&(r=Math.min(5,l-1)),e>=l-2&&(a=Math.max(2,l-4)),a>2&&t.push("ellipsis-start");for(let e=a;e<=r;e++)t.push(e);return r<l-1&&t.push("ellipsis-end"),t.push(l),t})().map(a=>"string"==typeof a?(0,t.jsx)("span",{className:"px-2 text-muted-foreground",children:"..."},a):(0,t.jsx)("button",{onClick:()=>o(a),className:`min-w-[2.5rem] px-3 py-2 text-sm rounded-md transition-colors ${e===a?"bg-primary text-primary-foreground":"bg-muted text-muted-foreground hover:bg-muted/80"}`,"aria-label":`Page ${a}`,"aria-current":e===a?"page":void 0,children:a},a))}),(0,t.jsxs)("button",{onClick:()=>o(e+1),disabled:e===l,className:n(e===l),"aria-label":"Next page",children:[(0,t.jsx)("span",{className:"hidden sm:inline",children:"Next"}),(0,t.jsx)(r.f,{className:"w-4 h-4"})]})]})}])},24578,e=>{let t={claude:{id:"claude",label:"Claude Code",badgeClasses:"bg-orange-500/10 text-orange-400 border-orange-500/20"},codex:{id:"codex",label:"OpenAI Codex",badgeClasses:"bg-purple-500/10 text-purple-400 border-purple-500/20"},copilot:{id:"copilot",label:"GitHub Copilot",badgeClasses:"bg-blue-500/10 text-blue-400 border-blue-500/20"},cursor:{id:"cursor",label:"Cursor Agent",badgeClasses:"bg-emerald-500/10 text-emerald-400 border-emerald-500/20"},opencode:{id:"opencode",label:"OpenCode",badgeClasses:"bg-amber-500/10 text-amber-400 border-amber-500/20"},pi:{id:"pi",label:"Pi",badgeClasses:"bg-pink-500/10 text-pink-400 border-pink-500/20"},hermes:{id:"hermes",label:"Hermes",badgeClasses:"bg-indigo-500/10 text-indigo-400 border-indigo-500/20"},openclaw:{id:"openclaw",label:"OpenClaw",badgeClasses:"bg-teal-500/10 text-teal-400 border-teal-500/20"},factory:{id:"factory",label:"Factory Droid",badgeClasses:"bg-rose-500/10 text-rose-400 border-rose-500/20"},devin:{id:"devin",label:"Devin CLI",badgeClasses:"bg-violet-500/10 text-violet-400 border-violet-500/20"},antigravity:{id:"antigravity",label:"Antigravity CLI",badgeClasses:"bg-cyan-500/10 text-cyan-400 border-cyan-500/20"},goose:{id:"goose",label:"Goose",badgeClasses:"bg-lime-500/10 text-lime-400 border-lime-500/20"}};e.s(["z",0,["claude","codex","copilot","cursor","opencode","pi","hermes","openclaw","factory","devin","antigravity","goose"],"G",0,function(e){return t[e]?.badgeClasses??t.claude.badgeClasses},"A",0,function(e){return t[e]?.label??e},"U",0,function(e){return"string"==typeof e&&Object.prototype.hasOwnProperty.call(t,e)}])},34713,e=>{e.s(["M",0,function(e){if(e<1e3)return`${e}ms`;let t=Math.round(e/100);if(t<600)return`${(t/10).toFixed(1)}s`;let a=Math.round(e/1e3);if(a<3600){let e=Math.floor(a/60);return`${e}m ${a%60}s`}let r=Math.round(e/6e4),l=Math.floor(r/60);return`${l}h ${r%60}m`},"g",0,function(e){let t=Date.now()-e;return t<6e4?`${Math.max(1,Math.floor(t/1e3))}s ago`:t<36e5?`${Math.floor(t/6e4)}m ago`:t<864e5?`${Math.floor(t/36e5)}h ago`:`${Math.floor(t/864e5)}d ago`}])},80060,2711,e=>{var t=e.i(18566),a=e.i(71645);function r(e){let t=e.getFullYear(),a=String(e.getMonth()+1).padStart(2,"0"),r=String(e.getDate()).padStart(2,"0");return`${t}-${a}-${r}`}function l(e){var t;if(!e||(t=e,!/^\d{4}-\d{2}-\d{2}$/.test(t)||isNaN(Date.parse(t))))return null;let[a,r,l]=e.split("-").map(Number);return new Date(a,r-1,l,12,0,0)}e.s(["f",0,function(){let e=(0,t.useSearchParams)(),r=(0,t.useRouter)(),l=(0,t.usePathname)(),o=(0,a.useRef)(null),n=(0,a.useCallback)(t=>e.get(t),[e]),s=(0,a.useCallback)(()=>new URLSearchParams(e.toString()),[e]),i=(0,a.useCallback)(t=>{o.current&&clearTimeout(o.current),o.current=setTimeout(()=>{let a=new URLSearchParams(e.toString());for(let[e,r]of Object.entries(t))void 0===r||""===r?a.delete(e):a.set(e,r);let o=a.toString(),n=o?`${l}?${o}`:l;r.replace(n,{scroll:!1})},150)},[e,l,r]);return(0,a.useEffect)(()=>()=>{o.current&&clearTimeout(o.current)},[]),{get:n,getAll:s,setAll:i}}],80060),e.s(["V",0,function(e){let t={};return e.from&&(t.from=r(e.from)),e.to&&(t.to=r(e.to)),t},"l",0,function(e){if(0!==e.length)return e.map(e=>encodeURIComponent(e)).join(",")},"t",0,function(e){return e<=1?void 0:String(e)},"w",0,function(e){return e?e.split(",").map(e=>decodeURIComponent(e.trim())).filter(e=>e.length>0):[]},"m",0,function(e){if(!e)return 1;let t=parseInt(e,10);return Number.isFinite(t)&&t>=1?t:1},"S",0,function(e){return e&&["all","last-hour","today","last-7-days","last-30-days","custom"].includes(e)?e:"all"},"R",0,function(e,t){return{from:l(e),to:l(t)}},"$",0,function(e){return"all"===e?void 0:e}],2711)},89664,e=>{var t=e.i(56420);let a={name:"check",size:24,node:[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]};a.node;let r=(0,t.f)(a);e.s(["f",0,r],89664)},24071,e=>{var t=e.i(56420);let a={name:"chevron-left",size:24,node:[["path",{d:"m15 18-6-6 6-6",key:"1wnfg3"}]]};a.node;let r=(0,t.f)(a);e.s(["f",0,r],24071)},67927,e=>{var t=e.i(56420);let a={name:"chevron-right",size:24,node:[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]};a.node;let r=(0,t.f)(a);e.s(["f",0,r],67927)},8734,e=>{var t=e.i(56420);let a={name:"copy",size:24,node:[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]]};a.node;let r=(0,t.f)(a);e.s(["f",0,r],8734)},16306,e=>{var t=e.i(56420);let a={name:"shield-alert",size:24,node:[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"M12 8v4",key:"1got3b"}],["path",{d:"M12 16h.01",key:"1drbdi"}]]};a.node;let r=(0,t.f)(a);e.s(["f",0,r],16306)},84026,29407,e=>{var t=e.i(56420);let a={name:"shield-check",size:24,node:[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]]};a.node;let r=(0,t.f)(a);e.s(["f",0,r],84026);let l={name:"shield-x",size:24,node:[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m14.5 9.5-5 5",key:"17q4r4"}],["path",{d:"m9.5 9.5 5 5",key:"18nt4w"}]],aliases:["shield-close"]};l.node;let o=(0,t.f)(l);e.s(["f",0,o],29407)},82954,e=>{var t=e.i(56420);let a={name:"shield",size:24,node:[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}]]};a.node;let r=(0,t.f)(a);e.s(["f",0,r],82954)},63676,e=>{var t=e.i(56420);let a={name:"x",size:24,node:[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]};a.node;let r=(0,t.f)(a);e.s(["X",0,r],63676)}])})();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,53348,e=>{"use strict";var r=e.i(43476),t=e.i(71645),n=e.i(39440),o=e.i(9969);e.s(["default",0,function({error:e,reset:i}){return(0,t.useEffect)(()=>{(0,n.f)().then(r=>{(0,o.$)(r),(0,o.L)("client_error",{error_message:e.message,error_name:e.name,error_digest:e.digest,boundary:"global"})}).catch(()=>{})},[e]),(0,r.jsx)("html",{children:(0,r.jsx)("body",{children:(0,r.jsx)("main",{style:{minHeight:"100vh",display:"flex",alignItems:"center",justifyContent:"center",background:"#031035",color:"#f8fafc",fontFamily:"system-ui, sans-serif"},children:(0,r.jsxs)("div",{style:{textAlign:"center",padding:"2rem",border:"1px solid rgba(239,68,68,0.4)",borderRadius:"0.5rem",maxWidth:"500px"},children:[(0,r.jsx)("h2",{style:{color:"#ef4444",marginBottom:"0.5rem",fontSize:"1.25rem"},children:"Something went wrong"}),(0,r.jsx)("p",{style:{color:"#94a3b8",marginBottom:"1.5rem"},children:e.message||"An unexpected error occurred."}),(0,r.jsx)("button",{onClick:i,style:{padding:"0.5rem 1.25rem",background:"#3b82f6",color:"white",border:"none",borderRadius:"0.375rem",cursor:"pointer",fontSize:"0.875rem"},children:"Try again"})]})})})})}])}]);
|
|
@@ -4,6 +4,36 @@
|
|
|
4
4
|
|
|
5
5
|
### Added
|
|
6
6
|
|
|
7
|
+
- `fp fleet deploy --target POLICY=INTEGRATION[/agt_ID]` scopes a machine's assignment; repeat the flag to include multiple alternatives. `--all-agents POLICY` explicitly clears its scope. Deploy plans, show, history, rollback and JSON readback retain assignment targets.
|
|
8
|
+
- `fp policies publish <id> --kind regex|jev|both [--source f.mjs] [--semantic f.json]`: a
|
|
9
|
+
FailproofAI Cloud policy can carry Jev checks — `jev` (declarations only, no JavaScript) or
|
|
10
|
+
`both` (JavaScript reviewable by exactly its own checks; the server derives the authority).
|
|
11
|
+
The kind follows from what is given when `--kind` is omitted, and a kind that contradicts its
|
|
12
|
+
inputs is exit 2 before anything is sent. The positional source still works.
|
|
13
|
+
- `fp fleet deploy … --jev-mode off|observe|enforce|local` sets the machine's Jev mode from
|
|
14
|
+
FailproofAI Cloud (overriding the machine's own; `local` hands it back). Omitted, the request
|
|
15
|
+
carries no `jevMode` and the mode is unchanged; `--jev-mode` alone is a valid deploy. An
|
|
16
|
+
`observe` effect on a `jev` policy is refused (exit 2) — Jev is observed through the mode.
|
|
17
|
+
- `fp fleet jev-mode <machine…|--all> <mode>` changes only the Jev mode, through its own
|
|
18
|
+
route (`PUT /api/enforcement/deployments/{id}/jev-mode`), never the policy set — and
|
|
19
|
+
`fp fleet deploy --jev-mode` with no change to the set now uses it too. A deploy is a full
|
|
20
|
+
replace of the set the CLI read, which leaves out disabled policies' assignments, so a
|
|
21
|
+
mode change sent as a deploy deleted them and reverted concurrent edits.
|
|
22
|
+
- `fp fleet rollback` says which Jev mode it restores (`jev mode <now> → <then>`, and
|
|
23
|
+
`jevModeBefore` in `--json`); `fp fleet history` shows each generation's Jev mode and marks a
|
|
24
|
+
mode-only generation as a change.
|
|
25
|
+
- The `--jev-mode` and `fleet jev-mode` help says what `enforce` does: Jev's checks block calls
|
|
26
|
+
as well as clearing what they review, machine-wide (installed packs' checks included); an
|
|
27
|
+
`observe` effect on a `both` policy withholds its Jev checks.
|
|
28
|
+
- The `fleet deploy`, `fleet jev-mode` and `policies publish` help says Jev checks run on
|
|
29
|
+
FailproofAI Cloud and nothing is installed on the machine, and `fleet show`/`fleet jev-mode`
|
|
30
|
+
name the reasons a machine with a mode cannot ask (`jev_unconfigured`, `transcripts_disabled`)
|
|
31
|
+
and the `jev_budget` report.
|
|
32
|
+
- `fp policies list/show` print each version's kind and Jev check names (`show` also the
|
|
33
|
+
declarations); `fp fleet list/show` print the machine's Jev mode and the policy errors it
|
|
34
|
+
reported, and `--json` carries `kind`/`semantic`/`semanticSha256`/`authority`/`reviewedBy`,
|
|
35
|
+
`jevMode` and `policyErrors`/`policyErrorsAt` when the server sends them.
|
|
36
|
+
|
|
7
37
|
- `jev:evaluate` is a known permission — Jev through FailproofAI Cloud, charged to the
|
|
8
38
|
org's plan — so `fp keys` and `fp users` accept it instead of refusing it as unknown,
|
|
9
39
|
and the `admin` preset carries it, as the server's built-in admin set now does. The
|
|
@@ -13,9 +43,15 @@
|
|
|
13
43
|
(`events:add` + `policies:pull` + `jev:evaluate`), for an enrolled machine using Jev through
|
|
14
44
|
FailproofAI Cloud. `fp keys` also refuses `jev:evaluate` without both prerequisites itself
|
|
15
45
|
(exit 2, naming what is missing) instead of sending the key for the server's 422. (#833)
|
|
46
|
+
- `fp policies list` and `fp policies show` print what a `jev`/`both` version's checks take of a
|
|
47
|
+
machine's Jev question budget (`jevChars`), and a deploy refused with `jev_budget_exceeded`
|
|
48
|
+
lists each Jev policy's share, largest first (the `hint` under `--json`), so the refusal says
|
|
49
|
+
which policy to leave off.
|
|
16
50
|
|
|
17
51
|
### Changed
|
|
18
52
|
|
|
53
|
+
- `fp policies publish`'s refusal of Jev fields in the JavaScript now says where Jev checks go:
|
|
54
|
+
the policy's own declarations, `--kind both --semantic checks.json`.
|
|
19
55
|
- `fp guardrails summary` shows how many machines run Jev in each mode, reading the summary's
|
|
20
56
|
`jev.machines.observe` and `jev.machines.enforce`.
|
|
21
57
|
- `fp policies publish` (and `policies compose --publish`) refuses a source carrying Jev
|
|
@@ -27,6 +63,11 @@
|
|
|
27
63
|
same comment-aware detection, as the dashboard's publish form; the server does not read
|
|
28
64
|
the source, so nothing else refuses it for `fp`. (#833)
|
|
29
65
|
|
|
66
|
+
### Fixed
|
|
67
|
+
|
|
68
|
+
- `fp fleet history` keeps `gen`, `when` and `jev` visible at 80 columns (rollback needs `gen`): the policy list is shortened, then dropped, and `change` wraps first.
|
|
69
|
+
- `fp fleet show` and `fp fleet history` name an exact-profile target by the profile label the machine reports (`claude/.claude-work`, with the `agt_…` id kept in `show`), falling back to the bare id.
|
|
70
|
+
|
|
30
71
|
## 0.0.1 — 2026-09-22
|
|
31
72
|
|
|
32
73
|
**First stable release.** `0.0.1b1` and `0.0.1b2` are the two betas behind it; the
|
|
@@ -139,6 +139,48 @@ fp fleet deploy ci-runner-01 --add no-force-push
|
|
|
139
139
|
fp guardrails summary --since 24h
|
|
140
140
|
```
|
|
141
141
|
|
|
142
|
+
A policy has a **kind**: `regex` (JavaScript — the default, and what every
|
|
143
|
+
earlier version is), `jev` (Jev checks only, from a JSON file of declarations —
|
|
144
|
+
each exactly a failproofai pack manifest's semantic entry), or `both`
|
|
145
|
+
(JavaScript whose verdict its own Jev checks may clear, deployed as one).
|
|
146
|
+
Jev checks run on FailproofAI Cloud; nothing is installed on the machine.
|
|
147
|
+
`--jev-mode` sets a machine's Jev mode from FailproofAI Cloud, overriding the
|
|
148
|
+
machine's own; omit it and the mode is left alone. Under `observe` or `enforce`
|
|
149
|
+
the machine sends each checked tool call to FailproofAI Cloud, which asks the
|
|
150
|
+
checks deployed to it. The mode is the machine's, so it covers installed packs'
|
|
151
|
+
checks too, and in `enforce` Jev's checks **block** calls as well as clearing
|
|
152
|
+
the regex verdicts they review. `fp fleet jev-mode` changes only the mode, and
|
|
153
|
+
never rewrites a machine's policy set.
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
fp policies publish prod-db-intent --kind jev --semantic ./checks.json
|
|
157
|
+
fp policies publish no-prod-db --kind both --source ./rule.mjs --semantic ./checks.json
|
|
158
|
+
fp fleet deploy ci-runner-01 --add prod-db-intent --jev-mode observe
|
|
159
|
+
fp fleet jev-mode --all enforce # the mode alone, every machine with a deployment
|
|
160
|
+
fp fleet show ci-runner-01 # its Jev mode, and any policy errors it reported
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Published policy versions do not contain agent-routing code. Each assignment
|
|
164
|
+
can instead target every agent (the default), an integration including future
|
|
165
|
+
profiles, or a particular profile reported by that machine. The same target
|
|
166
|
+
applies to the regex and Jev parts of a `both` policy:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
fp fleet deploy ci-runner-01 --add no-prod-db --target no-prod-db=hermes
|
|
170
|
+
fp fleet deploy ci-runner-01 --target no-prod-db=hermes/agt_1234567890abcdef
|
|
171
|
+
fp fleet deploy ci-runner-01 --all-agents no-prod-db # explicitly remove targeting
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Repeat `--target` for multiple alternatives on one assignment. `fp fleet
|
|
175
|
+
show` and `fp fleet history` display the assigned scope; `--json` includes
|
|
176
|
+
`agentTargets` on each targeted policy. A machine must report agent-scoping
|
|
177
|
+
support before accepting a new target. When its identity cannot be resolved,
|
|
178
|
+
a targeted policy does not match; unscoped policy remains in force.
|
|
179
|
+
Newly installed shell hooks carry their settings scope. Reinstall older hooks
|
|
180
|
+
to add the hint; without it, or for an integration that cannot report the
|
|
181
|
+
source of multiple installed scopes, the machine reports
|
|
182
|
+
`agent_scope_unresolved` rather than guessing which profile ran the call.
|
|
183
|
+
|
|
142
184
|
**A deploy REPLACES a machine's whole policy set.** The server takes the full
|
|
143
185
|
list and does not merge, so `fleet deploy` reads what the machine currently runs,
|
|
144
186
|
applies your `--add`/`--remove`, prints the complete resulting set, and writes
|
|
@@ -256,4 +298,3 @@ working as an opt-out if it is ever switched back on. See
|
|
|
256
298
|
cd fp-cloud-cli
|
|
257
299
|
uv run --extra dev pytest
|
|
258
300
|
```
|
|
259
|
-
|
|
@@ -23,7 +23,7 @@ import re
|
|
|
23
23
|
import uuid
|
|
24
24
|
from dataclasses import dataclass
|
|
25
25
|
from enum import Enum
|
|
26
|
-
from typing import Any, Callable, Dict, Iterator, List, Optional, Sequence, Union
|
|
26
|
+
from typing import Any, Callable, Dict, Iterator, List, Optional, Sequence, Tuple, Union
|
|
27
27
|
|
|
28
28
|
import httpx
|
|
29
29
|
|
|
@@ -487,9 +487,39 @@ def _raise_for_status(response: httpx.Response, ctx: ClientContext) -> None:
|
|
|
487
487
|
message or f"Request failed with status {response.status_code}.",
|
|
488
488
|
status=response.status_code,
|
|
489
489
|
request_id=request_id,
|
|
490
|
+
hint=_jev_budget_breakdown(response),
|
|
490
491
|
)
|
|
491
492
|
|
|
492
493
|
|
|
494
|
+
def _jev_budget_breakdown(response: httpx.Response) -> Optional[str]:
|
|
495
|
+
"""The per-policy breakdown of a deploy's ``jev_budget_exceeded`` refusal.
|
|
496
|
+
|
|
497
|
+
The message carries only the total (``used``/``budget``); the body also lists
|
|
498
|
+
every Jev policy version the machine would carry with its question
|
|
499
|
+
characters (``policies: [{id, version, chars}]``), which is what tells an
|
|
500
|
+
operator WHICH policy to leave off. Largest first. ``None`` for any other
|
|
501
|
+
error, and for a publish refusal, which names no other policies.
|
|
502
|
+
"""
|
|
503
|
+
try:
|
|
504
|
+
data = response.json()
|
|
505
|
+
except Exception:
|
|
506
|
+
return None
|
|
507
|
+
if not isinstance(data, dict) or data.get("code") != "jev_budget_exceeded":
|
|
508
|
+
return None
|
|
509
|
+
rows = [
|
|
510
|
+
(str(p.get("id", "")), p.get("version"), p["chars"])
|
|
511
|
+
for p in data.get("policies") or []
|
|
512
|
+
if isinstance(p, dict) and isinstance(p.get("chars"), int)
|
|
513
|
+
]
|
|
514
|
+
if not rows:
|
|
515
|
+
return None
|
|
516
|
+
rows.sort(key=lambda row: -row[2])
|
|
517
|
+
parts = [f"{pid}@{version} {chars:,}" if version is not None else f"{pid} {chars:,}"
|
|
518
|
+
for pid, version, chars in rows]
|
|
519
|
+
return ("Jev question characters per policy: " + " · ".join(parts)
|
|
520
|
+
+ " — deploy fewer of these Jev policies to this machine")
|
|
521
|
+
|
|
522
|
+
|
|
493
523
|
def _get_json(ctx: ClientContext, path: str, params: Optional[Dict[str, Any]] = None) -> Any:
|
|
494
524
|
clean = {k: v for k, v in (params or {}).items() if v is not None}
|
|
495
525
|
url = _path(ctx, path) # chokepoint 1 of 4 for the /api -> /v1 rewrite
|
|
@@ -1643,22 +1673,43 @@ def list_policies(ctx: ClientContext) -> List[PolicyVersion]:
|
|
|
1643
1673
|
|
|
1644
1674
|
|
|
1645
1675
|
def publish_policy(
|
|
1646
|
-
ctx: ClientContext,
|
|
1676
|
+
ctx: ClientContext,
|
|
1677
|
+
policy_id: str,
|
|
1678
|
+
source: Optional[str],
|
|
1679
|
+
description: str = "",
|
|
1680
|
+
*,
|
|
1681
|
+
kind: Optional[str] = None,
|
|
1682
|
+
semantic: Optional[List[Any]] = None,
|
|
1647
1683
|
) -> PolicyVersion:
|
|
1648
1684
|
"""POST /api/enforcement/policies — mints a NEW VERSION; never edits in place.
|
|
1649
1685
|
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1686
|
+
``kind`` is ``regex`` (JavaScript ``source`` only — what an omitted kind
|
|
1687
|
+
means to the server), ``jev`` (Jev declarations in ``semantic``, no
|
|
1688
|
+
``source``) or ``both`` (the two together: the JavaScript is reviewable by
|
|
1689
|
+
exactly its own checks). ``authority``/``reviewedBy`` are derived by the
|
|
1690
|
+
server from the kind and are never sent.
|
|
1691
|
+
|
|
1692
|
+
Refuses Jev fields in the JAVASCRIPT before anything is sent, here because
|
|
1693
|
+
`policies publish` and `policies compose --publish` both route through it —
|
|
1694
|
+
and regardless of `--no-verify`, which only skips the syntax check. Jev
|
|
1695
|
+
checks belong in ``semantic``, where they take effect.
|
|
1653
1696
|
"""
|
|
1654
|
-
problem = cloud_publish_problem(source)
|
|
1697
|
+
problem = cloud_publish_problem(source or "")
|
|
1655
1698
|
if problem:
|
|
1656
1699
|
raise ApiError(
|
|
1657
1700
|
f"{policy_id} cannot be published as a cloud policy: {problem}",
|
|
1658
|
-
hint=("Jev checks
|
|
1659
|
-
"
|
|
1701
|
+
hint=("put Jev checks in the policy's Jev declarations — `--kind both --semantic "
|
|
1702
|
+
"checks.json` — or ship them in a failproofai pack with `failproofai publish`"),
|
|
1660
1703
|
)
|
|
1661
|
-
body = {"id": policy_id, "
|
|
1704
|
+
body: Dict[str, Any] = {"id": policy_id, "description": description}
|
|
1705
|
+
# `source` is omitted for a Jev-only policy: the server takes absent (or
|
|
1706
|
+
# empty) as "no JavaScript", which is what a `jev` version is.
|
|
1707
|
+
if source:
|
|
1708
|
+
body["source"] = source
|
|
1709
|
+
if kind is not None:
|
|
1710
|
+
body["kind"] = kind
|
|
1711
|
+
if semantic is not None:
|
|
1712
|
+
body["semantic"] = semantic
|
|
1662
1713
|
return PolicyVersion.from_dict(_post_json(ctx, "/api/enforcement/policies", body) or {})
|
|
1663
1714
|
|
|
1664
1715
|
|
|
@@ -1681,6 +1732,29 @@ def list_machines(ctx: ClientContext) -> List[Machine]:
|
|
|
1681
1732
|
return [Machine.from_dict(m) for m in items]
|
|
1682
1733
|
|
|
1683
1734
|
|
|
1735
|
+
def machine_agents(ctx: ClientContext, machine_id: str) -> List[Dict[str, Any]]:
|
|
1736
|
+
"""GET /api/enforcement/machines/{id}/agents — the agent profiles the daemon
|
|
1737
|
+
last reported: `[{instanceId, integration, profileLabel, scope, ...}]`."""
|
|
1738
|
+
data = _get_json(ctx, f"/api/enforcement/machines/{machine_id}/agents")
|
|
1739
|
+
items = data if isinstance(data, list) else (data or {}).get("agents", [])
|
|
1740
|
+
return [a for a in items if isinstance(a, dict)]
|
|
1741
|
+
|
|
1742
|
+
|
|
1743
|
+
def agent_labels(ctx: ClientContext, machine_id: str) -> Dict[str, str]:
|
|
1744
|
+
"""`{instanceId: profileLabel}` for one machine, or `{}` when unavailable.
|
|
1745
|
+
|
|
1746
|
+
Best effort on purpose: it only names the `agt_…` ids an exact-profile
|
|
1747
|
+
assignment carries. A server without the roster route, or a machine that
|
|
1748
|
+
never reported one, falls back to the bare id rather than failing the view.
|
|
1749
|
+
"""
|
|
1750
|
+
try:
|
|
1751
|
+
agents = machine_agents(ctx, machine_id)
|
|
1752
|
+
except Exception: # noqa: BLE001 — a missing label must never fail the read
|
|
1753
|
+
return {}
|
|
1754
|
+
return {str(a["instanceId"]): str(a["profileLabel"])
|
|
1755
|
+
for a in agents if a.get("instanceId") and a.get("profileLabel")}
|
|
1756
|
+
|
|
1757
|
+
|
|
1684
1758
|
def rename_machine(ctx: ClientContext, machine_id: str, label: str) -> Dict[str, Any]:
|
|
1685
1759
|
"""PATCH /api/enforcement/machines/{id} — a human label, not the id."""
|
|
1686
1760
|
path = f"/api/enforcement/machines/{machine_id}"
|
|
@@ -1708,14 +1782,45 @@ def get_deployment(ctx: ClientContext, machine_id: str) -> Optional[Deployment]:
|
|
|
1708
1782
|
|
|
1709
1783
|
|
|
1710
1784
|
def deploy_policies(
|
|
1711
|
-
ctx: ClientContext,
|
|
1785
|
+
ctx: ClientContext,
|
|
1786
|
+
machine_id: str,
|
|
1787
|
+
policies: Sequence[PolicyRef],
|
|
1788
|
+
jev_mode: Optional[str] = None,
|
|
1712
1789
|
) -> Deployment:
|
|
1713
|
-
"""PUT /api/enforcement/deployments/{id} — REPLACES the machine's whole set.
|
|
1790
|
+
"""PUT /api/enforcement/deployments/{id} — REPLACES the machine's whole set.
|
|
1791
|
+
|
|
1792
|
+
``jev_mode`` (``off|observe|enforce|local``) is sent only when given: the
|
|
1793
|
+
server reads an ABSENT ``jevMode`` as "keep the machine's current one", so a
|
|
1794
|
+
deploy that says nothing about Jev never changes it. ``local`` stops
|
|
1795
|
+
FailproofAI Cloud overriding the machine's own mode.
|
|
1796
|
+
"""
|
|
1714
1797
|
path = f"/api/enforcement/deployments/{machine_id}"
|
|
1715
|
-
body = {"policies": [p.to_dict() for p in policies]}
|
|
1798
|
+
body: Dict[str, Any] = {"policies": [p.to_dict() for p in policies]}
|
|
1799
|
+
if jev_mode is not None:
|
|
1800
|
+
body["jevMode"] = jev_mode
|
|
1716
1801
|
return Deployment.from_dict(_request_json(ctx, "PUT", path, json_body=body) or {})
|
|
1717
1802
|
|
|
1718
1803
|
|
|
1804
|
+
def set_jev_mode(ctx: ClientContext, machine_id: str, jev_mode: str) -> Tuple[Deployment, bool]:
|
|
1805
|
+
"""PUT /api/enforcement/deployments/{id}/jev-mode — change ONLY the Jev mode.
|
|
1806
|
+
|
|
1807
|
+
``jev_mode`` is ``off|observe|enforce|local`` (``local`` stops FailproofAI
|
|
1808
|
+
Cloud overriding the machine's own mode). Unlike a deploy, this never
|
|
1809
|
+
touches the machine's policy set: a deploy is a full replace of the set, and
|
|
1810
|
+
the set a client can read leaves out disabled policies' assignments, so a
|
|
1811
|
+
mode change sent as a deploy deleted them — and reverted any edit made since
|
|
1812
|
+
the set was read. It still mints a new generation, which is how the machine
|
|
1813
|
+
picks the mode up.
|
|
1814
|
+
|
|
1815
|
+
Returns the deployment and whether anything changed: setting the mode the
|
|
1816
|
+
machine already has changes nothing and mints nothing (``changed: false``).
|
|
1817
|
+
A machine with no deployment is a 404 — a deploy is how it gets its first.
|
|
1818
|
+
"""
|
|
1819
|
+
path = f"/api/enforcement/deployments/{machine_id}/jev-mode"
|
|
1820
|
+
data = _request_json(ctx, "PUT", path, json_body={"jevMode": jev_mode}) or {}
|
|
1821
|
+
return Deployment.from_dict(data), bool(data.get("changed", True))
|
|
1822
|
+
|
|
1823
|
+
|
|
1719
1824
|
def deployment_history(ctx: ClientContext, machine_id: str) -> List[Dict[str, Any]]:
|
|
1720
1825
|
"""GET /api/enforcement/deployments/{id}/history — every generation, newest first."""
|
|
1721
1826
|
path = f"/api/enforcement/deployments/{machine_id}/history"
|