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.
Files changed (195) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  6. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  7. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.rsc +2 -2
  10. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  11. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +2 -2
  12. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  14. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  16. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  17. package/.next/standalone/.next/server/app/_not-found.rsc +3 -3
  18. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  19. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  20. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  21. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/api/audit/run/route.js +22 -22
  23. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  31. package/.next/standalone/.next/server/app/audit/page.js +1 -1
  32. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  34. package/.next/standalone/.next/server/app/index.html +1 -1
  35. package/.next/standalone/.next/server/app/index.rsc +3 -3
  36. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  37. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +3 -3
  38. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  39. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  40. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  42. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
  43. package/.next/standalone/.next/server/app/policies/page.js +4 -4
  44. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  46. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  47. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  49. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  50. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  51. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  52. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  53. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  54. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  55. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  56. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +8 -8
  57. package/.next/standalone/.next/server/app/settings/page.js +7 -7
  58. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  60. package/.next/standalone/.next/server/chunks/{[externals]__0-ha5ydqq0mws._.js → [externals]__0vt9i6fqinjne._.js} +1 -1
  61. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__20xe05hm5u8_1._.js → [root-of-the-server]__0f_4g-vazrvxv._.js} +1 -1
  62. package/.next/standalone/.next/server/chunks/_0hf5q4xzyg40j._.js +1 -1
  63. package/.next/standalone/.next/server/chunks/_0tovk6q0miud6._.js +1 -1
  64. package/.next/standalone/.next/server/chunks/_0trp3ycqv17cl._.js +1 -1
  65. package/.next/standalone/.next/server/chunks/_19011-u44143j._.js +1 -1
  66. package/.next/standalone/.next/server/chunks/_1ek68lnh2daur._.js +1 -1
  67. package/.next/standalone/.next/server/chunks/{_0kc-piux3hady._.js → _1y590mjzpkbos._.js} +2 -2
  68. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4vkpxxu7._.js +1 -1
  69. package/.next/standalone/.next/server/chunks/src_hooks_0_em82s16zfym._.js +5 -0
  70. package/.next/standalone/.next/server/chunks/src_hooks_builtin-policies_ts_1yq5ybgx1v98e._.js +3 -0
  71. package/.next/standalone/.next/server/chunks/src_hooks_custom-hooks-loader_ts_0lnb3n3h94wnn._.js +1 -1
  72. package/.next/standalone/.next/server/chunks/src_hooks_daemon-client_ts_0il_yu1zc0291._.js +1 -1
  73. package/.next/standalone/.next/server/chunks/src_hooks_fp-home_ts_09kv0bn_4twyh._.js +1 -1
  74. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0588st-tpiuia._.js +3 -0
  75. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__08ch3staq3ejw._.js +1 -1
  76. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0vcyntk4snf0f._.js → [root-of-the-server]__0b78b0j8iyv6l._.js} +1 -1
  77. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0bigvy9xmv2ka._.js +1 -1
  78. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h86bl2qf0k_o._.js +1 -1
  79. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1_xbg2kx30wp-._.js → [root-of-the-server]__0naevg4bcoh54._.js} +1 -1
  80. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0pia5h3p8re4c._.js +1 -1
  81. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1-ey9fgesfbc0._.js +1 -1
  82. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__15usvs-ms-0hf._.js +1 -1
  83. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__16gb3_92zvk5x._.js +1 -1
  84. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1avi2kypusyb_._.js +1 -1
  85. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1r7drlqli-ado._.js +1 -1
  86. package/.next/standalone/.next/server/chunks/ssr/_0eoura3_9gjb-._.js +1 -1
  87. package/.next/standalone/.next/server/chunks/ssr/_0pedwblqdoh58._.js +1 -1
  88. package/.next/standalone/.next/server/chunks/ssr/_128ebxr83nk6g._.js +3 -0
  89. package/.next/standalone/.next/server/chunks/ssr/{_1gb0ifpkowwub._.js → _1a3tubds7s7ow._.js} +10 -9
  90. package/.next/standalone/.next/server/chunks/ssr/_1djqs9i27e4kg._.js +1 -1
  91. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo5_vm5f.js +2 -2
  92. package/.next/standalone/.next/server/chunks/ssr/app_actions_get-scheduled-audit_ts_0ei9snixb5fd-._.js +1 -1
  93. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47q0xq5w._.js +1 -1
  94. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x33jqb6._.js +1 -1
  95. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpcz0uxur._.js +1 -1
  96. package/.next/standalone/.next/server/chunks/ssr/app_settings_02tf1h47jmhhd._.js +1 -1
  97. 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
  98. package/.next/standalone/.next/server/chunks/ssr/package_json_[json]_cjs_013tntikdft59._.js +1 -1
  99. package/.next/standalone/.next/server/chunks/ssr/src_hooks_06kzv9ddabn3_._.js +1 -1
  100. package/.next/standalone/.next/server/chunks/ssr/src_hooks_0agdmptetetdt._.js +5 -0
  101. package/.next/standalone/.next/server/chunks/ssr/src_hooks_0mdwgjisv4fub._.js +5 -0
  102. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1cv9_c4l1tzrm._.js +1 -1
  103. package/.next/standalone/.next/server/chunks/ssr/src_hooks_daemon-client_ts_0k41_i7jg6ndv._.js +1 -1
  104. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  105. package/.next/standalone/.next/server/pages/404.html +1 -1
  106. package/.next/standalone/.next/server/pages/500.html +1 -1
  107. package/.next/standalone/.next/server/preview-props.json +3 -3
  108. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  109. package/.next/standalone/.next/server/server-reference-manifest.json +23 -23
  110. package/.next/standalone/.next/static/chunks/{3tn1bzk094e8e.js → 0_zfad4co-t2z.js} +1 -1
  111. package/.next/standalone/.next/static/chunks/0jutamwqz8pg5.js +1 -0
  112. package/.next/standalone/.next/static/chunks/{2meqb9kygrncb.js → 114hbolvrv94e.js} +1 -1
  113. package/.next/standalone/.next/static/chunks/{2znt1784j9gd9.js → 18433qz-8njv3.js} +1 -1
  114. package/.next/standalone/.next/static/chunks/1b32-afqacaen.js +1 -0
  115. package/.next/standalone/.next/static/chunks/{13jv-xvabeb62.js → 1p-hsi58i-6h0.js} +1 -1
  116. package/.next/standalone/.next/static/chunks/{0pjxnp927y7_j.js → 2-tf6_7u623kd.js} +1 -1
  117. package/.next/standalone/.next/static/chunks/{2qz5piiir-qqm.js → 24w3x2da9sk49.js} +1 -1
  118. package/.next/standalone/.next/static/chunks/{0a-ccsp3xmypz.js → 3-pku9qwv1yfu.js} +1 -1
  119. package/.next/standalone/.next/static/chunks/42e8zab314654.js +1 -0
  120. package/.next/standalone/fp-cloud-cli/CHANGELOG.md +41 -0
  121. package/.next/standalone/fp-cloud-cli/README.md +42 -1
  122. package/.next/standalone/fp-cloud-cli/fp_cli/client.py +117 -12
  123. package/.next/standalone/fp-cloud-cli/fp_cli/commands/fleet_cmds.py +285 -19
  124. package/.next/standalone/fp-cloud-cli/fp_cli/commands/policies_cmds.py +84 -18
  125. package/.next/standalone/fp-cloud-cli/fp_cli/enforcement.py +170 -6
  126. package/.next/standalone/fp-cloud-cli/fp_cli/models.py +89 -6
  127. package/.next/standalone/fp-cloud-cli/fp_cli/output.py +259 -21
  128. package/.next/standalone/fp-cloud-cli/fp_cli/policy_check.py +8 -5
  129. package/.next/standalone/fp-cloud-cli/skill/SKILL.md +2 -2
  130. package/.next/standalone/fp-cloud-cli/skill/references/commands.md +17 -9
  131. package/.next/standalone/fp-cloud-cli/tests/test_cloud_jev.py +692 -0
  132. package/.next/standalone/fp-cloud-cli/tests/test_enforcement_logic.py +46 -0
  133. package/.next/standalone/fp-cloud-cli/tests/test_output.py +23 -0
  134. package/.next/standalone/hermes-plugin/README.md +6 -4
  135. package/.next/standalone/hermes-plugin/__init__.py +16 -0
  136. package/.next/standalone/hermes-plugin/client.py +5 -1
  137. package/.next/standalone/hermes-plugin/plugin.yaml +1 -1
  138. package/.next/standalone/package.json +9 -9
  139. package/.next/standalone/server.js +1 -1
  140. package/bin/failproofai.mjs +17 -1
  141. package/dist/cli.mjs +2930 -1747
  142. package/dist/worker.mjs +1450 -145
  143. package/hermes-plugin/README.md +6 -4
  144. package/hermes-plugin/__init__.py +16 -0
  145. package/hermes-plugin/client.py +5 -1
  146. package/hermes-plugin/plugin.yaml +1 -1
  147. package/package.json +9 -9
  148. package/src/audit/open-browser.ts +1 -19
  149. package/src/hooks/agent-roster.ts +172 -0
  150. package/src/hooks/agent-targets.ts +60 -0
  151. package/src/hooks/cloud-connection.ts +97 -27
  152. package/src/hooks/cloud-enrollment-cli.ts +7 -4
  153. package/src/hooks/cloud-introspect.ts +2 -5
  154. package/src/hooks/cloud-managed-policies.ts +380 -20
  155. package/src/hooks/cloud-policy-errors.ts +481 -0
  156. package/src/hooks/config-status.ts +1 -1
  157. package/src/hooks/configure-wizard.ts +15 -311
  158. package/src/hooks/custom-hooks-loader.ts +26 -3
  159. package/src/hooks/daemon-client.ts +9 -4
  160. package/src/hooks/daemon-service.ts +2 -1
  161. package/src/hooks/effective-reviewers.ts +22 -1
  162. package/src/hooks/fp-home.ts +6 -0
  163. package/src/hooks/handler.ts +235 -18
  164. package/src/hooks/health-probe.ts +15 -0
  165. package/src/hooks/integrations.ts +18 -11
  166. package/src/hooks/jev-cli.ts +197 -0
  167. package/src/hooks/jev-cloud-connection.ts +92 -0
  168. package/src/hooks/manager.ts +122 -16
  169. package/src/hooks/pack-failclosed.ts +2 -0
  170. package/src/hooks/pack-manifest.ts +8 -1
  171. package/src/hooks/policy-evaluator.ts +5 -1
  172. package/src/hooks/policy-registry.ts +3 -2
  173. package/src/hooks/policy-reviewability.ts +21 -5
  174. package/src/hooks/semantic/cloud-jev-health.ts +227 -0
  175. package/src/hooks/semantic/cloud-jev.ts +677 -0
  176. package/src/hooks/semantic/jev-client.ts +18 -1
  177. package/src/hooks/semantic/jev-config.ts +173 -3
  178. package/src/hooks/semantic/jev-review.ts +129 -10
  179. package/src/hooks/semantic/pack-policies.ts +2 -1
  180. package/src/hooks/semantic/types.ts +8 -0
  181. package/src/hooks/session-pause-cli.ts +1 -1
  182. package/src/hooks/tui.ts +0 -271
  183. package/src/hooks/worker-server.ts +5 -1
  184. package/.next/standalone/.next/server/chunks/src_hooks_1aveq0uhs2t1b._.js +0 -5
  185. package/.next/standalone/.next/server/chunks/src_hooks_1eem5a7cyqlb2._.js +0 -3
  186. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ts0fg5yih1c1._.js +0 -3
  187. package/.next/standalone/.next/server/chunks/ssr/_1ve8y151ua7qx._.js +0 -3
  188. package/.next/standalone/.next/server/chunks/ssr/src_hooks_effective-reviewers_ts_1h4wtvo55j16x._.js +0 -5
  189. package/.next/standalone/.next/static/chunks/2ank2u3apy8m6.js +0 -1
  190. package/.next/standalone/.next/static/chunks/2ohktp_-lkv3x.js +0 -1
  191. package/.next/standalone/.next/static/chunks/2py0eldhwz8ay.js +0 -1
  192. package/src/hooks/cloud-login.ts +0 -245
  193. /package/.next/standalone/.next/static/{SNr7o_-G4kNBQTR-iLZMM → xq8UgF5yFxty1VWxY2hyn}/_buildManifest.js +0 -0
  194. /package/.next/standalone/.next/static/{SNr7o_-G4kNBQTR-iLZMM → xq8UgF5yFxty1VWxY2hyn}/_clientMiddlewareManifest.js +0 -0
  195. /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,23417,e=>{var t=e.i(95187);let a=(0,t.createServerReference)("6054c975f99afafd19c3ef2563888cdfd417fcee2d",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)}])})();
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, policy_id: str, source: str, description: str = ""
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
- Refuses Jev fields a cloud policy never reads before anything is sent, here
1651
- because `policies publish` and `policies compose --publish` both route through
1652
- it — and regardless of `--no-verify`, which only skips the syntax check.
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 ship in a failproofai pack — `failproofai publish` — where "
1659
- "semanticPolicies.add and authority: \"reviewable\" take effect"),
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, "source": source, "description": description}
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, machine_id: str, policies: Sequence[PolicyRef]
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"