@openacp/cli 0.2.22 → 0.2.24
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/autostart-YBYXQA77.js +18 -0
- package/dist/autostart-YBYXQA77.js.map +1 -0
- package/dist/{setup-XQBEZZQB.js → chunk-4BN7NSKB.js} +140 -9
- package/dist/chunk-4BN7NSKB.js.map +1 -0
- package/dist/chunk-CQMS5U7Z.js +63 -0
- package/dist/chunk-CQMS5U7Z.js.map +1 -0
- package/dist/{chunk-XOVJLTEC.js → chunk-FGXG3H3F.js} +14 -58
- package/dist/chunk-FGXG3H3F.js.map +1 -0
- package/dist/{chunk-HTXK4NLG.js → chunk-IX63F4JG.js} +463 -44
- package/dist/chunk-IX63F4JG.js.map +1 -0
- package/dist/{chunk-ZATQZUJT.js → chunk-MNJDYDGH.js} +9 -1
- package/dist/{chunk-ZATQZUJT.js.map → chunk-MNJDYDGH.js.map} +1 -1
- package/dist/chunk-PQRVTUNH.js +145 -0
- package/dist/chunk-PQRVTUNH.js.map +1 -0
- package/dist/chunk-QWUJIKTX.js +527 -0
- package/dist/chunk-QWUJIKTX.js.map +1 -0
- package/dist/chunk-S6O7SM6A.js +129 -0
- package/dist/chunk-S6O7SM6A.js.map +1 -0
- package/dist/chunk-WXS6ONOD.js +103 -0
- package/dist/chunk-WXS6ONOD.js.map +1 -0
- package/dist/cli.js +354 -4
- package/dist/cli.js.map +1 -1
- package/dist/config-2XALNLAA.js +14 -0
- package/dist/config-2XALNLAA.js.map +1 -0
- package/dist/config-editor-56B6YU7B.js +11 -0
- package/dist/config-editor-56B6YU7B.js.map +1 -0
- package/dist/daemon-3E5OMLT3.js +29 -0
- package/dist/daemon-3E5OMLT3.js.map +1 -0
- package/dist/index.d.ts +96 -18
- package/dist/index.js +35 -6
- package/dist/install-cloudflared-57NRTI4E.js +8 -0
- package/dist/install-cloudflared-57NRTI4E.js.map +1 -0
- package/dist/{main-GC6JY7DS.js → main-YNCSLYVV.js} +43 -11
- package/dist/main-YNCSLYVV.js.map +1 -0
- package/dist/setup-FTNJACSC.js +27 -0
- package/dist/setup-FTNJACSC.js.map +1 -0
- package/dist/{tunnel-service-I6NUMBT4.js → tunnel-service-I6WM6USB.js} +10 -10
- package/dist/tunnel-service-I6WM6USB.js.map +1 -0
- package/package.json +2 -2
- package/dist/chunk-HTXK4NLG.js.map +0 -1
- package/dist/chunk-XOVJLTEC.js.map +0 -1
- package/dist/main-GC6JY7DS.js.map +0 -1
- package/dist/setup-XQBEZZQB.js.map +0 -1
- package/dist/tunnel-service-I6NUMBT4.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createChildLogger,
|
|
3
3
|
createSessionLogger
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-MNJDYDGH.js";
|
|
5
5
|
|
|
6
6
|
// src/core/streams.ts
|
|
7
7
|
function nodeToWebWritable(nodeStream) {
|
|
@@ -580,6 +580,7 @@ var Session = class {
|
|
|
580
580
|
try {
|
|
581
581
|
const start = Date.now();
|
|
582
582
|
await this.agentInstance.prompt('Reply with only "ready".');
|
|
583
|
+
this.status = "active";
|
|
583
584
|
this.log.info({ durationMs: Date.now() - start }, "Warm-up complete");
|
|
584
585
|
} catch (err) {
|
|
585
586
|
this.log.error({ err }, "Warm-up failed");
|
|
@@ -1259,6 +1260,187 @@ var ChannelAdapter = class {
|
|
|
1259
1260
|
}
|
|
1260
1261
|
};
|
|
1261
1262
|
|
|
1263
|
+
// src/core/api-server.ts
|
|
1264
|
+
import * as http from "http";
|
|
1265
|
+
import * as fs3 from "fs";
|
|
1266
|
+
import * as path4 from "path";
|
|
1267
|
+
import * as os2 from "os";
|
|
1268
|
+
var log4 = createChildLogger({ module: "api-server" });
|
|
1269
|
+
var DEFAULT_PORT_FILE = path4.join(os2.homedir(), ".openacp", "api.port");
|
|
1270
|
+
var ApiServer = class {
|
|
1271
|
+
constructor(core, config, portFilePath) {
|
|
1272
|
+
this.core = core;
|
|
1273
|
+
this.config = config;
|
|
1274
|
+
this.portFilePath = portFilePath ?? DEFAULT_PORT_FILE;
|
|
1275
|
+
}
|
|
1276
|
+
server = null;
|
|
1277
|
+
actualPort = 0;
|
|
1278
|
+
portFilePath;
|
|
1279
|
+
async start() {
|
|
1280
|
+
this.server = http.createServer((req, res) => this.handleRequest(req, res));
|
|
1281
|
+
await new Promise((resolve, reject) => {
|
|
1282
|
+
this.server.on("error", (err) => {
|
|
1283
|
+
if (err.code === "EADDRINUSE") {
|
|
1284
|
+
log4.warn({ port: this.config.port }, "API port in use, continuing without API server");
|
|
1285
|
+
this.server = null;
|
|
1286
|
+
resolve();
|
|
1287
|
+
} else {
|
|
1288
|
+
reject(err);
|
|
1289
|
+
}
|
|
1290
|
+
});
|
|
1291
|
+
this.server.listen(this.config.port, this.config.host, () => {
|
|
1292
|
+
const addr = this.server.address();
|
|
1293
|
+
if (addr && typeof addr === "object") {
|
|
1294
|
+
this.actualPort = addr.port;
|
|
1295
|
+
}
|
|
1296
|
+
this.writePortFile();
|
|
1297
|
+
log4.info({ host: this.config.host, port: this.actualPort }, "API server listening");
|
|
1298
|
+
resolve();
|
|
1299
|
+
});
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
async stop() {
|
|
1303
|
+
this.removePortFile();
|
|
1304
|
+
if (this.server) {
|
|
1305
|
+
await new Promise((resolve) => {
|
|
1306
|
+
this.server.close(() => resolve());
|
|
1307
|
+
});
|
|
1308
|
+
this.server = null;
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
getPort() {
|
|
1312
|
+
return this.actualPort;
|
|
1313
|
+
}
|
|
1314
|
+
writePortFile() {
|
|
1315
|
+
const dir = path4.dirname(this.portFilePath);
|
|
1316
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
1317
|
+
fs3.writeFileSync(this.portFilePath, String(this.actualPort));
|
|
1318
|
+
}
|
|
1319
|
+
removePortFile() {
|
|
1320
|
+
try {
|
|
1321
|
+
fs3.unlinkSync(this.portFilePath);
|
|
1322
|
+
} catch {
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
async handleRequest(req, res) {
|
|
1326
|
+
const method = req.method?.toUpperCase();
|
|
1327
|
+
const url = req.url || "";
|
|
1328
|
+
try {
|
|
1329
|
+
if (method === "POST" && url === "/api/sessions") {
|
|
1330
|
+
await this.handleCreateSession(req, res);
|
|
1331
|
+
} else if (method === "DELETE" && url.match(/^\/api\/sessions\/(.+)$/)) {
|
|
1332
|
+
const sessionId = url.match(/^\/api\/sessions\/(.+)$/)[1];
|
|
1333
|
+
await this.handleCancelSession(sessionId, res);
|
|
1334
|
+
} else if (method === "GET" && url === "/api/sessions") {
|
|
1335
|
+
await this.handleListSessions(res);
|
|
1336
|
+
} else if (method === "GET" && url === "/api/agents") {
|
|
1337
|
+
await this.handleListAgents(res);
|
|
1338
|
+
} else {
|
|
1339
|
+
this.sendJson(res, 404, { error: "Not found" });
|
|
1340
|
+
}
|
|
1341
|
+
} catch (err) {
|
|
1342
|
+
log4.error({ err }, "API request error");
|
|
1343
|
+
this.sendJson(res, 500, { error: "Internal server error" });
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
async handleCreateSession(req, res) {
|
|
1347
|
+
const body = await this.readBody(req);
|
|
1348
|
+
let agent;
|
|
1349
|
+
let workspace;
|
|
1350
|
+
if (body) {
|
|
1351
|
+
try {
|
|
1352
|
+
const parsed = JSON.parse(body);
|
|
1353
|
+
agent = parsed.agent;
|
|
1354
|
+
workspace = parsed.workspace;
|
|
1355
|
+
} catch {
|
|
1356
|
+
this.sendJson(res, 400, { error: "Invalid JSON body" });
|
|
1357
|
+
return;
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
const config = this.core.configManager.get();
|
|
1361
|
+
const activeSessions = this.core.sessionManager.listSessions().filter((s) => s.status === "active" || s.status === "initializing");
|
|
1362
|
+
if (activeSessions.length >= config.security.maxConcurrentSessions) {
|
|
1363
|
+
this.sendJson(res, 429, {
|
|
1364
|
+
error: `Max concurrent sessions (${config.security.maxConcurrentSessions}) reached. Cancel a session first.`
|
|
1365
|
+
});
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
const [adapterId, adapter] = this.core.adapters.entries().next().value ?? [null, null];
|
|
1369
|
+
const channelId = adapterId ?? "api";
|
|
1370
|
+
const session = await this.core.handleNewSession(channelId, agent, workspace);
|
|
1371
|
+
if (adapter) {
|
|
1372
|
+
try {
|
|
1373
|
+
const threadId = await adapter.createSessionThread(session.id, `\u{1F504} ${session.agentName} \u2014 New Session`);
|
|
1374
|
+
session.threadId = threadId;
|
|
1375
|
+
this.core.wireSessionEvents(session, adapter);
|
|
1376
|
+
} catch (err) {
|
|
1377
|
+
log4.warn({ err, sessionId: session.id }, "Failed to create session thread on adapter, running headless");
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
if (!adapter) {
|
|
1381
|
+
session.agentInstance.onPermissionRequest = async (request) => {
|
|
1382
|
+
const allowOption = request.options.find((o) => o.isAllow);
|
|
1383
|
+
log4.debug({ sessionId: session.id, permissionId: request.id, option: allowOption?.id }, "Auto-approving permission for API session");
|
|
1384
|
+
return allowOption?.id ?? request.options[0]?.id ?? "";
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1387
|
+
session.warmup().catch((err) => log4.warn({ err, sessionId: session.id }, "API session warmup failed"));
|
|
1388
|
+
this.sendJson(res, 200, {
|
|
1389
|
+
sessionId: session.id,
|
|
1390
|
+
agent: session.agentName,
|
|
1391
|
+
status: session.status,
|
|
1392
|
+
workspace: session.workingDirectory
|
|
1393
|
+
});
|
|
1394
|
+
}
|
|
1395
|
+
async handleCancelSession(sessionId, res) {
|
|
1396
|
+
const session = this.core.sessionManager.getSession(sessionId);
|
|
1397
|
+
if (!session) {
|
|
1398
|
+
this.sendJson(res, 404, { error: `Session "${sessionId}" not found` });
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
await session.cancel();
|
|
1402
|
+
this.sendJson(res, 200, { ok: true });
|
|
1403
|
+
}
|
|
1404
|
+
async handleListSessions(res) {
|
|
1405
|
+
const sessions = this.core.sessionManager.listSessions();
|
|
1406
|
+
this.sendJson(res, 200, {
|
|
1407
|
+
sessions: sessions.map((s) => ({
|
|
1408
|
+
id: s.id,
|
|
1409
|
+
agent: s.agentName,
|
|
1410
|
+
status: s.status,
|
|
1411
|
+
name: s.name ?? null,
|
|
1412
|
+
workspace: s.workingDirectory
|
|
1413
|
+
}))
|
|
1414
|
+
});
|
|
1415
|
+
}
|
|
1416
|
+
async handleListAgents(res) {
|
|
1417
|
+
const agents = this.core.agentManager.getAvailableAgents();
|
|
1418
|
+
const defaultAgent = this.core.configManager.get().defaultAgent;
|
|
1419
|
+
this.sendJson(res, 200, {
|
|
1420
|
+
agents: agents.map((a) => ({
|
|
1421
|
+
name: a.name,
|
|
1422
|
+
command: a.command,
|
|
1423
|
+
args: a.args
|
|
1424
|
+
})),
|
|
1425
|
+
default: defaultAgent
|
|
1426
|
+
});
|
|
1427
|
+
}
|
|
1428
|
+
sendJson(res, status, data) {
|
|
1429
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
1430
|
+
res.end(JSON.stringify(data));
|
|
1431
|
+
}
|
|
1432
|
+
readBody(req) {
|
|
1433
|
+
return new Promise((resolve) => {
|
|
1434
|
+
let data = "";
|
|
1435
|
+
req.on("data", (chunk) => {
|
|
1436
|
+
data += chunk;
|
|
1437
|
+
});
|
|
1438
|
+
req.on("end", () => resolve(data));
|
|
1439
|
+
req.on("error", () => resolve(""));
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
};
|
|
1443
|
+
|
|
1262
1444
|
// src/adapters/telegram/adapter.ts
|
|
1263
1445
|
import { Bot } from "grammy";
|
|
1264
1446
|
|
|
@@ -1453,24 +1635,33 @@ var MessageDraft = class {
|
|
|
1453
1635
|
});
|
|
1454
1636
|
this.messageId = msg.message_id;
|
|
1455
1637
|
} else {
|
|
1456
|
-
await this.bot.api.editMessageText(
|
|
1457
|
-
|
|
1458
|
-
|
|
1638
|
+
await this.bot.api.editMessageText(
|
|
1639
|
+
this.chatId,
|
|
1640
|
+
this.messageId,
|
|
1641
|
+
truncated,
|
|
1642
|
+
{
|
|
1643
|
+
parse_mode: "HTML"
|
|
1644
|
+
}
|
|
1645
|
+
);
|
|
1459
1646
|
}
|
|
1460
1647
|
} catch {
|
|
1461
1648
|
try {
|
|
1462
1649
|
if (!this.messageId) {
|
|
1463
|
-
const msg = await this.bot.api.sendMessage(
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1650
|
+
const msg = await this.bot.api.sendMessage(
|
|
1651
|
+
this.chatId,
|
|
1652
|
+
this.buffer.slice(0, 4096),
|
|
1653
|
+
{
|
|
1654
|
+
message_thread_id: this.threadId,
|
|
1655
|
+
disable_notification: true
|
|
1656
|
+
}
|
|
1657
|
+
);
|
|
1467
1658
|
this.messageId = msg.message_id;
|
|
1468
1659
|
}
|
|
1469
1660
|
} catch {
|
|
1470
1661
|
}
|
|
1471
1662
|
}
|
|
1472
1663
|
}
|
|
1473
|
-
async finalize() {
|
|
1664
|
+
async finalize(replyMarkup) {
|
|
1474
1665
|
if (this.flushTimer) {
|
|
1475
1666
|
clearTimeout(this.flushTimer);
|
|
1476
1667
|
this.flushTimer = void 0;
|
|
@@ -1482,25 +1673,38 @@ var MessageDraft = class {
|
|
|
1482
1673
|
try {
|
|
1483
1674
|
for (let i = 0; i < chunks.length; i++) {
|
|
1484
1675
|
const chunk = chunks[i];
|
|
1676
|
+
const isLast = i === chunks.length - 1;
|
|
1677
|
+
const markup = isLast && replyMarkup ? { reply_markup: replyMarkup } : {};
|
|
1485
1678
|
if (i === 0 && this.messageId) {
|
|
1486
|
-
await this.bot.api.editMessageText(
|
|
1487
|
-
|
|
1488
|
-
|
|
1679
|
+
await this.bot.api.editMessageText(
|
|
1680
|
+
this.chatId,
|
|
1681
|
+
this.messageId,
|
|
1682
|
+
chunk,
|
|
1683
|
+
{
|
|
1684
|
+
parse_mode: "HTML",
|
|
1685
|
+
...markup
|
|
1686
|
+
}
|
|
1687
|
+
);
|
|
1489
1688
|
} else {
|
|
1490
1689
|
const msg = await this.bot.api.sendMessage(this.chatId, chunk, {
|
|
1491
1690
|
message_thread_id: this.threadId,
|
|
1492
1691
|
parse_mode: "HTML",
|
|
1493
|
-
disable_notification: true
|
|
1692
|
+
disable_notification: true,
|
|
1693
|
+
...markup
|
|
1494
1694
|
});
|
|
1495
1695
|
this.messageId = msg.message_id;
|
|
1496
1696
|
}
|
|
1497
1697
|
}
|
|
1498
1698
|
} catch {
|
|
1499
1699
|
try {
|
|
1500
|
-
await this.bot.api.sendMessage(
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1700
|
+
await this.bot.api.sendMessage(
|
|
1701
|
+
this.chatId,
|
|
1702
|
+
this.buffer.slice(0, 4096),
|
|
1703
|
+
{
|
|
1704
|
+
message_thread_id: this.threadId,
|
|
1705
|
+
disable_notification: true
|
|
1706
|
+
}
|
|
1707
|
+
);
|
|
1504
1708
|
} catch {
|
|
1505
1709
|
}
|
|
1506
1710
|
}
|
|
@@ -1509,6 +1713,9 @@ var MessageDraft = class {
|
|
|
1509
1713
|
getMessageId() {
|
|
1510
1714
|
return this.messageId;
|
|
1511
1715
|
}
|
|
1716
|
+
getBuffer() {
|
|
1717
|
+
return this.buffer;
|
|
1718
|
+
}
|
|
1512
1719
|
};
|
|
1513
1720
|
|
|
1514
1721
|
// src/adapters/telegram/topics.ts
|
|
@@ -1545,7 +1752,7 @@ function buildDeepLink(chatId, messageId) {
|
|
|
1545
1752
|
// src/adapters/telegram/commands.ts
|
|
1546
1753
|
import { InlineKeyboard } from "grammy";
|
|
1547
1754
|
import { nanoid as nanoid2 } from "nanoid";
|
|
1548
|
-
var
|
|
1755
|
+
var log6 = createChildLogger({ module: "telegram-commands" });
|
|
1549
1756
|
function setupCommands(bot, core, chatId) {
|
|
1550
1757
|
bot.command("new", (ctx) => handleNew(ctx, core, chatId));
|
|
1551
1758
|
bot.command("new_chat", (ctx) => handleNewChat(ctx, core, chatId));
|
|
@@ -1600,7 +1807,7 @@ async function handleNew(ctx, core, chatId) {
|
|
|
1600
1807
|
const args = matchStr.split(" ").filter(Boolean);
|
|
1601
1808
|
const agentName = args[0];
|
|
1602
1809
|
const workspace = args[1];
|
|
1603
|
-
|
|
1810
|
+
log6.info({ userId: ctx.from?.id, agentName }, "New session command");
|
|
1604
1811
|
let threadId;
|
|
1605
1812
|
try {
|
|
1606
1813
|
const topicName = `\u{1F504} New Session`;
|
|
@@ -1633,15 +1840,16 @@ async function handleNew(ctx, core, chatId) {
|
|
|
1633
1840
|
parse_mode: "HTML"
|
|
1634
1841
|
}
|
|
1635
1842
|
);
|
|
1636
|
-
session.warmup().catch((err) =>
|
|
1843
|
+
session.warmup().catch((err) => log6.error({ err }, "Warm-up error"));
|
|
1637
1844
|
} catch (err) {
|
|
1845
|
+
log6.error({ err }, "Session creation failed");
|
|
1638
1846
|
if (threadId) {
|
|
1639
1847
|
try {
|
|
1640
1848
|
await ctx.api.deleteForumTopic(chatId, threadId);
|
|
1641
1849
|
} catch {
|
|
1642
1850
|
}
|
|
1643
1851
|
}
|
|
1644
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1852
|
+
const message = err instanceof Error ? err.message : typeof err === "object" ? JSON.stringify(err) : String(err);
|
|
1645
1853
|
await ctx.reply(`\u274C ${escapeHtml(message)}`, { parse_mode: "HTML" });
|
|
1646
1854
|
}
|
|
1647
1855
|
}
|
|
@@ -1686,7 +1894,7 @@ async function handleNewChat(ctx, core, chatId) {
|
|
|
1686
1894
|
parse_mode: "HTML"
|
|
1687
1895
|
}
|
|
1688
1896
|
);
|
|
1689
|
-
session.warmup().catch((err) =>
|
|
1897
|
+
session.warmup().catch((err) => log6.error({ err }, "Warm-up error"));
|
|
1690
1898
|
} catch (err) {
|
|
1691
1899
|
const message = err instanceof Error ? err.message : String(err);
|
|
1692
1900
|
await ctx.reply(`\u274C ${escapeHtml(message)}`, { parse_mode: "HTML" });
|
|
@@ -1700,7 +1908,7 @@ async function handleCancel(ctx, core) {
|
|
|
1700
1908
|
String(threadId)
|
|
1701
1909
|
);
|
|
1702
1910
|
if (session) {
|
|
1703
|
-
|
|
1911
|
+
log6.info({ sessionId: session.id }, "Cancel session command");
|
|
1704
1912
|
await session.cancel();
|
|
1705
1913
|
await ctx.reply("\u26D4 Session cancelled.", { parse_mode: "HTML" });
|
|
1706
1914
|
}
|
|
@@ -1808,6 +2016,41 @@ function setupSkillCallbacks(bot, core) {
|
|
|
1808
2016
|
await session.enqueuePrompt(`/${entry.commandName}`);
|
|
1809
2017
|
});
|
|
1810
2018
|
}
|
|
2019
|
+
async function executeNewSession(bot, core, chatId, agentName, workspace) {
|
|
2020
|
+
const threadId = await createSessionTopic(bot, chatId, "\u{1F504} New Session");
|
|
2021
|
+
await bot.api.sendMessage(chatId, "\u23F3 Setting up session, please wait...", {
|
|
2022
|
+
message_thread_id: threadId,
|
|
2023
|
+
parse_mode: "HTML"
|
|
2024
|
+
});
|
|
2025
|
+
try {
|
|
2026
|
+
const session = await core.handleNewSession(
|
|
2027
|
+
"telegram",
|
|
2028
|
+
agentName,
|
|
2029
|
+
workspace
|
|
2030
|
+
);
|
|
2031
|
+
session.threadId = String(threadId);
|
|
2032
|
+
await core.sessionManager.updateSessionPlatform(session.id, {
|
|
2033
|
+
topicId: threadId
|
|
2034
|
+
});
|
|
2035
|
+
const finalName = `\u{1F504} ${session.agentName} \u2014 New Session`;
|
|
2036
|
+
await renameSessionTopic(bot, chatId, threadId, finalName);
|
|
2037
|
+
session.warmup().catch((err) => log6.error({ err }, "Warm-up error"));
|
|
2038
|
+
return { session, threadId };
|
|
2039
|
+
} catch (err) {
|
|
2040
|
+
try {
|
|
2041
|
+
await bot.api.deleteForumTopic(chatId, threadId);
|
|
2042
|
+
} catch {
|
|
2043
|
+
}
|
|
2044
|
+
throw err;
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
async function executeCancelSession(core, excludeSessionId) {
|
|
2048
|
+
const sessions = core.sessionManager.listSessions("telegram").filter((s) => s.status === "active" && s.id !== excludeSessionId).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
2049
|
+
const session = sessions[0];
|
|
2050
|
+
if (!session) return null;
|
|
2051
|
+
await session.cancel();
|
|
2052
|
+
return session;
|
|
2053
|
+
}
|
|
1811
2054
|
var STATIC_COMMANDS = [
|
|
1812
2055
|
{ command: "new", description: "Create new session" },
|
|
1813
2056
|
{ command: "new_chat", description: "New chat, same agent & workspace" },
|
|
@@ -1821,7 +2064,7 @@ var STATIC_COMMANDS = [
|
|
|
1821
2064
|
// src/adapters/telegram/permissions.ts
|
|
1822
2065
|
import { InlineKeyboard as InlineKeyboard2 } from "grammy";
|
|
1823
2066
|
import { nanoid as nanoid3 } from "nanoid";
|
|
1824
|
-
var
|
|
2067
|
+
var log7 = createChildLogger({ module: "telegram-permissions" });
|
|
1825
2068
|
var PermissionHandler = class {
|
|
1826
2069
|
constructor(bot, chatId, getSession, sendNotification) {
|
|
1827
2070
|
this.bot = bot;
|
|
@@ -1881,7 +2124,7 @@ ${escapeHtml(request.description)}`,
|
|
|
1881
2124
|
}
|
|
1882
2125
|
const session = this.getSession(pending.sessionId);
|
|
1883
2126
|
const isAllow = pending.options.find((o) => o.id === optionId)?.isAllow ?? false;
|
|
1884
|
-
|
|
2127
|
+
log7.info({ requestId: pending.requestId, optionId, isAllow }, "Permission responded");
|
|
1885
2128
|
if (session?.pendingPermission?.requestId === pending.requestId) {
|
|
1886
2129
|
session.pendingPermission.resolve(optionId);
|
|
1887
2130
|
session.pendingPermission = void 0;
|
|
@@ -1947,8 +2190,166 @@ function redirectToAssistant(chatId, assistantTopicId) {
|
|
|
1947
2190
|
return `\u{1F4AC} Please use the <a href="${link}">\u{1F916} Assistant</a> topic to chat with OpenACP.`;
|
|
1948
2191
|
}
|
|
1949
2192
|
|
|
2193
|
+
// src/adapters/telegram/action-detect.ts
|
|
2194
|
+
import { nanoid as nanoid4 } from "nanoid";
|
|
2195
|
+
import { InlineKeyboard as InlineKeyboard3 } from "grammy";
|
|
2196
|
+
var CMD_NEW_RE = /\/new(?:\s+([^\s\u0080-\uFFFF]+)(?:\s+([^\s\u0080-\uFFFF]+))?)?/;
|
|
2197
|
+
var CMD_CANCEL_RE = /\/cancel\b/;
|
|
2198
|
+
var KW_NEW_RE = /(?:tao|tạo|create|new)\s+session/i;
|
|
2199
|
+
var KW_CANCEL_RE = /(?:huy|huỷ|cancel|dung|dừng)\s+session/i;
|
|
2200
|
+
function detectAction(text) {
|
|
2201
|
+
if (!text) return null;
|
|
2202
|
+
const cancelCmd = CMD_CANCEL_RE.exec(text);
|
|
2203
|
+
if (cancelCmd) return { action: "cancel_session" };
|
|
2204
|
+
const newCmd = CMD_NEW_RE.exec(text);
|
|
2205
|
+
if (newCmd) {
|
|
2206
|
+
return {
|
|
2207
|
+
action: "new_session",
|
|
2208
|
+
agent: newCmd[1] || void 0,
|
|
2209
|
+
workspace: newCmd[2] || void 0
|
|
2210
|
+
};
|
|
2211
|
+
}
|
|
2212
|
+
if (KW_CANCEL_RE.test(text)) return { action: "cancel_session" };
|
|
2213
|
+
if (KW_NEW_RE.test(text))
|
|
2214
|
+
return { action: "new_session", agent: void 0, workspace: void 0 };
|
|
2215
|
+
return null;
|
|
2216
|
+
}
|
|
2217
|
+
var ACTION_TTL_MS = 5 * 60 * 1e3;
|
|
2218
|
+
var actionMap = /* @__PURE__ */ new Map();
|
|
2219
|
+
function storeAction(action) {
|
|
2220
|
+
const id = nanoid4(10);
|
|
2221
|
+
actionMap.set(id, { action, createdAt: Date.now() });
|
|
2222
|
+
for (const [key, entry] of actionMap) {
|
|
2223
|
+
if (Date.now() - entry.createdAt > ACTION_TTL_MS) {
|
|
2224
|
+
actionMap.delete(key);
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
return id;
|
|
2228
|
+
}
|
|
2229
|
+
function getAction(id) {
|
|
2230
|
+
const entry = actionMap.get(id);
|
|
2231
|
+
if (!entry) return void 0;
|
|
2232
|
+
if (Date.now() - entry.createdAt > ACTION_TTL_MS) {
|
|
2233
|
+
actionMap.delete(id);
|
|
2234
|
+
return void 0;
|
|
2235
|
+
}
|
|
2236
|
+
return entry.action;
|
|
2237
|
+
}
|
|
2238
|
+
function removeAction(id) {
|
|
2239
|
+
actionMap.delete(id);
|
|
2240
|
+
}
|
|
2241
|
+
function buildActionKeyboard(actionId, action) {
|
|
2242
|
+
const keyboard = new InlineKeyboard3();
|
|
2243
|
+
if (action.action === "new_session") {
|
|
2244
|
+
keyboard.text("\u2705 T\u1EA1o session", `a:${actionId}`);
|
|
2245
|
+
keyboard.text("\u274C Hu\u1EF7", `a:dismiss:${actionId}`);
|
|
2246
|
+
} else {
|
|
2247
|
+
keyboard.text("\u26D4 Hu\u1EF7 session", `a:${actionId}`);
|
|
2248
|
+
keyboard.text("\u274C Kh\xF4ng", `a:dismiss:${actionId}`);
|
|
2249
|
+
}
|
|
2250
|
+
return keyboard;
|
|
2251
|
+
}
|
|
2252
|
+
function setupActionCallbacks(bot, core, chatId, getAssistantSessionId) {
|
|
2253
|
+
bot.callbackQuery(/^a:dismiss:/, async (ctx) => {
|
|
2254
|
+
const actionId = ctx.callbackQuery.data.replace("a:dismiss:", "");
|
|
2255
|
+
removeAction(actionId);
|
|
2256
|
+
try {
|
|
2257
|
+
await ctx.editMessageReplyMarkup({
|
|
2258
|
+
reply_markup: { inline_keyboard: [] }
|
|
2259
|
+
});
|
|
2260
|
+
} catch {
|
|
2261
|
+
}
|
|
2262
|
+
await ctx.answerCallbackQuery({ text: "\u0110\xE3 hu\u1EF7" });
|
|
2263
|
+
});
|
|
2264
|
+
bot.callbackQuery(/^a:(?!dismiss)/, async (ctx) => {
|
|
2265
|
+
const actionId = ctx.callbackQuery.data.replace("a:", "");
|
|
2266
|
+
const action = getAction(actionId);
|
|
2267
|
+
if (!action) {
|
|
2268
|
+
await ctx.answerCallbackQuery({ text: "Action \u0111\xE3 h\u1EBFt h\u1EA1n" });
|
|
2269
|
+
return;
|
|
2270
|
+
}
|
|
2271
|
+
removeAction(actionId);
|
|
2272
|
+
try {
|
|
2273
|
+
if (action.action === "new_session") {
|
|
2274
|
+
await ctx.answerCallbackQuery({ text: "\u23F3 \u0110ang t\u1EA1o session..." });
|
|
2275
|
+
const { threadId } = await executeNewSession(
|
|
2276
|
+
bot,
|
|
2277
|
+
core,
|
|
2278
|
+
chatId,
|
|
2279
|
+
action.agent,
|
|
2280
|
+
action.workspace
|
|
2281
|
+
);
|
|
2282
|
+
const topicLink = `https://t.me/c/${String(chatId).replace("-100", "")}/${threadId}`;
|
|
2283
|
+
const originalText = ctx.callbackQuery.message?.text ?? "";
|
|
2284
|
+
try {
|
|
2285
|
+
await ctx.editMessageText(
|
|
2286
|
+
originalText + `
|
|
2287
|
+
|
|
2288
|
+
\u2705 Session created \u2192 <a href="${topicLink}">Go to topic</a>`,
|
|
2289
|
+
{ parse_mode: "HTML" }
|
|
2290
|
+
);
|
|
2291
|
+
} catch {
|
|
2292
|
+
await ctx.editMessageReplyMarkup({
|
|
2293
|
+
reply_markup: { inline_keyboard: [] }
|
|
2294
|
+
});
|
|
2295
|
+
}
|
|
2296
|
+
} else if (action.action === "cancel_session") {
|
|
2297
|
+
const assistantId = getAssistantSessionId();
|
|
2298
|
+
const cancelled = await executeCancelSession(core, assistantId);
|
|
2299
|
+
if (cancelled) {
|
|
2300
|
+
await ctx.answerCallbackQuery({ text: "\u26D4 Session \u0111\xE3 hu\u1EF7" });
|
|
2301
|
+
const originalText = ctx.callbackQuery.message?.text ?? "";
|
|
2302
|
+
try {
|
|
2303
|
+
await ctx.editMessageText(
|
|
2304
|
+
originalText + `
|
|
2305
|
+
|
|
2306
|
+
\u26D4 Session "${cancelled.name ?? cancelled.id}" \u0111\xE3 hu\u1EF7`,
|
|
2307
|
+
{ parse_mode: "HTML" }
|
|
2308
|
+
);
|
|
2309
|
+
} catch {
|
|
2310
|
+
await ctx.editMessageReplyMarkup({
|
|
2311
|
+
reply_markup: { inline_keyboard: [] }
|
|
2312
|
+
});
|
|
2313
|
+
}
|
|
2314
|
+
} else {
|
|
2315
|
+
await ctx.answerCallbackQuery({
|
|
2316
|
+
text: "Kh\xF4ng c\xF3 session n\xE0o \u0111ang ch\u1EA1y"
|
|
2317
|
+
});
|
|
2318
|
+
try {
|
|
2319
|
+
await ctx.editMessageReplyMarkup({
|
|
2320
|
+
reply_markup: { inline_keyboard: [] }
|
|
2321
|
+
});
|
|
2322
|
+
} catch {
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
} catch {
|
|
2327
|
+
await ctx.answerCallbackQuery({ text: "\u274C L\u1ED7i, th\u1EED l\u1EA1i sau" });
|
|
2328
|
+
try {
|
|
2329
|
+
await ctx.editMessageReplyMarkup({
|
|
2330
|
+
reply_markup: { inline_keyboard: [] }
|
|
2331
|
+
});
|
|
2332
|
+
} catch {
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
});
|
|
2336
|
+
}
|
|
2337
|
+
|
|
1950
2338
|
// src/adapters/telegram/adapter.ts
|
|
1951
|
-
var
|
|
2339
|
+
var log8 = createChildLogger({ module: "telegram" });
|
|
2340
|
+
function patchedFetch(input, init) {
|
|
2341
|
+
if (init?.signal && !(init.signal instanceof AbortSignal)) {
|
|
2342
|
+
const nativeController = new AbortController();
|
|
2343
|
+
const polyfillSignal = init.signal;
|
|
2344
|
+
if (polyfillSignal.aborted) {
|
|
2345
|
+
nativeController.abort();
|
|
2346
|
+
} else {
|
|
2347
|
+
polyfillSignal.addEventListener("abort", () => nativeController.abort());
|
|
2348
|
+
}
|
|
2349
|
+
init = { ...init, signal: nativeController.signal };
|
|
2350
|
+
}
|
|
2351
|
+
return fetch(input, init);
|
|
2352
|
+
}
|
|
1952
2353
|
var TelegramAdapter = class extends ChannelAdapter {
|
|
1953
2354
|
bot;
|
|
1954
2355
|
telegramConfig;
|
|
@@ -1966,10 +2367,10 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
1966
2367
|
this.telegramConfig = config;
|
|
1967
2368
|
}
|
|
1968
2369
|
async start() {
|
|
1969
|
-
this.bot = new Bot(this.telegramConfig.botToken, { client: { fetch } });
|
|
2370
|
+
this.bot = new Bot(this.telegramConfig.botToken, { client: { fetch: patchedFetch } });
|
|
1970
2371
|
this.bot.catch((err) => {
|
|
1971
2372
|
const rootCause = err.error instanceof Error ? err.error : err;
|
|
1972
|
-
|
|
2373
|
+
log8.error({ err: rootCause }, "Telegram bot error");
|
|
1973
2374
|
});
|
|
1974
2375
|
this.bot.api.config.use((prev, method, payload, signal) => {
|
|
1975
2376
|
if (method === "getUpdates") {
|
|
@@ -2008,6 +2409,12 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2008
2409
|
(notification) => this.sendNotification(notification)
|
|
2009
2410
|
);
|
|
2010
2411
|
setupSkillCallbacks(this.bot, this.core);
|
|
2412
|
+
setupActionCallbacks(
|
|
2413
|
+
this.bot,
|
|
2414
|
+
this.core,
|
|
2415
|
+
this.telegramConfig.chatId,
|
|
2416
|
+
() => this.assistantSession?.id
|
|
2417
|
+
);
|
|
2011
2418
|
setupMenuCallbacks(
|
|
2012
2419
|
this.bot,
|
|
2013
2420
|
this.core,
|
|
@@ -2022,7 +2429,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2022
2429
|
this.setupRoutes();
|
|
2023
2430
|
this.bot.start({
|
|
2024
2431
|
allowed_updates: ["message", "callback_query"],
|
|
2025
|
-
onStart: () =>
|
|
2432
|
+
onStart: () => log8.info(
|
|
2026
2433
|
{ chatId: this.telegramConfig.chatId },
|
|
2027
2434
|
"Telegram bot started"
|
|
2028
2435
|
)
|
|
@@ -2034,7 +2441,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2034
2441
|
this.assistantTopicId
|
|
2035
2442
|
);
|
|
2036
2443
|
} catch (err) {
|
|
2037
|
-
|
|
2444
|
+
log8.error({ err }, "Failed to spawn assistant");
|
|
2038
2445
|
}
|
|
2039
2446
|
try {
|
|
2040
2447
|
const config = this.core.configManager.get();
|
|
@@ -2055,7 +2462,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2055
2462
|
reply_markup: buildMenuKeyboard()
|
|
2056
2463
|
});
|
|
2057
2464
|
} catch (err) {
|
|
2058
|
-
|
|
2465
|
+
log8.warn({ err }, "Failed to send welcome message");
|
|
2059
2466
|
}
|
|
2060
2467
|
}
|
|
2061
2468
|
async stop() {
|
|
@@ -2063,7 +2470,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2063
2470
|
await this.assistantSession.destroy();
|
|
2064
2471
|
}
|
|
2065
2472
|
await this.bot.stop();
|
|
2066
|
-
|
|
2473
|
+
log8.info("Telegram bot stopped");
|
|
2067
2474
|
}
|
|
2068
2475
|
setupRoutes() {
|
|
2069
2476
|
this.bot.on("message:text", async (ctx) => {
|
|
@@ -2081,7 +2488,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2081
2488
|
ctx.replyWithChatAction("typing").catch(() => {
|
|
2082
2489
|
});
|
|
2083
2490
|
handleAssistantMessage(this.assistantSession, ctx.message.text).catch(
|
|
2084
|
-
(err) =>
|
|
2491
|
+
(err) => log8.error({ err }, "Assistant error")
|
|
2085
2492
|
);
|
|
2086
2493
|
return;
|
|
2087
2494
|
}
|
|
@@ -2092,7 +2499,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2092
2499
|
threadId: String(threadId),
|
|
2093
2500
|
userId: String(ctx.from.id),
|
|
2094
2501
|
text: ctx.message.text
|
|
2095
|
-
}).catch((err) =>
|
|
2502
|
+
}).catch((err) => log8.error({ err }, "handleMessage error"));
|
|
2096
2503
|
});
|
|
2097
2504
|
}
|
|
2098
2505
|
// --- ChannelAdapter implementations ---
|
|
@@ -2182,6 +2589,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2182
2589
|
break;
|
|
2183
2590
|
}
|
|
2184
2591
|
case "usage": {
|
|
2592
|
+
await this.finalizeDraft(sessionId);
|
|
2185
2593
|
await this.bot.api.sendMessage(
|
|
2186
2594
|
this.telegramConfig.chatId,
|
|
2187
2595
|
formatUsage(
|
|
@@ -2227,7 +2635,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2227
2635
|
}
|
|
2228
2636
|
}
|
|
2229
2637
|
async sendPermissionRequest(sessionId, request) {
|
|
2230
|
-
|
|
2638
|
+
log8.info({ sessionId, requestId: request.id }, "Permission request sent");
|
|
2231
2639
|
const session = this.core.sessionManager.getSession(
|
|
2232
2640
|
sessionId
|
|
2233
2641
|
);
|
|
@@ -2235,7 +2643,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2235
2643
|
await this.permissionHandler.sendPermissionRequest(session, request);
|
|
2236
2644
|
}
|
|
2237
2645
|
async sendNotification(notification) {
|
|
2238
|
-
|
|
2646
|
+
log8.info(
|
|
2239
2647
|
{ sessionId: notification.sessionId, type: notification.type },
|
|
2240
2648
|
"Notification sent"
|
|
2241
2649
|
);
|
|
@@ -2261,7 +2669,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2261
2669
|
});
|
|
2262
2670
|
}
|
|
2263
2671
|
async createSessionThread(sessionId, name) {
|
|
2264
|
-
|
|
2672
|
+
log8.info({ sessionId, name }, "Session topic created");
|
|
2265
2673
|
return String(
|
|
2266
2674
|
await createSessionTopic(this.bot, this.telegramConfig.chatId, name)
|
|
2267
2675
|
);
|
|
@@ -2325,7 +2733,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2325
2733
|
}
|
|
2326
2734
|
);
|
|
2327
2735
|
} catch (err) {
|
|
2328
|
-
|
|
2736
|
+
log8.error({ err, sessionId }, "Failed to send skill commands");
|
|
2329
2737
|
}
|
|
2330
2738
|
await this.updateCommandAutocomplete(session.agentName, commands);
|
|
2331
2739
|
}
|
|
@@ -2356,12 +2764,12 @@ Workspace: <code>${workspace}</code>
|
|
|
2356
2764
|
await this.bot.api.setMyCommands(all, {
|
|
2357
2765
|
scope: { type: "chat", chat_id: this.telegramConfig.chatId }
|
|
2358
2766
|
});
|
|
2359
|
-
|
|
2767
|
+
log8.info(
|
|
2360
2768
|
{ count: all.length, skills: validSkills.length },
|
|
2361
2769
|
"Updated command autocomplete"
|
|
2362
2770
|
);
|
|
2363
2771
|
} catch (err) {
|
|
2364
|
-
|
|
2772
|
+
log8.error(
|
|
2365
2773
|
{ err, commands: all },
|
|
2366
2774
|
"Failed to update command autocomplete"
|
|
2367
2775
|
);
|
|
@@ -2369,10 +2777,20 @@ Workspace: <code>${workspace}</code>
|
|
|
2369
2777
|
}
|
|
2370
2778
|
async finalizeDraft(sessionId) {
|
|
2371
2779
|
const draft = this.sessionDrafts.get(sessionId);
|
|
2372
|
-
if (draft)
|
|
2373
|
-
|
|
2374
|
-
|
|
2780
|
+
if (!draft) return;
|
|
2781
|
+
let keyboard;
|
|
2782
|
+
if (sessionId === this.assistantSession?.id) {
|
|
2783
|
+
const fullText = draft.getBuffer();
|
|
2784
|
+
if (fullText) {
|
|
2785
|
+
const detected = detectAction(fullText);
|
|
2786
|
+
if (detected) {
|
|
2787
|
+
const actionId = storeAction(detected);
|
|
2788
|
+
keyboard = buildActionKeyboard(actionId, detected);
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2375
2791
|
}
|
|
2792
|
+
await draft.finalize(keyboard);
|
|
2793
|
+
this.sessionDrafts.delete(sessionId);
|
|
2376
2794
|
}
|
|
2377
2795
|
};
|
|
2378
2796
|
|
|
@@ -2387,6 +2805,7 @@ export {
|
|
|
2387
2805
|
NotificationManager,
|
|
2388
2806
|
OpenACPCore,
|
|
2389
2807
|
ChannelAdapter,
|
|
2808
|
+
ApiServer,
|
|
2390
2809
|
TelegramAdapter
|
|
2391
2810
|
};
|
|
2392
|
-
//# sourceMappingURL=chunk-
|
|
2811
|
+
//# sourceMappingURL=chunk-IX63F4JG.js.map
|