@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 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. Enable at least one provider
149
- 3. Add API keys for paid providers
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/web-api",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Web search, read, and summarize tools with provider-based backend selection for Pi coding agent",
5
5
  "type": "module",
6
6
  "license": "MIT",
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, _ctx) => {
27
- await showSettingsDialog(pi);
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, _ctx) => {
33
+ handler: async (_args, ctx) => {
36
34
  const stats = webCache.getStats();
37
35
  const cleared = webCache.clear();
38
36
 
39
- await pi.ui.notify({
40
- message: `Cache cleared: ${cleared} entries removed (${stats.totalSizeBytes} bytes freed)`,
41
- level: "success",
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 { ExtensionAPI } from "@mariozechner/pi-coding-agent";
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(pi: ExtensionAPI): Promise<void> {
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 selected = await pi.ui.select({
39
- title: "Web API Settings",
40
- message: "Select a provider to configure:",
41
- options,
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 (!selected || selected === "__exit__") {
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(pi, selected);
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
- pi: ExtensionAPI,
76
+ ctx: ExtensionCommandContext,
59
77
  providerId: string
60
78
  ): Promise<void> {
61
79
  const provider = registry.getProvider(providerId);
62
80
  if (!provider) {
63
- await pi.ui.notify({
64
- message: `Provider "${providerId}" not found`,
65
- level: "error",
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 selected = await pi.ui.select({
114
- title: `Configure ${provider.name}`,
115
- message: `Capabilities: ${provider.capabilities.join(", ")}`,
116
- options,
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 (selected) {
139
+ switch (selectedValue) {
120
140
  case "__toggle__":
121
141
  setProviderEnabled(providerId, !enabled);
122
- await pi.ui.notify({
123
- message: `${provider.name} ${!enabled ? "enabled" : "disabled"}`,
124
- level: "success",
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(pi, providerId, provider.name);
150
+ await inputApiKey(ctx, providerId, provider.name);
131
151
  break;
132
152
 
133
153
  case "__remove_key__":
134
154
  removeApiKey(providerId);
135
- await pi.ui.notify({
136
- message: `API key removed for ${provider.name}`,
137
- level: "success",
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
- pi: ExtensionAPI,
171
+ ctx: ExtensionCommandContext,
152
172
  providerId: string,
153
173
  providerName: string
154
174
  ): Promise<void> {
155
- const apiKey = await pi.ui.input({
156
- title: `API Key for ${providerName}`,
157
- message: `Enter API key (env: ${registry.getProvider(providerId)?.apiKeyEnv || "N/A"}):`,
158
- placeholder: "sk-...",
159
- validate: async (value: string) => {
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
- await pi.ui.notify({
173
- message: `API key saved for ${providerName}`,
174
- level: "success",
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
  }