@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,298 @@
1
+ /**
2
+ * PDF Content Extractor
3
+ *
4
+ * Converts PDFs to Markdown. The `auto` provider chain runs Datalab
5
+ * (deterministic, layout-aware) first, then Gemini API, with unpdf as the
6
+ * deterministic local fallback; the chain can also be pinned with
7
+ * `pdf.provider`.
8
+ */
9
+ import { existsSync, readFileSync } from "node:fs";
10
+ import { writeFile, mkdir } from "node:fs/promises";
11
+ import { join, basename } from "node:path";
12
+ import { tmpdir } from "node:os";
13
+ import { CredentialResolutionError } from "./credential-source.js";
14
+ import { DATALAB_MODE_VALUES, DEFAULT_DATALAB_TIMEOUT_MS, isDatalabApiAvailable, normalizeDatalabMode, extractPDFViaDatalab, } from "./datalab-pdf-extract.js";
15
+ import { isGeminiApiAvailable } from "./gemini-api.js";
16
+ import { extractPDFViaGemini } from "./gemini-pdf-extract.js";
17
+ import { getWebSearchConfigPath } from "./utils.js";
18
+ export const PDF_PROVIDER_VALUES = new Set([
19
+ "auto",
20
+ "gemini",
21
+ "datalab",
22
+ "unpdf",
23
+ ]);
24
+ export const DEFAULT_PDF_MAX_SIZE_MB = 20;
25
+ export const MAX_PDF_MAX_SIZE_MB = 50;
26
+ export const MAX_DATALAB_TIMEOUT_MS = 300_000;
27
+ const DEFAULT_MAX_PAGES = 100;
28
+ const DEFAULT_OUTPUT_DIR = join(tmpdir(), "pi-web-pdf");
29
+ const CONFIG_PATH = getWebSearchConfigPath();
30
+ const PAGE_MARKER_PATTERN = /^<!-- Page (\d+) -->$/gm;
31
+ export function loadPDFConfig() {
32
+ if (!existsSync(CONFIG_PATH)) {
33
+ return {
34
+ enabled: true,
35
+ maxSizeMB: DEFAULT_PDF_MAX_SIZE_MB,
36
+ provider: "auto",
37
+ datalabMode: normalizeDatalabMode(process.env.DATALAB_MODE),
38
+ datalabTimeoutMs: DEFAULT_DATALAB_TIMEOUT_MS,
39
+ };
40
+ }
41
+ const rawText = readFileSync(CONFIG_PATH, "utf-8");
42
+ let raw;
43
+ try {
44
+ raw = JSON.parse(rawText);
45
+ }
46
+ catch (err) {
47
+ const message = err instanceof Error ? err.message : String(err);
48
+ throw new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);
49
+ }
50
+ const root = raw && typeof raw === "object" ? raw : {};
51
+ const pdf = root.pdf && typeof root.pdf === "object"
52
+ ? root.pdf
53
+ : {};
54
+ const enabled = pdf.enabled !== false;
55
+ const configured = pdf.maxSizeMB;
56
+ const normalized = typeof configured === "number" &&
57
+ Number.isFinite(configured) &&
58
+ configured > 0
59
+ ? Math.min(configured, MAX_PDF_MAX_SIZE_MB)
60
+ : DEFAULT_PDF_MAX_SIZE_MB;
61
+ const provider = typeof pdf.provider === "string" &&
62
+ PDF_PROVIDER_VALUES.has(pdf.provider)
63
+ ? pdf.provider
64
+ : "auto";
65
+ const datalabMode = typeof pdf.datalabMode === "string" &&
66
+ DATALAB_MODE_VALUES.has(pdf.datalabMode)
67
+ ? pdf.datalabMode
68
+ : normalizeDatalabMode(process.env.DATALAB_MODE);
69
+ const configuredTimeout = pdf.datalabTimeoutMs;
70
+ const datalabTimeoutMs = typeof configuredTimeout === "number" &&
71
+ Number.isFinite(configuredTimeout) &&
72
+ configuredTimeout > 0
73
+ ? Math.min(configuredTimeout, MAX_DATALAB_TIMEOUT_MS)
74
+ : DEFAULT_DATALAB_TIMEOUT_MS;
75
+ return {
76
+ enabled,
77
+ maxSizeMB: normalized,
78
+ provider,
79
+ datalabMode,
80
+ datalabTimeoutMs,
81
+ };
82
+ }
83
+ async function getUnpdf() {
84
+ if (typeof Promise.try !==
85
+ "function") {
86
+ const { default: promiseTry } = await import("promise.try");
87
+ promiseTry.shim();
88
+ }
89
+ const [unpdf, pdfjs] = await Promise.all([
90
+ import("unpdf"),
91
+ import("unpdf/pdfjs"),
92
+ ]);
93
+ const { VerbosityLevel } = pdfjs;
94
+ return { getDocumentProxy: unpdf.getDocumentProxy, VerbosityLevel };
95
+ }
96
+ /**
97
+ * Extract text from a PDF buffer and save it to a Markdown file.
98
+ */
99
+ export async function extractPDFToMarkdown(buffer, url, options = {}) {
100
+ const { maxPages = DEFAULT_MAX_PAGES, outputDir = DEFAULT_OUTPUT_DIR, filename, signal, geminiTimeoutMs, } = options;
101
+ const safeMaxPages = Number.isFinite(maxPages)
102
+ ? Math.max(1, Math.floor(maxPages))
103
+ : DEFAULT_MAX_PAGES;
104
+ const urlTitle = extractTitleFromURL(url);
105
+ const pdfConfig = loadPDFConfig();
106
+ const provider = pdfConfig.provider;
107
+ if (provider === "auto" || provider === "datalab") {
108
+ try {
109
+ if (isDatalabApiAvailable()) {
110
+ const result = await extractPDFViaDatalab(buffer, {
111
+ maxPages: safeMaxPages,
112
+ title: urlTitle,
113
+ mode: pdfConfig.datalabMode,
114
+ timeoutMs: pdfConfig.datalabTimeoutMs,
115
+ ...(signal ? { signal } : {}),
116
+ });
117
+ return writeMarkdownResult({
118
+ markdownBody: result.markdown,
119
+ title: urlTitle,
120
+ pages: result.pages,
121
+ outputDir,
122
+ filename,
123
+ url,
124
+ });
125
+ }
126
+ }
127
+ catch (err) {
128
+ if (shouldRethrowExtractionError(err, signal))
129
+ throw err;
130
+ }
131
+ }
132
+ if (provider === "auto" || provider === "gemini") {
133
+ try {
134
+ if (isGeminiApiAvailable()) {
135
+ const markdownBody = await extractPDFViaGemini(buffer, {
136
+ maxPages: safeMaxPages,
137
+ title: urlTitle,
138
+ ...(signal ? { signal } : {}),
139
+ ...(geminiTimeoutMs !== undefined
140
+ ? { timeoutMs: geminiTimeoutMs }
141
+ : {}),
142
+ });
143
+ return writeMarkdownResult({
144
+ markdownBody,
145
+ title: urlTitle,
146
+ pages: countPageMarkers(markdownBody),
147
+ outputDir,
148
+ filename,
149
+ url,
150
+ });
151
+ }
152
+ }
153
+ catch (err) {
154
+ if (shouldRethrowExtractionError(err, signal))
155
+ throw err;
156
+ }
157
+ }
158
+ const { getDocumentProxy, VerbosityLevel } = await getUnpdf();
159
+ const pdf = await getDocumentProxy(new Uint8Array(buffer), {
160
+ verbosity: VerbosityLevel.ERRORS,
161
+ });
162
+ const metadata = await pdf.getMetadata();
163
+ const metadataInfo = metadata.info && typeof metadata.info === "object"
164
+ ? metadata.info
165
+ : null;
166
+ const metaTitle = typeof metadataInfo?.Title === "string" ? metadataInfo.Title : undefined;
167
+ const metaAuthor = typeof metadataInfo?.Author === "string" ? metadataInfo.Author : undefined;
168
+ const title = metaTitle?.trim() || urlTitle;
169
+ const pagesToExtract = Math.min(pdf.numPages, safeMaxPages);
170
+ const truncated = pdf.numPages > safeMaxPages;
171
+ const pages = [];
172
+ for (let i = 1; i <= pagesToExtract; i++) {
173
+ const page = await pdf.getPage(i);
174
+ const textContent = await page.getTextContent();
175
+ const pageText = textContent.items
176
+ .map((item) => {
177
+ const textItem = item;
178
+ return textItem.str || "";
179
+ })
180
+ .join(" ")
181
+ .replace(/\s+/g, " ")
182
+ .trim();
183
+ if (pageText) {
184
+ pages.push({ pageNum: i, text: pageText });
185
+ }
186
+ }
187
+ const bodyLines = [];
188
+ for (let i = 0; i < pages.length; i++) {
189
+ if (i > 0) {
190
+ bodyLines.push("");
191
+ bodyLines.push(`<!-- Page ${pages[i].pageNum} -->`);
192
+ bodyLines.push("");
193
+ }
194
+ bodyLines.push(pages[i].text);
195
+ }
196
+ return writeMarkdownResult({
197
+ markdownBody: bodyLines.join("\n"),
198
+ title,
199
+ pages: pdf.numPages,
200
+ outputDir,
201
+ filename,
202
+ url,
203
+ metaAuthor,
204
+ truncated,
205
+ pagesToExtract,
206
+ });
207
+ }
208
+ async function writeMarkdownResult(options) {
209
+ const lines = [];
210
+ lines.push(`# ${options.title}`);
211
+ lines.push("");
212
+ lines.push(`> Source: ${options.url}`);
213
+ lines.push(`> Pages: ${options.pages}${options.truncated ? ` (extracted first ${options.pagesToExtract})` : ""}`);
214
+ if (options.metaAuthor)
215
+ lines.push(`> Author: ${options.metaAuthor}`);
216
+ lines.push("");
217
+ lines.push("---");
218
+ lines.push("");
219
+ if (options.markdownBody)
220
+ lines.push(options.markdownBody);
221
+ if (options.truncated) {
222
+ lines.push("");
223
+ lines.push("---");
224
+ lines.push("");
225
+ lines.push(`*[Truncated: Only first ${options.pagesToExtract} of ${options.pages} pages extracted]*`);
226
+ }
227
+ const content = lines.join("\n");
228
+ const outputFilename = options.filename || sanitizeFilename(options.title) + ".md";
229
+ const outputPath = join(options.outputDir, outputFilename);
230
+ await mkdir(options.outputDir, { recursive: true });
231
+ await writeFile(outputPath, content, "utf-8");
232
+ return {
233
+ title: options.title,
234
+ pages: options.pages,
235
+ chars: content.length,
236
+ outputPath,
237
+ };
238
+ }
239
+ function countPageMarkers(markdown) {
240
+ return [...markdown.matchAll(PAGE_MARKER_PATTERN)].length;
241
+ }
242
+ function shouldRethrowExtractionError(err, signal) {
243
+ if (signal?.aborted)
244
+ return true;
245
+ if (err instanceof CredentialResolutionError)
246
+ return true;
247
+ const message = err instanceof Error ? err.message : String(err);
248
+ return message.startsWith("Failed to parse ");
249
+ }
250
+ /**
251
+ * Extract a reasonable title from URL
252
+ */
253
+ function extractTitleFromURL(url) {
254
+ try {
255
+ const urlObj = new URL(url);
256
+ const pathname = urlObj.pathname;
257
+ let filename = basename(pathname, ".pdf");
258
+ if (urlObj.hostname.includes("arxiv.org")) {
259
+ const match = pathname.match(/\/(?:pdf|abs)\/(\d+\.\d+)/);
260
+ if (match) {
261
+ filename = `arxiv-${match[1]}`;
262
+ }
263
+ }
264
+ filename = filename.replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim();
265
+ return filename || "document";
266
+ }
267
+ catch {
268
+ return "document";
269
+ }
270
+ }
271
+ /**
272
+ * Sanitize string for use as filename
273
+ */
274
+ function sanitizeFilename(name) {
275
+ return (name
276
+ .toLowerCase()
277
+ .replace(/[^a-z0-9\s-]/g, "")
278
+ .replace(/\s+/g, "-")
279
+ .replace(/-+/g, "-")
280
+ .slice(0, 100)
281
+ .replace(/^-|-$/g, "") || "document");
282
+ }
283
+ /**
284
+ * Check if URL or content-type indicates a PDF
285
+ */
286
+ export function isPDF(url, contentType) {
287
+ if (contentType?.includes("application/pdf")) {
288
+ return true;
289
+ }
290
+ try {
291
+ const urlObj = new URL(url);
292
+ return urlObj.pathname.toLowerCase().endsWith(".pdf");
293
+ }
294
+ catch {
295
+ return false;
296
+ }
297
+ }
298
+ //# sourceMappingURL=pdf-extract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf-extract.js","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/pdf-extract.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * PDF Content Extractor\n *\n * Converts PDFs to Markdown. The `auto` provider chain runs Datalab\n * (deterministic, layout-aware) first, then Gemini API, with unpdf as the\n * deterministic local fallback; the chain can also be pinned with\n * `pdf.provider`.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { writeFile, mkdir } from \"node:fs/promises\";\nimport { join, basename } from \"node:path\";\nimport { tmpdir } from \"node:os\";\nimport { CredentialResolutionError } from \"./credential-source.ts\";\nimport {\n\tDATALAB_MODE_VALUES,\n\tDEFAULT_DATALAB_TIMEOUT_MS,\n\tisDatalabApiAvailable,\n\tnormalizeDatalabMode,\n\textractPDFViaDatalab,\n\ttype DatalabMode,\n} from \"./datalab-pdf-extract.ts\";\nimport { isGeminiApiAvailable } from \"./gemini-api.ts\";\nimport { extractPDFViaGemini } from \"./gemini-pdf-extract.ts\";\nimport { getWebSearchConfigPath } from \"./utils.ts\";\n\nexport interface PDFExtractResult {\n\ttitle: string;\n\tpages: number;\n\tchars: number;\n\toutputPath: string;\n}\n\nexport interface PDFExtractOptions {\n\tmaxPages?: number;\n\toutputDir?: string;\n\tfilename?: string;\n\tsignal?: AbortSignal;\n\tgeminiTimeoutMs?: number;\n}\n\nexport type PDFProvider = \"auto\" | \"gemini\" | \"datalab\" | \"unpdf\";\n\nexport const PDF_PROVIDER_VALUES = new Set<PDFProvider>([\n\t\"auto\",\n\t\"gemini\",\n\t\"datalab\",\n\t\"unpdf\",\n]);\n\nexport interface PDFConfig {\n\tenabled: boolean;\n\tmaxSizeMB: number;\n\tprovider: PDFProvider;\n\tdatalabMode: DatalabMode;\n\tdatalabTimeoutMs: number;\n}\n\nexport const DEFAULT_PDF_MAX_SIZE_MB = 20;\nexport const MAX_PDF_MAX_SIZE_MB = 50;\nexport const MAX_DATALAB_TIMEOUT_MS = 300_000;\nconst DEFAULT_MAX_PAGES = 100;\nconst DEFAULT_OUTPUT_DIR = join(tmpdir(), \"pi-web-pdf\");\nconst CONFIG_PATH = getWebSearchConfigPath();\nconst PAGE_MARKER_PATTERN = /^<!-- Page (\\d+) -->$/gm;\n\nexport function loadPDFConfig(): PDFConfig {\n\tif (!existsSync(CONFIG_PATH)) {\n\t\treturn {\n\t\t\tenabled: true,\n\t\t\tmaxSizeMB: DEFAULT_PDF_MAX_SIZE_MB,\n\t\t\tprovider: \"auto\",\n\t\t\tdatalabMode: normalizeDatalabMode(process.env.DATALAB_MODE),\n\t\t\tdatalabTimeoutMs: DEFAULT_DATALAB_TIMEOUT_MS,\n\t\t};\n\t}\n\n\tconst rawText = readFileSync(CONFIG_PATH, \"utf-8\");\n\tlet raw: unknown;\n\ttry {\n\t\traw = JSON.parse(rawText) as unknown;\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tthrow new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);\n\t}\n\n\tconst root =\n\t\traw && typeof raw === \"object\" ? (raw as Record<string, unknown>) : {};\n\tconst pdf =\n\t\troot.pdf && typeof root.pdf === \"object\"\n\t\t\t? (root.pdf as Record<string, unknown>)\n\t\t\t: {};\n\tconst enabled = pdf.enabled !== false;\n\tconst configured = pdf.maxSizeMB;\n\tconst normalized =\n\t\ttypeof configured === \"number\" &&\n\t\tNumber.isFinite(configured) &&\n\t\tconfigured > 0\n\t\t\t? Math.min(configured, MAX_PDF_MAX_SIZE_MB)\n\t\t\t: DEFAULT_PDF_MAX_SIZE_MB;\n\n\tconst provider =\n\t\ttypeof pdf.provider === \"string\" &&\n\t\tPDF_PROVIDER_VALUES.has(pdf.provider as PDFProvider)\n\t\t\t? (pdf.provider as PDFProvider)\n\t\t\t: \"auto\";\n\tconst datalabMode =\n\t\ttypeof pdf.datalabMode === \"string\" &&\n\t\tDATALAB_MODE_VALUES.has(pdf.datalabMode as DatalabMode)\n\t\t\t? (pdf.datalabMode as DatalabMode)\n\t\t\t: normalizeDatalabMode(process.env.DATALAB_MODE);\n\tconst configuredTimeout = pdf.datalabTimeoutMs;\n\tconst datalabTimeoutMs =\n\t\ttypeof configuredTimeout === \"number\" &&\n\t\tNumber.isFinite(configuredTimeout) &&\n\t\tconfiguredTimeout > 0\n\t\t\t? Math.min(configuredTimeout, MAX_DATALAB_TIMEOUT_MS)\n\t\t\t: DEFAULT_DATALAB_TIMEOUT_MS;\n\n\treturn {\n\t\tenabled,\n\t\tmaxSizeMB: normalized,\n\t\tprovider,\n\t\tdatalabMode,\n\t\tdatalabTimeoutMs,\n\t};\n}\n\nasync function getUnpdf() {\n\tif (\n\t\ttypeof (Promise as PromiseConstructor & { try?: unknown }).try !==\n\t\t\"function\"\n\t) {\n\t\tconst { default: promiseTry } = await import(\"promise.try\");\n\t\tpromiseTry.shim();\n\t}\n\n\tconst [unpdf, pdfjs] = await Promise.all([\n\t\timport(\"unpdf\"),\n\t\timport(\"unpdf/pdfjs\"),\n\t]);\n\tconst { VerbosityLevel } = pdfjs as typeof pdfjs & {\n\t\tVerbosityLevel: { ERRORS: number };\n\t};\n\n\treturn { getDocumentProxy: unpdf.getDocumentProxy, VerbosityLevel };\n}\n\n/**\n * Extract text from a PDF buffer and save it to a Markdown file.\n */\nexport async function extractPDFToMarkdown(\n\tbuffer: ArrayBuffer,\n\turl: string,\n\toptions: PDFExtractOptions = {},\n): Promise<PDFExtractResult> {\n\tconst {\n\t\tmaxPages = DEFAULT_MAX_PAGES,\n\t\toutputDir = DEFAULT_OUTPUT_DIR,\n\t\tfilename,\n\t\tsignal,\n\t\tgeminiTimeoutMs,\n\t} = options;\n\n\tconst safeMaxPages = Number.isFinite(maxPages)\n\t\t? Math.max(1, Math.floor(maxPages))\n\t\t: DEFAULT_MAX_PAGES;\n\tconst urlTitle = extractTitleFromURL(url);\n\tconst pdfConfig = loadPDFConfig();\n\tconst provider = pdfConfig.provider;\n\n\tif (provider === \"auto\" || provider === \"datalab\") {\n\t\ttry {\n\t\t\tif (isDatalabApiAvailable()) {\n\t\t\t\tconst result = await extractPDFViaDatalab(buffer, {\n\t\t\t\t\tmaxPages: safeMaxPages,\n\t\t\t\t\ttitle: urlTitle,\n\t\t\t\t\tmode: pdfConfig.datalabMode,\n\t\t\t\t\ttimeoutMs: pdfConfig.datalabTimeoutMs,\n\t\t\t\t\t...(signal ? { signal } : {}),\n\t\t\t\t});\n\t\t\t\treturn writeMarkdownResult({\n\t\t\t\t\tmarkdownBody: result.markdown,\n\t\t\t\t\ttitle: urlTitle,\n\t\t\t\t\tpages: result.pages,\n\t\t\t\t\toutputDir,\n\t\t\t\t\tfilename,\n\t\t\t\t\turl,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tif (shouldRethrowExtractionError(err, signal)) throw err;\n\t\t}\n\t}\n\n\tif (provider === \"auto\" || provider === \"gemini\") {\n\t\ttry {\n\t\t\tif (isGeminiApiAvailable()) {\n\t\t\t\tconst markdownBody = await extractPDFViaGemini(buffer, {\n\t\t\t\t\tmaxPages: safeMaxPages,\n\t\t\t\t\ttitle: urlTitle,\n\t\t\t\t\t...(signal ? { signal } : {}),\n\t\t\t\t\t...(geminiTimeoutMs !== undefined\n\t\t\t\t\t\t? { timeoutMs: geminiTimeoutMs }\n\t\t\t\t\t\t: {}),\n\t\t\t\t});\n\t\t\t\treturn writeMarkdownResult({\n\t\t\t\t\tmarkdownBody,\n\t\t\t\t\ttitle: urlTitle,\n\t\t\t\t\tpages: countPageMarkers(markdownBody),\n\t\t\t\t\toutputDir,\n\t\t\t\t\tfilename,\n\t\t\t\t\turl,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tif (shouldRethrowExtractionError(err, signal)) throw err;\n\t\t}\n\t}\n\n\tconst { getDocumentProxy, VerbosityLevel } = await getUnpdf();\n\tconst pdf = await getDocumentProxy(new Uint8Array(buffer), {\n\t\tverbosity: VerbosityLevel.ERRORS,\n\t});\n\tconst metadata = await pdf.getMetadata();\n\tconst metadataInfo =\n\t\tmetadata.info && typeof metadata.info === \"object\"\n\t\t\t? (metadata.info as Record<string, unknown>)\n\t\t\t: null;\n\n\tconst metaTitle =\n\t\ttypeof metadataInfo?.Title === \"string\" ? metadataInfo.Title : undefined;\n\tconst metaAuthor =\n\t\ttypeof metadataInfo?.Author === \"string\" ? metadataInfo.Author : undefined;\n\tconst title = metaTitle?.trim() || urlTitle;\n\tconst pagesToExtract = Math.min(pdf.numPages, safeMaxPages);\n\tconst truncated = pdf.numPages > safeMaxPages;\n\tconst pages: { pageNum: number; text: string }[] = [];\n\n\tfor (let i = 1; i <= pagesToExtract; i++) {\n\t\tconst page = await pdf.getPage(i);\n\t\tconst textContent = await page.getTextContent();\n\t\tconst pageText = textContent.items\n\t\t\t.map((item: unknown) => {\n\t\t\t\tconst textItem = item as { str?: string };\n\t\t\t\treturn textItem.str || \"\";\n\t\t\t})\n\t\t\t.join(\" \")\n\t\t\t.replace(/\\s+/g, \" \")\n\t\t\t.trim();\n\n\t\tif (pageText) {\n\t\t\tpages.push({ pageNum: i, text: pageText });\n\t\t}\n\t}\n\n\tconst bodyLines: string[] = [];\n\tfor (let i = 0; i < pages.length; i++) {\n\t\tif (i > 0) {\n\t\t\tbodyLines.push(\"\");\n\t\t\tbodyLines.push(`<!-- Page ${pages[i].pageNum} -->`);\n\t\t\tbodyLines.push(\"\");\n\t\t}\n\t\tbodyLines.push(pages[i].text);\n\t}\n\n\treturn writeMarkdownResult({\n\t\tmarkdownBody: bodyLines.join(\"\\n\"),\n\t\ttitle,\n\t\tpages: pdf.numPages,\n\t\toutputDir,\n\t\tfilename,\n\t\turl,\n\t\tmetaAuthor,\n\t\ttruncated,\n\t\tpagesToExtract,\n\t});\n}\n\nasync function writeMarkdownResult(options: {\n\tmarkdownBody: string;\n\ttitle: string;\n\tpages: number;\n\toutputDir: string;\n\tfilename?: string;\n\turl: string;\n\tmetaAuthor?: string;\n\ttruncated?: boolean;\n\tpagesToExtract?: number;\n}): Promise<PDFExtractResult> {\n\tconst lines: string[] = [];\n\tlines.push(`# ${options.title}`);\n\tlines.push(\"\");\n\tlines.push(`> Source: ${options.url}`);\n\tlines.push(\n\t\t`> Pages: ${options.pages}${options.truncated ? ` (extracted first ${options.pagesToExtract})` : \"\"}`,\n\t);\n\tif (options.metaAuthor) lines.push(`> Author: ${options.metaAuthor}`);\n\tlines.push(\"\");\n\tlines.push(\"---\");\n\tlines.push(\"\");\n\tif (options.markdownBody) lines.push(options.markdownBody);\n\n\tif (options.truncated) {\n\t\tlines.push(\"\");\n\t\tlines.push(\"---\");\n\t\tlines.push(\"\");\n\t\tlines.push(\n\t\t\t`*[Truncated: Only first ${options.pagesToExtract} of ${options.pages} pages extracted]*`,\n\t\t);\n\t}\n\n\tconst content = lines.join(\"\\n\");\n\tconst outputFilename =\n\t\toptions.filename || sanitizeFilename(options.title) + \".md\";\n\tconst outputPath = join(options.outputDir, outputFilename);\n\n\tawait mkdir(options.outputDir, { recursive: true });\n\tawait writeFile(outputPath, content, \"utf-8\");\n\n\treturn {\n\t\ttitle: options.title,\n\t\tpages: options.pages,\n\t\tchars: content.length,\n\t\toutputPath,\n\t};\n}\n\nfunction countPageMarkers(markdown: string): number {\n\treturn [...markdown.matchAll(PAGE_MARKER_PATTERN)].length;\n}\n\nfunction shouldRethrowExtractionError(\n\terr: unknown,\n\tsignal?: AbortSignal,\n): boolean {\n\tif (signal?.aborted) return true;\n\tif (err instanceof CredentialResolutionError) return true;\n\tconst message = err instanceof Error ? err.message : String(err);\n\treturn message.startsWith(\"Failed to parse \");\n}\n\n/**\n * Extract a reasonable title from URL\n */\nfunction extractTitleFromURL(url: string): string {\n\ttry {\n\t\tconst urlObj = new URL(url);\n\t\tconst pathname = urlObj.pathname;\n\n\t\tlet filename = basename(pathname, \".pdf\");\n\n\t\tif (urlObj.hostname.includes(\"arxiv.org\")) {\n\t\t\tconst match = pathname.match(/\\/(?:pdf|abs)\\/(\\d+\\.\\d+)/);\n\t\t\tif (match) {\n\t\t\t\tfilename = `arxiv-${match[1]}`;\n\t\t\t}\n\t\t}\n\n\t\tfilename = filename.replace(/[_-]+/g, \" \").replace(/\\s+/g, \" \").trim();\n\n\t\treturn filename || \"document\";\n\t} catch {\n\t\treturn \"document\";\n\t}\n}\n\n/**\n * Sanitize string for use as filename\n */\nfunction sanitizeFilename(name: string): string {\n\treturn (\n\t\tname\n\t\t\t.toLowerCase()\n\t\t\t.replace(/[^a-z0-9\\s-]/g, \"\")\n\t\t\t.replace(/\\s+/g, \"-\")\n\t\t\t.replace(/-+/g, \"-\")\n\t\t\t.slice(0, 100)\n\t\t\t.replace(/^-|-$/g, \"\") || \"document\"\n\t);\n}\n\n/**\n * Check if URL or content-type indicates a PDF\n */\nexport function isPDF(url: string, contentType?: string): boolean {\n\tif (contentType?.includes(\"application/pdf\")) {\n\t\treturn true;\n\t}\n\ttry {\n\t\tconst urlObj = new URL(url);\n\t\treturn urlObj.pathname.toLowerCase().endsWith(\".pdf\");\n\t} catch {\n\t\treturn false;\n\t}\n}\n"]}
@@ -0,0 +1,20 @@
1
+ import type { ExtractedContent } from "./extract.ts";
2
+ export interface SearchResult {
3
+ title: string;
4
+ url: string;
5
+ snippet: string;
6
+ }
7
+ export interface SearchResponse {
8
+ answer: string;
9
+ results: SearchResult[];
10
+ inlineContent?: ExtractedContent[];
11
+ }
12
+ export interface SearchOptions {
13
+ numResults?: number;
14
+ recencyFilter?: "day" | "week" | "month" | "year";
15
+ domainFilter?: string[];
16
+ signal?: AbortSignal;
17
+ }
18
+ export declare function isPerplexityAvailable(): boolean;
19
+ export declare function searchWithPerplexity(query: string, options?: SearchOptions): Promise<SearchResponse>;
20
+ //# sourceMappingURL=perplexity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perplexity.d.ts","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/perplexity.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAcrD,MAAM,WAAW,YAAY;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,aAAa,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,aAAa;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IAClD,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAkED,wBAAgB,qBAAqB,IAAI,OAAO,CAM/C;AAED,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,cAAc,CAAC,CA8F9G","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { activityMonitor } from \"./activity.ts\";\nimport type { ExtractedContent } from \"./extract.ts\";\nimport { hasCredentialSource, redactCredential, resolveCredential } from \"./credential-source.ts\";\nimport { getWebSearchConfigPath } from \"./utils.ts\";\n\nconst PERPLEXITY_API_URL = \"https://api.perplexity.ai/chat/completions\";\nconst CONFIG_PATH = getWebSearchConfigPath();\n\nconst RATE_LIMIT = {\n\tmaxRequests: 10,\n\twindowMs: 60 * 1000,\n};\n\nconst requestTimestamps: number[] = [];\n\nexport interface SearchResult {\n\ttitle: string;\n\turl: string;\n\tsnippet: string;\n}\n\nexport interface SearchResponse {\n\tanswer: string;\n\tresults: SearchResult[];\n\tinlineContent?: ExtractedContent[];\n}\n\nexport interface SearchOptions {\n\tnumResults?: number;\n\trecencyFilter?: \"day\" | \"week\" | \"month\" | \"year\";\n\tdomainFilter?: string[];\n\tsignal?: AbortSignal;\n}\n\ninterface WebSearchConfig {\n\tperplexityApiKey?: unknown;\n}\n\nlet cachedConfig: WebSearchConfig | null = null;\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 content = readFileSync(CONFIG_PATH, \"utf-8\");\n\ttry {\n\t\tcachedConfig = JSON.parse(content) as WebSearchConfig;\n\t\treturn cachedConfig;\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tthrow new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);\n\t}\n}\n\nasync function getApiKey(signal?: AbortSignal): Promise<string> {\n\tconst key = await resolveCredential({\n\t\tprovider: \"Perplexity\",\n\t\tconfiguredValue: loadConfig().perplexityApiKey,\n\t\tenvironmentValue: process.env.PERPLEXITY_API_KEY,\n\t\tsignal,\n\t});\n\tif (!key) {\n\t\tthrow new Error(\n\t\t\t\"Perplexity API key not found. Either:\\n\" +\n\t\t\t` 1. Create ${CONFIG_PATH} with { \"perplexityApiKey\": \"your-key\" }\\n` +\n\t\t\t\" 2. Set PERPLEXITY_API_KEY environment variable\\n\" +\n\t\t\t\"Get a key at https://perplexity.ai/settings/api\"\n\t\t);\n\t}\n\treturn key;\n}\n\nfunction checkRateLimit(): void {\n\tconst now = Date.now();\n\tconst windowStart = now - RATE_LIMIT.windowMs;\n\n\twhile (requestTimestamps.length > 0 && requestTimestamps[0] < windowStart) {\n\t\trequestTimestamps.shift();\n\t}\n\n\tif (requestTimestamps.length >= RATE_LIMIT.maxRequests) {\n\t\tconst waitMs = requestTimestamps[0] + RATE_LIMIT.windowMs - now;\n\t\tthrow new Error(`Rate limited. Try again in ${Math.ceil(waitMs / 1000)}s`);\n\t}\n\n\trequestTimestamps.push(now);\n}\n\nfunction validateDomainFilter(domains: string[]): string[] {\n\treturn domains.filter((d) => {\n\t\tconst domain = d.startsWith(\"-\") ? d.slice(1) : d;\n\t\treturn /^[a-zA-Z0-9][a-zA-Z0-9-_.]*\\.[a-zA-Z]{2,}$/.test(domain);\n\t});\n}\n\nexport function isPerplexityAvailable(): boolean {\n\treturn hasCredentialSource({\n\t\tprovider: \"Perplexity\",\n\t\tconfiguredValue: loadConfig().perplexityApiKey,\n\t\tenvironmentValue: process.env.PERPLEXITY_API_KEY,\n\t});\n}\n\nexport async function searchWithPerplexity(query: string, options: SearchOptions = {}): Promise<SearchResponse> {\n\tcheckRateLimit();\n\n\tconst activityId = activityMonitor.logStart({ type: \"api\", query });\n\n\tactivityMonitor.updateRateLimit({\n\t\tused: requestTimestamps.length,\n\t\tmax: RATE_LIMIT.maxRequests,\n\t\toldestTimestamp: requestTimestamps[0] ?? null,\n\t\twindowMs: RATE_LIMIT.windowMs,\n\t});\n\n\tconst apiKey = await getApiKey(options.signal);\n\tconst numResults = typeof options.numResults === \"number\" && Number.isFinite(options.numResults)\n\t\t? Math.max(1, Math.min(Math.floor(options.numResults), 20))\n\t\t: 5;\n\n\tconst requestBody: Record<string, unknown> = {\n\t\tmodel: \"sonar\",\n\t\tmessages: [{ role: \"user\", content: query }],\n\t\tmax_tokens: 1024,\n\t\treturn_related_questions: false,\n\t};\n\n\tif (options.recencyFilter) {\n\t\trequestBody.search_recency_filter = options.recencyFilter;\n\t}\n\n\tif (options.domainFilter && options.domainFilter.length > 0) {\n\t\tconst validated = validateDomainFilter(options.domainFilter);\n\t\tif (validated.length > 0) {\n\t\t\trequestBody.search_domain_filter = validated;\n\t\t}\n\t}\n\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetch(PERPLEXITY_API_URL, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${apiKey}`,\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t\tbody: JSON.stringify(requestBody),\n\t\t\t...(options.signal ? { signal: options.signal } : {}),\n\t\t});\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tconst redactedMessage = redactCredential(message, apiKey);\n\t\tif (redactedMessage.toLowerCase().includes(\"abort\")) {\n\t\t\tactivityMonitor.logComplete(activityId, 0);\n\t\t} else {\n\t\t\tactivityMonitor.logError(activityId, redactedMessage);\n\t\t}\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\tif (!response.ok) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tconst errorText = redactCredential(await response.text(), apiKey);\n\t\tthrow new Error(`Perplexity API error ${response.status}: ${errorText}`);\n\t}\n\n\tlet data: Record<string, unknown>;\n\ttry {\n\t\tdata = (await response.json()) as Record<string, unknown>;\n\t} catch (err) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tthrow new Error(`Perplexity API returned invalid JSON: ${message}`);\n\t}\n\n\tconst answer = (data.choices as Array<{ message?: { content?: string } }>)?.[0]?.message?.content || \"\";\n\tconst citations = Array.isArray(data.citations) ? data.citations : [];\n\n\tconst results: SearchResult[] = [];\n\tfor (let i = 0; i < Math.min(citations.length, numResults); i++) {\n\t\tconst citation = citations[i];\n\t\tif (typeof citation === \"string\") {\n\t\t\tresults.push({ title: `Source ${i + 1}`, url: citation, snippet: \"\" });\n\t\t} else if (citation && typeof citation === \"object\" && typeof citation.url === \"string\") {\n\t\t\tresults.push({\n\t\t\t\ttitle: citation.title || `Source ${i + 1}`,\n\t\t\t\turl: citation.url,\n\t\t\t\tsnippet: \"\",\n\t\t\t});\n\t\t}\n\t}\n\n\tactivityMonitor.logComplete(activityId, response.status);\n\treturn { answer, results };\n}\n"]}
@@ -0,0 +1,159 @@
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 PERPLEXITY_API_URL = "https://api.perplexity.ai/chat/completions";
6
+ const CONFIG_PATH = getWebSearchConfigPath();
7
+ const RATE_LIMIT = {
8
+ maxRequests: 10,
9
+ windowMs: 60 * 1000,
10
+ };
11
+ const requestTimestamps = [];
12
+ let cachedConfig = null;
13
+ function loadConfig() {
14
+ if (cachedConfig)
15
+ return cachedConfig;
16
+ if (!existsSync(CONFIG_PATH)) {
17
+ cachedConfig = {};
18
+ return cachedConfig;
19
+ }
20
+ const content = readFileSync(CONFIG_PATH, "utf-8");
21
+ try {
22
+ cachedConfig = JSON.parse(content);
23
+ return cachedConfig;
24
+ }
25
+ catch (err) {
26
+ const message = err instanceof Error ? err.message : String(err);
27
+ throw new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);
28
+ }
29
+ }
30
+ async function getApiKey(signal) {
31
+ const key = await resolveCredential({
32
+ provider: "Perplexity",
33
+ configuredValue: loadConfig().perplexityApiKey,
34
+ environmentValue: process.env.PERPLEXITY_API_KEY,
35
+ signal,
36
+ });
37
+ if (!key) {
38
+ throw new Error("Perplexity API key not found. Either:\n" +
39
+ ` 1. Create ${CONFIG_PATH} with { "perplexityApiKey": "your-key" }\n` +
40
+ " 2. Set PERPLEXITY_API_KEY environment variable\n" +
41
+ "Get a key at https://perplexity.ai/settings/api");
42
+ }
43
+ return key;
44
+ }
45
+ function checkRateLimit() {
46
+ const now = Date.now();
47
+ const windowStart = now - RATE_LIMIT.windowMs;
48
+ while (requestTimestamps.length > 0 && requestTimestamps[0] < windowStart) {
49
+ requestTimestamps.shift();
50
+ }
51
+ if (requestTimestamps.length >= RATE_LIMIT.maxRequests) {
52
+ const waitMs = requestTimestamps[0] + RATE_LIMIT.windowMs - now;
53
+ throw new Error(`Rate limited. Try again in ${Math.ceil(waitMs / 1000)}s`);
54
+ }
55
+ requestTimestamps.push(now);
56
+ }
57
+ function validateDomainFilter(domains) {
58
+ return domains.filter((d) => {
59
+ const domain = d.startsWith("-") ? d.slice(1) : d;
60
+ return /^[a-zA-Z0-9][a-zA-Z0-9-_.]*\.[a-zA-Z]{2,}$/.test(domain);
61
+ });
62
+ }
63
+ export function isPerplexityAvailable() {
64
+ return hasCredentialSource({
65
+ provider: "Perplexity",
66
+ configuredValue: loadConfig().perplexityApiKey,
67
+ environmentValue: process.env.PERPLEXITY_API_KEY,
68
+ });
69
+ }
70
+ export async function searchWithPerplexity(query, options = {}) {
71
+ checkRateLimit();
72
+ const activityId = activityMonitor.logStart({ type: "api", query });
73
+ activityMonitor.updateRateLimit({
74
+ used: requestTimestamps.length,
75
+ max: RATE_LIMIT.maxRequests,
76
+ oldestTimestamp: requestTimestamps[0] ?? null,
77
+ windowMs: RATE_LIMIT.windowMs,
78
+ });
79
+ const apiKey = await getApiKey(options.signal);
80
+ const numResults = typeof options.numResults === "number" && Number.isFinite(options.numResults)
81
+ ? Math.max(1, Math.min(Math.floor(options.numResults), 20))
82
+ : 5;
83
+ const requestBody = {
84
+ model: "sonar",
85
+ messages: [{ role: "user", content: query }],
86
+ max_tokens: 1024,
87
+ return_related_questions: false,
88
+ };
89
+ if (options.recencyFilter) {
90
+ requestBody.search_recency_filter = options.recencyFilter;
91
+ }
92
+ if (options.domainFilter && options.domainFilter.length > 0) {
93
+ const validated = validateDomainFilter(options.domainFilter);
94
+ if (validated.length > 0) {
95
+ requestBody.search_domain_filter = validated;
96
+ }
97
+ }
98
+ let response;
99
+ try {
100
+ response = await fetch(PERPLEXITY_API_URL, {
101
+ method: "POST",
102
+ headers: {
103
+ Authorization: `Bearer ${apiKey}`,
104
+ "Content-Type": "application/json",
105
+ },
106
+ body: JSON.stringify(requestBody),
107
+ ...(options.signal ? { signal: options.signal } : {}),
108
+ });
109
+ }
110
+ catch (err) {
111
+ const message = err instanceof Error ? err.message : String(err);
112
+ const redactedMessage = redactCredential(message, apiKey);
113
+ if (redactedMessage.toLowerCase().includes("abort")) {
114
+ activityMonitor.logComplete(activityId, 0);
115
+ }
116
+ else {
117
+ activityMonitor.logError(activityId, redactedMessage);
118
+ }
119
+ if (redactedMessage === message)
120
+ throw err;
121
+ const redactedError = new Error(redactedMessage);
122
+ if (err instanceof Error)
123
+ redactedError.name = err.name;
124
+ throw redactedError;
125
+ }
126
+ if (!response.ok) {
127
+ activityMonitor.logComplete(activityId, response.status);
128
+ const errorText = redactCredential(await response.text(), apiKey);
129
+ throw new Error(`Perplexity API error ${response.status}: ${errorText}`);
130
+ }
131
+ let data;
132
+ try {
133
+ data = (await response.json());
134
+ }
135
+ catch (err) {
136
+ activityMonitor.logComplete(activityId, response.status);
137
+ const message = err instanceof Error ? err.message : String(err);
138
+ throw new Error(`Perplexity API returned invalid JSON: ${message}`);
139
+ }
140
+ const answer = data.choices?.[0]?.message?.content || "";
141
+ const citations = Array.isArray(data.citations) ? data.citations : [];
142
+ const results = [];
143
+ for (let i = 0; i < Math.min(citations.length, numResults); i++) {
144
+ const citation = citations[i];
145
+ if (typeof citation === "string") {
146
+ results.push({ title: `Source ${i + 1}`, url: citation, snippet: "" });
147
+ }
148
+ else if (citation && typeof citation === "object" && typeof citation.url === "string") {
149
+ results.push({
150
+ title: citation.title || `Source ${i + 1}`,
151
+ url: citation.url,
152
+ snippet: "",
153
+ });
154
+ }
155
+ }
156
+ activityMonitor.logComplete(activityId, response.status);
157
+ return { answer, results };
158
+ }
159
+ //# sourceMappingURL=perplexity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perplexity.js","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/perplexity.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { activityMonitor } from \"./activity.ts\";\nimport type { ExtractedContent } from \"./extract.ts\";\nimport { hasCredentialSource, redactCredential, resolveCredential } from \"./credential-source.ts\";\nimport { getWebSearchConfigPath } from \"./utils.ts\";\n\nconst PERPLEXITY_API_URL = \"https://api.perplexity.ai/chat/completions\";\nconst CONFIG_PATH = getWebSearchConfigPath();\n\nconst RATE_LIMIT = {\n\tmaxRequests: 10,\n\twindowMs: 60 * 1000,\n};\n\nconst requestTimestamps: number[] = [];\n\nexport interface SearchResult {\n\ttitle: string;\n\turl: string;\n\tsnippet: string;\n}\n\nexport interface SearchResponse {\n\tanswer: string;\n\tresults: SearchResult[];\n\tinlineContent?: ExtractedContent[];\n}\n\nexport interface SearchOptions {\n\tnumResults?: number;\n\trecencyFilter?: \"day\" | \"week\" | \"month\" | \"year\";\n\tdomainFilter?: string[];\n\tsignal?: AbortSignal;\n}\n\ninterface WebSearchConfig {\n\tperplexityApiKey?: unknown;\n}\n\nlet cachedConfig: WebSearchConfig | null = null;\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 content = readFileSync(CONFIG_PATH, \"utf-8\");\n\ttry {\n\t\tcachedConfig = JSON.parse(content) as WebSearchConfig;\n\t\treturn cachedConfig;\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tthrow new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);\n\t}\n}\n\nasync function getApiKey(signal?: AbortSignal): Promise<string> {\n\tconst key = await resolveCredential({\n\t\tprovider: \"Perplexity\",\n\t\tconfiguredValue: loadConfig().perplexityApiKey,\n\t\tenvironmentValue: process.env.PERPLEXITY_API_KEY,\n\t\tsignal,\n\t});\n\tif (!key) {\n\t\tthrow new Error(\n\t\t\t\"Perplexity API key not found. Either:\\n\" +\n\t\t\t` 1. Create ${CONFIG_PATH} with { \"perplexityApiKey\": \"your-key\" }\\n` +\n\t\t\t\" 2. Set PERPLEXITY_API_KEY environment variable\\n\" +\n\t\t\t\"Get a key at https://perplexity.ai/settings/api\"\n\t\t);\n\t}\n\treturn key;\n}\n\nfunction checkRateLimit(): void {\n\tconst now = Date.now();\n\tconst windowStart = now - RATE_LIMIT.windowMs;\n\n\twhile (requestTimestamps.length > 0 && requestTimestamps[0] < windowStart) {\n\t\trequestTimestamps.shift();\n\t}\n\n\tif (requestTimestamps.length >= RATE_LIMIT.maxRequests) {\n\t\tconst waitMs = requestTimestamps[0] + RATE_LIMIT.windowMs - now;\n\t\tthrow new Error(`Rate limited. Try again in ${Math.ceil(waitMs / 1000)}s`);\n\t}\n\n\trequestTimestamps.push(now);\n}\n\nfunction validateDomainFilter(domains: string[]): string[] {\n\treturn domains.filter((d) => {\n\t\tconst domain = d.startsWith(\"-\") ? d.slice(1) : d;\n\t\treturn /^[a-zA-Z0-9][a-zA-Z0-9-_.]*\\.[a-zA-Z]{2,}$/.test(domain);\n\t});\n}\n\nexport function isPerplexityAvailable(): boolean {\n\treturn hasCredentialSource({\n\t\tprovider: \"Perplexity\",\n\t\tconfiguredValue: loadConfig().perplexityApiKey,\n\t\tenvironmentValue: process.env.PERPLEXITY_API_KEY,\n\t});\n}\n\nexport async function searchWithPerplexity(query: string, options: SearchOptions = {}): Promise<SearchResponse> {\n\tcheckRateLimit();\n\n\tconst activityId = activityMonitor.logStart({ type: \"api\", query });\n\n\tactivityMonitor.updateRateLimit({\n\t\tused: requestTimestamps.length,\n\t\tmax: RATE_LIMIT.maxRequests,\n\t\toldestTimestamp: requestTimestamps[0] ?? null,\n\t\twindowMs: RATE_LIMIT.windowMs,\n\t});\n\n\tconst apiKey = await getApiKey(options.signal);\n\tconst numResults = typeof options.numResults === \"number\" && Number.isFinite(options.numResults)\n\t\t? Math.max(1, Math.min(Math.floor(options.numResults), 20))\n\t\t: 5;\n\n\tconst requestBody: Record<string, unknown> = {\n\t\tmodel: \"sonar\",\n\t\tmessages: [{ role: \"user\", content: query }],\n\t\tmax_tokens: 1024,\n\t\treturn_related_questions: false,\n\t};\n\n\tif (options.recencyFilter) {\n\t\trequestBody.search_recency_filter = options.recencyFilter;\n\t}\n\n\tif (options.domainFilter && options.domainFilter.length > 0) {\n\t\tconst validated = validateDomainFilter(options.domainFilter);\n\t\tif (validated.length > 0) {\n\t\t\trequestBody.search_domain_filter = validated;\n\t\t}\n\t}\n\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetch(PERPLEXITY_API_URL, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${apiKey}`,\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t\tbody: JSON.stringify(requestBody),\n\t\t\t...(options.signal ? { signal: options.signal } : {}),\n\t\t});\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tconst redactedMessage = redactCredential(message, apiKey);\n\t\tif (redactedMessage.toLowerCase().includes(\"abort\")) {\n\t\t\tactivityMonitor.logComplete(activityId, 0);\n\t\t} else {\n\t\t\tactivityMonitor.logError(activityId, redactedMessage);\n\t\t}\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\tif (!response.ok) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tconst errorText = redactCredential(await response.text(), apiKey);\n\t\tthrow new Error(`Perplexity API error ${response.status}: ${errorText}`);\n\t}\n\n\tlet data: Record<string, unknown>;\n\ttry {\n\t\tdata = (await response.json()) as Record<string, unknown>;\n\t} catch (err) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tthrow new Error(`Perplexity API returned invalid JSON: ${message}`);\n\t}\n\n\tconst answer = (data.choices as Array<{ message?: { content?: string } }>)?.[0]?.message?.content || \"\";\n\tconst citations = Array.isArray(data.citations) ? data.citations : [];\n\n\tconst results: SearchResult[] = [];\n\tfor (let i = 0; i < Math.min(citations.length, numResults); i++) {\n\t\tconst citation = citations[i];\n\t\tif (typeof citation === \"string\") {\n\t\t\tresults.push({ title: `Source ${i + 1}`, url: citation, snippet: \"\" });\n\t\t} else if (citation && typeof citation === \"object\" && typeof citation.url === \"string\") {\n\t\t\tresults.push({\n\t\t\t\ttitle: citation.title || `Source ${i + 1}`,\n\t\t\t\turl: citation.url,\n\t\t\t\tsnippet: \"\",\n\t\t\t});\n\t\t}\n\t}\n\n\tactivityMonitor.logComplete(activityId, response.status);\n\treturn { answer, results };\n}\n"]}
@@ -0,0 +1,10 @@
1
+ import type { ExtractedContent, ExtractOptions } from "./extract.ts";
2
+ import type { SearchOptions, SearchResponse } from "./perplexity.ts";
3
+ interface QueritSearchOptions extends SearchOptions {
4
+ includeContent?: boolean;
5
+ }
6
+ export declare function isQueritAvailable(): boolean;
7
+ export declare function searchWithQuerit(query: string, options?: QueritSearchOptions): Promise<SearchResponse>;
8
+ export declare function extractWithQuerit(url: string, signal?: AbortSignal, options?: ExtractOptions): Promise<ExtractedContent | null>;
9
+ export {};
10
+ //# sourceMappingURL=querit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"querit.d.ts","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/querit.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AA0DrE,UAAU,mBAAoB,SAAQ,aAAa;IAClD,cAAc,CAAC,EAAE,OAAO,CAAC;CACzB;AA4CD,wBAAgB,iBAAiB,IAAI,OAAO,CAM3C;AA4OD,wBAAsB,gBAAgB,CACrC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,mBAAwB,GAC/B,OAAO,CAAC,cAAc,CAAC,CA+BzB;AAED,wBAAsB,iBAAiB,CACtC,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,WAAW,EACpB,OAAO,GAAE,cAAmB,GAC1B,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA0BlC","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { activityMonitor } from \"./activity.ts\";\nimport { hasCredentialSource, redactCredential, resolveCredential } from \"./credential-source.ts\";\nimport type { ExtractedContent, ExtractOptions } from \"./extract.ts\";\nimport type { SearchOptions, SearchResponse } from \"./perplexity.ts\";\nimport { getWebSearchConfigPath } from \"./utils.ts\";\n\nconst QUERIT_SEARCH_URL = \"https://api.querit.ai/v1/search\";\nconst QUERIT_CONTENTS_URL = \"https://api.querit.ai/v1/contents\";\nconst CONFIG_PATH = getWebSearchConfigPath();\nconst SEARCH_TIMEOUT_MS = 60_000;\nconst CONTENTS_TIMEOUT_MS = 60_000;\nconst MAX_CONTENT_URLS = 10;\n\ninterface WebSearchConfig {\n\tqueritApiKey?: unknown;\n}\n\ninterface QueritSearchResult {\n\turl?: string | null;\n\ttitle?: string | null;\n\tsnippet?: string | null;\n\tpage_age?: string | null;\n\tsite_name?: string | null;\n}\n\ninterface QueritSearchResponse {\n\terror_code?: number | string;\n\terror_msg?: string;\n\tsearch_id?: number | string;\n\tresults?: { result?: QueritSearchResult[] };\n}\n\ninterface QueritContentMetadata {\n\ttitle?: string | null;\n\turl?: string | null;\n\tpublishTime?: string | null;\n\tsiteName?: string | null;\n\tsiteIcon?: string | null;\n}\n\ninterface QueritContentResult {\n\tid?: string | null;\n\turl?: string | null;\n\tcontent?: string | null;\n\textrasMeta?: QueritContentMetadata | null;\n}\n\ninterface QueritContentStatus {\n\tid?: string | null;\n\tstatus?: string | null;\n}\n\ninterface QueritContentsResponse {\n\terror_code?: number | string;\n\terror_msg?: string;\n\tsearch_id?: number | string;\n\tresults?: QueritContentResult[];\n\tstatuses?: QueritContentStatus[];\n\tsearchTime?: number;\n}\n\ninterface QueritSearchOptions extends SearchOptions {\n\tincludeContent?: boolean;\n}\n\nlet cachedConfig: WebSearchConfig | null = null;\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\tconst message = err instanceof Error ? err.message : String(err);\n\t\tthrow new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);\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> {\n\tconst key = await resolveCredential({\n\t\tprovider: \"Querit\",\n\t\tconfiguredValue: loadConfig().queritApiKey,\n\t\tenvironmentValue: process.env.QUERIT_API_KEY,\n\t\tsignal,\n\t});\n\tif (!key) {\n\t\tthrow new Error(\n\t\t\t\"Querit API key not found. Either:\\n\" +\n\t\t\t` 1. Create ${CONFIG_PATH} with { \"queritApiKey\": \"your-key\" }\\n` +\n\t\t\t\" 2. Set QUERIT_API_KEY environment variable\\n\" +\n\t\t\t\"Create a key at https://www.querit.ai/en/dashboard/api-keys\",\n\t\t);\n\t}\n\treturn key;\n}\n\nexport function isQueritAvailable(): boolean {\n\treturn hasCredentialSource({\n\t\tprovider: \"Querit\",\n\t\tconfiguredValue: loadConfig().queritApiKey,\n\t\tenvironmentValue: process.env.QUERIT_API_KEY,\n\t});\n}\n\nfunction errorMessage(err: unknown): string {\n\treturn err instanceof Error ? err.message : String(err);\n}\n\nfunction requestSignal(signal: AbortSignal | undefined, timeoutMs: number): AbortSignal {\n\tconst timeout = AbortSignal.timeout(timeoutMs);\n\treturn signal ? AbortSignal.any([signal, timeout]) : timeout;\n}\n\nfunction normalizeNumResults(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\nfunction mapDomainFilter(domainFilter: string[] | undefined): { include: string[]; exclude: string[] } {\n\tconst include: string[] = [];\n\tconst exclude: string[] = [];\n\tfor (const raw of domainFilter ?? []) {\n\t\tconst domain = normalizeDomain(raw);\n\t\tif (!domain) continue;\n\t\tconst target = raw.trim().startsWith(\"-\") ? exclude : include;\n\t\tif (!target.includes(domain)) target.push(domain);\n\t}\n\treturn { include, exclude };\n}\n\nfunction mapRecencyFilter(value: SearchOptions[\"recencyFilter\"]): string | undefined {\n\tif (value === \"day\") return \"d1\";\n\tif (value === \"week\") return \"w1\";\n\tif (value === \"month\") return \"m1\";\n\tif (value === \"year\") return \"y1\";\n\treturn undefined;\n}\n\nfunction buildSearchBody(query: string, options: QueritSearchOptions): Record<string, unknown> {\n\tconst { include, exclude } = mapDomainFilter(options.domainFilter);\n\tconst date = mapRecencyFilter(options.recencyFilter);\n\tconst filters: Record<string, unknown> = {};\n\tif (include.length > 0 || exclude.length > 0) {\n\t\tfilters.sites = {\n\t\t\t...(include.length > 0 ? { include } : {}),\n\t\t\t...(exclude.length > 0 ? { exclude } : {}),\n\t\t};\n\t}\n\tif (date) filters.timeRange = { date };\n\treturn {\n\t\tquery,\n\t\tcount: normalizeNumResults(options.numResults),\n\t\t...(Object.keys(filters).length > 0 ? { filters } : {}),\n\t};\n}\n\nfunction normalizeTimeoutMs(value: number | undefined): number {\n\tif (typeof value !== \"number\" || !Number.isFinite(value)) return CONTENTS_TIMEOUT_MS;\n\treturn Math.max(1, Math.floor(value));\n}\n\nfunction crawlTimeoutSeconds(value: number | undefined): number {\n\tif (typeof value !== \"number\" || !Number.isFinite(value)) return 10;\n\treturn Math.max(1, Math.min(Math.ceil(value / 1_000), 60));\n}\n\nfunction buildContentsBody(urls: string[], options: ExtractOptions = {}): Record<string, unknown> {\n\treturn {\n\t\turls,\n\t\tformat: \"markdown\",\n\t\tcrawlTimeout: crawlTimeoutSeconds(options.timeoutMs),\n\t\textrasMeta: true,\n\t};\n}\n\nasync function queritJsonRequest<T>(\n\tlabel: \"Search\" | \"Contents\",\n\turl: string,\n\tapiKey: string,\n\tbody: Record<string, unknown>,\n\ttimeoutMs: number,\n\tsignal?: AbortSignal,\n): Promise<T> {\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetch(url, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${apiKey}`,\n\t\t\t\tAccept: \"application/json\",\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(signal, timeoutMs),\n\t\t});\n\t} catch (err) {\n\t\tconst message = errorMessage(err);\n\t\tconst isRequestAbort = err instanceof Error\n\t\t\t? err.name === \"AbortError\" || err.name === \"TimeoutError\" || /abort|timeout/i.test(message)\n\t\t\t: /abort|timeout/i.test(message);\n\t\tif (!signal?.aborted && isRequestAbort) {\n\t\t\tconst timeoutError = new Error(`Querit ${label} API request timed out after ${Math.ceil(timeoutMs / 1_000)} seconds`);\n\t\t\ttimeoutError.name = \"TimeoutError\";\n\t\t\tthrow timeoutError;\n\t\t}\n\t\tconst redactedMessage = redactCredential(message, apiKey);\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\tconst raw = await response.text();\n\tif (!response.ok) {\n\t\tthrow new Error(`Querit ${label} API error ${response.status}: ${redactCredential(raw, apiKey).slice(0, 300)}`);\n\t}\n\ttry {\n\t\treturn JSON.parse(raw) as T;\n\t} catch (err) {\n\t\tthrow new Error(`Querit ${label} API returned invalid JSON: ${errorMessage(err)}`);\n\t}\n}\n\nfunction assertApiSuccess(label: \"Search\" | \"Contents\", data: QueritSearchResponse | QueritContentsResponse): void {\n\tconst code = Number(data.error_code);\n\tif (!Number.isFinite(code) || code !== 200) {\n\t\tconst renderedCode = data.error_code === undefined ? \"unknown\" : String(data.error_code);\n\t\tconst message = typeof data.error_msg === \"string\" && data.error_msg.trim() ? `: ${data.error_msg.trim()}` : \"\";\n\t\tthrow new Error(`Querit ${label} API returned error ${renderedCode}${message}`);\n\t}\n}\n\nfunction mapSearchResults(data: QueritSearchResponse): SearchResponse[\"results\"] {\n\tconst items = data.results?.result;\n\tif (!Array.isArray(items)) {\n\t\tthrow new Error(\"Querit Search API returned an unexpected response shape\");\n\t}\n\treturn items.flatMap((item) => {\n\t\tif (!item || typeof item.url !== \"string\" || item.url.trim().length === 0) return [];\n\t\tconst url = item.url.trim();\n\t\treturn [{\n\t\t\ttitle: typeof item.title === \"string\" && item.title.trim() ? item.title.trim() : url,\n\t\t\turl,\n\t\t\tsnippet: typeof item.snippet === \"string\" ? item.snippet.replace(/\\s+/g, \" \").trim() : \"\",\n\t\t}];\n\t});\n}\n\nfunction buildAnswer(results: SearchResponse[\"results\"]): string {\n\treturn results.map((result) => {\n\t\tif (result.snippet) return `${result.snippet}\\nSource: ${result.title} (${result.url})`;\n\t\treturn `Source: ${result.title} (${result.url})`;\n\t}).join(\"\\n\\n\");\n}\n\nfunction mapContentResult(result: QueritContentResult | undefined, requestedUrl: string): ExtractedContent | null {\n\tif (!result || typeof result.content !== \"string\" || result.content.trim().length === 0) return null;\n\tconst metadata = result.extrasMeta;\n\treturn {\n\t\turl: requestedUrl,\n\t\ttitle: metadata && typeof metadata.title === \"string\" ? metadata.title.trim() : \"\",\n\t\tcontent: result.content.trim(),\n\t\terror: null,\n\t};\n}\n\nfunction findContentResult(\n\tdata: QueritContentsResponse,\n\trequestedUrl: string,\n\tindex: number,\n\trequestedCount: number,\n): QueritContentResult | undefined {\n\tif (!Array.isArray(data.results)) return undefined;\n\tconst exact = data.results.find((item) => item?.url === requestedUrl || item?.extrasMeta?.url === requestedUrl);\n\tif (exact) return exact;\n\tif (requestedCount === 1) return data.results[0];\n\treturn data.results.length === requestedCount ? data.results[index] : undefined;\n}\n\nfunction failedContentStatus(data: QueritContentsResponse, result: QueritContentResult | undefined, index: number): boolean {\n\tif (!Array.isArray(data.statuses)) return false;\n\tconst status = result?.id\n\t\t? data.statuses.find((item) => item?.id === result.id)\n\t\t: data.statuses[index];\n\treturn status?.status === \"failed\";\n}\n\nasync function fetchContentsBatch(\n\turls: string[],\n\tapiKey: string,\n\tsignal?: AbortSignal,\n\toptions: ExtractOptions = {},\n): Promise<QueritContentsResponse> {\n\tconst data = await queritJsonRequest<QueritContentsResponse>(\n\t\t\"Contents\",\n\t\tQUERIT_CONTENTS_URL,\n\t\tapiKey,\n\t\tbuildContentsBody(urls, options),\n\t\tnormalizeTimeoutMs(options.timeoutMs),\n\t\tsignal,\n\t);\n\tassertApiSuccess(\"Contents\", data);\n\tif (!Array.isArray(data.results) || !Array.isArray(data.statuses)) {\n\t\tthrow new Error(\"Querit Contents API returned an unexpected response shape\");\n\t}\n\treturn data;\n}\n\nasync function fetchInlineContent(urls: string[], apiKey: string, signal?: AbortSignal): Promise<ExtractedContent[]> {\n\tconst content: ExtractedContent[] = [];\n\tfor (let offset = 0; offset < urls.length; offset += MAX_CONTENT_URLS) {\n\t\tconst batch = urls.slice(offset, offset + MAX_CONTENT_URLS);\n\t\tconst data = await fetchContentsBatch(batch, apiKey, signal);\n\t\tfor (let index = 0; index < batch.length; index++) {\n\t\t\tconst requestedUrl = batch[index];\n\t\t\tconst result = findContentResult(data, requestedUrl, index, batch.length);\n\t\t\tconst mapped = mapContentResult(result, requestedUrl);\n\t\t\tif (mapped) content.push(mapped);\n\t\t}\n\t}\n\treturn content;\n}\n\nexport async function searchWithQuerit(\n\tquery: string,\n\toptions: QueritSearchOptions = {},\n): Promise<SearchResponse> {\n\tconst apiKey = await getApiKey(options.signal);\n\tconst activityId = activityMonitor.logStart({ type: \"api\", query });\n\ttry {\n\t\tconst data = await queritJsonRequest<QueritSearchResponse>(\n\t\t\t\"Search\",\n\t\t\tQUERIT_SEARCH_URL,\n\t\t\tapiKey,\n\t\t\tbuildSearchBody(query, options),\n\t\t\tSEARCH_TIMEOUT_MS,\n\t\t\toptions.signal,\n\t\t);\n\t\tassertApiSuccess(\"Search\", data);\n\t\tconst results = mapSearchResults(data);\n\t\tconst response: SearchResponse = { answer: buildAnswer(results), results };\n\t\tif (options.includeContent && results.length > 0) {\n\t\t\tconst inlineContent = await fetchInlineContent(results.map((result) => result.url), apiKey, options.signal);\n\t\t\tif (inlineContent.length > 0) response.inlineContent = inlineContent;\n\t\t}\n\t\tactivityMonitor.logComplete(activityId, 200);\n\t\treturn response;\n\t} catch (err) {\n\t\tconst message = errorMessage(err);\n\t\tconst redactedMessage = redactCredential(message, apiKey);\n\t\tif (options.signal?.aborted) 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\nexport async function extractWithQuerit(\n\turl: string,\n\tsignal?: AbortSignal,\n\toptions: ExtractOptions = {},\n): Promise<ExtractedContent | null> {\n\tconst apiKey = await getApiKey(signal);\n\tconst activityId = activityMonitor.logStart({ type: \"fetch\", url });\n\ttry {\n\t\tconst data = await fetchContentsBatch([url], apiKey, signal, options);\n\t\tconst result = findContentResult(data, url, 0, 1);\n\t\tconst mapped = mapContentResult(result, url);\n\t\tif (mapped) {\n\t\t\tactivityMonitor.logComplete(activityId, 200);\n\t\t\treturn mapped;\n\t\t}\n\t\tif (failedContentStatus(data, result, 0)) {\n\t\t\tthrow new Error(`Querit Contents API failed to crawl ${url}`);\n\t\t}\n\t\tactivityMonitor.logComplete(activityId, 200);\n\t\treturn null;\n\t} catch (err) {\n\t\tconst message = errorMessage(err);\n\t\tconst redactedMessage = redactCredential(message, apiKey);\n\t\tif (signal?.aborted) 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"]}