@botconnector/bccli 0.4.16 → 0.5.0-rc.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.
Files changed (185) hide show
  1. package/LICENSE +203 -21
  2. package/THIRD_PARTY_NOTICES.md +9 -0
  3. package/branding.mjs +347 -0
  4. package/cli.mjs +120 -0
  5. package/core.mjs +260 -0
  6. package/credentials.mjs +52 -0
  7. package/gateway.mjs +2 -0
  8. package/model-command.mjs +61 -0
  9. package/package.json +35 -59
  10. package/provider-cli.mjs +77 -0
  11. package/providers.mjs +166 -0
  12. package/proxy.mjs +242 -0
  13. package/startup-menu.mjs +120 -0
  14. package/README.en.md +0 -253
  15. package/README.md +0 -250
  16. package/dist/abort.d.ts +0 -5
  17. package/dist/acp-JZW3XHE5.js +0 -221
  18. package/dist/acp.d.ts +0 -44
  19. package/dist/agent.d.ts +0 -147
  20. package/dist/agents.d.ts +0 -4
  21. package/dist/args-XVO7TB4C.js +0 -13
  22. package/dist/args.d.ts +0 -59
  23. package/dist/budget.d.ts +0 -26
  24. package/dist/checkpoints.d.ts +0 -35
  25. package/dist/chunk-3CYQYHPK.js +0 -202
  26. package/dist/chunk-6JK4IBGK.js +0 -1075
  27. package/dist/chunk-6SDRWEPN.js +0 -50
  28. package/dist/chunk-7GM6NN5E.js +0 -341
  29. package/dist/chunk-7XD47KN5.js +0 -71
  30. package/dist/chunk-INMSTUB2.js +0 -56
  31. package/dist/chunk-ISOKH5WB.js +0 -256
  32. package/dist/chunk-JUC3ZOCV.js +0 -50
  33. package/dist/chunk-KDVL2NP6.js +0 -861
  34. package/dist/chunk-KEKDRROL.js +0 -216
  35. package/dist/chunk-KJIXA6MP.js +0 -159
  36. package/dist/chunk-OK2USAXX.js +0 -88
  37. package/dist/chunk-PSO7R5AD.js +0 -10
  38. package/dist/chunk-Q42ZGVAQ.js +0 -91
  39. package/dist/chunk-R754XIBJ.js +0 -523
  40. package/dist/chunk-S3QEZDKB.js +0 -270
  41. package/dist/chunk-SWNRA3EW.js +0 -78
  42. package/dist/chunk-TLCL6DC7.js +0 -378
  43. package/dist/chunk-V5D6PJQN.js +0 -8
  44. package/dist/chunk-YYTFJUO2.js +0 -148
  45. package/dist/chunk-ZJRZTMJF.js +0 -2669
  46. package/dist/cli.d.ts +0 -1
  47. package/dist/cli.js +0 -233
  48. package/dist/clipboardImage.d.ts +0 -17
  49. package/dist/commands.d.ts +0 -16
  50. package/dist/completion-Q7VHOPPV.js +0 -125
  51. package/dist/completion.d.ts +0 -14
  52. package/dist/config-T7MKGJVG.js +0 -34
  53. package/dist/config-YYEAEN2C.js +0 -25
  54. package/dist/config.d.ts +0 -99
  55. package/dist/context.d.ts +0 -13
  56. package/dist/contextWindow.d.ts +0 -3
  57. package/dist/diff.d.ts +0 -7
  58. package/dist/exit.d.ts +0 -9
  59. package/dist/extensions.d.ts +0 -34
  60. package/dist/ghBotCli-7VUGI63S.js +0 -680
  61. package/dist/ghBotCli.d.ts +0 -9
  62. package/dist/ghBotDispatch.d.ts +0 -68
  63. package/dist/ghBotRunner.d.ts +0 -82
  64. package/dist/ghBotServe.d.ts +0 -21
  65. package/dist/ghBotWebhook.d.ts +0 -45
  66. package/dist/hooks.d.ts +0 -27
  67. package/dist/i18n/id.d.ts +0 -1
  68. package/dist/i18n.d.ts +0 -16
  69. package/dist/integrationCli-FBRSTF5F.js +0 -876
  70. package/dist/integrationCli.d.ts +0 -9
  71. package/dist/keybinds.d.ts +0 -22
  72. package/dist/login-BSMUGW7L.js +0 -18
  73. package/dist/login.d.ts +0 -21
  74. package/dist/lsp.d.ts +0 -73
  75. package/dist/mcp/catalog.d.ts +0 -14
  76. package/dist/mcp/config.d.ts +0 -36
  77. package/dist/mcp/manager.d.ts +0 -52
  78. package/dist/mcp/oauth.d.ts +0 -82
  79. package/dist/mcpCli-ZT7CRYMP.js +0 -113
  80. package/dist/mcpCli.d.ts +0 -3
  81. package/dist/mcpServer/serve.d.ts +0 -8
  82. package/dist/mcpServer/server.d.ts +0 -19
  83. package/dist/models.d.ts +0 -13
  84. package/dist/modelsCli-2ZRW4CC4.js +0 -36
  85. package/dist/modelsCli.d.ts +0 -15
  86. package/dist/npmRunner-LU43GM3W.js +0 -46
  87. package/dist/npmRunner.d.ts +0 -21
  88. package/dist/oauthLogin-THSM6FE7.js +0 -210
  89. package/dist/oauthLogin.d.ts +0 -47
  90. package/dist/permissions.d.ts +0 -30
  91. package/dist/pluginSpec.d.ts +0 -25
  92. package/dist/plugins.d.ts +0 -45
  93. package/dist/pluginsCli-EKQI4CJW.js +0 -288
  94. package/dist/pluginsCli.d.ts +0 -16
  95. package/dist/prCli-FMQTOBIV.js +0 -94
  96. package/dist/prCli.d.ts +0 -46
  97. package/dist/presets.d.ts +0 -8
  98. package/dist/print.d.ts +0 -17
  99. package/dist/provider.d.ts +0 -145
  100. package/dist/providerCli-3TFEKV4C.js +0 -15
  101. package/dist/providerCli.d.ts +0 -17
  102. package/dist/providers.d.ts +0 -6
  103. package/dist/reasoning.d.ts +0 -12
  104. package/dist/repomap.d.ts +0 -10
  105. package/dist/sdk.d.ts +0 -61
  106. package/dist/sdk.js +0 -6494
  107. package/dist/serve-3BODPEPR.js +0 -532
  108. package/dist/serve-V6YVZTNR.js +0 -178
  109. package/dist/serve.d.ts +0 -37
  110. package/dist/serveHeartbeat.d.ts +0 -14
  111. package/dist/serveProtocol.d.ts +0 -47
  112. package/dist/session-LX4UNPCD.js +0 -12
  113. package/dist/session.d.ts +0 -33
  114. package/dist/setup-MFG3BWEU.js +0 -24
  115. package/dist/setup.d.ts +0 -79
  116. package/dist/slash/copy.d.ts +0 -1
  117. package/dist/slash/diff.d.ts +0 -1
  118. package/dist/slash/doctor.d.ts +0 -18
  119. package/dist/slash/export.d.ts +0 -2
  120. package/dist/slash/info.d.ts +0 -46
  121. package/dist/slash/memory.d.ts +0 -10
  122. package/dist/statsCli-7MIQHLVW.js +0 -353
  123. package/dist/statsCli.d.ts +0 -62
  124. package/dist/textToolCalls.d.ts +0 -6
  125. package/dist/thinking.d.ts +0 -17
  126. package/dist/tokenBudget.d.ts +0 -22
  127. package/dist/tools/bash.d.ts +0 -54
  128. package/dist/tools/diagnostics.d.ts +0 -13
  129. package/dist/tools/edit.d.ts +0 -12
  130. package/dist/tools/fetch.d.ts +0 -41
  131. package/dist/tools/glob.d.ts +0 -5
  132. package/dist/tools/globSafety.d.ts +0 -7
  133. package/dist/tools/grep.d.ts +0 -25
  134. package/dist/tools/ignore.d.ts +0 -2
  135. package/dist/tools/index.d.ts +0 -12
  136. package/dist/tools/memory.d.ts +0 -13
  137. package/dist/tools/paths.d.ts +0 -4
  138. package/dist/tools/plan.d.ts +0 -10
  139. package/dist/tools/read.d.ts +0 -16
  140. package/dist/tools/repomap.d.ts +0 -2
  141. package/dist/tools/sandbox.d.ts +0 -15
  142. package/dist/tools/shell.d.ts +0 -18
  143. package/dist/tools/skill.d.ts +0 -3
  144. package/dist/tools/task.d.ts +0 -28
  145. package/dist/tools/todo.d.ts +0 -13
  146. package/dist/tools/types.d.ts +0 -52
  147. package/dist/tools/websearch.d.ts +0 -9
  148. package/dist/tools/write.d.ts +0 -5
  149. package/dist/transferCli-SRSYQLSJ.js +0 -73
  150. package/dist/transferCli.d.ts +0 -23
  151. package/dist/ui/App.d.ts +0 -7
  152. package/dist/ui/DiffView.d.ts +0 -3
  153. package/dist/ui/EditableText.d.ts +0 -7
  154. package/dist/ui/LinePrompt.d.ts +0 -6
  155. package/dist/ui/ListPicker.d.ts +0 -11
  156. package/dist/ui/Markdown.d.ts +0 -8
  157. package/dist/ui/McpMenu.d.ts +0 -11
  158. package/dist/ui/ModelPicker.d.ts +0 -6
  159. package/dist/ui/PermissionPrompt.d.ts +0 -5
  160. package/dist/ui/PlanApproval.d.ts +0 -5
  161. package/dist/ui/PromptInput.d.ts +0 -20
  162. package/dist/ui/ProviderMenu.d.ts +0 -10
  163. package/dist/ui/Spinner.d.ts +0 -4
  164. package/dist/ui/StatusBar.d.ts +0 -9
  165. package/dist/ui/TodoList.d.ts +0 -4
  166. package/dist/ui/ToolBlock.d.ts +0 -14
  167. package/dist/ui/complete.d.ts +0 -8
  168. package/dist/ui/index.d.ts +0 -7
  169. package/dist/ui/lineEdit.d.ts +0 -42
  170. package/dist/ui/liveCap.d.ts +0 -9
  171. package/dist/ui/replay.d.ts +0 -4
  172. package/dist/ui/stdinFilter.d.ts +0 -18
  173. package/dist/ui/table.d.ts +0 -20
  174. package/dist/ui/theme.d.ts +0 -5
  175. package/dist/ui/thinkingMouse.d.ts +0 -38
  176. package/dist/ui/transcript.d.ts +0 -55
  177. package/dist/ui/tty.d.ts +0 -11
  178. package/dist/ui/vim.d.ts +0 -26
  179. package/dist/ui-LZ4JGF2V.js +0 -2913
  180. package/dist/update-P7MU4ZM7.js +0 -145
  181. package/dist/update.d.ts +0 -30
  182. package/dist/verify.d.ts +0 -16
  183. package/dist/version.d.ts +0 -1
  184. package/dist/webBudget.d.ts +0 -25
  185. package/dist/worktree.d.ts +0 -12
@@ -1,270 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- runCommand,
4
- runHooks
5
- } from "./chunk-KDVL2NP6.js";
6
- import {
7
- ConfigError
8
- } from "./chunk-TLCL6DC7.js";
9
- import {
10
- t
11
- } from "./chunk-R754XIBJ.js";
12
-
13
- // src/verify.ts
14
- var MAX_VERIFY_ROUNDS = 2;
15
- async function runVerify(commands, opts) {
16
- const run = opts.run ?? runCommand;
17
- const failures = [];
18
- for (const cmd of commands) {
19
- const r = await run(cmd, { cwd: opts.cwd, timeoutMs: opts.timeoutMs ?? 3e5, signal: opts.signal, env: opts.env });
20
- const exitCode = r.exitCode ?? -1;
21
- if (exitCode !== 0) failures.push({ cmd, exitCode, output: r.output });
22
- }
23
- return failures;
24
- }
25
- function verifyFollowup(failures, maxBytes = 8e3) {
26
- const blocks = failures.map((f) => {
27
- const output = f.output.length > maxBytes ? `${f.output.slice(0, maxBytes)}
28
- [truncated]` : f.output;
29
- return `$ ${f.cmd}
30
- [exit code ${f.exitCode}]
31
- ${output}`;
32
- });
33
- return t("Automatic verification after your edits failed. Fix the problems so these commands pass, then stop.\n\n{blocks}", {
34
- blocks: blocks.join("\n\n")
35
- });
36
- }
37
-
38
- // src/print.ts
39
- function toolLabel(tool) {
40
- if (tool === "web_search") return "Search";
41
- return tool.charAt(0).toUpperCase() + tool.slice(1);
42
- }
43
- function resolvePrintPrompt(prompt, stdinText) {
44
- const cleanStdin = stdinText?.trim();
45
- const cleanPrompt = prompt?.trim();
46
- if (cleanStdin && cleanPrompt) return `${cleanStdin}
47
-
48
- ${cleanPrompt}`;
49
- return cleanPrompt || cleanStdin || void 0;
50
- }
51
- async function runPrint(rt, prompt, io = { out: process.stdout, err: process.stderr }, opts = {}) {
52
- const format = opts.outputFormat ?? "text";
53
- const emit = (line) => io.out.write(`${JSON.stringify(line)}
54
- `);
55
- const startedMs = Date.now();
56
- let result = "";
57
- const toolNames = /* @__PURE__ */ new Map();
58
- rt.interaction.approvePlan = async () => {
59
- if (opts.allowAll) return "allowAll";
60
- io.err.write(`${t("Plan needs approval; run with --allow-all.")}
61
- `);
62
- return "no";
63
- };
64
- let failed = false;
65
- let wroteText = false;
66
- let edited = false;
67
- rt.agent.onEvent = (event) => {
68
- switch (event.type) {
69
- case "text":
70
- if (format === "text") {
71
- io.out.write(event.delta);
72
- wroteText = true;
73
- } else {
74
- result += event.delta;
75
- if (format === "stream-json") emit({ type: "text_delta", delta: event.delta });
76
- }
77
- break;
78
- case "thinking":
79
- if (format === "stream-json") emit({ type: "thinking_delta", delta: event.delta });
80
- break;
81
- case "textReplace":
82
- result = event.text;
83
- break;
84
- case "toolStart":
85
- toolNames.set(event.id, event.tool);
86
- io.err.write(`\u23BF ${toolLabel(event.tool)} ${event.target}
87
- `);
88
- if (format === "stream-json") emit({ type: "tool_use", tool: event.tool, target: event.target });
89
- break;
90
- case "toolEnd":
91
- if (event.isError) io.err.write(` \u2717 ${event.output.split("\n")[0]}
92
- `);
93
- if (event.isError && opts.failOnToolError) failed = true;
94
- if ((event.tool === "edit" || event.tool === "write") && !event.isError) edited = true;
95
- if (format === "stream-json") emit({ type: "tool_result", tool: toolNames.get(event.id), isError: !!event.isError, output: event.output });
96
- break;
97
- case "stepLimit":
98
- io.err.write(`${t("Step limit reached.")}
99
- `);
100
- failed = true;
101
- break;
102
- case "budgetExceeded":
103
- io.err.write(`${t("Budget exceeded ({kind}): {used} of {limit}. Further model calls are blocked; raise usageCap in ~/.bccli/config.json.", { kind: event.kind, used: Math.round(event.used * 100) / 100, limit: event.limit })}
104
- `);
105
- if (format === "stream-json") emit({ type: "error", message: "budgetExceeded" });
106
- failed = true;
107
- break;
108
- case "aborted":
109
- io.err.write(`${t("Cancelled.")}
110
- `);
111
- failed = true;
112
- break;
113
- case "error":
114
- io.err.write(`Error: ${event.message}
115
- `);
116
- if (format === "stream-json") emit({ type: "error", message: event.message });
117
- failed = true;
118
- break;
119
- }
120
- };
121
- rt.agent.askPermission = async (req) => {
122
- io.err.write(` ${t("Needs permission for {tool}. Run with --allow-all or --allowed-tools {kind}.", { tool: req.tool, kind: req.kind })}
123
- `);
124
- return "no";
125
- };
126
- const controller = new AbortController();
127
- const onSigint = () => controller.abort();
128
- process.once("SIGINT", onSigint);
129
- const onSigbreak = () => controller.abort();
130
- const useSigbreak = process.platform === "win32";
131
- if (useSigbreak) process.once("SIGBREAK", onSigbreak);
132
- const start = await runHooks(rt.config.hooks, "SessionStart", {}, { env: rt.env, cwd: rt.cwd });
133
- for (const warning of start.warnings) io.err.write(`${warning}
134
- `);
135
- await rt.emitPlugins("SessionStart");
136
- try {
137
- await rt.agent.run(prompt, controller.signal);
138
- const commands = rt.config.verifyCommands;
139
- if (edited && commands.length && !controller.signal.aborted) {
140
- for (let round = 0; round < MAX_VERIFY_ROUNDS; round++) {
141
- io.err.write(`${t("Verifying edits: {cmds}", { cmds: commands.join(", ") })}
142
- `);
143
- const failures = await runVerify(commands, { cwd: rt.cwd, env: rt.env, signal: controller.signal });
144
- if (!failures.length) {
145
- io.err.write(`${t("Verification passed.")}
146
- `);
147
- break;
148
- }
149
- if (round === MAX_VERIFY_ROUNDS - 1 || controller.signal.aborted) {
150
- io.err.write(`${t("Verification still failing after {n} fix round(s). Run the commands manually to see why.", { n: MAX_VERIFY_ROUNDS })}
151
- `);
152
- failed = true;
153
- break;
154
- }
155
- await rt.agent.run(verifyFollowup(failures), controller.signal);
156
- }
157
- }
158
- } finally {
159
- process.off("SIGINT", onSigint);
160
- if (useSigbreak) process.off("SIGBREAK", onSigbreak);
161
- }
162
- const stop = await runHooks(rt.config.hooks, "Stop", {}, { env: rt.env, cwd: rt.cwd });
163
- for (const warning of stop.warnings) io.err.write(`${warning}
164
- `);
165
- await rt.emitPlugins("Stop");
166
- const durationMs = Math.max(0, Date.now() - startedMs);
167
- if (format === "json") {
168
- io.out.write(
169
- `${JSON.stringify({
170
- status: failed ? "failed" : "ok",
171
- model: rt.modelRef,
172
- session_id: rt.session.id,
173
- usage: rt.agent.totalUsage,
174
- duration_ms: durationMs,
175
- result
176
- })}
177
- `
178
- );
179
- } else if (format === "stream-json") {
180
- if (result) emit({ type: "message", text: result });
181
- emit({
182
- type: "result",
183
- status: failed ? "failed" : "ok",
184
- model: rt.modelRef,
185
- session_id: rt.session.id,
186
- usage: rt.agent.totalUsage,
187
- duration_ms: durationMs
188
- });
189
- } else if (wroteText) {
190
- io.out.write("\n");
191
- }
192
- return failed ? 1 : 0;
193
- }
194
-
195
- // src/worktree.ts
196
- import { execFile } from "child_process";
197
- import { existsSync } from "fs";
198
- import { basename, dirname, join } from "path";
199
- function git(cwd, args, bin = "git") {
200
- return new Promise((resolve) => {
201
- execFile(
202
- bin,
203
- args,
204
- { cwd, maxBuffer: 20 * 1024 * 1024 },
205
- (error, stdout, stderr) => resolve({ ok: !error, out: error ? stderr : stdout, missing: error?.code === "ENOENT" })
206
- );
207
- });
208
- }
209
- var NAME_RE = /^[A-Za-z0-9._-]+$/;
210
- function samePath(a, b) {
211
- const norm = (p) => process.platform === "win32" ? p.replace(/\//g, "\\").toLowerCase() : p.replace(/\\/g, "/");
212
- return norm(a) === norm(b);
213
- }
214
- function worktreeEntries(porcelain) {
215
- return porcelain.split(/\r?\n\r?\n/).map((block) => {
216
- const path = /^worktree (.+)$/m.exec(block)?.[1]?.trim();
217
- if (!path) return void 0;
218
- const branch = /^branch refs\/heads\/(.+)$/m.exec(block)?.[1];
219
- return branch ? { path, branch } : { path };
220
- }).filter((entry) => !!entry);
221
- }
222
- async function resolveWorktree(cwd, name, bin = "git") {
223
- if (!NAME_RE.test(name)) {
224
- throw new ConfigError(t('Invalid worktree name "{name}": use letters, digits, dot, dash, or underscore (no slashes).', { name }));
225
- }
226
- const inside = await git(cwd, ["rev-parse", "--is-inside-work-tree"], bin);
227
- if (inside.missing) throw new ConfigError(t("git is not installed or not on PATH, so -w cannot be used."));
228
- if (!inside.ok || inside.out.trim() !== "true") throw new ConfigError(t("This folder is not a git repository, so -w cannot create a worktree."));
229
- const list = await git(cwd, ["worktree", "list", "--porcelain"], bin);
230
- if (!list.ok) throw new ConfigError(t("This folder is not a git repository, so -w cannot create a worktree."));
231
- const entries = worktreeEntries(list.out);
232
- const main = entries[0]?.path;
233
- if (!main) throw new ConfigError(t("This folder is not a git repository, so -w cannot create a worktree."));
234
- const path = join(dirname(main), `${basename(main)}.worktrees`, name);
235
- if (entries.some((entry) => samePath(entry.path, path))) return { path, created: false };
236
- if (existsSync(path)) {
237
- throw new ConfigError(t("Worktree path {path} already exists but is not a registered worktree; remove or rename it first.", { path }));
238
- }
239
- const branch = `bccli/${name}`;
240
- const hasBranch = (await git(main, ["rev-parse", "--verify", "-q", `refs/heads/${branch}`], bin)).ok;
241
- const add = hasBranch ? await git(main, ["worktree", "add", path, branch], bin) : await git(main, ["worktree", "add", "-b", branch, path], bin);
242
- if (!add.ok) throw new ConfigError(add.out.trim() || t("This folder is not a git repository, so -w cannot create a worktree."));
243
- return { path, created: true };
244
- }
245
- async function worktreeListText(cwd, bin = "git") {
246
- const inside = await git(cwd, ["rev-parse", "--is-inside-work-tree"], bin);
247
- if (inside.missing) return t("git is not installed or not on PATH, so /worktree cannot be used.");
248
- if (!inside.ok || inside.out.trim() !== "true") return t("This folder is not a git repository, so there is no diff.");
249
- const list = await git(cwd, ["worktree", "list", "--porcelain"], bin);
250
- if (!list.ok) return t("This folder is not a git repository, so there is no diff.");
251
- const rows = worktreeEntries(list.out).map((entry) => ` ${entry.path}${entry.branch ? ` (${entry.branch})` : ""}`);
252
- return [
253
- t("Git worktrees:"),
254
- ...rows,
255
- "",
256
- t("Create one with: bccli -w <name>"),
257
- t("Remove with: git worktree remove <path>")
258
- ].join("\n");
259
- }
260
-
261
- export {
262
- MAX_VERIFY_ROUNDS,
263
- runVerify,
264
- verifyFollowup,
265
- toolLabel,
266
- resolvePrintPrompt,
267
- runPrint,
268
- resolveWorktree,
269
- worktreeListText
270
- };
@@ -1,78 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- listAllModels
4
- } from "./chunk-INMSTUB2.js";
5
- import {
6
- hasKey,
7
- providerName,
8
- removeGlobalProvider,
9
- writeGlobalConfig
10
- } from "./chunk-6SDRWEPN.js";
11
- import {
12
- PRESETS,
13
- loadConfig,
14
- saveCredential
15
- } from "./chunk-TLCL6DC7.js";
16
- import {
17
- t
18
- } from "./chunk-R754XIBJ.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
- const createdEntry = !preset && !config.providers[id];
53
- if (createdEntry) {
54
- if (!args.url) {
55
- deps.err(t("{id} is not a preset. For a custom provider add --url <OpenAI-compatible base URL>.", { id }));
56
- return 1;
57
- }
58
- writeGlobalConfig(
59
- { providers: { [id]: { baseURL: args.url, ...args.name ? { name: args.name } : {}, ...args.keyEnv ? { apiKeyEnv: args.keyEnv } : {} } } },
60
- deps.env
61
- );
62
- }
63
- const needsKey = !!loadConfig(deps.cwd, deps.env).providers[id].apiKeyEnv || !preset;
64
- const key = needsKey ? await deps.readSecret(t("API key for {id} (leave empty if not needed): ", { id })) : "";
65
- const result = await addProviderKey(id, key, deps);
66
- if (!result.ok) {
67
- if (createdEntry) removeGlobalProvider(id, deps.env);
68
- deps.err(result.error);
69
- return 1;
70
- }
71
- deps.out(t("{id} is ready. {n} models available.", { id, n: result.models }));
72
- return 0;
73
- }
74
-
75
- export {
76
- addProviderKey,
77
- runProviderCommand
78
- };