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,506 +0,0 @@
1
- //! Uploader behaviour against a real HTTP server.
2
- //!
3
- //! The unit tests in the module cover the pure parts (filename retry state,
4
- //! line splitting). These cover the parts that only show up over the wire, and
5
- //! each one guards a way of losing data silently rather than loudly.
6
-
7
- use std::fs;
8
- use std::path::{Path, PathBuf};
9
- use std::sync::atomic::Ordering;
10
- use std::time::{Duration, SystemTime, UNIX_EPOCH};
11
-
12
- use fpai_collect::Uploader;
13
- use wiremock::matchers::{header, method, path};
14
- use wiremock::{Mock, MockServer, ResponseTemplate};
15
-
16
- fn tmpdir(name: &str) -> PathBuf {
17
- let d = std::env::temp_dir().join(format!(
18
- "fpai-up-{}-{}-{}",
19
- name,
20
- std::process::id(),
21
- SystemTime::now()
22
- .duration_since(UNIX_EPOCH)
23
- .unwrap()
24
- .as_nanos()
25
- ));
26
- fs::create_dir_all(&d).unwrap();
27
- d
28
- }
29
-
30
- fn write_batch(dir: &Path, name: &str, lines: usize) -> PathBuf {
31
- let p = dir.join(name);
32
- let body: String = (0..lines)
33
- .map(|i| format!("{{\"type\":\"tool_use\",\"n\":{i}}}\n"))
34
- .collect();
35
- fs::write(&p, body).unwrap();
36
- p
37
- }
38
-
39
- /// Fast retries so a test does not wait out a real backoff.
40
- fn uploader(server: &MockServer, failed: &Path) -> Uploader {
41
- Uploader::new(
42
- format!("{}/events", server.uri()),
43
- "test-key".into(),
44
- failed.to_path_buf(),
45
- )
46
- .unwrap()
47
- .with_retry_base(Duration::from_millis(1))
48
- }
49
-
50
- fn parked(dir: &Path) -> Vec<String> {
51
- let mut v: Vec<String> = fs::read_dir(dir)
52
- .map(|rd| {
53
- rd.filter_map(|e| e.ok())
54
- .map(|e| e.file_name().to_string_lossy().to_string())
55
- .collect()
56
- })
57
- .unwrap_or_default();
58
- v.sort();
59
- v
60
- }
61
-
62
- #[tokio::test]
63
- async fn a_2xx_with_an_accepting_ack_deletes_the_batch() {
64
- let server = MockServer::start().await;
65
- Mock::given(method("POST"))
66
- .and(path("/events"))
67
- .and(header("content-type", "application/x-ndjson"))
68
- .and(header("authorization", "Bearer test-key"))
69
- .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
70
- "accepted": 3, "skipped": 0
71
- })))
72
- .mount(&server)
73
- .await;
74
-
75
- let spool = tmpdir("ok-spool");
76
- let failed = tmpdir("ok-failed");
77
- let batch = write_batch(&spool, "hooks-s-1-0.jsonl", 3);
78
-
79
- let up = uploader(&server, &failed);
80
- up.upload_file(&batch).await.unwrap();
81
-
82
- assert!(!batch.exists(), "a delivered batch must be deleted");
83
- assert!(parked(&failed).is_empty());
84
- assert_eq!(up.metrics().accepted_total.load(Ordering::Relaxed), 3);
85
- assert!(up.metrics().last_ok_ts.load(Ordering::Relaxed) > 0);
86
-
87
- fs::remove_dir_all(&spool).ok();
88
- fs::remove_dir_all(&failed).ok();
89
- }
90
-
91
- #[tokio::test]
92
- async fn a_200_that_stored_nothing_is_counted_as_fully_skipped() {
93
- // The failure this exists for: a systematically malformed transform gets
94
- // HTTP 200 for every batch while the server stores none of it. Without
95
- // reading the ack body that is indistinguishable from working perfectly.
96
- let server = MockServer::start().await;
97
- Mock::given(method("POST"))
98
- .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
99
- "accepted": 0, "skipped": 5
100
- })))
101
- .mount(&server)
102
- .await;
103
-
104
- let spool = tmpdir("skip-spool");
105
- let failed = tmpdir("skip-failed");
106
- let batch = write_batch(&spool, "claude-s-1-0.jsonl", 5);
107
-
108
- let up = uploader(&server, &failed);
109
- up.upload_file(&batch).await.unwrap();
110
-
111
- let m = up.metrics();
112
- assert_eq!(m.batches_fully_skipped.load(Ordering::Relaxed), 1);
113
- assert_eq!(m.skipped_total.load(Ordering::Relaxed), 5);
114
- assert_eq!(m.accepted_total.load(Ordering::Relaxed), 0);
115
-
116
- fs::remove_dir_all(&spool).ok();
117
- fs::remove_dir_all(&failed).ok();
118
- }
119
-
120
- #[tokio::test]
121
- async fn a_4xx_parks_immediately_with_its_status_and_is_not_retried() {
122
- let server = MockServer::start().await;
123
- Mock::given(method("POST"))
124
- .respond_with(ResponseTemplate::new(401))
125
- .expect(1) // exactly one attempt: retrying a rotated key is pointless
126
- .mount(&server)
127
- .await;
128
-
129
- let spool = tmpdir("4xx-spool");
130
- let failed = tmpdir("4xx-failed");
131
- let batch = write_batch(&spool, "claude-s-1-0.jsonl", 2);
132
-
133
- let up = uploader(&server, &failed);
134
- let err = up.upload_file(&batch).await.unwrap_err();
135
- assert!(format!("{err}").contains("401"));
136
-
137
- let files = parked(&failed);
138
- assert_eq!(files.len(), 1, "got {files:?}");
139
- assert!(
140
- files[0].contains(".c401"),
141
- "the status must be recorded so an automatic retry pass skips it: {files:?}"
142
- );
143
- assert!(!batch.exists(), "the batch moved rather than being copied");
144
-
145
- fs::remove_dir_all(&spool).ok();
146
- fs::remove_dir_all(&failed).ok();
147
- }
148
-
149
- #[tokio::test]
150
- async fn a_429_is_retried_rather_than_treated_as_definitive() {
151
- // 408 and 429 are the two 4xx that mean "try again". Parking them would
152
- // strand a batch the server explicitly asked us to resend.
153
- let server = MockServer::start().await;
154
- Mock::given(method("POST"))
155
- .respond_with(ResponseTemplate::new(429).insert_header("retry-after", "0"))
156
- .expect(5) // max_retries, not 1
157
- .mount(&server)
158
- .await;
159
-
160
- let spool = tmpdir("429-spool");
161
- let failed = tmpdir("429-failed");
162
- let batch = write_batch(&spool, "claude-s-1-0.jsonl", 1);
163
-
164
- let up = uploader(&server, &failed);
165
- let err = up.upload_file(&batch).await.unwrap_err();
166
- assert!(format!("{err}").contains("429"));
167
-
168
- // Parked WITHOUT a client status, so it stays auto-retryable.
169
- let files = parked(&failed);
170
- assert_eq!(files.len(), 1, "got {files:?}");
171
- assert!(
172
- !files[0].contains(".c429"),
173
- "a retryable status must not be recorded as definitive: {files:?}"
174
- );
175
-
176
- fs::remove_dir_all(&spool).ok();
177
- fs::remove_dir_all(&failed).ok();
178
- }
179
-
180
- #[tokio::test]
181
- async fn a_5xx_retries_then_parks_without_a_client_status() {
182
- let server = MockServer::start().await;
183
- Mock::given(method("POST"))
184
- .respond_with(ResponseTemplate::new(503))
185
- .expect(5)
186
- .mount(&server)
187
- .await;
188
-
189
- let spool = tmpdir("5xx-spool");
190
- let failed = tmpdir("5xx-failed");
191
- let batch = write_batch(&spool, "claude-s-1-0.jsonl", 1);
192
-
193
- let up = uploader(&server, &failed);
194
- let err = up.upload_file(&batch).await.unwrap_err();
195
- assert!(format!("{err}").contains("503"));
196
-
197
- let files = parked(&failed);
198
- assert_eq!(files.len(), 1);
199
- assert!(files[0].contains(".a1"), "attempt count encoded: {files:?}");
200
- assert!(
201
- !files[0].ends_with(".poison"),
202
- "one failure is not poison yet"
203
- );
204
-
205
- fs::remove_dir_all(&spool).ok();
206
- fs::remove_dir_all(&failed).ok();
207
- }
208
-
209
- #[tokio::test]
210
- async fn a_transient_failure_that_recovers_still_delivers() {
211
- let server = MockServer::start().await;
212
- // Fail twice, then succeed. Mounted newest-first by wiremock, so the
213
- // limited mock is consulted before the fallback.
214
- Mock::given(method("POST"))
215
- .respond_with(ResponseTemplate::new(500))
216
- .up_to_n_times(2)
217
- .mount(&server)
218
- .await;
219
- Mock::given(method("POST"))
220
- .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
221
- "accepted": 1, "skipped": 0
222
- })))
223
- .mount(&server)
224
- .await;
225
-
226
- let spool = tmpdir("flap-spool");
227
- let failed = tmpdir("flap-failed");
228
- let batch = write_batch(&spool, "claude-s-1-0.jsonl", 1);
229
-
230
- let up = uploader(&server, &failed);
231
- up.upload_file(&batch).await.unwrap();
232
-
233
- assert!(!batch.exists());
234
- assert!(
235
- parked(&failed).is_empty(),
236
- "a recovered batch must not be parked"
237
- );
238
- assert_eq!(up.metrics().accepted_total.load(Ordering::Relaxed), 1);
239
-
240
- fs::remove_dir_all(&spool).ok();
241
- fs::remove_dir_all(&failed).ok();
242
- }
243
-
244
- #[tokio::test]
245
- async fn an_oversized_batch_is_split_and_every_chunk_delivered() {
246
- let server = MockServer::start().await;
247
- Mock::given(method("POST"))
248
- .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
249
- "accepted": 1, "skipped": 0
250
- })))
251
- .mount(&server)
252
- .await;
253
-
254
- let spool = tmpdir("split-spool");
255
- let failed = tmpdir("split-failed");
256
- let batch = write_batch(&spool, "claude-s-1-0.jsonl", 50);
257
-
258
- // Cap well under the file size, forcing several requests.
259
- let up = uploader(&server, &failed).with_max_upload_bytes(64);
260
- up.upload_file(&batch).await.unwrap();
261
-
262
- let requests = server.received_requests().await.unwrap();
263
- assert!(
264
- requests.len() > 1,
265
- "expected a split, got {} request(s)",
266
- requests.len()
267
- );
268
-
269
- // Splitting happens IN MEMORY. Chunks written to disk beside the original
270
- // would be files the watcher had never seen, so it would post them
271
- // concurrently — the same payload delivered twice.
272
- let leftovers: Vec<String> = fs::read_dir(&spool)
273
- .unwrap()
274
- .filter_map(|e| e.ok())
275
- .map(|e| e.file_name().to_string_lossy().to_string())
276
- .collect();
277
- assert!(
278
- leftovers.is_empty(),
279
- "split chunks must never hit the spool: {leftovers:?}"
280
- );
281
-
282
- fs::remove_dir_all(&spool).ok();
283
- fs::remove_dir_all(&failed).ok();
284
- }
285
-
286
- #[tokio::test]
287
- async fn a_vanished_batch_is_not_an_error() {
288
- // Two tasks can race for the same file; whoever loses must not report a
289
- // failure for work the other already did.
290
- let server = MockServer::start().await;
291
- let spool = tmpdir("gone-spool");
292
- let failed = tmpdir("gone-failed");
293
- let up = uploader(&server, &failed);
294
-
295
- up.upload_file(&spool.join("never-existed.jsonl"))
296
- .await
297
- .unwrap();
298
-
299
- fs::remove_dir_all(&spool).ok();
300
- fs::remove_dir_all(&failed).ok();
301
- }
302
-
303
- #[tokio::test]
304
- async fn parking_never_overwrites_an_existing_parked_batch() {
305
- // A parked batch is the last copy of data the server does not have, so a
306
- // name collision must not destroy one.
307
- let server = MockServer::start().await;
308
- Mock::given(method("POST"))
309
- .respond_with(ResponseTemplate::new(401))
310
- .mount(&server)
311
- .await;
312
-
313
- let spool = tmpdir("collide-spool");
314
- let failed = tmpdir("collide-failed");
315
- let up = uploader(&server, &failed);
316
-
317
- for i in 0..2 {
318
- let batch = write_batch(&spool, "claude-s-1-0.jsonl", i + 1);
319
- let _ = up.upload_file(&batch).await;
320
- }
321
-
322
- let files = parked(&failed);
323
- assert_eq!(
324
- files.len(),
325
- 2,
326
- "both parked batches must survive: {files:?}"
327
- );
328
-
329
- fs::remove_dir_all(&spool).ok();
330
- fs::remove_dir_all(&failed).ok();
331
- }
332
-
333
- #[tokio::test]
334
- async fn a_batch_written_by_the_spool_writer_is_delivered_end_to_end() {
335
- // The two halves have to agree on the filename shape and the NDJSON body.
336
- // Testing them separately would let a change to either drift silently.
337
- let server = MockServer::start().await;
338
- Mock::given(method("POST"))
339
- .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
340
- "accepted": 2, "skipped": 0
341
- })))
342
- .mount(&server)
343
- .await;
344
-
345
- let spool = tmpdir("e2e-spool");
346
- let failed = tmpdir("e2e-failed");
347
-
348
- let mut w = fpai_collect::SpoolWriter::new(
349
- spool.clone(),
350
- fpai_collect::spool::DEFAULT_MAX_BATCH_BYTES,
351
- "hooks",
352
- "18efefd6",
353
- );
354
- w.push(serde_json::json!({
355
- "timestamp": "2026-08-03T10:00:00.000000Z", "session_id": "s1",
356
- "agent_id": "claude", "type": "hook_triggered", "hook_name": "PreToolUse"
357
- }))
358
- .await
359
- .unwrap();
360
- w.push(serde_json::json!({
361
- "timestamp": "2026-08-03T10:00:00.000100Z", "session_id": "s1",
362
- "agent_id": "claude", "type": "hook_completed", "hook_name": "PreToolUse"
363
- }))
364
- .await
365
- .unwrap();
366
- w.flush().await.unwrap();
367
-
368
- let batch = fs::read_dir(&spool)
369
- .unwrap()
370
- .filter_map(|e| e.ok())
371
- .map(|e| e.path())
372
- .find(|p| fpai_collect::spool::is_batch_file(p))
373
- .expect("the spool writer should have produced a batch");
374
-
375
- let up = uploader(&server, &failed);
376
- up.upload_file(&batch).await.unwrap();
377
-
378
- assert!(!batch.exists());
379
- assert!(parked(&failed).is_empty());
380
- assert_eq!(up.metrics().accepted_total.load(Ordering::Relaxed), 2);
381
-
382
- // The body the server received is exactly the NDJSON the spool wrote.
383
- let reqs = server.received_requests().await.unwrap();
384
- assert_eq!(reqs.len(), 1);
385
- let body = String::from_utf8(reqs[0].body.clone()).unwrap();
386
- assert_eq!(body.lines().count(), 2);
387
- assert!(body.contains("hook_triggered") && body.contains("hook_completed"));
388
-
389
- fs::remove_dir_all(&spool).ok();
390
- fs::remove_dir_all(&failed).ok();
391
- }
392
-
393
- // ---------------------------------------------------------------------------
394
- // Redirect / non-ack hardening
395
- //
396
- // An adversarial red-team pointed a live daemon at a dashboard-lookalike
397
- // (POST /events -> 307 -> /login -> 200 text/html). reqwest followed the
398
- // redirect, re-POSTed the batch to the login page, parsed the HTML as a
399
- // default ack, and DELETED the spool file. Cross-host, the same follow shipped
400
- // full event payloads to an attacker. These pin the fix.
401
- // ---------------------------------------------------------------------------
402
-
403
- #[tokio::test]
404
- async fn a_redirect_is_parked_not_followed() {
405
- // The exfiltration vector: a 3xx must never be followed, or the batch (and
406
- // its prompts/command text) is re-POSTed to wherever Location points.
407
- let server = MockServer::start().await;
408
- Mock::given(method("POST"))
409
- .and(path("/events"))
410
- .respond_with(ResponseTemplate::new(307).insert_header("location", "/login?next=%2Fevents"))
411
- .expect(1) // exactly one POST — the redirect is NOT followed to /login
412
- .mount(&server)
413
- .await;
414
- // If the client followed the redirect it would hit this; it must not.
415
- Mock::given(method("POST"))
416
- .and(path("/login"))
417
- .respond_with(ResponseTemplate::new(200).set_body_string("<html>login</html>"))
418
- .expect(0)
419
- .mount(&server)
420
- .await;
421
-
422
- let spool = tmpdir("redir-spool");
423
- let failed = tmpdir("redir-failed");
424
- let batch = write_batch(&spool, "hooks-s-1-0.jsonl", 3);
425
-
426
- let up = uploader(&server, &failed);
427
- let err = up.upload_file(&batch).await.unwrap_err();
428
- assert!(format!("{err}").contains("307"), "got {err}");
429
-
430
- let files = parked(&failed);
431
- assert_eq!(
432
- files.len(),
433
- 1,
434
- "the batch must be parked, not lost: {files:?}"
435
- );
436
- assert!(
437
- files[0].contains(".c307"),
438
- "the redirect status must be recorded: {files:?}"
439
- );
440
- assert!(
441
- !batch.exists(),
442
- "the batch moved to failed/, it was not deleted as delivered"
443
- );
444
-
445
- fs::remove_dir_all(&spool).ok();
446
- fs::remove_dir_all(&failed).ok();
447
- }
448
-
449
- #[tokio::test]
450
- async fn a_200_that_is_not_an_ingest_ack_is_parked_not_deleted() {
451
- // A login page, a proxy or a static host can answer 200. Without a numeric
452
- // `accepted` it is not delivery, and treating it as such deletes the only
453
- // copy of the data.
454
- let server = MockServer::start().await;
455
- Mock::given(method("POST"))
456
- .respond_with(ResponseTemplate::new(200).set_body_string("<html>hello</html>"))
457
- .expect(1)
458
- .mount(&server)
459
- .await;
460
-
461
- let spool = tmpdir("noack-spool");
462
- let failed = tmpdir("noack-failed");
463
- let batch = write_batch(&spool, "hooks-s-1-0.jsonl", 3);
464
-
465
- let up = uploader(&server, &failed);
466
- let err = up.upload_file(&batch).await.unwrap_err();
467
- assert!(format!("{err}").contains("200"), "got {err}");
468
-
469
- let files = parked(&failed);
470
- assert_eq!(files.len(), 1, "a non-ack 200 must be parked: {files:?}");
471
- assert!(
472
- !batch.exists(),
473
- "the batch moved to failed/ rather than vanishing"
474
- );
475
-
476
- fs::remove_dir_all(&spool).ok();
477
- fs::remove_dir_all(&failed).ok();
478
- }
479
-
480
- #[tokio::test]
481
- async fn a_200_whose_json_lacks_accepted_is_parked() {
482
- // `{"status":"ok"}` parses as JSON but is not an ingest ack. The required
483
- // `accepted` field is what rejects it.
484
- let server = MockServer::start().await;
485
- Mock::given(method("POST"))
486
- .respond_with(
487
- ResponseTemplate::new(200)
488
- .insert_header("content-type", "application/json")
489
- .set_body_string(r#"{"status":"ok"}"#),
490
- )
491
- .expect(1)
492
- .mount(&server)
493
- .await;
494
-
495
- let spool = tmpdir("badack-spool");
496
- let failed = tmpdir("badack-failed");
497
- let batch = write_batch(&spool, "hooks-s-1-0.jsonl", 2);
498
-
499
- let up = uploader(&server, &failed);
500
- assert!(up.upload_file(&batch).await.is_err());
501
- assert_eq!(parked(&failed).len(), 1, "must be parked");
502
- assert!(!batch.exists());
503
-
504
- fs::remove_dir_all(&spool).ok();
505
- fs::remove_dir_all(&failed).ok();
506
- }
@@ -1,16 +0,0 @@
1
- [package]
2
- name = "fpai-ipc"
3
- version.workspace = true
4
- edition.workspace = true
5
- license-file.workspace = true
6
- repository.workspace = true
7
- description = "Wire protocol (framing + envelope + peer verification) shared by failproofaid and its tests"
8
- publish = false
9
-
10
- [dependencies]
11
- serde = { version = "1", features = ["derive"] }
12
- serde_json = "1"
13
- libc = "0.2"
14
-
15
- [dev-dependencies]
16
- proptest = "1"
@@ -1,175 +0,0 @@
1
- //! The JSON envelope carried inside each [`crate::framing`] frame.
2
- //!
3
- //! This mirrors the `(argv --hook <Event> --cli <cli>, stdin JSON payload)
4
- //! -> (stdout, stderr, exitCode)` contract every hook invocation already
5
- //! has today — see `src/hooks/handler.ts`'s `handleHookEvent`. The daemon
6
- //! adds nothing to that contract; it only relays it over a socket instead
7
- //! of a fresh process's argv/stdin/stdout.
8
-
9
- use serde::{Deserialize, Serialize};
10
-
11
- /// Bumped whenever a wire-incompatible change is made to either message
12
- /// enum below. The client treats any mismatch identically to "daemon
13
- /// unreachable" (see `docs/configuration.mdx` and the failproofaid plan) —
14
- /// there is no negotiation, only agree-or-fall-back.
15
- pub const PROTOCOL_VERSION: u32 = 1;
16
-
17
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
18
- #[serde(tag = "type", rename_all = "camelCase")]
19
- pub enum ClientMessage {
20
- /// Cheap liveness/handshake check — used by tests and by a client that
21
- /// only wants to confirm a compatible daemon is listening.
22
- #[serde(rename_all = "camelCase")]
23
- Ping { protocol_version: u32 },
24
- /// One hook evaluation request, corresponding 1:1 to one
25
- /// `failproofai --hook <Event> --cli <cli>` invocation.
26
- #[serde(rename_all = "camelCase")]
27
- Hook {
28
- protocol_version: u32,
29
- hook_event: String,
30
- cli: String,
31
- /// The raw stdin payload the calling agent CLI wrote to the
32
- /// one-shot `failproofai` process — forwarded verbatim.
33
- stdin: String,
34
- /// Best-effort cwd of the *originating* CLI process, not the
35
- /// daemon's own cwd (the daemon is a single long-lived process; its
36
- /// own `cwd` does not vary per request and must never be used to
37
- /// resolve project config or custom policies).
38
- cwd: Option<String>,
39
- },
40
- }
41
-
42
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
43
- #[serde(tag = "type", rename_all = "camelCase")]
44
- pub enum ServerMessage {
45
- #[serde(rename_all = "camelCase")]
46
- Pong { protocol_version: u32 },
47
- #[serde(rename_all = "camelCase")]
48
- HookResult {
49
- protocol_version: u32,
50
- exit_code: i32,
51
- stdout: String,
52
- stderr: String,
53
- },
54
- /// The daemon accepted the connection and parsed the request, but could
55
- /// not produce a verdict (e.g. the worker is down/hung). Distinct from
56
- /// `HookResult` so a client can tell "ran and decided" apart from
57
- /// "daemon couldn't evaluate at all" — the latter is what drives the
58
- /// client's fail-closed path.
59
- #[serde(rename_all = "camelCase")]
60
- Error {
61
- protocol_version: u32,
62
- message: String,
63
- },
64
- }
65
-
66
- impl ClientMessage {
67
- pub fn protocol_version(&self) -> u32 {
68
- match self {
69
- ClientMessage::Ping { protocol_version } => *protocol_version,
70
- ClientMessage::Hook {
71
- protocol_version, ..
72
- } => *protocol_version,
73
- }
74
- }
75
- }
76
-
77
- impl ServerMessage {
78
- pub fn protocol_version(&self) -> u32 {
79
- match self {
80
- ServerMessage::Pong { protocol_version } => *protocol_version,
81
- ServerMessage::HookResult {
82
- protocol_version, ..
83
- } => *protocol_version,
84
- ServerMessage::Error {
85
- protocol_version, ..
86
- } => *protocol_version,
87
- }
88
- }
89
- }
90
-
91
- #[cfg(test)]
92
- mod tests {
93
- use super::*;
94
-
95
- #[test]
96
- fn ping_serializes_with_a_type_tag() {
97
- let msg = ClientMessage::Ping {
98
- protocol_version: PROTOCOL_VERSION,
99
- };
100
- let json = serde_json::to_value(&msg).unwrap();
101
- assert_eq!(json["type"], "ping");
102
- assert_eq!(json["protocolVersion"], PROTOCOL_VERSION);
103
- }
104
-
105
- #[test]
106
- fn hook_request_uses_camel_case_field_names_on_the_wire() {
107
- let msg = ClientMessage::Hook {
108
- protocol_version: PROTOCOL_VERSION,
109
- hook_event: "PreToolUse".to_string(),
110
- cli: "claude".to_string(),
111
- stdin: "{}".to_string(),
112
- cwd: Some("/repo".to_string()),
113
- };
114
- let json = serde_json::to_value(&msg).unwrap();
115
- assert_eq!(json["type"], "hook");
116
- assert_eq!(json["hookEvent"], "PreToolUse");
117
- assert_eq!(json["cli"], "claude");
118
- assert_eq!(json["cwd"], "/repo");
119
- }
120
-
121
- #[test]
122
- fn hook_request_cwd_is_optional() {
123
- let json = serde_json::json!({
124
- "type": "hook",
125
- "protocolVersion": 1,
126
- "hookEvent": "Stop",
127
- "cli": "codex",
128
- "stdin": "{}"
129
- });
130
- let msg: ClientMessage = serde_json::from_value(json).unwrap();
131
- match msg {
132
- ClientMessage::Hook { cwd, .. } => assert_eq!(cwd, None),
133
- _ => panic!("expected Hook variant"),
134
- }
135
- }
136
-
137
- #[test]
138
- fn unknown_message_type_fails_to_deserialize() {
139
- let json = serde_json::json!({ "type": "bogus", "protocolVersion": 1 });
140
- let result: Result<ClientMessage, _> = serde_json::from_value(json);
141
- assert!(result.is_err());
142
- }
143
-
144
- #[test]
145
- fn server_hook_result_round_trips() {
146
- let msg = ServerMessage::HookResult {
147
- protocol_version: PROTOCOL_VERSION,
148
- exit_code: 2,
149
- stdout: String::new(),
150
- stderr: "blocked".to_string(),
151
- };
152
- let json = serde_json::to_string(&msg).unwrap();
153
- let decoded: ServerMessage = serde_json::from_str(&json).unwrap();
154
- assert_eq!(decoded, msg);
155
- }
156
-
157
- #[test]
158
- fn protocol_version_accessor_matches_every_variant() {
159
- assert_eq!(
160
- ClientMessage::Ping {
161
- protocol_version: 7
162
- }
163
- .protocol_version(),
164
- 7
165
- );
166
- assert_eq!(
167
- ServerMessage::Error {
168
- protocol_version: 9,
169
- message: "x".to_string()
170
- }
171
- .protocol_version(),
172
- 9
173
- );
174
- }
175
- }