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
@@ -0,0 +1,934 @@
1
+ import {
2
+ ConfigSchema,
3
+ DealsConfigSchema,
4
+ ensureWorkspace,
5
+ generateWallet,
6
+ getWalletAddress,
7
+ importWallet,
8
+ isNewWorkspace,
9
+ saveWallet,
10
+ walletExists
11
+ } from "./chunk-NERLQY2H.js";
12
+ import "./chunk-QUAPFI2N.js";
13
+ import {
14
+ getProviderMetadata,
15
+ getSupportedProviders,
16
+ validateApiKeyFormat
17
+ } from "./chunk-LRCPA7SC.js";
18
+ import "./chunk-XBKSS6DM.js";
19
+ import {
20
+ TELEGRAM_MAX_MESSAGE_LENGTH
21
+ } from "./chunk-RO62LO6Z.js";
22
+ import {
23
+ fetchWithTimeout
24
+ } from "./chunk-VAUJSSD3.js";
25
+ import "./chunk-4DU3C27M.js";
26
+ import {
27
+ TELETON_ROOT
28
+ } from "./chunk-EYWNOHMJ.js";
29
+ import {
30
+ createLogger
31
+ } from "./chunk-RCMD3U65.js";
32
+ import "./chunk-QGM4M3NI.js";
33
+
34
+ // src/webui/setup-server.ts
35
+ import { Hono as Hono2 } from "hono";
36
+ import { serve } from "@hono/node-server";
37
+ import { cors } from "hono/cors";
38
+ import { bodyLimit } from "hono/body-limit";
39
+ import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
40
+ import { join as join3, dirname as dirname2, resolve, relative } from "path";
41
+ import { fileURLToPath } from "url";
42
+ import { exec } from "child_process";
43
+ import { platform } from "os";
44
+
45
+ // src/webui/routes/setup.ts
46
+ import { Hono } from "hono";
47
+ import { existsSync as existsSync2, readFileSync, writeFileSync as writeFileSync2 } from "fs";
48
+ import { join as join2 } from "path";
49
+ import YAML from "yaml";
50
+
51
+ // src/webui/setup-auth.ts
52
+ import { TelegramClient, Api } from "telegram";
53
+ import { StringSession } from "telegram/sessions/index.js";
54
+ import { computeCheck } from "telegram/Password.js";
55
+ import { Logger, LogLevel } from "telegram/extensions/Logger.js";
56
+ import { writeFileSync, mkdirSync, existsSync } from "fs";
57
+ import { dirname, join } from "path";
58
+ import { randomBytes } from "crypto";
59
+ var log = createLogger("Setup");
60
+ var SESSION_TTL_MS = 5 * 60 * 1e3;
61
+ var MAX_CODE_ATTEMPTS = 5;
62
+ var MAX_PASSWORD_ATTEMPTS = 3;
63
+ var TelegramAuthManager = class {
64
+ session = null;
65
+ /**
66
+ * Send verification code to phone number
67
+ */
68
+ async sendCode(apiId, apiHash, phone) {
69
+ await this.cleanup();
70
+ const gramLogger = new Logger(LogLevel.NONE);
71
+ const client = new TelegramClient(new StringSession(""), apiId, apiHash, {
72
+ connectionRetries: 3,
73
+ floodSleepThreshold: 0,
74
+ baseLogger: gramLogger
75
+ });
76
+ await client.connect();
77
+ const result = await client.sendCode({ apiId, apiHash }, phone);
78
+ const id = randomBytes(16).toString("hex");
79
+ const expiresAt = Date.now() + SESSION_TTL_MS;
80
+ this.session = {
81
+ id,
82
+ client,
83
+ phone,
84
+ phoneCodeHash: result.phoneCodeHash,
85
+ state: "code_sent",
86
+ codeAttempts: 0,
87
+ passwordAttempts: 0,
88
+ createdAt: Date.now(),
89
+ apiId,
90
+ apiHash,
91
+ timer: setTimeout(() => this.cleanup(), SESSION_TTL_MS)
92
+ };
93
+ log.info("Telegram verification code sent");
94
+ return { authSessionId: id, codeViaApp: result.isCodeViaApp, expiresAt };
95
+ }
96
+ /**
97
+ * Verify the code entered by the user
98
+ */
99
+ async verifyCode(authSessionId, code) {
100
+ const session = this.getSession(authSessionId);
101
+ if (!session) return { status: "expired" };
102
+ if (session.codeAttempts >= MAX_CODE_ATTEMPTS) {
103
+ return { status: "too_many_attempts" };
104
+ }
105
+ session.codeAttempts++;
106
+ try {
107
+ const result = await session.client.invoke(
108
+ new Api.auth.SignIn({
109
+ phoneNumber: session.phone,
110
+ phoneCodeHash: session.phoneCodeHash,
111
+ phoneCode: code
112
+ })
113
+ );
114
+ session.state = "authenticated";
115
+ const user = this.extractUser(result);
116
+ await this.saveSession(session);
117
+ log.info("Telegram authentication successful");
118
+ return { status: "authenticated", user };
119
+ } catch (err) {
120
+ const error = err;
121
+ if (error.errorMessage === "SESSION_PASSWORD_NEEDED") {
122
+ session.state = "2fa_required";
123
+ try {
124
+ const passwordResult = await session.client.invoke(new Api.account.GetPassword());
125
+ session.passwordHint = passwordResult.hint ?? void 0;
126
+ } catch {
127
+ }
128
+ return { status: "2fa_required", passwordHint: session.passwordHint };
129
+ }
130
+ if (error.errorMessage === "PHONE_CODE_INVALID") {
131
+ return { status: "invalid_code" };
132
+ }
133
+ if (error.errorMessage === "PHONE_CODE_EXPIRED") {
134
+ session.state = "failed";
135
+ return { status: "expired" };
136
+ }
137
+ throw err;
138
+ }
139
+ }
140
+ /**
141
+ * Verify 2FA password
142
+ */
143
+ async verifyPassword(authSessionId, password) {
144
+ const session = this.getSession(authSessionId);
145
+ if (!session) return { status: "expired" };
146
+ if (session.state !== "2fa_required") return { status: "expired" };
147
+ if (session.passwordAttempts >= MAX_PASSWORD_ATTEMPTS) {
148
+ return { status: "too_many_attempts" };
149
+ }
150
+ session.passwordAttempts++;
151
+ try {
152
+ const srpResult = await session.client.invoke(new Api.account.GetPassword());
153
+ const srpCheck = await computeCheck(srpResult, password);
154
+ const result = await session.client.invoke(
155
+ new Api.auth.CheckPassword({ password: srpCheck })
156
+ );
157
+ session.state = "authenticated";
158
+ const user = this.extractUser(result);
159
+ await this.saveSession(session);
160
+ log.info("Telegram 2FA authentication successful");
161
+ return { status: "authenticated", user };
162
+ } catch (err) {
163
+ const error = err;
164
+ if (error.errorMessage === "PASSWORD_HASH_INVALID") {
165
+ return { status: "invalid_password" };
166
+ }
167
+ throw err;
168
+ }
169
+ }
170
+ /**
171
+ * Resend verification code
172
+ */
173
+ async resendCode(authSessionId) {
174
+ const session = this.getSession(authSessionId);
175
+ if (!session || session.state !== "code_sent") return null;
176
+ const result = await session.client.invoke(
177
+ new Api.auth.ResendCode({
178
+ phoneNumber: session.phone,
179
+ phoneCodeHash: session.phoneCodeHash
180
+ })
181
+ );
182
+ if (result instanceof Api.auth.SentCode) {
183
+ session.phoneCodeHash = result.phoneCodeHash;
184
+ session.codeAttempts = 0;
185
+ const codeViaApp = result.type instanceof Api.auth.SentCodeTypeApp;
186
+ return { codeViaApp };
187
+ }
188
+ return { codeViaApp: false };
189
+ }
190
+ /**
191
+ * Cancel and clean up session
192
+ */
193
+ async cancelSession(authSessionId) {
194
+ if (this.session?.id === authSessionId) {
195
+ await this.cleanup();
196
+ }
197
+ }
198
+ /**
199
+ * Clean up: disconnect client, clear timer, remove session
200
+ */
201
+ async cleanup() {
202
+ if (!this.session) return;
203
+ clearTimeout(this.session.timer);
204
+ try {
205
+ if (this.session.client.connected) {
206
+ await this.session.client.disconnect();
207
+ }
208
+ } catch (err) {
209
+ log.warn({ err }, "Error disconnecting auth client");
210
+ }
211
+ this.session = null;
212
+ }
213
+ getSession(id) {
214
+ if (!this.session || this.session.id !== id) return null;
215
+ if (Date.now() - this.session.createdAt > SESSION_TTL_MS) {
216
+ this.cleanup();
217
+ return null;
218
+ }
219
+ return this.session;
220
+ }
221
+ extractUser(result) {
222
+ if (result instanceof Api.auth.Authorization && result.user instanceof Api.User) {
223
+ return {
224
+ id: Number(result.user.id),
225
+ firstName: result.user.firstName ?? "",
226
+ username: result.user.username ?? void 0
227
+ };
228
+ }
229
+ return void 0;
230
+ }
231
+ async saveSession(session) {
232
+ const sessionString = session.client.session.save();
233
+ const sessionPath = join(TELETON_ROOT, "telegram_session.txt");
234
+ const dir = dirname(sessionPath);
235
+ if (!existsSync(dir)) {
236
+ mkdirSync(dir, { recursive: true });
237
+ }
238
+ writeFileSync(sessionPath, sessionString, { mode: 384 });
239
+ log.info("Telegram session saved");
240
+ }
241
+ };
242
+
243
+ // src/webui/routes/setup.ts
244
+ var log2 = createLogger("Setup");
245
+ var MODEL_OPTIONS = {
246
+ anthropic: [
247
+ {
248
+ value: "claude-opus-4-5-20251101",
249
+ name: "Claude Opus 4.5",
250
+ description: "Most capable, $5/M"
251
+ },
252
+ { value: "claude-sonnet-4-0", name: "Claude Sonnet 4", description: "Balanced, $3/M" },
253
+ {
254
+ value: "claude-haiku-4-5-20251001",
255
+ name: "Claude Haiku 4.5",
256
+ description: "Fast & cheap, $1/M"
257
+ },
258
+ {
259
+ value: "claude-3-5-haiku-20241022",
260
+ name: "Claude 3.5 Haiku",
261
+ description: "Cheapest, $0.80/M"
262
+ }
263
+ ],
264
+ openai: [
265
+ { value: "gpt-5", name: "GPT-5", description: "Most capable, 400K ctx, $1.25/M" },
266
+ { value: "gpt-4o", name: "GPT-4o", description: "Balanced, 128K ctx, $2.50/M" },
267
+ { value: "gpt-4.1", name: "GPT-4.1", description: "1M ctx, $2/M" },
268
+ { value: "gpt-4.1-mini", name: "GPT-4.1 Mini", description: "1M ctx, cheap, $0.40/M" },
269
+ { value: "o3", name: "o3", description: "Reasoning, 200K ctx, $2/M" }
270
+ ],
271
+ google: [
272
+ { value: "gemini-2.5-flash", name: "Gemini 2.5 Flash", description: "Fast, 1M ctx, $0.30/M" },
273
+ {
274
+ value: "gemini-2.5-pro",
275
+ name: "Gemini 2.5 Pro",
276
+ description: "Most capable, 1M ctx, $1.25/M"
277
+ },
278
+ { value: "gemini-2.0-flash", name: "Gemini 2.0 Flash", description: "Cheap, 1M ctx, $0.10/M" }
279
+ ],
280
+ xai: [
281
+ { value: "grok-4-fast", name: "Grok 4 Fast", description: "Vision, 2M ctx, $0.20/M" },
282
+ { value: "grok-4", name: "Grok 4", description: "Reasoning, 256K ctx, $3/M" },
283
+ { value: "grok-3", name: "Grok 3", description: "Stable, 131K ctx, $3/M" }
284
+ ],
285
+ groq: [
286
+ {
287
+ value: "meta-llama/llama-4-maverick-17b-128e-instruct",
288
+ name: "Llama 4 Maverick",
289
+ description: "Vision, 131K ctx, $0.20/M"
290
+ },
291
+ { value: "qwen/qwen3-32b", name: "Qwen3 32B", description: "Reasoning, 131K ctx, $0.29/M" },
292
+ {
293
+ value: "deepseek-r1-distill-llama-70b",
294
+ name: "DeepSeek R1 70B",
295
+ description: "Reasoning, 131K ctx, $0.75/M"
296
+ },
297
+ {
298
+ value: "llama-3.3-70b-versatile",
299
+ name: "Llama 3.3 70B",
300
+ description: "General purpose, 131K ctx, $0.59/M"
301
+ }
302
+ ],
303
+ openrouter: [
304
+ { value: "anthropic/claude-opus-4.5", name: "Claude Opus 4.5", description: "200K ctx, $5/M" },
305
+ { value: "openai/gpt-5", name: "GPT-5", description: "400K ctx, $1.25/M" },
306
+ { value: "google/gemini-2.5-flash", name: "Gemini 2.5 Flash", description: "1M ctx, $0.30/M" },
307
+ {
308
+ value: "deepseek/deepseek-r1",
309
+ name: "DeepSeek R1",
310
+ description: "Reasoning, 64K ctx, $0.70/M"
311
+ },
312
+ { value: "x-ai/grok-4", name: "Grok 4", description: "256K ctx, $3/M" }
313
+ ],
314
+ moonshot: [
315
+ { value: "kimi-k2.5", name: "Kimi K2.5", description: "Free, 256K ctx, multimodal" },
316
+ {
317
+ value: "kimi-k2-thinking",
318
+ name: "Kimi K2 Thinking",
319
+ description: "Free, 256K ctx, reasoning"
320
+ }
321
+ ],
322
+ mistral: [
323
+ {
324
+ value: "devstral-small-2507",
325
+ name: "Devstral Small",
326
+ description: "Coding, 128K ctx, $0.10/M"
327
+ },
328
+ {
329
+ value: "devstral-medium-latest",
330
+ name: "Devstral Medium",
331
+ description: "Coding, 262K ctx, $0.40/M"
332
+ },
333
+ {
334
+ value: "mistral-large-latest",
335
+ name: "Mistral Large",
336
+ description: "General, 128K ctx, $2/M"
337
+ },
338
+ {
339
+ value: "magistral-small",
340
+ name: "Magistral Small",
341
+ description: "Reasoning, 128K ctx, $0.50/M"
342
+ }
343
+ ]
344
+ };
345
+ function maskKey(key) {
346
+ if (key.length <= 10) return "***";
347
+ return key.slice(0, 6) + "..." + key.slice(-4);
348
+ }
349
+ function createSetupRoutes() {
350
+ const app = new Hono();
351
+ const authManager = new TelegramAuthManager();
352
+ app.get("/status", async (c) => {
353
+ try {
354
+ const configPath = join2(TELETON_ROOT, "config.yaml");
355
+ const sessionPath = join2(TELETON_ROOT, "telegram_session.txt");
356
+ const envApiKey = process.env.TELETON_API_KEY;
357
+ const envApiId = process.env.TELETON_TG_API_ID;
358
+ const envApiHash = process.env.TELETON_TG_API_HASH;
359
+ const envPhone = process.env.TELETON_TG_PHONE;
360
+ return c.json({
361
+ success: true,
362
+ data: {
363
+ workspaceExists: existsSync2(join2(TELETON_ROOT, "workspace")),
364
+ configExists: existsSync2(configPath),
365
+ walletExists: walletExists(),
366
+ walletAddress: getWalletAddress(),
367
+ sessionExists: existsSync2(sessionPath),
368
+ envVars: {
369
+ apiKey: envApiKey ? maskKey(envApiKey) : null,
370
+ apiKeyRaw: !!envApiKey,
371
+ telegramApiId: envApiId ?? null,
372
+ telegramApiHash: envApiHash ? maskKey(envApiHash) : null,
373
+ telegramPhone: envPhone ?? null
374
+ }
375
+ }
376
+ });
377
+ } catch (err) {
378
+ return c.json(
379
+ { success: false, error: err instanceof Error ? err.message : String(err) },
380
+ 500
381
+ );
382
+ }
383
+ });
384
+ app.get("/providers", (c) => {
385
+ const providers = getSupportedProviders().map((p) => ({
386
+ id: p.id,
387
+ displayName: p.displayName,
388
+ defaultModel: p.defaultModel,
389
+ utilityModel: p.utilityModel,
390
+ toolLimit: p.toolLimit,
391
+ keyPrefix: p.keyPrefix,
392
+ consoleUrl: p.consoleUrl,
393
+ requiresApiKey: p.id !== "cocoon" && p.id !== "local",
394
+ requiresBaseUrl: p.id === "local"
395
+ }));
396
+ return c.json({ success: true, data: providers });
397
+ });
398
+ app.get("/models/:provider", (c) => {
399
+ const provider = c.req.param("provider");
400
+ const models = MODEL_OPTIONS[provider] || [];
401
+ const result = [
402
+ ...models,
403
+ {
404
+ value: "__custom__",
405
+ name: "Custom",
406
+ description: "Enter a model ID manually",
407
+ isCustom: true
408
+ }
409
+ ];
410
+ return c.json({ success: true, data: result });
411
+ });
412
+ app.post("/validate/api-key", async (c) => {
413
+ try {
414
+ const body = await c.req.json();
415
+ const error = validateApiKeyFormat(body.provider, body.apiKey);
416
+ return c.json({ success: true, data: { valid: !error, error } });
417
+ } catch (err) {
418
+ return c.json(
419
+ { success: false, error: err instanceof Error ? err.message : String(err) },
420
+ 400
421
+ );
422
+ }
423
+ });
424
+ app.post("/validate/bot-token", async (c) => {
425
+ try {
426
+ const body = await c.req.json();
427
+ if (!body.token || !body.token.includes(":")) {
428
+ return c.json({
429
+ success: true,
430
+ data: { valid: false, networkError: false, error: "Invalid format (expected id:hash)" }
431
+ });
432
+ }
433
+ try {
434
+ const res = await fetchWithTimeout(`https://api.telegram.org/bot${body.token}/getMe`);
435
+ const data = await res.json();
436
+ if (!data.ok) {
437
+ return c.json({
438
+ success: true,
439
+ data: { valid: false, networkError: false, error: "Bot token is invalid" }
440
+ });
441
+ }
442
+ return c.json({
443
+ success: true,
444
+ data: {
445
+ valid: true,
446
+ networkError: false,
447
+ bot: { username: data.result.username, firstName: data.result.first_name }
448
+ }
449
+ });
450
+ } catch {
451
+ return c.json({
452
+ success: true,
453
+ data: { valid: false, networkError: true, error: "Could not reach Telegram API" }
454
+ });
455
+ }
456
+ } catch (err) {
457
+ return c.json(
458
+ { success: false, error: err instanceof Error ? err.message : String(err) },
459
+ 400
460
+ );
461
+ }
462
+ });
463
+ app.post("/workspace/init", async (c) => {
464
+ try {
465
+ const body = await c.req.json().catch(() => ({ agentName: void 0, workspaceDir: void 0 }));
466
+ const workspace = await ensureWorkspace({
467
+ workspaceDir: body.workspaceDir,
468
+ ensureTemplates: true
469
+ });
470
+ if (body.agentName?.trim() && existsSync2(workspace.identityPath)) {
471
+ const identity = readFileSync(workspace.identityPath, "utf-8");
472
+ const updated = identity.replace(
473
+ "[Your name - pick one or ask your human]",
474
+ body.agentName.trim()
475
+ );
476
+ writeFileSync2(workspace.identityPath, updated, "utf-8");
477
+ }
478
+ return c.json({
479
+ success: true,
480
+ data: { created: !isNewWorkspace(workspace) === false, path: workspace.root }
481
+ });
482
+ } catch (err) {
483
+ return c.json(
484
+ { success: false, error: err instanceof Error ? err.message : String(err) },
485
+ 500
486
+ );
487
+ }
488
+ });
489
+ app.get("/wallet/status", (c) => {
490
+ const exists = walletExists();
491
+ const address = exists ? getWalletAddress() : void 0;
492
+ return c.json({ success: true, data: { exists, address } });
493
+ });
494
+ app.post("/wallet/generate", async (c) => {
495
+ try {
496
+ const wallet = await generateWallet();
497
+ saveWallet(wallet);
498
+ log2.info("New TON wallet generated via setup UI");
499
+ return c.json({
500
+ success: true,
501
+ data: { address: wallet.address, mnemonic: wallet.mnemonic }
502
+ });
503
+ } catch (err) {
504
+ return c.json(
505
+ { success: false, error: err instanceof Error ? err.message : String(err) },
506
+ 500
507
+ );
508
+ }
509
+ });
510
+ app.post("/wallet/import", async (c) => {
511
+ try {
512
+ const body = await c.req.json();
513
+ const words = body.mnemonic.trim().split(/\s+/);
514
+ if (words.length !== 24) {
515
+ return c.json({ success: false, error: `Expected 24 words, got ${words.length}` }, 400);
516
+ }
517
+ const wallet = await importWallet(words);
518
+ saveWallet(wallet);
519
+ log2.info("TON wallet imported via setup UI");
520
+ return c.json({ success: true, data: { address: wallet.address } });
521
+ } catch (err) {
522
+ return c.json(
523
+ { success: false, error: err instanceof Error ? err.message : String(err) },
524
+ 400
525
+ );
526
+ }
527
+ });
528
+ app.post("/telegram/send-code", async (c) => {
529
+ try {
530
+ const body = await c.req.json();
531
+ if (!body.apiId || !body.apiHash || !body.phone) {
532
+ return c.json({ success: false, error: "Missing apiId, apiHash, or phone" }, 400);
533
+ }
534
+ const result = await authManager.sendCode(body.apiId, body.apiHash, body.phone);
535
+ return c.json({ success: true, data: result });
536
+ } catch (err) {
537
+ const error = err;
538
+ if (error.seconds) {
539
+ return c.json(
540
+ {
541
+ success: false,
542
+ error: `Rate limited. Please wait ${error.seconds} seconds.`
543
+ },
544
+ 429
545
+ );
546
+ }
547
+ return c.json(
548
+ { success: false, error: error.errorMessage || error.message || String(err) },
549
+ 500
550
+ );
551
+ }
552
+ });
553
+ app.post("/telegram/verify-code", async (c) => {
554
+ try {
555
+ const body = await c.req.json();
556
+ if (!body.authSessionId || !body.code) {
557
+ return c.json({ success: false, error: "Missing authSessionId or code" }, 400);
558
+ }
559
+ const result = await authManager.verifyCode(body.authSessionId, body.code);
560
+ return c.json({ success: true, data: result });
561
+ } catch (err) {
562
+ const error = err;
563
+ if (error.seconds) {
564
+ return c.json(
565
+ {
566
+ success: false,
567
+ error: `Rate limited. Please wait ${error.seconds} seconds.`
568
+ },
569
+ 429
570
+ );
571
+ }
572
+ return c.json(
573
+ { success: false, error: error.errorMessage || error.message || String(err) },
574
+ 500
575
+ );
576
+ }
577
+ });
578
+ app.post("/telegram/verify-password", async (c) => {
579
+ try {
580
+ const body = await c.req.json();
581
+ if (!body.authSessionId || !body.password) {
582
+ return c.json({ success: false, error: "Missing authSessionId or password" }, 400);
583
+ }
584
+ const result = await authManager.verifyPassword(body.authSessionId, body.password);
585
+ return c.json({ success: true, data: result });
586
+ } catch (err) {
587
+ const error = err;
588
+ if (error.seconds) {
589
+ return c.json(
590
+ {
591
+ success: false,
592
+ error: `Rate limited. Please wait ${error.seconds} seconds.`
593
+ },
594
+ 429
595
+ );
596
+ }
597
+ return c.json(
598
+ { success: false, error: error.errorMessage || error.message || String(err) },
599
+ 500
600
+ );
601
+ }
602
+ });
603
+ app.post("/telegram/resend-code", async (c) => {
604
+ try {
605
+ const body = await c.req.json();
606
+ if (!body.authSessionId) {
607
+ return c.json({ success: false, error: "Missing authSessionId" }, 400);
608
+ }
609
+ const result = await authManager.resendCode(body.authSessionId);
610
+ if (!result) {
611
+ return c.json({ success: false, error: "Session expired or invalid" }, 400);
612
+ }
613
+ return c.json({ success: true, data: result });
614
+ } catch (err) {
615
+ const error = err;
616
+ if (error.seconds) {
617
+ return c.json(
618
+ {
619
+ success: false,
620
+ error: `Rate limited. Please wait ${error.seconds} seconds.`
621
+ },
622
+ 429
623
+ );
624
+ }
625
+ return c.json(
626
+ { success: false, error: error.errorMessage || error.message || String(err) },
627
+ 500
628
+ );
629
+ }
630
+ });
631
+ app.delete("/telegram/session", async (c) => {
632
+ try {
633
+ const body = await c.req.json().catch(() => ({ authSessionId: "" }));
634
+ await authManager.cancelSession(body.authSessionId);
635
+ return c.json({ success: true });
636
+ } catch (err) {
637
+ return c.json(
638
+ { success: false, error: err instanceof Error ? err.message : String(err) },
639
+ 500
640
+ );
641
+ }
642
+ });
643
+ app.post("/config/save", async (c) => {
644
+ try {
645
+ const input = await c.req.json();
646
+ const workspace = await ensureWorkspace({ ensureTemplates: true });
647
+ const providerMeta = getProviderMetadata(input.agent.provider);
648
+ const config = {
649
+ meta: {
650
+ version: "1.0.0",
651
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
652
+ onboard_command: "teleton setup --ui"
653
+ },
654
+ agent: {
655
+ provider: input.agent.provider,
656
+ api_key: input.agent.api_key ?? "",
657
+ ...input.agent.base_url ? { base_url: input.agent.base_url } : {},
658
+ model: input.agent.model || providerMeta.defaultModel,
659
+ max_tokens: 4096,
660
+ temperature: 0.7,
661
+ system_prompt: null,
662
+ max_agentic_iterations: input.agent.max_agentic_iterations ?? 5,
663
+ session_reset_policy: {
664
+ daily_reset_enabled: true,
665
+ daily_reset_hour: 4,
666
+ idle_expiry_enabled: true,
667
+ idle_expiry_minutes: 1440
668
+ }
669
+ },
670
+ telegram: {
671
+ api_id: input.telegram.api_id,
672
+ api_hash: input.telegram.api_hash,
673
+ phone: input.telegram.phone,
674
+ session_name: "teleton_session",
675
+ session_path: workspace.sessionPath,
676
+ dm_policy: input.telegram.dm_policy ?? "open",
677
+ allow_from: [],
678
+ group_policy: input.telegram.group_policy ?? "open",
679
+ group_allow_from: [],
680
+ require_mention: input.telegram.require_mention ?? true,
681
+ max_message_length: TELEGRAM_MAX_MESSAGE_LENGTH,
682
+ typing_simulation: true,
683
+ rate_limit_messages_per_second: 1,
684
+ rate_limit_groups_per_minute: 20,
685
+ admin_ids: [input.telegram.owner_id],
686
+ owner_id: input.telegram.owner_id,
687
+ agent_channel: null,
688
+ debounce_ms: 1500,
689
+ bot_token: input.telegram.bot_token,
690
+ bot_username: input.telegram.bot_username
691
+ },
692
+ storage: {
693
+ sessions_file: `${workspace.root}/sessions.json`,
694
+ pairing_file: `${workspace.root}/pairing.json`,
695
+ memory_file: `${workspace.root}/memory.json`,
696
+ history_limit: 100
697
+ },
698
+ embedding: { provider: "local" },
699
+ deals: DealsConfigSchema.parse({
700
+ enabled: true,
701
+ ...input.deals ?? {}
702
+ }),
703
+ webui: {
704
+ enabled: input.webui?.enabled ?? false,
705
+ port: 7777,
706
+ host: "127.0.0.1",
707
+ cors_origins: ["http://localhost:5173", "http://localhost:7777"],
708
+ log_requests: false
709
+ },
710
+ logging: { level: "info", pretty: true },
711
+ dev: { hot_reload: false },
712
+ tool_rag: {
713
+ enabled: true,
714
+ top_k: 25,
715
+ always_include: [
716
+ "telegram_send_message",
717
+ "telegram_reply_message",
718
+ "telegram_send_photo",
719
+ "telegram_send_document",
720
+ "journal_*",
721
+ "workspace_*",
722
+ "web_*"
723
+ ],
724
+ skip_unlimited_providers: false
725
+ },
726
+ mcp: { servers: {} },
727
+ plugins: {},
728
+ ...input.cocoon ? { cocoon: input.cocoon } : {},
729
+ ...input.tonapi_key ? { tonapi_key: input.tonapi_key } : {},
730
+ ...input.tavily_api_key ? { tavily_api_key: input.tavily_api_key } : {}
731
+ };
732
+ ConfigSchema.parse(config);
733
+ const configPath = workspace.configPath;
734
+ writeFileSync2(configPath, YAML.stringify(config), { encoding: "utf-8", mode: 384 });
735
+ log2.info(`Configuration saved: ${configPath}`);
736
+ return c.json({ success: true, data: { path: configPath } });
737
+ } catch (err) {
738
+ return c.json(
739
+ { success: false, error: err instanceof Error ? err.message : String(err) },
740
+ 400
741
+ );
742
+ }
743
+ });
744
+ return app;
745
+ }
746
+
747
+ // src/webui/setup-server.ts
748
+ import { randomBytes as randomBytes2 } from "crypto";
749
+ import { readFileSync as readYaml, writeFileSync as writeFileSync3 } from "fs";
750
+ import YAML2 from "yaml";
751
+ var log3 = createLogger("Setup");
752
+ function findWebDist() {
753
+ const candidates = [resolve("dist/web"), resolve("web")];
754
+ const __dirname = dirname2(fileURLToPath(import.meta.url));
755
+ candidates.push(resolve(__dirname, "web"), resolve(__dirname, "../dist/web"));
756
+ for (const candidate of candidates) {
757
+ if (existsSync3(join3(candidate, "index.html"))) {
758
+ return candidate;
759
+ }
760
+ }
761
+ return null;
762
+ }
763
+ function autoOpenBrowser(url) {
764
+ const os = platform();
765
+ let cmd;
766
+ if (os === "darwin") {
767
+ cmd = `open "${url}"`;
768
+ } else if (os === "win32") {
769
+ cmd = `start "${url}"`;
770
+ } else {
771
+ cmd = `xdg-open "${url}"`;
772
+ }
773
+ exec(cmd, (err) => {
774
+ if (err) {
775
+ log3.info(`Open this URL in your browser: ${url}`);
776
+ }
777
+ });
778
+ }
779
+ var SetupServer = class {
780
+ constructor(port = 7777) {
781
+ this.port = port;
782
+ this.app = new Hono2();
783
+ this.launchPromise = new Promise((resolve2) => {
784
+ this.launchResolve = resolve2;
785
+ });
786
+ this.setupMiddleware();
787
+ this.setupRoutes();
788
+ this.setupStaticServing();
789
+ }
790
+ app;
791
+ server = null;
792
+ launchResolve = null;
793
+ launchPromise;
794
+ /** Returns a promise that resolves with the auth token when the user clicks "Start Agent" */
795
+ waitForLaunch() {
796
+ return this.launchPromise;
797
+ }
798
+ setupMiddleware() {
799
+ this.app.use(
800
+ "*",
801
+ cors({
802
+ origin: [
803
+ "http://localhost:5173",
804
+ `http://localhost:${this.port}`,
805
+ "http://127.0.0.1:5173",
806
+ `http://127.0.0.1:${this.port}`
807
+ ],
808
+ credentials: true,
809
+ allowMethods: ["GET", "HEAD", "PUT", "POST", "DELETE", "PATCH"],
810
+ allowHeaders: ["Content-Type"],
811
+ maxAge: 3600
812
+ })
813
+ );
814
+ this.app.use(
815
+ "*",
816
+ bodyLimit({
817
+ maxSize: 2 * 1024 * 1024,
818
+ onError: (c) => c.json({ success: false, error: "Request body too large (max 2MB)" }, 413)
819
+ })
820
+ );
821
+ this.app.use("*", async (c, next) => {
822
+ await next();
823
+ c.res.headers.set("X-Content-Type-Options", "nosniff");
824
+ c.res.headers.set("X-Frame-Options", "DENY");
825
+ c.res.headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
826
+ });
827
+ }
828
+ setupRoutes() {
829
+ this.app.get("/health", (c) => c.json({ status: "ok" }));
830
+ this.app.route("/api/setup", createSetupRoutes());
831
+ this.app.get(
832
+ "/auth/check",
833
+ (c) => c.json({ success: true, data: { authenticated: false, setup: true } })
834
+ );
835
+ this.app.post("/api/setup/launch", async (c) => {
836
+ try {
837
+ const token = randomBytes2(32).toString("hex");
838
+ const configPath = join3(TELETON_ROOT, "config.yaml");
839
+ const raw = readYaml(configPath, "utf-8");
840
+ const config = YAML2.parse(raw);
841
+ config.webui = { ...config.webui || {}, enabled: true, auth_token: token };
842
+ writeFileSync3(configPath, YAML2.stringify(config), { encoding: "utf-8", mode: 384 });
843
+ log3.info("Launch requested \u2014 auth token generated");
844
+ const resolve2 = this.launchResolve;
845
+ this.launchResolve = null;
846
+ if (resolve2) {
847
+ setTimeout(() => resolve2(token), 500);
848
+ }
849
+ return c.json({ success: true, data: { token } });
850
+ } catch (err) {
851
+ return c.json(
852
+ { success: false, error: err instanceof Error ? err.message : String(err) },
853
+ 500
854
+ );
855
+ }
856
+ });
857
+ this.app.onError((err, c) => {
858
+ log3.error({ err }, "Setup server error");
859
+ return c.json({ success: false, error: err.message || "Internal server error" }, 500);
860
+ });
861
+ }
862
+ setupStaticServing() {
863
+ const webDist = findWebDist();
864
+ if (!webDist) return;
865
+ const indexHtml = readFileSync2(join3(webDist, "index.html"), "utf-8");
866
+ const mimeTypes = {
867
+ js: "application/javascript",
868
+ css: "text/css",
869
+ svg: "image/svg+xml",
870
+ png: "image/png",
871
+ jpg: "image/jpeg",
872
+ jpeg: "image/jpeg",
873
+ ico: "image/x-icon",
874
+ json: "application/json",
875
+ woff2: "font/woff2",
876
+ woff: "font/woff"
877
+ };
878
+ this.app.get("*", (c) => {
879
+ const filePath = resolve(join3(webDist, c.req.path));
880
+ const rel = relative(webDist, filePath);
881
+ if (rel.startsWith("..") || resolve(filePath) !== filePath) {
882
+ return c.html(indexHtml);
883
+ }
884
+ try {
885
+ const content = readFileSync2(filePath);
886
+ const ext = filePath.split(".").pop() || "";
887
+ if (mimeTypes[ext]) {
888
+ const immutable = c.req.path.startsWith("/assets/");
889
+ return c.body(content, 200, {
890
+ "Content-Type": mimeTypes[ext],
891
+ "Cache-Control": immutable ? "public, max-age=31536000, immutable" : "public, max-age=3600"
892
+ });
893
+ }
894
+ } catch {
895
+ }
896
+ return c.html(indexHtml);
897
+ });
898
+ }
899
+ async start() {
900
+ return new Promise((resolve2, reject) => {
901
+ try {
902
+ this.server = serve(
903
+ {
904
+ fetch: this.app.fetch,
905
+ hostname: "127.0.0.1",
906
+ port: this.port
907
+ },
908
+ () => {
909
+ const url = `http://localhost:${this.port}/setup`;
910
+ log3.info(`Setup wizard: ${url}`);
911
+ autoOpenBrowser(url);
912
+ resolve2();
913
+ }
914
+ );
915
+ } catch (error) {
916
+ reject(error);
917
+ }
918
+ });
919
+ }
920
+ async stop() {
921
+ if (this.server) {
922
+ return new Promise((resolve2) => {
923
+ this.server.closeAllConnections();
924
+ this.server.close(() => {
925
+ log3.info("Setup server stopped");
926
+ resolve2();
927
+ });
928
+ });
929
+ }
930
+ }
931
+ };
932
+ export {
933
+ SetupServer
934
+ };