failproofai 1.0.0-beta.4 → 1.0.0-beta.6

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 (369) 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]__0spkm68._.js +10 -10
  116. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1_a_gwn._.js +3 -0
  117. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1a2hcb_._.js +3 -0
  118. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1e7-pa7._.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/_0igtyj5._.js +3 -0
  128. package/.next/standalone/.next/server/chunks/ssr/_0r-dsre._.js +3 -0
  129. package/.next/standalone/.next/server/chunks/ssr/_0rjgfln._.js +3 -0
  130. package/.next/standalone/.next/server/chunks/ssr/_0se484m._.js +3 -0
  131. package/.next/standalone/.next/server/chunks/ssr/_0uz7o9y._.js +3 -0
  132. package/.next/standalone/.next/server/chunks/ssr/_11_p9y8._.js +1 -1
  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/{2c90h1g6x8z8y.js → 0_2n13i94rnw5.js} +1 -1
  150. package/.next/standalone/.next/static/chunks/1bm0q12hgs22l.css +2 -0
  151. package/.next/standalone/.next/static/chunks/1v1gqvpjk62jm.js +1 -0
  152. package/.next/standalone/.next/static/chunks/23557z4e6dtsr.js +1 -0
  153. package/.next/standalone/.next/static/chunks/28zkwzu50puki.js +1 -0
  154. package/.next/standalone/.next/static/chunks/2uvhw57mf9lg_.css +2 -0
  155. package/.next/standalone/.next/static/chunks/3081gykdubv8b.js +1 -0
  156. package/.next/standalone/.next/static/chunks/{06jakt_xhoolk.js → 34fa65k0cldkj.js} +1 -1
  157. package/.next/standalone/.next/static/chunks/35v07j9gc1pvr.js +6 -0
  158. package/.next/standalone/.next/static/chunks/{2zb3m1tzq6h98.js → 3f_939wi3kfnx.js} +22 -22
  159. package/.next/standalone/.next/static/chunks/{31cqiv_cpgj9m.js → 3p1vpm2_-tfwm.js} +1 -1
  160. package/.next/standalone/.next/static/chunks/{0ue9ak_xexiqh.js → 3rbpw_waieafm.js} +1 -1
  161. package/.next/standalone/.next/static/chunks/40ffnfpkeyvb0.js +1 -0
  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 +197 -80
  197. package/dist/cli.mjs +17699 -15345
  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 +163 -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 +32 -40
  229. package/src/hooks/configure-wizard.ts +696 -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 +595 -27
  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 +342 -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-lock.ts +119 -0
  246. package/src/hooks/policy-types.ts +0 -7
  247. package/src/hooks/session-pause-cli.ts +15 -13
  248. package/src/hooks/session-pause.ts +36 -4
  249. package/src/hooks/setup-state.ts +256 -0
  250. package/src/hooks/tui.ts +4 -4
  251. package/src/hooks/worker-server.ts +53 -1
  252. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0f8nu8d._.js +0 -3
  253. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0szqzl8._.js +0 -3
  254. package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +0 -3
  255. package/.next/standalone/.next/server/chunks/[root-of-the-server]__19120tr._.js +0 -3
  256. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1fwl2mz._.js +0 -3
  257. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1myjm-d._.js +0 -3
  258. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rhmvod._.js +0 -3
  259. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__02r5bgf._.js +0 -4
  260. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0cmdyn_._.js +0 -3
  261. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e8sjqm._.js +0 -4
  262. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0lkzqax._.js +0 -4
  263. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ls0gsr._.js +0 -4
  264. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0n_uvv0._.js +0 -3
  265. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qucxyj._.js +0 -4
  266. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0sdrz-j._.js +0 -3
  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]__1j4u3yd._.js +0 -4
  270. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1zqz4v8._.js +0 -4
  271. package/.next/standalone/.next/server/chunks/ssr/_1m_p7pi._.js +0 -3
  272. package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_1q9xxca.js +0 -3
  273. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1ezd2jf._.js +0 -5
  274. package/.next/standalone/.next/server/chunks/ssr/src_hooks_1tnuifj._.js +0 -5
  275. package/.next/standalone/.next/static/chunks/0h7auy7hzjyhw.js +0 -1
  276. package/.next/standalone/.next/static/chunks/0oe2-hp0_2tzs.js +0 -6
  277. package/.next/standalone/.next/static/chunks/1fw3pxobk_otl.js +0 -1
  278. package/.next/standalone/.next/static/chunks/3h5_3u46gu9jx.js +0 -1
  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/.next/static/chunks/43a1ywqsg9bq9.js +0 -1
  282. package/.next/standalone/Cargo.lock +0 -2222
  283. package/.next/standalone/Cargo.toml +0 -9
  284. package/.next/standalone/crates/CLOUD_POLICIES.md +0 -136
  285. package/.next/standalone/crates/PROTOCOL.md +0 -122
  286. package/.next/standalone/crates/failproofaid/Cargo.toml +0 -25
  287. package/.next/standalone/crates/failproofaid/src/cloud_client.rs +0 -484
  288. package/.next/standalone/crates/failproofaid/src/cloud_policies.rs +0 -921
  289. package/.next/standalone/crates/failproofaid/src/lock.rs +0 -81
  290. package/.next/standalone/crates/failproofaid/src/main.rs +0 -532
  291. package/.next/standalone/crates/failproofaid/src/paths.rs +0 -201
  292. package/.next/standalone/crates/failproofaid/src/server.rs +0 -502
  293. package/.next/standalone/crates/failproofaid/src/worker.rs +0 -400
  294. package/.next/standalone/crates/failproofaid/tests/daemon_e2e.rs +0 -241
  295. package/.next/standalone/crates/fpai-collect/Cargo.toml +0 -43
  296. package/.next/standalone/crates/fpai-collect/src/config.rs +0 -412
  297. package/.next/standalone/crates/fpai-collect/src/cursor.rs +0 -520
  298. package/.next/standalone/crates/fpai-collect/src/delivery.rs +0 -472
  299. package/.next/standalone/crates/fpai-collect/src/filetail.rs +0 -484
  300. package/.next/standalone/crates/fpai-collect/src/health.rs +0 -369
  301. package/.next/standalone/crates/fpai-collect/src/lib.rs +0 -34
  302. package/.next/standalone/crates/fpai-collect/src/redact.rs +0 -516
  303. package/.next/standalone/crates/fpai-collect/src/sources/claude/mod.rs +0 -279
  304. package/.next/standalone/crates/fpai-collect/src/sources/claude/transform.rs +0 -619
  305. package/.next/standalone/crates/fpai-collect/src/sources/codex/mod.rs +0 -156
  306. package/.next/standalone/crates/fpai-collect/src/sources/codex/transform.rs +0 -715
  307. package/.next/standalone/crates/fpai-collect/src/sources/copilot/mod.rs +0 -158
  308. package/.next/standalone/crates/fpai-collect/src/sources/copilot/transform.rs +0 -512
  309. package/.next/standalone/crates/fpai-collect/src/sources/goose/mod.rs +0 -511
  310. package/.next/standalone/crates/fpai-collect/src/sources/goose/transform.rs +0 -586
  311. package/.next/standalone/crates/fpai-collect/src/sources/hermes/mod.rs +0 -318
  312. package/.next/standalone/crates/fpai-collect/src/sources/hermes/transform.rs +0 -579
  313. package/.next/standalone/crates/fpai-collect/src/sources/hooks/mod.rs +0 -310
  314. package/.next/standalone/crates/fpai-collect/src/sources/hooks/transform.rs +0 -448
  315. package/.next/standalone/crates/fpai-collect/src/sources/mod.rs +0 -23
  316. package/.next/standalone/crates/fpai-collect/src/sources/openclaw/mod.rs +0 -176
  317. package/.next/standalone/crates/fpai-collect/src/sources/openclaw/transform.rs +0 -500
  318. package/.next/standalone/crates/fpai-collect/src/sources/opencode/mod.rs +0 -315
  319. package/.next/standalone/crates/fpai-collect/src/sources/opencode/transform.rs +0 -493
  320. package/.next/standalone/crates/fpai-collect/src/sources/pi/mod.rs +0 -201
  321. package/.next/standalone/crates/fpai-collect/src/sources/pi/transform.rs +0 -533
  322. package/.next/standalone/crates/fpai-collect/src/spool.rs +0 -458
  323. package/.next/standalone/crates/fpai-collect/src/sqlitepoll.rs +0 -387
  324. package/.next/standalone/crates/fpai-collect/src/supervisor.rs +0 -423
  325. package/.next/standalone/crates/fpai-collect/src/uploader.rs +0 -600
  326. package/.next/standalone/crates/fpai-collect/tests/claude_source.rs +0 -738
  327. package/.next/standalone/crates/fpai-collect/tests/claude_subagents.rs +0 -393
  328. package/.next/standalone/crates/fpai-collect/tests/codex_source.rs +0 -975
  329. package/.next/standalone/crates/fpai-collect/tests/config.rs +0 -286
  330. package/.next/standalone/crates/fpai-collect/tests/copilot_source.rs +0 -1019
  331. package/.next/standalone/crates/fpai-collect/tests/delivery.rs +0 -281
  332. package/.next/standalone/crates/fpai-collect/tests/goose_source.rs +0 -1008
  333. package/.next/standalone/crates/fpai-collect/tests/hermes_source.rs +0 -1129
  334. package/.next/standalone/crates/fpai-collect/tests/hooks_source.rs +0 -621
  335. package/.next/standalone/crates/fpai-collect/tests/openclaw_source.rs +0 -854
  336. package/.next/standalone/crates/fpai-collect/tests/opencode_source.rs +0 -1478
  337. package/.next/standalone/crates/fpai-collect/tests/pi_source.rs +0 -1191
  338. package/.next/standalone/crates/fpai-collect/tests/supervisor.rs +0 -290
  339. package/.next/standalone/crates/fpai-collect/tests/uploader.rs +0 -506
  340. package/.next/standalone/crates/fpai-ipc/Cargo.toml +0 -16
  341. package/.next/standalone/crates/fpai-ipc/src/envelope.rs +0 -175
  342. package/.next/standalone/crates/fpai-ipc/src/framing.rs +0 -177
  343. package/.next/standalone/crates/fpai-ipc/src/lib.rs +0 -10
  344. package/.next/standalone/crates/fpai-ipc/src/peer.rs +0 -80
  345. package/.next/standalone/docker-hook-sync/Dockerfile +0 -79
  346. package/.next/standalone/docker-hook-sync/entrypoint.sh +0 -109
  347. package/.next/standalone/integration-suite/Dockerfile +0 -47
  348. package/.next/standalone/integration-suite/README.md +0 -129
  349. package/.next/standalone/integration-suite/canary-policies.mjs +0 -45
  350. package/.next/standalone/integration-suite/capture-tokens.sh +0 -48
  351. package/.next/standalone/integration-suite/ci-entrypoint.sh +0 -179
  352. package/.next/standalone/integration-suite/inject-tokens.sh +0 -44
  353. package/.next/standalone/integration-suite/install-clis.sh +0 -136
  354. package/.next/standalone/integration-suite/probe-cli.sh +0 -248
  355. package/.next/standalone/integration-suite/report.js +0 -120
  356. package/.next/standalone/integration-suite/run.sh +0 -159
  357. package/.next/standalone/openclaw-plugin/index.js +0 -223
  358. package/.next/standalone/openclaw-plugin/openclaw.plugin.json +0 -6
  359. package/.next/standalone/openclaw-plugin/package.json +0 -12
  360. package/.next/standalone/osv-scanner.toml +0 -17
  361. package/.next/standalone/pi-extension/index.ts +0 -524
  362. package/.next/standalone/pi-extension/package.json +0 -12
  363. package/.next/standalone/postcss.config.mjs +0 -8
  364. package/.next/standalone/readme-arch-hq.gif +0 -0
  365. package/.next/standalone/rust-toolchain.toml +0 -4
  366. package/src/auth/cli.ts +0 -371
  367. /package/.next/standalone/.next/static/{StV6Lay7Q1cU4ysYCFJmH → Ptn8aBQl_cnNgOd0ebyv0}/_buildManifest.js +0 -0
  368. /package/.next/standalone/.next/static/{StV6Lay7Q1cU4ysYCFJmH → Ptn8aBQl_cnNgOd0ebyv0}/_clientMiddlewareManifest.js +0 -0
  369. /package/.next/standalone/.next/static/{StV6Lay7Q1cU4ysYCFJmH → Ptn8aBQl_cnNgOd0ebyv0}/_ssgManifest.js +0 -0
@@ -30,10 +30,19 @@
30
30
  */
31
31
  import {
32
32
  cloudCredentialPath,
33
+ validateCloudUrl,
33
34
  verifyCloudCredentials,
34
35
  writeCloudCredentials,
35
36
  type CloudCredentials,
36
37
  } from "./cloud-enrollment";
38
+ import { updateConfig, readCredentials, writeCredentials } from "./fp-config";
39
+ import {
40
+ introspectKey,
41
+ hasPermission,
42
+ describeOrg,
43
+ PERMISSION_EVENTS,
44
+ PERMISSION_POLICIES,
45
+ } from "./cloud-introspect";
37
46
  import {
38
47
  ingestPath,
39
48
  validateIngestKey,
@@ -42,8 +51,23 @@ import {
42
51
  type CollectorSettings,
43
52
  } from "./collector-config";
44
53
 
45
- /** The path ingest is served on, appended to the cloud base URL. */
46
- export const INGEST_PATH = "/events";
54
+ /**
55
+ * The path ingest is served on, appended to the cloud base URL.
56
+ *
57
+ * `/v1/events` rather than `/events`, and the difference is load-bearing for
58
+ * the hosted deployment. The server mounts its route list TWICE — flat at `/`
59
+ * and nested under `/v1` — so both reach the same handler when you talk to the
60
+ * server directly. But on the dashboard hostname a reverse proxy routes only
61
+ * `/v1/*` and `/enforcement/v1/*` to the server and sends EVERYTHING ELSE to
62
+ * the Next.js app. So `https://app.befailproof.ai/events` does not reach ingest
63
+ * at all; it reaches a web app that will happily answer.
64
+ *
65
+ * `/v1/events` is therefore the only path that works on all three shapes: the
66
+ * dashboard hostname, the server hostname, and a Compose deployment with no
67
+ * proxy at all. That is what lets one `--connect <origin>` configure BOTH
68
+ * capabilities from a single URL.
69
+ */
70
+ export const INGEST_PATH = "/v1/events";
47
71
 
48
72
  /**
49
73
  * The ingest endpoint for a cloud base URL.
@@ -66,20 +90,45 @@ export function ingestUrlFor(baseUrl: string): string {
66
90
  */
67
91
  export function cloudBaseFor(url: string): string {
68
92
  const trimmed = url.replace(/\/$/, "");
69
- return trimmed.endsWith(INGEST_PATH) ? trimmed.slice(0, -INGEST_PATH.length) : trimmed;
93
+ // Strip either ingest path. People paste what an older setup asked for, what
94
+ // is already in their config, or what a colleague sent them — and `/events`
95
+ // was the documented value for the whole life of the product before `/v1`.
96
+ // Refusing a URL we can read perfectly well is pedantry; silently treating
97
+ // `https://host/events` as an ORIGIN would be worse, because every derived
98
+ // URL would then carry a stray `/events` segment.
99
+ for (const suffix of [INGEST_PATH, "/events"]) {
100
+ if (trimmed.endsWith(suffix)) return trimmed.slice(0, -suffix.length);
101
+ }
102
+ return trimmed;
70
103
  }
71
104
 
72
105
  export interface ConnectInput {
73
- /** Cloud base URL, already validated by `validateCloudUrl`. */
106
+ /**
107
+ * Cloud base URL. Re-validated with `validateCloudUrl` on entry rather than
108
+ * trusted — this used to say "already validated by `validateCloudUrl`", and
109
+ * the wizard did not.
110
+ */
74
111
  url: string;
75
112
  token: string;
76
113
  machineId: string;
77
- /** Transcripts are a separate, explicit opt-in. Never defaulted on. */
114
+ /** Human-facing display name for this machine. Defaults to the hostname. */
115
+ machineLabel?: string;
116
+ /**
117
+ * Send session transcripts.
118
+ *
119
+ * Both callers — the wizard and `--connect` — now pass `true` unless the
120
+ * user opts out, because transcripts are what makes a dashboard worth
121
+ * connecting to. This function itself still requires it EXPLICITLY and
122
+ * treats `undefined` as off: a library that silently opts a caller into
123
+ * shipping prompts and file contents is the wrong default at this layer,
124
+ * whatever the product default above it happens to be.
125
+ */
78
126
  sessions?: boolean;
79
127
  environment?: string;
80
128
  /** Injected by tests so nothing reaches the network. */
81
129
  verifyPolicy?: typeof verifyCloudCredentials;
82
130
  verifyIngest?: typeof validateIngestKey;
131
+ introspect?: typeof introspectKey;
83
132
  }
84
133
 
85
134
  export interface CapabilityOutcome {
@@ -90,6 +139,8 @@ export interface CapabilityOutcome {
90
139
  export interface ConnectOutcome {
91
140
  policy: CapabilityOutcome & { policyCount?: number; generation?: number };
92
141
  ingest: CapabilityOutcome;
142
+ /** Which organisation the key belongs to. Absent on a pre-introspect server. */
143
+ org?: { id?: string; slug?: string; name?: string };
93
144
  /** True when at least one capability was configured. */
94
145
  anyConfigured: boolean;
95
146
  }
@@ -102,22 +153,81 @@ export interface ConnectOutcome {
102
153
  * connection the machine does not have.
103
154
  */
104
155
  export async function connectToCloud(input: ConnectInput): Promise<ConnectOutcome> {
156
+ // Checked HERE, not only at the call sites. `ConnectInput.url` is documented
157
+ // as "already validated by `validateCloudUrl`", and that was true of exactly
158
+ // one of the two callers: `--connect` validated, and the interactive wizard —
159
+ // the documented primary path — matched `/^https?:\/\//` and handed the raw
160
+ // string straight to `validateIngestKey` and then to here. So the flow most
161
+ // people use sent the machine's bearer token to any `http://` host in clear.
162
+ // A precondition worth stating in a docstring is worth enforcing at the
163
+ // boundary that depends on it.
164
+ const checked = validateCloudUrl(input.url);
165
+ if (!checked.ok) {
166
+ return {
167
+ policy: { ok: false, reason: checked.reason },
168
+ ingest: { ok: false, reason: checked.reason },
169
+ anyConfigured: false,
170
+ };
171
+ }
172
+
105
173
  const creds: CloudCredentials = {
106
174
  url: input.url,
107
175
  machineId: input.machineId,
108
176
  token: input.token,
177
+ machineLabel: input.machineLabel,
109
178
  };
110
179
 
111
180
  const verifyPolicy = input.verifyPolicy ?? verifyCloudCredentials;
112
181
  const verifyIngest = input.verifyIngest ?? validateIngestKey;
182
+ const introspect = input.introspect ?? introspectKey;
113
183
  const ingestUrl = ingestUrlFor(input.url);
114
184
 
185
+ // ── Ask the key what it is, before using it for anything ──────────────────
186
+ //
187
+ // One request answers three questions that are otherwise indistinguishable
188
+ // until much later: is the key accepted, which ORG does it report into, and
189
+ // does it carry the permissions this machine needs. Probing the endpoints
190
+ // (below) can only ever answer the first, and only by inference.
191
+ //
192
+ // A rejected key stops here — probing two more endpoints with a credential
193
+ // the server has already refused tells us nothing and costs two more
194
+ // unauthenticated database lookups on a surface with no rate limiting.
195
+ const identity = await introspect(input.url, input.token);
196
+ if (identity.kind === "rejected") {
197
+ return {
198
+ policy: { ok: false, reason: "the server did not accept that key" },
199
+ ingest: { ok: false, reason: "the server did not accept that key" },
200
+ anyConfigured: false,
201
+ };
202
+ }
203
+
204
+ // `unsupported` — an AgentEye older than the introspect endpoint. Fall
205
+ // through to probing, which is what every release before this one did. The
206
+ // CLI ships independently of whatever server a customer runs, so a good key
207
+ // on an older deployment must still connect.
208
+ const known = identity.kind === "ok" ? identity.identity : null;
209
+
210
+ // Skip a probe the key provably cannot pass. This is not an optimisation: a
211
+ // permission failure surfaces from `/events` as a 403 whose message reads
212
+ // like a server problem, where introspect can say exactly which permission is
213
+ // missing and for which org.
214
+ const canIngest = known ? hasPermission(known, PERMISSION_EVENTS) : true;
215
+ const canPolicy = known ? hasPermission(known, PERMISSION_POLICIES) : true;
216
+
217
+ const missing = (permission: string) =>
218
+ `that key does not carry \`${permission}\`` +
219
+ (known ? ` (it is valid for ${describeOrg(known)})` : "");
220
+
115
221
  // Both checks run regardless of the other's result: reporting "cannot pull
116
222
  // policy" while staying silent about ingest would send someone to fix one
117
223
  // permission and hit the next one afterwards.
118
224
  const [policyResult, ingestResult] = await Promise.all([
119
- verifyPolicy(creds),
120
- verifyIngest({ url: ingestUrl, key: input.token }),
225
+ canPolicy
226
+ ? verifyPolicy(creds)
227
+ : Promise.resolve({ ok: false as const, reason: missing(PERMISSION_POLICIES) }),
228
+ canIngest
229
+ ? verifyIngest({ url: ingestUrl, key: input.token })
230
+ : Promise.resolve({ ok: false as const, reason: missing(PERMISSION_EVENTS) }),
121
231
  ]);
122
232
 
123
233
  const outcome: ConnectOutcome = {
@@ -126,28 +236,52 @@ export async function connectToCloud(input: ConnectInput): Promise<ConnectOutcom
126
236
  : { ok: false, reason: policyResult.reason },
127
237
  ingest: ingestResult.ok ? { ok: true } : { ok: false, reason: ingestResult.reason },
128
238
  anyConfigured: false,
239
+ org: known
240
+ ? { id: known.orgId, slug: known.orgSlug, name: known.orgName }
241
+ : undefined,
129
242
  };
130
243
 
131
244
  if (policyResult.ok) {
132
- writeCloudCredentials(creds);
245
+ writeCloudCredentials(creds);
133
246
  outcome.anyConfigured = true;
134
247
  }
135
248
 
249
+ // Mode is the hard gate, set only once a capability actually verified.
250
+ // "cloud" is never inferred from a token merely being present: a machine
251
+ // that has not proven it can reach the server must stay provably silent.
252
+ if (policyResult.ok || ingestResult.ok) {
253
+ updateConfig({ mode: "cloud" });
254
+ }
255
+
136
256
  if (ingestResult.ok) {
137
257
  writeIngestCredential({ url: ingestUrl, key: input.token });
138
258
  const settings: CollectorSettings = {
139
259
  // Hook activity is what makes the dashboard show anything at all, and it
140
260
  // carries decisions and tool names — never file contents.
141
261
  hooks: true,
142
- // Transcripts carry prompts, file contents and whatever was pasted into
143
- // a terminal, so they stay off unless explicitly asked for.
262
+ // Explicit only — see the `sessions` doc on ConnectInput. Callers decide
263
+ // the product default; this layer never infers one.
144
264
  sessions: input.sessions === true,
145
265
  environment: input.environment,
266
+ // So the daemon stamps this machine's id on every collected event and the
267
+ // dashboard groups by machine, not by per-project agent_id.
268
+ machineId: input.machineId,
146
269
  };
147
270
  writeCollectorSettings(settings);
148
271
  outcome.anyConfigured = true;
149
272
  }
150
273
 
274
+ // Last, and only once something actually configured: recording an org for a
275
+ // machine that connected to nothing would be a claim about where data goes
276
+ // from a machine that sends none. Merged into whatever the two writers above
277
+ // just wrote, so it cannot race them.
278
+ if (outcome.anyConfigured && known && (known.orgId || known.orgSlug || known.orgName)) {
279
+ writeCredentials({
280
+ ...readCredentials(),
281
+ org: { id: known.orgId, slug: known.orgSlug, name: known.orgName },
282
+ });
283
+ }
284
+
151
285
  return outcome;
152
286
  }
153
287
 
@@ -161,8 +295,19 @@ export async function connectToCloud(input: ConnectInput): Promise<ConnectOutcom
161
295
  export function describeOutcome(outcome: ConnectOutcome, machineId: string, url: string): string[] {
162
296
  const lines: string[] = [];
163
297
 
298
+ // Named on every branch that connected, including the partial ones. A key
299
+ // pasted from the wrong organisation authenticates perfectly and reports into
300
+ // somewhere the user is not looking; the org is the only thing on screen that
301
+ // makes that visible, and it is worth nothing if it only prints on success.
302
+ const org = outcome.org
303
+ ? outcome.org.name && outcome.org.slug
304
+ ? `${outcome.org.name} (${outcome.org.slug})`
305
+ : (outcome.org.name ?? outcome.org.slug ?? outcome.org.id)
306
+ : undefined;
307
+ const into = org ? ` into ${org}` : "";
308
+
164
309
  if (outcome.policy.ok && outcome.ingest.ok) {
165
- lines.push(`Connected to ${url} as ${machineId}.`);
310
+ lines.push(`Connected to ${url} as ${machineId}${into}.`);
166
311
  const n = outcome.policy.policyCount ?? 0;
167
312
  lines.push(` Policy ${n} polic${n === 1 ? "y" : "ies"} assigned (generation ${outcome.policy.generation ?? 0}).`);
168
313
  lines.push(` Dashboard hook activity will be sent to ${ingestUrlFor(url)}.`);
@@ -171,7 +316,7 @@ export function describeOutcome(outcome: ConnectOutcome, machineId: string, url:
171
316
 
172
317
  if (outcome.policy.ok && !outcome.ingest.ok) {
173
318
  const n = outcome.policy.policyCount ?? 0;
174
- lines.push(`Connected to ${url} as ${machineId} for policy only.`);
319
+ lines.push(`Connected to ${url} as ${machineId}${into}, for policy only.`);
175
320
  lines.push(` Policy ${n} polic${n === 1 ? "y" : "ies"} assigned (generation ${outcome.policy.generation ?? 0}).`);
176
321
  lines.push(` Dashboard NOT configured: ${outcome.ingest.reason}`);
177
322
  lines.push("");
@@ -181,7 +326,7 @@ export function describeOutcome(outcome: ConnectOutcome, machineId: string, url:
181
326
  }
182
327
 
183
328
  if (!outcome.policy.ok && outcome.ingest.ok) {
184
- lines.push(`Connected to ${url} as ${machineId} for dashboard reporting only.`);
329
+ lines.push(`Connected to ${url} as ${machineId}${into}, for dashboard reporting only.`);
185
330
  lines.push(` Dashboard hook activity will be sent to ${ingestUrlFor(url)}.`);
186
331
  lines.push(` Cloud policy NOT configured: ${outcome.policy.reason}`);
187
332
  lines.push("");
@@ -201,5 +346,9 @@ export function configuredPaths(outcome: ConnectOutcome): string[] {
201
346
  const paths: string[] = [];
202
347
  if (outcome.policy.ok) paths.push(cloudCredentialPath());
203
348
  if (outcome.ingest.ok) paths.push(ingestPath());
204
- return paths;
349
+ // Deduplicated because layout 2 consolidated both credentials into
350
+ // `credentials.toml`, so the two capabilities now name the SAME file and the
351
+ // closing note read "stored in <path> and <path>" — the same path, twice.
352
+ // They stay two entries above because the JSON override can still split them.
353
+ return [...new Set(paths)];
205
354
  }
@@ -6,12 +6,17 @@ import {
6
6
  clearCloudCredentials,
7
7
  maskToken,
8
8
  readCloudCredentials,
9
+ resolveMachineId,
10
+ resolveMachineLabel,
9
11
  validateCloudUrl,
10
12
  verifyCloudCredentials,
11
13
  writeCloudCredentials,
12
14
  cloudCredentialPath,
13
15
  } from "./cloud-enrollment";
14
- import { daemonServiceStatus } from "./daemon-service";
16
+ import { daemonRestartCommand, daemonServiceStatus, daemonVersionSkew } from "./daemon-service";
17
+ import { clearActiveCloudManagedPolicies } from "./cloud-managed-policies";
18
+ import { readVersionFile, readCredentials } from "./fp-config";
19
+ import { version as cliVersion } from "../../package.json";
15
20
  import { daemonSocketPresent } from "./daemon-client";
16
21
  import {
17
22
  cloudBaseFor,
@@ -26,23 +31,45 @@ import {
26
31
  readIngestCredential,
27
32
  validateIngestKey,
28
33
  } from "./collector-config";
34
+ import { updateConfig } from "./fp-config";
35
+ import type { introspectKey } from "./cloud-introspect";
29
36
 
30
37
  export interface ConnectOptions {
31
38
  url?: string;
32
39
  token?: string;
33
40
  machineId?: string;
34
- /** Defaults to the hostname; injected for tests. */
41
+ /** Human-facing display name for this machine. Defaults to the hostname. */
42
+ machineLabel?: string;
43
+ /**
44
+ * The hostname, passed in by the caller (and injected by tests). Used as the
45
+ * default machine *label*, no longer as the machine id — the id now mints or
46
+ * reuses a stable key so two hosts with the same hostname never merge.
47
+ */
35
48
  defaultMachineId?: string;
36
49
  /**
37
- * Send full session transcripts as well as hook decisions. Off unless asked
38
- * for: a transcript carries prompts, file contents and whatever was pasted
39
- * into a terminal, so it can never be a side effect of connecting.
50
+ * Send full session transcripts as well as hook decisions.
51
+ *
52
+ * DEFAULTS ON. A transcript carries prompts, file contents and whatever was
53
+ * pasted into a terminal — which is precisely what makes a dashboard worth
54
+ * connecting to, so hiding it behind an opt-in produced the empty-dashboard
55
+ * problem in a different costume. `false` is the explicit opt-out; the
56
+ * disclosure is printed either way.
40
57
  */
41
58
  sessions?: boolean;
42
59
  environment?: string;
43
- /** Injected for tests so nothing reaches the network. */
60
+ /**
61
+ * Injected for tests so nothing reaches the network.
62
+ *
63
+ * All THREE must be here, and a new network call added below has to gain its
64
+ * seam in the same commit: `introspect` was added without one, so every test
65
+ * in `cloud-enrollment-cli.test.ts` silently began making a real request to
66
+ * `be.failproof.ai`. It usually failed fast enough to pass, which is the worst
67
+ * available outcome — the suite went intermittently red on network timing
68
+ * rather than on anything a change had broken.
69
+ */
44
70
  verify?: typeof verifyCloudCredentials;
45
71
  verifyIngest?: typeof validateIngestKey;
72
+ introspect?: typeof introspectKey;
46
73
  daemonStatus?: () => ReturnType<typeof daemonServiceStatus>;
47
74
  }
48
75
 
@@ -116,10 +143,14 @@ export async function runConnectCommand(opts: ConnectOptions): Promise<CommandRe
116
143
  const validated = validateCloudUrl(cloudBaseFor(opts.url));
117
144
  if (!validated.ok) return { exitCode: 1, lines: [validated.reason] };
118
145
 
119
- const machineId = (opts.machineId ?? opts.defaultMachineId ?? "").trim();
120
- if (!machineId) {
121
- return { exitCode: 1, lines: ["Could not determine a machine id — pass one with --machine-id <id>."] };
122
- }
146
+ // The stable key: an explicit --machine-id wins, else the id already enrolled
147
+ // here is reused (idempotent re-connect), else a fresh one is minted. Never
148
+ // the hostname — that is the label, so same-hostname hosts do not merge.
149
+ const machineId = resolveMachineId(opts.machineId);
150
+ const machineLabel = resolveMachineLabel(opts.machineLabel ?? opts.defaultMachineId);
151
+ // Humans read the label; the id is shown in parentheses only when it differs,
152
+ // so an operator who set an explicit --machine-id still sees it.
153
+ const shownAs = machineLabel === machineId ? machineId : `${machineLabel} (${machineId})`;
123
154
 
124
155
  // ONE connection, both capabilities. Each is verified before anything is
125
156
  // written and reported on its own, because a key can carry `policies:pull`
@@ -128,14 +159,16 @@ export async function runConnectCommand(opts: ConnectOptions): Promise<CommandRe
128
159
  url: validated.url,
129
160
  token: opts.token,
130
161
  machineId,
162
+ machineLabel,
131
163
  sessions: opts.sessions,
132
164
  environment: opts.environment,
133
165
  verifyPolicy: opts.verify,
134
166
  verifyIngest: opts.verifyIngest,
167
+ introspect: opts.introspect,
135
168
  });
136
169
 
137
170
  if (!outcome.anyConfigured) {
138
- return { exitCode: 1, lines: describeOutcome(outcome, machineId, validated.url) };
171
+ return { exitCode: 1, lines: describeOutcome(outcome, shownAs, validated.url) };
139
172
  }
140
173
 
141
174
  // The exit code tracks the PRIMARY purpose — enrolling this machine for
@@ -149,13 +182,20 @@ export async function runConnectCommand(opts: ConnectOptions): Promise<CommandRe
149
182
  const status = (opts.daemonStatus ?? daemonServiceStatus)();
150
183
  const paths = configuredPaths(outcome);
151
184
  const lines = [
152
- ...describeOutcome(outcome, machineId, validated.url),
185
+ ...describeOutcome(outcome, shownAs, validated.url),
153
186
  ` Token ${maskToken(opts.token)} stored in ${paths.join(" and ")} (owner-only).`,
154
187
  ];
155
- if (outcome.ingest.ok && opts.sessions !== true) {
156
- // Said out loud rather than left as a default: someone who wanted
157
- // transcripts should not discover months later that none were sent.
158
- lines.push(" Session transcripts are NOT being sent. Add --send-transcripts to include them.");
188
+ if (outcome.ingest.ok) {
189
+ // Stated on BOTH branches, never only on the surprising one. Connecting
190
+ // sends transcripts by default — prompts, file contents, command output —
191
+ // and a default that carries that much is one the user has to be told
192
+ // about at the moment it takes effect, not left to find in --help.
193
+ lines.push(
194
+ opts.sessions === false
195
+ ? " Session transcripts are NOT being sent (--no-transcripts). Decisions only."
196
+ : " Sending policy decisions AND full session transcripts (prompts, file\n" +
197
+ " contents, command output). Use --no-transcripts for decisions only.",
198
+ );
159
199
  }
160
200
  lines.push(...daemonWarning(status));
161
201
 
@@ -170,24 +210,63 @@ export function runDisconnectCommand(): CommandResult {
170
210
  // leave the machine still shipping activity to a cloud the user believes
171
211
  // they have left — the one outcome nobody expects from this command.
172
212
  const removedIngest = clearIngestCredential();
213
+ // Stop ENFORCING them too, not merely refreshing them. Clearing the
214
+ // credential ends the daemon's polling; every artifact already on disk stayed
215
+ // referenced by `active.json` and kept being loaded on every tool call — so a
216
+ // machine that had deliberately left its organisation went on being governed
217
+ // by whatever generation was current when it left, indefinitely, while
218
+ // `--status` reported it as unconnected.
219
+ const stoppedManaged = clearActiveCloudManagedPolicies();
220
+ // Back to OSS. Leaving mode = "cloud" with no credentials would describe a
221
+ // machine that does not exist, and every cloud code path keys off this flag
222
+ // rather than off "is a token lying around" precisely so that a disconnected
223
+ // machine is provably silent instead of silent-by-happenstance.
224
+ updateConfig({ mode: "oss" });
173
225
 
174
- if (!removed && !existing && !hadIngest) {
226
+ if (!removed && !existing && !hadIngest && !stoppedManaged) {
175
227
  return { exitCode: 0, lines: ["This machine is not connected to Failproof Cloud."] };
176
228
  }
177
229
 
178
230
  const lines = [
179
231
  "Disconnected from Failproof Cloud.",
180
232
  "",
181
- " Cloud-managed policies already on disk stop being refreshed. Local",
182
- " builtin, custom and convention policies are unaffected.",
233
+ stoppedManaged
234
+ ? " Cloud-managed policies stop being enforced and stop being refreshed.\n" +
235
+ " Local builtin, custom and convention policies are unaffected."
236
+ : " Local builtin, custom and convention policies are unaffected.",
183
237
  ];
184
238
  if (removedIngest) {
185
- lines.push(" Hook activity and transcripts stop being sent.");
239
+ // Named honestly. The collector manager starts once for the daemon's
240
+ // lifetime (`main.rs`) and the uploader caches its bearer key at
241
+ // construction, so nothing already running notices this file disappear —
242
+ // "stop being sent" was true only of the NEXT daemon start. Telling the
243
+ // user the step that makes it true beats a claim that quietly is not.
244
+ lines.push(
245
+ " No new hook activity or transcripts will be queued. A running failproofaid",
246
+ " keeps the key it started with, so restart it to stop the current process",
247
+ ` sending: ${daemonRestartCommand() ?? "restart failproofaid"}`,
248
+ );
186
249
  }
187
250
  return { exitCode: 0, lines };
188
251
  }
189
252
 
190
253
  /** The connection half of `--status`. Never prints the token. */
254
+ /**
255
+ * The version line for `--status`.
256
+ *
257
+ * First thing worth knowing in a bug report, and the only place a user can
258
+ * currently find out which daemon they are running without listing a directory.
259
+ */
260
+ export function versionStatusLines(): string[] {
261
+ const skew = daemonVersionSkew();
262
+ const recorded = readVersionFile();
263
+ const daemon = recorded?.daemon ?? "not installed";
264
+ return [
265
+ `CLI ${cliVersion} · daemon ${daemon}${skew ? " (STALE)" : ""} · layout ${recorded?.layout ?? "-"}`,
266
+ ...(skew ? [" Run `failproofai config` to update the daemon."] : []),
267
+ ];
268
+ }
269
+
191
270
  export function connectionStatusLines(daemonStatus = daemonServiceStatus): string[] {
192
271
  const envUrl = process.env.FAILPROOFAI_CLOUD_URL;
193
272
  if (envUrl) {
@@ -206,15 +285,34 @@ export function connectionStatusLines(daemonStatus = daemonServiceStatus): strin
206
285
  // catch, and two unrelated lines made it easy to miss.
207
286
  if (!creds && !ingest) return ["Cloud: not connected."];
208
287
 
288
+ // Recorded at connect time from the server's own answer, never guessed from
289
+ // the URL: one deployment hosts many orgs, so the host says nothing about
290
+ // which one this machine's data lands in. Absent against a server with no
291
+ // introspect endpoint, and on credentials written before this was recorded —
292
+ // in both cases the line is simply omitted rather than guessed at.
293
+ const org = readCredentials().org;
294
+ const orgLine = org
295
+ ? org.name && org.slug
296
+ ? `${org.name} (${org.slug})`
297
+ : (org.name ?? org.slug ?? org.id)
298
+ : undefined;
299
+
209
300
  const lines: string[] = [];
210
301
  if (creds) {
211
- lines.push(`Cloud: connected to ${creds.url} as ${creds.machineId} (token ${maskToken(creds.token)}).`);
302
+ // Show the human label with the stable id in parentheses; fall back to the
303
+ // bare id for credentials written before labels existed.
304
+ const shownAs = creds.machineLabel
305
+ ? `${creds.machineLabel} (${creds.machineId})`
306
+ : creds.machineId;
307
+ lines.push(`Cloud: connected to ${creds.url} as ${shownAs} (token ${maskToken(creds.token)}).`);
212
308
  lines.push(` Policy pulling centrally-managed policies.`);
213
309
  } else {
214
310
  lines.push(`Cloud: connected to ${cloudBaseFor(ingest!.url)} for reporting only.`);
215
311
  lines.push(` Policy NOT pulling — this machine enforces only its local policies.`);
216
312
  }
217
313
 
314
+ if (orgLine) lines.push(` Org ${orgLine}`);
315
+
218
316
  if (ingest) {
219
317
  lines.push(` Dashboard sending hook activity to ${ingest.url}.`);
220
318
  } else {