teleton 0.8.0 → 0.8.2

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 (44) hide show
  1. package/README.md +28 -11
  2. package/dist/{chunk-H36RFKRI.js → chunk-2IZU3REP.js} +572 -174
  3. package/dist/chunk-3UFPFWYP.js +12 -0
  4. package/dist/{chunk-NUGDTPE4.js → chunk-4L66JHQE.js} +2 -1
  5. package/dist/{chunk-TVRZJIZX.js → chunk-55SKE6YH.js} +4 -4
  6. package/dist/{setup-server-QXED3D2L.js → chunk-57URFK6M.js} +161 -210
  7. package/dist/chunk-5SEMA47R.js +75 -0
  8. package/dist/{chunk-JHYZYFZJ.js → chunk-7YKSXOQQ.js} +17 -2
  9. package/dist/{chunk-IJBWWQE4.js → chunk-C4NKJT2Z.js} +12 -0
  10. package/dist/{chunk-RQBAMUCV.js → chunk-GGXJLMOH.js} +1451 -743
  11. package/dist/{chunk-WIKM24GZ.js → chunk-H7MFXJZK.js} +7 -2
  12. package/dist/{chunk-U56QTM46.js → chunk-HEDJCLA6.js} +85 -44
  13. package/dist/{chunk-QVBSUYVX.js → chunk-J73TA3UM.js} +17 -9
  14. package/dist/{chunk-P36I6OIV.js → chunk-LC4TV3KL.js} +13 -2
  15. package/dist/{chunk-RCMD3U65.js → chunk-NQ6FZKCE.js} +13 -0
  16. package/dist/{chunk-SD4NLLYG.js → chunk-VYKW7FMV.js} +224 -93
  17. package/dist/chunk-W25Z7CM6.js +487 -0
  18. package/dist/{chunk-OJCLKU5Z.js → chunk-WFTC3JJW.js} +16 -0
  19. package/dist/{server-H3QA252W.js → chunk-XBSCYMKM.js} +369 -374
  20. package/dist/{chunk-PHSAHTK4.js → chunk-YOSUPUAJ.js} +75 -7
  21. package/dist/cli/index.js +67 -22
  22. package/dist/{client-LNZTDQSA.js → client-YOOHI776.js} +4 -4
  23. package/dist/{get-my-gifts-OMGKOEPM.js → get-my-gifts-Y7EN7RK4.js} +3 -3
  24. package/dist/index.js +15 -14
  25. package/dist/{memory-AS7WKGTW.js → memory-Q6EWGK2S.js} +7 -5
  26. package/dist/memory-hook-WUXJNVT5.js +18 -0
  27. package/dist/{migrate-POHWYEIW.js → migrate-WFU6COBN.js} +5 -5
  28. package/dist/server-GYZXKIKU.js +787 -0
  29. package/dist/server-YODFBZKG.js +392 -0
  30. package/dist/setup-server-IZBUOJRU.js +215 -0
  31. package/dist/{store-GAFULOOX.js → store-7M4XV6M5.js} +6 -6
  32. package/dist/{task-dependency-resolver-3FIKQ7Z6.js → task-dependency-resolver-L6UUMTHK.js} +3 -3
  33. package/dist/{task-executor-RUTFG6VG.js → task-executor-XBNJLUCS.js} +3 -3
  34. package/dist/{tasks-BEZ4QRI2.js → tasks-WQIKXDX5.js} +1 -1
  35. package/dist/{tool-adapter-IH5VGBOO.js → tool-adapter-IVX2XQJE.js} +1 -1
  36. package/dist/{tool-index-H3SHOJC3.js → tool-index-NYH57UWP.js} +9 -6
  37. package/dist/{transcript-IMNE6KU3.js → transcript-IM7G25OS.js} +2 -2
  38. package/dist/web/assets/index-BfYCdwLI.js +80 -0
  39. package/dist/web/assets/{index-BrVqauzj.css → index-DmlyQVhR.css} +1 -1
  40. package/dist/web/assets/{index.es-DkU1GvWU.js → index.es-DitvF-9H.js} +1 -1
  41. package/dist/web/index.html +2 -2
  42. package/package.json +14 -5
  43. package/dist/chunk-XBE4JB7C.js +0 -8
  44. package/dist/web/assets/index-DYeEkvJ6.js +0 -72
@@ -1,50 +1,51 @@
1
1
  import {
2
2
  getModelsForProvider
3
- } from "./chunk-OJCLKU5Z.js";
3
+ } from "./chunk-WFTC3JJW.js";
4
4
  import {
5
5
  CONFIGURABLE_KEYS,
6
+ TonProxyManager,
6
7
  WorkspaceSecurityError,
7
8
  adaptPlugin,
8
9
  clearPromptCache,
9
10
  deleteNestedValue,
10
11
  deletePluginSecret,
11
12
  ensurePluginDeps,
13
+ getBlocklistConfig,
12
14
  getNestedValue,
15
+ getPluginPriorities,
13
16
  getTokenUsage,
17
+ getTonProxyManager,
18
+ getTriggersConfig,
14
19
  listPluginSecretKeys,
15
20
  readRawConfig,
21
+ resetPluginPriority,
22
+ setBlocklistConfig,
16
23
  setNestedValue,
24
+ setPluginPriority,
25
+ setTonProxyManager,
26
+ setTriggersConfig,
17
27
  validateDirectory,
18
28
  validatePath,
19
29
  validateReadPath,
20
30
  validateWritePath,
21
31
  writePluginSecret,
22
32
  writeRawConfig
23
- } from "./chunk-RQBAMUCV.js";
33
+ } from "./chunk-GGXJLMOH.js";
24
34
  import {
25
35
  invalidateEndpointCache,
26
36
  invalidateTonClientCache,
27
37
  setToncenterApiKey
28
- } from "./chunk-JHYZYFZJ.js";
29
- import "./chunk-TVRZJIZX.js";
30
- import "./chunk-7TECSLJ4.js";
38
+ } from "./chunk-7YKSXOQQ.js";
31
39
  import {
32
40
  getErrorMessage
33
- } from "./chunk-XBE4JB7C.js";
34
- import "./chunk-QVBSUYVX.js";
41
+ } from "./chunk-3UFPFWYP.js";
35
42
  import {
36
43
  getProviderMetadata,
37
44
  validateApiKeyFormat
38
- } from "./chunk-PHSAHTK4.js";
39
- import "./chunk-P36I6OIV.js";
40
- import "./chunk-SD4NLLYG.js";
41
- import "./chunk-U56QTM46.js";
45
+ } from "./chunk-YOSUPUAJ.js";
42
46
  import {
43
47
  setTonapiKey
44
48
  } from "./chunk-VFA7QMCZ.js";
45
- import "./chunk-IJBWWQE4.js";
46
- import "./chunk-XQUHC3JZ.js";
47
- import "./chunk-R4YSJ4EY.js";
48
49
  import {
49
50
  WORKSPACE_PATHS,
50
51
  WORKSPACE_ROOT
@@ -53,41 +54,10 @@ import {
53
54
  addLogListener,
54
55
  clearLogListeners,
55
56
  createLogger
56
- } from "./chunk-RCMD3U65.js";
57
+ } from "./chunk-NQ6FZKCE.js";
57
58
  import {
58
59
  getTaskStore
59
- } from "./chunk-NUGDTPE4.js";
60
- import "./chunk-3RG5ZIWI.js";
61
-
62
- // src/webui/server.ts
63
- import { Hono as Hono12 } from "hono";
64
- import { serve } from "@hono/node-server";
65
- import { cors } from "hono/cors";
66
- import { streamSSE as streamSSE2 } from "hono/streaming";
67
- import { bodyLimit } from "hono/body-limit";
68
- import { setCookie, getCookie, deleteCookie } from "hono/cookie";
69
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
70
- import { join as join4, dirname, resolve as resolve2, relative as relative2 } from "path";
71
- import { fileURLToPath } from "url";
72
-
73
- // src/webui/middleware/auth.ts
74
- import { randomBytes, timingSafeEqual } from "crypto";
75
- var COOKIE_NAME = "teleton_session";
76
- var COOKIE_MAX_AGE = 7 * 24 * 60 * 60;
77
- function generateToken() {
78
- return randomBytes(32).toString("base64url");
79
- }
80
- function maskToken(token) {
81
- if (token.length < 12) return "****";
82
- return `${token.slice(0, 4)}...${token.slice(-4)}`;
83
- }
84
- function safeCompare(a, b) {
85
- if (!a || !b) return false;
86
- const bufA = Buffer.from(a);
87
- const bufB = Buffer.from(b);
88
- if (bufA.length !== bufB.length) return false;
89
- return timingSafeEqual(bufA, bufB);
90
- }
60
+ } from "./chunk-4L66JHQE.js";
91
61
 
92
62
  // src/webui/log-interceptor.ts
93
63
  var LogInterceptor = class {
@@ -413,15 +383,14 @@ function createLogsRoutes(_deps) {
413
383
  const app = new Hono3();
414
384
  app.get("/stream", (c) => {
415
385
  return streamSSE(c, async (stream) => {
416
- let cleanup;
417
386
  let aborted = false;
418
387
  stream.onAbort(() => {
419
388
  aborted = true;
420
389
  if (cleanup) cleanup();
421
390
  });
422
- cleanup = logInterceptor.addListener((entry) => {
391
+ const cleanup = logInterceptor.addListener((entry) => {
423
392
  if (!aborted) {
424
- stream.writeSSE({
393
+ void stream.writeSSE({
425
394
  data: JSON.stringify(entry),
426
395
  event: "log"
427
396
  });
@@ -435,9 +404,9 @@ function createLogsRoutes(_deps) {
435
404
  }),
436
405
  event: "log"
437
406
  });
438
- await new Promise((resolve3) => {
439
- if (aborted) return resolve3();
440
- stream.onAbort(() => resolve3());
407
+ await new Promise((resolve2) => {
408
+ if (aborted) return resolve2();
409
+ stream.onAbort(() => resolve2());
441
410
  });
442
411
  if (cleanup) cleanup();
443
412
  });
@@ -717,6 +686,52 @@ function createPluginsRoutes(deps) {
717
686
  const data = deps.marketplace ? deps.marketplace.modules.filter((m) => deps.toolRegistry.isPluginModule(m.name)).map((m) => ({ name: m.name, version: m.version ?? "0.0.0" })) : deps.plugins;
718
687
  return c.json({ success: true, data });
719
688
  });
689
+ app.get("/priorities", (c) => {
690
+ try {
691
+ const priorities = getPluginPriorities(deps.memory.db);
692
+ const data = {};
693
+ for (const [name, priority] of priorities) {
694
+ data[name] = priority;
695
+ }
696
+ return c.json({ success: true, data });
697
+ } catch (err) {
698
+ return c.json({ success: false, error: getErrorMessage(err) }, 500);
699
+ }
700
+ });
701
+ app.post("/priorities", async (c) => {
702
+ try {
703
+ const body = await c.req.json();
704
+ const { pluginName, priority } = body;
705
+ if (!pluginName || typeof pluginName !== "string") {
706
+ return c.json({ success: false, error: "pluginName is required" }, 400);
707
+ }
708
+ if (typeof priority !== "number" || !Number.isInteger(priority)) {
709
+ return c.json({ success: false, error: "priority must be an integer" }, 400);
710
+ }
711
+ if (priority < -1e3 || priority > 1e3) {
712
+ return c.json(
713
+ { success: false, error: "priority must be between -1000 and 1000" },
714
+ 400
715
+ );
716
+ }
717
+ setPluginPriority(deps.memory.db, pluginName, priority);
718
+ return c.json({
719
+ success: true,
720
+ data: { pluginName, priority }
721
+ });
722
+ } catch (err) {
723
+ return c.json({ success: false, error: getErrorMessage(err) }, 500);
724
+ }
725
+ });
726
+ app.delete("/priorities/:name", (c) => {
727
+ try {
728
+ const name = c.req.param("name");
729
+ resetPluginPriority(deps.memory.db, name);
730
+ return c.json({ success: true, data: null });
731
+ } catch (err) {
732
+ return c.json({ success: false, error: getErrorMessage(err) }, 500);
733
+ }
734
+ });
720
735
  return app;
721
736
  }
722
737
 
@@ -782,7 +797,7 @@ function createMcpRoutes(deps) {
782
797
  entry.url = body.url;
783
798
  } else {
784
799
  entry.command = "npx";
785
- entry.args = ["-y", body.package, ...body.args || []];
800
+ entry.args = ["-y", body.package ?? "", ...body.args || []];
786
801
  }
787
802
  if (body.scope && body.scope !== "always") entry.scope = body.scope;
788
803
  if (body.env && Object.keys(body.env).length > 0) entry.env = body.env;
@@ -833,7 +848,7 @@ function createMcpRoutes(deps) {
833
848
  return app;
834
849
  }
835
850
  function deriveServerName(pkg) {
836
- const unscoped = pkg.includes("/") ? pkg.split("/").pop() : pkg;
851
+ const unscoped = pkg.includes("/") ? pkg.split("/").pop() ?? pkg : pkg;
837
852
  return unscoped.replace(/^server-/, "").replace(/^mcp-server-/, "").replace(/^mcp-/, "");
838
853
  }
839
854
 
@@ -850,6 +865,8 @@ import {
850
865
  existsSync
851
866
  } from "fs";
852
867
  import { join as join2, relative } from "path";
868
+ var MAX_SCAN_DEPTH = 10;
869
+ var MAX_SCAN_ENTRIES = 5e3;
853
870
  function errorResponse(c, error, status = 500) {
854
871
  const message = getErrorMessage(error);
855
872
  const code = error instanceof WorkspaceSecurityError ? 403 : status;
@@ -866,35 +883,46 @@ var IMAGE_MIME_TYPES = {
866
883
  ".bmp": "image/bmp",
867
884
  ".ico": "image/x-icon"
868
885
  };
869
- function getWorkspaceStats(dir) {
870
- let files = 0;
871
- let size = 0;
872
- if (!existsSync(dir)) return { files, size };
886
+ function getWorkspaceStats(dir, depth = 0, state = { files: 0, size: 0, truncated: false }) {
887
+ if (!existsSync(dir)) return state;
888
+ if (depth >= MAX_SCAN_DEPTH || state.files >= MAX_SCAN_ENTRIES) {
889
+ state.truncated = true;
890
+ return state;
891
+ }
873
892
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
893
+ if (state.files >= MAX_SCAN_ENTRIES) {
894
+ state.truncated = true;
895
+ return state;
896
+ }
874
897
  const fullPath = join2(dir, entry.name);
875
898
  if (entry.isDirectory()) {
876
- const sub = getWorkspaceStats(fullPath);
877
- files += sub.files;
878
- size += sub.size;
899
+ getWorkspaceStats(fullPath, depth + 1, state);
879
900
  } else if (entry.isFile()) {
880
- files++;
901
+ state.files++;
881
902
  try {
882
- size += statSync(fullPath).size;
903
+ state.size += statSync(fullPath).size;
883
904
  } catch {
884
905
  }
885
906
  }
886
907
  }
887
- return { files, size };
908
+ return state;
888
909
  }
889
- function listDir(absPath, recursive) {
890
- if (!existsSync(absPath)) return [];
891
- const entries = [];
910
+ function listDir(absPath, recursive, depth = 0, state = { entries: [], truncated: false }) {
911
+ if (!existsSync(absPath)) return state;
912
+ if (depth >= MAX_SCAN_DEPTH || state.entries.length >= MAX_SCAN_ENTRIES) {
913
+ state.truncated = true;
914
+ return state;
915
+ }
892
916
  for (const entry of readdirSync(absPath, { withFileTypes: true })) {
917
+ if (state.entries.length >= MAX_SCAN_ENTRIES) {
918
+ state.truncated = true;
919
+ return state;
920
+ }
893
921
  const fullPath = join2(absPath, entry.name);
894
922
  const relPath = relative(WORKSPACE_ROOT, fullPath);
895
923
  try {
896
924
  const stats = statSync(fullPath);
897
- entries.push({
925
+ state.entries.push({
898
926
  name: entry.name,
899
927
  path: relPath,
900
928
  isDirectory: entry.isDirectory(),
@@ -902,12 +930,12 @@ function listDir(absPath, recursive) {
902
930
  mtime: stats.mtime.toISOString()
903
931
  });
904
932
  if (recursive && entry.isDirectory()) {
905
- entries.push(...listDir(fullPath, true));
933
+ listDir(fullPath, true, depth + 1, state);
906
934
  }
907
935
  } catch {
908
936
  }
909
937
  }
910
- return entries;
938
+ return state;
911
939
  }
912
940
  function createWorkspaceRoutes(_deps) {
913
941
  const app = new Hono8();
@@ -927,12 +955,18 @@ function createWorkspaceRoutes(_deps) {
927
955
  const response2 = { success: true, data: [] };
928
956
  return c.json(response2);
929
957
  }
930
- const entries = listDir(validated.absolutePath, recursive);
931
- entries.sort((a, b) => {
958
+ const result = listDir(validated.absolutePath, recursive);
959
+ result.entries.sort((a, b) => {
932
960
  if (a.isDirectory !== b.isDirectory) return a.isDirectory ? -1 : 1;
933
961
  return a.name.localeCompare(b.name);
934
962
  });
935
- const response = { success: true, data: entries };
963
+ const response = {
964
+ success: true,
965
+ data: {
966
+ entries: result.entries,
967
+ ...result.truncated && { truncated: true }
968
+ }
969
+ };
936
970
  return c.json(response);
937
971
  } catch (error) {
938
972
  return errorResponse(c, error);
@@ -1101,7 +1135,8 @@ function createWorkspaceRoutes(_deps) {
1101
1135
  data: {
1102
1136
  root: WORKSPACE_ROOT,
1103
1137
  totalFiles: stats.files,
1104
- totalSize: stats.size
1138
+ totalSize: stats.size,
1139
+ ...stats.truncated && { truncated: true }
1105
1140
  }
1106
1141
  };
1107
1142
  return c.json(response);
@@ -1658,7 +1693,7 @@ var MarketplaceService = class {
1658
1693
  const registry = await this.getRegistry();
1659
1694
  const entry = registry.find((e) => e.id === pluginId);
1660
1695
  if (!entry) throw new Error(`Plugin "${pluginId}" not found in registry`);
1661
- const manifest = await this.fetchRemoteManifest(entry);
1696
+ const _manifest = await this.fetchRemoteManifest(entry);
1662
1697
  mkdirSync2(pluginDir, { recursive: true });
1663
1698
  await this.downloadDir(entry.path, pluginDir);
1664
1699
  await ensurePluginDeps(pluginDir, pluginId);
@@ -1834,7 +1869,7 @@ function createMarketplaceRoutes(deps) {
1834
1869
  const result = await svc.installPlugin(body.id);
1835
1870
  deps.plugins.length = 0;
1836
1871
  deps.plugins.push(
1837
- ...deps.marketplace.modules.filter((m) => deps.toolRegistry.isPluginModule(m.name)).map((m) => ({ name: m.name, version: m.version ?? "0.0.0" }))
1872
+ ...(deps.marketplace?.modules ?? []).filter((m) => deps.toolRegistry.isPluginModule(m.name)).map((m) => ({ name: m.name, version: m.version ?? "0.0.0" }))
1838
1873
  );
1839
1874
  return c.json({ success: true, data: result });
1840
1875
  } catch (err) {
@@ -1858,7 +1893,7 @@ function createMarketplaceRoutes(deps) {
1858
1893
  const result = await svc.uninstallPlugin(body.id);
1859
1894
  deps.plugins.length = 0;
1860
1895
  deps.plugins.push(
1861
- ...deps.marketplace.modules.filter((m) => deps.toolRegistry.isPluginModule(m.name)).map((m) => ({ name: m.name, version: m.version ?? "0.0.0" }))
1896
+ ...(deps.marketplace?.modules ?? []).filter((m) => deps.toolRegistry.isPluginModule(m.name)).map((m) => ({ name: m.name, version: m.version ?? "0.0.0" }))
1862
1897
  );
1863
1898
  return c.json({ success: true, data: result });
1864
1899
  } catch (err) {
@@ -1882,7 +1917,7 @@ function createMarketplaceRoutes(deps) {
1882
1917
  const result = await svc.updatePlugin(body.id);
1883
1918
  deps.plugins.length = 0;
1884
1919
  deps.plugins.push(
1885
- ...deps.marketplace.modules.filter((m) => deps.toolRegistry.isPluginModule(m.name)).map((m) => ({ name: m.name, version: m.version ?? "0.0.0" }))
1920
+ ...(deps.marketplace?.modules ?? []).filter((m) => deps.toolRegistry.isPluginModule(m.name)).map((m) => ({ name: m.name, version: m.version ?? "0.0.0" }))
1886
1921
  );
1887
1922
  return c.json({ success: true, data: result });
1888
1923
  } catch (err) {
@@ -1972,322 +2007,282 @@ function createMarketplaceRoutes(deps) {
1972
2007
  return app;
1973
2008
  }
1974
2009
 
1975
- // src/webui/server.ts
1976
- var log2 = createLogger("WebUI");
1977
- function findWebDist() {
1978
- const candidates = [
1979
- resolve2("dist/web"),
1980
- // npm start / teleton start (from project root)
1981
- resolve2("web")
1982
- // fallback
1983
- ];
1984
- const __dirname = dirname(fileURLToPath(import.meta.url));
1985
- candidates.push(
1986
- resolve2(__dirname, "web"),
1987
- // dist/web when __dirname = dist/
1988
- resolve2(__dirname, "../dist/web")
1989
- // when running with tsx from src/
1990
- );
1991
- for (const candidate of candidates) {
1992
- if (existsSync3(join4(candidate, "index.html"))) {
1993
- return candidate;
2010
+ // src/webui/routes/hooks.ts
2011
+ import { Hono as Hono12 } from "hono";
2012
+ import { randomUUID } from "crypto";
2013
+ function createHooksRoutes(deps) {
2014
+ const app = new Hono12();
2015
+ app.get("/blocklist", (c) => {
2016
+ try {
2017
+ const data = getBlocklistConfig(deps.memory.db);
2018
+ return c.json({ success: true, data });
2019
+ } catch (err) {
2020
+ return c.json({ success: false, error: getErrorMessage(err) }, 500);
1994
2021
  }
1995
- }
1996
- return null;
1997
- }
1998
- var WebUIServer = class {
1999
- app;
2000
- server = null;
2001
- deps;
2002
- authToken;
2003
- constructor(deps) {
2004
- this.deps = deps;
2005
- this.app = new Hono12();
2006
- this.authToken = deps.config.auth_token || generateToken();
2007
- this.setupMiddleware();
2008
- this.setupRoutes();
2009
- }
2010
- /** Set an HttpOnly session cookie */
2011
- setSessionCookie(c) {
2012
- setCookie(c, COOKIE_NAME, this.authToken, {
2013
- path: "/",
2014
- httpOnly: true,
2015
- sameSite: "Strict",
2016
- secure: false,
2017
- // localhost is HTTP
2018
- maxAge: COOKIE_MAX_AGE
2019
- });
2020
- }
2021
- setupMiddleware() {
2022
- this.app.use(
2023
- "*",
2024
- cors({
2025
- origin: this.deps.config.cors_origins,
2026
- credentials: true,
2027
- allowMethods: ["GET", "HEAD", "PUT", "POST", "DELETE", "PATCH"],
2028
- allowHeaders: ["Content-Type", "Authorization"],
2029
- maxAge: 3600
2030
- })
2031
- );
2032
- if (this.deps.config.log_requests) {
2033
- this.app.use("*", async (c, next) => {
2034
- const start = Date.now();
2035
- await next();
2036
- const duration = Date.now() - start;
2037
- log2.info(`${c.req.method} ${c.req.path} \u2192 ${c.res.status} (${duration}ms)`);
2038
- });
2022
+ });
2023
+ app.put("/blocklist", async (c) => {
2024
+ try {
2025
+ const body = await c.req.json();
2026
+ if (typeof body.enabled !== "boolean") {
2027
+ return c.json({ success: false, error: "enabled must be a boolean" }, 400);
2028
+ }
2029
+ if (!Array.isArray(body.keywords)) {
2030
+ return c.json({ success: false, error: "keywords must be an array" }, 400);
2031
+ }
2032
+ if (body.keywords.length > 200) {
2033
+ return c.json({ success: false, error: "Maximum 200 keywords" }, 400);
2034
+ }
2035
+ const keywords = body.keywords.map((k) => typeof k === "string" ? k.trim() : "").filter((k) => k.length >= 2);
2036
+ const message = typeof body.message === "string" ? body.message.slice(0, 500) : "";
2037
+ const config = {
2038
+ enabled: body.enabled,
2039
+ keywords,
2040
+ message
2041
+ };
2042
+ setBlocklistConfig(deps.memory.db, config);
2043
+ deps.userHookEvaluator?.reload();
2044
+ return c.json({ success: true, data: config });
2045
+ } catch (err) {
2046
+ return c.json({ success: false, error: getErrorMessage(err) }, 500);
2039
2047
  }
2040
- this.app.use(
2041
- "*",
2042
- bodyLimit({
2043
- maxSize: 2 * 1024 * 1024,
2044
- // 2MB
2045
- onError: (c) => c.json({ success: false, error: "Request body too large (max 2MB)" }, 413)
2046
- })
2047
- );
2048
- this.app.use("*", async (c, next) => {
2049
- await next();
2050
- c.res.headers.set("X-Content-Type-Options", "nosniff");
2051
- c.res.headers.set("X-Frame-Options", "DENY");
2052
- c.res.headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
2053
- });
2054
- this.app.use("/api/*", async (c, next) => {
2055
- const cookieToken = getCookie(c, COOKIE_NAME);
2056
- if (cookieToken && safeCompare(cookieToken, this.authToken)) {
2057
- return next();
2058
- }
2059
- const authHeader = c.req.header("Authorization");
2060
- if (authHeader) {
2061
- const match = authHeader.match(/^Bearer\s+(.+)$/i);
2062
- if (match && safeCompare(match[1], this.authToken)) {
2063
- return next();
2064
- }
2048
+ });
2049
+ app.get("/triggers", (c) => {
2050
+ try {
2051
+ const data = getTriggersConfig(deps.memory.db);
2052
+ return c.json({ success: true, data });
2053
+ } catch (err) {
2054
+ return c.json({ success: false, error: getErrorMessage(err) }, 500);
2055
+ }
2056
+ });
2057
+ app.post("/triggers", async (c) => {
2058
+ try {
2059
+ const body = await c.req.json();
2060
+ const keyword = typeof body.keyword === "string" ? body.keyword.trim() : "";
2061
+ const context = typeof body.context === "string" ? body.context.trim() : "";
2062
+ if (keyword.length < 2 || keyword.length > 100) {
2063
+ return c.json(
2064
+ { success: false, error: "keyword must be 2-100 characters" },
2065
+ 400
2066
+ );
2065
2067
  }
2066
- const queryToken = c.req.query("token");
2067
- if (queryToken && safeCompare(queryToken, this.authToken)) {
2068
- return next();
2068
+ if (context.length < 1 || context.length > 2e3) {
2069
+ return c.json(
2070
+ { success: false, error: "context must be 1-2000 characters" },
2071
+ 400
2072
+ );
2069
2073
  }
2070
- return c.json({ success: false, error: "Unauthorized" }, 401);
2071
- });
2072
- }
2073
- setupRoutes() {
2074
- this.app.get("/health", (c) => c.json({ status: "ok" }));
2075
- this.app.get("/auth/exchange", (c) => {
2076
- const token = c.req.query("token");
2077
- if (!token || !safeCompare(token, this.authToken)) {
2078
- return c.json({ success: false, error: "Invalid token" }, 401);
2079
- }
2080
- this.setSessionCookie(c);
2081
- return c.redirect("/");
2082
- });
2083
- this.app.post("/auth/login", async (c) => {
2084
- try {
2085
- const body = await c.req.json();
2086
- if (!body.token || !safeCompare(body.token, this.authToken)) {
2087
- return c.json({ success: false, error: "Invalid token" }, 401);
2074
+ const triggers = getTriggersConfig(deps.memory.db);
2075
+ if (triggers.length >= 50) {
2076
+ return c.json({ success: false, error: "Maximum 50 triggers" }, 400);
2077
+ }
2078
+ const entry = {
2079
+ id: randomUUID(),
2080
+ keyword,
2081
+ context,
2082
+ enabled: body.enabled !== false
2083
+ };
2084
+ triggers.push(entry);
2085
+ setTriggersConfig(deps.memory.db, triggers);
2086
+ deps.userHookEvaluator?.reload();
2087
+ return c.json({ success: true, data: entry });
2088
+ } catch (err) {
2089
+ return c.json({ success: false, error: getErrorMessage(err) }, 500);
2090
+ }
2091
+ });
2092
+ app.put("/triggers/:id", async (c) => {
2093
+ try {
2094
+ const id = c.req.param("id");
2095
+ const body = await c.req.json();
2096
+ const triggers = getTriggersConfig(deps.memory.db);
2097
+ const idx = triggers.findIndex((t) => t.id === id);
2098
+ if (idx === -1) {
2099
+ return c.json({ success: false, error: "Trigger not found" }, 404);
2100
+ }
2101
+ if (typeof body.keyword === "string") {
2102
+ const kw = body.keyword.trim();
2103
+ if (kw.length < 2 || kw.length > 100) {
2104
+ return c.json(
2105
+ { success: false, error: "keyword must be 2-100 characters" },
2106
+ 400
2107
+ );
2088
2108
  }
2089
- this.setSessionCookie(c);
2090
- return c.json({ success: true });
2091
- } catch {
2092
- return c.json({ success: false, error: "Invalid request body" }, 400);
2109
+ triggers[idx].keyword = kw;
2093
2110
  }
2094
- });
2095
- this.app.post("/auth/logout", (c) => {
2096
- deleteCookie(c, COOKIE_NAME, { path: "/" });
2097
- return c.json({ success: true });
2098
- });
2099
- this.app.get("/auth/check", (c) => {
2100
- const cookieToken = getCookie(c, COOKIE_NAME);
2101
- const authenticated = !!(cookieToken && safeCompare(cookieToken, this.authToken));
2102
- return c.json({ success: true, data: { authenticated } });
2103
- });
2104
- this.app.route("/api/status", createStatusRoutes(this.deps));
2105
- this.app.route("/api/tools", createToolsRoutes(this.deps));
2106
- this.app.route("/api/logs", createLogsRoutes(this.deps));
2107
- this.app.route("/api/memory", createMemoryRoutes(this.deps));
2108
- this.app.route("/api/soul", createSoulRoutes(this.deps));
2109
- this.app.route("/api/plugins", createPluginsRoutes(this.deps));
2110
- this.app.route("/api/mcp", createMcpRoutes(this.deps));
2111
- this.app.route("/api/workspace", createWorkspaceRoutes(this.deps));
2112
- this.app.route("/api/tasks", createTasksRoutes(this.deps));
2113
- this.app.route("/api/config", createConfigRoutes(this.deps));
2114
- this.app.route("/api/marketplace", createMarketplaceRoutes(this.deps));
2115
- this.app.post("/api/agent/start", async (c) => {
2116
- const lifecycle = this.deps.lifecycle;
2117
- if (!lifecycle) {
2118
- return c.json({ error: "Agent lifecycle not available" }, 503);
2119
- }
2120
- const state = lifecycle.getState();
2121
- if (state === "running") {
2122
- return c.json({ state: "running" }, 409);
2123
- }
2124
- if (state === "stopping") {
2125
- return c.json({ error: "Agent is currently stopping, please wait" }, 409);
2126
- }
2127
- lifecycle.start().catch((err) => {
2128
- log2.error({ err }, "Agent start failed");
2129
- });
2130
- return c.json({ state: "starting" });
2131
- });
2132
- this.app.post("/api/agent/stop", async (c) => {
2133
- const lifecycle = this.deps.lifecycle;
2134
- if (!lifecycle) {
2135
- return c.json({ error: "Agent lifecycle not available" }, 503);
2111
+ if (typeof body.context === "string") {
2112
+ const ctx = body.context.trim();
2113
+ if (ctx.length < 1 || ctx.length > 2e3) {
2114
+ return c.json(
2115
+ { success: false, error: "context must be 1-2000 characters" },
2116
+ 400
2117
+ );
2118
+ }
2119
+ triggers[idx].context = ctx;
2136
2120
  }
2137
- const state = lifecycle.getState();
2138
- if (state === "stopped") {
2139
- return c.json({ state: "stopped" }, 409);
2121
+ if (typeof body.enabled === "boolean") {
2122
+ triggers[idx].enabled = body.enabled;
2140
2123
  }
2141
- if (state === "starting") {
2142
- return c.json({ error: "Agent is currently starting, please wait" }, 409);
2124
+ setTriggersConfig(deps.memory.db, triggers);
2125
+ deps.userHookEvaluator?.reload();
2126
+ return c.json({ success: true, data: triggers[idx] });
2127
+ } catch (err) {
2128
+ return c.json({ success: false, error: getErrorMessage(err) }, 500);
2129
+ }
2130
+ });
2131
+ app.delete("/triggers/:id", (c) => {
2132
+ try {
2133
+ const id = c.req.param("id");
2134
+ const triggers = getTriggersConfig(deps.memory.db);
2135
+ const filtered = triggers.filter((t) => t.id !== id);
2136
+ setTriggersConfig(deps.memory.db, filtered);
2137
+ deps.userHookEvaluator?.reload();
2138
+ return c.json({ success: true, data: null });
2139
+ } catch (err) {
2140
+ return c.json({ success: false, error: getErrorMessage(err) }, 500);
2141
+ }
2142
+ });
2143
+ app.patch("/triggers/:id/toggle", async (c) => {
2144
+ try {
2145
+ const id = c.req.param("id");
2146
+ const body = await c.req.json();
2147
+ if (typeof body.enabled !== "boolean") {
2148
+ return c.json({ success: false, error: "enabled must be a boolean" }, 400);
2143
2149
  }
2144
- lifecycle.stop().catch((err) => {
2145
- log2.error({ err }, "Agent stop failed");
2146
- });
2147
- return c.json({ state: "stopping" });
2148
- });
2149
- this.app.get("/api/agent/status", (c) => {
2150
- const lifecycle = this.deps.lifecycle;
2151
- if (!lifecycle) {
2152
- return c.json({ error: "Agent lifecycle not available" }, 503);
2150
+ const triggers = getTriggersConfig(deps.memory.db);
2151
+ const trigger = triggers.find((t) => t.id === id);
2152
+ if (!trigger) {
2153
+ return c.json({ success: false, error: "Trigger not found" }, 404);
2153
2154
  }
2155
+ trigger.enabled = body.enabled;
2156
+ setTriggersConfig(deps.memory.db, triggers);
2157
+ deps.userHookEvaluator?.reload();
2154
2158
  return c.json({
2155
- state: lifecycle.getState(),
2156
- uptime: lifecycle.getUptime(),
2157
- error: lifecycle.getError() ?? null
2159
+ success: true,
2160
+ data: { id, enabled: body.enabled }
2158
2161
  });
2159
- });
2160
- this.app.get("/api/agent/events", (c) => {
2161
- const lifecycle = this.deps.lifecycle;
2162
- if (!lifecycle) {
2163
- return c.json({ error: "Agent lifecycle not available" }, 503);
2164
- }
2165
- return streamSSE2(c, async (stream) => {
2166
- let aborted = false;
2167
- stream.onAbort(() => {
2168
- aborted = true;
2169
- });
2170
- const now = Date.now();
2171
- await stream.writeSSE({
2172
- event: "status",
2173
- id: String(now),
2174
- data: JSON.stringify({
2175
- state: lifecycle.getState(),
2176
- error: lifecycle.getError() ?? null,
2177
- timestamp: now
2178
- }),
2179
- retry: 3e3
2180
- });
2181
- const onStateChange = (event) => {
2182
- if (aborted) return;
2183
- stream.writeSSE({
2184
- event: "status",
2185
- id: String(event.timestamp),
2186
- data: JSON.stringify({
2187
- state: event.state,
2188
- error: event.error ?? null,
2189
- timestamp: event.timestamp
2190
- })
2191
- });
2192
- };
2193
- lifecycle.on("stateChange", onStateChange);
2194
- while (!aborted) {
2195
- await stream.sleep(3e4);
2196
- if (aborted) break;
2197
- await stream.writeSSE({
2198
- event: "ping",
2199
- data: ""
2200
- });
2201
- }
2202
- lifecycle.off("stateChange", onStateChange);
2162
+ } catch (err) {
2163
+ return c.json({ success: false, error: getErrorMessage(err) }, 500);
2164
+ }
2165
+ });
2166
+ return app;
2167
+ }
2168
+
2169
+ // src/webui/routes/ton-proxy.ts
2170
+ import { Hono as Hono13 } from "hono";
2171
+ var log2 = createLogger("TonProxyRoute");
2172
+ function createTonProxyRoutes(deps) {
2173
+ const app = new Hono13();
2174
+ app.get("/", (c) => {
2175
+ const mgr = getTonProxyManager();
2176
+ if (!mgr) {
2177
+ return c.json({
2178
+ success: true,
2179
+ data: { running: false, installed: false, port: 8080, enabled: false }
2203
2180
  });
2181
+ }
2182
+ return c.json({
2183
+ success: true,
2184
+ data: { ...mgr.getStatus(), enabled: true }
2204
2185
  });
2205
- const webDist = findWebDist();
2206
- if (webDist) {
2207
- const indexHtml = readFileSync3(join4(webDist, "index.html"), "utf-8");
2208
- const mimeTypes = {
2209
- js: "application/javascript",
2210
- css: "text/css",
2211
- svg: "image/svg+xml",
2212
- png: "image/png",
2213
- jpg: "image/jpeg",
2214
- jpeg: "image/jpeg",
2215
- ico: "image/x-icon",
2216
- json: "application/json",
2217
- woff2: "font/woff2",
2218
- woff: "font/woff"
2219
- };
2220
- this.app.get("*", (c) => {
2221
- const filePath = resolve2(join4(webDist, c.req.path));
2222
- const rel = relative2(webDist, filePath);
2223
- if (rel.startsWith("..") || resolve2(filePath) !== filePath) {
2224
- return c.html(indexHtml);
2225
- }
2226
- try {
2227
- const content = readFileSync3(filePath);
2228
- const ext = filePath.split(".").pop() || "";
2229
- if (mimeTypes[ext]) {
2230
- const immutable = c.req.path.startsWith("/assets/");
2231
- return c.body(content, 200, {
2232
- "Content-Type": mimeTypes[ext],
2233
- "Cache-Control": immutable ? "public, max-age=31536000, immutable" : "public, max-age=3600"
2234
- });
2235
- }
2236
- } catch {
2237
- }
2238
- return c.html(indexHtml);
2186
+ });
2187
+ app.post("/start", async (c) => {
2188
+ try {
2189
+ const runtimeConfig = deps.agent.getConfig();
2190
+ const port = runtimeConfig.ton_proxy?.port ?? 8080;
2191
+ const binaryPath = runtimeConfig.ton_proxy?.binary_path;
2192
+ const existing = getTonProxyManager();
2193
+ if (existing?.isRunning()) await existing.stop();
2194
+ const mgr = new TonProxyManager({ enabled: true, port, binary_path: binaryPath });
2195
+ setTonProxyManager(mgr);
2196
+ await mgr.start();
2197
+ const raw = readRawConfig(deps.configPath);
2198
+ setNestedValue(raw, "ton_proxy.enabled", true);
2199
+ writeRawConfig(raw, deps.configPath);
2200
+ setNestedValue(runtimeConfig, "ton_proxy.enabled", true);
2201
+ log2.info(`TON Proxy started on port ${port} (WebUI)`);
2202
+ return c.json({
2203
+ success: true,
2204
+ data: { ...mgr.getStatus(), enabled: true }
2239
2205
  });
2206
+ } catch (err) {
2207
+ log2.error({ err }, "Failed to start TON Proxy");
2208
+ return c.json(
2209
+ { success: false, error: err instanceof Error ? err.message : String(err) },
2210
+ 500
2211
+ );
2240
2212
  }
2241
- this.app.onError((err, c) => {
2242
- log2.error({ err }, "WebUI error");
2213
+ });
2214
+ app.post("/stop", async (c) => {
2215
+ try {
2216
+ const mgr = getTonProxyManager();
2217
+ if (mgr) {
2218
+ await mgr.stop();
2219
+ setTonProxyManager(null);
2220
+ }
2221
+ const runtimeConfig = deps.agent.getConfig();
2222
+ const raw = readRawConfig(deps.configPath);
2223
+ setNestedValue(raw, "ton_proxy.enabled", false);
2224
+ writeRawConfig(raw, deps.configPath);
2225
+ setNestedValue(runtimeConfig, "ton_proxy.enabled", false);
2226
+ log2.info("TON Proxy stopped (WebUI)");
2227
+ return c.json({
2228
+ success: true,
2229
+ data: { running: false, installed: true, port: 8080, enabled: false }
2230
+ });
2231
+ } catch (err) {
2232
+ log2.error({ err }, "Failed to stop TON Proxy");
2243
2233
  return c.json(
2244
- {
2245
- success: false,
2246
- error: err.message || "Internal server error"
2247
- },
2234
+ { success: false, error: err instanceof Error ? err.message : String(err) },
2248
2235
  500
2249
2236
  );
2250
- });
2251
- }
2252
- async start() {
2253
- return new Promise((resolve3, reject) => {
2254
- try {
2255
- logInterceptor.install();
2256
- this.server = serve(
2257
- {
2258
- fetch: this.app.fetch,
2259
- hostname: this.deps.config.host,
2260
- port: this.deps.config.port
2261
- },
2262
- (info) => {
2263
- const url = `http://${info.address}:${info.port}`;
2264
- log2.info(`WebUI server running`);
2265
- log2.info(`URL: ${url}/auth/exchange?token=${this.authToken}`);
2266
- log2.info(`Token: ${maskToken(this.authToken)} (use Bearer header for API access)`);
2267
- resolve3();
2268
- }
2269
- );
2270
- } catch (error) {
2271
- logInterceptor.uninstall();
2272
- reject(error);
2237
+ }
2238
+ });
2239
+ app.post("/uninstall", async (c) => {
2240
+ try {
2241
+ const mgr = getTonProxyManager();
2242
+ if (mgr) {
2243
+ await mgr.uninstall();
2244
+ setTonProxyManager(null);
2245
+ } else {
2246
+ const runtimeConfig2 = deps.agent.getConfig();
2247
+ const port = runtimeConfig2.ton_proxy?.port ?? 8080;
2248
+ const binaryPath = runtimeConfig2.ton_proxy?.binary_path;
2249
+ const tmp = new TonProxyManager({ enabled: false, port, binary_path: binaryPath });
2250
+ await tmp.uninstall();
2273
2251
  }
2274
- });
2275
- }
2276
- async stop() {
2277
- if (this.server) {
2278
- return new Promise((resolve3) => {
2279
- this.server.close(() => {
2280
- logInterceptor.uninstall();
2281
- log2.info("WebUI server stopped");
2282
- resolve3();
2283
- });
2252
+ const runtimeConfig = deps.agent.getConfig();
2253
+ const raw = readRawConfig(deps.configPath);
2254
+ setNestedValue(raw, "ton_proxy.enabled", false);
2255
+ writeRawConfig(raw, deps.configPath);
2256
+ setNestedValue(runtimeConfig, "ton_proxy.enabled", false);
2257
+ log2.info("TON Proxy uninstalled (WebUI)");
2258
+ return c.json({
2259
+ success: true,
2260
+ data: { running: false, installed: false, port: 8080, enabled: false }
2284
2261
  });
2262
+ } catch (err) {
2263
+ log2.error({ err }, "Failed to uninstall TON Proxy");
2264
+ return c.json(
2265
+ { success: false, error: err instanceof Error ? err.message : String(err) },
2266
+ 500
2267
+ );
2285
2268
  }
2286
- }
2287
- getToken() {
2288
- return this.authToken;
2289
- }
2290
- };
2269
+ });
2270
+ return app;
2271
+ }
2272
+
2291
2273
  export {
2292
- WebUIServer
2274
+ logInterceptor,
2275
+ createStatusRoutes,
2276
+ createToolsRoutes,
2277
+ createLogsRoutes,
2278
+ createMemoryRoutes,
2279
+ createSoulRoutes,
2280
+ createPluginsRoutes,
2281
+ createMcpRoutes,
2282
+ createWorkspaceRoutes,
2283
+ createTasksRoutes,
2284
+ createConfigRoutes,
2285
+ createMarketplaceRoutes,
2286
+ createHooksRoutes,
2287
+ createTonProxyRoutes
2293
2288
  };