pi-free 2.0.1 → 2.0.4
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 +179 -3
- package/README.md +495 -393
- package/config.ts +46 -54
- package/constants.ts +6 -0
- package/index.ts +39 -12
- package/lib/built-in-toggle.ts +63 -43
- package/lib/model-enhancer.ts +20 -20
- package/lib/open-browser.ts +1 -1
- package/lib/provider-compat.ts +46 -0
- package/lib/registry.ts +193 -144
- package/lib/toggle-state.ts +86 -0
- package/lib/types.ts +101 -108
- package/package.json +8 -8
- package/provider-failover/benchmark-lookup.ts +637 -247
- package/provider-helper.ts +279 -260
- package/providers/cline/cline-auth.ts +473 -473
- package/providers/cline/cline-models.ts +129 -128
- package/providers/cline/cline.ts +311 -298
- package/providers/crofai/crofai.ts +170 -0
- package/providers/dynamic-built-in/index.ts +259 -308
- package/providers/kilo/kilo-auth.ts +155 -155
- package/providers/kilo/kilo-models.ts +2 -1
- package/providers/kilo/kilo.ts +263 -235
- package/providers/nvidia/nvidia.ts +476 -152
- package/providers/ollama/ollama.ts +130 -7
- package/providers/opencode-session.ts +3 -4
- package/providers/qwen/qwen-models.ts +101 -101
- package/providers/zenmux/zenmux.ts +176 -0
- package/scripts/check-extensions.mjs +64 -55
- package/provider-factory.ts +0 -207
- package/providers/cloudflare/cloudflare.ts +0 -368
- package/providers/modal/modal.ts +0 -44
package/providers/kilo/kilo.ts
CHANGED
|
@@ -1,235 +1,263 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Kilo Provider Extension
|
|
3
|
-
*
|
|
4
|
-
* Provides access to 300+ AI models via the Kilo Gateway (OpenRouter-compatible).
|
|
5
|
-
* Fetches ALL models at startup (like Cline/OpenRouter), defaults to free-only view.
|
|
6
|
-
* Run /login kilo or use /toggle-kilo to access paid models.
|
|
7
|
-
*
|
|
8
|
-
* Responds to global free-only filter for free/paid model filtering.
|
|
9
|
-
*
|
|
10
|
-
* Usage:
|
|
11
|
-
* pi install git:github.com/apmantza/pi-free
|
|
12
|
-
* # Free models visible immediately; /login kilo for paid access
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import type { Api, Model, OAuthCredentials } from "@mariozechner/pi-ai";
|
|
16
|
-
import type {
|
|
17
|
-
ExtensionAPI,
|
|
18
|
-
ProviderModelConfig,
|
|
19
|
-
} from "@mariozechner/pi-coding-agent";
|
|
20
|
-
import {
|
|
21
|
-
getKiloFreeOnly,
|
|
22
|
-
getKiloShowPaid,
|
|
23
|
-
PROVIDER_KILO,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
import {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
let
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
//
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Kilo Provider Extension
|
|
3
|
+
*
|
|
4
|
+
* Provides access to 300+ AI models via the Kilo Gateway (OpenRouter-compatible).
|
|
5
|
+
* Fetches ALL models at startup (like Cline/OpenRouter), defaults to free-only view.
|
|
6
|
+
* Run /login kilo or use /toggle-kilo to access paid models.
|
|
7
|
+
*
|
|
8
|
+
* Responds to global free-only filter for free/paid model filtering.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* pi install git:github.com/apmantza/pi-free
|
|
12
|
+
* # Free models visible immediately; /login kilo for paid access
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { Api, Model, OAuthCredentials } from "@mariozechner/pi-ai";
|
|
16
|
+
import type {
|
|
17
|
+
ExtensionAPI,
|
|
18
|
+
ProviderModelConfig,
|
|
19
|
+
} from "@mariozechner/pi-coding-agent";
|
|
20
|
+
import {
|
|
21
|
+
getKiloFreeOnly,
|
|
22
|
+
getKiloShowPaid,
|
|
23
|
+
PROVIDER_KILO,
|
|
24
|
+
saveConfig,
|
|
25
|
+
} from "../../config.ts";
|
|
26
|
+
import { URL_KILO_TOS } from "../../constants.ts";
|
|
27
|
+
import { isFreeModel, registerWithGlobalToggle } from "../../lib/registry.ts";
|
|
28
|
+
import { cleanModelName, logWarning } from "../../lib/util.ts";
|
|
29
|
+
import {
|
|
30
|
+
createCtxReRegister,
|
|
31
|
+
createReRegister,
|
|
32
|
+
enhanceWithCI,
|
|
33
|
+
type StoredModels,
|
|
34
|
+
} from "../../provider-helper.ts";
|
|
35
|
+
import { loginKilo, refreshKiloToken } from "./kilo-auth.ts";
|
|
36
|
+
import { fetchKiloModels, KILO_GATEWAY_BASE } from "./kilo-models.ts";
|
|
37
|
+
|
|
38
|
+
const KILO_PROVIDER_CONFIG = {
|
|
39
|
+
providerId: PROVIDER_KILO,
|
|
40
|
+
baseUrl: KILO_GATEWAY_BASE,
|
|
41
|
+
apiKey: "KILO_API_KEY",
|
|
42
|
+
headers: {
|
|
43
|
+
"X-KILOCODE-EDITORNAME": "Pi",
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default async function (pi: ExtensionAPI) {
|
|
48
|
+
// Try to fetch ALL models at startup (like Cline/OpenRouter)
|
|
49
|
+
// If no API key, this will return free models only
|
|
50
|
+
let allModels: ProviderModelConfig[] = [];
|
|
51
|
+
let freeModels: ProviderModelConfig[] = [];
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
// Fetch all models (returns free-only if no auth, all if auth available)
|
|
55
|
+
allModels = await fetchKiloModels({ freeOnly: false });
|
|
56
|
+
// Derive free list using isFreeModel with allModels for detection
|
|
57
|
+
freeModels = allModels.filter((m) =>
|
|
58
|
+
isFreeModel({ ...m, provider: PROVIDER_KILO }, allModels),
|
|
59
|
+
);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
logWarning("kilo", "Failed to fetch models at startup", error);
|
|
62
|
+
// Fallback: try to fetch just free models
|
|
63
|
+
try {
|
|
64
|
+
freeModels = await fetchKiloModels({ freeOnly: true });
|
|
65
|
+
} catch (e) {
|
|
66
|
+
logWarning("kilo", "Failed to fetch free models", e);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// State tracking
|
|
71
|
+
const kiloShowPaid = getKiloShowPaid();
|
|
72
|
+
const kiloFreeOnly = getKiloFreeOnly();
|
|
73
|
+
let showPaidModels = kiloShowPaid;
|
|
74
|
+
let currentModels = kiloShowPaid && !kiloFreeOnly ? allModels : freeModels;
|
|
75
|
+
|
|
76
|
+
// Shared model storage for global toggle
|
|
77
|
+
const stored: StoredModels = { free: freeModels, all: allModels };
|
|
78
|
+
|
|
79
|
+
// Create re-register function
|
|
80
|
+
const reRegister = createReRegister(pi, {
|
|
81
|
+
...KILO_PROVIDER_CONFIG,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Register with global toggle system
|
|
85
|
+
registerWithGlobalToggle(
|
|
86
|
+
PROVIDER_KILO,
|
|
87
|
+
stored,
|
|
88
|
+
reRegister,
|
|
89
|
+
!!process.env.KILO_API_KEY,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
// OAuth config for Kilo
|
|
93
|
+
const oauthConfig = {
|
|
94
|
+
name: "Kilo",
|
|
95
|
+
login: async (callbacks: any) => {
|
|
96
|
+
const cred = await loginKilo(callbacks);
|
|
97
|
+
try {
|
|
98
|
+
// Fetch all models with the new token
|
|
99
|
+
const newModels = await fetchKiloModels({
|
|
100
|
+
token: cred.access,
|
|
101
|
+
freeOnly: false,
|
|
102
|
+
});
|
|
103
|
+
allModels = newModels;
|
|
104
|
+
stored.all = allModels;
|
|
105
|
+
freeModels = allModels.filter((m) =>
|
|
106
|
+
isFreeModel({ ...m, provider: PROVIDER_KILO }, allModels),
|
|
107
|
+
);
|
|
108
|
+
stored.free = freeModels;
|
|
109
|
+
|
|
110
|
+
// Update global toggle registration with new lists
|
|
111
|
+
const globalReRegister = createReRegister(pi, {
|
|
112
|
+
...KILO_PROVIDER_CONFIG,
|
|
113
|
+
});
|
|
114
|
+
registerWithGlobalToggle(PROVIDER_KILO, stored, globalReRegister, true);
|
|
115
|
+
|
|
116
|
+
// If paid mode is enabled, show all models
|
|
117
|
+
if (showPaidModels && !kiloFreeOnly) {
|
|
118
|
+
currentModels = allModels;
|
|
119
|
+
globalReRegister(allModels);
|
|
120
|
+
}
|
|
121
|
+
} catch (error) {
|
|
122
|
+
logWarning("kilo", "Failed to fetch models after login", error);
|
|
123
|
+
}
|
|
124
|
+
return cred;
|
|
125
|
+
},
|
|
126
|
+
refreshToken: refreshKiloToken,
|
|
127
|
+
getApiKey: (cred: OAuthCredentials) => cred.access,
|
|
128
|
+
modifyModels: (models: Model<Api>[], _cred: OAuthCredentials) => {
|
|
129
|
+
if (!showPaidModels || kiloFreeOnly || allModels.length === 0) {
|
|
130
|
+
return models;
|
|
131
|
+
}
|
|
132
|
+
const template = models.find((m) => m.provider === PROVIDER_KILO);
|
|
133
|
+
if (!template) return models;
|
|
134
|
+
const nonKilo = models.filter((m) => m.provider !== PROVIDER_KILO);
|
|
135
|
+
const fullModels = allModels.map((m) => ({
|
|
136
|
+
...template,
|
|
137
|
+
id: m.id,
|
|
138
|
+
name: cleanModelName(m.name),
|
|
139
|
+
reasoning: m.reasoning,
|
|
140
|
+
input: m.input,
|
|
141
|
+
cost: m.cost,
|
|
142
|
+
contextWindow: m.contextWindow,
|
|
143
|
+
maxTokens: m.maxTokens,
|
|
144
|
+
}));
|
|
145
|
+
return [...nonKilo, ...fullModels];
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// Register initial provider (default to free models)
|
|
150
|
+
pi.registerProvider(PROVIDER_KILO, {
|
|
151
|
+
baseUrl: KILO_GATEWAY_BASE,
|
|
152
|
+
apiKey: "KILO_API_KEY",
|
|
153
|
+
api: "openai-completions" as const,
|
|
154
|
+
headers: {
|
|
155
|
+
"X-KILOCODE-EDITORNAME": "Pi",
|
|
156
|
+
"User-Agent": "pi-free-providers",
|
|
157
|
+
},
|
|
158
|
+
models: enhanceWithCI(currentModels),
|
|
159
|
+
oauth: oauthConfig,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Registration complete - models registered silently (use LOG_LEVEL=info to see details)
|
|
163
|
+
|
|
164
|
+
// Per-provider toggle command
|
|
165
|
+
pi.registerCommand("toggle-kilo", {
|
|
166
|
+
description: "Toggle between free and all Kilo models",
|
|
167
|
+
handler: async (_args, ctx) => {
|
|
168
|
+
showPaidModels = !showPaidModels;
|
|
169
|
+
saveConfig({ kilo_show_paid: showPaidModels });
|
|
170
|
+
|
|
171
|
+
// Determine which models to show
|
|
172
|
+
const modelsToShow =
|
|
173
|
+
showPaidModels && allModels.length > 0 ? allModels : freeModels;
|
|
174
|
+
|
|
175
|
+
currentModels = modelsToShow;
|
|
176
|
+
reRegister(modelsToShow);
|
|
177
|
+
|
|
178
|
+
const freeCount = freeModels.length;
|
|
179
|
+
const paidCount = allModels.length - freeCount;
|
|
180
|
+
|
|
181
|
+
if (showPaidModels && allModels.length > 0) {
|
|
182
|
+
ctx.ui.notify(
|
|
183
|
+
`kilo: showing all ${allModels.length} models (${freeCount} free, ${paidCount} paid)`,
|
|
184
|
+
"info",
|
|
185
|
+
);
|
|
186
|
+
} else {
|
|
187
|
+
ctx.ui.notify(
|
|
188
|
+
`kilo: showing ${freeCount} free models (${paidCount} paid hidden)`,
|
|
189
|
+
"info",
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Status bar + ToS notice on provider selection
|
|
196
|
+
let tosShown = false;
|
|
197
|
+
pi.on("model_select", async (_event, ctx) => {
|
|
198
|
+
if (ctx.model?.provider !== PROVIDER_KILO) {
|
|
199
|
+
ctx.ui.setStatus(`${PROVIDER_KILO}-status`, undefined);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Build status line
|
|
204
|
+
const free = freeModels.length;
|
|
205
|
+
const total = allModels.length;
|
|
206
|
+
const paid = total - free;
|
|
207
|
+
let status: string;
|
|
208
|
+
if (paid === 0) {
|
|
209
|
+
status = `kilo: ${free} free models`;
|
|
210
|
+
} else if (showPaidModels) {
|
|
211
|
+
status = `kilo: ${total} models (free + paid)`;
|
|
212
|
+
} else {
|
|
213
|
+
status = `kilo: ${free} free \u00b7 ${paid} paid`;
|
|
214
|
+
}
|
|
215
|
+
ctx.ui.setStatus(`${PROVIDER_KILO}-status`, status);
|
|
216
|
+
|
|
217
|
+
// ToS notice (once)
|
|
218
|
+
if (tosShown) return;
|
|
219
|
+
tosShown = true;
|
|
220
|
+
const cred = ctx.modelRegistry.authStorage.get(PROVIDER_KILO);
|
|
221
|
+
if (cred?.type === "oauth") return;
|
|
222
|
+
const paidCount = allModels.length - freeModels.length;
|
|
223
|
+
if (paidCount > 0) {
|
|
224
|
+
ctx.ui.notify(
|
|
225
|
+
`Kilo: ${freeModels.length} free models shown. Use /toggle-kilo or /login kilo for ${paidCount} paid models. Terms: ${URL_KILO_TOS}`,
|
|
226
|
+
"info",
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// Refresh models on session start if authenticated
|
|
232
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
233
|
+
const cred = ctx.modelRegistry.authStorage.get(PROVIDER_KILO);
|
|
234
|
+
|
|
235
|
+
if (cred?.type === "oauth") {
|
|
236
|
+
try {
|
|
237
|
+
const newModels = await fetchKiloModels({
|
|
238
|
+
token: cred.access,
|
|
239
|
+
freeOnly: false,
|
|
240
|
+
});
|
|
241
|
+
allModels = newModels;
|
|
242
|
+
stored.all = allModels;
|
|
243
|
+
freeModels = allModels.filter((m) =>
|
|
244
|
+
isFreeModel({ ...m, provider: PROVIDER_KILO }, allModels),
|
|
245
|
+
);
|
|
246
|
+
stored.free = freeModels;
|
|
247
|
+
|
|
248
|
+
// Update global toggle registration
|
|
249
|
+
const ctxReRegister = createCtxReRegister(ctx as any, {
|
|
250
|
+
...KILO_PROVIDER_CONFIG,
|
|
251
|
+
});
|
|
252
|
+
registerWithGlobalToggle(PROVIDER_KILO, stored, ctxReRegister, true);
|
|
253
|
+
|
|
254
|
+
// Apply current view mode
|
|
255
|
+
if (showPaidModels && !getKiloFreeOnly()) {
|
|
256
|
+
ctxReRegister(allModels);
|
|
257
|
+
}
|
|
258
|
+
} catch (error) {
|
|
259
|
+
logWarning("kilo", "Failed to refresh models at session start", error);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
}
|