teleton 0.8.2 → 0.8.4
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 +230 -294
- package/dist/bootstrap-NNEI3Z5H.js +126 -0
- package/dist/{chunk-HEDJCLA6.js → chunk-35MX4ZUI.js} +2 -122
- package/dist/{chunk-57URFK6M.js → chunk-5LOHRZYY.js} +64 -15
- package/dist/{chunk-YOSUPUAJ.js → chunk-6OOHHJ4N.js} +1 -174
- package/dist/{chunk-W25Z7CM6.js → chunk-ALKAAG4O.js} +3 -3
- package/dist/chunk-CUE4UZXR.js +129 -0
- package/dist/{chunk-XBSCYMKM.js → chunk-G7PCW63M.js} +14 -14
- package/dist/{chunk-VYKW7FMV.js → chunk-GHMXWAXI.js} +1 -1
- package/dist/chunk-JROBTXWY.js +908 -0
- package/dist/{chunk-J73TA3UM.js → chunk-LVTKJQ7O.js} +7 -5
- package/dist/{chunk-GGXJLMOH.js → chunk-LZQOX6YY.js} +283 -1061
- package/dist/{chunk-7YKSXOQQ.js → chunk-NH2CNRKJ.js} +131 -241
- package/dist/chunk-NVKBBTI6.js +128 -0
- package/dist/{chunk-2IZU3REP.js → chunk-UMUONAD6.js} +143 -220
- package/dist/chunk-WTDAICGT.js +175 -0
- package/dist/{chunk-55SKE6YH.js → chunk-XDZDOKIF.js} +1 -1
- package/dist/cli/index.js +41 -24
- package/dist/{client-YOOHI776.js → client-5KD25NOP.js} +4 -3
- package/dist/index.js +14 -10
- package/dist/local-IHKJFQJS.js +9 -0
- package/dist/{memory-Q6EWGK2S.js → memory-QMJRM3XJ.js} +5 -3
- package/dist/{memory-hook-WUXJNVT5.js → memory-hook-VUNWZ3NY.js} +5 -4
- package/dist/{migrate-WFU6COBN.js → migrate-5VBAP52B.js} +3 -2
- package/dist/{server-YODFBZKG.js → server-AJCOURH7.js} +13 -10
- package/dist/{server-GYZXKIKU.js → server-WWGVDFPW.js} +38 -13
- package/dist/{setup-server-IZBUOJRU.js → setup-server-VDY64CWW.js} +5 -3
- package/dist/{store-7M4XV6M5.js → store-BY7S6IFN.js} +4 -3
- package/dist/{tool-index-NYH57UWP.js → tool-index-FTERJSZK.js} +2 -1
- package/package.json +1 -1
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
ConfigSchema,
|
|
3
2
|
getCachedTonClient,
|
|
4
3
|
getKeyPair,
|
|
5
4
|
getTonPrice,
|
|
@@ -7,7 +6,7 @@ import {
|
|
|
7
6
|
getWalletBalance,
|
|
8
7
|
invalidateTonClientCache,
|
|
9
8
|
loadWallet
|
|
10
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-JROBTXWY.js";
|
|
11
10
|
import {
|
|
12
11
|
getOrCreateSession,
|
|
13
12
|
getSession,
|
|
@@ -15,11 +14,18 @@ import {
|
|
|
15
14
|
resetSessionWithPolicy,
|
|
16
15
|
shouldResetSession,
|
|
17
16
|
updateSession
|
|
18
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-XDZDOKIF.js";
|
|
18
|
+
import {
|
|
19
|
+
ContextBuilder,
|
|
20
|
+
createDbWrapper,
|
|
21
|
+
getDatabase,
|
|
22
|
+
migrateFromMainDb,
|
|
23
|
+
openModuleDb
|
|
24
|
+
} from "./chunk-GHMXWAXI.js";
|
|
19
25
|
import {
|
|
20
26
|
saveSessionMemory,
|
|
21
27
|
summarizeWithFallback
|
|
22
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-ALKAAG4O.js";
|
|
23
29
|
import {
|
|
24
30
|
getErrorMessage,
|
|
25
31
|
isHttpError
|
|
@@ -34,28 +40,6 @@ import {
|
|
|
34
40
|
ut,
|
|
35
41
|
ye
|
|
36
42
|
} from "./chunk-7TECSLJ4.js";
|
|
37
|
-
import {
|
|
38
|
-
chatWithContext,
|
|
39
|
-
getEffectiveApiKey,
|
|
40
|
-
getProviderModel,
|
|
41
|
-
loadContextFromTranscript
|
|
42
|
-
} from "./chunk-J73TA3UM.js";
|
|
43
|
-
import {
|
|
44
|
-
getProviderMetadata,
|
|
45
|
-
getSupportedProviders
|
|
46
|
-
} from "./chunk-YOSUPUAJ.js";
|
|
47
|
-
import {
|
|
48
|
-
appendToTranscript,
|
|
49
|
-
archiveTranscript,
|
|
50
|
-
transcriptExists
|
|
51
|
-
} from "./chunk-LC4TV3KL.js";
|
|
52
|
-
import {
|
|
53
|
-
ContextBuilder,
|
|
54
|
-
createDbWrapper,
|
|
55
|
-
getDatabase,
|
|
56
|
-
migrateFromMainDb,
|
|
57
|
-
openModuleDb
|
|
58
|
-
} from "./chunk-VYKW7FMV.js";
|
|
59
43
|
import {
|
|
60
44
|
GECKOTERMINAL_API_URL,
|
|
61
45
|
tonapiFetch
|
|
@@ -84,6 +68,15 @@ import {
|
|
|
84
68
|
SERVER_ERROR_MAX_RETRIES,
|
|
85
69
|
TOOL_CONCURRENCY_LIMIT
|
|
86
70
|
} from "./chunk-C4NKJT2Z.js";
|
|
71
|
+
import {
|
|
72
|
+
chatWithContext,
|
|
73
|
+
getEffectiveApiKey,
|
|
74
|
+
getProviderModel,
|
|
75
|
+
loadContextFromTranscript
|
|
76
|
+
} from "./chunk-LVTKJQ7O.js";
|
|
77
|
+
import {
|
|
78
|
+
getProviderMetadata
|
|
79
|
+
} from "./chunk-6OOHHJ4N.js";
|
|
87
80
|
import {
|
|
88
81
|
fetchWithTimeout
|
|
89
82
|
} from "./chunk-XQUHC3JZ.js";
|
|
@@ -96,6 +89,11 @@ import {
|
|
|
96
89
|
RETRY_DEFAULT_MAX_DELAY_MS,
|
|
97
90
|
RETRY_DEFAULT_TIMEOUT_MS
|
|
98
91
|
} from "./chunk-R4YSJ4EY.js";
|
|
92
|
+
import {
|
|
93
|
+
appendToTranscript,
|
|
94
|
+
archiveTranscript,
|
|
95
|
+
transcriptExists
|
|
96
|
+
} from "./chunk-LC4TV3KL.js";
|
|
99
97
|
import {
|
|
100
98
|
ALLOWED_EXTENSIONS,
|
|
101
99
|
TELETON_ROOT,
|
|
@@ -106,140 +104,17 @@ import {
|
|
|
106
104
|
createLogger
|
|
107
105
|
} from "./chunk-NQ6FZKCE.js";
|
|
108
106
|
|
|
109
|
-
// src/config/loader.ts
|
|
110
|
-
import { readFileSync, existsSync, writeFileSync, mkdirSync } from "fs";
|
|
111
|
-
import { parse, stringify } from "yaml";
|
|
112
|
-
import { homedir } from "os";
|
|
113
|
-
import { dirname, join } from "path";
|
|
114
|
-
var log = createLogger("Config");
|
|
115
|
-
var DEFAULT_CONFIG_PATH = join(TELETON_ROOT, "config.yaml");
|
|
116
|
-
function expandPath(path) {
|
|
117
|
-
if (path.startsWith("~")) {
|
|
118
|
-
return join(homedir(), path.slice(1));
|
|
119
|
-
}
|
|
120
|
-
return path;
|
|
121
|
-
}
|
|
122
|
-
function loadConfig(configPath = DEFAULT_CONFIG_PATH) {
|
|
123
|
-
const fullPath = expandPath(configPath);
|
|
124
|
-
if (!existsSync(fullPath)) {
|
|
125
|
-
throw new Error(`Config file not found: ${fullPath}
|
|
126
|
-
Run 'teleton setup' to create one.`);
|
|
127
|
-
}
|
|
128
|
-
let content;
|
|
129
|
-
try {
|
|
130
|
-
content = readFileSync(fullPath, "utf-8");
|
|
131
|
-
} catch (error) {
|
|
132
|
-
throw new Error(`Cannot read config file ${fullPath}: ${error.message}`);
|
|
133
|
-
}
|
|
134
|
-
let raw;
|
|
135
|
-
try {
|
|
136
|
-
raw = parse(content);
|
|
137
|
-
} catch (error) {
|
|
138
|
-
throw new Error(`Invalid YAML in ${fullPath}: ${error.message}`);
|
|
139
|
-
}
|
|
140
|
-
if (raw && typeof raw === "object" && "market" in raw) {
|
|
141
|
-
log.warn("config.market is deprecated and ignored. Use market-api plugin instead.");
|
|
142
|
-
delete raw.market;
|
|
143
|
-
}
|
|
144
|
-
const result = ConfigSchema.safeParse(raw);
|
|
145
|
-
if (!result.success) {
|
|
146
|
-
throw new Error(`Invalid config: ${result.error.message}`);
|
|
147
|
-
}
|
|
148
|
-
const config = result.data;
|
|
149
|
-
const provider = config.agent.provider;
|
|
150
|
-
if (provider !== "anthropic" && provider !== "claude-code" && !raw.agent?.model) {
|
|
151
|
-
const meta = getProviderMetadata(provider);
|
|
152
|
-
config.agent.model = meta.defaultModel;
|
|
153
|
-
}
|
|
154
|
-
config.telegram.session_path = expandPath(config.telegram.session_path);
|
|
155
|
-
config.storage.sessions_file = expandPath(config.storage.sessions_file);
|
|
156
|
-
config.storage.memory_file = expandPath(config.storage.memory_file);
|
|
157
|
-
if (process.env.TELETON_API_KEY) {
|
|
158
|
-
config.agent.api_key = process.env.TELETON_API_KEY;
|
|
159
|
-
}
|
|
160
|
-
if (process.env.TELETON_TG_API_ID) {
|
|
161
|
-
const apiId = parseInt(process.env.TELETON_TG_API_ID, 10);
|
|
162
|
-
if (isNaN(apiId)) {
|
|
163
|
-
throw new Error(
|
|
164
|
-
`Invalid TELETON_TG_API_ID environment variable: "${process.env.TELETON_TG_API_ID}" is not a valid integer`
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
config.telegram.api_id = apiId;
|
|
168
|
-
}
|
|
169
|
-
if (process.env.TELETON_TG_API_HASH) {
|
|
170
|
-
config.telegram.api_hash = process.env.TELETON_TG_API_HASH;
|
|
171
|
-
}
|
|
172
|
-
if (process.env.TELETON_TG_PHONE) {
|
|
173
|
-
config.telegram.phone = process.env.TELETON_TG_PHONE;
|
|
174
|
-
}
|
|
175
|
-
if (process.env.TELETON_WEBUI_ENABLED) {
|
|
176
|
-
config.webui.enabled = process.env.TELETON_WEBUI_ENABLED === "true";
|
|
177
|
-
}
|
|
178
|
-
if (process.env.TELETON_WEBUI_PORT) {
|
|
179
|
-
const port = parseInt(process.env.TELETON_WEBUI_PORT, 10);
|
|
180
|
-
if (!isNaN(port) && port >= 1024 && port <= 65535) {
|
|
181
|
-
config.webui.port = port;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
if (process.env.TELETON_WEBUI_HOST) {
|
|
185
|
-
config.webui.host = process.env.TELETON_WEBUI_HOST;
|
|
186
|
-
if (!["127.0.0.1", "localhost", "::1"].includes(config.webui.host)) {
|
|
187
|
-
log.warn(
|
|
188
|
-
{ host: config.webui.host },
|
|
189
|
-
"WebUI bound to non-loopback address \u2014 ensure auth_token is set"
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
if (process.env.TELETON_API_ENABLED) {
|
|
194
|
-
if (!config.api) config.api = { enabled: false, port: 7778, key_hash: "", allowed_ips: [] };
|
|
195
|
-
config.api.enabled = process.env.TELETON_API_ENABLED === "true";
|
|
196
|
-
}
|
|
197
|
-
if (process.env.TELETON_API_PORT) {
|
|
198
|
-
const port = parseInt(process.env.TELETON_API_PORT, 10);
|
|
199
|
-
if (!isNaN(port) && port >= 1024 && port <= 65535) {
|
|
200
|
-
if (!config.api) config.api = { enabled: false, port: 7778, key_hash: "", allowed_ips: [] };
|
|
201
|
-
config.api.port = port;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
if (process.env.TELETON_BASE_URL) {
|
|
205
|
-
try {
|
|
206
|
-
new URL(process.env.TELETON_BASE_URL);
|
|
207
|
-
config.agent.base_url = process.env.TELETON_BASE_URL;
|
|
208
|
-
} catch {
|
|
209
|
-
throw new Error(
|
|
210
|
-
`Invalid TELETON_BASE_URL: "${process.env.TELETON_BASE_URL}" is not a valid URL`
|
|
211
|
-
);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
if (process.env.TELETON_TAVILY_API_KEY) {
|
|
215
|
-
config.tavily_api_key = process.env.TELETON_TAVILY_API_KEY;
|
|
216
|
-
}
|
|
217
|
-
if (process.env.TELETON_TONAPI_KEY) {
|
|
218
|
-
config.tonapi_key = process.env.TELETON_TONAPI_KEY;
|
|
219
|
-
}
|
|
220
|
-
if (process.env.TELETON_TONCENTER_API_KEY) {
|
|
221
|
-
config.toncenter_api_key = process.env.TELETON_TONCENTER_API_KEY;
|
|
222
|
-
}
|
|
223
|
-
return config;
|
|
224
|
-
}
|
|
225
|
-
function configExists(configPath = DEFAULT_CONFIG_PATH) {
|
|
226
|
-
return existsSync(expandPath(configPath));
|
|
227
|
-
}
|
|
228
|
-
function getDefaultConfigPath() {
|
|
229
|
-
return DEFAULT_CONFIG_PATH;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
107
|
// src/soul/loader.ts
|
|
233
|
-
import { readFileSync as
|
|
108
|
+
import { readFileSync as readFileSync2, existsSync as existsSync3 } from "fs";
|
|
234
109
|
|
|
235
110
|
// src/memory/daily-logs.ts
|
|
236
|
-
import { existsSync as
|
|
237
|
-
import { join
|
|
111
|
+
import { existsSync as existsSync2, mkdirSync, appendFileSync, readFileSync } from "fs";
|
|
112
|
+
import { join } from "path";
|
|
238
113
|
|
|
239
114
|
// src/workspace/validator.ts
|
|
240
|
-
import { existsSync
|
|
115
|
+
import { existsSync, lstatSync, readdirSync } from "fs";
|
|
241
116
|
import { resolve, normalize, relative, extname, basename } from "path";
|
|
242
|
-
import { homedir
|
|
117
|
+
import { homedir } from "os";
|
|
243
118
|
var WorkspaceSecurityError = class extends Error {
|
|
244
119
|
constructor(message, attemptedPath) {
|
|
245
120
|
super(message);
|
|
@@ -273,7 +148,7 @@ function validatePath(inputPath, allowCreate = false) {
|
|
|
273
148
|
if (decodedPath.startsWith("/")) {
|
|
274
149
|
absolutePath = resolve(normalize(decodedPath));
|
|
275
150
|
} else if (decodedPath.startsWith("~/")) {
|
|
276
|
-
const expanded = decodedPath.replace(/^~(?=$|[\\/])/,
|
|
151
|
+
const expanded = decodedPath.replace(/^~(?=$|[\\/])/, homedir());
|
|
277
152
|
absolutePath = resolve(expanded);
|
|
278
153
|
} else {
|
|
279
154
|
absolutePath = resolve(WORKSPACE_ROOT, normalize(decodedPath));
|
|
@@ -285,7 +160,7 @@ function validatePath(inputPath, allowCreate = false) {
|
|
|
285
160
|
inputPath
|
|
286
161
|
);
|
|
287
162
|
}
|
|
288
|
-
const exists =
|
|
163
|
+
const exists = existsSync(absolutePath);
|
|
289
164
|
if (!exists && !allowCreate) {
|
|
290
165
|
throw new WorkspaceSecurityError(
|
|
291
166
|
`File not found: '${inputPath}' does not exist in workspace.`,
|
|
@@ -349,7 +224,7 @@ function validateDirectory(inputPath) {
|
|
|
349
224
|
}
|
|
350
225
|
|
|
351
226
|
// src/memory/daily-logs.ts
|
|
352
|
-
var
|
|
227
|
+
var log = createLogger("Memory");
|
|
353
228
|
var MEMORY_DIR = WORKSPACE_PATHS.MEMORY_DIR;
|
|
354
229
|
function formatDate(date) {
|
|
355
230
|
const year = date.getFullYear();
|
|
@@ -358,11 +233,11 @@ function formatDate(date) {
|
|
|
358
233
|
return `${year}-${month}-${day}`;
|
|
359
234
|
}
|
|
360
235
|
function getDailyLogPath(date = /* @__PURE__ */ new Date()) {
|
|
361
|
-
return
|
|
236
|
+
return join(MEMORY_DIR, `${formatDate(date)}.md`);
|
|
362
237
|
}
|
|
363
238
|
function ensureMemoryDir() {
|
|
364
|
-
if (!
|
|
365
|
-
|
|
239
|
+
if (!existsSync2(MEMORY_DIR)) {
|
|
240
|
+
mkdirSync(MEMORY_DIR, { recursive: true });
|
|
366
241
|
}
|
|
367
242
|
}
|
|
368
243
|
function appendToDailyLog(content, date = /* @__PURE__ */ new Date()) {
|
|
@@ -370,7 +245,7 @@ function appendToDailyLog(content, date = /* @__PURE__ */ new Date()) {
|
|
|
370
245
|
ensureMemoryDir();
|
|
371
246
|
const logPath = getDailyLogPath(date);
|
|
372
247
|
const timestamp = date.toLocaleTimeString("en-US", { hour12: false });
|
|
373
|
-
if (!
|
|
248
|
+
if (!existsSync2(logPath)) {
|
|
374
249
|
const header = `# Daily Log - ${formatDate(date)}
|
|
375
250
|
|
|
376
251
|
`;
|
|
@@ -384,16 +259,16 @@ ${content}
|
|
|
384
259
|
|
|
385
260
|
`;
|
|
386
261
|
appendFileSync(logPath, entry, "utf-8");
|
|
387
|
-
|
|
262
|
+
log.info(`Daily log updated: ${logPath}`);
|
|
388
263
|
} catch (error) {
|
|
389
|
-
|
|
264
|
+
log.error({ err: error }, "Failed to write daily log");
|
|
390
265
|
}
|
|
391
266
|
}
|
|
392
267
|
function readDailyLog(date = /* @__PURE__ */ new Date()) {
|
|
393
268
|
try {
|
|
394
269
|
const logPath = getDailyLogPath(date);
|
|
395
|
-
if (!
|
|
396
|
-
return
|
|
270
|
+
if (!existsSync2(logPath)) return null;
|
|
271
|
+
return readFileSync(logPath, "utf-8");
|
|
397
272
|
} catch {
|
|
398
273
|
return null;
|
|
399
274
|
}
|
|
@@ -474,7 +349,7 @@ function cachedReadFile(path) {
|
|
|
474
349
|
if (cached && now < cached.expiry) return cached.content;
|
|
475
350
|
let content = null;
|
|
476
351
|
try {
|
|
477
|
-
if (
|
|
352
|
+
if (existsSync3(path)) content = readFileSync2(path, "utf-8");
|
|
478
353
|
} catch {
|
|
479
354
|
}
|
|
480
355
|
fileCache.set(path, { content, expiry: now + FILE_CACHE_TTL });
|
|
@@ -757,7 +632,7 @@ var DEFAULT_COMPACTION_CONFIG = {
|
|
|
757
632
|
memoryFlushEnabled: true,
|
|
758
633
|
softThresholdTokens: DEFAULT_SOFT_THRESHOLD_TOKENS
|
|
759
634
|
};
|
|
760
|
-
var
|
|
635
|
+
var log2 = createLogger("Memory");
|
|
761
636
|
function estimateContextTokens(context) {
|
|
762
637
|
let charCount = 0;
|
|
763
638
|
if (context.systemPrompt) {
|
|
@@ -789,7 +664,7 @@ function shouldFlushMemory(context, config, tokenCount) {
|
|
|
789
664
|
const tokens = tokenCount ?? estimateContextTokens(context);
|
|
790
665
|
const softThreshold = config.softThresholdTokens ?? FALLBACK_SOFT_THRESHOLD_TOKENS;
|
|
791
666
|
if (tokens >= softThreshold) {
|
|
792
|
-
|
|
667
|
+
log2.info(`Memory flush needed: ~${tokens} tokens (soft threshold: ${softThreshold})`);
|
|
793
668
|
return true;
|
|
794
669
|
}
|
|
795
670
|
return false;
|
|
@@ -811,7 +686,7 @@ function flushMemoryToDailyLog(context) {
|
|
|
811
686
|
}
|
|
812
687
|
}
|
|
813
688
|
writeSummaryToDailyLog(summary.join("\n"));
|
|
814
|
-
|
|
689
|
+
log2.info(`Memory flushed to daily log`);
|
|
815
690
|
}
|
|
816
691
|
function shouldCompact(context, config, tokenCount) {
|
|
817
692
|
if (!config.enabled) {
|
|
@@ -819,13 +694,13 @@ function shouldCompact(context, config, tokenCount) {
|
|
|
819
694
|
}
|
|
820
695
|
const messageCount = context.messages.length;
|
|
821
696
|
if (config.maxMessages && messageCount >= config.maxMessages) {
|
|
822
|
-
|
|
697
|
+
log2.info(`Compaction needed: ${messageCount} messages (max: ${config.maxMessages})`);
|
|
823
698
|
return true;
|
|
824
699
|
}
|
|
825
700
|
if (config.maxTokens) {
|
|
826
701
|
const tokens = tokenCount ?? estimateContextTokens(context);
|
|
827
702
|
if (tokens >= config.maxTokens) {
|
|
828
|
-
|
|
703
|
+
log2.info(`Compaction needed: ~${tokens} tokens (max: ${config.maxTokens})`);
|
|
829
704
|
return true;
|
|
830
705
|
}
|
|
831
706
|
}
|
|
@@ -871,12 +746,12 @@ async function compactContext(context, config, apiKey, provider, utilityModel) {
|
|
|
871
746
|
iterations++;
|
|
872
747
|
}
|
|
873
748
|
if (hasOrphanedToolResults(context.messages.slice(cutIndex))) {
|
|
874
|
-
|
|
749
|
+
log2.warn(`Compaction: couldn't find clean cut point, keeping all messages`);
|
|
875
750
|
return context;
|
|
876
751
|
}
|
|
877
752
|
const recentMessages = context.messages.slice(cutIndex);
|
|
878
753
|
const oldMessages = context.messages.slice(0, cutIndex);
|
|
879
|
-
|
|
754
|
+
log2.info(
|
|
880
755
|
`Compacting ${oldMessages.length} old messages, keeping ${recentMessages.length} recent (cut at clean boundary)`
|
|
881
756
|
);
|
|
882
757
|
try {
|
|
@@ -906,7 +781,7 @@ Keep each section concise. Omit a section if empty. Preserve specific names, num
|
|
|
906
781
|
provider,
|
|
907
782
|
utilityModel
|
|
908
783
|
});
|
|
909
|
-
|
|
784
|
+
log2.info(`AI Summary: ${result.tokensUsed} tokens, ${result.chunksProcessed} chunks processed`);
|
|
910
785
|
const summaryText = `[Auto-compacted ${oldMessages.length} messages]
|
|
911
786
|
|
|
912
787
|
${result.summary}`;
|
|
@@ -920,7 +795,7 @@ ${result.summary}`;
|
|
|
920
795
|
messages: [summaryMessage, ...recentMessages]
|
|
921
796
|
};
|
|
922
797
|
} catch (error) {
|
|
923
|
-
|
|
798
|
+
log2.error({ err: error }, "AI summarization failed, using fallback");
|
|
924
799
|
const summaryText = `[Auto-compacted: ${oldMessages.length} earlier messages from this conversation]`;
|
|
925
800
|
const summaryMessage = {
|
|
926
801
|
role: "user",
|
|
@@ -935,7 +810,7 @@ ${result.summary}`;
|
|
|
935
810
|
}
|
|
936
811
|
async function compactAndSaveTranscript(sessionId, context, config, apiKey, chatId, provider, utilityModel) {
|
|
937
812
|
const newSessionId = randomUUID();
|
|
938
|
-
|
|
813
|
+
log2.info(`Creating compacted transcript: ${sessionId} \u2192 ${newSessionId}`);
|
|
939
814
|
if (chatId) {
|
|
940
815
|
await saveSessionMemory({
|
|
941
816
|
oldSessionId: sessionId,
|
|
@@ -969,7 +844,7 @@ var CompactionManager = class {
|
|
|
969
844
|
if (this.config.memoryFlushEnabled) {
|
|
970
845
|
flushMemoryToDailyLog(context);
|
|
971
846
|
}
|
|
972
|
-
|
|
847
|
+
log2.info(`Auto-compacting session ${sessionId}`);
|
|
973
848
|
const newSessionId = await compactAndSaveTranscript(
|
|
974
849
|
sessionId,
|
|
975
850
|
context,
|
|
@@ -979,7 +854,7 @@ var CompactionManager = class {
|
|
|
979
854
|
provider,
|
|
980
855
|
utilityModel
|
|
981
856
|
);
|
|
982
|
-
|
|
857
|
+
log2.info(`Compaction complete: ${newSessionId}`);
|
|
983
858
|
return newSessionId;
|
|
984
859
|
}
|
|
985
860
|
updateConfig(config) {
|
|
@@ -1111,7 +986,7 @@ function maskOldToolResults(messages, options) {
|
|
|
1111
986
|
}
|
|
1112
987
|
|
|
1113
988
|
// src/agent/runtime.ts
|
|
1114
|
-
var
|
|
989
|
+
var log3 = createLogger("Agent");
|
|
1115
990
|
var globalTokenUsage = { totalTokens: 0, totalCost: 0 };
|
|
1116
991
|
function getTokenUsage() {
|
|
1117
992
|
return { ...globalTokenUsage };
|
|
@@ -1224,7 +1099,7 @@ var AgentRuntime = class {
|
|
|
1224
1099
|
if (this.userHookEvaluator) {
|
|
1225
1100
|
const hookResult = this.userHookEvaluator.evaluate(userMessage);
|
|
1226
1101
|
if (hookResult.blocked) {
|
|
1227
|
-
|
|
1102
|
+
log3.info("Message blocked by keyword filter");
|
|
1228
1103
|
return { content: hookResult.blockMessage ?? "", toolCalls: [] };
|
|
1229
1104
|
}
|
|
1230
1105
|
if (hookResult.additionalContext) {
|
|
@@ -1250,7 +1125,7 @@ var AgentRuntime = class {
|
|
|
1250
1125
|
};
|
|
1251
1126
|
await this.hookRunner.runModifyingHook("message:receive", msgEvent);
|
|
1252
1127
|
if (msgEvent.block) {
|
|
1253
|
-
|
|
1128
|
+
log3.info(`\u{1F6AB} Message blocked by hook: ${msgEvent.blockReason || "no reason"}`);
|
|
1254
1129
|
return { content: "", toolCalls: [] };
|
|
1255
1130
|
}
|
|
1256
1131
|
effectiveMessage = sanitizeForContext(msgEvent.text);
|
|
@@ -1262,7 +1137,7 @@ var AgentRuntime = class {
|
|
|
1262
1137
|
const now = timestamp ?? Date.now();
|
|
1263
1138
|
const resetPolicy = this.config.agent.session_reset_policy;
|
|
1264
1139
|
if (shouldResetSession(session, resetPolicy)) {
|
|
1265
|
-
|
|
1140
|
+
log3.info(`\u{1F504} Auto-resetting session based on policy`);
|
|
1266
1141
|
if (this.hookRunner) {
|
|
1267
1142
|
await this.hookRunner.runObservingHook("session:end", {
|
|
1268
1143
|
sessionId: session.sessionId,
|
|
@@ -1272,7 +1147,7 @@ var AgentRuntime = class {
|
|
|
1272
1147
|
}
|
|
1273
1148
|
if (transcriptExists(session.sessionId)) {
|
|
1274
1149
|
try {
|
|
1275
|
-
|
|
1150
|
+
log3.info(`\u{1F4BE} Saving memory before daily reset...`);
|
|
1276
1151
|
const oldContext = loadContextFromTranscript(session.sessionId);
|
|
1277
1152
|
await saveSessionMemory({
|
|
1278
1153
|
oldSessionId: session.sessionId,
|
|
@@ -1283,9 +1158,9 @@ var AgentRuntime = class {
|
|
|
1283
1158
|
provider: this.config.agent.provider,
|
|
1284
1159
|
utilityModel: this.config.agent.utility_model
|
|
1285
1160
|
});
|
|
1286
|
-
|
|
1161
|
+
log3.info(`\u2705 Memory saved before reset`);
|
|
1287
1162
|
} catch (error) {
|
|
1288
|
-
|
|
1163
|
+
log3.warn({ err: error }, `\u26A0\uFE0F Failed to save memory before reset`);
|
|
1289
1164
|
}
|
|
1290
1165
|
}
|
|
1291
1166
|
session = resetSessionWithPolicy(chatId, resetPolicy);
|
|
@@ -1293,9 +1168,9 @@ var AgentRuntime = class {
|
|
|
1293
1168
|
let context = loadContextFromTranscript(session.sessionId);
|
|
1294
1169
|
const isNewSession = context.messages.length === 0;
|
|
1295
1170
|
if (!isNewSession) {
|
|
1296
|
-
|
|
1171
|
+
log3.info(`\u{1F4D6} Loading existing session: ${session.sessionId}`);
|
|
1297
1172
|
} else {
|
|
1298
|
-
|
|
1173
|
+
log3.info(`\u{1F195} Starting new session: ${session.sessionId}`);
|
|
1299
1174
|
}
|
|
1300
1175
|
if (this.hookRunner) {
|
|
1301
1176
|
await this.hookRunner.runObservingHook("session:start", {
|
|
@@ -1324,13 +1199,13 @@ var AgentRuntime = class {
|
|
|
1324
1199
|
formattedMessage = `${pendingContext}
|
|
1325
1200
|
|
|
1326
1201
|
${formattedMessage}`;
|
|
1327
|
-
|
|
1202
|
+
log3.debug(`\u{1F4CB} Including ${pendingContext.split("\n").length - 1} pending messages`);
|
|
1328
1203
|
}
|
|
1329
|
-
|
|
1204
|
+
log3.debug(`\u{1F4E8} Formatted message: ${formattedMessage.substring(0, 100)}...`);
|
|
1330
1205
|
const preview = formattedMessage.slice(0, 50).replace(/\n/g, " ");
|
|
1331
1206
|
const who = senderUsername ? `@${senderUsername}` : userName;
|
|
1332
1207
|
const msgType = isGroup ? `Group ${chatId} ${who}` : `DM ${who}`;
|
|
1333
|
-
|
|
1208
|
+
log3.info(`\u{1F4E8} ${msgType}: "${preview}${formattedMessage.length > 50 ? "..." : ""}"`);
|
|
1334
1209
|
let relevantContext = "";
|
|
1335
1210
|
let queryEmbedding;
|
|
1336
1211
|
const isNonTrivial = !isTrivialMessage(effectiveMessage);
|
|
@@ -1349,7 +1224,7 @@ ${formattedMessage}`;
|
|
|
1349
1224
|
searchQuery.slice(0, EMBEDDING_QUERY_MAX_CHARS)
|
|
1350
1225
|
);
|
|
1351
1226
|
} catch (error) {
|
|
1352
|
-
|
|
1227
|
+
log3.warn({ err: error }, "Embedding computation failed");
|
|
1353
1228
|
}
|
|
1354
1229
|
}
|
|
1355
1230
|
if (this.contextBuilder && isNonTrivial) {
|
|
@@ -1383,12 +1258,12 @@ ${sanitizedFeed.join("\n")}`
|
|
|
1383
1258
|
}
|
|
1384
1259
|
if (contextParts.length > 0) {
|
|
1385
1260
|
relevantContext = contextParts.join("\n\n");
|
|
1386
|
-
|
|
1261
|
+
log3.debug(
|
|
1387
1262
|
`\u{1F50D} Found ${dbContext.relevantKnowledge.length} knowledge chunks, ${dbContext.relevantFeed.length} feed messages`
|
|
1388
1263
|
);
|
|
1389
1264
|
}
|
|
1390
1265
|
} catch (error) {
|
|
1391
|
-
|
|
1266
|
+
log3.warn({ err: error }, "Context building failed");
|
|
1392
1267
|
}
|
|
1393
1268
|
}
|
|
1394
1269
|
const memoryStats = this.getMemoryStats();
|
|
@@ -1456,7 +1331,7 @@ ${allHookContext}` : "");
|
|
|
1456
1331
|
this.config.agent.utility_model
|
|
1457
1332
|
);
|
|
1458
1333
|
if (preemptiveCompaction) {
|
|
1459
|
-
|
|
1334
|
+
log3.info(`\u{1F5DC}\uFE0F Preemptive compaction triggered, reloading session...`);
|
|
1460
1335
|
session = getSession(chatId);
|
|
1461
1336
|
context = loadContextFromTranscript(session.sessionId);
|
|
1462
1337
|
context.messages.push(userMsg);
|
|
@@ -1478,7 +1353,7 @@ ${allHookContext}` : "");
|
|
|
1478
1353
|
chatId,
|
|
1479
1354
|
isAdmin
|
|
1480
1355
|
);
|
|
1481
|
-
|
|
1356
|
+
log3.info(`\u{1F50D} Tool RAG: ${tools.length}/${this.toolRegistry.count} tools selected`);
|
|
1482
1357
|
} else {
|
|
1483
1358
|
tools = this.toolRegistry?.getForContext(
|
|
1484
1359
|
effectiveIsGroup,
|
|
@@ -1500,7 +1375,7 @@ ${allHookContext}` : "");
|
|
|
1500
1375
|
const seenToolSignatures = /* @__PURE__ */ new Set();
|
|
1501
1376
|
while (iteration < maxIterations) {
|
|
1502
1377
|
iteration++;
|
|
1503
|
-
|
|
1378
|
+
log3.debug(`\u{1F504} Agentic iteration ${iteration}/${maxIterations}`);
|
|
1504
1379
|
const iterationStartIndex = context.messages.length;
|
|
1505
1380
|
const maskedMessages = maskOldToolResults(context.messages, {
|
|
1506
1381
|
toolRegistry: this.toolRegistry ?? void 0,
|
|
@@ -1539,35 +1414,35 @@ ${allHookContext}` : "");
|
|
|
1539
1414
|
"Context overflow persists after session reset. Message may be too large for the model's context window."
|
|
1540
1415
|
);
|
|
1541
1416
|
}
|
|
1542
|
-
|
|
1543
|
-
|
|
1417
|
+
log3.error(`\u{1F6A8} Context overflow detected: ${errorMsg}`);
|
|
1418
|
+
log3.info(`\u{1F4BE} Saving session memory before reset...`);
|
|
1544
1419
|
const summary = extractContextSummary(context, CONTEXT_OVERFLOW_SUMMARY_MESSAGES);
|
|
1545
1420
|
appendToDailyLog(summary);
|
|
1546
|
-
|
|
1421
|
+
log3.info(`\u2705 Memory saved to daily log`);
|
|
1547
1422
|
const archived = archiveTranscript(session.sessionId);
|
|
1548
1423
|
if (!archived) {
|
|
1549
|
-
|
|
1424
|
+
log3.error(
|
|
1550
1425
|
`\u26A0\uFE0F Failed to archive transcript ${session.sessionId}, proceeding with reset anyway`
|
|
1551
1426
|
);
|
|
1552
1427
|
}
|
|
1553
|
-
|
|
1428
|
+
log3.info(`\u{1F504} Resetting session due to context overflow...`);
|
|
1554
1429
|
session = resetSession(chatId);
|
|
1555
1430
|
context = { messages: [userMsg] };
|
|
1556
1431
|
appendToTranscript(session.sessionId, userMsg);
|
|
1557
|
-
|
|
1432
|
+
log3.info(`\u{1F504} Retrying with fresh context...`);
|
|
1558
1433
|
continue;
|
|
1559
1434
|
} else if (errorMsg.toLowerCase().includes("rate") || errorMsg.includes("429")) {
|
|
1560
1435
|
rateLimitRetries++;
|
|
1561
1436
|
if (rateLimitRetries <= RATE_LIMIT_MAX_RETRIES) {
|
|
1562
1437
|
const delay = 1e3 * Math.pow(2, rateLimitRetries - 1);
|
|
1563
|
-
|
|
1438
|
+
log3.warn(
|
|
1564
1439
|
`\u{1F6AB} Rate limited, retrying in ${delay}ms (attempt ${rateLimitRetries}/${RATE_LIMIT_MAX_RETRIES})...`
|
|
1565
1440
|
);
|
|
1566
1441
|
await new Promise((r3) => setTimeout(r3, delay));
|
|
1567
1442
|
iteration--;
|
|
1568
1443
|
continue;
|
|
1569
1444
|
}
|
|
1570
|
-
|
|
1445
|
+
log3.error(`\u{1F6AB} Rate limited after ${RATE_LIMIT_MAX_RETRIES} retries: ${errorMsg}`);
|
|
1571
1446
|
throw new Error(
|
|
1572
1447
|
`API rate limited after ${RATE_LIMIT_MAX_RETRIES} retries. Please try again later.`
|
|
1573
1448
|
);
|
|
@@ -1575,19 +1450,19 @@ ${allHookContext}` : "");
|
|
|
1575
1450
|
serverErrorRetries++;
|
|
1576
1451
|
if (serverErrorRetries <= SERVER_ERROR_MAX_RETRIES) {
|
|
1577
1452
|
const delay = 2e3 * Math.pow(2, serverErrorRetries - 1);
|
|
1578
|
-
|
|
1453
|
+
log3.warn(
|
|
1579
1454
|
`\u{1F504} Server error, retrying in ${delay}ms (attempt ${serverErrorRetries}/${SERVER_ERROR_MAX_RETRIES})...`
|
|
1580
1455
|
);
|
|
1581
1456
|
await new Promise((r3) => setTimeout(r3, delay));
|
|
1582
1457
|
iteration--;
|
|
1583
1458
|
continue;
|
|
1584
1459
|
}
|
|
1585
|
-
|
|
1460
|
+
log3.error(`\u{1F6A8} Server error after ${SERVER_ERROR_MAX_RETRIES} retries: ${errorMsg}`);
|
|
1586
1461
|
throw new Error(
|
|
1587
1462
|
`API server error after ${SERVER_ERROR_MAX_RETRIES} retries. The provider may be experiencing issues.`
|
|
1588
1463
|
);
|
|
1589
1464
|
} else {
|
|
1590
|
-
|
|
1465
|
+
log3.error(`\u{1F6A8} API error: ${errorMsg}`);
|
|
1591
1466
|
throw new Error(`API error: ${errorMsg || "Unknown error"}`);
|
|
1592
1467
|
}
|
|
1593
1468
|
}
|
|
@@ -1604,15 +1479,15 @@ ${allHookContext}` : "");
|
|
|
1604
1479
|
}
|
|
1605
1480
|
const toolCalls = response2.message.content.filter((block) => block.type === "toolCall");
|
|
1606
1481
|
if (toolCalls.length === 0) {
|
|
1607
|
-
|
|
1482
|
+
log3.info(`\u{1F504} ${iteration}/${maxIterations} \u2192 done`);
|
|
1608
1483
|
finalResponse = response2;
|
|
1609
1484
|
break;
|
|
1610
1485
|
}
|
|
1611
1486
|
if (!this.toolRegistry || !toolContext) {
|
|
1612
|
-
|
|
1487
|
+
log3.error("\u26A0\uFE0F Cannot execute tools: registry or context missing");
|
|
1613
1488
|
break;
|
|
1614
1489
|
}
|
|
1615
|
-
|
|
1490
|
+
log3.debug(`\u{1F527} Executing ${toolCalls.length} tool call(s)`);
|
|
1616
1491
|
context.messages.push(response2.message);
|
|
1617
1492
|
const iterationToolNames = [];
|
|
1618
1493
|
const fullContext = {
|
|
@@ -1712,7 +1587,7 @@ ${allHookContext}` : "");
|
|
|
1712
1587
|
};
|
|
1713
1588
|
await this.hookRunner.runObservingHook("tool:after", afterEvent);
|
|
1714
1589
|
}
|
|
1715
|
-
|
|
1590
|
+
log3.debug(`${block.name}: ${exec.result.success ? "\u2713" : "\u2717"} ${exec.result.error || ""}`);
|
|
1716
1591
|
iterationToolNames.push(`${block.name} ${exec.result.success ? "\u2713" : "\u2717"}`);
|
|
1717
1592
|
totalToolCalls.push({
|
|
1718
1593
|
name: block.name,
|
|
@@ -1720,7 +1595,7 @@ ${allHookContext}` : "");
|
|
|
1720
1595
|
});
|
|
1721
1596
|
let resultText = JSON.stringify(exec.result);
|
|
1722
1597
|
if (resultText.length > MAX_TOOL_RESULT_SIZE) {
|
|
1723
|
-
|
|
1598
|
+
log3.warn(`\u26A0\uFE0F Tool result too large (${resultText.length} chars), truncating...`);
|
|
1724
1599
|
const data = exec.result.data;
|
|
1725
1600
|
if (data?.summary || data?.message) {
|
|
1726
1601
|
resultText = JSON.stringify({
|
|
@@ -1784,26 +1659,26 @@ ${allHookContext}` : "");
|
|
|
1784
1659
|
appendToTranscript(session.sessionId, toolResultMsg);
|
|
1785
1660
|
}
|
|
1786
1661
|
}
|
|
1787
|
-
|
|
1662
|
+
log3.info(`\u{1F504} ${iteration}/${maxIterations} \u2192 ${iterationToolNames.join(", ")}`);
|
|
1788
1663
|
const iterSignatures = toolPlans.map(
|
|
1789
1664
|
(p2) => `${p2.block.name}:${JSON.stringify(p2.params, Object.keys(p2.params).sort())}`
|
|
1790
1665
|
);
|
|
1791
1666
|
const allDuplicates = iterSignatures.length > 0 && iterSignatures.every((sig) => seenToolSignatures.has(sig));
|
|
1792
1667
|
for (const sig of iterSignatures) seenToolSignatures.add(sig);
|
|
1793
1668
|
if (allDuplicates) {
|
|
1794
|
-
|
|
1669
|
+
log3.warn(
|
|
1795
1670
|
`\u{1F501} Loop stall detected: all ${iterSignatures.length} tool call(s) are repeats \u2014 breaking early`
|
|
1796
1671
|
);
|
|
1797
1672
|
finalResponse = response2;
|
|
1798
1673
|
break;
|
|
1799
1674
|
}
|
|
1800
1675
|
if (iteration === maxIterations) {
|
|
1801
|
-
|
|
1676
|
+
log3.info(`\u26A0\uFE0F Max iterations reached (${maxIterations})`);
|
|
1802
1677
|
finalResponse = response2;
|
|
1803
1678
|
}
|
|
1804
1679
|
}
|
|
1805
1680
|
if (!finalResponse) {
|
|
1806
|
-
|
|
1681
|
+
log3.error("\u26A0\uFE0F Agentic loop exited early without final response");
|
|
1807
1682
|
return {
|
|
1808
1683
|
content: "Internal error: Agent loop failed to produce a response.",
|
|
1809
1684
|
toolCalls: []
|
|
@@ -1831,20 +1706,20 @@ ${allHookContext}` : "");
|
|
|
1831
1706
|
if (u.cacheRead) cacheParts.push(`${(u.cacheRead / 1e3).toFixed(1)}K cached`);
|
|
1832
1707
|
if (u.cacheWrite) cacheParts.push(`${(u.cacheWrite / 1e3).toFixed(1)}K new`);
|
|
1833
1708
|
const cacheInfo = cacheParts.length > 0 ? ` (${cacheParts.join(", ")})` : "";
|
|
1834
|
-
|
|
1709
|
+
log3.info(`\u{1F4B0} ${inK}K in${cacheInfo}, ${u.output} out | $${u.totalCost.toFixed(3)}`);
|
|
1835
1710
|
globalTokenUsage.totalTokens += u.input + u.output + u.cacheRead + u.cacheWrite;
|
|
1836
1711
|
globalTokenUsage.totalCost += u.totalCost;
|
|
1837
1712
|
}
|
|
1838
1713
|
let content = accumulatedTexts.join("\n").trim() || response.text;
|
|
1839
1714
|
const usedTelegramSendTool = totalToolCalls.some((tc) => TELEGRAM_SEND_TOOLS.has(tc.name));
|
|
1840
1715
|
if (!content && totalToolCalls.length > 0 && !usedTelegramSendTool) {
|
|
1841
|
-
|
|
1716
|
+
log3.warn("\u26A0\uFE0F Empty response after tool calls - generating fallback");
|
|
1842
1717
|
content = "I executed the requested action but couldn't generate a response. Please try again.";
|
|
1843
1718
|
} else if (!content && usedTelegramSendTool) {
|
|
1844
|
-
|
|
1719
|
+
log3.info("\u2705 Response sent via Telegram tool - no additional text needed");
|
|
1845
1720
|
content = "";
|
|
1846
1721
|
} else if (!content && accumulatedUsage.input === 0 && accumulatedUsage.output === 0) {
|
|
1847
|
-
|
|
1722
|
+
log3.warn("\u26A0\uFE0F Empty response with zero tokens - possible API issue");
|
|
1848
1723
|
content = "I couldn't process your request. Please try again.";
|
|
1849
1724
|
}
|
|
1850
1725
|
let responseMetadata = {};
|
|
@@ -1861,7 +1736,7 @@ ${allHookContext}` : "");
|
|
|
1861
1736
|
};
|
|
1862
1737
|
await this.hookRunner.runModifyingHook("response:before", responseBeforeEvent);
|
|
1863
1738
|
if (responseBeforeEvent.block) {
|
|
1864
|
-
|
|
1739
|
+
log3.info(
|
|
1865
1740
|
`\u{1F6AB} Response blocked by hook: ${responseBeforeEvent.blockReason || "no reason"}`
|
|
1866
1741
|
);
|
|
1867
1742
|
content = "";
|
|
@@ -1888,7 +1763,7 @@ ${allHookContext}` : "");
|
|
|
1888
1763
|
toolCalls: totalToolCalls
|
|
1889
1764
|
};
|
|
1890
1765
|
} catch (error) {
|
|
1891
|
-
|
|
1766
|
+
log3.error({ err: error }, "Agent error");
|
|
1892
1767
|
throw error;
|
|
1893
1768
|
}
|
|
1894
1769
|
}
|
|
@@ -1901,7 +1776,7 @@ ${allHookContext}` : "");
|
|
|
1901
1776
|
).run(chatId);
|
|
1902
1777
|
db.prepare(`DELETE FROM tg_messages WHERE chat_id = ?`).run(chatId);
|
|
1903
1778
|
resetSession(chatId);
|
|
1904
|
-
|
|
1779
|
+
log3.info(`\u{1F5D1}\uFE0F Cleared history for chat ${chatId}`);
|
|
1905
1780
|
}
|
|
1906
1781
|
getConfig() {
|
|
1907
1782
|
return this.config;
|
|
@@ -1922,7 +1797,7 @@ ${allHookContext}` : "");
|
|
|
1922
1797
|
}
|
|
1923
1798
|
configureCompaction(config) {
|
|
1924
1799
|
this.compactionManager.updateConfig(config);
|
|
1925
|
-
|
|
1800
|
+
log3.info({ config: this.compactionManager.getConfig() }, `\u{1F5DC}\uFE0F Compaction config updated`);
|
|
1926
1801
|
}
|
|
1927
1802
|
getCompactionConfig() {
|
|
1928
1803
|
return this.compactionManager.getConfig();
|
|
@@ -2132,7 +2007,7 @@ function unescapeHtml(text) {
|
|
|
2132
2007
|
}
|
|
2133
2008
|
|
|
2134
2009
|
// src/bot/inline-router.ts
|
|
2135
|
-
var
|
|
2010
|
+
var log4 = createLogger("InlineRouter");
|
|
2136
2011
|
var INLINE_TIMEOUT_MS = 5e3;
|
|
2137
2012
|
var CALLBACK_TIMEOUT_MS = 15e3;
|
|
2138
2013
|
function compileGlob(pattern) {
|
|
@@ -2153,11 +2028,11 @@ var InlineRouter = class {
|
|
|
2153
2028
|
}
|
|
2154
2029
|
registerPlugin(name, handlers) {
|
|
2155
2030
|
this.plugins.set(name, handlers);
|
|
2156
|
-
|
|
2031
|
+
log4.info(`Registered plugin "${name}" for inline routing`);
|
|
2157
2032
|
}
|
|
2158
2033
|
unregisterPlugin(name) {
|
|
2159
2034
|
this.plugins.delete(name);
|
|
2160
|
-
|
|
2035
|
+
log4.info(`Unregistered plugin "${name}" from inline routing`);
|
|
2161
2036
|
}
|
|
2162
2037
|
hasPlugin(name) {
|
|
2163
2038
|
return this.plugins.has(name);
|
|
@@ -2229,7 +2104,7 @@ var InlineRouter = class {
|
|
|
2229
2104
|
is_personal: true
|
|
2230
2105
|
});
|
|
2231
2106
|
} catch (error) {
|
|
2232
|
-
|
|
2107
|
+
log4.error({ err: error }, `Plugin "${pluginName}" inline query handler failed`);
|
|
2233
2108
|
try {
|
|
2234
2109
|
await ctx.answerInlineQuery([], { cache_time: 0, is_personal: true });
|
|
2235
2110
|
} catch {
|
|
@@ -2289,7 +2164,7 @@ var InlineRouter = class {
|
|
|
2289
2164
|
} catch (error) {
|
|
2290
2165
|
const errMsg = error?.errorMessage;
|
|
2291
2166
|
if (errMsg === "MESSAGE_NOT_MODIFIED") return;
|
|
2292
|
-
|
|
2167
|
+
log4.debug(`GramJS edit failed, falling back to Grammy: ${errMsg || error}`);
|
|
2293
2168
|
}
|
|
2294
2169
|
}
|
|
2295
2170
|
const replyMarkup = styledButtons ? toGrammyKeyboard(styledButtons) : void 0;
|
|
@@ -2309,7 +2184,7 @@ var InlineRouter = class {
|
|
|
2309
2184
|
await ctx.answerCallbackQuery();
|
|
2310
2185
|
}
|
|
2311
2186
|
} catch (error) {
|
|
2312
|
-
|
|
2187
|
+
log4.error({ err: error }, `Plugin "${pluginName}" callback handler failed`);
|
|
2313
2188
|
if (!answered) {
|
|
2314
2189
|
try {
|
|
2315
2190
|
await ctx.answerCallbackQuery({ text: "Error processing action" });
|
|
@@ -2332,7 +2207,7 @@ var InlineRouter = class {
|
|
|
2332
2207
|
};
|
|
2333
2208
|
await plugin.onChosenResult(crCtx);
|
|
2334
2209
|
} catch (error) {
|
|
2335
|
-
|
|
2210
|
+
log4.error({ err: error }, `Plugin "${pluginName}" chosen result handler failed`);
|
|
2336
2211
|
}
|
|
2337
2212
|
}
|
|
2338
2213
|
/**
|
|
@@ -2410,8 +2285,8 @@ function withTimeout(promise, ms, message) {
|
|
|
2410
2285
|
}
|
|
2411
2286
|
|
|
2412
2287
|
// src/agent/tools/plugin-loader.ts
|
|
2413
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
2414
|
-
import { join as
|
|
2288
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync4, existsSync as existsSync5, statSync } from "fs";
|
|
2289
|
+
import { join as join3 } from "path";
|
|
2415
2290
|
import { pathToFileURL } from "url";
|
|
2416
2291
|
import { execFile } from "child_process";
|
|
2417
2292
|
|
|
@@ -2442,7 +2317,7 @@ import { promisify } from "util";
|
|
|
2442
2317
|
|
|
2443
2318
|
// src/agent/tools/plugin-validator.ts
|
|
2444
2319
|
import { z } from "zod";
|
|
2445
|
-
var
|
|
2320
|
+
var log5 = createLogger("PluginValidator");
|
|
2446
2321
|
var ManifestSchema = z.object({
|
|
2447
2322
|
name: z.string().min(1).max(64).regex(
|
|
2448
2323
|
/^[a-z0-9][a-z0-9-]*$/,
|
|
@@ -2485,20 +2360,20 @@ function validateToolDefs(defs, pluginName) {
|
|
|
2485
2360
|
const valid = [];
|
|
2486
2361
|
for (const def of defs) {
|
|
2487
2362
|
if (!def || typeof def !== "object") {
|
|
2488
|
-
|
|
2363
|
+
log5.warn(`[${pluginName}] tool is not an object, skipping`);
|
|
2489
2364
|
continue;
|
|
2490
2365
|
}
|
|
2491
2366
|
const t = def;
|
|
2492
2367
|
if (!t.name || typeof t.name !== "string") {
|
|
2493
|
-
|
|
2368
|
+
log5.warn(`[${pluginName}] tool missing 'name', skipping`);
|
|
2494
2369
|
continue;
|
|
2495
2370
|
}
|
|
2496
2371
|
if (!t.description || typeof t.description !== "string") {
|
|
2497
|
-
|
|
2372
|
+
log5.warn(`[${pluginName}] tool "${t.name}" missing 'description', skipping`);
|
|
2498
2373
|
continue;
|
|
2499
2374
|
}
|
|
2500
2375
|
if (!t.execute || typeof t.execute !== "function") {
|
|
2501
|
-
|
|
2376
|
+
log5.warn(`[${pluginName}] tool "${t.name}" missing 'execute' function, skipping`);
|
|
2502
2377
|
continue;
|
|
2503
2378
|
}
|
|
2504
2379
|
valid.push(t);
|
|
@@ -2558,25 +2433,25 @@ function withTxLock(fn) {
|
|
|
2558
2433
|
}
|
|
2559
2434
|
|
|
2560
2435
|
// src/ton/transfer.ts
|
|
2561
|
-
var
|
|
2436
|
+
var log6 = createLogger("TON");
|
|
2562
2437
|
async function sendTon(params) {
|
|
2563
2438
|
return withTxLock(async () => {
|
|
2564
2439
|
try {
|
|
2565
2440
|
const { toAddress: toAddress2, amount, comment = "", bounce = false } = params;
|
|
2566
2441
|
if (!Number.isFinite(amount) || amount <= 0) {
|
|
2567
|
-
|
|
2442
|
+
log6.error({ amount }, "Invalid transfer amount");
|
|
2568
2443
|
return null;
|
|
2569
2444
|
}
|
|
2570
2445
|
let recipientAddress;
|
|
2571
2446
|
try {
|
|
2572
2447
|
recipientAddress = Address.parse(toAddress2);
|
|
2573
2448
|
} catch (e) {
|
|
2574
|
-
|
|
2449
|
+
log6.error({ err: e }, `Invalid recipient address: ${toAddress2}`);
|
|
2575
2450
|
return null;
|
|
2576
2451
|
}
|
|
2577
2452
|
const keyPair = await getKeyPair();
|
|
2578
2453
|
if (!keyPair) {
|
|
2579
|
-
|
|
2454
|
+
log6.error("Wallet not initialized");
|
|
2580
2455
|
return null;
|
|
2581
2456
|
}
|
|
2582
2457
|
const wallet = WalletContractV5R1.create({
|
|
@@ -2600,7 +2475,7 @@ async function sendTon(params) {
|
|
|
2600
2475
|
]
|
|
2601
2476
|
});
|
|
2602
2477
|
const pseudoHash = `${seqno}_${Date.now()}_${amount.toFixed(2)}`;
|
|
2603
|
-
|
|
2478
|
+
log6.info(`Sent ${amount} TON to ${toAddress2.slice(0, 8)}... - seqno: ${seqno}`);
|
|
2604
2479
|
return pseudoHash;
|
|
2605
2480
|
} catch (error) {
|
|
2606
2481
|
const err = error;
|
|
@@ -2608,14 +2483,14 @@ async function sendTon(params) {
|
|
|
2608
2483
|
if (status === 429 || status !== void 0 && status >= 500) {
|
|
2609
2484
|
invalidateTonClientCache();
|
|
2610
2485
|
}
|
|
2611
|
-
|
|
2486
|
+
log6.error({ err: error }, "Error sending TON");
|
|
2612
2487
|
throw error;
|
|
2613
2488
|
}
|
|
2614
2489
|
});
|
|
2615
2490
|
}
|
|
2616
2491
|
|
|
2617
2492
|
// src/utils/retry.ts
|
|
2618
|
-
var
|
|
2493
|
+
var log7 = createLogger("Utils");
|
|
2619
2494
|
var DEFAULT_OPTIONS = {
|
|
2620
2495
|
maxAttempts: RETRY_DEFAULT_MAX_ATTEMPTS,
|
|
2621
2496
|
baseDelayMs: RETRY_DEFAULT_BASE_DELAY_MS,
|
|
@@ -2639,7 +2514,7 @@ async function withRetry(fn, options = {}) {
|
|
|
2639
2514
|
return result;
|
|
2640
2515
|
} catch (error) {
|
|
2641
2516
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
2642
|
-
|
|
2517
|
+
log7.warn(`Retry attempt ${attempt}/${opts.maxAttempts} failed: ${lastError.message}`);
|
|
2643
2518
|
if (attempt < opts.maxAttempts) {
|
|
2644
2519
|
const delay = Math.min(opts.baseDelayMs * Math.pow(2, attempt - 1), opts.maxDelayMs);
|
|
2645
2520
|
await sleep(delay);
|
|
@@ -6117,7 +5992,7 @@ var DEDUST_GAS = {
|
|
|
6117
5992
|
var NATIVE_TON_ADDRESS = "EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c";
|
|
6118
5993
|
|
|
6119
5994
|
// src/agent/tools/dedust/asset-cache.ts
|
|
6120
|
-
var
|
|
5995
|
+
var log8 = createLogger("Tools");
|
|
6121
5996
|
var ASSET_LIST_URL = "https://assets.dedust.io/list.json";
|
|
6122
5997
|
var CACHE_TTL_MS = 10 * 60 * 1e3;
|
|
6123
5998
|
var cachedAssets = [];
|
|
@@ -6136,7 +6011,7 @@ async function getAssetList() {
|
|
|
6136
6011
|
return cachedAssets;
|
|
6137
6012
|
} catch (error) {
|
|
6138
6013
|
if (cachedAssets.length > 0) {
|
|
6139
|
-
|
|
6014
|
+
log8.warn({ err: error }, "Asset list fetch failed, using stale cache");
|
|
6140
6015
|
return cachedAssets;
|
|
6141
6016
|
}
|
|
6142
6017
|
throw error;
|
|
@@ -6189,7 +6064,7 @@ var stonApiClient = new StonApiClient();
|
|
|
6189
6064
|
function isTon(asset) {
|
|
6190
6065
|
return asset.toLowerCase() === "ton";
|
|
6191
6066
|
}
|
|
6192
|
-
async function getStonfiQuote(fromAsset, toAsset, amount, slippage,
|
|
6067
|
+
async function getStonfiQuote(fromAsset, toAsset, amount, slippage, log12) {
|
|
6193
6068
|
try {
|
|
6194
6069
|
const isTonInput = isTon(fromAsset);
|
|
6195
6070
|
const isTonOutput = isTon(toAsset);
|
|
@@ -6220,11 +6095,11 @@ async function getStonfiQuote(fromAsset, toAsset, amount, slippage, log13) {
|
|
|
6220
6095
|
fee: feeAmount.toFixed(6)
|
|
6221
6096
|
};
|
|
6222
6097
|
} catch (err) {
|
|
6223
|
-
|
|
6098
|
+
log12.debug("dex.quoteSTONfi() failed:", err);
|
|
6224
6099
|
return null;
|
|
6225
6100
|
}
|
|
6226
6101
|
}
|
|
6227
|
-
async function getDedustQuote(fromAsset, toAsset, amount, slippage,
|
|
6102
|
+
async function getDedustQuote(fromAsset, toAsset, amount, slippage, log12) {
|
|
6228
6103
|
try {
|
|
6229
6104
|
const isTonInput = isTon(fromAsset);
|
|
6230
6105
|
const isTonOutput = isTon(toAsset);
|
|
@@ -6258,7 +6133,7 @@ async function getDedustQuote(fromAsset, toAsset, amount, slippage, log13) {
|
|
|
6258
6133
|
poolType
|
|
6259
6134
|
};
|
|
6260
6135
|
} catch (err) {
|
|
6261
|
-
|
|
6136
|
+
log12.debug("dex.quoteDeDust() failed:", err);
|
|
6262
6137
|
return null;
|
|
6263
6138
|
}
|
|
6264
6139
|
}
|
|
@@ -6430,14 +6305,14 @@ function validateDexParams(amount, slippage) {
|
|
|
6430
6305
|
throw new PluginSDKError("Slippage must be between 0 and 1", "OPERATION_FAILED");
|
|
6431
6306
|
}
|
|
6432
6307
|
}
|
|
6433
|
-
function createDexSDK(
|
|
6308
|
+
function createDexSDK(log12) {
|
|
6434
6309
|
return {
|
|
6435
6310
|
async quote(params) {
|
|
6436
6311
|
validateDexParams(params.amount, params.slippage);
|
|
6437
6312
|
const slippage = params.slippage ?? 0.01;
|
|
6438
6313
|
const [stonfi, dedust] = await Promise.all([
|
|
6439
|
-
getStonfiQuote(params.fromAsset, params.toAsset, params.amount, slippage,
|
|
6440
|
-
getDedustQuote(params.fromAsset, params.toAsset, params.amount, slippage,
|
|
6314
|
+
getStonfiQuote(params.fromAsset, params.toAsset, params.amount, slippage, log12),
|
|
6315
|
+
getDedustQuote(params.fromAsset, params.toAsset, params.amount, slippage, log12)
|
|
6441
6316
|
]);
|
|
6442
6317
|
if (!stonfi && !dedust) {
|
|
6443
6318
|
throw new PluginSDKError("No DEX has liquidity for this pair", "OPERATION_FAILED");
|
|
@@ -6471,7 +6346,7 @@ function createDexSDK(log13) {
|
|
|
6471
6346
|
params.toAsset,
|
|
6472
6347
|
params.amount,
|
|
6473
6348
|
params.slippage ?? 0.01,
|
|
6474
|
-
|
|
6349
|
+
log12
|
|
6475
6350
|
);
|
|
6476
6351
|
},
|
|
6477
6352
|
async quoteDeDust(params) {
|
|
@@ -6480,25 +6355,25 @@ function createDexSDK(log13) {
|
|
|
6480
6355
|
params.toAsset,
|
|
6481
6356
|
params.amount,
|
|
6482
6357
|
params.slippage ?? 0.01,
|
|
6483
|
-
|
|
6358
|
+
log12
|
|
6484
6359
|
);
|
|
6485
6360
|
},
|
|
6486
6361
|
async swap(params) {
|
|
6487
6362
|
validateDexParams(params.amount, params.slippage);
|
|
6488
6363
|
if (params.dex === "stonfi") {
|
|
6489
|
-
return executeSTONfiSwap(params,
|
|
6364
|
+
return executeSTONfiSwap(params, log12);
|
|
6490
6365
|
}
|
|
6491
6366
|
if (params.dex === "dedust") {
|
|
6492
|
-
return executeDedustSwap(params,
|
|
6367
|
+
return executeDedustSwap(params, log12);
|
|
6493
6368
|
}
|
|
6494
6369
|
const quoteResult = await this.quote(params);
|
|
6495
|
-
return quoteResult.recommended === "stonfi" ? executeSTONfiSwap(params,
|
|
6370
|
+
return quoteResult.recommended === "stonfi" ? executeSTONfiSwap(params, log12) : executeDedustSwap(params, log12);
|
|
6496
6371
|
},
|
|
6497
6372
|
async swapSTONfi(params) {
|
|
6498
|
-
return executeSTONfiSwap(params,
|
|
6373
|
+
return executeSTONfiSwap(params, log12);
|
|
6499
6374
|
},
|
|
6500
6375
|
async swapDeDust(params) {
|
|
6501
|
-
return executeDedustSwap(params,
|
|
6376
|
+
return executeDedustSwap(params, log12);
|
|
6502
6377
|
}
|
|
6503
6378
|
};
|
|
6504
6379
|
}
|
|
@@ -6535,7 +6410,7 @@ function normalizeDomain(domain) {
|
|
|
6535
6410
|
if (!d.endsWith(".ton")) d += ".ton";
|
|
6536
6411
|
return d;
|
|
6537
6412
|
}
|
|
6538
|
-
function createDnsSDK(
|
|
6413
|
+
function createDnsSDK(log12) {
|
|
6539
6414
|
return {
|
|
6540
6415
|
async check(domain) {
|
|
6541
6416
|
const normalized = normalizeDomain(domain);
|
|
@@ -6558,7 +6433,7 @@ function createDnsSDK(log13) {
|
|
|
6558
6433
|
};
|
|
6559
6434
|
} catch (err) {
|
|
6560
6435
|
if (err instanceof PluginSDKError) throw err;
|
|
6561
|
-
|
|
6436
|
+
log12.debug("dns.check() failed:", err);
|
|
6562
6437
|
throw new PluginSDKError(
|
|
6563
6438
|
`Failed to check domain: ${err instanceof Error ? err.message : String(err)}`,
|
|
6564
6439
|
"OPERATION_FAILED"
|
|
@@ -6571,7 +6446,7 @@ function createDnsSDK(log13) {
|
|
|
6571
6446
|
const response = await tonapiFetch(`/dns/${encodeURIComponent(normalized)}`);
|
|
6572
6447
|
if (response.status === 404) return null;
|
|
6573
6448
|
if (!response.ok) {
|
|
6574
|
-
|
|
6449
|
+
log12.debug(`dns.resolve() TonAPI error: ${response.status}`);
|
|
6575
6450
|
return null;
|
|
6576
6451
|
}
|
|
6577
6452
|
const data = await response.json();
|
|
@@ -6583,7 +6458,7 @@ function createDnsSDK(log13) {
|
|
|
6583
6458
|
expirationDate: data.expiring_at || void 0
|
|
6584
6459
|
};
|
|
6585
6460
|
} catch (err) {
|
|
6586
|
-
|
|
6461
|
+
log12.debug("dns.resolve() failed:", err);
|
|
6587
6462
|
return null;
|
|
6588
6463
|
}
|
|
6589
6464
|
},
|
|
@@ -6593,7 +6468,7 @@ function createDnsSDK(log13) {
|
|
|
6593
6468
|
`/dns/auctions?tld=ton&limit=${Math.min(limit ?? 20, 100)}`
|
|
6594
6469
|
);
|
|
6595
6470
|
if (!response.ok) {
|
|
6596
|
-
|
|
6471
|
+
log12.debug(`dns.getAuctions() TonAPI error: ${response.status}`);
|
|
6597
6472
|
return [];
|
|
6598
6473
|
}
|
|
6599
6474
|
const data = await response.json();
|
|
@@ -6606,7 +6481,7 @@ function createDnsSDK(log13) {
|
|
|
6606
6481
|
bids: a.bids || 0
|
|
6607
6482
|
}));
|
|
6608
6483
|
} catch (err) {
|
|
6609
|
-
|
|
6484
|
+
log12.debug("dns.getAuctions() failed:", err);
|
|
6610
6485
|
return [];
|
|
6611
6486
|
}
|
|
6612
6487
|
},
|
|
@@ -6920,25 +6795,25 @@ function findJettonBalance(balances, jettonAddress) {
|
|
|
6920
6795
|
}
|
|
6921
6796
|
});
|
|
6922
6797
|
}
|
|
6923
|
-
function cleanupOldTransactions(db, retentionDays,
|
|
6798
|
+
function cleanupOldTransactions(db, retentionDays, log12) {
|
|
6924
6799
|
if (Math.random() > CLEANUP_PROBABILITY) return;
|
|
6925
6800
|
try {
|
|
6926
6801
|
const cutoff = Math.floor(Date.now() / 1e3) - retentionDays * 24 * 60 * 60;
|
|
6927
6802
|
const result = db.prepare("DELETE FROM used_transactions WHERE used_at < ?").run(cutoff);
|
|
6928
6803
|
if (result.changes > 0) {
|
|
6929
|
-
|
|
6804
|
+
log12.debug(`Cleaned up ${result.changes} old transaction records (>${retentionDays}d)`);
|
|
6930
6805
|
}
|
|
6931
6806
|
} catch (err) {
|
|
6932
|
-
|
|
6807
|
+
log12.error("Transaction cleanup failed:", err);
|
|
6933
6808
|
}
|
|
6934
6809
|
}
|
|
6935
|
-
function createTonSDK(
|
|
6810
|
+
function createTonSDK(log12, db) {
|
|
6936
6811
|
return {
|
|
6937
6812
|
getAddress() {
|
|
6938
6813
|
try {
|
|
6939
6814
|
return getWalletAddress();
|
|
6940
6815
|
} catch (err) {
|
|
6941
|
-
|
|
6816
|
+
log12.error("ton.getAddress() failed:", err);
|
|
6942
6817
|
return null;
|
|
6943
6818
|
}
|
|
6944
6819
|
},
|
|
@@ -6948,7 +6823,7 @@ function createTonSDK(log13, db) {
|
|
|
6948
6823
|
if (!addr) return null;
|
|
6949
6824
|
return await getWalletBalance(addr);
|
|
6950
6825
|
} catch (err) {
|
|
6951
|
-
|
|
6826
|
+
log12.error("ton.getBalance() failed:", err);
|
|
6952
6827
|
return null;
|
|
6953
6828
|
}
|
|
6954
6829
|
},
|
|
@@ -6956,7 +6831,7 @@ function createTonSDK(log13, db) {
|
|
|
6956
6831
|
try {
|
|
6957
6832
|
return await getTonPrice();
|
|
6958
6833
|
} catch (err) {
|
|
6959
|
-
|
|
6834
|
+
log12.error("ton.getPrice() failed:", err);
|
|
6960
6835
|
return null;
|
|
6961
6836
|
}
|
|
6962
6837
|
},
|
|
@@ -7007,7 +6882,7 @@ function createTonSDK(log13, db) {
|
|
|
7007
6882
|
);
|
|
7008
6883
|
return formatTransactions(transactions);
|
|
7009
6884
|
} catch (err) {
|
|
7010
|
-
|
|
6885
|
+
log12.error("ton.getTransactions() failed:", err);
|
|
7011
6886
|
return [];
|
|
7012
6887
|
}
|
|
7013
6888
|
},
|
|
@@ -7030,7 +6905,7 @@ function createTonSDK(log13, db) {
|
|
|
7030
6905
|
throw new PluginSDKError("Wallet not initialized", "WALLET_NOT_INITIALIZED");
|
|
7031
6906
|
}
|
|
7032
6907
|
const maxAgeMinutes = params.maxAgeMinutes ?? DEFAULT_MAX_AGE_MINUTES;
|
|
7033
|
-
cleanupOldTransactions(db, DEFAULT_TX_RETENTION_DAYS,
|
|
6908
|
+
cleanupOldTransactions(db, DEFAULT_TX_RETENTION_DAYS, log12);
|
|
7034
6909
|
try {
|
|
7035
6910
|
const txs = await this.getTransactions(address4, 20);
|
|
7036
6911
|
for (const tx of txs) {
|
|
@@ -7064,7 +6939,7 @@ function createTonSDK(log13, db) {
|
|
|
7064
6939
|
};
|
|
7065
6940
|
} catch (err) {
|
|
7066
6941
|
if (err instanceof PluginSDKError) throw err;
|
|
7067
|
-
|
|
6942
|
+
log12.error("ton.verifyPayment() failed:", err);
|
|
7068
6943
|
return {
|
|
7069
6944
|
verified: false,
|
|
7070
6945
|
error: `Verification failed: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -7078,7 +6953,7 @@ function createTonSDK(log13, db) {
|
|
|
7078
6953
|
if (!addr) return [];
|
|
7079
6954
|
const response = await tonapiFetch(`/accounts/${encodeURIComponent(addr)}/jettons`);
|
|
7080
6955
|
if (!response.ok) {
|
|
7081
|
-
|
|
6956
|
+
log12.error(`ton.getJettonBalances() TonAPI error: ${response.status}`);
|
|
7082
6957
|
return [];
|
|
7083
6958
|
}
|
|
7084
6959
|
const data = await response.json();
|
|
@@ -7102,7 +6977,7 @@ function createTonSDK(log13, db) {
|
|
|
7102
6977
|
}
|
|
7103
6978
|
return balances;
|
|
7104
6979
|
} catch (err) {
|
|
7105
|
-
|
|
6980
|
+
log12.error("ton.getJettonBalances() failed:", err);
|
|
7106
6981
|
return [];
|
|
7107
6982
|
}
|
|
7108
6983
|
},
|
|
@@ -7111,7 +6986,7 @@ function createTonSDK(log13, db) {
|
|
|
7111
6986
|
const response = await tonapiFetch(`/jettons/${encodeURIComponent(jettonAddress)}`);
|
|
7112
6987
|
if (response.status === 404) return null;
|
|
7113
6988
|
if (!response.ok) {
|
|
7114
|
-
|
|
6989
|
+
log12.error(`ton.getJettonInfo() TonAPI error: ${response.status}`);
|
|
7115
6990
|
return null;
|
|
7116
6991
|
}
|
|
7117
6992
|
const data = await response.json();
|
|
@@ -7129,7 +7004,7 @@ function createTonSDK(log13, db) {
|
|
|
7129
7004
|
image: data.preview || metadata.image || void 0
|
|
7130
7005
|
};
|
|
7131
7006
|
} catch (err) {
|
|
7132
|
-
|
|
7007
|
+
log12.error("ton.getJettonInfo() failed:", err);
|
|
7133
7008
|
return null;
|
|
7134
7009
|
}
|
|
7135
7010
|
},
|
|
@@ -7222,7 +7097,7 @@ function createTonSDK(log13, db) {
|
|
|
7222
7097
|
if (status === 429 || status && status >= 500) {
|
|
7223
7098
|
invalidateTonClientCache();
|
|
7224
7099
|
if (attempt < MAX_SEND_ATTEMPTS) {
|
|
7225
|
-
|
|
7100
|
+
log12.warn(
|
|
7226
7101
|
`sendJetton attempt ${attempt} failed (${status}): ${JSON.stringify(respData ?? err.message)}, retrying...`
|
|
7227
7102
|
);
|
|
7228
7103
|
await new Promise((r3) => setTimeout(r3, 1e3 * attempt));
|
|
@@ -7252,14 +7127,14 @@ function createTonSDK(log13, db) {
|
|
|
7252
7127
|
try {
|
|
7253
7128
|
const response = await tonapiFetch(`/accounts/${encodeURIComponent(ownerAddress)}/jettons`);
|
|
7254
7129
|
if (!response.ok) {
|
|
7255
|
-
|
|
7130
|
+
log12.error(`ton.getJettonWalletAddress() TonAPI error: ${response.status}`);
|
|
7256
7131
|
return null;
|
|
7257
7132
|
}
|
|
7258
7133
|
const data = await response.json();
|
|
7259
7134
|
const match = findJettonBalance(data.balances ?? [], jettonAddress);
|
|
7260
7135
|
return match ? match.wallet_address.address : null;
|
|
7261
7136
|
} catch (err) {
|
|
7262
|
-
|
|
7137
|
+
log12.error("ton.getJettonWalletAddress() failed:", err);
|
|
7263
7138
|
return null;
|
|
7264
7139
|
}
|
|
7265
7140
|
},
|
|
@@ -7436,7 +7311,7 @@ function createTonSDK(log13, db) {
|
|
|
7436
7311
|
const wallet = loadWallet();
|
|
7437
7312
|
return wallet?.publicKey ?? null;
|
|
7438
7313
|
} catch (err) {
|
|
7439
|
-
|
|
7314
|
+
log12.error("ton.getPublicKey() failed:", err);
|
|
7440
7315
|
return null;
|
|
7441
7316
|
}
|
|
7442
7317
|
},
|
|
@@ -7452,14 +7327,14 @@ function createTonSDK(log13, db) {
|
|
|
7452
7327
|
`/accounts/${encodeURIComponent(addr)}/nfts?limit=100&indirect_ownership=true`
|
|
7453
7328
|
);
|
|
7454
7329
|
if (!response.ok) {
|
|
7455
|
-
|
|
7330
|
+
log12.error(`ton.getNftItems() TonAPI error: ${response.status}`);
|
|
7456
7331
|
return [];
|
|
7457
7332
|
}
|
|
7458
7333
|
const data = await response.json();
|
|
7459
7334
|
if (!Array.isArray(data.nft_items)) return [];
|
|
7460
7335
|
return data.nft_items.filter((item) => item.trust !== "blacklist").map((item) => mapNftItem(item));
|
|
7461
7336
|
} catch (err) {
|
|
7462
|
-
|
|
7337
|
+
log12.error("ton.getNftItems() failed:", err);
|
|
7463
7338
|
return [];
|
|
7464
7339
|
}
|
|
7465
7340
|
},
|
|
@@ -7468,13 +7343,13 @@ function createTonSDK(log13, db) {
|
|
|
7468
7343
|
const response = await tonapiFetch(`/nfts/${encodeURIComponent(nftAddress)}`);
|
|
7469
7344
|
if (response.status === 404) return null;
|
|
7470
7345
|
if (!response.ok) {
|
|
7471
|
-
|
|
7346
|
+
log12.error(`ton.getNftInfo() TonAPI error: ${response.status}`);
|
|
7472
7347
|
return null;
|
|
7473
7348
|
}
|
|
7474
7349
|
const item = await response.json();
|
|
7475
7350
|
return mapNftItem(item);
|
|
7476
7351
|
} catch (err) {
|
|
7477
|
-
|
|
7352
|
+
log12.error("ton.getNftInfo() failed:", err);
|
|
7478
7353
|
return null;
|
|
7479
7354
|
}
|
|
7480
7355
|
},
|
|
@@ -7507,7 +7382,7 @@ function createTonSDK(log13, db) {
|
|
|
7507
7382
|
`/rates?tokens=${encodeURIComponent(jettonAddress)}¤cies=usd,ton`
|
|
7508
7383
|
);
|
|
7509
7384
|
if (!response.ok) {
|
|
7510
|
-
|
|
7385
|
+
log12.debug(`ton.getJettonPrice() TonAPI error: ${response.status}`);
|
|
7511
7386
|
return null;
|
|
7512
7387
|
}
|
|
7513
7388
|
const data = await response.json();
|
|
@@ -7521,7 +7396,7 @@ function createTonSDK(log13, db) {
|
|
|
7521
7396
|
change30d: rateData.diff_30d?.USD ?? null
|
|
7522
7397
|
};
|
|
7523
7398
|
} catch (err) {
|
|
7524
|
-
|
|
7399
|
+
log12.debug("ton.getJettonPrice() failed:", err);
|
|
7525
7400
|
return null;
|
|
7526
7401
|
}
|
|
7527
7402
|
},
|
|
@@ -7535,7 +7410,7 @@ function createTonSDK(log13, db) {
|
|
|
7535
7410
|
tonapiFetch(`/jettons/${encodeURIComponent(jettonAddress)}`)
|
|
7536
7411
|
]);
|
|
7537
7412
|
if (!holdersResponse.ok) {
|
|
7538
|
-
|
|
7413
|
+
log12.debug(`ton.getJettonHolders() TonAPI error: ${holdersResponse.status}`);
|
|
7539
7414
|
return [];
|
|
7540
7415
|
}
|
|
7541
7416
|
const data = await holdersResponse.json();
|
|
@@ -7555,7 +7430,7 @@ function createTonSDK(log13, db) {
|
|
|
7555
7430
|
};
|
|
7556
7431
|
});
|
|
7557
7432
|
} catch (err) {
|
|
7558
|
-
|
|
7433
|
+
log12.debug("ton.getJettonHolders() failed:", err);
|
|
7559
7434
|
return [];
|
|
7560
7435
|
}
|
|
7561
7436
|
},
|
|
@@ -7627,13 +7502,13 @@ function createTonSDK(log13, db) {
|
|
|
7627
7502
|
holders: holdersCount
|
|
7628
7503
|
};
|
|
7629
7504
|
} catch (err) {
|
|
7630
|
-
|
|
7505
|
+
log12.debug("ton.getJettonHistory() failed:", err);
|
|
7631
7506
|
return null;
|
|
7632
7507
|
}
|
|
7633
7508
|
},
|
|
7634
7509
|
// ─── Sub-namespaces ───────────────────────────────────────────
|
|
7635
|
-
dex: Object.freeze(createDexSDK(
|
|
7636
|
-
dns: Object.freeze(createDnsSDK(
|
|
7510
|
+
dex: Object.freeze(createDexSDK(log12)),
|
|
7511
|
+
dns: Object.freeze(createDnsSDK(log12))
|
|
7637
7512
|
};
|
|
7638
7513
|
}
|
|
7639
7514
|
function mapNftItem(item) {
|
|
@@ -7686,7 +7561,7 @@ async function getApi() {
|
|
|
7686
7561
|
}
|
|
7687
7562
|
|
|
7688
7563
|
// src/sdk/telegram-messages.ts
|
|
7689
|
-
function createTelegramMessagesSDK(bridge,
|
|
7564
|
+
function createTelegramMessagesSDK(bridge, log12) {
|
|
7690
7565
|
function requireBridge2() {
|
|
7691
7566
|
requireBridge(bridge);
|
|
7692
7567
|
}
|
|
@@ -7793,7 +7668,7 @@ function createTelegramMessagesSDK(bridge, log13) {
|
|
|
7793
7668
|
return (resultData.messages ?? []).map(toSimpleMessage);
|
|
7794
7669
|
} catch (err) {
|
|
7795
7670
|
if (err instanceof PluginSDKError) throw err;
|
|
7796
|
-
|
|
7671
|
+
log12.error("telegram.searchMessages() failed:", err);
|
|
7797
7672
|
return [];
|
|
7798
7673
|
}
|
|
7799
7674
|
},
|
|
@@ -8036,7 +7911,7 @@ function createTelegramMessagesSDK(bridge, log13) {
|
|
|
8036
7911
|
return messages;
|
|
8037
7912
|
} catch (err) {
|
|
8038
7913
|
if (err instanceof PluginSDKError) throw err;
|
|
8039
|
-
|
|
7914
|
+
log12.error("telegram.getScheduledMessages() failed:", err);
|
|
8040
7915
|
return [];
|
|
8041
7916
|
}
|
|
8042
7917
|
},
|
|
@@ -8097,7 +7972,7 @@ function createTelegramMessagesSDK(bridge, log13) {
|
|
|
8097
7972
|
}
|
|
8098
7973
|
|
|
8099
7974
|
// src/sdk/telegram-social.ts
|
|
8100
|
-
function createTelegramSocialSDK(bridge,
|
|
7975
|
+
function createTelegramSocialSDK(bridge, log12) {
|
|
8101
7976
|
function requireBridge2() {
|
|
8102
7977
|
requireBridge(bridge);
|
|
8103
7978
|
}
|
|
@@ -8172,7 +8047,7 @@ function createTelegramSocialSDK(bridge, log13) {
|
|
|
8172
8047
|
return null;
|
|
8173
8048
|
} catch (err) {
|
|
8174
8049
|
if (err instanceof PluginSDKError) throw err;
|
|
8175
|
-
|
|
8050
|
+
log12.error("telegram.getChatInfo() failed:", err);
|
|
8176
8051
|
return null;
|
|
8177
8052
|
}
|
|
8178
8053
|
},
|
|
@@ -8286,7 +8161,7 @@ function createTelegramSocialSDK(bridge, log13) {
|
|
|
8286
8161
|
});
|
|
8287
8162
|
} catch (err) {
|
|
8288
8163
|
if (err instanceof PluginSDKError) throw err;
|
|
8289
|
-
|
|
8164
|
+
log12.error("telegram.getParticipants() failed:", err);
|
|
8290
8165
|
return [];
|
|
8291
8166
|
}
|
|
8292
8167
|
},
|
|
@@ -8648,7 +8523,7 @@ function createTelegramSocialSDK(bridge, log13) {
|
|
|
8648
8523
|
}));
|
|
8649
8524
|
} catch (err) {
|
|
8650
8525
|
if (err instanceof PluginSDKError) throw err;
|
|
8651
|
-
|
|
8526
|
+
log12.error("telegram.getDialogs() failed:", err);
|
|
8652
8527
|
return [];
|
|
8653
8528
|
}
|
|
8654
8529
|
},
|
|
@@ -8662,7 +8537,7 @@ function createTelegramSocialSDK(bridge, log13) {
|
|
|
8662
8537
|
return messages.map(toSimpleMessage);
|
|
8663
8538
|
} catch (err) {
|
|
8664
8539
|
if (err instanceof PluginSDKError) throw err;
|
|
8665
|
-
|
|
8540
|
+
log12.error("telegram.getHistory() failed:", err);
|
|
8666
8541
|
return [];
|
|
8667
8542
|
}
|
|
8668
8543
|
},
|
|
@@ -8693,7 +8568,7 @@ function createTelegramSocialSDK(bridge, log13) {
|
|
|
8693
8568
|
}));
|
|
8694
8569
|
} catch (err) {
|
|
8695
8570
|
if (err instanceof PluginSDKError) throw err;
|
|
8696
|
-
|
|
8571
|
+
log12.error("telegram.getStarsTransactions() failed:", err);
|
|
8697
8572
|
return [];
|
|
8698
8573
|
}
|
|
8699
8574
|
},
|
|
@@ -8798,7 +8673,7 @@ function createTelegramSocialSDK(bridge, log13) {
|
|
|
8798
8673
|
};
|
|
8799
8674
|
} catch (err) {
|
|
8800
8675
|
if (err instanceof PluginSDKError) throw err;
|
|
8801
|
-
|
|
8676
|
+
log12.error("telegram.getCollectibleInfo() failed:", err);
|
|
8802
8677
|
return null;
|
|
8803
8678
|
}
|
|
8804
8679
|
},
|
|
@@ -8836,7 +8711,7 @@ function createTelegramSocialSDK(bridge, log13) {
|
|
|
8836
8711
|
} catch (err) {
|
|
8837
8712
|
if (err.errorMessage === "STARGIFT_SLUG_INVALID") return null;
|
|
8838
8713
|
if (err instanceof PluginSDKError) throw err;
|
|
8839
|
-
|
|
8714
|
+
log12.error("telegram.getUniqueGift() failed:", err);
|
|
8840
8715
|
return null;
|
|
8841
8716
|
}
|
|
8842
8717
|
},
|
|
@@ -8862,7 +8737,7 @@ function createTelegramSocialSDK(bridge, log13) {
|
|
|
8862
8737
|
} catch (err) {
|
|
8863
8738
|
if (err.errorMessage === "STARGIFT_SLUG_INVALID") return null;
|
|
8864
8739
|
if (err instanceof PluginSDKError) throw err;
|
|
8865
|
-
|
|
8740
|
+
log12.error("telegram.getUniqueGiftValue() failed:", err);
|
|
8866
8741
|
return null;
|
|
8867
8742
|
}
|
|
8868
8743
|
},
|
|
@@ -8897,13 +8772,13 @@ function createTelegramSocialSDK(bridge, log13) {
|
|
|
8897
8772
|
const client = getClient2();
|
|
8898
8773
|
const { Api: Api4, helpers } = await import("telegram");
|
|
8899
8774
|
const { CustomFile } = await import("telegram/client/uploads.js");
|
|
8900
|
-
const { readFileSync:
|
|
8775
|
+
const { readFileSync: readFileSync6, statSync: statSync2 } = await import("fs");
|
|
8901
8776
|
const { basename: basename2 } = await import("path");
|
|
8902
8777
|
const { resolve: resolve2, normalize: normalize2 } = await import("path");
|
|
8903
|
-
const { homedir:
|
|
8778
|
+
const { homedir: homedir2 } = await import("os");
|
|
8904
8779
|
const { realpathSync } = await import("fs");
|
|
8905
8780
|
const filePath = realpathSync(resolve2(normalize2(mediaPath)));
|
|
8906
|
-
const home =
|
|
8781
|
+
const home = homedir2();
|
|
8907
8782
|
const teletonWorkspace = `${home}/.teleton/workspace/`;
|
|
8908
8783
|
const allowedPrefixes = [
|
|
8909
8784
|
"/tmp/",
|
|
@@ -8922,7 +8797,7 @@ function createTelegramSocialSDK(bridge, log13) {
|
|
|
8922
8797
|
}
|
|
8923
8798
|
const fileName = basename2(filePath);
|
|
8924
8799
|
const fileSize = statSync2(filePath).size;
|
|
8925
|
-
const fileBuffer =
|
|
8800
|
+
const fileBuffer = readFileSync6(filePath);
|
|
8926
8801
|
const isVideo = filePath.toLowerCase().match(/\.(mp4|mov|avi|webm|mkv|m4v)$/);
|
|
8927
8802
|
const customFile = new CustomFile(fileName, fileSize, filePath, fileBuffer);
|
|
8928
8803
|
const uploadedFile = await client.uploadFile({
|
|
@@ -8973,7 +8848,7 @@ function createTelegramSocialSDK(bridge, log13) {
|
|
|
8973
8848
|
}
|
|
8974
8849
|
|
|
8975
8850
|
// src/sdk/telegram.ts
|
|
8976
|
-
function createTelegramSDK(bridge,
|
|
8851
|
+
function createTelegramSDK(bridge, log12) {
|
|
8977
8852
|
function requireBridge2() {
|
|
8978
8853
|
requireBridge(bridge);
|
|
8979
8854
|
}
|
|
@@ -9077,7 +8952,7 @@ function createTelegramSDK(bridge, log13) {
|
|
|
9077
8952
|
timestamp: m.timestamp
|
|
9078
8953
|
}));
|
|
9079
8954
|
} catch (err) {
|
|
9080
|
-
|
|
8955
|
+
log12.error("telegram.getMessages() failed:", err);
|
|
9081
8956
|
return [];
|
|
9082
8957
|
}
|
|
9083
8958
|
},
|
|
@@ -9099,7 +8974,7 @@ function createTelegramSDK(bridge, log13) {
|
|
|
9099
8974
|
return bridge.isAvailable();
|
|
9100
8975
|
},
|
|
9101
8976
|
getRawClient() {
|
|
9102
|
-
|
|
8977
|
+
log12.warn("getRawClient() called \u2014 this bypasses SDK sandbox guarantees");
|
|
9103
8978
|
if (!bridge.isAvailable()) return null;
|
|
9104
8979
|
try {
|
|
9105
8980
|
return bridge.getClient().getClient();
|
|
@@ -9108,23 +8983,23 @@ function createTelegramSDK(bridge, log13) {
|
|
|
9108
8983
|
}
|
|
9109
8984
|
},
|
|
9110
8985
|
// Spread extended methods from sub-modules
|
|
9111
|
-
...createTelegramMessagesSDK(bridge,
|
|
9112
|
-
...createTelegramSocialSDK(bridge,
|
|
8986
|
+
...createTelegramMessagesSDK(bridge, log12),
|
|
8987
|
+
...createTelegramSocialSDK(bridge, log12)
|
|
9113
8988
|
};
|
|
9114
8989
|
}
|
|
9115
8990
|
|
|
9116
8991
|
// src/sdk/secrets.ts
|
|
9117
|
-
import { readFileSync as
|
|
9118
|
-
import { join as
|
|
9119
|
-
var SECRETS_DIR =
|
|
8992
|
+
import { readFileSync as readFileSync3, writeFileSync, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
|
|
8993
|
+
import { join as join2 } from "path";
|
|
8994
|
+
var SECRETS_DIR = join2(TELETON_ROOT, "plugins", "data");
|
|
9120
8995
|
function getSecretsPath(pluginName) {
|
|
9121
|
-
return
|
|
8996
|
+
return join2(SECRETS_DIR, `${pluginName}.secrets.json`);
|
|
9122
8997
|
}
|
|
9123
8998
|
function readSecretsFile(pluginName) {
|
|
9124
8999
|
const filePath = getSecretsPath(pluginName);
|
|
9125
9000
|
try {
|
|
9126
|
-
if (!
|
|
9127
|
-
const raw =
|
|
9001
|
+
if (!existsSync4(filePath)) return {};
|
|
9002
|
+
const raw = readFileSync3(filePath, "utf-8");
|
|
9128
9003
|
const parsed = JSON.parse(raw);
|
|
9129
9004
|
if (typeof parsed !== "object" || parsed === null) return {};
|
|
9130
9005
|
return parsed;
|
|
@@ -9133,40 +9008,40 @@ function readSecretsFile(pluginName) {
|
|
|
9133
9008
|
}
|
|
9134
9009
|
}
|
|
9135
9010
|
function writePluginSecret(pluginName, key, value) {
|
|
9136
|
-
|
|
9011
|
+
mkdirSync2(SECRETS_DIR, { recursive: true, mode: 448 });
|
|
9137
9012
|
const filePath = getSecretsPath(pluginName);
|
|
9138
9013
|
const existing = readSecretsFile(pluginName);
|
|
9139
9014
|
existing[key] = value;
|
|
9140
|
-
|
|
9015
|
+
writeFileSync(filePath, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
9141
9016
|
}
|
|
9142
9017
|
function deletePluginSecret(pluginName, key) {
|
|
9143
9018
|
const existing = readSecretsFile(pluginName);
|
|
9144
9019
|
if (!(key in existing)) return false;
|
|
9145
9020
|
delete existing[key];
|
|
9146
9021
|
const filePath = getSecretsPath(pluginName);
|
|
9147
|
-
|
|
9022
|
+
writeFileSync(filePath, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
9148
9023
|
return true;
|
|
9149
9024
|
}
|
|
9150
9025
|
function listPluginSecretKeys(pluginName) {
|
|
9151
9026
|
return Object.keys(readSecretsFile(pluginName));
|
|
9152
9027
|
}
|
|
9153
|
-
function createSecretsSDK(pluginName, pluginConfig,
|
|
9028
|
+
function createSecretsSDK(pluginName, pluginConfig, log12) {
|
|
9154
9029
|
const envPrefix = pluginName.replace(/-/g, "_").toUpperCase();
|
|
9155
9030
|
function get(key) {
|
|
9156
9031
|
const envKey = `${envPrefix}_${key.toUpperCase()}`;
|
|
9157
9032
|
const envValue = process.env[envKey];
|
|
9158
9033
|
if (envValue) {
|
|
9159
|
-
|
|
9034
|
+
log12.debug(`Secret "${key}" resolved from env var ${envKey}`);
|
|
9160
9035
|
return envValue;
|
|
9161
9036
|
}
|
|
9162
9037
|
const stored = readSecretsFile(pluginName);
|
|
9163
9038
|
if (key in stored && stored[key]) {
|
|
9164
|
-
|
|
9039
|
+
log12.debug(`Secret "${key}" resolved from secrets store`);
|
|
9165
9040
|
return stored[key];
|
|
9166
9041
|
}
|
|
9167
9042
|
const configValue = pluginConfig[key];
|
|
9168
9043
|
if (configValue !== void 0 && configValue !== null) {
|
|
9169
|
-
|
|
9044
|
+
log12.debug(`Secret "${key}" resolved from pluginConfig`);
|
|
9170
9045
|
return String(configValue);
|
|
9171
9046
|
}
|
|
9172
9047
|
return void 0;
|
|
@@ -9268,7 +9143,7 @@ function createStorageSDK(db) {
|
|
|
9268
9143
|
}
|
|
9269
9144
|
|
|
9270
9145
|
// src/sdk/bot.ts
|
|
9271
|
-
function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLimiter,
|
|
9146
|
+
function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLimiter, log12) {
|
|
9272
9147
|
if (!router || !manifest || !manifest.inline && !manifest.callbacks) {
|
|
9273
9148
|
return null;
|
|
9274
9149
|
}
|
|
@@ -9291,7 +9166,7 @@ function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLi
|
|
|
9291
9166
|
},
|
|
9292
9167
|
onInlineQuery(handler) {
|
|
9293
9168
|
if (handlers.onInlineQuery) {
|
|
9294
|
-
|
|
9169
|
+
log12.warn("onInlineQuery called again \u2014 overwriting previous handler");
|
|
9295
9170
|
}
|
|
9296
9171
|
handlers.onInlineQuery = async (ctx) => {
|
|
9297
9172
|
if (rateLimiter) {
|
|
@@ -9337,7 +9212,7 @@ function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLi
|
|
|
9337
9212
|
return;
|
|
9338
9213
|
} catch (error) {
|
|
9339
9214
|
if (error?.errorMessage === "MESSAGE_NOT_MODIFIED") return;
|
|
9340
|
-
|
|
9215
|
+
log12.warn(`GramJS edit failed, falling back to Grammy: ${error?.errorMessage || error}`);
|
|
9341
9216
|
}
|
|
9342
9217
|
}
|
|
9343
9218
|
if (grammyBot) {
|
|
@@ -9350,7 +9225,7 @@ function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLi
|
|
|
9350
9225
|
});
|
|
9351
9226
|
} catch (error) {
|
|
9352
9227
|
if (error?.description?.includes("message is not modified")) return;
|
|
9353
|
-
|
|
9228
|
+
log12.error(`Failed to edit inline message: ${error?.description || error}`);
|
|
9354
9229
|
}
|
|
9355
9230
|
}
|
|
9356
9231
|
},
|
|
@@ -9409,13 +9284,13 @@ function createSafeDb(db) {
|
|
|
9409
9284
|
});
|
|
9410
9285
|
}
|
|
9411
9286
|
function createPluginSDK(deps, opts) {
|
|
9412
|
-
const
|
|
9287
|
+
const log12 = createLogger2(opts.pluginName);
|
|
9413
9288
|
const safeDb = opts.db ? createSafeDb(opts.db) : null;
|
|
9414
|
-
const ton = Object.freeze(createTonSDK(
|
|
9415
|
-
const telegram = Object.freeze(createTelegramSDK(deps.bridge,
|
|
9416
|
-
const secrets = Object.freeze(createSecretsSDK(opts.pluginName, opts.pluginConfig,
|
|
9289
|
+
const ton = Object.freeze(createTonSDK(log12, safeDb));
|
|
9290
|
+
const telegram = Object.freeze(createTelegramSDK(deps.bridge, log12));
|
|
9291
|
+
const secrets = Object.freeze(createSecretsSDK(opts.pluginName, opts.pluginConfig, log12));
|
|
9417
9292
|
const storage = safeDb ? Object.freeze(createStorageSDK(safeDb)) : null;
|
|
9418
|
-
const frozenLog = Object.freeze(
|
|
9293
|
+
const frozenLog = Object.freeze(log12);
|
|
9419
9294
|
const frozenConfig = Object.freeze(JSON.parse(JSON.stringify(opts.sanitizedConfig ?? {})));
|
|
9420
9295
|
const frozenPluginConfig = Object.freeze(JSON.parse(JSON.stringify(opts.pluginConfig ?? {})));
|
|
9421
9296
|
let cachedBot;
|
|
@@ -9446,20 +9321,20 @@ function createPluginSDK(deps, opts) {
|
|
|
9446
9321
|
},
|
|
9447
9322
|
on(hookName, handler, onOpts) {
|
|
9448
9323
|
if (!opts.hookRegistry) {
|
|
9449
|
-
|
|
9324
|
+
log12.warn(`Hook registration unavailable \u2014 sdk.on() ignored`);
|
|
9450
9325
|
return;
|
|
9451
9326
|
}
|
|
9452
9327
|
if (opts.declaredHooks) {
|
|
9453
9328
|
const declared = opts.declaredHooks.some((h2) => h2.name === hookName);
|
|
9454
9329
|
if (!declared) {
|
|
9455
|
-
|
|
9330
|
+
log12.warn(`Hook "${hookName}" not declared in manifest \u2014 registration rejected`);
|
|
9456
9331
|
return;
|
|
9457
9332
|
}
|
|
9458
9333
|
}
|
|
9459
9334
|
const rawPriority = Number(onOpts?.priority) || 0;
|
|
9460
9335
|
const clampedPriority = Math.max(-1e3, Math.min(1e3, rawPriority));
|
|
9461
9336
|
if (rawPriority !== clampedPriority) {
|
|
9462
|
-
|
|
9337
|
+
log12.debug(`Hook "${hookName}" priority ${rawPriority} clamped to ${clampedPriority}`);
|
|
9463
9338
|
}
|
|
9464
9339
|
const registered = opts.hookRegistry.register({
|
|
9465
9340
|
pluginId: opts.pluginName,
|
|
@@ -9469,7 +9344,7 @@ function createPluginSDK(deps, opts) {
|
|
|
9469
9344
|
globalPriority: opts.globalPriority ?? 0
|
|
9470
9345
|
});
|
|
9471
9346
|
if (!registered) {
|
|
9472
|
-
|
|
9347
|
+
log12.warn(
|
|
9473
9348
|
`Hook registration limit reached for plugin "${opts.pluginName}" \u2014 "${hookName}" rejected`
|
|
9474
9349
|
);
|
|
9475
9350
|
}
|
|
@@ -9588,24 +9463,24 @@ var HookRegistry = class {
|
|
|
9588
9463
|
|
|
9589
9464
|
// src/agent/tools/plugin-loader.ts
|
|
9590
9465
|
var execFileAsync = promisify(execFile);
|
|
9591
|
-
var
|
|
9592
|
-
var PLUGIN_DATA_DIR =
|
|
9466
|
+
var log9 = createLogger("PluginLoader");
|
|
9467
|
+
var PLUGIN_DATA_DIR = join3(TELETON_ROOT, "plugins", "data");
|
|
9593
9468
|
function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps, hookRegistry, pluginPriorities) {
|
|
9594
9469
|
let manifest = null;
|
|
9595
9470
|
if (raw.manifest) {
|
|
9596
9471
|
try {
|
|
9597
9472
|
manifest = validateManifest(raw.manifest);
|
|
9598
9473
|
} catch (err) {
|
|
9599
|
-
|
|
9474
|
+
log9.warn(
|
|
9600
9475
|
`[${entryName}] invalid manifest, ignoring: ${err instanceof Error ? err.message : err}`
|
|
9601
9476
|
);
|
|
9602
9477
|
}
|
|
9603
9478
|
}
|
|
9604
9479
|
if (!manifest) {
|
|
9605
|
-
const manifestPath =
|
|
9480
|
+
const manifestPath = join3(WORKSPACE_PATHS.PLUGINS_DIR, entryName, "manifest.json");
|
|
9606
9481
|
try {
|
|
9607
|
-
if (
|
|
9608
|
-
const diskManifest = JSON.parse(
|
|
9482
|
+
if (existsSync5(manifestPath)) {
|
|
9483
|
+
const diskManifest = JSON.parse(readFileSync4(manifestPath, "utf-8"));
|
|
9609
9484
|
if (diskManifest && typeof diskManifest.version === "string") {
|
|
9610
9485
|
manifest = {
|
|
9611
9486
|
name: entryName,
|
|
@@ -9680,7 +9555,7 @@ function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps, hookReg
|
|
|
9680
9555
|
},
|
|
9681
9556
|
migrate() {
|
|
9682
9557
|
try {
|
|
9683
|
-
const dbPath =
|
|
9558
|
+
const dbPath = join3(PLUGIN_DATA_DIR, `${pluginName}.db`);
|
|
9684
9559
|
pluginDb = openModuleDb(dbPath);
|
|
9685
9560
|
if (hasMigrate) {
|
|
9686
9561
|
raw.migrate?.(pluginDb);
|
|
@@ -9785,36 +9660,36 @@ function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps, hookReg
|
|
|
9785
9660
|
return module;
|
|
9786
9661
|
}
|
|
9787
9662
|
async function ensurePluginDeps(pluginDir, pluginEntry) {
|
|
9788
|
-
const pkgJson =
|
|
9789
|
-
const lockfile =
|
|
9790
|
-
const nodeModules =
|
|
9791
|
-
if (!
|
|
9792
|
-
if (!
|
|
9793
|
-
|
|
9663
|
+
const pkgJson = join3(pluginDir, "package.json");
|
|
9664
|
+
const lockfile = join3(pluginDir, "package-lock.json");
|
|
9665
|
+
const nodeModules = join3(pluginDir, "node_modules");
|
|
9666
|
+
if (!existsSync5(pkgJson)) return;
|
|
9667
|
+
if (!existsSync5(lockfile)) {
|
|
9668
|
+
log9.warn(
|
|
9794
9669
|
`[${pluginEntry}] package.json without package-lock.json \u2014 skipping (lockfile required)`
|
|
9795
9670
|
);
|
|
9796
9671
|
return;
|
|
9797
9672
|
}
|
|
9798
|
-
if (
|
|
9799
|
-
const marker =
|
|
9800
|
-
if (
|
|
9673
|
+
if (existsSync5(nodeModules)) {
|
|
9674
|
+
const marker = join3(nodeModules, ".package-lock.json");
|
|
9675
|
+
if (existsSync5(marker) && statSync(marker).mtimeMs >= statSync(lockfile).mtimeMs) return;
|
|
9801
9676
|
}
|
|
9802
|
-
|
|
9677
|
+
log9.info(`[${pluginEntry}] Installing dependencies...`);
|
|
9803
9678
|
try {
|
|
9804
9679
|
await execFileAsync("npm", ["ci", "--ignore-scripts", "--no-audit", "--no-fund"], {
|
|
9805
9680
|
cwd: pluginDir,
|
|
9806
9681
|
timeout: 6e4,
|
|
9807
9682
|
env: { ...process.env, NODE_ENV: "production" }
|
|
9808
9683
|
});
|
|
9809
|
-
|
|
9684
|
+
log9.info(`[${pluginEntry}] Dependencies installed`);
|
|
9810
9685
|
} catch (err) {
|
|
9811
|
-
|
|
9686
|
+
log9.error(`[${pluginEntry}] Failed to install deps: ${String(err).slice(0, 300)}`);
|
|
9812
9687
|
}
|
|
9813
9688
|
}
|
|
9814
9689
|
async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
|
|
9815
9690
|
const hookRegistry = new HookRegistry();
|
|
9816
9691
|
const pluginsDir = WORKSPACE_PATHS.PLUGINS_DIR;
|
|
9817
|
-
if (!
|
|
9692
|
+
if (!existsSync5(pluginsDir)) {
|
|
9818
9693
|
return { modules: [], hookRegistry };
|
|
9819
9694
|
}
|
|
9820
9695
|
let pluginPriorities = /* @__PURE__ */ new Map();
|
|
@@ -9830,15 +9705,15 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
|
|
|
9830
9705
|
const pluginPaths = [];
|
|
9831
9706
|
for (const entry of entries) {
|
|
9832
9707
|
if (entry === "data") continue;
|
|
9833
|
-
const entryPath =
|
|
9708
|
+
const entryPath = join3(pluginsDir, entry);
|
|
9834
9709
|
let modulePath = null;
|
|
9835
9710
|
try {
|
|
9836
9711
|
const stat = statSync(entryPath);
|
|
9837
9712
|
if (stat.isFile() && entry.endsWith(".js")) {
|
|
9838
9713
|
modulePath = entryPath;
|
|
9839
9714
|
} else if (stat.isDirectory()) {
|
|
9840
|
-
const indexPath =
|
|
9841
|
-
if (
|
|
9715
|
+
const indexPath = join3(entryPath, "index.js");
|
|
9716
|
+
if (existsSync5(indexPath)) {
|
|
9842
9717
|
modulePath = indexPath;
|
|
9843
9718
|
}
|
|
9844
9719
|
}
|
|
@@ -9850,7 +9725,7 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
|
|
|
9850
9725
|
}
|
|
9851
9726
|
}
|
|
9852
9727
|
await Promise.allSettled(
|
|
9853
|
-
pluginPaths.filter(({ path }) => path.endsWith("index.js")).map(({ entry }) => ensurePluginDeps(
|
|
9728
|
+
pluginPaths.filter(({ path }) => path.endsWith("index.js")).map(({ entry }) => ensurePluginDeps(join3(pluginsDir, entry), entry))
|
|
9854
9729
|
);
|
|
9855
9730
|
const loadResults = await Promise.allSettled(
|
|
9856
9731
|
pluginPaths.map(async ({ entry, path }) => {
|
|
@@ -9861,7 +9736,7 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
|
|
|
9861
9736
|
);
|
|
9862
9737
|
for (const result of loadResults) {
|
|
9863
9738
|
if (result.status === "rejected") {
|
|
9864
|
-
|
|
9739
|
+
log9.error(
|
|
9865
9740
|
`Plugin failed to load: ${result.reason instanceof Error ? result.reason.message : result.reason}`
|
|
9866
9741
|
);
|
|
9867
9742
|
continue;
|
|
@@ -9869,7 +9744,7 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
|
|
|
9869
9744
|
const { entry, mod } = result.value;
|
|
9870
9745
|
try {
|
|
9871
9746
|
if (!mod.tools || typeof mod.tools !== "function" && !Array.isArray(mod.tools)) {
|
|
9872
|
-
|
|
9747
|
+
log9.warn(`Plugin "${entry}": no 'tools' array or function exported, skipping`);
|
|
9873
9748
|
continue;
|
|
9874
9749
|
}
|
|
9875
9750
|
const adapted = adaptPlugin(
|
|
@@ -9882,679 +9757,35 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
|
|
|
9882
9757
|
pluginPriorities
|
|
9883
9758
|
);
|
|
9884
9759
|
if (loadedNames.has(adapted.name)) {
|
|
9885
|
-
|
|
9760
|
+
log9.warn(`Plugin "${adapted.name}" already loaded, skipping duplicate from "${entry}"`);
|
|
9886
9761
|
continue;
|
|
9887
9762
|
}
|
|
9888
9763
|
loadedNames.add(adapted.name);
|
|
9889
9764
|
modules.push(adapted);
|
|
9890
9765
|
} catch (err) {
|
|
9891
|
-
|
|
9766
|
+
log9.error(`Plugin "${entry}" failed to adapt: ${err instanceof Error ? err.message : err}`);
|
|
9892
9767
|
}
|
|
9893
9768
|
}
|
|
9894
9769
|
return { modules, hookRegistry };
|
|
9895
9770
|
}
|
|
9896
9771
|
|
|
9897
|
-
// src/config/configurable-keys.ts
|
|
9898
|
-
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync7 } from "fs";
|
|
9899
|
-
import { parse as parse2, stringify as stringify2 } from "yaml";
|
|
9900
|
-
var noValidation = () => void 0;
|
|
9901
|
-
var identity = (v) => v;
|
|
9902
|
-
var nonEmpty = (v) => v.length > 0 ? void 0 : "Must not be empty";
|
|
9903
|
-
function numberInRange(min, max) {
|
|
9904
|
-
return (v) => {
|
|
9905
|
-
const n2 = Number(v);
|
|
9906
|
-
if (isNaN(n2)) return "Must be a number";
|
|
9907
|
-
if (n2 < min || n2 > max) return `Must be between ${min} and ${max}`;
|
|
9908
|
-
return void 0;
|
|
9909
|
-
};
|
|
9910
|
-
}
|
|
9911
|
-
function enumValidator(options) {
|
|
9912
|
-
return (v) => options.includes(v) ? void 0 : `Must be one of: ${options.join(", ")}`;
|
|
9913
|
-
}
|
|
9914
|
-
function positiveInteger(v) {
|
|
9915
|
-
const n2 = Number(v);
|
|
9916
|
-
if (!Number.isInteger(n2) || n2 <= 0) return "Must be a positive integer";
|
|
9917
|
-
return void 0;
|
|
9918
|
-
}
|
|
9919
|
-
function validateUrl(v) {
|
|
9920
|
-
if (v === "") return void 0;
|
|
9921
|
-
if (v.startsWith("http://") || v.startsWith("https://")) return void 0;
|
|
9922
|
-
return "Must be empty or start with http:// or https://";
|
|
9923
|
-
}
|
|
9924
|
-
var CONFIGURABLE_KEYS = {
|
|
9925
|
-
// ─── API Keys ──────────────────────────────────────────────────────
|
|
9926
|
-
"agent.api_key": {
|
|
9927
|
-
type: "string",
|
|
9928
|
-
category: "API Keys",
|
|
9929
|
-
label: "LLM API Key",
|
|
9930
|
-
description: "LLM provider API key",
|
|
9931
|
-
sensitive: true,
|
|
9932
|
-
hotReload: "instant",
|
|
9933
|
-
validate: (v) => v.length >= 10 ? void 0 : "Must be at least 10 characters",
|
|
9934
|
-
mask: (v) => v.slice(0, 8) + "****",
|
|
9935
|
-
parse: identity
|
|
9936
|
-
},
|
|
9937
|
-
tavily_api_key: {
|
|
9938
|
-
type: "string",
|
|
9939
|
-
category: "API Keys",
|
|
9940
|
-
label: "Tavily API Key",
|
|
9941
|
-
description: "Tavily API key for web search",
|
|
9942
|
-
sensitive: true,
|
|
9943
|
-
hotReload: "instant",
|
|
9944
|
-
validate: (v) => v.startsWith("tvly-") ? void 0 : "Must start with 'tvly-'",
|
|
9945
|
-
mask: (v) => v.slice(0, 9) + "****",
|
|
9946
|
-
parse: identity
|
|
9947
|
-
},
|
|
9948
|
-
tonapi_key: {
|
|
9949
|
-
type: "string",
|
|
9950
|
-
category: "API Keys",
|
|
9951
|
-
label: "TonAPI Key",
|
|
9952
|
-
description: "TonAPI key for higher rate limits",
|
|
9953
|
-
sensitive: true,
|
|
9954
|
-
hotReload: "instant",
|
|
9955
|
-
validate: (v) => v.length >= 10 ? void 0 : "Must be at least 10 characters",
|
|
9956
|
-
mask: (v) => v.slice(0, 10) + "****",
|
|
9957
|
-
parse: identity
|
|
9958
|
-
},
|
|
9959
|
-
toncenter_api_key: {
|
|
9960
|
-
type: "string",
|
|
9961
|
-
category: "API Keys",
|
|
9962
|
-
label: "TonCenter API Key",
|
|
9963
|
-
description: "TonCenter API key for dedicated RPC endpoint (free at toncenter.com)",
|
|
9964
|
-
sensitive: true,
|
|
9965
|
-
hotReload: "instant",
|
|
9966
|
-
validate: (v) => v.length >= 10 ? void 0 : "Must be at least 10 characters",
|
|
9967
|
-
mask: (v) => v.slice(0, 10) + "****",
|
|
9968
|
-
parse: identity
|
|
9969
|
-
},
|
|
9970
|
-
"telegram.bot_token": {
|
|
9971
|
-
type: "string",
|
|
9972
|
-
category: "API Keys",
|
|
9973
|
-
label: "Bot Token",
|
|
9974
|
-
description: "Bot token from @BotFather",
|
|
9975
|
-
sensitive: true,
|
|
9976
|
-
hotReload: "instant",
|
|
9977
|
-
validate: (v) => v.includes(":") ? void 0 : "Must contain ':' (e.g., 123456:ABC...)",
|
|
9978
|
-
mask: (v) => v.split(":")[0] + ":****",
|
|
9979
|
-
parse: identity
|
|
9980
|
-
},
|
|
9981
|
-
// ─── Agent ─────────────────────────────────────────────────────────
|
|
9982
|
-
"agent.provider": {
|
|
9983
|
-
type: "enum",
|
|
9984
|
-
category: "Agent",
|
|
9985
|
-
label: "Provider",
|
|
9986
|
-
description: "LLM provider",
|
|
9987
|
-
sensitive: false,
|
|
9988
|
-
hotReload: "instant",
|
|
9989
|
-
options: getSupportedProviders().map((p2) => p2.id),
|
|
9990
|
-
validate: enumValidator(getSupportedProviders().map((p2) => p2.id)),
|
|
9991
|
-
mask: identity,
|
|
9992
|
-
parse: identity
|
|
9993
|
-
},
|
|
9994
|
-
"agent.model": {
|
|
9995
|
-
type: "string",
|
|
9996
|
-
category: "Agent",
|
|
9997
|
-
label: "Model",
|
|
9998
|
-
description: "Main LLM model ID",
|
|
9999
|
-
sensitive: false,
|
|
10000
|
-
hotReload: "instant",
|
|
10001
|
-
validate: nonEmpty,
|
|
10002
|
-
mask: identity,
|
|
10003
|
-
parse: identity
|
|
10004
|
-
},
|
|
10005
|
-
"agent.utility_model": {
|
|
10006
|
-
type: "string",
|
|
10007
|
-
category: "Agent",
|
|
10008
|
-
label: "Utility Model",
|
|
10009
|
-
description: "Cheap model for summarization (auto-detected if empty)",
|
|
10010
|
-
sensitive: false,
|
|
10011
|
-
hotReload: "instant",
|
|
10012
|
-
validate: noValidation,
|
|
10013
|
-
mask: identity,
|
|
10014
|
-
parse: identity
|
|
10015
|
-
},
|
|
10016
|
-
"agent.temperature": {
|
|
10017
|
-
type: "number",
|
|
10018
|
-
category: "Agent",
|
|
10019
|
-
label: "Temperature",
|
|
10020
|
-
description: "Response creativity (0.0 = deterministic, 2.0 = max)",
|
|
10021
|
-
sensitive: false,
|
|
10022
|
-
hotReload: "instant",
|
|
10023
|
-
validate: numberInRange(0, 2),
|
|
10024
|
-
mask: identity,
|
|
10025
|
-
parse: (v) => Number(v)
|
|
10026
|
-
},
|
|
10027
|
-
"agent.max_tokens": {
|
|
10028
|
-
type: "number",
|
|
10029
|
-
category: "Agent",
|
|
10030
|
-
label: "Max Tokens",
|
|
10031
|
-
description: "Maximum response length in tokens",
|
|
10032
|
-
sensitive: false,
|
|
10033
|
-
hotReload: "instant",
|
|
10034
|
-
validate: numberInRange(256, 128e3),
|
|
10035
|
-
mask: identity,
|
|
10036
|
-
parse: (v) => Number(v)
|
|
10037
|
-
},
|
|
10038
|
-
"agent.max_agentic_iterations": {
|
|
10039
|
-
type: "number",
|
|
10040
|
-
category: "Agent",
|
|
10041
|
-
label: "Max Iterations",
|
|
10042
|
-
description: "Max tool-call loop iterations per message",
|
|
10043
|
-
sensitive: false,
|
|
10044
|
-
hotReload: "instant",
|
|
10045
|
-
validate: numberInRange(1, 20),
|
|
10046
|
-
mask: identity,
|
|
10047
|
-
parse: (v) => Number(v)
|
|
10048
|
-
},
|
|
10049
|
-
"agent.base_url": {
|
|
10050
|
-
type: "string",
|
|
10051
|
-
category: "Agent",
|
|
10052
|
-
label: "API Base URL",
|
|
10053
|
-
description: "Base URL for local LLM server (requires restart)",
|
|
10054
|
-
sensitive: false,
|
|
10055
|
-
hotReload: "restart",
|
|
10056
|
-
validate: validateUrl,
|
|
10057
|
-
mask: identity,
|
|
10058
|
-
parse: identity
|
|
10059
|
-
},
|
|
10060
|
-
"cocoon.port": {
|
|
10061
|
-
type: "number",
|
|
10062
|
-
category: "Agent",
|
|
10063
|
-
label: "Cocoon Port",
|
|
10064
|
-
description: "Cocoon proxy port (requires restart)",
|
|
10065
|
-
sensitive: false,
|
|
10066
|
-
hotReload: "restart",
|
|
10067
|
-
validate: numberInRange(1, 65535),
|
|
10068
|
-
mask: identity,
|
|
10069
|
-
parse: (v) => Number(v)
|
|
10070
|
-
},
|
|
10071
|
-
// ─── Session ───────────────────────────────────────────────────
|
|
10072
|
-
"agent.session_reset_policy.daily_reset_enabled": {
|
|
10073
|
-
type: "boolean",
|
|
10074
|
-
category: "Session",
|
|
10075
|
-
label: "Daily Reset",
|
|
10076
|
-
description: "Enable daily session reset at specified hour",
|
|
10077
|
-
sensitive: false,
|
|
10078
|
-
hotReload: "instant",
|
|
10079
|
-
validate: enumValidator(["true", "false"]),
|
|
10080
|
-
mask: identity,
|
|
10081
|
-
parse: (v) => v === "true"
|
|
10082
|
-
},
|
|
10083
|
-
"agent.session_reset_policy.daily_reset_hour": {
|
|
10084
|
-
type: "number",
|
|
10085
|
-
category: "Session",
|
|
10086
|
-
label: "Reset Hour",
|
|
10087
|
-
description: "Hour (0-23 UTC) for daily session reset",
|
|
10088
|
-
sensitive: false,
|
|
10089
|
-
hotReload: "instant",
|
|
10090
|
-
validate: numberInRange(0, 23),
|
|
10091
|
-
mask: identity,
|
|
10092
|
-
parse: (v) => Number(v)
|
|
10093
|
-
},
|
|
10094
|
-
"agent.session_reset_policy.idle_expiry_enabled": {
|
|
10095
|
-
type: "boolean",
|
|
10096
|
-
category: "Session",
|
|
10097
|
-
label: "Idle Expiry",
|
|
10098
|
-
description: "Enable automatic session expiry after idle period",
|
|
10099
|
-
sensitive: false,
|
|
10100
|
-
hotReload: "instant",
|
|
10101
|
-
validate: enumValidator(["true", "false"]),
|
|
10102
|
-
mask: identity,
|
|
10103
|
-
parse: (v) => v === "true"
|
|
10104
|
-
},
|
|
10105
|
-
"agent.session_reset_policy.idle_expiry_minutes": {
|
|
10106
|
-
type: "number",
|
|
10107
|
-
category: "Session",
|
|
10108
|
-
label: "Idle Minutes",
|
|
10109
|
-
description: "Idle minutes before session expires (minimum 1)",
|
|
10110
|
-
sensitive: false,
|
|
10111
|
-
hotReload: "instant",
|
|
10112
|
-
validate: numberInRange(1, Number.MAX_SAFE_INTEGER),
|
|
10113
|
-
mask: identity,
|
|
10114
|
-
parse: (v) => Number(v)
|
|
10115
|
-
},
|
|
10116
|
-
// ─── Telegram ──────────────────────────────────────────────────────
|
|
10117
|
-
"telegram.bot_username": {
|
|
10118
|
-
type: "string",
|
|
10119
|
-
category: "Telegram",
|
|
10120
|
-
label: "Bot Username",
|
|
10121
|
-
description: "Bot username without @",
|
|
10122
|
-
sensitive: false,
|
|
10123
|
-
hotReload: "instant",
|
|
10124
|
-
validate: (v) => v.length >= 3 ? void 0 : "Must be at least 3 characters",
|
|
10125
|
-
mask: identity,
|
|
10126
|
-
parse: identity
|
|
10127
|
-
},
|
|
10128
|
-
"telegram.dm_policy": {
|
|
10129
|
-
type: "enum",
|
|
10130
|
-
category: "Telegram",
|
|
10131
|
-
label: "DM Policy",
|
|
10132
|
-
description: "Who can message the bot in private",
|
|
10133
|
-
sensitive: false,
|
|
10134
|
-
hotReload: "instant",
|
|
10135
|
-
options: ["admin-only", "allowlist", "open", "disabled"],
|
|
10136
|
-
optionLabels: {
|
|
10137
|
-
"admin-only": "Admin Only",
|
|
10138
|
-
allowlist: "Allow Users",
|
|
10139
|
-
open: "Open",
|
|
10140
|
-
disabled: "Disabled"
|
|
10141
|
-
},
|
|
10142
|
-
validate: enumValidator(["open", "allowlist", "admin-only", "disabled"]),
|
|
10143
|
-
mask: identity,
|
|
10144
|
-
parse: identity
|
|
10145
|
-
},
|
|
10146
|
-
"telegram.group_policy": {
|
|
10147
|
-
type: "enum",
|
|
10148
|
-
category: "Telegram",
|
|
10149
|
-
label: "Group Policy",
|
|
10150
|
-
description: "Which groups the bot can respond in",
|
|
10151
|
-
sensitive: false,
|
|
10152
|
-
hotReload: "instant",
|
|
10153
|
-
options: ["open", "allowlist", "admin-only", "disabled"],
|
|
10154
|
-
optionLabels: {
|
|
10155
|
-
open: "Open",
|
|
10156
|
-
allowlist: "Allow Groups",
|
|
10157
|
-
"admin-only": "Admin Only",
|
|
10158
|
-
disabled: "Disabled"
|
|
10159
|
-
},
|
|
10160
|
-
validate: enumValidator(["open", "allowlist", "admin-only", "disabled"]),
|
|
10161
|
-
mask: identity,
|
|
10162
|
-
parse: identity
|
|
10163
|
-
},
|
|
10164
|
-
"telegram.require_mention": {
|
|
10165
|
-
type: "boolean",
|
|
10166
|
-
category: "Telegram",
|
|
10167
|
-
label: "Require Mention",
|
|
10168
|
-
description: "Require @mention in groups to respond",
|
|
10169
|
-
sensitive: false,
|
|
10170
|
-
hotReload: "instant",
|
|
10171
|
-
validate: enumValidator(["true", "false"]),
|
|
10172
|
-
mask: identity,
|
|
10173
|
-
parse: (v) => v === "true"
|
|
10174
|
-
},
|
|
10175
|
-
"telegram.owner_name": {
|
|
10176
|
-
type: "string",
|
|
10177
|
-
category: "Telegram",
|
|
10178
|
-
label: "Owner Name",
|
|
10179
|
-
description: "Owner's first name (used in system prompt)",
|
|
10180
|
-
sensitive: false,
|
|
10181
|
-
hotReload: "instant",
|
|
10182
|
-
validate: noValidation,
|
|
10183
|
-
mask: identity,
|
|
10184
|
-
parse: identity
|
|
10185
|
-
},
|
|
10186
|
-
"telegram.owner_username": {
|
|
10187
|
-
type: "string",
|
|
10188
|
-
category: "Telegram",
|
|
10189
|
-
label: "Owner Username",
|
|
10190
|
-
description: "Owner's Telegram username (without @)",
|
|
10191
|
-
sensitive: false,
|
|
10192
|
-
hotReload: "instant",
|
|
10193
|
-
validate: noValidation,
|
|
10194
|
-
mask: identity,
|
|
10195
|
-
parse: identity
|
|
10196
|
-
},
|
|
10197
|
-
"telegram.debounce_ms": {
|
|
10198
|
-
type: "number",
|
|
10199
|
-
category: "Telegram",
|
|
10200
|
-
label: "Debounce (ms)",
|
|
10201
|
-
description: "Group message debounce delay in ms (0 = disabled)",
|
|
10202
|
-
sensitive: false,
|
|
10203
|
-
hotReload: "instant",
|
|
10204
|
-
validate: numberInRange(0, 1e4),
|
|
10205
|
-
mask: identity,
|
|
10206
|
-
parse: (v) => Number(v)
|
|
10207
|
-
},
|
|
10208
|
-
"telegram.agent_channel": {
|
|
10209
|
-
type: "string",
|
|
10210
|
-
category: "Telegram",
|
|
10211
|
-
label: "Agent Channel",
|
|
10212
|
-
description: "Channel username for auto-publishing",
|
|
10213
|
-
sensitive: false,
|
|
10214
|
-
hotReload: "instant",
|
|
10215
|
-
validate: noValidation,
|
|
10216
|
-
mask: identity,
|
|
10217
|
-
parse: identity
|
|
10218
|
-
},
|
|
10219
|
-
"telegram.typing_simulation": {
|
|
10220
|
-
type: "boolean",
|
|
10221
|
-
category: "Telegram",
|
|
10222
|
-
label: "Typing Simulation",
|
|
10223
|
-
description: "Simulate typing indicator before sending replies",
|
|
10224
|
-
sensitive: false,
|
|
10225
|
-
hotReload: "instant",
|
|
10226
|
-
validate: enumValidator(["true", "false"]),
|
|
10227
|
-
mask: identity,
|
|
10228
|
-
parse: (v) => v === "true"
|
|
10229
|
-
},
|
|
10230
|
-
"telegram.owner_id": {
|
|
10231
|
-
type: "number",
|
|
10232
|
-
category: "Telegram",
|
|
10233
|
-
label: "Admin ID",
|
|
10234
|
-
description: "Primary admin Telegram user ID (auto-added to Admin IDs)",
|
|
10235
|
-
sensitive: false,
|
|
10236
|
-
hotReload: "instant",
|
|
10237
|
-
validate: positiveInteger,
|
|
10238
|
-
mask: identity,
|
|
10239
|
-
parse: (v) => Number(v)
|
|
10240
|
-
},
|
|
10241
|
-
"telegram.max_message_length": {
|
|
10242
|
-
type: "number",
|
|
10243
|
-
category: "Telegram",
|
|
10244
|
-
label: "Max Message Length",
|
|
10245
|
-
description: "Maximum message length in characters",
|
|
10246
|
-
sensitive: false,
|
|
10247
|
-
hotReload: "instant",
|
|
10248
|
-
validate: numberInRange(1, 32768),
|
|
10249
|
-
mask: identity,
|
|
10250
|
-
parse: (v) => Number(v)
|
|
10251
|
-
},
|
|
10252
|
-
"telegram.rate_limit_messages_per_second": {
|
|
10253
|
-
type: "number",
|
|
10254
|
-
category: "Telegram",
|
|
10255
|
-
label: "Rate Limit \u2014 Messages/sec",
|
|
10256
|
-
description: "Rate limit: messages per second (requires restart)",
|
|
10257
|
-
sensitive: false,
|
|
10258
|
-
hotReload: "restart",
|
|
10259
|
-
validate: numberInRange(0.1, 10),
|
|
10260
|
-
mask: identity,
|
|
10261
|
-
parse: (v) => Number(v)
|
|
10262
|
-
},
|
|
10263
|
-
"telegram.rate_limit_groups_per_minute": {
|
|
10264
|
-
type: "number",
|
|
10265
|
-
category: "Telegram",
|
|
10266
|
-
label: "Rate Limit \u2014 Groups/min",
|
|
10267
|
-
description: "Rate limit: groups per minute (requires restart)",
|
|
10268
|
-
sensitive: false,
|
|
10269
|
-
hotReload: "restart",
|
|
10270
|
-
validate: numberInRange(1, 60),
|
|
10271
|
-
mask: identity,
|
|
10272
|
-
parse: (v) => Number(v)
|
|
10273
|
-
},
|
|
10274
|
-
"telegram.admin_ids": {
|
|
10275
|
-
type: "array",
|
|
10276
|
-
itemType: "number",
|
|
10277
|
-
category: "Telegram",
|
|
10278
|
-
label: "Admin IDs",
|
|
10279
|
-
description: "Admin user IDs with elevated access",
|
|
10280
|
-
sensitive: false,
|
|
10281
|
-
hotReload: "instant",
|
|
10282
|
-
validate: positiveInteger,
|
|
10283
|
-
mask: identity,
|
|
10284
|
-
parse: (v) => Number(v)
|
|
10285
|
-
},
|
|
10286
|
-
"telegram.allow_from": {
|
|
10287
|
-
type: "array",
|
|
10288
|
-
itemType: "number",
|
|
10289
|
-
category: "Telegram",
|
|
10290
|
-
label: "Allowed Users",
|
|
10291
|
-
description: "User IDs allowed for DM access",
|
|
10292
|
-
sensitive: false,
|
|
10293
|
-
hotReload: "instant",
|
|
10294
|
-
validate: positiveInteger,
|
|
10295
|
-
mask: identity,
|
|
10296
|
-
parse: (v) => Number(v)
|
|
10297
|
-
},
|
|
10298
|
-
"telegram.group_allow_from": {
|
|
10299
|
-
type: "array",
|
|
10300
|
-
itemType: "number",
|
|
10301
|
-
category: "Telegram",
|
|
10302
|
-
label: "Allowed Groups",
|
|
10303
|
-
description: "Group IDs allowed for group access",
|
|
10304
|
-
sensitive: false,
|
|
10305
|
-
hotReload: "instant",
|
|
10306
|
-
validate: positiveInteger,
|
|
10307
|
-
mask: identity,
|
|
10308
|
-
parse: (v) => Number(v)
|
|
10309
|
-
},
|
|
10310
|
-
// ─── Embedding ─────────────────────────────────────────────────────
|
|
10311
|
-
"embedding.provider": {
|
|
10312
|
-
type: "enum",
|
|
10313
|
-
category: "Embedding",
|
|
10314
|
-
label: "Embedding Provider",
|
|
10315
|
-
description: "Embedding provider for RAG",
|
|
10316
|
-
sensitive: false,
|
|
10317
|
-
hotReload: "instant",
|
|
10318
|
-
options: ["local", "anthropic", "none"],
|
|
10319
|
-
validate: enumValidator(["local", "anthropic", "none"]),
|
|
10320
|
-
mask: identity,
|
|
10321
|
-
parse: identity
|
|
10322
|
-
},
|
|
10323
|
-
"embedding.model": {
|
|
10324
|
-
type: "string",
|
|
10325
|
-
category: "Embedding",
|
|
10326
|
-
label: "Embedding Model",
|
|
10327
|
-
description: "Embedding model ID (requires restart)",
|
|
10328
|
-
sensitive: false,
|
|
10329
|
-
hotReload: "restart",
|
|
10330
|
-
validate: noValidation,
|
|
10331
|
-
mask: identity,
|
|
10332
|
-
parse: identity
|
|
10333
|
-
},
|
|
10334
|
-
// ─── WebUI ─────────────────────────────────────────────────────────
|
|
10335
|
-
"webui.port": {
|
|
10336
|
-
type: "number",
|
|
10337
|
-
category: "WebUI",
|
|
10338
|
-
label: "WebUI Port",
|
|
10339
|
-
description: "HTTP server port (requires restart)",
|
|
10340
|
-
sensitive: false,
|
|
10341
|
-
hotReload: "restart",
|
|
10342
|
-
validate: numberInRange(1024, 65535),
|
|
10343
|
-
mask: identity,
|
|
10344
|
-
parse: (v) => Number(v)
|
|
10345
|
-
},
|
|
10346
|
-
"webui.log_requests": {
|
|
10347
|
-
type: "boolean",
|
|
10348
|
-
category: "WebUI",
|
|
10349
|
-
label: "Log HTTP Requests",
|
|
10350
|
-
description: "Log all HTTP requests to console",
|
|
10351
|
-
sensitive: false,
|
|
10352
|
-
hotReload: "instant",
|
|
10353
|
-
validate: enumValidator(["true", "false"]),
|
|
10354
|
-
mask: identity,
|
|
10355
|
-
parse: (v) => v === "true"
|
|
10356
|
-
},
|
|
10357
|
-
// ─── Deals ─────────────────────────────────────────────────────────
|
|
10358
|
-
"deals.enabled": {
|
|
10359
|
-
type: "boolean",
|
|
10360
|
-
category: "Deals",
|
|
10361
|
-
label: "Deals Enabled",
|
|
10362
|
-
description: "Enable the deals/escrow module",
|
|
10363
|
-
sensitive: false,
|
|
10364
|
-
hotReload: "instant",
|
|
10365
|
-
validate: enumValidator(["true", "false"]),
|
|
10366
|
-
mask: identity,
|
|
10367
|
-
parse: (v) => v === "true"
|
|
10368
|
-
},
|
|
10369
|
-
"deals.expiry_seconds": {
|
|
10370
|
-
type: "number",
|
|
10371
|
-
category: "Deals",
|
|
10372
|
-
label: "Deal Expiry",
|
|
10373
|
-
description: "Deal expiry timeout in seconds",
|
|
10374
|
-
sensitive: false,
|
|
10375
|
-
hotReload: "instant",
|
|
10376
|
-
validate: numberInRange(10, 3600),
|
|
10377
|
-
mask: identity,
|
|
10378
|
-
parse: (v) => Number(v)
|
|
10379
|
-
},
|
|
10380
|
-
"deals.buy_max_floor_percent": {
|
|
10381
|
-
type: "number",
|
|
10382
|
-
category: "Deals",
|
|
10383
|
-
label: "Buy Max Floor %",
|
|
10384
|
-
description: "Maximum floor % for buy deals",
|
|
10385
|
-
sensitive: false,
|
|
10386
|
-
hotReload: "instant",
|
|
10387
|
-
validate: numberInRange(1, 100),
|
|
10388
|
-
mask: identity,
|
|
10389
|
-
parse: (v) => Number(v)
|
|
10390
|
-
},
|
|
10391
|
-
"deals.sell_min_floor_percent": {
|
|
10392
|
-
type: "number",
|
|
10393
|
-
category: "Deals",
|
|
10394
|
-
label: "Sell Min Floor %",
|
|
10395
|
-
description: "Minimum floor % for sell deals",
|
|
10396
|
-
sensitive: false,
|
|
10397
|
-
hotReload: "instant",
|
|
10398
|
-
validate: numberInRange(100, 500),
|
|
10399
|
-
mask: identity,
|
|
10400
|
-
parse: (v) => Number(v)
|
|
10401
|
-
},
|
|
10402
|
-
// ─── TON Proxy ────────────────────────────────────────────────────
|
|
10403
|
-
"ton_proxy.enabled": {
|
|
10404
|
-
type: "boolean",
|
|
10405
|
-
category: "TON Proxy",
|
|
10406
|
-
label: "TON Proxy Enabled",
|
|
10407
|
-
description: "Enable Tonutils-Proxy for .ton site access (auto-downloads binary on first run)",
|
|
10408
|
-
sensitive: false,
|
|
10409
|
-
hotReload: "instant",
|
|
10410
|
-
validate: enumValidator(["true", "false"]),
|
|
10411
|
-
mask: identity,
|
|
10412
|
-
parse: (v) => v === "true"
|
|
10413
|
-
},
|
|
10414
|
-
"ton_proxy.port": {
|
|
10415
|
-
type: "number",
|
|
10416
|
-
category: "TON Proxy",
|
|
10417
|
-
label: "Proxy Port",
|
|
10418
|
-
description: "HTTP proxy port for .ton sites (default: 8080)",
|
|
10419
|
-
sensitive: false,
|
|
10420
|
-
hotReload: "restart",
|
|
10421
|
-
validate: numberInRange(1, 65535),
|
|
10422
|
-
mask: identity,
|
|
10423
|
-
parse: (v) => Number(v)
|
|
10424
|
-
},
|
|
10425
|
-
"ton_proxy.binary_path": {
|
|
10426
|
-
type: "string",
|
|
10427
|
-
category: "TON Proxy",
|
|
10428
|
-
label: "Binary Path",
|
|
10429
|
-
description: "Custom path to tonutils-proxy-cli (leave empty for auto-download)",
|
|
10430
|
-
sensitive: false,
|
|
10431
|
-
hotReload: "restart",
|
|
10432
|
-
validate: noValidation,
|
|
10433
|
-
mask: identity,
|
|
10434
|
-
parse: identity
|
|
10435
|
-
},
|
|
10436
|
-
// ─── Capabilities ──────────────────────────────────────────────────
|
|
10437
|
-
"capabilities.exec.mode": {
|
|
10438
|
-
type: "enum",
|
|
10439
|
-
category: "Coding Agent",
|
|
10440
|
-
label: "Exec Mode",
|
|
10441
|
-
description: "System execution: off (disabled) or yolo (full system access)",
|
|
10442
|
-
sensitive: false,
|
|
10443
|
-
hotReload: "restart",
|
|
10444
|
-
options: ["off", "yolo"],
|
|
10445
|
-
optionLabels: { off: "Disabled", yolo: "YOLO" },
|
|
10446
|
-
validate: enumValidator(["off", "yolo"]),
|
|
10447
|
-
mask: identity,
|
|
10448
|
-
parse: identity
|
|
10449
|
-
},
|
|
10450
|
-
"capabilities.exec.scope": {
|
|
10451
|
-
type: "enum",
|
|
10452
|
-
category: "Coding Agent",
|
|
10453
|
-
label: "Exec Scope",
|
|
10454
|
-
description: "Who can trigger exec tools",
|
|
10455
|
-
sensitive: false,
|
|
10456
|
-
hotReload: "restart",
|
|
10457
|
-
options: ["admin-only", "allowlist", "all"],
|
|
10458
|
-
optionLabels: { "admin-only": "Admin Only", allowlist: "Allowlist", all: "Everyone" },
|
|
10459
|
-
validate: enumValidator(["admin-only", "allowlist", "all"]),
|
|
10460
|
-
mask: identity,
|
|
10461
|
-
parse: identity
|
|
10462
|
-
},
|
|
10463
|
-
// ─── Developer ─────────────────────────────────────────────────────
|
|
10464
|
-
"dev.hot_reload": {
|
|
10465
|
-
type: "boolean",
|
|
10466
|
-
category: "Developer",
|
|
10467
|
-
label: "Hot Reload",
|
|
10468
|
-
description: "Watch ~/.teleton/plugins/ for live changes",
|
|
10469
|
-
sensitive: false,
|
|
10470
|
-
hotReload: "instant",
|
|
10471
|
-
validate: enumValidator(["true", "false"]),
|
|
10472
|
-
mask: identity,
|
|
10473
|
-
parse: (v) => v === "true"
|
|
10474
|
-
}
|
|
10475
|
-
};
|
|
10476
|
-
var FORBIDDEN_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
10477
|
-
function assertSafePath(parts) {
|
|
10478
|
-
if (parts.some((p2) => FORBIDDEN_SEGMENTS.has(p2))) {
|
|
10479
|
-
throw new Error("Invalid config path: forbidden segment");
|
|
10480
|
-
}
|
|
10481
|
-
}
|
|
10482
|
-
function getNestedValue(obj, path) {
|
|
10483
|
-
const parts = path.split(".");
|
|
10484
|
-
assertSafePath(parts);
|
|
10485
|
-
let current = obj;
|
|
10486
|
-
for (const part of parts) {
|
|
10487
|
-
if (current == null || typeof current !== "object") return void 0;
|
|
10488
|
-
current = current[part];
|
|
10489
|
-
}
|
|
10490
|
-
return current;
|
|
10491
|
-
}
|
|
10492
|
-
function setNestedValue(obj, path, value) {
|
|
10493
|
-
const parts = path.split(".");
|
|
10494
|
-
assertSafePath(parts);
|
|
10495
|
-
let current = obj;
|
|
10496
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
10497
|
-
if (current[parts[i]] == null || typeof current[parts[i]] !== "object") {
|
|
10498
|
-
current[parts[i]] = {};
|
|
10499
|
-
}
|
|
10500
|
-
current = current[parts[i]];
|
|
10501
|
-
}
|
|
10502
|
-
current[parts[parts.length - 1]] = value;
|
|
10503
|
-
}
|
|
10504
|
-
function deleteNestedValue(obj, path) {
|
|
10505
|
-
const parts = path.split(".");
|
|
10506
|
-
assertSafePath(parts);
|
|
10507
|
-
let current = obj;
|
|
10508
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
10509
|
-
if (current == null || typeof current !== "object") return;
|
|
10510
|
-
current = current[parts[i]];
|
|
10511
|
-
}
|
|
10512
|
-
if (current != null && typeof current === "object") {
|
|
10513
|
-
delete current[parts[parts.length - 1]];
|
|
10514
|
-
}
|
|
10515
|
-
}
|
|
10516
|
-
function readRawConfig(configPath) {
|
|
10517
|
-
const fullPath = expandPath(configPath);
|
|
10518
|
-
if (!existsSync7(fullPath)) {
|
|
10519
|
-
throw new Error(`Config file not found: ${fullPath}
|
|
10520
|
-
Run 'teleton setup' to create one.`);
|
|
10521
|
-
}
|
|
10522
|
-
const raw = parse2(readFileSync6(fullPath, "utf-8"));
|
|
10523
|
-
if (!raw || typeof raw !== "object") {
|
|
10524
|
-
throw new Error(`Invalid config file: ${fullPath}`);
|
|
10525
|
-
}
|
|
10526
|
-
return raw;
|
|
10527
|
-
}
|
|
10528
|
-
function writeRawConfig(raw, configPath) {
|
|
10529
|
-
const clone = { ...raw };
|
|
10530
|
-
delete clone.market;
|
|
10531
|
-
const result = ConfigSchema.safeParse(clone);
|
|
10532
|
-
if (!result.success) {
|
|
10533
|
-
throw new Error(`Refusing to save invalid config: ${result.error.message}`);
|
|
10534
|
-
}
|
|
10535
|
-
raw.meta = raw.meta ?? {};
|
|
10536
|
-
raw.meta.last_modified_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
10537
|
-
const fullPath = expandPath(configPath);
|
|
10538
|
-
writeFileSync3(fullPath, stringify2(raw), { encoding: "utf-8", mode: 384 });
|
|
10539
|
-
}
|
|
10540
|
-
|
|
10541
9772
|
// src/ton-proxy/manager.ts
|
|
10542
9773
|
import { spawn, execSync } from "child_process";
|
|
10543
9774
|
import {
|
|
10544
|
-
existsSync as
|
|
9775
|
+
existsSync as existsSync6,
|
|
10545
9776
|
chmodSync,
|
|
10546
9777
|
createWriteStream,
|
|
10547
|
-
readFileSync as
|
|
10548
|
-
writeFileSync as
|
|
9778
|
+
readFileSync as readFileSync5,
|
|
9779
|
+
writeFileSync as writeFileSync2,
|
|
10549
9780
|
unlinkSync
|
|
10550
9781
|
} from "fs";
|
|
10551
9782
|
import { mkdir } from "fs/promises";
|
|
10552
|
-
import { join as
|
|
9783
|
+
import { join as join4 } from "path";
|
|
10553
9784
|
import { pipeline } from "stream/promises";
|
|
10554
|
-
var
|
|
9785
|
+
var log10 = createLogger("TonProxy");
|
|
10555
9786
|
var GITHUB_REPO = "xssnick/Tonutils-Proxy";
|
|
10556
|
-
var BINARY_DIR =
|
|
10557
|
-
var PID_FILE =
|
|
9787
|
+
var BINARY_DIR = join4(TELETON_ROOT, "bin");
|
|
9788
|
+
var PID_FILE = join4(TELETON_ROOT, "ton-proxy.pid");
|
|
10558
9789
|
var HEALTH_CHECK_INTERVAL_MS = 3e4;
|
|
10559
9790
|
var HEALTH_CHECK_TIMEOUT_MS = 5e3;
|
|
10560
9791
|
var KILL_GRACE_MS = 5e3;
|
|
@@ -10570,11 +9801,11 @@ var TonProxyManager = class {
|
|
|
10570
9801
|
/** Resolve the binary path — user-specified or auto-detected */
|
|
10571
9802
|
getBinaryPath() {
|
|
10572
9803
|
if (this.config.binary_path) return this.config.binary_path;
|
|
10573
|
-
return
|
|
9804
|
+
return join4(BINARY_DIR, getBinaryName());
|
|
10574
9805
|
}
|
|
10575
9806
|
/** Check if the binary exists on disk */
|
|
10576
9807
|
isInstalled() {
|
|
10577
|
-
return
|
|
9808
|
+
return existsSync6(this.getBinaryPath());
|
|
10578
9809
|
}
|
|
10579
9810
|
/** Whether the proxy process is currently running */
|
|
10580
9811
|
isRunning() {
|
|
@@ -10586,7 +9817,7 @@ var TonProxyManager = class {
|
|
|
10586
9817
|
*/
|
|
10587
9818
|
async install() {
|
|
10588
9819
|
const binaryName = getBinaryName();
|
|
10589
|
-
|
|
9820
|
+
log10.info(`Downloading TON Proxy binary (${binaryName})...`);
|
|
10590
9821
|
await mkdir(BINARY_DIR, { recursive: true });
|
|
10591
9822
|
const releaseUrl = `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`;
|
|
10592
9823
|
const releaseRes = await fetch(releaseUrl, {
|
|
@@ -10598,7 +9829,7 @@ var TonProxyManager = class {
|
|
|
10598
9829
|
const release = await releaseRes.json();
|
|
10599
9830
|
const tag = release.tag_name;
|
|
10600
9831
|
const downloadUrl = `https://github.com/${GITHUB_REPO}/releases/download/${tag}/${binaryName}`;
|
|
10601
|
-
|
|
9832
|
+
log10.info(`Downloading ${downloadUrl}`);
|
|
10602
9833
|
const res = await fetch(downloadUrl);
|
|
10603
9834
|
if (!res.ok || !res.body) {
|
|
10604
9835
|
throw new Error(`Download failed: ${res.status} ${res.statusText}`);
|
|
@@ -10607,17 +9838,17 @@ var TonProxyManager = class {
|
|
|
10607
9838
|
const fileStream = createWriteStream(dest);
|
|
10608
9839
|
await pipeline(res.body, fileStream);
|
|
10609
9840
|
chmodSync(dest, 493);
|
|
10610
|
-
|
|
9841
|
+
log10.info(`TON Proxy installed: ${dest} (${tag})`);
|
|
10611
9842
|
}
|
|
10612
9843
|
/** Kill any orphan proxy process from a previous session */
|
|
10613
9844
|
killOrphan() {
|
|
10614
|
-
if (
|
|
9845
|
+
if (existsSync6(PID_FILE)) {
|
|
10615
9846
|
try {
|
|
10616
|
-
const pid = parseInt(
|
|
9847
|
+
const pid = parseInt(readFileSync5(PID_FILE, "utf-8").trim(), 10);
|
|
10617
9848
|
if (pid && !isNaN(pid)) {
|
|
10618
9849
|
try {
|
|
10619
9850
|
process.kill(pid, 0);
|
|
10620
|
-
|
|
9851
|
+
log10.warn(`Killing orphan TON Proxy (PID ${pid}) from previous session`);
|
|
10621
9852
|
process.kill(pid, "SIGTERM");
|
|
10622
9853
|
} catch {
|
|
10623
9854
|
}
|
|
@@ -10634,7 +9865,7 @@ var TonProxyManager = class {
|
|
|
10634
9865
|
const pidMatch = out.match(/pid=(\d+)/);
|
|
10635
9866
|
if (pidMatch) {
|
|
10636
9867
|
const pid = parseInt(pidMatch[1], 10);
|
|
10637
|
-
|
|
9868
|
+
log10.warn(`Port ${this.config.port} occupied by PID ${pid}, killing it`);
|
|
10638
9869
|
try {
|
|
10639
9870
|
process.kill(pid, "SIGTERM");
|
|
10640
9871
|
} catch {
|
|
@@ -10647,22 +9878,22 @@ var TonProxyManager = class {
|
|
|
10647
9878
|
/** Write PID to file for orphan detection */
|
|
10648
9879
|
writePidFile(pid) {
|
|
10649
9880
|
try {
|
|
10650
|
-
|
|
9881
|
+
writeFileSync2(PID_FILE, String(pid), { mode: 384 });
|
|
10651
9882
|
} catch {
|
|
10652
|
-
|
|
9883
|
+
log10.warn("Failed to write TON Proxy PID file");
|
|
10653
9884
|
}
|
|
10654
9885
|
}
|
|
10655
9886
|
/** Remove PID file */
|
|
10656
9887
|
removePidFile() {
|
|
10657
9888
|
try {
|
|
10658
|
-
if (
|
|
9889
|
+
if (existsSync6(PID_FILE)) unlinkSync(PID_FILE);
|
|
10659
9890
|
} catch {
|
|
10660
9891
|
}
|
|
10661
9892
|
}
|
|
10662
9893
|
/** Start the proxy process */
|
|
10663
9894
|
async start() {
|
|
10664
9895
|
if (this.isRunning()) {
|
|
10665
|
-
|
|
9896
|
+
log10.warn("TON Proxy is already running");
|
|
10666
9897
|
return;
|
|
10667
9898
|
}
|
|
10668
9899
|
this.restartCount = 0;
|
|
@@ -10673,7 +9904,7 @@ var TonProxyManager = class {
|
|
|
10673
9904
|
}
|
|
10674
9905
|
const binaryPath = this.getBinaryPath();
|
|
10675
9906
|
const port = String(this.config.port);
|
|
10676
|
-
|
|
9907
|
+
log10.info(`Starting TON Proxy on 127.0.0.1:${port}`);
|
|
10677
9908
|
this.process = spawn(binaryPath, ["-addr", `127.0.0.1:${port}`], {
|
|
10678
9909
|
cwd: BINARY_DIR,
|
|
10679
9910
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -10681,24 +9912,24 @@ var TonProxyManager = class {
|
|
|
10681
9912
|
});
|
|
10682
9913
|
this.process.stdout?.on("data", (chunk) => {
|
|
10683
9914
|
const line = chunk.toString().trim();
|
|
10684
|
-
if (line)
|
|
9915
|
+
if (line) log10.debug(`[proxy] ${line}`);
|
|
10685
9916
|
});
|
|
10686
9917
|
this.process.stderr?.on("data", (chunk) => {
|
|
10687
9918
|
const line = chunk.toString().trim();
|
|
10688
|
-
if (line)
|
|
9919
|
+
if (line) log10.warn(`[proxy:err] ${line}`);
|
|
10689
9920
|
});
|
|
10690
9921
|
this.process.on("exit", (code, signal) => {
|
|
10691
|
-
|
|
9922
|
+
log10.info(`TON Proxy exited (code=${code}, signal=${signal})`);
|
|
10692
9923
|
this.process = null;
|
|
10693
9924
|
this.removePidFile();
|
|
10694
9925
|
if (code !== 0 && code !== null && this.restartCount < this.maxRestarts) {
|
|
10695
9926
|
this.restartCount++;
|
|
10696
|
-
|
|
10697
|
-
this.start().catch((err) =>
|
|
9927
|
+
log10.warn(`Auto-restarting TON Proxy (attempt ${this.restartCount}/${this.maxRestarts})`);
|
|
9928
|
+
this.start().catch((err) => log10.error({ err }, "Failed to auto-restart TON Proxy"));
|
|
10698
9929
|
}
|
|
10699
9930
|
});
|
|
10700
9931
|
this.process.on("error", (err) => {
|
|
10701
|
-
|
|
9932
|
+
log10.error({ err }, "TON Proxy process error");
|
|
10702
9933
|
this.process = null;
|
|
10703
9934
|
});
|
|
10704
9935
|
this.startHealthCheck();
|
|
@@ -10716,14 +9947,14 @@ var TonProxyManager = class {
|
|
|
10716
9947
|
});
|
|
10717
9948
|
});
|
|
10718
9949
|
if (this.process?.pid) this.writePidFile(this.process.pid);
|
|
10719
|
-
|
|
9950
|
+
log10.info(`TON Proxy running on 127.0.0.1:${port} (PID ${this.process?.pid})`);
|
|
10720
9951
|
}
|
|
10721
9952
|
/** Stop the proxy process gracefully */
|
|
10722
9953
|
async stop() {
|
|
10723
9954
|
this.stopHealthCheck();
|
|
10724
9955
|
if (!this.process) return;
|
|
10725
9956
|
this.maxRestarts = 0;
|
|
10726
|
-
|
|
9957
|
+
log10.info("Stopping TON Proxy...");
|
|
10727
9958
|
return new Promise((resolve2) => {
|
|
10728
9959
|
if (!this.process) {
|
|
10729
9960
|
resolve2();
|
|
@@ -10731,7 +9962,7 @@ var TonProxyManager = class {
|
|
|
10731
9962
|
}
|
|
10732
9963
|
const forceKill = setTimeout(() => {
|
|
10733
9964
|
if (this.process) {
|
|
10734
|
-
|
|
9965
|
+
log10.warn("TON Proxy did not exit gracefully, sending SIGKILL");
|
|
10735
9966
|
this.process.kill("SIGKILL");
|
|
10736
9967
|
}
|
|
10737
9968
|
}, KILL_GRACE_MS);
|
|
@@ -10750,10 +9981,10 @@ var TonProxyManager = class {
|
|
|
10750
9981
|
await this.stop();
|
|
10751
9982
|
}
|
|
10752
9983
|
const binaryPath = this.getBinaryPath();
|
|
10753
|
-
if (
|
|
9984
|
+
if (existsSync6(binaryPath)) {
|
|
10754
9985
|
const { unlink } = await import("fs/promises");
|
|
10755
9986
|
await unlink(binaryPath);
|
|
10756
|
-
|
|
9987
|
+
log10.info(`TON Proxy binary removed: ${binaryPath}`);
|
|
10757
9988
|
}
|
|
10758
9989
|
}
|
|
10759
9990
|
/** Get proxy status for WebUI / tools */
|
|
@@ -10787,7 +10018,7 @@ var TonProxyManager = class {
|
|
|
10787
10018
|
}).catch(() => null);
|
|
10788
10019
|
clearTimeout(timeout);
|
|
10789
10020
|
if (!res) {
|
|
10790
|
-
|
|
10021
|
+
log10.warn("TON Proxy health check failed (no response)");
|
|
10791
10022
|
}
|
|
10792
10023
|
} catch {
|
|
10793
10024
|
}
|
|
@@ -10844,7 +10075,7 @@ var tonProxyStatusExecutor = async () => {
|
|
|
10844
10075
|
};
|
|
10845
10076
|
|
|
10846
10077
|
// src/ton-proxy/module.ts
|
|
10847
|
-
var
|
|
10078
|
+
var log11 = createLogger("TonProxyModule");
|
|
10848
10079
|
var manager = null;
|
|
10849
10080
|
function getTonProxyManager() {
|
|
10850
10081
|
return manager;
|
|
@@ -10871,9 +10102,9 @@ var tonProxyModule = {
|
|
|
10871
10102
|
setProxyManager(manager);
|
|
10872
10103
|
try {
|
|
10873
10104
|
await manager.start();
|
|
10874
|
-
|
|
10105
|
+
log11.info(`TON Proxy started on port ${proxyConfig.port}`);
|
|
10875
10106
|
} catch (err) {
|
|
10876
|
-
|
|
10107
|
+
log11.error({ err }, "Failed to start TON Proxy");
|
|
10877
10108
|
manager = null;
|
|
10878
10109
|
}
|
|
10879
10110
|
},
|
|
@@ -10925,9 +10156,6 @@ function setTriggersConfig(db, triggers) {
|
|
|
10925
10156
|
}
|
|
10926
10157
|
|
|
10927
10158
|
export {
|
|
10928
|
-
loadConfig,
|
|
10929
|
-
configExists,
|
|
10930
|
-
getDefaultConfigPath,
|
|
10931
10159
|
WorkspaceSecurityError,
|
|
10932
10160
|
validatePath,
|
|
10933
10161
|
validateReadPath,
|
|
@@ -10972,12 +10200,6 @@ export {
|
|
|
10972
10200
|
adaptPlugin,
|
|
10973
10201
|
ensurePluginDeps,
|
|
10974
10202
|
loadEnhancedPlugins,
|
|
10975
|
-
CONFIGURABLE_KEYS,
|
|
10976
|
-
getNestedValue,
|
|
10977
|
-
setNestedValue,
|
|
10978
|
-
deleteNestedValue,
|
|
10979
|
-
readRawConfig,
|
|
10980
|
-
writeRawConfig,
|
|
10981
10203
|
TonProxyManager,
|
|
10982
10204
|
getTonProxyManager,
|
|
10983
10205
|
setTonProxyManager,
|