@pi-unipi/web-api 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/package.json +1 -1
- package/src/commands.ts +10 -12
- package/src/tui/settings-dialog.ts +62 -47
package/README.md
CHANGED
|
@@ -128,6 +128,9 @@ web_llm_summarize(url: "https://example.com/research", prompt: "Extract key find
|
|
|
128
128
|
|
|
129
129
|
Interactive settings dialog for managing providers and API keys.
|
|
130
130
|
|
|
131
|
+
- **Auto-enable on key input** — provider is automatically enabled when you add a valid API key (no extra toggle step)
|
|
132
|
+
- **Cursor memory** — last configured provider moves to the top of the list when you return to the menu
|
|
133
|
+
|
|
131
134
|
### /unipi:web-cache-clear
|
|
132
135
|
|
|
133
136
|
Clear all cached web content.
|
|
@@ -145,8 +148,8 @@ Clear all cached web content.
|
|
|
145
148
|
If you see "No search provider available":
|
|
146
149
|
|
|
147
150
|
1. Run `/unipi:web-settings`
|
|
148
|
-
2.
|
|
149
|
-
3.
|
|
151
|
+
2. Add API keys for paid providers (they auto-enable on key input)
|
|
152
|
+
3. Or manually enable a free provider
|
|
150
153
|
|
|
151
154
|
### API key invalid
|
|
152
155
|
|
package/package.json
CHANGED
package/src/commands.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Registers /unipi:web-settings and /unipi:web-cache-clear commands.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
7
|
+
import type { ExtensionAPI, ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
|
|
8
8
|
import { UNIPI_PREFIX } from "@pi-unipi/core";
|
|
9
9
|
import { showSettingsDialog } from "./tui/settings-dialog.js";
|
|
10
10
|
import { webCache } from "./cache.js";
|
|
@@ -20,26 +20,24 @@ export const WEB_COMMANDS = {
|
|
|
20
20
|
*/
|
|
21
21
|
export function registerWebCommands(pi: ExtensionAPI): void {
|
|
22
22
|
// --- /unipi:web-settings command ---
|
|
23
|
-
pi.registerCommand({
|
|
24
|
-
name: `${UNIPI_PREFIX}${WEB_COMMANDS.SETTINGS}`,
|
|
23
|
+
pi.registerCommand(`${UNIPI_PREFIX}${WEB_COMMANDS.SETTINGS}`, {
|
|
25
24
|
description: "Configure web API providers and API keys",
|
|
26
|
-
handler: async (_args,
|
|
27
|
-
await showSettingsDialog(
|
|
25
|
+
handler: async (_args, ctx) => {
|
|
26
|
+
await showSettingsDialog(ctx);
|
|
28
27
|
},
|
|
29
28
|
});
|
|
30
29
|
|
|
31
30
|
// --- /unipi:web-cache-clear command ---
|
|
32
|
-
pi.registerCommand({
|
|
33
|
-
name: `${UNIPI_PREFIX}${WEB_COMMANDS.CACHE_CLEAR}`,
|
|
31
|
+
pi.registerCommand(`${UNIPI_PREFIX}${WEB_COMMANDS.CACHE_CLEAR}`, {
|
|
34
32
|
description: "Clear all cached web content",
|
|
35
|
-
handler: async (_args,
|
|
33
|
+
handler: async (_args, ctx) => {
|
|
36
34
|
const stats = webCache.getStats();
|
|
37
35
|
const cleared = webCache.clear();
|
|
38
36
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
ctx.ui.notify(
|
|
38
|
+
`Cache cleared: ${cleared} entries removed (${stats.totalSizeBytes} bytes freed)`,
|
|
39
|
+
"info",
|
|
40
|
+
);
|
|
43
41
|
},
|
|
44
42
|
});
|
|
45
43
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Uses pi's TUI components for provider selection and key input.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type {
|
|
8
|
+
import type { ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
|
|
9
9
|
import { registry } from "../providers/registry.js";
|
|
10
10
|
import {
|
|
11
11
|
getApiKey,
|
|
@@ -21,8 +21,9 @@ import { getProviderOptions, getProviderStatuses } from "./provider-selector.js"
|
|
|
21
21
|
* Show settings dialog.
|
|
22
22
|
* This is the main entry point for /unipi:web-settings command.
|
|
23
23
|
*/
|
|
24
|
-
export async function showSettingsDialog(
|
|
24
|
+
export async function showSettingsDialog(ctx: ExtensionCommandContext): Promise<void> {
|
|
25
25
|
let running = true;
|
|
26
|
+
let lastSelected: string | undefined;
|
|
26
27
|
|
|
27
28
|
while (running) {
|
|
28
29
|
const options = getProviderOptions();
|
|
@@ -34,20 +35,37 @@ export async function showSettingsDialog(pi: ExtensionAPI): Promise<void> {
|
|
|
34
35
|
description: "Exit settings",
|
|
35
36
|
});
|
|
36
37
|
|
|
38
|
+
// Move last selected provider to top of list for quick re-entry
|
|
39
|
+
if (lastSelected && lastSelected !== "__exit__") {
|
|
40
|
+
const idx = options.findIndex(o => o.value === lastSelected);
|
|
41
|
+
if (idx > 0) {
|
|
42
|
+
const [item] = options.splice(idx, 1);
|
|
43
|
+
options.unshift(item);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
37
47
|
// Show provider list
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
48
|
+
const labels = options.map(o => o.value === "__exit__" ? o.label : `${o.label} — ${o.description}`);
|
|
49
|
+
const selected = await ctx.ui.select(
|
|
50
|
+
"Web API Settings",
|
|
51
|
+
labels,
|
|
52
|
+
);
|
|
53
|
+
// Map label back to value
|
|
54
|
+
const selectedOpt = options.find(o => {
|
|
55
|
+
const full = o.value === "__exit__" ? o.label : `${o.label} — ${o.description}`;
|
|
56
|
+
return full === selected;
|
|
42
57
|
});
|
|
58
|
+
const selectedValue = selectedOpt?.value;
|
|
43
59
|
|
|
44
|
-
if (!
|
|
60
|
+
if (!selectedValue || selectedValue === "__exit__") {
|
|
45
61
|
running = false;
|
|
46
62
|
continue;
|
|
47
63
|
}
|
|
48
64
|
|
|
65
|
+
lastSelected = selectedValue;
|
|
66
|
+
|
|
49
67
|
// Show provider configuration
|
|
50
|
-
await configureProvider(
|
|
68
|
+
await configureProvider(ctx, selectedValue);
|
|
51
69
|
}
|
|
52
70
|
}
|
|
53
71
|
|
|
@@ -55,15 +73,15 @@ export async function showSettingsDialog(pi: ExtensionAPI): Promise<void> {
|
|
|
55
73
|
* Configure a specific provider.
|
|
56
74
|
*/
|
|
57
75
|
async function configureProvider(
|
|
58
|
-
|
|
76
|
+
ctx: ExtensionCommandContext,
|
|
59
77
|
providerId: string
|
|
60
78
|
): Promise<void> {
|
|
61
79
|
const provider = registry.getProvider(providerId);
|
|
62
80
|
if (!provider) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
81
|
+
ctx.ui.notify(
|
|
82
|
+
`Provider "${providerId}" not found`,
|
|
83
|
+
"error",
|
|
84
|
+
);
|
|
67
85
|
return;
|
|
68
86
|
}
|
|
69
87
|
|
|
@@ -110,32 +128,34 @@ async function configureProvider(
|
|
|
110
128
|
description: "Return to provider list",
|
|
111
129
|
});
|
|
112
130
|
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
131
|
+
const labels = options.map(o => `${o.label} — ${o.description}`);
|
|
132
|
+
const selected = await ctx.ui.select(
|
|
133
|
+
`Configure ${provider.name} (${provider.capabilities.join(", ")})`,
|
|
134
|
+
labels,
|
|
135
|
+
);
|
|
136
|
+
const selectedOpt = options.find(o => `${o.label} — ${o.description}` === selected);
|
|
137
|
+
const selectedValue = selectedOpt?.value;
|
|
118
138
|
|
|
119
|
-
switch (
|
|
139
|
+
switch (selectedValue) {
|
|
120
140
|
case "__toggle__":
|
|
121
141
|
setProviderEnabled(providerId, !enabled);
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
142
|
+
ctx.ui.notify(
|
|
143
|
+
`${provider.name} ${!enabled ? "enabled" : "disabled"}`,
|
|
144
|
+
"info",
|
|
145
|
+
);
|
|
126
146
|
break;
|
|
127
147
|
|
|
128
148
|
case "__add_key__":
|
|
129
149
|
case "__update_key__":
|
|
130
|
-
await inputApiKey(
|
|
150
|
+
await inputApiKey(ctx, providerId, provider.name);
|
|
131
151
|
break;
|
|
132
152
|
|
|
133
153
|
case "__remove_key__":
|
|
134
154
|
removeApiKey(providerId);
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
155
|
+
ctx.ui.notify(
|
|
156
|
+
`API key removed for ${provider.name}`,
|
|
157
|
+
"info",
|
|
158
|
+
);
|
|
139
159
|
break;
|
|
140
160
|
|
|
141
161
|
case "__back__":
|
|
@@ -148,30 +168,25 @@ async function configureProvider(
|
|
|
148
168
|
* Input API key for a provider.
|
|
149
169
|
*/
|
|
150
170
|
async function inputApiKey(
|
|
151
|
-
|
|
171
|
+
ctx: ExtensionCommandContext,
|
|
152
172
|
providerId: string,
|
|
153
173
|
providerName: string
|
|
154
174
|
): Promise<void> {
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (!value || value.trim().length === 0) {
|
|
161
|
-
return "API key cannot be empty";
|
|
162
|
-
}
|
|
163
|
-
if (!validateApiKeyFormat(providerId, value)) {
|
|
164
|
-
return "API key format looks invalid";
|
|
165
|
-
}
|
|
166
|
-
return null;
|
|
167
|
-
},
|
|
168
|
-
});
|
|
175
|
+
const envHint = registry.getProvider(providerId)?.apiKeyEnv || "N/A";
|
|
176
|
+
const apiKey = await ctx.ui.input(
|
|
177
|
+
`API Key for ${providerName} (env: ${envHint})`,
|
|
178
|
+
"sk-...",
|
|
179
|
+
);
|
|
169
180
|
|
|
170
181
|
if (apiKey) {
|
|
171
182
|
setApiKey(providerId, apiKey);
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
183
|
+
// Auto-enable provider on successful key input
|
|
184
|
+
if (!isProviderEnabled(providerId)) {
|
|
185
|
+
setProviderEnabled(providerId, true);
|
|
186
|
+
}
|
|
187
|
+
ctx.ui.notify(
|
|
188
|
+
`API key saved for ${providerName} — enabled`,
|
|
189
|
+
"info",
|
|
190
|
+
);
|
|
176
191
|
}
|
|
177
192
|
}
|