@botconnector/bccli 0.4.0-beta.50 → 0.4.0-beta.51
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 +17 -1
- package/dist/acp-NWP4FFTG.js +182 -0
- package/dist/{chunk-EUCSCUYP.js → chunk-VU5HNQHX.js} +1 -1
- package/dist/cli.js +6 -6
- package/dist/{serve-XDHMB7TO.js → serve-YSUJD57D.js} +1 -1
- package/dist/{update-IQR346E5.js → update-O4WCLKOO.js} +1 -1
- package/package.json +1 -1
- package/dist/acp-V5RP42VB.js +0 -114
package/README.md
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
# BCCLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@botconnector/bccli)
|
|
4
|
+
[](https://www.npmjs.com/package/@botconnector/bccli)
|
|
5
|
+
[](https://nodejs.org/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://botconnector.id/id/download)
|
|
8
|
+
|
|
9
|
+
Agent AI coding dari [BotConnector](https://botconnector.id/) yang bekerja di terminal: membaca kode, mengedit file,
|
|
4
10
|
dan menjalankan perintah — dengan izin kamu.
|
|
5
11
|
|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
*Rekaman sesi asli: BCCLI membuat file `hello.js` lalu menjalankannya dengan `node`.*
|
|
15
|
+
|
|
6
16
|
```bash
|
|
7
17
|
npm i -g @botconnector/bccli@next # beta terbaru, butuh Node.js 22+
|
|
8
18
|
bccli login # simpan API key BotConnector Cloud
|
|
@@ -209,3 +219,9 @@ npm test && npm run typecheck && npm run lint && npm run build
|
|
|
209
219
|
CI (`ci.yml`) menjalankan hal yang sama di Linux, macOS, dan Windows.
|
|
210
220
|
Rilis: naikkan `version` di `package.json`, lalu push tag `v<version>` — `release.yml`
|
|
211
221
|
menjalankan test, smoke test ke BotConnector Cloud, dan `npm publish --provenance`.
|
|
222
|
+
|
|
223
|
+
## Pelajari lebih lanjut
|
|
224
|
+
|
|
225
|
+
- [Unduh & panduan BCCLI](https://botconnector.id/id/download)
|
|
226
|
+
- [Developer API BotConnector](https://botconnector.id/id/developers/api) — endpoint kompatibel OpenAI SDK
|
|
227
|
+
- [Switchboard](https://github.com/farijarifriyanto-debug/switchboard) — harness agent open source dari BotConnector
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/acp.ts
|
|
4
|
+
var ACP_PROTOCOL_VERSION = 1;
|
|
5
|
+
var TOOL_KINDS = {
|
|
6
|
+
read: "read",
|
|
7
|
+
repo_map: "read",
|
|
8
|
+
edit: "edit",
|
|
9
|
+
write: "edit",
|
|
10
|
+
grep: "search",
|
|
11
|
+
glob: "search",
|
|
12
|
+
bash: "execute",
|
|
13
|
+
powershell: "execute",
|
|
14
|
+
fetch: "fetch",
|
|
15
|
+
web_search: "fetch"
|
|
16
|
+
};
|
|
17
|
+
var kindOf = (tool) => TOOL_KINDS[tool] ?? "other";
|
|
18
|
+
var titleOf = (tool, target) => (target ? `${tool}: ${target}` : tool).slice(0, 200);
|
|
19
|
+
var MAX_OUTPUT = 4e3;
|
|
20
|
+
var PERMISSION_OPTIONS = [
|
|
21
|
+
{ optionId: "allow", name: "Allow", kind: "allow_once" },
|
|
22
|
+
{ optionId: "allow_session", name: "Allow for this session", kind: "allow_always" },
|
|
23
|
+
{ optionId: "reject", name: "Reject", kind: "reject_once" }
|
|
24
|
+
];
|
|
25
|
+
var ANSWERS = { allow: "yes", allow_session: "session", reject: "no" };
|
|
26
|
+
function runAcp(input, output, opts) {
|
|
27
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
28
|
+
const awaiting = /* @__PURE__ */ new Map();
|
|
29
|
+
let nextSession = 1;
|
|
30
|
+
let nextRequest = 1;
|
|
31
|
+
const write = (msg) => {
|
|
32
|
+
output.write(`${JSON.stringify(msg)}
|
|
33
|
+
`);
|
|
34
|
+
};
|
|
35
|
+
const reply = (id, result) => write({ jsonrpc: "2.0", id, result });
|
|
36
|
+
const replyError = (id, code, message) => write({ jsonrpc: "2.0", id, error: { code, message } });
|
|
37
|
+
const update = (sessionId, body) => write({ jsonrpc: "2.0", method: "session/update", params: { sessionId, update: body } });
|
|
38
|
+
const ioFor = (sid, session) => ({
|
|
39
|
+
toolStart(id, tool, target) {
|
|
40
|
+
session.open.push({ id, tool, target });
|
|
41
|
+
update(sid, { sessionUpdate: "tool_call", toolCallId: id, title: titleOf(tool, target), kind: kindOf(tool), status: "in_progress", rawInput: { target } });
|
|
42
|
+
},
|
|
43
|
+
toolEnd(id, output2, isError) {
|
|
44
|
+
session.open = session.open.filter((c) => c.id !== id);
|
|
45
|
+
const text = output2.length > MAX_OUTPUT ? `${output2.slice(0, MAX_OUTPUT)}
|
|
46
|
+
[truncated]` : output2;
|
|
47
|
+
update(sid, {
|
|
48
|
+
sessionUpdate: "tool_call_update",
|
|
49
|
+
toolCallId: id,
|
|
50
|
+
status: isError ? "failed" : "completed",
|
|
51
|
+
...text ? { content: [{ type: "content", content: { type: "text", text } }] } : {}
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
askPermission(req) {
|
|
55
|
+
return new Promise((resolve) => {
|
|
56
|
+
const requestId = nextRequest++;
|
|
57
|
+
const call = [...session.open].reverse().find((c) => c.tool === req.tool && c.target === req.target);
|
|
58
|
+
const settle = (answer) => {
|
|
59
|
+
session.pending.delete(settle);
|
|
60
|
+
awaiting.delete(requestId);
|
|
61
|
+
resolve(answer);
|
|
62
|
+
};
|
|
63
|
+
session.pending.add(settle);
|
|
64
|
+
awaiting.set(requestId, (message) => {
|
|
65
|
+
const outcome = message.result?.outcome;
|
|
66
|
+
settle(outcome?.outcome === "selected" ? ANSWERS[outcome.optionId ?? ""] ?? "no" : "no");
|
|
67
|
+
});
|
|
68
|
+
write({
|
|
69
|
+
jsonrpc: "2.0",
|
|
70
|
+
id: requestId,
|
|
71
|
+
method: "session/request_permission",
|
|
72
|
+
params: {
|
|
73
|
+
sessionId: sid,
|
|
74
|
+
toolCall: { toolCallId: call?.id ?? `permission-${requestId}`, title: titleOf(req.tool, req.target), kind: kindOf(req.tool), status: "pending" },
|
|
75
|
+
options: PERMISSION_OPTIONS
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
const handle = async (msg) => {
|
|
82
|
+
const { id, method, params } = msg ?? {};
|
|
83
|
+
if (method === void 0 && id !== void 0 && (msg.result !== void 0 || msg.error !== void 0)) {
|
|
84
|
+
awaiting.get(id)?.(msg);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (method === "initialize") {
|
|
88
|
+
return reply(id, {
|
|
89
|
+
protocolVersion: ACP_PROTOCOL_VERSION,
|
|
90
|
+
agentCapabilities: {
|
|
91
|
+
loadSession: false,
|
|
92
|
+
promptCapabilities: { image: false, audio: false, embeddedContext: false }
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if (method === "session/new") {
|
|
97
|
+
const sessionId = `s${nextSession++}`;
|
|
98
|
+
sessions.set(sessionId, { controller: null, pending: /* @__PURE__ */ new Set(), open: [] });
|
|
99
|
+
return reply(id, { sessionId });
|
|
100
|
+
}
|
|
101
|
+
if (method === "session/cancel") {
|
|
102
|
+
const session = sessions.get(params?.sessionId ?? "");
|
|
103
|
+
session?.controller?.abort();
|
|
104
|
+
for (const settle of [...session?.pending ?? []]) settle("no");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (method === "session/prompt") {
|
|
108
|
+
const sid = params?.sessionId ?? "";
|
|
109
|
+
const session = sessions.get(sid);
|
|
110
|
+
if (!session) return replyError(id, -32602, "Unknown session");
|
|
111
|
+
const text = (params?.prompt ?? []).filter((p) => p?.type === "text").map((p) => String(p.text ?? "")).join("\n");
|
|
112
|
+
const controller = new AbortController();
|
|
113
|
+
session.controller = controller;
|
|
114
|
+
let cancelled = false;
|
|
115
|
+
controller.signal.addEventListener("abort", () => {
|
|
116
|
+
cancelled = true;
|
|
117
|
+
});
|
|
118
|
+
try {
|
|
119
|
+
session.agent ??= opts.newAgent(params?.cwd ?? opts.cwd);
|
|
120
|
+
await session.agent.run(text, (chunk) => update(sid, { sessionUpdate: "agent_message_chunk", content: { type: "text", text: chunk } }), controller.signal, ioFor(sid, session));
|
|
121
|
+
} catch (e) {
|
|
122
|
+
if (cancelled) return reply(id, { stopReason: "cancelled" });
|
|
123
|
+
return replyError(id, -32603, e.message);
|
|
124
|
+
} finally {
|
|
125
|
+
session.controller = null;
|
|
126
|
+
session.open = [];
|
|
127
|
+
}
|
|
128
|
+
return reply(id, { stopReason: cancelled ? "cancelled" : "end_turn" });
|
|
129
|
+
}
|
|
130
|
+
if (id !== void 0) return replyError(id, -32601, "Method not found");
|
|
131
|
+
};
|
|
132
|
+
return new Promise((resolve) => {
|
|
133
|
+
let buf = "";
|
|
134
|
+
const onData = (chunk) => {
|
|
135
|
+
buf += chunk.toString();
|
|
136
|
+
let idx = buf.indexOf("\n");
|
|
137
|
+
while (idx >= 0) {
|
|
138
|
+
const line = buf.slice(0, idx);
|
|
139
|
+
buf = buf.slice(idx + 1);
|
|
140
|
+
idx = buf.indexOf("\n");
|
|
141
|
+
if (!line.trim()) continue;
|
|
142
|
+
let msg;
|
|
143
|
+
try {
|
|
144
|
+
msg = JSON.parse(line);
|
|
145
|
+
} catch {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
void handle(msg);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
const onEnd = () => {
|
|
152
|
+
input.removeListener("data", onData);
|
|
153
|
+
resolve();
|
|
154
|
+
};
|
|
155
|
+
input.on("data", onData);
|
|
156
|
+
input.on("end", onEnd);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
function runtimeAgent(cwd, allowAll = false) {
|
|
160
|
+
let rt;
|
|
161
|
+
return {
|
|
162
|
+
async run(text, onChunk, signal, io) {
|
|
163
|
+
if (!rt) {
|
|
164
|
+
const { parseCliArgs } = await import("./args-2R45YZ42.js");
|
|
165
|
+
const { createRuntime } = await import("./setup-LLWREJQH.js");
|
|
166
|
+
rt = createRuntime({ cwd, args: { ...parseCliArgs([]), allowAll } });
|
|
167
|
+
}
|
|
168
|
+
if (io && !allowAll) rt.agent.askPermission = (req) => io.askPermission(req);
|
|
169
|
+
rt.agent.onEvent = (event) => {
|
|
170
|
+
if (event.type === "text") onChunk(event.delta);
|
|
171
|
+
else if (event.type === "toolStart") io?.toolStart(event.id, event.tool, event.target);
|
|
172
|
+
else if (event.type === "toolEnd") io?.toolEnd(event.id, event.output, event.isError);
|
|
173
|
+
};
|
|
174
|
+
await rt.agent.run(text, signal);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
export {
|
|
179
|
+
ACP_PROTOCOL_VERSION,
|
|
180
|
+
runAcp,
|
|
181
|
+
runtimeAgent
|
|
182
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -12,7 +12,7 @@ import "./chunk-DP7EBW53.js";
|
|
|
12
12
|
import "./chunk-PSO7R5AD.js";
|
|
13
13
|
import {
|
|
14
14
|
VERSION
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-VU5HNQHX.js";
|
|
16
16
|
import {
|
|
17
17
|
helpText,
|
|
18
18
|
parseCliArgs
|
|
@@ -108,16 +108,16 @@ async function main() {
|
|
|
108
108
|
return runModelsCommand(args, { env: process.env, cwd, out: (s) => console.log(s), err: (s) => console.error(s), fetch: globalThis.fetch });
|
|
109
109
|
}
|
|
110
110
|
if (args.command === "update") {
|
|
111
|
-
const { runUpdateCommand } = await import("./update-
|
|
111
|
+
const { runUpdateCommand } = await import("./update-O4WCLKOO.js");
|
|
112
112
|
return runUpdateCommand({ env: process.env });
|
|
113
113
|
}
|
|
114
114
|
if (args.command === "acp") {
|
|
115
|
-
const { runtimeAgent, runAcp } = await import("./acp-
|
|
116
|
-
await runAcp(process.stdin, process.stdout, { cwd, newAgent: (c) => runtimeAgent(c) });
|
|
115
|
+
const { runtimeAgent, runAcp } = await import("./acp-NWP4FFTG.js");
|
|
116
|
+
await runAcp(process.stdin, process.stdout, { cwd, newAgent: (c) => runtimeAgent(c, args.allowAll) });
|
|
117
117
|
return 0;
|
|
118
118
|
}
|
|
119
119
|
if (args.command === "serve") {
|
|
120
|
-
const { startServe } = await import("./serve-
|
|
120
|
+
const { startServe } = await import("./serve-YSUJD57D.js");
|
|
121
121
|
const handle = await startServe({ port: args.port, host: args.host, token: args.token, cwd, env: process.env });
|
|
122
122
|
process.stderr.write(`bccli serve ${handle.url} \u2014 Authorization: Bearer ${handle.token}
|
|
123
123
|
`);
|
|
@@ -195,7 +195,7 @@ async function main() {
|
|
|
195
195
|
if (!process.stdin.isTTY) throw new ConfigError(t('Interactive mode needs a terminal. For scripts/CI use: bccli -p "task"'));
|
|
196
196
|
if (rt.config.updateCheck !== "off" && !process.env.BCCLI_NO_UPDATE_CHECK) {
|
|
197
197
|
try {
|
|
198
|
-
const { checkForUpdate } = await import("./update-
|
|
198
|
+
const { checkForUpdate } = await import("./update-O4WCLKOO.js");
|
|
199
199
|
const update = await checkForUpdate({ home: rt.home, fetch: globalThis.fetch, now: /* @__PURE__ */ new Date() });
|
|
200
200
|
if (update) console.error(update);
|
|
201
201
|
} catch {
|
package/package.json
CHANGED
package/dist/acp-V5RP42VB.js
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/acp.ts
|
|
4
|
-
var ACP_PROTOCOL_VERSION = 1;
|
|
5
|
-
function runAcp(input, output, opts) {
|
|
6
|
-
const sessions = /* @__PURE__ */ new Map();
|
|
7
|
-
let nextSession = 1;
|
|
8
|
-
const write = (msg) => {
|
|
9
|
-
output.write(`${JSON.stringify(msg)}
|
|
10
|
-
`);
|
|
11
|
-
};
|
|
12
|
-
const reply = (id, result) => write({ jsonrpc: "2.0", id, result });
|
|
13
|
-
const replyError = (id, code, message) => write({ jsonrpc: "2.0", id, error: { code, message } });
|
|
14
|
-
const handle = async (msg) => {
|
|
15
|
-
const { id, method, params } = msg ?? {};
|
|
16
|
-
if (method === "initialize") {
|
|
17
|
-
return reply(id, {
|
|
18
|
-
protocolVersion: ACP_PROTOCOL_VERSION,
|
|
19
|
-
agentCapabilities: {
|
|
20
|
-
loadSession: false,
|
|
21
|
-
promptCapabilities: { image: false, audio: false, embeddedContext: false }
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
if (method === "session/new") {
|
|
26
|
-
const sessionId = `s${nextSession++}`;
|
|
27
|
-
sessions.set(sessionId, { controller: null });
|
|
28
|
-
return reply(id, { sessionId });
|
|
29
|
-
}
|
|
30
|
-
if (method === "session/cancel") {
|
|
31
|
-
sessions.get(params?.sessionId ?? "")?.controller?.abort();
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
if (method === "session/prompt") {
|
|
35
|
-
const sid = params?.sessionId ?? "";
|
|
36
|
-
const session = sessions.get(sid);
|
|
37
|
-
if (!session) return replyError(id, -32602, "Unknown session");
|
|
38
|
-
const text = (params?.prompt ?? []).filter((p) => p?.type === "text").map((p) => String(p.text ?? "")).join("\n");
|
|
39
|
-
const controller = new AbortController();
|
|
40
|
-
session.controller = controller;
|
|
41
|
-
let cancelled = false;
|
|
42
|
-
controller.signal.addEventListener("abort", () => {
|
|
43
|
-
cancelled = true;
|
|
44
|
-
});
|
|
45
|
-
try {
|
|
46
|
-
const agent = opts.newAgent(params?.cwd ?? opts.cwd);
|
|
47
|
-
await agent.run(
|
|
48
|
-
text,
|
|
49
|
-
(chunk) => {
|
|
50
|
-
write({
|
|
51
|
-
jsonrpc: "2.0",
|
|
52
|
-
method: "session/update",
|
|
53
|
-
params: {
|
|
54
|
-
sessionId: sid,
|
|
55
|
-
update: { sessionUpdate: "agent_message_chunk", content: { type: "text", text: chunk } }
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
},
|
|
59
|
-
controller.signal
|
|
60
|
-
);
|
|
61
|
-
} catch (e) {
|
|
62
|
-
return replyError(id, -32603, e.message);
|
|
63
|
-
} finally {
|
|
64
|
-
session.controller = null;
|
|
65
|
-
}
|
|
66
|
-
return reply(id, { stopReason: cancelled ? "cancelled" : "end_turn" });
|
|
67
|
-
}
|
|
68
|
-
if (id !== void 0) return replyError(id, -32601, "Method not found");
|
|
69
|
-
};
|
|
70
|
-
return new Promise((resolve) => {
|
|
71
|
-
let buf = "";
|
|
72
|
-
const onData = (chunk) => {
|
|
73
|
-
buf += chunk.toString();
|
|
74
|
-
let idx = buf.indexOf("\n");
|
|
75
|
-
while (idx >= 0) {
|
|
76
|
-
const line = buf.slice(0, idx);
|
|
77
|
-
buf = buf.slice(idx + 1);
|
|
78
|
-
idx = buf.indexOf("\n");
|
|
79
|
-
if (!line.trim()) continue;
|
|
80
|
-
let msg;
|
|
81
|
-
try {
|
|
82
|
-
msg = JSON.parse(line);
|
|
83
|
-
} catch {
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
void handle(msg);
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
const onEnd = () => {
|
|
90
|
-
input.removeListener("data", onData);
|
|
91
|
-
resolve();
|
|
92
|
-
};
|
|
93
|
-
input.on("data", onData);
|
|
94
|
-
input.on("end", onEnd);
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
function runtimeAgent(cwd) {
|
|
98
|
-
return {
|
|
99
|
-
async run(text, onChunk, signal) {
|
|
100
|
-
const { parseCliArgs } = await import("./args-2R45YZ42.js");
|
|
101
|
-
const { createRuntime } = await import("./setup-LLWREJQH.js");
|
|
102
|
-
const rt = createRuntime({ cwd, args: { ...parseCliArgs([]), allowAll: true } });
|
|
103
|
-
rt.agent.onEvent = (event) => {
|
|
104
|
-
if (event.type === "text") onChunk(event.delta);
|
|
105
|
-
};
|
|
106
|
-
await rt.agent.run(text, signal);
|
|
107
|
-
}
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
export {
|
|
111
|
-
ACP_PROTOCOL_VERSION,
|
|
112
|
-
runAcp,
|
|
113
|
-
runtimeAgent
|
|
114
|
-
};
|