teleton 0.6.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -31
- package/dist/{chunk-6L6KGATM.js → chunk-3YM57ZAV.js} +1638 -1749
- package/dist/{chunk-D5I7GBV7.js → chunk-FNV5FF35.js} +22 -13
- package/dist/{chunk-4IPJ25HE.js → chunk-HZNZT4TG.js} +1106 -711
- package/dist/chunk-LRCPA7SC.js +149 -0
- package/dist/chunk-ND2X5FWB.js +368 -0
- package/dist/chunk-NERLQY2H.js +421 -0
- package/dist/{chunk-YBA6IBGT.js → chunk-OCLG5GKI.js} +24 -24
- package/dist/{chunk-ADCMUNYU.js → chunk-RBU6JXD3.js} +60 -55
- package/dist/chunk-RCMD3U65.js +141 -0
- package/dist/{chunk-ECSCVEQQ.js → chunk-UCN6TI25.js} +7 -3
- package/dist/{chunk-WL2Q3VRD.js → chunk-UDD7FYOU.js} +12 -4
- package/dist/chunk-VAUJSSD3.js +20 -0
- package/dist/chunk-XBE4JB7C.js +8 -0
- package/dist/{chunk-GDCODBNO.js → chunk-XBKSS6DM.js} +2 -16
- package/dist/cli/index.js +878 -433
- package/dist/client-3VWE7NC4.js +29 -0
- package/dist/{get-my-gifts-KVULMBJ3.js → get-my-gifts-RI7FAXAL.js} +3 -1
- package/dist/index.js +17 -11
- package/dist/{memory-TVDOGQXS.js → memory-5SS3Q5EA.js} +7 -5
- package/dist/{migrate-QIEMPOMT.js → migrate-M7SJMDOL.js} +14 -9
- package/dist/{server-RSWVCVY3.js → server-DS5OARW6.js} +174 -85
- package/dist/setup-server-C7ZTPHD5.js +934 -0
- package/dist/{task-dependency-resolver-72DLY2HV.js → task-dependency-resolver-WKZWJLLM.js} +19 -15
- package/dist/{task-executor-VXB6DAV2.js → task-executor-PD3H4MLO.js} +4 -1
- package/dist/tool-adapter-Y3TCEQOC.js +145 -0
- package/dist/{tool-index-DKI2ZNOU.js → tool-index-MIVK3D7H.js} +14 -9
- package/dist/{transcript-7V4UNID4.js → transcript-UDJZP6NK.js} +2 -1
- package/dist/web/assets/complete-fZLnb5Ot.js +1 -0
- package/dist/web/assets/index-BqwoDycr.js +72 -0
- package/dist/web/assets/index-CRDIf07k.css +1 -0
- package/dist/web/assets/index.es-D81xLR29.js +11 -0
- package/dist/web/assets/login-telegram-BP7CJDmx.js +1 -0
- package/dist/web/assets/run-DOrDowjK.js +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +7 -3
- package/dist/chunk-2QUJLHCZ.js +0 -362
- package/dist/web/assets/index-BNhrx9S1.js +0 -67
- package/dist/web/assets/index-CqrrRLOh.css +0 -1
|
@@ -1,13 +1,34 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ConfigSchema,
|
|
3
|
+
getKeyPair,
|
|
4
|
+
getTonPrice,
|
|
5
|
+
getWalletAddress,
|
|
6
|
+
getWalletBalance,
|
|
7
|
+
loadWallet
|
|
8
|
+
} from "./chunk-NERLQY2H.js";
|
|
9
|
+
import {
|
|
10
|
+
getCachedHttpEndpoint
|
|
11
|
+
} from "./chunk-QUAPFI2N.js";
|
|
12
|
+
import {
|
|
13
|
+
require_BigInteger
|
|
14
|
+
} from "./chunk-TSKJCWQQ.js";
|
|
15
|
+
import {
|
|
16
|
+
getErrorMessage
|
|
17
|
+
} from "./chunk-XBE4JB7C.js";
|
|
18
|
+
import {
|
|
19
|
+
getProviderMetadata
|
|
20
|
+
} from "./chunk-LRCPA7SC.js";
|
|
1
21
|
import {
|
|
2
22
|
createDbWrapper,
|
|
3
23
|
migrateFromMainDb,
|
|
4
24
|
openModuleDb
|
|
5
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-UCN6TI25.js";
|
|
6
26
|
import {
|
|
7
|
-
COINGECKO_API_URL,
|
|
8
|
-
fetchWithTimeout,
|
|
9
27
|
tonapiFetch
|
|
10
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-XBKSS6DM.js";
|
|
29
|
+
import {
|
|
30
|
+
PAYMENT_TOLERANCE_RATIO
|
|
31
|
+
} from "./chunk-RO62LO6Z.js";
|
|
11
32
|
import {
|
|
12
33
|
RETRY_BLOCKCHAIN_BASE_DELAY_MS,
|
|
13
34
|
RETRY_BLOCKCHAIN_MAX_DELAY_MS,
|
|
@@ -17,10 +38,6 @@ import {
|
|
|
17
38
|
RETRY_DEFAULT_MAX_DELAY_MS,
|
|
18
39
|
RETRY_DEFAULT_TIMEOUT_MS
|
|
19
40
|
} from "./chunk-4DU3C27M.js";
|
|
20
|
-
import {
|
|
21
|
-
PAYMENT_TOLERANCE_RATIO,
|
|
22
|
-
TELEGRAM_MAX_MESSAGE_LENGTH
|
|
23
|
-
} from "./chunk-RO62LO6Z.js";
|
|
24
41
|
import {
|
|
25
42
|
ALLOWED_EXTENSIONS,
|
|
26
43
|
TELETON_ROOT,
|
|
@@ -28,244 +45,19 @@ import {
|
|
|
28
45
|
WORKSPACE_ROOT
|
|
29
46
|
} from "./chunk-EYWNOHMJ.js";
|
|
30
47
|
import {
|
|
31
|
-
|
|
32
|
-
} from "./chunk-
|
|
48
|
+
createLogger
|
|
49
|
+
} from "./chunk-RCMD3U65.js";
|
|
33
50
|
import {
|
|
34
|
-
__require
|
|
51
|
+
__require,
|
|
52
|
+
__toESM
|
|
35
53
|
} from "./chunk-QGM4M3NI.js";
|
|
36
54
|
|
|
37
|
-
// src/config/providers.ts
|
|
38
|
-
var PROVIDER_REGISTRY = {
|
|
39
|
-
anthropic: {
|
|
40
|
-
id: "anthropic",
|
|
41
|
-
displayName: "Anthropic (Claude)",
|
|
42
|
-
envVar: "ANTHROPIC_API_KEY",
|
|
43
|
-
keyPrefix: "sk-ant-",
|
|
44
|
-
keyHint: "sk-ant-api03-...",
|
|
45
|
-
consoleUrl: "https://console.anthropic.com/",
|
|
46
|
-
defaultModel: "claude-opus-4-5-20251101",
|
|
47
|
-
utilityModel: "claude-3-5-haiku-20241022",
|
|
48
|
-
toolLimit: null,
|
|
49
|
-
piAiProvider: "anthropic"
|
|
50
|
-
},
|
|
51
|
-
openai: {
|
|
52
|
-
id: "openai",
|
|
53
|
-
displayName: "OpenAI (GPT-4o)",
|
|
54
|
-
envVar: "OPENAI_API_KEY",
|
|
55
|
-
keyPrefix: "sk-",
|
|
56
|
-
keyHint: "sk-proj-...",
|
|
57
|
-
consoleUrl: "https://platform.openai.com/api-keys",
|
|
58
|
-
defaultModel: "gpt-4o",
|
|
59
|
-
utilityModel: "gpt-4o-mini",
|
|
60
|
-
toolLimit: 128,
|
|
61
|
-
piAiProvider: "openai"
|
|
62
|
-
},
|
|
63
|
-
google: {
|
|
64
|
-
id: "google",
|
|
65
|
-
displayName: "Google (Gemini)",
|
|
66
|
-
envVar: "GOOGLE_API_KEY",
|
|
67
|
-
keyPrefix: null,
|
|
68
|
-
keyHint: "AIza...",
|
|
69
|
-
consoleUrl: "https://aistudio.google.com/apikey",
|
|
70
|
-
defaultModel: "gemini-2.5-flash",
|
|
71
|
-
utilityModel: "gemini-2.0-flash-lite",
|
|
72
|
-
toolLimit: 128,
|
|
73
|
-
piAiProvider: "google"
|
|
74
|
-
},
|
|
75
|
-
xai: {
|
|
76
|
-
id: "xai",
|
|
77
|
-
displayName: "xAI (Grok)",
|
|
78
|
-
envVar: "XAI_API_KEY",
|
|
79
|
-
keyPrefix: "xai-",
|
|
80
|
-
keyHint: "xai-...",
|
|
81
|
-
consoleUrl: "https://console.x.ai/",
|
|
82
|
-
defaultModel: "grok-3",
|
|
83
|
-
utilityModel: "grok-3-mini-fast",
|
|
84
|
-
toolLimit: 128,
|
|
85
|
-
piAiProvider: "xai"
|
|
86
|
-
},
|
|
87
|
-
groq: {
|
|
88
|
-
id: "groq",
|
|
89
|
-
displayName: "Groq",
|
|
90
|
-
envVar: "GROQ_API_KEY",
|
|
91
|
-
keyPrefix: "gsk_",
|
|
92
|
-
keyHint: "gsk_...",
|
|
93
|
-
consoleUrl: "https://console.groq.com/keys",
|
|
94
|
-
defaultModel: "llama-3.3-70b-versatile",
|
|
95
|
-
utilityModel: "llama-3.1-8b-instant",
|
|
96
|
-
toolLimit: 128,
|
|
97
|
-
piAiProvider: "groq"
|
|
98
|
-
},
|
|
99
|
-
openrouter: {
|
|
100
|
-
id: "openrouter",
|
|
101
|
-
displayName: "OpenRouter",
|
|
102
|
-
envVar: "OPENROUTER_API_KEY",
|
|
103
|
-
keyPrefix: "sk-or-",
|
|
104
|
-
keyHint: "sk-or-v1-...",
|
|
105
|
-
consoleUrl: "https://openrouter.ai/keys",
|
|
106
|
-
defaultModel: "anthropic/claude-opus-4.5",
|
|
107
|
-
utilityModel: "google/gemini-2.5-flash-lite",
|
|
108
|
-
toolLimit: 128,
|
|
109
|
-
piAiProvider: "openrouter"
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
function getProviderMetadata(provider) {
|
|
113
|
-
const meta = PROVIDER_REGISTRY[provider];
|
|
114
|
-
if (!meta) {
|
|
115
|
-
throw new Error(`Unknown provider: ${provider}`);
|
|
116
|
-
}
|
|
117
|
-
return meta;
|
|
118
|
-
}
|
|
119
|
-
function getSupportedProviders() {
|
|
120
|
-
return Object.values(PROVIDER_REGISTRY);
|
|
121
|
-
}
|
|
122
|
-
function validateApiKeyFormat(provider, key) {
|
|
123
|
-
const meta = PROVIDER_REGISTRY[provider];
|
|
124
|
-
if (!meta) return `Unknown provider: ${provider}`;
|
|
125
|
-
if (!key || key.trim().length === 0) return "API key is required";
|
|
126
|
-
if (meta.keyPrefix && !key.startsWith(meta.keyPrefix)) {
|
|
127
|
-
return `Invalid format (should start with ${meta.keyPrefix})`;
|
|
128
|
-
}
|
|
129
|
-
return void 0;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
55
|
// src/config/loader.ts
|
|
133
|
-
import { readFileSync, existsSync, writeFileSync, mkdirSync
|
|
56
|
+
import { readFileSync, existsSync, writeFileSync, mkdirSync } from "fs";
|
|
134
57
|
import { parse, stringify } from "yaml";
|
|
135
58
|
import { homedir } from "os";
|
|
136
59
|
import { dirname, join } from "path";
|
|
137
|
-
|
|
138
|
-
// src/config/schema.ts
|
|
139
|
-
import { z } from "zod";
|
|
140
|
-
var DMPolicy = z.enum(["pairing", "allowlist", "open", "disabled"]);
|
|
141
|
-
var GroupPolicy = z.enum(["open", "allowlist", "disabled"]);
|
|
142
|
-
var SessionResetPolicySchema = z.object({
|
|
143
|
-
daily_reset_enabled: z.boolean().default(true).describe("Enable daily session reset"),
|
|
144
|
-
daily_reset_hour: z.number().min(0).max(23).default(4).describe("Hour of day (0-23) to reset sessions"),
|
|
145
|
-
idle_expiry_enabled: z.boolean().default(true).describe("Enable session reset after idle period"),
|
|
146
|
-
idle_expiry_minutes: z.number().default(1440).describe("Minutes of inactivity before session reset (default: 24h)")
|
|
147
|
-
});
|
|
148
|
-
var AgentConfigSchema = z.object({
|
|
149
|
-
provider: z.enum(["anthropic", "openai", "google", "xai", "groq", "openrouter"]).default("anthropic"),
|
|
150
|
-
api_key: z.string(),
|
|
151
|
-
model: z.string().default("claude-opus-4-5-20251101"),
|
|
152
|
-
utility_model: z.string().optional().describe("Cheap model for summarization (auto-detected if omitted)"),
|
|
153
|
-
max_tokens: z.number().default(4096),
|
|
154
|
-
temperature: z.number().default(0.7),
|
|
155
|
-
system_prompt: z.string().nullable().default(null),
|
|
156
|
-
max_agentic_iterations: z.number().default(5).describe("Maximum number of agentic loop iterations (tool call \u2192 result \u2192 tool call cycles)"),
|
|
157
|
-
session_reset_policy: SessionResetPolicySchema.default(SessionResetPolicySchema.parse({}))
|
|
158
|
-
});
|
|
159
|
-
var TelegramConfigSchema = z.object({
|
|
160
|
-
api_id: z.number(),
|
|
161
|
-
api_hash: z.string(),
|
|
162
|
-
phone: z.string(),
|
|
163
|
-
session_name: z.string().default("teleton_session"),
|
|
164
|
-
session_path: z.string().default("~/.teleton"),
|
|
165
|
-
dm_policy: DMPolicy.default("pairing"),
|
|
166
|
-
allow_from: z.array(z.number()).default([]),
|
|
167
|
-
group_policy: GroupPolicy.default("open"),
|
|
168
|
-
group_allow_from: z.array(z.number()).default([]),
|
|
169
|
-
require_mention: z.boolean().default(true),
|
|
170
|
-
max_message_length: z.number().default(TELEGRAM_MAX_MESSAGE_LENGTH),
|
|
171
|
-
typing_simulation: z.boolean().default(true),
|
|
172
|
-
rate_limit_messages_per_second: z.number().default(1),
|
|
173
|
-
rate_limit_groups_per_minute: z.number().default(20),
|
|
174
|
-
admin_ids: z.array(z.number()).default([]),
|
|
175
|
-
agent_channel: z.string().nullable().default(null),
|
|
176
|
-
owner_name: z.string().optional().describe("Owner's first name (e.g., 'Alex')"),
|
|
177
|
-
owner_username: z.string().optional().describe("Owner's Telegram username (without @)"),
|
|
178
|
-
owner_id: z.number().optional().describe("Owner's Telegram user ID"),
|
|
179
|
-
debounce_ms: z.number().default(1500).describe("Debounce delay in milliseconds for group messages (0 = disabled)"),
|
|
180
|
-
bot_token: z.string().optional().describe("Telegram Bot token from @BotFather for inline deal buttons"),
|
|
181
|
-
bot_username: z.string().optional().describe("Bot username without @ (e.g., 'teleton_deals_bot')")
|
|
182
|
-
});
|
|
183
|
-
var StorageConfigSchema = z.object({
|
|
184
|
-
sessions_file: z.string().default("~/.teleton/sessions.json"),
|
|
185
|
-
pairing_file: z.string().default("~/.teleton/pairing.json"),
|
|
186
|
-
memory_file: z.string().default("~/.teleton/memory.json"),
|
|
187
|
-
history_limit: z.number().default(100)
|
|
188
|
-
});
|
|
189
|
-
var MetaConfigSchema = z.object({
|
|
190
|
-
version: z.string().default("1.0.0"),
|
|
191
|
-
created_at: z.string().optional(),
|
|
192
|
-
last_modified_at: z.string().optional(),
|
|
193
|
-
onboard_command: z.string().default("teleton setup")
|
|
194
|
-
});
|
|
195
|
-
var _DealsObject = z.object({
|
|
196
|
-
enabled: z.boolean().default(true),
|
|
197
|
-
expiry_seconds: z.number().default(120),
|
|
198
|
-
buy_max_floor_percent: z.number().default(100),
|
|
199
|
-
sell_min_floor_percent: z.number().default(105),
|
|
200
|
-
poll_interval_ms: z.number().default(5e3),
|
|
201
|
-
max_verification_retries: z.number().default(12),
|
|
202
|
-
expiry_check_interval_ms: z.number().default(6e4)
|
|
203
|
-
});
|
|
204
|
-
var DealsConfigSchema = _DealsObject.default(_DealsObject.parse({}));
|
|
205
|
-
var _WebUIObject = z.object({
|
|
206
|
-
enabled: z.boolean().default(false).describe("Enable WebUI server"),
|
|
207
|
-
port: z.number().default(7777).describe("HTTP server port"),
|
|
208
|
-
host: z.string().default("127.0.0.1").describe("Bind address (localhost only for security)"),
|
|
209
|
-
auth_token: z.string().optional().describe("Bearer token for API auth (auto-generated if omitted)"),
|
|
210
|
-
cors_origins: z.array(z.string()).default(["http://localhost:5173", "http://localhost:7777"]).describe("Allowed CORS origins for development"),
|
|
211
|
-
log_requests: z.boolean().default(false).describe("Log all HTTP requests")
|
|
212
|
-
});
|
|
213
|
-
var WebUIConfigSchema = _WebUIObject.default(_WebUIObject.parse({}));
|
|
214
|
-
var _EmbeddingObject = z.object({
|
|
215
|
-
provider: z.enum(["local", "anthropic", "none"]).default("local").describe("Embedding provider: local (ONNX), anthropic (API), or none (FTS5-only)"),
|
|
216
|
-
model: z.string().optional().describe("Model override (default: Xenova/all-MiniLM-L6-v2 for local)")
|
|
217
|
-
});
|
|
218
|
-
var EmbeddingConfigSchema = _EmbeddingObject.default(_EmbeddingObject.parse({}));
|
|
219
|
-
var _DevObject = z.object({
|
|
220
|
-
hot_reload: z.boolean().default(false).describe("Enable plugin hot-reload (watches ~/.teleton/plugins/ for changes)")
|
|
221
|
-
});
|
|
222
|
-
var DevConfigSchema = _DevObject.default(_DevObject.parse({}));
|
|
223
|
-
var McpServerSchema = z.object({
|
|
224
|
-
command: z.string().optional().describe("Stdio command (e.g. 'npx @modelcontextprotocol/server-filesystem /tmp')"),
|
|
225
|
-
args: z.array(z.string()).optional().describe("Explicit args array (overrides command splitting)"),
|
|
226
|
-
env: z.record(z.string(), z.string()).optional().describe("Environment variables for stdio server"),
|
|
227
|
-
url: z.string().url().optional().describe("SSE/HTTP endpoint URL (alternative to command)"),
|
|
228
|
-
scope: z.enum(["always", "dm-only", "group-only", "admin-only"]).default("always").describe("Tool scope"),
|
|
229
|
-
enabled: z.boolean().default(true).describe("Enable/disable this server")
|
|
230
|
-
}).refine((s) => s.command || s.url, {
|
|
231
|
-
message: "Each MCP server needs either 'command' (stdio) or 'url' (SSE/HTTP)"
|
|
232
|
-
});
|
|
233
|
-
var _McpObject = z.object({
|
|
234
|
-
servers: z.record(z.string(), McpServerSchema).default({})
|
|
235
|
-
});
|
|
236
|
-
var McpConfigSchema = _McpObject.default(_McpObject.parse({}));
|
|
237
|
-
var _ToolRagObject = z.object({
|
|
238
|
-
enabled: z.boolean().default(false).describe("Enable semantic tool retrieval (Tool RAG)"),
|
|
239
|
-
top_k: z.number().default(25).describe("Max tools to retrieve per LLM call"),
|
|
240
|
-
always_include: z.array(z.string()).default([
|
|
241
|
-
"telegram_send_message",
|
|
242
|
-
"telegram_reply_message",
|
|
243
|
-
"telegram_send_photo",
|
|
244
|
-
"telegram_send_document",
|
|
245
|
-
"journal_*",
|
|
246
|
-
"workspace_*",
|
|
247
|
-
"web_*"
|
|
248
|
-
]).describe("Tool name patterns always included (prefix glob with *)"),
|
|
249
|
-
skip_unlimited_providers: z.boolean().default(false).describe("Skip Tool RAG for providers with no tool limit (e.g. Anthropic)")
|
|
250
|
-
});
|
|
251
|
-
var ToolRagConfigSchema = _ToolRagObject.default(_ToolRagObject.parse({}));
|
|
252
|
-
var ConfigSchema = z.object({
|
|
253
|
-
meta: MetaConfigSchema.default(MetaConfigSchema.parse({})),
|
|
254
|
-
agent: AgentConfigSchema,
|
|
255
|
-
telegram: TelegramConfigSchema,
|
|
256
|
-
storage: StorageConfigSchema.default(StorageConfigSchema.parse({})),
|
|
257
|
-
embedding: EmbeddingConfigSchema,
|
|
258
|
-
deals: DealsConfigSchema,
|
|
259
|
-
webui: WebUIConfigSchema,
|
|
260
|
-
dev: DevConfigSchema,
|
|
261
|
-
tool_rag: ToolRagConfigSchema,
|
|
262
|
-
mcp: McpConfigSchema,
|
|
263
|
-
plugins: z.record(z.string(), z.unknown()).default({}).describe("Per-plugin config (key = plugin name with underscores)"),
|
|
264
|
-
tonapi_key: z.string().optional().describe("TonAPI key for higher rate limits (from @tonapi_bot)"),
|
|
265
|
-
tavily_api_key: z.string().optional().describe("Tavily API key for web search & extract (free at https://tavily.com)")
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
// src/config/loader.ts
|
|
60
|
+
var log = createLogger("Config");
|
|
269
61
|
var DEFAULT_CONFIG_PATH = join(TELETON_ROOT, "config.yaml");
|
|
270
62
|
function expandPath(path) {
|
|
271
63
|
if (path.startsWith("~")) {
|
|
@@ -292,7 +84,7 @@ Run 'teleton setup' to create one.`);
|
|
|
292
84
|
throw new Error(`Invalid YAML in ${fullPath}: ${error.message}`);
|
|
293
85
|
}
|
|
294
86
|
if (raw && typeof raw === "object" && "market" in raw) {
|
|
295
|
-
|
|
87
|
+
log.warn("config.market is deprecated and ignored. Use market-api plugin instead.");
|
|
296
88
|
delete raw.market;
|
|
297
89
|
}
|
|
298
90
|
const result = ConfigSchema.safeParse(raw);
|
|
@@ -332,12 +124,28 @@ Run 'teleton setup' to create one.`);
|
|
|
332
124
|
}
|
|
333
125
|
if (process.env.TELETON_WEBUI_PORT) {
|
|
334
126
|
const port = parseInt(process.env.TELETON_WEBUI_PORT, 10);
|
|
335
|
-
if (!isNaN(port)) {
|
|
127
|
+
if (!isNaN(port) && port >= 1024 && port <= 65535) {
|
|
336
128
|
config.webui.port = port;
|
|
337
129
|
}
|
|
338
130
|
}
|
|
339
131
|
if (process.env.TELETON_WEBUI_HOST) {
|
|
340
132
|
config.webui.host = process.env.TELETON_WEBUI_HOST;
|
|
133
|
+
if (!["127.0.0.1", "localhost", "::1"].includes(config.webui.host)) {
|
|
134
|
+
log.warn(
|
|
135
|
+
{ host: config.webui.host },
|
|
136
|
+
"WebUI bound to non-loopback address \u2014 ensure auth_token is set"
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (process.env.TELETON_BASE_URL) {
|
|
141
|
+
try {
|
|
142
|
+
new URL(process.env.TELETON_BASE_URL);
|
|
143
|
+
config.agent.base_url = process.env.TELETON_BASE_URL;
|
|
144
|
+
} catch {
|
|
145
|
+
throw new Error(
|
|
146
|
+
`Invalid TELETON_BASE_URL: "${process.env.TELETON_BASE_URL}" is not a valid URL`
|
|
147
|
+
);
|
|
148
|
+
}
|
|
341
149
|
}
|
|
342
150
|
if (process.env.TELETON_TAVILY_API_KEY) {
|
|
343
151
|
config.tavily_api_key = process.env.TELETON_TAVILY_API_KEY;
|
|
@@ -354,169 +162,447 @@ function getDefaultConfigPath() {
|
|
|
354
162
|
return DEFAULT_CONFIG_PATH;
|
|
355
163
|
}
|
|
356
164
|
|
|
357
|
-
// src/
|
|
358
|
-
import {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
import {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
165
|
+
// src/soul/loader.ts
|
|
166
|
+
import { readFileSync as readFileSync3, existsSync as existsSync4 } from "fs";
|
|
167
|
+
|
|
168
|
+
// src/memory/daily-logs.ts
|
|
169
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, appendFileSync, readFileSync as readFileSync2 } from "fs";
|
|
170
|
+
import { join as join2 } from "path";
|
|
171
|
+
|
|
172
|
+
// src/workspace/validator.ts
|
|
173
|
+
import { existsSync as existsSync2, lstatSync, readdirSync } from "fs";
|
|
174
|
+
import { resolve, normalize, relative, extname, basename } from "path";
|
|
175
|
+
import { homedir as homedir2 } from "os";
|
|
176
|
+
var WorkspaceSecurityError = class extends Error {
|
|
177
|
+
constructor(message, attemptedPath) {
|
|
178
|
+
super(message);
|
|
179
|
+
this.attemptedPath = attemptedPath;
|
|
180
|
+
this.name = "WorkspaceSecurityError";
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
function decodeRecursive(str) {
|
|
184
|
+
let decoded = str;
|
|
185
|
+
let prev = "";
|
|
186
|
+
let iterations = 0;
|
|
187
|
+
const maxIterations = 10;
|
|
188
|
+
while (decoded !== prev && iterations < maxIterations) {
|
|
189
|
+
prev = decoded;
|
|
190
|
+
try {
|
|
191
|
+
decoded = decodeURIComponent(decoded);
|
|
192
|
+
} catch {
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
iterations++;
|
|
196
|
+
}
|
|
197
|
+
return decoded;
|
|
198
|
+
}
|
|
199
|
+
function validatePath(inputPath, allowCreate = false) {
|
|
200
|
+
if (!inputPath || inputPath.trim() === "") {
|
|
201
|
+
throw new WorkspaceSecurityError("Path cannot be empty.", inputPath);
|
|
202
|
+
}
|
|
203
|
+
const trimmedPath = inputPath.trim().replace(/\\/g, "/");
|
|
204
|
+
const decodedPath = decodeRecursive(trimmedPath);
|
|
205
|
+
let absolutePath;
|
|
206
|
+
if (decodedPath.startsWith("/")) {
|
|
207
|
+
absolutePath = resolve(normalize(decodedPath));
|
|
208
|
+
} else if (decodedPath.startsWith("~/")) {
|
|
209
|
+
const expanded = decodedPath.replace(/^~(?=$|[\\/])/, homedir2());
|
|
210
|
+
absolutePath = resolve(expanded);
|
|
211
|
+
} else {
|
|
212
|
+
absolutePath = resolve(WORKSPACE_ROOT, normalize(decodedPath));
|
|
213
|
+
}
|
|
214
|
+
const relativePath = relative(WORKSPACE_ROOT, absolutePath);
|
|
215
|
+
if (relativePath.startsWith("..") || relativePath.startsWith("/")) {
|
|
216
|
+
throw new WorkspaceSecurityError(
|
|
217
|
+
`Access denied: Path '${inputPath}' is outside the workspace. Only files in ~/.teleton/workspace/ are accessible.`,
|
|
218
|
+
inputPath
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
const exists = existsSync2(absolutePath);
|
|
222
|
+
if (!exists && !allowCreate) {
|
|
223
|
+
throw new WorkspaceSecurityError(
|
|
224
|
+
`File not found: '${inputPath}' does not exist in workspace.`,
|
|
225
|
+
inputPath
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
if (exists) {
|
|
229
|
+
const stats = lstatSync(absolutePath);
|
|
230
|
+
if (stats.isSymbolicLink()) {
|
|
231
|
+
throw new WorkspaceSecurityError(
|
|
232
|
+
`Access denied: Symbolic links are not allowed for security reasons.`,
|
|
233
|
+
inputPath
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
373
237
|
return {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
238
|
+
absolutePath,
|
|
239
|
+
relativePath,
|
|
240
|
+
exists,
|
|
241
|
+
isDirectory: exists ? lstatSync(absolutePath).isDirectory() : false,
|
|
242
|
+
extension: extname(absolutePath).toLowerCase(),
|
|
243
|
+
filename: basename(absolutePath)
|
|
379
244
|
};
|
|
380
245
|
}
|
|
381
|
-
function
|
|
382
|
-
const
|
|
383
|
-
if (
|
|
384
|
-
|
|
246
|
+
function validateReadPath(inputPath) {
|
|
247
|
+
const validated = validatePath(inputPath, false);
|
|
248
|
+
if (validated.isDirectory) {
|
|
249
|
+
throw new WorkspaceSecurityError(`Cannot read directory as file: '${inputPath}'`, inputPath);
|
|
385
250
|
}
|
|
386
|
-
|
|
387
|
-
chmodSync2(WALLET_FILE, 384);
|
|
388
|
-
_walletCache = void 0;
|
|
389
|
-
_keyPairCache = null;
|
|
251
|
+
return validated;
|
|
390
252
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
253
|
+
var IMMUTABLE_FILES = ["SOUL.md", "STRATEGY.md", "SECURITY.md"];
|
|
254
|
+
function validateWritePath(inputPath, fileType) {
|
|
255
|
+
const validated = validatePath(inputPath, true);
|
|
256
|
+
if (IMMUTABLE_FILES.includes(validated.filename)) {
|
|
257
|
+
throw new WorkspaceSecurityError(
|
|
258
|
+
`Cannot write to ${validated.filename}. This file is configured by the owner. Use memory_write instead.`,
|
|
259
|
+
inputPath
|
|
260
|
+
);
|
|
396
261
|
}
|
|
262
|
+
if (fileType && ALLOWED_EXTENSIONS[fileType]) {
|
|
263
|
+
const allowedExts = ALLOWED_EXTENSIONS[fileType];
|
|
264
|
+
if (!allowedExts.includes(validated.extension)) {
|
|
265
|
+
throw new WorkspaceSecurityError(
|
|
266
|
+
`Invalid file type: '${validated.extension}' is not allowed for ${fileType}. Allowed: ${allowedExts.join(", ")}`,
|
|
267
|
+
inputPath
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return validated;
|
|
272
|
+
}
|
|
273
|
+
function validateDirectory(inputPath) {
|
|
274
|
+
const validated = validatePath(inputPath, true);
|
|
275
|
+
if (validated.exists && !validated.isDirectory) {
|
|
276
|
+
throw new WorkspaceSecurityError(
|
|
277
|
+
`Path exists but is not a directory: '${inputPath}'`,
|
|
278
|
+
inputPath
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
return validated;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// src/memory/daily-logs.ts
|
|
285
|
+
var log2 = createLogger("Memory");
|
|
286
|
+
var MEMORY_DIR = WORKSPACE_PATHS.MEMORY_DIR;
|
|
287
|
+
function formatDate(date) {
|
|
288
|
+
const year = date.getFullYear();
|
|
289
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
290
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
291
|
+
return `${year}-${month}-${day}`;
|
|
292
|
+
}
|
|
293
|
+
function getDailyLogPath(date = /* @__PURE__ */ new Date()) {
|
|
294
|
+
return join2(MEMORY_DIR, `${formatDate(date)}.md`);
|
|
295
|
+
}
|
|
296
|
+
function ensureMemoryDir() {
|
|
297
|
+
if (!existsSync3(MEMORY_DIR)) {
|
|
298
|
+
mkdirSync2(MEMORY_DIR, { recursive: true });
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
function appendToDailyLog(content, date = /* @__PURE__ */ new Date()) {
|
|
397
302
|
try {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
303
|
+
ensureMemoryDir();
|
|
304
|
+
const logPath = getDailyLogPath(date);
|
|
305
|
+
const timestamp = date.toLocaleTimeString("en-US", { hour12: false });
|
|
306
|
+
if (!existsSync3(logPath)) {
|
|
307
|
+
const header = `# Daily Log - ${formatDate(date)}
|
|
308
|
+
|
|
309
|
+
`;
|
|
310
|
+
appendFileSync(logPath, header, "utf-8");
|
|
311
|
+
}
|
|
312
|
+
const entry = `## ${timestamp}
|
|
313
|
+
|
|
314
|
+
${content}
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
`;
|
|
319
|
+
appendFileSync(logPath, entry, "utf-8");
|
|
320
|
+
log2.info(`Daily log updated: ${logPath}`);
|
|
401
321
|
} catch (error) {
|
|
402
|
-
|
|
403
|
-
|
|
322
|
+
log2.error({ err: error }, "Failed to write daily log");
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
function readDailyLog(date = /* @__PURE__ */ new Date()) {
|
|
326
|
+
try {
|
|
327
|
+
const logPath = getDailyLogPath(date);
|
|
328
|
+
if (!existsSync3(logPath)) return null;
|
|
329
|
+
return readFileSync2(logPath, "utf-8");
|
|
330
|
+
} catch {
|
|
404
331
|
return null;
|
|
405
332
|
}
|
|
406
333
|
}
|
|
407
|
-
|
|
408
|
-
|
|
334
|
+
var DAILY_LOG_LINE_LIMIT = 100;
|
|
335
|
+
function truncateDailyLog(content) {
|
|
336
|
+
const lines = content.split("\n");
|
|
337
|
+
if (lines.length <= DAILY_LOG_LINE_LIMIT) return content;
|
|
338
|
+
const truncated = lines.slice(-DAILY_LOG_LINE_LIMIT).join("\n");
|
|
339
|
+
const dropped = lines.length - DAILY_LOG_LINE_LIMIT;
|
|
340
|
+
return `_[... ${dropped} earlier lines omitted]_
|
|
341
|
+
|
|
342
|
+
${truncated}`;
|
|
409
343
|
}
|
|
410
|
-
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
|
|
344
|
+
function readRecentMemory() {
|
|
345
|
+
const today = /* @__PURE__ */ new Date();
|
|
346
|
+
const yesterday = new Date(today);
|
|
347
|
+
yesterday.setDate(yesterday.getDate() - 1);
|
|
348
|
+
const parts = [];
|
|
349
|
+
const yesterdayLog = readDailyLog(yesterday);
|
|
350
|
+
if (yesterdayLog) {
|
|
351
|
+
parts.push(`## Yesterday (${formatDate(yesterday)})
|
|
352
|
+
|
|
353
|
+
${truncateDailyLog(yesterdayLog)}`);
|
|
414
354
|
}
|
|
415
|
-
const
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
};
|
|
355
|
+
const todayLog = readDailyLog(today);
|
|
356
|
+
if (todayLog) {
|
|
357
|
+
parts.push(`## Today (${formatDate(today)})
|
|
358
|
+
|
|
359
|
+
${truncateDailyLog(todayLog)}`);
|
|
360
|
+
}
|
|
361
|
+
if (parts.length === 0) {
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
return `# Recent Memory
|
|
365
|
+
|
|
366
|
+
${parts.join("\n\n---\n\n")}`;
|
|
428
367
|
}
|
|
429
|
-
function
|
|
430
|
-
|
|
431
|
-
|
|
368
|
+
function writeSummaryToDailyLog(summary) {
|
|
369
|
+
appendToDailyLog(`### Memory Flush (Pre-Compaction)
|
|
370
|
+
|
|
371
|
+
${summary}`);
|
|
432
372
|
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
373
|
+
|
|
374
|
+
// src/utils/sanitize.ts
|
|
375
|
+
function sanitizeForPrompt(text) {
|
|
376
|
+
return text.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, "").replace(/[\u00AD\u034F\u061C\u180E\u200B-\u200F\u2060-\u2064\uFEFF]/g, "").replace(/[\u202A-\u202E\u2066-\u2069]/g, "").replace(/[\r\n\u2028\u2029]+/g, " ").replace(/#{1,6}\s/g, "").replace(/<\/?[a-zA-Z_][^>]*>/g, "").replace(/`{3,}/g, "`").trim().slice(0, 128);
|
|
377
|
+
}
|
|
378
|
+
function sanitizeForContext(text) {
|
|
379
|
+
return text.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, "").replace(/[\u00AD\u034F\u061C\u180E\u200B-\u200F\u2060-\u2064\uFEFF]/g, "").replace(/[\u202A-\u202E\u2066-\u2069]/g, "").replace(/[\u2028\u2029]/g, "\n").replace(/<\/?[a-zA-Z_][^>]*>/g, "").replace(/`{3,}/g, "``").trim();
|
|
439
380
|
}
|
|
440
|
-
|
|
381
|
+
|
|
382
|
+
// src/soul/loader.ts
|
|
383
|
+
var SOUL_PATHS = [WORKSPACE_PATHS.SOUL];
|
|
384
|
+
var STRATEGY_PATHS = [WORKSPACE_PATHS.STRATEGY];
|
|
385
|
+
var SECURITY_PATHS = [WORKSPACE_PATHS.SECURITY];
|
|
386
|
+
var MEMORY_PATH = WORKSPACE_PATHS.MEMORY;
|
|
387
|
+
var DEFAULT_SOUL = `# Teleton AI
|
|
388
|
+
|
|
389
|
+
You are Teleton, a personal AI assistant that operates through Telegram.
|
|
390
|
+
|
|
391
|
+
## Personality
|
|
392
|
+
- Helpful and concise
|
|
393
|
+
- Direct and honest
|
|
394
|
+
- Friendly but professional
|
|
395
|
+
|
|
396
|
+
## Guidelines
|
|
397
|
+
- Keep responses short and actionable
|
|
398
|
+
- Use markdown when appropriate
|
|
399
|
+
- Respect user privacy
|
|
400
|
+
- Be transparent about capabilities and limitations
|
|
401
|
+
`;
|
|
402
|
+
var fileCache = /* @__PURE__ */ new Map();
|
|
403
|
+
var FILE_CACHE_TTL = 6e4;
|
|
404
|
+
function cachedReadFile(path) {
|
|
405
|
+
const now = Date.now();
|
|
406
|
+
const cached = fileCache.get(path);
|
|
407
|
+
if (cached && now < cached.expiry) return cached.content;
|
|
408
|
+
let content = null;
|
|
441
409
|
try {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
const { Address: Address2 } = await import("@ton/core");
|
|
445
|
-
const addressObj = Address2.parse(address);
|
|
446
|
-
const balance = await client.getBalance(addressObj);
|
|
447
|
-
const balanceFormatted = fromNano(balance);
|
|
448
|
-
return {
|
|
449
|
-
balance: balanceFormatted,
|
|
450
|
-
balanceNano: balance.toString()
|
|
451
|
-
};
|
|
452
|
-
} catch (error) {
|
|
453
|
-
console.error("Failed to get balance:", error);
|
|
454
|
-
return null;
|
|
410
|
+
if (existsSync4(path)) content = readFileSync3(path, "utf-8");
|
|
411
|
+
} catch {
|
|
455
412
|
}
|
|
413
|
+
fileCache.set(path, { content, expiry: now + FILE_CACHE_TTL });
|
|
414
|
+
return content;
|
|
415
|
+
}
|
|
416
|
+
function clearPromptCache() {
|
|
417
|
+
fileCache.clear();
|
|
456
418
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
return { ..._tonPriceCache };
|
|
419
|
+
function loadSoul() {
|
|
420
|
+
for (const path of SOUL_PATHS) {
|
|
421
|
+
const content = cachedReadFile(path);
|
|
422
|
+
if (content) return content;
|
|
462
423
|
}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
424
|
+
return DEFAULT_SOUL;
|
|
425
|
+
}
|
|
426
|
+
function loadStrategy() {
|
|
427
|
+
for (const path of STRATEGY_PATHS) {
|
|
428
|
+
const content = cachedReadFile(path);
|
|
429
|
+
if (content) return content;
|
|
430
|
+
}
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
433
|
+
function loadSecurity() {
|
|
434
|
+
for (const path of SECURITY_PATHS) {
|
|
435
|
+
const content = cachedReadFile(path);
|
|
436
|
+
if (content) return content;
|
|
437
|
+
}
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
var MEMORY_HARD_LIMIT = 150;
|
|
441
|
+
function loadPersistentMemory() {
|
|
442
|
+
const content = cachedReadFile(MEMORY_PATH);
|
|
443
|
+
if (!content) return null;
|
|
444
|
+
const lines = content.split("\n");
|
|
445
|
+
if (lines.length <= MEMORY_HARD_LIMIT) {
|
|
446
|
+
return content;
|
|
447
|
+
}
|
|
448
|
+
const truncated = lines.slice(0, MEMORY_HARD_LIMIT).join("\n");
|
|
449
|
+
const remaining = lines.length - MEMORY_HARD_LIMIT;
|
|
450
|
+
return `${truncated}
|
|
451
|
+
|
|
452
|
+
_[... ${remaining} more lines not loaded. Consider consolidating MEMORY.md to keep it under ${MEMORY_HARD_LIMIT} lines.]_`;
|
|
453
|
+
}
|
|
454
|
+
function loadMemoryContext() {
|
|
455
|
+
const parts = [];
|
|
456
|
+
const persistentMemory = loadPersistentMemory();
|
|
457
|
+
if (persistentMemory) {
|
|
458
|
+
parts.push(`## Persistent Memory
|
|
459
|
+
|
|
460
|
+
${sanitizeForContext(persistentMemory)}`);
|
|
461
|
+
}
|
|
462
|
+
const recentMemory = readRecentMemory();
|
|
463
|
+
if (recentMemory) {
|
|
464
|
+
parts.push(sanitizeForContext(recentMemory));
|
|
465
|
+
}
|
|
466
|
+
if (parts.length === 0) {
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
return parts.join("\n\n---\n\n");
|
|
470
|
+
}
|
|
471
|
+
function buildSystemPrompt(options) {
|
|
472
|
+
const soul = options.soul ?? loadSoul();
|
|
473
|
+
const parts = [soul];
|
|
474
|
+
const security = loadSecurity();
|
|
475
|
+
if (security) {
|
|
476
|
+
parts.push(`
|
|
477
|
+
${security}`);
|
|
478
|
+
}
|
|
479
|
+
const includeStrategy = options.includeStrategy ?? true;
|
|
480
|
+
if (includeStrategy) {
|
|
481
|
+
const strategy = options.strategy ?? loadStrategy();
|
|
482
|
+
if (strategy) {
|
|
483
|
+
parts.push(`
|
|
484
|
+
${strategy}`);
|
|
472
485
|
}
|
|
473
|
-
} catch {
|
|
474
486
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
487
|
+
parts.push(`
|
|
488
|
+
## Your Workspace
|
|
489
|
+
|
|
490
|
+
You have a personal workspace at \`~/.teleton/workspace/\` where you can store and manage files.
|
|
491
|
+
|
|
492
|
+
**Structure:**
|
|
493
|
+
- \`SOUL.md\` - Your personality and behavior guidelines
|
|
494
|
+
- \`MEMORY.md\` - Persistent memory (long-term facts you've learned)
|
|
495
|
+
- \`STRATEGY.md\` - Business strategy and trading rules
|
|
496
|
+
- \`memory/\` - Daily logs (auto-created per day)
|
|
497
|
+
- \`downloads/\` - Media downloaded from Telegram
|
|
498
|
+
- \`uploads/\` - Files ready to send
|
|
499
|
+
- \`temp/\` - Temporary working files
|
|
500
|
+
- \`memes/\` - Your meme collection (images, GIFs for reactions)
|
|
501
|
+
|
|
502
|
+
**Tools available:**
|
|
503
|
+
- \`workspace_list\` - List files in a directory
|
|
504
|
+
- \`workspace_read\` - Read a file
|
|
505
|
+
- \`workspace_write\` - Write/create a file
|
|
506
|
+
- \`workspace_delete\` - Delete a file
|
|
507
|
+
- \`workspace_rename\` - Rename or move a file
|
|
508
|
+
- \`workspace_info\` - Get workspace stats
|
|
509
|
+
|
|
510
|
+
**Tips:**
|
|
511
|
+
- Save interesting memes to \`memes/\` with descriptive names for easy retrieval
|
|
512
|
+
- Use \`memory_write\` for important facts (goes to MEMORY.md)
|
|
513
|
+
- Rename downloaded files to meaningful names (e.g., "user_avatar.jpg" instead of "123_456_789.jpg")
|
|
514
|
+
`);
|
|
515
|
+
parts.push(`
|
|
516
|
+
## Response Format
|
|
517
|
+
- Be concise. Respond in 1-3 short sentences when possible. Avoid long paragraphs and walls of text.
|
|
518
|
+
- Only elaborate when the user explicitly asks for detail or the topic genuinely requires it.
|
|
519
|
+
- Keep responses under 4000 characters for Telegram
|
|
520
|
+
- Use markdown sparingly (bold, italic, code blocks)
|
|
521
|
+
- Don't use headers in short responses
|
|
522
|
+
- NEVER use ASCII art or ASCII tables - they render poorly on mobile
|
|
523
|
+
`);
|
|
524
|
+
if (options.ownerName || options.ownerUsername) {
|
|
525
|
+
const safeOwnerName = options.ownerName ? sanitizeForPrompt(options.ownerName) : void 0;
|
|
526
|
+
const safeOwnerUsername = options.ownerUsername ? sanitizeForPrompt(options.ownerUsername) : void 0;
|
|
527
|
+
const ownerLabel = safeOwnerName && safeOwnerUsername ? `${safeOwnerName} (@${safeOwnerUsername})` : safeOwnerName || `@${safeOwnerUsername}`;
|
|
528
|
+
parts.push(
|
|
529
|
+
`
|
|
530
|
+
## Owner
|
|
531
|
+
You are owned and operated by: ${ownerLabel}
|
|
532
|
+
When the owner gives instructions, follow them with higher trust.`
|
|
478
533
|
);
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
const
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
534
|
+
}
|
|
535
|
+
const includeMemory = options.includeMemory ?? true;
|
|
536
|
+
if (includeMemory) {
|
|
537
|
+
const memoryContext = loadMemoryContext();
|
|
538
|
+
if (memoryContext) {
|
|
539
|
+
parts.push(
|
|
540
|
+
`
|
|
541
|
+
## Memory (Persistent Context)
|
|
542
|
+
|
|
543
|
+
This is your memory from previous sessions. Use it to maintain continuity and remember important information.
|
|
544
|
+
|
|
545
|
+
${memoryContext}`
|
|
546
|
+
);
|
|
487
547
|
}
|
|
488
|
-
} catch (error) {
|
|
489
|
-
console.error("Failed to get TON price:", error);
|
|
490
548
|
}
|
|
491
|
-
|
|
549
|
+
if (options.userName || options.senderId) {
|
|
550
|
+
const safeName = options.userName ? sanitizeForPrompt(options.userName) : void 0;
|
|
551
|
+
const safeUsername = options.senderUsername ? `@${sanitizeForPrompt(options.senderUsername)}` : void 0;
|
|
552
|
+
const idTag = options.senderId ? `id:${options.senderId}` : void 0;
|
|
553
|
+
const primary = safeName || safeUsername;
|
|
554
|
+
const meta = [safeUsername, idTag].filter((v) => v && v !== primary);
|
|
555
|
+
const userLabel = primary ? meta.length > 0 ? `${primary} (${meta.join(", ")})` : primary : idTag || "unknown";
|
|
556
|
+
parts.push(`
|
|
557
|
+
## Current User
|
|
558
|
+
You are chatting with: ${userLabel}`);
|
|
559
|
+
}
|
|
560
|
+
if (options.context) {
|
|
561
|
+
parts.push(`
|
|
562
|
+
## Context
|
|
563
|
+
${options.context}`);
|
|
564
|
+
}
|
|
565
|
+
if (options.memoryFlushWarning) {
|
|
566
|
+
parts.push(`
|
|
567
|
+
## Memory Flush Warning
|
|
568
|
+
|
|
569
|
+
Your conversation context is approaching the limit and may be compacted soon.
|
|
570
|
+
**Always respond to the user's message first.** Then, if there's anything important worth preserving, consider using \`memory_write\` alongside your response:
|
|
571
|
+
|
|
572
|
+
- \`target: "persistent"\` for facts, lessons, contacts, decisions
|
|
573
|
+
- \`target: "daily"\` for session notes, events, temporary context
|
|
574
|
+
`);
|
|
575
|
+
}
|
|
576
|
+
return parts.join("\n");
|
|
492
577
|
}
|
|
493
578
|
|
|
494
579
|
// src/agent/tools/plugin-loader.ts
|
|
495
|
-
import { readdirSync, readFileSync as
|
|
580
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync5, existsSync as existsSync6, statSync } from "fs";
|
|
496
581
|
import { join as join4 } from "path";
|
|
497
582
|
import { pathToFileURL } from "url";
|
|
498
583
|
import { execFile } from "child_process";
|
|
499
584
|
import { promisify } from "util";
|
|
500
585
|
|
|
501
586
|
// src/agent/tools/plugin-validator.ts
|
|
502
|
-
import { z
|
|
503
|
-
var
|
|
504
|
-
|
|
587
|
+
import { z } from "zod";
|
|
588
|
+
var log3 = createLogger("PluginValidator");
|
|
589
|
+
var ManifestSchema = z.object({
|
|
590
|
+
name: z.string().min(1).max(64).regex(
|
|
505
591
|
/^[a-z0-9][a-z0-9-]*$/,
|
|
506
592
|
"Must be lowercase alphanumeric with hyphens, starting with a letter or number"
|
|
507
593
|
),
|
|
508
|
-
version:
|
|
509
|
-
author:
|
|
510
|
-
description:
|
|
511
|
-
dependencies:
|
|
512
|
-
defaultConfig:
|
|
513
|
-
sdkVersion:
|
|
514
|
-
secrets:
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
required:
|
|
518
|
-
description:
|
|
519
|
-
env:
|
|
594
|
+
version: z.string().regex(/^\d+\.\d+\.\d+$/, "Must be semver (e.g., 1.0.0)"),
|
|
595
|
+
author: z.string().max(128).optional(),
|
|
596
|
+
description: z.string().max(256).optional(),
|
|
597
|
+
dependencies: z.array(z.string()).optional(),
|
|
598
|
+
defaultConfig: z.record(z.string(), z.unknown()).optional(),
|
|
599
|
+
sdkVersion: z.string().max(32).optional(),
|
|
600
|
+
secrets: z.record(
|
|
601
|
+
z.string(),
|
|
602
|
+
z.object({
|
|
603
|
+
required: z.boolean(),
|
|
604
|
+
description: z.string().max(256),
|
|
605
|
+
env: z.string().max(128).optional()
|
|
520
606
|
})
|
|
521
607
|
).optional()
|
|
522
608
|
});
|
|
@@ -527,20 +613,20 @@ function validateToolDefs(defs, pluginName) {
|
|
|
527
613
|
const valid = [];
|
|
528
614
|
for (const def of defs) {
|
|
529
615
|
if (!def || typeof def !== "object") {
|
|
530
|
-
|
|
616
|
+
log3.warn(`[${pluginName}] tool is not an object, skipping`);
|
|
531
617
|
continue;
|
|
532
618
|
}
|
|
533
619
|
const t = def;
|
|
534
620
|
if (!t.name || typeof t.name !== "string") {
|
|
535
|
-
|
|
621
|
+
log3.warn(`[${pluginName}] tool missing 'name', skipping`);
|
|
536
622
|
continue;
|
|
537
623
|
}
|
|
538
624
|
if (!t.description || typeof t.description !== "string") {
|
|
539
|
-
|
|
625
|
+
log3.warn(`[${pluginName}] tool "${t.name}" missing 'description', skipping`);
|
|
540
626
|
continue;
|
|
541
627
|
}
|
|
542
628
|
if (!t.execute || typeof t.execute !== "function") {
|
|
543
|
-
|
|
629
|
+
log3.warn(`[${pluginName}] tool "${t.name}" missing 'execute' function, skipping`);
|
|
544
630
|
continue;
|
|
545
631
|
}
|
|
546
632
|
valid.push(t);
|
|
@@ -572,35 +658,40 @@ var PluginSDKError = class extends Error {
|
|
|
572
658
|
var SDK_VERSION = "1.0.0";
|
|
573
659
|
|
|
574
660
|
// src/ton/transfer.ts
|
|
575
|
-
import { WalletContractV5R1
|
|
661
|
+
import { WalletContractV5R1, TonClient, toNano, internal } from "@ton/ton";
|
|
576
662
|
import { Address, SendMode } from "@ton/core";
|
|
663
|
+
var log4 = createLogger("TON");
|
|
577
664
|
async function sendTon(params) {
|
|
578
665
|
try {
|
|
579
666
|
const { toAddress, amount, comment = "", bounce = false } = params;
|
|
667
|
+
if (!Number.isFinite(amount) || amount <= 0) {
|
|
668
|
+
log4.error({ amount }, "Invalid transfer amount");
|
|
669
|
+
return null;
|
|
670
|
+
}
|
|
580
671
|
let recipientAddress;
|
|
581
672
|
try {
|
|
582
673
|
recipientAddress = Address.parse(toAddress);
|
|
583
674
|
} catch (e) {
|
|
584
|
-
|
|
675
|
+
log4.error({ err: e }, `Invalid recipient address: ${toAddress}`);
|
|
585
676
|
return null;
|
|
586
677
|
}
|
|
587
678
|
const keyPair = await getKeyPair();
|
|
588
679
|
if (!keyPair) {
|
|
589
|
-
|
|
680
|
+
log4.error("Wallet not initialized");
|
|
590
681
|
return null;
|
|
591
682
|
}
|
|
592
|
-
const wallet =
|
|
683
|
+
const wallet = WalletContractV5R1.create({
|
|
593
684
|
workchain: 0,
|
|
594
685
|
publicKey: keyPair.publicKey
|
|
595
686
|
});
|
|
596
687
|
const endpoint = await getCachedHttpEndpoint();
|
|
597
|
-
const client = new
|
|
688
|
+
const client = new TonClient({ endpoint });
|
|
598
689
|
const contract = client.open(wallet);
|
|
599
690
|
const seqno = await contract.getSeqno();
|
|
600
691
|
await contract.sendTransfer({
|
|
601
692
|
seqno,
|
|
602
693
|
secretKey: keyPair.secretKey,
|
|
603
|
-
sendMode: SendMode.PAY_GAS_SEPARATELY
|
|
694
|
+
sendMode: SendMode.PAY_GAS_SEPARATELY,
|
|
604
695
|
messages: [
|
|
605
696
|
internal({
|
|
606
697
|
to: recipientAddress,
|
|
@@ -611,15 +702,16 @@ async function sendTon(params) {
|
|
|
611
702
|
]
|
|
612
703
|
});
|
|
613
704
|
const pseudoHash = `${seqno}_${Date.now()}_${amount.toFixed(2)}`;
|
|
614
|
-
|
|
705
|
+
log4.info(`Sent ${amount} TON to ${toAddress.slice(0, 8)}... - seqno: ${seqno}`);
|
|
615
706
|
return pseudoHash;
|
|
616
707
|
} catch (error) {
|
|
617
|
-
|
|
708
|
+
log4.error({ err: error }, "Error sending TON");
|
|
618
709
|
return null;
|
|
619
710
|
}
|
|
620
711
|
}
|
|
621
712
|
|
|
622
713
|
// src/utils/retry.ts
|
|
714
|
+
var log5 = createLogger("Utils");
|
|
623
715
|
var DEFAULT_OPTIONS = {
|
|
624
716
|
maxAttempts: RETRY_DEFAULT_MAX_ATTEMPTS,
|
|
625
717
|
baseDelayMs: RETRY_DEFAULT_BASE_DELAY_MS,
|
|
@@ -643,7 +735,7 @@ async function withRetry(fn, options = {}) {
|
|
|
643
735
|
return result;
|
|
644
736
|
} catch (error) {
|
|
645
737
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
646
|
-
|
|
738
|
+
log5.warn(`Retry attempt ${attempt}/${opts.maxAttempts} failed: ${lastError.message}`);
|
|
647
739
|
if (attempt < opts.maxAttempts) {
|
|
648
740
|
const delay = Math.min(opts.baseDelayMs * Math.pow(2, attempt - 1), opts.maxDelayMs);
|
|
649
741
|
await sleep(delay);
|
|
@@ -661,7 +753,7 @@ async function withBlockchainRetry(fn, operation = "blockchain operation") {
|
|
|
661
753
|
timeout: RETRY_BLOCKCHAIN_TIMEOUT_MS
|
|
662
754
|
});
|
|
663
755
|
} catch (error) {
|
|
664
|
-
const message =
|
|
756
|
+
const message = getErrorMessage(error);
|
|
665
757
|
throw new Error(`${operation} failed after retries: ${message}`);
|
|
666
758
|
}
|
|
667
759
|
}
|
|
@@ -670,25 +762,25 @@ async function withBlockchainRetry(fn, operation = "blockchain operation") {
|
|
|
670
762
|
var DEFAULT_MAX_AGE_MINUTES = 10;
|
|
671
763
|
var DEFAULT_TX_RETENTION_DAYS = 30;
|
|
672
764
|
var CLEANUP_PROBABILITY = 0.1;
|
|
673
|
-
function cleanupOldTransactions(db, retentionDays,
|
|
765
|
+
function cleanupOldTransactions(db, retentionDays, log7) {
|
|
674
766
|
if (Math.random() > CLEANUP_PROBABILITY) return;
|
|
675
767
|
try {
|
|
676
768
|
const cutoff = Math.floor(Date.now() / 1e3) - retentionDays * 24 * 60 * 60;
|
|
677
769
|
const result = db.prepare("DELETE FROM used_transactions WHERE used_at < ?").run(cutoff);
|
|
678
770
|
if (result.changes > 0) {
|
|
679
|
-
|
|
771
|
+
log7.debug(`Cleaned up ${result.changes} old transaction records (>${retentionDays}d)`);
|
|
680
772
|
}
|
|
681
773
|
} catch (err) {
|
|
682
|
-
|
|
774
|
+
log7.error("Transaction cleanup failed:", err);
|
|
683
775
|
}
|
|
684
776
|
}
|
|
685
|
-
function createTonSDK(
|
|
777
|
+
function createTonSDK(log7, db) {
|
|
686
778
|
return {
|
|
687
779
|
getAddress() {
|
|
688
780
|
try {
|
|
689
781
|
return getWalletAddress();
|
|
690
782
|
} catch (err) {
|
|
691
|
-
|
|
783
|
+
log7.error("ton.getAddress() failed:", err);
|
|
692
784
|
return null;
|
|
693
785
|
}
|
|
694
786
|
},
|
|
@@ -698,7 +790,7 @@ function createTonSDK(log, db) {
|
|
|
698
790
|
if (!addr) return null;
|
|
699
791
|
return await getWalletBalance(addr);
|
|
700
792
|
} catch (err) {
|
|
701
|
-
|
|
793
|
+
log7.error("ton.getBalance() failed:", err);
|
|
702
794
|
return null;
|
|
703
795
|
}
|
|
704
796
|
},
|
|
@@ -706,7 +798,7 @@ function createTonSDK(log, db) {
|
|
|
706
798
|
try {
|
|
707
799
|
return await getTonPrice();
|
|
708
800
|
} catch (err) {
|
|
709
|
-
|
|
801
|
+
log7.error("ton.getPrice() failed:", err);
|
|
710
802
|
return null;
|
|
711
803
|
}
|
|
712
804
|
},
|
|
@@ -748,13 +840,13 @@ function createTonSDK(log, db) {
|
|
|
748
840
|
},
|
|
749
841
|
async getTransactions(address, limit) {
|
|
750
842
|
try {
|
|
751
|
-
const { TonClient:
|
|
843
|
+
const { TonClient: TonClient2 } = await import("@ton/ton");
|
|
752
844
|
const { Address: Address2 } = await import("@ton/core");
|
|
753
845
|
const { getCachedHttpEndpoint: getCachedHttpEndpoint2 } = await import("./endpoint-FLYNEZ2F.js");
|
|
754
846
|
const { formatTransactions } = await import("./format-transactions-FD74HI5N.js");
|
|
755
847
|
const addressObj = Address2.parse(address);
|
|
756
848
|
const endpoint = await getCachedHttpEndpoint2();
|
|
757
|
-
const client = new
|
|
849
|
+
const client = new TonClient2({ endpoint });
|
|
758
850
|
const transactions = await withBlockchainRetry(
|
|
759
851
|
() => client.getTransactions(addressObj, {
|
|
760
852
|
limit: Math.min(limit ?? 10, 50)
|
|
@@ -763,7 +855,7 @@ function createTonSDK(log, db) {
|
|
|
763
855
|
);
|
|
764
856
|
return formatTransactions(transactions);
|
|
765
857
|
} catch (err) {
|
|
766
|
-
|
|
858
|
+
log7.error("ton.getTransactions() failed:", err);
|
|
767
859
|
return [];
|
|
768
860
|
}
|
|
769
861
|
},
|
|
@@ -779,7 +871,7 @@ function createTonSDK(log, db) {
|
|
|
779
871
|
throw new PluginSDKError("Wallet not initialized", "WALLET_NOT_INITIALIZED");
|
|
780
872
|
}
|
|
781
873
|
const maxAgeMinutes = params.maxAgeMinutes ?? DEFAULT_MAX_AGE_MINUTES;
|
|
782
|
-
cleanupOldTransactions(db, DEFAULT_TX_RETENTION_DAYS,
|
|
874
|
+
cleanupOldTransactions(db, DEFAULT_TX_RETENTION_DAYS, log7);
|
|
783
875
|
try {
|
|
784
876
|
const txs = await this.getTransactions(address, 20);
|
|
785
877
|
for (const tx of txs) {
|
|
@@ -813,7 +905,7 @@ function createTonSDK(log, db) {
|
|
|
813
905
|
};
|
|
814
906
|
} catch (err) {
|
|
815
907
|
if (err instanceof PluginSDKError) throw err;
|
|
816
|
-
|
|
908
|
+
log7.error("ton.verifyPayment() failed:", err);
|
|
817
909
|
return {
|
|
818
910
|
verified: false,
|
|
819
911
|
error: `Verification failed: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -827,7 +919,7 @@ function createTonSDK(log, db) {
|
|
|
827
919
|
if (!addr) return [];
|
|
828
920
|
const response = await tonapiFetch(`/accounts/${addr}/jettons`);
|
|
829
921
|
if (!response.ok) {
|
|
830
|
-
|
|
922
|
+
log7.error(`ton.getJettonBalances() TonAPI error: ${response.status}`);
|
|
831
923
|
return [];
|
|
832
924
|
}
|
|
833
925
|
const data = await response.json();
|
|
@@ -835,7 +927,7 @@ function createTonSDK(log, db) {
|
|
|
835
927
|
for (const item of data.balances || []) {
|
|
836
928
|
const { balance, wallet_address, jetton } = item;
|
|
837
929
|
if (jetton.verification === "blacklist") continue;
|
|
838
|
-
const decimals = jetton.decimals
|
|
930
|
+
const decimals = jetton.decimals ?? 9;
|
|
839
931
|
const rawBalance = BigInt(balance);
|
|
840
932
|
const divisor = BigInt(10 ** decimals);
|
|
841
933
|
const wholePart = rawBalance / divisor;
|
|
@@ -855,7 +947,7 @@ function createTonSDK(log, db) {
|
|
|
855
947
|
}
|
|
856
948
|
return balances;
|
|
857
949
|
} catch (err) {
|
|
858
|
-
|
|
950
|
+
log7.error("ton.getJettonBalances() failed:", err);
|
|
859
951
|
return [];
|
|
860
952
|
}
|
|
861
953
|
},
|
|
@@ -864,7 +956,7 @@ function createTonSDK(log, db) {
|
|
|
864
956
|
const response = await tonapiFetch(`/jettons/${jettonAddress}`);
|
|
865
957
|
if (response.status === 404) return null;
|
|
866
958
|
if (!response.ok) {
|
|
867
|
-
|
|
959
|
+
log7.error(`ton.getJettonInfo() TonAPI error: ${response.status}`);
|
|
868
960
|
return null;
|
|
869
961
|
}
|
|
870
962
|
const data = await response.json();
|
|
@@ -882,13 +974,13 @@ function createTonSDK(log, db) {
|
|
|
882
974
|
image: data.preview || metadata.image || void 0
|
|
883
975
|
};
|
|
884
976
|
} catch (err) {
|
|
885
|
-
|
|
977
|
+
log7.error("ton.getJettonInfo() failed:", err);
|
|
886
978
|
return null;
|
|
887
979
|
}
|
|
888
980
|
},
|
|
889
981
|
async sendJetton(jettonAddress, to, amount, opts) {
|
|
890
982
|
const { Address: Address2, beginCell, SendMode: SendMode2 } = await import("@ton/core");
|
|
891
|
-
const { WalletContractV5R1:
|
|
983
|
+
const { WalletContractV5R1: WalletContractV5R12, TonClient: TonClient2, toNano: toNano2, internal: internal2 } = await import("@ton/ton");
|
|
892
984
|
const { getCachedHttpEndpoint: getCachedHttpEndpoint2 } = await import("./endpoint-FLYNEZ2F.js");
|
|
893
985
|
const walletData = loadWallet();
|
|
894
986
|
if (!walletData) {
|
|
@@ -902,72 +994,82 @@ function createTonSDK(log, db) {
|
|
|
902
994
|
} catch {
|
|
903
995
|
throw new PluginSDKError("Invalid recipient address", "INVALID_ADDRESS");
|
|
904
996
|
}
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
if (!jettonBalance) {
|
|
917
|
-
throw new PluginSDKError(
|
|
918
|
-
`You don't own any of this jetton: ${jettonAddress}`,
|
|
919
|
-
"OPERATION_FAILED"
|
|
997
|
+
try {
|
|
998
|
+
const jettonsResponse = await tonapiFetch(`/accounts/${walletData.address}/jettons`);
|
|
999
|
+
if (!jettonsResponse.ok) {
|
|
1000
|
+
throw new PluginSDKError(
|
|
1001
|
+
`Failed to fetch jetton balances: ${jettonsResponse.status}`,
|
|
1002
|
+
"OPERATION_FAILED"
|
|
1003
|
+
);
|
|
1004
|
+
}
|
|
1005
|
+
const jettonsData = await jettonsResponse.json();
|
|
1006
|
+
const jettonBalance = jettonsData.balances?.find(
|
|
1007
|
+
(b) => b.jetton.address.toLowerCase() === jettonAddress.toLowerCase() || Address2.parse(b.jetton.address).toString() === Address2.parse(jettonAddress).toString()
|
|
920
1008
|
);
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
1009
|
+
if (!jettonBalance) {
|
|
1010
|
+
throw new PluginSDKError(
|
|
1011
|
+
`You don't own any of this jetton: ${jettonAddress}`,
|
|
1012
|
+
"OPERATION_FAILED"
|
|
1013
|
+
);
|
|
1014
|
+
}
|
|
1015
|
+
const senderJettonWallet = jettonBalance.wallet_address.address;
|
|
1016
|
+
const decimals = jettonBalance.jetton.decimals ?? 9;
|
|
1017
|
+
const currentBalance = BigInt(jettonBalance.balance);
|
|
1018
|
+
const amountStr = amount.toFixed(decimals);
|
|
1019
|
+
const [whole, frac = ""] = amountStr.split(".");
|
|
1020
|
+
const amountInUnits = BigInt(whole + (frac + "0".repeat(decimals)).slice(0, decimals));
|
|
1021
|
+
if (amountInUnits > currentBalance) {
|
|
1022
|
+
throw new PluginSDKError(
|
|
1023
|
+
`Insufficient balance. Have ${Number(currentBalance) / 10 ** decimals}, need ${amount}`,
|
|
1024
|
+
"OPERATION_FAILED"
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
const comment = opts?.comment;
|
|
1028
|
+
let forwardPayload = beginCell().endCell();
|
|
1029
|
+
if (comment) {
|
|
1030
|
+
forwardPayload = beginCell().storeUint(0, 32).storeStringTail(comment).endCell();
|
|
1031
|
+
}
|
|
1032
|
+
const JETTON_TRANSFER_OP = 260734629;
|
|
1033
|
+
const messageBody = beginCell().storeUint(JETTON_TRANSFER_OP, 32).storeUint(0, 64).storeCoins(amountInUnits).storeAddress(Address2.parse(to)).storeAddress(Address2.parse(walletData.address)).storeBit(false).storeCoins(comment ? toNano2("0.01") : BigInt(1)).storeBit(comment ? true : false).storeMaybeRef(comment ? forwardPayload : null).endCell();
|
|
1034
|
+
const keyPair = await getKeyPair();
|
|
1035
|
+
if (!keyPair) {
|
|
1036
|
+
throw new PluginSDKError("Wallet key derivation failed", "OPERATION_FAILED");
|
|
1037
|
+
}
|
|
1038
|
+
const wallet = WalletContractV5R12.create({
|
|
1039
|
+
workchain: 0,
|
|
1040
|
+
publicKey: keyPair.publicKey
|
|
1041
|
+
});
|
|
1042
|
+
const endpoint = await getCachedHttpEndpoint2();
|
|
1043
|
+
const client = new TonClient2({ endpoint });
|
|
1044
|
+
const walletContract = client.open(wallet);
|
|
1045
|
+
const seqno = await walletContract.getSeqno();
|
|
1046
|
+
await walletContract.sendTransfer({
|
|
1047
|
+
seqno,
|
|
1048
|
+
secretKey: keyPair.secretKey,
|
|
1049
|
+
sendMode: SendMode2.PAY_GAS_SEPARATELY,
|
|
1050
|
+
messages: [
|
|
1051
|
+
internal2({
|
|
1052
|
+
to: Address2.parse(senderJettonWallet),
|
|
1053
|
+
value: toNano2("0.05"),
|
|
1054
|
+
body: messageBody,
|
|
1055
|
+
bounce: true
|
|
1056
|
+
})
|
|
1057
|
+
]
|
|
1058
|
+
});
|
|
1059
|
+
return { success: true, seqno };
|
|
1060
|
+
} catch (err) {
|
|
1061
|
+
if (err instanceof PluginSDKError) throw err;
|
|
927
1062
|
throw new PluginSDKError(
|
|
928
|
-
`
|
|
1063
|
+
`Failed to send jetton: ${err instanceof Error ? err.message : String(err)}`,
|
|
929
1064
|
"OPERATION_FAILED"
|
|
930
1065
|
);
|
|
931
1066
|
}
|
|
932
|
-
const comment = opts?.comment;
|
|
933
|
-
let forwardPayload = beginCell().endCell();
|
|
934
|
-
if (comment) {
|
|
935
|
-
forwardPayload = beginCell().storeUint(0, 32).storeStringTail(comment).endCell();
|
|
936
|
-
}
|
|
937
|
-
const JETTON_TRANSFER_OP = 260734629;
|
|
938
|
-
const messageBody = beginCell().storeUint(JETTON_TRANSFER_OP, 32).storeUint(0, 64).storeCoins(amountInUnits).storeAddress(Address2.parse(to)).storeAddress(Address2.parse(walletData.address)).storeBit(false).storeCoins(comment ? toNano2("0.01") : BigInt(1)).storeBit(comment ? true : false).storeMaybeRef(comment ? forwardPayload : null).endCell();
|
|
939
|
-
const keyPair = await getKeyPair();
|
|
940
|
-
if (!keyPair) {
|
|
941
|
-
throw new PluginSDKError("Wallet key derivation failed", "OPERATION_FAILED");
|
|
942
|
-
}
|
|
943
|
-
const wallet = WalletContractV5R13.create({
|
|
944
|
-
workchain: 0,
|
|
945
|
-
publicKey: keyPair.publicKey
|
|
946
|
-
});
|
|
947
|
-
const endpoint = await getCachedHttpEndpoint2();
|
|
948
|
-
const client = new TonClient3({ endpoint });
|
|
949
|
-
const walletContract = client.open(wallet);
|
|
950
|
-
const seqno = await walletContract.getSeqno();
|
|
951
|
-
await walletContract.sendTransfer({
|
|
952
|
-
seqno,
|
|
953
|
-
secretKey: keyPair.secretKey,
|
|
954
|
-
sendMode: SendMode2.PAY_GAS_SEPARATELY + SendMode2.IGNORE_ERRORS,
|
|
955
|
-
messages: [
|
|
956
|
-
internal2({
|
|
957
|
-
to: Address2.parse(senderJettonWallet),
|
|
958
|
-
value: toNano2("0.05"),
|
|
959
|
-
body: messageBody,
|
|
960
|
-
bounce: true
|
|
961
|
-
})
|
|
962
|
-
]
|
|
963
|
-
});
|
|
964
|
-
return { success: true, seqno };
|
|
965
1067
|
},
|
|
966
1068
|
async getJettonWalletAddress(ownerAddress, jettonAddress) {
|
|
967
1069
|
try {
|
|
968
1070
|
const response = await tonapiFetch(`/accounts/${ownerAddress}/jettons`);
|
|
969
1071
|
if (!response.ok) {
|
|
970
|
-
|
|
1072
|
+
log7.error(`ton.getJettonWalletAddress() TonAPI error: ${response.status}`);
|
|
971
1073
|
return null;
|
|
972
1074
|
}
|
|
973
1075
|
const { Address: Address2 } = await import("@ton/core");
|
|
@@ -977,7 +1079,7 @@ function createTonSDK(log, db) {
|
|
|
977
1079
|
);
|
|
978
1080
|
return match ? match.wallet_address.address : null;
|
|
979
1081
|
} catch (err) {
|
|
980
|
-
|
|
1082
|
+
log7.error("ton.getJettonWalletAddress() failed:", err);
|
|
981
1083
|
return null;
|
|
982
1084
|
}
|
|
983
1085
|
},
|
|
@@ -990,14 +1092,14 @@ function createTonSDK(log, db) {
|
|
|
990
1092
|
`/accounts/${encodeURIComponent(addr)}/nfts?limit=100&indirect_ownership=true`
|
|
991
1093
|
);
|
|
992
1094
|
if (!response.ok) {
|
|
993
|
-
|
|
1095
|
+
log7.error(`ton.getNftItems() TonAPI error: ${response.status}`);
|
|
994
1096
|
return [];
|
|
995
1097
|
}
|
|
996
1098
|
const data = await response.json();
|
|
997
1099
|
if (!Array.isArray(data.nft_items)) return [];
|
|
998
1100
|
return data.nft_items.filter((item) => item.trust !== "blacklist").map((item) => mapNftItem(item));
|
|
999
1101
|
} catch (err) {
|
|
1000
|
-
|
|
1102
|
+
log7.error("ton.getNftItems() failed:", err);
|
|
1001
1103
|
return [];
|
|
1002
1104
|
}
|
|
1003
1105
|
},
|
|
@@ -1006,13 +1108,13 @@ function createTonSDK(log, db) {
|
|
|
1006
1108
|
const response = await tonapiFetch(`/nfts/${nftAddress}`);
|
|
1007
1109
|
if (response.status === 404) return null;
|
|
1008
1110
|
if (!response.ok) {
|
|
1009
|
-
|
|
1111
|
+
log7.error(`ton.getNftInfo() TonAPI error: ${response.status}`);
|
|
1010
1112
|
return null;
|
|
1011
1113
|
}
|
|
1012
1114
|
const item = await response.json();
|
|
1013
1115
|
return mapNftItem(item);
|
|
1014
1116
|
} catch (err) {
|
|
1015
|
-
|
|
1117
|
+
log7.error("ton.getNftInfo() failed:", err);
|
|
1016
1118
|
return null;
|
|
1017
1119
|
}
|
|
1018
1120
|
},
|
|
@@ -1061,12 +1163,18 @@ function mapNftItem(item) {
|
|
|
1061
1163
|
};
|
|
1062
1164
|
}
|
|
1063
1165
|
|
|
1064
|
-
// src/
|
|
1065
|
-
|
|
1166
|
+
// src/utils/gramjs-bigint.ts
|
|
1167
|
+
var import_big_integer = __toESM(require_BigInteger(), 1);
|
|
1168
|
+
import { randomBytes } from "crypto";
|
|
1169
|
+
function toLong(value) {
|
|
1170
|
+
return (0, import_big_integer.default)(String(value));
|
|
1171
|
+
}
|
|
1172
|
+
function randomLong() {
|
|
1173
|
+
return toLong(randomBytes(8).readBigUInt64BE());
|
|
1174
|
+
}
|
|
1066
1175
|
|
|
1067
1176
|
// src/sdk/telegram-messages.ts
|
|
1068
|
-
|
|
1069
|
-
function createTelegramMessagesSDK(bridge, log) {
|
|
1177
|
+
function createTelegramMessagesSDK(bridge, log7) {
|
|
1070
1178
|
function requireBridge() {
|
|
1071
1179
|
if (!bridge.isAvailable()) {
|
|
1072
1180
|
throw new PluginSDKError(
|
|
@@ -1128,12 +1236,11 @@ function createTelegramMessagesSDK(bridge, log) {
|
|
|
1128
1236
|
fromPeer: fromChatId,
|
|
1129
1237
|
toPeer: toChatId,
|
|
1130
1238
|
id: [messageId],
|
|
1131
|
-
randomId: [
|
|
1239
|
+
randomId: [randomLong()]
|
|
1132
1240
|
})
|
|
1133
1241
|
);
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
for (const update of updates.updates) {
|
|
1242
|
+
if (result instanceof Api.Updates || result instanceof Api.UpdatesCombined) {
|
|
1243
|
+
for (const update of result.updates) {
|
|
1137
1244
|
if (update.className === "UpdateNewMessage" || update.className === "UpdateNewChannelMessage") {
|
|
1138
1245
|
return update.message.id;
|
|
1139
1246
|
}
|
|
@@ -1187,7 +1294,7 @@ function createTelegramMessagesSDK(bridge, log) {
|
|
|
1187
1294
|
return (resultData.messages ?? []).map(toSimpleMessage);
|
|
1188
1295
|
} catch (err) {
|
|
1189
1296
|
if (err instanceof PluginSDKError) throw err;
|
|
1190
|
-
|
|
1297
|
+
log7.error("telegram.searchMessages() failed:", err);
|
|
1191
1298
|
return [];
|
|
1192
1299
|
}
|
|
1193
1300
|
},
|
|
@@ -1213,7 +1320,7 @@ function createTelegramMessagesSDK(bridge, log) {
|
|
|
1213
1320
|
try {
|
|
1214
1321
|
const gramJsClient = getClient();
|
|
1215
1322
|
const { Api } = await import("telegram");
|
|
1216
|
-
const
|
|
1323
|
+
const bigInt2 = (await import("./BigInteger-DQ33LTTE.js")).default;
|
|
1217
1324
|
const peer = await gramJsClient.getInputEntity(chatId);
|
|
1218
1325
|
const result = await gramJsClient.invoke(
|
|
1219
1326
|
new Api.messages.GetReplies({
|
|
@@ -1225,7 +1332,7 @@ function createTelegramMessagesSDK(bridge, log) {
|
|
|
1225
1332
|
limit,
|
|
1226
1333
|
maxId: 0,
|
|
1227
1334
|
minId: 0,
|
|
1228
|
-
hash:
|
|
1335
|
+
hash: bigInt2(0)
|
|
1229
1336
|
})
|
|
1230
1337
|
);
|
|
1231
1338
|
const messages = [];
|
|
@@ -1412,8 +1519,7 @@ function createTelegramMessagesSDK(bridge, log) {
|
|
|
1412
1519
|
}
|
|
1413
1520
|
|
|
1414
1521
|
// src/sdk/telegram-social.ts
|
|
1415
|
-
|
|
1416
|
-
function createTelegramSocialSDK(bridge, log) {
|
|
1522
|
+
function createTelegramSocialSDK(bridge, log7) {
|
|
1417
1523
|
function requireBridge() {
|
|
1418
1524
|
if (!bridge.isAvailable()) {
|
|
1419
1525
|
throw new PluginSDKError(
|
|
@@ -1455,9 +1561,7 @@ function createTelegramSocialSDK(bridge, log) {
|
|
|
1455
1561
|
let description;
|
|
1456
1562
|
let membersCount;
|
|
1457
1563
|
try {
|
|
1458
|
-
const fullChannel = await client.invoke(
|
|
1459
|
-
new Api.channels.GetFullChannel({ channel: entity })
|
|
1460
|
-
);
|
|
1564
|
+
const fullChannel = await client.invoke(new Api.channels.GetFullChannel({ channel }));
|
|
1461
1565
|
const fullChat = fullChannel.fullChat;
|
|
1462
1566
|
description = fullChat.about || void 0;
|
|
1463
1567
|
membersCount = fullChat.participantsCount || void 0;
|
|
@@ -1495,7 +1599,7 @@ function createTelegramSocialSDK(bridge, log) {
|
|
|
1495
1599
|
return null;
|
|
1496
1600
|
} catch (err) {
|
|
1497
1601
|
if (err instanceof PluginSDKError) throw err;
|
|
1498
|
-
|
|
1602
|
+
log7.error("telegram.getChatInfo() failed:", err);
|
|
1499
1603
|
return null;
|
|
1500
1604
|
}
|
|
1501
1605
|
},
|
|
@@ -1548,12 +1652,14 @@ function createTelegramSocialSDK(bridge, log) {
|
|
|
1548
1652
|
}
|
|
1549
1653
|
if (result.users && result.users.length > 0) {
|
|
1550
1654
|
const user = result.users[0];
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1655
|
+
if (user instanceof Api.User) {
|
|
1656
|
+
return {
|
|
1657
|
+
id: Number(user.id),
|
|
1658
|
+
type: "user",
|
|
1659
|
+
username: user.username || void 0,
|
|
1660
|
+
title: user.firstName || void 0
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1557
1663
|
}
|
|
1558
1664
|
if (result.chats && result.chats.length > 0) {
|
|
1559
1665
|
const chat = result.chats[0];
|
|
@@ -1561,8 +1667,8 @@ function createTelegramSocialSDK(bridge, log) {
|
|
|
1561
1667
|
return {
|
|
1562
1668
|
id: Number(chat.id),
|
|
1563
1669
|
type,
|
|
1564
|
-
username: chat.username || void 0,
|
|
1565
|
-
title: chat.title || void 0
|
|
1670
|
+
username: chat instanceof Api.Channel ? chat.username || void 0 : void 0,
|
|
1671
|
+
title: chat instanceof Api.Channel || chat instanceof Api.Chat ? chat.title || void 0 : void 0
|
|
1566
1672
|
};
|
|
1567
1673
|
}
|
|
1568
1674
|
return null;
|
|
@@ -1586,20 +1692,23 @@ function createTelegramSocialSDK(bridge, log) {
|
|
|
1586
1692
|
filter: new Api.ChannelParticipantsRecent(),
|
|
1587
1693
|
offset: 0,
|
|
1588
1694
|
limit: limit ?? 100,
|
|
1589
|
-
hash: 0
|
|
1695
|
+
hash: toLong(0)
|
|
1590
1696
|
})
|
|
1591
1697
|
);
|
|
1592
1698
|
const resultData = result;
|
|
1593
|
-
return (resultData.users || []).map((user) =>
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1699
|
+
return (resultData.users || []).map((user) => {
|
|
1700
|
+
const u = user;
|
|
1701
|
+
return {
|
|
1702
|
+
id: Number(u.id),
|
|
1703
|
+
firstName: u.firstName || "",
|
|
1704
|
+
lastName: u.lastName || void 0,
|
|
1705
|
+
username: u.username || void 0,
|
|
1706
|
+
isBot: u.bot || false
|
|
1707
|
+
};
|
|
1708
|
+
});
|
|
1600
1709
|
} catch (err) {
|
|
1601
1710
|
if (err instanceof PluginSDKError) throw err;
|
|
1602
|
-
|
|
1711
|
+
log7.error("telegram.getParticipants() failed:", err);
|
|
1603
1712
|
return [];
|
|
1604
1713
|
}
|
|
1605
1714
|
},
|
|
@@ -1618,7 +1727,7 @@ function createTelegramSocialSDK(bridge, log) {
|
|
|
1618
1727
|
const anonymous = opts?.isAnonymous ?? true;
|
|
1619
1728
|
const multipleChoice = opts?.multipleChoice ?? false;
|
|
1620
1729
|
const poll = new Api.Poll({
|
|
1621
|
-
id:
|
|
1730
|
+
id: randomLong(),
|
|
1622
1731
|
question: new Api.TextWithEntities({ text: question, entities: [] }),
|
|
1623
1732
|
answers: answers.map(
|
|
1624
1733
|
(opt, idx) => new Api.PollAnswer({
|
|
@@ -1634,10 +1743,10 @@ function createTelegramSocialSDK(bridge, log) {
|
|
|
1634
1743
|
peer: chatId,
|
|
1635
1744
|
media: new Api.InputMediaPoll({ poll }),
|
|
1636
1745
|
message: "",
|
|
1637
|
-
randomId:
|
|
1746
|
+
randomId: randomLong()
|
|
1638
1747
|
})
|
|
1639
1748
|
);
|
|
1640
|
-
if (result
|
|
1749
|
+
if (result instanceof Api.Updates || result instanceof Api.UpdatesCombined) {
|
|
1641
1750
|
for (const update of result.updates) {
|
|
1642
1751
|
if (update.className === "UpdateNewMessage" || update.className === "UpdateNewChannelMessage") {
|
|
1643
1752
|
return update.message?.id ?? 0;
|
|
@@ -1671,7 +1780,7 @@ function createTelegramSocialSDK(bridge, log) {
|
|
|
1671
1780
|
const client = getClient();
|
|
1672
1781
|
const { Api } = await import("telegram");
|
|
1673
1782
|
const poll = new Api.Poll({
|
|
1674
|
-
id:
|
|
1783
|
+
id: randomLong(),
|
|
1675
1784
|
question: new Api.TextWithEntities({ text: question, entities: [] }),
|
|
1676
1785
|
answers: answers.map(
|
|
1677
1786
|
(opt, idx) => new Api.PollAnswer({
|
|
@@ -1693,10 +1802,10 @@ function createTelegramSocialSDK(bridge, log) {
|
|
|
1693
1802
|
solutionEntities: []
|
|
1694
1803
|
}),
|
|
1695
1804
|
message: "",
|
|
1696
|
-
randomId:
|
|
1805
|
+
randomId: randomLong()
|
|
1697
1806
|
})
|
|
1698
1807
|
);
|
|
1699
|
-
if (result
|
|
1808
|
+
if (result instanceof Api.Updates || result instanceof Api.UpdatesCombined) {
|
|
1700
1809
|
for (const update of result.updates) {
|
|
1701
1810
|
if (update.className === "UpdateNewMessage" || update.className === "UpdateNewChannelMessage") {
|
|
1702
1811
|
return update.message?.id ?? 0;
|
|
@@ -1962,12 +2071,32 @@ function createTelegramSocialSDK(bridge, log) {
|
|
|
1962
2071
|
const client = getClient();
|
|
1963
2072
|
const { Api, helpers } = await import("telegram");
|
|
1964
2073
|
const { CustomFile } = await import("telegram/client/uploads.js");
|
|
1965
|
-
const { readFileSync:
|
|
2074
|
+
const { readFileSync: readFileSync7, statSync: statSync2 } = await import("fs");
|
|
1966
2075
|
const { basename: basename2 } = await import("path");
|
|
1967
|
-
const
|
|
2076
|
+
const { resolve: resolve2, normalize: normalize2 } = await import("path");
|
|
2077
|
+
const { homedir: homedir3 } = await import("os");
|
|
2078
|
+
const { realpathSync } = await import("fs");
|
|
2079
|
+
const filePath = realpathSync(resolve2(normalize2(mediaPath)));
|
|
2080
|
+
const home = homedir3();
|
|
2081
|
+
const teletonWorkspace = `${home}/.teleton/workspace/`;
|
|
2082
|
+
const allowedPrefixes = [
|
|
2083
|
+
"/tmp/",
|
|
2084
|
+
`${home}/Downloads/`,
|
|
2085
|
+
`${home}/Pictures/`,
|
|
2086
|
+
`${home}/Videos/`,
|
|
2087
|
+
`${teletonWorkspace}uploads/`,
|
|
2088
|
+
`${teletonWorkspace}downloads/`,
|
|
2089
|
+
`${teletonWorkspace}memes/`
|
|
2090
|
+
];
|
|
2091
|
+
if (!allowedPrefixes.some((p) => filePath.startsWith(p))) {
|
|
2092
|
+
throw new PluginSDKError(
|
|
2093
|
+
"sendStory: media path must be within /tmp, Downloads, Pictures, or Videos",
|
|
2094
|
+
"OPERATION_FAILED"
|
|
2095
|
+
);
|
|
2096
|
+
}
|
|
1968
2097
|
const fileName = basename2(filePath);
|
|
1969
2098
|
const fileSize = statSync2(filePath).size;
|
|
1970
|
-
const fileBuffer =
|
|
2099
|
+
const fileBuffer = readFileSync7(filePath);
|
|
1971
2100
|
const isVideo = filePath.toLowerCase().match(/\.(mp4|mov|avi)$/);
|
|
1972
2101
|
const customFile = new CustomFile(fileName, fileSize, filePath, fileBuffer);
|
|
1973
2102
|
const uploadedFile = await client.uploadFile({
|
|
@@ -2004,7 +2133,8 @@ function createTelegramSocialSDK(bridge, log) {
|
|
|
2004
2133
|
randomId: helpers.generateRandomBigInt()
|
|
2005
2134
|
})
|
|
2006
2135
|
);
|
|
2007
|
-
|
|
2136
|
+
const storyUpdate = result instanceof Api.Updates ? result.updates.find((u) => u.className === "UpdateStory") : void 0;
|
|
2137
|
+
return storyUpdate?.story?.id ?? 0;
|
|
2008
2138
|
} catch (err) {
|
|
2009
2139
|
if (err instanceof PluginSDKError) throw err;
|
|
2010
2140
|
throw new PluginSDKError(
|
|
@@ -2017,7 +2147,7 @@ function createTelegramSocialSDK(bridge, log) {
|
|
|
2017
2147
|
}
|
|
2018
2148
|
|
|
2019
2149
|
// src/sdk/telegram.ts
|
|
2020
|
-
function createTelegramSDK(bridge,
|
|
2150
|
+
function createTelegramSDK(bridge, log7) {
|
|
2021
2151
|
function requireBridge() {
|
|
2022
2152
|
if (!bridge.isAvailable()) {
|
|
2023
2153
|
throw new PluginSDKError(
|
|
@@ -2073,17 +2203,17 @@ function createTelegramSDK(bridge, log) {
|
|
|
2073
2203
|
peer: chatId,
|
|
2074
2204
|
media: new Api.InputMediaDice({ emoticon }),
|
|
2075
2205
|
message: "",
|
|
2076
|
-
randomId:
|
|
2206
|
+
randomId: randomLong(),
|
|
2077
2207
|
replyTo: replyToId ? new Api.InputReplyToMessage({ replyToMsgId: replyToId }) : void 0
|
|
2078
2208
|
})
|
|
2079
2209
|
);
|
|
2080
2210
|
let value;
|
|
2081
2211
|
let messageId;
|
|
2082
|
-
if (result
|
|
2212
|
+
if (result instanceof Api.Updates || result instanceof Api.UpdatesCombined) {
|
|
2083
2213
|
for (const update of result.updates) {
|
|
2084
2214
|
if (update.className === "UpdateNewMessage" || update.className === "UpdateNewChannelMessage") {
|
|
2085
2215
|
const msg = update.message;
|
|
2086
|
-
if (msg
|
|
2216
|
+
if (msg instanceof Api.Message && msg.media?.className === "MessageMediaDice") {
|
|
2087
2217
|
value = msg.media.value;
|
|
2088
2218
|
messageId = msg.id;
|
|
2089
2219
|
break;
|
|
@@ -2127,7 +2257,7 @@ function createTelegramSDK(bridge, log) {
|
|
|
2127
2257
|
timestamp: m.timestamp
|
|
2128
2258
|
}));
|
|
2129
2259
|
} catch (err) {
|
|
2130
|
-
|
|
2260
|
+
log7.error("telegram.getMessages() failed:", err);
|
|
2131
2261
|
return [];
|
|
2132
2262
|
}
|
|
2133
2263
|
},
|
|
@@ -2149,6 +2279,7 @@ function createTelegramSDK(bridge, log) {
|
|
|
2149
2279
|
return bridge.isAvailable();
|
|
2150
2280
|
},
|
|
2151
2281
|
getRawClient() {
|
|
2282
|
+
log7.warn("getRawClient() called \u2014 this bypasses SDK sandbox guarantees");
|
|
2152
2283
|
if (!bridge.isAvailable()) return null;
|
|
2153
2284
|
try {
|
|
2154
2285
|
return bridge.getClient().getClient();
|
|
@@ -2157,13 +2288,13 @@ function createTelegramSDK(bridge, log) {
|
|
|
2157
2288
|
}
|
|
2158
2289
|
},
|
|
2159
2290
|
// Spread extended methods from sub-modules
|
|
2160
|
-
...createTelegramMessagesSDK(bridge,
|
|
2161
|
-
...createTelegramSocialSDK(bridge,
|
|
2291
|
+
...createTelegramMessagesSDK(bridge, log7),
|
|
2292
|
+
...createTelegramSocialSDK(bridge, log7)
|
|
2162
2293
|
};
|
|
2163
2294
|
}
|
|
2164
2295
|
|
|
2165
2296
|
// src/sdk/secrets.ts
|
|
2166
|
-
import { readFileSync as
|
|
2297
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, existsSync as existsSync5 } from "fs";
|
|
2167
2298
|
import { join as join3 } from "path";
|
|
2168
2299
|
var SECRETS_DIR = join3(TELETON_ROOT, "plugins", "data");
|
|
2169
2300
|
function getSecretsPath(pluginName) {
|
|
@@ -2172,8 +2303,8 @@ function getSecretsPath(pluginName) {
|
|
|
2172
2303
|
function readSecretsFile(pluginName) {
|
|
2173
2304
|
const filePath = getSecretsPath(pluginName);
|
|
2174
2305
|
try {
|
|
2175
|
-
if (!
|
|
2176
|
-
const raw =
|
|
2306
|
+
if (!existsSync5(filePath)) return {};
|
|
2307
|
+
const raw = readFileSync4(filePath, "utf-8");
|
|
2177
2308
|
const parsed = JSON.parse(raw);
|
|
2178
2309
|
if (typeof parsed !== "object" || parsed === null) return {};
|
|
2179
2310
|
return parsed;
|
|
@@ -2182,40 +2313,40 @@ function readSecretsFile(pluginName) {
|
|
|
2182
2313
|
}
|
|
2183
2314
|
}
|
|
2184
2315
|
function writePluginSecret(pluginName, key, value) {
|
|
2185
|
-
mkdirSync3(SECRETS_DIR, { recursive: true });
|
|
2316
|
+
mkdirSync3(SECRETS_DIR, { recursive: true, mode: 448 });
|
|
2186
2317
|
const filePath = getSecretsPath(pluginName);
|
|
2187
2318
|
const existing = readSecretsFile(pluginName);
|
|
2188
2319
|
existing[key] = value;
|
|
2189
|
-
|
|
2320
|
+
writeFileSync2(filePath, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
2190
2321
|
}
|
|
2191
2322
|
function deletePluginSecret(pluginName, key) {
|
|
2192
2323
|
const existing = readSecretsFile(pluginName);
|
|
2193
2324
|
if (!(key in existing)) return false;
|
|
2194
2325
|
delete existing[key];
|
|
2195
2326
|
const filePath = getSecretsPath(pluginName);
|
|
2196
|
-
|
|
2327
|
+
writeFileSync2(filePath, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
2197
2328
|
return true;
|
|
2198
2329
|
}
|
|
2199
2330
|
function listPluginSecretKeys(pluginName) {
|
|
2200
2331
|
return Object.keys(readSecretsFile(pluginName));
|
|
2201
2332
|
}
|
|
2202
|
-
function createSecretsSDK(pluginName, pluginConfig,
|
|
2333
|
+
function createSecretsSDK(pluginName, pluginConfig, log7) {
|
|
2203
2334
|
const envPrefix = pluginName.replace(/-/g, "_").toUpperCase();
|
|
2204
2335
|
function get(key) {
|
|
2205
2336
|
const envKey = `${envPrefix}_${key.toUpperCase()}`;
|
|
2206
2337
|
const envValue = process.env[envKey];
|
|
2207
2338
|
if (envValue) {
|
|
2208
|
-
|
|
2339
|
+
log7.debug(`Secret "${key}" resolved from env var ${envKey}`);
|
|
2209
2340
|
return envValue;
|
|
2210
2341
|
}
|
|
2211
2342
|
const stored = readSecretsFile(pluginName);
|
|
2212
2343
|
if (key in stored && stored[key]) {
|
|
2213
|
-
|
|
2344
|
+
log7.debug(`Secret "${key}" resolved from secrets store`);
|
|
2214
2345
|
return stored[key];
|
|
2215
2346
|
}
|
|
2216
2347
|
const configValue = pluginConfig[key];
|
|
2217
2348
|
if (configValue !== void 0 && configValue !== null) {
|
|
2218
|
-
|
|
2349
|
+
log7.debug(`Secret "${key}" resolved from pluginConfig`);
|
|
2219
2350
|
return String(configValue);
|
|
2220
2351
|
}
|
|
2221
2352
|
return void 0;
|
|
@@ -2307,38 +2438,61 @@ function createStorageSDK(db) {
|
|
|
2307
2438
|
}
|
|
2308
2439
|
|
|
2309
2440
|
// src/sdk/index.ts
|
|
2441
|
+
var sdkLog = createLogger("SDK");
|
|
2442
|
+
var BLOCKED_SQL_RE = /\b(ATTACH|DETACH)\s+DATABASE\b/i;
|
|
2443
|
+
function createSafeDb(db) {
|
|
2444
|
+
return new Proxy(db, {
|
|
2445
|
+
get(target, prop, receiver) {
|
|
2446
|
+
const value = Reflect.get(target, prop, receiver);
|
|
2447
|
+
if (prop === "exec") {
|
|
2448
|
+
return (sql) => {
|
|
2449
|
+
if (BLOCKED_SQL_RE.test(sql)) {
|
|
2450
|
+
throw new Error("ATTACH/DETACH DATABASE is not allowed in plugin context");
|
|
2451
|
+
}
|
|
2452
|
+
return target.exec(sql);
|
|
2453
|
+
};
|
|
2454
|
+
}
|
|
2455
|
+
if (prop === "prepare") {
|
|
2456
|
+
return (sql) => {
|
|
2457
|
+
if (BLOCKED_SQL_RE.test(sql)) {
|
|
2458
|
+
throw new Error("ATTACH/DETACH DATABASE is not allowed in plugin context");
|
|
2459
|
+
}
|
|
2460
|
+
return target.prepare(sql);
|
|
2461
|
+
};
|
|
2462
|
+
}
|
|
2463
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
2464
|
+
}
|
|
2465
|
+
});
|
|
2466
|
+
}
|
|
2310
2467
|
function createPluginSDK(deps, opts) {
|
|
2311
|
-
const
|
|
2312
|
-
const
|
|
2313
|
-
const
|
|
2314
|
-
const
|
|
2315
|
-
const
|
|
2316
|
-
const
|
|
2468
|
+
const log7 = createLogger2(opts.pluginName);
|
|
2469
|
+
const safeDb = opts.db ? createSafeDb(opts.db) : null;
|
|
2470
|
+
const ton = Object.freeze(createTonSDK(log7, safeDb));
|
|
2471
|
+
const telegram = Object.freeze(createTelegramSDK(deps.bridge, log7));
|
|
2472
|
+
const secrets = Object.freeze(createSecretsSDK(opts.pluginName, opts.pluginConfig, log7));
|
|
2473
|
+
const storage = safeDb ? Object.freeze(createStorageSDK(safeDb)) : null;
|
|
2474
|
+
const frozenLog = Object.freeze(log7);
|
|
2317
2475
|
const frozenConfig = Object.freeze(opts.sanitizedConfig);
|
|
2318
|
-
const frozenPluginConfig = Object.freeze(opts.pluginConfig);
|
|
2476
|
+
const frozenPluginConfig = Object.freeze(JSON.parse(JSON.stringify(opts.pluginConfig ?? {})));
|
|
2319
2477
|
return Object.freeze({
|
|
2320
2478
|
version: SDK_VERSION,
|
|
2321
2479
|
ton,
|
|
2322
2480
|
telegram,
|
|
2323
2481
|
secrets,
|
|
2324
2482
|
storage,
|
|
2325
|
-
db:
|
|
2483
|
+
db: safeDb,
|
|
2326
2484
|
config: frozenConfig,
|
|
2327
2485
|
pluginConfig: frozenPluginConfig,
|
|
2328
2486
|
log: frozenLog
|
|
2329
2487
|
});
|
|
2330
2488
|
}
|
|
2331
|
-
function
|
|
2332
|
-
const
|
|
2489
|
+
function createLogger2(pluginName) {
|
|
2490
|
+
const pinoChild = createLogger(`plugin:${pluginName}`);
|
|
2333
2491
|
return {
|
|
2334
|
-
info: (...args) =>
|
|
2335
|
-
warn: (...args) =>
|
|
2336
|
-
error: (...args) =>
|
|
2337
|
-
debug: (...args) =>
|
|
2338
|
-
if (process.env.DEBUG || process.env.VERBOSE) {
|
|
2339
|
-
console.log(`\u{1F50D} ${prefix}`, ...args);
|
|
2340
|
-
}
|
|
2341
|
-
}
|
|
2492
|
+
info: (...args) => pinoChild.info(args.map(String).join(" ")),
|
|
2493
|
+
warn: (...args) => pinoChild.warn(args.map(String).join(" ")),
|
|
2494
|
+
error: (...args) => pinoChild.error(args.map(String).join(" ")),
|
|
2495
|
+
debug: (...args) => pinoChild.debug(args.map(String).join(" "))
|
|
2342
2496
|
};
|
|
2343
2497
|
}
|
|
2344
2498
|
function parseSemver(v) {
|
|
@@ -2358,22 +2512,22 @@ function semverGte(a, b) {
|
|
|
2358
2512
|
function semverSatisfies(current, range) {
|
|
2359
2513
|
const cur = parseSemver(current);
|
|
2360
2514
|
if (!cur) {
|
|
2361
|
-
|
|
2362
|
-
return
|
|
2515
|
+
sdkLog.warn(`[SDK] Could not parse current version "${current}", rejecting`);
|
|
2516
|
+
return false;
|
|
2363
2517
|
}
|
|
2364
2518
|
if (range.startsWith(">=")) {
|
|
2365
2519
|
const req2 = parseSemver(range.slice(2));
|
|
2366
2520
|
if (!req2) {
|
|
2367
|
-
|
|
2368
|
-
return
|
|
2521
|
+
sdkLog.warn(`[SDK] Malformed sdkVersion range "${range}", rejecting`);
|
|
2522
|
+
return false;
|
|
2369
2523
|
}
|
|
2370
2524
|
return semverGte(cur, req2);
|
|
2371
2525
|
}
|
|
2372
2526
|
if (range.startsWith("^")) {
|
|
2373
2527
|
const req2 = parseSemver(range.slice(1));
|
|
2374
2528
|
if (!req2) {
|
|
2375
|
-
|
|
2376
|
-
return
|
|
2529
|
+
sdkLog.warn(`[SDK] Malformed sdkVersion range "${range}", rejecting`);
|
|
2530
|
+
return false;
|
|
2377
2531
|
}
|
|
2378
2532
|
if (req2.major === 0) {
|
|
2379
2533
|
return cur.major === 0 && cur.minor === req2.minor && semverGte(cur, req2);
|
|
@@ -2382,14 +2536,15 @@ function semverSatisfies(current, range) {
|
|
|
2382
2536
|
}
|
|
2383
2537
|
const req = parseSemver(range);
|
|
2384
2538
|
if (!req) {
|
|
2385
|
-
|
|
2386
|
-
return
|
|
2539
|
+
sdkLog.warn(`[SDK] Malformed sdkVersion "${range}", rejecting`);
|
|
2540
|
+
return false;
|
|
2387
2541
|
}
|
|
2388
2542
|
return cur.major === req.major && cur.minor === req.minor && cur.patch === req.patch;
|
|
2389
2543
|
}
|
|
2390
2544
|
|
|
2391
2545
|
// src/agent/tools/plugin-loader.ts
|
|
2392
2546
|
var execFileAsync = promisify(execFile);
|
|
2547
|
+
var log6 = createLogger("PluginLoader");
|
|
2393
2548
|
var PLUGIN_DATA_DIR = join4(TELETON_ROOT, "plugins", "data");
|
|
2394
2549
|
function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps) {
|
|
2395
2550
|
let manifest = null;
|
|
@@ -2397,17 +2552,16 @@ function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps) {
|
|
|
2397
2552
|
try {
|
|
2398
2553
|
manifest = validateManifest(raw.manifest);
|
|
2399
2554
|
} catch (err) {
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
err instanceof Error ? err.message : err
|
|
2555
|
+
log6.warn(
|
|
2556
|
+
`[${entryName}] invalid manifest, ignoring: ${err instanceof Error ? err.message : err}`
|
|
2403
2557
|
);
|
|
2404
2558
|
}
|
|
2405
2559
|
}
|
|
2406
2560
|
if (!manifest) {
|
|
2407
2561
|
const manifestPath = join4(WORKSPACE_PATHS.PLUGINS_DIR, entryName, "manifest.json");
|
|
2408
2562
|
try {
|
|
2409
|
-
if (
|
|
2410
|
-
const diskManifest = JSON.parse(
|
|
2563
|
+
if (existsSync6(manifestPath)) {
|
|
2564
|
+
const diskManifest = JSON.parse(readFileSync5(manifestPath, "utf-8"));
|
|
2411
2565
|
if (diskManifest && typeof diskManifest.version === "string") {
|
|
2412
2566
|
manifest = {
|
|
2413
2567
|
name: entryName,
|
|
@@ -2439,12 +2593,13 @@ function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps) {
|
|
|
2439
2593
|
const pluginConfigKey = pluginName.replace(/-/g, "_");
|
|
2440
2594
|
const rawPluginConfig = config.plugins?.[pluginConfigKey] ?? {};
|
|
2441
2595
|
const pluginConfig = { ...manifest?.defaultConfig, ...rawPluginConfig };
|
|
2442
|
-
const
|
|
2596
|
+
const pluginLog = createLogger(`Plugin:${pluginName}`);
|
|
2597
|
+
const logFn = (...args) => pluginLog.info(args.map(String).join(" "));
|
|
2443
2598
|
if (manifest?.secrets) {
|
|
2444
2599
|
const dummyLogger = {
|
|
2445
|
-
info:
|
|
2446
|
-
warn: (...a) =>
|
|
2447
|
-
error: (...a) =>
|
|
2600
|
+
info: (...a) => pluginLog.info(a.map(String).join(" ")),
|
|
2601
|
+
warn: (...a) => pluginLog.warn(a.map(String).join(" ")),
|
|
2602
|
+
error: (...a) => pluginLog.error(a.map(String).join(" ")),
|
|
2448
2603
|
debug: () => {
|
|
2449
2604
|
}
|
|
2450
2605
|
};
|
|
@@ -2458,8 +2613,8 @@ function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps) {
|
|
|
2458
2613
|
}
|
|
2459
2614
|
}
|
|
2460
2615
|
if (missing.length > 0) {
|
|
2461
|
-
|
|
2462
|
-
|
|
2616
|
+
pluginLog.warn(
|
|
2617
|
+
`Missing required secrets:
|
|
2463
2618
|
` + missing.map((m) => ` \u2022 ${m}`).join("\n") + `
|
|
2464
2619
|
Set via: /plugin set ${pluginName} <key> <value>`
|
|
2465
2620
|
);
|
|
@@ -2492,10 +2647,7 @@ function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps) {
|
|
|
2492
2647
|
}
|
|
2493
2648
|
}
|
|
2494
2649
|
} catch (err) {
|
|
2495
|
-
|
|
2496
|
-
`\u274C [${pluginName}] migrate() failed:`,
|
|
2497
|
-
err instanceof Error ? err.message : err
|
|
2498
|
-
);
|
|
2650
|
+
pluginLog.error(`migrate() failed: ${err instanceof Error ? err.message : err}`);
|
|
2499
2651
|
if (pluginDb) {
|
|
2500
2652
|
try {
|
|
2501
2653
|
pluginDb.close();
|
|
@@ -2546,10 +2698,7 @@ function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps) {
|
|
|
2546
2698
|
};
|
|
2547
2699
|
});
|
|
2548
2700
|
} catch (err) {
|
|
2549
|
-
|
|
2550
|
-
`\u274C [${pluginName}] tools() failed:`,
|
|
2551
|
-
err instanceof Error ? err.message : err
|
|
2552
|
-
);
|
|
2701
|
+
pluginLog.error(`tools() failed: ${err instanceof Error ? err.message : err}`);
|
|
2553
2702
|
return [];
|
|
2554
2703
|
}
|
|
2555
2704
|
},
|
|
@@ -2561,24 +2710,18 @@ function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps) {
|
|
|
2561
2710
|
db: pluginDb ?? null,
|
|
2562
2711
|
config: sanitizedConfig,
|
|
2563
2712
|
pluginConfig,
|
|
2564
|
-
log
|
|
2713
|
+
log: logFn
|
|
2565
2714
|
};
|
|
2566
2715
|
await raw.start(enhancedContext);
|
|
2567
2716
|
} catch (err) {
|
|
2568
|
-
|
|
2569
|
-
`\u274C [${pluginName}] start() failed:`,
|
|
2570
|
-
err instanceof Error ? err.message : err
|
|
2571
|
-
);
|
|
2717
|
+
pluginLog.error(`start() failed: ${err instanceof Error ? err.message : err}`);
|
|
2572
2718
|
}
|
|
2573
2719
|
},
|
|
2574
2720
|
async stop() {
|
|
2575
2721
|
try {
|
|
2576
2722
|
await raw.stop?.();
|
|
2577
2723
|
} catch (err) {
|
|
2578
|
-
|
|
2579
|
-
`\u274C [${pluginName}] stop() failed:`,
|
|
2580
|
-
err instanceof Error ? err.message : err
|
|
2581
|
-
);
|
|
2724
|
+
pluginLog.error(`stop() failed: ${err instanceof Error ? err.message : err}`);
|
|
2582
2725
|
} finally {
|
|
2583
2726
|
if (pluginDb) {
|
|
2584
2727
|
try {
|
|
@@ -2596,35 +2739,35 @@ async function ensurePluginDeps(pluginDir, pluginEntry) {
|
|
|
2596
2739
|
const pkgJson = join4(pluginDir, "package.json");
|
|
2597
2740
|
const lockfile = join4(pluginDir, "package-lock.json");
|
|
2598
2741
|
const nodeModules = join4(pluginDir, "node_modules");
|
|
2599
|
-
if (!
|
|
2600
|
-
if (!
|
|
2601
|
-
|
|
2602
|
-
|
|
2742
|
+
if (!existsSync6(pkgJson)) return;
|
|
2743
|
+
if (!existsSync6(lockfile)) {
|
|
2744
|
+
log6.warn(
|
|
2745
|
+
`[${pluginEntry}] package.json without package-lock.json \u2014 skipping (lockfile required)`
|
|
2603
2746
|
);
|
|
2604
2747
|
return;
|
|
2605
2748
|
}
|
|
2606
|
-
if (
|
|
2749
|
+
if (existsSync6(nodeModules)) {
|
|
2607
2750
|
const marker = join4(nodeModules, ".package-lock.json");
|
|
2608
|
-
if (
|
|
2751
|
+
if (existsSync6(marker) && statSync(marker).mtimeMs >= statSync(lockfile).mtimeMs) return;
|
|
2609
2752
|
}
|
|
2610
|
-
|
|
2753
|
+
log6.info(`[${pluginEntry}] Installing dependencies...`);
|
|
2611
2754
|
try {
|
|
2612
2755
|
await execFileAsync("npm", ["ci", "--ignore-scripts", "--no-audit", "--no-fund"], {
|
|
2613
2756
|
cwd: pluginDir,
|
|
2614
2757
|
timeout: 6e4,
|
|
2615
2758
|
env: { ...process.env, NODE_ENV: "production" }
|
|
2616
2759
|
});
|
|
2617
|
-
|
|
2760
|
+
log6.info(`[${pluginEntry}] Dependencies installed`);
|
|
2618
2761
|
} catch (err) {
|
|
2619
|
-
|
|
2762
|
+
log6.error(`[${pluginEntry}] Failed to install deps: ${String(err).slice(0, 300)}`);
|
|
2620
2763
|
}
|
|
2621
2764
|
}
|
|
2622
2765
|
async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps) {
|
|
2623
2766
|
const pluginsDir = WORKSPACE_PATHS.PLUGINS_DIR;
|
|
2624
|
-
if (!
|
|
2767
|
+
if (!existsSync6(pluginsDir)) {
|
|
2625
2768
|
return [];
|
|
2626
2769
|
}
|
|
2627
|
-
const entries =
|
|
2770
|
+
const entries = readdirSync2(pluginsDir);
|
|
2628
2771
|
const modules = [];
|
|
2629
2772
|
const loadedNames = /* @__PURE__ */ new Set();
|
|
2630
2773
|
const pluginPaths = [];
|
|
@@ -2638,7 +2781,7 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps) {
|
|
|
2638
2781
|
modulePath = entryPath;
|
|
2639
2782
|
} else if (stat.isDirectory()) {
|
|
2640
2783
|
const indexPath = join4(entryPath, "index.js");
|
|
2641
|
-
if (
|
|
2784
|
+
if (existsSync6(indexPath)) {
|
|
2642
2785
|
modulePath = indexPath;
|
|
2643
2786
|
}
|
|
2644
2787
|
}
|
|
@@ -2661,156 +2804,402 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps) {
|
|
|
2661
2804
|
);
|
|
2662
2805
|
for (const result of loadResults) {
|
|
2663
2806
|
if (result.status === "rejected") {
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
result.reason instanceof Error ? result.reason.message : result.reason
|
|
2807
|
+
log6.error(
|
|
2808
|
+
`Plugin failed to load: ${result.reason instanceof Error ? result.reason.message : result.reason}`
|
|
2667
2809
|
);
|
|
2668
2810
|
continue;
|
|
2669
2811
|
}
|
|
2670
2812
|
const { entry, mod } = result.value;
|
|
2671
2813
|
try {
|
|
2672
2814
|
if (!mod.tools || typeof mod.tools !== "function" && !Array.isArray(mod.tools)) {
|
|
2673
|
-
|
|
2815
|
+
log6.warn(`Plugin "${entry}": no 'tools' array or function exported, skipping`);
|
|
2674
2816
|
continue;
|
|
2675
2817
|
}
|
|
2676
2818
|
const adapted = adaptPlugin(mod, entry, config, loadedModuleNames, sdkDeps);
|
|
2677
2819
|
if (loadedNames.has(adapted.name)) {
|
|
2678
|
-
|
|
2679
|
-
`\u26A0\uFE0F Plugin "${adapted.name}" already loaded, skipping duplicate from "${entry}"`
|
|
2680
|
-
);
|
|
2820
|
+
log6.warn(`Plugin "${adapted.name}" already loaded, skipping duplicate from "${entry}"`);
|
|
2681
2821
|
continue;
|
|
2682
2822
|
}
|
|
2683
2823
|
loadedNames.add(adapted.name);
|
|
2684
2824
|
modules.push(adapted);
|
|
2685
2825
|
} catch (err) {
|
|
2686
|
-
|
|
2687
|
-
`\u274C Plugin "${entry}" failed to adapt:`,
|
|
2688
|
-
err instanceof Error ? err.message : err
|
|
2689
|
-
);
|
|
2826
|
+
log6.error(`Plugin "${entry}" failed to adapt: ${err instanceof Error ? err.message : err}`);
|
|
2690
2827
|
}
|
|
2691
2828
|
}
|
|
2692
2829
|
return modules;
|
|
2693
2830
|
}
|
|
2694
2831
|
|
|
2695
|
-
// src/
|
|
2696
|
-
import {
|
|
2697
|
-
import {
|
|
2698
|
-
|
|
2699
|
-
var
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2832
|
+
// src/config/configurable-keys.ts
|
|
2833
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync7 } from "fs";
|
|
2834
|
+
import { parse as parse2, stringify as stringify2 } from "yaml";
|
|
2835
|
+
var noValidation = () => void 0;
|
|
2836
|
+
var identity = (v) => v;
|
|
2837
|
+
var nonEmpty = (v) => v.length > 0 ? void 0 : "Must not be empty";
|
|
2838
|
+
function numberInRange(min, max) {
|
|
2839
|
+
return (v) => {
|
|
2840
|
+
const n = Number(v);
|
|
2841
|
+
if (isNaN(n)) return "Must be a number";
|
|
2842
|
+
if (n < min || n > max) return `Must be between ${min} and ${max}`;
|
|
2843
|
+
return void 0;
|
|
2844
|
+
};
|
|
2845
|
+
}
|
|
2846
|
+
function enumValidator(options) {
|
|
2847
|
+
return (v) => options.includes(v) ? void 0 : `Must be one of: ${options.join(", ")}`;
|
|
2848
|
+
}
|
|
2849
|
+
var CONFIGURABLE_KEYS = {
|
|
2850
|
+
// ─── API Keys ──────────────────────────────────────────────────────
|
|
2851
|
+
"agent.api_key": {
|
|
2852
|
+
type: "string",
|
|
2853
|
+
category: "API Keys",
|
|
2854
|
+
description: "LLM provider API key",
|
|
2855
|
+
sensitive: true,
|
|
2856
|
+
validate: (v) => v.length >= 10 ? void 0 : "Must be at least 10 characters",
|
|
2857
|
+
mask: (v) => v.slice(0, 8) + "****",
|
|
2858
|
+
parse: identity
|
|
2859
|
+
},
|
|
2860
|
+
tavily_api_key: {
|
|
2861
|
+
type: "string",
|
|
2862
|
+
category: "API Keys",
|
|
2863
|
+
description: "Tavily API key for web search",
|
|
2864
|
+
sensitive: true,
|
|
2865
|
+
validate: (v) => v.startsWith("tvly-") ? void 0 : "Must start with 'tvly-'",
|
|
2866
|
+
mask: (v) => v.slice(0, 9) + "****",
|
|
2867
|
+
parse: identity
|
|
2868
|
+
},
|
|
2869
|
+
tonapi_key: {
|
|
2870
|
+
type: "string",
|
|
2871
|
+
category: "API Keys",
|
|
2872
|
+
description: "TonAPI key for higher rate limits",
|
|
2873
|
+
sensitive: true,
|
|
2874
|
+
validate: (v) => v.length >= 10 ? void 0 : "Must be at least 10 characters",
|
|
2875
|
+
mask: (v) => v.slice(0, 10) + "****",
|
|
2876
|
+
parse: identity
|
|
2877
|
+
},
|
|
2878
|
+
"telegram.bot_token": {
|
|
2879
|
+
type: "string",
|
|
2880
|
+
category: "API Keys",
|
|
2881
|
+
description: "Bot token from @BotFather",
|
|
2882
|
+
sensitive: true,
|
|
2883
|
+
validate: (v) => v.includes(":") ? void 0 : "Must contain ':' (e.g., 123456:ABC...)",
|
|
2884
|
+
mask: (v) => v.split(":")[0] + ":****",
|
|
2885
|
+
parse: identity
|
|
2886
|
+
},
|
|
2887
|
+
// ─── Agent ─────────────────────────────────────────────────────────
|
|
2888
|
+
"agent.provider": {
|
|
2889
|
+
type: "enum",
|
|
2890
|
+
category: "Agent",
|
|
2891
|
+
description: "LLM provider",
|
|
2892
|
+
sensitive: false,
|
|
2893
|
+
options: [
|
|
2894
|
+
"anthropic",
|
|
2895
|
+
"openai",
|
|
2896
|
+
"google",
|
|
2897
|
+
"xai",
|
|
2898
|
+
"groq",
|
|
2899
|
+
"openrouter",
|
|
2900
|
+
"moonshot",
|
|
2901
|
+
"mistral",
|
|
2902
|
+
"cocoon",
|
|
2903
|
+
"local"
|
|
2904
|
+
],
|
|
2905
|
+
validate: enumValidator([
|
|
2906
|
+
"anthropic",
|
|
2907
|
+
"openai",
|
|
2908
|
+
"google",
|
|
2909
|
+
"xai",
|
|
2910
|
+
"groq",
|
|
2911
|
+
"openrouter",
|
|
2912
|
+
"moonshot",
|
|
2913
|
+
"mistral",
|
|
2914
|
+
"cocoon",
|
|
2915
|
+
"local"
|
|
2916
|
+
]),
|
|
2917
|
+
mask: identity,
|
|
2918
|
+
parse: identity
|
|
2919
|
+
},
|
|
2920
|
+
"agent.model": {
|
|
2921
|
+
type: "string",
|
|
2922
|
+
category: "Agent",
|
|
2923
|
+
description: "Main LLM model ID",
|
|
2924
|
+
sensitive: false,
|
|
2925
|
+
validate: nonEmpty,
|
|
2926
|
+
mask: identity,
|
|
2927
|
+
parse: identity
|
|
2928
|
+
},
|
|
2929
|
+
"agent.utility_model": {
|
|
2930
|
+
type: "string",
|
|
2931
|
+
category: "Agent",
|
|
2932
|
+
description: "Cheap model for summarization (auto-detected if empty)",
|
|
2933
|
+
sensitive: false,
|
|
2934
|
+
validate: noValidation,
|
|
2935
|
+
mask: identity,
|
|
2936
|
+
parse: identity
|
|
2937
|
+
},
|
|
2938
|
+
"agent.temperature": {
|
|
2939
|
+
type: "number",
|
|
2940
|
+
category: "Agent",
|
|
2941
|
+
description: "Response creativity (0.0 = deterministic, 2.0 = max)",
|
|
2942
|
+
sensitive: false,
|
|
2943
|
+
validate: numberInRange(0, 2),
|
|
2944
|
+
mask: identity,
|
|
2945
|
+
parse: (v) => Number(v)
|
|
2946
|
+
},
|
|
2947
|
+
"agent.max_tokens": {
|
|
2948
|
+
type: "number",
|
|
2949
|
+
category: "Agent",
|
|
2950
|
+
description: "Maximum response length in tokens",
|
|
2951
|
+
sensitive: false,
|
|
2952
|
+
validate: numberInRange(256, 128e3),
|
|
2953
|
+
mask: identity,
|
|
2954
|
+
parse: (v) => Number(v)
|
|
2955
|
+
},
|
|
2956
|
+
"agent.max_agentic_iterations": {
|
|
2957
|
+
type: "number",
|
|
2958
|
+
category: "Agent",
|
|
2959
|
+
description: "Max tool-call loop iterations per message",
|
|
2960
|
+
sensitive: false,
|
|
2961
|
+
validate: numberInRange(1, 20),
|
|
2962
|
+
mask: identity,
|
|
2963
|
+
parse: (v) => Number(v)
|
|
2964
|
+
},
|
|
2965
|
+
// ─── Session ───────────────────────────────────────────────────
|
|
2966
|
+
"agent.session_reset_policy.daily_reset_enabled": {
|
|
2967
|
+
type: "boolean",
|
|
2968
|
+
category: "Session",
|
|
2969
|
+
description: "Enable daily session reset at specified hour",
|
|
2970
|
+
sensitive: false,
|
|
2971
|
+
validate: enumValidator(["true", "false"]),
|
|
2972
|
+
mask: identity,
|
|
2973
|
+
parse: (v) => v === "true"
|
|
2974
|
+
},
|
|
2975
|
+
"agent.session_reset_policy.daily_reset_hour": {
|
|
2976
|
+
type: "number",
|
|
2977
|
+
category: "Session",
|
|
2978
|
+
description: "Hour (0-23 UTC) for daily session reset",
|
|
2979
|
+
sensitive: false,
|
|
2980
|
+
validate: numberInRange(0, 23),
|
|
2981
|
+
mask: identity,
|
|
2982
|
+
parse: (v) => Number(v)
|
|
2983
|
+
},
|
|
2984
|
+
"agent.session_reset_policy.idle_expiry_enabled": {
|
|
2985
|
+
type: "boolean",
|
|
2986
|
+
category: "Session",
|
|
2987
|
+
description: "Enable automatic session expiry after idle period",
|
|
2988
|
+
sensitive: false,
|
|
2989
|
+
validate: enumValidator(["true", "false"]),
|
|
2990
|
+
mask: identity,
|
|
2991
|
+
parse: (v) => v === "true"
|
|
2992
|
+
},
|
|
2993
|
+
"agent.session_reset_policy.idle_expiry_minutes": {
|
|
2994
|
+
type: "number",
|
|
2995
|
+
category: "Session",
|
|
2996
|
+
description: "Idle minutes before session expires (minimum 1)",
|
|
2997
|
+
sensitive: false,
|
|
2998
|
+
validate: numberInRange(1, Number.MAX_SAFE_INTEGER),
|
|
2999
|
+
mask: identity,
|
|
3000
|
+
parse: (v) => Number(v)
|
|
3001
|
+
},
|
|
3002
|
+
// ─── Telegram ──────────────────────────────────────────────────────
|
|
3003
|
+
"telegram.bot_username": {
|
|
3004
|
+
type: "string",
|
|
3005
|
+
category: "Telegram",
|
|
3006
|
+
description: "Bot username without @",
|
|
3007
|
+
sensitive: false,
|
|
3008
|
+
validate: (v) => v.length >= 3 ? void 0 : "Must be at least 3 characters",
|
|
3009
|
+
mask: identity,
|
|
3010
|
+
parse: identity
|
|
3011
|
+
},
|
|
3012
|
+
"telegram.dm_policy": {
|
|
3013
|
+
type: "enum",
|
|
3014
|
+
category: "Telegram",
|
|
3015
|
+
description: "DM access policy",
|
|
3016
|
+
sensitive: false,
|
|
3017
|
+
options: ["pairing", "allowlist", "open", "disabled"],
|
|
3018
|
+
validate: enumValidator(["pairing", "allowlist", "open", "disabled"]),
|
|
3019
|
+
mask: identity,
|
|
3020
|
+
parse: identity
|
|
3021
|
+
},
|
|
3022
|
+
"telegram.group_policy": {
|
|
3023
|
+
type: "enum",
|
|
3024
|
+
category: "Telegram",
|
|
3025
|
+
description: "Group access policy",
|
|
3026
|
+
sensitive: false,
|
|
3027
|
+
options: ["open", "allowlist", "disabled"],
|
|
3028
|
+
validate: enumValidator(["open", "allowlist", "disabled"]),
|
|
3029
|
+
mask: identity,
|
|
3030
|
+
parse: identity
|
|
3031
|
+
},
|
|
3032
|
+
"telegram.require_mention": {
|
|
3033
|
+
type: "boolean",
|
|
3034
|
+
category: "Telegram",
|
|
3035
|
+
description: "Require @mention in groups to respond",
|
|
3036
|
+
sensitive: false,
|
|
3037
|
+
validate: enumValidator(["true", "false"]),
|
|
3038
|
+
mask: identity,
|
|
3039
|
+
parse: (v) => v === "true"
|
|
3040
|
+
},
|
|
3041
|
+
"telegram.owner_name": {
|
|
3042
|
+
type: "string",
|
|
3043
|
+
category: "Telegram",
|
|
3044
|
+
description: "Owner's first name (used in system prompt)",
|
|
3045
|
+
sensitive: false,
|
|
3046
|
+
validate: noValidation,
|
|
3047
|
+
mask: identity,
|
|
3048
|
+
parse: identity
|
|
3049
|
+
},
|
|
3050
|
+
"telegram.owner_username": {
|
|
3051
|
+
type: "string",
|
|
3052
|
+
category: "Telegram",
|
|
3053
|
+
description: "Owner's Telegram username (without @)",
|
|
3054
|
+
sensitive: false,
|
|
3055
|
+
validate: noValidation,
|
|
3056
|
+
mask: identity,
|
|
3057
|
+
parse: identity
|
|
3058
|
+
},
|
|
3059
|
+
"telegram.debounce_ms": {
|
|
3060
|
+
type: "number",
|
|
3061
|
+
category: "Telegram",
|
|
3062
|
+
description: "Group message debounce delay in ms (0 = disabled)",
|
|
3063
|
+
sensitive: false,
|
|
3064
|
+
validate: numberInRange(0, 1e4),
|
|
3065
|
+
mask: identity,
|
|
3066
|
+
parse: (v) => Number(v)
|
|
3067
|
+
},
|
|
3068
|
+
"telegram.agent_channel": {
|
|
3069
|
+
type: "string",
|
|
3070
|
+
category: "Telegram",
|
|
3071
|
+
description: "Channel username for auto-publishing",
|
|
3072
|
+
sensitive: false,
|
|
3073
|
+
validate: noValidation,
|
|
3074
|
+
mask: identity,
|
|
3075
|
+
parse: identity
|
|
3076
|
+
},
|
|
3077
|
+
"telegram.typing_simulation": {
|
|
3078
|
+
type: "boolean",
|
|
3079
|
+
category: "Telegram",
|
|
3080
|
+
description: "Simulate typing indicator before sending replies",
|
|
3081
|
+
sensitive: false,
|
|
3082
|
+
validate: enumValidator(["true", "false"]),
|
|
3083
|
+
mask: identity,
|
|
3084
|
+
parse: (v) => v === "true"
|
|
3085
|
+
},
|
|
3086
|
+
// ─── Embedding ─────────────────────────────────────────────────────
|
|
3087
|
+
"embedding.provider": {
|
|
3088
|
+
type: "enum",
|
|
3089
|
+
category: "Embedding",
|
|
3090
|
+
description: "Embedding provider for RAG",
|
|
3091
|
+
sensitive: false,
|
|
3092
|
+
options: ["local", "anthropic", "none"],
|
|
3093
|
+
validate: enumValidator(["local", "anthropic", "none"]),
|
|
3094
|
+
mask: identity,
|
|
3095
|
+
parse: identity
|
|
3096
|
+
},
|
|
3097
|
+
// ─── WebUI ─────────────────────────────────────────────────────────
|
|
3098
|
+
"webui.port": {
|
|
3099
|
+
type: "number",
|
|
3100
|
+
category: "WebUI",
|
|
3101
|
+
description: "HTTP server port (requires restart)",
|
|
3102
|
+
sensitive: false,
|
|
3103
|
+
validate: numberInRange(1024, 65535),
|
|
3104
|
+
mask: identity,
|
|
3105
|
+
parse: (v) => Number(v)
|
|
3106
|
+
},
|
|
3107
|
+
"webui.log_requests": {
|
|
3108
|
+
type: "boolean",
|
|
3109
|
+
category: "WebUI",
|
|
3110
|
+
description: "Log all HTTP requests to console",
|
|
3111
|
+
sensitive: false,
|
|
3112
|
+
validate: enumValidator(["true", "false"]),
|
|
3113
|
+
mask: identity,
|
|
3114
|
+
parse: (v) => v === "true"
|
|
3115
|
+
},
|
|
3116
|
+
// ─── Deals ─────────────────────────────────────────────────────────
|
|
3117
|
+
"deals.enabled": {
|
|
3118
|
+
type: "boolean",
|
|
3119
|
+
category: "Deals",
|
|
3120
|
+
description: "Enable the deals/escrow module",
|
|
3121
|
+
sensitive: false,
|
|
3122
|
+
validate: enumValidator(["true", "false"]),
|
|
3123
|
+
mask: identity,
|
|
3124
|
+
parse: (v) => v === "true"
|
|
3125
|
+
},
|
|
3126
|
+
// ─── Developer ─────────────────────────────────────────────────────
|
|
3127
|
+
"dev.hot_reload": {
|
|
3128
|
+
type: "boolean",
|
|
3129
|
+
category: "Developer",
|
|
3130
|
+
description: "Watch ~/.teleton/plugins/ for live changes",
|
|
3131
|
+
sensitive: false,
|
|
3132
|
+
validate: enumValidator(["true", "false"]),
|
|
3133
|
+
mask: identity,
|
|
3134
|
+
parse: (v) => v === "true"
|
|
2704
3135
|
}
|
|
2705
3136
|
};
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
const maxIterations = 10;
|
|
2711
|
-
while (decoded !== prev && iterations < maxIterations) {
|
|
2712
|
-
prev = decoded;
|
|
2713
|
-
try {
|
|
2714
|
-
decoded = decodeURIComponent(decoded);
|
|
2715
|
-
} catch {
|
|
2716
|
-
break;
|
|
2717
|
-
}
|
|
2718
|
-
iterations++;
|
|
3137
|
+
var FORBIDDEN_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
3138
|
+
function assertSafePath(parts) {
|
|
3139
|
+
if (parts.some((p) => FORBIDDEN_SEGMENTS.has(p))) {
|
|
3140
|
+
throw new Error("Invalid config path: forbidden segment");
|
|
2719
3141
|
}
|
|
2720
|
-
return decoded;
|
|
2721
3142
|
}
|
|
2722
|
-
function
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
const
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
throw new WorkspaceSecurityError(
|
|
2740
|
-
`Access denied: Path '${inputPath}' is outside the workspace. Only files in ~/.teleton/workspace/ are accessible.`,
|
|
2741
|
-
inputPath
|
|
2742
|
-
);
|
|
2743
|
-
}
|
|
2744
|
-
const exists = existsSync5(absolutePath);
|
|
2745
|
-
if (!exists && !allowCreate) {
|
|
2746
|
-
throw new WorkspaceSecurityError(
|
|
2747
|
-
`File not found: '${inputPath}' does not exist in workspace.`,
|
|
2748
|
-
inputPath
|
|
2749
|
-
);
|
|
2750
|
-
}
|
|
2751
|
-
if (exists) {
|
|
2752
|
-
const stats = lstatSync(absolutePath);
|
|
2753
|
-
if (stats.isSymbolicLink()) {
|
|
2754
|
-
throw new WorkspaceSecurityError(
|
|
2755
|
-
`Access denied: Symbolic links are not allowed for security reasons.`,
|
|
2756
|
-
inputPath
|
|
2757
|
-
);
|
|
3143
|
+
function getNestedValue(obj, path) {
|
|
3144
|
+
const parts = path.split(".");
|
|
3145
|
+
assertSafePath(parts);
|
|
3146
|
+
let current = obj;
|
|
3147
|
+
for (const part of parts) {
|
|
3148
|
+
if (current == null || typeof current !== "object") return void 0;
|
|
3149
|
+
current = current[part];
|
|
3150
|
+
}
|
|
3151
|
+
return current;
|
|
3152
|
+
}
|
|
3153
|
+
function setNestedValue(obj, path, value) {
|
|
3154
|
+
const parts = path.split(".");
|
|
3155
|
+
assertSafePath(parts);
|
|
3156
|
+
let current = obj;
|
|
3157
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
3158
|
+
if (current[parts[i]] == null || typeof current[parts[i]] !== "object") {
|
|
3159
|
+
current[parts[i]] = {};
|
|
2758
3160
|
}
|
|
3161
|
+
current = current[parts[i]];
|
|
2759
3162
|
}
|
|
2760
|
-
|
|
2761
|
-
absolutePath,
|
|
2762
|
-
relativePath,
|
|
2763
|
-
exists,
|
|
2764
|
-
isDirectory: exists ? lstatSync(absolutePath).isDirectory() : false,
|
|
2765
|
-
extension: extname(absolutePath).toLowerCase(),
|
|
2766
|
-
filename: basename(absolutePath)
|
|
2767
|
-
};
|
|
3163
|
+
current[parts[parts.length - 1]] = value;
|
|
2768
3164
|
}
|
|
2769
|
-
function
|
|
2770
|
-
const
|
|
2771
|
-
|
|
2772
|
-
|
|
3165
|
+
function deleteNestedValue(obj, path) {
|
|
3166
|
+
const parts = path.split(".");
|
|
3167
|
+
assertSafePath(parts);
|
|
3168
|
+
let current = obj;
|
|
3169
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
3170
|
+
if (current == null || typeof current !== "object") return;
|
|
3171
|
+
current = current[parts[i]];
|
|
3172
|
+
}
|
|
3173
|
+
if (current != null && typeof current === "object") {
|
|
3174
|
+
delete current[parts[parts.length - 1]];
|
|
2773
3175
|
}
|
|
2774
|
-
return validated;
|
|
2775
3176
|
}
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
`Cannot write to ${validated.filename}. This file is configured by the owner. Use memory_write instead.`,
|
|
2782
|
-
inputPath
|
|
2783
|
-
);
|
|
3177
|
+
function readRawConfig(configPath) {
|
|
3178
|
+
const fullPath = expandPath(configPath);
|
|
3179
|
+
if (!existsSync7(fullPath)) {
|
|
3180
|
+
throw new Error(`Config file not found: ${fullPath}
|
|
3181
|
+
Run 'teleton setup' to create one.`);
|
|
2784
3182
|
}
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
throw new WorkspaceSecurityError(
|
|
2789
|
-
`Invalid file type: '${validated.extension}' is not allowed for ${fileType}. Allowed: ${allowedExts.join(", ")}`,
|
|
2790
|
-
inputPath
|
|
2791
|
-
);
|
|
2792
|
-
}
|
|
3183
|
+
const raw = parse2(readFileSync6(fullPath, "utf-8"));
|
|
3184
|
+
if (!raw || typeof raw !== "object") {
|
|
3185
|
+
throw new Error(`Invalid config file: ${fullPath}`);
|
|
2793
3186
|
}
|
|
2794
|
-
return
|
|
3187
|
+
return raw;
|
|
2795
3188
|
}
|
|
2796
|
-
function
|
|
2797
|
-
const
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
);
|
|
3189
|
+
function writeRawConfig(raw, configPath) {
|
|
3190
|
+
const clone = { ...raw };
|
|
3191
|
+
delete clone.market;
|
|
3192
|
+
const result = ConfigSchema.safeParse(clone);
|
|
3193
|
+
if (!result.success) {
|
|
3194
|
+
throw new Error(`Refusing to save invalid config: ${result.error.message}`);
|
|
2803
3195
|
}
|
|
2804
|
-
|
|
3196
|
+
raw.meta = raw.meta ?? {};
|
|
3197
|
+
raw.meta.last_modified_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
3198
|
+
const fullPath = expandPath(configPath);
|
|
3199
|
+
writeFileSync3(fullPath, stringify2(raw), { encoding: "utf-8", mode: 384 });
|
|
2805
3200
|
}
|
|
2806
3201
|
|
|
2807
3202
|
export {
|
|
2808
|
-
DealsConfigSchema,
|
|
2809
|
-
ConfigSchema,
|
|
2810
|
-
getProviderMetadata,
|
|
2811
|
-
getSupportedProviders,
|
|
2812
|
-
validateApiKeyFormat,
|
|
2813
|
-
expandPath,
|
|
2814
3203
|
loadConfig,
|
|
2815
3204
|
configExists,
|
|
2816
3205
|
getDefaultConfigPath,
|
|
@@ -2819,21 +3208,27 @@ export {
|
|
|
2819
3208
|
validateReadPath,
|
|
2820
3209
|
validateWritePath,
|
|
2821
3210
|
validateDirectory,
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
getWalletBalance,
|
|
2830
|
-
getTonPrice,
|
|
3211
|
+
appendToDailyLog,
|
|
3212
|
+
writeSummaryToDailyLog,
|
|
3213
|
+
sanitizeForPrompt,
|
|
3214
|
+
sanitizeForContext,
|
|
3215
|
+
clearPromptCache,
|
|
3216
|
+
loadSoul,
|
|
3217
|
+
buildSystemPrompt,
|
|
2831
3218
|
writePluginSecret,
|
|
2832
3219
|
deletePluginSecret,
|
|
2833
3220
|
listPluginSecretKeys,
|
|
3221
|
+
toLong,
|
|
3222
|
+
randomLong,
|
|
2834
3223
|
withBlockchainRetry,
|
|
2835
3224
|
sendTon,
|
|
2836
3225
|
adaptPlugin,
|
|
2837
3226
|
ensurePluginDeps,
|
|
2838
|
-
loadEnhancedPlugins
|
|
3227
|
+
loadEnhancedPlugins,
|
|
3228
|
+
CONFIGURABLE_KEYS,
|
|
3229
|
+
getNestedValue,
|
|
3230
|
+
setNestedValue,
|
|
3231
|
+
deleteNestedValue,
|
|
3232
|
+
readRawConfig,
|
|
3233
|
+
writeRawConfig
|
|
2839
3234
|
};
|