@botconnector/bccli 0.4.0-beta.1 → 0.4.0-beta.3
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-EAKPBBGX.js → args-FCLPQ7TO.js} +2 -2
- package/dist/{chunk-5ISYVAXI.js → chunk-2JBRLGKD.js} +2 -2
- package/dist/{chunk-QH7XXNHU.js → chunk-L7L4RGKG.js} +2 -2
- package/dist/{chunk-XQLXE666.js → chunk-NGLKANMD.js} +1 -1
- package/dist/{chunk-Z5JM76EL.js → chunk-RZUJYAQV.js} +2 -1
- package/dist/{chunk-5C6NQFSI.js → chunk-U7DLMVPZ.js} +1 -1
- package/dist/{chunk-TV6LEHOH.js → chunk-UESQNU4A.js} +1 -1
- package/dist/{chunk-M7INIL2C.js → chunk-UN7QXPCB.js} +1 -1
- package/dist/cli.js +87 -13
- package/dist/{config-XPWPGNZJ.js → config-NPSYFAFD.js} +2 -2
- package/dist/{login-FTMR64IA.js → login-KHU7ONSR.js} +1 -1
- package/dist/{mcpCli-BV3IGR47.js → mcpCli-Q2H63ISP.js} +3 -3
- package/dist/{providerCli-SJRCBSRE.js → providerCli-G3X7KENO.js} +3 -3
- package/dist/{ui-LWZ75D7H.js → ui-A32PTF4F.js} +59 -22
- package/package.json +1 -1
|
@@ -4,13 +4,13 @@ import {
|
|
|
4
4
|
providerName,
|
|
5
5
|
removeGlobalProvider,
|
|
6
6
|
writeGlobalConfig
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-U7DLMVPZ.js";
|
|
8
8
|
import {
|
|
9
9
|
PRESETS,
|
|
10
10
|
loadConfig,
|
|
11
11
|
readCredentials,
|
|
12
12
|
saveCredential
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-RZUJYAQV.js";
|
|
14
14
|
|
|
15
15
|
// src/provider.ts
|
|
16
16
|
var ProviderError = class extends Error {
|
|
@@ -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-G3X7KENO.js");
|
|
39
|
+
const { parseCliArgs } = await import("./args-FCLPQ7TO.js");
|
|
40
40
|
return runProviderCommand(parseCliArgs(["provider", "add", providerId]), {
|
|
41
41
|
env: process.env,
|
|
42
42
|
cwd,
|
|
@@ -91,11 +91,12 @@ function resolveModel(config, modelRef, env = process.env) {
|
|
|
91
91
|
}
|
|
92
92
|
function removeCredential(providerId, env = process.env) {
|
|
93
93
|
const creds = readCredentials(env);
|
|
94
|
-
if (!(providerId in creds)) return;
|
|
94
|
+
if (!(providerId in creds)) return false;
|
|
95
95
|
delete creds[providerId];
|
|
96
96
|
const path = join(bccliHome(env), "credentials");
|
|
97
97
|
writeFileSync(path, JSON.stringify(creds, null, 2), { mode: 384 });
|
|
98
98
|
chmodSync(path, 384);
|
|
99
|
+
return true;
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
export {
|
package/dist/cli.js
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
import {
|
|
3
3
|
HELP_TEXT,
|
|
4
4
|
parseCliArgs
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-UN7QXPCB.js";
|
|
6
6
|
import {
|
|
7
7
|
addProviderKey,
|
|
8
8
|
createProvider,
|
|
9
9
|
listAllModels
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-2JBRLGKD.js";
|
|
11
11
|
import {
|
|
12
12
|
runLogin
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-L7L4RGKG.js";
|
|
14
14
|
import {
|
|
15
15
|
BUILTIN_AGENTS,
|
|
16
16
|
buildSystemPrompt,
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
} from "./chunk-ELML4KVQ.js";
|
|
27
27
|
import {
|
|
28
28
|
providerName
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-U7DLMVPZ.js";
|
|
30
30
|
import {
|
|
31
31
|
Session,
|
|
32
32
|
pruneSessions
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
bccliHome,
|
|
37
37
|
loadConfig,
|
|
38
38
|
resolveModel
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-RZUJYAQV.js";
|
|
40
40
|
|
|
41
41
|
// src/tools/index.ts
|
|
42
42
|
import { z as z8 } from "zod";
|
|
@@ -521,6 +521,71 @@ function toolDefinitions(tools) {
|
|
|
521
521
|
});
|
|
522
522
|
}
|
|
523
523
|
|
|
524
|
+
// src/textToolCalls.ts
|
|
525
|
+
var PAIR = /(?:<arg_key>\s*|`)?([A-Za-z_]\w*)(?:\s*<\/arg_key>)?`?\s*(?:=\s*`|<arg_value>)([\s\S]*?)<\/arg_value>/g;
|
|
526
|
+
var CALL_START = /<tool_call>|\n?[^\n]*\bTool call list\b[^\n]*|\n\s*\d+\.\s*Tool:/g;
|
|
527
|
+
function coerce(value, type) {
|
|
528
|
+
const types = Array.isArray(type) ? type : [type];
|
|
529
|
+
const v = value.trim();
|
|
530
|
+
if ((types.includes("integer") || types.includes("number")) && v !== "" && !Number.isNaN(Number(v))) return Number(v);
|
|
531
|
+
if (types.includes("boolean") && /^(true|false)$/i.test(v)) return v.toLowerCase() === "true";
|
|
532
|
+
if ((types.includes("object") || types.includes("array")) && /^[[{]/.test(v)) {
|
|
533
|
+
try {
|
|
534
|
+
return JSON.parse(v);
|
|
535
|
+
} catch {
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return value.replace(/^\n+|\n+$/g, "");
|
|
539
|
+
}
|
|
540
|
+
function pickTool(name, keys, tools) {
|
|
541
|
+
const named = name && tools.find((t) => t.function.name === name);
|
|
542
|
+
if (named) return named;
|
|
543
|
+
let best;
|
|
544
|
+
let bestScore = 0;
|
|
545
|
+
let tie = false;
|
|
546
|
+
for (const t of tools) {
|
|
547
|
+
const params = t.function.parameters;
|
|
548
|
+
const props = Object.keys(params.properties ?? {});
|
|
549
|
+
if (!(params.required ?? []).every((r) => keys.includes(r))) continue;
|
|
550
|
+
const score = keys.filter((k) => props.includes(k)).length;
|
|
551
|
+
if (score > bestScore) [best, bestScore, tie] = [t, score, false];
|
|
552
|
+
else if (score === bestScore && score > 0) tie = true;
|
|
553
|
+
}
|
|
554
|
+
return tie ? void 0 : best;
|
|
555
|
+
}
|
|
556
|
+
function recoverTextToolCalls(text, tools) {
|
|
557
|
+
if (!/<\/arg_value>|<tool_call>/.test(text)) return null;
|
|
558
|
+
const starts = [...text.matchAll(CALL_START)].map((m) => m.index ?? 0);
|
|
559
|
+
const firstPair = text.search(PAIR);
|
|
560
|
+
const cut = Math.min(...[...starts, firstPair].filter((i) => i >= 0));
|
|
561
|
+
const bounds = starts.filter((i) => i >= cut);
|
|
562
|
+
if (!bounds.length || bounds[0] > cut) bounds.unshift(cut);
|
|
563
|
+
const toolCalls = [];
|
|
564
|
+
bounds.forEach((start, n) => {
|
|
565
|
+
const segment = text.slice(start, bounds[n + 1] ?? text.length);
|
|
566
|
+
const name = /<tool_call>\s*([A-Za-z_][\w.-]*)/.exec(segment)?.[1] ?? /Tool:\s*`?([A-Za-z_][\w.-]*)`?/.exec(segment)?.[1];
|
|
567
|
+
let args = [];
|
|
568
|
+
const flush = () => {
|
|
569
|
+
if (!args.length) return;
|
|
570
|
+
const keys = args.map(([k]) => k);
|
|
571
|
+
const tool = pickTool(name, keys, tools);
|
|
572
|
+
if (tool) {
|
|
573
|
+
const props = tool.function.parameters.properties ?? {};
|
|
574
|
+
const input = Object.fromEntries(args.filter(([k]) => k in props).map(([k, v]) => [k, coerce(v, props[k].type)]));
|
|
575
|
+
toolCalls.push({ id: `text_call_${toolCalls.length}`, name: tool.function.name, arguments: JSON.stringify(input) });
|
|
576
|
+
}
|
|
577
|
+
args = [];
|
|
578
|
+
};
|
|
579
|
+
for (const m of segment.matchAll(PAIR)) {
|
|
580
|
+
if (args.some(([k]) => k === m[1])) flush();
|
|
581
|
+
args.push([m[1], m[2]]);
|
|
582
|
+
}
|
|
583
|
+
flush();
|
|
584
|
+
});
|
|
585
|
+
if (!toolCalls.length) return null;
|
|
586
|
+
return { text: text.slice(0, cut).trim(), toolCalls };
|
|
587
|
+
}
|
|
588
|
+
|
|
524
589
|
// src/agent.ts
|
|
525
590
|
var estimateTokens = (text) => Math.ceil(text.length / 4);
|
|
526
591
|
var COMPACT_PROMPT = "Summarize the conversation so far for yourself so you can continue the work with no other context. Include: the user goals, decisions made, files touched and their current state, commands run and results, the task in progress and the next step. Be concise but complete.";
|
|
@@ -615,12 +680,19 @@ ${completion.text}` });
|
|
|
615
680
|
await this.compact(signal);
|
|
616
681
|
this.push({ role: "user", content: `Lanjutkan tugas ini sesuai ringkasan di atas: ${text}` });
|
|
617
682
|
}
|
|
618
|
-
|
|
683
|
+
let completion = await this.provider.chat({
|
|
619
684
|
messages: [{ role: "system", content: this.systemPrompt }, ...this.messages],
|
|
620
685
|
tools: this.turnDefinitions,
|
|
621
686
|
signal,
|
|
622
687
|
onText: (delta) => this.onEvent({ type: "text", delta })
|
|
623
688
|
});
|
|
689
|
+
if (!completion.toolCalls.length && completion.finishReason !== "repetition") {
|
|
690
|
+
const recovered = recoverTextToolCalls(completion.text, this.turnDefinitions);
|
|
691
|
+
if (recovered) {
|
|
692
|
+
completion = { ...completion, ...recovered };
|
|
693
|
+
this.onEvent({ type: "textReplace", text: recovered.text });
|
|
694
|
+
}
|
|
695
|
+
}
|
|
624
696
|
const usage = completion.usage ?? {
|
|
625
697
|
inputTokens: estimateTokens(this.systemPrompt) + estimateTokens(JSON.stringify(this.messages)),
|
|
626
698
|
outputTokens: estimateTokens(completion.text) + (completion.toolCalls.length ? estimateTokens(JSON.stringify(completion.toolCalls)) : 0)
|
|
@@ -1173,6 +1245,8 @@ function createRuntime(opts) {
|
|
|
1173
1245
|
resume(previous) {
|
|
1174
1246
|
session = previous;
|
|
1175
1247
|
agent.load(previous.load());
|
|
1248
|
+
agent.totalUsage = { inputTokens: 0, outputTokens: 0 };
|
|
1249
|
+
todos.set([]);
|
|
1176
1250
|
checkpoints.clear();
|
|
1177
1251
|
},
|
|
1178
1252
|
newSession() {
|
|
@@ -1198,7 +1272,7 @@ function createRuntime(opts) {
|
|
|
1198
1272
|
}
|
|
1199
1273
|
|
|
1200
1274
|
// src/version.ts
|
|
1201
|
-
var VERSION = true ? "0.4.0-beta.
|
|
1275
|
+
var VERSION = true ? "0.4.0-beta.3" : "0.0.0-dev";
|
|
1202
1276
|
|
|
1203
1277
|
// src/cli.ts
|
|
1204
1278
|
async function main() {
|
|
@@ -1214,13 +1288,13 @@ async function main() {
|
|
|
1214
1288
|
const cwd = process.cwd();
|
|
1215
1289
|
if (args.command === "login") return runLogin(args.loginProvider ?? "bc-cloud", cwd);
|
|
1216
1290
|
if (args.command === "mcp") {
|
|
1217
|
-
const { runMcpCommand } = await import("./mcpCli-
|
|
1218
|
-
const { readSecret } = await import("./login-
|
|
1291
|
+
const { runMcpCommand } = await import("./mcpCli-Q2H63ISP.js");
|
|
1292
|
+
const { readSecret } = await import("./login-KHU7ONSR.js");
|
|
1219
1293
|
return runMcpCommand(args, { env: process.env, cwd, out: (s) => console.log(s), err: (s) => console.error(s), readSecret });
|
|
1220
1294
|
}
|
|
1221
1295
|
if (args.command === "provider") {
|
|
1222
|
-
const { runProviderCommand } = await import("./providerCli-
|
|
1223
|
-
const { readSecret } = await import("./login-
|
|
1296
|
+
const { runProviderCommand } = await import("./providerCli-G3X7KENO.js");
|
|
1297
|
+
const { readSecret } = await import("./login-KHU7ONSR.js");
|
|
1224
1298
|
return runProviderCommand(args, { env: process.env, cwd, out: (s) => console.log(s), err: (s) => console.error(s), readSecret });
|
|
1225
1299
|
}
|
|
1226
1300
|
if (args.command === "models") {
|
|
@@ -1237,7 +1311,7 @@ async function main() {
|
|
|
1237
1311
|
}
|
|
1238
1312
|
if (args.print) {
|
|
1239
1313
|
if (!args.prompt) throw new ConfigError('Mode -p butuh tugas, contoh: bccli -p "jelaskan repo ini"');
|
|
1240
|
-
const { serversToStart } = await import("./config-
|
|
1314
|
+
const { serversToStart } = await import("./config-NPSYFAFD.js");
|
|
1241
1315
|
const plan = serversToStart(rt.home, cwd);
|
|
1242
1316
|
for (const s of plan.needTrust) {
|
|
1243
1317
|
console.error(`Server MCP project "${s.name}" dilewati (belum diizinkan; jalankan bccli interaktif sekali untuk menyetujuinya).`);
|
|
@@ -1250,7 +1324,7 @@ async function main() {
|
|
|
1250
1324
|
}
|
|
1251
1325
|
}
|
|
1252
1326
|
if (!process.stdin.isTTY) throw new ConfigError('Mode interaktif butuh terminal. Untuk skrip/CI pakai: bccli -p "tugas"');
|
|
1253
|
-
const { startInteractive } = await import("./ui-
|
|
1327
|
+
const { startInteractive } = await import("./ui-A32PTF4F.js");
|
|
1254
1328
|
return startInteractive(rt, { initialPrompt: args.prompt, resume: args.resume, version: VERSION });
|
|
1255
1329
|
}
|
|
1256
1330
|
main().then(
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
import {
|
|
3
3
|
CATALOG,
|
|
4
4
|
fillTemplate
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-UESQNU4A.js";
|
|
6
6
|
import {
|
|
7
7
|
addGlobalServer,
|
|
8
8
|
globalMcpPath,
|
|
9
9
|
readMcpFile,
|
|
10
10
|
removeGlobalServer
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-NGLKANMD.js";
|
|
12
12
|
import {
|
|
13
13
|
bccliHome
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-RZUJYAQV.js";
|
|
15
15
|
|
|
16
16
|
// src/mcpCli.ts
|
|
17
17
|
async function runMcpCommand(args, deps) {
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import {
|
|
3
3
|
addProviderKey,
|
|
4
4
|
runProviderCommand
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-2JBRLGKD.js";
|
|
6
|
+
import "./chunk-U7DLMVPZ.js";
|
|
7
|
+
import "./chunk-RZUJYAQV.js";
|
|
8
8
|
export {
|
|
9
9
|
addProviderKey,
|
|
10
10
|
runProviderCommand
|
|
@@ -13,31 +13,31 @@ import {
|
|
|
13
13
|
hasKey,
|
|
14
14
|
providerName,
|
|
15
15
|
writeGlobalConfig
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-U7DLMVPZ.js";
|
|
17
17
|
import {
|
|
18
18
|
Session
|
|
19
19
|
} from "./chunk-SJH3YLW4.js";
|
|
20
20
|
import {
|
|
21
21
|
CATALOG,
|
|
22
22
|
fillTemplate
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-UESQNU4A.js";
|
|
24
24
|
import {
|
|
25
25
|
addGlobalServer,
|
|
26
26
|
globalMcpPath,
|
|
27
27
|
readMcpFile,
|
|
28
28
|
removeGlobalServer
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-NGLKANMD.js";
|
|
30
30
|
import {
|
|
31
31
|
PRESETS,
|
|
32
32
|
removeCredential
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-RZUJYAQV.js";
|
|
34
34
|
|
|
35
35
|
// src/ui/index.tsx
|
|
36
36
|
import { render } from "ink";
|
|
37
37
|
|
|
38
38
|
// src/ui/App.tsx
|
|
39
|
-
import {
|
|
40
|
-
import { mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
39
|
+
import { execFile as execFile2 } from "child_process";
|
|
40
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
41
41
|
import { homedir } from "os";
|
|
42
42
|
import { dirname, join, relative, resolve } from "path";
|
|
43
43
|
import { Box as Box13, Static, Text as Text15, useApp, useInput as useInput9 } from "ink";
|
|
@@ -90,13 +90,19 @@ function osc52(text, inTmux) {
|
|
|
90
90
|
|
|
91
91
|
// src/slash/diff.ts
|
|
92
92
|
import { execFile } from "child_process";
|
|
93
|
-
function git(cwd, args) {
|
|
93
|
+
function git(cwd, args, bin = "git") {
|
|
94
94
|
return new Promise((resolve2) => {
|
|
95
|
-
execFile(
|
|
95
|
+
execFile(
|
|
96
|
+
bin,
|
|
97
|
+
args,
|
|
98
|
+
{ cwd, maxBuffer: 20 * 1024 * 1024 },
|
|
99
|
+
(error, stdout, stderr) => resolve2({ ok: !error, out: error ? stderr : stdout, missing: error?.code === "ENOENT" })
|
|
100
|
+
);
|
|
96
101
|
});
|
|
97
102
|
}
|
|
98
|
-
async function gitDiff(cwd, maxLines = 400) {
|
|
99
|
-
const inside = await git(cwd, ["rev-parse", "--is-inside-work-tree"]);
|
|
103
|
+
async function gitDiff(cwd, maxLines = 400, bin = "git") {
|
|
104
|
+
const inside = await git(cwd, ["rev-parse", "--is-inside-work-tree"], bin);
|
|
105
|
+
if (inside.missing) return "git tidak terpasang atau tidak ada di PATH, jadi /diff tidak bisa dipakai.";
|
|
100
106
|
if (!inside.ok || inside.out.trim() !== "true") return "Folder ini bukan repository git, jadi tidak ada diff.";
|
|
101
107
|
const base = (await git(cwd, ["rev-parse", "--verify", "-q", "HEAD"])).ok ? "HEAD" : "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
102
108
|
const [stat, diff, untracked] = await Promise.all([
|
|
@@ -117,13 +123,17 @@ async function doctorText(input, deps) {
|
|
|
117
123
|
const major = Number(deps.nodeVersion.replace(/^v/, "").split(".")[0]);
|
|
118
124
|
const lines = [major >= 22 ? `\u2713 Node ${deps.nodeVersion}` : `\u2717 Node ${deps.nodeVersion} (butuh \u2265 22)`];
|
|
119
125
|
for (const p of input.providers) lines.push(p.ready ? `\u2713 ${p.name}` : `\u25CB ${p.name} (belum ada key)`);
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
126
|
+
const cmds = ["rg", "git", "npx", "uvx"];
|
|
127
|
+
const [connection, found] = await Promise.all([
|
|
128
|
+
deps.listModels().then(
|
|
129
|
+
(models) => `\u2713 Koneksi ${input.activeProvider} (${models.length} model)`,
|
|
130
|
+
(error) => `\u2717 Koneksi ${input.activeProvider}: ${error.message}`
|
|
131
|
+
),
|
|
132
|
+
Promise.all(cmds.map(async (cmd) => deps.which(cmd)))
|
|
133
|
+
]);
|
|
134
|
+
lines.push(connection);
|
|
125
135
|
for (const m of input.mcp) lines.push(m.status === "error" ? `\u2717 MCP ${m.name}: ${m.error ?? ""}` : `\u2713 MCP ${m.name} (${m.status})`);
|
|
126
|
-
for (const
|
|
136
|
+
for (const [i, cmd] of cmds.entries()) lines.push(`${found[i] ? "\u2713" : "\u2717"} ${cmd}`);
|
|
127
137
|
return lines.join("\n");
|
|
128
138
|
}
|
|
129
139
|
|
|
@@ -186,6 +196,10 @@ function appendMemory(file, text) {
|
|
|
186
196
|
appendFileSync(file, `${prefix}- ${text.trim()}
|
|
187
197
|
`);
|
|
188
198
|
}
|
|
199
|
+
function parseMemoryArgs(args) {
|
|
200
|
+
const match = /^global(?:\s+|$)/i.exec(args.trim());
|
|
201
|
+
return match ? { global: true, text: args.trim().slice(match[0].length).trim() } : { global: false, text: args.trim() };
|
|
202
|
+
}
|
|
189
203
|
|
|
190
204
|
// src/ui/Markdown.tsx
|
|
191
205
|
import { Box, Text } from "ink";
|
|
@@ -833,6 +847,7 @@ function App({ runtime, initialPrompt, version }) {
|
|
|
833
847
|
const chosen = others.find((s) => s.session.file === id);
|
|
834
848
|
if (!chosen) return;
|
|
835
849
|
runtime.resume(chosen.session);
|
|
850
|
+
setTokens(0);
|
|
836
851
|
const tail = runtime.agent.messages.filter((m) => (m.role === "user" || m.role === "assistant") && m.content).slice(-3);
|
|
837
852
|
notice(
|
|
838
853
|
[`Melanjutkan sesi (${runtime.agent.messages.length} pesan):`, ...tail.map((m) => `${m.role === "user" ? ">" : "\u25CF"} ${String(m.content).slice(0, 200)}`)].join(
|
|
@@ -874,7 +889,7 @@ function App({ runtime, initialPrompt, version }) {
|
|
|
874
889
|
activeProvider: runtime.providerLabel()
|
|
875
890
|
},
|
|
876
891
|
{
|
|
877
|
-
which: (cmd) =>
|
|
892
|
+
which: (cmd) => new Promise((done) => execFile2(process.platform === "win32" ? "where" : "which", [cmd], (error) => done(!error))),
|
|
878
893
|
nodeVersion: process.version,
|
|
879
894
|
listModels: () => runtime.agent.provider.listModels()
|
|
880
895
|
}
|
|
@@ -910,8 +925,11 @@ function App({ runtime, initialPrompt, version }) {
|
|
|
910
925
|
notice(files.length ? files.map((f) => `${f.path} (${f.lines} baris)`).join("\n") : "Belum ada AGENTS.md / BCCLI.md. Tambah dengan /memory <teks>.");
|
|
911
926
|
return;
|
|
912
927
|
}
|
|
913
|
-
const global =
|
|
914
|
-
|
|
928
|
+
const { global, text } = parseMemoryArgs(args);
|
|
929
|
+
if (!text) {
|
|
930
|
+
notice("Tulis teksnya: /memory global <teks>.", "warn");
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
915
933
|
const file = global ? join(runtime.home, "BCCLI.md") : join(runtime.cwd, "AGENTS.md");
|
|
916
934
|
try {
|
|
917
935
|
mkdirSync(dirname(file), { recursive: true });
|
|
@@ -937,15 +955,27 @@ function App({ runtime, initialPrompt, version }) {
|
|
|
937
955
|
return;
|
|
938
956
|
}
|
|
939
957
|
runtime.setModel(runtime.modelRef);
|
|
958
|
+
const envName = runtime.config.providers[id]?.apiKeyEnv;
|
|
959
|
+
if (envName && runtime.env[envName]) {
|
|
960
|
+
notice(`Key ${runtime.providerLabel()} tersimpan, tapi ${envName} di environment lebih diutamakan dan tetap dipakai.`, "warn");
|
|
961
|
+
return;
|
|
962
|
+
}
|
|
940
963
|
notice(`Key ${runtime.providerLabel()} tersimpan \xB7 ${result.models} model.`);
|
|
941
964
|
return;
|
|
942
965
|
}
|
|
943
966
|
case "logout": {
|
|
944
967
|
const id = runtime.modelRef.slice(0, runtime.modelRef.indexOf("/"));
|
|
945
|
-
removeCredential(id, runtime.env);
|
|
968
|
+
const removed = removeCredential(id, runtime.env);
|
|
946
969
|
const envName = runtime.config.providers[id]?.apiKeyEnv;
|
|
947
970
|
if (envName && runtime.env[envName]) {
|
|
948
|
-
notice(
|
|
971
|
+
notice(
|
|
972
|
+
`${removed ? "Key tersimpan dihapus, tapi" : "Tidak ada key tersimpan;"} ${envName} masih ada di environment dan tetap dipakai.`,
|
|
973
|
+
"warn"
|
|
974
|
+
);
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
if (!removed) {
|
|
978
|
+
notice(`Tidak ada key tersimpan untuk ${runtime.providerLabel()}.`);
|
|
949
979
|
return;
|
|
950
980
|
}
|
|
951
981
|
try {
|
|
@@ -996,6 +1026,13 @@ function App({ runtime, initialPrompt, version }) {
|
|
|
996
1026
|
case "export": {
|
|
997
1027
|
const name2 = args || `bccli-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16).replace(/[:T]/g, "-")}.md`;
|
|
998
1028
|
const file = resolve(runtime.cwd, name2);
|
|
1029
|
+
if (existsSync2(file)) {
|
|
1030
|
+
const answer2 = await ask(`${file} sudah ada. Timpa? (y/N)`);
|
|
1031
|
+
if (!answer2 || !/^y(a|es)?$/i.test(answer2.trim())) {
|
|
1032
|
+
notice("Ekspor dibatalkan.");
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
999
1036
|
try {
|
|
1000
1037
|
writeFileSync(file, exportMarkdown(runtime.agent.messages, `Sesi BCCLI ${runtime.startedAt.toLocaleString("id-ID")}`));
|
|
1001
1038
|
} catch (error) {
|
|
@@ -1219,7 +1256,7 @@ async function startInteractive(rt, opts) {
|
|
|
1219
1256
|
if (chosen) rt.resume(chosen.session);
|
|
1220
1257
|
}
|
|
1221
1258
|
}
|
|
1222
|
-
const { serversToStart, setProjectTrust } = await import("./config-
|
|
1259
|
+
const { serversToStart, setProjectTrust } = await import("./config-NPSYFAFD.js");
|
|
1223
1260
|
const plan = serversToStart(rt.home, rt.cwd);
|
|
1224
1261
|
if (plan.needTrust.length) {
|
|
1225
1262
|
const { createInterface } = await import("readline/promises");
|