@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,8 +1,10 @@
1
1
  /**
2
2
  * PDF Content Extractor
3
3
  *
4
- * Uses Gemini for structured PDF-to-Markdown conversion when configured,
5
- * with unpdf as the deterministic local fallback.
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`.
6
8
  */
7
9
 
8
10
  import { existsSync, readFileSync } from "node:fs";
@@ -10,288 +12,385 @@ import { writeFile, mkdir } from "node:fs/promises";
10
12
  import { join, basename } from "node:path";
11
13
  import { tmpdir } from "node:os";
12
14
  import { CredentialResolutionError } from "./credential-source.ts";
15
+ import {
16
+ DATALAB_MODE_VALUES,
17
+ DEFAULT_DATALAB_TIMEOUT_MS,
18
+ isDatalabApiAvailable,
19
+ normalizeDatalabMode,
20
+ extractPDFViaDatalab,
21
+ type DatalabMode,
22
+ } from "./datalab-pdf-extract.ts";
13
23
  import { isGeminiApiAvailable } from "./gemini-api.ts";
14
24
  import { extractPDFViaGemini } from "./gemini-pdf-extract.ts";
15
25
  import { getWebSearchConfigPath } from "./utils.ts";
16
26
 
17
27
  export interface PDFExtractResult {
18
- title: string;
19
- pages: number;
20
- chars: number;
21
- outputPath: string;
28
+ title: string;
29
+ pages: number;
30
+ chars: number;
31
+ outputPath: string;
22
32
  }
23
33
 
24
34
  export interface PDFExtractOptions {
25
- maxPages?: number;
26
- outputDir?: string;
27
- filename?: string;
28
- signal?: AbortSignal;
29
- geminiTimeoutMs?: number;
35
+ maxPages?: number;
36
+ outputDir?: string;
37
+ filename?: string;
38
+ signal?: AbortSignal;
39
+ geminiTimeoutMs?: number;
30
40
  }
31
41
 
42
+ export type PDFProvider = "auto" | "gemini" | "datalab" | "unpdf";
43
+
44
+ export const PDF_PROVIDER_VALUES = new Set<PDFProvider>([
45
+ "auto",
46
+ "gemini",
47
+ "datalab",
48
+ "unpdf",
49
+ ]);
50
+
32
51
  export interface PDFConfig {
33
- maxSizeMB: number;
52
+ enabled: boolean;
53
+ maxSizeMB: number;
54
+ provider: PDFProvider;
55
+ datalabMode: DatalabMode;
56
+ datalabTimeoutMs: number;
34
57
  }
35
58
 
36
59
  export const DEFAULT_PDF_MAX_SIZE_MB = 20;
37
60
  export const MAX_PDF_MAX_SIZE_MB = 50;
61
+ export const MAX_DATALAB_TIMEOUT_MS = 300_000;
38
62
  const DEFAULT_MAX_PAGES = 100;
39
63
  const DEFAULT_OUTPUT_DIR = join(tmpdir(), "pi-web-pdf");
40
64
  const CONFIG_PATH = getWebSearchConfigPath();
41
65
  const PAGE_MARKER_PATTERN = /^<!-- Page (\d+) -->$/gm;
42
66
 
43
- let cachedPDFConfig: PDFConfig | null = null;
44
-
45
67
  export function loadPDFConfig(): PDFConfig {
46
- if (cachedPDFConfig) return { ...cachedPDFConfig };
47
- if (!existsSync(CONFIG_PATH)) {
48
- cachedPDFConfig = { maxSizeMB: DEFAULT_PDF_MAX_SIZE_MB };
49
- return { ...cachedPDFConfig };
50
- }
51
-
52
- const rawText = readFileSync(CONFIG_PATH, "utf-8");
53
- let raw: unknown;
54
- try {
55
- raw = JSON.parse(rawText) as unknown;
56
- } catch (err) {
57
- const message = err instanceof Error ? err.message : String(err);
58
- throw new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);
59
- }
60
-
61
- const root = raw && typeof raw === "object" ? raw as Record<string, unknown> : {};
62
- const pdf = root.pdf && typeof root.pdf === "object"
63
- ? root.pdf as Record<string, unknown>
64
- : {};
65
- const configured = pdf.maxSizeMB;
66
- const normalized = typeof configured === "number" && Number.isFinite(configured) && configured > 0
67
- ? Math.min(configured, MAX_PDF_MAX_SIZE_MB)
68
- : DEFAULT_PDF_MAX_SIZE_MB;
69
-
70
- cachedPDFConfig = { maxSizeMB: normalized };
71
- return { ...cachedPDFConfig };
68
+ if (!existsSync(CONFIG_PATH)) {
69
+ return {
70
+ enabled: true,
71
+ maxSizeMB: DEFAULT_PDF_MAX_SIZE_MB,
72
+ provider: "auto",
73
+ datalabMode: normalizeDatalabMode(process.env.DATALAB_MODE),
74
+ datalabTimeoutMs: DEFAULT_DATALAB_TIMEOUT_MS,
75
+ };
76
+ }
77
+
78
+ const rawText = readFileSync(CONFIG_PATH, "utf-8");
79
+ let raw: unknown;
80
+ try {
81
+ raw = JSON.parse(rawText) as unknown;
82
+ } catch (err) {
83
+ const message = err instanceof Error ? err.message : String(err);
84
+ throw new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);
85
+ }
86
+
87
+ const root =
88
+ raw && typeof raw === "object" ? (raw as Record<string, unknown>) : {};
89
+ const pdf =
90
+ root.pdf && typeof root.pdf === "object"
91
+ ? (root.pdf as Record<string, unknown>)
92
+ : {};
93
+ const enabled = pdf.enabled !== false;
94
+ const configured = pdf.maxSizeMB;
95
+ const normalized =
96
+ typeof configured === "number" &&
97
+ Number.isFinite(configured) &&
98
+ configured > 0
99
+ ? Math.min(configured, MAX_PDF_MAX_SIZE_MB)
100
+ : DEFAULT_PDF_MAX_SIZE_MB;
101
+
102
+ const provider =
103
+ typeof pdf.provider === "string" &&
104
+ PDF_PROVIDER_VALUES.has(pdf.provider as PDFProvider)
105
+ ? (pdf.provider as PDFProvider)
106
+ : "auto";
107
+ const datalabMode =
108
+ typeof pdf.datalabMode === "string" &&
109
+ DATALAB_MODE_VALUES.has(pdf.datalabMode as DatalabMode)
110
+ ? (pdf.datalabMode as DatalabMode)
111
+ : normalizeDatalabMode(process.env.DATALAB_MODE);
112
+ const configuredTimeout = pdf.datalabTimeoutMs;
113
+ const datalabTimeoutMs =
114
+ typeof configuredTimeout === "number" &&
115
+ Number.isFinite(configuredTimeout) &&
116
+ configuredTimeout > 0
117
+ ? Math.min(configuredTimeout, MAX_DATALAB_TIMEOUT_MS)
118
+ : DEFAULT_DATALAB_TIMEOUT_MS;
119
+
120
+ return {
121
+ enabled,
122
+ maxSizeMB: normalized,
123
+ provider,
124
+ datalabMode,
125
+ datalabTimeoutMs,
126
+ };
72
127
  }
73
128
 
74
129
  async function getUnpdf() {
75
- if (typeof (Promise as PromiseConstructor & { try?: unknown }).try !== "function") {
76
- const { default: promiseTry } = await import("promise.try");
77
- promiseTry.shim();
78
- }
79
-
80
- const [unpdf, pdfjs] = await Promise.all([
81
- import("unpdf"),
82
- import("unpdf/pdfjs"),
83
- ]);
84
- const { VerbosityLevel } = pdfjs as typeof pdfjs & { VerbosityLevel: { ERRORS: number } };
85
-
86
- return { getDocumentProxy: unpdf.getDocumentProxy, VerbosityLevel };
130
+ if (
131
+ typeof (Promise as PromiseConstructor & { try?: unknown }).try !==
132
+ "function"
133
+ ) {
134
+ const { default: promiseTry } = await import("promise.try");
135
+ promiseTry.shim();
136
+ }
137
+
138
+ const [unpdf, pdfjs] = await Promise.all([
139
+ import("unpdf"),
140
+ import("unpdf/pdfjs"),
141
+ ]);
142
+ const { VerbosityLevel } = pdfjs as typeof pdfjs & {
143
+ VerbosityLevel: { ERRORS: number };
144
+ };
145
+
146
+ return { getDocumentProxy: unpdf.getDocumentProxy, VerbosityLevel };
87
147
  }
88
148
 
89
149
  /**
90
150
  * Extract text from a PDF buffer and save it to a Markdown file.
91
151
  */
92
152
  export async function extractPDFToMarkdown(
93
- buffer: ArrayBuffer,
94
- url: string,
95
- options: PDFExtractOptions = {}
153
+ buffer: ArrayBuffer,
154
+ url: string,
155
+ options: PDFExtractOptions = {},
96
156
  ): Promise<PDFExtractResult> {
97
- const {
98
- maxPages = DEFAULT_MAX_PAGES,
99
- outputDir = DEFAULT_OUTPUT_DIR,
100
- filename,
101
- signal,
102
- geminiTimeoutMs,
103
- } = options;
104
-
105
- const safeMaxPages = Number.isFinite(maxPages)
106
- ? Math.max(1, Math.floor(maxPages))
107
- : DEFAULT_MAX_PAGES;
108
- const urlTitle = extractTitleFromURL(url);
109
-
110
- try {
111
- if (isGeminiApiAvailable()) {
112
- const markdownBody = await extractPDFViaGemini(buffer, {
113
- maxPages: safeMaxPages,
114
- title: urlTitle,
115
- ...(signal ? { signal } : {}),
116
- ...(geminiTimeoutMs !== undefined ? { timeoutMs: geminiTimeoutMs } : {}),
117
- });
118
- return writeMarkdownResult({
119
- markdownBody,
120
- title: urlTitle,
121
- pages: countPageMarkers(markdownBody),
122
- outputDir,
123
- filename,
124
- url,
125
- });
126
- }
127
- } catch (err) {
128
- if (shouldRethrowGeminiError(err, signal)) throw err;
129
- }
130
-
131
- const { getDocumentProxy, VerbosityLevel } = await getUnpdf();
132
- const pdf = await getDocumentProxy(new Uint8Array(buffer), {
133
- verbosity: VerbosityLevel.ERRORS,
134
- });
135
- const metadata = await pdf.getMetadata();
136
- const metadataInfo = metadata.info && typeof metadata.info === "object"
137
- ? metadata.info as Record<string, unknown>
138
- : null;
139
-
140
- const metaTitle = typeof metadataInfo?.Title === "string" ? metadataInfo.Title : undefined;
141
- const metaAuthor = typeof metadataInfo?.Author === "string" ? metadataInfo.Author : undefined;
142
- const title = metaTitle?.trim() || urlTitle;
143
- const pagesToExtract = Math.min(pdf.numPages, safeMaxPages);
144
- const truncated = pdf.numPages > safeMaxPages;
145
- const pages: { pageNum: number; text: string }[] = [];
146
-
147
- for (let i = 1; i <= pagesToExtract; i++) {
148
- const page = await pdf.getPage(i);
149
- const textContent = await page.getTextContent();
150
- const pageText = textContent.items
151
- .map((item: unknown) => {
152
- const textItem = item as { str?: string };
153
- return textItem.str || "";
154
- })
155
- .join(" ")
156
- .replace(/\s+/g, " ")
157
- .trim();
158
-
159
- if (pageText) {
160
- pages.push({ pageNum: i, text: pageText });
161
- }
162
- }
163
-
164
- const bodyLines: string[] = [];
165
- for (let i = 0; i < pages.length; i++) {
166
- if (i > 0) {
167
- bodyLines.push("");
168
- bodyLines.push(`<!-- Page ${pages[i].pageNum} -->`);
169
- bodyLines.push("");
170
- }
171
- bodyLines.push(pages[i].text);
172
- }
173
-
174
- return writeMarkdownResult({
175
- markdownBody: bodyLines.join("\n"),
176
- title,
177
- pages: pdf.numPages,
178
- outputDir,
179
- filename,
180
- url,
181
- metaAuthor,
182
- truncated,
183
- pagesToExtract,
184
- });
157
+ const {
158
+ maxPages = DEFAULT_MAX_PAGES,
159
+ outputDir = DEFAULT_OUTPUT_DIR,
160
+ filename,
161
+ signal,
162
+ geminiTimeoutMs,
163
+ } = options;
164
+
165
+ const safeMaxPages = Number.isFinite(maxPages)
166
+ ? Math.max(1, Math.floor(maxPages))
167
+ : DEFAULT_MAX_PAGES;
168
+ const urlTitle = extractTitleFromURL(url);
169
+ const pdfConfig = loadPDFConfig();
170
+ const provider = pdfConfig.provider;
171
+
172
+ if (provider === "auto" || provider === "datalab") {
173
+ try {
174
+ if (isDatalabApiAvailable()) {
175
+ const result = await extractPDFViaDatalab(buffer, {
176
+ maxPages: safeMaxPages,
177
+ title: urlTitle,
178
+ mode: pdfConfig.datalabMode,
179
+ timeoutMs: pdfConfig.datalabTimeoutMs,
180
+ ...(signal ? { signal } : {}),
181
+ });
182
+ return writeMarkdownResult({
183
+ markdownBody: result.markdown,
184
+ title: urlTitle,
185
+ pages: result.pages,
186
+ outputDir,
187
+ filename,
188
+ url,
189
+ });
190
+ }
191
+ } catch (err) {
192
+ if (shouldRethrowExtractionError(err, signal)) throw err;
193
+ }
194
+ }
195
+
196
+ if (provider === "auto" || provider === "gemini") {
197
+ try {
198
+ if (isGeminiApiAvailable()) {
199
+ const markdownBody = await extractPDFViaGemini(buffer, {
200
+ maxPages: safeMaxPages,
201
+ title: urlTitle,
202
+ ...(signal ? { signal } : {}),
203
+ ...(geminiTimeoutMs !== undefined
204
+ ? { timeoutMs: geminiTimeoutMs }
205
+ : {}),
206
+ });
207
+ return writeMarkdownResult({
208
+ markdownBody,
209
+ title: urlTitle,
210
+ pages: countPageMarkers(markdownBody),
211
+ outputDir,
212
+ filename,
213
+ url,
214
+ });
215
+ }
216
+ } catch (err) {
217
+ if (shouldRethrowExtractionError(err, signal)) throw err;
218
+ }
219
+ }
220
+
221
+ const { getDocumentProxy, VerbosityLevel } = await getUnpdf();
222
+ const pdf = await getDocumentProxy(new Uint8Array(buffer), {
223
+ verbosity: VerbosityLevel.ERRORS,
224
+ });
225
+ const metadata = await pdf.getMetadata();
226
+ const metadataInfo =
227
+ metadata.info && typeof metadata.info === "object"
228
+ ? (metadata.info as Record<string, unknown>)
229
+ : null;
230
+
231
+ const metaTitle =
232
+ typeof metadataInfo?.Title === "string" ? metadataInfo.Title : undefined;
233
+ const metaAuthor =
234
+ typeof metadataInfo?.Author === "string" ? metadataInfo.Author : undefined;
235
+ const title = metaTitle?.trim() || urlTitle;
236
+ const pagesToExtract = Math.min(pdf.numPages, safeMaxPages);
237
+ const truncated = pdf.numPages > safeMaxPages;
238
+ const pages: { pageNum: number; text: string }[] = [];
239
+
240
+ for (let i = 1; i <= pagesToExtract; i++) {
241
+ const page = await pdf.getPage(i);
242
+ const textContent = await page.getTextContent();
243
+ const pageText = textContent.items
244
+ .map((item: unknown) => {
245
+ const textItem = item as { str?: string };
246
+ return textItem.str || "";
247
+ })
248
+ .join(" ")
249
+ .replace(/\s+/g, " ")
250
+ .trim();
251
+
252
+ if (pageText) {
253
+ pages.push({ pageNum: i, text: pageText });
254
+ }
255
+ }
256
+
257
+ const bodyLines: string[] = [];
258
+ for (let i = 0; i < pages.length; i++) {
259
+ if (i > 0) {
260
+ bodyLines.push("");
261
+ bodyLines.push(`<!-- Page ${pages[i].pageNum} -->`);
262
+ bodyLines.push("");
263
+ }
264
+ bodyLines.push(pages[i].text);
265
+ }
266
+
267
+ return writeMarkdownResult({
268
+ markdownBody: bodyLines.join("\n"),
269
+ title,
270
+ pages: pdf.numPages,
271
+ outputDir,
272
+ filename,
273
+ url,
274
+ metaAuthor,
275
+ truncated,
276
+ pagesToExtract,
277
+ });
185
278
  }
186
279
 
187
280
  async function writeMarkdownResult(options: {
188
- markdownBody: string;
189
- title: string;
190
- pages: number;
191
- outputDir: string;
192
- filename?: string;
193
- url: string;
194
- metaAuthor?: string;
195
- truncated?: boolean;
196
- pagesToExtract?: number;
281
+ markdownBody: string;
282
+ title: string;
283
+ pages: number;
284
+ outputDir: string;
285
+ filename?: string;
286
+ url: string;
287
+ metaAuthor?: string;
288
+ truncated?: boolean;
289
+ pagesToExtract?: number;
197
290
  }): Promise<PDFExtractResult> {
198
- const lines: string[] = [];
199
- lines.push(`# ${options.title}`);
200
- lines.push("");
201
- lines.push(`> Source: ${options.url}`);
202
- lines.push(`> Pages: ${options.pages}${options.truncated ? ` (extracted first ${options.pagesToExtract})` : ""}`);
203
- if (options.metaAuthor) lines.push(`> Author: ${options.metaAuthor}`);
204
- lines.push("");
205
- lines.push("---");
206
- lines.push("");
207
- if (options.markdownBody) lines.push(options.markdownBody);
208
-
209
- if (options.truncated) {
210
- lines.push("");
211
- lines.push("---");
212
- lines.push("");
213
- lines.push(`*[Truncated: Only first ${options.pagesToExtract} of ${options.pages} pages extracted]*`);
214
- }
215
-
216
- const content = lines.join("\n");
217
- const outputFilename = options.filename || sanitizeFilename(options.title) + ".md";
218
- const outputPath = join(options.outputDir, outputFilename);
219
-
220
- await mkdir(options.outputDir, { recursive: true });
221
- await writeFile(outputPath, content, "utf-8");
222
-
223
- return {
224
- title: options.title,
225
- pages: options.pages,
226
- chars: content.length,
227
- outputPath,
228
- };
291
+ const lines: string[] = [];
292
+ lines.push(`# ${options.title}`);
293
+ lines.push("");
294
+ lines.push(`> Source: ${options.url}`);
295
+ lines.push(
296
+ `> Pages: ${options.pages}${options.truncated ? ` (extracted first ${options.pagesToExtract})` : ""}`,
297
+ );
298
+ if (options.metaAuthor) lines.push(`> Author: ${options.metaAuthor}`);
299
+ lines.push("");
300
+ lines.push("---");
301
+ lines.push("");
302
+ if (options.markdownBody) lines.push(options.markdownBody);
303
+
304
+ if (options.truncated) {
305
+ lines.push("");
306
+ lines.push("---");
307
+ lines.push("");
308
+ lines.push(
309
+ `*[Truncated: Only first ${options.pagesToExtract} of ${options.pages} pages extracted]*`,
310
+ );
311
+ }
312
+
313
+ const content = lines.join("\n");
314
+ const outputFilename =
315
+ options.filename || sanitizeFilename(options.title) + ".md";
316
+ const outputPath = join(options.outputDir, outputFilename);
317
+
318
+ await mkdir(options.outputDir, { recursive: true });
319
+ await writeFile(outputPath, content, "utf-8");
320
+
321
+ return {
322
+ title: options.title,
323
+ pages: options.pages,
324
+ chars: content.length,
325
+ outputPath,
326
+ };
229
327
  }
230
328
 
231
329
  function countPageMarkers(markdown: string): number {
232
- return [...markdown.matchAll(PAGE_MARKER_PATTERN)].length;
330
+ return [...markdown.matchAll(PAGE_MARKER_PATTERN)].length;
233
331
  }
234
332
 
235
- function shouldRethrowGeminiError(err: unknown, signal?: AbortSignal): boolean {
236
- if (signal?.aborted) return true;
237
- if (err instanceof CredentialResolutionError) return true;
238
- const message = err instanceof Error ? err.message : String(err);
239
- return message.startsWith("Failed to parse ");
333
+ function shouldRethrowExtractionError(
334
+ err: unknown,
335
+ signal?: AbortSignal,
336
+ ): boolean {
337
+ if (signal?.aborted) return true;
338
+ if (err instanceof CredentialResolutionError) return true;
339
+ const message = err instanceof Error ? err.message : String(err);
340
+ return message.startsWith("Failed to parse ");
240
341
  }
241
342
 
242
343
  /**
243
344
  * Extract a reasonable title from URL
244
345
  */
245
346
  function extractTitleFromURL(url: string): string {
246
- try {
247
- const urlObj = new URL(url);
248
- const pathname = urlObj.pathname;
249
-
250
- let filename = basename(pathname, ".pdf");
251
-
252
- if (urlObj.hostname.includes("arxiv.org")) {
253
- const match = pathname.match(/\/(?:pdf|abs)\/(\d+\.\d+)/);
254
- if (match) {
255
- filename = `arxiv-${match[1]}`;
256
- }
257
- }
258
-
259
- filename = filename
260
- .replace(/[_-]+/g, " ")
261
- .replace(/\s+/g, " ")
262
- .trim();
263
-
264
- return filename || "document";
265
- } catch {
266
- return "document";
267
- }
347
+ try {
348
+ const urlObj = new URL(url);
349
+ const pathname = urlObj.pathname;
350
+
351
+ let filename = basename(pathname, ".pdf");
352
+
353
+ if (urlObj.hostname.includes("arxiv.org")) {
354
+ const match = pathname.match(/\/(?:pdf|abs)\/(\d+\.\d+)/);
355
+ if (match) {
356
+ filename = `arxiv-${match[1]}`;
357
+ }
358
+ }
359
+
360
+ filename = filename.replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim();
361
+
362
+ return filename || "document";
363
+ } catch {
364
+ return "document";
365
+ }
268
366
  }
269
367
 
270
368
  /**
271
369
  * Sanitize string for use as filename
272
370
  */
273
371
  function sanitizeFilename(name: string): string {
274
- return name
275
- .toLowerCase()
276
- .replace(/[^a-z0-9\s-]/g, "")
277
- .replace(/\s+/g, "-")
278
- .replace(/-+/g, "-")
279
- .slice(0, 100)
280
- .replace(/^-|-$/g, "")
281
- || "document";
372
+ return (
373
+ name
374
+ .toLowerCase()
375
+ .replace(/[^a-z0-9\s-]/g, "")
376
+ .replace(/\s+/g, "-")
377
+ .replace(/-+/g, "-")
378
+ .slice(0, 100)
379
+ .replace(/^-|-$/g, "") || "document"
380
+ );
282
381
  }
283
382
 
284
383
  /**
285
384
  * Check if URL or content-type indicates a PDF
286
385
  */
287
386
  export function isPDF(url: string, contentType?: string): boolean {
288
- if (contentType?.includes("application/pdf")) {
289
- return true;
290
- }
291
- try {
292
- const urlObj = new URL(url);
293
- return urlObj.pathname.toLowerCase().endsWith(".pdf");
294
- } catch {
295
- return false;
296
- }
387
+ if (contentType?.includes("application/pdf")) {
388
+ return true;
389
+ }
390
+ try {
391
+ const urlObj = new URL(url);
392
+ return urlObj.pathname.toLowerCase().endsWith(".pdf");
393
+ } catch {
394
+ return false;
395
+ }
297
396
  }