@openacp/cli 0.2.23 → 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-EIBLQU3H.js → chunk-IX63F4JG.js} +449 -43
- 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-VJUX7RUY.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-EIBLQU3H.js.map +0 -1
- package/dist/chunk-XOVJLTEC.js.map +0 -1
- package/dist/main-VJUX7RUY.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,153 @@ 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" });
|
|
1952
2340
|
function patchedFetch(input, init) {
|
|
1953
2341
|
if (init?.signal && !(init.signal instanceof AbortSignal)) {
|
|
1954
2342
|
const nativeController = new AbortController();
|
|
@@ -1982,7 +2370,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
1982
2370
|
this.bot = new Bot(this.telegramConfig.botToken, { client: { fetch: patchedFetch } });
|
|
1983
2371
|
this.bot.catch((err) => {
|
|
1984
2372
|
const rootCause = err.error instanceof Error ? err.error : err;
|
|
1985
|
-
|
|
2373
|
+
log8.error({ err: rootCause }, "Telegram bot error");
|
|
1986
2374
|
});
|
|
1987
2375
|
this.bot.api.config.use((prev, method, payload, signal) => {
|
|
1988
2376
|
if (method === "getUpdates") {
|
|
@@ -2021,6 +2409,12 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2021
2409
|
(notification) => this.sendNotification(notification)
|
|
2022
2410
|
);
|
|
2023
2411
|
setupSkillCallbacks(this.bot, this.core);
|
|
2412
|
+
setupActionCallbacks(
|
|
2413
|
+
this.bot,
|
|
2414
|
+
this.core,
|
|
2415
|
+
this.telegramConfig.chatId,
|
|
2416
|
+
() => this.assistantSession?.id
|
|
2417
|
+
);
|
|
2024
2418
|
setupMenuCallbacks(
|
|
2025
2419
|
this.bot,
|
|
2026
2420
|
this.core,
|
|
@@ -2035,7 +2429,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2035
2429
|
this.setupRoutes();
|
|
2036
2430
|
this.bot.start({
|
|
2037
2431
|
allowed_updates: ["message", "callback_query"],
|
|
2038
|
-
onStart: () =>
|
|
2432
|
+
onStart: () => log8.info(
|
|
2039
2433
|
{ chatId: this.telegramConfig.chatId },
|
|
2040
2434
|
"Telegram bot started"
|
|
2041
2435
|
)
|
|
@@ -2047,7 +2441,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2047
2441
|
this.assistantTopicId
|
|
2048
2442
|
);
|
|
2049
2443
|
} catch (err) {
|
|
2050
|
-
|
|
2444
|
+
log8.error({ err }, "Failed to spawn assistant");
|
|
2051
2445
|
}
|
|
2052
2446
|
try {
|
|
2053
2447
|
const config = this.core.configManager.get();
|
|
@@ -2068,7 +2462,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2068
2462
|
reply_markup: buildMenuKeyboard()
|
|
2069
2463
|
});
|
|
2070
2464
|
} catch (err) {
|
|
2071
|
-
|
|
2465
|
+
log8.warn({ err }, "Failed to send welcome message");
|
|
2072
2466
|
}
|
|
2073
2467
|
}
|
|
2074
2468
|
async stop() {
|
|
@@ -2076,7 +2470,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2076
2470
|
await this.assistantSession.destroy();
|
|
2077
2471
|
}
|
|
2078
2472
|
await this.bot.stop();
|
|
2079
|
-
|
|
2473
|
+
log8.info("Telegram bot stopped");
|
|
2080
2474
|
}
|
|
2081
2475
|
setupRoutes() {
|
|
2082
2476
|
this.bot.on("message:text", async (ctx) => {
|
|
@@ -2094,7 +2488,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2094
2488
|
ctx.replyWithChatAction("typing").catch(() => {
|
|
2095
2489
|
});
|
|
2096
2490
|
handleAssistantMessage(this.assistantSession, ctx.message.text).catch(
|
|
2097
|
-
(err) =>
|
|
2491
|
+
(err) => log8.error({ err }, "Assistant error")
|
|
2098
2492
|
);
|
|
2099
2493
|
return;
|
|
2100
2494
|
}
|
|
@@ -2105,7 +2499,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2105
2499
|
threadId: String(threadId),
|
|
2106
2500
|
userId: String(ctx.from.id),
|
|
2107
2501
|
text: ctx.message.text
|
|
2108
|
-
}).catch((err) =>
|
|
2502
|
+
}).catch((err) => log8.error({ err }, "handleMessage error"));
|
|
2109
2503
|
});
|
|
2110
2504
|
}
|
|
2111
2505
|
// --- ChannelAdapter implementations ---
|
|
@@ -2195,6 +2589,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2195
2589
|
break;
|
|
2196
2590
|
}
|
|
2197
2591
|
case "usage": {
|
|
2592
|
+
await this.finalizeDraft(sessionId);
|
|
2198
2593
|
await this.bot.api.sendMessage(
|
|
2199
2594
|
this.telegramConfig.chatId,
|
|
2200
2595
|
formatUsage(
|
|
@@ -2240,7 +2635,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2240
2635
|
}
|
|
2241
2636
|
}
|
|
2242
2637
|
async sendPermissionRequest(sessionId, request) {
|
|
2243
|
-
|
|
2638
|
+
log8.info({ sessionId, requestId: request.id }, "Permission request sent");
|
|
2244
2639
|
const session = this.core.sessionManager.getSession(
|
|
2245
2640
|
sessionId
|
|
2246
2641
|
);
|
|
@@ -2248,7 +2643,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2248
2643
|
await this.permissionHandler.sendPermissionRequest(session, request);
|
|
2249
2644
|
}
|
|
2250
2645
|
async sendNotification(notification) {
|
|
2251
|
-
|
|
2646
|
+
log8.info(
|
|
2252
2647
|
{ sessionId: notification.sessionId, type: notification.type },
|
|
2253
2648
|
"Notification sent"
|
|
2254
2649
|
);
|
|
@@ -2274,7 +2669,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2274
2669
|
});
|
|
2275
2670
|
}
|
|
2276
2671
|
async createSessionThread(sessionId, name) {
|
|
2277
|
-
|
|
2672
|
+
log8.info({ sessionId, name }, "Session topic created");
|
|
2278
2673
|
return String(
|
|
2279
2674
|
await createSessionTopic(this.bot, this.telegramConfig.chatId, name)
|
|
2280
2675
|
);
|
|
@@ -2338,7 +2733,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2338
2733
|
}
|
|
2339
2734
|
);
|
|
2340
2735
|
} catch (err) {
|
|
2341
|
-
|
|
2736
|
+
log8.error({ err, sessionId }, "Failed to send skill commands");
|
|
2342
2737
|
}
|
|
2343
2738
|
await this.updateCommandAutocomplete(session.agentName, commands);
|
|
2344
2739
|
}
|
|
@@ -2369,12 +2764,12 @@ Workspace: <code>${workspace}</code>
|
|
|
2369
2764
|
await this.bot.api.setMyCommands(all, {
|
|
2370
2765
|
scope: { type: "chat", chat_id: this.telegramConfig.chatId }
|
|
2371
2766
|
});
|
|
2372
|
-
|
|
2767
|
+
log8.info(
|
|
2373
2768
|
{ count: all.length, skills: validSkills.length },
|
|
2374
2769
|
"Updated command autocomplete"
|
|
2375
2770
|
);
|
|
2376
2771
|
} catch (err) {
|
|
2377
|
-
|
|
2772
|
+
log8.error(
|
|
2378
2773
|
{ err, commands: all },
|
|
2379
2774
|
"Failed to update command autocomplete"
|
|
2380
2775
|
);
|
|
@@ -2382,10 +2777,20 @@ Workspace: <code>${workspace}</code>
|
|
|
2382
2777
|
}
|
|
2383
2778
|
async finalizeDraft(sessionId) {
|
|
2384
2779
|
const draft = this.sessionDrafts.get(sessionId);
|
|
2385
|
-
if (draft)
|
|
2386
|
-
|
|
2387
|
-
|
|
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
|
+
}
|
|
2388
2791
|
}
|
|
2792
|
+
await draft.finalize(keyboard);
|
|
2793
|
+
this.sessionDrafts.delete(sessionId);
|
|
2389
2794
|
}
|
|
2390
2795
|
};
|
|
2391
2796
|
|
|
@@ -2400,6 +2805,7 @@ export {
|
|
|
2400
2805
|
NotificationManager,
|
|
2401
2806
|
OpenACPCore,
|
|
2402
2807
|
ChannelAdapter,
|
|
2808
|
+
ApiServer,
|
|
2403
2809
|
TelegramAdapter
|
|
2404
2810
|
};
|
|
2405
|
-
//# sourceMappingURL=chunk-
|
|
2811
|
+
//# sourceMappingURL=chunk-IX63F4JG.js.map
|