@botconnector/bccli 0.4.0-beta.35 → 0.4.0-beta.36
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/dist/{args-EFFR44JW.js → args-GJUGK4F4.js} +3 -3
- package/dist/{chunk-AOVIQXXO.js → chunk-3DHJO7NC.js} +1 -1
- package/dist/{chunk-UJH2JT6H.js → chunk-3NTTXK4Y.js} +1 -1
- package/dist/{chunk-GEG6QT5H.js → chunk-5LSTEXSG.js} +1 -1
- package/dist/{chunk-NM3C75BT.js → chunk-BTCJNE7R.js} +3 -3
- package/dist/{chunk-IJGTX7RC.js → chunk-CZMTWXCE.js} +25 -4
- package/dist/{chunk-LRMC4AOV.js → chunk-HKHKYFZI.js} +8 -5
- package/dist/{chunk-FNBTY56Z.js → chunk-NSCC5VMS.js} +2 -2
- package/dist/{chunk-GJAX4NCM.js → chunk-QKWSDZJQ.js} +3 -3
- package/dist/{chunk-CDUPYHNL.js → chunk-SZRM2VOB.js} +1 -1
- package/dist/cli.js +21 -20
- package/dist/{config-DMF4AO7E.js → config-53MC3AH2.js} +2 -2
- package/dist/{integrationCli-W5LJQJ4J.js → integrationCli-JWLY6E7E.js} +102 -5
- package/dist/{login-7IARC5WX.js → login-LKAO5PVR.js} +1 -1
- package/dist/{mcpCli-UGNEDBMU.js → mcpCli-THHBIX7I.js} +3 -3
- package/dist/providerCli-WJXJW3W2.js +12 -0
- package/dist/{ui-KK6ARPCL.js → ui-I5BTZATT.js} +13 -8
- package/package.json +1 -1
- package/dist/providerCli-4JTIW3YA.js +0 -12
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
helpText,
|
|
4
4
|
parseCliArgs
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-BTCJNE7R.js";
|
|
6
6
|
import "./chunk-ELML4KVQ.js";
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-3NTTXK4Y.js";
|
|
8
|
+
import "./chunk-CZMTWXCE.js";
|
|
9
9
|
export {
|
|
10
10
|
helpText,
|
|
11
11
|
parseCliArgs
|
|
@@ -5,12 +5,12 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
REASONING_LEVELS,
|
|
7
7
|
isReasoningLevel
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-3NTTXK4Y.js";
|
|
9
9
|
import {
|
|
10
10
|
ConfigError,
|
|
11
11
|
parseLang,
|
|
12
12
|
t
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-CZMTWXCE.js";
|
|
14
14
|
|
|
15
15
|
// src/args.ts
|
|
16
16
|
import { parseArgs } from "util";
|
|
@@ -24,7 +24,7 @@ Usage:
|
|
|
24
24
|
bccli provider list|add <id>|remove <id> manage providers (custom: --url <url> [--name N] [--key-env ENV])
|
|
25
25
|
bccli mcp list|add <name>|remove <name> manage MCP servers (catalog, or --url <url>)
|
|
26
26
|
bccli integrations show the status of external agent integrations
|
|
27
|
-
bccli connect <agent> opencode, aider, cline, dsh, codex, claude-code, cursor, openai-cli, openai-sdk, openai-compatible
|
|
27
|
+
bccli connect <agent> opencode, aider, cline, dsh, codex, claude-code, cursor, openai-cli, openai-sdk, openai-compatible, openclaw, hermes
|
|
28
28
|
bccli disconnect <agent> remove an integration and restore the previous config
|
|
29
29
|
|
|
30
30
|
Options:
|
|
@@ -49,7 +49,7 @@ var ID = {
|
|
|
49
49
|
"Session number (enter = new): ": "Nomor sesi (enter = baru): ",
|
|
50
50
|
'This repo wants to run the MCP server "{name}" ({what}). Allow? [y/N] ': 'Repo ini ingin menjalankan server MCP "{name}" ({what}). Izinkan? [y/N] ',
|
|
51
51
|
"Conversation summarized to fit the model context.": "Percakapan diringkas agar muat di konteks model.",
|
|
52
|
-
'Step limit of
|
|
52
|
+
'Step limit of {n} reached. Type "continue" to keep going.': 'Batas {n} langkah tercapai. Ketik "lanjut" untuk meneruskan.',
|
|
53
53
|
"Cancelled.": "Dibatalkan.",
|
|
54
54
|
"Permission mode: {mode}": "Mode izin: {mode}",
|
|
55
55
|
"Folder: {cwd}": "Folder: {cwd}",
|
|
@@ -111,7 +111,7 @@ Usage:
|
|
|
111
111
|
bccli provider list|add <id>|remove <id> manage providers (custom: --url <url> [--name N] [--key-env ENV])
|
|
112
112
|
bccli mcp list|add <name>|remove <name> manage MCP servers (catalog, or --url <url>)
|
|
113
113
|
bccli integrations show the status of external agent integrations
|
|
114
|
-
bccli connect <agent> opencode, aider, cline, dsh, codex, claude-code, cursor, openai-cli, openai-sdk, openai-compatible
|
|
114
|
+
bccli connect <agent> opencode, aider, cline, dsh, codex, claude-code, cursor, openai-cli, openai-sdk, openai-compatible, openclaw, hermes
|
|
115
115
|
bccli disconnect <agent> remove an integration and restore the previous config
|
|
116
116
|
|
|
117
117
|
Options:
|
|
@@ -134,7 +134,7 @@ Pemakaian:
|
|
|
134
134
|
bccli provider list|add <id>|remove <id> kelola provider (custom: --url <url> [--name N] [--key-env ENV])
|
|
135
135
|
bccli mcp list|add <nama>|remove <nama> kelola server MCP (katalog, atau --url <url>)
|
|
136
136
|
bccli integrations lihat status integrasi agent eksternal
|
|
137
|
-
bccli connect <agent> opencode, aider, cline, dsh, codex, claude-code, cursor, openai-cli, openai-sdk, openai-compatible
|
|
137
|
+
bccli connect <agent> opencode, aider, cline, dsh, codex, claude-code, cursor, openai-cli, openai-sdk, openai-compatible, openclaw, hermes
|
|
138
138
|
bccli disconnect <agent> lepas integrasi dan pulihkan config sebelumnya
|
|
139
139
|
|
|
140
140
|
Opsi:
|
|
@@ -155,6 +155,7 @@ Opsi:
|
|
|
155
155
|
"Needs permission for {tool}. Run with --allow-all or --allowed-tools {kind}.": "Butuh izin untuk {tool}. Jalankan dengan --allow-all atau --allowed-tools {kind}.",
|
|
156
156
|
"--lang must be one of: en, id": "--lang harus salah satu dari: en, id",
|
|
157
157
|
"Invalid config: {path} ({error})": "Config tidak valid: {path} ({error})",
|
|
158
|
+
"maxSteps must be a positive number, or 0/null to disable the limit.": "maxSteps harus berupa angka positif, atau 0/null untuk menonaktifkan batas.",
|
|
158
159
|
"Model must be in provider/model format, e.g. bc-cloud/glm-5.3-flash (got: {ref})": "Model harus berformat provider/model, contoh bc-cloud/glm-5.3-flash (dapat: {ref})",
|
|
159
160
|
'Provider "{id}" is not in the config. Available: {list}': 'Provider "{id}" tidak ada di config. Tersedia: {list}',
|
|
160
161
|
"No API key for {id} yet. Run `bccli login` or set the env var {env}.": "API key untuk {id} belum ada. Jalankan `bccli login` atau set env {env}.",
|
|
@@ -195,10 +196,11 @@ Opsi:
|
|
|
195
196
|
'-p mode needs a task, e.g. bccli -p "explain this repo"': 'Mode -p butuh tugas, contoh: bccli -p "jelaskan repo ini"',
|
|
196
197
|
'Project MCP server "{name}" skipped (not approved yet; run interactive bccli once to approve it).': 'Server MCP project "{name}" dilewati (belum diizinkan; jalankan bccli interaktif sekali untuk menyetujuinya).',
|
|
197
198
|
'Interactive mode needs a terminal. For scripts/CI use: bccli -p "task"': 'Mode interaktif butuh terminal. Untuk skrip/CI pakai: bccli -p "tugas"',
|
|
198
|
-
"Agent must be one of: opencode, aider, cline, deepseek-harness (dsh), cursor, openai-cli, openai-sdk, openai-compatible, codex, claude-code": "Agent harus salah satu dari: opencode, aider, cline, deepseek-harness (dsh), cursor, openai-cli, openai-sdk, openai-compatible, codex, claude-code",
|
|
199
|
+
"Agent must be one of: opencode, aider, cline, deepseek-harness (dsh), cursor, openai-cli, openai-sdk, openai-compatible, codex, claude-code, openclaw, hermes": "Agent harus salah satu dari: opencode, aider, cline, deepseek-harness (dsh), cursor, openai-cli, openai-sdk, openai-compatible, codex, claude-code, openclaw, hermes",
|
|
199
200
|
"Cannot determine the home directory.": "Home directory tidak dapat ditentukan.",
|
|
200
201
|
"No BotConnector API key yet. Run bccli login bc-cloud first.": "API key BotConnector belum tersedia. Jalankan bccli login bc-cloud terlebih dahulu.",
|
|
201
202
|
"Could not load the BotConnector model catalog (HTTP {status}).": "Katalog model BotConnector gagal dimuat (HTTP {status}).",
|
|
203
|
+
"The BotConnector model catalog is empty; cannot configure external agents.": "Katalog model BotConnector kosong; agent eksternal tidak dapat dikonfigurasi.",
|
|
202
204
|
"The OpenCode config is not valid JSON: {path}": "Config OpenCode tidak valid JSON: {path}",
|
|
203
205
|
"OpenCode is connected to BotConnector ({n} models).": "OpenCode terhubung ke BotConnector ({n} model).",
|
|
204
206
|
"The Aider config already has its own env-file/openai-api settings: {path}. BCCLI will not overwrite them.": "Config Aider sudah memiliki env-file/openai-api setting sendiri: {path}. BCCLI tidak akan menimpanya.",
|
|
@@ -219,6 +221,15 @@ Opsi:
|
|
|
219
221
|
"Codex already has botconnector.config.toml. BCCLI will not overwrite it.": "Codex sudah memiliki botconnector.config.toml. BCCLI tidak akan menimpanya.",
|
|
220
222
|
"Codex CLI is connected to the BotConnector Responses API.": "Codex CLI terhubung ke BotConnector Responses API.",
|
|
221
223
|
"Claude Code is connected to the BotConnector Anthropic Messages compatibility API.": "Claude Code terhubung ke BotConnector Anthropic Messages compatibility API.",
|
|
224
|
+
'OpenClaw already has a provider "{id}" with a different endpoint ({base}). BCCLI will not overwrite it; disconnect or change that provider first.': 'OpenClaw sudah memiliki provider "{id}" dengan endpoint lain ({base}). BCCLI tidak akan menimpanya; disconnect/ubah provider tersebut lebih dulu.',
|
|
225
|
+
"The OpenClaw config is not valid JSON: {path}": "Config OpenClaw tidak valid JSON: {path}",
|
|
226
|
+
"OpenClaw is connected to BotConnector ({n} models; default {model}).": "OpenClaw terhubung ke BotConnector ({n} model; default {model}).",
|
|
227
|
+
"API key: env {env} (SecretRef, not stored in the config).": "API key: env {env} (SecretRef, tidak disimpan di config).",
|
|
228
|
+
"Set the default model in OpenClaw with: openclaw models set {provider}/{model}": "Atur model default di OpenClaw dengan: openclaw models set {provider}/{model}",
|
|
229
|
+
"The Hermes config is not valid YAML: {path}": "Config Hermes tidak valid YAML: {path}",
|
|
230
|
+
'Hermes already has a provider "{id}" with a different endpoint ({base}). BCCLI will not overwrite it; disconnect or change that provider first.': 'Hermes sudah memiliki provider "{id}" dengan endpoint lain ({base}). BCCLI tidak akan menimpanya; disconnect/ubah provider tersebut lebih dulu.',
|
|
231
|
+
"Hermes is connected to BotConnector ({n} models; default {model}).": "Hermes terhubung ke BotConnector ({n} model; default {model}).",
|
|
232
|
+
"API key: env {env} only (via key_env; a plaintext key in config.yaml is never written).": "API key: hanya env {env} (melalui key_env; key plaintext tidak pernah ditulis ke config.yaml).",
|
|
222
233
|
"{target}: not managed by BCCLI yet.": "{target}: belum dikelola oleh BCCLI.",
|
|
223
234
|
"{target}: the BotConnector integration was removed and the config restored.": "{target}: integrasi BotConnector dilepas dan config dipulihkan.",
|
|
224
235
|
"Custom commands:": "Perintah custom:",
|
|
@@ -361,6 +372,15 @@ function readJsonConfig(path) {
|
|
|
361
372
|
throw new ConfigError(t("Invalid config: {path} ({error})", { path, error: error.message }));
|
|
362
373
|
}
|
|
363
374
|
}
|
|
375
|
+
function resolvedMaxSteps(project, global) {
|
|
376
|
+
const hasProjectValue = Object.hasOwn(project, "maxSteps");
|
|
377
|
+
const value = hasProjectValue ? project.maxSteps : global.maxSteps;
|
|
378
|
+
if (value == null || value === 0) return void 0;
|
|
379
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
380
|
+
throw new ConfigError(t("maxSteps must be a positive number, or 0/null to disable the limit."));
|
|
381
|
+
}
|
|
382
|
+
return Math.max(1, Math.floor(value));
|
|
383
|
+
}
|
|
364
384
|
function untrustedProviders(project, known) {
|
|
365
385
|
const out = {};
|
|
366
386
|
for (const [id, provider] of Object.entries(project.providers ?? {})) {
|
|
@@ -378,6 +398,7 @@ function loadConfig(cwd, env = process.env) {
|
|
|
378
398
|
model: project.model ?? global.model ?? DEFAULT_CONFIG.model,
|
|
379
399
|
permissionMode: global.permissionMode ?? DEFAULT_CONFIG.permissionMode,
|
|
380
400
|
reasoning: global.reasoning ?? DEFAULT_CONFIG.reasoning,
|
|
401
|
+
maxSteps: resolvedMaxSteps(project, global),
|
|
381
402
|
providers: { ...known, ...fromProject },
|
|
382
403
|
projectProviders: Object.keys(fromProject),
|
|
383
404
|
allow: [...global.allow ?? []]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
t
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-CZMTWXCE.js";
|
|
5
5
|
|
|
6
6
|
// src/print.ts
|
|
7
7
|
function toolLabel(tool) {
|
|
@@ -73,14 +73,12 @@ var BUILTIN_AGENTS = [
|
|
|
73
73
|
name: "explore",
|
|
74
74
|
description: "Read-only: search and read code to answer a question (fast, safe, can run in parallel).",
|
|
75
75
|
tools: ["read", "grep", "glob"],
|
|
76
|
-
prompt: EXPLORE_PROMPT
|
|
77
|
-
maxSteps: 30
|
|
76
|
+
prompt: EXPLORE_PROMPT
|
|
78
77
|
},
|
|
79
78
|
{
|
|
80
79
|
name: "general",
|
|
81
80
|
description: "Full tools: carry out a self-contained sub-task (edits and commands still need user permission).",
|
|
82
|
-
prompt: GENERAL_PROMPT
|
|
83
|
-
maxSteps: 50
|
|
81
|
+
prompt: GENERAL_PROMPT
|
|
84
82
|
}
|
|
85
83
|
];
|
|
86
84
|
|
|
@@ -223,6 +221,11 @@ function loadAgentDefs(r) {
|
|
|
223
221
|
if (data.tools)
|
|
224
222
|
def.tools = Array.isArray(data.tools) ? data.tools : String(data.tools).split(",").map((s) => s.trim()).filter(Boolean);
|
|
225
223
|
if (data.model) def.model = str(data.model);
|
|
224
|
+
const rawMaxSteps = str(data.maxSteps ?? data["max-steps"]);
|
|
225
|
+
if (rawMaxSteps) {
|
|
226
|
+
const maxSteps = Number(rawMaxSteps);
|
|
227
|
+
if (Number.isFinite(maxSteps) && maxSteps > 0) def.maxSteps = Math.max(1, Math.floor(maxSteps));
|
|
228
|
+
}
|
|
226
229
|
byName.set(name, def);
|
|
227
230
|
}
|
|
228
231
|
}
|
|
@@ -35,8 +35,8 @@ function readSecret(prompt) {
|
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
37
|
async function runLogin(providerId, cwd) {
|
|
38
|
-
const { runProviderCommand } = await import("./providerCli-
|
|
39
|
-
const { parseCliArgs } = await import("./args-
|
|
38
|
+
const { runProviderCommand } = await import("./providerCli-WJXJW3W2.js");
|
|
39
|
+
const { parseCliArgs } = await import("./args-GJUGK4F4.js");
|
|
40
40
|
return runProviderCommand(parseCliArgs(["provider", "add", providerId]), {
|
|
41
41
|
env: process.env,
|
|
42
42
|
cwd,
|
|
@@ -4,17 +4,17 @@ import {
|
|
|
4
4
|
providerName,
|
|
5
5
|
removeGlobalProvider,
|
|
6
6
|
writeGlobalConfig
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-3DHJO7NC.js";
|
|
8
8
|
import {
|
|
9
9
|
reasoningPayload
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-3NTTXK4Y.js";
|
|
11
11
|
import {
|
|
12
12
|
PRESETS,
|
|
13
13
|
loadConfig,
|
|
14
14
|
readCredentials,
|
|
15
15
|
saveCredential,
|
|
16
16
|
t
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-CZMTWXCE.js";
|
|
18
18
|
|
|
19
19
|
// src/provider.ts
|
|
20
20
|
import OpenAI from "openai";
|
package/dist/cli.js
CHANGED
|
@@ -10,11 +10,11 @@ import {
|
|
|
10
10
|
parseFrontmatter,
|
|
11
11
|
runPrint,
|
|
12
12
|
validateGlobPattern
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-HKHKYFZI.js";
|
|
14
14
|
import {
|
|
15
15
|
helpText,
|
|
16
16
|
parseCliArgs
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-BTCJNE7R.js";
|
|
18
18
|
import {
|
|
19
19
|
Permissions
|
|
20
20
|
} from "./chunk-ELML4KVQ.js";
|
|
@@ -24,15 +24,15 @@ import {
|
|
|
24
24
|
createProvider,
|
|
25
25
|
hasPendingProgrammaticReplay,
|
|
26
26
|
listAllModels
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-QKWSDZJQ.js";
|
|
28
28
|
import {
|
|
29
29
|
providerName,
|
|
30
30
|
readGlobalConfigFile
|
|
31
|
-
} from "./chunk-
|
|
32
|
-
import "./chunk-
|
|
31
|
+
} from "./chunk-3DHJO7NC.js";
|
|
32
|
+
import "./chunk-3NTTXK4Y.js";
|
|
33
33
|
import {
|
|
34
34
|
runLogin
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-NSCC5VMS.js";
|
|
36
36
|
import {
|
|
37
37
|
Session,
|
|
38
38
|
pruneSessions
|
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
resolveModel,
|
|
46
46
|
setLanguage,
|
|
47
47
|
t
|
|
48
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-CZMTWXCE.js";
|
|
49
49
|
|
|
50
50
|
// src/textToolCalls.ts
|
|
51
51
|
var PAIR = /(?:<arg_key>\s*|`)?([A-Za-z_]\w*)(?:\s*<\/arg_key>)?`?\s*(?:=\s*`|<arg_value>)([\s\S]*?)<\/arg_value>/g;
|
|
@@ -935,7 +935,7 @@ var Agent = class {
|
|
|
935
935
|
this.turnTools = this.tools;
|
|
936
936
|
this.turnDefinitions = this.definitions;
|
|
937
937
|
this.messages = [...opts.history ?? []];
|
|
938
|
-
this.maxSteps = opts.maxSteps
|
|
938
|
+
this.maxSteps = typeof opts.maxSteps === "number" && Number.isFinite(opts.maxSteps) && opts.maxSteps > 0 ? Math.max(1, Math.floor(opts.maxSteps)) : void 0;
|
|
939
939
|
this.contextWindow = opts.contextWindow ?? 128e3;
|
|
940
940
|
this.systemPrompt = opts.systemPrompt;
|
|
941
941
|
this.reasoning = opts.reasoning ?? "auto";
|
|
@@ -1028,7 +1028,7 @@ ${completion.text}` });
|
|
|
1028
1028
|
try {
|
|
1029
1029
|
if (this.lastInputTokens > this.contextWindow * 0.8 && !hasPendingProgrammaticReplay(this.messages)) await this.compact(signal);
|
|
1030
1030
|
this.push({ role: "user", content: text });
|
|
1031
|
-
for (let step = 0; step < this.maxSteps; step++) {
|
|
1031
|
+
for (let step = 0; this.maxSteps === void 0 || step < this.maxSteps; step++) {
|
|
1032
1032
|
if (step > 0 && this.lastInputTokens > this.contextWindow * 0.8 && !hasPendingProgrammaticReplay(this.messages)) {
|
|
1033
1033
|
await this.compact(signal);
|
|
1034
1034
|
this.push({ role: "user", content: `Continue this task using the summary above: ${text}` });
|
|
@@ -1115,7 +1115,7 @@ ${completion.text}` });
|
|
|
1115
1115
|
return;
|
|
1116
1116
|
}
|
|
1117
1117
|
}
|
|
1118
|
-
this.onEvent({ type: "stepLimit" });
|
|
1118
|
+
if (this.maxSteps !== void 0) this.onEvent({ type: "stepLimit", maxSteps: this.maxSteps });
|
|
1119
1119
|
} catch (error) {
|
|
1120
1120
|
if (signal.aborted) {
|
|
1121
1121
|
this.onEvent({ type: "aborted" });
|
|
@@ -1536,7 +1536,7 @@ ${list}`,
|
|
|
1536
1536
|
# Your role
|
|
1537
1537
|
${def.prompt}`,
|
|
1538
1538
|
cwd: opts.cwd,
|
|
1539
|
-
maxSteps: def.maxSteps
|
|
1539
|
+
maxSteps: def.maxSteps,
|
|
1540
1540
|
label: def.name,
|
|
1541
1541
|
reasoning: opts.reasoning?.() ?? "auto",
|
|
1542
1542
|
// Subagent edits belong to the parent's turn so /undo reverts them too.
|
|
@@ -1560,7 +1560,7 @@ ${def.prompt}`,
|
|
|
1560
1560
|
const report = last && typeof last.content === "string" ? last.content : "(no report)";
|
|
1561
1561
|
const suffix = outcome?.type === "stepLimit" ? `
|
|
1562
1562
|
|
|
1563
|
-
(The subagent stopped at its ${
|
|
1563
|
+
(The subagent stopped at its ${outcome.maxSteps}-step limit.)` : "";
|
|
1564
1564
|
return { output: report + suffix, display };
|
|
1565
1565
|
}
|
|
1566
1566
|
});
|
|
@@ -1674,6 +1674,7 @@ function createRuntime(opts) {
|
|
|
1674
1674
|
cwd: opts.cwd,
|
|
1675
1675
|
history,
|
|
1676
1676
|
reasoning,
|
|
1677
|
+
maxSteps: config.maxSteps,
|
|
1677
1678
|
onMessage: (m) => session.append(m),
|
|
1678
1679
|
onReset: () => session.reset(),
|
|
1679
1680
|
onTurnStart: () => checkpoints.beginTurn(),
|
|
@@ -1758,7 +1759,7 @@ function createRuntime(opts) {
|
|
|
1758
1759
|
}
|
|
1759
1760
|
|
|
1760
1761
|
// src/version.ts
|
|
1761
|
-
var VERSION = true ? "0.4.0-beta.
|
|
1762
|
+
var VERSION = true ? "0.4.0-beta.36" : "0.0.0-dev";
|
|
1762
1763
|
|
|
1763
1764
|
// src/cli.ts
|
|
1764
1765
|
function chooseLanguage(argv) {
|
|
@@ -1783,17 +1784,17 @@ async function main() {
|
|
|
1783
1784
|
const cwd = process.cwd();
|
|
1784
1785
|
if (args.command === "login") return runLogin(args.loginProvider ?? "bc-cloud", cwd);
|
|
1785
1786
|
if (args.command === "mcp") {
|
|
1786
|
-
const { runMcpCommand } = await import("./mcpCli-
|
|
1787
|
-
const { readSecret } = await import("./login-
|
|
1787
|
+
const { runMcpCommand } = await import("./mcpCli-THHBIX7I.js");
|
|
1788
|
+
const { readSecret } = await import("./login-LKAO5PVR.js");
|
|
1788
1789
|
return runMcpCommand(args, { env: process.env, cwd, out: (s) => console.log(s), err: (s) => console.error(s), readSecret });
|
|
1789
1790
|
}
|
|
1790
1791
|
if (args.command === "provider") {
|
|
1791
|
-
const { runProviderCommand } = await import("./providerCli-
|
|
1792
|
-
const { readSecret } = await import("./login-
|
|
1792
|
+
const { runProviderCommand } = await import("./providerCli-WJXJW3W2.js");
|
|
1793
|
+
const { readSecret } = await import("./login-LKAO5PVR.js");
|
|
1793
1794
|
return runProviderCommand(args, { env: process.env, cwd, out: (s) => console.log(s), err: (s) => console.error(s), readSecret });
|
|
1794
1795
|
}
|
|
1795
1796
|
if (args.command === "connect" || args.command === "disconnect" || args.command === "integrations") {
|
|
1796
|
-
const { runIntegrationCommand } = await import("./integrationCli-
|
|
1797
|
+
const { runIntegrationCommand } = await import("./integrationCli-JWLY6E7E.js");
|
|
1797
1798
|
return runIntegrationCommand(args, {
|
|
1798
1799
|
env: process.env,
|
|
1799
1800
|
cwd,
|
|
@@ -1816,7 +1817,7 @@ async function main() {
|
|
|
1816
1817
|
}
|
|
1817
1818
|
if (args.print) {
|
|
1818
1819
|
if (!args.prompt) throw new ConfigError(t('-p mode needs a task, e.g. bccli -p "explain this repo"'));
|
|
1819
|
-
const { serversToStart } = await import("./config-
|
|
1820
|
+
const { serversToStart } = await import("./config-53MC3AH2.js");
|
|
1820
1821
|
const plan = serversToStart(rt.home, cwd);
|
|
1821
1822
|
for (const s of plan.needTrust) {
|
|
1822
1823
|
console.error(t('Project MCP server "{name}" skipped (not approved yet; run interactive bccli once to approve it).', { name: s.name }));
|
|
@@ -1829,7 +1830,7 @@ async function main() {
|
|
|
1829
1830
|
}
|
|
1830
1831
|
}
|
|
1831
1832
|
if (!process.stdin.isTTY) throw new ConfigError(t('Interactive mode needs a terminal. For scripts/CI use: bccli -p "task"'));
|
|
1832
|
-
const { startInteractive } = await import("./ui-
|
|
1833
|
+
const { startInteractive } = await import("./ui-I5BTZATT.js");
|
|
1833
1834
|
return startInteractive(rt, { initialPrompt: args.prompt, resume: args.resume, version: VERSION });
|
|
1834
1835
|
}
|
|
1835
1836
|
main().then(
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
readCredentials,
|
|
7
7
|
resolveModel,
|
|
8
8
|
t
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-CZMTWXCE.js";
|
|
10
10
|
|
|
11
11
|
// src/integrationCli.ts
|
|
12
12
|
import { chmodSync, copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
@@ -23,11 +23,15 @@ function statePath(target, env) {
|
|
|
23
23
|
function keyPath(env) {
|
|
24
24
|
return join(integrationHome(env), "bc-cloud.key");
|
|
25
25
|
}
|
|
26
|
+
var OPENCLAW_ENV_ID = "BOTCONNECTOR_API_KEY";
|
|
27
|
+
var HERMES_ENV_ID = "BOTCONNECTOR_API_KEY";
|
|
28
|
+
var HERMES_PROVIDER_ID = "botconnector-cloud";
|
|
29
|
+
var FALLBACK_DEFAULT_MODEL = "glm-5.3-flash";
|
|
26
30
|
function parseTarget(raw) {
|
|
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;
|
|
31
|
+
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" || raw === "openclaw" || raw === "hermes") return raw;
|
|
28
32
|
if (raw === "dsh") return "deepseek-harness";
|
|
29
33
|
throw new ConfigError(
|
|
30
|
-
t("Agent must be one of: opencode, aider, cline, deepseek-harness (dsh), cursor, openai-cli, openai-sdk, openai-compatible, codex, claude-code")
|
|
34
|
+
t("Agent must be one of: opencode, aider, cline, deepseek-harness (dsh), cursor, openai-cli, openai-sdk, openai-compatible, codex, claude-code, openclaw, hermes")
|
|
31
35
|
);
|
|
32
36
|
}
|
|
33
37
|
function homeDir(env) {
|
|
@@ -83,7 +87,9 @@ async function botConnectorModels(deps) {
|
|
|
83
87
|
});
|
|
84
88
|
if (!response.ok) throw new ConfigError(t("Could not load the BotConnector model catalog (HTTP {status}).", { status: response.status }));
|
|
85
89
|
const payload = await response.json();
|
|
86
|
-
|
|
90
|
+
const models = (payload.data ?? []).map((item) => item.id).filter((id) => typeof id === "string" && !!id);
|
|
91
|
+
if (!models.length) throw new ConfigError(t("The BotConnector model catalog is empty; cannot configure external agents."));
|
|
92
|
+
return models;
|
|
87
93
|
}
|
|
88
94
|
async function connectOpenCode(deps) {
|
|
89
95
|
const home = homeDir(deps.env);
|
|
@@ -135,6 +141,15 @@ function stripManagedBlock(input) {
|
|
|
135
141
|
return right ? `${left}
|
|
136
142
|
${right}` : left;
|
|
137
143
|
}
|
|
144
|
+
function externalAgentDefaultModel(models, deps) {
|
|
145
|
+
try {
|
|
146
|
+
const config = loadConfig(deps.cwd, deps.env);
|
|
147
|
+
const preferred = config.model.startsWith("bc-cloud/") ? config.model.slice("bc-cloud/".length) : "";
|
|
148
|
+
if (preferred && models.includes(preferred)) return preferred;
|
|
149
|
+
} catch {
|
|
150
|
+
}
|
|
151
|
+
return models.includes(FALLBACK_DEFAULT_MODEL) ? FALLBACK_DEFAULT_MODEL : models[0];
|
|
152
|
+
}
|
|
138
153
|
function yamlQuote(value) {
|
|
139
154
|
return JSON.stringify(value.replace(/\\/g, "/"));
|
|
140
155
|
}
|
|
@@ -537,6 +552,86 @@ async function connectClaudeCode(deps) {
|
|
|
537
552
|
deps.out(`Launcher: ${launcher}`);
|
|
538
553
|
deps.out(`Default coding model: ${model}`);
|
|
539
554
|
}
|
|
555
|
+
async function connectOpenClaw(deps) {
|
|
556
|
+
const home = homeDir(deps.env);
|
|
557
|
+
const clawHome = deps.env.OPENCLAW_CONFIG_DIR || join(home, ".openclaw");
|
|
558
|
+
const path = join(clawHome, "openclaw.json");
|
|
559
|
+
const backup = safeBackup(path, deps.env);
|
|
560
|
+
ensureSecret(deps.env);
|
|
561
|
+
const models = await botConnectorModels(deps);
|
|
562
|
+
const model = externalAgentDefaultModel(models, deps);
|
|
563
|
+
let doc = {};
|
|
564
|
+
if (existsSync(path)) {
|
|
565
|
+
try {
|
|
566
|
+
doc = JSON.parse(readFileSync(path, "utf8"));
|
|
567
|
+
} catch {
|
|
568
|
+
throw new ConfigError(t("The OpenClaw config is not valid JSON: {path}", { path }));
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
const modelsRoot = doc.models && typeof doc.models === "object" && !Array.isArray(doc.models) ? { ...doc.models } : {};
|
|
572
|
+
const providers = modelsRoot.providers && typeof modelsRoot.providers === "object" && !Array.isArray(modelsRoot.providers) ? { ...modelsRoot.providers } : {};
|
|
573
|
+
const existing = providers[PROVIDER_ID];
|
|
574
|
+
if (existing?.baseUrl && existing.baseUrl.replace(/\/+$/, "") !== BASE_URL) {
|
|
575
|
+
throw new ConfigError(t('OpenClaw already has a provider "{id}" with a different endpoint ({base}). BCCLI will not overwrite it; disconnect or change that provider first.', { id: PROVIDER_ID, base: existing.baseUrl }));
|
|
576
|
+
}
|
|
577
|
+
const secretRef = { source: "env", provider: PROVIDER_ID, id: OPENCLAW_ENV_ID };
|
|
578
|
+
providers[PROVIDER_ID] = {
|
|
579
|
+
baseUrl: BASE_URL,
|
|
580
|
+
api: "openai-completions",
|
|
581
|
+
apiKey: secretRef,
|
|
582
|
+
// Live catalog ids only; OpenClaw resolves provider capabilities itself.
|
|
583
|
+
models: models.map((id) => ({ id, name: id }))
|
|
584
|
+
};
|
|
585
|
+
modelsRoot.providers = providers;
|
|
586
|
+
doc.models = modelsRoot;
|
|
587
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
588
|
+
writeFileSync(path, `${JSON.stringify(doc, null, 2)}
|
|
589
|
+
`);
|
|
590
|
+
writeState("openclaw", {
|
|
591
|
+
target: "openclaw",
|
|
592
|
+
files: [{ path, ...backup }],
|
|
593
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
594
|
+
}, deps.env);
|
|
595
|
+
deps.out(t("OpenClaw is connected to BotConnector ({n} models; default {model}).", { n: models.length, model }));
|
|
596
|
+
deps.out(`Config: ${path}`);
|
|
597
|
+
deps.out(t("API key: env {env} (SecretRef, not stored in the config).", { env: OPENCLAW_ENV_ID }));
|
|
598
|
+
deps.out(t("Set the default model in OpenClaw with: openclaw models set {provider}/{model}", { provider: PROVIDER_ID, model }));
|
|
599
|
+
}
|
|
600
|
+
async function connectHermes(deps) {
|
|
601
|
+
const home = homeDir(deps.env);
|
|
602
|
+
const hermesHome = deps.env.HERMES_HOME || join(home, ".hermes");
|
|
603
|
+
const path = join(hermesHome, "config.yaml");
|
|
604
|
+
const backup = safeBackup(path, deps.env);
|
|
605
|
+
ensureSecret(deps.env);
|
|
606
|
+
const models = await botConnectorModels(deps);
|
|
607
|
+
const model = externalAgentDefaultModel(models, deps);
|
|
608
|
+
const source = existsSync(path) ? readFileSync(path, "utf8") : "";
|
|
609
|
+
const doc = parseDocument(source);
|
|
610
|
+
if (doc.errors.length) throw new ConfigError(t("The Hermes config is not valid YAML: {path}", { path }));
|
|
611
|
+
const previousProvider = doc.getIn(["providers", HERMES_PROVIDER_ID]);
|
|
612
|
+
if (previousProvider?.api && previousProvider.api.replace(/\/+$/, "") !== BASE_URL) {
|
|
613
|
+
throw new ConfigError(t('Hermes already has a provider "{id}" with a different endpoint ({base}). BCCLI will not overwrite it; disconnect or change that provider first.', { id: HERMES_PROVIDER_ID, base: previousProvider.api }));
|
|
614
|
+
}
|
|
615
|
+
doc.setIn(["providers", HERMES_PROVIDER_ID], {
|
|
616
|
+
api: BASE_URL,
|
|
617
|
+
key_env: HERMES_ENV_ID,
|
|
618
|
+
transport: "chat_completions",
|
|
619
|
+
default_model: model,
|
|
620
|
+
discover_models: true,
|
|
621
|
+
models: Object.fromEntries(models.map((id) => [id, {}]))
|
|
622
|
+
});
|
|
623
|
+
doc.setIn(["model"], { default: model, provider: `custom:${HERMES_PROVIDER_ID}` });
|
|
624
|
+
mkdirSync(hermesHome, { recursive: true });
|
|
625
|
+
writeFileSync(path, doc.toString());
|
|
626
|
+
writeState("hermes", {
|
|
627
|
+
target: "hermes",
|
|
628
|
+
files: [{ path, ...backup }],
|
|
629
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
630
|
+
}, deps.env);
|
|
631
|
+
deps.out(t("Hermes is connected to BotConnector ({n} models; default {model}).", { n: models.length, model }));
|
|
632
|
+
deps.out(`Config: ${path}`);
|
|
633
|
+
deps.out(t("API key: env {env} only (via key_env; a plaintext key in config.yaml is never written).", { env: HERMES_ENV_ID }));
|
|
634
|
+
}
|
|
540
635
|
function disconnect(target, deps) {
|
|
541
636
|
const state = readState(target, deps.env);
|
|
542
637
|
if (!state) {
|
|
@@ -555,7 +650,7 @@ function disconnect(target, deps) {
|
|
|
555
650
|
deps.out(t("{target}: the BotConnector integration was removed and the config restored.", { target }));
|
|
556
651
|
}
|
|
557
652
|
function listIntegrations(deps) {
|
|
558
|
-
for (const target of ["opencode", "aider", "cline", "deepseek-harness", "cursor", "openai-cli", "openai-sdk", "openai-compatible", "codex", "claude-code"]) {
|
|
653
|
+
for (const target of ["opencode", "aider", "cline", "deepseek-harness", "cursor", "openai-cli", "openai-sdk", "openai-compatible", "codex", "claude-code", "openclaw", "hermes"]) {
|
|
559
654
|
deps.out(`${target} ${readState(target, deps.env) ? "connected" : "not connected"}`);
|
|
560
655
|
}
|
|
561
656
|
}
|
|
@@ -578,6 +673,8 @@ async function runIntegrationCommand(args, deps) {
|
|
|
578
673
|
else if (target === "openai-sdk") await connectOpenAiSdk(deps);
|
|
579
674
|
else if (target === "openai-compatible") await connectOpenAiCompatible(deps);
|
|
580
675
|
else if (target === "codex") await connectCodex(deps);
|
|
676
|
+
else if (target === "openclaw") await connectOpenClaw(deps);
|
|
677
|
+
else if (target === "hermes") await connectHermes(deps);
|
|
581
678
|
else await connectClaudeCode(deps);
|
|
582
679
|
return 0;
|
|
583
680
|
}
|
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
import {
|
|
3
3
|
CATALOG,
|
|
4
4
|
fillTemplate
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-5LSTEXSG.js";
|
|
6
6
|
import {
|
|
7
7
|
addGlobalServer,
|
|
8
8
|
globalMcpPath,
|
|
9
9
|
readMcpFile,
|
|
10
10
|
removeGlobalServer
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-SZRM2VOB.js";
|
|
12
12
|
import {
|
|
13
13
|
bccliHome,
|
|
14
14
|
t
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-CZMTWXCE.js";
|
|
16
16
|
|
|
17
17
|
// src/mcpCli.ts
|
|
18
18
|
async function runMcpCommand(args, deps) {
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
parseFrontmatter,
|
|
8
8
|
toolLabel,
|
|
9
9
|
validateGlobPattern
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-HKHKYFZI.js";
|
|
11
11
|
import {
|
|
12
12
|
nextMode
|
|
13
13
|
} from "./chunk-ELML4KVQ.js";
|
|
@@ -15,26 +15,26 @@ import {
|
|
|
15
15
|
hasKey,
|
|
16
16
|
providerName,
|
|
17
17
|
writeGlobalConfig
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-3DHJO7NC.js";
|
|
19
19
|
import {
|
|
20
20
|
REASONING_LEVELS,
|
|
21
21
|
assertReasoningSupported,
|
|
22
22
|
parseReasoningLevel,
|
|
23
23
|
supportedReasoningLevels
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-3NTTXK4Y.js";
|
|
25
25
|
import {
|
|
26
26
|
Session
|
|
27
27
|
} from "./chunk-SJH3YLW4.js";
|
|
28
28
|
import {
|
|
29
29
|
CATALOG,
|
|
30
30
|
fillTemplate
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-5LSTEXSG.js";
|
|
32
32
|
import {
|
|
33
33
|
addGlobalServer,
|
|
34
34
|
globalMcpPath,
|
|
35
35
|
readMcpFile,
|
|
36
36
|
removeGlobalServer
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-SZRM2VOB.js";
|
|
38
38
|
import {
|
|
39
39
|
PRESETS,
|
|
40
40
|
getLanguage,
|
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
removeCredential,
|
|
44
44
|
setLanguage,
|
|
45
45
|
t
|
|
46
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-CZMTWXCE.js";
|
|
47
47
|
|
|
48
48
|
// src/ui/index.tsx
|
|
49
49
|
import { render } from "ink";
|
|
@@ -1180,7 +1180,7 @@ var nextId = 0;
|
|
|
1180
1180
|
var entry = (e) => ({ ...e, id: nextId++ });
|
|
1181
1181
|
var NOTICES = {
|
|
1182
1182
|
compacted: ["Conversation summarized to fit the model context.", "info"],
|
|
1183
|
-
stepLimit: ['Step limit of
|
|
1183
|
+
stepLimit: ['Step limit of {n} reached. Type "continue" to keep going.', "warn"],
|
|
1184
1184
|
aborted: ["Cancelled.", "warn"]
|
|
1185
1185
|
};
|
|
1186
1186
|
var isEmptyCont = (e) => e.kind === "assistant" && e.cont && !e.text;
|
|
@@ -1255,6 +1255,11 @@ function applyEvent(t2, event) {
|
|
|
1255
1255
|
}
|
|
1256
1256
|
case "error":
|
|
1257
1257
|
return { ...t2, live: [...t2.live, entry({ kind: "notice", text: `Error: ${event.message}`, tone: "error" })] };
|
|
1258
|
+
case "stepLimit": {
|
|
1259
|
+
const limit = event.maxSteps;
|
|
1260
|
+
const notice = NOTICES.stepLimit;
|
|
1261
|
+
return notice ? { ...t2, live: [...t2.live, entry({ kind: "notice", text: t(notice[0], { n: limit }), tone: notice[1] })] } : t2;
|
|
1262
|
+
}
|
|
1258
1263
|
default: {
|
|
1259
1264
|
const notice = NOTICES[event.type];
|
|
1260
1265
|
return notice ? { ...t2, live: [...t2.live, entry({ kind: "notice", text: t(notice[0]), tone: notice[1] })] } : t2;
|
|
@@ -1964,7 +1969,7 @@ async function startInteractive(rt, opts) {
|
|
|
1964
1969
|
if (chosen) rt.resume(chosen.session);
|
|
1965
1970
|
}
|
|
1966
1971
|
}
|
|
1967
|
-
const { serversToStart, setProjectTrust } = await import("./config-
|
|
1972
|
+
const { serversToStart, setProjectTrust } = await import("./config-53MC3AH2.js");
|
|
1968
1973
|
const plan = serversToStart(rt.home, rt.cwd);
|
|
1969
1974
|
if (plan.needTrust.length) {
|
|
1970
1975
|
const { createInterface } = await import("readline/promises");
|
package/package.json
CHANGED