failproofai 1.0.0-beta.5 → 1.0.0-beta.7

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 (368) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-path-routes-manifest.json +2 -1
  3. package/.next/standalone/.next/build-manifest.json +3 -3
  4. package/.next/standalone/.next/prerender-manifest.json +3 -3
  5. package/.next/standalone/.next/required-server-files.json +1 -1
  6. package/.next/standalone/.next/routes-manifest.json +6 -0
  7. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  8. package/.next/standalone/.next/server/app/_global-error/page.js +3 -1
  9. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  10. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  13. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  14. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  15. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  16. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  17. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  18. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  19. package/.next/standalone/.next/server/app/_not-found/page.js +3 -1
  20. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  21. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  22. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  23. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  24. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  25. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  26. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
  27. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  28. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  29. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  30. package/.next/standalone/.next/server/app/api/audit/invite/route.js +3 -2
  31. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/audit/run/route.js +4 -3
  33. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/audit/status/route.js +1 -1
  35. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/auth/login-request/route.js +2 -1
  37. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +3 -2
  39. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/auth/logout/route.js +3 -2
  41. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/auth/reminder/route.js +3 -2
  43. package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/api/auth/status/route.js +3 -2
  45. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js +1 -1
  47. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  49. package/.next/standalone/.next/server/app/audit/page.js +5 -3
  50. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  51. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  52. package/.next/standalone/.next/server/app/index.html +1 -1
  53. package/.next/standalone/.next/server/app/index.rsc +15 -15
  54. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  55. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  56. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  57. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  58. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  59. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  60. package/.next/standalone/.next/server/app/page.js +3 -1
  61. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  62. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  63. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +10 -10
  64. package/.next/standalone/.next/server/app/policies/page.js +4 -2
  65. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  67. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  68. package/.next/standalone/.next/server/app/project/[name]/page.js +4 -2
  69. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  70. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  71. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  72. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  73. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +6 -4
  74. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  75. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  76. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  77. package/.next/standalone/.next/server/app/projects/page.js +4 -2
  78. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  79. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  80. package/.next/standalone/.next/server/app/settings/page/app-paths-manifest.json +3 -0
  81. package/.next/standalone/.next/server/app/settings/page/build-manifest.json +18 -0
  82. package/.next/standalone/.next/server/app/settings/page/next-font-manifest.json +6 -0
  83. package/.next/standalone/.next/server/app/settings/page/react-loadable-manifest.json +1 -0
  84. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +53 -0
  85. package/.next/standalone/.next/server/app/settings/page.js +18 -0
  86. package/.next/standalone/.next/server/app/settings/page.js.map +5 -0
  87. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -0
  88. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +3 -0
  89. package/.next/standalone/.next/server/app-paths-manifest.json +2 -1
  90. package/.next/standalone/.next/server/chunks/{[externals]__1nl3dvw._.js → [externals]__0r-zamh._.js} +2 -2
  91. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06hexd0._.js +3 -0
  92. package/.next/standalone/.next/server/chunks/[root-of-the-server]__094p1f1._.js +3 -0
  93. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0xu5z._.js +1 -1
  94. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ed01_d._.js +3 -0
  95. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g0vcwt._.js +3 -0
  96. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0h3lfmp._.js → [root-of-the-server]__0ky_n0r._.js} +2 -2
  97. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__18d-9oi._.js → [root-of-the-server]__0lk_935._.js} +2 -2
  98. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1p7s8-d._.js → [root-of-the-server]__0pbp9fe._.js} +2 -2
  99. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q-v9z2._.js +1 -1
  100. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__10oa-v9._.js → [root-of-the-server]__0v2fpxe._.js} +2 -2
  101. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0sb_5m8._.js → [root-of-the-server]__131ebum._.js} +3 -3
  102. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0-dh5jy._.js → [root-of-the-server]__1_ps0jw._.js} +2 -2
  103. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0l4vuqm._.js → [root-of-the-server]__1a7i2q4._.js} +1 -1
  104. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ibjy5x._.js +3 -0
  105. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1iny24d._.js +3 -0
  106. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +1 -1
  107. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tyedjt._.js +3 -0
  108. package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +5 -0
  109. package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +5 -0
  110. package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +5 -0
  111. package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +5 -5
  112. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  113. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__06bp_fg._.js +5 -0
  114. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__074qmkp._.js +3 -0
  115. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0c_qcgp._.js +3 -0
  116. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0kbc9bs._.js +3 -0
  117. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0spkm68._.js +10 -10
  118. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1a2hcb_._.js +3 -0
  119. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0rtdfof._.js → [root-of-the-server]__1rdruw4._.js} +1 -1
  120. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1rv-sc9._.js +3 -0
  121. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1tyf0tc._.js +5 -0
  122. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1u-wx42._.js +4 -0
  123. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1v185mo._.js +3 -0
  124. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0rag5dg._.js → [root-of-the-server]__1yw368c._.js} +2 -2
  125. package/.next/standalone/.next/server/chunks/ssr/_0-yi74u._.js +3 -0
  126. package/.next/standalone/.next/server/chunks/ssr/_04mekdy._.js +3 -0
  127. package/.next/standalone/.next/server/chunks/ssr/_0r-dsre._.js +3 -0
  128. package/.next/standalone/.next/server/chunks/ssr/_0rjgfln._.js +3 -0
  129. package/.next/standalone/.next/server/chunks/ssr/_0se484m._.js +3 -0
  130. package/.next/standalone/.next/server/chunks/ssr/_0uz7o9y._.js +3 -0
  131. package/.next/standalone/.next/server/chunks/ssr/_11_p9y8._.js +1 -1
  132. package/.next/standalone/.next/server/chunks/ssr/_194yh9u._.js +3 -0
  133. package/.next/standalone/.next/server/chunks/ssr/_1fahg9e._.js +3 -0
  134. package/.next/standalone/.next/server/chunks/ssr/_1gmzkwj._.js +5 -0
  135. package/.next/standalone/.next/server/chunks/ssr/_1w8sola._.js +3 -0
  136. package/.next/standalone/.next/server/chunks/ssr/_1w_6p9g._.js +3 -0
  137. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +2 -2
  138. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  139. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
  140. package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0dwo94s.js +4 -0
  141. package/.next/standalone/.next/server/chunks/ssr/src_hooks_builtin-policies_ts_09j2ndl._.js +3 -0
  142. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +5 -0
  143. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  144. package/.next/standalone/.next/server/middleware.js +2 -2
  145. package/.next/standalone/.next/server/pages/404.html +1 -1
  146. package/.next/standalone/.next/server/pages/500.html +1 -1
  147. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  148. package/.next/standalone/.next/server/server-reference-manifest.json +54 -12
  149. package/.next/standalone/.next/static/chunks/0945ybz3fsvba.js +1 -0
  150. package/.next/standalone/.next/static/chunks/{0cr85wuc9gc9k.js → 0gr76ps8_zm26.js} +1 -1
  151. package/.next/standalone/.next/static/chunks/{2cqk4a9s893oy.js → 0m-m7wgtxdoav.js} +1 -1
  152. package/.next/standalone/.next/static/chunks/{1tp9dex23zs8y.js → 11l6oh9cyb35a.js} +1 -1
  153. package/.next/standalone/.next/static/chunks/166f8qnh03jqc.js +6 -0
  154. package/.next/standalone/.next/static/chunks/1bm0q12hgs22l.css +2 -0
  155. package/.next/standalone/.next/static/chunks/1v1gqvpjk62jm.js +1 -0
  156. package/.next/standalone/.next/static/chunks/{2s9g1dsde_ts1.js → 2aej0vi1fm87m.js} +1 -1
  157. package/.next/standalone/.next/static/chunks/{1bkynffp3q1f7.js → 2bxld6-rym3ah.js} +22 -22
  158. package/.next/standalone/.next/static/chunks/{2v1rpzrh2i1wv.js → 2p2kz4aj1tooj.js} +1 -1
  159. package/.next/standalone/.next/static/chunks/2uvhw57mf9lg_.css +2 -0
  160. package/.next/standalone/.next/static/chunks/36pc3_8fpb6ic.js +1 -0
  161. package/.next/standalone/.next/static/chunks/{2k285z07qce47.js → 3t1wr_kx6brwc.js} +1 -1
  162. package/.next/standalone/app/actions/get-hooks-config.ts +2 -1
  163. package/.next/standalone/app/actions/get-scheduled-audit.ts +70 -0
  164. package/.next/standalone/app/actions/update-scheduled-audit.ts +44 -0
  165. package/.next/standalone/app/api/audit/run/route.ts +28 -0
  166. package/.next/standalone/app/api/audit/status/route.ts +13 -1
  167. package/.next/standalone/app/api/auth/status/route.ts +1 -1
  168. package/.next/standalone/app/audit/_components/auth-dialog.css +159 -0
  169. package/.next/standalone/app/audit/_components/auth-dialog.tsx +3 -0
  170. package/.next/standalone/app/audit/audit-styles.css +6 -149
  171. package/.next/standalone/app/settings/page.tsx +31 -0
  172. package/.next/standalone/app/settings/settings-client.tsx +488 -0
  173. package/.next/standalone/components/navbar.tsx +2 -0
  174. package/.next/standalone/lib/auth/api-server-client.ts +2 -3
  175. package/.next/standalone/lib/auth/auth-store.ts +4 -4
  176. package/.next/standalone/lib/codex-sessions.ts +2 -1
  177. package/.next/standalone/lib/dashboard-host.ts +101 -0
  178. package/.next/standalone/lib/install-check.ts +22 -7
  179. package/.next/standalone/lib/pi-sessions.ts +99 -14
  180. package/.next/standalone/lib/telemetry-enabled.ts +54 -0
  181. package/.next/standalone/lib/telemetry-id.ts +52 -1
  182. package/.next/standalone/lib/telemetry.ts +8 -4
  183. package/.next/standalone/node_modules/react/cjs/react.development.js +1 -1
  184. package/.next/standalone/node_modules/react/cjs/react.production.js +1 -1
  185. package/.next/standalone/node_modules/react/package.json +3 -3
  186. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.browser.production.js +1 -1
  187. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server-legacy.node.production.js +1 -1
  188. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.browser.production.js +3 -3
  189. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.edge.production.js +3 -3
  190. package/.next/standalone/node_modules/react-dom/cjs/react-dom-server.node.production.js +3 -3
  191. package/.next/standalone/node_modules/react-dom/cjs/react-dom.production.js +1 -1
  192. package/.next/standalone/node_modules/react-dom/package.json +4 -4
  193. package/.next/standalone/package.json +7 -6
  194. package/.next/standalone/proxy.ts +89 -1
  195. package/.next/standalone/server.js +1 -1
  196. package/bin/failproofai.mjs +202 -86
  197. package/dist/cli.mjs +17803 -15346
  198. package/dist/worker.mjs +1620 -257
  199. package/lib/auth/api-server-client.ts +2 -3
  200. package/lib/auth/auth-store.ts +4 -4
  201. package/lib/codex-sessions.ts +2 -1
  202. package/lib/dashboard-host.ts +101 -0
  203. package/lib/install-check.ts +22 -7
  204. package/lib/pi-sessions.ts +99 -14
  205. package/lib/telemetry-enabled.ts +54 -0
  206. package/lib/telemetry-id.ts +52 -1
  207. package/lib/telemetry.ts +8 -4
  208. package/package.json +7 -6
  209. package/scripts/install-telemetry.mjs +8 -1
  210. package/scripts/launch.ts +25 -3
  211. package/scripts/parse-script-args.ts +17 -1
  212. package/scripts/prune-standalone.mjs +20 -0
  213. package/scripts/translate-docs/readme-translator.ts +163 -4
  214. package/scripts/validate-mdx.ts +153 -13
  215. package/src/audit/audit-lock.ts +285 -0
  216. package/src/audit/audit-schedule.ts +111 -0
  217. package/src/audit/cache.ts +18 -4
  218. package/src/audit/cli-adapters/goose.ts +2 -1
  219. package/src/audit/cli-adapters/hermes.ts +28 -10
  220. package/src/audit/cli.ts +191 -35
  221. package/src/audit/dashboard-cache.ts +2 -1
  222. package/src/hooks/builtin-policies.ts +100 -3
  223. package/src/hooks/cloud-connection.ts +160 -14
  224. package/src/hooks/cloud-enrollment-cli.ts +119 -21
  225. package/src/hooks/cloud-enrollment.ts +87 -17
  226. package/src/hooks/cloud-introspect.ts +147 -0
  227. package/src/hooks/cloud-managed-policies.ts +33 -2
  228. package/src/hooks/collector-config.ts +25 -43
  229. package/src/hooks/configure-wizard.ts +750 -318
  230. package/src/hooks/custom-hooks-loader.ts +125 -11
  231. package/src/hooks/daemon-client.ts +83 -17
  232. package/src/hooks/daemon-download.ts +54 -2
  233. package/src/hooks/daemon-service.ts +605 -29
  234. package/src/hooks/first-run-gate.ts +39 -0
  235. package/src/hooks/fp-config.ts +497 -0
  236. package/src/hooks/fp-home.ts +355 -0
  237. package/src/hooks/fp-reset.ts +332 -0
  238. package/src/hooks/handler.ts +42 -1
  239. package/src/hooks/hook-activity-store.ts +35 -20
  240. package/src/hooks/hook-logger.ts +2 -1
  241. package/src/hooks/hook-telemetry.ts +5 -1
  242. package/src/hooks/hooks-config.ts +4 -3
  243. package/src/hooks/loader-utils.ts +166 -5
  244. package/src/hooks/manager.ts +5 -4
  245. package/src/hooks/onboarding-attempt.ts +179 -0
  246. package/src/hooks/onboarding-lock.ts +119 -0
  247. package/src/hooks/policy-types.ts +0 -7
  248. package/src/hooks/session-pause-cli.ts +15 -13
  249. package/src/hooks/session-pause.ts +36 -4
  250. package/src/hooks/setup-state.ts +256 -0
  251. package/src/hooks/tui.ts +4 -4
  252. package/src/hooks/worker-server.ts +53 -1
  253. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0f8nu8d._.js +0 -3
  254. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0szqzl8._.js +0 -3
  255. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +0 -3
  256. package/.next/standalone/.next/server/chunks/[root-of-the-server]__19120tr._.js +0 -3
  257. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +0 -3
  258. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +0 -3
  259. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rhmvod._.js +0 -3
  260. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02r5bgf._.js +0 -4
  261. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__094nop3._.js +0 -3
  262. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e8sjqm._.js +0 -4
  263. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lkzqax._.js +0 -4
  264. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ls0gsr._.js +0 -4
  265. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n_uvv0._.js +0 -3
  266. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qucxyj._.js +0 -4
  267. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0u02miy._.js +0 -4
  268. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__14dd6h8._.js +0 -3
  269. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1gak4pd._.js +0 -3
  270. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1j4u3yd._.js +0 -4
  271. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zqz4v8._.js +0 -4
  272. package/.next/standalone/.next/server/chunks/ssr/_1m_p7pi._.js +0 -3
  273. package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_12c_2cz.js +0 -3
  274. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1ezd2jf._.js +0 -5
  275. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.js +0 -5
  276. package/.next/standalone/.next/static/chunks/0h7auy7hzjyhw.js +0 -1
  277. package/.next/standalone/.next/static/chunks/1bw4dfzflelcv.js +0 -1
  278. package/.next/standalone/.next/static/chunks/34sjdw8lv46e3.js +0 -6
  279. package/.next/standalone/.next/static/chunks/3i27c3hcriawq.css +0 -1
  280. package/.next/standalone/.next/static/chunks/3zdwnnthucs_1.css +0 -2
  281. package/.next/standalone/Cargo.lock +0 -2222
  282. package/.next/standalone/Cargo.toml +0 -9
  283. package/.next/standalone/crates/CLOUD_POLICIES.md +0 -136
  284. package/.next/standalone/crates/PROTOCOL.md +0 -122
  285. package/.next/standalone/crates/failproofaid/Cargo.toml +0 -25
  286. package/.next/standalone/crates/failproofaid/src/cloud_client.rs +0 -484
  287. package/.next/standalone/crates/failproofaid/src/cloud_policies.rs +0 -921
  288. package/.next/standalone/crates/failproofaid/src/lock.rs +0 -81
  289. package/.next/standalone/crates/failproofaid/src/main.rs +0 -639
  290. package/.next/standalone/crates/failproofaid/src/paths.rs +0 -201
  291. package/.next/standalone/crates/failproofaid/src/server.rs +0 -502
  292. package/.next/standalone/crates/failproofaid/src/worker.rs +0 -400
  293. package/.next/standalone/crates/failproofaid/tests/daemon_e2e.rs +0 -241
  294. package/.next/standalone/crates/fpai-collect/Cargo.toml +0 -43
  295. package/.next/standalone/crates/fpai-collect/src/config.rs +0 -420
  296. package/.next/standalone/crates/fpai-collect/src/cursor.rs +0 -520
  297. package/.next/standalone/crates/fpai-collect/src/delivery.rs +0 -472
  298. package/.next/standalone/crates/fpai-collect/src/filetail.rs +0 -487
  299. package/.next/standalone/crates/fpai-collect/src/health.rs +0 -369
  300. package/.next/standalone/crates/fpai-collect/src/lib.rs +0 -34
  301. package/.next/standalone/crates/fpai-collect/src/redact.rs +0 -516
  302. package/.next/standalone/crates/fpai-collect/src/sources/claude/mod.rs +0 -279
  303. package/.next/standalone/crates/fpai-collect/src/sources/claude/transform.rs +0 -619
  304. package/.next/standalone/crates/fpai-collect/src/sources/codex/mod.rs +0 -156
  305. package/.next/standalone/crates/fpai-collect/src/sources/codex/transform.rs +0 -715
  306. package/.next/standalone/crates/fpai-collect/src/sources/copilot/mod.rs +0 -158
  307. package/.next/standalone/crates/fpai-collect/src/sources/copilot/transform.rs +0 -512
  308. package/.next/standalone/crates/fpai-collect/src/sources/goose/mod.rs +0 -511
  309. package/.next/standalone/crates/fpai-collect/src/sources/goose/transform.rs +0 -586
  310. package/.next/standalone/crates/fpai-collect/src/sources/hermes/mod.rs +0 -318
  311. package/.next/standalone/crates/fpai-collect/src/sources/hermes/transform.rs +0 -579
  312. package/.next/standalone/crates/fpai-collect/src/sources/hooks/mod.rs +0 -314
  313. package/.next/standalone/crates/fpai-collect/src/sources/hooks/transform.rs +0 -448
  314. package/.next/standalone/crates/fpai-collect/src/sources/mod.rs +0 -23
  315. package/.next/standalone/crates/fpai-collect/src/sources/openclaw/mod.rs +0 -176
  316. package/.next/standalone/crates/fpai-collect/src/sources/openclaw/transform.rs +0 -500
  317. package/.next/standalone/crates/fpai-collect/src/sources/opencode/mod.rs +0 -315
  318. package/.next/standalone/crates/fpai-collect/src/sources/opencode/transform.rs +0 -493
  319. package/.next/standalone/crates/fpai-collect/src/sources/pi/mod.rs +0 -201
  320. package/.next/standalone/crates/fpai-collect/src/sources/pi/transform.rs +0 -533
  321. package/.next/standalone/crates/fpai-collect/src/spool.rs +0 -535
  322. package/.next/standalone/crates/fpai-collect/src/sqlitepoll.rs +0 -390
  323. package/.next/standalone/crates/fpai-collect/src/supervisor.rs +0 -423
  324. package/.next/standalone/crates/fpai-collect/src/uploader.rs +0 -600
  325. package/.next/standalone/crates/fpai-collect/tests/claude_source.rs +0 -739
  326. package/.next/standalone/crates/fpai-collect/tests/claude_subagents.rs +0 -394
  327. package/.next/standalone/crates/fpai-collect/tests/codex_source.rs +0 -976
  328. package/.next/standalone/crates/fpai-collect/tests/config.rs +0 -286
  329. package/.next/standalone/crates/fpai-collect/tests/copilot_source.rs +0 -1020
  330. package/.next/standalone/crates/fpai-collect/tests/delivery.rs +0 -281
  331. package/.next/standalone/crates/fpai-collect/tests/goose_source.rs +0 -1009
  332. package/.next/standalone/crates/fpai-collect/tests/hermes_source.rs +0 -1130
  333. package/.next/standalone/crates/fpai-collect/tests/hooks_source.rs +0 -622
  334. package/.next/standalone/crates/fpai-collect/tests/openclaw_source.rs +0 -855
  335. package/.next/standalone/crates/fpai-collect/tests/opencode_source.rs +0 -1478
  336. package/.next/standalone/crates/fpai-collect/tests/pi_source.rs +0 -1192
  337. package/.next/standalone/crates/fpai-collect/tests/supervisor.rs +0 -290
  338. package/.next/standalone/crates/fpai-collect/tests/uploader.rs +0 -506
  339. package/.next/standalone/crates/fpai-ipc/Cargo.toml +0 -16
  340. package/.next/standalone/crates/fpai-ipc/src/envelope.rs +0 -175
  341. package/.next/standalone/crates/fpai-ipc/src/framing.rs +0 -177
  342. package/.next/standalone/crates/fpai-ipc/src/lib.rs +0 -10
  343. package/.next/standalone/crates/fpai-ipc/src/peer.rs +0 -80
  344. package/.next/standalone/docker-hook-sync/Dockerfile +0 -79
  345. package/.next/standalone/docker-hook-sync/entrypoint.sh +0 -109
  346. package/.next/standalone/integration-suite/Dockerfile +0 -47
  347. package/.next/standalone/integration-suite/README.md +0 -129
  348. package/.next/standalone/integration-suite/canary-policies.mjs +0 -45
  349. package/.next/standalone/integration-suite/capture-tokens.sh +0 -48
  350. package/.next/standalone/integration-suite/ci-entrypoint.sh +0 -179
  351. package/.next/standalone/integration-suite/inject-tokens.sh +0 -44
  352. package/.next/standalone/integration-suite/install-clis.sh +0 -136
  353. package/.next/standalone/integration-suite/probe-cli.sh +0 -248
  354. package/.next/standalone/integration-suite/report.js +0 -120
  355. package/.next/standalone/integration-suite/run.sh +0 -159
  356. package/.next/standalone/openclaw-plugin/index.js +0 -223
  357. package/.next/standalone/openclaw-plugin/openclaw.plugin.json +0 -6
  358. package/.next/standalone/openclaw-plugin/package.json +0 -12
  359. package/.next/standalone/osv-scanner.toml +0 -17
  360. package/.next/standalone/pi-extension/index.ts +0 -524
  361. package/.next/standalone/pi-extension/package.json +0 -12
  362. package/.next/standalone/postcss.config.mjs +0 -8
  363. package/.next/standalone/readme-arch-hq.gif +0 -0
  364. package/.next/standalone/rust-toolchain.toml +0 -4
  365. package/src/auth/cli.ts +0 -371
  366. /package/.next/standalone/.next/static/{LOuwMmfomO6hQYfMQYs4K → LUaLkKg40nSkHrIajexIk}/_buildManifest.js +0 -0
  367. /package/.next/standalone/.next/static/{LOuwMmfomO6hQYfMQYs4K → LUaLkKg40nSkHrIajexIk}/_clientMiddlewareManifest.js +0 -0
  368. /package/.next/standalone/.next/static/{LOuwMmfomO6hQYfMQYs4K → LUaLkKg40nSkHrIajexIk}/_ssgManifest.js +0 -0
@@ -1,484 +0,0 @@
1
- use crate::cloud_policies::{DesiredPolicy, DesiredState, PolicyStore};
2
- use reqwest::Url;
3
- use reqwest::blocking::Client;
4
- use std::sync::Arc;
5
- use std::sync::atomic::{AtomicBool, Ordering};
6
- use std::thread::JoinHandle;
7
- use std::time::{Duration, Instant};
8
-
9
- const DEFAULT_POLL_MS: u64 = 30_000;
10
- const MINIMUM_POLL_MS: u64 = 100;
11
-
12
- #[derive(Clone)]
13
- pub struct CloudClient {
14
- base_url: Url,
15
- token: String,
16
- machine_id: String,
17
- client: Client,
18
- }
19
-
20
- /// On-disk enrolment, written by `failproofai config --connect`.
21
- ///
22
- /// The credential deliberately does NOT live in the service unit: that file is
23
- /// installed world-readable (0644, `/etc/systemd/system`), so a token there
24
- /// would be readable by every local user and echoed by `systemctl show`.
25
- #[derive(serde::Deserialize)]
26
- struct StoredCredentials {
27
- #[serde(rename = "schemaVersion")]
28
- schema_version: u32,
29
- url: String,
30
- #[serde(rename = "machineId")]
31
- machine_id: String,
32
- token: String,
33
- }
34
-
35
- /// `~/.failproofai/cloud.json`, or `FAILPROOFAI_CLOUD_CREDENTIALS`.
36
- /// The same two rules the TS side applies, so the pair cannot drift.
37
- pub fn credentials_path() -> Option<std::path::PathBuf> {
38
- if let Some(path) = std::env::var_os("FAILPROOFAI_CLOUD_CREDENTIALS") {
39
- return Some(std::path::PathBuf::from(path));
40
- }
41
- let home = std::env::var_os("HOME")?;
42
- Some(
43
- std::path::PathBuf::from(home)
44
- .join(".failproofai")
45
- .join("cloud.json"),
46
- )
47
- }
48
-
49
- impl CloudClient {
50
- /// Environment first, then the credential file.
51
- ///
52
- /// Env wins so CI, containers and tests keep working unchanged, and so an
53
- /// operator who prefers env-only configuration loses nothing.
54
- pub fn from_env_or_file() -> Result<Option<Self>, String> {
55
- if let Some(client) = Self::from_env()? {
56
- return Ok(Some(client));
57
- }
58
- Self::from_file()
59
- }
60
-
61
- pub fn from_env() -> Result<Option<Self>, String> {
62
- let Some(base_url) = env_value("FAILPROOFAI_CLOUD_URL") else {
63
- return Ok(None);
64
- };
65
- let token = env_value("FAILPROOFAI_CLOUD_TOKEN")
66
- .ok_or("FAILPROOFAI_CLOUD_TOKEN is required when FAILPROOFAI_CLOUD_URL is set")?;
67
- let machine_id = env_value("FAILPROOFAI_MACHINE_ID")
68
- .ok_or("FAILPROOFAI_MACHINE_ID is required when FAILPROOFAI_CLOUD_URL is set")?;
69
- Self::new(&base_url, token, machine_id).map(Some)
70
- }
71
-
72
- /// A missing file means "not enrolled" — not an error. A malformed one IS
73
- /// an error: it was written by us, so bad content means something is wrong
74
- /// that the operator should see rather than a silently unenrolled machine.
75
- pub fn from_file() -> Result<Option<Self>, String> {
76
- let Some(path) = credentials_path() else {
77
- return Ok(None);
78
- };
79
- let bytes = match std::fs::read(&path) {
80
- Ok(bytes) => bytes,
81
- Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(None),
82
- Err(err) => return Err(format!("failed to read {}: {err}", path.display())),
83
- };
84
- let stored: StoredCredentials = serde_json::from_slice(&bytes)
85
- .map_err(|err| format!("invalid credentials in {}: {err}", path.display()))?;
86
- if stored.schema_version != 1 {
87
- return Err(format!(
88
- "unsupported credentials schema {} in {}",
89
- stored.schema_version,
90
- path.display()
91
- ));
92
- }
93
- if stored.token.is_empty() {
94
- return Err(format!("empty token in {}", path.display()));
95
- }
96
- Self::new(&stored.url, stored.token, stored.machine_id).map(Some)
97
- }
98
-
99
- fn new(base_url: &str, token: String, machine_id: String) -> Result<Self, String> {
100
- let mut base_url =
101
- Url::parse(base_url).map_err(|err| format!("invalid FAILPROOFAI_CLOUD_URL: {err}"))?;
102
- if !matches!(base_url.scheme(), "http" | "https") {
103
- return Err("FAILPROOFAI_CLOUD_URL must use http or https".to_string());
104
- }
105
- if !base_url.path().ends_with('/') {
106
- base_url.set_path(&format!("{}/", base_url.path()));
107
- }
108
- if machine_id.is_empty() {
109
- return Err("FAILPROOFAI_MACHINE_ID cannot be empty".to_string());
110
- }
111
- let client = Client::builder()
112
- .connect_timeout(Duration::from_secs(5))
113
- .timeout(Duration::from_secs(15))
114
- .build()
115
- .map_err(|err| format!("failed to build cloud HTTP client: {err}"))?;
116
- Ok(Self {
117
- base_url,
118
- token,
119
- machine_id,
120
- client,
121
- })
122
- }
123
-
124
- pub fn desired_state(&self) -> Result<DesiredState, String> {
125
- let mut url = self
126
- .base_url
127
- .join("enforcement/v1/desired-state")
128
- .map_err(|err| format!("failed to build desired-state URL: {err}"))?;
129
- url.query_pairs_mut()
130
- .append_pair("machineId", &self.machine_id);
131
- self.client
132
- .get(url)
133
- .bearer_auth(&self.token)
134
- .send()
135
- .and_then(|response| response.error_for_status())
136
- .map_err(|err| format!("desired-state request failed: {err}"))?
137
- .json()
138
- .map_err(|err| format!("invalid desired-state response: {err}"))
139
- }
140
-
141
- fn artifact(&self, policy: &DesiredPolicy) -> Result<Vec<u8>, String> {
142
- let url = self
143
- .base_url
144
- .join(&policy.artifact_url)
145
- .map_err(|err| format!("invalid artifact URL: {err}"))?;
146
- if url.origin() != self.base_url.origin() {
147
- return Err("artifact URL points outside the configured cloud origin".to_string());
148
- }
149
- self.client
150
- .get(url)
151
- .bearer_auth(&self.token)
152
- .send()
153
- .and_then(|response| response.error_for_status())
154
- .map_err(|err| format!("artifact request failed: {err}"))?
155
- .bytes()
156
- .map(|bytes| bytes.to_vec())
157
- .map_err(|err| format!("failed to read artifact response: {err}"))
158
- }
159
- }
160
-
161
- /// One maintenance lane that re-resolves enrolment on every tick.
162
- ///
163
- /// Enrolment is deliberately NOT read once at startup. `failproofai config
164
- /// --connect` writes a credential file without root, and the service is a
165
- /// SYSTEM unit — so requiring a restart to notice it would put `sudo systemctl
166
- /// restart` back into the flow and undo the reason the credential lives in a
167
- /// file at all. Re-resolving per tick also makes token rotation and
168
- /// `--disconnect` take effect within one interval, with nothing to restart.
169
- ///
170
- /// Resolution failures degrade to integrity-only rather than killing the lane:
171
- /// a machine that was pulling policy keeps its last known-good generation and
172
- /// keeps repairing tampering while its credentials are broken.
173
- ///
174
- /// Two intervals, chosen per tick, so both documented knobs keep their meaning
175
- /// now that one lane serves both cases: `FAILPROOFAI_CLOUD_POLICY_POLL_MS` when
176
- /// enrolled, `FAILPROOFAI_CLOUD_POLICY_RECONCILE_MS` when not.
177
- pub fn spawn_maintenance(
178
- store: PolicyStore,
179
- shutdown: Arc<AtomicBool>,
180
- poll_interval: Duration,
181
- idle_interval: Duration,
182
- ) -> JoinHandle<()> {
183
- std::thread::spawn(move || {
184
- let mut last_state: Option<bool> = None;
185
- while !shutdown.load(Ordering::Relaxed) {
186
- let cloud = match CloudClient::from_env_or_file() {
187
- Ok(client) => client,
188
- Err(err) => {
189
- eprintln!("[failproofaid] cloud enrolment error: {err}");
190
- None
191
- }
192
- };
193
-
194
- // Log only on transition, so a disconnected machine does not print
195
- // a line every 30 seconds forever.
196
- let enrolled = cloud.is_some();
197
- if last_state != Some(enrolled) {
198
- eprintln!(
199
- "[failproofaid] cloud-managed policy polling {}",
200
- if enrolled { "enabled" } else { "disabled" }
201
- );
202
- last_state = Some(enrolled);
203
- }
204
-
205
- if let Some(cloud) = cloud.as_ref() {
206
- poll_once(&store, cloud);
207
- }
208
-
209
- // Runs whether or not cloud is reachable: poll failures never
210
- // discard the last known-good generation, and local tampering is
211
- // still repaired while the cloud is offline or unconfigured.
212
- if let Err(err) = store.repair_active_from_cache() {
213
- eprintln!("[failproofaid] cloud policy integrity error: {err}");
214
- }
215
- wait_until_shutdown(
216
- &shutdown,
217
- if enrolled {
218
- poll_interval
219
- } else {
220
- idle_interval
221
- },
222
- );
223
- }
224
- })
225
- }
226
-
227
- fn poll_once(store: &PolicyStore, cloud: &CloudClient) {
228
- match cloud.desired_state() {
229
- Ok(desired) => {
230
- match store.reconcile(&desired, &|policy: &DesiredPolicy| cloud.artifact(policy)) {
231
- Ok(outcome)
232
- if outcome.activated || outcome.downloaded > 0 || outcome.repaired > 0 =>
233
- {
234
- eprintln!(
235
- "[failproofaid] cloud policy generation {} active (downloaded {}, repaired {})",
236
- outcome.generation, outcome.downloaded, outcome.repaired
237
- );
238
- }
239
- Ok(_) => {}
240
- Err(err) => eprintln!("[failproofaid] cloud policy reconcile error: {err}"),
241
- }
242
- }
243
- Err(err) => eprintln!("[failproofaid] cloud policy poll error: {err}"),
244
- }
245
- }
246
-
247
- pub fn poll_interval_from_env() -> Duration {
248
- let milliseconds = env_value("FAILPROOFAI_CLOUD_POLICY_POLL_MS")
249
- .and_then(|value| value.parse::<u64>().ok())
250
- .unwrap_or(DEFAULT_POLL_MS)
251
- .max(MINIMUM_POLL_MS);
252
- Duration::from_millis(milliseconds)
253
- }
254
-
255
- fn env_value(name: &str) -> Option<String> {
256
- std::env::var(name)
257
- .ok()
258
- .map(|value| value.trim().to_string())
259
- .filter(|value| !value.is_empty())
260
- }
261
-
262
- fn wait_until_shutdown(shutdown: &AtomicBool, interval: Duration) {
263
- let deadline = Instant::now() + interval;
264
- while !shutdown.load(Ordering::Relaxed) && Instant::now() < deadline {
265
- let remaining = deadline.saturating_duration_since(Instant::now());
266
- std::thread::sleep(remaining.min(Duration::from_millis(50)));
267
- }
268
- }
269
-
270
- #[cfg(test)]
271
- mod tests {
272
- use super::*;
273
- // Only the fixtures construct an effect explicitly.
274
- use crate::cloud_policies::PolicyEffect;
275
-
276
- // std::env::set_var is process-global, so these must not interleave with
277
- // each other or with anything else reading the same variables.
278
- static ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
279
-
280
- /// Clears the vars it set and removes its scratch directory. Kept as a
281
- /// guard so a failing assertion cannot leak process-global env into the
282
- /// next test.
283
- struct EnvGuard(std::path::PathBuf);
284
- impl Drop for EnvGuard {
285
- fn drop(&mut self) {
286
- unsafe {
287
- std::env::remove_var("FAILPROOFAI_CLOUD_CREDENTIALS");
288
- std::env::remove_var("FAILPROOFAI_CLOUD_URL");
289
- std::env::remove_var("FAILPROOFAI_CLOUD_TOKEN");
290
- std::env::remove_var("FAILPROOFAI_MACHINE_ID");
291
- }
292
- let _ = fs::remove_dir_all(&self.0);
293
- }
294
- }
295
-
296
- static SCRATCH_SEQ: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0);
297
-
298
- fn with_credentials_file(contents: Option<&str>) -> EnvGuard {
299
- let seq = SCRATCH_SEQ.fetch_add(1, Ordering::Relaxed);
300
- let dir =
301
- std::env::temp_dir().join(format!("failproofaid-creds-{}-{seq}", std::process::id()));
302
- fs::create_dir_all(&dir).unwrap();
303
- let path = dir.join("cloud.json");
304
- if let Some(contents) = contents {
305
- fs::write(&path, contents).unwrap();
306
- }
307
- unsafe {
308
- std::env::set_var("FAILPROOFAI_CLOUD_CREDENTIALS", &path);
309
- std::env::remove_var("FAILPROOFAI_CLOUD_URL");
310
- std::env::remove_var("FAILPROOFAI_CLOUD_TOKEN");
311
- std::env::remove_var("FAILPROOFAI_MACHINE_ID");
312
- }
313
- EnvGuard(dir)
314
- }
315
-
316
- const GOOD: &str =
317
- r#"{"schemaVersion":1,"url":"https://cloud.example","machineId":"m-1","token":"secret"}"#;
318
-
319
- #[test]
320
- fn no_credentials_file_means_not_enrolled_rather_than_an_error() {
321
- let _lock = ENV_LOCK.lock().unwrap();
322
- let _guard = with_credentials_file(None);
323
- assert!(CloudClient::from_file().unwrap().is_none());
324
- }
325
-
326
- #[test]
327
- fn reads_a_valid_credentials_file() {
328
- let _lock = ENV_LOCK.lock().unwrap();
329
- let _guard = with_credentials_file(Some(GOOD));
330
- let client = CloudClient::from_file().unwrap().expect("enrolled");
331
- assert_eq!(client.machine_id, "m-1");
332
- assert_eq!(client.token, "secret");
333
- assert_eq!(client.base_url.host_str(), Some("cloud.example"));
334
- }
335
-
336
- #[test]
337
- fn a_malformed_credentials_file_is_an_error_not_a_silent_disconnect() {
338
- // We wrote this file. Bad content means something is wrong that the
339
- // operator needs to see — reporting "not enrolled" would leave a
340
- // machine quietly unmanaged while looking healthy.
341
- let _lock = ENV_LOCK.lock().unwrap();
342
- let _guard = with_credentials_file(Some("{ not json"));
343
- let err = CloudClient::from_file()
344
- .err()
345
- .expect("malformed file must error");
346
- assert!(err.contains("invalid credentials"), "{err}");
347
- }
348
-
349
- #[test]
350
- fn rejects_an_unknown_schema_version() {
351
- let _lock = ENV_LOCK.lock().unwrap();
352
- let _guard = with_credentials_file(Some(
353
- r#"{"schemaVersion":99,"url":"https://c.example","machineId":"m","token":"t"}"#,
354
- ));
355
- let err = CloudClient::from_file()
356
- .err()
357
- .expect("unknown schema must error");
358
- assert!(err.contains("unsupported credentials schema"), "{err}");
359
- }
360
-
361
- #[test]
362
- fn rejects_an_empty_token_and_an_empty_machine_id() {
363
- let _lock = ENV_LOCK.lock().unwrap();
364
- {
365
- let _guard = with_credentials_file(Some(
366
- r#"{"schemaVersion":1,"url":"https://c.example","machineId":"m","token":""}"#,
367
- ));
368
- let err = CloudClient::from_file()
369
- .err()
370
- .expect("empty token must error");
371
- assert!(err.contains("empty token"), "{err}");
372
- }
373
- let _guard = with_credentials_file(Some(
374
- r#"{"schemaVersion":1,"url":"https://c.example","machineId":"","token":"t"}"#,
375
- ));
376
- assert!(CloudClient::from_file().is_err());
377
- }
378
-
379
- #[test]
380
- fn environment_wins_over_the_credentials_file() {
381
- // CI, containers and the existing tests configure by env; enrolment
382
- // must not silently override them.
383
- let _lock = ENV_LOCK.lock().unwrap();
384
- let _guard = with_credentials_file(Some(GOOD));
385
- unsafe {
386
- std::env::set_var("FAILPROOFAI_CLOUD_URL", "https://env.example");
387
- std::env::set_var("FAILPROOFAI_CLOUD_TOKEN", "env-token");
388
- std::env::set_var("FAILPROOFAI_MACHINE_ID", "env-machine");
389
- }
390
- let client = CloudClient::from_env_or_file().unwrap().expect("enrolled");
391
- assert_eq!(client.machine_id, "env-machine");
392
- assert_eq!(client.token, "env-token");
393
- }
394
-
395
- #[test]
396
- fn falls_back_to_the_file_when_only_some_env_vars_are_set() {
397
- // FAILPROOFAI_CLOUD_URL is the switch: without it, from_env returns
398
- // None and the file is consulted rather than erroring.
399
- let _lock = ENV_LOCK.lock().unwrap();
400
- let _guard = with_credentials_file(Some(GOOD));
401
- unsafe {
402
- std::env::set_var("FAILPROOFAI_CLOUD_TOKEN", "stray");
403
- }
404
- let client = CloudClient::from_env_or_file().unwrap().expect("enrolled");
405
- assert_eq!(client.machine_id, "m-1");
406
- }
407
- use sha2::{Digest, Sha256};
408
- use std::fs;
409
- use std::io::{Read, Write};
410
- use std::net::TcpListener;
411
-
412
- #[test]
413
- fn fetches_desired_state_and_artifact_into_the_store() {
414
- let artifact = b"export default 'managed';\n".to_vec();
415
- let sha = format!("{:x}", Sha256::digest(&artifact));
416
- let listener = TcpListener::bind("127.0.0.1:0").unwrap();
417
- let address = listener.local_addr().unwrap();
418
- let expected_sha = sha.clone();
419
- let expected_artifact = artifact.clone();
420
- let server = std::thread::spawn(move || {
421
- for _ in 0..2 {
422
- let (mut stream, _) = listener.accept().unwrap();
423
- let mut request = [0_u8; 4096];
424
- let read = stream.read(&mut request).unwrap();
425
- let request = String::from_utf8_lossy(&request[..read]);
426
- assert!(
427
- request.contains("Authorization: Bearer test-token")
428
- || request.contains("authorization: Bearer test-token")
429
- );
430
- let body = if request
431
- .starts_with("GET /enforcement/v1/desired-state?machineId=machine-1")
432
- {
433
- format!(r#"{{"schemaVersion":1,"generation":7,"policies":[{{"id":"guard","revision":2,"sha256":"{expected_sha}","artifactUrl":"/enforcement/v1/artifacts/{expected_sha}"}}]}}"#).into_bytes()
434
- } else {
435
- expected_artifact.clone()
436
- };
437
- let content_type = if request.contains("desired-state") {
438
- "application/json"
439
- } else {
440
- "text/javascript"
441
- };
442
- write!(stream, "HTTP/1.1 200 OK\r\nContent-Length: {}\r\nContent-Type: {}\r\nConnection: close\r\n\r\n", body.len(), content_type).unwrap();
443
- stream.write_all(&body).unwrap();
444
- }
445
- });
446
-
447
- let cloud = CloudClient::new(
448
- &format!("http://{address}"),
449
- "test-token".into(),
450
- "machine-1".into(),
451
- )
452
- .unwrap();
453
- let desired = cloud.desired_state().unwrap();
454
- let root =
455
- std::env::temp_dir().join(format!("failproofaid-http-test-{}", std::process::id()));
456
- let _ = fs::remove_dir_all(&root);
457
- let store = PolicyStore::new(root.clone());
458
- let outcome = store
459
- .reconcile(&desired, &|policy: &DesiredPolicy| cloud.artifact(policy))
460
- .unwrap();
461
- assert_eq!(outcome.generation, 7);
462
- assert_eq!(outcome.downloaded, 1);
463
- assert_eq!(
464
- fs::read(root.join("generations/7/guard.mjs")).unwrap(),
465
- artifact
466
- );
467
- server.join().unwrap();
468
- fs::remove_dir_all(root).unwrap();
469
- }
470
-
471
- #[test]
472
- fn rejects_cross_origin_artifacts_before_sending_the_token() {
473
- let cloud =
474
- CloudClient::new("https://cloud.example", "secret".into(), "machine".into()).unwrap();
475
- let policy = DesiredPolicy {
476
- id: "guard".into(),
477
- revision: 1,
478
- sha256: "0".repeat(64),
479
- artifact_url: "https://evil.example/artifact".into(),
480
- effect: PolicyEffect::Enforce,
481
- };
482
- assert!(cloud.artifact(&policy).unwrap_err().contains("outside"));
483
- }
484
- }