@humain/terminal 0.0.2 → 0.0.3-beta.0

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 (680) hide show
  1. package/CHANGELOG.md +241 -24
  2. package/README.md +143 -119
  3. package/dist/cli/args.d.ts +4 -2
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +37 -39
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/cli/auth-check.d.ts +19 -0
  8. package/dist/cli/auth-check.d.ts.map +1 -0
  9. package/dist/cli/auth-check.js +50 -0
  10. package/dist/cli/auth-check.js.map +1 -0
  11. package/dist/cli/auth-command.d.ts +24 -0
  12. package/dist/cli/auth-command.d.ts.map +1 -0
  13. package/dist/cli/auth-command.js +103 -0
  14. package/dist/cli/auth-command.js.map +1 -0
  15. package/dist/cli/config-selector.d.ts.map +1 -1
  16. package/dist/cli/config-selector.js +2 -2
  17. package/dist/cli/config-selector.js.map +1 -1
  18. package/dist/cli/credential-print.d.ts +5 -15
  19. package/dist/cli/credential-print.d.ts.map +1 -1
  20. package/dist/cli/credential-print.js +42 -83
  21. package/dist/cli/credential-print.js.map +1 -1
  22. package/dist/cli/experimental/auth.d.ts +16 -0
  23. package/dist/cli/experimental/auth.d.ts.map +1 -0
  24. package/dist/cli/experimental/auth.js +13 -0
  25. package/dist/cli/experimental/auth.js.map +1 -0
  26. package/dist/cli/experimental/cli.d.ts +6 -0
  27. package/dist/cli/experimental/cli.d.ts.map +1 -0
  28. package/dist/cli/experimental/cli.js +5 -0
  29. package/dist/cli/experimental/cli.js.map +1 -0
  30. package/dist/cli/experimental/command-options.d.ts +17 -0
  31. package/dist/cli/experimental/command-options.d.ts.map +1 -0
  32. package/dist/cli/experimental/command-options.js +35 -0
  33. package/dist/cli/experimental/command-options.js.map +1 -0
  34. package/dist/cli/experimental/command.d.ts +63 -0
  35. package/dist/cli/experimental/command.d.ts.map +1 -0
  36. package/dist/cli/experimental/command.js +130 -0
  37. package/dist/cli/experimental/command.js.map +1 -0
  38. package/dist/cli/experimental/commands/client.d.ts +13 -0
  39. package/dist/cli/experimental/commands/client.d.ts.map +1 -0
  40. package/dist/cli/experimental/commands/client.js +25 -0
  41. package/dist/cli/experimental/commands/client.js.map +1 -0
  42. package/dist/cli/experimental/commands/pi.d.ts +15 -0
  43. package/dist/cli/experimental/commands/pi.d.ts.map +1 -0
  44. package/dist/cli/experimental/commands/pi.js +28 -0
  45. package/dist/cli/experimental/commands/pi.js.map +1 -0
  46. package/dist/cli/experimental/commands/server.d.ts +13 -0
  47. package/dist/cli/experimental/commands/server.d.ts.map +1 -0
  48. package/dist/cli/experimental/commands/server.js +25 -0
  49. package/dist/cli/experimental/commands/server.js.map +1 -0
  50. package/dist/cli/experimental/transport-address.d.ts +10 -0
  51. package/dist/cli/experimental/transport-address.d.ts.map +1 -0
  52. package/dist/cli/experimental/transport-address.js +38 -0
  53. package/dist/cli/experimental/transport-address.js.map +1 -0
  54. package/dist/cli/list-models.d.ts +1 -1
  55. package/dist/cli/list-models.d.ts.map +1 -1
  56. package/dist/cli/list-models.js +2 -2
  57. package/dist/cli/list-models.js.map +1 -1
  58. package/dist/cli/startup-ui.d.ts +1 -1
  59. package/dist/cli/startup-ui.d.ts.map +1 -1
  60. package/dist/cli/startup-ui.js +2 -2
  61. package/dist/cli/startup-ui.js.map +1 -1
  62. package/dist/client/index.d.ts +3 -0
  63. package/dist/client/index.d.ts.map +1 -0
  64. package/dist/client/index.js +3 -0
  65. package/dist/client/index.js.map +1 -0
  66. package/dist/client/remote-session.d.ts +53 -0
  67. package/dist/client/remote-session.d.ts.map +1 -0
  68. package/dist/client/remote-session.js +340 -0
  69. package/dist/client/remote-session.js.map +1 -0
  70. package/dist/client/transcript.d.ts +12 -0
  71. package/dist/client/transcript.d.ts.map +1 -0
  72. package/dist/client/transcript.js +98 -0
  73. package/dist/client/transcript.js.map +1 -0
  74. package/dist/core/agent-session-services.d.ts +1 -0
  75. package/dist/core/agent-session-services.d.ts.map +1 -1
  76. package/dist/core/agent-session-services.js +1 -0
  77. package/dist/core/agent-session-services.js.map +1 -1
  78. package/dist/core/agent-session.d.ts +1 -10
  79. package/dist/core/agent-session.d.ts.map +1 -1
  80. package/dist/core/agent-session.js +72 -70
  81. package/dist/core/agent-session.js.map +1 -1
  82. package/dist/core/auth-storage.d.ts +25 -9
  83. package/dist/core/auth-storage.d.ts.map +1 -1
  84. package/dist/core/auth-storage.js +237 -39
  85. package/dist/core/auth-storage.js.map +1 -1
  86. package/dist/core/extensions/index.d.ts +1 -1
  87. package/dist/core/extensions/index.d.ts.map +1 -1
  88. package/dist/core/extensions/index.js.map +1 -1
  89. package/dist/core/extensions/loader.d.ts.map +1 -1
  90. package/dist/core/extensions/loader.js +60 -41
  91. package/dist/core/extensions/loader.js.map +1 -1
  92. package/dist/core/extensions/runner.d.ts +2 -1
  93. package/dist/core/extensions/runner.d.ts.map +1 -1
  94. package/dist/core/extensions/runner.js +3 -0
  95. package/dist/core/extensions/runner.js.map +1 -1
  96. package/dist/core/extensions/types.d.ts +27 -4
  97. package/dist/core/extensions/types.d.ts.map +1 -1
  98. package/dist/core/extensions/types.js.map +1 -1
  99. package/dist/core/http-dispatcher.d.ts.map +1 -1
  100. package/dist/core/http-dispatcher.js +5 -0
  101. package/dist/core/http-dispatcher.js.map +1 -1
  102. package/dist/core/keybindings.d.ts +45 -5
  103. package/dist/core/keybindings.d.ts.map +1 -1
  104. package/dist/core/keybindings.js.map +1 -1
  105. package/dist/core/model-config.d.ts +29 -5
  106. package/dist/core/model-config.d.ts.map +1 -1
  107. package/dist/core/model-config.js +4 -0
  108. package/dist/core/model-config.js.map +1 -1
  109. package/dist/core/model-registry.d.ts +5 -3
  110. package/dist/core/model-registry.d.ts.map +1 -1
  111. package/dist/core/model-registry.js +13 -10
  112. package/dist/core/model-registry.js.map +1 -1
  113. package/dist/core/model-resolver.d.ts +4 -3
  114. package/dist/core/model-resolver.d.ts.map +1 -1
  115. package/dist/core/model-resolver.js +42 -10
  116. package/dist/core/model-resolver.js.map +1 -1
  117. package/dist/core/model-runtime.d.ts +30 -13
  118. package/dist/core/model-runtime.d.ts.map +1 -1
  119. package/dist/core/model-runtime.js +233 -73
  120. package/dist/core/model-runtime.js.map +1 -1
  121. package/dist/core/models-store.d.ts +12 -7
  122. package/dist/core/models-store.d.ts.map +1 -1
  123. package/dist/core/models-store.js +83 -14
  124. package/dist/core/models-store.js.map +1 -1
  125. package/dist/core/package-manager.d.ts +6 -3
  126. package/dist/core/package-manager.d.ts.map +1 -1
  127. package/dist/core/package-manager.js +80 -61
  128. package/dist/core/package-manager.js.map +1 -1
  129. package/dist/core/pi-manifest.d.ts +8 -0
  130. package/dist/core/pi-manifest.d.ts.map +1 -0
  131. package/dist/core/pi-manifest.js +28 -0
  132. package/dist/core/pi-manifest.js.map +1 -0
  133. package/dist/core/provider-composer.d.ts +4 -1
  134. package/dist/core/provider-composer.d.ts.map +1 -1
  135. package/dist/core/provider-composer.js +36 -15
  136. package/dist/core/provider-composer.js.map +1 -1
  137. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  138. package/dist/core/remote-catalog-provider.js +73 -67
  139. package/dist/core/remote-catalog-provider.js.map +1 -1
  140. package/dist/core/resource-loader.d.ts.map +1 -1
  141. package/dist/core/resource-loader.js +2 -2
  142. package/dist/core/resource-loader.js.map +1 -1
  143. package/dist/core/runtime-credentials.d.ts +5 -5
  144. package/dist/core/runtime-credentials.d.ts.map +1 -1
  145. package/dist/core/runtime-credentials.js +11 -8
  146. package/dist/core/runtime-credentials.js.map +1 -1
  147. package/dist/core/sdk.d.ts +1 -1
  148. package/dist/core/sdk.d.ts.map +1 -1
  149. package/dist/core/sdk.js +2 -2
  150. package/dist/core/sdk.js.map +1 -1
  151. package/dist/core/session-manager.d.ts.map +1 -1
  152. package/dist/core/session-manager.js +3 -1
  153. package/dist/core/session-manager.js.map +1 -1
  154. package/dist/core/settings-manager.d.ts +12 -0
  155. package/dist/core/settings-manager.d.ts.map +1 -1
  156. package/dist/core/settings-manager.js +40 -16
  157. package/dist/core/settings-manager.js.map +1 -1
  158. package/dist/core/system-prompt.d.ts.map +1 -1
  159. package/dist/core/system-prompt.js +8 -0
  160. package/dist/core/system-prompt.js.map +1 -1
  161. package/dist/core/tools/bash.d.ts +5 -1
  162. package/dist/core/tools/bash.d.ts.map +1 -1
  163. package/dist/core/tools/bash.js +16 -15
  164. package/dist/core/tools/bash.js.map +1 -1
  165. package/dist/core/tools/edit.d.ts +4 -0
  166. package/dist/core/tools/edit.d.ts.map +1 -1
  167. package/dist/core/tools/edit.js +11 -7
  168. package/dist/core/tools/edit.js.map +1 -1
  169. package/dist/core/tools/find.d.ts +7 -0
  170. package/dist/core/tools/find.d.ts.map +1 -1
  171. package/dist/core/tools/find.js +16 -19
  172. package/dist/core/tools/find.js.map +1 -1
  173. package/dist/core/tools/grep.d.ts +4 -0
  174. package/dist/core/tools/grep.d.ts.map +1 -1
  175. package/dist/core/tools/grep.js +5 -1
  176. package/dist/core/tools/grep.js.map +1 -1
  177. package/dist/core/tools/ls.d.ts +4 -0
  178. package/dist/core/tools/ls.d.ts.map +1 -1
  179. package/dist/core/tools/ls.js +5 -1
  180. package/dist/core/tools/ls.js.map +1 -1
  181. package/dist/core/tools/read.d.ts +4 -0
  182. package/dist/core/tools/read.d.ts.map +1 -1
  183. package/dist/core/tools/read.js +7 -3
  184. package/dist/core/tools/read.js.map +1 -1
  185. package/dist/core/tools/write.d.ts +4 -0
  186. package/dist/core/tools/write.d.ts.map +1 -1
  187. package/dist/core/tools/write.js +6 -2
  188. package/dist/core/tools/write.js.map +1 -1
  189. package/dist/extensions/llama/index.d.ts.map +1 -1
  190. package/dist/extensions/llama/index.js +11 -2
  191. package/dist/extensions/llama/index.js.map +1 -1
  192. package/dist/extensions/llama/provider.d.ts.map +1 -1
  193. package/dist/extensions/llama/provider.js +21 -7
  194. package/dist/extensions/llama/provider.js.map +1 -1
  195. package/dist/humain/agent-execution-protocol.d.ts +218 -0
  196. package/dist/humain/agent-execution-protocol.d.ts.map +1 -0
  197. package/dist/humain/agent-execution-protocol.js +204 -0
  198. package/dist/humain/agent-execution-protocol.js.map +1 -0
  199. package/dist/humain/agent-execution-runtime.d.ts +35 -0
  200. package/dist/humain/agent-execution-runtime.d.ts.map +1 -0
  201. package/dist/humain/agent-execution-runtime.js +170 -0
  202. package/dist/humain/agent-execution-runtime.js.map +1 -0
  203. package/dist/humain/index.d.ts.map +1 -1
  204. package/dist/humain/index.js +8 -7
  205. package/dist/humain/index.js.map +1 -1
  206. package/dist/humain/mcp-adapter/vendor/CHANGELOG.md +660 -0
  207. package/dist/humain/mcp-adapter/vendor/LICENSE +21 -0
  208. package/dist/humain/mcp-adapter/vendor/README.md +733 -0
  209. package/dist/humain/mcp-adapter/vendor/abort.ts +34 -0
  210. package/dist/humain/mcp-adapter/vendor/agent-dir.ts +60 -0
  211. package/dist/humain/mcp-adapter/vendor/agent-plugin-loader.ts +377 -0
  212. package/dist/humain/mcp-adapter/vendor/app-bridge.bundle.js +67 -0
  213. package/dist/humain/mcp-adapter/vendor/banner.png +0 -0
  214. package/dist/humain/mcp-adapter/vendor/cli.js +209 -0
  215. package/dist/humain/mcp-adapter/vendor/commands.ts +627 -0
  216. package/dist/humain/mcp-adapter/vendor/config.ts +1226 -0
  217. package/dist/humain/mcp-adapter/vendor/consent-manager.ts +64 -0
  218. package/dist/humain/mcp-adapter/vendor/direct-tools.ts +604 -0
  219. package/dist/humain/mcp-adapter/vendor/elicitation-handler.ts +348 -0
  220. package/dist/humain/mcp-adapter/vendor/error-signal.ts +21 -0
  221. package/dist/humain/mcp-adapter/vendor/errors.ts +223 -0
  222. package/dist/humain/mcp-adapter/vendor/glimpse-ui.ts +80 -0
  223. package/dist/humain/mcp-adapter/vendor/host-html-template.ts +461 -0
  224. package/dist/humain/mcp-adapter/vendor/index.bundle.d.mts +4 -0
  225. package/dist/humain/mcp-adapter/vendor/index.bundle.mjs +15923 -0
  226. package/dist/humain/mcp-adapter/vendor/index.ts +898 -0
  227. package/dist/humain/mcp-adapter/vendor/init.ts +649 -0
  228. package/dist/humain/mcp-adapter/vendor/json-schema-validator.ts +69 -0
  229. package/dist/humain/mcp-adapter/vendor/lifecycle.ts +147 -0
  230. package/dist/humain/mcp-adapter/vendor/logger.ts +169 -0
  231. package/dist/humain/mcp-adapter/vendor/mcp-auth-flow.ts +993 -0
  232. package/dist/humain/mcp-adapter/vendor/mcp-auth.ts +1000 -0
  233. package/dist/humain/mcp-adapter/vendor/mcp-callback-server.ts +510 -0
  234. package/dist/humain/mcp-adapter/vendor/mcp-code.ts +354 -0
  235. package/dist/humain/mcp-adapter/vendor/mcp-keyring-helper.cjs +89 -0
  236. package/dist/humain/mcp-adapter/vendor/mcp-oauth-provider.ts +616 -0
  237. package/dist/humain/mcp-adapter/vendor/mcp-output-guard.ts +408 -0
  238. package/dist/humain/mcp-adapter/vendor/mcp-panel.ts +1015 -0
  239. package/dist/humain/mcp-adapter/vendor/mcp-probe.ts +187 -0
  240. package/dist/humain/mcp-adapter/vendor/mcp-script-worker.mjs +130 -0
  241. package/dist/humain/mcp-adapter/vendor/mcp-setup-panel.ts +666 -0
  242. package/dist/humain/mcp-adapter/vendor/mcp-status.ts +109 -0
  243. package/dist/humain/mcp-adapter/vendor/mcp-trace.ts +306 -0
  244. package/dist/humain/mcp-adapter/vendor/metadata-cache.ts +353 -0
  245. package/dist/humain/mcp-adapter/vendor/npx-resolver.ts +543 -0
  246. package/dist/humain/mcp-adapter/vendor/oauth-handler.ts +30 -0
  247. package/dist/humain/mcp-adapter/vendor/oauth.ts +48 -0
  248. package/dist/humain/mcp-adapter/vendor/onboarding-state.ts +76 -0
  249. package/dist/humain/mcp-adapter/vendor/package.json +178 -0
  250. package/dist/humain/mcp-adapter/vendor/panel-keys.ts +53 -0
  251. package/dist/humain/mcp-adapter/vendor/prompts.ts +353 -0
  252. package/dist/humain/mcp-adapter/vendor/proxy-modes.ts +1331 -0
  253. package/dist/humain/mcp-adapter/vendor/resource-tools.ts +17 -0
  254. package/dist/humain/mcp-adapter/vendor/runtime-owner.ts +91 -0
  255. package/dist/humain/mcp-adapter/vendor/sampling-handler.ts +284 -0
  256. package/dist/humain/mcp-adapter/vendor/search-ranking.ts +206 -0
  257. package/dist/humain/mcp-adapter/vendor/server-manager.ts +1124 -0
  258. package/dist/humain/mcp-adapter/vendor/session-recovery.ts +151 -0
  259. package/dist/humain/mcp-adapter/vendor/skills/mcp-scripting/SKILL.md +38 -0
  260. package/dist/humain/mcp-adapter/vendor/state.ts +62 -0
  261. package/dist/humain/mcp-adapter/vendor/tool-approval.ts +182 -0
  262. package/dist/humain/mcp-adapter/vendor/tool-metadata.ts +276 -0
  263. package/dist/humain/mcp-adapter/vendor/tool-registrar.ts +253 -0
  264. package/dist/humain/mcp-adapter/vendor/tool-result-renderer.ts +423 -0
  265. package/dist/humain/mcp-adapter/vendor/ts-shape.ts +149 -0
  266. package/dist/humain/mcp-adapter/vendor/types.ts +859 -0
  267. package/dist/humain/mcp-adapter/vendor/ui-app-bridge-helpers.ts +40 -0
  268. package/dist/humain/mcp-adapter/vendor/ui-resource-handler.ts +250 -0
  269. package/dist/humain/mcp-adapter/vendor/ui-server.ts +866 -0
  270. package/dist/humain/mcp-adapter/vendor/ui-session.ts +570 -0
  271. package/dist/humain/mcp-adapter/vendor/ui-stream-types.ts +89 -0
  272. package/dist/humain/mcp-adapter/vendor/ui-tool-visibility.ts +25 -0
  273. package/dist/humain/mcp-adapter/vendor/unix-socket-transport.ts +85 -0
  274. package/dist/humain/mcp-adapter/vendor/utils.ts +310 -0
  275. package/dist/humain/mcp-adapter.d.ts +3 -0
  276. package/dist/humain/mcp-adapter.d.ts.map +1 -0
  277. package/dist/humain/mcp-adapter.js +5 -0
  278. package/dist/humain/mcp-adapter.js.map +1 -0
  279. package/dist/humain/pi-agent-execution-host.d.ts +34 -0
  280. package/dist/humain/pi-agent-execution-host.d.ts.map +1 -0
  281. package/dist/humain/pi-agent-execution-host.js +77 -0
  282. package/dist/humain/pi-agent-execution-host.js.map +1 -0
  283. package/dist/humain/web-access/host.d.ts +4 -0
  284. package/dist/humain/web-access/host.d.ts.map +1 -0
  285. package/dist/humain/web-access/host.js +3 -0
  286. package/dist/humain/web-access/host.js.map +1 -0
  287. package/dist/humain/web-access/index.d.ts +3 -0
  288. package/dist/humain/web-access/index.d.ts.map +1 -0
  289. package/dist/humain/web-access/index.js +5 -0
  290. package/dist/humain/web-access/index.js.map +1 -0
  291. package/dist/humain/web-access/vendor/activity.d.ts +34 -0
  292. package/dist/humain/web-access/vendor/activity.d.ts.map +1 -0
  293. package/dist/humain/web-access/vendor/activity.js +68 -0
  294. package/dist/humain/web-access/vendor/activity.js.map +1 -0
  295. package/dist/humain/web-access/vendor/anysearch.d.ts +8 -0
  296. package/dist/humain/web-access/vendor/anysearch.d.ts.map +1 -0
  297. package/dist/humain/web-access/vendor/anysearch.js +167 -0
  298. package/dist/humain/web-access/vendor/anysearch.js.map +1 -0
  299. package/dist/humain/web-access/vendor/bocha.d.ts +4 -0
  300. package/dist/humain/web-access/vendor/bocha.d.ts.map +1 -0
  301. package/dist/humain/web-access/vendor/bocha.js +219 -0
  302. package/dist/humain/web-access/vendor/bocha.js.map +1 -0
  303. package/dist/humain/web-access/vendor/brave.d.ts +4 -0
  304. package/dist/humain/web-access/vendor/brave.d.ts.map +1 -0
  305. package/dist/humain/web-access/vendor/brave.js +194 -0
  306. package/dist/humain/web-access/vendor/brave.js.map +1 -0
  307. package/dist/humain/web-access/vendor/brightdata-unlocker.d.ts +12 -0
  308. package/dist/humain/web-access/vendor/brightdata-unlocker.d.ts.map +1 -0
  309. package/dist/humain/web-access/vendor/brightdata-unlocker.js +230 -0
  310. package/dist/humain/web-access/vendor/brightdata-unlocker.js.map +1 -0
  311. package/dist/humain/web-access/vendor/brightdata.d.ts +8 -0
  312. package/dist/humain/web-access/vendor/brightdata.d.ts.map +1 -0
  313. package/dist/humain/web-access/vendor/brightdata.js +519 -0
  314. package/dist/humain/web-access/vendor/brightdata.js.map +1 -0
  315. package/dist/humain/web-access/vendor/chrome-cookies.d.ts +10 -0
  316. package/dist/humain/web-access/vendor/chrome-cookies.d.ts.map +1 -0
  317. package/dist/humain/web-access/vendor/chrome-cookies.js +438 -0
  318. package/dist/humain/web-access/vendor/chrome-cookies.js.map +1 -0
  319. package/dist/humain/web-access/vendor/content-find.d.ts +11 -0
  320. package/dist/humain/web-access/vendor/content-find.d.ts.map +1 -0
  321. package/dist/humain/web-access/vendor/content-find.js +117 -0
  322. package/dist/humain/web-access/vendor/content-find.js.map +1 -0
  323. package/dist/humain/web-access/vendor/credential-source.d.ts +28 -0
  324. package/dist/humain/web-access/vendor/credential-source.d.ts.map +1 -0
  325. package/dist/humain/web-access/vendor/credential-source.js +151 -0
  326. package/dist/humain/web-access/vendor/credential-source.js.map +1 -0
  327. package/dist/humain/web-access/vendor/curator-page.d.ts +29 -0
  328. package/dist/humain/web-access/vendor/curator-page.d.ts.map +1 -0
  329. package/dist/humain/web-access/vendor/curator-page.js +3554 -0
  330. package/dist/humain/web-access/vendor/curator-page.js.map +1 -0
  331. package/dist/humain/web-access/vendor/curator-server.d.ts +93 -0
  332. package/dist/humain/web-access/vendor/curator-server.d.ts.map +1 -0
  333. package/dist/humain/web-access/vendor/curator-server.js +688 -0
  334. package/dist/humain/web-access/vendor/curator-server.js.map +1 -0
  335. package/dist/humain/web-access/vendor/datalab-pdf-extract.d.ts +60 -0
  336. package/dist/humain/web-access/vendor/datalab-pdf-extract.d.ts.map +1 -0
  337. package/dist/humain/web-access/vendor/datalab-pdf-extract.js +416 -0
  338. package/dist/humain/web-access/vendor/datalab-pdf-extract.js.map +1 -0
  339. package/dist/humain/web-access/vendor/declared-web-links.d.ts +16 -0
  340. package/dist/humain/web-access/vendor/declared-web-links.d.ts.map +1 -0
  341. package/dist/humain/web-access/vendor/declared-web-links.js +168 -0
  342. package/dist/humain/web-access/vendor/declared-web-links.js.map +1 -0
  343. package/dist/humain/web-access/vendor/duckduckgo.d.ts +4 -0
  344. package/dist/humain/web-access/vendor/duckduckgo.d.ts.map +1 -0
  345. package/dist/humain/web-access/vendor/duckduckgo.js +123 -0
  346. package/dist/humain/web-access/vendor/duckduckgo.js.map +1 -0
  347. package/dist/humain/web-access/vendor/exa.d.ts +10 -0
  348. package/dist/humain/web-access/vendor/exa.d.ts.map +1 -0
  349. package/dist/humain/web-access/vendor/exa.js +426 -0
  350. package/dist/humain/web-access/vendor/exa.js.map +1 -0
  351. package/dist/humain/web-access/vendor/extract.d.ts +46 -0
  352. package/dist/humain/web-access/vendor/extract.d.ts.map +1 -0
  353. package/dist/humain/web-access/vendor/extract.js +1064 -0
  354. package/dist/humain/web-access/vendor/extract.js.map +1 -0
  355. package/dist/humain/web-access/vendor/feature-config.d.ts +3 -0
  356. package/dist/humain/web-access/vendor/feature-config.d.ts.map +1 -0
  357. package/dist/humain/web-access/vendor/feature-config.js +27 -0
  358. package/dist/humain/web-access/vendor/feature-config.js.map +1 -0
  359. package/dist/humain/web-access/vendor/fetch-params.d.ts +25 -0
  360. package/dist/humain/web-access/vendor/fetch-params.d.ts.map +1 -0
  361. package/dist/humain/web-access/vendor/fetch-params.js +57 -0
  362. package/dist/humain/web-access/vendor/fetch-params.js.map +1 -0
  363. package/dist/humain/web-access/vendor/firecrawl.d.ts +12 -0
  364. package/dist/humain/web-access/vendor/firecrawl.d.ts.map +1 -0
  365. package/dist/humain/web-access/vendor/firecrawl.js +239 -0
  366. package/dist/humain/web-access/vendor/firecrawl.js.map +1 -0
  367. package/dist/humain/web-access/vendor/gemini-api.d.ts +27 -0
  368. package/dist/humain/web-access/vendor/gemini-api.d.ts.map +1 -0
  369. package/dist/humain/web-access/vendor/gemini-api.js +230 -0
  370. package/dist/humain/web-access/vendor/gemini-api.js.map +1 -0
  371. package/dist/humain/web-access/vendor/gemini-pdf-extract.d.ts +9 -0
  372. package/dist/humain/web-access/vendor/gemini-pdf-extract.d.ts.map +1 -0
  373. package/dist/humain/web-access/vendor/gemini-pdf-extract.js +85 -0
  374. package/dist/humain/web-access/vendor/gemini-pdf-extract.js.map +1 -0
  375. package/dist/humain/web-access/vendor/gemini-search.d.ts +40 -0
  376. package/dist/humain/web-access/vendor/gemini-search.d.ts.map +1 -0
  377. package/dist/humain/web-access/vendor/gemini-search.js +814 -0
  378. package/dist/humain/web-access/vendor/gemini-search.js.map +1 -0
  379. package/dist/humain/web-access/vendor/gemini-url-context.d.ts +4 -0
  380. package/dist/humain/web-access/vendor/gemini-url-context.d.ts.map +1 -0
  381. package/dist/humain/web-access/vendor/gemini-url-context.js +101 -0
  382. package/dist/humain/web-access/vendor/gemini-url-context.js.map +1 -0
  383. package/dist/humain/web-access/vendor/gemini-web-config.d.ts +4 -0
  384. package/dist/humain/web-access/vendor/gemini-web-config.d.ts.map +1 -0
  385. package/dist/humain/web-access/vendor/gemini-web-config.js +42 -0
  386. package/dist/humain/web-access/vendor/gemini-web-config.js.map +1 -0
  387. package/dist/humain/web-access/vendor/gemini-web.d.ts +18 -0
  388. package/dist/humain/web-access/vendor/gemini-web.d.ts.map +1 -0
  389. package/dist/humain/web-access/vendor/gemini-web.js +385 -0
  390. package/dist/humain/web-access/vendor/gemini-web.js.map +1 -0
  391. package/dist/humain/web-access/vendor/github-api.d.ts +7 -0
  392. package/dist/humain/web-access/vendor/github-api.d.ts.map +1 -0
  393. package/dist/humain/web-access/vendor/github-api.js +160 -0
  394. package/dist/humain/web-access/vendor/github-api.js.map +1 -0
  395. package/dist/humain/web-access/vendor/github-extract.d.ts +13 -0
  396. package/dist/humain/web-access/vendor/github-extract.d.ts.map +1 -0
  397. package/dist/humain/web-access/vendor/github-extract.js +635 -0
  398. package/dist/humain/web-access/vendor/github-extract.js.map +1 -0
  399. package/dist/humain/web-access/vendor/index.d.ts +4 -0
  400. package/dist/humain/web-access/vendor/index.d.ts.map +1 -0
  401. package/dist/humain/web-access/vendor/index.js +3002 -0
  402. package/dist/humain/web-access/vendor/index.js.map +1 -0
  403. package/dist/humain/web-access/vendor/jina-search.d.ts +8 -0
  404. package/dist/humain/web-access/vendor/jina-search.d.ts.map +1 -0
  405. package/dist/humain/web-access/vendor/jina-search.js +255 -0
  406. package/dist/humain/web-access/vendor/jina-search.js.map +1 -0
  407. package/dist/humain/web-access/vendor/kagi.d.ts +15 -0
  408. package/dist/humain/web-access/vendor/kagi.d.ts.map +1 -0
  409. package/dist/humain/web-access/vendor/kagi.js +262 -0
  410. package/dist/humain/web-access/vendor/kagi.js.map +1 -0
  411. package/dist/humain/web-access/vendor/ollama.d.ts +15 -0
  412. package/dist/humain/web-access/vendor/ollama.d.ts.map +1 -0
  413. package/dist/humain/web-access/vendor/ollama.js +216 -0
  414. package/dist/humain/web-access/vendor/ollama.js.map +1 -0
  415. package/dist/humain/web-access/vendor/openai-search.d.ts +15 -0
  416. package/dist/humain/web-access/vendor/openai-search.d.ts.map +1 -0
  417. package/dist/humain/web-access/vendor/openai-search.js +459 -0
  418. package/dist/humain/web-access/vendor/openai-search.js.map +1 -0
  419. package/dist/humain/web-access/vendor/page-query.d.ts +16 -0
  420. package/dist/humain/web-access/vendor/page-query.d.ts.map +1 -0
  421. package/dist/humain/web-access/vendor/page-query.js +79 -0
  422. package/dist/humain/web-access/vendor/page-query.js.map +1 -0
  423. package/dist/humain/web-access/vendor/parallel.d.ts +12 -0
  424. package/dist/humain/web-access/vendor/parallel.d.ts.map +1 -0
  425. package/dist/humain/web-access/vendor/parallel.js +343 -0
  426. package/dist/humain/web-access/vendor/parallel.js.map +1 -0
  427. package/dist/humain/web-access/vendor/pdf-extract.d.ts +44 -0
  428. package/dist/humain/web-access/vendor/pdf-extract.d.ts.map +1 -0
  429. package/dist/humain/web-access/vendor/pdf-extract.js +298 -0
  430. package/dist/humain/web-access/vendor/pdf-extract.js.map +1 -0
  431. package/dist/humain/web-access/vendor/perplexity.d.ts +20 -0
  432. package/dist/humain/web-access/vendor/perplexity.d.ts.map +1 -0
  433. package/dist/humain/web-access/vendor/perplexity.js +159 -0
  434. package/dist/humain/web-access/vendor/perplexity.js.map +1 -0
  435. package/dist/humain/web-access/vendor/querit.d.ts +10 -0
  436. package/dist/humain/web-access/vendor/querit.d.ts.map +1 -0
  437. package/dist/humain/web-access/vendor/querit.js +335 -0
  438. package/dist/humain/web-access/vendor/querit.js.map +1 -0
  439. package/dist/humain/web-access/vendor/render-search-error.d.ts +57 -0
  440. package/dist/humain/web-access/vendor/render-search-error.d.ts.map +1 -0
  441. package/dist/humain/web-access/vendor/render-search-error.js +130 -0
  442. package/dist/humain/web-access/vendor/render-search-error.js.map +1 -0
  443. package/dist/humain/web-access/vendor/rsc-extract.d.ts +12 -0
  444. package/dist/humain/web-access/vendor/rsc-extract.d.ts.map +1 -0
  445. package/dist/humain/web-access/vendor/rsc-extract.js +329 -0
  446. package/dist/humain/web-access/vendor/rsc-extract.js.map +1 -0
  447. package/dist/humain/web-access/vendor/search1api.d.ts +10 -0
  448. package/dist/humain/web-access/vendor/search1api.d.ts.map +1 -0
  449. package/dist/humain/web-access/vendor/search1api.js +251 -0
  450. package/dist/humain/web-access/vendor/search1api.js.map +1 -0
  451. package/dist/humain/web-access/vendor/searchinfinity.d.ts +8 -0
  452. package/dist/humain/web-access/vendor/searchinfinity.d.ts.map +1 -0
  453. package/dist/humain/web-access/vendor/searchinfinity.js +229 -0
  454. package/dist/humain/web-access/vendor/searchinfinity.js.map +1 -0
  455. package/dist/humain/web-access/vendor/searxng.d.ts +4 -0
  456. package/dist/humain/web-access/vendor/searxng.d.ts.map +1 -0
  457. package/dist/humain/web-access/vendor/searxng.js +238 -0
  458. package/dist/humain/web-access/vendor/searxng.js.map +1 -0
  459. package/dist/humain/web-access/vendor/serpbase.d.ts +4 -0
  460. package/dist/humain/web-access/vendor/serpbase.d.ts.map +1 -0
  461. package/dist/humain/web-access/vendor/serpbase.js +211 -0
  462. package/dist/humain/web-access/vendor/serpbase.js.map +1 -0
  463. package/dist/humain/web-access/vendor/serpdive.d.ts +8 -0
  464. package/dist/humain/web-access/vendor/serpdive.d.ts.map +1 -0
  465. package/dist/humain/web-access/vendor/serpdive.js +267 -0
  466. package/dist/humain/web-access/vendor/serpdive.js.map +1 -0
  467. package/dist/humain/web-access/vendor/source-check.d.ts +99 -0
  468. package/dist/humain/web-access/vendor/source-check.d.ts.map +1 -0
  469. package/dist/humain/web-access/vendor/source-check.js +205 -0
  470. package/dist/humain/web-access/vendor/source-check.js.map +1 -0
  471. package/dist/humain/web-access/vendor/ssrf-protection.d.ts +50 -0
  472. package/dist/humain/web-access/vendor/ssrf-protection.d.ts.map +1 -0
  473. package/dist/humain/web-access/vendor/ssrf-protection.js +466 -0
  474. package/dist/humain/web-access/vendor/ssrf-protection.js.map +1 -0
  475. package/dist/humain/web-access/vendor/storage.d.ts +55 -0
  476. package/dist/humain/web-access/vendor/storage.d.ts.map +1 -0
  477. package/dist/humain/web-access/vendor/storage.js +485 -0
  478. package/dist/humain/web-access/vendor/storage.js.map +1 -0
  479. package/dist/humain/web-access/vendor/summary-model-scope.d.ts +25 -0
  480. package/dist/humain/web-access/vendor/summary-model-scope.d.ts.map +1 -0
  481. package/dist/humain/web-access/vendor/summary-model-scope.js +98 -0
  482. package/dist/humain/web-access/vendor/summary-model-scope.js.map +1 -0
  483. package/dist/humain/web-access/vendor/summary-review.d.ts +24 -0
  484. package/dist/humain/web-access/vendor/summary-review.d.ts.map +1 -0
  485. package/dist/humain/web-access/vendor/summary-review.js +341 -0
  486. package/dist/humain/web-access/vendor/summary-review.js.map +1 -0
  487. package/dist/humain/web-access/vendor/tavily.d.ts +8 -0
  488. package/dist/humain/web-access/vendor/tavily.d.ts.map +1 -0
  489. package/dist/humain/web-access/vendor/tavily.js +194 -0
  490. package/dist/humain/web-access/vendor/tavily.js.map +1 -0
  491. package/dist/humain/web-access/vendor/tinyfish.d.ts +11 -0
  492. package/dist/humain/web-access/vendor/tinyfish.d.ts.map +1 -0
  493. package/dist/humain/web-access/vendor/tinyfish.js +331 -0
  494. package/dist/humain/web-access/vendor/tinyfish.js.map +1 -0
  495. package/dist/humain/web-access/vendor/utils.d.ts +20 -0
  496. package/dist/humain/web-access/vendor/utils.d.ts.map +1 -0
  497. package/dist/humain/web-access/vendor/utils.js +91 -0
  498. package/dist/humain/web-access/vendor/utils.js.map +1 -0
  499. package/dist/humain/web-access/vendor/video-extract.d.ts +16 -0
  500. package/dist/humain/web-access/vendor/video-extract.d.ts.map +1 -0
  501. package/dist/humain/web-access/vendor/video-extract.js +335 -0
  502. package/dist/humain/web-access/vendor/video-extract.js.map +1 -0
  503. package/dist/humain/web-access/vendor/xai-search.d.ts +13 -0
  504. package/dist/humain/web-access/vendor/xai-search.d.ts.map +1 -0
  505. package/dist/humain/web-access/vendor/xai-search.js +331 -0
  506. package/dist/humain/web-access/vendor/xai-search.js.map +1 -0
  507. package/dist/humain/web-access/vendor/youtube-extract.d.ts +27 -0
  508. package/dist/humain/web-access/vendor/youtube-extract.d.ts.map +1 -0
  509. package/dist/humain/web-access/vendor/youtube-extract.js +282 -0
  510. package/dist/humain/web-access/vendor/youtube-extract.js.map +1 -0
  511. package/dist/index.d.ts +7 -4
  512. package/dist/index.d.ts.map +1 -1
  513. package/dist/index.js +5 -2
  514. package/dist/index.js.map +1 -1
  515. package/dist/main.d.ts +2 -2
  516. package/dist/main.d.ts.map +1 -1
  517. package/dist/main.js +169 -102
  518. package/dist/main.js.map +1 -1
  519. package/dist/modes/agent-exec-mode.d.ts +14 -0
  520. package/dist/modes/agent-exec-mode.d.ts.map +1 -0
  521. package/dist/modes/agent-exec-mode.js +92 -0
  522. package/dist/modes/agent-exec-mode.js.map +1 -0
  523. package/dist/modes/index.d.ts +4 -2
  524. package/dist/modes/index.d.ts.map +1 -1
  525. package/dist/modes/index.js +2 -1
  526. package/dist/modes/index.js.map +1 -1
  527. package/dist/modes/interactive/components/assistant-message.d.ts +5 -2
  528. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  529. package/dist/modes/interactive/components/assistant-message.js +13 -4
  530. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  531. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  532. package/dist/modes/interactive/components/custom-editor.js +7 -0
  533. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  534. package/dist/modes/interactive/components/earendil-announcement.d.ts +5 -0
  535. package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -0
  536. package/dist/modes/interactive/components/earendil-announcement.js +40 -0
  537. package/dist/modes/interactive/components/earendil-announcement.js.map +1 -0
  538. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  539. package/dist/modes/interactive/components/footer.js +1 -1
  540. package/dist/modes/interactive/components/footer.js.map +1 -1
  541. package/dist/modes/interactive/components/markdown-transform.d.ts +3 -0
  542. package/dist/modes/interactive/components/markdown-transform.d.ts.map +1 -0
  543. package/dist/modes/interactive/components/markdown-transform.js +19 -0
  544. package/dist/modes/interactive/components/markdown-transform.js.map +1 -0
  545. package/dist/modes/interactive/components/mermaid.d.ts +11 -0
  546. package/dist/modes/interactive/components/mermaid.d.ts.map +1 -0
  547. package/dist/modes/interactive/components/mermaid.js +75 -0
  548. package/dist/modes/interactive/components/mermaid.js.map +1 -0
  549. package/dist/modes/interactive/components/model-selector.d.ts +1 -1
  550. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  551. package/dist/modes/interactive/components/model-selector.js +16 -4
  552. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  553. package/dist/modes/interactive/components/scoped-models-selector.d.ts +4 -0
  554. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  555. package/dist/modes/interactive/components/scoped-models-selector.js +26 -0
  556. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  557. package/dist/modes/interactive/components/settings-selector.d.ts +8 -2
  558. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  559. package/dist/modes/interactive/components/settings-selector.js +30 -0
  560. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  561. package/dist/modes/interactive/components/user-message.d.ts +3 -1
  562. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  563. package/dist/modes/interactive/components/user-message.js +9 -2
  564. package/dist/modes/interactive/components/user-message.js.map +1 -1
  565. package/dist/modes/interactive/interactive-mode.d.ts +45 -9
  566. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  567. package/dist/modes/interactive/interactive-mode.js +581 -211
  568. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  569. package/dist/modes/interactive/theme/dark.json +1 -0
  570. package/dist/modes/interactive/theme/light.json +1 -0
  571. package/dist/modes/interactive/theme/theme-controller.d.ts +3 -0
  572. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  573. package/dist/modes/interactive/theme/theme-controller.js +10 -1
  574. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  575. package/dist/modes/interactive/theme/theme-schema.json +5 -1
  576. package/dist/modes/interactive/theme/theme.d.ts +2 -2
  577. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  578. package/dist/modes/interactive/theme/theme.js +24 -6
  579. package/dist/modes/interactive/theme/theme.js.map +1 -1
  580. package/dist/modes/json-event.d.ts +28 -0
  581. package/dist/modes/json-event.d.ts.map +1 -0
  582. package/dist/modes/json-event.js +12 -0
  583. package/dist/modes/json-event.js.map +1 -0
  584. package/dist/modes/print-mode.d.ts.map +1 -1
  585. package/dist/modes/print-mode.js +12 -2
  586. package/dist/modes/print-mode.js.map +1 -1
  587. package/dist/modes/rpc/rpc-client.d.ts +12 -5
  588. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  589. package/dist/modes/rpc/rpc-client.js +55 -1
  590. package/dist/modes/rpc/rpc-client.js.map +1 -1
  591. package/dist/modes/rpc/rpc-mode.d.ts +1 -1
  592. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  593. package/dist/modes/rpc/rpc-mode.js +13 -3
  594. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  595. package/dist/modes/rpc/rpc-types.d.ts +15 -0
  596. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  597. package/dist/modes/rpc/rpc-types.js.map +1 -1
  598. package/dist/package-manager-cli.d.ts.map +1 -1
  599. package/dist/package-manager-cli.js +11 -9
  600. package/dist/package-manager-cli.js.map +1 -1
  601. package/dist/rpc-entry.d.ts.map +1 -1
  602. package/dist/rpc-entry.js +3 -2
  603. package/dist/rpc-entry.js.map +1 -1
  604. package/dist/server/create-harness.d.ts +26 -0
  605. package/dist/server/create-harness.d.ts.map +1 -0
  606. package/dist/server/create-harness.js +104 -0
  607. package/dist/server/create-harness.js.map +1 -0
  608. package/dist/utils/abort.d.ts +5 -0
  609. package/dist/utils/abort.d.ts.map +1 -0
  610. package/dist/utils/abort.js +48 -0
  611. package/dist/utils/abort.js.map +1 -0
  612. package/dist/utils/clipboard.d.ts +1 -1
  613. package/dist/utils/clipboard.d.ts.map +1 -1
  614. package/dist/utils/clipboard.js +22 -2
  615. package/dist/utils/clipboard.js.map +1 -1
  616. package/dist/utils/management-http.d.ts +23 -0
  617. package/dist/utils/management-http.d.ts.map +1 -0
  618. package/dist/utils/management-http.js +50 -0
  619. package/dist/utils/management-http.js.map +1 -0
  620. package/dist/utils/paths.d.ts +3 -0
  621. package/dist/utils/paths.d.ts.map +1 -1
  622. package/dist/utils/paths.js +23 -1
  623. package/dist/utils/paths.js.map +1 -1
  624. package/dist/utils/tool-result-images.d.ts +19 -0
  625. package/dist/utils/tool-result-images.d.ts.map +1 -0
  626. package/dist/utils/tool-result-images.js +45 -0
  627. package/dist/utils/tool-result-images.js.map +1 -0
  628. package/dist/utils/tools-manager.d.ts.map +1 -1
  629. package/dist/utils/tools-manager.js +13 -25
  630. package/dist/utils/tools-manager.js.map +1 -1
  631. package/dist/utils/version-check.d.ts +4 -0
  632. package/dist/utils/version-check.d.ts.map +1 -1
  633. package/dist/utils/version-check.js +23 -5
  634. package/dist/utils/version-check.js.map +1 -1
  635. package/docs/compaction.md +2 -2
  636. package/docs/custom-provider.md +7 -5
  637. package/docs/extensions.md +30 -5
  638. package/docs/humain-terminal.md +21 -1
  639. package/docs/json.md +24 -19
  640. package/docs/keybindings.md +46 -14
  641. package/docs/models.md +32 -11
  642. package/docs/providers.md +9 -1
  643. package/docs/quickstart.md +39 -37
  644. package/docs/rpc.md +31 -11
  645. package/docs/sdk.md +21 -2
  646. package/docs/security.md +21 -21
  647. package/docs/session-format.md +14 -14
  648. package/docs/settings.md +4 -1
  649. package/docs/themes.md +5 -3
  650. package/docs/usage.md +7 -0
  651. package/examples/extensions/custom-compaction.ts +3 -18
  652. package/examples/extensions/custom-provider-anthropic/index.ts +5 -1
  653. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  654. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  655. package/examples/extensions/custom-provider-gitlab-duo/index.ts +5 -1
  656. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  657. package/examples/extensions/doom-overlay/wad-finder.ts +7 -3
  658. package/examples/extensions/gondolin/package-lock.json +2 -2
  659. package/examples/extensions/gondolin/package.json +1 -1
  660. package/examples/extensions/handoff.ts +4 -13
  661. package/examples/extensions/qna.ts +5 -9
  662. package/examples/extensions/sandbox/package-lock.json +2 -2
  663. package/examples/extensions/sandbox/package.json +1 -1
  664. package/examples/extensions/summarize.ts +9 -20
  665. package/examples/extensions/with-deps/package-lock.json +2 -2
  666. package/examples/extensions/with-deps/package.json +1 -1
  667. package/examples/rpc-extension-ui.ts +11 -2
  668. package/examples/sdk/02-custom-model.ts +2 -3
  669. package/examples/sdk/09-api-keys-and-oauth.ts +1 -1
  670. package/examples/sdk/12-full-control.ts +1 -1
  671. package/examples/sdk/README.md +1 -1
  672. package/npm-shrinkwrap.json +2771 -535
  673. package/package.json +40 -11
  674. package/src/humain/mcp-adapter/UPSTREAM.md +15 -0
  675. package/src/humain/mcp-adapter/upstream.json +7 -0
  676. package/src/humain/mcp-adapter/vendor/CHANGELOG.md +660 -0
  677. package/src/humain/mcp-adapter/vendor/LICENSE +21 -0
  678. package/src/humain/web-access/UPSTREAM.md +17 -0
  679. package/src/humain/web-access/upstream.json +23 -0
  680. package/src/humain/web-access/vendor/LICENSE +21 -0
@@ -0,0 +1,519 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { activityMonitor } from "./activity.js";
3
+ import { hasCredentialSource, redactCredential, resolveCredential } from "./credential-source.js";
4
+ import { getWebSearchConfigPath } from "./utils.js";
5
+ const BRIGHTDATA_API_URL = "https://api.brightdata.com/request";
6
+ const CONFIG_PATH = getWebSearchConfigPath();
7
+ const SEARCH_TIMEOUT_MS = 60_000;
8
+ // Bright Data proxies a real Google SERP through a provisioned "zone" and bills
9
+ // per successful request against it. A SERP zone (Bright Data zone type `serp`)
10
+ // and a Web Unlocker zone (type `unblocker`) are separate products with separate
11
+ // prices, so the zone used for search is its own required setting and is never
12
+ // guessed: the wrong zone is either an opaque 400 or a charge against the wrong
13
+ // line of the invoice. Pricing: https://brightdata.com/pricing
14
+ const ZONE_PATTERN = /^[A-Za-z0-9_-]+$/;
15
+ // Google's own time filter, passed straight through in the proxied URL. Unlike a
16
+ // query-text hint this is an engine-side filter, so results outside the window
17
+ // are not returned at all.
18
+ const RECENCY_TBS = {
19
+ day: "qdr:d",
20
+ week: "qdr:w",
21
+ month: "qdr:m",
22
+ year: "qdr:y",
23
+ };
24
+ let cachedConfig = null;
25
+ // `web-search.json` is a credential store: its own text is the secret. V8 quotes a
26
+ // window of the source it choked on back inside the `JSON.parse` message — with a
27
+ // short file, the whole file — so `{"brightdataApiKey": bd-real-token}` (quotes
28
+ // forgotten around a pasted token) produces
29
+ // `Unexpected token 'b', "{"brightdataApiKey": bd-real-token}" is not valid JSON`.
30
+ // There is no credential to redact against at this point, because the credential is
31
+ // what the file was being read for, so the parser's text is dropped entirely and only
32
+ // its position is kept. That position is also the only part that cannot carry a
33
+ // status-shaped phrase into `providerErrorStatus`.
34
+ //
35
+ // This is a deliberate divergence from `serpdive.ts:59-62`, `brave.ts:33-36`,
36
+ // `anysearch.ts:48-51` and `firecrawl.ts:50-53`, which all quote the parser message
37
+ // verbatim. They have the same leak; fixing it for every provider is a separate
38
+ // change, and this module is not going to copy the bug forward to justify symmetry.
39
+ function configParseDetail(err) {
40
+ const position = errorMessage(err).match(/at position \d+(?: \(line \d+ column \d+\))?/i);
41
+ return position ? `not valid JSON, ${position[0]}` : "not valid JSON";
42
+ }
43
+ function loadConfig() {
44
+ if (cachedConfig)
45
+ return cachedConfig;
46
+ if (!existsSync(CONFIG_PATH)) {
47
+ cachedConfig = {};
48
+ return cachedConfig;
49
+ }
50
+ const raw = readFileSync(CONFIG_PATH, "utf-8");
51
+ let parsed;
52
+ try {
53
+ parsed = JSON.parse(raw);
54
+ }
55
+ catch (err) {
56
+ throw new Error(`Failed to parse ${CONFIG_PATH}: ${configParseDetail(err)}`);
57
+ }
58
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
59
+ throw new Error(`Invalid config in ${CONFIG_PATH}: expected a JSON object`);
60
+ }
61
+ cachedConfig = parsed;
62
+ return cachedConfig;
63
+ }
64
+ async function getApiKey(signal) {
65
+ return resolveCredential({
66
+ provider: "Bright Data",
67
+ configuredValue: loadConfig().brightdataApiKey,
68
+ environmentValue: process.env.BRIGHTDATA_API_KEY,
69
+ signal,
70
+ });
71
+ }
72
+ async function requireApiKey(signal) {
73
+ const apiKey = await getApiKey(signal);
74
+ if (!apiKey) {
75
+ throw new Error("Bright Data API key not found. Either:\n" +
76
+ ` 1. Create ${CONFIG_PATH} with { "brightdataApiKey": "your-key" }\n` +
77
+ " 2. Set BRIGHTDATA_API_KEY environment variable\n" +
78
+ "Get a key at https://brightdata.com/cp/setting/users");
79
+ }
80
+ return apiKey;
81
+ }
82
+ // A zone name is an account-scoped identifier, not a URL. Validating the charset
83
+ // here keeps a stray value out of the request body, where Bright Data reports it
84
+ // as a generic 400 that reads like a credential problem.
85
+ //
86
+ // Total by construction: an invalid zone is `null`, never a throw.
87
+ // `isBrightDataAvailable()` reads this, and availability is evaluated outside its
88
+ // callers' error handling — `gemini-search.ts` checks it before entering the
89
+ // per-provider try/catch, and `index.ts` awaits `getProviderAvailability()` on the
90
+ // `web_search` path with nothing around it. A throw here would therefore take
91
+ // `web_search` down for Brave and OpenAI too, over one mistyped Bright Data
92
+ // setting. The loud, actionable error belongs on the request path, in
93
+ // `requireSerpZone()`, where only this provider is affected.
94
+ function normalizeZone(value) {
95
+ if (typeof value !== "string")
96
+ return null;
97
+ const trimmed = value.trim();
98
+ if (!trimmed)
99
+ return null;
100
+ return ZONE_PATTERN.test(trimmed) ? trimmed : null;
101
+ }
102
+ // Resolved by presence, not by validity: a `BRIGHTDATA_SERP_ZONE` that is set but
103
+ // malformed must not silently hand the request to the config file's zone. The
104
+ // setting the user actually filled in is the setting the error names.
105
+ function serpZoneSetting() {
106
+ const fromEnv = process.env.BRIGHTDATA_SERP_ZONE;
107
+ if (typeof fromEnv === "string" && fromEnv.trim()) {
108
+ return { raw: fromEnv.trim(), label: "BRIGHTDATA_SERP_ZONE" };
109
+ }
110
+ const configured = loadConfig().brightdataSerpZone;
111
+ if (typeof configured === "string" && configured.trim()) {
112
+ return { raw: configured.trim(), label: `brightdataSerpZone in ${CONFIG_PATH}` };
113
+ }
114
+ return null;
115
+ }
116
+ function getSerpZone() {
117
+ const setting = serpZoneSetting();
118
+ return setting ? normalizeZone(setting.raw) : null;
119
+ }
120
+ // There is no safe default, and in particular no fallback to a Web Unlocker zone:
121
+ // `serp` and `unblocker` are different Bright Data zone types, priced differently,
122
+ // and an Unlocker zone does not return SERP JSON — substituting one buys a
123
+ // confusing paid failure instead of a search. A missing or malformed SERP zone
124
+ // fails loudly here, before anything billable happens.
125
+ function requireSerpZone() {
126
+ const setting = serpZoneSetting();
127
+ const zone = setting ? normalizeZone(setting.raw) : null;
128
+ if (zone)
129
+ return zone;
130
+ if (setting) {
131
+ throw new Error(`Bright Data SERP zone is invalid: ${setting.label} must be a zone name of letters, digits, "-", or "_" ` +
132
+ `(got "${untrustedText(setting.raw, 60)}").\n` +
133
+ "The zone must be of Bright Data type `serp`; a Web Unlocker zone (type `unblocker`) is a different product and does not return SERP JSON.\n" +
134
+ "Create or rename one at https://brightdata.com/cp/zones");
135
+ }
136
+ throw new Error("Bright Data SERP zone is invalid or missing. Either:\n" +
137
+ ` 1. Create ${CONFIG_PATH} with { "brightdataSerpZone": "your_serp_zone" }\n` +
138
+ " 2. Set BRIGHTDATA_SERP_ZONE environment variable\n" +
139
+ "The zone must be of Bright Data type `serp`; a Web Unlocker zone is a different product and does not return SERP JSON.\n" +
140
+ "Create one at https://brightdata.com/cp/zones");
141
+ }
142
+ function normalizeCount(value) {
143
+ if (typeof value !== "number" || !Number.isFinite(value))
144
+ return 5;
145
+ return Math.max(1, Math.min(Math.floor(value), 20));
146
+ }
147
+ function normalizeDomain(value) {
148
+ let input = value.trim().toLowerCase();
149
+ if (!input)
150
+ return null;
151
+ if (input.startsWith("-"))
152
+ input = input.slice(1).trim();
153
+ if (!input)
154
+ return null;
155
+ try {
156
+ const parsed = input.includes("://") ? new URL(input) : new URL(`https://${input}`);
157
+ input = parsed.hostname;
158
+ }
159
+ catch {
160
+ input = input.split("/")[0]?.split(":")[0] ?? "";
161
+ }
162
+ input = input.replace(/^\.+|\.+$/g, "");
163
+ return /^[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/i.test(input) ? input : null;
164
+ }
165
+ function parseDomainFilter(domainFilter) {
166
+ const filters = { include: [], exclude: [] };
167
+ if (!domainFilter?.length)
168
+ return filters;
169
+ for (const raw of domainFilter) {
170
+ const domain = normalizeDomain(raw);
171
+ if (!domain)
172
+ continue;
173
+ const target = raw.trim().startsWith("-") ? filters.exclude : filters.include;
174
+ if (!target.includes(domain))
175
+ target.push(domain);
176
+ }
177
+ return filters;
178
+ }
179
+ // Bright Data has no domain parameter, but the engine behind it is Google, so the
180
+ // filter is expressed the way Google expresses it: as `site:` operators inside
181
+ // the query. That asks the engine for the right pages instead of discarding the
182
+ // wrong ones locally — the difference SERPdive documents as a limitation.
183
+ function buildSearchQuery(query, filters) {
184
+ const parts = [query];
185
+ if (filters.include.length === 1) {
186
+ parts.push(`site:${filters.include[0]}`);
187
+ }
188
+ else if (filters.include.length > 1) {
189
+ parts.push(filters.include.map((domain) => `site:${domain}`).join(" OR "));
190
+ }
191
+ for (const domain of filters.exclude) {
192
+ parts.push(`-site:${domain}`);
193
+ }
194
+ return parts.join(" ");
195
+ }
196
+ function buildSerpUrl(searchQuery, numResults, recencyFilter) {
197
+ const params = new URLSearchParams({ q: searchQuery });
198
+ // One SERP request is billed once whatever `num` says, so asking for a little
199
+ // headroom is free and leaves something to keep after local re-filtering.
200
+ params.set("num", String(Math.min(numResults + 5, 20)));
201
+ const tbs = recencyFilter ? RECENCY_TBS[recencyFilter] : undefined;
202
+ if (tbs)
203
+ params.set("tbs", tbs);
204
+ // `brd_json=1` is what makes Bright Data return the SERP as JSON instead of
205
+ // Google's HTML. Without it `data_format: "parsed_light"` has nothing to read.
206
+ params.set("brd_json", "1");
207
+ return `https://www.google.com/search?${params.toString()}`;
208
+ }
209
+ function domainMatches(hostname, domain) {
210
+ return hostname === domain || hostname.endsWith(`.${domain}`);
211
+ }
212
+ // `site:` narrows the SERP but does not guarantee it: Google still mixes in
213
+ // related hosts, and an OR group is honoured loosely. The same filter is applied
214
+ // again here so the contract holds on what is actually returned.
215
+ function passesDomainFilters(url, filters) {
216
+ if (filters.include.length === 0 && filters.exclude.length === 0)
217
+ return true;
218
+ let hostname;
219
+ try {
220
+ hostname = new URL(url).hostname.toLowerCase();
221
+ }
222
+ catch {
223
+ return false;
224
+ }
225
+ if (filters.exclude.some((domain) => domainMatches(hostname, domain)))
226
+ return false;
227
+ if (filters.include.length === 0)
228
+ return true;
229
+ return filters.include.some((domain) => domainMatches(hostname, domain));
230
+ }
231
+ function requestSignal(signal) {
232
+ const timeout = AbortSignal.timeout(SEARCH_TIMEOUT_MS);
233
+ return signal ? AbortSignal.any([signal, timeout]) : timeout;
234
+ }
235
+ function errorMessage(err) {
236
+ return err instanceof Error ? err.message : String(err);
237
+ }
238
+ // Text that did not originate here — a proxied response body, a `JSON.parse`
239
+ // message quoting that body, a config value — is quoted into Error messages, and
240
+ // `gemini-search.ts` classifies routing failures by reading a status back out of
241
+ // the message text (`providerErrorStatus`, /\b(?:error|status|http)\s+(\d{3})\b/).
242
+ // A proxied Google interstitial that merely mentions "Error 503" would otherwise
243
+ // be read as *our* status and retried as a transient failure, when what actually
244
+ // happened is that a 200 was billed and is unusable. Status-shaped phrases in
245
+ // quoted text are rewritten to `upstream <code>`: the number is still reported,
246
+ // but only this module's own wording can name the status we received.
247
+ const STATUS_SHAPED_PATTERN = /\b(?:error|status|http)[\s:=-]{1,4}(\d{3})\b/gi;
248
+ // `providerErrorStatus` is not the only thing that reads our message.
249
+ // `classifyProviderError` also matches bare keyword phrases, and its branch order
250
+ // decides which ones can reach us: `searchRouting.fallbackOn` accepts only
251
+ // `transient`, `quota` and `network`, and of those three only the `quota` branch
252
+ // (/rate limit|quota|too many requests/) runs BEFORE the `invalid-response` branch
253
+ // that this module's own wording always triggers. So a billed 200 whose body reads
254
+ // "You have exceeded your rate limit" would classify as `quota`, and with
255
+ // `fallbackOn: ["quota"]` the user is charged, silently served the next provider's
256
+ // answer, and shown no diagnostic — the failure mode this module exists to prevent.
257
+ // `transient` and `network` sit after `invalid-response` and are unreachable for the
258
+ // same reason; the test suite pins that ordering assumption rather than trusting it.
259
+ //
260
+ // The phrase is replaced rather than removed: the reader still learns the upstream
261
+ // page mentioned a rate limit, but no substring of the replacement matches any
262
+ // classifier branch ("rate-limit" is hyphenated, so /rate limit/ cannot match).
263
+ const QUOTA_SHAPED_PATTERN = /rate limit|quota|too many requests/gi;
264
+ // The complete inventory of places foreign text is quoted into a message or a log
265
+ // line, and what each one is required to apply. Adding a fifth means adding it here:
266
+ //
267
+ // | site | redactCredential | untrustedText |
268
+ // | ----------------------------------------------- | ---------------- | ------------- |
269
+ // | non-2xx response body | yes | yes (300) |
270
+ // | invalid-JSON raw body | yes | yes (300) |
271
+ // | Bright Data's own 200 error envelope | yes | yes (200) |
272
+ // | transport/abort error from `fetch` | yes | n/a — see (a) |
273
+ // | rejected zone value | n/a — see (b) | yes (60) |
274
+ // | `JSON.parse` message on a response body | n/a — see (c) | n/a — see (c) |
275
+ // | `JSON.parse` failure on `web-search.json` | n/a — see (c) | n/a — see (c) |
276
+ //
277
+ // (a) That message is generated locally by undici or by `AbortSignal`, never by the
278
+ // proxied page, and the abort branch below matches on it; collapsing and
279
+ // truncating it would buy nothing and could break that match.
280
+ // (b) The zone is resolved *before* the credential on purpose, so that a config
281
+ // mistake never spawns a `!command` resolver. There is therefore no key to redact
282
+ // against yet, and the value being echoed is the user's own zone setting.
283
+ // (c) Neither parser message is quoted at all, so neither needs a filter or a cap. V8
284
+ // embeds a *prefix* of the offending input in its message, and `redactCredential`
285
+ // substitutes whole values, so a prefix cannot be redacted out of it. Dropping the
286
+ // message removes the leak by construction — see `configParseDetail` and the
287
+ // `JSON.parse` catch on the response path.
288
+ // Truncation happens BEFORE the rewrite, and the order is the whole point.
289
+ // `STATUS_SHAPED_PATTERN` requires `(\d{3})\b`, so `error 5031` is deliberately left
290
+ // alone — it is not a status. Sanitising first and slicing second let the cut land on
291
+ // the 4th digit and hand back a freshly forged `error 503`, which
292
+ // `gemini-search.ts`'s `providerErrorStatus` then reads as *our* status: a billed 200
293
+ // classified `transient` and silently re-run on the next provider. Slicing first means
294
+ // the rewrite always sees the exact text that will be quoted.
295
+ //
296
+ // The rewrite runs last and is therefore allowed to push the result a few characters
297
+ // past `limit` ("error 503" → "upstream 503"). `limit` bounds how much upstream text is
298
+ // quoted, not the final string length; a second `slice()` after the rewrite would
299
+ // re-open exactly the hole this ordering closes.
300
+ function untrustedText(text, limit = 300) {
301
+ return text
302
+ .replace(/\s+/g, " ")
303
+ .trim()
304
+ .slice(0, limit)
305
+ .replace(STATUS_SHAPED_PATTERN, "upstream $1")
306
+ .replace(QUOTA_SHAPED_PATTERN, "upstream rate-limit notice");
307
+ }
308
+ function invalidResponse(zone, message) {
309
+ return new Error(`Bright Data API returned invalid response for zone ${zone}: ${message}`);
310
+ }
311
+ // Bright Data reports some failures inside a normal HTTP 200 body — e.g.
312
+ // `{"error":"zone not found","code":"zone_missing"}` — because `format: "raw"`
313
+ // means the HTTP layer describes the proxy hop, not the outcome.
314
+ //
315
+ // The envelope is upstream text, so it gets the same two-step treatment as every other
316
+ // quoted body: `redactCredential` first (an upstream "token <yours> is not valid for
317
+ // this zone" must not reprint the token), then `untrustedText`.
318
+ function envelopeError(envelope, apiKey) {
319
+ const parts = [];
320
+ const { error, errors } = envelope;
321
+ if (typeof error === "string" && error.trim())
322
+ parts.push(error.trim());
323
+ else if (error && typeof error === "object")
324
+ parts.push(JSON.stringify(error));
325
+ if (Array.isArray(errors) && errors.length > 0)
326
+ parts.push(JSON.stringify(errors));
327
+ else if (typeof errors === "string" && errors.trim())
328
+ parts.push(errors.trim());
329
+ if (parts.length === 0)
330
+ return null;
331
+ for (const key of ["code", "error_code"]) {
332
+ const code = envelope[key];
333
+ if (typeof code === "string" && code.trim())
334
+ parts.push(`${key} ${code.trim()}`);
335
+ else if (typeof code === "number")
336
+ parts.push(`${key} ${code}`);
337
+ }
338
+ return untrustedText(redactCredential(parts.join(", "), apiKey), 200);
339
+ }
340
+ // The envelope is validated strictly — every branch below throws — because a
341
+ // shape this provider cannot read means the request was billed for something
342
+ // unusable, and returning `{ answer: "", results: [] }` would report a paid
343
+ // failure as "the web had no answer". That includes the two shapes that are JSON
344
+ // but not a SERP: Bright Data's own error envelope, and an envelope with no
345
+ // `organic` array at all. anysearch.ts rejects the equivalent shape the same way.
346
+ // Individual organic entries are the one deliberate exception: a real SERP mixes
347
+ // in entries with no link, and one of those must not throw away a page of results
348
+ // that was already paid for.
349
+ function parseSerpResponse(value, zone, apiKey) {
350
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
351
+ throw invalidResponse(zone, "expected an object envelope");
352
+ }
353
+ const envelope = value;
354
+ const upstreamError = envelopeError(envelope, apiKey);
355
+ if (upstreamError) {
356
+ throw invalidResponse(zone, `Bright Data reported an error instead of a SERP: ${upstreamError}`);
357
+ }
358
+ if (envelope.organic === undefined || envelope.organic === null) {
359
+ throw invalidResponse(zone, "expected an organic array and the envelope carried none. A `serp` zone queried with brd_json=1 " +
360
+ "returns { organic: [...] }; a zone of type `unblocker`, or a missing brd_json=1, is the usual cause");
361
+ }
362
+ if (!Array.isArray(envelope.organic))
363
+ throw invalidResponse(zone, "expected organic array");
364
+ const organic = [];
365
+ for (const [index, entry] of envelope.organic.entries()) {
366
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
367
+ throw invalidResponse(zone, `expected organic[${index}] object`);
368
+ }
369
+ organic.push(entry);
370
+ }
371
+ return { organic };
372
+ }
373
+ function mapResults(organic, numResults, filters) {
374
+ if (!Array.isArray(organic))
375
+ return [];
376
+ const mapped = [];
377
+ for (const item of organic) {
378
+ const url = typeof item.link === "string" ? item.link.trim() : "";
379
+ if (!url || !passesDomainFilters(url, filters))
380
+ continue;
381
+ const title = typeof item.title === "string" ? item.title.trim() : "";
382
+ mapped.push({
383
+ title: title || `Source ${mapped.length + 1}`,
384
+ url,
385
+ snippet: typeof item.description === "string" ? item.description.replace(/\s+/g, " ").trim() : "",
386
+ });
387
+ if (mapped.length >= numResults)
388
+ break;
389
+ }
390
+ return mapped;
391
+ }
392
+ // A SERP zone returns ranked links, never a synthesized answer, so one is
393
+ // assembled from the sources — the same shape brave.ts and searxng.ts produce.
394
+ function buildAnswer(results) {
395
+ return results
396
+ .map((result) => result.snippet
397
+ ? `${result.snippet}\nSource: ${result.title} (${result.url})`
398
+ : `Source: ${result.title} (${result.url})`)
399
+ .join("\n\n");
400
+ }
401
+ // Both halves are required: a token with no zone cannot make a request, and a
402
+ // zone with no token cannot either. Availability therefore checks the config this
403
+ // surface actually needs rather than merely a key, the way firecrawl.ts's
404
+ // `isFirecrawlAvailable()` checks its required base URL. Reporting "unavailable"
405
+ // for a half-finished setup keeps the incomplete case out of provider selection
406
+ // instead of turning it into a failed search.
407
+ //
408
+ // It is also a pure predicate that cannot throw. Callers evaluate it outside
409
+ // their error handling — `gemini-search.ts` before the per-provider try/catch,
410
+ // `index.ts` in the uncaught `getProviderAvailability()` await on the `web_search`
411
+ // path — so any throw from a malformed Bright Data setting or an unparseable
412
+ // config file would break search for every other provider too. Misconfiguration
413
+ // makes this provider unavailable here; the request path reports it in detail.
414
+ export function isBrightDataAvailable() {
415
+ try {
416
+ if (getSerpZone() === null)
417
+ return false;
418
+ return hasCredentialSource({
419
+ provider: "Bright Data",
420
+ configuredValue: loadConfig().brightdataApiKey,
421
+ environmentValue: process.env.BRIGHTDATA_API_KEY,
422
+ });
423
+ }
424
+ catch {
425
+ return false;
426
+ }
427
+ }
428
+ export async function searchWithBrightData(query, options = {}) {
429
+ // Zone before key: a config mistake must not spawn a `!command` credential
430
+ // resolver, and must not reach a billable endpoint.
431
+ const zone = requireSerpZone();
432
+ const apiKey = await requireApiKey(options.signal);
433
+ const numResults = normalizeCount(options.numResults);
434
+ const filters = parseDomainFilter(options.domainFilter);
435
+ const searchQuery = buildSearchQuery(query, filters);
436
+ const body = {
437
+ url: buildSerpUrl(searchQuery, numResults, options.recencyFilter),
438
+ zone,
439
+ // `format: "raw"` returns the proxied body verbatim; `data_format` selects
440
+ // Bright Data's own parsing of it. `parsed_light` is the SERP shape —
441
+ // { organic: [{ link, title, description }] } — and carries no page bodies.
442
+ format: "raw",
443
+ data_format: "parsed_light",
444
+ };
445
+ const activityId = activityMonitor.logStart({ type: "api", query: searchQuery });
446
+ let response;
447
+ try {
448
+ response = await fetch(BRIGHTDATA_API_URL, {
449
+ method: "POST",
450
+ headers: {
451
+ "Authorization": `Bearer ${apiKey}`,
452
+ "Content-Type": "application/json",
453
+ },
454
+ body: JSON.stringify(body),
455
+ signal: requestSignal(options.signal),
456
+ });
457
+ }
458
+ catch (err) {
459
+ const message = errorMessage(err);
460
+ const redactedMessage = redactCredential(message, apiKey);
461
+ if (redactedMessage.toLowerCase().includes("abort"))
462
+ activityMonitor.logComplete(activityId, 0);
463
+ else
464
+ activityMonitor.logError(activityId, redactedMessage);
465
+ if (redactedMessage === message)
466
+ throw err;
467
+ const redactedError = new Error(redactedMessage);
468
+ if (err instanceof Error)
469
+ redactedError.name = err.name;
470
+ throw redactedError;
471
+ }
472
+ // Every thrown message names the zone the request was billed against: with two
473
+ // Bright Data zone types in play, "which zone did I pay on" is the first thing
474
+ // a paid failure has to answer.
475
+ if (!response.ok) {
476
+ activityMonitor.logComplete(activityId, response.status);
477
+ const errorText = untrustedText(redactCredential(await response.text(), apiKey));
478
+ throw new Error(`Bright Data API error ${response.status} for zone ${zone}: ${errorText}`);
479
+ }
480
+ // The body is read as text before parsing on purpose. `format: "raw"` means a
481
+ // 200 can still carry an upstream interstitial instead of SERP JSON, and a
482
+ // bare parser error ("Unexpected token <") hides a request that was billed.
483
+ // Quoting the body is the only way the user learns what they paid for.
484
+ const raw = await response.text();
485
+ if (!raw.trim()) {
486
+ activityMonitor.logComplete(activityId, response.status);
487
+ throw new Error(`Bright Data API returned empty response for zone ${zone}`);
488
+ }
489
+ let rawData;
490
+ try {
491
+ rawData = JSON.parse(raw);
492
+ }
493
+ catch (err) {
494
+ activityMonitor.logComplete(activityId, response.status);
495
+ // The parser's own message is deliberately NOT quoted here. V8 embeds a short
496
+ // window of the offending input in it (`Unexpected token 'x', "xxxxxxxx-x"...`),
497
+ // and that window is a *prefix* of the body — so when the body starts with the
498
+ // credential, `redactCredential` cannot match it: it replaces the whole key, and
499
+ // a 10-character prefix is not the whole key. Redacting the detail half therefore
500
+ // still printed 10 cleartext characters of the token. The detail also adds nothing
501
+ // this message does not already carry: the wording names the failure and the body
502
+ // below shows what arrived, filtered. Dropping it removes the leak by construction
503
+ // rather than by pattern-matching.
504
+ const body = untrustedText(redactCredential(raw, apiKey));
505
+ throw new Error(`Bright Data API returned invalid JSON for zone ${zone}: ${body}`);
506
+ }
507
+ let data;
508
+ try {
509
+ data = parseSerpResponse(rawData, zone, apiKey);
510
+ }
511
+ catch (err) {
512
+ activityMonitor.logComplete(activityId, response.status);
513
+ throw err;
514
+ }
515
+ activityMonitor.logComplete(activityId, response.status);
516
+ const results = mapResults(data.organic, numResults, filters);
517
+ return { answer: buildAnswer(results), results };
518
+ }
519
+ //# sourceMappingURL=brightdata.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brightdata.js","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/brightdata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAClG,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEpD,MAAM,kBAAkB,GAAG,oCAAoC,CAAC;AAChE,MAAM,WAAW,GAAG,sBAAsB,EAAE,CAAC;AAC7C,MAAM,iBAAiB,GAAG,MAAM,CAAC;AAEjC,gFAAgF;AAChF,gFAAgF;AAChF,iFAAiF;AACjF,+EAA+E;AAC/E,gFAAgF;AAChF,+DAA+D;AAC/D,MAAM,YAAY,GAAG,kBAAkB,CAAC;AAExC,iFAAiF;AACjF,+EAA+E;AAC/E,2BAA2B;AAC3B,MAAM,WAAW,GAA2B;IAC3C,GAAG,EAAE,OAAO;IACZ,IAAI,EAAE,OAAO;IACb,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,OAAO;CACb,CAAC;AAwBF,IAAI,YAAY,GAA2B,IAAI,CAAC;AAEhD,mFAAmF;AACnF,oFAAkF;AAClF,kFAAgF;AAChF,4CAA4C;AAC5C,mFAAmF;AACnF,oFAAoF;AACpF,sFAAsF;AACtF,gFAAgF;AAChF,mDAAmD;AACnD,EAAE;AACF,8EAA8E;AAC9E,oFAAoF;AACpF,gFAAgF;AAChF,oFAAoF;AACpF,SAAS,iBAAiB,CAAC,GAAY,EAAU;IAChD,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAC;IAC1F,OAAO,QAAQ,CAAC,CAAC,CAAC,mBAAmB,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAAC;AAAA,CACtE;AAED,SAAS,UAAU,GAAoB;IACtC,IAAI,YAAY;QAAE,OAAO,YAAY,CAAC;IACtC,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC9B,YAAY,GAAG,EAAE,CAAC;QAClB,OAAO,YAAY,CAAC;IACrB,CAAC;IAED,MAAM,GAAG,GAAG,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC/C,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACJ,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,mBAAmB,WAAW,KAAK,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9E,CAAC;IACD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,KAAK,CAAC,qBAAqB,WAAW,0BAA0B,CAAC,CAAC;IAC7E,CAAC;IACD,YAAY,GAAG,MAAyB,CAAC;IACzC,OAAO,YAAY,CAAC;AAAA,CACpB;AAED,KAAK,UAAU,SAAS,CAAC,MAAoB,EAA0B;IACtE,OAAO,iBAAiB,CAAC;QACxB,QAAQ,EAAE,aAAa;QACvB,eAAe,EAAE,UAAU,EAAE,CAAC,gBAAgB;QAC9C,gBAAgB,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB;QAChD,MAAM;KACN,CAAC,CAAC;AAAA,CACH;AAED,KAAK,UAAU,aAAa,CAAC,MAAoB,EAAmB;IACnE,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,CAAC;IACvC,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACd,0CAA0C;YAC1C,eAAe,WAAW,4CAA4C;YACtE,oDAAoD;YACpD,sDAAsD,CACtD,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAAA,CACd;AAED,iFAAiF;AACjF,iFAAiF;AACjF,yDAAyD;AACzD,EAAE;AACF,mEAAmE;AACnE,kFAAkF;AAClF,+EAA6E;AAC7E,mFAAmF;AACnF,8EAA8E;AAC9E,4EAA4E;AAC5E,sEAAsE;AACtE,6DAA6D;AAC7D,SAAS,aAAa,CAAC,KAAc,EAAiB;IACrD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,OAAO,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AAAA,CACnD;AAOD,kFAAkF;AAClF,8EAA8E;AAC9E,sEAAsE;AACtE,SAAS,eAAe,GAAuB;IAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;IACjD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;QACnD,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE,CAAC;IAC/D,CAAC;IACD,MAAM,UAAU,GAAG,UAAU,EAAE,CAAC,kBAAkB,CAAC;IACnD,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC;QACzD,OAAO,EAAE,GAAG,EAAE,UAAU,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,yBAAyB,WAAW,EAAE,EAAE,CAAC;IAClF,CAAC;IACD,OAAO,IAAI,CAAC;AAAA,CACZ;AAED,SAAS,WAAW,GAAkB;IACrC,MAAM,OAAO,GAAG,eAAe,EAAE,CAAC;IAClC,OAAO,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAAA,CACnD;AAED,kFAAkF;AAClF,mFAAmF;AACnF,6EAA2E;AAC3E,+EAA+E;AAC/E,uDAAuD;AACvD,SAAS,eAAe,GAAW;IAClC,MAAM,OAAO,GAAG,eAAe,EAAE,CAAC;IAClC,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACzD,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IACtB,IAAI,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACd,qCAAqC,OAAO,CAAC,KAAK,uDAAuD;YACzG,SAAS,aAAa,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO;YAC9C,6IAA6I;YAC7I,yDAAyD,CACzD,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CACd,wDAAwD;QACxD,eAAe,WAAW,oDAAoD;QAC9E,sDAAsD;QACtD,0HAA0H;QAC1H,+CAA+C,CAC/C,CAAC;AAAA,CACF;AAED,SAAS,cAAc,CAAC,KAAyB,EAAU;IAC1D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACnE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAAA,CACpD;AAED,SAAS,eAAe,CAAC,KAAa,EAAiB;IACtD,IAAI,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACvC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACzD,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,WAAW,KAAK,EAAE,CAAC,CAAC;QACpF,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACR,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAClD,CAAC;IACD,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IACxC,OAAO,mCAAmC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AAAA,CACtE;AAOD,SAAS,iBAAiB,CAAC,YAAkC,EAAiB;IAC7E,MAAM,OAAO,GAAkB,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAC5D,IAAI,CAAC,YAAY,EAAE,MAAM;QAAE,OAAO,OAAO,CAAC;IAC1C,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,CAAC,MAAM;YAAE,SAAS;QACtB,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;QAC9E,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,OAAO,CAAC;AAAA,CACf;AAED,kFAAkF;AAClF,+EAA+E;AAC/E,gFAAgF;AAChF,4EAA0E;AAC1E,SAAS,gBAAgB,CAAC,KAAa,EAAE,OAAsB,EAAU;IACxE,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC;IACtB,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClC,KAAK,CAAC,IAAI,CAAC,QAAQ,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC1C,CAAC;SAAM,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5E,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACtC,KAAK,CAAC,IAAI,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAAA,CACvB;AAED,SAAS,YAAY,CAAC,WAAmB,EAAE,UAAkB,EAAE,aAA6C,EAAU;IACrH,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC;IACvD,8EAA8E;IAC9E,0EAA0E;IAC1E,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IACxD,MAAM,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACnE,IAAI,GAAG;QAAE,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAChC,4EAA4E;IAC5E,+EAA+E;IAC/E,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IAC5B,OAAO,iCAAiC,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;AAAA,CAC5D;AAED,SAAS,aAAa,CAAC,QAAgB,EAAE,MAAc,EAAW;IACjE,OAAO,QAAQ,KAAK,MAAM,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC;AAAA,CAC9D;AAED,4EAA4E;AAC5E,iFAAiF;AACjF,iEAAiE;AACjE,SAAS,mBAAmB,CAAC,GAAW,EAAE,OAAsB,EAAW;IAC1E,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9E,IAAI,QAAgB,CAAC;IACrB,IAAI,CAAC;QACJ,QAAQ,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;IACD,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACpF,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9C,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;AAAA,CACzE;AAED,SAAS,aAAa,CAAC,MAAoB,EAAe;IACzD,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACvD,OAAO,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;AAAA,CAC7D;AAED,SAAS,YAAY,CAAC,GAAY,EAAU;IAC3C,OAAO,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAAA,CACxD;AAED,+EAA6E;AAC7E,mFAAiF;AACjF,iFAAiF;AACjF,mFAAmF;AACnF,iFAAiF;AACjF,iFAAiF;AACjF,8EAA8E;AAC9E,gFAAgF;AAChF,sEAAsE;AACtE,MAAM,qBAAqB,GAAG,gDAAgD,CAAC;AAE/E,sEAAsE;AACtE,kFAAkF;AAClF,2EAA2E;AAC3E,iFAAiF;AACjF,mFAAmF;AACnF,mFAAmF;AACnF,0EAA0E;AAC1E,mFAAmF;AACnF,sFAAoF;AACpF,qFAAqF;AACrF,qFAAqF;AACrF,EAAE;AACF,mFAAmF;AACnF,+EAA+E;AAC/E,gFAAgF;AAChF,MAAM,oBAAoB,GAAG,sCAAsC,CAAC;AAEpE,kFAAkF;AAClF,qFAAqF;AACrF,EAAE;AACF,yFAAyF;AACzF,yFAAyF;AACzF,yFAAyF;AACzF,yFAAyF;AACzF,yFAAyF;AACzF,4FAA0F;AAC1F,2FAAyF;AACzF,6FAAyF;AACzF,6FAAyF;AACzF,EAAE;AACF,oFAAoF;AACpF,6EAA6E;AAC7E,kEAAkE;AAClE,gFAAgF;AAChF,sFAAsF;AACtF,8EAA8E;AAC9E,sFAAsF;AACtF,sFAAsF;AACtF,uFAAuF;AACvF,mFAAiF;AACjF,+CAA+C;AAE/C,2EAA2E;AAC3E,qFAAqF;AACrF,wFAAsF;AACtF,kEAAkE;AAClE,sFAAsF;AACtF,uFAAuF;AACvF,8DAA8D;AAC9D,EAAE;AACF,qFAAqF;AACrF,0FAAwF;AACxF,kFAAkF;AAClF,iDAAiD;AACjD,SAAS,aAAa,CAAC,IAAY,EAAE,KAAK,GAAG,GAAG,EAAU;IACzD,OAAO,IAAI;SACT,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE;SACN,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC;SACf,OAAO,CAAC,qBAAqB,EAAE,aAAa,CAAC;SAC7C,OAAO,CAAC,oBAAoB,EAAE,4BAA4B,CAAC,CAAC;AAAA,CAC9D;AAED,SAAS,eAAe,CAAC,IAAY,EAAE,OAAe,EAAS;IAC9D,OAAO,IAAI,KAAK,CAAC,sDAAsD,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC;AAAA,CAC3F;AAED,2EAAyE;AACzE,iFAA+E;AAC/E,iEAAiE;AACjE,EAAE;AACF,uFAAuF;AACvF,qFAAqF;AACrF,gEAAgE;AAChE,SAAS,aAAa,CAAC,QAAiC,EAAE,MAAqB,EAAiB;IAC/F,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAC;IACnC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE;QAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;SACnE,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/E,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;SAC9E,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE;QAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAChF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,KAAK,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC3B,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE;YAAE,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;aAC5E,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,aAAa,CAAC,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC;AAAA,CACtE;AAED,iFAA6E;AAC7E,6EAA6E;AAC7E,4EAA4E;AAC5E,iFAAiF;AACjF,4EAA4E;AAC5E,kFAAkF;AAClF,iFAAiF;AACjF,kFAAkF;AAClF,6BAA6B;AAC7B,SAAS,iBAAiB,CAAC,KAAc,EAAE,IAAY,EAAE,MAAqB,EAA0B;IACvG,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACjE,MAAM,eAAe,CAAC,IAAI,EAAE,6BAA6B,CAAC,CAAC;IAC5D,CAAC;IACD,MAAM,QAAQ,GAAG,KAAgC,CAAC;IAClD,MAAM,aAAa,GAAG,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACtD,IAAI,aAAa,EAAE,CAAC;QACnB,MAAM,eAAe,CAAC,IAAI,EAAE,oDAAoD,aAAa,EAAE,CAAC,CAAC;IAClG,CAAC;IACD,IAAI,QAAQ,CAAC,OAAO,KAAK,SAAS,IAAI,QAAQ,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QACjE,MAAM,eAAe,CACpB,IAAI,EACJ,iGAAiG;YACjG,qGAAqG,CACrG,CAAC;IACH,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,MAAM,eAAe,CAAC,IAAI,EAAE,wBAAwB,CAAC,CAAC;IAC5F,MAAM,OAAO,GAA8B,EAAE,CAAC;IAC9C,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QACzD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACjE,MAAM,eAAe,CAAC,IAAI,EAAE,oBAAoB,KAAK,UAAU,CAAC,CAAC;QAClE,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,KAAgC,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,CAAC;AAAA,CACnB;AAED,SAAS,UAAU,CAClB,OAA8C,EAC9C,UAAkB,EAClB,OAAsB,EACM;IAC5B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC;IACvC,MAAM,MAAM,GAA8B,EAAE,CAAC;IAC7C,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClE,IAAI,CAAC,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,EAAE,OAAO,CAAC;YAAE,SAAS;QACzD,MAAM,KAAK,GAAG,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtE,MAAM,CAAC,IAAI,CAAC;YACX,KAAK,EAAE,KAAK,IAAI,UAAU,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;YAC7C,GAAG;YACH,OAAO,EAAE,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE;SACjG,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,MAAM,IAAI,UAAU;YAAE,MAAM;IACxC,CAAC;IACD,OAAO,MAAM,CAAC;AAAA,CACd;AAED,0EAA0E;AAC1E,iFAA+E;AAC/E,SAAS,WAAW,CAAC,OAAkC,EAAU;IAChE,OAAO,OAAO;SACZ,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO;QAC9B,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,aAAa,MAAM,CAAC,KAAK,KAAK,MAAM,CAAC,GAAG,GAAG;QAC9D,CAAC,CAAC,WAAW,MAAM,CAAC,KAAK,KAAK,MAAM,CAAC,GAAG,GAAG,CAAC;SAC5C,IAAI,CAAC,MAAM,CAAC,CAAC;AAAA,CACf;AAED,8EAA8E;AAC9E,kFAAkF;AAClF,0EAA0E;AAC1E,iFAAiF;AACjF,gFAAgF;AAChF,8CAA8C;AAC9C,EAAE;AACF,6EAA6E;AAC7E,iFAA+E;AAC/E,mFAAmF;AACnF,+EAA6E;AAC7E,gFAAgF;AAChF,+EAA+E;AAC/E,MAAM,UAAU,qBAAqB,GAAY;IAChD,IAAI,CAAC;QACJ,IAAI,WAAW,EAAE,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC;QACzC,OAAO,mBAAmB,CAAC;YAC1B,QAAQ,EAAE,aAAa;YACvB,eAAe,EAAE,UAAU,EAAE,CAAC,gBAAgB;YAC9C,gBAAgB,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB;SAChD,CAAC,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AAAA,CACD;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,KAAa,EAAE,OAAO,GAA4B,EAAE,EAA2B;IACzH,2EAA2E;IAC3E,oDAAoD;IACpD,MAAM,IAAI,GAAG,eAAe,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACnD,MAAM,UAAU,GAAG,cAAc,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACtD,MAAM,OAAO,GAAG,iBAAiB,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACxD,MAAM,WAAW,GAAG,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACrD,MAAM,IAAI,GAA4B;QACrC,GAAG,EAAE,YAAY,CAAC,WAAW,EAAE,UAAU,EAAE,OAAO,CAAC,aAAa,CAAC;QACjE,IAAI;QACJ,2EAA2E;QAC3E,wEAAsE;QACtE,8EAA4E;QAC5E,MAAM,EAAE,KAAK;QACb,WAAW,EAAE,cAAc;KAC3B,CAAC;IAEF,MAAM,UAAU,GAAG,eAAe,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;IACjF,IAAI,QAAkB,CAAC;IACvB,IAAI,CAAC;QACJ,QAAQ,GAAG,MAAM,KAAK,CAAC,kBAAkB,EAAE;YAC1C,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACR,eAAe,EAAE,UAAU,MAAM,EAAE;gBACnC,cAAc,EAAE,kBAAkB;aAClC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;YAC1B,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC;SACrC,CAAC,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;QAClC,MAAM,eAAe,GAAG,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC1D,IAAI,eAAe,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,eAAe,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;;YAC3F,eAAe,CAAC,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;QAC3D,IAAI,eAAe,KAAK,OAAO;YAAE,MAAM,GAAG,CAAC;QAC3C,MAAM,aAAa,GAAG,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;QACjD,IAAI,GAAG,YAAY,KAAK;YAAE,aAAa,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;QACxD,MAAM,aAAa,CAAC;IACrB,CAAC;IAED,+EAA+E;IAC/E,+EAA+E;IAC/E,gCAAgC;IAChC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QAClB,eAAe,CAAC,WAAW,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QACzD,MAAM,SAAS,GAAG,aAAa,CAAC,gBAAgB,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;QACjF,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,CAAC,MAAM,aAAa,IAAI,KAAK,SAAS,EAAE,CAAC,CAAC;IAC5F,CAAC;IAED,8EAA8E;IAC9E,2EAA2E;IAC3E,4EAA4E;IAC5E,uEAAuE;IACvE,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAClC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;QACjB,eAAe,CAAC,WAAW,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QACzD,MAAM,IAAI,KAAK,CAAC,oDAAoD,IAAI,EAAE,CAAC,CAAC;IAC7E,CAAC;IAED,IAAI,OAAgB,CAAC;IACrB,IAAI,CAAC;QACJ,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,eAAe,CAAC,WAAW,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QACzD,8EAA8E;QAC9E,iFAAiF;QACjF,iFAA+E;QAC/E,iFAAiF;QACjF,kFAAkF;QAClF,mFAAmF;QACnF,kFAAkF;QAClF,mFAAmF;QACnF,mCAAmC;QACnC,MAAM,IAAI,GAAG,aAAa,CAAC,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,kDAAkD,IAAI,KAAK,IAAI,EAAE,CAAC,CAAC;IACpF,CAAC;IAED,IAAI,IAA4B,CAAC;IACjC,IAAI,CAAC;QACJ,IAAI,GAAG,iBAAiB,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IACjD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,eAAe,CAAC,WAAW,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QACzD,MAAM,GAAG,CAAC;IACX,CAAC;IAED,eAAe,CAAC,WAAW,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACzD,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;IAC9D,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;AAAA,CACjD","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { activityMonitor } from \"./activity.ts\";\nimport type { SearchOptions, SearchResponse } from \"./perplexity.ts\";\nimport { hasCredentialSource, redactCredential, resolveCredential } from \"./credential-source.ts\";\nimport { getWebSearchConfigPath } from \"./utils.ts\";\n\nconst BRIGHTDATA_API_URL = \"https://api.brightdata.com/request\";\nconst CONFIG_PATH = getWebSearchConfigPath();\nconst SEARCH_TIMEOUT_MS = 60_000;\n\n// Bright Data proxies a real Google SERP through a provisioned \"zone\" and bills\n// per successful request against it. A SERP zone (Bright Data zone type `serp`)\n// and a Web Unlocker zone (type `unblocker`) are separate products with separate\n// prices, so the zone used for search is its own required setting and is never\n// guessed: the wrong zone is either an opaque 400 or a charge against the wrong\n// line of the invoice. Pricing: https://brightdata.com/pricing\nconst ZONE_PATTERN = /^[A-Za-z0-9_-]+$/;\n\n// Google's own time filter, passed straight through in the proxied URL. Unlike a\n// query-text hint this is an engine-side filter, so results outside the window\n// are not returned at all.\nconst RECENCY_TBS: Record<string, string> = {\n\tday: \"qdr:d\",\n\tweek: \"qdr:w\",\n\tmonth: \"qdr:m\",\n\tyear: \"qdr:y\",\n};\n\ninterface WebSearchConfig {\n\tbrightdataApiKey?: unknown;\n\tbrightdataSerpZone?: unknown;\n}\n\ninterface BrightDataOrganicResult {\n\tlink?: unknown;\n\ttitle?: unknown;\n\tdescription?: unknown;\n}\n\ninterface BrightDataSerpResponse {\n\torganic?: BrightDataOrganicResult[];\n}\n\ninterface BrightDataSearchOptions extends SearchOptions {\n\t// Accepted for parity with the other providers and deliberately ignored: a\n\t// SERP zone returns ranked links, never page bodies, so there is nothing to\n\t// return inline and `inlineContent` is never set.\n\tincludeContent?: boolean;\n}\n\nlet cachedConfig: WebSearchConfig | null = null;\n\n// `web-search.json` is a credential store: its own text is the secret. V8 quotes a\n// window of the source it choked on back inside the `JSON.parse` message — with a\n// short file, the whole file — so `{\"brightdataApiKey\": bd-real-token}` (quotes\n// forgotten around a pasted token) produces\n// `Unexpected token 'b', \"{\"brightdataApiKey\": bd-real-token}\" is not valid JSON`.\n// There is no credential to redact against at this point, because the credential is\n// what the file was being read for, so the parser's text is dropped entirely and only\n// its position is kept. That position is also the only part that cannot carry a\n// status-shaped phrase into `providerErrorStatus`.\n//\n// This is a deliberate divergence from `serpdive.ts:59-62`, `brave.ts:33-36`,\n// `anysearch.ts:48-51` and `firecrawl.ts:50-53`, which all quote the parser message\n// verbatim. They have the same leak; fixing it for every provider is a separate\n// change, and this module is not going to copy the bug forward to justify symmetry.\nfunction configParseDetail(err: unknown): string {\n\tconst position = errorMessage(err).match(/at position \\d+(?: \\(line \\d+ column \\d+\\))?/i);\n\treturn position ? `not valid JSON, ${position[0]}` : \"not valid JSON\";\n}\n\nfunction loadConfig(): WebSearchConfig {\n\tif (cachedConfig) return cachedConfig;\n\tif (!existsSync(CONFIG_PATH)) {\n\t\tcachedConfig = {};\n\t\treturn cachedConfig;\n\t}\n\n\tconst raw = readFileSync(CONFIG_PATH, \"utf-8\");\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch (err) {\n\t\tthrow new Error(`Failed to parse ${CONFIG_PATH}: ${configParseDetail(err)}`);\n\t}\n\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n\t\tthrow new Error(`Invalid config in ${CONFIG_PATH}: expected a JSON object`);\n\t}\n\tcachedConfig = parsed as WebSearchConfig;\n\treturn cachedConfig;\n}\n\nasync function getApiKey(signal?: AbortSignal): Promise<string | null> {\n\treturn resolveCredential({\n\t\tprovider: \"Bright Data\",\n\t\tconfiguredValue: loadConfig().brightdataApiKey,\n\t\tenvironmentValue: process.env.BRIGHTDATA_API_KEY,\n\t\tsignal,\n\t});\n}\n\nasync function requireApiKey(signal?: AbortSignal): Promise<string> {\n\tconst apiKey = await getApiKey(signal);\n\tif (!apiKey) {\n\t\tthrow new Error(\n\t\t\t\"Bright Data API key not found. Either:\\n\" +\n\t\t\t` 1. Create ${CONFIG_PATH} with { \"brightdataApiKey\": \"your-key\" }\\n` +\n\t\t\t\" 2. Set BRIGHTDATA_API_KEY environment variable\\n\" +\n\t\t\t\"Get a key at https://brightdata.com/cp/setting/users\",\n\t\t);\n\t}\n\treturn apiKey;\n}\n\n// A zone name is an account-scoped identifier, not a URL. Validating the charset\n// here keeps a stray value out of the request body, where Bright Data reports it\n// as a generic 400 that reads like a credential problem.\n//\n// Total by construction: an invalid zone is `null`, never a throw.\n// `isBrightDataAvailable()` reads this, and availability is evaluated outside its\n// callers' error handling — `gemini-search.ts` checks it before entering the\n// per-provider try/catch, and `index.ts` awaits `getProviderAvailability()` on the\n// `web_search` path with nothing around it. A throw here would therefore take\n// `web_search` down for Brave and OpenAI too, over one mistyped Bright Data\n// setting. The loud, actionable error belongs on the request path, in\n// `requireSerpZone()`, where only this provider is affected.\nfunction normalizeZone(value: unknown): string | null {\n\tif (typeof value !== \"string\") return null;\n\tconst trimmed = value.trim();\n\tif (!trimmed) return null;\n\treturn ZONE_PATTERN.test(trimmed) ? trimmed : null;\n}\n\ninterface ZoneSetting {\n\traw: string;\n\tlabel: string;\n}\n\n// Resolved by presence, not by validity: a `BRIGHTDATA_SERP_ZONE` that is set but\n// malformed must not silently hand the request to the config file's zone. The\n// setting the user actually filled in is the setting the error names.\nfunction serpZoneSetting(): ZoneSetting | null {\n\tconst fromEnv = process.env.BRIGHTDATA_SERP_ZONE;\n\tif (typeof fromEnv === \"string\" && fromEnv.trim()) {\n\t\treturn { raw: fromEnv.trim(), label: \"BRIGHTDATA_SERP_ZONE\" };\n\t}\n\tconst configured = loadConfig().brightdataSerpZone;\n\tif (typeof configured === \"string\" && configured.trim()) {\n\t\treturn { raw: configured.trim(), label: `brightdataSerpZone in ${CONFIG_PATH}` };\n\t}\n\treturn null;\n}\n\nfunction getSerpZone(): string | null {\n\tconst setting = serpZoneSetting();\n\treturn setting ? normalizeZone(setting.raw) : null;\n}\n\n// There is no safe default, and in particular no fallback to a Web Unlocker zone:\n// `serp` and `unblocker` are different Bright Data zone types, priced differently,\n// and an Unlocker zone does not return SERP JSON — substituting one buys a\n// confusing paid failure instead of a search. A missing or malformed SERP zone\n// fails loudly here, before anything billable happens.\nfunction requireSerpZone(): string {\n\tconst setting = serpZoneSetting();\n\tconst zone = setting ? normalizeZone(setting.raw) : null;\n\tif (zone) return zone;\n\tif (setting) {\n\t\tthrow new Error(\n\t\t\t`Bright Data SERP zone is invalid: ${setting.label} must be a zone name of letters, digits, \"-\", or \"_\" ` +\n\t\t\t`(got \"${untrustedText(setting.raw, 60)}\").\\n` +\n\t\t\t\"The zone must be of Bright Data type `serp`; a Web Unlocker zone (type `unblocker`) is a different product and does not return SERP JSON.\\n\" +\n\t\t\t\"Create or rename one at https://brightdata.com/cp/zones\",\n\t\t);\n\t}\n\tthrow new Error(\n\t\t\"Bright Data SERP zone is invalid or missing. Either:\\n\" +\n\t\t` 1. Create ${CONFIG_PATH} with { \"brightdataSerpZone\": \"your_serp_zone\" }\\n` +\n\t\t\" 2. Set BRIGHTDATA_SERP_ZONE environment variable\\n\" +\n\t\t\"The zone must be of Bright Data type `serp`; a Web Unlocker zone is a different product and does not return SERP JSON.\\n\" +\n\t\t\"Create one at https://brightdata.com/cp/zones\",\n\t);\n}\n\nfunction normalizeCount(value: number | undefined): number {\n\tif (typeof value !== \"number\" || !Number.isFinite(value)) return 5;\n\treturn Math.max(1, Math.min(Math.floor(value), 20));\n}\n\nfunction normalizeDomain(value: string): string | null {\n\tlet input = value.trim().toLowerCase();\n\tif (!input) return null;\n\tif (input.startsWith(\"-\")) input = input.slice(1).trim();\n\tif (!input) return null;\n\ttry {\n\t\tconst parsed = input.includes(\"://\") ? new URL(input) : new URL(`https://${input}`);\n\t\tinput = parsed.hostname;\n\t} catch {\n\t\tinput = input.split(\"/\")[0]?.split(\":\")[0] ?? \"\";\n\t}\n\tinput = input.replace(/^\\.+|\\.+$/g, \"\");\n\treturn /^[a-z0-9][a-z0-9.-]*\\.[a-z]{2,}$/i.test(input) ? input : null;\n}\n\ninterface DomainFilters {\n\tinclude: string[];\n\texclude: string[];\n}\n\nfunction parseDomainFilter(domainFilter: string[] | undefined): DomainFilters {\n\tconst filters: DomainFilters = { include: [], exclude: [] };\n\tif (!domainFilter?.length) return filters;\n\tfor (const raw of domainFilter) {\n\t\tconst domain = normalizeDomain(raw);\n\t\tif (!domain) continue;\n\t\tconst target = raw.trim().startsWith(\"-\") ? filters.exclude : filters.include;\n\t\tif (!target.includes(domain)) target.push(domain);\n\t}\n\treturn filters;\n}\n\n// Bright Data has no domain parameter, but the engine behind it is Google, so the\n// filter is expressed the way Google expresses it: as `site:` operators inside\n// the query. That asks the engine for the right pages instead of discarding the\n// wrong ones locally — the difference SERPdive documents as a limitation.\nfunction buildSearchQuery(query: string, filters: DomainFilters): string {\n\tconst parts = [query];\n\tif (filters.include.length === 1) {\n\t\tparts.push(`site:${filters.include[0]}`);\n\t} else if (filters.include.length > 1) {\n\t\tparts.push(filters.include.map((domain) => `site:${domain}`).join(\" OR \"));\n\t}\n\tfor (const domain of filters.exclude) {\n\t\tparts.push(`-site:${domain}`);\n\t}\n\treturn parts.join(\" \");\n}\n\nfunction buildSerpUrl(searchQuery: string, numResults: number, recencyFilter: SearchOptions[\"recencyFilter\"]): string {\n\tconst params = new URLSearchParams({ q: searchQuery });\n\t// One SERP request is billed once whatever `num` says, so asking for a little\n\t// headroom is free and leaves something to keep after local re-filtering.\n\tparams.set(\"num\", String(Math.min(numResults + 5, 20)));\n\tconst tbs = recencyFilter ? RECENCY_TBS[recencyFilter] : undefined;\n\tif (tbs) params.set(\"tbs\", tbs);\n\t// `brd_json=1` is what makes Bright Data return the SERP as JSON instead of\n\t// Google's HTML. Without it `data_format: \"parsed_light\"` has nothing to read.\n\tparams.set(\"brd_json\", \"1\");\n\treturn `https://www.google.com/search?${params.toString()}`;\n}\n\nfunction domainMatches(hostname: string, domain: string): boolean {\n\treturn hostname === domain || hostname.endsWith(`.${domain}`);\n}\n\n// `site:` narrows the SERP but does not guarantee it: Google still mixes in\n// related hosts, and an OR group is honoured loosely. The same filter is applied\n// again here so the contract holds on what is actually returned.\nfunction passesDomainFilters(url: string, filters: DomainFilters): boolean {\n\tif (filters.include.length === 0 && filters.exclude.length === 0) return true;\n\tlet hostname: string;\n\ttry {\n\t\thostname = new URL(url).hostname.toLowerCase();\n\t} catch {\n\t\treturn false;\n\t}\n\tif (filters.exclude.some((domain) => domainMatches(hostname, domain))) return false;\n\tif (filters.include.length === 0) return true;\n\treturn filters.include.some((domain) => domainMatches(hostname, domain));\n}\n\nfunction requestSignal(signal?: AbortSignal): AbortSignal {\n\tconst timeout = AbortSignal.timeout(SEARCH_TIMEOUT_MS);\n\treturn signal ? AbortSignal.any([signal, timeout]) : timeout;\n}\n\nfunction errorMessage(err: unknown): string {\n\treturn err instanceof Error ? err.message : String(err);\n}\n\n// Text that did not originate here — a proxied response body, a `JSON.parse`\n// message quoting that body, a config value — is quoted into Error messages, and\n// `gemini-search.ts` classifies routing failures by reading a status back out of\n// the message text (`providerErrorStatus`, /\\b(?:error|status|http)\\s+(\\d{3})\\b/).\n// A proxied Google interstitial that merely mentions \"Error 503\" would otherwise\n// be read as *our* status and retried as a transient failure, when what actually\n// happened is that a 200 was billed and is unusable. Status-shaped phrases in\n// quoted text are rewritten to `upstream <code>`: the number is still reported,\n// but only this module's own wording can name the status we received.\nconst STATUS_SHAPED_PATTERN = /\\b(?:error|status|http)[\\s:=-]{1,4}(\\d{3})\\b/gi;\n\n// `providerErrorStatus` is not the only thing that reads our message.\n// `classifyProviderError` also matches bare keyword phrases, and its branch order\n// decides which ones can reach us: `searchRouting.fallbackOn` accepts only\n// `transient`, `quota` and `network`, and of those three only the `quota` branch\n// (/rate limit|quota|too many requests/) runs BEFORE the `invalid-response` branch\n// that this module's own wording always triggers. So a billed 200 whose body reads\n// \"You have exceeded your rate limit\" would classify as `quota`, and with\n// `fallbackOn: [\"quota\"]` the user is charged, silently served the next provider's\n// answer, and shown no diagnostic — the failure mode this module exists to prevent.\n// `transient` and `network` sit after `invalid-response` and are unreachable for the\n// same reason; the test suite pins that ordering assumption rather than trusting it.\n//\n// The phrase is replaced rather than removed: the reader still learns the upstream\n// page mentioned a rate limit, but no substring of the replacement matches any\n// classifier branch (\"rate-limit\" is hyphenated, so /rate limit/ cannot match).\nconst QUOTA_SHAPED_PATTERN = /rate limit|quota|too many requests/gi;\n\n// The complete inventory of places foreign text is quoted into a message or a log\n// line, and what each one is required to apply. Adding a fifth means adding it here:\n//\n// | site | redactCredential | untrustedText |\n// | ----------------------------------------------- | ---------------- | ------------- |\n// | non-2xx response body | yes | yes (300) |\n// | invalid-JSON raw body | yes | yes (300) |\n// | Bright Data's own 200 error envelope | yes | yes (200) |\n// | transport/abort error from `fetch` | yes | n/a — see (a) |\n// | rejected zone value | n/a — see (b) | yes (60) |\n// | `JSON.parse` message on a response body | n/a — see (c) | n/a — see (c) |\n// | `JSON.parse` failure on `web-search.json` | n/a — see (c) | n/a — see (c) |\n//\n// (a) That message is generated locally by undici or by `AbortSignal`, never by the\n// proxied page, and the abort branch below matches on it; collapsing and\n// truncating it would buy nothing and could break that match.\n// (b) The zone is resolved *before* the credential on purpose, so that a config\n// mistake never spawns a `!command` resolver. There is therefore no key to redact\n// against yet, and the value being echoed is the user's own zone setting.\n// (c) Neither parser message is quoted at all, so neither needs a filter or a cap. V8\n// embeds a *prefix* of the offending input in its message, and `redactCredential`\n// substitutes whole values, so a prefix cannot be redacted out of it. Dropping the\n// message removes the leak by construction — see `configParseDetail` and the\n// `JSON.parse` catch on the response path.\n\n// Truncation happens BEFORE the rewrite, and the order is the whole point.\n// `STATUS_SHAPED_PATTERN` requires `(\\d{3})\\b`, so `error 5031` is deliberately left\n// alone — it is not a status. Sanitising first and slicing second let the cut land on\n// the 4th digit and hand back a freshly forged `error 503`, which\n// `gemini-search.ts`'s `providerErrorStatus` then reads as *our* status: a billed 200\n// classified `transient` and silently re-run on the next provider. Slicing first means\n// the rewrite always sees the exact text that will be quoted.\n//\n// The rewrite runs last and is therefore allowed to push the result a few characters\n// past `limit` (\"error 503\" → \"upstream 503\"). `limit` bounds how much upstream text is\n// quoted, not the final string length; a second `slice()` after the rewrite would\n// re-open exactly the hole this ordering closes.\nfunction untrustedText(text: string, limit = 300): string {\n\treturn text\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim()\n\t\t.slice(0, limit)\n\t\t.replace(STATUS_SHAPED_PATTERN, \"upstream $1\")\n\t\t.replace(QUOTA_SHAPED_PATTERN, \"upstream rate-limit notice\");\n}\n\nfunction invalidResponse(zone: string, message: string): Error {\n\treturn new Error(`Bright Data API returned invalid response for zone ${zone}: ${message}`);\n}\n\n// Bright Data reports some failures inside a normal HTTP 200 body — e.g.\n// `{\"error\":\"zone not found\",\"code\":\"zone_missing\"}` — because `format: \"raw\"`\n// means the HTTP layer describes the proxy hop, not the outcome.\n//\n// The envelope is upstream text, so it gets the same two-step treatment as every other\n// quoted body: `redactCredential` first (an upstream \"token <yours> is not valid for\n// this zone\" must not reprint the token), then `untrustedText`.\nfunction envelopeError(envelope: Record<string, unknown>, apiKey: string | null): string | null {\n\tconst parts: string[] = [];\n\tconst { error, errors } = envelope;\n\tif (typeof error === \"string\" && error.trim()) parts.push(error.trim());\n\telse if (error && typeof error === \"object\") parts.push(JSON.stringify(error));\n\tif (Array.isArray(errors) && errors.length > 0) parts.push(JSON.stringify(errors));\n\telse if (typeof errors === \"string\" && errors.trim()) parts.push(errors.trim());\n\tif (parts.length === 0) return null;\n\tfor (const key of [\"code\", \"error_code\"]) {\n\t\tconst code = envelope[key];\n\t\tif (typeof code === \"string\" && code.trim()) parts.push(`${key} ${code.trim()}`);\n\t\telse if (typeof code === \"number\") parts.push(`${key} ${code}`);\n\t}\n\treturn untrustedText(redactCredential(parts.join(\", \"), apiKey), 200);\n}\n\n// The envelope is validated strictly — every branch below throws — because a\n// shape this provider cannot read means the request was billed for something\n// unusable, and returning `{ answer: \"\", results: [] }` would report a paid\n// failure as \"the web had no answer\". That includes the two shapes that are JSON\n// but not a SERP: Bright Data's own error envelope, and an envelope with no\n// `organic` array at all. anysearch.ts rejects the equivalent shape the same way.\n// Individual organic entries are the one deliberate exception: a real SERP mixes\n// in entries with no link, and one of those must not throw away a page of results\n// that was already paid for.\nfunction parseSerpResponse(value: unknown, zone: string, apiKey: string | null): BrightDataSerpResponse {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) {\n\t\tthrow invalidResponse(zone, \"expected an object envelope\");\n\t}\n\tconst envelope = value as Record<string, unknown>;\n\tconst upstreamError = envelopeError(envelope, apiKey);\n\tif (upstreamError) {\n\t\tthrow invalidResponse(zone, `Bright Data reported an error instead of a SERP: ${upstreamError}`);\n\t}\n\tif (envelope.organic === undefined || envelope.organic === null) {\n\t\tthrow invalidResponse(\n\t\t\tzone,\n\t\t\t\"expected an organic array and the envelope carried none. A `serp` zone queried with brd_json=1 \" +\n\t\t\t\"returns { organic: [...] }; a zone of type `unblocker`, or a missing brd_json=1, is the usual cause\",\n\t\t);\n\t}\n\tif (!Array.isArray(envelope.organic)) throw invalidResponse(zone, \"expected organic array\");\n\tconst organic: BrightDataOrganicResult[] = [];\n\tfor (const [index, entry] of envelope.organic.entries()) {\n\t\tif (!entry || typeof entry !== \"object\" || Array.isArray(entry)) {\n\t\t\tthrow invalidResponse(zone, `expected organic[${index}] object`);\n\t\t}\n\t\torganic.push(entry as BrightDataOrganicResult);\n\t}\n\treturn { organic };\n}\n\nfunction mapResults(\n\torganic: BrightDataOrganicResult[] | undefined,\n\tnumResults: number,\n\tfilters: DomainFilters,\n): SearchResponse[\"results\"] {\n\tif (!Array.isArray(organic)) return [];\n\tconst mapped: SearchResponse[\"results\"] = [];\n\tfor (const item of organic) {\n\t\tconst url = typeof item.link === \"string\" ? item.link.trim() : \"\";\n\t\tif (!url || !passesDomainFilters(url, filters)) continue;\n\t\tconst title = typeof item.title === \"string\" ? item.title.trim() : \"\";\n\t\tmapped.push({\n\t\t\ttitle: title || `Source ${mapped.length + 1}`,\n\t\t\turl,\n\t\t\tsnippet: typeof item.description === \"string\" ? item.description.replace(/\\s+/g, \" \").trim() : \"\",\n\t\t});\n\t\tif (mapped.length >= numResults) break;\n\t}\n\treturn mapped;\n}\n\n// A SERP zone returns ranked links, never a synthesized answer, so one is\n// assembled from the sources — the same shape brave.ts and searxng.ts produce.\nfunction buildAnswer(results: SearchResponse[\"results\"]): string {\n\treturn results\n\t\t.map((result) => result.snippet\n\t\t\t? `${result.snippet}\\nSource: ${result.title} (${result.url})`\n\t\t\t: `Source: ${result.title} (${result.url})`)\n\t\t.join(\"\\n\\n\");\n}\n\n// Both halves are required: a token with no zone cannot make a request, and a\n// zone with no token cannot either. Availability therefore checks the config this\n// surface actually needs rather than merely a key, the way firecrawl.ts's\n// `isFirecrawlAvailable()` checks its required base URL. Reporting \"unavailable\"\n// for a half-finished setup keeps the incomplete case out of provider selection\n// instead of turning it into a failed search.\n//\n// It is also a pure predicate that cannot throw. Callers evaluate it outside\n// their error handling — `gemini-search.ts` before the per-provider try/catch,\n// `index.ts` in the uncaught `getProviderAvailability()` await on the `web_search`\n// path — so any throw from a malformed Bright Data setting or an unparseable\n// config file would break search for every other provider too. Misconfiguration\n// makes this provider unavailable here; the request path reports it in detail.\nexport function isBrightDataAvailable(): boolean {\n\ttry {\n\t\tif (getSerpZone() === null) return false;\n\t\treturn hasCredentialSource({\n\t\t\tprovider: \"Bright Data\",\n\t\t\tconfiguredValue: loadConfig().brightdataApiKey,\n\t\t\tenvironmentValue: process.env.BRIGHTDATA_API_KEY,\n\t\t});\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport async function searchWithBrightData(query: string, options: BrightDataSearchOptions = {}): Promise<SearchResponse> {\n\t// Zone before key: a config mistake must not spawn a `!command` credential\n\t// resolver, and must not reach a billable endpoint.\n\tconst zone = requireSerpZone();\n\tconst apiKey = await requireApiKey(options.signal);\n\tconst numResults = normalizeCount(options.numResults);\n\tconst filters = parseDomainFilter(options.domainFilter);\n\tconst searchQuery = buildSearchQuery(query, filters);\n\tconst body: Record<string, unknown> = {\n\t\turl: buildSerpUrl(searchQuery, numResults, options.recencyFilter),\n\t\tzone,\n\t\t// `format: \"raw\"` returns the proxied body verbatim; `data_format` selects\n\t\t// Bright Data's own parsing of it. `parsed_light` is the SERP shape —\n\t\t// { organic: [{ link, title, description }] } — and carries no page bodies.\n\t\tformat: \"raw\",\n\t\tdata_format: \"parsed_light\",\n\t};\n\n\tconst activityId = activityMonitor.logStart({ type: \"api\", query: searchQuery });\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetch(BRIGHTDATA_API_URL, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"Authorization\": `Bearer ${apiKey}`,\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t\tbody: JSON.stringify(body),\n\t\t\tsignal: requestSignal(options.signal),\n\t\t});\n\t} catch (err) {\n\t\tconst message = errorMessage(err);\n\t\tconst redactedMessage = redactCredential(message, apiKey);\n\t\tif (redactedMessage.toLowerCase().includes(\"abort\")) activityMonitor.logComplete(activityId, 0);\n\t\telse activityMonitor.logError(activityId, redactedMessage);\n\t\tif (redactedMessage === message) throw err;\n\t\tconst redactedError = new Error(redactedMessage);\n\t\tif (err instanceof Error) redactedError.name = err.name;\n\t\tthrow redactedError;\n\t}\n\n\t// Every thrown message names the zone the request was billed against: with two\n\t// Bright Data zone types in play, \"which zone did I pay on\" is the first thing\n\t// a paid failure has to answer.\n\tif (!response.ok) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tconst errorText = untrustedText(redactCredential(await response.text(), apiKey));\n\t\tthrow new Error(`Bright Data API error ${response.status} for zone ${zone}: ${errorText}`);\n\t}\n\n\t// The body is read as text before parsing on purpose. `format: \"raw\"` means a\n\t// 200 can still carry an upstream interstitial instead of SERP JSON, and a\n\t// bare parser error (\"Unexpected token <\") hides a request that was billed.\n\t// Quoting the body is the only way the user learns what they paid for.\n\tconst raw = await response.text();\n\tif (!raw.trim()) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tthrow new Error(`Bright Data API returned empty response for zone ${zone}`);\n\t}\n\n\tlet rawData: unknown;\n\ttry {\n\t\trawData = JSON.parse(raw);\n\t} catch (err) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t// The parser's own message is deliberately NOT quoted here. V8 embeds a short\n\t\t// window of the offending input in it (`Unexpected token 'x', \"xxxxxxxx-x\"...`),\n\t\t// and that window is a *prefix* of the body — so when the body starts with the\n\t\t// credential, `redactCredential` cannot match it: it replaces the whole key, and\n\t\t// a 10-character prefix is not the whole key. Redacting the detail half therefore\n\t\t// still printed 10 cleartext characters of the token. The detail also adds nothing\n\t\t// this message does not already carry: the wording names the failure and the body\n\t\t// below shows what arrived, filtered. Dropping it removes the leak by construction\n\t\t// rather than by pattern-matching.\n\t\tconst body = untrustedText(redactCredential(raw, apiKey));\n\t\tthrow new Error(`Bright Data API returned invalid JSON for zone ${zone}: ${body}`);\n\t}\n\n\tlet data: BrightDataSerpResponse;\n\ttry {\n\t\tdata = parseSerpResponse(rawData, zone, apiKey);\n\t} catch (err) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tthrow err;\n\t}\n\n\tactivityMonitor.logComplete(activityId, response.status);\n\tconst results = mapResults(data.organic, numResults, filters);\n\treturn { answer: buildAnswer(results), results };\n}\n"]}
@@ -0,0 +1,10 @@
1
+ export type CookieMap = Record<string, string>;
2
+ export declare function getLastGoogleCookieDiagnostic(): string | null;
3
+ export declare function getGoogleCookies(options?: {
4
+ profile?: string;
5
+ requiredCookies?: string[];
6
+ }): Promise<{
7
+ cookies: CookieMap;
8
+ warnings: string[];
9
+ } | null>;
10
+ //# sourceMappingURL=chrome-cookies.d.ts.map