@herbertgao/pi-extensions 2026.8.2 → 2026.8.4

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 (392) hide show
  1. package/README.md +50 -2
  2. package/examples/pi-footer.json +275 -0
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +6 -8
  4. package/node_modules/@herbertgao/pi-cc-extensions/README.md +5 -7
  5. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.en.svg +101 -0
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +1 -17
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +5 -3
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/{utils/agent-summary.ts → feature/agent-summary/core.ts} +37 -35
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{agent-summary.ts → agent-summary/index.ts} +2 -6
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{session-reference.ts → reference/index.ts} +1 -1
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{agent-autocomplete.ts → reference/subagent.ts} +19 -9
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{pi-startup-header.ts → shell/startup-header.ts} +1 -1
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{working-message.ts → shell/working-message.ts} +5 -2
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +32 -28
  15. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +379 -85
  16. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +727 -0
  17. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +65 -775
  18. package/node_modules/@herbertgao/pi-cc-extensions/extensions/{feature → renderer}/markdown-enhance.ts +35 -31
  19. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-hover.ts → mouse/hover.ts} +5 -5
  20. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-interaction.ts → mouse/interaction.ts} +34 -35
  21. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-layout.ts → mouse/layout.ts} +5 -21
  22. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-packets.ts → mouse/packets.ts} +1 -1
  23. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-scroll.ts → mouse/scroll.ts} +48 -37
  24. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/ansi-utils.ts +56 -1
  25. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/diff-renderer.ts +2 -2
  26. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/line-width-safety.ts +1 -1
  27. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-display-utils.ts +12 -0
  28. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/write-execution.ts +1 -2
  29. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-grouping.ts → tool/grouping.ts} +18 -8
  30. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-result.ts → tool/result.ts} +4 -13
  31. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/fullscreen-detect.ts +0 -27
  32. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  33. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
  34. package/node_modules/@herbertgao/pi-subagents/package.json +2 -2
  35. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +1 -0
  36. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +3 -1
  37. package/node_modules/@herbertgao/pi-subagents/src/index.ts +33 -35
  38. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +26 -35
  39. package/node_modules/@herbertgao/pi-subagents/src/prompts.ts +15 -1
  40. package/node_modules/pi-footer/CHANGELOG.md +145 -0
  41. package/node_modules/pi-footer/LICENSE +21 -0
  42. package/node_modules/pi-footer/README.md +354 -0
  43. package/node_modules/pi-footer/package.json +68 -0
  44. package/node_modules/pi-footer/src/cache.ts +106 -0
  45. package/node_modules/pi-footer/src/colors.ts +237 -0
  46. package/node_modules/pi-footer/src/config.ts +202 -0
  47. package/node_modules/pi-footer/src/event-widgets.ts +41 -0
  48. package/node_modules/pi-footer/src/extension-statuses.ts +102 -0
  49. package/node_modules/pi-footer/src/git.ts +135 -0
  50. package/node_modules/pi-footer/src/index.ts +268 -0
  51. package/node_modules/pi-footer/src/metrics.ts +141 -0
  52. package/node_modules/pi-footer/src/presets.ts +383 -0
  53. package/node_modules/pi-footer/src/render.ts +114 -0
  54. package/node_modules/pi-footer/src/separators.ts +52 -0
  55. package/node_modules/pi-footer/src/types.ts +122 -0
  56. package/node_modules/pi-footer/src/ui/color-level-confirm.ts +22 -0
  57. package/node_modules/pi-footer/src/ui/color-options.ts +195 -0
  58. package/node_modules/pi-footer/src/ui/config-lifecycle.ts +61 -0
  59. package/node_modules/pi-footer/src/ui/edit-colors.ts +13 -0
  60. package/node_modules/pi-footer/src/ui/events.ts +35 -0
  61. package/node_modules/pi-footer/src/ui/extension-status-picker.ts +60 -0
  62. package/node_modules/pi-footer/src/ui/extension-statuses.ts +73 -0
  63. package/node_modules/pi-footer/src/ui/fields.ts +252 -0
  64. package/node_modules/pi-footer/src/ui/global-menu.ts +126 -0
  65. package/node_modules/pi-footer/src/ui/helpers.ts +44 -0
  66. package/node_modules/pi-footer/src/ui/layout.ts +17 -0
  67. package/node_modules/pi-footer/src/ui/line-list.ts +19 -0
  68. package/node_modules/pi-footer/src/ui/model.ts +64 -0
  69. package/node_modules/pi-footer/src/ui/navigation.ts +29 -0
  70. package/node_modules/pi-footer/src/ui/option-edit.ts +93 -0
  71. package/node_modules/pi-footer/src/ui/overlay-render.ts +103 -0
  72. package/node_modules/pi-footer/src/ui/screen-context.ts +18 -0
  73. package/node_modules/pi-footer/src/ui/screen-controller.ts +27 -0
  74. package/node_modules/pi-footer/src/ui/screen-render.ts +32 -0
  75. package/node_modules/pi-footer/src/ui/screen-state.ts +20 -0
  76. package/node_modules/pi-footer/src/ui/screen.ts +243 -0
  77. package/node_modules/pi-footer/src/ui/screens/add-widget.ts +92 -0
  78. package/node_modules/pi-footer/src/ui/screens/confirm-exit.ts +52 -0
  79. package/node_modules/pi-footer/src/ui/screens/controller.ts +12 -0
  80. package/node_modules/pi-footer/src/ui/screens/edit-colors.ts +68 -0
  81. package/node_modules/pi-footer/src/ui/screens/edit-widget.ts +115 -0
  82. package/node_modules/pi-footer/src/ui/screens/extension-status-row.ts +61 -0
  83. package/node_modules/pi-footer/src/ui/screens/global.ts +75 -0
  84. package/node_modules/pi-footer/src/ui/screens/line-list.ts +108 -0
  85. package/node_modules/pi-footer/src/ui/screens/main.ts +99 -0
  86. package/node_modules/pi-footer/src/ui/screens/terminal.ts +121 -0
  87. package/node_modules/pi-footer/src/ui/screens/widget-list.ts +154 -0
  88. package/node_modules/pi-footer/src/ui/terminal-menu.ts +32 -0
  89. package/node_modules/pi-footer/src/ui/theme.ts +48 -0
  90. package/node_modules/pi-footer/src/ui/title-bar.ts +100 -0
  91. package/node_modules/pi-footer/src/ui/widget-actions.ts +76 -0
  92. package/node_modules/pi-footer/src/ui.ts +66 -0
  93. package/node_modules/pi-footer/src/widgets/context.ts +28 -0
  94. package/node_modules/pi-footer/src/widgets/core/active-tools.ts +17 -0
  95. package/node_modules/pi-footer/src/widgets/core/context-window.ts +24 -0
  96. package/node_modules/pi-footer/src/widgets/core/cwd-basename.ts +19 -0
  97. package/node_modules/pi-footer/src/widgets/core/cwd.ts +108 -0
  98. package/node_modules/pi-footer/src/widgets/core/event.ts +34 -0
  99. package/node_modules/pi-footer/src/widgets/core/external-status.ts +102 -0
  100. package/node_modules/pi-footer/src/widgets/core/model-provider.ts +20 -0
  101. package/node_modules/pi-footer/src/widgets/core/model.ts +35 -0
  102. package/node_modules/pi-footer/src/widgets/core/provider.ts +17 -0
  103. package/node_modules/pi-footer/src/widgets/core/session-name.ts +17 -0
  104. package/node_modules/pi-footer/src/widgets/core/text-verbosity.ts +17 -0
  105. package/node_modules/pi-footer/src/widgets/core/thinking-level.ts +17 -0
  106. package/node_modules/pi-footer/src/widgets/git/ahead-behind.ts +17 -0
  107. package/node_modules/pi-footer/src/widgets/git/branch.ts +81 -0
  108. package/node_modules/pi-footer/src/widgets/git/clean.ts +19 -0
  109. package/node_modules/pi-footer/src/widgets/git/deletions.ts +17 -0
  110. package/node_modules/pi-footer/src/widgets/git/diff.ts +38 -0
  111. package/node_modules/pi-footer/src/widgets/git/insertions.ts +17 -0
  112. package/node_modules/pi-footer/src/widgets/git/remote.ts +17 -0
  113. package/node_modules/pi-footer/src/widgets/git/root.ts +17 -0
  114. package/node_modules/pi-footer/src/widgets/git/sha.ts +17 -0
  115. package/node_modules/pi-footer/src/widgets/git/staged.ts +17 -0
  116. package/node_modules/pi-footer/src/widgets/git/status.ts +19 -0
  117. package/node_modules/pi-footer/src/widgets/git/unstaged.ts +17 -0
  118. package/node_modules/pi-footer/src/widgets/git/untracked.ts +17 -0
  119. package/node_modules/pi-footer/src/widgets/instance.ts +118 -0
  120. package/node_modules/pi-footer/src/widgets/layout/custom-text.ts +18 -0
  121. package/node_modules/pi-footer/src/widgets/layout/flex-separator.ts +32 -0
  122. package/node_modules/pi-footer/src/widgets/layout/separator.ts +57 -0
  123. package/node_modules/pi-footer/src/widgets/layout/spacer.ts +31 -0
  124. package/node_modules/pi-footer/src/widgets/options.ts +122 -0
  125. package/node_modules/pi-footer/src/widgets/project/runtime.ts +361 -0
  126. package/node_modules/pi-footer/src/widgets/registry.ts +223 -0
  127. package/node_modules/pi-footer/src/widgets/session/assistant-messages.ts +17 -0
  128. package/node_modules/pi-footer/src/widgets/session/compactions.ts +17 -0
  129. package/node_modules/pi-footer/src/widgets/session/elapsed.ts +18 -0
  130. package/node_modules/pi-footer/src/widgets/session/last-activity.ts +18 -0
  131. package/node_modules/pi-footer/src/widgets/session/messages.ts +19 -0
  132. package/node_modules/pi-footer/src/widgets/session/session-id.ts +17 -0
  133. package/node_modules/pi-footer/src/widgets/session/session-start.ts +18 -0
  134. package/node_modules/pi-footer/src/widgets/session/tool-results.ts +17 -0
  135. package/node_modules/pi-footer/src/widgets/session/total-messages.ts +19 -0
  136. package/node_modules/pi-footer/src/widgets/session/total-time.ts +18 -0
  137. package/node_modules/pi-footer/src/widgets/session/user-messages.ts +17 -0
  138. package/node_modules/pi-footer/src/widgets/store.ts +26 -0
  139. package/node_modules/pi-footer/src/widgets/tokens/cache-hit-rate.ts +55 -0
  140. package/node_modules/pi-footer/src/widgets/tokens/cache-read.ts +18 -0
  141. package/node_modules/pi-footer/src/widgets/tokens/cache-write.ts +18 -0
  142. package/node_modules/pi-footer/src/widgets/tokens/context-bar.ts +108 -0
  143. package/node_modules/pi-footer/src/widgets/tokens/context-length.ts +26 -0
  144. package/node_modules/pi-footer/src/widgets/tokens/context-remaining.ts +29 -0
  145. package/node_modules/pi-footer/src/widgets/tokens/context.ts +29 -0
  146. package/node_modules/pi-footer/src/widgets/tokens/cost.ts +51 -0
  147. package/node_modules/pi-footer/src/widgets/tokens/input-speed.ts +25 -0
  148. package/node_modules/pi-footer/src/widgets/tokens/input-tokens.ts +18 -0
  149. package/node_modules/pi-footer/src/widgets/tokens/output-speed.ts +25 -0
  150. package/node_modules/pi-footer/src/widgets/tokens/output-tokens.ts +18 -0
  151. package/node_modules/pi-footer/src/widgets/tokens/tokens.ts +20 -0
  152. package/node_modules/pi-footer/src/widgets/tokens/total-speed.ts +25 -0
  153. package/node_modules/pi-footer/src/widgets/tokens/total-tokens.ts +18 -0
  154. package/node_modules/pi-footer/src/widgets/types.ts +181 -0
  155. package/node_modules/pi-footer/src/widgets/utils/colors.ts +11 -0
  156. package/node_modules/pi-footer/src/widgets/utils/context.ts +112 -0
  157. package/node_modules/pi-footer/src/widgets/utils/session.ts +19 -0
  158. package/node_modules/pi-footer/src/widgets/utils/token-format.ts +78 -0
  159. package/node_modules/pi-mcp-adapter/CHANGELOG.md +19 -0
  160. package/node_modules/pi-mcp-adapter/README.md +19 -4
  161. package/node_modules/pi-mcp-adapter/agent-dir.ts +40 -0
  162. package/node_modules/pi-mcp-adapter/commands.ts +12 -2
  163. package/node_modules/pi-mcp-adapter/direct-tools.ts +6 -8
  164. package/node_modules/pi-mcp-adapter/index.ts +15 -3
  165. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +87 -4
  166. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +138 -61
  167. package/node_modules/pi-mcp-adapter/mcp-oauth-provider.ts +43 -4
  168. package/node_modules/pi-mcp-adapter/oauth.ts +48 -0
  169. package/node_modules/pi-mcp-adapter/package.json +8 -2
  170. package/node_modules/pi-mcp-adapter/proxy-modes.ts +8 -10
  171. package/node_modules/pi-mcp-adapter/tool-registrar.ts +187 -3
  172. package/node_modules/pi-mcp-adapter/types.ts +2 -0
  173. package/node_modules/pi-web-access/CHANGELOG.md +45 -0
  174. package/node_modules/pi-web-access/README.md +104 -29
  175. package/node_modules/pi-web-access/bocha.ts +221 -0
  176. package/node_modules/pi-web-access/curator-page.ts +113 -12
  177. package/node_modules/pi-web-access/curator-server.ts +56 -20
  178. package/node_modules/pi-web-access/datalab-pdf-extract.ts +568 -0
  179. package/node_modules/pi-web-access/duckduckgo.ts +123 -0
  180. package/node_modules/pi-web-access/extract.ts +288 -143
  181. package/node_modules/pi-web-access/feature-config.ts +29 -0
  182. package/node_modules/pi-web-access/gemini-api.ts +4 -0
  183. package/node_modules/pi-web-access/gemini-search.ts +45 -12
  184. package/node_modules/pi-web-access/gemini-web.ts +68 -3
  185. package/node_modules/pi-web-access/index.ts +160 -71
  186. package/node_modules/pi-web-access/jina-search.ts +284 -0
  187. package/node_modules/pi-web-access/kagi.ts +12 -12
  188. package/node_modules/pi-web-access/openai-search.ts +12 -2
  189. package/node_modules/pi-web-access/package.json +4 -3
  190. package/node_modules/pi-web-access/pdf-extract.ts +327 -228
  191. package/node_modules/pi-web-access/storage.ts +440 -2
  192. package/node_modules/pi-web-access/summary-model-scope.ts +1 -1
  193. package/node_modules/pi-web-access/summary-review.ts +4 -2
  194. package/node_modules/pi-web-access/video-extract.ts +10 -8
  195. package/node_modules/pi-web-access/xai-search.ts +12 -2
  196. package/node_modules/pi-web-access/youtube-extract.ts +2 -1
  197. package/node_modules/remote-pi/LICENSE +21 -0
  198. package/node_modules/remote-pi/README.md +675 -0
  199. package/node_modules/remote-pi/dist/actions/handlers.d.ts +122 -0
  200. package/node_modules/remote-pi/dist/actions/handlers.js +160 -0
  201. package/node_modules/remote-pi/dist/actions/handlers.js.map +1 -0
  202. package/node_modules/remote-pi/dist/actions/registry.d.ts +39 -0
  203. package/node_modules/remote-pi/dist/actions/registry.js +58 -0
  204. package/node_modules/remote-pi/dist/actions/registry.js.map +1 -0
  205. package/node_modules/remote-pi/dist/bin/supervisord.d.ts +2 -0
  206. package/node_modules/remote-pi/dist/bin/supervisord.js +86 -0
  207. package/node_modules/remote-pi/dist/bin/supervisord.js.map +1 -0
  208. package/node_modules/remote-pi/dist/commands/builtin_mirror.d.ts +58 -0
  209. package/node_modules/remote-pi/dist/commands/builtin_mirror.js +71 -0
  210. package/node_modules/remote-pi/dist/commands/builtin_mirror.js.map +1 -0
  211. package/node_modules/remote-pi/dist/commands/list_commands.d.ts +60 -0
  212. package/node_modules/remote-pi/dist/commands/list_commands.js +73 -0
  213. package/node_modules/remote-pi/dist/commands/list_commands.js.map +1 -0
  214. package/node_modules/remote-pi/dist/config.d.ts +62 -0
  215. package/node_modules/remote-pi/dist/config.js +115 -0
  216. package/node_modules/remote-pi/dist/config.js.map +1 -0
  217. package/node_modules/remote-pi/dist/daemon/client.d.ts +20 -0
  218. package/node_modules/remote-pi/dist/daemon/client.js +131 -0
  219. package/node_modules/remote-pi/dist/daemon/client.js.map +1 -0
  220. package/node_modules/remote-pi/dist/daemon/control_protocol.d.ts +176 -0
  221. package/node_modules/remote-pi/dist/daemon/control_protocol.js +63 -0
  222. package/node_modules/remote-pi/dist/daemon/control_protocol.js.map +1 -0
  223. package/node_modules/remote-pi/dist/daemon/cron_log.d.ts +45 -0
  224. package/node_modules/remote-pi/dist/daemon/cron_log.js +72 -0
  225. package/node_modules/remote-pi/dist/daemon/cron_log.js.map +1 -0
  226. package/node_modules/remote-pi/dist/daemon/cron_registry.d.ts +80 -0
  227. package/node_modules/remote-pi/dist/daemon/cron_registry.js +194 -0
  228. package/node_modules/remote-pi/dist/daemon/cron_registry.js.map +1 -0
  229. package/node_modules/remote-pi/dist/daemon/id.d.ts +24 -0
  230. package/node_modules/remote-pi/dist/daemon/id.js +36 -0
  231. package/node_modules/remote-pi/dist/daemon/id.js.map +1 -0
  232. package/node_modules/remote-pi/dist/daemon/install.d.ts +170 -0
  233. package/node_modules/remote-pi/dist/daemon/install.js +547 -0
  234. package/node_modules/remote-pi/dist/daemon/install.js.map +1 -0
  235. package/node_modules/remote-pi/dist/daemon/registry.d.ts +63 -0
  236. package/node_modules/remote-pi/dist/daemon/registry.js +153 -0
  237. package/node_modules/remote-pi/dist/daemon/registry.js.map +1 -0
  238. package/node_modules/remote-pi/dist/daemon/rpc_child.d.ts +194 -0
  239. package/node_modules/remote-pi/dist/daemon/rpc_child.js +389 -0
  240. package/node_modules/remote-pi/dist/daemon/rpc_child.js.map +1 -0
  241. package/node_modules/remote-pi/dist/daemon/supervisor.d.ts +93 -0
  242. package/node_modules/remote-pi/dist/daemon/supervisor.js +596 -0
  243. package/node_modules/remote-pi/dist/daemon/supervisor.js.map +1 -0
  244. package/node_modules/remote-pi/dist/extension_ui_bridge.d.ts +26 -0
  245. package/node_modules/remote-pi/dist/extension_ui_bridge.js +341 -0
  246. package/node_modules/remote-pi/dist/extension_ui_bridge.js.map +1 -0
  247. package/node_modules/remote-pi/dist/index.d.ts +213 -0
  248. package/node_modules/remote-pi/dist/index.js +4996 -0
  249. package/node_modules/remote-pi/dist/index.js.map +1 -0
  250. package/node_modules/remote-pi/dist/mcp/mesh_result.d.ts +10 -0
  251. package/node_modules/remote-pi/dist/mcp/mesh_result.js +42 -0
  252. package/node_modules/remote-pi/dist/mcp/mesh_result.js.map +1 -0
  253. package/node_modules/remote-pi/dist/mcp/mesh_server.d.ts +16 -0
  254. package/node_modules/remote-pi/dist/mcp/mesh_server.js +327 -0
  255. package/node_modules/remote-pi/dist/mcp/mesh_server.js.map +1 -0
  256. package/node_modules/remote-pi/dist/mesh/canonical.d.ts +30 -0
  257. package/node_modules/remote-pi/dist/mesh/canonical.js +61 -0
  258. package/node_modules/remote-pi/dist/mesh/canonical.js.map +1 -0
  259. package/node_modules/remote-pi/dist/mesh/client.d.ts +18 -0
  260. package/node_modules/remote-pi/dist/mesh/client.js +125 -0
  261. package/node_modules/remote-pi/dist/mesh/client.js.map +1 -0
  262. package/node_modules/remote-pi/dist/mesh/encoding.d.ts +38 -0
  263. package/node_modules/remote-pi/dist/mesh/encoding.js +216 -0
  264. package/node_modules/remote-pi/dist/mesh/encoding.js.map +1 -0
  265. package/node_modules/remote-pi/dist/mesh/self_revoke.d.ts +73 -0
  266. package/node_modules/remote-pi/dist/mesh/self_revoke.js +401 -0
  267. package/node_modules/remote-pi/dist/mesh/self_revoke.js.map +1 -0
  268. package/node_modules/remote-pi/dist/mesh/siblings.d.ts +49 -0
  269. package/node_modules/remote-pi/dist/mesh/siblings.js +159 -0
  270. package/node_modules/remote-pi/dist/mesh/siblings.js.map +1 -0
  271. package/node_modules/remote-pi/dist/mesh/types.d.ts +34 -0
  272. package/node_modules/remote-pi/dist/mesh/types.js +11 -0
  273. package/node_modules/remote-pi/dist/mesh/types.js.map +1 -0
  274. package/node_modules/remote-pi/dist/mesh/verify.d.ts +17 -0
  275. package/node_modules/remote-pi/dist/mesh/verify.js +86 -0
  276. package/node_modules/remote-pi/dist/mesh/verify.js.map +1 -0
  277. package/node_modules/remote-pi/dist/pairing/crypto.d.ts +8 -0
  278. package/node_modules/remote-pi/dist/pairing/crypto.js +22 -0
  279. package/node_modules/remote-pi/dist/pairing/crypto.js.map +1 -0
  280. package/node_modules/remote-pi/dist/pairing/handshake.d.ts +28 -0
  281. package/node_modules/remote-pi/dist/pairing/handshake.js +113 -0
  282. package/node_modules/remote-pi/dist/pairing/handshake.js.map +1 -0
  283. package/node_modules/remote-pi/dist/pairing/noise-sha256.d.ts +16 -0
  284. package/node_modules/remote-pi/dist/pairing/noise-sha256.js +103 -0
  285. package/node_modules/remote-pi/dist/pairing/noise-sha256.js.map +1 -0
  286. package/node_modules/remote-pi/dist/pairing/qr.d.ts +59 -0
  287. package/node_modules/remote-pi/dist/pairing/qr.js +125 -0
  288. package/node_modules/remote-pi/dist/pairing/qr.js.map +1 -0
  289. package/node_modules/remote-pi/dist/pairing/storage.d.ts +120 -0
  290. package/node_modules/remote-pi/dist/pairing/storage.js +604 -0
  291. package/node_modules/remote-pi/dist/pairing/storage.js.map +1 -0
  292. package/node_modules/remote-pi/dist/protocol/codec.d.ts +7 -0
  293. package/node_modules/remote-pi/dist/protocol/codec.js +49 -0
  294. package/node_modules/remote-pi/dist/protocol/codec.js.map +1 -0
  295. package/node_modules/remote-pi/dist/protocol/types.d.ts +393 -0
  296. package/node_modules/remote-pi/dist/protocol/types.js +2 -0
  297. package/node_modules/remote-pi/dist/protocol/types.js.map +1 -0
  298. package/node_modules/remote-pi/dist/rooms.d.ts +29 -0
  299. package/node_modules/remote-pi/dist/rooms.js +57 -0
  300. package/node_modules/remote-pi/dist/rooms.js.map +1 -0
  301. package/node_modules/remote-pi/dist/session/address.d.ts +49 -0
  302. package/node_modules/remote-pi/dist/session/address.js +58 -0
  303. package/node_modules/remote-pi/dist/session/address.js.map +1 -0
  304. package/node_modules/remote-pi/dist/session/agent_bridge.d.ts +55 -0
  305. package/node_modules/remote-pi/dist/session/agent_bridge.js +146 -0
  306. package/node_modules/remote-pi/dist/session/agent_bridge.js.map +1 -0
  307. package/node_modules/remote-pi/dist/session/bridge.d.ts +37 -0
  308. package/node_modules/remote-pi/dist/session/bridge.js +130 -0
  309. package/node_modules/remote-pi/dist/session/bridge.js.map +1 -0
  310. package/node_modules/remote-pi/dist/session/broker.d.ts +189 -0
  311. package/node_modules/remote-pi/dist/session/broker.js +507 -0
  312. package/node_modules/remote-pi/dist/session/broker.js.map +1 -0
  313. package/node_modules/remote-pi/dist/session/broker_remote.d.ts +74 -0
  314. package/node_modules/remote-pi/dist/session/broker_remote.js +583 -0
  315. package/node_modules/remote-pi/dist/session/broker_remote.js.map +1 -0
  316. package/node_modules/remote-pi/dist/session/cwd_lock.d.ts +33 -0
  317. package/node_modules/remote-pi/dist/session/cwd_lock.js +126 -0
  318. package/node_modules/remote-pi/dist/session/cwd_lock.js.map +1 -0
  319. package/node_modules/remote-pi/dist/session/envelope.d.ts +33 -0
  320. package/node_modules/remote-pi/dist/session/envelope.js +117 -0
  321. package/node_modules/remote-pi/dist/session/envelope.js.map +1 -0
  322. package/node_modules/remote-pi/dist/session/global_config.d.ts +33 -0
  323. package/node_modules/remote-pi/dist/session/global_config.js +73 -0
  324. package/node_modules/remote-pi/dist/session/global_config.js.map +1 -0
  325. package/node_modules/remote-pi/dist/session/ipc.d.ts +27 -0
  326. package/node_modules/remote-pi/dist/session/ipc.js +22 -0
  327. package/node_modules/remote-pi/dist/session/ipc.js.map +1 -0
  328. package/node_modules/remote-pi/dist/session/leader_election.d.ts +32 -0
  329. package/node_modules/remote-pi/dist/session/leader_election.js +106 -0
  330. package/node_modules/remote-pi/dist/session/leader_election.js.map +1 -0
  331. package/node_modules/remote-pi/dist/session/local_config.d.ts +55 -0
  332. package/node_modules/remote-pi/dist/session/local_config.js +157 -0
  333. package/node_modules/remote-pi/dist/session/local_config.js.map +1 -0
  334. package/node_modules/remote-pi/dist/session/mesh_message.d.ts +20 -0
  335. package/node_modules/remote-pi/dist/session/mesh_message.js +28 -0
  336. package/node_modules/remote-pi/dist/session/mesh_message.js.map +1 -0
  337. package/node_modules/remote-pi/dist/session/mesh_node.d.ts +116 -0
  338. package/node_modules/remote-pi/dist/session/mesh_node.js +573 -0
  339. package/node_modules/remote-pi/dist/session/mesh_node.js.map +1 -0
  340. package/node_modules/remote-pi/dist/session/peer.d.ts +139 -0
  341. package/node_modules/remote-pi/dist/session/peer.js +451 -0
  342. package/node_modules/remote-pi/dist/session/peer.js.map +1 -0
  343. package/node_modules/remote-pi/dist/session/peer_inventory.d.ts +13 -0
  344. package/node_modules/remote-pi/dist/session/peer_inventory.js +48 -0
  345. package/node_modules/remote-pi/dist/session/peer_inventory.js.map +1 -0
  346. package/node_modules/remote-pi/dist/session/peer_limits.d.ts +9 -0
  347. package/node_modules/remote-pi/dist/session/peer_limits.js +21 -0
  348. package/node_modules/remote-pi/dist/session/peer_limits.js.map +1 -0
  349. package/node_modules/remote-pi/dist/session/setup_wizard.d.ts +39 -0
  350. package/node_modules/remote-pi/dist/session/setup_wizard.js +63 -0
  351. package/node_modules/remote-pi/dist/session/setup_wizard.js.map +1 -0
  352. package/node_modules/remote-pi/dist/session/tool_gate.d.ts +5 -0
  353. package/node_modules/remote-pi/dist/session/tool_gate.js +11 -0
  354. package/node_modules/remote-pi/dist/session/tool_gate.js.map +1 -0
  355. package/node_modules/remote-pi/dist/session/tools.d.ts +24 -0
  356. package/node_modules/remote-pi/dist/session/tools.js +252 -0
  357. package/node_modules/remote-pi/dist/session/tools.js.map +1 -0
  358. package/node_modules/remote-pi/dist/session/wizard.d.ts +13 -0
  359. package/node_modules/remote-pi/dist/session/wizard.js +20 -0
  360. package/node_modules/remote-pi/dist/session/wizard.js.map +1 -0
  361. package/node_modules/remote-pi/dist/settings.d.ts +15 -0
  362. package/node_modules/remote-pi/dist/settings.js +52 -0
  363. package/node_modules/remote-pi/dist/settings.js.map +1 -0
  364. package/node_modules/remote-pi/dist/transport/peer_channel.d.ts +37 -0
  365. package/node_modules/remote-pi/dist/transport/peer_channel.js +98 -0
  366. package/node_modules/remote-pi/dist/transport/peer_channel.js.map +1 -0
  367. package/node_modules/remote-pi/dist/transport/pi_forward_client.d.ts +29 -0
  368. package/node_modules/remote-pi/dist/transport/pi_forward_client.js +62 -0
  369. package/node_modules/remote-pi/dist/transport/pi_forward_client.js.map +1 -0
  370. package/node_modules/remote-pi/dist/transport/relay_client.d.ts +91 -0
  371. package/node_modules/remote-pi/dist/transport/relay_client.js +206 -0
  372. package/node_modules/remote-pi/dist/transport/relay_client.js.map +1 -0
  373. package/node_modules/remote-pi/dist/ui/footer.d.ts +32 -0
  374. package/node_modules/remote-pi/dist/ui/footer.js +34 -0
  375. package/node_modules/remote-pi/dist/ui/footer.js.map +1 -0
  376. package/node_modules/remote-pi/docs/daemon.md +289 -0
  377. package/node_modules/remote-pi/package.json +88 -0
  378. package/node_modules/remote-pi/service-templates/launchd.plist.template +35 -0
  379. package/node_modules/remote-pi/service-templates/systemd.service.template +19 -0
  380. package/node_modules/remote-pi/service-templates/task-launcher.vbs.template +10 -0
  381. package/node_modules/remote-pi/service-templates/task-scheduler.xml.template +38 -0
  382. package/node_modules/remote-pi/skills/agent-network/SKILL.md +310 -0
  383. package/package.json +25 -10
  384. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool-diff/write-display-utils.ts +0 -29
  385. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{session-reference-core.ts → reference/session.ts} +0 -0
  386. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{pi-aliases.ts → shell/aliases.ts} +0 -0
  387. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/ATTRIBUTION.md +0 -0
  388. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/diff-presentation.ts +0 -0
  389. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/index.ts +0 -0
  390. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/render-utils.ts +0 -0
  391. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/shiki-highlight.ts +0 -0
  392. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/types.ts +0 -0
@@ -1,3 +1,4 @@
1
+ import { existsSync, readFileSync } from "node:fs";
1
2
  import { Readability } from "@mozilla/readability";
2
3
  import { resizeImage } from "@earendil-works/pi-coding-agent";
3
4
  import { parseHTML } from "linkedom";
@@ -22,14 +23,20 @@ import { isVideoFile, extractVideo, extractVideoFrame, getLocalVideoDuration } f
22
23
  import { appendDeclaredWebLinks, discoverDeclaredWebLinks, type DeclaredWebLink } from "./declared-web-links.ts";
23
24
  import { fetchRemoteUrl, loadFetchContentDomainPolicy, loadSsrfConfig, validateRemoteUrl, type Lookup } from "./ssrf-protection.ts";
24
25
  import { formatSeconds, getWebSearchConfigPath } from "./utils.ts";
26
+ import { isImageEnabled } from "./feature-config.ts";
25
27
 
26
28
  const DEFAULT_TIMEOUT_MS = 30000;
27
29
  const CONCURRENT_LIMIT = 3;
28
30
 
29
- const NON_RECOVERABLE_ERRORS = ["Unsupported content type", "Response too large"];
31
+ const NON_RECOVERABLE_ERRORS = ["Unsupported content type", "Response too large", "PDF extraction is disabled", "Image fetching is disabled"];
30
32
  const MIN_USEFUL_CONTENT = 500;
31
33
  const SUPPORTED_IMAGE_TYPES = new Set(["image/png", "image/jpeg", "image/webp", "image/gif"]);
32
34
  const WEB_SEARCH_CONFIG_PATH = getWebSearchConfigPath();
35
+ const FETCH_PROVIDERS = ["http", "firecrawl", "jina", "tinyfish", "search1api", "querit", "kagi", "ollama", "parallel", "brightdata", "gemini"] as const;
36
+ type FetchProvider = typeof FETCH_PROVIDERS[number];
37
+ type FetchRouting = { providers: FetchProvider[]; allowRemoteHostedProviders: boolean };
38
+ const DEFAULT_FETCH_PROVIDER_ORDER: FetchProvider[] = ["http", "firecrawl", "jina", "tinyfish", "search1api", "querit", "kagi", "ollama", "parallel", "brightdata", "gemini"];
39
+ const REMOTE_HOSTED_FETCH_PROVIDERS = new Set<FetchProvider>(["jina", "tinyfish", "search1api", "querit", "kagi", "ollama", "parallel", "brightdata", "gemini"]);
33
40
 
34
41
  export { loadSsrfConfig } from "./ssrf-protection.ts";
35
42
 
@@ -49,6 +56,78 @@ function isAbortError(err: unknown): boolean {
49
56
  return errorMessage(err).toLowerCase().includes("abort");
50
57
  }
51
58
 
59
+ function isRedirectPolicyError(message: string): boolean {
60
+ return message.startsWith("Blocked internal ") ||
61
+ message.startsWith("Blocked hostname by fetch_content domain policy") ||
62
+ message.startsWith("Hostname not allowed by fetch_content domain policy") ||
63
+ message.startsWith("Too many redirects fetching ") ||
64
+ message === "Only HTTP and HTTPS URLs can be fetched remotely" ||
65
+ message === "URL must include a hostname" ||
66
+ message.startsWith("Failed to resolve ");
67
+ }
68
+
69
+ function imageGateError(): string | null {
70
+ try {
71
+ return isImageEnabled() ? null : "Image fetching is disabled by image.enabled";
72
+ } catch (err) {
73
+ return errorMessage(err);
74
+ }
75
+ }
76
+
77
+ function loadFetchRouting(): FetchRouting {
78
+ if (!existsSync(WEB_SEARCH_CONFIG_PATH)) {
79
+ return { providers: DEFAULT_FETCH_PROVIDER_ORDER, allowRemoteHostedProviders: false };
80
+ }
81
+
82
+ let raw: Record<string, unknown>;
83
+ try {
84
+ const parsed: unknown = JSON.parse(readFileSync(WEB_SEARCH_CONFIG_PATH, "utf-8"));
85
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
86
+ throw new Error("expected a JSON object");
87
+ }
88
+ raw = parsed as Record<string, unknown>;
89
+ } catch (err) {
90
+ const message = err instanceof Error ? err.message : String(err);
91
+ throw new Error(`Failed to parse ${WEB_SEARCH_CONFIG_PATH}: ${message}`);
92
+ }
93
+
94
+ if (!Object.hasOwn(raw, "fetchRouting")) {
95
+ return { providers: DEFAULT_FETCH_PROVIDER_ORDER, allowRemoteHostedProviders: false };
96
+ }
97
+ const routing = raw.fetchRouting;
98
+ if (!routing || typeof routing !== "object" || Array.isArray(routing)) {
99
+ throw new Error(`fetchRouting in ${WEB_SEARCH_CONFIG_PATH} must be an object`);
100
+ }
101
+
102
+ const routingConfig = routing as Record<string, unknown>;
103
+ const providersValue = routingConfig.providers;
104
+ let providers = DEFAULT_FETCH_PROVIDER_ORDER;
105
+ if (providersValue !== undefined) {
106
+ if (!Array.isArray(providersValue) || providersValue.length === 0) {
107
+ throw new Error(`fetchRouting.providers in ${WEB_SEARCH_CONFIG_PATH} must be a non-empty array`);
108
+ }
109
+
110
+ providers = [];
111
+ for (const provider of providersValue) {
112
+ const normalized = typeof provider === "string" ? provider.trim().toLowerCase() : "";
113
+ if (!FETCH_PROVIDERS.includes(normalized as FetchProvider)) {
114
+ throw new Error(`fetchRouting.providers in ${WEB_SEARCH_CONFIG_PATH} contains an invalid provider: ${String(provider)}`);
115
+ }
116
+ if (providers.includes(normalized as FetchProvider)) {
117
+ throw new Error(`fetchRouting.providers in ${WEB_SEARCH_CONFIG_PATH} must not contain duplicates: ${normalized}`);
118
+ }
119
+ providers.push(normalized as FetchProvider);
120
+ }
121
+ }
122
+
123
+ const allowRemoteHostedProvidersValue = routingConfig.allowRemoteHostedProviders;
124
+ if (allowRemoteHostedProvidersValue !== undefined && typeof allowRemoteHostedProvidersValue !== "boolean") {
125
+ throw new Error(`fetchRouting.allowRemoteHostedProviders in ${WEB_SEARCH_CONFIG_PATH} must be a boolean`);
126
+ }
127
+
128
+ return { providers, allowRemoteHostedProviders: allowRemoteHostedProvidersValue === true };
129
+ }
130
+
52
131
  function abortedResult(url: string): ExtractedContent {
53
132
  return { url, title: "", content: "", error: "Aborted" };
54
133
  }
@@ -276,6 +355,11 @@ export async function extractContent(
276
355
  return extractViaHttp(url, signal, options);
277
356
  }
278
357
 
358
+ if (options?.frames || options?.timestamp) {
359
+ const disabled = imageGateError();
360
+ if (disabled) return { url, title: "", content: "", error: disabled };
361
+ }
362
+
279
363
  if (options?.frames && !options.timestamp) {
280
364
  const frameCount = options.frames;
281
365
  const ytInfo = isYouTubeURL(url);
@@ -470,175 +554,227 @@ export async function extractContent(
470
554
 
471
555
  if (signal?.aborted) return abortedResult(url);
472
556
 
473
- const { declaredLinks = [], ...httpResult } = await extractViaHttp(url, signal, options);
557
+ let fetchRouting: FetchRouting;
558
+ try {
559
+ fetchRouting = loadFetchRouting();
560
+ } catch (err) {
561
+ return { url, title: "", content: "", error: errorMessage(err) };
562
+ }
563
+ const providerOrder = remoteUrl && !fetchRouting.allowRemoteHostedProviders
564
+ ? fetchRouting.providers.filter(provider => !REMOTE_HOSTED_FETCH_PROVIDERS.has(provider))
565
+ : fetchRouting.providers;
566
+ if (providerOrder.length === 0) {
567
+ return {
568
+ url,
569
+ title: "",
570
+ content: "",
571
+ error: "Remote hosted fetch providers are disabled unless fetchRouting.allowRemoteHostedProviders is true",
572
+ };
573
+ }
574
+
575
+ let httpResult: ExtractedContent | null = null;
576
+ let declaredLinks: DeclaredWebLink[] = [];
474
577
  const withDeclaredLinks = (result: ExtractedContent): ExtractedContent => ({
475
578
  ...result,
476
579
  content: appendDeclaredWebLinks(result.content, declaredLinks),
477
580
  });
478
-
479
- if (signal?.aborted) return abortedResult(url);
480
- if (!httpResult.error) return httpResult;
481
- if (NON_RECOVERABLE_ERRORS.some(prefix => httpResult.error!.startsWith(prefix))) return httpResult;
581
+ const parseErrorResult = (message: string): ExtractedContent => httpResult
582
+ ? { ...httpResult, error: message }
583
+ : { url, title: "", content: "", error: message };
584
+ const runHttpProvider = async (): Promise<ExtractedContent | null> => {
585
+ const { declaredLinks: discoveredLinks = [], ...result } = await extractViaHttp(url, signal, options);
586
+ httpResult = result;
587
+ declaredLinks = discoveredLinks;
588
+ if (signal?.aborted) return abortedResult(url);
589
+ if (!httpResult.error) return httpResult;
590
+ if (NON_RECOVERABLE_ERRORS.some(prefix => httpResult!.error!.startsWith(prefix)) || isRedirectPolicyError(httpResult.error) || isConfigParseError(httpResult.error)) {
591
+ return httpResult;
592
+ }
593
+ return null;
594
+ };
482
595
 
483
596
  let firecrawlError: string | null = null;
484
- try {
485
- if (isFirecrawlAvailable()) {
486
- const ssrf = loadSsrfConfig();
487
- const firecrawlResult = await extractWithFirecrawl(url, signal, {
488
- timeoutMs: options?.timeoutMs,
489
- ...(options?.lookup ? { lookup: options.lookup } : {}),
490
- ssrf,
491
- });
492
- if (firecrawlResult) return withDeclaredLinks(firecrawlResult);
493
- }
494
- } catch (err) {
495
- if (isAbortError(err)) return abortedResult(url);
496
- firecrawlError = errorMessage(err);
497
- if (isConfigParseError(err)) return { ...httpResult, error: firecrawlError };
597
+ let tinyfishError: string | null = null;
598
+ let search1apiError: string | null = null;
599
+ let queritError: string | null = null;
600
+ let kagiError: string | null = null;
601
+ let ollamaError: string | null = null;
602
+ let parallelError: string | null = null;
603
+ let brightdataError: string | null = null;
604
+
605
+ if (remoteUrl && providerOrder[0] !== "http") {
606
+ const httpGateResult = await runHttpProvider();
607
+ if (httpGateResult) return httpGateResult;
498
608
  }
499
- if (signal?.aborted) return abortedResult(url);
500
609
 
501
- const jinaResult = await extractWithJinaReader(url, signal, options?.lookup);
502
- if (jinaResult) return withDeclaredLinks(jinaResult);
503
- if (signal?.aborted) return abortedResult(url);
610
+ for (const provider of providerOrder) {
611
+ if (signal?.aborted) return abortedResult(url);
504
612
 
505
- let tinyfishError: string | null = null;
506
- try {
507
- if (isTinyFishAvailable()) {
508
- const tinyfishResult = await extractWithTinyFish(url, signal, options);
509
- if (tinyfishResult) return withDeclaredLinks(tinyfishResult);
510
- }
511
- } catch (err) {
512
- if (isAbortError(err)) return abortedResult(url);
513
- tinyfishError = errorMessage(err);
514
- if (isConfigParseError(err)) {
515
- return { ...httpResult, error: tinyfishError };
613
+ if (provider === "http") {
614
+ const result = await runHttpProvider();
615
+ if (result) return result;
616
+ continue;
516
617
  }
517
- }
518
- if (signal?.aborted) return abortedResult(url);
519
618
 
520
- let search1apiError: string | null = null;
521
- try {
522
- if (isSearch1APIAvailable()) {
523
- const search1apiResult = await extractWithSearch1API(url, signal, options);
524
- if (search1apiResult) return withDeclaredLinks(search1apiResult);
525
- }
526
- } catch (err) {
527
- if (isAbortError(err)) return abortedResult(url);
528
- search1apiError = errorMessage(err);
529
- if (isConfigParseError(err)) {
530
- return { ...httpResult, error: search1apiError };
619
+ if (provider === "firecrawl") {
620
+ try {
621
+ if (isFirecrawlAvailable()) {
622
+ const ssrf = loadSsrfConfig();
623
+ const firecrawlResult = await extractWithFirecrawl(url, signal, {
624
+ timeoutMs: options?.timeoutMs,
625
+ ...(options?.lookup ? { lookup: options.lookup } : {}),
626
+ ssrf,
627
+ });
628
+ if (firecrawlResult) return withDeclaredLinks(firecrawlResult);
629
+ }
630
+ } catch (err) {
631
+ if (isAbortError(err)) return abortedResult(url);
632
+ firecrawlError = errorMessage(err);
633
+ if (isConfigParseError(err)) return parseErrorResult(firecrawlError);
634
+ }
635
+ continue;
531
636
  }
532
- }
533
- if (signal?.aborted) return abortedResult(url);
534
637
 
535
- let queritError: string | null = null;
536
- try {
537
- if (isQueritAvailable()) {
538
- const queritResult = await extractWithQuerit(url, signal, options);
539
- if (queritResult) return withDeclaredLinks(queritResult);
638
+ if (provider === "jina") {
639
+ const jinaResult = await extractWithJinaReader(url, signal, options?.lookup);
640
+ if (jinaResult) return withDeclaredLinks(jinaResult);
641
+ continue;
540
642
  }
541
- } catch (err) {
542
- if (isAbortError(err)) return abortedResult(url);
543
- queritError = errorMessage(err);
544
- if (isConfigParseError(err)) {
545
- return { ...httpResult, error: queritError };
546
- }
547
- }
548
- if (signal?.aborted) return abortedResult(url);
549
643
 
550
- let kagiError: string | null = null;
551
- try {
552
- if (isKagiExtractAvailable()) {
553
- const ssrf = loadSsrfConfig();
554
- const kagiResult = await extractWithKagi(url, signal, {
555
- timeoutMs: options?.timeoutMs,
556
- ...(options?.lookup ? { lookup: options.lookup } : {}),
557
- ssrf,
558
- });
559
- if (kagiResult) return withDeclaredLinks(kagiResult);
560
- }
561
- } catch (err) {
562
- if (isAbortError(err)) return abortedResult(url);
563
- kagiError = errorMessage(err);
564
- if (isConfigParseError(err)) {
565
- return { ...httpResult, error: kagiError };
644
+ if (provider === "tinyfish") {
645
+ try {
646
+ if (isTinyFishAvailable()) {
647
+ const tinyfishResult = await extractWithTinyFish(url, signal, options);
648
+ if (tinyfishResult) return withDeclaredLinks(tinyfishResult);
649
+ }
650
+ } catch (err) {
651
+ if (isAbortError(err)) return abortedResult(url);
652
+ tinyfishError = errorMessage(err);
653
+ if (isConfigParseError(err)) return parseErrorResult(tinyfishError);
654
+ }
655
+ continue;
566
656
  }
567
- }
568
- if (signal?.aborted) return abortedResult(url);
569
657
 
570
- let ollamaError: string | null = null;
571
- try {
572
- if (isOllamaFetchAvailable()) {
573
- const ssrf = loadSsrfConfig();
574
- const ollamaResult = await extractWithOllama(url, signal, {
575
- timeoutMs: options?.timeoutMs,
576
- ...(options?.lookup ? { lookup: options.lookup } : {}),
577
- ssrf,
578
- });
579
- if (ollamaResult) return withDeclaredLinks(ollamaResult);
658
+ if (provider === "search1api") {
659
+ try {
660
+ if (isSearch1APIAvailable()) {
661
+ const search1apiResult = await extractWithSearch1API(url, signal, options);
662
+ if (search1apiResult) return withDeclaredLinks(search1apiResult);
663
+ }
664
+ } catch (err) {
665
+ if (isAbortError(err)) return abortedResult(url);
666
+ search1apiError = errorMessage(err);
667
+ if (isConfigParseError(err)) return parseErrorResult(search1apiError);
668
+ }
669
+ continue;
580
670
  }
581
- } catch (err) {
582
- if (isAbortError(err)) return abortedResult(url);
583
- ollamaError = errorMessage(err);
584
- if (isConfigParseError(err)) {
585
- return { ...httpResult, error: ollamaError };
671
+
672
+ if (provider === "querit") {
673
+ try {
674
+ if (isQueritAvailable()) {
675
+ const queritResult = await extractWithQuerit(url, signal, options);
676
+ if (queritResult) return withDeclaredLinks(queritResult);
677
+ }
678
+ } catch (err) {
679
+ if (isAbortError(err)) return abortedResult(url);
680
+ queritError = errorMessage(err);
681
+ if (isConfigParseError(err)) return parseErrorResult(queritError);
682
+ }
683
+ continue;
586
684
  }
587
- }
588
- if (signal?.aborted) return abortedResult(url);
589
685
 
590
- let parallelError: string | null = null;
591
- try {
592
- if (isParallelAvailable()) {
593
- const parallelResult = await extractWithParallel(url, signal, options);
594
- if (parallelResult) return withDeclaredLinks(parallelResult);
686
+ if (provider === "kagi") {
687
+ try {
688
+ if (isKagiExtractAvailable()) {
689
+ const ssrf = loadSsrfConfig();
690
+ const kagiResult = await extractWithKagi(url, signal, {
691
+ timeoutMs: options?.timeoutMs,
692
+ ...(options?.lookup ? { lookup: options.lookup } : {}),
693
+ ssrf,
694
+ });
695
+ if (kagiResult) return withDeclaredLinks(kagiResult);
696
+ }
697
+ } catch (err) {
698
+ if (isAbortError(err)) return abortedResult(url);
699
+ kagiError = errorMessage(err);
700
+ if (isConfigParseError(err)) return parseErrorResult(kagiError);
701
+ }
702
+ continue;
595
703
  }
596
- } catch (err) {
597
- if (isAbortError(err)) return abortedResult(url);
598
- parallelError = errorMessage(err);
599
- if (isConfigParseError(err)) {
600
- return { ...httpResult, error: parallelError };
704
+
705
+ if (provider === "ollama") {
706
+ try {
707
+ if (isOllamaFetchAvailable()) {
708
+ const ssrf = loadSsrfConfig();
709
+ const ollamaResult = await extractWithOllama(url, signal, {
710
+ timeoutMs: options?.timeoutMs,
711
+ ...(options?.lookup ? { lookup: options.lookup } : {}),
712
+ ssrf,
713
+ });
714
+ if (ollamaResult) return withDeclaredLinks(ollamaResult);
715
+ }
716
+ } catch (err) {
717
+ if (isAbortError(err)) return abortedResult(url);
718
+ ollamaError = errorMessage(err);
719
+ if (isConfigParseError(err)) return parseErrorResult(ollamaError);
720
+ }
721
+ continue;
601
722
  }
602
- }
603
- if (signal?.aborted) return abortedResult(url);
604
723
 
605
- let brightdataError: string | null = null;
606
- try {
607
- if (isBrightDataUnlockerAvailable()) {
608
- const ssrf = loadSsrfConfig();
609
- const brightdataResult = await extractWithBrightDataUnlocker(url, signal, {
610
- timeoutMs: options?.timeoutMs,
611
- ...(options?.lookup ? { lookup: options.lookup } : {}),
612
- ssrf,
613
- });
614
- if (brightdataResult) return withDeclaredLinks(brightdataResult);
724
+ if (provider === "parallel") {
725
+ try {
726
+ if (isParallelAvailable()) {
727
+ const parallelResult = await extractWithParallel(url, signal, options);
728
+ if (parallelResult) return withDeclaredLinks(parallelResult);
729
+ }
730
+ } catch (err) {
731
+ if (isAbortError(err)) return abortedResult(url);
732
+ parallelError = errorMessage(err);
733
+ if (isConfigParseError(err)) return parseErrorResult(parallelError);
734
+ }
735
+ continue;
615
736
  }
616
- } catch (err) {
617
- if (isAbortError(err)) return abortedResult(url);
618
- brightdataError = errorMessage(err);
619
- if (isConfigParseError(err)) {
620
- return { ...httpResult, error: brightdataError };
737
+
738
+ if (provider === "brightdata") {
739
+ try {
740
+ if (isBrightDataUnlockerAvailable()) {
741
+ const ssrf = loadSsrfConfig();
742
+ const brightdataResult = await extractWithBrightDataUnlocker(url, signal, {
743
+ timeoutMs: options?.timeoutMs,
744
+ ...(options?.lookup ? { lookup: options.lookup } : {}),
745
+ ssrf,
746
+ });
747
+ if (brightdataResult) return withDeclaredLinks(brightdataResult);
748
+ }
749
+ } catch (err) {
750
+ if (isAbortError(err)) return abortedResult(url);
751
+ brightdataError = errorMessage(err);
752
+ if (isConfigParseError(err)) return parseErrorResult(brightdataError);
753
+ }
754
+ continue;
621
755
  }
622
- }
623
- if (signal?.aborted) return abortedResult(url);
624
756
 
625
- let geminiResult: ExtractedContent | null = null;
626
- try {
627
- geminiResult = await extractWithUrlContext(url, signal)
628
- ?? await extractWithGeminiWeb(url, signal);
629
- } catch (err) {
630
- if (isAbortError(err)) return abortedResult(url);
631
- if (err instanceof CredentialResolutionError || isConfigParseError(err)) {
632
- return { ...httpResult, error: errorMessage(err) };
757
+ if (provider === "gemini") {
758
+ let geminiResult: ExtractedContent | null = null;
759
+ try {
760
+ geminiResult = await extractWithUrlContext(url, signal)
761
+ ?? await extractWithGeminiWeb(url, signal);
762
+ } catch (err) {
763
+ if (isAbortError(err)) return abortedResult(url);
764
+ if (err instanceof CredentialResolutionError || isConfigParseError(err)) {
765
+ return parseErrorResult(errorMessage(err));
766
+ }
767
+ }
768
+ if (geminiResult) return withDeclaredLinks(geminiResult);
633
769
  }
634
770
  }
635
771
 
636
- if (geminiResult) return withDeclaredLinks(geminiResult);
637
772
  if (signal?.aborted) return abortedResult(url);
638
- if (declaredLinks.length > 0) return { ...httpResult, error: null };
773
+ const finalHttpResult = httpResult as ExtractedContent | null;
774
+ if (finalHttpResult && declaredLinks.length > 0) return { ...finalHttpResult, error: null };
639
775
 
640
776
  const guidance = [
641
- httpResult.error,
777
+ finalHttpResult?.error ?? "No fetch_content provider returned content",
642
778
  ...(firecrawlError ? [`Firecrawl fallback failed: ${firecrawlError}`] : []),
643
779
  ...(tinyfishError ? [`TinyFish fallback failed: ${tinyfishError}`] : []),
644
780
  ...(search1apiError ? [`Search1API fallback failed: ${search1apiError}`] : []),
@@ -649,7 +785,7 @@ export async function extractContent(
649
785
  ...(brightdataError ? [`Bright Data fallback failed: ${brightdataError}`] : []),
650
786
  "",
651
787
  "Fallback options:",
652
- ` \u2022 Set firecrawlBaseUrl in ${WEB_SEARCH_CONFIG_PATH} to a self-hosted Firecrawl instance`,
788
+ ` • Set firecrawlBaseUrl in ${WEB_SEARCH_CONFIG_PATH} to a self-hosted Firecrawl instance`,
653
789
  ` • Set tinyfishApiKey in ${WEB_SEARCH_CONFIG_PATH} or TINYFISH_API_KEY`,
654
790
  ` • Set search1apiApiKey in ${WEB_SEARCH_CONFIG_PATH} or SEARCH1API_KEY`,
655
791
  ` • Set queritApiKey in ${WEB_SEARCH_CONFIG_PATH} or QUERIT_API_KEY`,
@@ -657,11 +793,11 @@ export async function extractContent(
657
793
  ` • Set ollamaApiKey in ${WEB_SEARCH_CONFIG_PATH} or OLLAMA_API_KEY`,
658
794
  ` • Set parallelApiKey in ${WEB_SEARCH_CONFIG_PATH} or PARALLEL_API_KEY`,
659
795
  ` • Set brightdataApiKey and brightdataUnlockerZone in ${WEB_SEARCH_CONFIG_PATH} or BRIGHTDATA_API_KEY and BRIGHTDATA_UNLOCKER_ZONE`,
660
- ` \u2022 Set GEMINI_API_KEY in ${WEB_SEARCH_CONFIG_PATH}`,
661
- " \u2022 Sign into gemini.google.com in Chrome",
662
- " \u2022 Use web_search to find content about this topic",
796
+ ` • Set GEMINI_API_KEY in ${WEB_SEARCH_CONFIG_PATH}`,
797
+ " • Sign into gemini.google.com in Chrome",
798
+ " • Use web_search to find content about this topic",
663
799
  ].join("\n");
664
- return { ...httpResult, error: guidance };
800
+ return { ...(finalHttpResult ?? { url, title: "", content: "", error: null }), error: guidance };
665
801
  }
666
802
 
667
803
  function isLikelyJSRendered(html: string): boolean {
@@ -818,6 +954,10 @@ async function extractViaHttp(
818
954
  const mimeType = contentType.split(";", 1)[0]?.trim().toLowerCase() ?? "";
819
955
  const isPDFContent = isPDF(url, contentType);
820
956
  const pdfConfig = isPDFContent ? loadPDFConfig() : null;
957
+ if (isPDFContent && pdfConfig && !pdfConfig.enabled) {
958
+ activityMonitor.logComplete(activityId, response.status);
959
+ return { url, title: "", content: "", error: "PDF extraction is disabled by pdf.enabled", mimeType, status: response.status };
960
+ }
821
961
  const maxResponseSize = (pdfConfig?.maxSizeMB ?? 5) * 1024 * 1024;
822
962
  if (contentLengthHeader) {
823
963
  const contentLength = Number.parseInt(contentLengthHeader, 10);
@@ -845,6 +985,11 @@ async function extractViaHttp(
845
985
  }
846
986
 
847
987
  if (SUPPORTED_IMAGE_TYPES.has(mimeType)) {
988
+ const disabled = imageGateError();
989
+ if (disabled) {
990
+ activityMonitor.logComplete(activityId, response.status);
991
+ return { url, title: "", content: "", error: disabled, mimeType, status: response.status };
992
+ }
848
993
  try {
849
994
  const buffer = await readResponseBufferWithLimit(response, maxResponseSize, () => responseSizeLimitError(maxResponseSize));
850
995
  const resized = await resizeImage(new Uint8Array(buffer), mimeType, { maxWidth: 2000, maxHeight: 2000 });
@@ -0,0 +1,29 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { getWebSearchConfigPath } from "./utils.ts";
3
+
4
+ const CONFIG_PATH = getWebSearchConfigPath();
5
+
6
+ type FeatureConfig = { image?: { enabled?: unknown } };
7
+
8
+ function loadFeatureConfig(): FeatureConfig {
9
+ if (!existsSync(CONFIG_PATH)) return {};
10
+ try {
11
+ const raw: unknown = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
12
+ return raw && typeof raw === "object" ? raw as FeatureConfig : {};
13
+ } catch (err) {
14
+ const message = err instanceof Error ? err.message : String(err);
15
+ throw new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);
16
+ }
17
+ }
18
+
19
+ export function isImageEnabled(): boolean {
20
+ return loadFeatureConfig().image?.enabled !== false;
21
+ }
22
+
23
+ export function canAttachImages(): boolean {
24
+ try {
25
+ return isImageEnabled();
26
+ } catch {
27
+ return false;
28
+ }
29
+ }
@@ -75,6 +75,10 @@ export function getVersionedApiBase(): string {
75
75
  return `${getApiHost()}/${API_VERSION}`;
76
76
  }
77
77
 
78
+ export function getUploadBase(): string {
79
+ return `${getApiHost()}/upload/${API_VERSION}`;
80
+ }
81
+
78
82
  function getLegacyCloudflareApiKey(): string | null {
79
83
  return normalizeApiKey(process.env.CLOUDFLARE_API_KEY) ?? normalizeApiKey(loadConfig().cloudflareApiKey);
80
84
  }