@jmcombs/pi-tavily-search 2.0.0 → 2.1.0
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/index.ts +48 -23
- package/package.json +2 -2
package/index.ts
CHANGED
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
* @jmcombs/pi-tavily-search — Real-time web search for the Pi coding agent.
|
|
3
3
|
*
|
|
4
4
|
* Registers a `tavily_search` tool that the LLM can call to perform a Tavily
|
|
5
|
-
* web search.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* web search. If no Tavily API key is configured, the tool prompts the user
|
|
6
|
+
* interactively via the TUI (never leaking the key into the agent's context).
|
|
7
|
+
* The key can also be set manually by running `/tavily_authenticate`.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* Supported configuration (if not using interactive prompt):
|
|
10
|
+
* 1. `AuthStorage` under the "tavily" key (`~/.pi/agent/auth.json`)
|
|
11
|
+
* 2. The `TAVILY_API_KEY` environment variable
|
|
11
12
|
*/
|
|
12
13
|
|
|
13
|
-
import { AuthStorage, type ExtensionAPI } from "@
|
|
14
|
+
import { AuthStorage, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
14
15
|
import { Type, type Static } from "typebox";
|
|
15
16
|
|
|
16
17
|
const TAVILY_SEARCH_ENDPOINT = "https://api.tavily.com/search";
|
|
@@ -48,16 +49,6 @@ interface TavilySearchResponse {
|
|
|
48
49
|
|
|
49
50
|
// ── Helpers ────────────────────────────────────────────────────────────
|
|
50
51
|
|
|
51
|
-
const MISSING_KEY_MESSAGE = [
|
|
52
|
-
"Error: No Tavily API key configured.",
|
|
53
|
-
"",
|
|
54
|
-
"Configure one of the following:",
|
|
55
|
-
" • Environment variable: export TAVILY_API_KEY=<your-key>",
|
|
56
|
-
' • ~/.pi/agent/auth.json: { "tavily": { "type": "api_key", "key": "<your-key>" } }',
|
|
57
|
-
' • Shell-resolved: { "tavily": { "type": "api_key", "key": "!security find-generic-password -ws tavily" } }',
|
|
58
|
-
' • 1Password: { "tavily": { "type": "api_key", "key": "!op read \'op://Personal/tavily/credential\'" } }',
|
|
59
|
-
].join("\n");
|
|
60
|
-
|
|
61
52
|
function formatResults(data: TavilySearchResponse, query: string): string {
|
|
62
53
|
const results = data.results ?? [];
|
|
63
54
|
if (results.length === 0) {
|
|
@@ -77,20 +68,54 @@ function formatResults(data: TavilySearchResponse, query: string): string {
|
|
|
77
68
|
export default function (pi: ExtensionAPI): void {
|
|
78
69
|
const authStorage = AuthStorage.create();
|
|
79
70
|
|
|
71
|
+
// Register /tavily_authenticate command for manual key entry.
|
|
72
|
+
// The input is captured by the TUI and never enters the LLM's context.
|
|
73
|
+
pi.registerCommand("tavily_authenticate", {
|
|
74
|
+
description: "Securely save your Tavily API key (input never visible to LLM).",
|
|
75
|
+
handler: async (_args, ctx) => {
|
|
76
|
+
const apiKey = await ctx.ui.input("Enter your Tavily API key:");
|
|
77
|
+
if (apiKey) {
|
|
78
|
+
authStorage.set("tavily", { type: "api_key" as const, key: apiKey });
|
|
79
|
+
ctx.ui.notify("Tavily API key saved successfully.", "info");
|
|
80
|
+
} else {
|
|
81
|
+
ctx.ui.notify("Authentication cancelled.", "warning");
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
80
86
|
pi.registerTool({
|
|
81
87
|
name: "tavily_search",
|
|
82
88
|
label: "Tavily Web Search",
|
|
83
89
|
description:
|
|
84
90
|
"Performs a web search using the Tavily API to get real-time information from the internet.",
|
|
85
91
|
parameters: tavilySearchSchema,
|
|
86
|
-
async execute(_toolCallId, params, signal, _onUpdate,
|
|
87
|
-
|
|
92
|
+
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
93
|
+
let apiKey = (await authStorage.getApiKey("tavily")) ?? process.env.TAVILY_API_KEY;
|
|
94
|
+
|
|
95
|
+
// Auto-authenticate: prompt for key if none is configured
|
|
88
96
|
if (!apiKey) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
97
|
+
const newKey = await ctx.ui.input("Enter your Tavily API key:");
|
|
98
|
+
if (!newKey) {
|
|
99
|
+
return {
|
|
100
|
+
content: [{ type: "text", text: "Search cancelled: no Tavily API key provided." }],
|
|
101
|
+
details: { error: "missing_api_key" },
|
|
102
|
+
isError: true,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
authStorage.set("tavily", { type: "api_key" as const, key: newKey });
|
|
106
|
+
apiKey = await authStorage.getApiKey("tavily");
|
|
107
|
+
if (!apiKey) {
|
|
108
|
+
return {
|
|
109
|
+
content: [
|
|
110
|
+
{
|
|
111
|
+
type: "text",
|
|
112
|
+
text: "Failed to resolve Tavily API key. Check your shell configuration.",
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
details: { error: "missing_api_key" },
|
|
116
|
+
isError: true,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
94
119
|
}
|
|
95
120
|
|
|
96
121
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jmcombs/pi-tavily-search",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Pi extension that performs real-time web search via the Tavily API.",
|
|
5
5
|
"homepage": "https://github.com/jmcombs/pi-extensions/tree/main/packages/tavily-search",
|
|
6
6
|
"repository": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"node": ">=22.0.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@
|
|
42
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
43
43
|
"typebox": "*"
|
|
44
44
|
}
|
|
45
45
|
}
|