pi-free 2.2.6 → 2.2.8
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 +31 -4
- package/README.md +3 -2
- package/config.ts +815 -803
- package/constants.ts +113 -121
- package/index.ts +6 -7
- package/lib/built-in-toggle.ts +426 -426
- package/lib/model-detection.ts +1 -1
- package/package.json +74 -74
- package/provider-helper.ts +29 -7
- package/providers/anyapi/anyapi.ts +270 -0
- package/providers/cline/cline-auth.ts +473 -473
- package/providers/cline/cline-xml-bridge.ts +1506 -1506
- package/providers/cline/cline.ts +205 -205
- package/providers/dynamic-built-in/index.ts +718 -718
- package/providers/kilo/kilo-auth.ts +152 -155
- package/providers/kilo/kilo.ts +14 -13
- package/providers/opencode-session.ts +514 -463
- package/providers/qoder/auth.ts +548 -548
- package/providers/qoder/qoder.ts +119 -119
- package/providers/qoder/stream.ts +585 -585
- package/providers/qoder/thinking-parser.ts +251 -251
- package/providers/qoder/transform.ts +192 -192
- package/providers/tokenrouter/tokenrouter.ts +636 -637
- package/providers/qwen/qwen-auth.ts +0 -416
- package/providers/qwen/qwen-models.ts +0 -101
- package/providers/qwen/qwen.ts +0 -200
package/providers/qwen/qwen.ts
DELETED
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Qwen OAuth Provider Extension
|
|
3
|
-
*
|
|
4
|
-
* @deprecated This provider is deprecated. Qwen no longer offers the 1,000 free API calls/day tier.
|
|
5
|
-
* The provider remains functional for existing authenticated users but new free tier registrations
|
|
6
|
-
* are not supported. Consider using other free providers like Kilo, Cline, or NVIDIA instead.
|
|
7
|
-
*
|
|
8
|
-
* Original description (now outdated):
|
|
9
|
-
* ~~Provides free access to Qwen 3.6 Plus via OAuth device flow.
|
|
10
|
-
* 1,000 free API calls/day — run /login qwen to authenticate.~~
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import type { Api, Model, OAuthCredentials } from "@earendil-works/pi-ai";
|
|
14
|
-
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
15
|
-
import { PROVIDER_QWEN, URL_QWEN_TOS } from "../../constants.ts";
|
|
16
|
-
import { createLogger } from "../../lib/logger.ts";
|
|
17
|
-
import { logWarning } from "../../lib/util.ts";
|
|
18
|
-
import {
|
|
19
|
-
createReRegister,
|
|
20
|
-
enhanceWithCI,
|
|
21
|
-
type StoredModels,
|
|
22
|
-
setupProvider,
|
|
23
|
-
} from "../../provider-helper.ts";
|
|
24
|
-
import { getQwenBaseUrl, loginQwen, refreshQwenToken } from "./qwen-auth.ts";
|
|
25
|
-
import { fetchQwenModels } from "./qwen-models.ts";
|
|
26
|
-
|
|
27
|
-
// =============================================================================
|
|
28
|
-
// 401 detection patterns
|
|
29
|
-
// =============================================================================
|
|
30
|
-
|
|
31
|
-
/** Patterns that indicate an auth failure requiring token refresh. */
|
|
32
|
-
const AUTH_ERROR_PATTERNS = [
|
|
33
|
-
"invalid access token",
|
|
34
|
-
"token expired",
|
|
35
|
-
"401",
|
|
36
|
-
"unauthorized",
|
|
37
|
-
"authentication",
|
|
38
|
-
] as const;
|
|
39
|
-
|
|
40
|
-
const _logger = createLogger("qwen");
|
|
41
|
-
|
|
42
|
-
// =============================================================================
|
|
43
|
-
// Constants
|
|
44
|
-
// =============================================================================
|
|
45
|
-
|
|
46
|
-
// Mirrors qwen-code's DEFAULT_QWEN_BASE_URL (used when resource_url is absent).
|
|
47
|
-
const DEFAULT_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1";
|
|
48
|
-
|
|
49
|
-
// Headers required by DashScope's OpenAI-compatible API for OAuth tokens.
|
|
50
|
-
// Replicates DashScopeOpenAICompatibleProvider.buildHeaders() from qwen-code.
|
|
51
|
-
const DASHSCOPE_HEADERS = {
|
|
52
|
-
"X-DashScope-AuthType": "qwen-oauth",
|
|
53
|
-
"X-DashScope-CacheControl": "enable",
|
|
54
|
-
"X-DashScope-UserAgent": "QwenCode/0.0.5 (pi-free)",
|
|
55
|
-
"Client-Code": "QwenCode",
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
// =============================================================================
|
|
59
|
-
// Extension entry point
|
|
60
|
-
// =============================================================================
|
|
61
|
-
|
|
62
|
-
export default async function qwenProvider(pi: ExtensionAPI) {
|
|
63
|
-
// DEPRECATION WARNING
|
|
64
|
-
_logger.warn(
|
|
65
|
-
"Qwen provider is deprecated. The 1,000 req/day free tier is no longer available.",
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
// Fetch static free-tier models
|
|
69
|
-
const models = await fetchQwenModels().catch((err) => {
|
|
70
|
-
logWarning("qwen", "Failed to load models at startup", err);
|
|
71
|
-
return [];
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
if (models.length === 0) {
|
|
75
|
-
logWarning("qwen", "No models available, skipping provider");
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const stored: StoredModels = { free: models, all: models };
|
|
80
|
-
|
|
81
|
-
// OAuth config for Qwen
|
|
82
|
-
const oauthConfig = {
|
|
83
|
-
name: "Qwen",
|
|
84
|
-
login: loginQwen,
|
|
85
|
-
refreshToken: refreshQwenToken,
|
|
86
|
-
getApiKey: (cred: OAuthCredentials) => cred.access,
|
|
87
|
-
modifyModels: (models: Model<Api>[], cred: OAuthCredentials) => {
|
|
88
|
-
// Mirror qwen-code: resolve baseUrl from resource_url per-credential.
|
|
89
|
-
// Chinese accounts → dashscope.aliyuncs.com/v1
|
|
90
|
-
// International accounts → portal.qwen.ai/v1 (or custom endpoint)
|
|
91
|
-
const baseUrl = getQwenBaseUrl(cred);
|
|
92
|
-
_logger.info("Qwen OAuth modifyModels called", {
|
|
93
|
-
baseUrl,
|
|
94
|
-
resource_url: cred.resource_url,
|
|
95
|
-
modelCount: models.length,
|
|
96
|
-
});
|
|
97
|
-
if (baseUrl === DEFAULT_BASE_URL) return models;
|
|
98
|
-
// modifyModels receives ALL models across providers — only patch Qwen ones.
|
|
99
|
-
const nonQwen = models.filter((m) => m.provider !== PROVIDER_QWEN);
|
|
100
|
-
const qwen = models
|
|
101
|
-
.filter((m) => m.provider === PROVIDER_QWEN)
|
|
102
|
-
.map((m) => ({ ...m, baseUrl }));
|
|
103
|
-
return [...nonQwen, ...qwen];
|
|
104
|
-
},
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
// Register provider with OpenAI-compatible API
|
|
108
|
-
function registerProvider(m = models) {
|
|
109
|
-
pi.registerProvider(PROVIDER_QWEN, {
|
|
110
|
-
baseUrl: DEFAULT_BASE_URL,
|
|
111
|
-
apiKey: "$QWEN_API_KEY",
|
|
112
|
-
api: "openai-completions" as const,
|
|
113
|
-
headers: {
|
|
114
|
-
"User-Agent": "pi-free",
|
|
115
|
-
...DASHSCOPE_HEADERS,
|
|
116
|
-
},
|
|
117
|
-
models: enhanceWithCI(m),
|
|
118
|
-
oauth: oauthConfig,
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
registerProvider();
|
|
123
|
-
|
|
124
|
-
// Wire up shared boilerplate (commands, model_select, turn_end, ToS)
|
|
125
|
-
const reRegister = createReRegister(pi, {
|
|
126
|
-
providerId: PROVIDER_QWEN,
|
|
127
|
-
baseUrl: DEFAULT_BASE_URL,
|
|
128
|
-
apiKey: "$QWEN_API_KEY",
|
|
129
|
-
oauth: oauthConfig as any,
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
setupProvider(
|
|
133
|
-
pi,
|
|
134
|
-
{
|
|
135
|
-
providerId: PROVIDER_QWEN,
|
|
136
|
-
tosUrl: URL_QWEN_TOS,
|
|
137
|
-
initialShowPaid: false,
|
|
138
|
-
reRegister: (m) => {
|
|
139
|
-
reRegister(m);
|
|
140
|
-
},
|
|
141
|
-
},
|
|
142
|
-
stored,
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
// Request counting + 401 auth-error detection with forced token refresh.
|
|
146
|
-
//
|
|
147
|
-
// When Qwen returns a 401 / "invalid access token" error, the stored token
|
|
148
|
-
// may have been revoked server-side even though its expiry hasn't been reached.
|
|
149
|
-
// We force-expire the credential in auth storage so that pi-core's next
|
|
150
|
-
// getApiKey() call will trigger a token refresh via refreshToken().
|
|
151
|
-
// This mirrors qwen-code's executeWithCredentialManagement() retry logic.
|
|
152
|
-
//
|
|
153
|
-
function isQwenAuthError(msg: {
|
|
154
|
-
role?: string;
|
|
155
|
-
errorMessage?: string;
|
|
156
|
-
}): boolean {
|
|
157
|
-
if (msg.role !== "assistant" || !msg.errorMessage) return false;
|
|
158
|
-
const errLower = msg.errorMessage.toLowerCase();
|
|
159
|
-
return AUTH_ERROR_PATTERNS.some((p) => errLower.includes(p));
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function forceExpireQwenToken(
|
|
163
|
-
ctx: any,
|
|
164
|
-
msg: { errorMessage?: string },
|
|
165
|
-
): void {
|
|
166
|
-
_logger.warn("Qwen auth error detected, force-expiring token for refresh", {
|
|
167
|
-
error: (msg.errorMessage ?? "").slice(0, 100),
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
try {
|
|
171
|
-
const authStorage = (ctx as any).modelRegistry?.authStorage;
|
|
172
|
-
if (authStorage) {
|
|
173
|
-
const cred = authStorage.get(PROVIDER_QWEN);
|
|
174
|
-
if (cred?.type === "oauth" && cred.expires > Date.now()) {
|
|
175
|
-
authStorage.set(PROVIDER_QWEN, { ...cred, expires: 0 });
|
|
176
|
-
_logger.info(
|
|
177
|
-
"Qwen token force-expired; will refresh on next request",
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
ctx.ui.notify("Qwen: auth error detected, refreshing token…", "warning");
|
|
182
|
-
} catch (e) {
|
|
183
|
-
_logger.warn("Failed to force-expire Qwen token", {
|
|
184
|
-
error: e instanceof Error ? e.message : String(e),
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
pi.on("turn_end", async (event, ctx) => {
|
|
190
|
-
if (ctx.model?.provider !== PROVIDER_QWEN) return;
|
|
191
|
-
|
|
192
|
-
const msg = (
|
|
193
|
-
event as { message?: { role?: string; errorMessage?: string } }
|
|
194
|
-
).message;
|
|
195
|
-
|
|
196
|
-
if (msg && isQwenAuthError(msg)) {
|
|
197
|
-
forceExpireQwenToken(ctx, msg);
|
|
198
|
-
}
|
|
199
|
-
});
|
|
200
|
-
}
|