acp-bridge 0.3.0
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/LICENSE +21 -0
- package/README.md +285 -0
- package/dist/cli.js +553 -0
- package/dist/daemon.js +1438 -0
- package/package.json +50 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const node_child_process_1 = require("node:child_process");
|
|
5
|
+
const node_fs_1 = require("node:fs");
|
|
6
|
+
const node_http_1 = require("node:http");
|
|
7
|
+
const node_path_1 = require("node:path");
|
|
8
|
+
const node_url_1 = require("node:url");
|
|
9
|
+
const DEFAULT_BASE_URL = "http://localhost:7800";
|
|
10
|
+
const PID_FILE = "/tmp/acp-bridge.pid";
|
|
11
|
+
function printJson(value) {
|
|
12
|
+
process.stdout.write(`${JSON.stringify(value)}\n`);
|
|
13
|
+
}
|
|
14
|
+
function printError(message, details) {
|
|
15
|
+
const body = { ok: false, error: message };
|
|
16
|
+
if (details !== undefined) {
|
|
17
|
+
body.details = details;
|
|
18
|
+
}
|
|
19
|
+
printJson(body);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
function parseArgs(argv) {
|
|
23
|
+
let baseUrl = process.env.ACP_BRIDGE_URL || DEFAULT_BASE_URL;
|
|
24
|
+
const args = [...argv];
|
|
25
|
+
while (args[0] === "--url") {
|
|
26
|
+
args.shift();
|
|
27
|
+
const value = args.shift();
|
|
28
|
+
if (!value) {
|
|
29
|
+
printError("missing value for --url");
|
|
30
|
+
}
|
|
31
|
+
baseUrl = value;
|
|
32
|
+
}
|
|
33
|
+
const command = args.shift();
|
|
34
|
+
if (!command) {
|
|
35
|
+
printError("missing command", {
|
|
36
|
+
usage: [
|
|
37
|
+
"start <type> --name <name> [--cwd <path>]",
|
|
38
|
+
"ask <name> [--stream] <prompt>",
|
|
39
|
+
"status <name>",
|
|
40
|
+
"list",
|
|
41
|
+
"stop <name>",
|
|
42
|
+
"approve <name> [--option <optionId>]",
|
|
43
|
+
"deny <name> [--option <optionId>]",
|
|
44
|
+
"cancel <name>",
|
|
45
|
+
"daemon start|stop|status",
|
|
46
|
+
"doctor",
|
|
47
|
+
"task create <json>",
|
|
48
|
+
"task status <taskId> [--subtask <subtaskId>]",
|
|
49
|
+
"task list",
|
|
50
|
+
"task cancel <taskId>",
|
|
51
|
+
],
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
if (!["start", "ask", "status", "list", "stop", "approve", "deny", "cancel", "daemon", "task", "doctor"].includes(command)) {
|
|
55
|
+
printError(`unknown command: ${command}`);
|
|
56
|
+
}
|
|
57
|
+
return { baseUrl, command, args };
|
|
58
|
+
}
|
|
59
|
+
function readPid() {
|
|
60
|
+
if (!(0, node_fs_1.existsSync)(PID_FILE)) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const raw = (0, node_fs_1.readFileSync)(PID_FILE, "utf8").trim();
|
|
64
|
+
const pid = Number(raw);
|
|
65
|
+
if (!Number.isInteger(pid) || pid <= 0) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
return pid;
|
|
69
|
+
}
|
|
70
|
+
function isProcessRunning(pid) {
|
|
71
|
+
try {
|
|
72
|
+
process.kill(pid, 0);
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function parseDaemonAction(args) {
|
|
80
|
+
const action = args.shift();
|
|
81
|
+
if (!action) {
|
|
82
|
+
printError("daemon requires start|stop|status");
|
|
83
|
+
}
|
|
84
|
+
if (!["start", "stop", "status"].includes(action)) {
|
|
85
|
+
printError(`unknown daemon action: ${action}`);
|
|
86
|
+
}
|
|
87
|
+
return action;
|
|
88
|
+
}
|
|
89
|
+
function daemonStart() {
|
|
90
|
+
const existingPid = readPid();
|
|
91
|
+
if (existingPid && isProcessRunning(existingPid)) {
|
|
92
|
+
return { ok: true, daemon: "running", pid: existingPid };
|
|
93
|
+
}
|
|
94
|
+
if (existingPid && !isProcessRunning(existingPid)) {
|
|
95
|
+
(0, node_fs_1.rmSync)(PID_FILE, { force: true });
|
|
96
|
+
}
|
|
97
|
+
const daemonEntrypoint = (0, node_path_1.join)(__dirname, "daemon.js");
|
|
98
|
+
const child = (0, node_child_process_1.spawn)(process.execPath, [daemonEntrypoint], {
|
|
99
|
+
detached: true,
|
|
100
|
+
stdio: "ignore",
|
|
101
|
+
env: process.env,
|
|
102
|
+
});
|
|
103
|
+
child.unref();
|
|
104
|
+
(0, node_fs_1.writeFileSync)(PID_FILE, `${child.pid}\n`, "utf8");
|
|
105
|
+
return { ok: true, daemon: "started", pid: child.pid };
|
|
106
|
+
}
|
|
107
|
+
function daemonStop() {
|
|
108
|
+
const pid = readPid();
|
|
109
|
+
if (!pid) {
|
|
110
|
+
return { ok: true, daemon: "stopped", pid: null };
|
|
111
|
+
}
|
|
112
|
+
if (!isProcessRunning(pid)) {
|
|
113
|
+
(0, node_fs_1.rmSync)(PID_FILE, { force: true });
|
|
114
|
+
return { ok: true, daemon: "stopped", pid };
|
|
115
|
+
}
|
|
116
|
+
process.kill(pid, "SIGTERM");
|
|
117
|
+
(0, node_fs_1.rmSync)(PID_FILE, { force: true });
|
|
118
|
+
return { ok: true, daemon: "stopped", pid };
|
|
119
|
+
}
|
|
120
|
+
function daemonStatus() {
|
|
121
|
+
const pid = readPid();
|
|
122
|
+
if (!pid) {
|
|
123
|
+
return { ok: true, daemon: "stopped" };
|
|
124
|
+
}
|
|
125
|
+
if (!isProcessRunning(pid)) {
|
|
126
|
+
(0, node_fs_1.rmSync)(PID_FILE, { force: true });
|
|
127
|
+
return { ok: true, daemon: "stopped" };
|
|
128
|
+
}
|
|
129
|
+
return { ok: true, daemon: "running", pid };
|
|
130
|
+
}
|
|
131
|
+
function parseStartArgs(args) {
|
|
132
|
+
const type = args.shift();
|
|
133
|
+
if (!type) {
|
|
134
|
+
printError("start requires <type>");
|
|
135
|
+
}
|
|
136
|
+
let name;
|
|
137
|
+
let cwd;
|
|
138
|
+
while (args.length > 0) {
|
|
139
|
+
const token = args.shift();
|
|
140
|
+
if (!token) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (token === "--name") {
|
|
144
|
+
const value = args.shift();
|
|
145
|
+
if (!value) {
|
|
146
|
+
printError("missing value for --name");
|
|
147
|
+
}
|
|
148
|
+
name = value;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (token === "--cwd") {
|
|
152
|
+
const value = args.shift();
|
|
153
|
+
if (!value) {
|
|
154
|
+
printError("missing value for --cwd");
|
|
155
|
+
}
|
|
156
|
+
cwd = value;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
printError(`unknown start option: ${token}`);
|
|
160
|
+
}
|
|
161
|
+
if (!name) {
|
|
162
|
+
printError("start requires --name <name>");
|
|
163
|
+
}
|
|
164
|
+
return { type, name, cwd };
|
|
165
|
+
}
|
|
166
|
+
function parseAskArgs(args) {
|
|
167
|
+
const name = args.shift();
|
|
168
|
+
if (!name) {
|
|
169
|
+
printError("ask requires <name>");
|
|
170
|
+
}
|
|
171
|
+
let stream = false;
|
|
172
|
+
const promptParts = [];
|
|
173
|
+
while (args.length > 0) {
|
|
174
|
+
const token = args.shift();
|
|
175
|
+
if (token === "--stream") {
|
|
176
|
+
stream = true;
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
promptParts.push(token);
|
|
180
|
+
}
|
|
181
|
+
const prompt = promptParts.join(" ").trim();
|
|
182
|
+
if (!prompt) {
|
|
183
|
+
printError("ask requires <prompt>");
|
|
184
|
+
}
|
|
185
|
+
return { name, prompt, stream };
|
|
186
|
+
}
|
|
187
|
+
function parsePermissionActionArgs(args, action) {
|
|
188
|
+
const name = args.shift();
|
|
189
|
+
if (!name) {
|
|
190
|
+
printError(`${action} requires <name>`);
|
|
191
|
+
}
|
|
192
|
+
let optionId;
|
|
193
|
+
while (args.length > 0) {
|
|
194
|
+
const token = args.shift();
|
|
195
|
+
if (!token) {
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (token === "--option") {
|
|
199
|
+
const value = args.shift();
|
|
200
|
+
if (!value) {
|
|
201
|
+
printError(`missing value for --option`);
|
|
202
|
+
}
|
|
203
|
+
optionId = value;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
printError(`unknown ${action} option: ${token}`);
|
|
207
|
+
}
|
|
208
|
+
return { name, optionId };
|
|
209
|
+
}
|
|
210
|
+
function parseTaskAction(args) {
|
|
211
|
+
const action = args.shift();
|
|
212
|
+
if (!action) {
|
|
213
|
+
printError("task requires create|status|list|cancel");
|
|
214
|
+
}
|
|
215
|
+
if (!["create", "status", "list", "cancel"].includes(action)) {
|
|
216
|
+
printError(`unknown task action: ${action}`);
|
|
217
|
+
}
|
|
218
|
+
const taskAction = action;
|
|
219
|
+
if (taskAction === "list") {
|
|
220
|
+
return { action: "list" };
|
|
221
|
+
}
|
|
222
|
+
if (taskAction === "create") {
|
|
223
|
+
const payload = args.join(" ").trim();
|
|
224
|
+
if (!payload) {
|
|
225
|
+
printError("task create requires a JSON payload");
|
|
226
|
+
}
|
|
227
|
+
try {
|
|
228
|
+
return { action: "create", body: JSON.parse(payload) };
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
232
|
+
printError(`invalid task JSON: ${message}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (taskAction === "status") {
|
|
236
|
+
const taskId = args.shift();
|
|
237
|
+
if (!taskId) {
|
|
238
|
+
printError("task status requires <taskId>");
|
|
239
|
+
}
|
|
240
|
+
let subtaskId;
|
|
241
|
+
while (args.length > 0) {
|
|
242
|
+
const token = args.shift();
|
|
243
|
+
if (!token) {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
if (token === "--subtask") {
|
|
247
|
+
const value = args.shift();
|
|
248
|
+
if (!value) {
|
|
249
|
+
printError("missing value for --subtask");
|
|
250
|
+
}
|
|
251
|
+
subtaskId = value;
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
printError(`unknown task status option: ${token}`);
|
|
255
|
+
}
|
|
256
|
+
return { action: "status", taskId, subtaskId };
|
|
257
|
+
}
|
|
258
|
+
const taskId = args.shift();
|
|
259
|
+
if (!taskId) {
|
|
260
|
+
printError("task cancel requires <taskId>");
|
|
261
|
+
}
|
|
262
|
+
if (args.length > 0) {
|
|
263
|
+
printError(`unknown task cancel option: ${args[0]}`);
|
|
264
|
+
}
|
|
265
|
+
return { action: "cancel", taskId };
|
|
266
|
+
}
|
|
267
|
+
function requestJson(baseUrl, method, path, body) {
|
|
268
|
+
return new Promise((resolve, reject) => {
|
|
269
|
+
let url;
|
|
270
|
+
try {
|
|
271
|
+
url = new node_url_1.URL(path, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
reject(new Error(`invalid base url: ${baseUrl}`));
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const payload = body === undefined ? undefined : JSON.stringify(body);
|
|
278
|
+
const req = (0, node_http_1.request)({
|
|
279
|
+
method,
|
|
280
|
+
protocol: url.protocol,
|
|
281
|
+
hostname: url.hostname,
|
|
282
|
+
port: url.port || undefined,
|
|
283
|
+
path: `${url.pathname}${url.search}`,
|
|
284
|
+
headers: payload
|
|
285
|
+
? {
|
|
286
|
+
"content-type": "application/json",
|
|
287
|
+
"content-length": Buffer.byteLength(payload),
|
|
288
|
+
}
|
|
289
|
+
: undefined,
|
|
290
|
+
}, (res) => {
|
|
291
|
+
const chunks = [];
|
|
292
|
+
res.on("data", (chunk) => {
|
|
293
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
294
|
+
});
|
|
295
|
+
res.on("end", () => {
|
|
296
|
+
const text = Buffer.concat(chunks).toString("utf8").trim();
|
|
297
|
+
let data = null;
|
|
298
|
+
if (text.length > 0) {
|
|
299
|
+
try {
|
|
300
|
+
data = JSON.parse(text);
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
data = { raw: text };
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
const statusCode = res.statusCode ?? 0;
|
|
307
|
+
if (statusCode >= 200 && statusCode < 300) {
|
|
308
|
+
resolve(data);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
reject({ statusCode, data });
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
req.on("error", (error) => {
|
|
315
|
+
reject(error);
|
|
316
|
+
});
|
|
317
|
+
if (payload) {
|
|
318
|
+
req.write(payload);
|
|
319
|
+
}
|
|
320
|
+
req.end();
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
function requestSse(baseUrl, path, body) {
|
|
324
|
+
return new Promise((resolve, reject) => {
|
|
325
|
+
let url;
|
|
326
|
+
try {
|
|
327
|
+
url = new node_url_1.URL(path, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
|
|
328
|
+
}
|
|
329
|
+
catch {
|
|
330
|
+
reject(new Error(`invalid base url: ${baseUrl}`));
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const payload = body === undefined ? undefined : JSON.stringify(body);
|
|
334
|
+
const req = (0, node_http_1.request)({
|
|
335
|
+
method: "POST",
|
|
336
|
+
protocol: url.protocol,
|
|
337
|
+
hostname: url.hostname,
|
|
338
|
+
port: url.port || undefined,
|
|
339
|
+
path: `${url.pathname}${url.search}`,
|
|
340
|
+
headers: payload
|
|
341
|
+
? {
|
|
342
|
+
"content-type": "application/json",
|
|
343
|
+
"content-length": Buffer.byteLength(payload),
|
|
344
|
+
}
|
|
345
|
+
: undefined,
|
|
346
|
+
}, (res) => {
|
|
347
|
+
const statusCode = res.statusCode ?? 0;
|
|
348
|
+
if (statusCode < 200 || statusCode >= 300) {
|
|
349
|
+
const chunks = [];
|
|
350
|
+
res.on("data", (chunk) => {
|
|
351
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
352
|
+
});
|
|
353
|
+
res.on("end", () => {
|
|
354
|
+
const text = Buffer.concat(chunks).toString("utf8").trim();
|
|
355
|
+
let data = null;
|
|
356
|
+
if (text) {
|
|
357
|
+
try {
|
|
358
|
+
data = JSON.parse(text);
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
data = { raw: text };
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
reject({ statusCode, data });
|
|
365
|
+
});
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
let buffer = "";
|
|
369
|
+
let doneResult = null;
|
|
370
|
+
const consumeBlock = (block) => {
|
|
371
|
+
const lines = block.split("\n");
|
|
372
|
+
let event = "message";
|
|
373
|
+
const dataLines = [];
|
|
374
|
+
for (const line of lines) {
|
|
375
|
+
if (line.startsWith("event:")) {
|
|
376
|
+
event = line.slice(6).trim();
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
if (line.startsWith("data:")) {
|
|
380
|
+
dataLines.push(line.slice(5).trim());
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (dataLines.length === 0) {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
let payloadData = dataLines.join("\n");
|
|
387
|
+
try {
|
|
388
|
+
payloadData = JSON.parse(payloadData);
|
|
389
|
+
}
|
|
390
|
+
catch {
|
|
391
|
+
// keep raw string
|
|
392
|
+
}
|
|
393
|
+
if (event === "chunk") {
|
|
394
|
+
const text = payloadData?.chunk;
|
|
395
|
+
if (typeof text === "string") {
|
|
396
|
+
process.stdout.write(text);
|
|
397
|
+
}
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
if (event === "done") {
|
|
401
|
+
doneResult = payloadData;
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
if (event === "error") {
|
|
405
|
+
reject(new Error(payloadData?.error || "stream error"));
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
res.on("data", (chunk) => {
|
|
409
|
+
buffer += Buffer.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk);
|
|
410
|
+
let splitIndex = buffer.indexOf("\n\n");
|
|
411
|
+
while (splitIndex >= 0) {
|
|
412
|
+
const block = buffer.slice(0, splitIndex).trim();
|
|
413
|
+
buffer = buffer.slice(splitIndex + 2);
|
|
414
|
+
if (block) {
|
|
415
|
+
consumeBlock(block);
|
|
416
|
+
}
|
|
417
|
+
splitIndex = buffer.indexOf("\n\n");
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
res.on("end", () => {
|
|
421
|
+
process.stdout.write("\n");
|
|
422
|
+
resolve(doneResult);
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
req.on("error", (error) => {
|
|
426
|
+
reject(error);
|
|
427
|
+
});
|
|
428
|
+
if (payload) {
|
|
429
|
+
req.write(payload);
|
|
430
|
+
}
|
|
431
|
+
req.end();
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
function printDoctor(result) {
|
|
435
|
+
const items = Array.isArray(result?.results) ? result.results : [];
|
|
436
|
+
for (const item of items) {
|
|
437
|
+
const type = typeof item?.type === "string" ? item.type : "unknown";
|
|
438
|
+
const status = typeof item?.status === "string" ? item.status : "error";
|
|
439
|
+
const icon = status === "ok" ? "✅" : status === "warning" ? "⚠️ " : "❌";
|
|
440
|
+
const message = typeof item?.message === "string" && item.message.length > 0
|
|
441
|
+
? item.message
|
|
442
|
+
: [
|
|
443
|
+
item?.binary === true ? "binary found" : item?.binary === false ? "binary missing" : "",
|
|
444
|
+
item?.apiKey === true ? "API key set" : item?.apiKey === false ? "API key missing" : "",
|
|
445
|
+
item?.endpoint === true ? "endpoint reachable" : item?.endpoint === false ? "endpoint unreachable" : "",
|
|
446
|
+
]
|
|
447
|
+
.filter((part) => part.length > 0)
|
|
448
|
+
.join(", ");
|
|
449
|
+
process.stdout.write(`${icon} ${type}: ${message}\n`);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
async function main() {
|
|
453
|
+
const { baseUrl, command, args } = parseArgs(process.argv.slice(2));
|
|
454
|
+
const rest = [...args];
|
|
455
|
+
try {
|
|
456
|
+
let result;
|
|
457
|
+
if (command === "start") {
|
|
458
|
+
const parsed = parseStartArgs(rest);
|
|
459
|
+
result = await requestJson(baseUrl, "POST", "/agents", {
|
|
460
|
+
type: parsed.type,
|
|
461
|
+
name: parsed.name,
|
|
462
|
+
cwd: parsed.cwd,
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
else if (command === "daemon") {
|
|
466
|
+
const action = parseDaemonAction(rest);
|
|
467
|
+
if (action === "start") {
|
|
468
|
+
result = daemonStart();
|
|
469
|
+
}
|
|
470
|
+
else if (action === "stop") {
|
|
471
|
+
result = daemonStop();
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
result = daemonStatus();
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
else if (command === "ask") {
|
|
478
|
+
const parsed = parseAskArgs(rest);
|
|
479
|
+
if (parsed.stream) {
|
|
480
|
+
result = await requestSse(baseUrl, `/agents/${encodeURIComponent(parsed.name)}/ask?stream=true`, { prompt: parsed.prompt });
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
result = await requestJson(baseUrl, "POST", `/agents/${encodeURIComponent(parsed.name)}/ask`, {
|
|
484
|
+
prompt: parsed.prompt,
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
else if (command === "status") {
|
|
489
|
+
const name = rest.shift();
|
|
490
|
+
if (!name) {
|
|
491
|
+
printError("status requires <name>");
|
|
492
|
+
}
|
|
493
|
+
result = await requestJson(baseUrl, "GET", `/agents/${encodeURIComponent(name)}`);
|
|
494
|
+
}
|
|
495
|
+
else if (command === "list") {
|
|
496
|
+
result = await requestJson(baseUrl, "GET", "/agents");
|
|
497
|
+
}
|
|
498
|
+
else if (command === "stop") {
|
|
499
|
+
const name = rest.shift();
|
|
500
|
+
if (!name) {
|
|
501
|
+
printError("stop requires <name>");
|
|
502
|
+
}
|
|
503
|
+
result = await requestJson(baseUrl, "DELETE", `/agents/${encodeURIComponent(name)}`);
|
|
504
|
+
}
|
|
505
|
+
else if (command === "approve" || command === "deny") {
|
|
506
|
+
const parsed = parsePermissionActionArgs(rest, command);
|
|
507
|
+
result = await requestJson(baseUrl, "POST", `/agents/${encodeURIComponent(parsed.name)}/${command}`, parsed.optionId ? { optionId: parsed.optionId } : undefined);
|
|
508
|
+
}
|
|
509
|
+
else if (command === "cancel") {
|
|
510
|
+
const name = rest.shift();
|
|
511
|
+
if (!name) {
|
|
512
|
+
printError("cancel requires <name>");
|
|
513
|
+
}
|
|
514
|
+
result = await requestJson(baseUrl, "POST", `/agents/${encodeURIComponent(name)}/cancel`);
|
|
515
|
+
}
|
|
516
|
+
else if (command === "task") {
|
|
517
|
+
const taskCommand = parseTaskAction(rest);
|
|
518
|
+
if (taskCommand.action === "create") {
|
|
519
|
+
result = await requestJson(baseUrl, "POST", "/tasks", taskCommand.body);
|
|
520
|
+
}
|
|
521
|
+
else if (taskCommand.action === "status") {
|
|
522
|
+
if (taskCommand.subtaskId) {
|
|
523
|
+
result = await requestJson(baseUrl, "GET", `/tasks/${encodeURIComponent(taskCommand.taskId)}/subtasks/${encodeURIComponent(taskCommand.subtaskId)}`);
|
|
524
|
+
}
|
|
525
|
+
else {
|
|
526
|
+
result = await requestJson(baseUrl, "GET", `/tasks/${encodeURIComponent(taskCommand.taskId)}`);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
else if (taskCommand.action === "list") {
|
|
530
|
+
result = await requestJson(baseUrl, "GET", "/tasks");
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
result = await requestJson(baseUrl, "DELETE", `/tasks/${encodeURIComponent(taskCommand.taskId)}`);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
else if (command === "doctor") {
|
|
537
|
+
result = await requestJson(baseUrl, "GET", "/doctor");
|
|
538
|
+
printDoctor(result);
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
printJson(result ?? { ok: true });
|
|
542
|
+
}
|
|
543
|
+
catch (error) {
|
|
544
|
+
if (error && typeof error === "object" && "statusCode" in error) {
|
|
545
|
+
printError(`http error ${error.statusCode}`, error.data);
|
|
546
|
+
}
|
|
547
|
+
if (error instanceof Error) {
|
|
548
|
+
printError(error.message);
|
|
549
|
+
}
|
|
550
|
+
printError(String(error));
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
void main();
|