teleton 0.8.0 → 0.8.1
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 +28 -11
- package/dist/{chunk-U56QTM46.js → chunk-3S4GGLLR.js} +28 -26
- package/dist/{chunk-NUGDTPE4.js → chunk-4L66JHQE.js} +2 -1
- package/dist/{chunk-H36RFKRI.js → chunk-5FNWBZ5K.js} +476 -143
- package/dist/{chunk-SD4NLLYG.js → chunk-7U7BOHCL.js} +80 -33
- package/dist/{chunk-QVBSUYVX.js → chunk-AYWEJCDB.js} +11 -3
- package/dist/{chunk-RQBAMUCV.js → chunk-CGOXE4WP.js} +1235 -201
- package/dist/{chunk-TVRZJIZX.js → chunk-KVXV7EF7.js} +3 -3
- package/dist/{chunk-WIKM24GZ.js → chunk-QBHRXLZS.js} +5 -0
- package/dist/{chunk-P36I6OIV.js → chunk-QV2GLOTK.js} +12 -1
- package/dist/{chunk-JHYZYFZJ.js → chunk-S6PHGKOC.js} +8 -1
- package/dist/{chunk-IJBWWQE4.js → chunk-UP55PXFH.js} +4 -0
- package/dist/cli/index.js +17 -16
- package/dist/{client-LNZTDQSA.js → client-MPHPIZB6.js} +2 -2
- package/dist/{get-my-gifts-OMGKOEPM.js → get-my-gifts-CC6HAVWB.js} +1 -1
- package/dist/index.js +11 -11
- package/dist/{memory-AS7WKGTW.js → memory-UBHM7ILG.js} +4 -4
- package/dist/{migrate-POHWYEIW.js → migrate-UBBEJ5BL.js} +4 -4
- package/dist/{server-H3QA252W.js → server-3FHI2SEB.js} +392 -51
- package/dist/{setup-server-QXED3D2L.js → setup-server-32XGDPE6.js} +157 -7
- package/dist/{store-GAFULOOX.js → store-M5IMUQCL.js} +5 -5
- package/dist/{task-dependency-resolver-3FIKQ7Z6.js → task-dependency-resolver-RR2O5S7B.js} +2 -2
- package/dist/{task-executor-RUTFG6VG.js → task-executor-6W5HRX5C.js} +2 -2
- package/dist/{tasks-BEZ4QRI2.js → tasks-WQIKXDX5.js} +1 -1
- package/dist/{tool-index-H3SHOJC3.js → tool-index-PMAOXWUA.js} +8 -5
- package/dist/{transcript-IMNE6KU3.js → transcript-NGDPSNIH.js} +1 -1
- package/dist/web/assets/index-BfYCdwLI.js +80 -0
- package/dist/web/assets/{index-BrVqauzj.css → index-DmlyQVhR.css} +1 -1
- package/dist/web/assets/{index.es-DkU1GvWU.js → index.es-DitvF-9H.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +11 -4
- package/dist/web/assets/index-DYeEkvJ6.js +0 -72
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
getWalletBalance,
|
|
8
8
|
invalidateTonClientCache,
|
|
9
9
|
loadWallet
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-S6PHGKOC.js";
|
|
11
11
|
import {
|
|
12
12
|
getOrCreateSession,
|
|
13
13
|
getSession,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
resetSessionWithPolicy,
|
|
16
16
|
shouldResetSession,
|
|
17
17
|
updateSession
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-KVXV7EF7.js";
|
|
19
19
|
import {
|
|
20
20
|
Mi,
|
|
21
21
|
Mn,
|
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
getProviderModel,
|
|
36
36
|
getUtilityModel,
|
|
37
37
|
loadContextFromTranscript
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-AYWEJCDB.js";
|
|
39
39
|
import {
|
|
40
40
|
getProviderMetadata,
|
|
41
41
|
getSupportedProviders
|
|
@@ -44,14 +44,14 @@ import {
|
|
|
44
44
|
appendToTranscript,
|
|
45
45
|
archiveTranscript,
|
|
46
46
|
transcriptExists
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-QV2GLOTK.js";
|
|
48
48
|
import {
|
|
49
49
|
ContextBuilder,
|
|
50
50
|
createDbWrapper,
|
|
51
51
|
getDatabase,
|
|
52
52
|
migrateFromMainDb,
|
|
53
53
|
openModuleDb
|
|
54
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-7U7BOHCL.js";
|
|
55
55
|
import {
|
|
56
56
|
GECKOTERMINAL_API_URL,
|
|
57
57
|
tonapiFetch
|
|
@@ -80,11 +80,13 @@ import {
|
|
|
80
80
|
OVERSIZED_MESSAGE_RATIO,
|
|
81
81
|
PAYMENT_TOLERANCE_RATIO,
|
|
82
82
|
RATE_LIMIT_MAX_RETRIES,
|
|
83
|
+
RESULT_TRUNCATION_KEEP_CHARS,
|
|
84
|
+
RESULT_TRUNCATION_THRESHOLD,
|
|
83
85
|
SERVER_ERROR_MAX_RETRIES,
|
|
84
86
|
SESSION_SLUG_MAX_TOKENS,
|
|
85
87
|
SESSION_SLUG_RECENT_MESSAGES,
|
|
86
88
|
TOKEN_ESTIMATE_SAFETY_MARGIN
|
|
87
|
-
} from "./chunk-
|
|
89
|
+
} from "./chunk-UP55PXFH.js";
|
|
88
90
|
import {
|
|
89
91
|
fetchWithTimeout
|
|
90
92
|
} from "./chunk-XQUHC3JZ.js";
|
|
@@ -1130,7 +1132,6 @@ This session was compacted and migrated to a new session ID. The summary above p
|
|
|
1130
1132
|
}
|
|
1131
1133
|
|
|
1132
1134
|
// src/memory/compaction.ts
|
|
1133
|
-
import { encodingForModel } from "js-tiktoken";
|
|
1134
1135
|
var DEFAULT_COMPACTION_CONFIG = {
|
|
1135
1136
|
enabled: true,
|
|
1136
1137
|
maxMessages: COMPACTION_MAX_MESSAGES,
|
|
@@ -1375,56 +1376,119 @@ var CompactionManager = class {
|
|
|
1375
1376
|
// src/memory/observation-masking.ts
|
|
1376
1377
|
var DEFAULT_MASKING_CONFIG = {
|
|
1377
1378
|
keepRecentCount: MASKING_KEEP_RECENT_COUNT,
|
|
1378
|
-
keepErrorResults: true
|
|
1379
|
+
keepErrorResults: true,
|
|
1380
|
+
truncationThreshold: RESULT_TRUNCATION_THRESHOLD,
|
|
1381
|
+
truncationKeepChars: RESULT_TRUNCATION_KEEP_CHARS
|
|
1379
1382
|
};
|
|
1380
1383
|
var isCocoonToolResult = (msg) => msg.role === "user" && Array.isArray(msg.content) && msg.content.some((c2) => c2.type === "text" && c2.text.includes("<tool_response>"));
|
|
1381
|
-
function
|
|
1384
|
+
function isExempt(toolMsg, config, toolRegistry) {
|
|
1385
|
+
if (config.keepErrorResults && toolMsg.isError) return true;
|
|
1386
|
+
if (toolRegistry && toolRegistry.getToolCategory(toolMsg.toolName) === "data-bearing")
|
|
1387
|
+
return true;
|
|
1388
|
+
return false;
|
|
1389
|
+
}
|
|
1390
|
+
function truncateToolResult(text, keepChars) {
|
|
1391
|
+
try {
|
|
1392
|
+
const parsed = JSON.parse(text);
|
|
1393
|
+
if (parsed.data?.summary) {
|
|
1394
|
+
return JSON.stringify({
|
|
1395
|
+
success: parsed.success,
|
|
1396
|
+
data: { summary: parsed.data.summary, _truncated: true }
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1399
|
+
if (parsed.data?.message) {
|
|
1400
|
+
return JSON.stringify({
|
|
1401
|
+
success: parsed.success,
|
|
1402
|
+
data: { summary: parsed.data.message, _truncated: true }
|
|
1403
|
+
});
|
|
1404
|
+
}
|
|
1405
|
+
} catch {
|
|
1406
|
+
}
|
|
1407
|
+
return text.slice(0, keepChars) + `
|
|
1408
|
+
...[truncated, original: ${text.length} chars]`;
|
|
1409
|
+
}
|
|
1410
|
+
function maskOldToolResults(messages, options) {
|
|
1411
|
+
const config = options?.config ?? DEFAULT_MASKING_CONFIG;
|
|
1412
|
+
const toolRegistry = options?.toolRegistry;
|
|
1413
|
+
const iterStart = options?.currentIterationStartIndex;
|
|
1382
1414
|
const toolResults = messages.map((msg, index) => ({ msg, index })).filter(({ msg }) => msg.role === "toolResult" || isCocoonToolResult(msg));
|
|
1383
|
-
|
|
1415
|
+
const needsMasking = toolResults.length > config.keepRecentCount;
|
|
1416
|
+
const needsTruncation = iterStart !== void 0 && config.truncationThreshold > 0;
|
|
1417
|
+
if (!needsMasking && !needsTruncation) {
|
|
1384
1418
|
return messages;
|
|
1385
1419
|
}
|
|
1386
|
-
const toMask = toolResults.slice(0, -config.keepRecentCount);
|
|
1387
1420
|
const result = [...messages];
|
|
1388
|
-
|
|
1389
|
-
|
|
1421
|
+
if (needsMasking) {
|
|
1422
|
+
const toMask = toolResults.slice(0, -config.keepRecentCount);
|
|
1423
|
+
for (const { msg, index } of toMask) {
|
|
1424
|
+
if (isCocoonToolResult(msg)) {
|
|
1425
|
+
result[index] = {
|
|
1426
|
+
...msg,
|
|
1427
|
+
content: [{ type: "text", text: "[Tool response masked]" }]
|
|
1428
|
+
};
|
|
1429
|
+
continue;
|
|
1430
|
+
}
|
|
1431
|
+
const toolMsg = msg;
|
|
1432
|
+
if (isExempt(toolMsg, config, toolRegistry)) continue;
|
|
1433
|
+
let summaryText = "";
|
|
1434
|
+
try {
|
|
1435
|
+
const textBlock = toolMsg.content.find((c2) => c2.type === "text");
|
|
1436
|
+
if (textBlock) {
|
|
1437
|
+
const parsed = JSON.parse(textBlock.text);
|
|
1438
|
+
if (parsed.data?.summary) {
|
|
1439
|
+
summaryText = ` - ${parsed.data.summary}`;
|
|
1440
|
+
} else if (parsed.data?.message) {
|
|
1441
|
+
summaryText = ` - ${parsed.data.message}`;
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
} catch {
|
|
1445
|
+
}
|
|
1390
1446
|
result[index] = {
|
|
1391
|
-
...
|
|
1392
|
-
content: [
|
|
1447
|
+
...toolMsg,
|
|
1448
|
+
content: [
|
|
1449
|
+
{
|
|
1450
|
+
type: "text",
|
|
1451
|
+
text: `[Tool: ${toolMsg.toolName} - ${toolMsg.isError ? "ERROR" : "OK"}${summaryText}]`
|
|
1452
|
+
}
|
|
1453
|
+
]
|
|
1393
1454
|
};
|
|
1394
|
-
continue;
|
|
1395
|
-
}
|
|
1396
|
-
const toolMsg = msg;
|
|
1397
|
-
if (config.keepErrorResults && toolMsg.isError) {
|
|
1398
|
-
continue;
|
|
1399
1455
|
}
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1456
|
+
}
|
|
1457
|
+
if (needsTruncation) {
|
|
1458
|
+
const recentResults = needsMasking ? toolResults.slice(-config.keepRecentCount) : toolResults;
|
|
1459
|
+
for (const { msg, index } of recentResults) {
|
|
1460
|
+
if (index >= iterStart) continue;
|
|
1461
|
+
if (isCocoonToolResult(msg)) {
|
|
1462
|
+
const userMsg = msg;
|
|
1463
|
+
if (!Array.isArray(userMsg.content)) continue;
|
|
1464
|
+
const textBlock2 = userMsg.content.find((c2) => c2.type === "text");
|
|
1465
|
+
if (textBlock2 && textBlock2.text.length > config.truncationThreshold) {
|
|
1466
|
+
result[index] = {
|
|
1467
|
+
...userMsg,
|
|
1468
|
+
content: [
|
|
1469
|
+
{
|
|
1470
|
+
type: "text",
|
|
1471
|
+
text: truncateToolResult(textBlock2.text, config.truncationKeepChars)
|
|
1472
|
+
}
|
|
1473
|
+
]
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
1403
1476
|
continue;
|
|
1404
1477
|
}
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
try {
|
|
1478
|
+
const toolMsg = msg;
|
|
1479
|
+
if (isExempt(toolMsg, config, toolRegistry)) continue;
|
|
1408
1480
|
const textBlock = toolMsg.content.find((c2) => c2.type === "text");
|
|
1409
|
-
if (textBlock)
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1481
|
+
if (!textBlock || textBlock.text.length <= config.truncationThreshold) continue;
|
|
1482
|
+
result[index] = {
|
|
1483
|
+
...toolMsg,
|
|
1484
|
+
content: [
|
|
1485
|
+
{
|
|
1486
|
+
type: "text",
|
|
1487
|
+
text: truncateToolResult(textBlock.text, config.truncationKeepChars)
|
|
1488
|
+
}
|
|
1489
|
+
]
|
|
1490
|
+
};
|
|
1418
1491
|
}
|
|
1419
|
-
result[index] = {
|
|
1420
|
-
...toolMsg,
|
|
1421
|
-
content: [
|
|
1422
|
-
{
|
|
1423
|
-
type: "text",
|
|
1424
|
-
text: `[Tool: ${toolMsg.toolName} - ${toolMsg.isError ? "ERROR" : "OK"}${summaryText}]`
|
|
1425
|
-
}
|
|
1426
|
-
]
|
|
1427
|
-
};
|
|
1428
1492
|
}
|
|
1429
1493
|
return result;
|
|
1430
1494
|
}
|
|
@@ -1484,6 +1548,8 @@ var AgentRuntime = class {
|
|
|
1484
1548
|
contextBuilder = null;
|
|
1485
1549
|
toolRegistry = null;
|
|
1486
1550
|
embedder = null;
|
|
1551
|
+
hookRunner;
|
|
1552
|
+
userHookEvaluator;
|
|
1487
1553
|
constructor(config, soul, toolRegistry) {
|
|
1488
1554
|
this.config = config;
|
|
1489
1555
|
this.soul = soul ?? "";
|
|
@@ -1504,6 +1570,12 @@ var AgentRuntime = class {
|
|
|
1504
1570
|
this.compactionManager = new CompactionManager(DEFAULT_COMPACTION_CONFIG);
|
|
1505
1571
|
}
|
|
1506
1572
|
}
|
|
1573
|
+
setHookRunner(runner) {
|
|
1574
|
+
this.hookRunner = runner;
|
|
1575
|
+
}
|
|
1576
|
+
setUserHookEvaluator(evaluator) {
|
|
1577
|
+
this.userHookEvaluator = evaluator;
|
|
1578
|
+
}
|
|
1507
1579
|
initializeContextBuilder(embedder, vectorEnabled) {
|
|
1508
1580
|
this.embedder = embedder;
|
|
1509
1581
|
const db = getDatabase().getDb();
|
|
@@ -1528,12 +1600,59 @@ var AgentRuntime = class {
|
|
|
1528
1600
|
messageId,
|
|
1529
1601
|
replyContext
|
|
1530
1602
|
} = opts;
|
|
1603
|
+
const effectiveIsGroup = isGroup ?? false;
|
|
1604
|
+
const processStartTime = Date.now();
|
|
1531
1605
|
try {
|
|
1606
|
+
let userHookContext = "";
|
|
1607
|
+
if (this.userHookEvaluator) {
|
|
1608
|
+
const hookResult = this.userHookEvaluator.evaluate(userMessage);
|
|
1609
|
+
if (hookResult.blocked) {
|
|
1610
|
+
log6.info("Message blocked by keyword filter");
|
|
1611
|
+
return { content: hookResult.blockMessage ?? "", toolCalls: [] };
|
|
1612
|
+
}
|
|
1613
|
+
if (hookResult.additionalContext) {
|
|
1614
|
+
userHookContext = sanitizeForContext(hookResult.additionalContext);
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
let effectiveMessage = userMessage;
|
|
1618
|
+
let hookMessageContext = "";
|
|
1619
|
+
if (this.hookRunner) {
|
|
1620
|
+
const msgEvent = {
|
|
1621
|
+
chatId,
|
|
1622
|
+
senderId: toolContext?.senderId ? String(toolContext.senderId) : chatId,
|
|
1623
|
+
senderName: userName ?? "",
|
|
1624
|
+
isGroup: effectiveIsGroup,
|
|
1625
|
+
isReply: !!replyContext,
|
|
1626
|
+
replyToMessageId: replyContext ? messageId : void 0,
|
|
1627
|
+
messageId: messageId ?? 0,
|
|
1628
|
+
timestamp: timestamp ?? Date.now(),
|
|
1629
|
+
text: userMessage,
|
|
1630
|
+
block: false,
|
|
1631
|
+
blockReason: "",
|
|
1632
|
+
additionalContext: ""
|
|
1633
|
+
};
|
|
1634
|
+
await this.hookRunner.runModifyingHook("message:receive", msgEvent);
|
|
1635
|
+
if (msgEvent.block) {
|
|
1636
|
+
log6.info(`\u{1F6AB} Message blocked by hook: ${msgEvent.blockReason || "no reason"}`);
|
|
1637
|
+
return { content: "", toolCalls: [] };
|
|
1638
|
+
}
|
|
1639
|
+
effectiveMessage = sanitizeForContext(msgEvent.text);
|
|
1640
|
+
if (msgEvent.additionalContext) {
|
|
1641
|
+
hookMessageContext = sanitizeForContext(msgEvent.additionalContext);
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1532
1644
|
let session = getOrCreateSession(chatId);
|
|
1533
1645
|
const now = timestamp ?? Date.now();
|
|
1534
1646
|
const resetPolicy = this.config.agent.session_reset_policy;
|
|
1535
1647
|
if (shouldResetSession(session, resetPolicy)) {
|
|
1536
1648
|
log6.info(`\u{1F504} Auto-resetting session based on policy`);
|
|
1649
|
+
if (this.hookRunner) {
|
|
1650
|
+
await this.hookRunner.runObservingHook("session:end", {
|
|
1651
|
+
sessionId: session.sessionId,
|
|
1652
|
+
chatId,
|
|
1653
|
+
messageCount: session.messageCount
|
|
1654
|
+
});
|
|
1655
|
+
}
|
|
1537
1656
|
if (transcriptExists(session.sessionId)) {
|
|
1538
1657
|
try {
|
|
1539
1658
|
log6.info(`\u{1F4BE} Saving memory before daily reset...`);
|
|
@@ -1555,11 +1674,19 @@ var AgentRuntime = class {
|
|
|
1555
1674
|
session = resetSessionWithPolicy(chatId, resetPolicy);
|
|
1556
1675
|
}
|
|
1557
1676
|
let context = loadContextFromTranscript(session.sessionId);
|
|
1558
|
-
|
|
1677
|
+
const isNewSession = context.messages.length === 0;
|
|
1678
|
+
if (!isNewSession) {
|
|
1559
1679
|
log6.info(`\u{1F4D6} Loading existing session: ${session.sessionId}`);
|
|
1560
1680
|
} else {
|
|
1561
1681
|
log6.info(`\u{1F195} Starting new session: ${session.sessionId}`);
|
|
1562
1682
|
}
|
|
1683
|
+
if (this.hookRunner) {
|
|
1684
|
+
await this.hookRunner.runObservingHook("session:start", {
|
|
1685
|
+
sessionId: session.sessionId,
|
|
1686
|
+
chatId,
|
|
1687
|
+
isResume: !isNewSession
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1563
1690
|
const previousTimestamp = session.updatedAt;
|
|
1564
1691
|
let formattedMessage = formatMessageEnvelope({
|
|
1565
1692
|
channel: "Telegram",
|
|
@@ -1569,8 +1696,8 @@ var AgentRuntime = class {
|
|
|
1569
1696
|
senderRank,
|
|
1570
1697
|
timestamp: now,
|
|
1571
1698
|
previousTimestamp,
|
|
1572
|
-
body:
|
|
1573
|
-
isGroup:
|
|
1699
|
+
body: effectiveMessage,
|
|
1700
|
+
isGroup: effectiveIsGroup,
|
|
1574
1701
|
hasMedia,
|
|
1575
1702
|
mediaType,
|
|
1576
1703
|
messageId,
|
|
@@ -1589,10 +1716,10 @@ ${formattedMessage}`;
|
|
|
1589
1716
|
log6.info(`\u{1F4E8} ${msgType}: "${preview}${formattedMessage.length > 50 ? "..." : ""}"`);
|
|
1590
1717
|
let relevantContext = "";
|
|
1591
1718
|
let queryEmbedding;
|
|
1592
|
-
const isNonTrivial = !isTrivialMessage(
|
|
1719
|
+
const isNonTrivial = !isTrivialMessage(effectiveMessage);
|
|
1593
1720
|
if (this.embedder && isNonTrivial) {
|
|
1594
1721
|
try {
|
|
1595
|
-
queryEmbedding = await this.embedder.embedQuery(
|
|
1722
|
+
queryEmbedding = await this.embedder.embedQuery(effectiveMessage);
|
|
1596
1723
|
} catch (error) {
|
|
1597
1724
|
log6.warn({ err: error }, "Embedding computation failed");
|
|
1598
1725
|
}
|
|
@@ -1600,7 +1727,7 @@ ${formattedMessage}`;
|
|
|
1600
1727
|
if (this.contextBuilder && isNonTrivial) {
|
|
1601
1728
|
try {
|
|
1602
1729
|
const dbContext = await this.contextBuilder.buildContext({
|
|
1603
|
-
query:
|
|
1730
|
+
query: effectiveMessage,
|
|
1604
1731
|
chatId,
|
|
1605
1732
|
includeAgentMemory: true,
|
|
1606
1733
|
includeFeedHistory: true,
|
|
@@ -1645,8 +1772,23 @@ ${statsContext}
|
|
|
1645
1772
|
${relevantContext}` : `You are in a Telegram conversation with chat ID: ${chatId}. Maintain conversation continuity.
|
|
1646
1773
|
|
|
1647
1774
|
${statsContext}`;
|
|
1775
|
+
let hookAdditionalContext = "";
|
|
1776
|
+
if (this.hookRunner) {
|
|
1777
|
+
const promptEvent = {
|
|
1778
|
+
chatId,
|
|
1779
|
+
sessionId: session.sessionId,
|
|
1780
|
+
isGroup: effectiveIsGroup,
|
|
1781
|
+
additionalContext: ""
|
|
1782
|
+
};
|
|
1783
|
+
await this.hookRunner.runModifyingHook("prompt:before", promptEvent);
|
|
1784
|
+
hookAdditionalContext = sanitizeForContext(promptEvent.additionalContext);
|
|
1785
|
+
}
|
|
1648
1786
|
const compactionConfig = this.compactionManager.getConfig();
|
|
1649
1787
|
const needsMemoryFlush = compactionConfig.enabled && compactionConfig.memoryFlushEnabled && context.messages.length > Math.floor((compactionConfig.maxMessages ?? 200) * 0.75);
|
|
1788
|
+
const allHookContext = [userHookContext, hookAdditionalContext, hookMessageContext].filter(Boolean).join("\n\n");
|
|
1789
|
+
const finalContext = additionalContext + (allHookContext ? `
|
|
1790
|
+
|
|
1791
|
+
${allHookContext}` : "");
|
|
1650
1792
|
const systemPrompt = buildSystemPrompt({
|
|
1651
1793
|
soul: this.soul,
|
|
1652
1794
|
userName,
|
|
@@ -1654,11 +1796,23 @@ ${statsContext}`;
|
|
|
1654
1796
|
senderId: toolContext?.senderId,
|
|
1655
1797
|
ownerName: this.config.telegram.owner_name,
|
|
1656
1798
|
ownerUsername: this.config.telegram.owner_username,
|
|
1657
|
-
context:
|
|
1658
|
-
includeMemory: !
|
|
1659
|
-
includeStrategy: !
|
|
1799
|
+
context: finalContext,
|
|
1800
|
+
includeMemory: !effectiveIsGroup,
|
|
1801
|
+
includeStrategy: !effectiveIsGroup,
|
|
1660
1802
|
memoryFlushWarning: needsMemoryFlush
|
|
1661
1803
|
});
|
|
1804
|
+
if (this.hookRunner) {
|
|
1805
|
+
const promptAfterEvent = {
|
|
1806
|
+
chatId,
|
|
1807
|
+
sessionId: session.sessionId,
|
|
1808
|
+
isGroup: effectiveIsGroup,
|
|
1809
|
+
promptLength: systemPrompt.length,
|
|
1810
|
+
sectionCount: (systemPrompt.match(/^#{1,3} /gm) || []).length,
|
|
1811
|
+
ragContextLength: relevantContext.length,
|
|
1812
|
+
hookContextLength: allHookContext.length
|
|
1813
|
+
};
|
|
1814
|
+
await this.hookRunner.runObservingHook("prompt:after", promptAfterEvent);
|
|
1815
|
+
}
|
|
1662
1816
|
const userMsg = {
|
|
1663
1817
|
role: "user",
|
|
1664
1818
|
content: formattedMessage,
|
|
@@ -1686,12 +1840,12 @@ ${statsContext}`;
|
|
|
1686
1840
|
let tools;
|
|
1687
1841
|
{
|
|
1688
1842
|
const toolIndex = this.toolRegistry?.getToolIndex();
|
|
1689
|
-
const useRAG = toolIndex?.isIndexed && this.config.tool_rag?.enabled !== false && !isTrivialMessage(
|
|
1843
|
+
const useRAG = toolIndex?.isIndexed && this.config.tool_rag?.enabled !== false && !isTrivialMessage(effectiveMessage) && !(providerMeta.toolLimit === null && this.config.tool_rag?.skip_unlimited_providers !== false);
|
|
1690
1844
|
if (useRAG && this.toolRegistry && queryEmbedding) {
|
|
1691
1845
|
tools = await this.toolRegistry.getForContextWithRAG(
|
|
1692
|
-
|
|
1846
|
+
effectiveMessage,
|
|
1693
1847
|
queryEmbedding,
|
|
1694
|
-
|
|
1848
|
+
effectiveIsGroup,
|
|
1695
1849
|
providerMeta.toolLimit,
|
|
1696
1850
|
chatId,
|
|
1697
1851
|
isAdmin
|
|
@@ -1699,7 +1853,7 @@ ${statsContext}`;
|
|
|
1699
1853
|
log6.info(`\u{1F50D} Tool RAG: ${tools.length}/${this.toolRegistry.count} tools selected`);
|
|
1700
1854
|
} else {
|
|
1701
1855
|
tools = this.toolRegistry?.getForContext(
|
|
1702
|
-
|
|
1856
|
+
effectiveIsGroup,
|
|
1703
1857
|
providerMeta.toolLimit,
|
|
1704
1858
|
chatId,
|
|
1705
1859
|
isAdmin
|
|
@@ -1718,11 +1872,11 @@ ${statsContext}`;
|
|
|
1718
1872
|
while (iteration < maxIterations) {
|
|
1719
1873
|
iteration++;
|
|
1720
1874
|
log6.debug(`\u{1F504} Agentic iteration ${iteration}/${maxIterations}`);
|
|
1721
|
-
const
|
|
1722
|
-
|
|
1723
|
-
void 0,
|
|
1724
|
-
|
|
1725
|
-
);
|
|
1875
|
+
const iterationStartIndex = context.messages.length;
|
|
1876
|
+
const maskedMessages = maskOldToolResults(context.messages, {
|
|
1877
|
+
toolRegistry: this.toolRegistry ?? void 0,
|
|
1878
|
+
currentIterationStartIndex: iterationStartIndex
|
|
1879
|
+
});
|
|
1726
1880
|
const maskedContext = { ...context, messages: maskedMessages };
|
|
1727
1881
|
const response2 = await chatWithContext(this.config.agent, {
|
|
1728
1882
|
systemPrompt,
|
|
@@ -1734,6 +1888,21 @@ ${statsContext}`;
|
|
|
1734
1888
|
const assistantMsg = response2.message;
|
|
1735
1889
|
if (assistantMsg.stopReason === "error") {
|
|
1736
1890
|
const errorMsg = assistantMsg.errorMessage || "";
|
|
1891
|
+
if (this.hookRunner) {
|
|
1892
|
+
const errorCode = errorMsg.includes("429") || errorMsg.toLowerCase().includes("rate") ? "RATE_LIMIT" : isContextOverflowError(errorMsg) ? "CONTEXT_OVERFLOW" : errorMsg.includes("500") || errorMsg.includes("502") || errorMsg.includes("503") ? "PROVIDER_ERROR" : "UNKNOWN";
|
|
1893
|
+
const responseErrorEvent = {
|
|
1894
|
+
chatId,
|
|
1895
|
+
sessionId: session.sessionId,
|
|
1896
|
+
isGroup: effectiveIsGroup,
|
|
1897
|
+
error: errorMsg,
|
|
1898
|
+
errorCode,
|
|
1899
|
+
provider,
|
|
1900
|
+
model: this.config.agent.model,
|
|
1901
|
+
retryCount: rateLimitRetries + serverErrorRetries,
|
|
1902
|
+
durationMs: Date.now() - processStartTime
|
|
1903
|
+
};
|
|
1904
|
+
await this.hookRunner.runObservingHook("response:error", responseErrorEvent);
|
|
1905
|
+
}
|
|
1737
1906
|
if (isContextOverflowError(errorMsg)) {
|
|
1738
1907
|
overflowResets++;
|
|
1739
1908
|
if (overflowResets > 1) {
|
|
@@ -1822,33 +1991,103 @@ ${statsContext}`;
|
|
|
1822
1991
|
const fullContext = {
|
|
1823
1992
|
...toolContext,
|
|
1824
1993
|
chatId,
|
|
1825
|
-
isGroup:
|
|
1994
|
+
isGroup: effectiveIsGroup
|
|
1826
1995
|
};
|
|
1827
|
-
|
|
1996
|
+
let toolParams = block.arguments ?? {};
|
|
1997
|
+
let blocked = false;
|
|
1998
|
+
let blockReason = "";
|
|
1999
|
+
if (this.hookRunner) {
|
|
2000
|
+
const beforeEvent = {
|
|
2001
|
+
toolName: block.name,
|
|
2002
|
+
params: structuredClone(toolParams),
|
|
2003
|
+
chatId,
|
|
2004
|
+
isGroup: effectiveIsGroup,
|
|
2005
|
+
block: false,
|
|
2006
|
+
blockReason: ""
|
|
2007
|
+
};
|
|
2008
|
+
await this.hookRunner.runModifyingHook("tool:before", beforeEvent);
|
|
2009
|
+
if (beforeEvent.block) {
|
|
2010
|
+
blocked = true;
|
|
2011
|
+
blockReason = beforeEvent.blockReason || "Blocked by plugin hook";
|
|
2012
|
+
} else {
|
|
2013
|
+
toolParams = structuredClone(beforeEvent.params);
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
let result;
|
|
2017
|
+
if (blocked) {
|
|
2018
|
+
result = { success: false, error: blockReason };
|
|
2019
|
+
if (this.hookRunner) {
|
|
2020
|
+
const afterEvent = {
|
|
2021
|
+
toolName: block.name,
|
|
2022
|
+
params: structuredClone(toolParams),
|
|
2023
|
+
result: { success: false, error: blockReason },
|
|
2024
|
+
durationMs: 0,
|
|
2025
|
+
chatId,
|
|
2026
|
+
isGroup: effectiveIsGroup,
|
|
2027
|
+
blocked: true,
|
|
2028
|
+
blockReason
|
|
2029
|
+
};
|
|
2030
|
+
await this.hookRunner.runObservingHook("tool:after", afterEvent);
|
|
2031
|
+
}
|
|
2032
|
+
} else {
|
|
2033
|
+
const startTime = Date.now();
|
|
2034
|
+
try {
|
|
2035
|
+
result = await this.toolRegistry.execute(
|
|
2036
|
+
{ ...block, arguments: toolParams },
|
|
2037
|
+
fullContext
|
|
2038
|
+
);
|
|
2039
|
+
} catch (execErr) {
|
|
2040
|
+
const durationMs2 = Date.now() - startTime;
|
|
2041
|
+
const errMsg = execErr instanceof Error ? execErr.message : String(execErr);
|
|
2042
|
+
const errStack = execErr instanceof Error ? execErr.stack : void 0;
|
|
2043
|
+
result = { success: false, error: errMsg };
|
|
2044
|
+
if (this.hookRunner) {
|
|
2045
|
+
const errorEvent = {
|
|
2046
|
+
toolName: block.name,
|
|
2047
|
+
params: structuredClone(toolParams),
|
|
2048
|
+
error: errMsg,
|
|
2049
|
+
// Note: stack traces are exposed to plugins for debugging — accepted tradeoff
|
|
2050
|
+
stack: errStack,
|
|
2051
|
+
chatId,
|
|
2052
|
+
isGroup: effectiveIsGroup,
|
|
2053
|
+
durationMs: durationMs2
|
|
2054
|
+
};
|
|
2055
|
+
await this.hookRunner.runObservingHook("tool:error", errorEvent);
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
const durationMs = Date.now() - startTime;
|
|
2059
|
+
if (this.hookRunner) {
|
|
2060
|
+
const afterEvent = {
|
|
2061
|
+
toolName: block.name,
|
|
2062
|
+
params: structuredClone(toolParams),
|
|
2063
|
+
result: { success: result.success, data: result.data, error: result.error },
|
|
2064
|
+
durationMs,
|
|
2065
|
+
chatId,
|
|
2066
|
+
isGroup: effectiveIsGroup
|
|
2067
|
+
};
|
|
2068
|
+
await this.hookRunner.runObservingHook("tool:after", afterEvent);
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
1828
2071
|
log6.debug(`${block.name}: ${result.success ? "\u2713" : "\u2717"} ${result.error || ""}`);
|
|
1829
2072
|
iterationToolNames.push(`${block.name} ${result.success ? "\u2713" : "\u2717"}`);
|
|
1830
2073
|
totalToolCalls.push({
|
|
1831
2074
|
name: block.name,
|
|
1832
2075
|
input: block.arguments
|
|
1833
2076
|
});
|
|
1834
|
-
let resultText = JSON.stringify(result
|
|
2077
|
+
let resultText = JSON.stringify(result);
|
|
1835
2078
|
if (resultText.length > MAX_TOOL_RESULT_SIZE) {
|
|
1836
2079
|
log6.warn(`\u26A0\uFE0F Tool result too large (${resultText.length} chars), truncating...`);
|
|
1837
2080
|
const data = result.data;
|
|
1838
2081
|
if (data?.summary || data?.message) {
|
|
1839
|
-
resultText = JSON.stringify(
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
},
|
|
1849
|
-
null,
|
|
1850
|
-
2
|
|
1851
|
-
);
|
|
2082
|
+
resultText = JSON.stringify({
|
|
2083
|
+
success: result.success,
|
|
2084
|
+
data: {
|
|
2085
|
+
summary: data.summary || data.message,
|
|
2086
|
+
_truncated: true,
|
|
2087
|
+
_originalSize: resultText.length,
|
|
2088
|
+
_message: "Full data truncated. Use limit parameter for smaller results."
|
|
2089
|
+
}
|
|
2090
|
+
});
|
|
1852
2091
|
} else {
|
|
1853
2092
|
resultText = resultText.slice(0, MAX_TOOL_RESULT_SIZE) + "\n...[TRUNCATED]";
|
|
1854
2093
|
}
|
|
@@ -1947,6 +2186,42 @@ ${statsContext}`;
|
|
|
1947
2186
|
log6.warn("\u26A0\uFE0F Empty response with zero tokens - possible API issue");
|
|
1948
2187
|
content = "I couldn't process your request. Please try again.";
|
|
1949
2188
|
}
|
|
2189
|
+
let responseMetadata = {};
|
|
2190
|
+
if (this.hookRunner) {
|
|
2191
|
+
const responseBeforeEvent = {
|
|
2192
|
+
chatId,
|
|
2193
|
+
sessionId: session.sessionId,
|
|
2194
|
+
isGroup: effectiveIsGroup,
|
|
2195
|
+
originalText: content,
|
|
2196
|
+
text: content,
|
|
2197
|
+
block: false,
|
|
2198
|
+
blockReason: "",
|
|
2199
|
+
metadata: {}
|
|
2200
|
+
};
|
|
2201
|
+
await this.hookRunner.runModifyingHook("response:before", responseBeforeEvent);
|
|
2202
|
+
if (responseBeforeEvent.block) {
|
|
2203
|
+
log6.info(
|
|
2204
|
+
`\u{1F6AB} Response blocked by hook: ${responseBeforeEvent.blockReason || "no reason"}`
|
|
2205
|
+
);
|
|
2206
|
+
content = "";
|
|
2207
|
+
} else {
|
|
2208
|
+
content = responseBeforeEvent.text;
|
|
2209
|
+
}
|
|
2210
|
+
responseMetadata = responseBeforeEvent.metadata;
|
|
2211
|
+
}
|
|
2212
|
+
if (this.hookRunner) {
|
|
2213
|
+
const responseAfterEvent = {
|
|
2214
|
+
chatId,
|
|
2215
|
+
sessionId: session.sessionId,
|
|
2216
|
+
isGroup: effectiveIsGroup,
|
|
2217
|
+
text: content,
|
|
2218
|
+
durationMs: Date.now() - processStartTime,
|
|
2219
|
+
toolsUsed: totalToolCalls.map((tc) => tc.name),
|
|
2220
|
+
tokenUsage: accumulatedUsage.input > 0 || accumulatedUsage.output > 0 ? { input: accumulatedUsage.input, output: accumulatedUsage.output } : void 0,
|
|
2221
|
+
metadata: responseMetadata
|
|
2222
|
+
};
|
|
2223
|
+
await this.hookRunner.runObservingHook("response:after", responseAfterEvent);
|
|
2224
|
+
}
|
|
1950
2225
|
return {
|
|
1951
2226
|
content,
|
|
1952
2227
|
toolCalls: totalToolCalls
|
|
@@ -2124,6 +2399,7 @@ function parseHtml(html) {
|
|
|
2124
2399
|
new Api2.MessageEntityCustomEmoji({
|
|
2125
2400
|
offset: open.offset,
|
|
2126
2401
|
length,
|
|
2402
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- GramJS BigInteger compat
|
|
2127
2403
|
documentId: BigInt(open.emojiId)
|
|
2128
2404
|
})
|
|
2129
2405
|
);
|
|
@@ -2262,11 +2538,14 @@ var InlineRouter = class {
|
|
|
2262
2538
|
}
|
|
2263
2539
|
async handleInlineQuery(ctx, pluginName, query, plugin) {
|
|
2264
2540
|
try {
|
|
2541
|
+
const inlineQuery = ctx.inlineQuery;
|
|
2542
|
+
const from = ctx.from;
|
|
2543
|
+
if (!inlineQuery || !from || !plugin.onInlineQuery) return;
|
|
2265
2544
|
const iqCtx = {
|
|
2266
2545
|
query,
|
|
2267
|
-
queryId:
|
|
2268
|
-
userId:
|
|
2269
|
-
offset:
|
|
2546
|
+
queryId: inlineQuery.id,
|
|
2547
|
+
userId: from.id,
|
|
2548
|
+
offset: inlineQuery.offset
|
|
2270
2549
|
};
|
|
2271
2550
|
const results = await withTimeout(
|
|
2272
2551
|
plugin.onInlineQuery(iqCtx),
|
|
@@ -2291,7 +2570,7 @@ var InlineRouter = class {
|
|
|
2291
2570
|
try {
|
|
2292
2571
|
let matchedHandler;
|
|
2293
2572
|
let matchGroups = [];
|
|
2294
|
-
for (const entry of plugin.onCallback) {
|
|
2573
|
+
for (const entry of plugin.onCallback ?? []) {
|
|
2295
2574
|
const groups = globMatch(entry.regex, strippedData);
|
|
2296
2575
|
if (groups !== null) {
|
|
2297
2576
|
matchedHandler = entry.handler;
|
|
@@ -2304,14 +2583,17 @@ var InlineRouter = class {
|
|
|
2304
2583
|
return;
|
|
2305
2584
|
}
|
|
2306
2585
|
const gramjsBotRef = this.gramjsBot;
|
|
2586
|
+
const callbackQuery = ctx.callbackQuery;
|
|
2587
|
+
const from = ctx.from;
|
|
2588
|
+
if (!from || !callbackQuery) return;
|
|
2307
2589
|
const cbCtx = {
|
|
2308
2590
|
data: strippedData,
|
|
2309
2591
|
match: matchGroups,
|
|
2310
|
-
userId:
|
|
2311
|
-
username:
|
|
2312
|
-
inlineMessageId:
|
|
2592
|
+
userId: from.id,
|
|
2593
|
+
username: from.username,
|
|
2594
|
+
inlineMessageId: callbackQuery.inline_message_id,
|
|
2313
2595
|
chatId: ctx.chat?.id?.toString(),
|
|
2314
|
-
messageId:
|
|
2596
|
+
messageId: callbackQuery.message?.message_id,
|
|
2315
2597
|
async answer(text, alert) {
|
|
2316
2598
|
if (!answered) {
|
|
2317
2599
|
answered = true;
|
|
@@ -2320,7 +2602,7 @@ var InlineRouter = class {
|
|
|
2320
2602
|
},
|
|
2321
2603
|
async editMessage(text, opts) {
|
|
2322
2604
|
const styledButtons = opts?.keyboard ? prefixButtons(opts.keyboard, pluginName) : void 0;
|
|
2323
|
-
const inlineMsgId = ctx.callbackQuery
|
|
2605
|
+
const inlineMsgId = ctx.callbackQuery?.inline_message_id;
|
|
2324
2606
|
if (inlineMsgId && gramjsBotRef?.isConnected() && styledButtons) {
|
|
2325
2607
|
try {
|
|
2326
2608
|
const strippedHtml = stripCustomEmoji(text);
|
|
@@ -2334,10 +2616,9 @@ var InlineRouter = class {
|
|
|
2334
2616
|
});
|
|
2335
2617
|
return;
|
|
2336
2618
|
} catch (error) {
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
);
|
|
2619
|
+
const errMsg = error?.errorMessage;
|
|
2620
|
+
if (errMsg === "MESSAGE_NOT_MODIFIED") return;
|
|
2621
|
+
log7.debug(`GramJS edit failed, falling back to Grammy: ${errMsg || error}`);
|
|
2341
2622
|
}
|
|
2342
2623
|
}
|
|
2343
2624
|
const replyMarkup = styledButtons ? toGrammyKeyboard(styledButtons) : void 0;
|
|
@@ -2368,13 +2649,15 @@ var InlineRouter = class {
|
|
|
2368
2649
|
}
|
|
2369
2650
|
async handleChosenResult(ctx, pluginName, plugin) {
|
|
2370
2651
|
try {
|
|
2371
|
-
const
|
|
2652
|
+
const chosenResult = ctx.chosenInlineResult;
|
|
2653
|
+
if (!chosenResult || !plugin.onChosenResult) return;
|
|
2654
|
+
const resultId = chosenResult.result_id;
|
|
2372
2655
|
const colonIdx = resultId.indexOf(":");
|
|
2373
2656
|
const strippedResultId = colonIdx > 0 ? resultId.slice(colonIdx + 1) : resultId;
|
|
2374
2657
|
const crCtx = {
|
|
2375
2658
|
resultId: strippedResultId,
|
|
2376
|
-
inlineMessageId:
|
|
2377
|
-
query:
|
|
2659
|
+
inlineMessageId: chosenResult.inline_message_id,
|
|
2660
|
+
query: chosenResult.query
|
|
2378
2661
|
};
|
|
2379
2662
|
await plugin.onChosenResult(crCtx);
|
|
2380
2663
|
} catch (error) {
|
|
@@ -2460,6 +2743,30 @@ import { readdirSync as readdirSync2, readFileSync as readFileSync5, existsSync
|
|
|
2460
2743
|
import { join as join5 } from "path";
|
|
2461
2744
|
import { pathToFileURL } from "url";
|
|
2462
2745
|
import { execFile } from "child_process";
|
|
2746
|
+
|
|
2747
|
+
// src/agent/tools/plugin-config-store.ts
|
|
2748
|
+
function getPluginPriorities(db) {
|
|
2749
|
+
const rows = db.prepare("SELECT plugin_name, priority FROM plugin_config").all();
|
|
2750
|
+
const map = /* @__PURE__ */ new Map();
|
|
2751
|
+
for (const row of rows) {
|
|
2752
|
+
map.set(row.plugin_name, row.priority);
|
|
2753
|
+
}
|
|
2754
|
+
return map;
|
|
2755
|
+
}
|
|
2756
|
+
function setPluginPriority(db, pluginName, priority) {
|
|
2757
|
+
db.prepare(
|
|
2758
|
+
`INSERT INTO plugin_config (plugin_name, priority, updated_at)
|
|
2759
|
+
VALUES (?, ?, datetime('now'))
|
|
2760
|
+
ON CONFLICT(plugin_name) DO UPDATE SET
|
|
2761
|
+
priority = excluded.priority,
|
|
2762
|
+
updated_at = excluded.updated_at`
|
|
2763
|
+
).run(pluginName, priority);
|
|
2764
|
+
}
|
|
2765
|
+
function resetPluginPriority(db, pluginName) {
|
|
2766
|
+
db.prepare("DELETE FROM plugin_config WHERE plugin_name = ?").run(pluginName);
|
|
2767
|
+
}
|
|
2768
|
+
|
|
2769
|
+
// src/agent/tools/plugin-loader.ts
|
|
2463
2770
|
import { promisify } from "util";
|
|
2464
2771
|
|
|
2465
2772
|
// src/agent/tools/plugin-validator.ts
|
|
@@ -2491,7 +2798,14 @@ var ManifestSchema = z.object({
|
|
|
2491
2798
|
inlinePerMinute: z.number().positive().optional(),
|
|
2492
2799
|
callbackPerMinute: z.number().positive().optional()
|
|
2493
2800
|
}).optional()
|
|
2494
|
-
}).optional()
|
|
2801
|
+
}).optional(),
|
|
2802
|
+
hooks: z.array(
|
|
2803
|
+
z.object({
|
|
2804
|
+
name: z.string().min(1).max(64),
|
|
2805
|
+
priority: z.number().optional(),
|
|
2806
|
+
description: z.string().max(256).optional()
|
|
2807
|
+
})
|
|
2808
|
+
).optional()
|
|
2495
2809
|
});
|
|
2496
2810
|
function validateManifest(raw) {
|
|
2497
2811
|
return ManifestSchema.parse(raw);
|
|
@@ -2607,8 +2921,9 @@ async function sendTon(params) {
|
|
|
2607
2921
|
log9.info(`Sent ${amount} TON to ${toAddress2.slice(0, 8)}... - seqno: ${seqno}`);
|
|
2608
2922
|
return pseudoHash;
|
|
2609
2923
|
} catch (error) {
|
|
2610
|
-
const
|
|
2611
|
-
|
|
2924
|
+
const err = error;
|
|
2925
|
+
const status = err?.status || err?.response?.status;
|
|
2926
|
+
if (status === 429 || status !== void 0 && status >= 500) {
|
|
2612
2927
|
invalidateTonClientCache();
|
|
2613
2928
|
}
|
|
2614
2929
|
log9.error({ err: error }, "Error sending TON");
|
|
@@ -3142,9 +3457,9 @@ var retryStatusCodes = /* @__PURE__ */ new Set([
|
|
|
3142
3457
|
var nullBodyResponses = /* @__PURE__ */ new Set([101, 204, 205, 304]);
|
|
3143
3458
|
function createFetch(globalOptions = {}) {
|
|
3144
3459
|
const {
|
|
3145
|
-
fetch:
|
|
3460
|
+
fetch: fetch3 = globalThis.fetch,
|
|
3146
3461
|
Headers: Headers2 = globalThis.Headers,
|
|
3147
|
-
AbortController:
|
|
3462
|
+
AbortController: AbortController3 = globalThis.AbortController
|
|
3148
3463
|
} = globalOptions;
|
|
3149
3464
|
async function onError(context) {
|
|
3150
3465
|
const isAbort = context.error && context.error.name === "AbortError" && !context.options.timeout || false;
|
|
@@ -3228,7 +3543,7 @@ function createFetch(globalOptions = {}) {
|
|
|
3228
3543
|
}
|
|
3229
3544
|
let abortTimeout;
|
|
3230
3545
|
if (!context.options.signal && context.options.timeout) {
|
|
3231
|
-
const controller = new
|
|
3546
|
+
const controller = new AbortController3();
|
|
3232
3547
|
abortTimeout = setTimeout(() => {
|
|
3233
3548
|
const error = new Error(
|
|
3234
3549
|
"[TimeoutError]: The operation was aborted due to timeout"
|
|
@@ -3240,7 +3555,7 @@ function createFetch(globalOptions = {}) {
|
|
|
3240
3555
|
context.options.signal = controller.signal;
|
|
3241
3556
|
}
|
|
3242
3557
|
try {
|
|
3243
|
-
context.response = await
|
|
3558
|
+
context.response = await fetch3(
|
|
3244
3559
|
context.request,
|
|
3245
3560
|
context.options
|
|
3246
3561
|
);
|
|
@@ -3302,7 +3617,7 @@ function createFetch(globalOptions = {}) {
|
|
|
3302
3617
|
return r3._data;
|
|
3303
3618
|
};
|
|
3304
3619
|
$fetch.raw = $fetchRaw;
|
|
3305
|
-
$fetch.native = (...args) =>
|
|
3620
|
+
$fetch.native = (...args) => fetch3(...args);
|
|
3306
3621
|
$fetch.create = (defaultOptions = {}, customGlobalOptions = {}) => createFetch({
|
|
3307
3622
|
...globalOptions,
|
|
3308
3623
|
...customGlobalOptions,
|
|
@@ -3333,10 +3648,10 @@ function createNodeFetch() {
|
|
|
3333
3648
|
return r(input, { ...nodeFetchOptions, ...init });
|
|
3334
3649
|
};
|
|
3335
3650
|
}
|
|
3336
|
-
var
|
|
3651
|
+
var fetch2 = globalThis.fetch ? (...args) => globalThis.fetch(...args) : createNodeFetch();
|
|
3337
3652
|
var Headers = globalThis.Headers || n;
|
|
3338
|
-
var
|
|
3339
|
-
var ofetch = createFetch({ fetch, Headers, AbortController });
|
|
3653
|
+
var AbortController2 = globalThis.AbortController || T;
|
|
3654
|
+
var ofetch = createFetch({ fetch: fetch2, Headers, AbortController: AbortController2 });
|
|
3340
3655
|
|
|
3341
3656
|
// node_modules/@ston-fi/api/dist/esm/index.js
|
|
3342
3657
|
var __create = Object.create;
|
|
@@ -6192,7 +6507,7 @@ var stonApiClient = new StonApiClient();
|
|
|
6192
6507
|
function isTon(asset) {
|
|
6193
6508
|
return asset.toLowerCase() === "ton";
|
|
6194
6509
|
}
|
|
6195
|
-
async function getStonfiQuote(fromAsset, toAsset, amount, slippage,
|
|
6510
|
+
async function getStonfiQuote(fromAsset, toAsset, amount, slippage, log15) {
|
|
6196
6511
|
try {
|
|
6197
6512
|
const isTonInput = isTon(fromAsset);
|
|
6198
6513
|
const isTonOutput = isTon(toAsset);
|
|
@@ -6223,11 +6538,11 @@ async function getStonfiQuote(fromAsset, toAsset, amount, slippage, log13) {
|
|
|
6223
6538
|
fee: feeAmount.toFixed(6)
|
|
6224
6539
|
};
|
|
6225
6540
|
} catch (err) {
|
|
6226
|
-
|
|
6541
|
+
log15.debug("dex.quoteSTONfi() failed:", err);
|
|
6227
6542
|
return null;
|
|
6228
6543
|
}
|
|
6229
6544
|
}
|
|
6230
|
-
async function getDedustQuote(fromAsset, toAsset, amount, slippage,
|
|
6545
|
+
async function getDedustQuote(fromAsset, toAsset, amount, slippage, log15) {
|
|
6231
6546
|
try {
|
|
6232
6547
|
const isTonInput = isTon(fromAsset);
|
|
6233
6548
|
const isTonOutput = isTon(toAsset);
|
|
@@ -6261,11 +6576,11 @@ async function getDedustQuote(fromAsset, toAsset, amount, slippage, log13) {
|
|
|
6261
6576
|
poolType
|
|
6262
6577
|
};
|
|
6263
6578
|
} catch (err) {
|
|
6264
|
-
|
|
6579
|
+
log15.debug("dex.quoteDeDust() failed:", err);
|
|
6265
6580
|
return null;
|
|
6266
6581
|
}
|
|
6267
6582
|
}
|
|
6268
|
-
async function executeSTONfiSwap(params,
|
|
6583
|
+
async function executeSTONfiSwap(params, _log) {
|
|
6269
6584
|
const { fromAsset, toAsset, amount, slippage = 0.01 } = params;
|
|
6270
6585
|
const walletData = loadWallet();
|
|
6271
6586
|
if (!walletData) {
|
|
@@ -6352,7 +6667,7 @@ async function executeSTONfiSwap(params, log13) {
|
|
|
6352
6667
|
};
|
|
6353
6668
|
});
|
|
6354
6669
|
}
|
|
6355
|
-
async function executeDedustSwap(params,
|
|
6670
|
+
async function executeDedustSwap(params, _log) {
|
|
6356
6671
|
const { fromAsset, toAsset, amount, slippage = 0.01 } = params;
|
|
6357
6672
|
const walletData = loadWallet();
|
|
6358
6673
|
if (!walletData) {
|
|
@@ -6372,7 +6687,7 @@ async function executeDedustSwap(params, log13) {
|
|
|
6372
6687
|
const fromDecimals = await getDecimals(isTonInput ? "ton" : fromAsset);
|
|
6373
6688
|
const toDecimals = await getDecimals(isTonOutput ? "ton" : toAsset);
|
|
6374
6689
|
const amountIn = toUnits(amount, fromDecimals);
|
|
6375
|
-
const { amountOut
|
|
6690
|
+
const { amountOut } = await pool.getEstimatedSwapOut({
|
|
6376
6691
|
assetIn: fromAssetObj,
|
|
6377
6692
|
amountIn
|
|
6378
6693
|
});
|
|
@@ -6433,14 +6748,14 @@ function validateDexParams(amount, slippage) {
|
|
|
6433
6748
|
throw new PluginSDKError("Slippage must be between 0 and 1", "OPERATION_FAILED");
|
|
6434
6749
|
}
|
|
6435
6750
|
}
|
|
6436
|
-
function createDexSDK(
|
|
6751
|
+
function createDexSDK(log15) {
|
|
6437
6752
|
return {
|
|
6438
6753
|
async quote(params) {
|
|
6439
6754
|
validateDexParams(params.amount, params.slippage);
|
|
6440
6755
|
const slippage = params.slippage ?? 0.01;
|
|
6441
6756
|
const [stonfi, dedust] = await Promise.all([
|
|
6442
|
-
getStonfiQuote(params.fromAsset, params.toAsset, params.amount, slippage,
|
|
6443
|
-
getDedustQuote(params.fromAsset, params.toAsset, params.amount, slippage,
|
|
6757
|
+
getStonfiQuote(params.fromAsset, params.toAsset, params.amount, slippage, log15),
|
|
6758
|
+
getDedustQuote(params.fromAsset, params.toAsset, params.amount, slippage, log15)
|
|
6444
6759
|
]);
|
|
6445
6760
|
if (!stonfi && !dedust) {
|
|
6446
6761
|
throw new PluginSDKError("No DEX has liquidity for this pair", "OPERATION_FAILED");
|
|
@@ -6474,7 +6789,7 @@ function createDexSDK(log13) {
|
|
|
6474
6789
|
params.toAsset,
|
|
6475
6790
|
params.amount,
|
|
6476
6791
|
params.slippage ?? 0.01,
|
|
6477
|
-
|
|
6792
|
+
log15
|
|
6478
6793
|
);
|
|
6479
6794
|
},
|
|
6480
6795
|
async quoteDeDust(params) {
|
|
@@ -6483,25 +6798,25 @@ function createDexSDK(log13) {
|
|
|
6483
6798
|
params.toAsset,
|
|
6484
6799
|
params.amount,
|
|
6485
6800
|
params.slippage ?? 0.01,
|
|
6486
|
-
|
|
6801
|
+
log15
|
|
6487
6802
|
);
|
|
6488
6803
|
},
|
|
6489
6804
|
async swap(params) {
|
|
6490
6805
|
validateDexParams(params.amount, params.slippage);
|
|
6491
6806
|
if (params.dex === "stonfi") {
|
|
6492
|
-
return executeSTONfiSwap(params,
|
|
6807
|
+
return executeSTONfiSwap(params, log15);
|
|
6493
6808
|
}
|
|
6494
6809
|
if (params.dex === "dedust") {
|
|
6495
|
-
return executeDedustSwap(params,
|
|
6810
|
+
return executeDedustSwap(params, log15);
|
|
6496
6811
|
}
|
|
6497
6812
|
const quoteResult = await this.quote(params);
|
|
6498
|
-
return quoteResult.recommended === "stonfi" ? executeSTONfiSwap(params,
|
|
6813
|
+
return quoteResult.recommended === "stonfi" ? executeSTONfiSwap(params, log15) : executeDedustSwap(params, log15);
|
|
6499
6814
|
},
|
|
6500
6815
|
async swapSTONfi(params) {
|
|
6501
|
-
return executeSTONfiSwap(params,
|
|
6816
|
+
return executeSTONfiSwap(params, log15);
|
|
6502
6817
|
},
|
|
6503
6818
|
async swapDeDust(params) {
|
|
6504
|
-
return executeDedustSwap(params,
|
|
6819
|
+
return executeDedustSwap(params, log15);
|
|
6505
6820
|
}
|
|
6506
6821
|
};
|
|
6507
6822
|
}
|
|
@@ -6538,7 +6853,7 @@ function normalizeDomain(domain) {
|
|
|
6538
6853
|
if (!d.endsWith(".ton")) d += ".ton";
|
|
6539
6854
|
return d;
|
|
6540
6855
|
}
|
|
6541
|
-
function createDnsSDK(
|
|
6856
|
+
function createDnsSDK(log15) {
|
|
6542
6857
|
return {
|
|
6543
6858
|
async check(domain) {
|
|
6544
6859
|
const normalized = normalizeDomain(domain);
|
|
@@ -6561,7 +6876,7 @@ function createDnsSDK(log13) {
|
|
|
6561
6876
|
};
|
|
6562
6877
|
} catch (err) {
|
|
6563
6878
|
if (err instanceof PluginSDKError) throw err;
|
|
6564
|
-
|
|
6879
|
+
log15.debug("dns.check() failed:", err);
|
|
6565
6880
|
throw new PluginSDKError(
|
|
6566
6881
|
`Failed to check domain: ${err instanceof Error ? err.message : String(err)}`,
|
|
6567
6882
|
"OPERATION_FAILED"
|
|
@@ -6574,7 +6889,7 @@ function createDnsSDK(log13) {
|
|
|
6574
6889
|
const response = await tonapiFetch(`/dns/${encodeURIComponent(normalized)}`);
|
|
6575
6890
|
if (response.status === 404) return null;
|
|
6576
6891
|
if (!response.ok) {
|
|
6577
|
-
|
|
6892
|
+
log15.debug(`dns.resolve() TonAPI error: ${response.status}`);
|
|
6578
6893
|
return null;
|
|
6579
6894
|
}
|
|
6580
6895
|
const data = await response.json();
|
|
@@ -6586,7 +6901,7 @@ function createDnsSDK(log13) {
|
|
|
6586
6901
|
expirationDate: data.expiring_at || void 0
|
|
6587
6902
|
};
|
|
6588
6903
|
} catch (err) {
|
|
6589
|
-
|
|
6904
|
+
log15.debug("dns.resolve() failed:", err);
|
|
6590
6905
|
return null;
|
|
6591
6906
|
}
|
|
6592
6907
|
},
|
|
@@ -6596,7 +6911,7 @@ function createDnsSDK(log13) {
|
|
|
6596
6911
|
`/dns/auctions?tld=ton&limit=${Math.min(limit ?? 20, 100)}`
|
|
6597
6912
|
);
|
|
6598
6913
|
if (!response.ok) {
|
|
6599
|
-
|
|
6914
|
+
log15.debug(`dns.getAuctions() TonAPI error: ${response.status}`);
|
|
6600
6915
|
return [];
|
|
6601
6916
|
}
|
|
6602
6917
|
const data = await response.json();
|
|
@@ -6609,7 +6924,7 @@ function createDnsSDK(log13) {
|
|
|
6609
6924
|
bids: a.bids || 0
|
|
6610
6925
|
}));
|
|
6611
6926
|
} catch (err) {
|
|
6612
|
-
|
|
6927
|
+
log15.debug("dns.getAuctions() failed:", err);
|
|
6613
6928
|
return [];
|
|
6614
6929
|
}
|
|
6615
6930
|
},
|
|
@@ -6648,7 +6963,10 @@ function createDnsSDK(log13) {
|
|
|
6648
6963
|
throw new PluginSDKError(`No active auction found for ${normalized}`, "OPERATION_FAILED");
|
|
6649
6964
|
}
|
|
6650
6965
|
try {
|
|
6651
|
-
await sendWalletMessage(
|
|
6966
|
+
await sendWalletMessage(
|
|
6967
|
+
Address6.parse(checkResult.nftAddress),
|
|
6968
|
+
toNano15(amount.toString())
|
|
6969
|
+
);
|
|
6652
6970
|
return { domain: normalized, bidAmount: amount.toString(), success: true };
|
|
6653
6971
|
} catch (err) {
|
|
6654
6972
|
if (err instanceof PluginSDKError) throw err;
|
|
@@ -6745,7 +7063,7 @@ import {
|
|
|
6745
7063
|
WalletContractV5R1 as WalletContractV5R14,
|
|
6746
7064
|
internal as internal4
|
|
6747
7065
|
} from "@ton/ton";
|
|
6748
|
-
import { Address as TonAddress, beginCell as beginCell13, SendMode as SendMode4 } from "@ton/core";
|
|
7066
|
+
import { Address as TonAddress, beginCell as beginCell13, SendMode as SendMode4, storeMessage } from "@ton/core";
|
|
6749
7067
|
|
|
6750
7068
|
// src/ton/format-transactions.ts
|
|
6751
7069
|
import { fromNano } from "@ton/ton";
|
|
@@ -6920,25 +7238,25 @@ function findJettonBalance(balances, jettonAddress) {
|
|
|
6920
7238
|
}
|
|
6921
7239
|
});
|
|
6922
7240
|
}
|
|
6923
|
-
function cleanupOldTransactions(db, retentionDays,
|
|
7241
|
+
function cleanupOldTransactions(db, retentionDays, log15) {
|
|
6924
7242
|
if (Math.random() > CLEANUP_PROBABILITY) return;
|
|
6925
7243
|
try {
|
|
6926
7244
|
const cutoff = Math.floor(Date.now() / 1e3) - retentionDays * 24 * 60 * 60;
|
|
6927
7245
|
const result = db.prepare("DELETE FROM used_transactions WHERE used_at < ?").run(cutoff);
|
|
6928
7246
|
if (result.changes > 0) {
|
|
6929
|
-
|
|
7247
|
+
log15.debug(`Cleaned up ${result.changes} old transaction records (>${retentionDays}d)`);
|
|
6930
7248
|
}
|
|
6931
7249
|
} catch (err) {
|
|
6932
|
-
|
|
7250
|
+
log15.error("Transaction cleanup failed:", err);
|
|
6933
7251
|
}
|
|
6934
7252
|
}
|
|
6935
|
-
function createTonSDK(
|
|
7253
|
+
function createTonSDK(log15, db) {
|
|
6936
7254
|
return {
|
|
6937
7255
|
getAddress() {
|
|
6938
7256
|
try {
|
|
6939
7257
|
return getWalletAddress();
|
|
6940
7258
|
} catch (err) {
|
|
6941
|
-
|
|
7259
|
+
log15.error("ton.getAddress() failed:", err);
|
|
6942
7260
|
return null;
|
|
6943
7261
|
}
|
|
6944
7262
|
},
|
|
@@ -6948,7 +7266,7 @@ function createTonSDK(log13, db) {
|
|
|
6948
7266
|
if (!addr) return null;
|
|
6949
7267
|
return await getWalletBalance(addr);
|
|
6950
7268
|
} catch (err) {
|
|
6951
|
-
|
|
7269
|
+
log15.error("ton.getBalance() failed:", err);
|
|
6952
7270
|
return null;
|
|
6953
7271
|
}
|
|
6954
7272
|
},
|
|
@@ -6956,7 +7274,7 @@ function createTonSDK(log13, db) {
|
|
|
6956
7274
|
try {
|
|
6957
7275
|
return await getTonPrice();
|
|
6958
7276
|
} catch (err) {
|
|
6959
|
-
|
|
7277
|
+
log15.error("ton.getPrice() failed:", err);
|
|
6960
7278
|
return null;
|
|
6961
7279
|
}
|
|
6962
7280
|
},
|
|
@@ -7007,7 +7325,7 @@ function createTonSDK(log13, db) {
|
|
|
7007
7325
|
);
|
|
7008
7326
|
return formatTransactions(transactions);
|
|
7009
7327
|
} catch (err) {
|
|
7010
|
-
|
|
7328
|
+
log15.error("ton.getTransactions() failed:", err);
|
|
7011
7329
|
return [];
|
|
7012
7330
|
}
|
|
7013
7331
|
},
|
|
@@ -7030,7 +7348,7 @@ function createTonSDK(log13, db) {
|
|
|
7030
7348
|
throw new PluginSDKError("Wallet not initialized", "WALLET_NOT_INITIALIZED");
|
|
7031
7349
|
}
|
|
7032
7350
|
const maxAgeMinutes = params.maxAgeMinutes ?? DEFAULT_MAX_AGE_MINUTES;
|
|
7033
|
-
cleanupOldTransactions(db, DEFAULT_TX_RETENTION_DAYS,
|
|
7351
|
+
cleanupOldTransactions(db, DEFAULT_TX_RETENTION_DAYS, log15);
|
|
7034
7352
|
try {
|
|
7035
7353
|
const txs = await this.getTransactions(address4, 20);
|
|
7036
7354
|
for (const tx of txs) {
|
|
@@ -7064,7 +7382,7 @@ function createTonSDK(log13, db) {
|
|
|
7064
7382
|
};
|
|
7065
7383
|
} catch (err) {
|
|
7066
7384
|
if (err instanceof PluginSDKError) throw err;
|
|
7067
|
-
|
|
7385
|
+
log15.error("ton.verifyPayment() failed:", err);
|
|
7068
7386
|
return {
|
|
7069
7387
|
verified: false,
|
|
7070
7388
|
error: `Verification failed: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -7078,7 +7396,7 @@ function createTonSDK(log13, db) {
|
|
|
7078
7396
|
if (!addr) return [];
|
|
7079
7397
|
const response = await tonapiFetch(`/accounts/${encodeURIComponent(addr)}/jettons`);
|
|
7080
7398
|
if (!response.ok) {
|
|
7081
|
-
|
|
7399
|
+
log15.error(`ton.getJettonBalances() TonAPI error: ${response.status}`);
|
|
7082
7400
|
return [];
|
|
7083
7401
|
}
|
|
7084
7402
|
const data = await response.json();
|
|
@@ -7102,7 +7420,7 @@ function createTonSDK(log13, db) {
|
|
|
7102
7420
|
}
|
|
7103
7421
|
return balances;
|
|
7104
7422
|
} catch (err) {
|
|
7105
|
-
|
|
7423
|
+
log15.error("ton.getJettonBalances() failed:", err);
|
|
7106
7424
|
return [];
|
|
7107
7425
|
}
|
|
7108
7426
|
},
|
|
@@ -7111,7 +7429,7 @@ function createTonSDK(log13, db) {
|
|
|
7111
7429
|
const response = await tonapiFetch(`/jettons/${encodeURIComponent(jettonAddress)}`);
|
|
7112
7430
|
if (response.status === 404) return null;
|
|
7113
7431
|
if (!response.ok) {
|
|
7114
|
-
|
|
7432
|
+
log15.error(`ton.getJettonInfo() TonAPI error: ${response.status}`);
|
|
7115
7433
|
return null;
|
|
7116
7434
|
}
|
|
7117
7435
|
const data = await response.json();
|
|
@@ -7129,7 +7447,7 @@ function createTonSDK(log13, db) {
|
|
|
7129
7447
|
image: data.preview || metadata.image || void 0
|
|
7130
7448
|
};
|
|
7131
7449
|
} catch (err) {
|
|
7132
|
-
|
|
7450
|
+
log15.error("ton.getJettonInfo() failed:", err);
|
|
7133
7451
|
return null;
|
|
7134
7452
|
}
|
|
7135
7453
|
},
|
|
@@ -7221,7 +7539,7 @@ function createTonSDK(log13, db) {
|
|
|
7221
7539
|
if (status === 429 || status && status >= 500) {
|
|
7222
7540
|
invalidateTonClientCache();
|
|
7223
7541
|
if (attempt < MAX_SEND_ATTEMPTS) {
|
|
7224
|
-
|
|
7542
|
+
log15.warn(
|
|
7225
7543
|
`sendJetton attempt ${attempt} failed (${status}): ${JSON.stringify(respData ?? err.message)}, retrying...`
|
|
7226
7544
|
);
|
|
7227
7545
|
await new Promise((r3) => setTimeout(r3, 1e3 * attempt));
|
|
@@ -7250,17 +7568,197 @@ function createTonSDK(log13, db) {
|
|
|
7250
7568
|
try {
|
|
7251
7569
|
const response = await tonapiFetch(`/accounts/${encodeURIComponent(ownerAddress)}/jettons`);
|
|
7252
7570
|
if (!response.ok) {
|
|
7253
|
-
|
|
7571
|
+
log15.error(`ton.getJettonWalletAddress() TonAPI error: ${response.status}`);
|
|
7254
7572
|
return null;
|
|
7255
7573
|
}
|
|
7256
7574
|
const data = await response.json();
|
|
7257
7575
|
const match = findJettonBalance(data.balances ?? [], jettonAddress);
|
|
7258
7576
|
return match ? match.wallet_address.address : null;
|
|
7259
7577
|
} catch (err) {
|
|
7260
|
-
|
|
7578
|
+
log15.error("ton.getJettonWalletAddress() failed:", err);
|
|
7579
|
+
return null;
|
|
7580
|
+
}
|
|
7581
|
+
},
|
|
7582
|
+
// ─── Signed Transfers (no broadcast) ──────────────────────────
|
|
7583
|
+
async createTransfer(to, amount, comment) {
|
|
7584
|
+
const walletData = loadWallet();
|
|
7585
|
+
if (!walletData) {
|
|
7586
|
+
throw new PluginSDKError("Wallet not initialized", "WALLET_NOT_INITIALIZED");
|
|
7587
|
+
}
|
|
7588
|
+
if (!Number.isFinite(amount) || amount <= 0) {
|
|
7589
|
+
throw new PluginSDKError("Amount must be a positive number", "OPERATION_FAILED");
|
|
7590
|
+
}
|
|
7591
|
+
try {
|
|
7592
|
+
TonAddress.parse(to);
|
|
7593
|
+
} catch {
|
|
7594
|
+
throw new PluginSDKError("Invalid TON address format", "INVALID_ADDRESS");
|
|
7595
|
+
}
|
|
7596
|
+
try {
|
|
7597
|
+
const keyPair = await getKeyPair();
|
|
7598
|
+
if (!keyPair) {
|
|
7599
|
+
throw new PluginSDKError("Wallet key derivation failed", "OPERATION_FAILED");
|
|
7600
|
+
}
|
|
7601
|
+
const boc = await withTxLock(async () => {
|
|
7602
|
+
const wallet = WalletContractV5R14.create({
|
|
7603
|
+
workchain: 0,
|
|
7604
|
+
publicKey: keyPair.publicKey
|
|
7605
|
+
});
|
|
7606
|
+
const client = await getCachedTonClient();
|
|
7607
|
+
const contract = client.open(wallet);
|
|
7608
|
+
const seqno = await contract.getSeqno();
|
|
7609
|
+
const transferCell = wallet.createTransfer({
|
|
7610
|
+
seqno,
|
|
7611
|
+
secretKey: keyPair.secretKey,
|
|
7612
|
+
sendMode: SendMode4.PAY_GAS_SEPARATELY,
|
|
7613
|
+
messages: [
|
|
7614
|
+
internal4({
|
|
7615
|
+
to: TonAddress.parse(to),
|
|
7616
|
+
value: tonToNano(amount),
|
|
7617
|
+
body: comment || "",
|
|
7618
|
+
bounce: false
|
|
7619
|
+
})
|
|
7620
|
+
]
|
|
7621
|
+
});
|
|
7622
|
+
const extMsg = beginCell13().store(
|
|
7623
|
+
storeMessage({
|
|
7624
|
+
info: {
|
|
7625
|
+
type: "external-in",
|
|
7626
|
+
dest: wallet.address,
|
|
7627
|
+
importFee: 0n
|
|
7628
|
+
},
|
|
7629
|
+
init: seqno === 0 ? wallet.init : void 0,
|
|
7630
|
+
body: transferCell
|
|
7631
|
+
})
|
|
7632
|
+
).endCell();
|
|
7633
|
+
return extMsg.toBoc().toString("base64");
|
|
7634
|
+
});
|
|
7635
|
+
return {
|
|
7636
|
+
boc,
|
|
7637
|
+
publicKey: walletData.publicKey,
|
|
7638
|
+
walletVersion: "v5r1"
|
|
7639
|
+
};
|
|
7640
|
+
} catch (err) {
|
|
7641
|
+
if (err instanceof PluginSDKError) throw err;
|
|
7642
|
+
throw new PluginSDKError(
|
|
7643
|
+
`Failed to create transfer: ${err instanceof Error ? err.message : String(err)}`,
|
|
7644
|
+
"OPERATION_FAILED"
|
|
7645
|
+
);
|
|
7646
|
+
}
|
|
7647
|
+
},
|
|
7648
|
+
async createJettonTransfer(jettonAddress, to, amount, opts) {
|
|
7649
|
+
const walletData = loadWallet();
|
|
7650
|
+
if (!walletData) {
|
|
7651
|
+
throw new PluginSDKError("Wallet not initialized", "WALLET_NOT_INITIALIZED");
|
|
7652
|
+
}
|
|
7653
|
+
if (!Number.isFinite(amount) || amount <= 0) {
|
|
7654
|
+
throw new PluginSDKError("Amount must be a positive number", "OPERATION_FAILED");
|
|
7655
|
+
}
|
|
7656
|
+
try {
|
|
7657
|
+
TonAddress.parse(to);
|
|
7658
|
+
} catch {
|
|
7659
|
+
throw new PluginSDKError("Invalid recipient address", "INVALID_ADDRESS");
|
|
7660
|
+
}
|
|
7661
|
+
try {
|
|
7662
|
+
const jettonsResponse = await tonapiFetch(
|
|
7663
|
+
`/accounts/${encodeURIComponent(walletData.address)}/jettons`
|
|
7664
|
+
);
|
|
7665
|
+
if (!jettonsResponse.ok) {
|
|
7666
|
+
throw new PluginSDKError(
|
|
7667
|
+
`Failed to fetch jetton balances: ${jettonsResponse.status}`,
|
|
7668
|
+
"OPERATION_FAILED"
|
|
7669
|
+
);
|
|
7670
|
+
}
|
|
7671
|
+
const jettonsData = await jettonsResponse.json();
|
|
7672
|
+
const jettonBalance = findJettonBalance(jettonsData.balances ?? [], jettonAddress);
|
|
7673
|
+
if (!jettonBalance) {
|
|
7674
|
+
throw new PluginSDKError(
|
|
7675
|
+
`You don't own any of this jetton: ${jettonAddress}`,
|
|
7676
|
+
"OPERATION_FAILED"
|
|
7677
|
+
);
|
|
7678
|
+
}
|
|
7679
|
+
const senderJettonWallet = jettonBalance.wallet_address.address;
|
|
7680
|
+
const decimals = jettonBalance.jetton.decimals ?? 9;
|
|
7681
|
+
const currentBalance = BigInt(jettonBalance.balance);
|
|
7682
|
+
const amountStr = amount.toFixed(decimals);
|
|
7683
|
+
const [whole, frac = ""] = amountStr.split(".");
|
|
7684
|
+
const amountInUnits = BigInt(whole + (frac + "0".repeat(decimals)).slice(0, decimals));
|
|
7685
|
+
if (amountInUnits > currentBalance) {
|
|
7686
|
+
const balStr = formatTokenBalance(currentBalance, decimals);
|
|
7687
|
+
throw new PluginSDKError(
|
|
7688
|
+
`Insufficient balance. Have ${balStr}, need ${amount}`,
|
|
7689
|
+
"OPERATION_FAILED"
|
|
7690
|
+
);
|
|
7691
|
+
}
|
|
7692
|
+
const comment = opts?.comment;
|
|
7693
|
+
let forwardPayload = beginCell13().endCell();
|
|
7694
|
+
if (comment) {
|
|
7695
|
+
forwardPayload = beginCell13().storeUint(0, 32).storeStringTail(comment).endCell();
|
|
7696
|
+
}
|
|
7697
|
+
const JETTON_TRANSFER_OP = 260734629;
|
|
7698
|
+
const messageBody = beginCell13().storeUint(JETTON_TRANSFER_OP, 32).storeUint(0, 64).storeCoins(amountInUnits).storeAddress(TonAddress.parse(to)).storeAddress(TonAddress.parse(walletData.address)).storeBit(false).storeCoins(comment ? tonToNano("0.01") : BigInt(1)).storeBit(comment ? 1 : 0).storeRef(comment ? forwardPayload : beginCell13().endCell()).endCell();
|
|
7699
|
+
const keyPair = await getKeyPair();
|
|
7700
|
+
if (!keyPair) {
|
|
7701
|
+
throw new PluginSDKError("Wallet key derivation failed", "OPERATION_FAILED");
|
|
7702
|
+
}
|
|
7703
|
+
const boc = await withTxLock(async () => {
|
|
7704
|
+
const wallet = WalletContractV5R14.create({
|
|
7705
|
+
workchain: 0,
|
|
7706
|
+
publicKey: keyPair.publicKey
|
|
7707
|
+
});
|
|
7708
|
+
const client = await getCachedTonClient();
|
|
7709
|
+
const walletContract = client.open(wallet);
|
|
7710
|
+
const seqno = await walletContract.getSeqno();
|
|
7711
|
+
const transferCell = wallet.createTransfer({
|
|
7712
|
+
seqno,
|
|
7713
|
+
secretKey: keyPair.secretKey,
|
|
7714
|
+
sendMode: SendMode4.PAY_GAS_SEPARATELY,
|
|
7715
|
+
messages: [
|
|
7716
|
+
internal4({
|
|
7717
|
+
to: TonAddress.parse(senderJettonWallet),
|
|
7718
|
+
value: tonToNano("0.05"),
|
|
7719
|
+
body: messageBody,
|
|
7720
|
+
bounce: true
|
|
7721
|
+
})
|
|
7722
|
+
]
|
|
7723
|
+
});
|
|
7724
|
+
const extMsg = beginCell13().store(
|
|
7725
|
+
storeMessage({
|
|
7726
|
+
info: {
|
|
7727
|
+
type: "external-in",
|
|
7728
|
+
dest: wallet.address,
|
|
7729
|
+
importFee: 0n
|
|
7730
|
+
},
|
|
7731
|
+
init: seqno === 0 ? wallet.init : void 0,
|
|
7732
|
+
body: transferCell
|
|
7733
|
+
})
|
|
7734
|
+
).endCell();
|
|
7735
|
+
return extMsg.toBoc().toString("base64");
|
|
7736
|
+
});
|
|
7737
|
+
return {
|
|
7738
|
+
boc,
|
|
7739
|
+
publicKey: walletData.publicKey,
|
|
7740
|
+
walletVersion: "v5r1"
|
|
7741
|
+
};
|
|
7742
|
+
} catch (err) {
|
|
7743
|
+
if (err instanceof PluginSDKError) throw err;
|
|
7744
|
+
throw new PluginSDKError(
|
|
7745
|
+
`Failed to create jetton transfer: ${err instanceof Error ? err.message : String(err)}`,
|
|
7746
|
+
"OPERATION_FAILED"
|
|
7747
|
+
);
|
|
7748
|
+
}
|
|
7749
|
+
},
|
|
7750
|
+
getPublicKey() {
|
|
7751
|
+
try {
|
|
7752
|
+
const wallet = loadWallet();
|
|
7753
|
+
return wallet?.publicKey ?? null;
|
|
7754
|
+
} catch (err) {
|
|
7755
|
+
log15.error("ton.getPublicKey() failed:", err);
|
|
7261
7756
|
return null;
|
|
7262
7757
|
}
|
|
7263
7758
|
},
|
|
7759
|
+
getWalletVersion() {
|
|
7760
|
+
return "v5r1";
|
|
7761
|
+
},
|
|
7264
7762
|
// ─── NFT ─────────────────────────────────────────────────────
|
|
7265
7763
|
async getNftItems(ownerAddress) {
|
|
7266
7764
|
try {
|
|
@@ -7270,14 +7768,14 @@ function createTonSDK(log13, db) {
|
|
|
7270
7768
|
`/accounts/${encodeURIComponent(addr)}/nfts?limit=100&indirect_ownership=true`
|
|
7271
7769
|
);
|
|
7272
7770
|
if (!response.ok) {
|
|
7273
|
-
|
|
7771
|
+
log15.error(`ton.getNftItems() TonAPI error: ${response.status}`);
|
|
7274
7772
|
return [];
|
|
7275
7773
|
}
|
|
7276
7774
|
const data = await response.json();
|
|
7277
7775
|
if (!Array.isArray(data.nft_items)) return [];
|
|
7278
7776
|
return data.nft_items.filter((item) => item.trust !== "blacklist").map((item) => mapNftItem(item));
|
|
7279
7777
|
} catch (err) {
|
|
7280
|
-
|
|
7778
|
+
log15.error("ton.getNftItems() failed:", err);
|
|
7281
7779
|
return [];
|
|
7282
7780
|
}
|
|
7283
7781
|
},
|
|
@@ -7286,13 +7784,13 @@ function createTonSDK(log13, db) {
|
|
|
7286
7784
|
const response = await tonapiFetch(`/nfts/${encodeURIComponent(nftAddress)}`);
|
|
7287
7785
|
if (response.status === 404) return null;
|
|
7288
7786
|
if (!response.ok) {
|
|
7289
|
-
|
|
7787
|
+
log15.error(`ton.getNftInfo() TonAPI error: ${response.status}`);
|
|
7290
7788
|
return null;
|
|
7291
7789
|
}
|
|
7292
7790
|
const item = await response.json();
|
|
7293
7791
|
return mapNftItem(item);
|
|
7294
7792
|
} catch (err) {
|
|
7295
|
-
|
|
7793
|
+
log15.error("ton.getNftInfo() failed:", err);
|
|
7296
7794
|
return null;
|
|
7297
7795
|
}
|
|
7298
7796
|
},
|
|
@@ -7325,7 +7823,7 @@ function createTonSDK(log13, db) {
|
|
|
7325
7823
|
`/rates?tokens=${encodeURIComponent(jettonAddress)}¤cies=usd,ton`
|
|
7326
7824
|
);
|
|
7327
7825
|
if (!response.ok) {
|
|
7328
|
-
|
|
7826
|
+
log15.debug(`ton.getJettonPrice() TonAPI error: ${response.status}`);
|
|
7329
7827
|
return null;
|
|
7330
7828
|
}
|
|
7331
7829
|
const data = await response.json();
|
|
@@ -7339,7 +7837,7 @@ function createTonSDK(log13, db) {
|
|
|
7339
7837
|
change30d: rateData.diff_30d?.USD ?? null
|
|
7340
7838
|
};
|
|
7341
7839
|
} catch (err) {
|
|
7342
|
-
|
|
7840
|
+
log15.debug("ton.getJettonPrice() failed:", err);
|
|
7343
7841
|
return null;
|
|
7344
7842
|
}
|
|
7345
7843
|
},
|
|
@@ -7353,7 +7851,7 @@ function createTonSDK(log13, db) {
|
|
|
7353
7851
|
tonapiFetch(`/jettons/${encodeURIComponent(jettonAddress)}`)
|
|
7354
7852
|
]);
|
|
7355
7853
|
if (!holdersResponse.ok) {
|
|
7356
|
-
|
|
7854
|
+
log15.debug(`ton.getJettonHolders() TonAPI error: ${holdersResponse.status}`);
|
|
7357
7855
|
return [];
|
|
7358
7856
|
}
|
|
7359
7857
|
const data = await holdersResponse.json();
|
|
@@ -7373,7 +7871,7 @@ function createTonSDK(log13, db) {
|
|
|
7373
7871
|
};
|
|
7374
7872
|
});
|
|
7375
7873
|
} catch (err) {
|
|
7376
|
-
|
|
7874
|
+
log15.debug("ton.getJettonHolders() failed:", err);
|
|
7377
7875
|
return [];
|
|
7378
7876
|
}
|
|
7379
7877
|
},
|
|
@@ -7445,13 +7943,13 @@ function createTonSDK(log13, db) {
|
|
|
7445
7943
|
holders: holdersCount
|
|
7446
7944
|
};
|
|
7447
7945
|
} catch (err) {
|
|
7448
|
-
|
|
7946
|
+
log15.debug("ton.getJettonHistory() failed:", err);
|
|
7449
7947
|
return null;
|
|
7450
7948
|
}
|
|
7451
7949
|
},
|
|
7452
7950
|
// ─── Sub-namespaces ───────────────────────────────────────────
|
|
7453
|
-
dex: Object.freeze(createDexSDK(
|
|
7454
|
-
dns: Object.freeze(createDnsSDK(
|
|
7951
|
+
dex: Object.freeze(createDexSDK(log15)),
|
|
7952
|
+
dns: Object.freeze(createDnsSDK(log15))
|
|
7455
7953
|
};
|
|
7456
7954
|
}
|
|
7457
7955
|
function mapNftItem(item) {
|
|
@@ -7500,6 +7998,7 @@ function toSimpleMessage(msg) {
|
|
|
7500
7998
|
return {
|
|
7501
7999
|
id: msg.id,
|
|
7502
8000
|
text: msg.message ?? "",
|
|
8001
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- GramJS fromId is a union of untyped peer types
|
|
7503
8002
|
senderId: Number(msg.fromId?.userId ?? msg.fromId?.channelId ?? 0),
|
|
7504
8003
|
timestamp: new Date(msg.date * 1e3)
|
|
7505
8004
|
};
|
|
@@ -7513,7 +8012,7 @@ async function getApi() {
|
|
|
7513
8012
|
}
|
|
7514
8013
|
|
|
7515
8014
|
// src/sdk/telegram-messages.ts
|
|
7516
|
-
function createTelegramMessagesSDK(bridge,
|
|
8015
|
+
function createTelegramMessagesSDK(bridge, log15) {
|
|
7517
8016
|
function requireBridge2() {
|
|
7518
8017
|
requireBridge(bridge);
|
|
7519
8018
|
}
|
|
@@ -7620,7 +8119,7 @@ function createTelegramMessagesSDK(bridge, log13) {
|
|
|
7620
8119
|
return (resultData.messages ?? []).map(toSimpleMessage);
|
|
7621
8120
|
} catch (err) {
|
|
7622
8121
|
if (err instanceof PluginSDKError) throw err;
|
|
7623
|
-
|
|
8122
|
+
log15.error("telegram.searchMessages() failed:", err);
|
|
7624
8123
|
return [];
|
|
7625
8124
|
}
|
|
7626
8125
|
},
|
|
@@ -7657,6 +8156,7 @@ function createTelegramMessagesSDK(bridge, log13) {
|
|
|
7657
8156
|
limit,
|
|
7658
8157
|
maxId: 0,
|
|
7659
8158
|
minId: 0,
|
|
8159
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- GramJS BigInteger compat requires bigint cast
|
|
7660
8160
|
hash: 0n
|
|
7661
8161
|
})
|
|
7662
8162
|
);
|
|
@@ -7847,6 +8347,7 @@ function createTelegramMessagesSDK(bridge, log13) {
|
|
|
7847
8347
|
const result = await gramJsClient.invoke(
|
|
7848
8348
|
new Api4.messages.GetScheduledHistory({
|
|
7849
8349
|
peer,
|
|
8350
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- GramJS BigInteger compat requires bigint cast
|
|
7850
8351
|
hash: 0n
|
|
7851
8352
|
})
|
|
7852
8353
|
);
|
|
@@ -7861,7 +8362,7 @@ function createTelegramMessagesSDK(bridge, log13) {
|
|
|
7861
8362
|
return messages;
|
|
7862
8363
|
} catch (err) {
|
|
7863
8364
|
if (err instanceof PluginSDKError) throw err;
|
|
7864
|
-
|
|
8365
|
+
log15.error("telegram.getScheduledMessages() failed:", err);
|
|
7865
8366
|
return [];
|
|
7866
8367
|
}
|
|
7867
8368
|
},
|
|
@@ -7922,7 +8423,7 @@ function createTelegramMessagesSDK(bridge, log13) {
|
|
|
7922
8423
|
}
|
|
7923
8424
|
|
|
7924
8425
|
// src/sdk/telegram-social.ts
|
|
7925
|
-
function createTelegramSocialSDK(bridge,
|
|
8426
|
+
function createTelegramSocialSDK(bridge, log15) {
|
|
7926
8427
|
function requireBridge2() {
|
|
7927
8428
|
requireBridge(bridge);
|
|
7928
8429
|
}
|
|
@@ -7997,7 +8498,7 @@ function createTelegramSocialSDK(bridge, log13) {
|
|
|
7997
8498
|
return null;
|
|
7998
8499
|
} catch (err) {
|
|
7999
8500
|
if (err instanceof PluginSDKError) throw err;
|
|
8000
|
-
|
|
8501
|
+
log15.error("telegram.getChatInfo() failed:", err);
|
|
8001
8502
|
return null;
|
|
8002
8503
|
}
|
|
8003
8504
|
},
|
|
@@ -8005,7 +8506,6 @@ function createTelegramSocialSDK(bridge, log13) {
|
|
|
8005
8506
|
requireBridge2();
|
|
8006
8507
|
try {
|
|
8007
8508
|
const client = getClient2();
|
|
8008
|
-
const Api4 = await getApi();
|
|
8009
8509
|
let entity;
|
|
8010
8510
|
try {
|
|
8011
8511
|
const id = typeof userId === "string" ? userId.replace("@", "") : userId.toString();
|
|
@@ -8112,7 +8612,7 @@ function createTelegramSocialSDK(bridge, log13) {
|
|
|
8112
8612
|
});
|
|
8113
8613
|
} catch (err) {
|
|
8114
8614
|
if (err instanceof PluginSDKError) throw err;
|
|
8115
|
-
|
|
8615
|
+
log15.error("telegram.getParticipants() failed:", err);
|
|
8116
8616
|
return [];
|
|
8117
8617
|
}
|
|
8118
8618
|
},
|
|
@@ -8474,7 +8974,7 @@ function createTelegramSocialSDK(bridge, log13) {
|
|
|
8474
8974
|
}));
|
|
8475
8975
|
} catch (err) {
|
|
8476
8976
|
if (err instanceof PluginSDKError) throw err;
|
|
8477
|
-
|
|
8977
|
+
log15.error("telegram.getDialogs() failed:", err);
|
|
8478
8978
|
return [];
|
|
8479
8979
|
}
|
|
8480
8980
|
},
|
|
@@ -8488,7 +8988,7 @@ function createTelegramSocialSDK(bridge, log13) {
|
|
|
8488
8988
|
return messages.map(toSimpleMessage);
|
|
8489
8989
|
} catch (err) {
|
|
8490
8990
|
if (err instanceof PluginSDKError) throw err;
|
|
8491
|
-
|
|
8991
|
+
log15.error("telegram.getHistory() failed:", err);
|
|
8492
8992
|
return [];
|
|
8493
8993
|
}
|
|
8494
8994
|
},
|
|
@@ -8519,7 +9019,7 @@ function createTelegramSocialSDK(bridge, log13) {
|
|
|
8519
9019
|
}));
|
|
8520
9020
|
} catch (err) {
|
|
8521
9021
|
if (err instanceof PluginSDKError) throw err;
|
|
8522
|
-
|
|
9022
|
+
log15.error("telegram.getStarsTransactions() failed:", err);
|
|
8523
9023
|
return [];
|
|
8524
9024
|
}
|
|
8525
9025
|
},
|
|
@@ -8624,7 +9124,7 @@ function createTelegramSocialSDK(bridge, log13) {
|
|
|
8624
9124
|
};
|
|
8625
9125
|
} catch (err) {
|
|
8626
9126
|
if (err instanceof PluginSDKError) throw err;
|
|
8627
|
-
|
|
9127
|
+
log15.error("telegram.getCollectibleInfo() failed:", err);
|
|
8628
9128
|
return null;
|
|
8629
9129
|
}
|
|
8630
9130
|
},
|
|
@@ -8662,7 +9162,7 @@ function createTelegramSocialSDK(bridge, log13) {
|
|
|
8662
9162
|
} catch (err) {
|
|
8663
9163
|
if (err.errorMessage === "STARGIFT_SLUG_INVALID") return null;
|
|
8664
9164
|
if (err instanceof PluginSDKError) throw err;
|
|
8665
|
-
|
|
9165
|
+
log15.error("telegram.getUniqueGift() failed:", err);
|
|
8666
9166
|
return null;
|
|
8667
9167
|
}
|
|
8668
9168
|
},
|
|
@@ -8688,7 +9188,7 @@ function createTelegramSocialSDK(bridge, log13) {
|
|
|
8688
9188
|
} catch (err) {
|
|
8689
9189
|
if (err.errorMessage === "STARGIFT_SLUG_INVALID") return null;
|
|
8690
9190
|
if (err instanceof PluginSDKError) throw err;
|
|
8691
|
-
|
|
9191
|
+
log15.error("telegram.getUniqueGiftValue() failed:", err);
|
|
8692
9192
|
return null;
|
|
8693
9193
|
}
|
|
8694
9194
|
},
|
|
@@ -8723,7 +9223,7 @@ function createTelegramSocialSDK(bridge, log13) {
|
|
|
8723
9223
|
const client = getClient2();
|
|
8724
9224
|
const { Api: Api4, helpers } = await import("telegram");
|
|
8725
9225
|
const { CustomFile } = await import("telegram/client/uploads.js");
|
|
8726
|
-
const { readFileSync:
|
|
9226
|
+
const { readFileSync: readFileSync8, statSync: statSync2 } = await import("fs");
|
|
8727
9227
|
const { basename: basename2 } = await import("path");
|
|
8728
9228
|
const { resolve: resolve2, normalize: normalize2 } = await import("path");
|
|
8729
9229
|
const { homedir: homedir3 } = await import("os");
|
|
@@ -8748,7 +9248,7 @@ function createTelegramSocialSDK(bridge, log13) {
|
|
|
8748
9248
|
}
|
|
8749
9249
|
const fileName = basename2(filePath);
|
|
8750
9250
|
const fileSize = statSync2(filePath).size;
|
|
8751
|
-
const fileBuffer =
|
|
9251
|
+
const fileBuffer = readFileSync8(filePath);
|
|
8752
9252
|
const isVideo = filePath.toLowerCase().match(/\.(mp4|mov|avi|webm|mkv|m4v)$/);
|
|
8753
9253
|
const customFile = new CustomFile(fileName, fileSize, filePath, fileBuffer);
|
|
8754
9254
|
const uploadedFile = await client.uploadFile({
|
|
@@ -8799,7 +9299,7 @@ function createTelegramSocialSDK(bridge, log13) {
|
|
|
8799
9299
|
}
|
|
8800
9300
|
|
|
8801
9301
|
// src/sdk/telegram.ts
|
|
8802
|
-
function createTelegramSDK(bridge,
|
|
9302
|
+
function createTelegramSDK(bridge, log15) {
|
|
8803
9303
|
function requireBridge2() {
|
|
8804
9304
|
requireBridge(bridge);
|
|
8805
9305
|
}
|
|
@@ -8903,7 +9403,7 @@ function createTelegramSDK(bridge, log13) {
|
|
|
8903
9403
|
timestamp: m.timestamp
|
|
8904
9404
|
}));
|
|
8905
9405
|
} catch (err) {
|
|
8906
|
-
|
|
9406
|
+
log15.error("telegram.getMessages() failed:", err);
|
|
8907
9407
|
return [];
|
|
8908
9408
|
}
|
|
8909
9409
|
},
|
|
@@ -8925,7 +9425,7 @@ function createTelegramSDK(bridge, log13) {
|
|
|
8925
9425
|
return bridge.isAvailable();
|
|
8926
9426
|
},
|
|
8927
9427
|
getRawClient() {
|
|
8928
|
-
|
|
9428
|
+
log15.warn("getRawClient() called \u2014 this bypasses SDK sandbox guarantees");
|
|
8929
9429
|
if (!bridge.isAvailable()) return null;
|
|
8930
9430
|
try {
|
|
8931
9431
|
return bridge.getClient().getClient();
|
|
@@ -8934,8 +9434,8 @@ function createTelegramSDK(bridge, log13) {
|
|
|
8934
9434
|
}
|
|
8935
9435
|
},
|
|
8936
9436
|
// Spread extended methods from sub-modules
|
|
8937
|
-
...createTelegramMessagesSDK(bridge,
|
|
8938
|
-
...createTelegramSocialSDK(bridge,
|
|
9437
|
+
...createTelegramMessagesSDK(bridge, log15),
|
|
9438
|
+
...createTelegramSocialSDK(bridge, log15)
|
|
8939
9439
|
};
|
|
8940
9440
|
}
|
|
8941
9441
|
|
|
@@ -8976,23 +9476,23 @@ function deletePluginSecret(pluginName, key) {
|
|
|
8976
9476
|
function listPluginSecretKeys(pluginName) {
|
|
8977
9477
|
return Object.keys(readSecretsFile(pluginName));
|
|
8978
9478
|
}
|
|
8979
|
-
function createSecretsSDK(pluginName, pluginConfig,
|
|
9479
|
+
function createSecretsSDK(pluginName, pluginConfig, log15) {
|
|
8980
9480
|
const envPrefix = pluginName.replace(/-/g, "_").toUpperCase();
|
|
8981
9481
|
function get(key) {
|
|
8982
9482
|
const envKey = `${envPrefix}_${key.toUpperCase()}`;
|
|
8983
9483
|
const envValue = process.env[envKey];
|
|
8984
9484
|
if (envValue) {
|
|
8985
|
-
|
|
9485
|
+
log15.debug(`Secret "${key}" resolved from env var ${envKey}`);
|
|
8986
9486
|
return envValue;
|
|
8987
9487
|
}
|
|
8988
9488
|
const stored = readSecretsFile(pluginName);
|
|
8989
9489
|
if (key in stored && stored[key]) {
|
|
8990
|
-
|
|
9490
|
+
log15.debug(`Secret "${key}" resolved from secrets store`);
|
|
8991
9491
|
return stored[key];
|
|
8992
9492
|
}
|
|
8993
9493
|
const configValue = pluginConfig[key];
|
|
8994
9494
|
if (configValue !== void 0 && configValue !== null) {
|
|
8995
|
-
|
|
9495
|
+
log15.debug(`Secret "${key}" resolved from pluginConfig`);
|
|
8996
9496
|
return String(configValue);
|
|
8997
9497
|
}
|
|
8998
9498
|
return void 0;
|
|
@@ -9094,7 +9594,7 @@ function createStorageSDK(db) {
|
|
|
9094
9594
|
}
|
|
9095
9595
|
|
|
9096
9596
|
// src/sdk/bot.ts
|
|
9097
|
-
function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLimiter,
|
|
9597
|
+
function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLimiter, log15) {
|
|
9098
9598
|
if (!router || !manifest || !manifest.inline && !manifest.callbacks) {
|
|
9099
9599
|
return null;
|
|
9100
9600
|
}
|
|
@@ -9102,7 +9602,7 @@ function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLi
|
|
|
9102
9602
|
const callbackLimit = manifest.rateLimits?.callbackPerMinute ?? 60;
|
|
9103
9603
|
const handlers = {};
|
|
9104
9604
|
function syncToRouter() {
|
|
9105
|
-
router
|
|
9605
|
+
router?.registerPlugin(pluginName, { ...handlers });
|
|
9106
9606
|
}
|
|
9107
9607
|
const sdk = {
|
|
9108
9608
|
get isAvailable() {
|
|
@@ -9117,7 +9617,7 @@ function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLi
|
|
|
9117
9617
|
},
|
|
9118
9618
|
onInlineQuery(handler) {
|
|
9119
9619
|
if (handlers.onInlineQuery) {
|
|
9120
|
-
|
|
9620
|
+
log15.warn("onInlineQuery called again \u2014 overwriting previous handler");
|
|
9121
9621
|
}
|
|
9122
9622
|
handlers.onInlineQuery = async (ctx) => {
|
|
9123
9623
|
if (rateLimiter) {
|
|
@@ -9163,7 +9663,7 @@ function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLi
|
|
|
9163
9663
|
return;
|
|
9164
9664
|
} catch (error) {
|
|
9165
9665
|
if (error?.errorMessage === "MESSAGE_NOT_MODIFIED") return;
|
|
9166
|
-
|
|
9666
|
+
log15.warn(`GramJS edit failed, falling back to Grammy: ${error?.errorMessage || error}`);
|
|
9167
9667
|
}
|
|
9168
9668
|
}
|
|
9169
9669
|
if (grammyBot) {
|
|
@@ -9176,7 +9676,7 @@ function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLi
|
|
|
9176
9676
|
});
|
|
9177
9677
|
} catch (error) {
|
|
9178
9678
|
if (error?.description?.includes("message is not modified")) return;
|
|
9179
|
-
|
|
9679
|
+
log15.error(`Failed to edit inline message: ${error?.description || error}`);
|
|
9180
9680
|
}
|
|
9181
9681
|
}
|
|
9182
9682
|
},
|
|
@@ -9235,13 +9735,13 @@ function createSafeDb(db) {
|
|
|
9235
9735
|
});
|
|
9236
9736
|
}
|
|
9237
9737
|
function createPluginSDK(deps, opts) {
|
|
9238
|
-
const
|
|
9738
|
+
const log15 = createLogger2(opts.pluginName);
|
|
9239
9739
|
const safeDb = opts.db ? createSafeDb(opts.db) : null;
|
|
9240
|
-
const ton = Object.freeze(createTonSDK(
|
|
9241
|
-
const telegram = Object.freeze(createTelegramSDK(deps.bridge,
|
|
9242
|
-
const secrets = Object.freeze(createSecretsSDK(opts.pluginName, opts.pluginConfig,
|
|
9740
|
+
const ton = Object.freeze(createTonSDK(log15, safeDb));
|
|
9741
|
+
const telegram = Object.freeze(createTelegramSDK(deps.bridge, log15));
|
|
9742
|
+
const secrets = Object.freeze(createSecretsSDK(opts.pluginName, opts.pluginConfig, log15));
|
|
9243
9743
|
const storage = safeDb ? Object.freeze(createStorageSDK(safeDb)) : null;
|
|
9244
|
-
const frozenLog = Object.freeze(
|
|
9744
|
+
const frozenLog = Object.freeze(log15);
|
|
9245
9745
|
const frozenConfig = Object.freeze(JSON.parse(JSON.stringify(opts.sanitizedConfig ?? {})));
|
|
9246
9746
|
const frozenPluginConfig = Object.freeze(JSON.parse(JSON.stringify(opts.pluginConfig ?? {})));
|
|
9247
9747
|
let cachedBot;
|
|
@@ -9269,6 +9769,36 @@ function createPluginSDK(deps, opts) {
|
|
|
9269
9769
|
);
|
|
9270
9770
|
if (result) cachedBot = result;
|
|
9271
9771
|
return result;
|
|
9772
|
+
},
|
|
9773
|
+
on(hookName, handler, onOpts) {
|
|
9774
|
+
if (!opts.hookRegistry) {
|
|
9775
|
+
log15.warn(`Hook registration unavailable \u2014 sdk.on() ignored`);
|
|
9776
|
+
return;
|
|
9777
|
+
}
|
|
9778
|
+
if (opts.declaredHooks) {
|
|
9779
|
+
const declared = opts.declaredHooks.some((h2) => h2.name === hookName);
|
|
9780
|
+
if (!declared) {
|
|
9781
|
+
log15.warn(`Hook "${hookName}" not declared in manifest \u2014 registration rejected`);
|
|
9782
|
+
return;
|
|
9783
|
+
}
|
|
9784
|
+
}
|
|
9785
|
+
const rawPriority = Number(onOpts?.priority) || 0;
|
|
9786
|
+
const clampedPriority = Math.max(-1e3, Math.min(1e3, rawPriority));
|
|
9787
|
+
if (rawPriority !== clampedPriority) {
|
|
9788
|
+
log15.debug(`Hook "${hookName}" priority ${rawPriority} clamped to ${clampedPriority}`);
|
|
9789
|
+
}
|
|
9790
|
+
const registered = opts.hookRegistry.register({
|
|
9791
|
+
pluginId: opts.pluginName,
|
|
9792
|
+
hookName,
|
|
9793
|
+
handler,
|
|
9794
|
+
priority: clampedPriority,
|
|
9795
|
+
globalPriority: opts.globalPriority ?? 0
|
|
9796
|
+
});
|
|
9797
|
+
if (!registered) {
|
|
9798
|
+
log15.warn(
|
|
9799
|
+
`Hook registration limit reached for plugin "${opts.pluginName}" \u2014 "${hookName}" rejected`
|
|
9800
|
+
);
|
|
9801
|
+
}
|
|
9272
9802
|
}
|
|
9273
9803
|
};
|
|
9274
9804
|
return Object.freeze(sdk);
|
|
@@ -9329,11 +9859,64 @@ function semverSatisfies(current, range) {
|
|
|
9329
9859
|
return cur.major === req.major && cur.minor === req.minor && cur.patch === req.patch;
|
|
9330
9860
|
}
|
|
9331
9861
|
|
|
9862
|
+
// src/sdk/hooks/registry.ts
|
|
9863
|
+
var MAX_HOOKS_PER_PLUGIN = 100;
|
|
9864
|
+
var HookRegistry = class {
|
|
9865
|
+
hooks = [];
|
|
9866
|
+
hookMap = /* @__PURE__ */ new Map();
|
|
9867
|
+
rebuildMap() {
|
|
9868
|
+
this.hookMap.clear();
|
|
9869
|
+
for (const h2 of this.hooks) {
|
|
9870
|
+
let arr = this.hookMap.get(h2.hookName);
|
|
9871
|
+
if (!arr) {
|
|
9872
|
+
arr = [];
|
|
9873
|
+
this.hookMap.set(h2.hookName, arr);
|
|
9874
|
+
}
|
|
9875
|
+
arr.push(h2);
|
|
9876
|
+
}
|
|
9877
|
+
for (const arr of this.hookMap.values()) {
|
|
9878
|
+
arr.sort((a, b) => {
|
|
9879
|
+
const aPrio = a.globalPriority + a.priority;
|
|
9880
|
+
const bPrio = b.globalPriority + b.priority;
|
|
9881
|
+
return aPrio - bPrio;
|
|
9882
|
+
});
|
|
9883
|
+
}
|
|
9884
|
+
}
|
|
9885
|
+
register(reg) {
|
|
9886
|
+
const pluginHookCount = this.hooks.filter((h2) => h2.pluginId === reg.pluginId).length;
|
|
9887
|
+
if (pluginHookCount >= MAX_HOOKS_PER_PLUGIN) {
|
|
9888
|
+
return false;
|
|
9889
|
+
}
|
|
9890
|
+
this.hooks.push({ ...reg, globalPriority: reg.globalPriority ?? 0 });
|
|
9891
|
+
this.rebuildMap();
|
|
9892
|
+
return true;
|
|
9893
|
+
}
|
|
9894
|
+
getHooks(name) {
|
|
9895
|
+
return this.hookMap.get(name) ?? [];
|
|
9896
|
+
}
|
|
9897
|
+
hasHooks(name) {
|
|
9898
|
+
return (this.hookMap.get(name)?.length ?? 0) > 0;
|
|
9899
|
+
}
|
|
9900
|
+
hasAnyHooks() {
|
|
9901
|
+
return this.hooks.length > 0;
|
|
9902
|
+
}
|
|
9903
|
+
unregister(pluginId) {
|
|
9904
|
+
const before = this.hooks.length;
|
|
9905
|
+
this.hooks = this.hooks.filter((h2) => h2.pluginId !== pluginId);
|
|
9906
|
+
this.rebuildMap();
|
|
9907
|
+
return before - this.hooks.length;
|
|
9908
|
+
}
|
|
9909
|
+
clear() {
|
|
9910
|
+
this.hooks = [];
|
|
9911
|
+
this.hookMap.clear();
|
|
9912
|
+
}
|
|
9913
|
+
};
|
|
9914
|
+
|
|
9332
9915
|
// src/agent/tools/plugin-loader.ts
|
|
9333
9916
|
var execFileAsync = promisify(execFile);
|
|
9334
9917
|
var log12 = createLogger("PluginLoader");
|
|
9335
9918
|
var PLUGIN_DATA_DIR = join5(TELETON_ROOT, "plugins", "data");
|
|
9336
|
-
function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps) {
|
|
9919
|
+
function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps, hookRegistry, pluginPriorities) {
|
|
9337
9920
|
let manifest = null;
|
|
9338
9921
|
if (raw.manifest) {
|
|
9339
9922
|
try {
|
|
@@ -9363,6 +9946,7 @@ function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps) {
|
|
|
9363
9946
|
}
|
|
9364
9947
|
const pluginName = manifest?.name ?? entryName.replace(/\.js$/, "");
|
|
9365
9948
|
const pluginVersion = manifest?.version ?? "0.0.0";
|
|
9949
|
+
const globalPriority = pluginPriorities?.get(pluginName) ?? 0;
|
|
9366
9950
|
if (manifest?.dependencies) {
|
|
9367
9951
|
for (const dep of manifest.dependencies) {
|
|
9368
9952
|
if (!loadedModuleNames.includes(dep)) {
|
|
@@ -9425,7 +10009,7 @@ function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps) {
|
|
|
9425
10009
|
const dbPath = join5(PLUGIN_DATA_DIR, `${pluginName}.db`);
|
|
9426
10010
|
pluginDb = openModuleDb(dbPath);
|
|
9427
10011
|
if (hasMigrate) {
|
|
9428
|
-
raw.migrate(pluginDb);
|
|
10012
|
+
raw.migrate?.(pluginDb);
|
|
9429
10013
|
const pluginTables = pluginDb.prepare(
|
|
9430
10014
|
`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'`
|
|
9431
10015
|
).all().map((t) => t.name).filter((n2) => n2 !== "_kv");
|
|
@@ -9453,7 +10037,10 @@ function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps) {
|
|
|
9453
10037
|
db: pluginDb,
|
|
9454
10038
|
sanitizedConfig,
|
|
9455
10039
|
pluginConfig,
|
|
9456
|
-
botManifest: manifest?.bot
|
|
10040
|
+
botManifest: manifest?.bot,
|
|
10041
|
+
hookRegistry,
|
|
10042
|
+
declaredHooks: manifest?.hooks,
|
|
10043
|
+
globalPriority
|
|
9457
10044
|
});
|
|
9458
10045
|
toolDefs = raw.tools(sdk);
|
|
9459
10046
|
} else if (Array.isArray(raw.tools)) {
|
|
@@ -9550,12 +10137,20 @@ async function ensurePluginDeps(pluginDir, pluginEntry) {
|
|
|
9550
10137
|
log12.error(`[${pluginEntry}] Failed to install deps: ${String(err).slice(0, 300)}`);
|
|
9551
10138
|
}
|
|
9552
10139
|
}
|
|
9553
|
-
async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps) {
|
|
10140
|
+
async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
|
|
10141
|
+
const hookRegistry = new HookRegistry();
|
|
9554
10142
|
const pluginsDir = WORKSPACE_PATHS.PLUGINS_DIR;
|
|
9555
10143
|
if (!existsSync6(pluginsDir)) {
|
|
9556
|
-
return [];
|
|
10144
|
+
return { modules: [], hookRegistry };
|
|
9557
10145
|
}
|
|
9558
|
-
|
|
10146
|
+
let pluginPriorities = /* @__PURE__ */ new Map();
|
|
10147
|
+
if (db) {
|
|
10148
|
+
try {
|
|
10149
|
+
pluginPriorities = getPluginPriorities(db);
|
|
10150
|
+
} catch {
|
|
10151
|
+
}
|
|
10152
|
+
}
|
|
10153
|
+
const entries = readdirSync2(pluginsDir).sort();
|
|
9559
10154
|
const modules = [];
|
|
9560
10155
|
const loadedNames = /* @__PURE__ */ new Set();
|
|
9561
10156
|
const pluginPaths = [];
|
|
@@ -9603,7 +10198,15 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps) {
|
|
|
9603
10198
|
log12.warn(`Plugin "${entry}": no 'tools' array or function exported, skipping`);
|
|
9604
10199
|
continue;
|
|
9605
10200
|
}
|
|
9606
|
-
const adapted = adaptPlugin(
|
|
10201
|
+
const adapted = adaptPlugin(
|
|
10202
|
+
mod,
|
|
10203
|
+
entry,
|
|
10204
|
+
config,
|
|
10205
|
+
loadedModuleNames,
|
|
10206
|
+
sdkDeps,
|
|
10207
|
+
hookRegistry,
|
|
10208
|
+
pluginPriorities
|
|
10209
|
+
);
|
|
9607
10210
|
if (loadedNames.has(adapted.name)) {
|
|
9608
10211
|
log12.warn(`Plugin "${adapted.name}" already loaded, skipping duplicate from "${entry}"`);
|
|
9609
10212
|
continue;
|
|
@@ -9614,7 +10217,7 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps) {
|
|
|
9614
10217
|
log12.error(`Plugin "${entry}" failed to adapt: ${err instanceof Error ? err.message : err}`);
|
|
9615
10218
|
}
|
|
9616
10219
|
}
|
|
9617
|
-
return modules;
|
|
10220
|
+
return { modules, hookRegistry };
|
|
9618
10221
|
}
|
|
9619
10222
|
|
|
9620
10223
|
// src/config/configurable-keys.ts
|
|
@@ -10122,6 +10725,40 @@ var CONFIGURABLE_KEYS = {
|
|
|
10122
10725
|
mask: identity,
|
|
10123
10726
|
parse: (v) => Number(v)
|
|
10124
10727
|
},
|
|
10728
|
+
// ─── TON Proxy ────────────────────────────────────────────────────
|
|
10729
|
+
"ton_proxy.enabled": {
|
|
10730
|
+
type: "boolean",
|
|
10731
|
+
category: "TON Proxy",
|
|
10732
|
+
label: "TON Proxy Enabled",
|
|
10733
|
+
description: "Enable Tonutils-Proxy for .ton site access (auto-downloads binary on first run)",
|
|
10734
|
+
sensitive: false,
|
|
10735
|
+
hotReload: "instant",
|
|
10736
|
+
validate: enumValidator(["true", "false"]),
|
|
10737
|
+
mask: identity,
|
|
10738
|
+
parse: (v) => v === "true"
|
|
10739
|
+
},
|
|
10740
|
+
"ton_proxy.port": {
|
|
10741
|
+
type: "number",
|
|
10742
|
+
category: "TON Proxy",
|
|
10743
|
+
label: "Proxy Port",
|
|
10744
|
+
description: "HTTP proxy port for .ton sites (default: 8080)",
|
|
10745
|
+
sensitive: false,
|
|
10746
|
+
hotReload: "restart",
|
|
10747
|
+
validate: numberInRange(1, 65535),
|
|
10748
|
+
mask: identity,
|
|
10749
|
+
parse: (v) => Number(v)
|
|
10750
|
+
},
|
|
10751
|
+
"ton_proxy.binary_path": {
|
|
10752
|
+
type: "string",
|
|
10753
|
+
category: "TON Proxy",
|
|
10754
|
+
label: "Binary Path",
|
|
10755
|
+
description: "Custom path to tonutils-proxy-cli (leave empty for auto-download)",
|
|
10756
|
+
sensitive: false,
|
|
10757
|
+
hotReload: "restart",
|
|
10758
|
+
validate: noValidation,
|
|
10759
|
+
mask: identity,
|
|
10760
|
+
parse: identity
|
|
10761
|
+
},
|
|
10125
10762
|
// ─── Capabilities ──────────────────────────────────────────────────
|
|
10126
10763
|
"capabilities.exec.mode": {
|
|
10127
10764
|
type: "enum",
|
|
@@ -10227,6 +10864,392 @@ function writeRawConfig(raw, configPath) {
|
|
|
10227
10864
|
writeFileSync3(fullPath, stringify2(raw), { encoding: "utf-8", mode: 384 });
|
|
10228
10865
|
}
|
|
10229
10866
|
|
|
10867
|
+
// src/ton-proxy/manager.ts
|
|
10868
|
+
import { spawn, execSync } from "child_process";
|
|
10869
|
+
import {
|
|
10870
|
+
existsSync as existsSync8,
|
|
10871
|
+
chmodSync,
|
|
10872
|
+
createWriteStream,
|
|
10873
|
+
readFileSync as readFileSync7,
|
|
10874
|
+
writeFileSync as writeFileSync4,
|
|
10875
|
+
unlinkSync
|
|
10876
|
+
} from "fs";
|
|
10877
|
+
import { mkdir as mkdir2 } from "fs/promises";
|
|
10878
|
+
import { join as join6 } from "path";
|
|
10879
|
+
import { pipeline } from "stream/promises";
|
|
10880
|
+
var log13 = createLogger("TonProxy");
|
|
10881
|
+
var GITHUB_REPO = "xssnick/Tonutils-Proxy";
|
|
10882
|
+
var BINARY_DIR = join6(TELETON_ROOT, "bin");
|
|
10883
|
+
var PID_FILE = join6(TELETON_ROOT, "ton-proxy.pid");
|
|
10884
|
+
var HEALTH_CHECK_INTERVAL_MS = 3e4;
|
|
10885
|
+
var HEALTH_CHECK_TIMEOUT_MS = 5e3;
|
|
10886
|
+
var KILL_GRACE_MS = 5e3;
|
|
10887
|
+
var TonProxyManager = class {
|
|
10888
|
+
process = null;
|
|
10889
|
+
healthInterval = null;
|
|
10890
|
+
config;
|
|
10891
|
+
restartCount = 0;
|
|
10892
|
+
maxRestarts = 3;
|
|
10893
|
+
constructor(config) {
|
|
10894
|
+
this.config = config;
|
|
10895
|
+
}
|
|
10896
|
+
/** Resolve the binary path — user-specified or auto-detected */
|
|
10897
|
+
getBinaryPath() {
|
|
10898
|
+
if (this.config.binary_path) return this.config.binary_path;
|
|
10899
|
+
return join6(BINARY_DIR, getBinaryName());
|
|
10900
|
+
}
|
|
10901
|
+
/** Check if the binary exists on disk */
|
|
10902
|
+
isInstalled() {
|
|
10903
|
+
return existsSync8(this.getBinaryPath());
|
|
10904
|
+
}
|
|
10905
|
+
/** Whether the proxy process is currently running */
|
|
10906
|
+
isRunning() {
|
|
10907
|
+
return this.process !== null && this.process.exitCode === null;
|
|
10908
|
+
}
|
|
10909
|
+
/**
|
|
10910
|
+
* Download the latest CLI binary from GitHub releases.
|
|
10911
|
+
* Fetches the latest release tag, then downloads the platform-appropriate binary.
|
|
10912
|
+
*/
|
|
10913
|
+
async install() {
|
|
10914
|
+
const binaryName = getBinaryName();
|
|
10915
|
+
log13.info(`Downloading TON Proxy binary (${binaryName})...`);
|
|
10916
|
+
await mkdir2(BINARY_DIR, { recursive: true });
|
|
10917
|
+
const releaseUrl = `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`;
|
|
10918
|
+
const releaseRes = await fetch(releaseUrl, {
|
|
10919
|
+
headers: { Accept: "application/vnd.github.v3+json" }
|
|
10920
|
+
});
|
|
10921
|
+
if (!releaseRes.ok) {
|
|
10922
|
+
throw new Error(`Failed to fetch latest release: ${releaseRes.status}`);
|
|
10923
|
+
}
|
|
10924
|
+
const release = await releaseRes.json();
|
|
10925
|
+
const tag = release.tag_name;
|
|
10926
|
+
const downloadUrl = `https://github.com/${GITHUB_REPO}/releases/download/${tag}/${binaryName}`;
|
|
10927
|
+
log13.info(`Downloading ${downloadUrl}`);
|
|
10928
|
+
const res = await fetch(downloadUrl);
|
|
10929
|
+
if (!res.ok || !res.body) {
|
|
10930
|
+
throw new Error(`Download failed: ${res.status} ${res.statusText}`);
|
|
10931
|
+
}
|
|
10932
|
+
const dest = this.getBinaryPath();
|
|
10933
|
+
const fileStream = createWriteStream(dest);
|
|
10934
|
+
await pipeline(res.body, fileStream);
|
|
10935
|
+
chmodSync(dest, 493);
|
|
10936
|
+
log13.info(`TON Proxy installed: ${dest} (${tag})`);
|
|
10937
|
+
}
|
|
10938
|
+
/** Kill any orphan proxy process from a previous session */
|
|
10939
|
+
killOrphan() {
|
|
10940
|
+
if (existsSync8(PID_FILE)) {
|
|
10941
|
+
try {
|
|
10942
|
+
const pid = parseInt(readFileSync7(PID_FILE, "utf-8").trim(), 10);
|
|
10943
|
+
if (pid && !isNaN(pid)) {
|
|
10944
|
+
try {
|
|
10945
|
+
process.kill(pid, 0);
|
|
10946
|
+
log13.warn(`Killing orphan TON Proxy (PID ${pid}) from previous session`);
|
|
10947
|
+
process.kill(pid, "SIGTERM");
|
|
10948
|
+
} catch {
|
|
10949
|
+
}
|
|
10950
|
+
}
|
|
10951
|
+
unlinkSync(PID_FILE);
|
|
10952
|
+
} catch {
|
|
10953
|
+
}
|
|
10954
|
+
}
|
|
10955
|
+
try {
|
|
10956
|
+
const out = execSync(`ss -tlnp 2>/dev/null | grep ':${this.config.port} ' || true`, {
|
|
10957
|
+
encoding: "utf-8",
|
|
10958
|
+
timeout: 3e3
|
|
10959
|
+
});
|
|
10960
|
+
const pidMatch = out.match(/pid=(\d+)/);
|
|
10961
|
+
if (pidMatch) {
|
|
10962
|
+
const pid = parseInt(pidMatch[1], 10);
|
|
10963
|
+
log13.warn(`Port ${this.config.port} occupied by PID ${pid}, killing it`);
|
|
10964
|
+
try {
|
|
10965
|
+
process.kill(pid, "SIGTERM");
|
|
10966
|
+
} catch {
|
|
10967
|
+
}
|
|
10968
|
+
execSync("sleep 0.5");
|
|
10969
|
+
}
|
|
10970
|
+
} catch {
|
|
10971
|
+
}
|
|
10972
|
+
}
|
|
10973
|
+
/** Write PID to file for orphan detection */
|
|
10974
|
+
writePidFile(pid) {
|
|
10975
|
+
try {
|
|
10976
|
+
writeFileSync4(PID_FILE, String(pid), { mode: 384 });
|
|
10977
|
+
} catch {
|
|
10978
|
+
log13.warn("Failed to write TON Proxy PID file");
|
|
10979
|
+
}
|
|
10980
|
+
}
|
|
10981
|
+
/** Remove PID file */
|
|
10982
|
+
removePidFile() {
|
|
10983
|
+
try {
|
|
10984
|
+
if (existsSync8(PID_FILE)) unlinkSync(PID_FILE);
|
|
10985
|
+
} catch {
|
|
10986
|
+
}
|
|
10987
|
+
}
|
|
10988
|
+
/** Start the proxy process */
|
|
10989
|
+
async start() {
|
|
10990
|
+
if (this.isRunning()) {
|
|
10991
|
+
log13.warn("TON Proxy is already running");
|
|
10992
|
+
return;
|
|
10993
|
+
}
|
|
10994
|
+
this.restartCount = 0;
|
|
10995
|
+
this.maxRestarts = 3;
|
|
10996
|
+
this.killOrphan();
|
|
10997
|
+
if (!this.isInstalled()) {
|
|
10998
|
+
await this.install();
|
|
10999
|
+
}
|
|
11000
|
+
const binaryPath = this.getBinaryPath();
|
|
11001
|
+
const port = String(this.config.port);
|
|
11002
|
+
log13.info(`Starting TON Proxy on 127.0.0.1:${port}`);
|
|
11003
|
+
this.process = spawn(binaryPath, ["-addr", `127.0.0.1:${port}`], {
|
|
11004
|
+
cwd: BINARY_DIR,
|
|
11005
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
11006
|
+
detached: false
|
|
11007
|
+
});
|
|
11008
|
+
this.process.stdout?.on("data", (chunk) => {
|
|
11009
|
+
const line = chunk.toString().trim();
|
|
11010
|
+
if (line) log13.debug(`[proxy] ${line}`);
|
|
11011
|
+
});
|
|
11012
|
+
this.process.stderr?.on("data", (chunk) => {
|
|
11013
|
+
const line = chunk.toString().trim();
|
|
11014
|
+
if (line) log13.warn(`[proxy:err] ${line}`);
|
|
11015
|
+
});
|
|
11016
|
+
this.process.on("exit", (code, signal) => {
|
|
11017
|
+
log13.info(`TON Proxy exited (code=${code}, signal=${signal})`);
|
|
11018
|
+
this.process = null;
|
|
11019
|
+
this.removePidFile();
|
|
11020
|
+
if (code !== 0 && code !== null && this.restartCount < this.maxRestarts) {
|
|
11021
|
+
this.restartCount++;
|
|
11022
|
+
log13.warn(`Auto-restarting TON Proxy (attempt ${this.restartCount}/${this.maxRestarts})`);
|
|
11023
|
+
this.start().catch((err) => log13.error({ err }, "Failed to auto-restart TON Proxy"));
|
|
11024
|
+
}
|
|
11025
|
+
});
|
|
11026
|
+
this.process.on("error", (err) => {
|
|
11027
|
+
log13.error({ err }, "TON Proxy process error");
|
|
11028
|
+
this.process = null;
|
|
11029
|
+
});
|
|
11030
|
+
this.startHealthCheck();
|
|
11031
|
+
await new Promise((resolve2, reject) => {
|
|
11032
|
+
const timer = setTimeout(() => {
|
|
11033
|
+
if (this.isRunning()) {
|
|
11034
|
+
resolve2();
|
|
11035
|
+
} else {
|
|
11036
|
+
reject(new Error("TON Proxy process exited immediately"));
|
|
11037
|
+
}
|
|
11038
|
+
}, 1e3);
|
|
11039
|
+
this.process?.on("exit", () => {
|
|
11040
|
+
clearTimeout(timer);
|
|
11041
|
+
reject(new Error("TON Proxy process exited during startup"));
|
|
11042
|
+
});
|
|
11043
|
+
});
|
|
11044
|
+
if (this.process?.pid) this.writePidFile(this.process.pid);
|
|
11045
|
+
log13.info(`TON Proxy running on 127.0.0.1:${port} (PID ${this.process?.pid})`);
|
|
11046
|
+
}
|
|
11047
|
+
/** Stop the proxy process gracefully */
|
|
11048
|
+
async stop() {
|
|
11049
|
+
this.stopHealthCheck();
|
|
11050
|
+
if (!this.process) return;
|
|
11051
|
+
this.maxRestarts = 0;
|
|
11052
|
+
log13.info("Stopping TON Proxy...");
|
|
11053
|
+
return new Promise((resolve2) => {
|
|
11054
|
+
if (!this.process) {
|
|
11055
|
+
resolve2();
|
|
11056
|
+
return;
|
|
11057
|
+
}
|
|
11058
|
+
const forceKill = setTimeout(() => {
|
|
11059
|
+
if (this.process) {
|
|
11060
|
+
log13.warn("TON Proxy did not exit gracefully, sending SIGKILL");
|
|
11061
|
+
this.process.kill("SIGKILL");
|
|
11062
|
+
}
|
|
11063
|
+
}, KILL_GRACE_MS);
|
|
11064
|
+
this.process.on("exit", () => {
|
|
11065
|
+
clearTimeout(forceKill);
|
|
11066
|
+
this.process = null;
|
|
11067
|
+
this.removePidFile();
|
|
11068
|
+
resolve2();
|
|
11069
|
+
});
|
|
11070
|
+
this.process.kill("SIGTERM");
|
|
11071
|
+
});
|
|
11072
|
+
}
|
|
11073
|
+
/** Remove the downloaded binary from disk */
|
|
11074
|
+
async uninstall() {
|
|
11075
|
+
if (this.isRunning()) {
|
|
11076
|
+
await this.stop();
|
|
11077
|
+
}
|
|
11078
|
+
const binaryPath = this.getBinaryPath();
|
|
11079
|
+
if (existsSync8(binaryPath)) {
|
|
11080
|
+
const { unlink } = await import("fs/promises");
|
|
11081
|
+
await unlink(binaryPath);
|
|
11082
|
+
log13.info(`TON Proxy binary removed: ${binaryPath}`);
|
|
11083
|
+
}
|
|
11084
|
+
}
|
|
11085
|
+
/** Get proxy status for WebUI / tools */
|
|
11086
|
+
getStatus() {
|
|
11087
|
+
return {
|
|
11088
|
+
running: this.isRunning(),
|
|
11089
|
+
port: this.config.port,
|
|
11090
|
+
installed: this.isInstalled(),
|
|
11091
|
+
pid: this.process?.pid
|
|
11092
|
+
};
|
|
11093
|
+
}
|
|
11094
|
+
startHealthCheck() {
|
|
11095
|
+
this.stopHealthCheck();
|
|
11096
|
+
this.healthInterval = setInterval(() => {
|
|
11097
|
+
void this.checkHealth();
|
|
11098
|
+
}, HEALTH_CHECK_INTERVAL_MS);
|
|
11099
|
+
}
|
|
11100
|
+
stopHealthCheck() {
|
|
11101
|
+
if (this.healthInterval) {
|
|
11102
|
+
clearInterval(this.healthInterval);
|
|
11103
|
+
this.healthInterval = null;
|
|
11104
|
+
}
|
|
11105
|
+
}
|
|
11106
|
+
async checkHealth() {
|
|
11107
|
+
if (!this.isRunning()) return;
|
|
11108
|
+
try {
|
|
11109
|
+
const controller = new AbortController();
|
|
11110
|
+
const timeout = setTimeout(() => controller.abort(), HEALTH_CHECK_TIMEOUT_MS);
|
|
11111
|
+
const res = await fetch(`http://127.0.0.1:${this.config.port}/`, {
|
|
11112
|
+
signal: controller.signal
|
|
11113
|
+
}).catch(() => null);
|
|
11114
|
+
clearTimeout(timeout);
|
|
11115
|
+
if (!res) {
|
|
11116
|
+
log13.warn("TON Proxy health check failed (no response)");
|
|
11117
|
+
}
|
|
11118
|
+
} catch {
|
|
11119
|
+
}
|
|
11120
|
+
}
|
|
11121
|
+
};
|
|
11122
|
+
function getBinaryName() {
|
|
11123
|
+
const platform = process.platform;
|
|
11124
|
+
const arch = process.arch;
|
|
11125
|
+
let os;
|
|
11126
|
+
switch (platform) {
|
|
11127
|
+
case "linux":
|
|
11128
|
+
os = "linux";
|
|
11129
|
+
break;
|
|
11130
|
+
case "darwin":
|
|
11131
|
+
os = "darwin";
|
|
11132
|
+
break;
|
|
11133
|
+
case "win32":
|
|
11134
|
+
os = "windows";
|
|
11135
|
+
break;
|
|
11136
|
+
default:
|
|
11137
|
+
throw new Error(`Unsupported platform: ${platform}`);
|
|
11138
|
+
}
|
|
11139
|
+
let cpuArch;
|
|
11140
|
+
switch (arch) {
|
|
11141
|
+
case "x64":
|
|
11142
|
+
cpuArch = "amd64";
|
|
11143
|
+
break;
|
|
11144
|
+
case "arm64":
|
|
11145
|
+
cpuArch = "arm64";
|
|
11146
|
+
break;
|
|
11147
|
+
default:
|
|
11148
|
+
throw new Error(`Unsupported architecture: ${arch}`);
|
|
11149
|
+
}
|
|
11150
|
+
const ext = platform === "win32" ? ".exe" : "";
|
|
11151
|
+
return `tonutils-proxy-cli-${os}-${cpuArch}${ext}`;
|
|
11152
|
+
}
|
|
11153
|
+
|
|
11154
|
+
// src/ton-proxy/tools.ts
|
|
11155
|
+
import { Type } from "@sinclair/typebox";
|
|
11156
|
+
var proxyManager = null;
|
|
11157
|
+
function setProxyManager(mgr) {
|
|
11158
|
+
proxyManager = mgr;
|
|
11159
|
+
}
|
|
11160
|
+
var tonProxyStatusTool = {
|
|
11161
|
+
name: "ton_proxy_status",
|
|
11162
|
+
description: "Check the status of the TON Proxy (Tonutils-Proxy). Returns whether the proxy is running, installed, the port, and PID.",
|
|
11163
|
+
parameters: Type.Object({})
|
|
11164
|
+
};
|
|
11165
|
+
var tonProxyStatusExecutor = async () => {
|
|
11166
|
+
if (!proxyManager) {
|
|
11167
|
+
return { success: true, data: { enabled: false, message: "TON Proxy is not configured" } };
|
|
11168
|
+
}
|
|
11169
|
+
return { success: true, data: proxyManager.getStatus() };
|
|
11170
|
+
};
|
|
11171
|
+
|
|
11172
|
+
// src/ton-proxy/module.ts
|
|
11173
|
+
var log14 = createLogger("TonProxyModule");
|
|
11174
|
+
var manager = null;
|
|
11175
|
+
function getTonProxyManager() {
|
|
11176
|
+
return manager;
|
|
11177
|
+
}
|
|
11178
|
+
function setTonProxyManager(mgr) {
|
|
11179
|
+
manager = mgr;
|
|
11180
|
+
setProxyManager(mgr);
|
|
11181
|
+
}
|
|
11182
|
+
var tonProxyModule = {
|
|
11183
|
+
name: "ton-proxy",
|
|
11184
|
+
version: "1.0.0",
|
|
11185
|
+
tools(config) {
|
|
11186
|
+
if (!config.ton_proxy?.enabled) return [];
|
|
11187
|
+
return [{ tool: tonProxyStatusTool, executor: tonProxyStatusExecutor }];
|
|
11188
|
+
},
|
|
11189
|
+
async start(context) {
|
|
11190
|
+
if (!context.config.ton_proxy?.enabled) return;
|
|
11191
|
+
const proxyConfig = context.config.ton_proxy;
|
|
11192
|
+
manager = new TonProxyManager({
|
|
11193
|
+
enabled: proxyConfig.enabled,
|
|
11194
|
+
port: proxyConfig.port,
|
|
11195
|
+
binary_path: proxyConfig.binary_path
|
|
11196
|
+
});
|
|
11197
|
+
setProxyManager(manager);
|
|
11198
|
+
try {
|
|
11199
|
+
await manager.start();
|
|
11200
|
+
log14.info(`TON Proxy started on port ${proxyConfig.port}`);
|
|
11201
|
+
} catch (err) {
|
|
11202
|
+
log14.error({ err }, "Failed to start TON Proxy");
|
|
11203
|
+
manager = null;
|
|
11204
|
+
}
|
|
11205
|
+
},
|
|
11206
|
+
async stop() {
|
|
11207
|
+
if (manager) {
|
|
11208
|
+
await manager.stop();
|
|
11209
|
+
manager = null;
|
|
11210
|
+
setProxyManager(null);
|
|
11211
|
+
}
|
|
11212
|
+
}
|
|
11213
|
+
};
|
|
11214
|
+
var module_default = tonProxyModule;
|
|
11215
|
+
|
|
11216
|
+
// src/agent/hooks/user-hook-store.ts
|
|
11217
|
+
function getUserHookConfig(db, key) {
|
|
11218
|
+
const row = db.prepare("SELECT value FROM user_hook_config WHERE key = ?").get(key);
|
|
11219
|
+
return row?.value ?? null;
|
|
11220
|
+
}
|
|
11221
|
+
function setUserHookConfig(db, key, value) {
|
|
11222
|
+
db.prepare(
|
|
11223
|
+
`INSERT INTO user_hook_config (key, value, updated_at)
|
|
11224
|
+
VALUES (?, ?, datetime('now'))
|
|
11225
|
+
ON CONFLICT(key) DO UPDATE SET
|
|
11226
|
+
value = excluded.value,
|
|
11227
|
+
updated_at = excluded.updated_at`
|
|
11228
|
+
).run(key, value);
|
|
11229
|
+
}
|
|
11230
|
+
function getBlocklistConfig(db) {
|
|
11231
|
+
const enabled = getUserHookConfig(db, "blocklist.enabled");
|
|
11232
|
+
const keywords = getUserHookConfig(db, "blocklist.keywords");
|
|
11233
|
+
const message = getUserHookConfig(db, "blocklist.message");
|
|
11234
|
+
return {
|
|
11235
|
+
enabled: enabled === "true",
|
|
11236
|
+
keywords: keywords ? JSON.parse(keywords) : [],
|
|
11237
|
+
message: message ?? ""
|
|
11238
|
+
};
|
|
11239
|
+
}
|
|
11240
|
+
function setBlocklistConfig(db, config) {
|
|
11241
|
+
setUserHookConfig(db, "blocklist.enabled", String(config.enabled));
|
|
11242
|
+
setUserHookConfig(db, "blocklist.keywords", JSON.stringify(config.keywords));
|
|
11243
|
+
setUserHookConfig(db, "blocklist.message", config.message);
|
|
11244
|
+
}
|
|
11245
|
+
function getTriggersConfig(db) {
|
|
11246
|
+
const raw = getUserHookConfig(db, "triggers");
|
|
11247
|
+
return raw ? JSON.parse(raw) : [];
|
|
11248
|
+
}
|
|
11249
|
+
function setTriggersConfig(db, triggers) {
|
|
11250
|
+
setUserHookConfig(db, "triggers", JSON.stringify(triggers));
|
|
11251
|
+
}
|
|
11252
|
+
|
|
10230
11253
|
export {
|
|
10231
11254
|
loadConfig,
|
|
10232
11255
|
configExists,
|
|
@@ -10268,6 +11291,9 @@ export {
|
|
|
10268
11291
|
toUnits,
|
|
10269
11292
|
fromUnits,
|
|
10270
11293
|
dexFactory,
|
|
11294
|
+
getPluginPriorities,
|
|
11295
|
+
setPluginPriority,
|
|
11296
|
+
resetPluginPriority,
|
|
10271
11297
|
InlineRouter,
|
|
10272
11298
|
adaptPlugin,
|
|
10273
11299
|
ensurePluginDeps,
|
|
@@ -10277,5 +11303,13 @@ export {
|
|
|
10277
11303
|
setNestedValue,
|
|
10278
11304
|
deleteNestedValue,
|
|
10279
11305
|
readRawConfig,
|
|
10280
|
-
writeRawConfig
|
|
11306
|
+
writeRawConfig,
|
|
11307
|
+
TonProxyManager,
|
|
11308
|
+
getTonProxyManager,
|
|
11309
|
+
setTonProxyManager,
|
|
11310
|
+
module_default,
|
|
11311
|
+
getBlocklistConfig,
|
|
11312
|
+
setBlocklistConfig,
|
|
11313
|
+
getTriggersConfig,
|
|
11314
|
+
setTriggersConfig
|
|
10281
11315
|
};
|