@herbertgao/pi-extensions 2026.8.2 → 2026.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -2
- package/examples/pi-footer.json +275 -0
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +6 -8
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +5 -7
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.en.svg +101 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +1 -17
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +5 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/{utils/agent-summary.ts → feature/agent-summary/core.ts} +37 -35
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{agent-summary.ts → agent-summary/index.ts} +2 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{session-reference.ts → reference/index.ts} +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{agent-autocomplete.ts → reference/subagent.ts} +19 -9
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{pi-startup-header.ts → shell/startup-header.ts} +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{working-message.ts → shell/working-message.ts} +5 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +32 -28
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +379 -85
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +727 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +65 -775
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/{feature → renderer}/markdown-enhance.ts +35 -31
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-hover.ts → mouse/hover.ts} +5 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-interaction.ts → mouse/interaction.ts} +34 -35
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-layout.ts → mouse/layout.ts} +5 -21
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-packets.ts → mouse/packets.ts} +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-scroll.ts → mouse/scroll.ts} +48 -37
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/ansi-utils.ts +56 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/diff-renderer.ts +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/line-width-safety.ts +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-display-utils.ts +12 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/write-execution.ts +1 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-grouping.ts → tool/grouping.ts} +18 -8
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-result.ts → tool/result.ts} +4 -13
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/fullscreen-detect.ts +0 -27
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
- package/node_modules/@herbertgao/pi-subagents/package.json +2 -2
- package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +1 -0
- package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +3 -1
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +33 -35
- package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +26 -35
- package/node_modules/@herbertgao/pi-subagents/src/prompts.ts +15 -1
- package/node_modules/pi-footer/CHANGELOG.md +145 -0
- package/node_modules/pi-footer/LICENSE +21 -0
- package/node_modules/pi-footer/README.md +354 -0
- package/node_modules/pi-footer/package.json +68 -0
- package/node_modules/pi-footer/src/cache.ts +106 -0
- package/node_modules/pi-footer/src/colors.ts +237 -0
- package/node_modules/pi-footer/src/config.ts +202 -0
- package/node_modules/pi-footer/src/event-widgets.ts +41 -0
- package/node_modules/pi-footer/src/extension-statuses.ts +102 -0
- package/node_modules/pi-footer/src/git.ts +135 -0
- package/node_modules/pi-footer/src/index.ts +268 -0
- package/node_modules/pi-footer/src/metrics.ts +141 -0
- package/node_modules/pi-footer/src/presets.ts +383 -0
- package/node_modules/pi-footer/src/render.ts +114 -0
- package/node_modules/pi-footer/src/separators.ts +52 -0
- package/node_modules/pi-footer/src/types.ts +122 -0
- package/node_modules/pi-footer/src/ui/color-level-confirm.ts +22 -0
- package/node_modules/pi-footer/src/ui/color-options.ts +195 -0
- package/node_modules/pi-footer/src/ui/config-lifecycle.ts +61 -0
- package/node_modules/pi-footer/src/ui/edit-colors.ts +13 -0
- package/node_modules/pi-footer/src/ui/events.ts +35 -0
- package/node_modules/pi-footer/src/ui/extension-status-picker.ts +60 -0
- package/node_modules/pi-footer/src/ui/extension-statuses.ts +73 -0
- package/node_modules/pi-footer/src/ui/fields.ts +252 -0
- package/node_modules/pi-footer/src/ui/global-menu.ts +126 -0
- package/node_modules/pi-footer/src/ui/helpers.ts +44 -0
- package/node_modules/pi-footer/src/ui/layout.ts +17 -0
- package/node_modules/pi-footer/src/ui/line-list.ts +19 -0
- package/node_modules/pi-footer/src/ui/model.ts +64 -0
- package/node_modules/pi-footer/src/ui/navigation.ts +29 -0
- package/node_modules/pi-footer/src/ui/option-edit.ts +93 -0
- package/node_modules/pi-footer/src/ui/overlay-render.ts +103 -0
- package/node_modules/pi-footer/src/ui/screen-context.ts +18 -0
- package/node_modules/pi-footer/src/ui/screen-controller.ts +27 -0
- package/node_modules/pi-footer/src/ui/screen-render.ts +32 -0
- package/node_modules/pi-footer/src/ui/screen-state.ts +20 -0
- package/node_modules/pi-footer/src/ui/screen.ts +243 -0
- package/node_modules/pi-footer/src/ui/screens/add-widget.ts +92 -0
- package/node_modules/pi-footer/src/ui/screens/confirm-exit.ts +52 -0
- package/node_modules/pi-footer/src/ui/screens/controller.ts +12 -0
- package/node_modules/pi-footer/src/ui/screens/edit-colors.ts +68 -0
- package/node_modules/pi-footer/src/ui/screens/edit-widget.ts +115 -0
- package/node_modules/pi-footer/src/ui/screens/extension-status-row.ts +61 -0
- package/node_modules/pi-footer/src/ui/screens/global.ts +75 -0
- package/node_modules/pi-footer/src/ui/screens/line-list.ts +108 -0
- package/node_modules/pi-footer/src/ui/screens/main.ts +99 -0
- package/node_modules/pi-footer/src/ui/screens/terminal.ts +121 -0
- package/node_modules/pi-footer/src/ui/screens/widget-list.ts +154 -0
- package/node_modules/pi-footer/src/ui/terminal-menu.ts +32 -0
- package/node_modules/pi-footer/src/ui/theme.ts +48 -0
- package/node_modules/pi-footer/src/ui/title-bar.ts +100 -0
- package/node_modules/pi-footer/src/ui/widget-actions.ts +76 -0
- package/node_modules/pi-footer/src/ui.ts +66 -0
- package/node_modules/pi-footer/src/widgets/context.ts +28 -0
- package/node_modules/pi-footer/src/widgets/core/active-tools.ts +17 -0
- package/node_modules/pi-footer/src/widgets/core/context-window.ts +24 -0
- package/node_modules/pi-footer/src/widgets/core/cwd-basename.ts +19 -0
- package/node_modules/pi-footer/src/widgets/core/cwd.ts +108 -0
- package/node_modules/pi-footer/src/widgets/core/event.ts +34 -0
- package/node_modules/pi-footer/src/widgets/core/external-status.ts +102 -0
- package/node_modules/pi-footer/src/widgets/core/model-provider.ts +20 -0
- package/node_modules/pi-footer/src/widgets/core/model.ts +35 -0
- package/node_modules/pi-footer/src/widgets/core/provider.ts +17 -0
- package/node_modules/pi-footer/src/widgets/core/session-name.ts +17 -0
- package/node_modules/pi-footer/src/widgets/core/text-verbosity.ts +17 -0
- package/node_modules/pi-footer/src/widgets/core/thinking-level.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/ahead-behind.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/branch.ts +81 -0
- package/node_modules/pi-footer/src/widgets/git/clean.ts +19 -0
- package/node_modules/pi-footer/src/widgets/git/deletions.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/diff.ts +38 -0
- package/node_modules/pi-footer/src/widgets/git/insertions.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/remote.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/root.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/sha.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/staged.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/status.ts +19 -0
- package/node_modules/pi-footer/src/widgets/git/unstaged.ts +17 -0
- package/node_modules/pi-footer/src/widgets/git/untracked.ts +17 -0
- package/node_modules/pi-footer/src/widgets/instance.ts +118 -0
- package/node_modules/pi-footer/src/widgets/layout/custom-text.ts +18 -0
- package/node_modules/pi-footer/src/widgets/layout/flex-separator.ts +32 -0
- package/node_modules/pi-footer/src/widgets/layout/separator.ts +57 -0
- package/node_modules/pi-footer/src/widgets/layout/spacer.ts +31 -0
- package/node_modules/pi-footer/src/widgets/options.ts +122 -0
- package/node_modules/pi-footer/src/widgets/project/runtime.ts +361 -0
- package/node_modules/pi-footer/src/widgets/registry.ts +223 -0
- package/node_modules/pi-footer/src/widgets/session/assistant-messages.ts +17 -0
- package/node_modules/pi-footer/src/widgets/session/compactions.ts +17 -0
- package/node_modules/pi-footer/src/widgets/session/elapsed.ts +18 -0
- package/node_modules/pi-footer/src/widgets/session/last-activity.ts +18 -0
- package/node_modules/pi-footer/src/widgets/session/messages.ts +19 -0
- package/node_modules/pi-footer/src/widgets/session/session-id.ts +17 -0
- package/node_modules/pi-footer/src/widgets/session/session-start.ts +18 -0
- package/node_modules/pi-footer/src/widgets/session/tool-results.ts +17 -0
- package/node_modules/pi-footer/src/widgets/session/total-messages.ts +19 -0
- package/node_modules/pi-footer/src/widgets/session/total-time.ts +18 -0
- package/node_modules/pi-footer/src/widgets/session/user-messages.ts +17 -0
- package/node_modules/pi-footer/src/widgets/store.ts +26 -0
- package/node_modules/pi-footer/src/widgets/tokens/cache-hit-rate.ts +55 -0
- package/node_modules/pi-footer/src/widgets/tokens/cache-read.ts +18 -0
- package/node_modules/pi-footer/src/widgets/tokens/cache-write.ts +18 -0
- package/node_modules/pi-footer/src/widgets/tokens/context-bar.ts +108 -0
- package/node_modules/pi-footer/src/widgets/tokens/context-length.ts +26 -0
- package/node_modules/pi-footer/src/widgets/tokens/context-remaining.ts +29 -0
- package/node_modules/pi-footer/src/widgets/tokens/context.ts +29 -0
- package/node_modules/pi-footer/src/widgets/tokens/cost.ts +51 -0
- package/node_modules/pi-footer/src/widgets/tokens/input-speed.ts +25 -0
- package/node_modules/pi-footer/src/widgets/tokens/input-tokens.ts +18 -0
- package/node_modules/pi-footer/src/widgets/tokens/output-speed.ts +25 -0
- package/node_modules/pi-footer/src/widgets/tokens/output-tokens.ts +18 -0
- package/node_modules/pi-footer/src/widgets/tokens/tokens.ts +20 -0
- package/node_modules/pi-footer/src/widgets/tokens/total-speed.ts +25 -0
- package/node_modules/pi-footer/src/widgets/tokens/total-tokens.ts +18 -0
- package/node_modules/pi-footer/src/widgets/types.ts +181 -0
- package/node_modules/pi-footer/src/widgets/utils/colors.ts +11 -0
- package/node_modules/pi-footer/src/widgets/utils/context.ts +112 -0
- package/node_modules/pi-footer/src/widgets/utils/session.ts +19 -0
- package/node_modules/pi-footer/src/widgets/utils/token-format.ts +78 -0
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +19 -0
- package/node_modules/pi-mcp-adapter/README.md +19 -4
- package/node_modules/pi-mcp-adapter/agent-dir.ts +40 -0
- package/node_modules/pi-mcp-adapter/commands.ts +12 -2
- package/node_modules/pi-mcp-adapter/direct-tools.ts +6 -8
- package/node_modules/pi-mcp-adapter/index.ts +15 -3
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +87 -4
- package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +138 -61
- package/node_modules/pi-mcp-adapter/mcp-oauth-provider.ts +43 -4
- package/node_modules/pi-mcp-adapter/oauth.ts +48 -0
- package/node_modules/pi-mcp-adapter/package.json +8 -2
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +8 -10
- package/node_modules/pi-mcp-adapter/tool-registrar.ts +187 -3
- package/node_modules/pi-mcp-adapter/types.ts +2 -0
- package/node_modules/pi-web-access/CHANGELOG.md +45 -0
- package/node_modules/pi-web-access/README.md +104 -29
- package/node_modules/pi-web-access/bocha.ts +221 -0
- package/node_modules/pi-web-access/curator-page.ts +113 -12
- package/node_modules/pi-web-access/curator-server.ts +56 -20
- package/node_modules/pi-web-access/datalab-pdf-extract.ts +568 -0
- package/node_modules/pi-web-access/duckduckgo.ts +123 -0
- package/node_modules/pi-web-access/extract.ts +288 -143
- package/node_modules/pi-web-access/feature-config.ts +29 -0
- package/node_modules/pi-web-access/gemini-api.ts +4 -0
- package/node_modules/pi-web-access/gemini-search.ts +45 -12
- package/node_modules/pi-web-access/gemini-web.ts +68 -3
- package/node_modules/pi-web-access/index.ts +160 -71
- package/node_modules/pi-web-access/jina-search.ts +284 -0
- package/node_modules/pi-web-access/kagi.ts +12 -12
- package/node_modules/pi-web-access/openai-search.ts +12 -2
- package/node_modules/pi-web-access/package.json +4 -3
- package/node_modules/pi-web-access/pdf-extract.ts +327 -228
- package/node_modules/pi-web-access/storage.ts +440 -2
- package/node_modules/pi-web-access/summary-model-scope.ts +1 -1
- package/node_modules/pi-web-access/summary-review.ts +4 -2
- package/node_modules/pi-web-access/video-extract.ts +10 -8
- package/node_modules/pi-web-access/xai-search.ts +12 -2
- package/node_modules/pi-web-access/youtube-extract.ts +2 -1
- package/node_modules/remote-pi/LICENSE +21 -0
- package/node_modules/remote-pi/README.md +675 -0
- package/node_modules/remote-pi/dist/actions/handlers.d.ts +122 -0
- package/node_modules/remote-pi/dist/actions/handlers.js +160 -0
- package/node_modules/remote-pi/dist/actions/handlers.js.map +1 -0
- package/node_modules/remote-pi/dist/actions/registry.d.ts +39 -0
- package/node_modules/remote-pi/dist/actions/registry.js +58 -0
- package/node_modules/remote-pi/dist/actions/registry.js.map +1 -0
- package/node_modules/remote-pi/dist/bin/supervisord.d.ts +2 -0
- package/node_modules/remote-pi/dist/bin/supervisord.js +86 -0
- package/node_modules/remote-pi/dist/bin/supervisord.js.map +1 -0
- package/node_modules/remote-pi/dist/commands/builtin_mirror.d.ts +58 -0
- package/node_modules/remote-pi/dist/commands/builtin_mirror.js +71 -0
- package/node_modules/remote-pi/dist/commands/builtin_mirror.js.map +1 -0
- package/node_modules/remote-pi/dist/commands/list_commands.d.ts +60 -0
- package/node_modules/remote-pi/dist/commands/list_commands.js +73 -0
- package/node_modules/remote-pi/dist/commands/list_commands.js.map +1 -0
- package/node_modules/remote-pi/dist/config.d.ts +62 -0
- package/node_modules/remote-pi/dist/config.js +115 -0
- package/node_modules/remote-pi/dist/config.js.map +1 -0
- package/node_modules/remote-pi/dist/daemon/client.d.ts +20 -0
- package/node_modules/remote-pi/dist/daemon/client.js +131 -0
- package/node_modules/remote-pi/dist/daemon/client.js.map +1 -0
- package/node_modules/remote-pi/dist/daemon/control_protocol.d.ts +176 -0
- package/node_modules/remote-pi/dist/daemon/control_protocol.js +63 -0
- package/node_modules/remote-pi/dist/daemon/control_protocol.js.map +1 -0
- package/node_modules/remote-pi/dist/daemon/cron_log.d.ts +45 -0
- package/node_modules/remote-pi/dist/daemon/cron_log.js +72 -0
- package/node_modules/remote-pi/dist/daemon/cron_log.js.map +1 -0
- package/node_modules/remote-pi/dist/daemon/cron_registry.d.ts +80 -0
- package/node_modules/remote-pi/dist/daemon/cron_registry.js +194 -0
- package/node_modules/remote-pi/dist/daemon/cron_registry.js.map +1 -0
- package/node_modules/remote-pi/dist/daemon/id.d.ts +24 -0
- package/node_modules/remote-pi/dist/daemon/id.js +36 -0
- package/node_modules/remote-pi/dist/daemon/id.js.map +1 -0
- package/node_modules/remote-pi/dist/daemon/install.d.ts +170 -0
- package/node_modules/remote-pi/dist/daemon/install.js +547 -0
- package/node_modules/remote-pi/dist/daemon/install.js.map +1 -0
- package/node_modules/remote-pi/dist/daemon/registry.d.ts +63 -0
- package/node_modules/remote-pi/dist/daemon/registry.js +153 -0
- package/node_modules/remote-pi/dist/daemon/registry.js.map +1 -0
- package/node_modules/remote-pi/dist/daemon/rpc_child.d.ts +194 -0
- package/node_modules/remote-pi/dist/daemon/rpc_child.js +389 -0
- package/node_modules/remote-pi/dist/daemon/rpc_child.js.map +1 -0
- package/node_modules/remote-pi/dist/daemon/supervisor.d.ts +93 -0
- package/node_modules/remote-pi/dist/daemon/supervisor.js +596 -0
- package/node_modules/remote-pi/dist/daemon/supervisor.js.map +1 -0
- package/node_modules/remote-pi/dist/extension_ui_bridge.d.ts +26 -0
- package/node_modules/remote-pi/dist/extension_ui_bridge.js +341 -0
- package/node_modules/remote-pi/dist/extension_ui_bridge.js.map +1 -0
- package/node_modules/remote-pi/dist/index.d.ts +213 -0
- package/node_modules/remote-pi/dist/index.js +4996 -0
- package/node_modules/remote-pi/dist/index.js.map +1 -0
- package/node_modules/remote-pi/dist/mcp/mesh_result.d.ts +10 -0
- package/node_modules/remote-pi/dist/mcp/mesh_result.js +42 -0
- package/node_modules/remote-pi/dist/mcp/mesh_result.js.map +1 -0
- package/node_modules/remote-pi/dist/mcp/mesh_server.d.ts +16 -0
- package/node_modules/remote-pi/dist/mcp/mesh_server.js +327 -0
- package/node_modules/remote-pi/dist/mcp/mesh_server.js.map +1 -0
- package/node_modules/remote-pi/dist/mesh/canonical.d.ts +30 -0
- package/node_modules/remote-pi/dist/mesh/canonical.js +61 -0
- package/node_modules/remote-pi/dist/mesh/canonical.js.map +1 -0
- package/node_modules/remote-pi/dist/mesh/client.d.ts +18 -0
- package/node_modules/remote-pi/dist/mesh/client.js +125 -0
- package/node_modules/remote-pi/dist/mesh/client.js.map +1 -0
- package/node_modules/remote-pi/dist/mesh/encoding.d.ts +38 -0
- package/node_modules/remote-pi/dist/mesh/encoding.js +216 -0
- package/node_modules/remote-pi/dist/mesh/encoding.js.map +1 -0
- package/node_modules/remote-pi/dist/mesh/self_revoke.d.ts +73 -0
- package/node_modules/remote-pi/dist/mesh/self_revoke.js +401 -0
- package/node_modules/remote-pi/dist/mesh/self_revoke.js.map +1 -0
- package/node_modules/remote-pi/dist/mesh/siblings.d.ts +49 -0
- package/node_modules/remote-pi/dist/mesh/siblings.js +159 -0
- package/node_modules/remote-pi/dist/mesh/siblings.js.map +1 -0
- package/node_modules/remote-pi/dist/mesh/types.d.ts +34 -0
- package/node_modules/remote-pi/dist/mesh/types.js +11 -0
- package/node_modules/remote-pi/dist/mesh/types.js.map +1 -0
- package/node_modules/remote-pi/dist/mesh/verify.d.ts +17 -0
- package/node_modules/remote-pi/dist/mesh/verify.js +86 -0
- package/node_modules/remote-pi/dist/mesh/verify.js.map +1 -0
- package/node_modules/remote-pi/dist/pairing/crypto.d.ts +8 -0
- package/node_modules/remote-pi/dist/pairing/crypto.js +22 -0
- package/node_modules/remote-pi/dist/pairing/crypto.js.map +1 -0
- package/node_modules/remote-pi/dist/pairing/handshake.d.ts +28 -0
- package/node_modules/remote-pi/dist/pairing/handshake.js +113 -0
- package/node_modules/remote-pi/dist/pairing/handshake.js.map +1 -0
- package/node_modules/remote-pi/dist/pairing/noise-sha256.d.ts +16 -0
- package/node_modules/remote-pi/dist/pairing/noise-sha256.js +103 -0
- package/node_modules/remote-pi/dist/pairing/noise-sha256.js.map +1 -0
- package/node_modules/remote-pi/dist/pairing/qr.d.ts +59 -0
- package/node_modules/remote-pi/dist/pairing/qr.js +125 -0
- package/node_modules/remote-pi/dist/pairing/qr.js.map +1 -0
- package/node_modules/remote-pi/dist/pairing/storage.d.ts +120 -0
- package/node_modules/remote-pi/dist/pairing/storage.js +604 -0
- package/node_modules/remote-pi/dist/pairing/storage.js.map +1 -0
- package/node_modules/remote-pi/dist/protocol/codec.d.ts +7 -0
- package/node_modules/remote-pi/dist/protocol/codec.js +49 -0
- package/node_modules/remote-pi/dist/protocol/codec.js.map +1 -0
- package/node_modules/remote-pi/dist/protocol/types.d.ts +393 -0
- package/node_modules/remote-pi/dist/protocol/types.js +2 -0
- package/node_modules/remote-pi/dist/protocol/types.js.map +1 -0
- package/node_modules/remote-pi/dist/rooms.d.ts +29 -0
- package/node_modules/remote-pi/dist/rooms.js +57 -0
- package/node_modules/remote-pi/dist/rooms.js.map +1 -0
- package/node_modules/remote-pi/dist/session/address.d.ts +49 -0
- package/node_modules/remote-pi/dist/session/address.js +58 -0
- package/node_modules/remote-pi/dist/session/address.js.map +1 -0
- package/node_modules/remote-pi/dist/session/agent_bridge.d.ts +55 -0
- package/node_modules/remote-pi/dist/session/agent_bridge.js +146 -0
- package/node_modules/remote-pi/dist/session/agent_bridge.js.map +1 -0
- package/node_modules/remote-pi/dist/session/bridge.d.ts +37 -0
- package/node_modules/remote-pi/dist/session/bridge.js +130 -0
- package/node_modules/remote-pi/dist/session/bridge.js.map +1 -0
- package/node_modules/remote-pi/dist/session/broker.d.ts +189 -0
- package/node_modules/remote-pi/dist/session/broker.js +507 -0
- package/node_modules/remote-pi/dist/session/broker.js.map +1 -0
- package/node_modules/remote-pi/dist/session/broker_remote.d.ts +74 -0
- package/node_modules/remote-pi/dist/session/broker_remote.js +583 -0
- package/node_modules/remote-pi/dist/session/broker_remote.js.map +1 -0
- package/node_modules/remote-pi/dist/session/cwd_lock.d.ts +33 -0
- package/node_modules/remote-pi/dist/session/cwd_lock.js +126 -0
- package/node_modules/remote-pi/dist/session/cwd_lock.js.map +1 -0
- package/node_modules/remote-pi/dist/session/envelope.d.ts +33 -0
- package/node_modules/remote-pi/dist/session/envelope.js +117 -0
- package/node_modules/remote-pi/dist/session/envelope.js.map +1 -0
- package/node_modules/remote-pi/dist/session/global_config.d.ts +33 -0
- package/node_modules/remote-pi/dist/session/global_config.js +73 -0
- package/node_modules/remote-pi/dist/session/global_config.js.map +1 -0
- package/node_modules/remote-pi/dist/session/ipc.d.ts +27 -0
- package/node_modules/remote-pi/dist/session/ipc.js +22 -0
- package/node_modules/remote-pi/dist/session/ipc.js.map +1 -0
- package/node_modules/remote-pi/dist/session/leader_election.d.ts +32 -0
- package/node_modules/remote-pi/dist/session/leader_election.js +106 -0
- package/node_modules/remote-pi/dist/session/leader_election.js.map +1 -0
- package/node_modules/remote-pi/dist/session/local_config.d.ts +55 -0
- package/node_modules/remote-pi/dist/session/local_config.js +157 -0
- package/node_modules/remote-pi/dist/session/local_config.js.map +1 -0
- package/node_modules/remote-pi/dist/session/mesh_message.d.ts +20 -0
- package/node_modules/remote-pi/dist/session/mesh_message.js +28 -0
- package/node_modules/remote-pi/dist/session/mesh_message.js.map +1 -0
- package/node_modules/remote-pi/dist/session/mesh_node.d.ts +116 -0
- package/node_modules/remote-pi/dist/session/mesh_node.js +573 -0
- package/node_modules/remote-pi/dist/session/mesh_node.js.map +1 -0
- package/node_modules/remote-pi/dist/session/peer.d.ts +139 -0
- package/node_modules/remote-pi/dist/session/peer.js +451 -0
- package/node_modules/remote-pi/dist/session/peer.js.map +1 -0
- package/node_modules/remote-pi/dist/session/peer_inventory.d.ts +13 -0
- package/node_modules/remote-pi/dist/session/peer_inventory.js +48 -0
- package/node_modules/remote-pi/dist/session/peer_inventory.js.map +1 -0
- package/node_modules/remote-pi/dist/session/peer_limits.d.ts +9 -0
- package/node_modules/remote-pi/dist/session/peer_limits.js +21 -0
- package/node_modules/remote-pi/dist/session/peer_limits.js.map +1 -0
- package/node_modules/remote-pi/dist/session/setup_wizard.d.ts +39 -0
- package/node_modules/remote-pi/dist/session/setup_wizard.js +63 -0
- package/node_modules/remote-pi/dist/session/setup_wizard.js.map +1 -0
- package/node_modules/remote-pi/dist/session/tool_gate.d.ts +5 -0
- package/node_modules/remote-pi/dist/session/tool_gate.js +11 -0
- package/node_modules/remote-pi/dist/session/tool_gate.js.map +1 -0
- package/node_modules/remote-pi/dist/session/tools.d.ts +24 -0
- package/node_modules/remote-pi/dist/session/tools.js +252 -0
- package/node_modules/remote-pi/dist/session/tools.js.map +1 -0
- package/node_modules/remote-pi/dist/session/wizard.d.ts +13 -0
- package/node_modules/remote-pi/dist/session/wizard.js +20 -0
- package/node_modules/remote-pi/dist/session/wizard.js.map +1 -0
- package/node_modules/remote-pi/dist/settings.d.ts +15 -0
- package/node_modules/remote-pi/dist/settings.js +52 -0
- package/node_modules/remote-pi/dist/settings.js.map +1 -0
- package/node_modules/remote-pi/dist/transport/peer_channel.d.ts +37 -0
- package/node_modules/remote-pi/dist/transport/peer_channel.js +98 -0
- package/node_modules/remote-pi/dist/transport/peer_channel.js.map +1 -0
- package/node_modules/remote-pi/dist/transport/pi_forward_client.d.ts +29 -0
- package/node_modules/remote-pi/dist/transport/pi_forward_client.js +62 -0
- package/node_modules/remote-pi/dist/transport/pi_forward_client.js.map +1 -0
- package/node_modules/remote-pi/dist/transport/relay_client.d.ts +91 -0
- package/node_modules/remote-pi/dist/transport/relay_client.js +206 -0
- package/node_modules/remote-pi/dist/transport/relay_client.js.map +1 -0
- package/node_modules/remote-pi/dist/ui/footer.d.ts +32 -0
- package/node_modules/remote-pi/dist/ui/footer.js +34 -0
- package/node_modules/remote-pi/dist/ui/footer.js.map +1 -0
- package/node_modules/remote-pi/docs/daemon.md +289 -0
- package/node_modules/remote-pi/package.json +88 -0
- package/node_modules/remote-pi/service-templates/launchd.plist.template +35 -0
- package/node_modules/remote-pi/service-templates/systemd.service.template +19 -0
- package/node_modules/remote-pi/service-templates/task-launcher.vbs.template +10 -0
- package/node_modules/remote-pi/service-templates/task-scheduler.xml.template +38 -0
- package/node_modules/remote-pi/skills/agent-network/SKILL.md +310 -0
- package/package.json +25 -10
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool-diff/write-display-utils.ts +0 -29
- /package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{session-reference-core.ts → reference/session.ts} +0 -0
- /package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{pi-aliases.ts → shell/aliases.ts} +0 -0
- /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/ATTRIBUTION.md +0 -0
- /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/diff-presentation.ts +0 -0
- /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/index.ts +0 -0
- /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/render-utils.ts +0 -0
- /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/shiki-highlight.ts +0 -0
- /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/types.ts +0 -0
|
@@ -8,7 +8,7 @@ import { abortable, throwIfAborted } from "./abort.ts";
|
|
|
8
8
|
import { isServerCacheValid, parseDirectToolSelectors } from "./metadata-cache.ts";
|
|
9
9
|
export { getMissingConfiguredDirectToolServers } from "./metadata-cache.ts";
|
|
10
10
|
import { formatSchema } from "./tool-metadata.ts";
|
|
11
|
-
import { resolveMcpResultContent, transformMcpContent } from "./tool-registrar.ts";
|
|
11
|
+
import { resolveMcpResultContent, transformMcpContent, transformMcpResourceContents } from "./tool-registrar.ts";
|
|
12
12
|
import { guardMcpOutput, guardedMcpDetails, resolveMcpOutputGuardOptions } from "./mcp-output-guard.ts";
|
|
13
13
|
import { maybeStartUiSession, summarizeUiSessionResult, type UiSessionRuntime } from "./ui-session.ts";
|
|
14
14
|
import { formatToolName, isServerDisabled, isToolAllowed, resolveToolPrefix } from "./types.ts";
|
|
@@ -20,6 +20,7 @@ import { combineAbortSignals, isAbortError } from "./runtime-owner.ts";
|
|
|
20
20
|
import { ensureToolCallApproved } from "./tool-approval.ts";
|
|
21
21
|
|
|
22
22
|
type ClientCallToolResult = Awaited<ReturnType<Client["callTool"]>>;
|
|
23
|
+
type ClientReadResourceResult = Awaited<ReturnType<Client["readResource"]>>;
|
|
23
24
|
|
|
24
25
|
const BUILTIN_NAMES = new Set(["read", "bash", "edit", "write", "grep", "find", "ls", "mcp"]);
|
|
25
26
|
const INSTRUCTIONS_SNIPPET_LENGTH = 150;
|
|
@@ -435,7 +436,7 @@ export function createDirectToolExecutor(
|
|
|
435
436
|
state.manager.incrementInFlight(spec.serverName);
|
|
436
437
|
|
|
437
438
|
if (spec.resourceUri) {
|
|
438
|
-
const result = await withSessionRecovery(
|
|
439
|
+
const result = await withSessionRecovery<ClientReadResourceResult>(
|
|
439
440
|
{
|
|
440
441
|
manager: state.manager,
|
|
441
442
|
config: state.config,
|
|
@@ -445,10 +446,7 @@ export function createDirectToolExecutor(
|
|
|
445
446
|
spec.serverName,
|
|
446
447
|
(conn) => conn.client.readResource({ uri: spec.resourceUri! }, requestOptions),
|
|
447
448
|
);
|
|
448
|
-
const content = (result.contents ?? [])
|
|
449
|
-
type: "text" as const,
|
|
450
|
-
text: "text" in c ? c.text : ("blob" in c ? `[Binary data: ${(c as { mimeType?: string }).mimeType ?? "unknown"}]` : JSON.stringify(c)),
|
|
451
|
-
}));
|
|
449
|
+
const content = transformMcpResourceContents(result.contents ?? [], state.owner?.signal);
|
|
452
450
|
const guarded = await guardMcpOutput(content.length > 0 ? content : [{ type: "text" as const, text: "(empty resource)" }], outputGuardOptions);
|
|
453
451
|
return {
|
|
454
452
|
content: guarded.content,
|
|
@@ -487,7 +485,7 @@ export function createDirectToolExecutor(
|
|
|
487
485
|
|
|
488
486
|
if (result.isError) {
|
|
489
487
|
const mcpContent = (result.content ?? []) as McpContent[];
|
|
490
|
-
const content = transformMcpContent(mcpContent);
|
|
488
|
+
const content = transformMcpContent(mcpContent, state.owner?.signal);
|
|
491
489
|
const outputContent = content.length > 0 ? content : [{ type: "text" as const, text: "(empty result)" }];
|
|
492
490
|
const schemaText = spec.inputSchema ? `\n\nExpected parameters:\n${formatSchema(spec.inputSchema)}` : "";
|
|
493
491
|
const guarded = await guardMcpOutput(outputContent, { ...outputGuardOptions, prefix: "Error: ", suffix: schemaText, emptyTextFallback: "Tool execution failed" });
|
|
@@ -497,7 +495,7 @@ export function createDirectToolExecutor(
|
|
|
497
495
|
};
|
|
498
496
|
}
|
|
499
497
|
|
|
500
|
-
const content = resolveMcpResultContent(result as Record<string, unknown
|
|
498
|
+
const content = resolveMcpResultContent(result as Record<string, unknown>, state.owner?.signal);
|
|
501
499
|
const outputContent = content.length > 0 ? content : [{ type: "text" as const, text: "(empty result)" }];
|
|
502
500
|
if (hasUi) {
|
|
503
501
|
const uiSummary = summarizeUiSessionResult(uiSession);
|
|
@@ -19,6 +19,7 @@ import { toolErrorOverride } from "./error-signal.ts";
|
|
|
19
19
|
import { createMcpRuntimeOwner, createOwnedUi, isAbortError, type McpRuntimeOwner } from "./runtime-owner.ts";
|
|
20
20
|
import { publishMcpStatusShutdown } from "./mcp-status.ts";
|
|
21
21
|
import { runMcpScript } from "./mcp-code.ts";
|
|
22
|
+
import { cleanupMaterializedBinaryResources } from "./tool-registrar.ts";
|
|
22
23
|
|
|
23
24
|
export type { McpAdapterOptions } from "./types.ts";
|
|
24
25
|
export {
|
|
@@ -168,13 +169,23 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
|
|
|
168
169
|
return resolveDirectTools(config, cache, prefix, envDirectToolOverride);
|
|
169
170
|
}
|
|
170
171
|
|
|
172
|
+
function getActiveToolsIfReady(): string[] | undefined {
|
|
173
|
+
try {
|
|
174
|
+
return pi.getActiveTools?.();
|
|
175
|
+
} catch (error) {
|
|
176
|
+
if (error instanceof Error
|
|
177
|
+
&& error.message.includes("Action methods cannot be called during extension loading")) return undefined;
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
171
182
|
function deactivateTools(toolNames: string[]): string[] {
|
|
172
183
|
if (toolNames.length === 0) return [];
|
|
173
184
|
const unregisterTool = (pi as ExtensionAPI & { unregisterTool?: (name: string) => boolean }).unregisterTool;
|
|
174
185
|
const unregistered = toolNames.filter((toolName) => unregisterTool?.(toolName) === true);
|
|
175
186
|
const fallbackNames = toolNames.filter((toolName) => !unregistered.includes(toolName));
|
|
176
187
|
const remove = new Set(toolNames);
|
|
177
|
-
const activeTools =
|
|
188
|
+
const activeTools = getActiveToolsIfReady();
|
|
178
189
|
if (!activeTools || activeTools.length === 0) {
|
|
179
190
|
for (const toolName of fallbackNames) fallbackDeactivatedTools.add(toolName);
|
|
180
191
|
return unregistered;
|
|
@@ -206,7 +217,7 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
|
|
|
206
217
|
registerDirectTool(spec);
|
|
207
218
|
registeredDirectTools.set(spec.prefixedName, fingerprint);
|
|
208
219
|
if (fallbackDeactivatedTools.delete(spec.prefixedName)) {
|
|
209
|
-
const activeTools =
|
|
220
|
+
const activeTools = getActiveToolsIfReady();
|
|
210
221
|
if (activeTools && !activeTools.includes(spec.prefixedName)) {
|
|
211
222
|
pi.setActiveTools([...activeTools, spec.prefixedName]);
|
|
212
223
|
}
|
|
@@ -275,6 +286,7 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
|
|
|
275
286
|
});
|
|
276
287
|
|
|
277
288
|
function startInitialization(ctx: ExtensionContext, owner: McpRuntimeOwner, oauthRuntime: McpOAuthRuntime, generation: number, staleReason: string): Promise<void> {
|
|
289
|
+
owner.addCleanup(() => cleanupMaterializedBinaryResources(owner.signal));
|
|
278
290
|
const promise = initializeMcp(pi, ctx, owner, {
|
|
279
291
|
...(programmaticConfig || options.configPath !== undefined
|
|
280
292
|
? {
|
|
@@ -847,7 +859,7 @@ function installMcpAdapter(pi: ExtensionAPI, options: McpAdapterOptions) {
|
|
|
847
859
|
registerProxyTool(description);
|
|
848
860
|
return;
|
|
849
861
|
}
|
|
850
|
-
const activeTools =
|
|
862
|
+
const activeTools = getActiveToolsIfReady();
|
|
851
863
|
if (activeTools && !activeTools.includes("mcp")) {
|
|
852
864
|
pi.setActiveTools([...activeTools, "mcp"]);
|
|
853
865
|
}
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
getOAuthState,
|
|
32
32
|
clearOAuthState,
|
|
33
33
|
getAuthBaseDir,
|
|
34
|
+
OAuthCredentialStoreError,
|
|
34
35
|
type AuthStorageOptions,
|
|
35
36
|
type StoredTokens,
|
|
36
37
|
} from "./mcp-auth.ts"
|
|
@@ -48,6 +49,10 @@ export interface McpOAuthRuntime {
|
|
|
48
49
|
|
|
49
50
|
export interface AuthenticateOptions {
|
|
50
51
|
onAuthorizationUrl?: (authorizationUrl: string) => void | Promise<void>
|
|
52
|
+
onAuthorizationInput?: (
|
|
53
|
+
authorizationUrl: string,
|
|
54
|
+
signal: AbortSignal,
|
|
55
|
+
) => Promise<string | undefined>
|
|
51
56
|
authStorageOptions?: AuthStorageOptions
|
|
52
57
|
signal?: AbortSignal
|
|
53
58
|
runtime?: McpOAuthRuntime
|
|
@@ -211,6 +216,27 @@ export function extractOAuthConfig(definition: ServerEntry): McpOAuthConfig {
|
|
|
211
216
|
}
|
|
212
217
|
config.clientUri = clientUri
|
|
213
218
|
}
|
|
219
|
+
if (definition.oauth?.logoUri !== undefined) {
|
|
220
|
+
if (typeof definition.oauth.logoUri !== "string") {
|
|
221
|
+
throw new Error("OAuth logoUri must be a string")
|
|
222
|
+
}
|
|
223
|
+
const logoUri = interpolateEnvVars(definition.oauth.logoUri).trim()
|
|
224
|
+
if (!logoUri) {
|
|
225
|
+
throw new Error("OAuth logoUri must not be empty")
|
|
226
|
+
}
|
|
227
|
+
// Consent screens fetch this server-side, so a local path silently renders
|
|
228
|
+
// nothing. Fail here instead, where the message can say why.
|
|
229
|
+
let parsed: URL
|
|
230
|
+
try {
|
|
231
|
+
parsed = new URL(logoUri)
|
|
232
|
+
} catch {
|
|
233
|
+
throw new Error("OAuth logoUri must be an absolute http(s) URL")
|
|
234
|
+
}
|
|
235
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
236
|
+
throw new Error("OAuth logoUri must be an absolute http(s) URL")
|
|
237
|
+
}
|
|
238
|
+
config.logoUri = logoUri
|
|
239
|
+
}
|
|
214
240
|
if (definition.oauth?.skipIssuerMetadataValidation !== undefined) {
|
|
215
241
|
if (typeof definition.oauth.skipIssuerMetadataValidation !== "boolean") {
|
|
216
242
|
throw new Error("OAuth skipIssuerMetadataValidation must be a boolean")
|
|
@@ -546,6 +572,53 @@ export function parseAuthorizationCodeInput(input: string, expectedState?: strin
|
|
|
546
572
|
return parseAuthorizationRedirectInput(input, expectedState).code
|
|
547
573
|
}
|
|
548
574
|
|
|
575
|
+
type AuthorizationResponse = {
|
|
576
|
+
input: AuthorizationCodeInput
|
|
577
|
+
source: "callback" | "manual"
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Wait for either the localhost callback or a manually pasted redirect URL.
|
|
582
|
+
* The manual input prompt is dismissed as soon as either path finishes.
|
|
583
|
+
*/
|
|
584
|
+
export async function waitForAuthorizationResponse(
|
|
585
|
+
callbackPromise: Promise<AuthorizationCodeInput>,
|
|
586
|
+
authorizationUrl: string,
|
|
587
|
+
expectedState: string,
|
|
588
|
+
onAuthorizationInput?: AuthenticateOptions["onAuthorizationInput"],
|
|
589
|
+
signal?: AbortSignal,
|
|
590
|
+
): Promise<AuthorizationResponse> {
|
|
591
|
+
if (!onAuthorizationInput) {
|
|
592
|
+
return {
|
|
593
|
+
input: await abortable(callbackPromise, signal),
|
|
594
|
+
source: "callback",
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
const inputController = new AbortController()
|
|
599
|
+
try {
|
|
600
|
+
const response = await abortable(Promise.race([
|
|
601
|
+
callbackPromise.then((input) => ({ input, source: "callback" as const })),
|
|
602
|
+
onAuthorizationInput(authorizationUrl, inputController.signal).then((input) => ({
|
|
603
|
+
input,
|
|
604
|
+
source: "manual" as const,
|
|
605
|
+
})),
|
|
606
|
+
]), signal)
|
|
607
|
+
|
|
608
|
+
if (response.source === "callback") return response
|
|
609
|
+
if (!response.input?.trim()) throw new Error("OAuth authentication cancelled")
|
|
610
|
+
if (!getSearchParamsFromInput(response.input.trim())) {
|
|
611
|
+
throw new Error("Paste the full OAuth callback URL, including its code and state parameters")
|
|
612
|
+
}
|
|
613
|
+
return {
|
|
614
|
+
input: parseAuthorizationRedirectInput(response.input, expectedState),
|
|
615
|
+
source: "manual",
|
|
616
|
+
}
|
|
617
|
+
} finally {
|
|
618
|
+
inputController.abort()
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
549
622
|
/**
|
|
550
623
|
* Complete OAuth authentication from manual user input.
|
|
551
624
|
*/
|
|
@@ -706,12 +779,22 @@ export async function authenticate(
|
|
|
706
779
|
console.warn(`MCP Auth: Failed to open browser for ${serverName}; waiting for manual callback`, { error })
|
|
707
780
|
}
|
|
708
781
|
|
|
709
|
-
const
|
|
782
|
+
const authorizationResponse = await waitForAuthorizationResponse(
|
|
783
|
+
callbackPromise,
|
|
784
|
+
authorizationUrl,
|
|
785
|
+
oauthState,
|
|
786
|
+
options.onAuthorizationInput,
|
|
787
|
+
signal,
|
|
788
|
+
)
|
|
789
|
+
if (authorizationResponse.source === "manual") {
|
|
790
|
+
cancelPendingCallback(oauthState)
|
|
791
|
+
}
|
|
710
792
|
|
|
711
|
-
// The callback server accepted only the flow-local reserved state.
|
|
793
|
+
// The callback server accepted only the flow-local reserved state. Manual
|
|
794
|
+
// input is checked against the same state before token exchange.
|
|
712
795
|
throwIfAborted(signal)
|
|
713
796
|
|
|
714
|
-
return await completeAuth(serverName,
|
|
797
|
+
return await completeAuth(serverName, authorizationResponse.input, {
|
|
715
798
|
...options,
|
|
716
799
|
...(signal ? { signal } : {}),
|
|
717
800
|
runtime,
|
|
@@ -799,7 +882,7 @@ export async function getValidToken(
|
|
|
799
882
|
authProvider.deactivate()
|
|
800
883
|
}
|
|
801
884
|
} catch (error) {
|
|
802
|
-
if (isAbortError(error, signal)) throw error
|
|
885
|
+
if (isAbortError(error, signal) || error instanceof OAuthCredentialStoreError) throw error
|
|
803
886
|
console.error(`MCP Auth: Token refresh failed for ${serverName}`, { error })
|
|
804
887
|
return null
|
|
805
888
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { createServer, type Server, type IncomingMessage, type ServerResponse } from "http"
|
|
9
|
+
import { getAppName } from "./agent-dir.ts"
|
|
9
10
|
import {
|
|
10
11
|
DEFAULT_OAUTH_CALLBACK_PATH,
|
|
11
12
|
getConfiguredOAuthCallbackPort,
|
|
@@ -15,45 +16,106 @@ import {
|
|
|
15
16
|
setOAuthCallbackPort,
|
|
16
17
|
} from "./mcp-oauth-provider.ts"
|
|
17
18
|
|
|
18
|
-
// HTML templates for callback responses
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
19
|
+
// HTML templates for callback responses.
|
|
20
|
+
//
|
|
21
|
+
// These pages are served from localhost during OAuth and are the last thing a
|
|
22
|
+
// user sees before returning to their terminal, so they are self-contained: no
|
|
23
|
+
// webfonts, no external assets, nothing that needs the network. They also name
|
|
24
|
+
// the host rather than hardcoding "Pi", so a distribution that rebrands pi
|
|
25
|
+
// (arc, tau, …) does not send its users back to an app they are not running.
|
|
26
|
+
|
|
27
|
+
/** Shared chrome: system fonts, a centred card, and light/dark support. */
|
|
28
|
+
const PAGE_STYLE = `
|
|
29
|
+
:root { color-scheme: light dark; }
|
|
30
|
+
* { box-sizing: border-box; }
|
|
31
|
+
body {
|
|
32
|
+
margin: 0;
|
|
33
|
+
min-height: 100vh;
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
justify-content: center;
|
|
37
|
+
padding: 24px;
|
|
38
|
+
font: 15px/1.55 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
39
|
+
background: #0f1117;
|
|
40
|
+
color: #e6e8ee;
|
|
41
|
+
}
|
|
42
|
+
.card {
|
|
43
|
+
width: 100%;
|
|
44
|
+
max-width: 26rem;
|
|
45
|
+
padding: 2.5rem 2rem;
|
|
46
|
+
text-align: center;
|
|
47
|
+
background: #161922;
|
|
48
|
+
border: 1px solid #242938;
|
|
49
|
+
border-radius: 14px;
|
|
50
|
+
box-shadow: 0 1px 2px rgba(0,0,0,.3), 0 12px 32px rgba(0,0,0,.25);
|
|
51
|
+
}
|
|
52
|
+
.badge {
|
|
53
|
+
width: 3rem; height: 3rem;
|
|
54
|
+
margin: 0 auto 1.25rem;
|
|
55
|
+
display: grid; place-items: center;
|
|
56
|
+
border-radius: 50%;
|
|
57
|
+
}
|
|
58
|
+
.badge svg { width: 1.5rem; height: 1.5rem; display: block; }
|
|
59
|
+
.ok { background: rgba(74,222,128,.12); color: #4ade80; }
|
|
60
|
+
.bad { background: rgba(248,113,113,.12); color: #f87171; }
|
|
61
|
+
h1 { margin: 0 0 .5rem; font-size: 1.15rem; font-weight: 600; letter-spacing: -0.01em; }
|
|
62
|
+
p { margin: 0; color: #9aa1b1; }
|
|
63
|
+
.app { color: #e6e8ee; font-weight: 500; }
|
|
64
|
+
.hint { margin-top: 1.25rem; font-size: .8125rem; color: #6b7280; }
|
|
65
|
+
code {
|
|
66
|
+
display: block;
|
|
67
|
+
margin-top: 1.25rem;
|
|
68
|
+
padding: .75rem .875rem;
|
|
69
|
+
text-align: left;
|
|
70
|
+
font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
71
|
+
color: #fca5a5;
|
|
72
|
+
background: rgba(248,113,113,.08);
|
|
73
|
+
border: 1px solid rgba(248,113,113,.2);
|
|
74
|
+
border-radius: 8px;
|
|
75
|
+
overflow-wrap: anywhere;
|
|
76
|
+
}
|
|
77
|
+
@media (prefers-color-scheme: light) {
|
|
78
|
+
body { background: #f6f7f9; color: #121620; }
|
|
79
|
+
.card { background: #fff; border-color: #e4e7ee; }
|
|
80
|
+
p { color: #5b6474; }
|
|
81
|
+
.app { color: #121620; }
|
|
82
|
+
.hint { color: #8b93a3; }
|
|
83
|
+
}`
|
|
84
|
+
|
|
85
|
+
const CHECK_ICON =
|
|
86
|
+
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg>'
|
|
87
|
+
|
|
88
|
+
const CROSS_ICON =
|
|
89
|
+
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12"/></svg>'
|
|
90
|
+
|
|
91
|
+
function page(options: {
|
|
92
|
+
title: string
|
|
93
|
+
heading: string
|
|
94
|
+
body: string
|
|
95
|
+
icon: string
|
|
96
|
+
tone: "ok" | "bad"
|
|
97
|
+
extra?: string
|
|
98
|
+
autoClose?: boolean
|
|
99
|
+
}): string {
|
|
100
|
+
return `<!DOCTYPE html>
|
|
101
|
+
<html lang="en">
|
|
41
102
|
<head>
|
|
42
|
-
<
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
h1 { color: #4ade80; margin-bottom: 1rem; }
|
|
47
|
-
p { color: #aaa; }
|
|
103
|
+
<meta charset="utf-8">
|
|
104
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
105
|
+
<title>${options.title}</title>
|
|
106
|
+
<style>${PAGE_STYLE}
|
|
48
107
|
</style>
|
|
49
108
|
</head>
|
|
50
109
|
<body>
|
|
51
|
-
<
|
|
52
|
-
<
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
110
|
+
<main class="card">
|
|
111
|
+
<div class="badge ${options.tone}">${options.icon}</div>
|
|
112
|
+
<h1>${options.heading}</h1>
|
|
113
|
+
<p>${options.body}</p>
|
|
114
|
+
${options.extra ?? ""}
|
|
115
|
+
</main>
|
|
116
|
+
${options.autoClose ? " <script>setTimeout(() => window.close(), 2000);</script>\n" : ""}</body>
|
|
56
117
|
</html>`
|
|
118
|
+
}
|
|
57
119
|
|
|
58
120
|
function escapeHtml(value: string): string {
|
|
59
121
|
return value
|
|
@@ -64,26 +126,41 @@ function escapeHtml(value: string): string {
|
|
|
64
126
|
.replace(/'/g, "'")
|
|
65
127
|
}
|
|
66
128
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
</
|
|
86
|
-
|
|
129
|
+
/** Built per request so a host that sets PI_PACKAGE_DIR late is still named right. */
|
|
130
|
+
function htmlSuccess(): string {
|
|
131
|
+
const app = escapeHtml(getAppName())
|
|
132
|
+
return page({
|
|
133
|
+
title: `${app} — Authorization Successful`,
|
|
134
|
+
heading: "Authorization Successful",
|
|
135
|
+
body: `You can close this window and return to <span class="app">${app}</span>.`,
|
|
136
|
+
icon: CHECK_ICON,
|
|
137
|
+
tone: "ok",
|
|
138
|
+
autoClose: true,
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function htmlManualSuccess(): string {
|
|
143
|
+
const app = escapeHtml(getAppName())
|
|
144
|
+
return page({
|
|
145
|
+
title: `${app} — Authorization Received`,
|
|
146
|
+
heading: "Authorization Received",
|
|
147
|
+
body: `Copy the full callback URL from your browser address bar and paste it back into <span class="app">${app}</span> with auth-complete.`,
|
|
148
|
+
icon: CHECK_ICON,
|
|
149
|
+
tone: "ok",
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function htmlError(error: string): string {
|
|
154
|
+
const app = escapeHtml(getAppName())
|
|
155
|
+
return page({
|
|
156
|
+
title: `${app} — Authorization Failed`,
|
|
157
|
+
heading: "Authorization Failed",
|
|
158
|
+
body: `Something went wrong during authorization. You can close this window and try again from <span class="app">${app}</span>.`,
|
|
159
|
+
icon: CROSS_ICON,
|
|
160
|
+
tone: "bad",
|
|
161
|
+
extra: `<code>${escapeHtml(error)}</code>`,
|
|
162
|
+
})
|
|
163
|
+
}
|
|
87
164
|
|
|
88
165
|
/** Result of a successful OAuth callback */
|
|
89
166
|
export interface OAuthCallbackResult {
|
|
@@ -145,7 +222,7 @@ function handleRequest(req: IncomingMessage, res: ServerResponse): void {
|
|
|
145
222
|
if (!state) {
|
|
146
223
|
const errorMsg = "Missing required state parameter - potential CSRF attack"
|
|
147
224
|
res.writeHead(400, { "Content-Type": "text/html" })
|
|
148
|
-
res.end(
|
|
225
|
+
res.end(htmlError(errorMsg))
|
|
149
226
|
return
|
|
150
227
|
}
|
|
151
228
|
|
|
@@ -157,14 +234,14 @@ function handleRequest(req: IncomingMessage, res: ServerResponse): void {
|
|
|
157
234
|
if (!pending && !isReserved) {
|
|
158
235
|
const errorMsg = "Invalid or expired state parameter - potential CSRF attack"
|
|
159
236
|
res.writeHead(400, { "Content-Type": "text/html" })
|
|
160
|
-
res.end(
|
|
237
|
+
res.end(htmlError(errorMsg))
|
|
161
238
|
return
|
|
162
239
|
}
|
|
163
240
|
|
|
164
241
|
const errorMsg = errorDescription || error
|
|
165
242
|
// Send HTTP response first before rejecting promise
|
|
166
243
|
res.writeHead(200, { "Content-Type": "text/html" })
|
|
167
|
-
res.end(
|
|
244
|
+
res.end(htmlError(errorMsg))
|
|
168
245
|
// Reject promise after response is sent (defer to allow test to attach handler)
|
|
169
246
|
if (pending) {
|
|
170
247
|
reservedAuthStates.delete(state)
|
|
@@ -179,20 +256,20 @@ function handleRequest(req: IncomingMessage, res: ServerResponse): void {
|
|
|
179
256
|
if (!pending && !isReserved) {
|
|
180
257
|
const errorMsg = "Invalid or expired state parameter - potential CSRF attack"
|
|
181
258
|
res.writeHead(400, { "Content-Type": "text/html" })
|
|
182
|
-
res.end(
|
|
259
|
+
res.end(htmlError(errorMsg))
|
|
183
260
|
return
|
|
184
261
|
}
|
|
185
262
|
|
|
186
263
|
// Require authorization code
|
|
187
264
|
if (!code) {
|
|
188
265
|
res.writeHead(400, { "Content-Type": "text/html" })
|
|
189
|
-
res.end(
|
|
266
|
+
res.end(htmlError("No authorization code provided"))
|
|
190
267
|
return
|
|
191
268
|
}
|
|
192
269
|
|
|
193
270
|
if (!pending) {
|
|
194
271
|
res.writeHead(200, { "Content-Type": "text/html" })
|
|
195
|
-
res.end(
|
|
272
|
+
res.end(htmlManualSuccess())
|
|
196
273
|
return
|
|
197
274
|
}
|
|
198
275
|
|
|
@@ -202,7 +279,7 @@ function handleRequest(req: IncomingMessage, res: ServerResponse): void {
|
|
|
202
279
|
pending.resolve({ code, ...(iss !== null ? { iss } : {}) })
|
|
203
280
|
|
|
204
281
|
res.writeHead(200, { "Content-Type": "text/html" })
|
|
205
|
-
res.end(
|
|
282
|
+
res.end(htmlSuccess())
|
|
206
283
|
}
|
|
207
284
|
|
|
208
285
|
/**
|
|
@@ -30,6 +30,37 @@ import {
|
|
|
30
30
|
type StoredClientInfo,
|
|
31
31
|
} from "./mcp-auth.ts"
|
|
32
32
|
import { resolveCommandSecret } from "./utils.ts"
|
|
33
|
+
import { getAppClientUri, getAppName } from "./agent-dir.ts"
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Client name advertised during Dynamic Client Registration.
|
|
37
|
+
*
|
|
38
|
+
* A distribution that rebrands pi (arc, tau, …) should register under its own
|
|
39
|
+
* name — otherwise every consent screen its users see asks them to authorize
|
|
40
|
+
* an app they have never run. Stock pi keeps the long-standing
|
|
41
|
+
* "Pi Coding Agent" so existing registrations are unaffected.
|
|
42
|
+
*/
|
|
43
|
+
function defaultClientName(): string {
|
|
44
|
+
const app = getAppName()
|
|
45
|
+
return app === "pi" ? "Pi Coding Agent" : app
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Client homepage advertised during Dynamic Client Registration.
|
|
50
|
+
*
|
|
51
|
+
* RFC 7591 defines client_uri as the home page *of the client*. Under a
|
|
52
|
+
* rebranded pi the client is that distribution, not this adapter, so pointing
|
|
53
|
+
* at the adapter's repository misidentifies it on the consent screen. There is
|
|
54
|
+
* no way to guess the right URL and a wrong one is worse than none, so the
|
|
55
|
+
* field is omitted unless a server config supplies oauth.clientUri.
|
|
56
|
+
*
|
|
57
|
+
* Stock pi keeps the historical value.
|
|
58
|
+
*/
|
|
59
|
+
function defaultClientUri(): string | undefined {
|
|
60
|
+
const declared = getAppClientUri()
|
|
61
|
+
if (declared) return declared
|
|
62
|
+
return getAppName() === "pi" ? "https://github.com/nicobailon/pi-mcp-adapter" : undefined
|
|
63
|
+
}
|
|
33
64
|
|
|
34
65
|
type IssuerBoundClientInformation = OAuthClientInformationMixed & { issuer?: string }
|
|
35
66
|
type IssuerBoundTokens = OAuthTokens & { issuer?: string }
|
|
@@ -86,6 +117,7 @@ export interface McpOAuthConfig {
|
|
|
86
117
|
redirectUri?: string
|
|
87
118
|
clientName?: string
|
|
88
119
|
clientUri?: string
|
|
120
|
+
logoUri?: string
|
|
89
121
|
skipIssuerMetadataValidation?: boolean
|
|
90
122
|
}
|
|
91
123
|
|
|
@@ -189,6 +221,11 @@ export class McpOAuthProvider implements OAuthClientProvider {
|
|
|
189
221
|
return this.redirectUrlSnapshot
|
|
190
222
|
}
|
|
191
223
|
|
|
224
|
+
/** Configured homepage, else the historical default on stock pi, else nothing. */
|
|
225
|
+
private get clientUri(): string | undefined {
|
|
226
|
+
return this.config.clientUri ?? defaultClientUri()
|
|
227
|
+
}
|
|
228
|
+
|
|
192
229
|
/**
|
|
193
230
|
* Client metadata for dynamic registration.
|
|
194
231
|
* Describes this client to the OAuth authorization server.
|
|
@@ -196,8 +233,9 @@ export class McpOAuthProvider implements OAuthClientProvider {
|
|
|
196
233
|
get clientMetadata(): OAuthClientMetadata {
|
|
197
234
|
if (this.usesClientCredentials) {
|
|
198
235
|
return {
|
|
199
|
-
client_name: this.config.clientName ??
|
|
200
|
-
client_uri: this.
|
|
236
|
+
client_name: this.config.clientName ?? defaultClientName(),
|
|
237
|
+
...(this.clientUri !== undefined ? { client_uri: this.clientUri } : {}),
|
|
238
|
+
...(this.config.logoUri !== undefined ? { logo_uri: this.config.logoUri } : {}),
|
|
201
239
|
redirect_uris: [],
|
|
202
240
|
grant_types: ["client_credentials"],
|
|
203
241
|
token_endpoint_auth_method: this.config.clientSecret ? "client_secret_post" : "none",
|
|
@@ -211,8 +249,9 @@ export class McpOAuthProvider implements OAuthClientProvider {
|
|
|
211
249
|
|
|
212
250
|
return {
|
|
213
251
|
redirect_uris: [redirectUrl],
|
|
214
|
-
client_name: this.config.clientName ??
|
|
215
|
-
client_uri: this.
|
|
252
|
+
client_name: this.config.clientName ?? defaultClientName(),
|
|
253
|
+
...(this.clientUri !== undefined ? { client_uri: this.clientUri } : {}),
|
|
254
|
+
...(this.config.logoUri !== undefined ? { logo_uri: this.config.logoUri } : {}),
|
|
216
255
|
grant_types: ["authorization_code", "refresh_token"],
|
|
217
256
|
response_types: ["code"],
|
|
218
257
|
token_endpoint_auth_method: this.config.clientSecret ? "client_secret_post" : "none",
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { getValidToken } from "./mcp-auth-flow.ts";
|
|
2
|
+
import {
|
|
3
|
+
inspectAuthForUrl,
|
|
4
|
+
updateTokens,
|
|
5
|
+
type AuthStorageOptions,
|
|
6
|
+
type StoredTokens,
|
|
7
|
+
} from "./mcp-auth.ts";
|
|
8
|
+
|
|
9
|
+
export type McpOAuthTokens = StoredTokens;
|
|
10
|
+
export type McpOAuthStorageOptions = AuthStorageOptions;
|
|
11
|
+
export interface McpOAuthTokenOptions {
|
|
12
|
+
authStorageOptions?: McpOAuthStorageOptions;
|
|
13
|
+
signal?: AbortSignal;
|
|
14
|
+
skipIssuerMetadataValidation?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export type McpOAuthTokenStatus =
|
|
17
|
+
| { status: "present"; tokens: McpOAuthTokens }
|
|
18
|
+
| { status: "absent" }
|
|
19
|
+
| { status: "unavailable"; message: string };
|
|
20
|
+
|
|
21
|
+
export async function getMcpOAuthTokensForUrl(
|
|
22
|
+
serverName: string,
|
|
23
|
+
serverUrl: string,
|
|
24
|
+
options: McpOAuthTokenOptions = {},
|
|
25
|
+
): Promise<McpOAuthTokens | undefined> {
|
|
26
|
+
return (await getValidToken(serverName, serverUrl, options)) ?? undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function inspectMcpOAuthTokensForUrl(
|
|
30
|
+
serverName: string,
|
|
31
|
+
serverUrl: string,
|
|
32
|
+
options?: McpOAuthStorageOptions,
|
|
33
|
+
): McpOAuthTokenStatus {
|
|
34
|
+
const status = inspectAuthForUrl(serverName, serverUrl, options);
|
|
35
|
+
if (status.status !== "present") return status;
|
|
36
|
+
return status.entry.tokens
|
|
37
|
+
? { status: "present", tokens: status.entry.tokens }
|
|
38
|
+
: { status: "absent" };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function updateMcpOAuthTokensForUrl(
|
|
42
|
+
serverName: string,
|
|
43
|
+
serverUrl: string,
|
|
44
|
+
tokens: McpOAuthTokens,
|
|
45
|
+
options?: McpOAuthStorageOptions,
|
|
46
|
+
): void {
|
|
47
|
+
updateTokens(serverName, tokens, serverUrl, options);
|
|
48
|
+
}
|