failproofai 1.0.0-beta.11 → 1.0.0-beta.13

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 (177) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/.next/required-server-files.json +1 -1
  5. package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
  6. package/.next/standalone/.next/server/app/_global-error/page.js +2 -3
  7. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  8. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
  11. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  12. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  13. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  14. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  15. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  16. package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  17. package/.next/standalone/.next/server/app/_not-found/page.js +2 -3
  18. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  19. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  20. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  21. package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
  22. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
  23. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  24. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
  25. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  26. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  27. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  28. package/.next/standalone/.next/server/app/api/audit/invite/route.js +2 -2
  29. package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/audit/run/route.js +2 -2
  31. package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/auth/login-request/route.js +3 -2
  34. package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +2 -2
  36. package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/auth/logout/route.js +2 -2
  38. package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/api/auth/reminder/route.js +2 -2
  40. package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/api/auth/status/route.js +2 -2
  42. package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  43. package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
  45. package/.next/standalone/.next/server/app/audit/page.js +2 -3
  46. package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
  47. package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
  48. package/.next/standalone/.next/server/app/index.html +1 -1
  49. package/.next/standalone/.next/server/app/index.rsc +15 -15
  50. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  51. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
  52. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
  53. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
  54. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  55. package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
  56. package/.next/standalone/.next/server/app/page.js +2 -3
  57. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  58. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  59. package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +10 -10
  60. package/.next/standalone/.next/server/app/policies/page.js +2 -3
  61. package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
  62. package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
  63. package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
  64. package/.next/standalone/.next/server/app/project/[name]/page.js +3 -4
  65. package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
  66. package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
  67. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
  68. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
  69. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +3 -4
  70. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
  71. package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
  72. package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
  73. package/.next/standalone/.next/server/app/projects/page.js +3 -4
  74. package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
  75. package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  76. package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
  77. package/.next/standalone/.next/server/app/settings/page.js +2 -3
  78. package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
  79. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  80. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1a7i2q4._.js → [root-of-the-server]__00ni9za._.js} +1 -1
  81. package/.next/standalone/.next/server/chunks/[root-of-the-server]__08y94xv._.js +3 -0
  82. package/.next/standalone/.next/server/chunks/[root-of-the-server]__094p1f1._.js +1 -1
  83. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0xu5z._.js +1 -1
  84. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ed01_d._.js +1 -1
  85. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1_ps0jw._.js → [root-of-the-server]__0effm9m._.js} +1 -1
  86. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0lk_935._.js → [root-of-the-server]__0ix8gm7._.js} +1 -1
  87. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0ky_n0r._.js → [root-of-the-server]__0ji11a-._.js} +1 -1
  88. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0v2fpxe._.js → [root-of-the-server]__0ojwjxl._.js} +1 -1
  89. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0vb5fl2._.js +3 -0
  90. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ibjy5x._.js +1 -1
  91. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tvn4wf._.js +1 -1
  92. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1uyc-z4._.js +3 -0
  93. package/.next/standalone/.next/server/chunks/[root-of-the-server]__20d06rh._.js +3 -0
  94. package/.next/standalone/.next/server/chunks/_0lxbzdq._.js +1 -3
  95. package/.next/standalone/.next/server/chunks/_1zuiiy3._.js +1 -3
  96. package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
  97. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1rdruw4._.js → [root-of-the-server]__0_1tiya._.js} +1 -1
  98. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__20ie7me._.js → [root-of-the-server]__0eeipso._.js} +2 -2
  99. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0spkm68._.js +3 -3
  100. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1tyf0tc._.js → [root-of-the-server]__17itmx-._.js} +2 -2
  101. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1gwr8l-._.js → [root-of-the-server]__1dx-a-c._.js} +2 -2
  102. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1mmefsr._.js +5 -0
  103. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1yw368c._.js → [root-of-the-server]__1n2vcv4._.js} +1 -1
  104. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1rv-sc9._.js +1 -1
  105. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1a2hcb_._.js → [root-of-the-server]__1tlvv9a._.js} +1 -1
  106. package/.next/standalone/.next/server/chunks/ssr/_0rjgfln._.js +1 -1
  107. package/.next/standalone/.next/server/chunks/ssr/{_1fahg9e._.js → _0vo2qyt._.js} +2 -2
  108. package/.next/standalone/.next/server/chunks/ssr/{_0uz7o9y._.js → _0xej-0e._.js} +2 -2
  109. package/.next/standalone/.next/server/chunks/ssr/{_04mekdy._.js → _10399ge._.js} +2 -2
  110. package/.next/standalone/.next/server/chunks/ssr/{_0se484m._.js → _149k9oy._.js} +2 -2
  111. package/.next/standalone/.next/server/chunks/ssr/{_0n4bc3i._.js → _166jvu8._.js} +1 -1
  112. package/.next/standalone/.next/server/chunks/ssr/{_0r-dsre._.js → _1_hwp06._.js} +2 -2
  113. package/.next/standalone/.next/server/chunks/ssr/{_1w8sola._.js → _1mnhi_r._.js} +2 -2
  114. package/.next/standalone/.next/server/chunks/ssr/{_1w_6p9g._.js → _1ror7k9._.js} +2 -2
  115. package/.next/standalone/.next/server/chunks/ssr/{_1gmzkwj._.js → _1sdgprl._.js} +1 -1
  116. package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
  117. package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
  118. package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
  119. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  120. package/.next/standalone/.next/server/pages/404.html +1 -1
  121. package/.next/standalone/.next/server/pages/500.html +1 -1
  122. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  123. package/.next/standalone/.next/server/server-reference-manifest.json +15 -15
  124. package/.next/standalone/.next/static/chunks/{0-m1vctj2jny6.js → 01s3vpzky_qew.js} +1 -1
  125. package/.next/standalone/.next/static/chunks/0km-jn9o5xyqs.css +2 -0
  126. package/.next/standalone/.next/static/chunks/{0on899o88y54u.js → 0zbcd_n8ku2ov.js} +1 -1
  127. package/.next/standalone/.next/static/chunks/{04y6igmx5olt6.js → 1erji0ratofla.js} +1 -1
  128. package/.next/standalone/.next/static/chunks/{06abmgw7cvecc.js → 23hnlzqx44vbe.js} +1 -1
  129. package/.next/standalone/.next/static/chunks/{1a9qlmo5s678i.js → 2cwsiyhc7d2cw.js} +1 -1
  130. package/.next/standalone/.next/static/chunks/{37nu_nos28w1x.js → 2y42ebzynyxdv.js} +1 -1
  131. package/.next/standalone/.next/static/chunks/{0ug1y_6-uwu5t.js → 315lncj7dsp9c.js} +1 -1
  132. package/.next/standalone/.next/static/chunks/31_px0dfgdg9j.js +6 -0
  133. package/.next/standalone/.next/static/chunks/{43ylh_2r15k-w.js → 3l4-fu3xt75j8.js} +1 -1
  134. package/.next/standalone/.next/static/chunks/3poscoxn69x9g.js +1 -0
  135. package/.next/standalone/app/policies/hooks-client.tsx +4 -4
  136. package/.next/standalone/ino.ujh3/.failproofai/VERSION +4 -0
  137. package/.next/standalone/ino.ujh3/.failproofai/state/last-version +1 -0
  138. package/.next/standalone/ino.ujh3/.failproofai/state/telemetry-id +1 -0
  139. package/.next/standalone/package.json +10 -10
  140. package/.next/standalone/server.js +1 -1
  141. package/bin/failproofai-worker.mjs +1 -1
  142. package/bin/failproofai.mjs +186 -5
  143. package/dist/cli.mjs +1130 -1352
  144. package/dist/worker.mjs +171 -1010
  145. package/package.json +10 -10
  146. package/src/hooks/backfill-cli.ts +1 -1
  147. package/src/hooks/cloud-connection.ts +4 -4
  148. package/src/hooks/cloud-enrollment-cli.ts +1 -1
  149. package/src/hooks/cloud-enrollment.ts +3 -3
  150. package/src/hooks/cloud-managed-policies.ts +60 -22
  151. package/src/hooks/configure-wizard.ts +124 -53
  152. package/src/hooks/custom-hooks-loader.ts +57 -8
  153. package/src/hooks/daemon-service.ts +66 -11
  154. package/src/hooks/flush-cli.ts +180 -0
  155. package/src/hooks/fp-config.ts +242 -118
  156. package/src/hooks/fp-home.ts +138 -22
  157. package/src/hooks/fp-reset.ts +357 -43
  158. package/src/hooks/handler.ts +11 -11
  159. package/src/hooks/harness-cli.ts +309 -0
  160. package/src/hooks/hook-activity-store.ts +5 -5
  161. package/src/hooks/manager.ts +35 -0
  162. package/src/hooks/onboarding-attempt.ts +22 -6
  163. package/src/hooks/tui.ts +64 -7
  164. package/src/hooks/uninstall-cli.ts +1 -1
  165. package/src/hooks/worker-server.ts +69 -12
  166. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06hexd0._.js +0 -3
  167. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1iny24d._.js +0 -3
  168. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1tyedjt._.js +0 -3
  169. package/.next/standalone/.next/server/chunks/lib_telemetry_ts_0dctyyw._.js +0 -5
  170. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1u-wx42._.js +0 -4
  171. package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-config_ts_04t589g._.js +0 -5
  172. package/.next/standalone/.next/static/chunks/0p_y4ndalc68e.js +0 -6
  173. package/.next/standalone/.next/static/chunks/13vrxdsdihqd_.css +0 -2
  174. package/.next/standalone/.next/static/chunks/19lrnqxn7ni7i.js +0 -1
  175. /package/.next/standalone/.next/static/{BZDo0aAHzDwh5ntxgIgpc → ziAsXfNE0cH_S7LHK-Py3}/_buildManifest.js +0 -0
  176. /package/.next/standalone/.next/static/{BZDo0aAHzDwh5ntxgIgpc → ziAsXfNE0cH_S7LHK-Py3}/_clientMiddlewareManifest.js +0 -0
  177. /package/.next/standalone/.next/static/{BZDo0aAHzDwh5ntxgIgpc → ziAsXfNE0cH_S7LHK-Py3}/_ssgManifest.js +0 -0
package/dist/cli.mjs CHANGED
@@ -19,7 +19,7 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
19
19
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
20
20
 
21
21
  // package.json
22
- var version2 = "1.0.0-beta.11", package_default;
22
+ var version2 = "1.0.0-beta.13", package_default;
23
23
  var init_package = __esm(() => {
24
24
  package_default = {
25
25
  name: "failproofai",
@@ -124,12 +124,12 @@ var init_package = __esm(() => {
124
124
  "html-to-image": "^1.11.13",
125
125
  html2canvas: "^1.4.1",
126
126
  "posthog-node": "^5.47.7",
127
- "smol-toml": "^1.7.1",
128
127
  "sql.js": "^1.14.1",
129
128
  yaml: "^2.9.0"
130
129
  },
131
130
  overrides: {
132
131
  postcss: "8.5.23",
132
+ nanoid: "3.3.17",
133
133
  "eslint-plugin-react-hooks": "7.0.1",
134
134
  vite: "8.0.16",
135
135
  undici: "7.29.0",
@@ -137,16 +137,16 @@ var init_package = __esm(() => {
137
137
  sharp: "0.35.0"
138
138
  },
139
139
  optionalDependencies: {
140
- "@failproofai/failproofaid-linux-x64": "1.0.0-beta.11",
141
- "@failproofai/failproofaid-linux-arm64": "1.0.0-beta.11",
142
- "@failproofai/failproofaid-darwin-x64": "1.0.0-beta.11",
143
- "@failproofai/failproofaid-darwin-arm64": "1.0.0-beta.11"
140
+ "@failproofai/failproofaid-linux-x64": "1.0.0-beta.13",
141
+ "@failproofai/failproofaid-linux-arm64": "1.0.0-beta.13",
142
+ "@failproofai/failproofaid-darwin-x64": "1.0.0-beta.13",
143
+ "@failproofai/failproofaid-darwin-arm64": "1.0.0-beta.13"
144
144
  },
145
145
  failproofaidBinaries: {
146
- "linux-x64": "e0621c8dd2b5f20e82d14e0a818832a065fb88b025da2a1769f2f81ee903ba89",
147
- "linux-arm64": "e15da1fab4d2655f587b5e8423e8b36058f839dbbbd5630fac5bad2b1a4a7ecf",
148
- "darwin-x64": "d54d493e905db563b33c14ad0d5cb0f16579cf2b9ec558ae0276855f323668f6",
149
- "darwin-arm64": "2b27fb6968d149dc9edf4d5f85d60df564dd13c3db011629e1789a487aa0a375"
146
+ "linux-x64": "b96dd035433adbf1290ad8b4ed32fc8134d4dfc03057b8f6a210c041ac7d8d93",
147
+ "linux-arm64": "0e11c7349de7d10488a6ea8f80714260e3dbedabcd06b0ab530119970b16404d",
148
+ "darwin-x64": "7d2943d6126430d9282ca106918be8aa2e0133c4c7189493996b14cbbb361d06",
149
+ "darwin-arm64": "dd6c646611ee81d7db88254917232bd5b0cfac514657bd2ce1062c7bb24cf960"
150
150
  }
151
151
  };
152
152
  });
@@ -154,971 +154,6 @@ var init_package = __esm(() => {
154
154
  // src/posthog-key.ts
155
155
  var POSTHOG_API_KEY = "phc_Ac1Ww1GqKc0z1SyrRWbmatEeQdlOQIsDEEdP8l8JRgX", POSTHOG_PRODUCT = "failproofai-oss";
156
156
 
157
- // node_modules/smol-toml/dist/date.js
158
- var DATE_TIME_RE, TomlDate;
159
- var init_date = __esm(() => {
160
- /*!
161
- * Copyright (c) Squirrel Chat et al., All rights reserved.
162
- * SPDX-License-Identifier: BSD-3-Clause
163
- *
164
- * Redistribution and use in source and binary forms, with or without
165
- * modification, are permitted provided that the following conditions are met:
166
- *
167
- * 1. Redistributions of source code must retain the above copyright notice, this
168
- * list of conditions and the following disclaimer.
169
- * 2. Redistributions in binary form must reproduce the above copyright notice,
170
- * this list of conditions and the following disclaimer in the
171
- * documentation and/or other materials provided with the distribution.
172
- * 3. Neither the name of the copyright holder nor the names of its contributors
173
- * may be used to endorse or promote products derived from this software without
174
- * specific prior written permission.
175
- *
176
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
177
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
178
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
179
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
180
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
181
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
182
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
183
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
184
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
185
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
186
- */
187
- DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[T ]?(?:(\d{2}):\d{2}(?::\d{2}(?:\.\d+)?)?)?(Z|[-+]\d{2}:\d{2})?$/i;
188
- TomlDate = class TomlDate extends Date {
189
- #hasDate = false;
190
- #hasTime = false;
191
- #offset = null;
192
- constructor(date) {
193
- let hasDate = true;
194
- let hasTime = true;
195
- let offset = "Z";
196
- if (typeof date === "string") {
197
- let match = date.match(DATE_TIME_RE);
198
- if (match) {
199
- if (!match[1]) {
200
- hasDate = false;
201
- date = `0000-01-01T${date}`;
202
- }
203
- hasTime = !!match[2];
204
- hasTime && date[10] === " " && (date = date.replace(" ", "T"));
205
- if (match[2] && +match[2] > 23) {
206
- date = "";
207
- } else {
208
- offset = match[3] || null;
209
- date = date.toUpperCase();
210
- if (!offset && hasTime)
211
- date += "Z";
212
- }
213
- } else {
214
- date = "";
215
- }
216
- }
217
- super(date);
218
- if (!isNaN(this.getTime())) {
219
- this.#hasDate = hasDate;
220
- this.#hasTime = hasTime;
221
- this.#offset = offset;
222
- }
223
- }
224
- isDateTime() {
225
- return this.#hasDate && this.#hasTime;
226
- }
227
- isLocal() {
228
- return !this.#hasDate || !this.#hasTime || !this.#offset;
229
- }
230
- isDate() {
231
- return this.#hasDate && !this.#hasTime;
232
- }
233
- isTime() {
234
- return this.#hasTime && !this.#hasDate;
235
- }
236
- isValid() {
237
- return this.#hasDate || this.#hasTime;
238
- }
239
- toISOString() {
240
- let iso = super.toISOString();
241
- if (this.isDate())
242
- return iso.slice(0, 10);
243
- if (this.isTime())
244
- return iso.slice(11, 23);
245
- if (this.#offset === null)
246
- return iso.slice(0, -1);
247
- if (this.#offset === "Z")
248
- return iso;
249
- let offset = +this.#offset.slice(1, 3) * 60 + +this.#offset.slice(4, 6);
250
- offset = this.#offset[0] === "-" ? offset : -offset;
251
- let offsetDate = new Date(this.getTime() - offset * 60000);
252
- return offsetDate.toISOString().slice(0, -1) + this.#offset;
253
- }
254
- static wrapAsOffsetDateTime(jsDate, offset = "Z") {
255
- let date = new TomlDate(jsDate);
256
- date.#offset = offset;
257
- return date;
258
- }
259
- static wrapAsLocalDateTime(jsDate) {
260
- let date = new TomlDate(jsDate);
261
- date.#offset = null;
262
- return date;
263
- }
264
- static wrapAsLocalDate(jsDate) {
265
- let date = new TomlDate(jsDate);
266
- date.#hasTime = false;
267
- date.#offset = null;
268
- return date;
269
- }
270
- static wrapAsLocalTime(jsDate) {
271
- let date = new TomlDate(jsDate);
272
- date.#hasDate = false;
273
- date.#offset = null;
274
- return date;
275
- }
276
- };
277
- });
278
-
279
- // node_modules/smol-toml/dist/error.js
280
- function getLineColFromPtr(string, ptr) {
281
- let lines = string.slice(0, ptr).split(/\r\n|\n|\r/g);
282
- return [lines.length, lines.pop().length + 1];
283
- }
284
- function makeCodeBlock(string, line, column) {
285
- let lines = string.split(/\r\n|\n|\r/g);
286
- let codeblock = "";
287
- let numberLen = (Math.log10(line + 1) | 0) + 1;
288
- for (let i = line - 1;i <= line + 1; i++) {
289
- let l = lines[i - 1];
290
- if (!l)
291
- continue;
292
- codeblock += i.toString().padEnd(numberLen, " ");
293
- codeblock += ": ";
294
- codeblock += l;
295
- codeblock += `
296
- `;
297
- if (i === line) {
298
- codeblock += " ".repeat(numberLen + column + 2);
299
- codeblock += `^
300
- `;
301
- }
302
- }
303
- return codeblock;
304
- }
305
- var TomlError;
306
- var init_error = __esm(() => {
307
- /*!
308
- * Copyright (c) Squirrel Chat et al., All rights reserved.
309
- * SPDX-License-Identifier: BSD-3-Clause
310
- *
311
- * Redistribution and use in source and binary forms, with or without
312
- * modification, are permitted provided that the following conditions are met:
313
- *
314
- * 1. Redistributions of source code must retain the above copyright notice, this
315
- * list of conditions and the following disclaimer.
316
- * 2. Redistributions in binary form must reproduce the above copyright notice,
317
- * this list of conditions and the following disclaimer in the
318
- * documentation and/or other materials provided with the distribution.
319
- * 3. Neither the name of the copyright holder nor the names of its contributors
320
- * may be used to endorse or promote products derived from this software without
321
- * specific prior written permission.
322
- *
323
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
324
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
325
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
326
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
327
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
328
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
329
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
330
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
331
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
332
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
333
- */
334
- TomlError = class TomlError extends Error {
335
- line;
336
- column;
337
- codeblock;
338
- constructor(message, options) {
339
- const [line, column] = getLineColFromPtr(options.toml, options.ptr);
340
- const codeblock = makeCodeBlock(options.toml, line, column);
341
- super(`Invalid TOML document: ${message}
342
-
343
- ${codeblock}`, options);
344
- this.line = line;
345
- this.column = column;
346
- this.codeblock = codeblock;
347
- }
348
- };
349
- });
350
-
351
- // node_modules/smol-toml/dist/primitive.js
352
- function parseString(str, ptr) {
353
- let c = str[ptr++];
354
- let first = c;
355
- let isLiteral = c === "'";
356
- let isMultiline = c === str[ptr] && c === str[ptr + 1];
357
- if (isMultiline) {
358
- if (str[ptr += 2] === `
359
- `)
360
- ptr++;
361
- else if (str[ptr] === "\r" && str[ptr + 1] === `
362
- `)
363
- ptr += 2;
364
- }
365
- let parsed = "";
366
- let sliceStart = ptr;
367
- let state = 0;
368
- for (let i = ptr;i < str.length; i++) {
369
- c = str[i];
370
- if (isMultiline && (c === `
371
- ` || c === "\r" && str[i + 1] === `
372
- `)) {
373
- state = state && 3;
374
- } else if (c < " " && c !== "\t" || c === "") {
375
- throw new TomlError("control characters are not allowed in strings", {
376
- toml: str,
377
- ptr: i
378
- });
379
- } else if ((!state || state === 3) && c === first && (!isMultiline || str[i + 1] === first && str[i + 2] === first)) {
380
- if (isMultiline) {
381
- if (str[i + 3] === first)
382
- i++;
383
- if (str[i + 3] === first)
384
- i++;
385
- }
386
- return [
387
- state ? parsed : parsed + str.slice(sliceStart, i),
388
- i + (isMultiline ? 3 : 1)
389
- ];
390
- } else if (!state) {
391
- if (!isLiteral && c === "\\") {
392
- parsed += str.slice(sliceStart, sliceStart = i);
393
- state = 1;
394
- }
395
- } else if (state === 1) {
396
- if (c === "x" || c === "u" || c === "U") {
397
- let value = 0;
398
- let len = c === "x" ? 2 : c === "u" ? 4 : 8;
399
- for (let j = 0;j < len; j++, i++) {
400
- let hex = str.charCodeAt(i + 1);
401
- let digit = hex >= 48 && hex <= 57 ? hex - 48 : hex >= 65 && hex <= 70 ? hex - 65 + 10 : hex >= 97 && hex <= 102 ? hex - 97 + 10 : -1;
402
- if (digit < 0)
403
- throw new TomlError("invalid non-hex character in unicode escape", { toml: str, ptr: i + 1 });
404
- value = value << 4 | digit;
405
- }
406
- if (value < 0 || value > 1114111 || value >= 55296 && value <= 57343) {
407
- throw new TomlError("invalid unicode escape", { toml: str, ptr: i });
408
- }
409
- parsed += String.fromCodePoint(value);
410
- sliceStart = i + 1;
411
- state = 0;
412
- } else if (c === " " || c === "\t") {
413
- state = 2;
414
- } else {
415
- if (c === "b")
416
- parsed += "\b";
417
- else if (c === "t")
418
- parsed += "\t";
419
- else if (c === "n")
420
- parsed += `
421
- `;
422
- else if (c === "f")
423
- parsed += "\f";
424
- else if (c === "r")
425
- parsed += "\r";
426
- else if (c === "e")
427
- parsed += "\x1B";
428
- else if (c === '"')
429
- parsed += '"';
430
- else if (c === "\\")
431
- parsed += "\\";
432
- else
433
- throw new TomlError("unrecognized escape sequence", { toml: str, ptr: i });
434
- sliceStart = i + 1;
435
- state = 0;
436
- }
437
- } else if (c !== " " && c !== "\t") {
438
- if (state === 2) {
439
- throw new TomlError("invalid escape: only line-ending whitespace may be escaped", {
440
- toml: str,
441
- ptr: sliceStart
442
- });
443
- }
444
- state = !isLiteral && c === "\\" ? 1 : 0;
445
- sliceStart = i;
446
- }
447
- }
448
- throw new TomlError("unfinished string", { toml: str, ptr });
449
- }
450
- function parseValue(value, toml, ptr, integersAsBigInt) {
451
- if (value === "true")
452
- return true;
453
- if (value === "false")
454
- return false;
455
- if (value === "-inf")
456
- return -Infinity;
457
- if (value === "inf" || value === "+inf")
458
- return Infinity;
459
- if (value === "nan" || value === "+nan" || value === "-nan")
460
- return NaN;
461
- if (value === "-0")
462
- return integersAsBigInt ? 0n : 0;
463
- let isInt = INT_REGEX.test(value);
464
- if (isInt || FLOAT_REGEX.test(value)) {
465
- if (LEADING_ZERO.test(value)) {
466
- throw new TomlError("leading zeroes are not allowed", {
467
- toml,
468
- ptr
469
- });
470
- }
471
- value = value.replace(/_/g, "");
472
- let numeric = +value;
473
- if (isNaN(numeric)) {
474
- throw new TomlError("invalid number", {
475
- toml,
476
- ptr
477
- });
478
- }
479
- if (isInt) {
480
- if ((isInt = !Number.isSafeInteger(numeric)) && !integersAsBigInt) {
481
- throw new TomlError("integer value cannot be represented losslessly", {
482
- toml,
483
- ptr
484
- });
485
- }
486
- if (isInt || integersAsBigInt === true)
487
- numeric = BigInt(value);
488
- }
489
- return numeric;
490
- }
491
- const date = new TomlDate(value);
492
- if (!date.isValid()) {
493
- throw new TomlError("invalid value", {
494
- toml,
495
- ptr
496
- });
497
- }
498
- return date;
499
- }
500
- var INT_REGEX, FLOAT_REGEX, LEADING_ZERO;
501
- var init_primitive = __esm(() => {
502
- init_date();
503
- init_error();
504
- /*!
505
- * Copyright (c) Squirrel Chat et al., All rights reserved.
506
- * SPDX-License-Identifier: BSD-3-Clause
507
- *
508
- * Redistribution and use in source and binary forms, with or without
509
- * modification, are permitted provided that the following conditions are met:
510
- *
511
- * 1. Redistributions of source code must retain the above copyright notice, this
512
- * list of conditions and the following disclaimer.
513
- * 2. Redistributions in binary form must reproduce the above copyright notice,
514
- * this list of conditions and the following disclaimer in the
515
- * documentation and/or other materials provided with the distribution.
516
- * 3. Neither the name of the copyright holder nor the names of its contributors
517
- * may be used to endorse or promote products derived from this software without
518
- * specific prior written permission.
519
- *
520
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
521
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
522
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
523
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
524
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
525
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
526
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
527
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
528
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
529
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
530
- */
531
- INT_REGEX = /^((0x[0-9a-fA-F](_?[0-9a-fA-F])*)|(([+-]|0[ob])?\d(_?\d)*))$/;
532
- FLOAT_REGEX = /^[+-]?\d(_?\d)*(\.\d(_?\d)*)?([eE][+-]?\d(_?\d)*)?$/;
533
- LEADING_ZERO = /^[+-]?0[0-9_]/;
534
- });
535
-
536
- // node_modules/smol-toml/dist/util.js
537
- function indexOfNewline(str, start = 0, end = str.length) {
538
- let idx = str.indexOf(`
539
- `, start);
540
- if (str[idx - 1] === "\r")
541
- idx--;
542
- return idx <= end ? idx : -1;
543
- }
544
- function skipComment(str, ptr) {
545
- for (let i = ptr;i < str.length; i++) {
546
- let c = str[i];
547
- if (c === `
548
- `)
549
- return i;
550
- if (c === "\r" && str[i + 1] === `
551
- `)
552
- return i + 1;
553
- if (c < " " && c !== "\t" || c === "") {
554
- throw new TomlError("control characters are not allowed in comments", {
555
- toml: str,
556
- ptr
557
- });
558
- }
559
- }
560
- return str.length;
561
- }
562
- function skipVoid(str, ptr, banNewLines, banComments) {
563
- let c;
564
- while (true) {
565
- while ((c = str[ptr]) === " " || c === "\t" || !banNewLines && (c === `
566
- ` || c === "\r" && str[ptr + 1] === `
567
- `))
568
- ptr++;
569
- if (banComments || c !== "#")
570
- break;
571
- ptr = skipComment(str, ptr);
572
- }
573
- return ptr;
574
- }
575
- function skipUntil(str, ptr, sep, end, banNewLines = false) {
576
- if (!end) {
577
- ptr = indexOfNewline(str, ptr);
578
- return ptr < 0 ? str.length : ptr;
579
- }
580
- for (let i = ptr;i < str.length; i++) {
581
- let c = str[i];
582
- if (c === "#") {
583
- i = indexOfNewline(str, i);
584
- if (i < 0)
585
- break;
586
- } else if (c === sep) {
587
- return i + 1;
588
- } else if (c === end || banNewLines && (c === `
589
- ` || c === "\r" && str[i + 1] === `
590
- `)) {
591
- return i;
592
- }
593
- }
594
- throw new TomlError("cannot find end of structure", {
595
- toml: str,
596
- ptr
597
- });
598
- }
599
- var init_util = __esm(() => {
600
- init_error();
601
- /*!
602
- * Copyright (c) Squirrel Chat et al., All rights reserved.
603
- * SPDX-License-Identifier: BSD-3-Clause
604
- *
605
- * Redistribution and use in source and binary forms, with or without
606
- * modification, are permitted provided that the following conditions are met:
607
- *
608
- * 1. Redistributions of source code must retain the above copyright notice, this
609
- * list of conditions and the following disclaimer.
610
- * 2. Redistributions in binary form must reproduce the above copyright notice,
611
- * this list of conditions and the following disclaimer in the
612
- * documentation and/or other materials provided with the distribution.
613
- * 3. Neither the name of the copyright holder nor the names of its contributors
614
- * may be used to endorse or promote products derived from this software without
615
- * specific prior written permission.
616
- *
617
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
618
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
619
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
620
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
621
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
622
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
623
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
624
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
625
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
626
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
627
- */
628
- });
629
-
630
- // node_modules/smol-toml/dist/extract.js
631
- function sliceAndTrimEndOf(str, startPtr, endPtr) {
632
- let value = str.slice(startPtr, endPtr);
633
- let commentIdx = value.indexOf("#");
634
- if (commentIdx > -1) {
635
- skipComment(str, commentIdx);
636
- value = value.slice(0, commentIdx);
637
- }
638
- return [value.trimEnd(), commentIdx];
639
- }
640
- function extractValue(str, ptr, end, depth, integersAsBigInt) {
641
- if (depth === 0) {
642
- throw new TomlError("document contains excessively nested structures. aborting.", {
643
- toml: str,
644
- ptr
645
- });
646
- }
647
- let c = str[ptr];
648
- if (c === "[" || c === "{") {
649
- let [value, endPtr2] = c === "[" ? parseArray(str, ptr, depth, integersAsBigInt) : parseInlineTable(str, ptr, depth, integersAsBigInt);
650
- if (end) {
651
- endPtr2 = skipVoid(str, endPtr2);
652
- if (str[endPtr2] === ",")
653
- endPtr2++;
654
- else if (str[endPtr2] !== end) {
655
- throw new TomlError("expected comma or end of structure", {
656
- toml: str,
657
- ptr: endPtr2
658
- });
659
- }
660
- }
661
- return [value, endPtr2];
662
- }
663
- if (c === '"' || c === "'") {
664
- let [parsed, endPtr2] = parseString(str, ptr);
665
- if (end) {
666
- endPtr2 = skipVoid(str, endPtr2);
667
- if (str[endPtr2] && str[endPtr2] !== "," && str[endPtr2] !== end && str[endPtr2] !== `
668
- ` && str[endPtr2] !== "\r") {
669
- throw new TomlError("unexpected character encountered", {
670
- toml: str,
671
- ptr: endPtr2
672
- });
673
- }
674
- if (str[endPtr2] === ",")
675
- endPtr2++;
676
- }
677
- return [parsed, endPtr2];
678
- }
679
- let endPtr = skipUntil(str, ptr, ",", end);
680
- let slice = sliceAndTrimEndOf(str, ptr, endPtr - (str[endPtr - 1] === "," ? 1 : 0));
681
- if (!slice[0]) {
682
- throw new TomlError("incomplete key-value declaration: no value specified", {
683
- toml: str,
684
- ptr
685
- });
686
- }
687
- if (end && slice[1] > -1) {
688
- endPtr = skipVoid(str, ptr + slice[1]);
689
- if (str[endPtr] === ",")
690
- endPtr++;
691
- }
692
- return [
693
- parseValue(slice[0], str, ptr, integersAsBigInt),
694
- endPtr
695
- ];
696
- }
697
- var init_extract = __esm(() => {
698
- init_primitive();
699
- init_struct();
700
- init_util();
701
- init_error();
702
- /*!
703
- * Copyright (c) Squirrel Chat et al., All rights reserved.
704
- * SPDX-License-Identifier: BSD-3-Clause
705
- *
706
- * Redistribution and use in source and binary forms, with or without
707
- * modification, are permitted provided that the following conditions are met:
708
- *
709
- * 1. Redistributions of source code must retain the above copyright notice, this
710
- * list of conditions and the following disclaimer.
711
- * 2. Redistributions in binary form must reproduce the above copyright notice,
712
- * this list of conditions and the following disclaimer in the
713
- * documentation and/or other materials provided with the distribution.
714
- * 3. Neither the name of the copyright holder nor the names of its contributors
715
- * may be used to endorse or promote products derived from this software without
716
- * specific prior written permission.
717
- *
718
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
719
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
720
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
721
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
722
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
723
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
724
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
725
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
726
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
727
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
728
- */
729
- });
730
-
731
- // node_modules/smol-toml/dist/struct.js
732
- function parseKey(str, ptr, end = "=") {
733
- let dot = ptr - 1;
734
- let parsed = [];
735
- let endPtr = str.indexOf(end, ptr);
736
- if (endPtr < 0) {
737
- throw new TomlError("incomplete key-value: cannot find end of key", {
738
- toml: str,
739
- ptr
740
- });
741
- }
742
- do {
743
- let c = str[ptr = ++dot];
744
- if (c !== " " && c !== "\t") {
745
- if (c === '"' || c === "'") {
746
- if (c === str[ptr + 1] && c === str[ptr + 2]) {
747
- throw new TomlError("multiline strings are not allowed in keys", {
748
- toml: str,
749
- ptr
750
- });
751
- }
752
- let [part, eos] = parseString(str, ptr);
753
- dot = str.indexOf(".", eos);
754
- let strEnd = str.slice(eos, dot < 0 || dot > endPtr ? endPtr : dot);
755
- let newLine = indexOfNewline(strEnd);
756
- if (newLine > -1) {
757
- throw new TomlError("newlines are not allowed in keys", {
758
- toml: str,
759
- ptr: ptr + dot + newLine
760
- });
761
- }
762
- if (strEnd.trimStart()) {
763
- throw new TomlError("found extra tokens after the string part", {
764
- toml: str,
765
- ptr: eos
766
- });
767
- }
768
- if (endPtr < eos) {
769
- endPtr = str.indexOf(end, eos);
770
- if (endPtr < 0) {
771
- throw new TomlError("incomplete key-value: cannot find end of key", {
772
- toml: str,
773
- ptr
774
- });
775
- }
776
- }
777
- parsed.push(part);
778
- } else {
779
- dot = str.indexOf(".", ptr);
780
- let part = str.slice(ptr, dot < 0 || dot > endPtr ? endPtr : dot);
781
- if (!KEY_PART_RE.test(part)) {
782
- throw new TomlError("only letter, numbers, dashes and underscores are allowed in keys", {
783
- toml: str,
784
- ptr
785
- });
786
- }
787
- parsed.push(part.trimEnd());
788
- }
789
- }
790
- } while (dot + 1 && dot < endPtr);
791
- return [parsed, skipVoid(str, endPtr + 1, true, true)];
792
- }
793
- function parseInlineTable(str, ptr, depth, integersAsBigInt) {
794
- let res = {};
795
- let seen = new Set;
796
- let c;
797
- ptr++;
798
- while ((c = str[ptr++]) !== "}" && c) {
799
- if (c === ",") {
800
- throw new TomlError("expected value, found comma", {
801
- toml: str,
802
- ptr: ptr - 1
803
- });
804
- } else if (c === "#")
805
- ptr = skipComment(str, ptr);
806
- else if (c !== " " && c !== "\t" && c !== `
807
- ` && c !== "\r") {
808
- let k;
809
- let t = res;
810
- let hasOwn = false;
811
- let [key, keyEndPtr] = parseKey(str, ptr - 1);
812
- for (let i = 0;i < key.length; i++) {
813
- if (i)
814
- t = hasOwn ? t[k] : t[k] = {};
815
- k = key[i];
816
- if ((hasOwn = Object.hasOwn(t, k)) && (typeof t[k] !== "object" || seen.has(t[k]))) {
817
- throw new TomlError("trying to redefine an already defined value", {
818
- toml: str,
819
- ptr
820
- });
821
- }
822
- if (!hasOwn && k === "__proto__") {
823
- Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
824
- }
825
- }
826
- if (hasOwn) {
827
- throw new TomlError("trying to redefine an already defined value", {
828
- toml: str,
829
- ptr
830
- });
831
- }
832
- let [value, valueEndPtr] = extractValue(str, keyEndPtr, "}", depth - 1, integersAsBigInt);
833
- seen.add(value);
834
- t[k] = value;
835
- ptr = valueEndPtr;
836
- }
837
- }
838
- if (!c) {
839
- throw new TomlError("unfinished table encountered", {
840
- toml: str,
841
- ptr
842
- });
843
- }
844
- return [res, ptr];
845
- }
846
- function parseArray(str, ptr, depth, integersAsBigInt) {
847
- let res = [];
848
- let c;
849
- ptr++;
850
- while ((c = str[ptr++]) !== "]" && c) {
851
- if (c === ",") {
852
- throw new TomlError("expected value, found comma", {
853
- toml: str,
854
- ptr: ptr - 1
855
- });
856
- } else if (c === "#")
857
- ptr = skipComment(str, ptr);
858
- else if (c !== " " && c !== "\t" && c !== `
859
- ` && c !== "\r") {
860
- let e = extractValue(str, ptr - 1, "]", depth - 1, integersAsBigInt);
861
- res.push(e[0]);
862
- ptr = e[1];
863
- }
864
- }
865
- if (!c) {
866
- throw new TomlError("unfinished array encountered", {
867
- toml: str,
868
- ptr
869
- });
870
- }
871
- return [res, ptr];
872
- }
873
- var KEY_PART_RE;
874
- var init_struct = __esm(() => {
875
- init_primitive();
876
- init_extract();
877
- init_util();
878
- init_error();
879
- /*!
880
- * Copyright (c) Squirrel Chat et al., All rights reserved.
881
- * SPDX-License-Identifier: BSD-3-Clause
882
- *
883
- * Redistribution and use in source and binary forms, with or without
884
- * modification, are permitted provided that the following conditions are met:
885
- *
886
- * 1. Redistributions of source code must retain the above copyright notice, this
887
- * list of conditions and the following disclaimer.
888
- * 2. Redistributions in binary form must reproduce the above copyright notice,
889
- * this list of conditions and the following disclaimer in the
890
- * documentation and/or other materials provided with the distribution.
891
- * 3. Neither the name of the copyright holder nor the names of its contributors
892
- * may be used to endorse or promote products derived from this software without
893
- * specific prior written permission.
894
- *
895
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
896
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
897
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
898
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
899
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
900
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
901
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
902
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
903
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
904
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
905
- */
906
- KEY_PART_RE = /^[a-zA-Z0-9-_]+[ \t]*$/;
907
- });
908
-
909
- // node_modules/smol-toml/dist/parse.js
910
- function peekTable(key, table, meta, type) {
911
- let t = table;
912
- let m = meta;
913
- let k;
914
- let hasOwn = false;
915
- let state;
916
- for (let i = 0;i < key.length; i++) {
917
- if (i) {
918
- t = hasOwn ? t[k] : t[k] = {};
919
- m = (state = m[k]).c;
920
- if (type === 0 && (state.t === 1 || state.t === 2)) {
921
- return null;
922
- }
923
- if (state.t === 2) {
924
- let l = t.length - 1;
925
- t = t[l];
926
- m = m[l].c;
927
- }
928
- }
929
- k = key[i];
930
- if ((hasOwn = Object.hasOwn(t, k)) && m[k]?.t === 0 && m[k]?.d) {
931
- return null;
932
- }
933
- if (!hasOwn) {
934
- if (k === "__proto__") {
935
- Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
936
- Object.defineProperty(m, k, { enumerable: true, configurable: true, writable: true });
937
- }
938
- m[k] = {
939
- t: i < key.length - 1 && type === 2 ? 3 : type,
940
- d: false,
941
- i: 0,
942
- c: {}
943
- };
944
- }
945
- }
946
- state = m[k];
947
- if (state.t !== type && !(type === 1 && state.t === 3)) {
948
- return null;
949
- }
950
- if (type === 2) {
951
- if (!state.d) {
952
- state.d = true;
953
- t[k] = [];
954
- }
955
- t[k].push(t = {});
956
- state.c[state.i++] = state = { t: 1, d: false, i: 0, c: {} };
957
- }
958
- if (state.d) {
959
- return null;
960
- }
961
- state.d = true;
962
- if (type === 1) {
963
- t = hasOwn ? t[k] : t[k] = {};
964
- } else if (type === 0 && hasOwn) {
965
- return null;
966
- }
967
- return [k, t, state.c];
968
- }
969
- function parse(toml, { maxDepth = 1000, integersAsBigInt } = {}) {
970
- let res = {};
971
- let meta = {};
972
- let tbl = res;
973
- let m = meta;
974
- for (let ptr = skipVoid(toml, 0);ptr < toml.length; ) {
975
- if (toml[ptr] === "[") {
976
- let isTableArray = toml[++ptr] === "[";
977
- let k = parseKey(toml, ptr += +isTableArray, "]");
978
- if (isTableArray) {
979
- if (toml[k[1] - 1] !== "]") {
980
- throw new TomlError("expected end of table declaration", {
981
- toml,
982
- ptr: k[1] - 1
983
- });
984
- }
985
- k[1]++;
986
- }
987
- let p = peekTable(k[0], res, meta, isTableArray ? 2 : 1);
988
- if (!p) {
989
- throw new TomlError("trying to redefine an already defined table or value", {
990
- toml,
991
- ptr
992
- });
993
- }
994
- m = p[2];
995
- tbl = p[1];
996
- ptr = k[1];
997
- } else {
998
- let k = parseKey(toml, ptr);
999
- let p = peekTable(k[0], tbl, m, 0);
1000
- if (!p) {
1001
- throw new TomlError("trying to redefine an already defined table or value", {
1002
- toml,
1003
- ptr
1004
- });
1005
- }
1006
- let v = extractValue(toml, k[1], undefined, maxDepth, integersAsBigInt);
1007
- p[1][p[0]] = v[0];
1008
- ptr = v[1];
1009
- }
1010
- ptr = skipVoid(toml, ptr, true);
1011
- if (toml[ptr] && toml[ptr] !== `
1012
- ` && toml[ptr] !== "\r") {
1013
- throw new TomlError("each key-value declaration must be followed by an end-of-line", {
1014
- toml,
1015
- ptr
1016
- });
1017
- }
1018
- ptr = skipVoid(toml, ptr);
1019
- }
1020
- return res;
1021
- }
1022
- var init_parse = __esm(() => {
1023
- init_struct();
1024
- init_extract();
1025
- init_util();
1026
- init_error();
1027
- /*!
1028
- * Copyright (c) Squirrel Chat et al., All rights reserved.
1029
- * SPDX-License-Identifier: BSD-3-Clause
1030
- *
1031
- * Redistribution and use in source and binary forms, with or without
1032
- * modification, are permitted provided that the following conditions are met:
1033
- *
1034
- * 1. Redistributions of source code must retain the above copyright notice, this
1035
- * list of conditions and the following disclaimer.
1036
- * 2. Redistributions in binary form must reproduce the above copyright notice,
1037
- * this list of conditions and the following disclaimer in the
1038
- * documentation and/or other materials provided with the distribution.
1039
- * 3. Neither the name of the copyright holder nor the names of its contributors
1040
- * may be used to endorse or promote products derived from this software without
1041
- * specific prior written permission.
1042
- *
1043
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
1044
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1045
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1046
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
1047
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1048
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
1049
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
1050
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
1051
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1052
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1053
- */
1054
- });
1055
-
1056
- // node_modules/smol-toml/dist/stringify.js
1057
- var init_stringify = __esm(() => {
1058
- /*!
1059
- * Copyright (c) Squirrel Chat et al., All rights reserved.
1060
- * SPDX-License-Identifier: BSD-3-Clause
1061
- *
1062
- * Redistribution and use in source and binary forms, with or without
1063
- * modification, are permitted provided that the following conditions are met:
1064
- *
1065
- * 1. Redistributions of source code must retain the above copyright notice, this
1066
- * list of conditions and the following disclaimer.
1067
- * 2. Redistributions in binary form must reproduce the above copyright notice,
1068
- * this list of conditions and the following disclaimer in the
1069
- * documentation and/or other materials provided with the distribution.
1070
- * 3. Neither the name of the copyright holder nor the names of its contributors
1071
- * may be used to endorse or promote products derived from this software without
1072
- * specific prior written permission.
1073
- *
1074
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
1075
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1076
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1077
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
1078
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1079
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
1080
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
1081
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
1082
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1083
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1084
- */
1085
- });
1086
-
1087
- // node_modules/smol-toml/dist/index.js
1088
- var init_dist = __esm(() => {
1089
- init_parse();
1090
- init_stringify();
1091
- init_date();
1092
- init_error();
1093
- /*!
1094
- * Copyright (c) Squirrel Chat et al., All rights reserved.
1095
- * SPDX-License-Identifier: BSD-3-Clause
1096
- *
1097
- * Redistribution and use in source and binary forms, with or without
1098
- * modification, are permitted provided that the following conditions are met:
1099
- *
1100
- * 1. Redistributions of source code must retain the above copyright notice, this
1101
- * list of conditions and the following disclaimer.
1102
- * 2. Redistributions in binary form must reproduce the above copyright notice,
1103
- * this list of conditions and the following disclaimer in the
1104
- * documentation and/or other materials provided with the distribution.
1105
- * 3. Neither the name of the copyright holder nor the names of its contributors
1106
- * may be used to endorse or promote products derived from this software without
1107
- * specific prior written permission.
1108
- *
1109
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
1110
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1111
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1112
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
1113
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1114
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
1115
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
1116
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
1117
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1118
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1119
- */
1120
- });
1121
-
1122
157
  // src/hooks/fp-home.ts
1123
158
  import { homedir } from "node:os";
1124
159
  import { resolve } from "node:path";
@@ -1143,7 +178,10 @@ function resettablePaths() {
1143
178
  legacy.collectorHealth(),
1144
179
  legacy.onboardingLock(),
1145
180
  legacy.cloudManagedPolicies(),
1146
- localPoliciesDir(),
181
+ legacy.configToml(),
182
+ legacy.credentialsToml(),
183
+ legacy.localPoliciesDir(),
184
+ globalPolicyConfigFile(),
1147
185
  cloudPoliciesDir(),
1148
186
  at("state"),
1149
187
  configFile(),
@@ -1153,10 +191,15 @@ function resettablePaths() {
1153
191
  logsDir()
1154
192
  ];
1155
193
  }
1156
- var LAYOUT_VERSION = 2, at = (...parts) => resolve(failproofaiHome(), ...parts), atHome = (home, ...parts) => home ? resolve(failproofaiHome(home), ...parts) : at(...parts), versionFile = (home) => atHome(home, "VERSION"), configFile = (home) => atHome(home, "config.toml"), credentialsFile = (home) => atHome(home, "credentials.toml"), binDir = (home) => atHome(home, "bin"), policiesDir = (home) => atHome(home, "policies"), localPoliciesDir = (home) => atHome(home, "policies", "local-policies"), globalPolicyConfigFile = (home) => resolve(localPoliciesDir(home), "policies-config.json"), cloudPoliciesDir = (home) => resolve(policiesDir(home), "cloud-policies"), customPoliciesDir = (home) => resolve(policiesDir(home), "custom-policies"), customAgentsDir = (home) => atHome(home, "custom-agents"), auditDir = (home) => atHome(home, "audit"), auditDashboardFile = (home) => resolve(auditDir(home), "dashboard.json"), auditCacheDir = (home) => resolve(auditDir(home), "cache"), hookActivityDir = (home) => atHome(home, "hook-activity"), runDir = (home) => atHome(home, "run"), daemonSocket = () => process.env.FAILPROOFAI_DAEMON_SOCKET || resolve(runDir(), "failproofaid.sock"), auditLockFile = (home) => resolve(runDir(home), "audit.lock"), stateDir = (home) => atHome(home, "state"), shimsDir = (home) => resolve(stateDir(home), "shims"), sessionPauseDir = () => resolve(stateDir(), "sessions"), telemetryIdFile = (home) => resolve(stateDir(home), "telemetry-id"), launcherMarker = (home) => resolve(stateDir(home), "launcher-configured"), onboardingAttemptFile = (home) => resolve(stateDir(home), "onboarding-attempt.json"), lastVersionFile = (home) => resolve(stateDir(home), "last-version"), onboardingLockFile = (home) => resolve(stateDir(home), "onboarding.lock"), codexSessionPathsFile = (home) => resolve(stateDir(home), "codex-session-paths.json"), logsDir = (home) => atHome(home, "logs"), legacy;
194
+ var LAYOUT_VERSION = 3, at = (...parts) => resolve(failproofaiHome(), ...parts), atHome = (home, ...parts) => home ? resolve(failproofaiHome(home), ...parts) : at(...parts), versionFile = (home) => atHome(home, "VERSION"), configFile = (home) => atHome(home, "config.json"), credentialsFile = (home) => atHome(home, "credentials.json"), binDir = (home) => atHome(home, "bin"), policiesDir = (home) => atHome(home, "policies"), customPoliciesDir, globalPolicyConfigFile = (home) => atHome(home, "policies-config.json"), cloudPoliciesDir = (home) => resolve(policiesDir(home), "cloud-policies"), customAgentsDir = (home) => atHome(home, "custom-agents"), auditDir = (home) => atHome(home, "audit"), auditDashboardFile = (home) => resolve(auditDir(home), "dashboard.json"), auditCacheDir = (home) => resolve(auditDir(home), "cache"), hookActivityDir = (home) => atHome(home, "hook-activity"), runDir = (home) => atHome(home, "run"), daemonSocket = () => process.env.FAILPROOFAI_DAEMON_SOCKET || resolve(runDir(), "failproofaid.sock"), auditLockFile = (home) => resolve(runDir(home), "audit.lock"), stateDir = (home) => atHome(home, "state"), shimsDir = (home) => resolve(stateDir(home), "shims"), spoolDir = (home) => resolve(stateDir(home), "spool"), sessionPauseDir = () => resolve(stateDir(), "sessions"), telemetryIdFile = (home) => resolve(stateDir(home), "telemetry-id"), launcherMarker = (home) => resolve(stateDir(home), "launcher-configured"), onboardingAttemptFile = (home) => resolve(stateDir(home), "onboarding-attempt.json"), lastVersionFile = (home) => resolve(stateDir(home), "last-version"), onboardingLockFile = (home) => resolve(stateDir(home), "onboarding.lock"), codexSessionPathsFile = (home) => resolve(stateDir(home), "codex-session-paths.json"), logsDir = (home) => atHome(home, "logs"), legacy;
1157
195
  var init_fp_home = __esm(() => {
196
+ customPoliciesDir = policiesDir;
1158
197
  legacy = {
1159
198
  policyConfig: () => at("policies-config.json"),
199
+ configToml: () => at("config.toml"),
200
+ credentialsToml: () => at("credentials.toml"),
201
+ localPoliciesDir: () => at("policies", "local-policies"),
202
+ customPoliciesDir: () => at("policies", "custom-policies"),
1160
203
  policyConfigLocal: () => at("policies-config.local.json"),
1161
204
  cloudCredentials: () => at("cloud.json"),
1162
205
  ingestCredentials: () => at("ingest.json"),
@@ -1186,6 +229,11 @@ function detectLayout() {
1186
229
  return { kind: "current", version: raw };
1187
230
  return raw.layout > LAYOUT_VERSION ? { kind: "future", found: raw.layout } : { kind: "stale", found: raw.layout };
1188
231
  }
232
+ if (existsSync(configFile())) {
233
+ return { kind: "current", version: { layout: LAYOUT_VERSION, cli: "" }, inferred: true };
234
+ }
235
+ if (existsSync(legacy.configToml()))
236
+ return { kind: "stale", found: LAYOUT_VERSION - 1 };
1189
237
  const layoutOneMarkers = [
1190
238
  legacy.policyConfig(),
1191
239
  legacy.cacheDir(),
@@ -1197,9 +245,22 @@ function detectLayout() {
1197
245
  ];
1198
246
  return layoutOneMarkers.some((p) => existsSync(p)) ? { kind: "stale", found: 1 } : { kind: "absent" };
1199
247
  }
248
+ function readLegacyTomlVersion(text) {
249
+ const layout = Number(/^\s*layout\s*=\s*(\d+)\s*$/m.exec(text)?.[1]);
250
+ if (!Number.isFinite(layout))
251
+ return null;
252
+ const str = (key) => new RegExp(`^\\s*${key}\\s*=\\s*"([^"]*)"`, "m").exec(text)?.[1];
253
+ return { layout, cli: str("cli") ?? "", daemon: str("daemon") };
254
+ }
1200
255
  function readVersionFile() {
256
+ let text;
257
+ try {
258
+ text = readFileSync(versionFile(), "utf8");
259
+ } catch {
260
+ return null;
261
+ }
1201
262
  try {
1202
- const parsed = parse(readFileSync(versionFile(), "utf8"));
263
+ const parsed = JSON.parse(text);
1203
264
  const layout = Number(parsed.layout);
1204
265
  if (!Number.isFinite(layout))
1205
266
  return null;
@@ -1209,7 +270,7 @@ function readVersionFile() {
1209
270
  daemon: typeof parsed.daemon === "string" ? parsed.daemon : undefined
1210
271
  };
1211
272
  } catch {
1212
- return null;
273
+ return readLegacyTomlVersion(text);
1213
274
  }
1214
275
  }
1215
276
  function writeVersionFile(v = {}) {
@@ -1219,16 +280,7 @@ function writeVersionFile(v = {}) {
1219
280
  cli: v.cli ?? version2,
1220
281
  daemon: v.clearDaemon ? undefined : v.daemon ?? existing?.daemon
1221
282
  };
1222
- const lines = [
1223
- "# Written by failproofai. `layout` is what tells a newer CLI whether this",
1224
- "# directory can be read as-is; do not edit it by hand.",
1225
- `layout = ${next.layout}`,
1226
- `cli = ${JSON.stringify(next.cli)}`
1227
- ];
1228
- if (next.daemon)
1229
- lines.push(`daemon = ${JSON.stringify(next.daemon)}`);
1230
- writeFileAt(versionFile(), lines.join(`
1231
- `) + `
283
+ writeFileAt(versionFile(), `${JSON.stringify({ layout: next.layout, cli: next.cli, ...next.daemon ? { daemon: next.daemon } : {} }, null, 2)}
1232
284
  `);
1233
285
  }
1234
286
  function readIntervalDays(raw) {
@@ -1239,9 +291,25 @@ function readIntervalDays(raw) {
1239
291
  return DEFAULT_AUDIT_INTERVAL_DAYS;
1240
292
  return Math.min(days, MAX_AUDIT_INTERVAL_DAYS);
1241
293
  }
294
+ function readSources(raw) {
295
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
296
+ return;
297
+ const out = {};
298
+ for (const [name, table] of Object.entries(raw)) {
299
+ if (!table || typeof table !== "object" || Array.isArray(table))
300
+ continue;
301
+ const entries = table.extra_paths;
302
+ if (!Array.isArray(entries))
303
+ continue;
304
+ const paths = entries.filter((e) => typeof e === "string" && e.trim() !== "");
305
+ if (paths.length > 0)
306
+ out[name] = { extraPaths: paths };
307
+ }
308
+ return Object.keys(out).length > 0 ? out : undefined;
309
+ }
1242
310
  function readConfig() {
1243
311
  try {
1244
- const parsed = parse(readFileSync(configFile(), "utf8"));
312
+ const parsed = JSON.parse(readFileSync(configFile(), "utf8"));
1245
313
  const mode = parsed.mode?.kind;
1246
314
  const daemon = parsed.daemon ?? {};
1247
315
  const collector = parsed.collector ?? {};
@@ -1256,7 +324,8 @@ function readConfig() {
1256
324
  hooksVerbosity: collector.hooks_verbosity === "all" || collector.hooks_verbosity === "off" ? collector.hooks_verbosity : "decisions",
1257
325
  redact: collector.redact === "off" ? "off" : "minimal",
1258
326
  environment: typeof collector.environment === "string" ? collector.environment : "local",
1259
- machineId: typeof collector.machine_id === "string" ? collector.machine_id : undefined
327
+ machineId: typeof collector.machine_id === "string" ? collector.machine_id : undefined,
328
+ sources: readSources(collector.sources)
1260
329
  },
1261
330
  telemetry: { enabled: telemetry.enabled !== false },
1262
331
  audit: { auto: audit.auto === true, intervalDays: readIntervalDays(audit.interval_days) }
@@ -1267,31 +336,24 @@ function readConfig() {
1267
336
  }
1268
337
  function writeConfig(config) {
1269
338
  const c = config.collector;
1270
- const lines = [
1271
- "# failproofai configuration. Safe to edit by hand.",
1272
- "# Credentials are NOT here — see credentials.toml (owner-only).",
1273
- "",
1274
- "[mode]",
1275
- '# "oss" — fully local. No transcripts, hook activity or policy leave',
1276
- "# this machine.",
1277
- '# "cloud" — reports to, and receives policy from, Failproof Cloud.',
1278
- `kind = ${JSON.stringify(config.mode)}`,
1279
- "",
1280
- "[daemon]",
1281
- "# When true, hooks route through failproofaid AND FAIL CLOSED if it is",
1282
- "# unreachable. Never set this by hand: `failproofai config` sets it only",
1283
- "# after verifying the service is genuinely running.",
1284
- `configured = ${config.daemon.configured}`
1285
- ];
1286
- lines.push("", "[collector]", "# Session transcripts carry prompts, file contents and command output.", `sessions = ${c.sessions}`, "# Hook decisions: which policy fired and what it decided. No file contents.", `hooks = ${c.hooks}`, `hooks_verbosity = ${JSON.stringify(c.hooksVerbosity)}`, `redact = ${JSON.stringify(c.redact)}`, `environment = ${JSON.stringify(c.environment)}`);
1287
- if (c.machineId)
1288
- lines.push(`machine_id = ${JSON.stringify(c.machineId)}`);
1289
- if (!config.telemetry.enabled) {
1290
- lines.push("", "[telemetry]", "enabled = false");
1291
- }
1292
- lines.push("", "[audit]", "# Scan this machine's agent history on a schedule and refresh the audit", "# dashboard. OFF by default: the scan reads the CONTENTS of every session", "# transcript on disk, so it waits to be asked. It runs as a separate", "# short-lived process — never on the hook path.", `auto = ${config.audit.auto}`, `interval_days = ${config.audit.intervalDays}`);
1293
- writeFileAt(configFile(), lines.join(`
1294
- `) + `
339
+ const out = {
340
+ mode: { kind: config.mode },
341
+ daemon: { configured: config.daemon.configured },
342
+ collector: {
343
+ sessions: c.sessions,
344
+ hooks: c.hooks,
345
+ hooks_verbosity: c.hooksVerbosity,
346
+ redact: c.redact,
347
+ environment: c.environment,
348
+ ...c.machineId ? { machine_id: c.machineId } : {},
349
+ ...Object.entries(c.sources ?? {}).some(([, s]) => s.extraPaths?.length) ? {
350
+ sources: Object.fromEntries(Object.entries(c.sources ?? {}).filter(([, s]) => s.extraPaths?.length).map(([name, s]) => [name, { extra_paths: s.extraPaths }]))
351
+ } : {}
352
+ },
353
+ ...config.telemetry.enabled ? {} : { telemetry: { enabled: false } },
354
+ audit: { auto: config.audit.auto, interval_days: config.audit.intervalDays }
355
+ };
356
+ writeFileAt(configFile(), `${JSON.stringify(out, null, 2)}
1295
357
  `);
1296
358
  }
1297
359
  function updateConfig(patch) {
@@ -1308,7 +370,7 @@ function updateConfig(patch) {
1308
370
  }
1309
371
  function readCredentials() {
1310
372
  try {
1311
- const parsed = parse(readFileSync(credentialsFile(), "utf8"));
373
+ const parsed = JSON.parse(readFileSync(credentialsFile(), "utf8"));
1312
374
  const cloud = parsed.cloud;
1313
375
  const ingest = parsed.ingest;
1314
376
  const auth = parsed.auth;
@@ -1341,34 +403,32 @@ function readCredentials() {
1341
403
  }
1342
404
  }
1343
405
  function writeCredentials(creds) {
1344
- const lines = [
1345
- "# failproofai credentials — owner-only (0600). Do not commit."
1346
- ];
406
+ const out = {};
1347
407
  if (creds.cloud) {
1348
- lines.push("", "[cloud]", `url = ${JSON.stringify(creds.cloud.url)}`, `machine_id = ${JSON.stringify(creds.cloud.machineId)}`, ...creds.cloud.machineLabel ? [`machine_label = ${JSON.stringify(creds.cloud.machineLabel)}`] : [], `token = ${JSON.stringify(creds.cloud.token)}`);
408
+ out.cloud = {
409
+ url: creds.cloud.url,
410
+ machine_id: creds.cloud.machineId,
411
+ ...creds.cloud.machineLabel ? { machine_label: creds.cloud.machineLabel } : {},
412
+ token: creds.cloud.token
413
+ };
1349
414
  }
1350
415
  if (creds.ingest) {
1351
- lines.push("", "[ingest]", `url = ${JSON.stringify(creds.ingest.url)}`, `key = ${JSON.stringify(creds.ingest.key)}`);
416
+ out.ingest = { url: creds.ingest.url, key: creds.ingest.key };
1352
417
  }
1353
418
  if (creds.org && (creds.org.id || creds.org.slug || creds.org.name)) {
1354
- lines.push("", "[org]");
1355
- if (creds.org.id)
1356
- lines.push(`id = ${JSON.stringify(creds.org.id)}`);
1357
- if (creds.org.slug)
1358
- lines.push(`slug = ${JSON.stringify(creds.org.slug)}`);
1359
- if (creds.org.name)
1360
- lines.push(`name = ${JSON.stringify(creds.org.name)}`);
419
+ out.org = {
420
+ ...creds.org.id ? { id: creds.org.id } : {},
421
+ ...creds.org.slug ? { slug: creds.org.slug } : {},
422
+ ...creds.org.name ? { name: creds.org.name } : {}
423
+ };
1361
424
  }
1362
425
  if (creds.auth && (creds.auth.sessionToken || creds.auth.email)) {
1363
- lines.push("", "[auth]");
1364
- if (creds.auth.baseUrl)
1365
- lines.push(`base_url = ${JSON.stringify(creds.auth.baseUrl)}`);
1366
- if (creds.auth.sessionToken)
1367
- lines.push(`session_token = ${JSON.stringify(creds.auth.sessionToken)}`);
1368
- if (creds.auth.expiresAt)
1369
- lines.push(`expires_at = ${creds.auth.expiresAt}`);
1370
- if (creds.auth.email)
1371
- lines.push(`email = ${JSON.stringify(creds.auth.email)}`);
426
+ out.auth = {
427
+ ...creds.auth.baseUrl ? { base_url: creds.auth.baseUrl } : {},
428
+ ...creds.auth.sessionToken ? { session_token: creds.auth.sessionToken } : {},
429
+ ...creds.auth.expiresAt ? { expires_at: creds.auth.expiresAt } : {},
430
+ ...creds.auth.email ? { email: creds.auth.email } : {}
431
+ };
1372
432
  }
1373
433
  const home = failproofaiHome();
1374
434
  mkdirSync(home, { recursive: true });
@@ -1376,8 +436,7 @@ function writeCredentials(creds) {
1376
436
  if (statSync(home).mode & 63)
1377
437
  chmodSync(home, 448);
1378
438
  } catch {}
1379
- writeFileSync(credentialsFile(), lines.join(`
1380
- `) + `
439
+ writeFileSync(credentialsFile(), `${JSON.stringify(out, null, 2)}
1381
440
  `, { mode: 384 });
1382
441
  try {
1383
442
  chmodSync(credentialsFile(), 384);
@@ -1389,7 +448,6 @@ function writeFileAt(path, contents) {
1389
448
  }
1390
449
  var DEFAULT_AUDIT_INTERVAL_DAYS = 7, MIN_AUDIT_INTERVAL_DAYS = 1, MAX_AUDIT_INTERVAL_DAYS = 90, DEFAULT_CONFIG;
1391
450
  var init_fp_config = __esm(() => {
1392
- init_dist();
1393
451
  init_package();
1394
452
  init_fp_home();
1395
453
  DEFAULT_CONFIG = {
@@ -5631,7 +4689,19 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
5631
4689
  const projectRoot = findProjectConfigDir(opts?.sessionCwd ?? process.cwd());
5632
4690
  const conventionEnabled = opts?.customPoliciesEnabled !== false;
5633
4691
  const loadedPaths = new Set;
5634
- const cloudManagedByPath = new Map((opts?.cloudManagedPolicies ?? []).map((policy) => [resolve5(policy.path), policy]));
4692
+ const cloudManagedByPath = new Map;
4693
+ for (const policy of opts?.cloudManagedPolicies ?? []) {
4694
+ const key = resolve5(policy.path);
4695
+ const existing = cloudManagedByPath.get(key);
4696
+ if (!existing) {
4697
+ cloudManagedByPath.set(key, policy);
4698
+ continue;
4699
+ }
4700
+ hookLogWarn(`cloud-managed policies ${existing.id} and ${policy.id} have identical source, so they share ` + `one artifact and load as one policy; enforcing it if either asks to enforce`);
4701
+ if (existing.effect !== "enforce" && policy.effect === "enforce") {
4702
+ cloudManagedByPath.set(key, policy);
4703
+ }
4704
+ }
5635
4705
  for (const customPoliciesPath of typeof customPoliciesPaths === "string" ? [customPoliciesPaths] : customPoliciesPaths ?? []) {
5636
4706
  const absPath = resolve5(projectRoot, customPoliciesPath);
5637
4707
  if (existsSync5(absPath)) {
@@ -5646,7 +4716,7 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
5646
4716
  const tagged = hook;
5647
4717
  if (cloudManaged) {
5648
4718
  tagged.__cloudManaged = cloudManaged;
5649
- tagged.__policyId = `cloud:${cloudManaged.id}@${cloudManaged.revision}:${hook.name}`;
4719
+ tagged.__policyId = `cloud:${cloudManaged.id}@${cloudManaged.version}:${hook.name}`;
5650
4720
  } else {
5651
4721
  tagged.__policyId = customPolicyId(absPath, hook.name);
5652
4722
  }
@@ -5658,23 +4728,24 @@ async function loadAllCustomHooks(customPoliciesPaths, opts) {
5658
4728
  }
5659
4729
  const hooksBeforeConvention = getCustomHooks().length;
5660
4730
  const projectDir = resolve5(projectRoot, ".failproofai", "policies");
4731
+ const projectScope = projectDir === customPoliciesDir() ? "user" : "project";
5661
4732
  if (conventionEnabled) {
5662
4733
  sweepStaleTmpArtifacts(projectDir);
5663
- warnSkippedPolicyFiles(projectDir, "project");
4734
+ warnSkippedPolicyFiles(projectDir, projectScope);
5664
4735
  }
5665
4736
  sweepStaleTmpArtifacts(shimsDir());
5666
4737
  const projectFiles = conventionEnabled ? discoverPolicyFiles(projectDir).filter((f) => !loadedPaths.has(f)) : [];
5667
4738
  for (const file of projectFiles) {
5668
4739
  loadedPaths.add(file);
5669
4740
  const hooksBefore = getCustomHooks().length;
5670
- await loadSingleFile(file, { conventionScope: "project" });
4741
+ await loadSingleFile(file, { conventionScope: projectScope });
5671
4742
  const newHooks = getCustomHooks().slice(hooksBefore);
5672
4743
  for (const hook of newHooks) {
5673
- hook.__policyId = conventionPolicyId("project", basename(file), hook.name);
4744
+ hook.__policyId = conventionPolicyId(projectScope, basename(file), hook.name);
5674
4745
  }
5675
4746
  if (newHooks.length > 0) {
5676
4747
  conventionSources.push({
5677
- scope: "project",
4748
+ scope: projectScope,
5678
4749
  file: basename(file),
5679
4750
  hookNames: newHooks.map((h) => h.name)
5680
4751
  });
@@ -10517,16 +9588,21 @@ function cloudManagedPolicyRoot() {
10517
9588
  function parseManifest(value) {
10518
9589
  if (!value || typeof value !== "object")
10519
9590
  throw new Error("active manifest is not an object");
10520
- const manifest = value;
10521
- if (manifest.schemaVersion !== ACTIVE_SCHEMA_VERSION) {
10522
- throw new Error(`unsupported active manifest schema ${String(manifest.schemaVersion)}`);
9591
+ const raw = value;
9592
+ if (!ACCEPTED_ACTIVE_SCHEMA_VERSIONS.includes(raw.schemaVersion)) {
9593
+ throw new Error(`unsupported active manifest schema ${String(raw.schemaVersion)} ` + `(supported: ${ACCEPTED_ACTIVE_SCHEMA_VERSIONS.join(", ")})`);
10523
9594
  }
10524
- if (!Number.isSafeInteger(manifest.generation) || (manifest.generation ?? -1) < 0) {
10525
- throw new Error("active manifest generation is invalid");
9595
+ const deployment = raw.deployment ?? raw.generation;
9596
+ if (!Number.isSafeInteger(deployment) || (deployment ?? -1) < 0) {
9597
+ throw new Error("active manifest deployment is invalid");
10526
9598
  }
10527
- if (!Array.isArray(manifest.policies))
9599
+ if (!Array.isArray(raw.policies))
10528
9600
  throw new Error("active manifest policies is not an array");
10529
- return manifest;
9601
+ const policies = raw.policies.map((p) => {
9602
+ const entry = p;
9603
+ return { ...entry, version: entry.version ?? entry.revision };
9604
+ });
9605
+ return { schemaVersion: raw.schemaVersion, deployment, policies };
10530
9606
  }
10531
9607
  function resolveManagedPath(root, candidate) {
10532
9608
  if (!candidate || isAbsolute2(candidate))
@@ -10572,8 +9648,8 @@ function readActiveCloudManagedPolicies() {
10572
9648
  if (!POLICY_ID_RE.test(policy.id) || policy.id === "." || policy.id === "..") {
10573
9649
  throw new Error(`unsafe cloud-managed policy id ${JSON.stringify(policy.id)}`);
10574
9650
  }
10575
- if (!Number.isSafeInteger(policy.revision) || policy.revision < 0) {
10576
- throw new Error(`invalid revision for cloud-managed policy ${policy.id}`);
9651
+ if (!Number.isSafeInteger(policy.version) || policy.version < 0) {
9652
+ throw new Error(`invalid version for cloud-managed policy ${policy.id}`);
10577
9653
  }
10578
9654
  if (!SHA256_RE.test(policy.sha256)) {
10579
9655
  throw new Error(`invalid SHA-256 for cloud-managed policy ${policy.id}`);
@@ -10592,17 +9668,18 @@ function readActiveCloudManagedPolicies() {
10592
9668
  }
10593
9669
  return {
10594
9670
  id: policy.id,
10595
- revision: policy.revision,
9671
+ version: policy.version,
10596
9672
  effect: policy.effect ?? "enforce",
10597
9673
  sha256: policy.sha256,
10598
9674
  path: path3,
10599
- generation: manifest.generation
9675
+ deployment: manifest.deployment
10600
9676
  };
10601
9677
  });
10602
9678
  }
10603
- var ACTIVE_SCHEMA_VERSION = 1, SHA256_RE, POLICY_ID_RE;
9679
+ var ACCEPTED_ACTIVE_SCHEMA_VERSIONS, SHA256_RE, POLICY_ID_RE;
10604
9680
  var init_cloud_managed_policies = __esm(() => {
10605
9681
  init_fp_home();
9682
+ ACCEPTED_ACTIVE_SCHEMA_VERSIONS = [1, 2];
10606
9683
  SHA256_RE = /^[a-f0-9]{64}$/;
10607
9684
  POLICY_ID_RE = /^[A-Za-z0-9._-]{1,128}$/;
10608
9685
  });
@@ -11512,20 +10589,40 @@ async function probeDaemon() {
11512
10589
  return { ok: false, reason: "unreachable" };
11513
10590
  }
11514
10591
  }
10592
+ async function daemonAcceptsConnectionsQuietly() {
10593
+ try {
10594
+ const { daemonAcceptsConnections: daemonAcceptsConnections3 } = await Promise.resolve().then(() => (init_daemon_client2(), exports_daemon_client2));
10595
+ return await daemonAcceptsConnections3();
10596
+ } catch {
10597
+ return false;
10598
+ }
10599
+ }
11515
10600
  async function probeDaemonEndToEnd() {
11516
10601
  return (await probeDaemon()).ok;
11517
10602
  }
11518
- async function waitForDaemonRunning() {
11519
- const deadline = Date.now() + SERVICE_START_TIMEOUT_MS;
10603
+ async function waitForDaemonRunning(deps = {}) {
10604
+ const status = deps.status ?? daemonServiceStatus;
10605
+ const accepts = deps.accepts ?? daemonAcceptsConnectionsQuietly;
10606
+ const sleep = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
10607
+ const now = deps.now ?? Date.now;
10608
+ const deadline = now() + SERVICE_START_TIMEOUT_MS;
11520
10609
  for (;; ) {
11521
- if (daemonServiceStatus() === "running")
10610
+ if (status() === "running")
11522
10611
  break;
11523
- if (Date.now() >= deadline)
10612
+ if (now() >= deadline)
11524
10613
  return false;
11525
- await new Promise((r) => setTimeout(r, SERVICE_START_POLL_MS));
10614
+ await sleep(SERVICE_START_POLL_MS);
10615
+ }
10616
+ const settleUntil = now() + SERVICE_SETTLE_MS;
10617
+ for (;; ) {
10618
+ if (status() !== "running")
10619
+ return false;
10620
+ if (await accepts())
10621
+ return true;
10622
+ if (now() >= settleUntil)
10623
+ return true;
10624
+ await sleep(SERVICE_START_POLL_MS);
11526
10625
  }
11527
- await new Promise((r) => setTimeout(r, SERVICE_SETTLE_MS));
11528
- return daemonServiceStatus() === "running";
11529
10626
  }
11530
10627
  function readInstalledServiceDefinition() {
11531
10628
  if (!isDaemonSupportedPlatform())
@@ -11756,6 +10853,18 @@ var init_daemon_service = __esm(() => {
11756
10853
  });
11757
10854
 
11758
10855
  // src/hooks/fp-reset.ts
10856
+ import {
10857
+ copyFileSync,
10858
+ existsSync as existsSync18,
10859
+ mkdirSync as mkdirSync10,
10860
+ readFileSync as readFileSync15,
10861
+ readdirSync as readdirSync13,
10862
+ renameSync as renameSync5,
10863
+ rmSync as rmSync6,
10864
+ rmdirSync,
10865
+ statSync as statSync15,
10866
+ writeFileSync as writeFileSync8
10867
+ } from "node:fs";
11759
10868
  function layoutWarningForHook() {
11760
10869
  const state = detectLayout();
11761
10870
  if (state.kind === "current" || state.kind === "absent")
@@ -11763,6 +10872,8 @@ function layoutWarningForHook() {
11763
10872
  if (state.kind === "future") {
11764
10873
  return `[failproofai] this directory was written by a newer version ` + `(layout ${state.found} vs ${LAYOUT_VERSION}) — policies are NOT being enforced. ` + `Upgrade failproofai.`;
11765
10874
  }
10875
+ if (existsSync18(globalPolicyConfigFile()))
10876
+ return null;
11766
10877
  return `[failproofai] setup predates this version — global policies are NOT being enforced. ` + `Run \`failproofai config\` to re-create it.`;
11767
10878
  }
11768
10879
  var init_fp_reset = __esm(() => {
@@ -11875,7 +10986,7 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
11875
10986
  let conventionHookNames = new Set;
11876
10987
  let activePause = null;
11877
10988
  const policyAttribution = new Map;
11878
- let cloudGeneration;
10989
+ let cloudDeployment;
11879
10990
  const observedResults = [];
11880
10991
  if (opts?.forceDecision) {
11881
10992
  config = { enabledPolicies: [] };
@@ -11898,7 +11009,7 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
11898
11009
  cli
11899
11010
  });
11900
11011
  }
11901
- cloudGeneration = cloudManagedPolicies[0]?.generation;
11012
+ cloudDeployment = cloudManagedPolicies[0]?.deployment;
11902
11013
  const configuredCustomPaths = config.customPoliciesPaths ?? config.customPoliciesPath;
11903
11014
  const allExplicitPaths = cloudManagedPolicies.length === 0 ? configuredCustomPaths : [
11904
11015
  ...typeof configuredCustomPaths === "string" ? [configuredCustomPaths] : configuredCustomPaths ?? [],
@@ -11923,7 +11034,7 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
11923
11034
  const hookName = hook.name;
11924
11035
  const conventionScope = hook.__conventionScope;
11925
11036
  const isConvention = !!conventionScope;
11926
- const prefix = cloudManaged ? `cloud/${cloudManaged.id}@${cloudManaged.revision}` : isConvention ? `.failproofai-${conventionScope}` : "custom";
11037
+ const prefix = cloudManaged ? `cloud/${cloudManaged.id}@${cloudManaged.version}` : isConvention ? `.failproofai-${conventionScope}` : "custom";
11927
11038
  const observeOnly = cloudManaged?.effect === "observe";
11928
11039
  const fn = async (ctx) => {
11929
11040
  if (observeOnly) {
@@ -11931,7 +11042,7 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
11931
11042
  if (shadow.decision !== "allow") {
11932
11043
  observedResults.push({
11933
11044
  policyId: cloudManaged.id,
11934
- revision: cloudManaged.revision,
11045
+ version: cloudManaged.version,
11935
11046
  decision: shadow.decision,
11936
11047
  reason: shadow.reason ?? null
11937
11048
  });
@@ -11962,7 +11073,7 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
11962
11073
  const registeredName = `${prefix}/${hookName}`;
11963
11074
  policyAttribution.set(registeredName, {
11964
11075
  source: cloudManaged ? "cloud" : isConvention ? "convention" : "custom",
11965
- ...cloudManaged ? { cloudPolicyId: cloudManaged.id, cloudRevision: cloudManaged.revision } : {}
11076
+ ...cloudManaged ? { cloudPolicyId: cloudManaged.id, cloudVersion: cloudManaged.version } : {}
11966
11077
  });
11967
11078
  registerPolicy(registeredName, hook.description ?? "", fn, hook.match ?? {}, -1);
11968
11079
  }
@@ -12011,10 +11122,10 @@ async function evaluateHookEvent(eventType, cli = "claude", stdinPayload, opts)
12011
11122
  return {
12012
11123
  policySource: attribution?.source ?? "builtin",
12013
11124
  ...attribution?.cloudPolicyId ? { cloudPolicyId: attribution.cloudPolicyId } : {},
12014
- ...attribution?.cloudRevision !== undefined ? { cloudRevision: attribution.cloudRevision } : {}
11125
+ ...attribution?.cloudVersion !== undefined ? { cloudVersion: attribution.cloudVersion } : {}
12015
11126
  };
12016
11127
  })() : {},
12017
- ...cloudGeneration !== undefined ? { cloudGeneration } : {},
11128
+ ...cloudDeployment !== undefined ? { cloudDeployment } : {},
12018
11129
  ...observedResults.length > 0 ? { observed: observedResults } : {},
12019
11130
  ...activePause ? { pausedBy: activePause.setBy, pauseExpiresAt: activePause.expiresAt } : {}
12020
11131
  };
@@ -12104,65 +11215,98 @@ var init_handler = __esm(() => {
12104
11215
  // src/hooks/fp-reset.ts
12105
11216
  var exports_fp_reset = {};
12106
11217
  __export(exports_fp_reset, {
11218
+ writeCarriedPolicyConfig: () => writeCarriedPolicyConfig,
12107
11219
  resetHome: () => resetHome,
11220
+ readCarriedTelemetryOptOut: () => readCarriedTelemetryOptOut,
11221
+ readCarriedPolicyConfig: () => readCarriedPolicyConfig,
12108
11222
  migrateHookActivity: () => migrateHookActivity,
12109
11223
  migrateConventionPolicies: () => migrateConventionPolicies,
12110
11224
  layoutWarningForHook: () => layoutWarningForHook2,
11225
+ layoutBlockerForScheduledRun: () => layoutBlockerForScheduledRun,
12111
11226
  checkLayoutForCli: () => checkLayoutForCli
12112
11227
  });
12113
- import { copyFileSync, existsSync as existsSync18, mkdirSync as mkdirSync10, readdirSync as readdirSync13, renameSync as renameSync5, rmSync as rmSync6 } from "node:fs";
12114
- import { resolve as resolve15 } from "node:path";
11228
+ import {
11229
+ copyFileSync as copyFileSync2,
11230
+ existsSync as existsSync19,
11231
+ mkdirSync as mkdirSync11,
11232
+ readFileSync as readFileSync16,
11233
+ readdirSync as readdirSync14,
11234
+ renameSync as renameSync6,
11235
+ rmSync as rmSync7,
11236
+ rmdirSync as rmdirSync2,
11237
+ statSync as statSync16,
11238
+ writeFileSync as writeFileSync9
11239
+ } from "node:fs";
11240
+ import { dirname as dirname9, resolve as resolve15 } from "node:path";
12115
11241
  function migrateConventionPolicies() {
12116
- const from = policiesDir();
12117
- if (!existsSync18(from))
11242
+ const from = legacy.customPoliciesDir();
11243
+ if (!existsSync19(from))
12118
11244
  return [];
12119
11245
  const moved = [];
12120
- try {
12121
- const entries = readdirSync13(from, { withFileTypes: true }).filter((e) => e.isFile() && USER_SOURCE_RE.test(e.name) && !e.name.endsWith(".d.ts"));
11246
+ const mergeInto = (dir, dest, prefix) => {
11247
+ let entries;
11248
+ try {
11249
+ entries = readdirSync14(dir, { withFileTypes: true });
11250
+ } catch {
11251
+ return;
11252
+ }
12122
11253
  if (entries.length === 0)
12123
- return [];
12124
- const to = customPoliciesDir();
12125
- mkdirSync10(to, { recursive: true });
11254
+ return;
11255
+ try {
11256
+ mkdirSync11(dest, { recursive: true });
11257
+ } catch {
11258
+ return;
11259
+ }
12126
11260
  for (const entry of entries) {
12127
- const target = resolve15(to, entry.name);
12128
- if (existsSync18(target))
11261
+ const source = resolve15(dir, entry.name);
11262
+ const target = resolve15(dest, entry.name);
11263
+ const name = `${prefix}${entry.name}`;
11264
+ if (!existsSync19(target)) {
11265
+ try {
11266
+ renameSync6(source, target);
11267
+ moved.push(name);
11268
+ } catch {}
12129
11269
  continue;
12130
- try {
12131
- renameSync5(resolve15(from, entry.name), target);
12132
- moved.push(entry.name);
12133
- } catch {}
11270
+ }
11271
+ if (entry.isDirectory() && statSync16(target).isDirectory()) {
11272
+ mergeInto(source, target, `${name}/`);
11273
+ }
12134
11274
  }
11275
+ };
11276
+ mergeInto(from, policiesDir(), "");
11277
+ try {
11278
+ rmdirSync2(from);
12135
11279
  } catch {}
12136
11280
  return moved.sort((a, b) => a.localeCompare(b));
12137
11281
  }
12138
11282
  function migrateHookActivity() {
12139
11283
  const from = legacy.hookActivityDir();
12140
- if (!existsSync18(from))
11284
+ if (!existsSync19(from))
12141
11285
  return [];
12142
11286
  const moved = [];
12143
11287
  try {
12144
- const entries = readdirSync13(from, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl"));
11288
+ const entries = readdirSync14(from, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl"));
12145
11289
  if (entries.length === 0)
12146
11290
  return [];
12147
11291
  const to = hookActivityDir();
12148
- mkdirSync10(to, { recursive: true });
11292
+ mkdirSync11(to, { recursive: true });
12149
11293
  const stamp2 = Date.now();
12150
11294
  let seq = 0;
12151
11295
  for (const entry of entries) {
12152
11296
  const source = resolve15(from, entry.name);
12153
11297
  let name = entry.name === "current.jsonl" ? `page-${stamp2}-${seq++}.jsonl` : entry.name;
12154
11298
  let target = resolve15(to, name);
12155
- while (existsSync18(target)) {
11299
+ while (existsSync19(target)) {
12156
11300
  name = `page-${stamp2}-${seq++}.jsonl`;
12157
11301
  target = resolve15(to, name);
12158
11302
  }
12159
11303
  try {
12160
- renameSync5(source, target);
11304
+ renameSync6(source, target);
12161
11305
  moved.push(name);
12162
11306
  } catch (err) {
12163
11307
  if (err?.code === "EXDEV") {
12164
11308
  try {
12165
- copyFileSync(source, target);
11309
+ copyFileSync2(source, target);
12166
11310
  moved.push(name);
12167
11311
  } catch {}
12168
11312
  }
@@ -12171,20 +11315,85 @@ function migrateHookActivity() {
12171
11315
  } catch {}
12172
11316
  return moved.sort((a, b) => a.localeCompare(b));
12173
11317
  }
11318
+ function readCarriedPolicyConfig() {
11319
+ const to = globalPolicyConfigFile();
11320
+ const layoutTwo = resolve15(legacy.localPoliciesDir(), "policies-config.json");
11321
+ const from = existsSync19(layoutTwo) ? layoutTwo : legacy.policyConfig();
11322
+ if (!existsSync19(from))
11323
+ return null;
11324
+ let parsed;
11325
+ try {
11326
+ parsed = JSON.parse(readFileSync16(from, "utf8"));
11327
+ } catch {
11328
+ return null;
11329
+ }
11330
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
11331
+ return null;
11332
+ const carried = {};
11333
+ let found = false;
11334
+ for (const key of CARRIED_POLICY_CONFIG_KEYS) {
11335
+ if (parsed[key] !== undefined) {
11336
+ carried[key] = parsed[key];
11337
+ found = true;
11338
+ }
11339
+ }
11340
+ if (!found)
11341
+ return null;
11342
+ if (carried.enabledPolicies === undefined)
11343
+ carried.enabledPolicies = [];
11344
+ return carried;
11345
+ }
11346
+ function writeCarriedPolicyConfig(carried) {
11347
+ if (!carried)
11348
+ return [];
11349
+ const to = globalPolicyConfigFile();
11350
+ try {
11351
+ mkdirSync11(dirname9(to), { recursive: true });
11352
+ writeFileSync9(to, JSON.stringify(carried, null, 2) + `
11353
+ `, "utf8");
11354
+ } catch {
11355
+ return [];
11356
+ }
11357
+ return Object.keys(carried).sort((a, b) => a.localeCompare(b));
11358
+ }
11359
+ function readCarriedTelemetryOptOut() {
11360
+ const from = legacy.configToml();
11361
+ if (!existsSync19(from))
11362
+ return false;
11363
+ try {
11364
+ const text = readFileSync16(from, "utf8");
11365
+ const start = text.search(/^\s*\[telemetry\]\s*$/m);
11366
+ if (start === -1)
11367
+ return false;
11368
+ const body = text.slice(start).replace(/^\s*\[telemetry\]\s*$/m, "");
11369
+ const nextTable = body.search(/^\s*\[/m);
11370
+ const section = nextTable === -1 ? body : body.slice(0, nextTable);
11371
+ return /^\s*enabled\s*=\s*false\s*$/m.test(section);
11372
+ } catch {
11373
+ return false;
11374
+ }
11375
+ }
12174
11376
  function resetHome(from) {
12175
11377
  const migrated = migrateConventionPolicies();
12176
11378
  const activity = migrateHookActivity();
11379
+ const pendingPolicyConfig = readCarriedPolicyConfig();
11380
+ const telemetryOptOut = readCarriedTelemetryOptOut();
12177
11381
  const removed = [];
12178
11382
  for (const path3 of resettablePaths()) {
12179
- if (!existsSync18(path3))
11383
+ if (from === LAYOUT_VERSION && path3 === globalPolicyConfigFile())
11384
+ continue;
11385
+ if (!existsSync19(path3))
12180
11386
  continue;
12181
11387
  try {
12182
- rmSync6(path3, { recursive: true, force: true });
11388
+ rmSync7(path3, { recursive: true, force: true });
12183
11389
  removed.push(path3);
12184
11390
  } catch {}
12185
11391
  }
11392
+ const policyConfig = writeCarriedPolicyConfig(pendingPolicyConfig);
11393
+ if (telemetryOptOut)
11394
+ updateConfig({ telemetry: { enabled: false } });
12186
11395
  writeVersionFile();
12187
- return { removed, migrated, activity, from };
11396
+ return { removed, migrated, activity, policyConfig, from };
12188
11397
  }
12189
11398
  async function checkLayoutForCli() {
12190
11399
  const state = detectLayout();
@@ -12227,7 +11436,7 @@ async function checkLayoutForCli() {
12227
11436
  ]
12228
11437
  };
12229
11438
  }
12230
- if (state.kind === "absent")
11439
+ if (state.kind === "absent" || state.kind === "current" && state.inferred)
12231
11440
  writeVersionFile();
12232
11441
  return {
12233
11442
  state,
@@ -12296,6 +11505,15 @@ function staleDaemonHint() {
12296
11505
  return [];
12297
11506
  }
12298
11507
  }
11508
+ function layoutBlockerForScheduledRun() {
11509
+ const state = detectLayout();
11510
+ if (state.kind === "current" || state.kind === "absent")
11511
+ return null;
11512
+ if (state.kind === "future") {
11513
+ return `[failproofai] this directory was written by a newer version ` + `(layout ${state.found} vs ${LAYOUT_VERSION}) — refusing to run unattended. ` + `Upgrade failproofai.`;
11514
+ }
11515
+ return `[failproofai] setup predates this version (layout ${state.found} vs ${LAYOUT_VERSION}) — ` + `refusing to run unattended, because completing it would reset this home. ` + `Run \`failproofai config\`.`;
11516
+ }
12299
11517
  function layoutWarningForHook2() {
12300
11518
  const state = detectLayout();
12301
11519
  if (state.kind === "current" || state.kind === "absent")
@@ -12303,20 +11521,31 @@ function layoutWarningForHook2() {
12303
11521
  if (state.kind === "future") {
12304
11522
  return `[failproofai] this directory was written by a newer version ` + `(layout ${state.found} vs ${LAYOUT_VERSION}) — policies are NOT being enforced. ` + `Upgrade failproofai.`;
12305
11523
  }
11524
+ if (existsSync19(globalPolicyConfigFile()))
11525
+ return null;
12306
11526
  return `[failproofai] setup predates this version — global policies are NOT being enforced. ` + `Run \`failproofai config\` to re-create it.`;
12307
11527
  }
12308
- var USER_SOURCE_RE;
11528
+ var CARRIED_POLICY_CONFIG_KEYS;
12309
11529
  var init_fp_reset2 = __esm(() => {
12310
11530
  init_fp_home();
12311
11531
  init_fp_config();
12312
11532
  init_daemon_service();
12313
- USER_SOURCE_RE = /\.(js|mjs|ts)$/;
11533
+ CARRIED_POLICY_CONFIG_KEYS = [
11534
+ "enabledPolicies",
11535
+ "customPoliciesPaths",
11536
+ "customPoliciesPath",
11537
+ "disabledCustomPolicies",
11538
+ "conventionPolicies",
11539
+ "disabledConventionPolicies",
11540
+ "policyParams",
11541
+ "llm"
11542
+ ];
12314
11543
  });
12315
11544
 
12316
11545
  // scripts/install-telemetry.mjs
12317
11546
  import { createHmac, randomUUID as randomUUID2 } from "node:crypto";
12318
11547
  import { execSync as execSync4 } from "node:child_process";
12319
- import { readFileSync as readFileSync15, writeFileSync as writeFileSync8, mkdirSync as mkdirSync11 } from "node:fs";
11548
+ import { readFileSync as readFileSync17, writeFileSync as writeFileSync10, mkdirSync as mkdirSync12 } from "node:fs";
12320
11549
  import { homedir as homedir20, platform, arch, hostname, cpus } from "node:os";
12321
11550
  import { join as join27 } from "node:path";
12322
11551
  function hashToId3(raw) {
@@ -12326,7 +11555,7 @@ function getInstanceId3() {
12326
11555
  const idDir = join27(homedir20(), ".failproofai");
12327
11556
  const idFile = join27(idDir, "instance-id");
12328
11557
  try {
12329
- const existing = readFileSync15(idFile, "utf-8").trim();
11558
+ const existing = readFileSync17(idFile, "utf-8").trim();
12330
11559
  if (existing)
12331
11560
  return existing;
12332
11561
  } catch {}
@@ -12335,7 +11564,7 @@ function getInstanceId3() {
12335
11564
  if (p === "linux") {
12336
11565
  for (const machineIdPath of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
12337
11566
  try {
12338
- const id2 = readFileSync15(machineIdPath, "utf-8").trim();
11567
+ const id2 = readFileSync17(machineIdPath, "utf-8").trim();
12339
11568
  if (id2)
12340
11569
  return hashToId3(id2);
12341
11570
  } catch {}
@@ -12367,8 +11596,8 @@ function getInstanceId3() {
12367
11596
  } catch {}
12368
11597
  const id = randomUUID2();
12369
11598
  try {
12370
- mkdirSync11(idDir, { recursive: true });
12371
- writeFileSync8(idFile, id, "utf-8");
11599
+ mkdirSync12(idDir, { recursive: true });
11600
+ writeFileSync10(idFile, id, "utf-8");
12372
11601
  } catch {}
12373
11602
  return id;
12374
11603
  }
@@ -12406,8 +11635,8 @@ __export(exports_install_check, {
12406
11635
  compareSemver: () => compareSemver,
12407
11636
  checkHooks: () => checkHooks
12408
11637
  });
12409
- import { existsSync as existsSync19, readFileSync as readFileSync16, writeFileSync as writeFileSync9, mkdirSync as mkdirSync12 } from "node:fs";
12410
- import { dirname as dirname9, resolve as resolve16 } from "node:path";
11638
+ import { existsSync as existsSync20, readFileSync as readFileSync18, writeFileSync as writeFileSync11, mkdirSync as mkdirSync13 } from "node:fs";
11639
+ import { dirname as dirname10, resolve as resolve16 } from "node:path";
12411
11640
  import { platform as platform2, arch as arch2, release, homedir as homedir21, hostname as hostname2 } from "node:os";
12412
11641
  import { createHmac as createHmac2 } from "node:crypto";
12413
11642
  function hashToId4(raw) {
@@ -12417,14 +11646,14 @@ function lastVersionPath() {
12417
11646
  return lastVersionFile();
12418
11647
  }
12419
11648
  function compareSemver(a, b) {
12420
- const parse2 = (v) => {
11649
+ const parse = (v) => {
12421
11650
  const m = /^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/.exec(v);
12422
11651
  if (!m)
12423
11652
  return null;
12424
11653
  return { nums: [Number(m[1]), Number(m[2]), Number(m[3])], pre: m[4] ?? null };
12425
11654
  };
12426
- const pa = parse2(a);
12427
- const pb = parse2(b);
11655
+ const pa = parse(a);
11656
+ const pb = parse(b);
12428
11657
  if (!pa || !pb)
12429
11658
  return a < b ? -1 : a > b ? 1 : 0;
12430
11659
  for (let i = 0;i < 3; i++) {
@@ -12462,12 +11691,12 @@ function compareSemver(a, b) {
12462
11691
  }
12463
11692
  function checkHooks() {
12464
11693
  const hooksConfigPath = globalPolicyConfigFile();
12465
- if (!existsSync19(hooksConfigPath)) {
11694
+ if (!existsSync20(hooksConfigPath)) {
12466
11695
  return { configured: false, registered: false, policyCount: 0 };
12467
11696
  }
12468
11697
  let config;
12469
11698
  try {
12470
- config = JSON.parse(readFileSync16(hooksConfigPath, "utf8"));
11699
+ config = JSON.parse(readFileSync18(hooksConfigPath, "utf8"));
12471
11700
  } catch {
12472
11701
  return { configured: false, registered: false, policyCount: 0 };
12473
11702
  }
@@ -12476,11 +11705,11 @@ function checkHooks() {
12476
11705
  }
12477
11706
  const policyCount = config.enabledPolicies.length;
12478
11707
  const settingsPath = resolve16(homedir21(), ".claude", "settings.json");
12479
- if (!existsSync19(settingsPath))
11708
+ if (!existsSync20(settingsPath))
12480
11709
  return { configured: true, registered: false, policyCount };
12481
11710
  let settings;
12482
11711
  try {
12483
- settings = JSON.parse(readFileSync16(settingsPath, "utf8"));
11712
+ settings = JSON.parse(readFileSync18(settingsPath, "utf8"));
12484
11713
  } catch {
12485
11714
  return { configured: true, registered: false, policyCount };
12486
11715
  }
@@ -12504,8 +11733,8 @@ async function maybeReportInstall(version3) {
12504
11733
  const versionFile2 = lastVersionPath();
12505
11734
  let previousVersion = null;
12506
11735
  try {
12507
- if (existsSync19(versionFile2)) {
12508
- previousVersion = readFileSync16(versionFile2, "utf8").trim() || null;
11736
+ if (existsSync20(versionFile2)) {
11737
+ previousVersion = readFileSync18(versionFile2, "utf8").trim() || null;
12509
11738
  }
12510
11739
  } catch {}
12511
11740
  if (previousVersion === version3)
@@ -12541,8 +11770,8 @@ async function maybeReportInstall(version3) {
12541
11770
  enabled_policy_count: hooks.policyCount
12542
11771
  }, { version: version3, timeoutMs: REPORT_TIMEOUT_MS, enabled: telemetryEnabled }));
12543
11772
  try {
12544
- mkdirSync12(dirname9(versionFile2), { recursive: true });
12545
- writeFileSync9(versionFile2, version3, "utf8");
11773
+ mkdirSync13(dirname10(versionFile2), { recursive: true });
11774
+ writeFileSync11(versionFile2, version3, "utf8");
12546
11775
  } catch {}
12547
11776
  await Promise.all(events.map((p) => p.catch(() => {})));
12548
11777
  } catch {}
@@ -12805,10 +12034,11 @@ function runPrompt(p) {
12805
12034
  return lines;
12806
12035
  };
12807
12036
  const collapse = (result) => {
12037
+ const summary = result === BACK ? "back" : p.summaryFor(result);
12808
12038
  repaint(stdout, region, [
12809
12039
  c.dim(BAR),
12810
12040
  `${c.dim(STEP_DONE)} ${p.message}`,
12811
- `${c.dim(BAR)} ${c.dim(p.summaryFor(result))}`
12041
+ `${c.dim(BAR)} ${c.dim(summary)}`
12812
12042
  ]);
12813
12043
  };
12814
12044
  return new Promise((resolve17) => {
@@ -12834,6 +12064,8 @@ function runPrompt(p) {
12834
12064
  return;
12835
12065
  if (key.ctrl && (key.name === "c" || key.name === "d") || key.name === "escape") {
12836
12066
  finish(null);
12067
+ } else if (key.name === "left" && p.allowBack) {
12068
+ finish(BACK);
12837
12069
  } else if (key.name === "up") {
12838
12070
  cursor = cursor > 0 ? cursor - 1 : choices.length - 1;
12839
12071
  repaint(stdout, region, build());
@@ -12877,7 +12109,8 @@ function selectOne(opts) {
12877
12109
  const hint = choice.hint ? ` ${c.dim(ellipsize(choice.hint, budget))}` : "";
12878
12110
  return `${dot} ${label}${hint}`;
12879
12111
  },
12880
- footer: "↑/↓ navigate · enter to select · esc to cancel",
12112
+ allowBack: opts.allowBack,
12113
+ footer: opts.allowBack ? "↑/↓ navigate · enter to select · ← back · esc to cancel" : "↑/↓ navigate · enter to select · esc to cancel",
12881
12114
  onKey: (key, cursor) => key.name === "return" ? { done: choices[cursor].value } : undefined,
12882
12115
  summaryFor: (value) => value === null ? "cancelled" : choices.find((ch) => ch.value === value)?.label ?? String(value)
12883
12116
  });
@@ -12911,7 +12144,8 @@ function multiSelect(opts) {
12911
12144
  return `${caret} ${box} ${label}${hint}`;
12912
12145
  },
12913
12146
  warnLine: () => warn ? c.warn(`Select at least ${minSelected}.`) : null,
12914
- footer: opts.hint ?? "↑/↓ move · space select · ctrl+a all · enter confirm",
12147
+ allowBack: opts.allowBack,
12148
+ footer: opts.hint ?? (opts.allowBack ? "↑/↓ move · space select · ctrl+a all · ← back · enter confirm" : "↑/↓ move · space select · ctrl+a all · enter confirm"),
12915
12149
  onKey: (key, cursor) => {
12916
12150
  if (key.name === "space") {
12917
12151
  if (choices[cursor]?.locked)
@@ -12973,11 +12207,13 @@ function promptText(opts) {
12973
12207
  return new Promise((resolve17) => {
12974
12208
  let value = "";
12975
12209
  const draw = (error) => {
12210
+ const cols = stdout.columns || 80;
12976
12211
  const shown = opts.mask ? "•".repeat(value.length) : value;
12977
12212
  const hint = opts.hint ? ` ${c.dim(opts.hint)}` : "";
12213
+ const line = truncate(`${c.bold(opts.message)} ${shown}${hint}`, cols - 1);
12978
12214
  const err = error ? `
12979
- ${c.warn(error)}` : "";
12980
- stdout.write(`\r\x1B[2K${c.bold(opts.message)} ${shown}${hint}${err}`);
12215
+ ${truncate(c.warn(error), cols - 3)}` : "";
12216
+ stdout.write(`\r\x1B[2K${line}${err}`);
12981
12217
  if (err)
12982
12218
  stdout.write("\x1B[1A");
12983
12219
  };
@@ -13024,7 +12260,7 @@ function promptText(opts) {
13024
12260
  stdin.on("keypress", onKey);
13025
12261
  });
13026
12262
  }
13027
- var ESC = "\x1B", BAR = "│", BAR_END = "└", STEP_ACTIVE = "◆", STEP_DONE = "◇", RADIO_ON = "●", RADIO_OFF = "○", CHECK_ON = "◼", CHECK_OFF = "◻", CARET = "❯", FLOWER = "❋", HUES, ANSI_RESET, ANSI_BOLD, ANSI_DIM, LOGO_MIN_COLS = 22, TAGLINE = "end-to-end failure layer for ai agents", LOGO_GRID, LOGO_TEAL, LOGO_PINK, WINDOW = 8;
12263
+ var ESC = "\x1B", BAR = "│", BAR_END = "└", STEP_ACTIVE = "◆", STEP_DONE = "◇", RADIO_ON = "●", RADIO_OFF = "○", CHECK_ON = "◼", CHECK_OFF = "◻", CARET = "❯", FLOWER = "❋", HUES, ANSI_RESET, ANSI_BOLD, ANSI_DIM, LOGO_MIN_COLS = 22, TAGLINE = "end-to-end failure layer for ai agents", LOGO_GRID, LOGO_TEAL, LOGO_PINK, WINDOW = 8, BACK;
13028
12264
  var init_tui = __esm(() => {
13029
12265
  HUES = {
13030
12266
  guide: { rgb: [102, 209, 181], basic: "36" },
@@ -13060,6 +12296,7 @@ var init_tui = __esm(() => {
13060
12296
  ];
13061
12297
  LOGO_TEAL = HUES.guide.rgb;
13062
12298
  LOGO_PINK = HUES.logoPink.rgb;
12299
+ BACK = Symbol("failproofai.back");
13063
12300
  });
13064
12301
 
13065
12302
  // src/hooks/collector-config.ts
@@ -14556,7 +13793,7 @@ var require_stringify = __commonJS((exports) => {
14556
13793
  props.push(doc.directives.tagString(tag));
14557
13794
  return props.join(" ");
14558
13795
  }
14559
- function stringify2(item, ctx, onComment, onChompKeep) {
13796
+ function stringify(item, ctx, onComment, onChompKeep) {
14560
13797
  if (identity.isPair(item))
14561
13798
  return item.toString(ctx, onComment, onChompKeep);
14562
13799
  if (identity.isAlias(item)) {
@@ -14585,14 +13822,14 @@ var require_stringify = __commonJS((exports) => {
14585
13822
  ${ctx.indent}${str2}`;
14586
13823
  }
14587
13824
  exports.createStringifyContext = createStringifyContext;
14588
- exports.stringify = stringify2;
13825
+ exports.stringify = stringify;
14589
13826
  });
14590
13827
 
14591
13828
  // node_modules/yaml/dist/stringify/stringifyPair.js
14592
13829
  var require_stringifyPair = __commonJS((exports) => {
14593
13830
  var identity = require_identity();
14594
13831
  var Scalar = require_Scalar();
14595
- var stringify2 = require_stringify();
13832
+ var stringify = require_stringify();
14596
13833
  var stringifyComment = require_stringifyComment();
14597
13834
  function stringifyPair({ key, value }, ctx, onComment, onChompKeep) {
14598
13835
  const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx;
@@ -14614,7 +13851,7 @@ var require_stringifyPair = __commonJS((exports) => {
14614
13851
  });
14615
13852
  let keyCommentDone = false;
14616
13853
  let chompKeep = false;
14617
- let str2 = stringify2.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true);
13854
+ let str2 = stringify.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true);
14618
13855
  if (!explicitKey && !ctx.inFlow && str2.length > 1024) {
14619
13856
  if (simpleKeys)
14620
13857
  throw new Error("With simple keys, single line scalar must not span more than 1024 characters");
@@ -14666,7 +13903,7 @@ ${indent}:`;
14666
13903
  ctx.indent = ctx.indent.substring(2);
14667
13904
  }
14668
13905
  let valueCommentDone = false;
14669
- const valueStr = stringify2.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true);
13906
+ const valueStr = stringify.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true);
14670
13907
  let ws = " ";
14671
13908
  if (keyComment || vsb || vcb) {
14672
13909
  ws = vsb ? `
@@ -14804,7 +14041,7 @@ var require_merge = __commonJS((exports) => {
14804
14041
  var require_addPairToJSMap = __commonJS((exports) => {
14805
14042
  var log = require_log();
14806
14043
  var merge = require_merge();
14807
- var stringify2 = require_stringify();
14044
+ var stringify = require_stringify();
14808
14045
  var identity = require_identity();
14809
14046
  var toJS = require_toJS();
14810
14047
  function addPairToJSMap(ctx, map, { key, value }) {
@@ -14840,7 +14077,7 @@ var require_addPairToJSMap = __commonJS((exports) => {
14840
14077
  if (typeof jsKey !== "object")
14841
14078
  return String(jsKey);
14842
14079
  if (identity.isNode(key) && ctx?.doc) {
14843
- const strCtx = stringify2.createStringifyContext(ctx.doc, {});
14080
+ const strCtx = stringify.createStringifyContext(ctx.doc, {});
14844
14081
  strCtx.anchors = new Set;
14845
14082
  for (const node of ctx.anchors.keys())
14846
14083
  strCtx.anchors.add(node.anchor);
@@ -14902,12 +14139,12 @@ var require_Pair = __commonJS((exports) => {
14902
14139
  // node_modules/yaml/dist/stringify/stringifyCollection.js
14903
14140
  var require_stringifyCollection = __commonJS((exports) => {
14904
14141
  var identity = require_identity();
14905
- var stringify2 = require_stringify();
14142
+ var stringify = require_stringify();
14906
14143
  var stringifyComment = require_stringifyComment();
14907
14144
  function stringifyCollection(collection, ctx, options) {
14908
14145
  const flow = ctx.inFlow ?? collection.flow;
14909
- const stringify3 = flow ? stringifyFlowCollection : stringifyBlockCollection;
14910
- return stringify3(collection, ctx, options);
14146
+ const stringify2 = flow ? stringifyFlowCollection : stringifyBlockCollection;
14147
+ return stringify2(collection, ctx, options);
14911
14148
  }
14912
14149
  function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) {
14913
14150
  const { indent, options: { commentString } } = ctx;
@@ -14932,7 +14169,7 @@ var require_stringifyCollection = __commonJS((exports) => {
14932
14169
  }
14933
14170
  }
14934
14171
  chompKeep = false;
14935
- let str3 = stringify2.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true);
14172
+ let str3 = stringify.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true);
14936
14173
  if (comment2)
14937
14174
  str3 += stringifyComment.lineComment(str3, itemIndent, commentString(comment2));
14938
14175
  if (chompKeep && comment2)
@@ -15001,7 +14238,7 @@ ${indent}${line}` : `
15001
14238
  }
15002
14239
  if (comment)
15003
14240
  reqNewline = true;
15004
- let str2 = stringify2.stringify(item, itemCtx, () => comment = null);
14241
+ let str2 = stringify.stringify(item, itemCtx, () => comment = null);
15005
14242
  reqNewline || (reqNewline = lines.length > linesAtValue || str2.includes(`
15006
14243
  `));
15007
14244
  if (i < items.length - 1) {
@@ -16242,7 +15479,7 @@ var require_Schema = __commonJS((exports) => {
16242
15479
  // node_modules/yaml/dist/stringify/stringifyDocument.js
16243
15480
  var require_stringifyDocument = __commonJS((exports) => {
16244
15481
  var identity = require_identity();
16245
- var stringify2 = require_stringify();
15482
+ var stringify = require_stringify();
16246
15483
  var stringifyComment = require_stringifyComment();
16247
15484
  function stringifyDocument(doc, options) {
16248
15485
  const lines = [];
@@ -16257,7 +15494,7 @@ var require_stringifyDocument = __commonJS((exports) => {
16257
15494
  }
16258
15495
  if (hasDirectives)
16259
15496
  lines.push("---");
16260
- const ctx = stringify2.createStringifyContext(doc, options);
15497
+ const ctx = stringify.createStringifyContext(doc, options);
16261
15498
  const { commentString } = ctx.options;
16262
15499
  if (doc.commentBefore) {
16263
15500
  if (lines.length !== 1)
@@ -16279,7 +15516,7 @@ var require_stringifyDocument = __commonJS((exports) => {
16279
15516
  contentComment = doc.contents.comment;
16280
15517
  }
16281
15518
  const onChompKeep = contentComment ? undefined : () => chompKeep = true;
16282
- let body = stringify2.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep);
15519
+ let body = stringify.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep);
16283
15520
  if (contentComment)
16284
15521
  body += stringifyComment.lineComment(body, "", commentString(contentComment));
16285
15522
  if ((body[0] === "|" || body[0] === ">") && lines[lines.length - 1] === "---") {
@@ -16287,7 +15524,7 @@ var require_stringifyDocument = __commonJS((exports) => {
16287
15524
  } else
16288
15525
  lines.push(body);
16289
15526
  } else {
16290
- lines.push(stringify2.stringify(doc.contents, ctx));
15527
+ lines.push(stringify.stringify(doc.contents, ctx));
16291
15528
  }
16292
15529
  if (doc.directives?.docEnd) {
16293
15530
  if (doc.comment) {
@@ -18306,7 +17543,7 @@ var require_cst_scalar = __commonJS((exports) => {
18306
17543
 
18307
17544
  // node_modules/yaml/dist/parse/cst-stringify.js
18308
17545
  var require_cst_stringify = __commonJS((exports) => {
18309
- var stringify2 = (cst) => ("type" in cst) ? stringifyToken(cst) : stringifyItem(cst);
17546
+ var stringify = (cst) => ("type" in cst) ? stringifyToken(cst) : stringifyItem(cst);
18310
17547
  function stringifyToken(token) {
18311
17548
  switch (token.type) {
18312
17549
  case "block-scalar": {
@@ -18359,7 +17596,7 @@ var require_cst_stringify = __commonJS((exports) => {
18359
17596
  res += stringifyToken(value);
18360
17597
  return res;
18361
17598
  }
18362
- exports.stringify = stringify2;
17599
+ exports.stringify = stringify;
18363
17600
  });
18364
17601
 
18365
17602
  // node_modules/yaml/dist/parse/cst-visit.js
@@ -20050,7 +19287,7 @@ var require_public_api = __commonJS((exports) => {
20050
19287
  }
20051
19288
  return doc;
20052
19289
  }
20053
- function parse2(src, reviver, options) {
19290
+ function parse(src, reviver, options) {
20054
19291
  let _reviver = undefined;
20055
19292
  if (typeof reviver === "function") {
20056
19293
  _reviver = reviver;
@@ -20069,7 +19306,7 @@ var require_public_api = __commonJS((exports) => {
20069
19306
  }
20070
19307
  return doc.toJS(Object.assign({ reviver: _reviver }, options));
20071
19308
  }
20072
- function stringify2(value, replacer, options) {
19309
+ function stringify(value, replacer, options) {
20073
19310
  let _replacer = null;
20074
19311
  if (typeof replacer === "function" || Array.isArray(replacer)) {
20075
19312
  _replacer = replacer;
@@ -20091,15 +19328,15 @@ var require_public_api = __commonJS((exports) => {
20091
19328
  return value.toString(options);
20092
19329
  return new Document.Document(value, _replacer, options).toString(options);
20093
19330
  }
20094
- exports.parse = parse2;
19331
+ exports.parse = parse;
20095
19332
  exports.parseAllDocuments = parseAllDocuments;
20096
19333
  exports.parseDocument = parseDocument;
20097
- exports.stringify = stringify2;
19334
+ exports.stringify = stringify;
20098
19335
  });
20099
19336
 
20100
19337
  // node_modules/yaml/dist/index.js
20101
19338
  var composer, Document, Schema, errors, Alias, identity, Pair, Scalar, YAMLMap, YAMLSeq, cst, lexer, lineCounter, parser, publicApi, visit, $Composer, $Document, $Schema, $YAMLError, $YAMLParseError, $YAMLWarning, $Alias, $isAlias, $isCollection, $isDocument, $isMap, $isNode, $isPair, $isScalar, $isSeq, $Pair, $Scalar, $YAMLMap, $YAMLSeq, $Lexer, $LineCounter, $Parser, $parse, $parseAllDocuments, $parseDocument, $stringify, $visit, $visitAsync;
20102
- var init_dist2 = __esm(() => {
19339
+ var init_dist = __esm(() => {
20103
19340
  composer = require_composer();
20104
19341
  Document = require_Document();
20105
19342
  Schema = require_Schema();
@@ -20148,31 +19385,31 @@ var init_dist2 = __esm(() => {
20148
19385
 
20149
19386
  // src/hooks/integrations.ts
20150
19387
  import { execSync as execSync5 } from "node:child_process";
20151
- import { readFileSync as readFileSync17, writeFileSync as writeFileSync10, existsSync as existsSync20, mkdirSync as mkdirSync13, unlinkSync as unlinkSync3 } from "node:fs";
20152
- import { resolve as resolve17, dirname as dirname10 } from "node:path";
19388
+ import { readFileSync as readFileSync19, writeFileSync as writeFileSync12, existsSync as existsSync21, mkdirSync as mkdirSync14, unlinkSync as unlinkSync3 } from "node:fs";
19389
+ import { resolve as resolve17, dirname as dirname11 } from "node:path";
20153
19390
  import { fileURLToPath } from "node:url";
20154
19391
  import { homedir as homedir22 } from "node:os";
20155
19392
  function readJsonFile(path3) {
20156
- if (!existsSync20(path3))
19393
+ if (!existsSync21(path3))
20157
19394
  return {};
20158
- const raw = readFileSync17(path3, "utf8");
19395
+ const raw = readFileSync19(path3, "utf8");
20159
19396
  return JSON.parse(raw);
20160
19397
  }
20161
19398
  function writeJsonFile(path3, data) {
20162
- mkdirSync13(dirname10(path3), { recursive: true });
20163
- writeFileSync10(path3, JSON.stringify(data, null, 2) + `
19399
+ mkdirSync14(dirname11(path3), { recursive: true });
19400
+ writeFileSync12(path3, JSON.stringify(data, null, 2) + `
20164
19401
  `, "utf8");
20165
19402
  }
20166
19403
  function readYamlDoc(path3) {
20167
19404
  try {
20168
- return existsSync20(path3) ? $parseDocument(readFileSync17(path3, "utf8")) : $parseDocument("");
19405
+ return existsSync21(path3) ? $parseDocument(readFileSync19(path3, "utf8")) : $parseDocument("");
20169
19406
  } catch {
20170
19407
  return $parseDocument("");
20171
19408
  }
20172
19409
  }
20173
19410
  function writeYamlDoc(path3, doc) {
20174
- mkdirSync13(dirname10(path3), { recursive: true });
20175
- writeFileSync10(path3, doc.toString(), "utf8");
19411
+ mkdirSync14(dirname11(path3), { recursive: true });
19412
+ writeFileSync12(path3, doc.toString(), "utf8");
20176
19413
  }
20177
19414
  function isMarkedHook(hook) {
20178
19415
  if (!hook || typeof hook !== "object")
@@ -20215,7 +19452,7 @@ function isMarkedCopilotHook(hook) {
20215
19452
  return false;
20216
19453
  }
20217
19454
  function opencodePluginFilePath(settingsPath) {
20218
- return resolve17(dirname10(settingsPath), "plugins", "failproofai.mjs");
19455
+ return resolve17(dirname11(settingsPath), "plugins", "failproofai.mjs");
20219
19456
  }
20220
19457
  function buildOpenCodePluginShim(binaryPath, scope) {
20221
19458
  const useNpx = scope === "project";
@@ -20451,7 +19688,7 @@ function makePiProjectRelativeEntry(extPath) {
20451
19688
  return extResolved;
20452
19689
  }
20453
19690
  function hermesConfigHasHooks(settingsPath) {
20454
- if (!existsSync20(settingsPath))
19691
+ if (!existsSync21(settingsPath))
20455
19692
  return false;
20456
19693
  try {
20457
19694
  const doc = readYamlDoc(settingsPath);
@@ -20467,7 +19704,7 @@ function hermesConfigHasHooks(settingsPath) {
20467
19704
  return false;
20468
19705
  }
20469
19706
  function unhookedHermesProfiles() {
20470
- return listHermesProfiles().filter((p) => existsSync20(p.home)).filter((p) => !hermesConfigHasHooks(resolve17(p.home, "config.yaml"))).map((p) => p.name);
19707
+ return listHermesProfiles().filter((p) => existsSync21(p.home)).filter((p) => !hermesConfigHasHooks(resolve17(p.home, "config.yaml"))).map((p) => p.name);
20471
19708
  }
20472
19709
  function getOpenClawPluginPath() {
20473
19710
  const fromEnv = process.env.FAILPROOFAI_PACKAGE_ROOT;
@@ -20501,7 +19738,7 @@ function detectInstalledClis() {
20501
19738
  }
20502
19739
  var claudeCode, codex, copilot, cursor, OPENCODE_PLUGIN_REL_PATH = "./plugins/failproofai.mjs", opencode, pi, hermes, OPENCLAW_PLUGIN_ID = "failproofai", openclaw, factory, devin, ANTIGRAVITY_HOOK_NAME = "failproofai", ANTIGRAVITY_TOOL_EVENTS, antigravity, goose, INTEGRATIONS;
20503
19740
  var init_integrations = __esm(() => {
20504
- init_dist2();
19741
+ init_dist();
20505
19742
  init_hermes_profiles();
20506
19743
  init_types();
20507
19744
  claudeCode = {
@@ -20613,7 +19850,7 @@ var init_integrations = __esm(() => {
20613
19850
  },
20614
19851
  hooksInstalledInSettings(scope, cwd) {
20615
19852
  const settingsPath = this.getSettingsPath(scope, cwd);
20616
- if (!existsSync20(settingsPath))
19853
+ if (!existsSync21(settingsPath))
20617
19854
  return false;
20618
19855
  try {
20619
19856
  const settings = this.readSettings(settingsPath);
@@ -20729,7 +19966,7 @@ var init_integrations = __esm(() => {
20729
19966
  },
20730
19967
  hooksInstalledInSettings(scope, cwd) {
20731
19968
  const settingsPath = this.getSettingsPath(scope, cwd);
20732
- if (!existsSync20(settingsPath))
19969
+ if (!existsSync21(settingsPath))
20733
19970
  return false;
20734
19971
  try {
20735
19972
  const settings = this.readSettings(settingsPath);
@@ -20843,7 +20080,7 @@ var init_integrations = __esm(() => {
20843
20080
  },
20844
20081
  hooksInstalledInSettings(scope, cwd) {
20845
20082
  const settingsPath = this.getSettingsPath(scope, cwd);
20846
- if (!existsSync20(settingsPath))
20083
+ if (!existsSync21(settingsPath))
20847
20084
  return false;
20848
20085
  try {
20849
20086
  const settings = this.readSettings(settingsPath);
@@ -20946,7 +20183,7 @@ var init_integrations = __esm(() => {
20946
20183
  },
20947
20184
  hooksInstalledInSettings(scope, cwd) {
20948
20185
  const settingsPath = this.getSettingsPath(scope, cwd);
20949
- if (!existsSync20(settingsPath))
20186
+ if (!existsSync21(settingsPath))
20950
20187
  return false;
20951
20188
  try {
20952
20189
  const settings = this.readSettings(settingsPath);
@@ -21006,8 +20243,8 @@ var init_integrations = __esm(() => {
21006
20243
  const effectiveScope = scope ?? "project";
21007
20244
  const settingsPath = effectiveScope === "user" ? resolve17(homedir22(), ".config", "opencode", "opencode.json") : resolve17(process.cwd(), ".opencode", "opencode.json");
21008
20245
  const pluginPath = opencodePluginFilePath(settingsPath);
21009
- mkdirSync13(dirname10(pluginPath), { recursive: true });
21010
- writeFileSync10(pluginPath, buildOpenCodePluginShim(binaryPath, effectiveScope), "utf8");
20246
+ mkdirSync14(dirname11(pluginPath), { recursive: true });
20247
+ writeFileSync12(pluginPath, buildOpenCodePluginShim(binaryPath, effectiveScope), "utf8");
21011
20248
  if (!Array.isArray(s.plugin))
21012
20249
  s.plugin = [];
21013
20250
  const desired = effectiveScope === "user" ? `file://${pluginPath}` : OPENCODE_PLUGIN_REL_PATH;
@@ -21030,9 +20267,9 @@ var init_integrations = __esm(() => {
21030
20267
  }
21031
20268
  this.writeSettings(settingsPath, settings);
21032
20269
  const pluginPath = opencodePluginFilePath(settingsPath);
21033
- if (existsSync20(pluginPath)) {
20270
+ if (existsSync21(pluginPath)) {
21034
20271
  try {
21035
- const content = readFileSync17(pluginPath, "utf8");
20272
+ const content = readFileSync19(pluginPath, "utf8");
21036
20273
  if (content.includes(FAILPROOFAI_HOOK_MARKER)) {
21037
20274
  unlinkSync3(pluginPath);
21038
20275
  if (removed === 0)
@@ -21044,7 +20281,7 @@ var init_integrations = __esm(() => {
21044
20281
  },
21045
20282
  hooksInstalledInSettings(scope, cwd) {
21046
20283
  const settingsPath = this.getSettingsPath(scope, cwd);
21047
- if (!existsSync20(settingsPath))
20284
+ if (!existsSync21(settingsPath))
21048
20285
  return false;
21049
20286
  try {
21050
20287
  const settings = this.readSettings(settingsPath);
@@ -21054,9 +20291,9 @@ var init_integrations = __esm(() => {
21054
20291
  if (!hasEntry)
21055
20292
  return false;
21056
20293
  const pluginPath = opencodePluginFilePath(settingsPath);
21057
- if (!existsSync20(pluginPath))
20294
+ if (!existsSync21(pluginPath))
21058
20295
  return false;
21059
- const content = readFileSync17(pluginPath, "utf8");
20296
+ const content = readFileSync19(pluginPath, "utf8");
21060
20297
  return content.includes(FAILPROOFAI_HOOK_MARKER);
21061
20298
  } catch {
21062
20299
  return false;
@@ -21121,7 +20358,7 @@ var init_integrations = __esm(() => {
21121
20358
  }
21122
20359
  },
21123
20360
  removeHooksFromFile(settingsPath) {
21124
- if (!existsSync20(settingsPath))
20361
+ if (!existsSync21(settingsPath))
21125
20362
  return 0;
21126
20363
  const settings = this.readSettings(settingsPath);
21127
20364
  if (!Array.isArray(settings.packages))
@@ -21136,7 +20373,7 @@ var init_integrations = __esm(() => {
21136
20373
  },
21137
20374
  hooksInstalledInSettings(scope, cwd) {
21138
20375
  const settingsPath = this.getSettingsPath(scope, cwd);
21139
- if (!existsSync20(settingsPath))
20376
+ if (!existsSync21(settingsPath))
21140
20377
  return false;
21141
20378
  try {
21142
20379
  const settings = this.readSettings(settingsPath);
@@ -21195,7 +20432,7 @@ var init_integrations = __esm(() => {
21195
20432
  doc.set("hooks_auto_accept", true);
21196
20433
  },
21197
20434
  removeHooksFromFile(settingsPath) {
21198
- if (!existsSync20(settingsPath))
20435
+ if (!existsSync21(settingsPath))
21199
20436
  return 0;
21200
20437
  const doc = readYamlDoc(settingsPath);
21201
20438
  const js = doc.toJS() ?? {};
@@ -21285,7 +20522,7 @@ var init_integrations = __esm(() => {
21285
20522
  hooks.allowConversationAccess = true;
21286
20523
  },
21287
20524
  removeHooksFromFile(settingsPath) {
21288
- if (!existsSync20(settingsPath))
20525
+ if (!existsSync21(settingsPath))
21289
20526
  return 0;
21290
20527
  const settings = this.readSettings(settingsPath);
21291
20528
  const plugins = settings.plugins;
@@ -21314,7 +20551,7 @@ var init_integrations = __esm(() => {
21314
20551
  },
21315
20552
  hooksInstalledInSettings(scope, cwd) {
21316
20553
  const settingsPath = this.getSettingsPath(scope, cwd);
21317
- if (!existsSync20(settingsPath))
20554
+ if (!existsSync21(settingsPath))
21318
20555
  return false;
21319
20556
  try {
21320
20557
  const settings = this.readSettings(settingsPath);
@@ -21411,7 +20648,7 @@ var init_integrations = __esm(() => {
21411
20648
  },
21412
20649
  hooksInstalledInSettings(scope, cwd) {
21413
20650
  const settingsPath = this.getSettingsPath(scope, cwd);
21414
- if (!existsSync20(settingsPath))
20651
+ if (!existsSync21(settingsPath))
21415
20652
  return false;
21416
20653
  try {
21417
20654
  const settings = this.readSettings(settingsPath);
@@ -21517,7 +20754,7 @@ var init_integrations = __esm(() => {
21517
20754
  },
21518
20755
  hooksInstalledInSettings(scope, cwd) {
21519
20756
  const settingsPath = this.getSettingsPath(scope, cwd);
21520
- if (!existsSync20(settingsPath))
20757
+ if (!existsSync21(settingsPath))
21521
20758
  return false;
21522
20759
  try {
21523
20760
  const settings = this.readSettings(settingsPath);
@@ -21656,7 +20893,7 @@ var init_integrations = __esm(() => {
21656
20893
  },
21657
20894
  hooksInstalledInSettings(scope, cwd) {
21658
20895
  const settingsPath = this.getSettingsPath(scope, cwd);
21659
- if (!existsSync20(settingsPath))
20896
+ if (!existsSync21(settingsPath))
21660
20897
  return false;
21661
20898
  try {
21662
20899
  const settings = this.readSettings(settingsPath);
@@ -21762,7 +20999,7 @@ var init_integrations = __esm(() => {
21762
20999
  },
21763
21000
  hooksInstalledInSettings(scope, cwd) {
21764
21001
  const settingsPath = this.getSettingsPath(scope, cwd);
21765
- if (!existsSync20(settingsPath))
21002
+ if (!existsSync21(settingsPath))
21766
21003
  return false;
21767
21004
  try {
21768
21005
  const settings = this.readSettings(settingsPath);
@@ -22041,7 +21278,7 @@ var init_cli_error = __esm(() => {
22041
21278
 
22042
21279
  // src/hooks/manager.ts
22043
21280
  import { execSync as execSync6 } from "node:child_process";
22044
- import { existsSync as existsSync21 } from "node:fs";
21281
+ import { existsSync as existsSync22 } from "node:fs";
22045
21282
  import { resolve as resolve18, basename as basename4 } from "node:path";
22046
21283
  import { platform as platform3, arch as arch3, release as release2, hostname as hostname3 } from "node:os";
22047
21284
  function getSettingsPath(scope, cwd) {
@@ -22152,7 +21389,7 @@ async function installHooksImpl(policyNames, scope = "user", cwd, includeBeta =
22152
21389
  } else if (customPoliciesPath && (typeof customPoliciesPath === "string" || customPoliciesPath.length > 0)) {
22153
21390
  const incoming = (typeof customPoliciesPath === "string" ? [customPoliciesPath] : customPoliciesPath).map((path3) => resolve18(path3));
22154
21391
  const carried = (previousConfig.customPoliciesPaths ?? (previousConfig.customPoliciesPath ? [previousConfig.customPoliciesPath] : [])).map((path3) => resolve18(path3)).filter((path3) => {
22155
- if (existsSync21(path3))
21392
+ if (existsSync22(path3))
22156
21393
  return true;
22157
21394
  console.log(`Dropping custom policies path (file no longer exists): ${path3}`);
22158
21395
  return false;
@@ -22344,7 +21581,7 @@ async function removeHooks(policyNames, scope = "user", cwd, opts) {
22344
21581
  const scopesToRemove = scope === "all" ? [...integration.scopes] : integration.scopes.includes(scope) ? [scope] : [];
22345
21582
  for (const s of scopesToRemove) {
22346
21583
  const settingsPaths = settingsPathsFor(integration, s, cwd);
22347
- const existing = settingsPaths.filter((p) => existsSync21(p));
21584
+ const existing = settingsPaths.filter((p) => existsSync22(p));
22348
21585
  if (existing.length === 0) {
22349
21586
  if (scope !== "all" && selectedClis.length === 1) {
22350
21587
  console.log("No settings file found. Nothing to remove.");
@@ -22424,6 +21661,7 @@ var init_manager = __esm(() => {
22424
21661
  init_cli_error();
22425
21662
  init_hook_logger();
22426
21663
  init_fp_home();
21664
+ init_cloud_managed_policies();
22427
21665
  VALID_POLICY_NAMES = new Set(BUILTIN_POLICIES.map((p) => p.name));
22428
21666
  });
22429
21667
 
@@ -22488,7 +21726,7 @@ var DEFAULT_FETCH_TIMEOUT_MS = 15000;
22488
21726
 
22489
21727
  // src/hooks/cloud-enrollment.ts
22490
21728
  import { randomUUID as randomUUID3 } from "node:crypto";
22491
- import { existsSync as existsSync22, readFileSync as readFileSync18, rmSync as rmSync7 } from "node:fs";
21729
+ import { existsSync as existsSync23, readFileSync as readFileSync20, rmSync as rmSync8 } from "node:fs";
22492
21730
  import { hostname as hostname4 } from "node:os";
22493
21731
  function cloudCredentialPath() {
22494
21732
  return process.env.FAILPROOFAI_CLOUD_CREDENTIALS ?? credentialsFile();
@@ -22527,10 +21765,10 @@ function readCloudCredentials() {
22527
21765
  if (!usingJsonOverride())
22528
21766
  return readCredentials().cloud ?? null;
22529
21767
  const path3 = cloudCredentialPath();
22530
- if (!existsSync22(path3))
21768
+ if (!existsSync23(path3))
22531
21769
  return null;
22532
21770
  try {
22533
- const raw = JSON.parse(readFileSync18(path3, "utf8"));
21771
+ const raw = JSON.parse(readFileSync20(path3, "utf8"));
22534
21772
  if (raw.schemaVersion !== SCHEMA_VERSION2)
22535
21773
  return null;
22536
21774
  if (typeof raw.url !== "string" || typeof raw.machineId !== "string" || typeof raw.token !== "string") {
@@ -22566,10 +21804,10 @@ function resolveMachineLabel(explicit) {
22566
21804
  function clearCloudCredentials() {
22567
21805
  if (usingJsonOverride()) {
22568
21806
  const path3 = cloudCredentialPath();
22569
- if (!existsSync22(path3))
21807
+ if (!existsSync23(path3))
22570
21808
  return false;
22571
21809
  try {
22572
- rmSync7(path3);
21810
+ rmSync8(path3);
22573
21811
  return true;
22574
21812
  } catch {
22575
21813
  return false;
@@ -22610,7 +21848,7 @@ async function verifyCloudCredentials(creds) {
22610
21848
  return {
22611
21849
  ok: true,
22612
21850
  policyCount: Array.isArray(body.policies) ? body.policies.length : 0,
22613
- generation: typeof body.generation === "number" ? body.generation : 0
21851
+ deployment: typeof body.deployment === "number" ? body.deployment : 0
22614
21852
  };
22615
21853
  } catch {
22616
21854
  return {
@@ -22735,7 +21973,7 @@ async function connectToCloud(input) {
22735
21973
  canIngest ? verifyIngest({ url: ingestUrl, key: input.token }) : Promise.resolve({ ok: false, reason: missing(PERMISSION_EVENTS) })
22736
21974
  ]);
22737
21975
  const outcome = {
22738
- policy: policyResult.ok ? { ok: true, policyCount: policyResult.policyCount, generation: policyResult.generation } : { ok: false, reason: policyResult.reason },
21976
+ policy: policyResult.ok ? { ok: true, policyCount: policyResult.policyCount, deployment: policyResult.deployment } : { ok: false, reason: policyResult.reason },
22739
21977
  ingest: ingestResult.ok ? { ok: true } : { ok: false, reason: ingestResult.reason },
22740
21978
  anyConfigured: false,
22741
21979
  org: known ? { id: known.orgId, slug: known.orgSlug, name: known.orgName } : undefined
@@ -22773,14 +22011,14 @@ function describeOutcome(outcome, machineId, url) {
22773
22011
  if (outcome.policy.ok && outcome.ingest.ok) {
22774
22012
  lines.push(`Connected to ${url} as ${machineId}${into}.`);
22775
22013
  const n = outcome.policy.policyCount ?? 0;
22776
- lines.push(` Policy ${n} polic${n === 1 ? "y" : "ies"} assigned (generation ${outcome.policy.generation ?? 0}).`);
22014
+ lines.push(` Policy ${n} polic${n === 1 ? "y" : "ies"} assigned (deployment ${outcome.policy.deployment ?? 0}).`);
22777
22015
  lines.push(` Dashboard hook activity will be sent to ${ingestUrlFor(url)}.`);
22778
22016
  return lines;
22779
22017
  }
22780
22018
  if (outcome.policy.ok && !outcome.ingest.ok) {
22781
22019
  const n = outcome.policy.policyCount ?? 0;
22782
22020
  lines.push(`Connected to ${url} as ${machineId}${into}, for policy only.`);
22783
- lines.push(` Policy ${n} polic${n === 1 ? "y" : "ies"} assigned (generation ${outcome.policy.generation ?? 0}).`);
22021
+ lines.push(` Policy ${n} polic${n === 1 ? "y" : "ies"} assigned (deployment ${outcome.policy.deployment ?? 0}).`);
22784
22022
  lines.push(` Dashboard NOT configured: ${outcome.ingest.reason}`);
22785
22023
  lines.push("");
22786
22024
  lines.push(" Enforcement works. Nothing will appear in the dashboard until this");
@@ -22817,8 +22055,8 @@ var init_cloud_connection = __esm(() => {
22817
22055
  });
22818
22056
 
22819
22057
  // src/hooks/setup-state.ts
22820
- import { existsSync as existsSync23, realpathSync as realpathSync2, statSync as statSync15 } from "node:fs";
22821
- import { dirname as dirname11, resolve as resolve19 } from "node:path";
22058
+ import { existsSync as existsSync24, realpathSync as realpathSync2, statSync as statSync17 } from "node:fs";
22059
+ import { dirname as dirname12, resolve as resolve19 } from "node:path";
22822
22060
  import { homedir as homedir25 } from "node:os";
22823
22061
  function globalConfigPath(home = homedir25()) {
22824
22062
  return globalPolicyConfigFile(home);
@@ -22831,7 +22069,7 @@ function legacyMarkerPath(home) {
22831
22069
  }
22832
22070
  function isDir(path3) {
22833
22071
  try {
22834
- return statSync15(path3).isDirectory();
22072
+ return statSync17(path3).isDirectory();
22835
22073
  } catch {
22836
22074
  return false;
22837
22075
  }
@@ -22842,7 +22080,7 @@ function findProjectRoot(start, home = homedir25()) {
22842
22080
  while (dir !== resolvedHome) {
22843
22081
  if (isDir(resolve19(dir, ".failproofai")))
22844
22082
  return dir;
22845
- const parent = dirname11(dir);
22083
+ const parent = dirname12(dir);
22846
22084
  if (parent === dir)
22847
22085
  break;
22848
22086
  dir = parent;
@@ -22874,10 +22112,10 @@ function detectSetupState(cwd = process.cwd(), home = homedir25()) {
22874
22112
  cwd: resolvedCwd,
22875
22113
  home: resolvedHome,
22876
22114
  projectRoot,
22877
- hasGlobalConfig: existsSync23(globalConfigPath(resolvedHome)),
22878
- hasProjectConfig: projectRoot !== resolvedHome && existsSync23(projectConfigPath(projectRoot)),
22115
+ hasGlobalConfig: existsSync24(globalConfigPath(resolvedHome)),
22116
+ hasProjectConfig: projectRoot !== resolvedHome && existsSync24(projectConfigPath(projectRoot)),
22879
22117
  hasGlobalHooks: hasGlobalHooksInstalled(),
22880
- hasLegacyMarker: existsSync23(legacyMarkerPath(resolvedHome)),
22118
+ hasLegacyMarker: existsSync24(legacyMarkerPath(resolvedHome)),
22881
22119
  inHomeDir: resolvedCwd === resolvedHome
22882
22120
  };
22883
22121
  }
@@ -22925,14 +22163,14 @@ var init_setup_state = __esm(() => {
22925
22163
  });
22926
22164
 
22927
22165
  // src/hooks/onboarding-attempt.ts
22928
- import { existsSync as existsSync24, mkdirSync as mkdirSync14, readFileSync as readFileSync19, rmSync as rmSync8, writeFileSync as writeFileSync11 } from "node:fs";
22929
- import { dirname as dirname12 } from "node:path";
22166
+ import { existsSync as existsSync25, mkdirSync as mkdirSync15, readFileSync as readFileSync21, rmSync as rmSync9, writeFileSync as writeFileSync13 } from "node:fs";
22167
+ import { dirname as dirname13 } from "node:path";
22930
22168
  function readOnboardingAttempt() {
22931
22169
  const path3 = onboardingAttemptFile();
22932
- if (!existsSync24(path3))
22170
+ if (!existsSync25(path3))
22933
22171
  return null;
22934
22172
  try {
22935
- const raw = JSON.parse(readFileSync19(path3, "utf8"));
22173
+ const raw = JSON.parse(readFileSync21(path3, "utf8"));
22936
22174
  if (raw.schemaVersion !== SCHEMA_VERSION3)
22937
22175
  return null;
22938
22176
  if (typeof raw.reason !== "string" || typeof raw.cliVersion !== "string")
@@ -22951,7 +22189,7 @@ function readOnboardingAttempt() {
22951
22189
  function recordOnboardingAttempt(reason, cliVersion, daemonStatus, now = Date.now()) {
22952
22190
  try {
22953
22191
  const path3 = onboardingAttemptFile();
22954
- mkdirSync14(dirname12(path3), { recursive: true });
22192
+ mkdirSync15(dirname13(path3), { recursive: true });
22955
22193
  const record = {
22956
22194
  schemaVersion: SCHEMA_VERSION3,
22957
22195
  reason,
@@ -22959,12 +22197,12 @@ function recordOnboardingAttempt(reason, cliVersion, daemonStatus, now = Date.no
22959
22197
  daemonStatus,
22960
22198
  at: now
22961
22199
  };
22962
- writeFileSync11(path3, JSON.stringify(record, null, 2), "utf8");
22200
+ writeFileSync13(path3, JSON.stringify(record, null, 2), "utf8");
22963
22201
  } catch {}
22964
22202
  }
22965
22203
  function clearOnboardingAttempt() {
22966
22204
  try {
22967
- rmSync8(onboardingAttemptFile(), { force: true });
22205
+ rmSync9(onboardingAttemptFile(), { force: true });
22968
22206
  } catch {}
22969
22207
  }
22970
22208
  function blockerCleared(attempt, probe) {
@@ -22975,6 +22213,8 @@ function blockerCleared(attempt, probe) {
22975
22213
  return probe.daemonStatus() !== attempt.daemonStatus || probe.canElevate();
22976
22214
  case "cancelled":
22977
22215
  return probe.cliVersion !== attempt.cliVersion;
22216
+ case "unsupported_platform":
22217
+ return probe.cliVersion !== attempt.cliVersion;
22978
22218
  case "not_a_tty":
22979
22219
  case "running_as_sudo":
22980
22220
  return true;
@@ -22987,14 +22227,16 @@ function attemptHintLines(attempt) {
22987
22227
  needs_root: "it needs root to install the failproofaid service",
22988
22228
  daemon_failed: "the failproofaid service could not be started",
22989
22229
  cancelled: "it was cancelled",
22230
+ unsupported_platform: "failproofaid does not run on this platform",
22990
22231
  not_a_tty: "there was no terminal to ask in",
22991
22232
  running_as_sudo: "it was run under sudo"
22992
22233
  };
22993
22234
  const detail = why[attempt.reason] ?? "it did not finish";
22235
+ const action = attempt.reason === "unsupported_platform" ? "Check for a failproofai update — this platform may be supported by a newer release." : "Run `failproofai config` when you are ready.";
22994
22236
  return [
22995
22237
  ``,
22996
22238
  `[failproofai] Setup is not finished — ${detail}.`,
22997
- ` Run \`failproofai config\` when you are ready.`,
22239
+ ` ${action}`,
22998
22240
  ``
22999
22241
  ];
23000
22242
  }
@@ -23004,7 +22246,7 @@ var init_onboarding_attempt = __esm(() => {
23004
22246
  });
23005
22247
 
23006
22248
  // src/hooks/onboarding-lock.ts
23007
- import { mkdirSync as mkdirSync15, readFileSync as readFileSync20, rmSync as rmSync9, writeFileSync as writeFileSync12 } from "node:fs";
22249
+ import { mkdirSync as mkdirSync16, readFileSync as readFileSync22, rmSync as rmSync10, writeFileSync as writeFileSync14 } from "node:fs";
23008
22250
  import { homedir as homedir26 } from "node:os";
23009
22251
  function onboardingLockPath(home = homedir26()) {
23010
22252
  return onboardingLockFile(home);
@@ -23021,7 +22263,7 @@ function processAlive(pid) {
23021
22263
  }
23022
22264
  function readLock(path3) {
23023
22265
  try {
23024
- const parsed = JSON.parse(readFileSync20(path3, "utf8"));
22266
+ const parsed = JSON.parse(readFileSync22(path3, "utf8"));
23025
22267
  if (typeof parsed.pid !== "number")
23026
22268
  return null;
23027
22269
  return { pid: parsed.pid, startedAt: Number(parsed.startedAt) || 0 };
@@ -23035,9 +22277,9 @@ function acquireOnboardingLock(home = homedir26()) {
23035
22277
  const held = readLock(path3);
23036
22278
  if (held && held.pid !== process.pid && processAlive(held.pid))
23037
22279
  return null;
23038
- mkdirSync15(stateDir(home), { recursive: true });
22280
+ mkdirSync16(stateDir(home), { recursive: true });
23039
22281
  const body = { pid: process.pid, startedAt: Date.now() };
23040
- writeFileSync12(path3, JSON.stringify(body), "utf8");
22282
+ writeFileSync14(path3, JSON.stringify(body), "utf8");
23041
22283
  let released = false;
23042
22284
  return {
23043
22285
  release() {
@@ -23047,7 +22289,7 @@ function acquireOnboardingLock(home = homedir26()) {
23047
22289
  try {
23048
22290
  const current = readLock(path3);
23049
22291
  if (!current || current.pid === process.pid)
23050
- rmSync9(path3, { force: true });
22292
+ rmSync10(path3, { force: true });
23051
22293
  } catch {}
23052
22294
  }
23053
22295
  };
@@ -23060,7 +22302,7 @@ var init_onboarding_lock = __esm(() => {
23060
22302
  });
23061
22303
 
23062
22304
  // lib/claude-sessions.ts
23063
- import { readdirSync as readdirSync14, statSync as statSync16, existsSync as existsSync26 } from "node:fs";
22305
+ import { readdirSync as readdirSync15, statSync as statSync18, existsSync as existsSync27 } from "node:fs";
23064
22306
  import { join as join28, basename as basename5 } from "node:path";
23065
22307
  function getClaudeProjectsRoot() {
23066
22308
  return getClaudeProjectsPath();
@@ -23069,7 +22311,7 @@ function listClaudeProjects() {
23069
22311
  const root = getClaudeProjectsRoot();
23070
22312
  let entries;
23071
22313
  try {
23072
- entries = readdirSync14(root, { withFileTypes: true });
22314
+ entries = readdirSync15(root, { withFileTypes: true });
23073
22315
  } catch {
23074
22316
  return [];
23075
22317
  }
@@ -23083,7 +22325,7 @@ function listClaudeTranscripts(project) {
23083
22325
  const out = [];
23084
22326
  let entries;
23085
22327
  try {
23086
- entries = readdirSync14(project.path, { withFileTypes: true });
22328
+ entries = readdirSync15(project.path, { withFileTypes: true });
23087
22329
  } catch {
23088
22330
  return out;
23089
22331
  }
@@ -23094,7 +22336,7 @@ function listClaudeTranscripts(project) {
23094
22336
  continue;
23095
22337
  const transcriptPath = join28(project.path, entry.name);
23096
22338
  try {
23097
- const s = statSync16(transcriptPath);
22339
+ const s = statSync18(transcriptPath);
23098
22340
  out.push({
23099
22341
  projectName: project.name,
23100
22342
  cwd: project.cwd,
@@ -23107,11 +22349,11 @@ function listClaudeTranscripts(project) {
23107
22349
  } catch {}
23108
22350
  } else if (entry.isDirectory() && UUID_RE2.test(entry.name)) {
23109
22351
  const subDir = join28(project.path, entry.name, "subagents");
23110
- if (!existsSync26(subDir))
22352
+ if (!existsSync27(subDir))
23111
22353
  continue;
23112
22354
  let subEntries;
23113
22355
  try {
23114
- subEntries = readdirSync14(subDir, { withFileTypes: true });
22356
+ subEntries = readdirSync15(subDir, { withFileTypes: true });
23115
22357
  } catch {
23116
22358
  continue;
23117
22359
  }
@@ -23121,7 +22363,7 @@ function listClaudeTranscripts(project) {
23121
22363
  const agentId = sub.name.slice(0, -".jsonl".length);
23122
22364
  const transcriptPath = join28(subDir, sub.name);
23123
22365
  try {
23124
- const s = statSync16(transcriptPath);
22366
+ const s = statSync18(transcriptPath);
23125
22367
  out.push({
23126
22368
  projectName: project.name,
23127
22369
  cwd: project.cwd,
@@ -23265,7 +22507,7 @@ var init_claude = __esm(() => {
23265
22507
  });
23266
22508
 
23267
22509
  // src/audit/cli-adapters/codex.ts
23268
- import { statSync as statSync17 } from "node:fs";
22510
+ import { statSync as statSync19 } from "node:fs";
23269
22511
  async function listCodexTranscriptMetadata(opts = {}) {
23270
22512
  const projectFilter = opts.projects ? new Set(opts.projects) : null;
23271
22513
  const sinceMs = opts.sinceMs ?? 0;
@@ -23282,7 +22524,7 @@ async function listCodexTranscriptMetadata(opts = {}) {
23282
22524
  continue;
23283
22525
  let sizeBytes = 0;
23284
22526
  try {
23285
- sizeBytes = statSync17(s.path).size;
22527
+ sizeBytes = statSync19(s.path).size;
23286
22528
  } catch {}
23287
22529
  if (!s.sessionId)
23288
22530
  continue;
@@ -23316,7 +22558,7 @@ var init_codex = __esm(() => {
23316
22558
  });
23317
22559
 
23318
22560
  // src/audit/cli-adapters/copilot.ts
23319
- import { statSync as statSync18 } from "node:fs";
22561
+ import { statSync as statSync20 } from "node:fs";
23320
22562
  async function listCopilotTranscriptMetadata(opts = {}) {
23321
22563
  const projectFilter = opts.projects ? new Set(opts.projects) : null;
23322
22564
  const sinceMs = opts.sinceMs ?? 0;
@@ -23333,7 +22575,7 @@ async function listCopilotTranscriptMetadata(opts = {}) {
23333
22575
  continue;
23334
22576
  let sizeBytes = 0;
23335
22577
  try {
23336
- sizeBytes = statSync18(s.path).size;
22578
+ sizeBytes = statSync20(s.path).size;
23337
22579
  } catch {}
23338
22580
  if (!s.sessionId)
23339
22581
  continue;
@@ -23367,7 +22609,7 @@ var init_copilot = __esm(() => {
23367
22609
  });
23368
22610
 
23369
22611
  // src/audit/cli-adapters/cursor.ts
23370
- import { statSync as statSync19 } from "node:fs";
22612
+ import { statSync as statSync21 } from "node:fs";
23371
22613
  async function listCursorTranscriptMetadata(opts = {}) {
23372
22614
  const projectFilter = opts.projects ? new Set(opts.projects) : null;
23373
22615
  const sinceMs = opts.sinceMs ?? 0;
@@ -23384,7 +22626,7 @@ async function listCursorTranscriptMetadata(opts = {}) {
23384
22626
  continue;
23385
22627
  let sizeBytes = 0;
23386
22628
  try {
23387
- sizeBytes = statSync19(s.path).size;
22629
+ sizeBytes = statSync21(s.path).size;
23388
22630
  } catch {}
23389
22631
  if (!s.sessionId)
23390
22632
  continue;
@@ -23630,7 +22872,7 @@ var init_opencode = __esm(() => {
23630
22872
  });
23631
22873
 
23632
22874
  // src/audit/cli-adapters/pi.ts
23633
- import { statSync as statSync20 } from "node:fs";
22875
+ import { statSync as statSync22 } from "node:fs";
23634
22876
  async function listPiTranscriptMetadata(opts = {}) {
23635
22877
  const projectFilter = opts.projects ? new Set(opts.projects) : null;
23636
22878
  const sinceMs = opts.sinceMs ?? 0;
@@ -23647,7 +22889,7 @@ async function listPiTranscriptMetadata(opts = {}) {
23647
22889
  continue;
23648
22890
  let sizeBytes = 0;
23649
22891
  try {
23650
- sizeBytes = statSync20(s.path).size;
22892
+ sizeBytes = statSync22(s.path).size;
23651
22893
  } catch {}
23652
22894
  if (!s.sessionId)
23653
22895
  continue;
@@ -24382,7 +23624,7 @@ var init_features = __esm(() => {
24382
23624
 
24383
23625
  // src/audit/cache.ts
24384
23626
  import { createHash as createHash6 } from "node:crypto";
24385
- import { existsSync as existsSync27, mkdirSync as mkdirSync16, readFileSync as readFileSync21, writeFileSync as writeFileSync13, chmodSync as chmodSync4 } from "node:fs";
23627
+ import { existsSync as existsSync28, mkdirSync as mkdirSync17, readFileSync as readFileSync23, writeFileSync as writeFileSync15, chmodSync as chmodSync4 } from "node:fs";
24386
23628
  import { join as join29 } from "node:path";
24387
23629
  function getEngineVersion() {
24388
23630
  if (cachedEngineVersion)
@@ -24409,10 +23651,10 @@ function readCachedTranscriptResult(transcriptPath, mtimeMs, sizeBytes) {
24409
23651
  if (sizeBytes === 0)
24410
23652
  return null;
24411
23653
  const cachePath = getCachePathFor(transcriptPath);
24412
- if (!existsSync27(cachePath))
23654
+ if (!existsSync28(cachePath))
24413
23655
  return null;
24414
23656
  try {
24415
- const raw = readFileSync21(cachePath, "utf-8");
23657
+ const raw = readFileSync23(cachePath, "utf-8");
24416
23658
  const entry = JSON.parse(raw);
24417
23659
  if (entry.schemaVersion !== CACHE_SCHEMA_VERSION)
24418
23660
  return null;
@@ -24436,7 +23678,7 @@ function writeCachedTranscriptResult(transcriptPath, mtimeMs, sizeBytes, result)
24436
23678
  return;
24437
23679
  const cachePath = getCachePathFor(transcriptPath);
24438
23680
  try {
24439
- mkdirSync16(auditCacheDir(), { recursive: true });
23681
+ mkdirSync17(auditCacheDir(), { recursive: true });
24440
23682
  const entry = {
24441
23683
  schemaVersion: CACHE_SCHEMA_VERSION,
24442
23684
  cachedAt: Date.now(),
@@ -24446,7 +23688,7 @@ function writeCachedTranscriptResult(transcriptPath, mtimeMs, sizeBytes, result)
24446
23688
  detectorVersion: getDetectorVersion(),
24447
23689
  result
24448
23690
  };
24449
- writeFileSync13(cachePath, JSON.stringify(entry), { encoding: "utf-8", mode: 384 });
23691
+ writeFileSync15(cachePath, JSON.stringify(entry), { encoding: "utf-8", mode: 384 });
24450
23692
  try {
24451
23693
  chmodSync4(cachePath, 384);
24452
23694
  } catch {}
@@ -24812,11 +24054,11 @@ var init_audit = __esm(() => {
24812
24054
  import {
24813
24055
  closeSync,
24814
24056
  linkSync,
24815
- mkdirSync as mkdirSync17,
24057
+ mkdirSync as mkdirSync18,
24816
24058
  openSync,
24817
- readFileSync as readFileSync22,
24059
+ readFileSync as readFileSync24,
24818
24060
  unlinkSync as unlinkSync4,
24819
- writeFileSync as writeFileSync14,
24061
+ writeFileSync as writeFileSync16,
24820
24062
  writeSync
24821
24063
  } from "node:fs";
24822
24064
  function acquireAuditLock(source, now = Date.now()) {
@@ -24875,7 +24117,7 @@ function handleFor(path3, info) {
24875
24117
  }
24876
24118
  function readLock2(path3) {
24877
24119
  try {
24878
- const parsed = JSON.parse(readFileSync22(path3, "utf8"));
24120
+ const parsed = JSON.parse(readFileSync24(path3, "utf8"));
24879
24121
  if (typeof parsed?.pid !== "number" || typeof parsed?.startedAt !== "number")
24880
24122
  return null;
24881
24123
  return {
@@ -24891,7 +24133,7 @@ function writeLockExclusive(path3, info) {
24891
24133
  const body = JSON.stringify(info);
24892
24134
  const staging = `${path3}.${process.pid}.tmp`;
24893
24135
  try {
24894
- writeFileSync14(staging, body, { mode: 384 });
24136
+ writeFileSync16(staging, body, { mode: 384 });
24895
24137
  } catch {
24896
24138
  return false;
24897
24139
  }
@@ -24925,8 +24167,8 @@ function createThenWrite(path3, body) {
24925
24167
  }
24926
24168
  }
24927
24169
  function ensureRunDir() {
24928
- mkdirSync17(failproofaiHome(), { recursive: true });
24929
- mkdirSync17(runDir(), { recursive: true, mode: 448 });
24170
+ mkdirSync18(failproofaiHome(), { recursive: true });
24171
+ mkdirSync18(runDir(), { recursive: true, mode: 448 });
24930
24172
  }
24931
24173
  var AUDIT_LOCK_MAX_AGE_MS;
24932
24174
  var init_audit_lock = __esm(() => {
@@ -25090,26 +24332,26 @@ function parseScriptArgs(argv) {
25090
24332
  var init_parse_script_args = () => {};
25091
24333
 
25092
24334
  // scripts/install-diagnosis.mjs
25093
- import { existsSync as existsSync28, readFileSync as readFileSync23, realpathSync as realpathSync3 } from "node:fs";
25094
- import { dirname as dirname13, resolve as resolve21 } from "node:path";
24335
+ import { existsSync as existsSync29, readFileSync as readFileSync25, realpathSync as realpathSync3 } from "node:fs";
24336
+ import { dirname as dirname14, resolve as resolve21 } from "node:path";
25095
24337
  import { homedir as homedir27, platform as platform4 } from "node:os";
25096
24338
  import { spawnSync } from "node:child_process";
25097
24339
  function findPackageRoot(start) {
25098
24340
  try {
25099
24341
  let dir = realpathSync3(start);
25100
- if (existsSync28(dir) && !existsSync28(resolve21(dir, "package.json"))) {
25101
- dir = dirname13(dir);
24342
+ if (existsSync29(dir) && !existsSync29(resolve21(dir, "package.json"))) {
24343
+ dir = dirname14(dir);
25102
24344
  }
25103
24345
  while (true) {
25104
24346
  const pkgPath = resolve21(dir, "package.json");
25105
- if (existsSync28(pkgPath)) {
24347
+ if (existsSync29(pkgPath)) {
25106
24348
  try {
25107
- const pkg = JSON.parse(readFileSync23(pkgPath, "utf8"));
24349
+ const pkg = JSON.parse(readFileSync25(pkgPath, "utf8"));
25108
24350
  if (pkg.name === PKG_NAME)
25109
24351
  return dir;
25110
24352
  } catch {}
25111
24353
  }
25112
- const parent = dirname13(dir);
24354
+ const parent = dirname14(dir);
25113
24355
  if (parent === dir)
25114
24356
  return null;
25115
24357
  dir = parent;
@@ -25122,7 +24364,7 @@ function readPackageVersion(packageRoot) {
25122
24364
  if (!packageRoot)
25123
24365
  return null;
25124
24366
  try {
25125
- const pkg = JSON.parse(readFileSync23(resolve21(packageRoot, "package.json"), "utf8"));
24367
+ const pkg = JSON.parse(readFileSync25(resolve21(packageRoot, "package.json"), "utf8"));
25126
24368
  return typeof pkg.version === "string" ? pkg.version : null;
25127
24369
  } catch {
25128
24370
  return null;
@@ -25149,7 +24391,7 @@ function locateNpmGlobal() {
25149
24391
  if (!root)
25150
24392
  return null;
25151
24393
  const candidate = resolve21(root, PKG_NAME);
25152
- return existsSync28(resolve21(candidate, "package.json")) ? candidate : null;
24394
+ return existsSync29(resolve21(candidate, "package.json")) ? candidate : null;
25153
24395
  } catch {
25154
24396
  return null;
25155
24397
  }
@@ -25157,7 +24399,7 @@ function locateNpmGlobal() {
25157
24399
  function locateBunGlobal() {
25158
24400
  try {
25159
24401
  const candidate = resolve21(homedir27(), ".bun", "install", "global", "node_modules", PKG_NAME);
25160
- return existsSync28(resolve21(candidate, "package.json")) ? candidate : null;
24402
+ return existsSync29(resolve21(candidate, "package.json")) ? candidate : null;
25161
24403
  } catch {
25162
24404
  return null;
25163
24405
  }
@@ -25264,8 +24506,8 @@ __export(exports_launch, {
25264
24506
  launch: () => launch
25265
24507
  });
25266
24508
  import { spawn as spawn2 } from "child_process";
25267
- import { realpathSync as realpathSync4, existsSync as existsSync29 } from "node:fs";
25268
- import { resolve as resolve22, dirname as dirname14 } from "node:path";
24509
+ import { realpathSync as realpathSync4, existsSync as existsSync30 } from "node:fs";
24510
+ import { resolve as resolve22, dirname as dirname15 } from "node:path";
25269
24511
  import { fileURLToPath as fileURLToPath2 } from "node:url";
25270
24512
  import { createInterface } from "node:readline";
25271
24513
  function launch(mode) {
@@ -25289,9 +24531,9 @@ function launch(mode) {
25289
24531
  process.env.PORT = port;
25290
24532
  process.env.HOSTNAME = bindHost;
25291
24533
  cmd = "node";
25292
- const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve22(dirname14(realpathSync4(fileURLToPath2(import.meta.url))), "..");
24534
+ const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve22(dirname15(realpathSync4(fileURLToPath2(import.meta.url))), "..");
25293
24535
  const serverJsPath = resolve22(packageRoot, ".next/standalone/server.js");
25294
- if (!existsSync29(serverJsPath)) {
24536
+ if (!existsSync30(serverJsPath)) {
25295
24537
  let shadowMessage = null;
25296
24538
  try {
25297
24539
  const diag = diagnoseShadow({ selfPackageRoot: packageRoot, selfVersion: version2 });
@@ -25734,11 +24976,12 @@ __export(exports_configure_wizard, {
25734
24976
  clisSupportingScope: () => clisSupportingScope,
25735
24977
  classifyDaemonInstallFailure: () => classifyDaemonInstallFailure,
25736
24978
  buildPresetChoices: () => buildPresetChoices,
24979
+ buildCompletionSummary: () => buildCompletionSummary,
25737
24980
  buildAgentChoices: () => buildAgentChoices
25738
24981
  });
25739
- import { existsSync as existsSync30, mkdirSync as mkdirSync18, writeFileSync as writeFileSync15, readFileSync as readFileSync24 } from "node:fs";
24982
+ import { existsSync as existsSync31, mkdirSync as mkdirSync19, writeFileSync as writeFileSync17, readFileSync as readFileSync26 } from "node:fs";
25740
24983
  import { homedir as homedir28 } from "node:os";
25741
- import { dirname as dirname15, resolve as resolve23, sep as sep5 } from "node:path";
24984
+ import { dirname as dirname16, resolve as resolve23, sep as sep5 } from "node:path";
25742
24985
  async function emit(event, props) {
25743
24986
  try {
25744
24987
  await trackHookEvent2(getInstanceId2(), event, props);
@@ -25845,8 +25088,8 @@ function setCustomPoliciesEnabled(scope, cwd, enabled) {
25845
25088
  const path3 = getConfigPathForScope(scope, cwd);
25846
25089
  let config = {};
25847
25090
  try {
25848
- if (existsSync30(path3))
25849
- config = JSON.parse(readFileSync24(path3, "utf8"));
25091
+ if (existsSync31(path3))
25092
+ config = JSON.parse(readFileSync26(path3, "utf8"));
25850
25093
  } catch {
25851
25094
  return;
25852
25095
  }
@@ -25855,7 +25098,7 @@ function setCustomPoliciesEnabled(scope, cwd, enabled) {
25855
25098
  else
25856
25099
  config.customPoliciesEnabled = false;
25857
25100
  try {
25858
- writeFileSync15(path3, JSON.stringify(config, null, 2) + `
25101
+ writeFileSync17(path3, JSON.stringify(config, null, 2) + `
25859
25102
  `, "utf8");
25860
25103
  } catch {}
25861
25104
  }
@@ -25902,14 +25145,28 @@ function describeCustomPolicies(cwd) {
25902
25145
  }
25903
25146
  return { active, warnings, fileCount, scopes };
25904
25147
  }
25148
+ function buildCompletionSummary(policiesCount, harnessesCount, customEnabled, daemonInstalled, connected) {
25149
+ const extras = [];
25150
+ if (customEnabled === true)
25151
+ extras.push("custom");
25152
+ else if (customEnabled === false)
25153
+ extras.push("custom off");
25154
+ if (daemonInstalled)
25155
+ extras.push("daemon");
25156
+ if (connected)
25157
+ extras.push("reporting");
25158
+ const extrasNote = extras.length > 0 ? ` · ${extras.join(", ")}` : "";
25159
+ const harnesses = `${harnessesCount} harness${harnessesCount === 1 ? "" : "es"}`;
25160
+ return `Setup complete — ${policiesCount} policies · ${harnesses}${extrasNote}`;
25161
+ }
25905
25162
  function reviewLines(state) {
25906
25163
  const { target, clis, policies, cwd, customEnabled, installDaemon, connect } = state;
25907
25164
  const scopes = scopesFor(target);
25908
25165
  const where = target === "both" ? `Global, plus this project (${homeify2(cwd)})` : target === "project" ? `This project (${homeify2(cwd)})` : "Everywhere (global)";
25909
25166
  const lines = [];
25910
- const assistantNames = clis.map((c2) => getIntegration(c2).displayName);
25167
+ const harnessNames = clis.map((c2) => getIntegration(c2).displayName);
25911
25168
  lines.push(` Where : ${where}`);
25912
- lines.push(` Assistants : ${assistantNames.length ? summarize(assistantNames, "assistants") : "(none)"}`);
25169
+ lines.push(` Harnesses : ${harnessNames.length ? summarize(harnessNames, "harnesses") : "(none)"}`);
25913
25170
  lines.push(policies.length === 0 ? " Policies : none enabled (add later: failproofai policies --install)" : ` Policies : ${policies.length} enabled`);
25914
25171
  if (installDaemon && isDaemonSupportedPlatform()) {
25915
25172
  lines.push(` Daemon : failproofaid, installed as a system service running as you`);
@@ -25955,12 +25212,12 @@ function firstRunMarkerPath() {
25955
25212
  return launcherMarker();
25956
25213
  }
25957
25214
  function hasSeenLauncher() {
25958
- return existsSync30(firstRunMarkerPath());
25215
+ return existsSync31(firstRunMarkerPath());
25959
25216
  }
25960
25217
  function markLauncherSeen() {
25961
25218
  try {
25962
- mkdirSync18(dirname15(firstRunMarkerPath()), { recursive: true });
25963
- writeFileSync15(firstRunMarkerPath(), "1", "utf8");
25219
+ mkdirSync19(dirname16(firstRunMarkerPath()), { recursive: true });
25220
+ writeFileSync17(firstRunMarkerPath(), "1", "utf8");
25964
25221
  } catch {}
25965
25222
  }
25966
25223
  function retryProbe() {
@@ -26053,7 +25310,20 @@ on its own.
26053
25310
  return { applied: false };
26054
25311
  }
26055
25312
  emit("configure_started", {});
26056
- intro("let's set up your safety net", stdout);
25313
+ if (!isDaemonSupportedPlatform()) {
25314
+ stdout.write(`failproofai requires failproofaid, its background policy daemon, which runs on
25315
+ ` + `Linux and macOS only — not ${process.platform}. Setup cannot continue here: an
25316
+ installation with no daemon behind it would read as configured while enforcing
25317
+ nothing, which is worse than not being set up at all.
25318
+
25319
+ Nothing was changed. This platform will be supported once failproofaid gains a
25320
+ ${process.platform} service target.
25321
+
25322
+ `);
25323
+ emit("configure_aborted", { reason: "unsupported_platform" });
25324
+ return { applied: false, abort: "unsupported_platform" };
25325
+ }
25326
+ intro("let's set up failproofai", stdout);
26057
25327
  const cancel = () => {
26058
25328
  outro("Cancelled — nothing was changed.", { ok: false }, stdout);
26059
25329
  return { applied: false, abort: "cancelled" };
@@ -26141,37 +25411,48 @@ refreshed. Re-run \`failproofai config\` once you can use sudo.
26141
25411
  const customEnabledBefore = readHooksConfig().customPoliciesEnabled !== false;
26142
25412
  const presetChoices = buildPresetChoices(cwd, customEnabledBefore);
26143
25413
  const hasCustomFiles = describeCustomPolicies(cwd).fileCount > 0;
26144
- const presets = await multiSelect({
26145
- message: "What should we guard against?",
26146
- choices: presetChoices,
26147
- summaryNoun: "bundles",
26148
- hint: "space toggles · combine presets · ↵ confirm · none is fine",
26149
- stdin,
26150
- stdout
26151
- });
26152
- if (presets === null)
26153
- return cancel();
26154
- const policies = resolvePresetSelection(presets);
26155
- const customEnabled = hasCustomFiles ? presets.includes(CUSTOM) : undefined;
26156
- const clisSel = await multiSelect({
26157
- message: "Which AI assistants should it protect?",
26158
- choices: [
26159
- {
26160
- label: "Everything available",
26161
- value: ALL_CLIS,
26162
- hint: `protect all ${clisSupportingScope(primaryScope).length} CLIs configurable here`,
26163
- summaryExclude: true
26164
- },
26165
- ...buildAgentChoices(primaryScope, cwd)
26166
- ],
26167
- minSelected: 1,
26168
- summaryNoun: "assistants",
26169
- hint: "detected CLIs are pre-selected · space toggles · ctrl+a all · ↵ confirm",
26170
- stdin,
26171
- stdout
26172
- });
26173
- if (clisSel === null)
26174
- return cancel();
25414
+ let presets = null;
25415
+ let clisSel = null;
25416
+ while (clisSel === null) {
25417
+ const priorPresets = presets;
25418
+ presets = await multiSelect({
25419
+ message: "What should we guard against?",
25420
+ choices: priorPresets ? presetChoices.map((c2) => ({ ...c2, checked: priorPresets.includes(c2.value) })) : presetChoices,
25421
+ summaryNoun: "bundles",
25422
+ hint: "space toggles · combine presets · ↵ confirm · none is fine",
25423
+ stdin,
25424
+ stdout
25425
+ });
25426
+ if (presets === null)
25427
+ return cancel();
25428
+ const priorClis = clisSel;
25429
+ const picked = await multiSelect({
25430
+ message: "Which harnesses should it protect?",
25431
+ choices: [
25432
+ {
25433
+ label: "Everything available",
25434
+ value: ALL_CLIS,
25435
+ hint: `protect all ${clisSupportingScope(primaryScope).length} CLIs configurable here`,
25436
+ summaryExclude: true
25437
+ },
25438
+ ...buildAgentChoices(primaryScope, cwd)
25439
+ ].map((c2) => priorClis ? { ...c2, checked: priorClis.includes(c2.value) } : c2),
25440
+ minSelected: 1,
25441
+ summaryNoun: "harnesses",
25442
+ hint: "detected CLIs are pre-selected · space toggles · ctrl+a all · ← back · ↵ confirm",
25443
+ allowBack: true,
25444
+ stdin,
25445
+ stdout
25446
+ });
25447
+ if (picked === null)
25448
+ return cancel();
25449
+ if (picked === BACK)
25450
+ continue;
25451
+ clisSel = picked;
25452
+ }
25453
+ const chosenPresets = presets ?? [];
25454
+ const policies = resolvePresetSelection(chosenPresets);
25455
+ const customEnabled = hasCustomFiles ? chosenPresets.includes(CUSTOM) : undefined;
26175
25456
  const supported = new Set(scopes.flatMap((s) => clisSupportingScope(s)));
26176
25457
  const clis = (clisSel.includes(ALL_CLIS) ? [...INTEGRATION_TYPES] : clisSel.filter((v) => v !== ALL_CLIS)).filter((id) => supported.has(id));
26177
25458
  let connect = null;
@@ -26184,15 +25465,15 @@ refreshed. Re-run \`failproofai config\` once you can use sudo.
26184
25465
  " dashboard. Staying local sends nothing, anywhere, ever."
26185
25466
  ],
26186
25467
  choices: [
26187
- {
26188
- label: "Not now — stay local",
26189
- value: "local",
26190
- hint: "policies still enforce · connect later with failproofai config --connect"
26191
- },
26192
25468
  {
26193
25469
  label: "Paste an API key",
26194
25470
  value: "key",
26195
25471
  hint: "reports decisions and transcripts to your dashboard"
25472
+ },
25473
+ {
25474
+ label: "Not now — stay local",
25475
+ value: "local",
25476
+ hint: "policies still enforce · connect later with failproofai config --connect"
26196
25477
  }
26197
25478
  ],
26198
25479
  stdin,
@@ -26406,7 +25687,7 @@ Hooks keep enforcing; scheduled audits stay off until it is.
26406
25687
  cli: clis,
26407
25688
  cli_count: clis.length,
26408
25689
  policy_count: policies.length,
26409
- source: presets.join("+"),
25690
+ source: chosenPresets.join("+"),
26410
25691
  connected: connect !== null
26411
25692
  });
26412
25693
  for (const scope of scopes) {
@@ -26445,11 +25726,7 @@ Hooks keep enforcing; scheduled audits stay off until it is.
26445
25726
  await applied;
26446
25727
  markLauncherSeen();
26447
25728
  clearOnboardingAttempt();
26448
- const customNote = customEnabled === true ? " + your custom policies" : customEnabled === false ? " · custom policies DISABLED" : "";
26449
- const daemonNote = daemonInstalled ? " · daemon on" : "";
26450
- const cloudNote = connected ? " · reporting on" : "";
26451
- const assistants = `${clis.length} assistant${clis.length === 1 ? "" : "s"}`;
26452
- outro(`Setup complete — ${policies.length} policies${customNote} · ${assistants}${daemonNote}${cloudNote}`, { ok: true }, stdout);
25729
+ outro(buildCompletionSummary(policies.length, clis.length, customEnabled, daemonInstalled, connected), { ok: true }, stdout);
26453
25730
  return {
26454
25731
  applied: true,
26455
25732
  target,
@@ -26484,6 +25761,121 @@ var init_configure_wizard = __esm(() => {
26484
25761
  init_onboarding_lock();
26485
25762
  });
26486
25763
 
25764
+ // src/hooks/flush-cli.ts
25765
+ var exports_flush_cli = {};
25766
+ __export(exports_flush_cli, {
25767
+ runFlushCommand: () => runFlushCommand,
25768
+ pendingBatches: () => pendingBatches,
25769
+ flushRequestPath: () => flushRequestPath,
25770
+ DEFAULT_FLUSH_TIMEOUT_SECS: () => DEFAULT_FLUSH_TIMEOUT_SECS
25771
+ });
25772
+ import { existsSync as existsSync32, mkdirSync as mkdirSync20, writeFileSync as writeFileSync18, readdirSync as readdirSync16 } from "node:fs";
25773
+ import { dirname as dirname17, join as join30 } from "node:path";
25774
+ function flushRequestPath(home) {
25775
+ return join30(failproofaiHome(home), "state", "flush-request.json");
25776
+ }
25777
+ function spoolDirs(home) {
25778
+ const root = spoolDir(home);
25779
+ if (!existsSync32(root))
25780
+ return [];
25781
+ try {
25782
+ return readdirSync16(root, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name !== "failed").map((e) => join30(root, e.name));
25783
+ } catch {
25784
+ return [];
25785
+ }
25786
+ }
25787
+ function pendingBatches(home) {
25788
+ let n = 0;
25789
+ for (const dir of spoolDirs(home)) {
25790
+ try {
25791
+ n += readdirSync16(dir).filter((f) => f.endsWith(".jsonl")).length;
25792
+ } catch {}
25793
+ }
25794
+ return n;
25795
+ }
25796
+ async function runFlushCommand(opts = {}) {
25797
+ const { home, wait = false } = opts;
25798
+ const timeoutSecs = opts.timeoutSecs ?? DEFAULT_FLUSH_TIMEOUT_SECS;
25799
+ const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
25800
+ const lines = [];
25801
+ const cfg = readConfig();
25802
+ if (!cfg.collector?.hooks && !cfg.collector?.sessions) {
25803
+ return {
25804
+ exitCode: 1,
25805
+ pending: 0,
25806
+ lines: [
25807
+ "Collection is off, so nothing is spooled to flush.",
25808
+ "Turn it on with `failproofai config`."
25809
+ ]
25810
+ };
25811
+ }
25812
+ if (!readIngestCredential()) {
25813
+ return {
25814
+ exitCode: 1,
25815
+ pending: 0,
25816
+ lines: [
25817
+ "This machine is not connected, so there is nowhere to flush to.",
25818
+ "Connect it with `failproofai config`."
25819
+ ]
25820
+ };
25821
+ }
25822
+ if (!isDaemonSupportedPlatform()) {
25823
+ return {
25824
+ exitCode: 1,
25825
+ pending: pendingBatches(home),
25826
+ lines: [
25827
+ `failproofaid does not run on ${process.platform}, and it is what delivers batches.`,
25828
+ "Nothing would pick this request up, so nothing was written."
25829
+ ]
25830
+ };
25831
+ }
25832
+ const status = daemonServiceStatus();
25833
+ if (status !== "running") {
25834
+ return {
25835
+ exitCode: 1,
25836
+ pending: pendingBatches(home),
25837
+ lines: [
25838
+ `failproofaid is ${status}, and it is what delivers batches.`,
25839
+ "Start it with `failproofai config`."
25840
+ ]
25841
+ };
25842
+ }
25843
+ const before = pendingBatches(home);
25844
+ if (before === 0) {
25845
+ return { exitCode: 0, pending: 0, lines: ["Nothing spooled — everything already delivered."] };
25846
+ }
25847
+ const path3 = flushRequestPath(home);
25848
+ mkdirSync20(dirname17(path3), { recursive: true });
25849
+ writeFileSync18(path3, `${JSON.stringify({ requestedAtMs: Date.now() }, null, 2)}
25850
+ `, {
25851
+ mode: 384
25852
+ });
25853
+ lines.push(`${before} batch${before === 1 ? "" : "es"} spooled. Requested delivery.`);
25854
+ if (!wait) {
25855
+ lines.push("The daemon picks this up within a few seconds.");
25856
+ return { exitCode: 0, pending: before, lines };
25857
+ }
25858
+ const deadline = Date.now() + timeoutSecs * 1000;
25859
+ let pending3 = before;
25860
+ while (Date.now() < deadline) {
25861
+ await sleep(1000);
25862
+ pending3 = pendingBatches(home);
25863
+ if (pending3 === 0) {
25864
+ lines.push("Spool drained.");
25865
+ return { exitCode: 0, pending: 0, lines };
25866
+ }
25867
+ }
25868
+ lines.push(`Still ${pending3} batch${pending3 === 1 ? "" : "es"} spooled after ${timeoutSecs}s.`, "Delivery continues in the background; re-run with a longer --timeout to keep watching.");
25869
+ return { exitCode: 1, pending: pending3, lines };
25870
+ }
25871
+ var DEFAULT_FLUSH_TIMEOUT_SECS = 60;
25872
+ var init_flush_cli = __esm(() => {
25873
+ init_fp_home();
25874
+ init_fp_config();
25875
+ init_collector_config();
25876
+ init_daemon_service();
25877
+ });
25878
+
26487
25879
  // src/hooks/backfill-cli.ts
26488
25880
  var exports_backfill_cli = {};
26489
25881
  __export(exports_backfill_cli, {
@@ -26491,16 +25883,16 @@ __export(exports_backfill_cli, {
26491
25883
  backfillRequestPath: () => backfillRequestPath,
26492
25884
  DEFAULT_BACKFILL_DAYS: () => DEFAULT_BACKFILL_DAYS
26493
25885
  });
26494
- import { existsSync as existsSync31, mkdirSync as mkdirSync19, writeFileSync as writeFileSync16, readdirSync as readdirSync15, statSync as statSync21 } from "node:fs";
26495
- import { dirname as dirname16, join as join30 } from "node:path";
25886
+ import { existsSync as existsSync33, mkdirSync as mkdirSync21, writeFileSync as writeFileSync19, readdirSync as readdirSync17, statSync as statSync23 } from "node:fs";
25887
+ import { dirname as dirname18, join as join31 } from "node:path";
26496
25888
  function backfillRequestPath(home) {
26497
- return join30(failproofaiHome(home), "state", "backfill-request.json");
25889
+ return join31(failproofaiHome(home), "state", "backfill-request.json");
26498
25890
  }
26499
25891
  function surveySessions(sinceMs, homeDir) {
26500
25892
  const out = [];
26501
25893
  for (const { cli, dir } of SESSION_DIRS) {
26502
- const root = join30(homeDir, dir);
26503
- if (!existsSync31(root))
25894
+ const root = join31(homeDir, dir);
25895
+ if (!existsSync33(root))
26504
25896
  continue;
26505
25897
  let files = 0;
26506
25898
  let newest = 0;
@@ -26509,18 +25901,18 @@ function surveySessions(sinceMs, homeDir) {
26509
25901
  return;
26510
25902
  let entries;
26511
25903
  try {
26512
- entries = readdirSync15(d, { withFileTypes: true });
25904
+ entries = readdirSync17(d, { withFileTypes: true });
26513
25905
  } catch {
26514
25906
  return;
26515
25907
  }
26516
25908
  for (const e of entries) {
26517
- const p = join30(d, e.name);
25909
+ const p = join31(d, e.name);
26518
25910
  if (e.isDirectory()) {
26519
25911
  walk(p, depth + 1);
26520
25912
  continue;
26521
25913
  }
26522
25914
  try {
26523
- const st = statSync21(p);
25915
+ const st = statSync23(p);
26524
25916
  if (st.mtimeMs >= sinceMs) {
26525
25917
  files += 1;
26526
25918
  newest = Math.max(newest, st.mtimeMs);
@@ -26538,7 +25930,7 @@ function runBackfillCommand(opts = {}) {
26538
25930
  const now = opts.now ?? Date.now();
26539
25931
  const sinceMs = opts.sinceMs ?? now - DEFAULT_BACKFILL_DAYS * 24 * 60 * 60 * 1000;
26540
25932
  const home = failproofaiHome();
26541
- if (!existsSync31(home)) {
25933
+ if (!existsSync33(home)) {
26542
25934
  return {
26543
25935
  exitCode: 1,
26544
25936
  lines: [`No failproofai home at ${home}. Run \`failproofai config\` first.`]
@@ -26566,11 +25958,11 @@ function runBackfillCommand(opts = {}) {
26566
25958
  exitCode: 1,
26567
25959
  lines: [
26568
25960
  "Collection is switched off, so a backfill would re-read history and send",
26569
- "none of it. Enable it in ~/.failproofai/config.toml under [collector]."
25961
+ 'none of it. Enable it in ~/.failproofai/config.json under "collector".'
26570
25962
  ]
26571
25963
  };
26572
25964
  }
26573
- const found = surveySessions(sinceMs, dirname16(home));
25965
+ const found = surveySessions(sinceMs, dirname18(home));
26574
25966
  const totalFiles = found.reduce((n, f) => n + f.files, 0);
26575
25967
  const sinceLabel = new Date(sinceMs).toISOString().slice(0, 10);
26576
25968
  const lines = [
@@ -26593,8 +25985,8 @@ function runBackfillCommand(opts = {}) {
26593
25985
  }
26594
25986
  try {
26595
25987
  const path3 = backfillRequestPath();
26596
- mkdirSync19(dirname16(path3), { recursive: true });
26597
- writeFileSync16(path3, `${JSON.stringify({ sinceMs, requestedAtMs: now }, null, 2)}
25988
+ mkdirSync21(dirname18(path3), { recursive: true });
25989
+ writeFileSync19(path3, `${JSON.stringify({ sinceMs, requestedAtMs: now }, null, 2)}
26598
25990
  `, {
26599
25991
  mode: 384
26600
25992
  });
@@ -26635,13 +26027,210 @@ var init_backfill_cli = __esm(() => {
26635
26027
  ];
26636
26028
  });
26637
26029
 
26030
+ // src/hooks/harness-cli.ts
26031
+ var exports_harness_cli = {};
26032
+ __export(exports_harness_cli, {
26033
+ runHarnessCommand: () => runHarnessCommand,
26034
+ removePath: () => removePath,
26035
+ listPaths: () => listPaths,
26036
+ addPath: () => addPath,
26037
+ HARNESS_KEYS: () => HARNESS_KEYS
26038
+ });
26039
+ function unknownHarness(name) {
26040
+ return fail([
26041
+ `Unknown harness: ${name}`,
26042
+ "",
26043
+ `Known harnesses: ${HARNESS_KEYS.join(", ")}`
26044
+ ]);
26045
+ }
26046
+ function pathOf(entry) {
26047
+ const eq = entry.indexOf("=");
26048
+ if (eq <= 0)
26049
+ return entry.trim();
26050
+ const label = entry.slice(0, eq).trim();
26051
+ return label === "" ? entry.trim() : entry.slice(eq + 1).trim();
26052
+ }
26053
+ function labelOf(entry) {
26054
+ const eq = entry.indexOf("=");
26055
+ if (eq <= 0)
26056
+ return null;
26057
+ const label = entry.slice(0, eq).trim();
26058
+ return label === "" ? null : label;
26059
+ }
26060
+ function currentPaths(harness) {
26061
+ return readConfig().collector.sources?.[harness]?.extraPaths ?? [];
26062
+ }
26063
+ function writePaths(harness, paths) {
26064
+ const cfg = readConfig();
26065
+ const sources = { ...cfg.collector.sources ?? {} };
26066
+ if (paths.length > 0)
26067
+ sources[harness] = { extraPaths: paths };
26068
+ else
26069
+ delete sources[harness];
26070
+ updateConfig({
26071
+ collector: {
26072
+ ...cfg.collector,
26073
+ sources: Object.keys(sources).length > 0 ? sources : undefined
26074
+ }
26075
+ });
26076
+ }
26077
+ function addPath(harness, entry) {
26078
+ if (!HARNESS_KEYS.includes(harness))
26079
+ return unknownHarness(harness);
26080
+ const trimmed = entry.trim();
26081
+ if (trimmed === "")
26082
+ return fail(["A path is required."]);
26083
+ if (pathOf(trimmed) === "") {
26084
+ return fail([`Could not read a path out of ${JSON.stringify(entry)}.`, "", "Use `<path>` or `<label>=<path>`."]);
26085
+ }
26086
+ const existing = currentPaths(harness);
26087
+ if (existing.includes(trimmed)) {
26088
+ return ok([`${harness}: ${trimmed} is already configured.`]);
26089
+ }
26090
+ const samePath = existing.find((e) => pathOf(e) === pathOf(trimmed));
26091
+ if (samePath) {
26092
+ return fail([
26093
+ `${harness} already captures ${pathOf(trimmed)} as ${JSON.stringify(samePath)}.`,
26094
+ "",
26095
+ "Remove it first if you meant to relabel it:",
26096
+ ` failproofai harness remove-path ${harness} ${pathOf(trimmed)}`
26097
+ ]);
26098
+ }
26099
+ const wanted = labelOf(trimmed);
26100
+ if (wanted) {
26101
+ const sameLabel = existing.find((e) => labelOf(e) === wanted);
26102
+ if (sameLabel) {
26103
+ return fail([
26104
+ `${harness} already uses the label ${JSON.stringify(wanted)} for ${pathOf(sameLabel)}.`,
26105
+ "",
26106
+ "Labels namespace agent ids, so two paths cannot share one."
26107
+ ]);
26108
+ }
26109
+ }
26110
+ writePaths(harness, [...existing, trimmed]);
26111
+ return ok([
26112
+ `${harness}: now also capturing ${pathOf(trimmed)}`,
26113
+ wanted ? ` agent ids will be namespaced ${wanted}-*` : " a label will be derived from the folder name; `failproofai harness list` shows it",
26114
+ ` written to ${configFile()}`,
26115
+ ...TAKES_EFFECT_HINT
26116
+ ]);
26117
+ }
26118
+ function removePath(harness, target) {
26119
+ if (!HARNESS_KEYS.includes(harness))
26120
+ return unknownHarness(harness);
26121
+ const existing = currentPaths(harness);
26122
+ const t = target.trim();
26123
+ const kept = existing.filter((e) => e !== t && pathOf(e) !== t && labelOf(e) !== t);
26124
+ if (kept.length === existing.length) {
26125
+ return fail([
26126
+ `${harness} has no extra path matching ${JSON.stringify(t)}.`,
26127
+ "",
26128
+ existing.length > 0 ? `Configured: ${existing.join(", ")}` : `${harness} has no extra paths configured.`
26129
+ ]);
26130
+ }
26131
+ writePaths(harness, kept);
26132
+ return ok([
26133
+ `${harness}: no longer capturing ${t}`,
26134
+ "",
26135
+ "Already-collected sessions from that path are NOT removed — they are on the",
26136
+ "server. This only stops new ones.",
26137
+ ...TAKES_EFFECT_HINT
26138
+ ]);
26139
+ }
26140
+ function listPaths(harness) {
26141
+ if (harness && !HARNESS_KEYS.includes(harness))
26142
+ return unknownHarness(harness);
26143
+ const sources = readConfig().collector.sources ?? {};
26144
+ const names = harness ? [harness] : HARNESS_KEYS.filter((k) => sources[k]?.extraPaths?.length);
26145
+ const unknown = Object.keys(sources).filter((k) => !HARNESS_KEYS.includes(k));
26146
+ if (names.length === 0 && unknown.length === 0) {
26147
+ return ok([
26148
+ "No extra capture paths configured.",
26149
+ "",
26150
+ "Every harness is watching only its default location. Add one with:",
26151
+ " failproofai harness add-path <harness> [<label>=]<path>",
26152
+ "",
26153
+ `Harnesses: ${HARNESS_KEYS.join(", ")}`
26154
+ ]);
26155
+ }
26156
+ const lines = [];
26157
+ for (const name of names) {
26158
+ const entries = sources[name]?.extraPaths ?? [];
26159
+ if (entries.length === 0) {
26160
+ if (harness)
26161
+ lines.push(`${name}: no extra paths configured (default location only)`);
26162
+ continue;
26163
+ }
26164
+ lines.push(`${name}:`);
26165
+ for (const e of entries) {
26166
+ const label = labelOf(e);
26167
+ lines.push(label ? ` ${pathOf(e)} → agent ids ${label}-*` : ` ${pathOf(e)} → label derived from the folder name`);
26168
+ }
26169
+ }
26170
+ if (unknown.length > 0) {
26171
+ if (lines.length === 0)
26172
+ lines.push("No extra capture paths configured for any known harness.");
26173
+ lines.push("", `⚠ config.json configures ${unknown.length} unknown harness(es): ${unknown.join(", ")}`, " Nothing is captured from them.", ` Known: ${HARNESS_KEYS.join(", ")}`);
26174
+ }
26175
+ return ok(lines);
26176
+ }
26177
+ function runHarnessCommand(argv) {
26178
+ const [sub, ...rest] = argv;
26179
+ switch (sub) {
26180
+ case "add-path":
26181
+ if (rest.length < 2) {
26182
+ return fail(["Usage: failproofai harness add-path <harness> [<label>=]<path>"]);
26183
+ }
26184
+ return addPath(rest[0], rest.slice(1).join(" "));
26185
+ case "remove-path":
26186
+ if (rest.length < 2) {
26187
+ return fail(["Usage: failproofai harness remove-path <harness> <path|label>"]);
26188
+ }
26189
+ return removePath(rest[0], rest.slice(1).join(" "));
26190
+ case "list":
26191
+ return listPaths(rest[0]);
26192
+ default:
26193
+ return fail([
26194
+ sub ? `Unknown subcommand: ${sub}` : "A subcommand is required.",
26195
+ "",
26196
+ "Usage:",
26197
+ " failproofai harness list [<harness>]",
26198
+ " failproofai harness add-path <harness> [<label>=]<path>",
26199
+ " failproofai harness remove-path <harness> <path|label>"
26200
+ ]);
26201
+ }
26202
+ }
26203
+ var HARNESS_KEYS, ok = (lines) => ({ lines, exitCode: 0 }), fail = (lines) => ({ lines, exitCode: 1 }), TAKES_EFFECT_HINT;
26204
+ var init_harness_cli = __esm(() => {
26205
+ init_fp_config();
26206
+ init_fp_home();
26207
+ HARNESS_KEYS = [
26208
+ "claude",
26209
+ "codex",
26210
+ "copilot",
26211
+ "openclaw",
26212
+ "pi",
26213
+ "factory",
26214
+ "antigravity",
26215
+ "cursor",
26216
+ "goose",
26217
+ "opencode",
26218
+ "devin",
26219
+ "hermes"
26220
+ ];
26221
+ TAKES_EFFECT_HINT = [
26222
+ "",
26223
+ "The daemon picks this up on its own within a few seconds — no restart, no sudo."
26224
+ ];
26225
+ });
26226
+
26638
26227
  // src/hooks/uninstall-cli.ts
26639
26228
  var exports_uninstall_cli = {};
26640
26229
  __export(exports_uninstall_cli, {
26641
26230
  serviceStateLabel: () => serviceStateLabel,
26642
26231
  runUninstallCommand: () => runUninstallCommand
26643
26232
  });
26644
- import { existsSync as existsSync32, rmSync as rmSync10 } from "node:fs";
26233
+ import { existsSync as existsSync34, rmSync as rmSync11 } from "node:fs";
26645
26234
  function survey(cwd) {
26646
26235
  const clis = [];
26647
26236
  for (const id of listInstallableIds()) {
@@ -26662,9 +26251,9 @@ function survey(cwd) {
26662
26251
  return {
26663
26252
  clis,
26664
26253
  servicePath,
26665
- serviceInstalled: !!servicePath && existsSync32(servicePath),
26254
+ serviceInstalled: !!servicePath && existsSync34(servicePath),
26666
26255
  daemonConfigured,
26667
- homeExists: existsSync32(failproofaiHome())
26256
+ homeExists: existsSync34(failproofaiHome())
26668
26257
  };
26669
26258
  }
26670
26259
  async function runUninstallCommand(opts = {}) {
@@ -26731,7 +26320,7 @@ async function runUninstallCommand(opts = {}) {
26731
26320
  purged: false,
26732
26321
  lines: [
26733
26322
  ...lines,
26734
- `✗ could not update ${failproofaiHome()}/config.toml: ${err instanceof Error ? err.message : String(err)}`,
26323
+ `✗ could not update ${failproofaiHome()}/config.json: ${err instanceof Error ? err.message : String(err)}`,
26735
26324
  ``,
26736
26325
  `Stopped before touching the service. Removing it while this machine still`,
26737
26326
  `requires it would deny every tool call. Fix the file's permissions and re-run.`
@@ -26754,7 +26343,7 @@ async function runUninstallCommand(opts = {}) {
26754
26343
  if (found.serviceInstalled) {
26755
26344
  try {
26756
26345
  await uninstallDaemonService();
26757
- if (found.servicePath && existsSync32(found.servicePath)) {
26346
+ if (found.servicePath && existsSync34(found.servicePath)) {
26758
26347
  failures.push(`the service at ${found.servicePath} is still there (most often: no sudo). ` + `Remove it with the commands below.`);
26759
26348
  } else {
26760
26349
  lines.push(`✓ stopped and removed the daemon service`);
@@ -26765,7 +26354,7 @@ async function runUninstallCommand(opts = {}) {
26765
26354
  }
26766
26355
  if (opts.purge && found.homeExists) {
26767
26356
  try {
26768
- rmSync10(failproofaiHome(), { recursive: true, force: true });
26357
+ rmSync11(failproofaiHome(), { recursive: true, force: true });
26769
26358
  purged = true;
26770
26359
  lines.push(`✓ deleted ${failproofaiHome()}`);
26771
26360
  } catch (err) {
@@ -26776,7 +26365,7 @@ async function runUninstallCommand(opts = {}) {
26776
26365
  lines.push(``, `Not everything could be removed:`);
26777
26366
  for (const f of failures)
26778
26367
  lines.push(` ✗ ${f}`);
26779
- if (found.servicePath && existsSync32(found.servicePath)) {
26368
+ if (found.servicePath && existsSync34(found.servicePath)) {
26780
26369
  lines.push(``, `To remove the service by hand:`, ...manualServiceRemoval(found.servicePath), ``, `(\`${daemonStatusCommand()}\` shows its current state.)`);
26781
26370
  }
26782
26371
  return { exitCode: 1, lines, planLines, purged };
@@ -26836,6 +26425,7 @@ __export(exports_tui, {
26836
26425
  CHECK_OFF: () => CHECK_OFF2,
26837
26426
  CARET: () => CARET2,
26838
26427
  BAR: () => BAR2,
26428
+ BACK: () => BACK2,
26839
26429
  ANSI_RESET: () => ANSI_RESET2,
26840
26430
  ANSI_DIM: () => ANSI_DIM2,
26841
26431
  ANSI_BOLD: () => ANSI_BOLD2
@@ -26979,14 +26569,14 @@ function renderLaunchBanner2(version3, stdout = process.stdout) {
26979
26569
  }
26980
26570
  function outro2(message, opts = {}, stdout = process.stdout) {
26981
26571
  const c2 = paint2(colorsEnabled2(stdout));
26982
- const ok = opts.ok !== false;
26572
+ const ok2 = opts.ok !== false;
26983
26573
  if (!stdout.isTTY) {
26984
26574
  stdout.write(message + `
26985
26575
  `);
26986
26576
  return;
26987
26577
  }
26988
- const end = ok ? c2.pink(BAR_END2) : c2.dim(BAR_END2);
26989
- const text = ok ? c2.pink(message) : c2.dim(message);
26578
+ const end = ok2 ? c2.pink(BAR_END2) : c2.dim(BAR_END2);
26579
+ const text = ok2 ? c2.pink(message) : c2.dim(message);
26990
26580
  writeLines2(stdout, [c2.dim(BAR2), `${end} ${text}`]);
26991
26581
  }
26992
26582
  function repaint2(out, region, lines) {
@@ -27062,10 +26652,11 @@ function runPrompt2(p) {
27062
26652
  return lines;
27063
26653
  };
27064
26654
  const collapse = (result) => {
26655
+ const summary = result === BACK2 ? "back" : p.summaryFor(result);
27065
26656
  repaint2(stdout, region, [
27066
26657
  c2.dim(BAR2),
27067
26658
  `${c2.dim(STEP_DONE2)} ${p.message}`,
27068
- `${c2.dim(BAR2)} ${c2.dim(p.summaryFor(result))}`
26659
+ `${c2.dim(BAR2)} ${c2.dim(summary)}`
27069
26660
  ]);
27070
26661
  };
27071
26662
  return new Promise((resolve24) => {
@@ -27091,6 +26682,8 @@ function runPrompt2(p) {
27091
26682
  return;
27092
26683
  if (key.ctrl && (key.name === "c" || key.name === "d") || key.name === "escape") {
27093
26684
  finish(null);
26685
+ } else if (key.name === "left" && p.allowBack) {
26686
+ finish(BACK2);
27094
26687
  } else if (key.name === "up") {
27095
26688
  cursor2 = cursor2 > 0 ? cursor2 - 1 : choices.length - 1;
27096
26689
  repaint2(stdout, region, build());
@@ -27134,7 +26727,8 @@ function selectOne2(opts) {
27134
26727
  const hint = choice.hint ? ` ${c2.dim(ellipsize2(choice.hint, budget))}` : "";
27135
26728
  return `${dot} ${label}${hint}`;
27136
26729
  },
27137
- footer: "↑/↓ navigate · enter to select · esc to cancel",
26730
+ allowBack: opts.allowBack,
26731
+ footer: opts.allowBack ? "↑/↓ navigate · enter to select · ← back · esc to cancel" : "↑/↓ navigate · enter to select · esc to cancel",
27138
26732
  onKey: (key, cursor2) => key.name === "return" ? { done: choices[cursor2].value } : undefined,
27139
26733
  summaryFor: (value) => value === null ? "cancelled" : choices.find((ch) => ch.value === value)?.label ?? String(value)
27140
26734
  });
@@ -27168,7 +26762,8 @@ function multiSelect2(opts) {
27168
26762
  return `${caret} ${box} ${label}${hint}`;
27169
26763
  },
27170
26764
  warnLine: () => warn ? c2.warn(`Select at least ${minSelected}.`) : null,
27171
- footer: opts.hint ?? "↑/↓ move · space select · ctrl+a all · enter confirm",
26765
+ allowBack: opts.allowBack,
26766
+ footer: opts.hint ?? (opts.allowBack ? "↑/↓ move · space select · ctrl+a all · ← back · enter confirm" : "↑/↓ move · space select · ctrl+a all · enter confirm"),
27172
26767
  onKey: (key, cursor2) => {
27173
26768
  if (key.name === "space") {
27174
26769
  if (choices[cursor2]?.locked)
@@ -27230,11 +26825,13 @@ function promptText2(opts) {
27230
26825
  return new Promise((resolve24) => {
27231
26826
  let value = "";
27232
26827
  const draw = (error) => {
26828
+ const cols = stdout.columns || 80;
27233
26829
  const shown = opts.mask ? "•".repeat(value.length) : value;
27234
26830
  const hint = opts.hint ? ` ${c2.dim(opts.hint)}` : "";
26831
+ const line = truncate2(`${c2.bold(opts.message)} ${shown}${hint}`, cols - 1);
27235
26832
  const err = error ? `
27236
- ${c2.warn(error)}` : "";
27237
- stdout.write(`\r\x1B[2K${c2.bold(opts.message)} ${shown}${hint}${err}`);
26833
+ ${truncate2(c2.warn(error), cols - 3)}` : "";
26834
+ stdout.write(`\r\x1B[2K${line}${err}`);
27238
26835
  if (err)
27239
26836
  stdout.write("\x1B[1A");
27240
26837
  };
@@ -27281,7 +26878,7 @@ function promptText2(opts) {
27281
26878
  stdin.on("keypress", onKey);
27282
26879
  });
27283
26880
  }
27284
- var ESC2 = "\x1B", BAR2 = "│", BAR_END2 = "└", STEP_ACTIVE2 = "◆", STEP_DONE2 = "◇", RADIO_ON2 = "●", RADIO_OFF2 = "○", CHECK_ON2 = "◼", CHECK_OFF2 = "◻", CARET2 = "❯", FLOWER2 = "❋", HUES2, ANSI_RESET2, ANSI_BOLD2, ANSI_DIM2, LOGO_MIN_COLS2 = 22, TAGLINE2 = "end-to-end failure layer for ai agents", LOGO_GRID2, LOGO_TEAL2, LOGO_PINK2, WINDOW3 = 8;
26881
+ var ESC2 = "\x1B", BAR2 = "│", BAR_END2 = "└", STEP_ACTIVE2 = "◆", STEP_DONE2 = "◇", RADIO_ON2 = "●", RADIO_OFF2 = "○", CHECK_ON2 = "◼", CHECK_OFF2 = "◻", CARET2 = "❯", FLOWER2 = "❋", HUES2, ANSI_RESET2, ANSI_BOLD2, ANSI_DIM2, LOGO_MIN_COLS2 = 22, TAGLINE2 = "end-to-end failure layer for ai agents", LOGO_GRID2, LOGO_TEAL2, LOGO_PINK2, WINDOW3 = 8, BACK2;
27285
26882
  var init_tui2 = __esm(() => {
27286
26883
  HUES2 = {
27287
26884
  guide: { rgb: [102, 209, 181], basic: "36" },
@@ -27317,6 +26914,7 @@ var init_tui2 = __esm(() => {
27317
26914
  ];
27318
26915
  LOGO_TEAL2 = HUES2.guide.rgb;
27319
26916
  LOGO_PINK2 = HUES2.logoPink.rgb;
26917
+ BACK2 = Symbol("failproofai.back");
27320
26918
  });
27321
26919
 
27322
26920
  // src/hooks/manager.ts
@@ -27329,7 +26927,7 @@ __export(exports_manager, {
27329
26927
  getSettingsPath: () => getSettingsPath2
27330
26928
  });
27331
26929
  import { execSync as execSync7 } from "node:child_process";
27332
- import { existsSync as existsSync33 } from "node:fs";
26930
+ import { existsSync as existsSync35 } from "node:fs";
27333
26931
  import { resolve as resolve24, basename as basename6 } from "node:path";
27334
26932
  import { platform as platform5, arch as arch4, release as release3, hostname as hostname5 } from "node:os";
27335
26933
  function getSettingsPath2(scope, cwd) {
@@ -27440,7 +27038,7 @@ async function installHooksImpl2(policyNames, scope = "user", cwd, includeBeta =
27440
27038
  } else if (customPoliciesPath && (typeof customPoliciesPath === "string" || customPoliciesPath.length > 0)) {
27441
27039
  const incoming = (typeof customPoliciesPath === "string" ? [customPoliciesPath] : customPoliciesPath).map((path3) => resolve24(path3));
27442
27040
  const carried = (previousConfig.customPoliciesPaths ?? (previousConfig.customPoliciesPath ? [previousConfig.customPoliciesPath] : [])).map((path3) => resolve24(path3)).filter((path3) => {
27443
- if (existsSync33(path3))
27041
+ if (existsSync35(path3))
27444
27042
  return true;
27445
27043
  console.log(`Dropping custom policies path (file no longer exists): ${path3}`);
27446
27044
  return false;
@@ -27632,7 +27230,7 @@ async function removeHooks2(policyNames, scope = "user", cwd, opts) {
27632
27230
  const scopesToRemove = scope === "all" ? [...integration.scopes] : integration.scopes.includes(scope) ? [scope] : [];
27633
27231
  for (const s of scopesToRemove) {
27634
27232
  const settingsPaths = settingsPathsFor(integration, s, cwd);
27635
- const existing = settingsPaths.filter((p) => existsSync33(p));
27233
+ const existing = settingsPaths.filter((p) => existsSync35(p));
27636
27234
  if (existing.length === 0) {
27637
27235
  if (scope !== "all" && selectedClis.length === 1) {
27638
27236
  console.log("No settings file found. Nothing to remove.");
@@ -27833,7 +27431,7 @@ Failproof AI Hook Policies
27833
27431
  for (const path3 of explicitPaths) {
27834
27432
  const absPath = resolve24(findProjectConfigDir(cwd ?? process.cwd()), path3);
27835
27433
  console.log(` ${absPath}`);
27836
- if (!existsSync33(absPath)) {
27434
+ if (!existsSync35(absPath)) {
27837
27435
  console.log(` \x1B[31m✗ File not found: ${absPath}\x1B[0m`);
27838
27436
  continue;
27839
27437
  }
@@ -27901,6 +27499,21 @@ Failproof AI Hook Policies
27901
27499
  }
27902
27500
  console.log();
27903
27501
  }
27502
+ try {
27503
+ const cloud = readActiveCloudManagedPolicies();
27504
+ if (cloud.length > 0) {
27505
+ const gen = cloud[0].deployment;
27506
+ console.log(`
27507
+ ── Cloud-managed — deployment ${gen} ─────────`);
27508
+ const colWidth = Math.max(nameColWidth, ...cloud.map((c2) => c2.id.length + 2));
27509
+ for (const artifact of cloud) {
27510
+ const status = artifact.effect === "observe" ? "\x1B[33m◐ OBS\x1B[0m" : "\x1B[32m✓ ON\x1B[0m";
27511
+ console.log(` ${status} ${artifact.id.padEnd(colWidth)}v${artifact.version}`);
27512
+ }
27513
+ console.log("\n Managed from the dashboard — not switchable with `failproofai policies`.");
27514
+ console.log();
27515
+ }
27516
+ } catch {}
27904
27517
  try {
27905
27518
  syncConventionPolicies(discovered.user, "user");
27906
27519
  } catch (err) {
@@ -27920,6 +27533,7 @@ var init_manager2 = __esm(() => {
27920
27533
  init_cli_error();
27921
27534
  init_hook_logger();
27922
27535
  init_fp_home();
27536
+ init_cloud_managed_policies();
27923
27537
  VALID_POLICY_NAMES2 = new Set(BUILTIN_POLICIES.map((p) => p.name));
27924
27538
  });
27925
27539
 
@@ -28968,8 +28582,8 @@ __export(exports_launch2, {
28968
28582
  launch: () => launch2
28969
28583
  });
28970
28584
  import { spawn as spawn3 } from "child_process";
28971
- import { realpathSync as realpathSync5, existsSync as existsSync34 } from "node:fs";
28972
- import { resolve as resolve25, dirname as dirname17 } from "node:path";
28585
+ import { realpathSync as realpathSync5, existsSync as existsSync36 } from "node:fs";
28586
+ import { resolve as resolve25, dirname as dirname19 } from "node:path";
28973
28587
  import { fileURLToPath as fileURLToPath3 } from "node:url";
28974
28588
  import { createInterface as createInterface2 } from "node:readline";
28975
28589
  function launch2(mode) {
@@ -28993,9 +28607,9 @@ function launch2(mode) {
28993
28607
  process.env.PORT = port;
28994
28608
  process.env.HOSTNAME = bindHost;
28995
28609
  cmd = "node";
28996
- const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve25(dirname17(realpathSync5(fileURLToPath3(import.meta.url))), "..");
28610
+ const packageRoot = process.env.FAILPROOFAI_PACKAGE_ROOT ?? resolve25(dirname19(realpathSync5(fileURLToPath3(import.meta.url))), "..");
28997
28611
  const serverJsPath = resolve25(packageRoot, ".next/standalone/server.js");
28998
- if (!existsSync34(serverJsPath)) {
28612
+ if (!existsSync36(serverJsPath)) {
28999
28613
  let shadowMessage = null;
29000
28614
  try {
29001
28615
  const diag = diagnoseShadow({ selfPackageRoot: packageRoot, selfVersion: version2 });
@@ -29092,17 +28706,17 @@ var init_cli_error2 = __esm(() => {
29092
28706
 
29093
28707
  // bin/failproofai.mjs
29094
28708
  import { realpathSync as realpathSync6 } from "fs";
29095
- import { dirname as dirname18, resolve as resolve26 } from "path";
28709
+ import { dirname as dirname20, resolve as resolve26 } from "path";
29096
28710
  import { fileURLToPath as fileURLToPath4 } from "url";
29097
28711
  // package.json
29098
- var version = "1.0.0-beta.11";
28712
+ var version = "1.0.0-beta.13";
29099
28713
 
29100
28714
  // bin/failproofai.mjs
29101
28715
  if (!process.env.FAILPROOFAI_PACKAGE_ROOT) {
29102
- process.env.FAILPROOFAI_PACKAGE_ROOT = resolve26(dirname18(realpathSync6(fileURLToPath4(import.meta.url))), "..");
28716
+ process.env.FAILPROOFAI_PACKAGE_ROOT = resolve26(dirname20(realpathSync6(fileURLToPath4(import.meta.url))), "..");
29103
28717
  }
29104
28718
  if (!process.env.FAILPROOFAI_DIST_PATH) {
29105
- process.env.FAILPROOFAI_DIST_PATH = resolve26(dirname18(realpathSync6(fileURLToPath4(import.meta.url))), "..", "dist");
28719
+ process.env.FAILPROOFAI_DIST_PATH = resolve26(dirname20(realpathSync6(fileURLToPath4(import.meta.url))), "..", "dist");
29106
28720
  }
29107
28721
  var args = process.argv.slice(2);
29108
28722
  if (args[0] === "p")
@@ -29226,7 +28840,7 @@ if (hookIdx >= 0) {
29226
28840
  }
29227
28841
  }
29228
28842
  async function runCli() {
29229
- const SUBCOMMANDS = ["policies", "policy", "audit", "config", "uninstall", "backfill"];
28843
+ const SUBCOMMANDS = ["policies", "policy", "audit", "config", "uninstall", "backfill", "flush", "harness"];
29230
28844
  if ((args.includes("--help") || args.includes("-h")) && !SUBCOMMANDS.includes(args[0])) {
29231
28845
  const extraArgs = args.filter((a) => a !== "--help" && a !== "-h");
29232
28846
  if (extraArgs.length > 0) {
@@ -29242,6 +28856,13 @@ USAGE
29242
28856
  COMMANDS
29243
28857
  (no args) Launch the policy dashboard
29244
28858
  config Interactive setup \u2014 pick scope, agents & policies
28859
+ --connect <url> --token <key> Connect to Failproof Cloud non-interactively
28860
+ --machine-id <id> Stable id for this machine
28861
+ --machine-label <name> Human-readable name in the dashboard
28862
+ --no-transcripts Report decisions only, never transcripts
28863
+ --disconnect Stop pulling policy and sending activity
28864
+ --status Show connection, daemon and pause state
28865
+ --pause / --resume Pause or resume enforcement
29245
28866
 
29246
28867
  policy add <name> Enable a single policy (see \`policy --help\`)
29247
28868
  policy remove <name> Disable a single policy
@@ -29268,10 +28889,30 @@ COMMANDS
29268
28889
 
29269
28890
  policies --help, -h Show this help for the policies command
29270
28891
 
28892
+ harness list Show extra capture paths per agent CLI
28893
+ harness add-path <h> <path> Also capture sessions from <path> for harness
28894
+ <h>. Accepts \`<label>=<path>\`; the label
28895
+ namespaces agent ids so two copies of one
28896
+ project stay distinct.
28897
+ harness remove-path <h> <p> Stop capturing that path
28898
+ harness --help, -h Show this help for the harness command
28899
+
29271
28900
  audit Audit your agent's behavior, then open the
29272
28901
  dashboard at http://localhost:8020/audit
29273
28902
  audit --help, -h Show this help for the audit command
29274
28903
 
28904
+ backfill Re-send history the collector already read past
28905
+ \u2014 after clearing the dashboard, re-enrolling a
28906
+ machine, or connecting later than the work
28907
+ --since <when> How far back: 30d, 6m, or YYYY-MM-DD
28908
+ (default: 30 days)
28909
+ --dry-run Report what would be re-read, change nothing
28910
+
28911
+ flush Deliver everything already spooled, now,
28912
+ instead of waiting for the next sweep
28913
+ --wait Block until the spool drains (or --timeout)
28914
+ --timeout <secs> How long to wait with --wait (default: 60)
28915
+
29275
28916
  uninstall Remove failproofai from this machine: hook
29276
28917
  entries from every agent CLI, and the daemon
29277
28918
  service. Run this BEFORE \`npm rm -g failproofai\`
@@ -29311,6 +28952,10 @@ EXAMPLES
29311
28952
  failproofai policies --uninstall --cli opencode
29312
28953
  failproofai policies --uninstall --cli pi
29313
28954
  failproofai policies --uninstall --custom
28955
+ failproofai backfill --since 6m
28956
+ failproofai backfill --dry-run
28957
+ failproofai flush --wait
28958
+ failproofai config --status
29314
28959
 
29315
28960
  LINKS
29316
28961
  \u2B50 Star us: https://github.com/failproofai/failproofai
@@ -29332,8 +28977,8 @@ Run \`failproofai --help\` for usage.`);
29332
28977
  {
29333
28978
  const isHelpOrVersion = args.includes("--help") || args.includes("-h") || args.includes("--version") || args.includes("-v");
29334
28979
  if (args[0] === "audit" && args.includes("--scheduled")) {
29335
- const { layoutWarningForHook: layoutWarningForHook3 } = await Promise.resolve().then(() => (init_fp_reset2(), exports_fp_reset));
29336
- const warning = layoutWarningForHook3();
28980
+ const { layoutBlockerForScheduledRun: layoutBlockerForScheduledRun2 } = await Promise.resolve().then(() => (init_fp_reset2(), exports_fp_reset));
28981
+ const warning = layoutBlockerForScheduledRun2();
29337
28982
  if (warning) {
29338
28983
  console.error(warning);
29339
28984
  process.exit(1);
@@ -29359,6 +29004,68 @@ Run \`failproofai --help\` for usage.`);
29359
29004
  await maybeFirstRunConfigure2({}, { postSetupAudit: args[0] !== "audit", force: layoutWasReset });
29360
29005
  } catch {}
29361
29006
  }
29007
+ if (args[0] === "flush") {
29008
+ const subArgs = args.slice(1);
29009
+ if (subArgs.includes("--help") || subArgs.includes("-h")) {
29010
+ console.log(`
29011
+ failproofai flush \u2014 deliver what is already spooled, now
29012
+
29013
+ USAGE
29014
+ failproofai flush [--wait] [--timeout <secs>]
29015
+
29016
+ WHY
29017
+ The collector is unhurried on purpose: a batch is swept once it is older than
29018
+ two minutes, at most 64 per pass, on a 60-second cadence. That pacing keeps a
29019
+ backlog from stampeding the server, and it is exactly wrong when you are
29020
+ standing at a dashboard waiting to see your own events \u2014 "not delivered yet"
29021
+ and "not working" look identical from there.
29022
+
29023
+ This asks the daemon to make a pass right now, with no minimum age and no
29024
+ per-pass cap. It re-sends nothing: only batches already spooled and not yet
29025
+ delivered. For history the collector has already read past, use \`backfill\`.
29026
+
29027
+ OPTIONS
29028
+ --wait Block until the spool drains, or --timeout elapses.
29029
+ --timeout <secs> How long --wait waits. Default: 60.
29030
+ `);
29031
+ process.exit(0);
29032
+ }
29033
+ const KNOWN = new Set(["--wait", "--timeout"]);
29034
+ const unknown = subArgs.find((a, i) => a.startsWith("-") && !KNOWN.has(a) && subArgs[i - 1] !== "--timeout");
29035
+ if (unknown) {
29036
+ throw new CliError3(`Unexpected argument: ${unknown}
29037
+ Run \`failproofai flush --help\` for usage.`);
29038
+ }
29039
+ let timeoutSecs;
29040
+ const tIdx = subArgs.indexOf("--timeout");
29041
+ if (tIdx >= 0) {
29042
+ const raw = subArgs[tIdx + 1];
29043
+ if (!raw || raw.startsWith("-"))
29044
+ throw new CliError3("Missing value after --timeout.");
29045
+ const n = Number(raw);
29046
+ if (!Number.isFinite(n) || n <= 0) {
29047
+ throw new CliError3(`Could not read --timeout ${raw}. Give a number of seconds.`);
29048
+ }
29049
+ timeoutSecs = n;
29050
+ }
29051
+ lastSubcommand = "flush";
29052
+ const { runFlushCommand: runFlushCommand2 } = await Promise.resolve().then(() => (init_flush_cli(), exports_flush_cli));
29053
+ const result = await runFlushCommand2({ wait: subArgs.includes("--wait"), timeoutSecs });
29054
+ for (const line of result.lines) {
29055
+ if (result.exitCode === 0)
29056
+ console.log(line);
29057
+ else
29058
+ console.error(line);
29059
+ }
29060
+ await track("cli_flush", {
29061
+ ok: result.exitCode === 0,
29062
+ waited: subArgs.includes("--wait"),
29063
+ pending: result.pending
29064
+ });
29065
+ lastSubcommand = null;
29066
+ await exitAfterFlush(result.exitCode);
29067
+ return;
29068
+ }
29362
29069
  if (args[0] === "backfill") {
29363
29070
  const subArgs = args.slice(1);
29364
29071
  if (subArgs.includes("--help") || subArgs.includes("-h")) {
@@ -29426,6 +29133,71 @@ Run \`failproofai backfill --help\` for usage.`);
29426
29133
  await exitAfterFlush(result.exitCode);
29427
29134
  return;
29428
29135
  }
29136
+ if (args[0] === "harness") {
29137
+ const subArgs = args.slice(1);
29138
+ if (subArgs.length === 0 || subArgs.includes("--help") || subArgs.includes("-h")) {
29139
+ console.log(`
29140
+ failproofai harness \u2014 capture sessions from more than one location per agent CLI
29141
+
29142
+ USAGE
29143
+ failproofai harness list [<harness>]
29144
+ failproofai harness add-path <harness> [<label>=]<path>
29145
+ failproofai harness remove-path <harness> <path|label>
29146
+
29147
+ WHY
29148
+ Each agent CLI is watched wherever its own installer put it \u2014 ~/.claude/projects,
29149
+ ~/.hermes/state.db, and so on. That misses every other arrangement: a second
29150
+ profile, a mounted team share, a container's home beside the host's, an agent
29151
+ an operator moved. Those hold real sessions and nothing collects them.
29152
+
29153
+ THE LABEL
29154
+ An entry is \`<path>\` or \`<label>=<path>\`. The label namespaces agent ids as
29155
+ <label>-<agentId>, and it matters: two locations holding the same project
29156
+ derive the SAME id (it comes from the cwd inside the transcript, identical in
29157
+ both copies), so without a label they merge into one agent whose sessions
29158
+ interleave from two machines' worth of history. Omit it and one is derived
29159
+ from the folder name.
29160
+
29161
+ HARNESSES
29162
+ claude, codex, copilot, openclaw, pi, factory, antigravity, cursor,
29163
+ goose, opencode, devin, hermes
29164
+
29165
+ \`claude\` covers subagent transcripts too \u2014 they live under the same root.
29166
+
29167
+ NOTES
29168
+ \u2022 Session collection must be on for any of this to be read; extra paths follow
29169
+ [collector] in ~/.failproofai/config.toml like the default ones do.
29170
+ \u2022 A path overlapping one already captured is REFUSED by the daemon at startup
29171
+ rather than collected twice under two ids. It says so in the journal.
29172
+ \u2022 Takes effect within seconds \u2014 the daemon re-reads config.toml on an interval
29173
+ and cycles its collector. No restart, no sudo.
29174
+
29175
+ EXAMPLES
29176
+ failproofai harness add-path claude work=/srv/team/.claude/projects
29177
+ failproofai harness add-path hermes prod=/srv/hermes-prod/state.db
29178
+ failproofai harness add-path codex /mnt/other-home/.codex/sessions
29179
+ failproofai harness list
29180
+ failproofai harness remove-path claude work
29181
+ `.trimStart());
29182
+ process.exit(0);
29183
+ }
29184
+ lastSubcommand = "harness";
29185
+ const { runHarnessCommand: runHarnessCommand2 } = await Promise.resolve().then(() => (init_harness_cli(), exports_harness_cli));
29186
+ const result = runHarnessCommand2(subArgs);
29187
+ for (const line of result.lines) {
29188
+ if (result.exitCode === 0)
29189
+ console.log(line);
29190
+ else
29191
+ console.error(line);
29192
+ }
29193
+ await track("cli_harness", {
29194
+ ok: result.exitCode === 0,
29195
+ sub: ["list", "add-path", "remove-path"].includes(subArgs[0]) ? subArgs[0] : "unknown"
29196
+ });
29197
+ lastSubcommand = null;
29198
+ await exitAfterFlush(result.exitCode);
29199
+ return;
29200
+ }
29429
29201
  if (args[0] === "uninstall") {
29430
29202
  const subArgs = args.slice(1);
29431
29203
  if (subArgs.includes("--help") || subArgs.includes("-h")) {
@@ -29836,7 +29608,7 @@ USAGE
29836
29608
  WHAT IT DOES
29837
29609
  Walks you through 4 quick steps and writes everything for you:
29838
29610
  1. Where \u2014 global (all projects) or just this project
29839
- 2. Assistants \u2014 which agent CLIs to protect (Claude, Codex, ...)
29611
+ 2. Harnesses \u2014 which agent CLIs to protect (Claude, Codex, ...)
29840
29612
  3. Policies \u2014 presets (combine any), Everything, or a custom pick
29841
29613
  4. Review \u2014 confirms the exact files it will change, then applies
29842
29614
 
@@ -29845,7 +29617,13 @@ FAILPROOF CLOUD
29845
29617
  Connect this machine to Failproof Cloud
29846
29618
  [--no-transcripts] decisions only, no transcripts
29847
29619
  failproofai config --disconnect Stop pulling policy and sending activity
29848
- failproofai config --status Show connection and pause state
29620
+ failproofai config --status Show connection, daemon and pause state
29621
+ failproofai config --pause [--session <id>]
29622
+ Pause enforcement, time-boxed
29623
+ failproofai config --resume [--all]
29624
+ Resume enforcement
29625
+
29626
+ --machine-label <name> Human-readable name in the dashboard
29849
29627
 
29850
29628
  One connection, two capabilities: this machine PULLS centrally-managed
29851
29629
  policies and SENDS what its hooks decided, so the dashboard shows the fleet