@vtstech/pi-api 1.2.2 → 1.2.3
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/api.js +177 -6
- package/package.json +10 -6
package/api.js
CHANGED
|
@@ -1,7 +1,178 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
// shared/format.ts
|
|
2
|
+
function section(title) {
|
|
3
|
+
return `
|
|
4
|
+
\u2500\u2500 ${title} ${"\u2500".repeat(Math.max(1, 60 - title.length - 4))}`;
|
|
5
|
+
}
|
|
6
|
+
function ok(msg) {
|
|
7
|
+
return ` \u2705 ${msg}`;
|
|
8
|
+
}
|
|
9
|
+
function warn(msg) {
|
|
10
|
+
return ` \u26A0\uFE0F ${msg}`;
|
|
11
|
+
}
|
|
12
|
+
function info(msg) {
|
|
13
|
+
return ` \u2139\uFE0F ${msg}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// shared/ollama.ts
|
|
17
|
+
import * as fs from "node:fs";
|
|
18
|
+
import * as path from "node:path";
|
|
19
|
+
import os from "node:os";
|
|
20
|
+
|
|
21
|
+
// shared/debug.ts
|
|
22
|
+
var DEBUG_ENABLED = process?.env?.PI_EXTENSIONS_DEBUG === "1";
|
|
23
|
+
function debugLog(module, message, ...args) {
|
|
24
|
+
if (!DEBUG_ENABLED) return;
|
|
25
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
26
|
+
console.debug(`[pi-ext:${module}] ${timestamp} ${message}`, ...args);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// shared/ollama.ts
|
|
30
|
+
var EXTENSION_VERSION = "1.2.3";
|
|
31
|
+
var MODELS_JSON_PATH = path.join(os.homedir(), ".pi", "agent", "models.json");
|
|
32
|
+
var _modelsJsonCache = null;
|
|
33
|
+
var _ollamaBaseUrlCache = null;
|
|
34
|
+
var CACHE_TTL_MS = 2e3;
|
|
35
|
+
function getOllamaBaseUrl() {
|
|
36
|
+
const now = Date.now();
|
|
37
|
+
if (_ollamaBaseUrlCache && now - _ollamaBaseUrlCache.ts < CACHE_TTL_MS) return _ollamaBaseUrlCache.data;
|
|
38
|
+
try {
|
|
39
|
+
if (fs.existsSync(MODELS_JSON_PATH)) {
|
|
40
|
+
const raw = fs.readFileSync(MODELS_JSON_PATH, "utf-8");
|
|
41
|
+
const config = JSON.parse(raw);
|
|
42
|
+
const baseUrl = config?.providers?.["ollama"]?.baseUrl;
|
|
43
|
+
if (baseUrl) {
|
|
44
|
+
const result = baseUrl.replace(/\/v1\/?$/, "");
|
|
45
|
+
_ollamaBaseUrlCache = { data: result, ts: now };
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
} catch (err) {
|
|
50
|
+
debugLog("ollama", "failed to parse models.json for base URL", err);
|
|
51
|
+
}
|
|
52
|
+
if (process.env.OLLAMA_HOST) {
|
|
53
|
+
const result = `http://${process.env.OLLAMA_HOST.replace(/^https?:\/\//, "")}`;
|
|
54
|
+
_ollamaBaseUrlCache = { data: result, ts: now };
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
const fallback = "http://localhost:11434";
|
|
58
|
+
_ollamaBaseUrlCache = { data: fallback, ts: now };
|
|
59
|
+
return fallback;
|
|
60
|
+
}
|
|
61
|
+
function readModelsJson() {
|
|
62
|
+
const now = Date.now();
|
|
63
|
+
if (_modelsJsonCache && now - _modelsJsonCache.ts < CACHE_TTL_MS) return _modelsJsonCache.data;
|
|
64
|
+
try {
|
|
65
|
+
if (fs.existsSync(MODELS_JSON_PATH)) {
|
|
66
|
+
const raw = fs.readFileSync(MODELS_JSON_PATH, "utf-8");
|
|
67
|
+
const data = JSON.parse(raw);
|
|
68
|
+
_modelsJsonCache = { data, ts: now };
|
|
69
|
+
return data;
|
|
70
|
+
}
|
|
71
|
+
} catch (err) {
|
|
72
|
+
debugLog("ollama", "failed to read/parse models.json", err);
|
|
73
|
+
}
|
|
74
|
+
const empty = { providers: {} };
|
|
75
|
+
_modelsJsonCache = { data: empty, ts: now };
|
|
76
|
+
return empty;
|
|
77
|
+
}
|
|
78
|
+
function writeModelsJson(data) {
|
|
79
|
+
const dir = path.dirname(MODELS_JSON_PATH);
|
|
80
|
+
if (!fs.existsSync(dir)) {
|
|
81
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
82
|
+
}
|
|
83
|
+
const tmpPath = MODELS_JSON_PATH + ".tmp";
|
|
84
|
+
fs.writeFileSync(tmpPath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
85
|
+
fs.renameSync(tmpPath, MODELS_JSON_PATH);
|
|
86
|
+
_modelsJsonCache = null;
|
|
87
|
+
_ollamaBaseUrlCache = null;
|
|
88
|
+
}
|
|
89
|
+
var _modelsJsonLock = null;
|
|
90
|
+
async function acquireModelsJsonLock() {
|
|
91
|
+
while (_modelsJsonLock) {
|
|
92
|
+
await _modelsJsonLock;
|
|
93
|
+
}
|
|
94
|
+
let releaseLock;
|
|
95
|
+
_modelsJsonLock = new Promise((resolve) => {
|
|
96
|
+
releaseLock = resolve;
|
|
97
|
+
});
|
|
98
|
+
return {
|
|
99
|
+
release: () => {
|
|
100
|
+
releaseLock();
|
|
101
|
+
_modelsJsonLock = null;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
async function readModifyWriteModelsJson(modifier) {
|
|
106
|
+
const { release } = await acquireModelsJsonLock();
|
|
107
|
+
try {
|
|
108
|
+
const data = readModelsJson();
|
|
109
|
+
const modified = modifier(data);
|
|
110
|
+
if (modified === null) return false;
|
|
111
|
+
writeModelsJson(modified);
|
|
112
|
+
return true;
|
|
113
|
+
} finally {
|
|
114
|
+
release();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
var BUILTIN_PROVIDERS = {
|
|
118
|
+
openrouter: { api: "openai-completions", baseUrl: "https://openrouter.ai/api/v1", envKey: "OPENROUTER_API_KEY" },
|
|
119
|
+
anthropic: { api: "anthropic-messages", baseUrl: "https://api.anthropic.com/v1", envKey: "ANTHROPIC_API_KEY" },
|
|
120
|
+
google: { api: "gemini", baseUrl: "https://generativelanguage.googleapis.com", envKey: "GOOGLE_API_KEY" },
|
|
121
|
+
openai: { api: "openai-completions", baseUrl: "https://api.openai.com/v1", envKey: "OPENAI_API_KEY" },
|
|
122
|
+
groq: { api: "openai-completions", baseUrl: "https://api.groq.com/v1", envKey: "GROQ_API_KEY" },
|
|
123
|
+
deepseek: { api: "openai-completions", baseUrl: "https://api.deepseek.com/v1", envKey: "DEEPSEEK_API_KEY" },
|
|
124
|
+
mistral: { api: "openai-completions", baseUrl: "https://api.mistral.ai/v1", envKey: "MISTRAL_API_KEY" },
|
|
125
|
+
xai: { api: "openai-completions", baseUrl: "https://api.x.ai/v1", envKey: "XAI_API_KEY" },
|
|
126
|
+
together: { api: "openai-completions", baseUrl: "https://api.together.xyz/v1", envKey: "TOGETHER_API_KEY" },
|
|
127
|
+
fireworks: { api: "openai-completions", baseUrl: "https://api.fireworks.ai/inference/v1", envKey: "FIREWORKS_API_KEY" },
|
|
128
|
+
cohere: { api: "cohere-chat", baseUrl: "https://api.cohere.com/v1", envKey: "COHERE_API_KEY" },
|
|
129
|
+
zai: { api: "openai-completions", baseUrl: "https://open.bigmodel.cn/api/paas/v4", envKey: "ZAI_API_KEY" }
|
|
130
|
+
};
|
|
131
|
+
function isLocalProvider(baseUrl, providerName) {
|
|
132
|
+
if (providerName === "ollama") return true;
|
|
133
|
+
const url = baseUrl || "";
|
|
134
|
+
return url.includes("localhost") || url.includes("127.0.0.1") || url.includes("0.0.0.0");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// shared/config-io.ts
|
|
138
|
+
import * as fs2 from "fs";
|
|
139
|
+
import * as path2 from "path";
|
|
140
|
+
import os2 from "os";
|
|
141
|
+
var PI_AGENT_DIR = path2.join(os2.homedir(), ".pi", "agent");
|
|
142
|
+
function readJsonConfig(filePath, defaultValue = {}) {
|
|
143
|
+
try {
|
|
144
|
+
if (fs2.existsSync(filePath)) {
|
|
145
|
+
return JSON.parse(fs2.readFileSync(filePath, "utf-8"));
|
|
146
|
+
}
|
|
147
|
+
} catch (err) {
|
|
148
|
+
debugLog("config-io", `failed to read config: ${filePath}`, err);
|
|
149
|
+
}
|
|
150
|
+
return defaultValue;
|
|
151
|
+
}
|
|
152
|
+
function writeJsonConfig(filePath, data) {
|
|
153
|
+
const dir = path2.dirname(filePath);
|
|
154
|
+
if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
|
|
155
|
+
const content = JSON.stringify(data, null, 2) + "\n";
|
|
156
|
+
const tmpPath = filePath + ".tmp";
|
|
157
|
+
try {
|
|
158
|
+
fs2.writeFileSync(tmpPath, content, "utf-8");
|
|
159
|
+
fs2.renameSync(tmpPath, filePath);
|
|
160
|
+
} catch {
|
|
161
|
+
fs2.writeFileSync(filePath, content, "utf-8");
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
var SETTINGS_PATH = path2.join(PI_AGENT_DIR, "settings.json");
|
|
165
|
+
var SECURITY_PATH = path2.join(PI_AGENT_DIR, "security.json");
|
|
166
|
+
var REACT_MODE_PATH = path2.join(PI_AGENT_DIR, "react-mode.json");
|
|
167
|
+
var MODEL_TEST_CONFIG_PATH = path2.join(PI_AGENT_DIR, "model-test-config.json");
|
|
168
|
+
function readSettings() {
|
|
169
|
+
return readJsonConfig(SETTINGS_PATH);
|
|
170
|
+
}
|
|
171
|
+
function writeSettings(data) {
|
|
172
|
+
writeJsonConfig(SETTINGS_PATH, data);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// extensions/api.ts
|
|
5
176
|
var API_MODES = {
|
|
6
177
|
"anthropic-messages": "Anthropic Claude API and compatibles",
|
|
7
178
|
"openai-completions": "OpenAI Chat Completions API and compatibles",
|
|
@@ -64,7 +235,7 @@ function resolveProvider(config, explicit, currentProvider) {
|
|
|
64
235
|
if (!provider) return null;
|
|
65
236
|
return { name: local.name, config: provider };
|
|
66
237
|
}
|
|
67
|
-
function
|
|
238
|
+
function api_default(pi) {
|
|
68
239
|
const branding = [
|
|
69
240
|
` \u26A1 Pi API Mode Switcher v${EXTENSION_VERSION}`,
|
|
70
241
|
` Written by VTSTech`,
|
|
@@ -609,5 +780,5 @@ function api_temp_default(pi) {
|
|
|
609
780
|
});
|
|
610
781
|
}
|
|
611
782
|
export {
|
|
612
|
-
|
|
783
|
+
api_default as default
|
|
613
784
|
};
|
package/package.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vtstech/pi-api",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "API Mode Switcher extension for Pi Coding Agent",
|
|
5
5
|
"main": "api.js",
|
|
6
|
-
"keywords": [
|
|
6
|
+
"keywords": [
|
|
7
|
+
"pi-package",
|
|
8
|
+
"pi",
|
|
9
|
+
"pi-coding-agent",
|
|
10
|
+
"pi-extensions"
|
|
11
|
+
],
|
|
7
12
|
"license": "MIT",
|
|
8
13
|
"access": "public",
|
|
9
14
|
"type": "module",
|
|
@@ -13,13 +18,12 @@
|
|
|
13
18
|
"type": "git",
|
|
14
19
|
"url": "https://github.com/VTSTech/pi-coding-agent"
|
|
15
20
|
},
|
|
16
|
-
"dependencies": {
|
|
17
|
-
"@vtstech/pi-shared": "1.2.2"
|
|
18
|
-
},
|
|
19
21
|
"peerDependencies": {
|
|
20
22
|
"@mariozechner/pi-coding-agent": ">=0.66"
|
|
21
23
|
},
|
|
22
24
|
"pi": {
|
|
23
|
-
"extensions": [
|
|
25
|
+
"extensions": [
|
|
26
|
+
"./api.js"
|
|
27
|
+
]
|
|
24
28
|
}
|
|
25
29
|
}
|