@botconnector/bccli 0.4.0-beta.3 → 0.4.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-2JBRLGKD.js → chunk-C5AFWX7H.js} +73 -11
- package/dist/{chunk-L7L4RGKG.js → chunk-GW36UMHZ.js} +1 -1
- package/dist/cli.js +9 -8
- package/dist/{login-KHU7ONSR.js → login-F4G5QXOU.js} +1 -1
- package/dist/{providerCli-G3X7KENO.js → providerCli-MXV6PD6F.js} +1 -1
- package/dist/{ui-A32PTF4F.js → ui-7FXPXXGN.js} +54 -1
- package/package.json +1 -1
|
@@ -12,6 +12,54 @@ import {
|
|
|
12
12
|
saveCredential
|
|
13
13
|
} from "./chunk-RZUJYAQV.js";
|
|
14
14
|
|
|
15
|
+
// src/thinking.ts
|
|
16
|
+
var OPEN = "<think>";
|
|
17
|
+
var CLOSE = "</think>";
|
|
18
|
+
function partialTag(text, tag) {
|
|
19
|
+
for (let n = Math.min(tag.length - 1, text.length); n > 0; n--) if (tag.startsWith(text.slice(-n))) return n;
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
var ThinkSplitter = class {
|
|
23
|
+
inside = false;
|
|
24
|
+
held = "";
|
|
25
|
+
push(delta) {
|
|
26
|
+
let rest = this.held + delta;
|
|
27
|
+
this.held = "";
|
|
28
|
+
let text = "";
|
|
29
|
+
let thinking = "";
|
|
30
|
+
while (rest) {
|
|
31
|
+
const tag = this.inside ? CLOSE : OPEN;
|
|
32
|
+
const at = rest.indexOf(tag);
|
|
33
|
+
if (at >= 0) {
|
|
34
|
+
if (this.inside) thinking += rest.slice(0, at);
|
|
35
|
+
else text += rest.slice(0, at);
|
|
36
|
+
rest = rest.slice(at + tag.length);
|
|
37
|
+
this.inside = !this.inside;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const keep = partialTag(rest, tag);
|
|
41
|
+
const out = rest.slice(0, rest.length - keep);
|
|
42
|
+
if (this.inside) thinking += out;
|
|
43
|
+
else text += out;
|
|
44
|
+
this.held = rest.slice(rest.length - keep);
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
return { text, thinking };
|
|
48
|
+
}
|
|
49
|
+
/** Whatever was held back as a possible tag start is plain text after all. */
|
|
50
|
+
flush() {
|
|
51
|
+
const held = this.held;
|
|
52
|
+
this.held = "";
|
|
53
|
+
return this.inside ? { text: "", thinking: held } : { text: held, thinking: "" };
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
function splitThinking(text) {
|
|
57
|
+
const s = new ThinkSplitter();
|
|
58
|
+
const a = s.push(text);
|
|
59
|
+
const b = s.flush();
|
|
60
|
+
return { text: a.text + b.text, thinking: a.thinking + b.thinking };
|
|
61
|
+
}
|
|
62
|
+
|
|
15
63
|
// src/provider.ts
|
|
16
64
|
var ProviderError = class extends Error {
|
|
17
65
|
constructor(message, status) {
|
|
@@ -58,8 +106,21 @@ function errorMessage(body) {
|
|
|
58
106
|
}
|
|
59
107
|
}
|
|
60
108
|
var toUsage = (u) => u ? { inputTokens: u.prompt_tokens ?? 0, outputTokens: u.completion_tokens ?? 0 } : void 0;
|
|
61
|
-
async function readStream(res, onText) {
|
|
109
|
+
async function readStream(res, onText, onThinking) {
|
|
62
110
|
let text = "";
|
|
111
|
+
let thinking = "";
|
|
112
|
+
const splitter = new ThinkSplitter();
|
|
113
|
+
const emit = (part) => {
|
|
114
|
+
if (part.thinking) {
|
|
115
|
+
thinking += part.thinking;
|
|
116
|
+
onThinking?.(part.thinking);
|
|
117
|
+
}
|
|
118
|
+
if (part.text) {
|
|
119
|
+
text += part.text;
|
|
120
|
+
onText?.(part.text);
|
|
121
|
+
if (loopAt < 0) loopAt = detectRepetition(text);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
63
124
|
let usage;
|
|
64
125
|
let finishReason;
|
|
65
126
|
const calls = [];
|
|
@@ -84,11 +145,9 @@ async function readStream(res, onText) {
|
|
|
84
145
|
if (!choice) return;
|
|
85
146
|
if (choice.finish_reason) finishReason = choice.finish_reason;
|
|
86
147
|
const delta = choice.delta ?? {};
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (loopAt < 0) loopAt = detectRepetition(text);
|
|
91
|
-
}
|
|
148
|
+
const reasoning = delta.reasoning_content || delta.reasoning;
|
|
149
|
+
if (reasoning) emit({ text: "", thinking: reasoning });
|
|
150
|
+
if (delta.content) emit(splitter.push(delta.content));
|
|
92
151
|
for (const tc of delta.tool_calls ?? []) {
|
|
93
152
|
let target = tc.index !== void 0 ? byIndex.get(tc.index) : current;
|
|
94
153
|
if (!target || tc.index === void 0 && tc.id && target.id && target.id !== tc.id) {
|
|
@@ -110,11 +169,12 @@ async function readStream(res, onText) {
|
|
|
110
169
|
buffer = buffer.slice(newline + 1);
|
|
111
170
|
newline = buffer.indexOf("\n");
|
|
112
171
|
}
|
|
113
|
-
if (loopAt >= 0) return { text: text.slice(0, loopAt), toolCalls: [], usage, finishReason: "repetition" };
|
|
172
|
+
if (loopAt >= 0) return { text: text.slice(0, loopAt), toolCalls: [], thinking: thinking || void 0, usage, finishReason: "repetition" };
|
|
114
173
|
}
|
|
115
174
|
handle(buffer.trim());
|
|
175
|
+
emit(splitter.flush());
|
|
116
176
|
const toolCalls = calls.map((call, index) => ({ ...call, id: call.id || `call_${index}` }));
|
|
117
|
-
return { text, toolCalls, usage, finishReason };
|
|
177
|
+
return { text, toolCalls, thinking: thinking || void 0, usage, finishReason };
|
|
118
178
|
}
|
|
119
179
|
async function readJson(res) {
|
|
120
180
|
const body = await res.json();
|
|
@@ -124,7 +184,9 @@ async function readJson(res) {
|
|
|
124
184
|
name: tc.function?.name ?? "",
|
|
125
185
|
arguments: tc.function?.arguments ?? ""
|
|
126
186
|
}));
|
|
127
|
-
|
|
187
|
+
const split = splitThinking(choice?.message?.content ?? "");
|
|
188
|
+
const thinking = (choice?.message?.reasoning_content || choice?.message?.reasoning || "") + split.thinking;
|
|
189
|
+
return { text: split.text, toolCalls, thinking: thinking || void 0, usage: toUsage(body.usage), finishReason: choice?.finish_reason };
|
|
128
190
|
}
|
|
129
191
|
function createProvider({ baseURL, apiKey, model, fetch: doFetch = fetch, retryDelayMs = 1e3 }) {
|
|
130
192
|
const headers = { "content-type": "application/json" };
|
|
@@ -149,7 +211,7 @@ function createProvider({ baseURL, apiKey, model, fetch: doFetch = fetch, retryD
|
|
|
149
211
|
throw lastError ?? new ProviderError("Permintaan gagal");
|
|
150
212
|
}
|
|
151
213
|
return {
|
|
152
|
-
async chat({ messages, tools, signal, onText }) {
|
|
214
|
+
async chat({ messages, tools, signal, onText, onThinking }) {
|
|
153
215
|
const res = await post(
|
|
154
216
|
"/chat/completions",
|
|
155
217
|
{ model, messages, stream: true, stream_options: { include_usage: true }, ...tools?.length ? { tools } : {} },
|
|
@@ -157,7 +219,7 @@ function createProvider({ baseURL, apiKey, model, fetch: doFetch = fetch, retryD
|
|
|
157
219
|
);
|
|
158
220
|
if ((res.headers.get("content-type") ?? "").includes("application/json")) return readJson(res);
|
|
159
221
|
if (!res.body) throw new ProviderError("Respons provider kosong");
|
|
160
|
-
return readStream(res, onText);
|
|
222
|
+
return readStream(res, onText, onThinking);
|
|
161
223
|
},
|
|
162
224
|
async listModels() {
|
|
163
225
|
const res = await doFetch(`${baseURL}/models`, { headers });
|
|
@@ -35,7 +35,7 @@ function readSecret(prompt) {
|
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
37
|
async function runLogin(providerId, cwd) {
|
|
38
|
-
const { runProviderCommand } = await import("./providerCli-
|
|
38
|
+
const { runProviderCommand } = await import("./providerCli-MXV6PD6F.js");
|
|
39
39
|
const { parseCliArgs } = await import("./args-FCLPQ7TO.js");
|
|
40
40
|
return runProviderCommand(parseCliArgs(["provider", "add", providerId]), {
|
|
41
41
|
env: process.env,
|
package/dist/cli.js
CHANGED
|
@@ -7,10 +7,10 @@ import {
|
|
|
7
7
|
addProviderKey,
|
|
8
8
|
createProvider,
|
|
9
9
|
listAllModels
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-C5AFWX7H.js";
|
|
11
11
|
import {
|
|
12
12
|
runLogin
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-GW36UMHZ.js";
|
|
14
14
|
import {
|
|
15
15
|
BUILTIN_AGENTS,
|
|
16
16
|
buildSystemPrompt,
|
|
@@ -684,7 +684,8 @@ ${completion.text}` });
|
|
|
684
684
|
messages: [{ role: "system", content: this.systemPrompt }, ...this.messages],
|
|
685
685
|
tools: this.turnDefinitions,
|
|
686
686
|
signal,
|
|
687
|
-
onText: (delta) => this.onEvent({ type: "text", delta })
|
|
687
|
+
onText: (delta) => this.onEvent({ type: "text", delta }),
|
|
688
|
+
onThinking: (delta) => this.onEvent({ type: "thinking", delta })
|
|
688
689
|
});
|
|
689
690
|
if (!completion.toolCalls.length && completion.finishReason !== "repetition") {
|
|
690
691
|
const recovered = recoverTextToolCalls(completion.text, this.turnDefinitions);
|
|
@@ -1272,7 +1273,7 @@ function createRuntime(opts) {
|
|
|
1272
1273
|
}
|
|
1273
1274
|
|
|
1274
1275
|
// src/version.ts
|
|
1275
|
-
var VERSION = true ? "0.4.0-beta.
|
|
1276
|
+
var VERSION = true ? "0.4.0-beta.4" : "0.0.0-dev";
|
|
1276
1277
|
|
|
1277
1278
|
// src/cli.ts
|
|
1278
1279
|
async function main() {
|
|
@@ -1289,12 +1290,12 @@ async function main() {
|
|
|
1289
1290
|
if (args.command === "login") return runLogin(args.loginProvider ?? "bc-cloud", cwd);
|
|
1290
1291
|
if (args.command === "mcp") {
|
|
1291
1292
|
const { runMcpCommand } = await import("./mcpCli-Q2H63ISP.js");
|
|
1292
|
-
const { readSecret } = await import("./login-
|
|
1293
|
+
const { readSecret } = await import("./login-F4G5QXOU.js");
|
|
1293
1294
|
return runMcpCommand(args, { env: process.env, cwd, out: (s) => console.log(s), err: (s) => console.error(s), readSecret });
|
|
1294
1295
|
}
|
|
1295
1296
|
if (args.command === "provider") {
|
|
1296
|
-
const { runProviderCommand } = await import("./providerCli-
|
|
1297
|
-
const { readSecret } = await import("./login-
|
|
1297
|
+
const { runProviderCommand } = await import("./providerCli-MXV6PD6F.js");
|
|
1298
|
+
const { readSecret } = await import("./login-F4G5QXOU.js");
|
|
1298
1299
|
return runProviderCommand(args, { env: process.env, cwd, out: (s) => console.log(s), err: (s) => console.error(s), readSecret });
|
|
1299
1300
|
}
|
|
1300
1301
|
if (args.command === "models") {
|
|
@@ -1324,7 +1325,7 @@ async function main() {
|
|
|
1324
1325
|
}
|
|
1325
1326
|
}
|
|
1326
1327
|
if (!process.stdin.isTTY) throw new ConfigError('Mode interaktif butuh terminal. Untuk skrip/CI pakai: bccli -p "tugas"');
|
|
1327
|
-
const { startInteractive } = await import("./ui-
|
|
1328
|
+
const { startInteractive } = await import("./ui-7FXPXXGN.js");
|
|
1328
1329
|
return startInteractive(rt, { initialPrompt: args.prompt, resume: args.resume, version: VERSION });
|
|
1329
1330
|
}
|
|
1330
1331
|
main().then(
|
|
@@ -638,6 +638,11 @@ function applyEvent(t, event) {
|
|
|
638
638
|
if (last?.kind === "assistant") return { ...t, live: [...t.live.slice(0, -1), { ...last, text: last.text + event.delta }] };
|
|
639
639
|
return { ...t, live: [...t.live, entry({ kind: "assistant", text: event.delta })] };
|
|
640
640
|
}
|
|
641
|
+
case "thinking": {
|
|
642
|
+
const last = t.live.at(-1);
|
|
643
|
+
if (last?.kind === "thinking") return { ...t, live: [...t.live.slice(0, -1), { ...last, text: last.text + event.delta }] };
|
|
644
|
+
return { ...t, live: [...t.live, entry({ kind: "thinking", text: event.delta })] };
|
|
645
|
+
}
|
|
641
646
|
case "textReplace": {
|
|
642
647
|
const last = t.live.at(-1);
|
|
643
648
|
if (last?.kind !== "assistant") return t;
|
|
@@ -687,6 +692,7 @@ function helpText(runtime) {
|
|
|
687
692
|
"",
|
|
688
693
|
"shift+tab ganti mode izin esc batalkan giliran",
|
|
689
694
|
"ctrl+o output alat terakhir lengkap \\ + enter baris baru",
|
|
695
|
+
"ctrl+t buka/tutup thinking model",
|
|
690
696
|
"@file + tab lengkapi nama file \u2191\u2193 riwayat input"
|
|
691
697
|
].join("\n");
|
|
692
698
|
}
|
|
@@ -713,6 +719,8 @@ function App({ runtime, initialPrompt, version }) {
|
|
|
713
719
|
const [mcpMenu, setMcpMenu] = useState8(null);
|
|
714
720
|
const [listPicker, setListPicker] = useState8(null);
|
|
715
721
|
const [planAsk, setPlanAsk] = useState8(null);
|
|
722
|
+
const [queued, setQueued] = useState8([]);
|
|
723
|
+
const [showThinking, setShowThinking] = useState8(false);
|
|
716
724
|
useEffect2(() => runtime.todos.subscribe(setTodos), [runtime]);
|
|
717
725
|
const extraCommands = [
|
|
718
726
|
...runtime.commands.filter((c) => !SLASH_COMMANDS.some((b) => b.name === c.name.toLowerCase())).map((c) => ({ name: c.name.toLowerCase(), description: c.description ?? "perintah custom" })),
|
|
@@ -1086,6 +1094,19 @@ ARGUMENTS: ${slash.args}`);
|
|
|
1086
1094
|
},
|
|
1087
1095
|
[runSlash, runTurn, runtime, notice]
|
|
1088
1096
|
);
|
|
1097
|
+
const onPrompt = useCallback(
|
|
1098
|
+
(text) => {
|
|
1099
|
+
if (busy) setQueued((q) => [...q, text]);
|
|
1100
|
+
else submit(text);
|
|
1101
|
+
},
|
|
1102
|
+
[busy, submit]
|
|
1103
|
+
);
|
|
1104
|
+
useEffect2(() => {
|
|
1105
|
+
if (busy || pending || planAsk || !queued.length) return;
|
|
1106
|
+
const [next, ...rest] = queued;
|
|
1107
|
+
setQueued(rest);
|
|
1108
|
+
submit(next);
|
|
1109
|
+
}, [busy, pending, planAsk, queued, submit]);
|
|
1089
1110
|
const initialSent = useRef(false);
|
|
1090
1111
|
useEffect2(() => {
|
|
1091
1112
|
if (initialPrompt && !initialSent.current) {
|
|
@@ -1100,6 +1121,25 @@ ARGUMENTS: ${slash.args}`);
|
|
|
1100
1121
|
setMode(next);
|
|
1101
1122
|
} else if (key.escape && busy && !pending) {
|
|
1102
1123
|
controller.current?.abort();
|
|
1124
|
+
if (queued.length) {
|
|
1125
|
+
setQueued([]);
|
|
1126
|
+
notice(`${queued.length} pesan antrian dibatalkan.`, "warn");
|
|
1127
|
+
}
|
|
1128
|
+
} else if (key.ctrl && input === "t") {
|
|
1129
|
+
const next = !showThinking;
|
|
1130
|
+
setShowThinking(next);
|
|
1131
|
+
setTranscript((t) => {
|
|
1132
|
+
const last = [...t.done, ...t.live].findLast((e) => e.kind === "thinking");
|
|
1133
|
+
const reprint = next && last?.kind === "thinking" && t.done.includes(last);
|
|
1134
|
+
return {
|
|
1135
|
+
...t,
|
|
1136
|
+
done: [
|
|
1137
|
+
...t.done,
|
|
1138
|
+
...reprint ? [entry({ kind: "thinking", text: last.text, open: true })] : [],
|
|
1139
|
+
entry({ kind: "notice", text: next ? "Thinking ditampilkan (ctrl+t untuk menutup)." : "Thinking disembunyikan (ctrl+t untuk membuka).", tone: "info" })
|
|
1140
|
+
]
|
|
1141
|
+
};
|
|
1142
|
+
});
|
|
1103
1143
|
} else if (key.ctrl && input === "o" && lastTool.current) {
|
|
1104
1144
|
const t = lastTool.current;
|
|
1105
1145
|
notice(`\u23BF ${t.tool} ${t.target}
|
|
@@ -1180,6 +1220,18 @@ ${t.output}`);
|
|
|
1180
1220
|
/* @__PURE__ */ jsx15(Text15, { color: color(ACCENT), children: "\u25CF " }),
|
|
1181
1221
|
/* @__PURE__ */ jsx15(Markdown, { text: e.text.replace(/^\s+/, "") })
|
|
1182
1222
|
] }, e.id);
|
|
1223
|
+
case "thinking": {
|
|
1224
|
+
const text = e.text.trim();
|
|
1225
|
+
if (!text) return null;
|
|
1226
|
+
if (!(e.open ?? showThinking)) {
|
|
1227
|
+
const lines = text.split("\n").length;
|
|
1228
|
+
return /* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: `\u273B Berpikir \xB7 ${lines} baris \xB7 ctrl+t buka` }) }, e.id);
|
|
1229
|
+
}
|
|
1230
|
+
return /* @__PURE__ */ jsxs10(Box13, { marginTop: 1, flexDirection: "column", children: [
|
|
1231
|
+
/* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "\u273B Berpikir (ctrl+t tutup)" }),
|
|
1232
|
+
/* @__PURE__ */ jsx15(Box13, { paddingLeft: 2, children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, italic: true, children: text }) })
|
|
1233
|
+
] }, e.id);
|
|
1234
|
+
}
|
|
1183
1235
|
case "tool":
|
|
1184
1236
|
return /* @__PURE__ */ jsx15(ToolBlock, { tool: e.tool, target: e.target, output: e.output, display: e.display, isError: e.isError, done: e.done, sub: e.sub }, e.id);
|
|
1185
1237
|
case "notice":
|
|
@@ -1232,7 +1284,8 @@ ${t.output}`);
|
|
|
1232
1284
|
}
|
|
1233
1285
|
) : null,
|
|
1234
1286
|
/* @__PURE__ */ jsx15(TodoList, { items: todos }),
|
|
1235
|
-
/* @__PURE__ */ jsx15(
|
|
1287
|
+
queued.map((q, i) => /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: ` \u23F3 antri: ${q}` }, `${i}-${q}`)),
|
|
1288
|
+
/* @__PURE__ */ jsx15(PromptInput, { disabled: !!pending || !!picker || !!providerMenu || !!prompt || !!planAsk || !!mcpMenu || !!listPicker, history, cwd: runtime.cwd, onSubmit: onPrompt, extraCommands }),
|
|
1236
1289
|
/* @__PURE__ */ jsx15(StatusBar, { mode, tokens, busy, model: modelLabel })
|
|
1237
1290
|
] });
|
|
1238
1291
|
}
|