@botconnector/bccli 0.4.9 → 0.4.11
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/{acp-VEXFXT5P.js → acp-IPHWC6CO.js} +20 -5
- package/dist/acp.d.ts +17 -1
- package/dist/args-K5AJFDCZ.js +13 -0
- package/dist/args.d.ts +6 -0
- package/dist/budget.d.ts +4 -1
- package/dist/{chunk-2YG6L3YO.js → chunk-2DFY43RP.js} +2 -2
- package/dist/{chunk-KREGVQ63.js → chunk-6BXW7DGQ.js} +47 -18
- package/dist/{chunk-WSSJH765.js → chunk-6KZIOICR.js} +1 -1
- package/dist/{chunk-OUJRD7GG.js → chunk-6M5T7N7A.js} +8 -3
- package/dist/{chunk-DRJSANKH.js → chunk-C2FUGQJX.js} +9 -7
- package/dist/{chunk-OETLWNGS.js → chunk-F54FDJBD.js} +3 -3
- package/dist/{chunk-XCVYR5BK.js → chunk-G5TG43R4.js} +15 -1
- package/dist/{chunk-RRECJTOG.js → chunk-IHQGXDSG.js} +1 -1
- package/dist/{chunk-AWBEZY4D.js → chunk-JFYQCPXL.js} +7 -5
- package/dist/{chunk-6H6MPG7L.js → chunk-MBAMDCCV.js} +23 -9
- package/dist/{chunk-HCW67S5M.js → chunk-N6APAYSF.js} +1 -1
- package/dist/{chunk-TLCFBQ6E.js → chunk-NT2G5FSX.js} +2 -2
- package/dist/{chunk-CSIQDFEF.js → chunk-OAXD3X4R.js} +34 -50
- package/dist/{chunk-EOJNFGOP.js → chunk-OOOS5RCT.js} +98 -28
- package/dist/{chunk-Z2ELYJ5O.js → chunk-WSZF2CTU.js} +11 -4
- package/dist/{chunk-62UTSCWF.js → chunk-X24APPXO.js} +1 -1
- package/dist/{chunk-EAR2C6TS.js → chunk-XZ2TI5HV.js} +1 -1
- package/dist/{chunk-HPYI7L4W.js → chunk-Y6AZGGH7.js} +4 -4
- package/dist/{chunk-WFZKJY3R.js → chunk-YYTFJUO2.js} +3 -2
- package/dist/cli.js +40 -40
- package/dist/{completion-FETH4OGC.js → completion-QVDGAYUY.js} +1 -1
- package/dist/{config-KR5JI5RA.js → config-7TQ2KVVX.js} +3 -3
- package/dist/{config-RVMC23W3.js → config-RZY3XQZJ.js} +2 -2
- package/dist/{ghBotCli-Z6GWXCJ6.js → ghBotCli-HHJ4ANTH.js} +18 -16
- package/dist/{integrationCli-GPRR6J6W.js → integrationCli-LNNZDGZX.js} +40 -5
- package/dist/{login-DFSN77L3.js → login-MQEZGF2C.js} +6 -4
- package/dist/login.d.ts +2 -0
- package/dist/{mcpCli-64P3SVUE.js → mcpCli-CUIOYJ2A.js} +22 -7
- package/dist/{modelsCli-PFQY3G2Q.js → modelsCli-AAFHFI4H.js} +6 -6
- package/dist/{oauthLogin-NR22NWEV.js → oauthLogin-YFL2N4QA.js} +1 -1
- package/dist/{pluginsCli-UKRJ3AHN.js → pluginsCli-2QXXFLQA.js} +6 -2
- package/dist/{prCli-6FCI36AJ.js → prCli-KSA73RCS.js} +1 -1
- package/dist/print.d.ts +1 -0
- package/dist/providerCli-TXMVPSW5.js +15 -0
- package/dist/sdk.js +196 -87
- package/dist/{serve-PKUTKQQT.js → serve-EIQHEUYD.js} +3 -3
- package/dist/{serve-JYXPQAVY.js → serve-QYDBFTHC.js} +16 -16
- package/dist/session-CQCQ7KAP.js +12 -0
- package/dist/setup-DBSGMWHM.js +24 -0
- package/dist/setup.d.ts +5 -0
- package/dist/{statsCli-FR7GOKWO.js → statsCli-EHNJIQ2Z.js} +2 -2
- package/dist/tools/bash.d.ts +2 -0
- package/dist/{transferCli-GIE4MEJ4.js → transferCli-IXJ2O74Q.js} +4 -4
- package/dist/{ui-NZVOCQSU.js → ui-A2LASWDK.js} +14 -14
- package/dist/{update-DIYDD7LT.js → update-TR62JV3A.js} +2 -2
- package/package.json +1 -1
- package/dist/args-6XMUZDAK.js +0 -13
- package/dist/providerCli-PBSZECBM.js +0 -15
- package/dist/session-XABMBIIF.js +0 -12
- package/dist/setup-CLPQLTM7.js +0 -24
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
t
|
|
4
|
+
} from "./chunk-G5TG43R4.js";
|
|
2
5
|
|
|
3
6
|
// src/acp.ts
|
|
4
7
|
var ACP_PROTOCOL_VERSION = 1;
|
|
@@ -175,16 +178,27 @@ function runAcp(input, output, opts) {
|
|
|
175
178
|
input.on("end", onEnd);
|
|
176
179
|
});
|
|
177
180
|
}
|
|
178
|
-
function
|
|
181
|
+
function planDecisionFrom(answer) {
|
|
182
|
+
if (answer === "no") return "no";
|
|
183
|
+
if (answer === "session") return "acceptEdits";
|
|
184
|
+
return "default";
|
|
185
|
+
}
|
|
186
|
+
function runtimeAgent(cwd, allowAll = false, deps = {}) {
|
|
179
187
|
let rt;
|
|
180
188
|
return {
|
|
181
189
|
async run(text, onChunk, signal, io) {
|
|
182
190
|
if (!rt) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
191
|
+
if (deps.createRuntime) rt = deps.createRuntime({ cwd, allowAll });
|
|
192
|
+
else {
|
|
193
|
+
const { parseCliArgs } = await import("./args-K5AJFDCZ.js");
|
|
194
|
+
const { createRuntime } = await import("./setup-DBSGMWHM.js");
|
|
195
|
+
rt = createRuntime({ cwd, args: { ...parseCliArgs([]), allowAll } });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (io && !allowAll) {
|
|
199
|
+
rt.agent.askPermission = (req) => io.askPermission(req);
|
|
200
|
+
rt.interaction.approvePlan = async (plan) => planDecisionFrom(await io.askPermission({ tool: "exit_plan", kind: "edit", target: t("plan"), preview: plan }));
|
|
186
201
|
}
|
|
187
|
-
if (io && !allowAll) rt.agent.askPermission = (req) => io.askPermission(req);
|
|
188
202
|
let turnError;
|
|
189
203
|
rt.agent.onEvent = (event) => {
|
|
190
204
|
if (event.type === "text") onChunk(event.delta);
|
|
@@ -201,6 +215,7 @@ function runtimeAgent(cwd, allowAll = false) {
|
|
|
201
215
|
}
|
|
202
216
|
export {
|
|
203
217
|
ACP_PROTOCOL_VERSION,
|
|
218
|
+
planDecisionFrom,
|
|
204
219
|
runAcp,
|
|
205
220
|
runtimeAgent
|
|
206
221
|
};
|
package/dist/acp.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { PermissionAnswer, PermissionAsk } from './agent';
|
|
2
|
+
import type { Runtime } from './setup';
|
|
3
|
+
import type { PlanDecision } from './tools/plan';
|
|
2
4
|
export declare const ACP_PROTOCOL_VERSION = 1;
|
|
3
5
|
/** What the agent reports to the editor besides text: tool calls, and permission questions the editor answers. */
|
|
4
6
|
export interface AcpIo {
|
|
@@ -21,8 +23,22 @@ export declare function runAcp(input: NodeJS.ReadableStream, output: NodeJS.Writ
|
|
|
21
23
|
cwd: string;
|
|
22
24
|
newAgent: (cwd: string) => AcpAgent;
|
|
23
25
|
}): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* A plan has no ACP primitive of its own, so it is asked through session/request_permission.
|
|
28
|
+
* 'yes' approves and keeps the normal per-tool prompts; 'session' also stops asking for edits
|
|
29
|
+
* for the rest of the session (acceptEdits). A plan answer never turns on allowAll, which stays
|
|
30
|
+
* an explicit user choice.
|
|
31
|
+
*/
|
|
32
|
+
export declare function planDecisionFrom(answer: PermissionAnswer): PlanDecision;
|
|
33
|
+
export interface AcpRuntimeDeps {
|
|
34
|
+
/** Test seam: build the runtime without reading config/plugins. */
|
|
35
|
+
createRuntime?: (opts: {
|
|
36
|
+
cwd: string;
|
|
37
|
+
allowAll: boolean;
|
|
38
|
+
}) => Runtime;
|
|
39
|
+
}
|
|
24
40
|
/**
|
|
25
41
|
* Production agent: one bccli runtime per ACP session. Permission questions go to the editor
|
|
26
42
|
* (session/request_permission); `allowAll` skips them and runs everything unasked.
|
|
27
43
|
*/
|
|
28
|
-
export declare function runtimeAgent(cwd: string, allowAll?: boolean): AcpAgent;
|
|
44
|
+
export declare function runtimeAgent(cwd: string, allowAll?: boolean, deps?: AcpRuntimeDeps): AcpAgent;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
helpText,
|
|
4
|
+
parseCliArgs
|
|
5
|
+
} from "./chunk-WSZF2CTU.js";
|
|
6
|
+
import "./chunk-YYTFJUO2.js";
|
|
7
|
+
import "./chunk-XZ2TI5HV.js";
|
|
8
|
+
import "./chunk-C2FUGQJX.js";
|
|
9
|
+
import "./chunk-G5TG43R4.js";
|
|
10
|
+
export {
|
|
11
|
+
helpText,
|
|
12
|
+
parseCliArgs
|
|
13
|
+
};
|
package/dist/args.d.ts
CHANGED
|
@@ -5,6 +5,10 @@ export interface CliArgs {
|
|
|
5
5
|
command: 'run' | 'login' | 'models' | 'update' | 'acp' | 'serve' | 'plugins' | 'gh-bot' | 'provider' | 'mcp' | 'connect' | 'disconnect' | 'integrations' | 'completion' | 'stats' | 'export' | 'import' | 'pr';
|
|
6
6
|
subArgs: string[];
|
|
7
7
|
url?: string;
|
|
8
|
+
/** `bccli mcp add <name> --command <cmd>`: server stdio, bukan HTTP. */
|
|
9
|
+
stdioCommand?: string;
|
|
10
|
+
/** Argumen untuk server stdio (dipisah spasi). */
|
|
11
|
+
stdioArgs?: string;
|
|
8
12
|
name?: string;
|
|
9
13
|
keyEnv?: string;
|
|
10
14
|
values: string[];
|
|
@@ -16,6 +20,8 @@ export interface CliArgs {
|
|
|
16
20
|
continue: boolean;
|
|
17
21
|
resume: boolean;
|
|
18
22
|
allowAll: boolean;
|
|
23
|
+
/** `-p` untuk CI: kegagalan tool apa pun membuat exit code 1. */
|
|
24
|
+
failOnToolError: boolean;
|
|
19
25
|
allowedTools: string[];
|
|
20
26
|
worktree?: string;
|
|
21
27
|
outputFormat?: 'text' | 'json' | 'stream-json';
|
package/dist/budget.d.ts
CHANGED
|
@@ -3,19 +3,22 @@ export interface UsageCap {
|
|
|
3
3
|
tokens?: number;
|
|
4
4
|
/** Total USD per session; requires `prices`. */
|
|
5
5
|
usd?: number;
|
|
6
|
-
/** USD per 1M tokens
|
|
6
|
+
/** USD per 1M tokens. `cachedInput` is optional: without it cache reads cost the input price. */
|
|
7
7
|
prices?: Record<string, {
|
|
8
8
|
input: number;
|
|
9
9
|
output: number;
|
|
10
|
+
cachedInput?: number;
|
|
10
11
|
}>;
|
|
11
12
|
}
|
|
12
13
|
export declare function priceFor(cap: UsageCap, model: string): {
|
|
13
14
|
input: number;
|
|
14
15
|
output: number;
|
|
16
|
+
cachedInput?: number;
|
|
15
17
|
} | undefined;
|
|
16
18
|
export declare function budgetStatus(usage: {
|
|
17
19
|
inputTokens: number;
|
|
18
20
|
outputTokens: number;
|
|
21
|
+
cachedInputTokens?: number;
|
|
19
22
|
}, cap: UsageCap | undefined, model: string): {
|
|
20
23
|
kind: 'tokens' | 'usd';
|
|
21
24
|
used: number;
|
|
@@ -24,52 +24,52 @@ import {
|
|
|
24
24
|
resolvePath,
|
|
25
25
|
runHooks,
|
|
26
26
|
validateGlobPattern
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-OAXD3X4R.js";
|
|
28
28
|
import {
|
|
29
29
|
createWebSearchTool,
|
|
30
30
|
fetchTool
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-6KZIOICR.js";
|
|
32
32
|
import {
|
|
33
33
|
defineTool
|
|
34
34
|
} from "./chunk-PSO7R5AD.js";
|
|
35
35
|
import {
|
|
36
36
|
Permissions
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-YYTFJUO2.js";
|
|
38
38
|
import {
|
|
39
39
|
addProviderKey
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-JFYQCPXL.js";
|
|
41
41
|
import {
|
|
42
42
|
listAllModels
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-Y6AZGGH7.js";
|
|
44
44
|
import {
|
|
45
45
|
providerName
|
|
46
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-X24APPXO.js";
|
|
47
47
|
import {
|
|
48
48
|
FileOAuthProvider,
|
|
49
49
|
hasStoredTokens
|
|
50
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-IHQGXDSG.js";
|
|
51
51
|
import {
|
|
52
52
|
Session
|
|
53
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-F54FDJBD.js";
|
|
54
54
|
import {
|
|
55
55
|
completionAssistantMessage,
|
|
56
56
|
createProvider,
|
|
57
57
|
hasPendingProgrammaticReplay,
|
|
58
58
|
imageUserMessage,
|
|
59
59
|
withFallbackProvider
|
|
60
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-OOOS5RCT.js";
|
|
61
61
|
import {
|
|
62
62
|
expandEnvVars
|
|
63
|
-
} from "./chunk-
|
|
63
|
+
} from "./chunk-2DFY43RP.js";
|
|
64
64
|
import {
|
|
65
65
|
ConfigError,
|
|
66
66
|
bccliHome,
|
|
67
67
|
loadConfig,
|
|
68
68
|
resolveModel
|
|
69
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-C2FUGQJX.js";
|
|
70
70
|
import {
|
|
71
71
|
t
|
|
72
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-G5TG43R4.js";
|
|
73
73
|
|
|
74
74
|
// src/textToolCalls.ts
|
|
75
75
|
var PAIR = /(?:<arg_key>\s*|`)?([A-Za-z_]\w*)(?:\s*<\/arg_key>)?`?\s*(?:=\s*`|<arg_value>)([\s\S]*?)<\/arg_value>/g;
|
|
@@ -622,6 +622,12 @@ async function readTextResult(input, buffer) {
|
|
|
622
622
|
const lines = buffer.toString("utf8").split(/\r?\n/);
|
|
623
623
|
const start = (input.offset ?? 1) - 1;
|
|
624
624
|
const limit = input.limit ?? 2e3;
|
|
625
|
+
if (start >= lines.length) {
|
|
626
|
+
return {
|
|
627
|
+
output: `offset ${input.offset} is past the end of ${input.path} (${lines.length} line(s)); start at 1.`,
|
|
628
|
+
isError: true
|
|
629
|
+
};
|
|
630
|
+
}
|
|
625
631
|
const requested = lines.slice(start, start + limit);
|
|
626
632
|
const shown = [];
|
|
627
633
|
let size = 0;
|
|
@@ -703,7 +709,9 @@ var writeTool = defineTool({
|
|
|
703
709
|
target: (input) => input.path,
|
|
704
710
|
async validate(input, ctx) {
|
|
705
711
|
const abs = resolvePath(ctx.cwd, input.path);
|
|
706
|
-
|
|
712
|
+
if (existsSync(abs) && !ctx.readFiles.has(abs)) return `${input.path} already exists. Read it with read before overwriting it.`;
|
|
713
|
+
if (isFileModifiedSinceRead(ctx.readFiles, abs)) return `${input.path} has been modified since it was last read. Read it again before overwriting it.`;
|
|
714
|
+
return void 0;
|
|
707
715
|
},
|
|
708
716
|
async preview(input, ctx) {
|
|
709
717
|
const abs = resolvePath(ctx.cwd, input.path);
|
|
@@ -715,6 +723,9 @@ var writeTool = defineTool({
|
|
|
715
723
|
if (existsSync(abs) && !ctx.readFiles.has(abs)) {
|
|
716
724
|
return { output: `${input.path} already exists. Read it with read before overwriting it.`, isError: true };
|
|
717
725
|
}
|
|
726
|
+
if (isFileModifiedSinceRead(ctx.readFiles, abs)) {
|
|
727
|
+
return { output: `${input.path} has been modified since it was last read. Read it again before overwriting it.`, isError: true };
|
|
728
|
+
}
|
|
718
729
|
try {
|
|
719
730
|
await ctx.checkpoint?.(abs);
|
|
720
731
|
await mkdir(dirname2(abs), { recursive: true });
|
|
@@ -824,7 +835,9 @@ function budgetStatus(usage, cap, model) {
|
|
|
824
835
|
if (cap.usd !== void 0) {
|
|
825
836
|
const price = priceFor(cap, model);
|
|
826
837
|
if (price) {
|
|
827
|
-
const
|
|
838
|
+
const cached = Math.max(0, Math.min(usage.cachedInputTokens ?? 0, usage.inputTokens));
|
|
839
|
+
const cachedRate = price.cachedInput ?? price.input;
|
|
840
|
+
const usd = ((usage.inputTokens - cached) * price.input + cached * cachedRate + usage.outputTokens * price.output) / 1e6;
|
|
828
841
|
if (usd >= cap.usd) return { kind: "usd", used: usd, limit: cap.usd };
|
|
829
842
|
}
|
|
830
843
|
}
|
|
@@ -2366,6 +2379,15 @@ function loadPlugins(specs, opts) {
|
|
|
2366
2379
|
|
|
2367
2380
|
// src/setup.ts
|
|
2368
2381
|
var TOOL_RULES = { bash: "bash", edit: "edit", write: "edit", fetch: "fetch" };
|
|
2382
|
+
var pluginCache = /* @__PURE__ */ new Map();
|
|
2383
|
+
function loadPluginsOnce(specs, opts) {
|
|
2384
|
+
const key = JSON.stringify({ specs, home: opts.home, cwd: opts.cwd });
|
|
2385
|
+
const cached = pluginCache.get(key);
|
|
2386
|
+
if (cached) return cached;
|
|
2387
|
+
const loaded = loadPlugins(specs, opts);
|
|
2388
|
+
pluginCache.set(key, loaded);
|
|
2389
|
+
return loaded;
|
|
2390
|
+
}
|
|
2369
2391
|
function lunaPtcAutoEnabled(env) {
|
|
2370
2392
|
const value = String(env.BCCLI_LUNA_PTC ?? "").trim().toLowerCase();
|
|
2371
2393
|
return !["0", "false", "off", "no"].includes(value);
|
|
@@ -2373,6 +2395,7 @@ function lunaPtcAutoEnabled(env) {
|
|
|
2373
2395
|
function createRuntime(opts) {
|
|
2374
2396
|
const env = opts.env ?? process.env;
|
|
2375
2397
|
const home = bccliHome(env);
|
|
2398
|
+
const persistSession = opts.persistSession !== false;
|
|
2376
2399
|
let config = loadConfig(opts.cwd, env);
|
|
2377
2400
|
let modelRef = opts.args.model ?? config.model;
|
|
2378
2401
|
let reasoning = opts.args.reasoning ?? config.reasoning;
|
|
@@ -2461,7 +2484,7 @@ function createRuntime(opts) {
|
|
|
2461
2484
|
createRepoMapTool()
|
|
2462
2485
|
];
|
|
2463
2486
|
if (config.lsp?.servers.length) baseTools.push(createDiagnosticsTool(config.lsp.servers));
|
|
2464
|
-
const plugins =
|
|
2487
|
+
const plugins = loadPluginsOnce(config.plugins ?? [], {
|
|
2465
2488
|
home,
|
|
2466
2489
|
cwd: opts.cwd,
|
|
2467
2490
|
env,
|
|
@@ -2499,9 +2522,15 @@ function createRuntime(opts) {
|
|
|
2499
2522
|
usageCap: config.usageCap,
|
|
2500
2523
|
budgetModel: modelRef,
|
|
2501
2524
|
contextWindow: contextWindowFor(modelRef, env),
|
|
2502
|
-
onMessage: (m) =>
|
|
2503
|
-
|
|
2504
|
-
|
|
2525
|
+
onMessage: (m) => {
|
|
2526
|
+
if (persistSession) session.append(m);
|
|
2527
|
+
},
|
|
2528
|
+
onUsage: (u) => {
|
|
2529
|
+
if (persistSession) session.appendUsage({ ...u, model: modelRef });
|
|
2530
|
+
},
|
|
2531
|
+
onReset: () => {
|
|
2532
|
+
if (persistSession) session.reset();
|
|
2533
|
+
},
|
|
2505
2534
|
onTurnStart: () => {
|
|
2506
2535
|
for (let i = turnMarks.length - 1; i >= 0 && turnMarks[i] > agent.messages.length; i--) {
|
|
2507
2536
|
turnMarks.splice(i, 1);
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import {
|
|
3
3
|
runCommand,
|
|
4
4
|
runHooks
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-OAXD3X4R.js";
|
|
6
6
|
import {
|
|
7
7
|
ConfigError
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-C2FUGQJX.js";
|
|
9
9
|
import {
|
|
10
10
|
t
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-G5TG43R4.js";
|
|
12
12
|
|
|
13
13
|
// src/verify.ts
|
|
14
14
|
var MAX_VERIFY_ROUNDS = 2;
|
|
@@ -90,6 +90,7 @@ async function runPrint(rt, prompt, io = { out: process.stdout, err: process.std
|
|
|
90
90
|
case "toolEnd":
|
|
91
91
|
if (event.isError) io.err.write(` \u2717 ${event.output.split("\n")[0]}
|
|
92
92
|
`);
|
|
93
|
+
if (event.isError && opts.failOnToolError) failed = true;
|
|
93
94
|
if ((event.tool === "edit" || event.tool === "write") && !event.isError) edited = true;
|
|
94
95
|
if (format === "stream-json") emit({ type: "tool_result", tool: toolNames.get(event.id), isError: !!event.isError, output: event.output });
|
|
95
96
|
break;
|
|
@@ -125,6 +126,9 @@ async function runPrint(rt, prompt, io = { out: process.stdout, err: process.std
|
|
|
125
126
|
const controller = new AbortController();
|
|
126
127
|
const onSigint = () => controller.abort();
|
|
127
128
|
process.once("SIGINT", onSigint);
|
|
129
|
+
const onSigbreak = () => controller.abort();
|
|
130
|
+
const useSigbreak = process.platform === "win32";
|
|
131
|
+
if (useSigbreak) process.once("SIGBREAK", onSigbreak);
|
|
128
132
|
const start = await runHooks(rt.config.hooks, "SessionStart", {}, { env: rt.env, cwd: rt.cwd });
|
|
129
133
|
for (const warning of start.warnings) io.err.write(`${warning}
|
|
130
134
|
`);
|
|
@@ -153,6 +157,7 @@ async function runPrint(rt, prompt, io = { out: process.stdout, err: process.std
|
|
|
153
157
|
}
|
|
154
158
|
} finally {
|
|
155
159
|
process.off("SIGINT", onSigint);
|
|
160
|
+
if (useSigbreak) process.off("SIGBREAK", onSigbreak);
|
|
156
161
|
}
|
|
157
162
|
const stop = await runHooks(rt.config.hooks, "Stop", {}, { env: rt.env, cwd: rt.cwd });
|
|
158
163
|
for (const warning of stop.warnings) io.err.write(`${warning}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
t
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-G5TG43R4.js";
|
|
5
5
|
|
|
6
6
|
// src/config.ts
|
|
7
7
|
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
|
|
@@ -149,6 +149,9 @@ function resolvedUsageCap(global) {
|
|
|
149
149
|
if (!price || !nonNegative(price.input) || !nonNegative(price.output)) {
|
|
150
150
|
throw new ConfigError(t('usageCap.prices["{key}"] must have numeric input and output (USD per 1M tokens).', { key }));
|
|
151
151
|
}
|
|
152
|
+
if (price.cachedInput !== void 0 && !nonNegative(price.cachedInput)) {
|
|
153
|
+
throw new ConfigError(t('usageCap.prices["{key}"].cachedInput must be a number >= 0 (USD per 1M tokens).', { key }));
|
|
154
|
+
}
|
|
152
155
|
}
|
|
153
156
|
}
|
|
154
157
|
if (tokens === void 0 && usd === void 0) throw new ConfigError(t("usageCap needs at least one of: tokens, usd."));
|
|
@@ -236,7 +239,7 @@ function loadConfig(cwd, env = process.env) {
|
|
|
236
239
|
const project = readJsonConfig(join(cwd, ".bccli", "config.json"));
|
|
237
240
|
const known = { ...DEFAULT_CONFIG.providers, ...global.providers };
|
|
238
241
|
const fromProject = untrustedProviders(project, known);
|
|
239
|
-
const rawFallbacks =
|
|
242
|
+
const rawFallbacks = global.fallbackModels;
|
|
240
243
|
const fallbackModels = Array.isArray(rawFallbacks) ? rawFallbacks.filter((m) => typeof m === "string" && m.includes("/")) : void 0;
|
|
241
244
|
return {
|
|
242
245
|
model: project.model ?? global.model ?? DEFAULT_CONFIG.model,
|
|
@@ -305,10 +308,7 @@ function quarantineCorruptFile(path, error) {
|
|
|
305
308
|
}
|
|
306
309
|
function updateGlobalConfig(env, mutate) {
|
|
307
310
|
const path = globalConfigPath(env);
|
|
308
|
-
|
|
309
|
-
if (existsSync(path)) {
|
|
310
|
-
cfg = JSON.parse(readFileSync(path, "utf8"));
|
|
311
|
-
}
|
|
311
|
+
const cfg = existsSync(path) ? readJsonConfig(path) : {};
|
|
312
312
|
mutate(cfg);
|
|
313
313
|
mkdirSync(dirname(path), { recursive: true });
|
|
314
314
|
writeFileAtomic(path, `${JSON.stringify(cfg, null, 2)}
|
|
@@ -339,7 +339,9 @@ function resolveModel(config, modelRef, env = process.env) {
|
|
|
339
339
|
if (!provider) {
|
|
340
340
|
throw new ConfigError(t('Provider "{id}" is not in the config. Available: {list}', { id: providerId, list: Object.keys(config.providers).join(", ") }));
|
|
341
341
|
}
|
|
342
|
-
const
|
|
342
|
+
const untrusted = config.projectProviders?.includes(providerId) ?? false;
|
|
343
|
+
const stored = untrusted ? void 0 : readCredentials(env)[providerId];
|
|
344
|
+
const apiKey = stored || (provider.apiKeyEnv ? env[provider.apiKeyEnv] : void 0) || void 0;
|
|
343
345
|
if (provider.apiKeyEnv && !apiKey) {
|
|
344
346
|
throw new ConfigError(t("No API key for {id} yet. Run `bccli login` or set the env var {env}.", { id: providerId, env: provider.apiKeyEnv }));
|
|
345
347
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
contentText
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-OOOS5RCT.js";
|
|
5
5
|
|
|
6
6
|
// src/session.ts
|
|
7
7
|
import { randomBytes } from "crypto";
|
|
8
8
|
import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync } from "fs";
|
|
9
|
-
import { basename, join } from "path";
|
|
9
|
+
import { basename, dirname, join } from "path";
|
|
10
10
|
function projectDir(home, cwd) {
|
|
11
11
|
const slug = cwd.replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(-100) || "root";
|
|
12
12
|
return join(home, "sessions", slug);
|
|
@@ -32,7 +32,6 @@ var Session = class _Session {
|
|
|
32
32
|
}
|
|
33
33
|
static create(home, cwd, now = /* @__PURE__ */ new Date()) {
|
|
34
34
|
const dir = projectDir(home, cwd);
|
|
35
|
-
mkdirSync(dir, { recursive: true });
|
|
36
35
|
const stamp = now.toISOString().replace(/[:.]/g, "-");
|
|
37
36
|
return new _Session(join(dir, `${stamp}-${randomBytes(3).toString("hex")}.jsonl`));
|
|
38
37
|
}
|
|
@@ -49,6 +48,7 @@ var Session = class _Session {
|
|
|
49
48
|
return _Session.list(home, cwd)[0]?.session;
|
|
50
49
|
}
|
|
51
50
|
write(record) {
|
|
51
|
+
mkdirSync(dirname(this.file), { recursive: true });
|
|
52
52
|
appendFileSync(this.file, `${JSON.stringify(record)}
|
|
53
53
|
`);
|
|
54
54
|
}
|
|
@@ -136,6 +136,7 @@ Options:
|
|
|
136
136
|
-r, --resume pick a session to continue
|
|
137
137
|
--allow-all run all tools without asking for permission
|
|
138
138
|
--allowed-tools <a,b> tools allowed without asking: bash, edit, fetch
|
|
139
|
+
--fail-on-tool-error with -p: exit 1 when any tool call fails (for CI)
|
|
139
140
|
-w, --worktree <name> run in git worktree ../<repo>.worktrees/<name> (created, or reused if it exists)
|
|
140
141
|
--output-format <fmt> text | json | stream-json for -p (machine-readable output)
|
|
141
142
|
--permission-mode <mode> default | acceptEdits | plan | allowAll
|
|
@@ -166,6 +167,7 @@ Opsi:
|
|
|
166
167
|
-r, --resume pilih sesi untuk dilanjutkan
|
|
167
168
|
--allow-all jalankan semua alat tanpa minta izin
|
|
168
169
|
--allowed-tools <a,b> alat yang boleh tanpa izin: bash, edit, fetch
|
|
170
|
+
--fail-on-tool-error dengan -p: keluar 1 bila ada tool gagal (untuk CI)
|
|
169
171
|
-w, --worktree <name> jalankan di git worktree ../<repo>.worktrees/<name> (dibuat, atau dipakai ulang jika ada)
|
|
170
172
|
--output-format <fmt> text | json | stream-json untuk -p (output mesin)
|
|
171
173
|
--permission-mode <mode> default | acceptEdits | plan | allowAll
|
|
@@ -183,6 +185,7 @@ Opsi:
|
|
|
183
185
|
"usageCap.usd must be a number greater than 0.": "usageCap.usd harus angka lebih dari 0.",
|
|
184
186
|
"usageCap.usd requires usageCap.prices (USD per 1M tokens).": "usageCap.usd memerlukan usageCap.prices (USD per 1 juta token).",
|
|
185
187
|
'usageCap.prices["{key}"] must have numeric input and output (USD per 1M tokens).': 'usageCap.prices["{key}"] harus punya input dan output numerik (USD per 1 juta token).',
|
|
188
|
+
'usageCap.prices["{key}"].cachedInput must be a number >= 0 (USD per 1M tokens).': 'usageCap.prices["{key}"].cachedInput harus angka >= 0 (USD per 1 juta token).',
|
|
186
189
|
"usageCap needs at least one of: tokens, usd.": "usageCap memerlukan setidaknya salah satu dari: tokens, usd.",
|
|
187
190
|
'updateCheck must be "on" or "off".': 'updateCheck harus "on" atau "off".',
|
|
188
191
|
"repoMap must be true or false.": "repoMap harus true atau false.",
|
|
@@ -224,6 +227,7 @@ Opsi:
|
|
|
224
227
|
'npm removed the files of {name} but the config entry {entry} remains: {reason}. bccli will refuse to start while that entry is listed \u2014 fix the config error at {path}, then run "bccli plugins remove {name}" again (safe to repeat), or delete the entry manually.': 'npm sudah menghapus file {name} tetapi entry config {entry} tertinggal: {reason}. bccli menolak start selama entry itu terdaftar \u2014 perbaiki error config di {path}, lalu jalankan lagi "bccli plugins remove {name}" (aman diulang), atau hapus entry secara manual.',
|
|
225
228
|
"Skipping malformed entry: {entry} \u2014 fix it manually in {path}.": "Melewatkan entry rusak: {entry} \u2014 perbaiki manual di {path}.",
|
|
226
229
|
"No managed plugins to update.": "Tidak ada plugin terelola untuk diperbarui.",
|
|
230
|
+
"No plugins are installed. Install one with: bccli plugins install <spec>": "Belum ada plugin terpasang. Pasang dengan: bccli plugins install <spec>",
|
|
227
231
|
"update requires a plugin name or no argument: bccli plugins update [name]": "update butuh nama plugin atau tanpa argumen: bccli plugins update [nama]",
|
|
228
232
|
"{name} updated to latest.": "{name} diperbarui ke versi terbaru.",
|
|
229
233
|
"Use either --key or --logout, not both.": "Pakai --key atau --logout, jangan keduanya.",
|
|
@@ -281,6 +285,13 @@ Opsi:
|
|
|
281
285
|
"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}.",
|
|
282
286
|
"Error from the provider: {message}": "Error dari provider: {message}",
|
|
283
287
|
"Cannot connect to {url}: {error}": "Tidak bisa terhubung ke {url}: {error}",
|
|
288
|
+
"The API key was rejected. Check the key for this provider (bccli login) or its API key environment variable.": "Kunci API ditolak. Periksa kunci provider ini (bccli login) atau env var kuncinya.",
|
|
289
|
+
"Model or endpoint not found. Check the model name and the provider base URL.": "Model atau endpoint tidak ditemukan. Periksa nama model dan base URL provider.",
|
|
290
|
+
"Rate limit reached. Wait a moment, or switch model with /model.": "Batas laju tercapai. Tunggu sebentar, atau ganti model dengan /model.",
|
|
291
|
+
"The provider is having trouble. Try again, or switch model with /model.": "Penyedia sedang bermasalah. Coba lagi, atau ganti model dengan /model.",
|
|
292
|
+
"The provider did not respond within {seconds}s. Check the connection, then try again.": "Penyedia tidak merespons dalam {seconds} detik. Periksa koneksi, lalu coba lagi.",
|
|
293
|
+
"The provider stopped sending data for {seconds}s. The answer above may be incomplete; try again.": "Penyedia berhenti mengirim data selama {seconds} detik. Jawaban di atas mungkin tidak lengkap; coba lagi.",
|
|
294
|
+
"The response ended before it finished (no finish_reason or [DONE]). The answer above is incomplete; try again.": "Respons berakhir sebelum selesai (tanpa finish_reason atau [DONE]). Jawaban di atas tidak lengkap; coba lagi.",
|
|
284
295
|
"{status} from {url}: {message}": "{status} dari {url}: {message}",
|
|
285
296
|
"Request failed": "Permintaan gagal",
|
|
286
297
|
"Connection closed before the response finished.": "Koneksi ditutup sebelum respons selesai.",
|
|
@@ -305,7 +316,8 @@ Opsi:
|
|
|
305
316
|
"Usage: bccli mcp {action} <name>": "Pemakaian: bccli mcp {action} <nama>",
|
|
306
317
|
"{name} removed.": "{name} dihapus.",
|
|
307
318
|
"{name} installed ({url}).": "{name} terpasang ({url}).",
|
|
308
|
-
"{name}
|
|
319
|
+
"{name} installed (stdio: {command}).": "{name} terpasang (stdio: {command}).",
|
|
320
|
+
"{name} is not in the catalog. For another server use: bccli mcp add {name} --url <url> (HTTP) or bccli mcp add {name} --command <cmd> (stdio).": "{name} tidak ada di katalog. Untuk server lain pakai: bccli mcp add {name} --url <url> (HTTP) atau bccli mcp add {name} --command <cmd> (stdio).",
|
|
309
321
|
"{name} installed. Active in the next bccli session.": "{name} terpasang. Aktif di sesi bccli berikutnya.",
|
|
310
322
|
"control a browser (open pages, click, fill forms, screenshot)": "kontrol browser (buka halaman, klik, isi form, screenshot)",
|
|
311
323
|
"up-to-date library documentation": "dokumentasi library terbaru",
|
|
@@ -359,6 +371,8 @@ Opsi:
|
|
|
359
371
|
"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).",
|
|
360
372
|
"{target}: not managed by BCCLI yet.": "{target}: belum dikelola oleh BCCLI.",
|
|
361
373
|
"{target}: the BotConnector integration was removed and the config restored.": "{target}: integrasi BotConnector dilepas dan config dipulihkan.",
|
|
374
|
+
'{target}: {path} changed after BCCLI connected it, so the old backup was not copied over it. Remove the BCCLI part (provider "botconnector" or the marked block) from that file by hand.': '{target}: {path} berubah setelah BCCLI menyambungkannya, jadi backup lama tidak ditimpa ke file itu. Hapus bagian BCCLI (provider "botconnector" atau blok bertanda) dari file tersebut secara manual.',
|
|
375
|
+
"{target}: the BotConnector integration was removed. {n} file(s) were left untouched (see above); delete the BCCLI entries in them by hand for a clean removal.": "{target}: integrasi BotConnector dilepas. {n} file tidak disentuh (lihat di atas); hapus entri BCCLI di dalamnya secara manual agar bersih.",
|
|
362
376
|
"Custom commands:": "Perintah custom:",
|
|
363
377
|
"Skills:": "Skill:",
|
|
364
378
|
"shift+tab switch permission mode esc cancel the turn": "shift+tab ganti mode izin esc batalkan giliran",
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
listAllModels
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-Y6AZGGH7.js";
|
|
5
5
|
import {
|
|
6
6
|
hasKey,
|
|
7
7
|
providerName,
|
|
8
8
|
removeGlobalProvider,
|
|
9
9
|
writeGlobalConfig
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-X24APPXO.js";
|
|
11
11
|
import {
|
|
12
12
|
PRESETS,
|
|
13
13
|
loadConfig,
|
|
14
14
|
saveCredential
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-C2FUGQJX.js";
|
|
16
16
|
import {
|
|
17
17
|
t
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-G5TG43R4.js";
|
|
19
19
|
|
|
20
20
|
// src/providerCli.ts
|
|
21
21
|
async function addProviderKey(providerId, key, deps) {
|
|
@@ -49,7 +49,8 @@ async function runProviderCommand(args, deps) {
|
|
|
49
49
|
return 1;
|
|
50
50
|
}
|
|
51
51
|
const preset = PRESETS.find((p) => p.id === id);
|
|
52
|
-
|
|
52
|
+
const createdEntry = !preset && !config.providers[id];
|
|
53
|
+
if (createdEntry) {
|
|
53
54
|
if (!args.url) {
|
|
54
55
|
deps.err(t("{id} is not a preset. For a custom provider add --url <OpenAI-compatible base URL>.", { id }));
|
|
55
56
|
return 1;
|
|
@@ -63,6 +64,7 @@ async function runProviderCommand(args, deps) {
|
|
|
63
64
|
const key = needsKey ? await deps.readSecret(t("API key for {id} (leave empty if not needed): ", { id })) : "";
|
|
64
65
|
const result = await addProviderKey(id, key, deps);
|
|
65
66
|
if (!result.ok) {
|
|
67
|
+
if (createdEntry) removeGlobalProvider(id, deps.env);
|
|
66
68
|
deps.err(result.error);
|
|
67
69
|
return 1;
|
|
68
70
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
defaultOpen
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-IHQGXDSG.js";
|
|
5
5
|
import {
|
|
6
6
|
removeCredential,
|
|
7
7
|
saveCredential
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-C2FUGQJX.js";
|
|
9
9
|
import {
|
|
10
10
|
t
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-G5TG43R4.js";
|
|
12
12
|
|
|
13
13
|
// src/login.ts
|
|
14
14
|
function readSecret(prompt) {
|
|
@@ -44,6 +44,11 @@ function readSecret(prompt) {
|
|
|
44
44
|
stdin.on("data", onData);
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
|
+
function echoFor(ch) {
|
|
48
|
+
const code = ch.codePointAt(0) ?? 0;
|
|
49
|
+
if (code < 32 || code === 127) return void 0;
|
|
50
|
+
return ch;
|
|
51
|
+
}
|
|
47
52
|
function readLine(prompt) {
|
|
48
53
|
return new Promise((resolve) => {
|
|
49
54
|
process.stdout.write(prompt);
|
|
@@ -71,8 +76,16 @@ function readLine(prompt) {
|
|
|
71
76
|
return;
|
|
72
77
|
}
|
|
73
78
|
if (code === 3) process.exit(130);
|
|
74
|
-
if (code === 127 || code === 8)
|
|
75
|
-
|
|
79
|
+
if (code === 127 || code === 8) {
|
|
80
|
+
if (value) {
|
|
81
|
+
value = value.slice(0, -1);
|
|
82
|
+
process.stdout.write("\b \b");
|
|
83
|
+
}
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const echo = echoFor(ch);
|
|
87
|
+
if (echo) process.stdout.write(echo);
|
|
88
|
+
value += ch;
|
|
76
89
|
}
|
|
77
90
|
};
|
|
78
91
|
stdin.on("data", onData);
|
|
@@ -91,8 +104,8 @@ function defaultLoginDeps() {
|
|
|
91
104
|
fetch: globalThis.fetch,
|
|
92
105
|
// Wiring NYATA jalur lama: persis runLogin lama (provider add + prompt key).
|
|
93
106
|
legacyAdd: async (providerId, targetCwd) => {
|
|
94
|
-
const { runProviderCommand } = await import("./providerCli-
|
|
95
|
-
const { parseCliArgs: parse } = await import("./args-
|
|
107
|
+
const { runProviderCommand } = await import("./providerCli-TXMVPSW5.js");
|
|
108
|
+
const { parseCliArgs: parse } = await import("./args-K5AJFDCZ.js");
|
|
96
109
|
return runProviderCommand(parse(["provider", "add", providerId]), {
|
|
97
110
|
env: process.env,
|
|
98
111
|
cwd: targetCwd,
|
|
@@ -117,12 +130,12 @@ async function runLogin(providerId, cwd, args, deps) {
|
|
|
117
130
|
);
|
|
118
131
|
return 1;
|
|
119
132
|
}
|
|
120
|
-
const { runOauthLogout } = await import("./oauthLogin-
|
|
133
|
+
const { runOauthLogout } = await import("./oauthLogin-YFL2N4QA.js");
|
|
121
134
|
return runOauthLogout({ out: deps.out, err: deps.err, env: deps.env, removeCredential: deps.removeCredential });
|
|
122
135
|
}
|
|
123
136
|
if (args.key) return deps.legacyAdd(providerId, cwd);
|
|
124
137
|
if (providerId === "openrouter") {
|
|
125
|
-
const { runOauthLogin } = await import("./oauthLogin-
|
|
138
|
+
const { runOauthLogin } = await import("./oauthLogin-YFL2N4QA.js");
|
|
126
139
|
return runOauthLogin({
|
|
127
140
|
out: deps.out,
|
|
128
141
|
err: deps.err,
|
|
@@ -139,6 +152,7 @@ async function runLogin(providerId, cwd, args, deps) {
|
|
|
139
152
|
|
|
140
153
|
export {
|
|
141
154
|
readSecret,
|
|
155
|
+
echoFor,
|
|
142
156
|
readLine,
|
|
143
157
|
defaultLoginDeps,
|
|
144
158
|
runLogin
|