@ashx-j/lunr 0.2.26 → 0.2.28
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 +25 -1
- package/LICENSE +22 -0
- package/dist/builtin-extensions/ashxj-thinking.d.ts +1 -1
- package/dist/builtin-extensions/ashxj-thinking.d.ts.map +1 -1
- package/dist/builtin-extensions/ashxj-thinking.js +1 -1
- package/dist/builtin-extensions/ashxj-thinking.js.map +1 -1
- package/dist/builtin-extensions/ashxj-tui.d.ts +11 -1
- package/dist/builtin-extensions/ashxj-tui.d.ts.map +1 -1
- package/dist/builtin-extensions/ashxj-tui.js +6 -10
- package/dist/builtin-extensions/ashxj-tui.js.map +1 -1
- package/dist/builtin-extensions/lunr-cron.d.ts +4 -5
- package/dist/builtin-extensions/lunr-cron.d.ts.map +1 -1
- package/dist/builtin-extensions/lunr-cron.js +94 -108
- package/dist/builtin-extensions/lunr-cron.js.map +1 -1
- package/dist/builtin-extensions/lunr-local-providers/local-servers.d.ts.map +1 -1
- package/dist/builtin-extensions/lunr-local-providers/local-servers.js +3 -1
- package/dist/builtin-extensions/lunr-local-providers/local-servers.js.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/accounting.d.ts.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/accounting.js +5 -2
- package/dist/builtin-extensions/narumiruna-pi-goal/src/accounting.js.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.d.ts.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.js +2 -2
- package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.js.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.d.ts.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.d.ts.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.js +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.js.map +1 -1
- package/dist/builtin-extensions/pi-intercom/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-intercom/index.js +2 -2
- package/dist/builtin-extensions/pi-intercom/index.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.d.ts +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.js +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/index.js +1 -15
- package/dist/builtin-extensions/pi-lsp-extension/src/index.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.d.ts +6 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.js +187 -132
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.d.ts +8 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.js +142 -112
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.d.ts +12 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.js +14 -7
- package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.d.ts +11 -3
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.js +67 -41
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.d.ts +4 -2
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.js +3 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/commands.d.ts +2 -1
- package/dist/builtin-extensions/pi-mcp-adapter/commands.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/commands.js +6 -6
- package/dist/builtin-extensions/pi-mcp-adapter/commands.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/config.d.ts +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/config.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/config.js +11 -5
- package/dist/builtin-extensions/pi-mcp-adapter/config.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js +2 -2
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.js +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.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 +48 -40
- package/dist/builtin-extensions/pi-mcp-adapter/index.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/init.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/init.js +3 -1
- package/dist/builtin-extensions/pi-mcp-adapter/init.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.d.ts +12 -40
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.js +166 -235
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.d.ts +5 -0
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.js +43 -13
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.d.ts +14 -3
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.js +73 -21
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.js +4 -4
- package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.js.map +1 -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 +2 -2
- package/dist/builtin-extensions/pi-mcp-adapter/server-manager.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/state.d.ts +2 -0
- package/dist/builtin-extensions/pi-mcp-adapter/state.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/state.js.map +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/index.d.ts +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/index.js +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/index.js.map +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/models.d.ts +11 -5
- package/dist/builtin-extensions/pi-ollama-cloud/models.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/models.js +12 -32
- package/dist/builtin-extensions/pi-ollama-cloud/models.js.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/index.js +5 -3
- package/dist/builtin-extensions/pi-prompt-template-model/index.js.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.d.ts +2 -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 +21 -12
- 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.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js +7 -24
- 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.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js +39 -77
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.d.ts +4 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.js +2 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/background/chain-root-attachment.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts +6 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js +17 -31
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js +7 -2
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts +5 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js +78 -42
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js +8 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts +0 -3
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +134 -28
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js +26 -5
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts +7 -8
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js +60 -32
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/session-tokens.d.ts +7 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/session-tokens.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/session-tokens.js +70 -38
- package/dist/builtin-extensions/pi-subagents/src/shared/session-tokens.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts +9 -0
- package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/types.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/utils.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/utils.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/utils.js +15 -5
- package/dist/builtin-extensions/pi-subagents/src/shared/utils.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js +4 -3
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js.map +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 +13 -62
- 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 +1 -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 +18 -9
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +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 +3 -3
- package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js.map +1 -1
- package/dist/builtin-extensions/pi-tps.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-tps.js +28 -17
- package/dist/builtin-extensions/pi-tps.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/activity.d.ts +1 -0
- package/dist/builtin-extensions/pi-web-access/activity.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/activity.js +27 -5
- package/dist/builtin-extensions/pi-web-access/activity.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/bounded-body.d.ts +3 -0
- package/dist/builtin-extensions/pi-web-access/bounded-body.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-web-access/bounded-body.js +43 -0
- package/dist/builtin-extensions/pi-web-access/bounded-body.js.map +1 -0
- package/dist/builtin-extensions/pi-web-access/content-page.d.ts +24 -0
- package/dist/builtin-extensions/pi-web-access/content-page.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-web-access/content-page.js +57 -0
- package/dist/builtin-extensions/pi-web-access/content-page.js.map +1 -0
- package/dist/builtin-extensions/pi-web-access/extract.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/extract.js +7 -2
- package/dist/builtin-extensions/pi-web-access/extract.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/github-extract.d.ts +0 -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 +147 -36
- 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 +48 -73
- package/dist/builtin-extensions/pi-web-access/index.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts +12 -4
- package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/session-cleanup.js +35 -12
- package/dist/builtin-extensions/pi-web-access/session-cleanup.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/storage.d.ts +9 -6
- package/dist/builtin-extensions/pi-web-access/storage.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/storage.js +36 -24
- package/dist/builtin-extensions/pi-web-access/storage.js.map +1 -1
- package/dist/builtin-extensions/simple-pi-memory.d.ts +1 -1
- package/dist/builtin-extensions/simple-pi-memory.d.ts.map +1 -1
- package/dist/builtin-extensions/simple-pi-memory.js +1 -1
- package/dist/builtin-extensions/simple-pi-memory.js.map +1 -1
- package/dist/catalog/models.json +1 -1
- package/dist/catalog/providers/google-vertex.json +1 -1
- package/dist/catalog/providers/google.json +1 -1
- package/dist/catalog/publication.json +3 -3
- package/dist/cli/install-cli.d.ts.map +1 -1
- package/dist/cli/install-cli.js +38 -30
- package/dist/cli/install-cli.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts +13 -0
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +75 -11
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +1 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +1 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +46 -21
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +344 -126
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/browser/runtime.d.ts +2 -0
- package/dist/core/browser/runtime.d.ts.map +1 -1
- package/dist/core/browser/runtime.js +80 -45
- package/dist/core/browser/runtime.js.map +1 -1
- package/dist/core/browser/setup.d.ts.map +1 -1
- package/dist/core/browser/setup.js +12 -1
- package/dist/core/browser/setup.js.map +1 -1
- package/dist/core/cache-stats.d.ts +1 -1
- package/dist/core/cache-stats.d.ts.map +1 -1
- package/dist/core/cache-stats.js +1 -1
- package/dist/core/cache-stats.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +2 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +1 -0
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +5 -11
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +38 -92
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/computer-use/adapter.d.ts.map +1 -1
- package/dist/core/computer-use/adapter.js +7 -2
- package/dist/core/computer-use/adapter.js.map +1 -1
- package/dist/core/computer-use/image.d.ts.map +1 -1
- package/dist/core/computer-use/image.js +17 -5
- package/dist/core/computer-use/image.js.map +1 -1
- package/dist/core/computer-use/schemas.d.ts.map +1 -1
- package/dist/core/computer-use/schemas.js +23 -6
- package/dist/core/computer-use/schemas.js.map +1 -1
- package/dist/core/computer-use/workflow.d.ts.map +1 -1
- package/dist/core/computer-use/workflow.js +459 -119
- package/dist/core/computer-use/workflow.js.map +1 -1
- package/dist/core/context-breakdown.d.ts +2 -2
- package/dist/core/context-breakdown.d.ts.map +1 -1
- package/dist/core/context-breakdown.js +22 -20
- package/dist/core/context-breakdown.js.map +1 -1
- package/dist/core/cron/jobs.d.ts +26 -8
- package/dist/core/cron/jobs.d.ts.map +1 -1
- package/dist/core/cron/jobs.js +357 -232
- package/dist/core/cron/jobs.js.map +1 -1
- package/dist/core/cron/origin-context.d.ts +1 -0
- package/dist/core/cron/origin-context.d.ts.map +1 -1
- package/dist/core/cron/origin-context.js.map +1 -1
- package/dist/core/cron/scheduler.d.ts +22 -17
- package/dist/core/cron/scheduler.d.ts.map +1 -1
- package/dist/core/cron/scheduler.js +153 -73
- package/dist/core/cron/scheduler.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +1 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +8 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +11 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +4 -0
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +22 -1
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/large-subagent-launch.d.ts +1 -1
- package/dist/core/large-subagent-launch.d.ts.map +1 -1
- package/dist/core/large-subagent-launch.js +7 -7
- package/dist/core/large-subagent-launch.js.map +1 -1
- package/dist/core/model-registry.d.ts +2 -0
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +5 -0
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-runtime.d.ts +4 -1
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +40 -4
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/permissions.d.ts +3 -3
- package/dist/core/permissions.d.ts.map +1 -1
- package/dist/core/permissions.js +11 -35
- package/dist/core/permissions.js.map +1 -1
- package/dist/core/plan-mode.d.ts +5 -13
- package/dist/core/plan-mode.d.ts.map +1 -1
- package/dist/core/plan-mode.js +126 -116
- package/dist/core/plan-mode.js.map +1 -1
- package/dist/core/project-trust.d.ts.map +1 -1
- package/dist/core/project-trust.js +1 -1
- package/dist/core/project-trust.js.map +1 -1
- package/dist/core/prompt-resource-bridge.d.ts +12 -0
- package/dist/core/prompt-resource-bridge.d.ts.map +1 -0
- package/dist/core/prompt-resource-bridge.js +9 -0
- package/dist/core/prompt-resource-bridge.js.map +1 -0
- package/dist/core/protected-targets.d.ts +6 -0
- package/dist/core/protected-targets.d.ts.map +1 -0
- package/dist/core/protected-targets.js +45 -0
- package/dist/core/protected-targets.js.map +1 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +7 -0
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/rollback.d.ts +19 -3
- package/dist/core/rollback.d.ts.map +1 -1
- package/dist/core/rollback.js +172 -56
- package/dist/core/rollback.js.map +1 -1
- package/dist/core/runtime-credentials.d.ts +3 -0
- package/dist/core/runtime-credentials.d.ts.map +1 -1
- package/dist/core/runtime-credentials.js +14 -1
- package/dist/core/runtime-credentials.js.map +1 -1
- package/dist/core/sdk.d.ts +3 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +1 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +11 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +111 -33
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-ownership.d.ts.map +1 -1
- package/dist/core/session-ownership.js +8 -3
- package/dist/core/session-ownership.js.map +1 -1
- package/dist/core/session-retention.d.ts.map +1 -1
- package/dist/core/session-retention.js +18 -7
- package/dist/core/session-retention.js.map +1 -1
- package/dist/core/session-startup-settings.d.ts +10 -0
- package/dist/core/session-startup-settings.d.ts.map +1 -0
- package/dist/core/session-startup-settings.js +70 -0
- package/dist/core/session-startup-settings.js.map +1 -0
- package/dist/core/subscriptions.d.ts +16 -7
- package/dist/core/subscriptions.d.ts.map +1 -1
- package/dist/core/subscriptions.js +96 -55
- package/dist/core/subscriptions.js.map +1 -1
- package/dist/core/trust-manager.d.ts +2 -1
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +6 -1
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/core/update-check.d.ts.map +1 -1
- package/dist/core/update-check.js.map +1 -1
- package/dist/core/usage-accounting.d.ts +47 -0
- package/dist/core/usage-accounting.d.ts.map +1 -0
- package/dist/core/usage-accounting.js +205 -0
- package/dist/core/usage-accounting.js.map +1 -0
- package/dist/core/usage-adapters/kimi-coding.d.ts +2 -2
- package/dist/core/usage-adapters/kimi-coding.d.ts.map +1 -1
- package/dist/core/usage-adapters/kimi-coding.js +2 -2
- package/dist/core/usage-adapters/kimi-coding.js.map +1 -1
- package/dist/core/usage-adapters/openai-codex.d.ts +3 -2
- package/dist/core/usage-adapters/openai-codex.d.ts.map +1 -1
- package/dist/core/usage-adapters/openai-codex.js +3 -3
- package/dist/core/usage-adapters/openai-codex.js.map +1 -1
- package/dist/core/usage-adapters/xai.d.ts +2 -2
- package/dist/core/usage-adapters/xai.d.ts.map +1 -1
- package/dist/core/usage-adapters/xai.js +3 -3
- package/dist/core/usage-adapters/xai.js.map +1 -1
- package/dist/core/usage-adapters/zai.d.ts +2 -2
- package/dist/core/usage-adapters/zai.d.ts.map +1 -1
- package/dist/core/usage-adapters/zai.js +2 -2
- package/dist/core/usage-adapters/zai.js.map +1 -1
- package/dist/core/usage-history.d.ts +1 -18
- package/dist/core/usage-history.d.ts.map +1 -1
- package/dist/core/usage-history.js +46 -114
- package/dist/core/usage-history.js.map +1 -1
- package/dist/core/usage-service.d.ts +9 -2
- package/dist/core/usage-service.d.ts.map +1 -1
- package/dist/core/usage-service.js +95 -50
- package/dist/core/usage-service.js.map +1 -1
- package/dist/gateway/agent-bridge.d.ts +9 -1
- package/dist/gateway/agent-bridge.d.ts.map +1 -1
- package/dist/gateway/agent-bridge.js +91 -37
- package/dist/gateway/agent-bridge.js.map +1 -1
- package/dist/gateway/buttons.d.ts +8 -1
- package/dist/gateway/buttons.d.ts.map +1 -1
- package/dist/gateway/buttons.js +76 -18
- package/dist/gateway/buttons.js.map +1 -1
- package/dist/gateway/commands.d.ts +1 -0
- package/dist/gateway/commands.d.ts.map +1 -1
- package/dist/gateway/commands.js +10 -4
- package/dist/gateway/commands.js.map +1 -1
- package/dist/gateway/cron.d.ts +12 -11
- package/dist/gateway/cron.d.ts.map +1 -1
- package/dist/gateway/cron.js +65 -56
- package/dist/gateway/cron.js.map +1 -1
- package/dist/gateway/index.d.ts.map +1 -1
- package/dist/gateway/index.js +2 -1
- package/dist/gateway/index.js.map +1 -1
- package/dist/gateway/mobile-commands.d.ts +1 -0
- package/dist/gateway/mobile-commands.d.ts.map +1 -1
- package/dist/gateway/mobile-commands.js +69 -15
- package/dist/gateway/mobile-commands.js.map +1 -1
- package/dist/gateway/presenter.d.ts +12 -2
- package/dist/gateway/presenter.d.ts.map +1 -1
- package/dist/gateway/presenter.js +73 -38
- package/dist/gateway/presenter.js.map +1 -1
- package/dist/gateway/router.d.ts +2 -2
- package/dist/gateway/router.d.ts.map +1 -1
- package/dist/gateway/router.js +17 -2
- package/dist/gateway/router.js.map +1 -1
- package/dist/gateway/service.d.ts +2 -0
- package/dist/gateway/service.d.ts.map +1 -1
- package/dist/gateway/service.js +153 -0
- package/dist/gateway/service.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +86 -33
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +6 -4
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/context-view.d.ts.map +1 -1
- package/dist/modes/interactive/components/context-view.js +2 -2
- package/dist/modes/interactive/components/context-view.js.map +1 -1
- package/dist/modes/interactive/components/copyable-text.d.ts.map +1 -1
- package/dist/modes/interactive/components/copyable-text.js +3 -2
- package/dist/modes/interactive/components/copyable-text.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +1 -1
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +8 -12
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/usage-view.d.ts +5 -0
- package/dist/modes/interactive/components/usage-view.d.ts.map +1 -1
- package/dist/modes/interactive/components/usage-view.js +21 -6
- package/dist/modes/interactive/components/usage-view.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +3 -7
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +151 -131
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/smooth-streaming.d.ts.map +1 -1
- package/dist/modes/interactive/smooth-streaming.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +2 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/node-runtime/{adapter-VFG7WNPX.js → adapter-247RTYLS.js} +4 -5
- package/dist/node-runtime/agent-session-services-RTUGP3XR.js +54 -0
- package/dist/node-runtime/{brave-UWDA3I22.js → brave-2AFFAXNN.js} +1 -1
- package/dist/node-runtime/{builtin-extensions-I2HIFEMY.js → builtin-extensions-N4EUD6SB.js} +8 -7
- package/dist/node-runtime/{chunk-QH6YL7VK.js → chunk-252ZVPPF.js} +3 -3
- package/dist/node-runtime/{chunk-PCEW76PM.js → chunk-2OIPYKLZ.js} +6 -6
- package/dist/node-runtime/{chunk-XBWPBZBN.js → chunk-2SWW7KMW.js} +1 -1
- package/dist/node-runtime/{chunk-UQUCFAJ3.js → chunk-2VIW3IMF.js} +40 -7
- package/dist/node-runtime/{chunk-UOEDI7RP.js → chunk-4KNKQGHM.js} +2 -2
- package/dist/node-runtime/chunk-4QRUZ64T.js +1203 -0
- package/dist/node-runtime/{chunk-FQMM2EZ2.js → chunk-64XTQNLA.js} +29 -7
- package/dist/node-runtime/{chunk-SPVDBUOS.js → chunk-6O3IW3IA.js} +2 -2
- package/dist/node-runtime/{chunk-5ZQXRPMC.js → chunk-6ZZHGUSA.js} +2 -2
- package/dist/node-runtime/{chunk-WIF24I7G.js → chunk-APVM5NEU.js} +149 -144
- package/dist/node-runtime/{chunk-LWYIVUGV.js → chunk-B2IBAXNX.js} +48 -31
- package/dist/node-runtime/{chunk-M3LZLQW2.js → chunk-BDRGN3L2.js} +1 -0
- package/dist/node-runtime/{chunk-LJTIVM7I.js → chunk-BMRXLQWQ.js} +464 -160
- package/dist/node-runtime/{chunk-GJ2RDBRL.js → chunk-BYC6RKCN.js} +4 -2
- package/dist/node-runtime/{chunk-T4DA4XE7.js → chunk-DFMPRAQQ.js} +162 -82
- package/dist/node-runtime/{chunk-SN2KSMPQ.js → chunk-DQSCFU7R.js} +13 -7
- package/dist/node-runtime/{chunk-UVWCDQYF.js → chunk-E5DQGNJI.js} +455 -278
- package/dist/node-runtime/{chunk-4SBQAI4T.js → chunk-EBLAZVSA.js} +2058 -73
- package/dist/node-runtime/{chunk-CSFI6YXL.js → chunk-ES6PBSIE.js} +1 -1
- package/dist/node-runtime/{chunk-IF24A3FH.js → chunk-EVM653DD.js} +4 -4
- package/dist/node-runtime/{chunk-HTFRRDSF.js → chunk-F2SMQ6ES.js} +3 -2
- package/dist/node-runtime/chunk-F2V2F4ZG.js +114 -0
- package/dist/node-runtime/{chunk-IKU6Y7DL.js → chunk-FE3WZI7F.js} +9 -6
- package/dist/node-runtime/{chunk-6VYZ5UFP.js → chunk-FLGEEH27.js} +1 -1
- package/dist/node-runtime/{chunk-5EFIU3OM.js → chunk-FTKEZL27.js} +1 -1
- package/dist/node-runtime/{chunk-RGQAEOM5.js → chunk-FYIFPFXE.js} +23 -6
- package/dist/node-runtime/{chunk-HMBGROQK.js → chunk-GLDXBBL6.js} +3 -3
- package/dist/node-runtime/{chunk-KWA4OC2J.js → chunk-H6YBCWKG.js} +1 -1
- package/dist/node-runtime/{chunk-2MI5OPMN.js → chunk-HNNHHJAB.js} +68 -50
- package/dist/node-runtime/{chunk-I74LN5JG.js → chunk-HUD2RFJW.js} +2 -2
- package/dist/node-runtime/{chunk-XHIRAZ26.js → chunk-IAZSK7KK.js} +1 -1
- package/dist/node-runtime/{chunk-GYIXXQAO.js → chunk-ICKGAKWX.js} +23 -59
- package/dist/node-runtime/{chunk-X3PGBLQ7.js → chunk-IHHTWSXN.js} +9 -9
- package/dist/node-runtime/{chunk-DAPBPCBP.js → chunk-IPP5BIV5.js} +25 -3
- package/dist/node-runtime/{chunk-QFWG2RVL.js → chunk-IXG6F4NT.js} +11 -0
- package/dist/node-runtime/{chunk-L24TPBS4.js → chunk-JEK35V2M.js} +45 -31
- package/dist/node-runtime/{chunk-RDDAZOMU.js → chunk-JL4HYQXK.js} +1 -1
- package/dist/node-runtime/{chunk-2HMIRK7W.js → chunk-LHV3V67B.js} +5 -5
- package/dist/node-runtime/{chunk-UAEOTO52.js → chunk-MNQXDFPV.js} +6 -2
- package/dist/node-runtime/{chunk-MPKSOJBT.js → chunk-NHATCSOI.js} +4 -7
- package/dist/node-runtime/{chunk-RMQZU2YU.js → chunk-NNNR3QIY.js} +1 -1
- package/dist/node-runtime/{chunk-2KIPT4HJ.js → chunk-PWHTSQC4.js} +83 -21
- package/dist/node-runtime/{chunk-RWTKOC3B.js → chunk-QBTJG3UM.js} +1 -1
- package/dist/node-runtime/{chunk-RCR3XAQY.js → chunk-QT3F6UEV.js} +233 -169
- package/dist/node-runtime/{chunk-GS42T6MX.js → chunk-QW3AAHWN.js} +1 -1
- package/dist/node-runtime/{chunk-DVQSHCVP.js → chunk-R4WSB2MQ.js} +5 -5
- package/dist/node-runtime/{chunk-B4HP6ZWM.js → chunk-RHCGYWO7.js} +1 -1
- package/dist/node-runtime/{chunk-2CFDTH5B.js → chunk-RLGY2QIL.js} +45 -59
- package/dist/node-runtime/{chunk-CDVHZOOK.js → chunk-RY3FJ6WI.js} +29 -27
- package/dist/node-runtime/{chunk-KKUTQQV7.js → chunk-RYRGL7P4.js} +1 -1
- package/dist/node-runtime/{chunk-I54LGHWM.js → chunk-STMX7W5I.js} +3 -3
- package/dist/node-runtime/chunk-SWWHZ2RI.js +158 -0
- package/dist/node-runtime/{chunk-5Q4A37RB.js → chunk-TCT6PCUR.js} +32 -11
- package/dist/node-runtime/{chunk-P7XBVXGT.js → chunk-U3YVZ6UC.js} +18 -14
- package/dist/node-runtime/{chunk-D5UCKPBQ.js → chunk-UBWW2PID.js} +1 -1
- package/dist/node-runtime/{chunk-E75WFZ33.js → chunk-UNUZICC4.js} +3 -3
- package/dist/node-runtime/{chunk-RAK2QA2B.js → chunk-UYRKUKFE.js} +4 -4
- package/dist/node-runtime/{chunk-LWJXTA6Z.js → chunk-VSUEAI7O.js} +1377 -45
- package/dist/node-runtime/{chunk-NAY6UUQZ.js → chunk-WDIVLMOH.js} +1 -1
- package/dist/node-runtime/{chunk-E24EO34J.js → chunk-WPXUR3HE.js} +1 -1
- package/dist/node-runtime/{chunk-SLVOAQOY.js → chunk-WXYNALHD.js} +113 -1
- package/dist/node-runtime/{chunk-KJ6DWS3K.js → chunk-XEOCUEUJ.js} +121 -60
- package/dist/node-runtime/chunk-YD3IZAZ7.js +47 -0
- package/dist/node-runtime/chunk-YSB6URMT.js +41 -0
- package/dist/node-runtime/{chunk-YWOJIGFY.js → chunk-ZEVFBFNF.js} +95 -142
- package/dist/node-runtime/{chunk-5B6VKFEV.js → chunk-ZIQUS5QO.js} +1 -1
- package/dist/node-runtime/cli-runtime.js +4 -4
- package/dist/node-runtime/{command-FSBU2EVZ.js → command-YRT7I2BW.js} +5 -5
- package/dist/node-runtime/{commands-NAAL7KNT.js → commands-2ETOWWRA.js} +16 -16
- package/dist/node-runtime/{commands-EMBQQVVT.js → commands-D6A6H4TS.js} +11 -18
- package/dist/node-runtime/{config-QDD5PUE2.js → config-CU7M7BTK.js} +1 -1
- package/dist/node-runtime/{config-D4QUPAHX.js → config-DS7XLMEM.js} +2 -2
- package/dist/node-runtime/{direct-tool-executor-4D7UXYU4.js → direct-tool-executor-2QJXVQD2.js} +6 -11
- package/dist/node-runtime/{exa-LL64QZR2.js → exa-6VBZNPGE.js} +1 -1
- package/dist/node-runtime/{export-html-VM4J2MNR.js → export-html-DBQRRMAY.js} +4 -4
- package/dist/node-runtime/{extension-input-E7BJZ35H.js → extension-input-VBBMX7YV.js} +5 -5
- package/dist/node-runtime/{extension-selector-WU42XDTU.js → extension-selector-T5AOG5XB.js} +5 -5
- package/dist/node-runtime/{extract-EQJE7FZZ.js → extract-BFX2N35A.js} +3 -3
- package/dist/node-runtime/{gateway-DQM333YV.js → gateway-V6K2CDWN.js} +51 -42
- package/dist/node-runtime/{gemini-search-35OCRFLN.js → gemini-search-C66QHDT6.js} +2 -2
- package/dist/node-runtime/{gemini-url-context-UQY3PHHW.js → gemini-url-context-T7DEK73R.js} +3 -3
- package/dist/node-runtime/{github-extract-F64RZOEA.js → github-extract-HOGE3D6F.js} +107 -38
- package/dist/node-runtime/{goal-NJEE5CTU.js → goal-J7TYMOBK.js} +14 -8
- package/dist/node-runtime/index.js +51 -48
- package/dist/node-runtime/{init-P5AUV77I.js → init-JBQJIPYI.js} +3 -6
- package/dist/node-runtime/{install-cli-NN4YAU44.js → install-cli-INGHIVBY.js} +30 -25
- package/dist/node-runtime/interactive-mode-ASZ7APYP.js +75 -0
- package/dist/node-runtime/{interactive-view-NGFAMHPW.js → interactive-view-K33PM4OS.js} +8 -8
- package/dist/node-runtime/{lsp-manager-3ASUSO4T.js → lsp-manager-S554V5HR.js} +240 -155
- package/dist/node-runtime/{lunr-browser-IR7TL3J7.js → lunr-browser-H4FDOR3C.js} +7 -7
- package/dist/node-runtime/{lunr-computer-use-Y23CCDIJ.js → lunr-computer-use-VDP2QNRQ.js} +9 -9
- package/dist/node-runtime/{lunr-cron-USHC4LXH.js → lunr-cron-WD3YH4CR.js} +93 -80
- package/dist/node-runtime/main.js +43 -41
- package/dist/node-runtime/{mobile-commands-O5LCHL32.js → mobile-commands-ICUXFW5V.js} +12 -12
- package/dist/node-runtime/{model-runtime-OO5HFLWE.js → model-runtime-MZ2JMDV4.js} +4 -4
- package/dist/node-runtime/{openai-search-B3MSNCQA.js → openai-search-K72LJDYN.js} +1 -1
- package/dist/node-runtime/{package-manager-cli-Q7BL6KZO.js → package-manager-cli-CYM262SI.js} +20 -20
- package/dist/node-runtime/{parallel-AI2MNSCU.js → parallel-X724NCNU.js} +1 -1
- package/dist/node-runtime/{perplexity-QVJW3I35.js → perplexity-RDHQHVMK.js} +2 -2
- package/dist/node-runtime/{pi-intercom-QM53RLTS.js → pi-intercom-NHHEPBMI.js} +2 -2
- package/dist/node-runtime/{pi-mcp-adapter-3PHF4KZ4.js → pi-mcp-adapter-PP3GQJQ2.js} +98 -94
- package/dist/node-runtime/{pi-ollama-cloud-2F3Y2QNM.js → pi-ollama-cloud-4MNWIMFN.js} +11 -24
- package/dist/node-runtime/{pi-prompt-template-model-QWTCGLJU.js → pi-prompt-template-model-FTGELLS5.js} +32 -36
- package/dist/node-runtime/{pi-subagents-7IBSSGEW.js → pi-subagents-QPU3OXE4.js} +114 -167
- package/dist/node-runtime/{pi-web-access-WTGMNZ32.js → pi-web-access-Z3LP246C.js} +144 -108
- package/dist/node-runtime/{print-mode-XQ76CYMT.js → print-mode-2XHN5SUA.js} +3 -3
- package/dist/node-runtime/{process-registry-4FAKZQOL.js → process-registry-Q4QWIU33.js} +3 -3
- package/dist/node-runtime/{proxy-modes-5FAPSZEZ.js → proxy-modes-VEGC6QG5.js} +8 -15
- package/dist/node-runtime/rpc-entry.js +43 -41
- package/dist/node-runtime/{rpc-mode-O3XV4SID.js → rpc-mode-32DHJPEJ.js} +4 -4
- package/dist/node-runtime/{runtime-4VA6JKNF.js → runtime-EDUUGAQD.js} +56 -46
- package/dist/node-runtime/{runtime-EFPPD2UW.js → runtime-UGXM4NPQ.js} +2 -2
- package/dist/node-runtime/{runtime-bridges-7WVCLI76.js → runtime-bridges-DNKMNJCF.js} +7 -7
- package/dist/node-runtime/{service-MDQIPQ26.js → service-IHLGFLBT.js} +6 -4
- package/dist/node-runtime/{session-handoff-GWARVEWT.js → session-handoff-LYH53ESV.js} +4 -4
- package/dist/node-runtime/{session-manager-JB5J6XBN.js → session-manager-ZJ3N5Q74.js} +3 -3
- package/dist/node-runtime/{session-selector-GIM4DHQ2.js → session-selector-I73U3EIO.js} +7 -7
- package/dist/node-runtime/{settings-manager-DJDM5AHM.js → settings-manager-P2TKGYTR.js} +5 -5
- package/dist/node-runtime/{setup-JBKOLJ6F.js → setup-H7CCELIL.js} +15 -1
- package/dist/node-runtime/{setup-PFBWRNUI.js → setup-OLQ4GXWS.js} +17 -17
- package/dist/node-runtime/{src-IVPNZ2BX.js → src-6RV4PXKJ.js} +64 -36
- package/dist/node-runtime/{subagent-executor-LPZ4BHWE.js → subagent-executor-UI6SHG3X.js} +412 -288
- package/dist/node-runtime/{tavily-WEZFVFP7.js → tavily-KZBFYQB6.js} +1 -1
- package/dist/node-runtime/{theme-UOO5RVFN.js → theme-YLVZKKZD.js} +2 -2
- package/dist/node-runtime/{trust-manager-MMISNCYO.js → trust-manager-ZCU76D23.js} +2 -2
- package/dist/node-runtime/{update-cli-HNMOTNGX.js → update-cli-F3XXJANQ.js} +6 -6
- package/dist/node-runtime/{usage-service-LFB2SWYV.js → usage-service-CPAGBED5.js} +1 -1
- package/dist/node-runtime/{video-extract-OOZHR7SU.js → video-extract-UPKYIBW3.js} +3 -3
- package/dist/node-runtime/{workflow-OHPBEFO4.js → workflow-ICDI4N3J.js} +208 -78
- package/dist/node-runtime/{youtube-extract-FNPAUNHU.js → youtube-extract-77NT7FWH.js} +4 -4
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/exif-orientation.d.ts.map +1 -1
- package/dist/utils/exif-orientation.js +25 -10
- package/dist/utils/exif-orientation.js.map +1 -1
- package/dist/utils/image-resize-core.d.ts.map +1 -1
- package/dist/utils/image-resize-core.js +2 -0
- package/dist/utils/image-resize-core.js.map +1 -1
- package/dist/utils/paths.d.ts +4 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +33 -2
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/sanitize-terminal-text.d.ts +7 -0
- package/dist/utils/sanitize-terminal-text.d.ts.map +1 -0
- package/dist/utils/sanitize-terminal-text.js +67 -0
- package/dist/utils/sanitize-terminal-text.js.map +1 -0
- package/docs/browser.md +1 -1
- package/docs/custom-provider.md +3 -3
- package/docs/extensions.md +8 -4
- package/docs/features.md +25 -7
- package/docs/keybindings.md +1 -1
- package/docs/models.md +5 -5
- package/docs/packages.md +1 -1
- package/docs/providers.md +2 -2
- package/docs/rpc.md +7 -7
- package/docs/security.md +10 -5
- package/docs/sessions.md +2 -2
- package/docs/skills.md +1 -1
- package/docs/terminal-setup.md +7 -7
- package/docs/themes.md +1 -1
- package/docs/usage.md +1 -1
- package/examples/extensions/auto-commit-on-exit.ts +1 -1
- package/examples/extensions/bash-spawn-hook.ts +1 -1
- package/examples/extensions/built-in-tool-renderer.ts +1 -1
- package/examples/extensions/claude-rules.ts +1 -1
- package/examples/extensions/commands.ts +1 -1
- package/examples/extensions/custom-compaction.ts +1 -1
- package/examples/extensions/custom-provider-anthropic/index.ts +3 -3
- 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/index.ts +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/doom-overlay/index.ts +1 -1
- package/examples/extensions/git-merge-and-resolve.ts +2 -2
- package/examples/extensions/gondolin/index.ts +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/inline-bash.ts +2 -2
- package/examples/extensions/input-transform-streaming.ts +2 -2
- package/examples/extensions/input-transform.ts +2 -2
- package/examples/extensions/interactive-shell.ts +1 -1
- package/examples/extensions/kimi-deferred-tools.ts +1 -1
- package/examples/extensions/mac-system-theme.ts +1 -1
- package/examples/extensions/minimal-mode.ts +1 -1
- package/examples/extensions/modal-editor.ts +1 -1
- package/examples/extensions/model-status.ts +1 -1
- package/examples/extensions/notify.ts +3 -3
- package/examples/extensions/overlay-qa-tests.ts +1 -1
- package/examples/extensions/overlay-test.ts +1 -1
- package/examples/extensions/pirate.ts +1 -1
- package/examples/extensions/preset.ts +3 -3
- package/examples/extensions/project-trust.ts +4 -4
- package/examples/extensions/prompt-customizer.ts +1 -1
- package/examples/extensions/rainbow-editor.ts +1 -1
- package/examples/extensions/rpc-demo.ts +1 -1
- package/examples/extensions/sandbox/index.ts +7 -7
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/shutdown-command.ts +5 -5
- package/examples/extensions/ssh.ts +2 -2
- package/examples/extensions/subagent/README.md +3 -3
- package/examples/extensions/subagent/index.ts +2 -2
- package/examples/extensions/titlebar-spinner.ts +1 -1
- package/examples/extensions/tool-override.ts +1 -1
- package/examples/extensions/tools.ts +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/extensions/working-indicator.ts +1 -1
- package/examples/extensions/working-message-test.ts +1 -1
- package/examples/sdk/01-minimal.ts +1 -1
- package/examples/sdk/03-custom-prompt.ts +1 -1
- package/examples/sdk/04-skills.ts +1 -1
- package/examples/sdk/06-extensions.ts +2 -2
- package/examples/sdk/08-prompt-templates.ts +1 -1
- package/install-lock/package-lock.json +24 -24
- package/install-lock/package.json +2 -2
- package/npm-shrinkwrap.json +60 -24
- package/package.json +9 -9
- package/dist/modes/interactive/components/exit-card.d.ts +0 -22
- package/dist/modes/interactive/components/exit-card.d.ts.map +0 -1
- package/dist/modes/interactive/components/exit-card.js +0 -69
- package/dist/modes/interactive/components/exit-card.js.map +0 -1
- package/dist/node-runtime/agent-session-services-5YXS2PLF.js +0 -51
- package/dist/node-runtime/chunk-2IDTZAG5.js +0 -1415
- package/dist/node-runtime/chunk-3MISGG4S.js +0 -769
- package/dist/node-runtime/chunk-4HXHDDCF.js +0 -16
- package/dist/node-runtime/chunk-5B5QN3ZQ.js +0 -356
- package/dist/node-runtime/chunk-7IQGBKBJ.js +0 -2033
- package/dist/node-runtime/chunk-AUXVGBO4.js +0 -19
- package/dist/node-runtime/chunk-PTFBPO3C.js +0 -50
- package/dist/node-runtime/interactive-mode-YOHZVMKX.js +0 -74
- package/dist/node-runtime/mcp-auth-flow-QVLFVLEO.js +0 -33
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, truncateHead, } from "../../core/tools/truncate.js";
|
|
2
|
+
function utf8Prefix(bytes, limit) {
|
|
3
|
+
let end = Math.min(limit, bytes.length);
|
|
4
|
+
while (end > 0 && end < bytes.length && (bytes[end] & 0xc0) === 0x80)
|
|
5
|
+
end--;
|
|
6
|
+
return bytes.subarray(0, end).toString("utf8");
|
|
7
|
+
}
|
|
8
|
+
/** Page every retrieval response, including lists and errors. Offset counts UTF-8 bytes. */
|
|
9
|
+
export function contentPage(text, offset = 0, details = {}) {
|
|
10
|
+
const bytes = Buffer.from(text, "utf8");
|
|
11
|
+
if (!Number.isSafeInteger(offset) ||
|
|
12
|
+
offset < 0 ||
|
|
13
|
+
offset > bytes.length ||
|
|
14
|
+
(offset < bytes.length && (bytes[offset] & 0xc0) === 0x80)) {
|
|
15
|
+
return {
|
|
16
|
+
content: [
|
|
17
|
+
{
|
|
18
|
+
type: "text",
|
|
19
|
+
text: "Invalid offset. Use 0 or the nextOffset returned by the previous page.",
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
details: { error: "Invalid offset" },
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
// Reserve space for the continuation notice within the same byte and line limits.
|
|
26
|
+
const remaining = bytes.subarray(offset).toString("utf8");
|
|
27
|
+
const page = truncateHead(remaining, {
|
|
28
|
+
maxBytes: DEFAULT_MAX_BYTES - 256,
|
|
29
|
+
maxLines: DEFAULT_MAX_LINES - 3,
|
|
30
|
+
});
|
|
31
|
+
const body = page.firstLineExceedsLimit
|
|
32
|
+
? utf8Prefix(bytes.subarray(offset), DEFAULT_MAX_BYTES - 256)
|
|
33
|
+
: page.content;
|
|
34
|
+
const end = offset + Buffer.byteLength(body, "utf8");
|
|
35
|
+
const truncated = end < bytes.length;
|
|
36
|
+
const notice = truncated
|
|
37
|
+
? `\n\n[Truncated. Continue with the same responseId and selectors, offset: ${end}.]`
|
|
38
|
+
: "";
|
|
39
|
+
const metadata = Object.fromEntries(Object.entries(details).map(([key, value]) => [
|
|
40
|
+
key,
|
|
41
|
+
typeof value === "string"
|
|
42
|
+
? utf8Prefix(Buffer.from(value, "utf8"), 512)
|
|
43
|
+
: value,
|
|
44
|
+
]));
|
|
45
|
+
return {
|
|
46
|
+
content: [{ type: "text", text: body + notice }],
|
|
47
|
+
details: {
|
|
48
|
+
...metadata,
|
|
49
|
+
offset,
|
|
50
|
+
nextOffset: truncated ? end : null,
|
|
51
|
+
truncated,
|
|
52
|
+
totalBytes: bytes.length,
|
|
53
|
+
pageBytes: end - offset,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=content-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-page.js","sourceRoot":"","sources":["../../../src/builtin-extensions/pi-web-access/content-page.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,GACZ,MAAM,8BAA8B,CAAC;AAEtC,SAAS,UAAU,CAAC,KAAa,EAAE,KAAa,EAAU;IACzD,IAAI,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACxC,OAAO,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,IAAI;QAAE,GAAG,EAAE,CAAC;IAC5E,OAAO,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAAA,CAC/C;AAED,4FAA4F;AAC5F,MAAM,UAAU,WAAW,CAC1B,IAAY,EACZ,MAAM,GAAG,CAAC,EACV,OAAO,GAA4B,EAAE,EACpC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACxC,IACC,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC;QAC7B,MAAM,GAAG,CAAC;QACV,MAAM,GAAG,KAAK,CAAC,MAAM;QACrB,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,EACzD,CAAC;QACF,OAAO;YACN,OAAO,EAAE;gBACR;oBACC,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE,wEAAwE;iBAC9E;aACD;YACD,OAAO,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE;SACpC,CAAC;IACH,CAAC;IAED,kFAAkF;IAClF,MAAM,SAAS,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC1D,MAAM,IAAI,GAAG,YAAY,CAAC,SAAS,EAAE;QACpC,QAAQ,EAAE,iBAAiB,GAAG,GAAG;QACjC,QAAQ,EAAE,iBAAiB,GAAG,CAAC;KAC/B,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,IAAI,CAAC,qBAAqB;QACtC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB,GAAG,GAAG,CAAC;QAC7D,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;IAChB,MAAM,GAAG,GAAG,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACrD,MAAM,SAAS,GAAG,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC;IACrC,MAAM,MAAM,GAAG,SAAS;QACvB,CAAC,CAAC,4EAA4E,GAAG,IAAI;QACrF,CAAC,CAAC,EAAE,CAAC;IACN,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,CAClC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC;QAC7C,GAAG;QACH,OAAO,KAAK,KAAK,QAAQ;YACxB,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC;YAC7C,CAAC,CAAC,KAAK;KACR,CAAC,CACF,CAAC;IACF,OAAO;QACN,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAAC;QACzD,OAAO,EAAE;YACR,GAAG,QAAQ;YACX,MAAM;YACN,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI;YAClC,SAAS;YACT,UAAU,EAAE,KAAK,CAAC,MAAM;YACxB,SAAS,EAAE,GAAG,GAAG,MAAM;SACvB;KACD,CAAC;AAAA,CACF","sourcesContent":["import {\n\tDEFAULT_MAX_BYTES,\n\tDEFAULT_MAX_LINES,\n\ttruncateHead,\n} from \"../../core/tools/truncate.ts\";\n\nfunction utf8Prefix(bytes: Buffer, limit: number): string {\n\tlet end = Math.min(limit, bytes.length);\n\twhile (end > 0 && end < bytes.length && (bytes[end] & 0xc0) === 0x80) end--;\n\treturn bytes.subarray(0, end).toString(\"utf8\");\n}\n\n/** Page every retrieval response, including lists and errors. Offset counts UTF-8 bytes. */\nexport function contentPage(\n\ttext: string,\n\toffset = 0,\n\tdetails: Record<string, unknown> = {},\n) {\n\tconst bytes = Buffer.from(text, \"utf8\");\n\tif (\n\t\t!Number.isSafeInteger(offset) ||\n\t\toffset < 0 ||\n\t\toffset > bytes.length ||\n\t\t(offset < bytes.length && (bytes[offset] & 0xc0) === 0x80)\n\t) {\n\t\treturn {\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\ttext: \"Invalid offset. Use 0 or the nextOffset returned by the previous page.\",\n\t\t\t\t},\n\t\t\t],\n\t\t\tdetails: { error: \"Invalid offset\" },\n\t\t};\n\t}\n\n\t// Reserve space for the continuation notice within the same byte and line limits.\n\tconst remaining = bytes.subarray(offset).toString(\"utf8\");\n\tconst page = truncateHead(remaining, {\n\t\tmaxBytes: DEFAULT_MAX_BYTES - 256,\n\t\tmaxLines: DEFAULT_MAX_LINES - 3,\n\t});\n\tconst body = page.firstLineExceedsLimit\n\t\t? utf8Prefix(bytes.subarray(offset), DEFAULT_MAX_BYTES - 256)\n\t\t: page.content;\n\tconst end = offset + Buffer.byteLength(body, \"utf8\");\n\tconst truncated = end < bytes.length;\n\tconst notice = truncated\n\t\t? `\\n\\n[Truncated. Continue with the same responseId and selectors, offset: ${end}.]`\n\t\t: \"\";\n\tconst metadata = Object.fromEntries(\n\t\tObject.entries(details).map(([key, value]) => [\n\t\t\tkey,\n\t\t\ttypeof value === \"string\"\n\t\t\t\t? utf8Prefix(Buffer.from(value, \"utf8\"), 512)\n\t\t\t\t: value,\n\t\t]),\n\t);\n\treturn {\n\t\tcontent: [{ type: \"text\" as const, text: body + notice }],\n\t\tdetails: {\n\t\t\t...metadata,\n\t\t\toffset,\n\t\t\tnextOffset: truncated ? end : null,\n\t\t\ttruncated,\n\t\t\ttotalBytes: bytes.length,\n\t\t\tpageBytes: end - offset,\n\t\t},\n\t};\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../../src/builtin-extensions/pi-web-access/extract.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAqC,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAgDtF;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAuB9C;AAyBD,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAC3D,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAExD,MAAM,WAAW,gBAAgB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAgJD,wBAAsB,cAAc,CACnC,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE,cAAc,GACtB,OAAO,CAAC,gBAAgB,CAAC,CA0Q3B;AAuLD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK/D;AAMD,wBAAsB,eAAe,CACpC,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,CAAC,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE,cAAc,GACtB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAE7B","sourcesContent":["// @ts-nocheck\nimport { Readability } from \"@mozilla/readability\";\nimport { parseHTML } from \"linkedom\";\nimport TurndownService from \"turndown\";\nimport pLimit from \"p-limit\";\nimport { activityMonitor } from \"./activity.ts\";\nimport { extractRSCContent } from \"./rsc-extract.ts\";\nimport {\n\tloadGeminiUrlContext,\n\tloadGithubExtract,\n\tloadParallel,\n\tloadPdfExtract,\n\tloadVideoExtract,\n\tloadYoutubeExtract,\n} from \"./lazy.ts\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { fetchRemoteUrl, validateRemoteUrl, type Lookup } from \"./ssrf-protection.ts\";\nimport { formatSeconds, getWebSearchConfigPath } from \"./utils.ts\";\n\nconst YOUTUBE_URL_RE =\n\t/(?:(?:www\\.|m\\.)?youtube\\.com\\/(?:watch\\?.*v=|shorts\\/|live\\/|embed\\/|v\\/)|youtu\\.be\\/)([a-zA-Z0-9_-]{11})/;\n\nfunction isYouTubeURL(url: string): { isYouTube: boolean; videoId: string | null } {\n\ttry {\n\t\tconst parsed = new URL(url);\n\t\tif (parsed.pathname === \"/playlist\") {\n\t\t\treturn { isYouTube: false, videoId: null };\n\t\t}\n\t} catch {\n\t}\n\tconst match = url.match(YOUTUBE_URL_RE);\n\tif (!match) return { isYouTube: false, videoId: null };\n\treturn { isYouTube: true, videoId: match[1] };\n}\n\nfunction isPDF(url: string, contentType?: string): boolean {\n\tif (contentType?.includes(\"application/pdf\")) return true;\n\ttry {\n\t\treturn new URL(url).pathname.toLowerCase().endsWith(\".pdf\");\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction looksLikeLocalPath(input: string): boolean {\n\treturn input.startsWith(\"/\") || input.startsWith(\"./\") || input.startsWith(\"../\") || input.startsWith(\"file://\");\n}\n\nfunction looksLikeGitHubUrl(url: string): boolean {\n\ttry {\n\t\tconst host = new URL(url).hostname.toLowerCase();\n\t\treturn host === \"github.com\" || host === \"www.github.com\" || host.endsWith(\".github.com\");\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nconst DEFAULT_TIMEOUT_MS = 30000;\nconst CONCURRENT_LIMIT = 3;\n\nconst NON_RECOVERABLE_ERRORS = [\"Unsupported content type\", \"Response too large\"];\nconst MIN_USEFUL_CONTENT = 500;\nconst WEB_SEARCH_CONFIG_PATH = getWebSearchConfigPath();\n\n/**\n * Read `ssrf.allowRanges` (CIDR strings) from web-search.json. Returns [] when\n * the file is missing, unreadable, or the key is unset so SSRF protection stays\n * fully on by default. Throws when `ssrf.allowRanges` is present but not an array\n * so a mistyped value (e.g. a bare string instead of a JSON array) fails loudly\n * instead of being silently ignored. Exempts synthetic ranges used by TUN/fake-IP\n * proxies (e.g. 198.18.0.0/15).\n */\nexport function loadSsrfAllowRanges(): string[] {\n\tlet value: unknown;\n\ttry {\n\t\tif (!existsSync(WEB_SEARCH_CONFIG_PATH)) return [];\n\t\tconst raw = readFileSync(WEB_SEARCH_CONFIG_PATH, \"utf-8\");\n\t\tvalue = (JSON.parse(raw) as { ssrf?: { allowRanges?: unknown } })?.ssrf?.allowRanges;\n\t} catch {\n\t\t// Missing/unreadable file or invalid JSON: fail safe with SSRF fully on.\n\t\treturn [];\n\t}\n\tif (value === undefined || value === null) return [];\n\tif (!Array.isArray(value)) {\n\t\tthrow new Error(`ssrf.allowRanges in ${WEB_SEARCH_CONFIG_PATH} must be an array of CIDR strings`);\n\t}\n\tconst ranges: string[] = [];\n\tfor (const [index, entry] of value.entries()) {\n\t\tif (typeof entry !== \"string\") {\n\t\t\tthrow new Error(`ssrf.allowRanges in ${WEB_SEARCH_CONFIG_PATH} must contain only CIDR strings; entry ${index + 1} is ${typeof entry}`);\n\t\t}\n\t\tconst trimmed = entry.trim();\n\t\tif (trimmed) ranges.push(trimmed);\n\t}\n\treturn ranges;\n}\n\nfunction errorMessage(err: unknown): string {\n\treturn err instanceof Error ? err.message : String(err);\n}\n\nfunction isConfigParseError(err: unknown): boolean {\n\treturn errorMessage(err).startsWith(\"Failed to parse \");\n}\n\nfunction isAbortError(err: unknown): boolean {\n\treturn errorMessage(err).toLowerCase().includes(\"abort\");\n}\n\nfunction abortedResult(url: string): ExtractedContent {\n\treturn { url, title: \"\", content: \"\", error: \"Aborted\" };\n}\n\nconst turndown = new TurndownService({\n\theadingStyle: \"atx\",\n\tcodeBlockStyle: \"fenced\",\n});\n\nconst fetchLimit = pLimit(CONCURRENT_LIMIT);\n\nexport interface VideoFrame {\n\tdata: string;\n\tmimeType: string;\n\ttimestamp: string;\n}\n\nexport type FrameData = { data: string; mimeType: string };\nexport type FrameResult = FrameData | { error: string };\n\nexport interface ExtractedContent {\n\turl: string;\n\ttitle: string;\n\tcontent: string;\n\terror: string | null;\n\tthumbnail?: { data: string; mimeType: string };\n\tframes?: VideoFrame[];\n\tduration?: number;\n}\n\nexport interface ExtractOptions {\n\ttimeoutMs?: number;\n\tforceClone?: boolean;\n\tprompt?: string;\n\ttimestamp?: string;\n\tframes?: number;\n\tmodel?: string;\n\t/** Custom DNS resolver used for SSRF validation. Primarily a test seam. */\n\tlookup?: Lookup;\n}\n\nconst JINA_READER_BASE = \"https://r.jina.ai/\";\nconst JINA_TIMEOUT_MS = 30000;\n\nasync function extractWithJinaReader(\n\turl: string,\n\tsignal?: AbortSignal,\n\tlookup?: Lookup,\n): Promise<ExtractedContent | null> {\n\tconst jinaUrl = JINA_READER_BASE + url;\n\n\tconst activityId = activityMonitor.logStart({ type: \"api\", query: `jina: ${url}` });\n\n\ttry {\n\t\tawait validateRemoteUrl(url, { allowRanges: loadSsrfAllowRanges(), lookup });\n\t\tconst res = await fetch(jinaUrl, {\n\t\t\theaders: {\n\t\t\t\t\"Accept\": \"text/markdown\",\n\t\t\t\t\"X-No-Cache\": \"true\",\n\t\t\t},\n\t\t\tsignal: AbortSignal.any([\n\t\t\t\tAbortSignal.timeout(JINA_TIMEOUT_MS),\n\t\t\t\t...(signal ? [signal] : []),\n\t\t\t]),\n\t\t});\n\n\t\tif (!res.ok) {\n\t\t\tactivityMonitor.logComplete(activityId, res.status);\n\t\t\treturn null;\n\t\t}\n\n\t\tconst content = await res.text();\n\t\tactivityMonitor.logComplete(activityId, res.status);\n\n\t\tconst contentStart = content.indexOf(\"Markdown Content:\");\n\t\tif (contentStart < 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst markdownPart = content.slice(contentStart + 17).trim(); // 17 = \"Markdown Content:\".length\n\n\t\t// Check for failed JS rendering or minimal content\n\t\tif (markdownPart.length < 100 ||\n\t\t\tmarkdownPart.startsWith(\"Loading...\") ||\n\t\t\tmarkdownPart.startsWith(\"Please enable JavaScript\")) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst title = extractHeadingTitle(markdownPart) ?? (new URL(url).pathname.split(\"/\").pop() || url);\n\t\treturn { url, title, content: markdownPart, error: null };\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tif (message.toLowerCase().includes(\"abort\")) {\n\t\t\tactivityMonitor.logComplete(activityId, 0);\n\t\t} else {\n\t\t\tactivityMonitor.logError(activityId, message);\n\t\t}\n\t\treturn null;\n\t}\n}\n\nfunction parseTimestamp(ts: string): number | null {\n\tconst num = Number(ts);\n\tif (!isNaN(num) && num >= 0) return Math.floor(num);\n\tconst parts = ts.split(\":\").map(Number);\n\tif (parts.some(p => isNaN(p) || p < 0)) return null;\n\tif (parts.length === 3) return Math.floor(parts[0] * 3600 + parts[1] * 60 + parts[2]);\n\tif (parts.length === 2) return Math.floor(parts[0] * 60 + parts[1]);\n\treturn null;\n}\n\ntype TimestampSpec = { type: \"single\"; seconds: number } | { type: \"range\"; start: number; end: number };\n\nfunction parseTimestampSpec(ts: string): TimestampSpec | null {\n\tconst dashIdx = ts.indexOf(\"-\", 1);\n\tif (dashIdx > 0) {\n\t\tconst start = parseTimestamp(ts.slice(0, dashIdx));\n\t\tconst end = parseTimestamp(ts.slice(dashIdx + 1));\n\t\tif (start !== null && end !== null && end > start) return { type: \"range\", start, end };\n\t}\n\tconst seconds = parseTimestamp(ts);\n\treturn seconds !== null ? { type: \"single\", seconds } : null;\n}\n\nconst DEFAULT_RANGE_FRAMES = 6;\nconst MIN_FRAME_INTERVAL = 5;\n\nfunction computeRangeTimestamps(start: number, end: number, maxFrames: number = DEFAULT_RANGE_FRAMES): number[] {\n\tif (maxFrames <= 1) return [start];\n\tconst duration = end - start;\n\tconst idealInterval = duration / (maxFrames - 1);\n\tif (idealInterval < MIN_FRAME_INTERVAL) {\n\t\tconst timestamps: number[] = [];\n\t\tfor (let t = start; t <= end && timestamps.length < maxFrames; t += MIN_FRAME_INTERVAL) {\n\t\t\ttimestamps.push(t);\n\t\t}\n\t\treturn timestamps;\n\t}\n\treturn Array.from({ length: maxFrames }, (_, i) => Math.round(start + i * idealInterval));\n}\n\nfunction buildFrameResult(\n\turl: string, label: string, requestedCount: number,\n\tframes: VideoFrame[], error: string | null, duration?: number,\n): ExtractedContent {\n\tif (frames.length === 0) {\n\t\tconst msg = error ?? \"Frame extraction failed\";\n\t\treturn { url, title: `Frames ${label} (0/${requestedCount})`, content: msg, error: msg };\n\t}\n\treturn {\n\t\turl,\n\t\ttitle: `Frames ${label} (${frames.length}/${requestedCount})`,\n\t\tcontent: `${frames.length} frames extracted from ${label}`,\n\t\terror: null,\n\t\tframes,\n\t\tduration,\n\t};\n}\n\nasync function extractLocalFrames(\n\tfilePath: string, timestamps: number[],\n): Promise<{ frames: VideoFrame[]; error: string | null }> {\n\tconst { extractVideoFrame } = await loadVideoExtract();\n\tconst results = await Promise.all(timestamps.map(async (t) => {\n\t\tconst frame = await extractVideoFrame(filePath, t);\n\t\tif (\"error\" in frame) return { error: frame.error };\n\t\treturn { ...frame, timestamp: formatSeconds(t) };\n\t}));\n\tconst frames = results.filter((f): f is VideoFrame => \"data\" in f);\n\tconst firstError = results.find((f): f is { error: string } => \"error\" in f);\n\treturn { frames, error: frames.length === 0 && firstError ? firstError.error : null };\n}\n\nasync function safeVideoInfo(url: string): Promise<{ info: Awaited<ReturnType<typeof loadVideoExtract>> extends { isVideoFile: infer F } ? (F extends (...args: any) => infer R ? R : null) : null; error?: string }> {\n\tif (!looksLikeLocalPath(url)) return { info: null };\n\ttry {\n\t\tconst { isVideoFile } = await loadVideoExtract();\n\t\treturn { info: isVideoFile(url) };\n\t} catch (err) {\n\t\treturn { info: null, error: errorMessage(err) };\n\t}\n}\n\nexport async function extractContent(\n\turl: string,\n\tsignal?: AbortSignal,\n\toptions?: ExtractOptions,\n): Promise<ExtractedContent> {\n\tif (signal?.aborted) {\n\t\treturn { url, title: \"\", content: \"\", error: \"Aborted\" };\n\t}\n\n\tif (options?.frames && !options.timestamp) {\n\t\tconst frameCount = options.frames;\n\t\tconst ytInfo = isYouTubeURL(url);\n\t\tif (ytInfo.isYouTube && ytInfo.videoId) {\n\t\t\tconst yt = await loadYoutubeExtract();\n\t\t\tconst streamInfo = await yt.getYouTubeStreamInfo(ytInfo.videoId);\n\t\t\tif (\"error\" in streamInfo) {\n\t\t\t\treturn { url, title: \"Frames\", content: streamInfo.error, error: streamInfo.error };\n\t\t\t}\n\t\t\tif (streamInfo.duration === null) {\n\t\t\t\tconst error = \"Cannot determine video duration. Use a timestamp range instead.\";\n\t\t\t\treturn { url, title: \"Frames\", content: error, error };\n\t\t\t}\n\t\t\tconst dur = Math.floor(streamInfo.duration);\n\t\t\tconst timestamps = computeRangeTimestamps(0, dur, frameCount);\n\t\t\tconst result = await yt.extractYouTubeFrames(ytInfo.videoId, timestamps, streamInfo);\n\t\t\tconst label = `${formatSeconds(0)}-${formatSeconds(dur)}`;\n\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error, streamInfo.duration);\n\t\t}\n\n\t\tconst localVideo = await safeVideoInfo(url);\n\t\tif (localVideo.error) {\n\t\t\treturn { url, title: \"\", content: \"\", error: localVideo.error };\n\t\t}\n\t\tif (localVideo.info) {\n\t\t\tconst { getLocalVideoDuration } = await loadVideoExtract();\n\t\t\tconst durationResult = await getLocalVideoDuration(localVideo.info.absolutePath);\n\t\t\tif (typeof durationResult !== \"number\") {\n\t\t\t\treturn { url, title: \"Frames\", content: durationResult.error, error: durationResult.error };\n\t\t\t}\n\t\t\tconst dur = Math.floor(durationResult);\n\t\t\tconst timestamps = computeRangeTimestamps(0, dur, frameCount);\n\t\t\tconst result = await extractLocalFrames(localVideo.info.absolutePath, timestamps);\n\t\t\tconst label = `${formatSeconds(0)}-${formatSeconds(dur)}`;\n\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error, durationResult);\n\t\t}\n\n\t\treturn { url, title: \"\", content: \"\", error: \"Frame extraction only works with YouTube and local video files\" };\n\t}\n\n\tif (options?.timestamp) {\n\t\tconst spec = parseTimestampSpec(options.timestamp);\n\t\tif (!spec) {\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: \"\",\n\t\t\t\tcontent: \"\",\n\t\t\t\terror: `Invalid timestamp format: \"${options.timestamp}\". Use \"H:MM:SS\", \"MM:SS\", \"85\", or \"start-end\".`,\n\t\t\t};\n\t\t}\n\n\t\tconst frameCount = options.frames;\n\t\tconst ytInfo = isYouTubeURL(url);\n\t\tif (ytInfo.isYouTube && ytInfo.videoId) {\n\t\t\tconst yt = await loadYoutubeExtract();\n\t\t\tconst streamInfo = await yt.getYouTubeStreamInfo(ytInfo.videoId);\n\t\t\tif (\"error\" in streamInfo) {\n\t\t\t\tif (spec.type === \"range\") {\n\t\t\t\t\tconst label = `${formatSeconds(spec.start)}-${formatSeconds(spec.end)}`;\n\t\t\t\t\treturn { url, title: `Frames ${label}`, content: streamInfo.error, error: streamInfo.error };\n\t\t\t\t}\n\t\t\t\tif (frameCount) {\n\t\t\t\t\tconst end = spec.seconds + (frameCount - 1) * MIN_FRAME_INTERVAL;\n\t\t\t\t\tconst label = `${formatSeconds(spec.seconds)}-${formatSeconds(end)}`;\n\t\t\t\t\treturn { url, title: `Frames ${label}`, content: streamInfo.error, error: streamInfo.error };\n\t\t\t\t}\n\t\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: streamInfo.error, error: streamInfo.error };\n\t\t\t}\n\n\t\t\tif (spec.type === \"range\") {\n\t\t\t\tconst label = `${formatSeconds(spec.start)}-${formatSeconds(spec.end)}`;\n\t\t\t\tif (streamInfo.duration !== null && spec.end > streamInfo.duration) {\n\t\t\t\t\tconst error = `Timestamp ${formatSeconds(spec.end)} exceeds video duration (${formatSeconds(Math.floor(streamInfo.duration))})`;\n\t\t\t\t\treturn { url, title: `Frames ${label}`, content: error, error };\n\t\t\t\t}\n\t\t\t\tconst timestamps = frameCount\n\t\t\t\t\t? computeRangeTimestamps(spec.start, spec.end, frameCount)\n\t\t\t\t\t: computeRangeTimestamps(spec.start, spec.end);\n\t\t\t\tconst result = await yt.extractYouTubeFrames(ytInfo.videoId, timestamps, streamInfo);\n\t\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error, result.duration ?? undefined);\n\t\t\t}\n\n\t\t\tif (frameCount) {\n\t\t\t\tconst end = spec.seconds + (frameCount - 1) * MIN_FRAME_INTERVAL;\n\t\t\t\tconst label = `${formatSeconds(spec.seconds)}-${formatSeconds(end)}`;\n\t\t\t\tif (streamInfo.duration !== null && end > streamInfo.duration) {\n\t\t\t\t\tconst error = `Timestamp ${formatSeconds(end)} exceeds video duration (${formatSeconds(Math.floor(streamInfo.duration))})`;\n\t\t\t\t\treturn { url, title: `Frames ${label}`, content: error, error };\n\t\t\t\t}\n\t\t\t\tconst timestamps = computeRangeTimestamps(spec.seconds, end, frameCount);\n\t\t\t\tconst result = await yt.extractYouTubeFrames(ytInfo.videoId, timestamps, streamInfo);\n\t\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error, result.duration ?? undefined);\n\t\t\t}\n\n\t\t\tif (streamInfo.duration !== null && spec.seconds > streamInfo.duration) {\n\t\t\t\tconst error = `Timestamp ${formatSeconds(spec.seconds)} exceeds video duration (${formatSeconds(Math.floor(streamInfo.duration))})`;\n\t\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: error, error };\n\t\t\t}\n\t\t\tconst frame = await yt.extractYouTubeFrame(ytInfo.videoId, spec.seconds, streamInfo);\n\t\t\tif (\"error\" in frame) {\n\t\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: frame.error, error: frame.error };\n\t\t\t}\n\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: `Video frame at ${options.timestamp}`, error: null, thumbnail: frame };\n\t\t}\n\n\t\tconst localVideo = await safeVideoInfo(url);\n\t\tif (localVideo.error) {\n\t\t\treturn { url, title: \"\", content: \"\", error: localVideo.error };\n\t\t}\n\t\tif (localVideo.info) {\n\t\t\tif (spec.type === \"range\") {\n\t\t\t\tconst timestamps = frameCount\n\t\t\t\t\t? computeRangeTimestamps(spec.start, spec.end, frameCount)\n\t\t\t\t\t: computeRangeTimestamps(spec.start, spec.end);\n\t\t\t\tconst result = await extractLocalFrames(localVideo.info.absolutePath, timestamps);\n\t\t\t\tconst label = `${formatSeconds(spec.start)}-${formatSeconds(spec.end)}`;\n\t\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error);\n\t\t\t}\n\n\t\t\tif (frameCount) {\n\t\t\t\tconst end = spec.seconds + (frameCount - 1) * MIN_FRAME_INTERVAL;\n\t\t\t\tconst timestamps = computeRangeTimestamps(spec.seconds, end, frameCount);\n\t\t\t\tconst result = await extractLocalFrames(localVideo.info.absolutePath, timestamps);\n\t\t\t\tconst label = `${formatSeconds(spec.seconds)}-${formatSeconds(end)}`;\n\t\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error);\n\t\t\t}\n\n\t\t\tconst { extractVideoFrame } = await loadVideoExtract();\n\t\t\tconst frame = await extractVideoFrame(localVideo.info.absolutePath, spec.seconds);\n\t\t\tif (\"error\" in frame) {\n\t\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: frame.error, error: frame.error };\n\t\t\t}\n\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: `Video frame at ${options.timestamp}`, error: null, thumbnail: frame };\n\t\t}\n\n\t\treturn { url, title: \"\", content: \"\", error: \"Timestamp extraction only works with YouTube and local video files\" };\n\t}\n\n\tconst localVideo = await safeVideoInfo(url);\n\tif (localVideo.error) {\n\t\treturn { url, title: \"\", content: \"\", error: localVideo.error };\n\t}\n\tif (localVideo.info) {\n\t\ttry {\n\t\t\tconst { extractVideo } = await loadVideoExtract();\n\t\t\tconst result = await extractVideo(localVideo.info, signal, options);\n\t\t\tif (signal?.aborted) return abortedResult(url);\n\t\t\treturn result ?? { url, title: \"\", content: \"\", error: `Video analysis requires Gemini access. Either:\\n 1. Sign into gemini.google.com in Chrome (free, uses cookies)\\n 2. Set GEMINI_API_KEY in ${WEB_SEARCH_CONFIG_PATH}` };\n\t\t} catch (err) {\n\t\t\tif (isAbortError(err)) return abortedResult(url);\n\t\t\treturn { url, title: \"\", content: \"\", error: errorMessage(err) };\n\t\t}\n\t}\n\n\ttry {\n\t\tconst parsed = new URL(url);\n\t\tif (parsed.protocol === \"http:\" || parsed.protocol === \"https:\") {\n\t\t\tawait validateRemoteUrl(parsed, { allowRanges: loadSsrfAllowRanges(), lookup: options?.lookup });\n\t\t}\n\t} catch (err) {\n\t\treturn { url, title: \"\", content: \"\", error: errorMessage(err) };\n\t}\n\n\tif (looksLikeGitHubUrl(url)) {\n\t\ttry {\n\t\t\tconst { extractGitHub } = await loadGithubExtract();\n\t\t\tconst ghResult = await extractGitHub(url, signal, options?.forceClone);\n\t\t\tif (ghResult) return ghResult;\n\t\t\tif (signal?.aborted) return abortedResult(url);\n\t\t} catch (err) {\n\t\t\tconst message = errorMessage(err);\n\t\t\tif (isAbortError(err)) return abortedResult(url);\n\t\t\tif (isConfigParseError(err)) {\n\t\t\t\treturn { url, title: \"\", content: \"\", error: message };\n\t\t\t}\n\t\t}\n\t}\n\n\tconst ytInfo = isYouTubeURL(url);\n\tif (ytInfo.isYouTube) {\n\t\tlet youtubeEnabled = false;\n\t\tlet yt;\n\t\ttry {\n\t\t\tyt = await loadYoutubeExtract();\n\t\t\tyoutubeEnabled = yt.isYouTubeEnabled();\n\t\t} catch (err) {\n\t\t\treturn { url, title: \"\", content: \"\", error: errorMessage(err) };\n\t\t}\n\t\tif (youtubeEnabled) {\n\t\t\ttry {\n\t\t\t\tconst ytResult = await yt.extractYouTube(url, signal, options?.prompt, options?.model);\n\t\t\t\tif (ytResult) return ytResult;\n\t\t\t\tif (signal?.aborted) return abortedResult(url);\n\t\t\t} catch (err) {\n\t\t\t\tconst message = errorMessage(err);\n\t\t\t\tif (isAbortError(err)) return abortedResult(url);\n\t\t\t\treturn { url, title: \"\", content: \"\", error: message };\n\t\t\t}\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: \"\",\n\t\t\t\tcontent: \"\",\n\t\t\t\terror: \"Could not extract YouTube video content. Sign into Google in Chrome for automatic access, or set GEMINI_API_KEY.\",\n\t\t\t};\n\t\t}\n\t}\n\n\tif (signal?.aborted) return abortedResult(url);\n\n\tconst httpResult = await extractViaHttp(url, signal, options);\n\n\tif (signal?.aborted) return abortedResult(url);\n\tif (!httpResult.error) return httpResult;\n\tif (NON_RECOVERABLE_ERRORS.some(prefix => httpResult.error!.startsWith(prefix))) return httpResult;\n\n\tconst jinaResult = await extractWithJinaReader(url, signal, options?.lookup);\n\tif (jinaResult) return jinaResult;\n\tif (signal?.aborted) return abortedResult(url);\n\n\tlet parallelError: string | null = null;\n\ttry {\n\t\tconst parallel = await loadParallel();\n\t\tif (parallel.isParallelAvailable()) {\n\t\t\tconst parallelResult = await parallel.extractWithParallel(url, signal, options);\n\t\t\tif (parallelResult) return parallelResult;\n\t\t}\n\t} catch (err) {\n\t\tif (isAbortError(err)) return abortedResult(url);\n\t\tparallelError = errorMessage(err);\n\t\tif (isConfigParseError(err)) {\n\t\t\treturn { ...httpResult, error: parallelError };\n\t\t}\n\t}\n\tif (signal?.aborted) return abortedResult(url);\n\n\tlet geminiResult: ExtractedContent | null = null;\n\ttry {\n\t\tconst geminiUrl = await loadGeminiUrlContext();\n\t\tgeminiResult = await geminiUrl.extractWithUrlContext(url, signal)\n\t\t\t?? await geminiUrl.extractWithGeminiWeb(url, signal);\n\t} catch (err) {\n\t\tif (isAbortError(err)) return abortedResult(url);\n\t\tif (isConfigParseError(err)) {\n\t\t\treturn { ...httpResult, error: errorMessage(err) };\n\t\t}\n\t}\n\n\tif (geminiResult) return geminiResult;\n\tif (signal?.aborted) return abortedResult(url);\n\n\tconst guidance = [\n\t\thttpResult.error,\n\t\t...(parallelError ? [`Parallel fallback failed: ${parallelError}`] : []),\n\t\t\"\",\n\t\t\"Fallback options:\",\n\t\t` \\u2022 Set PARALLEL_API_KEY in ${WEB_SEARCH_CONFIG_PATH}`,\n\t\t` \\u2022 Set GEMINI_API_KEY in ${WEB_SEARCH_CONFIG_PATH}`,\n\t\t\" \\u2022 Sign into gemini.google.com in Chrome\",\n\t\t\" \\u2022 Use web_search to find content about this topic\",\n\t].join(\"\\n\");\n\treturn { ...httpResult, error: guidance };\n}\n\nfunction isLikelyJSRendered(html: string): boolean {\n\t// Extract body content\n\tconst bodyMatch = html.match(/<body[^>]*>([\\s\\S]*?)<\\/body>/i);\n\tif (!bodyMatch) return false;\n\n\tconst bodyHtml = bodyMatch[1];\n\n\t// Strip tags to get text content\n\tconst textContent = bodyHtml\n\t\t.replace(/<script[\\s\\S]*?<\\/script>/gi, \"\")\n\t\t.replace(/<style[\\s\\S]*?<\\/style>/gi, \"\")\n\t\t.replace(/<[^>]+>/g, \"\")\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim();\n\n\t// Count scripts\n\tconst scriptCount = (html.match(/<script/gi) || []).length;\n\n\t// Heuristic: little text content but many scripts suggests JS rendering\n\treturn textContent.length < 500 && scriptCount > 3;\n}\n\nasync function extractViaHttp(\n\turl: string,\n\tsignal?: AbortSignal,\n\toptions?: ExtractOptions,\n): Promise<ExtractedContent> {\n\tconst timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\tconst activityId = activityMonitor.logStart({ type: \"fetch\", url });\n\n\tconst controller = new AbortController();\n\tconst timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n\tconst onAbort = () => controller.abort();\n\tsignal?.addEventListener(\"abort\", onAbort);\n\n\ttry {\n\t\tconst response = await fetchRemoteUrl(\n\t\t\turl,\n\t\t\t{\n\t\t\t\tsignal: controller.signal,\n\t\t\t\theaders: {\n\t\t\t\t\t\"User-Agent\": \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36\",\n\t\t\t\t\t\"Accept\": \"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8\",\n\t\t\t\t\t\"Accept-Language\": \"en-US,en;q=0.9\",\n\t\t\t\t\t\"Cache-Control\": \"no-cache\",\n\t\t\t\t\t\"Sec-Fetch-Dest\": \"document\",\n\t\t\t\t\t\"Sec-Fetch-Mode\": \"navigate\",\n\t\t\t\t\t\"Sec-Fetch-Site\": \"none\",\n\t\t\t\t\t\"Sec-Fetch-User\": \"?1\",\n\t\t\t\t\t\"Upgrade-Insecure-Requests\": \"1\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ allowRanges: loadSsrfAllowRanges(), lookup: options?.lookup },\n\t\t);\n\n\t\tif (!response.ok) {\n\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: \"\",\n\t\t\t\tcontent: \"\",\n\t\t\t\terror: `HTTP ${response.status}: ${response.statusText}`,\n\t\t\t};\n\t\t}\n\n\t\tconst contentLengthHeader = response.headers.get(\"content-length\");\n\t\tconst contentType = response.headers.get(\"content-type\") || \"\";\n\t\tconst isPDFContent = isPDF(url, contentType);\n\t\tconst maxResponseSize = isPDFContent ? 20 * 1024 * 1024 : 5 * 1024 * 1024;\n\t\tif (contentLengthHeader) {\n\t\t\tconst contentLength = parseInt(contentLengthHeader, 10);\n\t\t\tif (contentLength > maxResponseSize) {\n\t\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\t\treturn {\n\t\t\t\t\turl,\n\t\t\t\t\ttitle: \"\",\n\t\t\t\t\tcontent: \"\",\n\t\t\t\t\terror: `Response too large (${Math.round(contentLength / 1024 / 1024)}MB)`,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tif (isPDFContent) {\n\t\t\ttry {\n\t\t\t\tconst buffer = await response.arrayBuffer();\n\t\t\t\tconst { extractPDFToMarkdown } = await loadPdfExtract();\n\t\t\t\tconst result = await extractPDFToMarkdown(buffer, url);\n\t\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\t\treturn {\n\t\t\t\t\turl,\n\t\t\t\t\ttitle: result.title,\n\t\t\t\t\tcontent: `PDF extracted and saved to: ${result.outputPath}\\n\\nPages: ${result.pages}\\nCharacters: ${result.chars}`,\n\t\t\t\t\terror: null,\n\t\t\t\t};\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\tactivityMonitor.logError(activityId, message);\n\t\t\t\treturn { url, title: \"\", content: \"\", error: `PDF extraction failed: ${message}` };\n\t\t\t}\n\t\t}\n\n\t\tif (contentType.includes(\"application/octet-stream\") ||\n\t\t\tcontentType.includes(\"image/\") ||\n\t\t\tcontentType.includes(\"audio/\") ||\n\t\t\tcontentType.includes(\"video/\") ||\n\t\t\tcontentType.includes(\"application/zip\")) {\n\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: \"\",\n\t\t\t\tcontent: \"\",\n\t\t\t\terror: `Unsupported content type: ${contentType.split(\";\")[0]}`,\n\t\t\t};\n\t\t}\n\n\t\tconst text = await response.text();\n\t\tconst isHTML = contentType.includes(\"text/html\") || contentType.includes(\"application/xhtml+xml\");\n\n\t\tif (!isHTML) {\n\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\tconst title = extractTextTitle(text, url);\n\t\t\treturn { url, title, content: text, error: null };\n\t\t}\n\n\t\tconst { document } = parseHTML(text);\n\t\tconst reader = new Readability(document as unknown as Document);\n\t\tconst article = reader.parse();\n\n\t\tif (!article) {\n\t\t\tconst rscResult = extractRSCContent(text);\n\t\t\tif (rscResult) {\n\t\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\t\treturn { url, title: rscResult.title, content: rscResult.content, error: null };\n\t\t\t}\n\n\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\n\t\t\t// Provide more specific error message\n\t\t\tconst jsRendered = isLikelyJSRendered(text);\n\t\t\tconst errorMsg = jsRendered\n\t\t\t\t? \"Page appears to be JavaScript-rendered (content loads dynamically)\"\n\t\t\t\t: \"Could not extract readable content from HTML structure\";\n\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: \"\",\n\t\t\t\tcontent: \"\",\n\t\t\t\terror: errorMsg,\n\t\t\t};\n\t\t}\n\n\t\tconst markdown = turndown.turndown(article.content);\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\n\t\tif (markdown.length < MIN_USEFUL_CONTENT) {\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: article.title || \"\",\n\t\t\t\tcontent: markdown,\n\t\t\t\terror: isLikelyJSRendered(text)\n\t\t\t\t\t? \"Page appears to be JavaScript-rendered (content loads dynamically)\"\n\t\t\t\t\t: \"Extracted content appears incomplete\",\n\t\t\t};\n\t\t}\n\n\t\treturn { url, title: article.title || \"\", content: markdown, error: null };\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tif (message.toLowerCase().includes(\"abort\")) {\n\t\t\tactivityMonitor.logComplete(activityId, 0);\n\t\t} else {\n\t\t\tactivityMonitor.logError(activityId, message);\n\t\t}\n\t\treturn { url, title: \"\", content: \"\", error: message };\n\t} finally {\n\t\tclearTimeout(timeoutId);\n\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t}\n}\n\nexport function extractHeadingTitle(text: string): string | null {\n\tconst match = text.match(/^#{1,2}\\s+(.+)/m);\n\tif (!match) return null;\n\tconst cleaned = match[1].replace(/\\*+/g, \"\").trim();\n\treturn cleaned || null;\n}\n\nfunction extractTextTitle(text: string, url: string): string {\n\treturn extractHeadingTitle(text) ?? (new URL(url).pathname.split(\"/\").pop() || url);\n}\n\nexport async function fetchAllContent(\n\turls: string[],\n\tsignal?: AbortSignal,\n\toptions?: ExtractOptions,\n): Promise<ExtractedContent[]> {\n\treturn Promise.all(urls.map((url) => fetchLimit(() => extractContent(url, signal, options))));\n}\n"]}
|
|
1
|
+
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../../src/builtin-extensions/pi-web-access/extract.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAqC,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAgDtF;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAuB9C;AAyBD,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAC3D,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAExD,MAAM,WAAW,gBAAgB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAgJD,wBAAsB,cAAc,CACnC,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE,cAAc,GACtB,OAAO,CAAC,gBAAgB,CAAC,CA0Q3B;AA0LD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK/D;AAMD,wBAAsB,eAAe,CACpC,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,CAAC,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE,cAAc,GACtB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAE7B","sourcesContent":["// @ts-nocheck\nimport { Readability } from \"@mozilla/readability\";\nimport { parseHTML } from \"linkedom\";\nimport TurndownService from \"turndown\";\nimport pLimit from \"p-limit\";\nimport { readBoundedBody } from \"./bounded-body.ts\";\nimport { activityMonitor } from \"./activity.ts\";\nimport { extractRSCContent } from \"./rsc-extract.ts\";\nimport {\n\tloadGeminiUrlContext,\n\tloadGithubExtract,\n\tloadParallel,\n\tloadPdfExtract,\n\tloadVideoExtract,\n\tloadYoutubeExtract,\n} from \"./lazy.ts\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { fetchRemoteUrl, validateRemoteUrl, type Lookup } from \"./ssrf-protection.ts\";\nimport { formatSeconds, getWebSearchConfigPath } from \"./utils.ts\";\n\nconst YOUTUBE_URL_RE =\n\t/(?:(?:www\\.|m\\.)?youtube\\.com\\/(?:watch\\?.*v=|shorts\\/|live\\/|embed\\/|v\\/)|youtu\\.be\\/)([a-zA-Z0-9_-]{11})/;\n\nfunction isYouTubeURL(url: string): { isYouTube: boolean; videoId: string | null } {\n\ttry {\n\t\tconst parsed = new URL(url);\n\t\tif (parsed.pathname === \"/playlist\") {\n\t\t\treturn { isYouTube: false, videoId: null };\n\t\t}\n\t} catch {\n\t}\n\tconst match = url.match(YOUTUBE_URL_RE);\n\tif (!match) return { isYouTube: false, videoId: null };\n\treturn { isYouTube: true, videoId: match[1] };\n}\n\nfunction isPDF(url: string, contentType?: string): boolean {\n\tif (contentType?.includes(\"application/pdf\")) return true;\n\ttry {\n\t\treturn new URL(url).pathname.toLowerCase().endsWith(\".pdf\");\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction looksLikeLocalPath(input: string): boolean {\n\treturn input.startsWith(\"/\") || input.startsWith(\"./\") || input.startsWith(\"../\") || input.startsWith(\"file://\");\n}\n\nfunction looksLikeGitHubUrl(url: string): boolean {\n\ttry {\n\t\tconst host = new URL(url).hostname.toLowerCase();\n\t\treturn host === \"github.com\" || host === \"www.github.com\" || host.endsWith(\".github.com\");\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nconst DEFAULT_TIMEOUT_MS = 30000;\nconst CONCURRENT_LIMIT = 3;\n\nconst NON_RECOVERABLE_ERRORS = [\"Unsupported content type\", \"Response too large\"];\nconst MIN_USEFUL_CONTENT = 500;\nconst WEB_SEARCH_CONFIG_PATH = getWebSearchConfigPath();\n\n/**\n * Read `ssrf.allowRanges` (CIDR strings) from web-search.json. Returns [] when\n * the file is missing, unreadable, or the key is unset so SSRF protection stays\n * fully on by default. Throws when `ssrf.allowRanges` is present but not an array\n * so a mistyped value (e.g. a bare string instead of a JSON array) fails loudly\n * instead of being silently ignored. Exempts synthetic ranges used by TUN/fake-IP\n * proxies (e.g. 198.18.0.0/15).\n */\nexport function loadSsrfAllowRanges(): string[] {\n\tlet value: unknown;\n\ttry {\n\t\tif (!existsSync(WEB_SEARCH_CONFIG_PATH)) return [];\n\t\tconst raw = readFileSync(WEB_SEARCH_CONFIG_PATH, \"utf-8\");\n\t\tvalue = (JSON.parse(raw) as { ssrf?: { allowRanges?: unknown } })?.ssrf?.allowRanges;\n\t} catch {\n\t\t// Missing/unreadable file or invalid JSON: fail safe with SSRF fully on.\n\t\treturn [];\n\t}\n\tif (value === undefined || value === null) return [];\n\tif (!Array.isArray(value)) {\n\t\tthrow new Error(`ssrf.allowRanges in ${WEB_SEARCH_CONFIG_PATH} must be an array of CIDR strings`);\n\t}\n\tconst ranges: string[] = [];\n\tfor (const [index, entry] of value.entries()) {\n\t\tif (typeof entry !== \"string\") {\n\t\t\tthrow new Error(`ssrf.allowRanges in ${WEB_SEARCH_CONFIG_PATH} must contain only CIDR strings; entry ${index + 1} is ${typeof entry}`);\n\t\t}\n\t\tconst trimmed = entry.trim();\n\t\tif (trimmed) ranges.push(trimmed);\n\t}\n\treturn ranges;\n}\n\nfunction errorMessage(err: unknown): string {\n\treturn err instanceof Error ? err.message : String(err);\n}\n\nfunction isConfigParseError(err: unknown): boolean {\n\treturn errorMessage(err).startsWith(\"Failed to parse \");\n}\n\nfunction isAbortError(err: unknown): boolean {\n\treturn errorMessage(err).toLowerCase().includes(\"abort\");\n}\n\nfunction abortedResult(url: string): ExtractedContent {\n\treturn { url, title: \"\", content: \"\", error: \"Aborted\" };\n}\n\nconst turndown = new TurndownService({\n\theadingStyle: \"atx\",\n\tcodeBlockStyle: \"fenced\",\n});\n\nconst fetchLimit = pLimit(CONCURRENT_LIMIT);\n\nexport interface VideoFrame {\n\tdata: string;\n\tmimeType: string;\n\ttimestamp: string;\n}\n\nexport type FrameData = { data: string; mimeType: string };\nexport type FrameResult = FrameData | { error: string };\n\nexport interface ExtractedContent {\n\turl: string;\n\ttitle: string;\n\tcontent: string;\n\terror: string | null;\n\tthumbnail?: { data: string; mimeType: string };\n\tframes?: VideoFrame[];\n\tduration?: number;\n}\n\nexport interface ExtractOptions {\n\ttimeoutMs?: number;\n\tforceClone?: boolean;\n\tprompt?: string;\n\ttimestamp?: string;\n\tframes?: number;\n\tmodel?: string;\n\t/** Custom DNS resolver used for SSRF validation. Primarily a test seam. */\n\tlookup?: Lookup;\n}\n\nconst JINA_READER_BASE = \"https://r.jina.ai/\";\nconst JINA_TIMEOUT_MS = 30000;\n\nasync function extractWithJinaReader(\n\turl: string,\n\tsignal?: AbortSignal,\n\tlookup?: Lookup,\n): Promise<ExtractedContent | null> {\n\tconst jinaUrl = JINA_READER_BASE + url;\n\n\tconst activityId = activityMonitor.logStart({ type: \"api\", query: `jina: ${url}` });\n\n\ttry {\n\t\tawait validateRemoteUrl(url, { allowRanges: loadSsrfAllowRanges(), lookup });\n\t\tconst res = await fetch(jinaUrl, {\n\t\t\theaders: {\n\t\t\t\t\"Accept\": \"text/markdown\",\n\t\t\t\t\"X-No-Cache\": \"true\",\n\t\t\t},\n\t\t\tsignal: AbortSignal.any([\n\t\t\t\tAbortSignal.timeout(JINA_TIMEOUT_MS),\n\t\t\t\t...(signal ? [signal] : []),\n\t\t\t]),\n\t\t});\n\n\t\tif (!res.ok) {\n\t\t\tactivityMonitor.logComplete(activityId, res.status);\n\t\t\treturn null;\n\t\t}\n\n\t\tconst content = await res.text();\n\t\tactivityMonitor.logComplete(activityId, res.status);\n\n\t\tconst contentStart = content.indexOf(\"Markdown Content:\");\n\t\tif (contentStart < 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst markdownPart = content.slice(contentStart + 17).trim(); // 17 = \"Markdown Content:\".length\n\n\t\t// Check for failed JS rendering or minimal content\n\t\tif (markdownPart.length < 100 ||\n\t\t\tmarkdownPart.startsWith(\"Loading...\") ||\n\t\t\tmarkdownPart.startsWith(\"Please enable JavaScript\")) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst title = extractHeadingTitle(markdownPart) ?? (new URL(url).pathname.split(\"/\").pop() || url);\n\t\treturn { url, title, content: markdownPart, error: null };\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tif (message.toLowerCase().includes(\"abort\")) {\n\t\t\tactivityMonitor.logComplete(activityId, 0);\n\t\t} else {\n\t\t\tactivityMonitor.logError(activityId, message);\n\t\t}\n\t\treturn null;\n\t}\n}\n\nfunction parseTimestamp(ts: string): number | null {\n\tconst num = Number(ts);\n\tif (!isNaN(num) && num >= 0) return Math.floor(num);\n\tconst parts = ts.split(\":\").map(Number);\n\tif (parts.some(p => isNaN(p) || p < 0)) return null;\n\tif (parts.length === 3) return Math.floor(parts[0] * 3600 + parts[1] * 60 + parts[2]);\n\tif (parts.length === 2) return Math.floor(parts[0] * 60 + parts[1]);\n\treturn null;\n}\n\ntype TimestampSpec = { type: \"single\"; seconds: number } | { type: \"range\"; start: number; end: number };\n\nfunction parseTimestampSpec(ts: string): TimestampSpec | null {\n\tconst dashIdx = ts.indexOf(\"-\", 1);\n\tif (dashIdx > 0) {\n\t\tconst start = parseTimestamp(ts.slice(0, dashIdx));\n\t\tconst end = parseTimestamp(ts.slice(dashIdx + 1));\n\t\tif (start !== null && end !== null && end > start) return { type: \"range\", start, end };\n\t}\n\tconst seconds = parseTimestamp(ts);\n\treturn seconds !== null ? { type: \"single\", seconds } : null;\n}\n\nconst DEFAULT_RANGE_FRAMES = 6;\nconst MIN_FRAME_INTERVAL = 5;\n\nfunction computeRangeTimestamps(start: number, end: number, maxFrames: number = DEFAULT_RANGE_FRAMES): number[] {\n\tif (maxFrames <= 1) return [start];\n\tconst duration = end - start;\n\tconst idealInterval = duration / (maxFrames - 1);\n\tif (idealInterval < MIN_FRAME_INTERVAL) {\n\t\tconst timestamps: number[] = [];\n\t\tfor (let t = start; t <= end && timestamps.length < maxFrames; t += MIN_FRAME_INTERVAL) {\n\t\t\ttimestamps.push(t);\n\t\t}\n\t\treturn timestamps;\n\t}\n\treturn Array.from({ length: maxFrames }, (_, i) => Math.round(start + i * idealInterval));\n}\n\nfunction buildFrameResult(\n\turl: string, label: string, requestedCount: number,\n\tframes: VideoFrame[], error: string | null, duration?: number,\n): ExtractedContent {\n\tif (frames.length === 0) {\n\t\tconst msg = error ?? \"Frame extraction failed\";\n\t\treturn { url, title: `Frames ${label} (0/${requestedCount})`, content: msg, error: msg };\n\t}\n\treturn {\n\t\turl,\n\t\ttitle: `Frames ${label} (${frames.length}/${requestedCount})`,\n\t\tcontent: `${frames.length} frames extracted from ${label}`,\n\t\terror: null,\n\t\tframes,\n\t\tduration,\n\t};\n}\n\nasync function extractLocalFrames(\n\tfilePath: string, timestamps: number[],\n): Promise<{ frames: VideoFrame[]; error: string | null }> {\n\tconst { extractVideoFrame } = await loadVideoExtract();\n\tconst results = await Promise.all(timestamps.map(async (t) => {\n\t\tconst frame = await extractVideoFrame(filePath, t);\n\t\tif (\"error\" in frame) return { error: frame.error };\n\t\treturn { ...frame, timestamp: formatSeconds(t) };\n\t}));\n\tconst frames = results.filter((f): f is VideoFrame => \"data\" in f);\n\tconst firstError = results.find((f): f is { error: string } => \"error\" in f);\n\treturn { frames, error: frames.length === 0 && firstError ? firstError.error : null };\n}\n\nasync function safeVideoInfo(url: string): Promise<{ info: Awaited<ReturnType<typeof loadVideoExtract>> extends { isVideoFile: infer F } ? (F extends (...args: any) => infer R ? R : null) : null; error?: string }> {\n\tif (!looksLikeLocalPath(url)) return { info: null };\n\ttry {\n\t\tconst { isVideoFile } = await loadVideoExtract();\n\t\treturn { info: isVideoFile(url) };\n\t} catch (err) {\n\t\treturn { info: null, error: errorMessage(err) };\n\t}\n}\n\nexport async function extractContent(\n\turl: string,\n\tsignal?: AbortSignal,\n\toptions?: ExtractOptions,\n): Promise<ExtractedContent> {\n\tif (signal?.aborted) {\n\t\treturn { url, title: \"\", content: \"\", error: \"Aborted\" };\n\t}\n\n\tif (options?.frames && !options.timestamp) {\n\t\tconst frameCount = options.frames;\n\t\tconst ytInfo = isYouTubeURL(url);\n\t\tif (ytInfo.isYouTube && ytInfo.videoId) {\n\t\t\tconst yt = await loadYoutubeExtract();\n\t\t\tconst streamInfo = await yt.getYouTubeStreamInfo(ytInfo.videoId);\n\t\t\tif (\"error\" in streamInfo) {\n\t\t\t\treturn { url, title: \"Frames\", content: streamInfo.error, error: streamInfo.error };\n\t\t\t}\n\t\t\tif (streamInfo.duration === null) {\n\t\t\t\tconst error = \"Cannot determine video duration. Use a timestamp range instead.\";\n\t\t\t\treturn { url, title: \"Frames\", content: error, error };\n\t\t\t}\n\t\t\tconst dur = Math.floor(streamInfo.duration);\n\t\t\tconst timestamps = computeRangeTimestamps(0, dur, frameCount);\n\t\t\tconst result = await yt.extractYouTubeFrames(ytInfo.videoId, timestamps, streamInfo);\n\t\t\tconst label = `${formatSeconds(0)}-${formatSeconds(dur)}`;\n\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error, streamInfo.duration);\n\t\t}\n\n\t\tconst localVideo = await safeVideoInfo(url);\n\t\tif (localVideo.error) {\n\t\t\treturn { url, title: \"\", content: \"\", error: localVideo.error };\n\t\t}\n\t\tif (localVideo.info) {\n\t\t\tconst { getLocalVideoDuration } = await loadVideoExtract();\n\t\t\tconst durationResult = await getLocalVideoDuration(localVideo.info.absolutePath);\n\t\t\tif (typeof durationResult !== \"number\") {\n\t\t\t\treturn { url, title: \"Frames\", content: durationResult.error, error: durationResult.error };\n\t\t\t}\n\t\t\tconst dur = Math.floor(durationResult);\n\t\t\tconst timestamps = computeRangeTimestamps(0, dur, frameCount);\n\t\t\tconst result = await extractLocalFrames(localVideo.info.absolutePath, timestamps);\n\t\t\tconst label = `${formatSeconds(0)}-${formatSeconds(dur)}`;\n\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error, durationResult);\n\t\t}\n\n\t\treturn { url, title: \"\", content: \"\", error: \"Frame extraction only works with YouTube and local video files\" };\n\t}\n\n\tif (options?.timestamp) {\n\t\tconst spec = parseTimestampSpec(options.timestamp);\n\t\tif (!spec) {\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: \"\",\n\t\t\t\tcontent: \"\",\n\t\t\t\terror: `Invalid timestamp format: \"${options.timestamp}\". Use \"H:MM:SS\", \"MM:SS\", \"85\", or \"start-end\".`,\n\t\t\t};\n\t\t}\n\n\t\tconst frameCount = options.frames;\n\t\tconst ytInfo = isYouTubeURL(url);\n\t\tif (ytInfo.isYouTube && ytInfo.videoId) {\n\t\t\tconst yt = await loadYoutubeExtract();\n\t\t\tconst streamInfo = await yt.getYouTubeStreamInfo(ytInfo.videoId);\n\t\t\tif (\"error\" in streamInfo) {\n\t\t\t\tif (spec.type === \"range\") {\n\t\t\t\t\tconst label = `${formatSeconds(spec.start)}-${formatSeconds(spec.end)}`;\n\t\t\t\t\treturn { url, title: `Frames ${label}`, content: streamInfo.error, error: streamInfo.error };\n\t\t\t\t}\n\t\t\t\tif (frameCount) {\n\t\t\t\t\tconst end = spec.seconds + (frameCount - 1) * MIN_FRAME_INTERVAL;\n\t\t\t\t\tconst label = `${formatSeconds(spec.seconds)}-${formatSeconds(end)}`;\n\t\t\t\t\treturn { url, title: `Frames ${label}`, content: streamInfo.error, error: streamInfo.error };\n\t\t\t\t}\n\t\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: streamInfo.error, error: streamInfo.error };\n\t\t\t}\n\n\t\t\tif (spec.type === \"range\") {\n\t\t\t\tconst label = `${formatSeconds(spec.start)}-${formatSeconds(spec.end)}`;\n\t\t\t\tif (streamInfo.duration !== null && spec.end > streamInfo.duration) {\n\t\t\t\t\tconst error = `Timestamp ${formatSeconds(spec.end)} exceeds video duration (${formatSeconds(Math.floor(streamInfo.duration))})`;\n\t\t\t\t\treturn { url, title: `Frames ${label}`, content: error, error };\n\t\t\t\t}\n\t\t\t\tconst timestamps = frameCount\n\t\t\t\t\t? computeRangeTimestamps(spec.start, spec.end, frameCount)\n\t\t\t\t\t: computeRangeTimestamps(spec.start, spec.end);\n\t\t\t\tconst result = await yt.extractYouTubeFrames(ytInfo.videoId, timestamps, streamInfo);\n\t\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error, result.duration ?? undefined);\n\t\t\t}\n\n\t\t\tif (frameCount) {\n\t\t\t\tconst end = spec.seconds + (frameCount - 1) * MIN_FRAME_INTERVAL;\n\t\t\t\tconst label = `${formatSeconds(spec.seconds)}-${formatSeconds(end)}`;\n\t\t\t\tif (streamInfo.duration !== null && end > streamInfo.duration) {\n\t\t\t\t\tconst error = `Timestamp ${formatSeconds(end)} exceeds video duration (${formatSeconds(Math.floor(streamInfo.duration))})`;\n\t\t\t\t\treturn { url, title: `Frames ${label}`, content: error, error };\n\t\t\t\t}\n\t\t\t\tconst timestamps = computeRangeTimestamps(spec.seconds, end, frameCount);\n\t\t\t\tconst result = await yt.extractYouTubeFrames(ytInfo.videoId, timestamps, streamInfo);\n\t\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error, result.duration ?? undefined);\n\t\t\t}\n\n\t\t\tif (streamInfo.duration !== null && spec.seconds > streamInfo.duration) {\n\t\t\t\tconst error = `Timestamp ${formatSeconds(spec.seconds)} exceeds video duration (${formatSeconds(Math.floor(streamInfo.duration))})`;\n\t\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: error, error };\n\t\t\t}\n\t\t\tconst frame = await yt.extractYouTubeFrame(ytInfo.videoId, spec.seconds, streamInfo);\n\t\t\tif (\"error\" in frame) {\n\t\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: frame.error, error: frame.error };\n\t\t\t}\n\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: `Video frame at ${options.timestamp}`, error: null, thumbnail: frame };\n\t\t}\n\n\t\tconst localVideo = await safeVideoInfo(url);\n\t\tif (localVideo.error) {\n\t\t\treturn { url, title: \"\", content: \"\", error: localVideo.error };\n\t\t}\n\t\tif (localVideo.info) {\n\t\t\tif (spec.type === \"range\") {\n\t\t\t\tconst timestamps = frameCount\n\t\t\t\t\t? computeRangeTimestamps(spec.start, spec.end, frameCount)\n\t\t\t\t\t: computeRangeTimestamps(spec.start, spec.end);\n\t\t\t\tconst result = await extractLocalFrames(localVideo.info.absolutePath, timestamps);\n\t\t\t\tconst label = `${formatSeconds(spec.start)}-${formatSeconds(spec.end)}`;\n\t\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error);\n\t\t\t}\n\n\t\t\tif (frameCount) {\n\t\t\t\tconst end = spec.seconds + (frameCount - 1) * MIN_FRAME_INTERVAL;\n\t\t\t\tconst timestamps = computeRangeTimestamps(spec.seconds, end, frameCount);\n\t\t\t\tconst result = await extractLocalFrames(localVideo.info.absolutePath, timestamps);\n\t\t\t\tconst label = `${formatSeconds(spec.seconds)}-${formatSeconds(end)}`;\n\t\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error);\n\t\t\t}\n\n\t\t\tconst { extractVideoFrame } = await loadVideoExtract();\n\t\t\tconst frame = await extractVideoFrame(localVideo.info.absolutePath, spec.seconds);\n\t\t\tif (\"error\" in frame) {\n\t\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: frame.error, error: frame.error };\n\t\t\t}\n\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: `Video frame at ${options.timestamp}`, error: null, thumbnail: frame };\n\t\t}\n\n\t\treturn { url, title: \"\", content: \"\", error: \"Timestamp extraction only works with YouTube and local video files\" };\n\t}\n\n\tconst localVideo = await safeVideoInfo(url);\n\tif (localVideo.error) {\n\t\treturn { url, title: \"\", content: \"\", error: localVideo.error };\n\t}\n\tif (localVideo.info) {\n\t\ttry {\n\t\t\tconst { extractVideo } = await loadVideoExtract();\n\t\t\tconst result = await extractVideo(localVideo.info, signal, options);\n\t\t\tif (signal?.aborted) return abortedResult(url);\n\t\t\treturn result ?? { url, title: \"\", content: \"\", error: `Video analysis requires Gemini access. Either:\\n 1. Sign into gemini.google.com in Chrome (free, uses cookies)\\n 2. Set GEMINI_API_KEY in ${WEB_SEARCH_CONFIG_PATH}` };\n\t\t} catch (err) {\n\t\t\tif (isAbortError(err)) return abortedResult(url);\n\t\t\treturn { url, title: \"\", content: \"\", error: errorMessage(err) };\n\t\t}\n\t}\n\n\ttry {\n\t\tconst parsed = new URL(url);\n\t\tif (parsed.protocol === \"http:\" || parsed.protocol === \"https:\") {\n\t\t\tawait validateRemoteUrl(parsed, { allowRanges: loadSsrfAllowRanges(), lookup: options?.lookup });\n\t\t}\n\t} catch (err) {\n\t\treturn { url, title: \"\", content: \"\", error: errorMessage(err) };\n\t}\n\n\tif (looksLikeGitHubUrl(url)) {\n\t\ttry {\n\t\t\tconst { extractGitHub } = await loadGithubExtract();\n\t\t\tconst ghResult = await extractGitHub(url, signal, options?.forceClone);\n\t\t\tif (ghResult) return ghResult;\n\t\t\tif (signal?.aborted) return abortedResult(url);\n\t\t} catch (err) {\n\t\t\tconst message = errorMessage(err);\n\t\t\tif (isAbortError(err)) return abortedResult(url);\n\t\t\tif (isConfigParseError(err)) {\n\t\t\t\treturn { url, title: \"\", content: \"\", error: message };\n\t\t\t}\n\t\t}\n\t}\n\n\tconst ytInfo = isYouTubeURL(url);\n\tif (ytInfo.isYouTube) {\n\t\tlet youtubeEnabled = false;\n\t\tlet yt;\n\t\ttry {\n\t\t\tyt = await loadYoutubeExtract();\n\t\t\tyoutubeEnabled = yt.isYouTubeEnabled();\n\t\t} catch (err) {\n\t\t\treturn { url, title: \"\", content: \"\", error: errorMessage(err) };\n\t\t}\n\t\tif (youtubeEnabled) {\n\t\t\ttry {\n\t\t\t\tconst ytResult = await yt.extractYouTube(url, signal, options?.prompt, options?.model);\n\t\t\t\tif (ytResult) return ytResult;\n\t\t\t\tif (signal?.aborted) return abortedResult(url);\n\t\t\t} catch (err) {\n\t\t\t\tconst message = errorMessage(err);\n\t\t\t\tif (isAbortError(err)) return abortedResult(url);\n\t\t\t\treturn { url, title: \"\", content: \"\", error: message };\n\t\t\t}\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: \"\",\n\t\t\t\tcontent: \"\",\n\t\t\t\terror: \"Could not extract YouTube video content. Sign into Google in Chrome for automatic access, or set GEMINI_API_KEY.\",\n\t\t\t};\n\t\t}\n\t}\n\n\tif (signal?.aborted) return abortedResult(url);\n\n\tconst httpResult = await extractViaHttp(url, signal, options);\n\n\tif (signal?.aborted) return abortedResult(url);\n\tif (!httpResult.error) return httpResult;\n\tif (NON_RECOVERABLE_ERRORS.some(prefix => httpResult.error!.startsWith(prefix))) return httpResult;\n\n\tconst jinaResult = await extractWithJinaReader(url, signal, options?.lookup);\n\tif (jinaResult) return jinaResult;\n\tif (signal?.aborted) return abortedResult(url);\n\n\tlet parallelError: string | null = null;\n\ttry {\n\t\tconst parallel = await loadParallel();\n\t\tif (parallel.isParallelAvailable()) {\n\t\t\tconst parallelResult = await parallel.extractWithParallel(url, signal, options);\n\t\t\tif (parallelResult) return parallelResult;\n\t\t}\n\t} catch (err) {\n\t\tif (isAbortError(err)) return abortedResult(url);\n\t\tparallelError = errorMessage(err);\n\t\tif (isConfigParseError(err)) {\n\t\t\treturn { ...httpResult, error: parallelError };\n\t\t}\n\t}\n\tif (signal?.aborted) return abortedResult(url);\n\n\tlet geminiResult: ExtractedContent | null = null;\n\ttry {\n\t\tconst geminiUrl = await loadGeminiUrlContext();\n\t\tgeminiResult = await geminiUrl.extractWithUrlContext(url, signal)\n\t\t\t?? await geminiUrl.extractWithGeminiWeb(url, signal);\n\t} catch (err) {\n\t\tif (isAbortError(err)) return abortedResult(url);\n\t\tif (isConfigParseError(err)) {\n\t\t\treturn { ...httpResult, error: errorMessage(err) };\n\t\t}\n\t}\n\n\tif (geminiResult) return geminiResult;\n\tif (signal?.aborted) return abortedResult(url);\n\n\tconst guidance = [\n\t\thttpResult.error,\n\t\t...(parallelError ? [`Parallel fallback failed: ${parallelError}`] : []),\n\t\t\"\",\n\t\t\"Fallback options:\",\n\t\t` \\u2022 Set PARALLEL_API_KEY in ${WEB_SEARCH_CONFIG_PATH}`,\n\t\t` \\u2022 Set GEMINI_API_KEY in ${WEB_SEARCH_CONFIG_PATH}`,\n\t\t\" \\u2022 Sign into gemini.google.com in Chrome\",\n\t\t\" \\u2022 Use web_search to find content about this topic\",\n\t].join(\"\\n\");\n\treturn { ...httpResult, error: guidance };\n}\n\nfunction isLikelyJSRendered(html: string): boolean {\n\t// Extract body content\n\tconst bodyMatch = html.match(/<body[^>]*>([\\s\\S]*?)<\\/body>/i);\n\tif (!bodyMatch) return false;\n\n\tconst bodyHtml = bodyMatch[1];\n\n\t// Strip tags to get text content\n\tconst textContent = bodyHtml\n\t\t.replace(/<script[\\s\\S]*?<\\/script>/gi, \"\")\n\t\t.replace(/<style[\\s\\S]*?<\\/style>/gi, \"\")\n\t\t.replace(/<[^>]+>/g, \"\")\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim();\n\n\t// Count scripts\n\tconst scriptCount = (html.match(/<script/gi) || []).length;\n\n\t// Heuristic: little text content but many scripts suggests JS rendering\n\treturn textContent.length < 500 && scriptCount > 3;\n}\n\nasync function extractViaHttp(\n\turl: string,\n\tsignal?: AbortSignal,\n\toptions?: ExtractOptions,\n): Promise<ExtractedContent> {\n\tconst timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\tconst activityId = activityMonitor.logStart({ type: \"fetch\", url });\n\n\tconst controller = new AbortController();\n\tconst timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n\tconst onAbort = () => controller.abort();\n\tif (signal?.aborted) controller.abort();\n\tsignal?.addEventListener(\"abort\", onAbort);\n\n\ttry {\n\t\tconst response = await fetchRemoteUrl(\n\t\t\turl,\n\t\t\t{\n\t\t\t\tsignal: controller.signal,\n\t\t\t\theaders: {\n\t\t\t\t\t\"User-Agent\": \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36\",\n\t\t\t\t\t\"Accept\": \"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8\",\n\t\t\t\t\t\"Accept-Language\": \"en-US,en;q=0.9\",\n\t\t\t\t\t\"Cache-Control\": \"no-cache\",\n\t\t\t\t\t\"Sec-Fetch-Dest\": \"document\",\n\t\t\t\t\t\"Sec-Fetch-Mode\": \"navigate\",\n\t\t\t\t\t\"Sec-Fetch-Site\": \"none\",\n\t\t\t\t\t\"Sec-Fetch-User\": \"?1\",\n\t\t\t\t\t\"Upgrade-Insecure-Requests\": \"1\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ allowRanges: loadSsrfAllowRanges(), lookup: options?.lookup },\n\t\t);\n\n\t\tif (!response.ok) {\n\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: \"\",\n\t\t\t\tcontent: \"\",\n\t\t\t\terror: `HTTP ${response.status}: ${response.statusText}`,\n\t\t\t};\n\t\t}\n\n\t\tconst contentLengthHeader = response.headers.get(\"content-length\");\n\t\tconst contentType = response.headers.get(\"content-type\") || \"\";\n\t\tconst isPDFContent = isPDF(url, contentType);\n\t\tconst maxResponseSize = isPDFContent ? 20 * 1024 * 1024 : 5 * 1024 * 1024;\n\t\tif (contentLengthHeader) {\n\t\t\tconst contentLength = parseInt(contentLengthHeader, 10);\n\t\t\tif (contentLength > maxResponseSize) {\n\t\t\t\tawait response.body?.cancel();\n\t\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\t\treturn {\n\t\t\t\t\turl,\n\t\t\t\t\ttitle: \"\",\n\t\t\t\t\tcontent: \"\",\n\t\t\t\t\terror: `Response too large (${Math.round(contentLength / 1024 / 1024)}MB)`,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tif (isPDFContent) {\n\t\t\tconst bytes = await readBoundedBody(response, maxResponseSize, controller.signal);\n\t\t\ttry {\n\t\t\t\tconst buffer = bytes.buffer;\n\t\t\t\tconst { extractPDFToMarkdown } = await loadPdfExtract();\n\t\t\t\tconst result = await extractPDFToMarkdown(buffer, url);\n\t\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\t\treturn {\n\t\t\t\t\turl,\n\t\t\t\t\ttitle: result.title,\n\t\t\t\t\tcontent: `PDF extracted and saved to: ${result.outputPath}\\n\\nPages: ${result.pages}\\nCharacters: ${result.chars}`,\n\t\t\t\t\terror: null,\n\t\t\t\t};\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\tactivityMonitor.logError(activityId, message);\n\t\t\t\treturn { url, title: \"\", content: \"\", error: `PDF extraction failed: ${message}` };\n\t\t\t}\n\t\t}\n\n\t\tif (contentType.includes(\"application/octet-stream\") ||\n\t\t\tcontentType.includes(\"image/\") ||\n\t\t\tcontentType.includes(\"audio/\") ||\n\t\t\tcontentType.includes(\"video/\") ||\n\t\t\tcontentType.includes(\"application/zip\")) {\n\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: \"\",\n\t\t\t\tcontent: \"\",\n\t\t\t\terror: `Unsupported content type: ${contentType.split(\";\")[0]}`,\n\t\t\t};\n\t\t}\n\n\t\tconst text = new TextDecoder().decode(await readBoundedBody(response, maxResponseSize, controller.signal));\n\t\tconst isHTML = contentType.includes(\"text/html\") || contentType.includes(\"application/xhtml+xml\");\n\n\t\tif (!isHTML) {\n\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\tconst title = extractTextTitle(text, url);\n\t\t\treturn { url, title, content: text, error: null };\n\t\t}\n\n\t\tconst { document } = parseHTML(text);\n\t\tconst reader = new Readability(document as unknown as Document);\n\t\tconst article = reader.parse();\n\n\t\tif (!article) {\n\t\t\tconst rscResult = extractRSCContent(text);\n\t\t\tif (rscResult) {\n\t\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\t\treturn { url, title: rscResult.title, content: rscResult.content, error: null };\n\t\t\t}\n\n\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\n\t\t\t// Provide more specific error message\n\t\t\tconst jsRendered = isLikelyJSRendered(text);\n\t\t\tconst errorMsg = jsRendered\n\t\t\t\t? \"Page appears to be JavaScript-rendered (content loads dynamically)\"\n\t\t\t\t: \"Could not extract readable content from HTML structure\";\n\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: \"\",\n\t\t\t\tcontent: \"\",\n\t\t\t\terror: errorMsg,\n\t\t\t};\n\t\t}\n\n\t\tconst markdown = turndown.turndown(article.content);\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\n\t\tif (markdown.length < MIN_USEFUL_CONTENT) {\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: article.title || \"\",\n\t\t\t\tcontent: markdown,\n\t\t\t\terror: isLikelyJSRendered(text)\n\t\t\t\t\t? \"Page appears to be JavaScript-rendered (content loads dynamically)\"\n\t\t\t\t\t: \"Extracted content appears incomplete\",\n\t\t\t};\n\t\t}\n\n\t\treturn { url, title: article.title || \"\", content: markdown, error: null };\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tif (message.toLowerCase().includes(\"abort\")) {\n\t\t\tactivityMonitor.logComplete(activityId, 0);\n\t\t} else {\n\t\t\tactivityMonitor.logError(activityId, message);\n\t\t}\n\t\treturn { url, title: \"\", content: \"\", error: message };\n\t} finally {\n\t\tclearTimeout(timeoutId);\n\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t}\n}\n\nexport function extractHeadingTitle(text: string): string | null {\n\tconst match = text.match(/^#{1,2}\\s+(.+)/m);\n\tif (!match) return null;\n\tconst cleaned = match[1].replace(/\\*+/g, \"\").trim();\n\treturn cleaned || null;\n}\n\nfunction extractTextTitle(text: string, url: string): string {\n\treturn extractHeadingTitle(text) ?? (new URL(url).pathname.split(\"/\").pop() || url);\n}\n\nexport async function fetchAllContent(\n\turls: string[],\n\tsignal?: AbortSignal,\n\toptions?: ExtractOptions,\n): Promise<ExtractedContent[]> {\n\treturn Promise.all(urls.map((url) => fetchLimit(() => extractContent(url, signal, options))));\n}\n"]}
|
|
@@ -3,6 +3,7 @@ import { Readability } from "@mozilla/readability";
|
|
|
3
3
|
import { parseHTML } from "linkedom";
|
|
4
4
|
import TurndownService from "turndown";
|
|
5
5
|
import pLimit from "p-limit";
|
|
6
|
+
import { readBoundedBody } from "./bounded-body.js";
|
|
6
7
|
import { activityMonitor } from "./activity.js";
|
|
7
8
|
import { extractRSCContent } from "./rsc-extract.js";
|
|
8
9
|
import { loadGeminiUrlContext, loadGithubExtract, loadParallel, loadPdfExtract, loadVideoExtract, loadYoutubeExtract, } from "./lazy.js";
|
|
@@ -523,6 +524,8 @@ async function extractViaHttp(url, signal, options) {
|
|
|
523
524
|
const controller = new AbortController();
|
|
524
525
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
525
526
|
const onAbort = () => controller.abort();
|
|
527
|
+
if (signal?.aborted)
|
|
528
|
+
controller.abort();
|
|
526
529
|
signal?.addEventListener("abort", onAbort);
|
|
527
530
|
try {
|
|
528
531
|
const response = await fetchRemoteUrl(url, {
|
|
@@ -555,6 +558,7 @@ async function extractViaHttp(url, signal, options) {
|
|
|
555
558
|
if (contentLengthHeader) {
|
|
556
559
|
const contentLength = parseInt(contentLengthHeader, 10);
|
|
557
560
|
if (contentLength > maxResponseSize) {
|
|
561
|
+
await response.body?.cancel();
|
|
558
562
|
activityMonitor.logComplete(activityId, response.status);
|
|
559
563
|
return {
|
|
560
564
|
url,
|
|
@@ -565,8 +569,9 @@ async function extractViaHttp(url, signal, options) {
|
|
|
565
569
|
}
|
|
566
570
|
}
|
|
567
571
|
if (isPDFContent) {
|
|
572
|
+
const bytes = await readBoundedBody(response, maxResponseSize, controller.signal);
|
|
568
573
|
try {
|
|
569
|
-
const buffer =
|
|
574
|
+
const buffer = bytes.buffer;
|
|
570
575
|
const { extractPDFToMarkdown } = await loadPdfExtract();
|
|
571
576
|
const result = await extractPDFToMarkdown(buffer, url);
|
|
572
577
|
activityMonitor.logComplete(activityId, response.status);
|
|
@@ -596,7 +601,7 @@ async function extractViaHttp(url, signal, options) {
|
|
|
596
601
|
error: `Unsupported content type: ${contentType.split(";")[0]}`,
|
|
597
602
|
};
|
|
598
603
|
}
|
|
599
|
-
const text = await response.
|
|
604
|
+
const text = new TextDecoder().decode(await readBoundedBody(response, maxResponseSize, controller.signal));
|
|
600
605
|
const isHTML = contentType.includes("text/html") || contentType.includes("application/xhtml+xml");
|
|
601
606
|
if (!isHTML) {
|
|
602
607
|
activityMonitor.logComplete(activityId, response.status);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract.js","sourceRoot":"","sources":["../../../src/builtin-extensions/pi-web-access/extract.ts"],"names":[],"mappings":"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","sourcesContent":["// @ts-nocheck\nimport { Readability } from \"@mozilla/readability\";\nimport { parseHTML } from \"linkedom\";\nimport TurndownService from \"turndown\";\nimport pLimit from \"p-limit\";\nimport { activityMonitor } from \"./activity.ts\";\nimport { extractRSCContent } from \"./rsc-extract.ts\";\nimport {\n\tloadGeminiUrlContext,\n\tloadGithubExtract,\n\tloadParallel,\n\tloadPdfExtract,\n\tloadVideoExtract,\n\tloadYoutubeExtract,\n} from \"./lazy.ts\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { fetchRemoteUrl, validateRemoteUrl, type Lookup } from \"./ssrf-protection.ts\";\nimport { formatSeconds, getWebSearchConfigPath } from \"./utils.ts\";\n\nconst YOUTUBE_URL_RE =\n\t/(?:(?:www\\.|m\\.)?youtube\\.com\\/(?:watch\\?.*v=|shorts\\/|live\\/|embed\\/|v\\/)|youtu\\.be\\/)([a-zA-Z0-9_-]{11})/;\n\nfunction isYouTubeURL(url: string): { isYouTube: boolean; videoId: string | null } {\n\ttry {\n\t\tconst parsed = new URL(url);\n\t\tif (parsed.pathname === \"/playlist\") {\n\t\t\treturn { isYouTube: false, videoId: null };\n\t\t}\n\t} catch {\n\t}\n\tconst match = url.match(YOUTUBE_URL_RE);\n\tif (!match) return { isYouTube: false, videoId: null };\n\treturn { isYouTube: true, videoId: match[1] };\n}\n\nfunction isPDF(url: string, contentType?: string): boolean {\n\tif (contentType?.includes(\"application/pdf\")) return true;\n\ttry {\n\t\treturn new URL(url).pathname.toLowerCase().endsWith(\".pdf\");\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction looksLikeLocalPath(input: string): boolean {\n\treturn input.startsWith(\"/\") || input.startsWith(\"./\") || input.startsWith(\"../\") || input.startsWith(\"file://\");\n}\n\nfunction looksLikeGitHubUrl(url: string): boolean {\n\ttry {\n\t\tconst host = new URL(url).hostname.toLowerCase();\n\t\treturn host === \"github.com\" || host === \"www.github.com\" || host.endsWith(\".github.com\");\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nconst DEFAULT_TIMEOUT_MS = 30000;\nconst CONCURRENT_LIMIT = 3;\n\nconst NON_RECOVERABLE_ERRORS = [\"Unsupported content type\", \"Response too large\"];\nconst MIN_USEFUL_CONTENT = 500;\nconst WEB_SEARCH_CONFIG_PATH = getWebSearchConfigPath();\n\n/**\n * Read `ssrf.allowRanges` (CIDR strings) from web-search.json. Returns [] when\n * the file is missing, unreadable, or the key is unset so SSRF protection stays\n * fully on by default. Throws when `ssrf.allowRanges` is present but not an array\n * so a mistyped value (e.g. a bare string instead of a JSON array) fails loudly\n * instead of being silently ignored. Exempts synthetic ranges used by TUN/fake-IP\n * proxies (e.g. 198.18.0.0/15).\n */\nexport function loadSsrfAllowRanges(): string[] {\n\tlet value: unknown;\n\ttry {\n\t\tif (!existsSync(WEB_SEARCH_CONFIG_PATH)) return [];\n\t\tconst raw = readFileSync(WEB_SEARCH_CONFIG_PATH, \"utf-8\");\n\t\tvalue = (JSON.parse(raw) as { ssrf?: { allowRanges?: unknown } })?.ssrf?.allowRanges;\n\t} catch {\n\t\t// Missing/unreadable file or invalid JSON: fail safe with SSRF fully on.\n\t\treturn [];\n\t}\n\tif (value === undefined || value === null) return [];\n\tif (!Array.isArray(value)) {\n\t\tthrow new Error(`ssrf.allowRanges in ${WEB_SEARCH_CONFIG_PATH} must be an array of CIDR strings`);\n\t}\n\tconst ranges: string[] = [];\n\tfor (const [index, entry] of value.entries()) {\n\t\tif (typeof entry !== \"string\") {\n\t\t\tthrow new Error(`ssrf.allowRanges in ${WEB_SEARCH_CONFIG_PATH} must contain only CIDR strings; entry ${index + 1} is ${typeof entry}`);\n\t\t}\n\t\tconst trimmed = entry.trim();\n\t\tif (trimmed) ranges.push(trimmed);\n\t}\n\treturn ranges;\n}\n\nfunction errorMessage(err: unknown): string {\n\treturn err instanceof Error ? err.message : String(err);\n}\n\nfunction isConfigParseError(err: unknown): boolean {\n\treturn errorMessage(err).startsWith(\"Failed to parse \");\n}\n\nfunction isAbortError(err: unknown): boolean {\n\treturn errorMessage(err).toLowerCase().includes(\"abort\");\n}\n\nfunction abortedResult(url: string): ExtractedContent {\n\treturn { url, title: \"\", content: \"\", error: \"Aborted\" };\n}\n\nconst turndown = new TurndownService({\n\theadingStyle: \"atx\",\n\tcodeBlockStyle: \"fenced\",\n});\n\nconst fetchLimit = pLimit(CONCURRENT_LIMIT);\n\nexport interface VideoFrame {\n\tdata: string;\n\tmimeType: string;\n\ttimestamp: string;\n}\n\nexport type FrameData = { data: string; mimeType: string };\nexport type FrameResult = FrameData | { error: string };\n\nexport interface ExtractedContent {\n\turl: string;\n\ttitle: string;\n\tcontent: string;\n\terror: string | null;\n\tthumbnail?: { data: string; mimeType: string };\n\tframes?: VideoFrame[];\n\tduration?: number;\n}\n\nexport interface ExtractOptions {\n\ttimeoutMs?: number;\n\tforceClone?: boolean;\n\tprompt?: string;\n\ttimestamp?: string;\n\tframes?: number;\n\tmodel?: string;\n\t/** Custom DNS resolver used for SSRF validation. Primarily a test seam. */\n\tlookup?: Lookup;\n}\n\nconst JINA_READER_BASE = \"https://r.jina.ai/\";\nconst JINA_TIMEOUT_MS = 30000;\n\nasync function extractWithJinaReader(\n\turl: string,\n\tsignal?: AbortSignal,\n\tlookup?: Lookup,\n): Promise<ExtractedContent | null> {\n\tconst jinaUrl = JINA_READER_BASE + url;\n\n\tconst activityId = activityMonitor.logStart({ type: \"api\", query: `jina: ${url}` });\n\n\ttry {\n\t\tawait validateRemoteUrl(url, { allowRanges: loadSsrfAllowRanges(), lookup });\n\t\tconst res = await fetch(jinaUrl, {\n\t\t\theaders: {\n\t\t\t\t\"Accept\": \"text/markdown\",\n\t\t\t\t\"X-No-Cache\": \"true\",\n\t\t\t},\n\t\t\tsignal: AbortSignal.any([\n\t\t\t\tAbortSignal.timeout(JINA_TIMEOUT_MS),\n\t\t\t\t...(signal ? [signal] : []),\n\t\t\t]),\n\t\t});\n\n\t\tif (!res.ok) {\n\t\t\tactivityMonitor.logComplete(activityId, res.status);\n\t\t\treturn null;\n\t\t}\n\n\t\tconst content = await res.text();\n\t\tactivityMonitor.logComplete(activityId, res.status);\n\n\t\tconst contentStart = content.indexOf(\"Markdown Content:\");\n\t\tif (contentStart < 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst markdownPart = content.slice(contentStart + 17).trim(); // 17 = \"Markdown Content:\".length\n\n\t\t// Check for failed JS rendering or minimal content\n\t\tif (markdownPart.length < 100 ||\n\t\t\tmarkdownPart.startsWith(\"Loading...\") ||\n\t\t\tmarkdownPart.startsWith(\"Please enable JavaScript\")) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst title = extractHeadingTitle(markdownPart) ?? (new URL(url).pathname.split(\"/\").pop() || url);\n\t\treturn { url, title, content: markdownPart, error: null };\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tif (message.toLowerCase().includes(\"abort\")) {\n\t\t\tactivityMonitor.logComplete(activityId, 0);\n\t\t} else {\n\t\t\tactivityMonitor.logError(activityId, message);\n\t\t}\n\t\treturn null;\n\t}\n}\n\nfunction parseTimestamp(ts: string): number | null {\n\tconst num = Number(ts);\n\tif (!isNaN(num) && num >= 0) return Math.floor(num);\n\tconst parts = ts.split(\":\").map(Number);\n\tif (parts.some(p => isNaN(p) || p < 0)) return null;\n\tif (parts.length === 3) return Math.floor(parts[0] * 3600 + parts[1] * 60 + parts[2]);\n\tif (parts.length === 2) return Math.floor(parts[0] * 60 + parts[1]);\n\treturn null;\n}\n\ntype TimestampSpec = { type: \"single\"; seconds: number } | { type: \"range\"; start: number; end: number };\n\nfunction parseTimestampSpec(ts: string): TimestampSpec | null {\n\tconst dashIdx = ts.indexOf(\"-\", 1);\n\tif (dashIdx > 0) {\n\t\tconst start = parseTimestamp(ts.slice(0, dashIdx));\n\t\tconst end = parseTimestamp(ts.slice(dashIdx + 1));\n\t\tif (start !== null && end !== null && end > start) return { type: \"range\", start, end };\n\t}\n\tconst seconds = parseTimestamp(ts);\n\treturn seconds !== null ? { type: \"single\", seconds } : null;\n}\n\nconst DEFAULT_RANGE_FRAMES = 6;\nconst MIN_FRAME_INTERVAL = 5;\n\nfunction computeRangeTimestamps(start: number, end: number, maxFrames: number = DEFAULT_RANGE_FRAMES): number[] {\n\tif (maxFrames <= 1) return [start];\n\tconst duration = end - start;\n\tconst idealInterval = duration / (maxFrames - 1);\n\tif (idealInterval < MIN_FRAME_INTERVAL) {\n\t\tconst timestamps: number[] = [];\n\t\tfor (let t = start; t <= end && timestamps.length < maxFrames; t += MIN_FRAME_INTERVAL) {\n\t\t\ttimestamps.push(t);\n\t\t}\n\t\treturn timestamps;\n\t}\n\treturn Array.from({ length: maxFrames }, (_, i) => Math.round(start + i * idealInterval));\n}\n\nfunction buildFrameResult(\n\turl: string, label: string, requestedCount: number,\n\tframes: VideoFrame[], error: string | null, duration?: number,\n): ExtractedContent {\n\tif (frames.length === 0) {\n\t\tconst msg = error ?? \"Frame extraction failed\";\n\t\treturn { url, title: `Frames ${label} (0/${requestedCount})`, content: msg, error: msg };\n\t}\n\treturn {\n\t\turl,\n\t\ttitle: `Frames ${label} (${frames.length}/${requestedCount})`,\n\t\tcontent: `${frames.length} frames extracted from ${label}`,\n\t\terror: null,\n\t\tframes,\n\t\tduration,\n\t};\n}\n\nasync function extractLocalFrames(\n\tfilePath: string, timestamps: number[],\n): Promise<{ frames: VideoFrame[]; error: string | null }> {\n\tconst { extractVideoFrame } = await loadVideoExtract();\n\tconst results = await Promise.all(timestamps.map(async (t) => {\n\t\tconst frame = await extractVideoFrame(filePath, t);\n\t\tif (\"error\" in frame) return { error: frame.error };\n\t\treturn { ...frame, timestamp: formatSeconds(t) };\n\t}));\n\tconst frames = results.filter((f): f is VideoFrame => \"data\" in f);\n\tconst firstError = results.find((f): f is { error: string } => \"error\" in f);\n\treturn { frames, error: frames.length === 0 && firstError ? firstError.error : null };\n}\n\nasync function safeVideoInfo(url: string): Promise<{ info: Awaited<ReturnType<typeof loadVideoExtract>> extends { isVideoFile: infer F } ? (F extends (...args: any) => infer R ? R : null) : null; error?: string }> {\n\tif (!looksLikeLocalPath(url)) return { info: null };\n\ttry {\n\t\tconst { isVideoFile } = await loadVideoExtract();\n\t\treturn { info: isVideoFile(url) };\n\t} catch (err) {\n\t\treturn { info: null, error: errorMessage(err) };\n\t}\n}\n\nexport async function extractContent(\n\turl: string,\n\tsignal?: AbortSignal,\n\toptions?: ExtractOptions,\n): Promise<ExtractedContent> {\n\tif (signal?.aborted) {\n\t\treturn { url, title: \"\", content: \"\", error: \"Aborted\" };\n\t}\n\n\tif (options?.frames && !options.timestamp) {\n\t\tconst frameCount = options.frames;\n\t\tconst ytInfo = isYouTubeURL(url);\n\t\tif (ytInfo.isYouTube && ytInfo.videoId) {\n\t\t\tconst yt = await loadYoutubeExtract();\n\t\t\tconst streamInfo = await yt.getYouTubeStreamInfo(ytInfo.videoId);\n\t\t\tif (\"error\" in streamInfo) {\n\t\t\t\treturn { url, title: \"Frames\", content: streamInfo.error, error: streamInfo.error };\n\t\t\t}\n\t\t\tif (streamInfo.duration === null) {\n\t\t\t\tconst error = \"Cannot determine video duration. Use a timestamp range instead.\";\n\t\t\t\treturn { url, title: \"Frames\", content: error, error };\n\t\t\t}\n\t\t\tconst dur = Math.floor(streamInfo.duration);\n\t\t\tconst timestamps = computeRangeTimestamps(0, dur, frameCount);\n\t\t\tconst result = await yt.extractYouTubeFrames(ytInfo.videoId, timestamps, streamInfo);\n\t\t\tconst label = `${formatSeconds(0)}-${formatSeconds(dur)}`;\n\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error, streamInfo.duration);\n\t\t}\n\n\t\tconst localVideo = await safeVideoInfo(url);\n\t\tif (localVideo.error) {\n\t\t\treturn { url, title: \"\", content: \"\", error: localVideo.error };\n\t\t}\n\t\tif (localVideo.info) {\n\t\t\tconst { getLocalVideoDuration } = await loadVideoExtract();\n\t\t\tconst durationResult = await getLocalVideoDuration(localVideo.info.absolutePath);\n\t\t\tif (typeof durationResult !== \"number\") {\n\t\t\t\treturn { url, title: \"Frames\", content: durationResult.error, error: durationResult.error };\n\t\t\t}\n\t\t\tconst dur = Math.floor(durationResult);\n\t\t\tconst timestamps = computeRangeTimestamps(0, dur, frameCount);\n\t\t\tconst result = await extractLocalFrames(localVideo.info.absolutePath, timestamps);\n\t\t\tconst label = `${formatSeconds(0)}-${formatSeconds(dur)}`;\n\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error, durationResult);\n\t\t}\n\n\t\treturn { url, title: \"\", content: \"\", error: \"Frame extraction only works with YouTube and local video files\" };\n\t}\n\n\tif (options?.timestamp) {\n\t\tconst spec = parseTimestampSpec(options.timestamp);\n\t\tif (!spec) {\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: \"\",\n\t\t\t\tcontent: \"\",\n\t\t\t\terror: `Invalid timestamp format: \"${options.timestamp}\". Use \"H:MM:SS\", \"MM:SS\", \"85\", or \"start-end\".`,\n\t\t\t};\n\t\t}\n\n\t\tconst frameCount = options.frames;\n\t\tconst ytInfo = isYouTubeURL(url);\n\t\tif (ytInfo.isYouTube && ytInfo.videoId) {\n\t\t\tconst yt = await loadYoutubeExtract();\n\t\t\tconst streamInfo = await yt.getYouTubeStreamInfo(ytInfo.videoId);\n\t\t\tif (\"error\" in streamInfo) {\n\t\t\t\tif (spec.type === \"range\") {\n\t\t\t\t\tconst label = `${formatSeconds(spec.start)}-${formatSeconds(spec.end)}`;\n\t\t\t\t\treturn { url, title: `Frames ${label}`, content: streamInfo.error, error: streamInfo.error };\n\t\t\t\t}\n\t\t\t\tif (frameCount) {\n\t\t\t\t\tconst end = spec.seconds + (frameCount - 1) * MIN_FRAME_INTERVAL;\n\t\t\t\t\tconst label = `${formatSeconds(spec.seconds)}-${formatSeconds(end)}`;\n\t\t\t\t\treturn { url, title: `Frames ${label}`, content: streamInfo.error, error: streamInfo.error };\n\t\t\t\t}\n\t\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: streamInfo.error, error: streamInfo.error };\n\t\t\t}\n\n\t\t\tif (spec.type === \"range\") {\n\t\t\t\tconst label = `${formatSeconds(spec.start)}-${formatSeconds(spec.end)}`;\n\t\t\t\tif (streamInfo.duration !== null && spec.end > streamInfo.duration) {\n\t\t\t\t\tconst error = `Timestamp ${formatSeconds(spec.end)} exceeds video duration (${formatSeconds(Math.floor(streamInfo.duration))})`;\n\t\t\t\t\treturn { url, title: `Frames ${label}`, content: error, error };\n\t\t\t\t}\n\t\t\t\tconst timestamps = frameCount\n\t\t\t\t\t? computeRangeTimestamps(spec.start, spec.end, frameCount)\n\t\t\t\t\t: computeRangeTimestamps(spec.start, spec.end);\n\t\t\t\tconst result = await yt.extractYouTubeFrames(ytInfo.videoId, timestamps, streamInfo);\n\t\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error, result.duration ?? undefined);\n\t\t\t}\n\n\t\t\tif (frameCount) {\n\t\t\t\tconst end = spec.seconds + (frameCount - 1) * MIN_FRAME_INTERVAL;\n\t\t\t\tconst label = `${formatSeconds(spec.seconds)}-${formatSeconds(end)}`;\n\t\t\t\tif (streamInfo.duration !== null && end > streamInfo.duration) {\n\t\t\t\t\tconst error = `Timestamp ${formatSeconds(end)} exceeds video duration (${formatSeconds(Math.floor(streamInfo.duration))})`;\n\t\t\t\t\treturn { url, title: `Frames ${label}`, content: error, error };\n\t\t\t\t}\n\t\t\t\tconst timestamps = computeRangeTimestamps(spec.seconds, end, frameCount);\n\t\t\t\tconst result = await yt.extractYouTubeFrames(ytInfo.videoId, timestamps, streamInfo);\n\t\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error, result.duration ?? undefined);\n\t\t\t}\n\n\t\t\tif (streamInfo.duration !== null && spec.seconds > streamInfo.duration) {\n\t\t\t\tconst error = `Timestamp ${formatSeconds(spec.seconds)} exceeds video duration (${formatSeconds(Math.floor(streamInfo.duration))})`;\n\t\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: error, error };\n\t\t\t}\n\t\t\tconst frame = await yt.extractYouTubeFrame(ytInfo.videoId, spec.seconds, streamInfo);\n\t\t\tif (\"error\" in frame) {\n\t\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: frame.error, error: frame.error };\n\t\t\t}\n\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: `Video frame at ${options.timestamp}`, error: null, thumbnail: frame };\n\t\t}\n\n\t\tconst localVideo = await safeVideoInfo(url);\n\t\tif (localVideo.error) {\n\t\t\treturn { url, title: \"\", content: \"\", error: localVideo.error };\n\t\t}\n\t\tif (localVideo.info) {\n\t\t\tif (spec.type === \"range\") {\n\t\t\t\tconst timestamps = frameCount\n\t\t\t\t\t? computeRangeTimestamps(spec.start, spec.end, frameCount)\n\t\t\t\t\t: computeRangeTimestamps(spec.start, spec.end);\n\t\t\t\tconst result = await extractLocalFrames(localVideo.info.absolutePath, timestamps);\n\t\t\t\tconst label = `${formatSeconds(spec.start)}-${formatSeconds(spec.end)}`;\n\t\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error);\n\t\t\t}\n\n\t\t\tif (frameCount) {\n\t\t\t\tconst end = spec.seconds + (frameCount - 1) * MIN_FRAME_INTERVAL;\n\t\t\t\tconst timestamps = computeRangeTimestamps(spec.seconds, end, frameCount);\n\t\t\t\tconst result = await extractLocalFrames(localVideo.info.absolutePath, timestamps);\n\t\t\t\tconst label = `${formatSeconds(spec.seconds)}-${formatSeconds(end)}`;\n\t\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error);\n\t\t\t}\n\n\t\t\tconst { extractVideoFrame } = await loadVideoExtract();\n\t\t\tconst frame = await extractVideoFrame(localVideo.info.absolutePath, spec.seconds);\n\t\t\tif (\"error\" in frame) {\n\t\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: frame.error, error: frame.error };\n\t\t\t}\n\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: `Video frame at ${options.timestamp}`, error: null, thumbnail: frame };\n\t\t}\n\n\t\treturn { url, title: \"\", content: \"\", error: \"Timestamp extraction only works with YouTube and local video files\" };\n\t}\n\n\tconst localVideo = await safeVideoInfo(url);\n\tif (localVideo.error) {\n\t\treturn { url, title: \"\", content: \"\", error: localVideo.error };\n\t}\n\tif (localVideo.info) {\n\t\ttry {\n\t\t\tconst { extractVideo } = await loadVideoExtract();\n\t\t\tconst result = await extractVideo(localVideo.info, signal, options);\n\t\t\tif (signal?.aborted) return abortedResult(url);\n\t\t\treturn result ?? { url, title: \"\", content: \"\", error: `Video analysis requires Gemini access. Either:\\n 1. Sign into gemini.google.com in Chrome (free, uses cookies)\\n 2. Set GEMINI_API_KEY in ${WEB_SEARCH_CONFIG_PATH}` };\n\t\t} catch (err) {\n\t\t\tif (isAbortError(err)) return abortedResult(url);\n\t\t\treturn { url, title: \"\", content: \"\", error: errorMessage(err) };\n\t\t}\n\t}\n\n\ttry {\n\t\tconst parsed = new URL(url);\n\t\tif (parsed.protocol === \"http:\" || parsed.protocol === \"https:\") {\n\t\t\tawait validateRemoteUrl(parsed, { allowRanges: loadSsrfAllowRanges(), lookup: options?.lookup });\n\t\t}\n\t} catch (err) {\n\t\treturn { url, title: \"\", content: \"\", error: errorMessage(err) };\n\t}\n\n\tif (looksLikeGitHubUrl(url)) {\n\t\ttry {\n\t\t\tconst { extractGitHub } = await loadGithubExtract();\n\t\t\tconst ghResult = await extractGitHub(url, signal, options?.forceClone);\n\t\t\tif (ghResult) return ghResult;\n\t\t\tif (signal?.aborted) return abortedResult(url);\n\t\t} catch (err) {\n\t\t\tconst message = errorMessage(err);\n\t\t\tif (isAbortError(err)) return abortedResult(url);\n\t\t\tif (isConfigParseError(err)) {\n\t\t\t\treturn { url, title: \"\", content: \"\", error: message };\n\t\t\t}\n\t\t}\n\t}\n\n\tconst ytInfo = isYouTubeURL(url);\n\tif (ytInfo.isYouTube) {\n\t\tlet youtubeEnabled = false;\n\t\tlet yt;\n\t\ttry {\n\t\t\tyt = await loadYoutubeExtract();\n\t\t\tyoutubeEnabled = yt.isYouTubeEnabled();\n\t\t} catch (err) {\n\t\t\treturn { url, title: \"\", content: \"\", error: errorMessage(err) };\n\t\t}\n\t\tif (youtubeEnabled) {\n\t\t\ttry {\n\t\t\t\tconst ytResult = await yt.extractYouTube(url, signal, options?.prompt, options?.model);\n\t\t\t\tif (ytResult) return ytResult;\n\t\t\t\tif (signal?.aborted) return abortedResult(url);\n\t\t\t} catch (err) {\n\t\t\t\tconst message = errorMessage(err);\n\t\t\t\tif (isAbortError(err)) return abortedResult(url);\n\t\t\t\treturn { url, title: \"\", content: \"\", error: message };\n\t\t\t}\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: \"\",\n\t\t\t\tcontent: \"\",\n\t\t\t\terror: \"Could not extract YouTube video content. Sign into Google in Chrome for automatic access, or set GEMINI_API_KEY.\",\n\t\t\t};\n\t\t}\n\t}\n\n\tif (signal?.aborted) return abortedResult(url);\n\n\tconst httpResult = await extractViaHttp(url, signal, options);\n\n\tif (signal?.aborted) return abortedResult(url);\n\tif (!httpResult.error) return httpResult;\n\tif (NON_RECOVERABLE_ERRORS.some(prefix => httpResult.error!.startsWith(prefix))) return httpResult;\n\n\tconst jinaResult = await extractWithJinaReader(url, signal, options?.lookup);\n\tif (jinaResult) return jinaResult;\n\tif (signal?.aborted) return abortedResult(url);\n\n\tlet parallelError: string | null = null;\n\ttry {\n\t\tconst parallel = await loadParallel();\n\t\tif (parallel.isParallelAvailable()) {\n\t\t\tconst parallelResult = await parallel.extractWithParallel(url, signal, options);\n\t\t\tif (parallelResult) return parallelResult;\n\t\t}\n\t} catch (err) {\n\t\tif (isAbortError(err)) return abortedResult(url);\n\t\tparallelError = errorMessage(err);\n\t\tif (isConfigParseError(err)) {\n\t\t\treturn { ...httpResult, error: parallelError };\n\t\t}\n\t}\n\tif (signal?.aborted) return abortedResult(url);\n\n\tlet geminiResult: ExtractedContent | null = null;\n\ttry {\n\t\tconst geminiUrl = await loadGeminiUrlContext();\n\t\tgeminiResult = await geminiUrl.extractWithUrlContext(url, signal)\n\t\t\t?? await geminiUrl.extractWithGeminiWeb(url, signal);\n\t} catch (err) {\n\t\tif (isAbortError(err)) return abortedResult(url);\n\t\tif (isConfigParseError(err)) {\n\t\t\treturn { ...httpResult, error: errorMessage(err) };\n\t\t}\n\t}\n\n\tif (geminiResult) return geminiResult;\n\tif (signal?.aborted) return abortedResult(url);\n\n\tconst guidance = [\n\t\thttpResult.error,\n\t\t...(parallelError ? [`Parallel fallback failed: ${parallelError}`] : []),\n\t\t\"\",\n\t\t\"Fallback options:\",\n\t\t` \\u2022 Set PARALLEL_API_KEY in ${WEB_SEARCH_CONFIG_PATH}`,\n\t\t` \\u2022 Set GEMINI_API_KEY in ${WEB_SEARCH_CONFIG_PATH}`,\n\t\t\" \\u2022 Sign into gemini.google.com in Chrome\",\n\t\t\" \\u2022 Use web_search to find content about this topic\",\n\t].join(\"\\n\");\n\treturn { ...httpResult, error: guidance };\n}\n\nfunction isLikelyJSRendered(html: string): boolean {\n\t// Extract body content\n\tconst bodyMatch = html.match(/<body[^>]*>([\\s\\S]*?)<\\/body>/i);\n\tif (!bodyMatch) return false;\n\n\tconst bodyHtml = bodyMatch[1];\n\n\t// Strip tags to get text content\n\tconst textContent = bodyHtml\n\t\t.replace(/<script[\\s\\S]*?<\\/script>/gi, \"\")\n\t\t.replace(/<style[\\s\\S]*?<\\/style>/gi, \"\")\n\t\t.replace(/<[^>]+>/g, \"\")\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim();\n\n\t// Count scripts\n\tconst scriptCount = (html.match(/<script/gi) || []).length;\n\n\t// Heuristic: little text content but many scripts suggests JS rendering\n\treturn textContent.length < 500 && scriptCount > 3;\n}\n\nasync function extractViaHttp(\n\turl: string,\n\tsignal?: AbortSignal,\n\toptions?: ExtractOptions,\n): Promise<ExtractedContent> {\n\tconst timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\tconst activityId = activityMonitor.logStart({ type: \"fetch\", url });\n\n\tconst controller = new AbortController();\n\tconst timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n\tconst onAbort = () => controller.abort();\n\tsignal?.addEventListener(\"abort\", onAbort);\n\n\ttry {\n\t\tconst response = await fetchRemoteUrl(\n\t\t\turl,\n\t\t\t{\n\t\t\t\tsignal: controller.signal,\n\t\t\t\theaders: {\n\t\t\t\t\t\"User-Agent\": \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36\",\n\t\t\t\t\t\"Accept\": \"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8\",\n\t\t\t\t\t\"Accept-Language\": \"en-US,en;q=0.9\",\n\t\t\t\t\t\"Cache-Control\": \"no-cache\",\n\t\t\t\t\t\"Sec-Fetch-Dest\": \"document\",\n\t\t\t\t\t\"Sec-Fetch-Mode\": \"navigate\",\n\t\t\t\t\t\"Sec-Fetch-Site\": \"none\",\n\t\t\t\t\t\"Sec-Fetch-User\": \"?1\",\n\t\t\t\t\t\"Upgrade-Insecure-Requests\": \"1\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ allowRanges: loadSsrfAllowRanges(), lookup: options?.lookup },\n\t\t);\n\n\t\tif (!response.ok) {\n\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: \"\",\n\t\t\t\tcontent: \"\",\n\t\t\t\terror: `HTTP ${response.status}: ${response.statusText}`,\n\t\t\t};\n\t\t}\n\n\t\tconst contentLengthHeader = response.headers.get(\"content-length\");\n\t\tconst contentType = response.headers.get(\"content-type\") || \"\";\n\t\tconst isPDFContent = isPDF(url, contentType);\n\t\tconst maxResponseSize = isPDFContent ? 20 * 1024 * 1024 : 5 * 1024 * 1024;\n\t\tif (contentLengthHeader) {\n\t\t\tconst contentLength = parseInt(contentLengthHeader, 10);\n\t\t\tif (contentLength > maxResponseSize) {\n\t\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\t\treturn {\n\t\t\t\t\turl,\n\t\t\t\t\ttitle: \"\",\n\t\t\t\t\tcontent: \"\",\n\t\t\t\t\terror: `Response too large (${Math.round(contentLength / 1024 / 1024)}MB)`,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tif (isPDFContent) {\n\t\t\ttry {\n\t\t\t\tconst buffer = await response.arrayBuffer();\n\t\t\t\tconst { extractPDFToMarkdown } = await loadPdfExtract();\n\t\t\t\tconst result = await extractPDFToMarkdown(buffer, url);\n\t\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\t\treturn {\n\t\t\t\t\turl,\n\t\t\t\t\ttitle: result.title,\n\t\t\t\t\tcontent: `PDF extracted and saved to: ${result.outputPath}\\n\\nPages: ${result.pages}\\nCharacters: ${result.chars}`,\n\t\t\t\t\terror: null,\n\t\t\t\t};\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\tactivityMonitor.logError(activityId, message);\n\t\t\t\treturn { url, title: \"\", content: \"\", error: `PDF extraction failed: ${message}` };\n\t\t\t}\n\t\t}\n\n\t\tif (contentType.includes(\"application/octet-stream\") ||\n\t\t\tcontentType.includes(\"image/\") ||\n\t\t\tcontentType.includes(\"audio/\") ||\n\t\t\tcontentType.includes(\"video/\") ||\n\t\t\tcontentType.includes(\"application/zip\")) {\n\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: \"\",\n\t\t\t\tcontent: \"\",\n\t\t\t\terror: `Unsupported content type: ${contentType.split(\";\")[0]}`,\n\t\t\t};\n\t\t}\n\n\t\tconst text = await response.text();\n\t\tconst isHTML = contentType.includes(\"text/html\") || contentType.includes(\"application/xhtml+xml\");\n\n\t\tif (!isHTML) {\n\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\tconst title = extractTextTitle(text, url);\n\t\t\treturn { url, title, content: text, error: null };\n\t\t}\n\n\t\tconst { document } = parseHTML(text);\n\t\tconst reader = new Readability(document as unknown as Document);\n\t\tconst article = reader.parse();\n\n\t\tif (!article) {\n\t\t\tconst rscResult = extractRSCContent(text);\n\t\t\tif (rscResult) {\n\t\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\t\treturn { url, title: rscResult.title, content: rscResult.content, error: null };\n\t\t\t}\n\n\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\n\t\t\t// Provide more specific error message\n\t\t\tconst jsRendered = isLikelyJSRendered(text);\n\t\t\tconst errorMsg = jsRendered\n\t\t\t\t? \"Page appears to be JavaScript-rendered (content loads dynamically)\"\n\t\t\t\t: \"Could not extract readable content from HTML structure\";\n\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: \"\",\n\t\t\t\tcontent: \"\",\n\t\t\t\terror: errorMsg,\n\t\t\t};\n\t\t}\n\n\t\tconst markdown = turndown.turndown(article.content);\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\n\t\tif (markdown.length < MIN_USEFUL_CONTENT) {\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: article.title || \"\",\n\t\t\t\tcontent: markdown,\n\t\t\t\terror: isLikelyJSRendered(text)\n\t\t\t\t\t? \"Page appears to be JavaScript-rendered (content loads dynamically)\"\n\t\t\t\t\t: \"Extracted content appears incomplete\",\n\t\t\t};\n\t\t}\n\n\t\treturn { url, title: article.title || \"\", content: markdown, error: null };\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tif (message.toLowerCase().includes(\"abort\")) {\n\t\t\tactivityMonitor.logComplete(activityId, 0);\n\t\t} else {\n\t\t\tactivityMonitor.logError(activityId, message);\n\t\t}\n\t\treturn { url, title: \"\", content: \"\", error: message };\n\t} finally {\n\t\tclearTimeout(timeoutId);\n\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t}\n}\n\nexport function extractHeadingTitle(text: string): string | null {\n\tconst match = text.match(/^#{1,2}\\s+(.+)/m);\n\tif (!match) return null;\n\tconst cleaned = match[1].replace(/\\*+/g, \"\").trim();\n\treturn cleaned || null;\n}\n\nfunction extractTextTitle(text: string, url: string): string {\n\treturn extractHeadingTitle(text) ?? (new URL(url).pathname.split(\"/\").pop() || url);\n}\n\nexport async function fetchAllContent(\n\turls: string[],\n\tsignal?: AbortSignal,\n\toptions?: ExtractOptions,\n): Promise<ExtractedContent[]> {\n\treturn Promise.all(urls.map((url) => fetchLimit(() => extractContent(url, signal, options))));\n}\n"]}
|
|
1
|
+
{"version":3,"file":"extract.js","sourceRoot":"","sources":["../../../src/builtin-extensions/pi-web-access/extract.ts"],"names":[],"mappings":"AAAA,cAAc;AACd,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,eAAe,MAAM,UAAU,CAAC;AACvC,OAAO,MAAM,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EACN,oBAAoB,EACpB,iBAAiB,EACjB,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,kBAAkB,GAClB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAe,MAAM,sBAAsB,CAAC;AACtF,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEnE,MAAM,cAAc,GACnB,4GAA4G,CAAC;AAE9G,SAAS,YAAY,CAAC,GAAW,EAAkD;IAClF,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;YACrC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC5C,CAAC;IACF,CAAC;IAAC,MAAM,CAAC;IACT,CAAC;IACD,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IACxC,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACvD,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAAA,CAC9C;AAED,SAAS,KAAK,CAAC,GAAW,EAAE,WAAoB,EAAW;IAC1D,IAAI,WAAW,EAAE,QAAQ,CAAC,iBAAiB,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1D,IAAI,CAAC;QACJ,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AAAA,CACD;AAED,SAAS,kBAAkB,CAAC,KAAa,EAAW;IACnD,OAAO,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;AAAA,CACjH;AAED,SAAS,kBAAkB,CAAC,GAAW,EAAW;IACjD,IAAI,CAAC;QACJ,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;QACjD,OAAO,IAAI,KAAK,YAAY,IAAI,IAAI,KAAK,gBAAgB,IAAI,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IAC3F,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AAAA,CACD;AAED,MAAM,kBAAkB,GAAG,KAAK,CAAC;AACjC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAE3B,MAAM,sBAAsB,GAAG,CAAC,0BAA0B,EAAE,oBAAoB,CAAC,CAAC;AAClF,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAC/B,MAAM,sBAAsB,GAAG,sBAAsB,EAAE,CAAC;AAExD;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,GAAa;IAC/C,IAAI,KAAc,CAAC;IACnB,IAAI,CAAC;QACJ,IAAI,CAAC,UAAU,CAAC,sBAAsB,CAAC;YAAE,OAAO,EAAE,CAAC;QACnD,MAAM,GAAG,GAAG,YAAY,CAAC,sBAAsB,EAAE,OAAO,CAAC,CAAC;QAC1D,KAAK,GAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAA0C,EAAE,IAAI,EAAE,WAAW,CAAC;IACtF,CAAC;IAAC,MAAM,CAAC;QACR,yEAAyE;QACzE,OAAO,EAAE,CAAC;IACX,CAAC;IACD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IACrD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,uBAAuB,sBAAsB,mCAAmC,CAAC,CAAC;IACnG,CAAC;IACD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QAC9C,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,uBAAuB,sBAAsB,0CAA0C,KAAK,GAAG,CAAC,OAAO,OAAO,KAAK,EAAE,CAAC,CAAC;QACxI,CAAC;QACD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,OAAO;YAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,MAAM,CAAC;AAAA,CACd;AAED,SAAS,YAAY,CAAC,GAAY,EAAU;IAC3C,OAAO,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAAA,CACxD;AAED,SAAS,kBAAkB,CAAC,GAAY,EAAW;IAClD,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC;AAAA,CACxD;AAED,SAAS,YAAY,CAAC,GAAY,EAAW;IAC5C,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AAAA,CACzD;AAED,SAAS,aAAa,CAAC,GAAW,EAAoB;IACrD,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AAAA,CACzD;AAED,MAAM,QAAQ,GAAG,IAAI,eAAe,CAAC;IACpC,YAAY,EAAE,KAAK;IACnB,cAAc,EAAE,QAAQ;CACxB,CAAC,CAAC;AAEH,MAAM,UAAU,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;AAgC5C,MAAM,gBAAgB,GAAG,oBAAoB,CAAC;AAC9C,MAAM,eAAe,GAAG,KAAK,CAAC;AAE9B,KAAK,UAAU,qBAAqB,CACnC,GAAW,EACX,MAAoB,EACpB,MAAe,EACoB;IACnC,MAAM,OAAO,GAAG,gBAAgB,GAAG,GAAG,CAAC;IAEvC,MAAM,UAAU,GAAG,eAAe,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,GAAG,EAAE,EAAE,CAAC,CAAC;IAEpF,IAAI,CAAC;QACJ,MAAM,iBAAiB,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,mBAAmB,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QAC7E,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,OAAO,EAAE;YAChC,OAAO,EAAE;gBACR,QAAQ,EAAE,eAAe;gBACzB,YAAY,EAAE,MAAM;aACpB;YACD,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC;gBACvB,WAAW,CAAC,OAAO,CAAC,eAAe,CAAC;gBACpC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;aAC3B,CAAC;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACb,eAAe,CAAC,WAAW,CAAC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;YACpD,OAAO,IAAI,CAAC;QACb,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QACjC,eAAe,CAAC,WAAW,CAAC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;QAEpD,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;QAC1D,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;YACtB,OAAO,IAAI,CAAC;QACb,CAAC;QAED,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,YAAY,GAAG,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,kCAAkC;QAEhG,mDAAmD;QACnD,IAAI,YAAY,CAAC,MAAM,GAAG,GAAG;YAC5B,YAAY,CAAC,UAAU,CAAC,YAAY,CAAC;YACrC,YAAY,CAAC,UAAU,CAAC,0BAA0B,CAAC,EAAE,CAAC;YACtD,OAAO,IAAI,CAAC;QACb,CAAC;QAED,MAAM,KAAK,GAAG,mBAAmB,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,GAAG,CAAC,CAAC;QACnG,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAC3D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7C,eAAe,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;QAC5C,CAAC;aAAM,CAAC;YACP,eAAe,CAAC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC/C,CAAC;QACD,OAAO,IAAI,CAAC;IACb,CAAC;AAAA,CACD;AAED,SAAS,cAAc,CAAC,EAAU,EAAiB;IAClD,MAAM,GAAG,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC;IACvB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACxC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACtF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACpE,OAAO,IAAI,CAAC;AAAA,CACZ;AAID,SAAS,kBAAkB,CAAC,EAAU,EAAwB;IAC7D,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACnC,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QACjB,MAAM,KAAK,GAAG,cAAc,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QACnD,MAAM,GAAG,GAAG,cAAc,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;QAClD,IAAI,KAAK,KAAK,IAAI,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,GAAG,KAAK;YAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IACzF,CAAC;IACD,MAAM,OAAO,GAAG,cAAc,CAAC,EAAE,CAAC,CAAC;IACnC,OAAO,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAAA,CAC7D;AAED,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAC/B,MAAM,kBAAkB,GAAG,CAAC,CAAC;AAE7B,SAAS,sBAAsB,CAAC,KAAa,EAAE,GAAW,EAAE,SAAS,GAAW,oBAAoB,EAAY;IAC/G,IAAI,SAAS,IAAI,CAAC;QAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,QAAQ,GAAG,GAAG,GAAG,KAAK,CAAC;IAC7B,MAAM,aAAa,GAAG,QAAQ,GAAG,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;IACjD,IAAI,aAAa,GAAG,kBAAkB,EAAE,CAAC;QACxC,MAAM,UAAU,GAAa,EAAE,CAAC;QAChC,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,IAAI,GAAG,IAAI,UAAU,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC,IAAI,kBAAkB,EAAE,CAAC;YACxF,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;QACD,OAAO,UAAU,CAAC;IACnB,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC;AAAA,CAC1F;AAED,SAAS,gBAAgB,CACxB,GAAW,EAAE,KAAa,EAAE,cAAsB,EAClD,MAAoB,EAAE,KAAoB,EAAE,QAAiB,EAC1C;IACnB,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,KAAK,IAAI,yBAAyB,CAAC;QAC/C,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,KAAK,OAAO,cAAc,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IAC1F,CAAC;IACD,OAAO;QACN,GAAG;QACH,KAAK,EAAE,UAAU,KAAK,KAAK,MAAM,CAAC,MAAM,IAAI,cAAc,GAAG;QAC7D,OAAO,EAAE,GAAG,MAAM,CAAC,MAAM,0BAA0B,KAAK,EAAE;QAC1D,KAAK,EAAE,IAAI;QACX,MAAM;QACN,QAAQ;KACR,CAAC;AAAA,CACF;AAED,KAAK,UAAU,kBAAkB,CAChC,QAAgB,EAAE,UAAoB,EACoB;IAC1D,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,gBAAgB,EAAE,CAAC;IACvD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7D,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QACnD,IAAI,OAAO,IAAI,KAAK;YAAE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;QACpD,OAAO,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;IAAA,CACjD,CAAC,CAAC,CAAC;IACJ,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAmB,EAAE,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;IACnE,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAA0B,EAAE,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC;IAC7E,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AAAA,CACtF;AAED,KAAK,UAAU,aAAa,CAAC,GAAW,EAA8K;IACrN,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACpD,IAAI,CAAC;QACJ,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,gBAAgB,EAAE,CAAC;QACjD,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;IACnC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;IACjD,CAAC;AAAA,CACD;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CACnC,GAAW,EACX,MAAoB,EACpB,OAAwB,EACI;IAC5B,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;QACrB,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAC1D,CAAC;IAED,IAAI,OAAO,EAAE,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;QAC3C,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;QAClC,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACxC,MAAM,EAAE,GAAG,MAAM,kBAAkB,EAAE,CAAC;YACtC,MAAM,UAAU,GAAG,MAAM,EAAE,CAAC,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACjE,IAAI,OAAO,IAAI,UAAU,EAAE,CAAC;gBAC3B,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,CAAC;YACrF,CAAC;YACD,IAAI,UAAU,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;gBAClC,MAAM,KAAK,GAAG,iEAAiE,CAAC;gBAChF,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YACxD,CAAC;YACD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YAC5C,MAAM,UAAU,GAAG,sBAAsB,CAAC,CAAC,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;YAC9D,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,oBAAoB,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;YACrF,MAAM,KAAK,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1D,OAAO,gBAAgB,CAAC,GAAG,EAAE,KAAK,EAAE,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC1G,CAAC;QAED,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;QAC5C,IAAI,UAAU,CAAC,KAAK,EAAE,CAAC;YACtB,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,CAAC;QACjE,CAAC;QACD,IAAI,UAAU,CAAC,IAAI,EAAE,CAAC;YACrB,MAAM,EAAE,qBAAqB,EAAE,GAAG,MAAM,gBAAgB,EAAE,CAAC;YAC3D,MAAM,cAAc,GAAG,MAAM,qBAAqB,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACjF,IAAI,OAAO,cAAc,KAAK,QAAQ,EAAE,CAAC;gBACxC,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,cAAc,CAAC,KAAK,EAAE,CAAC;YAC7F,CAAC;YACD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;YACvC,MAAM,UAAU,GAAG,sBAAsB,CAAC,CAAC,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;YAC9D,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;YAClF,MAAM,KAAK,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1D,OAAO,gBAAgB,CAAC,GAAG,EAAE,KAAK,EAAE,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;QACrG,CAAC;QAED,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,gEAAgE,EAAE,CAAC;IACjH,CAAC;IAED,IAAI,OAAO,EAAE,SAAS,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,kBAAkB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACnD,IAAI,CAAC,IAAI,EAAE,CAAC;YACX,OAAO;gBACN,GAAG;gBACH,KAAK,EAAE,EAAE;gBACT,OAAO,EAAE,EAAE;gBACX,KAAK,EAAE,8BAA8B,OAAO,CAAC,SAAS,kDAAkD;aACxG,CAAC;QACH,CAAC;QAED,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;QAClC,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACxC,MAAM,EAAE,GAAG,MAAM,kBAAkB,EAAE,CAAC;YACtC,MAAM,UAAU,GAAG,MAAM,EAAE,CAAC,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACjE,IAAI,OAAO,IAAI,UAAU,EAAE,CAAC;gBAC3B,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBAC3B,MAAM,KAAK,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;oBACxE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,KAAK,EAAE,EAAE,OAAO,EAAE,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,CAAC;gBAC9F,CAAC;gBACD,IAAI,UAAU,EAAE,CAAC;oBAChB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,GAAG,CAAC,UAAU,GAAG,CAAC,CAAC,GAAG,kBAAkB,CAAC;oBACjE,MAAM,KAAK,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;oBACrE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,KAAK,EAAE,EAAE,OAAO,EAAE,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,CAAC;gBAC9F,CAAC;gBACD,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,OAAO,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,CAAC;YAC5G,CAAC;YAED,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;gBAC3B,MAAM,KAAK,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxE,IAAI,UAAU,CAAC,QAAQ,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,GAAG,UAAU,CAAC,QAAQ,EAAE,CAAC;oBACpE,MAAM,KAAK,GAAG,aAAa,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,4BAA4B,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC;oBAChI,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;gBACjE,CAAC;gBACD,MAAM,UAAU,GAAG,UAAU;oBAC5B,CAAC,CAAC,sBAAsB,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC;oBAC1D,CAAC,CAAC,sBAAsB,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;gBAChD,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,oBAAoB,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;gBACrF,OAAO,gBAAgB,CAAC,GAAG,EAAE,KAAK,EAAE,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,IAAI,SAAS,CAAC,CAAC;YACnH,CAAC;YAED,IAAI,UAAU,EAAE,CAAC;gBAChB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,GAAG,CAAC,UAAU,GAAG,CAAC,CAAC,GAAG,kBAAkB,CAAC;gBACjE,MAAM,KAAK,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;gBACrE,IAAI,UAAU,CAAC,QAAQ,KAAK,IAAI,IAAI,GAAG,GAAG,UAAU,CAAC,QAAQ,EAAE,CAAC;oBAC/D,MAAM,KAAK,GAAG,aAAa,aAAa,CAAC,GAAG,CAAC,4BAA4B,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC;oBAC3H,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;gBACjE,CAAC;gBACD,MAAM,UAAU,GAAG,sBAAsB,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;gBACzE,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,oBAAoB,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;gBACrF,OAAO,gBAAgB,CAAC,GAAG,EAAE,KAAK,EAAE,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,IAAI,SAAS,CAAC,CAAC;YACnH,CAAC;YAED,IAAI,UAAU,CAAC,QAAQ,KAAK,IAAI,IAAI,IAAI,CAAC,OAAO,GAAG,UAAU,CAAC,QAAQ,EAAE,CAAC;gBACxE,MAAM,KAAK,GAAG,aAAa,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,4BAA4B,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC;gBACpI,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,OAAO,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YAC/E,CAAC;YACD,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,mBAAmB,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;YACrF,IAAI,OAAO,IAAI,KAAK,EAAE,CAAC;gBACtB,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,OAAO,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;YAClG,CAAC;YACD,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,OAAO,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,kBAAkB,OAAO,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QACvI,CAAC;QAED,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;QAC5C,IAAI,UAAU,CAAC,KAAK,EAAE,CAAC;YACtB,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,CAAC;QACjE,CAAC;QACD,IAAI,UAAU,CAAC,IAAI,EAAE,CAAC;YACrB,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;gBAC3B,MAAM,UAAU,GAAG,UAAU;oBAC5B,CAAC,CAAC,sBAAsB,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC;oBAC1D,CAAC,CAAC,sBAAsB,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;gBAChD,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;gBAClF,MAAM,KAAK,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxE,OAAO,gBAAgB,CAAC,GAAG,EAAE,KAAK,EAAE,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;YACrF,CAAC;YAED,IAAI,UAAU,EAAE,CAAC;gBAChB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,GAAG,CAAC,UAAU,GAAG,CAAC,CAAC,GAAG,kBAAkB,CAAC;gBACjE,MAAM,UAAU,GAAG,sBAAsB,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;gBACzE,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;gBAClF,MAAM,KAAK,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;gBACrE,OAAO,gBAAgB,CAAC,GAAG,EAAE,KAAK,EAAE,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;YACrF,CAAC;YAED,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,gBAAgB,EAAE,CAAC;YACvD,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YAClF,IAAI,OAAO,IAAI,KAAK,EAAE,CAAC;gBACtB,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,OAAO,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;YAClG,CAAC;YACD,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,OAAO,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,kBAAkB,OAAO,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QACvI,CAAC;QAED,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,oEAAoE,EAAE,CAAC;IACrH,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;IAC5C,IAAI,UAAU,CAAC,KAAK,EAAE,CAAC;QACtB,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,CAAC;IACjE,CAAC;IACD,IAAI,UAAU,CAAC,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC;YACJ,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,gBAAgB,EAAE,CAAC;YAClD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;YACpE,IAAI,MAAM,EAAE,OAAO;gBAAE,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;YAC/C,OAAO,MAAM,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,+IAA+I,sBAAsB,EAAE,EAAE,CAAC;QAClO,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,IAAI,YAAY,CAAC,GAAG,CAAC;gBAAE,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;YACjD,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;QAClE,CAAC;IACF,CAAC;IAED,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACjE,MAAM,iBAAiB,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,mBAAmB,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QAClG,CAAC;IACF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;IAClE,CAAC;IAED,IAAI,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7B,IAAI,CAAC;YACJ,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,iBAAiB,EAAE,CAAC;YACpD,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;YACvE,IAAI,QAAQ;gBAAE,OAAO,QAAQ,CAAC;YAC9B,IAAI,MAAM,EAAE,OAAO;gBAAE,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;QAChD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;YAClC,IAAI,YAAY,CAAC,GAAG,CAAC;gBAAE,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;YACjD,IAAI,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC7B,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;YACxD,CAAC;QACF,CAAC;IACF,CAAC;IAED,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACtB,IAAI,cAAc,GAAG,KAAK,CAAC;QAC3B,IAAI,EAAE,CAAC;QACP,IAAI,CAAC;YACJ,EAAE,GAAG,MAAM,kBAAkB,EAAE,CAAC;YAChC,cAAc,GAAG,EAAE,CAAC,gBAAgB,EAAE,CAAC;QACxC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;QAClE,CAAC;QACD,IAAI,cAAc,EAAE,CAAC;YACpB,IAAI,CAAC;gBACJ,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;gBACvF,IAAI,QAAQ;oBAAE,OAAO,QAAQ,CAAC;gBAC9B,IAAI,MAAM,EAAE,OAAO;oBAAE,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;YAChD,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;gBAClC,IAAI,YAAY,CAAC,GAAG,CAAC;oBAAE,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;gBACjD,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;YACxD,CAAC;YACD,OAAO;gBACN,GAAG;gBACH,KAAK,EAAE,EAAE;gBACT,OAAO,EAAE,EAAE;gBACX,KAAK,EAAE,kHAAkH;aACzH,CAAC;QACH,CAAC;IACF,CAAC;IAED,IAAI,MAAM,EAAE,OAAO;QAAE,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;IAE/C,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAE9D,IAAI,MAAM,EAAE,OAAO;QAAE,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;IAC/C,IAAI,CAAC,UAAU,CAAC,KAAK;QAAE,OAAO,UAAU,CAAC;IACzC,IAAI,sBAAsB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,KAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAAE,OAAO,UAAU,CAAC;IAEnG,MAAM,UAAU,GAAG,MAAM,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC7E,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC;IAClC,IAAI,MAAM,EAAE,OAAO;QAAE,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;IAE/C,IAAI,aAAa,GAAkB,IAAI,CAAC;IACxC,IAAI,CAAC;QACJ,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;QACtC,IAAI,QAAQ,CAAC,mBAAmB,EAAE,EAAE,CAAC;YACpC,MAAM,cAAc,GAAG,MAAM,QAAQ,CAAC,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;YAChF,IAAI,cAAc;gBAAE,OAAO,cAAc,CAAC;QAC3C,CAAC;IACF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,IAAI,YAAY,CAAC,GAAG,CAAC;YAAE,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;QACjD,aAAa,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7B,OAAO,EAAE,GAAG,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;QAChD,CAAC;IACF,CAAC;IACD,IAAI,MAAM,EAAE,OAAO;QAAE,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;IAE/C,IAAI,YAAY,GAA4B,IAAI,CAAC;IACjD,IAAI,CAAC;QACJ,MAAM,SAAS,GAAG,MAAM,oBAAoB,EAAE,CAAC;QAC/C,YAAY,GAAG,MAAM,SAAS,CAAC,qBAAqB,CAAC,GAAG,EAAE,MAAM,CAAC;eAC7D,MAAM,SAAS,CAAC,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACvD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,IAAI,YAAY,CAAC,GAAG,CAAC;YAAE,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;QACjD,IAAI,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7B,OAAO,EAAE,GAAG,UAAU,EAAE,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;QACpD,CAAC;IACF,CAAC;IAED,IAAI,YAAY;QAAE,OAAO,YAAY,CAAC;IACtC,IAAI,MAAM,EAAE,OAAO;QAAE,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;IAE/C,MAAM,QAAQ,GAAG;QAChB,UAAU,CAAC,KAAK;QAChB,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,6BAA6B,aAAa,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACxE,EAAE;QACF,mBAAmB;QACnB,oCAAoC,sBAAsB,EAAE;QAC5D,kCAAkC,sBAAsB,EAAE;QAC1D,gDAAgD;QAChD,0DAA0D;KAC1D,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,OAAO,EAAE,GAAG,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;AAAA,CAC1C;AAED,SAAS,kBAAkB,CAAC,IAAY,EAAW;IAClD,uBAAuB;IACvB,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;IAC/D,IAAI,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAE7B,MAAM,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;IAE9B,iCAAiC;IACjC,MAAM,WAAW,GAAG,QAAQ;SAC1B,OAAO,CAAC,6BAA6B,EAAE,EAAE,CAAC;SAC1C,OAAO,CAAC,2BAA2B,EAAE,EAAE,CAAC;SACxC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;IAET,gBAAgB;IAChB,MAAM,WAAW,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;IAE3D,wEAAwE;IACxE,OAAO,WAAW,CAAC,MAAM,GAAG,GAAG,IAAI,WAAW,GAAG,CAAC,CAAC;AAAA,CACnD;AAED,KAAK,UAAU,cAAc,CAC5B,GAAW,EACX,MAAoB,EACpB,OAAwB,EACI;IAC5B,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,IAAI,kBAAkB,CAAC;IAC3D,MAAM,UAAU,GAAG,eAAe,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;IAEpE,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;IAElE,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IACzC,IAAI,MAAM,EAAE,OAAO;QAAE,UAAU,CAAC,KAAK,EAAE,CAAC;IACxC,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAE3C,IAAI,CAAC;QACJ,MAAM,QAAQ,GAAG,MAAM,cAAc,CACpC,GAAG,EACH;YACC,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,OAAO,EAAE;gBACR,YAAY,EAAE,uHAAuH;gBACrI,QAAQ,EAAE,kGAAkG;gBAC5G,iBAAiB,EAAE,gBAAgB;gBACnC,eAAe,EAAE,UAAU;gBAC3B,gBAAgB,EAAE,UAAU;gBAC5B,gBAAgB,EAAE,UAAU;gBAC5B,gBAAgB,EAAE,MAAM;gBACxB,gBAAgB,EAAE,IAAI;gBACtB,2BAA2B,EAAE,GAAG;aAChC;SACD,EACD,EAAE,WAAW,EAAE,mBAAmB,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAC/D,CAAC;QAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YAClB,eAAe,CAAC,WAAW,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;YACzD,OAAO;gBACN,GAAG;gBACH,KAAK,EAAE,EAAE;gBACT,OAAO,EAAE,EAAE;gBACX,KAAK,EAAE,QAAQ,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,UAAU,EAAE;aACxD,CAAC;QACH,CAAC;QAED,MAAM,mBAAmB,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QACnE,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;QAC/D,MAAM,YAAY,GAAG,KAAK,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAC7C,MAAM,eAAe,GAAG,YAAY,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;QAC1E,IAAI,mBAAmB,EAAE,CAAC;YACzB,MAAM,aAAa,GAAG,QAAQ,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC;YACxD,IAAI,aAAa,GAAG,eAAe,EAAE,CAAC;gBACrC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;gBAC9B,eAAe,CAAC,WAAW,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;gBACzD,OAAO;oBACN,GAAG;oBACH,KAAK,EAAE,EAAE;oBACT,OAAO,EAAE,EAAE;oBACX,KAAK,EAAE,uBAAuB,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,IAAI,GAAG,IAAI,CAAC,KAAK;iBAC1E,CAAC;YACH,CAAC;QACF,CAAC;QAED,IAAI,YAAY,EAAE,CAAC;YAClB,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,QAAQ,EAAE,eAAe,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;YAClF,IAAI,CAAC;gBACJ,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;gBAC5B,MAAM,EAAE,oBAAoB,EAAE,GAAG,MAAM,cAAc,EAAE,CAAC;gBACxD,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;gBACvD,eAAe,CAAC,WAAW,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;gBACzD,OAAO;oBACN,GAAG;oBACH,KAAK,EAAE,MAAM,CAAC,KAAK;oBACnB,OAAO,EAAE,+BAA+B,MAAM,CAAC,UAAU,cAAc,MAAM,CAAC,KAAK,iBAAiB,MAAM,CAAC,KAAK,EAAE;oBAClH,KAAK,EAAE,IAAI;iBACX,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACjE,eAAe,CAAC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;gBAC9C,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,0BAA0B,OAAO,EAAE,EAAE,CAAC;YACpF,CAAC;QACF,CAAC;QAED,IAAI,WAAW,CAAC,QAAQ,CAAC,0BAA0B,CAAC;YACnD,WAAW,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAC9B,WAAW,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAC9B,WAAW,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAC9B,WAAW,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;YAC1C,eAAe,CAAC,WAAW,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;YACzD,OAAO;gBACN,GAAG;gBACH,KAAK,EAAE,EAAE;gBACT,OAAO,EAAE,EAAE;gBACX,KAAK,EAAE,6BAA6B,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE;aAC/D,CAAC;QACH,CAAC;QAED,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,eAAe,CAAC,QAAQ,EAAE,eAAe,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QAC3G,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,WAAW,CAAC,QAAQ,CAAC,uBAAuB,CAAC,CAAC;QAElG,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,eAAe,CAAC,WAAW,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;YACzD,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAC1C,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QACnD,CAAC;QAED,MAAM,EAAE,QAAQ,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,QAA+B,CAAC,CAAC;QAChE,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC;QAE/B,IAAI,CAAC,OAAO,EAAE,CAAC;YACd,MAAM,SAAS,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAC1C,IAAI,SAAS,EAAE,CAAC;gBACf,eAAe,CAAC,WAAW,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;gBACzD,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;YACjF,CAAC;YAED,eAAe,CAAC,WAAW,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;YAEzD,sCAAsC;YACtC,MAAM,UAAU,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;YAC5C,MAAM,QAAQ,GAAG,UAAU;gBAC1B,CAAC,CAAC,oEAAoE;gBACtE,CAAC,CAAC,wDAAwD,CAAC;YAE5D,OAAO;gBACN,GAAG;gBACH,KAAK,EAAE,EAAE;gBACT,OAAO,EAAE,EAAE;gBACX,KAAK,EAAE,QAAQ;aACf,CAAC;QACH,CAAC;QAED,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACpD,eAAe,CAAC,WAAW,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QAEzD,IAAI,QAAQ,CAAC,MAAM,GAAG,kBAAkB,EAAE,CAAC;YAC1C,OAAO;gBACN,GAAG;gBACH,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE;gBAC1B,OAAO,EAAE,QAAQ;gBACjB,KAAK,EAAE,kBAAkB,CAAC,IAAI,CAAC;oBAC9B,CAAC,CAAC,oEAAoE;oBACtE,CAAC,CAAC,sCAAsC;aACzC,CAAC;QACH,CAAC;QAED,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAC5E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7C,eAAe,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;QAC5C,CAAC;aAAM,CAAC;YACP,eAAe,CAAC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC/C,CAAC;QACD,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IACxD,CAAC;YAAS,CAAC;QACV,YAAY,CAAC,SAAS,CAAC,CAAC;QACxB,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;AAAA,CACD;AAED,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAiB;IAChE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAC5C,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACpD,OAAO,OAAO,IAAI,IAAI,CAAC;AAAA,CACvB;AAED,SAAS,gBAAgB,CAAC,IAAY,EAAE,GAAW,EAAU;IAC5D,OAAO,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,GAAG,CAAC,CAAC;AAAA,CACpF;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACpC,IAAc,EACd,MAAoB,EACpB,OAAwB,EACM;IAC9B,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AAAA,CAC9F","sourcesContent":["// @ts-nocheck\nimport { Readability } from \"@mozilla/readability\";\nimport { parseHTML } from \"linkedom\";\nimport TurndownService from \"turndown\";\nimport pLimit from \"p-limit\";\nimport { readBoundedBody } from \"./bounded-body.ts\";\nimport { activityMonitor } from \"./activity.ts\";\nimport { extractRSCContent } from \"./rsc-extract.ts\";\nimport {\n\tloadGeminiUrlContext,\n\tloadGithubExtract,\n\tloadParallel,\n\tloadPdfExtract,\n\tloadVideoExtract,\n\tloadYoutubeExtract,\n} from \"./lazy.ts\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { fetchRemoteUrl, validateRemoteUrl, type Lookup } from \"./ssrf-protection.ts\";\nimport { formatSeconds, getWebSearchConfigPath } from \"./utils.ts\";\n\nconst YOUTUBE_URL_RE =\n\t/(?:(?:www\\.|m\\.)?youtube\\.com\\/(?:watch\\?.*v=|shorts\\/|live\\/|embed\\/|v\\/)|youtu\\.be\\/)([a-zA-Z0-9_-]{11})/;\n\nfunction isYouTubeURL(url: string): { isYouTube: boolean; videoId: string | null } {\n\ttry {\n\t\tconst parsed = new URL(url);\n\t\tif (parsed.pathname === \"/playlist\") {\n\t\t\treturn { isYouTube: false, videoId: null };\n\t\t}\n\t} catch {\n\t}\n\tconst match = url.match(YOUTUBE_URL_RE);\n\tif (!match) return { isYouTube: false, videoId: null };\n\treturn { isYouTube: true, videoId: match[1] };\n}\n\nfunction isPDF(url: string, contentType?: string): boolean {\n\tif (contentType?.includes(\"application/pdf\")) return true;\n\ttry {\n\t\treturn new URL(url).pathname.toLowerCase().endsWith(\".pdf\");\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction looksLikeLocalPath(input: string): boolean {\n\treturn input.startsWith(\"/\") || input.startsWith(\"./\") || input.startsWith(\"../\") || input.startsWith(\"file://\");\n}\n\nfunction looksLikeGitHubUrl(url: string): boolean {\n\ttry {\n\t\tconst host = new URL(url).hostname.toLowerCase();\n\t\treturn host === \"github.com\" || host === \"www.github.com\" || host.endsWith(\".github.com\");\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nconst DEFAULT_TIMEOUT_MS = 30000;\nconst CONCURRENT_LIMIT = 3;\n\nconst NON_RECOVERABLE_ERRORS = [\"Unsupported content type\", \"Response too large\"];\nconst MIN_USEFUL_CONTENT = 500;\nconst WEB_SEARCH_CONFIG_PATH = getWebSearchConfigPath();\n\n/**\n * Read `ssrf.allowRanges` (CIDR strings) from web-search.json. Returns [] when\n * the file is missing, unreadable, or the key is unset so SSRF protection stays\n * fully on by default. Throws when `ssrf.allowRanges` is present but not an array\n * so a mistyped value (e.g. a bare string instead of a JSON array) fails loudly\n * instead of being silently ignored. Exempts synthetic ranges used by TUN/fake-IP\n * proxies (e.g. 198.18.0.0/15).\n */\nexport function loadSsrfAllowRanges(): string[] {\n\tlet value: unknown;\n\ttry {\n\t\tif (!existsSync(WEB_SEARCH_CONFIG_PATH)) return [];\n\t\tconst raw = readFileSync(WEB_SEARCH_CONFIG_PATH, \"utf-8\");\n\t\tvalue = (JSON.parse(raw) as { ssrf?: { allowRanges?: unknown } })?.ssrf?.allowRanges;\n\t} catch {\n\t\t// Missing/unreadable file or invalid JSON: fail safe with SSRF fully on.\n\t\treturn [];\n\t}\n\tif (value === undefined || value === null) return [];\n\tif (!Array.isArray(value)) {\n\t\tthrow new Error(`ssrf.allowRanges in ${WEB_SEARCH_CONFIG_PATH} must be an array of CIDR strings`);\n\t}\n\tconst ranges: string[] = [];\n\tfor (const [index, entry] of value.entries()) {\n\t\tif (typeof entry !== \"string\") {\n\t\t\tthrow new Error(`ssrf.allowRanges in ${WEB_SEARCH_CONFIG_PATH} must contain only CIDR strings; entry ${index + 1} is ${typeof entry}`);\n\t\t}\n\t\tconst trimmed = entry.trim();\n\t\tif (trimmed) ranges.push(trimmed);\n\t}\n\treturn ranges;\n}\n\nfunction errorMessage(err: unknown): string {\n\treturn err instanceof Error ? err.message : String(err);\n}\n\nfunction isConfigParseError(err: unknown): boolean {\n\treturn errorMessage(err).startsWith(\"Failed to parse \");\n}\n\nfunction isAbortError(err: unknown): boolean {\n\treturn errorMessage(err).toLowerCase().includes(\"abort\");\n}\n\nfunction abortedResult(url: string): ExtractedContent {\n\treturn { url, title: \"\", content: \"\", error: \"Aborted\" };\n}\n\nconst turndown = new TurndownService({\n\theadingStyle: \"atx\",\n\tcodeBlockStyle: \"fenced\",\n});\n\nconst fetchLimit = pLimit(CONCURRENT_LIMIT);\n\nexport interface VideoFrame {\n\tdata: string;\n\tmimeType: string;\n\ttimestamp: string;\n}\n\nexport type FrameData = { data: string; mimeType: string };\nexport type FrameResult = FrameData | { error: string };\n\nexport interface ExtractedContent {\n\turl: string;\n\ttitle: string;\n\tcontent: string;\n\terror: string | null;\n\tthumbnail?: { data: string; mimeType: string };\n\tframes?: VideoFrame[];\n\tduration?: number;\n}\n\nexport interface ExtractOptions {\n\ttimeoutMs?: number;\n\tforceClone?: boolean;\n\tprompt?: string;\n\ttimestamp?: string;\n\tframes?: number;\n\tmodel?: string;\n\t/** Custom DNS resolver used for SSRF validation. Primarily a test seam. */\n\tlookup?: Lookup;\n}\n\nconst JINA_READER_BASE = \"https://r.jina.ai/\";\nconst JINA_TIMEOUT_MS = 30000;\n\nasync function extractWithJinaReader(\n\turl: string,\n\tsignal?: AbortSignal,\n\tlookup?: Lookup,\n): Promise<ExtractedContent | null> {\n\tconst jinaUrl = JINA_READER_BASE + url;\n\n\tconst activityId = activityMonitor.logStart({ type: \"api\", query: `jina: ${url}` });\n\n\ttry {\n\t\tawait validateRemoteUrl(url, { allowRanges: loadSsrfAllowRanges(), lookup });\n\t\tconst res = await fetch(jinaUrl, {\n\t\t\theaders: {\n\t\t\t\t\"Accept\": \"text/markdown\",\n\t\t\t\t\"X-No-Cache\": \"true\",\n\t\t\t},\n\t\t\tsignal: AbortSignal.any([\n\t\t\t\tAbortSignal.timeout(JINA_TIMEOUT_MS),\n\t\t\t\t...(signal ? [signal] : []),\n\t\t\t]),\n\t\t});\n\n\t\tif (!res.ok) {\n\t\t\tactivityMonitor.logComplete(activityId, res.status);\n\t\t\treturn null;\n\t\t}\n\n\t\tconst content = await res.text();\n\t\tactivityMonitor.logComplete(activityId, res.status);\n\n\t\tconst contentStart = content.indexOf(\"Markdown Content:\");\n\t\tif (contentStart < 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst markdownPart = content.slice(contentStart + 17).trim(); // 17 = \"Markdown Content:\".length\n\n\t\t// Check for failed JS rendering or minimal content\n\t\tif (markdownPart.length < 100 ||\n\t\t\tmarkdownPart.startsWith(\"Loading...\") ||\n\t\t\tmarkdownPart.startsWith(\"Please enable JavaScript\")) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst title = extractHeadingTitle(markdownPart) ?? (new URL(url).pathname.split(\"/\").pop() || url);\n\t\treturn { url, title, content: markdownPart, error: null };\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tif (message.toLowerCase().includes(\"abort\")) {\n\t\t\tactivityMonitor.logComplete(activityId, 0);\n\t\t} else {\n\t\t\tactivityMonitor.logError(activityId, message);\n\t\t}\n\t\treturn null;\n\t}\n}\n\nfunction parseTimestamp(ts: string): number | null {\n\tconst num = Number(ts);\n\tif (!isNaN(num) && num >= 0) return Math.floor(num);\n\tconst parts = ts.split(\":\").map(Number);\n\tif (parts.some(p => isNaN(p) || p < 0)) return null;\n\tif (parts.length === 3) return Math.floor(parts[0] * 3600 + parts[1] * 60 + parts[2]);\n\tif (parts.length === 2) return Math.floor(parts[0] * 60 + parts[1]);\n\treturn null;\n}\n\ntype TimestampSpec = { type: \"single\"; seconds: number } | { type: \"range\"; start: number; end: number };\n\nfunction parseTimestampSpec(ts: string): TimestampSpec | null {\n\tconst dashIdx = ts.indexOf(\"-\", 1);\n\tif (dashIdx > 0) {\n\t\tconst start = parseTimestamp(ts.slice(0, dashIdx));\n\t\tconst end = parseTimestamp(ts.slice(dashIdx + 1));\n\t\tif (start !== null && end !== null && end > start) return { type: \"range\", start, end };\n\t}\n\tconst seconds = parseTimestamp(ts);\n\treturn seconds !== null ? { type: \"single\", seconds } : null;\n}\n\nconst DEFAULT_RANGE_FRAMES = 6;\nconst MIN_FRAME_INTERVAL = 5;\n\nfunction computeRangeTimestamps(start: number, end: number, maxFrames: number = DEFAULT_RANGE_FRAMES): number[] {\n\tif (maxFrames <= 1) return [start];\n\tconst duration = end - start;\n\tconst idealInterval = duration / (maxFrames - 1);\n\tif (idealInterval < MIN_FRAME_INTERVAL) {\n\t\tconst timestamps: number[] = [];\n\t\tfor (let t = start; t <= end && timestamps.length < maxFrames; t += MIN_FRAME_INTERVAL) {\n\t\t\ttimestamps.push(t);\n\t\t}\n\t\treturn timestamps;\n\t}\n\treturn Array.from({ length: maxFrames }, (_, i) => Math.round(start + i * idealInterval));\n}\n\nfunction buildFrameResult(\n\turl: string, label: string, requestedCount: number,\n\tframes: VideoFrame[], error: string | null, duration?: number,\n): ExtractedContent {\n\tif (frames.length === 0) {\n\t\tconst msg = error ?? \"Frame extraction failed\";\n\t\treturn { url, title: `Frames ${label} (0/${requestedCount})`, content: msg, error: msg };\n\t}\n\treturn {\n\t\turl,\n\t\ttitle: `Frames ${label} (${frames.length}/${requestedCount})`,\n\t\tcontent: `${frames.length} frames extracted from ${label}`,\n\t\terror: null,\n\t\tframes,\n\t\tduration,\n\t};\n}\n\nasync function extractLocalFrames(\n\tfilePath: string, timestamps: number[],\n): Promise<{ frames: VideoFrame[]; error: string | null }> {\n\tconst { extractVideoFrame } = await loadVideoExtract();\n\tconst results = await Promise.all(timestamps.map(async (t) => {\n\t\tconst frame = await extractVideoFrame(filePath, t);\n\t\tif (\"error\" in frame) return { error: frame.error };\n\t\treturn { ...frame, timestamp: formatSeconds(t) };\n\t}));\n\tconst frames = results.filter((f): f is VideoFrame => \"data\" in f);\n\tconst firstError = results.find((f): f is { error: string } => \"error\" in f);\n\treturn { frames, error: frames.length === 0 && firstError ? firstError.error : null };\n}\n\nasync function safeVideoInfo(url: string): Promise<{ info: Awaited<ReturnType<typeof loadVideoExtract>> extends { isVideoFile: infer F } ? (F extends (...args: any) => infer R ? R : null) : null; error?: string }> {\n\tif (!looksLikeLocalPath(url)) return { info: null };\n\ttry {\n\t\tconst { isVideoFile } = await loadVideoExtract();\n\t\treturn { info: isVideoFile(url) };\n\t} catch (err) {\n\t\treturn { info: null, error: errorMessage(err) };\n\t}\n}\n\nexport async function extractContent(\n\turl: string,\n\tsignal?: AbortSignal,\n\toptions?: ExtractOptions,\n): Promise<ExtractedContent> {\n\tif (signal?.aborted) {\n\t\treturn { url, title: \"\", content: \"\", error: \"Aborted\" };\n\t}\n\n\tif (options?.frames && !options.timestamp) {\n\t\tconst frameCount = options.frames;\n\t\tconst ytInfo = isYouTubeURL(url);\n\t\tif (ytInfo.isYouTube && ytInfo.videoId) {\n\t\t\tconst yt = await loadYoutubeExtract();\n\t\t\tconst streamInfo = await yt.getYouTubeStreamInfo(ytInfo.videoId);\n\t\t\tif (\"error\" in streamInfo) {\n\t\t\t\treturn { url, title: \"Frames\", content: streamInfo.error, error: streamInfo.error };\n\t\t\t}\n\t\t\tif (streamInfo.duration === null) {\n\t\t\t\tconst error = \"Cannot determine video duration. Use a timestamp range instead.\";\n\t\t\t\treturn { url, title: \"Frames\", content: error, error };\n\t\t\t}\n\t\t\tconst dur = Math.floor(streamInfo.duration);\n\t\t\tconst timestamps = computeRangeTimestamps(0, dur, frameCount);\n\t\t\tconst result = await yt.extractYouTubeFrames(ytInfo.videoId, timestamps, streamInfo);\n\t\t\tconst label = `${formatSeconds(0)}-${formatSeconds(dur)}`;\n\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error, streamInfo.duration);\n\t\t}\n\n\t\tconst localVideo = await safeVideoInfo(url);\n\t\tif (localVideo.error) {\n\t\t\treturn { url, title: \"\", content: \"\", error: localVideo.error };\n\t\t}\n\t\tif (localVideo.info) {\n\t\t\tconst { getLocalVideoDuration } = await loadVideoExtract();\n\t\t\tconst durationResult = await getLocalVideoDuration(localVideo.info.absolutePath);\n\t\t\tif (typeof durationResult !== \"number\") {\n\t\t\t\treturn { url, title: \"Frames\", content: durationResult.error, error: durationResult.error };\n\t\t\t}\n\t\t\tconst dur = Math.floor(durationResult);\n\t\t\tconst timestamps = computeRangeTimestamps(0, dur, frameCount);\n\t\t\tconst result = await extractLocalFrames(localVideo.info.absolutePath, timestamps);\n\t\t\tconst label = `${formatSeconds(0)}-${formatSeconds(dur)}`;\n\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error, durationResult);\n\t\t}\n\n\t\treturn { url, title: \"\", content: \"\", error: \"Frame extraction only works with YouTube and local video files\" };\n\t}\n\n\tif (options?.timestamp) {\n\t\tconst spec = parseTimestampSpec(options.timestamp);\n\t\tif (!spec) {\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: \"\",\n\t\t\t\tcontent: \"\",\n\t\t\t\terror: `Invalid timestamp format: \"${options.timestamp}\". Use \"H:MM:SS\", \"MM:SS\", \"85\", or \"start-end\".`,\n\t\t\t};\n\t\t}\n\n\t\tconst frameCount = options.frames;\n\t\tconst ytInfo = isYouTubeURL(url);\n\t\tif (ytInfo.isYouTube && ytInfo.videoId) {\n\t\t\tconst yt = await loadYoutubeExtract();\n\t\t\tconst streamInfo = await yt.getYouTubeStreamInfo(ytInfo.videoId);\n\t\t\tif (\"error\" in streamInfo) {\n\t\t\t\tif (spec.type === \"range\") {\n\t\t\t\t\tconst label = `${formatSeconds(spec.start)}-${formatSeconds(spec.end)}`;\n\t\t\t\t\treturn { url, title: `Frames ${label}`, content: streamInfo.error, error: streamInfo.error };\n\t\t\t\t}\n\t\t\t\tif (frameCount) {\n\t\t\t\t\tconst end = spec.seconds + (frameCount - 1) * MIN_FRAME_INTERVAL;\n\t\t\t\t\tconst label = `${formatSeconds(spec.seconds)}-${formatSeconds(end)}`;\n\t\t\t\t\treturn { url, title: `Frames ${label}`, content: streamInfo.error, error: streamInfo.error };\n\t\t\t\t}\n\t\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: streamInfo.error, error: streamInfo.error };\n\t\t\t}\n\n\t\t\tif (spec.type === \"range\") {\n\t\t\t\tconst label = `${formatSeconds(spec.start)}-${formatSeconds(spec.end)}`;\n\t\t\t\tif (streamInfo.duration !== null && spec.end > streamInfo.duration) {\n\t\t\t\t\tconst error = `Timestamp ${formatSeconds(spec.end)} exceeds video duration (${formatSeconds(Math.floor(streamInfo.duration))})`;\n\t\t\t\t\treturn { url, title: `Frames ${label}`, content: error, error };\n\t\t\t\t}\n\t\t\t\tconst timestamps = frameCount\n\t\t\t\t\t? computeRangeTimestamps(spec.start, spec.end, frameCount)\n\t\t\t\t\t: computeRangeTimestamps(spec.start, spec.end);\n\t\t\t\tconst result = await yt.extractYouTubeFrames(ytInfo.videoId, timestamps, streamInfo);\n\t\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error, result.duration ?? undefined);\n\t\t\t}\n\n\t\t\tif (frameCount) {\n\t\t\t\tconst end = spec.seconds + (frameCount - 1) * MIN_FRAME_INTERVAL;\n\t\t\t\tconst label = `${formatSeconds(spec.seconds)}-${formatSeconds(end)}`;\n\t\t\t\tif (streamInfo.duration !== null && end > streamInfo.duration) {\n\t\t\t\t\tconst error = `Timestamp ${formatSeconds(end)} exceeds video duration (${formatSeconds(Math.floor(streamInfo.duration))})`;\n\t\t\t\t\treturn { url, title: `Frames ${label}`, content: error, error };\n\t\t\t\t}\n\t\t\t\tconst timestamps = computeRangeTimestamps(spec.seconds, end, frameCount);\n\t\t\t\tconst result = await yt.extractYouTubeFrames(ytInfo.videoId, timestamps, streamInfo);\n\t\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error, result.duration ?? undefined);\n\t\t\t}\n\n\t\t\tif (streamInfo.duration !== null && spec.seconds > streamInfo.duration) {\n\t\t\t\tconst error = `Timestamp ${formatSeconds(spec.seconds)} exceeds video duration (${formatSeconds(Math.floor(streamInfo.duration))})`;\n\t\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: error, error };\n\t\t\t}\n\t\t\tconst frame = await yt.extractYouTubeFrame(ytInfo.videoId, spec.seconds, streamInfo);\n\t\t\tif (\"error\" in frame) {\n\t\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: frame.error, error: frame.error };\n\t\t\t}\n\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: `Video frame at ${options.timestamp}`, error: null, thumbnail: frame };\n\t\t}\n\n\t\tconst localVideo = await safeVideoInfo(url);\n\t\tif (localVideo.error) {\n\t\t\treturn { url, title: \"\", content: \"\", error: localVideo.error };\n\t\t}\n\t\tif (localVideo.info) {\n\t\t\tif (spec.type === \"range\") {\n\t\t\t\tconst timestamps = frameCount\n\t\t\t\t\t? computeRangeTimestamps(spec.start, spec.end, frameCount)\n\t\t\t\t\t: computeRangeTimestamps(spec.start, spec.end);\n\t\t\t\tconst result = await extractLocalFrames(localVideo.info.absolutePath, timestamps);\n\t\t\t\tconst label = `${formatSeconds(spec.start)}-${formatSeconds(spec.end)}`;\n\t\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error);\n\t\t\t}\n\n\t\t\tif (frameCount) {\n\t\t\t\tconst end = spec.seconds + (frameCount - 1) * MIN_FRAME_INTERVAL;\n\t\t\t\tconst timestamps = computeRangeTimestamps(spec.seconds, end, frameCount);\n\t\t\t\tconst result = await extractLocalFrames(localVideo.info.absolutePath, timestamps);\n\t\t\t\tconst label = `${formatSeconds(spec.seconds)}-${formatSeconds(end)}`;\n\t\t\t\treturn buildFrameResult(url, label, timestamps.length, result.frames, result.error);\n\t\t\t}\n\n\t\t\tconst { extractVideoFrame } = await loadVideoExtract();\n\t\t\tconst frame = await extractVideoFrame(localVideo.info.absolutePath, spec.seconds);\n\t\t\tif (\"error\" in frame) {\n\t\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: frame.error, error: frame.error };\n\t\t\t}\n\t\t\treturn { url, title: `Frame at ${options.timestamp}`, content: `Video frame at ${options.timestamp}`, error: null, thumbnail: frame };\n\t\t}\n\n\t\treturn { url, title: \"\", content: \"\", error: \"Timestamp extraction only works with YouTube and local video files\" };\n\t}\n\n\tconst localVideo = await safeVideoInfo(url);\n\tif (localVideo.error) {\n\t\treturn { url, title: \"\", content: \"\", error: localVideo.error };\n\t}\n\tif (localVideo.info) {\n\t\ttry {\n\t\t\tconst { extractVideo } = await loadVideoExtract();\n\t\t\tconst result = await extractVideo(localVideo.info, signal, options);\n\t\t\tif (signal?.aborted) return abortedResult(url);\n\t\t\treturn result ?? { url, title: \"\", content: \"\", error: `Video analysis requires Gemini access. Either:\\n 1. Sign into gemini.google.com in Chrome (free, uses cookies)\\n 2. Set GEMINI_API_KEY in ${WEB_SEARCH_CONFIG_PATH}` };\n\t\t} catch (err) {\n\t\t\tif (isAbortError(err)) return abortedResult(url);\n\t\t\treturn { url, title: \"\", content: \"\", error: errorMessage(err) };\n\t\t}\n\t}\n\n\ttry {\n\t\tconst parsed = new URL(url);\n\t\tif (parsed.protocol === \"http:\" || parsed.protocol === \"https:\") {\n\t\t\tawait validateRemoteUrl(parsed, { allowRanges: loadSsrfAllowRanges(), lookup: options?.lookup });\n\t\t}\n\t} catch (err) {\n\t\treturn { url, title: \"\", content: \"\", error: errorMessage(err) };\n\t}\n\n\tif (looksLikeGitHubUrl(url)) {\n\t\ttry {\n\t\t\tconst { extractGitHub } = await loadGithubExtract();\n\t\t\tconst ghResult = await extractGitHub(url, signal, options?.forceClone);\n\t\t\tif (ghResult) return ghResult;\n\t\t\tif (signal?.aborted) return abortedResult(url);\n\t\t} catch (err) {\n\t\t\tconst message = errorMessage(err);\n\t\t\tif (isAbortError(err)) return abortedResult(url);\n\t\t\tif (isConfigParseError(err)) {\n\t\t\t\treturn { url, title: \"\", content: \"\", error: message };\n\t\t\t}\n\t\t}\n\t}\n\n\tconst ytInfo = isYouTubeURL(url);\n\tif (ytInfo.isYouTube) {\n\t\tlet youtubeEnabled = false;\n\t\tlet yt;\n\t\ttry {\n\t\t\tyt = await loadYoutubeExtract();\n\t\t\tyoutubeEnabled = yt.isYouTubeEnabled();\n\t\t} catch (err) {\n\t\t\treturn { url, title: \"\", content: \"\", error: errorMessage(err) };\n\t\t}\n\t\tif (youtubeEnabled) {\n\t\t\ttry {\n\t\t\t\tconst ytResult = await yt.extractYouTube(url, signal, options?.prompt, options?.model);\n\t\t\t\tif (ytResult) return ytResult;\n\t\t\t\tif (signal?.aborted) return abortedResult(url);\n\t\t\t} catch (err) {\n\t\t\t\tconst message = errorMessage(err);\n\t\t\t\tif (isAbortError(err)) return abortedResult(url);\n\t\t\t\treturn { url, title: \"\", content: \"\", error: message };\n\t\t\t}\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: \"\",\n\t\t\t\tcontent: \"\",\n\t\t\t\terror: \"Could not extract YouTube video content. Sign into Google in Chrome for automatic access, or set GEMINI_API_KEY.\",\n\t\t\t};\n\t\t}\n\t}\n\n\tif (signal?.aborted) return abortedResult(url);\n\n\tconst httpResult = await extractViaHttp(url, signal, options);\n\n\tif (signal?.aborted) return abortedResult(url);\n\tif (!httpResult.error) return httpResult;\n\tif (NON_RECOVERABLE_ERRORS.some(prefix => httpResult.error!.startsWith(prefix))) return httpResult;\n\n\tconst jinaResult = await extractWithJinaReader(url, signal, options?.lookup);\n\tif (jinaResult) return jinaResult;\n\tif (signal?.aborted) return abortedResult(url);\n\n\tlet parallelError: string | null = null;\n\ttry {\n\t\tconst parallel = await loadParallel();\n\t\tif (parallel.isParallelAvailable()) {\n\t\t\tconst parallelResult = await parallel.extractWithParallel(url, signal, options);\n\t\t\tif (parallelResult) return parallelResult;\n\t\t}\n\t} catch (err) {\n\t\tif (isAbortError(err)) return abortedResult(url);\n\t\tparallelError = errorMessage(err);\n\t\tif (isConfigParseError(err)) {\n\t\t\treturn { ...httpResult, error: parallelError };\n\t\t}\n\t}\n\tif (signal?.aborted) return abortedResult(url);\n\n\tlet geminiResult: ExtractedContent | null = null;\n\ttry {\n\t\tconst geminiUrl = await loadGeminiUrlContext();\n\t\tgeminiResult = await geminiUrl.extractWithUrlContext(url, signal)\n\t\t\t?? await geminiUrl.extractWithGeminiWeb(url, signal);\n\t} catch (err) {\n\t\tif (isAbortError(err)) return abortedResult(url);\n\t\tif (isConfigParseError(err)) {\n\t\t\treturn { ...httpResult, error: errorMessage(err) };\n\t\t}\n\t}\n\n\tif (geminiResult) return geminiResult;\n\tif (signal?.aborted) return abortedResult(url);\n\n\tconst guidance = [\n\t\thttpResult.error,\n\t\t...(parallelError ? [`Parallel fallback failed: ${parallelError}`] : []),\n\t\t\"\",\n\t\t\"Fallback options:\",\n\t\t` \\u2022 Set PARALLEL_API_KEY in ${WEB_SEARCH_CONFIG_PATH}`,\n\t\t` \\u2022 Set GEMINI_API_KEY in ${WEB_SEARCH_CONFIG_PATH}`,\n\t\t\" \\u2022 Sign into gemini.google.com in Chrome\",\n\t\t\" \\u2022 Use web_search to find content about this topic\",\n\t].join(\"\\n\");\n\treturn { ...httpResult, error: guidance };\n}\n\nfunction isLikelyJSRendered(html: string): boolean {\n\t// Extract body content\n\tconst bodyMatch = html.match(/<body[^>]*>([\\s\\S]*?)<\\/body>/i);\n\tif (!bodyMatch) return false;\n\n\tconst bodyHtml = bodyMatch[1];\n\n\t// Strip tags to get text content\n\tconst textContent = bodyHtml\n\t\t.replace(/<script[\\s\\S]*?<\\/script>/gi, \"\")\n\t\t.replace(/<style[\\s\\S]*?<\\/style>/gi, \"\")\n\t\t.replace(/<[^>]+>/g, \"\")\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim();\n\n\t// Count scripts\n\tconst scriptCount = (html.match(/<script/gi) || []).length;\n\n\t// Heuristic: little text content but many scripts suggests JS rendering\n\treturn textContent.length < 500 && scriptCount > 3;\n}\n\nasync function extractViaHttp(\n\turl: string,\n\tsignal?: AbortSignal,\n\toptions?: ExtractOptions,\n): Promise<ExtractedContent> {\n\tconst timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\tconst activityId = activityMonitor.logStart({ type: \"fetch\", url });\n\n\tconst controller = new AbortController();\n\tconst timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n\tconst onAbort = () => controller.abort();\n\tif (signal?.aborted) controller.abort();\n\tsignal?.addEventListener(\"abort\", onAbort);\n\n\ttry {\n\t\tconst response = await fetchRemoteUrl(\n\t\t\turl,\n\t\t\t{\n\t\t\t\tsignal: controller.signal,\n\t\t\t\theaders: {\n\t\t\t\t\t\"User-Agent\": \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36\",\n\t\t\t\t\t\"Accept\": \"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8\",\n\t\t\t\t\t\"Accept-Language\": \"en-US,en;q=0.9\",\n\t\t\t\t\t\"Cache-Control\": \"no-cache\",\n\t\t\t\t\t\"Sec-Fetch-Dest\": \"document\",\n\t\t\t\t\t\"Sec-Fetch-Mode\": \"navigate\",\n\t\t\t\t\t\"Sec-Fetch-Site\": \"none\",\n\t\t\t\t\t\"Sec-Fetch-User\": \"?1\",\n\t\t\t\t\t\"Upgrade-Insecure-Requests\": \"1\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ allowRanges: loadSsrfAllowRanges(), lookup: options?.lookup },\n\t\t);\n\n\t\tif (!response.ok) {\n\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: \"\",\n\t\t\t\tcontent: \"\",\n\t\t\t\terror: `HTTP ${response.status}: ${response.statusText}`,\n\t\t\t};\n\t\t}\n\n\t\tconst contentLengthHeader = response.headers.get(\"content-length\");\n\t\tconst contentType = response.headers.get(\"content-type\") || \"\";\n\t\tconst isPDFContent = isPDF(url, contentType);\n\t\tconst maxResponseSize = isPDFContent ? 20 * 1024 * 1024 : 5 * 1024 * 1024;\n\t\tif (contentLengthHeader) {\n\t\t\tconst contentLength = parseInt(contentLengthHeader, 10);\n\t\t\tif (contentLength > maxResponseSize) {\n\t\t\t\tawait response.body?.cancel();\n\t\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\t\treturn {\n\t\t\t\t\turl,\n\t\t\t\t\ttitle: \"\",\n\t\t\t\t\tcontent: \"\",\n\t\t\t\t\terror: `Response too large (${Math.round(contentLength / 1024 / 1024)}MB)`,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tif (isPDFContent) {\n\t\t\tconst bytes = await readBoundedBody(response, maxResponseSize, controller.signal);\n\t\t\ttry {\n\t\t\t\tconst buffer = bytes.buffer;\n\t\t\t\tconst { extractPDFToMarkdown } = await loadPdfExtract();\n\t\t\t\tconst result = await extractPDFToMarkdown(buffer, url);\n\t\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\t\treturn {\n\t\t\t\t\turl,\n\t\t\t\t\ttitle: result.title,\n\t\t\t\t\tcontent: `PDF extracted and saved to: ${result.outputPath}\\n\\nPages: ${result.pages}\\nCharacters: ${result.chars}`,\n\t\t\t\t\terror: null,\n\t\t\t\t};\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\t\tactivityMonitor.logError(activityId, message);\n\t\t\t\treturn { url, title: \"\", content: \"\", error: `PDF extraction failed: ${message}` };\n\t\t\t}\n\t\t}\n\n\t\tif (contentType.includes(\"application/octet-stream\") ||\n\t\t\tcontentType.includes(\"image/\") ||\n\t\t\tcontentType.includes(\"audio/\") ||\n\t\t\tcontentType.includes(\"video/\") ||\n\t\t\tcontentType.includes(\"application/zip\")) {\n\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: \"\",\n\t\t\t\tcontent: \"\",\n\t\t\t\terror: `Unsupported content type: ${contentType.split(\";\")[0]}`,\n\t\t\t};\n\t\t}\n\n\t\tconst text = new TextDecoder().decode(await readBoundedBody(response, maxResponseSize, controller.signal));\n\t\tconst isHTML = contentType.includes(\"text/html\") || contentType.includes(\"application/xhtml+xml\");\n\n\t\tif (!isHTML) {\n\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\tconst title = extractTextTitle(text, url);\n\t\t\treturn { url, title, content: text, error: null };\n\t\t}\n\n\t\tconst { document } = parseHTML(text);\n\t\tconst reader = new Readability(document as unknown as Document);\n\t\tconst article = reader.parse();\n\n\t\tif (!article) {\n\t\t\tconst rscResult = extractRSCContent(text);\n\t\t\tif (rscResult) {\n\t\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\t\t\treturn { url, title: rscResult.title, content: rscResult.content, error: null };\n\t\t\t}\n\n\t\t\tactivityMonitor.logComplete(activityId, response.status);\n\n\t\t\t// Provide more specific error message\n\t\t\tconst jsRendered = isLikelyJSRendered(text);\n\t\t\tconst errorMsg = jsRendered\n\t\t\t\t? \"Page appears to be JavaScript-rendered (content loads dynamically)\"\n\t\t\t\t: \"Could not extract readable content from HTML structure\";\n\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: \"\",\n\t\t\t\tcontent: \"\",\n\t\t\t\terror: errorMsg,\n\t\t\t};\n\t\t}\n\n\t\tconst markdown = turndown.turndown(article.content);\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\n\t\tif (markdown.length < MIN_USEFUL_CONTENT) {\n\t\t\treturn {\n\t\t\t\turl,\n\t\t\t\ttitle: article.title || \"\",\n\t\t\t\tcontent: markdown,\n\t\t\t\terror: isLikelyJSRendered(text)\n\t\t\t\t\t? \"Page appears to be JavaScript-rendered (content loads dynamically)\"\n\t\t\t\t\t: \"Extracted content appears incomplete\",\n\t\t\t};\n\t\t}\n\n\t\treturn { url, title: article.title || \"\", content: markdown, error: null };\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tif (message.toLowerCase().includes(\"abort\")) {\n\t\t\tactivityMonitor.logComplete(activityId, 0);\n\t\t} else {\n\t\t\tactivityMonitor.logError(activityId, message);\n\t\t}\n\t\treturn { url, title: \"\", content: \"\", error: message };\n\t} finally {\n\t\tclearTimeout(timeoutId);\n\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t}\n}\n\nexport function extractHeadingTitle(text: string): string | null {\n\tconst match = text.match(/^#{1,2}\\s+(.+)/m);\n\tif (!match) return null;\n\tconst cleaned = match[1].replace(/\\*+/g, \"\").trim();\n\treturn cleaned || null;\n}\n\nfunction extractTextTitle(text: string, url: string): string {\n\treturn extractHeadingTitle(text) ?? (new URL(url).pathname.split(\"/\").pop() || url);\n}\n\nexport async function fetchAllContent(\n\turls: string[],\n\tsignal?: AbortSignal,\n\toptions?: ExtractOptions,\n): Promise<ExtractedContent[]> {\n\treturn Promise.all(urls.map((url) => fetchLimit(() => extractContent(url, signal, options))));\n}\n"]}
|
|
@@ -9,5 +9,4 @@ export interface GitHubUrlInfo {
|
|
|
9
9
|
}
|
|
10
10
|
export declare function parseGitHubUrl(url: string): GitHubUrlInfo | null;
|
|
11
11
|
export declare function extractGitHub(url: string, signal?: AbortSignal, forceClone?: boolean): Promise<ExtractedContent | null>;
|
|
12
|
-
export declare function clearCloneCache(): void;
|
|
13
12
|
//# sourceMappingURL=github-extract.d.ts.map
|