pi-free 2.0.7 → 2.0.9

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.
Files changed (42) hide show
  1. package/CHANGELOG.md +96 -10
  2. package/README.md +572 -495
  3. package/config.ts +58 -11
  4. package/constants.ts +12 -0
  5. package/index.ts +67 -3
  6. package/lib/built-in-toggle.ts +2 -2
  7. package/lib/model-detection.ts +2 -1
  8. package/lib/model-enhancer.ts +1 -1
  9. package/lib/open-browser.ts +1 -1
  10. package/lib/provider-compat.ts +1 -1
  11. package/lib/quota-monitor.ts +123 -0
  12. package/lib/registry.ts +1 -1
  13. package/lib/types.ts +101 -101
  14. package/lib/util.ts +460 -351
  15. package/package.json +4 -4
  16. package/provider-failover/benchmark-lookup.ts +743 -702
  17. package/provider-failover/benchmarks-chunk-0.ts +48 -48
  18. package/provider-failover/benchmarks-chunk-1.ts +44 -44
  19. package/provider-failover/benchmarks-chunk-2.ts +39 -39
  20. package/provider-failover/benchmarks-chunk-3.ts +41 -41
  21. package/provider-failover/benchmarks-chunk-4.ts +33 -33
  22. package/provider-helper.ts +1 -1
  23. package/providers/cline/cline-auth.ts +473 -473
  24. package/providers/cline/cline-models.ts +2 -2
  25. package/providers/cline/cline.ts +3 -3
  26. package/providers/codestral/codestral.ts +139 -0
  27. package/providers/crofai/crofai.ts +14 -85
  28. package/providers/deepinfra/deepinfra.ts +109 -0
  29. package/providers/dynamic-built-in/index.ts +1 -1
  30. package/providers/kilo/kilo-auth.ts +155 -155
  31. package/providers/kilo/kilo.ts +3 -3
  32. package/providers/llm7/llm7.ts +156 -0
  33. package/providers/model-fetcher.ts +2 -2
  34. package/providers/nvidia/nvidia.ts +5 -5
  35. package/providers/ollama/ollama.ts +2 -2
  36. package/providers/opencode-session.ts +1 -1
  37. package/providers/qwen/qwen-auth.ts +1 -1
  38. package/providers/qwen/qwen-models.ts +1 -1
  39. package/providers/qwen/qwen.ts +3 -3
  40. package/providers/sambanova/sambanova.ts +109 -0
  41. package/providers/zenmux/zenmux.ts +6 -3
  42. package/scripts/check-extensions.mjs +6 -4
@@ -1,155 +1,155 @@
1
- /**
2
- * Kilo device authorization flow and token management.
3
- */
4
-
5
- import type {
6
- OAuthCredentials,
7
- OAuthLoginCallbacks,
8
- } from "@mariozechner/pi-ai";
9
- import {
10
- KILO_POLL_INTERVAL_MS,
11
- KILO_TOKEN_EXPIRATION_MS,
12
- } from "../../constants.ts";
13
- import { createLogger } from "../../lib/logger.ts";
14
- import { openBrowser } from "../../lib/open-browser.ts";
15
-
16
- const _logger = createLogger("kilo-auth");
17
-
18
- const KILO_API_BASE = process.env.KILO_API_URL || "https://api.kilo.ai";
19
- const DEVICE_AUTH_ENDPOINT = `${KILO_API_BASE}/api/device-auth/codes`;
20
- const PROFILE_ENDPOINT = `${KILO_API_BASE}/api/profile`;
21
-
22
- // =============================================================================
23
- // Balance & Rate Limit
24
- // =============================================================================
25
-
26
- export async function fetchKiloBalance(token: string): Promise<number | null> {
27
- try {
28
- const response = await fetch(`${PROFILE_ENDPOINT}/balance`, {
29
- headers: {
30
- Authorization: `Bearer ${token}`,
31
- "Content-Type": "application/json",
32
- },
33
- });
34
- if (!response.ok) return null;
35
- const data = (await response.json()) as { balance?: number };
36
- return data.balance ?? null;
37
- } catch {
38
- return null;
39
- }
40
- }
41
-
42
- export function formatCredits(balance: number): string {
43
- return balance >= 1000
44
- ? `$${(balance / 1000).toFixed(1)}k`
45
- : `$${balance.toFixed(2)}`;
46
- }
47
-
48
- // =============================================================================
49
- // Device auth
50
- // =============================================================================
51
-
52
- interface DeviceAuthResponse {
53
- code: string;
54
- verificationUrl: string;
55
- expiresIn: number;
56
- }
57
-
58
- interface DeviceAuthPollResponse {
59
- status: "pending" | "approved" | "denied" | "expired";
60
- token?: string;
61
- }
62
-
63
- function abortableSleep(ms: number, signal?: AbortSignal): Promise<void> {
64
- return new Promise((resolve, reject) => {
65
- if (signal?.aborted) {
66
- reject(new Error("Login cancelled"));
67
- return;
68
- }
69
- const timeout = setTimeout(resolve, ms);
70
- signal?.addEventListener(
71
- "abort",
72
- () => {
73
- clearTimeout(timeout);
74
- reject(new Error("Login cancelled"));
75
- },
76
- { once: true },
77
- );
78
- });
79
- }
80
-
81
- async function initiateDeviceAuth(): Promise<DeviceAuthResponse> {
82
- const response = await fetch(DEVICE_AUTH_ENDPOINT, {
83
- method: "POST",
84
- headers: { "Content-Type": "application/json" },
85
- });
86
- if (!response.ok) {
87
- throw new Error(
88
- response.status === 429
89
- ? "Too many pending authorization requests. Please try again later."
90
- : `Failed to initiate device authorization: ${response.status}`,
91
- );
92
- }
93
- return (await response.json()) as DeviceAuthResponse;
94
- }
95
-
96
- async function pollDeviceAuth(code: string): Promise<DeviceAuthPollResponse> {
97
- const response = await fetch(`${DEVICE_AUTH_ENDPOINT}/${code}`);
98
- if (response.status === 202) return { status: "pending" };
99
- if (response.status === 403) return { status: "denied" };
100
- if (response.status === 410) return { status: "expired" };
101
- if (!response.ok)
102
- throw new Error(`Failed to poll device authorization: ${response.status}`);
103
- return (await response.json()) as DeviceAuthPollResponse;
104
- }
105
-
106
- export async function loginKilo(
107
- callbacks: OAuthLoginCallbacks,
108
- ): Promise<OAuthCredentials> {
109
- callbacks.onProgress?.("Initiating device authorization...");
110
- const { code, verificationUrl, expiresIn } = await initiateDeviceAuth();
111
-
112
- callbacks.onAuth({
113
- url: verificationUrl,
114
- instructions: `Enter code: ${code}`,
115
- });
116
- openBrowser(verificationUrl);
117
- callbacks.onProgress?.("Waiting for browser authorization...");
118
-
119
- const deadline = Date.now() + expiresIn * 1000;
120
- while (Date.now() < deadline) {
121
- if (callbacks.signal?.aborted) throw new Error("Login cancelled");
122
- await abortableSleep(KILO_POLL_INTERVAL_MS, callbacks.signal);
123
-
124
- const result = await pollDeviceAuth(code);
125
- if (result.status === "approved") {
126
- if (!result.token)
127
- throw new Error("Authorization approved but no token received");
128
- callbacks.onProgress?.("Login successful!");
129
- return {
130
- refresh: result.token,
131
- access: result.token,
132
- expires: Date.now() + KILO_TOKEN_EXPIRATION_MS,
133
- };
134
- }
135
- if (result.status === "denied")
136
- throw new Error("Authorization denied by user.");
137
- if (result.status === "expired")
138
- throw new Error("Authorization code expired. Please try again.");
139
-
140
- const remaining = Math.ceil((deadline - Date.now()) / 1000);
141
- callbacks.onProgress?.(
142
- `Waiting for browser authorization... (${remaining}s remaining)`,
143
- );
144
- }
145
- throw new Error("Authentication timed out. Please try again.");
146
- }
147
-
148
- export async function refreshKiloToken(
149
- credentials: OAuthCredentials,
150
- ): Promise<OAuthCredentials> {
151
- if (credentials.expires > Date.now()) return credentials;
152
- throw new Error(
153
- "Kilo token expired. Please run /login kilo to re-authenticate.",
154
- );
155
- }
1
+ /**
2
+ * Kilo device authorization flow and token management.
3
+ */
4
+
5
+ import type {
6
+ OAuthCredentials,
7
+ OAuthLoginCallbacks,
8
+ } from "@earendil-works/pi-ai";
9
+ import {
10
+ KILO_POLL_INTERVAL_MS,
11
+ KILO_TOKEN_EXPIRATION_MS,
12
+ } from "../../constants.ts";
13
+ import { createLogger } from "../../lib/logger.ts";
14
+ import { openBrowser } from "../../lib/open-browser.ts";
15
+
16
+ const _logger = createLogger("kilo-auth");
17
+
18
+ const KILO_API_BASE = process.env.KILO_API_URL || "https://api.kilo.ai";
19
+ const DEVICE_AUTH_ENDPOINT = `${KILO_API_BASE}/api/device-auth/codes`;
20
+ const PROFILE_ENDPOINT = `${KILO_API_BASE}/api/profile`;
21
+
22
+ // =============================================================================
23
+ // Balance & Rate Limit
24
+ // =============================================================================
25
+
26
+ export async function fetchKiloBalance(token: string): Promise<number | null> {
27
+ try {
28
+ const response = await fetch(`${PROFILE_ENDPOINT}/balance`, {
29
+ headers: {
30
+ Authorization: `Bearer ${token}`,
31
+ "Content-Type": "application/json",
32
+ },
33
+ });
34
+ if (!response.ok) return null;
35
+ const data = (await response.json()) as { balance?: number };
36
+ return data.balance ?? null;
37
+ } catch {
38
+ return null;
39
+ }
40
+ }
41
+
42
+ export function formatCredits(balance: number): string {
43
+ return balance >= 1000
44
+ ? `$${(balance / 1000).toFixed(1)}k`
45
+ : `$${balance.toFixed(2)}`;
46
+ }
47
+
48
+ // =============================================================================
49
+ // Device auth
50
+ // =============================================================================
51
+
52
+ interface DeviceAuthResponse {
53
+ code: string;
54
+ verificationUrl: string;
55
+ expiresIn: number;
56
+ }
57
+
58
+ interface DeviceAuthPollResponse {
59
+ status: "pending" | "approved" | "denied" | "expired";
60
+ token?: string;
61
+ }
62
+
63
+ function abortableSleep(ms: number, signal?: AbortSignal): Promise<void> {
64
+ return new Promise((resolve, reject) => {
65
+ if (signal?.aborted) {
66
+ reject(new Error("Login cancelled"));
67
+ return;
68
+ }
69
+ const timeout = setTimeout(resolve, ms);
70
+ signal?.addEventListener(
71
+ "abort",
72
+ () => {
73
+ clearTimeout(timeout);
74
+ reject(new Error("Login cancelled"));
75
+ },
76
+ { once: true },
77
+ );
78
+ });
79
+ }
80
+
81
+ async function initiateDeviceAuth(): Promise<DeviceAuthResponse> {
82
+ const response = await fetch(DEVICE_AUTH_ENDPOINT, {
83
+ method: "POST",
84
+ headers: { "Content-Type": "application/json" },
85
+ });
86
+ if (!response.ok) {
87
+ throw new Error(
88
+ response.status === 429
89
+ ? "Too many pending authorization requests. Please try again later."
90
+ : `Failed to initiate device authorization: ${response.status}`,
91
+ );
92
+ }
93
+ return (await response.json()) as DeviceAuthResponse;
94
+ }
95
+
96
+ async function pollDeviceAuth(code: string): Promise<DeviceAuthPollResponse> {
97
+ const response = await fetch(`${DEVICE_AUTH_ENDPOINT}/${code}`);
98
+ if (response.status === 202) return { status: "pending" };
99
+ if (response.status === 403) return { status: "denied" };
100
+ if (response.status === 410) return { status: "expired" };
101
+ if (!response.ok)
102
+ throw new Error(`Failed to poll device authorization: ${response.status}`);
103
+ return (await response.json()) as DeviceAuthPollResponse;
104
+ }
105
+
106
+ export async function loginKilo(
107
+ callbacks: OAuthLoginCallbacks,
108
+ ): Promise<OAuthCredentials> {
109
+ callbacks.onProgress?.("Initiating device authorization...");
110
+ const { code, verificationUrl, expiresIn } = await initiateDeviceAuth();
111
+
112
+ callbacks.onAuth({
113
+ url: verificationUrl,
114
+ instructions: `Enter code: ${code}`,
115
+ });
116
+ openBrowser(verificationUrl);
117
+ callbacks.onProgress?.("Waiting for browser authorization...");
118
+
119
+ const deadline = Date.now() + expiresIn * 1000;
120
+ while (Date.now() < deadline) {
121
+ if (callbacks.signal?.aborted) throw new Error("Login cancelled");
122
+ await abortableSleep(KILO_POLL_INTERVAL_MS, callbacks.signal);
123
+
124
+ const result = await pollDeviceAuth(code);
125
+ if (result.status === "approved") {
126
+ if (!result.token)
127
+ throw new Error("Authorization approved but no token received");
128
+ callbacks.onProgress?.("Login successful!");
129
+ return {
130
+ refresh: result.token,
131
+ access: result.token,
132
+ expires: Date.now() + KILO_TOKEN_EXPIRATION_MS,
133
+ };
134
+ }
135
+ if (result.status === "denied")
136
+ throw new Error("Authorization denied by user.");
137
+ if (result.status === "expired")
138
+ throw new Error("Authorization code expired. Please try again.");
139
+
140
+ const remaining = Math.ceil((deadline - Date.now()) / 1000);
141
+ callbacks.onProgress?.(
142
+ `Waiting for browser authorization... (${remaining}s remaining)`,
143
+ );
144
+ }
145
+ throw new Error("Authentication timed out. Please try again.");
146
+ }
147
+
148
+ export async function refreshKiloToken(
149
+ credentials: OAuthCredentials,
150
+ ): Promise<OAuthCredentials> {
151
+ if (credentials.expires > Date.now()) return credentials;
152
+ throw new Error(
153
+ "Kilo token expired. Please run /login kilo to re-authenticate.",
154
+ );
155
+ }
@@ -12,11 +12,11 @@
12
12
  * # Free models visible immediately; /login kilo for paid access
13
13
  */
14
14
 
15
- import type { Api, Model, OAuthCredentials } from "@mariozechner/pi-ai";
15
+ import type { Api, Model, OAuthCredentials } from "@earendil-works/pi-ai";
16
16
  import type {
17
17
  ExtensionAPI,
18
18
  ProviderModelConfig,
19
- } from "@mariozechner/pi-coding-agent";
19
+ } from "@earendil-works/pi-coding-agent";
20
20
  import {
21
21
  getKiloFreeOnly,
22
22
  getKiloShowPaid,
@@ -44,7 +44,7 @@ const KILO_PROVIDER_CONFIG = {
44
44
  },
45
45
  };
46
46
 
47
- export default async function (pi: ExtensionAPI) {
47
+ export default async function kiloProvider(pi: ExtensionAPI) {
48
48
  // Try to fetch ALL models at startup (like Cline/OpenRouter)
49
49
  // If no API key, this will return free models only
50
50
  let allModels: ProviderModelConfig[] = [];
@@ -0,0 +1,156 @@
1
+ /**
2
+ * LLM7.io Provider Extension
3
+ *
4
+ * LLM7.io is an LLM API gateway that routes requests across multiple
5
+ * providers (OpenAI, Mistral, Google, DeepSeek, Cloudflare, etc.) through
6
+ * a single OpenAI-compatible endpoint.
7
+ *
8
+ * Free tier:
9
+ * - Free token from https://token.llm7.io/
10
+ * - 100 req/hr, 20 req/min, 2 req/s
11
+ * - No credit card required
12
+ *
13
+ * Pro tier ($12/mo):
14
+ * - Higher rate limits, JSON mode, function calling
15
+ * - Access to "pro" routing selector
16
+ *
17
+ * Model selectors (not specific model IDs — LLM7 routes randomly):
18
+ * - "default" — first available free model (free)
19
+ * - "fast" — lowest latency option (free)
20
+ * - "pro" — highest quality, longer reasoning (paid)
21
+ *
22
+ * Endpoint:
23
+ * Chat: https://api.llm7.io/v1/chat/completions
24
+ *
25
+ * Setup:
26
+ * 1. Get free token from https://token.llm7.io/
27
+ * 2. Set LLM7_API_KEY env var (or add to ~/.pi/free.json)
28
+ *
29
+ * Usage:
30
+ * pi install git:github.com/apmantza/pi-free
31
+ * # Set LLM7_API_KEY env var
32
+ * # Models appear in /model selector as "llm7/default", "llm7/fast", "llm7/pro"
33
+ */
34
+
35
+ import type {
36
+ ExtensionAPI,
37
+ ProviderModelConfig,
38
+ } from "@earendil-works/pi-coding-agent";
39
+ import { getLlm7ApiKey, getLlm7ShowPaid } from "../../config.ts";
40
+ import { BASE_URL_LLM7, PROVIDER_LLM7 } from "../../constants.ts";
41
+ import { createLogger } from "../../lib/logger.ts";
42
+ import { isFreeModel, registerWithGlobalToggle } from "../../lib/registry.ts";
43
+ import { createReRegister, setupProvider } from "../../provider-helper.ts";
44
+
45
+ const _logger = createLogger("llm7");
46
+
47
+ // =============================================================================
48
+ // Model Definitions
49
+ // =============================================================================
50
+
51
+ const LLM7_MODELS: ProviderModelConfig[] = [
52
+ {
53
+ id: "default",
54
+ name: "LLM7 Default",
55
+ reasoning: false,
56
+ input: ["text"],
57
+ cost: {
58
+ input: 0,
59
+ output: 0,
60
+ cacheRead: 0,
61
+ cacheWrite: 0,
62
+ },
63
+ contextWindow: 32_000,
64
+ maxTokens: 4_096,
65
+ },
66
+ {
67
+ id: "fast",
68
+ name: "LLM7 Fast",
69
+ reasoning: false,
70
+ input: ["text"],
71
+ cost: {
72
+ input: 0,
73
+ output: 0,
74
+ cacheRead: 0,
75
+ cacheWrite: 0,
76
+ },
77
+ contextWindow: 32_000,
78
+ maxTokens: 4_096,
79
+ },
80
+ {
81
+ id: "pro",
82
+ name: "LLM7 Pro",
83
+ reasoning: false,
84
+ input: ["text"],
85
+ cost: {
86
+ input: 0.3, // Requires $12/mo LLM7 Pro subscription
87
+ output: 0.9,
88
+ cacheRead: 0,
89
+ cacheWrite: 0,
90
+ },
91
+ contextWindow: 32_000,
92
+ maxTokens: 4_096,
93
+ },
94
+ ];
95
+
96
+ // =============================================================================
97
+ // Extension Entry Point
98
+ // =============================================================================
99
+
100
+ export default async function llm7Provider(pi: ExtensionAPI) {
101
+ const apiKey = getLlm7ApiKey();
102
+
103
+ if (!apiKey) {
104
+ _logger.info(
105
+ "[llm7] Skipping — LLM7_API_KEY not set. Get a free token at https://token.llm7.io/",
106
+ );
107
+ return;
108
+ }
109
+
110
+ _logger.info("[llm7] Using LLM7_API_KEY");
111
+
112
+ const allModels = LLM7_MODELS;
113
+ const freeModels = allModels.filter((m) =>
114
+ isFreeModel({ ...m, provider: PROVIDER_LLM7 }, allModels),
115
+ );
116
+
117
+ const stored = { free: freeModels, all: allModels };
118
+
119
+ _logger.info(
120
+ `[llm7] Registered ${allModels.length} models (${freeModels.length} free)`,
121
+ );
122
+
123
+ // Create re-register function
124
+ const reRegister = createReRegister(pi, {
125
+ providerId: PROVIDER_LLM7,
126
+ baseUrl: BASE_URL_LLM7,
127
+ apiKey,
128
+ });
129
+
130
+ // Register with global toggle
131
+ registerWithGlobalToggle(PROVIDER_LLM7, stored, reRegister, true);
132
+
133
+ // Setup provider with toggle command
134
+ setupProvider(
135
+ pi,
136
+ {
137
+ providerId: PROVIDER_LLM7,
138
+ initialShowPaid: getLlm7ShowPaid(),
139
+ tosUrl: "https://llm7.io/",
140
+ reRegister: (models, _stored) => {
141
+ if (_stored) {
142
+ stored.free = _stored.free;
143
+ stored.all = _stored.all;
144
+ }
145
+ reRegister(models);
146
+ },
147
+ },
148
+ stored,
149
+ );
150
+
151
+ // Initial registration — respect persisted toggle state
152
+ const showPaid = getLlm7ShowPaid();
153
+ const initialModels =
154
+ showPaid && stored.all.length > 0 ? stored.all : freeModels;
155
+ reRegister(initialModels);
156
+ }
@@ -108,8 +108,8 @@ export async function fetchOpenRouterCompatibleModels(
108
108
 
109
109
  // Filter by pricing if freeOnly
110
110
  if (freeOnly) {
111
- const prompt = parseFloat(m.pricing?.prompt ?? "1");
112
- const completion = parseFloat(m.pricing?.completion ?? "1");
111
+ const prompt = Number.parseFloat(m.pricing?.prompt ?? "1");
112
+ const completion = Number.parseFloat(m.pricing?.completion ?? "1");
113
113
  if (prompt !== 0 || completion !== 0) return false;
114
114
  }
115
115
 
@@ -16,7 +16,7 @@
16
16
  import type {
17
17
  ExtensionAPI,
18
18
  ProviderModelConfig,
19
- } from "@mariozechner/pi-coding-agent";
19
+ } from "@earendil-works/pi-coding-agent";
20
20
  import {
21
21
  applyHidden,
22
22
  getNvidiaApiKey,
@@ -146,9 +146,9 @@ function inferModelFromId(id: string): ModelsDevModel | null {
146
146
  const name = id
147
147
  .split("/")
148
148
  .pop()!
149
- .replace(/-/g, " ")
150
- .replace(/\b\w/g, (c) => c.toUpperCase())
151
- .replace(/\b(\d+(?:\.\d+)?)b\b/gi, "$1B");
149
+ .replaceAll("-", " ")
150
+ .replaceAll(/\b\w/g, (c) => c.toUpperCase())
151
+ .replaceAll(/\b(\d+(?:\.\d+)?)b\b/gi, "$1B");
152
152
 
153
153
  const hasVision = /vision|multimodal|vl/i.test(id);
154
154
  const hasReasoning = /reason|r1|thinking/i.test(id);
@@ -369,7 +369,7 @@ async function runNvidiaProbe(
369
369
  );
370
370
  }
371
371
 
372
- export default async function (pi: ExtensionAPI) {
372
+ export default async function nvidiaProvider(pi: ExtensionAPI) {
373
373
  const apiKey = getNvidiaApiKey();
374
374
  const hasKey = !!apiKey;
375
375
 
@@ -23,7 +23,7 @@
23
23
  import type {
24
24
  ExtensionAPI,
25
25
  ProviderModelConfig,
26
- } from "@mariozechner/pi-coding-agent";
26
+ } from "@earendil-works/pi-coding-agent";
27
27
  import {
28
28
  applyHidden,
29
29
  getOllamaApiKey,
@@ -140,7 +140,7 @@ async function fetchOllamaModels(
140
140
  // Extension Entry Point
141
141
  // =============================================================================
142
142
 
143
- export default async function (pi: ExtensionAPI) {
143
+ export default async function ollamaProvider(pi: ExtensionAPI) {
144
144
  const apiKey = getOllamaApiKey();
145
145
 
146
146
  if (!apiKey) {
@@ -11,7 +11,7 @@ export function createOpenCodeSessionTracker() {
11
11
  let requestCount = 0;
12
12
 
13
13
  function generateId(): string {
14
- return randomUUID().replace(/-/g, "");
14
+ return randomUUID().replaceAll("-", "");
15
15
  }
16
16
 
17
17
  function getSessionId(): string {
@@ -16,7 +16,7 @@ import crypto from "node:crypto";
16
16
  import type {
17
17
  OAuthCredentials,
18
18
  OAuthLoginCallbacks,
19
- } from "@mariozechner/pi-ai";
19
+ } from "@earendil-works/pi-ai";
20
20
  import { createLogger } from "../../lib/logger.ts";
21
21
  import { openBrowser } from "../../lib/open-browser.ts";
22
22
 
@@ -5,7 +5,7 @@
5
5
  * This provider remains for backward compatibility but should not be used.
6
6
  */
7
7
 
8
- import type { ProviderModelConfig } from "@mariozechner/pi-coding-agent";
8
+ import type { ProviderModelConfig } from "@earendil-works/pi-coding-agent";
9
9
  import { createLogger } from "../../lib/logger.ts";
10
10
 
11
11
  const _logger = createLogger("qwen-models");
@@ -10,8 +10,8 @@
10
10
  * 1,000 free API calls/day — run /login qwen to authenticate.~~
11
11
  */
12
12
 
13
- import type { Api, Model, OAuthCredentials } from "@mariozechner/pi-ai";
14
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
13
+ import type { Api, Model, OAuthCredentials } from "@earendil-works/pi-ai";
14
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
15
15
  import { PROVIDER_QWEN, URL_QWEN_TOS } from "../../constants.ts";
16
16
  import { createLogger } from "../../lib/logger.ts";
17
17
  import { logWarning } from "../../lib/util.ts";
@@ -59,7 +59,7 @@ const DASHSCOPE_HEADERS = {
59
59
  // Extension entry point
60
60
  // =============================================================================
61
61
 
62
- export default async function (pi: ExtensionAPI) {
62
+ export default async function qwenProvider(pi: ExtensionAPI) {
63
63
  // DEPRECATION WARNING
64
64
  _logger.warn(
65
65
  "Qwen provider is deprecated. The 1,000 req/day free tier is no longer available.",