pi-web-scout 0.1.0 → 0.1.1

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 CHANGED
@@ -10,6 +10,12 @@ No-key web search extension for [Pi](https://pi.dev). It registers a `web_search
10
10
  - No credential command execution.
11
11
  - Provider architecture ready for future keyed APIs such as Brave, Serper, Tavily, Exa, etc.
12
12
 
13
+ ## Install
14
+
15
+ ```bash
16
+ pi install npm:pi-web-scout
17
+ ```
18
+
13
19
  ## Try locally
14
20
 
15
21
  ```bash
@@ -89,6 +95,18 @@ The extension currently reads project config only and does not write config file
89
95
 
90
96
  `mode: "combine"` queries all enabled providers in the fallback chain, deduplicates URLs, and ranks repeated results higher with a simple reciprocal-rank score.
91
97
 
98
+ ## Compatibility aliases
99
+
100
+ `web_search` accepts these argument aliases:
101
+
102
+ - `maxResults` → `max_results`
103
+ - `provider: "ddg"` → `"duckduckgo"`
104
+ - `mode: "first-success"` → `"first_success"`
105
+
106
+ `web_read` accepts:
107
+
108
+ - `maxChars` → `max_chars`
109
+
92
110
  ## Security notes
93
111
 
94
112
  This package intentionally avoids:
package/SECURITY.md CHANGED
@@ -37,7 +37,7 @@ PI_WEB_SCOUT_BRAVE_API_KEY
37
37
 
38
38
  ## SSRF notes
39
39
 
40
- `web_read` blocks common local/private/metadata targets and validates every redirect hop. It does not perform custom DNS resolution, so DNS rebinding protection is best-effort.
40
+ `web_read` blocks common local/private/metadata targets and validates every redirect hop. It does not perform custom DNS resolution, so DNS rebinding protection is best-effort and DNS rebinding is not fully blocked.
41
41
 
42
42
  ## Reporting
43
43
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-web-scout",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "No-key web search extension for Pi, with provider architecture ready for keyed search APIs.",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
package/src/format.ts CHANGED
@@ -13,7 +13,8 @@ export function formatSearchResults(
13
13
  lines.push("", "Provider runs:");
14
14
  for (const run of runs) {
15
15
  const status = run.error ? `failed: ${run.error}` : `${run.results.length} result${run.results.length === 1 ? "" : "s"}`;
16
- lines.push(`- ${run.provider}: ${status}`);
16
+ const suffix = run.keySource ? ` (${run.keySource})` : "";
17
+ lines.push(`- ${run.provider}: ${status}${suffix}`);
17
18
  }
18
19
  }
19
20
  lines.push("");
@@ -28,7 +28,10 @@ export const jinaProvider: SearchProvider = {
28
28
  });
29
29
 
30
30
  if (!response.ok) {
31
- throw new Error(`Jina returned HTTP ${response.status}`);
31
+ const hint = response.status === 401 || response.status === 403
32
+ ? " (Jina may require an API key or may be rate-limiting no-key requests)"
33
+ : "";
34
+ throw new Error(`Jina returned HTTP ${response.status}${hint}`);
32
35
  }
33
36
 
34
37
  return parseJinaResponse(await response.json(), request.maxResults);
package/src/tool.ts CHANGED
@@ -12,20 +12,39 @@ import type { ProviderId, ProviderRunResult, ResolvedConfig, SearchMode, SearchR
12
12
  const PROVIDERS = ["auto", "duckduckgo", "marginalia", "jina", "brave"] as const;
13
13
  const MODES = ["first_success", "combine"] as const;
14
14
 
15
+ function normalizeSearchArgs(args: unknown): unknown {
16
+ if (!args || typeof args !== "object" || Array.isArray(args)) return args;
17
+ const next = { ...(args as Record<string, unknown>) };
18
+ if (next.max_results === undefined && next.maxResults !== undefined) next.max_results = next.maxResults;
19
+ if (next.provider === "ddg") next.provider = "duckduckgo";
20
+ if (next.mode === "first-success") next.mode = "first_success";
21
+ return next;
22
+ }
23
+
24
+ function normalizeReadArgs(args: unknown): unknown {
25
+ if (!args || typeof args !== "object" || Array.isArray(args)) return args;
26
+ const next = { ...(args as Record<string, unknown>) };
27
+ if (next.max_chars === undefined && next.maxChars !== undefined) next.max_chars = next.maxChars;
28
+ return next;
29
+ }
30
+
15
31
  export function registerWebSearchTool(pi: ExtensionAPI): void {
16
32
  registerStatusCommand(pi);
17
33
 
18
34
  pi.registerTool({
19
35
  name: "web_search",
20
- label: "Web Scout Search",
36
+ label: "Pi Web Scout Search",
21
37
  description:
22
- "Search the web using no-key search providers. Defaults to DuckDuckGo HTML and is architected for future keyed providers.",
38
+ "pi-web-scout: search the web using no-key search providers. Defaults to DuckDuckGo HTML and is architected for future keyed providers.",
23
39
  promptSnippet: "web_search: search the public web without requiring API keys.",
24
40
  promptGuidelines: [
25
41
  "Use web_search when current or source-backed information is needed.",
26
42
  "Prefer web_search over guessing facts that may have changed recently.",
27
43
  "Use web_search with mode=combine when broad coverage matters more than speed.",
28
44
  ],
45
+ prepareArguments(args) {
46
+ return normalizeSearchArgs(args);
47
+ },
29
48
  parameters: Type.Object({
30
49
  query: Type.String({ description: "Search query" }),
31
50
  max_results: Type.Optional(Type.Number({ description: "Number of results, 1-20. Default comes from config or 5." })),
@@ -68,6 +87,7 @@ export function registerWebSearchTool(pi: ExtensionAPI): void {
68
87
  provider: run.provider,
69
88
  resultCount: run.results.length,
70
89
  error: run.error,
90
+ keySource: run.keySource,
71
91
  })),
72
92
  results: runResult.results,
73
93
  },
@@ -77,13 +97,16 @@ export function registerWebSearchTool(pi: ExtensionAPI): void {
77
97
 
78
98
  pi.registerTool({
79
99
  name: "web_read",
80
- label: "Web Read",
81
- description: "Fetch a public HTTP(S) URL and extract readable text with SSRF protection. No browser or JavaScript execution.",
100
+ label: "Pi Web Scout Read",
101
+ description: "pi-web-scout: fetch a public HTTP(S) URL and extract readable text with SSRF protection. No browser or JavaScript execution.",
82
102
  promptSnippet: "web_read: read and extract text from a specific public URL.",
83
103
  promptGuidelines: [
84
104
  "Use web_read after web_search when snippets are insufficient and a source needs to be read.",
85
105
  "Do not use web_read for localhost, private network, or metadata URLs; those are blocked.",
86
106
  ],
107
+ prepareArguments(args) {
108
+ return normalizeReadArgs(args);
109
+ },
87
110
  parameters: Type.Object({
88
111
  url: Type.String({ description: "Public HTTP(S) URL to read" }),
89
112
  max_chars: Type.Optional(Type.Number({ description: "Maximum extracted characters, 1000-50000. Default 12000." })),