@herbertgao/pi-extensions 2026.8.5 → 2026.8.7
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 +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +19 -7
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +30 -9
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +272 -22
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +94 -77
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +48 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/message-display.ts +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +14 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +55 -17
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +41 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +75 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +7 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +64 -1
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +16 -0
- package/node_modules/@herbertgao/pi-subagents/README.md +30 -22
- package/node_modules/@herbertgao/pi-subagents/examples/agent-tool-description.md +5 -5
- package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
- package/node_modules/@herbertgao/pi-subagents/src/agent-file-toggle.ts +7 -1
- package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +48 -19
- package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +22 -15
- package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +26 -2
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +219 -40
- package/node_modules/@herbertgao/pi-subagents/src/invocation-config.ts +101 -3
- package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +18 -4
- package/node_modules/@herbertgao/pi-subagents/src/settings.ts +99 -0
- package/node_modules/@herbertgao/pi-subagents/src/types.ts +22 -3
- package/node_modules/@herbertgao/pi-subagents/src/ui/agent-widget.ts +51 -3
- package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +20 -2
- package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +34 -10
- package/node_modules/@herbertgao/pi-subagents/src/usage.ts +129 -1
- package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +29 -5
- package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +3 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +46 -10
- package/node_modules/@juicesharp/rpiv-ask-user-question/config.ts +25 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/configuration.md +13 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +8 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/docs/localization.md +3 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/en.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/locales/zh.json +2 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +3 -2
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/build-questionnaire.ts +10 -0
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/key-router.ts +3 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/questionnaire-session.ts +30 -7
- package/node_modules/@juicesharp/rpiv-ask-user-question/state/state.ts +14 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/dialog-builder.ts +30 -4
- package/node_modules/@juicesharp/rpiv-ask-user-question/view/tab-content-strategy.ts +26 -5
- package/node_modules/@narumitw/pi-btw/README.md +119 -103
- package/node_modules/@narumitw/pi-btw/dist/index.ts +2898 -0
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +7 -0
- package/node_modules/@narumitw/pi-btw/package.json +13 -9
- package/node_modules/@narumitw/pi-btw/src/btw.ts +40 -42
- package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +55 -10
- package/node_modules/@narumitw/pi-btw/src/main-tree-picker.ts +390 -0
- package/node_modules/@narumitw/pi-btw/src/menu.ts +78 -15
- package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
- package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +136 -45
- package/node_modules/pi-lens/CHANGELOG.md +1004 -0
- package/node_modules/pi-lens/README.md +11 -3
- package/node_modules/pi-lens/config/ruff/core.toml +42 -0
- package/node_modules/pi-lens/dist/clients/actionable-warnings.js +130 -27
- package/node_modules/pi-lens/dist/clients/advisory-provenance.js +427 -0
- package/node_modules/pi-lens/dist/clients/agent-nudge.js +79 -16
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +9 -3
- package/node_modules/pi-lens/dist/clients/atomic-write.js +9 -3
- package/node_modules/pi-lens/dist/clients/bash-file-access.js +285 -8
- package/node_modules/pi-lens/dist/clients/biome-client.js +176 -37
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +428 -0
- package/node_modules/pi-lens/dist/clients/blocker-past-eof.js +119 -0
- package/node_modules/pi-lens/dist/clients/bootstrap.js +12 -1
- package/node_modules/pi-lens/dist/clients/bounded-pid-file-lock.js +12 -3
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.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 +11 -3
- package/node_modules/pi-lens/dist/clients/cascade-budget.js +196 -0
- package/node_modules/pi-lens/dist/clients/cascade-format.js +76 -2
- package/node_modules/pi-lens/dist/clients/cascade-types.js +9 -1
- package/node_modules/pi-lens/dist/clients/child-unref.js +54 -6
- package/node_modules/pi-lens/dist/clients/code-quality-warnings.js +76 -24
- package/node_modules/pi-lens/dist/clients/console-guard-install.js +16 -4
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +249 -52
- package/node_modules/pi-lens/dist/clients/deadline-utils.js +8 -1
- package/node_modules/pi-lens/dist/clients/debug-handles.js +5 -0
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +62 -13
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +43 -9
- package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +153 -37
- package/node_modules/pi-lens/dist/clients/diagnostic-line-freshness.js +298 -0
- package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +165 -11
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +325 -72
- package/node_modules/pi-lens/dist/clients/dispatch/lazy.js +7 -4
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +5 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +234 -18
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +187 -19
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +6 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +11 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +398 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +11 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +12 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +18 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +895 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +4 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +16 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +81 -41
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +16 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +23 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +331 -55
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +38 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +97 -19
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +14 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +20 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +22 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +10 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +18 -6
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +73 -12
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +488 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +84 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/lazy-installer.js +260 -27
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +943 -93
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +86 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +72 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +106 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +9 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +18 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +4 -2
- package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
- package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +28 -5
- package/node_modules/pi-lens/dist/clients/extension-log.js +317 -3
- package/node_modules/pi-lens/dist/clients/file-kinds.js +8 -2
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +294 -0
- package/node_modules/pi-lens/dist/clients/finding-identity.js +88 -0
- package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
- package/node_modules/pi-lens/dist/clients/format-events-publish.js +39 -8
- package/node_modules/pi-lens/dist/clients/formatters-lazy.js +3 -2
- package/node_modules/pi-lens/dist/clients/formatters.js +500 -115
- package/node_modules/pi-lens/dist/clients/generation-guard.js +216 -0
- package/node_modules/pi-lens/dist/clients/git-guard.js +49 -21
- package/node_modules/pi-lens/dist/clients/gitleaks-client.js +181 -8
- package/node_modules/pi-lens/dist/clients/go-client.js +21 -39
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +219 -10
- package/node_modules/pi-lens/dist/clients/grammar-source.js +333 -10
- package/node_modules/pi-lens/dist/clients/host-ports.js +9 -1
- package/node_modules/pi-lens/dist/clients/installer/index.js +1119 -101
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh-session.js +58 -0
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +715 -0
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +643 -97
- package/node_modules/pi-lens/dist/clients/instance-registry.js +87 -17
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +86 -15
- package/node_modules/pi-lens/dist/clients/knip-client.js +361 -24
- package/node_modules/pi-lens/dist/clients/language-policy.js +8 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +2 -0
- package/node_modules/pi-lens/dist/clients/latency-logger.js +487 -9
- package/node_modules/pi-lens/dist/clients/lazy-import.js +21 -0
- package/node_modules/pi-lens/dist/clients/ledger-bounds.js +21 -0
- package/node_modules/pi-lens/dist/clients/lens-events.js +56 -25
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +16 -0
- package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +45 -2
- package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +160 -20
- package/node_modules/pi-lens/dist/clients/lsp/client.js +1666 -148
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +111 -0
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +389 -0
- package/node_modules/pi-lens/dist/clients/lsp/index.js +2496 -171
- package/node_modules/pi-lens/dist/clients/lsp/inferred-project.js +215 -0
- package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +146 -14
- package/node_modules/pi-lens/dist/clients/lsp/language.js +48 -4
- package/node_modules/pi-lens/dist/clients/lsp/server.js +517 -69
- package/node_modules/pi-lens/dist/clients/lsp/spawn-history.js +22 -0
- package/node_modules/pi-lens/dist/clients/lsp/sync-kind.js +43 -0
- package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +172 -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 +61 -3
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +422 -28
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-session.js +30 -0
- package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +175 -0
- package/node_modules/pi-lens/dist/clients/lsp-lazy.js +3 -2
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +4 -0
- package/node_modules/pi-lens/dist/clients/mcp/session.js +39 -18
- package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
- package/node_modules/pi-lens/dist/clients/module-report.js +8 -0
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +15 -1
- package/node_modules/pi-lens/dist/clients/package-manager.js +170 -14
- package/node_modules/pi-lens/dist/clients/path-attribution-telemetry.js +27 -0
- package/node_modules/pi-lens/dist/clients/path-utils.js +160 -0
- package/node_modules/pi-lens/dist/clients/pipeline.js +178 -30
- package/node_modules/pi-lens/dist/clients/post-autofix-notice.js +37 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/cache.js +17 -3
- package/node_modules/pi-lens/dist/clients/project-diagnostics/extractors.js +89 -22
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +85 -15
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +15 -3
- 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/runner-adapters/trivy.js +50 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +5 -0
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +299 -0
- package/node_modules/pi-lens/dist/clients/quiet-window-config.js +47 -0
- package/node_modules/pi-lens/dist/clients/quiet-window.js +11 -25
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +1 -1
- package/node_modules/pi-lens/dist/clients/read-guard.js +184 -10
- package/node_modules/pi-lens/dist/clients/resource-sampler.js +49 -17
- package/node_modules/pi-lens/dist/clients/reverse-deps.js +50 -11
- package/node_modules/pi-lens/dist/clients/review-graph/query.js +76 -4
- package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +248 -13
- package/node_modules/pi-lens/dist/clients/runtime-context.js +112 -12
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +472 -61
- package/node_modules/pi-lens/dist/clients/runtime-session.js +359 -13
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +217 -22
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +269 -22
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +948 -87
- package/node_modules/pi-lens/dist/clients/rust-client.js +21 -37
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +220 -7
- package/node_modules/pi-lens/dist/clients/scratch-tree-policy.js +161 -0
- package/node_modules/pi-lens/dist/clients/search-read-registration.js +34 -6
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +224 -8
- package/node_modules/pi-lens/dist/clients/sg-runner.js +249 -37
- package/node_modules/pi-lens/dist/clients/single-flight.js +158 -0
- package/node_modules/pi-lens/dist/clients/smells-rollup.js +18 -11
- package/node_modules/pi-lens/dist/clients/stale-marker.js +17 -0
- package/node_modules/pi-lens/dist/clients/startup-timing.js +26 -2
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +574 -32
- package/node_modules/pi-lens/dist/clients/tool-event.js +56 -5
- package/node_modules/pi-lens/dist/clients/tool-policy.js +127 -20
- package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-cache.js +75 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +769 -64
- package/node_modules/pi-lens/dist/clients/tree-sitter-shared.js +11 -0
- package/node_modules/pi-lens/dist/clients/tree-sitter-symbol-extractor.js +90 -0
- package/node_modules/pi-lens/dist/clients/trivy-client.js +19 -1
- package/node_modules/pi-lens/dist/clients/warm-attach.js +17 -0
- package/node_modules/pi-lens/dist/clients/widget-state.js +336 -36
- package/node_modules/pi-lens/dist/clients/word-index.js +305 -33
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +214 -14
- package/node_modules/pi-lens/dist/index.js +64677 -49484
- package/node_modules/pi-lens/dist/mcp/server.js +8 -2
- 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/lens-diagnostic-mark.js +5 -2
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +469 -56
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +190 -46
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +46 -7
- package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
- package/node_modules/pi-lens/docs/agent-guide.md +44 -14
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +518 -501
- package/node_modules/pi-lens/docs/dispositions.md +21 -0
- package/node_modules/pi-lens/docs/features.md +33 -6
- package/node_modules/pi-lens/docs/globalconfig.md +14 -0
- package/node_modules/pi-lens/docs/language-coverage.md +71 -5
- package/node_modules/pi-lens/docs/mcp.md +2 -2
- package/node_modules/pi-lens/docs/servercapabilities.md +4 -1
- package/node_modules/pi-lens/docs/settings.md +25 -1
- package/node_modules/pi-lens/docs/usage.md +24 -5
- package/node_modules/pi-lens/docs/word-index.md +35 -0
- package/node_modules/pi-lens/package.json +10 -6
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-js-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/no-reflect-get-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/__snapshots__/ts-void-zero-snapshot.yml +23 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-bare-object-param-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +22 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-compile-call-test.yml +29 -1
- 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-dupe-class-members-test.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-js-test.yml +7 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-inner-html-test.yml +7 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-known-value-widening-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-non-null-assertion-test.yml +216 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-return-value-in-generator-test.yml +12 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-runtime-typeof-test.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-server-bind-wildcard-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-shape-in-symbol-names-test.yml +13 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-js-test.yml +5 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-sql-in-code-test.yml +5 -1
- 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-unknown-parameters-test.yml +12 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-returns-test.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-any-test.yml +10 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unsafe-dictionary-unknown-test.yml +8 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/redundant-unsafe-function-test.yml +35 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/require-safety-comment-for-as-unknown-as-test.yml +177 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ts-void-zero-test.yml +1 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member-js.yml +13 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-await-expression-member.yml +13 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-host-path-in-win32-branch.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-bare-object-param.yml +60 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +66 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-compile-call.yml +63 -5
- 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-dupe-class-members.yml +52 -8
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html-js.yml +27 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-inner-html.yml +27 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-known-value-widening.yml +71 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-non-null-assertion.yml +316 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-json-store-write.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +29 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +30 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +41 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +42 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-return-value-in-generator.yml +22 -5
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-runtime-typeof.yml +121 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-server-bind-wildcard.yml +23 -7
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-shape-in-symbol-names.yml +68 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code-js.yml +41 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-sql-in-code.yml +41 -2
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +32 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-parameters.yml +67 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-returns.yml +106 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-any.yml +50 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unsafe-dictionary-unknown.yml +56 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-win32-isabsolute-for-qualification.yml +2 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/redundant-unsafe-function.yml +32 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/require-safety-comment-for-as-unknown-as.yml +184 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/ts-void-zero.yml +5 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/self-scan-baseline.json +5 -0
- package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +132 -1
- package/node_modules/pi-lens/scripts/download-grammars.js +25 -0
- package/node_modules/pi-lens/scripts/grammars.lock.json +9 -0
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +12 -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 +87 -116
- package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +170 -0
- package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
- package/node_modules/pi-lens/vendor/grammars/tree-sitter-cue.wasm +0 -0
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +49 -0
- package/node_modules/pi-mcp-adapter/README.md +51 -6
- package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
- package/node_modules/pi-mcp-adapter/cli.js +188 -5
- package/node_modules/pi-mcp-adapter/commands.ts +128 -22
- package/node_modules/pi-mcp-adapter/config.ts +28 -10
- package/node_modules/pi-mcp-adapter/direct-tools.ts +23 -19
- package/node_modules/pi-mcp-adapter/index.ts +219 -32
- package/node_modules/pi-mcp-adapter/init.ts +36 -4
- package/node_modules/pi-mcp-adapter/lifecycle.ts +315 -24
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +39 -6
- package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +387 -0
- package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +27 -3
- package/node_modules/pi-mcp-adapter/mcp-code.ts +4 -0
- package/node_modules/pi-mcp-adapter/mcp-panel.ts +42 -13
- package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +46 -25
- package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +168 -0
- package/node_modules/pi-mcp-adapter/package.json +4 -1
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +5 -4
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +330 -0
- package/node_modules/pi-mcp-adapter/search-ranking.ts +92 -22
- package/node_modules/pi-mcp-adapter/server-manager.ts +145 -2
- package/node_modules/pi-mcp-adapter/session-recovery.ts +7 -0
- package/node_modules/pi-mcp-adapter/tool-approval.ts +15 -2
- package/node_modules/pi-mcp-adapter/tool-metadata.ts +38 -18
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +44 -4
- package/node_modules/pi-mcp-adapter/types.ts +95 -10
- package/node_modules/pi-mcp-adapter/ui-server.ts +18 -16
- package/node_modules/pi-mcp-adapter/utils.ts +70 -0
- package/node_modules/pi-web-access/CHANGELOG.md +37 -0
- package/node_modules/pi-web-access/README.md +38 -14
- package/node_modules/pi-web-access/anysearch.ts +4 -2
- package/node_modules/pi-web-access/auth-fetch.ts +148 -0
- package/node_modules/pi-web-access/brave.ts +16 -4
- package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
- package/node_modules/pi-web-access/curator-page.ts +8 -3
- package/node_modules/pi-web-access/curator-server.ts +5 -1
- package/node_modules/pi-web-access/exa.ts +18 -7
- package/node_modules/pi-web-access/extract.ts +134 -37
- 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 +33 -7
- package/node_modules/pi-web-access/index.ts +160 -104
- package/node_modules/pi-web-access/package.json +2 -2
- package/node_modules/pi-web-access/page-query.ts +4 -2
- package/node_modules/pi-web-access/parallel-mcp.ts +249 -0
- package/node_modules/pi-web-access/parallel.ts +2 -2
- package/node_modules/pi-web-access/query-rewrite.ts +51 -0
- package/node_modules/pi-web-access/serper.ts +211 -0
- package/node_modules/pi-web-access/summary-model-scope.ts +12 -4
- package/node_modules/pi-web-access/summary-review.ts +51 -23
- package/node_modules/pi-web-access/tavily.ts +16 -4
- package/node_modules/pi-web-access/utils.ts +87 -0
- package/node_modules/pi-web-access/valyu.ts +199 -0
- package/node_modules/pi-web-access/youtube-extract.ts +2 -2
- package/package.json +13 -13
|
@@ -103,9 +103,11 @@ function parseResponse(value: unknown): AnySearchResponse {
|
|
|
103
103
|
if (typeof title !== "string") throw invalidResponse(`expected data.results[${index}].title string`);
|
|
104
104
|
if (typeof url !== "string") throw invalidResponse(`expected data.results[${index}].url string`);
|
|
105
105
|
if (typeof snippet !== "string") throw invalidResponse(`expected data.results[${index}].snippet string`);
|
|
106
|
-
if (typeof content !== "string")
|
|
106
|
+
if (content !== undefined && content !== null && typeof content !== "string") {
|
|
107
|
+
throw invalidResponse(`expected data.results[${index}].content string`);
|
|
108
|
+
}
|
|
107
109
|
if (!url) throw invalidResponse(`expected data.results[${index}].url to be non-empty`);
|
|
108
|
-
results.push({ title, url, snippet, content });
|
|
110
|
+
results.push({ title, url, snippet, content: typeof content === "string" ? content : "" });
|
|
109
111
|
}
|
|
110
112
|
|
|
111
113
|
return { code: 0, data: { results, metadata: data.metadata as Record<string, unknown> } };
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { getWebSearchConfigPath } from "./utils.ts";
|
|
3
|
+
|
|
4
|
+
const WEB_SEARCH_CONFIG_PATH = getWebSearchConfigPath();
|
|
5
|
+
const AUTH_PROFILE_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
|
|
6
|
+
|
|
7
|
+
export type AuthFetchRequest = true | string;
|
|
8
|
+
export type AuthFetchCache = "session" | "off";
|
|
9
|
+
|
|
10
|
+
export interface AuthFetchProfile {
|
|
11
|
+
name: string;
|
|
12
|
+
hosts: string[];
|
|
13
|
+
chromeProfile?: string;
|
|
14
|
+
redirects: "same-origin";
|
|
15
|
+
cache: AuthFetchCache;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface AuthFetchConfigRoot {
|
|
19
|
+
authFetch?: unknown;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function resolveAuthFetchProfile(request: AuthFetchRequest): AuthFetchProfile {
|
|
23
|
+
const profiles = loadAuthFetchProfiles();
|
|
24
|
+
if (profiles.length === 0) {
|
|
25
|
+
throw new Error(`auth requires at least one authFetch profile in ${WEB_SEARCH_CONFIG_PATH}`);
|
|
26
|
+
}
|
|
27
|
+
if (request === true) {
|
|
28
|
+
if (profiles.length !== 1) {
|
|
29
|
+
throw new Error("auth: true requires exactly one authFetch profile; use a profile name instead");
|
|
30
|
+
}
|
|
31
|
+
return profiles[0];
|
|
32
|
+
}
|
|
33
|
+
const name = request.trim();
|
|
34
|
+
const profile = profiles.find(candidate => candidate.name === name);
|
|
35
|
+
if (!profile) throw new Error(`Unknown authFetch profile: ${name}`);
|
|
36
|
+
return profile;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function assertAuthFetchUrl(profile: AuthFetchProfile, rawUrl: string): URL {
|
|
40
|
+
let url: URL;
|
|
41
|
+
try {
|
|
42
|
+
url = new URL(rawUrl);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
45
|
+
throw new Error(`Authenticated fetch requires an HTTPS URL: ${message}`);
|
|
46
|
+
}
|
|
47
|
+
if (url.protocol !== "https:") throw new Error("Authenticated fetch requires an HTTPS URL");
|
|
48
|
+
const hostname = normalizeHostname(url.hostname);
|
|
49
|
+
if (!profile.hosts.some(host => hostMatches(hostname, host))) {
|
|
50
|
+
throw new Error(`URL host ${hostname} is not allowed by authFetch profile ${profile.name}`);
|
|
51
|
+
}
|
|
52
|
+
return url;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function authFetchRedirectGuard(profile: AuthFetchProfile, from: URL, to: URL): void {
|
|
56
|
+
if (profile.redirects === "same-origin" && to.origin !== from.origin) {
|
|
57
|
+
throw new Error(`Authenticated fetch refused cross-origin redirect: ${from.origin} -> ${to.origin}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function loadAuthFetchProfiles(): AuthFetchProfile[] {
|
|
62
|
+
if (!existsSync(WEB_SEARCH_CONFIG_PATH)) return [];
|
|
63
|
+
const raw = readFileSync(WEB_SEARCH_CONFIG_PATH, "utf-8");
|
|
64
|
+
let parsed: AuthFetchConfigRoot;
|
|
65
|
+
try {
|
|
66
|
+
const value: unknown = JSON.parse(raw);
|
|
67
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("expected a JSON object");
|
|
68
|
+
parsed = value as AuthFetchConfigRoot;
|
|
69
|
+
} catch (err) {
|
|
70
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
71
|
+
throw new Error(`Failed to parse ${WEB_SEARCH_CONFIG_PATH}: ${message}`);
|
|
72
|
+
}
|
|
73
|
+
if (parsed.authFetch === undefined || parsed.authFetch === null) return [];
|
|
74
|
+
if (typeof parsed.authFetch !== "object" || Array.isArray(parsed.authFetch)) {
|
|
75
|
+
throw new Error(`authFetch in ${WEB_SEARCH_CONFIG_PATH} must be an object`);
|
|
76
|
+
}
|
|
77
|
+
return Object.entries(parsed.authFetch as Record<string, unknown>).map(([name, value]) => parseProfile(name, value));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function parseProfile(name: string, value: unknown): AuthFetchProfile {
|
|
81
|
+
if (!AUTH_PROFILE_NAME_PATTERN.test(name)) {
|
|
82
|
+
throw new Error(`authFetch profile name ${JSON.stringify(name)} must start with a letter and contain only letters, numbers, underscores, or hyphens`);
|
|
83
|
+
}
|
|
84
|
+
if (Array.isArray(value)) {
|
|
85
|
+
return { name, hosts: parseHosts(value, `authFetch.${name}`), redirects: "same-origin", cache: "session" };
|
|
86
|
+
}
|
|
87
|
+
if (!value || typeof value !== "object") {
|
|
88
|
+
throw new Error(`authFetch.${name} in ${WEB_SEARCH_CONFIG_PATH} must be an array of hosts or an object`);
|
|
89
|
+
}
|
|
90
|
+
const config = value as Record<string, unknown>;
|
|
91
|
+
if (!Array.isArray(config.hosts)) {
|
|
92
|
+
throw new Error(`authFetch.${name}.hosts in ${WEB_SEARCH_CONFIG_PATH} must be a non-empty array of hostnames`);
|
|
93
|
+
}
|
|
94
|
+
const redirects = config.redirects ?? "same-origin";
|
|
95
|
+
if (redirects !== "same-origin") {
|
|
96
|
+
throw new Error(`authFetch.${name}.redirects in ${WEB_SEARCH_CONFIG_PATH} must be "same-origin"`);
|
|
97
|
+
}
|
|
98
|
+
const cache = config.cache ?? "session";
|
|
99
|
+
if (cache !== "session" && cache !== "off") {
|
|
100
|
+
throw new Error(`authFetch.${name}.cache in ${WEB_SEARCH_CONFIG_PATH} must be "session" or "off"`);
|
|
101
|
+
}
|
|
102
|
+
const chromeProfile = parseChromeProfile(config.chromeProfile, `authFetch.${name}.chromeProfile`);
|
|
103
|
+
return {
|
|
104
|
+
name,
|
|
105
|
+
hosts: parseHosts(config.hosts, `authFetch.${name}.hosts`),
|
|
106
|
+
...(chromeProfile ? { chromeProfile } : {}),
|
|
107
|
+
redirects,
|
|
108
|
+
cache,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function parseHosts(value: unknown[], label: string): string[] {
|
|
113
|
+
if (value.length === 0) throw new Error(`${label} in ${WEB_SEARCH_CONFIG_PATH} must be a non-empty array of hostnames`);
|
|
114
|
+
const hosts = value.map((entry) => {
|
|
115
|
+
if (typeof entry !== "string") throw new Error(`${label} in ${WEB_SEARCH_CONFIG_PATH} must contain only hostnames`);
|
|
116
|
+
return parseHost(entry, label);
|
|
117
|
+
});
|
|
118
|
+
return [...new Set(hosts)];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function parseHost(value: string, label: string): string {
|
|
122
|
+
const host = normalizeHostname(value.trim());
|
|
123
|
+
if (!host || host.startsWith(".") || host.endsWith(".") || /\s|[\\/?:#@*]/.test(host)) {
|
|
124
|
+
throw new Error(`${label} in ${WEB_SEARCH_CONFIG_PATH} contains an invalid hostname: ${JSON.stringify(value)}`);
|
|
125
|
+
}
|
|
126
|
+
if (host.length > 253 || !/^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i.test(host)) {
|
|
127
|
+
throw new Error(`${label} in ${WEB_SEARCH_CONFIG_PATH} contains an invalid hostname: ${JSON.stringify(value)}`);
|
|
128
|
+
}
|
|
129
|
+
return host;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function parseChromeProfile(value: unknown, label: string): string | undefined {
|
|
133
|
+
if (value === undefined || value === null) return undefined;
|
|
134
|
+
if (typeof value !== "string") throw new Error(`${label} in ${WEB_SEARCH_CONFIG_PATH} must be a string`);
|
|
135
|
+
const normalized = value.trim();
|
|
136
|
+
if (!normalized || normalized === "." || normalized === ".." || normalized.includes("/") || normalized.includes("\\")) {
|
|
137
|
+
throw new Error(`${label} in ${WEB_SEARCH_CONFIG_PATH} must be a profile directory name, not a path`);
|
|
138
|
+
}
|
|
139
|
+
return normalized;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function normalizeHostname(hostname: string): string {
|
|
143
|
+
return hostname.toLowerCase().replace(/^\[|\]$/g, "").replace(/\.$/, "");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function hostMatches(hostname: string, allowedHost: string): boolean {
|
|
147
|
+
return hostname === allowedHost || hostname.endsWith(`.${allowedHost}`);
|
|
148
|
+
}
|
|
@@ -2,14 +2,15 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import { activityMonitor } from "./activity.ts";
|
|
3
3
|
import type { SearchOptions, SearchResult, SearchResponse } from "./perplexity.ts";
|
|
4
4
|
import { hasCredentialSource, redactCredential, resolveCredential } from "./credential-source.ts";
|
|
5
|
-
import { getWebSearchConfigPath } from "./utils.ts";
|
|
5
|
+
import { fetchWithCredentialRedirects, getWebSearchConfigPath, resolveApiBaseUrl } from "./utils.ts";
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const BRAVE_API_BASE_URL = "https://api.search.brave.com/res/v1";
|
|
8
8
|
const CONFIG_PATH = getWebSearchConfigPath();
|
|
9
9
|
const SEARCH_TIMEOUT_MS = 30_000;
|
|
10
10
|
|
|
11
11
|
interface WebSearchConfig {
|
|
12
12
|
braveApiKey?: unknown;
|
|
13
|
+
braveBaseUrl?: unknown;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
interface NormalizedDomainFilters {
|
|
@@ -45,6 +46,16 @@ async function getApiKey(signal?: AbortSignal): Promise<string | null> {
|
|
|
45
46
|
});
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
function getApiUrl(): string {
|
|
50
|
+
return `${resolveApiBaseUrl({
|
|
51
|
+
configKey: "braveBaseUrl",
|
|
52
|
+
configuredValue: loadConfig().braveBaseUrl,
|
|
53
|
+
defaultValue: BRAVE_API_BASE_URL,
|
|
54
|
+
environmentKey: "BRAVE_BASE_URL",
|
|
55
|
+
environmentValue: process.env.BRAVE_BASE_URL,
|
|
56
|
+
})}/web/search`;
|
|
57
|
+
}
|
|
58
|
+
|
|
48
59
|
function normalizeCount(value: number | undefined): number {
|
|
49
60
|
if (typeof value !== "number" || !Number.isFinite(value)) return 5;
|
|
50
61
|
return Math.max(1, Math.min(Math.floor(value), 20));
|
|
@@ -129,6 +140,7 @@ export async function searchWithBrave(
|
|
|
129
140
|
query: string,
|
|
130
141
|
options: SearchOptions = {},
|
|
131
142
|
): Promise<SearchResponse> {
|
|
143
|
+
const apiUrl = getApiUrl();
|
|
132
144
|
const apiKey = await getApiKey(options.signal);
|
|
133
145
|
if (!apiKey) {
|
|
134
146
|
throw new Error(
|
|
@@ -160,7 +172,7 @@ export async function searchWithBrave(
|
|
|
160
172
|
}
|
|
161
173
|
|
|
162
174
|
try {
|
|
163
|
-
const response = await
|
|
175
|
+
const response = await fetchWithCredentialRedirects(`${apiUrl}?${params.toString()}`, {
|
|
164
176
|
method: "GET",
|
|
165
177
|
headers: {
|
|
166
178
|
"X-Subscription-Token": apiKey,
|
|
@@ -170,7 +182,7 @@ export async function searchWithBrave(
|
|
|
170
182
|
signal: options.signal
|
|
171
183
|
? AbortSignal.any([AbortSignal.timeout(SEARCH_TIMEOUT_MS), options.signal])
|
|
172
184
|
: AbortSignal.timeout(SEARCH_TIMEOUT_MS),
|
|
173
|
-
});
|
|
185
|
+
}, ["X-Subscription-Token"]);
|
|
174
186
|
|
|
175
187
|
if (!response.ok) {
|
|
176
188
|
activityMonitor.logError(activityId, `HTTP ${response.status}`);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
2
|
import { pbkdf2Sync, createDecipheriv } from "node:crypto";
|
|
3
3
|
import { copyFileSync, existsSync, mkdtempSync, readdirSync, realpathSync, rmSync } from "node:fs";
|
|
4
|
-
import { tmpdir, homedir
|
|
4
|
+
import { tmpdir, homedir } from "node:os";
|
|
5
5
|
import { isAbsolute, join, sep } from "node:path";
|
|
6
6
|
import { isBrowserCookieAccessAllowed } from "./gemini-web-config.ts";
|
|
7
7
|
|
|
@@ -18,6 +18,12 @@ interface BrowserConfig {
|
|
|
18
18
|
type SqliteRow = Record<string, unknown>;
|
|
19
19
|
type SqliteFailure = "unavailable" | "query";
|
|
20
20
|
|
|
21
|
+
interface BrowserCookieEntry {
|
|
22
|
+
name: string;
|
|
23
|
+
value: string;
|
|
24
|
+
path: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
21
27
|
const GOOGLE_ORIGINS = [
|
|
22
28
|
"https://gemini.google.com",
|
|
23
29
|
"https://accounts.google.com",
|
|
@@ -33,6 +39,7 @@ const ALL_COOKIE_NAMES = new Set([
|
|
|
33
39
|
const MACOS_BROWSER_CONFIGS: BrowserConfig[] = [
|
|
34
40
|
{ name: "Helium", baseDir: "Library/Application Support/net.imput.helium", keychainService: "Helium Storage Key", keychainAccount: "Helium" },
|
|
35
41
|
{ name: "Chrome", baseDir: "Library/Application Support/Google/Chrome", keychainService: "Chrome Safe Storage", keychainAccount: "Chrome" },
|
|
42
|
+
{ name: "Brave", baseDir: "Library/Application Support/BraveSoftware/Brave-Browser", keychainService: "Brave Safe Storage", keychainAccount: "Brave" },
|
|
36
43
|
{ name: "Arc", baseDir: "Library/Application Support/Arc/User Data", keychainService: "Arc Safe Storage", keychainAccount: "Arc" },
|
|
37
44
|
];
|
|
38
45
|
|
|
@@ -50,16 +57,32 @@ export function getLastGoogleCookieDiagnostic(): string | null {
|
|
|
50
57
|
return lastCookieDiagnostic;
|
|
51
58
|
}
|
|
52
59
|
|
|
60
|
+
export function getLastBrowserCookieDiagnostic(): string | null {
|
|
61
|
+
return lastCookieDiagnostic;
|
|
62
|
+
}
|
|
63
|
+
|
|
53
64
|
export async function getGoogleCookies(
|
|
54
65
|
options?: { profile?: string; requiredCookies?: string[] },
|
|
55
66
|
): Promise<{ cookies: CookieMap; warnings: string[] } | null> {
|
|
67
|
+
return getBrowserCookiesForHosts({
|
|
68
|
+
hosts: GOOGLE_ORIGINS.map((origin) => new URL(origin).hostname),
|
|
69
|
+
profile: options?.profile,
|
|
70
|
+
requiredCookies: options?.requiredCookies,
|
|
71
|
+
cookieNames: ALL_COOKIE_NAMES,
|
|
72
|
+
requiredLabel: "Gemini",
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function getBrowserCookiesForHosts(
|
|
77
|
+
options: { hosts: string[]; profile?: string; requiredCookies?: string[]; cookieNames?: Iterable<string>; requiredLabel?: string; requestUrl?: URL },
|
|
78
|
+
): Promise<{ cookies: CookieMap; warnings: string[]; cookieHeader?: string } | null> {
|
|
56
79
|
lastCookieDiagnostic = null;
|
|
57
80
|
if (!isBrowserCookieAccessAllowed()) {
|
|
58
|
-
lastCookieDiagnostic = "Browser cookie access is disabled; enable allowBrowserCookies to use
|
|
81
|
+
lastCookieDiagnostic = "Browser cookie access is disabled; enable allowBrowserCookies to use browser cookies.";
|
|
59
82
|
return null;
|
|
60
83
|
}
|
|
61
84
|
|
|
62
|
-
const currentPlatform = platform
|
|
85
|
+
const currentPlatform = process.platform;
|
|
63
86
|
const configs = currentPlatform === "darwin" ? MACOS_BROWSER_CONFIGS : currentPlatform === "linux" ? LINUX_BROWSER_CONFIGS : [];
|
|
64
87
|
if (configs.length === 0) {
|
|
65
88
|
lastCookieDiagnostic = "Chromium cookie extraction is unsupported on this platform.";
|
|
@@ -67,17 +90,23 @@ export async function getGoogleCookies(
|
|
|
67
90
|
}
|
|
68
91
|
|
|
69
92
|
const warningSet = new Set<string>();
|
|
70
|
-
const rawProfile = typeof options
|
|
71
|
-
const requestedProfile = normalizeProfileName(options
|
|
93
|
+
const rawProfile = typeof options.profile === "string" ? options.profile.trim() : "";
|
|
94
|
+
const requestedProfile = normalizeProfileName(options.profile);
|
|
72
95
|
if (rawProfile && !requestedProfile) {
|
|
73
96
|
lastCookieDiagnostic = "Configured Chromium profile must be a profile directory name, not a path.";
|
|
74
97
|
return null;
|
|
75
98
|
}
|
|
76
|
-
const requiredCookies = normalizeCookieNames(options
|
|
77
|
-
const
|
|
99
|
+
const requiredCookies = normalizeCookieNames(options.requiredCookies);
|
|
100
|
+
const cookieNames = normalizeCookieNames(options.cookieNames ? [...options.cookieNames] : undefined);
|
|
101
|
+
const hosts = normalizeHosts(options.hosts);
|
|
102
|
+
if (hosts.length === 0) {
|
|
103
|
+
lastCookieDiagnostic = "No valid cookie hosts were requested.";
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
78
106
|
const home = homedir();
|
|
79
107
|
let sawCookieDatabase = false;
|
|
80
108
|
let sawRequiredCookies = false;
|
|
109
|
+
let sawAnyHostCookie = false;
|
|
81
110
|
let sawBackendFailure: SqliteFailure | undefined;
|
|
82
111
|
let sawUnsafeProfilePath = false;
|
|
83
112
|
|
|
@@ -117,25 +146,36 @@ export async function getGoogleCookies(
|
|
|
117
146
|
const metaVersion = await readMetaVersion(tempDb);
|
|
118
147
|
if (metaVersion.failure) sawBackendFailure = metaVersion.failure;
|
|
119
148
|
if (metaVersion.value === null) continue;
|
|
120
|
-
const rowsResult = await queryCookieRows(tempDb, hosts,
|
|
149
|
+
const rowsResult = await queryCookieRows(tempDb, hosts, cookieNames ?? null, Boolean(options.requestUrl));
|
|
121
150
|
if (rowsResult.status === "failure") {
|
|
122
151
|
sawBackendFailure = rowsResult.failure;
|
|
123
152
|
continue;
|
|
124
153
|
}
|
|
125
154
|
|
|
155
|
+
const entries: BrowserCookieEntry[] = [];
|
|
126
156
|
const cookies: CookieMap = {};
|
|
127
157
|
for (const row of rowsResult.rows) {
|
|
128
158
|
const name = typeof row.name === "string" ? row.name : "";
|
|
129
|
-
if (!
|
|
159
|
+
if (!name) continue;
|
|
130
160
|
let value = typeof row.value === "string" && row.value.length > 0 ? row.value : null;
|
|
131
161
|
if (!value && typeof row.encrypted_value_hex === "string" && /^[0-9a-f]*$/i.test(row.encrypted_value_hex)) {
|
|
132
162
|
value = decryptCookieValue(Buffer.from(row.encrypted_value_hex, "hex"), key, metaVersion.value >= 24);
|
|
133
163
|
}
|
|
134
|
-
if (value)
|
|
164
|
+
if (!value) continue;
|
|
165
|
+
const path = typeof row.path === "string" && row.path.startsWith("/") ? row.path : "/";
|
|
166
|
+
if (options.requestUrl && !pathMatches(options.requestUrl.pathname || "/", path)) continue;
|
|
167
|
+
entries.push({ name, value, path });
|
|
168
|
+
if (!cookies[name]) cookies[name] = value;
|
|
135
169
|
}
|
|
136
170
|
|
|
171
|
+
if (entries.length > 0) sawAnyHostCookie = true;
|
|
137
172
|
if (requiredCookies?.length && !requiredCookies.every((name) => Boolean(cookies[name]))) continue;
|
|
138
|
-
|
|
173
|
+
if (entries.length === 0) continue;
|
|
174
|
+
return {
|
|
175
|
+
cookies,
|
|
176
|
+
warnings: [...warningSet],
|
|
177
|
+
...(options.requestUrl ? { cookieHeader: buildCookieHeader(entries) } : {}),
|
|
178
|
+
};
|
|
139
179
|
} finally {
|
|
140
180
|
rmSync(tempDir, { recursive: true, force: true });
|
|
141
181
|
}
|
|
@@ -153,7 +193,11 @@ export async function getGoogleCookies(
|
|
|
153
193
|
? `Chromium profile '${requestedProfile}' does not contain a cookie database.`
|
|
154
194
|
: "No detected Chromium profile contains a cookie database.";
|
|
155
195
|
} else if (requiredCookies?.length && !sawRequiredCookies) {
|
|
156
|
-
lastCookieDiagnostic =
|
|
196
|
+
lastCookieDiagnostic = `No detected Chromium profile contains the required ${options.requiredLabel ?? "browser"} cookies.`;
|
|
197
|
+
} else if (!sawAnyHostCookie) {
|
|
198
|
+
lastCookieDiagnostic = options.requestUrl
|
|
199
|
+
? "No detected Chromium profile contains cookies for the requested URL."
|
|
200
|
+
: "No detected Chromium profile contains cookies for the requested host.";
|
|
157
201
|
} else if (warningSet.size > 0) {
|
|
158
202
|
lastCookieDiagnostic = [...warningSet][0];
|
|
159
203
|
} else {
|
|
@@ -193,6 +237,10 @@ function normalizeCookieNames(names: string[] | undefined): string[] | undefined
|
|
|
193
237
|
return normalized.length > 0 ? [...new Set(normalized)] : undefined;
|
|
194
238
|
}
|
|
195
239
|
|
|
240
|
+
function normalizeHosts(hosts: string[]): string[] {
|
|
241
|
+
return [...new Set(hosts.map(host => host.trim().toLowerCase().replace(/^\[|\]$/g, "").replace(/\.$/, "")).filter(Boolean))];
|
|
242
|
+
}
|
|
243
|
+
|
|
196
244
|
function listBrowserProfiles(home: string, config: BrowserConfig): string[] {
|
|
197
245
|
const basePath = join(home, config.baseDir);
|
|
198
246
|
if (!existsSync(basePath)) return ["Default"];
|
|
@@ -246,7 +294,7 @@ function removePkcs7Padding(buf: Buffer): Buffer {
|
|
|
246
294
|
return !padding || padding > 16 ? buf : buf.subarray(0, buf.length - padding);
|
|
247
295
|
}
|
|
248
296
|
|
|
249
|
-
function readBrowserPassword(config: BrowserConfig, currentPlatform:
|
|
297
|
+
function readBrowserPassword(config: BrowserConfig, currentPlatform: typeof process.platform): Promise<string | null> {
|
|
250
298
|
const cacheKey = `${currentPlatform}:${config.name}`;
|
|
251
299
|
const cached = browserPasswordCache.get(cacheKey);
|
|
252
300
|
if (cached) return cached;
|
|
@@ -386,19 +434,58 @@ async function hasCookieNames(dbPath: string, hosts: string[], names: string[]):
|
|
|
386
434
|
return { present: names.every((name) => present.has(name)) };
|
|
387
435
|
}
|
|
388
436
|
|
|
389
|
-
async function queryCookieRows(dbPath: string, hosts: string[], names: Iterable<string>): Promise<QueryResult> {
|
|
390
|
-
|
|
437
|
+
async function queryCookieRows(dbPath: string, hosts: string[], names: Iterable<string> | null, filterExpired: boolean): Promise<QueryResult> {
|
|
438
|
+
const columns = await readCookieColumns(dbPath);
|
|
439
|
+
if (columns.status === "failure") return columns;
|
|
440
|
+
const pathExpr = columns.columns.has("path") ? "path" : "'/' AS path";
|
|
441
|
+
const expiresExpr = columns.columns.has("expires_utc") ? "expires_utc" : "0 AS expires_utc";
|
|
442
|
+
const expiryFilter = filterExpired && columns.columns.has("expires_utc") ? ` AND (expires_utc = 0 OR expires_utc > ${chromeExpiryNowMicros()})` : "";
|
|
443
|
+
const partitionFilter = filterExpired ? unpartitionedCookieFilter(columns.columns) : "";
|
|
444
|
+
return runSqliteQuery(dbPath, `SELECT name, value, host_key, ${pathExpr}, ${expiresExpr}, hex(encrypted_value) AS encrypted_value_hex FROM cookies WHERE ${buildCookieWhere(hosts, names ?? undefined)}${expiryFilter}${partitionFilter} ORDER BY length(path) DESC, expires_utc ASC`);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
async function readCookieColumns(dbPath: string): Promise<{ status: "success"; columns: Set<string> } | { status: "failure"; failure: SqliteFailure }> {
|
|
448
|
+
const result = await runSqliteQuery(dbPath, "PRAGMA table_info(cookies)");
|
|
449
|
+
if (result.status === "failure") return result;
|
|
450
|
+
return { status: "success", columns: new Set(result.rows.map(row => typeof row.name === "string" ? row.name : "")) };
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function chromeExpiryNowMicros(): number {
|
|
454
|
+
return (Date.now() + 11644473600000) * 1000;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function unpartitionedCookieFilter(columns: Set<string>): string {
|
|
458
|
+
const clauses: string[] = [];
|
|
459
|
+
if (columns.has("top_frame_site_key")) clauses.push("(top_frame_site_key IS NULL OR top_frame_site_key = '')");
|
|
460
|
+
if (columns.has("partition_key")) clauses.push("(partition_key IS NULL OR partition_key = '')");
|
|
461
|
+
if (columns.has("is_partitioned")) clauses.push("(is_partitioned IS NULL OR is_partitioned = 0)");
|
|
462
|
+
return clauses.length > 0 ? ` AND ${clauses.join(" AND ")}` : "";
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function buildCookieHeader(entries: BrowserCookieEntry[]): string {
|
|
466
|
+
return entries
|
|
467
|
+
.sort((a, b) => b.path.length - a.path.length)
|
|
468
|
+
.map(({ name, value }) => `${name}=${value}`)
|
|
469
|
+
.join("; ");
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function pathMatches(requestPath: string, cookiePath: string): boolean {
|
|
473
|
+
if (requestPath === cookiePath) return true;
|
|
474
|
+
if (!requestPath.startsWith(cookiePath)) return false;
|
|
475
|
+
if (cookiePath.endsWith("/")) return true;
|
|
476
|
+
return requestPath[cookiePath.length] === "/";
|
|
391
477
|
}
|
|
392
478
|
|
|
393
479
|
function buildCookieWhere(hosts: string[], cookieNames?: Iterable<string>): string {
|
|
394
480
|
const hostClauses: string[] = [];
|
|
395
481
|
for (const host of hosts) {
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
482
|
+
const escapedHost = escapeSqlString(host);
|
|
483
|
+
hostClauses.push(`host_key = '${escapedHost}'`);
|
|
484
|
+
for (const candidate of domainCookieHosts(host)) {
|
|
485
|
+
hostClauses.push(`host_key = '.${escapeSqlString(candidate)}'`);
|
|
399
486
|
}
|
|
400
487
|
}
|
|
401
|
-
let where = `(${hostClauses.join(" OR ")})`;
|
|
488
|
+
let where = `(${[...new Set(hostClauses)].join(" OR ")})`;
|
|
402
489
|
const names = cookieNames ? [...cookieNames].filter(Boolean) : [];
|
|
403
490
|
if (names.length) where += ` AND name IN (${names.map((name) => `'${escapeSqlString(name)}'`).join(", ")})`;
|
|
404
491
|
return where;
|
|
@@ -408,11 +495,11 @@ function escapeSqlString(value: string): string {
|
|
|
408
495
|
return value.replaceAll("'", "''");
|
|
409
496
|
}
|
|
410
497
|
|
|
411
|
-
function
|
|
498
|
+
function domainCookieHosts(host: string): string[] {
|
|
412
499
|
const parts = host.split(".").filter(Boolean);
|
|
413
|
-
if (parts.length <= 1) return [
|
|
414
|
-
const candidates = new Set(
|
|
415
|
-
for (let i =
|
|
500
|
+
if (parts.length <= 1) return [];
|
|
501
|
+
const candidates = new Set<string>();
|
|
502
|
+
for (let i = 0; i <= parts.length - 2; i++) candidates.add(parts.slice(i).join("."));
|
|
416
503
|
return [...candidates];
|
|
417
504
|
}
|
|
418
505
|
|
|
@@ -8,7 +8,7 @@ function safeInlineJSON(data: unknown): string {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
function buildProviderButtons(
|
|
11
|
-
available: { all: boolean; openai: boolean; brave: boolean; parallel: boolean; tinyfish: boolean; search1api: boolean; searchinfinity: boolean; querit: boolean; tavily: boolean; jina: boolean; serpdive: boolean; kagi: boolean; bocha: boolean; ollama: boolean; searxng: boolean; duckduckgo: boolean; perplexity: boolean; exa: boolean; gemini: boolean; anysearch: boolean; xai: boolean; brightdata: boolean; serpbase: boolean },
|
|
11
|
+
available: { all: boolean; openai: boolean; brave: boolean; parallel: boolean; "parallel-mcp": boolean; tinyfish: boolean; search1api: boolean; searchinfinity: boolean; querit: boolean; tavily: boolean; firecrawl: boolean; jina: boolean; serpdive: boolean; kagi: boolean; bocha: boolean; ollama: boolean; searxng: boolean; duckduckgo: boolean; perplexity: boolean; exa: boolean; gemini: boolean; anysearch: boolean; xai: boolean; brightdata: boolean; serpbase: boolean; serper: boolean; valyu: boolean },
|
|
12
12
|
selected: string,
|
|
13
13
|
hasInitialQueries: boolean,
|
|
14
14
|
): string {
|
|
@@ -18,11 +18,13 @@ function buildProviderButtons(
|
|
|
18
18
|
{ value: "exa", label: "Exa", available: available.exa },
|
|
19
19
|
{ value: "brave", label: "Brave", available: available.brave },
|
|
20
20
|
{ value: "parallel", label: "Parallel", available: available.parallel },
|
|
21
|
+
{ value: "parallel-mcp", label: "Parallel MCP", available: available["parallel-mcp"] },
|
|
21
22
|
{ value: "tinyfish", label: "TinyFish", available: available.tinyfish },
|
|
22
23
|
{ value: "search1api", label: "Search1API", available: available.search1api },
|
|
23
24
|
{ value: "searchinfinity", label: "Searchinfinity", available: available.searchinfinity },
|
|
24
25
|
{ value: "querit", label: "Querit", available: available.querit },
|
|
25
26
|
{ value: "tavily", label: "Tavily", available: available.tavily },
|
|
27
|
+
{ value: "firecrawl", label: "Firecrawl", available: available.firecrawl },
|
|
26
28
|
{ value: "jina", label: "Jina", available: available.jina },
|
|
27
29
|
{ value: "serpdive", label: "SERPdive", available: available.serpdive },
|
|
28
30
|
{ value: "kagi", label: "Kagi", available: available.kagi },
|
|
@@ -36,6 +38,8 @@ function buildProviderButtons(
|
|
|
36
38
|
{ value: "xai", label: "xAI", available: available.xai },
|
|
37
39
|
{ value: "brightdata", label: "Bright Data", available: available.brightdata },
|
|
38
40
|
{ value: "serpbase", label: "SerpBase", available: available.serpbase },
|
|
41
|
+
{ value: "serper", label: "Serper", available: available.serper },
|
|
42
|
+
{ value: "valyu", label: "Valyu", available: available.valyu },
|
|
39
43
|
];
|
|
40
44
|
|
|
41
45
|
return providers
|
|
@@ -54,7 +58,7 @@ export function generateCuratorPage(
|
|
|
54
58
|
queries: string[],
|
|
55
59
|
sessionToken: string,
|
|
56
60
|
timeout: number,
|
|
57
|
-
availableProviders: { all: boolean; openai: boolean; brave: boolean; parallel: boolean; tinyfish: boolean; search1api: boolean; searchinfinity: boolean; querit: boolean; tavily: boolean; jina: boolean; serpdive: boolean; kagi: boolean; bocha: boolean; ollama: boolean; searxng: boolean; duckduckgo: boolean; perplexity: boolean; exa: boolean; gemini: boolean; anysearch: boolean; xai: boolean; brightdata: boolean; serpbase: boolean },
|
|
61
|
+
availableProviders: { all: boolean; openai: boolean; brave: boolean; parallel: boolean; "parallel-mcp": boolean; tinyfish: boolean; search1api: boolean; searchinfinity: boolean; querit: boolean; tavily: boolean; firecrawl: boolean; jina: boolean; serpdive: boolean; kagi: boolean; bocha: boolean; ollama: boolean; searxng: boolean; duckduckgo: boolean; perplexity: boolean; exa: boolean; gemini: boolean; anysearch: boolean; xai: boolean; brightdata: boolean; serpbase: boolean; serper: boolean; valyu: boolean },
|
|
58
62
|
defaultProvider: string,
|
|
59
63
|
searchProvider: string,
|
|
60
64
|
summaryModels: Array<{ value: string; label: string }>,
|
|
@@ -1455,7 +1459,7 @@ const SCRIPT = `(function() {
|
|
|
1455
1459
|
var token = DATA.sessionToken;
|
|
1456
1460
|
var timeoutSec = DATA.timeout;
|
|
1457
1461
|
var queries = Array.isArray(DATA.queries) ? DATA.queries : [];
|
|
1458
|
-
var providers = ["all", "openai", "exa", "brave", "parallel", "tinyfish", "search1api", "searchinfinity", "querit", "tavily", "jina", "serpdive", "kagi", "bocha", "ollama", "searxng", "duckduckgo", "perplexity", "gemini", "anysearch", "xai", "brightdata", "serpbase"];
|
|
1462
|
+
var providers = ["all", "openai", "exa", "brave", "parallel", "parallel-mcp", "tinyfish", "search1api", "searchinfinity", "querit", "tavily", "firecrawl", "jina", "serpdive", "kagi", "bocha", "ollama", "searxng", "duckduckgo", "perplexity", "gemini", "anysearch", "xai", "brightdata", "serpbase", "serper", "valyu"];
|
|
1459
1463
|
var availProviders = DATA.availableProviders && typeof DATA.availableProviders === "object" ? DATA.availableProviders : {};
|
|
1460
1464
|
var workflow = "summary-review";
|
|
1461
1465
|
var initialDefaultProvider = typeof DATA.defaultProvider === "string" ? DATA.defaultProvider : "exa";
|
|
@@ -1667,6 +1671,7 @@ const SCRIPT = `(function() {
|
|
|
1667
1671
|
if (provider === "searchinfinity") return "Searchinfinity";
|
|
1668
1672
|
if (provider === "querit") return "Querit";
|
|
1669
1673
|
if (provider === "tavily") return "Tavily";
|
|
1674
|
+
if (provider === "firecrawl") return "Firecrawl";
|
|
1670
1675
|
if (provider === "jina") return "Jina";
|
|
1671
1676
|
if (provider === "serpdive") return "SERPdive";
|
|
1672
1677
|
if (provider === "kagi") return "Kagi";
|
|
@@ -18,7 +18,7 @@ export interface CuratorServerOptions {
|
|
|
18
18
|
queries: string[];
|
|
19
19
|
sessionToken: string;
|
|
20
20
|
timeout: number;
|
|
21
|
-
availableProviders: { all: boolean; openai: boolean; brave: boolean; parallel: boolean; tinyfish: boolean; search1api: boolean; searchinfinity: boolean; querit: boolean; tavily: boolean; jina: boolean; serpdive: boolean; kagi: boolean; bocha: boolean; ollama: boolean; searxng: boolean; duckduckgo: boolean; perplexity: boolean; exa: boolean; gemini: boolean; anysearch: boolean; xai: boolean; brightdata: boolean; serpbase: boolean };
|
|
21
|
+
availableProviders: { all: boolean; openai: boolean; brave: boolean; parallel: boolean; "parallel-mcp": boolean; tinyfish: boolean; search1api: boolean; searchinfinity: boolean; querit: boolean; tavily: boolean; firecrawl: boolean; jina: boolean; serpdive: boolean; kagi: boolean; bocha: boolean; ollama: boolean; searxng: boolean; duckduckgo: boolean; perplexity: boolean; exa: boolean; gemini: boolean; anysearch: boolean; xai: boolean; brightdata: boolean; serpbase: boolean; serper: boolean; valyu: boolean };
|
|
22
22
|
defaultProvider: string;
|
|
23
23
|
searchProvider: string;
|
|
24
24
|
summaryModels: Array<{ value: string; label: string }>;
|
|
@@ -275,11 +275,13 @@ export function startCuratorServer(
|
|
|
275
275
|
if (provider === "openai") return availableProviders.openai;
|
|
276
276
|
if (provider === "brave") return availableProviders.brave;
|
|
277
277
|
if (provider === "parallel") return availableProviders.parallel;
|
|
278
|
+
if (provider === "parallel-mcp") return availableProviders["parallel-mcp"];
|
|
278
279
|
if (provider === "tinyfish") return availableProviders.tinyfish;
|
|
279
280
|
if (provider === "search1api") return availableProviders.search1api;
|
|
280
281
|
if (provider === "searchinfinity") return availableProviders.searchinfinity;
|
|
281
282
|
if (provider === "querit") return availableProviders.querit;
|
|
282
283
|
if (provider === "tavily") return availableProviders.tavily;
|
|
284
|
+
if (provider === "firecrawl") return availableProviders.firecrawl;
|
|
283
285
|
if (provider === "jina") return availableProviders.jina;
|
|
284
286
|
if (provider === "serpdive") return availableProviders.serpdive;
|
|
285
287
|
if (provider === "kagi") return availableProviders.kagi;
|
|
@@ -294,6 +296,8 @@ export function startCuratorServer(
|
|
|
294
296
|
if (provider === "xai") return availableProviders.xai;
|
|
295
297
|
if (provider === "brightdata") return availableProviders.brightdata;
|
|
296
298
|
if (provider === "serpbase") return availableProviders.serpbase;
|
|
299
|
+
if (provider === "serper") return availableProviders.serper;
|
|
300
|
+
if (provider === "valyu") return availableProviders.valyu;
|
|
297
301
|
return false;
|
|
298
302
|
}
|
|
299
303
|
|
|
@@ -3,10 +3,9 @@ import { activityMonitor } from "./activity.ts";
|
|
|
3
3
|
import type { ExtractedContent } from "./extract.ts";
|
|
4
4
|
import type { SearchOptions, SearchResponse } from "./perplexity.ts";
|
|
5
5
|
import { hasCredentialSource, redactCredential, resolveCredential } from "./credential-source.ts";
|
|
6
|
-
import { getWebSearchConfigPath } from "./utils.ts";
|
|
6
|
+
import { fetchWithCredentialRedirects, getWebSearchConfigPath, resolveApiBaseUrl } from "./utils.ts";
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
const EXA_SEARCH_URL = "https://api.exa.ai/search";
|
|
8
|
+
const EXA_API_BASE_URL = "https://api.exa.ai";
|
|
10
9
|
const EXA_MCP_URL = "https://mcp.exa.ai/mcp";
|
|
11
10
|
const CONFIG_PATH = getWebSearchConfigPath();
|
|
12
11
|
const EXA_MCP_ADVANCED_TOOL = "web_search_advanced_exa";
|
|
@@ -14,6 +13,7 @@ const EXA_MCP_BASIC_TOOL = "web_search_exa";
|
|
|
14
13
|
|
|
15
14
|
interface WebSearchConfig {
|
|
16
15
|
exaApiKey?: unknown;
|
|
16
|
+
exaBaseUrl?: unknown;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
interface ExaAnswerResponse {
|
|
@@ -80,6 +80,16 @@ async function getApiKey(signal?: AbortSignal): Promise<string | null> {
|
|
|
80
80
|
});
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
function getApiBaseUrl(): string {
|
|
84
|
+
return resolveApiBaseUrl({
|
|
85
|
+
configKey: "exaBaseUrl",
|
|
86
|
+
configuredValue: loadConfig().exaBaseUrl,
|
|
87
|
+
defaultValue: EXA_API_BASE_URL,
|
|
88
|
+
environmentKey: "EXA_BASE_URL",
|
|
89
|
+
environmentValue: process.env.EXA_BASE_URL,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
83
93
|
function exaApiHeaders(apiKey: string): Record<string, string> {
|
|
84
94
|
return {
|
|
85
95
|
"x-api-key": apiKey,
|
|
@@ -447,6 +457,7 @@ export async function searchWithExa(query: string, options: ExaSearchOptions = {
|
|
|
447
457
|
return searchWithExaMcp(query, options);
|
|
448
458
|
}
|
|
449
459
|
|
|
460
|
+
const apiBaseUrl = getApiBaseUrl();
|
|
450
461
|
const useSearch = options.includeContent
|
|
451
462
|
|| !!options.recencyFilter
|
|
452
463
|
|| !!options.domainFilter?.length
|
|
@@ -456,12 +467,12 @@ export async function searchWithExa(query: string, options: ExaSearchOptions = {
|
|
|
456
467
|
|
|
457
468
|
try {
|
|
458
469
|
if (!useSearch) {
|
|
459
|
-
const response = await
|
|
470
|
+
const response = await fetchWithCredentialRedirects(`${apiBaseUrl}/answer`, {
|
|
460
471
|
method: "POST",
|
|
461
472
|
headers: exaApiHeaders(apiKey),
|
|
462
473
|
body: JSON.stringify({ query }),
|
|
463
474
|
signal: requestSignal(options.signal),
|
|
464
|
-
});
|
|
475
|
+
}, ["x-api-key"]);
|
|
465
476
|
|
|
466
477
|
if (!response.ok) {
|
|
467
478
|
const errorText = redactCredential(await response.text(), apiKey);
|
|
@@ -476,7 +487,7 @@ export async function searchWithExa(query: string, options: ExaSearchOptions = {
|
|
|
476
487
|
};
|
|
477
488
|
}
|
|
478
489
|
|
|
479
|
-
const response = await
|
|
490
|
+
const response = await fetchWithCredentialRedirects(`${apiBaseUrl}/search`, {
|
|
480
491
|
method: "POST",
|
|
481
492
|
headers: exaApiHeaders(apiKey),
|
|
482
493
|
body: JSON.stringify({
|
|
@@ -486,7 +497,7 @@ export async function searchWithExa(query: string, options: ExaSearchOptions = {
|
|
|
486
497
|
: { highlights: true },
|
|
487
498
|
}),
|
|
488
499
|
signal: requestSignal(options.signal),
|
|
489
|
-
});
|
|
500
|
+
}, ["x-api-key"]);
|
|
490
501
|
|
|
491
502
|
if (!response.ok) {
|
|
492
503
|
const errorText = redactCredential(await response.text(), apiKey);
|