@herbertgao/pi-extensions 2026.8.4 → 2026.8.6
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 +5 -5
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +11 -3
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +11 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +24 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +91 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +7 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +7 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +21 -12
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +4 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +53 -14
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +22 -12
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +2 -4
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +18 -0
- package/node_modules/@herbertgao/pi-subagents/package.json +8 -4
- package/node_modules/@herbertgao/pi-subagents/src/agent-color.ts +72 -67
- package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +255 -0
- package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +191 -3
- package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +20 -4
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +289 -113
- package/node_modules/@herbertgao/pi-subagents/src/output-file.ts +24 -1
- package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +24 -2
- package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +15 -6
- package/node_modules/@herbertgao/pi-subagents/src/ui/schedule-menu.ts +9 -8
- package/node_modules/@herbertgao/pi-subagents/src/ui/select-item.ts +48 -0
- package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +9 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +2 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +20 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +6 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/keyboard.md +7 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +7 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +16 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +4 -2
- package/node_modules/@narumitw/pi-btw/README.md +32 -24
- package/node_modules/@narumitw/pi-btw/package.json +6 -6
- package/node_modules/@narumitw/pi-btw/src/btw.ts +4 -2
- package/node_modules/@narumitw/pi-btw/src/menu.ts +34 -13
- package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
- package/node_modules/pi-lens/CHANGELOG.md +2535 -954
- package/node_modules/pi-lens/README.md +13 -0
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +73 -18
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +314 -0
- package/node_modules/pi-lens/dist/clients/agent-behavior-client.js +21 -4
- package/node_modules/pi-lens/dist/clients/agent-nudge.js +14 -7
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +68 -16
- package/node_modules/pi-lens/dist/clients/ast-grep-types.js +0 -1
- package/node_modules/pi-lens/dist/clients/ast-grep-yaml-synth.js +37 -10
- package/node_modules/pi-lens/dist/clients/atomic-write-staging.js +63 -0
- package/node_modules/pi-lens/dist/clients/atomic-write.js +114 -14
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +193 -23
- package/node_modules/pi-lens/dist/clients/biome-client.js +136 -24
- package/node_modules/pi-lens/dist/clients/bootstrap.js +11 -2
- package/node_modules/pi-lens/dist/clients/bounded-cache.js +34 -0
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +192 -0
- package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
- package/node_modules/pi-lens/dist/clients/bus-publish.js +19 -4
- package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +47 -5
- package/node_modules/pi-lens/dist/clients/cache-manager.js +97 -9
- package/node_modules/pi-lens/dist/clients/cache-observability.js +301 -14
- package/node_modules/pi-lens/dist/clients/call-graph.js +449 -117
- package/node_modules/pi-lens/dist/clients/cascade-format.js +57 -2
- package/node_modules/pi-lens/dist/clients/child-unref.js +85 -0
- package/node_modules/pi-lens/dist/clients/codebase-model.js +52 -23
- package/node_modules/pi-lens/dist/clients/collateral-test-role.js +40 -0
- package/node_modules/pi-lens/dist/clients/complexity-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/console-guard-install.js +26 -0
- package/node_modules/pi-lens/dist/clients/cooperative-budget.js +42 -0
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +137 -31
- package/node_modules/pi-lens/dist/clients/debug-handles.js +206 -0
- package/node_modules/pi-lens/dist/clients/debug-heap.js +167 -0
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +153 -0
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +361 -77
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +65 -13
- package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +18 -4
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +176 -29
- package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +23 -7
- package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +8 -18
- package/node_modules/pi-lens/dist/clients/dispatch/facts/tree-sitter-facts.js +8 -5
- package/node_modules/pi-lens/dist/clients/dispatch/indent-detect.js +35 -0
- package/node_modules/pi-lens/dist/clients/dispatch/inline-suppressions.js +17 -8
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +1172 -683
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +14 -0
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +11 -1
- package/node_modules/pi-lens/dist/clients/dispatch/rule-id-normalize.js +50 -0
- package/node_modules/pi-lens/dist/clients/dispatch/rule-policy.js +135 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +23 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +5 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +154 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +5 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +47 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +9 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +4 -8
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +2 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +163 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +37 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tree-sitter.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +13 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +226 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +69 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +3 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +473 -42
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +22 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +97 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +5 -4
- package/node_modules/pi-lens/dist/clients/disposition-publish.js +18 -4
- package/node_modules/pi-lens/dist/clients/durable-store.js +97 -0
- package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +94 -3
- package/node_modules/pi-lens/dist/clients/extension-log.js +432 -0
- package/node_modules/pi-lens/dist/clients/extension-mode.js +73 -0
- package/node_modules/pi-lens/dist/clients/file-kinds.js +57 -0
- package/node_modules/pi-lens/dist/clients/file-role.js +18 -3
- package/node_modules/pi-lens/dist/clients/file-utils.js +56 -34
- package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
- package/node_modules/pi-lens/dist/clients/format-events-publish.js +51 -7
- package/node_modules/pi-lens/dist/clients/format-service.js +24 -8
- package/node_modules/pi-lens/dist/clients/formatters-lazy.js +8 -0
- package/node_modules/pi-lens/dist/clients/formatters.js +358 -61
- package/node_modules/pi-lens/dist/clients/generated-artifacts.js +148 -15
- package/node_modules/pi-lens/dist/clients/git-guard.js +678 -11
- package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +4 -0
- package/node_modules/pi-lens/dist/clients/go-client.js +23 -40
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +124 -7
- package/node_modules/pi-lens/dist/clients/grammar-source.js +12 -4
- package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +21 -3
- package/node_modules/pi-lens/dist/clients/host-ports.js +31 -0
- package/node_modules/pi-lens/dist/clients/installer/index.js +395 -100
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +142 -172
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +27 -55
- package/node_modules/pi-lens/dist/clients/knip-client.js +58 -42
- package/node_modules/pi-lens/dist/clients/language-policy.js +14 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +13 -7
- package/node_modules/pi-lens/dist/clients/latency-logger.js +72 -1
- package/node_modules/pi-lens/dist/clients/lens-config.js +12 -1
- package/node_modules/pi-lens/dist/clients/lens-engine.js +86 -17
- package/node_modules/pi-lens/dist/clients/lens-events.js +63 -9
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
- package/node_modules/pi-lens/dist/clients/lens-map.js +94 -19
- package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +73 -0
- package/node_modules/pi-lens/dist/clients/log-cleanup.js +2 -1
- package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +59 -14
- package/node_modules/pi-lens/dist/clients/lsp/client.js +662 -56
- package/node_modules/pi-lens/dist/clients/lsp/config.js +13 -2
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +115 -0
- package/node_modules/pi-lens/dist/clients/lsp/edits.js +1012 -121
- package/node_modules/pi-lens/dist/clients/lsp/index.js +2025 -800
- package/node_modules/pi-lens/dist/clients/lsp/launch.js +32 -26
- package/node_modules/pi-lens/dist/clients/lsp/path-utils.js +1 -1
- package/node_modules/pi-lens/dist/clients/lsp/ruby-drive-dirs.js +83 -0
- package/node_modules/pi-lens/dist/clients/lsp/server.js +437 -86
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +96 -0
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
- package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +18 -3
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +28 -4
- package/node_modules/pi-lens/dist/clients/lsp-lazy.js +8 -0
- package/node_modules/pi-lens/dist/clients/lsp-mutation.js +248 -0
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +127 -32
- package/node_modules/pi-lens/dist/clients/mcp/ipc.js +157 -24
- package/node_modules/pi-lens/dist/clients/mcp/session.js +235 -14
- package/node_modules/pi-lens/dist/clients/memory-sampler.js +115 -0
- package/node_modules/pi-lens/dist/clients/metrics-client.js +2 -1
- package/node_modules/pi-lens/dist/clients/metrics-history.js +2 -1
- package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
- package/node_modules/pi-lens/dist/clients/module-report-lsp.js +5 -0
- package/node_modules/pi-lens/dist/clients/module-report.js +168 -3
- package/node_modules/pi-lens/dist/clients/ndjson-logger.js +319 -118
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +1 -1
- package/node_modules/pi-lens/dist/clients/partial-edit-apply.js +120 -5
- package/node_modules/pi-lens/dist/clients/path-utils.js +110 -9
- package/node_modules/pi-lens/dist/clients/pipeline.js +76 -15
- package/node_modules/pi-lens/dist/clients/print-mode.js +21 -0
- package/node_modules/pi-lens/dist/clients/project-changes.js +40 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +3 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +1 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +8 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +23 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +21 -1
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +86 -9
- package/node_modules/pi-lens/dist/clients/project-report.js +27 -13
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +175 -20
- package/node_modules/pi-lens/dist/clients/project-trust.js +181 -0
- package/node_modules/pi-lens/dist/clients/quiet-window.js +18 -4
- package/node_modules/pi-lens/dist/clients/read-bridge.js +147 -0
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +159 -1
- package/node_modules/pi-lens/dist/clients/read-guard-tool-lines.js +150 -29
- package/node_modules/pi-lens/dist/clients/read-guard.js +185 -4
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +70 -84
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +1122 -221
- package/node_modules/pi-lens/dist/clients/review-graph/git-identity.js +2 -1
- package/node_modules/pi-lens/dist/clients/review-graph/import-resolvers.js +21 -10
- package/node_modules/pi-lens/dist/clients/review-graph/query.js +24 -0
- package/node_modules/pi-lens/dist/clients/review-graph/symbol-id.js +46 -0
- package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +57 -6
- package/node_modules/pi-lens/dist/clients/review-graph/workspace-modules.js +51 -3
- package/node_modules/pi-lens/dist/clients/review-graph-logger.js +30 -1
- package/node_modules/pi-lens/dist/clients/ruff-client.js +27 -44
- package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +257 -77
- package/node_modules/pi-lens/dist/clients/runtime-context.js +129 -12
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +242 -26
- package/node_modules/pi-lens/dist/clients/runtime-session.js +428 -117
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +71 -48
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +342 -33
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +619 -93
- package/node_modules/pi-lens/dist/clients/rust-client.js +23 -38
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +777 -62
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +90 -6
- package/node_modules/pi-lens/dist/clients/session-state-store.js +3 -2
- package/node_modules/pi-lens/dist/clients/sg-runner.js +424 -196
- package/node_modules/pi-lens/dist/clients/slow-fs.js +2 -1
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +231 -0
- package/node_modules/pi-lens/dist/clients/source-filter.js +184 -20
- package/node_modules/pi-lens/dist/clients/source-walker.js +66 -10
- package/node_modules/pi-lens/dist/clients/startup-scan.js +15 -16
- package/node_modules/pi-lens/dist/clients/startup-timing.js +7 -1
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +469 -32
- package/node_modules/pi-lens/dist/clients/tool-event.js +35 -0
- package/node_modules/pi-lens/dist/clients/tool-policy.js +390 -39
- package/node_modules/pi-lens/dist/clients/tool-render.js +155 -0
- package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +6 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +419 -50
- package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +21 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-query-loader.js +7 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +7 -2
- package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +44 -7
- package/node_modules/pi-lens/dist/clients/user-notify.js +51 -0
- package/node_modules/pi-lens/dist/clients/vanished-instance-marker.js +57 -0
- package/node_modules/pi-lens/dist/clients/warm-attach.js +50 -13
- package/node_modules/pi-lens/dist/clients/widget-state.js +300 -43
- package/node_modules/pi-lens/dist/clients/word-index.js +680 -81
- package/node_modules/pi-lens/dist/clients/workspace-topology.js +89 -6
- package/node_modules/pi-lens/dist/index.js +51785 -36118
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +182 -29
- package/node_modules/pi-lens/dist/mcp/server.js +219 -46
- package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
- package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
- package/node_modules/pi-lens/dist/tools/ast-grep-search.js +93 -27
- package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +203 -38
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +210 -42
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -3
- package/node_modules/pi-lens/dist/tools/module-report.js +9 -0
- package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
- package/node_modules/pi-lens/docs/agent-guide.md +40 -15
- package/node_modules/pi-lens/docs/agent-tools.md +21 -10
- package/node_modules/pi-lens/docs/analysisall.md +192 -0
- package/node_modules/pi-lens/docs/api-ports-inventory.md +165 -0
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +14 -3
- package/node_modules/pi-lens/docs/durable-store-audit-1202.md +32 -0
- package/node_modules/pi-lens/docs/environment-variables.md +44 -0
- package/node_modules/pi-lens/docs/fable.md +27 -0
- package/node_modules/pi-lens/docs/features.md +45 -3
- package/node_modules/pi-lens/docs/globalconfig.md +56 -1
- package/node_modules/pi-lens/docs/language-coverage.md +4 -3
- package/node_modules/pi-lens/docs/lsp-capability-matrix.md +2 -2
- package/node_modules/pi-lens/docs/mcp.md +37 -4
- package/node_modules/pi-lens/docs/servercapabilities.md +3 -3
- package/node_modules/pi-lens/docs/settings.md +18 -1
- package/node_modules/pi-lens/docs/tools.md +4 -1
- package/node_modules/pi-lens/docs/usage.md +41 -5
- package/node_modules/pi-lens/docs/word-index.md +35 -0
- package/node_modules/pi-lens/package.json +10 -10
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-except-test.yml +49 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-host-path-in-win32-branch-test.yml +87 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-js-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-nested-links-test.yml +11 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-json-store-write-test.yml +17 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-win32-isabsolute-for-qualification-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-except.yml +3 -15
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +25 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-nested-links.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +30 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +13 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +27 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +23 -0
- package/node_modules/pi-lens/rules/tree-sitter-queries/tsx/no-nested-links.yml +4 -6
- package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/duplicate-function-arg.yml +2 -2
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +134 -2
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +10 -11
- package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +8 -114
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +129 -0
- package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +38 -0
- package/node_modules/pi-mcp-adapter/README.md +38 -4
- package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
- package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +1 -1
- package/node_modules/pi-mcp-adapter/cli.js +25 -4
- package/node_modules/pi-mcp-adapter/commands.ts +15 -7
- package/node_modules/pi-mcp-adapter/config.ts +4 -4
- package/node_modules/pi-mcp-adapter/direct-tools.ts +58 -8
- package/node_modules/pi-mcp-adapter/host-html-template.ts +0 -8
- package/node_modules/pi-mcp-adapter/index.ts +8 -3
- package/node_modules/pi-mcp-adapter/init.ts +29 -5
- package/node_modules/pi-mcp-adapter/lifecycle.ts +0 -4
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +1 -2
- package/node_modules/pi-mcp-adapter/mcp-auth.ts +60 -2
- package/node_modules/pi-mcp-adapter/mcp-panel.ts +45 -9
- package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +22 -20
- package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +37 -4
- package/node_modules/pi-mcp-adapter/npx-resolver.ts +81 -26
- package/node_modules/pi-mcp-adapter/package.json +4 -3
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +108 -26
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +336 -0
- package/node_modules/pi-mcp-adapter/search-ranking.ts +86 -7
- package/node_modules/pi-mcp-adapter/server-manager.ts +41 -7
- package/node_modules/pi-mcp-adapter/session-recovery.ts +8 -2
- package/node_modules/pi-mcp-adapter/tool-approval.ts +48 -7
- package/node_modules/pi-mcp-adapter/tool-metadata.ts +62 -4
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +138 -12
- package/node_modules/pi-mcp-adapter/types.ts +176 -27
- package/node_modules/pi-mcp-adapter/ui-server.ts +20 -1
- package/node_modules/pi-web-access/CHANGELOG.md +14 -0
- package/node_modules/pi-web-access/README.md +18 -12
- package/node_modules/pi-web-access/auth-fetch.ts +148 -0
- package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
- package/node_modules/pi-web-access/curator-page.ts +5 -3
- package/node_modules/pi-web-access/curator-server.ts +2 -1
- package/node_modules/pi-web-access/extract.ts +106 -34
- package/node_modules/pi-web-access/fetch-params.ts +17 -3
- package/node_modules/pi-web-access/firecrawl.ts +172 -12
- package/node_modules/pi-web-access/gemini-search.ts +18 -4
- package/node_modules/pi-web-access/index.ts +120 -48
- package/node_modules/pi-web-access/package.json +2 -2
- package/node_modules/pi-web-access/summary-review.ts +11 -5
- package/node_modules/pi-web-access/youtube-extract.ts +2 -2
- package/package.json +12 -13
- package/node_modules/pi-lens/dist/clients/ast-grep-parser.js +0 -86
- package/node_modules/pi-lens/dist/clients/lsp/interactive-install.js +0 -367
- package/node_modules/pi-lens/dist/clients/lsp/lsp-index.js +0 -10
- package/node_modules/pi-lens/dist/clients/lsp/server-strategies.js +0 -5
- package/node_modules/pi-lens/dist/clients/project-metadata.js +0 -690
- package/node_modules/pi-lens/dist/clients/source-groups.js +0 -96
- package/node_modules/pi-lens/dist/clients/startup-marker.js +0 -6
- package/node_modules/pi-lens/dist/clients/types.js +0 -11
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import { spawn, spawnSync, type ChildProcess } from "node:child_process";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import type { FetchLike } from "@modelcontextprotocol/client";
|
|
4
|
+
import type { HttpRequestHeadersCommand } from "./types.ts";
|
|
5
|
+
import { interpolateEnvVars } from "./utils.ts";
|
|
6
|
+
|
|
7
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
8
|
+
const MAX_OUTPUT_BYTES = 64 * 1024;
|
|
9
|
+
const USE_PROCESS_GROUP = process.platform !== "win32";
|
|
10
|
+
const CLEANUP_TOKEN_ENV = "PI_MCP_REQUEST_HEADERS_CLEANUP_TOKEN";
|
|
11
|
+
|
|
12
|
+
function isNoSuchProcessError(error: unknown): boolean {
|
|
13
|
+
return typeof error === "object" && error !== null && "code" in error && (error as NodeJS.ErrnoException).code === "ESRCH";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function runPosixPs(args: string[]): { status: number | null; stdout: string } {
|
|
17
|
+
if (process.env.PI_MCP_ADAPTER_TEST_FAIL_PS === "1") return { status: 1, stdout: "" };
|
|
18
|
+
return spawnSync("ps", args, { encoding: "utf8" });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function collectPosixDescendantPids(rootPid: number): number[] {
|
|
22
|
+
const result = runPosixPs(["-axo", "pid=,ppid="]);
|
|
23
|
+
if (result.status !== 0) {
|
|
24
|
+
throw new Error(`HTTP request headers command cleanup failed: ps exited with code ${result.status ?? "unknown"}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const childrenByParent = new Map<number, number[]>();
|
|
28
|
+
for (const line of result.stdout.split("\n")) {
|
|
29
|
+
const [pidText, ppidText] = line.trim().split(/\s+/, 2);
|
|
30
|
+
const pid = Number(pidText);
|
|
31
|
+
const ppid = Number(ppidText);
|
|
32
|
+
if (!Number.isInteger(pid) || !Number.isInteger(ppid)) continue;
|
|
33
|
+
const children = childrenByParent.get(ppid);
|
|
34
|
+
if (children) children.push(pid);
|
|
35
|
+
else childrenByParent.set(ppid, [pid]);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const descendants: number[] = [];
|
|
39
|
+
const stack = [...(childrenByParent.get(rootPid) ?? [])];
|
|
40
|
+
while (stack.length > 0) {
|
|
41
|
+
const pid = stack.pop()!;
|
|
42
|
+
descendants.push(pid);
|
|
43
|
+
stack.push(...(childrenByParent.get(pid) ?? []));
|
|
44
|
+
}
|
|
45
|
+
return descendants;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function collectPosixCleanupTokenPids(cleanupToken: string): number[] {
|
|
49
|
+
const result = runPosixPs(["axeww", "-o", "pid=,command="]);
|
|
50
|
+
if (result.status !== 0) {
|
|
51
|
+
throw new Error(`HTTP request headers command cleanup failed: ps exited with code ${result.status ?? "unknown"}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const needle = `${CLEANUP_TOKEN_ENV}=${cleanupToken}`;
|
|
55
|
+
const pids: number[] = [];
|
|
56
|
+
for (const line of result.stdout.split("\n")) {
|
|
57
|
+
const trimmed = line.trim();
|
|
58
|
+
if (!trimmed.includes(needle)) continue;
|
|
59
|
+
const [pidText] = trimmed.split(/\s+/, 1);
|
|
60
|
+
const pid = Number(pidText);
|
|
61
|
+
if (Number.isInteger(pid) && pid !== process.pid) pids.push(pid);
|
|
62
|
+
}
|
|
63
|
+
return pids;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function assertPosixProcessDiscoveryAvailable(): void {
|
|
67
|
+
collectPosixDescendantPids(process.pid);
|
|
68
|
+
collectPosixCleanupTokenPids(`${process.pid}-preflight`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function isTaskkillNoSuchProcess(result: ReturnType<typeof spawnSync>): boolean {
|
|
72
|
+
return `${result.stdout ?? ""}\n${result.stderr ?? ""}`.toLowerCase().includes("not found");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function signalPid(pid: number, signal: NodeJS.Signals): void {
|
|
76
|
+
try {
|
|
77
|
+
process.kill(pid, signal);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if (!isNoSuchProcessError(error)) throw error;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function signalProcessGroup(pid: number, signal: NodeJS.Signals): void {
|
|
84
|
+
try {
|
|
85
|
+
process.kill(-pid, signal);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
if (!isNoSuchProcessError(error)) throw error;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function killRequestHeadersCommand(child: ChildProcess, trackedPosixDescendantPids = new Set<number>(), cleanupToken?: string): void {
|
|
92
|
+
if (process.platform === "win32" && child.pid !== undefined) {
|
|
93
|
+
const result = spawnSync("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
|
|
94
|
+
encoding: "utf8",
|
|
95
|
+
windowsHide: true,
|
|
96
|
+
});
|
|
97
|
+
if (result.status === 0 || isTaskkillNoSuchProcess(result)) return;
|
|
98
|
+
throw new Error(`HTTP request headers command cleanup failed: taskkill exited with code ${result.status ?? "unknown"}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (USE_PROCESS_GROUP && child.pid !== undefined) {
|
|
102
|
+
const frozenPids = new Set<number>();
|
|
103
|
+
let cleanupError: Error | undefined;
|
|
104
|
+
try {
|
|
105
|
+
signalProcessGroup(child.pid, "SIGSTOP");
|
|
106
|
+
for (const pid of trackedPosixDescendantPids) {
|
|
107
|
+
signalPid(pid, "SIGSTOP");
|
|
108
|
+
frozenPids.add(pid);
|
|
109
|
+
}
|
|
110
|
+
let stablePasses = 0;
|
|
111
|
+
for (let pass = 0; pass < 16; pass++) {
|
|
112
|
+
const candidates = [
|
|
113
|
+
...collectPosixDescendantPids(child.pid),
|
|
114
|
+
...(cleanupToken ? collectPosixCleanupTokenPids(cleanupToken) : []),
|
|
115
|
+
];
|
|
116
|
+
const newPids = candidates.filter(pid => !frozenPids.has(pid));
|
|
117
|
+
if (newPids.length === 0) {
|
|
118
|
+
stablePasses++;
|
|
119
|
+
if (stablePasses >= 2) return;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
stablePasses = 0;
|
|
123
|
+
for (const pid of newPids) {
|
|
124
|
+
signalPid(pid, "SIGSTOP");
|
|
125
|
+
frozenPids.add(pid);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
cleanupError = new Error("HTTP request headers command cleanup failed: descendant process tree did not stabilize");
|
|
129
|
+
} catch (error) {
|
|
130
|
+
cleanupError = error instanceof Error ? error : new Error(String(error));
|
|
131
|
+
} finally {
|
|
132
|
+
signalProcessGroup(child.pid, "SIGKILL");
|
|
133
|
+
for (const pid of frozenPids) signalPid(pid, "SIGKILL");
|
|
134
|
+
}
|
|
135
|
+
if (cleanupError) throw cleanupError;
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
child.kill("SIGKILL");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface HttpRequestCommandEnvelope {
|
|
143
|
+
version: 1;
|
|
144
|
+
method: string;
|
|
145
|
+
url: string;
|
|
146
|
+
bodyBase64: string;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
type CommandResult =
|
|
150
|
+
| { status: "error"; error: Error }
|
|
151
|
+
| { status: "success"; headers: Headers };
|
|
152
|
+
|
|
153
|
+
function resolvedCommand(config: HttpRequestHeadersCommand): {
|
|
154
|
+
command: string;
|
|
155
|
+
args: string[];
|
|
156
|
+
env: NodeJS.ProcessEnv;
|
|
157
|
+
timeoutMs: number;
|
|
158
|
+
} {
|
|
159
|
+
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
160
|
+
throw new Error("HTTP request headers command must be an object");
|
|
161
|
+
}
|
|
162
|
+
if (typeof config.command !== "string" || config.command.trim() === "") {
|
|
163
|
+
throw new Error("HTTP request headers command requires a non-empty command");
|
|
164
|
+
}
|
|
165
|
+
if (config.args !== undefined && (!Array.isArray(config.args) || config.args.some(arg => typeof arg !== "string"))) {
|
|
166
|
+
throw new Error("HTTP request headers command args must be strings");
|
|
167
|
+
}
|
|
168
|
+
if (config.env !== undefined && (
|
|
169
|
+
typeof config.env !== "object"
|
|
170
|
+
|| Array.isArray(config.env)
|
|
171
|
+
|| Object.values(config.env).some(value => typeof value !== "string")
|
|
172
|
+
)) {
|
|
173
|
+
throw new Error("HTTP request headers command env values must be strings");
|
|
174
|
+
}
|
|
175
|
+
const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
176
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > 60_000) {
|
|
177
|
+
throw new Error("HTTP request headers command timeoutMs must be an integer between 1 and 60000");
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
command: interpolateEnvVars(config.command),
|
|
181
|
+
args: (config.args ?? []).map(interpolateEnvVars),
|
|
182
|
+
env: {
|
|
183
|
+
...process.env,
|
|
184
|
+
...Object.fromEntries(
|
|
185
|
+
Object.entries(config.env ?? {}).map(([key, value]) => [key, interpolateEnvVars(value)]),
|
|
186
|
+
),
|
|
187
|
+
},
|
|
188
|
+
timeoutMs,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async function invokeRequestHeadersCommand(
|
|
193
|
+
config: HttpRequestHeadersCommand,
|
|
194
|
+
envelope: HttpRequestCommandEnvelope,
|
|
195
|
+
signal: AbortSignal,
|
|
196
|
+
): Promise<Headers> {
|
|
197
|
+
const resolved = resolvedCommand(config);
|
|
198
|
+
if (USE_PROCESS_GROUP) assertPosixProcessDiscoveryAvailable();
|
|
199
|
+
return new Promise<Headers>((resolve, reject) => {
|
|
200
|
+
let stdout = Buffer.alloc(0);
|
|
201
|
+
let settled = false;
|
|
202
|
+
const cleanupToken = randomUUID();
|
|
203
|
+
const child = spawn(resolved.command, resolved.args, {
|
|
204
|
+
env: { ...resolved.env, [CLEANUP_TOKEN_ENV]: cleanupToken },
|
|
205
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
206
|
+
windowsHide: true,
|
|
207
|
+
detached: USE_PROCESS_GROUP,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const trackedPosixDescendantPids = new Set<number>();
|
|
211
|
+
let trackingError: Error | undefined;
|
|
212
|
+
const trackPosixDescendants = () => {
|
|
213
|
+
if (!USE_PROCESS_GROUP || child.pid === undefined || settled || trackingError) return;
|
|
214
|
+
try {
|
|
215
|
+
for (const pid of collectPosixDescendantPids(child.pid)) trackedPosixDescendantPids.add(pid);
|
|
216
|
+
for (const pid of collectPosixCleanupTokenPids(cleanupToken)) trackedPosixDescendantPids.add(pid);
|
|
217
|
+
} catch (error) {
|
|
218
|
+
trackingError = error instanceof Error ? error : new Error(String(error));
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
const descendantTracker = USE_PROCESS_GROUP ? setInterval(trackPosixDescendants, 50) : undefined;
|
|
222
|
+
descendantTracker?.unref();
|
|
223
|
+
|
|
224
|
+
const finish = (result: CommandResult) => {
|
|
225
|
+
if (settled) return;
|
|
226
|
+
settled = true;
|
|
227
|
+
clearTimeout(timer);
|
|
228
|
+
if (descendantTracker) clearInterval(descendantTracker);
|
|
229
|
+
signal.removeEventListener("abort", abort);
|
|
230
|
+
if (result.status === "error") reject(result.error);
|
|
231
|
+
else resolve(result.headers);
|
|
232
|
+
};
|
|
233
|
+
const finishAfterKill = (result: CommandResult) => {
|
|
234
|
+
try {
|
|
235
|
+
killRequestHeadersCommand(child, trackedPosixDescendantPids, cleanupToken);
|
|
236
|
+
finish(result);
|
|
237
|
+
} catch (cleanupError) {
|
|
238
|
+
finish({ status: "error", error: cleanupError instanceof Error ? cleanupError : new Error(String(cleanupError)) });
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
const failAfterKill = (message: string) => {
|
|
242
|
+
finishAfterKill({ status: "error", error: trackingError ?? new Error(message) });
|
|
243
|
+
};
|
|
244
|
+
const abort = () => {
|
|
245
|
+
failAfterKill("HTTP request headers command aborted");
|
|
246
|
+
};
|
|
247
|
+
const timer = setTimeout(() => {
|
|
248
|
+
failAfterKill(`HTTP request headers command timed out after ${resolved.timeoutMs}ms`);
|
|
249
|
+
}, resolved.timeoutMs);
|
|
250
|
+
|
|
251
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
252
|
+
if (signal.aborted) {
|
|
253
|
+
abort();
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
child.on("error", () => finish({ status: "error", error: new Error("HTTP request headers command failed to start") }));
|
|
258
|
+
child.stdout.on("data", (chunk: Buffer | string) => {
|
|
259
|
+
if (settled) return;
|
|
260
|
+
stdout = Buffer.concat([stdout, Buffer.from(chunk)]);
|
|
261
|
+
if (stdout.byteLength > MAX_OUTPUT_BYTES) {
|
|
262
|
+
failAfterKill("HTTP request headers command output exceeded 64 KiB");
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
child.on("close", code => {
|
|
266
|
+
if (settled) return;
|
|
267
|
+
if (trackingError) {
|
|
268
|
+
failAfterKill(trackingError.message);
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
if (code !== 0) {
|
|
272
|
+
failAfterKill(`HTTP request headers command exited with code ${code ?? "unknown"}`);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
let parsed: unknown;
|
|
276
|
+
try {
|
|
277
|
+
parsed = JSON.parse(stdout.toString("utf8"));
|
|
278
|
+
} catch {
|
|
279
|
+
finishAfterKill({ status: "error", error: new Error("HTTP request headers command returned invalid JSON") });
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
283
|
+
finishAfterKill({ status: "error", error: new Error("HTTP request headers command must return a JSON object") });
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const entries = Object.entries(parsed);
|
|
287
|
+
if (entries.some(([, value]) => typeof value !== "string")) {
|
|
288
|
+
finishAfterKill({ status: "error", error: new Error("HTTP request headers command values must be strings") });
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
try {
|
|
292
|
+
finishAfterKill({ status: "success", headers: new Headers(entries as Array<[string, string]>) });
|
|
293
|
+
} catch {
|
|
294
|
+
finishAfterKill({ status: "error", error: new Error("HTTP request headers command returned an invalid header") });
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
child.stdin.on("error", () => {});
|
|
299
|
+
child.stdin.end(JSON.stringify(envelope));
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** Wrap fetch so a trusted command can derive headers from the exact request. */
|
|
304
|
+
export function createRequestHeadersCommandFetch(
|
|
305
|
+
config: HttpRequestHeadersCommand,
|
|
306
|
+
delegate: FetchLike = globalThis.fetch,
|
|
307
|
+
): FetchLike {
|
|
308
|
+
// Validate static configuration before the first request.
|
|
309
|
+
resolvedCommand(config);
|
|
310
|
+
return async (input: URL | RequestInfo, init?: RequestInit): Promise<Response> => {
|
|
311
|
+
const request = new Request(input, init);
|
|
312
|
+
const body = Buffer.from(await request.clone().arrayBuffer());
|
|
313
|
+
const derived = await invokeRequestHeadersCommand(config, {
|
|
314
|
+
version: 1,
|
|
315
|
+
method: request.method.toUpperCase(),
|
|
316
|
+
url: request.url,
|
|
317
|
+
bodyBase64: body.toString("base64"),
|
|
318
|
+
}, request.signal);
|
|
319
|
+
const headers = new Headers(request.headers);
|
|
320
|
+
derived.forEach((value, name) => headers.set(name, value));
|
|
321
|
+
return delegate(new URL(request.url), {
|
|
322
|
+
method: request.method,
|
|
323
|
+
headers,
|
|
324
|
+
...(request.method === "GET" || request.method === "HEAD" ? {} : { body }),
|
|
325
|
+
signal: request.signal,
|
|
326
|
+
cache: request.cache,
|
|
327
|
+
credentials: request.credentials,
|
|
328
|
+
integrity: request.integrity,
|
|
329
|
+
keepalive: request.keepalive,
|
|
330
|
+
mode: request.mode,
|
|
331
|
+
redirect: request.redirect,
|
|
332
|
+
referrer: request.referrer,
|
|
333
|
+
referrerPolicy: request.referrerPolicy,
|
|
334
|
+
});
|
|
335
|
+
};
|
|
336
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { McpExtensionState } from "./state.ts";
|
|
2
|
-
import type { ToolMetadata } from "./types.ts";
|
|
3
|
-
import { getServerPrefix, isServerDisabled } from "./types.ts";
|
|
2
|
+
import type { ServerEntry, ToolMetadata, ToolPrefix } from "./types.ts";
|
|
3
|
+
import { getServerPrefix, getToolNameCandidates, isServerDisabled, matchesToolPattern, resolveToolPrefix } from "./types.ts";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Shortest field token allowed to stem-match a longer query token.
|
|
@@ -14,6 +14,7 @@ const FIELD_WEIGHTS = {
|
|
|
14
14
|
originalName: 10,
|
|
15
15
|
server: 8,
|
|
16
16
|
description: 5,
|
|
17
|
+
keywords: 5,
|
|
17
18
|
} as const;
|
|
18
19
|
|
|
19
20
|
export interface RankedToolMatch {
|
|
@@ -22,6 +23,36 @@ export interface RankedToolMatch {
|
|
|
22
23
|
score: number;
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Resolve the configured searchKeywords entries that apply to a tool.
|
|
28
|
+
* Keys match by original name, prefixed name, or glob — the same candidate
|
|
29
|
+
* set includeTools/excludeTools use — and all matching entries are unioned.
|
|
30
|
+
*/
|
|
31
|
+
export function resolveSearchKeywords(
|
|
32
|
+
definition: ServerEntry | undefined,
|
|
33
|
+
toolOriginalName: string,
|
|
34
|
+
serverName: string,
|
|
35
|
+
globalPrefix: ToolPrefix,
|
|
36
|
+
): string[] {
|
|
37
|
+
const map = definition?.searchKeywords;
|
|
38
|
+
if (!map || typeof map !== "object" || Array.isArray(map)) return [];
|
|
39
|
+
const candidates = getToolNameCandidates(toolOriginalName, serverName, resolveToolPrefix(definition, globalPrefix));
|
|
40
|
+
const keywords: string[] = [];
|
|
41
|
+
const seen = new Set<string>();
|
|
42
|
+
for (const [pattern, values] of Object.entries(map)) {
|
|
43
|
+
if (!Array.isArray(values)) continue;
|
|
44
|
+
if (!matchesToolPattern(candidates, [pattern])) continue;
|
|
45
|
+
for (const value of values) {
|
|
46
|
+
if (typeof value !== "string") continue;
|
|
47
|
+
const trimmed = value.trim();
|
|
48
|
+
if (!trimmed || seen.has(trimmed)) continue;
|
|
49
|
+
seen.add(trimmed);
|
|
50
|
+
keywords.push(trimmed);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return keywords;
|
|
54
|
+
}
|
|
55
|
+
|
|
25
56
|
export function normalizeSearchText(value: string): string {
|
|
26
57
|
return value
|
|
27
58
|
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
@@ -33,7 +64,7 @@ export function tokenize(value: string): string[] {
|
|
|
33
64
|
return normalizeSearchText(value).split(/[^a-z0-9]+/).filter(Boolean);
|
|
34
65
|
}
|
|
35
66
|
|
|
36
|
-
export function scoreToolMatch(tool: ToolMetadata, server: string, query: string): number | null {
|
|
67
|
+
export function scoreToolMatch(tool: ToolMetadata, server: string, query: string, keywords?: string[]): number | null {
|
|
37
68
|
const normalizedQuery = normalizeSearchText(query).trim();
|
|
38
69
|
const queryTokens = tokenize(query);
|
|
39
70
|
if (queryTokens.length === 0) return null;
|
|
@@ -78,6 +109,43 @@ export function scoreToolMatch(tool: ToolMetadata, server: string, query: string
|
|
|
78
109
|
}
|
|
79
110
|
}
|
|
80
111
|
|
|
112
|
+
// Configured keywords are discrete phrases, so the phrase-level bonus is
|
|
113
|
+
// computed per phrase (best match wins) rather than on a joined string,
|
|
114
|
+
// which would phrase-match queries spanning two unrelated keywords.
|
|
115
|
+
if (keywords !== undefined && keywords.length > 0) {
|
|
116
|
+
const weight = FIELD_WEIGHTS.keywords;
|
|
117
|
+
const phrases = keywords.map(keyword => normalizeSearchText(keyword).trim()).filter(Boolean);
|
|
118
|
+
let phraseScore = 0;
|
|
119
|
+
for (const phrase of phrases) {
|
|
120
|
+
if (phrase === normalizedQuery) {
|
|
121
|
+
phraseScore = Math.max(phraseScore, weight * 14);
|
|
122
|
+
phraseMatched = true;
|
|
123
|
+
wholeFieldExact = true;
|
|
124
|
+
} else if (phrase.startsWith(normalizedQuery)) {
|
|
125
|
+
phraseScore = Math.max(phraseScore, weight * 9);
|
|
126
|
+
phraseMatched = true;
|
|
127
|
+
} else if (phrase.includes(normalizedQuery)) {
|
|
128
|
+
phraseScore = Math.max(phraseScore, weight * 6);
|
|
129
|
+
phraseMatched = true;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
score += phraseScore;
|
|
133
|
+
|
|
134
|
+
const keywordTokens = phrases.flatMap(tokenize);
|
|
135
|
+
for (const token of queryTokens) {
|
|
136
|
+
if (keywordTokens.includes(token)) {
|
|
137
|
+
score += weight * 4;
|
|
138
|
+
matchedTokens.add(token);
|
|
139
|
+
} else if (keywordTokens.some(keywordToken => keywordToken.startsWith(token) || (keywordToken.length >= MIN_STEM_LENGTH && token.startsWith(keywordToken)))) {
|
|
140
|
+
score += weight * 2;
|
|
141
|
+
matchedTokens.add(token);
|
|
142
|
+
} else if (phrases.some(phrase => phrase.includes(token))) {
|
|
143
|
+
score += weight;
|
|
144
|
+
matchedTokens.add(token);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
81
149
|
const coverage = matchedTokens.size / queryTokens.length;
|
|
82
150
|
if (!phraseMatched && (queryTokens.length <= 2 ? coverage !== 1 : coverage < 0.6)) return null;
|
|
83
151
|
|
|
@@ -88,13 +156,24 @@ export function scoreToolMatch(tool: ToolMetadata, server: string, query: string
|
|
|
88
156
|
return score;
|
|
89
157
|
}
|
|
90
158
|
|
|
91
|
-
export function rankToolMatches(
|
|
159
|
+
export function rankToolMatches(
|
|
160
|
+
state: McpExtensionState,
|
|
161
|
+
query: string,
|
|
162
|
+
server?: string,
|
|
163
|
+
includeKeywords = true,
|
|
164
|
+
): RankedToolMatch[] {
|
|
92
165
|
const matches: RankedToolMatch[] = [];
|
|
166
|
+
const globalPrefix = state.config.settings?.toolPrefix ?? "server";
|
|
93
167
|
for (const [serverName, metadata] of state.toolMetadata.entries()) {
|
|
94
168
|
if (server && serverName !== server) continue;
|
|
95
|
-
|
|
169
|
+
const definition = state.config.mcpServers[serverName];
|
|
170
|
+
if (isServerDisabled(definition)) continue;
|
|
171
|
+
const hasKeywords = includeKeywords && definition?.searchKeywords !== undefined;
|
|
96
172
|
for (const tool of metadata) {
|
|
97
|
-
const
|
|
173
|
+
const keywords = hasKeywords
|
|
174
|
+
? resolveSearchKeywords(definition, tool.originalName, serverName, globalPrefix)
|
|
175
|
+
: undefined;
|
|
176
|
+
const score = scoreToolMatch(tool, serverName, query, keywords);
|
|
98
177
|
if (score !== null) matches.push({ server: serverName, tool, score });
|
|
99
178
|
}
|
|
100
179
|
}
|
|
@@ -123,5 +202,5 @@ export function rankSuggestions(state: McpExtensionState, name: string, limit: n
|
|
|
123
202
|
.sort((a, b) => b.length - a.length)
|
|
124
203
|
.map(candidate => name.slice(candidate.length + 1));
|
|
125
204
|
const query = stripped[0] ?? name;
|
|
126
|
-
return rankToolMatches(state, query).slice(0, limit).map(match => match.tool.name);
|
|
205
|
+
return rankToolMatches(state, query, undefined, false).slice(0, limit).map(match => match.tool.name);
|
|
127
206
|
}
|
|
@@ -31,7 +31,7 @@ import { createJsonSchemaValidator } from "./json-schema-validator.ts";
|
|
|
31
31
|
import { logger } from "./logger.ts";
|
|
32
32
|
import { McpOAuthProvider } from "./mcp-oauth-provider.ts";
|
|
33
33
|
import { extractOAuthConfig, supportsOAuth, type McpOAuthRuntime } from "./mcp-auth-flow.ts";
|
|
34
|
-
import type
|
|
34
|
+
import { invalidateAuthEntryCache, type AuthStorageOptions } from "./mcp-auth.ts";
|
|
35
35
|
import { registerSamplingHandler, type ServerSamplingConfig } from "./sampling-handler.ts";
|
|
36
36
|
import {
|
|
37
37
|
handleUrlElicitation,
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
type ServerElicitationConfig,
|
|
40
40
|
} from "./elicitation-handler.ts";
|
|
41
41
|
import {
|
|
42
|
+
interpolateEnvVars,
|
|
42
43
|
resolveBearerToken,
|
|
43
44
|
resolveCommandSecret,
|
|
44
45
|
resolveCommandSecretsRecord,
|
|
@@ -55,6 +56,7 @@ import {
|
|
|
55
56
|
traceTransportKind,
|
|
56
57
|
wrapTransportWithMcpTrace,
|
|
57
58
|
} from "./mcp-trace.ts";
|
|
59
|
+
import { createRequestHeadersCommandFetch } from "./request-headers-command.ts";
|
|
58
60
|
|
|
59
61
|
const MAX_CAPTURED_STDERR_BYTES = 8 * 1024;
|
|
60
62
|
const MAX_CAPTURED_STDERR_LINES = 3;
|
|
@@ -129,8 +131,11 @@ export interface ServerConnection {
|
|
|
129
131
|
lastUsedAt: number;
|
|
130
132
|
inFlight: number;
|
|
131
133
|
status: "connected" | "closed" | "needs-auth";
|
|
134
|
+
/** True once this needs-auth episode discarded the cached credential. */
|
|
135
|
+
credentialsInvalidated?: boolean;
|
|
132
136
|
}
|
|
133
137
|
|
|
138
|
+
|
|
134
139
|
type UiStreamListener = (serverName: string, notification: ServerStreamResultPatchNotification["params"]) => void;
|
|
135
140
|
type MetadataListChangedListener = (serverName: string, reason: string) => void;
|
|
136
141
|
|
|
@@ -238,10 +243,12 @@ export class McpServerManager {
|
|
|
238
243
|
return existing;
|
|
239
244
|
}
|
|
240
245
|
|
|
246
|
+
const credentialsInvalidated = existing?.status === "needs-auth"
|
|
247
|
+
&& existing.credentialsInvalidated === true;
|
|
241
248
|
const generation = this.closeGenerations.get(name) ?? 0;
|
|
242
249
|
const attemptController = new AbortController();
|
|
243
250
|
const attemptSignal = combineAbortSignals(ownedSignal, attemptController.signal);
|
|
244
|
-
const connectionAttempt = this.createConnection(name, definition, attemptSignal, ownedSignal);
|
|
251
|
+
const connectionAttempt = this.createConnection(name, definition, attemptSignal, ownedSignal, credentialsInvalidated);
|
|
245
252
|
const promise = definition.url
|
|
246
253
|
? connectionAttempt.catch(async error => { throw await this.enrichHttpConnectionError(definition, error); })
|
|
247
254
|
: connectionAttempt;
|
|
@@ -324,6 +331,7 @@ export class McpServerManager {
|
|
|
324
331
|
definition: ServerDefinition,
|
|
325
332
|
signal?: AbortSignal,
|
|
326
333
|
requestSignal?: AbortSignal,
|
|
334
|
+
credentialsInvalidated = false,
|
|
327
335
|
): Promise<ServerConnection> {
|
|
328
336
|
throwIfAborted(signal);
|
|
329
337
|
|
|
@@ -338,6 +346,7 @@ export class McpServerManager {
|
|
|
338
346
|
let client: Client;
|
|
339
347
|
let transport: Transport;
|
|
340
348
|
let clientConnected = false;
|
|
349
|
+
let invalidated = credentialsInvalidated;
|
|
341
350
|
let transportAlreadyTraced = false;
|
|
342
351
|
let stderrTail: Buffer<ArrayBufferLike> = Buffer.alloc(0);
|
|
343
352
|
const configuredTransports = [definition.command, definition.url, definition.socket]
|
|
@@ -351,7 +360,7 @@ export class McpServerManager {
|
|
|
351
360
|
if (definition.command) {
|
|
352
361
|
client = this.createClient(name, definition);
|
|
353
362
|
let command = definition.command;
|
|
354
|
-
let args = definition.args ?? [];
|
|
363
|
+
let args = (definition.args ?? []).map(interpolateEnvVars);
|
|
355
364
|
|
|
356
365
|
if (command === "npx" || command === "npm") {
|
|
357
366
|
const resolved = await resolveNpxBinary(command, args, signal);
|
|
@@ -387,9 +396,11 @@ export class McpServerManager {
|
|
|
387
396
|
requestOptions,
|
|
388
397
|
signal,
|
|
389
398
|
traceObserver,
|
|
399
|
+
invalidated,
|
|
390
400
|
);
|
|
391
401
|
client = httpConnection.client;
|
|
392
402
|
transport = httpConnection.transport;
|
|
403
|
+
invalidated = httpConnection.credentialsInvalidated;
|
|
393
404
|
if (httpConnection.status === "needs-auth") {
|
|
394
405
|
return {
|
|
395
406
|
client,
|
|
@@ -401,6 +412,7 @@ export class McpServerManager {
|
|
|
401
412
|
lastUsedAt: Date.now(),
|
|
402
413
|
inFlight: 0,
|
|
403
414
|
status: "needs-auth",
|
|
415
|
+
credentialsInvalidated: invalidated,
|
|
404
416
|
};
|
|
405
417
|
}
|
|
406
418
|
clientConnected = true;
|
|
@@ -477,6 +489,10 @@ export class McpServerManager {
|
|
|
477
489
|
// A cleanup failure remains a setup failure rather than being hidden
|
|
478
490
|
// behind needs-auth.
|
|
479
491
|
if (isUnauthorizedHttpError(error) && supportsOAuth(definition) && cleanupFailures.length === 0) {
|
|
492
|
+
if (!invalidated) {
|
|
493
|
+
invalidateAuthEntryCache(name);
|
|
494
|
+
invalidated = true;
|
|
495
|
+
}
|
|
480
496
|
return {
|
|
481
497
|
client,
|
|
482
498
|
transport,
|
|
@@ -487,6 +503,7 @@ export class McpServerManager {
|
|
|
487
503
|
lastUsedAt: Date.now(),
|
|
488
504
|
inFlight: 0,
|
|
489
505
|
status: "needs-auth",
|
|
506
|
+
credentialsInvalidated: invalidated,
|
|
490
507
|
};
|
|
491
508
|
}
|
|
492
509
|
|
|
@@ -694,7 +711,8 @@ export class McpServerManager {
|
|
|
694
711
|
requestOptions: RequestOptions | undefined,
|
|
695
712
|
signal?: AbortSignal,
|
|
696
713
|
traceObserver?: McpTraceObserver,
|
|
697
|
-
|
|
714
|
+
credentialsInvalidated = false,
|
|
715
|
+
): Promise<{ client: Client; transport: Transport; status: "connected" | "needs-auth"; credentialsInvalidated: boolean }> {
|
|
698
716
|
throwIfAborted(signal);
|
|
699
717
|
const serverUrl = resolveServerUrl(definition)!;
|
|
700
718
|
const url = new URL(serverUrl);
|
|
@@ -729,6 +747,9 @@ export class McpServerManager {
|
|
|
729
747
|
}
|
|
730
748
|
|
|
731
749
|
const requestInit = Object.keys(headers).length > 0 ? { headers } : undefined;
|
|
750
|
+
const requestFetch = definition.requestHeadersCommand
|
|
751
|
+
? createRequestHeadersCommandFetch(definition.requestHeadersCommand)
|
|
752
|
+
: undefined;
|
|
732
753
|
const createAuthProvider = (): McpOAuthProvider => new McpOAuthProvider(
|
|
733
754
|
serverName,
|
|
734
755
|
serverUrl,
|
|
@@ -755,6 +776,7 @@ export class McpServerManager {
|
|
|
755
776
|
const authProvider = "provider" in authState ? authState.provider : undefined;
|
|
756
777
|
const transportOptions = {
|
|
757
778
|
...(requestInit !== undefined ? { requestInit } : {}),
|
|
779
|
+
...(requestFetch !== undefined ? { fetch: requestFetch } : {}),
|
|
758
780
|
...(authProvider !== undefined ? { authProvider } : {}),
|
|
759
781
|
...(authProvider !== undefined
|
|
760
782
|
&& definition.oauth !== false
|
|
@@ -791,9 +813,10 @@ export class McpServerManager {
|
|
|
791
813
|
// OAuth challenge; use SSE only for definitive endpoint incompatibility.
|
|
792
814
|
// Agent Plugins set httpTransport, and their declared transport is used without fallback.
|
|
793
815
|
let kind: "streamable-http" | "sse" = definition.httpTransport ?? "streamable-http";
|
|
816
|
+
let invalidated = credentialsInvalidated;
|
|
794
817
|
for (;;) {
|
|
795
818
|
const result = await attempt(kind);
|
|
796
|
-
if (result.status === "connected") return result;
|
|
819
|
+
if (result.status === "connected") return { ...result, credentialsInvalidated: invalidated };
|
|
797
820
|
if (result.error instanceof AggregateError
|
|
798
821
|
&& result.error.message === "MCP connection abort cleanup failed") {
|
|
799
822
|
throw result.error;
|
|
@@ -806,7 +829,16 @@ export class McpServerManager {
|
|
|
806
829
|
}
|
|
807
830
|
if (isUnauthorizedHttpError(result.error)) {
|
|
808
831
|
if (supportsOAuth(definition)) {
|
|
809
|
-
|
|
832
|
+
if (!invalidated) {
|
|
833
|
+
invalidateAuthEntryCache(serverName);
|
|
834
|
+
invalidated = true;
|
|
835
|
+
}
|
|
836
|
+
return {
|
|
837
|
+
client: result.client,
|
|
838
|
+
transport: result.transport,
|
|
839
|
+
status: "needs-auth",
|
|
840
|
+
credentialsInvalidated: invalidated,
|
|
841
|
+
};
|
|
810
842
|
}
|
|
811
843
|
throw result.error;
|
|
812
844
|
}
|
|
@@ -850,6 +882,7 @@ export class McpServerManager {
|
|
|
850
882
|
return { prompts, failed: false };
|
|
851
883
|
} catch (error) {
|
|
852
884
|
if (requestOptions?.signal?.aborted) throwIfAborted(requestOptions.signal);
|
|
885
|
+
if (isUnauthorizedHttpError(error)) throw error;
|
|
853
886
|
const message = error instanceof Error ? error.message : String(error);
|
|
854
887
|
logger.debug(`MCP: prompts/list failed: ${message}`);
|
|
855
888
|
return { prompts: [], failed: true };
|
|
@@ -871,10 +904,11 @@ export class McpServerManager {
|
|
|
871
904
|
} while (cursor);
|
|
872
905
|
|
|
873
906
|
return allResources;
|
|
874
|
-
} catch {
|
|
907
|
+
} catch (error) {
|
|
875
908
|
if (requestOptions?.signal?.aborted) {
|
|
876
909
|
throwIfAborted(requestOptions.signal);
|
|
877
910
|
}
|
|
911
|
+
if (isUnauthorizedHttpError(error)) throw error;
|
|
878
912
|
// The server advertises resources but the listing failed
|
|
879
913
|
return [];
|
|
880
914
|
}
|