@herbertgao/pi-extensions 2026.8.2 → 2026.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -2
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +6 -8
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +5 -7
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.en.svg +101 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +1 -17
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +5 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/{utils/agent-summary.ts → feature/agent-summary/core.ts} +37 -35
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{agent-summary.ts → agent-summary/index.ts} +2 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{session-reference.ts → reference/index.ts} +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{agent-autocomplete.ts → reference/subagent.ts} +19 -9
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{pi-startup-header.ts → shell/startup-header.ts} +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{working-message.ts → shell/working-message.ts} +5 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +32 -28
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +379 -85
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +727 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +65 -775
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/{feature → renderer}/markdown-enhance.ts +35 -31
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-hover.ts → mouse/hover.ts} +5 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-interaction.ts → mouse/interaction.ts} +34 -35
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-layout.ts → mouse/layout.ts} +5 -21
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-packets.ts → mouse/packets.ts} +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-scroll.ts → mouse/scroll.ts} +48 -37
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/ansi-utils.ts +56 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/diff-renderer.ts +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/line-width-safety.ts +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-display-utils.ts +12 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/write-execution.ts +1 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-grouping.ts → tool/grouping.ts} +18 -8
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-result.ts → tool/result.ts} +4 -13
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/fullscreen-detect.ts +0 -27
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
- package/node_modules/@herbertgao/pi-subagents/package.json +2 -2
- package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +1 -0
- package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +3 -1
- package/node_modules/@herbertgao/pi-subagents/src/index.ts +33 -35
- package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +26 -35
- package/node_modules/@herbertgao/pi-subagents/src/prompts.ts +15 -1
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +19 -0
- package/node_modules/pi-mcp-adapter/README.md +19 -4
- package/node_modules/pi-mcp-adapter/agent-dir.ts +40 -0
- package/node_modules/pi-mcp-adapter/commands.ts +12 -2
- package/node_modules/pi-mcp-adapter/direct-tools.ts +6 -8
- package/node_modules/pi-mcp-adapter/index.ts +15 -3
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +87 -4
- package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +138 -61
- package/node_modules/pi-mcp-adapter/mcp-oauth-provider.ts +43 -4
- package/node_modules/pi-mcp-adapter/oauth.ts +48 -0
- package/node_modules/pi-mcp-adapter/package.json +8 -2
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +8 -10
- package/node_modules/pi-mcp-adapter/tool-registrar.ts +187 -3
- package/node_modules/pi-mcp-adapter/types.ts +2 -0
- package/node_modules/pi-web-access/CHANGELOG.md +45 -0
- package/node_modules/pi-web-access/README.md +104 -29
- package/node_modules/pi-web-access/bocha.ts +221 -0
- package/node_modules/pi-web-access/curator-page.ts +113 -12
- package/node_modules/pi-web-access/curator-server.ts +56 -20
- package/node_modules/pi-web-access/datalab-pdf-extract.ts +568 -0
- package/node_modules/pi-web-access/duckduckgo.ts +123 -0
- package/node_modules/pi-web-access/extract.ts +288 -143
- package/node_modules/pi-web-access/feature-config.ts +29 -0
- package/node_modules/pi-web-access/gemini-api.ts +4 -0
- package/node_modules/pi-web-access/gemini-search.ts +45 -12
- package/node_modules/pi-web-access/gemini-web.ts +68 -3
- package/node_modules/pi-web-access/index.ts +160 -71
- package/node_modules/pi-web-access/jina-search.ts +284 -0
- package/node_modules/pi-web-access/kagi.ts +12 -12
- package/node_modules/pi-web-access/openai-search.ts +12 -2
- package/node_modules/pi-web-access/package.json +4 -3
- package/node_modules/pi-web-access/pdf-extract.ts +327 -228
- package/node_modules/pi-web-access/storage.ts +440 -2
- package/node_modules/pi-web-access/summary-model-scope.ts +1 -1
- package/node_modules/pi-web-access/summary-review.ts +4 -2
- package/node_modules/pi-web-access/video-extract.ts +10 -8
- package/node_modules/pi-web-access/xai-search.ts +12 -2
- package/node_modules/pi-web-access/youtube-extract.ts +2 -1
- package/node_modules/remote-pi/LICENSE +21 -0
- package/node_modules/remote-pi/README.md +675 -0
- package/node_modules/remote-pi/dist/actions/handlers.d.ts +122 -0
- package/node_modules/remote-pi/dist/actions/handlers.js +160 -0
- package/node_modules/remote-pi/dist/actions/handlers.js.map +1 -0
- package/node_modules/remote-pi/dist/actions/registry.d.ts +39 -0
- package/node_modules/remote-pi/dist/actions/registry.js +58 -0
- package/node_modules/remote-pi/dist/actions/registry.js.map +1 -0
- package/node_modules/remote-pi/dist/bin/supervisord.d.ts +2 -0
- package/node_modules/remote-pi/dist/bin/supervisord.js +86 -0
- package/node_modules/remote-pi/dist/bin/supervisord.js.map +1 -0
- package/node_modules/remote-pi/dist/commands/builtin_mirror.d.ts +58 -0
- package/node_modules/remote-pi/dist/commands/builtin_mirror.js +71 -0
- package/node_modules/remote-pi/dist/commands/builtin_mirror.js.map +1 -0
- package/node_modules/remote-pi/dist/commands/list_commands.d.ts +60 -0
- package/node_modules/remote-pi/dist/commands/list_commands.js +73 -0
- package/node_modules/remote-pi/dist/commands/list_commands.js.map +1 -0
- package/node_modules/remote-pi/dist/config.d.ts +62 -0
- package/node_modules/remote-pi/dist/config.js +115 -0
- package/node_modules/remote-pi/dist/config.js.map +1 -0
- package/node_modules/remote-pi/dist/daemon/client.d.ts +20 -0
- package/node_modules/remote-pi/dist/daemon/client.js +131 -0
- package/node_modules/remote-pi/dist/daemon/client.js.map +1 -0
- package/node_modules/remote-pi/dist/daemon/control_protocol.d.ts +176 -0
- package/node_modules/remote-pi/dist/daemon/control_protocol.js +63 -0
- package/node_modules/remote-pi/dist/daemon/control_protocol.js.map +1 -0
- package/node_modules/remote-pi/dist/daemon/cron_log.d.ts +45 -0
- package/node_modules/remote-pi/dist/daemon/cron_log.js +72 -0
- package/node_modules/remote-pi/dist/daemon/cron_log.js.map +1 -0
- package/node_modules/remote-pi/dist/daemon/cron_registry.d.ts +80 -0
- package/node_modules/remote-pi/dist/daemon/cron_registry.js +194 -0
- package/node_modules/remote-pi/dist/daemon/cron_registry.js.map +1 -0
- package/node_modules/remote-pi/dist/daemon/id.d.ts +24 -0
- package/node_modules/remote-pi/dist/daemon/id.js +36 -0
- package/node_modules/remote-pi/dist/daemon/id.js.map +1 -0
- package/node_modules/remote-pi/dist/daemon/install.d.ts +170 -0
- package/node_modules/remote-pi/dist/daemon/install.js +547 -0
- package/node_modules/remote-pi/dist/daemon/install.js.map +1 -0
- package/node_modules/remote-pi/dist/daemon/registry.d.ts +63 -0
- package/node_modules/remote-pi/dist/daemon/registry.js +153 -0
- package/node_modules/remote-pi/dist/daemon/registry.js.map +1 -0
- package/node_modules/remote-pi/dist/daemon/rpc_child.d.ts +194 -0
- package/node_modules/remote-pi/dist/daemon/rpc_child.js +389 -0
- package/node_modules/remote-pi/dist/daemon/rpc_child.js.map +1 -0
- package/node_modules/remote-pi/dist/daemon/supervisor.d.ts +93 -0
- package/node_modules/remote-pi/dist/daemon/supervisor.js +596 -0
- package/node_modules/remote-pi/dist/daemon/supervisor.js.map +1 -0
- package/node_modules/remote-pi/dist/extension_ui_bridge.d.ts +26 -0
- package/node_modules/remote-pi/dist/extension_ui_bridge.js +341 -0
- package/node_modules/remote-pi/dist/extension_ui_bridge.js.map +1 -0
- package/node_modules/remote-pi/dist/index.d.ts +213 -0
- package/node_modules/remote-pi/dist/index.js +4996 -0
- package/node_modules/remote-pi/dist/index.js.map +1 -0
- package/node_modules/remote-pi/dist/mcp/mesh_result.d.ts +10 -0
- package/node_modules/remote-pi/dist/mcp/mesh_result.js +42 -0
- package/node_modules/remote-pi/dist/mcp/mesh_result.js.map +1 -0
- package/node_modules/remote-pi/dist/mcp/mesh_server.d.ts +16 -0
- package/node_modules/remote-pi/dist/mcp/mesh_server.js +327 -0
- package/node_modules/remote-pi/dist/mcp/mesh_server.js.map +1 -0
- package/node_modules/remote-pi/dist/mesh/canonical.d.ts +30 -0
- package/node_modules/remote-pi/dist/mesh/canonical.js +61 -0
- package/node_modules/remote-pi/dist/mesh/canonical.js.map +1 -0
- package/node_modules/remote-pi/dist/mesh/client.d.ts +18 -0
- package/node_modules/remote-pi/dist/mesh/client.js +125 -0
- package/node_modules/remote-pi/dist/mesh/client.js.map +1 -0
- package/node_modules/remote-pi/dist/mesh/encoding.d.ts +38 -0
- package/node_modules/remote-pi/dist/mesh/encoding.js +216 -0
- package/node_modules/remote-pi/dist/mesh/encoding.js.map +1 -0
- package/node_modules/remote-pi/dist/mesh/self_revoke.d.ts +73 -0
- package/node_modules/remote-pi/dist/mesh/self_revoke.js +401 -0
- package/node_modules/remote-pi/dist/mesh/self_revoke.js.map +1 -0
- package/node_modules/remote-pi/dist/mesh/siblings.d.ts +49 -0
- package/node_modules/remote-pi/dist/mesh/siblings.js +159 -0
- package/node_modules/remote-pi/dist/mesh/siblings.js.map +1 -0
- package/node_modules/remote-pi/dist/mesh/types.d.ts +34 -0
- package/node_modules/remote-pi/dist/mesh/types.js +11 -0
- package/node_modules/remote-pi/dist/mesh/types.js.map +1 -0
- package/node_modules/remote-pi/dist/mesh/verify.d.ts +17 -0
- package/node_modules/remote-pi/dist/mesh/verify.js +86 -0
- package/node_modules/remote-pi/dist/mesh/verify.js.map +1 -0
- package/node_modules/remote-pi/dist/pairing/crypto.d.ts +8 -0
- package/node_modules/remote-pi/dist/pairing/crypto.js +22 -0
- package/node_modules/remote-pi/dist/pairing/crypto.js.map +1 -0
- package/node_modules/remote-pi/dist/pairing/handshake.d.ts +28 -0
- package/node_modules/remote-pi/dist/pairing/handshake.js +113 -0
- package/node_modules/remote-pi/dist/pairing/handshake.js.map +1 -0
- package/node_modules/remote-pi/dist/pairing/noise-sha256.d.ts +16 -0
- package/node_modules/remote-pi/dist/pairing/noise-sha256.js +103 -0
- package/node_modules/remote-pi/dist/pairing/noise-sha256.js.map +1 -0
- package/node_modules/remote-pi/dist/pairing/qr.d.ts +59 -0
- package/node_modules/remote-pi/dist/pairing/qr.js +125 -0
- package/node_modules/remote-pi/dist/pairing/qr.js.map +1 -0
- package/node_modules/remote-pi/dist/pairing/storage.d.ts +120 -0
- package/node_modules/remote-pi/dist/pairing/storage.js +604 -0
- package/node_modules/remote-pi/dist/pairing/storage.js.map +1 -0
- package/node_modules/remote-pi/dist/protocol/codec.d.ts +7 -0
- package/node_modules/remote-pi/dist/protocol/codec.js +49 -0
- package/node_modules/remote-pi/dist/protocol/codec.js.map +1 -0
- package/node_modules/remote-pi/dist/protocol/types.d.ts +393 -0
- package/node_modules/remote-pi/dist/protocol/types.js +2 -0
- package/node_modules/remote-pi/dist/protocol/types.js.map +1 -0
- package/node_modules/remote-pi/dist/rooms.d.ts +29 -0
- package/node_modules/remote-pi/dist/rooms.js +57 -0
- package/node_modules/remote-pi/dist/rooms.js.map +1 -0
- package/node_modules/remote-pi/dist/session/address.d.ts +49 -0
- package/node_modules/remote-pi/dist/session/address.js +58 -0
- package/node_modules/remote-pi/dist/session/address.js.map +1 -0
- package/node_modules/remote-pi/dist/session/agent_bridge.d.ts +55 -0
- package/node_modules/remote-pi/dist/session/agent_bridge.js +146 -0
- package/node_modules/remote-pi/dist/session/agent_bridge.js.map +1 -0
- package/node_modules/remote-pi/dist/session/bridge.d.ts +37 -0
- package/node_modules/remote-pi/dist/session/bridge.js +130 -0
- package/node_modules/remote-pi/dist/session/bridge.js.map +1 -0
- package/node_modules/remote-pi/dist/session/broker.d.ts +189 -0
- package/node_modules/remote-pi/dist/session/broker.js +507 -0
- package/node_modules/remote-pi/dist/session/broker.js.map +1 -0
- package/node_modules/remote-pi/dist/session/broker_remote.d.ts +74 -0
- package/node_modules/remote-pi/dist/session/broker_remote.js +583 -0
- package/node_modules/remote-pi/dist/session/broker_remote.js.map +1 -0
- package/node_modules/remote-pi/dist/session/cwd_lock.d.ts +33 -0
- package/node_modules/remote-pi/dist/session/cwd_lock.js +126 -0
- package/node_modules/remote-pi/dist/session/cwd_lock.js.map +1 -0
- package/node_modules/remote-pi/dist/session/envelope.d.ts +33 -0
- package/node_modules/remote-pi/dist/session/envelope.js +117 -0
- package/node_modules/remote-pi/dist/session/envelope.js.map +1 -0
- package/node_modules/remote-pi/dist/session/global_config.d.ts +33 -0
- package/node_modules/remote-pi/dist/session/global_config.js +73 -0
- package/node_modules/remote-pi/dist/session/global_config.js.map +1 -0
- package/node_modules/remote-pi/dist/session/ipc.d.ts +27 -0
- package/node_modules/remote-pi/dist/session/ipc.js +22 -0
- package/node_modules/remote-pi/dist/session/ipc.js.map +1 -0
- package/node_modules/remote-pi/dist/session/leader_election.d.ts +32 -0
- package/node_modules/remote-pi/dist/session/leader_election.js +106 -0
- package/node_modules/remote-pi/dist/session/leader_election.js.map +1 -0
- package/node_modules/remote-pi/dist/session/local_config.d.ts +55 -0
- package/node_modules/remote-pi/dist/session/local_config.js +157 -0
- package/node_modules/remote-pi/dist/session/local_config.js.map +1 -0
- package/node_modules/remote-pi/dist/session/mesh_message.d.ts +20 -0
- package/node_modules/remote-pi/dist/session/mesh_message.js +28 -0
- package/node_modules/remote-pi/dist/session/mesh_message.js.map +1 -0
- package/node_modules/remote-pi/dist/session/mesh_node.d.ts +116 -0
- package/node_modules/remote-pi/dist/session/mesh_node.js +573 -0
- package/node_modules/remote-pi/dist/session/mesh_node.js.map +1 -0
- package/node_modules/remote-pi/dist/session/peer.d.ts +139 -0
- package/node_modules/remote-pi/dist/session/peer.js +451 -0
- package/node_modules/remote-pi/dist/session/peer.js.map +1 -0
- package/node_modules/remote-pi/dist/session/peer_inventory.d.ts +13 -0
- package/node_modules/remote-pi/dist/session/peer_inventory.js +48 -0
- package/node_modules/remote-pi/dist/session/peer_inventory.js.map +1 -0
- package/node_modules/remote-pi/dist/session/peer_limits.d.ts +9 -0
- package/node_modules/remote-pi/dist/session/peer_limits.js +21 -0
- package/node_modules/remote-pi/dist/session/peer_limits.js.map +1 -0
- package/node_modules/remote-pi/dist/session/setup_wizard.d.ts +39 -0
- package/node_modules/remote-pi/dist/session/setup_wizard.js +63 -0
- package/node_modules/remote-pi/dist/session/setup_wizard.js.map +1 -0
- package/node_modules/remote-pi/dist/session/tool_gate.d.ts +5 -0
- package/node_modules/remote-pi/dist/session/tool_gate.js +11 -0
- package/node_modules/remote-pi/dist/session/tool_gate.js.map +1 -0
- package/node_modules/remote-pi/dist/session/tools.d.ts +24 -0
- package/node_modules/remote-pi/dist/session/tools.js +252 -0
- package/node_modules/remote-pi/dist/session/tools.js.map +1 -0
- package/node_modules/remote-pi/dist/session/wizard.d.ts +13 -0
- package/node_modules/remote-pi/dist/session/wizard.js +20 -0
- package/node_modules/remote-pi/dist/session/wizard.js.map +1 -0
- package/node_modules/remote-pi/dist/settings.d.ts +15 -0
- package/node_modules/remote-pi/dist/settings.js +52 -0
- package/node_modules/remote-pi/dist/settings.js.map +1 -0
- package/node_modules/remote-pi/dist/transport/peer_channel.d.ts +37 -0
- package/node_modules/remote-pi/dist/transport/peer_channel.js +98 -0
- package/node_modules/remote-pi/dist/transport/peer_channel.js.map +1 -0
- package/node_modules/remote-pi/dist/transport/pi_forward_client.d.ts +29 -0
- package/node_modules/remote-pi/dist/transport/pi_forward_client.js +62 -0
- package/node_modules/remote-pi/dist/transport/pi_forward_client.js.map +1 -0
- package/node_modules/remote-pi/dist/transport/relay_client.d.ts +91 -0
- package/node_modules/remote-pi/dist/transport/relay_client.js +206 -0
- package/node_modules/remote-pi/dist/transport/relay_client.js.map +1 -0
- package/node_modules/remote-pi/dist/ui/footer.d.ts +32 -0
- package/node_modules/remote-pi/dist/ui/footer.js +34 -0
- package/node_modules/remote-pi/dist/ui/footer.js.map +1 -0
- package/node_modules/remote-pi/docs/daemon.md +289 -0
- package/node_modules/remote-pi/package.json +88 -0
- package/node_modules/remote-pi/service-templates/launchd.plist.template +35 -0
- package/node_modules/remote-pi/service-templates/systemd.service.template +19 -0
- package/node_modules/remote-pi/service-templates/task-launcher.vbs.template +10 -0
- package/node_modules/remote-pi/service-templates/task-scheduler.xml.template +38 -0
- package/node_modules/remote-pi/skills/agent-network/SKILL.md +310 -0
- package/package.json +19 -9
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool-diff/write-display-utils.ts +0 -29
- /package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{session-reference-core.ts → reference/session.ts} +0 -0
- /package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{pi-aliases.ts → shell/aliases.ts} +0 -0
- /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/ATTRIBUTION.md +0 -0
- /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/diff-presentation.ts +0 -0
- /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/index.ts +0 -0
- /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/render-utils.ts +0 -0
- /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/shiki-highlight.ts +0 -0
- /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/types.ts +0 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { activityMonitor } from "./activity.ts";
|
|
3
|
+
import { hasCredentialSource, redactCredential, resolveCredential } from "./credential-source.ts";
|
|
4
|
+
import type { ExtractedContent } from "./extract.ts";
|
|
5
|
+
import type { SearchOptions, SearchResponse } from "./perplexity.ts";
|
|
6
|
+
import { getWebSearchConfigPath } from "./utils.ts";
|
|
7
|
+
|
|
8
|
+
const JINA_SEARCH_BASE_URL = "https://s.jina.ai/";
|
|
9
|
+
const CONFIG_PATH = getWebSearchConfigPath();
|
|
10
|
+
const SEARCH_TIMEOUT_MS = 60_000;
|
|
11
|
+
|
|
12
|
+
interface WebSearchConfig {
|
|
13
|
+
jinaApiKey?: unknown;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface JinaSearchItem {
|
|
17
|
+
title?: unknown;
|
|
18
|
+
url?: unknown;
|
|
19
|
+
description?: unknown;
|
|
20
|
+
content?: unknown;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface JinaSearchEnvelope {
|
|
24
|
+
code?: unknown;
|
|
25
|
+
data?: unknown;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface JinaSearchOptions extends SearchOptions {
|
|
29
|
+
includeContent?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let cachedConfig: WebSearchConfig | null = null;
|
|
33
|
+
|
|
34
|
+
function loadConfig(): WebSearchConfig {
|
|
35
|
+
if (cachedConfig) return cachedConfig;
|
|
36
|
+
if (!existsSync(CONFIG_PATH)) {
|
|
37
|
+
cachedConfig = {};
|
|
38
|
+
return cachedConfig;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const raw = readFileSync(CONFIG_PATH, "utf8");
|
|
42
|
+
try {
|
|
43
|
+
const parsed: unknown = JSON.parse(raw);
|
|
44
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
45
|
+
throw new Error("expected a JSON object");
|
|
46
|
+
}
|
|
47
|
+
cachedConfig = parsed as WebSearchConfig;
|
|
48
|
+
return cachedConfig;
|
|
49
|
+
} catch (err) {
|
|
50
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
51
|
+
throw new Error(`Failed to parse ${CONFIG_PATH}: ${message}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function getApiKey(signal?: AbortSignal): Promise<string | null> {
|
|
56
|
+
return resolveCredential({
|
|
57
|
+
provider: "Jina Search",
|
|
58
|
+
configuredValue: loadConfig().jinaApiKey,
|
|
59
|
+
environmentValue: process.env.JINA_API_KEY,
|
|
60
|
+
signal,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function requireApiKey(signal?: AbortSignal): Promise<string> {
|
|
65
|
+
const apiKey = await getApiKey(signal);
|
|
66
|
+
if (!apiKey) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
"Jina Search API key not found. Either:\n" +
|
|
69
|
+
` 1. Create ${CONFIG_PATH} with { "jinaApiKey": "your-key" }\n` +
|
|
70
|
+
" 2. Set JINA_API_KEY environment variable\n" +
|
|
71
|
+
"Get a key at https://jina.ai/api-dashboard",
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
return apiKey;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function normalizeCount(value: number | undefined): number {
|
|
78
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return 5;
|
|
79
|
+
return Math.max(1, Math.min(Math.floor(value), 20));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function normalizeDomain(value: string): string | null {
|
|
83
|
+
let input = value.trim().toLowerCase();
|
|
84
|
+
if (input.startsWith("-")) input = input.slice(1).trim();
|
|
85
|
+
if (!input) return null;
|
|
86
|
+
try {
|
|
87
|
+
const parsed = input.includes("://") ? new URL(input) : new URL(`https://${input}`);
|
|
88
|
+
input = parsed.hostname;
|
|
89
|
+
} catch {
|
|
90
|
+
input = input.split("/")[0]?.split(":")[0] ?? "";
|
|
91
|
+
}
|
|
92
|
+
input = input.replace(/^\.+|\.+$/g, "");
|
|
93
|
+
return /^[a-z0-9][a-z0-9.-]*\.[a-z]{2,}$/i.test(input) ? input : null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function mapDomainFilter(domainFilter: string[] | undefined): { includes: string[]; excludes: string[] } {
|
|
97
|
+
const includes: string[] = [];
|
|
98
|
+
const excludes: string[] = [];
|
|
99
|
+
for (const raw of domainFilter ?? []) {
|
|
100
|
+
const domain = normalizeDomain(raw);
|
|
101
|
+
if (!domain) continue;
|
|
102
|
+
const target = raw.trim().startsWith("-") ? excludes : includes;
|
|
103
|
+
if (!target.includes(domain)) target.push(domain);
|
|
104
|
+
}
|
|
105
|
+
return { includes, excludes };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function buildSearchRequest(query: string, options: JinaSearchOptions, numResults: number): { url: string; filters: ReturnType<typeof mapDomainFilter> } {
|
|
109
|
+
const filters = mapDomainFilter(options.domainFilter);
|
|
110
|
+
const recency = options.recencyFilter ? ` published in the past ${options.recencyFilter}` : "";
|
|
111
|
+
const exclusions = filters.excludes.map((domain) => ` -site:${domain}`).join("");
|
|
112
|
+
const constrainedQuery = `${query.trim()}${exclusions}${recency}`.trim();
|
|
113
|
+
const url = new URL(encodeURIComponent(constrainedQuery), JINA_SEARCH_BASE_URL);
|
|
114
|
+
url.searchParams.set("count", String(numResults));
|
|
115
|
+
for (const domain of filters.includes) url.searchParams.append("site", domain);
|
|
116
|
+
return { url: url.toString(), filters };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function requestSignal(signal?: AbortSignal): { signal: AbortSignal; timeout: AbortSignal } {
|
|
120
|
+
const timeout = AbortSignal.timeout(SEARCH_TIMEOUT_MS);
|
|
121
|
+
return {
|
|
122
|
+
timeout,
|
|
123
|
+
signal: signal ? AbortSignal.any([signal, timeout]) : timeout,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function errorMessage(err: unknown): string {
|
|
128
|
+
return err instanceof Error ? err.message : String(err);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function parseItems(value: unknown): JinaSearchItem[] {
|
|
132
|
+
if (Array.isArray(value)) return value as JinaSearchItem[];
|
|
133
|
+
if (!value || typeof value !== "object") {
|
|
134
|
+
throw new Error("Jina Search API returned invalid response: expected an object or array");
|
|
135
|
+
}
|
|
136
|
+
const envelope = value as JinaSearchEnvelope;
|
|
137
|
+
if (typeof envelope.code === "number" && envelope.code !== 200) {
|
|
138
|
+
throw new Error(`Jina Search API error ${envelope.code}: response envelope reported failure`);
|
|
139
|
+
}
|
|
140
|
+
if (!Array.isArray(envelope.data)) {
|
|
141
|
+
throw new Error("Jina Search API returned invalid response: expected data array");
|
|
142
|
+
}
|
|
143
|
+
return envelope.data as JinaSearchItem[];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function passesDomainFilter(url: string, filters: ReturnType<typeof mapDomainFilter>): boolean {
|
|
147
|
+
let hostname: string;
|
|
148
|
+
try {
|
|
149
|
+
hostname = new URL(url).hostname.toLowerCase();
|
|
150
|
+
} catch {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
const matches = (domain: string): boolean => hostname === domain || hostname.endsWith(`.${domain}`);
|
|
154
|
+
if (filters.includes.length > 0 && !filters.includes.some(matches)) return false;
|
|
155
|
+
return !filters.excludes.some(matches);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function mapItems(items: JinaSearchItem[], numResults: number, filters: ReturnType<typeof mapDomainFilter>): {
|
|
159
|
+
results: SearchResponse["results"];
|
|
160
|
+
content: ExtractedContent[];
|
|
161
|
+
} {
|
|
162
|
+
const results: SearchResponse["results"] = [];
|
|
163
|
+
const content: ExtractedContent[] = [];
|
|
164
|
+
const seen = new Set<string>();
|
|
165
|
+
for (const item of items) {
|
|
166
|
+
const url = typeof item?.url === "string" ? item.url.trim() : "";
|
|
167
|
+
if (!url || seen.has(url) || !passesDomainFilter(url, filters)) continue;
|
|
168
|
+
seen.add(url);
|
|
169
|
+
const title = typeof item.title === "string" && item.title.trim()
|
|
170
|
+
? item.title.trim()
|
|
171
|
+
: `Source ${results.length + 1}`;
|
|
172
|
+
const snippet = typeof item.description === "string"
|
|
173
|
+
? item.description.replace(/\s+/g, " ").trim()
|
|
174
|
+
: "";
|
|
175
|
+
results.push({ title, url, snippet });
|
|
176
|
+
if (typeof item.content === "string" && item.content.trim()) {
|
|
177
|
+
content.push({ url, title, content: item.content.trim(), error: null });
|
|
178
|
+
}
|
|
179
|
+
if (results.length >= numResults) break;
|
|
180
|
+
}
|
|
181
|
+
return { results, content };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function buildAnswer(results: SearchResponse["results"]): string {
|
|
185
|
+
return results
|
|
186
|
+
.map((result) => result.snippet
|
|
187
|
+
? `${result.snippet}\nSource: ${result.title} (${result.url})`
|
|
188
|
+
: `Source: ${result.title} (${result.url})`)
|
|
189
|
+
.join("\n\n");
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function rethrowRequestError(
|
|
193
|
+
err: unknown,
|
|
194
|
+
apiKey: string,
|
|
195
|
+
activityId: string,
|
|
196
|
+
request: ReturnType<typeof requestSignal>,
|
|
197
|
+
callerSignal?: AbortSignal,
|
|
198
|
+
): never {
|
|
199
|
+
if (request.timeout.aborted && !callerSignal?.aborted) {
|
|
200
|
+
activityMonitor.logComplete(activityId, 408);
|
|
201
|
+
throw new Error("Jina Search API error 408: request timed out");
|
|
202
|
+
}
|
|
203
|
+
const message = errorMessage(err);
|
|
204
|
+
const redactedMessage = redactCredential(message, apiKey);
|
|
205
|
+
if (redactedMessage.toLowerCase().includes("abort")) activityMonitor.logComplete(activityId, 0);
|
|
206
|
+
else activityMonitor.logError(activityId, redactedMessage);
|
|
207
|
+
if (redactedMessage === message) throw err;
|
|
208
|
+
const redactedError = new Error(redactedMessage);
|
|
209
|
+
if (err instanceof Error) redactedError.name = err.name;
|
|
210
|
+
throw redactedError;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function isJinaSearchAvailable(): boolean {
|
|
214
|
+
return hasCredentialSource({
|
|
215
|
+
provider: "Jina Search",
|
|
216
|
+
configuredValue: loadConfig().jinaApiKey,
|
|
217
|
+
environmentValue: process.env.JINA_API_KEY,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export async function searchWithJina(query: string, options: JinaSearchOptions = {}): Promise<SearchResponse> {
|
|
222
|
+
const apiKey = await requireApiKey(options.signal);
|
|
223
|
+
const numResults = normalizeCount(options.numResults);
|
|
224
|
+
const { url, filters } = buildSearchRequest(query, options, numResults);
|
|
225
|
+
const activityId = activityMonitor.logStart({ type: "api", query });
|
|
226
|
+
const request = requestSignal(options.signal);
|
|
227
|
+
let response: Response;
|
|
228
|
+
try {
|
|
229
|
+
response = await fetch(url, {
|
|
230
|
+
headers: {
|
|
231
|
+
"Accept": "application/json",
|
|
232
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
233
|
+
"User-Agent": "pi-web-access",
|
|
234
|
+
"X-Respond-With": options.includeContent ? "content" : "no-content",
|
|
235
|
+
"X-Retain-Images": "none",
|
|
236
|
+
},
|
|
237
|
+
signal: request.signal,
|
|
238
|
+
});
|
|
239
|
+
} catch (err) {
|
|
240
|
+
rethrowRequestError(err, apiKey, activityId, request, options.signal);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (!response.ok) {
|
|
244
|
+
let body: string;
|
|
245
|
+
try {
|
|
246
|
+
body = redactCredential(await response.text(), apiKey);
|
|
247
|
+
} catch (err) {
|
|
248
|
+
rethrowRequestError(err, apiKey, activityId, request, options.signal);
|
|
249
|
+
}
|
|
250
|
+
activityMonitor.logComplete(activityId, response.status);
|
|
251
|
+
throw new Error(`Jina Search API error ${response.status}: ${body.slice(0, 300)}`);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
let raw: unknown;
|
|
255
|
+
try {
|
|
256
|
+
raw = await response.json();
|
|
257
|
+
} catch (err) {
|
|
258
|
+
if (!(err instanceof SyntaxError)) {
|
|
259
|
+
rethrowRequestError(err, apiKey, activityId, request, options.signal);
|
|
260
|
+
}
|
|
261
|
+
activityMonitor.logComplete(activityId, response.status);
|
|
262
|
+
throw new Error("Jina Search API returned invalid JSON");
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
let mapped: ReturnType<typeof mapItems>;
|
|
266
|
+
try {
|
|
267
|
+
mapped = mapItems(parseItems(raw), numResults, filters);
|
|
268
|
+
} catch (err) {
|
|
269
|
+
const message = errorMessage(err);
|
|
270
|
+
const redactedMessage = redactCredential(message, apiKey);
|
|
271
|
+
activityMonitor.logError(activityId, redactedMessage);
|
|
272
|
+
if (redactedMessage === message) throw err;
|
|
273
|
+
const redactedError = new Error(redactedMessage);
|
|
274
|
+
if (err instanceof Error) redactedError.name = err.name;
|
|
275
|
+
throw redactedError;
|
|
276
|
+
}
|
|
277
|
+
activityMonitor.logComplete(activityId, response.status);
|
|
278
|
+
const result: SearchResponse = {
|
|
279
|
+
answer: buildAnswer(mapped.results),
|
|
280
|
+
results: mapped.results,
|
|
281
|
+
};
|
|
282
|
+
if (options.includeContent && mapped.content.length > 0) result.inlineContent = mapped.content;
|
|
283
|
+
return result;
|
|
284
|
+
}
|
|
@@ -6,7 +6,7 @@ import { hasCredentialSource, redactCredential, resolveCredential } from "./cred
|
|
|
6
6
|
import { fetchRemoteUrl, loadFetchContentDomainPolicy, loadSsrfConfig, validateRemoteUrl, type SsrfConfig } from "./ssrf-protection.ts";
|
|
7
7
|
import { getWebSearchConfigPath } from "./utils.ts";
|
|
8
8
|
|
|
9
|
-
const KAGI_SEARCH_URL = "https://kagi.com/api/
|
|
9
|
+
const KAGI_SEARCH_URL = "https://kagi.com/api/v1/search";
|
|
10
10
|
const KAGI_EXTRACT_URL = "https://kagi.com/api/v1/extract";
|
|
11
11
|
const CONFIG_PATH = getWebSearchConfigPath();
|
|
12
12
|
const SEARCH_TIMEOUT_MS = 60_000;
|
|
@@ -95,8 +95,6 @@ function appendSearchItems(value: unknown, results: SearchResponse["results"], i
|
|
|
95
95
|
}
|
|
96
96
|
if (!value || typeof value !== "object") return;
|
|
97
97
|
const item = value as Record<string, unknown>;
|
|
98
|
-
const nested = item.results ?? item.items ?? item.list;
|
|
99
|
-
if (Array.isArray(nested)) appendSearchItems(nested, results, inlineContent);
|
|
100
98
|
const url = firstString(item.url, item.href, item.link);
|
|
101
99
|
if (!url) return;
|
|
102
100
|
const title = firstString(item.title, item.name) ?? url;
|
|
@@ -126,8 +124,11 @@ function parseSearchResponse(value: unknown): { results: SearchResponse["results
|
|
|
126
124
|
const envelope = value as Record<string, unknown>;
|
|
127
125
|
const results: SearchResponse["results"] = [];
|
|
128
126
|
const inlineContent: ExtractedContent[] = [];
|
|
129
|
-
|
|
130
|
-
|
|
127
|
+
const data = envelope.data;
|
|
128
|
+
const items = (typeof data === "object" && data !== null && !Array.isArray(data))
|
|
129
|
+
? (data as Record<string, unknown>).search
|
|
130
|
+
: data;
|
|
131
|
+
appendSearchItems(items, results, inlineContent);
|
|
131
132
|
return { results, inlineContent };
|
|
132
133
|
}
|
|
133
134
|
|
|
@@ -165,14 +166,13 @@ export function isKagiAvailable(): boolean {
|
|
|
165
166
|
export async function searchWithKagi(query: string, options: KagiSearchOptions = {}): Promise<SearchResponse> {
|
|
166
167
|
const apiKey = await requireApiKey(options.signal);
|
|
167
168
|
const numResults = normalizeCount(options.numResults);
|
|
168
|
-
const url = new URL(KAGI_SEARCH_URL);
|
|
169
|
-
url.searchParams.set("q", query);
|
|
170
|
-
url.searchParams.set("limit", String(numResults));
|
|
171
169
|
const activityId = activityMonitor.logStart({ type: "api", query });
|
|
172
170
|
let response: Response;
|
|
173
171
|
try {
|
|
174
|
-
response = await fetch(
|
|
175
|
-
|
|
172
|
+
response = await fetch(KAGI_SEARCH_URL, {
|
|
173
|
+
method: "POST",
|
|
174
|
+
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json", Accept: "application/json" },
|
|
175
|
+
body: JSON.stringify({ query, limit: numResults }),
|
|
176
176
|
signal: options.signal ? AbortSignal.any([AbortSignal.timeout(SEARCH_TIMEOUT_MS), options.signal]) : AbortSignal.timeout(SEARCH_TIMEOUT_MS),
|
|
177
177
|
});
|
|
178
178
|
} catch (err) {
|
|
@@ -228,8 +228,8 @@ export async function extractWithKagi(url: string, signal?: AbortSignal, options
|
|
|
228
228
|
try {
|
|
229
229
|
response = await fetchRemoteUrl(KAGI_EXTRACT_URL, {
|
|
230
230
|
method: "POST",
|
|
231
|
-
headers: { Authorization: `
|
|
232
|
-
body: JSON.stringify({
|
|
231
|
+
headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json", Accept: "application/json" },
|
|
232
|
+
body: JSON.stringify({ pages: [{ url }] }),
|
|
233
233
|
signal: signal ? AbortSignal.any([AbortSignal.timeout(options.timeoutMs ?? SEARCH_TIMEOUT_MS), signal]) : AbortSignal.timeout(options.timeoutMs ?? SEARCH_TIMEOUT_MS),
|
|
234
234
|
}, {
|
|
235
235
|
allowRanges: ssrf.allowRanges,
|
|
@@ -38,11 +38,13 @@ interface WebSearchConfig {
|
|
|
38
38
|
openaiSearchModel?: unknown;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
type ProviderHeaders = Record<string, string | null>;
|
|
42
|
+
|
|
41
43
|
interface OpenAIAuth {
|
|
42
44
|
provider: "openai-codex" | "openai";
|
|
43
45
|
apiKey: string;
|
|
44
46
|
model: string;
|
|
45
|
-
headers:
|
|
47
|
+
headers: ProviderHeaders;
|
|
46
48
|
responsesUrl: string;
|
|
47
49
|
}
|
|
48
50
|
|
|
@@ -155,6 +157,14 @@ function resolveConfiguredSearchModel(value: unknown): string | undefined {
|
|
|
155
157
|
return value.trim();
|
|
156
158
|
}
|
|
157
159
|
|
|
160
|
+
function toRequestHeaders(headers: ProviderHeaders): Record<string, string> {
|
|
161
|
+
const requestHeaders: Record<string, string> = {};
|
|
162
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
163
|
+
if (value !== null) requestHeaders[name] = value;
|
|
164
|
+
}
|
|
165
|
+
return requestHeaders;
|
|
166
|
+
}
|
|
167
|
+
|
|
158
168
|
async function resolvePiAuth(ctx: ExtensionContext, responsesUrl: string, modelOverride?: string): Promise<OpenAIAuth | undefined> {
|
|
159
169
|
let models: ReturnType<typeof ctx.modelRegistry.getAll>;
|
|
160
170
|
try {
|
|
@@ -407,7 +417,7 @@ export async function searchWithOpenAI(
|
|
|
407
417
|
|
|
408
418
|
const activityId = activityMonitor.logStart({ type: "api", query });
|
|
409
419
|
const headers: Record<string, string> = {
|
|
410
|
-
...auth.headers,
|
|
420
|
+
...toRequestHeaders(auth.headers),
|
|
411
421
|
Authorization: `Bearer ${auth.apiKey}`,
|
|
412
422
|
"Content-Type": "application/json",
|
|
413
423
|
"OpenAI-Beta": "responses=experimental",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-web-access",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Web search, URL fetching, GitHub repo cloning, PDF extraction, YouTube video understanding, and local video analysis for Pi coding agent. Supports OpenAI, Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, SERPdive, Kagi, Ollama, AnySearch, Bright Data SERP, SerpBase, SearXNG, Firecrawl extraction, Exa, Perplexity, and Gemini.",
|
|
3
|
+
"version": "0.22.0",
|
|
4
|
+
"description": "Web search, URL fetching, GitHub repo cloning, PDF extraction, YouTube video understanding, and local video analysis for Pi coding agent. Supports OpenAI, Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Jina, SERPdive, Kagi, Ollama, AnySearch, Bright Data SERP, SerpBase, SearXNG, Firecrawl extraction, Exa, Perplexity, and Gemini.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "node --test",
|
|
@@ -50,7 +50,8 @@
|
|
|
50
50
|
"promise.try": "^2.0.1",
|
|
51
51
|
"turndown": "^7.2.0",
|
|
52
52
|
"typebox": "^1.1.38",
|
|
53
|
-
"unpdf": "^1.6.2"
|
|
53
|
+
"unpdf": "^1.6.2",
|
|
54
|
+
"undici": "^8.9.0"
|
|
54
55
|
},
|
|
55
56
|
"peerDependencies": {
|
|
56
57
|
"@earendil-works/pi-ai": "*",
|