@botconnector/bccli 0.4.0-beta.5 → 0.4.0-beta.51

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.
Files changed (58) hide show
  1. package/README.md +97 -2
  2. package/dist/acp-NWP4FFTG.js +182 -0
  3. package/dist/args-2R45YZ42.js +13 -0
  4. package/dist/{chunk-U7DLMVPZ.js → chunk-2QYHSESB.js} +2 -1
  5. package/dist/{chunk-UESQNU4A.js → chunk-4WVIPZFV.js} +14 -9
  6. package/dist/chunk-7F3IWBPQ.js +191 -0
  7. package/dist/chunk-DP7EBW53.js +341 -0
  8. package/dist/{chunk-SJH3YLW4.js → chunk-F6ACN47V.js} +60 -6
  9. package/dist/chunk-FTFVW7XD.js +235 -0
  10. package/dist/{chunk-ELML4KVQ.js → chunk-GAFJEQTJ.js} +32 -2
  11. package/dist/{chunk-NGLKANMD.js → chunk-H73FO5YK.js} +24 -2
  12. package/dist/chunk-KD2LMIS4.js +145 -0
  13. package/dist/chunk-KIZC5XV4.js +2141 -0
  14. package/dist/chunk-N7KP6RNP.js +76 -0
  15. package/dist/chunk-NDEJUL44.js +361 -0
  16. package/dist/chunk-PSO7R5AD.js +10 -0
  17. package/dist/chunk-QXOV4DUZ.js +56 -0
  18. package/dist/chunk-QZKLZELZ.js +64 -0
  19. package/dist/chunk-R4BJBTWC.js +940 -0
  20. package/dist/chunk-SNKVS26H.js +63 -0
  21. package/dist/chunk-TQWCM5RO.js +231 -0
  22. package/dist/chunk-VU5HNQHX.js +8 -0
  23. package/dist/chunk-XLC6T4SX.js +500 -0
  24. package/dist/chunk-YIYX3FFS.js +733 -0
  25. package/dist/cli.js +157 -1281
  26. package/dist/completion-OV3MW2N4.js +125 -0
  27. package/dist/{config-NPSYFAFD.js → config-AZZFOXKV.js} +5 -2
  28. package/dist/config-H5A7XHHK.js +30 -0
  29. package/dist/ghBotCli-7WDYF4L7.js +677 -0
  30. package/dist/integrationCli-ZMTSILYF.js +685 -0
  31. package/dist/login-W4Y6WHLV.js +16 -0
  32. package/dist/{mcpCli-Q2H63ISP.js → mcpCli-5QISTOFK.js} +42 -9
  33. package/dist/modelsCli-FZMBD4OE.js +36 -0
  34. package/dist/npmRunner-LU43GM3W.js +46 -0
  35. package/dist/oauthLogin-VESMXG4M.js +210 -0
  36. package/dist/pluginsCli-GEHR7FQR.js +284 -0
  37. package/dist/prCli-JXA7DJW4.js +72 -0
  38. package/dist/providerCli-EUZGKP5F.js +15 -0
  39. package/dist/sdk.js +5486 -0
  40. package/dist/serve-6KX4MXHC.js +178 -0
  41. package/dist/serve-YSUJD57D.js +515 -0
  42. package/dist/session-F6MG3UCU.js +12 -0
  43. package/dist/setup-LLWREJQH.js +24 -0
  44. package/dist/statsCli-H4HEY5ID.js +353 -0
  45. package/dist/transferCli-2XUKBG2L.js +71 -0
  46. package/dist/ui-6HJVYI46.js +2370 -0
  47. package/dist/update-O4WCLKOO.js +109 -0
  48. package/package.json +24 -3
  49. package/dist/args-FCLPQ7TO.js +0 -11
  50. package/dist/chunk-5O4HD7IU.js +0 -253
  51. package/dist/chunk-C5AFWX7H.js +0 -328
  52. package/dist/chunk-GW36UMHZ.js +0 -52
  53. package/dist/chunk-RZUJYAQV.js +0 -112
  54. package/dist/chunk-UN7QXPCB.js +0 -82
  55. package/dist/login-F4G5QXOU.js +0 -9
  56. package/dist/providerCli-MXV6PD6F.js +0 -11
  57. package/dist/session-7UB77CEG.js +0 -9
  58. package/dist/ui-RYTSKEIA.js +0 -1344
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ listAllModels
4
+ } from "./chunk-QXOV4DUZ.js";
5
+ import {
6
+ hasKey,
7
+ providerName,
8
+ removeGlobalProvider,
9
+ writeGlobalConfig
10
+ } from "./chunk-2QYHSESB.js";
11
+ import {
12
+ PRESETS,
13
+ loadConfig,
14
+ saveCredential
15
+ } from "./chunk-NDEJUL44.js";
16
+ import {
17
+ t
18
+ } from "./chunk-XLC6T4SX.js";
19
+
20
+ // src/providerCli.ts
21
+ async function addProviderKey(providerId, key, deps) {
22
+ const config = loadConfig(deps.cwd, deps.env);
23
+ const [group] = await listAllModels(config, deps.env, { only: providerId, fetch: deps.fetch, keyOverride: key || void 0 });
24
+ if (!group || group.error) return { ok: false, error: t("The API key was rejected or {provider} could not be reached.", { provider: providerName(config, providerId) }) };
25
+ if (key) saveCredential(providerId, key, deps.env);
26
+ return { ok: true, models: group.models.length };
27
+ }
28
+ async function runProviderCommand(args, deps) {
29
+ const [action = "list", id] = args.subArgs;
30
+ const config = loadConfig(deps.cwd, deps.env);
31
+ if (action === "list") {
32
+ for (const pid of Object.keys(config.providers)) {
33
+ const mark = hasKey(config, pid, deps.env) ? "\u2713" : "\u25CB";
34
+ deps.out(`${mark} ${pid.padEnd(14)} ${providerName(config, pid).padEnd(20)} ${config.providers[pid].baseURL}`);
35
+ }
36
+ return 0;
37
+ }
38
+ if (!id) {
39
+ deps.err(t("Usage: bccli provider {action} <id>", { action }));
40
+ return 1;
41
+ }
42
+ if (action === "remove") {
43
+ removeGlobalProvider(id, deps.env);
44
+ deps.out(t("{id} removed.", { id }));
45
+ return 0;
46
+ }
47
+ if (action !== "add") {
48
+ deps.err(t("Unknown action: {action}. Use list, add, or remove.", { action }));
49
+ return 1;
50
+ }
51
+ const preset = PRESETS.find((p) => p.id === id);
52
+ if (!preset && !config.providers[id]) {
53
+ if (!args.url) {
54
+ deps.err(t("{id} is not a preset. For a custom provider add --url <OpenAI-compatible base URL>.", { id }));
55
+ return 1;
56
+ }
57
+ writeGlobalConfig(
58
+ { providers: { [id]: { baseURL: args.url, ...args.name ? { name: args.name } : {}, ...args.keyEnv ? { apiKeyEnv: args.keyEnv } : {} } } },
59
+ deps.env
60
+ );
61
+ }
62
+ const needsKey = !!loadConfig(deps.cwd, deps.env).providers[id].apiKeyEnv || !preset;
63
+ const key = needsKey ? await deps.readSecret(t("API key for {id} (leave empty if not needed): ", { id })) : "";
64
+ const result = await addProviderKey(id, key, deps);
65
+ if (!result.ok) {
66
+ deps.err(result.error);
67
+ return 1;
68
+ }
69
+ deps.out(t("{id} is ready. {n} models available.", { id, n: result.models }));
70
+ return 0;
71
+ }
72
+
73
+ export {
74
+ addProviderKey,
75
+ runProviderCommand
76
+ };
@@ -0,0 +1,361 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ t
4
+ } from "./chunk-XLC6T4SX.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 = (provider.apiKeyEnv ? env[provider.apiKeyEnv] : void 0) || readCredentials(env)[providerId] || 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
+ };
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/tools/types.ts
4
+ function defineTool(tool) {
5
+ return tool;
6
+ }
7
+
8
+ export {
9
+ defineTool
10
+ };
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ hasKey,
4
+ providerName
5
+ } from "./chunk-2QYHSESB.js";
6
+ import {
7
+ createProvider
8
+ } from "./chunk-R4BJBTWC.js";
9
+ import {
10
+ PRESETS,
11
+ readCredentials
12
+ } from "./chunk-NDEJUL44.js";
13
+ import {
14
+ t
15
+ } from "./chunk-XLC6T4SX.js";
16
+
17
+ // src/models.ts
18
+ function order(ids) {
19
+ const presetIds = PRESETS.map((p) => p.id);
20
+ const presets = presetIds.filter((id) => ids.includes(id));
21
+ const custom = ids.filter((id) => !presetIds.includes(id)).sort();
22
+ return [...presets, ...custom];
23
+ }
24
+ async function listAllModels(config, env = process.env, opts = {}) {
25
+ const timeoutMs = opts.timeoutMs ?? 5e3;
26
+ const baseFetch = opts.fetch ?? fetch;
27
+ const untrusted = new Set(config.projectProviders ?? []);
28
+ const ids = order(Object.keys(config.providers)).filter((id) => opts.only ? id === opts.only : !untrusted.has(id) && hasKey(config, id, env));
29
+ const results = await Promise.all(
30
+ ids.map(async (id) => {
31
+ const provider = config.providers[id];
32
+ const apiKey = (opts.keyOverride && id === opts.only ? opts.keyOverride : void 0) || (provider.apiKeyEnv ? env[provider.apiKeyEnv] : void 0) || readCredentials(env)[id] || void 0;
33
+ const local = !provider.apiKeyEnv;
34
+ const timedFetch = ((input, init) => baseFetch(input, { ...init, signal: AbortSignal.timeout(timeoutMs) }));
35
+ const list = createProvider({ baseURL: provider.baseURL.replace(/\/+$/, ""), apiKey, model: "", fetch: timedFetch }).listModels();
36
+ let timer;
37
+ const timeout = new Promise((_, reject) => {
38
+ timer = setTimeout(() => reject(new Error("timeout")), timeoutMs);
39
+ });
40
+ try {
41
+ const models = await Promise.race([list, timeout]);
42
+ return { providerId: id, providerName: providerName(config, id), models };
43
+ } catch {
44
+ if (local && !opts.only) return void 0;
45
+ return { providerId: id, providerName: providerName(config, id), models: [], error: t("could not be reached") };
46
+ } finally {
47
+ clearTimeout(timer);
48
+ }
49
+ })
50
+ );
51
+ return results.filter((g) => !!g);
52
+ }
53
+
54
+ export {
55
+ listAllModels
56
+ };
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/tokenBudget.ts
4
+ var estimateTokens = (text) => Math.ceil(text.length / 4);
5
+ var estimateMessages = (messages) => estimateTokens(JSON.stringify(messages));
6
+ function trimToFit(messages, maxTokens) {
7
+ let out = messages;
8
+ while (estimateMessages(out) > maxTokens) {
9
+ const boundary = out.findIndex((m, i) => i > 0 && m.role === "user");
10
+ if (boundary === -1) return out;
11
+ out = out.slice(boundary);
12
+ }
13
+ return out;
14
+ }
15
+ function splitKeepRecent(messages, keepTokens) {
16
+ if (messages.length === 0) return [[], []];
17
+ let start = messages.length;
18
+ for (let i = messages.length - 1; i >= 0; i--) {
19
+ if (i > 0 && messages[i].role !== "user") continue;
20
+ if (estimateMessages(messages.slice(i)) <= keepTokens) start = i;
21
+ else break;
22
+ }
23
+ if (start === messages.length) {
24
+ for (let i = messages.length - 1; i >= 0; i--) {
25
+ if (messages[i].role === "user") {
26
+ start = i;
27
+ break;
28
+ }
29
+ }
30
+ }
31
+ return [messages.slice(0, start), messages.slice(start)];
32
+ }
33
+ var TOOL_STUB = "[Old tool result content cleared]";
34
+ var TOOL_PRUNE_KEEP_TOKENS = 4e4;
35
+ var TOOL_PRUNE_MIN_TOKENS = 2e4;
36
+ var isToolStub = (content) => content.startsWith(TOOL_STUB);
37
+ function pruneOldToolOutputs(messages) {
38
+ let kept = 0;
39
+ let oldTokens = 0;
40
+ const old = [];
41
+ for (let i = messages.length - 1; i >= 0; i--) {
42
+ const m = messages[i];
43
+ if (m.role !== "tool" || isToolStub(m.content)) continue;
44
+ kept += estimateTokens(m.content);
45
+ if (kept > TOOL_PRUNE_KEEP_TOKENS) {
46
+ old.push(i);
47
+ oldTokens += estimateTokens(m.content);
48
+ }
49
+ }
50
+ if (oldTokens <= TOOL_PRUNE_MIN_TOKENS) return false;
51
+ for (const i of old) {
52
+ const m = messages[i];
53
+ messages[i] = { ...m, content: TOOL_STUB };
54
+ }
55
+ return true;
56
+ }
57
+
58
+ export {
59
+ estimateTokens,
60
+ estimateMessages,
61
+ trimToFit,
62
+ splitKeepRecent,
63
+ pruneOldToolOutputs
64
+ };