@humain/terminal 0.0.3-beta.3 → 0.0.4-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/README.md +1 -0
- package/dist/bun/cli.d.ts.map +1 -1
- package/dist/bun/cli.js.map +1 -1
- package/dist/bun/restore-sandbox-env.d.ts.map +1 -1
- package/dist/bun/restore-sandbox-env.js.map +1 -1
- package/dist/cli/args.d.ts +1 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +11 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/auth-check.d.ts.map +1 -1
- package/dist/cli/auth-check.js.map +1 -1
- package/dist/cli/auth-command.d.ts.map +1 -1
- package/dist/cli/auth-command.js.map +1 -1
- package/dist/cli/config-selector.d.ts.map +1 -1
- package/dist/cli/config-selector.js.map +1 -1
- package/dist/cli/credential-print.d.ts.map +1 -1
- package/dist/cli/credential-print.js.map +1 -1
- package/dist/cli/experimental/auth.d.ts.map +1 -1
- package/dist/cli/experimental/auth.js.map +1 -1
- package/dist/cli/experimental/cli.d.ts.map +1 -1
- package/dist/cli/experimental/command-options.d.ts.map +1 -1
- package/dist/cli/experimental/command-options.js.map +1 -1
- package/dist/cli/experimental/command.d.ts.map +1 -1
- package/dist/cli/experimental/command.js +2 -5
- package/dist/cli/experimental/command.js.map +1 -1
- package/dist/cli/experimental/commands/client.d.ts.map +1 -1
- package/dist/cli/experimental/commands/client.js.map +1 -1
- package/dist/cli/experimental/commands/pi.d.ts.map +1 -1
- package/dist/cli/experimental/commands/pi.js.map +1 -1
- package/dist/cli/experimental/commands/server.d.ts.map +1 -1
- package/dist/cli/experimental/commands/server.js.map +1 -1
- package/dist/cli/experimental/transport-address.d.ts.map +1 -1
- package/dist/cli/experimental/transport-address.js.map +1 -1
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/cli/initial-message.d.ts +1 -1
- package/dist/cli/initial-message.d.ts.map +1 -1
- package/dist/cli/initial-message.js.map +1 -1
- package/dist/cli/list-models.d.ts.map +1 -1
- package/dist/cli/list-models.js.map +1 -1
- package/dist/cli/project-trust.d.ts.map +1 -1
- package/dist/cli/project-trust.js.map +1 -1
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/cli/startup-ui.d.ts.map +1 -1
- package/dist/cli/startup-ui.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/remote-session.d.ts.map +1 -1
- package/dist/client/remote-session.js.map +1 -1
- package/dist/client/transcript.d.ts.map +1 -1
- package/dist/client/transcript.js +1 -1
- package/dist/client/transcript.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +0 -8
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +36 -2
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +107 -129
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-guidance.d.ts.map +1 -1
- package/dist/core/auth-guidance.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +3 -8
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/cache-stats.d.ts.map +1 -1
- package/dist/core/cache-stats.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/index.d.ts.map +1 -1
- package/dist/core/compaction/utils.d.ts.map +1 -1
- package/dist/core/compaction/utils.js.map +1 -1
- package/dist/core/defaults.d.ts.map +1 -1
- package/dist/core/diagnostics.d.ts.map +1 -1
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/exec.d.ts.map +1 -1
- package/dist/core/exec.js.map +1 -1
- package/dist/core/experimental.d.ts +4 -0
- package/dist/core/experimental.d.ts.map +1 -1
- package/dist/core/experimental.js +4 -0
- package/dist/core/experimental.js.map +1 -1
- package/dist/core/export-html/ansi-to-html.d.ts.map +1 -1
- package/dist/core/export-html/ansi-to-html.js.map +1 -1
- package/dist/core/export-html/index.d.ts.map +1 -1
- package/dist/core/export-html/index.js.map +1 -1
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
- package/dist/core/export-html/tool-renderer.js.map +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +22 -30
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +4 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/extensions/wrapper.d.ts.map +1 -1
- package/dist/core/extensions/wrapper.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +17 -18
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/keybindings.d.ts +24 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +0 -1
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-config.d.ts +5 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +1 -2
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +0 -1
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +15 -25
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/models-store.d.ts.map +1 -1
- package/dist/core/models-store.js +3 -4
- package/dist/core/models-store.js.map +1 -1
- package/dist/core/output-guard.d.ts.map +1 -1
- package/dist/core/output-guard.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +0 -7
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/pi-manifest.d.ts.map +1 -1
- package/dist/core/pi-manifest.js.map +1 -1
- package/dist/core/project-trust.d.ts.map +1 -1
- package/dist/core/project-trust.js.map +1 -1
- package/dist/core/prompt-templates.d.ts.map +1 -1
- package/dist/core/prompt-templates.js.map +1 -1
- package/dist/core/provider-attribution.d.ts.map +1 -1
- package/dist/core/provider-attribution.js.map +1 -1
- package/dist/core/provider-composer.d.ts.map +1 -1
- package/dist/core/provider-composer.js.map +1 -1
- package/dist/core/radius.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/resolve-config-value.d.ts.map +1 -1
- package/dist/core/resolve-config-value.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +0 -44
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/runtime-credentials.d.ts.map +1 -1
- package/dist/core/runtime-credentials.js +1 -2
- package/dist/core/runtime-credentials.js.map +1 -1
- package/dist/core/sdk.d.ts +5 -3
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +3 -2
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-cwd.d.ts.map +1 -1
- package/dist/core/session-cwd.js +0 -1
- package/dist/core/session-cwd.js.map +1 -1
- package/dist/core/session-manager.d.ts +4 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +55 -35
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +6 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +19 -17
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/source-info.d.ts.map +1 -1
- package/dist/core/source-info.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +1 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/telemetry.d.ts.map +1 -1
- package/dist/core/telemetry.js.map +1 -1
- package/dist/core/timings.d.ts.map +1 -1
- package/dist/core/timings.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +10 -5
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +2 -0
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
- package/dist/core/tools/file-mutation-queue.js.map +1 -1
- package/dist/core/tools/find.d.ts +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +1 -1
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +1 -1
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +2 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +9 -1
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +12 -18
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/core/tools/path-utils.d.ts.map +1 -1
- package/dist/core/tools/path-utils.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +2 -0
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts.map +1 -1
- package/dist/core/tools/render-utils.js.map +1 -1
- package/dist/core/tools/subagent.d.ts +82 -0
- package/dist/core/tools/subagent.d.ts.map +1 -0
- package/dist/core/tools/subagent.js +980 -0
- package/dist/core/tools/subagent.js.map +1 -0
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
- package/dist/core/tools/truncate.d.ts.map +1 -1
- package/dist/core/tools/truncate.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +2 -1
- package/dist/core/tools/write.js.map +1 -1
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +0 -1
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/core/usage-totals.d.ts.map +1 -1
- package/dist/core/usage-totals.js.map +1 -1
- package/dist/extensions/index.d.ts.map +1 -1
- package/dist/extensions/llama/client.d.ts.map +1 -1
- package/dist/extensions/llama/client.js +0 -2
- package/dist/extensions/llama/client.js.map +1 -1
- package/dist/extensions/llama/huggingface.d.ts.map +1 -1
- package/dist/extensions/llama/huggingface.js +0 -2
- package/dist/extensions/llama/huggingface.js.map +1 -1
- package/dist/extensions/llama/index.d.ts.map +1 -1
- package/dist/extensions/llama/index.js.map +1 -1
- package/dist/extensions/llama/provider.d.ts.map +1 -1
- package/dist/extensions/llama/provider.js.map +1 -1
- package/dist/extensions/llama/ui.d.ts.map +1 -1
- package/dist/extensions/llama/ui.js +12 -28
- package/dist/extensions/llama/ui.js.map +1 -1
- package/dist/humain/agent-execution-protocol.d.ts.map +1 -1
- package/dist/humain/agent-execution-protocol.js.map +1 -1
- package/dist/humain/agent-execution-runtime.d.ts.map +1 -1
- package/dist/humain/agent-execution-runtime.js.map +1 -1
- package/dist/humain/bedrock-provider/vendor/index.bundle.mjs +127 -7
- package/dist/humain/brand-logo.d.ts.map +1 -1
- package/dist/humain/index.d.ts.map +1 -1
- package/dist/humain/index.js.map +1 -1
- package/dist/humain/legacy-workflow-tiers.d.ts.map +1 -1
- package/dist/humain/legacy-workflow-tiers.js.map +1 -1
- package/dist/humain/mcp-adapter/vendor/CHANGELOG.md +36 -0
- package/dist/humain/mcp-adapter/vendor/README.md +47 -5
- package/dist/humain/mcp-adapter/vendor/cli.js +163 -1
- package/dist/humain/mcp-adapter/vendor/commands.ts +128 -22
- package/dist/humain/mcp-adapter/vendor/config.ts +25 -7
- package/dist/humain/mcp-adapter/vendor/direct-tools.ts +5 -4
- package/dist/humain/mcp-adapter/vendor/index.bundle.mjs +1663 -458
- package/dist/humain/mcp-adapter/vendor/index.ts +219 -32
- package/dist/humain/mcp-adapter/vendor/init.ts +36 -4
- package/dist/humain/mcp-adapter/vendor/lifecycle.ts +315 -24
- package/dist/humain/mcp-adapter/vendor/mcp-auth-flow.ts +39 -6
- package/dist/humain/mcp-adapter/vendor/mcp-bearer-store.ts +387 -0
- package/dist/humain/mcp-adapter/vendor/mcp-callback-server.ts +27 -3
- package/dist/humain/mcp-adapter/vendor/mcp-code.ts +4 -0
- package/dist/humain/mcp-adapter/vendor/mcp-panel.ts +42 -13
- package/dist/humain/mcp-adapter/vendor/metadata-cache.ts +32 -21
- package/dist/humain/mcp-adapter/vendor/package-mcp-loader.ts +168 -0
- package/dist/humain/mcp-adapter/vendor/package.json +179 -177
- package/dist/humain/mcp-adapter/vendor/proxy-modes.ts +5 -4
- package/dist/humain/mcp-adapter/vendor/request-headers-command.ts +31 -37
- package/dist/humain/mcp-adapter/vendor/search-ranking.ts +92 -22
- package/dist/humain/mcp-adapter/vendor/server-manager.ts +140 -2
- package/dist/humain/mcp-adapter/vendor/session-recovery.ts +7 -0
- package/dist/humain/mcp-adapter/vendor/tool-approval.ts +15 -2
- package/dist/humain/mcp-adapter/vendor/tool-result-renderer.ts +44 -4
- package/dist/humain/mcp-adapter/vendor/types.ts +2 -0
- package/dist/humain/mcp-adapter/vendor/ui-server.ts +18 -16
- package/dist/humain/mcp-adapter/vendor/utils.ts +70 -0
- package/dist/humain/mcp-adapter.d.ts.map +1 -1
- package/dist/humain/mcp-adapter.js.map +1 -1
- package/dist/humain/pi-agent-execution-host.d.ts.map +1 -1
- package/dist/humain/pi-agent-execution-host.js.map +1 -1
- package/dist/humain/web-access/host.d.ts.map +1 -1
- package/dist/humain/web-access/index.d.ts.map +1 -1
- package/dist/humain/web-access/index.js.map +1 -1
- package/dist/humain/web-access/vendor/activity.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/activity.js +7 -5
- package/dist/humain/web-access/vendor/activity.js.map +1 -1
- package/dist/humain/web-access/vendor/anysearch.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/anysearch.js +3 -2
- package/dist/humain/web-access/vendor/anysearch.js.map +1 -1
- package/dist/humain/web-access/vendor/auth-fetch.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/auth-fetch.js.map +1 -1
- package/dist/humain/web-access/vendor/bocha.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/bocha.js.map +1 -1
- package/dist/humain/web-access/vendor/brave.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/brave.js +14 -4
- package/dist/humain/web-access/vendor/brave.js.map +1 -1
- package/dist/humain/web-access/vendor/brightdata-unlocker.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/brightdata-unlocker.js.map +1 -1
- package/dist/humain/web-access/vendor/brightdata.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/brightdata.js.map +1 -1
- package/dist/humain/web-access/vendor/chrome-cookies.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/chrome-cookies.js +92 -14
- package/dist/humain/web-access/vendor/chrome-cookies.js.map +1 -1
- package/dist/humain/web-access/vendor/content-find.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/content-find.js.map +1 -1
- package/dist/humain/web-access/vendor/credential-source.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/credential-source.js +0 -2
- package/dist/humain/web-access/vendor/credential-source.js.map +1 -1
- package/dist/humain/web-access/vendor/curator-page.d.ts +3 -0
- package/dist/humain/web-access/vendor/curator-page.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/curator-page.js +4 -1
- package/dist/humain/web-access/vendor/curator-page.js.map +1 -1
- package/dist/humain/web-access/vendor/curator-server.d.ts +3 -0
- package/dist/humain/web-access/vendor/curator-server.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/curator-server.js +6 -0
- package/dist/humain/web-access/vendor/curator-server.js.map +1 -1
- package/dist/humain/web-access/vendor/data-uri-sanitize.d.ts +21 -0
- package/dist/humain/web-access/vendor/data-uri-sanitize.d.ts.map +1 -0
- package/dist/humain/web-access/vendor/data-uri-sanitize.js +373 -0
- package/dist/humain/web-access/vendor/data-uri-sanitize.js.map +1 -0
- package/dist/humain/web-access/vendor/datalab-pdf-extract.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/datalab-pdf-extract.js.map +1 -1
- package/dist/humain/web-access/vendor/declared-web-links.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/declared-web-links.js.map +1 -1
- package/dist/humain/web-access/vendor/duckduckgo.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/duckduckgo.js.map +1 -1
- package/dist/humain/web-access/vendor/exa.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/exa.js +16 -7
- package/dist/humain/web-access/vendor/exa.js.map +1 -1
- package/dist/humain/web-access/vendor/extract.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/extract.js +45 -5
- package/dist/humain/web-access/vendor/extract.js.map +1 -1
- package/dist/humain/web-access/vendor/feature-config.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/feature-config.js.map +1 -1
- package/dist/humain/web-access/vendor/fetch-params.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/fetch-params.js.map +1 -1
- package/dist/humain/web-access/vendor/firecrawl.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/firecrawl.js +15 -2
- package/dist/humain/web-access/vendor/firecrawl.js.map +1 -1
- package/dist/humain/web-access/vendor/gemini-api.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/gemini-api.js.map +1 -1
- package/dist/humain/web-access/vendor/gemini-pdf-extract.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/gemini-pdf-extract.js.map +1 -1
- package/dist/humain/web-access/vendor/gemini-search.d.ts +2 -2
- package/dist/humain/web-access/vendor/gemini-search.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/gemini-search.js +25 -8
- package/dist/humain/web-access/vendor/gemini-search.js.map +1 -1
- package/dist/humain/web-access/vendor/gemini-url-context.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/gemini-url-context.js.map +1 -1
- package/dist/humain/web-access/vendor/gemini-web-config.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/gemini-web-config.js.map +1 -1
- package/dist/humain/web-access/vendor/gemini-web.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/gemini-web.js.map +1 -1
- package/dist/humain/web-access/vendor/github-api.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/github-api.js.map +1 -1
- package/dist/humain/web-access/vendor/github-extract.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/github-extract.js +69 -35
- package/dist/humain/web-access/vendor/github-extract.js.map +1 -1
- package/dist/humain/web-access/vendor/index.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/index.js +58 -54
- package/dist/humain/web-access/vendor/index.js.map +1 -1
- package/dist/humain/web-access/vendor/jina-search.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/jina-search.js.map +1 -1
- package/dist/humain/web-access/vendor/kagi.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/kagi.js.map +1 -1
- package/dist/humain/web-access/vendor/ollama.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/ollama.js.map +1 -1
- package/dist/humain/web-access/vendor/openai-search.d.ts +1 -1
- package/dist/humain/web-access/vendor/openai-search.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/openai-search.js +15 -5
- package/dist/humain/web-access/vendor/openai-search.js.map +1 -1
- package/dist/humain/web-access/vendor/page-query.d.ts +1 -2
- package/dist/humain/web-access/vendor/page-query.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/page-query.js +5 -2
- package/dist/humain/web-access/vendor/page-query.js.map +1 -1
- package/dist/humain/web-access/vendor/parallel-mcp.d.ts +10 -0
- package/dist/humain/web-access/vendor/parallel-mcp.d.ts.map +1 -0
- package/dist/humain/web-access/vendor/parallel-mcp.js +224 -0
- package/dist/humain/web-access/vendor/parallel-mcp.js.map +1 -0
- package/dist/humain/web-access/vendor/parallel.d.ts +1 -0
- package/dist/humain/web-access/vendor/parallel.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/parallel.js +2 -2
- package/dist/humain/web-access/vendor/parallel.js.map +1 -1
- package/dist/humain/web-access/vendor/pdf-extract.d.ts +1 -0
- package/dist/humain/web-access/vendor/pdf-extract.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/pdf-extract.js +15 -5
- package/dist/humain/web-access/vendor/pdf-extract.js.map +1 -1
- package/dist/humain/web-access/vendor/perplexity.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/perplexity.js.map +1 -1
- package/dist/humain/web-access/vendor/querit.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/querit.js.map +1 -1
- package/dist/humain/web-access/vendor/query-rewrite.d.ts +3 -0
- package/dist/humain/web-access/vendor/query-rewrite.d.ts.map +1 -0
- package/dist/humain/web-access/vendor/query-rewrite.js +42 -0
- package/dist/humain/web-access/vendor/query-rewrite.js.map +1 -0
- package/dist/humain/web-access/vendor/render-search-error.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/render-search-error.js.map +1 -1
- package/dist/humain/web-access/vendor/rsc-extract.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/rsc-extract.js.map +1 -1
- package/dist/humain/web-access/vendor/search1api.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/search1api.js.map +1 -1
- package/dist/humain/web-access/vendor/searchinfinity.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/searchinfinity.js.map +1 -1
- package/dist/humain/web-access/vendor/searxng.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/searxng.js.map +1 -1
- package/dist/humain/web-access/vendor/serpbase.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/serpbase.js.map +1 -1
- package/dist/humain/web-access/vendor/serpdive.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/serpdive.js.map +1 -1
- package/dist/humain/web-access/vendor/serper.d.ts +4 -0
- package/dist/humain/web-access/vendor/serper.d.ts.map +1 -0
- package/dist/humain/web-access/vendor/serper.js +206 -0
- package/dist/humain/web-access/vendor/serper.js.map +1 -0
- package/dist/humain/web-access/vendor/source-check.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/source-check.js.map +1 -1
- package/dist/humain/web-access/vendor/ssrf-protection.d.ts +2 -0
- package/dist/humain/web-access/vendor/ssrf-protection.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/ssrf-protection.js +11 -2
- package/dist/humain/web-access/vendor/ssrf-protection.js.map +1 -1
- package/dist/humain/web-access/vendor/storage.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/storage.js.map +1 -1
- package/dist/humain/web-access/vendor/summary-model-scope.d.ts +5 -0
- package/dist/humain/web-access/vendor/summary-model-scope.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/summary-model-scope.js +10 -5
- package/dist/humain/web-access/vendor/summary-model-scope.js.map +1 -1
- package/dist/humain/web-access/vendor/summary-review.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/summary-review.js +40 -14
- package/dist/humain/web-access/vendor/summary-review.js.map +1 -1
- package/dist/humain/web-access/vendor/tavily.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/tavily.js +14 -4
- package/dist/humain/web-access/vendor/tavily.js.map +1 -1
- package/dist/humain/web-access/vendor/tinyfish.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/tinyfish.js.map +1 -1
- package/dist/humain/web-access/vendor/utils.d.ts +10 -0
- package/dist/humain/web-access/vendor/utils.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/utils.js +70 -0
- package/dist/humain/web-access/vendor/utils.js.map +1 -1
- package/dist/humain/web-access/vendor/valyu.d.ts +6 -0
- package/dist/humain/web-access/vendor/valyu.d.ts.map +1 -0
- package/dist/humain/web-access/vendor/valyu.js +194 -0
- package/dist/humain/web-access/vendor/valyu.js.map +1 -0
- package/dist/humain/web-access/vendor/video-extract.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/video-extract.js.map +1 -1
- package/dist/humain/web-access/vendor/xai-search.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/xai-search.js.map +1 -1
- package/dist/humain/web-access/vendor/youtube-extract.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/youtube-extract.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +4 -0
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js.map +1 -1
- package/dist/modes/agent-exec-mode.d.ts.map +1 -1
- package/dist/modes/agent-exec-mode.js.map +1 -1
- package/dist/modes/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/armin.d.ts.map +1 -1
- package/dist/modes/interactive/components/armin.js +6 -10
- package/dist/modes/interactive/components/armin.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +2 -9
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +4 -9
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.js +0 -3
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js +1 -3
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +1 -3
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +5 -22
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
- package/dist/modes/interactive/components/countdown-timer.js +0 -5
- package/dist/modes/interactive/components/countdown-timer.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +1 -8
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-entry.js +1 -4
- package/dist/modes/interactive/components/custom-entry.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js +1 -7
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
- package/dist/modes/interactive/components/daxnuts.js +6 -8
- package/dist/modes/interactive/components/daxnuts.js.map +1 -1
- package/dist/modes/interactive/components/diff.d.ts.map +1 -1
- package/dist/modes/interactive/components/diff.js +2 -2
- package/dist/modes/interactive/components/diff.js.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.js +0 -1
- package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -1
- package/dist/modes/interactive/components/earendil-announcement.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +1 -7
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-input.js +2 -8
- package/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +1 -9
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.js +2 -4
- package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +1 -3
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +3 -9
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/markdown-transform.d.ts.map +1 -1
- package/dist/modes/interactive/components/markdown-transform.js.map +1 -1
- package/dist/modes/interactive/components/mermaid.d.ts.map +1 -1
- package/dist/modes/interactive/components/mermaid.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +14 -26
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js +3 -11
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +9 -14
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +33 -57
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +3 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +10 -14
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.js +0 -1
- package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.js +1 -3
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
- package/dist/modes/interactive/components/status-indicator.js +0 -2
- package/dist/modes/interactive/components/status-indicator.js.map +1 -1
- package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/theme-selector.js +0 -2
- package/dist/modes/interactive/components/theme-selector.js.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +0 -1
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +19 -25
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +18 -34
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/trust-selector.js +0 -6
- package/dist/modes/interactive/components/trust-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.js +3 -6
- package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +0 -4
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
- package/dist/modes/interactive/components/visual-truncate.js.map +1 -1
- package/dist/modes/interactive/external-editor.d.ts.map +1 -1
- package/dist/modes/interactive/external-editor.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +18 -2
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +147 -137
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/model-catalog-refresh.d.ts +7 -0
- package/dist/modes/interactive/model-catalog-refresh.d.ts.map +1 -0
- package/dist/modes/interactive/model-catalog-refresh.js +36 -0
- package/dist/modes/interactive/model-catalog-refresh.js.map +1 -0
- package/dist/modes/interactive/model-search.d.ts.map +1 -1
- package/dist/modes/interactive/model-search.js.map +1 -1
- package/dist/modes/interactive/theme/dark.json +2 -0
- package/dist/modes/interactive/theme/light.json +2 -0
- package/dist/modes/interactive/theme/theme-controller.d.ts +10 -2
- package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-controller.js +25 -18
- package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
- package/dist/modes/interactive/theme/theme-schema.json +9 -1
- package/dist/modes/interactive/theme/theme.d.ts +7 -5
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +12 -8
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/json-event.d.ts +4 -1
- package/dist/modes/json-event.d.ts.map +1 -1
- package/dist/modes/json-event.js +5 -2
- package/dist/modes/json-event.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/jsonl.d.ts.map +1 -1
- package/dist/modes/rpc/jsonl.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +9 -10
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/rpc-entry.d.ts.map +1 -1
- package/dist/rpc-entry.js.map +1 -1
- package/dist/server/create-harness.d.ts.map +1 -1
- package/dist/server/create-harness.js +2 -0
- package/dist/server/create-harness.js.map +1 -1
- package/dist/utils/abort.d.ts.map +1 -1
- package/dist/utils/abort.js.map +1 -1
- package/dist/utils/ansi.d.ts.map +1 -1
- package/dist/utils/ansi.js.map +1 -1
- package/dist/utils/changelog.d.ts.map +1 -1
- package/dist/utils/changelog.js.map +1 -1
- package/dist/utils/child-process.d.ts.map +1 -1
- package/dist/utils/child-process.js.map +1 -1
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +1 -1
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/clipboard-native.d.ts.map +1 -1
- package/dist/utils/clipboard-native.js.map +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/deprecation.d.ts.map +1 -1
- package/dist/utils/deprecation.js.map +1 -1
- package/dist/utils/exif-orientation.d.ts.map +1 -1
- package/dist/utils/exif-orientation.js.map +1 -1
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/fs-watch.d.ts.map +1 -1
- package/dist/utils/fs-watch.js.map +1 -1
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/html.d.ts.map +1 -1
- package/dist/utils/html.js.map +1 -1
- package/dist/utils/image-convert.d.ts.map +1 -1
- package/dist/utils/image-convert.js.map +1 -1
- package/dist/utils/image-process.d.ts.map +1 -1
- package/dist/utils/image-process.js.map +1 -1
- package/dist/utils/image-resize-core.d.ts.map +1 -1
- package/dist/utils/image-resize-core.js.map +1 -1
- package/dist/utils/image-resize-worker.d.ts.map +1 -1
- package/dist/utils/image-resize-worker.js.map +1 -1
- package/dist/utils/image-resize.d.ts.map +1 -1
- package/dist/utils/image-resize.js.map +1 -1
- package/dist/utils/json.d.ts.map +1 -1
- package/dist/utils/json.js.map +1 -1
- package/dist/utils/management-http.d.ts.map +1 -1
- package/dist/utils/management-http.js.map +1 -1
- package/dist/utils/mime.d.ts.map +1 -1
- package/dist/utils/mime.js.map +1 -1
- package/dist/utils/open-browser.d.ts.map +1 -1
- package/dist/utils/open-browser.js.map +1 -1
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/photon.d.ts.map +1 -1
- package/dist/utils/photon.js.map +1 -1
- package/dist/utils/pi-user-agent.d.ts.map +1 -1
- package/dist/utils/pi-user-agent.js.map +1 -1
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/sleep.d.ts.map +1 -1
- package/dist/utils/sleep.js.map +1 -1
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js +1 -1
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/dist/utils/tool-result-images.d.ts.map +1 -1
- package/dist/utils/tool-result-images.js.map +1 -1
- package/dist/utils/tools-manager.d.ts +10 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +14 -19
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js.map +1 -1
- package/dist/utils/windows-self-update.d.ts.map +1 -1
- package/dist/utils/windows-self-update.js.map +1 -1
- package/docs/compaction.md +3 -3
- package/docs/custom-provider.md +1 -1
- package/docs/environment-variables.md +1 -0
- package/docs/extensions.md +5 -1
- package/docs/json.md +6 -4
- package/docs/keybindings.md +8 -2
- package/docs/rpc.md +15 -4
- package/docs/sdk.md +9 -0
- package/docs/settings.md +31 -0
- package/docs/terminal-setup.md +23 -2
- package/docs/themes.md +23 -3
- package/docs/tui.md +2 -2
- package/docs/usage.md +3 -2
- package/examples/extensions/README.md +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +2 -2
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/minimal-mode.ts +6 -6
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +2 -2
- package/examples/extensions/subagent/README.md +24 -147
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +853 -661
- package/package.json +33 -33
- package/src/humain/mcp-adapter/UPSTREAM.md +1 -1
- package/src/humain/mcp-adapter/upstream.json +3 -3
- package/src/humain/mcp-adapter/vendor/CHANGELOG.md +36 -0
- package/src/humain/web-access/UPSTREAM.md +1 -1
- package/src/humain/web-access/upstream.json +4 -4
- package/examples/extensions/subagent/agents.ts +0 -126
- package/examples/extensions/subagent/index.ts +0 -1015
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serpdive.js","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/serpdive.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { activityMonitor } from \"./activity.ts\";\nimport type { ExtractedContent } from \"./extract.ts\";\nimport type { SearchOptions, SearchResponse } from \"./perplexity.ts\";\nimport { hasCredentialSource, redactCredential, resolveCredential } from \"./credential-source.ts\";\nimport { getWebSearchConfigPath } from \"./utils.ts\";\n\nconst SERPDIVE_API_URL = \"https://api.serpdive.com/v1/search\";\nconst CONFIG_PATH = getWebSearchConfigPath();\nconst SEARCH_TIMEOUT_MS = 60_000;\n\n// Retrieval depth. The default is deliberately the free tier: this provider\n// ships inside someone else's framework, and installing it must never start\n// spending on a user's behalf without them choosing it.\n// krill — free, fair use, no synthesized answer (see buildAnswer below)\n// mako — the fact-carrying sentences of each page, 1 credit\n// moby — the full readable content of every page, 1.5 credits\n// Pricing: https://serpdive.com/pricing\nconst MODELS = [\"krill\", \"mako\", \"moby\"] as const;\ntype SerpdiveModel = (typeof MODELS)[number];\nconst DEFAULT_MODEL: SerpdiveModel = \"krill\";\n\ninterface WebSearchConfig {\n\tserpdiveApiKey?: unknown;\n\tserpdiveModel?: unknown;\n}\n\ninterface SerpdiveResult {\n\turl?: string;\n\ttitle?: string | null;\n\tdate?: string;\n\tcontent?: string;\n}\n\ninterface SerpdiveResponse {\n\tquery?: string;\n\tmodel?: string;\n\tanswer?: string | null;\n\tresults?: SerpdiveResult[];\n}\n\ninterface SerpdiveSearchOptions extends SearchOptions {\n\tincludeContent?: boolean;\n}\n\nlet cachedConfig: WebSearchConfig | null = null;\n\nfunction loadConfig(): WebSearchConfig {\n\tif (cachedConfig) return cachedConfig;\n\tif (!existsSync(CONFIG_PATH)) {\n\t\tcachedConfig = {};\n\t\treturn cachedConfig;\n\t}\n\n\tconst raw = readFileSync(CONFIG_PATH, \"utf-8\");\n\ttry {\n\t\tcachedConfig = JSON.parse(raw) as WebSearchConfig;\n\t\treturn cachedConfig;\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tthrow new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);\n\t}\n}\n\nasync function getApiKey(signal?: AbortSignal): Promise<string | null> {\n\treturn resolveCredential({\n\t\tprovider: \"SERPdive\",\n\t\tconfiguredValue: loadConfig().serpdiveApiKey,\n\t\tenvironmentValue: process.env.SERPDIVE_API_KEY,\n\t\tsignal,\n\t});\n}\n\nasync function requireApiKey(signal?: AbortSignal): Promise<string> {\n\tconst apiKey = await getApiKey(signal);\n\tif (!apiKey) {\n\t\tthrow new Error(\n\t\t\t\"SERPdive API key not found. Either:\\n\" +\n\t\t\t` 1. Create ${CONFIG_PATH} with { \"serpdiveApiKey\": \"your-key\" }\\n` +\n\t\t\t\" 2. Set SERPDIVE_API_KEY environment variable\\n\" +\n\t\t\t\"Get a key at https://serpdive.com/dashboard/keys\",\n\t\t);\n\t}\n\treturn apiKey;\n}\n\n// An unknown value falls back to the free default rather than failing: a typo\n// in a config file must not cost the user money, and must not break search.\nfunction resolveModel(): SerpdiveModel {\n\tconst raw = process.env.SERPDIVE_MODEL ?? loadConfig().serpdiveModel;\n\tif (typeof raw !== \"string\") return DEFAULT_MODEL;\n\tconst value = raw.trim().toLowerCase();\n\treturn (MODELS as readonly string[]).includes(value) ? value as SerpdiveModel : DEFAULT_MODEL;\n}\n\nfunction normalizeCount(value: number | undefined): number {\n\tif (typeof value !== \"number\" || !Number.isFinite(value)) return 5;\n\treturn Math.max(1, Math.min(Math.floor(value), 20));\n}\n\nfunction normalizeDomain(value: string): string | null {\n\tlet input = value.trim().toLowerCase();\n\tif (!input) return null;\n\tif (input.startsWith(\"-\")) input = input.slice(1).trim();\n\tif (!input) return null;\n\ttry {\n\t\tconst parsed = input.includes(\"://\") ? new URL(input) : new URL(`https://${input}`);\n\t\tinput = parsed.hostname;\n\t} catch {\n\t\tinput = input.split(\"/\")[0]?.split(\":\")[0] ?? \"\";\n\t}\n\tinput = input.replace(/^\\.+|\\.+$/g, \"\");\n\treturn /^[a-z0-9][a-z0-9.-]*\\.[a-z]{2,}$/i.test(input) ? input : null;\n}\n\ninterface DomainFilters {\n\tinclude: string[];\n\texclude: string[];\n}\n\nfunction parseDomainFilter(domainFilter: string[] | undefined): DomainFilters {\n\tconst filters: DomainFilters = { include: [], exclude: [] };\n\tif (!domainFilter?.length) return filters;\n\tfor (const raw of domainFilter) {\n\t\tconst domain = normalizeDomain(raw);\n\t\tif (!domain) continue;\n\t\tconst target = raw.trim().startsWith(\"-\") ? filters.exclude : filters.include;\n\t\tif (!target.includes(domain)) target.push(domain);\n\t}\n\treturn filters;\n}\n\nfunction domainMatches(hostname: string, domain: string): boolean {\n\treturn hostname === domain || hostname.endsWith(`.${domain}`);\n}\n\n// SERPdive exposes no include/exclude domain parameter, so the filter is applied\n// here, on what came back. It can therefore only ever narrow a page of results —\n// it cannot ask the engine for more pages from a given domain.\nfunction passesDomainFilters(url: string, filters: DomainFilters): boolean {\n\tif (filters.include.length === 0 && filters.exclude.length === 0) return true;\n\tlet hostname: string;\n\ttry {\n\t\thostname = new URL(url).hostname.toLowerCase();\n\t} catch {\n\t\treturn false;\n\t}\n\tif (filters.exclude.some((domain) => domainMatches(hostname, domain))) return false;\n\tif (filters.include.length === 0) return true;\n\treturn filters.include.some((domain) => domainMatches(hostname, domain));\n}\n\n// SERPdive has NO time-range parameter. Recency is expressed inside the question\n// and read by the engine, which biases ranking toward recent pages — it is a\n// hint, never a guaranteed freshness filter, and results outside the window can\n// still come back.\nfunction applyRecencyHint(query: string, recencyFilter: SearchOptions[\"recencyFilter\"]): string {\n\tif (!recencyFilter) return query;\n\tconst hints: Record<string, string> = {\n\t\tday: \"past 24 hours\",\n\t\tweek: \"past week\",\n\t\tmonth: \"past month\",\n\t\tyear: \"past year\",\n\t};\n\tconst hint = hints[recencyFilter];\n\treturn hint ? `${query} ${hint}` : query;\n}\n\nfunction requestSignal(signal?: AbortSignal): AbortSignal {\n\tconst timeout = AbortSignal.timeout(SEARCH_TIMEOUT_MS);\n\treturn signal ? AbortSignal.any([signal, timeout]) : timeout;\n}\n\nfunction errorMessage(err: unknown): string {\n\treturn err instanceof Error ? err.message : String(err);\n}\n\nfunction mapResults(\n\tresults: SerpdiveResult[] | undefined,\n\tnumResults: number,\n\tfilters: DomainFilters,\n): SearchResponse[\"results\"] {\n\tif (!Array.isArray(results)) return [];\n\tconst mapped: SearchResponse[\"results\"] = [];\n\tfor (const item of results) {\n\t\tif (!item?.url || !passesDomainFilters(item.url, filters)) continue;\n\t\tmapped.push({\n\t\t\ttitle: item.title || `Source ${mapped.length + 1}`,\n\t\t\turl: item.url,\n\t\t\tsnippet: typeof item.content === \"string\" ? item.content.replace(/\\s+/g, \" \").trim() : \"\",\n\t\t});\n\t\tif (mapped.length >= numResults) break;\n\t}\n\treturn mapped;\n}\n\nfunction mapInlineContent(results: SerpdiveResult[] | undefined, filters: DomainFilters): ExtractedContent[] {\n\tif (!Array.isArray(results)) return [];\n\treturn results.flatMap((item) => {\n\t\tif (!item?.url || !passesDomainFilters(item.url, filters)) return [];\n\t\tif (typeof item.content !== \"string\" || item.content.trim().length === 0) return [];\n\t\treturn [{\n\t\t\turl: item.url,\n\t\t\ttitle: item.title || \"\",\n\t\t\tcontent: item.content,\n\t\t\terror: null,\n\t\t}];\n\t});\n}\n\n// The free krill tier returns extracted content but no synthesized answer, so\n// one is assembled from the sources — the same shape brave.ts and searxng.ts\n// produce for providers that do not synthesize. mako and moby ask the API for a\n// real answer and use it when it comes back.\nfunction buildAnswer(apiAnswer: string | null | undefined, results: SearchResponse[\"results\"]): string {\n\tif (typeof apiAnswer === \"string\" && apiAnswer.trim().length > 0) return apiAnswer;\n\treturn results\n\t\t.map((result) => {\n\t\t\tif (result.snippet) return `${result.snippet}\\nSource: ${result.title} (${result.url})`;\n\t\t\treturn `Source: ${result.title} (${result.url})`;\n\t\t})\n\t\t.join(\"\\n\\n\");\n}\n\nexport function isSerpdiveAvailable(): boolean {\n\treturn hasCredentialSource({\n\t\tprovider: \"SERPdive\",\n\t\tconfiguredValue: loadConfig().serpdiveApiKey,\n\t\tenvironmentValue: process.env.SERPDIVE_API_KEY,\n\t});\n}\n\nexport async function searchWithSerpdive(query: string, options: SerpdiveSearchOptions = {}): Promise<SearchResponse> {\n\tconst apiKey = await requireApiKey(options.signal);\n\tconst numResults = normalizeCount(options.numResults);\n\tconst filters = parseDomainFilter(options.domainFilter);\n\tconst model = resolveModel();\n\tconst body: Record<string, unknown> = {\n\t\tquery: applyRecencyHint(query, options.recencyFilter),\n\t\tmodel,\n\t\t// max_results is a CAP, never a minimum: the engine returns what it\n\t\t// judges relevant, up to this many. Asking for more does not produce more.\n\t\tmax_results: Math.min(numResults, 10),\n\t\t// krill has no answer synthesis — asking for one there is silently ignored\n\t\t// by the API, so it is not asked for at all.\n\t\t...(model === \"krill\" ? {} : { answer: true }),\n\t};\n\n\tconst activityId = activityMonitor.logStart({ type: \"api\", query });\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetch(SERPDIVE_API_URL, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"Authorization\": `Bearer ${apiKey}`,\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t\tbody: JSON.stringify(body),\n\t\t\tsignal: requestSignal(options.signal),\n\t\t});\n\t} catch (err) {\n\t\tconst message = errorMessage(err);\n\t\tconst redactedMessage = redactCredential(message, apiKey);\n\t\tif (redactedMessage.toLowerCase().includes(\"abort\")) activityMonitor.logComplete(activityId, 0);\n\t\telse activityMonitor.logError(activityId, redactedMessage);\n\t\tif (redactedMessage === message) throw err;\n\t\tconst redactedError = new Error(redactedMessage);\n\t\tif (err instanceof Error) redactedError.name = err.name;\n\t\tthrow redactedError;\n\t}\n\n\tif (!response.ok) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tconst errorText = redactCredential(await response.text(), apiKey);\n\t\tthrow new Error(`SERPdive API error ${response.status}: ${errorText.slice(0, 300)}`);\n\t}\n\n\tlet data: SerpdiveResponse;\n\ttry {\n\t\tdata = await response.json() as SerpdiveResponse;\n\t} catch (err) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tthrow new Error(`SERPdive API returned invalid JSON: ${errorMessage(err)}`);\n\t}\n\n\tactivityMonitor.logComplete(activityId, response.status);\n\tconst results = mapResults(data.results, numResults, filters);\n\tconst result: SearchResponse = {\n\t\tanswer: buildAnswer(data.answer, results),\n\t\tresults,\n\t};\n\tif (options.includeContent) {\n\t\tconst inlineContent = mapInlineContent(data.results, filters);\n\t\tif (inlineContent.length > 0) result.inlineContent = inlineContent;\n\t}\n\treturn result;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"serpdive.js","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/serpdive.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { activityMonitor } from \"./activity.ts\";\nimport type { ExtractedContent } from \"./extract.ts\";\nimport type { SearchOptions, SearchResponse } from \"./perplexity.ts\";\nimport { hasCredentialSource, redactCredential, resolveCredential } from \"./credential-source.ts\";\nimport { getWebSearchConfigPath } from \"./utils.ts\";\n\nconst SERPDIVE_API_URL = \"https://api.serpdive.com/v1/search\";\nconst CONFIG_PATH = getWebSearchConfigPath();\nconst SEARCH_TIMEOUT_MS = 60_000;\n\n// Retrieval depth. The default is deliberately the free tier: this provider\n// ships inside someone else's framework, and installing it must never start\n// spending on a user's behalf without them choosing it.\n// krill — free, fair use, no synthesized answer (see buildAnswer below)\n// mako — the fact-carrying sentences of each page, 1 credit\n// moby — the full readable content of every page, 1.5 credits\n// Pricing: https://serpdive.com/pricing\nconst MODELS = [\"krill\", \"mako\", \"moby\"] as const;\ntype SerpdiveModel = (typeof MODELS)[number];\nconst DEFAULT_MODEL: SerpdiveModel = \"krill\";\n\ninterface WebSearchConfig {\n\tserpdiveApiKey?: unknown;\n\tserpdiveModel?: unknown;\n}\n\ninterface SerpdiveResult {\n\turl?: string;\n\ttitle?: string | null;\n\tdate?: string;\n\tcontent?: string;\n}\n\ninterface SerpdiveResponse {\n\tquery?: string;\n\tmodel?: string;\n\tanswer?: string | null;\n\tresults?: SerpdiveResult[];\n}\n\ninterface SerpdiveSearchOptions extends SearchOptions {\n\tincludeContent?: boolean;\n}\n\nlet cachedConfig: WebSearchConfig | null = null;\n\nfunction loadConfig(): WebSearchConfig {\n\tif (cachedConfig) return cachedConfig;\n\tif (!existsSync(CONFIG_PATH)) {\n\t\tcachedConfig = {};\n\t\treturn cachedConfig;\n\t}\n\n\tconst raw = readFileSync(CONFIG_PATH, \"utf-8\");\n\ttry {\n\t\tcachedConfig = JSON.parse(raw) as WebSearchConfig;\n\t\treturn cachedConfig;\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tthrow new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);\n\t}\n}\n\nasync function getApiKey(signal?: AbortSignal): Promise<string | null> {\n\treturn resolveCredential({\n\t\tprovider: \"SERPdive\",\n\t\tconfiguredValue: loadConfig().serpdiveApiKey,\n\t\tenvironmentValue: process.env.SERPDIVE_API_KEY,\n\t\tsignal,\n\t});\n}\n\nasync function requireApiKey(signal?: AbortSignal): Promise<string> {\n\tconst apiKey = await getApiKey(signal);\n\tif (!apiKey) {\n\t\tthrow new Error(\n\t\t\t\"SERPdive API key not found. Either:\\n\" +\n\t\t\t` 1. Create ${CONFIG_PATH} with { \"serpdiveApiKey\": \"your-key\" }\\n` +\n\t\t\t\" 2. Set SERPDIVE_API_KEY environment variable\\n\" +\n\t\t\t\"Get a key at https://serpdive.com/dashboard/keys\",\n\t\t);\n\t}\n\treturn apiKey;\n}\n\n// An unknown value falls back to the free default rather than failing: a typo\n// in a config file must not cost the user money, and must not break search.\nfunction resolveModel(): SerpdiveModel {\n\tconst raw = process.env.SERPDIVE_MODEL ?? loadConfig().serpdiveModel;\n\tif (typeof raw !== \"string\") return DEFAULT_MODEL;\n\tconst value = raw.trim().toLowerCase();\n\treturn (MODELS as readonly string[]).includes(value) ? value as SerpdiveModel : DEFAULT_MODEL;\n}\n\nfunction normalizeCount(value: number | undefined): number {\n\tif (typeof value !== \"number\" || !Number.isFinite(value)) return 5;\n\treturn Math.max(1, Math.min(Math.floor(value), 20));\n}\n\nfunction normalizeDomain(value: string): string | null {\n\tlet input = value.trim().toLowerCase();\n\tif (!input) return null;\n\tif (input.startsWith(\"-\")) input = input.slice(1).trim();\n\tif (!input) return null;\n\ttry {\n\t\tconst parsed = input.includes(\"://\") ? new URL(input) : new URL(`https://${input}`);\n\t\tinput = parsed.hostname;\n\t} catch {\n\t\tinput = input.split(\"/\")[0]?.split(\":\")[0] ?? \"\";\n\t}\n\tinput = input.replace(/^\\.+|\\.+$/g, \"\");\n\treturn /^[a-z0-9][a-z0-9.-]*\\.[a-z]{2,}$/i.test(input) ? input : null;\n}\n\ninterface DomainFilters {\n\tinclude: string[];\n\texclude: string[];\n}\n\nfunction parseDomainFilter(domainFilter: string[] | undefined): DomainFilters {\n\tconst filters: DomainFilters = { include: [], exclude: [] };\n\tif (!domainFilter?.length) return filters;\n\tfor (const raw of domainFilter) {\n\t\tconst domain = normalizeDomain(raw);\n\t\tif (!domain) continue;\n\t\tconst target = raw.trim().startsWith(\"-\") ? filters.exclude : filters.include;\n\t\tif (!target.includes(domain)) target.push(domain);\n\t}\n\treturn filters;\n}\n\nfunction domainMatches(hostname: string, domain: string): boolean {\n\treturn hostname === domain || hostname.endsWith(`.${domain}`);\n}\n\n// SERPdive exposes no include/exclude domain parameter, so the filter is applied\n// here, on what came back. It can therefore only ever narrow a page of results —\n// it cannot ask the engine for more pages from a given domain.\nfunction passesDomainFilters(url: string, filters: DomainFilters): boolean {\n\tif (filters.include.length === 0 && filters.exclude.length === 0) return true;\n\tlet hostname: string;\n\ttry {\n\t\thostname = new URL(url).hostname.toLowerCase();\n\t} catch {\n\t\treturn false;\n\t}\n\tif (filters.exclude.some((domain) => domainMatches(hostname, domain))) return false;\n\tif (filters.include.length === 0) return true;\n\treturn filters.include.some((domain) => domainMatches(hostname, domain));\n}\n\n// SERPdive has NO time-range parameter. Recency is expressed inside the question\n// and read by the engine, which biases ranking toward recent pages — it is a\n// hint, never a guaranteed freshness filter, and results outside the window can\n// still come back.\nfunction applyRecencyHint(query: string, recencyFilter: SearchOptions[\"recencyFilter\"]): string {\n\tif (!recencyFilter) return query;\n\tconst hints: Record<string, string> = {\n\t\tday: \"past 24 hours\",\n\t\tweek: \"past week\",\n\t\tmonth: \"past month\",\n\t\tyear: \"past year\",\n\t};\n\tconst hint = hints[recencyFilter];\n\treturn hint ? `${query} ${hint}` : query;\n}\n\nfunction requestSignal(signal?: AbortSignal): AbortSignal {\n\tconst timeout = AbortSignal.timeout(SEARCH_TIMEOUT_MS);\n\treturn signal ? AbortSignal.any([signal, timeout]) : timeout;\n}\n\nfunction errorMessage(err: unknown): string {\n\treturn err instanceof Error ? err.message : String(err);\n}\n\nfunction mapResults(\n\tresults: SerpdiveResult[] | undefined,\n\tnumResults: number,\n\tfilters: DomainFilters,\n): SearchResponse[\"results\"] {\n\tif (!Array.isArray(results)) return [];\n\tconst mapped: SearchResponse[\"results\"] = [];\n\tfor (const item of results) {\n\t\tif (!item?.url || !passesDomainFilters(item.url, filters)) continue;\n\t\tmapped.push({\n\t\t\ttitle: item.title || `Source ${mapped.length + 1}`,\n\t\t\turl: item.url,\n\t\t\tsnippet: typeof item.content === \"string\" ? item.content.replace(/\\s+/g, \" \").trim() : \"\",\n\t\t});\n\t\tif (mapped.length >= numResults) break;\n\t}\n\treturn mapped;\n}\n\nfunction mapInlineContent(results: SerpdiveResult[] | undefined, filters: DomainFilters): ExtractedContent[] {\n\tif (!Array.isArray(results)) return [];\n\treturn results.flatMap((item) => {\n\t\tif (!item?.url || !passesDomainFilters(item.url, filters)) return [];\n\t\tif (typeof item.content !== \"string\" || item.content.trim().length === 0) return [];\n\t\treturn [{\n\t\t\turl: item.url,\n\t\t\ttitle: item.title || \"\",\n\t\t\tcontent: item.content,\n\t\t\terror: null,\n\t\t}];\n\t});\n}\n\n// The free krill tier returns extracted content but no synthesized answer, so\n// one is assembled from the sources — the same shape brave.ts and searxng.ts\n// produce for providers that do not synthesize. mako and moby ask the API for a\n// real answer and use it when it comes back.\nfunction buildAnswer(apiAnswer: string | null | undefined, results: SearchResponse[\"results\"]): string {\n\tif (typeof apiAnswer === \"string\" && apiAnswer.trim().length > 0) return apiAnswer;\n\treturn results\n\t\t.map((result) => {\n\t\t\tif (result.snippet) return `${result.snippet}\\nSource: ${result.title} (${result.url})`;\n\t\t\treturn `Source: ${result.title} (${result.url})`;\n\t\t})\n\t\t.join(\"\\n\\n\");\n}\n\nexport function isSerpdiveAvailable(): boolean {\n\treturn hasCredentialSource({\n\t\tprovider: \"SERPdive\",\n\t\tconfiguredValue: loadConfig().serpdiveApiKey,\n\t\tenvironmentValue: process.env.SERPDIVE_API_KEY,\n\t});\n}\n\nexport async function searchWithSerpdive(query: string, options: SerpdiveSearchOptions = {}): Promise<SearchResponse> {\n\tconst apiKey = await requireApiKey(options.signal);\n\tconst numResults = normalizeCount(options.numResults);\n\tconst filters = parseDomainFilter(options.domainFilter);\n\tconst model = resolveModel();\n\tconst body: Record<string, unknown> = {\n\t\tquery: applyRecencyHint(query, options.recencyFilter),\n\t\tmodel,\n\t\t// max_results is a CAP, never a minimum: the engine returns what it\n\t\t// judges relevant, up to this many. Asking for more does not produce more.\n\t\tmax_results: Math.min(numResults, 10),\n\t\t// krill has no answer synthesis — asking for one there is silently ignored\n\t\t// by the API, so it is not asked for at all.\n\t\t...(model === \"krill\" ? {} : { answer: true }),\n\t};\n\n\tconst activityId = activityMonitor.logStart({ type: \"api\", query });\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetch(SERPDIVE_API_URL, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"Authorization\": `Bearer ${apiKey}`,\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t},\n\t\t\tbody: JSON.stringify(body),\n\t\t\tsignal: requestSignal(options.signal),\n\t\t});\n\t} catch (err) {\n\t\tconst message = errorMessage(err);\n\t\tconst redactedMessage = redactCredential(message, apiKey);\n\t\tif (redactedMessage.toLowerCase().includes(\"abort\")) activityMonitor.logComplete(activityId, 0);\n\t\telse activityMonitor.logError(activityId, redactedMessage);\n\t\tif (redactedMessage === message) throw err;\n\t\tconst redactedError = new Error(redactedMessage);\n\t\tif (err instanceof Error) redactedError.name = err.name;\n\t\tthrow redactedError;\n\t}\n\n\tif (!response.ok) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tconst errorText = redactCredential(await response.text(), apiKey);\n\t\tthrow new Error(`SERPdive API error ${response.status}: ${errorText.slice(0, 300)}`);\n\t}\n\n\tlet data: SerpdiveResponse;\n\ttry {\n\t\tdata = await response.json() as SerpdiveResponse;\n\t} catch (err) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tthrow new Error(`SERPdive API returned invalid JSON: ${errorMessage(err)}`);\n\t}\n\n\tactivityMonitor.logComplete(activityId, response.status);\n\tconst results = mapResults(data.results, numResults, filters);\n\tconst result: SearchResponse = {\n\t\tanswer: buildAnswer(data.answer, results),\n\t\tresults,\n\t};\n\tif (options.includeContent) {\n\t\tconst inlineContent = mapInlineContent(data.results, filters);\n\t\tif (inlineContent.length > 0) result.inlineContent = inlineContent;\n\t}\n\treturn result;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serper.d.ts","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/serper.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAmJrE,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,cAAc,CAAC,CAyD1G"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { activityMonitor } from "./activity.js";
|
|
3
|
+
import { hasCredentialSource, redactCredential, resolveCredential } from "./credential-source.js";
|
|
4
|
+
import { getWebSearchConfigPath } from "./utils.js";
|
|
5
|
+
const SERPER_SEARCH_URL = "https://google.serper.dev/search";
|
|
6
|
+
const CONFIG_PATH = getWebSearchConfigPath();
|
|
7
|
+
const SEARCH_TIMEOUT_MS = 60_000;
|
|
8
|
+
const RECENCY_TBS = {
|
|
9
|
+
day: "qdr:d",
|
|
10
|
+
week: "qdr:w",
|
|
11
|
+
month: "qdr:m",
|
|
12
|
+
year: "qdr:y",
|
|
13
|
+
};
|
|
14
|
+
let cachedConfig = null;
|
|
15
|
+
function loadConfig() {
|
|
16
|
+
if (cachedConfig)
|
|
17
|
+
return cachedConfig;
|
|
18
|
+
if (!existsSync(CONFIG_PATH)) {
|
|
19
|
+
cachedConfig = {};
|
|
20
|
+
return cachedConfig;
|
|
21
|
+
}
|
|
22
|
+
const raw = readFileSync(CONFIG_PATH, "utf-8");
|
|
23
|
+
let parsed;
|
|
24
|
+
try {
|
|
25
|
+
parsed = JSON.parse(raw);
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
29
|
+
throw new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);
|
|
30
|
+
}
|
|
31
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
32
|
+
throw new Error(`Invalid config in ${CONFIG_PATH}: expected a JSON object`);
|
|
33
|
+
}
|
|
34
|
+
cachedConfig = parsed;
|
|
35
|
+
return cachedConfig;
|
|
36
|
+
}
|
|
37
|
+
async function getApiKey(signal) {
|
|
38
|
+
return resolveCredential({
|
|
39
|
+
provider: "Serper",
|
|
40
|
+
configuredValue: loadConfig().serperApiKey,
|
|
41
|
+
environmentValue: process.env.SERPER_API_KEY,
|
|
42
|
+
signal,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async function requireApiKey(signal) {
|
|
46
|
+
const apiKey = await getApiKey(signal);
|
|
47
|
+
if (!apiKey) {
|
|
48
|
+
throw new Error("Serper API key not found. Either:\n" +
|
|
49
|
+
` 1. Create ${CONFIG_PATH} with { "serperApiKey": "your-key" }\n` +
|
|
50
|
+
" 2. Set SERPER_API_KEY environment variable\n" +
|
|
51
|
+
"Get a key at https://serper.dev");
|
|
52
|
+
}
|
|
53
|
+
return apiKey;
|
|
54
|
+
}
|
|
55
|
+
function normalizeCount(value) {
|
|
56
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
57
|
+
return 5;
|
|
58
|
+
return Math.max(1, Math.min(Math.floor(value), 20));
|
|
59
|
+
}
|
|
60
|
+
function normalizeDomain(value) {
|
|
61
|
+
let input = value.trim().toLowerCase();
|
|
62
|
+
if (!input)
|
|
63
|
+
return null;
|
|
64
|
+
if (input.startsWith("-"))
|
|
65
|
+
input = input.slice(1).trim();
|
|
66
|
+
if (!input)
|
|
67
|
+
return null;
|
|
68
|
+
try {
|
|
69
|
+
const parsed = input.includes("://") ? new URL(input) : new URL(`https://${input}`);
|
|
70
|
+
input = parsed.hostname;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
input = input.split("/")[0]?.split(":")[0] ?? "";
|
|
74
|
+
}
|
|
75
|
+
input = input.replace(/^\.+|\.+$/g, "");
|
|
76
|
+
return /^[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/i.test(input) ? input : null;
|
|
77
|
+
}
|
|
78
|
+
function parseDomainFilter(domainFilter) {
|
|
79
|
+
const filters = { include: [], exclude: [] };
|
|
80
|
+
for (const raw of domainFilter ?? []) {
|
|
81
|
+
const domain = normalizeDomain(raw);
|
|
82
|
+
if (!domain)
|
|
83
|
+
continue;
|
|
84
|
+
const target = raw.trim().startsWith("-") ? filters.exclude : filters.include;
|
|
85
|
+
if (!target.includes(domain))
|
|
86
|
+
target.push(domain);
|
|
87
|
+
}
|
|
88
|
+
return filters;
|
|
89
|
+
}
|
|
90
|
+
function passesDomainFilters(url, filters) {
|
|
91
|
+
if (filters.include.length === 0 && filters.exclude.length === 0)
|
|
92
|
+
return true;
|
|
93
|
+
let hostname;
|
|
94
|
+
try {
|
|
95
|
+
hostname = new URL(url).hostname.toLowerCase();
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
const matches = (domain) => hostname === domain || hostname.endsWith(`.${domain}`);
|
|
101
|
+
if (filters.exclude.some(matches))
|
|
102
|
+
return false;
|
|
103
|
+
return filters.include.length === 0 || filters.include.some(matches);
|
|
104
|
+
}
|
|
105
|
+
function buildQuery(query, filters) {
|
|
106
|
+
const parts = [query];
|
|
107
|
+
if (filters.include.length === 1)
|
|
108
|
+
parts.push(`site:${filters.include[0]}`);
|
|
109
|
+
if (filters.include.length > 1)
|
|
110
|
+
parts.push(`(${filters.include.map(domain => `site:${domain}`).join(" OR ")})`);
|
|
111
|
+
for (const domain of filters.exclude)
|
|
112
|
+
parts.push(`-site:${domain}`);
|
|
113
|
+
return parts.join(" ");
|
|
114
|
+
}
|
|
115
|
+
function errorMessage(err) {
|
|
116
|
+
return err instanceof Error ? err.message : String(err);
|
|
117
|
+
}
|
|
118
|
+
function invalidResponse(message) {
|
|
119
|
+
return new Error(`Serper API returned invalid response: ${message}`);
|
|
120
|
+
}
|
|
121
|
+
function parseResponse(value) {
|
|
122
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
123
|
+
throw invalidResponse("expected an object envelope");
|
|
124
|
+
const envelope = value;
|
|
125
|
+
if (!Array.isArray(envelope.organic))
|
|
126
|
+
throw invalidResponse("expected organic array");
|
|
127
|
+
return envelope.organic;
|
|
128
|
+
}
|
|
129
|
+
function buildAnswer(results) {
|
|
130
|
+
return results.map((result) => result.snippet
|
|
131
|
+
? `${result.snippet}\nSource: ${result.title} (${result.url})`
|
|
132
|
+
: `Source: ${result.title} (${result.url})`).join("\n\n");
|
|
133
|
+
}
|
|
134
|
+
export function isSerperAvailable() {
|
|
135
|
+
return hasCredentialSource({ provider: "Serper", configuredValue: loadConfig().serperApiKey, environmentValue: process.env.SERPER_API_KEY });
|
|
136
|
+
}
|
|
137
|
+
export async function searchWithSerper(query, options = {}) {
|
|
138
|
+
const apiKey = await requireApiKey(options.signal);
|
|
139
|
+
const numResults = normalizeCount(options.numResults);
|
|
140
|
+
const filters = parseDomainFilter(options.domainFilter);
|
|
141
|
+
const requestCount = options.domainFilter?.length ? Math.min(20, numResults + 5) : numResults;
|
|
142
|
+
const activityId = activityMonitor.logStart({ type: "api", query });
|
|
143
|
+
let response;
|
|
144
|
+
try {
|
|
145
|
+
response = await fetch(SERPER_SEARCH_URL, {
|
|
146
|
+
method: "POST",
|
|
147
|
+
headers: { "X-API-KEY": apiKey, "Content-Type": "application/json", Accept: "application/json" },
|
|
148
|
+
body: JSON.stringify({ q: buildQuery(query, filters), num: requestCount, ...(options.recencyFilter ? { tbs: RECENCY_TBS[options.recencyFilter] } : {}) }),
|
|
149
|
+
signal: options.signal ? AbortSignal.any([AbortSignal.timeout(SEARCH_TIMEOUT_MS), options.signal]) : AbortSignal.timeout(SEARCH_TIMEOUT_MS),
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
const message = errorMessage(err);
|
|
154
|
+
const redactedMessage = redactCredential(message, apiKey);
|
|
155
|
+
if (redactedMessage.toLowerCase().includes("abort"))
|
|
156
|
+
activityMonitor.logComplete(activityId, 0);
|
|
157
|
+
else
|
|
158
|
+
activityMonitor.logError(activityId, redactedMessage);
|
|
159
|
+
if (redactedMessage === message)
|
|
160
|
+
throw err;
|
|
161
|
+
const redactedError = new Error(redactedMessage);
|
|
162
|
+
if (err instanceof Error)
|
|
163
|
+
redactedError.name = err.name;
|
|
164
|
+
throw redactedError;
|
|
165
|
+
}
|
|
166
|
+
if (!response.ok) {
|
|
167
|
+
activityMonitor.logComplete(activityId, response.status);
|
|
168
|
+
const errorText = redactCredential(await response.text(), apiKey);
|
|
169
|
+
throw new Error(`Serper API error ${response.status}: ${errorText.slice(0, 300)}`);
|
|
170
|
+
}
|
|
171
|
+
let rawData;
|
|
172
|
+
try {
|
|
173
|
+
rawData = await response.json();
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
activityMonitor.logComplete(activityId, response.status);
|
|
177
|
+
throw new Error(`Serper API returned invalid JSON: ${errorMessage(err)}`);
|
|
178
|
+
}
|
|
179
|
+
let entries;
|
|
180
|
+
try {
|
|
181
|
+
entries = parseResponse(rawData);
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
activityMonitor.logComplete(activityId, response.status);
|
|
185
|
+
throw err;
|
|
186
|
+
}
|
|
187
|
+
activityMonitor.logComplete(activityId, response.status);
|
|
188
|
+
const results = [];
|
|
189
|
+
for (const entry of entries) {
|
|
190
|
+
if (!entry || typeof entry !== "object")
|
|
191
|
+
continue;
|
|
192
|
+
if (typeof entry.link !== "string" || !entry.link)
|
|
193
|
+
continue;
|
|
194
|
+
if (!passesDomainFilters(entry.link, filters))
|
|
195
|
+
continue;
|
|
196
|
+
results.push({
|
|
197
|
+
title: typeof entry.title === "string" && entry.title.trim() ? entry.title.trim() : `Source ${results.length + 1}`,
|
|
198
|
+
url: entry.link,
|
|
199
|
+
snippet: typeof entry.snippet === "string" ? entry.snippet : "",
|
|
200
|
+
});
|
|
201
|
+
if (results.length >= numResults)
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
return { answer: buildAnswer(results), results };
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=serper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serper.js","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/serper.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { activityMonitor } from \"./activity.ts\";\nimport type { SearchOptions, SearchResponse } from \"./perplexity.ts\";\nimport { hasCredentialSource, redactCredential, resolveCredential } from \"./credential-source.ts\";\nimport { getWebSearchConfigPath } from \"./utils.ts\";\n\nconst SERPER_SEARCH_URL = \"https://google.serper.dev/search\";\nconst CONFIG_PATH = getWebSearchConfigPath();\nconst SEARCH_TIMEOUT_MS = 60_000;\nconst RECENCY_TBS: Record<NonNullable<SearchOptions[\"recencyFilter\"]>, string> = {\n\tday: \"qdr:d\",\n\tweek: \"qdr:w\",\n\tmonth: \"qdr:m\",\n\tyear: \"qdr:y\",\n};\n\ninterface WebSearchConfig {\n\tserperApiKey?: unknown;\n}\n\ninterface SerperResult {\n\ttitle?: unknown;\n\tlink?: unknown;\n\tsnippet?: unknown;\n}\n\nlet cachedConfig: WebSearchConfig | null = null;\n\nfunction loadConfig(): WebSearchConfig {\n\tif (cachedConfig) return cachedConfig;\n\tif (!existsSync(CONFIG_PATH)) {\n\t\tcachedConfig = {};\n\t\treturn cachedConfig;\n\t}\n\tconst raw = readFileSync(CONFIG_PATH, \"utf-8\");\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tthrow new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);\n\t}\n\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n\t\tthrow new Error(`Invalid config in ${CONFIG_PATH}: expected a JSON object`);\n\t}\n\tcachedConfig = parsed as WebSearchConfig;\n\treturn cachedConfig;\n}\n\nasync function getApiKey(signal?: AbortSignal): Promise<string | null> {\n\treturn resolveCredential({\n\t\tprovider: \"Serper\",\n\t\tconfiguredValue: loadConfig().serperApiKey,\n\t\tenvironmentValue: process.env.SERPER_API_KEY,\n\t\tsignal,\n\t});\n}\n\nasync function requireApiKey(signal?: AbortSignal): Promise<string> {\n\tconst apiKey = await getApiKey(signal);\n\tif (!apiKey) {\n\t\tthrow new Error(\n\t\t\t\"Serper API key not found. Either:\\n\" +\n\t\t\t` 1. Create ${CONFIG_PATH} with { \"serperApiKey\": \"your-key\" }\\n` +\n\t\t\t\" 2. Set SERPER_API_KEY environment variable\\n\" +\n\t\t\t\"Get a key at https://serper.dev\",\n\t\t);\n\t}\n\treturn apiKey;\n}\n\nfunction normalizeCount(value: number | undefined): number {\n\tif (typeof value !== \"number\" || !Number.isFinite(value)) return 5;\n\treturn Math.max(1, Math.min(Math.floor(value), 20));\n}\n\ninterface DomainFilters {\n\tinclude: string[];\n\texclude: string[];\n}\n\nfunction normalizeDomain(value: string): string | null {\n\tlet input = value.trim().toLowerCase();\n\tif (!input) return null;\n\tif (input.startsWith(\"-\")) input = input.slice(1).trim();\n\tif (!input) return null;\n\ttry {\n\t\tconst parsed = input.includes(\"://\") ? new URL(input) : new URL(`https://${input}`);\n\t\tinput = parsed.hostname;\n\t} catch {\n\t\tinput = input.split(\"/\")[0]?.split(\":\")[0] ?? \"\";\n\t}\n\tinput = input.replace(/^\\.+|\\.+$/g, \"\");\n\treturn /^[a-z0-9][a-z0-9.-]*\\.[a-z]{2,}$/i.test(input) ? input : null;\n}\n\nfunction parseDomainFilter(domainFilter: string[] | undefined): DomainFilters {\n\tconst filters: DomainFilters = { include: [], exclude: [] };\n\tfor (const raw of domainFilter ?? []) {\n\t\tconst domain = normalizeDomain(raw);\n\t\tif (!domain) continue;\n\t\tconst target = raw.trim().startsWith(\"-\") ? filters.exclude : filters.include;\n\t\tif (!target.includes(domain)) target.push(domain);\n\t}\n\treturn filters;\n}\n\nfunction passesDomainFilters(url: string, filters: DomainFilters): boolean {\n\tif (filters.include.length === 0 && filters.exclude.length === 0) return true;\n\tlet hostname: string;\n\ttry {\n\t\thostname = new URL(url).hostname.toLowerCase();\n\t} catch {\n\t\treturn false;\n\t}\n\tconst matches = (domain: string) => hostname === domain || hostname.endsWith(`.${domain}`);\n\tif (filters.exclude.some(matches)) return false;\n\treturn filters.include.length === 0 || filters.include.some(matches);\n}\n\nfunction buildQuery(query: string, filters: DomainFilters): string {\n\tconst parts = [query];\n\tif (filters.include.length === 1) parts.push(`site:${filters.include[0]}`);\n\tif (filters.include.length > 1) parts.push(`(${filters.include.map(domain => `site:${domain}`).join(\" OR \")})`);\n\tfor (const domain of filters.exclude) parts.push(`-site:${domain}`);\n\treturn parts.join(\" \");\n}\n\nfunction errorMessage(err: unknown): string {\n\treturn err instanceof Error ? err.message : String(err);\n}\n\nfunction invalidResponse(message: string): Error {\n\treturn new Error(`Serper API returned invalid response: ${message}`);\n}\n\nfunction parseResponse(value: unknown): SerperResult[] {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) throw invalidResponse(\"expected an object envelope\");\n\tconst envelope = value as Record<string, unknown>;\n\tif (!Array.isArray(envelope.organic)) throw invalidResponse(\"expected organic array\");\n\treturn envelope.organic as SerperResult[];\n}\n\nfunction buildAnswer(results: SearchResponse[\"results\"]): string {\n\treturn results.map((result) => result.snippet\n\t\t? `${result.snippet}\\nSource: ${result.title} (${result.url})`\n\t\t: `Source: ${result.title} (${result.url})`).join(\"\\n\\n\");\n}\n\nexport function isSerperAvailable(): boolean {\n\treturn hasCredentialSource({ provider: \"Serper\", configuredValue: loadConfig().serperApiKey, environmentValue: process.env.SERPER_API_KEY });\n}\n\nexport async function searchWithSerper(query: string, options: SearchOptions = {}): Promise<SearchResponse> {\n\tconst apiKey = await requireApiKey(options.signal);\n\tconst numResults = normalizeCount(options.numResults);\n\tconst filters = parseDomainFilter(options.domainFilter);\n\tconst requestCount = options.domainFilter?.length ? Math.min(20, numResults + 5) : numResults;\n\tconst activityId = activityMonitor.logStart({ type: \"api\", query });\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetch(SERPER_SEARCH_URL, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { \"X-API-KEY\": apiKey, \"Content-Type\": \"application/json\", Accept: \"application/json\" },\n\t\t\tbody: JSON.stringify({ q: buildQuery(query, filters), num: requestCount, ...(options.recencyFilter ? { tbs: RECENCY_TBS[options.recencyFilter] } : {}) }),\n\t\t\tsignal: options.signal ? AbortSignal.any([AbortSignal.timeout(SEARCH_TIMEOUT_MS), options.signal]) : AbortSignal.timeout(SEARCH_TIMEOUT_MS),\n\t\t});\n\t} catch (err) {\n\t\tconst message = errorMessage(err);\n\t\tconst redactedMessage = redactCredential(message, apiKey);\n\t\tif (redactedMessage.toLowerCase().includes(\"abort\")) activityMonitor.logComplete(activityId, 0);\n\t\telse activityMonitor.logError(activityId, redactedMessage);\n\t\tif (redactedMessage === message) throw err;\n\t\tconst redactedError = new Error(redactedMessage);\n\t\tif (err instanceof Error) redactedError.name = err.name;\n\t\tthrow redactedError;\n\t}\n\tif (!response.ok) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tconst errorText = redactCredential(await response.text(), apiKey);\n\t\tthrow new Error(`Serper API error ${response.status}: ${errorText.slice(0, 300)}`);\n\t}\n\tlet rawData: unknown;\n\ttry {\n\t\trawData = await response.json();\n\t} catch (err) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tthrow new Error(`Serper API returned invalid JSON: ${errorMessage(err)}`);\n\t}\n\tlet entries: SerperResult[];\n\ttry {\n\t\tentries = parseResponse(rawData);\n\t} catch (err) {\n\t\tactivityMonitor.logComplete(activityId, response.status);\n\t\tthrow err;\n\t}\n\tactivityMonitor.logComplete(activityId, response.status);\n\tconst results: SearchResponse[\"results\"] = [];\n\tfor (const entry of entries) {\n\t\tif (!entry || typeof entry !== \"object\") continue;\n\t\tif (typeof entry.link !== \"string\" || !entry.link) continue;\n\t\tif (!passesDomainFilters(entry.link, filters)) continue;\n\t\tresults.push({\n\t\t\ttitle: typeof entry.title === \"string\" && entry.title.trim() ? entry.title.trim() : `Source ${results.length + 1}`,\n\t\t\turl: entry.link,\n\t\t\tsnippet: typeof entry.snippet === \"string\" ? entry.snippet : \"\",\n\t\t});\n\t\tif (results.length >= numResults) break;\n\t}\n\treturn { answer: buildAnswer(results), results };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source-check.d.ts","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/source-check.ts"],"names":[],"mappings":"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","sourcesContent":["// Structured source checking and machine-readable research artifacts.\nimport { createHash } from \"node:crypto\";\nimport { generateId, getResult, storeResult } from \"./storage.ts\";\nimport type { SearchResult } from \"./perplexity.ts\";\nimport type { ExtractedContent } from \"./extract.ts\";\n\nexport type SourceQuality = \"official_docs\" | \"vendor_docs\" | \"repo_issue\" | \"blog\" | \"forum\" | \"news\" | \"unknown\";\nexport type ClaimStatus = \"supported\" | \"contradicted\" | \"unclear\" | \"missing-evidence\";\nexport type RecencyFilter = \"day\" | \"week\" | \"month\" | \"year\";\n\nexport interface ResearchSource {\n\trank: number;\n\turl: string;\n\ttitle: string;\n\tsnippet?: string;\n\tfetch_timestamp?: number;\n\tcontent_hash?: string;\n\tquality: SourceQuality;\n\tfetched?: boolean;\n\tfetch_error?: string;\n}\n\nexport interface ResearchPassage {\n\tpassage_id: string;\n\tsource_url: string;\n\tsource_rank: number;\n\ttext: string;\n\textraction_span?: { start: number; end: number };\n\tcontent_hash?: string;\n}\n\nexport interface ClaimAssessment {\n\tclaim: string;\n\tstatus: ClaimStatus;\n\tsupporting_passages: string[];\n\tcontradicting_passages: string[];\n\trationale: string;\n\tconfidence: number;\n}\n\nexport interface ResearchArtifact {\n\tid: string;\n\ttype: \"research\";\n\ttimestamp: number;\n\tquery: string;\n\tsources: ResearchSource[];\n\tpassages: ResearchPassage[];\n\tclaims?: ClaimAssessment[];\n\tprovider?: string;\n\tsummary?: string;\n\tcontent_hash?: string;\n\tfilters?: { recency?: RecencyFilter; domain_include?: string[]; domain_exclude?: string[] };\n\terrors?: Array<{ query: string; error: string }>;\n}\n\nexport interface ResearchSearchRequest {\n\tquery: string;\n\tnumResults: number;\n\trecencyFilter?: RecencyFilter;\n\tdomainFilter?: string[];\n}\nexport interface ResearchSearchResult { url: string; title: string; snippet: string; rank: number }\nexport interface ResearchSearchResponse { provider: string; results: ResearchSearchResult[]; summary?: string }\nexport interface ResearchProvider { name: string; search(req: ResearchSearchRequest): Promise<ResearchSearchResponse> }\n\nconst OFFICIAL_DOCS_HOSTS = /^(developers\\.|docs\\.|learn\\.|reference\\.)|\\.github\\.io$/i;\nconst OFFICIAL_DOCS_PATHS = /\\/(docs?|reference)(\\/|\\b)/i;\nconst VENDOR_DOCS_PATHS = /\\/(documentation|docs?)\\//i;\nconst REPO_ISSUE_PATHS = /\\/(issues|pull|pulls)\\//i;\nconst BLOG_HOSTS = /(medium\\.com|substack\\.com|dev\\.to|hashnode\\.)/i;\nconst BLOG_PATHS = /\\/blogs?\\//i;\nconst FORUM_HOSTS = /(stackoverflow\\.com|serverfault\\.com|superuser\\.com|discourse\\.|community\\.)/i;\nconst FORUM_PATHS = /\\/(forum|forums|threads)\\//i;\nconst NEWS_HOSTS = /(reuters\\.com|bloomberg\\.com|techcrunch\\.com|theverge\\.com|arstechnica\\.com|wired\\.com|cnet\\.com|zdnet\\.com)/i;\nconst NEWS_PATHS = /\\/news(\\/|$)/i;\n\nexport function classifySource(url: string): SourceQuality {\n\tlet host = \"\";\n\tlet path = \"\";\n\ttry {\n\t\tconst parsed = new URL(url);\n\t\thost = parsed.hostname;\n\t\tpath = parsed.pathname;\n\t} catch {\n\t\treturn \"unknown\";\n\t}\n\tif (REPO_ISSUE_PATHS.test(path)) return \"repo_issue\";\n\tif (OFFICIAL_DOCS_HOSTS.test(host) || OFFICIAL_DOCS_PATHS.test(path)) return \"official_docs\";\n\tif (VENDOR_DOCS_PATHS.test(path)) return \"vendor_docs\";\n\tif (NEWS_HOSTS.test(host) || NEWS_PATHS.test(path)) return \"news\";\n\tif (FORUM_HOSTS.test(host) || FORUM_PATHS.test(path)) return \"forum\";\n\tif (BLOG_HOSTS.test(host) || BLOG_PATHS.test(path)) return \"blog\";\n\treturn \"unknown\";\n}\n\nexport function hashContent(text: string): string {\n\treturn `sha256:${createHash(\"sha256\").update(text, \"utf8\").digest(\"hex\")}`;\n}\n\ninterface Span { text: string; start: number; end: number }\n\nfunction tokenize(value: string): string[] {\n\treturn [...new Set(value.toLowerCase().split(/[^a-z0-9]+/).filter((term) => term.length > 3))];\n}\n\nfunction extractRelevantSpans(content: string, hint: string): Span[] {\n\tconst sentences: Span[] = [];\n\tconst sentencePattern = /[^.!?]+(?:[.!?]+(?=\\s|$)|$)/g;\n\tfor (const match of content.matchAll(sentencePattern)) {\n\t\tconst raw = match[0];\n\t\tconst text = raw.trim();\n\t\tif (text.length > 0 && text.length <= 400) {\n\t\t\tconst start = (match.index ?? 0) + raw.indexOf(text);\n\t\t\tsentences.push({ text, start, end: start + text.length });\n\t\t}\n\t}\n\tconst terms = tokenize(hint);\n\tif (terms.length === 0) return [];\n\treturn sentences\n\t\t.map((sentence, index) => ({ sentence, index, score: terms.filter((term) => sentence.text.toLowerCase().includes(term)).length }))\n\t\t.filter((item) => item.score > 0)\n\t\t.sort((a, b) => b.score - a.score || a.index - b.index)\n\t\t.slice(0, 3)\n\t\t.map(({ sentence }) => sentence);\n}\n\nfunction passageId(sourceRank: number, index: number): string {\n\treturn `p-${sourceRank}-${index}`;\n}\n\nexport function buildPassages(sources: ResearchSource[], fetched: ExtractedContent[] = [], hint = \"\"): ResearchPassage[] {\n\tconst passages: ResearchPassage[] = [];\n\tconst fetchedByUrl = new Map(fetched.map((item) => [item.url, item]));\n\tfor (const source of sources) {\n\t\tif (source.snippet) {\n\t\t\tpassages.push({\n\t\t\t\tpassage_id: passageId(source.rank, 0),\n\t\t\t\tsource_url: source.url,\n\t\t\t\tsource_rank: source.rank,\n\t\t\t\ttext: source.snippet,\n\t\t\t\tcontent_hash: hashContent(source.snippet),\n\t\t\t});\n\t\t}\n\t\tconst page = fetchedByUrl.get(source.url);\n\t\tif (page && !page.error && page.content) {\n\t\t\tconst passageHint = source.snippet?.trim() || hint;\n\t\t\tfor (const [index, span] of extractRelevantSpans(page.content, passageHint).entries()) {\n\t\t\t\tpassages.push({\n\t\t\t\t\tpassage_id: passageId(source.rank, index + 1),\n\t\t\t\t\tsource_url: source.url,\n\t\t\t\t\tsource_rank: source.rank,\n\t\t\t\t\ttext: span.text,\n\t\t\t\t\textraction_span: { start: span.start, end: span.end },\n\t\t\t\t\tcontent_hash: hashContent(span.text),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\treturn passages;\n}\n\nconst CONTRADICTION_MARKERS = [\"not true\", \"false\", \"incorrect\", \"debunked\", \"retracted\", \"no longer\", \"never\", \"denied\", \"contrary\", \"misleading\"];\nconst SUPPORT_MARKERS = [\"yes\", \"true\", \"correct\", \"confirmed\", \"according to\", \"shows that\", \"demonstrates\", \"reported\", \"verified\", \"established\"];\n\nfunction containsPhrase(value: string, phrase: string): boolean {\n\tconst escaped = phrase.trim().toLowerCase().split(/\\s+/).map((term) => term.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")).join(\"\\\\s+\");\n\treturn new RegExp(`(?:^|[^a-z0-9])${escaped}(?=$|[^a-z0-9])`, \"i\").test(value);\n}\n\nfunction markerIsNegated(value: string, marker: string): boolean {\n\tconst escaped = marker.trim().toLowerCase().split(/\\s+/).map((term) => term.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")).join(\"\\\\s+\");\n\tconst markerPattern = new RegExp(`(?:^|[^a-z0-9])${escaped}(?=$|[^a-z0-9])`, \"i\");\n\tconst match = markerPattern.exec(value);\n\tif (!match || match.index === undefined) return false;\n\tconst matchedMarker = match[0].replace(/^[^a-z0-9]+/i, \"\");\n\tconst beforeMarker = value.slice(0, match.index + match[0].length - matchedMarker.length);\n\treturn /(?:^|[^a-z0-9])(?:not|no|never|without)\\s+$/i.test(beforeMarker);\n}\n\nfunction hasPolarityMarker(value: string, markers: string[], allowNegated = false): boolean {\n\treturn markers.some((marker) => containsPhrase(value, marker) && (allowNegated || !markerIsNegated(value, marker)));\n}\n\nexport function assessClaim(claim: string, passages: ResearchPassage[]): ClaimAssessment {\n\tconst terms = tokenize(claim);\n\tif (terms.length === 0 || passages.length === 0) {\n\t\treturn { claim, status: \"missing-evidence\", supporting_passages: [], contradicting_passages: [], rationale: \"No passages available that discuss the claim's terms.\", confidence: 0.2 };\n\t}\n\tconst supporting: string[] = [];\n\tconst contradicting: string[] = [];\n\tfor (const passage of passages) {\n\t\tconst lower = passage.text.toLowerCase();\n\t\tconst overlap = terms.filter((term) => containsPhrase(lower, term)).length;\n\t\tif (overlap < Math.max(2, Math.ceil(terms.length / 4))) continue;\n\t\tconst contra = hasPolarityMarker(lower, CONTRADICTION_MARKERS);\n\t\tconst support = hasPolarityMarker(lower, SUPPORT_MARKERS);\n\t\tif (contra && !support) contradicting.push(passage.passage_id);\n\t\telse if (support && !contra) supporting.push(passage.passage_id);\n\t}\n\tif (contradicting.length > 0 && supporting.length === 0) {\n\t\treturn { claim, status: \"contradicted\", supporting_passages: [], contradicting_passages: contradicting, rationale: `${contradicting.length} passage(s) contradict the claim; none support it.`, confidence: Math.min(0.85, 0.5 + contradicting.length * 0.1) };\n\t}\n\tif (supporting.length > 0 && contradicting.length === 0) {\n\t\treturn { claim, status: \"supported\", supporting_passages: supporting, contradicting_passages: [], rationale: `${supporting.length} passage(s) support the claim; none contradict it.`, confidence: Math.min(0.85, 0.5 + supporting.length * 0.1) };\n\t}\n\tif (supporting.length > 0 || contradicting.length > 0) {\n\t\treturn { claim, status: \"unclear\", supporting_passages: supporting, contradicting_passages: contradicting, rationale: `${supporting.length} supporting and ${contradicting.length} contradicting passage(s); evidence is mixed.`, confidence: 0.4 };\n\t}\n\treturn { claim, status: \"unclear\", supporting_passages: [], contradicting_passages: [], rationale: \"Passages mention the claim's terms but contain no clear support or contradiction markers.\", confidence: 0.3 };\n}\n\ninterface RankedSearchResult extends SearchResult {\n\trank?: number;\n}\n\nexport interface BuildArtifactInput {\n\tquery: string;\n\tprovider?: string;\n\tsummary?: string;\n\tresults: RankedSearchResult[];\n\tfetched?: ExtractedContent[];\n\trecency?: RecencyFilter;\n\tdomainFilter?: string[];\n}\n\nexport function buildResearchArtifact(input: BuildArtifactInput): ResearchArtifact {\n\tconst filters = input.domainFilter ?? [];\n\tconst fetchedByUrl = new Map((input.fetched ?? []).map((page) => [page.url, page]));\n\tconst sources: ResearchSource[] = [];\n\tconst seen = new Set<string>();\n\tfor (const [index, result] of input.results.entries()) {\n\t\tif (seen.has(result.url)) continue;\n\t\tseen.add(result.url);\n\t\tconst page = fetchedByUrl.get(result.url);\n\t\tconst fetched = Boolean(page && !page.error);\n\t\tsources.push({\n\t\t\trank: result.rank ?? index + 1,\n\t\t\turl: result.url,\n\t\t\ttitle: result.title,\n\t\t\tsnippet: result.snippet,\n\t\t\tquality: classifySource(result.url),\n\t\t\tfetched,\n\t\t\t...(page ? { fetch_timestamp: Date.now() } : {}),\n\t\t\t...(page && !page.error ? { content_hash: hashContent(page.content) } : {}),\n\t\t\t...(page?.error ? { fetch_error: page.error } : {}),\n\t\t});\n\t}\n\tconst passages = buildPassages(sources, input.fetched, input.query);\n\tconst domainInclude = filters.filter((domain) => !domain.startsWith(\"-\"));\n\tconst domainExclude = filters.filter((domain) => domain.startsWith(\"-\")).map((domain) => domain.slice(1));\n\treturn {\n\t\tid: generateId(),\n\t\ttype: \"research\",\n\t\ttimestamp: Date.now(),\n\t\tquery: input.query,\n\t\tsources,\n\t\tpassages,\n\t\t...(input.provider !== undefined ? { provider: input.provider } : {}),\n\t\t...(input.summary !== undefined ? { summary: input.summary } : {}),\n\t\t...(passages.length > 0 ? { content_hash: hashContent(passages.map((passage) => passage.text).join(\"\\n\")) } : {}),\n\t\tfilters: {\n\t\t\t...(input.recency !== undefined ? { recency: input.recency } : {}),\n\t\t\tdomain_include: domainInclude,\n\t\t\tdomain_exclude: domainExclude,\n\t\t},\n\t};\n}\n\nexport function withClaimAssessment(artifact: ResearchArtifact, claims: string[]): ResearchArtifact {\n\treturn { ...artifact, claims: claims.map((claim) => assessClaim(claim, artifact.passages)) };\n}\n\nexport function storeResearchArtifact(artifact: ResearchArtifact): void {\n\tif (!artifact.id) throw new Error(\"Research artifact id must not be empty\");\n\tstoreResult(artifact.id, { id: artifact.id, type: \"research\", timestamp: artifact.timestamp, artifact });\n}\n\nexport function getResearchArtifact(id: string): ResearchArtifact | null {\n\tconst data = getResult(id);\n\tif (!data || data.type !== \"research\" || !data.artifact || typeof data.artifact !== \"object\") return null;\n\treturn data.artifact as ResearchArtifact;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"source-check.d.ts","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/source-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source-check.js","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/source-check.ts"],"names":[],"mappings":"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","sourcesContent":["// Structured source checking and machine-readable research artifacts.\nimport { createHash } from \"node:crypto\";\nimport { generateId, getResult, storeResult } from \"./storage.ts\";\nimport type { SearchResult } from \"./perplexity.ts\";\nimport type { ExtractedContent } from \"./extract.ts\";\n\nexport type SourceQuality = \"official_docs\" | \"vendor_docs\" | \"repo_issue\" | \"blog\" | \"forum\" | \"news\" | \"unknown\";\nexport type ClaimStatus = \"supported\" | \"contradicted\" | \"unclear\" | \"missing-evidence\";\nexport type RecencyFilter = \"day\" | \"week\" | \"month\" | \"year\";\n\nexport interface ResearchSource {\n\trank: number;\n\turl: string;\n\ttitle: string;\n\tsnippet?: string;\n\tfetch_timestamp?: number;\n\tcontent_hash?: string;\n\tquality: SourceQuality;\n\tfetched?: boolean;\n\tfetch_error?: string;\n}\n\nexport interface ResearchPassage {\n\tpassage_id: string;\n\tsource_url: string;\n\tsource_rank: number;\n\ttext: string;\n\textraction_span?: { start: number; end: number };\n\tcontent_hash?: string;\n}\n\nexport interface ClaimAssessment {\n\tclaim: string;\n\tstatus: ClaimStatus;\n\tsupporting_passages: string[];\n\tcontradicting_passages: string[];\n\trationale: string;\n\tconfidence: number;\n}\n\nexport interface ResearchArtifact {\n\tid: string;\n\ttype: \"research\";\n\ttimestamp: number;\n\tquery: string;\n\tsources: ResearchSource[];\n\tpassages: ResearchPassage[];\n\tclaims?: ClaimAssessment[];\n\tprovider?: string;\n\tsummary?: string;\n\tcontent_hash?: string;\n\tfilters?: { recency?: RecencyFilter; domain_include?: string[]; domain_exclude?: string[] };\n\terrors?: Array<{ query: string; error: string }>;\n}\n\nexport interface ResearchSearchRequest {\n\tquery: string;\n\tnumResults: number;\n\trecencyFilter?: RecencyFilter;\n\tdomainFilter?: string[];\n}\nexport interface ResearchSearchResult { url: string; title: string; snippet: string; rank: number }\nexport interface ResearchSearchResponse { provider: string; results: ResearchSearchResult[]; summary?: string }\nexport interface ResearchProvider { name: string; search(req: ResearchSearchRequest): Promise<ResearchSearchResponse> }\n\nconst OFFICIAL_DOCS_HOSTS = /^(developers\\.|docs\\.|learn\\.|reference\\.)|\\.github\\.io$/i;\nconst OFFICIAL_DOCS_PATHS = /\\/(docs?|reference)(\\/|\\b)/i;\nconst VENDOR_DOCS_PATHS = /\\/(documentation|docs?)\\//i;\nconst REPO_ISSUE_PATHS = /\\/(issues|pull|pulls)\\//i;\nconst BLOG_HOSTS = /(medium\\.com|substack\\.com|dev\\.to|hashnode\\.)/i;\nconst BLOG_PATHS = /\\/blogs?\\//i;\nconst FORUM_HOSTS = /(stackoverflow\\.com|serverfault\\.com|superuser\\.com|discourse\\.|community\\.)/i;\nconst FORUM_PATHS = /\\/(forum|forums|threads)\\//i;\nconst NEWS_HOSTS = /(reuters\\.com|bloomberg\\.com|techcrunch\\.com|theverge\\.com|arstechnica\\.com|wired\\.com|cnet\\.com|zdnet\\.com)/i;\nconst NEWS_PATHS = /\\/news(\\/|$)/i;\n\nexport function classifySource(url: string): SourceQuality {\n\tlet host = \"\";\n\tlet path = \"\";\n\ttry {\n\t\tconst parsed = new URL(url);\n\t\thost = parsed.hostname;\n\t\tpath = parsed.pathname;\n\t} catch {\n\t\treturn \"unknown\";\n\t}\n\tif (REPO_ISSUE_PATHS.test(path)) return \"repo_issue\";\n\tif (OFFICIAL_DOCS_HOSTS.test(host) || OFFICIAL_DOCS_PATHS.test(path)) return \"official_docs\";\n\tif (VENDOR_DOCS_PATHS.test(path)) return \"vendor_docs\";\n\tif (NEWS_HOSTS.test(host) || NEWS_PATHS.test(path)) return \"news\";\n\tif (FORUM_HOSTS.test(host) || FORUM_PATHS.test(path)) return \"forum\";\n\tif (BLOG_HOSTS.test(host) || BLOG_PATHS.test(path)) return \"blog\";\n\treturn \"unknown\";\n}\n\nexport function hashContent(text: string): string {\n\treturn `sha256:${createHash(\"sha256\").update(text, \"utf8\").digest(\"hex\")}`;\n}\n\ninterface Span { text: string; start: number; end: number }\n\nfunction tokenize(value: string): string[] {\n\treturn [...new Set(value.toLowerCase().split(/[^a-z0-9]+/).filter((term) => term.length > 3))];\n}\n\nfunction extractRelevantSpans(content: string, hint: string): Span[] {\n\tconst sentences: Span[] = [];\n\tconst sentencePattern = /[^.!?]+(?:[.!?]+(?=\\s|$)|$)/g;\n\tfor (const match of content.matchAll(sentencePattern)) {\n\t\tconst raw = match[0];\n\t\tconst text = raw.trim();\n\t\tif (text.length > 0 && text.length <= 400) {\n\t\t\tconst start = (match.index ?? 0) + raw.indexOf(text);\n\t\t\tsentences.push({ text, start, end: start + text.length });\n\t\t}\n\t}\n\tconst terms = tokenize(hint);\n\tif (terms.length === 0) return [];\n\treturn sentences\n\t\t.map((sentence, index) => ({ sentence, index, score: terms.filter((term) => sentence.text.toLowerCase().includes(term)).length }))\n\t\t.filter((item) => item.score > 0)\n\t\t.sort((a, b) => b.score - a.score || a.index - b.index)\n\t\t.slice(0, 3)\n\t\t.map(({ sentence }) => sentence);\n}\n\nfunction passageId(sourceRank: number, index: number): string {\n\treturn `p-${sourceRank}-${index}`;\n}\n\nexport function buildPassages(sources: ResearchSource[], fetched: ExtractedContent[] = [], hint = \"\"): ResearchPassage[] {\n\tconst passages: ResearchPassage[] = [];\n\tconst fetchedByUrl = new Map(fetched.map((item) => [item.url, item]));\n\tfor (const source of sources) {\n\t\tif (source.snippet) {\n\t\t\tpassages.push({\n\t\t\t\tpassage_id: passageId(source.rank, 0),\n\t\t\t\tsource_url: source.url,\n\t\t\t\tsource_rank: source.rank,\n\t\t\t\ttext: source.snippet,\n\t\t\t\tcontent_hash: hashContent(source.snippet),\n\t\t\t});\n\t\t}\n\t\tconst page = fetchedByUrl.get(source.url);\n\t\tif (page && !page.error && page.content) {\n\t\t\tconst passageHint = source.snippet?.trim() || hint;\n\t\t\tfor (const [index, span] of extractRelevantSpans(page.content, passageHint).entries()) {\n\t\t\t\tpassages.push({\n\t\t\t\t\tpassage_id: passageId(source.rank, index + 1),\n\t\t\t\t\tsource_url: source.url,\n\t\t\t\t\tsource_rank: source.rank,\n\t\t\t\t\ttext: span.text,\n\t\t\t\t\textraction_span: { start: span.start, end: span.end },\n\t\t\t\t\tcontent_hash: hashContent(span.text),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\treturn passages;\n}\n\nconst CONTRADICTION_MARKERS = [\"not true\", \"false\", \"incorrect\", \"debunked\", \"retracted\", \"no longer\", \"never\", \"denied\", \"contrary\", \"misleading\"];\nconst SUPPORT_MARKERS = [\"yes\", \"true\", \"correct\", \"confirmed\", \"according to\", \"shows that\", \"demonstrates\", \"reported\", \"verified\", \"established\"];\n\nfunction containsPhrase(value: string, phrase: string): boolean {\n\tconst escaped = phrase.trim().toLowerCase().split(/\\s+/).map((term) => term.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")).join(\"\\\\s+\");\n\treturn new RegExp(`(?:^|[^a-z0-9])${escaped}(?=$|[^a-z0-9])`, \"i\").test(value);\n}\n\nfunction markerIsNegated(value: string, marker: string): boolean {\n\tconst escaped = marker.trim().toLowerCase().split(/\\s+/).map((term) => term.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")).join(\"\\\\s+\");\n\tconst markerPattern = new RegExp(`(?:^|[^a-z0-9])${escaped}(?=$|[^a-z0-9])`, \"i\");\n\tconst match = markerPattern.exec(value);\n\tif (!match || match.index === undefined) return false;\n\tconst matchedMarker = match[0].replace(/^[^a-z0-9]+/i, \"\");\n\tconst beforeMarker = value.slice(0, match.index + match[0].length - matchedMarker.length);\n\treturn /(?:^|[^a-z0-9])(?:not|no|never|without)\\s+$/i.test(beforeMarker);\n}\n\nfunction hasPolarityMarker(value: string, markers: string[], allowNegated = false): boolean {\n\treturn markers.some((marker) => containsPhrase(value, marker) && (allowNegated || !markerIsNegated(value, marker)));\n}\n\nexport function assessClaim(claim: string, passages: ResearchPassage[]): ClaimAssessment {\n\tconst terms = tokenize(claim);\n\tif (terms.length === 0 || passages.length === 0) {\n\t\treturn { claim, status: \"missing-evidence\", supporting_passages: [], contradicting_passages: [], rationale: \"No passages available that discuss the claim's terms.\", confidence: 0.2 };\n\t}\n\tconst supporting: string[] = [];\n\tconst contradicting: string[] = [];\n\tfor (const passage of passages) {\n\t\tconst lower = passage.text.toLowerCase();\n\t\tconst overlap = terms.filter((term) => containsPhrase(lower, term)).length;\n\t\tif (overlap < Math.max(2, Math.ceil(terms.length / 4))) continue;\n\t\tconst contra = hasPolarityMarker(lower, CONTRADICTION_MARKERS);\n\t\tconst support = hasPolarityMarker(lower, SUPPORT_MARKERS);\n\t\tif (contra && !support) contradicting.push(passage.passage_id);\n\t\telse if (support && !contra) supporting.push(passage.passage_id);\n\t}\n\tif (contradicting.length > 0 && supporting.length === 0) {\n\t\treturn { claim, status: \"contradicted\", supporting_passages: [], contradicting_passages: contradicting, rationale: `${contradicting.length} passage(s) contradict the claim; none support it.`, confidence: Math.min(0.85, 0.5 + contradicting.length * 0.1) };\n\t}\n\tif (supporting.length > 0 && contradicting.length === 0) {\n\t\treturn { claim, status: \"supported\", supporting_passages: supporting, contradicting_passages: [], rationale: `${supporting.length} passage(s) support the claim; none contradict it.`, confidence: Math.min(0.85, 0.5 + supporting.length * 0.1) };\n\t}\n\tif (supporting.length > 0 || contradicting.length > 0) {\n\t\treturn { claim, status: \"unclear\", supporting_passages: supporting, contradicting_passages: contradicting, rationale: `${supporting.length} supporting and ${contradicting.length} contradicting passage(s); evidence is mixed.`, confidence: 0.4 };\n\t}\n\treturn { claim, status: \"unclear\", supporting_passages: [], contradicting_passages: [], rationale: \"Passages mention the claim's terms but contain no clear support or contradiction markers.\", confidence: 0.3 };\n}\n\ninterface RankedSearchResult extends SearchResult {\n\trank?: number;\n}\n\nexport interface BuildArtifactInput {\n\tquery: string;\n\tprovider?: string;\n\tsummary?: string;\n\tresults: RankedSearchResult[];\n\tfetched?: ExtractedContent[];\n\trecency?: RecencyFilter;\n\tdomainFilter?: string[];\n}\n\nexport function buildResearchArtifact(input: BuildArtifactInput): ResearchArtifact {\n\tconst filters = input.domainFilter ?? [];\n\tconst fetchedByUrl = new Map((input.fetched ?? []).map((page) => [page.url, page]));\n\tconst sources: ResearchSource[] = [];\n\tconst seen = new Set<string>();\n\tfor (const [index, result] of input.results.entries()) {\n\t\tif (seen.has(result.url)) continue;\n\t\tseen.add(result.url);\n\t\tconst page = fetchedByUrl.get(result.url);\n\t\tconst fetched = Boolean(page && !page.error);\n\t\tsources.push({\n\t\t\trank: result.rank ?? index + 1,\n\t\t\turl: result.url,\n\t\t\ttitle: result.title,\n\t\t\tsnippet: result.snippet,\n\t\t\tquality: classifySource(result.url),\n\t\t\tfetched,\n\t\t\t...(page ? { fetch_timestamp: Date.now() } : {}),\n\t\t\t...(page && !page.error ? { content_hash: hashContent(page.content) } : {}),\n\t\t\t...(page?.error ? { fetch_error: page.error } : {}),\n\t\t});\n\t}\n\tconst passages = buildPassages(sources, input.fetched, input.query);\n\tconst domainInclude = filters.filter((domain) => !domain.startsWith(\"-\"));\n\tconst domainExclude = filters.filter((domain) => domain.startsWith(\"-\")).map((domain) => domain.slice(1));\n\treturn {\n\t\tid: generateId(),\n\t\ttype: \"research\",\n\t\ttimestamp: Date.now(),\n\t\tquery: input.query,\n\t\tsources,\n\t\tpassages,\n\t\t...(input.provider !== undefined ? { provider: input.provider } : {}),\n\t\t...(input.summary !== undefined ? { summary: input.summary } : {}),\n\t\t...(passages.length > 0 ? { content_hash: hashContent(passages.map((passage) => passage.text).join(\"\\n\")) } : {}),\n\t\tfilters: {\n\t\t\t...(input.recency !== undefined ? { recency: input.recency } : {}),\n\t\t\tdomain_include: domainInclude,\n\t\t\tdomain_exclude: domainExclude,\n\t\t},\n\t};\n}\n\nexport function withClaimAssessment(artifact: ResearchArtifact, claims: string[]): ResearchArtifact {\n\treturn { ...artifact, claims: claims.map((claim) => assessClaim(claim, artifact.passages)) };\n}\n\nexport function storeResearchArtifact(artifact: ResearchArtifact): void {\n\tif (!artifact.id) throw new Error(\"Research artifact id must not be empty\");\n\tstoreResult(artifact.id, { id: artifact.id, type: \"research\", timestamp: artifact.timestamp, artifact });\n}\n\nexport function getResearchArtifact(id: string): ResearchArtifact | null {\n\tconst data = getResult(id);\n\tif (!data || data.type !== \"research\" || !data.artifact || typeof data.artifact !== \"object\") return null;\n\treturn data.artifact as ResearchArtifact;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"source-check.js","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/source-check.ts"],"names":[],"mappings":"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","sourcesContent":["// Structured source checking and machine-readable research artifacts.\nimport { createHash } from \"node:crypto\";\nimport { generateId, getResult, storeResult } from \"./storage.ts\";\nimport type { SearchResult } from \"./perplexity.ts\";\nimport type { ExtractedContent } from \"./extract.ts\";\n\nexport type SourceQuality = \"official_docs\" | \"vendor_docs\" | \"repo_issue\" | \"blog\" | \"forum\" | \"news\" | \"unknown\";\nexport type ClaimStatus = \"supported\" | \"contradicted\" | \"unclear\" | \"missing-evidence\";\nexport type RecencyFilter = \"day\" | \"week\" | \"month\" | \"year\";\n\nexport interface ResearchSource {\n\trank: number;\n\turl: string;\n\ttitle: string;\n\tsnippet?: string;\n\tfetch_timestamp?: number;\n\tcontent_hash?: string;\n\tquality: SourceQuality;\n\tfetched?: boolean;\n\tfetch_error?: string;\n}\n\nexport interface ResearchPassage {\n\tpassage_id: string;\n\tsource_url: string;\n\tsource_rank: number;\n\ttext: string;\n\textraction_span?: { start: number; end: number };\n\tcontent_hash?: string;\n}\n\nexport interface ClaimAssessment {\n\tclaim: string;\n\tstatus: ClaimStatus;\n\tsupporting_passages: string[];\n\tcontradicting_passages: string[];\n\trationale: string;\n\tconfidence: number;\n}\n\nexport interface ResearchArtifact {\n\tid: string;\n\ttype: \"research\";\n\ttimestamp: number;\n\tquery: string;\n\tsources: ResearchSource[];\n\tpassages: ResearchPassage[];\n\tclaims?: ClaimAssessment[];\n\tprovider?: string;\n\tsummary?: string;\n\tcontent_hash?: string;\n\tfilters?: { recency?: RecencyFilter; domain_include?: string[]; domain_exclude?: string[] };\n\terrors?: Array<{ query: string; error: string }>;\n}\n\nexport interface ResearchSearchRequest {\n\tquery: string;\n\tnumResults: number;\n\trecencyFilter?: RecencyFilter;\n\tdomainFilter?: string[];\n}\nexport interface ResearchSearchResult { url: string; title: string; snippet: string; rank: number }\nexport interface ResearchSearchResponse { provider: string; results: ResearchSearchResult[]; summary?: string }\nexport interface ResearchProvider { name: string; search(req: ResearchSearchRequest): Promise<ResearchSearchResponse> }\n\nconst OFFICIAL_DOCS_HOSTS = /^(developers\\.|docs\\.|learn\\.|reference\\.)|\\.github\\.io$/i;\nconst OFFICIAL_DOCS_PATHS = /\\/(docs?|reference)(\\/|\\b)/i;\nconst VENDOR_DOCS_PATHS = /\\/(documentation|docs?)\\//i;\nconst REPO_ISSUE_PATHS = /\\/(issues|pull|pulls)\\//i;\nconst BLOG_HOSTS = /(medium\\.com|substack\\.com|dev\\.to|hashnode\\.)/i;\nconst BLOG_PATHS = /\\/blogs?\\//i;\nconst FORUM_HOSTS = /(stackoverflow\\.com|serverfault\\.com|superuser\\.com|discourse\\.|community\\.)/i;\nconst FORUM_PATHS = /\\/(forum|forums|threads)\\//i;\nconst NEWS_HOSTS = /(reuters\\.com|bloomberg\\.com|techcrunch\\.com|theverge\\.com|arstechnica\\.com|wired\\.com|cnet\\.com|zdnet\\.com)/i;\nconst NEWS_PATHS = /\\/news(\\/|$)/i;\n\nexport function classifySource(url: string): SourceQuality {\n\tlet host = \"\";\n\tlet path = \"\";\n\ttry {\n\t\tconst parsed = new URL(url);\n\t\thost = parsed.hostname;\n\t\tpath = parsed.pathname;\n\t} catch {\n\t\treturn \"unknown\";\n\t}\n\tif (REPO_ISSUE_PATHS.test(path)) return \"repo_issue\";\n\tif (OFFICIAL_DOCS_HOSTS.test(host) || OFFICIAL_DOCS_PATHS.test(path)) return \"official_docs\";\n\tif (VENDOR_DOCS_PATHS.test(path)) return \"vendor_docs\";\n\tif (NEWS_HOSTS.test(host) || NEWS_PATHS.test(path)) return \"news\";\n\tif (FORUM_HOSTS.test(host) || FORUM_PATHS.test(path)) return \"forum\";\n\tif (BLOG_HOSTS.test(host) || BLOG_PATHS.test(path)) return \"blog\";\n\treturn \"unknown\";\n}\n\nexport function hashContent(text: string): string {\n\treturn `sha256:${createHash(\"sha256\").update(text, \"utf8\").digest(\"hex\")}`;\n}\n\ninterface Span { text: string; start: number; end: number }\n\nfunction tokenize(value: string): string[] {\n\treturn [...new Set(value.toLowerCase().split(/[^a-z0-9]+/).filter((term) => term.length > 3))];\n}\n\nfunction extractRelevantSpans(content: string, hint: string): Span[] {\n\tconst sentences: Span[] = [];\n\tconst sentencePattern = /[^.!?]+(?:[.!?]+(?=\\s|$)|$)/g;\n\tfor (const match of content.matchAll(sentencePattern)) {\n\t\tconst raw = match[0];\n\t\tconst text = raw.trim();\n\t\tif (text.length > 0 && text.length <= 400) {\n\t\t\tconst start = (match.index ?? 0) + raw.indexOf(text);\n\t\t\tsentences.push({ text, start, end: start + text.length });\n\t\t}\n\t}\n\tconst terms = tokenize(hint);\n\tif (terms.length === 0) return [];\n\treturn sentences\n\t\t.map((sentence, index) => ({ sentence, index, score: terms.filter((term) => sentence.text.toLowerCase().includes(term)).length }))\n\t\t.filter((item) => item.score > 0)\n\t\t.sort((a, b) => b.score - a.score || a.index - b.index)\n\t\t.slice(0, 3)\n\t\t.map(({ sentence }) => sentence);\n}\n\nfunction passageId(sourceRank: number, index: number): string {\n\treturn `p-${sourceRank}-${index}`;\n}\n\nexport function buildPassages(sources: ResearchSource[], fetched: ExtractedContent[] = [], hint = \"\"): ResearchPassage[] {\n\tconst passages: ResearchPassage[] = [];\n\tconst fetchedByUrl = new Map(fetched.map((item) => [item.url, item]));\n\tfor (const source of sources) {\n\t\tif (source.snippet) {\n\t\t\tpassages.push({\n\t\t\t\tpassage_id: passageId(source.rank, 0),\n\t\t\t\tsource_url: source.url,\n\t\t\t\tsource_rank: source.rank,\n\t\t\t\ttext: source.snippet,\n\t\t\t\tcontent_hash: hashContent(source.snippet),\n\t\t\t});\n\t\t}\n\t\tconst page = fetchedByUrl.get(source.url);\n\t\tif (page && !page.error && page.content) {\n\t\t\tconst passageHint = source.snippet?.trim() || hint;\n\t\t\tfor (const [index, span] of extractRelevantSpans(page.content, passageHint).entries()) {\n\t\t\t\tpassages.push({\n\t\t\t\t\tpassage_id: passageId(source.rank, index + 1),\n\t\t\t\t\tsource_url: source.url,\n\t\t\t\t\tsource_rank: source.rank,\n\t\t\t\t\ttext: span.text,\n\t\t\t\t\textraction_span: { start: span.start, end: span.end },\n\t\t\t\t\tcontent_hash: hashContent(span.text),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\treturn passages;\n}\n\nconst CONTRADICTION_MARKERS = [\"not true\", \"false\", \"incorrect\", \"debunked\", \"retracted\", \"no longer\", \"never\", \"denied\", \"contrary\", \"misleading\"];\nconst SUPPORT_MARKERS = [\"yes\", \"true\", \"correct\", \"confirmed\", \"according to\", \"shows that\", \"demonstrates\", \"reported\", \"verified\", \"established\"];\n\nfunction containsPhrase(value: string, phrase: string): boolean {\n\tconst escaped = phrase.trim().toLowerCase().split(/\\s+/).map((term) => term.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")).join(\"\\\\s+\");\n\treturn new RegExp(`(?:^|[^a-z0-9])${escaped}(?=$|[^a-z0-9])`, \"i\").test(value);\n}\n\nfunction markerIsNegated(value: string, marker: string): boolean {\n\tconst escaped = marker.trim().toLowerCase().split(/\\s+/).map((term) => term.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")).join(\"\\\\s+\");\n\tconst markerPattern = new RegExp(`(?:^|[^a-z0-9])${escaped}(?=$|[^a-z0-9])`, \"i\");\n\tconst match = markerPattern.exec(value);\n\tif (!match || match.index === undefined) return false;\n\tconst matchedMarker = match[0].replace(/^[^a-z0-9]+/i, \"\");\n\tconst beforeMarker = value.slice(0, match.index + match[0].length - matchedMarker.length);\n\treturn /(?:^|[^a-z0-9])(?:not|no|never|without)\\s+$/i.test(beforeMarker);\n}\n\nfunction hasPolarityMarker(value: string, markers: string[], allowNegated = false): boolean {\n\treturn markers.some((marker) => containsPhrase(value, marker) && (allowNegated || !markerIsNegated(value, marker)));\n}\n\nexport function assessClaim(claim: string, passages: ResearchPassage[]): ClaimAssessment {\n\tconst terms = tokenize(claim);\n\tif (terms.length === 0 || passages.length === 0) {\n\t\treturn { claim, status: \"missing-evidence\", supporting_passages: [], contradicting_passages: [], rationale: \"No passages available that discuss the claim's terms.\", confidence: 0.2 };\n\t}\n\tconst supporting: string[] = [];\n\tconst contradicting: string[] = [];\n\tfor (const passage of passages) {\n\t\tconst lower = passage.text.toLowerCase();\n\t\tconst overlap = terms.filter((term) => containsPhrase(lower, term)).length;\n\t\tif (overlap < Math.max(2, Math.ceil(terms.length / 4))) continue;\n\t\tconst contra = hasPolarityMarker(lower, CONTRADICTION_MARKERS);\n\t\tconst support = hasPolarityMarker(lower, SUPPORT_MARKERS);\n\t\tif (contra && !support) contradicting.push(passage.passage_id);\n\t\telse if (support && !contra) supporting.push(passage.passage_id);\n\t}\n\tif (contradicting.length > 0 && supporting.length === 0) {\n\t\treturn { claim, status: \"contradicted\", supporting_passages: [], contradicting_passages: contradicting, rationale: `${contradicting.length} passage(s) contradict the claim; none support it.`, confidence: Math.min(0.85, 0.5 + contradicting.length * 0.1) };\n\t}\n\tif (supporting.length > 0 && contradicting.length === 0) {\n\t\treturn { claim, status: \"supported\", supporting_passages: supporting, contradicting_passages: [], rationale: `${supporting.length} passage(s) support the claim; none contradict it.`, confidence: Math.min(0.85, 0.5 + supporting.length * 0.1) };\n\t}\n\tif (supporting.length > 0 || contradicting.length > 0) {\n\t\treturn { claim, status: \"unclear\", supporting_passages: supporting, contradicting_passages: contradicting, rationale: `${supporting.length} supporting and ${contradicting.length} contradicting passage(s); evidence is mixed.`, confidence: 0.4 };\n\t}\n\treturn { claim, status: \"unclear\", supporting_passages: [], contradicting_passages: [], rationale: \"Passages mention the claim's terms but contain no clear support or contradiction markers.\", confidence: 0.3 };\n}\n\ninterface RankedSearchResult extends SearchResult {\n\trank?: number;\n}\n\nexport interface BuildArtifactInput {\n\tquery: string;\n\tprovider?: string;\n\tsummary?: string;\n\tresults: RankedSearchResult[];\n\tfetched?: ExtractedContent[];\n\trecency?: RecencyFilter;\n\tdomainFilter?: string[];\n}\n\nexport function buildResearchArtifact(input: BuildArtifactInput): ResearchArtifact {\n\tconst filters = input.domainFilter ?? [];\n\tconst fetchedByUrl = new Map((input.fetched ?? []).map((page) => [page.url, page]));\n\tconst sources: ResearchSource[] = [];\n\tconst seen = new Set<string>();\n\tfor (const [index, result] of input.results.entries()) {\n\t\tif (seen.has(result.url)) continue;\n\t\tseen.add(result.url);\n\t\tconst page = fetchedByUrl.get(result.url);\n\t\tconst fetched = Boolean(page && !page.error);\n\t\tsources.push({\n\t\t\trank: result.rank ?? index + 1,\n\t\t\turl: result.url,\n\t\t\ttitle: result.title,\n\t\t\tsnippet: result.snippet,\n\t\t\tquality: classifySource(result.url),\n\t\t\tfetched,\n\t\t\t...(page ? { fetch_timestamp: Date.now() } : {}),\n\t\t\t...(page && !page.error ? { content_hash: hashContent(page.content) } : {}),\n\t\t\t...(page?.error ? { fetch_error: page.error } : {}),\n\t\t});\n\t}\n\tconst passages = buildPassages(sources, input.fetched, input.query);\n\tconst domainInclude = filters.filter((domain) => !domain.startsWith(\"-\"));\n\tconst domainExclude = filters.filter((domain) => domain.startsWith(\"-\")).map((domain) => domain.slice(1));\n\treturn {\n\t\tid: generateId(),\n\t\ttype: \"research\",\n\t\ttimestamp: Date.now(),\n\t\tquery: input.query,\n\t\tsources,\n\t\tpassages,\n\t\t...(input.provider !== undefined ? { provider: input.provider } : {}),\n\t\t...(input.summary !== undefined ? { summary: input.summary } : {}),\n\t\t...(passages.length > 0 ? { content_hash: hashContent(passages.map((passage) => passage.text).join(\"\\n\")) } : {}),\n\t\tfilters: {\n\t\t\t...(input.recency !== undefined ? { recency: input.recency } : {}),\n\t\t\tdomain_include: domainInclude,\n\t\t\tdomain_exclude: domainExclude,\n\t\t},\n\t};\n}\n\nexport function withClaimAssessment(artifact: ResearchArtifact, claims: string[]): ResearchArtifact {\n\treturn { ...artifact, claims: claims.map((claim) => assessClaim(claim, artifact.passages)) };\n}\n\nexport function storeResearchArtifact(artifact: ResearchArtifact): void {\n\tif (!artifact.id) throw new Error(\"Research artifact id must not be empty\");\n\tstoreResult(artifact.id, { id: artifact.id, type: \"research\", timestamp: artifact.timestamp, artifact });\n}\n\nexport function getResearchArtifact(id: string): ResearchArtifact | null {\n\tconst data = getResult(id);\n\tif (!data || data.type !== \"research\" || !data.artifact || typeof data.artifact !== \"object\") return null;\n\treturn data.artifact as ResearchArtifact;\n}\n"]}
|
|
@@ -32,6 +32,8 @@ interface ValidationOptions {
|
|
|
32
32
|
* the local SSRF preflight. This does not configure proxy transport.
|
|
33
33
|
*/
|
|
34
34
|
trustEnvProxy?: boolean;
|
|
35
|
+
/** Allow loopback URLs for explicit provider base endpoints, not fetched targets. */
|
|
36
|
+
allowLoopback?: boolean;
|
|
35
37
|
}
|
|
36
38
|
interface RedirectRequestInitArgs {
|
|
37
39
|
from: URL;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssrf-protection.d.ts","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/ssrf-protection.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,aAAa,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAChE,MAAM,MAAM,MAAM,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;AACpE,KAAK,KAAK,GAAG,OAAO,KAAK,CAAC;AA2C1B,MAAM,WAAW,UAAU;IAC1B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC5B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;CACf;AAID,wBAAgB,4BAA4B,IAAI,YAAY,CAkB3D;AA2BD,wBAAgB,cAAc,IAAI,UAAU,CAwB3C;AAED,UAAU,iBAAiB;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACxB;AAQD,UAAU,uBAAuB;IAChC,IAAI,EAAE,GAAG,CAAC;IACV,EAAE,EAAE,GAAG,CAAC;IACR,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;CACnB;AAED,UAAU,kBAAmB,SAAQ,iBAAiB;IACrD,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,uBAAuB,KAAK,WAAW,CAAC;CAC5D;AAMD,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,GAAG,CAAC,CAmC3G;AAED,wBAAsB,cAAc,CACnC,GAAG,EAAE,MAAM,GAAG,GAAG,EACjB,IAAI,GAAE,WAAgB,EACtB,OAAO,GAAE,kBAAuB,GAC9B,OAAO,CAAC,QAAQ,CAAC,CAwBnB","sourcesContent":["import { lookup as dnsLookup } from \"node:dns/promises\";\nimport { existsSync, readFileSync, statSync } from \"node:fs\";\nimport net from \"node:net\";\nimport { getWebSearchConfigPath } from \"./utils.ts\";\n\nconst DEFAULT_MAX_REDIRECTS = 5;\nconst REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);\n\nexport type LookupAddress = { address: string; family: number };\nexport type Lookup = (hostname: string) => Promise<LookupAddress[]>;\ntype Fetch = typeof fetch;\n\nconst WEB_SEARCH_CONFIG_PATH = getWebSearchConfigPath();\n\nlet cachedConfigRoot: { signature: string; value: Record<string, unknown> | null } | null = null;\n\nfunction loadConfigRoot(): Record<string, unknown> | null {\n\tif (!existsSync(WEB_SEARCH_CONFIG_PATH)) return null;\n\n\tlet signature: string;\n\ttry {\n\t\tconst stat = statSync(WEB_SEARCH_CONFIG_PATH);\n\t\tsignature = `${stat.mtimeMs}:${stat.size}`;\n\t} catch {\n\t\treturn null;\n\t}\n\n\tif (cachedConfigRoot?.signature === signature) return cachedConfigRoot.value;\n\n\tlet raw: string;\n\ttry {\n\t\traw = readFileSync(WEB_SEARCH_CONFIG_PATH, \"utf-8\");\n\t} catch {\n\t\t// Do not memoize read failures: a chmod fix changes neither mtime nor size,\n\t\t// so a cached failure would permanently fail-open the domain policy.\n\t\treturn null;\n\t}\n\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tthrow new Error(`Failed to parse ${WEB_SEARCH_CONFIG_PATH}: ${message}`);\n\t}\n\n\tconst value = parsed && typeof parsed === \"object\" && !Array.isArray(parsed)\n\t\t? parsed as Record<string, unknown>\n\t\t: null;\n\tcachedConfigRoot = { signature, value };\n\treturn value;\n}\n\nexport interface SsrfConfig {\n\tallowRanges: string[];\n\ttrustEnvProxy: boolean;\n}\n\nexport interface DomainPolicy {\n\tallow: string[];\n\tdeny: string[];\n}\n\nconst DEFAULT_DOMAIN_POLICY: DomainPolicy = { allow: [], deny: [] };\n\nexport function loadFetchContentDomainPolicy(): DomainPolicy {\n\tconst parsed = loadConfigRoot();\n\tif (!parsed) return { ...DEFAULT_DOMAIN_POLICY };\n\tconst fetchContent = parsed.fetchContent;\n\tif (fetchContent === undefined || fetchContent === null) return { ...DEFAULT_DOMAIN_POLICY };\n\tif (typeof fetchContent !== \"object\" || Array.isArray(fetchContent)) {\n\t\tthrow new Error(`fetchContent in ${WEB_SEARCH_CONFIG_PATH} must be an object`);\n\t}\n\tconst policy = (fetchContent as { domainPolicy?: unknown }).domainPolicy;\n\tif (policy === undefined || policy === null) return { ...DEFAULT_DOMAIN_POLICY };\n\tif (typeof policy !== \"object\" || Array.isArray(policy)) {\n\t\tthrow new Error(`fetchContent.domainPolicy in ${WEB_SEARCH_CONFIG_PATH} must be an object`);\n\t}\n\tconst config = policy as { allow?: unknown; deny?: unknown };\n\treturn {\n\t\tallow: parseDomainEntries(config.allow, \"allow\"),\n\t\tdeny: parseDomainEntries(config.deny, \"deny\"),\n\t};\n}\n\nfunction parseDomainEntries(value: unknown, field: \"allow\" | \"deny\"): string[] {\n\tif (value === undefined || value === null) return [];\n\tif (!Array.isArray(value)) {\n\t\tthrow new Error(`fetchContent.domainPolicy.${field} in ${WEB_SEARCH_CONFIG_PATH} must be an array of hostnames`);\n\t}\n\treturn value.map((entry, index) => {\n\t\tif (typeof entry !== \"string\") {\n\t\t\tthrow new Error(`fetchContent.domainPolicy.${field} in ${WEB_SEARCH_CONFIG_PATH} must contain only hostnames; entry ${index + 1} is ${typeof entry}`);\n\t\t}\n\t\tconst hostname = normalizeDomainEntry(entry);\n\t\tif (!hostname) {\n\t\t\tthrow new Error(`fetchContent.domainPolicy.${field} in ${WEB_SEARCH_CONFIG_PATH} contains an invalid hostname: ${JSON.stringify(entry)}`);\n\t\t}\n\t\treturn hostname;\n\t});\n}\n\nfunction normalizeDomainEntry(entry: string): string | null {\n\tconst hostname = normalizeHostname(entry.trim());\n\tif (!hostname || /\\s|[\\\\/?:#@]/.test(hostname)) return null;\n\tif (net.isIP(hostname)) return hostname;\n\tif (hostname.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(hostname)) return null;\n\treturn hostname;\n}\n\nexport function loadSsrfConfig(): SsrfConfig {\n\tconst parsed = loadConfigRoot();\n\tif (!parsed) return { allowRanges: [], trustEnvProxy: false };\n\tconst ssrf = parsed.ssrf;\n\tif (ssrf === undefined || ssrf === null) return { allowRanges: [], trustEnvProxy: false };\n\tif (typeof ssrf !== \"object\" || Array.isArray(ssrf)) {\n\t\tthrow new Error(`ssrf in ${WEB_SEARCH_CONFIG_PATH} must be an object`);\n\t}\n\tconst config = ssrf as { allowRanges?: unknown; trustEnvProxy?: unknown };\n\tif (config.allowRanges !== undefined && config.allowRanges !== null && !Array.isArray(config.allowRanges)) {\n\t\tthrow new Error(`ssrf.allowRanges in ${WEB_SEARCH_CONFIG_PATH} must be an array of CIDR strings`);\n\t}\n\tif (config.trustEnvProxy !== undefined && typeof config.trustEnvProxy !== \"boolean\") {\n\t\tthrow new Error(`ssrf.trustEnvProxy in ${WEB_SEARCH_CONFIG_PATH} must be a boolean`);\n\t}\n\tconst allowRangesValue: unknown[] = Array.isArray(config.allowRanges) ? config.allowRanges : [];\n\tconst allowRanges = allowRangesValue.map((entry, index) => {\n\t\tif (typeof entry !== \"string\") {\n\t\t\tthrow new Error(`ssrf.allowRanges in ${WEB_SEARCH_CONFIG_PATH} must contain only CIDR strings; entry ${index + 1} is ${typeof entry}`);\n\t\t}\n\t\treturn entry.trim();\n\t}).filter(Boolean);\n\tparseAllowRanges(allowRanges);\n\treturn { allowRanges, trustEnvProxy: config.trustEnvProxy === true };\n}\n\ninterface ValidationOptions {\n\tlookup?: Lookup;\n\t/** Optional hostname policy for fetch_content target URLs. */\n\tdomainPolicy?: DomainPolicy;\n\t/**\n\t * CIDR ranges (e.g. \"198.18.0.0/15\") to exempt from the SSRF guard.\n\t * Useful when a host runs a TUN/fake-IP proxy (Surge, Clash, Mihomo, ...)\n\t * that resolves public domains into a reserved range. Entries are validated\n\t * strictly; an invalid entry throws so misconfiguration is not silent.\n\t */\n\tallowRanges?: string[];\n\t/**\n\t * When true, trust an explicitly-configured HTTP(S) proxy for hostname\n\t * resolution instead of performing local DNS lookups inside the sandbox.\n\t * Literal IPs and localhost remain blocked, and NO_PROXY hosts still use\n\t * the local SSRF preflight. This does not configure proxy transport.\n\t */\n\ttrustEnvProxy?: boolean;\n}\n\n/** Parsed entry from `allowRanges`: a network address (4 or 16 bytes) + prefix length. */\ninterface ParsedCidr {\n\tbytes: Uint8Array;\n\tprefix: number;\n}\n\ninterface RedirectRequestInitArgs {\n\tfrom: URL;\n\tto: URL;\n\tinit: RequestInit;\n\tresponse: Response;\n}\n\ninterface FetchRemoteOptions extends ValidationOptions {\n\tfetch?: Fetch;\n\tmaxRedirects?: number;\n\tonRedirect?: (args: RedirectRequestInitArgs) => RequestInit;\n}\n\nasync function defaultLookup(hostname: string): Promise<LookupAddress[]> {\n\treturn dnsLookup(hostname, { all: true, verbatim: true });\n}\n\nexport async function validateRemoteUrl(rawUrl: string | URL, options: ValidationOptions = {}): Promise<URL> {\n\tconst url = rawUrl instanceof URL ? rawUrl : new URL(rawUrl);\n\tif (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n\t\tthrow new Error(\"Only HTTP and HTTPS URLs can be fetched remotely\");\n\t}\n\n\tconst hostname = normalizeHostname(url.hostname);\n\tif (!hostname) throw new Error(\"URL must include a hostname\");\n\tif (hostname === \"localhost\" || hostname.endsWith(\".localhost\")) {\n\t\tthrow new Error(`Blocked internal hostname: ${hostname}`);\n\t}\n\n\tconst allowRanges = parseAllowRanges(options.allowRanges);\n\tassertDomainPolicy(hostname, options.domainPolicy);\n\n\tif (net.isIP(hostname)) {\n\t\tassertPublicAddress(hostname, hostname, allowRanges);\n\t\treturn url;\n\t}\n\n\tif (shouldTrustEnvProxy(url, options.trustEnvProxy === true)) return url;\n\n\tlet addresses: LookupAddress[];\n\ttry {\n\t\taddresses = await (options.lookup ?? defaultLookup)(hostname);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tthrow new Error(`Failed to resolve ${hostname}: ${message}`);\n\t}\n\n\tif (addresses.length === 0) throw new Error(`Failed to resolve ${hostname}: no addresses returned`);\n\tfor (const { address } of addresses) {\n\t\tassertPublicAddress(address, hostname, allowRanges);\n\t}\n\treturn url;\n}\n\nexport async function fetchRemoteUrl(\n\turl: string | URL,\n\tinit: RequestInit = {},\n\toptions: FetchRemoteOptions = {},\n): Promise<Response> {\n\tconst fetchImpl = options.fetch ?? fetch;\n\tconst maxRedirects = options.maxRedirects ?? DEFAULT_MAX_REDIRECTS;\n\tlet current = await validateRemoteUrl(url, options);\n\tlet requestInit = init;\n\n\tfor (let redirects = 0; redirects <= maxRedirects; redirects++) {\n\t\tconst response = await fetchImpl(current, { ...requestInit, redirect: \"manual\" });\n\t\tif (!REDIRECT_STATUSES.has(response.status)) return response;\n\n\t\tconst location = response.headers.get(\"location\");\n\t\tif (!location) return response;\n\t\tif (redirects === maxRedirects) throw new Error(`Too many redirects fetching ${current.toString()}`);\n\n\t\tconst from = current;\n\t\tcurrent = await validateRemoteUrl(new URL(location, current), options);\n\t\tif (response.status === 303 || ((response.status === 301 || response.status === 302) && requestInit.method?.toUpperCase() === \"POST\")) {\n\t\t\tconst { body: _body, ...nextInit } = requestInit;\n\t\t\trequestInit = { ...nextInit, method: \"GET\" };\n\t\t}\n\t\tif (options.onRedirect) requestInit = options.onRedirect({ from, to: current, init: requestInit, response });\n\t}\n\n\tthrow new Error(`Too many redirects fetching ${current.toString()}`);\n}\n\nfunction normalizeHostname(hostname: string): string {\n\treturn hostname.toLowerCase().replace(/^\\[|\\]$/g, \"\").replace(/\\.$/, \"\");\n}\n\nfunction assertDomainPolicy(hostname: string, policy?: DomainPolicy): void {\n\tif (!policy) return;\n\tif (policy.deny.some((entry) => domainMatches(hostname, entry))) {\n\t\tthrow new Error(`Blocked hostname by fetch_content domain policy: ${hostname}`);\n\t}\n\tif (policy.allow.length > 0 && !policy.allow.some((entry) => domainMatches(hostname, entry))) {\n\t\tthrow new Error(`Hostname not allowed by fetch_content domain policy: ${hostname}`);\n\t}\n}\n\nfunction domainMatches(hostname: string, entry: string): boolean {\n\treturn hostname === entry || hostname.endsWith(`.${entry}`);\n}\n\nfunction getProxyForProtocol(protocol: string): string {\n\tconst candidates = protocol === \"http:\"\n\t\t? [process.env.HTTP_PROXY, process.env.http_proxy, process.env.ALL_PROXY, process.env.all_proxy]\n\t\t: protocol === \"https:\"\n\t\t\t? [process.env.HTTPS_PROXY, process.env.https_proxy, process.env.HTTP_PROXY, process.env.http_proxy, process.env.ALL_PROXY, process.env.all_proxy]\n\t\t\t: [];\n\tfor (const candidate of candidates) {\n\t\tconst value = candidate?.trim();\n\t\tif (!value) continue;\n\t\ttry {\n\t\t\tconst proxyUrl = new URL(value);\n\t\t\tif ((proxyUrl.protocol === \"http:\" || proxyUrl.protocol === \"https:\") && proxyUrl.hostname) return value;\n\t\t} catch {\n\t\t\t// Invalid proxy env vars should not weaken local DNS SSRF checks.\n\t\t}\n\t}\n\treturn \"\";\n}\n\nfunction hostnameMatchesNoProxy(hostname: string, port: string, entry: string): boolean {\n\tconst trimmed = entry.trim();\n\tif (!trimmed) return false;\n\tif (trimmed === \"*\") return true;\n\n\t// NO_PROXY entries may include a port. Strip it only after handling\n\t// bracketed IPv6 literals, which can contain several colons.\n\tlet hostEntry = trimmed;\n\tlet entryPort: string | undefined;\n\tif (hostEntry.startsWith(\"[\")) {\n\t\tconst closingBracket = hostEntry.indexOf(\"]\");\n\t\tif (closingBracket >= 0) {\n\t\t\tconst suffix = hostEntry.slice(closingBracket + 1);\n\t\t\tif (/^:\\\\d+$/.test(suffix)) entryPort = suffix.slice(1);\n\t\t\thostEntry = hostEntry.slice(0, closingBracket + 1);\n\t\t}\n\t} else {\n\t\tconst colon = hostEntry.lastIndexOf(\":\");\n\t\tif (colon > -1 && /^\\d+$/.test(hostEntry.slice(colon + 1))) {\n\t\t\tentryPort = hostEntry.slice(colon + 1);\n\t\t\thostEntry = hostEntry.slice(0, colon);\n\t\t}\n\t}\n\tif (entryPort !== undefined && entryPort !== port) return false;\n\n\tconst normalizedEntry = normalizeHostname(hostEntry);\n\tif (!normalizedEntry) return false;\n\tif (normalizedEntry === hostname) return true;\n\tconst suffix = normalizedEntry.startsWith(\"*.\")\n\t\t? normalizedEntry.slice(1)\n\t\t: normalizedEntry.startsWith(\".\")\n\t\t\t? normalizedEntry\n\t\t\t: `.${normalizedEntry}`;\n\treturn hostname.endsWith(suffix);\n}\n\nfunction shouldTrustEnvProxy(url: URL, enabled: boolean): boolean {\n\tif (!enabled || !getProxyForProtocol(url.protocol)) return false;\n\tconst hostname = normalizeHostname(url.hostname);\n\tconst port = url.port || (url.protocol === \"https:\" ? \"443\" : \"80\");\n\tconst noProxy = process.env.NO_PROXY || process.env.no_proxy || \"\";\n\treturn !noProxy.split(\",\").some(entry => hostnameMatchesNoProxy(hostname, port, entry));\n}\n\nfunction assertPublicAddress(address: string, hostname: string, allowRanges: ParsedCidr[] = []): void {\n\tconst normalized = normalizeHostname(address);\n\tconst ipVersion = net.isIP(normalized);\n\tif (ipVersion === 0) throw new Error(`Resolved non-IP address for ${hostname}: ${address}`);\n\t// Explicitly-allowed ranges bypass the private/reserved checks below. This lets\n\t// users exempt synthetic ranges produced by TUN/fake-IP proxies (e.g. 198.18/15).\n\tif (isInAllowedRange(normalized, ipVersion, allowRanges)) return;\n\tif (ipVersion === 4 && isBlockedIPv4(normalized)) {\n\t\tconst hint = isFakeIpProxyAddress(normalized)\n\t\t\t? '. This address is in 198.18.0.0/15, commonly used by TUN/fake-IP proxies. If that matches your setup, configure ssrf.allowRanges with [\"198.18.0.0/15\"] in web-search.json.'\n\t\t\t: \"\";\n\t\tthrow new Error(`Blocked internal address for ${hostname}: ${normalized}${hint}`);\n\t}\n\tif (ipVersion === 6 && isBlockedIPv6(normalized)) {\n\t\tthrow new Error(`Blocked internal address for ${hostname}: ${normalized}`);\n\t}\n}\n\nfunction isFakeIpProxyAddress(address: string): boolean {\n\tconst [a, b] = address.split(\".\").map(part => Number(part));\n\treturn a === 198 && (b === 18 || b === 19);\n}\n\nfunction isBlockedIPv4(address: string): boolean {\n\tconst parts = address.split(\".\").map(part => Number(part));\n\tif (parts.length !== 4 || parts.some(part => !Number.isInteger(part) || part < 0 || part > 255)) return true;\n\tconst [a, b] = parts;\n\treturn a === 0 ||\n\t\ta === 10 ||\n\t\ta === 127 ||\n\t\t(a === 100 && b >= 64 && b <= 127) ||\n\t\t(a === 169 && b === 254) ||\n\t\t(a === 172 && b >= 16 && b <= 31) ||\n\t\t(a === 192 && b === 168) ||\n\t\tisFakeIpProxyAddress(address) ||\n\t\ta >= 224;\n}\n\nfunction isBlockedIPv6(address: string): boolean {\n\tconst groups = parseIPv6(address);\n\tif (!groups) return true;\n\n\tconst first = groups[0];\n\tif (groups.every(group => group === 0)) return true;\n\tif (groups.slice(0, 7).every(group => group === 0) && groups[7] === 1) return true;\n\tif ((first & 0xfe00) === 0xfc00) return true;\n\tif ((first & 0xffc0) === 0xfe80) return true;\n\n\tconst isMappedIPv4 = groups.slice(0, 5).every(group => group === 0) && groups[5] === 0xffff;\n\tif (isMappedIPv4) {\n\t\tconst ipv4 = [groups[6] >> 8, groups[6] & 0xff, groups[7] >> 8, groups[7] & 0xff].join(\".\");\n\t\treturn isBlockedIPv4(ipv4);\n\t}\n\n\treturn false;\n}\n\nfunction parseIPv6(address: string): number[] | null {\n\tif (address.includes(\".\")) {\n\t\tconst lastColon = address.lastIndexOf(\":\");\n\t\tconst ipv4 = address.slice(lastColon + 1);\n\t\tif (net.isIP(ipv4) !== 4) return null;\n\t\tconst octets = ipv4.split(\".\").map(part => Number(part));\n\t\taddress = `${address.slice(0, lastColon)}:${((octets[0] << 8) | octets[1]).toString(16)}:${((octets[2] << 8) | octets[3]).toString(16)}`;\n\t}\n\n\tconst pieces = address.split(\"::\");\n\tif (pieces.length > 2) return null;\n\n\tconst left = pieces[0] ? pieces[0].split(\":\") : [];\n\tconst right = pieces.length === 2 && pieces[1] ? pieces[1].split(\":\") : [];\n\tconst missing = 8 - left.length - right.length;\n\tif (pieces.length === 1 && missing !== 0) return null;\n\tif (pieces.length === 2 && missing < 0) return null;\n\n\tconst groups = [...left, ...Array(missing).fill(\"0\"), ...right].map(part => {\n\t\tif (!/^[0-9a-f]{1,4}$/i.test(part)) return -1;\n\t\treturn parseInt(part, 16);\n\t});\n\treturn groups.length === 8 && groups.every(group => group >= 0 && group <= 0xffff) ? groups : null;\n}\n\n/** Parse `allowRanges` config value into validated CIDR rules. Throws on malformed entries. */\nfunction parseAllowRanges(input: unknown): ParsedCidr[] {\n\tif (input === undefined || input === null) return [];\n\tif (!Array.isArray(input)) {\n\t\tthrow new Error(\"ssrf.allowRanges must be an array of CIDR strings\");\n\t}\n\tconst rules: ParsedCidr[] = [];\n\tfor (const entry of input) {\n\t\tif (typeof entry !== \"string\") {\n\t\t\tthrow new Error(`ssrf.allowRanges entries must be strings, got ${typeof entry}`);\n\t\t}\n\t\tconst rule = parseCidr(entry.trim());\n\t\tif (!rule) {\n\t\t\tthrow new Error(`Invalid CIDR notation in ssrf.allowRanges: \"${entry}\"`);\n\t\t}\n\t\trules.push(rule);\n\t}\n\treturn rules;\n}\n\n/** Parse a single CIDR (e.g. \"198.18.0.0/15\", \"fd00::/8\") or bare host (\"1.2.3.4\"). Returns null if invalid. */\nfunction parseCidr(raw: string): ParsedCidr | null {\n\tif (!raw) return null;\n\tconst slash = raw.lastIndexOf(\"/\");\n\tconst addrPart = slash >= 0 ? raw.slice(0, slash) : raw;\n\tconst prefixPart = slash >= 0 ? raw.slice(slash + 1) : null;\n\t// A slash must be followed by digits. Number(\"\")/Number(\" \") are 0, which\n\t// would silently turn \"198.18.0.0/\" into /0 and exempt every address.\n\tif (prefixPart !== null && !/^\\d+$/.test(prefixPart)) return null;\n\tconst version = net.isIP(addrPart);\n\n\tif (version === 4) {\n\t\tconst bytes = ipv4ToBytes(addrPart);\n\t\tif (!bytes) return null;\n\t\tconst prefix = prefixPart === null ? 32 : Number(prefixPart);\n\t\tif (!Number.isInteger(prefix) || prefix < 1 || prefix > 32) return null;\n\t\treturn { bytes, prefix };\n\t}\n\tif (version === 6) {\n\t\tconst groups = parseIPv6(addrPart);\n\t\tif (!groups) return null;\n\t\tconst prefix = prefixPart === null ? 128 : Number(prefixPart);\n\t\tif (!Number.isInteger(prefix) || prefix < 1 || prefix > 128) return null;\n\t\treturn { bytes: ipv6GroupsToBytes(groups), prefix };\n\t}\n\treturn null;\n}\n\nfunction ipv4ToBytes(address: string): Uint8Array | null {\n\tconst parts = address.split(\".\");\n\tif (parts.length !== 4) return null;\n\tconst bytes = new Uint8Array(4);\n\tfor (let i = 0; i < 4; i++) {\n\t\tconst octet = Number(parts[i]);\n\t\tif (!Number.isInteger(octet) || octet < 0 || octet > 255) return null;\n\t\tbytes[i] = octet;\n\t}\n\treturn bytes;\n}\n\nfunction ipv6GroupsToBytes(groups: number[]): Uint8Array {\n\tconst bytes = new Uint8Array(16);\n\tfor (let i = 0; i < 8; i++) {\n\t\tbytes[i * 2] = groups[i] >> 8;\n\t\tbytes[i * 2 + 1] = groups[i] & 0xff;\n\t}\n\treturn bytes;\n}\n\nfunction ipToBytes(address: string, version: number): Uint8Array | null {\n\tif (version === 4) return ipv4ToBytes(address);\n\tif (version === 6) {\n\t\tconst groups = parseIPv6(address);\n\t\treturn groups ? ipv6GroupsToBytes(groups) : null;\n\t}\n\treturn null;\n}\n\n/** True if `address` (already validated as `ipVersion`) falls within any allowed CIDR. */\nfunction isInAllowedRange(address: string, ipVersion: number, allowRanges: ParsedCidr[]): boolean {\n\tif (allowRanges.length === 0) return false;\n\tconst addrBytes = ipToBytes(address, ipVersion);\n\tif (!addrBytes) return false;\n\tfor (const rule of allowRanges) {\n\t\t// Only compare same-family rules (4-byte IPv4 vs 16-byte IPv6).\n\t\tif (rule.bytes.length !== addrBytes.length) continue;\n\t\tif (bytesMatchPrefix(addrBytes, rule.bytes, rule.prefix)) return true;\n\t}\n\treturn false;\n}\n\n/** Compare the leading `prefix` bits of two equal-length byte arrays. */\nfunction bytesMatchPrefix(addr: Uint8Array, network: Uint8Array, prefix: number): boolean {\n\tconst fullBytes = prefix >> 3;\n\tconst remBits = prefix & 7;\n\tfor (let i = 0; i < fullBytes; i++) {\n\t\tif (addr[i] !== network[i]) return false;\n\t}\n\tif (remBits > 0 && fullBytes < addr.length) {\n\t\tconst mask = (0xff << (8 - remBits)) & 0xff;\n\t\tif ((addr[fullBytes] & mask) !== (network[fullBytes] & mask)) return false;\n\t}\n\treturn true;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ssrf-protection.d.ts","sourceRoot":"","sources":["../../../../src/humain/web-access/vendor/ssrf-protection.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,aAAa,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAChE,MAAM,MAAM,MAAM,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;AACpE,KAAK,KAAK,GAAG,OAAO,KAAK,CAAC;AA2C1B,MAAM,WAAW,UAAU;IAC1B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC5B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;CACf;AAID,wBAAgB,4BAA4B,IAAI,YAAY,CAkB3D;AA2BD,wBAAgB,cAAc,IAAI,UAAU,CAwB3C;AAED,UAAU,iBAAiB;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,qFAAqF;IACrF,aAAa,CAAC,EAAE,OAAO,CAAC;CACxB;AAQD,UAAU,uBAAuB;IAChC,IAAI,EAAE,GAAG,CAAC;IACV,EAAE,EAAE,GAAG,CAAC;IACR,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;CACnB;AAED,UAAU,kBAAmB,SAAQ,iBAAiB;IACrD,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,uBAAuB,KAAK,WAAW,CAAC;CAC5D;AAMD,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,GAAG,CAAC,CA0C3G;AAED,wBAAsB,cAAc,CACnC,GAAG,EAAE,MAAM,GAAG,GAAG,EACjB,IAAI,GAAE,WAAgB,EACtB,OAAO,GAAE,kBAAuB,GAC9B,OAAO,CAAC,QAAQ,CAAC,CAwBnB"}
|