@pi-unipi/web-api 0.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.
@@ -0,0 +1,177 @@
1
+ /**
2
+ * @unipi/web-api — Settings TUI dialog
3
+ *
4
+ * Interactive TUI for API key management.
5
+ * Uses pi's TUI components for provider selection and key input.
6
+ */
7
+
8
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
9
+ import { registry } from "../providers/registry.js";
10
+ import {
11
+ getApiKey,
12
+ setApiKey,
13
+ removeApiKey,
14
+ isProviderEnabled,
15
+ setProviderEnabled,
16
+ validateApiKeyFormat,
17
+ } from "../settings.js";
18
+ import { getProviderOptions, getProviderStatuses } from "./provider-selector.js";
19
+
20
+ /**
21
+ * Show settings dialog.
22
+ * This is the main entry point for /unipi:web-settings command.
23
+ */
24
+ export async function showSettingsDialog(pi: ExtensionAPI): Promise<void> {
25
+ let running = true;
26
+
27
+ while (running) {
28
+ const options = getProviderOptions();
29
+
30
+ // Add exit option
31
+ options.push({
32
+ label: "← Back",
33
+ value: "__exit__",
34
+ description: "Exit settings",
35
+ });
36
+
37
+ // Show provider list
38
+ const selected = await pi.ui.select({
39
+ title: "Web API Settings",
40
+ message: "Select a provider to configure:",
41
+ options,
42
+ });
43
+
44
+ if (!selected || selected === "__exit__") {
45
+ running = false;
46
+ continue;
47
+ }
48
+
49
+ // Show provider configuration
50
+ await configureProvider(pi, selected);
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Configure a specific provider.
56
+ */
57
+ async function configureProvider(
58
+ pi: ExtensionAPI,
59
+ providerId: string
60
+ ): Promise<void> {
61
+ const provider = registry.getProvider(providerId);
62
+ if (!provider) {
63
+ await pi.ui.notify({
64
+ message: `Provider "${providerId}" not found`,
65
+ level: "error",
66
+ });
67
+ return;
68
+ }
69
+
70
+ const hasApiKey = !!getApiKey(providerId);
71
+ const enabled = isProviderEnabled(providerId);
72
+
73
+ const options = [];
74
+
75
+ // Toggle enable/disable
76
+ options.push({
77
+ label: enabled ? "✓ Enabled" : "✗ Disabled",
78
+ value: "__toggle__",
79
+ description: enabled
80
+ ? "Click to disable this provider"
81
+ : "Click to enable this provider",
82
+ });
83
+
84
+ // API key management (only for providers that require it)
85
+ if (provider.requiresApiKey) {
86
+ if (hasApiKey) {
87
+ options.push({
88
+ label: "🔑 Update API Key",
89
+ value: "__update_key__",
90
+ description: "Update the API key",
91
+ });
92
+ options.push({
93
+ label: "🗑️ Remove API Key",
94
+ value: "__remove_key__",
95
+ description: "Remove the stored API key",
96
+ });
97
+ } else {
98
+ options.push({
99
+ label: "🔑 Add API Key",
100
+ value: "__add_key__",
101
+ description: "Add an API key for this provider",
102
+ });
103
+ }
104
+ }
105
+
106
+ // Back option
107
+ options.push({
108
+ label: "← Back",
109
+ value: "__back__",
110
+ description: "Return to provider list",
111
+ });
112
+
113
+ const selected = await pi.ui.select({
114
+ title: `Configure ${provider.name}`,
115
+ message: `Capabilities: ${provider.capabilities.join(", ")}`,
116
+ options,
117
+ });
118
+
119
+ switch (selected) {
120
+ case "__toggle__":
121
+ setProviderEnabled(providerId, !enabled);
122
+ await pi.ui.notify({
123
+ message: `${provider.name} ${!enabled ? "enabled" : "disabled"}`,
124
+ level: "success",
125
+ });
126
+ break;
127
+
128
+ case "__add_key__":
129
+ case "__update_key__":
130
+ await inputApiKey(pi, providerId, provider.name);
131
+ break;
132
+
133
+ case "__remove_key__":
134
+ removeApiKey(providerId);
135
+ await pi.ui.notify({
136
+ message: `API key removed for ${provider.name}`,
137
+ level: "success",
138
+ });
139
+ break;
140
+
141
+ case "__back__":
142
+ default:
143
+ break;
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Input API key for a provider.
149
+ */
150
+ async function inputApiKey(
151
+ pi: ExtensionAPI,
152
+ providerId: string,
153
+ providerName: string
154
+ ): 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
+ });
169
+
170
+ if (apiKey) {
171
+ setApiKey(providerId, apiKey);
172
+ await pi.ui.notify({
173
+ message: `API key saved for ${providerName}`,
174
+ level: "success",
175
+ });
176
+ }
177
+ }