@botconnector/bccli 0.4.0-beta.7 → 0.4.0-beta.9
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 +25 -1
- package/dist/{args-FCLPQ7TO.js → args-JPVSGCGY.js} +3 -2
- package/dist/{chunk-RZUJYAQV.js → chunk-2MGINW4O.js} +2 -0
- package/dist/{chunk-U7DLMVPZ.js → chunk-7S6JXQCH.js} +1 -1
- package/dist/{chunk-NGLKANMD.js → chunk-BFZXRKW5.js} +1 -1
- package/dist/chunk-CLN4Z5E3.js +60 -0
- package/dist/{chunk-UESQNU4A.js → chunk-DX7CZTBB.js} +1 -1
- package/dist/{chunk-BRTSYM5O.js → chunk-ISGBGFSU.js} +2 -2
- package/dist/{chunk-FMM53YSU.js → chunk-U7GK73CK.js} +14 -5
- package/dist/{chunk-UN7QXPCB.js → chunk-ZYCYTEKD.js} +17 -3
- package/dist/cli.js +51 -25
- package/dist/{config-NPSYFAFD.js → config-YUNKPCNB.js} +2 -2
- package/dist/integrationCli-BGT5UZU4.js +223 -0
- package/dist/{login-4277DGKX.js → login-6K32PIXV.js} +1 -1
- package/dist/{mcpCli-Q2H63ISP.js → mcpCli-DZWQPTOS.js} +3 -3
- package/dist/providerCli-Q7LRM2NP.js +12 -0
- package/dist/{ui-4EBFPASA.js → ui-6GTQUJOE.js} +53 -10
- package/package.json +1 -1
- package/dist/providerCli-6ON7QEVN.js +0 -11
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Agent AI coding dari BotConnector yang bekerja di terminal: membaca kode, menged
|
|
|
4
4
|
dan menjalankan perintah — dengan izin kamu.
|
|
5
5
|
|
|
6
6
|
```bash
|
|
7
|
-
npm i -g @botconnector/bccli@next #
|
|
7
|
+
npm i -g @botconnector/bccli@next # beta terbaru, butuh Node.js 22+
|
|
8
8
|
bccli login # simpan API key BotConnector Cloud
|
|
9
9
|
cd project-kamu && bccli
|
|
10
10
|
```
|
|
@@ -17,6 +17,7 @@ cd project-kamu && bccli
|
|
|
17
17
|
| `bccli -p "tugas"` | satu tugas tanpa interaksi (skrip/CI) |
|
|
18
18
|
| `bccli -c` / `bccli -r` | lanjutkan sesi terakhir / pilih sesi |
|
|
19
19
|
| `bccli -m provider/model` | pilih model |
|
|
20
|
+
| `bccli --reasoning <auto|off|low|medium|high|max>` | pilih level reasoning untuk invocation ini |
|
|
20
21
|
| `bccli models` | daftar model |
|
|
21
22
|
|
|
22
23
|
Perintah di dalam sesi (ketik `/` untuk saran, ↑↓ pilih, Enter jalankan):
|
|
@@ -25,6 +26,7 @@ Perintah di dalam sesi (ketik `/` untuk saran, ↑↓ pilih, Enter jalankan):
|
|
|
25
26
|
|---|---|
|
|
26
27
|
| `/help` | daftar perintah dan pintasan |
|
|
27
28
|
| `/model` | ganti model |
|
|
29
|
+
| `/reasoning` | pilih Auto / Off / Low / Medium / High / Max |
|
|
28
30
|
| `/provider` | tambah/pilih provider AI |
|
|
29
31
|
| `/mcp` | pasang/kelola server MCP |
|
|
30
32
|
| `/new` | sesi baru (sesi lama tetap tersimpan) |
|
|
@@ -76,10 +78,32 @@ Provider lain yang OpenAI-compatible: pilih **Custom…** di `/provider`, atau
|
|
|
76
78
|
`/model` menampilkan model dari **semua** provider yang sudah punya key, dikelompokkan
|
|
77
79
|
per provider; ketik untuk menyaring. Pilihan tersimpan sebagai default.
|
|
78
80
|
|
|
81
|
+
Reasoning dapat dipilih lewat `bccli --reasoning <auto|off|low|medium|high|max>` atau `/reasoning`.
|
|
82
|
+
**Auto** memakai default model/provider dan BCCLI tidak mengirim override reasoning apa pun. BCCLI tidak
|
|
83
|
+
otomatis menurunkan atau menaikkan reasoning effort. Level manual diteruskan bila kontrak provider/model
|
|
84
|
+
mendukungnya; level yang tidak didukung menghasilkan error yang jelas dan tidak di-downgrade diam-diam.
|
|
85
|
+
|
|
79
86
|
`.bccli/config.json` di dalam project hanya boleh memilih `model` dan menambah provider baru tanpa API key —
|
|
87
|
+
preference `reasoning` hanya dibaca dari config user `~/.bccli/config.json`, bukan dari repo yang di-clone —
|
|
80
88
|
izin (`permissionMode`, `allow`) dan provider yang sudah ada hanya diatur dari `~/.bccli/config.json`,
|
|
81
89
|
supaya repo yang kamu clone tidak bisa memberi dirinya izin penuh atau mencuri API key.
|
|
82
90
|
|
|
91
|
+
## Integrasi agent eksternal
|
|
92
|
+
|
|
93
|
+
BCCLI dapat memasang BotConnector sebagai provider untuk agent lain:
|
|
94
|
+
|
|
95
|
+
| Perintah | Fungsi |
|
|
96
|
+
|---|---|
|
|
97
|
+
| `bccli integrations` | lihat status integrasi |
|
|
98
|
+
| `bccli connect opencode` | hubungkan OpenCode ke BotConnector |
|
|
99
|
+
| `bccli connect aider` | hubungkan Aider ke BotConnector |
|
|
100
|
+
| `bccli disconnect <agent>` | pulihkan config sebelum integrasi |
|
|
101
|
+
|
|
102
|
+
OpenCode memakai config global `~/.config/opencode/opencode.json`. BCCLI mengambil katalog model BotConnector saat connect,
|
|
103
|
+
menjaga config provider lain, dan menyimpan secret di file terpisah di `~/.bccli/integrations/`.
|
|
104
|
+
Aider memakai `~/.aider.conf.yml` yang menunjuk ke env file BCCLI terpisah; BCCLI menolak menimpa setting
|
|
105
|
+
OpenAI/env-file Aider yang sudah ada. Sebelum mengubah config eksternal, BCCLI membuat backup agar disconnect dapat memulihkannya.
|
|
106
|
+
|
|
83
107
|
## Internet
|
|
84
108
|
|
|
85
109
|
- `web_search`: mencari di web tanpa perlu izin. Memakai akun BotConnector Cloud
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
HELP_TEXT,
|
|
4
4
|
parseCliArgs
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-ZYCYTEKD.js";
|
|
6
6
|
import "./chunk-ELML4KVQ.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-CLN4Z5E3.js";
|
|
8
|
+
import "./chunk-2MGINW4O.js";
|
|
8
9
|
export {
|
|
9
10
|
HELP_TEXT,
|
|
10
11
|
parseCliArgs
|
|
@@ -22,6 +22,7 @@ var ConfigError = class extends Error {
|
|
|
22
22
|
var DEFAULT_CONFIG = {
|
|
23
23
|
model: "bc-cloud/glm-5.3-flash",
|
|
24
24
|
permissionMode: "default",
|
|
25
|
+
reasoning: "auto",
|
|
25
26
|
providers: Object.fromEntries(PRESETS.map((p) => [p.id, { baseURL: p.baseURL, apiKeyEnv: p.apiKeyEnv }])),
|
|
26
27
|
allow: []
|
|
27
28
|
};
|
|
@@ -52,6 +53,7 @@ function loadConfig(cwd, env = process.env) {
|
|
|
52
53
|
return {
|
|
53
54
|
model: project.model ?? global.model ?? DEFAULT_CONFIG.model,
|
|
54
55
|
permissionMode: global.permissionMode ?? DEFAULT_CONFIG.permissionMode,
|
|
56
|
+
reasoning: global.reasoning ?? DEFAULT_CONFIG.reasoning,
|
|
55
57
|
providers: { ...known, ...fromProject },
|
|
56
58
|
projectProviders: Object.keys(fromProject),
|
|
57
59
|
allow: [...global.allow ?? []]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/reasoning.ts
|
|
4
|
+
var REASONING_LEVELS = ["auto", "off", "low", "medium", "high", "max"];
|
|
5
|
+
var OPENAI_LEVELS = ["auto", "low", "medium", "high"];
|
|
6
|
+
var BOTCONNECTOR_LEVELS = REASONING_LEVELS;
|
|
7
|
+
function isReasoningLevel(value) {
|
|
8
|
+
return REASONING_LEVELS.includes(value);
|
|
9
|
+
}
|
|
10
|
+
function parseReasoningLevel(value, label = "reasoning") {
|
|
11
|
+
if (!isReasoningLevel(value)) {
|
|
12
|
+
throw new Error(`${label} harus salah satu dari: ${REASONING_LEVELS.join(", ")}`);
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
function reasoningCapability(providerId) {
|
|
17
|
+
switch (providerId) {
|
|
18
|
+
case "bc-cloud":
|
|
19
|
+
return { levels: BOTCONNECTOR_LEVELS, parameter: "reasoning_effort" };
|
|
20
|
+
case "openai":
|
|
21
|
+
return { levels: OPENAI_LEVELS, parameter: "reasoning_effort" };
|
|
22
|
+
case "openrouter":
|
|
23
|
+
return { levels: ["auto", "low", "medium", "high"], parameter: "reasoning" };
|
|
24
|
+
default:
|
|
25
|
+
return void 0;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function supportedReasoningLevels(providerId) {
|
|
29
|
+
return reasoningCapability(providerId)?.levels ?? ["auto"];
|
|
30
|
+
}
|
|
31
|
+
function assertReasoningSupported(providerId, level) {
|
|
32
|
+
if (level === "auto") return;
|
|
33
|
+
const capability = reasoningCapability(providerId);
|
|
34
|
+
if (!capability) {
|
|
35
|
+
throw new Error("Provider ini belum memiliki kontrak reasoning manual. Gunakan Auto.");
|
|
36
|
+
}
|
|
37
|
+
if (!capability.levels.includes(level)) {
|
|
38
|
+
const supported = capability.levels.filter((x) => x !== "auto").map(capitalize).join("/");
|
|
39
|
+
throw new Error(`Model/provider ini tidak mendukung reasoning level '${level}'. Gunakan Auto${supported ? `/${supported}` : ""}.`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function reasoningPayload(providerId, level) {
|
|
43
|
+
if (level === "auto") return {};
|
|
44
|
+
assertReasoningSupported(providerId, level);
|
|
45
|
+
const capability = reasoningCapability(providerId);
|
|
46
|
+
if (capability.parameter === "reasoning") return { reasoning: { effort: level } };
|
|
47
|
+
return { reasoning_effort: level };
|
|
48
|
+
}
|
|
49
|
+
function capitalize(value) {
|
|
50
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export {
|
|
54
|
+
REASONING_LEVELS,
|
|
55
|
+
isReasoningLevel,
|
|
56
|
+
parseReasoningLevel,
|
|
57
|
+
supportedReasoningLevels,
|
|
58
|
+
assertReasoningSupported,
|
|
59
|
+
reasoningPayload
|
|
60
|
+
};
|
|
@@ -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-Q7LRM2NP.js");
|
|
39
|
+
const { parseCliArgs } = await import("./args-JPVSGCGY.js");
|
|
40
40
|
return runProviderCommand(parseCliArgs(["provider", "add", providerId]), {
|
|
41
41
|
env: process.env,
|
|
42
42
|
cwd,
|
|
@@ -4,13 +4,16 @@ import {
|
|
|
4
4
|
providerName,
|
|
5
5
|
removeGlobalProvider,
|
|
6
6
|
writeGlobalConfig
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-7S6JXQCH.js";
|
|
8
|
+
import {
|
|
9
|
+
reasoningPayload
|
|
10
|
+
} from "./chunk-CLN4Z5E3.js";
|
|
8
11
|
import {
|
|
9
12
|
PRESETS,
|
|
10
13
|
loadConfig,
|
|
11
14
|
readCredentials,
|
|
12
15
|
saveCredential
|
|
13
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-2MGINW4O.js";
|
|
14
17
|
|
|
15
18
|
// src/thinking.ts
|
|
16
19
|
var OPEN = "<think>";
|
|
@@ -188,7 +191,7 @@ async function readJson(res) {
|
|
|
188
191
|
const thinking = (choice?.message?.reasoning_content || choice?.message?.reasoning || "") + split.thinking;
|
|
189
192
|
return { text: split.text, toolCalls, thinking: thinking || void 0, usage: toUsage(body.usage), finishReason: choice?.finish_reason };
|
|
190
193
|
}
|
|
191
|
-
function createProvider({ baseURL, apiKey, model, fetch: doFetch = fetch, retryDelayMs = 1e3 }) {
|
|
194
|
+
function createProvider({ baseURL, apiKey, model, providerId, fetch: doFetch = fetch, retryDelayMs = 1e3 }) {
|
|
192
195
|
const headers = { "content-type": "application/json" };
|
|
193
196
|
if (apiKey) headers.authorization = `Bearer ${apiKey}`;
|
|
194
197
|
if (/^https:\/\/api\.botconnector\.id\/v1\/?$/.test(baseURL) && apiKey?.startsWith("bc_live_")) {
|
|
@@ -215,10 +218,16 @@ function createProvider({ baseURL, apiKey, model, fetch: doFetch = fetch, retryD
|
|
|
215
218
|
throw lastError ?? new ProviderError("Permintaan gagal");
|
|
216
219
|
}
|
|
217
220
|
return {
|
|
218
|
-
async chat({ messages, tools, signal, onText, onThinking }) {
|
|
221
|
+
async chat({ messages, tools, signal, onText, onThinking, reasoning = "auto" }) {
|
|
222
|
+
let reasoningOverride;
|
|
223
|
+
try {
|
|
224
|
+
reasoningOverride = reasoningPayload(providerId, reasoning);
|
|
225
|
+
} catch (error) {
|
|
226
|
+
throw new ProviderError(error.message);
|
|
227
|
+
}
|
|
219
228
|
const res = await post(
|
|
220
229
|
"/chat/completions",
|
|
221
|
-
{ model, messages, stream: true, stream_options: { include_usage: true }, ...tools?.length ? { tools } : {} },
|
|
230
|
+
{ model, messages, stream: true, stream_options: { include_usage: true }, ...tools?.length ? { tools } : {}, ...reasoningOverride },
|
|
222
231
|
signal
|
|
223
232
|
);
|
|
224
233
|
if ((res.headers.get("content-type") ?? "").includes("application/json")) return readJson(res);
|
|
@@ -2,9 +2,13 @@
|
|
|
2
2
|
import {
|
|
3
3
|
MODE_ORDER
|
|
4
4
|
} from "./chunk-ELML4KVQ.js";
|
|
5
|
+
import {
|
|
6
|
+
REASONING_LEVELS,
|
|
7
|
+
isReasoningLevel
|
|
8
|
+
} from "./chunk-CLN4Z5E3.js";
|
|
5
9
|
import {
|
|
6
10
|
ConfigError
|
|
7
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-2MGINW4O.js";
|
|
8
12
|
|
|
9
13
|
// src/args.ts
|
|
10
14
|
import { parseArgs } from "util";
|
|
@@ -17,9 +21,13 @@ Pemakaian:
|
|
|
17
21
|
bccli models daftar model dari provider aktif
|
|
18
22
|
bccli provider list|add <id>|remove <id> kelola provider (custom: --url <url> [--name N] [--key-env ENV])
|
|
19
23
|
bccli mcp list|add <nama>|remove <nama> kelola server MCP (katalog, atau --url <url>)
|
|
24
|
+
bccli integrations lihat status integrasi agent eksternal
|
|
25
|
+
bccli connect <agent> hubungkan BotConnector ke agent (opencode, aider)
|
|
26
|
+
bccli disconnect <agent> lepas integrasi dan pulihkan config sebelumnya
|
|
20
27
|
|
|
21
28
|
Opsi:
|
|
22
29
|
-m, --model <provider/model> pilih model, contoh bc-cloud/glm-5.3-flash
|
|
30
|
+
--reasoning <level> auto | off | low | medium | high | max
|
|
23
31
|
-c, --continue lanjutkan sesi terakhir di folder ini
|
|
24
32
|
-r, --resume pilih sesi untuk dilanjutkan
|
|
25
33
|
--allow-all jalankan semua alat tanpa minta izin
|
|
@@ -34,6 +42,7 @@ function parseCliArgs(argv) {
|
|
|
34
42
|
options: {
|
|
35
43
|
print: { type: "boolean", short: "p" },
|
|
36
44
|
model: { type: "string", short: "m" },
|
|
45
|
+
reasoning: { type: "string" },
|
|
37
46
|
continue: { type: "boolean", short: "c" },
|
|
38
47
|
resume: { type: "boolean", short: "r" },
|
|
39
48
|
"allow-all": { type: "boolean" },
|
|
@@ -47,12 +56,16 @@ function parseCliArgs(argv) {
|
|
|
47
56
|
value: { type: "string", multiple: true }
|
|
48
57
|
}
|
|
49
58
|
});
|
|
59
|
+
const reasoning = values.reasoning;
|
|
60
|
+
if (reasoning && !isReasoningLevel(reasoning)) {
|
|
61
|
+
throw new ConfigError(`--reasoning harus salah satu dari: ${REASONING_LEVELS.join(", ")}`);
|
|
62
|
+
}
|
|
50
63
|
const mode = values["permission-mode"];
|
|
51
64
|
if (mode && !MODE_ORDER.includes(mode)) {
|
|
52
65
|
throw new ConfigError(`--permission-mode harus salah satu dari: ${MODE_ORDER.join(", ")}`);
|
|
53
66
|
}
|
|
54
67
|
const [first, ...rest] = positionals;
|
|
55
|
-
const SUBCOMMANDS = ["login", "models", "provider", "mcp"];
|
|
68
|
+
const SUBCOMMANDS = ["login", "models", "provider", "mcp", "connect", "disconnect", "integrations"];
|
|
56
69
|
const command = SUBCOMMANDS.includes(first) ? first : "run";
|
|
57
70
|
const words = command === "run" ? positionals : rest;
|
|
58
71
|
return {
|
|
@@ -60,6 +73,7 @@ function parseCliArgs(argv) {
|
|
|
60
73
|
prompt: command === "run" && words.length ? words.join(" ") : void 0,
|
|
61
74
|
print: !!values.print,
|
|
62
75
|
model: values.model,
|
|
76
|
+
reasoning,
|
|
63
77
|
continue: !!values.continue,
|
|
64
78
|
resume: !!values.resume,
|
|
65
79
|
allowAll: !!values["allow-all"],
|
|
@@ -68,7 +82,7 @@ function parseCliArgs(argv) {
|
|
|
68
82
|
help: !!values.help,
|
|
69
83
|
version: !!values.version,
|
|
70
84
|
loginProvider: command === "login" ? rest[0] ?? "bc-cloud" : void 0,
|
|
71
|
-
subArgs:
|
|
85
|
+
subArgs: ["provider", "mcp", "connect", "disconnect", "integrations"].includes(command) ? rest : [],
|
|
72
86
|
url: values.url,
|
|
73
87
|
name: values.name,
|
|
74
88
|
keyEnv: values["key-env"],
|
package/dist/cli.js
CHANGED
|
@@ -1,16 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
HELP_TEXT,
|
|
4
|
-
parseCliArgs
|
|
5
|
-
} from "./chunk-UN7QXPCB.js";
|
|
6
|
-
import {
|
|
7
|
-
addProviderKey,
|
|
8
|
-
createProvider,
|
|
9
|
-
listAllModels
|
|
10
|
-
} from "./chunk-FMM53YSU.js";
|
|
11
|
-
import {
|
|
12
|
-
runLogin
|
|
13
|
-
} from "./chunk-BRTSYM5O.js";
|
|
14
2
|
import {
|
|
15
3
|
BUILTIN_AGENTS,
|
|
16
4
|
buildSystemPrompt,
|
|
@@ -21,12 +9,25 @@ import {
|
|
|
21
9
|
parseFrontmatter,
|
|
22
10
|
runPrint
|
|
23
11
|
} from "./chunk-KV27KTOX.js";
|
|
12
|
+
import {
|
|
13
|
+
HELP_TEXT,
|
|
14
|
+
parseCliArgs
|
|
15
|
+
} from "./chunk-ZYCYTEKD.js";
|
|
24
16
|
import {
|
|
25
17
|
Permissions
|
|
26
18
|
} from "./chunk-ELML4KVQ.js";
|
|
19
|
+
import {
|
|
20
|
+
addProviderKey,
|
|
21
|
+
createProvider,
|
|
22
|
+
listAllModels
|
|
23
|
+
} from "./chunk-U7GK73CK.js";
|
|
27
24
|
import {
|
|
28
25
|
providerName
|
|
29
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-7S6JXQCH.js";
|
|
27
|
+
import "./chunk-CLN4Z5E3.js";
|
|
28
|
+
import {
|
|
29
|
+
runLogin
|
|
30
|
+
} from "./chunk-ISGBGFSU.js";
|
|
30
31
|
import {
|
|
31
32
|
Session,
|
|
32
33
|
pruneSessions
|
|
@@ -36,7 +37,7 @@ import {
|
|
|
36
37
|
bccliHome,
|
|
37
38
|
loadConfig,
|
|
38
39
|
resolveModel
|
|
39
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-2MGINW4O.js";
|
|
40
41
|
|
|
41
42
|
// src/tools/index.ts
|
|
42
43
|
import { z as z8 } from "zod";
|
|
@@ -616,6 +617,7 @@ var Agent = class {
|
|
|
616
617
|
this.maxSteps = opts.maxSteps ?? 50;
|
|
617
618
|
this.contextWindow = opts.contextWindow ?? 128e3;
|
|
618
619
|
this.systemPrompt = opts.systemPrompt;
|
|
620
|
+
this.reasoning = opts.reasoning ?? "auto";
|
|
619
621
|
}
|
|
620
622
|
opts;
|
|
621
623
|
provider;
|
|
@@ -625,6 +627,7 @@ var Agent = class {
|
|
|
625
627
|
lastInputTokens = 0;
|
|
626
628
|
onEvent = () => {
|
|
627
629
|
};
|
|
630
|
+
reasoning;
|
|
628
631
|
askPermission = async () => "no";
|
|
629
632
|
tools;
|
|
630
633
|
definitions;
|
|
@@ -674,7 +677,8 @@ var Agent = class {
|
|
|
674
677
|
messages: [{ role: "system", content: this.systemPrompt }, ...this.messages, { role: "user", content: COMPACT_PROMPT }],
|
|
675
678
|
// Some gateways reject tool_calls in history when no tools are declared.
|
|
676
679
|
tools: this.definitions,
|
|
677
|
-
signal
|
|
680
|
+
signal,
|
|
681
|
+
reasoning: this.reasoning
|
|
678
682
|
});
|
|
679
683
|
this.clear();
|
|
680
684
|
this.push({ role: "user", content: `Ringkasan percakapan sebelumnya:
|
|
@@ -699,7 +703,8 @@ ${completion.text}` });
|
|
|
699
703
|
tools: this.turnDefinitions,
|
|
700
704
|
signal,
|
|
701
705
|
onText: (delta) => this.onEvent({ type: "text", delta }),
|
|
702
|
-
onThinking: (delta) => this.onEvent({ type: "thinking", delta })
|
|
706
|
+
onThinking: (delta) => this.onEvent({ type: "thinking", delta }),
|
|
707
|
+
reasoning: this.reasoning
|
|
703
708
|
});
|
|
704
709
|
if (!completion.toolCalls.length && completion.finishReason !== "repetition") {
|
|
705
710
|
const recovered = recoverTextToolCalls(completion.text, this.turnDefinitions);
|
|
@@ -1161,6 +1166,7 @@ ${def.prompt}`,
|
|
|
1161
1166
|
cwd: opts.cwd,
|
|
1162
1167
|
maxSteps: def.maxSteps ?? 50,
|
|
1163
1168
|
label: def.name,
|
|
1169
|
+
reasoning: opts.reasoning?.() ?? "auto",
|
|
1164
1170
|
// Subagent edits belong to the parent's turn so /undo reverts them too.
|
|
1165
1171
|
checkpoint: ctx.checkpoint
|
|
1166
1172
|
});
|
|
@@ -1229,9 +1235,10 @@ function createRuntime(opts) {
|
|
|
1229
1235
|
const home = bccliHome(env);
|
|
1230
1236
|
let config = loadConfig(opts.cwd, env);
|
|
1231
1237
|
let modelRef = opts.args.model ?? config.model;
|
|
1238
|
+
let reasoning = opts.args.reasoning ?? config.reasoning;
|
|
1232
1239
|
const makeProvider = (ref) => {
|
|
1233
1240
|
const resolved = resolveModel(config, ref, env);
|
|
1234
|
-
return opts.provider ?? createProvider({ baseURL: resolved.baseURL, apiKey: resolved.apiKey, model: resolved.model });
|
|
1241
|
+
return opts.provider ?? createProvider({ baseURL: resolved.baseURL, apiKey: resolved.apiKey, model: resolved.model, providerId: resolved.providerId });
|
|
1235
1242
|
};
|
|
1236
1243
|
const provider = makeProvider(modelRef);
|
|
1237
1244
|
const mode = opts.args.allowAll ? "allowAll" : opts.args.permissionMode ?? config.permissionMode;
|
|
@@ -1274,7 +1281,8 @@ function createRuntime(opts) {
|
|
|
1274
1281
|
provider: () => agent.provider,
|
|
1275
1282
|
providerFor: makeProvider,
|
|
1276
1283
|
systemPrompt,
|
|
1277
|
-
cwd: opts.cwd
|
|
1284
|
+
cwd: opts.cwd,
|
|
1285
|
+
reasoning: () => reasoning
|
|
1278
1286
|
});
|
|
1279
1287
|
const agent = new Agent({
|
|
1280
1288
|
provider,
|
|
@@ -1283,6 +1291,7 @@ function createRuntime(opts) {
|
|
|
1283
1291
|
systemPrompt,
|
|
1284
1292
|
cwd: opts.cwd,
|
|
1285
1293
|
history,
|
|
1294
|
+
reasoning,
|
|
1286
1295
|
onMessage: (m) => session.append(m),
|
|
1287
1296
|
onReset: () => session.reset(),
|
|
1288
1297
|
onTurnStart: () => checkpoints.beginTurn(),
|
|
@@ -1324,6 +1333,9 @@ function createRuntime(opts) {
|
|
|
1324
1333
|
get modelRef() {
|
|
1325
1334
|
return modelRef;
|
|
1326
1335
|
},
|
|
1336
|
+
get reasoning() {
|
|
1337
|
+
return reasoning;
|
|
1338
|
+
},
|
|
1327
1339
|
agent,
|
|
1328
1340
|
get session() {
|
|
1329
1341
|
return session;
|
|
@@ -1353,12 +1365,16 @@ function createRuntime(opts) {
|
|
|
1353
1365
|
setModel(ref) {
|
|
1354
1366
|
agent.provider = makeProvider(ref);
|
|
1355
1367
|
modelRef = ref;
|
|
1368
|
+
},
|
|
1369
|
+
setReasoning(level) {
|
|
1370
|
+
reasoning = level;
|
|
1371
|
+
agent.reasoning = level;
|
|
1356
1372
|
}
|
|
1357
1373
|
};
|
|
1358
1374
|
}
|
|
1359
1375
|
|
|
1360
1376
|
// src/version.ts
|
|
1361
|
-
var VERSION = true ? "0.4.0-beta.
|
|
1377
|
+
var VERSION = true ? "0.4.0-beta.9" : "0.0.0-dev";
|
|
1362
1378
|
|
|
1363
1379
|
// src/cli.ts
|
|
1364
1380
|
async function main() {
|
|
@@ -1374,15 +1390,25 @@ async function main() {
|
|
|
1374
1390
|
const cwd = process.cwd();
|
|
1375
1391
|
if (args.command === "login") return runLogin(args.loginProvider ?? "bc-cloud", cwd);
|
|
1376
1392
|
if (args.command === "mcp") {
|
|
1377
|
-
const { runMcpCommand } = await import("./mcpCli-
|
|
1378
|
-
const { readSecret } = await import("./login-
|
|
1393
|
+
const { runMcpCommand } = await import("./mcpCli-DZWQPTOS.js");
|
|
1394
|
+
const { readSecret } = await import("./login-6K32PIXV.js");
|
|
1379
1395
|
return runMcpCommand(args, { env: process.env, cwd, out: (s) => console.log(s), err: (s) => console.error(s), readSecret });
|
|
1380
1396
|
}
|
|
1381
1397
|
if (args.command === "provider") {
|
|
1382
|
-
const { runProviderCommand } = await import("./providerCli-
|
|
1383
|
-
const { readSecret } = await import("./login-
|
|
1398
|
+
const { runProviderCommand } = await import("./providerCli-Q7LRM2NP.js");
|
|
1399
|
+
const { readSecret } = await import("./login-6K32PIXV.js");
|
|
1384
1400
|
return runProviderCommand(args, { env: process.env, cwd, out: (s) => console.log(s), err: (s) => console.error(s), readSecret });
|
|
1385
1401
|
}
|
|
1402
|
+
if (args.command === "connect" || args.command === "disconnect" || args.command === "integrations") {
|
|
1403
|
+
const { runIntegrationCommand } = await import("./integrationCli-BGT5UZU4.js");
|
|
1404
|
+
return runIntegrationCommand(args, {
|
|
1405
|
+
env: process.env,
|
|
1406
|
+
cwd,
|
|
1407
|
+
out: (s) => console.log(s),
|
|
1408
|
+
err: (s) => console.error(s),
|
|
1409
|
+
fetch: globalThis.fetch
|
|
1410
|
+
});
|
|
1411
|
+
}
|
|
1386
1412
|
if (args.command === "models") {
|
|
1387
1413
|
const config = loadConfig(cwd);
|
|
1388
1414
|
const resolved = resolveModel(config, args.model ?? config.model);
|
|
@@ -1397,7 +1423,7 @@ async function main() {
|
|
|
1397
1423
|
}
|
|
1398
1424
|
if (args.print) {
|
|
1399
1425
|
if (!args.prompt) throw new ConfigError('Mode -p butuh tugas, contoh: bccli -p "jelaskan repo ini"');
|
|
1400
|
-
const { serversToStart } = await import("./config-
|
|
1426
|
+
const { serversToStart } = await import("./config-YUNKPCNB.js");
|
|
1401
1427
|
const plan = serversToStart(rt.home, cwd);
|
|
1402
1428
|
for (const s of plan.needTrust) {
|
|
1403
1429
|
console.error(`Server MCP project "${s.name}" dilewati (belum diizinkan; jalankan bccli interaktif sekali untuk menyetujuinya).`);
|
|
@@ -1410,7 +1436,7 @@ async function main() {
|
|
|
1410
1436
|
}
|
|
1411
1437
|
}
|
|
1412
1438
|
if (!process.stdin.isTTY) throw new ConfigError('Mode interaktif butuh terminal. Untuk skrip/CI pakai: bccli -p "tugas"');
|
|
1413
|
-
const { startInteractive } = await import("./ui-
|
|
1439
|
+
const { startInteractive } = await import("./ui-6GTQUJOE.js");
|
|
1414
1440
|
return startInteractive(rt, { initialPrompt: args.prompt, resume: args.resume, version: VERSION });
|
|
1415
1441
|
}
|
|
1416
1442
|
main().then(
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
ConfigError,
|
|
4
|
+
bccliHome,
|
|
5
|
+
loadConfig,
|
|
6
|
+
readCredentials,
|
|
7
|
+
resolveModel
|
|
8
|
+
} from "./chunk-2MGINW4O.js";
|
|
9
|
+
|
|
10
|
+
// src/integrationCli.ts
|
|
11
|
+
import { chmodSync, copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
12
|
+
import { dirname, join } from "path";
|
|
13
|
+
var BASE_URL = "https://api.botconnector.id/v1";
|
|
14
|
+
var PROVIDER_ID = "botconnector";
|
|
15
|
+
function integrationHome(env) {
|
|
16
|
+
return join(bccliHome(env), "integrations");
|
|
17
|
+
}
|
|
18
|
+
function statePath(target, env) {
|
|
19
|
+
return join(integrationHome(env), `${target}.state.json`);
|
|
20
|
+
}
|
|
21
|
+
function keyPath(env) {
|
|
22
|
+
return join(integrationHome(env), "bc-cloud.key");
|
|
23
|
+
}
|
|
24
|
+
function parseTarget(raw) {
|
|
25
|
+
if (raw === "opencode" || raw === "aider") return raw;
|
|
26
|
+
throw new ConfigError("Agent harus salah satu dari: opencode, aider");
|
|
27
|
+
}
|
|
28
|
+
function homeDir(env) {
|
|
29
|
+
const value = env.HOME || env.USERPROFILE;
|
|
30
|
+
if (!value) throw new ConfigError("Home directory tidak dapat ditentukan.");
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
function ensureSecret(env) {
|
|
34
|
+
const key = readCredentials(env)["bc-cloud"] || env.BOTCONNECTOR_API_KEY;
|
|
35
|
+
if (!key) throw new ConfigError("API key BotConnector belum tersedia. Jalankan bccli login bc-cloud terlebih dahulu.");
|
|
36
|
+
const dir = integrationHome(env);
|
|
37
|
+
mkdirSync(dir, { recursive: true });
|
|
38
|
+
const path = keyPath(env);
|
|
39
|
+
writeFileSync(path, `${key}
|
|
40
|
+
`, { mode: 384 });
|
|
41
|
+
try {
|
|
42
|
+
chmodSync(path, 384);
|
|
43
|
+
} catch {
|
|
44
|
+
}
|
|
45
|
+
return path;
|
|
46
|
+
}
|
|
47
|
+
function safeBackup(path, env) {
|
|
48
|
+
if (!existsSync(path)) return { created: true };
|
|
49
|
+
const dir = integrationHome(env);
|
|
50
|
+
mkdirSync(dir, { recursive: true });
|
|
51
|
+
const name = path.split(/[\\/]/).pop() || "config";
|
|
52
|
+
const backup = join(dir, `${name}.${Date.now()}.bak`);
|
|
53
|
+
copyFileSync(path, backup);
|
|
54
|
+
return { backup, created: false };
|
|
55
|
+
}
|
|
56
|
+
function writeState(target, state, env) {
|
|
57
|
+
const path = statePath(target, env);
|
|
58
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
59
|
+
writeFileSync(path, `${JSON.stringify(state, null, 2)}
|
|
60
|
+
`, { mode: 384 });
|
|
61
|
+
}
|
|
62
|
+
function readState(target, env) {
|
|
63
|
+
const path = statePath(target, env);
|
|
64
|
+
if (!existsSync(path)) return null;
|
|
65
|
+
try {
|
|
66
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
67
|
+
} catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async function botConnectorModels(deps) {
|
|
72
|
+
const config = loadConfig(deps.cwd);
|
|
73
|
+
const resolved = resolveModel(config, config.model, deps.env);
|
|
74
|
+
const key = readCredentials(deps.env)["bc-cloud"] || resolved.apiKey;
|
|
75
|
+
if (!key) throw new ConfigError("API key BotConnector belum tersedia. Jalankan bccli login bc-cloud terlebih dahulu.");
|
|
76
|
+
const response = await deps.fetch(`${BASE_URL}/models`, {
|
|
77
|
+
headers: {
|
|
78
|
+
authorization: `Bearer ${key}`,
|
|
79
|
+
"x-botconnector-client": "bccli",
|
|
80
|
+
"x-botconnector-client-version": "0.4.0"
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
if (!response.ok) throw new ConfigError(`Katalog model BotConnector gagal dimuat (HTTP ${response.status}).`);
|
|
84
|
+
const payload = await response.json();
|
|
85
|
+
return (payload.data ?? []).map((item) => item.id).filter((id) => typeof id === "string" && !!id);
|
|
86
|
+
}
|
|
87
|
+
async function connectOpenCode(deps) {
|
|
88
|
+
const home = homeDir(deps.env);
|
|
89
|
+
const path = join(home, ".config", "opencode", "opencode.json");
|
|
90
|
+
const backup = safeBackup(path, deps.env);
|
|
91
|
+
const secret = ensureSecret(deps.env);
|
|
92
|
+
const models = await botConnectorModels(deps);
|
|
93
|
+
let doc = {};
|
|
94
|
+
if (existsSync(path)) {
|
|
95
|
+
try {
|
|
96
|
+
doc = JSON.parse(readFileSync(path, "utf8"));
|
|
97
|
+
} catch {
|
|
98
|
+
throw new ConfigError(`Config OpenCode tidak valid JSON: ${path}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const providers = doc.provider && typeof doc.provider === "object" && !Array.isArray(doc.provider) ? { ...doc.provider } : {};
|
|
102
|
+
providers[PROVIDER_ID] = {
|
|
103
|
+
npm: "@ai-sdk/openai-compatible",
|
|
104
|
+
name: "BotConnector",
|
|
105
|
+
options: {
|
|
106
|
+
baseURL: BASE_URL,
|
|
107
|
+
apiKey: `{file:${secret.replace(/\\/g, "/")}}`
|
|
108
|
+
},
|
|
109
|
+
models: Object.fromEntries(models.map((id) => [id, { name: id }]))
|
|
110
|
+
};
|
|
111
|
+
if (!doc.$schema) doc.$schema = "https://opencode.ai/config.json";
|
|
112
|
+
doc.provider = providers;
|
|
113
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
114
|
+
writeFileSync(path, `${JSON.stringify(doc, null, 2)}
|
|
115
|
+
`);
|
|
116
|
+
writeState("opencode", {
|
|
117
|
+
target: "opencode",
|
|
118
|
+
files: [{ path, ...backup }],
|
|
119
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
120
|
+
}, deps.env);
|
|
121
|
+
deps.out(`OpenCode terhubung ke BotConnector (${models.length} model).`);
|
|
122
|
+
deps.out(`Config: ${path}`);
|
|
123
|
+
}
|
|
124
|
+
var AIDER_BEGIN = "# BEGIN BCCLI BOTCONNECTOR";
|
|
125
|
+
var AIDER_END = "# END BCCLI BOTCONNECTOR";
|
|
126
|
+
function stripManagedBlock(input) {
|
|
127
|
+
const start = input.indexOf(AIDER_BEGIN);
|
|
128
|
+
if (start < 0) return input;
|
|
129
|
+
const end = input.indexOf(AIDER_END, start);
|
|
130
|
+
if (end < 0) return input;
|
|
131
|
+
const after = end + AIDER_END.length;
|
|
132
|
+
const left = input.slice(0, start).trimEnd();
|
|
133
|
+
const right = input.slice(after).trimStart();
|
|
134
|
+
return right ? `${left}
|
|
135
|
+
${right}` : left;
|
|
136
|
+
}
|
|
137
|
+
function yamlQuote(value) {
|
|
138
|
+
return JSON.stringify(value.replace(/\\/g, "/"));
|
|
139
|
+
}
|
|
140
|
+
async function connectAider(deps) {
|
|
141
|
+
const home = homeDir(deps.env);
|
|
142
|
+
const configPath = join(home, ".aider.conf.yml");
|
|
143
|
+
const envPath = join(integrationHome(deps.env), "aider.env");
|
|
144
|
+
const configBackup = safeBackup(configPath, deps.env);
|
|
145
|
+
const envBackup = safeBackup(envPath, deps.env);
|
|
146
|
+
const secret = ensureSecret(deps.env);
|
|
147
|
+
const key = readFileSync(secret, "utf8").trim();
|
|
148
|
+
const config = loadConfig(deps.cwd);
|
|
149
|
+
const defaultModel = config.model.startsWith("bc-cloud/") ? config.model.slice("bc-cloud/".length) : "glm-5.3-flash";
|
|
150
|
+
let existing = existsSync(configPath) ? readFileSync(configPath, "utf8") : "";
|
|
151
|
+
existing = stripManagedBlock(existing);
|
|
152
|
+
if (/^\s*(env-file|openai-api-base|openai-api-key)\s*:/m.test(existing)) {
|
|
153
|
+
throw new ConfigError(`Config Aider sudah memiliki env-file/openai-api setting sendiri: ${configPath}. BCCLI tidak akan menimpanya.`);
|
|
154
|
+
}
|
|
155
|
+
mkdirSync(dirname(envPath), { recursive: true });
|
|
156
|
+
writeFileSync(envPath, [
|
|
157
|
+
`OPENAI_API_BASE=${BASE_URL}`,
|
|
158
|
+
`OPENAI_API_KEY=${key}`,
|
|
159
|
+
`AIDER_MODEL=openai/${defaultModel}`,
|
|
160
|
+
""
|
|
161
|
+
].join("\n"), { mode: 384 });
|
|
162
|
+
try {
|
|
163
|
+
chmodSync(envPath, 384);
|
|
164
|
+
} catch {
|
|
165
|
+
}
|
|
166
|
+
const managed = [
|
|
167
|
+
AIDER_BEGIN,
|
|
168
|
+
`env-file: ${yamlQuote(envPath)}`,
|
|
169
|
+
AIDER_END
|
|
170
|
+
].join("\n");
|
|
171
|
+
const merged = existing.trim() ? `${existing.trimEnd()}
|
|
172
|
+
|
|
173
|
+
${managed}
|
|
174
|
+
` : `${managed}
|
|
175
|
+
`;
|
|
176
|
+
writeFileSync(configPath, merged);
|
|
177
|
+
writeState("aider", {
|
|
178
|
+
target: "aider",
|
|
179
|
+
files: [{ path: configPath, ...configBackup }, { path: envPath, ...envBackup }],
|
|
180
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
181
|
+
}, deps.env);
|
|
182
|
+
deps.out("Aider terhubung ke BotConnector.");
|
|
183
|
+
deps.out(`Config: ${configPath}`);
|
|
184
|
+
}
|
|
185
|
+
function disconnect(target, deps) {
|
|
186
|
+
const state = readState(target, deps.env);
|
|
187
|
+
if (!state) {
|
|
188
|
+
deps.out(`${target}: belum dikelola oleh BCCLI.`);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
for (const file of [...state.files].reverse()) {
|
|
192
|
+
if (file.backup && existsSync(file.backup)) {
|
|
193
|
+
mkdirSync(dirname(file.path), { recursive: true });
|
|
194
|
+
copyFileSync(file.backup, file.path);
|
|
195
|
+
} else if (file.created) {
|
|
196
|
+
rmSync(file.path, { force: true });
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
rmSync(statePath(target, deps.env), { force: true });
|
|
200
|
+
deps.out(`${target}: integrasi BotConnector dilepas dan config dipulihkan.`);
|
|
201
|
+
}
|
|
202
|
+
function listIntegrations(deps) {
|
|
203
|
+
for (const target of ["opencode", "aider"]) {
|
|
204
|
+
deps.out(`${target} ${readState(target, deps.env) ? "connected" : "not connected"}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async function runIntegrationCommand(args, deps) {
|
|
208
|
+
if (args.command === "integrations") {
|
|
209
|
+
listIntegrations(deps);
|
|
210
|
+
return 0;
|
|
211
|
+
}
|
|
212
|
+
const target = parseTarget(args.subArgs[0]);
|
|
213
|
+
if (args.command === "disconnect") {
|
|
214
|
+
disconnect(target, deps);
|
|
215
|
+
return 0;
|
|
216
|
+
}
|
|
217
|
+
if (target === "opencode") await connectOpenCode(deps);
|
|
218
|
+
else await connectAider(deps);
|
|
219
|
+
return 0;
|
|
220
|
+
}
|
|
221
|
+
export {
|
|
222
|
+
runIntegrationCommand
|
|
223
|
+
};
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
import {
|
|
3
3
|
CATALOG,
|
|
4
4
|
fillTemplate
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-DX7CZTBB.js";
|
|
6
6
|
import {
|
|
7
7
|
addGlobalServer,
|
|
8
8
|
globalMcpPath,
|
|
9
9
|
readMcpFile,
|
|
10
10
|
removeGlobalServer
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-BFZXRKW5.js";
|
|
12
12
|
import {
|
|
13
13
|
bccliHome
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-2MGINW4O.js";
|
|
15
15
|
|
|
16
16
|
// src/mcpCli.ts
|
|
17
17
|
async function runMcpCommand(args, deps) {
|
|
@@ -13,24 +13,30 @@ import {
|
|
|
13
13
|
hasKey,
|
|
14
14
|
providerName,
|
|
15
15
|
writeGlobalConfig
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-7S6JXQCH.js";
|
|
17
|
+
import {
|
|
18
|
+
REASONING_LEVELS,
|
|
19
|
+
assertReasoningSupported,
|
|
20
|
+
parseReasoningLevel,
|
|
21
|
+
supportedReasoningLevels
|
|
22
|
+
} from "./chunk-CLN4Z5E3.js";
|
|
17
23
|
import {
|
|
18
24
|
Session
|
|
19
25
|
} from "./chunk-SJH3YLW4.js";
|
|
20
26
|
import {
|
|
21
27
|
CATALOG,
|
|
22
28
|
fillTemplate
|
|
23
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-DX7CZTBB.js";
|
|
24
30
|
import {
|
|
25
31
|
addGlobalServer,
|
|
26
32
|
globalMcpPath,
|
|
27
33
|
readMcpFile,
|
|
28
34
|
removeGlobalServer
|
|
29
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-BFZXRKW5.js";
|
|
30
36
|
import {
|
|
31
37
|
PRESETS,
|
|
32
38
|
removeCredential
|
|
33
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-2MGINW4O.js";
|
|
34
40
|
|
|
35
41
|
// src/ui/index.tsx
|
|
36
42
|
import { render } from "ink";
|
|
@@ -47,6 +53,7 @@ import { useCallback, useEffect as useEffect2, useRef, useState as useState8 } f
|
|
|
47
53
|
var SLASH_COMMANDS = [
|
|
48
54
|
{ name: "help", description: "daftar perintah dan pintasan" },
|
|
49
55
|
{ name: "model", description: "ganti model" },
|
|
56
|
+
{ name: "reasoning", description: "atur reasoning: auto/off/low/medium/high/max" },
|
|
50
57
|
{ name: "provider", description: "tambah/pilih provider AI" },
|
|
51
58
|
{ name: "mcp", description: "pasang/kelola server MCP" },
|
|
52
59
|
{ name: "new", description: "sesi baru (sesi lama tetap tersimpan)" },
|
|
@@ -272,7 +279,7 @@ function ListPicker({ title, items, onPick }) {
|
|
|
272
279
|
useInput2((input, key) => {
|
|
273
280
|
if (key.upArrow) setIndex(Math.max(0, cursor - 1));
|
|
274
281
|
else if (key.downArrow) setIndex(Math.min(shown.length - 1, cursor + 1));
|
|
275
|
-
else if (key.return) onPick(shown[cursor]?.id);
|
|
282
|
+
else if (key.return && !shown[cursor]?.disabled) onPick(shown[cursor]?.id);
|
|
276
283
|
else if (key.escape) onPick(void 0);
|
|
277
284
|
else if (key.backspace || key.delete) {
|
|
278
285
|
setFilter((f) => f.slice(0, -1));
|
|
@@ -285,7 +292,7 @@ function ListPicker({ title, items, onPick }) {
|
|
|
285
292
|
const start = Math.max(0, Math.min(cursor - 5, shown.length - VISIBLE));
|
|
286
293
|
return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", borderStyle: "round", borderColor: color("cyan"), paddingX: 1, children: [
|
|
287
294
|
/* @__PURE__ */ jsx3(Text3, { bold: true, children: `${title} (\u2191\u2193 enter \xB7 ketik untuk menyaring${filter ? `: ${filter}` : ""} \xB7 esc batal)` }),
|
|
288
|
-
shown.slice(start, start + VISIBLE).map((item, i) => /* @__PURE__ */ jsxs2(Text3, { color: start + i === cursor ? color("green") : void 0, children: [
|
|
295
|
+
shown.slice(start, start + VISIBLE).map((item, i) => /* @__PURE__ */ jsxs2(Text3, { color: start + i === cursor && !item.disabled ? color("green") : void 0, dimColor: item.disabled, children: [
|
|
289
296
|
`${start + i === cursor ? "\u203A" : " "} ${item.label}`,
|
|
290
297
|
item.hint ? /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: ` ${item.hint}` }) : null
|
|
291
298
|
] }, item.id)),
|
|
@@ -533,9 +540,9 @@ var LABEL = {
|
|
|
533
540
|
function formatTokens(n) {
|
|
534
541
|
return n >= 1e3 ? `${Math.round(n / 1e3)}k` : String(n);
|
|
535
542
|
}
|
|
536
|
-
function StatusBar({ mode, tokens, busy, model }) {
|
|
543
|
+
function StatusBar({ mode, tokens, busy, model, reasoning = "auto" }) {
|
|
537
544
|
const danger = mode === "allowAll";
|
|
538
|
-
return /* @__PURE__ */ jsx10(Text10, { color: danger ? color("red") : void 0, dimColor: !danger, children: ` ${LABEL[mode]}${model ? ` \xB7 ${model}` : ""} \xB7 shift+tab ganti mode \xB7 ${formatTokens(tokens)} token \xB7 ${busy ? "esc batal" : "/ perintah"}` });
|
|
545
|
+
return /* @__PURE__ */ jsx10(Text10, { color: danger ? color("red") : void 0, dimColor: !danger, children: ` ${LABEL[mode]}${model ? ` \xB7 ${model}` : ""} \xB7 reasoning: ${reasoning} \xB7 shift+tab ganti mode \xB7 ${formatTokens(tokens)} token \xB7 ${busy ? "esc batal" : "/ perintah"}` });
|
|
539
546
|
}
|
|
540
547
|
|
|
541
548
|
// src/ui/McpMenu.tsx
|
|
@@ -729,6 +736,7 @@ function App({ runtime, initialPrompt, version }) {
|
|
|
729
736
|
const [busy, setBusy] = useState8(false);
|
|
730
737
|
const [startedAt, setStartedAt] = useState8(0);
|
|
731
738
|
const [mode, setMode] = useState8(runtime.agent.permissions.mode);
|
|
739
|
+
const [reasoning, setReasoning] = useState8(runtime.reasoning);
|
|
732
740
|
const [tokens, setTokens] = useState8(runtime.agent.totalUsage.inputTokens + runtime.agent.totalUsage.outputTokens);
|
|
733
741
|
const [asks, setAsks] = useState8([]);
|
|
734
742
|
const pending = asks[0] ?? null;
|
|
@@ -814,6 +822,41 @@ function App({ runtime, initialPrompt, version }) {
|
|
|
814
822
|
setBusy(false);
|
|
815
823
|
return;
|
|
816
824
|
}
|
|
825
|
+
case "reasoning": {
|
|
826
|
+
const apply = (value) => {
|
|
827
|
+
try {
|
|
828
|
+
const level = parseReasoningLevel(value, "/reasoning");
|
|
829
|
+
const providerId2 = runtime.modelRef.slice(0, runtime.modelRef.indexOf("/"));
|
|
830
|
+
assertReasoningSupported(providerId2, level);
|
|
831
|
+
runtime.setReasoning(level);
|
|
832
|
+
writeGlobalConfig({ reasoning: level }, runtime.env);
|
|
833
|
+
setReasoning(level);
|
|
834
|
+
notice(`Reasoning: ${level} (tersimpan sebagai default)`);
|
|
835
|
+
} catch (error) {
|
|
836
|
+
notice(error.message, "error");
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
if (args) {
|
|
840
|
+
apply(args);
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
const providerId = runtime.modelRef.slice(0, runtime.modelRef.indexOf("/"));
|
|
844
|
+
const supported = supportedReasoningLevels(providerId);
|
|
845
|
+
setListPicker({
|
|
846
|
+
title: "Reasoning",
|
|
847
|
+
items: REASONING_LEVELS.map((level) => ({
|
|
848
|
+
id: level,
|
|
849
|
+
label: level === "auto" ? "Auto (model default)" : level.charAt(0).toUpperCase() + level.slice(1),
|
|
850
|
+
disabled: !supported.includes(level),
|
|
851
|
+
hint: supported.includes(level) ? void 0 : "tidak didukung provider ini"
|
|
852
|
+
})),
|
|
853
|
+
onPick: (id) => {
|
|
854
|
+
setListPicker(null);
|
|
855
|
+
if (id) apply(id);
|
|
856
|
+
}
|
|
857
|
+
});
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
817
860
|
case "model": {
|
|
818
861
|
if (args) {
|
|
819
862
|
try {
|
|
@@ -1325,7 +1368,7 @@ ${t.output}`);
|
|
|
1325
1368
|
/* @__PURE__ */ jsx15(TodoList, { items: todos }),
|
|
1326
1369
|
queued.map((q, i) => /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: ` \u23F3 antri: ${q}` }, `${i}-${q}`)),
|
|
1327
1370
|
/* @__PURE__ */ jsx15(PromptInput, { disabled: !!pending || !!picker || !!providerMenu || !!prompt || !!planAsk || !!mcpMenu || !!listPicker, history, cwd: runtime.cwd, onSubmit: onPrompt, extraCommands }),
|
|
1328
|
-
/* @__PURE__ */ jsx15(StatusBar, { mode, tokens, busy, model: modelLabel })
|
|
1371
|
+
/* @__PURE__ */ jsx15(StatusBar, { mode, tokens, busy, model: modelLabel, reasoning })
|
|
1329
1372
|
] });
|
|
1330
1373
|
}
|
|
1331
1374
|
|
|
@@ -1348,7 +1391,7 @@ async function startInteractive(rt, opts) {
|
|
|
1348
1391
|
if (chosen) rt.resume(chosen.session);
|
|
1349
1392
|
}
|
|
1350
1393
|
}
|
|
1351
|
-
const { serversToStart, setProjectTrust } = await import("./config-
|
|
1394
|
+
const { serversToStart, setProjectTrust } = await import("./config-YUNKPCNB.js");
|
|
1352
1395
|
const plan = serversToStart(rt.home, rt.cwd);
|
|
1353
1396
|
if (plan.needTrust.length) {
|
|
1354
1397
|
const { createInterface } = await import("readline/promises");
|
package/package.json
CHANGED