@ashx-j/lunr 0.2.25 → 0.2.27
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 +31 -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/index.d.ts.map +1 -1
- package/dist/builtin-extensions/index.js +1 -0
- package/dist/builtin-extensions/index.js.map +1 -1
- package/dist/builtin-extensions/lunr-computer-use.d.ts +3 -0
- package/dist/builtin-extensions/lunr-computer-use.d.ts.map +1 -0
- package/dist/builtin-extensions/lunr-computer-use.js +179 -0
- package/dist/builtin-extensions/lunr-computer-use.js.map +1 -0
- 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/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/extension/schemas.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts +4 -4
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js +8 -5
- package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts +1 -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 +15 -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 +1 -0
- 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 +21 -4
- 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 +11 -4
- 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.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +121 -25
- 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 +25 -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/chain-outputs.d.ts +1 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/chain-outputs.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/chain-outputs.js +5 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/chain-outputs.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.js +2 -0
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/child-tools.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/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/tui/communication.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/communication.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/communication.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +2 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js +22 -11
- 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 +20 -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/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 +40 -6
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +309 -76
- 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/computer-use/adapter.d.ts +28 -0
- package/dist/core/computer-use/adapter.d.ts.map +1 -0
- package/dist/core/computer-use/adapter.js +138 -0
- package/dist/core/computer-use/adapter.js.map +1 -0
- package/dist/core/computer-use/image.d.ts +27 -0
- package/dist/core/computer-use/image.d.ts.map +1 -0
- package/dist/core/computer-use/image.js +84 -0
- package/dist/core/computer-use/image.js.map +1 -0
- package/dist/core/computer-use/lease.d.ts +18 -0
- package/dist/core/computer-use/lease.d.ts.map +1 -0
- package/dist/core/computer-use/lease.js +129 -0
- package/dist/core/computer-use/lease.js.map +1 -0
- package/dist/core/computer-use/macos-runtime.d.ts +14 -0
- package/dist/core/computer-use/macos-runtime.d.ts.map +1 -0
- package/dist/core/computer-use/macos-runtime.js +148 -0
- package/dist/core/computer-use/macos-runtime.js.map +1 -0
- package/dist/core/computer-use/policy.d.ts +19 -0
- package/dist/core/computer-use/policy.d.ts.map +1 -0
- package/dist/core/computer-use/policy.js +67 -0
- package/dist/core/computer-use/policy.js.map +1 -0
- package/dist/core/computer-use/release.generated.d.ts +31 -0
- package/dist/core/computer-use/release.generated.d.ts.map +1 -0
- package/dist/core/computer-use/release.generated.js +36 -0
- package/dist/core/computer-use/release.generated.js.map +1 -0
- package/dist/core/computer-use/runtime.d.ts +13 -0
- package/dist/core/computer-use/runtime.d.ts.map +1 -0
- package/dist/core/computer-use/runtime.js +158 -0
- package/dist/core/computer-use/runtime.js.map +1 -0
- package/dist/core/computer-use/schemas.d.ts +100 -0
- package/dist/core/computer-use/schemas.d.ts.map +1 -0
- package/dist/core/computer-use/schemas.js +96 -0
- package/dist/core/computer-use/schemas.js.map +1 -0
- package/dist/core/computer-use/windows-desktop.d.ts +3 -0
- package/dist/core/computer-use/windows-desktop.d.ts.map +1 -0
- package/dist/core/computer-use/windows-desktop.js +48 -0
- package/dist/core/computer-use/windows-desktop.js.map +1 -0
- package/dist/core/computer-use/workflow.d.ts +38 -0
- package/dist/core/computer-use/workflow.d.ts.map +1 -0
- package/dist/core/computer-use/workflow.js +900 -0
- package/dist/core/computer-use/workflow.js.map +1 -0
- 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/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +4 -0
- package/dist/core/extensions/loader.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 +8 -0
- 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 +4 -4
- package/dist/core/permissions.d.ts.map +1 -1
- package/dist/core/permissions.js +25 -38
- 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 +135 -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 +6 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +95 -32
- 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/settings-manager.d.ts +6 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +19 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/subagent-event-transport.d.ts +3 -0
- package/dist/core/subagent-event-transport.d.ts.map +1 -0
- package/dist/core/subagent-event-transport.js +61 -0
- package/dist/core/subagent-event-transport.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-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-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/adapters/discord.d.ts.map +1 -1
- package/dist/gateway/adapters/discord.js +2 -2
- package/dist/gateway/adapters/discord.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/approval.d.ts.map +1 -1
- package/dist/gateway/approval.js +3 -3
- package/dist/gateway/approval.js.map +1 -1
- package/dist/gateway/authz.d.ts +4 -4
- package/dist/gateway/authz.d.ts.map +1 -1
- package/dist/gateway/authz.js +8 -12
- package/dist/gateway/authz.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 +12 -10
- 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 +85 -62
- package/dist/gateway/cron.js.map +1 -1
- package/dist/gateway/index.d.ts.map +1 -1
- package/dist/gateway/index.js +5 -4
- 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 +64 -22
- 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 +74 -39
- 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 +22 -9
- 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/gateway/setup.d.ts.map +1 -1
- package/dist/gateway/setup.js +6 -4
- package/dist/gateway/setup.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/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/settings-selector.d.ts +4 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +20 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +2 -2
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +104 -109
- 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/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +9 -1
- package/dist/modes/print-mode.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-247RTYLS.js +16 -0
- package/dist/node-runtime/agent-session-services-KDMSPQDY.js +53 -0
- package/dist/node-runtime/{brave-UWDA3I22.js → brave-2AFFAXNN.js} +1 -1
- package/dist/node-runtime/{builtin-extensions-ZLAX7TUH.js → builtin-extensions-OX6KELNX.js} +9 -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-FQMM2EZ2.js → chunk-64XTQNLA.js} +29 -7
- package/dist/node-runtime/{chunk-NRKGMJYU.js → chunk-6O3IW3IA.js} +2 -2
- package/dist/node-runtime/{chunk-5ZQXRPMC.js → chunk-6ZZHGUSA.js} +2 -2
- package/dist/node-runtime/chunk-A2WO6WIN.js +1197 -0
- package/dist/node-runtime/{chunk-ZXSGTGJ6.js → chunk-APVM5NEU.js} +169 -148
- package/dist/node-runtime/{chunk-RC7DMV45.js → chunk-AYHH7YSD.js} +205 -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-GJ2RDBRL.js → chunk-BYC6RKCN.js} +4 -2
- package/dist/node-runtime/{chunk-KWIYUUZK.js → chunk-C3P6Q3B2.js} +469 -283
- package/dist/node-runtime/{chunk-YCUZEGJG.js → chunk-DQSCFU7R.js} +17 -8
- package/dist/node-runtime/chunk-EBLAZVSA.js +4170 -0
- package/dist/node-runtime/{chunk-2KIPT4HJ.js → chunk-EPR2F3GU.js} +68 -20
- 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-FE3WZI7F.js +290 -0
- package/dist/node-runtime/{chunk-GYIXXQAO.js → chunk-FIRA54AC.js} +2 -5
- 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-FY4A63RK.js +73 -0
- package/dist/node-runtime/chunk-FYIFPFXE.js +98 -0
- 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-6XL67VVY.js → chunk-HUD2RFJW.js} +17 -2
- package/dist/node-runtime/{chunk-I54LGHWM.js → chunk-I2H5QMWW.js} +3 -3
- package/dist/node-runtime/chunk-IAZSK7KK.js +108 -0
- package/dist/node-runtime/{chunk-2HHLJWGD.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-RDDAZOMU.js → chunk-JL4HYQXK.js} +1 -1
- package/dist/node-runtime/{chunk-CDVHZOOK.js → chunk-JQYNHAKX.js} +1 -1
- package/dist/node-runtime/{chunk-WF7PGWRZ.js → chunk-JWEVT363.js} +37 -28
- 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-IJIFHRTB.js → chunk-MQPKUHRN.js} +165 -85
- 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-RWTKOC3B.js → chunk-QBTJG3UM.js} +1 -1
- package/dist/node-runtime/{chunk-E4D3IGP2.js → chunk-QNVMKNNN.js} +20 -17
- package/dist/node-runtime/{chunk-GS42T6MX.js → chunk-QW3AAHWN.js} +1 -1
- package/dist/node-runtime/{chunk-7S3T6YEA.js → chunk-R4WSB2MQ.js} +5 -5
- package/dist/node-runtime/{chunk-B4HP6ZWM.js → chunk-RHCGYWO7.js} +1 -1
- package/dist/node-runtime/{chunk-3XPLBUEM.js → chunk-RLGY2QIL.js} +49 -59
- package/dist/node-runtime/{chunk-KKUTQQV7.js → chunk-RYRGL7P4.js} +1 -1
- package/dist/node-runtime/{chunk-AD6VXAO7.js → chunk-TCT6PCUR.js} +47 -115
- package/dist/node-runtime/chunk-U5F4IC7R.js +53 -0
- package/dist/node-runtime/{chunk-OCVIXVTZ.js → chunk-UBWW2PID.js} +13 -2
- package/dist/node-runtime/{chunk-HFVZUPFJ.js → chunk-UNUZICC4.js} +3 -3
- package/dist/node-runtime/{chunk-LQP3RB67.js → chunk-UYRKUKFE.js} +1 -1
- package/dist/node-runtime/{chunk-DSIKTUPW.js → chunk-VSUEAI7O.js} +2993 -3820
- 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-VQFSUJJG.js → chunk-ZDJUR45D.js} +430 -129
- package/dist/node-runtime/{chunk-YDO2F2IA.js → chunk-ZEVFBFNF.js} +95 -142
- package/dist/node-runtime/chunk-ZIQUS5QO.js +180 -0
- package/dist/node-runtime/cli-runtime.js +4 -4
- package/dist/node-runtime/{command-6WDOYKS6.js → command-3HMSY6W2.js} +5 -5
- package/dist/node-runtime/{commands-NZLSOHPE.js → commands-D6A6H4TS.js} +11 -16
- package/dist/node-runtime/commands-OCSSEEWJ.js +37 -0
- 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-77GW4F5P.js → direct-tool-executor-2QJXVQD2.js} +8 -10
- package/dist/node-runtime/{exa-LL64QZR2.js → exa-6VBZNPGE.js} +1 -1
- package/dist/node-runtime/{export-html-VM4J2MNR.js → export-html-BRWF5AHI.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-INAYBF4Q.js → gateway-6623C2U3.js} +61 -53
- 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-AIHEDUYA.js} +5 -5
- package/dist/node-runtime/index.js +54 -48
- package/dist/node-runtime/{init-HS6NQXSK.js → init-JBQJIPYI.js} +3 -4
- package/dist/node-runtime/{install-cli-KL4UDDAD.js → install-cli-INGHIVBY.js} +30 -25
- package/dist/node-runtime/interactive-mode-FM5DNUSW.js +74 -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-VDP2QNRQ.js +158 -0
- package/dist/node-runtime/{lunr-cron-LWIZJM2N.js → lunr-cron-LKVA5CZZ.js} +94 -79
- package/dist/node-runtime/main.js +42 -39
- package/dist/node-runtime/{mobile-commands-J7OJEKFO.js → mobile-commands-XT3NQXLD.js} +13 -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-VYZM5WHN.js → package-manager-cli-CYM262SI.js} +20 -19
- 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-5UKBZP44.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-DNY7EAIG.js → pi-subagents-LT3CTO4E.js} +114 -116
- package/dist/node-runtime/{pi-web-access-WTGMNZ32.js → pi-web-access-Z3LP246C.js} +144 -108
- package/dist/node-runtime/{print-mode-XSFM26EL.js → print-mode-2XHN5SUA.js} +4 -3
- package/dist/node-runtime/{process-registry-4FAKZQOL.js → process-registry-Q4QWIU33.js} +3 -3
- package/dist/node-runtime/{proxy-modes-LGEUYD7R.js → proxy-modes-VEGC6QG5.js} +9 -13
- package/dist/node-runtime/rpc-entry.js +42 -39
- 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-UGXM4NPQ.js +21 -0
- package/dist/node-runtime/{runtime-bridges-VRDDR73P.js → runtime-bridges-DNKMNJCF.js} +8 -7
- package/dist/node-runtime/{service-MDQIPQ26.js → service-IHLGFLBT.js} +6 -4
- package/dist/node-runtime/{session-handoff-GWARVEWT.js → session-handoff-LNSPDTKY.js} +4 -4
- package/dist/node-runtime/{session-manager-JB5J6XBN.js → session-manager-YIFIAVGC.js} +3 -3
- package/dist/node-runtime/{session-selector-GIM4DHQ2.js → session-selector-I73U3EIO.js} +7 -7
- package/dist/node-runtime/{settings-manager-J4SPQPDJ.js → settings-manager-P2TKGYTR.js} +6 -5
- package/dist/node-runtime/{setup-JBKOLJ6F.js → setup-H7CCELIL.js} +15 -1
- package/dist/node-runtime/{setup-CQ2NLVK5.js → setup-OLQ4GXWS.js} +22 -21
- package/dist/node-runtime/{src-IVPNZ2BX.js → src-6RV4PXKJ.js} +64 -36
- package/dist/node-runtime/{subagent-executor-KZZQGAKV.js → subagent-executor-Q5F7DBVM.js} +141 -62
- 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-GTFJC7XK.js → update-cli-F3XXJANQ.js} +7 -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-ICDI4N3J.js +767 -0
- 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 +2 -0
- package/dist/utils/image-resize-core.d.ts.map +1 -1
- package/dist/utils/image-resize-core.js +12 -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/computer-use.md +362 -0
- package/docs/custom-provider.md +3 -3
- package/docs/extensions.md +8 -4
- package/docs/features.md +22 -12
- 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 +3841 -0
- package/install-lock/package.json +18 -0
- package/native/computer-use/LICENSE.md +21 -0
- package/npm-shrinkwrap.json +3858 -0
- package/package.json +15 -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-YXXFCP5J.js +0 -49
- package/dist/node-runtime/chunk-4HXHDDCF.js +0 -16
- package/dist/node-runtime/chunk-4TLO4PO2.js +0 -3427
- package/dist/node-runtime/chunk-AUXVGBO4.js +0 -19
- package/dist/node-runtime/chunk-OVX6X377.js +0 -50
- package/dist/node-runtime/chunk-QTU6AKNJ.js +0 -773
- package/dist/node-runtime/chunk-TORR5XBV.js +0 -356
- package/dist/node-runtime/commands-QC6VUZJQ.js +0 -36
- package/dist/node-runtime/interactive-mode-Z5M7WYYZ.js +0 -72
- package/dist/node-runtime/mcp-auth-flow-QIXHW6QN.js +0 -32
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-oauth-provider.d.ts","sourceRoot":"","sources":["../../../src/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.ts"],"names":[],"mappings":"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","sourcesContent":["// @ts-nocheck\n/**\n * MCP OAuth Provider\n * \n * Implementation of the MCP SDK's OAuthClientProvider interface.\n * Handles OAuth client registration, token storage, and authorization redirection.\n */\n\nimport type { AddClientAuthentication, OAuthClientProvider } from \"@modelcontextprotocol/sdk/client/auth.js\"\nimport { UnauthorizedError } from \"@modelcontextprotocol/sdk/client/auth.js\"\nimport type {\n OAuthClientMetadata,\n OAuthTokens,\n OAuthClientInformation,\n OAuthClientInformationFull,\n} from \"@modelcontextprotocol/sdk/shared/auth.js\"\nimport {\n getAuthForUrl,\n updateTokens,\n updateClientInfo,\n updateCodeVerifier,\n updateOAuthState,\n clearAllCredentials,\n clearClientInfo,\n clearTokens,\n type StoredTokens,\n type StoredClientInfo,\n} from \"./mcp-auth.ts\"\n\n// Callback server configuration\nconst DEFAULT_OAUTH_CALLBACK_PORT = 19876\nconst DEFAULT_OAUTH_CALLBACK_PATH = \"/callback\"\n\nlet configuredOAuthCallbackPort = DEFAULT_OAUTH_CALLBACK_PORT\n\nif (process.env.MCP_OAUTH_CALLBACK_PORT) {\n const parsedPort = Number.parseInt(process.env.MCP_OAUTH_CALLBACK_PORT, 10)\n if (Number.isInteger(parsedPort) && parsedPort > 0 && parsedPort <= 65535) {\n configuredOAuthCallbackPort = parsedPort\n }\n}\n\nlet oauthCallbackPort = configuredOAuthCallbackPort\nlet oauthCallbackPath = DEFAULT_OAUTH_CALLBACK_PATH\n\nexport function getConfiguredOAuthCallbackPort(): number {\n return configuredOAuthCallbackPort\n}\n\nexport function getOAuthCallbackPort(): number {\n return oauthCallbackPort\n}\n\nexport function setOAuthCallbackPort(port: number): void {\n oauthCallbackPort = port\n}\n\nexport function getOAuthCallbackPath(): string {\n return oauthCallbackPath\n}\n\nexport function setOAuthCallbackPath(path: string): void {\n oauthCallbackPath = path.startsWith(\"/\") ? path : `/${path}`\n}\n\n/** Configuration options for OAuth */\nexport interface McpOAuthConfig {\n grantType?: \"authorization_code\" | \"client_credentials\"\n clientId?: string\n clientSecret?: string\n scope?: string\n redirectUri?: string\n clientName?: string\n clientUri?: string\n}\n\n/** Callbacks for OAuth flow interactions */\nexport interface McpOAuthCallbacks {\n onRedirect: (url: URL) => void | Promise<void>\n}\n\n/**\n * OAuth provider implementation for MCP servers.\n * Implements the OAuthClientProvider interface from the MCP SDK.\n */\nexport class McpOAuthProvider implements OAuthClientProvider {\n private readonly redirectUrlSnapshot: string | undefined\n\n constructor(\n private serverName: string,\n private serverUrl: string,\n private config: McpOAuthConfig,\n private callbacks: McpOAuthCallbacks,\n ) {\n this.redirectUrlSnapshot = config.grantType === \"client_credentials\"\n ? undefined\n : config.redirectUri ?? `http://localhost:${getOAuthCallbackPort()}${getOAuthCallbackPath()}`\n }\n\n private get usesClientCredentials(): boolean {\n return this.config.grantType === \"client_credentials\"\n }\n\n /**\n * The redirect URL for OAuth callbacks.\n * This must match the redirect_uri in client metadata.\n */\n get redirectUrl(): string | undefined {\n return this.redirectUrlSnapshot\n }\n\n /**\n * Client metadata for dynamic registration.\n * Describes this client to the OAuth authorization server.\n */\n get clientMetadata(): OAuthClientMetadata {\n if (this.usesClientCredentials) {\n return {\n client_name: this.config.clientName ?? \"Pi Coding Agent\",\n client_uri: this.config.clientUri ?? \"https://github.com/nicobailon/pi-mcp-adapter\",\n redirect_uris: [],\n grant_types: [\"client_credentials\"],\n token_endpoint_auth_method: this.config.clientSecret ? \"client_secret_post\" : \"none\",\n }\n }\n\n const redirectUrl = this.redirectUrl\n if (!redirectUrl) {\n throw new Error(\"redirectUrl is required for authorization_code flow\")\n }\n\n return {\n redirect_uris: [redirectUrl],\n client_name: this.config.clientName ?? \"Pi Coding Agent\",\n client_uri: this.config.clientUri ?? \"https://github.com/nicobailon/pi-mcp-adapter\",\n grant_types: [\"authorization_code\", \"refresh_token\"],\n response_types: [\"code\"],\n token_endpoint_auth_method: this.config.clientSecret ? \"client_secret_post\" : \"none\",\n ...(this.config.scope !== undefined ? { scope: this.config.scope } : {}),\n }\n }\n\n /**\n * Get client information (for pre-registered or dynamically registered clients).\n * Returns undefined if no client info exists or if the server URL has changed.\n */\n async clientInformation(): Promise<OAuthClientInformation | undefined> {\n // Check config first (pre-registered client)\n if (this.config.clientId) {\n return {\n client_id: this.config.clientId,\n client_secret: this.config.clientSecret,\n }\n }\n\n // Check stored client info (from dynamic registration)\n // Use getAuthForUrl to validate credentials are for the current server URL\n const entry = await getAuthForUrl(this.serverName, this.serverUrl)\n if (entry?.clientInfo) {\n // Check if client secret has expired\n if (entry.clientInfo.clientSecretExpiresAt && entry.clientInfo.clientSecretExpiresAt < Date.now() / 1000) {\n return undefined\n }\n return {\n client_id: entry.clientInfo.clientId,\n client_secret: entry.clientInfo.clientSecret,\n }\n }\n\n // No client info or URL changed - will trigger dynamic registration\n return undefined\n }\n\n /**\n * Save client information from dynamic registration.\n */\n async saveClientInformation(info: OAuthClientInformationFull): Promise<void> {\n const redirectUris = info.redirect_uris ?? (this.redirectUrl ? [this.redirectUrl] : undefined)\n const clientInfo: StoredClientInfo = {\n clientId: info.client_id,\n clientSecret: info.client_secret,\n clientIdIssuedAt: info.client_id_issued_at,\n clientSecretExpiresAt: info.client_secret_expires_at,\n redirectUris,\n }\n updateClientInfo(this.serverName, clientInfo, this.serverUrl)\n }\n\n /**\n * Get stored OAuth tokens.\n * Returns undefined if no tokens exist or if the server URL has changed.\n */\n async tokens(): Promise<OAuthTokens | undefined> {\n // Use getAuthForUrl to validate tokens are for the current server URL\n const entry = await getAuthForUrl(this.serverName, this.serverUrl)\n if (!entry?.tokens) return undefined\n\n return {\n access_token: entry.tokens.accessToken,\n token_type: \"Bearer\",\n refresh_token: entry.tokens.refreshToken,\n expires_in: entry.tokens.expiresAt\n ? Math.max(0, Math.floor(entry.tokens.expiresAt - Date.now() / 1000))\n : undefined,\n scope: entry.tokens.scope,\n }\n }\n\n /**\n * Save OAuth tokens.\n */\n async saveTokens(tokens: OAuthTokens): Promise<void> {\n const storedTokens: StoredTokens = {\n accessToken: tokens.access_token,\n refreshToken: tokens.refresh_token,\n expiresAt: tokens.expires_in ? Date.now() / 1000 + tokens.expires_in : undefined,\n scope: tokens.scope,\n }\n updateTokens(this.serverName, storedTokens, this.serverUrl)\n }\n\n /**\n * Redirect the user to the authorization URL.\n * This opens the browser for the user to authenticate.\n *\n * Throws UnauthorizedError when called outside of a user-initiated flow\n * (no oauthState saved by startAuth). That path is reached when the SDK\n * falls through from a failed refresh into a fresh authorization_code\n * flow, which library hosts cannot complete in-process.\n */\n async redirectToAuthorization(authorizationUrl: URL): Promise<void> {\n if (this.usesClientCredentials) {\n throw new Error(\"redirectToAuthorization is not used for client_credentials flow\")\n }\n // No saved oauthState means we're on the post-refresh authorize fallback.\n const entry = await getAuthForUrl(this.serverName, this.serverUrl)\n if (!entry?.oauthState) {\n throw new UnauthorizedError(\n `Re-authentication required for MCP server: ${this.serverName}`,\n )\n }\n // URL is passed to callback, not logged (may contain sensitive params)\n await this.callbacks.onRedirect(authorizationUrl)\n }\n\n /**\n * Save the PKCE code verifier.\n */\n async saveCodeVerifier(codeVerifier: string): Promise<void> {\n updateCodeVerifier(this.serverName, codeVerifier, this.serverUrl)\n }\n\n /**\n * Get the stored PKCE code verifier.\n * @throws Error if no code verifier is stored\n */\n async codeVerifier(): Promise<string> {\n if (this.usesClientCredentials) {\n throw new Error(\"codeVerifier is not used for client_credentials flow\")\n }\n const entry = await getAuthForUrl(this.serverName, this.serverUrl)\n if (!entry?.codeVerifier) {\n throw new Error(`No code verifier saved for MCP server: ${this.serverName}`)\n }\n return entry.codeVerifier\n }\n\n /**\n * Save the OAuth state parameter for CSRF protection.\n */\n async saveState(state: string): Promise<void> {\n updateOAuthState(this.serverName, state, this.serverUrl)\n }\n\n /**\n * Get the stored OAuth state parameter.\n * @throws UnauthorizedError if no flow is in progress (see redirectToAuthorization)\n */\n async state(): Promise<string> {\n if (this.usesClientCredentials) {\n throw new Error(\"state is not used for client_credentials flow\")\n }\n const entry = await getAuthForUrl(this.serverName, this.serverUrl)\n if (!entry?.oauthState) {\n throw new UnauthorizedError(\n `Re-authentication required for MCP server: ${this.serverName}`,\n )\n }\n return entry.oauthState\n }\n\n /**\n * Invalidate credentials when authentication fails.\n * Clears tokens, client info, or all credentials based on the type.\n */\n async invalidateCredentials(type: \"all\" | \"client\" | \"tokens\"): Promise<void> {\n switch (type) {\n case \"all\":\n clearAllCredentials(this.serverName)\n break\n case \"client\":\n clearClientInfo(this.serverName)\n break\n case \"tokens\":\n clearTokens(this.serverName)\n break\n }\n }\n\n /**\n * Adds configured authorization-code scope without replacing the SDK's\n * default token endpoint authentication behavior.\n */\n addClientAuthentication: AddClientAuthentication = async (headers, params, _url, metadata) => {\n if (params.get(\"grant_type\") === \"authorization_code\" && !params.has(\"scope\") && this.config.scope) {\n params.set(\"scope\", this.config.scope)\n }\n\n const clientInfo = await this.clientInformation()\n if (!clientInfo) {\n return\n }\n\n const supportedMethods = metadata?.token_endpoint_auth_methods_supported ?? []\n const hasClientSecret = clientInfo.client_secret !== undefined\n let authMethod: \"client_secret_basic\" | \"client_secret_post\" | \"none\"\n\n if (supportedMethods.length === 0) {\n authMethod = hasClientSecret ? \"client_secret_post\" : \"none\"\n } else if (hasClientSecret && supportedMethods.includes(\"client_secret_basic\")) {\n authMethod = \"client_secret_basic\"\n } else if (hasClientSecret && supportedMethods.includes(\"client_secret_post\")) {\n authMethod = \"client_secret_post\"\n } else if (supportedMethods.includes(\"none\")) {\n authMethod = \"none\"\n } else {\n authMethod = hasClientSecret ? \"client_secret_post\" : \"none\"\n }\n\n if (authMethod === \"client_secret_basic\") {\n if (!clientInfo.client_secret) {\n throw new Error(\"client_secret_basic authentication requires a client_secret\")\n }\n headers.set(\"Authorization\", `Basic ${Buffer.from(`${clientInfo.client_id}:${clientInfo.client_secret}`).toString(\"base64\")}`)\n return\n }\n\n if (!params.has(\"client_id\")) {\n params.set(\"client_id\", clientInfo.client_id)\n }\n if (authMethod === \"client_secret_post\" && clientInfo.client_secret && !params.has(\"client_secret\")) {\n params.set(\"client_secret\", clientInfo.client_secret)\n }\n }\n\n prepareTokenRequest(scope?: string): URLSearchParams | undefined {\n if (!this.usesClientCredentials) {\n return undefined\n }\n\n const params = new URLSearchParams({ grant_type: \"client_credentials\" })\n const requestedScope = scope ?? this.config.scope\n if (requestedScope) {\n params.set(\"scope\", requestedScope)\n }\n return params\n }\n}\n\nexport { DEFAULT_OAUTH_CALLBACK_PORT, DEFAULT_OAUTH_CALLBACK_PATH }\n"]}
|
|
1
|
+
{"version":3,"file":"mcp-oauth-provider.d.ts","sourceRoot":"","sources":["../../../src/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.ts"],"names":[],"mappings":"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","sourcesContent":["// @ts-nocheck\n/**\n * MCP OAuth Provider\n * \n * Implementation of the MCP SDK's OAuthClientProvider interface.\n * Handles OAuth client registration, token storage, and authorization redirection.\n */\n\nimport type { AddClientAuthentication, OAuthClientProvider } from \"@modelcontextprotocol/sdk/client/auth.js\"\nimport { UnauthorizedError } from \"@modelcontextprotocol/sdk/client/auth.js\"\nimport type {\n OAuthClientMetadata,\n OAuthTokens,\n OAuthClientInformation,\n OAuthClientInformationFull,\n} from \"@modelcontextprotocol/sdk/shared/auth.js\"\nimport {\n getAuthForUrl,\n updateTokens,\n updateClientInfo,\n updateCodeVerifier,\n updateOAuthState,\n clearAllCredentials,\n clearClientInfo,\n clearTokens,\n type StoredTokens,\n type StoredClientInfo,\n} from \"./mcp-auth.ts\"\n\nimport { throwIfAborted } from \"./abort.ts\"\n\n/** Temporary authorization state belongs to one flow, never the credential cache. */\nexport interface McpOAuthFlowState {\n oauthState: string\n codeVerifier?: string\n signal: AbortSignal\n clientInfo?: StoredClientInfo\n}\n\n// Callback server configuration\nconst DEFAULT_OAUTH_CALLBACK_PORT = 19876\nconst DEFAULT_OAUTH_CALLBACK_PATH = \"/callback\"\n\nlet configuredOAuthCallbackPort = DEFAULT_OAUTH_CALLBACK_PORT\n\nif (process.env.MCP_OAUTH_CALLBACK_PORT) {\n const parsedPort = Number.parseInt(process.env.MCP_OAUTH_CALLBACK_PORT, 10)\n if (Number.isInteger(parsedPort) && parsedPort > 0 && parsedPort <= 65535) {\n configuredOAuthCallbackPort = parsedPort\n }\n}\n\nlet oauthCallbackPort = configuredOAuthCallbackPort\nlet oauthCallbackPath = DEFAULT_OAUTH_CALLBACK_PATH\n\nexport function getConfiguredOAuthCallbackPort(): number {\n return configuredOAuthCallbackPort\n}\n\nexport function getOAuthCallbackPort(): number {\n return oauthCallbackPort\n}\n\nexport function setOAuthCallbackPort(port: number): void {\n oauthCallbackPort = port\n}\n\nexport function getOAuthCallbackPath(): string {\n return oauthCallbackPath\n}\n\nexport function setOAuthCallbackPath(path: string): void {\n oauthCallbackPath = path.startsWith(\"/\") ? path : `/${path}`\n}\n\n/** Configuration options for OAuth */\nexport interface McpOAuthConfig {\n grantType?: \"authorization_code\" | \"client_credentials\"\n clientId?: string\n clientSecret?: string\n scope?: string\n redirectUri?: string\n clientName?: string\n clientUri?: string\n}\n\n/** Callbacks for OAuth flow interactions */\nexport interface McpOAuthCallbacks {\n onRedirect: (url: URL) => void | Promise<void>\n}\n\n/**\n * OAuth provider implementation for MCP servers.\n * Implements the OAuthClientProvider interface from the MCP SDK.\n */\nexport class McpOAuthProvider implements OAuthClientProvider {\n private readonly redirectUrlSnapshot: string | undefined\n private flowClientInfo?: StoredClientInfo\n private flowClientInfoLoaded = false\n\n constructor(\n private serverName: string,\n private serverUrl: string,\n private config: McpOAuthConfig,\n private callbacks: McpOAuthCallbacks,\n private flowState?: McpOAuthFlowState,\n ) {\n if (flowState) {\n const info = flowState.clientInfo\n this.flowClientInfo = info?.clientSecretExpiresAt && info.clientSecretExpiresAt < Date.now() / 1000\n ? undefined : info\n this.flowClientInfoLoaded = true\n }\n this.redirectUrlSnapshot = config.grantType === \"client_credentials\"\n ? undefined\n : config.redirectUri ?? `http://localhost:${getOAuthCallbackPort()}${getOAuthCallbackPath()}`\n }\n\n private get usesClientCredentials(): boolean {\n return this.config.grantType === \"client_credentials\"\n }\n\n /**\n * The redirect URL for OAuth callbacks.\n * This must match the redirect_uri in client metadata.\n */\n get redirectUrl(): string | undefined {\n return this.redirectUrlSnapshot\n }\n\n /**\n * Client metadata for dynamic registration.\n * Describes this client to the OAuth authorization server.\n */\n get clientMetadata(): OAuthClientMetadata {\n if (this.usesClientCredentials) {\n return {\n client_name: this.config.clientName ?? \"lunR\",\n ...(this.config.clientUri ? { client_uri: this.config.clientUri } : {}),\n redirect_uris: [],\n grant_types: [\"client_credentials\"],\n token_endpoint_auth_method: this.config.clientSecret ? \"client_secret_post\" : \"none\",\n }\n }\n\n const redirectUrl = this.redirectUrl\n if (!redirectUrl) {\n throw new Error(\"redirectUrl is required for authorization_code flow\")\n }\n\n return {\n redirect_uris: [redirectUrl],\n client_name: this.config.clientName ?? \"lunR\",\n ...(this.config.clientUri ? { client_uri: this.config.clientUri } : {}),\n grant_types: [\"authorization_code\", \"refresh_token\"],\n response_types: [\"code\"],\n token_endpoint_auth_method: this.config.clientSecret ? \"client_secret_post\" : \"none\",\n ...(this.config.scope !== undefined ? { scope: this.config.scope } : {}),\n }\n }\n\n /**\n * Get client information (for pre-registered or dynamically registered clients).\n * Returns undefined if no client info exists or if the server URL has changed.\n */\n async clientInformation(): Promise<OAuthClientInformation | undefined> {\n // Check config first (pre-registered client)\n if (this.config.clientId) {\n return {\n client_id: this.config.clientId,\n client_secret: this.config.clientSecret,\n }\n }\n\n // An active flow keeps the registration it started with even if another\n // session uses the same server name with a different endpoint.\n if (!this.flowState || !this.flowClientInfoLoaded) {\n const entry = await getAuthForUrl(this.serverName, this.serverUrl)\n throwIfAborted(this.flowState?.signal)\n const info = entry?.clientInfo\n this.flowClientInfo = info?.clientSecretExpiresAt && info.clientSecretExpiresAt < Date.now() / 1000\n ? undefined : info\n this.flowClientInfoLoaded = true\n }\n if (!this.flowClientInfo) return undefined\n return {\n client_id: this.flowClientInfo.clientId,\n client_secret: this.flowClientInfo.clientSecret,\n }\n }\n\n /**\n * Save client information from dynamic registration.\n */\n async saveClientInformation(info: OAuthClientInformationFull): Promise<void> {\n throwIfAborted(this.flowState?.signal)\n const redirectUris = info.redirect_uris ?? (this.redirectUrl ? [this.redirectUrl] : undefined)\n const clientInfo: StoredClientInfo = {\n clientId: info.client_id,\n clientSecret: info.client_secret,\n clientIdIssuedAt: info.client_id_issued_at,\n clientSecretExpiresAt: info.client_secret_expires_at,\n redirectUris,\n }\n if (this.flowState) {\n this.flowClientInfo = clientInfo\n this.flowClientInfoLoaded = true\n }\n updateClientInfo(this.serverName, clientInfo, this.serverUrl)\n }\n\n /**\n * Get stored OAuth tokens.\n * Returns undefined if no tokens exist or if the server URL has changed.\n */\n async tokens(): Promise<OAuthTokens | undefined> {\n // Use getAuthForUrl to validate tokens are for the current server URL\n const entry = await getAuthForUrl(this.serverName, this.serverUrl)\n if (!entry?.tokens) return undefined\n\n return {\n access_token: entry.tokens.accessToken,\n token_type: \"Bearer\",\n refresh_token: entry.tokens.refreshToken,\n expires_in: entry.tokens.expiresAt\n ? Math.max(0, Math.floor(entry.tokens.expiresAt - Date.now() / 1000))\n : undefined,\n scope: entry.tokens.scope,\n }\n }\n\n /**\n * Save OAuth tokens.\n */\n async saveTokens(tokens: OAuthTokens): Promise<void> {\n throwIfAborted(this.flowState?.signal)\n const storedTokens: StoredTokens = {\n accessToken: tokens.access_token,\n refreshToken: tokens.refresh_token,\n expiresAt: tokens.expires_in ? Date.now() / 1000 + tokens.expires_in : undefined,\n scope: tokens.scope,\n }\n // Keep the completed flow's registration with its tokens if another\n // same-name endpoint replaced the shared credential entry meanwhile.\n if (this.flowState && this.flowClientInfo) {\n updateClientInfo(this.serverName, this.flowClientInfo, this.serverUrl)\n }\n updateTokens(this.serverName, storedTokens, this.serverUrl)\n }\n\n /**\n * Redirect the user to the authorization URL.\n * This opens the browser for the user to authenticate.\n *\n * Throws UnauthorizedError when called without owned or legacy flow state.\n * That path is reached when the SDK\n * falls through from a failed refresh into a fresh authorization_code\n * flow, which library hosts cannot complete in-process.\n */\n async redirectToAuthorization(authorizationUrl: URL): Promise<void> {\n if (this.usesClientCredentials) {\n throw new Error(\"redirectToAuthorization is not used for client_credentials flow\")\n }\n throwIfAborted(this.flowState?.signal)\n if (this.flowState) {\n await this.callbacks.onRedirect(authorizationUrl)\n return\n }\n // No saved oauthState means we're on the post-refresh authorize fallback.\n const entry = await getAuthForUrl(this.serverName, this.serverUrl)\n if (!entry?.oauthState) {\n throw new UnauthorizedError(\n `Re-authentication required for MCP server: ${this.serverName}`,\n )\n }\n // URL is passed to callback, not logged (may contain sensitive params)\n await this.callbacks.onRedirect(authorizationUrl)\n }\n\n /**\n * Save the PKCE code verifier.\n */\n async saveCodeVerifier(codeVerifier: string): Promise<void> {\n throwIfAborted(this.flowState?.signal)\n if (this.flowState) {\n this.flowState.codeVerifier = codeVerifier\n return\n }\n updateCodeVerifier(this.serverName, codeVerifier, this.serverUrl)\n }\n\n /**\n * Get the stored PKCE code verifier.\n * @throws Error if no code verifier is stored\n */\n async codeVerifier(): Promise<string> {\n throwIfAborted(this.flowState?.signal)\n if (this.flowState) {\n if (!this.flowState.codeVerifier) throw new Error(`No code verifier saved for MCP server: ${this.serverName}`)\n return this.flowState.codeVerifier\n }\n if (this.usesClientCredentials) {\n throw new Error(\"codeVerifier is not used for client_credentials flow\")\n }\n const entry = await getAuthForUrl(this.serverName, this.serverUrl)\n if (!entry?.codeVerifier) {\n throw new Error(`No code verifier saved for MCP server: ${this.serverName}`)\n }\n return entry.codeVerifier\n }\n\n /**\n * Save the OAuth state parameter for CSRF protection.\n */\n async saveState(state: string): Promise<void> {\n throwIfAborted(this.flowState?.signal)\n if (this.flowState) {\n this.flowState.oauthState = state\n return\n }\n updateOAuthState(this.serverName, state, this.serverUrl)\n }\n\n /**\n * Get the stored OAuth state parameter.\n * @throws UnauthorizedError if no flow is in progress (see redirectToAuthorization)\n */\n async state(): Promise<string> {\n throwIfAborted(this.flowState?.signal)\n if (this.flowState) return this.flowState.oauthState\n if (this.usesClientCredentials) {\n throw new Error(\"state is not used for client_credentials flow\")\n }\n const entry = await getAuthForUrl(this.serverName, this.serverUrl)\n if (!entry?.oauthState) {\n throw new UnauthorizedError(\n `Re-authentication required for MCP server: ${this.serverName}`,\n )\n }\n return entry.oauthState\n }\n\n /**\n * Invalidate credentials when authentication fails.\n * Clears tokens, client info, or all credentials based on the type.\n */\n async invalidateCredentials(type: \"all\" | \"client\" | \"tokens\"): Promise<void> {\n throwIfAborted(this.flowState?.signal)\n if (this.flowState && type !== \"tokens\") {\n this.flowClientInfo = undefined\n this.flowClientInfoLoaded = true\n }\n switch (type) {\n case \"all\":\n clearAllCredentials(this.serverName)\n break\n case \"client\":\n clearClientInfo(this.serverName)\n break\n case \"tokens\":\n clearTokens(this.serverName)\n break\n }\n }\n\n /**\n * Adds configured authorization-code scope without replacing the SDK's\n * default token endpoint authentication behavior.\n */\n addClientAuthentication: AddClientAuthentication = async (headers, params, _url, metadata) => {\n if (params.get(\"grant_type\") === \"authorization_code\" && !params.has(\"scope\") && this.config.scope) {\n params.set(\"scope\", this.config.scope)\n }\n\n const clientInfo = await this.clientInformation()\n if (!clientInfo) {\n return\n }\n\n const supportedMethods = metadata?.token_endpoint_auth_methods_supported ?? []\n const hasClientSecret = clientInfo.client_secret !== undefined\n let authMethod: \"client_secret_basic\" | \"client_secret_post\" | \"none\"\n\n if (supportedMethods.length === 0) {\n authMethod = hasClientSecret ? \"client_secret_post\" : \"none\"\n } else if (hasClientSecret && supportedMethods.includes(\"client_secret_basic\")) {\n authMethod = \"client_secret_basic\"\n } else if (hasClientSecret && supportedMethods.includes(\"client_secret_post\")) {\n authMethod = \"client_secret_post\"\n } else if (supportedMethods.includes(\"none\")) {\n authMethod = \"none\"\n } else {\n authMethod = hasClientSecret ? \"client_secret_post\" : \"none\"\n }\n\n if (authMethod === \"client_secret_basic\") {\n if (!clientInfo.client_secret) {\n throw new Error(\"client_secret_basic authentication requires a client_secret\")\n }\n headers.set(\"Authorization\", `Basic ${Buffer.from(`${clientInfo.client_id}:${clientInfo.client_secret}`).toString(\"base64\")}`)\n return\n }\n\n if (!params.has(\"client_id\")) {\n params.set(\"client_id\", clientInfo.client_id)\n }\n if (authMethod === \"client_secret_post\" && clientInfo.client_secret && !params.has(\"client_secret\")) {\n params.set(\"client_secret\", clientInfo.client_secret)\n }\n }\n\n prepareTokenRequest(scope?: string): URLSearchParams | undefined {\n if (!this.usesClientCredentials) {\n return undefined\n }\n\n const params = new URLSearchParams({ grant_type: \"client_credentials\" })\n const requestedScope = scope ?? this.config.scope\n if (requestedScope) {\n params.set(\"scope\", requestedScope)\n }\n return params\n }\n}\n\nexport { DEFAULT_OAUTH_CALLBACK_PORT, DEFAULT_OAUTH_CALLBACK_PATH }\n"]}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js";
|
|
9
9
|
import { getAuthForUrl, updateTokens, updateClientInfo, updateCodeVerifier, updateOAuthState, clearAllCredentials, clearClientInfo, clearTokens, } from "./mcp-auth.js";
|
|
10
|
+
import { throwIfAborted } from "./abort.js";
|
|
10
11
|
// Callback server configuration
|
|
11
12
|
const DEFAULT_OAUTH_CALLBACK_PORT = 19876;
|
|
12
13
|
const DEFAULT_OAUTH_CALLBACK_PATH = "/callback";
|
|
@@ -43,12 +44,22 @@ export class McpOAuthProvider {
|
|
|
43
44
|
serverUrl;
|
|
44
45
|
config;
|
|
45
46
|
callbacks;
|
|
47
|
+
flowState;
|
|
46
48
|
redirectUrlSnapshot;
|
|
47
|
-
|
|
49
|
+
flowClientInfo;
|
|
50
|
+
flowClientInfoLoaded = false;
|
|
51
|
+
constructor(serverName, serverUrl, config, callbacks, flowState) {
|
|
48
52
|
this.serverName = serverName;
|
|
49
53
|
this.serverUrl = serverUrl;
|
|
50
54
|
this.config = config;
|
|
51
55
|
this.callbacks = callbacks;
|
|
56
|
+
this.flowState = flowState;
|
|
57
|
+
if (flowState) {
|
|
58
|
+
const info = flowState.clientInfo;
|
|
59
|
+
this.flowClientInfo = info?.clientSecretExpiresAt && info.clientSecretExpiresAt < Date.now() / 1000
|
|
60
|
+
? undefined : info;
|
|
61
|
+
this.flowClientInfoLoaded = true;
|
|
62
|
+
}
|
|
52
63
|
this.redirectUrlSnapshot = config.grantType === "client_credentials"
|
|
53
64
|
? undefined
|
|
54
65
|
: config.redirectUri ?? `http://localhost:${getOAuthCallbackPort()}${getOAuthCallbackPath()}`;
|
|
@@ -70,8 +81,8 @@ export class McpOAuthProvider {
|
|
|
70
81
|
get clientMetadata() {
|
|
71
82
|
if (this.usesClientCredentials) {
|
|
72
83
|
return {
|
|
73
|
-
client_name: this.config.clientName ?? "
|
|
74
|
-
client_uri: this.config.clientUri
|
|
84
|
+
client_name: this.config.clientName ?? "lunR",
|
|
85
|
+
...(this.config.clientUri ? { client_uri: this.config.clientUri } : {}),
|
|
75
86
|
redirect_uris: [],
|
|
76
87
|
grant_types: ["client_credentials"],
|
|
77
88
|
token_endpoint_auth_method: this.config.clientSecret ? "client_secret_post" : "none",
|
|
@@ -83,8 +94,8 @@ export class McpOAuthProvider {
|
|
|
83
94
|
}
|
|
84
95
|
return {
|
|
85
96
|
redirect_uris: [redirectUrl],
|
|
86
|
-
client_name: this.config.clientName ?? "
|
|
87
|
-
client_uri: this.config.clientUri
|
|
97
|
+
client_name: this.config.clientName ?? "lunR",
|
|
98
|
+
...(this.config.clientUri ? { client_uri: this.config.clientUri } : {}),
|
|
88
99
|
grant_types: ["authorization_code", "refresh_token"],
|
|
89
100
|
response_types: ["code"],
|
|
90
101
|
token_endpoint_auth_method: this.config.clientSecret ? "client_secret_post" : "none",
|
|
@@ -103,26 +114,28 @@ export class McpOAuthProvider {
|
|
|
103
114
|
client_secret: this.config.clientSecret,
|
|
104
115
|
};
|
|
105
116
|
}
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
client_id: entry.clientInfo.clientId,
|
|
116
|
-
client_secret: entry.clientInfo.clientSecret,
|
|
117
|
-
};
|
|
117
|
+
// An active flow keeps the registration it started with even if another
|
|
118
|
+
// session uses the same server name with a different endpoint.
|
|
119
|
+
if (!this.flowState || !this.flowClientInfoLoaded) {
|
|
120
|
+
const entry = await getAuthForUrl(this.serverName, this.serverUrl);
|
|
121
|
+
throwIfAborted(this.flowState?.signal);
|
|
122
|
+
const info = entry?.clientInfo;
|
|
123
|
+
this.flowClientInfo = info?.clientSecretExpiresAt && info.clientSecretExpiresAt < Date.now() / 1000
|
|
124
|
+
? undefined : info;
|
|
125
|
+
this.flowClientInfoLoaded = true;
|
|
118
126
|
}
|
|
119
|
-
|
|
120
|
-
|
|
127
|
+
if (!this.flowClientInfo)
|
|
128
|
+
return undefined;
|
|
129
|
+
return {
|
|
130
|
+
client_id: this.flowClientInfo.clientId,
|
|
131
|
+
client_secret: this.flowClientInfo.clientSecret,
|
|
132
|
+
};
|
|
121
133
|
}
|
|
122
134
|
/**
|
|
123
135
|
* Save client information from dynamic registration.
|
|
124
136
|
*/
|
|
125
137
|
async saveClientInformation(info) {
|
|
138
|
+
throwIfAborted(this.flowState?.signal);
|
|
126
139
|
const redirectUris = info.redirect_uris ?? (this.redirectUrl ? [this.redirectUrl] : undefined);
|
|
127
140
|
const clientInfo = {
|
|
128
141
|
clientId: info.client_id,
|
|
@@ -131,6 +144,10 @@ export class McpOAuthProvider {
|
|
|
131
144
|
clientSecretExpiresAt: info.client_secret_expires_at,
|
|
132
145
|
redirectUris,
|
|
133
146
|
};
|
|
147
|
+
if (this.flowState) {
|
|
148
|
+
this.flowClientInfo = clientInfo;
|
|
149
|
+
this.flowClientInfoLoaded = true;
|
|
150
|
+
}
|
|
134
151
|
updateClientInfo(this.serverName, clientInfo, this.serverUrl);
|
|
135
152
|
}
|
|
136
153
|
/**
|
|
@@ -156,20 +173,26 @@ export class McpOAuthProvider {
|
|
|
156
173
|
* Save OAuth tokens.
|
|
157
174
|
*/
|
|
158
175
|
async saveTokens(tokens) {
|
|
176
|
+
throwIfAborted(this.flowState?.signal);
|
|
159
177
|
const storedTokens = {
|
|
160
178
|
accessToken: tokens.access_token,
|
|
161
179
|
refreshToken: tokens.refresh_token,
|
|
162
180
|
expiresAt: tokens.expires_in ? Date.now() / 1000 + tokens.expires_in : undefined,
|
|
163
181
|
scope: tokens.scope,
|
|
164
182
|
};
|
|
183
|
+
// Keep the completed flow's registration with its tokens if another
|
|
184
|
+
// same-name endpoint replaced the shared credential entry meanwhile.
|
|
185
|
+
if (this.flowState && this.flowClientInfo) {
|
|
186
|
+
updateClientInfo(this.serverName, this.flowClientInfo, this.serverUrl);
|
|
187
|
+
}
|
|
165
188
|
updateTokens(this.serverName, storedTokens, this.serverUrl);
|
|
166
189
|
}
|
|
167
190
|
/**
|
|
168
191
|
* Redirect the user to the authorization URL.
|
|
169
192
|
* This opens the browser for the user to authenticate.
|
|
170
193
|
*
|
|
171
|
-
* Throws UnauthorizedError when called
|
|
172
|
-
*
|
|
194
|
+
* Throws UnauthorizedError when called without owned or legacy flow state.
|
|
195
|
+
* That path is reached when the SDK
|
|
173
196
|
* falls through from a failed refresh into a fresh authorization_code
|
|
174
197
|
* flow, which library hosts cannot complete in-process.
|
|
175
198
|
*/
|
|
@@ -177,6 +200,11 @@ export class McpOAuthProvider {
|
|
|
177
200
|
if (this.usesClientCredentials) {
|
|
178
201
|
throw new Error("redirectToAuthorization is not used for client_credentials flow");
|
|
179
202
|
}
|
|
203
|
+
throwIfAborted(this.flowState?.signal);
|
|
204
|
+
if (this.flowState) {
|
|
205
|
+
await this.callbacks.onRedirect(authorizationUrl);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
180
208
|
// No saved oauthState means we're on the post-refresh authorize fallback.
|
|
181
209
|
const entry = await getAuthForUrl(this.serverName, this.serverUrl);
|
|
182
210
|
if (!entry?.oauthState) {
|
|
@@ -189,6 +217,11 @@ export class McpOAuthProvider {
|
|
|
189
217
|
* Save the PKCE code verifier.
|
|
190
218
|
*/
|
|
191
219
|
async saveCodeVerifier(codeVerifier) {
|
|
220
|
+
throwIfAborted(this.flowState?.signal);
|
|
221
|
+
if (this.flowState) {
|
|
222
|
+
this.flowState.codeVerifier = codeVerifier;
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
192
225
|
updateCodeVerifier(this.serverName, codeVerifier, this.serverUrl);
|
|
193
226
|
}
|
|
194
227
|
/**
|
|
@@ -196,6 +229,12 @@ export class McpOAuthProvider {
|
|
|
196
229
|
* @throws Error if no code verifier is stored
|
|
197
230
|
*/
|
|
198
231
|
async codeVerifier() {
|
|
232
|
+
throwIfAborted(this.flowState?.signal);
|
|
233
|
+
if (this.flowState) {
|
|
234
|
+
if (!this.flowState.codeVerifier)
|
|
235
|
+
throw new Error(`No code verifier saved for MCP server: ${this.serverName}`);
|
|
236
|
+
return this.flowState.codeVerifier;
|
|
237
|
+
}
|
|
199
238
|
if (this.usesClientCredentials) {
|
|
200
239
|
throw new Error("codeVerifier is not used for client_credentials flow");
|
|
201
240
|
}
|
|
@@ -209,6 +248,11 @@ export class McpOAuthProvider {
|
|
|
209
248
|
* Save the OAuth state parameter for CSRF protection.
|
|
210
249
|
*/
|
|
211
250
|
async saveState(state) {
|
|
251
|
+
throwIfAborted(this.flowState?.signal);
|
|
252
|
+
if (this.flowState) {
|
|
253
|
+
this.flowState.oauthState = state;
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
212
256
|
updateOAuthState(this.serverName, state, this.serverUrl);
|
|
213
257
|
}
|
|
214
258
|
/**
|
|
@@ -216,6 +260,9 @@ export class McpOAuthProvider {
|
|
|
216
260
|
* @throws UnauthorizedError if no flow is in progress (see redirectToAuthorization)
|
|
217
261
|
*/
|
|
218
262
|
async state() {
|
|
263
|
+
throwIfAborted(this.flowState?.signal);
|
|
264
|
+
if (this.flowState)
|
|
265
|
+
return this.flowState.oauthState;
|
|
219
266
|
if (this.usesClientCredentials) {
|
|
220
267
|
throw new Error("state is not used for client_credentials flow");
|
|
221
268
|
}
|
|
@@ -230,6 +277,11 @@ export class McpOAuthProvider {
|
|
|
230
277
|
* Clears tokens, client info, or all credentials based on the type.
|
|
231
278
|
*/
|
|
232
279
|
async invalidateCredentials(type) {
|
|
280
|
+
throwIfAborted(this.flowState?.signal);
|
|
281
|
+
if (this.flowState && type !== "tokens") {
|
|
282
|
+
this.flowClientInfo = undefined;
|
|
283
|
+
this.flowClientInfoLoaded = true;
|
|
284
|
+
}
|
|
233
285
|
switch (type) {
|
|
234
286
|
case "all":
|
|
235
287
|
clearAllCredentials(this.serverName);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-oauth-provider.js","sourceRoot":"","sources":["../../../src/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.ts"],"names":[],"mappings":"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","sourcesContent":["// @ts-nocheck\n/**\n * MCP OAuth Provider\n * \n * Implementation of the MCP SDK's OAuthClientProvider interface.\n * Handles OAuth client registration, token storage, and authorization redirection.\n */\n\nimport type { AddClientAuthentication, OAuthClientProvider } from \"@modelcontextprotocol/sdk/client/auth.js\"\nimport { UnauthorizedError } from \"@modelcontextprotocol/sdk/client/auth.js\"\nimport type {\n OAuthClientMetadata,\n OAuthTokens,\n OAuthClientInformation,\n OAuthClientInformationFull,\n} from \"@modelcontextprotocol/sdk/shared/auth.js\"\nimport {\n getAuthForUrl,\n updateTokens,\n updateClientInfo,\n updateCodeVerifier,\n updateOAuthState,\n clearAllCredentials,\n clearClientInfo,\n clearTokens,\n type StoredTokens,\n type StoredClientInfo,\n} from \"./mcp-auth.ts\"\n\n// Callback server configuration\nconst DEFAULT_OAUTH_CALLBACK_PORT = 19876\nconst DEFAULT_OAUTH_CALLBACK_PATH = \"/callback\"\n\nlet configuredOAuthCallbackPort = DEFAULT_OAUTH_CALLBACK_PORT\n\nif (process.env.MCP_OAUTH_CALLBACK_PORT) {\n const parsedPort = Number.parseInt(process.env.MCP_OAUTH_CALLBACK_PORT, 10)\n if (Number.isInteger(parsedPort) && parsedPort > 0 && parsedPort <= 65535) {\n configuredOAuthCallbackPort = parsedPort\n }\n}\n\nlet oauthCallbackPort = configuredOAuthCallbackPort\nlet oauthCallbackPath = DEFAULT_OAUTH_CALLBACK_PATH\n\nexport function getConfiguredOAuthCallbackPort(): number {\n return configuredOAuthCallbackPort\n}\n\nexport function getOAuthCallbackPort(): number {\n return oauthCallbackPort\n}\n\nexport function setOAuthCallbackPort(port: number): void {\n oauthCallbackPort = port\n}\n\nexport function getOAuthCallbackPath(): string {\n return oauthCallbackPath\n}\n\nexport function setOAuthCallbackPath(path: string): void {\n oauthCallbackPath = path.startsWith(\"/\") ? path : `/${path}`\n}\n\n/** Configuration options for OAuth */\nexport interface McpOAuthConfig {\n grantType?: \"authorization_code\" | \"client_credentials\"\n clientId?: string\n clientSecret?: string\n scope?: string\n redirectUri?: string\n clientName?: string\n clientUri?: string\n}\n\n/** Callbacks for OAuth flow interactions */\nexport interface McpOAuthCallbacks {\n onRedirect: (url: URL) => void | Promise<void>\n}\n\n/**\n * OAuth provider implementation for MCP servers.\n * Implements the OAuthClientProvider interface from the MCP SDK.\n */\nexport class McpOAuthProvider implements OAuthClientProvider {\n private readonly redirectUrlSnapshot: string | undefined\n\n constructor(\n private serverName: string,\n private serverUrl: string,\n private config: McpOAuthConfig,\n private callbacks: McpOAuthCallbacks,\n ) {\n this.redirectUrlSnapshot = config.grantType === \"client_credentials\"\n ? undefined\n : config.redirectUri ?? `http://localhost:${getOAuthCallbackPort()}${getOAuthCallbackPath()}`\n }\n\n private get usesClientCredentials(): boolean {\n return this.config.grantType === \"client_credentials\"\n }\n\n /**\n * The redirect URL for OAuth callbacks.\n * This must match the redirect_uri in client metadata.\n */\n get redirectUrl(): string | undefined {\n return this.redirectUrlSnapshot\n }\n\n /**\n * Client metadata for dynamic registration.\n * Describes this client to the OAuth authorization server.\n */\n get clientMetadata(): OAuthClientMetadata {\n if (this.usesClientCredentials) {\n return {\n client_name: this.config.clientName ?? \"Pi Coding Agent\",\n client_uri: this.config.clientUri ?? \"https://github.com/nicobailon/pi-mcp-adapter\",\n redirect_uris: [],\n grant_types: [\"client_credentials\"],\n token_endpoint_auth_method: this.config.clientSecret ? \"client_secret_post\" : \"none\",\n }\n }\n\n const redirectUrl = this.redirectUrl\n if (!redirectUrl) {\n throw new Error(\"redirectUrl is required for authorization_code flow\")\n }\n\n return {\n redirect_uris: [redirectUrl],\n client_name: this.config.clientName ?? \"Pi Coding Agent\",\n client_uri: this.config.clientUri ?? \"https://github.com/nicobailon/pi-mcp-adapter\",\n grant_types: [\"authorization_code\", \"refresh_token\"],\n response_types: [\"code\"],\n token_endpoint_auth_method: this.config.clientSecret ? \"client_secret_post\" : \"none\",\n ...(this.config.scope !== undefined ? { scope: this.config.scope } : {}),\n }\n }\n\n /**\n * Get client information (for pre-registered or dynamically registered clients).\n * Returns undefined if no client info exists or if the server URL has changed.\n */\n async clientInformation(): Promise<OAuthClientInformation | undefined> {\n // Check config first (pre-registered client)\n if (this.config.clientId) {\n return {\n client_id: this.config.clientId,\n client_secret: this.config.clientSecret,\n }\n }\n\n // Check stored client info (from dynamic registration)\n // Use getAuthForUrl to validate credentials are for the current server URL\n const entry = await getAuthForUrl(this.serverName, this.serverUrl)\n if (entry?.clientInfo) {\n // Check if client secret has expired\n if (entry.clientInfo.clientSecretExpiresAt && entry.clientInfo.clientSecretExpiresAt < Date.now() / 1000) {\n return undefined\n }\n return {\n client_id: entry.clientInfo.clientId,\n client_secret: entry.clientInfo.clientSecret,\n }\n }\n\n // No client info or URL changed - will trigger dynamic registration\n return undefined\n }\n\n /**\n * Save client information from dynamic registration.\n */\n async saveClientInformation(info: OAuthClientInformationFull): Promise<void> {\n const redirectUris = info.redirect_uris ?? (this.redirectUrl ? [this.redirectUrl] : undefined)\n const clientInfo: StoredClientInfo = {\n clientId: info.client_id,\n clientSecret: info.client_secret,\n clientIdIssuedAt: info.client_id_issued_at,\n clientSecretExpiresAt: info.client_secret_expires_at,\n redirectUris,\n }\n updateClientInfo(this.serverName, clientInfo, this.serverUrl)\n }\n\n /**\n * Get stored OAuth tokens.\n * Returns undefined if no tokens exist or if the server URL has changed.\n */\n async tokens(): Promise<OAuthTokens | undefined> {\n // Use getAuthForUrl to validate tokens are for the current server URL\n const entry = await getAuthForUrl(this.serverName, this.serverUrl)\n if (!entry?.tokens) return undefined\n\n return {\n access_token: entry.tokens.accessToken,\n token_type: \"Bearer\",\n refresh_token: entry.tokens.refreshToken,\n expires_in: entry.tokens.expiresAt\n ? Math.max(0, Math.floor(entry.tokens.expiresAt - Date.now() / 1000))\n : undefined,\n scope: entry.tokens.scope,\n }\n }\n\n /**\n * Save OAuth tokens.\n */\n async saveTokens(tokens: OAuthTokens): Promise<void> {\n const storedTokens: StoredTokens = {\n accessToken: tokens.access_token,\n refreshToken: tokens.refresh_token,\n expiresAt: tokens.expires_in ? Date.now() / 1000 + tokens.expires_in : undefined,\n scope: tokens.scope,\n }\n updateTokens(this.serverName, storedTokens, this.serverUrl)\n }\n\n /**\n * Redirect the user to the authorization URL.\n * This opens the browser for the user to authenticate.\n *\n * Throws UnauthorizedError when called outside of a user-initiated flow\n * (no oauthState saved by startAuth). That path is reached when the SDK\n * falls through from a failed refresh into a fresh authorization_code\n * flow, which library hosts cannot complete in-process.\n */\n async redirectToAuthorization(authorizationUrl: URL): Promise<void> {\n if (this.usesClientCredentials) {\n throw new Error(\"redirectToAuthorization is not used for client_credentials flow\")\n }\n // No saved oauthState means we're on the post-refresh authorize fallback.\n const entry = await getAuthForUrl(this.serverName, this.serverUrl)\n if (!entry?.oauthState) {\n throw new UnauthorizedError(\n `Re-authentication required for MCP server: ${this.serverName}`,\n )\n }\n // URL is passed to callback, not logged (may contain sensitive params)\n await this.callbacks.onRedirect(authorizationUrl)\n }\n\n /**\n * Save the PKCE code verifier.\n */\n async saveCodeVerifier(codeVerifier: string): Promise<void> {\n updateCodeVerifier(this.serverName, codeVerifier, this.serverUrl)\n }\n\n /**\n * Get the stored PKCE code verifier.\n * @throws Error if no code verifier is stored\n */\n async codeVerifier(): Promise<string> {\n if (this.usesClientCredentials) {\n throw new Error(\"codeVerifier is not used for client_credentials flow\")\n }\n const entry = await getAuthForUrl(this.serverName, this.serverUrl)\n if (!entry?.codeVerifier) {\n throw new Error(`No code verifier saved for MCP server: ${this.serverName}`)\n }\n return entry.codeVerifier\n }\n\n /**\n * Save the OAuth state parameter for CSRF protection.\n */\n async saveState(state: string): Promise<void> {\n updateOAuthState(this.serverName, state, this.serverUrl)\n }\n\n /**\n * Get the stored OAuth state parameter.\n * @throws UnauthorizedError if no flow is in progress (see redirectToAuthorization)\n */\n async state(): Promise<string> {\n if (this.usesClientCredentials) {\n throw new Error(\"state is not used for client_credentials flow\")\n }\n const entry = await getAuthForUrl(this.serverName, this.serverUrl)\n if (!entry?.oauthState) {\n throw new UnauthorizedError(\n `Re-authentication required for MCP server: ${this.serverName}`,\n )\n }\n return entry.oauthState\n }\n\n /**\n * Invalidate credentials when authentication fails.\n * Clears tokens, client info, or all credentials based on the type.\n */\n async invalidateCredentials(type: \"all\" | \"client\" | \"tokens\"): Promise<void> {\n switch (type) {\n case \"all\":\n clearAllCredentials(this.serverName)\n break\n case \"client\":\n clearClientInfo(this.serverName)\n break\n case \"tokens\":\n clearTokens(this.serverName)\n break\n }\n }\n\n /**\n * Adds configured authorization-code scope without replacing the SDK's\n * default token endpoint authentication behavior.\n */\n addClientAuthentication: AddClientAuthentication = async (headers, params, _url, metadata) => {\n if (params.get(\"grant_type\") === \"authorization_code\" && !params.has(\"scope\") && this.config.scope) {\n params.set(\"scope\", this.config.scope)\n }\n\n const clientInfo = await this.clientInformation()\n if (!clientInfo) {\n return\n }\n\n const supportedMethods = metadata?.token_endpoint_auth_methods_supported ?? []\n const hasClientSecret = clientInfo.client_secret !== undefined\n let authMethod: \"client_secret_basic\" | \"client_secret_post\" | \"none\"\n\n if (supportedMethods.length === 0) {\n authMethod = hasClientSecret ? \"client_secret_post\" : \"none\"\n } else if (hasClientSecret && supportedMethods.includes(\"client_secret_basic\")) {\n authMethod = \"client_secret_basic\"\n } else if (hasClientSecret && supportedMethods.includes(\"client_secret_post\")) {\n authMethod = \"client_secret_post\"\n } else if (supportedMethods.includes(\"none\")) {\n authMethod = \"none\"\n } else {\n authMethod = hasClientSecret ? \"client_secret_post\" : \"none\"\n }\n\n if (authMethod === \"client_secret_basic\") {\n if (!clientInfo.client_secret) {\n throw new Error(\"client_secret_basic authentication requires a client_secret\")\n }\n headers.set(\"Authorization\", `Basic ${Buffer.from(`${clientInfo.client_id}:${clientInfo.client_secret}`).toString(\"base64\")}`)\n return\n }\n\n if (!params.has(\"client_id\")) {\n params.set(\"client_id\", clientInfo.client_id)\n }\n if (authMethod === \"client_secret_post\" && clientInfo.client_secret && !params.has(\"client_secret\")) {\n params.set(\"client_secret\", clientInfo.client_secret)\n }\n }\n\n prepareTokenRequest(scope?: string): URLSearchParams | undefined {\n if (!this.usesClientCredentials) {\n return undefined\n }\n\n const params = new URLSearchParams({ grant_type: \"client_credentials\" })\n const requestedScope = scope ?? this.config.scope\n if (requestedScope) {\n params.set(\"scope\", requestedScope)\n }\n return params\n }\n}\n\nexport { DEFAULT_OAUTH_CALLBACK_PORT, DEFAULT_OAUTH_CALLBACK_PATH }\n"]}
|
|
1
|
+
{"version":3,"file":"mcp-oauth-provider.js","sourceRoot":"","sources":["../../../src/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.ts"],"names":[],"mappings":"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","sourcesContent":["// @ts-nocheck\n/**\n * MCP OAuth Provider\n * \n * Implementation of the MCP SDK's OAuthClientProvider interface.\n * Handles OAuth client registration, token storage, and authorization redirection.\n */\n\nimport type { AddClientAuthentication, OAuthClientProvider } from \"@modelcontextprotocol/sdk/client/auth.js\"\nimport { UnauthorizedError } from \"@modelcontextprotocol/sdk/client/auth.js\"\nimport type {\n OAuthClientMetadata,\n OAuthTokens,\n OAuthClientInformation,\n OAuthClientInformationFull,\n} from \"@modelcontextprotocol/sdk/shared/auth.js\"\nimport {\n getAuthForUrl,\n updateTokens,\n updateClientInfo,\n updateCodeVerifier,\n updateOAuthState,\n clearAllCredentials,\n clearClientInfo,\n clearTokens,\n type StoredTokens,\n type StoredClientInfo,\n} from \"./mcp-auth.ts\"\n\nimport { throwIfAborted } from \"./abort.ts\"\n\n/** Temporary authorization state belongs to one flow, never the credential cache. */\nexport interface McpOAuthFlowState {\n oauthState: string\n codeVerifier?: string\n signal: AbortSignal\n clientInfo?: StoredClientInfo\n}\n\n// Callback server configuration\nconst DEFAULT_OAUTH_CALLBACK_PORT = 19876\nconst DEFAULT_OAUTH_CALLBACK_PATH = \"/callback\"\n\nlet configuredOAuthCallbackPort = DEFAULT_OAUTH_CALLBACK_PORT\n\nif (process.env.MCP_OAUTH_CALLBACK_PORT) {\n const parsedPort = Number.parseInt(process.env.MCP_OAUTH_CALLBACK_PORT, 10)\n if (Number.isInteger(parsedPort) && parsedPort > 0 && parsedPort <= 65535) {\n configuredOAuthCallbackPort = parsedPort\n }\n}\n\nlet oauthCallbackPort = configuredOAuthCallbackPort\nlet oauthCallbackPath = DEFAULT_OAUTH_CALLBACK_PATH\n\nexport function getConfiguredOAuthCallbackPort(): number {\n return configuredOAuthCallbackPort\n}\n\nexport function getOAuthCallbackPort(): number {\n return oauthCallbackPort\n}\n\nexport function setOAuthCallbackPort(port: number): void {\n oauthCallbackPort = port\n}\n\nexport function getOAuthCallbackPath(): string {\n return oauthCallbackPath\n}\n\nexport function setOAuthCallbackPath(path: string): void {\n oauthCallbackPath = path.startsWith(\"/\") ? path : `/${path}`\n}\n\n/** Configuration options for OAuth */\nexport interface McpOAuthConfig {\n grantType?: \"authorization_code\" | \"client_credentials\"\n clientId?: string\n clientSecret?: string\n scope?: string\n redirectUri?: string\n clientName?: string\n clientUri?: string\n}\n\n/** Callbacks for OAuth flow interactions */\nexport interface McpOAuthCallbacks {\n onRedirect: (url: URL) => void | Promise<void>\n}\n\n/**\n * OAuth provider implementation for MCP servers.\n * Implements the OAuthClientProvider interface from the MCP SDK.\n */\nexport class McpOAuthProvider implements OAuthClientProvider {\n private readonly redirectUrlSnapshot: string | undefined\n private flowClientInfo?: StoredClientInfo\n private flowClientInfoLoaded = false\n\n constructor(\n private serverName: string,\n private serverUrl: string,\n private config: McpOAuthConfig,\n private callbacks: McpOAuthCallbacks,\n private flowState?: McpOAuthFlowState,\n ) {\n if (flowState) {\n const info = flowState.clientInfo\n this.flowClientInfo = info?.clientSecretExpiresAt && info.clientSecretExpiresAt < Date.now() / 1000\n ? undefined : info\n this.flowClientInfoLoaded = true\n }\n this.redirectUrlSnapshot = config.grantType === \"client_credentials\"\n ? undefined\n : config.redirectUri ?? `http://localhost:${getOAuthCallbackPort()}${getOAuthCallbackPath()}`\n }\n\n private get usesClientCredentials(): boolean {\n return this.config.grantType === \"client_credentials\"\n }\n\n /**\n * The redirect URL for OAuth callbacks.\n * This must match the redirect_uri in client metadata.\n */\n get redirectUrl(): string | undefined {\n return this.redirectUrlSnapshot\n }\n\n /**\n * Client metadata for dynamic registration.\n * Describes this client to the OAuth authorization server.\n */\n get clientMetadata(): OAuthClientMetadata {\n if (this.usesClientCredentials) {\n return {\n client_name: this.config.clientName ?? \"lunR\",\n ...(this.config.clientUri ? { client_uri: this.config.clientUri } : {}),\n redirect_uris: [],\n grant_types: [\"client_credentials\"],\n token_endpoint_auth_method: this.config.clientSecret ? \"client_secret_post\" : \"none\",\n }\n }\n\n const redirectUrl = this.redirectUrl\n if (!redirectUrl) {\n throw new Error(\"redirectUrl is required for authorization_code flow\")\n }\n\n return {\n redirect_uris: [redirectUrl],\n client_name: this.config.clientName ?? \"lunR\",\n ...(this.config.clientUri ? { client_uri: this.config.clientUri } : {}),\n grant_types: [\"authorization_code\", \"refresh_token\"],\n response_types: [\"code\"],\n token_endpoint_auth_method: this.config.clientSecret ? \"client_secret_post\" : \"none\",\n ...(this.config.scope !== undefined ? { scope: this.config.scope } : {}),\n }\n }\n\n /**\n * Get client information (for pre-registered or dynamically registered clients).\n * Returns undefined if no client info exists or if the server URL has changed.\n */\n async clientInformation(): Promise<OAuthClientInformation | undefined> {\n // Check config first (pre-registered client)\n if (this.config.clientId) {\n return {\n client_id: this.config.clientId,\n client_secret: this.config.clientSecret,\n }\n }\n\n // An active flow keeps the registration it started with even if another\n // session uses the same server name with a different endpoint.\n if (!this.flowState || !this.flowClientInfoLoaded) {\n const entry = await getAuthForUrl(this.serverName, this.serverUrl)\n throwIfAborted(this.flowState?.signal)\n const info = entry?.clientInfo\n this.flowClientInfo = info?.clientSecretExpiresAt && info.clientSecretExpiresAt < Date.now() / 1000\n ? undefined : info\n this.flowClientInfoLoaded = true\n }\n if (!this.flowClientInfo) return undefined\n return {\n client_id: this.flowClientInfo.clientId,\n client_secret: this.flowClientInfo.clientSecret,\n }\n }\n\n /**\n * Save client information from dynamic registration.\n */\n async saveClientInformation(info: OAuthClientInformationFull): Promise<void> {\n throwIfAborted(this.flowState?.signal)\n const redirectUris = info.redirect_uris ?? (this.redirectUrl ? [this.redirectUrl] : undefined)\n const clientInfo: StoredClientInfo = {\n clientId: info.client_id,\n clientSecret: info.client_secret,\n clientIdIssuedAt: info.client_id_issued_at,\n clientSecretExpiresAt: info.client_secret_expires_at,\n redirectUris,\n }\n if (this.flowState) {\n this.flowClientInfo = clientInfo\n this.flowClientInfoLoaded = true\n }\n updateClientInfo(this.serverName, clientInfo, this.serverUrl)\n }\n\n /**\n * Get stored OAuth tokens.\n * Returns undefined if no tokens exist or if the server URL has changed.\n */\n async tokens(): Promise<OAuthTokens | undefined> {\n // Use getAuthForUrl to validate tokens are for the current server URL\n const entry = await getAuthForUrl(this.serverName, this.serverUrl)\n if (!entry?.tokens) return undefined\n\n return {\n access_token: entry.tokens.accessToken,\n token_type: \"Bearer\",\n refresh_token: entry.tokens.refreshToken,\n expires_in: entry.tokens.expiresAt\n ? Math.max(0, Math.floor(entry.tokens.expiresAt - Date.now() / 1000))\n : undefined,\n scope: entry.tokens.scope,\n }\n }\n\n /**\n * Save OAuth tokens.\n */\n async saveTokens(tokens: OAuthTokens): Promise<void> {\n throwIfAborted(this.flowState?.signal)\n const storedTokens: StoredTokens = {\n accessToken: tokens.access_token,\n refreshToken: tokens.refresh_token,\n expiresAt: tokens.expires_in ? Date.now() / 1000 + tokens.expires_in : undefined,\n scope: tokens.scope,\n }\n // Keep the completed flow's registration with its tokens if another\n // same-name endpoint replaced the shared credential entry meanwhile.\n if (this.flowState && this.flowClientInfo) {\n updateClientInfo(this.serverName, this.flowClientInfo, this.serverUrl)\n }\n updateTokens(this.serverName, storedTokens, this.serverUrl)\n }\n\n /**\n * Redirect the user to the authorization URL.\n * This opens the browser for the user to authenticate.\n *\n * Throws UnauthorizedError when called without owned or legacy flow state.\n * That path is reached when the SDK\n * falls through from a failed refresh into a fresh authorization_code\n * flow, which library hosts cannot complete in-process.\n */\n async redirectToAuthorization(authorizationUrl: URL): Promise<void> {\n if (this.usesClientCredentials) {\n throw new Error(\"redirectToAuthorization is not used for client_credentials flow\")\n }\n throwIfAborted(this.flowState?.signal)\n if (this.flowState) {\n await this.callbacks.onRedirect(authorizationUrl)\n return\n }\n // No saved oauthState means we're on the post-refresh authorize fallback.\n const entry = await getAuthForUrl(this.serverName, this.serverUrl)\n if (!entry?.oauthState) {\n throw new UnauthorizedError(\n `Re-authentication required for MCP server: ${this.serverName}`,\n )\n }\n // URL is passed to callback, not logged (may contain sensitive params)\n await this.callbacks.onRedirect(authorizationUrl)\n }\n\n /**\n * Save the PKCE code verifier.\n */\n async saveCodeVerifier(codeVerifier: string): Promise<void> {\n throwIfAborted(this.flowState?.signal)\n if (this.flowState) {\n this.flowState.codeVerifier = codeVerifier\n return\n }\n updateCodeVerifier(this.serverName, codeVerifier, this.serverUrl)\n }\n\n /**\n * Get the stored PKCE code verifier.\n * @throws Error if no code verifier is stored\n */\n async codeVerifier(): Promise<string> {\n throwIfAborted(this.flowState?.signal)\n if (this.flowState) {\n if (!this.flowState.codeVerifier) throw new Error(`No code verifier saved for MCP server: ${this.serverName}`)\n return this.flowState.codeVerifier\n }\n if (this.usesClientCredentials) {\n throw new Error(\"codeVerifier is not used for client_credentials flow\")\n }\n const entry = await getAuthForUrl(this.serverName, this.serverUrl)\n if (!entry?.codeVerifier) {\n throw new Error(`No code verifier saved for MCP server: ${this.serverName}`)\n }\n return entry.codeVerifier\n }\n\n /**\n * Save the OAuth state parameter for CSRF protection.\n */\n async saveState(state: string): Promise<void> {\n throwIfAborted(this.flowState?.signal)\n if (this.flowState) {\n this.flowState.oauthState = state\n return\n }\n updateOAuthState(this.serverName, state, this.serverUrl)\n }\n\n /**\n * Get the stored OAuth state parameter.\n * @throws UnauthorizedError if no flow is in progress (see redirectToAuthorization)\n */\n async state(): Promise<string> {\n throwIfAborted(this.flowState?.signal)\n if (this.flowState) return this.flowState.oauthState\n if (this.usesClientCredentials) {\n throw new Error(\"state is not used for client_credentials flow\")\n }\n const entry = await getAuthForUrl(this.serverName, this.serverUrl)\n if (!entry?.oauthState) {\n throw new UnauthorizedError(\n `Re-authentication required for MCP server: ${this.serverName}`,\n )\n }\n return entry.oauthState\n }\n\n /**\n * Invalidate credentials when authentication fails.\n * Clears tokens, client info, or all credentials based on the type.\n */\n async invalidateCredentials(type: \"all\" | \"client\" | \"tokens\"): Promise<void> {\n throwIfAborted(this.flowState?.signal)\n if (this.flowState && type !== \"tokens\") {\n this.flowClientInfo = undefined\n this.flowClientInfoLoaded = true\n }\n switch (type) {\n case \"all\":\n clearAllCredentials(this.serverName)\n break\n case \"client\":\n clearClientInfo(this.serverName)\n break\n case \"tokens\":\n clearTokens(this.serverName)\n break\n }\n }\n\n /**\n * Adds configured authorization-code scope without replacing the SDK's\n * default token endpoint authentication behavior.\n */\n addClientAuthentication: AddClientAuthentication = async (headers, params, _url, metadata) => {\n if (params.get(\"grant_type\") === \"authorization_code\" && !params.has(\"scope\") && this.config.scope) {\n params.set(\"scope\", this.config.scope)\n }\n\n const clientInfo = await this.clientInformation()\n if (!clientInfo) {\n return\n }\n\n const supportedMethods = metadata?.token_endpoint_auth_methods_supported ?? []\n const hasClientSecret = clientInfo.client_secret !== undefined\n let authMethod: \"client_secret_basic\" | \"client_secret_post\" | \"none\"\n\n if (supportedMethods.length === 0) {\n authMethod = hasClientSecret ? \"client_secret_post\" : \"none\"\n } else if (hasClientSecret && supportedMethods.includes(\"client_secret_basic\")) {\n authMethod = \"client_secret_basic\"\n } else if (hasClientSecret && supportedMethods.includes(\"client_secret_post\")) {\n authMethod = \"client_secret_post\"\n } else if (supportedMethods.includes(\"none\")) {\n authMethod = \"none\"\n } else {\n authMethod = hasClientSecret ? \"client_secret_post\" : \"none\"\n }\n\n if (authMethod === \"client_secret_basic\") {\n if (!clientInfo.client_secret) {\n throw new Error(\"client_secret_basic authentication requires a client_secret\")\n }\n headers.set(\"Authorization\", `Basic ${Buffer.from(`${clientInfo.client_id}:${clientInfo.client_secret}`).toString(\"base64\")}`)\n return\n }\n\n if (!params.has(\"client_id\")) {\n params.set(\"client_id\", clientInfo.client_id)\n }\n if (authMethod === \"client_secret_post\" && clientInfo.client_secret && !params.has(\"client_secret\")) {\n params.set(\"client_secret\", clientInfo.client_secret)\n }\n }\n\n prepareTokenRequest(scope?: string): URLSearchParams | undefined {\n if (!this.usesClientCredentials) {\n return undefined\n }\n\n const params = new URLSearchParams({ grant_type: \"client_credentials\" })\n const requestedScope = scope ?? this.config.scope\n if (requestedScope) {\n params.set(\"scope\", requestedScope)\n }\n return params\n }\n}\n\nexport { DEFAULT_OAUTH_CALLBACK_PORT, DEFAULT_OAUTH_CALLBACK_PATH }\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy-modes.d.ts","sourceRoot":"","sources":["../../../src/builtin-extensions/pi-mcp-adapter/proxy-modes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAGjF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAYpD,KAAK,eAAe,GAAG,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAmGhE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,iBAAiB,GAAG,eAAe,CA4E3E;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,iBAAiB,GAAG,eAAe,CAsDvE;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAoC7G;AAED,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CA+B/H;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,GAAG,eAAe,CAuC3F;AAED,wBAAgB,aAAa,CAC3B,KAAK,EAAE,iBAAiB,EACxB,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,OAAO,EACf,MAAM,CAAC,EAAE,MAAM,EACf,cAAc,CAAC,EAAE,OAAO,GACvB,eAAe,CAwGjB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,GAAG,eAAe,CAqDrF;AAED,wBAAsB,cAAc,CAAC,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC,CAqDjI;AAED,wBAAsB,WAAW,CAC/B,KAAK,EAAE,iBAAiB,EACxB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,cAAc,CAAC,EAAE,MAAM,EACvB,UAAU,CAAC,EAAE,MAAM,QAAQ,EAAE,EAC7B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,eAAe,CAAC,CAyW1B","sourcesContent":["// @ts-nocheck\nimport type { AgentToolResult, ToolInfo } from \"@ashx-j/lunr\";\nimport { UrlElicitationRequiredError } from \"@modelcontextprotocol/sdk/types.js\";\nimport { checkSync } from \"recheck\";\nimport type { McpExtensionState } from \"./state.ts\";\nimport type { ToolMetadata, McpContent } from \"./types.ts\";\nimport { getServerPrefix, parseUiPromptHandoff } from \"./types.ts\";\nimport { lazyConnect, updateServerMetadata, updateMetadataCache, getFailureAgeSeconds, updateStatusBar } from \"./init.ts\";\nimport { abortable, throwIfAborted } from \"./abort.ts\";\nimport { buildToolMetadata, getToolNames, findToolByName, formatSchema } from \"./tool-metadata.ts\";\nimport { resolveMcpResultContent, transformMcpContent } from \"./tool-registrar.ts\";\nimport { guardMcpOutput, guardedMcpDetails, resolveMcpOutputGuardOptions } from \"./mcp-output-guard.ts\";\nimport { maybeStartUiSession, type UiSessionRuntime } from \"./ui-session.ts\";\nimport { formatAuthRequiredMessage, truncateAtWord } from \"./utils.ts\";\nimport { authenticate, completeAuthFromInput, startAuth, supportsOAuth } from \"./mcp-auth-flow.ts\";\n\ntype ProxyToolResult = AgentToolResult<Record<string, unknown>>;\n\nconst MAX_REGEX_SEARCH_QUERY_LENGTH = 256;\nconst REGEX_SAFETY_CHECK_PARAMS = {\n attackTimeout: 50,\n incubationTimeout: 50,\n timeout: 250,\n} as const;\n\ntype AutoAuthResult =\n | { status: \"skipped\" }\n | { status: \"success\" }\n | { status: \"failed\"; message: string };\n\nfunction getAuthRequiredMessage(\n state: McpExtensionState,\n serverName: string,\n defaultMessage = `Server \"${serverName}\" requires OAuth authentication. Run mcp({ action: \"auth-start\", server: \"${serverName}\" }) to get a browser URL, or /mcp-auth ${serverName} in an interactive local session.`,\n): string {\n return formatAuthRequiredMessage(state.config, serverName, defaultMessage);\n}\n\nfunction getAuthFailedMessage(state: McpExtensionState, serverName: string, message: string): string {\n const customGuidance = state.config.settings?.authRequiredMessage;\n if (customGuidance) {\n return `OAuth authentication failed for \"${serverName}\": ${message}. ${getAuthRequiredMessage(state, serverName)}`;\n }\n return `OAuth authentication failed for \"${serverName}\": ${message}. Run mcp({ action: \"auth-start\", server: \"${serverName}\" }) to get a browser URL, or /mcp-auth ${serverName} in an interactive local session.`;\n}\n\nfunction getRedirectPort(authorizationUrl: string): number | undefined {\n try {\n const redirectUri = new URL(authorizationUrl).searchParams.get(\"redirect_uri\");\n if (!redirectUri) return undefined;\n const port = Number.parseInt(new URL(redirectUri).port, 10);\n return Number.isInteger(port) ? port : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction formatManualAuthInstructions(serverName: string, authorizationUrl: string): string {\n const port = getRedirectPort(authorizationUrl);\n const portNote = port\n ? `\\nThe redirect URL will use local port ${port}. On a remote server it is expected for that localhost page to fail locally; copy the address bar URL anyway.`\n : \"\";\n\n return [\n `MCP OAuth required for \"${serverName}\".`,\n \"\",\n \"Open this URL in your local browser:\",\n \"\",\n authorizationUrl,\n \"\",\n \"After approving, copy the full redirected localhost URL from your browser address bar and send it back with:\",\n `mcp({ action: \"auth-complete\", server: \"${serverName}\", args: '{\"redirectUrl\":\"PASTE_REDIRECT_URL_HERE\"}' })`,\n \"\",\n \"You can also pass just the `code` query parameter as `args: '{\\\"code\\\":\\\"PASTE_CODE_HERE\\\"}'`.\",\n portNote.trimEnd(),\n ].filter(Boolean).join(\"\\n\");\n}\n\nasync function attemptAutoAuth(\n state: McpExtensionState,\n serverName: string,\n): Promise<AutoAuthResult> {\n if (state.config.settings?.autoAuth !== true) {\n return { status: \"skipped\" };\n }\n\n const definition = state.config.mcpServers[serverName];\n if (!definition || !supportsOAuth(definition) || !definition.url) {\n return { status: \"skipped\" };\n }\n\n const grantType = definition.oauth ? definition.oauth.grantType ?? \"authorization_code\" : \"authorization_code\";\n if (!state.ui && grantType !== \"client_credentials\") {\n return {\n status: \"failed\",\n message: getAuthRequiredMessage(\n state,\n serverName,\n `Server \"${serverName}\" requires OAuth authentication. Run mcp({ action: \"auth-start\", server: \"${serverName}\" }) to get a browser URL, or /mcp-auth ${serverName} in an interactive local session.`,\n ),\n };\n }\n\n try {\n await authenticate(serverName, definition.url, definition);\n return { status: \"success\" };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n status: \"failed\",\n message: getAuthFailedMessage(state, serverName, message),\n };\n }\n}\n\nexport function executeUiMessages(state: McpExtensionState): ProxyToolResult {\n const sessions = state.completedUiSessions;\n\n if (sessions.length === 0) {\n return {\n content: [{ type: \"text\" as const, text: \"No UI session messages available.\" }],\n details: { sessions: 0 },\n };\n }\n\n const output: string[] = [];\n output.push(`UI Session Messages (${sessions.length} session${sessions.length > 1 ? \"s\" : \"\"}):\\n`);\n\n const allPrompts: string[] = [];\n const allIntents = sessions.flatMap((session) => session.messages.intents);\n const parsedHandoffs: Array<{ intent: string; params: Record<string, unknown>; raw: string }> = [];\n\n for (const session of sessions) {\n const timestamp = session.completedAt.toLocaleTimeString();\n output.push(`\\n## ${session.serverName} / ${session.toolName} (${timestamp}, ${session.reason})`);\n\n const plainPrompts: string[] = [];\n for (const prompt of session.messages.prompts) {\n allPrompts.push(prompt);\n const handoff = parseUiPromptHandoff(prompt);\n if (handoff) {\n parsedHandoffs.push(handoff);\n } else {\n plainPrompts.push(prompt);\n }\n }\n\n if (plainPrompts.length > 0) {\n output.push(\"\\n### Prompts:\");\n for (const prompt of plainPrompts) {\n output.push(`- ${prompt}`);\n }\n }\n\n const intentsForSession = [\n ...session.messages.intents,\n ...session.messages.prompts\n .map((prompt) => parseUiPromptHandoff(prompt))\n .filter((handoff): handoff is NonNullable<typeof handoff> => !!handoff)\n .map((handoff) => ({ intent: handoff.intent, params: handoff.params })),\n ];\n\n if (intentsForSession.length > 0) {\n output.push(\"\\n### Intents:\");\n for (const intent of intentsForSession) {\n const params = intent.params ? ` (${JSON.stringify(intent.params)})` : \"\";\n output.push(`- ${intent.intent}${params}`);\n }\n }\n\n if (session.messages.notifications.length > 0) {\n output.push(\"\\n### Notifications:\");\n for (const notification of session.messages.notifications) {\n output.push(`- ${notification}`);\n }\n }\n }\n\n const count = sessions.length;\n state.completedUiSessions = [];\n\n return {\n content: [{ type: \"text\" as const, text: output.join(\"\\n\") }],\n details: {\n sessions: count,\n prompts: allPrompts,\n intents: [...allIntents, ...parsedHandoffs.map(({ intent, params }) => ({ intent, params }))],\n handoffs: parsedHandoffs,\n cleared: true,\n },\n };\n}\n\nexport function executeStatus(state: McpExtensionState): ProxyToolResult {\n const servers: Array<{ name: string; status: string; toolCount: number; failedAgo: number | null }> = [];\n\n for (const name of Object.keys(state.config.mcpServers)) {\n const connection = state.manager.getConnection(name);\n const metadata = state.toolMetadata.get(name);\n const toolCount = metadata?.length ?? 0;\n const failedAgo = getFailureAgeSeconds(state, name);\n let status = \"not connected\";\n if (connection?.status === \"connected\") {\n status = \"connected\";\n } else if (connection?.status === \"needs-auth\") {\n status = \"needs-auth\";\n } else if (failedAgo !== null) {\n status = \"failed\";\n } else if (metadata !== undefined) {\n status = \"cached\";\n }\n\n servers.push({ name, status, toolCount, failedAgo });\n }\n\n const totalTools = servers.reduce((sum, s) => sum + s.toolCount, 0);\n const connectedCount = servers.filter(s => s.status === \"connected\").length;\n\n let text = `MCP: ${connectedCount}/${servers.length} servers, ${totalTools} tools\\n\\n`;\n for (const server of servers) {\n if (server.status === \"connected\") {\n text += `✓ ${server.name} (${server.toolCount} tools)\\n`;\n continue;\n }\n if (server.status === \"needs-auth\") {\n text += `⚠ ${server.name} (needs auth)\\n`;\n continue;\n }\n if (server.status === \"cached\") {\n text += `○ ${server.name} (${server.toolCount} tools, cached)\\n`;\n continue;\n }\n if (server.status === \"failed\") {\n text += `✗ ${server.name} (failed ${server.failedAgo ?? 0}s ago)\\n`;\n continue;\n }\n text += `○ ${server.name} (not connected)\\n`;\n }\n\n if (servers.length > 0) {\n text += `\\nmcp({ server: \"name\" }) to list tools, mcp({ search: \"...\" }) to search`;\n }\n\n return {\n content: [{ type: \"text\" as const, text: text.trim() }],\n details: { mode: \"status\", servers, totalTools, connectedCount },\n };\n}\n\nexport async function executeAuthStart(state: McpExtensionState, serverName: string): Promise<ProxyToolResult> {\n const definition = state.config.mcpServers[serverName];\n if (!definition) {\n return {\n content: [{ type: \"text\" as const, text: `Server \"${serverName}\" not found. Use mcp({}) to see available servers.` }],\n details: { mode: \"auth-start\", error: \"not_found\", server: serverName },\n };\n }\n\n if (!definition.url || !supportsOAuth(definition)) {\n return {\n content: [{ type: \"text\" as const, text: `Server \"${serverName}\" is not configured for OAuth over HTTP.` }],\n details: { mode: \"auth-start\", error: \"oauth_not_supported\", server: serverName },\n };\n }\n\n try {\n const { authorizationUrl } = await startAuth(serverName, definition.url, definition);\n if (!authorizationUrl) {\n return {\n content: [{ type: \"text\" as const, text: `OAuth authentication successful for \"${serverName}\".` }],\n details: { mode: \"auth-start\", server: serverName, authenticated: true },\n };\n }\n\n return {\n content: [{ type: \"text\" as const, text: formatManualAuthInstructions(serverName, authorizationUrl) }],\n details: { mode: \"auth-start\", server: serverName, authorizationUrl },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: \"text\" as const, text: `Failed to start OAuth for \"${serverName}\": ${message}` }],\n details: { mode: \"auth-start\", error: \"auth_start_failed\", server: serverName, message },\n };\n }\n}\n\nexport async function executeAuthComplete(state: McpExtensionState, serverName: string, input: string): Promise<ProxyToolResult> {\n if (!state.config.mcpServers[serverName]) {\n return {\n content: [{ type: \"text\" as const, text: `Server \"${serverName}\" not found. Use mcp({}) to see available servers.` }],\n details: { mode: \"auth-complete\", error: \"not_found\", server: serverName },\n };\n }\n\n try {\n const status = await completeAuthFromInput(serverName, input);\n if (status !== \"authenticated\") {\n return {\n content: [{ type: \"text\" as const, text: `OAuth authentication did not complete for \"${serverName}\".` }],\n details: { mode: \"auth-complete\", error: \"not_authenticated\", server: serverName, status },\n };\n }\n\n await state.manager.close(serverName);\n state.failureTracker.delete(serverName);\n updateStatusBar(state);\n return {\n content: [{ type: \"text\" as const, text: `OAuth authentication successful for \"${serverName}\". Run mcp({ connect: \"${serverName}\" }) to connect with the new token.` }],\n details: { mode: \"auth-complete\", server: serverName, authenticated: true },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: \"text\" as const, text: `Failed to complete OAuth for \"${serverName}\": ${message}` }],\n details: { mode: \"auth-complete\", error: \"auth_complete_failed\", server: serverName, message },\n };\n }\n}\n\nexport function executeDescribe(state: McpExtensionState, toolName: string): ProxyToolResult {\n let serverName: string | undefined;\n let toolMeta: ToolMetadata | undefined;\n\n for (const [server, metadata] of state.toolMetadata.entries()) {\n const found = findToolByName(metadata, toolName);\n if (found) {\n serverName = server;\n toolMeta = found;\n break;\n }\n }\n\n if (!serverName || !toolMeta) {\n return {\n content: [{ type: \"text\" as const, text: `Tool \"${toolName}\" not found. Use mcp({ search: \"...\" }) to search.` }],\n details: { mode: \"describe\", error: \"tool_not_found\", requestedTool: toolName },\n };\n }\n\n let text = `${toolMeta.name}\\n`;\n text += `Server: ${serverName}\\n`;\n if (toolMeta.resourceUri) {\n text += `Type: Resource (reads from ${toolMeta.resourceUri})\\n`;\n }\n text += `\\n${toolMeta.description || \"(no description)\"}\\n`;\n\n if (toolMeta.inputSchema && !toolMeta.resourceUri) {\n text += `\\nParameters:\\n${formatSchema(toolMeta.inputSchema)}`;\n } else if (toolMeta.resourceUri) {\n text += `\\nNo parameters required (resource tool).`;\n } else {\n text += `\\nNo parameters defined.`;\n }\n\n return {\n content: [{ type: \"text\" as const, text: text.trim() }],\n details: { mode: \"describe\", tool: toolMeta, server: serverName },\n };\n}\n\nexport function executeSearch(\n state: McpExtensionState,\n query: string,\n regex?: boolean,\n server?: string,\n includeSchemas?: boolean,\n): ProxyToolResult {\n const showSchemas = includeSchemas !== false;\n\n const matches: Array<{ server: string; tool: ToolMetadata }> = [];\n\n let pattern: RegExp;\n try {\n if (regex) {\n if (query.length > MAX_REGEX_SEARCH_QUERY_LENGTH) {\n return {\n content: [{ type: \"text\" as const, text: `Regex query is too long; maximum length is ${MAX_REGEX_SEARCH_QUERY_LENGTH} characters.` }],\n details: { mode: \"search\", error: \"query_too_long\", query, maxLength: MAX_REGEX_SEARCH_QUERY_LENGTH },\n };\n }\n\n pattern = new RegExp(query, \"i\");\n let safety;\n try {\n safety = checkSync(query, \"i\", REGEX_SAFETY_CHECK_PARAMS);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: \"text\" as const, text: \"Regex query rejected because safety analysis failed.\" }],\n details: { mode: \"search\", error: \"unsafe_pattern\", query, reason: message },\n };\n }\n if (safety.status !== \"safe\") {\n return {\n content: [{ type: \"text\" as const, text: `Regex query rejected as unsafe (${safety.status}).` }],\n details: { mode: \"search\", error: \"unsafe_pattern\", query, safetyStatus: safety.status },\n };\n }\n } else {\n const terms = query.trim().split(/\\s+/).filter(t => t.length > 0);\n if (terms.length === 0) {\n return {\n content: [{ type: \"text\" as const, text: \"Search query cannot be empty\" }],\n details: { mode: \"search\", error: \"empty_query\" },\n };\n }\n const escaped = terms.map(t => t.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"));\n pattern = new RegExp(escaped.join(\"|\"), \"i\");\n }\n } catch {\n return {\n content: [{ type: \"text\" as const, text: `Invalid regex: ${query}` }],\n details: { mode: \"search\", error: \"invalid_pattern\", query },\n };\n }\n\n for (const [serverName, metadata] of state.toolMetadata.entries()) {\n if (server && serverName !== server) continue;\n for (const tool of metadata) {\n if (pattern.test(tool.name) || pattern.test(tool.description)) {\n matches.push({\n server: serverName,\n tool,\n });\n }\n }\n }\n\n const totalCount = matches.length;\n\n if (totalCount === 0) {\n const msg = server\n ? `No tools matching \"${query}\" in \"${server}\"`\n : `No tools matching \"${query}\"`;\n return {\n content: [{ type: \"text\" as const, text: msg }],\n details: { mode: \"search\", matches: [], count: 0, query },\n };\n }\n\n let text = `Found ${totalCount} tool${totalCount === 1 ? \"\" : \"s\"} matching \"${query}\":\\n\\n`;\n\n for (const match of matches) {\n if (showSchemas) {\n text += `${match.tool.name}\\n`;\n text += ` ${match.tool.description || \"(no description)\"}\\n`;\n if (match.tool.inputSchema && !match.tool.resourceUri) {\n text += `\\n Parameters:\\n${formatSchema(match.tool.inputSchema, \" \")}\\n`;\n } else if (match.tool.resourceUri) {\n text += ` No parameters (resource tool).\\n`;\n }\n text += \"\\n\";\n } else {\n text += `- ${match.tool.name}`;\n if (match.tool.description) {\n text += ` - ${truncateAtWord(match.tool.description, 50)}`;\n }\n text += \"\\n\";\n }\n }\n\n return {\n content: [{ type: \"text\" as const, text: text.trim() }],\n details: {\n mode: \"search\",\n matches: matches.map(m => ({ server: m.server, tool: m.tool.name })),\n count: totalCount,\n query,\n },\n };\n}\n\nexport function executeList(state: McpExtensionState, server: string): ProxyToolResult {\n if (!state.config.mcpServers[server]) {\n return {\n content: [{ type: \"text\" as const, text: `Server \"${server}\" not found. Use mcp({}) to see available servers.` }],\n details: { mode: \"list\", server, tools: [], count: 0, error: \"not_found\" },\n };\n }\n\n const metadata = state.toolMetadata.get(server);\n const toolNames = metadata?.map(m => m.name) ?? [];\n const connection = state.manager.getConnection(server);\n\n if (toolNames.length === 0) {\n if (connection?.status === \"connected\") {\n return {\n content: [{ type: \"text\" as const, text: `Server \"${server}\" has no tools.` }],\n details: { mode: \"list\", server, tools: [], count: 0 },\n };\n }\n if (metadata !== undefined) {\n return {\n content: [{ type: \"text\" as const, text: `Server \"${server}\" has no cached tools (not connected).` }],\n details: { mode: \"list\", server, tools: [], count: 0, cached: true },\n };\n }\n return {\n content: [{ type: \"text\" as const, text: `Server \"${server}\" is configured but not connected. Use mcp({ connect: \"${server}\" }) or /mcp reconnect ${server} to retry.` }],\n details: { mode: \"list\", server, tools: [], count: 0, error: \"not_connected\" },\n };\n }\n\n const cachedNote = connection?.status === \"connected\" ? \"\" : \" (not connected, cached)\";\n let text = `${server} (${toolNames.length} tools${cachedNote}):\\n\\n`;\n\n const descMap = new Map<string, string>();\n if (metadata) {\n for (const m of metadata) {\n descMap.set(m.name, m.description);\n }\n }\n\n for (const tool of toolNames) {\n const desc = descMap.get(tool) ?? \"\";\n const truncated = truncateAtWord(desc, 50);\n text += `- ${tool}`;\n if (truncated) text += ` - ${truncated}`;\n text += \"\\n\";\n }\n\n return {\n content: [{ type: \"text\" as const, text: text.trim() }],\n details: { mode: \"list\", server, tools: toolNames, count: toolNames.length },\n };\n}\n\nexport async function executeConnect(state: McpExtensionState, serverName: string, signal?: AbortSignal): Promise<ProxyToolResult> {\n throwIfAborted(signal);\n const definition = state.config.mcpServers[serverName];\n if (!definition) {\n return {\n content: [{ type: \"text\" as const, text: `Server \"${serverName}\" not found. Use mcp({}) to see available servers.` }],\n details: { mode: \"connect\", error: \"not_found\", server: serverName },\n };\n }\n\n try {\n if (state.ui) {\n state.ui.setStatus(\"mcp\", `MCP: connecting to ${serverName}...`);\n }\n let connection = await state.manager.connect(serverName, definition, signal);\n if (connection.status === \"needs-auth\") {\n const autoAuth = await attemptAutoAuth(state, serverName);\n if (autoAuth.status === \"failed\") {\n return {\n content: [{ type: \"text\" as const, text: autoAuth.message }],\n details: { mode: \"connect\", error: \"auth_required\", server: serverName, message: autoAuth.message },\n };\n }\n if (autoAuth.status === \"success\") {\n await state.manager.close(serverName);\n connection = await state.manager.connect(serverName, definition, signal);\n }\n if (connection.status === \"needs-auth\") {\n const message = getAuthRequiredMessage(state, serverName);\n return {\n content: [{ type: \"text\" as const, text: message }],\n details: { mode: \"connect\", error: \"auth_required\", server: serverName, message },\n };\n }\n }\n const prefix = state.config.settings?.toolPrefix ?? \"server\";\n const { metadata } = buildToolMetadata(connection.tools, connection.resources, definition, serverName, prefix);\n state.toolMetadata.set(serverName, metadata);\n updateMetadataCache(state, serverName);\n state.failureTracker.delete(serverName);\n updateStatusBar(state);\n return executeList(state, serverName);\n } catch (error) {\n if (!signal?.aborted) {\n state.failureTracker.set(serverName, Date.now());\n }\n updateStatusBar(state);\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: \"text\" as const, text: `Failed to connect to \"${serverName}\": ${message}` }],\n details: { mode: \"connect\", error: signal?.aborted ? \"aborted\" : \"connect_failed\", server: serverName, message },\n };\n }\n}\n\nexport async function executeCall(\n state: McpExtensionState,\n toolName: string,\n args?: Record<string, unknown>,\n serverOverride?: string,\n getPiTools?: () => ToolInfo[],\n signal?: AbortSignal,\n): Promise<ProxyToolResult> {\n throwIfAborted(signal);\n let serverName: string | undefined = serverOverride;\n let toolMeta: ToolMetadata | undefined;\n let autoAuthAttempted = false;\n const prefixMode = state.config.settings?.toolPrefix ?? \"server\";\n\n if (serverName && !state.config.mcpServers[serverName]) {\n return {\n content: [{ type: \"text\" as const, text: `Server \"${serverName}\" not found. Use mcp({}) to see available servers.` }],\n details: { mode: \"call\", error: \"server_not_found\", server: serverName },\n };\n }\n\n if (serverName) {\n toolMeta = findToolByName(state.toolMetadata.get(serverName), toolName);\n } else {\n for (const [server, metadata] of state.toolMetadata.entries()) {\n const found = findToolByName(metadata, toolName);\n if (found) {\n serverName = server;\n toolMeta = found;\n break;\n }\n }\n }\n\n if (serverName && !toolMeta) {\n const connected = await lazyConnect(state, serverName, signal);\n if (connected) {\n toolMeta = findToolByName(state.toolMetadata.get(serverName), toolName);\n } else {\n const needsAuthConnection = state.manager.getConnection(serverName);\n if (needsAuthConnection?.status === \"needs-auth\") {\n if (!autoAuthAttempted) {\n autoAuthAttempted = true;\n const autoAuth = await attemptAutoAuth(state, serverName);\n if (autoAuth.status === \"failed\") {\n return {\n content: [{ type: \"text\" as const, text: autoAuth.message }],\n details: { mode: \"call\", error: \"auth_required\", server: serverName, message: autoAuth.message },\n };\n }\n if (autoAuth.status === \"success\") {\n await state.manager.close(serverName);\n state.failureTracker.delete(serverName);\n const connectedAfterAuth = await lazyConnect(state, serverName, signal);\n if (connectedAfterAuth) {\n toolMeta = findToolByName(state.toolMetadata.get(serverName), toolName);\n if (!toolMeta) {\n return {\n content: [{ type: \"text\" as const, text: `Tool \"${toolName}\" not found on \"${serverName}\" after reconnect.` }],\n details: { mode: \"call\", error: \"tool_not_found_after_reconnect\", requestedTool: toolName },\n };\n }\n }\n }\n }\n\n if (!toolMeta && state.manager.getConnection(serverName)?.status === \"needs-auth\") {\n const message = getAuthRequiredMessage(state, serverName);\n return {\n content: [{ type: \"text\" as const, text: message }],\n details: { mode: \"call\", error: \"auth_required\", server: serverName, message },\n };\n }\n }\n\n if (!toolMeta) {\n const failedAgo = getFailureAgeSeconds(state, serverName);\n if (failedAgo !== null) {\n return {\n content: [{ type: \"text\" as const, text: `Server \"${serverName}\" not available (last failed ${failedAgo}s ago)` }],\n details: { mode: \"call\", error: \"server_backoff\", server: serverName },\n };\n }\n }\n }\n }\n\n let prefixMatchedServer: string | undefined;\n\n if (!serverName && !toolMeta && prefixMode !== \"none\") {\n const candidates = Object.keys(state.config.mcpServers)\n .map(name => ({ name, prefix: getServerPrefix(name, prefixMode) }))\n .filter(c => c.prefix && toolName.startsWith(c.prefix + \"_\"))\n .sort((a, b) => b.prefix.length - a.prefix.length);\n\n for (const { name: configuredServer } of candidates) {\n const existingConnection = state.manager.getConnection(configuredServer);\n const failedAgo = getFailureAgeSeconds(state, configuredServer);\n if (failedAgo !== null && existingConnection?.status !== \"needs-auth\") continue;\n\n let connected = await lazyConnect(state, configuredServer, signal);\n if (!connected && state.manager.getConnection(configuredServer)?.status === \"needs-auth\" && !autoAuthAttempted) {\n autoAuthAttempted = true;\n const autoAuth = await attemptAutoAuth(state, configuredServer);\n if (autoAuth.status === \"failed\") {\n return {\n content: [{ type: \"text\" as const, text: autoAuth.message }],\n details: { mode: \"call\", error: \"auth_required\", server: configuredServer, message: autoAuth.message },\n };\n }\n if (autoAuth.status === \"success\") {\n await state.manager.close(configuredServer);\n state.failureTracker.delete(configuredServer);\n connected = await lazyConnect(state, configuredServer, signal);\n }\n }\n\n if (!connected) continue;\n if (!prefixMatchedServer) prefixMatchedServer = configuredServer;\n toolMeta = findToolByName(state.toolMetadata.get(configuredServer), toolName);\n if (toolMeta) {\n serverName = configuredServer;\n break;\n }\n }\n }\n\n if (!serverName || !toolMeta) {\n const nativeTool = !serverOverride\n ? getPiTools?.().find((tool) => tool.name === toolName && tool.name !== \"mcp\")\n : undefined;\n if (nativeTool) {\n return {\n content: [{ type: \"text\" as const, text: `\"${toolName}\" is a native Pi tool. Call ${toolName} directly instead of using mcp({ tool: \"${toolName}\" }).` }],\n details: { mode: \"call\", error: \"native_tool\", requestedTool: toolName },\n };\n }\n\n const hintServer = serverName ?? prefixMatchedServer;\n const available = hintServer ? getToolNames(state, hintServer) : [];\n let msg = `Tool \"${toolName}\" not found.`;\n if (available.length > 0) {\n msg += ` Server \"${hintServer}\" has: ${available.join(\", \")}`;\n } else {\n msg += ` Use mcp({ search: \"...\" }) to search.`;\n }\n return {\n content: [{ type: \"text\" as const, text: msg }],\n details: { mode: \"call\", error: \"tool_not_found\", requestedTool: toolName, hintServer },\n };\n }\n\n let connection = state.manager.getConnection(serverName);\n if (connection?.status === \"needs-auth\") {\n if (!autoAuthAttempted) {\n autoAuthAttempted = true;\n const autoAuth = await attemptAutoAuth(state, serverName);\n if (autoAuth.status === \"failed\") {\n return {\n content: [{ type: \"text\" as const, text: autoAuth.message }],\n details: { mode: \"call\", error: \"auth_required\", server: serverName, message: autoAuth.message },\n };\n }\n if (autoAuth.status === \"success\") {\n await state.manager.close(serverName);\n state.failureTracker.delete(serverName);\n connection = state.manager.getConnection(serverName);\n }\n }\n\n if (connection?.status === \"needs-auth\") {\n const message = getAuthRequiredMessage(state, serverName);\n return {\n content: [{ type: \"text\" as const, text: message }],\n details: { mode: \"call\", error: \"auth_required\", server: serverName, message },\n };\n }\n }\n if (!connection || connection.status !== \"connected\") {\n const failedAgo = getFailureAgeSeconds(state, serverName);\n if (failedAgo !== null) {\n return {\n content: [{ type: \"text\" as const, text: `Server \"${serverName}\" not available (last failed ${failedAgo}s ago)` }],\n details: { mode: \"call\", error: \"server_backoff\", server: serverName },\n };\n }\n\n const definition = state.config.mcpServers[serverName];\n if (!definition) {\n return {\n content: [{ type: \"text\" as const, text: `Server \"${serverName}\" not connected` }],\n details: { mode: \"call\", error: \"server_not_connected\", server: serverName },\n };\n }\n\n try {\n if (state.ui) {\n state.ui.setStatus(\"mcp\", `MCP: connecting to ${serverName}...`);\n }\n connection = await state.manager.connect(serverName, definition, signal);\n if (connection.status === \"needs-auth\") {\n if (!autoAuthAttempted) {\n autoAuthAttempted = true;\n const autoAuth = await attemptAutoAuth(state, serverName);\n if (autoAuth.status === \"failed\") {\n return {\n content: [{ type: \"text\" as const, text: autoAuth.message }],\n details: { mode: \"call\", error: \"auth_required\", server: serverName, message: autoAuth.message },\n };\n }\n if (autoAuth.status === \"success\") {\n await state.manager.close(serverName);\n connection = await state.manager.connect(serverName, definition, signal);\n }\n }\n\n if (connection.status === \"needs-auth\") {\n const message = getAuthRequiredMessage(state, serverName);\n return {\n content: [{ type: \"text\" as const, text: message }],\n details: { mode: \"call\", error: \"auth_required\", server: serverName, message },\n };\n }\n }\n state.failureTracker.delete(serverName);\n updateServerMetadata(state, serverName);\n updateMetadataCache(state, serverName);\n updateStatusBar(state);\n toolMeta = findToolByName(state.toolMetadata.get(serverName), toolName);\n if (!toolMeta) {\n const available = getToolNames(state, serverName);\n const hint = available.length > 0\n ? `Available tools on \"${serverName}\": ${available.join(\", \")}`\n : `Server \"${serverName}\" has no tools.`;\n return {\n content: [{ type: \"text\" as const, text: `Tool \"${toolName}\" not found on \"${serverName}\" after reconnect. ${hint}` }],\n details: { mode: \"call\", error: \"tool_not_found_after_reconnect\", requestedTool: toolName },\n };\n }\n } catch (error) {\n if (!signal?.aborted) {\n state.failureTracker.set(serverName, Date.now());\n }\n updateStatusBar(state);\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: \"text\" as const, text: `Failed to connect to \"${serverName}\": ${message}` }],\n details: { mode: \"call\", error: signal?.aborted ? \"aborted\" : \"connect_failed\", message },\n };\n }\n }\n\n let uiSession: UiSessionRuntime | null = null;\n const requestOptions = state.manager.getRequestOptions?.(serverName, signal) ?? (signal ? { signal } : undefined);\n\n const outputGuardOptions = resolveMcpOutputGuardOptions(state.config.settings);\n\n try {\n state.manager.touch(serverName);\n state.manager.incrementInFlight(serverName);\n\n if (toolMeta.resourceUri) {\n const result = await connection.client.readResource({ uri: toolMeta.resourceUri }, requestOptions);\n const content = (result.contents ?? []).map(c => ({\n type: \"text\" as const,\n text: \"text\" in c ? c.text : (\"blob\" in c ? `[Binary data: ${(c as { mimeType?: string }).mimeType ?? \"unknown\"}]` : JSON.stringify(c)),\n }));\n const guarded = await guardMcpOutput(content.length > 0 ? content : [{ type: \"text\" as const, text: \"(empty resource)\" }], outputGuardOptions);\n return {\n content: guarded.content,\n details: { mode: \"call\", resourceUri: toolMeta.resourceUri, server: serverName, ...guardedMcpDetails(guarded) },\n };\n }\n\n uiSession = toolMeta.uiResourceUri\n ? await maybeStartUiSession(state, {\n serverName,\n toolName: toolMeta.originalName,\n toolArgs: args ?? {},\n uiResourceUri: toolMeta.uiResourceUri,\n streamMode: toolMeta.uiStreamMode,\n })\n : null;\n\n const resultPromise = connection.client.callTool({\n name: toolMeta.originalName,\n arguments: args ?? {},\n _meta: uiSession?.requestMeta,\n }, undefined, requestOptions);\n\n if (toolMeta.uiResourceUri) {\n const result = await abortable(resultPromise, signal);\n uiSession?.sendToolResult(result as unknown as import(\"@modelcontextprotocol/sdk/types.js\").CallToolResult);\n\n if (result.isError) {\n const mcpContent = (result.content ?? []) as McpContent[];\n const content = transformMcpContent(mcpContent);\n const outputContent = content.length > 0 ? content : [{ type: \"text\" as const, text: \"(empty result)\" }];\n const schemaText = toolMeta.inputSchema ? `\\n\\nExpected parameters:\\n${formatSchema(toolMeta.inputSchema)}` : \"\";\n const guarded = await guardMcpOutput(outputContent, { ...outputGuardOptions, prefix: \"Error: \", suffix: schemaText, emptyTextFallback: \"Tool execution failed\", rawMcpResult: result });\n return {\n content: guarded.content,\n details: { mode: \"call\", error: \"tool_error\", ...guardedMcpDetails(guarded) },\n };\n }\n\n const content = resolveMcpResultContent(result as Record<string, unknown>);\n const outputContent = content.length > 0 ? content : [{ type: \"text\" as const, text: \"(empty result)\" }];\n const uiMessage = uiSession?.reused\n ? \"Updated the open UI.\"\n : \"📺 Interactive UI is now open in your browser. I'll respond to your prompts and intents as you interact with it.\";\n const guarded = await guardMcpOutput(outputContent, { ...outputGuardOptions, suffix: `\\n\\n${uiMessage}`, rawMcpResult: result });\n return {\n content: guarded.content,\n details: { mode: \"call\", ...guardedMcpDetails(guarded), server: serverName, tool: toolMeta.originalName, uiOpen: true },\n };\n }\n\n const result = await abortable(resultPromise, signal);\n\n if (result.isError) {\n const mcpContent = (result.content ?? []) as McpContent[];\n const content = transformMcpContent(mcpContent);\n const outputContent = content.length > 0 ? content : [{ type: \"text\" as const, text: \"(empty result)\" }];\n const schemaText = toolMeta.inputSchema ? `\\n\\nExpected parameters:\\n${formatSchema(toolMeta.inputSchema)}` : \"\";\n const guarded = await guardMcpOutput(outputContent, { ...outputGuardOptions, prefix: \"Error: \", suffix: schemaText, emptyTextFallback: \"Tool execution failed\", rawMcpResult: result });\n return {\n content: guarded.content,\n details: { mode: \"call\", error: \"tool_error\", ...guardedMcpDetails(guarded) },\n };\n }\n\n const content = resolveMcpResultContent(result as Record<string, unknown>);\n const outputContent = content.length > 0 ? content : [{ type: \"text\" as const, text: \"(empty result)\" }];\n const guarded = await guardMcpOutput(outputContent, { ...outputGuardOptions, rawMcpResult: result });\n return {\n content: guarded.content,\n details: { mode: \"call\", ...guardedMcpDetails(guarded), server: serverName, tool: toolMeta.originalName },\n };\n } catch (error) {\n if (error instanceof UrlElicitationRequiredError) {\n const action = await state.manager.handleUrlElicitationRequired(serverName, error);\n const message = action === \"accept\"\n ? \"The original MCP tool did not run. Complete the opened browser interaction, then retry the tool.\"\n : `The URL interaction was ${action === \"decline\" ? \"declined\" : \"cancelled\"}.`;\n uiSession?.sendToolCancelled(message);\n return {\n content: [{ type: \"text\" as const, text: message }],\n details: { mode: \"call\", error: \"url_elicitation_required\", server: serverName, action },\n };\n }\n const message = error instanceof Error ? error.message : String(error);\n uiSession?.sendToolCancelled(message);\n\n const schemaText = toolMeta.inputSchema ? `\\n\\nExpected parameters:\\n${formatSchema(toolMeta.inputSchema)}` : \"\";\n const guarded = await guardMcpOutput([{ type: \"text\" as const, text: message }], { ...outputGuardOptions, prefix: \"Failed to call tool: \", suffix: schemaText });\n\n return {\n content: guarded.content,\n details: { mode: \"call\", error: \"call_failed\", message: guarded.outputGuard ? \"output truncated; see outputGuard.fullOutputPath\" : message, ...guardedMcpDetails(guarded) },\n };\n } finally {\n if (uiSession?.reused) {\n uiSession.close();\n }\n state.manager.decrementInFlight(serverName);\n state.manager.touch(serverName);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"proxy-modes.d.ts","sourceRoot":"","sources":["../../../src/builtin-extensions/pi-mcp-adapter/proxy-modes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAGjF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAYpD,KAAK,eAAe,GAAG,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAmGhE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,iBAAiB,GAAG,eAAe,CA4E3E;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,iBAAiB,GAAG,eAAe,CAsDvE;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAoC7G;AAED,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CA+B/H;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,GAAG,eAAe,CAuC3F;AAED,wBAAgB,aAAa,CAC3B,KAAK,EAAE,iBAAiB,EACxB,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,OAAO,EACf,MAAM,CAAC,EAAE,MAAM,EACf,cAAc,CAAC,EAAE,OAAO,GACvB,eAAe,CAwGjB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,GAAG,eAAe,CAqDrF;AAED,wBAAsB,cAAc,CAAC,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC,CAqDjI;AAED,wBAAsB,WAAW,CAC/B,KAAK,EAAE,iBAAiB,EACxB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,cAAc,CAAC,EAAE,MAAM,EACvB,UAAU,CAAC,EAAE,MAAM,QAAQ,EAAE,EAC7B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,eAAe,CAAC,CAyW1B","sourcesContent":["// @ts-nocheck\nimport type { AgentToolResult, ToolInfo } from \"@ashx-j/lunr\";\nimport { UrlElicitationRequiredError } from \"@modelcontextprotocol/sdk/types.js\";\nimport { checkSync } from \"recheck\";\nimport type { McpExtensionState } from \"./state.ts\";\nimport type { ToolMetadata, McpContent } from \"./types.ts\";\nimport { getServerPrefix, parseUiPromptHandoff } from \"./types.ts\";\nimport { lazyConnect, updateServerMetadata, updateMetadataCache, getFailureAgeSeconds, updateStatusBar } from \"./init.ts\";\nimport { abortable, throwIfAborted } from \"./abort.ts\";\nimport { buildToolMetadata, getToolNames, findToolByName, formatSchema } from \"./tool-metadata.ts\";\nimport { resolveMcpResultContent, transformMcpContent } from \"./tool-registrar.ts\";\nimport { guardMcpOutput, guardedMcpDetails, resolveMcpOutputGuardOptions } from \"./mcp-output-guard.ts\";\nimport { maybeStartUiSession, type UiSessionRuntime } from \"./ui-session.ts\";\nimport { formatAuthRequiredMessage, truncateAtWord } from \"./utils.ts\";\nimport { supportsOAuth } from \"./mcp-auth-flow.ts\";\n\ntype ProxyToolResult = AgentToolResult<Record<string, unknown>>;\n\nconst MAX_REGEX_SEARCH_QUERY_LENGTH = 256;\nconst REGEX_SAFETY_CHECK_PARAMS = {\n attackTimeout: 50,\n incubationTimeout: 50,\n timeout: 250,\n} as const;\n\ntype AutoAuthResult =\n | { status: \"skipped\" }\n | { status: \"success\" }\n | { status: \"failed\"; message: string };\n\nfunction getAuthRequiredMessage(\n state: McpExtensionState,\n serverName: string,\n defaultMessage = `Server \"${serverName}\" requires OAuth authentication. Run mcp({ action: \"auth-start\", server: \"${serverName}\" }) to get a browser URL, or /mcp-auth ${serverName} in an interactive local session.`,\n): string {\n return formatAuthRequiredMessage(state.config, serverName, defaultMessage);\n}\n\nfunction getAuthFailedMessage(state: McpExtensionState, serverName: string, message: string): string {\n const customGuidance = state.config.settings?.authRequiredMessage;\n if (customGuidance) {\n return `OAuth authentication failed for \"${serverName}\": ${message}. ${getAuthRequiredMessage(state, serverName)}`;\n }\n return `OAuth authentication failed for \"${serverName}\": ${message}. Run mcp({ action: \"auth-start\", server: \"${serverName}\" }) to get a browser URL, or /mcp-auth ${serverName} in an interactive local session.`;\n}\n\nfunction getRedirectPort(authorizationUrl: string): number | undefined {\n try {\n const redirectUri = new URL(authorizationUrl).searchParams.get(\"redirect_uri\");\n if (!redirectUri) return undefined;\n const port = Number.parseInt(new URL(redirectUri).port, 10);\n return Number.isInteger(port) ? port : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction formatManualAuthInstructions(serverName: string, authorizationUrl: string): string {\n const port = getRedirectPort(authorizationUrl);\n const portNote = port\n ? `\\nThe redirect URL will use local port ${port}. On a remote server it is expected for that localhost page to fail locally; copy the address bar URL anyway.`\n : \"\";\n\n return [\n `MCP OAuth required for \"${serverName}\".`,\n \"\",\n \"Open this URL in your local browser:\",\n \"\",\n authorizationUrl,\n \"\",\n \"After approving, copy the full redirected localhost URL from your browser address bar and send it back with:\",\n `mcp({ action: \"auth-complete\", server: \"${serverName}\", args: '{\"redirectUrl\":\"PASTE_REDIRECT_URL_HERE\"}' })`,\n \"\",\n \"You can also pass just the `code` query parameter as `args: '{\\\"code\\\":\\\"PASTE_CODE_HERE\\\"}'`.\",\n portNote.trimEnd(),\n ].filter(Boolean).join(\"\\n\");\n}\n\nasync function attemptAutoAuth(\n state: McpExtensionState,\n serverName: string,\n): Promise<AutoAuthResult> {\n if (state.config.settings?.autoAuth !== true) {\n return { status: \"skipped\" };\n }\n\n const definition = state.config.mcpServers[serverName];\n if (!definition || !supportsOAuth(definition) || !definition.url) {\n return { status: \"skipped\" };\n }\n\n const grantType = definition.oauth ? definition.oauth.grantType ?? \"authorization_code\" : \"authorization_code\";\n if (!state.ui && grantType !== \"client_credentials\") {\n return {\n status: \"failed\",\n message: getAuthRequiredMessage(\n state,\n serverName,\n `Server \"${serverName}\" requires OAuth authentication. Run mcp({ action: \"auth-start\", server: \"${serverName}\" }) to get a browser URL, or /mcp-auth ${serverName} in an interactive local session.`,\n ),\n };\n }\n\n try {\n await state.authFlow.authenticate(serverName, definition.url, definition);\n return { status: \"success\" };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n status: \"failed\",\n message: getAuthFailedMessage(state, serverName, message),\n };\n }\n}\n\nexport function executeUiMessages(state: McpExtensionState): ProxyToolResult {\n const sessions = state.completedUiSessions;\n\n if (sessions.length === 0) {\n return {\n content: [{ type: \"text\" as const, text: \"No UI session messages available.\" }],\n details: { sessions: 0 },\n };\n }\n\n const output: string[] = [];\n output.push(`UI Session Messages (${sessions.length} session${sessions.length > 1 ? \"s\" : \"\"}):\\n`);\n\n const allPrompts: string[] = [];\n const allIntents = sessions.flatMap((session) => session.messages.intents);\n const parsedHandoffs: Array<{ intent: string; params: Record<string, unknown>; raw: string }> = [];\n\n for (const session of sessions) {\n const timestamp = session.completedAt.toLocaleTimeString();\n output.push(`\\n## ${session.serverName} / ${session.toolName} (${timestamp}, ${session.reason})`);\n\n const plainPrompts: string[] = [];\n for (const prompt of session.messages.prompts) {\n allPrompts.push(prompt);\n const handoff = parseUiPromptHandoff(prompt);\n if (handoff) {\n parsedHandoffs.push(handoff);\n } else {\n plainPrompts.push(prompt);\n }\n }\n\n if (plainPrompts.length > 0) {\n output.push(\"\\n### Prompts:\");\n for (const prompt of plainPrompts) {\n output.push(`- ${prompt}`);\n }\n }\n\n const intentsForSession = [\n ...session.messages.intents,\n ...session.messages.prompts\n .map((prompt) => parseUiPromptHandoff(prompt))\n .filter((handoff): handoff is NonNullable<typeof handoff> => !!handoff)\n .map((handoff) => ({ intent: handoff.intent, params: handoff.params })),\n ];\n\n if (intentsForSession.length > 0) {\n output.push(\"\\n### Intents:\");\n for (const intent of intentsForSession) {\n const params = intent.params ? ` (${JSON.stringify(intent.params)})` : \"\";\n output.push(`- ${intent.intent}${params}`);\n }\n }\n\n if (session.messages.notifications.length > 0) {\n output.push(\"\\n### Notifications:\");\n for (const notification of session.messages.notifications) {\n output.push(`- ${notification}`);\n }\n }\n }\n\n const count = sessions.length;\n state.completedUiSessions = [];\n\n return {\n content: [{ type: \"text\" as const, text: output.join(\"\\n\") }],\n details: {\n sessions: count,\n prompts: allPrompts,\n intents: [...allIntents, ...parsedHandoffs.map(({ intent, params }) => ({ intent, params }))],\n handoffs: parsedHandoffs,\n cleared: true,\n },\n };\n}\n\nexport function executeStatus(state: McpExtensionState): ProxyToolResult {\n const servers: Array<{ name: string; status: string; toolCount: number; failedAgo: number | null }> = [];\n\n for (const name of Object.keys(state.config.mcpServers)) {\n const connection = state.manager.getConnection(name);\n const metadata = state.toolMetadata.get(name);\n const toolCount = metadata?.length ?? 0;\n const failedAgo = getFailureAgeSeconds(state, name);\n let status = \"not connected\";\n if (connection?.status === \"connected\") {\n status = \"connected\";\n } else if (connection?.status === \"needs-auth\") {\n status = \"needs-auth\";\n } else if (failedAgo !== null) {\n status = \"failed\";\n } else if (metadata !== undefined) {\n status = \"cached\";\n }\n\n servers.push({ name, status, toolCount, failedAgo });\n }\n\n const totalTools = servers.reduce((sum, s) => sum + s.toolCount, 0);\n const connectedCount = servers.filter(s => s.status === \"connected\").length;\n\n let text = `MCP: ${connectedCount}/${servers.length} servers, ${totalTools} tools\\n\\n`;\n for (const server of servers) {\n if (server.status === \"connected\") {\n text += `✓ ${server.name} (${server.toolCount} tools)\\n`;\n continue;\n }\n if (server.status === \"needs-auth\") {\n text += `⚠ ${server.name} (needs auth)\\n`;\n continue;\n }\n if (server.status === \"cached\") {\n text += `○ ${server.name} (${server.toolCount} tools, cached)\\n`;\n continue;\n }\n if (server.status === \"failed\") {\n text += `✗ ${server.name} (failed ${server.failedAgo ?? 0}s ago)\\n`;\n continue;\n }\n text += `○ ${server.name} (not connected)\\n`;\n }\n\n if (servers.length > 0) {\n text += `\\nmcp({ server: \"name\" }) to list tools, mcp({ search: \"...\" }) to search`;\n }\n\n return {\n content: [{ type: \"text\" as const, text: text.trim() }],\n details: { mode: \"status\", servers, totalTools, connectedCount },\n };\n}\n\nexport async function executeAuthStart(state: McpExtensionState, serverName: string): Promise<ProxyToolResult> {\n const definition = state.config.mcpServers[serverName];\n if (!definition) {\n return {\n content: [{ type: \"text\" as const, text: `Server \"${serverName}\" not found. Use mcp({}) to see available servers.` }],\n details: { mode: \"auth-start\", error: \"not_found\", server: serverName },\n };\n }\n\n if (!definition.url || !supportsOAuth(definition)) {\n return {\n content: [{ type: \"text\" as const, text: `Server \"${serverName}\" is not configured for OAuth over HTTP.` }],\n details: { mode: \"auth-start\", error: \"oauth_not_supported\", server: serverName },\n };\n }\n\n try {\n const { authorizationUrl } = await state.authFlow.startAuth(serverName, definition.url, definition);\n if (!authorizationUrl) {\n return {\n content: [{ type: \"text\" as const, text: `OAuth authentication successful for \"${serverName}\".` }],\n details: { mode: \"auth-start\", server: serverName, authenticated: true },\n };\n }\n\n return {\n content: [{ type: \"text\" as const, text: formatManualAuthInstructions(serverName, authorizationUrl) }],\n details: { mode: \"auth-start\", server: serverName, authorizationUrl },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: \"text\" as const, text: `Failed to start OAuth for \"${serverName}\": ${message}` }],\n details: { mode: \"auth-start\", error: \"auth_start_failed\", server: serverName, message },\n };\n }\n}\n\nexport async function executeAuthComplete(state: McpExtensionState, serverName: string, input: string): Promise<ProxyToolResult> {\n if (!state.config.mcpServers[serverName]) {\n return {\n content: [{ type: \"text\" as const, text: `Server \"${serverName}\" not found. Use mcp({}) to see available servers.` }],\n details: { mode: \"auth-complete\", error: \"not_found\", server: serverName },\n };\n }\n\n try {\n const status = await state.authFlow.completeAuthFromInput(serverName, input);\n if (status !== \"authenticated\") {\n return {\n content: [{ type: \"text\" as const, text: `OAuth authentication did not complete for \"${serverName}\".` }],\n details: { mode: \"auth-complete\", error: \"not_authenticated\", server: serverName, status },\n };\n }\n\n await state.manager.close(serverName);\n state.failureTracker.delete(serverName);\n updateStatusBar(state);\n return {\n content: [{ type: \"text\" as const, text: `OAuth authentication successful for \"${serverName}\". Run mcp({ connect: \"${serverName}\" }) to connect with the new token.` }],\n details: { mode: \"auth-complete\", server: serverName, authenticated: true },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: \"text\" as const, text: `Failed to complete OAuth for \"${serverName}\": ${message}` }],\n details: { mode: \"auth-complete\", error: \"auth_complete_failed\", server: serverName, message },\n };\n }\n}\n\nexport function executeDescribe(state: McpExtensionState, toolName: string): ProxyToolResult {\n let serverName: string | undefined;\n let toolMeta: ToolMetadata | undefined;\n\n for (const [server, metadata] of state.toolMetadata.entries()) {\n const found = findToolByName(metadata, toolName);\n if (found) {\n serverName = server;\n toolMeta = found;\n break;\n }\n }\n\n if (!serverName || !toolMeta) {\n return {\n content: [{ type: \"text\" as const, text: `Tool \"${toolName}\" not found. Use mcp({ search: \"...\" }) to search.` }],\n details: { mode: \"describe\", error: \"tool_not_found\", requestedTool: toolName },\n };\n }\n\n let text = `${toolMeta.name}\\n`;\n text += `Server: ${serverName}\\n`;\n if (toolMeta.resourceUri) {\n text += `Type: Resource (reads from ${toolMeta.resourceUri})\\n`;\n }\n text += `\\n${toolMeta.description || \"(no description)\"}\\n`;\n\n if (toolMeta.inputSchema && !toolMeta.resourceUri) {\n text += `\\nParameters:\\n${formatSchema(toolMeta.inputSchema)}`;\n } else if (toolMeta.resourceUri) {\n text += `\\nNo parameters required (resource tool).`;\n } else {\n text += `\\nNo parameters defined.`;\n }\n\n return {\n content: [{ type: \"text\" as const, text: text.trim() }],\n details: { mode: \"describe\", tool: toolMeta, server: serverName },\n };\n}\n\nexport function executeSearch(\n state: McpExtensionState,\n query: string,\n regex?: boolean,\n server?: string,\n includeSchemas?: boolean,\n): ProxyToolResult {\n const showSchemas = includeSchemas !== false;\n\n const matches: Array<{ server: string; tool: ToolMetadata }> = [];\n\n let pattern: RegExp;\n try {\n if (regex) {\n if (query.length > MAX_REGEX_SEARCH_QUERY_LENGTH) {\n return {\n content: [{ type: \"text\" as const, text: `Regex query is too long; maximum length is ${MAX_REGEX_SEARCH_QUERY_LENGTH} characters.` }],\n details: { mode: \"search\", error: \"query_too_long\", query, maxLength: MAX_REGEX_SEARCH_QUERY_LENGTH },\n };\n }\n\n pattern = new RegExp(query, \"i\");\n let safety;\n try {\n safety = checkSync(query, \"i\", REGEX_SAFETY_CHECK_PARAMS);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: \"text\" as const, text: \"Regex query rejected because safety analysis failed.\" }],\n details: { mode: \"search\", error: \"unsafe_pattern\", query, reason: message },\n };\n }\n if (safety.status !== \"safe\") {\n return {\n content: [{ type: \"text\" as const, text: `Regex query rejected as unsafe (${safety.status}).` }],\n details: { mode: \"search\", error: \"unsafe_pattern\", query, safetyStatus: safety.status },\n };\n }\n } else {\n const terms = query.trim().split(/\\s+/).filter(t => t.length > 0);\n if (terms.length === 0) {\n return {\n content: [{ type: \"text\" as const, text: \"Search query cannot be empty\" }],\n details: { mode: \"search\", error: \"empty_query\" },\n };\n }\n const escaped = terms.map(t => t.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"));\n pattern = new RegExp(escaped.join(\"|\"), \"i\");\n }\n } catch {\n return {\n content: [{ type: \"text\" as const, text: `Invalid regex: ${query}` }],\n details: { mode: \"search\", error: \"invalid_pattern\", query },\n };\n }\n\n for (const [serverName, metadata] of state.toolMetadata.entries()) {\n if (server && serverName !== server) continue;\n for (const tool of metadata) {\n if (pattern.test(tool.name) || pattern.test(tool.description)) {\n matches.push({\n server: serverName,\n tool,\n });\n }\n }\n }\n\n const totalCount = matches.length;\n\n if (totalCount === 0) {\n const msg = server\n ? `No tools matching \"${query}\" in \"${server}\"`\n : `No tools matching \"${query}\"`;\n return {\n content: [{ type: \"text\" as const, text: msg }],\n details: { mode: \"search\", matches: [], count: 0, query },\n };\n }\n\n let text = `Found ${totalCount} tool${totalCount === 1 ? \"\" : \"s\"} matching \"${query}\":\\n\\n`;\n\n for (const match of matches) {\n if (showSchemas) {\n text += `${match.tool.name}\\n`;\n text += ` ${match.tool.description || \"(no description)\"}\\n`;\n if (match.tool.inputSchema && !match.tool.resourceUri) {\n text += `\\n Parameters:\\n${formatSchema(match.tool.inputSchema, \" \")}\\n`;\n } else if (match.tool.resourceUri) {\n text += ` No parameters (resource tool).\\n`;\n }\n text += \"\\n\";\n } else {\n text += `- ${match.tool.name}`;\n if (match.tool.description) {\n text += ` - ${truncateAtWord(match.tool.description, 50)}`;\n }\n text += \"\\n\";\n }\n }\n\n return {\n content: [{ type: \"text\" as const, text: text.trim() }],\n details: {\n mode: \"search\",\n matches: matches.map(m => ({ server: m.server, tool: m.tool.name })),\n count: totalCount,\n query,\n },\n };\n}\n\nexport function executeList(state: McpExtensionState, server: string): ProxyToolResult {\n if (!state.config.mcpServers[server]) {\n return {\n content: [{ type: \"text\" as const, text: `Server \"${server}\" not found. Use mcp({}) to see available servers.` }],\n details: { mode: \"list\", server, tools: [], count: 0, error: \"not_found\" },\n };\n }\n\n const metadata = state.toolMetadata.get(server);\n const toolNames = metadata?.map(m => m.name) ?? [];\n const connection = state.manager.getConnection(server);\n\n if (toolNames.length === 0) {\n if (connection?.status === \"connected\") {\n return {\n content: [{ type: \"text\" as const, text: `Server \"${server}\" has no tools.` }],\n details: { mode: \"list\", server, tools: [], count: 0 },\n };\n }\n if (metadata !== undefined) {\n return {\n content: [{ type: \"text\" as const, text: `Server \"${server}\" has no cached tools (not connected).` }],\n details: { mode: \"list\", server, tools: [], count: 0, cached: true },\n };\n }\n return {\n content: [{ type: \"text\" as const, text: `Server \"${server}\" is configured but not connected. Use mcp({ connect: \"${server}\" }) or /mcp reconnect ${server} to retry.` }],\n details: { mode: \"list\", server, tools: [], count: 0, error: \"not_connected\" },\n };\n }\n\n const cachedNote = connection?.status === \"connected\" ? \"\" : \" (not connected, cached)\";\n let text = `${server} (${toolNames.length} tools${cachedNote}):\\n\\n`;\n\n const descMap = new Map<string, string>();\n if (metadata) {\n for (const m of metadata) {\n descMap.set(m.name, m.description);\n }\n }\n\n for (const tool of toolNames) {\n const desc = descMap.get(tool) ?? \"\";\n const truncated = truncateAtWord(desc, 50);\n text += `- ${tool}`;\n if (truncated) text += ` - ${truncated}`;\n text += \"\\n\";\n }\n\n return {\n content: [{ type: \"text\" as const, text: text.trim() }],\n details: { mode: \"list\", server, tools: toolNames, count: toolNames.length },\n };\n}\n\nexport async function executeConnect(state: McpExtensionState, serverName: string, signal?: AbortSignal): Promise<ProxyToolResult> {\n throwIfAborted(signal);\n const definition = state.config.mcpServers[serverName];\n if (!definition) {\n return {\n content: [{ type: \"text\" as const, text: `Server \"${serverName}\" not found. Use mcp({}) to see available servers.` }],\n details: { mode: \"connect\", error: \"not_found\", server: serverName },\n };\n }\n\n try {\n if (state.ui) {\n state.ui.setStatus(\"mcp\", `MCP: connecting to ${serverName}...`);\n }\n let connection = await state.manager.connect(serverName, definition, signal);\n if (connection.status === \"needs-auth\") {\n const autoAuth = await attemptAutoAuth(state, serverName);\n if (autoAuth.status === \"failed\") {\n return {\n content: [{ type: \"text\" as const, text: autoAuth.message }],\n details: { mode: \"connect\", error: \"auth_required\", server: serverName, message: autoAuth.message },\n };\n }\n if (autoAuth.status === \"success\") {\n await state.manager.close(serverName);\n connection = await state.manager.connect(serverName, definition, signal);\n }\n if (connection.status === \"needs-auth\") {\n const message = getAuthRequiredMessage(state, serverName);\n return {\n content: [{ type: \"text\" as const, text: message }],\n details: { mode: \"connect\", error: \"auth_required\", server: serverName, message },\n };\n }\n }\n const prefix = state.config.settings?.toolPrefix ?? \"server\";\n const { metadata } = buildToolMetadata(connection.tools, connection.resources, definition, serverName, prefix);\n state.toolMetadata.set(serverName, metadata);\n updateMetadataCache(state, serverName);\n state.failureTracker.delete(serverName);\n updateStatusBar(state);\n return executeList(state, serverName);\n } catch (error) {\n if (!signal?.aborted) {\n state.failureTracker.set(serverName, Date.now());\n }\n updateStatusBar(state);\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: \"text\" as const, text: `Failed to connect to \"${serverName}\": ${message}` }],\n details: { mode: \"connect\", error: signal?.aborted ? \"aborted\" : \"connect_failed\", server: serverName, message },\n };\n }\n}\n\nexport async function executeCall(\n state: McpExtensionState,\n toolName: string,\n args?: Record<string, unknown>,\n serverOverride?: string,\n getPiTools?: () => ToolInfo[],\n signal?: AbortSignal,\n): Promise<ProxyToolResult> {\n throwIfAborted(signal);\n let serverName: string | undefined = serverOverride;\n let toolMeta: ToolMetadata | undefined;\n let autoAuthAttempted = false;\n const prefixMode = state.config.settings?.toolPrefix ?? \"server\";\n\n if (serverName && !state.config.mcpServers[serverName]) {\n return {\n content: [{ type: \"text\" as const, text: `Server \"${serverName}\" not found. Use mcp({}) to see available servers.` }],\n details: { mode: \"call\", error: \"server_not_found\", server: serverName },\n };\n }\n\n if (serverName) {\n toolMeta = findToolByName(state.toolMetadata.get(serverName), toolName);\n } else {\n for (const [server, metadata] of state.toolMetadata.entries()) {\n const found = findToolByName(metadata, toolName);\n if (found) {\n serverName = server;\n toolMeta = found;\n break;\n }\n }\n }\n\n if (serverName && !toolMeta) {\n const connected = await lazyConnect(state, serverName, signal);\n if (connected) {\n toolMeta = findToolByName(state.toolMetadata.get(serverName), toolName);\n } else {\n const needsAuthConnection = state.manager.getConnection(serverName);\n if (needsAuthConnection?.status === \"needs-auth\") {\n if (!autoAuthAttempted) {\n autoAuthAttempted = true;\n const autoAuth = await attemptAutoAuth(state, serverName);\n if (autoAuth.status === \"failed\") {\n return {\n content: [{ type: \"text\" as const, text: autoAuth.message }],\n details: { mode: \"call\", error: \"auth_required\", server: serverName, message: autoAuth.message },\n };\n }\n if (autoAuth.status === \"success\") {\n await state.manager.close(serverName);\n state.failureTracker.delete(serverName);\n const connectedAfterAuth = await lazyConnect(state, serverName, signal);\n if (connectedAfterAuth) {\n toolMeta = findToolByName(state.toolMetadata.get(serverName), toolName);\n if (!toolMeta) {\n return {\n content: [{ type: \"text\" as const, text: `Tool \"${toolName}\" not found on \"${serverName}\" after reconnect.` }],\n details: { mode: \"call\", error: \"tool_not_found_after_reconnect\", requestedTool: toolName },\n };\n }\n }\n }\n }\n\n if (!toolMeta && state.manager.getConnection(serverName)?.status === \"needs-auth\") {\n const message = getAuthRequiredMessage(state, serverName);\n return {\n content: [{ type: \"text\" as const, text: message }],\n details: { mode: \"call\", error: \"auth_required\", server: serverName, message },\n };\n }\n }\n\n if (!toolMeta) {\n const failedAgo = getFailureAgeSeconds(state, serverName);\n if (failedAgo !== null) {\n return {\n content: [{ type: \"text\" as const, text: `Server \"${serverName}\" not available (last failed ${failedAgo}s ago)` }],\n details: { mode: \"call\", error: \"server_backoff\", server: serverName },\n };\n }\n }\n }\n }\n\n let prefixMatchedServer: string | undefined;\n\n if (!serverName && !toolMeta && prefixMode !== \"none\") {\n const candidates = Object.keys(state.config.mcpServers)\n .map(name => ({ name, prefix: getServerPrefix(name, prefixMode) }))\n .filter(c => c.prefix && toolName.startsWith(c.prefix + \"_\"))\n .sort((a, b) => b.prefix.length - a.prefix.length);\n\n for (const { name: configuredServer } of candidates) {\n const existingConnection = state.manager.getConnection(configuredServer);\n const failedAgo = getFailureAgeSeconds(state, configuredServer);\n if (failedAgo !== null && existingConnection?.status !== \"needs-auth\") continue;\n\n let connected = await lazyConnect(state, configuredServer, signal);\n if (!connected && state.manager.getConnection(configuredServer)?.status === \"needs-auth\" && !autoAuthAttempted) {\n autoAuthAttempted = true;\n const autoAuth = await attemptAutoAuth(state, configuredServer);\n if (autoAuth.status === \"failed\") {\n return {\n content: [{ type: \"text\" as const, text: autoAuth.message }],\n details: { mode: \"call\", error: \"auth_required\", server: configuredServer, message: autoAuth.message },\n };\n }\n if (autoAuth.status === \"success\") {\n await state.manager.close(configuredServer);\n state.failureTracker.delete(configuredServer);\n connected = await lazyConnect(state, configuredServer, signal);\n }\n }\n\n if (!connected) continue;\n if (!prefixMatchedServer) prefixMatchedServer = configuredServer;\n toolMeta = findToolByName(state.toolMetadata.get(configuredServer), toolName);\n if (toolMeta) {\n serverName = configuredServer;\n break;\n }\n }\n }\n\n if (!serverName || !toolMeta) {\n const nativeTool = !serverOverride\n ? getPiTools?.().find((tool) => tool.name === toolName && tool.name !== \"mcp\")\n : undefined;\n if (nativeTool) {\n return {\n content: [{ type: \"text\" as const, text: `\"${toolName}\" is a native Pi tool. Call ${toolName} directly instead of using mcp({ tool: \"${toolName}\" }).` }],\n details: { mode: \"call\", error: \"native_tool\", requestedTool: toolName },\n };\n }\n\n const hintServer = serverName ?? prefixMatchedServer;\n const available = hintServer ? getToolNames(state, hintServer) : [];\n let msg = `Tool \"${toolName}\" not found.`;\n if (available.length > 0) {\n msg += ` Server \"${hintServer}\" has: ${available.join(\", \")}`;\n } else {\n msg += ` Use mcp({ search: \"...\" }) to search.`;\n }\n return {\n content: [{ type: \"text\" as const, text: msg }],\n details: { mode: \"call\", error: \"tool_not_found\", requestedTool: toolName, hintServer },\n };\n }\n\n let connection = state.manager.getConnection(serverName);\n if (connection?.status === \"needs-auth\") {\n if (!autoAuthAttempted) {\n autoAuthAttempted = true;\n const autoAuth = await attemptAutoAuth(state, serverName);\n if (autoAuth.status === \"failed\") {\n return {\n content: [{ type: \"text\" as const, text: autoAuth.message }],\n details: { mode: \"call\", error: \"auth_required\", server: serverName, message: autoAuth.message },\n };\n }\n if (autoAuth.status === \"success\") {\n await state.manager.close(serverName);\n state.failureTracker.delete(serverName);\n connection = state.manager.getConnection(serverName);\n }\n }\n\n if (connection?.status === \"needs-auth\") {\n const message = getAuthRequiredMessage(state, serverName);\n return {\n content: [{ type: \"text\" as const, text: message }],\n details: { mode: \"call\", error: \"auth_required\", server: serverName, message },\n };\n }\n }\n if (!connection || connection.status !== \"connected\") {\n const failedAgo = getFailureAgeSeconds(state, serverName);\n if (failedAgo !== null) {\n return {\n content: [{ type: \"text\" as const, text: `Server \"${serverName}\" not available (last failed ${failedAgo}s ago)` }],\n details: { mode: \"call\", error: \"server_backoff\", server: serverName },\n };\n }\n\n const definition = state.config.mcpServers[serverName];\n if (!definition) {\n return {\n content: [{ type: \"text\" as const, text: `Server \"${serverName}\" not connected` }],\n details: { mode: \"call\", error: \"server_not_connected\", server: serverName },\n };\n }\n\n try {\n if (state.ui) {\n state.ui.setStatus(\"mcp\", `MCP: connecting to ${serverName}...`);\n }\n connection = await state.manager.connect(serverName, definition, signal);\n if (connection.status === \"needs-auth\") {\n if (!autoAuthAttempted) {\n autoAuthAttempted = true;\n const autoAuth = await attemptAutoAuth(state, serverName);\n if (autoAuth.status === \"failed\") {\n return {\n content: [{ type: \"text\" as const, text: autoAuth.message }],\n details: { mode: \"call\", error: \"auth_required\", server: serverName, message: autoAuth.message },\n };\n }\n if (autoAuth.status === \"success\") {\n await state.manager.close(serverName);\n connection = await state.manager.connect(serverName, definition, signal);\n }\n }\n\n if (connection.status === \"needs-auth\") {\n const message = getAuthRequiredMessage(state, serverName);\n return {\n content: [{ type: \"text\" as const, text: message }],\n details: { mode: \"call\", error: \"auth_required\", server: serverName, message },\n };\n }\n }\n state.failureTracker.delete(serverName);\n updateServerMetadata(state, serverName);\n updateMetadataCache(state, serverName);\n updateStatusBar(state);\n toolMeta = findToolByName(state.toolMetadata.get(serverName), toolName);\n if (!toolMeta) {\n const available = getToolNames(state, serverName);\n const hint = available.length > 0\n ? `Available tools on \"${serverName}\": ${available.join(\", \")}`\n : `Server \"${serverName}\" has no tools.`;\n return {\n content: [{ type: \"text\" as const, text: `Tool \"${toolName}\" not found on \"${serverName}\" after reconnect. ${hint}` }],\n details: { mode: \"call\", error: \"tool_not_found_after_reconnect\", requestedTool: toolName },\n };\n }\n } catch (error) {\n if (!signal?.aborted) {\n state.failureTracker.set(serverName, Date.now());\n }\n updateStatusBar(state);\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: \"text\" as const, text: `Failed to connect to \"${serverName}\": ${message}` }],\n details: { mode: \"call\", error: signal?.aborted ? \"aborted\" : \"connect_failed\", message },\n };\n }\n }\n\n let uiSession: UiSessionRuntime | null = null;\n const requestOptions = state.manager.getRequestOptions?.(serverName, signal) ?? (signal ? { signal } : undefined);\n\n const outputGuardOptions = resolveMcpOutputGuardOptions(state.config.settings);\n\n try {\n state.manager.touch(serverName);\n state.manager.incrementInFlight(serverName);\n\n if (toolMeta.resourceUri) {\n const result = await connection.client.readResource({ uri: toolMeta.resourceUri }, requestOptions);\n const content = (result.contents ?? []).map(c => ({\n type: \"text\" as const,\n text: \"text\" in c ? c.text : (\"blob\" in c ? `[Binary data: ${(c as { mimeType?: string }).mimeType ?? \"unknown\"}]` : JSON.stringify(c)),\n }));\n const guarded = await guardMcpOutput(content.length > 0 ? content : [{ type: \"text\" as const, text: \"(empty resource)\" }], outputGuardOptions);\n return {\n content: guarded.content,\n details: { mode: \"call\", resourceUri: toolMeta.resourceUri, server: serverName, ...guardedMcpDetails(guarded) },\n };\n }\n\n uiSession = toolMeta.uiResourceUri\n ? await maybeStartUiSession(state, {\n serverName,\n toolName: toolMeta.originalName,\n toolArgs: args ?? {},\n uiResourceUri: toolMeta.uiResourceUri,\n streamMode: toolMeta.uiStreamMode,\n })\n : null;\n\n const resultPromise = connection.client.callTool({\n name: toolMeta.originalName,\n arguments: args ?? {},\n _meta: uiSession?.requestMeta,\n }, undefined, requestOptions);\n\n if (toolMeta.uiResourceUri) {\n const result = await abortable(resultPromise, signal);\n uiSession?.sendToolResult(result as unknown as import(\"@modelcontextprotocol/sdk/types.js\").CallToolResult);\n\n if (result.isError) {\n const mcpContent = (result.content ?? []) as McpContent[];\n const content = transformMcpContent(mcpContent);\n const outputContent = content.length > 0 ? content : [{ type: \"text\" as const, text: \"(empty result)\" }];\n const schemaText = toolMeta.inputSchema ? `\\n\\nExpected parameters:\\n${formatSchema(toolMeta.inputSchema)}` : \"\";\n const guarded = await guardMcpOutput(outputContent, { ...outputGuardOptions, prefix: \"Error: \", suffix: schemaText, emptyTextFallback: \"Tool execution failed\", rawMcpResult: result });\n return {\n content: guarded.content,\n details: { mode: \"call\", error: \"tool_error\", ...guardedMcpDetails(guarded) },\n };\n }\n\n const content = resolveMcpResultContent(result as Record<string, unknown>);\n const outputContent = content.length > 0 ? content : [{ type: \"text\" as const, text: \"(empty result)\" }];\n const uiMessage = uiSession?.reused\n ? \"Updated the open UI.\"\n : \"📺 Interactive UI is now open in your browser. I'll respond to your prompts and intents as you interact with it.\";\n const guarded = await guardMcpOutput(outputContent, { ...outputGuardOptions, suffix: `\\n\\n${uiMessage}`, rawMcpResult: result });\n return {\n content: guarded.content,\n details: { mode: \"call\", ...guardedMcpDetails(guarded), server: serverName, tool: toolMeta.originalName, uiOpen: true },\n };\n }\n\n const result = await abortable(resultPromise, signal);\n\n if (result.isError) {\n const mcpContent = (result.content ?? []) as McpContent[];\n const content = transformMcpContent(mcpContent);\n const outputContent = content.length > 0 ? content : [{ type: \"text\" as const, text: \"(empty result)\" }];\n const schemaText = toolMeta.inputSchema ? `\\n\\nExpected parameters:\\n${formatSchema(toolMeta.inputSchema)}` : \"\";\n const guarded = await guardMcpOutput(outputContent, { ...outputGuardOptions, prefix: \"Error: \", suffix: schemaText, emptyTextFallback: \"Tool execution failed\", rawMcpResult: result });\n return {\n content: guarded.content,\n details: { mode: \"call\", error: \"tool_error\", ...guardedMcpDetails(guarded) },\n };\n }\n\n const content = resolveMcpResultContent(result as Record<string, unknown>);\n const outputContent = content.length > 0 ? content : [{ type: \"text\" as const, text: \"(empty result)\" }];\n const guarded = await guardMcpOutput(outputContent, { ...outputGuardOptions, rawMcpResult: result });\n return {\n content: guarded.content,\n details: { mode: \"call\", ...guardedMcpDetails(guarded), server: serverName, tool: toolMeta.originalName },\n };\n } catch (error) {\n if (error instanceof UrlElicitationRequiredError) {\n const action = await state.manager.handleUrlElicitationRequired(serverName, error);\n const message = action === \"accept\"\n ? \"The original MCP tool did not run. Complete the opened browser interaction, then retry the tool.\"\n : `The URL interaction was ${action === \"decline\" ? \"declined\" : \"cancelled\"}.`;\n uiSession?.sendToolCancelled(message);\n return {\n content: [{ type: \"text\" as const, text: message }],\n details: { mode: \"call\", error: \"url_elicitation_required\", server: serverName, action },\n };\n }\n const message = error instanceof Error ? error.message : String(error);\n uiSession?.sendToolCancelled(message);\n\n const schemaText = toolMeta.inputSchema ? `\\n\\nExpected parameters:\\n${formatSchema(toolMeta.inputSchema)}` : \"\";\n const guarded = await guardMcpOutput([{ type: \"text\" as const, text: message }], { ...outputGuardOptions, prefix: \"Failed to call tool: \", suffix: schemaText });\n\n return {\n content: guarded.content,\n details: { mode: \"call\", error: \"call_failed\", message: guarded.outputGuard ? \"output truncated; see outputGuard.fullOutputPath\" : message, ...guardedMcpDetails(guarded) },\n };\n } finally {\n if (uiSession?.reused) {\n uiSession.close();\n }\n state.manager.decrementInFlight(serverName);\n state.manager.touch(serverName);\n }\n}\n"]}
|