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.
Files changed (39) hide show
  1. package/README.md +34 -31
  2. package/dist/{chunk-6L6KGATM.js → chunk-3YM57ZAV.js} +1638 -1749
  3. package/dist/{chunk-D5I7GBV7.js → chunk-FNV5FF35.js} +22 -13
  4. package/dist/{chunk-4IPJ25HE.js → chunk-HZNZT4TG.js} +1106 -711
  5. package/dist/chunk-LRCPA7SC.js +149 -0
  6. package/dist/chunk-ND2X5FWB.js +368 -0
  7. package/dist/chunk-NERLQY2H.js +421 -0
  8. package/dist/{chunk-YBA6IBGT.js → chunk-OCLG5GKI.js} +24 -24
  9. package/dist/{chunk-ADCMUNYU.js → chunk-RBU6JXD3.js} +60 -55
  10. package/dist/chunk-RCMD3U65.js +141 -0
  11. package/dist/{chunk-ECSCVEQQ.js → chunk-UCN6TI25.js} +7 -3
  12. package/dist/{chunk-WL2Q3VRD.js → chunk-UDD7FYOU.js} +12 -4
  13. package/dist/chunk-VAUJSSD3.js +20 -0
  14. package/dist/chunk-XBE4JB7C.js +8 -0
  15. package/dist/{chunk-GDCODBNO.js → chunk-XBKSS6DM.js} +2 -16
  16. package/dist/cli/index.js +878 -433
  17. package/dist/client-3VWE7NC4.js +29 -0
  18. package/dist/{get-my-gifts-KVULMBJ3.js → get-my-gifts-RI7FAXAL.js} +3 -1
  19. package/dist/index.js +17 -11
  20. package/dist/{memory-TVDOGQXS.js → memory-5SS3Q5EA.js} +7 -5
  21. package/dist/{migrate-QIEMPOMT.js → migrate-M7SJMDOL.js} +14 -9
  22. package/dist/{server-RSWVCVY3.js → server-DS5OARW6.js} +174 -85
  23. package/dist/setup-server-C7ZTPHD5.js +934 -0
  24. package/dist/{task-dependency-resolver-72DLY2HV.js → task-dependency-resolver-WKZWJLLM.js} +19 -15
  25. package/dist/{task-executor-VXB6DAV2.js → task-executor-PD3H4MLO.js} +4 -1
  26. package/dist/tool-adapter-Y3TCEQOC.js +145 -0
  27. package/dist/{tool-index-DKI2ZNOU.js → tool-index-MIVK3D7H.js} +14 -9
  28. package/dist/{transcript-7V4UNID4.js → transcript-UDJZP6NK.js} +2 -1
  29. package/dist/web/assets/complete-fZLnb5Ot.js +1 -0
  30. package/dist/web/assets/index-BqwoDycr.js +72 -0
  31. package/dist/web/assets/index-CRDIf07k.css +1 -0
  32. package/dist/web/assets/index.es-D81xLR29.js +11 -0
  33. package/dist/web/assets/login-telegram-BP7CJDmx.js +1 -0
  34. package/dist/web/assets/run-DOrDowjK.js +1 -0
  35. package/dist/web/index.html +2 -2
  36. package/package.json +7 -3
  37. package/dist/chunk-2QUJLHCZ.js +0 -362
  38. package/dist/web/assets/index-BNhrx9S1.js +0 -67
  39. 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-ECSCVEQQ.js";
25
+ } from "./chunk-UCN6TI25.js";
6
26
  import {
7
- COINGECKO_API_URL,
8
- fetchWithTimeout,
9
27
  tonapiFetch
10
- } from "./chunk-GDCODBNO.js";
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
- getCachedHttpEndpoint
32
- } from "./chunk-QUAPFI2N.js";
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, chmodSync } from "fs";
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
- console.warn("\u26A0\uFE0F config.market is deprecated and ignored. Use market-api plugin instead.");
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/ton/wallet-service.ts
358
- import { mnemonicNew, mnemonicToPrivateKey, mnemonicValidate } from "@ton/crypto";
359
- import { WalletContractV5R1, TonClient, fromNano } from "@ton/ton";
360
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, chmodSync as chmodSync2 } from "fs";
361
- import { join as join2, dirname as dirname2 } from "path";
362
- var WALLET_FILE = join2(TELETON_ROOT, "wallet.json");
363
- var _walletCache;
364
- var _keyPairCache = null;
365
- async function generateWallet() {
366
- const mnemonic = await mnemonicNew(24);
367
- const keyPair = await mnemonicToPrivateKey(mnemonic);
368
- const wallet = WalletContractV5R1.create({
369
- workchain: 0,
370
- publicKey: keyPair.publicKey
371
- });
372
- const address = wallet.address.toString({ bounceable: true, testOnly: false });
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
- version: "w5r1",
375
- address,
376
- publicKey: keyPair.publicKey.toString("hex"),
377
- mnemonic,
378
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
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 saveWallet(wallet) {
382
- const dir = dirname2(WALLET_FILE);
383
- if (!existsSync2(dir)) {
384
- mkdirSync2(dir, { recursive: true });
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
- writeFileSync2(WALLET_FILE, JSON.stringify(wallet, null, 2), "utf-8");
387
- chmodSync2(WALLET_FILE, 384);
388
- _walletCache = void 0;
389
- _keyPairCache = null;
251
+ return validated;
390
252
  }
391
- function loadWallet() {
392
- if (_walletCache !== void 0) return _walletCache;
393
- if (!existsSync2(WALLET_FILE)) {
394
- _walletCache = null;
395
- return null;
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
- const content = readFileSync2(WALLET_FILE, "utf-8");
399
- _walletCache = JSON.parse(content);
400
- return _walletCache;
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
- console.error("Failed to load wallet:", error);
403
- _walletCache = null;
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
- function walletExists() {
408
- return existsSync2(WALLET_FILE);
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
- async function importWallet(mnemonic) {
411
- const valid = await mnemonicValidate(mnemonic);
412
- if (!valid) {
413
- throw new Error("Invalid mnemonic: words do not form a valid TON seed phrase");
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 keyPair = await mnemonicToPrivateKey(mnemonic);
416
- const wallet = WalletContractV5R1.create({
417
- workchain: 0,
418
- publicKey: keyPair.publicKey
419
- });
420
- const address = wallet.address.toString({ bounceable: true, testOnly: false });
421
- return {
422
- version: "w5r1",
423
- address,
424
- publicKey: keyPair.publicKey.toString("hex"),
425
- mnemonic,
426
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
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 getWalletAddress() {
430
- const wallet = loadWallet();
431
- return wallet?.address || null;
368
+ function writeSummaryToDailyLog(summary) {
369
+ appendToDailyLog(`### Memory Flush (Pre-Compaction)
370
+
371
+ ${summary}`);
432
372
  }
433
- async function getKeyPair() {
434
- if (_keyPairCache) return _keyPairCache;
435
- const wallet = loadWallet();
436
- if (!wallet) return null;
437
- _keyPairCache = await mnemonicToPrivateKey(wallet.mnemonic);
438
- return _keyPairCache;
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
- async function getWalletBalance(address) {
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
- const endpoint = await getCachedHttpEndpoint();
443
- const client = new TonClient({ endpoint });
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
- var TON_PRICE_CACHE_TTL_MS = 3e4;
458
- var _tonPriceCache = null;
459
- async function getTonPrice() {
460
- if (_tonPriceCache && Date.now() - _tonPriceCache.timestamp < TON_PRICE_CACHE_TTL_MS) {
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
- try {
464
- const response = await tonapiFetch(`/rates?tokens=ton&currencies=usd`);
465
- if (response.ok) {
466
- const data = await response.json();
467
- const price = data?.rates?.TON?.prices?.USD;
468
- if (typeof price === "number" && price > 0) {
469
- _tonPriceCache = { usd: price, source: "TonAPI", timestamp: Date.now() };
470
- return _tonPriceCache;
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
- try {
476
- const response = await fetchWithTimeout(
477
- `${COINGECKO_API_URL}/simple/price?ids=the-open-network&vs_currencies=usd`
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
- if (!response.ok) {
480
- throw new Error(`CoinGecko API error: ${response.status}`);
481
- }
482
- const data = await response.json();
483
- const price = data["the-open-network"]?.usd;
484
- if (typeof price === "number" && price > 0) {
485
- _tonPriceCache = { usd: price, source: "CoinGecko", timestamp: Date.now() };
486
- return _tonPriceCache;
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
- return null;
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 readFileSync4, existsSync as existsSync4, statSync } from "fs";
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 as z2 } from "zod";
503
- var ManifestSchema = z2.object({
504
- name: z2.string().min(1).max(64).regex(
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: z2.string().regex(/^\d+\.\d+\.\d+$/, "Must be semver (e.g., 1.0.0)"),
509
- author: z2.string().max(128).optional(),
510
- description: z2.string().max(256).optional(),
511
- dependencies: z2.array(z2.string()).optional(),
512
- defaultConfig: z2.record(z2.string(), z2.unknown()).optional(),
513
- sdkVersion: z2.string().max(32).optional(),
514
- secrets: z2.record(
515
- z2.string(),
516
- z2.object({
517
- required: z2.boolean(),
518
- description: z2.string().max(256),
519
- env: z2.string().max(128).optional()
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
- console.warn(`\u26A0\uFE0F [${pluginName}] tool is not an object, skipping`);
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
- console.warn(`\u26A0\uFE0F [${pluginName}] tool missing 'name', skipping`);
621
+ log3.warn(`[${pluginName}] tool missing 'name', skipping`);
536
622
  continue;
537
623
  }
538
624
  if (!t.description || typeof t.description !== "string") {
539
- console.warn(`\u26A0\uFE0F [${pluginName}] tool "${t.name}" missing 'description', skipping`);
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
- console.warn(`\u26A0\uFE0F [${pluginName}] tool "${t.name}" missing 'execute' function, skipping`);
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 as WalletContractV5R12, TonClient as TonClient2, toNano, internal } from "@ton/ton";
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
- console.error(`Invalid recipient address: ${toAddress}`, e);
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
- console.error("Wallet not initialized");
680
+ log4.error("Wallet not initialized");
590
681
  return null;
591
682
  }
592
- const wallet = WalletContractV5R12.create({
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 TonClient2({ endpoint });
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 + SendMode.IGNORE_ERRORS,
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
- console.log(`\u{1F4B8} [TON] Sent ${amount} TON to ${toAddress.slice(0, 8)}... - seqno: ${seqno}`);
705
+ log4.info(`Sent ${amount} TON to ${toAddress.slice(0, 8)}... - seqno: ${seqno}`);
615
706
  return pseudoHash;
616
707
  } catch (error) {
617
- console.error("Error sending TON:", error);
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
- console.warn(`Retry attempt ${attempt}/${opts.maxAttempts} failed:`, lastError.message);
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 = error instanceof Error ? error.message : String(error);
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, log) {
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
- log.debug(`Cleaned up ${result.changes} old transaction records (>${retentionDays}d)`);
771
+ log7.debug(`Cleaned up ${result.changes} old transaction records (>${retentionDays}d)`);
680
772
  }
681
773
  } catch (err) {
682
- log.error("Transaction cleanup failed:", err);
774
+ log7.error("Transaction cleanup failed:", err);
683
775
  }
684
776
  }
685
- function createTonSDK(log, db) {
777
+ function createTonSDK(log7, db) {
686
778
  return {
687
779
  getAddress() {
688
780
  try {
689
781
  return getWalletAddress();
690
782
  } catch (err) {
691
- log.error("ton.getAddress() failed:", err);
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
- log.error("ton.getBalance() failed:", err);
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
- log.error("ton.getPrice() failed:", err);
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: TonClient3 } = await import("@ton/ton");
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 TonClient3({ endpoint });
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
- log.error("ton.getTransactions() failed:", err);
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, log);
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
- log.error("ton.verifyPayment() failed:", err);
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
- log.error(`ton.getJettonBalances() TonAPI error: ${response.status}`);
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 || 9;
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
- log.error("ton.getJettonBalances() failed:", err);
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
- log.error(`ton.getJettonInfo() TonAPI error: ${response.status}`);
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
- log.error("ton.getJettonInfo() failed:", err);
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: WalletContractV5R13, TonClient: TonClient3, toNano: toNano2, internal: internal2 } = await import("@ton/ton");
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
- const jettonsResponse = await tonapiFetch(`/accounts/${walletData.address}/jettons`);
906
- if (!jettonsResponse.ok) {
907
- throw new PluginSDKError(
908
- `Failed to fetch jetton balances: ${jettonsResponse.status}`,
909
- "OPERATION_FAILED"
910
- );
911
- }
912
- const jettonsData = await jettonsResponse.json();
913
- const jettonBalance = jettonsData.balances?.find(
914
- (b) => b.jetton.address.toLowerCase() === jettonAddress.toLowerCase() || Address2.parse(b.jetton.address).toString() === Address2.parse(jettonAddress).toString()
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
- const senderJettonWallet = jettonBalance.wallet_address.address;
923
- const decimals = jettonBalance.jetton.decimals || 9;
924
- const currentBalance = BigInt(jettonBalance.balance);
925
- const amountInUnits = BigInt(Math.floor(amount * 10 ** decimals));
926
- if (amountInUnits > currentBalance) {
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
- `Insufficient balance. Have ${Number(currentBalance) / 10 ** decimals}, need ${amount}`,
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
- log.error(`ton.getJettonWalletAddress() TonAPI error: ${response.status}`);
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
- log.error("ton.getJettonWalletAddress() failed:", err);
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
- log.error(`ton.getNftItems() TonAPI error: ${response.status}`);
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
- log.error("ton.getNftItems() failed:", err);
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
- log.error(`ton.getNftInfo() TonAPI error: ${response.status}`);
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
- log.error("ton.getNftInfo() failed:", err);
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/sdk/telegram.ts
1065
- import { randomBytes as randomBytes3 } from "crypto";
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
- import { randomBytes } from "crypto";
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: [randomBytes(8).readBigUInt64BE()]
1239
+ randomId: [randomLong()]
1132
1240
  })
1133
1241
  );
1134
- const updates = result;
1135
- if (updates.updates) {
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
- log.error("telegram.searchMessages() failed:", err);
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 bigInt = (await import("./BigInteger-DQ33LTTE.js")).default;
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: bigInt(0)
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
- import { randomBytes as randomBytes2 } from "crypto";
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
- log.error("telegram.getChatInfo() failed:", err);
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
- return {
1552
- id: Number(user.id),
1553
- type: "user",
1554
- username: user.username || void 0,
1555
- title: user.firstName || void 0
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
- id: Number(user.id),
1595
- firstName: user.firstName || "",
1596
- lastName: user.lastName || void 0,
1597
- username: user.username || void 0,
1598
- isBot: user.bot || false
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
- log.error("telegram.getParticipants() failed:", err);
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: randomBytes2(8).readBigUInt64BE(),
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: randomBytes2(8).readBigUInt64BE()
1746
+ randomId: randomLong()
1638
1747
  })
1639
1748
  );
1640
- if (result.className === "Updates" || result.className === "UpdatesCombined") {
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: randomBytes2(8).readBigUInt64BE(),
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: randomBytes2(8).readBigUInt64BE()
1805
+ randomId: randomLong()
1697
1806
  })
1698
1807
  );
1699
- if (result.className === "Updates" || result.className === "UpdatesCombined") {
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: readFileSync5, statSync: statSync2 } = await import("fs");
2074
+ const { readFileSync: readFileSync7, statSync: statSync2 } = await import("fs");
1966
2075
  const { basename: basename2 } = await import("path");
1967
- const filePath = mediaPath;
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 = readFileSync5(filePath);
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
- return result.id || 0;
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, log) {
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: randomBytes3(8).readBigUInt64BE(),
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.className === "Updates" || result.className === "UpdatesCombined") {
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?.media?.className === "MessageMediaDice") {
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
- log.error("telegram.getMessages() failed:", err);
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, log),
2161
- ...createTelegramSocialSDK(bridge, log)
2291
+ ...createTelegramMessagesSDK(bridge, log7),
2292
+ ...createTelegramSocialSDK(bridge, log7)
2162
2293
  };
2163
2294
  }
2164
2295
 
2165
2296
  // src/sdk/secrets.ts
2166
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync3 } from "fs";
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 (!existsSync3(filePath)) return {};
2176
- const raw = readFileSync3(filePath, "utf-8");
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
- writeFileSync3(filePath, JSON.stringify(existing, null, 2), { mode: 384 });
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
- writeFileSync3(filePath, JSON.stringify(existing, null, 2), { mode: 384 });
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, log) {
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
- log.debug(`Secret "${key}" resolved from env var ${envKey}`);
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
- log.debug(`Secret "${key}" resolved from secrets store`);
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
- log.debug(`Secret "${key}" resolved from pluginConfig`);
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 log = createLogger(opts.pluginName);
2312
- const ton = Object.freeze(createTonSDK(log, opts.db));
2313
- const telegram = Object.freeze(createTelegramSDK(deps.bridge, log));
2314
- const secrets = Object.freeze(createSecretsSDK(opts.pluginName, opts.pluginConfig, log));
2315
- const storage = opts.db ? Object.freeze(createStorageSDK(opts.db)) : null;
2316
- const frozenLog = Object.freeze(log);
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: opts.db,
2483
+ db: safeDb,
2326
2484
  config: frozenConfig,
2327
2485
  pluginConfig: frozenPluginConfig,
2328
2486
  log: frozenLog
2329
2487
  });
2330
2488
  }
2331
- function createLogger(pluginName) {
2332
- const prefix = `[${pluginName}]`;
2489
+ function createLogger2(pluginName) {
2490
+ const pinoChild = createLogger(`plugin:${pluginName}`);
2333
2491
  return {
2334
- info: (...args) => console.log(prefix, ...args),
2335
- warn: (...args) => console.warn(`\u26A0\uFE0F ${prefix}`, ...args),
2336
- error: (...args) => console.error(`\u274C ${prefix}`, ...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
- console.warn(`\u26A0\uFE0F [SDK] Could not parse current version "${current}", skipping check`);
2362
- return true;
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
- console.warn(`\u26A0\uFE0F [SDK] Malformed sdkVersion range "${range}", skipping check`);
2368
- return true;
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
- console.warn(`\u26A0\uFE0F [SDK] Malformed sdkVersion range "${range}", skipping check`);
2376
- return true;
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
- console.warn(`\u26A0\uFE0F [SDK] Malformed sdkVersion "${range}", skipping check`);
2386
- return true;
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
- console.warn(
2401
- `\u26A0\uFE0F [${entryName}] invalid manifest, ignoring:`,
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 (existsSync4(manifestPath)) {
2410
- const diskManifest = JSON.parse(readFileSync4(manifestPath, "utf-8"));
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 log = (...args) => console.log(`[${pluginName}]`, ...args);
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: log,
2446
- warn: (...a) => console.warn(`\u26A0\uFE0F [${pluginName}]`, ...a),
2447
- error: (...a) => console.error(`\u274C [${pluginName}]`, ...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
- console.warn(
2462
- `\u26A0\uFE0F [${pluginName}] Missing required secrets:
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
- console.error(
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
- console.error(
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
- console.error(
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
- console.error(
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 (!existsSync4(pkgJson)) return;
2600
- if (!existsSync4(lockfile)) {
2601
- console.warn(
2602
- `\u26A0\uFE0F [${pluginEntry}] package.json without package-lock.json \u2014 skipping (lockfile required)`
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 (existsSync4(nodeModules)) {
2749
+ if (existsSync6(nodeModules)) {
2607
2750
  const marker = join4(nodeModules, ".package-lock.json");
2608
- if (existsSync4(marker) && statSync(marker).mtimeMs >= statSync(lockfile).mtimeMs) return;
2751
+ if (existsSync6(marker) && statSync(marker).mtimeMs >= statSync(lockfile).mtimeMs) return;
2609
2752
  }
2610
- console.log(`\u{1F4E6} [${pluginEntry}] Installing dependencies...`);
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
- console.log(`\u{1F4E6} [${pluginEntry}] Dependencies installed`);
2760
+ log6.info(`[${pluginEntry}] Dependencies installed`);
2618
2761
  } catch (err) {
2619
- console.error(`\u274C [${pluginEntry}] Failed to install deps: ${String(err).slice(0, 300)}`);
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 (!existsSync4(pluginsDir)) {
2767
+ if (!existsSync6(pluginsDir)) {
2625
2768
  return [];
2626
2769
  }
2627
- const entries = readdirSync(pluginsDir);
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 (existsSync4(indexPath)) {
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
- console.error(
2665
- `\u274C Plugin failed to load:`,
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
- console.warn(`\u26A0\uFE0F Plugin "${entry}": no 'tools' array or function exported, skipping`);
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
- console.warn(
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
- console.error(
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/workspace/validator.ts
2696
- import { existsSync as existsSync5, lstatSync, readdirSync as readdirSync2 } from "fs";
2697
- import { resolve, normalize, relative, extname, basename } from "path";
2698
- import { homedir as homedir2 } from "os";
2699
- var WorkspaceSecurityError = class extends Error {
2700
- constructor(message, attemptedPath) {
2701
- super(message);
2702
- this.attemptedPath = attemptedPath;
2703
- this.name = "WorkspaceSecurityError";
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
- function decodeRecursive(str) {
2707
- let decoded = str;
2708
- let prev = "";
2709
- let iterations = 0;
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 validatePath(inputPath, allowCreate = false) {
2723
- if (!inputPath || inputPath.trim() === "") {
2724
- throw new WorkspaceSecurityError("Path cannot be empty.", inputPath);
2725
- }
2726
- const trimmedPath = inputPath.trim().replace(/\\/g, "/");
2727
- const decodedPath = decodeRecursive(trimmedPath);
2728
- let absolutePath;
2729
- if (decodedPath.startsWith("/")) {
2730
- absolutePath = resolve(normalize(decodedPath));
2731
- } else if (decodedPath.startsWith("~/")) {
2732
- const expanded = decodedPath.replace(/^~(?=$|[\\/])/, homedir2());
2733
- absolutePath = resolve(expanded);
2734
- } else {
2735
- absolutePath = resolve(WORKSPACE_ROOT, normalize(decodedPath));
2736
- }
2737
- const relativePath = relative(WORKSPACE_ROOT, absolutePath);
2738
- if (relativePath.startsWith("..") || relativePath.startsWith("/")) {
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
- return {
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 validateReadPath(inputPath) {
2770
- const validated = validatePath(inputPath, false);
2771
- if (validated.isDirectory) {
2772
- throw new WorkspaceSecurityError(`Cannot read directory as file: '${inputPath}'`, inputPath);
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
- var IMMUTABLE_FILES = ["SOUL.md", "STRATEGY.md", "SECURITY.md"];
2777
- function validateWritePath(inputPath, fileType) {
2778
- const validated = validatePath(inputPath, true);
2779
- if (IMMUTABLE_FILES.includes(validated.filename)) {
2780
- throw new WorkspaceSecurityError(
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
- if (fileType && ALLOWED_EXTENSIONS[fileType]) {
2786
- const allowedExts = ALLOWED_EXTENSIONS[fileType];
2787
- if (!allowedExts.includes(validated.extension)) {
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 validated;
3187
+ return raw;
2795
3188
  }
2796
- function validateDirectory(inputPath) {
2797
- const validated = validatePath(inputPath, true);
2798
- if (validated.exists && !validated.isDirectory) {
2799
- throw new WorkspaceSecurityError(
2800
- `Path exists but is not a directory: '${inputPath}'`,
2801
- inputPath
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
- return validated;
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
- generateWallet,
2823
- saveWallet,
2824
- loadWallet,
2825
- walletExists,
2826
- importWallet,
2827
- getWalletAddress,
2828
- getKeyPair,
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
  };