@botconnector/bccli 0.4.0-beta.8 → 0.4.0
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.en.md +242 -0
- package/README.md +95 -3
- package/dist/acp-ATIL4JOZ.js +182 -0
- package/dist/args-J5ONVGIF.js +13 -0
- package/dist/chunk-45SDLMTV.js +341 -0
- package/dist/chunk-6M5X2KZE.js +8 -0
- package/dist/chunk-7F3IWBPQ.js +191 -0
- package/dist/{chunk-CLN4Z5E3.js → chunk-BLOYFIAD.js} +6 -3
- package/dist/chunk-BNA74HPN.js +145 -0
- package/dist/{chunk-DX7CZTBB.js → chunk-BQ2IGPBR.js} +14 -9
- package/dist/chunk-JL5ZCSP2.js +76 -0
- package/dist/chunk-KEMYIGCP.js +235 -0
- package/dist/{chunk-BFZXRKW5.js → chunk-L7QI6JN2.js} +24 -2
- package/dist/chunk-LACUWBWS.js +361 -0
- package/dist/chunk-M7CALSP3.js +733 -0
- package/dist/{chunk-ELML4KVQ.js → chunk-O7M3TGIW.js} +34 -2
- package/dist/chunk-PSO7R5AD.js +10 -0
- package/dist/chunk-PUN4HMAG.js +940 -0
- package/dist/{chunk-SJH3YLW4.js → chunk-QDQU6VY2.js} +60 -6
- package/dist/chunk-QZKLZELZ.js +64 -0
- package/dist/{chunk-7S6JXQCH.js → chunk-SPRNLS7K.js} +2 -1
- package/dist/chunk-TLPFQIS2.js +231 -0
- package/dist/chunk-TVALKHFB.js +2143 -0
- package/dist/chunk-UZWJQF67.js +500 -0
- package/dist/chunk-XBO5GACQ.js +56 -0
- package/dist/cli.js +162 -1383
- package/dist/completion-HPPFDAPB.js +125 -0
- package/dist/{config-YUNKPCNB.js → config-COPNMMV6.js} +5 -2
- package/dist/config-RZ5JONMX.js +30 -0
- package/dist/ghBotCli-IBCZIWT5.js +677 -0
- package/dist/integrationCli-P75R2VSC.js +685 -0
- package/dist/login-SYIQP46J.js +16 -0
- package/dist/{mcpCli-DZWQPTOS.js → mcpCli-QUV5SD64.js} +42 -9
- package/dist/modelsCli-TRRB5LX4.js +36 -0
- package/dist/npmRunner-LU43GM3W.js +46 -0
- package/dist/oauthLogin-6FWQABJ7.js +210 -0
- package/dist/pluginsCli-GNW4TPVV.js +284 -0
- package/dist/prCli-5IMV3VNQ.js +78 -0
- package/dist/providerCli-FAOZHOKW.js +15 -0
- package/dist/sdk.js +5490 -0
- package/dist/serve-6QLBJUYW.js +178 -0
- package/dist/serve-JG7RLIUD.js +515 -0
- package/dist/session-KBAQ4BZ5.js +12 -0
- package/dist/setup-FJAWDWV4.js +24 -0
- package/dist/statsCli-CF3R7ATL.js +353 -0
- package/dist/transferCli-TK2GSVRK.js +71 -0
- package/dist/ui-B3CJ6TGE.js +2365 -0
- package/dist/update-K5IQ2MYR.js +145 -0
- package/package.json +25 -4
- package/dist/args-FLTRM57K.js +0 -12
- package/dist/chunk-2MGINW4O.js +0 -114
- package/dist/chunk-IM3KMPVZ.js +0 -52
- package/dist/chunk-KV27KTOX.js +0 -254
- package/dist/chunk-U7GK73CK.js +0 -341
- package/dist/chunk-Z7PM3LTC.js +0 -93
- package/dist/login-JXFORO3J.js +0 -9
- package/dist/providerCli-Q7LRM2NP.js +0 -12
- package/dist/session-7UB77CEG.js +0 -9
- package/dist/ui-6GTQUJOE.js +0 -1415
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
t
|
|
4
|
+
} from "./chunk-UZWJQF67.js";
|
|
5
|
+
|
|
6
|
+
// src/config.ts
|
|
7
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
|
|
8
|
+
import { homedir } from "os";
|
|
9
|
+
import { dirname, join } from "path";
|
|
10
|
+
|
|
11
|
+
// src/presets.ts
|
|
12
|
+
var PRESETS = [
|
|
13
|
+
{ id: "bc-cloud", name: "BotConnector Cloud", baseURL: "https://api.botconnector.id/v1", apiKeyEnv: "BOTCONNECTOR_API_KEY" },
|
|
14
|
+
{ id: "openrouter", name: "OpenRouter", baseURL: "https://openrouter.ai/api/v1", apiKeyEnv: "OPENROUTER_API_KEY" },
|
|
15
|
+
{ id: "openai", name: "OpenAI", baseURL: "https://api.openai.com/v1", apiKeyEnv: "OPENAI_API_KEY" },
|
|
16
|
+
{ id: "gemini", name: "Google Gemini", baseURL: "https://generativelanguage.googleapis.com/v1beta/openai", apiKeyEnv: "GEMINI_API_KEY" },
|
|
17
|
+
{ id: "deepseek", name: "DeepSeek", baseURL: "https://api.deepseek.com/v1", apiKeyEnv: "DEEPSEEK_API_KEY" },
|
|
18
|
+
{ id: "groq", name: "Groq", baseURL: "https://api.groq.com/openai/v1", apiKeyEnv: "GROQ_API_KEY" },
|
|
19
|
+
{ id: "ollama-cloud", name: "Ollama Cloud", baseURL: "https://ollama.com/v1", apiKeyEnv: "OLLAMA_API_KEY" },
|
|
20
|
+
{ id: "ollama", name: "Ollama lokal", baseURL: "http://127.0.0.1:11434/v1", local: true },
|
|
21
|
+
{ id: "lmstudio", name: "LM Studio lokal", baseURL: "http://127.0.0.1:1234/v1", local: true }
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
// src/keybinds.ts
|
|
25
|
+
var KEYBIND_ACTIONS = ["thinking", "toolOutput", "pasteImage"];
|
|
26
|
+
var DEFAULT_KEYBINDS = {
|
|
27
|
+
thinking: "ctrl+t",
|
|
28
|
+
toolOutput: "ctrl+o",
|
|
29
|
+
pasteImage: "alt+v"
|
|
30
|
+
};
|
|
31
|
+
function defaultKeybinds() {
|
|
32
|
+
return Object.fromEntries(KEYBIND_ACTIONS.map((action) => [action, parseKeybind(DEFAULT_KEYBINDS[action])]));
|
|
33
|
+
}
|
|
34
|
+
function parseKeybind(raw) {
|
|
35
|
+
const spec = raw.trim().toLowerCase();
|
|
36
|
+
const parts = spec.split("+");
|
|
37
|
+
const key = parts.pop() ?? "";
|
|
38
|
+
if (!/^[a-z]$/.test(key)) return void 0;
|
|
39
|
+
if (!parts.length || new Set(parts).size !== parts.length) return void 0;
|
|
40
|
+
if (parts.some((p) => p !== "ctrl" && p !== "alt")) return void 0;
|
|
41
|
+
return { spec, ctrl: parts.includes("ctrl"), meta: parts.includes("alt"), key };
|
|
42
|
+
}
|
|
43
|
+
function matchKeybind(kb, input, key) {
|
|
44
|
+
return kb.ctrl === !!key.ctrl && kb.meta === !!key.meta && input.toLowerCase() === kb.key;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/ui/theme.ts
|
|
48
|
+
var THEMES = ["default", "blue", "amber", "magenta", "mono"];
|
|
49
|
+
var REMAP = {
|
|
50
|
+
blue: { green: "blue" },
|
|
51
|
+
amber: { green: "yellow" },
|
|
52
|
+
magenta: { green: "magenta" }
|
|
53
|
+
};
|
|
54
|
+
var theme = "default";
|
|
55
|
+
function setTheme(name) {
|
|
56
|
+
theme = name;
|
|
57
|
+
}
|
|
58
|
+
function color(name) {
|
|
59
|
+
if (process.env.NO_COLOR) return void 0;
|
|
60
|
+
if (theme === "mono") return void 0;
|
|
61
|
+
return REMAP[theme]?.[name] ?? name;
|
|
62
|
+
}
|
|
63
|
+
var ACCENT = "green";
|
|
64
|
+
|
|
65
|
+
// src/config.ts
|
|
66
|
+
var ConfigError = class extends Error {
|
|
67
|
+
};
|
|
68
|
+
var DEFAULT_MAX_STEPS = null;
|
|
69
|
+
var DEFAULT_CONFIG = {
|
|
70
|
+
model: "bc-cloud/glm-5.3-flash",
|
|
71
|
+
permissionMode: "default",
|
|
72
|
+
reasoning: "auto",
|
|
73
|
+
vision: true,
|
|
74
|
+
networkPolicy: "allow",
|
|
75
|
+
keybinds: defaultKeybinds(),
|
|
76
|
+
verifyCommands: [],
|
|
77
|
+
repoMap: false,
|
|
78
|
+
theme: "default",
|
|
79
|
+
editor: "emacs",
|
|
80
|
+
sandbox: "off",
|
|
81
|
+
providers: Object.fromEntries(PRESETS.map((p) => [p.id, { baseURL: p.baseURL, apiKeyEnv: p.apiKeyEnv }])),
|
|
82
|
+
allow: []
|
|
83
|
+
};
|
|
84
|
+
function bccliHome(env = process.env) {
|
|
85
|
+
return env.BCCLI_HOME ?? join(homedir(), ".bccli");
|
|
86
|
+
}
|
|
87
|
+
function readJsonConfig(path) {
|
|
88
|
+
if (!existsSync(path)) return {};
|
|
89
|
+
try {
|
|
90
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
91
|
+
} catch (error) {
|
|
92
|
+
throw new ConfigError(t("Invalid config: {path} ({error})", { path, error: error.message }));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function resolvedMaxSteps(global) {
|
|
96
|
+
const value = global.maxSteps;
|
|
97
|
+
if (value === void 0) return DEFAULT_MAX_STEPS;
|
|
98
|
+
if (value === null || value === 0) return null;
|
|
99
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
100
|
+
throw new ConfigError(t("maxSteps must be a positive number, or 0/null to disable the limit."));
|
|
101
|
+
}
|
|
102
|
+
return Math.max(1, Math.floor(value));
|
|
103
|
+
}
|
|
104
|
+
function resolvedSessionRetentionDays(global) {
|
|
105
|
+
const value = global.sessionRetentionDays;
|
|
106
|
+
if (value === void 0 || value === null || value === 0) return null;
|
|
107
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
108
|
+
throw new ConfigError(t("sessionRetentionDays must be a positive number of days, or 0/null to keep sessions."));
|
|
109
|
+
}
|
|
110
|
+
return Math.max(1, Math.floor(value));
|
|
111
|
+
}
|
|
112
|
+
function resolvedNetworkPolicy(global) {
|
|
113
|
+
const value = global.networkPolicy ?? "allow";
|
|
114
|
+
if (value !== "allow" && value !== "offline") {
|
|
115
|
+
throw new ConfigError(t('networkPolicy must be "allow" or "offline".'));
|
|
116
|
+
}
|
|
117
|
+
return value;
|
|
118
|
+
}
|
|
119
|
+
function resolvedPlugins(global) {
|
|
120
|
+
const value = global.plugins;
|
|
121
|
+
if (value === void 0) return [];
|
|
122
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
|
|
123
|
+
throw new ConfigError(t("plugins must be an array of strings."));
|
|
124
|
+
}
|
|
125
|
+
return [...value];
|
|
126
|
+
}
|
|
127
|
+
function resolvedUpdateCheck(global) {
|
|
128
|
+
const value = global.updateCheck;
|
|
129
|
+
if (value === void 0) return void 0;
|
|
130
|
+
if (value !== "on" && value !== "off") throw new ConfigError(t('updateCheck must be "on" or "off".'));
|
|
131
|
+
return value;
|
|
132
|
+
}
|
|
133
|
+
function resolvedUsageCap(global) {
|
|
134
|
+
const cap = global.usageCap;
|
|
135
|
+
if (cap === void 0) return void 0;
|
|
136
|
+
if (typeof cap !== "object" || cap === null || Array.isArray(cap)) {
|
|
137
|
+
throw new ConfigError(t('usageCap must be an object, e.g. { "tokens": 2000000 }.'));
|
|
138
|
+
}
|
|
139
|
+
const { tokens, usd, prices } = cap;
|
|
140
|
+
const positive = (v) => typeof v === "number" && Number.isFinite(v) && v > 0;
|
|
141
|
+
const nonNegative = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0;
|
|
142
|
+
if (tokens !== void 0 && !positive(tokens)) throw new ConfigError(t("usageCap.tokens must be a number greater than 0."));
|
|
143
|
+
if (usd !== void 0 && !positive(usd)) throw new ConfigError(t("usageCap.usd must be a number greater than 0."));
|
|
144
|
+
if (usd !== void 0 && (prices === void 0 || !Object.keys(prices).length)) {
|
|
145
|
+
throw new ConfigError(t("usageCap.usd requires usageCap.prices (USD per 1M tokens)."));
|
|
146
|
+
}
|
|
147
|
+
if (prices !== void 0) {
|
|
148
|
+
for (const [key, price] of Object.entries(prices)) {
|
|
149
|
+
if (!price || !nonNegative(price.input) || !nonNegative(price.output)) {
|
|
150
|
+
throw new ConfigError(t('usageCap.prices["{key}"] must have numeric input and output (USD per 1M tokens).', { key }));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (tokens === void 0 && usd === void 0) throw new ConfigError(t("usageCap needs at least one of: tokens, usd."));
|
|
155
|
+
return { ...tokens !== void 0 ? { tokens } : {}, ...usd !== void 0 ? { usd } : {}, ...prices !== void 0 ? { prices } : {} };
|
|
156
|
+
}
|
|
157
|
+
function resolvedLsp(global) {
|
|
158
|
+
if (global.lsp === void 0) return void 0;
|
|
159
|
+
const lsp = global.lsp;
|
|
160
|
+
if (typeof global.lsp !== "object" || global.lsp === null || !Array.isArray(lsp.servers)) {
|
|
161
|
+
throw new ConfigError(t('lsp must be an object with a "servers" array.'));
|
|
162
|
+
}
|
|
163
|
+
for (const s of lsp.servers) {
|
|
164
|
+
const server = s;
|
|
165
|
+
if (typeof server?.command !== "string" || !server.command || !Array.isArray(server.extensions) || server.extensions.length === 0 || server.extensions.some((e) => typeof e !== "string" || !e.startsWith(".")) || server.args !== void 0 && (!Array.isArray(server.args) || server.args.some((a) => typeof a !== "string"))) {
|
|
166
|
+
throw new ConfigError(t('Each lsp server needs { "extensions": [".ts"], "command": "...", "args": ["..."] }.'));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return { servers: lsp.servers };
|
|
170
|
+
}
|
|
171
|
+
function resolvedSandbox(global) {
|
|
172
|
+
const value = global.sandbox ?? "off";
|
|
173
|
+
if (value !== "off" && value !== "on") throw new ConfigError(t('sandbox must be "off" or "on".'));
|
|
174
|
+
return value;
|
|
175
|
+
}
|
|
176
|
+
function resolvedEditor(global) {
|
|
177
|
+
const value = global.editor ?? "emacs";
|
|
178
|
+
if (value !== "emacs" && value !== "vim") throw new ConfigError(t('editor must be "emacs" or "vim".'));
|
|
179
|
+
return value;
|
|
180
|
+
}
|
|
181
|
+
function resolvedTheme(global) {
|
|
182
|
+
const value = global.theme ?? "default";
|
|
183
|
+
if (typeof value !== "string" || !THEMES.includes(value)) {
|
|
184
|
+
throw new ConfigError(t("theme must be one of: {themes}.", { themes: THEMES.join(", ") }));
|
|
185
|
+
}
|
|
186
|
+
return value;
|
|
187
|
+
}
|
|
188
|
+
function resolvedRepoMap(global) {
|
|
189
|
+
const value = global.repoMap ?? false;
|
|
190
|
+
if (typeof value !== "boolean") throw new ConfigError(t("repoMap must be true or false."));
|
|
191
|
+
return value;
|
|
192
|
+
}
|
|
193
|
+
function resolvedVerifyCommands(global) {
|
|
194
|
+
const value = global.verifyCommands;
|
|
195
|
+
if (value === void 0) return [];
|
|
196
|
+
if (!Array.isArray(value) || value.some((c) => typeof c !== "string" || !c.trim())) {
|
|
197
|
+
throw new ConfigError(t("verifyCommands must be an array of non-empty strings."));
|
|
198
|
+
}
|
|
199
|
+
return value.map((c) => c.trim());
|
|
200
|
+
}
|
|
201
|
+
function resolvedKeybinds(global) {
|
|
202
|
+
const raw = global.keybinds ?? {};
|
|
203
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
204
|
+
throw new ConfigError(t("keybinds must be an object mapping action to binding."));
|
|
205
|
+
}
|
|
206
|
+
const out = {};
|
|
207
|
+
for (const action of KEYBIND_ACTIONS) {
|
|
208
|
+
const spec = raw[action];
|
|
209
|
+
const value = spec === void 0 ? DEFAULT_KEYBINDS[action] : spec;
|
|
210
|
+
if (typeof value !== "string") {
|
|
211
|
+
throw new ConfigError(t('Keybind for "{action}" must be a string like "ctrl+t".', { action }));
|
|
212
|
+
}
|
|
213
|
+
const parsed = parseKeybind(value);
|
|
214
|
+
if (!parsed) {
|
|
215
|
+
throw new ConfigError(t('Keybind "{spec}" for "{action}" is invalid: use ctrl or alt plus one letter, e.g. "ctrl+t".', { spec: value, action }));
|
|
216
|
+
}
|
|
217
|
+
out[action] = parsed;
|
|
218
|
+
}
|
|
219
|
+
for (const key of Object.keys(raw)) {
|
|
220
|
+
if (!KEYBIND_ACTIONS.includes(key)) {
|
|
221
|
+
throw new ConfigError(t('Unknown keybind action "{action}". Known actions: {list}.', { action: key, list: KEYBIND_ACTIONS.join(", ") }));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return out;
|
|
225
|
+
}
|
|
226
|
+
function untrustedProviders(project, known) {
|
|
227
|
+
const out = {};
|
|
228
|
+
for (const [id, provider] of Object.entries(project.providers ?? {})) {
|
|
229
|
+
if (known[id] || typeof provider?.baseURL !== "string") continue;
|
|
230
|
+
out[id] = { baseURL: provider.baseURL };
|
|
231
|
+
}
|
|
232
|
+
return out;
|
|
233
|
+
}
|
|
234
|
+
function loadConfig(cwd, env = process.env) {
|
|
235
|
+
const global = readJsonConfig(join(bccliHome(env), "config.json"));
|
|
236
|
+
const project = readJsonConfig(join(cwd, ".bccli", "config.json"));
|
|
237
|
+
const known = { ...DEFAULT_CONFIG.providers, ...global.providers };
|
|
238
|
+
const fromProject = untrustedProviders(project, known);
|
|
239
|
+
return {
|
|
240
|
+
model: project.model ?? global.model ?? DEFAULT_CONFIG.model,
|
|
241
|
+
permissionMode: global.permissionMode ?? DEFAULT_CONFIG.permissionMode,
|
|
242
|
+
reasoning: global.reasoning ?? DEFAULT_CONFIG.reasoning,
|
|
243
|
+
thinkingVisible: global.thinkingVisible === true,
|
|
244
|
+
thinkingClickCollapse: global.thinkingClickCollapse === true,
|
|
245
|
+
sessionRetentionDays: resolvedSessionRetentionDays(global),
|
|
246
|
+
maxSteps: resolvedMaxSteps(global),
|
|
247
|
+
hooks: global.hooks,
|
|
248
|
+
vision: global.vision ?? DEFAULT_CONFIG.vision,
|
|
249
|
+
networkPolicy: resolvedNetworkPolicy(global),
|
|
250
|
+
plugins: resolvedPlugins(global),
|
|
251
|
+
keybinds: resolvedKeybinds(global),
|
|
252
|
+
verifyCommands: resolvedVerifyCommands(global),
|
|
253
|
+
repoMap: resolvedRepoMap(global),
|
|
254
|
+
theme: resolvedTheme(global),
|
|
255
|
+
editor: resolvedEditor(global),
|
|
256
|
+
sandbox: resolvedSandbox(global),
|
|
257
|
+
lsp: resolvedLsp(global),
|
|
258
|
+
usageCap: resolvedUsageCap(global),
|
|
259
|
+
updateCheck: resolvedUpdateCheck(global),
|
|
260
|
+
providers: { ...known, ...fromProject },
|
|
261
|
+
projectProviders: Object.keys(fromProject),
|
|
262
|
+
allow: [...global.allow ?? []]
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function globalConfigPath(env = process.env) {
|
|
266
|
+
return join(bccliHome(env), "config.json");
|
|
267
|
+
}
|
|
268
|
+
function readGlobalPlugins(env = process.env) {
|
|
269
|
+
const raw = readJsonConfig(globalConfigPath(env));
|
|
270
|
+
const value = raw.plugins;
|
|
271
|
+
if (value === void 0) return [];
|
|
272
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
|
|
273
|
+
throw new ConfigError(t("plugins must be an array of strings."));
|
|
274
|
+
}
|
|
275
|
+
return value;
|
|
276
|
+
}
|
|
277
|
+
function writeFileAtomic(path, data) {
|
|
278
|
+
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
279
|
+
try {
|
|
280
|
+
writeFileSync(tmp, data);
|
|
281
|
+
renameSync(tmp, path);
|
|
282
|
+
} catch (error) {
|
|
283
|
+
try {
|
|
284
|
+
rmSync(tmp, { force: true });
|
|
285
|
+
} catch {
|
|
286
|
+
}
|
|
287
|
+
throw error;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
function updateGlobalConfig(env, mutate) {
|
|
291
|
+
const path = globalConfigPath(env);
|
|
292
|
+
let cfg = {};
|
|
293
|
+
if (existsSync(path)) {
|
|
294
|
+
cfg = JSON.parse(readFileSync(path, "utf8"));
|
|
295
|
+
}
|
|
296
|
+
mutate(cfg);
|
|
297
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
298
|
+
writeFileAtomic(path, `${JSON.stringify(cfg, null, 2)}
|
|
299
|
+
`);
|
|
300
|
+
}
|
|
301
|
+
function readCredentials(env = process.env) {
|
|
302
|
+
const path = join(bccliHome(env), "credentials");
|
|
303
|
+
if (!existsSync(path)) return {};
|
|
304
|
+
try {
|
|
305
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
306
|
+
} catch {
|
|
307
|
+
return {};
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
function saveCredential(providerId, key, env = process.env) {
|
|
311
|
+
const home = bccliHome(env);
|
|
312
|
+
mkdirSync(home, { recursive: true });
|
|
313
|
+
const path = join(home, "credentials");
|
|
314
|
+
writeFileSync(path, JSON.stringify({ ...readCredentials(env), [providerId]: key }, null, 2), { mode: 384 });
|
|
315
|
+
chmodSync(path, 384);
|
|
316
|
+
}
|
|
317
|
+
function resolveModel(config, modelRef, env = process.env) {
|
|
318
|
+
const slash = modelRef.indexOf("/");
|
|
319
|
+
if (slash <= 0 || slash === modelRef.length - 1) {
|
|
320
|
+
throw new ConfigError(t("Model must be in provider/model format, e.g. bc-cloud/glm-5.3-flash (got: {ref})", { ref: modelRef }));
|
|
321
|
+
}
|
|
322
|
+
const providerId = modelRef.slice(0, slash);
|
|
323
|
+
const provider = config.providers[providerId];
|
|
324
|
+
if (!provider) {
|
|
325
|
+
throw new ConfigError(t('Provider "{id}" is not in the config. Available: {list}', { id: providerId, list: Object.keys(config.providers).join(", ") }));
|
|
326
|
+
}
|
|
327
|
+
const apiKey = readCredentials(env)[providerId] || (provider.apiKeyEnv ? env[provider.apiKeyEnv] : void 0) || void 0;
|
|
328
|
+
if (provider.apiKeyEnv && !apiKey) {
|
|
329
|
+
throw new ConfigError(t("No API key for {id} yet. Run `bccli login` or set the env var {env}.", { id: providerId, env: provider.apiKeyEnv }));
|
|
330
|
+
}
|
|
331
|
+
return { providerId, model: modelRef.slice(slash + 1), baseURL: provider.baseURL.replace(/\/+$/, ""), apiKey };
|
|
332
|
+
}
|
|
333
|
+
function removeCredential(providerId, env = process.env) {
|
|
334
|
+
const creds = readCredentials(env);
|
|
335
|
+
if (!(providerId in creds)) return false;
|
|
336
|
+
delete creds[providerId];
|
|
337
|
+
const path = join(bccliHome(env), "credentials");
|
|
338
|
+
writeFileSync(path, JSON.stringify(creds, null, 2), { mode: 384 });
|
|
339
|
+
chmodSync(path, 384);
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export {
|
|
344
|
+
PRESETS,
|
|
345
|
+
matchKeybind,
|
|
346
|
+
setTheme,
|
|
347
|
+
color,
|
|
348
|
+
ACCENT,
|
|
349
|
+
ConfigError,
|
|
350
|
+
DEFAULT_MAX_STEPS,
|
|
351
|
+
bccliHome,
|
|
352
|
+
readJsonConfig,
|
|
353
|
+
loadConfig,
|
|
354
|
+
globalConfigPath,
|
|
355
|
+
readGlobalPlugins,
|
|
356
|
+
updateGlobalConfig,
|
|
357
|
+
readCredentials,
|
|
358
|
+
saveCredential,
|
|
359
|
+
resolveModel,
|
|
360
|
+
removeCredential
|
|
361
|
+
};
|