teleton 0.8.2 → 0.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +230 -294
  2. package/dist/bootstrap-NNEI3Z5H.js +126 -0
  3. package/dist/{chunk-HEDJCLA6.js → chunk-35MX4ZUI.js} +2 -122
  4. package/dist/{chunk-57URFK6M.js → chunk-5LOHRZYY.js} +64 -15
  5. package/dist/{chunk-YOSUPUAJ.js → chunk-6OOHHJ4N.js} +1 -174
  6. package/dist/{chunk-W25Z7CM6.js → chunk-ALKAAG4O.js} +3 -3
  7. package/dist/chunk-CUE4UZXR.js +129 -0
  8. package/dist/{chunk-XBSCYMKM.js → chunk-G7PCW63M.js} +14 -14
  9. package/dist/{chunk-VYKW7FMV.js → chunk-GHMXWAXI.js} +1 -1
  10. package/dist/chunk-JROBTXWY.js +908 -0
  11. package/dist/{chunk-J73TA3UM.js → chunk-LVTKJQ7O.js} +7 -5
  12. package/dist/{chunk-GGXJLMOH.js → chunk-LZQOX6YY.js} +283 -1061
  13. package/dist/{chunk-7YKSXOQQ.js → chunk-NH2CNRKJ.js} +131 -241
  14. package/dist/chunk-NVKBBTI6.js +128 -0
  15. package/dist/{chunk-2IZU3REP.js → chunk-UMUONAD6.js} +143 -220
  16. package/dist/chunk-WTDAICGT.js +175 -0
  17. package/dist/{chunk-55SKE6YH.js → chunk-XDZDOKIF.js} +1 -1
  18. package/dist/cli/index.js +41 -24
  19. package/dist/{client-YOOHI776.js → client-5KD25NOP.js} +4 -3
  20. package/dist/index.js +14 -10
  21. package/dist/local-IHKJFQJS.js +9 -0
  22. package/dist/{memory-Q6EWGK2S.js → memory-QMJRM3XJ.js} +5 -3
  23. package/dist/{memory-hook-WUXJNVT5.js → memory-hook-VUNWZ3NY.js} +5 -4
  24. package/dist/{migrate-WFU6COBN.js → migrate-5VBAP52B.js} +3 -2
  25. package/dist/{server-YODFBZKG.js → server-AJCOURH7.js} +13 -10
  26. package/dist/{server-GYZXKIKU.js → server-WWGVDFPW.js} +38 -13
  27. package/dist/{setup-server-IZBUOJRU.js → setup-server-VDY64CWW.js} +5 -3
  28. package/dist/{store-7M4XV6M5.js → store-BY7S6IFN.js} +4 -3
  29. package/dist/{tool-index-NYH57UWP.js → tool-index-FTERJSZK.js} +2 -1
  30. package/package.json +1 -1
@@ -1,13 +1,9 @@
1
- import {
2
- COINGECKO_API_URL,
3
- tonapiFetch
4
- } from "./chunk-VFA7QMCZ.js";
5
1
  import {
6
2
  TELEGRAM_MAX_MESSAGE_LENGTH
7
3
  } from "./chunk-C4NKJT2Z.js";
8
4
  import {
9
- fetchWithTimeout
10
- } from "./chunk-XQUHC3JZ.js";
5
+ getProviderMetadata
6
+ } from "./chunk-6OOHHJ4N.js";
11
7
  import {
12
8
  TELETON_ROOT,
13
9
  WORKSPACE_PATHS,
@@ -17,227 +13,11 @@ import {
17
13
  createLogger
18
14
  } from "./chunk-NQ6FZKCE.js";
19
15
 
20
- // src/ton/endpoint.ts
21
- var ENDPOINT_CACHE_TTL_MS = 6e4;
22
- var ORBS_HOST = "ton.access.orbs.network";
23
- var ORBS_TOPOLOGY_URL = `https://${ORBS_HOST}/mngr/nodes?npm_version=2.3.3`;
24
- var TONCENTER_URL = `https://toncenter.com/api/v2/jsonRPC`;
25
- var _cache = null;
26
- var _toncenterApiKey;
27
- function setToncenterApiKey(key) {
28
- _toncenterApiKey = key;
29
- }
30
- function getToncenterApiKey() {
31
- return _toncenterApiKey;
32
- }
33
- async function discoverOrbsEndpoint() {
34
- const res = await fetch(ORBS_TOPOLOGY_URL, { signal: AbortSignal.timeout(5e3) });
35
- const nodes = await res.json();
36
- const healthy = nodes.filter(
37
- (n) => n.Healthy === "1" && n.Weight > 0 && n.Mngr?.health?.["v2-mainnet"]
38
- );
39
- if (healthy.length === 0) throw new Error("no healthy orbs nodes");
40
- const totalWeight = healthy.reduce((sum, n) => sum + n.Weight, 0);
41
- let r = Math.floor(Math.random() * totalWeight);
42
- let chosen = healthy[0];
43
- for (const node of healthy) {
44
- r -= node.Weight;
45
- if (r < 0) {
46
- chosen = node;
47
- break;
48
- }
49
- }
50
- return `https://${ORBS_HOST}/${chosen.NodeId}/1/mainnet/toncenter-api-v2/jsonRPC`;
51
- }
52
- async function getCachedHttpEndpoint() {
53
- if (_cache && Date.now() - _cache.ts < ENDPOINT_CACHE_TTL_MS) {
54
- return _cache.url;
55
- }
56
- let url;
57
- if (_toncenterApiKey) {
58
- try {
59
- const testUrl = `https://toncenter.com/api/v2/getAddressInformation?address=EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c`;
60
- const res = await fetch(testUrl, {
61
- headers: { "X-API-Key": _toncenterApiKey },
62
- signal: AbortSignal.timeout(5e3)
63
- });
64
- if (!res.ok) throw new Error(`TonCenter ${res.status}`);
65
- url = TONCENTER_URL;
66
- } catch {
67
- try {
68
- url = await discoverOrbsEndpoint();
69
- } catch {
70
- url = TONCENTER_URL;
71
- }
72
- }
73
- } else {
74
- try {
75
- url = await discoverOrbsEndpoint();
76
- } catch {
77
- url = TONCENTER_URL;
78
- }
79
- }
80
- _cache = { url, ts: Date.now() };
81
- return url;
82
- }
83
- function invalidateEndpointCache() {
84
- _cache = null;
85
- }
86
-
87
- // src/ton/wallet-service.ts
88
- import { mnemonicNew, mnemonicToPrivateKey, mnemonicValidate } from "@ton/crypto";
89
- import { WalletContractV5R1, TonClient, fromNano } from "@ton/ton";
90
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
91
- import { join, dirname } from "path";
92
- var log = createLogger("TON");
93
- var WALLET_FILE = join(TELETON_ROOT, "wallet.json");
94
- var _walletCache;
95
- var _keyPairCache = null;
96
- var _tonClientCache = null;
97
- async function generateWallet() {
98
- const mnemonic = await mnemonicNew(24);
99
- const keyPair = await mnemonicToPrivateKey(mnemonic);
100
- const wallet = WalletContractV5R1.create({
101
- workchain: 0,
102
- publicKey: keyPair.publicKey
103
- });
104
- const address = wallet.address.toString({ bounceable: true, testOnly: false });
105
- return {
106
- version: "w5r1",
107
- address,
108
- publicKey: keyPair.publicKey.toString("hex"),
109
- mnemonic,
110
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
111
- };
112
- }
113
- function saveWallet(wallet) {
114
- const dir = dirname(WALLET_FILE);
115
- if (!existsSync(dir)) {
116
- mkdirSync(dir, { recursive: true });
117
- }
118
- writeFileSync(WALLET_FILE, JSON.stringify(wallet, null, 2), { encoding: "utf-8", mode: 384 });
119
- _walletCache = void 0;
120
- _keyPairCache = null;
121
- }
122
- function loadWallet() {
123
- if (_walletCache !== void 0) return _walletCache;
124
- if (!existsSync(WALLET_FILE)) {
125
- _walletCache = null;
126
- return null;
127
- }
128
- try {
129
- const content = readFileSync(WALLET_FILE, "utf-8");
130
- const parsed = JSON.parse(content);
131
- if (!parsed.mnemonic || !Array.isArray(parsed.mnemonic) || parsed.mnemonic.length !== 24) {
132
- throw new Error("Invalid wallet.json: mnemonic must be a 24-word array");
133
- }
134
- _walletCache = parsed;
135
- return _walletCache;
136
- } catch (error) {
137
- log.error({ err: error }, "Failed to load wallet");
138
- _walletCache = null;
139
- return null;
140
- }
141
- }
142
- function walletExists() {
143
- return existsSync(WALLET_FILE);
144
- }
145
- async function importWallet(mnemonic) {
146
- const valid = await mnemonicValidate(mnemonic);
147
- if (!valid) {
148
- throw new Error("Invalid mnemonic: words do not form a valid TON seed phrase");
149
- }
150
- const keyPair = await mnemonicToPrivateKey(mnemonic);
151
- const wallet = WalletContractV5R1.create({
152
- workchain: 0,
153
- publicKey: keyPair.publicKey
154
- });
155
- const address = wallet.address.toString({ bounceable: true, testOnly: false });
156
- return {
157
- version: "w5r1",
158
- address,
159
- publicKey: keyPair.publicKey.toString("hex"),
160
- mnemonic,
161
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
162
- };
163
- }
164
- function getWalletAddress() {
165
- const wallet = loadWallet();
166
- return wallet?.address || null;
167
- }
168
- async function getCachedTonClient() {
169
- const endpoint = await getCachedHttpEndpoint();
170
- if (_tonClientCache && _tonClientCache.endpoint === endpoint) {
171
- return _tonClientCache.client;
172
- }
173
- const apiKey = getToncenterApiKey();
174
- const client = new TonClient({ endpoint, ...apiKey && { apiKey } });
175
- _tonClientCache = { client, endpoint };
176
- return client;
177
- }
178
- function invalidateTonClientCache() {
179
- _tonClientCache = null;
180
- invalidateEndpointCache();
181
- }
182
- async function getKeyPair() {
183
- if (_keyPairCache) return _keyPairCache;
184
- const wallet = loadWallet();
185
- if (!wallet) return null;
186
- _keyPairCache = await mnemonicToPrivateKey(wallet.mnemonic);
187
- return _keyPairCache;
188
- }
189
- async function getWalletBalance(address) {
190
- try {
191
- const client = await getCachedTonClient();
192
- const { Address } = await import("@ton/core");
193
- const addressObj = Address.parse(address);
194
- const balance = await client.getBalance(addressObj);
195
- const balanceFormatted = fromNano(balance);
196
- return {
197
- balance: balanceFormatted,
198
- balanceNano: balance.toString()
199
- };
200
- } catch (error) {
201
- log.error({ err: error }, "Failed to get balance");
202
- return null;
203
- }
204
- }
205
- var TON_PRICE_CACHE_TTL_MS = 3e4;
206
- var _tonPriceCache = null;
207
- async function getTonPrice() {
208
- if (_tonPriceCache && Date.now() - _tonPriceCache.timestamp < TON_PRICE_CACHE_TTL_MS) {
209
- return { ..._tonPriceCache };
210
- }
211
- try {
212
- const response = await tonapiFetch(`/rates?tokens=ton&currencies=usd`);
213
- if (response.ok) {
214
- const data = await response.json();
215
- const price = data?.rates?.TON?.prices?.USD;
216
- if (typeof price === "number" && price > 0) {
217
- _tonPriceCache = { usd: price, source: "TonAPI", timestamp: Date.now() };
218
- return _tonPriceCache;
219
- }
220
- }
221
- } catch {
222
- }
223
- try {
224
- const response = await fetchWithTimeout(
225
- `${COINGECKO_API_URL}/simple/price?ids=the-open-network&vs_currencies=usd`
226
- );
227
- if (!response.ok) {
228
- throw new Error(`CoinGecko API error: ${response.status}`);
229
- }
230
- const data = await response.json();
231
- const price = data["the-open-network"]?.usd;
232
- if (typeof price === "number" && price > 0) {
233
- _tonPriceCache = { usd: price, source: "CoinGecko", timestamp: Date.now() };
234
- return _tonPriceCache;
235
- }
236
- } catch (error) {
237
- log.error({ err: error }, "Failed to get TON price");
238
- }
239
- return null;
240
- }
16
+ // src/config/loader.ts
17
+ import { readFileSync, existsSync, writeFileSync, mkdirSync } from "fs";
18
+ import { parse, stringify } from "yaml";
19
+ import { homedir } from "os";
20
+ import { dirname, join } from "path";
241
21
 
242
22
  // src/config/schema.ts
243
23
  import { z } from "zod";
@@ -429,6 +209,125 @@ var ConfigSchema = z.object({
429
209
  tavily_api_key: z.string().optional().describe("Tavily API key for web search & extract (free at https://tavily.com)")
430
210
  });
431
211
 
212
+ // src/config/loader.ts
213
+ var log = createLogger("Config");
214
+ var DEFAULT_CONFIG_PATH = join(TELETON_ROOT, "config.yaml");
215
+ function expandPath(path) {
216
+ if (path.startsWith("~")) {
217
+ return join(homedir(), path.slice(1));
218
+ }
219
+ return path;
220
+ }
221
+ function loadConfig(configPath = DEFAULT_CONFIG_PATH) {
222
+ const fullPath = expandPath(configPath);
223
+ if (!existsSync(fullPath)) {
224
+ throw new Error(`Config file not found: ${fullPath}
225
+ Run 'teleton setup' to create one.`);
226
+ }
227
+ let content;
228
+ try {
229
+ content = readFileSync(fullPath, "utf-8");
230
+ } catch (error) {
231
+ throw new Error(`Cannot read config file ${fullPath}: ${error.message}`);
232
+ }
233
+ let raw;
234
+ try {
235
+ raw = parse(content);
236
+ } catch (error) {
237
+ throw new Error(`Invalid YAML in ${fullPath}: ${error.message}`);
238
+ }
239
+ if (raw && typeof raw === "object" && "market" in raw) {
240
+ log.warn("config.market is deprecated and ignored. Use market-api plugin instead.");
241
+ delete raw.market;
242
+ }
243
+ const result = ConfigSchema.safeParse(raw);
244
+ if (!result.success) {
245
+ throw new Error(`Invalid config: ${result.error.message}`);
246
+ }
247
+ const config = result.data;
248
+ const provider = config.agent.provider;
249
+ if (provider !== "anthropic" && provider !== "claude-code" && !raw.agent?.model) {
250
+ const meta = getProviderMetadata(provider);
251
+ config.agent.model = meta.defaultModel;
252
+ }
253
+ config.telegram.session_path = expandPath(config.telegram.session_path);
254
+ config.storage.sessions_file = expandPath(config.storage.sessions_file);
255
+ config.storage.memory_file = expandPath(config.storage.memory_file);
256
+ if (process.env.TELETON_API_KEY) {
257
+ config.agent.api_key = process.env.TELETON_API_KEY;
258
+ }
259
+ if (process.env.TELETON_TG_API_ID) {
260
+ const apiId = parseInt(process.env.TELETON_TG_API_ID, 10);
261
+ if (isNaN(apiId)) {
262
+ throw new Error(
263
+ `Invalid TELETON_TG_API_ID environment variable: "${process.env.TELETON_TG_API_ID}" is not a valid integer`
264
+ );
265
+ }
266
+ config.telegram.api_id = apiId;
267
+ }
268
+ if (process.env.TELETON_TG_API_HASH) {
269
+ config.telegram.api_hash = process.env.TELETON_TG_API_HASH;
270
+ }
271
+ if (process.env.TELETON_TG_PHONE) {
272
+ config.telegram.phone = process.env.TELETON_TG_PHONE;
273
+ }
274
+ if (process.env.TELETON_WEBUI_ENABLED) {
275
+ config.webui.enabled = process.env.TELETON_WEBUI_ENABLED === "true";
276
+ }
277
+ if (process.env.TELETON_WEBUI_PORT) {
278
+ const port = parseInt(process.env.TELETON_WEBUI_PORT, 10);
279
+ if (!isNaN(port) && port >= 1024 && port <= 65535) {
280
+ config.webui.port = port;
281
+ }
282
+ }
283
+ if (process.env.TELETON_WEBUI_HOST) {
284
+ config.webui.host = process.env.TELETON_WEBUI_HOST;
285
+ if (!["127.0.0.1", "localhost", "::1"].includes(config.webui.host)) {
286
+ log.warn(
287
+ { host: config.webui.host },
288
+ "WebUI bound to non-loopback address \u2014 ensure auth_token is set"
289
+ );
290
+ }
291
+ }
292
+ if (process.env.TELETON_API_ENABLED) {
293
+ if (!config.api) config.api = { enabled: false, port: 7778, key_hash: "", allowed_ips: [] };
294
+ config.api.enabled = process.env.TELETON_API_ENABLED === "true";
295
+ }
296
+ if (process.env.TELETON_API_PORT) {
297
+ const port = parseInt(process.env.TELETON_API_PORT, 10);
298
+ if (!isNaN(port) && port >= 1024 && port <= 65535) {
299
+ if (!config.api) config.api = { enabled: false, port: 7778, key_hash: "", allowed_ips: [] };
300
+ config.api.port = port;
301
+ }
302
+ }
303
+ if (process.env.TELETON_BASE_URL) {
304
+ try {
305
+ new URL(process.env.TELETON_BASE_URL);
306
+ config.agent.base_url = process.env.TELETON_BASE_URL;
307
+ } catch {
308
+ throw new Error(
309
+ `Invalid TELETON_BASE_URL: "${process.env.TELETON_BASE_URL}" is not a valid URL`
310
+ );
311
+ }
312
+ }
313
+ if (process.env.TELETON_TAVILY_API_KEY) {
314
+ config.tavily_api_key = process.env.TELETON_TAVILY_API_KEY;
315
+ }
316
+ if (process.env.TELETON_TONAPI_KEY) {
317
+ config.tonapi_key = process.env.TELETON_TONAPI_KEY;
318
+ }
319
+ if (process.env.TELETON_TONCENTER_API_KEY) {
320
+ config.toncenter_api_key = process.env.TELETON_TONCENTER_API_KEY;
321
+ }
322
+ return config;
323
+ }
324
+ function configExists(configPath = DEFAULT_CONFIG_PATH) {
325
+ return existsSync(expandPath(configPath));
326
+ }
327
+ function getDefaultConfigPath() {
328
+ return DEFAULT_CONFIG_PATH;
329
+ }
330
+
432
331
  // src/workspace/manager.ts
433
332
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, copyFileSync } from "fs";
434
333
  import { join as join2, dirname as dirname2 } from "path";
@@ -524,20 +423,11 @@ function loadTemplate(name) {
524
423
  export {
525
424
  DealsConfigSchema,
526
425
  ConfigSchema,
426
+ expandPath,
427
+ loadConfig,
428
+ configExists,
429
+ getDefaultConfigPath,
527
430
  ensureWorkspace,
528
431
  isNewWorkspace,
529
- loadTemplate,
530
- setToncenterApiKey,
531
- invalidateEndpointCache,
532
- generateWallet,
533
- saveWallet,
534
- loadWallet,
535
- walletExists,
536
- importWallet,
537
- getWalletAddress,
538
- getCachedTonClient,
539
- invalidateTonClientCache,
540
- getKeyPair,
541
- getWalletBalance,
542
- getTonPrice
432
+ loadTemplate
543
433
  };
@@ -0,0 +1,128 @@
1
+ import {
2
+ createLogger
3
+ } from "./chunk-NQ6FZKCE.js";
4
+
5
+ // src/agent/lifecycle.ts
6
+ import { EventEmitter } from "events";
7
+ var log = createLogger("Lifecycle");
8
+ var AgentLifecycle = class extends EventEmitter {
9
+ state = "stopped";
10
+ error;
11
+ startPromise = null;
12
+ stopPromise = null;
13
+ runningSince = null;
14
+ registeredStartFn = null;
15
+ registeredStopFn = null;
16
+ getState() {
17
+ return this.state;
18
+ }
19
+ getError() {
20
+ return this.error;
21
+ }
22
+ getUptime() {
23
+ if (this.state !== "running" || this.runningSince === null) {
24
+ return null;
25
+ }
26
+ return Math.floor((Date.now() - this.runningSince) / 1e3);
27
+ }
28
+ /**
29
+ * Register the start/stop callbacks so start()/stop() can be called without args.
30
+ */
31
+ registerCallbacks(startFn, stopFn) {
32
+ this.registeredStartFn = startFn;
33
+ this.registeredStopFn = stopFn;
34
+ }
35
+ /**
36
+ * Start the agent. Uses the provided callback or falls back to registered one.
37
+ * - No-op if already running
38
+ * - Returns existing promise if already starting
39
+ * - Throws if currently stopping
40
+ */
41
+ async start(startFn) {
42
+ const fn = startFn ?? this.registeredStartFn;
43
+ if (!fn) {
44
+ throw new Error("No start function provided or registered");
45
+ }
46
+ if (this.state === "running") {
47
+ return;
48
+ }
49
+ if (this.state === "starting") {
50
+ return this.startPromise ?? Promise.resolve();
51
+ }
52
+ if (this.state === "stopping") {
53
+ throw new Error("Cannot start while agent is stopping");
54
+ }
55
+ this.transition("starting");
56
+ this.startPromise = (async () => {
57
+ try {
58
+ await fn();
59
+ this.error = void 0;
60
+ this.runningSince = Date.now();
61
+ this.transition("running");
62
+ } catch (err) {
63
+ const message = err instanceof Error ? err.message : String(err);
64
+ this.error = message;
65
+ this.runningSince = null;
66
+ this.transition("stopped", message);
67
+ throw err;
68
+ } finally {
69
+ this.startPromise = null;
70
+ }
71
+ })();
72
+ return this.startPromise;
73
+ }
74
+ /**
75
+ * Stop the agent. Uses the provided callback or falls back to registered one.
76
+ * - No-op if already stopped
77
+ * - Returns existing promise if already stopping
78
+ * - If starting, waits for start to complete then stops
79
+ */
80
+ async stop(stopFn) {
81
+ const fn = stopFn ?? this.registeredStopFn;
82
+ if (!fn) {
83
+ throw new Error("No stop function provided or registered");
84
+ }
85
+ if (this.state === "stopped") {
86
+ return;
87
+ }
88
+ if (this.state === "stopping") {
89
+ return this.stopPromise ?? Promise.resolve();
90
+ }
91
+ if (this.state === "starting" && this.startPromise) {
92
+ try {
93
+ await this.startPromise;
94
+ } catch {
95
+ return;
96
+ }
97
+ }
98
+ this.transition("stopping");
99
+ this.stopPromise = (async () => {
100
+ try {
101
+ await fn();
102
+ } catch (err) {
103
+ log.error({ err }, "Error during agent stop");
104
+ } finally {
105
+ this.runningSince = null;
106
+ this.transition("stopped");
107
+ this.stopPromise = null;
108
+ }
109
+ })();
110
+ return this.stopPromise;
111
+ }
112
+ transition(newState, error) {
113
+ this.state = newState;
114
+ const event = {
115
+ state: newState,
116
+ timestamp: Date.now()
117
+ };
118
+ if (error !== void 0) {
119
+ event.error = error;
120
+ }
121
+ log.info(`Agent state: ${newState}${error ? ` (${error})` : ""}`);
122
+ this.emit("stateChange", event);
123
+ }
124
+ };
125
+
126
+ export {
127
+ AgentLifecycle
128
+ };