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
|
@@ -7,48 +7,51 @@
|
|
|
7
7
|
* node scripts/check-extensions.mjs <dir> # from installed location
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
11
|
-
import { join, resolve, dirname } from "node:path";
|
|
12
10
|
import { execSync } from "node:child_process";
|
|
11
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
12
|
+
import { dirname, join, resolve } from "node:path";
|
|
13
13
|
|
|
14
14
|
const installDir = resolve(process.argv[2] ?? ".");
|
|
15
15
|
const fromSource = process.argv[2] == null;
|
|
16
16
|
|
|
17
17
|
function getFiles() {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
18
|
+
if (fromSource) {
|
|
19
|
+
// Use npm pack --dry-run to get exactly the files that would be published
|
|
20
|
+
const out = execSync("npm pack --dry-run 2>&1", { encoding: "utf8" });
|
|
21
|
+
return out
|
|
22
|
+
.split("\n")
|
|
23
|
+
.map((l) => l.match(/npm notice \S+\s+(.+)/)?.[1]?.trim())
|
|
24
|
+
.filter((f) => f && (f.endsWith(".ts") || f.endsWith(".mjs")))
|
|
25
|
+
.map((f) => join(installDir, f));
|
|
26
|
+
}
|
|
27
|
+
// Installed location: walk all .ts/.mjs files
|
|
28
|
+
const files = [];
|
|
29
|
+
function walk(dir) {
|
|
30
|
+
for (const entry of readdirSync(dir)) {
|
|
31
|
+
const full = join(dir, entry);
|
|
32
|
+
if (entry === "node_modules") continue;
|
|
33
|
+
if (statSync(full).isDirectory()) walk(full);
|
|
34
|
+
else if (entry.endsWith(".ts") || entry.endsWith(".mjs"))
|
|
35
|
+
files.push(full);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
walk(installDir);
|
|
39
|
+
return files;
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
function resolveImport(fromFile, importPath) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
const base = join(dirname(fromFile), importPath);
|
|
44
|
+
for (const candidate of [
|
|
45
|
+
base,
|
|
46
|
+
base.replace(/\.js$/, ".ts"), // .js → .ts (TypeScript ESM convention)
|
|
47
|
+
base + ".ts",
|
|
48
|
+
join(base, "index.ts"),
|
|
49
|
+
]) {
|
|
50
|
+
try {
|
|
51
|
+
if (statSync(candidate).isFile()) return candidate;
|
|
52
|
+
} catch {}
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
const files = getFiles();
|
|
@@ -59,29 +62,35 @@ let failed = 0;
|
|
|
59
62
|
const seen = new Set();
|
|
60
63
|
|
|
61
64
|
for (const file of files) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
65
|
+
const src = readFileSync(file, "utf8");
|
|
66
|
+
const relFile = file.slice(installDir.length + 1).replace(/\\/g, "/");
|
|
67
|
+
// Strip comments before matching imports
|
|
68
|
+
const stripped = src
|
|
69
|
+
.replace(/\/\/[^\n]*/g, "")
|
|
70
|
+
.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
71
|
+
const importRe = /from\s+['"](\.[^'"]+)['"]/g;
|
|
72
|
+
let match;
|
|
73
|
+
while ((match = importRe.exec(stripped)) !== null) {
|
|
74
|
+
const importPath = match[1];
|
|
75
|
+
const key = `${relFile}:${importPath}`;
|
|
76
|
+
if (seen.has(key)) continue;
|
|
77
|
+
seen.add(key);
|
|
78
|
+
totalImports++;
|
|
79
|
+
if (!resolveImport(file, importPath)) {
|
|
80
|
+
console.error(` ✗ ${relFile}`);
|
|
81
|
+
console.error(` imports '${importPath}' → NOT FOUND`);
|
|
82
|
+
failed++;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
80
85
|
}
|
|
81
86
|
|
|
82
|
-
console.log(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
87
|
+
console.log(
|
|
88
|
+
`Checked ${totalImports} relative import(s) across ${files.length} file(s).`,
|
|
89
|
+
);
|
|
90
|
+
console.log(
|
|
91
|
+
failed === 0
|
|
92
|
+
? `\nAll imports resolve OK ✓`
|
|
93
|
+
: `\n${failed} import(s) could not be resolved ✗`,
|
|
94
|
+
);
|
|
86
95
|
|
|
87
96
|
process.exit(failed > 0 ? 1 : 0);
|
package/provider-factory.ts
DELETED
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Provider Factory
|
|
3
|
-
*
|
|
4
|
-
* Extracts the common boilerplate pattern repeated across providers:
|
|
5
|
-
* - API key check
|
|
6
|
-
* - SHOW_PAID flag check
|
|
7
|
-
* - Model fetching with error handling
|
|
8
|
-
* - Provider registration
|
|
9
|
-
* - setupProvider wiring
|
|
10
|
-
*
|
|
11
|
-
* Usage:
|
|
12
|
-
* export default createProvider(pi, {
|
|
13
|
-
* providerId: PROVIDER_NVIDIA,
|
|
14
|
-
* baseUrl: BASE_URL_NVIDIA,
|
|
15
|
-
* apiKeyEnvVar: "NVIDIA_API_KEY",
|
|
16
|
-
* fetchModels: fetchNvidiaModels,
|
|
17
|
-
* showPaidFlag: "NVIDIA_SHOW_PAID",
|
|
18
|
-
* });
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
import type {
|
|
22
|
-
ExtensionAPI,
|
|
23
|
-
ProviderModelConfig,
|
|
24
|
-
} from "@mariozechner/pi-coding-agent";
|
|
25
|
-
import {
|
|
26
|
-
getModalApiKey,
|
|
27
|
-
getNvidiaApiKey,
|
|
28
|
-
getNvidiaShowPaid,
|
|
29
|
-
getOpencodeApiKey,
|
|
30
|
-
} from "./config.ts";
|
|
31
|
-
import { createLogger } from "./lib/logger.ts";
|
|
32
|
-
import { logWarning } from "./lib/util.ts";
|
|
33
|
-
import {
|
|
34
|
-
createReRegister,
|
|
35
|
-
enhanceWithCI,
|
|
36
|
-
type StoredModels,
|
|
37
|
-
setupProvider,
|
|
38
|
-
} from "./provider-helper.ts";
|
|
39
|
-
|
|
40
|
-
const _logger = createLogger("provider-factory");
|
|
41
|
-
|
|
42
|
-
// =============================================================================
|
|
43
|
-
// Types
|
|
44
|
-
// =============================================================================
|
|
45
|
-
|
|
46
|
-
export interface ProviderDefinition {
|
|
47
|
-
/** Provider identifier (e.g., "nvidia", "modal") */
|
|
48
|
-
providerId: string;
|
|
49
|
-
/** Base URL for the API */
|
|
50
|
-
baseUrl: string;
|
|
51
|
-
/** Environment variable name for the API key */
|
|
52
|
-
apiKeyEnvVar: string;
|
|
53
|
-
/** Config key for the API key (e.g., "nvidia_api_key") */
|
|
54
|
-
apiKeyConfigKey: string;
|
|
55
|
-
/** Function to fetch models */
|
|
56
|
-
fetchModels: () => Promise<ProviderModelConfig[]>;
|
|
57
|
-
/** SHOW_PAID flag name (e.g., "NVIDIA_SHOW_PAID") - if set, provider requires this to be true */
|
|
58
|
-
showPaidFlag?: string;
|
|
59
|
-
/** ToS URL to show on first use */
|
|
60
|
-
tosUrl?: string;
|
|
61
|
-
/** Whether this provider has a free tier (free + paid models). Default: false */
|
|
62
|
-
hasFreeTier?: boolean;
|
|
63
|
-
/** Whether to skip creating a toggle command (e.g., for single-model providers). Default: false */
|
|
64
|
-
skipToggle?: boolean;
|
|
65
|
-
/** Additional headers to include in requests */
|
|
66
|
-
extraHeaders?: Record<string, string>;
|
|
67
|
-
/** Optional hook to modify request payload before sending */
|
|
68
|
-
beforeProviderRequest?: (
|
|
69
|
-
payload: Record<string, unknown>,
|
|
70
|
-
) => Record<string, unknown> | undefined;
|
|
71
|
-
/** Optional flag to indicate paid mode (for error handling) */
|
|
72
|
-
isPaidMode?: boolean;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// =============================================================================
|
|
76
|
-
// Config value getters (dynamic lookup)
|
|
77
|
-
// =============================================================================
|
|
78
|
-
|
|
79
|
-
const API_KEY_GETTERS: Record<string, () => string | undefined> = {
|
|
80
|
-
nvidia_api_key: getNvidiaApiKey,
|
|
81
|
-
opencode_api_key: getOpencodeApiKey,
|
|
82
|
-
modal_api_key: getModalApiKey,
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
const SHOW_PAID_GETTERS: Record<string, () => boolean> = {
|
|
86
|
-
NVIDIA_SHOW_PAID: getNvidiaShowPaid,
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
// =============================================================================
|
|
90
|
-
// Factory
|
|
91
|
-
// =============================================================================
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Create a provider with minimal boilerplate.
|
|
95
|
-
*
|
|
96
|
-
* Handles:
|
|
97
|
-
* - API key check
|
|
98
|
-
* - SHOW_PAID flag check (if applicable)
|
|
99
|
-
* - Model fetching with error handling
|
|
100
|
-
* - Provider registration with OpenAI-compatible API
|
|
101
|
-
* - setupProvider wiring for commands and events
|
|
102
|
-
*
|
|
103
|
-
* For providers with OAuth or custom session logic, don't use this factory.
|
|
104
|
-
*/
|
|
105
|
-
export async function createProvider(
|
|
106
|
-
pi: ExtensionAPI,
|
|
107
|
-
def: ProviderDefinition,
|
|
108
|
-
): Promise<void> {
|
|
109
|
-
// 1. Get API key from config
|
|
110
|
-
const getApiKey = API_KEY_GETTERS[def.apiKeyConfigKey];
|
|
111
|
-
if (!getApiKey) {
|
|
112
|
-
_logger.error(`Unknown API key config: ${def.apiKeyConfigKey}`);
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
const apiKey = getApiKey();
|
|
116
|
-
|
|
117
|
-
// 2. Check key exists
|
|
118
|
-
if (!apiKey) {
|
|
119
|
-
_logger.warn(
|
|
120
|
-
`No API key found — set ${def.apiKeyEnvVar} or add ${def.apiKeyConfigKey} to ~/.pi/free.json`,
|
|
121
|
-
);
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// 3. Check paid flag (if applicable)
|
|
126
|
-
if (def.showPaidFlag) {
|
|
127
|
-
const getShowPaid = SHOW_PAID_GETTERS[def.showPaidFlag];
|
|
128
|
-
if (getShowPaid && !getShowPaid()) {
|
|
129
|
-
_logger.info(
|
|
130
|
-
`${def.providerId} disabled. Set ${def.showPaidFlag}=true to enable.`,
|
|
131
|
-
);
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// 4. Fetch models
|
|
137
|
-
let models: ProviderModelConfig[] = [];
|
|
138
|
-
try {
|
|
139
|
-
models = await def.fetchModels();
|
|
140
|
-
} catch (error) {
|
|
141
|
-
logWarning(def.providerId, "Failed to fetch models", error);
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (models.length === 0) {
|
|
146
|
-
_logger.warn(`No models available for ${def.providerId}`);
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// 5. Build storage (free/all or single set)
|
|
151
|
-
const stored: StoredModels = def.hasFreeTier
|
|
152
|
-
? {
|
|
153
|
-
free: models.filter((m) => (m.cost?.input ?? 0) === 0),
|
|
154
|
-
all: models,
|
|
155
|
-
}
|
|
156
|
-
: { free: models, all: models };
|
|
157
|
-
|
|
158
|
-
// 6. Register provider (pass literal key so we don't mutate process.env)
|
|
159
|
-
pi.registerProvider(def.providerId, {
|
|
160
|
-
baseUrl: def.baseUrl,
|
|
161
|
-
apiKey,
|
|
162
|
-
api: "openai-completions" as const,
|
|
163
|
-
headers: {
|
|
164
|
-
"User-Agent": "pi-free-providers",
|
|
165
|
-
...def.extraHeaders,
|
|
166
|
-
},
|
|
167
|
-
models: enhanceWithCI(models),
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
// 7. Setup boilerplate
|
|
171
|
-
const config = {
|
|
172
|
-
providerId: def.providerId,
|
|
173
|
-
baseUrl: def.baseUrl,
|
|
174
|
-
apiKey,
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
const reRegister = createReRegister(pi, config);
|
|
178
|
-
|
|
179
|
-
setupProvider(
|
|
180
|
-
pi,
|
|
181
|
-
{
|
|
182
|
-
providerId: def.providerId,
|
|
183
|
-
tosUrl: def.tosUrl,
|
|
184
|
-
initialShowPaid: def.isPaidMode ?? !!def.showPaidFlag,
|
|
185
|
-
reRegister: (m: ProviderModelConfig[]) => {
|
|
186
|
-
stored.free = m;
|
|
187
|
-
stored.all = m;
|
|
188
|
-
reRegister(m);
|
|
189
|
-
},
|
|
190
|
-
skipToggle: def.skipToggle,
|
|
191
|
-
},
|
|
192
|
-
stored,
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
// 8. Optional: before_provider_request hook
|
|
196
|
-
if (def.beforeProviderRequest) {
|
|
197
|
-
const hook = def.beforeProviderRequest;
|
|
198
|
-
(pi.on as (event: string, handler: (e: unknown) => unknown) => void)(
|
|
199
|
-
"before_provider_request",
|
|
200
|
-
(event: unknown) => {
|
|
201
|
-
const evt = event as { type: string; payload: unknown };
|
|
202
|
-
const payload = evt.payload as Record<string, unknown>;
|
|
203
|
-
return hook(payload);
|
|
204
|
-
},
|
|
205
|
-
);
|
|
206
|
-
}
|
|
207
|
-
}
|