@ashx-j/lunr 0.2.17 → 0.2.18
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.
- package/CHANGELOG.md +19 -1
- package/README.md +2 -1
- package/dist/builtin-extensions/ashxj-thinking.d.ts +1 -0
- package/dist/builtin-extensions/ashxj-thinking.d.ts.map +1 -1
- package/dist/builtin-extensions/ashxj-thinking.js +44 -19
- package/dist/builtin-extensions/ashxj-thinking.js.map +1 -1
- package/dist/builtin-extensions/ashxj-tui.d.ts +2 -2
- package/dist/builtin-extensions/ashxj-tui.d.ts.map +1 -1
- package/dist/builtin-extensions/ashxj-tui.js +8 -11
- package/dist/builtin-extensions/ashxj-tui.js.map +1 -1
- package/dist/builtin-extensions/index.d.ts.map +1 -1
- package/dist/builtin-extensions/index.js +2 -1
- package/dist/builtin-extensions/index.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/index.d.ts +4 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/index.js +172 -171
- package/dist/builtin-extensions/pi-lsp-extension/src/index.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/runtime.d.ts +50 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/runtime.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/runtime.js +131 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/runtime.js.map +1 -0
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.d.ts +7 -0
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js +211 -0
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js.map +1 -0
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tools.d.ts +0 -5
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tools.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tools.js +0 -202
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tools.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/index.js +276 -109
- package/dist/builtin-extensions/pi-mcp-adapter/index.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/init.d.ts +4 -1
- package/dist/builtin-extensions/pi-mcp-adapter/init.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/init.js +10 -3
- package/dist/builtin-extensions/pi-mcp-adapter/init.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/metadata-cache.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/metadata-cache.js +6 -1
- package/dist/builtin-extensions/pi-mcp-adapter/metadata-cache.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/server-manager.d.ts +6 -1
- package/dist/builtin-extensions/pi-mcp-adapter/server-manager.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/server-manager.js +33 -8
- package/dist/builtin-extensions/pi-mcp-adapter/server-manager.js.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js +2 -1
- package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.d.ts +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js +68 -15
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.js +5 -2
- package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/fork-context.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/fork-context.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/fork-context.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +2 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js +72 -31
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/review.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/review.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js +2 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/extract.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/extract.js +105 -54
- package/dist/builtin-extensions/pi-web-access/extract.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/gemini-search.d.ts +1 -1
- package/dist/builtin-extensions/pi-web-access/gemini-search.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/gemini-search.js +82 -65
- package/dist/builtin-extensions/pi-web-access/gemini-search.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/github-extract.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/github-extract.js +2 -0
- package/dist/builtin-extensions/pi-web-access/github-extract.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/index.js +259 -168
- package/dist/builtin-extensions/pi-web-access/index.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/lazy.d.ts +18 -0
- package/dist/builtin-extensions/pi-web-access/lazy.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-web-access/lazy.js +19 -0
- package/dist/builtin-extensions/pi-web-access/lazy.js.map +1 -0
- package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts +6 -0
- package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-web-access/session-cleanup.js +17 -0
- package/dist/builtin-extensions/pi-web-access/session-cleanup.js.map +1 -0
- package/dist/cli-runtime.d.ts +3 -0
- package/dist/cli-runtime.d.ts.map +1 -0
- package/dist/cli-runtime.js +72 -0
- package/dist/cli-runtime.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +11 -60
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +6 -3
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/host-static.d.ts.map +1 -1
- package/dist/core/extensions/host-static.js +34 -12
- package/dist/core/extensions/host-static.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +1 -2
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +10 -64
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/keybindings.d.ts +2 -2
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +2 -2
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +1 -1
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/sdk.d.ts +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +1 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/timings.d.ts +2 -1
- package/dist/core/timings.d.ts.map +1 -1
- package/dist/core/timings.js +17 -2
- package/dist/core/timings.js.map +1 -1
- package/dist/gateway/agent-bridge.d.ts +6 -1
- package/dist/gateway/agent-bridge.d.ts.map +1 -1
- package/dist/gateway/agent-bridge.js +23 -6
- package/dist/gateway/agent-bridge.js.map +1 -1
- package/dist/gateway/cron.d.ts +1 -1
- package/dist/gateway/cron.d.ts.map +1 -1
- package/dist/gateway/cron.js +3 -1
- package/dist/gateway/cron.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +20 -14
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/injected-context.d.ts +2 -0
- package/dist/modes/interactive/injected-context.d.ts.map +1 -0
- package/dist/modes/interactive/injected-context.js +7 -0
- package/dist/modes/interactive/injected-context.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +2 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +32 -8
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/thinking-level-slash.d.ts +4 -0
- package/dist/modes/interactive/thinking-level-slash.d.ts.map +1 -0
- package/dist/modes/interactive/thinking-level-slash.js +23 -0
- package/dist/modes/interactive/thinking-level-slash.js.map +1 -0
- package/dist/node-runtime/agent-session-services-35WNVGMR.js +36 -0
- package/dist/node-runtime/benchmark-request-UC7PD3V3.js +57 -0
- package/dist/node-runtime/brave-UWDA3I22.js +143 -0
- package/dist/node-runtime/builtin-extensions-QATGXENO.js +28 -0
- package/dist/node-runtime/chunk-2SJB2AWP.js +47 -0
- package/dist/node-runtime/chunk-3NMWLTPV.js +64 -0
- package/dist/node-runtime/chunk-3WFAEVKS.js +115 -0
- package/dist/node-runtime/chunk-3X3KZKMI.js +49 -0
- package/dist/node-runtime/chunk-4CSFRBCN.js +7558 -0
- package/dist/node-runtime/chunk-4DJ6HEGH.js +296 -0
- package/dist/node-runtime/chunk-4HXHDDCF.js +16 -0
- package/dist/node-runtime/chunk-4I23VTC4.js +429 -0
- package/dist/node-runtime/chunk-4TLO4PO2.js +3427 -0
- package/dist/node-runtime/chunk-4ZWGEEAS.js +69 -0
- package/dist/node-runtime/chunk-53YJI7JU.js +137 -0
- package/dist/node-runtime/chunk-5EFIU3OM.js +295 -0
- package/dist/node-runtime/chunk-6BEIBLQ3.js +53 -0
- package/dist/node-runtime/chunk-6KDIOJL3.js +72 -0
- package/dist/node-runtime/chunk-7N3YHTYF.js +26 -0
- package/dist/node-runtime/chunk-7NBZ5P7I.js +24 -0
- package/dist/node-runtime/chunk-AD6VXAO7.js +342 -0
- package/dist/node-runtime/chunk-ARC4KOTI.js +11178 -0
- package/dist/node-runtime/chunk-AROOY4BB.js +173 -0
- package/dist/node-runtime/chunk-ATGA2WLC.js +5646 -0
- package/dist/node-runtime/chunk-AUXVGBO4.js +19 -0
- package/dist/node-runtime/chunk-AY6CAJSL.js +30 -0
- package/dist/node-runtime/chunk-BHO7O6E5.js +296 -0
- package/dist/node-runtime/chunk-BI3DHYCN.js +8845 -0
- package/dist/node-runtime/chunk-BOHIDQT4.js +21 -0
- package/dist/node-runtime/chunk-BRJX2PRC.js +53 -0
- package/dist/node-runtime/chunk-CBGB57XT.js +20211 -0
- package/dist/node-runtime/chunk-CCZJ3PYY.js +17 -0
- package/dist/node-runtime/chunk-CGBXPL3E.js +1171 -0
- package/dist/node-runtime/chunk-CJECDQKP.js +19 -0
- package/dist/node-runtime/chunk-CSFI6YXL.js +130 -0
- package/dist/node-runtime/chunk-DAPBPCBP.js +53 -0
- package/dist/node-runtime/chunk-DDWHZ633.js +37 -0
- package/dist/node-runtime/chunk-DMMTJQMQ.js +43 -0
- package/dist/node-runtime/chunk-DVGWF6A2.js +1180 -0
- package/dist/node-runtime/chunk-EBF6COVM.js +1734 -0
- package/dist/node-runtime/chunk-FQMM2EZ2.js +248 -0
- package/dist/node-runtime/chunk-FVDOXRUD.js +96 -0
- package/dist/node-runtime/chunk-G4P75AIS.js +9471 -0
- package/dist/node-runtime/chunk-G6LRAP36.js +105 -0
- package/dist/node-runtime/chunk-GKLEQ3I4.js +860 -0
- package/dist/node-runtime/chunk-H2EOZYMP.js +57 -0
- package/dist/node-runtime/chunk-HQCKFM2O.js +56 -0
- package/dist/node-runtime/chunk-HYHJIRDW.js +19 -0
- package/dist/node-runtime/chunk-I6R76AG7.js +22 -0
- package/dist/node-runtime/chunk-IRPJXI6U.js +1269 -0
- package/dist/node-runtime/chunk-J26TSGDL.js +633 -0
- package/dist/node-runtime/chunk-JZQ7F36T.js +610 -0
- package/dist/node-runtime/chunk-KBKMCJDU.js +126 -0
- package/dist/node-runtime/chunk-KWA4OC2J.js +100 -0
- package/dist/node-runtime/chunk-M3LZLQW2.js +98 -0
- package/dist/node-runtime/chunk-MCUNIRD5.js +8388 -0
- package/dist/node-runtime/chunk-NI6OTSBH.js +966 -0
- package/dist/node-runtime/chunk-NUIFPZWX.js +209 -0
- package/dist/node-runtime/chunk-OBMWP3I5.js +68 -0
- package/dist/node-runtime/chunk-OCVIXVTZ.js +88 -0
- package/dist/node-runtime/chunk-OPEK5RRY.js +466 -0
- package/dist/node-runtime/chunk-OR4L3IB2.js +42 -0
- package/dist/node-runtime/chunk-PL6JKXWI.js +3622 -0
- package/dist/node-runtime/chunk-PNJSG3IZ.js +807 -0
- package/dist/node-runtime/chunk-PVCDKGR6.js +55 -0
- package/dist/node-runtime/chunk-Q4Q6D4IY.js +364 -0
- package/dist/node-runtime/chunk-Q5ZU2VRT.js +83 -0
- package/dist/node-runtime/chunk-QFWG2RVL.js +5477 -0
- package/dist/node-runtime/chunk-QZMODZQ2.js +305 -0
- package/dist/node-runtime/chunk-R5EDISSV.js +375 -0
- package/dist/node-runtime/chunk-RNBHABBA.js +986 -0
- package/dist/node-runtime/chunk-RQ2Q5IYR.js +72 -0
- package/dist/node-runtime/chunk-RRIZE6QQ.js +17 -0
- package/dist/node-runtime/chunk-SN6G4OU6.js +4753 -0
- package/dist/node-runtime/chunk-STERBGUB.js +949 -0
- package/dist/node-runtime/chunk-UWQYRSYQ.js +8973 -0
- package/dist/node-runtime/chunk-VRB2IYGG.js +53 -0
- package/dist/node-runtime/chunk-VSELYKWU.js +772 -0
- package/dist/node-runtime/chunk-W3CVH5RX.js +62 -0
- package/dist/node-runtime/chunk-WJVFOIB5.js +24921 -0
- package/dist/node-runtime/chunk-WKDK4C5P.js +2834 -0
- package/dist/node-runtime/chunk-XATUM2HK.js +46 -0
- package/dist/node-runtime/chunk-XBWPBZBN.js +125 -0
- package/dist/node-runtime/chunk-XTHQ7XR7.js +509 -0
- package/dist/node-runtime/chunk-ZBV7FAHG.js +1345 -0
- package/dist/node-runtime/cli-runtime.js +80 -0
- package/dist/node-runtime/clipboard-OZ63BBC6.js +12 -0
- package/dist/node-runtime/clipboard-image-7QR6GXVA.js +13 -0
- package/dist/node-runtime/command-AAV3YRR5.js +13 -0
- package/dist/node-runtime/commands-MV5KOZ64.js +291 -0
- package/dist/node-runtime/config-QDD5PUE2.js +73 -0
- package/dist/node-runtime/curator-server-XCM43VEF.js +3816 -0
- package/dist/node-runtime/customize-XEXL2DGQ.js +12 -0
- package/dist/node-runtime/direct-tool-executor-BK7CLEBX.js +188 -0
- package/dist/node-runtime/exa-LL64QZR2.js +329 -0
- package/dist/node-runtime/export-html-TEXVV5RP.js +150 -0
- package/dist/node-runtime/extension-input-O4HSAEM4.js +14 -0
- package/dist/node-runtime/extension-selector-X5FCX43D.js +14 -0
- package/dist/node-runtime/extract-XLQSJN4V.js +17 -0
- package/dist/node-runtime/file-sync-HCOP2QVF.js +117 -0
- package/dist/node-runtime/gateway-OAC4OBBL.js +2137 -0
- package/dist/node-runtime/gemini-api-QE7ANVDZ.js +29 -0
- package/dist/node-runtime/gemini-search-U7SUMPQA.js +330 -0
- package/dist/node-runtime/gemini-url-context-PXWCBQHK.js +103 -0
- package/dist/node-runtime/gemini-web-MM446SEH.js +14 -0
- package/dist/node-runtime/github-extract-F64RZOEA.js +619 -0
- package/dist/node-runtime/goal-NJEE5CTU.js +1831 -0
- package/dist/node-runtime/index.js +859 -0
- package/dist/node-runtime/init-3C7Z24OI.js +28 -0
- package/dist/node-runtime/install-cli-EXMJQESX.js +308 -0
- package/dist/node-runtime/interactive-mode-2SZUO53H.js +56 -0
- package/dist/node-runtime/interactive-view-S6J3QHEF.js +247 -0
- package/dist/node-runtime/lsp-manager-3ASUSO4T.js +1104 -0
- package/dist/node-runtime/lunr-cron-AUUYP4N4.js +272 -0
- package/dist/node-runtime/main.js +54 -0
- package/dist/node-runtime/mcp-auth-flow-QIXHW6QN.js +32 -0
- package/dist/node-runtime/mcp-panel-IIGMXQGP.js +501 -0
- package/dist/node-runtime/mcp-setup-panel-FEK7663I.js +355 -0
- package/dist/node-runtime/memory-cap-3S4XRLNP.js +20 -0
- package/dist/node-runtime/model-tiers-NJLHUEGK.js +20 -0
- package/dist/node-runtime/openai-search-B3MSNCQA.js +288 -0
- package/dist/node-runtime/package-manager-cli-7RKSZGEN.js +749 -0
- package/dist/node-runtime/parallel-AI2MNSCU.js +265 -0
- package/dist/node-runtime/parser-manager-TQHWYDH7.js +152 -0
- package/dist/node-runtime/pdf-extract-IPE6H4HP.js +57 -0
- package/dist/node-runtime/perplexity-QVJW3I35.js +12 -0
- package/dist/node-runtime/pi-intercom-EEMBGQID.js +2215 -0
- package/dist/node-runtime/pi-mcp-adapter-GUFZWW54.js +428 -0
- package/dist/node-runtime/pi-ollama-cloud-2F3Y2QNM.js +1440 -0
- package/dist/node-runtime/pi-prompt-template-model-QWTCGLJU.js +3946 -0
- package/dist/node-runtime/pi-subagents-7YJ3I4VE.js +6048 -0
- package/dist/node-runtime/pi-web-access-2CQGFLT2.js +1867 -0
- package/dist/node-runtime/print-mode-XSFM26EL.js +12 -0
- package/dist/node-runtime/proxy-modes-V6YVFH3C.js +685 -0
- package/dist/node-runtime/rpc-entry.js +62 -0
- package/dist/node-runtime/rpc-mode-W5ZTLTMZ.js +13 -0
- package/dist/node-runtime/runtime-bridges-3RKBDLNA.js +14 -0
- package/dist/node-runtime/session-manager-E7AREDTV.js +33 -0
- package/dist/node-runtime/session-selector-FJ7M2AIX.js +14 -0
- package/dist/node-runtime/settings-manager-GHPTUN7R.js +21 -0
- package/dist/node-runtime/src-BPQFJR76.js +2427 -0
- package/dist/node-runtime/subagent-executor-5Q5A4HJ2.js +8499 -0
- package/dist/node-runtime/summary-review-YSMXKITO.js +228 -0
- package/dist/node-runtime/tavily-WEZFVFP7.js +154 -0
- package/dist/node-runtime/timings-UAD5BEAS.js +16 -0
- package/dist/node-runtime/tool-renderer-MV5O452K.js +204 -0
- package/dist/node-runtime/update-cli-L3JIBH2U.js +70 -0
- package/dist/node-runtime/v4-5MNC3H7G.js +424 -0
- package/dist/node-runtime/video-extract-OYT5WKS5.js +304 -0
- package/dist/node-runtime/workspace-index-6DBXSTJ6.js +163 -0
- package/dist/node-runtime/youtube-extract-TQYKGFGS.js +264 -0
- package/dist/rpc.d.ts +3 -0
- package/dist/rpc.d.ts.map +1 -0
- package/dist/rpc.js +18 -0
- package/dist/rpc.js.map +1 -0
- package/dist/startup/benchmark-request.d.ts +3 -0
- package/dist/startup/benchmark-request.d.ts.map +1 -0
- package/dist/startup/benchmark-request.js +65 -0
- package/dist/startup/benchmark-request.js.map +1 -0
- package/dist/startup/interactive-view.d.ts.map +1 -1
- package/dist/startup/interactive-view.js +1 -2
- package/dist/startup/interactive-view.js.map +1 -1
- package/dist/startup/startup-milestones.d.ts +1 -1
- package/dist/startup/startup-milestones.d.ts.map +1 -1
- package/dist/startup/startup-milestones.js.map +1 -1
- package/dist/utils/await-with-abort.d.ts +2 -0
- package/dist/utils/await-with-abort.d.ts.map +1 -0
- package/dist/utils/await-with-abort.js +19 -0
- package/dist/utils/await-with-abort.js.map +1 -0
- package/docs/development.md +5 -1
- package/docs/features.md +2 -2
- package/docs/interactive-startup.md +95 -48
- package/docs/keybindings.md +2 -2
- package/docs/terminal-setup.md +1 -1
- package/docs/usage.md +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/hidden-thinking-label.ts +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +8 -8
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { createRequire as __lunrRequire } from "node:module"; const require = __lunrRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
MAX_FILE_SIZE,
|
|
4
|
+
MAX_INDEX_FILES,
|
|
5
|
+
SKIP_DIRS,
|
|
6
|
+
extractSymbols
|
|
7
|
+
} from "./chunk-BHO7O6E5.js";
|
|
8
|
+
import "./chunk-BRJX2PRC.js";
|
|
9
|
+
|
|
10
|
+
// packages/coding-agent/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/workspace-index.js
|
|
11
|
+
import { resolve } from "node:path";
|
|
12
|
+
import { readdir, stat, readFile } from "node:fs/promises";
|
|
13
|
+
var WorkspaceIndex = class {
|
|
14
|
+
rootDir;
|
|
15
|
+
treeSitter;
|
|
16
|
+
/** Map from symbol name (lowercase) to entries */
|
|
17
|
+
index = /* @__PURE__ */ new Map();
|
|
18
|
+
/** Reverse index: file path → set of index keys that have entries for this file */
|
|
19
|
+
fileToKeys = /* @__PURE__ */ new Map();
|
|
20
|
+
/** Set of indexed file paths (absolute) */
|
|
21
|
+
indexedFiles = /* @__PURE__ */ new Set();
|
|
22
|
+
/** Whether the initial build has completed */
|
|
23
|
+
built = !1;
|
|
24
|
+
building = null;
|
|
25
|
+
constructor(rootDir, treeSitter) {
|
|
26
|
+
this.rootDir = rootDir, this.treeSitter = treeSitter;
|
|
27
|
+
}
|
|
28
|
+
/** Build the index by walking the project tree. Deduplicates concurrent calls. */
|
|
29
|
+
async build() {
|
|
30
|
+
if (!this.built) {
|
|
31
|
+
if (this.building) {
|
|
32
|
+
await this.building;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
this.building = this._build(), await this.building, this.built = !0, this.building = null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async _build() {
|
|
39
|
+
let files = await this.collectFiles(this.rootDir), batchSize = 50;
|
|
40
|
+
for (let i = 0; i < files.length; i += batchSize) {
|
|
41
|
+
let batch = files.slice(i, i + batchSize);
|
|
42
|
+
await Promise.all(batch.map((f) => this.indexFile(f).catch(() => {
|
|
43
|
+
})));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Collect all indexable files under a directory */
|
|
47
|
+
async collectFiles(dir, collected = []) {
|
|
48
|
+
if (collected.length >= MAX_INDEX_FILES)
|
|
49
|
+
return collected;
|
|
50
|
+
try {
|
|
51
|
+
let entries = await readdir(dir, { withFileTypes: !0 });
|
|
52
|
+
for (let entry of entries) {
|
|
53
|
+
if (collected.length >= MAX_INDEX_FILES)
|
|
54
|
+
break;
|
|
55
|
+
if (entry.isDirectory()) {
|
|
56
|
+
if (SKIP_DIRS.has(entry.name) || entry.name.startsWith("."))
|
|
57
|
+
continue;
|
|
58
|
+
await this.collectFiles(resolve(dir, entry.name), collected);
|
|
59
|
+
} else if (entry.isFile()) {
|
|
60
|
+
let languageId = this.treeSitter.getLanguageId(entry.name);
|
|
61
|
+
languageId && this.treeSitter.hasGrammar(languageId) && collected.push(resolve(dir, entry.name));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
} catch {
|
|
65
|
+
}
|
|
66
|
+
return collected;
|
|
67
|
+
}
|
|
68
|
+
/** Index (or re-index) a single file */
|
|
69
|
+
async indexFile(filePath) {
|
|
70
|
+
let absPath = resolve(filePath);
|
|
71
|
+
this.removeFile(absPath);
|
|
72
|
+
try {
|
|
73
|
+
if ((await stat(absPath)).size > MAX_FILE_SIZE)
|
|
74
|
+
return;
|
|
75
|
+
let content = await readFile(absPath, "utf-8"), languageId = this.treeSitter.getLanguageId(absPath);
|
|
76
|
+
if (!languageId)
|
|
77
|
+
return;
|
|
78
|
+
let tree = await this.treeSitter.parse(absPath, content);
|
|
79
|
+
if (!tree)
|
|
80
|
+
return;
|
|
81
|
+
let symbols = extractSymbols(tree, languageId);
|
|
82
|
+
this.addSymbols(absPath, symbols), this.indexedFiles.add(absPath);
|
|
83
|
+
} catch {
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/** Index a file from already-available content (avoids re-reading) */
|
|
87
|
+
async indexFileContent(filePath, content) {
|
|
88
|
+
let absPath = resolve(filePath);
|
|
89
|
+
this.removeFile(absPath);
|
|
90
|
+
let languageId = this.treeSitter.getLanguageId(absPath);
|
|
91
|
+
if (!languageId)
|
|
92
|
+
return;
|
|
93
|
+
let tree = await this.treeSitter.parse(absPath, content);
|
|
94
|
+
if (!tree)
|
|
95
|
+
return;
|
|
96
|
+
let symbols = extractSymbols(tree, languageId);
|
|
97
|
+
this.addSymbols(absPath, symbols), this.indexedFiles.add(absPath);
|
|
98
|
+
}
|
|
99
|
+
addSymbols(filePath, symbols) {
|
|
100
|
+
for (let sym of symbols) {
|
|
101
|
+
let entry = {
|
|
102
|
+
name: sym.name,
|
|
103
|
+
kind: sym.kind,
|
|
104
|
+
file: filePath,
|
|
105
|
+
line: sym.line
|
|
106
|
+
}, key = sym.name.toLowerCase(), existing = this.index.get(key);
|
|
107
|
+
existing ? existing.push(entry) : this.index.set(key, [entry]);
|
|
108
|
+
let keys = this.fileToKeys.get(filePath);
|
|
109
|
+
keys || (keys = /* @__PURE__ */ new Set(), this.fileToKeys.set(filePath, keys)), keys.add(key), sym.children && this.addSymbols(filePath, sym.children);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/** Remove all entries for a file (O(keys-per-file) via reverse index) */
|
|
113
|
+
removeFile(filePath) {
|
|
114
|
+
let absPath = resolve(filePath);
|
|
115
|
+
this.indexedFiles.delete(absPath);
|
|
116
|
+
let keys = this.fileToKeys.get(absPath);
|
|
117
|
+
if (keys) {
|
|
118
|
+
for (let key of keys) {
|
|
119
|
+
let entries = this.index.get(key);
|
|
120
|
+
if (!entries)
|
|
121
|
+
continue;
|
|
122
|
+
let filtered = entries.filter((e) => e.file !== absPath);
|
|
123
|
+
filtered.length === 0 ? this.index.delete(key) : this.index.set(key, filtered);
|
|
124
|
+
}
|
|
125
|
+
this.fileToKeys.delete(absPath);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/** Search for symbols matching a query (fuzzy) */
|
|
129
|
+
search(query) {
|
|
130
|
+
if (!query)
|
|
131
|
+
return [];
|
|
132
|
+
let queryLower = query.toLowerCase(), exact = this.index.get(queryLower) ?? [], fuzzy = [];
|
|
133
|
+
for (let [key, entries] of this.index)
|
|
134
|
+
key !== queryLower && key.includes(queryLower) && fuzzy.push(...entries);
|
|
135
|
+
let results = [...exact, ...fuzzy];
|
|
136
|
+
return results.sort((a, b) => {
|
|
137
|
+
let aExact = a.name.toLowerCase() === queryLower ? 0 : 1, bExact = b.name.toLowerCase() === queryLower ? 0 : 1;
|
|
138
|
+
return aExact !== bExact ? aExact - bExact : a.name.length - b.name.length;
|
|
139
|
+
}), results.slice(0, 100);
|
|
140
|
+
}
|
|
141
|
+
/** Get all symbols for a specific file */
|
|
142
|
+
getSymbolsForFile(filePath) {
|
|
143
|
+
let absPath = resolve(filePath), results = [];
|
|
144
|
+
for (let entries of this.index.values())
|
|
145
|
+
for (let entry of entries)
|
|
146
|
+
entry.file === absPath && results.push(entry);
|
|
147
|
+
return results.sort((a, b) => a.line - b.line);
|
|
148
|
+
}
|
|
149
|
+
/** Get index stats */
|
|
150
|
+
getStats() {
|
|
151
|
+
let symbolCount = 0;
|
|
152
|
+
for (let entries of this.index.values())
|
|
153
|
+
symbolCount += entries.length;
|
|
154
|
+
return { files: this.indexedFiles.size, symbols: symbolCount };
|
|
155
|
+
}
|
|
156
|
+
/** Whether the index has been built */
|
|
157
|
+
get isBuilt() {
|
|
158
|
+
return this.built;
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
export {
|
|
162
|
+
WorkspaceIndex
|
|
163
|
+
};
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { createRequire as __lunrRequire } from "node:module"; const require = __lunrRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
isGeminiWebAvailable,
|
|
4
|
+
queryWithCookies
|
|
5
|
+
} from "./chunk-XTHQ7XR7.js";
|
|
6
|
+
import "./chunk-DMMTJQMQ.js";
|
|
7
|
+
import {
|
|
8
|
+
isGeminiApiAvailable,
|
|
9
|
+
queryGeminiApiWithVideo
|
|
10
|
+
} from "./chunk-3WFAEVKS.js";
|
|
11
|
+
import {
|
|
12
|
+
isPerplexityAvailable,
|
|
13
|
+
searchWithPerplexity
|
|
14
|
+
} from "./chunk-XBWPBZBN.js";
|
|
15
|
+
import {
|
|
16
|
+
extractHeadingTitle
|
|
17
|
+
} from "./chunk-WJVFOIB5.js";
|
|
18
|
+
import "./chunk-7NBZ5P7I.js";
|
|
19
|
+
import {
|
|
20
|
+
activityMonitor
|
|
21
|
+
} from "./chunk-DAPBPCBP.js";
|
|
22
|
+
import {
|
|
23
|
+
formatSeconds,
|
|
24
|
+
getWebSearchConfigPath,
|
|
25
|
+
isTimeoutError,
|
|
26
|
+
mapFfmpegError,
|
|
27
|
+
readExecError,
|
|
28
|
+
trimErrorText
|
|
29
|
+
} from "./chunk-VRB2IYGG.js";
|
|
30
|
+
import "./chunk-BRJX2PRC.js";
|
|
31
|
+
|
|
32
|
+
// packages/coding-agent/dist/builtin-extensions/pi-web-access/youtube-extract.js
|
|
33
|
+
import { execFileSync } from "node:child_process";
|
|
34
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
35
|
+
var CONFIG_PATH = getWebSearchConfigPath(), YOUTUBE_PROMPT = `Extract the complete content of this YouTube video. Include:
|
|
36
|
+
1. Video title, channel name, and duration
|
|
37
|
+
2. A brief summary (2-3 sentences)
|
|
38
|
+
3. Full transcript with timestamps
|
|
39
|
+
4. Descriptions of any code, terminal commands, diagrams, slides, or UI shown on screen
|
|
40
|
+
|
|
41
|
+
Format as markdown.`, YOUTUBE_REGEX = /(?:(?:www\.|m\.)?youtube\.com\/(?:watch\?.*v=|shorts\/|live\/|embed\/|v\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/;
|
|
42
|
+
function errorMessage(err) {
|
|
43
|
+
return err instanceof Error ? err.message : String(err);
|
|
44
|
+
}
|
|
45
|
+
function shouldRethrow(err) {
|
|
46
|
+
return errorMessage(err).startsWith("Failed to parse ");
|
|
47
|
+
}
|
|
48
|
+
function addAttemptError(errors, label, err) {
|
|
49
|
+
let message = errorMessage(err).replace(/\s+/g, " ").trim();
|
|
50
|
+
message && errors.push(`${label}: ${message}`);
|
|
51
|
+
}
|
|
52
|
+
function normalizePreferredModel(value, fallback) {
|
|
53
|
+
if (typeof value != "string")
|
|
54
|
+
return fallback;
|
|
55
|
+
let normalized = value.trim();
|
|
56
|
+
return normalized.length > 0 ? normalized : fallback;
|
|
57
|
+
}
|
|
58
|
+
function normalizeEnabled(value, fallback) {
|
|
59
|
+
return typeof value == "boolean" ? value : fallback;
|
|
60
|
+
}
|
|
61
|
+
var defaults = { enabled: !0, preferredModel: "gemini-3-flash-preview" }, cachedConfig = null;
|
|
62
|
+
function loadYouTubeConfig() {
|
|
63
|
+
if (cachedConfig)
|
|
64
|
+
return cachedConfig;
|
|
65
|
+
if (!existsSync(CONFIG_PATH))
|
|
66
|
+
return cachedConfig = { ...defaults }, cachedConfig;
|
|
67
|
+
let rawText = readFileSync(CONFIG_PATH, "utf-8"), raw;
|
|
68
|
+
try {
|
|
69
|
+
raw = JSON.parse(rawText);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
let message = err instanceof Error ? err.message : String(err);
|
|
72
|
+
throw new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);
|
|
73
|
+
}
|
|
74
|
+
let yt = raw.youtube ?? {};
|
|
75
|
+
return cachedConfig = {
|
|
76
|
+
enabled: normalizeEnabled(yt.enabled, defaults.enabled),
|
|
77
|
+
preferredModel: normalizePreferredModel(yt.preferredModel, defaults.preferredModel)
|
|
78
|
+
}, cachedConfig;
|
|
79
|
+
}
|
|
80
|
+
function isYouTubeURL(url) {
|
|
81
|
+
try {
|
|
82
|
+
if (new URL(url).pathname === "/playlist")
|
|
83
|
+
return { isYouTube: !1, videoId: null };
|
|
84
|
+
} catch {
|
|
85
|
+
}
|
|
86
|
+
let match = url.match(YOUTUBE_REGEX);
|
|
87
|
+
return match ? { isYouTube: !0, videoId: match[1] } : { isYouTube: !1, videoId: null };
|
|
88
|
+
}
|
|
89
|
+
function isYouTubeEnabled() {
|
|
90
|
+
return loadYouTubeConfig().enabled;
|
|
91
|
+
}
|
|
92
|
+
async function extractYouTube(url, signal, prompt, model) {
|
|
93
|
+
let config = loadYouTubeConfig(), { videoId } = isYouTubeURL(url), canonicalUrl = videoId ? `https://www.youtube.com/watch?v=${videoId}` : url, effectivePrompt = prompt ?? YOUTUBE_PROMPT, effectiveModel = model ?? config.preferredModel, activityId = activityMonitor.logStart({ type: "fetch", url: `youtube.com/${videoId ?? "video"}` }), attemptErrors = [], result = await tryGeminiWeb(canonicalUrl, effectivePrompt, effectiveModel, signal, attemptErrors) ?? await tryGeminiApi(canonicalUrl, effectivePrompt, effectiveModel, signal, attemptErrors) ?? await tryPerplexity(url, effectivePrompt, signal, attemptErrors);
|
|
94
|
+
if (result) {
|
|
95
|
+
if (result.url = url, !result.error && videoId) {
|
|
96
|
+
let thumb = await fetchYouTubeThumbnail(videoId);
|
|
97
|
+
thumb && (result.thumbnail = thumb);
|
|
98
|
+
}
|
|
99
|
+
return activityMonitor.logComplete(activityId, result.error ? 0 : 200), result;
|
|
100
|
+
}
|
|
101
|
+
if (signal?.aborted)
|
|
102
|
+
return activityMonitor.logComplete(activityId, 0), null;
|
|
103
|
+
let error = attemptErrors.length > 0 ? ["Could not extract YouTube video content.", "", ...attemptErrors.map((message) => `- ${message}`)].join(`
|
|
104
|
+
`) : "Could not extract YouTube video content. Sign into Google in Chrome for automatic access, or set GEMINI_API_KEY.";
|
|
105
|
+
return activityMonitor.logError(activityId, error), { url, title: "", content: "", error };
|
|
106
|
+
}
|
|
107
|
+
function mapYtDlpError(err) {
|
|
108
|
+
let { code, stderr, message } = readExecError(err);
|
|
109
|
+
if (code === "ENOENT")
|
|
110
|
+
return "yt-dlp is not installed. Install with: brew install yt-dlp";
|
|
111
|
+
if (isTimeoutError(err))
|
|
112
|
+
return "yt-dlp timed out fetching video info";
|
|
113
|
+
let lower = stderr.toLowerCase();
|
|
114
|
+
if (lower.includes("private"))
|
|
115
|
+
return "Video is private or unavailable";
|
|
116
|
+
if (lower.includes("sign in"))
|
|
117
|
+
return "Video is age-restricted and requires authentication";
|
|
118
|
+
if (lower.includes("not available"))
|
|
119
|
+
return "Video is unavailable in your region or has been removed";
|
|
120
|
+
if (lower.includes("live"))
|
|
121
|
+
return "Cannot extract frames from a live stream";
|
|
122
|
+
let snippet = trimErrorText(stderr || message);
|
|
123
|
+
return snippet ? `yt-dlp failed: ${snippet}` : "yt-dlp failed";
|
|
124
|
+
}
|
|
125
|
+
async function getYouTubeStreamInfo(videoId) {
|
|
126
|
+
try {
|
|
127
|
+
let lines = execFileSync("yt-dlp", [
|
|
128
|
+
"--print",
|
|
129
|
+
"duration",
|
|
130
|
+
"-g",
|
|
131
|
+
`https://www.youtube.com/watch?v=${videoId}`
|
|
132
|
+
], { timeout: 15e3, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim().split(/\r?\n/), rawDuration = lines[0]?.trim(), streamUrl = lines[1]?.trim();
|
|
133
|
+
if (!streamUrl)
|
|
134
|
+
return { error: "yt-dlp failed: missing stream URL" };
|
|
135
|
+
let parsedDuration = rawDuration && rawDuration !== "NA" ? Number.parseFloat(rawDuration) : NaN, duration = Number.isFinite(parsedDuration) ? parsedDuration : null;
|
|
136
|
+
return { streamUrl, duration };
|
|
137
|
+
} catch (err) {
|
|
138
|
+
return { error: mapYtDlpError(err) };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async function extractFrameFromStream(streamUrl, seconds) {
|
|
142
|
+
try {
|
|
143
|
+
let buffer = execFileSync("ffmpeg", [
|
|
144
|
+
"-ss",
|
|
145
|
+
String(seconds),
|
|
146
|
+
"-i",
|
|
147
|
+
streamUrl,
|
|
148
|
+
"-frames:v",
|
|
149
|
+
"1",
|
|
150
|
+
"-f",
|
|
151
|
+
"image2pipe",
|
|
152
|
+
"-vcodec",
|
|
153
|
+
"mjpeg",
|
|
154
|
+
"pipe:1"
|
|
155
|
+
], { maxBuffer: 5242880, timeout: 3e4, stdio: ["pipe", "pipe", "pipe"] });
|
|
156
|
+
return buffer.length === 0 ? { error: "ffmpeg failed: empty output" } : { data: buffer.toString("base64"), mimeType: "image/jpeg" };
|
|
157
|
+
} catch (err) {
|
|
158
|
+
return { error: mapFfmpegError(err) };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async function extractYouTubeFrame(videoId, seconds, streamInfo) {
|
|
162
|
+
let info = streamInfo ?? await getYouTubeStreamInfo(videoId);
|
|
163
|
+
return "error" in info ? info : extractFrameFromStream(info.streamUrl, seconds);
|
|
164
|
+
}
|
|
165
|
+
async function extractYouTubeFrames(videoId, timestamps, streamInfo) {
|
|
166
|
+
let info = streamInfo ?? await getYouTubeStreamInfo(videoId);
|
|
167
|
+
if ("error" in info)
|
|
168
|
+
return { frames: [], duration: null, error: info.error };
|
|
169
|
+
let results = await Promise.all(timestamps.map(async (t) => {
|
|
170
|
+
let frame = await extractFrameFromStream(info.streamUrl, t);
|
|
171
|
+
return "error" in frame ? { error: frame.error } : { ...frame, timestamp: formatSeconds(t) };
|
|
172
|
+
})), frames = results.filter((f) => "data" in f), errorResult = results.find((f) => "error" in f);
|
|
173
|
+
return { frames, duration: info.duration, error: frames.length === 0 && errorResult ? errorResult.error : null };
|
|
174
|
+
}
|
|
175
|
+
async function fetchYouTubeThumbnail(videoId) {
|
|
176
|
+
try {
|
|
177
|
+
let res = await fetch(`https://img.youtube.com/vi/${videoId}/hqdefault.jpg`, {
|
|
178
|
+
signal: AbortSignal.timeout(5e3)
|
|
179
|
+
});
|
|
180
|
+
if (!res.ok)
|
|
181
|
+
return null;
|
|
182
|
+
let buffer = Buffer.from(await res.arrayBuffer());
|
|
183
|
+
return buffer.length === 0 ? null : { data: buffer.toString("base64"), mimeType: "image/jpeg" };
|
|
184
|
+
} catch {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async function tryGeminiWeb(url, prompt, model, signal, attemptErrors) {
|
|
189
|
+
try {
|
|
190
|
+
let cookies = await isGeminiWebAvailable();
|
|
191
|
+
if (!cookies || signal?.aborted)
|
|
192
|
+
return null;
|
|
193
|
+
let text = await queryWithCookies(prompt, cookies, {
|
|
194
|
+
youtubeUrl: url,
|
|
195
|
+
model,
|
|
196
|
+
signal,
|
|
197
|
+
timeoutMs: 12e4
|
|
198
|
+
});
|
|
199
|
+
return {
|
|
200
|
+
url,
|
|
201
|
+
title: extractHeadingTitle(text) ?? "YouTube Video",
|
|
202
|
+
content: text,
|
|
203
|
+
error: null
|
|
204
|
+
};
|
|
205
|
+
} catch (err) {
|
|
206
|
+
if (shouldRethrow(err))
|
|
207
|
+
throw err;
|
|
208
|
+
return signal?.aborted || addAttemptError(attemptErrors, "Gemini Web", err), null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
async function tryGeminiApi(url, prompt, model, signal, attemptErrors) {
|
|
212
|
+
try {
|
|
213
|
+
if (!isGeminiApiAvailable() || signal?.aborted)
|
|
214
|
+
return null;
|
|
215
|
+
let text = await queryGeminiApiWithVideo(prompt, url, {
|
|
216
|
+
model,
|
|
217
|
+
signal,
|
|
218
|
+
timeoutMs: 12e4
|
|
219
|
+
});
|
|
220
|
+
return {
|
|
221
|
+
url,
|
|
222
|
+
title: extractHeadingTitle(text) ?? "YouTube Video",
|
|
223
|
+
content: text,
|
|
224
|
+
error: null
|
|
225
|
+
};
|
|
226
|
+
} catch (err) {
|
|
227
|
+
if (shouldRethrow(err))
|
|
228
|
+
throw err;
|
|
229
|
+
return signal?.aborted || addAttemptError(attemptErrors, "Gemini API", err), null;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
async function tryPerplexity(url, prompt, signal, attemptErrors) {
|
|
233
|
+
try {
|
|
234
|
+
if (signal?.aborted || !isPerplexityAvailable())
|
|
235
|
+
return null;
|
|
236
|
+
let perplexityQuery = prompt === YOUTUBE_PROMPT ? `Summarize this YouTube video in detail: ${url}` : `${prompt} YouTube video: ${url}`, { answer } = await searchWithPerplexity(perplexityQuery, { signal });
|
|
237
|
+
if (!answer)
|
|
238
|
+
return null;
|
|
239
|
+
let content = `# Video Summary (via Perplexity)
|
|
240
|
+
|
|
241
|
+
${answer}
|
|
242
|
+
|
|
243
|
+
*Full video understanding requires Gemini access. Set GEMINI_API_KEY or sign into Google in Chrome.*`;
|
|
244
|
+
return {
|
|
245
|
+
url,
|
|
246
|
+
title: "Video Summary (via Perplexity)",
|
|
247
|
+
content,
|
|
248
|
+
error: null
|
|
249
|
+
};
|
|
250
|
+
} catch (err) {
|
|
251
|
+
if (shouldRethrow(err))
|
|
252
|
+
throw err;
|
|
253
|
+
return signal?.aborted || addAttemptError(attemptErrors, "Perplexity", err), null;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
export {
|
|
257
|
+
extractYouTube,
|
|
258
|
+
extractYouTubeFrame,
|
|
259
|
+
extractYouTubeFrames,
|
|
260
|
+
fetchYouTubeThumbnail,
|
|
261
|
+
getYouTubeStreamInfo,
|
|
262
|
+
isYouTubeEnabled,
|
|
263
|
+
isYouTubeURL
|
|
264
|
+
};
|
package/dist/rpc.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc.d.ts","sourceRoot":"","sources":["../src/rpc.ts"],"names":[],"mappings":"","sourcesContent":["#!/usr/bin/env node\nimport { existsSync } from \"node:fs\";\n\nconst bundled = new URL(\"./node-runtime/rpc-entry.js\", import.meta.url);\nif (existsSync(bundled)) {\n\tawait import(bundled.href);\n} else {\n\tawait import(\"./rpc-entry.ts\");\n}\n"]}
|
package/dist/rpc.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
3
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
4
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
5
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
return path;
|
|
9
|
+
};
|
|
10
|
+
import { existsSync } from "node:fs";
|
|
11
|
+
const bundled = new URL("./node-runtime/rpc-entry.js", import.meta.url);
|
|
12
|
+
if (existsSync(bundled)) {
|
|
13
|
+
await import(__rewriteRelativeImportExtension(bundled.href));
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
await import("./rpc-entry.js");
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=rpc.js.map
|
package/dist/rpc.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc.js","sourceRoot":"","sources":["../src/rpc.ts"],"names":[],"mappings":";;;;;;;;;AACA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,6BAA6B,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;AACxE,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;IACzB,MAAM,MAAM,kCAAC,OAAO,CAAC,IAAI,EAAC,CAAC;AAC5B,CAAC;KAAM,CAAC;IACP,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;AAChC,CAAC","sourcesContent":["#!/usr/bin/env node\nimport { existsSync } from \"node:fs\";\n\nconst bundled = new URL(\"./node-runtime/rpc-entry.js\", import.meta.url);\nif (existsSync(bundled)) {\n\tawait import(bundled.href);\n} else {\n\tawait import(\"./rpc-entry.ts\");\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"benchmark-request.d.ts","sourceRoot":"","sources":["../../src/startup/benchmark-request.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAG7D,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAiElF","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { createFauxCore, fauxAssistantMessage, fauxToolCall } from \"@ashx-j/lunr-ai\";\nimport type { AgentSession } from \"../core/agent-session.ts\";\nimport { markStartupMilestone } from \"./startup-milestones.ts\";\n\nexport function prepareBenchmarkRequest(session: AgentSession): () => Promise<void> {\n\tsession.setSessionName(\"Startup benchmark\");\n\tconst faux = createFauxCore({ provider: \"lunr-startup-benchmark\" });\n\tconst toolUrl = process.env.PI_STARTUP_BENCHMARK_TOOL_URL;\n\tif (toolUrl && new URL(toolUrl).hostname !== \"127.0.0.1\") throw new Error(\"Startup tool fixture must be local\");\n\tconst requestedTool = process.env.PI_STARTUP_BENCHMARK_TOOL;\n\tlet toolCall = toolUrl ? fauxToolCall(\"fetch_content\", { url: toolUrl }) : undefined;\n\tif (requestedTool === \"mcp\") toolCall = fauxToolCall(\"mcp\", {});\n\telse if (requestedTool === \"subagent\") toolCall = fauxToolCall(\"subagent\", { action: \"status\", view: \"fleet\" });\n\telse if (requestedTool === \"lsp\") toolCall = fauxToolCall(\"code_overview\", { path: \".\" });\n\telse if (requestedTool) throw new Error(`Unknown startup fixture tool: ${requestedTool}`);\n\tfaux.setResponses([\n\t\t(context) => {\n\t\t\tmarkStartupMilestone(\"first_request_dispatched\");\n\t\t\tconst expected = session.agent.state.tools.map((tool) => tool.name).sort();\n\t\t\tconst actual = context.tools?.map((tool) => tool.name).sort() ?? [];\n\t\t\tif (JSON.stringify(actual) !== JSON.stringify(expected) || !context.systemPrompt) {\n\t\t\t\tthrow new Error(\"Startup request lost registered tools or instructions\");\n\t\t\t}\n\t\t\tconst toolSchemaHash = createHash(\"sha256\").update(JSON.stringify(context.tools)).digest(\"hex\");\n\t\t\tprocess.stderr.write(\n\t\t\t\t`LUNR_STARTUP_REQUEST ${JSON.stringify({ tools: actual, toolSchemaHash, hasSystemPrompt: true })}\\n`,\n\t\t\t);\n\t\t\treturn toolCall\n\t\t\t\t? fauxAssistantMessage([toolCall], { stopReason: \"toolUse\" })\n\t\t\t\t: fauxAssistantMessage(\"startup-request-ok\");\n\t\t},\n\t\t(context) => {\n\t\t\tconst result = context.messages.at(-1);\n\t\t\tif (\n\t\t\t\tresult?.role !== \"toolResult\" ||\n\t\t\t\tresult.isError ||\n\t\t\t\tresult.toolName !== toolCall?.name ||\n\t\t\t\t(toolUrl && !JSON.stringify(result.content).includes(\"This local article verifies\")) ||\n\t\t\t\t(requestedTool === \"lsp\" && !JSON.stringify(result.content).includes(\"increment\"))\n\t\t\t) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Startup tool ${toolCall?.name} did not complete its local fixture: ${JSON.stringify(result?.content)}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tmarkStartupMilestone(\"first_tool_completed\");\n\t\t\treturn fauxAssistantMessage(\"startup-request-ok\");\n\t\t},\n\t]);\n\tsession.modelRuntime.registerProvider(\"lunr-startup-benchmark\", {\n\t\tapi: faux.api,\n\t\tapiKey: \"local-benchmark-only\",\n\t\tmodels: faux.models,\n\t\tstreamSimple: faux.streamSimple,\n\t});\n\tsession.agent.state.model = faux.getModel();\n\treturn async () => {\n\t\tawait session.prompt(\n\t\t\ttoolCall ? \"Use the local startup fixture tool.\" : \"Reply with startup-request-ok. Do not use tools.\",\n\t\t);\n\t\tconst last = session.state.messages.at(-1);\n\t\tif (last?.role !== \"assistant\" || last.stopReason === \"error\" || faux.state.callCount !== (toolCall ? 2 : 1)) {\n\t\t\tconst error = new Error(\n\t\t\t\t`Startup benchmark did not complete its local provider request: ${last?.role === \"assistant\" ? (last.errorMessage ?? last.stopReason) : last?.role}, calls=${faux.state.callCount}`,\n\t\t\t);\n\t\t\tconsole.error(error.message);\n\t\t\tthrow error;\n\t\t}\n\t\tmarkStartupMilestone(\"first_response_completed\");\n\t};\n}\n"]}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { createFauxCore, fauxAssistantMessage, fauxToolCall } from "@ashx-j/lunr-ai";
|
|
3
|
+
import { markStartupMilestone } from "./startup-milestones.js";
|
|
4
|
+
export function prepareBenchmarkRequest(session) {
|
|
5
|
+
session.setSessionName("Startup benchmark");
|
|
6
|
+
const faux = createFauxCore({ provider: "lunr-startup-benchmark" });
|
|
7
|
+
const toolUrl = process.env.PI_STARTUP_BENCHMARK_TOOL_URL;
|
|
8
|
+
if (toolUrl && new URL(toolUrl).hostname !== "127.0.0.1")
|
|
9
|
+
throw new Error("Startup tool fixture must be local");
|
|
10
|
+
const requestedTool = process.env.PI_STARTUP_BENCHMARK_TOOL;
|
|
11
|
+
let toolCall = toolUrl ? fauxToolCall("fetch_content", { url: toolUrl }) : undefined;
|
|
12
|
+
if (requestedTool === "mcp")
|
|
13
|
+
toolCall = fauxToolCall("mcp", {});
|
|
14
|
+
else if (requestedTool === "subagent")
|
|
15
|
+
toolCall = fauxToolCall("subagent", { action: "status", view: "fleet" });
|
|
16
|
+
else if (requestedTool === "lsp")
|
|
17
|
+
toolCall = fauxToolCall("code_overview", { path: "." });
|
|
18
|
+
else if (requestedTool)
|
|
19
|
+
throw new Error(`Unknown startup fixture tool: ${requestedTool}`);
|
|
20
|
+
faux.setResponses([
|
|
21
|
+
(context) => {
|
|
22
|
+
markStartupMilestone("first_request_dispatched");
|
|
23
|
+
const expected = session.agent.state.tools.map((tool) => tool.name).sort();
|
|
24
|
+
const actual = context.tools?.map((tool) => tool.name).sort() ?? [];
|
|
25
|
+
if (JSON.stringify(actual) !== JSON.stringify(expected) || !context.systemPrompt) {
|
|
26
|
+
throw new Error("Startup request lost registered tools or instructions");
|
|
27
|
+
}
|
|
28
|
+
const toolSchemaHash = createHash("sha256").update(JSON.stringify(context.tools)).digest("hex");
|
|
29
|
+
process.stderr.write(`LUNR_STARTUP_REQUEST ${JSON.stringify({ tools: actual, toolSchemaHash, hasSystemPrompt: true })}\n`);
|
|
30
|
+
return toolCall
|
|
31
|
+
? fauxAssistantMessage([toolCall], { stopReason: "toolUse" })
|
|
32
|
+
: fauxAssistantMessage("startup-request-ok");
|
|
33
|
+
},
|
|
34
|
+
(context) => {
|
|
35
|
+
const result = context.messages.at(-1);
|
|
36
|
+
if (result?.role !== "toolResult" ||
|
|
37
|
+
result.isError ||
|
|
38
|
+
result.toolName !== toolCall?.name ||
|
|
39
|
+
(toolUrl && !JSON.stringify(result.content).includes("This local article verifies")) ||
|
|
40
|
+
(requestedTool === "lsp" && !JSON.stringify(result.content).includes("increment"))) {
|
|
41
|
+
throw new Error(`Startup tool ${toolCall?.name} did not complete its local fixture: ${JSON.stringify(result?.content)}`);
|
|
42
|
+
}
|
|
43
|
+
markStartupMilestone("first_tool_completed");
|
|
44
|
+
return fauxAssistantMessage("startup-request-ok");
|
|
45
|
+
},
|
|
46
|
+
]);
|
|
47
|
+
session.modelRuntime.registerProvider("lunr-startup-benchmark", {
|
|
48
|
+
api: faux.api,
|
|
49
|
+
apiKey: "local-benchmark-only",
|
|
50
|
+
models: faux.models,
|
|
51
|
+
streamSimple: faux.streamSimple,
|
|
52
|
+
});
|
|
53
|
+
session.agent.state.model = faux.getModel();
|
|
54
|
+
return async () => {
|
|
55
|
+
await session.prompt(toolCall ? "Use the local startup fixture tool." : "Reply with startup-request-ok. Do not use tools.");
|
|
56
|
+
const last = session.state.messages.at(-1);
|
|
57
|
+
if (last?.role !== "assistant" || last.stopReason === "error" || faux.state.callCount !== (toolCall ? 2 : 1)) {
|
|
58
|
+
const error = new Error(`Startup benchmark did not complete its local provider request: ${last?.role === "assistant" ? (last.errorMessage ?? last.stopReason) : last?.role}, calls=${faux.state.callCount}`);
|
|
59
|
+
console.error(error.message);
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
markStartupMilestone("first_response_completed");
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=benchmark-request.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"benchmark-request.js","sourceRoot":"","sources":["../../src/startup/benchmark-request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE3F,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAE/D,MAAM,UAAU,uBAAuB,CAAC,OAAqB,EAAuB;IACnF,OAAO,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,cAAc,CAAC,EAAE,QAAQ,EAAE,wBAAwB,EAAE,CAAC,CAAC;IACpE,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC;IAC1D,IAAI,OAAO,IAAI,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,QAAQ,KAAK,WAAW;QAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IAChH,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC;IAC5D,IAAI,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,eAAe,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACrF,IAAI,aAAa,KAAK,KAAK;QAAE,QAAQ,GAAG,YAAY,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;SAC3D,IAAI,aAAa,KAAK,UAAU;QAAE,QAAQ,GAAG,YAAY,CAAC,UAAU,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;SAC3G,IAAI,aAAa,KAAK,KAAK;QAAE,QAAQ,GAAG,YAAY,CAAC,eAAe,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;SACrF,IAAI,aAAa;QAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,aAAa,EAAE,CAAC,CAAC;IAC1F,IAAI,CAAC,YAAY,CAAC;QACjB,CAAC,OAAO,EAAE,EAAE,CAAC;YACZ,oBAAoB,CAAC,0BAA0B,CAAC,CAAC;YACjD,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;YAC3E,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC;YACpE,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;gBAClF,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;YAC1E,CAAC;YACD,MAAM,cAAc,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAChG,OAAO,CAAC,MAAM,CAAC,KAAK,CACnB,wBAAwB,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,IAAI,CACpG,CAAC;YACF,OAAO,QAAQ;gBACd,CAAC,CAAC,oBAAoB,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;gBAC7D,CAAC,CAAC,oBAAoB,CAAC,oBAAoB,CAAC,CAAC;QAAA,CAC9C;QACD,CAAC,OAAO,EAAE,EAAE,CAAC;YACZ,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YACvC,IACC,MAAM,EAAE,IAAI,KAAK,YAAY;gBAC7B,MAAM,CAAC,OAAO;gBACd,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,IAAI;gBAClC,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,6BAA6B,CAAC,CAAC;gBACpF,CAAC,aAAa,KAAK,KAAK,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,EACjF,CAAC;gBACF,MAAM,IAAI,KAAK,CACd,gBAAgB,QAAQ,EAAE,IAAI,wCAAwC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CACvG,CAAC;YACH,CAAC;YACD,oBAAoB,CAAC,sBAAsB,CAAC,CAAC;YAC7C,OAAO,oBAAoB,CAAC,oBAAoB,CAAC,CAAC;QAAA,CAClD;KACD,CAAC,CAAC;IACH,OAAO,CAAC,YAAY,CAAC,gBAAgB,CAAC,wBAAwB,EAAE;QAC/D,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,MAAM,EAAE,sBAAsB;QAC9B,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,YAAY,EAAE,IAAI,CAAC,YAAY;KAC/B,CAAC,CAAC;IACH,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;IAC5C,OAAO,KAAK,IAAI,EAAE,CAAC;QAClB,MAAM,OAAO,CAAC,MAAM,CACnB,QAAQ,CAAC,CAAC,CAAC,qCAAqC,CAAC,CAAC,CAAC,kDAAkD,CACrG,CAAC;QACF,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3C,IAAI,IAAI,EAAE,IAAI,KAAK,WAAW,IAAI,IAAI,CAAC,UAAU,KAAK,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9G,MAAM,KAAK,GAAG,IAAI,KAAK,CACtB,kEAAkE,IAAI,EAAE,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,WAAW,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CACnL,CAAC;YACF,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC7B,MAAM,KAAK,CAAC;QACb,CAAC;QACD,oBAAoB,CAAC,0BAA0B,CAAC,CAAC;IAAA,CACjD,CAAC;AAAA,CACF","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { createFauxCore, fauxAssistantMessage, fauxToolCall } from \"@ashx-j/lunr-ai\";\nimport type { AgentSession } from \"../core/agent-session.ts\";\nimport { markStartupMilestone } from \"./startup-milestones.ts\";\n\nexport function prepareBenchmarkRequest(session: AgentSession): () => Promise<void> {\n\tsession.setSessionName(\"Startup benchmark\");\n\tconst faux = createFauxCore({ provider: \"lunr-startup-benchmark\" });\n\tconst toolUrl = process.env.PI_STARTUP_BENCHMARK_TOOL_URL;\n\tif (toolUrl && new URL(toolUrl).hostname !== \"127.0.0.1\") throw new Error(\"Startup tool fixture must be local\");\n\tconst requestedTool = process.env.PI_STARTUP_BENCHMARK_TOOL;\n\tlet toolCall = toolUrl ? fauxToolCall(\"fetch_content\", { url: toolUrl }) : undefined;\n\tif (requestedTool === \"mcp\") toolCall = fauxToolCall(\"mcp\", {});\n\telse if (requestedTool === \"subagent\") toolCall = fauxToolCall(\"subagent\", { action: \"status\", view: \"fleet\" });\n\telse if (requestedTool === \"lsp\") toolCall = fauxToolCall(\"code_overview\", { path: \".\" });\n\telse if (requestedTool) throw new Error(`Unknown startup fixture tool: ${requestedTool}`);\n\tfaux.setResponses([\n\t\t(context) => {\n\t\t\tmarkStartupMilestone(\"first_request_dispatched\");\n\t\t\tconst expected = session.agent.state.tools.map((tool) => tool.name).sort();\n\t\t\tconst actual = context.tools?.map((tool) => tool.name).sort() ?? [];\n\t\t\tif (JSON.stringify(actual) !== JSON.stringify(expected) || !context.systemPrompt) {\n\t\t\t\tthrow new Error(\"Startup request lost registered tools or instructions\");\n\t\t\t}\n\t\t\tconst toolSchemaHash = createHash(\"sha256\").update(JSON.stringify(context.tools)).digest(\"hex\");\n\t\t\tprocess.stderr.write(\n\t\t\t\t`LUNR_STARTUP_REQUEST ${JSON.stringify({ tools: actual, toolSchemaHash, hasSystemPrompt: true })}\\n`,\n\t\t\t);\n\t\t\treturn toolCall\n\t\t\t\t? fauxAssistantMessage([toolCall], { stopReason: \"toolUse\" })\n\t\t\t\t: fauxAssistantMessage(\"startup-request-ok\");\n\t\t},\n\t\t(context) => {\n\t\t\tconst result = context.messages.at(-1);\n\t\t\tif (\n\t\t\t\tresult?.role !== \"toolResult\" ||\n\t\t\t\tresult.isError ||\n\t\t\t\tresult.toolName !== toolCall?.name ||\n\t\t\t\t(toolUrl && !JSON.stringify(result.content).includes(\"This local article verifies\")) ||\n\t\t\t\t(requestedTool === \"lsp\" && !JSON.stringify(result.content).includes(\"increment\"))\n\t\t\t) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Startup tool ${toolCall?.name} did not complete its local fixture: ${JSON.stringify(result?.content)}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tmarkStartupMilestone(\"first_tool_completed\");\n\t\t\treturn fauxAssistantMessage(\"startup-request-ok\");\n\t\t},\n\t]);\n\tsession.modelRuntime.registerProvider(\"lunr-startup-benchmark\", {\n\t\tapi: faux.api,\n\t\tapiKey: \"local-benchmark-only\",\n\t\tmodels: faux.models,\n\t\tstreamSimple: faux.streamSimple,\n\t});\n\tsession.agent.state.model = faux.getModel();\n\treturn async () => {\n\t\tawait session.prompt(\n\t\t\ttoolCall ? \"Use the local startup fixture tool.\" : \"Reply with startup-request-ok. Do not use tools.\",\n\t\t);\n\t\tconst last = session.state.messages.at(-1);\n\t\tif (last?.role !== \"assistant\" || last.stopReason === \"error\" || faux.state.callCount !== (toolCall ? 2 : 1)) {\n\t\t\tconst error = new Error(\n\t\t\t\t`Startup benchmark did not complete its local provider request: ${last?.role === \"assistant\" ? (last.errorMessage ?? last.stopReason) : last?.role}, calls=${faux.state.callCount}`,\n\t\t\t);\n\t\t\tconsole.error(error.message);\n\t\t\tthrow error;\n\t\t}\n\t\tmarkStartupMilestone(\"first_response_completed\");\n\t};\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactive-view.d.ts","sourceRoot":"","sources":["../../src/startup/interactive-view.ts"],"names":[],"mappings":"AAIA,OAAO,EACN,KAAK,SAAS,EACd,SAAS,EACT,KAAK,eAAe,EAMpB,GAAG,EACH,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,gBAAgB,EAAuC,MAAM,oCAAoC,CAAC;AAE3G,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAMnF,KAAK,cAAc,GAAG,CAAC,UAAU,CAAC,EAAE,mBAAmB,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;AAsCnF,qBAAa,eAAe;IAC3B,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC;IACjB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;IACrD,QAAQ,CAAC,WAAW,EAAE,kBAAkB,CAAC;IACzC,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,gBAAgB,CAAC,CAAa;IACtC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAyB;IAClD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAC7C,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,eAAe,YAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAmB;IACnD,OAAO,CAAC,iBAAiB,CAAgB;IACzC,OAAO,CAAC,iBAAiB,CAAc;IACvC,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,aAAa,CAA4C;IAEjE,cAsDC;IAED,IAAI,eAAe,IAAI,OAAO,CAE7B;IAED,KAAK,IAAI,IAAI,CAMZ;IAED,KAAK,IAAI,IAAI,CAIZ;IAED,MAAM,IAAI,IAAI,CAKb;IAED,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAEjC;IAED,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAE3B;IAED,OAAO,IAAI,eAAe,CAGzB;IAED,gBAAgB,CAAC,SAAS,EAAE,MAAM,eAAe,GAAG,IAAI,CAEvD;IAED,gFAAgF;IAChF,QAAQ,IAAI,IAAI,CAYf;IAED,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAS9B;IAEK,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAOxC;IAED,IAAI,IAAI,IAAI,CAUX;IAEK,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,CAAC,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAmBlG;IAEK,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO9D;IAEK,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAgB5E;IAEK,aAAa,CAClB,qBAAqB,EAAE,cAAc,EACrC,iBAAiB,EAAE,cAAc,GAC/B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAqBxB;IAED,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,oBAAoB;YA0Bd,cAAc;CA4B5B","sourcesContent":["import * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport {\n\ttype Component,\n\tContainer,\n\ttype EditorComponent,\n\tisFocusable,\n\tProcessTerminal,\n\tSpacer,\n\tsetKeybindings,\n\tText,\n\tTUI,\n} from \"@ashx-j/lunr-tui\";\nimport { getChatboxEditor, renderStatsLine, thinkingThemeToken } from \"../builtin-extensions/ashxj-tui.ts\";\nimport { APP_NAME, getAgentDir, VERSION } from \"../config.ts\";\nimport { KeybindingsManager } from \"../core/keybindings.ts\";\nimport { registerPermissionModeBridge } from \"../core/permission-mode.ts\";\nimport type { SessionInfo, SessionListProgress } from \"../core/session-manager.ts\";\nimport type { Settings } from \"../core/settings-manager.ts\";\nimport { BootScreenComponent } from \"../modes/interactive/components/boot-screen.ts\";\nimport { getEditorTheme, getThemeName, initTheme, theme } from \"../modes/interactive/theme/theme.ts\";\nimport { markStartupMilestone } from \"./startup-milestones.ts\";\n\ntype SessionsLoader = (onProgress?: SessionListProgress) => Promise<SessionInfo[]>;\n\n// Read display preferences only: no auth, models, resources, migrations, or writes.\nfunction readDisplaySettings(): Partial<Settings> {\n\ttry {\n\t\tconst value = JSON.parse(fs.readFileSync(path.join(getAgentDir(), \"settings.json\"), \"utf8\"));\n\t\treturn value && typeof value === \"object\" && !Array.isArray(value) ? value : {};\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nclass StartupProcessTerminal extends ProcessTerminal {\n\tprivate watchForFirstFrame = false;\n\tprivate firstFrameCallback: (() => void) | undefined;\n\n\tsetFirstFrameCallback(callback: () => void): void {\n\t\tthis.firstFrameCallback = callback;\n\t\tthis.watchForFirstFrame = true;\n\t}\n\n\toverride start(onInput: (data: string) => void, onResize: () => void): void {\n\t\tsuper.start(onInput, onResize);\n\t\tmarkStartupMilestone(\"input_handler_armed\");\n\t\tif (process.stdin.isTTY) markStartupMilestone(\"raw_mode_active\");\n\t}\n\n\toverride write(data: string): void {\n\t\tsuper.write(data);\n\t\t// TUI ends each content frame with synchronized-output end. Setup writes do not count.\n\t\tif (this.watchForFirstFrame && data.includes(\"\\x1b[?2026l\")) {\n\t\t\tthis.watchForFirstFrame = false;\n\t\t\tmarkStartupMilestone(\"first_frame_committed\");\n\t\t\tthis.firstFrameCallback?.();\n\t\t}\n\t}\n}\n\nexport class InteractiveView {\n\treadonly ui: TUI;\n\treadonly editor: ReturnType<typeof getChatboxEditor>;\n\treadonly keybindings: KeybindingsManager;\n\tprivate submitRequested = false;\n\tprivate inputUnsubscribe?: () => void;\n\tprivate modalActive = false;\n\tprivate readonly terminal: StartupProcessTerminal;\n\tprivate readonly status = new Text(\"\", 1, 0);\n\treadonly header: Component;\n\treadonly footer: Component;\n\treadonly statusContainer = new Container();\n\tprivate readonly editorContainer = new Container();\n\tprivate firstFramePromise: Promise<void>;\n\tprivate resolveFirstFrame!: () => void;\n\tprivate exitPromise: Promise<void>;\n\tprivate resolveExit!: () => void;\n\tprivate started = false;\n\tprivate handedOff = false;\n\tprivate exitRequested = false;\n\tprivate lastClearAt = 0;\n\tprivate currentEditor: () => EditorComponent = () => this.editor;\n\n\tconstructor() {\n\t\tconst settings = readDisplaySettings();\n\t\tconst permission = settings.defaultPermissionMode;\n\t\tregisterPermissionModeBridge(() =>\n\t\t\tpermission === \"auto\" || permission === \"yolo\" || permission === \"plan\" ? permission : \"manual\",\n\t\t);\n\t\tinitTheme(typeof settings.theme === \"string\" ? settings.theme : undefined);\n\t\tthis.terminal = new StartupProcessTerminal();\n\t\tthis.ui = new TUI(this.terminal, settings.showHardwareCursor);\n\t\tthis.ui.setClearOnShrink(settings.terminal?.clearOnShrink ?? false);\n\t\tthis.keybindings = KeybindingsManager.create();\n\t\tsetKeybindings(this.keybindings);\n\t\tconst ctx = {\n\t\t\tmode: \"tui\" as const,\n\t\t\thasUI: true,\n\t\t\tmodel: settings.defaultModel ? { id: settings.defaultModel, provider: settings.defaultProvider } : undefined,\n\t\t\tsessionManager: { getEntries: () => [] },\n\t\t\tgetContextUsage: () => undefined,\n\t\t\tui: { theme, setEditorComponent() {}, setFooter() {} },\n\t\t};\n\t\tconst effort = settings.defaultThinkingLevel ?? \"off\";\n\t\tthis.editor = getChatboxEditor(this.ui, getEditorTheme(), this.keybindings as never, ctx, {\n\t\t\tgetThinkingLevel: () => effort,\n\t\t\ton() {},\n\t\t});\n\t\tthis.editor.borderColor = (text) => theme.fg(thinkingThemeToken(effort) as Parameters<typeof theme.fg>[0], text);\n\t\tthis.editor.disableSubmit = true;\n\t\tthis.header = settings.quietStartup\n\t\t\t? new Text(\"\", 0, 0)\n\t\t\t: new BootScreenComponent(theme.bold(theme.fg(\"accent\", APP_NAME)) + theme.fg(\"dim\", ` v${VERSION}`), [\n\t\t\t\t\t{ label: \"directory\", value: process.cwd() },\n\t\t\t\t\t{ label: \"config\", value: getAgentDir() },\n\t\t\t\t\t{ label: \"theme\", value: getThemeName() },\n\t\t\t\t]);\n\t\tthis.footer = {\n\t\t\tinvalidate() {},\n\t\t\trender: (width) =>\n\t\t\t\trenderStatsLine(width, ctx, theme, {\n\t\t\t\t\tgetGitBranch: () => null,\n\t\t\t\t\tgetExtensionStatuses: () => new Map(),\n\t\t\t\t\tgetAvailableProviderCount: () => 0,\n\t\t\t\t\tonBranchChange: () => () => {},\n\t\t\t\t}),\n\t\t};\n\t\tthis.editorContainer.addChild(this.editor);\n\t\tthis.statusContainer.addChild(this.status);\n\t\tthis.firstFramePromise = new Promise((resolve) => {\n\t\t\tthis.resolveFirstFrame = resolve;\n\t\t});\n\t\tthis.exitPromise = new Promise((resolve) => {\n\t\t\tthis.resolveExit = resolve;\n\t\t});\n\t\tthis.terminal.setFirstFrameCallback(this.resolveFirstFrame);\n\t\tthis.installInputHandlers();\n\t}\n\n\tget isExitRequested(): boolean {\n\t\treturn this.exitRequested;\n\t}\n\n\tstart(): void {\n\t\tif (this.started) return;\n\t\tthis.restoreLayout();\n\t\tthis.ui.setAlternateScreen(true);\n\t\tthis.ui.start();\n\t\tthis.started = true;\n\t}\n\n\tpause(): void {\n\t\tif (!this.started || this.handedOff) return;\n\t\tthis.ui.stop();\n\t\tthis.started = false;\n\t}\n\n\tresume(): void {\n\t\tif (this.started || this.handedOff || this.exitRequested) return;\n\t\tthis.restoreLayout();\n\t\tthis.ui.start();\n\t\tthis.started = true;\n\t}\n\n\twaitForFirstFrame(): Promise<void> {\n\t\treturn this.firstFramePromise;\n\t}\n\n\twaitForExit(): Promise<void> {\n\t\treturn this.exitPromise;\n\t}\n\n\tbinding(): InteractiveView {\n\t\tthis.handedOff = true;\n\t\treturn this;\n\t}\n\n\tsetCurrentEditor(getEditor: () => EditorComponent): void {\n\t\tthis.currentEditor = getEditor;\n\t}\n\n\t/** Called only after the normal submit handler and all extensions are ready. */\n\tactivate(): void {\n\t\tif (this.exitRequested) return;\n\t\tthis.inputUnsubscribe?.();\n\t\tthis.inputUnsubscribe = undefined;\n\t\tthis.editor.onPasteImage = undefined;\n\t\tthis.editor.disableSubmit = false;\n\t\tthis.status.setText(\"\");\n\t\tif (this.submitRequested) {\n\t\t\tthis.submitRequested = false;\n\t\t\tthis.currentEditor().handleInput(\"\\r\");\n\t\t}\n\t\tthis.ui.requestRender();\n\t}\n\n\tshowError(error: unknown): void {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tthis.status.setText(\n\t\t\t`Startup failed: ${message}\\nYour draft is intact. Press Ctrl+C twice or Ctrl+D on an empty draft to exit.`,\n\t\t);\n\t\tthis.editor.disableSubmit = true;\n\t\tthis.statusContainer.clear();\n\t\tthis.statusContainer.addChild(this.status);\n\t\tthis.ui.requestRender();\n\t}\n\n\tasync fail(error: unknown): Promise<void> {\n\t\tthis.showError(error);\n\t\tif (process.env.PI_STARTUP_BENCHMARK === \"1\") {\n\t\t\tthis.stop();\n\t\t\treturn;\n\t\t}\n\t\tawait this.exitPromise;\n\t}\n\n\tstop(): void {\n\t\tif (this.exitRequested) return;\n\t\tthis.exitRequested = true;\n\t\tif (this.started) {\n\t\t\tthis.ui.stop();\n\t\t\tthis.started = false;\n\t\t}\n\t\tthis.inputUnsubscribe?.();\n\t\tthis.resolveFirstFrame();\n\t\tthis.resolveExit();\n\t}\n\n\tasync select<T>(title: string, options: Array<{ label: string; value: T }>): Promise<T | undefined> {\n\t\tconst { ExtensionSelectorComponent } = await import(\"../modes/interactive/components/extension-selector.ts\");\n\t\treturn new Promise((resolve) => {\n\t\t\tlet settled = false;\n\t\t\tconst finish = (value: T | undefined) => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tthis.restoreLayout();\n\t\t\t\tresolve(value);\n\t\t\t};\n\t\t\tconst selector = new ExtensionSelectorComponent(\n\t\t\t\ttitle,\n\t\t\t\toptions.map((option) => option.label),\n\t\t\t\t(label) => finish(options.find((option) => option.label === label)?.value),\n\t\t\t\t() => finish(undefined),\n\t\t\t\t{ tui: this.ui },\n\t\t\t);\n\t\t\tthis.showModal(selector, selector);\n\t\t});\n\t}\n\n\tasync confirm(title: string, message: string): Promise<boolean> {\n\t\treturn (\n\t\t\t(await this.select(`${title}\\n${message}`, [\n\t\t\t\t{ label: \"Yes\", value: true },\n\t\t\t\t{ label: \"No\", value: false },\n\t\t\t])) ?? false\n\t\t);\n\t}\n\n\tasync input(title: string, placeholder?: string): Promise<string | undefined> {\n\t\tconst { ExtensionInputComponent } = await import(\"../modes/interactive/components/extension-input.ts\");\n\t\treturn new Promise((resolve) => {\n\t\t\tlet settled = false;\n\t\t\tconst finish = (value: string | undefined) => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tinput.dispose();\n\t\t\t\tthis.restoreLayout();\n\t\t\t\tresolve(value);\n\t\t\t};\n\t\t\tconst input = new ExtensionInputComponent(title, placeholder, finish, () => finish(undefined), {\n\t\t\t\ttui: this.ui,\n\t\t\t});\n\t\t\tthis.showModal(input, input);\n\t\t});\n\t}\n\n\tasync selectSession(\n\t\tcurrentSessionsLoader: SessionsLoader,\n\t\tallSessionsLoader: SessionsLoader,\n\t): Promise<string | null> {\n\t\tconst { SessionSelectorComponent } = await import(\"../modes/interactive/components/session-selector.ts\");\n\t\treturn new Promise((resolve) => {\n\t\t\tlet settled = false;\n\t\t\tconst finish = (path: string | null) => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tthis.restoreLayout();\n\t\t\t\tresolve(path);\n\t\t\t};\n\t\t\tconst selector = new SessionSelectorComponent(\n\t\t\t\tcurrentSessionsLoader,\n\t\t\t\tallSessionsLoader,\n\t\t\t\t(path) => finish(path),\n\t\t\t\t() => finish(null),\n\t\t\t\t() => this.stop(),\n\t\t\t\t() => this.ui.requestRender(),\n\t\t\t\t{ showRenameHint: false, keybindings: this.keybindings },\n\t\t\t);\n\t\t\tthis.showModal(selector, selector.getSessionList());\n\t\t});\n\t}\n\n\tprivate showModal(component: Parameters<TUI[\"addChild\"]>[0], focus: Parameters<TUI[\"setFocus\"]>[0]): void {\n\t\tthis.modalActive = true;\n\t\tthis.ui.pinFrom(null);\n\t\tthis.ui.clear();\n\t\tthis.ui.addChild(component);\n\t\tthis.ui.setFocus(focus);\n\t\tthis.ui.requestRender(true);\n\t}\n\n\tprivate restoreLayout(): void {\n\t\tthis.modalActive = false;\n\t\tthis.ui.pinFrom(null);\n\t\tthis.ui.clear();\n\t\tthis.ui.addChild(new Spacer(1));\n\t\tthis.ui.addChild(this.header);\n\t\tthis.ui.addChild(new Spacer(1));\n\t\tthis.ui.addChild(this.statusContainer);\n\t\tthis.ui.addChild(this.editorContainer);\n\t\tthis.ui.addChild(this.footer);\n\t\tthis.ui.pinFrom(this.editorContainer);\n\t\tthis.ui.setFocus(this.editor);\n\t\tthis.ui.requestRender(true);\n\t}\n\n\tprivate installInputHandlers(): void {\n\t\tthis.editor.onAction(\"app.clear\", () => {\n\t\t\tconst now = Date.now();\n\t\t\tif (this.editor.getText().length === 0 && now - this.lastClearAt < 500) {\n\t\t\t\tthis.stop();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.editor.setText(\"\");\n\t\t\tthis.lastClearAt = now;\n\t\t});\n\t\tthis.editor.onCtrlD = () => this.stop();\n\t\tthis.editor.onPasteImage = () => void this.pasteClipboard();\n\t\tthis.inputUnsubscribe = this.ui.addInputListener((data) => {\n\t\t\tconst editor = this.currentEditor();\n\t\t\tif (this.modalActive || !isFocusable(editor) || !editor.focused) return;\n\t\t\tif (this.keybindings.matches(data, \"tui.input.submit\")) {\n\t\t\t\tthis.submitRequested = editor.getText().trim().length > 0;\n\t\t\t\treturn { consume: true };\n\t\t\t}\n\t\t\tif (this.keybindings.matches(data, \"app.interrupt\") || this.keybindings.matches(data, \"app.clear\")) {\n\t\t\t\tthis.submitRequested = false;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t});\n\t}\n\n\tprivate async pasteClipboard(): Promise<void> {\n\t\ttry {\n\t\t\tconst [{ readClipboardText }, { extensionForImageMimeType, readClipboardImage }] = await Promise.all([\n\t\t\t\timport(\"../utils/clipboard.ts\"),\n\t\t\t\timport(\"../utils/clipboard-image.ts\"),\n\t\t\t]);\n\t\t\tconst image = await readClipboardImage();\n\t\t\tif (image) {\n\t\t\t\tconst extension = extensionForImageMimeType(image.mimeType) ?? \"png\";\n\t\t\t\tconst filePath = path.join(os.tmpdir(), `lunr-clipboard-${crypto.randomUUID()}.${extension}`);\n\t\t\t\tfs.writeFileSync(filePath, Buffer.from(image.bytes));\n\t\t\t\tconst id = this.editor.insertImageMarker({ path: filePath, mimeType: image.mimeType });\n\t\t\t\tthis.status.setText(`Pasted [image_${id}] while startup continues.`);\n\t\t\t\tthis.ui.requestRender();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst text = await readClipboardText();\n\t\t\tif (text) {\n\t\t\t\tthis.editor.insertTextAtCursor(text);\n\t\t\t\tthis.ui.requestRender();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.status.setText(\"Clipboard contains no image or text.\");\n\t\t} catch (error) {\n\t\t\tthis.status.setText(`Clipboard paste failed: ${error instanceof Error ? error.message : String(error)}`);\n\t\t}\n\t\tthis.ui.requestRender();\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"interactive-view.d.ts","sourceRoot":"","sources":["../../src/startup/interactive-view.ts"],"names":[],"mappings":"AAIA,OAAO,EACN,KAAK,SAAS,EACd,SAAS,EACT,KAAK,eAAe,EAMpB,GAAG,EACH,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,gBAAgB,EAAuC,MAAM,oCAAoC,CAAC;AAE3G,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAMnF,KAAK,cAAc,GAAG,CAAC,UAAU,CAAC,EAAE,mBAAmB,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;AAsCnF,qBAAa,eAAe;IAC3B,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC;IACjB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;IACrD,QAAQ,CAAC,WAAW,EAAE,kBAAkB,CAAC;IACzC,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,gBAAgB,CAAC,CAAa;IACtC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAyB;IAClD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAC7C,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,eAAe,YAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAmB;IACnD,OAAO,CAAC,iBAAiB,CAAgB;IACzC,OAAO,CAAC,iBAAiB,CAAc;IACvC,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,aAAa,CAA4C;IAEjE,cAsDC;IAED,IAAI,eAAe,IAAI,OAAO,CAE7B;IAED,KAAK,IAAI,IAAI,CAMZ;IAED,KAAK,IAAI,IAAI,CAIZ;IAED,MAAM,IAAI,IAAI,CAKb;IAED,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAEjC;IAED,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAE3B;IAED,OAAO,IAAI,eAAe,CAGzB;IAED,gBAAgB,CAAC,SAAS,EAAE,MAAM,eAAe,GAAG,IAAI,CAEvD;IAED,gFAAgF;IAChF,QAAQ,IAAI,IAAI,CAYf;IAED,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAS9B;IAEK,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAOxC;IAED,IAAI,IAAI,IAAI,CAUX;IAEK,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,CAAC,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAmBlG;IAEK,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO9D;IAEK,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAgB5E;IAEK,aAAa,CAClB,qBAAqB,EAAE,cAAc,EACrC,iBAAiB,EAAE,cAAc,GAC/B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAqBxB;IAED,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,oBAAoB;YA0Bd,cAAc;CA2B5B","sourcesContent":["import * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport {\n\ttype Component,\n\tContainer,\n\ttype EditorComponent,\n\tisFocusable,\n\tProcessTerminal,\n\tSpacer,\n\tsetKeybindings,\n\tText,\n\tTUI,\n} from \"@ashx-j/lunr-tui\";\nimport { getChatboxEditor, renderStatsLine, thinkingThemeToken } from \"../builtin-extensions/ashxj-tui.ts\";\nimport { APP_NAME, getAgentDir, VERSION } from \"../config.ts\";\nimport { KeybindingsManager } from \"../core/keybindings.ts\";\nimport { registerPermissionModeBridge } from \"../core/permission-mode.ts\";\nimport type { SessionInfo, SessionListProgress } from \"../core/session-manager.ts\";\nimport type { Settings } from \"../core/settings-manager.ts\";\nimport { BootScreenComponent } from \"../modes/interactive/components/boot-screen.ts\";\nimport { getEditorTheme, getThemeName, initTheme, theme } from \"../modes/interactive/theme/theme.ts\";\nimport { markStartupMilestone } from \"./startup-milestones.ts\";\n\ntype SessionsLoader = (onProgress?: SessionListProgress) => Promise<SessionInfo[]>;\n\n// Read display preferences only: no auth, models, resources, migrations, or writes.\nfunction readDisplaySettings(): Partial<Settings> {\n\ttry {\n\t\tconst value = JSON.parse(fs.readFileSync(path.join(getAgentDir(), \"settings.json\"), \"utf8\"));\n\t\treturn value && typeof value === \"object\" && !Array.isArray(value) ? value : {};\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nclass StartupProcessTerminal extends ProcessTerminal {\n\tprivate watchForFirstFrame = false;\n\tprivate firstFrameCallback: (() => void) | undefined;\n\n\tsetFirstFrameCallback(callback: () => void): void {\n\t\tthis.firstFrameCallback = callback;\n\t\tthis.watchForFirstFrame = true;\n\t}\n\n\toverride start(onInput: (data: string) => void, onResize: () => void): void {\n\t\tsuper.start(onInput, onResize);\n\t\tmarkStartupMilestone(\"input_handler_armed\");\n\t\tif (process.stdin.isTTY) markStartupMilestone(\"raw_mode_active\");\n\t}\n\n\toverride write(data: string): void {\n\t\tsuper.write(data);\n\t\t// TUI ends each content frame with synchronized-output end. Setup writes do not count.\n\t\tif (this.watchForFirstFrame && data.includes(\"\\x1b[?2026l\")) {\n\t\t\tthis.watchForFirstFrame = false;\n\t\t\tmarkStartupMilestone(\"first_frame_committed\");\n\t\t\tthis.firstFrameCallback?.();\n\t\t}\n\t}\n}\n\nexport class InteractiveView {\n\treadonly ui: TUI;\n\treadonly editor: ReturnType<typeof getChatboxEditor>;\n\treadonly keybindings: KeybindingsManager;\n\tprivate submitRequested = false;\n\tprivate inputUnsubscribe?: () => void;\n\tprivate modalActive = false;\n\tprivate readonly terminal: StartupProcessTerminal;\n\tprivate readonly status = new Text(\"\", 1, 0);\n\treadonly header: Component;\n\treadonly footer: Component;\n\treadonly statusContainer = new Container();\n\tprivate readonly editorContainer = new Container();\n\tprivate firstFramePromise: Promise<void>;\n\tprivate resolveFirstFrame!: () => void;\n\tprivate exitPromise: Promise<void>;\n\tprivate resolveExit!: () => void;\n\tprivate started = false;\n\tprivate handedOff = false;\n\tprivate exitRequested = false;\n\tprivate lastClearAt = 0;\n\tprivate currentEditor: () => EditorComponent = () => this.editor;\n\n\tconstructor() {\n\t\tconst settings = readDisplaySettings();\n\t\tconst permission = settings.defaultPermissionMode;\n\t\tregisterPermissionModeBridge(() =>\n\t\t\tpermission === \"auto\" || permission === \"yolo\" || permission === \"plan\" ? permission : \"manual\",\n\t\t);\n\t\tinitTheme(typeof settings.theme === \"string\" ? settings.theme : undefined);\n\t\tthis.terminal = new StartupProcessTerminal();\n\t\tthis.ui = new TUI(this.terminal, settings.showHardwareCursor);\n\t\tthis.ui.setClearOnShrink(settings.terminal?.clearOnShrink ?? false);\n\t\tthis.keybindings = KeybindingsManager.create();\n\t\tsetKeybindings(this.keybindings);\n\t\tconst ctx = {\n\t\t\tmode: \"tui\" as const,\n\t\t\thasUI: true,\n\t\t\tmodel: settings.defaultModel ? { id: settings.defaultModel, provider: settings.defaultProvider } : undefined,\n\t\t\tsessionManager: { getEntries: () => [] },\n\t\t\tgetContextUsage: () => undefined,\n\t\t\tui: { theme, setEditorComponent() {}, setFooter() {} },\n\t\t};\n\t\tconst effort = settings.defaultThinkingLevel ?? \"off\";\n\t\tthis.editor = getChatboxEditor(this.ui, getEditorTheme(), this.keybindings as never, ctx, {\n\t\t\tgetThinkingLevel: () => effort,\n\t\t\ton() {},\n\t\t});\n\t\tthis.editor.borderColor = (text) => theme.fg(thinkingThemeToken(effort) as Parameters<typeof theme.fg>[0], text);\n\t\tthis.editor.disableSubmit = true;\n\t\tthis.header = settings.quietStartup\n\t\t\t? new Text(\"\", 0, 0)\n\t\t\t: new BootScreenComponent(theme.bold(theme.fg(\"accent\", APP_NAME)) + theme.fg(\"dim\", ` v${VERSION}`), [\n\t\t\t\t\t{ label: \"directory\", value: process.cwd() },\n\t\t\t\t\t{ label: \"config\", value: getAgentDir() },\n\t\t\t\t\t{ label: \"theme\", value: getThemeName() },\n\t\t\t\t]);\n\t\tthis.footer = {\n\t\t\tinvalidate() {},\n\t\t\trender: (width) =>\n\t\t\t\trenderStatsLine(width, ctx, theme, {\n\t\t\t\t\tgetGitBranch: () => null,\n\t\t\t\t\tgetExtensionStatuses: () => new Map(),\n\t\t\t\t\tgetAvailableProviderCount: () => 0,\n\t\t\t\t\tonBranchChange: () => () => {},\n\t\t\t\t}),\n\t\t};\n\t\tthis.editorContainer.addChild(this.editor);\n\t\tthis.statusContainer.addChild(this.status);\n\t\tthis.firstFramePromise = new Promise((resolve) => {\n\t\t\tthis.resolveFirstFrame = resolve;\n\t\t});\n\t\tthis.exitPromise = new Promise((resolve) => {\n\t\t\tthis.resolveExit = resolve;\n\t\t});\n\t\tthis.terminal.setFirstFrameCallback(this.resolveFirstFrame);\n\t\tthis.installInputHandlers();\n\t}\n\n\tget isExitRequested(): boolean {\n\t\treturn this.exitRequested;\n\t}\n\n\tstart(): void {\n\t\tif (this.started) return;\n\t\tthis.restoreLayout();\n\t\tthis.ui.setAlternateScreen(true);\n\t\tthis.ui.start();\n\t\tthis.started = true;\n\t}\n\n\tpause(): void {\n\t\tif (!this.started || this.handedOff) return;\n\t\tthis.ui.stop();\n\t\tthis.started = false;\n\t}\n\n\tresume(): void {\n\t\tif (this.started || this.handedOff || this.exitRequested) return;\n\t\tthis.restoreLayout();\n\t\tthis.ui.start();\n\t\tthis.started = true;\n\t}\n\n\twaitForFirstFrame(): Promise<void> {\n\t\treturn this.firstFramePromise;\n\t}\n\n\twaitForExit(): Promise<void> {\n\t\treturn this.exitPromise;\n\t}\n\n\tbinding(): InteractiveView {\n\t\tthis.handedOff = true;\n\t\treturn this;\n\t}\n\n\tsetCurrentEditor(getEditor: () => EditorComponent): void {\n\t\tthis.currentEditor = getEditor;\n\t}\n\n\t/** Called only after the normal submit handler and all extensions are ready. */\n\tactivate(): void {\n\t\tif (this.exitRequested) return;\n\t\tthis.inputUnsubscribe?.();\n\t\tthis.inputUnsubscribe = undefined;\n\t\tthis.editor.onPasteImage = undefined;\n\t\tthis.editor.disableSubmit = false;\n\t\tthis.status.setText(\"\");\n\t\tif (this.submitRequested) {\n\t\t\tthis.submitRequested = false;\n\t\t\tthis.currentEditor().handleInput(\"\\r\");\n\t\t}\n\t\tthis.ui.requestRender();\n\t}\n\n\tshowError(error: unknown): void {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tthis.status.setText(\n\t\t\t`Startup failed: ${message}\\nYour draft is intact. Press Ctrl+C twice or Ctrl+D on an empty draft to exit.`,\n\t\t);\n\t\tthis.editor.disableSubmit = true;\n\t\tthis.statusContainer.clear();\n\t\tthis.statusContainer.addChild(this.status);\n\t\tthis.ui.requestRender();\n\t}\n\n\tasync fail(error: unknown): Promise<void> {\n\t\tthis.showError(error);\n\t\tif (process.env.PI_STARTUP_BENCHMARK === \"1\") {\n\t\t\tthis.stop();\n\t\t\treturn;\n\t\t}\n\t\tawait this.exitPromise;\n\t}\n\n\tstop(): void {\n\t\tif (this.exitRequested) return;\n\t\tthis.exitRequested = true;\n\t\tif (this.started) {\n\t\t\tthis.ui.stop();\n\t\t\tthis.started = false;\n\t\t}\n\t\tthis.inputUnsubscribe?.();\n\t\tthis.resolveFirstFrame();\n\t\tthis.resolveExit();\n\t}\n\n\tasync select<T>(title: string, options: Array<{ label: string; value: T }>): Promise<T | undefined> {\n\t\tconst { ExtensionSelectorComponent } = await import(\"../modes/interactive/components/extension-selector.ts\");\n\t\treturn new Promise((resolve) => {\n\t\t\tlet settled = false;\n\t\t\tconst finish = (value: T | undefined) => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tthis.restoreLayout();\n\t\t\t\tresolve(value);\n\t\t\t};\n\t\t\tconst selector = new ExtensionSelectorComponent(\n\t\t\t\ttitle,\n\t\t\t\toptions.map((option) => option.label),\n\t\t\t\t(label) => finish(options.find((option) => option.label === label)?.value),\n\t\t\t\t() => finish(undefined),\n\t\t\t\t{ tui: this.ui },\n\t\t\t);\n\t\t\tthis.showModal(selector, selector);\n\t\t});\n\t}\n\n\tasync confirm(title: string, message: string): Promise<boolean> {\n\t\treturn (\n\t\t\t(await this.select(`${title}\\n${message}`, [\n\t\t\t\t{ label: \"Yes\", value: true },\n\t\t\t\t{ label: \"No\", value: false },\n\t\t\t])) ?? false\n\t\t);\n\t}\n\n\tasync input(title: string, placeholder?: string): Promise<string | undefined> {\n\t\tconst { ExtensionInputComponent } = await import(\"../modes/interactive/components/extension-input.ts\");\n\t\treturn new Promise((resolve) => {\n\t\t\tlet settled = false;\n\t\t\tconst finish = (value: string | undefined) => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tinput.dispose();\n\t\t\t\tthis.restoreLayout();\n\t\t\t\tresolve(value);\n\t\t\t};\n\t\t\tconst input = new ExtensionInputComponent(title, placeholder, finish, () => finish(undefined), {\n\t\t\t\ttui: this.ui,\n\t\t\t});\n\t\t\tthis.showModal(input, input);\n\t\t});\n\t}\n\n\tasync selectSession(\n\t\tcurrentSessionsLoader: SessionsLoader,\n\t\tallSessionsLoader: SessionsLoader,\n\t): Promise<string | null> {\n\t\tconst { SessionSelectorComponent } = await import(\"../modes/interactive/components/session-selector.ts\");\n\t\treturn new Promise((resolve) => {\n\t\t\tlet settled = false;\n\t\t\tconst finish = (path: string | null) => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tthis.restoreLayout();\n\t\t\t\tresolve(path);\n\t\t\t};\n\t\t\tconst selector = new SessionSelectorComponent(\n\t\t\t\tcurrentSessionsLoader,\n\t\t\t\tallSessionsLoader,\n\t\t\t\t(path) => finish(path),\n\t\t\t\t() => finish(null),\n\t\t\t\t() => this.stop(),\n\t\t\t\t() => this.ui.requestRender(),\n\t\t\t\t{ showRenameHint: false, keybindings: this.keybindings },\n\t\t\t);\n\t\t\tthis.showModal(selector, selector.getSessionList());\n\t\t});\n\t}\n\n\tprivate showModal(component: Parameters<TUI[\"addChild\"]>[0], focus: Parameters<TUI[\"setFocus\"]>[0]): void {\n\t\tthis.modalActive = true;\n\t\tthis.ui.pinFrom(null);\n\t\tthis.ui.clear();\n\t\tthis.ui.addChild(component);\n\t\tthis.ui.setFocus(focus);\n\t\tthis.ui.requestRender(true);\n\t}\n\n\tprivate restoreLayout(): void {\n\t\tthis.modalActive = false;\n\t\tthis.ui.pinFrom(null);\n\t\tthis.ui.clear();\n\t\tthis.ui.addChild(new Spacer(1));\n\t\tthis.ui.addChild(this.header);\n\t\tthis.ui.addChild(new Spacer(1));\n\t\tthis.ui.addChild(this.statusContainer);\n\t\tthis.ui.addChild(this.editorContainer);\n\t\tthis.ui.addChild(this.footer);\n\t\tthis.ui.pinFrom(this.editorContainer);\n\t\tthis.ui.setFocus(this.editor);\n\t\tthis.ui.requestRender(true);\n\t}\n\n\tprivate installInputHandlers(): void {\n\t\tthis.editor.onAction(\"app.clear\", () => {\n\t\t\tconst now = Date.now();\n\t\t\tif (this.editor.getText().length === 0 && now - this.lastClearAt < 500) {\n\t\t\t\tthis.stop();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.editor.setText(\"\");\n\t\t\tthis.lastClearAt = now;\n\t\t});\n\t\tthis.editor.onCtrlD = () => this.stop();\n\t\tthis.editor.onPasteImage = () => void this.pasteClipboard();\n\t\tthis.inputUnsubscribe = this.ui.addInputListener((data) => {\n\t\t\tconst editor = this.currentEditor();\n\t\t\tif (this.modalActive || !isFocusable(editor) || !editor.focused) return;\n\t\t\tif (this.keybindings.matches(data, \"tui.input.submit\")) {\n\t\t\t\tthis.submitRequested = editor.getText().trim().length > 0;\n\t\t\t\treturn { consume: true };\n\t\t\t}\n\t\t\tif (this.keybindings.matches(data, \"app.interrupt\") || this.keybindings.matches(data, \"app.clear\")) {\n\t\t\t\tthis.submitRequested = false;\n\t\t\t}\n\t\t\treturn undefined;\n\t\t});\n\t}\n\n\tprivate async pasteClipboard(): Promise<void> {\n\t\ttry {\n\t\t\tconst [{ readClipboardText }, { extensionForImageMimeType, readClipboardImage }] = await Promise.all([\n\t\t\t\timport(\"../utils/clipboard.ts\"),\n\t\t\t\timport(\"../utils/clipboard-image.ts\"),\n\t\t\t]);\n\t\t\tconst image = await readClipboardImage();\n\t\t\tif (image) {\n\t\t\t\tconst extension = extensionForImageMimeType(image.mimeType) ?? \"png\";\n\t\t\t\tconst filePath = path.join(os.tmpdir(), `lunr-clipboard-${crypto.randomUUID()}.${extension}`);\n\t\t\t\tfs.writeFileSync(filePath, Buffer.from(image.bytes));\n\t\t\t\tthis.editor.insertImageMarker({ path: filePath, mimeType: image.mimeType });\n\t\t\t\tthis.ui.requestRender();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst text = await readClipboardText();\n\t\t\tif (text) {\n\t\t\t\tthis.editor.insertTextAtCursor(text);\n\t\t\t\tthis.ui.requestRender();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.status.setText(\"Clipboard contains no image or text.\");\n\t\t} catch (error) {\n\t\t\tthis.status.setText(`Clipboard paste failed: ${error instanceof Error ? error.message : String(error)}`);\n\t\t}\n\t\tthis.ui.requestRender();\n\t}\n}\n"]}
|
|
@@ -310,8 +310,7 @@ export class InteractiveView {
|
|
|
310
310
|
const extension = extensionForImageMimeType(image.mimeType) ?? "png";
|
|
311
311
|
const filePath = path.join(os.tmpdir(), `lunr-clipboard-${crypto.randomUUID()}.${extension}`);
|
|
312
312
|
fs.writeFileSync(filePath, Buffer.from(image.bytes));
|
|
313
|
-
|
|
314
|
-
this.status.setText(`Pasted [image_${id}] while startup continues.`);
|
|
313
|
+
this.editor.insertImageMarker({ path: filePath, mimeType: image.mimeType });
|
|
315
314
|
this.ui.requestRender();
|
|
316
315
|
return;
|
|
317
316
|
}
|