@botconnector/bccli 0.4.0-beta.13 → 0.4.0-beta.15
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 +14 -0
- package/dist/args-L555OESS.js +12 -0
- package/dist/{chunk-6BLZELE6.js → chunk-4KMOCORM.js} +2 -2
- package/dist/{chunk-TRMRDQ43.js → chunk-6AVRFRFU.js} +31 -24
- package/dist/{chunk-7S6JXQCH.js → chunk-D7CF42EW.js} +2 -1
- package/dist/chunk-DDA5TJ7T.js +440 -0
- package/dist/{chunk-BFZXRKW5.js → chunk-ERVDVPKJ.js} +4 -3
- package/dist/{chunk-KV27KTOX.js → chunk-K5J2GGXT.js} +11 -4
- package/dist/{chunk-DX7CZTBB.js → chunk-RQGMYLVH.js} +11 -10
- package/dist/{chunk-U7GK73CK.js → chunk-RY7NCJXH.js} +19 -18
- package/dist/{chunk-CLN4Z5E3.js → chunk-VEZFR7M7.js} +6 -3
- package/dist/cli.js +141 -119
- package/dist/{config-YUNKPCNB.js → config-CGYYFECG.js} +2 -2
- package/dist/{integrationCli-3CGHHTM2.js → integrationCli-RQO3HHQE.js} +31 -30
- package/dist/{login-N2CJC6RU.js → login-O2BQX6LZ.js} +1 -1
- package/dist/{mcpCli-DZWQPTOS.js → mcpCli-QJE7P76I.js} +11 -10
- package/dist/providerCli-3FJAUH7C.js +12 -0
- package/dist/{ui-JD5YWGPE.js → ui-HVH3TR3I.js} +218 -192
- package/package.json +1 -1
- package/dist/args-SKWUZOJD.js +0 -12
- package/dist/chunk-2MGINW4O.js +0 -114
- package/dist/providerCli-Q7LRM2NP.js +0 -12
|
@@ -4,8 +4,9 @@ import {
|
|
|
4
4
|
bccliHome,
|
|
5
5
|
loadConfig,
|
|
6
6
|
readCredentials,
|
|
7
|
-
resolveModel
|
|
8
|
-
|
|
7
|
+
resolveModel,
|
|
8
|
+
t
|
|
9
|
+
} from "./chunk-DDA5TJ7T.js";
|
|
9
10
|
|
|
10
11
|
// src/integrationCli.ts
|
|
11
12
|
import { chmodSync, copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
@@ -26,17 +27,17 @@ function parseTarget(raw) {
|
|
|
26
27
|
if (raw === "opencode" || raw === "aider" || raw === "cline" || raw === "deepseek-harness" || raw === "cursor" || raw === "openai-cli" || raw === "openai-sdk" || raw === "openai-compatible" || raw === "codex" || raw === "claude-code") return raw;
|
|
27
28
|
if (raw === "dsh") return "deepseek-harness";
|
|
28
29
|
throw new ConfigError(
|
|
29
|
-
"Agent
|
|
30
|
+
t("Agent must be one of: opencode, aider, cline, deepseek-harness (dsh), cursor, openai-cli, openai-sdk, openai-compatible, codex, claude-code")
|
|
30
31
|
);
|
|
31
32
|
}
|
|
32
33
|
function homeDir(env) {
|
|
33
34
|
const value = env.HOME || env.USERPROFILE;
|
|
34
|
-
if (!value) throw new ConfigError("
|
|
35
|
+
if (!value) throw new ConfigError(t("Cannot determine the home directory."));
|
|
35
36
|
return value;
|
|
36
37
|
}
|
|
37
38
|
function ensureSecret(env) {
|
|
38
39
|
const key = readCredentials(env)["bc-cloud"] || env.BOTCONNECTOR_API_KEY;
|
|
39
|
-
if (!key) throw new ConfigError("API key
|
|
40
|
+
if (!key) throw new ConfigError(t("No BotConnector API key yet. Run bccli login bc-cloud first."));
|
|
40
41
|
const dir = integrationHome(env);
|
|
41
42
|
mkdirSync(dir, { recursive: true });
|
|
42
43
|
const path = keyPath(env);
|
|
@@ -76,11 +77,11 @@ async function botConnectorModels(deps) {
|
|
|
76
77
|
const config = loadConfig(deps.cwd);
|
|
77
78
|
const resolved = resolveModel(config, config.model, deps.env);
|
|
78
79
|
const key = readCredentials(deps.env)["bc-cloud"] || resolved.apiKey;
|
|
79
|
-
if (!key) throw new ConfigError("API key
|
|
80
|
+
if (!key) throw new ConfigError(t("No BotConnector API key yet. Run bccli login bc-cloud first."));
|
|
80
81
|
const response = await deps.fetch(`${BASE_URL}/models`, {
|
|
81
82
|
headers: { authorization: `Bearer ${key}` }
|
|
82
83
|
});
|
|
83
|
-
if (!response.ok) throw new ConfigError(
|
|
84
|
+
if (!response.ok) throw new ConfigError(t("Could not load the BotConnector model catalog (HTTP {status}).", { status: response.status }));
|
|
84
85
|
const payload = await response.json();
|
|
85
86
|
return (payload.data ?? []).map((item) => item.id).filter((id) => typeof id === "string" && !!id);
|
|
86
87
|
}
|
|
@@ -95,7 +96,7 @@ async function connectOpenCode(deps) {
|
|
|
95
96
|
try {
|
|
96
97
|
doc = JSON.parse(readFileSync(path, "utf8"));
|
|
97
98
|
} catch {
|
|
98
|
-
throw new ConfigError(
|
|
99
|
+
throw new ConfigError(t("The OpenCode config is not valid JSON: {path}", { path }));
|
|
99
100
|
}
|
|
100
101
|
}
|
|
101
102
|
const providers = doc.provider && typeof doc.provider === "object" && !Array.isArray(doc.provider) ? { ...doc.provider } : {};
|
|
@@ -118,7 +119,7 @@ async function connectOpenCode(deps) {
|
|
|
118
119
|
files: [{ path, ...backup }],
|
|
119
120
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
120
121
|
}, deps.env);
|
|
121
|
-
deps.out(
|
|
122
|
+
deps.out(t("OpenCode is connected to BotConnector ({n} models).", { n: models.length }));
|
|
122
123
|
deps.out(`Config: ${path}`);
|
|
123
124
|
}
|
|
124
125
|
var AIDER_BEGIN = "# BEGIN BCCLI BOTCONNECTOR";
|
|
@@ -150,7 +151,7 @@ async function connectAider(deps) {
|
|
|
150
151
|
let existing = existsSync(configPath) ? readFileSync(configPath, "utf8") : "";
|
|
151
152
|
existing = stripManagedBlock(existing);
|
|
152
153
|
if (/^\s*(env-file|openai-api-base|openai-api-key)\s*:/m.test(existing)) {
|
|
153
|
-
throw new ConfigError(
|
|
154
|
+
throw new ConfigError(t("The Aider config already has its own env-file/openai-api settings: {path}. BCCLI will not overwrite them.", { path: configPath }));
|
|
154
155
|
}
|
|
155
156
|
mkdirSync(dirname(envPath), { recursive: true });
|
|
156
157
|
writeFileSync(envPath, [
|
|
@@ -179,7 +180,7 @@ ${managed}
|
|
|
179
180
|
files: [{ path: configPath, ...configBackup }, { path: envPath, ...envBackup }],
|
|
180
181
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
181
182
|
}, deps.env);
|
|
182
|
-
deps.out("Aider
|
|
183
|
+
deps.out(t("Aider is connected to BotConnector."));
|
|
183
184
|
deps.out(`Config: ${configPath}`);
|
|
184
185
|
}
|
|
185
186
|
function externalDefaultModel(models, deps) {
|
|
@@ -187,7 +188,7 @@ function externalDefaultModel(models, deps) {
|
|
|
187
188
|
const preferred = config.model.startsWith("bc-cloud/") ? config.model.slice("bc-cloud/".length) : "";
|
|
188
189
|
if (preferred && models.includes(preferred)) return preferred;
|
|
189
190
|
const first = models[0];
|
|
190
|
-
if (!first) throw new ConfigError("
|
|
191
|
+
if (!first) throw new ConfigError(t("The BotConnector catalog has no model that external agents can use."));
|
|
191
192
|
return first;
|
|
192
193
|
}
|
|
193
194
|
async function connectCline(deps) {
|
|
@@ -204,7 +205,7 @@ async function connectCline(deps) {
|
|
|
204
205
|
try {
|
|
205
206
|
doc = JSON.parse(readFileSync(path, "utf8"));
|
|
206
207
|
} catch {
|
|
207
|
-
throw new ConfigError(
|
|
208
|
+
throw new ConfigError(t("The Cline config is not valid JSON: {path}", { path }));
|
|
208
209
|
}
|
|
209
210
|
}
|
|
210
211
|
const providers = doc.providers && typeof doc.providers === "object" && !Array.isArray(doc.providers) ? { ...doc.providers } : {};
|
|
@@ -212,7 +213,7 @@ async function connectCline(deps) {
|
|
|
212
213
|
const currentBase = current?.settings?.baseUrl;
|
|
213
214
|
if (currentBase && currentBase.replace(/\/+$/, "") !== BASE_URL) {
|
|
214
215
|
throw new ConfigError(
|
|
215
|
-
"Cline
|
|
216
|
+
t("Cline already uses another openai-compatible provider ({base}). BCCLI will not overwrite it; disconnect or change that provider first.", { base: currentBase })
|
|
216
217
|
);
|
|
217
218
|
}
|
|
218
219
|
providers["openai-compatible"] = {
|
|
@@ -236,7 +237,7 @@ async function connectCline(deps) {
|
|
|
236
237
|
files: [{ path, ...backup }],
|
|
237
238
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
238
239
|
}, deps.env);
|
|
239
|
-
deps.out(
|
|
240
|
+
deps.out(t("Cline is connected to BotConnector ({n} models available; default {model}).", { n: models.length, model }));
|
|
240
241
|
deps.out(`Config: ${path}`);
|
|
241
242
|
deps.out("Catatan: Cline menyimpan API key provider di providers.json miliknya.");
|
|
242
243
|
}
|
|
@@ -265,10 +266,10 @@ async function connectDeepSeekHarness(deps) {
|
|
|
265
266
|
const models = await botConnectorModels(deps);
|
|
266
267
|
const source = existsSync(settingsPath) ? readFileSync(settingsPath, "utf8") : "{}\n";
|
|
267
268
|
const doc = parseDocument(source);
|
|
268
|
-
if (doc.errors.length) throw new ConfigError(
|
|
269
|
+
if (doc.errors.length) throw new ConfigError(t("The DeepSeek Harness config is not valid YAML: {path}", { path: settingsPath }));
|
|
269
270
|
const existingProvider = doc.getIn(["llm-pi-ai", "providers", PROVIDER_ID]);
|
|
270
271
|
if (existingProvider?.baseURL && existingProvider.baseURL.replace(/\/+$/, "") !== BASE_URL) {
|
|
271
|
-
throw new ConfigError(
|
|
272
|
+
throw new ConfigError(t('DeepSeek Harness already has a provider "{id}" with a different endpoint.', { id: PROVIDER_ID }));
|
|
272
273
|
}
|
|
273
274
|
doc.setIn(["llm-pi-ai", "providers", PROVIDER_ID], {
|
|
274
275
|
displayName: "BotConnector",
|
|
@@ -282,7 +283,7 @@ async function connectDeepSeekHarness(deps) {
|
|
|
282
283
|
let envText = existsSync(envPath) ? readFileSync(envPath, "utf8") : "";
|
|
283
284
|
envText = stripDshEnvBlock(envText);
|
|
284
285
|
if (/^\s*BOTCONNECTOR_API_KEY\s*=/m.test(envText)) {
|
|
285
|
-
throw new ConfigError(
|
|
286
|
+
throw new ConfigError(t("$DSH_HOME/.env already has BOTCONNECTOR_API_KEY outside the BCCLI block: {path}", { path: envPath }));
|
|
286
287
|
}
|
|
287
288
|
const managed = [DSH_ENV_BEGIN, `BOTCONNECTOR_API_KEY=${key}`, DSH_ENV_END].join("\n");
|
|
288
289
|
const mergedEnv = envText.trim() ? `${envText.trimEnd()}
|
|
@@ -302,11 +303,11 @@ ${managed}
|
|
|
302
303
|
files: [{ path: settingsPath, ...settingsBackup }, { path: envPath, ...envBackup }],
|
|
303
304
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
304
305
|
}, deps.env);
|
|
305
|
-
deps.out(
|
|
306
|
+
deps.out(t("DeepSeek Harness is connected to BotConnector ({n} models).", { n: models.length }));
|
|
306
307
|
deps.out(`Config: ${settingsPath}`);
|
|
307
308
|
deps.out("Provider: botconnector (OpenAI Chat Completions)");
|
|
308
|
-
deps.out("
|
|
309
|
-
deps.out("
|
|
309
|
+
deps.out(t("Run: dsh web"));
|
|
310
|
+
deps.out(t("Equivalent long form: dsh --profile web"));
|
|
310
311
|
}
|
|
311
312
|
function managedProfilePath(target, env, ext = "env") {
|
|
312
313
|
return join(integrationHome(env), `${target}.${ext}`);
|
|
@@ -369,13 +370,13 @@ async function connectOpenAiCli(deps) {
|
|
|
369
370
|
}, deps.env);
|
|
370
371
|
deps.out("OpenAI CLI profile BotConnector siap.");
|
|
371
372
|
deps.out(`Launcher: ${launcher}`);
|
|
372
|
-
deps.out("
|
|
373
|
+
deps.out(t("The launcher does not store the API key in the script; the key is read from BCCLI storage when it runs."));
|
|
373
374
|
}
|
|
374
375
|
async function connectOpenAiSdk(deps) {
|
|
375
376
|
const path = writeOpenAiEnvProfile("openai-sdk", deps);
|
|
376
377
|
deps.out("OpenAI SDK profile BotConnector siap.");
|
|
377
378
|
deps.out(`Env profile: ${path}`);
|
|
378
|
-
deps.out("Python
|
|
379
|
+
deps.out(t("The Python and Node OpenAI SDKs can read OPENAI_BASE_URL and OPENAI_API_KEY from this profile."));
|
|
379
380
|
}
|
|
380
381
|
async function connectOpenAiCompatible(deps) {
|
|
381
382
|
const path = writeOpenAiEnvProfile("openai-compatible", deps);
|
|
@@ -412,14 +413,14 @@ async function connectCursor(deps) {
|
|
|
412
413
|
}, deps.env);
|
|
413
414
|
deps.out("Cursor guided setup profile dibuat.");
|
|
414
415
|
deps.out(`Panduan: ${path}`);
|
|
415
|
-
deps.out("BCCLI
|
|
416
|
+
deps.out(t("BCCLI does not edit Cursor\u2019s internal storage because that override format is not a stable external configuration."));
|
|
416
417
|
}
|
|
417
418
|
function agentToolDefaultModel(models) {
|
|
418
419
|
for (const id of ["ling-3.0-flash", "mimo-v2.5", "deepseek-v4-flash"]) {
|
|
419
420
|
if (models.includes(id)) return id;
|
|
420
421
|
}
|
|
421
422
|
const first = models[0];
|
|
422
|
-
if (!first) throw new ConfigError("
|
|
423
|
+
if (!first) throw new ConfigError(t("The BotConnector catalog has no model for external coding agents."));
|
|
423
424
|
return first;
|
|
424
425
|
}
|
|
425
426
|
async function connectCodex(deps) {
|
|
@@ -429,7 +430,7 @@ async function connectCodex(deps) {
|
|
|
429
430
|
const profilePath = join(codexHome, "botconnector.config.toml");
|
|
430
431
|
const launcher = join(integrationHome(deps.env), process.platform === "win32" ? "codex-botconnector.cmd" : "codex-botconnector");
|
|
431
432
|
if (existsSync(profilePath)) {
|
|
432
|
-
throw new ConfigError("Codex
|
|
433
|
+
throw new ConfigError(t("Codex already has botconnector.config.toml. BCCLI will not overwrite it."));
|
|
433
434
|
}
|
|
434
435
|
const profileBackup = safeBackup(profilePath, deps.env);
|
|
435
436
|
const launcherBackup = safeBackup(launcher, deps.env);
|
|
@@ -480,7 +481,7 @@ async function connectCodex(deps) {
|
|
|
480
481
|
files: [{ path: profilePath, ...profileBackup }, { path: launcher, ...launcherBackup }],
|
|
481
482
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
482
483
|
}, deps.env);
|
|
483
|
-
deps.out("Codex CLI
|
|
484
|
+
deps.out(t("Codex CLI is connected to the BotConnector Responses API."));
|
|
484
485
|
deps.out(`Profile: ${profilePath}`);
|
|
485
486
|
deps.out(`Launcher: ${launcher}`);
|
|
486
487
|
deps.out(`Default coding model: ${model}`);
|
|
@@ -532,14 +533,14 @@ async function connectClaudeCode(deps) {
|
|
|
532
533
|
files: [{ path: launcher, ...backup }],
|
|
533
534
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
534
535
|
}, deps.env);
|
|
535
|
-
deps.out("Claude Code
|
|
536
|
+
deps.out(t("Claude Code is connected to the BotConnector Anthropic Messages compatibility API."));
|
|
536
537
|
deps.out(`Launcher: ${launcher}`);
|
|
537
538
|
deps.out(`Default coding model: ${model}`);
|
|
538
539
|
}
|
|
539
540
|
function disconnect(target, deps) {
|
|
540
541
|
const state = readState(target, deps.env);
|
|
541
542
|
if (!state) {
|
|
542
|
-
deps.out(
|
|
543
|
+
deps.out(t("{target}: not managed by BCCLI yet.", { target }));
|
|
543
544
|
return;
|
|
544
545
|
}
|
|
545
546
|
for (const file of [...state.files].reverse()) {
|
|
@@ -551,7 +552,7 @@ function disconnect(target, deps) {
|
|
|
551
552
|
}
|
|
552
553
|
}
|
|
553
554
|
rmSync(statePath(target, deps.env), { force: true });
|
|
554
|
-
deps.out(
|
|
555
|
+
deps.out(t("{target}: the BotConnector integration was removed and the config restored.", { target }));
|
|
555
556
|
}
|
|
556
557
|
function listIntegrations(deps) {
|
|
557
558
|
for (const target of ["opencode", "aider", "cline", "deepseek-harness", "cursor", "openai-cli", "openai-sdk", "openai-compatible", "codex", "claude-code"]) {
|
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
import {
|
|
3
3
|
CATALOG,
|
|
4
4
|
fillTemplate
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-RQGMYLVH.js";
|
|
6
6
|
import {
|
|
7
7
|
addGlobalServer,
|
|
8
8
|
globalMcpPath,
|
|
9
9
|
readMcpFile,
|
|
10
10
|
removeGlobalServer
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-ERVDVPKJ.js";
|
|
12
12
|
import {
|
|
13
|
-
bccliHome
|
|
14
|
-
|
|
13
|
+
bccliHome,
|
|
14
|
+
t
|
|
15
|
+
} from "./chunk-DDA5TJ7T.js";
|
|
15
16
|
|
|
16
17
|
// src/mcpCli.ts
|
|
17
18
|
async function runMcpCommand(args, deps) {
|
|
@@ -24,26 +25,26 @@ async function runMcpCommand(args, deps) {
|
|
|
24
25
|
return 0;
|
|
25
26
|
}
|
|
26
27
|
if (!name) {
|
|
27
|
-
deps.err(
|
|
28
|
+
deps.err(t("Usage: bccli mcp {action} <name>", { action }));
|
|
28
29
|
return 1;
|
|
29
30
|
}
|
|
30
31
|
if (action === "remove") {
|
|
31
32
|
removeGlobalServer(home, name);
|
|
32
|
-
deps.out(
|
|
33
|
+
deps.out(t("{name} removed.", { name }));
|
|
33
34
|
return 0;
|
|
34
35
|
}
|
|
35
36
|
if (action !== "add") {
|
|
36
|
-
deps.err(
|
|
37
|
+
deps.err(t("Unknown action: {action}. Use list, add, or remove.", { action }));
|
|
37
38
|
return 1;
|
|
38
39
|
}
|
|
39
40
|
if (args.url) {
|
|
40
41
|
addGlobalServer(home, name, { type: "http", url: args.url });
|
|
41
|
-
deps.out(
|
|
42
|
+
deps.out(t("{name} installed ({url}).", { name, url: args.url }));
|
|
42
43
|
return 0;
|
|
43
44
|
}
|
|
44
45
|
const entry = CATALOG.find((c) => c.name === name);
|
|
45
46
|
if (!entry) {
|
|
46
|
-
deps.err(
|
|
47
|
+
deps.err(t("{name} is not in the catalog. For another remote server use: bccli mcp add {name} --url <url>", { name }));
|
|
47
48
|
return 1;
|
|
48
49
|
}
|
|
49
50
|
const values = Object.fromEntries(args.values.map((v) => [v.slice(0, v.indexOf("=")), v.slice(v.indexOf("=") + 1)]));
|
|
@@ -57,7 +58,7 @@ async function runMcpCommand(args, deps) {
|
|
|
57
58
|
deps.err(`${error.message}${hint ? `. Contoh: bccli mcp add ${name} ${hint}` : ""}`);
|
|
58
59
|
return 1;
|
|
59
60
|
}
|
|
60
|
-
deps.out(
|
|
61
|
+
deps.out(t("{name} installed. Active in the next bccli session.", { name }));
|
|
61
62
|
return 0;
|
|
62
63
|
}
|
|
63
64
|
export {
|