teleton 0.8.2 → 0.8.3

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/dist/bootstrap-DDFVEMYI.js +128 -0
  2. package/dist/{chunk-XBSCYMKM.js → chunk-2ERTYRHA.js} +6 -6
  3. package/dist/{chunk-2IZU3REP.js → chunk-33Z47EXI.js} +139 -214
  4. package/dist/{chunk-HEDJCLA6.js → chunk-35MX4ZUI.js} +2 -122
  5. package/dist/{chunk-YOSUPUAJ.js → chunk-6OOHHJ4N.js} +1 -174
  6. package/dist/{chunk-GGXJLMOH.js → chunk-7MWKT67G.js} +295 -416
  7. package/dist/chunk-AEHTQI3H.js +142 -0
  8. package/dist/{chunk-7YKSXOQQ.js → chunk-AERHOXGC.js} +78 -320
  9. package/dist/{chunk-W25Z7CM6.js → chunk-ALKAAG4O.js} +3 -3
  10. package/dist/chunk-CUE4UZXR.js +129 -0
  11. package/dist/chunk-FUNF6H4W.js +251 -0
  12. package/dist/{chunk-VYKW7FMV.js → chunk-GHMXWAXI.js} +1 -1
  13. package/dist/{chunk-J73TA3UM.js → chunk-LVTKJQ7O.js} +7 -5
  14. package/dist/chunk-NVKBBTI6.js +128 -0
  15. package/dist/{chunk-57URFK6M.js → chunk-OIMAE24Q.js} +51 -13
  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 +42 -22
  19. package/dist/{client-YOOHI776.js → client-5KD25NOP.js} +4 -3
  20. package/dist/index.js +15 -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-GYZXKIKU.js → server-JF6FX772.js} +39 -13
  26. package/dist/{server-YODFBZKG.js → server-N4T7E25M.js} +14 -10
  27. package/dist/{setup-server-IZBUOJRU.js → setup-server-IX3BFPPH.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
@@ -0,0 +1,129 @@
1
+ import {
2
+ TELETON_ROOT
3
+ } from "./chunk-EYWNOHMJ.js";
4
+ import {
5
+ createLogger
6
+ } from "./chunk-NQ6FZKCE.js";
7
+
8
+ // src/memory/embeddings/local.ts
9
+ import { pipeline, env } from "@huggingface/transformers";
10
+ import { join, dirname } from "path";
11
+ import { mkdirSync, writeFileSync, renameSync, statSync, unlinkSync } from "fs";
12
+ var log = createLogger("Memory");
13
+ var modelCacheDir = join(TELETON_ROOT, "models");
14
+ try {
15
+ mkdirSync(modelCacheDir, { recursive: true });
16
+ } catch {
17
+ }
18
+ env.cacheDir = modelCacheDir;
19
+ var MIN_FILE_SIZES = { "onnx/model.onnx": 1e6 };
20
+ function isCacheFileValid(filePath, fileName) {
21
+ try {
22
+ return statSync(filePath).size >= (MIN_FILE_SIZES[fileName] ?? 1);
23
+ } catch {
24
+ return false;
25
+ }
26
+ }
27
+ async function ensureModelCached(model) {
28
+ const files = ["config.json", "tokenizer_config.json", "tokenizer.json", "onnx/model.onnx"];
29
+ const baseUrl = `https://huggingface.co/${model}/resolve/main`;
30
+ for (const file of files) {
31
+ const localPath = join(modelCacheDir, model, file);
32
+ if (isCacheFileValid(localPath, file)) continue;
33
+ try {
34
+ unlinkSync(localPath);
35
+ } catch {
36
+ }
37
+ log.info(`Downloading ${model}/${file}...`);
38
+ mkdirSync(dirname(localPath), { recursive: true });
39
+ const res = await fetch(`${baseUrl}/${file}`, { redirect: "follow" });
40
+ if (!res.ok) {
41
+ throw new Error(`Failed to download ${model}/${file}: ${res.status} ${res.statusText}`);
42
+ }
43
+ const buffer = Buffer.from(await res.arrayBuffer());
44
+ const tmpPath = localPath + ".tmp";
45
+ writeFileSync(tmpPath, buffer);
46
+ renameSync(tmpPath, localPath);
47
+ }
48
+ }
49
+ var extractorPromise = null;
50
+ function getExtractor(model) {
51
+ if (!extractorPromise) {
52
+ log.info(`Loading local embedding model: ${model} (cache: ${modelCacheDir})`);
53
+ extractorPromise = pipeline("feature-extraction", model, {
54
+ dtype: "fp32",
55
+ // Explicit cache_dir to avoid any env race condition
56
+ cache_dir: modelCacheDir,
57
+ // Prevent pthread_setaffinity_np EINVAL on VPS/containers with restricted CPU sets.
58
+ // ONNX Runtime skips thread affinity when thread counts are explicit.
59
+ session_options: { intraOpNumThreads: 1, interOpNumThreads: 1 }
60
+ }).then((ext) => {
61
+ log.info(`Local embedding model ready`);
62
+ return ext;
63
+ }).catch((err) => {
64
+ log.error(`Failed to load embedding model: ${err.message}`);
65
+ extractorPromise = null;
66
+ throw err;
67
+ });
68
+ }
69
+ return extractorPromise;
70
+ }
71
+ var LocalEmbeddingProvider = class {
72
+ id = "local";
73
+ model;
74
+ dimensions;
75
+ _disabled = false;
76
+ constructor(config) {
77
+ this.model = config.model || "Xenova/all-MiniLM-L6-v2";
78
+ this.dimensions = 384;
79
+ }
80
+ /**
81
+ * Pre-download and load the model at startup.
82
+ * If loading fails, retries once then marks provider as disabled (FTS5-only).
83
+ * Call this once during app init — avoids retry spam on every message.
84
+ */
85
+ async warmup() {
86
+ for (let attempt = 1; attempt <= 2; attempt++) {
87
+ try {
88
+ await ensureModelCached(this.model);
89
+ await getExtractor(this.model);
90
+ return true;
91
+ } catch {
92
+ if (attempt === 1) {
93
+ log.warn(`Embedding model load failed (attempt 1), retrying...`);
94
+ await new Promise((r) => setTimeout(r, 1e3));
95
+ } else {
96
+ log.warn(
97
+ `Local embedding model unavailable \u2014 falling back to FTS5-only search (no vector embeddings)`
98
+ );
99
+ this._disabled = true;
100
+ return false;
101
+ }
102
+ }
103
+ }
104
+ return false;
105
+ }
106
+ async embedQuery(text) {
107
+ if (this._disabled) return [];
108
+ const extractor = await getExtractor(this.model);
109
+ const output = await extractor(text, { pooling: "mean", normalize: true });
110
+ return Array.from(output.data);
111
+ }
112
+ async embedBatch(texts) {
113
+ if (this._disabled) return [];
114
+ if (texts.length === 0) return [];
115
+ const extractor = await getExtractor(this.model);
116
+ const output = await extractor(texts, { pooling: "mean", normalize: true });
117
+ const data = output.data;
118
+ const dims = this.dimensions;
119
+ const results = [];
120
+ for (let i = 0; i < texts.length; i++) {
121
+ results.push(Array.from(data.slice(i * dims, (i + 1) * dims)));
122
+ }
123
+ return results;
124
+ }
125
+ };
126
+
127
+ export {
128
+ LocalEmbeddingProvider
129
+ };
@@ -0,0 +1,251 @@
1
+ import {
2
+ COINGECKO_API_URL,
3
+ tonapiFetch
4
+ } from "./chunk-VFA7QMCZ.js";
5
+ import {
6
+ fetchWithTimeout
7
+ } from "./chunk-XQUHC3JZ.js";
8
+ import {
9
+ TELETON_ROOT
10
+ } from "./chunk-EYWNOHMJ.js";
11
+ import {
12
+ createLogger
13
+ } from "./chunk-NQ6FZKCE.js";
14
+
15
+ // src/ton/endpoint.ts
16
+ var ENDPOINT_CACHE_TTL_MS = 6e4;
17
+ var ORBS_HOST = "ton.access.orbs.network";
18
+ var ORBS_TOPOLOGY_URL = `https://${ORBS_HOST}/mngr/nodes?npm_version=2.3.3`;
19
+ var TONCENTER_URL = `https://toncenter.com/api/v2/jsonRPC`;
20
+ var _cache = null;
21
+ var _toncenterApiKey;
22
+ function setToncenterApiKey(key) {
23
+ _toncenterApiKey = key;
24
+ }
25
+ function getToncenterApiKey() {
26
+ return _toncenterApiKey;
27
+ }
28
+ async function discoverOrbsEndpoint() {
29
+ const res = await fetch(ORBS_TOPOLOGY_URL, { signal: AbortSignal.timeout(5e3) });
30
+ const nodes = await res.json();
31
+ const healthy = nodes.filter(
32
+ (n) => n.Healthy === "1" && n.Weight > 0 && n.Mngr?.health?.["v2-mainnet"]
33
+ );
34
+ if (healthy.length === 0) throw new Error("no healthy orbs nodes");
35
+ const totalWeight = healthy.reduce((sum, n) => sum + n.Weight, 0);
36
+ let r = Math.floor(Math.random() * totalWeight);
37
+ let chosen = healthy[0];
38
+ for (const node of healthy) {
39
+ r -= node.Weight;
40
+ if (r < 0) {
41
+ chosen = node;
42
+ break;
43
+ }
44
+ }
45
+ return `https://${ORBS_HOST}/${chosen.NodeId}/1/mainnet/toncenter-api-v2/jsonRPC`;
46
+ }
47
+ async function getCachedHttpEndpoint() {
48
+ if (_cache && Date.now() - _cache.ts < ENDPOINT_CACHE_TTL_MS) {
49
+ return _cache.url;
50
+ }
51
+ let url;
52
+ if (_toncenterApiKey) {
53
+ try {
54
+ const testUrl = `https://toncenter.com/api/v2/getAddressInformation?address=EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c`;
55
+ const res = await fetch(testUrl, {
56
+ headers: { "X-API-Key": _toncenterApiKey },
57
+ signal: AbortSignal.timeout(5e3)
58
+ });
59
+ if (!res.ok) throw new Error(`TonCenter ${res.status}`);
60
+ url = TONCENTER_URL;
61
+ } catch {
62
+ try {
63
+ url = await discoverOrbsEndpoint();
64
+ } catch {
65
+ url = TONCENTER_URL;
66
+ }
67
+ }
68
+ } else {
69
+ try {
70
+ url = await discoverOrbsEndpoint();
71
+ } catch {
72
+ url = TONCENTER_URL;
73
+ }
74
+ }
75
+ _cache = { url, ts: Date.now() };
76
+ return url;
77
+ }
78
+ function invalidateEndpointCache() {
79
+ _cache = null;
80
+ }
81
+
82
+ // src/ton/wallet-service.ts
83
+ import { mnemonicNew, mnemonicToPrivateKey, mnemonicValidate } from "@ton/crypto";
84
+ import { WalletContractV5R1, TonClient, fromNano } from "@ton/ton";
85
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
86
+ import { join, dirname } from "path";
87
+ var log = createLogger("TON");
88
+ var WALLET_FILE = join(TELETON_ROOT, "wallet.json");
89
+ var _walletCache;
90
+ var _keyPairCache = null;
91
+ var _tonClientCache = null;
92
+ async function generateWallet() {
93
+ const mnemonic = await mnemonicNew(24);
94
+ const keyPair = await mnemonicToPrivateKey(mnemonic);
95
+ const wallet = WalletContractV5R1.create({
96
+ workchain: 0,
97
+ publicKey: keyPair.publicKey
98
+ });
99
+ const address = wallet.address.toString({ bounceable: true, testOnly: false });
100
+ return {
101
+ version: "w5r1",
102
+ address,
103
+ publicKey: keyPair.publicKey.toString("hex"),
104
+ mnemonic,
105
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
106
+ };
107
+ }
108
+ function saveWallet(wallet) {
109
+ const dir = dirname(WALLET_FILE);
110
+ if (!existsSync(dir)) {
111
+ mkdirSync(dir, { recursive: true });
112
+ }
113
+ writeFileSync(WALLET_FILE, JSON.stringify(wallet, null, 2), { encoding: "utf-8", mode: 384 });
114
+ _walletCache = void 0;
115
+ _keyPairCache = null;
116
+ }
117
+ function loadWallet() {
118
+ if (_walletCache !== void 0) return _walletCache;
119
+ if (!existsSync(WALLET_FILE)) {
120
+ _walletCache = null;
121
+ return null;
122
+ }
123
+ try {
124
+ const content = readFileSync(WALLET_FILE, "utf-8");
125
+ const parsed = JSON.parse(content);
126
+ if (!parsed.mnemonic || !Array.isArray(parsed.mnemonic) || parsed.mnemonic.length !== 24) {
127
+ throw new Error("Invalid wallet.json: mnemonic must be a 24-word array");
128
+ }
129
+ _walletCache = parsed;
130
+ return _walletCache;
131
+ } catch (error) {
132
+ log.error({ err: error }, "Failed to load wallet");
133
+ _walletCache = null;
134
+ return null;
135
+ }
136
+ }
137
+ function walletExists() {
138
+ return existsSync(WALLET_FILE);
139
+ }
140
+ async function importWallet(mnemonic) {
141
+ const valid = await mnemonicValidate(mnemonic);
142
+ if (!valid) {
143
+ throw new Error("Invalid mnemonic: words do not form a valid TON seed phrase");
144
+ }
145
+ const keyPair = await mnemonicToPrivateKey(mnemonic);
146
+ const wallet = WalletContractV5R1.create({
147
+ workchain: 0,
148
+ publicKey: keyPair.publicKey
149
+ });
150
+ const address = wallet.address.toString({ bounceable: true, testOnly: false });
151
+ return {
152
+ version: "w5r1",
153
+ address,
154
+ publicKey: keyPair.publicKey.toString("hex"),
155
+ mnemonic,
156
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
157
+ };
158
+ }
159
+ function getWalletAddress() {
160
+ const wallet = loadWallet();
161
+ return wallet?.address || null;
162
+ }
163
+ async function getCachedTonClient() {
164
+ const endpoint = await getCachedHttpEndpoint();
165
+ if (_tonClientCache && _tonClientCache.endpoint === endpoint) {
166
+ return _tonClientCache.client;
167
+ }
168
+ const apiKey = getToncenterApiKey();
169
+ const client = new TonClient({ endpoint, ...apiKey && { apiKey } });
170
+ _tonClientCache = { client, endpoint };
171
+ return client;
172
+ }
173
+ function invalidateTonClientCache() {
174
+ _tonClientCache = null;
175
+ invalidateEndpointCache();
176
+ }
177
+ async function getKeyPair() {
178
+ if (_keyPairCache) return _keyPairCache;
179
+ const wallet = loadWallet();
180
+ if (!wallet) return null;
181
+ _keyPairCache = await mnemonicToPrivateKey(wallet.mnemonic);
182
+ return _keyPairCache;
183
+ }
184
+ async function getWalletBalance(address) {
185
+ try {
186
+ const client = await getCachedTonClient();
187
+ const { Address } = await import("@ton/core");
188
+ const addressObj = Address.parse(address);
189
+ const balance = await client.getBalance(addressObj);
190
+ const balanceFormatted = fromNano(balance);
191
+ return {
192
+ balance: balanceFormatted,
193
+ balanceNano: balance.toString()
194
+ };
195
+ } catch (error) {
196
+ log.error({ err: error }, "Failed to get balance");
197
+ return null;
198
+ }
199
+ }
200
+ var TON_PRICE_CACHE_TTL_MS = 3e4;
201
+ var _tonPriceCache = null;
202
+ async function getTonPrice() {
203
+ if (_tonPriceCache && Date.now() - _tonPriceCache.timestamp < TON_PRICE_CACHE_TTL_MS) {
204
+ return { ..._tonPriceCache };
205
+ }
206
+ try {
207
+ const response = await tonapiFetch(`/rates?tokens=ton&currencies=usd`);
208
+ if (response.ok) {
209
+ const data = await response.json();
210
+ const price = data?.rates?.TON?.prices?.USD;
211
+ if (typeof price === "number" && price > 0) {
212
+ _tonPriceCache = { usd: price, source: "TonAPI", timestamp: Date.now() };
213
+ return _tonPriceCache;
214
+ }
215
+ }
216
+ } catch {
217
+ }
218
+ try {
219
+ const response = await fetchWithTimeout(
220
+ `${COINGECKO_API_URL}/simple/price?ids=the-open-network&vs_currencies=usd`
221
+ );
222
+ if (!response.ok) {
223
+ throw new Error(`CoinGecko API error: ${response.status}`);
224
+ }
225
+ const data = await response.json();
226
+ const price = data["the-open-network"]?.usd;
227
+ if (typeof price === "number" && price > 0) {
228
+ _tonPriceCache = { usd: price, source: "CoinGecko", timestamp: Date.now() };
229
+ return _tonPriceCache;
230
+ }
231
+ } catch (error) {
232
+ log.error({ err: error }, "Failed to get TON price");
233
+ }
234
+ return null;
235
+ }
236
+
237
+ export {
238
+ setToncenterApiKey,
239
+ invalidateEndpointCache,
240
+ generateWallet,
241
+ saveWallet,
242
+ loadWallet,
243
+ walletExists,
244
+ importWallet,
245
+ getWalletAddress,
246
+ getCachedTonClient,
247
+ invalidateTonClientCache,
248
+ getKeyPair,
249
+ getWalletBalance,
250
+ getTonPrice
251
+ };
@@ -3,7 +3,7 @@ import {
3
3
  createEmbeddingProvider,
4
4
  hashText,
5
5
  serializeEmbedding
6
- } from "./chunk-HEDJCLA6.js";
6
+ } from "./chunk-35MX4ZUI.js";
7
7
  import {
8
8
  FEED_MESSAGE_MAX_CHARS,
9
9
  HYBRID_SEARCH_MIN_SCORE,
@@ -1,15 +1,17 @@
1
1
  import {
2
2
  getClaudeCodeApiKey,
3
- getProviderMetadata,
4
3
  refreshClaudeCodeApiKey
5
- } from "./chunk-YOSUPUAJ.js";
4
+ } from "./chunk-WTDAICGT.js";
6
5
  import {
7
- appendToTranscript,
8
- readTranscript
9
- } from "./chunk-LC4TV3KL.js";
6
+ getProviderMetadata
7
+ } from "./chunk-6OOHHJ4N.js";
10
8
  import {
11
9
  fetchWithTimeout
12
10
  } from "./chunk-XQUHC3JZ.js";
11
+ import {
12
+ appendToTranscript,
13
+ readTranscript
14
+ } from "./chunk-LC4TV3KL.js";
13
15
  import {
14
16
  createLogger
15
17
  } from "./chunk-NQ6FZKCE.js";
@@ -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
+ };
@@ -2,26 +2,30 @@ import {
2
2
  getModelsForProvider
3
3
  } from "./chunk-WFTC3JJW.js";
4
4
  import {
5
- ConfigSchema,
6
- DealsConfigSchema,
7
- ensureWorkspace,
8
5
  generateWallet,
9
6
  getWalletAddress,
10
7
  importWallet,
11
- isNewWorkspace,
12
8
  saveWallet,
13
9
  walletExists
14
- } from "./chunk-7YKSXOQQ.js";
10
+ } from "./chunk-FUNF6H4W.js";
11
+ import {
12
+ ConfigSchema,
13
+ DealsConfigSchema,
14
+ ensureWorkspace,
15
+ isNewWorkspace
16
+ } from "./chunk-AERHOXGC.js";
17
+ import {
18
+ TELEGRAM_MAX_MESSAGE_LENGTH
19
+ } from "./chunk-C4NKJT2Z.js";
15
20
  import {
16
21
  getClaudeCodeApiKey,
22
+ isClaudeCodeTokenValid
23
+ } from "./chunk-WTDAICGT.js";
24
+ import {
17
25
  getProviderMetadata,
18
26
  getSupportedProviders,
19
- isClaudeCodeTokenValid,
20
27
  validateApiKeyFormat
21
- } from "./chunk-YOSUPUAJ.js";
22
- import {
23
- TELEGRAM_MAX_MESSAGE_LENGTH
24
- } from "./chunk-C4NKJT2Z.js";
28
+ } from "./chunk-6OOHHJ4N.js";
25
29
  import {
26
30
  fetchWithTimeout
27
31
  } from "./chunk-XQUHC3JZ.js";
@@ -34,7 +38,7 @@ import {
34
38
 
35
39
  // src/webui/routes/setup.ts
36
40
  import { Hono } from "hono";
37
- import { existsSync as existsSync2, readFileSync, writeFileSync as writeFileSync2 } from "fs";
41
+ import { existsSync as existsSync2, readFileSync, writeFileSync as writeFileSync2, statSync } from "fs";
38
42
  import { join as join2 } from "path";
39
43
  import YAML from "yaml";
40
44
 
@@ -386,7 +390,7 @@ function maskKey(key) {
386
390
  if (key.length <= 10) return "***";
387
391
  return key.slice(0, 6) + "..." + key.slice(-4);
388
392
  }
389
- function createSetupRoutes() {
393
+ function createSetupRoutes(options) {
390
394
  const app = new Hono();
391
395
  const authManager = new TelegramAuthManager();
392
396
  app.get("/status", async (c) => {
@@ -744,6 +748,33 @@ function createSetupRoutes() {
744
748
  );
745
749
  }
746
750
  });
751
+ app.post("/embeddings/warmup", async (c) => {
752
+ try {
753
+ const { LocalEmbeddingProvider } = await import("./local-IHKJFQJS.js");
754
+ const provider = new LocalEmbeddingProvider({});
755
+ const success = await provider.warmup();
756
+ return c.json({
757
+ success,
758
+ model: provider.model,
759
+ dimensions: provider.dimensions
760
+ });
761
+ } catch (error) {
762
+ return c.json(
763
+ { success: false, error: error instanceof Error ? error.message : String(error) },
764
+ 500
765
+ );
766
+ }
767
+ });
768
+ app.get("/embeddings/status", (c) => {
769
+ const model = "Xenova/all-MiniLM-L6-v2";
770
+ const modelPath = join2(TELETON_ROOT, "models", model, "onnx", "model.onnx");
771
+ try {
772
+ const stats = statSync(modelPath);
773
+ return c.json({ cached: true, model, modelPath, sizeBytes: stats.size });
774
+ } catch {
775
+ return c.json({ cached: false, model, modelPath });
776
+ }
777
+ });
747
778
  app.post("/config/save", async (c) => {
748
779
  try {
749
780
  const input = await c.req.json();
@@ -840,7 +871,14 @@ function createSetupRoutes() {
840
871
  ...input.cocoon ? { cocoon: input.cocoon } : {},
841
872
  ...input.tonapi_key ? { tonapi_key: input.tonapi_key } : {},
842
873
  ...input.toncenter_api_key ? { toncenter_api_key: input.toncenter_api_key } : {},
843
- ...input.tavily_api_key ? { tavily_api_key: input.tavily_api_key } : {}
874
+ ...input.tavily_api_key ? { tavily_api_key: input.tavily_api_key } : {},
875
+ // Persist Management API key hash so it survives reboots
876
+ api: {
877
+ enabled: true,
878
+ port: 7778,
879
+ host: "0.0.0.0",
880
+ ...options?.keyHash ? { key_hash: options.keyHash } : {}
881
+ }
844
882
  };
845
883
  ConfigSchema.parse(config);
846
884
  const configPath = workspace.configPath;