0sec-cli 0.21.4 → 0.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. package/0.js +56 -56
  2. package/README.md +48 -63
  3. package/chunks/{adapt-loop-IGBRQAXO.js → adapt-loop-J6QCZZTB.js} +4 -4
  4. package/chunks/{artifact-scraper-LNQJUORS.js → artifact-scraper-MFKT6OI4.js} +2 -2
  5. package/chunks/{assumption-mining-4MJTP6H5.js → assumption-mining-646LYSJY.js} +16 -16
  6. package/chunks/{chunk-Z3IKZX3X.js → chunk-23DG5YKS.js} +1 -1
  7. package/chunks/{chunk-2WSXFFJZ.js → chunk-3E2KALZ5.js} +2 -2
  8. package/chunks/{chunk-TF46UWFU.js → chunk-4G7OVYJX.js} +1 -1
  9. package/chunks/{chunk-E2UOY5PJ.js → chunk-5BTJMINS.js} +12 -70
  10. package/chunks/{chunk-QZVG3BG4.js → chunk-5IBCBQEH.js} +1 -2
  11. package/chunks/{chunk-YA4PUM4H.js → chunk-7XCKYNLO.js} +1 -1
  12. package/chunks/{chunk-BACXRTQ4.js → chunk-AV2LG2L7.js} +87 -767
  13. package/chunks/{chunk-IMGWPNQM.js → chunk-BCSRRQG5.js} +76 -593
  14. package/chunks/{chunk-NOPBXZUV.js → chunk-BOUGKEKA.js} +1 -1
  15. package/chunks/{chunk-RF5QGKLR.js → chunk-CPWA6RWE.js} +3 -3
  16. package/chunks/{chunk-P6YG6TEZ.js → chunk-DKSCN2AB.js} +1 -1
  17. package/chunks/{chunk-YPOA3W4O.js → chunk-GOEA3NW6.js} +877 -110
  18. package/chunks/{chunk-ANDKY54G.js → chunk-HHPWRY2M.js} +4019 -7299
  19. package/chunks/{chunk-NDJU5ZZ7.js → chunk-IFME676H.js} +3 -3
  20. package/chunks/{chunk-HBKCGJAV.js → chunk-IVQV574Y.js} +1 -1
  21. package/chunks/{chunk-W4JOXE4N.js → chunk-IXT64DV5.js} +709 -1929
  22. package/chunks/{chunk-PC6RCKQV.js → chunk-KAU6NLGP.js} +59 -26
  23. package/chunks/{chunk-FAH6V4QM.js → chunk-LGCASF2C.js} +1117 -2291
  24. package/chunks/{chunk-CAJJRUTV.js → chunk-NLH4LMWH.js} +1 -1
  25. package/chunks/chunk-OHHCGDJ2.js +5523 -0
  26. package/chunks/{chunk-YHWWF4QY.js → chunk-OSBPGWTE.js} +3 -3
  27. package/chunks/{chunk-G34QP2XW.js → chunk-TM74SSHC.js} +1 -1
  28. package/chunks/{chunk-VOAXLO4A.js → chunk-TO35Y326.js} +5 -721
  29. package/chunks/{chunk-FWAYJ2IV.js → chunk-TV27OWIW.js} +8 -8
  30. package/chunks/chunk-UELXHK2Y.js +1950 -0
  31. package/chunks/{chunk-CL5KAUM6.js → chunk-V6GHX34G.js} +274 -8
  32. package/chunks/{chunk-WZISK5VC.js → chunk-VWITLWXB.js} +53 -7
  33. package/chunks/{chunk-ER3SUMYJ.js → chunk-W6UWZFKL.js} +0 -195
  34. package/chunks/chunk-WSDDGFA6.js +114 -0
  35. package/chunks/{chunk-5T6D5BVY.js → chunk-WZRVS7SB.js} +1 -1
  36. package/chunks/chunk-X7LAEEUW.js +2143 -0
  37. package/chunks/chunk-XA2O4WXA.js +1228 -0
  38. package/chunks/{chunk-Y5H6E24H.js → chunk-XCISCI4T.js} +1 -1
  39. package/chunks/{chunk-WPQLBN4S.js → chunk-XDKB2P5X.js} +1 -1
  40. package/chunks/{chunk-SBMB4OL3.js → chunk-YL6YAGLL.js} +62 -8
  41. package/chunks/{chunk-O6SD4A36.js → chunk-ZKQ2I3S2.js} +1 -1
  42. package/chunks/{codex-models-TYYV4JNN.js → codex-models-NHQW45SO.js} +5 -3
  43. package/chunks/{commands-WF5RG4I2.js → commands-N6GN5SBF.js} +2152 -3643
  44. package/chunks/{db-U3WQZVAO.js → db-5IWEJUYV.js} +3 -3
  45. package/chunks/{disclose-67GZCDBO.js → disclose-LDNKAVRH.js} +10 -8
  46. package/chunks/{dist-ACC3DZUZ.js → dist-BS4CY4RG.js} +3 -1
  47. package/chunks/{dist-EVTN3UTT.js → dist-FSS7MBDF.js} +2 -2
  48. package/chunks/{dist-F6X62TQK.js → dist-L3XJWWZ6.js} +225 -192
  49. package/chunks/{eval-runner-SA6OUGHZ.js → eval-runner-EMEGF24P.js} +11 -11
  50. package/chunks/{exploit-agent-36Q7XQFP.js → exploit-agent-KW4EB7BE.js} +1 -1
  51. package/chunks/{exploit-autoclimb-J4OTSTZ6.js → exploit-autoclimb-CXJ2ERRO.js} +1 -1
  52. package/chunks/{exploit-climb-PYEJDIC4.js → exploit-climb-TJWLY4XC.js} +2 -2
  53. package/chunks/fix-UUKPXCFB.js +26 -0
  54. package/chunks/{harness-GDUMKGSE.js → harness-C5HOKIEV.js} +3 -3
  55. package/chunks/{hunt-scan-UDJSCISU.js → hunt-scan-KZZ444V2.js} +14 -14
  56. package/chunks/{identity-JTIVJSV7.js → identity-62SVRW4H.js} +1 -1
  57. package/chunks/{kernel-primitive-F2IXUFAX.js → kernel-primitive-Z77IR7YS.js} +2 -2
  58. package/chunks/{kernel-vm-runner-276LHIOV.js → kernel-vm-runner-JW77P276.js} +2 -2
  59. package/chunks/{memsafety-scan-NHUOGTEX.js → memsafety-scan-FCPAXIZE.js} +2 -2
  60. package/chunks/{native-loop-3BSYVDUI.js → native-loop-P6P5GYVJ.js} +10 -10
  61. package/chunks/{npm-detectors-VIYJXKFQ.js → npm-detectors-Z2ZIQCN4.js} +2 -2
  62. package/chunks/orchestrate-DWWUT5RC.js +58 -0
  63. package/chunks/{process-PNMC36JI.js → process-N245QIQG.js} +1 -4
  64. package/chunks/{run-CBGE6DRQ.js → run-GAFKBCTN.js} +14935 -17598
  65. package/chunks/{runtime-PXO5G6UV.js → runtime-H7YBR5IZ.js} +4 -5
  66. package/chunks/{scope-NJHIDDTN.js → scope-QWY4VQZ7.js} +4 -1
  67. package/chunks/{session-store-46WBZM22.js → session-store-XIDFGG7K.js} +2 -2
  68. package/chunks/{variant-candidates-UH2WC2HA.js → variant-candidates-7D4F5E4H.js} +3 -3
  69. package/chunks/{web-recon-prepass-SFXU44SS.js → web-recon-prepass-VXXBPDAV.js} +3 -3
  70. package/cli-path-traversal-fixture.js +352 -0
  71. package/dashboard/assets/bot-Bg1Tp9_d.js +1 -0
  72. package/dashboard/assets/dist-B_dDCPbu.js +1 -0
  73. package/dashboard/assets/findings-page-mayGhNx6.js +5 -0
  74. package/dashboard/assets/format--susmHX7.js +1 -0
  75. package/dashboard/assets/input-TuVPR3Zl.js +1 -0
  76. package/dashboard/assets/jsx-runtime-MGseXjj5.js +3 -0
  77. package/dashboard/assets/live-page-HWWHX99U.js +1 -0
  78. package/dashboard/assets/meta-tile-ckHo-w7k.js +1 -0
  79. package/dashboard/assets/operations-BxZ0OoXO.js +10 -0
  80. package/dashboard/assets/operations-app-D3d35rgo.js +46 -0
  81. package/dashboard/assets/overview-page-W_4IteEr.js +1 -0
  82. package/dashboard/assets/page-header-Bw9AJgBV.js +1 -0
  83. package/dashboard/assets/scans-page-CgzVXHnn.js +1 -0
  84. package/dashboard/assets/siren-KQj8QAnJ.js +1 -0
  85. package/dashboard/assets/table-8EDklulN.js +1 -0
  86. package/dashboard/assets/tabs-CFHPOwg8.js +1 -0
  87. package/dashboard/index.html +2 -3
  88. package/node_modules/node-addon-api/LICENSE.md +9 -0
  89. package/node_modules/node-addon-api/README.md +95 -0
  90. package/node_modules/node-addon-api/common.gypi +21 -0
  91. package/node_modules/node-addon-api/except.gypi +25 -0
  92. package/node_modules/node-addon-api/index.js +14 -0
  93. package/node_modules/node-addon-api/napi-inl.deprecated.h +186 -0
  94. package/node_modules/node-addon-api/napi-inl.h +7250 -0
  95. package/node_modules/node-addon-api/napi.h +3463 -0
  96. package/node_modules/node-addon-api/node_addon_api.gyp +42 -0
  97. package/node_modules/node-addon-api/node_api.gyp +9 -0
  98. package/node_modules/node-addon-api/noexcept.gypi +26 -0
  99. package/node_modules/node-addon-api/nothing.c +0 -0
  100. package/node_modules/node-addon-api/package-support.json +21 -0
  101. package/node_modules/node-addon-api/package.json +481 -0
  102. package/node_modules/node-addon-api/tools/README.md +73 -0
  103. package/node_modules/node-addon-api/tools/check-napi.js +99 -0
  104. package/node_modules/node-addon-api/tools/clang-format.js +71 -0
  105. package/node_modules/node-addon-api/tools/conversion.js +301 -0
  106. package/node_modules/node-gyp-build/LICENSE +21 -0
  107. package/node_modules/node-gyp-build/README.md +58 -0
  108. package/node_modules/node-gyp-build/SECURITY.md +5 -0
  109. package/node_modules/node-gyp-build/bin.js +84 -0
  110. package/node_modules/node-gyp-build/build-test.js +19 -0
  111. package/node_modules/node-gyp-build/index.js +6 -0
  112. package/node_modules/node-gyp-build/node-gyp-build.js +207 -0
  113. package/node_modules/node-gyp-build/optional.js +7 -0
  114. package/node_modules/node-gyp-build/package.json +43 -0
  115. package/node_modules/tree-sitter/LICENSE +21 -0
  116. package/node_modules/tree-sitter/README.md +90 -0
  117. package/node_modules/tree-sitter/binding.gyp +76 -0
  118. package/node_modules/tree-sitter/index.js +895 -0
  119. package/node_modules/tree-sitter/package.json +62 -0
  120. package/node_modules/tree-sitter/prebuilds/darwin-arm64/tree-sitter.node +0 -0
  121. package/node_modules/tree-sitter/prebuilds/darwin-x64/tree-sitter.node +0 -0
  122. package/node_modules/tree-sitter/prebuilds/linux-x64/tree-sitter.node +0 -0
  123. package/node_modules/tree-sitter/prebuilds/win32-x64/tree-sitter.node +0 -0
  124. package/node_modules/tree-sitter/src/addon_data.h +47 -0
  125. package/node_modules/tree-sitter/src/binding.cc +35 -0
  126. package/node_modules/tree-sitter/src/conversions.cc +140 -0
  127. package/node_modules/tree-sitter/src/conversions.h +22 -0
  128. package/node_modules/tree-sitter/src/language.cc +106 -0
  129. package/node_modules/tree-sitter/src/language.h +17 -0
  130. package/node_modules/tree-sitter/src/logger.cc +70 -0
  131. package/node_modules/tree-sitter/src/logger.h +19 -0
  132. package/node_modules/tree-sitter/src/lookaheaditerator.cc +122 -0
  133. package/node_modules/tree-sitter/src/lookaheaditerator.h +33 -0
  134. package/node_modules/tree-sitter/src/node.cc +1056 -0
  135. package/node_modules/tree-sitter/src/node.h +30 -0
  136. package/node_modules/tree-sitter/src/parser.cc +298 -0
  137. package/node_modules/tree-sitter/src/parser.h +35 -0
  138. package/node_modules/tree-sitter/src/query.cc +383 -0
  139. package/node_modules/tree-sitter/src/query.h +39 -0
  140. package/node_modules/tree-sitter/src/tree.cc +307 -0
  141. package/node_modules/tree-sitter/src/tree.h +45 -0
  142. package/node_modules/tree-sitter/src/tree_cursor.cc +213 -0
  143. package/node_modules/tree-sitter/src/tree_cursor.h +52 -0
  144. package/node_modules/tree-sitter/tree-sitter.d.ts +211 -0
  145. package/node_modules/tree-sitter/vendor/tree-sitter/LICENSE +21 -0
  146. package/node_modules/tree-sitter/vendor/tree-sitter/README.md +21 -0
  147. package/node_modules/tree-sitter/vendor/tree-sitter/lib/README.md +6 -0
  148. package/node_modules/tree-sitter/vendor/tree-sitter/lib/include/tree_sitter/api.h +1266 -0
  149. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/alloc.c +48 -0
  150. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/alloc.h +41 -0
  151. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/array.h +290 -0
  152. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/atomic.h +68 -0
  153. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/clock.h +146 -0
  154. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/error_costs.h +11 -0
  155. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/get_changed_ranges.c +501 -0
  156. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/get_changed_ranges.h +36 -0
  157. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/host.h +21 -0
  158. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/language.c +221 -0
  159. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/language.h +299 -0
  160. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/length.h +52 -0
  161. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/lexer.c +419 -0
  162. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/lexer.h +49 -0
  163. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/lib.c +14 -0
  164. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/node.c +774 -0
  165. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/parser.c +2151 -0
  166. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/parser.h +230 -0
  167. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/point.h +62 -0
  168. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/query.c +4134 -0
  169. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/reduce_action.h +34 -0
  170. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/reusable_node.h +95 -0
  171. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/stack.c +899 -0
  172. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/stack.h +133 -0
  173. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/subtree.c +1060 -0
  174. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/subtree.h +382 -0
  175. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/tree.c +165 -0
  176. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/tree.h +31 -0
  177. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/tree_cursor.c +714 -0
  178. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/tree_cursor.h +48 -0
  179. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/ICU_SHA +1 -0
  180. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/LICENSE +414 -0
  181. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/README.md +29 -0
  182. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/ptypes.h +1 -0
  183. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/umachine.h +448 -0
  184. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/urename.h +1 -0
  185. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/utf.h +1 -0
  186. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/utf16.h +733 -0
  187. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode/utf8.h +881 -0
  188. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/unicode.h +50 -0
  189. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/wasm/stdlib-symbols.txt +23 -0
  190. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/wasm/stdlib.c +109 -0
  191. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/wasm/wasm-stdlib.h +1294 -0
  192. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/wasm_store.c +1823 -0
  193. package/node_modules/tree-sitter/vendor/tree-sitter/lib/src/wasm_store.h +31 -0
  194. package/node_modules/tree-sitter/vendor/tree-sitter/test/fixtures/grammars/.gitkeep +0 -0
  195. package/node_modules/tree-sitter-c/LICENSE +21 -0
  196. package/node_modules/tree-sitter-c/README.md +18 -0
  197. package/node_modules/tree-sitter-c/binding.gyp +20 -0
  198. package/node_modules/tree-sitter-c/bindings/node/binding.cc +20 -0
  199. package/node_modules/tree-sitter-c/bindings/node/index.d.ts +28 -0
  200. package/node_modules/tree-sitter-c/bindings/node/index.js +7 -0
  201. package/node_modules/tree-sitter-c/grammar.js +1463 -0
  202. package/node_modules/tree-sitter-c/package.json +108 -0
  203. package/node_modules/tree-sitter-c/prebuilds/darwin-arm64/tree-sitter-c.node +0 -0
  204. package/node_modules/tree-sitter-c/prebuilds/darwin-x64/tree-sitter-c.node +0 -0
  205. package/node_modules/tree-sitter-c/prebuilds/linux-x64/tree-sitter-c.node +0 -0
  206. package/node_modules/tree-sitter-c/prebuilds/win32-x64/tree-sitter-c.node +0 -0
  207. package/node_modules/tree-sitter-c/queries/highlights.scm +81 -0
  208. package/node_modules/tree-sitter-c/queries/tags.scm +9 -0
  209. package/node_modules/tree-sitter-c/src/grammar.json +9682 -0
  210. package/node_modules/tree-sitter-c/src/node-types.json +4556 -0
  211. package/node_modules/tree-sitter-c/src/parser.c +113852 -0
  212. package/node_modules/tree-sitter-c/src/tree_sitter/alloc.h +54 -0
  213. package/node_modules/tree-sitter-c/src/tree_sitter/array.h +290 -0
  214. package/node_modules/tree-sitter-c/src/tree_sitter/parser.h +265 -0
  215. package/package.json +10 -3
  216. package/vendor/tree-sitter-0.21.1-node24.json +16 -0
  217. package/vendor/tree-sitter-0.21.1-node24.tgz +0 -0
  218. package/chunks/chunk-CGHCEN7W.js +0 -169
  219. package/chunks/chunk-CXKDWFNO.js +0 -102
  220. package/chunks/chunk-GIQKESMN.js +0 -687
  221. package/chunks/chunk-HPQZHOIU.js +0 -67
  222. package/chunks/chunk-PD2BBBKT.js +0 -245
  223. package/chunks/chunk-WF5W75WY.js +0 -1070
  224. package/chunks/fix-J2KBRJDV.js +0 -12
  225. package/chunks/orchestrate-DHBFRTSI.js +0 -59
  226. package/chunks/replay-runner-LK2XFIVG.js +0 -39
  227. package/dashboard/assets/bot-yILPnRgD.js +0 -1
  228. package/dashboard/assets/chevron-down-BeDJ-Vka.js +0 -1
  229. package/dashboard/assets/circle-alert-BEnMBMT-.js +0 -1
  230. package/dashboard/assets/client-DRYsuOGl.js +0 -9
  231. package/dashboard/assets/copy-Bi5vzoF9.js +0 -1
  232. package/dashboard/assets/desktop-BT7RkC4q.js +0 -32
  233. package/dashboard/assets/desktop-OaBI0E0e.css +0 -1
  234. package/dashboard/assets/dist-CmX-h7a4.js +0 -1
  235. package/dashboard/assets/dist-DAwYQmf_.js +0 -45
  236. package/dashboard/assets/findings-page-B4rwF6fZ.js +0 -5
  237. package/dashboard/assets/format-LNQSSpn6.js +0 -1
  238. package/dashboard/assets/jsx-runtime-C7oxC63R.js +0 -1
  239. package/dashboard/assets/live-page-DoNwpPAO.js +0 -1
  240. package/dashboard/assets/meta-tile-BMtBC-Qq.js +0 -1
  241. package/dashboard/assets/operations-app-ER0gg7GN.js +0 -2
  242. package/dashboard/assets/operations-e2CGYKB1.js +0 -4
  243. package/dashboard/assets/overview-page-CmeWH17A.js +0 -1
  244. package/dashboard/assets/page-header-CIJyycfz.js +0 -1
  245. package/dashboard/assets/play-Dwo-9xt6.js +0 -1
  246. package/dashboard/assets/scans-page-CuhzWhHx.js +0 -1
  247. package/dashboard/assets/search-CiooTLck.js +0 -1
  248. package/dashboard/assets/siren-CCBwTSuk.js +0 -1
  249. package/dashboard/assets/table-BgsSBdke.js +0 -1
  250. package/dashboard/assets/tabs-vn6v7Bk6.js +0 -1
  251. package/dashboard/desktop.html +0 -21
@@ -5,19 +5,21 @@ import {
5
5
  ANALYTICS_LEVEL_ENV,
6
6
  analyticsPipeline,
7
7
  levelAtLeast,
8
+ loadCloudCredentials,
8
9
  resolveAnalyticsLevel
9
- } from "./chunk-ANDKY54G.js";
10
+ } from "./chunk-OHHCGDJ2.js";
10
11
  import {
11
12
  DEFAULT_ALLOW_MODEL_SELF_EXTENSION,
12
13
  createPresentationEvent,
13
14
  homeStateDir
14
- } from "./chunk-PC6RCKQV.js";
15
+ } from "./chunk-KAU6NLGP.js";
15
16
 
16
17
  // packages/cli/src/tui/settings-store.ts
17
18
  import { useSyncExternalStore } from "react";
18
19
 
19
20
  // packages/cli/src/tui/settings.ts
20
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
21
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
22
+ import { randomUUID } from "node:crypto";
21
23
  import { dirname as dirname2, join as join3 } from "node:path";
22
24
 
23
25
  // packages/cli/src/tui/keybindings.ts
@@ -202,62 +204,15 @@ var KEYBINDINGS = [
202
204
  // ── View ────────────────────────────────────────────────────────────────────
203
205
  // The only rebindable set: self-contained, stateless toggles that call
204
206
  // `updateSetting(...)` and take no part in text entry or modal cycles.
205
- {
206
- id: "view.left-sidebar",
207
- keys: "Ctrl+B",
208
- defaultChords: ["ctrl+b"],
209
- rebindable: true,
210
- description: "Toggle the left sidebar (persists across the session).",
211
- category: "View",
212
- handler: 'if (matchesBinding(key, "view.left-sidebar", \u2026)) updateSetting("showLeftSidebar", \u2026)'
213
- },
214
- {
215
- id: "view.right-sidebar",
216
- keys: "Ctrl+L",
217
- defaultChords: ["ctrl+l"],
218
- rebindable: true,
219
- description: "Toggle the right sidebar (persists across the session).",
220
- category: "View",
221
- handler: 'if (matchesBinding(key, "view.right-sidebar", \u2026)) updateSetting("showRightSidebar", \u2026)'
222
- },
223
207
  {
224
208
  id: "view.transcript-detail",
225
209
  keys: "Ctrl+R",
226
210
  defaultChords: ["ctrl+r"],
227
211
  rebindable: true,
228
- description: "Toggle the whole transcript between collapsed and expanded tool/reasoning detail (persists across the session).",
212
+ description: "Toggle folded tool/reasoning detail; output previews stay bounded until explicitly disclosed.",
229
213
  category: "View",
230
214
  handler: 'if (matchesBinding(key, "view.transcript-detail", \u2026)) updateSetting("transcriptDetail", \u2026)'
231
215
  },
232
- {
233
- id: "overlay.review-toggle",
234
- keys: "Ctrl+O",
235
- defaultChords: ["ctrl+o"],
236
- rebindable: true,
237
- description: "Open (and close) the full-screen transcript-review overlay.",
238
- category: "View",
239
- handler: 'if (matchesBinding(key, "overlay.review-toggle", \u2026)) setReviewOpen(true) / (reviewOpen) close'
240
- },
241
- {
242
- id: "overlay.review-top",
243
- keys: "Ctrl+Home",
244
- defaultChords: ["ctrl+home"],
245
- rebindable: false,
246
- description: "Jump to the top of the transcript-review overlay.",
247
- category: "View",
248
- lockReason: "modal",
249
- handler: 'if (reviewOpen && key.ctrl && key.name === "home") review.scrollY = 0'
250
- },
251
- {
252
- id: "overlay.review-bottom",
253
- keys: "Ctrl+End",
254
- defaultChords: ["ctrl+end"],
255
- rebindable: false,
256
- description: "Jump to the bottom of the transcript-review overlay.",
257
- category: "View",
258
- lockReason: "modal",
259
- handler: 'if (reviewOpen && key.ctrl && key.name === "end") review.scrollY = review.maxScrollY'
260
- },
261
216
  // ── Autonomy ─────────────────────────────────────────────────────────────────
262
217
  {
263
218
  id: "autonomy.cycle-mode",
@@ -1387,7 +1342,7 @@ var DEFS = [
1387
1342
  {
1388
1343
  key: "showLogo",
1389
1344
  label: "Logo",
1390
- description: 'Block "ZEROSECURITY" wordmark shown on an empty transcript.',
1345
+ description: 'Block "0.SECURITY" wordmark shown on an empty transcript.',
1391
1346
  kind: "boolean",
1392
1347
  default: true,
1393
1348
  group: "Display"
@@ -1416,22 +1371,6 @@ var DEFS = [
1416
1371
  default: true,
1417
1372
  group: "Transcript"
1418
1373
  },
1419
- {
1420
- key: "showLeftSidebar",
1421
- label: "Left sidebar",
1422
- description: "Left sidebar in the chat view: recent sessions to resume, plus this run's findings. Hidden on narrow terminals.",
1423
- kind: "boolean",
1424
- default: false,
1425
- group: "Display"
1426
- },
1427
- {
1428
- key: "showRightSidebar",
1429
- label: "Right sidebar",
1430
- description: "Right sidebar: live agents, their activity, the current plan and findings. Hidden on narrow terminals.",
1431
- kind: "boolean",
1432
- default: false,
1433
- group: "Display"
1434
- },
1435
1374
  {
1436
1375
  key: "showTimestamps",
1437
1376
  label: "Timestamps",
@@ -1511,9 +1450,9 @@ var DEFS = [
1511
1450
  {
1512
1451
  key: "roleLabelStyle",
1513
1452
  label: "Role label",
1514
- description: 'Speaker name on each message: "You" for your turns, "0" for answers. Full and short add the elapsed age when one is known; glyph shows the name alone; off omits the label entirely. Bubble cards carry it top-left on the card border, with your messages right-aligned and answers left.',
1453
+ description: 'Labels your messages as "You"; assistant replies stay unlabeled. Full and short include the optional age, glyph shows the name alone, and off hides the label.',
1515
1454
  kind: "enum",
1516
- default: "full",
1455
+ default: "off",
1517
1456
  choices: ["full", "short", "glyph", "off"],
1518
1457
  group: "Display"
1519
1458
  },
@@ -1537,16 +1476,16 @@ var DEFS = [
1537
1476
  {
1538
1477
  key: "transcriptDetail",
1539
1478
  label: "Transcript detail",
1540
- description: "Expanded shows every step (thinking, tool calls + their output) inline \u2014 the default; collapsed folds each turn's successful steps into one-line summaries (failures always show). Ctrl+R toggles it live.",
1479
+ description: "Fold tool and reasoning steps. Output previews stay within 20 lines; click a card for retained detail.",
1541
1480
  kind: "enum",
1542
- default: "expanded",
1543
- choices: ["expanded", "collapsed"],
1481
+ default: "collapsed",
1482
+ choices: ["collapsed", "expanded"],
1544
1483
  group: "Transcript"
1545
1484
  },
1546
1485
  {
1547
1486
  key: "autoCompaction",
1548
1487
  label: "Auto-compaction",
1549
- description: "Automatically summarise older turns when the conversation nears the model's context window (the recap stays viewable with Ctrl+O).",
1488
+ description: "Automatically summarise older turns when the conversation nears the model's context window.",
1550
1489
  kind: "boolean",
1551
1490
  default: true,
1552
1491
  group: "Context"
@@ -1606,7 +1545,7 @@ var DEFS = [
1606
1545
  label: "Token usage",
1607
1546
  description: 'Per-turn "in\u2192out tok" line under each answer.',
1608
1547
  kind: "boolean",
1609
- default: true,
1548
+ default: false,
1610
1549
  group: "Telemetry"
1611
1550
  },
1612
1551
  {
@@ -1614,7 +1553,7 @@ var DEFS = [
1614
1553
  label: "Cost",
1615
1554
  description: "Estimated dollar cost, per turn and in the status bar.",
1616
1555
  kind: "boolean",
1617
- default: true,
1556
+ default: false,
1618
1557
  group: "Telemetry"
1619
1558
  },
1620
1559
  {
@@ -1646,7 +1585,7 @@ var DEFS = [
1646
1585
  {
1647
1586
  key: "logoAnimation",
1648
1587
  label: "Logo animation",
1649
- description: 'Intro animation for the "ZEROSECURITY" wordmark: glitch (a neon-flecked scramble that resolves \u2014 the default), rainbow (a looping hue sweep), matrix (a green matrix-rain cascade), wave (a rippling cyan wavefront), neon (a neon-sign warm-up flicker), shimmer (a bright comet with a gradient tail), pulse (the slash breathes), strike (an orange slash strikes through the 0), draw (letters draw in behind a pen tip), fade (a centre-out bloom), typein (per-cell reveal), sweep (a bright bar wipes across) or off (static).',
1588
+ description: 'Intro animation for the "0.SECURITY" wordmark: glitch (a neon-flecked scramble that resolves \u2014 the default), rainbow (a looping hue sweep), matrix (a green matrix-rain cascade), wave (a rippling cyan wavefront), neon (a neon-sign warm-up flicker), shimmer (a bright comet with a gradient tail), pulse (the slash breathes), strike (an orange slash strikes through the 0), draw (letters draw in behind a pen tip), fade (a centre-out bloom), typein (per-cell reveal), sweep (a bright bar wipes across) or off (static).',
1650
1589
  kind: "enum",
1651
1590
  default: "glitch",
1652
1591
  choices: [
@@ -1677,19 +1616,19 @@ var DEFS = [
1677
1616
  },
1678
1617
  {
1679
1618
  key: "analyticsLevel",
1680
- label: "Analytics and training data",
1681
- description: "Full is the new-install default. Usage shares feature counters and error categories, not tool content. Commands adds tool arguments/results and submitted code for model training and security research; Full also adds scope and findings. Recognized credentials are scrubbed; emails, URLs, identifiers and other content are retained. Sending uses authenticated Cloud storage and is not anonymous. Each tool/code content field is limited to 256 KiB after credential scrubbing; oversized records are reported locally, not silently truncated. Explicit ZERO_ANALYTICS_LEVEL and offline/no-telemetry/DO_NOT_TRACK restrictions win over broader settings. Problem reports are separate. Applies to this computer, not this project.",
1619
+ label: "Data sharing",
1620
+ description: "Usage sends counts, timing, cost and error categories. Commands adds tool inputs/outputs and code; Full adds scope and findings. Credentials are scrubbed, but identifying content may remain. Uploads require an endpoint and are not anonymous. Environment opt-outs take precedence. Applies to this computer.",
1682
1621
  kind: "enum",
1683
- default: "full",
1622
+ default: "off",
1684
1623
  choices: ["off", "usage", "commands", "full"],
1685
1624
  group: "Privacy"
1686
1625
  },
1687
1626
  {
1688
1627
  key: "diagnosticReporting",
1689
1628
  label: "Problem reports",
1690
- description: "Send limited diagnostics automatically by default, ask first, or turn reporting off. Uses Cloud sign-in or a configured HTTPS feedback endpoint. Never includes prompts, tool arguments or output. Applies to this computer, not this project.",
1629
+ description: "Review diagnostics before sending, send automatically, or keep reports local. Requires a configured endpoint. Off/Usage sharing sends categories only; Commands/Full can include scrubbed errors, stacks and failure output. Applies to this computer.",
1691
1630
  kind: "enum",
1692
- default: "automatic",
1631
+ default: "ask",
1693
1632
  choices: ["off", "ask", "automatic"],
1694
1633
  group: "Privacy"
1695
1634
  },
@@ -1702,6 +1641,15 @@ var DEFS = [
1702
1641
  choices: ["off", "notify", "automatic"],
1703
1642
  group: "Updates"
1704
1643
  },
1644
+ {
1645
+ key: "executionProfile",
1646
+ label: "Execution profile",
1647
+ description: "SmolVM runs the whole console, agents, browser and shell in the online Kali workbench. Prepare its approved image and explicit credential grants with 0 workbench setup. Applies to this computer on the next launch; failures never fall back to host execution.",
1648
+ kind: "enum",
1649
+ default: "local",
1650
+ choices: ["local", "smolvm"],
1651
+ group: "Security"
1652
+ },
1705
1653
  {
1706
1654
  key: "symbolPreset",
1707
1655
  label: "Symbols",
@@ -1741,8 +1689,6 @@ var DEFAULT_SETTINGS = {
1741
1689
  showRuntimeNotices: true,
1742
1690
  showTurnSummary: false,
1743
1691
  showSubagents: true,
1744
- showLeftSidebar: false,
1745
- showRightSidebar: false,
1746
1692
  showTimestamps: false,
1747
1693
  showObjective: true,
1748
1694
  showScope: true,
@@ -1750,13 +1696,14 @@ var DEFAULT_SETTINGS = {
1750
1696
  composerStyle: "border",
1751
1697
  busyInputMode: "steer",
1752
1698
  onboardingCompleted: false,
1699
+ executionProfile: "local",
1753
1700
  allowSubagentPeerMessaging: true,
1754
1701
  allowSubagentOperatorMessaging: true,
1755
1702
  transcriptStyle: "minimal",
1756
- roleLabelStyle: "full",
1703
+ roleLabelStyle: "off",
1757
1704
  toolCardStyle: "compact",
1758
1705
  richToolCards: true,
1759
- transcriptDetail: "expanded",
1706
+ transcriptDetail: "collapsed",
1760
1707
  autoCompaction: true,
1761
1708
  compactionThreshold: "80%",
1762
1709
  theme: DEFAULT_THEME_NAME,
@@ -1764,21 +1711,22 @@ var DEFAULT_SETTINGS = {
1764
1711
  allowDevSourceUpdates: false,
1765
1712
  autoEvolveFinderLenses: false,
1766
1713
  autoPromoteFinderLenses: false,
1767
- showTokenUsage: true,
1768
- showCost: true,
1714
+ showTokenUsage: false,
1715
+ showCost: false,
1769
1716
  showContextMeter: true,
1770
1717
  modelDisplay: "statusbar",
1771
1718
  elapsedTimer: "left",
1772
1719
  logoAnimation: "glitch",
1773
1720
  reduceMotion: false,
1774
- analyticsLevel: "full",
1775
- diagnosticReporting: "automatic",
1721
+ analyticsLevel: "off",
1722
+ diagnosticReporting: "ask",
1776
1723
  diagnosticReportingPrompted: false,
1777
1724
  updatePolicy: "automatic",
1778
1725
  symbolPreset: "unicode",
1779
1726
  rosterSort: "attention",
1780
1727
  leaderKey: "off",
1781
- keybindings: {}
1728
+ keybindings: {},
1729
+ modelPreference: null
1782
1730
  };
1783
1731
  var SETTINGS_FILENAME = "tui-settings.json";
1784
1732
  function settingsFilePath(homeDir2) {
@@ -1816,6 +1764,9 @@ function strictValueAt(raw, key) {
1816
1764
  if (key === "keybindings") {
1817
1765
  return typeof value === "object" && value !== null && !Array.isArray(value) ? sanitizeKeybindingOverrides(value) : void 0;
1818
1766
  }
1767
+ if (key === "modelPreference") {
1768
+ return normalizeModelPreference(value);
1769
+ }
1819
1770
  const def = DEF_BY_KEY.get(key);
1820
1771
  if (def?.kind === "boolean") {
1821
1772
  return typeof value === "boolean" ? value : void 0;
@@ -1824,7 +1775,7 @@ function strictValueAt(raw, key) {
1824
1775
  return typeof value === "string" && choices.includes(value) ? value : void 0;
1825
1776
  }
1826
1777
  function isOperatorSetting(key) {
1827
- return key === "onboardingCompleted" || key === "analyticsLevel" || key === "diagnosticReporting" || key === "diagnosticReportingPrompted" || key === "updatePolicy" || key === "allowDevSourceUpdates";
1778
+ return key === "onboardingCompleted" || key === "analyticsLevel" || key === "diagnosticReporting" || key === "diagnosticReportingPrompted" || key === "updatePolicy" || key === "allowDevSourceUpdates" || key === "executionProfile" || key === "modelPreference";
1828
1779
  }
1829
1780
  function resolveLayeredSettings(globalRaw, projectRaw) {
1830
1781
  const settings = {};
@@ -1862,13 +1813,6 @@ function booleanAt(raw, key) {
1862
1813
  const value = rawValue(raw, key);
1863
1814
  return typeof value === "boolean" ? value : DEFAULT_SETTINGS[key];
1864
1815
  }
1865
- function booleanWithLegacy(raw, key, legacyKey) {
1866
- const value = rawValue(raw, key);
1867
- if (typeof value === "boolean") return value;
1868
- const legacy = rawValue(raw, legacyKey);
1869
- if (typeof legacy === "boolean") return legacy;
1870
- return DEFAULT_SETTINGS[key];
1871
- }
1872
1816
  function enumAt(raw, key) {
1873
1817
  const def = DEF_BY_KEY.get(key);
1874
1818
  const value = rawValue(raw, key);
@@ -1882,6 +1826,13 @@ function themeAt(raw) {
1882
1826
  function keybindingsAt(raw) {
1883
1827
  return sanitizeKeybindingOverrides(rawValue(raw, "keybindings"));
1884
1828
  }
1829
+ function normalizeModelPreference(raw) {
1830
+ if (raw === null) return null;
1831
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return void 0;
1832
+ const value = raw;
1833
+ if (typeof value.providerId !== "string" || !value.providerId.trim() || typeof value.model !== "string" || !value.model.trim() || typeof value.connectionIdentity !== "string" || !/^[a-f0-9]{64}$/.test(value.connectionIdentity)) return void 0;
1834
+ return { providerId: value.providerId, model: value.model, connectionIdentity: value.connectionIdentity };
1835
+ }
1885
1836
  function normalizeSettings(raw) {
1886
1837
  return {
1887
1838
  showStatusBar: booleanAt(raw, "showStatusBar"),
@@ -1892,10 +1843,6 @@ function normalizeSettings(raw) {
1892
1843
  showRuntimeNotices: booleanAt(raw, "showRuntimeNotices"),
1893
1844
  showTurnSummary: booleanAt(raw, "showTurnSummary"),
1894
1845
  showSubagents: booleanAt(raw, "showSubagents"),
1895
- showLeftSidebar: booleanAt(raw, "showLeftSidebar"),
1896
- // Back-compat: the right sidebar was `showAgentRail` before it grew a
1897
- // context strip and a left twin. A pre-rename file keeps its choice.
1898
- showRightSidebar: booleanWithLegacy(raw, "showRightSidebar", "showAgentRail"),
1899
1846
  showTimestamps: booleanAt(raw, "showTimestamps"),
1900
1847
  showObjective: booleanAt(raw, "showObjective"),
1901
1848
  showScope: booleanAt(raw, "showScope"),
@@ -1903,6 +1850,7 @@ function normalizeSettings(raw) {
1903
1850
  composerStyle: enumAt(raw, "composerStyle"),
1904
1851
  busyInputMode: enumAt(raw, "busyInputMode"),
1905
1852
  onboardingCompleted: booleanAt(raw, "onboardingCompleted"),
1853
+ executionProfile: enumAt(raw, "executionProfile"),
1906
1854
  allowSubagentPeerMessaging: booleanAt(raw, "allowSubagentPeerMessaging"),
1907
1855
  allowSubagentOperatorMessaging: booleanAt(raw, "allowSubagentOperatorMessaging"),
1908
1856
  transcriptStyle: enumAt(raw, "transcriptStyle"),
@@ -1931,25 +1879,37 @@ function normalizeSettings(raw) {
1931
1879
  symbolPreset: enumAt(raw, "symbolPreset"),
1932
1880
  rosterSort: enumAt(raw, "rosterSort"),
1933
1881
  leaderKey: enumAt(raw, "leaderKey"),
1934
- keybindings: keybindingsAt(raw)
1882
+ keybindings: keybindingsAt(raw),
1883
+ modelPreference: normalizeModelPreference(rawValue(raw, "modelPreference")) ?? null
1935
1884
  };
1936
1885
  }
1937
1886
  function loadSettings(homeDir2, projectDir2) {
1938
1887
  return loadLayeredSettings({ homeDir: homeDir2, projectDir: projectDir2 }).settings;
1939
1888
  }
1940
- function loadGlobalSettings(homeDir2) {
1889
+ function loadGlobalSettings(homeDir2, options = {}) {
1941
1890
  syncThemeChoices(homeDir2);
1942
- return normalizeSettings(readRawSettingsFile(settingsFilePath(homeDir2)));
1891
+ const raw = readRawSettingsFile(settingsFilePath(homeDir2));
1892
+ if (options.requireExecutionProfile && strictValueAt(raw, "executionProfile") === void 0) {
1893
+ throw new Error("Configured workbench execution profile is missing or invalid. Run 0 workbench setup or 0 workbench disable to make an explicit operator choice; host fallback is refused.");
1894
+ }
1895
+ return normalizeSettings(raw);
1943
1896
  }
1944
1897
  function saveSettings(settings, homeDir2) {
1898
+ const path = settingsFilePath(homeDir2);
1899
+ const temporary = `${path}.${randomUUID()}.tmp`;
1945
1900
  try {
1946
- const path = settingsFilePath(homeDir2);
1947
1901
  mkdirSync2(dirname2(path), { recursive: true });
1948
- writeFileSync2(path, `${JSON.stringify(normalizeSettings(settings), null, 2)}
1949
- `, "utf8");
1902
+ writeFileSync2(temporary, `${JSON.stringify(normalizeSettings(settings), null, 2)}
1903
+ `, { encoding: "utf8", flag: "wx", mode: 384 });
1904
+ renameSync(temporary, path);
1950
1905
  return true;
1951
1906
  } catch {
1952
1907
  return false;
1908
+ } finally {
1909
+ try {
1910
+ rmSync2(temporary, { force: true });
1911
+ } catch {
1912
+ }
1953
1913
  }
1954
1914
  }
1955
1915
  function sanitizeOverrides(raw) {
@@ -2002,6 +1962,10 @@ function getSettings() {
2002
1962
  }
2003
1963
  return cached.settings;
2004
1964
  }
1965
+ function getSettingSources() {
1966
+ getSettings();
1967
+ return cached.sources;
1968
+ }
2005
1969
  function setSettings(next) {
2006
1970
  const value = normalizeSettings(next);
2007
1971
  const saved = saveSettings(value, homeDir);
@@ -2378,6 +2342,789 @@ function writePresentationLine(text, eventType = "output.stdout") {
2378
2342
  `, eventType);
2379
2343
  }
2380
2344
 
2345
+ // packages/cli/src/tui/provider-status.ts
2346
+ var PROVIDER_DEFS = [
2347
+ {
2348
+ id: "chatgpt-codex",
2349
+ label: "ChatGPT Codex",
2350
+ methods: ["oauth"],
2351
+ // OAuth, not an API key. Both tokens are accepted and the access token is
2352
+ // read first (llm-api.ts L874-875, L1386-1394), so it leads the list.
2353
+ envVars: ["ZERO_CHATGPT_ACCESS_TOKEN", "ZERO_CHATGPT_OAUTH_REFRESH_TOKEN"],
2354
+ fileSource: "~/.codex/auth.json (override with ZERO_CHATGPT_AUTH_FILE)",
2355
+ hint: "run `codex login` to write ~/.codex/auth.json, or invoke 0 with env ZERO_CHATGPT_OAUTH_REFRESH_TOKEN=..."
2356
+ },
2357
+ {
2358
+ id: "deepseek",
2359
+ label: "DeepSeek",
2360
+ methods: ["api-key"],
2361
+ envVars: ["DEEPSEEK_API_KEY"],
2362
+ hint: "set DEEPSEEK_API_KEY (endpoint override: DEEPSEEK_BASE_URL)"
2363
+ },
2364
+ {
2365
+ id: "openrouter",
2366
+ label: "OpenRouter",
2367
+ // OAuth (PKCE browser sign-in) is preferred; a pasted OPENROUTER_API_KEY
2368
+ // remains a secondary path. The browser flow provisions a durable
2369
+ // `sk-or-...` key and stores it as an api_key record written to
2370
+ // OPENROUTER_API_KEY (envVars[0], which llm-api already reads), so — unlike
2371
+ // the xai/kimi device flows — the credential is a key, not OAuth tokens.
2372
+ methods: ["oauth", "api-key"],
2373
+ envVars: ["OPENROUTER_API_KEY"],
2374
+ hint: "sign in with your OpenRouter account, or set OPENROUTER_API_KEY=sk-or-... from openrouter.ai/keys"
2375
+ },
2376
+ {
2377
+ id: "azure",
2378
+ label: "Azure OpenAI",
2379
+ methods: ["api-key"],
2380
+ // Only the Azure key authenticates; the deployment URL comes from
2381
+ // AZURE_OPENAI_BASE_URL / OPENAI_BASE_URL / ~/.codex/config.toml
2382
+ // (L1435-1445). A key with no reachable base URL still counts as
2383
+ // configured here because detectProvider selects azure on the key alone.
2384
+ envVars: ["AZURE_OPENAI_API_KEY"],
2385
+ hint: "set AZURE_OPENAI_API_KEY plus AZURE_OPENAI_BASE_URL (or [model_providers.azure] in ~/.codex/config.toml)"
2386
+ },
2387
+ {
2388
+ id: "openai",
2389
+ label: "OpenAI",
2390
+ methods: ["api-key"],
2391
+ envVars: ["OPENAI_API_KEY"],
2392
+ hint: "set OPENAI_API_KEY=sk-... from platform.openai.com/api-keys"
2393
+ },
2394
+ {
2395
+ id: "z-ai",
2396
+ label: "Z.ai GLM",
2397
+ methods: ["api-key"],
2398
+ envVars: ["Z_AI_API_KEY"],
2399
+ hint: "set Z_AI_API_KEY from your Z.ai Coding Plan (endpoint override: Z_AI_BASE_URL)"
2400
+ },
2401
+ {
2402
+ id: "kimi",
2403
+ label: "Moonshot Kimi",
2404
+ // OAuth (device sign-in) is preferred; a pasted KIMI_API_KEY remains a
2405
+ // secondary path. The OAuth access token is written to KIMI_API_KEY as a
2406
+ // Bearer (llm-api.ts reads it there, no change needed), and the refresh
2407
+ // token to the 0-owned var so the store can round-trip it.
2408
+ methods: ["oauth", "api-key"],
2409
+ envVars: ["KIMI_API_KEY", "ZERO_KIMI_OAUTH_REFRESH_TOKEN"],
2410
+ hint: "sign in with your Kimi account, or set KIMI_API_KEY from your Kimi coding plan (endpoint override: KIMI_BASE_URL)"
2411
+ },
2412
+ {
2413
+ id: "qwen",
2414
+ label: "Alibaba Qwen",
2415
+ methods: ["api-key"],
2416
+ envVars: ["QWEN_API_KEY"],
2417
+ hint: "set QWEN_API_KEY from Alibaba Model Studio (endpoint override: QWEN_BASE_URL)"
2418
+ },
2419
+ {
2420
+ id: "xai",
2421
+ label: "xAI Grok",
2422
+ // OAuth (device sign-in) is preferred; a pasted XAI_API_KEY remains a
2423
+ // secondary path. The OAuth access token is written to XAI_API_KEY as a
2424
+ // Bearer (llm-api.ts reads it there, no change needed), and the refresh
2425
+ // token to the 0-owned var so the store can round-trip it.
2426
+ methods: ["oauth", "api-key"],
2427
+ envVars: ["XAI_API_KEY", "ZERO_XAI_OAUTH_REFRESH_TOKEN"],
2428
+ hint: "sign in with your xAI account, or set XAI_API_KEY from console.x.ai (endpoint override: XAI_BASE_URL)"
2429
+ },
2430
+ {
2431
+ id: "opencode",
2432
+ label: "OpenCode Zen",
2433
+ methods: ["api-key"],
2434
+ envVars: ["OPENCODE_API_KEY"],
2435
+ hint: "set OPENCODE_API_KEY from opencode.ai/auth (endpoint override: OPENCODE_BASE_URL)"
2436
+ },
2437
+ {
2438
+ id: "copilot",
2439
+ label: "GitHub Copilot",
2440
+ // OAuth only (device-code sign-in). The GitHub device-flow access token is
2441
+ // written to ZERO_COPILOT_GITHUB_TOKEN (envVars[0]) and sent directly as a
2442
+ // Bearer to api.githubcopilot.com — no secondary exchange, no refresh, so
2443
+ // there is no refresh-token env var.
2444
+ methods: ["oauth"],
2445
+ envVars: ["ZERO_COPILOT_GITHUB_TOKEN"],
2446
+ hint: "sign in with your GitHub Copilot account (device sign-in), or set ZERO_COPILOT_GITHUB_TOKEN=... (endpoint override: COPILOT_BASE_URL)"
2447
+ },
2448
+ {
2449
+ id: "google",
2450
+ label: "Google Gemini (Code Assist)",
2451
+ // OAuth only (PKCE browser sign-in — the Gemini CLI flow). The minted
2452
+ // access token is written to ZERO_GEMINI_ACCESS_TOKEN (envVars[0]) and the
2453
+ // refresh token to ZERO_GEMINI_OAUTH_REFRESH_TOKEN (the /REFRESH/i var); the
2454
+ // runtime refreshes on demand against oauth2.googleapis.com. There is no
2455
+ // pasted-key equivalent — Code Assist authenticates only via OAuth.
2456
+ methods: ["oauth"],
2457
+ envVars: ["ZERO_GEMINI_ACCESS_TOKEN", "ZERO_GEMINI_OAUTH_REFRESH_TOKEN"],
2458
+ hint: "sign in with your Google account (browser sign-in), or set ZERO_GEMINI_OAUTH_REFRESH_TOKEN=... for Gemini Code Assist (project override: GOOGLE_CLOUD_PROJECT / ZERO_GEMINI_PROJECT)"
2459
+ },
2460
+ {
2461
+ id: "anthropic",
2462
+ label: "Anthropic",
2463
+ // API key only for now. The schema supports OAuth, but Anthropic/Claude
2464
+ // subscription OAuth is deliberately deferred — do not add "oauth" here
2465
+ // until that workstream lands.
2466
+ methods: ["api-key"],
2467
+ envVars: ["ANTHROPIC_API_KEY"],
2468
+ hint: "set ANTHROPIC_API_KEY=sk-ant-... from console.anthropic.com"
2469
+ }
2470
+ ];
2471
+ function primaryMethod(methods) {
2472
+ return methods[0] ?? "api-key";
2473
+ }
2474
+ var PROVIDERS = PROVIDER_DEFS.map((def) => ({
2475
+ ...def,
2476
+ auth: primaryMethod(def.methods)
2477
+ }));
2478
+ function providerSupportsMethod(providerId, method) {
2479
+ const info = PROVIDERS.find((candidate) => candidate.id === providerId);
2480
+ return info !== void 0 && info.methods.includes(method);
2481
+ }
2482
+ function hasCredential(value) {
2483
+ return typeof value === "string" && value.trim().length > 0;
2484
+ }
2485
+ function satisfyingVar(info, env) {
2486
+ return info.envVars.find((name) => hasCredential(env[name]));
2487
+ }
2488
+ function providerStates(env) {
2489
+ return PROVIDERS.map((info) => {
2490
+ const via = satisfyingVar(info, env);
2491
+ return via === void 0 ? { ...info, configured: false } : { ...info, configured: true, via };
2492
+ });
2493
+ }
2494
+ function isProviderConfigured(providerId, env) {
2495
+ const info = PROVIDERS.find((candidate) => candidate.id === providerId);
2496
+ return info !== void 0 && satisfyingVar(info, env) !== void 0;
2497
+ }
2498
+
2499
+ // packages/cli/src/tui/credential-store.ts
2500
+ import { chmodSync, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
2501
+ import { dirname as dirname3, join as join5 } from "node:path";
2502
+ var CREDENTIALS_FILENAME = "credentials.json";
2503
+ var FILE_MODE = 384;
2504
+ var DIR_MODE = 448;
2505
+ function credentialsFilePath(homeDir2) {
2506
+ return join5(homeStateDir(homeDir2), CREDENTIALS_FILENAME);
2507
+ }
2508
+ var STORABLE_PROVIDER_IDS = new Set(
2509
+ PROVIDERS.filter((provider) => provider.methods.includes("api-key")).map((provider) => provider.id)
2510
+ );
2511
+ function hasCredential2(value) {
2512
+ return typeof value === "string" && value.trim().length > 0;
2513
+ }
2514
+ function normalizeCredentials(raw) {
2515
+ const out = {};
2516
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return out;
2517
+ for (const [providerId, value] of Object.entries(raw)) {
2518
+ if (!STORABLE_PROVIDER_IDS.has(providerId)) continue;
2519
+ if (typeof value !== "string") continue;
2520
+ const secret = value.trim();
2521
+ if (secret.length === 0) continue;
2522
+ out[providerId] = secret;
2523
+ }
2524
+ return out;
2525
+ }
2526
+ function loadCredentials(homeDir2) {
2527
+ return projectApiKeys(loadAccountStore(homeDir2));
2528
+ }
2529
+ function saveCredentials(creds, homeDir2) {
2530
+ const store = loadAccountStore(homeDir2);
2531
+ const desired = normalizeCredentials(creds);
2532
+ return saveAccountStore(replaceApiKeyLayer(store, desired), homeDir2);
2533
+ }
2534
+ function projectApiKeys(store) {
2535
+ const out = {};
2536
+ for (const providerId of Object.keys(store.providers)) {
2537
+ const record = getActiveAccount(store, providerId);
2538
+ if (record?.kind === "api_key") out[providerId] = record.secret;
2539
+ }
2540
+ return out;
2541
+ }
2542
+ function replaceApiKeyLayer(store, desired) {
2543
+ const providers = {};
2544
+ for (const [providerId, entry] of Object.entries(store.providers)) {
2545
+ const kept = {};
2546
+ for (const [accountId, record] of Object.entries(entry.accounts)) {
2547
+ if (record.kind !== "api_key") kept[accountId] = record;
2548
+ }
2549
+ const keptIds = Object.keys(kept);
2550
+ if (keptIds.length === 0) continue;
2551
+ const active = entry.activeAccountId !== void 0 && kept[entry.activeAccountId] !== void 0 ? entry.activeAccountId : keptIds[0];
2552
+ providers[providerId] = { activeAccountId: active, accounts: kept };
2553
+ }
2554
+ for (const [providerId, secret] of Object.entries(desired)) {
2555
+ const existing = providers[providerId];
2556
+ const accounts = existing ? { ...existing.accounts } : {};
2557
+ accounts[DEFAULT_ACCOUNT_ID] = { kind: "api_key", secret };
2558
+ providers[providerId] = { activeAccountId: DEFAULT_ACCOUNT_ID, accounts };
2559
+ }
2560
+ return { version: STORE_VERSION, providers };
2561
+ }
2562
+ function credentialEnvPatch(creds, env) {
2563
+ const stored = normalizeCredentials(creds);
2564
+ const patch = {};
2565
+ for (const info of PROVIDERS) {
2566
+ const secret = stored[info.id];
2567
+ if (secret === void 0) continue;
2568
+ if (info.envVars.some((name) => hasCredential2(env[name]))) continue;
2569
+ const target = info.envVars[0];
2570
+ if (target === void 0) continue;
2571
+ patch[target] = secret;
2572
+ }
2573
+ return patch;
2574
+ }
2575
+ var MASK = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
2576
+ var TAIL = 4;
2577
+ var PREFIX = 6;
2578
+ var MIN_LENGTH_FOR_TAIL = 12;
2579
+ var MIN_LENGTH_FOR_PREFIX = 20;
2580
+ function redactSecret(secret) {
2581
+ const value = typeof secret === "string" ? secret.trim() : "";
2582
+ if (value.length < MIN_LENGTH_FOR_TAIL) return MASK;
2583
+ const tail = value.slice(-TAIL);
2584
+ if (value.length < MIN_LENGTH_FOR_PREFIX) return `${MASK}${tail}`;
2585
+ return `${value.slice(0, PREFIX)}\u2026${tail}`;
2586
+ }
2587
+ var STORE_VERSION = 2;
2588
+ var DEFAULT_ACCOUNT_ID = "default";
2589
+ var KNOWN_PROVIDER_IDS = new Set(PROVIDERS.map((provider) => provider.id));
2590
+ function emptyStore() {
2591
+ return { version: STORE_VERSION, providers: {} };
2592
+ }
2593
+ function isPlainObject(value) {
2594
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2595
+ }
2596
+ function isSafeKey(key) {
2597
+ return key.length > 0 && key !== "__proto__" && key !== "constructor" && key !== "prototype";
2598
+ }
2599
+ function trimmedString(value) {
2600
+ if (typeof value !== "string") return void 0;
2601
+ const trimmed = value.trim();
2602
+ return trimmed.length > 0 ? trimmed : void 0;
2603
+ }
2604
+ function normalizeTokens(raw) {
2605
+ if (!isPlainObject(raw)) return void 0;
2606
+ const tokens = {};
2607
+ const accessToken = trimmedString(raw.accessToken);
2608
+ const refreshToken = trimmedString(raw.refreshToken);
2609
+ if (accessToken !== void 0) tokens.accessToken = accessToken;
2610
+ if (refreshToken !== void 0) tokens.refreshToken = refreshToken;
2611
+ if (tokens.accessToken === void 0 && tokens.refreshToken === void 0) return void 0;
2612
+ if (typeof raw.expiresAt === "number" && Number.isFinite(raw.expiresAt)) tokens.expiresAt = raw.expiresAt;
2613
+ const tokenType = trimmedString(raw.tokenType);
2614
+ if (tokenType !== void 0) tokens.tokenType = tokenType;
2615
+ const scope = trimmedString(raw.scope);
2616
+ if (scope !== void 0) tokens.scope = scope;
2617
+ return tokens;
2618
+ }
2619
+ function normalizeIdentity(raw) {
2620
+ if (!isPlainObject(raw)) return void 0;
2621
+ const identity = {};
2622
+ const label = trimmedString(raw.label);
2623
+ const accountId = trimmedString(raw.accountId);
2624
+ const email = trimmedString(raw.email);
2625
+ if (label !== void 0) identity.label = label;
2626
+ if (accountId !== void 0) identity.accountId = accountId;
2627
+ if (email !== void 0) identity.email = email;
2628
+ return Object.keys(identity).length > 0 ? identity : void 0;
2629
+ }
2630
+ function normalizeRecord(providerId, raw) {
2631
+ if (!isPlainObject(raw)) return void 0;
2632
+ if (raw.kind === "api_key") {
2633
+ if (!providerSupportsMethod(providerId, "api-key")) return void 0;
2634
+ const secret = trimmedString(raw.secret);
2635
+ return secret === void 0 ? void 0 : { kind: "api_key", secret };
2636
+ }
2637
+ if (raw.kind === "oauth") {
2638
+ if (!providerSupportsMethod(providerId, "oauth")) return void 0;
2639
+ const tokens = normalizeTokens(raw.tokens);
2640
+ if (tokens === void 0) return void 0;
2641
+ const identity = normalizeIdentity(raw.identity);
2642
+ return identity === void 0 ? { kind: "oauth", tokens } : { kind: "oauth", tokens, identity };
2643
+ }
2644
+ return void 0;
2645
+ }
2646
+ function normalizeProviderAccounts(providerId, raw) {
2647
+ if (!isPlainObject(raw)) return void 0;
2648
+ const accountsRaw = isPlainObject(raw.accounts) ? raw.accounts : {};
2649
+ const accounts = {};
2650
+ for (const [accountId, recordRaw] of Object.entries(accountsRaw)) {
2651
+ if (!isSafeKey(accountId)) continue;
2652
+ const record = normalizeRecord(providerId, recordRaw);
2653
+ if (record !== void 0) accounts[accountId] = record;
2654
+ }
2655
+ const ids = Object.keys(accounts);
2656
+ if (ids.length === 0) return void 0;
2657
+ const active = typeof raw.activeAccountId === "string" && accounts[raw.activeAccountId] !== void 0 ? raw.activeAccountId : ids[0];
2658
+ return { activeAccountId: active, accounts };
2659
+ }
2660
+ function normalizeAccountStore(raw) {
2661
+ if (!isPlainObject(raw)) return emptyStore();
2662
+ const looksV2 = isPlainObject(raw.providers) || raw.version !== void 0;
2663
+ const store = emptyStore();
2664
+ if (looksV2) {
2665
+ const providersRaw = isPlainObject(raw.providers) ? raw.providers : {};
2666
+ for (const [providerId, entry] of Object.entries(providersRaw)) {
2667
+ if (!isSafeKey(providerId) || !KNOWN_PROVIDER_IDS.has(providerId)) continue;
2668
+ const accounts = normalizeProviderAccounts(providerId, entry);
2669
+ if (accounts !== void 0) store.providers[providerId] = accounts;
2670
+ }
2671
+ return store;
2672
+ }
2673
+ for (const [providerId, secret] of Object.entries(normalizeCredentials(raw))) {
2674
+ store.providers[providerId] = {
2675
+ activeAccountId: DEFAULT_ACCOUNT_ID,
2676
+ accounts: { [DEFAULT_ACCOUNT_ID]: { kind: "api_key", secret } }
2677
+ };
2678
+ }
2679
+ return store;
2680
+ }
2681
+ function loadAccountStore(homeDir2) {
2682
+ try {
2683
+ const text = readFileSync4(credentialsFilePath(homeDir2), "utf8");
2684
+ return normalizeAccountStore(JSON.parse(text));
2685
+ } catch {
2686
+ return emptyStore();
2687
+ }
2688
+ }
2689
+ function saveAccountStore(store, homeDir2) {
2690
+ try {
2691
+ const path = credentialsFilePath(homeDir2);
2692
+ const dir = dirname3(path);
2693
+ mkdirSync3(dir, { recursive: true, mode: DIR_MODE });
2694
+ chmodSync(dir, DIR_MODE);
2695
+ writeFileSync3(path, `${JSON.stringify(normalizeAccountStore(store), null, 2)}
2696
+ `, {
2697
+ encoding: "utf8",
2698
+ mode: FILE_MODE
2699
+ });
2700
+ chmodSync(path, FILE_MODE);
2701
+ return true;
2702
+ } catch {
2703
+ return false;
2704
+ }
2705
+ }
2706
+ function getActiveAccount(store, providerId) {
2707
+ const entry = store.providers[providerId];
2708
+ if (entry === void 0) return void 0;
2709
+ const id = entry.activeAccountId;
2710
+ if (id !== void 0 && entry.accounts[id] !== void 0) return entry.accounts[id];
2711
+ const first = Object.keys(entry.accounts)[0];
2712
+ return first === void 0 ? void 0 : entry.accounts[first];
2713
+ }
2714
+ function chooseAccountId(record, requested) {
2715
+ if (requested !== void 0 && isSafeKey(requested)) return requested;
2716
+ if (record.kind === "oauth" && record.identity?.accountId !== void 0 && isSafeKey(record.identity.accountId)) {
2717
+ return record.identity.accountId;
2718
+ }
2719
+ if (record.kind === "api_key") return DEFAULT_ACCOUNT_ID;
2720
+ return `account-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
2721
+ }
2722
+ function addAccount(store, providerId, record, options) {
2723
+ if (!KNOWN_PROVIDER_IDS.has(providerId)) {
2724
+ throw new Error(`unknown provider "${providerId}"`);
2725
+ }
2726
+ const method = record.kind === "api_key" ? "api-key" : "oauth";
2727
+ if (!providerSupportsMethod(providerId, method)) {
2728
+ throw new Error(`provider "${providerId}" does not support ${record.kind} accounts`);
2729
+ }
2730
+ const accountId = chooseAccountId(record, options?.accountId);
2731
+ const existing = store.providers[providerId];
2732
+ const accounts = { ...existing?.accounts ?? {} };
2733
+ accounts[accountId] = record;
2734
+ const makeActive = options?.makeActive ?? true;
2735
+ const activeAccountId = makeActive ? accountId : existing?.activeAccountId !== void 0 && accounts[existing.activeAccountId] !== void 0 ? existing.activeAccountId : accountId;
2736
+ return {
2737
+ store: { version: STORE_VERSION, providers: { ...store.providers, [providerId]: { activeAccountId, accounts } } },
2738
+ accountId
2739
+ };
2740
+ }
2741
+ function accountEnvPatch(store, env) {
2742
+ const normalized = normalizeAccountStore(store);
2743
+ const patch = {};
2744
+ for (const info of PROVIDERS) {
2745
+ const record = getActiveAccount(normalized, info.id);
2746
+ if (record === void 0) continue;
2747
+ if (info.envVars.some((name) => hasCredential2(env[name]))) continue;
2748
+ if (record.kind === "api_key") {
2749
+ const target = info.envVars[0];
2750
+ if (target !== void 0) patch[target] = record.secret;
2751
+ continue;
2752
+ }
2753
+ const accessVar = info.envVars[0];
2754
+ if (accessVar !== void 0 && record.tokens.accessToken !== void 0) {
2755
+ patch[accessVar] = record.tokens.accessToken;
2756
+ }
2757
+ if (record.tokens.refreshToken !== void 0) {
2758
+ const refreshVar = info.envVars.find((name) => /REFRESH/i.test(name));
2759
+ if (refreshVar !== void 0) patch[refreshVar] = record.tokens.refreshToken;
2760
+ }
2761
+ }
2762
+ return patch;
2763
+ }
2764
+
2765
+ // packages/cli/src/tui/feedback.ts
2766
+ import { appendFileSync, mkdirSync as mkdirSync4 } from "node:fs";
2767
+ import { dirname as dirname4, join as join6 } from "node:path";
2768
+ import { homedir as homedir2 } from "node:os";
2769
+ function feedbackFilePath(homeDir2) {
2770
+ return join6(homeDir2 ?? homedir2(), ".0", "feedback.md");
2771
+ }
2772
+ function formatFeedbackEntry(entry) {
2773
+ const context = [
2774
+ entry.version ? `version ${entry.version}` : null,
2775
+ entry.model ? `model ${entry.model}` : null,
2776
+ entry.mode ? `mode ${entry.mode}` : null
2777
+ ].filter((part) => part !== null);
2778
+ const lines = [`## ${entry.timestamp}`];
2779
+ if (context.length > 0) lines.push(`_${context.join(" \xB7 ")}_`);
2780
+ lines.push("", entry.message.trim(), "");
2781
+ return `${lines.join("\n")}
2782
+ `;
2783
+ }
2784
+ function appendFeedback(entry, homeDir2) {
2785
+ const path = feedbackFilePath(homeDir2);
2786
+ try {
2787
+ mkdirSync4(dirname4(path), { recursive: true });
2788
+ appendFileSync(path, formatFeedbackEntry(entry), "utf8");
2789
+ return { ok: true, path };
2790
+ } catch (error) {
2791
+ return {
2792
+ ok: false,
2793
+ path,
2794
+ error: error instanceof Error ? error.message : String(error)
2795
+ };
2796
+ }
2797
+ }
2798
+ function parseFeedbackCommand(raw) {
2799
+ const text = raw.trim();
2800
+ if (!text) return { kind: "usage" };
2801
+ const separator = text.search(/\s/);
2802
+ const verb = separator < 0 ? text : text.slice(0, separator);
2803
+ const rest = separator < 0 ? "" : text.slice(separator).trim();
2804
+ if (verb === "submit") return rest ? { kind: "submit", message: rest } : { kind: "usage" };
2805
+ if (verb === "send") return rest ? { kind: "usage" } : { kind: "send" };
2806
+ if (verb === "cancel") return rest ? { kind: "usage" } : { kind: "cancel" };
2807
+ return { kind: "record", message: text };
2808
+ }
2809
+ var DEFAULT_FEEDBACK_URL = "";
2810
+ var FEEDBACK_URL_ENV = "ZERO_FEEDBACK_URL";
2811
+ var CLOUD_FEEDBACK_PATH = "/api/cli-feedback";
2812
+ function defaultCloudCredentials(env) {
2813
+ try {
2814
+ const credentials = loadCloudCredentials({
2815
+ env,
2816
+ warn: () => {
2817
+ }
2818
+ });
2819
+ return { host: credentials.host, token: credentials.token };
2820
+ } catch {
2821
+ return null;
2822
+ }
2823
+ }
2824
+ function cloudFeedbackUrl(host) {
2825
+ try {
2826
+ const url = new URL(host);
2827
+ if (url.hostname === "cloud.0.ai") url.hostname = "cloud.0.security";
2828
+ url.pathname = CLOUD_FEEDBACK_PATH;
2829
+ url.search = "";
2830
+ url.hash = "";
2831
+ return url.toString();
2832
+ } catch {
2833
+ return null;
2834
+ }
2835
+ }
2836
+ function resolveFeedbackTarget(env = process.env, options = {}) {
2837
+ const configured = env[FEEDBACK_URL_ENV]?.trim();
2838
+ if (configured) return { url: configured };
2839
+ if (DEFAULT_FEEDBACK_URL) return { url: DEFAULT_FEEDBACK_URL };
2840
+ if (options.allowCloud === false) return null;
2841
+ const credentials = (options.cloudCredentials ?? (() => defaultCloudCredentials(env)))();
2842
+ if (!credentials) return null;
2843
+ const url = cloudFeedbackUrl(credentials.host);
2844
+ return url ? { url, authorization: `Bearer ${credentials.token}` } : null;
2845
+ }
2846
+ var FEEDBACK_OPT_OUT_ENV = ["ZERO_OFFLINE", "ZERO_NO_TELEMETRY", "DO_NOT_TRACK"];
2847
+ var FEEDBACK_TIMEOUT_MS = 5e3;
2848
+ var MAX_BODY_BYTES = 64 * 1024;
2849
+ function isOptOutSet(value) {
2850
+ if (value === void 0) return false;
2851
+ const normalized = value.trim().toLowerCase();
2852
+ if (normalized === "") return false;
2853
+ return normalized !== "0" && normalized !== "false" && normalized !== "no";
2854
+ }
2855
+ function submissionBlockedReason(env = process.env, options = {}) {
2856
+ for (const name of FEEDBACK_OPT_OUT_ENV) {
2857
+ if (isOptOutSet(env[name])) return "opt-out";
2858
+ }
2859
+ return targetBlockedReason(resolveFeedbackTarget(env, options));
2860
+ }
2861
+ function targetBlockedReason(target) {
2862
+ if (target === null) return "no-endpoint";
2863
+ let parsed;
2864
+ try {
2865
+ parsed = new URL(target.url);
2866
+ } catch {
2867
+ return "insecure-endpoint";
2868
+ }
2869
+ if (parsed.protocol !== "https:") return "insecure-endpoint";
2870
+ return null;
2871
+ }
2872
+ function describeSkip(reason) {
2873
+ switch (reason) {
2874
+ case "opt-out":
2875
+ return `Submission disabled by ${FEEDBACK_OPT_OUT_ENV.join(" / ")}. Saved locally only.`;
2876
+ case "no-endpoint":
2877
+ return `No feedback endpoint configured (set ${FEEDBACK_URL_ENV}). Saved locally only.`;
2878
+ case "insecure-endpoint":
2879
+ return `Refusing a non-HTTPS ${FEEDBACK_URL_ENV}. Saved locally only.`;
2880
+ }
2881
+ }
2882
+ var SECRET_RULES = [
2883
+ { label: "an OpenAI/Anthropic-style key (sk-\u2026)", pattern: /\bsk-[A-Za-z0-9_-]{16,}/ },
2884
+ { label: "a GitHub token (ghp_/gho_/ghu_/ghs_/ghr_\u2026)", pattern: /\bgh[pousr]_[A-Za-z0-9]{20,}/ },
2885
+ { label: "an AWS access key id (AKIA\u2026/ASIA\u2026)", pattern: /\b(?:AKIA|ASIA|ABIA|ACCA)[0-9A-Z]{16}\b/ },
2886
+ { label: "a Google API key (AIza\u2026)", pattern: /\bAIza[0-9A-Za-z_-]{35}\b/ },
2887
+ { label: "a Slack token (xox\u2026)", pattern: /\bxox[abprs]-[A-Za-z0-9-]{10,}/ },
2888
+ {
2889
+ label: "an Authorization header",
2890
+ pattern: /authorization\s*[:=]\s*(?:bearer|basic|token|digest)\s+\S+/i
2891
+ },
2892
+ { label: "a JWT (eyJ\u2026)", pattern: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]+/ },
2893
+ {
2894
+ label: "a PEM private key block",
2895
+ pattern: /-----BEGIN (?:[A-Z0-9 ]+ )?PRIVATE KEY-----/
2896
+ },
2897
+ {
2898
+ label: "a credential-shaped assignment (password=/api_key=/secret=\u2026)",
2899
+ pattern: /\b(?:pass(?:word|wd)?|api[_-]?key|secret|token|credentials?)\s*[:=]\s*\S{6,}/i
2900
+ },
2901
+ {
2902
+ // Long opaque runs. Requires mixed case *and* a digit so ordinary prose,
2903
+ // file paths, and hyphenated identifiers do not trip it.
2904
+ label: "a long high-entropy string (base64-ish)",
2905
+ pattern: /(?=[A-Za-z0-9+/_-]{40,})(?=[A-Za-z0-9+/_-]*[a-z])(?=[A-Za-z0-9+/_-]*[A-Z])(?=[A-Za-z0-9+/_-]*[0-9])[A-Za-z0-9+/_-]{40,}={0,2}/
2906
+ }
2907
+ ];
2908
+ function scanForSecrets(message) {
2909
+ const warnings = [];
2910
+ for (const rule of SECRET_RULES) {
2911
+ if (rule.pattern.test(message)) warnings.push(`Message appears to contain ${rule.label}.`);
2912
+ }
2913
+ return warnings;
2914
+ }
2915
+ function serializePayload(payload) {
2916
+ const body = {
2917
+ message: payload.message,
2918
+ timestamp: payload.timestamp
2919
+ };
2920
+ if (payload.version !== void 0) body.version = payload.version;
2921
+ if (payload.model !== void 0) body.model = payload.model;
2922
+ if (payload.mode !== void 0) body.mode = payload.mode;
2923
+ return JSON.stringify(body);
2924
+ }
2925
+ function requestHeaders(target, redactAuthorization = false) {
2926
+ const headers = { "content-type": "application/json" };
2927
+ if (target.authorization) {
2928
+ headers.authorization = redactAuthorization ? "Bearer <redacted>" : target.authorization;
2929
+ }
2930
+ return headers;
2931
+ }
2932
+ function buildSubmitPreview(payload, env = process.env, options = {}) {
2933
+ if (submissionBlockedReason(env, options) !== null) return null;
2934
+ const target = resolveFeedbackTarget(env, options);
2935
+ if (target === null) return null;
2936
+ return {
2937
+ url: target.url,
2938
+ body: serializePayload(payload),
2939
+ headers: requestHeaders(target, true),
2940
+ warnings: scanForSecrets(payload.message)
2941
+ };
2942
+ }
2943
+ async function submitFeedback(payload, env = process.env, opts = {}) {
2944
+ const blocked = submissionBlockedReason(env, opts);
2945
+ if (blocked !== null) return { ok: false, skipped: blocked, error: describeSkip(blocked) };
2946
+ const target = resolveFeedbackTarget(env, opts);
2947
+ const targetBlocked = targetBlockedReason(target);
2948
+ if (targetBlocked !== null) return { ok: false, skipped: targetBlocked, error: describeSkip(targetBlocked) };
2949
+ if (target === null) return { ok: false, skipped: "no-endpoint", error: describeSkip("no-endpoint") };
2950
+ const body = serializePayload(payload);
2951
+ if (Buffer.byteLength(body, "utf8") > MAX_BODY_BYTES) {
2952
+ return { ok: false, error: `Message too large to submit (limit ${MAX_BODY_BYTES} bytes).` };
2953
+ }
2954
+ const reviewed = opts.expectedPreview;
2955
+ if (reviewed !== void 0) {
2956
+ const currentHeaders = requestHeaders(target, true);
2957
+ if (target.url !== reviewed.url || body !== reviewed.body || Object.keys(currentHeaders).length !== Object.keys(reviewed.headers).length || !Object.keys(currentHeaders).every((k) => currentHeaders[k] === reviewed.headers[k])) {
2958
+ return {
2959
+ ok: false,
2960
+ error: "Reviewed feedback destination has changed. Preview the updated submission before sending again."
2961
+ };
2962
+ }
2963
+ }
2964
+ const doFetch = opts.fetchImpl ?? fetch;
2965
+ const timeoutMs = opts.timeoutMs ?? FEEDBACK_TIMEOUT_MS;
2966
+ const controller = new AbortController();
2967
+ let timer;
2968
+ const timeout = new Promise((resolve) => {
2969
+ timer = setTimeout(() => {
2970
+ controller.abort();
2971
+ resolve({ ok: false, error: `Feedback submission timed out after ${timeoutMs}ms.` });
2972
+ }, timeoutMs);
2973
+ timer.unref?.();
2974
+ });
2975
+ const attempt = (async () => {
2976
+ try {
2977
+ const response = await doFetch(target.url, {
2978
+ method: "POST",
2979
+ headers: requestHeaders(target),
2980
+ body,
2981
+ signal: controller.signal,
2982
+ redirect: "error"
2983
+ });
2984
+ return response.ok ? { ok: true, status: response.status } : { ok: false, status: response.status, error: `Endpoint returned ${response.status}.` };
2985
+ } catch (error) {
2986
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
2987
+ }
2988
+ })();
2989
+ try {
2990
+ return await Promise.race([attempt, timeout]);
2991
+ } finally {
2992
+ if (timer !== void 0) clearTimeout(timer);
2993
+ }
2994
+ }
2995
+ var DIAGNOSTIC_PLATFORMS = /* @__PURE__ */ new Set(["darwin", "linux", "win32", "aix", "freebsd", "openbsd", "sunos"]);
2996
+ var DIAGNOSTIC_ARCHS = /* @__PURE__ */ new Set(["x64", "arm64", "arm", "ia32", "s390", "mips", "ppc64"]);
2997
+ var DIAGNOSTIC_RUNTIMES = /* @__PURE__ */ new Set(["node", "bun", "deno"]);
2998
+ function classifyFailureText(text) {
2999
+ const t = text.toLowerCase();
3000
+ if (/exited?\s+-?\d+|exit code|non-?zero exit/.test(t)) return "nonzero-exit";
3001
+ if (/enoent|no such file|command not found|not found on path|cannot find (?:module|the )/.test(t)) return "not-found";
3002
+ if (/etimedout|timed out|\btimeout\b|deadline exceeded/.test(t)) return "timeout";
3003
+ if (/econnrefused|econnreset|epipe|network|fetch failed|socket hang up|getaddrinfo|dns|tls|certificate/.test(t)) return "network";
3004
+ if (/eacces|eperm|permission denied|forbidden|unauthor|401|403/.test(t)) return "permission";
3005
+ if (/rate.?limit|too many requests|\b429\b|quota|overloaded/.test(t)) return "rate-limit";
3006
+ if (/unexpected token|json|yaml|parse error|invalid json|malformed/.test(t)) return "parse";
3007
+ if (/out of memory|enomem|heap|maximum call stack/.test(t)) return "resource";
3008
+ if (/stream completed without final response|response stream failed|incomplete/.test(t)) return "stream-incomplete";
3009
+ return null;
3010
+ }
3011
+ function diagnosticError(error) {
3012
+ try {
3013
+ if (error instanceof TypeError) return "TypeError";
3014
+ if (error instanceof ReferenceError) return "ReferenceError";
3015
+ if (error instanceof SyntaxError) return "SyntaxError";
3016
+ if (error instanceof RangeError) return "RangeError";
3017
+ if (error instanceof URIError) return "URIError";
3018
+ if (error instanceof EvalError) return "EvalError";
3019
+ if (error instanceof Error) {
3020
+ const mode = classifyFailureText(error.message ?? "");
3021
+ return mode ? `Error:${mode}` : "Error";
3022
+ }
3023
+ if (typeof error === "string") {
3024
+ return classifyFailureText(error) ?? "tool-error";
3025
+ }
3026
+ } catch {
3027
+ }
3028
+ return "unknown";
3029
+ }
3030
+ function diagnosticVersion(raw) {
3031
+ if (typeof raw !== "string" || raw.length > 128) return "unknown";
3032
+ return /^v?(\d{1,6}\.\d{1,6}\.\d{1,6})(?:[-+][a-zA-Z0-9.+-]+)?$/.exec(raw)?.[1] ?? "unknown";
3033
+ }
3034
+ function buildDiagnosticFeedback(info, env = process.env) {
3035
+ const kind = info.kind === "tool" ? "tool" : info.kind === "runtime" ? "runtime" : "unknown";
3036
+ const platform = DIAGNOSTIC_PLATFORMS.has(info.platform) ? info.platform : "unknown";
3037
+ const arch = DIAGNOSTIC_ARCHS.has(info.arch) ? info.arch : "unknown";
3038
+ const runtime = DIAGNOSTIC_RUNTIMES.has(info.runtime) ? info.runtime : "unknown";
3039
+ const runtimeVersion = diagnosticVersion(info.runtimeVersion);
3040
+ const version = diagnosticVersion(info.version);
3041
+ const timestamp = typeof info.timestamp === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(info.timestamp) && Number.isFinite(Date.parse(info.timestamp)) ? info.timestamp : (/* @__PURE__ */ new Date()).toISOString();
3042
+ let message = `Diagnostic: ${kind} error \u2014 ${platform}/${arch} on ${runtime} ${runtimeVersion}
3043
+ Version: ${version}
3044
+ Error: ${diagnosticError(info.error)}`;
3045
+ if (diagnosticDetailAllowed(env)) {
3046
+ const detail = redactDiagnosticDetail(buildDiagnosticDetailText(info));
3047
+ if (detail) message = capUtf8Bytes(`${message}
3048
+
3049
+ ${detail}`, MAX_DIAGNOSTIC_DETAIL_MESSAGE_BYTES);
3050
+ }
3051
+ return { message, timestamp, version };
3052
+ }
3053
+ var MAX_DIAGNOSTIC_DETAIL_MESSAGE_BYTES = 8192;
3054
+ var MAX_DIAGNOSTIC_DETAIL_CHARS = 6e3;
3055
+ function diagnosticDetailAllowed(env) {
3056
+ const level = (env["ZERO_ANALYTICS_LEVEL"] ?? "").trim().toLowerCase();
3057
+ return level === "commands" || level === "full";
3058
+ }
3059
+ function buildDiagnosticDetailText(info) {
3060
+ const parts = [];
3061
+ if (info.toolName) parts.push(`Tool: ${info.toolName}`);
3062
+ const err = info.error;
3063
+ if (err instanceof Error) {
3064
+ parts.push(`${err.name}: ${err.message || "(no message)"}`);
3065
+ if (typeof err.stack === "string" && err.stack.trim()) parts.push(err.stack.trim());
3066
+ } else {
3067
+ const text = diagnosticErrorText(err);
3068
+ if (text) parts.push(text);
3069
+ }
3070
+ const exitOutput = typeof info.exitOutput === "string" ? info.exitOutput.trim() : "";
3071
+ if (exitOutput && !parts.some((p) => p.includes(exitOutput))) parts.push(exitOutput);
3072
+ let detail = parts.join("\n").trim();
3073
+ if (detail.length > MAX_DIAGNOSTIC_DETAIL_CHARS) detail = detail.slice(0, MAX_DIAGNOSTIC_DETAIL_CHARS - 1) + "\u2026";
3074
+ return detail;
3075
+ }
3076
+ var DETAIL_REDACTIONS = [
3077
+ { re: /-----BEGIN (?:[A-Z0-9 ]+ )?PRIVATE KEY-----[\s\S]*?-----END (?:[A-Z0-9 ]+ )?PRIVATE KEY-----/g, to: "\u2039redacted:private-key\u203A" },
3078
+ { re: /\bsk-[A-Za-z0-9_-]{16,}/g, to: "\u2039redacted:key\u203A" },
3079
+ { re: /\bgh[pousr]_[A-Za-z0-9]{20,}/g, to: "\u2039redacted:gh-token\u203A" },
3080
+ { re: /\b(?:AKIA|ASIA|ABIA|ACCA)[0-9A-Z]{16}\b/g, to: "\u2039redacted:aws-key\u203A" },
3081
+ { re: /\bAIza[0-9A-Za-z_-]{35}\b/g, to: "\u2039redacted:gcp-key\u203A" },
3082
+ { re: /\bxox[abprs]-[A-Za-z0-9-]{10,}/g, to: "\u2039redacted:slack-token\u203A" },
3083
+ { re: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]+/g, to: "\u2039redacted:jwt\u203A" },
3084
+ { re: /authorization\s*[:=]\s*(?:bearer|basic|token|digest)\s+\S+/gi, to: "authorization: \u2039redacted\u203A" },
3085
+ { re: /\b((?:pass(?:word|wd)?|api[_-]?key|secret|token|credentials?)\s*[:=]\s*)\S{6,}/gi, to: "$1\u2039redacted\u203A" },
3086
+ // Connection strings: keep the scheme/host shape, drop the inline credentials.
3087
+ // MUST run before the email rule so `user:pass@host` is not mis-read as an
3088
+ // email (which would swallow the host too).
3089
+ { re: /([a-z][a-z0-9+.-]*:\/\/)[^\s/@:]+:[^\s/@]+@/gi, to: "$1\u2039redacted\u203A@" },
3090
+ { re: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g, to: "\u2039redacted:email\u203A" },
3091
+ // Home-dir usernames in file paths — keep the path shape, anonymise the user.
3092
+ { re: /(\/home\/)[^/\s]+/g, to: "$1\u2039user\u203A" },
3093
+ { re: /(\/Users\/)[^/\s]+/g, to: "$1\u2039user\u203A" },
3094
+ { re: /([A-Za-z]:\\Users\\)[^\\\s]+/g, to: "$1\u2039user\u203A" }
3095
+ ];
3096
+ function redactDiagnosticDetail(text) {
3097
+ if (typeof text !== "string" || text.length === 0) return "";
3098
+ try {
3099
+ let out = text;
3100
+ for (const { re, to } of DETAIL_REDACTIONS) out = out.replace(re, to);
3101
+ return out;
3102
+ } catch {
3103
+ return "";
3104
+ }
3105
+ }
3106
+ function capUtf8Bytes(text, maxBytes) {
3107
+ if (Buffer.byteLength(text, "utf8") <= maxBytes) return text;
3108
+ let lo = 0, hi = text.length;
3109
+ while (lo < hi) {
3110
+ const mid = Math.ceil((lo + hi) / 2);
3111
+ if (Buffer.byteLength(text.slice(0, mid), "utf8") <= maxBytes - 1) lo = mid;
3112
+ else hi = mid - 1;
3113
+ }
3114
+ return text.slice(0, lo) + "\u2026";
3115
+ }
3116
+ function diagnosticErrorText(error) {
3117
+ if (typeof error === "string") return error.trim();
3118
+ if (error instanceof Error) return (error.message || error.name || "Error").trim();
3119
+ if (error === null || error === void 0) return "";
3120
+ try {
3121
+ const text = String(error).trim();
3122
+ return text === "[object Object]" ? "" : text;
3123
+ } catch {
3124
+ return "";
3125
+ }
3126
+ }
3127
+
2381
3128
  export {
2382
3129
  KEYBINDINGS,
2383
3130
  keybindingsByCategory,
@@ -2419,6 +3166,7 @@ export {
2419
3166
  readProjectOverrides,
2420
3167
  saveProjectOverrides,
2421
3168
  getSettings,
3169
+ getSettingSources,
2422
3170
  updateSetting,
2423
3171
  resetSettings,
2424
3172
  previewSetting,
@@ -2435,5 +3183,24 @@ export {
2435
3183
  suspendProcessPresentationStreamBridge,
2436
3184
  resumeProcessPresentationStreamBridge,
2437
3185
  processPresentationOutput,
2438
- writePresentationLine
3186
+ writePresentationLine,
3187
+ PROVIDERS,
3188
+ providerStates,
3189
+ isProviderConfigured,
3190
+ loadCredentials,
3191
+ saveCredentials,
3192
+ credentialEnvPatch,
3193
+ redactSecret,
3194
+ loadAccountStore,
3195
+ saveAccountStore,
3196
+ addAccount,
3197
+ accountEnvPatch,
3198
+ appendFeedback,
3199
+ parseFeedbackCommand,
3200
+ FEEDBACK_OPT_OUT_ENV,
3201
+ submissionBlockedReason,
3202
+ describeSkip,
3203
+ buildSubmitPreview,
3204
+ submitFeedback,
3205
+ buildDiagnosticFeedback
2439
3206
  };