teleton 0.8.1 → 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 (43) hide show
  1. package/dist/bootstrap-DDFVEMYI.js +128 -0
  2. package/dist/{server-3FHI2SEB.js → chunk-2ERTYRHA.js} +26 -372
  3. package/dist/{chunk-5FNWBZ5K.js → chunk-33Z47EXI.js} +264 -274
  4. package/dist/{chunk-3S4GGLLR.js → chunk-35MX4ZUI.js} +23 -104
  5. package/dist/chunk-3UFPFWYP.js +12 -0
  6. package/dist/chunk-5SEMA47R.js +75 -0
  7. package/dist/{chunk-PHSAHTK4.js → chunk-6OOHHJ4N.js} +3 -108
  8. package/dist/{chunk-CGOXE4WP.js → chunk-7MWKT67G.js} +467 -914
  9. package/dist/chunk-AEHTQI3H.js +142 -0
  10. package/dist/{chunk-S6PHGKOC.js → chunk-AERHOXGC.js} +88 -322
  11. package/dist/chunk-ALKAAG4O.js +487 -0
  12. package/dist/{chunk-UP55PXFH.js → chunk-C4NKJT2Z.js} +8 -0
  13. package/dist/chunk-CUE4UZXR.js +129 -0
  14. package/dist/chunk-FUNF6H4W.js +251 -0
  15. package/dist/{chunk-7U7BOHCL.js → chunk-GHMXWAXI.js} +147 -63
  16. package/dist/{chunk-QBHRXLZS.js → chunk-H7MFXJZK.js} +2 -2
  17. package/dist/{chunk-QV2GLOTK.js → chunk-LC4TV3KL.js} +1 -1
  18. package/dist/{chunk-AYWEJCDB.js → chunk-LVTKJQ7O.js} +12 -10
  19. package/dist/{chunk-RCMD3U65.js → chunk-NQ6FZKCE.js} +13 -0
  20. package/dist/chunk-NVKBBTI6.js +128 -0
  21. package/dist/{setup-server-32XGDPE6.js → chunk-OIMAE24Q.js} +55 -216
  22. package/dist/{chunk-OJCLKU5Z.js → chunk-WFTC3JJW.js} +16 -0
  23. package/dist/chunk-WTDAICGT.js +175 -0
  24. package/dist/{chunk-KVXV7EF7.js → chunk-XDZDOKIF.js} +2 -2
  25. package/dist/cli/index.js +91 -27
  26. package/dist/{client-MPHPIZB6.js → client-5KD25NOP.js} +5 -4
  27. package/dist/{get-my-gifts-CC6HAVWB.js → get-my-gifts-Y7EN7RK4.js} +3 -3
  28. package/dist/index.js +19 -13
  29. package/dist/local-IHKJFQJS.js +9 -0
  30. package/dist/{memory-UBHM7ILG.js → memory-QMJRM3XJ.js} +9 -5
  31. package/dist/memory-hook-VUNWZ3NY.js +19 -0
  32. package/dist/{migrate-UBBEJ5BL.js → migrate-5VBAP52B.js} +5 -4
  33. package/dist/server-JF6FX772.js +813 -0
  34. package/dist/server-N4T7E25M.js +396 -0
  35. package/dist/setup-server-IX3BFPPH.js +217 -0
  36. package/dist/{store-M5IMUQCL.js → store-BY7S6IFN.js} +6 -5
  37. package/dist/{task-dependency-resolver-RR2O5S7B.js → task-dependency-resolver-L6UUMTHK.js} +2 -2
  38. package/dist/{task-executor-6W5HRX5C.js → task-executor-XBNJLUCS.js} +2 -2
  39. package/dist/{tool-adapter-IH5VGBOO.js → tool-adapter-IVX2XQJE.js} +1 -1
  40. package/dist/{tool-index-PMAOXWUA.js → tool-index-FTERJSZK.js} +4 -3
  41. package/dist/{transcript-NGDPSNIH.js → transcript-IM7G25OS.js} +2 -2
  42. package/package.json +4 -2
  43. package/dist/chunk-XBE4JB7C.js +0 -8
@@ -0,0 +1,813 @@
1
+ import {
2
+ createSetupRoutes
3
+ } from "./chunk-OIMAE24Q.js";
4
+ import {
5
+ createConfigRoutes,
6
+ createHooksRoutes,
7
+ createLogsRoutes,
8
+ createMarketplaceRoutes,
9
+ createMcpRoutes,
10
+ createMemoryRoutes,
11
+ createPluginsRoutes,
12
+ createSoulRoutes,
13
+ createStatusRoutes,
14
+ createTasksRoutes,
15
+ createTonProxyRoutes,
16
+ createToolsRoutes,
17
+ createWorkspaceRoutes,
18
+ logInterceptor
19
+ } from "./chunk-2ERTYRHA.js";
20
+ import {
21
+ ensureTlsCert
22
+ } from "./chunk-5SEMA47R.js";
23
+ import "./chunk-WFTC3JJW.js";
24
+ import "./chunk-7MWKT67G.js";
25
+ import "./chunk-FUNF6H4W.js";
26
+ import "./chunk-AEHTQI3H.js";
27
+ import "./chunk-AERHOXGC.js";
28
+ import "./chunk-XDZDOKIF.js";
29
+ import "./chunk-GHMXWAXI.js";
30
+ import "./chunk-ALKAAG4O.js";
31
+ import "./chunk-3UFPFWYP.js";
32
+ import "./chunk-7TECSLJ4.js";
33
+ import "./chunk-35MX4ZUI.js";
34
+ import "./chunk-VFA7QMCZ.js";
35
+ import "./chunk-C4NKJT2Z.js";
36
+ import "./chunk-LVTKJQ7O.js";
37
+ import "./chunk-WTDAICGT.js";
38
+ import "./chunk-6OOHHJ4N.js";
39
+ import "./chunk-XQUHC3JZ.js";
40
+ import "./chunk-R4YSJ4EY.js";
41
+ import "./chunk-LC4TV3KL.js";
42
+ import "./chunk-CUE4UZXR.js";
43
+ import {
44
+ TELETON_ROOT
45
+ } from "./chunk-EYWNOHMJ.js";
46
+ import {
47
+ createLogger
48
+ } from "./chunk-NQ6FZKCE.js";
49
+ import "./chunk-4L66JHQE.js";
50
+ import "./chunk-3RG5ZIWI.js";
51
+
52
+ // src/api/server.ts
53
+ import { Hono as Hono6 } from "hono";
54
+ import { bodyLimit } from "hono/body-limit";
55
+ import { timeout } from "hono/timeout";
56
+ import { streamSSE as streamSSE2 } from "hono/streaming";
57
+ import { serve } from "@hono/node-server";
58
+ import { createServer as createHttpsServer } from "https";
59
+ import { randomBytes, createHash as createHash2 } from "crypto";
60
+ import { HTTPException as HTTPException3 } from "hono/http-exception";
61
+ import { existsSync, statSync } from "fs";
62
+ import { join as join2 } from "path";
63
+
64
+ // src/api/deps.ts
65
+ import { HTTPException } from "hono/http-exception";
66
+ function createDepsAdapter(apiDeps) {
67
+ const handler = {
68
+ get(target, prop, receiver) {
69
+ const value = Reflect.get(target, prop, receiver);
70
+ if (prop === "config" || prop === "configPath" || prop === "lifecycle" || prop === "userHookEvaluator" || prop === "marketplace") {
71
+ return value;
72
+ }
73
+ if (value === null || value === void 0) {
74
+ throw new HTTPException(503, {
75
+ message: `Service unavailable: ${String(prop)} is not initialized (agent not running)`
76
+ });
77
+ }
78
+ return value;
79
+ }
80
+ };
81
+ return new Proxy(apiDeps, handler);
82
+ }
83
+
84
+ // src/api/schemas/common.ts
85
+ import { z } from "zod";
86
+ var ProblemDetailSchema = z.object({
87
+ type: z.string().default("about:blank"),
88
+ title: z.string(),
89
+ status: z.number(),
90
+ detail: z.string().optional(),
91
+ instance: z.string().optional()
92
+ });
93
+ function createProblem(status, title, detail, instance) {
94
+ return {
95
+ type: "about:blank",
96
+ title,
97
+ status,
98
+ ...detail ? { detail } : {},
99
+ ...instance ? { instance } : {}
100
+ };
101
+ }
102
+ function createProblemResponse(c, status, title, detail, headers) {
103
+ const body = createProblem(status, title, detail, c.req.path);
104
+ return c.json(body, status, {
105
+ "Content-Type": "application/problem+json",
106
+ ...headers ?? {}
107
+ });
108
+ }
109
+
110
+ // src/api/middleware/request-id.ts
111
+ import { randomUUID } from "crypto";
112
+ var requestId = async (c, next) => {
113
+ const id = c.req.header("X-Request-Id") || randomUUID();
114
+ c.set("requestId", id);
115
+ c.header("X-Request-Id", id);
116
+ await next();
117
+ };
118
+
119
+ // src/api/middleware/auth.ts
120
+ import { createHash, timingSafeEqual } from "crypto";
121
+ import { HTTPException as HTTPException2 } from "hono/http-exception";
122
+ var MAX_FAILED = 10;
123
+ var WINDOW_MS = 5 * 60 * 1e3;
124
+ var BLOCK_MS = 15 * 60 * 1e3;
125
+ function normalizeIp(ip) {
126
+ if (ip.startsWith("::ffff:")) return ip.slice(7);
127
+ return ip;
128
+ }
129
+ function hashApiKey(key) {
130
+ return createHash("sha256").update(key).digest("hex");
131
+ }
132
+ function createAuthMiddleware(config) {
133
+ const failedAttempts = /* @__PURE__ */ new Map();
134
+ const cleanupInterval = setInterval(() => {
135
+ const now = Date.now();
136
+ for (const [ip, attempt] of failedAttempts) {
137
+ if (attempt.blockedUntil < now && now - attempt.blockedUntil > WINDOW_MS) {
138
+ failedAttempts.delete(ip);
139
+ }
140
+ }
141
+ }, WINDOW_MS);
142
+ cleanupInterval.unref();
143
+ return async (c, next) => {
144
+ const rawIp = c.env?.ip ?? c.req.header("x-real-ip") ?? "unknown";
145
+ const sourceIp = normalizeIp(rawIp);
146
+ if (config.allowedIps.length > 0 && !config.allowedIps.includes(sourceIp)) {
147
+ throw new HTTPException2(403, {
148
+ res: createProblemResponse(c, 403, "Forbidden", "IP address not in whitelist")
149
+ });
150
+ }
151
+ const attempt = failedAttempts.get(sourceIp);
152
+ if (attempt && attempt.blockedUntil > Date.now()) {
153
+ const retryAfter = Math.ceil((attempt.blockedUntil - Date.now()) / 1e3);
154
+ throw new HTTPException2(429, {
155
+ res: createProblemResponse(
156
+ c,
157
+ 429,
158
+ "Too Many Requests",
159
+ `IP blocked due to too many failed auth attempts. Retry after ${retryAfter}s`,
160
+ { "Retry-After": String(retryAfter) }
161
+ )
162
+ });
163
+ }
164
+ const authHeader = c.req.header("Authorization");
165
+ if (!authHeader) {
166
+ throw new HTTPException2(401, {
167
+ res: createProblemResponse(
168
+ c,
169
+ 401,
170
+ "Unauthorized",
171
+ "Missing Authorization header. Use: Authorization: Bearer tltn_..."
172
+ )
173
+ });
174
+ }
175
+ const match = authHeader.match(/^Bearer\s+(tltn_.+)$/);
176
+ if (!match) {
177
+ throw new HTTPException2(401, {
178
+ res: createProblemResponse(
179
+ c,
180
+ 401,
181
+ "Unauthorized",
182
+ "Invalid Authorization format. Expected: Bearer tltn_..."
183
+ )
184
+ });
185
+ }
186
+ const apiKey = match[1];
187
+ const keyHash = hashApiKey(apiKey);
188
+ const storedBuf = Buffer.from(config.keyHash, "hex");
189
+ const providedBuf = Buffer.from(keyHash, "hex");
190
+ if (storedBuf.length !== providedBuf.length || !timingSafeEqual(storedBuf, providedBuf)) {
191
+ const existing = failedAttempts.get(sourceIp);
192
+ const count = (existing?.count ?? 0) + 1;
193
+ if (count >= MAX_FAILED) {
194
+ failedAttempts.set(sourceIp, {
195
+ count,
196
+ blockedUntil: Date.now() + BLOCK_MS
197
+ });
198
+ } else {
199
+ failedAttempts.set(sourceIp, {
200
+ count,
201
+ blockedUntil: 0
202
+ });
203
+ }
204
+ throw new HTTPException2(401, {
205
+ res: createProblemResponse(c, 401, "Unauthorized", "Invalid API key")
206
+ });
207
+ }
208
+ failedAttempts.delete(sourceIp);
209
+ c.set("keyPrefix", apiKey.slice(0, 10));
210
+ await next();
211
+ };
212
+ }
213
+
214
+ // src/api/middleware/rate-limit.ts
215
+ import { rateLimiter } from "hono-rate-limiter";
216
+ function keyGenerator(c) {
217
+ return c.get("keyPrefix") || "anonymous";
218
+ }
219
+ function createLimiter(windowMs, limit) {
220
+ return rateLimiter({
221
+ windowMs,
222
+ limit,
223
+ keyGenerator,
224
+ handler: (c) => {
225
+ const retryAfter = Math.ceil(windowMs / 1e3);
226
+ return createProblemResponse(
227
+ c,
228
+ 429,
229
+ "Too Many Requests",
230
+ `Rate limit exceeded. Try again in ${retryAfter}s`,
231
+ { "Retry-After": String(retryAfter) }
232
+ );
233
+ }
234
+ });
235
+ }
236
+ var globalRateLimit = createLimiter(6e4, 60);
237
+ var mutatingRateLimit = async (c, next) => {
238
+ const method = c.req.method;
239
+ if (method === "GET" || method === "HEAD" || method === "OPTIONS") {
240
+ return next();
241
+ }
242
+ return createLimiter(6e4, 10)(c, next);
243
+ };
244
+ var readRateLimit = async (c, next) => {
245
+ if (c.req.method !== "GET") {
246
+ return next();
247
+ }
248
+ return createLimiter(6e4, 300)(c, next);
249
+ };
250
+
251
+ // src/api/middleware/audit.ts
252
+ var auditLog = createLogger("Audit");
253
+ var auditMiddleware = async (c, next) => {
254
+ const method = c.req.method;
255
+ if (method === "GET" || method === "HEAD" || method === "OPTIONS") {
256
+ return next();
257
+ }
258
+ const start = Date.now();
259
+ await next();
260
+ const durationMs = Date.now() - start;
261
+ auditLog.warn({
262
+ audit: true,
263
+ requestId: c.get("requestId"),
264
+ event: "api_mutation",
265
+ method,
266
+ path: c.req.path,
267
+ statusCode: c.res.status,
268
+ durationMs,
269
+ sourceIp: c.env?.ip ?? "unknown",
270
+ keyPrefix: c.get("keyPrefix") ?? "unknown"
271
+ });
272
+ };
273
+
274
+ // src/api/routes/agent.ts
275
+ import { Hono } from "hono";
276
+ var log = createLogger("ManagementAPI");
277
+ function createAgentRoutes(lifecycle) {
278
+ const app = new Hono();
279
+ app.post("/restart", async (c) => {
280
+ if (!lifecycle) {
281
+ return createProblemResponse(c, 503, "Service Unavailable", "Agent lifecycle not available");
282
+ }
283
+ const state = lifecycle.getState();
284
+ if (state === "starting" || state === "stopping") {
285
+ return createProblemResponse(c, 409, "Conflict", `Agent is currently ${state}, please wait`);
286
+ }
287
+ (async () => {
288
+ try {
289
+ if (lifecycle.getState() === "running") {
290
+ await lifecycle.stop();
291
+ }
292
+ await lifecycle.start();
293
+ log.info("Agent restarted via Management API");
294
+ } catch (err) {
295
+ log.error({ err }, "Agent restart failed");
296
+ }
297
+ })().catch(() => {
298
+ });
299
+ return c.json({ state: "restarting" });
300
+ });
301
+ return app;
302
+ }
303
+
304
+ // src/api/routes/system.ts
305
+ import { Hono as Hono2 } from "hono";
306
+ import { readFileSync } from "fs";
307
+ import { join, dirname } from "path";
308
+ import { fileURLToPath } from "url";
309
+ import os from "os";
310
+ var API_VERSION = "1.0.0";
311
+ function readPackageVersion() {
312
+ try {
313
+ const __dirname = dirname(fileURLToPath(import.meta.url));
314
+ const candidates = [
315
+ join(__dirname, "../../package.json"),
316
+ join(__dirname, "../../../package.json")
317
+ ];
318
+ for (const p of candidates) {
319
+ try {
320
+ const pkg = JSON.parse(readFileSync(p, "utf-8"));
321
+ return pkg.version ?? "unknown";
322
+ } catch {
323
+ continue;
324
+ }
325
+ }
326
+ } catch {
327
+ }
328
+ return "unknown";
329
+ }
330
+ var cachedVersion = readPackageVersion();
331
+ function createSystemRoutes() {
332
+ const app = new Hono2();
333
+ app.get("/version", (c) => {
334
+ return c.json({
335
+ teleton: cachedVersion,
336
+ node: process.version,
337
+ os: process.platform,
338
+ arch: process.arch,
339
+ apiVersion: API_VERSION
340
+ });
341
+ });
342
+ app.get("/info", (c) => {
343
+ const cpus = os.cpus();
344
+ const totalMem = os.totalmem();
345
+ const freeMem = os.freemem();
346
+ return c.json({
347
+ cpu: {
348
+ model: cpus[0]?.model ?? "unknown",
349
+ cores: cpus.length,
350
+ loadAvg: os.loadavg()
351
+ },
352
+ memory: {
353
+ total: totalMem,
354
+ free: freeMem,
355
+ used: totalMem - freeMem,
356
+ heapUsed: process.memoryUsage().heapUsed,
357
+ heapTotal: process.memoryUsage().heapTotal
358
+ },
359
+ uptime: {
360
+ process: Math.floor(process.uptime()),
361
+ system: Math.floor(os.uptime())
362
+ }
363
+ });
364
+ });
365
+ return app;
366
+ }
367
+
368
+ // src/api/routes/auth.ts
369
+ import { Hono as Hono3 } from "hono";
370
+ function createAuthRoutes() {
371
+ const app = new Hono3();
372
+ app.post("/validate", (c) => {
373
+ const keyPrefix = c.req.header("authorization")?.slice(7, 17) ?? "unknown";
374
+ return c.json({ valid: true, keyPrefix });
375
+ });
376
+ return app;
377
+ }
378
+
379
+ // src/api/routes/logs.ts
380
+ import { Hono as Hono4 } from "hono";
381
+ import { streamSSE } from "hono/streaming";
382
+ function createApiLogsRoutes() {
383
+ const app = new Hono4();
384
+ app.get("/recent", (c) => {
385
+ const linesParam = c.req.query("lines");
386
+ const lines = Math.min(Math.max(parseInt(linesParam || "100", 10) || 100, 1), 1e3);
387
+ const entries = [];
388
+ return c.json({
389
+ lines: entries.slice(-lines),
390
+ count: entries.length,
391
+ note: "Use GET /v1/logs/stream (SSE) for live log streaming"
392
+ });
393
+ });
394
+ app.get("/stream", (c) => {
395
+ return streamSSE(c, async (stream) => {
396
+ let aborted = false;
397
+ stream.onAbort(() => {
398
+ aborted = true;
399
+ if (cleanup) cleanup();
400
+ });
401
+ const cleanup = logInterceptor.addListener((entry) => {
402
+ if (!aborted) {
403
+ void stream.writeSSE({
404
+ data: JSON.stringify(entry),
405
+ event: "log"
406
+ });
407
+ }
408
+ });
409
+ await stream.writeSSE({
410
+ data: JSON.stringify({
411
+ level: "log",
412
+ message: "Management API log stream connected",
413
+ timestamp: Date.now()
414
+ }),
415
+ event: "log"
416
+ });
417
+ await new Promise((resolve) => {
418
+ if (aborted) return resolve();
419
+ stream.onAbort(() => resolve());
420
+ });
421
+ if (cleanup) cleanup();
422
+ });
423
+ });
424
+ return app;
425
+ }
426
+
427
+ // src/api/routes/memory.ts
428
+ import { Hono as Hono5 } from "hono";
429
+ var log2 = createLogger("ManagementAPI");
430
+ function createApiMemoryRoutes(getDb) {
431
+ const app = new Hono5();
432
+ app.delete("/sessions/:chatId", (c) => {
433
+ const db = getDb();
434
+ if (!db) {
435
+ return createProblemResponse(c, 503, "Service Unavailable", "Database not available");
436
+ }
437
+ const chatId = c.req.param("chatId");
438
+ const result = db.prepare("DELETE FROM sessions WHERE chat_id = ?").run(chatId);
439
+ if (result.changes === 0) {
440
+ return createProblemResponse(c, 404, "Not Found", `No session found for chat ${chatId}`);
441
+ }
442
+ log2.info(`Session deleted for chat ${chatId} via Management API`);
443
+ return c.json({ deleted: result.changes, chatId });
444
+ });
445
+ app.post("/sessions/prune", async (c) => {
446
+ const db = getDb();
447
+ if (!db) {
448
+ return createProblemResponse(c, 503, "Service Unavailable", "Database not available");
449
+ }
450
+ let maxAgeDays = 30;
451
+ try {
452
+ const body = await c.req.json();
453
+ if (body.maxAgeDays && body.maxAgeDays > 0) {
454
+ maxAgeDays = body.maxAgeDays;
455
+ }
456
+ } catch {
457
+ }
458
+ const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1e3;
459
+ const result = db.prepare("DELETE FROM sessions WHERE updated_at < ?").run(cutoff);
460
+ log2.info(`Pruned ${result.changes} sessions older than ${maxAgeDays} days via Management API`);
461
+ return c.json({ pruned: result.changes, maxAgeDays });
462
+ });
463
+ return app;
464
+ }
465
+
466
+ // src/api/server.ts
467
+ var log3 = createLogger("ManagementAPI");
468
+ var KEY_PREFIX = "tltn_";
469
+ function generateApiKey() {
470
+ return KEY_PREFIX + randomBytes(32).toString("base64url");
471
+ }
472
+ function hashApiKey2(key) {
473
+ return createHash2("sha256").update(key).digest("hex");
474
+ }
475
+ function getSetupStatus() {
476
+ return {
477
+ workspace: existsSync(join2(TELETON_ROOT, "workspace")),
478
+ config: existsSync(join2(TELETON_ROOT, "config.yaml")),
479
+ wallet: existsSync(join2(TELETON_ROOT, "wallet.json")),
480
+ telegram_session: existsSync(join2(TELETON_ROOT, "telegram_session.txt")),
481
+ embeddings_cached: (() => {
482
+ try {
483
+ return statSync(join2(TELETON_ROOT, "models", "Xenova", "all-MiniLM-L6-v2", "onnx", "model.onnx")).size > 1e6;
484
+ } catch {
485
+ return false;
486
+ }
487
+ })()
488
+ };
489
+ }
490
+ var SSE_PATHS = ["/v1/agent/events", "/v1/logs/stream"];
491
+ var ApiServer = class {
492
+ app;
493
+ server = null;
494
+ deps;
495
+ config;
496
+ tls = null;
497
+ apiKey = null;
498
+ keyHash;
499
+ constructor(deps, config) {
500
+ this.deps = deps;
501
+ this.config = config;
502
+ this.app = new Hono6();
503
+ if (config.key_hash) {
504
+ this.keyHash = config.key_hash;
505
+ } else {
506
+ this.apiKey = generateApiKey();
507
+ this.keyHash = hashApiKey2(this.apiKey);
508
+ }
509
+ }
510
+ /** Get current API key hash (for persisting in config) */
511
+ getKeyHash() {
512
+ return this.keyHash;
513
+ }
514
+ /** Update live deps (e.g., when agent starts/stops) */
515
+ updateDeps(partial) {
516
+ Object.assign(this.deps, partial);
517
+ }
518
+ setupMiddleware() {
519
+ this.app.use("*", async (c, next) => {
520
+ const incoming = c.env?.incoming;
521
+ if (incoming?.socket?.remoteAddress) {
522
+ c.env.ip = incoming.socket.remoteAddress;
523
+ }
524
+ await next();
525
+ });
526
+ this.app.use("*", requestId);
527
+ this.app.use(
528
+ "*",
529
+ bodyLimit({
530
+ maxSize: 2 * 1024 * 1024,
531
+ onError: (c) => {
532
+ return c.json(
533
+ createProblem(413, "Payload Too Large", "Request body exceeds 2MB limit"),
534
+ 413,
535
+ {
536
+ "Content-Type": "application/problem+json"
537
+ }
538
+ );
539
+ }
540
+ })
541
+ );
542
+ this.app.use("*", async (c, next) => {
543
+ if (SSE_PATHS.some((p) => c.req.path === p)) {
544
+ return next();
545
+ }
546
+ return timeout(3e4)(c, next);
547
+ });
548
+ this.app.use("*", async (c, next) => {
549
+ await next();
550
+ c.res.headers.set("X-Content-Type-Options", "nosniff");
551
+ c.res.headers.set("X-Frame-Options", "DENY");
552
+ c.res.headers.set("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
553
+ });
554
+ }
555
+ setupRoutes() {
556
+ this.app.get("/healthz", (c) => c.json({ status: "ok" }));
557
+ this.app.get("/readyz", (c) => {
558
+ const lifecycle = this.deps.lifecycle;
559
+ if (!lifecycle) {
560
+ return c.json({ status: "not_ready", reason: "lifecycle not initialized" }, 503);
561
+ }
562
+ const state = lifecycle.getState();
563
+ if (state === "running") {
564
+ return c.json({ status: "ready", state });
565
+ }
566
+ const setup = getSetupStatus();
567
+ return c.json({ status: "not_ready", state, setup }, 503);
568
+ });
569
+ const authMw = createAuthMiddleware({
570
+ keyHash: this.keyHash,
571
+ allowedIps: this.config.allowed_ips
572
+ });
573
+ this.app.use("/v1/*", authMw);
574
+ this.app.use("/v1/*", globalRateLimit);
575
+ this.app.use("/v1/*", mutatingRateLimit);
576
+ this.app.use("/v1/*", readRateLimit);
577
+ this.app.use("/v1/*", auditMiddleware);
578
+ this.app.get("/v1/openapi.json", (c) => {
579
+ return c.json({
580
+ openapi: "3.1.0",
581
+ info: {
582
+ title: "Teleton Management API",
583
+ version: "1.0.0",
584
+ description: "HTTPS management API for remote teleton agent administration"
585
+ },
586
+ servers: [{ url: `https://localhost:${this.config.port}` }]
587
+ });
588
+ });
589
+ const adaptedDeps = createDepsAdapter(this.deps);
590
+ this.app.route("/v1/status", createStatusRoutes(adaptedDeps));
591
+ this.app.route("/v1/tools", createToolsRoutes(adaptedDeps));
592
+ this.app.route("/v1/logs", createLogsRoutes(adaptedDeps));
593
+ this.app.route("/v1/memory", createMemoryRoutes(adaptedDeps));
594
+ this.app.route("/v1/soul", createSoulRoutes(adaptedDeps));
595
+ this.app.route("/v1/plugins", createPluginsRoutes(adaptedDeps));
596
+ this.app.route("/v1/mcp", createMcpRoutes(adaptedDeps));
597
+ this.app.route("/v1/workspace", createWorkspaceRoutes(adaptedDeps));
598
+ this.app.route("/v1/tasks", createTasksRoutes(adaptedDeps));
599
+ this.app.route("/v1/config", createConfigRoutes(adaptedDeps));
600
+ this.app.route("/v1/marketplace", createMarketplaceRoutes(adaptedDeps));
601
+ this.app.route("/v1/hooks", createHooksRoutes(adaptedDeps));
602
+ this.app.route("/v1/ton-proxy", createTonProxyRoutes(adaptedDeps));
603
+ this.app.route("/v1/setup", createSetupRoutes({ keyHash: this.keyHash }));
604
+ this.app.post("/v1/agent/start", async (c) => {
605
+ const lifecycle = this.deps.lifecycle;
606
+ if (!lifecycle) {
607
+ return c.json(
608
+ createProblem(503, "Service Unavailable", "Agent lifecycle not available"),
609
+ 503,
610
+ {
611
+ "Content-Type": "application/problem+json"
612
+ }
613
+ );
614
+ }
615
+ const state = lifecycle.getState();
616
+ if (state === "running") {
617
+ return c.json({ state: "running" }, 409);
618
+ }
619
+ if (state === "stopping") {
620
+ return c.json(
621
+ createProblem(409, "Conflict", "Agent is currently stopping, please wait"),
622
+ 409,
623
+ {
624
+ "Content-Type": "application/problem+json"
625
+ }
626
+ );
627
+ }
628
+ lifecycle.start().catch((err) => {
629
+ log3.error({ err }, "Agent start failed");
630
+ });
631
+ return c.json({ state: "starting" });
632
+ });
633
+ this.app.post("/v1/agent/stop", async (c) => {
634
+ const lifecycle = this.deps.lifecycle;
635
+ if (!lifecycle) {
636
+ return c.json(
637
+ createProblem(503, "Service Unavailable", "Agent lifecycle not available"),
638
+ 503,
639
+ {
640
+ "Content-Type": "application/problem+json"
641
+ }
642
+ );
643
+ }
644
+ const state = lifecycle.getState();
645
+ if (state === "stopped") {
646
+ return c.json({ state: "stopped" }, 409);
647
+ }
648
+ if (state === "starting") {
649
+ return c.json(
650
+ createProblem(409, "Conflict", "Agent is currently starting, please wait"),
651
+ 409,
652
+ {
653
+ "Content-Type": "application/problem+json"
654
+ }
655
+ );
656
+ }
657
+ lifecycle.stop().catch((err) => {
658
+ log3.error({ err }, "Agent stop failed");
659
+ });
660
+ return c.json({ state: "stopping" });
661
+ });
662
+ this.app.get("/v1/agent/status", (c) => {
663
+ const lifecycle = this.deps.lifecycle;
664
+ if (!lifecycle) {
665
+ return c.json(
666
+ createProblem(503, "Service Unavailable", "Agent lifecycle not available"),
667
+ 503,
668
+ {
669
+ "Content-Type": "application/problem+json"
670
+ }
671
+ );
672
+ }
673
+ return c.json({
674
+ state: lifecycle.getState(),
675
+ uptime: lifecycle.getUptime(),
676
+ error: lifecycle.getError() ?? null
677
+ });
678
+ });
679
+ this.app.get("/v1/agent/events", (c) => {
680
+ const lifecycle = this.deps.lifecycle;
681
+ if (!lifecycle) {
682
+ return c.json(
683
+ createProblem(503, "Service Unavailable", "Agent lifecycle not available"),
684
+ 503,
685
+ {
686
+ "Content-Type": "application/problem+json"
687
+ }
688
+ );
689
+ }
690
+ return streamSSE2(c, async (stream) => {
691
+ let aborted = false;
692
+ stream.onAbort(() => {
693
+ aborted = true;
694
+ });
695
+ const now = Date.now();
696
+ await stream.writeSSE({
697
+ event: "status",
698
+ id: String(now),
699
+ data: JSON.stringify({
700
+ state: lifecycle.getState(),
701
+ error: lifecycle.getError() ?? null,
702
+ timestamp: now
703
+ }),
704
+ retry: 3e3
705
+ });
706
+ const onStateChange = (event) => {
707
+ if (aborted) return;
708
+ void stream.writeSSE({
709
+ event: "status",
710
+ id: String(event.timestamp),
711
+ data: JSON.stringify({
712
+ state: event.state,
713
+ error: event.error ?? null,
714
+ timestamp: event.timestamp
715
+ })
716
+ });
717
+ };
718
+ lifecycle.on("stateChange", onStateChange);
719
+ while (!aborted) {
720
+ await stream.sleep(3e4);
721
+ if (aborted) break;
722
+ await stream.writeSSE({
723
+ event: "ping",
724
+ data: ""
725
+ });
726
+ }
727
+ lifecycle.off("stateChange", onStateChange);
728
+ });
729
+ });
730
+ this.app.route("/v1/agent", createAgentRoutes(this.deps.lifecycle));
731
+ this.app.route("/v1/system", createSystemRoutes());
732
+ this.app.route("/v1/auth", createAuthRoutes());
733
+ this.app.route("/v1/api-logs", createApiLogsRoutes());
734
+ this.app.route(
735
+ "/v1/api-memory",
736
+ createApiMemoryRoutes(() => this.deps.memory?.db ?? null)
737
+ );
738
+ this.app.onError((err, c) => {
739
+ log3.error({ err }, "Management API error");
740
+ if (err instanceof HTTPException3) {
741
+ if (err.res) return err.res;
742
+ return c.json(createProblem(err.status, err.message || "Error"), err.status, {
743
+ "Content-Type": "application/problem+json"
744
+ });
745
+ }
746
+ return c.json(
747
+ createProblem(500, "Internal Server Error", err.message || "An unexpected error occurred"),
748
+ 500,
749
+ { "Content-Type": "application/problem+json" }
750
+ );
751
+ });
752
+ }
753
+ async start() {
754
+ const tls = await ensureTlsCert(TELETON_ROOT);
755
+ this.tls = tls;
756
+ this.setupMiddleware();
757
+ this.setupRoutes();
758
+ return new Promise((resolve, reject) => {
759
+ try {
760
+ this.server = serve(
761
+ {
762
+ fetch: this.app.fetch,
763
+ port: this.config.port,
764
+ createServer: createHttpsServer,
765
+ serverOptions: {
766
+ cert: tls.cert,
767
+ key: tls.key
768
+ }
769
+ },
770
+ (info) => {
771
+ this.server.maxConnections = 20;
772
+ log3.info(`Management API server running on https://localhost:${info.port}`);
773
+ if (this.apiKey) {
774
+ log3.info(
775
+ `API key: ${KEY_PREFIX}${this.apiKey.slice(KEY_PREFIX.length, KEY_PREFIX.length + 4)}...`
776
+ );
777
+ }
778
+ log3.info(`TLS fingerprint: ${tls.fingerprint.slice(0, 16)}...`);
779
+ resolve();
780
+ }
781
+ );
782
+ this.server.on("error", (err) => {
783
+ log3.error({ err }, "Management API server error");
784
+ reject(err);
785
+ });
786
+ } catch (error) {
787
+ reject(error);
788
+ }
789
+ });
790
+ }
791
+ async stop() {
792
+ if (this.server) {
793
+ return new Promise((resolve) => {
794
+ this.server.closeAllConnections();
795
+ this.server.close(() => {
796
+ log3.info("Management API server stopped");
797
+ resolve();
798
+ });
799
+ });
800
+ }
801
+ }
802
+ getCredentials() {
803
+ if (!this.tls) return null;
804
+ return {
805
+ apiKey: this.apiKey ?? "",
806
+ fingerprint: this.tls.fingerprint,
807
+ port: this.config.port
808
+ };
809
+ }
810
+ };
811
+ export {
812
+ ApiServer
813
+ };