@botconnector/bccli 0.4.0-beta.36 → 0.4.0-beta.38
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/README.md +1 -0
- package/dist/args-MNVI2OJI.js +13 -0
- package/dist/chunk-3HCOAJEQ.js +190 -0
- package/dist/{chunk-CZMTWXCE.js → chunk-4RMP4CNT.js} +38 -124
- package/dist/{chunk-BTCJNE7R.js → chunk-ASTNW3X2.js} +17 -5
- package/dist/chunk-C6PKRYUX.js +115 -0
- package/dist/chunk-H5N4ST6B.js +149 -0
- package/dist/{chunk-QKWSDZJQ.js → chunk-H66PC2EB.js} +46 -119
- package/dist/{chunk-5LSTEXSG.js → chunk-HPZSXX4W.js} +6 -2
- package/dist/chunk-HXSREA7S.js +785 -0
- package/dist/{chunk-NSCC5VMS.js → chunk-OB7FN66F.js} +2 -2
- package/dist/{chunk-ELML4KVQ.js → chunk-R3ND76LW.js} +16 -2
- package/dist/{chunk-SZRM2VOB.js → chunk-TASYQW7B.js} +23 -2
- package/dist/{chunk-3DHJO7NC.js → chunk-U66JJ6RN.js} +1 -1
- package/dist/{chunk-SJH3YLW4.js → chunk-XFTBML3H.js} +4 -1
- package/dist/{chunk-3NTTXK4Y.js → chunk-YURAIW7A.js} +1 -1
- package/dist/cli.js +633 -290
- package/dist/{config-53MC3AH2.js → config-JRO5EOKZ.js} +5 -2
- package/dist/{integrationCli-JWLY6E7E.js → integrationCli-ZTZTQD2I.js} +4 -2
- package/dist/{login-LKAO5PVR.js → login-THRU4U4L.js} +1 -1
- package/dist/{mcpCli-THHBIX7I.js → mcpCli-5ZSAMBJS.js} +33 -5
- package/dist/providerCli-TGDE3633.js +14 -0
- package/dist/session-TVOWVP6Y.js +12 -0
- package/dist/{ui-I5BTZATT.js → ui-W4C6SRHA.js} +87 -38
- package/package.json +2 -1
- package/dist/args-GJUGK4F4.js +0 -12
- package/dist/chunk-HKHKYFZI.js +0 -297
- package/dist/providerCli-WJXJW3W2.js +0 -12
- package/dist/session-7UB77CEG.js +0 -9
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
t
|
|
4
|
+
} from "./chunk-4RMP4CNT.js";
|
|
5
|
+
|
|
6
|
+
// src/presets.ts
|
|
7
|
+
var PRESETS = [
|
|
8
|
+
{ id: "bc-cloud", name: "BotConnector Cloud", baseURL: "https://api.botconnector.id/v1", apiKeyEnv: "BOTCONNECTOR_API_KEY" },
|
|
9
|
+
{ id: "openrouter", name: "OpenRouter", baseURL: "https://openrouter.ai/api/v1", apiKeyEnv: "OPENROUTER_API_KEY" },
|
|
10
|
+
{ id: "openai", name: "OpenAI", baseURL: "https://api.openai.com/v1", apiKeyEnv: "OPENAI_API_KEY" },
|
|
11
|
+
{ id: "gemini", name: "Google Gemini", baseURL: "https://generativelanguage.googleapis.com/v1beta/openai", apiKeyEnv: "GEMINI_API_KEY" },
|
|
12
|
+
{ id: "deepseek", name: "DeepSeek", baseURL: "https://api.deepseek.com/v1", apiKeyEnv: "DEEPSEEK_API_KEY" },
|
|
13
|
+
{ id: "groq", name: "Groq", baseURL: "https://api.groq.com/openai/v1", apiKeyEnv: "GROQ_API_KEY" },
|
|
14
|
+
{ id: "ollama-cloud", name: "Ollama Cloud", baseURL: "https://ollama.com/v1", apiKeyEnv: "OLLAMA_API_KEY" },
|
|
15
|
+
{ id: "ollama", name: "Ollama lokal", baseURL: "http://127.0.0.1:11434/v1", local: true },
|
|
16
|
+
{ id: "lmstudio", name: "LM Studio lokal", baseURL: "http://127.0.0.1:1234/v1", local: true }
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
// src/config.ts
|
|
20
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
21
|
+
import { homedir } from "os";
|
|
22
|
+
import { join } from "path";
|
|
23
|
+
var ConfigError = class extends Error {
|
|
24
|
+
};
|
|
25
|
+
var DEFAULT_MAX_STEPS = 100;
|
|
26
|
+
var DEFAULT_CONFIG = {
|
|
27
|
+
model: "bc-cloud/glm-5.3-flash",
|
|
28
|
+
permissionMode: "default",
|
|
29
|
+
reasoning: "auto",
|
|
30
|
+
vision: true,
|
|
31
|
+
networkPolicy: "allow",
|
|
32
|
+
providers: Object.fromEntries(PRESETS.map((p) => [p.id, { baseURL: p.baseURL, apiKeyEnv: p.apiKeyEnv }])),
|
|
33
|
+
allow: []
|
|
34
|
+
};
|
|
35
|
+
function bccliHome(env = process.env) {
|
|
36
|
+
return env.BCCLI_HOME ?? join(homedir(), ".bccli");
|
|
37
|
+
}
|
|
38
|
+
function readJsonConfig(path) {
|
|
39
|
+
if (!existsSync(path)) return {};
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
42
|
+
} catch (error) {
|
|
43
|
+
throw new ConfigError(t("Invalid config: {path} ({error})", { path, error: error.message }));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function resolvedMaxSteps(global) {
|
|
47
|
+
const value = global.maxSteps;
|
|
48
|
+
if (value === void 0) return DEFAULT_MAX_STEPS;
|
|
49
|
+
if (value === null || value === 0) return null;
|
|
50
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
51
|
+
throw new ConfigError(t("maxSteps must be a positive number, or 0/null to disable the limit."));
|
|
52
|
+
}
|
|
53
|
+
return Math.max(1, Math.floor(value));
|
|
54
|
+
}
|
|
55
|
+
function resolvedNetworkPolicy(global) {
|
|
56
|
+
const value = global.networkPolicy ?? "allow";
|
|
57
|
+
if (value !== "allow" && value !== "offline") {
|
|
58
|
+
throw new ConfigError(t('networkPolicy must be "allow" or "offline".'));
|
|
59
|
+
}
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
function resolvedPlugins(global) {
|
|
63
|
+
const value = global.plugins;
|
|
64
|
+
if (value === void 0) return [];
|
|
65
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
|
|
66
|
+
throw new ConfigError(t("plugins must be an array of strings."));
|
|
67
|
+
}
|
|
68
|
+
return [...value];
|
|
69
|
+
}
|
|
70
|
+
function untrustedProviders(project, known) {
|
|
71
|
+
const out = {};
|
|
72
|
+
for (const [id, provider] of Object.entries(project.providers ?? {})) {
|
|
73
|
+
if (known[id] || typeof provider?.baseURL !== "string") continue;
|
|
74
|
+
out[id] = { baseURL: provider.baseURL };
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
function loadConfig(cwd, env = process.env) {
|
|
79
|
+
const global = readJsonConfig(join(bccliHome(env), "config.json"));
|
|
80
|
+
const project = readJsonConfig(join(cwd, ".bccli", "config.json"));
|
|
81
|
+
const known = { ...DEFAULT_CONFIG.providers, ...global.providers };
|
|
82
|
+
const fromProject = untrustedProviders(project, known);
|
|
83
|
+
return {
|
|
84
|
+
model: project.model ?? global.model ?? DEFAULT_CONFIG.model,
|
|
85
|
+
permissionMode: global.permissionMode ?? DEFAULT_CONFIG.permissionMode,
|
|
86
|
+
reasoning: global.reasoning ?? DEFAULT_CONFIG.reasoning,
|
|
87
|
+
maxSteps: resolvedMaxSteps(global),
|
|
88
|
+
hooks: global.hooks,
|
|
89
|
+
vision: global.vision ?? DEFAULT_CONFIG.vision,
|
|
90
|
+
networkPolicy: resolvedNetworkPolicy(global),
|
|
91
|
+
plugins: resolvedPlugins(global),
|
|
92
|
+
providers: { ...known, ...fromProject },
|
|
93
|
+
projectProviders: Object.keys(fromProject),
|
|
94
|
+
allow: [...global.allow ?? []]
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function readCredentials(env = process.env) {
|
|
98
|
+
const path = join(bccliHome(env), "credentials");
|
|
99
|
+
if (!existsSync(path)) return {};
|
|
100
|
+
try {
|
|
101
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
102
|
+
} catch {
|
|
103
|
+
return {};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function saveCredential(providerId, key, env = process.env) {
|
|
107
|
+
const home = bccliHome(env);
|
|
108
|
+
mkdirSync(home, { recursive: true });
|
|
109
|
+
const path = join(home, "credentials");
|
|
110
|
+
writeFileSync(path, JSON.stringify({ ...readCredentials(env), [providerId]: key }, null, 2), { mode: 384 });
|
|
111
|
+
chmodSync(path, 384);
|
|
112
|
+
}
|
|
113
|
+
function resolveModel(config, modelRef, env = process.env) {
|
|
114
|
+
const slash = modelRef.indexOf("/");
|
|
115
|
+
if (slash <= 0 || slash === modelRef.length - 1) {
|
|
116
|
+
throw new ConfigError(t("Model must be in provider/model format, e.g. bc-cloud/glm-5.3-flash (got: {ref})", { ref: modelRef }));
|
|
117
|
+
}
|
|
118
|
+
const providerId = modelRef.slice(0, slash);
|
|
119
|
+
const provider = config.providers[providerId];
|
|
120
|
+
if (!provider) {
|
|
121
|
+
throw new ConfigError(t('Provider "{id}" is not in the config. Available: {list}', { id: providerId, list: Object.keys(config.providers).join(", ") }));
|
|
122
|
+
}
|
|
123
|
+
const apiKey = (provider.apiKeyEnv ? env[provider.apiKeyEnv] : void 0) || readCredentials(env)[providerId] || void 0;
|
|
124
|
+
if (provider.apiKeyEnv && !apiKey) {
|
|
125
|
+
throw new ConfigError(t("No API key for {id} yet. Run `bccli login` or set the env var {env}.", { id: providerId, env: provider.apiKeyEnv }));
|
|
126
|
+
}
|
|
127
|
+
return { providerId, model: modelRef.slice(slash + 1), baseURL: provider.baseURL.replace(/\/+$/, ""), apiKey };
|
|
128
|
+
}
|
|
129
|
+
function removeCredential(providerId, env = process.env) {
|
|
130
|
+
const creds = readCredentials(env);
|
|
131
|
+
if (!(providerId in creds)) return false;
|
|
132
|
+
delete creds[providerId];
|
|
133
|
+
const path = join(bccliHome(env), "credentials");
|
|
134
|
+
writeFileSync(path, JSON.stringify(creds, null, 2), { mode: 384 });
|
|
135
|
+
chmodSync(path, 384);
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export {
|
|
140
|
+
PRESETS,
|
|
141
|
+
ConfigError,
|
|
142
|
+
bccliHome,
|
|
143
|
+
readJsonConfig,
|
|
144
|
+
loadConfig,
|
|
145
|
+
readCredentials,
|
|
146
|
+
saveCredential,
|
|
147
|
+
resolveModel,
|
|
148
|
+
removeCredential
|
|
149
|
+
};
|
|
@@ -1,20 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
hasKey,
|
|
4
|
-
providerName,
|
|
5
|
-
removeGlobalProvider,
|
|
6
|
-
writeGlobalConfig
|
|
7
|
-
} from "./chunk-3DHJO7NC.js";
|
|
8
2
|
import {
|
|
9
3
|
reasoningPayload
|
|
10
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-YURAIW7A.js";
|
|
11
5
|
import {
|
|
12
|
-
PRESETS,
|
|
13
|
-
loadConfig,
|
|
14
|
-
readCredentials,
|
|
15
|
-
saveCredential,
|
|
16
6
|
t
|
|
17
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-4RMP4CNT.js";
|
|
18
8
|
|
|
19
9
|
// src/provider.ts
|
|
20
10
|
import OpenAI from "openai";
|
|
@@ -98,15 +88,15 @@ function detectRepetition(text) {
|
|
|
98
88
|
}
|
|
99
89
|
function sleep(ms, signal) {
|
|
100
90
|
return new Promise((resolve, reject) => {
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
);
|
|
91
|
+
const onAbort = () => {
|
|
92
|
+
clearTimeout(timer);
|
|
93
|
+
reject(signal?.reason);
|
|
94
|
+
};
|
|
95
|
+
const timer = setTimeout(() => {
|
|
96
|
+
signal?.removeEventListener("abort", onAbort);
|
|
97
|
+
resolve();
|
|
98
|
+
}, ms);
|
|
99
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
110
100
|
});
|
|
111
101
|
}
|
|
112
102
|
function errorMessage(body) {
|
|
@@ -205,11 +195,38 @@ async function readJson(res) {
|
|
|
205
195
|
const thinking = (choice?.message?.reasoning_content || choice?.message?.reasoning || "") + split.thinking;
|
|
206
196
|
return { text: split.text, toolCalls, thinking: thinking || void 0, usage: toUsage(body.usage), finishReason: choice?.finish_reason };
|
|
207
197
|
}
|
|
198
|
+
function contentText(content) {
|
|
199
|
+
if (typeof content === "string") return content;
|
|
200
|
+
const text = content.filter((part) => part.type === "text").map((part) => part.text).join(" ").trim();
|
|
201
|
+
const hasImage = content.some((part) => part.type === "image_url");
|
|
202
|
+
if (!hasImage) return text;
|
|
203
|
+
return text ? `${text} [image]` : "[image]";
|
|
204
|
+
}
|
|
205
|
+
function imageUserMessage(images) {
|
|
206
|
+
const paths = images.map((image) => image.path);
|
|
207
|
+
const caption = paths.length === 1 ? `Image read from ${paths[0]}.` : `Images read from ${paths.join(", ")}.`;
|
|
208
|
+
return {
|
|
209
|
+
role: "user",
|
|
210
|
+
content: [
|
|
211
|
+
{ type: "text", text: caption },
|
|
212
|
+
...images.map((image) => ({ type: "image_url", image_url: { url: `data:${image.mediaType};base64,${image.data}` } }))
|
|
213
|
+
]
|
|
214
|
+
};
|
|
215
|
+
}
|
|
208
216
|
function responseInputFromMessages(messages) {
|
|
209
217
|
const input = [];
|
|
210
218
|
for (const message of messages) {
|
|
211
219
|
if (message.role === "system" || message.role === "user") {
|
|
212
|
-
|
|
220
|
+
if (Array.isArray(message.content)) {
|
|
221
|
+
input.push({
|
|
222
|
+
role: message.role,
|
|
223
|
+
content: message.content.map(
|
|
224
|
+
(part) => part.type === "image_url" ? { type: "input_image", image_url: part.image_url.url } : { type: "input_text", text: part.text }
|
|
225
|
+
)
|
|
226
|
+
});
|
|
227
|
+
} else {
|
|
228
|
+
input.push({ role: message.role, content: message.content });
|
|
229
|
+
}
|
|
213
230
|
continue;
|
|
214
231
|
}
|
|
215
232
|
if (message.role === "assistant") {
|
|
@@ -249,7 +266,7 @@ function shouldForceLunaPtc(messages, enabled) {
|
|
|
249
266
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
250
267
|
const message = messages[i];
|
|
251
268
|
if (message.role === "assistant" && message.responses_output_items?.some((item) => item.type === "program")) return true;
|
|
252
|
-
if (message.role === "user") return LUNA_PTC_FORCE_CUE.test(message.content);
|
|
269
|
+
if (message.role === "user") return LUNA_PTC_FORCE_CUE.test(contentText(message.content));
|
|
253
270
|
}
|
|
254
271
|
return false;
|
|
255
272
|
}
|
|
@@ -264,7 +281,7 @@ function shouldEnableLunaPtc(messages, enabled, programmaticToolNames = []) {
|
|
|
264
281
|
}
|
|
265
282
|
const turn = messages.slice(Math.max(0, lastUser));
|
|
266
283
|
const latestUser = lastUser >= 0 ? messages[lastUser] : void 0;
|
|
267
|
-
const prompt = latestUser?.role === "user" ? latestUser.content : "";
|
|
284
|
+
const prompt = latestUser?.role === "user" ? contentText(latestUser.content) : "";
|
|
268
285
|
if (LUNA_PTC_MULTI_WORK_CUE.test(prompt)) return true;
|
|
269
286
|
const safeToolNames = /* @__PURE__ */ new Set([...LUNA_PTC_SAFE_FUNCTIONS, ...programmaticToolNames]);
|
|
270
287
|
const safeCalls = /* @__PURE__ */ new Set();
|
|
@@ -852,106 +869,16 @@ function createProvider(options) {
|
|
|
852
869
|
const res = await doFetch(`${baseURL}/models`, { headers });
|
|
853
870
|
if (!res.ok) throw new ProviderError(t("{status} while fetching the model list", { status: res.status }), res.status);
|
|
854
871
|
const body = await res.json();
|
|
855
|
-
|
|
872
|
+
const ids = (body.data ?? []).map((m) => m?.id).filter((id) => typeof id === "string" && id.length > 0);
|
|
873
|
+
return [...new Set(ids)].sort();
|
|
856
874
|
}
|
|
857
875
|
};
|
|
858
876
|
}
|
|
859
877
|
|
|
860
|
-
// src/models.ts
|
|
861
|
-
function order(ids) {
|
|
862
|
-
const presetIds = PRESETS.map((p) => p.id);
|
|
863
|
-
const presets = presetIds.filter((id) => ids.includes(id));
|
|
864
|
-
const custom = ids.filter((id) => !presetIds.includes(id)).sort();
|
|
865
|
-
return [...presets, ...custom];
|
|
866
|
-
}
|
|
867
|
-
async function listAllModels(config, env = process.env, opts = {}) {
|
|
868
|
-
const timeoutMs = opts.timeoutMs ?? 5e3;
|
|
869
|
-
const baseFetch = opts.fetch ?? fetch;
|
|
870
|
-
const untrusted = new Set(config.projectProviders ?? []);
|
|
871
|
-
const ids = order(Object.keys(config.providers)).filter((id) => opts.only ? id === opts.only : !untrusted.has(id) && hasKey(config, id, env));
|
|
872
|
-
const results = await Promise.all(
|
|
873
|
-
ids.map(async (id) => {
|
|
874
|
-
const provider = config.providers[id];
|
|
875
|
-
const apiKey = (opts.keyOverride && id === opts.only ? opts.keyOverride : void 0) || (provider.apiKeyEnv ? env[provider.apiKeyEnv] : void 0) || readCredentials(env)[id] || void 0;
|
|
876
|
-
const local = !provider.apiKeyEnv;
|
|
877
|
-
const timedFetch = ((input, init) => baseFetch(input, { ...init, signal: AbortSignal.timeout(timeoutMs) }));
|
|
878
|
-
const list = createProvider({ baseURL: provider.baseURL.replace(/\/+$/, ""), apiKey, model: "", fetch: timedFetch }).listModels();
|
|
879
|
-
let timer;
|
|
880
|
-
const timeout = new Promise((_, reject) => {
|
|
881
|
-
timer = setTimeout(() => reject(new Error("timeout")), timeoutMs);
|
|
882
|
-
});
|
|
883
|
-
try {
|
|
884
|
-
const models = await Promise.race([list, timeout]);
|
|
885
|
-
return { providerId: id, providerName: providerName(config, id), models };
|
|
886
|
-
} catch {
|
|
887
|
-
if (local && !opts.only) return void 0;
|
|
888
|
-
return { providerId: id, providerName: providerName(config, id), models: [], error: t("could not be reached") };
|
|
889
|
-
} finally {
|
|
890
|
-
clearTimeout(timer);
|
|
891
|
-
}
|
|
892
|
-
})
|
|
893
|
-
);
|
|
894
|
-
return results.filter((g) => !!g);
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
// src/providerCli.ts
|
|
898
|
-
async function addProviderKey(providerId, key, deps) {
|
|
899
|
-
const config = loadConfig(deps.cwd, deps.env);
|
|
900
|
-
const [group] = await listAllModels(config, deps.env, { only: providerId, fetch: deps.fetch, keyOverride: key || void 0 });
|
|
901
|
-
if (!group || group.error) return { ok: false, error: t("The API key was rejected or {provider} could not be reached.", { provider: providerName(config, providerId) }) };
|
|
902
|
-
if (key) saveCredential(providerId, key, deps.env);
|
|
903
|
-
return { ok: true, models: group.models.length };
|
|
904
|
-
}
|
|
905
|
-
async function runProviderCommand(args, deps) {
|
|
906
|
-
const [action = "list", id] = args.subArgs;
|
|
907
|
-
const config = loadConfig(deps.cwd, deps.env);
|
|
908
|
-
if (action === "list") {
|
|
909
|
-
for (const pid of Object.keys(config.providers)) {
|
|
910
|
-
const mark = hasKey(config, pid, deps.env) ? "\u2713" : "\u25CB";
|
|
911
|
-
deps.out(`${mark} ${pid.padEnd(14)} ${providerName(config, pid).padEnd(20)} ${config.providers[pid].baseURL}`);
|
|
912
|
-
}
|
|
913
|
-
return 0;
|
|
914
|
-
}
|
|
915
|
-
if (!id) {
|
|
916
|
-
deps.err(t("Usage: bccli provider {action} <id>", { action }));
|
|
917
|
-
return 1;
|
|
918
|
-
}
|
|
919
|
-
if (action === "remove") {
|
|
920
|
-
removeGlobalProvider(id, deps.env);
|
|
921
|
-
deps.out(t("{id} removed.", { id }));
|
|
922
|
-
return 0;
|
|
923
|
-
}
|
|
924
|
-
if (action !== "add") {
|
|
925
|
-
deps.err(t("Unknown action: {action}. Use list, add, or remove.", { action }));
|
|
926
|
-
return 1;
|
|
927
|
-
}
|
|
928
|
-
const preset = PRESETS.find((p) => p.id === id);
|
|
929
|
-
if (!preset && !config.providers[id]) {
|
|
930
|
-
if (!args.url) {
|
|
931
|
-
deps.err(t("{id} is not a preset. For a custom provider add --url <OpenAI-compatible base URL>.", { id }));
|
|
932
|
-
return 1;
|
|
933
|
-
}
|
|
934
|
-
writeGlobalConfig(
|
|
935
|
-
{ providers: { [id]: { baseURL: args.url, ...args.name ? { name: args.name } : {}, ...args.keyEnv ? { apiKeyEnv: args.keyEnv } : {} } } },
|
|
936
|
-
deps.env
|
|
937
|
-
);
|
|
938
|
-
}
|
|
939
|
-
const needsKey = !!loadConfig(deps.cwd, deps.env).providers[id].apiKeyEnv || !preset;
|
|
940
|
-
const key = needsKey ? await deps.readSecret(t("API key for {id} (leave empty if not needed): ", { id })) : "";
|
|
941
|
-
const result = await addProviderKey(id, key, deps);
|
|
942
|
-
if (!result.ok) {
|
|
943
|
-
deps.err(result.error);
|
|
944
|
-
return 1;
|
|
945
|
-
}
|
|
946
|
-
deps.out(t("{id} is ready. {n} models available.", { id, n: result.models }));
|
|
947
|
-
return 0;
|
|
948
|
-
}
|
|
949
|
-
|
|
950
878
|
export {
|
|
879
|
+
contentText,
|
|
880
|
+
imageUserMessage,
|
|
951
881
|
hasPendingProgrammaticReplay,
|
|
952
882
|
completionAssistantMessage,
|
|
953
|
-
createProvider
|
|
954
|
-
listAllModels,
|
|
955
|
-
addProviderKey,
|
|
956
|
-
runProviderCommand
|
|
883
|
+
createProvider
|
|
957
884
|
};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
ConfigError
|
|
3
|
+
ConfigError
|
|
4
|
+
} from "./chunk-H5N4ST6B.js";
|
|
5
|
+
import {
|
|
4
6
|
t
|
|
5
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-4RMP4CNT.js";
|
|
6
8
|
|
|
7
9
|
// src/mcp/catalog.ts
|
|
8
10
|
var CATALOG = [
|
|
@@ -12,6 +14,7 @@ var CATALOG = [
|
|
|
12
14
|
{
|
|
13
15
|
name: "filesystem",
|
|
14
16
|
description: "access folders outside the project",
|
|
17
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: ${dir} is filled by fillTemplate, not a template literal
|
|
15
18
|
config: { command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem", "${dir}"] },
|
|
16
19
|
inputs: [{ key: "dir", label: "Folder the server may access" }]
|
|
17
20
|
},
|
|
@@ -19,6 +22,7 @@ var CATALOG = [
|
|
|
19
22
|
{
|
|
20
23
|
name: "github",
|
|
21
24
|
description: "GitHub issues, PRs, and repos",
|
|
25
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: ${token} is filled by fillTemplate, not a template literal
|
|
22
26
|
config: { type: "http", url: "https://api.githubcopilot.com/mcp/", headers: { Authorization: "Bearer ${token}" } },
|
|
23
27
|
inputs: [{ key: "token", label: "GitHub Personal Access Token", secret: true }]
|
|
24
28
|
}
|