pi-free 2.2.1 → 2.2.2
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/CHANGELOG.md +16 -0
- package/config.ts +73 -38
- package/constants.ts +5 -0
- package/index.ts +16 -4
- package/package.json +1 -1
- package/providers/bai/bai.ts +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.2.2] - 2026-06-19
|
|
11
|
+
|
|
12
|
+
### Refactored
|
|
13
|
+
|
|
14
|
+
- `config.ts`: Introduced `PROVIDER_META` table that pairs each
|
|
15
|
+
provider's ID, env-var prefix, and typed config key. `getProviderShowPaid`
|
|
16
|
+
now delegates to a generic `resolveShowPaidForProvider` resolver
|
|
17
|
+
instead of a 17-case switch. New `PROVIDER_OPENROUTER`, `PROVIDER_OPENCODE`,
|
|
18
|
+
and `PROVIDER_FASTROUTER` constants added to `constants.ts` and used
|
|
19
|
+
in the table (DRYKISS DRY/Architecture findings).
|
|
20
|
+
- `index.ts`: Wrap dynamic built-in provider import in `try/catch`
|
|
21
|
+
with full error+stack logging to both `~/.pi/free.log` and stderr
|
|
22
|
+
(DRYKISS Resilience finding).
|
|
23
|
+
- `providers/bai/bai.ts`: Improve startup-failure message to point users
|
|
24
|
+
at `~/.pi/free.log` and their API key (DRYKISS Resilience finding).
|
|
25
|
+
|
|
10
26
|
## [2.2.1] - 2026-06-19
|
|
11
27
|
|
|
12
28
|
### Security
|
package/config.ts
CHANGED
|
@@ -11,11 +11,33 @@
|
|
|
11
11
|
|
|
12
12
|
import { chmodSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
13
13
|
import { join } from "node:path";
|
|
14
|
+
import {
|
|
15
|
+
PROVIDER_BAI,
|
|
16
|
+
PROVIDER_CLINE,
|
|
17
|
+
PROVIDER_FASTROUTER,
|
|
18
|
+
PROVIDER_KILO,
|
|
19
|
+
PROVIDER_OLLAMA,
|
|
20
|
+
PROVIDER_OPENCODE,
|
|
21
|
+
PROVIDER_OPENROUTER,
|
|
22
|
+
PROVIDER_ROUTEWAY,
|
|
23
|
+
PROVIDER_TOKENROUTER,
|
|
24
|
+
PROVIDER_ZENMUX,
|
|
25
|
+
PROVIDER_CROFAI,
|
|
26
|
+
PROVIDER_CODESTRAL,
|
|
27
|
+
PROVIDER_LLM7,
|
|
28
|
+
PROVIDER_DEEPINFRA,
|
|
29
|
+
PROVIDER_SAMBANOVA,
|
|
30
|
+
PROVIDER_TOGETHER,
|
|
31
|
+
PROVIDER_NOVITA,
|
|
32
|
+
} from "./constants.ts";
|
|
14
33
|
export {
|
|
15
34
|
PROVIDER_BAI,
|
|
16
35
|
PROVIDER_CLINE,
|
|
36
|
+
PROVIDER_FASTROUTER,
|
|
17
37
|
PROVIDER_KILO,
|
|
18
38
|
PROVIDER_MODAL,
|
|
39
|
+
PROVIDER_OPENCODE,
|
|
40
|
+
PROVIDER_OPENROUTER,
|
|
19
41
|
PROVIDER_QWEN,
|
|
20
42
|
PROVIDER_ROUTEWAY,
|
|
21
43
|
PROVIDER_TOKENROUTER,
|
|
@@ -219,6 +241,56 @@ function resolveBool(envKey: string, fileVal?: boolean): boolean {
|
|
|
219
241
|
return fileVal === true;
|
|
220
242
|
}
|
|
221
243
|
|
|
244
|
+
// =============================================================================
|
|
245
|
+
// Per-provider metadata table
|
|
246
|
+
// Adding a new provider only requires a single entry here plus the
|
|
247
|
+
// corresponding field in the PiFreeConfig interface and CONFIG_TEMPLATE.
|
|
248
|
+
// Each entry pairs the provider ID with its env-var prefix (used for both
|
|
249
|
+
// the API key and show_paid flag) and the typed key on PiFreeConfig.
|
|
250
|
+
// =============================================================================
|
|
251
|
+
|
|
252
|
+
interface ProviderMeta {
|
|
253
|
+
id: string;
|
|
254
|
+
/** Env var prefix, e.g. "KILO" => KILO_SHOW_PAID and KILO_API_KEY */
|
|
255
|
+
prefix: string;
|
|
256
|
+
/** Typed accessor returning the show_paid value from PiFreeConfig */
|
|
257
|
+
showPaidKey: keyof PiFreeConfig;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const PROVIDER_META: readonly ProviderMeta[] = [
|
|
261
|
+
{ id: PROVIDER_KILO, prefix: "KILO", showPaidKey: "kilo_show_paid" },
|
|
262
|
+
{ id: PROVIDER_CLINE, prefix: "CLINE", showPaidKey: "cline_show_paid" },
|
|
263
|
+
{ id: PROVIDER_ZENMUX, prefix: "ZENMUX", showPaidKey: "zenmux_show_paid" },
|
|
264
|
+
{ id: PROVIDER_CROFAI, prefix: "CROFAI", showPaidKey: "crofai_show_paid" },
|
|
265
|
+
{ id: PROVIDER_CODESTRAL, prefix: "CODESTRAL", showPaidKey: "codestral_show_paid" },
|
|
266
|
+
{ id: PROVIDER_LLM7, prefix: "LLM7", showPaidKey: "llm7_show_paid" },
|
|
267
|
+
{ id: PROVIDER_DEEPINFRA, prefix: "DEEPINFRA", showPaidKey: "deepinfra_show_paid" },
|
|
268
|
+
{ id: PROVIDER_SAMBANOVA, prefix: "SAMBANOVA", showPaidKey: "sambanova_show_paid" },
|
|
269
|
+
{ id: PROVIDER_TOGETHER, prefix: "TOGETHER", showPaidKey: "together_show_paid" },
|
|
270
|
+
{ id: PROVIDER_NOVITA, prefix: "NOVITA", showPaidKey: "novita_show_paid" },
|
|
271
|
+
{ id: PROVIDER_ROUTEWAY, prefix: "ROUTEWAY", showPaidKey: "routeway_show_paid" },
|
|
272
|
+
{ id: PROVIDER_TOKENROUTER, prefix: "TOKENROUTER", showPaidKey: "tokenrouter_show_paid" },
|
|
273
|
+
{ id: PROVIDER_BAI, prefix: "BAI", showPaidKey: "bai_show_paid" },
|
|
274
|
+
{ id: PROVIDER_FASTROUTER, prefix: "FASTROUTER", showPaidKey: "fastrouter_show_paid" },
|
|
275
|
+
{ id: PROVIDER_OLLAMA, prefix: "OLLAMA", showPaidKey: "ollama_show_paid" },
|
|
276
|
+
{ id: PROVIDER_OPENROUTER, prefix: "OPENROUTER", showPaidKey: "openrouter_show_paid" },
|
|
277
|
+
{ id: PROVIDER_OPENCODE, prefix: "OPENCODE", showPaidKey: "opencode_show_paid" },
|
|
278
|
+
];
|
|
279
|
+
|
|
280
|
+
const PROVIDER_META_BY_ID = new Map(PROVIDER_META.map((m) => [m.id, m]));
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Generic show_paid resolver backed by PROVIDER_META. Returns false
|
|
284
|
+
* for unknown provider IDs (matches the previous switch default).
|
|
285
|
+
*/
|
|
286
|
+
function resolveShowPaidForProvider(providerId: string): boolean {
|
|
287
|
+
const meta = PROVIDER_META_BY_ID.get(providerId);
|
|
288
|
+
if (!meta) return false;
|
|
289
|
+
const cfg = loadConfigFile();
|
|
290
|
+
const fileVal = cfg[meta.showPaidKey];
|
|
291
|
+
return resolveBool(`${meta.prefix}_SHOW_PAID`, fileVal as boolean | undefined);
|
|
292
|
+
}
|
|
293
|
+
|
|
222
294
|
// =============================================================================
|
|
223
295
|
// Per-provider paid-model flags (getters so toggles reflect immediately)
|
|
224
296
|
// =============================================================================
|
|
@@ -310,44 +382,7 @@ export function getOpencodeShowPaid(): boolean {
|
|
|
310
382
|
}
|
|
311
383
|
|
|
312
384
|
export function getProviderShowPaid(providerId: string): boolean {
|
|
313
|
-
|
|
314
|
-
case "kilo":
|
|
315
|
-
return getKiloShowPaid();
|
|
316
|
-
case "cline":
|
|
317
|
-
return getClineShowPaid();
|
|
318
|
-
case "zenmux":
|
|
319
|
-
return getZenmuxShowPaid();
|
|
320
|
-
case "crofai":
|
|
321
|
-
return getCrofaiShowPaid();
|
|
322
|
-
case "codestral":
|
|
323
|
-
return getCodestralShowPaid();
|
|
324
|
-
case "llm7":
|
|
325
|
-
return getLlm7ShowPaid();
|
|
326
|
-
case "deepinfra":
|
|
327
|
-
return getDeepinfraShowPaid();
|
|
328
|
-
case "sambanova":
|
|
329
|
-
return getSambanovaShowPaid();
|
|
330
|
-
case "together":
|
|
331
|
-
return getTogetherShowPaid();
|
|
332
|
-
case "novita":
|
|
333
|
-
return getNovitaShowPaid();
|
|
334
|
-
case "routeway":
|
|
335
|
-
return getRoutewayShowPaid();
|
|
336
|
-
case "tokenrouter":
|
|
337
|
-
return getTokenrouterShowPaid();
|
|
338
|
-
case "bai":
|
|
339
|
-
return getBaiShowPaid();
|
|
340
|
-
case "fastrouter":
|
|
341
|
-
return getFastrouterShowPaid();
|
|
342
|
-
case "ollama-cloud":
|
|
343
|
-
return getOllamaShowPaid();
|
|
344
|
-
case "openrouter":
|
|
345
|
-
return getOpenrouterShowPaid();
|
|
346
|
-
case "opencode":
|
|
347
|
-
return getOpencodeShowPaid();
|
|
348
|
-
default:
|
|
349
|
-
return false;
|
|
350
|
-
}
|
|
385
|
+
return resolveShowPaidForProvider(providerId);
|
|
351
386
|
}
|
|
352
387
|
|
|
353
388
|
// =============================================================================
|
package/constants.ts
CHANGED
|
@@ -26,6 +26,11 @@ export const PROVIDER_ROUTEWAY = "routeway";
|
|
|
26
26
|
export const PROVIDER_TOKENROUTER = "tokenrouter";
|
|
27
27
|
export const PROVIDER_BAI = "bai";
|
|
28
28
|
|
|
29
|
+
// Built-in pi providers that pi-free wraps with toggles
|
|
30
|
+
export const PROVIDER_OPENROUTER = "openrouter";
|
|
31
|
+
export const PROVIDER_OPENCODE = "opencode";
|
|
32
|
+
export const PROVIDER_FASTROUTER = "fastrouter";
|
|
33
|
+
|
|
29
34
|
export const ALL_UNIQUE_PROVIDERS = [
|
|
30
35
|
PROVIDER_KILO,
|
|
31
36
|
PROVIDER_CLINE,
|
package/index.ts
CHANGED
|
@@ -379,10 +379,22 @@ export default async function piFreeEntry(pi: ExtensionAPI) {
|
|
|
379
379
|
|
|
380
380
|
// Setup dynamic built-in providers (Mistral, Groq, Cerebras, xAI, Hugging Face,
|
|
381
381
|
// OpenRouter/OpenCode from Pi auth, and FastRouter public model discovery)
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
382
|
+
try {
|
|
383
|
+
const { setupDynamicBuiltInProviders } = await import(
|
|
384
|
+
"./providers/dynamic-built-in/index.ts"
|
|
385
|
+
);
|
|
386
|
+
await setupDynamicBuiltInProviders(pi);
|
|
387
|
+
} catch (err) {
|
|
388
|
+
// Dynamic providers are a best-effort enhancement — if the import
|
|
389
|
+
// or init fails (e.g. upstream API change), continue with the
|
|
390
|
+
// already-registered static providers rather than failing the whole
|
|
391
|
+
// extension load. Log full error (message + stack) to the structured
|
|
392
|
+
// log so the user can investigate, but never block startup.
|
|
393
|
+
_logger.error("[pi-free] Dynamic built-in providers failed to load", {
|
|
394
|
+
error: err instanceof Error ? err.message : String(err),
|
|
395
|
+
stack: err instanceof Error ? err.stack : undefined,
|
|
396
|
+
});
|
|
397
|
+
}
|
|
386
398
|
|
|
387
399
|
// Setup toggles for pi's built-in providers (e.g., OpenCode)
|
|
388
400
|
setupBuiltInProviderToggles(pi);
|
package/package.json
CHANGED
package/providers/bai/bai.ts
CHANGED
|
@@ -184,7 +184,12 @@ export default async function baiProvider(pi: ExtensionAPI) {
|
|
|
184
184
|
const allModels = await fetchBaiModels(apiKey);
|
|
185
185
|
|
|
186
186
|
if (allModels.length === 0) {
|
|
187
|
-
|
|
187
|
+
// Either the API failed (already logged inside fetchBaiModels) or
|
|
188
|
+
// the API returned zero text chat models. We can't tell the user
|
|
189
|
+
// which, but we can give a hint to check the log / their key.
|
|
190
|
+
_logger.warn(
|
|
191
|
+
"[bai] No text chat models available — verify BAI_API_KEY is valid and see ~/.pi/free.log for details",
|
|
192
|
+
);
|
|
188
193
|
return;
|
|
189
194
|
}
|
|
190
195
|
|