alvin-bot 5.6.2 → 5.8.0

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 (137) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +1 -1
  3. package/dist/claude.js +1 -102
  4. package/dist/config.js +1 -96
  5. package/dist/engine.js +1 -90
  6. package/dist/find-claude-binary.js +1 -98
  7. package/dist/handlers/async-agent-chunk-handler.js +1 -50
  8. package/dist/handlers/background-bypass.js +1 -75
  9. package/dist/handlers/commands.js +1 -2336
  10. package/dist/handlers/cron-progress.js +1 -52
  11. package/dist/handlers/document.js +1 -194
  12. package/dist/handlers/message.js +1 -959
  13. package/dist/handlers/photo.js +1 -154
  14. package/dist/handlers/platform-message.js +1 -360
  15. package/dist/handlers/stuck-timer.js +1 -54
  16. package/dist/handlers/video.js +1 -237
  17. package/dist/handlers/voice.js +1 -148
  18. package/dist/i18n.js +1 -805
  19. package/dist/index.js +1 -697
  20. package/dist/init-data-dir.js +1 -98
  21. package/dist/middleware/auth.js +1 -233
  22. package/dist/migrate.js +1 -162
  23. package/dist/paths.js +1 -146
  24. package/dist/platforms/discord.js +1 -175
  25. package/dist/platforms/index.js +1 -130
  26. package/dist/platforms/signal.js +1 -205
  27. package/dist/platforms/slack-slash-parser.js +1 -32
  28. package/dist/platforms/slack.js +1 -501
  29. package/dist/platforms/telegram.js +1 -111
  30. package/dist/platforms/types.js +1 -8
  31. package/dist/platforms/whatsapp-auth-helpers.js +1 -53
  32. package/dist/platforms/whatsapp.js +1 -707
  33. package/dist/providers/claude-sdk-provider.js +1 -565
  34. package/dist/providers/codex-cli-provider.js +1 -134
  35. package/dist/providers/index.js +1 -7
  36. package/dist/providers/ollama-provider.js +1 -32
  37. package/dist/providers/openai-compatible.js +1 -406
  38. package/dist/providers/registry.js +1 -352
  39. package/dist/providers/runtime-header.js +1 -45
  40. package/dist/providers/tool-executor.js +1 -475
  41. package/dist/providers/types.js +1 -227
  42. package/dist/services/access.js +1 -144
  43. package/dist/services/allowed-users-gate.js +1 -56
  44. package/dist/services/alvin-dispatch.js +1 -130
  45. package/dist/services/alvin-mcp-tools.js +1 -104
  46. package/dist/services/asset-index.js +1 -224
  47. package/dist/services/async-agent-parser.js +1 -418
  48. package/dist/services/async-agent-watcher.js +1 -443
  49. package/dist/services/auto-diagnostic.js +1 -228
  50. package/dist/services/broadcast.js +1 -52
  51. package/dist/services/browser-manager.js +1 -562
  52. package/dist/services/browser-webfetch.js +1 -127
  53. package/dist/services/browser.js +1 -121
  54. package/dist/services/cdp-bootstrap.js +1 -357
  55. package/dist/services/compaction.js +1 -144
  56. package/dist/services/critical-notify.js +1 -203
  57. package/dist/services/cron-resolver.js +1 -58
  58. package/dist/services/cron-scheduling.js +1 -310
  59. package/dist/services/cron.js +1 -861
  60. package/dist/services/custom-tools.js +1 -317
  61. package/dist/services/delivery-queue.js +1 -173
  62. package/dist/services/delivery-registry.js +1 -21
  63. package/dist/services/disk-cleanup.js +1 -203
  64. package/dist/services/elevenlabs.js +1 -58
  65. package/dist/services/embeddings/auto-detect.js +1 -74
  66. package/dist/services/embeddings/fts5.js +1 -108
  67. package/dist/services/embeddings/gemini.js +1 -65
  68. package/dist/services/embeddings/index.js +1 -496
  69. package/dist/services/embeddings/ollama.js +1 -78
  70. package/dist/services/embeddings/openai.js +1 -49
  71. package/dist/services/embeddings/provider.js +1 -22
  72. package/dist/services/embeddings/vector-base.js +1 -113
  73. package/dist/services/embeddings-migration.js +1 -193
  74. package/dist/services/embeddings.js +1 -9
  75. package/dist/services/env-file.js +1 -50
  76. package/dist/services/exec-guard.js +1 -71
  77. package/dist/services/fallback-order.js +1 -154
  78. package/dist/services/file-permissions.js +1 -93
  79. package/dist/services/heartbeat-file.js +1 -65
  80. package/dist/services/heartbeat.js +1 -313
  81. package/dist/services/hooks.js +1 -44
  82. package/dist/services/imagegen.js +1 -72
  83. package/dist/services/language-detect.js +1 -154
  84. package/dist/services/markdown.js +1 -63
  85. package/dist/services/mcp.js +1 -263
  86. package/dist/services/memory-extractor.js +1 -178
  87. package/dist/services/memory-inject-mode.js +1 -43
  88. package/dist/services/memory-layers.js +1 -156
  89. package/dist/services/memory.js +1 -146
  90. package/dist/services/ollama-manager.js +1 -339
  91. package/dist/services/permissions-wizard.js +1 -291
  92. package/dist/services/personality.js +1 -376
  93. package/dist/services/plugins.js +1 -171
  94. package/dist/services/preflight.js +1 -292
  95. package/dist/services/process-manager.js +1 -291
  96. package/dist/services/release-highlights.js +1 -79
  97. package/dist/services/reminders.js +1 -97
  98. package/dist/services/restart.js +1 -48
  99. package/dist/services/security-audit.js +1 -74
  100. package/dist/services/self-diagnosis.js +1 -272
  101. package/dist/services/self-search.js +1 -129
  102. package/dist/services/session-persistence.js +1 -237
  103. package/dist/services/session.js +1 -282
  104. package/dist/services/skills.js +1 -290
  105. package/dist/services/ssrf-guard.js +1 -162
  106. package/dist/services/standing-orders.js +1 -29
  107. package/dist/services/steer-channel.js +1 -46
  108. package/dist/services/stop-controller.js +1 -52
  109. package/dist/services/subagent-dedup.js +1 -0
  110. package/dist/services/subagent-delivery.js +1 -452
  111. package/dist/services/subagent-stats.js +1 -123
  112. package/dist/services/subagents.js +1 -814
  113. package/dist/services/sudo.js +1 -329
  114. package/dist/services/telegram.js +1 -158
  115. package/dist/services/timing-safe-bearer.js +1 -51
  116. package/dist/services/tool-discovery.js +1 -214
  117. package/dist/services/trends.js +1 -580
  118. package/dist/services/updater.js +1 -291
  119. package/dist/services/usage-tracker.js +1 -144
  120. package/dist/services/users.js +1 -271
  121. package/dist/services/voice.js +1 -104
  122. package/dist/services/watchdog-brake.js +1 -154
  123. package/dist/services/watchdog.js +1 -311
  124. package/dist/services/workspaces.js +1 -276
  125. package/dist/tui/index.js +1 -667
  126. package/dist/util/console-formatter.js +1 -109
  127. package/dist/util/debounce.js +1 -24
  128. package/dist/util/telegram-error-filter.js +1 -62
  129. package/dist/version.js +1 -24
  130. package/dist/web/bind-strategy.js +1 -42
  131. package/dist/web/canvas.js +1 -30
  132. package/dist/web/doctor-api.js +1 -604
  133. package/dist/web/openai-compat.js +1 -252
  134. package/dist/web/server.js +1 -1831
  135. package/dist/web/setup-api.js +1 -1101
  136. package/package.json +5 -2
  137. package/dist/.metadata_never_index +0 -0
@@ -1,1831 +1 @@
1
- /**
2
- * Web Server — Local dashboard for Alvin Bot.
3
- *
4
- * Provides:
5
- * - Static file serving (web/public/)
6
- * - WebSocket for real-time chat + streaming
7
- * - REST API for settings, memory, sessions, etc.
8
- * - Simple password auth (WEB_PASSWORD env var)
9
- */
10
- import http from "http";
11
- import fs from "fs";
12
- import path from "path";
13
- import { resolve } from "path";
14
- import { execSync } from "child_process";
15
- import { WebSocketServer, WebSocket } from "ws";
16
- import { getRegistry } from "../engine.js";
17
- import { getSession, resetSession, getAllSessions } from "../services/session.js";
18
- import { getMemoryStats, loadLongTermMemory, loadDailyLog } from "../services/memory.js";
19
- import { getIndexStats } from "../services/embeddings.js";
20
- import { getLoadedPlugins } from "../services/plugins.js";
21
- import { getMCPStatus } from "../services/mcp.js";
22
- import { listProfiles } from "../services/users.js";
23
- import { listCustomTools, getCustomTools, executeCustomTool } from "../services/custom-tools.js";
24
- import { buildSystemPrompt, reloadSoul, getSoulContent } from "../services/personality.js";
25
- import { config } from "../config.js";
26
- import { handleSetupAPI } from "./setup-api.js";
27
- import { handleDoctorAPI } from "./doctor-api.js";
28
- import { handleOpenAICompat } from "./openai-compat.js";
29
- import { addCanvasClient } from "./canvas.js";
30
- import { BOT_ROOT, ENV_FILE, PUBLIC_DIR, MEMORY_DIR, MEMORY_FILE, SOUL_FILE, DATA_DIR, MCP_CONFIG, SKILLS_DIR } from "../paths.js";
31
- import { writeSecure } from "../services/file-permissions.js";
32
- import { timingSafeBearerMatch } from "../services/timing-safe-bearer.js";
33
- import { broadcast } from "../services/broadcast.js";
34
- import { BOT_VERSION } from "../version.js";
35
- import { decideNextBindAction } from "./bind-strategy.js";
36
- const WEB_PORT = parseInt(process.env.WEB_PORT || "3100");
37
- /** Tuning for the bind loop. Walk the port ladder `MAX_PORT_TRIES` times
38
- * then fall back to a `BACKGROUND_RETRY_MS` idle loop — the bot keeps
39
- * running on Telegram either way; see bind-strategy.ts for the pure
40
- * decision logic. */
41
- const MAX_PORT_TRIES = 20;
42
- const BACKGROUND_RETRY_MS = 30_000;
43
- /** Current live http.Server, if one has successfully bound. */
44
- let currentServer = null;
45
- /** Current live WebSocketServer attached to currentServer. */
46
- let wsServerRef = null;
47
- /** Background-retry timer handle — set when the bind loop is in its
48
- * idle wait between cycles, cleared when stopWebServer() cancels. */
49
- let bindRetryTimer = null;
50
- /** Flag flipped by stopWebServer(). Every bind-loop callback checks
51
- * this and exits silently if set, so stop is truly terminal. */
52
- let stopRequested = false;
53
- const WEB_PASSWORD = process.env.WEB_PASSWORD || "";
54
- /** The actual port the Web UI is running on (may differ from WEB_PORT if busy). */
55
- let actualWebPort = WEB_PORT;
56
- // ── MIME Types ──────────────────────────────────────────
57
- const MIME = {
58
- ".html": "text/html",
59
- ".css": "text/css",
60
- ".js": "application/javascript",
61
- ".json": "application/json",
62
- ".png": "image/png",
63
- ".jpg": "image/jpeg",
64
- ".svg": "image/svg+xml",
65
- ".ico": "image/x-icon",
66
- };
67
- // ── Auth ────────────────────────────────────────────────
68
- const activeSessions = new Set();
69
- function generateToken() {
70
- return Array.from(crypto.getRandomValues(new Uint8Array(32)))
71
- .map(b => b.toString(16).padStart(2, "0")).join("");
72
- }
73
- function checkAuth(req) {
74
- if (!WEB_PASSWORD)
75
- return true; // No password = open access
76
- const cookie = req.headers.cookie || "";
77
- const token = cookie.match(/alvinbot_token=([a-f0-9]+)/)?.[1];
78
- return token ? activeSessions.has(token) : false;
79
- }
80
- /** Returns true when the server is exposed to non-loopback addresses but
81
- * WEB_PASSWORD is empty. In that state every mutating / exec endpoint is
82
- * hard-refused regardless of any session cookie. */
83
- function isExposedWithoutPassword() {
84
- if (WEB_PASSWORD)
85
- return false; // password set → normal auth path
86
- const h = config.webHost;
87
- // loopback addresses are safe even without a password
88
- if (!h || h === "127.0.0.1" || h === "::1" || h === "localhost")
89
- return false;
90
- return true; // non-loopback + no password = exposed
91
- }
92
- // ── REST API ────────────────────────────────────────────
93
- async function handleAPI(req, res, urlPath, body) {
94
- res.setHeader("Content-Type", "application/json");
95
- // POST /api/login
96
- if (urlPath === "/api/login" && req.method === "POST") {
97
- try {
98
- const { password } = JSON.parse(body);
99
- // v5.x — refuse login entirely when WEB_PASSWORD is not set.
100
- // Previously `!WEB_PASSWORD || password === WEB_PASSWORD` minted a
101
- // session for ANY input (including "") when the env var was absent,
102
- // effectively making every unauthenticated request a valid session.
103
- // timingSafeBearerMatch already rejects empty expectedToken → reuse it
104
- // by wrapping the provided password as a synthetic "Bearer <pw>" header.
105
- const authOk = timingSafeBearerMatch(`Bearer ${password}`, WEB_PASSWORD);
106
- if (authOk) {
107
- const token = generateToken();
108
- activeSessions.add(token);
109
- res.setHeader("Set-Cookie", `alvinbot_token=${token}; Path=/; HttpOnly; SameSite=Strict; Max-Age=86400`);
110
- res.end(JSON.stringify({ ok: true }));
111
- }
112
- else {
113
- res.statusCode = 401;
114
- res.end(JSON.stringify({ error: "Wrong password" }));
115
- }
116
- }
117
- catch {
118
- res.statusCode = 400;
119
- res.end(JSON.stringify({ error: "Invalid request" }));
120
- }
121
- return;
122
- }
123
- // POST /api/webhook — external trigger endpoint with bearer auth (no cookie auth needed)
124
- if (urlPath === "/api/webhook" && req.method === "POST") {
125
- if (!config.webhookEnabled) {
126
- res.writeHead(404);
127
- res.end(JSON.stringify({ error: "Webhooks disabled" }));
128
- return;
129
- }
130
- // v4.12.2 — timing-safe bearer token comparison. Previously used
131
- // naive !== which leaks comparison position via timing side-channel.
132
- if (!timingSafeBearerMatch(req.headers.authorization, config.webhookToken ?? "")) {
133
- res.writeHead(401);
134
- res.end(JSON.stringify({ error: "Unauthorized" }));
135
- return;
136
- }
137
- try {
138
- const payload = JSON.parse(body);
139
- if (!payload.message) {
140
- res.writeHead(400);
141
- res.end(JSON.stringify({ error: "Missing message field" }));
142
- return;
143
- }
144
- const channel = payload.channel || "telegram";
145
- const chatId = payload.chatId || String(config.allowedUsers[0] || "");
146
- const { enqueue } = await import("../services/delivery-queue.js");
147
- const id = enqueue(channel, chatId, `[Webhook: ${payload.event || "unknown"}] ${payload.message}`);
148
- res.writeHead(200);
149
- res.end(JSON.stringify({ ok: true, queued: id }));
150
- }
151
- catch {
152
- res.writeHead(400);
153
- res.end(JSON.stringify({ error: "Invalid JSON body" }));
154
- }
155
- return;
156
- }
157
- // Auth check for all other API routes
158
- if (!checkAuth(req)) {
159
- res.statusCode = 401;
160
- res.end(JSON.stringify({ error: "Not authenticated" }));
161
- return;
162
- }
163
- // Hard gate: when the web server is reachable from non-loopback addresses
164
- // and WEB_PASSWORD is empty, mutating/exec endpoints are refused outright.
165
- // Local dev (127.0.0.1 + no password) is intentionally unaffected.
166
- // Endpoints that write files, execute commands, or modify configuration
167
- // are in scope; read-only status/config endpoints are not gated.
168
- const EXPOSED_DANGEROUS_ROUTES = new Set([
169
- "/api/terminal",
170
- "/api/skills/create",
171
- "/api/skills/update",
172
- "/api/skills/delete",
173
- "/api/files/save",
174
- "/api/files/delete",
175
- "/api/env/set",
176
- "/api/setup-wizard",
177
- "/api/soul/save",
178
- "/api/memory/save",
179
- "/api/memory/delete",
180
- "/api/session/reset",
181
- // cron — /api/cron/add was wrong; real route is /api/cron/create
182
- "/api/cron/create",
183
- "/api/cron/update",
184
- "/api/cron/delete",
185
- "/api/cron/toggle",
186
- "/api/cron/run",
187
- "/api/plugin/install",
188
- "/api/plugin/uninstall",
189
- // shell / process exec
190
- "/api/tools/execute",
191
- "/api/sudo/exec",
192
- "/api/sudo/setup",
193
- "/api/sudo/admin-dialog",
194
- "/api/sudo/revoke",
195
- // key / env writes
196
- "/api/providers/set-key",
197
- "/api/providers/set-primary",
198
- "/api/providers/set-fallbacks",
199
- "/api/providers/add-custom",
200
- "/api/providers/remove-custom",
201
- // platform config writes (writes ENV vars / runs npm install)
202
- "/api/platforms/configure",
203
- "/api/platforms/install-deps",
204
- // provider / fallback order writes
205
- "/api/fallback",
206
- "/api/fallback/move",
207
- // MCP config writes
208
- "/api/mcp/add",
209
- "/api/mcp/remove",
210
- // process restart (DoS vector)
211
- "/api/restart",
212
- // model switching (affects all users)
213
- "/api/models/switch",
214
- // WhatsApp auth / config writes
215
- "/api/whatsapp/disconnect",
216
- "/api/whatsapp/group-rules",
217
- ]);
218
- if (isExposedWithoutPassword() && EXPOSED_DANGEROUS_ROUTES.has(urlPath)) {
219
- res.statusCode = 403;
220
- res.end(JSON.stringify({ error: "refused: web exposed without WEB_PASSWORD" }));
221
- return;
222
- }
223
- // ── Setup APIs (platforms + models) ─────────────────
224
- const handled = await handleSetupAPI(req, res, urlPath, body);
225
- if (handled)
226
- return;
227
- // ── Doctor & Backup APIs ──────────────────────────
228
- const doctorHandled = await handleDoctorAPI(req, res, urlPath, body);
229
- if (doctorHandled)
230
- return;
231
- // GET /api/setup-check — is the bot fully configured?
232
- if (urlPath === "/api/setup-check") {
233
- const envPath = ENV_FILE;
234
- let env = {};
235
- try {
236
- const lines = fs.readFileSync(envPath, "utf-8").split("\n");
237
- for (const line of lines) {
238
- if (line.startsWith("#") || !line.includes("="))
239
- continue;
240
- const idx = line.indexOf("=");
241
- env[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
242
- }
243
- }
244
- catch { }
245
- const hasBotToken = !!(env.BOT_TOKEN || process.env.BOT_TOKEN);
246
- const hasAllowedUsers = !!(env.ALLOWED_USERS || process.env.ALLOWED_USERS);
247
- const hasPrimaryProvider = !!(env.PRIMARY_PROVIDER || process.env.PRIMARY_PROVIDER);
248
- // Check which providers have keys
249
- const providerKeys = {
250
- groq: !!(env.GROQ_API_KEY || process.env.GROQ_API_KEY),
251
- openai: !!(env.OPENAI_API_KEY || process.env.OPENAI_API_KEY),
252
- google: !!(env.GOOGLE_API_KEY || process.env.GOOGLE_API_KEY),
253
- nvidia: !!(env.NVIDIA_API_KEY || process.env.NVIDIA_API_KEY),
254
- anthropic: !!(env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_API_KEY),
255
- openrouter: !!(env.OPENROUTER_API_KEY || process.env.OPENROUTER_API_KEY),
256
- };
257
- const hasAnyProvider = hasPrimaryProvider || Object.values(providerKeys).some(Boolean);
258
- // Check Claude CLI
259
- let claudeCliInstalled = false;
260
- try {
261
- const { execSync } = await import("child_process");
262
- execSync("claude --version", { timeout: 5000, stdio: "pipe" });
263
- claudeCliInstalled = true;
264
- }
265
- catch { }
266
- const isComplete = hasBotToken && hasAllowedUsers && hasAnyProvider;
267
- res.end(JSON.stringify({
268
- isComplete,
269
- steps: {
270
- telegram: { done: hasBotToken && hasAllowedUsers, botToken: hasBotToken, allowedUsers: hasAllowedUsers },
271
- provider: { done: hasAnyProvider, primary: env.PRIMARY_PROVIDER || process.env.PRIMARY_PROVIDER || "", keys: providerKeys, claudeCli: claudeCliInstalled },
272
- },
273
- }));
274
- return;
275
- }
276
- // POST /api/setup-wizard — save all setup data at once (first-run wizard)
277
- if (urlPath === "/api/setup-wizard" && req.method === "POST") {
278
- try {
279
- const data = JSON.parse(body);
280
- const envPath = ENV_FILE;
281
- let content = fs.existsSync(envPath) ? fs.readFileSync(envPath, "utf-8") : "";
282
- const setEnv = (key, value) => {
283
- // M6 (centralized): reject values containing newline characters
284
- if (/[\n\r]/.test(value))
285
- throw new Error("env value must not contain newline characters");
286
- const regex = new RegExp(`^${key}=.*$`, "m");
287
- if (regex.test(content)) {
288
- content = content.replace(regex, `${key}=${value}`);
289
- }
290
- else {
291
- content = content.trimEnd() + `\n${key}=${value}\n`;
292
- }
293
- process.env[key] = value;
294
- };
295
- // Step 1: Telegram
296
- if (data.botToken)
297
- setEnv("BOT_TOKEN", data.botToken);
298
- if (data.allowedUsers)
299
- setEnv("ALLOWED_USERS", data.allowedUsers);
300
- // Step 2: Provider
301
- if (data.primaryProvider)
302
- setEnv("PRIMARY_PROVIDER", data.primaryProvider);
303
- if (data.apiKey && data.apiKeyEnv)
304
- setEnv(data.apiKeyEnv, data.apiKey);
305
- // Step 3: Optional
306
- if (data.webPassword)
307
- setEnv("WEB_PASSWORD", data.webPassword);
308
- fs.writeFileSync(envPath, content);
309
- res.end(JSON.stringify({ ok: true, note: "Setup complete! Restart needed." }));
310
- }
311
- catch (e) {
312
- res.statusCode = 400;
313
- res.end(JSON.stringify({ error: e instanceof Error ? e.message : String(e) }));
314
- }
315
- return;
316
- }
317
- // POST /api/validate-bot-token — validate a Telegram bot token
318
- if (urlPath === "/api/validate-bot-token" && req.method === "POST") {
319
- try {
320
- const { token } = JSON.parse(body);
321
- if (!token || !token.includes(":")) {
322
- res.end(JSON.stringify({ ok: false, error: "Invalid token format" }));
323
- return;
324
- }
325
- const tgRes = await fetch(`https://api.telegram.org/bot${token}/getMe`);
326
- const tgData = await tgRes.json();
327
- if (tgData.ok) {
328
- res.end(JSON.stringify({ ok: true, bot: { username: tgData.result.username, firstName: tgData.result.first_name, id: tgData.result.id } }));
329
- }
330
- else {
331
- res.end(JSON.stringify({ ok: false, error: tgData.description || "Invalid token" }));
332
- }
333
- }
334
- catch (e) {
335
- res.end(JSON.stringify({ ok: false, error: e instanceof Error ? e.message : String(e) }));
336
- }
337
- return;
338
- }
339
- // GET /api/status
340
- if (urlPath === "/api/status") {
341
- let modelInfo = { name: "Not configured", model: "none", status: "unconfigured" };
342
- try {
343
- const registry = getRegistry();
344
- const active = registry.getActive().getInfo();
345
- modelInfo = { name: active.name, model: active.model, status: active.status };
346
- }
347
- catch { /* engine not initialized — no provider configured */ }
348
- const memory = getMemoryStats();
349
- const index = getIndexStats();
350
- const plugins = getLoadedPlugins();
351
- const mcp = getMCPStatus();
352
- const users = listProfiles();
353
- const tools = listCustomTools();
354
- // Aggregate token usage across all sessions
355
- const { getAllSessions } = await import("../services/session.js");
356
- const allSessions = getAllSessions();
357
- let totalInputTokens = 0, totalOutputTokens = 0, totalCost = 0;
358
- for (const s of allSessions.values()) {
359
- totalInputTokens += s.totalInputTokens || 0;
360
- totalOutputTokens += s.totalOutputTokens || 0;
361
- totalCost += s.totalCost || 0;
362
- }
363
- const { config: appConfig } = await import("../config.js");
364
- res.end(JSON.stringify({
365
- bot: { version: BOT_VERSION, uptime: process.uptime() },
366
- model: modelInfo,
367
- memory: { ...memory, vectors: index.entries, indexSize: index.sizeBytes },
368
- plugins: plugins.length,
369
- mcp: mcp.length,
370
- users: users.length,
371
- tools: tools.length,
372
- tokens: {
373
- totalInput: totalInputTokens,
374
- totalOutput: totalOutputTokens,
375
- total: totalInputTokens + totalOutputTokens,
376
- totalCost,
377
- },
378
- setup: {
379
- telegram: !!appConfig.botToken,
380
- provider: modelInfo.status !== "unconfigured",
381
- },
382
- }));
383
- return;
384
- }
385
- // GET /api/models
386
- if (urlPath === "/api/models") {
387
- const registry = getRegistry();
388
- registry.listAll().then(models => {
389
- res.end(JSON.stringify({ models, active: registry.getActiveKey() }));
390
- });
391
- return;
392
- }
393
- // POST /api/models/switch
394
- if (urlPath === "/api/models/switch" && req.method === "POST") {
395
- try {
396
- const { key } = JSON.parse(body);
397
- const registry = getRegistry();
398
- const ok = registry.switchTo(key);
399
- res.end(JSON.stringify({ ok, active: registry.getActiveKey() }));
400
- }
401
- catch {
402
- res.statusCode = 400;
403
- res.end(JSON.stringify({ error: "Invalid request" }));
404
- }
405
- return;
406
- }
407
- // GET /api/fallback — Get fallback order + health
408
- if (urlPath === "/api/fallback" && req.method === "GET") {
409
- try {
410
- const { getFallbackOrder } = await import("../services/fallback-order.js");
411
- const { getHealthStatus, isFailedOver } = await import("../services/heartbeat.js");
412
- const registry = getRegistry();
413
- const providers = await registry.listAll();
414
- res.end(JSON.stringify({
415
- order: getFallbackOrder(),
416
- health: getHealthStatus(),
417
- failedOver: isFailedOver(),
418
- activeProvider: registry.getActiveKey(),
419
- availableProviders: providers.map(p => ({ key: p.key, name: p.name, status: p.status })),
420
- }));
421
- }
422
- catch (err) {
423
- res.end(JSON.stringify({ error: String(err) }));
424
- }
425
- return;
426
- }
427
- // POST /api/fallback — Set fallback order
428
- if (urlPath === "/api/fallback" && req.method === "POST") {
429
- try {
430
- const { primary, fallbacks } = JSON.parse(body);
431
- const { setFallbackOrder } = await import("../services/fallback-order.js");
432
- const result = setFallbackOrder(primary, fallbacks, "webui");
433
- res.end(JSON.stringify({ ok: true, order: result }));
434
- }
435
- catch (err) {
436
- res.statusCode = 400;
437
- res.end(JSON.stringify({ error: String(err) }));
438
- }
439
- return;
440
- }
441
- // POST /api/fallback/move — Move provider up/down
442
- if (urlPath === "/api/fallback/move" && req.method === "POST") {
443
- try {
444
- const { key, direction } = JSON.parse(body);
445
- const fb = await import("../services/fallback-order.js");
446
- const result = direction === "up" ? fb.moveUp(key, "webui") : fb.moveDown(key, "webui");
447
- res.end(JSON.stringify({ ok: true, order: result }));
448
- }
449
- catch (err) {
450
- res.statusCode = 400;
451
- res.end(JSON.stringify({ error: String(err) }));
452
- }
453
- return;
454
- }
455
- // GET /api/heartbeat — Health status
456
- if (urlPath === "/api/heartbeat") {
457
- try {
458
- const { getHealthStatus, isFailedOver } = await import("../services/heartbeat.js");
459
- res.end(JSON.stringify({
460
- health: getHealthStatus(),
461
- failedOver: isFailedOver(),
462
- }));
463
- }
464
- catch (err) {
465
- res.end(JSON.stringify({ health: [], failedOver: false }));
466
- }
467
- return;
468
- }
469
- // GET /api/memory
470
- if (urlPath === "/api/memory") {
471
- const ltm = loadLongTermMemory();
472
- const todayLog = loadDailyLog();
473
- const stats = getMemoryStats();
474
- const index = getIndexStats();
475
- // List daily log files
476
- let dailyFiles = [];
477
- try {
478
- dailyFiles = fs.readdirSync(MEMORY_DIR)
479
- .filter(f => f.endsWith(".md") && !f.startsWith("."))
480
- .sort()
481
- .reverse();
482
- }
483
- catch { /* empty */ }
484
- res.end(JSON.stringify({
485
- longTermMemory: ltm,
486
- todayLog,
487
- dailyFiles,
488
- stats,
489
- index: { entries: index.entries, files: index.files, sizeBytes: index.sizeBytes },
490
- }));
491
- return;
492
- }
493
- // GET /api/memory/:file
494
- if (urlPath.startsWith("/api/memory/")) {
495
- const file = urlPath.slice(12);
496
- if (file.includes("..") || !file.endsWith(".md")) {
497
- res.statusCode = 400;
498
- res.end(JSON.stringify({ error: "Invalid file" }));
499
- return;
500
- }
501
- try {
502
- const content = fs.readFileSync(resolve(MEMORY_DIR, file), "utf-8");
503
- res.end(JSON.stringify({ file, content }));
504
- }
505
- catch {
506
- res.statusCode = 404;
507
- res.end(JSON.stringify({ error: "File not found" }));
508
- }
509
- return;
510
- }
511
- // POST /api/memory/save
512
- if (urlPath === "/api/memory/save" && req.method === "POST") {
513
- try {
514
- const { file, content } = JSON.parse(body);
515
- if (file === "MEMORY.md") {
516
- fs.writeFileSync(MEMORY_FILE, content);
517
- }
518
- else if (file.endsWith(".md") && !file.includes("..")) {
519
- fs.writeFileSync(resolve(MEMORY_DIR, file), content);
520
- }
521
- else {
522
- res.statusCode = 400;
523
- res.end(JSON.stringify({ error: "Invalid file" }));
524
- return;
525
- }
526
- res.end(JSON.stringify({ ok: true }));
527
- }
528
- catch {
529
- res.statusCode = 400;
530
- res.end(JSON.stringify({ error: "Invalid request" }));
531
- }
532
- return;
533
- }
534
- // GET /api/plugins
535
- if (urlPath === "/api/plugins") {
536
- res.end(JSON.stringify({ plugins: getLoadedPlugins() }));
537
- return;
538
- }
539
- // v4.12.0 — Workspace overview: registry + per-workspace cost breakdown
540
- if (urlPath === "/api/workspaces") {
541
- try {
542
- const { listWorkspaces, getDefaultWorkspace } = await import("../services/workspaces.js");
543
- const { getCostByWorkspace } = await import("../services/session.js");
544
- const costs = getCostByWorkspace();
545
- const registered = listWorkspaces();
546
- const all = [getDefaultWorkspace(), ...registered];
547
- const payload = all.map((ws) => ({
548
- name: ws.name,
549
- purpose: ws.purpose,
550
- emoji: ws.emoji ?? null,
551
- color: ws.color ?? null,
552
- cwd: ws.cwd,
553
- channels: ws.channels,
554
- stats: costs[ws.name] ?? { totalCost: 0, sessionCount: 0, messageCount: 0, toolUseCount: 0 },
555
- }));
556
- res.end(JSON.stringify({ workspaces: payload }));
557
- }
558
- catch (err) {
559
- res.statusCode = 500;
560
- res.end(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }));
561
- }
562
- return;
563
- }
564
- // GET /api/users — Enhanced with session data
565
- if (urlPath === "/api/users" && req.method === "GET") {
566
- const { getAllSessions } = await import("../services/session.js");
567
- const profiles = listProfiles();
568
- const sessions = getAllSessions();
569
- const sessionMap = new Map(Array.from(sessions.entries()).map(([k, s]) => [Number(k), s]));
570
- const enriched = profiles.map(p => {
571
- const session = sessionMap.get(p.userId);
572
- return {
573
- ...p,
574
- session: session ? {
575
- isProcessing: session.isProcessing,
576
- totalCost: session.totalCost,
577
- historyLength: session.history.length,
578
- effort: session.effort,
579
- voiceReply: session.voiceReply,
580
- startedAt: session.startedAt,
581
- messageCount: session.messageCount,
582
- toolUseCount: session.toolUseCount,
583
- workingDir: session.workingDir,
584
- hasActiveQuery: !!session.abortController,
585
- queuedMessages: session.messageQueue.length,
586
- } : null,
587
- };
588
- });
589
- res.end(JSON.stringify({ users: enriched }));
590
- return;
591
- }
592
- // DELETE /api/users/:id — Kill session + delete user data
593
- if (urlPath.startsWith("/api/users/") && req.method === "DELETE") {
594
- // Pattern route — gate here since EXPOSED_DANGEROUS_ROUTES can't express :id
595
- if (isExposedWithoutPassword()) {
596
- res.statusCode = 403;
597
- res.end(JSON.stringify({ error: "refused: web exposed without WEB_PASSWORD" }));
598
- return;
599
- }
600
- const userId = parseInt(urlPath.split("/").pop() || "0");
601
- if (!userId) {
602
- res.statusCode = 400;
603
- res.end(JSON.stringify({ error: "Invalid user ID" }));
604
- return;
605
- }
606
- const { deleteUser } = await import("../services/users.js");
607
- const result = deleteUser(userId);
608
- res.end(JSON.stringify({ ok: true, ...result }));
609
- return;
610
- }
611
- // GET /api/tools
612
- if (urlPath === "/api/tools") {
613
- const tools = getCustomTools();
614
- res.end(JSON.stringify({ tools }));
615
- return;
616
- }
617
- // POST /api/tools/execute — run a tool by name
618
- if (urlPath === "/api/tools/execute" && req.method === "POST") {
619
- try {
620
- const { name, params } = JSON.parse(body);
621
- if (!name) {
622
- res.statusCode = 400;
623
- res.end(JSON.stringify({ error: "No tool name" }));
624
- return;
625
- }
626
- const output = await executeCustomTool(name, params || {});
627
- res.end(JSON.stringify({ ok: true, output }));
628
- }
629
- catch (err) {
630
- const error = err instanceof Error ? err.message : String(err);
631
- res.end(JSON.stringify({ error }));
632
- }
633
- return;
634
- }
635
- // ── MCP Management ─────────────────────────────────────
636
- // GET /api/mcp — list MCP servers + tools
637
- if (urlPath === "/api/mcp") {
638
- const { getMCPStatus, getMCPTools, hasMCPConfig } = await import("../services/mcp.js");
639
- const servers = getMCPStatus();
640
- const tools = getMCPTools();
641
- // Read raw config for editing
642
- const configPath = MCP_CONFIG;
643
- let rawConfig = { servers: {} };
644
- try {
645
- rawConfig = JSON.parse(fs.readFileSync(configPath, "utf-8"));
646
- }
647
- catch { }
648
- res.end(JSON.stringify({ servers, tools, config: rawConfig, hasConfig: hasMCPConfig() }));
649
- return;
650
- }
651
- // POST /api/mcp/add — add a new MCP server
652
- if (urlPath === "/api/mcp/add" && req.method === "POST") {
653
- try {
654
- const { name, command, args, url: serverUrl, env, headers } = JSON.parse(body);
655
- if (!name) {
656
- res.statusCode = 400;
657
- res.end(JSON.stringify({ error: "Name required" }));
658
- return;
659
- }
660
- const configPath = MCP_CONFIG;
661
- let config = { servers: {} };
662
- try {
663
- config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
664
- }
665
- catch { }
666
- const entry = {};
667
- if (command) {
668
- entry.command = command;
669
- entry.args = args || [];
670
- if (env)
671
- entry.env = env;
672
- }
673
- else if (serverUrl) {
674
- entry.url = serverUrl;
675
- if (headers)
676
- entry.headers = headers;
677
- }
678
- else {
679
- res.statusCode = 400;
680
- res.end(JSON.stringify({ error: "command or url required" }));
681
- return;
682
- }
683
- config.servers[name] = entry;
684
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
685
- res.end(JSON.stringify({ ok: true, note: "Restart needed to connect." }));
686
- }
687
- catch (e) {
688
- res.statusCode = 400;
689
- res.end(JSON.stringify({ error: e instanceof Error ? e.message : String(e) }));
690
- }
691
- return;
692
- }
693
- // POST /api/mcp/remove — remove an MCP server
694
- if (urlPath === "/api/mcp/remove" && req.method === "POST") {
695
- try {
696
- const { name } = JSON.parse(body);
697
- const configPath = MCP_CONFIG;
698
- let config = { servers: {} };
699
- try {
700
- config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
701
- }
702
- catch { }
703
- delete config.servers[name];
704
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
705
- res.end(JSON.stringify({ ok: true }));
706
- }
707
- catch (e) {
708
- res.statusCode = 400;
709
- res.end(JSON.stringify({ error: e instanceof Error ? e.message : String(e) }));
710
- }
711
- return;
712
- }
713
- // GET /api/mcp/discover — auto-discover MCP servers on the system
714
- if (urlPath === "/api/mcp/discover") {
715
- const discovered = [];
716
- const { execSync } = await import("child_process");
717
- // Check for common MCP server npm packages
718
- const knownServers = [
719
- { pkg: "@modelcontextprotocol/server-filesystem", name: "filesystem", args: ["/tmp"] },
720
- { pkg: "@modelcontextprotocol/server-brave-search", name: "brave-search", args: [] },
721
- { pkg: "@modelcontextprotocol/server-github", name: "github", args: [] },
722
- { pkg: "@modelcontextprotocol/server-postgres", name: "postgres", args: [] },
723
- { pkg: "@modelcontextprotocol/server-sqlite", name: "sqlite", args: [] },
724
- { pkg: "@modelcontextprotocol/server-slack", name: "slack", args: [] },
725
- { pkg: "@modelcontextprotocol/server-memory", name: "memory", args: [] },
726
- { pkg: "@modelcontextprotocol/server-puppeteer", name: "puppeteer", args: [] },
727
- { pkg: "@modelcontextprotocol/server-fetch", name: "web-fetch", args: [] },
728
- { pkg: "@anthropic/mcp-server-sequential-thinking", name: "sequential-thinking", args: [] },
729
- ];
730
- for (const s of knownServers) {
731
- try {
732
- execSync(`npx --yes ${s.pkg} --help`, { timeout: 5000, stdio: "pipe", env: { ...process.env, PATH: process.env.PATH + ":/opt/homebrew/bin:/usr/local/bin" } });
733
- discovered.push({ name: s.name, command: "npx", args: ["-y", s.pkg, ...s.args], source: "npm" });
734
- }
735
- catch {
736
- // Not installed — try checking if globally available
737
- try {
738
- execSync(`npm list -g ${s.pkg} --depth=0`, { timeout: 5000, stdio: "pipe" });
739
- discovered.push({ name: s.name, command: "npx", args: ["-y", s.pkg, ...s.args], source: "npm-global" });
740
- }
741
- catch { /* not installed */ }
742
- }
743
- }
744
- // Check for Claude Desktop MCP config
745
- const homeDir = process.env.HOME || process.env.USERPROFILE || "";
746
- const claudeConfigPaths = [
747
- resolve(homeDir, ".config/claude/claude_desktop_config.json"),
748
- resolve(homeDir, "Library/Application Support/Claude/claude_desktop_config.json"),
749
- resolve(homeDir, "AppData/Roaming/Claude/claude_desktop_config.json"),
750
- ];
751
- for (const cfgPath of claudeConfigPaths) {
752
- try {
753
- const cfg = JSON.parse(fs.readFileSync(cfgPath, "utf-8"));
754
- if (cfg.mcpServers) {
755
- for (const [name, srv] of Object.entries(cfg.mcpServers)) {
756
- if (srv.command) {
757
- discovered.push({ name: `claude-${name}`, command: srv.command, args: srv.args || [], source: "claude-desktop" });
758
- }
759
- }
760
- }
761
- }
762
- catch { /* not found */ }
763
- }
764
- res.end(JSON.stringify({ discovered }));
765
- return;
766
- }
767
- // ── Skills Management ─────────────────────────────────
768
- // GET /api/skills — already in setup-api.ts, but add full CRUD here
769
- // GET /api/skills/detail/:id — get full skill content
770
- if (urlPath?.match(/^\/api\/skills\/detail\//) && req.method === "GET") {
771
- const skillId = urlPath.split("/").pop();
772
- const { getSkills } = await import("../services/skills.js");
773
- const skill = getSkills().find(s => s.id === skillId);
774
- if (skill) {
775
- res.end(JSON.stringify({ ok: true, skill }));
776
- }
777
- else {
778
- res.statusCode = 404;
779
- res.end(JSON.stringify({ error: "Skill not found" }));
780
- }
781
- return;
782
- }
783
- // POST /api/skills/create — create a new skill
784
- if (urlPath === "/api/skills/create" && req.method === "POST") {
785
- try {
786
- const { id, name, description, triggers, category, content, priority } = JSON.parse(body);
787
- if (!id || !name) {
788
- res.statusCode = 400;
789
- res.end(JSON.stringify({ error: "id and name required" }));
790
- return;
791
- }
792
- // Security: reject id values that could escape SKILLS_DIR.
793
- // Mirror the resolve+startsWith containment pattern used in /api/files/save (server.ts ~:921).
794
- // Also guard against ids with path separators or absolute paths before
795
- // resolve() ever runs, so the raw string never reaches the filesystem.
796
- if (typeof id !== "string" ||
797
- id.includes("..") ||
798
- id.includes("/") ||
799
- id.includes("\\") ||
800
- path.isAbsolute(id)) {
801
- res.statusCode = 400;
802
- res.end(JSON.stringify({ error: "Invalid skill id" }));
803
- return;
804
- }
805
- const skillsDir = SKILLS_DIR;
806
- const skillDir = resolve(skillsDir, id);
807
- // Belt-and-suspenders: resolved path must still be inside SKILLS_DIR
808
- if (!skillDir.startsWith(skillsDir)) {
809
- res.statusCode = 400;
810
- res.end(JSON.stringify({ error: "Invalid skill id" }));
811
- return;
812
- }
813
- if (!fs.existsSync(skillDir))
814
- fs.mkdirSync(skillDir, { recursive: true });
815
- const frontmatter = [
816
- "---",
817
- `name: ${name}`,
818
- description ? `description: ${description}` : "",
819
- triggers ? `triggers: ${Array.isArray(triggers) ? triggers.join(", ") : triggers}` : "",
820
- `priority: ${priority || 3}`,
821
- `category: ${category || "custom"}`,
822
- "---",
823
- ].filter(Boolean).join("\n");
824
- fs.writeFileSync(resolve(skillDir, "SKILL.md"), `${frontmatter}\n\n${content || ""}`);
825
- // Force reload
826
- const { loadSkills } = await import("../services/skills.js");
827
- loadSkills();
828
- res.end(JSON.stringify({ ok: true }));
829
- }
830
- catch (e) {
831
- res.statusCode = 400;
832
- res.end(JSON.stringify({ error: e instanceof Error ? e.message : String(e) }));
833
- }
834
- return;
835
- }
836
- // POST /api/skills/update — update an existing skill
837
- if (urlPath === "/api/skills/update" && req.method === "POST") {
838
- try {
839
- const { id, content } = JSON.parse(body);
840
- // Security: same id containment as /api/skills/create
841
- if (typeof id !== "string" ||
842
- id.includes("..") ||
843
- id.includes("/") ||
844
- id.includes("\\") ||
845
- path.isAbsolute(id)) {
846
- res.statusCode = 400;
847
- res.end(JSON.stringify({ error: "Invalid skill id" }));
848
- return;
849
- }
850
- // Belt-and-suspenders: resolved path must still be inside SKILLS_DIR
851
- if (!resolve(SKILLS_DIR, id).startsWith(SKILLS_DIR)) {
852
- res.statusCode = 400;
853
- res.end(JSON.stringify({ error: "Invalid skill id" }));
854
- return;
855
- }
856
- const skillPath = resolve(SKILLS_DIR, id, "SKILL.md");
857
- if (!fs.existsSync(skillPath)) {
858
- // Try flat file
859
- const flatPath = resolve(SKILLS_DIR, id + ".md");
860
- if (fs.existsSync(flatPath)) {
861
- fs.writeFileSync(flatPath, content);
862
- }
863
- else {
864
- res.statusCode = 404;
865
- res.end(JSON.stringify({ error: "Skill not found" }));
866
- return;
867
- }
868
- }
869
- else {
870
- fs.writeFileSync(skillPath, content);
871
- }
872
- const { loadSkills } = await import("../services/skills.js");
873
- loadSkills();
874
- res.end(JSON.stringify({ ok: true }));
875
- }
876
- catch (e) {
877
- res.statusCode = 400;
878
- res.end(JSON.stringify({ error: e instanceof Error ? e.message : String(e) }));
879
- }
880
- return;
881
- }
882
- // POST /api/skills/delete — delete a skill
883
- if (urlPath === "/api/skills/delete" && req.method === "POST") {
884
- try {
885
- const { id } = JSON.parse(body);
886
- // Security: same raw-string id containment as /api/skills/create + /api/skills/update
887
- if (typeof id !== "string" ||
888
- id.includes("..") ||
889
- id.includes("/") ||
890
- id.includes("\\") ||
891
- path.isAbsolute(id)) {
892
- res.statusCode = 400;
893
- res.end(JSON.stringify({ error: "Invalid skill id" }));
894
- return;
895
- }
896
- const skillDir = resolve(SKILLS_DIR, id);
897
- const flatFile = resolve(SKILLS_DIR, id + ".md");
898
- if (fs.existsSync(skillDir)) {
899
- fs.rmSync(skillDir, { recursive: true });
900
- }
901
- else if (fs.existsSync(flatFile)) {
902
- fs.unlinkSync(flatFile);
903
- }
904
- else {
905
- res.statusCode = 404;
906
- res.end(JSON.stringify({ error: "Skill not found" }));
907
- return;
908
- }
909
- const { loadSkills } = await import("../services/skills.js");
910
- loadSkills();
911
- res.end(JSON.stringify({ ok: true }));
912
- }
913
- catch (e) {
914
- res.statusCode = 400;
915
- res.end(JSON.stringify({ error: e instanceof Error ? e.message : String(e) }));
916
- }
917
- return;
918
- }
919
- // GET /api/config
920
- if (urlPath === "/api/config") {
921
- res.end(JSON.stringify({
922
- providers: config.fallbackProviders,
923
- primaryProvider: config.primaryProvider,
924
- allowedUsers: config.allowedUsers,
925
- hasKeys: {
926
- groq: !!config.apiKeys.groq,
927
- openai: !!config.apiKeys.openai,
928
- google: !!config.apiKeys.google,
929
- nvidia: !!config.apiKeys.nvidia,
930
- openrouter: !!config.apiKeys.openrouter,
931
- },
932
- }));
933
- return;
934
- }
935
- // GET /api/sessions
936
- if (urlPath === "/api/sessions") {
937
- const sessions = getAllSessions();
938
- const profiles = listProfiles();
939
- const data = Array.from(sessions.entries()).map(([key, session]) => {
940
- const userId = Number(key.split(":").pop());
941
- const profile = profiles.find(p => p.userId === userId);
942
- return {
943
- userId: key,
944
- name: profile?.name || `User ${key}`,
945
- username: profile?.username,
946
- messageCount: session.messageCount,
947
- toolUseCount: session.toolUseCount,
948
- totalCost: session.totalCost,
949
- totalInputTokens: session.totalInputTokens || 0,
950
- totalOutputTokens: session.totalOutputTokens || 0,
951
- effort: session.effort,
952
- startedAt: session.startedAt,
953
- lastActivity: session.lastActivity,
954
- historyLength: session.history.length,
955
- isProcessing: session.isProcessing,
956
- provider: Object.keys(session.queriesByProvider).join(", ") || "none",
957
- };
958
- });
959
- res.end(JSON.stringify({ sessions: data }));
960
- return;
961
- }
962
- // GET /api/sessions/:userId/history
963
- if (urlPath.match(/^\/api\/sessions\/\d+\/history$/)) {
964
- const userId = parseInt(urlPath.split("/")[3]);
965
- const session = getSession(userId);
966
- res.end(JSON.stringify({
967
- userId,
968
- history: session.history.map(h => ({ role: h.role, content: h.content.slice(0, 2000) })),
969
- }));
970
- return;
971
- }
972
- // GET /api/files?path=...
973
- if (urlPath === "/api/files") {
974
- const params = new URLSearchParams((req.url || "").split("?")[1] || "");
975
- const reqPath = params.get("path") || "";
976
- const basePath = resolve(BOT_ROOT, reqPath || ".");
977
- // Security: must be within BOT_ROOT
978
- if (!basePath.startsWith(BOT_ROOT)) {
979
- res.statusCode = 403;
980
- res.end(JSON.stringify({ error: "Access denied" }));
981
- return;
982
- }
983
- try {
984
- const stat = fs.statSync(basePath);
985
- if (stat.isDirectory()) {
986
- const entries = fs.readdirSync(basePath, { withFileTypes: true })
987
- .filter(e => !e.name.startsWith(".") && e.name !== "node_modules")
988
- .map(e => ({
989
- name: e.name,
990
- type: e.isDirectory() ? "dir" : "file",
991
- size: e.isFile() ? fs.statSync(resolve(basePath, e.name)).size : 0,
992
- modified: fs.statSync(resolve(basePath, e.name)).mtimeMs,
993
- }))
994
- .sort((a, b) => {
995
- if (a.type !== b.type)
996
- return a.type === "dir" ? -1 : 1;
997
- return a.name.localeCompare(b.name);
998
- });
999
- res.end(JSON.stringify({ path: reqPath || ".", entries }));
1000
- }
1001
- else {
1002
- // Read file content — text files up to 500KB
1003
- const ext = path.extname(basePath).toLowerCase();
1004
- const textExts = new Set([
1005
- ".md", ".txt", ".json", ".js", ".ts", ".jsx", ".tsx", ".css", ".html", ".htm",
1006
- ".xml", ".svg", ".yml", ".yaml", ".toml", ".ini", ".cfg", ".conf", ".env",
1007
- ".sh", ".bash", ".zsh", ".fish", ".py", ".rb", ".go", ".rs", ".java", ".kt",
1008
- ".c", ".cpp", ".h", ".hpp", ".cs", ".php", ".sql", ".graphql", ".prisma",
1009
- ".dockerfile", ".gitignore", ".gitattributes", ".editorconfig", ".prettierrc",
1010
- ".eslintrc", ".babelrc", ".npmrc", ".nvmrc", ".lock", ".log", ".csv", ".tsv",
1011
- ".mjs", ".cjs", ".mts", ".cts", ".vue", ".svelte", ".astro",
1012
- ]);
1013
- // Files without extension that match known names are always text
1014
- const textNames = new Set([
1015
- "dockerfile", "makefile", "procfile", "gemfile", "rakefile",
1016
- "vagrantfile", "brewfile", "justfile", "taskfile", "cakefile",
1017
- "license", "licence", "readme", "changelog", "authors", "contributors",
1018
- ]);
1019
- const baseName = path.basename(basePath).toLowerCase();
1020
- const isKnownTextName = textNames.has(baseName);
1021
- const isText = textExts.has(ext) || isKnownTextName || (!ext && stat.size < 100_000);
1022
- if (stat.size > 500_000) {
1023
- res.end(JSON.stringify({ path: reqPath, content: `[File too large: ${(stat.size / 1024).toFixed(1)} KB — max 500 KB]`, size: stat.size }));
1024
- }
1025
- else if (isText) {
1026
- try {
1027
- const content = fs.readFileSync(basePath, "utf-8");
1028
- // Quick binary check: if >10% null bytes, it's binary
1029
- const nullCount = [...content.slice(0, 1000)].filter(c => c === "\0").length;
1030
- if (nullCount > 100) {
1031
- res.end(JSON.stringify({ path: reqPath, content: null, size: stat.size, binary: true }));
1032
- }
1033
- else {
1034
- res.end(JSON.stringify({ path: reqPath, content, size: stat.size }));
1035
- }
1036
- }
1037
- catch {
1038
- res.end(JSON.stringify({ path: reqPath, content: null, size: stat.size, binary: true }));
1039
- }
1040
- }
1041
- else {
1042
- res.end(JSON.stringify({ path: reqPath, content: null, size: stat.size, binary: true }));
1043
- }
1044
- }
1045
- }
1046
- catch {
1047
- res.statusCode = 404;
1048
- res.end(JSON.stringify({ error: "Not found" }));
1049
- }
1050
- return;
1051
- }
1052
- // POST /api/files/save
1053
- if (urlPath === "/api/files/save" && req.method === "POST") {
1054
- try {
1055
- const { path: filePath, content } = JSON.parse(body);
1056
- const absPath = resolve(BOT_ROOT, filePath);
1057
- if (!absPath.startsWith(BOT_ROOT)) {
1058
- res.statusCode = 403;
1059
- res.end(JSON.stringify({ error: "Access denied" }));
1060
- return;
1061
- }
1062
- fs.writeFileSync(absPath, content);
1063
- res.end(JSON.stringify({ ok: true }));
1064
- }
1065
- catch (err) {
1066
- res.statusCode = 400;
1067
- const error = err instanceof Error ? err.message : "Invalid request";
1068
- res.end(JSON.stringify({ error }));
1069
- }
1070
- return;
1071
- }
1072
- // POST /api/files/delete
1073
- if (urlPath === "/api/files/delete" && req.method === "POST") {
1074
- try {
1075
- const { path: filePath } = JSON.parse(body);
1076
- const absPath = resolve(BOT_ROOT, filePath);
1077
- if (!absPath.startsWith(BOT_ROOT)) {
1078
- res.statusCode = 403;
1079
- res.end(JSON.stringify({ error: "Access denied" }));
1080
- return;
1081
- }
1082
- // Safety: don't allow deleting critical files
1083
- const critical = [".env", "package.json", "tsconfig.json", "ecosystem.config.cjs"];
1084
- const baseName = path.basename(absPath);
1085
- if (critical.includes(baseName)) {
1086
- res.statusCode = 403;
1087
- res.end(JSON.stringify({ error: `${baseName} cannot be deleted (protected)` }));
1088
- return;
1089
- }
1090
- if (!fs.existsSync(absPath)) {
1091
- res.statusCode = 404;
1092
- res.end(JSON.stringify({ error: "File not found" }));
1093
- return;
1094
- }
1095
- const stat = fs.statSync(absPath);
1096
- if (stat.isDirectory()) {
1097
- res.statusCode = 400;
1098
- res.end(JSON.stringify({ error: "Directories cannot be deleted" }));
1099
- return;
1100
- }
1101
- fs.unlinkSync(absPath);
1102
- res.end(JSON.stringify({ ok: true }));
1103
- }
1104
- catch (err) {
1105
- res.statusCode = 400;
1106
- const error = err instanceof Error ? err.message : "Invalid request";
1107
- res.end(JSON.stringify({ error }));
1108
- }
1109
- return;
1110
- }
1111
- // POST /api/terminal
1112
- if (urlPath === "/api/terminal" && req.method === "POST") {
1113
- try {
1114
- const { command } = JSON.parse(body);
1115
- if (!command) {
1116
- res.statusCode = 400;
1117
- res.end(JSON.stringify({ error: "No command" }));
1118
- return;
1119
- }
1120
- // Security: limit command length
1121
- if (command.length > 10000) {
1122
- res.statusCode = 400;
1123
- res.end(JSON.stringify({ error: "Command too long (max 10000 chars)" }));
1124
- return;
1125
- }
1126
- const cwd = typeof (JSON.parse(body)).cwd === "string" ? resolve(JSON.parse(body).cwd) : BOT_ROOT;
1127
- const output = execSync(command, {
1128
- cwd,
1129
- stdio: "pipe",
1130
- timeout: 120000,
1131
- env: { ...process.env, PATH: process.env.PATH + ":/opt/homebrew/bin:/usr/local/bin" },
1132
- }).toString();
1133
- res.end(JSON.stringify({ output: output.slice(0, 100000) }));
1134
- }
1135
- catch (err) {
1136
- const error = err;
1137
- const stderr = error.stderr?.toString()?.trim() || "";
1138
- res.end(JSON.stringify({ output: stderr || error.message, exitCode: 1 }));
1139
- }
1140
- return;
1141
- }
1142
- // GET /api/env — read .env keys (names only, values masked)
1143
- if (urlPath === "/api/env") {
1144
- try {
1145
- const envContent = fs.existsSync(ENV_FILE) ? fs.readFileSync(ENV_FILE, "utf-8") : "";
1146
- const lines = envContent.split("\n").filter(l => l.includes("=") && !l.startsWith("#"));
1147
- const vars = lines.map(l => {
1148
- const [key, ...rest] = l.split("=");
1149
- const value = rest.join("=").trim();
1150
- // Mask sensitive values
1151
- const masked = key.includes("KEY") || key.includes("TOKEN") || key.includes("PASSWORD") || key.includes("SECRET")
1152
- ? (value.length > 4 ? value.slice(0, 4) + "..." + value.slice(-4) : "****")
1153
- : value;
1154
- return { key: key.trim(), value: masked, hasValue: value.length > 0 };
1155
- });
1156
- res.end(JSON.stringify({ vars }));
1157
- }
1158
- catch {
1159
- res.end(JSON.stringify({ vars: [] }));
1160
- }
1161
- return;
1162
- }
1163
- // POST /api/env/set — update an env var
1164
- if (urlPath === "/api/env/set" && req.method === "POST") {
1165
- try {
1166
- const { key, value } = JSON.parse(body);
1167
- if (!key || typeof key !== "string" || !key.match(/^[A-Z_][A-Z0-9_]*$/)) {
1168
- res.statusCode = 400;
1169
- res.end(JSON.stringify({ error: "Invalid key name" }));
1170
- return;
1171
- }
1172
- // M6: reject values containing newline characters — they allow injecting
1173
- // extra .env lines (e.g. value="good\nEVIL=injected").
1174
- if (typeof value === "string" && /[\n\r]/.test(value)) {
1175
- res.statusCode = 400;
1176
- res.end(JSON.stringify({ error: "Invalid value: newline characters not allowed" }));
1177
- return;
1178
- }
1179
- let envContent = fs.existsSync(ENV_FILE) ? fs.readFileSync(ENV_FILE, "utf-8") : "";
1180
- const regex = new RegExp(`^${key}=.*$`, "m");
1181
- if (regex.test(envContent)) {
1182
- envContent = envContent.replace(regex, `${key}=${value}`);
1183
- }
1184
- else {
1185
- envContent = envContent.trimEnd() + `\n${key}=${value}\n`;
1186
- }
1187
- // v4.12.2 — enforce 0o600 on .env
1188
- writeSecure(ENV_FILE, envContent);
1189
- res.end(JSON.stringify({ ok: true, note: "Restart required for changes to take effect" }));
1190
- }
1191
- catch {
1192
- res.statusCode = 400;
1193
- res.end(JSON.stringify({ error: "Invalid request" }));
1194
- }
1195
- return;
1196
- }
1197
- // GET /api/soul — read SOUL.md
1198
- if (urlPath === "/api/soul") {
1199
- const content = getSoulContent();
1200
- res.end(JSON.stringify({ content }));
1201
- return;
1202
- }
1203
- // POST /api/soul/save — update SOUL.md
1204
- if (urlPath === "/api/soul/save" && req.method === "POST") {
1205
- try {
1206
- const { content } = JSON.parse(body);
1207
- const soulPath = SOUL_FILE;
1208
- fs.writeFileSync(soulPath, content);
1209
- reloadSoul();
1210
- res.end(JSON.stringify({ ok: true }));
1211
- }
1212
- catch {
1213
- res.statusCode = 400;
1214
- res.end(JSON.stringify({ error: "Invalid request" }));
1215
- }
1216
- return;
1217
- }
1218
- // GET /api/platforms — platform adapter status
1219
- if (urlPath === "/api/platforms") {
1220
- const platforms = [
1221
- { name: "Telegram", key: "BOT_TOKEN", icon: "📱", configured: !!process.env.BOT_TOKEN },
1222
- { name: "Discord", key: "DISCORD_TOKEN", icon: "🎮", configured: !!process.env.DISCORD_TOKEN },
1223
- { name: "WhatsApp", key: "WHATSAPP_ENABLED", icon: "💬", configured: process.env.WHATSAPP_ENABLED === "true" },
1224
- { name: "Signal", key: "SIGNAL_API_URL", icon: "🔒", configured: !!process.env.SIGNAL_API_URL },
1225
- { name: "Web UI", key: "WEB_PORT", icon: "🌐", configured: true },
1226
- ];
1227
- res.end(JSON.stringify({ platforms }));
1228
- return;
1229
- }
1230
- // POST /api/restart — restart the bot process
1231
- if (urlPath === "/api/restart" && req.method === "POST") {
1232
- const { scheduleGracefulRestart } = await import("../services/restart.js");
1233
- res.end(JSON.stringify({ ok: true, note: "Restarting..." }));
1234
- scheduleGracefulRestart(500);
1235
- return;
1236
- }
1237
- // POST /api/chat/export — export chat history
1238
- if (urlPath === "/api/chat/export" && req.method === "POST") {
1239
- try {
1240
- const { messages, format } = JSON.parse(body);
1241
- if (format === "json") {
1242
- res.setHeader("Content-Type", "application/json");
1243
- res.end(JSON.stringify({ export: messages }, null, 2));
1244
- }
1245
- else {
1246
- // Markdown
1247
- const md = messages.map((m) => {
1248
- const prefix = m.role === "user" ? "**Du:**" : m.role === "assistant" ? "**Alvin Bot:**" : "*System:*";
1249
- const time = m.time ? ` _(${m.time})_` : "";
1250
- return `${prefix}${time}\n${m.text}\n`;
1251
- }).join("\n---\n\n");
1252
- res.setHeader("Content-Type", "text/markdown");
1253
- res.end(`# Chat Export — Alvin Bot\n_${new Date().toLocaleString("de-DE")}_\n\n---\n\n${md}`);
1254
- }
1255
- }
1256
- catch {
1257
- res.statusCode = 400;
1258
- res.end(JSON.stringify({ error: "Invalid request" }));
1259
- }
1260
- return;
1261
- }
1262
- // ── WhatsApp Group Management API ────────────────────────────────────
1263
- // GET /api/whatsapp/groups — list all WhatsApp groups (live from WA)
1264
- if (urlPath === "/api/whatsapp/groups" && req.method === "GET") {
1265
- try {
1266
- const { getWhatsAppAdapter } = await import("../platforms/whatsapp.js");
1267
- const adapter = getWhatsAppAdapter();
1268
- if (!adapter) {
1269
- res.end(JSON.stringify({ groups: [], error: "WhatsApp nicht verbunden" }));
1270
- return;
1271
- }
1272
- const groups = await adapter.getGroups();
1273
- res.end(JSON.stringify({ groups }));
1274
- }
1275
- catch (err) {
1276
- res.end(JSON.stringify({ groups: [], error: String(err) }));
1277
- }
1278
- return;
1279
- }
1280
- // GET /api/whatsapp/groups/:id/participants — fetch group participants
1281
- if (urlPath.match(/^\/api\/whatsapp\/groups\/[^/]+\/participants$/)) {
1282
- try {
1283
- const groupId = decodeURIComponent(urlPath.split("/")[4]);
1284
- const { getWhatsAppAdapter } = await import("../platforms/whatsapp.js");
1285
- const adapter = getWhatsAppAdapter();
1286
- if (!adapter) {
1287
- res.end(JSON.stringify({ participants: [], error: "WhatsApp nicht verbunden" }));
1288
- return;
1289
- }
1290
- const participants = await adapter.getGroupParticipants(groupId);
1291
- res.end(JSON.stringify({ participants }));
1292
- }
1293
- catch (err) {
1294
- res.end(JSON.stringify({ participants: [], error: String(err) }));
1295
- }
1296
- return;
1297
- }
1298
- // GET /api/whatsapp/group-rules — get all configured group rules
1299
- if (urlPath === "/api/whatsapp/group-rules" && req.method === "GET") {
1300
- const { getGroupRules } = await import("../platforms/whatsapp.js");
1301
- res.end(JSON.stringify({ rules: getGroupRules() }));
1302
- return;
1303
- }
1304
- // POST /api/whatsapp/group-rules — create or update a group rule
1305
- if (urlPath === "/api/whatsapp/group-rules" && req.method === "POST") {
1306
- try {
1307
- const rule = JSON.parse(body);
1308
- if (!rule.groupId) {
1309
- res.statusCode = 400;
1310
- res.end(JSON.stringify({ error: "groupId ist erforderlich" }));
1311
- return;
1312
- }
1313
- const { upsertGroupRule } = await import("../platforms/whatsapp.js");
1314
- const saved = upsertGroupRule(rule);
1315
- res.end(JSON.stringify({ ok: true, rule: saved }));
1316
- }
1317
- catch (err) {
1318
- res.statusCode = 400;
1319
- res.end(JSON.stringify({ error: String(err) }));
1320
- }
1321
- return;
1322
- }
1323
- // DELETE /api/whatsapp/group-rules/:id — delete a group rule
1324
- if (urlPath.match(/^\/api\/whatsapp\/group-rules\//) && req.method === "DELETE") {
1325
- // Pattern route — gate here since EXPOSED_DANGEROUS_ROUTES can't express :id
1326
- if (isExposedWithoutPassword()) {
1327
- res.statusCode = 403;
1328
- res.end(JSON.stringify({ error: "refused: web exposed without WEB_PASSWORD" }));
1329
- return;
1330
- }
1331
- const groupId = decodeURIComponent(urlPath.split("/").slice(4).join("/"));
1332
- const { deleteGroupRule } = await import("../platforms/whatsapp.js");
1333
- const ok = deleteGroupRule(groupId);
1334
- res.end(JSON.stringify({ ok }));
1335
- return;
1336
- }
1337
- res.statusCode = 404;
1338
- res.end(JSON.stringify({ error: "Not found" }));
1339
- }
1340
- // ── WebSocket Chat ──────────────────────────────────────
1341
- // Set of all currently connected chat WebSocket clients (excluding canvas).
1342
- // Populated on connect, cleaned up on close. Used to forward Telegram
1343
- // activity to every observer.
1344
- const chatClients = new Set();
1345
- /**
1346
- * Wire the broadcast bus once at module load. The bus is singleton, so
1347
- * subscribing here means every Telegram message fan-outs to every connected
1348
- * chat client — without any per-connection re-subscription.
1349
- */
1350
- broadcast.on("user_msg", (payload) => {
1351
- if (payload.platform !== "telegram")
1352
- return; // v4.5.0: telegram only for now
1353
- const json = JSON.stringify({
1354
- type: "mirror:user_msg",
1355
- text: payload.text,
1356
- platform: payload.platform,
1357
- userName: payload.userName,
1358
- ts: payload.ts,
1359
- });
1360
- for (const client of chatClients) {
1361
- if (client.readyState === WebSocket.OPEN)
1362
- client.send(json);
1363
- }
1364
- });
1365
- broadcast.on("response_start", (payload) => {
1366
- if (payload.platform !== "telegram")
1367
- return;
1368
- const json = JSON.stringify({ type: "mirror:response_start", platform: payload.platform, ts: payload.ts });
1369
- for (const client of chatClients) {
1370
- if (client.readyState === WebSocket.OPEN)
1371
- client.send(json);
1372
- }
1373
- });
1374
- broadcast.on("response_delta", (payload) => {
1375
- if (payload.platform !== "telegram")
1376
- return;
1377
- const json = JSON.stringify({ type: "mirror:response_delta", delta: payload.delta, platform: payload.platform, ts: payload.ts });
1378
- for (const client of chatClients) {
1379
- if (client.readyState === WebSocket.OPEN)
1380
- client.send(json);
1381
- }
1382
- });
1383
- broadcast.on("response_done", (payload) => {
1384
- if (payload.platform !== "telegram")
1385
- return;
1386
- const json = JSON.stringify({ type: "mirror:response_done", cost: payload.cost, platform: payload.platform, ts: payload.ts });
1387
- for (const client of chatClients) {
1388
- if (client.readyState === WebSocket.OPEN)
1389
- client.send(json);
1390
- }
1391
- });
1392
- function handleWebSocket(wss) {
1393
- wss.on("connection", (ws, req) => {
1394
- // Auth check
1395
- if (WEB_PASSWORD && !checkAuth(req)) {
1396
- ws.close(4001, "Not authenticated");
1397
- return;
1398
- }
1399
- // Canvas WebSocket — separate handler
1400
- const wsUrl = req.url || "/";
1401
- if (wsUrl === "/canvas/ws") {
1402
- addCanvasClient(ws);
1403
- return;
1404
- }
1405
- console.log("WebUI: client connected");
1406
- chatClients.add(ws);
1407
- ws.on("message", async (data) => {
1408
- try {
1409
- const msg = JSON.parse(data.toString());
1410
- if (msg.type === "chat") {
1411
- let { text, effort, file } = msg;
1412
- // v4.5.0: session routing. The client (TUI/WebUI) tells us which
1413
- // session it wants its message to go into. Supported targets:
1414
- // - "tui" → use msg.sessionKey (e.g. "tui:local" or
1415
- // "tui:ephemeral:…"). Isolated from Telegram.
1416
- // - "telegram" → route into the primary Telegram user's session.
1417
- // Responses go back to the client AND to the
1418
- // actual Telegram chat via the broadcast bus.
1419
- // - undefined → backwards-compatible: default to the primary
1420
- // allowed user's session (old behavior).
1421
- const target = msg.target;
1422
- const telegramUserId = config.allowedUsers[0] || 0;
1423
- let sessionKey;
1424
- if (target === "tui" && typeof msg.sessionKey === "string" && msg.sessionKey.startsWith("tui:")) {
1425
- sessionKey = msg.sessionKey;
1426
- }
1427
- else if (target === "telegram") {
1428
- sessionKey = telegramUserId;
1429
- }
1430
- else {
1431
- sessionKey = telegramUserId; // backwards compat
1432
- }
1433
- // Handle file upload — save to temp and reference in prompt
1434
- if (file?.dataUrl && file?.name) {
1435
- try {
1436
- const dataDir = resolve(DATA_DIR, "web-uploads");
1437
- if (!fs.existsSync(dataDir))
1438
- fs.mkdirSync(dataDir, { recursive: true });
1439
- const safeName = file.name.replace(/[^a-zA-Z0-9._-]/g, "_");
1440
- const filePath = resolve(dataDir, `${Date.now()}_${safeName}`);
1441
- const base64Data = file.dataUrl.split(",")[1] || file.dataUrl;
1442
- fs.writeFileSync(filePath, Buffer.from(base64Data, "base64"));
1443
- // Replace placeholder with actual file path
1444
- text = text.replace(/\[File attached:.*?\]/, `[File saved: ${filePath}]`);
1445
- }
1446
- catch (err) {
1447
- console.error("WebUI file upload error:", err);
1448
- }
1449
- }
1450
- const registry = getRegistry();
1451
- const activeProvider = registry.getActive();
1452
- const isSDK = activeProvider.config.type === "claude-sdk";
1453
- const session = getSession(sessionKey);
1454
- const queryOpts = {
1455
- prompt: text,
1456
- systemPrompt: buildSystemPrompt(isSDK, session.language, target === "telegram" ? "telegram" : "web-dashboard"),
1457
- workingDir: session.workingDir,
1458
- effort: effort || session.effort,
1459
- sessionId: isSDK ? session.sessionId : null,
1460
- history: !isSDK ? session.history : undefined,
1461
- };
1462
- let gotDone = false;
1463
- let finalText = ""; // v4.5.0: capture the final response for target=telegram relay
1464
- try {
1465
- // Stream response
1466
- for await (const chunk of registry.queryWithFallback(queryOpts)) {
1467
- if (ws.readyState !== WebSocket.OPEN)
1468
- break;
1469
- switch (chunk.type) {
1470
- case "text":
1471
- if (chunk.text)
1472
- finalText = chunk.text;
1473
- ws.send(JSON.stringify({ type: "text", text: chunk.text, delta: chunk.delta }));
1474
- break;
1475
- case "tool_use":
1476
- ws.send(JSON.stringify({ type: "tool", name: chunk.toolName, input: chunk.toolInput }));
1477
- break;
1478
- case "done":
1479
- gotDone = true;
1480
- if (chunk.text)
1481
- finalText = chunk.text;
1482
- if (chunk.sessionId)
1483
- session.sessionId = chunk.sessionId;
1484
- if (chunk.costUsd)
1485
- session.totalCost += chunk.costUsd;
1486
- if (chunk.inputTokens)
1487
- session.totalInputTokens = (session.totalInputTokens || 0) + chunk.inputTokens;
1488
- if (chunk.outputTokens)
1489
- session.totalOutputTokens = (session.totalOutputTokens || 0) + chunk.outputTokens;
1490
- ws.send(JSON.stringify({
1491
- type: "done", cost: chunk.costUsd, sessionId: chunk.sessionId,
1492
- inputTokens: chunk.inputTokens, outputTokens: chunk.outputTokens,
1493
- sessionTokens: { input: session.totalInputTokens || 0, output: session.totalOutputTokens || 0 },
1494
- }));
1495
- break;
1496
- case "error":
1497
- ws.send(JSON.stringify({ type: "error", error: chunk.error }));
1498
- gotDone = true; // error counts as done
1499
- break;
1500
- case "fallback":
1501
- ws.send(JSON.stringify({ type: "fallback", from: chunk.failedProvider, to: chunk.providerName }));
1502
- break;
1503
- }
1504
- }
1505
- // Ensure we always send done (in case stream ended without done/error chunk)
1506
- if (!gotDone && ws.readyState === WebSocket.OPEN) {
1507
- ws.send(JSON.stringify({ type: "done", cost: 0 }));
1508
- }
1509
- // v4.5.0: if the user typed in the TUI with target=telegram, we
1510
- // must also post the bot's final response to the actual Telegram
1511
- // chat so the continuity is preserved from the Telegram side.
1512
- // (Telegram bots cannot forge user messages, so only the
1513
- // response lands in the chat — the user prompt itself stays
1514
- // in the TUI.)
1515
- if (target === "telegram" && finalText.trim()) {
1516
- try {
1517
- const dq = await import("../services/delivery-queue.js");
1518
- dq.enqueue("telegram", String(telegramUserId), finalText);
1519
- }
1520
- catch (err) {
1521
- console.error("WebUI → Telegram relay failed:", err);
1522
- }
1523
- }
1524
- }
1525
- catch (streamErr) {
1526
- const errMsg = streamErr instanceof Error ? streamErr.message : String(streamErr);
1527
- console.error("WebUI stream error:", errMsg);
1528
- if (ws.readyState === WebSocket.OPEN) {
1529
- ws.send(JSON.stringify({ type: "error", error: errMsg }));
1530
- if (!gotDone) {
1531
- ws.send(JSON.stringify({ type: "done", cost: 0 }));
1532
- }
1533
- }
1534
- }
1535
- }
1536
- if (msg.type === "reset") {
1537
- // v4.5.0: reset the target session, not a hardcoded one.
1538
- const target = msg.target;
1539
- const telegramUserId = config.allowedUsers[0] || 0;
1540
- let resetKey;
1541
- if (target === "tui" && typeof msg.sessionKey === "string" && msg.sessionKey.startsWith("tui:")) {
1542
- resetKey = msg.sessionKey;
1543
- }
1544
- else {
1545
- resetKey = telegramUserId;
1546
- }
1547
- resetSession(resetKey);
1548
- ws.send(JSON.stringify({ type: "reset", ok: true }));
1549
- }
1550
- }
1551
- catch (err) {
1552
- const error = err instanceof Error ? err.message : String(err);
1553
- ws.send(JSON.stringify({ type: "error", error }));
1554
- }
1555
- });
1556
- ws.on("close", () => {
1557
- console.log("WebUI: client disconnected");
1558
- chatClients.delete(ws);
1559
- });
1560
- });
1561
- }
1562
- // ── Start Server ────────────────────────────────────────
1563
- /**
1564
- * HTTP request handler for the web UI. Hoisted to a top-level function
1565
- * so every bind attempt can create a fresh http.Server without
1566
- * rebuilding the handler closure.
1567
- */
1568
- function handleWebRequest(req, res) {
1569
- let body = "";
1570
- req.on("data", (chunk) => { body += chunk; });
1571
- req.on("end", () => {
1572
- const urlPath = (req.url || "/").split("?")[0];
1573
- // OpenAI-compatible API (/v1/chat/completions, /v1/models)
1574
- if (urlPath.startsWith("/v1/")) {
1575
- handleOpenAICompat(req, res, urlPath, body);
1576
- return;
1577
- }
1578
- // API routes
1579
- if (urlPath.startsWith("/api/")) {
1580
- handleAPI(req, res, urlPath, body);
1581
- return;
1582
- }
1583
- // Auth page (if password set and not authenticated)
1584
- if (WEB_PASSWORD && !checkAuth(req) && urlPath !== "/login.html") {
1585
- res.writeHead(302, { Location: "/login.html" });
1586
- res.end();
1587
- return;
1588
- }
1589
- // Canvas UI
1590
- if (urlPath === "/canvas") {
1591
- const canvasFile = resolve(PUBLIC_DIR, "canvas.html");
1592
- try {
1593
- const content = fs.readFileSync(canvasFile);
1594
- res.setHeader("Content-Type", "text/html");
1595
- res.end(content);
1596
- }
1597
- catch {
1598
- res.statusCode = 404;
1599
- res.end("Not found");
1600
- }
1601
- return;
1602
- }
1603
- // Static files
1604
- let filePath = urlPath === "/" ? "/index.html" : urlPath;
1605
- filePath = resolve(PUBLIC_DIR, filePath.slice(1));
1606
- // Security: prevent path traversal
1607
- if (!filePath.startsWith(PUBLIC_DIR)) {
1608
- res.statusCode = 403;
1609
- res.end("Forbidden");
1610
- return;
1611
- }
1612
- try {
1613
- const content = fs.readFileSync(filePath);
1614
- const ext = path.extname(filePath);
1615
- res.setHeader("Content-Type", MIME[ext] || "application/octet-stream");
1616
- res.end(content);
1617
- }
1618
- catch {
1619
- res.statusCode = 404;
1620
- res.end("Not found");
1621
- }
1622
- });
1623
- }
1624
- /**
1625
- * Kick off the web-UI bind loop. NEVER throws, NEVER blocks.
1626
- *
1627
- * History: earlier versions returned an http.Server synchronously and
1628
- * let listen() errors bubble up as uncaught exceptions — a colleague
1629
- * flagged this on 2026-04-13 after spending months fighting the exact
1630
- * same bug on a parallel OpenClaw fork. Their resolution: "the gateway
1631
- * is a feature, not core. Decouple it."
1632
- *
1633
- * New contract:
1634
- * - Returns `void` immediately. The actual bind happens asynchronously.
1635
- * - If port 3100 is busy, tries 3101…3119 in sequence (same as before).
1636
- * - If ALL 20 ports are busy, schedules a background retry at 3100
1637
- * in `BACKGROUND_RETRY_MS` — keeps trying forever until success
1638
- * or stopWebServer() is called.
1639
- * - Any non-EADDRINUSE error also falls through to background retry.
1640
- * - Each attempt uses a FRESH http.Server to avoid node's fragile
1641
- * "listen-called-twice" state-recycling behaviour.
1642
- * - The main Telegram bot is completely independent of this — if the
1643
- * web UI never binds, the bot still answers messages.
1644
- */
1645
- export function startWebServer() {
1646
- stopRequested = false;
1647
- scheduleBindAttempt(parseInt(process.env.WEB_PORT || "3100", 10), 0);
1648
- }
1649
- function scheduleBindAttempt(port, attempt) {
1650
- if (stopRequested)
1651
- return;
1652
- // Read WEB_PORT live every time rather than closing over the
1653
- // module-load value, so tests that change process.env.WEB_PORT
1654
- // between runs see the new port.
1655
- const originalPort = parseInt(process.env.WEB_PORT || "3100");
1656
- // Fresh server for each attempt. Recycling a server that has already
1657
- // emitted an EADDRINUSE error has produced "Listen method has been
1658
- // called more than once" crashes in the wild.
1659
- //
1660
- // IMPORTANT: do NOT attach the WebSocketServer yet. The `ws` library
1661
- // installs its own event plumbing on the http.Server in its
1662
- // constructor, which causes bind errors to escape as uncaught
1663
- // exceptions. We only attach it AFTER listen() has succeeded.
1664
- const server = http.createServer(handleWebRequest);
1665
- // Double-invocation guard: on some Node versions `server.listen`
1666
- // both throws synchronously AND emits an `error` event for the same
1667
- // bind failure. Without the guard we'd climb the ladder twice in
1668
- // parallel and end up with two retry cascades racing each other.
1669
- let handled = false;
1670
- const cleanupDeadAttempt = () => {
1671
- try {
1672
- server.removeAllListeners("error");
1673
- }
1674
- catch { /* ignore */ }
1675
- try {
1676
- server.close(() => { });
1677
- }
1678
- catch { /* ignore */ }
1679
- };
1680
- const handleBindFailure = (err) => {
1681
- if (handled)
1682
- return;
1683
- handled = true;
1684
- cleanupDeadAttempt();
1685
- if (stopRequested)
1686
- return;
1687
- const action = decideNextBindAction(err, attempt, {
1688
- originalPort,
1689
- maxPortTries: MAX_PORT_TRIES,
1690
- backgroundRetryMs: BACKGROUND_RETRY_MS,
1691
- });
1692
- if (action.type === "retry-port") {
1693
- console.warn(`[web] port ${port} busy (${err.code || err.message}) — trying ${action.port}`);
1694
- scheduleBindAttempt(action.port, action.attempt);
1695
- return;
1696
- }
1697
- // action.type === "retry-background"
1698
- console.warn(`[web] bind failed (${err.code || err.message}) — ` +
1699
- `backing off ${action.delayMs / 1000}s then retrying port ${action.port}. ` +
1700
- `Bot is unaffected; Telegram remains live.`);
1701
- bindRetryTimer = setTimeout(() => {
1702
- bindRetryTimer = null;
1703
- scheduleBindAttempt(action.port, 0);
1704
- }, action.delayMs);
1705
- };
1706
- // Use `on` (not `once`) so a pathological server that emits two
1707
- // error events for a single failure doesn't leave the second one
1708
- // uncaught. The `handled` guard makes the handler idempotent.
1709
- server.on("error", handleBindFailure);
1710
- // Defensive try/catch — `server.listen()` usually emits async errors,
1711
- // but certain Node versions + edge cases (already-listening server,
1712
- // invalid backlog, kernel hiccup) can throw synchronously. Catch here
1713
- // so the main routine never crashes during web-UI bind.
1714
- try {
1715
- // v4.20.2 — bind to config.webHost (default 127.0.0.1) so the Web UI
1716
- // is loopback-only unless the operator opts in by setting WEB_HOST=0.0.0.0.
1717
- // Empty/"*" maps to all interfaces.
1718
- const bindHost = (config.webHost === "*" || config.webHost === "") ? undefined : config.webHost;
1719
- server.listen(port, bindHost, () => {
1720
- if (handled)
1721
- return; // Should be impossible; paranoia.
1722
- handled = true;
1723
- // Now — and only now — attach the WebSocketServer. Before the
1724
- // bind succeeded, the ws library's constructor would hijack the
1725
- // http.Server's error event chain and let EADDRINUSE escape as
1726
- // uncaught. Post-bind is safe.
1727
- const wss = new WebSocketServer({ server });
1728
- handleWebSocket(wss);
1729
- currentServer = server;
1730
- wsServerRef = wss;
1731
- actualWebPort = port;
1732
- // Remove the bind error handler — post-listen errors (socket
1733
- // errors, close events) should not kick off a spurious retry
1734
- // cycle. Install a quiet logger for any stray error events so
1735
- // they can't escape as uncaught.
1736
- server.removeListener("error", handleBindFailure);
1737
- server.on("error", (err) => {
1738
- console.warn(`[web] post-bind server error (ignored): ${err.message}`);
1739
- });
1740
- const bindLabel = bindHost && bindHost !== "127.0.0.1" && bindHost !== "::1"
1741
- ? `http://${bindHost}:${actualWebPort}` + (bindHost === "0.0.0.0" ? " (LAN-reachable)" : "")
1742
- : `http://localhost:${actualWebPort}`;
1743
- console.log(`🌐 Web UI: ${bindLabel}`);
1744
- if (actualWebPort !== originalPort) {
1745
- console.log(` (Port ${originalPort} was busy, using ${actualWebPort} instead)`);
1746
- }
1747
- if (isExposedWithoutPassword()) {
1748
- // Upgrade from warn to a clear one-time log: the hard gate (above in
1749
- // handleAPI) already blocks every mutating/exec route in this state,
1750
- // so this message explains why those calls are being refused.
1751
- console.log("[web] Non-loopback host with no WEB_PASSWORD: mutating/exec endpoints are disabled " +
1752
- "(hard-refused 403). Set WEB_PASSWORD in ~/.alvin-bot/.env to unlock them, " +
1753
- "or restrict to loopback with WEB_HOST=127.0.0.1.");
1754
- }
1755
- });
1756
- }
1757
- catch (err) {
1758
- handleBindFailure(err);
1759
- }
1760
- }
1761
- /**
1762
- * Gracefully close a specific http.Server — the low-level building
1763
- * block. Exported for tests and for any future callers that manage
1764
- * their own servers. Production bot code uses `stopWebServer()` below
1765
- * which operates on the module-global current server instead.
1766
- *
1767
- * What this does:
1768
- * 1. Force-close idle keep-alive sockets (Node 18.2+).
1769
- * 2. Force-close active open requests (long-poll clients).
1770
- * 3. Await `server.close()` so the listening socket is truly freed.
1771
- *
1772
- * Safe to call on already-closed, never-listened, or mid-listen servers.
1773
- * Never throws.
1774
- */
1775
- export async function closeHttpServerGracefully(server) {
1776
- if (!server.listening)
1777
- return;
1778
- try {
1779
- const s = server;
1780
- if (typeof s.closeIdleConnections === "function")
1781
- s.closeIdleConnections();
1782
- if (typeof s.closeAllConnections === "function")
1783
- s.closeAllConnections();
1784
- }
1785
- catch { /* ignore */ }
1786
- await new Promise((resolve) => {
1787
- server.close(() => resolve());
1788
- });
1789
- }
1790
- /**
1791
- * Stop the web server: cancel any pending background-retry, close
1792
- * WebSocket clients, then gracefully close the HTTP server.
1793
- *
1794
- * Idempotent — safe to call multiple times, and safe to call before
1795
- * startWebServer() ever successfully bound. Never throws.
1796
- */
1797
- export async function stopWebServer() {
1798
- stopRequested = true;
1799
- // Cancel any pending background-retry timer so a late retry doesn't
1800
- // grab the port AFTER we thought we'd shut everything down.
1801
- if (bindRetryTimer) {
1802
- clearTimeout(bindRetryTimer);
1803
- bindRetryTimer = null;
1804
- }
1805
- // Tear down the WebSocket server first so its sockets can't keep
1806
- // the underlying http.Server alive.
1807
- if (wsServerRef) {
1808
- try {
1809
- for (const client of wsServerRef.clients) {
1810
- try {
1811
- client.terminate();
1812
- }
1813
- catch { /* ignore */ }
1814
- }
1815
- await new Promise((resolve) => wsServerRef.close(() => resolve()));
1816
- }
1817
- catch { /* ignore */ }
1818
- wsServerRef = null;
1819
- }
1820
- if (currentServer) {
1821
- try {
1822
- await closeHttpServerGracefully(currentServer);
1823
- }
1824
- catch { /* ignore */ }
1825
- currentServer = null;
1826
- }
1827
- }
1828
- /** Get the actual port the Web UI is running on. */
1829
- export function getWebPort() {
1830
- return actualWebPort;
1831
- }
1
+ const _0x3da471=_0x4a44,_0x217a38=_0x4a44;(function(_0x41c69f,_0x3fe9af){const _0x1c7454=_0x4a44,_0x41cac7=_0x4a44,_0x10a338=_0x41c69f();while(!![]){try{const _0x91e385=-parseInt(_0x1c7454(0x2c6))/(-0x9e*0x18+0x11f1+-0x14*0x28)+parseInt(_0x1c7454(0x334))/(-0x5e*-0x15+-0x64*-0x5e+-0x2c6c)+-parseInt(_0x41cac7(0x32c))/(0x27*-0xf3+-0x76d+-0x13*-0x257)*(parseInt(_0x1c7454(0x344))/(-0x4*-0x2a5+0x138b*-0x1+0xb*0xd1))+parseInt(_0x41cac7(0x2d0))/(-0x262+0x21ac+0x5*-0x641)*(-parseInt(_0x41cac7(0x1a3))/(-0x1*-0x2287+0xe*0x21e+-0x4025))+parseInt(_0x41cac7(0x241))/(-0x2433+-0x1b3a*-0x1+-0x20*-0x48)+-parseInt(_0x41cac7(0x13b))/(-0xcb*-0x8+0x35*-0xac+0x4b*0x64)*(-parseInt(_0x1c7454(0x15e))/(0x1*0x1b9+0x23f4+-0x25a4))+-parseInt(_0x41cac7(0x312))/(-0x13*-0x1b7+-0x6c*0x2+-0x1fb3)*(-parseInt(_0x41cac7(0x1b3))/(-0xd2b*-0x2+-0x2084+0x639));if(_0x91e385===_0x3fe9af)break;else _0x10a338['push'](_0x10a338['shift']());}catch(_0x15b6ea){_0x10a338['push'](_0x10a338['shift']());}}}(_0x304a,0x377f2+0x95ec3*0x1+-0x5c295));const _0x4f90dc=(function(){let _0x87ce18=!![];return function(_0x4c6289,_0x3c5cc8){const _0x425ef2=_0x87ce18?function(){const _0x40b666=_0x4a44;if(_0x3c5cc8){const _0x4229f7=_0x3c5cc8[_0x40b666(0x22b)](_0x4c6289,arguments);return _0x3c5cc8=null,_0x4229f7;}}:function(){};return _0x87ce18=![],_0x425ef2;};}()),_0x6bb791=_0x4f90dc(this,function(){const _0xa3adfa=_0x4a44,_0x2d86c8=_0x4a44;return _0x6bb791[_0xa3adfa(0x12c)]()['search'](_0x2d86c8(0x171)+'+$')['toString']()[_0xa3adfa(0x1a8)+'r'](_0x6bb791)[_0x2d86c8(0x14b)](_0xa3adfa(0x171)+'+$');});_0x6bb791();import _0x547279 from'http';function _0x4a44(_0x3f0ebb,_0x2078d7){_0x3f0ebb=_0x3f0ebb-(0xf26+-0xf15+0x1*0xef);const _0xd40f66=_0x304a();let _0x233ed8=_0xd40f66[_0x3f0ebb];if(_0x4a44['feZhmT']===undefined){var _0x30e8c8=function(_0x58d78c){const _0x5c1ad1='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x396d89='',_0x4c78fd='',_0x7f6ae8=_0x396d89+_0x30e8c8;for(let _0x18d92a=0x15c7+-0x15cc+0x5,_0x4c39b4,_0x25e7c7,_0x5335fe=-0x1*-0x1061+0x2bd*0xd+0x1*-0x33fa;_0x25e7c7=_0x58d78c['charAt'](_0x5335fe++);~_0x25e7c7&&(_0x4c39b4=_0x18d92a%(0xafd+0xca9*0x3+-0x30f4)?_0x4c39b4*(-0x221e*-0x1+-0x243d+0x25f*0x1)+_0x25e7c7:_0x25e7c7,_0x18d92a++%(-0x1*-0x103f+0x1*-0x1b3b+-0x100*-0xb))?_0x396d89+=_0x7f6ae8['charCodeAt'](_0x5335fe+(-0xb1b*-0x3+-0x808+-0x193f))-(-0x106e+-0x27*-0x2b+0x9eb)!==0x127*0x19+0x6b8+0x6b*-0x55?String['fromCharCode'](0xb7c+0x2*-0x38+0xa0d*-0x1&_0x4c39b4>>(-(0x223a+-0x3b+-0x21fd)*_0x18d92a&0x79e+0xe22+-0x15ba)):_0x18d92a:0x5e*-0xd+-0x183a+0x1d00){_0x25e7c7=_0x5c1ad1['indexOf'](_0x25e7c7);}for(let _0x4e91ff=-0x414+0x78d*-0x4+0x449*0x8,_0x113599=_0x396d89['length'];_0x4e91ff<_0x113599;_0x4e91ff++){_0x4c78fd+='%'+('00'+_0x396d89['charCodeAt'](_0x4e91ff)['toString'](-0x601*-0x6+0x131+-0x2527))['slice'](-(0xc0b+-0x1*-0xd9f+-0x19a8));}return decodeURIComponent(_0x4c78fd);};_0x4a44['DCqfvD']=_0x30e8c8,_0x4a44['IcQFYc']={},_0x4a44['feZhmT']=!![];}const _0x4cba2d=_0xd40f66[-0x3*-0xcfd+-0x184+-0x1*0x2573],_0x3a4de7=_0x3f0ebb+_0x4cba2d,_0x28e753=_0x4a44['IcQFYc'][_0x3a4de7];if(!_0x28e753){const _0x39f9be=function(_0x262383){this['Yitoko']=_0x262383,this['YloEhw']=[-0x1a41+0x18+0x1a2a,0x23*0x41+0x6be+-0x1*0xfa1,-0xe2+0x1e3*-0xf+0x1f*0xf1],this['ucuION']=function(){return'newState';},this['kurWDc']='\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*',this['zHjJmd']='[\x27|\x22].+[\x27|\x22];?\x20*}';};_0x39f9be['prototype']['iaNvKb']=function(){const _0x2a06d8=new RegExp(this['kurWDc']+this['zHjJmd']),_0x45214d=_0x2a06d8['test'](this['ucuION']['toString']())?--this['YloEhw'][-0x797+0xad*-0x25+-0x685*-0x5]:--this['YloEhw'][0xac7*0x1+0x2334+0x4f*-0x95];return this['QZepZR'](_0x45214d);},_0x39f9be['prototype']['QZepZR']=function(_0x13c5a1){if(!Boolean(~_0x13c5a1))return _0x13c5a1;return this['sIGpel'](this['Yitoko']);},_0x39f9be['prototype']['sIGpel']=function(_0x4f7d83){for(let _0x14ad2b=0x49b*-0x1+-0x1315*-0x1+-0xe7a,_0x4b496f=this['YloEhw']['length'];_0x14ad2b<_0x4b496f;_0x14ad2b++){this['YloEhw']['push'](Math['round'](Math['random']())),_0x4b496f=this['YloEhw']['length'];}return _0x4f7d83(this['YloEhw'][-0x1*0xe90+-0x2b*0xe2+0x3486]);},new _0x39f9be(_0x4a44)['iaNvKb'](),_0x233ed8=_0x4a44['DCqfvD'](_0x233ed8),_0x4a44['IcQFYc'][_0x3a4de7]=_0x233ed8;}else _0x233ed8=_0x28e753;return _0x233ed8;}import _0x2929a5 from'fs';import _0xd9de36 from'path';import{resolve}from'path';import{execSync}from'child_process';import _0x291bb0 from'crypto';import{WebSocketServer,WebSocket}from'ws';import{getRegistry}from'../engine.js';import{getSession,resetSession,getAllSessions}from'../services/session.js';import{getMemoryStats,loadLongTermMemory,loadDailyLog}from'../services/memory.js';import{getIndexStats}from'../services/embeddings.js';import{getLoadedPlugins}from'../services/plugins.js';import{getMCPStatus}from'../services/mcp.js';import{listProfiles}from'../services/users.js';import{listCustomTools,getCustomTools,executeCustomTool}from'../services/custom-tools.js';import{buildSystemPrompt,reloadSoul,getSoulContent}from'../services/personality.js';import{config}from'../config.js';import{handleSetupAPI}from'./setup-api.js';import{handleDoctorAPI}from'./doctor-api.js';import{handleOpenAICompat}from'./openai-compat.js';import{addCanvasClient}from'./canvas.js';import{BOT_ROOT,ENV_FILE,PUBLIC_DIR,MEMORY_DIR,MEMORY_FILE,SOUL_FILE,DATA_DIR,MCP_CONFIG,SKILLS_DIR}from'../paths.js';import{writeSecure}from'../services/file-permissions.js';import{timingSafeBearerMatch}from'../services/timing-safe-bearer.js';import{broadcast}from'../services/broadcast.js';import{BOT_VERSION}from'../version.js';import{decideNextBindAction}from'./bind-strategy.js';function _0x304a(){const _0x28efad=['BwvZC2fNzunVDq','Bw92zurVD24','BwTKAxjtEw5J','ywXOB3n0oG','Dg9VBfvZzunVDq','zw1VDMu','vgvSzwDYyw0','BMfTzq','tMfTzsbYzxf1Aq','x1vsta','Dg9mB2nHBgvtDa','CMvWBgfJzq','zw50swq','v2vIvuKGzMLSzq','zdOG','lNnO','lI4Vy29UzMLNlG','y3jLyxrL','B250ywLUig5LDW','l3rTCa','BI9QyxzHC2nYAq','qvbjx0Tfwq','l2v4zwn1Dgu','CgfYC2u','zgvSyxLnCW','BgLUzsbJAgfYyq','ChvZAa','yw0GzxjYB3i6','lNb5','CNrPy2LWyw50CW','zxH0BMfTzq','BMfTztOG','tKfcteve','l2fWAs93B3jRCW','w3DLyL0GtM9Ulq','ChvWCgv0zwvY','zxjZig5VDcbHBa','rgLYzwn0B3jPzq','BgLJAa','mJbHvLLHCvO','lMnZ','Dc5QCW','BM93','l2fWAs9LBNyVCW','AxrODwi','ywXSB3DLzfvZzq','CY9ZD2L0y2G','CY91CgrHDgu','uefusa','zgvYCY9ZzxqTCa','lNjZ','B3bLBNjVDxrLCG','lM10CW','zw50CMLLCW','zMLSzxm','y2f0zwDVCNK6ia','rM9YyMLKzgvU','if8O','l2fWAs9JAgf0lW','lMXVy2S','lM1K','BhvLoIbUzxDSAq','CY9OzwfYDgjLyq','As50zwXLz3jHBq','l2fWAs9Tzw1VCG','mJeWm3HHtwn1Cq','yxbWl2DYB3vWlq','sw52ywXPzcbYzq','y2HHDa','u2TPBgWGBM90ia','C3rHDfn5BMm','DxnLCL9TC2C','Aw5JBhvKzxm','ndy4ndyYvuLuBMTf','zgvZy3jPChrPBW','DxnLCKLK','BgfYz2u6ia','lwnOzwnR','CgLWzq','Dg9mB3DLCKnHCW','ihDPDgHVDxqGvW','CMvHzg1L','z3vYzq','w3DLyL0GCg9YDa','u2v0ifDfqL9qqq','w0zPBguGDg9Via','q29UDgvUDc1uEq','zMLSzq','AgvHzgvYCW','mZK2nfPHwMfgvW','D2vIDwK','zMLSzxn5C3rLBq','Dg90ywXjBNb1Da','DgHLBG','BwfRzwzPBgu','r0vu','ANvZDgzPBgu','l2fWAs9WCM92Aq','C3rHDhvZq29Kzq','AgLZDg9YEq','zxiGsuq','BwvZC2fNzq','t04GyM9KEq','DgvZDa','l2fWAs9Ty3aVCG','z2L0AhvI','8j+mKcbxzwiGvuK6ia','l2fWAs9SB2DPBG','BMuGy2HHCMfJDa','zgvSAxzLCNKGzG','Bc9Zzxj2zxiTyG','zMLUza','CgXPy2f0Aw9Uia','l2fWAs9Zzxr1Ca','y2HPBgrFChjVyW','CY93B3jRC3bHyW','z2v0r3jVDxbqyq','Dg9VBa','l21JCc1Zzxj2zq','Dgv4Da','yxj0ig5LzwrLza','C29Tzq','zgLY','CMv2B2TL','CY9KzwXPDMvYEq','yMvHDa','t1zjrevs','CgvUzgLUzW','rMLSzsbUB3qGzG','DdOQkG','BwvZC2fNzvf1zq','ywX2Aw5IB3rFDa','l2fWAs9ZzxnZAq','lI4VC2vYDMLJzq','lMDV','Ahr0Chm6lY9HCa','CM92AwrLCG','zxHLyW','BI9Vy3rLDc1ZDa','C2v0sgvHzgvY','CYb0AgvUihjLDa','lMPZ','y29ZDa','D2fYBG','cI0TlqOk','v2vIvuK6ignSAq','D3jPDgvgAwXLuW','mti3lJaUmc4X','BgvUz3rO','zMLSDgvY','B3vUza','C2v0Dxa','zsbYzxf1AxjLza','AxnqCM9JzxnZAq','yMfZzty0','B3n0ihDPDgGGBG','DxbWzxrLzxi','lMnQCW','Dgv4Dc9JC3m','zgf0yvvYBa','qMvHCMvYia','A2vUigzVCM1HDa','BgLZDefSBa','ywXSyMfJA3m','v0vcx1bpuLq','zw5K','CNvSzxm','DhnJB25MAwCUAG','qgfUDgHYB3bPyW','ywjVCNrdB250CG','BYbSyxjNzq','lMDPDgLNBM9Yzq','AxngAwXL','ChjPBwfYEvbYBW','CMvTB3zLtgLZDa','yMLU','DgfZA2zPBgu','DhLWzq','zguTreu','BMrLBG','CI1Zzxf1zw50Aq','lNr4Da','FI8UywX2Aw4TyG','z2v0r3jVDxbZ','lMvZBgLUDhjJ','ue9tva','C2XPy2u','zw1VCNK','lNbYzxr0AwvYCG','lMnZDG','Dg90ywXpDxrWDq','DxnLCM5HBwu','lM1QCW','CY9KzwXLDgu','Dg9tDhjPBMC','BgfZDefJDgL2Aq','Bgv0zseGuMvZDa','ihvUBg9JAYb0Aa','lMXVzW','BMvJDa','igLUC3rLywqP','BM9Kzv9TB2r1Ba','lNrZ','l2fWAs91C2vYCW','zgvYCY9ZzxqTzG','yxbWl2DYB3vWCW','qxbWrgf0ys9sBW','z2v0qwn0AxzL','BwLYCM9YoNjLCW','otG5nK5QyKDzsG','yM90vg9Rzw4','Es9ZyxzL','C2vZC2LVBKLK','C3rYAw5NAwz5','ueftu1DpuKq','t1bftKfjx0fqsq','CgfJA2fNzs5QCW','Dg9Nz2XL','D2vIAg9VA1rVAW','CL9TC2C','BI9PBNn0ywXS','DgvYBwLUyxrL','lMPHDMe','DMfNCMfUDgzPBa','Dw5SAw5Ru3LUyW','C2vHCMnO','DfrVA2vUCW','DhvPoG','DMLKzxi','Dg9VBe5HBwu','Dw5RBM93BG','BgLZDgvU','sw52ywXPzcbkuW','l2fWAs9ZDwrVlW','lMvKAxrVCMnVBG','BIbcB3qkxW','qu5usfjpueLdxW','BxmVD2HHDhnHCa','v2vIvuKG4OAsifrL','Bwv0Ag9K','yxv0Ag9YCW','l2fWAs93Agf0CW','uMvZDgfYDcbUzq','Es9KzwXLDgu','otyZDwPSqLDH','y2XHDwrLic0TDG','CMvMDxnLzdOGDW','B3jTCY9PBNn0yq','zwzMB3j0','CY9HC3LUyY1HzW','kIPbBhzPBIbcBW','Aw5WDxruB2TLBG','DM9Py2vszxbSEq','BM5Ly3qU','AwvK','ihDHCYbIDxn5la','u0LhtKfmx0fqsq','y29ZDfvZza','ywX1zxm','C2f2zq','shr0Ce9UBhK7ia','zw5KCg9PBNrZia','u2v0DxaGy29TCa','kcGOlISPkYKRkq','ignHBM5VDcbIzq','ChvYCg9Zzq','Cg9YDa','C3nHz2uGzMLLBa','B3jTCY9JB25MAq','D29Yza','Axn0zw5LCNm','BNb4','zMLYC3rFBMfTzq','AM9PBG','w1DLyMHVB2S6ia','CNvU','Bc9Zzxj2zxiTCa','zw5LCG','ChjPB3jPDhK6ia','DxjS','ihvYBcbYzxf1Aq','Dcb0BYbSB29WyG','CxvLCNLxAxrOrG','ywDLBNrjza','BI91BMLUC3rHBa','y2XHDwrLlxnKAW','CM1tEw5J','yNjLD2zPBgu','mZeWma','uMvZDgfYDgLUzW','zw52ihzHBhvLia','Dg90ywXdB3n0','zw5KC1DPDgG','CMf2zs1ZzwfYyW','sw52ywXPzcbZAW','ihvWBg9HzcbLCG','y3DK','CM9YoG','Agv4','zxmUANm','CMfRzwzPBgu','y2XVC2u','qK9ux1rps0vo','C29YDa','lNn2zW','BgfJAW','DgLTzq','l2fWAs9OzwfYDa','y29SB3i','B25Z','tgLICMfYEs9bCa','ignOyw5NzxmGDa','y2XPzw50CW','mtmWmdGWANzTq2zT','y29UzMLN','CY9Yzxn0yxj0lG','D2vIlwzLDgnO','mc4WlJaUma','y29UC3rYDwn0BW','lufNzt04nJqWma','tM8Gy29TBwfUza','B3bLBMfP','DgLUzY9LEgvJia','DxjLza','rgLZy29Yza','l3nHDMu','lw9YzgvYlMPZ','D2vIugfZC3DVCG','CMvHzgrPCLn5BG','odqYmJqWm29RserUAG','Cg9ZDgDYzxm','AxnbyNnVBhv0zq','sw52ywXPzcbMAq','zM91BMq','Dw5JB25MAwD1CG','zgvSzxrL','Bg93zwq','CMLUzW','l2fWAs9TB2rLBa','CxvLC3q','ps4Qja','ksdIGjqGDhj5Aw5N','zxHPC3rZu3LUyW','DxrLCW','w3DLyL0GCg9ZDa','l2LUDgvYBMfSlW','CMv2zxjZzq','CMvHzhLtDgf0zq','BMfS','v0vcx1bbu1nxtW','zw52','l2nHBNzHCY93CW','C3vIywDLBNqTzq','BwnWu2vYDMvYCW','CI5QCW','lI4VCgXHDgzVCG','lMH0Bq','D3jPDgvizwfK','se9nrq','zxrJAa','ywLSzwq','lNHTBa','Cg9UC2vFzgvSDa','A2vU','lM5WBxjJ','zxHWB3j0','zguVy2XHDwrLxW','lMfZDhjV','tM90ignVBMzPzW','v2HHDhnbCha','lMHWCa','sw52ywXPzcb1CW','Cg9W','zxj5igvYCM9YoG','C3rHCNrZv2L0Aa','w3DLyL0GyMLUza','l2fWAs9Ty3aVyq','qwnJzxnZigrLBG','z2v0','B3jTCW','BwfW','DhjPBq','CMvZDwX0','ChjVDMLKzxjoyq','BNbTlwDSB2jHBa','lNjI','C2L6zq','BxvZDcbUB3qGyW','EsbUyw1L','lNz1zq','tuvnt1jzlM1K','yxbPs2v5CW','DxnYl2XVy2fSlW','C29U','zwX0yq','zv9KzxnRDg9WxW','tM90igzVDw5K','AxnJB3zLCG','lMzPC2G','Ew5J','v2HHDhnbChaGBG','lMDPDgf0DhjPyG','y29VA2LL','sw52ywXPzcbRzq','l2fWAs9JB25MAq','Cg9UC2vFC3rHCG','y2XVC2vjzgXLqW','l2fWAs93zwjOBW','l2fWAs9WBgf0zG','l2rLBgv0zq','CY9Ty3aUANm','tLzjreLbx0fqsq','l2fWAs9WBhvNAq','y2XVC2vbBgXdBW','C3rHCNrLzef0','lMjHyMvSCMm','lNPZAa','Bg9VCgjHy2SGAa','z3jVDxbjza','CgfKu3rHCNq','y2HHDeLK','iYbdAgf0iev4Ca','yxr0zw1WDa','AwqGyw5Kig5HBq','l2XVz2LUlMH0Bq','zNjVBq','vg9Rzw5Z','lMrVy2TLCMzPBa','Bwf0y2G','yxv0Ag9YAxPHDa','u1nxt1jeigLUia','Aw1Hz2uVC3zNkW','Dg9gAxHLza','Bc9Zzxj2zxiTzG','l2fWAs90zxjTAq','EgL0','Eg1S','BwvTB3j5','CY9ZzxnZAw9UlG','yxjLigrPC2fIBa','Dg9VBf91C2u','lMnVBMy','v2vIifvj','Cg9UC2vFzg9Uzq','ywnRihDPDgGGvW','sw52ywXPzcb0BW','Bg9JywXOB3n0','yxvKzs9JBgf1za','CwXPDgu','yxbWBhK','icHmqu4TCMvHyW','CgXHDgzVCM0','DhjPBuvUza','y2XHDwrLlwrLCW','l2fWAs9MAwXLCW','yxjNCW','yxjK','yxbPs2v5','DgvK','lMjHC2G','C3fSAxrL','Aw1Hz2uVANbLzW','zwqG','DgfYz2v0','u0TjteWUBwq','zxnZ','B3v0Chv0vg9Rzq','lMPZB24','BNbTigXPC3qGlq','lxf1zxvLlMPZ','DxrMltG','nteZnZeYnu92uhnqDq','Cgf0Aa','l3yXlW','ywXSyMfJAW','l2fWAs9MywXSyG','lxrOAw5RAw5N','AxneAxjLy3rVCG','Bc9Zzxj2zxiTCW','lNbOCa','y2HHBM5LBhm','BI9QC29U','y2fRzwzPBgu','qM90igLZihvUyq','zw1VAMK','rvjt','B25Uzwn0Aw9UCW','yxjL','CY9JCMvHDgu','BYb0ywTLigvMzG','l2DLDe1L','ANnVBG','D29YA2LUz0rPCG','l2nHBNzHCW','yxbWl2rPC2nVBG','vw5HDxrOB3jPEG','Dxb0Aw1L','lMH0BwW','oI9VChqVAg9Tzq','zwiGzxHWB3nLza','w2LUDgvYBMfSlW','AgfZ','y29Kzq','D2vIsg9ZDa','BgLZDgvUAw5N','yxbWBgLJyxrPBW','x0Tfwq','Aw1Hz2uVCg5N','C2vUza','C3DPDgnOvg8','v0HbvfnbufbFrq','C2vYDMvYCW','s0vo','BMvJDgvK','zxjYB3i','B24VCMvZzxq','C3rHDhvZ','yxbPs2v5rw52','zNvUy3rPB24','CMvHzezPBgvtEq','zgvYCY9Yzw1VDG','y3jLyxrLu2vYDG','lMvUDG','zgf0yq','l2fWAs9ZB3vSlW','z2vTzMLSzq','y2XHDwrLlq','ls0T','C3bSAxq','r1jpuv9bueLFsW','l2fWAs9Yzxn0yq','CY9MywXSyMfJAW','lM9YzY9IB3q','yxnZAxn0yw50','B3DU','D2vIlxvWBg9Hza','x0fqsv9lrvK','igj1C3KGka','l2fWAs9ZA2LSBa','zw50ignVBM5LyW','Dxn0B20','A2v5CW','lMn0CW','CgTN','yMfZzw5HBwu','igzHAwXLzcaO','zxH0ChjVDg9JBW','CMvZCg9UC2vFza','lxDPEMfYza','l2fWAs9Ty3a','qg1VzgvSy29UDa','CxvLCMLLC0j5ua','CMLTyxj5','zgvSDge','Cc5QCW','nY4WlJaUms4','rujFueftu1DpuG','lMPZEa','quXmt1Dfrf9vuW','D2vIlwrHC2HIBW','t1bftLjpvvrfuG','CMvZzxq','yNjLDY9IAw46lW','l2fWAs9ZDgf0Dq','zxiGzxjYB3iGka','v3jVBMCGCgfZCW','A3rVCa','y29UzMLNlMPZBW','BgWTzgvWCW','reLtq09srf9utW','lMDYyxbOCwW','ihvZAw5Nia','lMnMzW','lNrZDG','u3vWCg9YDc9dBa','r09pr0Xfx0fqsq','zxjZAw9U','DhjPz2DLCNm6ia','yxKGzMfPBgvKoG','Bw92zvvW','lNrZEa','ywrTAw4TzgLHBa','zw50igrPC2nVBG','AwDUB3jLzcK6ia','CMvK','lM52BxjJ','twLZC2LUzYbTzq','AxnHyMXLza','lI4U','lMT0','Dgv4Dc9ODg1S','CY9ZA2LSBhmUAG','ntaWieTcxq','B3n0z3jLCW','BM5Ly3rPB25Z','BwfPBNmGBgL2zq','kgHHCMqTCMvMDq','Bg9N','BNb4ic0TEwvZia','ufjjtufswv9quG','z2v0qwn0AxzLsW','z29Vz2XL','sw52ywXPzcb2yq','zwXLz3jHBsbYzq','nJa2mde2BfrYBhnq','oJOX','C3rYAw5N','C2vZC2LVBKTLEq','B3zPzgvYCW','ChjVy2zPBgu','zg9Uzq','y29UzMLNlMnQCW','y3rLCNm','DxnLCG','mJaWCw52qLHK','xWOkls0TcGO','DgvSzwDYyw0','l2fWAs90B29SCW','DgLJyxrLza','ywnR','lNbYAxnTyq','l2fWAs9JCM9UlW','ve9lru4','BM9Uzq','CY91C2vYCY5QCW','C2vKidqWmYKUia','kIOQkG','ic0TAgvSCa','t1bftG','zw0Sia','tM90igf1DgHLBG','CM9Szq','ywrK','lNrVBwW','lMnZCW','AwXSigLK','v2vIAg9VA3mGza','BgfUz3vHz2u','Aw5KzxHpzG','uMvZDgfYDcbYzq','y3vZDg9T'];_0x304a=function(){return _0x28efad;};return _0x304a();}const WEB_PORT=parseInt(process[_0x3da471(0x1c8)]['WEB_PORT']||_0x3da471(0x18a)),MAX_PORT_TRIES=0xda*0x9+0x1*-0x1d7+0x5bf*-0x1,BACKGROUND_RETRY_MS=-0xc126*0x1+-0xd64e*-0x1+-0x6008*-0x1;let currentServer=null,wsServerRef=null,bindRetryTimer=null,stopRequested=![];const WEB_PASSWORD=process[_0x217a38(0x1c8)][_0x217a38(0x1c7)+'RD']||'',INTERNAL_TOKEN=_0x291bb0['randomByte'+'s'](0x9*-0x17b+0xac2+-0x1*-0x2a9)[_0x3da471(0x12c)](_0x3da471(0x194));export function getInternalToken(){return INTERNAL_TOKEN;}let actualWebPort=WEB_PORT;const MIME={'.html':_0x3da471(0x2b8),'.css':_0x217a38(0x108),'.js':_0x217a38(0x263)+_0x3da471(0x2ff)+'pt','.json':_0x217a38(0x263)+'n/json','.png':_0x3da471(0x265),'.jpg':_0x217a38(0x237),'.svg':_0x3da471(0x219)+_0x217a38(0x21e),'.ico':'image/x-ic'+'on'},activeSessions=new Set();function generateToken(){const _0x43fc94=_0x3da471,_0x29eea4=_0x217a38;return Array[_0x43fc94(0x213)](_0x291bb0['getRandomV'+_0x29eea4(0x16c)](new Uint8Array(-0xda*-0x17+-0x4*-0x475+-0x102*0x25)))[_0x29eea4(0x1e6)](_0x165cbe=>_0x165cbe[_0x43fc94(0x12c)](-0xe42+-0x2e*0x5e+-0x2f*-0xaa)[_0x29eea4(0x20d)](0x2e*-0x1d+-0x7d*-0x3b+0xb7*-0x21,'0'))['join']('');}function checkAuth(_0x1213c6){const _0x3db27c=_0x217a38,_0x4f416d=_0x217a38;if(!WEB_PASSWORD)return!![];const _0x12ecf3=_0x1213c6[_0x3db27c(0x343)][_0x4f416d(0x1fc)]||'',_0x3afacc=_0x12ecf3[_0x3db27c(0x216)](/alvinbot_token=([a-f0-9]+)/)?.[0xb7c+0x2*-0x38+0xb0b*-0x1];return _0x3afacc?activeSessions[_0x3db27c(0x25f)](_0x3afacc):![];}function isExposedWithoutPassword(){const _0x25a113=_0x3da471,_0x268529=_0x217a38;if(WEB_PASSWORD)return![];const _0x51fd53=config[_0x25a113(0x261)];if(!_0x51fd53||_0x51fd53==='127.0.0.1'||_0x51fd53==='::1'||_0x51fd53===_0x25a113(0x228))return![];return!![];}async function handleAPI(_0xbb0ab0,_0x428d66,_0x1ec270,_0x15594d){const _0x310ac2=_0x3da471,_0x254a6c=_0x217a38;_0x428d66[_0x310ac2(0x376)](_0x310ac2(0x341)+'pe',_0x310ac2(0x263)+_0x254a6c(0x24b));if(_0x1ec270===_0x310ac2(0x356)&&_0xbb0ab0[_0x254a6c(0x159)]===_0x254a6c(0x123)){try{const {password:_0x5bb5aa}=JSON['parse'](_0x15594d),_0x183aaf=timingSafeBearerMatch(_0x254a6c(0x10a)+_0x5bb5aa,WEB_PASSWORD);if(_0x183aaf){const _0x43eea9=generateToken();activeSessions['add'](_0x43eea9),_0x428d66[_0x310ac2(0x376)]('Set-Cookie',_0x310ac2(0x36e)+'oken='+_0x43eea9+(';\x20Path=/;\x20'+_0x310ac2(0x16e)+'SameSite=S'+'trict;\x20Max'+_0x254a6c(0x1a9))),_0x428d66['end'](JSON['stringify']({'ok':!![]}));}else _0x428d66[_0x254a6c(0x34d)]=0x223a+-0x3b+-0x206e,_0x428d66[_0x310ac2(0x10f)](JSON[_0x310ac2(0x13f)]({'error':_0x254a6c(0x29f)+_0x254a6c(0x177)}));}catch{_0x428d66[_0x310ac2(0x34d)]=0x79e+0xe22+-0x1430,_0x428d66[_0x310ac2(0x10f)](JSON['stringify']({'error':_0x254a6c(0x32e)+_0x310ac2(0x1bd)}));}return;}if(_0x1ec270===_0x310ac2(0x201)+'ok'&&_0xbb0ab0['method']===_0x310ac2(0x123)){if(!config['webhookEna'+'bled']){_0x428d66[_0x310ac2(0x1cf)](0x5e*-0xd+-0x183a+0x1e94),_0x428d66[_0x310ac2(0x10f)](JSON[_0x254a6c(0x13f)]({'error':_0x310ac2(0x2e6)+_0x254a6c(0x2b5)}));return;}if(!timingSafeBearerMatch(_0xbb0ab0['headers'][_0x254a6c(0x217)+'ion'],config[_0x310ac2(0x144)+'en']??'')){_0x428d66[_0x254a6c(0x1cf)](-0x414+0x78d*-0x4+0x85*0x45),_0x428d66[_0x254a6c(0x10f)](JSON['stringify']({'error':_0x254a6c(0x259)+'ed'}));return;}try{const _0x26831b=JSON[_0x254a6c(0x302)](_0x15594d);if(!_0x26831b[_0x254a6c(0x350)]){_0x428d66[_0x310ac2(0x1cf)](-0x601*-0x6+0x131+-0x23a7),_0x428d66['end'](JSON[_0x310ac2(0x13f)]({'error':_0x254a6c(0x2b4)+_0x254a6c(0x175)+'d'}));return;}const _0x5d89ff=_0x26831b['channel']||_0x254a6c(0x2d2),_0x5a6b5e=_0x26831b[_0x310ac2(0x20e)]||String(config['allowedUse'+'rs'][0xc0b+-0x1*-0xd9f+-0x19aa]||''),{enqueue:_0x53d040}=await import(_0x310ac2(0x370)+_0x310ac2(0x367)+_0x254a6c(0x23f)),_0x41be7e=_0x53d040(_0x5d89ff,_0x5a6b5e,_0x310ac2(0x17c)+(_0x26831b['event']||'unknown')+']\x20'+_0x26831b['message']);_0x428d66[_0x254a6c(0x1cf)](-0x3*-0xcfd+-0x184+-0x1*0x24ab),_0x428d66['end'](JSON[_0x254a6c(0x13f)]({'ok':!![],'queued':_0x41be7e}));}catch{_0x428d66[_0x310ac2(0x1cf)](-0x1a41+0x18+0x1bb9),_0x428d66[_0x310ac2(0x10f)](JSON['stringify']({'error':_0x254a6c(0x152)+_0x254a6c(0x351)}));}return;}if(_0x1ec270===_0x254a6c(0x1c3)+_0x254a6c(0x1ca)+_0x310ac2(0x21d)&&_0xbb0ab0[_0x254a6c(0x159)]===_0x310ac2(0x123)){if(_0x15594d['length']>(0x23*0x41+0x6be+-0xb*0x16b)*(-0xe2+0x1e3*-0xf+0x5*0x6a3)){_0x428d66[_0x254a6c(0x34d)]=-0x797+0xad*-0x25+-0x8b*-0x3f,_0x428d66[_0x254a6c(0x10f)](JSON[_0x254a6c(0x13f)]({'error':'Payload\x20to'+_0x310ac2(0x114)}));return;}if(!timingSafeBearerMatch(_0xbb0ab0['headers'][_0x254a6c(0x217)+'ion'],INTERNAL_TOKEN)){_0x428d66[_0x254a6c(0x34d)]=0xac7*0x1+0x2334+0xa*-0x471,_0x428d66['end'](JSON[_0x310ac2(0x13f)]({'error':_0x310ac2(0x259)+'ed'}));return;}let _0x55b9dd;try{const _0x265123=JSON[_0x310ac2(0x302)](_0x15594d);_0x55b9dd=typeof _0x265123[_0x254a6c(0x185)]===_0x254a6c(0x2c8)?_0x265123['agentId']:'';}catch{_0x428d66['statusCode']=0x49b*-0x1+-0x1315*-0x1+-0xcea,_0x428d66[_0x254a6c(0x10f)](JSON[_0x254a6c(0x13f)]({'error':_0x254a6c(0x152)+_0x254a6c(0x351)}));return;}if(!_0x55b9dd){_0x428d66[_0x310ac2(0x34d)]=-0x1*0xe90+-0x2b*0xe2+0x3616,_0x428d66[_0x254a6c(0x10f)](JSON[_0x254a6c(0x13f)]({'error':'Missing\x20ag'+_0x254a6c(0x2f7)}));return;}try{const {deliverByAgentId:_0x5661eb}=await import(_0x254a6c(0x370)+_0x310ac2(0x163)+'ent-watche'+_0x254a6c(0x1cc)),_0x2c9f27=await _0x5661eb(_0x55b9dd);_0x428d66[_0x254a6c(0x34d)]=_0x2c9f27===_0x310ac2(0x150)?0xc98+-0xc78+-0xba*-0x2:_0x2c9f27===_0x254a6c(0x36a)?0x8b3*0x1+-0x58f*0x1+-0x25a:-0x12b7+0x92*0x29+-0x3e3,_0x428d66[_0x254a6c(0x10f)](JSON['stringify']({'ok':_0x2c9f27!==_0x254a6c(0x150),'outcome':_0x2c9f27}));}catch(_0x38175f){_0x428d66[_0x310ac2(0x34d)]=0x86e+-0x7d3*0x1+-0x17*-0xf,_0x428d66[_0x254a6c(0x10f)](JSON[_0x310ac2(0x13f)]({'error':_0x310ac2(0x358)+_0x254a6c(0x1d2)})),console[_0x254a6c(0x26c)](_0x310ac2(0x25e)+'subagent-e'+'xit]\x20deliv'+_0x310ac2(0x1df),_0x38175f);}return;}if(!checkAuth(_0xbb0ab0)){_0x428d66[_0x254a6c(0x34d)]=0x169d+-0x1*0xdb1+-0x75b,_0x428d66[_0x254a6c(0x10f)](JSON['stringify']({'error':_0x310ac2(0x2e0)+_0x310ac2(0x2d4)}));return;}const _0x53055f=new Set([_0x254a6c(0x21c)+_0x310ac2(0x1c6),'/api/skill'+_0x254a6c(0x252),_0x254a6c(0x284)+_0x254a6c(0x31a),_0x254a6c(0x284)+'s/delete',_0x254a6c(0x230)+'/save',_0x310ac2(0x230)+_0x254a6c(0x203),'/api/env/s'+'et',_0x310ac2(0x35c)+_0x310ac2(0x28e),_0x254a6c(0x276)+'save','/api/memor'+_0x254a6c(0x13d),'/api/memor'+_0x254a6c(0x15d),_0x254a6c(0x36f)+_0x310ac2(0x26d),_0x254a6c(0x2d7)+_0x310ac2(0x2fc),_0x254a6c(0x2d7)+'update',_0x310ac2(0x2d7)+_0x254a6c(0x1b9),_0x254a6c(0x2d7)+_0x310ac2(0x143),'/api/cron/'+_0x310ac2(0x17d),_0x310ac2(0x206)+_0x254a6c(0x146),_0x254a6c(0x206)+_0x310ac2(0x186)+'l',_0x254a6c(0x2d3)+'/execute',_0x254a6c(0x153)+_0x254a6c(0x374),'/api/sudo/'+_0x310ac2(0x101),_0x310ac2(0x153)+_0x310ac2(0x2af)+'og','/api/sudo/'+_0x254a6c(0x366),_0x310ac2(0x34c)+'ders/set-k'+'ey',_0x310ac2(0x34c)+_0x254a6c(0x31c)+_0x254a6c(0x292),_0x254a6c(0x34c)+_0x310ac2(0x136)+_0x254a6c(0x10d),_0x310ac2(0x34c)+'ders/add-c'+_0x254a6c(0x286),'/api/provi'+_0x310ac2(0x272)+'e-custom',_0x254a6c(0x202)+_0x254a6c(0x176)+_0x254a6c(0x33d),_0x254a6c(0x202)+_0x310ac2(0x161)+_0x254a6c(0x2a2),_0x254a6c(0x245)+_0x310ac2(0x2d5),_0x310ac2(0x245)+'ack/move',_0x254a6c(0x1e2)+'dd','/api/mcp/r'+_0x310ac2(0x2f0),_0x310ac2(0x27c)+'rt',_0x310ac2(0x1bc)+'s/switch',_0x310ac2(0x15b)+_0x254a6c(0x258)+_0x310ac2(0x131),_0x310ac2(0x15b)+_0x310ac2(0x32d)+_0x254a6c(0x110)]);if(isExposedWithoutPassword()&&_0x53055f[_0x310ac2(0x25f)](_0x1ec270)){_0x428d66[_0x254a6c(0x34d)]=0x21a6+0x1973+-0x3986,_0x428d66[_0x310ac2(0x10f)](JSON[_0x310ac2(0x13f)]({'error':'refused:\x20w'+_0x310ac2(0x25d)+_0x310ac2(0x33b)+_0x254a6c(0x296)+'D'}));return;}const _0x1bf63e=await handleSetupAPI(_0xbb0ab0,_0x428d66,_0x1ec270,_0x15594d);if(_0x1bf63e)return;const _0x3616e6=await handleDoctorAPI(_0xbb0ab0,_0x428d66,_0x1ec270,_0x15594d);if(_0x3616e6)return;if(_0x1ec270===_0x310ac2(0x35c)+_0x310ac2(0x338)){const _0x2052d1=ENV_FILE;let _0x380598={};try{const _0x2326cb=_0x2929a5[_0x254a6c(0x271)+'nc'](_0x2052d1,_0x254a6c(0x240))[_0x310ac2(0x27a)]('\x0a');for(const _0x1d8add of _0x2326cb){if(_0x1d8add[_0x254a6c(0x1e0)]('#')||!_0x1d8add[_0x254a6c(0x333)]('='))continue;const _0x14ac91=_0x1d8add[_0x254a6c(0x2e8)]('=');_0x380598[_0x1d8add['slice'](-0x1fd*-0x6+-0x80f*0x4+0x2e*0x71,_0x14ac91)[_0x254a6c(0x1e7)]()]=_0x1d8add[_0x254a6c(0x124)](_0x14ac91+(-0x1*-0xe7d+-0x2636+0x17ba))['trim']();}}catch{}const _0x6faf12=!!(_0x380598[_0x310ac2(0x198)]||process['env'][_0x254a6c(0x198)]),_0x3077da=!!(_0x380598[_0x310ac2(0x298)+_0x254a6c(0x24f)]||process[_0x254a6c(0x1c8)][_0x254a6c(0x298)+_0x310ac2(0x24f)]),_0x7feef9=!!(_0x380598[_0x310ac2(0x2c1)+_0x254a6c(0x369)]||process[_0x310ac2(0x1c8)][_0x254a6c(0x2c1)+'OVIDER']),_0xed00f0={'groq':!!(_0x380598[_0x254a6c(0x27b)+'EY']||process['env'][_0x310ac2(0x27b)+'EY']),'openai':!!(_0x380598[_0x310ac2(0x141)+_0x310ac2(0x264)]||process[_0x310ac2(0x1c8)]['OPENAI_API'+_0x254a6c(0x264)]),'google':!!(_0x380598[_0x254a6c(0x2a9)+'_KEY']||process['env'][_0x310ac2(0x2a9)+'_KEY']),'nvidia':!!(_0x380598[_0x310ac2(0x205)+'_KEY']||process[_0x310ac2(0x1c8)][_0x254a6c(0x205)+_0x254a6c(0x264)]),'anthropic':!!(_0x380598[_0x254a6c(0x156)+'API_KEY']||process[_0x310ac2(0x1c8)][_0x254a6c(0x156)+_0x310ac2(0x300)]),'openrouter':!!(_0x380598[_0x254a6c(0x29a)+_0x310ac2(0x282)]||process[_0x254a6c(0x1c8)][_0x310ac2(0x29a)+'_API_KEY'])},_0x244ef4=_0x7feef9||Object['values'](_0xed00f0)[_0x310ac2(0x364)](Boolean);let _0x21bfd4=![];try{const {execSync:_0x3be447}=await import(_0x254a6c(0x35d)+'ess');_0x3be447(_0x310ac2(0x15f)+_0x310ac2(0x2aa),{'timeout':0x1388,'stdio':_0x310ac2(0x339)}),_0x21bfd4=!![];}catch{}const _0x10f6a5=_0x6faf12&&_0x3077da&&_0x244ef4;_0x428d66[_0x310ac2(0x10f)](JSON[_0x254a6c(0x13f)]({'isComplete':_0x10f6a5,'steps':{'telegram':{'done':_0x6faf12&&_0x3077da,'botToken':_0x6faf12,'allowedUsers':_0x3077da},'provider':{'done':_0x244ef4,'primary':_0x380598[_0x254a6c(0x2c1)+_0x254a6c(0x369)]||process['env']['PRIMARY_PR'+_0x254a6c(0x369)]||'','keys':_0xed00f0,'claudeCli':_0x21bfd4}}}));return;}if(_0x1ec270===_0x310ac2(0x35c)+_0x254a6c(0x28e)&&_0xbb0ab0[_0x254a6c(0x159)]===_0x310ac2(0x123)){try{const _0x35acef=JSON[_0x254a6c(0x302)](_0x15594d),_0xe6ab6b=ENV_FILE;let _0xa9162c=_0x2929a5[_0x254a6c(0x1c0)](_0xe6ab6b)?_0x2929a5[_0x254a6c(0x271)+'nc'](_0xe6ab6b,_0x254a6c(0x240)):'';const _0x594e19=(_0x191571,_0x38d2a7)=>{const _0x33aa5b=_0x310ac2,_0x42ef27=_0x254a6c;if(/[\n\r]/[_0x33aa5b(0x352)](_0x38d2a7))throw new Error(_0x42ef27(0x18c)+_0x33aa5b(0x1ed)+_0x42ef27(0x2fd)+_0x33aa5b(0x304)+_0x33aa5b(0x2ce));const _0x1d0900=new RegExp('^'+_0x191571+_0x42ef27(0x1be),'m');_0x1d0900['test'](_0xa9162c)?_0xa9162c=_0xa9162c[_0x33aa5b(0x2f6)](_0x1d0900,_0x191571+'='+_0x38d2a7):_0xa9162c=_0xa9162c[_0x33aa5b(0x22e)]()+('\x0a'+_0x191571+'='+_0x38d2a7+'\x0a'),process[_0x33aa5b(0x1c8)][_0x191571]=_0x38d2a7;};if(_0x35acef[_0x254a6c(0x13c)])_0x594e19(_0x310ac2(0x198),_0x35acef[_0x254a6c(0x13c)]);if(_0x35acef['allowedUse'+'rs'])_0x594e19(_0x310ac2(0x298)+_0x254a6c(0x24f),_0x35acef[_0x310ac2(0x318)+'rs']);if(_0x35acef[_0x310ac2(0x117)+_0x254a6c(0x14e)])_0x594e19(_0x254a6c(0x2c1)+_0x254a6c(0x369),_0x35acef['primaryPro'+'vider']);if(_0x35acef[_0x310ac2(0x233)]&&_0x35acef[_0x310ac2(0x26f)])_0x594e19(_0x35acef[_0x310ac2(0x26f)],_0x35acef['apiKey']);if(_0x35acef[_0x310ac2(0x1b1)+'d'])_0x594e19(_0x310ac2(0x1c7)+'RD',_0x35acef[_0x310ac2(0x1b1)+'d']);_0x2929a5[_0x254a6c(0x37d)+_0x254a6c(0x1f9)](_0xe6ab6b,_0xa9162c),_0x428d66[_0x310ac2(0x10f)](JSON[_0x254a6c(0x13f)]({'ok':!![],'note':_0x254a6c(0x170)+_0x310ac2(0x12e)+_0x310ac2(0x363)+'.'}));}catch(_0x399444){_0x428d66[_0x254a6c(0x34d)]=0x23e7+0xbd9*0x3+-0x45e2,_0x428d66['end'](JSON['stringify']({'error':_0x399444 instanceof Error?_0x399444[_0x254a6c(0x350)]:String(_0x399444)}));}return;}if(_0x1ec270==='/api/valid'+'ate-bot-to'+_0x254a6c(0x1d5)&&_0xbb0ab0['method']===_0x254a6c(0x123)){try{const {token:_0x2a14b2}=JSON[_0x254a6c(0x302)](_0x15594d);if(!_0x2a14b2||!_0x2a14b2[_0x310ac2(0x333)](':')){_0x428d66[_0x254a6c(0x10f)](JSON['stringify']({'ok':![],'error':_0x310ac2(0x227)+_0x310ac2(0x10b)}));return;}const _0x17b24b=await fetch(_0x310ac2(0x372)+_0x254a6c(0x32a)+_0x310ac2(0x27e)+_0x2a14b2+_0x254a6c(0x254)),_0x40f739=await _0x17b24b['json']();_0x40f739['ok']?_0x428d66[_0x254a6c(0x10f)](JSON[_0x254a6c(0x13f)]({'ok':!![],'bot':{'username':_0x40f739[_0x254a6c(0x1e8)][_0x310ac2(0x129)],'firstName':_0x40f739[_0x254a6c(0x1e8)][_0x254a6c(0x17a)],'id':_0x40f739[_0x310ac2(0x1e8)]['id']}})):_0x428d66[_0x310ac2(0x10f)](JSON['stringify']({'ok':![],'error':_0x40f739['descriptio'+'n']||'Invalid\x20to'+_0x254a6c(0x1d5)}));}catch(_0x4ff30e){_0x428d66[_0x310ac2(0x10f)](JSON[_0x254a6c(0x13f)]({'ok':![],'error':_0x4ff30e instanceof Error?_0x4ff30e[_0x310ac2(0x350)]:String(_0x4ff30e)}));}return;}if(_0x1ec270===_0x310ac2(0x29d)+'s'){let _0x310595={'name':_0x254a6c(0x1da)+_0x254a6c(0x1ad),'model':'none','status':'unconfigur'+'ed'};try{const _0x552636=getRegistry(),_0xef15ba=_0x552636[_0x310ac2(0x139)]()['getInfo']();_0x310595={'name':_0xef15ba[_0x310ac2(0x2f2)],'model':_0xef15ba['model'],'status':_0xef15ba[_0x254a6c(0x26e)]};}catch{}const _0x175c76=getMemoryStats(),_0xa9fd6f=getIndexStats(),_0x5ad516=getLoadedPlugins(),_0xdfc8cc=getMCPStatus(),_0x145895=listProfiles(),_0xa9db43=listCustomTools(),{getAllSessions:_0x4bde57}=await import('../service'+_0x310ac2(0x220)+'js'),_0x166736=_0x4bde57();let _0xca9dde=-0xbc2+-0x1*-0xf95+-0x1*0x3d3,_0x70de79=0x2f9*0x5+-0x17a6+-0xad*-0xd,_0x52851c=-0x762*-0x2+-0x1e2+-0xc2*0x11;for(const _0x13fe3d of _0x166736['values']()){_0xca9dde+=_0x13fe3d[_0x254a6c(0x347)+_0x254a6c(0x214)]||-0x1064+0x1aae+-0xa4a,_0x70de79+=_0x13fe3d[_0x254a6c(0x128)+_0x310ac2(0x14c)]||-0x1*0x204a+-0x3*0x350+-0x5e*-0x73,_0x52851c+=_0x13fe3d[_0x254a6c(0x18d)]||0x17*-0x13f+0x2087+-0x3de;}const {config:_0x1145fc}=await import(_0x310ac2(0x2fb)+'js');_0x428d66[_0x254a6c(0x10f)](JSON[_0x254a6c(0x13f)]({'bot':{'version':BOT_VERSION,'uptime':process[_0x310ac2(0x25a)]()},'model':_0x310595,'memory':{..._0x175c76,'vectors':_0xa9fd6f['entries'],'indexSize':_0xa9fd6f['sizeBytes']},'plugins':_0x5ad516[_0x310ac2(0x37f)],'mcp':_0xdfc8cc['length'],'users':_0x145895['length'],'tools':_0xa9db43[_0x254a6c(0x37f)],'tokens':{'totalInput':_0xca9dde,'totalOutput':_0x70de79,'total':_0xca9dde+_0x70de79,'totalCost':_0x52851c},'setup':{'telegram':!!_0x1145fc['botToken'],'provider':_0x310595[_0x254a6c(0x26e)]!==_0x254a6c(0x1b8)+'ed'}}));return;}if(_0x1ec270===_0x254a6c(0x1bc)+'s'){const _0x249c2c=getRegistry();_0x249c2c['listAll']()[_0x254a6c(0x348)](_0x371aa8=>{const _0x15a7c5=_0x254a6c;_0x428d66['end'](JSON['stringify']({'models':_0x371aa8,'active':_0x249c2c[_0x15a7c5(0x2c2)+'ey']()}));});return;}if(_0x1ec270===_0x254a6c(0x1bc)+_0x254a6c(0x319)&&_0xbb0ab0['method']===_0x310ac2(0x123)){try{const {key:_0x5e3061}=JSON['parse'](_0x15594d),_0x515d9d=getRegistry(),_0x400953=_0x515d9d[_0x254a6c(0x267)](_0x5e3061);_0x428d66['end'](JSON[_0x310ac2(0x13f)]({'ok':_0x400953,'active':_0x515d9d[_0x254a6c(0x2c2)+'ey']()}));}catch{_0x428d66['statusCode']=0xce6*0x1+-0x1a58+0xf02,_0x428d66['end'](JSON['stringify']({'error':_0x254a6c(0x32e)+_0x254a6c(0x1bd)}));}return;}if(_0x1ec270===_0x254a6c(0x245)+_0x310ac2(0x2d5)&&_0xbb0ab0[_0x310ac2(0x159)]===_0x310ac2(0x34a)){try{const {getFallbackOrder:_0x5c6735}=await import(_0x254a6c(0x370)+_0x254a6c(0x27d)+_0x254a6c(0x1b0)),{getHealthStatus:_0x16040e,isFailedOver:_0x4dd238}=await import(_0x310ac2(0x370)+'s/heartbea'+_0x310ac2(0x314)),_0x19b752=getRegistry(),_0x129008=await _0x19b752[_0x254a6c(0x10c)]();_0x428d66[_0x310ac2(0x10f)](JSON[_0x254a6c(0x13f)]({'order':_0x5c6735(),'health':_0x16040e(),'failedOver':_0x4dd238(),'activeProvider':_0x19b752[_0x310ac2(0x2c2)+'ey'](),'availableProviders':_0x129008[_0x310ac2(0x1e6)](_0x1d2159=>({'key':_0x1d2159['key'],'name':_0x1d2159['name'],'status':_0x1d2159['status']}))}));}catch(_0x4d4fc0){_0x428d66['end'](JSON[_0x254a6c(0x13f)]({'error':String(_0x4d4fc0)}));}return;}if(_0x1ec270===_0x254a6c(0x245)+_0x254a6c(0x2d5)&&_0xbb0ab0[_0x310ac2(0x159)]===_0x310ac2(0x123)){try{const {primary:_0x2858dd,fallbacks:_0x522e5d}=JSON[_0x254a6c(0x302)](_0x15594d),{setFallbackOrder:_0x50beb0}=await import('../service'+_0x310ac2(0x27d)+_0x254a6c(0x1b0)),_0x5037d3=_0x50beb0(_0x2858dd,_0x522e5d,'webui');_0x428d66[_0x254a6c(0x10f)](JSON[_0x254a6c(0x13f)]({'ok':!![],'order':_0x5037d3}));}catch(_0x1dcade){_0x428d66[_0x310ac2(0x34d)]=0x1857+0x2551+-0x281*0x18,_0x428d66[_0x254a6c(0x10f)](JSON[_0x310ac2(0x13f)]({'error':String(_0x1dcade)}));}return;}if(_0x1ec270===_0x254a6c(0x245)+'ack/move'&&_0xbb0ab0[_0x310ac2(0x159)]==='POST'){try{const {key:_0x566f13,direction:_0x236fd3}=JSON[_0x254a6c(0x302)](_0x15594d),_0x29b13d=await import('../service'+'s/fallback'+_0x310ac2(0x1b0)),_0x507d09=_0x236fd3==='up'?_0x29b13d[_0x254a6c(0x2ad)](_0x566f13,_0x310ac2(0x345)):_0x29b13d[_0x254a6c(0x2ec)](_0x566f13,_0x254a6c(0x345));_0x428d66['end'](JSON[_0x310ac2(0x13f)]({'ok':!![],'order':_0x507d09}));}catch(_0x1f3c9e){_0x428d66[_0x254a6c(0x34d)]=-0x2b*0xd3+-0x124*0x1+-0x23*-0x117,_0x428d66[_0x254a6c(0x10f)](JSON['stringify']({'error':String(_0x1f3c9e)}));}return;}if(_0x1ec270===_0x254a6c(0x19d)+_0x254a6c(0x368)){try{const {getHealthStatus:_0xc17af8,isFailedOver:_0x14ff3b}=await import(_0x254a6c(0x370)+_0x310ac2(0x329)+'t.js');_0x428d66['end'](JSON[_0x310ac2(0x13f)]({'health':_0xc17af8(),'failedOver':_0x14ff3b()}));}catch(_0x7fb7f6){_0x428d66[_0x254a6c(0x10f)](JSON[_0x310ac2(0x13f)]({'health':[],'failedOver':![]}));}return;}if(_0x1ec270===_0x310ac2(0x32b)+'y'){const _0x2df598=loadLongTermMemory(),_0x7c8564=loadDailyLog(),_0x2ecf1b=getMemoryStats(),_0x24cfba=getIndexStats();let _0x5edb2e=[];try{_0x5edb2e=_0x2929a5['readdirSyn'+'c'](MEMORY_DIR)[_0x254a6c(0x380)](_0x48db55=>_0x48db55[_0x254a6c(0x18e)](_0x254a6c(0x327))&&!_0x48db55[_0x310ac2(0x1e0)]('.'))[_0x310ac2(0x199)]()[_0x310ac2(0x1c4)]();}catch{}_0x428d66[_0x254a6c(0x10f)](JSON['stringify']({'longTermMemory':_0x2df598,'todayLog':_0x7c8564,'dailyFiles':_0x5edb2e,'stats':_0x2ecf1b,'index':{'entries':_0x24cfba[_0x310ac2(0x320)],'files':_0x24cfba[_0x254a6c(0x321)],'sizeBytes':_0x24cfba['sizeBytes']}}));return;}if(_0x1ec270[_0x310ac2(0x1e0)](_0x254a6c(0x32b)+'y/')){const _0x3821b3=_0x1ec270['slice'](-0x18*0x12c+0x99*-0x1a+0x45f*0xa);if(_0x3821b3[_0x310ac2(0x333)]('..')||!_0x3821b3[_0x254a6c(0x18e)](_0x310ac2(0x327))){_0x428d66['statusCode']=-0x1865*0x1+0x1*0x49+0x19ac,_0x428d66['end'](JSON[_0x254a6c(0x13f)]({'error':_0x254a6c(0x1b6)+'le'}));return;}try{const _0x356f49=_0x2929a5['readFileSy'+'nc'](resolve(MEMORY_DIR,_0x3821b3),_0x254a6c(0x240));_0x428d66[_0x310ac2(0x10f)](JSON[_0x310ac2(0x13f)]({'file':_0x3821b3,'content':_0x356f49}));}catch{_0x428d66[_0x310ac2(0x34d)]=-0x643+-0x1098+0x186f,_0x428d66[_0x310ac2(0x10f)](JSON[_0x254a6c(0x13f)]({'error':'File\x20not\x20f'+_0x254a6c(0x100)}));}return;}if(_0x1ec270===_0x310ac2(0x32b)+'y/save'&&_0xbb0ab0['method']===_0x254a6c(0x123)){try{const {file:_0x5cc22a,content:_0xe0ab6b}=JSON[_0x254a6c(0x302)](_0x15594d);if(_0x5cc22a===_0x254a6c(0x1f0))_0x2929a5['writeFileS'+'ync'](MEMORY_FILE,_0xe0ab6b);else{if(_0x5cc22a[_0x310ac2(0x18e)]('.md')&&!_0x5cc22a[_0x310ac2(0x333)]('..'))_0x2929a5[_0x254a6c(0x37d)+'ync'](resolve(MEMORY_DIR,_0x5cc22a),_0xe0ab6b);else{_0x428d66['statusCode']=0xde9*0x1+0x6*-0x1dd+-0x17*0xd,_0x428d66['end'](JSON['stringify']({'error':'Invalid\x20fi'+'le'}));return;}}_0x428d66[_0x310ac2(0x10f)](JSON[_0x254a6c(0x13f)]({'ok':!![]}));}catch{_0x428d66['statusCode']=0xb14+-0x72a+-0xe*0x2b,_0x428d66[_0x254a6c(0x10f)](JSON[_0x254a6c(0x13f)]({'error':'Invalid\x20re'+_0x310ac2(0x1bd)}));}return;}if(_0x1ec270==='/api/plugi'+'ns'){_0x428d66[_0x310ac2(0x10f)](JSON['stringify']({'plugins':getLoadedPlugins()}));return;}if(_0x1ec270===_0x310ac2(0x30c)+'paces'){try{const {listWorkspaces:_0x4d1263,getDefaultWorkspace:_0x1621f6}=await import(_0x254a6c(0x370)+_0x310ac2(0x35e)+_0x254a6c(0x195)),{getCostByWorkspace:_0x2f4f5f}=await import('../service'+'s/session.'+'js'),_0x5a6788=_0x2f4f5f(),_0x41ff5c=_0x4d1263(),_0xf3d99c=[_0x1621f6(),..._0x41ff5c],_0x482322=_0xf3d99c[_0x310ac2(0x1e6)](_0x7b0b2a=>({'name':_0x7b0b2a[_0x310ac2(0x2f2)],'purpose':_0x7b0b2a[_0x310ac2(0x173)],'emoji':_0x7b0b2a[_0x254a6c(0x24e)]??null,'color':_0x7b0b2a[_0x254a6c(0x19e)]??null,'cwd':_0x7b0b2a['cwd'],'channels':_0x7b0b2a[_0x310ac2(0x24a)],'stats':_0x5a6788[_0x7b0b2a[_0x254a6c(0x2f2)]]??{'totalCost':0x0,'sessionCount':0x0,'messageCount':0x0,'toolUseCount':0x0}}));_0x428d66[_0x254a6c(0x10f)](JSON['stringify']({'workspaces':_0x482322}));}catch(_0x198a26){_0x428d66[_0x254a6c(0x34d)]=0x21a0+-0x15e5+-0x9c7,_0x428d66['end'](JSON['stringify']({'error':_0x198a26 instanceof Error?_0x198a26[_0x254a6c(0x350)]:String(_0x198a26)}));}return;}if(_0x1ec270===_0x254a6c(0x135)&&_0xbb0ab0[_0x310ac2(0x159)]===_0x310ac2(0x34a)){const {getAllSessions:_0x3d31a2}=await import(_0x254a6c(0x370)+_0x254a6c(0x220)+'js'),_0x4805b7=listProfiles(),_0x197fea=_0x3d31a2(),_0x41f1f9=new Map(Array[_0x310ac2(0x213)](_0x197fea[_0x254a6c(0x320)]())[_0x310ac2(0x1e6)](([_0x41e97f,_0x45f10b])=>[Number(_0x41e97f),_0x45f10b])),_0x59a403=_0x4805b7[_0x310ac2(0x1e6)](_0x18ed53=>{const _0x5af274=_0x254a6c,_0x505c69=_0x254a6c,_0x5c66c1=_0x41f1f9[_0x5af274(0x1e4)](_0x18ed53['userId']);return{..._0x18ed53,'session':_0x5c66c1?{'isProcessing':_0x5c66c1['isProcessi'+'ng'],'totalCost':_0x5c66c1[_0x5af274(0x18d)],'historyLength':_0x5c66c1[_0x5af274(0x34e)][_0x5af274(0x37f)],'effort':_0x5c66c1['effort'],'voiceReply':_0x5c66c1[_0x5af274(0x166)],'startedAt':_0x5c66c1[_0x5af274(0x208)],'messageCount':_0x5c66c1['messageCou'+'nt'],'toolUseCount':_0x5c66c1[_0x505c69(0x2ef)+'nt'],'workingDir':_0x5c66c1[_0x505c69(0x256)],'hasActiveQuery':!!_0x5c66c1[_0x505c69(0x113)+'oller'],'queuedMessages':_0x5c66c1[_0x505c69(0x36d)+'ue'][_0x505c69(0x37f)]}:null};});_0x428d66[_0x310ac2(0x10f)](JSON[_0x310ac2(0x13f)]({'users':_0x59a403}));return;}if(_0x1ec270['startsWith'](_0x254a6c(0x135)+'/')&&_0xbb0ab0[_0x254a6c(0x159)]==='DELETE'){if(isExposedWithoutPassword()){_0x428d66[_0x254a6c(0x34d)]=0x2a1*-0x1+-0xe06+0x1*0x123a,_0x428d66[_0x310ac2(0x10f)](JSON[_0x310ac2(0x13f)]({'error':_0x310ac2(0x160)+_0x254a6c(0x25d)+_0x310ac2(0x33b)+_0x254a6c(0x296)+'D'}));return;}const _0x2bfea3=parseInt(_0x1ec270['split']('/')['pop']()||'0');if(!_0x2bfea3){_0x428d66['statusCode']=0x2*-0x128b+-0x5a7*0x5+0x42e9,_0x428d66[_0x310ac2(0x10f)](JSON[_0x254a6c(0x13f)]({'error':_0x310ac2(0x1dd)+_0x254a6c(0x34f)}));return;}const {deleteUser:_0x304e45}=await import(_0x310ac2(0x370)+_0x254a6c(0x2da)),_0x59ad01=_0x304e45(_0x2bfea3);_0x428d66[_0x254a6c(0x10f)](JSON[_0x254a6c(0x13f)]({'ok':!![],..._0x59ad01}));return;}if(_0x1ec270===_0x254a6c(0x2d3)){const _0x53bb09=getCustomTools();_0x428d66[_0x254a6c(0x10f)](JSON[_0x310ac2(0x13f)]({'tools':_0x53bb09}));return;}if(_0x1ec270===_0x310ac2(0x2d3)+_0x310ac2(0x301)&&_0xbb0ab0['method']===_0x310ac2(0x123)){try{const {name:_0x3bc3ef,params:_0x14ff75}=JSON['parse'](_0x15594d);if(!_0x3bc3ef){_0x428d66['statusCode']=-0x1*0x2076+-0x369+-0x559*-0x7,_0x428d66[_0x310ac2(0x10f)](JSON[_0x254a6c(0x13f)]({'error':'No\x20tool\x20na'+'me'}));return;}const _0x4b2bce=await executeCustomTool(_0x3bc3ef,_0x14ff75||{});_0x428d66[_0x254a6c(0x10f)](JSON['stringify']({'ok':!![],'output':_0x4b2bce}));}catch(_0x29ea6e){const _0x1502ef=_0x29ea6e instanceof Error?_0x29ea6e['message']:String(_0x29ea6e);_0x428d66['end'](JSON[_0x254a6c(0x13f)]({'error':_0x1502ef}));}return;}if(_0x1ec270===_0x254a6c(0x28f)){const {getMCPStatus:_0x25e3ea,getMCPTools:_0x32c1e0,hasMCPConfig:_0xa6adbd}=await import(_0x254a6c(0x370)+_0x254a6c(0x204)),_0x86f294=_0x25e3ea(),_0x40eab9=_0x32c1e0(),_0x1551cf=MCP_CONFIG;let _0x32d835={'servers':{}};try{_0x32d835=JSON['parse'](_0x2929a5[_0x254a6c(0x271)+'nc'](_0x1551cf,_0x310ac2(0x240)));}catch{}_0x428d66[_0x254a6c(0x10f)](JSON[_0x254a6c(0x13f)]({'servers':_0x86f294,'tools':_0x40eab9,'config':_0x32d835,'hasConfig':_0xa6adbd()}));return;}if(_0x1ec270==='/api/mcp/a'+'dd'&&_0xbb0ab0[_0x310ac2(0x159)]===_0x310ac2(0x123)){try{const {name:_0x1801e4,command:_0x4f0c34,args:_0x4da155,url:_0x41613e,env:_0x226587,headers:_0x1728b6}=JSON[_0x310ac2(0x302)](_0x15594d);if(!_0x1801e4){_0x428d66[_0x254a6c(0x34d)]=0x1*-0x2705+-0x2*0x1367+0x4f63,_0x428d66[_0x254a6c(0x10f)](JSON[_0x254a6c(0x13f)]({'error':_0x254a6c(0x2f3)+_0x310ac2(0x2b2)}));return;}const _0x29b00b=MCP_CONFIG;let _0x23d440={'servers':{}};try{_0x23d440=JSON[_0x254a6c(0x302)](_0x2929a5['readFileSy'+'nc'](_0x29b00b,_0x254a6c(0x240)));}catch{}const _0x2c3b7={};if(_0x4f0c34){_0x2c3b7['command']=_0x4f0c34,_0x2c3b7[_0x254a6c(0x231)]=_0x4da155||[];if(_0x226587)_0x2c3b7[_0x310ac2(0x1c8)]=_0x226587;}else{if(_0x41613e){_0x2c3b7['url']=_0x41613e;if(_0x1728b6)_0x2c3b7['headers']=_0x1728b6;}else{_0x428d66['statusCode']=0x1*-0x1c1d+-0x209e+0x3e4b,_0x428d66['end'](JSON['stringify']({'error':'command\x20or'+_0x254a6c(0x182)+_0x254a6c(0x2b2)}));return;}}_0x23d440[_0x254a6c(0x269)][_0x1801e4]=_0x2c3b7,_0x2929a5[_0x310ac2(0x37d)+'ync'](_0x29b00b,JSON[_0x254a6c(0x13f)](_0x23d440,null,-0x408+-0x1*-0x14a1+-0x89*0x1f)),_0x428d66[_0x254a6c(0x10f)](JSON[_0x254a6c(0x13f)]({'ok':!![],'note':_0x310ac2(0x15c)+'eded\x20to\x20co'+_0x254a6c(0x167)}));}catch(_0x1a71a2){_0x428d66['statusCode']=0xe31+0x1683+0x1192*-0x2,_0x428d66[_0x254a6c(0x10f)](JSON[_0x254a6c(0x13f)]({'error':_0x1a71a2 instanceof Error?_0x1a71a2[_0x254a6c(0x350)]:String(_0x1a71a2)}));}return;}if(_0x1ec270===_0x254a6c(0x353)+'emove'&&_0xbb0ab0['method']==='POST'){try{const {name:_0x2e4247}=JSON[_0x254a6c(0x302)](_0x15594d),_0x28036a=MCP_CONFIG;let _0x5be068={'servers':{}};try{_0x5be068=JSON[_0x254a6c(0x302)](_0x2929a5[_0x310ac2(0x271)+'nc'](_0x28036a,_0x254a6c(0x240)));}catch{}delete _0x5be068['servers'][_0x2e4247],_0x2929a5[_0x254a6c(0x37d)+_0x310ac2(0x1f9)](_0x28036a,JSON[_0x254a6c(0x13f)](_0x5be068,null,0x411+-0x4b3+0x2*0x52)),_0x428d66[_0x254a6c(0x10f)](JSON['stringify']({'ok':!![]}));}catch(_0x2754b0){_0x428d66[_0x254a6c(0x34d)]=-0x16ae+0x33d*-0x1+0x69*0x43,_0x428d66['end'](JSON['stringify']({'error':_0x2754b0 instanceof Error?_0x2754b0[_0x310ac2(0x350)]:String(_0x2754b0)}));}return;}if(_0x1ec270==='/api/mcp/d'+_0x310ac2(0x1f7)){const _0x2d4bf9=[],{execSync:_0x46362d}=await import(_0x310ac2(0x35d)+_0x254a6c(0x23b)),_0x44b129=[{'pkg':'@modelcont'+_0x254a6c(0x28c)+_0x254a6c(0x21b)+'ilesystem','name':_0x254a6c(0x346),'args':[_0x310ac2(0x2fe)]},{'pkg':'@modelcont'+'extprotoco'+_0x310ac2(0x359)+_0x254a6c(0x18f)+'h','name':'brave-sear'+'ch','args':[]},{'pkg':_0x310ac2(0x290)+_0x254a6c(0x28c)+'l/server-g'+_0x310ac2(0x317),'name':_0x310ac2(0x354),'args':[]},{'pkg':_0x310ac2(0x290)+_0x310ac2(0x28c)+'l/server-p'+_0x310ac2(0x2bb),'name':_0x310ac2(0x1b4),'args':[]},{'pkg':_0x254a6c(0x290)+'extprotoco'+'l/server-s'+_0x310ac2(0x22a),'name':_0x310ac2(0x236),'args':[]},{'pkg':_0x254a6c(0x290)+_0x310ac2(0x28c)+_0x310ac2(0x248)+_0x254a6c(0x19b),'name':'slack','args':[]},{'pkg':'@modelcont'+_0x310ac2(0x28c)+'l/server-m'+_0x254a6c(0x125),'name':_0x310ac2(0x21f),'args':[]},{'pkg':_0x310ac2(0x290)+_0x254a6c(0x28c)+_0x310ac2(0x17e)+_0x254a6c(0x106),'name':_0x254a6c(0x30e),'args':[]},{'pkg':'@modelcont'+_0x254a6c(0x28c)+_0x254a6c(0x21b)+_0x254a6c(0x1d1),'name':_0x310ac2(0x1a6),'args':[]},{'pkg':_0x254a6c(0x112)+_0x254a6c(0x361)+_0x254a6c(0x11e)+'al-thinkin'+'g','name':'sequential'+_0x310ac2(0x246),'args':[]}];for(const _0xe76036 of _0x44b129){try{_0x46362d(_0x310ac2(0x2c0)+_0xe76036[_0x310ac2(0x289)]+_0x254a6c(0x2dd),{'timeout':0x1388,'stdio':_0x310ac2(0x339),'env':{...process[_0x254a6c(0x1c8)],'PATH':process[_0x254a6c(0x1c8)][_0x310ac2(0x31b)]+(':/opt/home'+_0x310ac2(0x29c)+_0x254a6c(0x1f2)+_0x310ac2(0x119))}}),_0x2d4bf9[_0x254a6c(0x305)]({'name':_0xe76036[_0x254a6c(0x2f2)],'command':_0x310ac2(0x179),'args':['-y',_0xe76036[_0x310ac2(0x289)],..._0xe76036[_0x254a6c(0x231)]],'source':'npm'});}catch{try{_0x46362d(_0x254a6c(0x23e)+'g\x20'+_0xe76036[_0x310ac2(0x289)]+'\x20--depth=0',{'timeout':0x1388,'stdio':_0x254a6c(0x339)}),_0x2d4bf9[_0x310ac2(0x305)]({'name':_0xe76036[_0x310ac2(0x2f2)],'command':'npx','args':['-y',_0xe76036[_0x310ac2(0x289)],..._0xe76036['args']],'source':_0x254a6c(0x1ea)});}catch{}}}const _0x2ec3d6=process[_0x254a6c(0x1c8)][_0x254a6c(0x1d0)]||process['env']['USERPROFIL'+'E']||'',_0x2cb9aa=[resolve(_0x2ec3d6,'.config/cl'+_0x310ac2(0x229)+_0x310ac2(0x1f5)+_0x310ac2(0x2a1)+'n'),resolve(_0x2ec3d6,_0x310ac2(0x1a0)+_0x310ac2(0x35b)+_0x254a6c(0x2a8)+_0x254a6c(0x229)+_0x310ac2(0x1f5)+_0x310ac2(0x2a1)+'n'),resolve(_0x2ec3d6,_0x310ac2(0x138)+'aming/Clau'+_0x310ac2(0x1d8)+'desktop_co'+'nfig.json')];for(const _0x238772 of _0x2cb9aa){try{const _0x19b812=JSON[_0x254a6c(0x302)](_0x2929a5['readFileSy'+'nc'](_0x238772,'utf-8'));if(_0x19b812[_0x254a6c(0x1cb)])for(const [_0x47a627,_0x46b17a]of Object['entries'](_0x19b812['mcpServers'])){_0x46b17a['command']&&_0x2d4bf9[_0x310ac2(0x305)]({'name':_0x310ac2(0x278)+_0x47a627,'command':_0x46b17a['command'],'args':_0x46b17a[_0x254a6c(0x231)]||[],'source':_0x254a6c(0x22f)+_0x310ac2(0x2a0)});}}catch{}}_0x428d66['end'](JSON[_0x254a6c(0x13f)]({'discovered':_0x2d4bf9}));return;}if(_0x1ec270?.[_0x310ac2(0x216)](/^\/api\/skills\/detail\//)&&_0xbb0ab0[_0x310ac2(0x159)]==='GET'){const _0x2b007b=_0x1ec270[_0x310ac2(0x27a)]('/')[_0x310ac2(0x1de)](),{getSkills:_0x439167}=await import('../service'+'s/skills.j'+'s'),_0x5b2ac4=_0x439167()[_0x310ac2(0x35a)](_0x1bde4e=>_0x1bde4e['id']===_0x2b007b);_0x5b2ac4?_0x428d66['end'](JSON[_0x310ac2(0x13f)]({'ok':!![],'skill':_0x5b2ac4})):(_0x428d66[_0x310ac2(0x34d)]=0x1*-0x157f+0x14aa+0x269,_0x428d66[_0x254a6c(0x10f)](JSON['stringify']({'error':_0x310ac2(0x330)+_0x310ac2(0x1b7)})));return;}if(_0x1ec270===_0x310ac2(0x284)+_0x310ac2(0x252)&&_0xbb0ab0[_0x310ac2(0x159)]===_0x254a6c(0x123)){try{const {id:_0x43bcfb,name:_0x426787,description:_0x5859c5,triggers:_0x344520,category:_0x35c918,content:_0x11eece,priority:_0x394f20}=JSON['parse'](_0x15594d);if(!_0x43bcfb||!_0x426787){_0x428d66[_0x310ac2(0x34d)]=-0x1*0x1e71+-0xa5f*-0x2+0x3*0x3c1,_0x428d66['end'](JSON['stringify']({'error':_0x254a6c(0x211)+_0x310ac2(0x102)}));return;}if(typeof _0x43bcfb!==_0x254a6c(0x2c8)||_0x43bcfb[_0x254a6c(0x333)]('..')||_0x43bcfb[_0x310ac2(0x333)]('/')||_0x43bcfb[_0x310ac2(0x333)]('\x5c')||_0xd9de36['isAbsolute'](_0x43bcfb)){_0x428d66[_0x254a6c(0x34d)]=0x1622+-0xac4+-0x1f6*0x5,_0x428d66[_0x310ac2(0x10f)](JSON[_0x310ac2(0x13f)]({'error':_0x254a6c(0x190)+_0x254a6c(0x2e5)}));return;}const _0x163a1a=SKILLS_DIR,_0x3154ac=resolve(_0x163a1a,_0x43bcfb);if(!_0x3154ac[_0x254a6c(0x1e0)](_0x163a1a)){_0x428d66['statusCode']=-0x1bb9+-0x8*-0x335+0x3a1,_0x428d66[_0x310ac2(0x10f)](JSON['stringify']({'error':_0x254a6c(0x190)+_0x254a6c(0x2e5)}));return;}if(!_0x2929a5[_0x254a6c(0x1c0)](_0x3154ac))_0x2929a5['mkdirSync'](_0x3154ac,{'recursive':!![]});const _0x8af016=['---',_0x254a6c(0x30a)+_0x426787,_0x5859c5?_0x310ac2(0x335)+'n:\x20'+_0x5859c5:'',_0x344520?_0x310ac2(0x2ab)+(Array['isArray'](_0x344520)?_0x344520[_0x254a6c(0x17b)](',\x20'):_0x344520):'',_0x254a6c(0x180)+(_0x394f20||-0x1*0x1487+-0x1596+0x1*0x2a20),_0x310ac2(0x322)+(_0x35c918||_0x310ac2(0x2ea)),_0x254a6c(0x279)][_0x310ac2(0x380)](Boolean)[_0x310ac2(0x17b)]('\x0a');_0x2929a5[_0x254a6c(0x37d)+_0x254a6c(0x1f9)](resolve(_0x3154ac,_0x254a6c(0x23a)),_0x8af016+'\x0a\x0a'+(_0x11eece||''));const {loadSkills:_0x19b180}=await import(_0x254a6c(0x370)+'s/skills.j'+'s');_0x19b180(),_0x428d66[_0x310ac2(0x10f)](JSON[_0x254a6c(0x13f)]({'ok':!![]}));}catch(_0x2ff3cd){_0x428d66[_0x254a6c(0x34d)]=-0x2530+-0xbb*-0x18+0x7*0x308,_0x428d66['end'](JSON[_0x254a6c(0x13f)]({'error':_0x2ff3cd instanceof Error?_0x2ff3cd[_0x310ac2(0x350)]:String(_0x2ff3cd)}));}return;}if(_0x1ec270==='/api/skill'+'s/update'&&_0xbb0ab0['method']===_0x254a6c(0x123)){try{const {id:_0x534c1d,content:_0xd391}=JSON[_0x254a6c(0x302)](_0x15594d);if(typeof _0x534c1d!==_0x254a6c(0x2c8)||_0x534c1d[_0x310ac2(0x333)]('..')||_0x534c1d['includes']('/')||_0x534c1d[_0x254a6c(0x333)]('\x5c')||_0xd9de36[_0x254a6c(0x1b5)](_0x534c1d)){_0x428d66['statusCode']=0x2*0x132a+-0x18e6+-0xbde,_0x428d66[_0x254a6c(0x10f)](JSON[_0x254a6c(0x13f)]({'error':'Invalid\x20sk'+_0x254a6c(0x2e5)}));return;}if(!resolve(SKILLS_DIR,_0x534c1d)[_0x310ac2(0x1e0)](SKILLS_DIR)){_0x428d66['statusCode']=-0x695+-0x33*0x4d+0x5df*0x4,_0x428d66[_0x310ac2(0x10f)](JSON[_0x310ac2(0x13f)]({'error':'Invalid\x20sk'+_0x254a6c(0x2e5)}));return;}const _0x12f4b5=resolve(SKILLS_DIR,_0x534c1d,_0x254a6c(0x23a));if(!_0x2929a5['existsSync'](_0x12f4b5)){const _0x239362=resolve(SKILLS_DIR,_0x534c1d+_0x254a6c(0x327));if(_0x2929a5[_0x254a6c(0x1c0)](_0x239362))_0x2929a5[_0x254a6c(0x37d)+_0x254a6c(0x1f9)](_0x239362,_0xd391);else{_0x428d66[_0x254a6c(0x34d)]=0x262+-0x1956+0x8*0x311,_0x428d66[_0x310ac2(0x10f)](JSON[_0x310ac2(0x13f)]({'error':_0x254a6c(0x330)+'found'}));return;}}else _0x2929a5[_0x310ac2(0x37d)+_0x254a6c(0x1f9)](_0x12f4b5,_0xd391);const {loadSkills:_0x217eab}=await import(_0x254a6c(0x370)+_0x254a6c(0x2b9)+'s');_0x217eab(),_0x428d66[_0x310ac2(0x10f)](JSON[_0x310ac2(0x13f)]({'ok':!![]}));}catch(_0x496db0){_0x428d66[_0x310ac2(0x34d)]=0x103c+-0x2157+-0x3b*-0x51,_0x428d66[_0x254a6c(0x10f)](JSON['stringify']({'error':_0x496db0 instanceof Error?_0x496db0[_0x310ac2(0x350)]:String(_0x496db0)}));}return;}if(_0x1ec270===_0x254a6c(0x284)+_0x254a6c(0x12b)&&_0xbb0ab0['method']===_0x310ac2(0x123)){try{const {id:_0x43e6ac}=JSON['parse'](_0x15594d);if(typeof _0x43e6ac!=='string'||_0x43e6ac[_0x310ac2(0x333)]('..')||_0x43e6ac['includes']('/')||_0x43e6ac[_0x310ac2(0x333)]('\x5c')||_0xd9de36['isAbsolute'](_0x43e6ac)){_0x428d66['statusCode']=0xc43+0x1537+0xff5*-0x2,_0x428d66[_0x254a6c(0x10f)](JSON[_0x254a6c(0x13f)]({'error':'Invalid\x20sk'+_0x254a6c(0x2e5)}));return;}const _0x1d61cb=resolve(SKILLS_DIR,_0x43e6ac),_0x30ffee=resolve(SKILLS_DIR,_0x43e6ac+_0x310ac2(0x327));if(_0x2929a5[_0x310ac2(0x1c0)](_0x1d61cb))_0x2929a5[_0x310ac2(0x188)](_0x1d61cb,{'recursive':!![]});else{if(_0x2929a5[_0x310ac2(0x1c0)](_0x30ffee))_0x2929a5[_0x254a6c(0x14a)](_0x30ffee);else{_0x428d66[_0x254a6c(0x34d)]=0x1a32+0x16aa+-0x4*0xbd2,_0x428d66[_0x254a6c(0x10f)](JSON[_0x310ac2(0x13f)]({'error':_0x254a6c(0x330)+'found'}));return;}}const {loadSkills:_0x5e81a5}=await import(_0x254a6c(0x370)+'s/skills.j'+'s');_0x5e81a5(),_0x428d66['end'](JSON[_0x310ac2(0x13f)]({'ok':!![]}));}catch(_0x2ce149){_0x428d66['statusCode']=0x21e5+-0x9fb+-0x165a,_0x428d66['end'](JSON['stringify']({'error':_0x2ce149 instanceof Error?_0x2ce149[_0x254a6c(0x350)]:String(_0x2ce149)}));}return;}if(_0x1ec270===_0x310ac2(0x1fe)+'g'){_0x428d66[_0x310ac2(0x10f)](JSON['stringify']({'providers':config['fallbackPr'+_0x310ac2(0x2ca)],'primaryProvider':config[_0x310ac2(0x117)+_0x254a6c(0x14e)],'allowedUsers':config[_0x254a6c(0x318)+'rs'],'hasKeys':{'groq':!!config[_0x310ac2(0x1f1)]['groq'],'openai':!!config['apiKeys'][_0x310ac2(0x1ab)],'google':!!config[_0x310ac2(0x1f1)][_0x310ac2(0x2c3)],'nvidia':!!config[_0x254a6c(0x1f1)]['nvidia'],'openrouter':!!config[_0x310ac2(0x1f1)][_0x254a6c(0x31e)]}}));return;}if(_0x1ec270==='/api/sessi'+_0x310ac2(0x19f)){const _0x216961=getAllSessions(),_0xd88ea3=listProfiles(),_0x1355ab=Array['from'](_0x216961[_0x254a6c(0x320)]())[_0x310ac2(0x1e6)](([_0x239adf,_0x367c8a])=>{const _0x1f5115=_0x310ac2,_0x54a8ea=_0x310ac2,_0x6edbc3=Number(_0x239adf[_0x1f5115(0x27a)](':')['pop']()),_0x5a22fb=_0xd88ea3[_0x1f5115(0x35a)](_0x2f22ad=>_0x2f22ad[_0x54a8ea(0x336)]===_0x6edbc3);return{'userId':_0x239adf,'name':_0x5a22fb?.[_0x54a8ea(0x2f2)]||'User\x20'+_0x239adf,'username':_0x5a22fb?.[_0x54a8ea(0x129)],'messageCount':_0x367c8a[_0x54a8ea(0x2eb)+'nt'],'toolUseCount':_0x367c8a['toolUseCou'+'nt'],'totalCost':_0x367c8a[_0x1f5115(0x18d)],'totalInputTokens':_0x367c8a[_0x54a8ea(0x347)+_0x1f5115(0x214)]||-0x1*-0x1c87+0x25*-0x67+0x3*-0x48c,'totalOutputTokens':_0x367c8a[_0x54a8ea(0x128)+_0x54a8ea(0x14c)]||0x1eb*0xa+-0x2689+-0x135b*-0x1,'effort':_0x367c8a[_0x54a8ea(0x162)],'startedAt':_0x367c8a['startedAt'],'lastActivity':_0x367c8a[_0x54a8ea(0x12d)+'ty'],'historyLength':_0x367c8a[_0x54a8ea(0x34e)][_0x1f5115(0x37f)],'isProcessing':_0x367c8a[_0x1f5115(0x103)+'ng'],'provider':Object[_0x54a8ea(0x287)](_0x367c8a[_0x1f5115(0x291)+_0x54a8ea(0x373)])[_0x54a8ea(0x17b)](',\x20')||_0x54a8ea(0x2d9)};});_0x428d66[_0x310ac2(0x10f)](JSON['stringify']({'sessions':_0x1355ab}));return;}if(_0x1ec270[_0x310ac2(0x216)](/^\/api\/sessions\/\d+\/history$/)){const _0x57e374=parseInt(_0x1ec270[_0x310ac2(0x27a)]('/')[-0x1fc8+0x2*0x99d+0xc91]),_0x38541a=getSession(_0x57e374);_0x428d66[_0x254a6c(0x10f)](JSON[_0x254a6c(0x13f)]({'userId':_0x57e374,'history':_0x38541a['history'][_0x310ac2(0x1e6)](_0x1f63e7=>({'role':_0x1f63e7[_0x310ac2(0x2e1)],'content':_0x1f63e7['content'][_0x254a6c(0x124)](-0x142d*0x1+0x2181+0x355*-0x4,-0x48+0x215d+-0x1945)}))}));return;}if(_0x1ec270==='/api/files'){const _0x168b68=new URLSearchParams((_0xbb0ab0[_0x310ac2(0x181)]||'')[_0x310ac2(0x27a)]('?')[0x17fd+0x2014+-0x3810]||''),_0x386507=_0x168b68[_0x310ac2(0x1e4)](_0x310ac2(0x242))||'',_0x554243=resolve(BOT_ROOT,_0x386507||'.');if(!_0x554243['startsWith'](BOT_ROOT)){_0x428d66[_0x254a6c(0x34d)]=0xf9c+0x4*0x8e4+0x1*-0x3199,_0x428d66[_0x254a6c(0x10f)](JSON['stringify']({'error':_0x254a6c(0x1e3)+'ied'}));return;}try{const _0x36fccc=_0x2929a5[_0x310ac2(0x331)](_0x554243);if(_0x36fccc['isDirector'+'y']()){const _0x2de3f8=_0x2929a5[_0x310ac2(0x1b2)+'c'](_0x554243,{'withFileTypes':!![]})[_0x254a6c(0x380)](_0x14ba99=>!_0x14ba99[_0x310ac2(0x2f2)][_0x254a6c(0x1e0)]('.')&&_0x14ba99['name']!==_0x310ac2(0x133)+'es')[_0x310ac2(0x1e6)](_0x519889=>({'name':_0x519889['name'],'type':_0x519889[_0x254a6c(0x247)+'y']()?_0x254a6c(0x365):_0x310ac2(0x342),'size':_0x519889[_0x310ac2(0x116)]()?_0x2929a5['statSync'](resolve(_0x554243,_0x519889['name']))[_0x310ac2(0x1ec)]:-0x74*-0x40+-0x535+-0x17cb,'modified':_0x2929a5['statSync'](resolve(_0x554243,_0x519889['name']))['mtimeMs']}))[_0x254a6c(0x199)]((_0x129c21,_0x3ed771)=>{const _0x11ee1e=_0x310ac2,_0x4b87d4=_0x254a6c;if(_0x129c21['type']!==_0x3ed771[_0x11ee1e(0x11b)])return _0x129c21[_0x4b87d4(0x11b)]==='dir'?-(0xc51+-0x15da+0x6f*0x16):0xab5*-0x2+-0x2*0x1c5+0x18f5*0x1;return _0x129c21[_0x11ee1e(0x2f2)]['localeComp'+_0x11ee1e(0x251)](_0x3ed771['name']);});_0x428d66[_0x254a6c(0x10f)](JSON[_0x254a6c(0x13f)]({'path':_0x386507||'.','entries':_0x2de3f8}));}else{const _0x4b9721=_0xd9de36[_0x254a6c(0x309)](_0x554243)['toLowerCas'+'e'](),_0x312ec5=new Set([_0x310ac2(0x327),_0x310ac2(0x11f),_0x310ac2(0x23d),_0x254a6c(0x378),_0x254a6c(0x134),_0x254a6c(0x297),_0x310ac2(0x2ae),_0x310ac2(0x2e4),_0x310ac2(0x25b),_0x310ac2(0x1ce),_0x310ac2(0x1d3),_0x254a6c(0x19a),'.yml','.yaml',_0x310ac2(0x2e3),'.ini',_0x310ac2(0x2a6),_0x254a6c(0x223),'.env',_0x254a6c(0x2fa),_0x254a6c(0x235),_0x310ac2(0x20a),_0x254a6c(0x1f8),_0x310ac2(0x307),_0x254a6c(0x1eb),_0x254a6c(0x371),_0x310ac2(0x31d),_0x310ac2(0x148),_0x254a6c(0x2b7),'.c','.cpp','.h',_0x254a6c(0x1dc),_0x310ac2(0x313),_0x254a6c(0x249),'.sql',_0x254a6c(0x2a4),_0x254a6c(0x2d6),_0x254a6c(0x215)+'e',_0x310ac2(0x115),_0x310ac2(0x1fb)+_0x310ac2(0x1c1),_0x254a6c(0x154)+'fig',_0x310ac2(0x126)+'c',_0x310ac2(0x122),_0x254a6c(0x209),_0x310ac2(0x1d6),_0x254a6c(0x2b3),_0x254a6c(0x326),_0x310ac2(0x130),_0x310ac2(0x127),_0x254a6c(0x2a7),_0x310ac2(0x12a),_0x310ac2(0x107),_0x310ac2(0x31f),_0x310ac2(0x288),_0x254a6c(0x1ef),'.svelte',_0x254a6c(0x1d9)]),_0x553ce1=new Set(['dockerfile',_0x310ac2(0x349),_0x254a6c(0x2cb),_0x254a6c(0x277),_0x254a6c(0x196),_0x254a6c(0x149)+'e',_0x310ac2(0x189),_0x254a6c(0x34b),_0x254a6c(0x11a),_0x254a6c(0x24c),'license','licence',_0x254a6c(0x33c),'changelog',_0x254a6c(0x15a),'contributo'+'rs']),_0x303b65=_0xd9de36[_0x254a6c(0x28a)](_0x554243)[_0x254a6c(0x33a)+'e'](),_0x2887c6=_0x553ce1[_0x254a6c(0x25f)](_0x303b65),_0x23e842=_0x312ec5[_0x310ac2(0x25f)](_0x4b9721)||_0x2887c6||!_0x4b9721&&_0x36fccc[_0x310ac2(0x1ec)]<0x1a6c1+0x1*-0x21196+0x1f175;if(_0x36fccc[_0x310ac2(0x1ec)]>0x1fca6+0x71959+-0x174df)_0x428d66[_0x254a6c(0x10f)](JSON[_0x310ac2(0x13f)]({'path':_0x386507,'content':_0x254a6c(0x340)+_0x310ac2(0x337)+(_0x36fccc[_0x310ac2(0x1ec)]/(0x1abc+-0x1d7a+-0x6be*-0x1))[_0x254a6c(0x21a)](-0x3*-0xc33+0x17*-0x157+-0x5c7)+('\x20KB\x20—\x20max\x20'+_0x310ac2(0x2ba)),'size':_0x36fccc[_0x254a6c(0x1ec)]}));else{if(_0x23e842)try{const _0x36cfe9=_0x2929a5[_0x254a6c(0x271)+'nc'](_0x554243,'utf-8'),_0x151290=[..._0x36cfe9[_0x310ac2(0x124)](0x14f1+-0x7*-0x482+0x1*-0x347f,0x871*-0x1+-0x4*-0x182+0x31*0x21)]['filter'](_0x269896=>_0x269896==='\x00')[_0x254a6c(0x37f)];_0x151290>-0xcff+-0x2f*0x40+0x1923?_0x428d66['end'](JSON[_0x254a6c(0x13f)]({'path':_0x386507,'content':null,'size':_0x36fccc[_0x310ac2(0x1ec)],'binary':!![]})):_0x428d66[_0x310ac2(0x10f)](JSON[_0x254a6c(0x13f)]({'path':_0x386507,'content':_0x36cfe9,'size':_0x36fccc[_0x254a6c(0x1ec)]}));}catch{_0x428d66[_0x310ac2(0x10f)](JSON[_0x254a6c(0x13f)]({'path':_0x386507,'content':null,'size':_0x36fccc['size'],'binary':!![]}));}else _0x428d66[_0x254a6c(0x10f)](JSON[_0x310ac2(0x13f)]({'path':_0x386507,'content':null,'size':_0x36fccc[_0x254a6c(0x1ec)],'binary':!![]}));}}}catch{_0x428d66['statusCode']=-0x1efd+0x14a1+0x8*0x17e,_0x428d66[_0x254a6c(0x10f)](JSON[_0x310ac2(0x13f)]({'error':_0x254a6c(0x1f6)}));}return;}if(_0x1ec270===_0x254a6c(0x230)+_0x254a6c(0x1af)&&_0xbb0ab0['method']==='POST'){try{const {path:_0x1e15d1,content:_0x4b91e9}=JSON['parse'](_0x15594d),_0x4a390d=resolve(BOT_ROOT,_0x1e15d1);if(!_0x4a390d[_0x310ac2(0x1e0)](BOT_ROOT)){_0x428d66['statusCode']=0x2*-0x45b+-0x7*-0x448+0x1*-0x13af,_0x428d66[_0x254a6c(0x10f)](JSON[_0x310ac2(0x13f)]({'error':'Access\x20den'+_0x310ac2(0x168)}));return;}_0x2929a5[_0x310ac2(0x37d)+_0x254a6c(0x1f9)](_0x4a390d,_0x4b91e9),_0x428d66[_0x254a6c(0x10f)](JSON[_0x310ac2(0x13f)]({'ok':!![]}));}catch(_0x26114d){_0x428d66['statusCode']=-0xbb*-0x7+0x28*0x2+-0x3dd;const _0x45aad4=_0x26114d instanceof Error?_0x26114d[_0x310ac2(0x350)]:'Invalid\x20re'+'quest';_0x428d66[_0x254a6c(0x10f)](JSON[_0x310ac2(0x13f)]({'error':_0x45aad4}));}return;}if(_0x1ec270===_0x310ac2(0x230)+'/delete'&&_0xbb0ab0[_0x310ac2(0x159)]===_0x254a6c(0x123)){try{const {path:_0x22d99d}=JSON[_0x254a6c(0x302)](_0x15594d),_0x28aefd=resolve(BOT_ROOT,_0x22d99d);if(!_0x28aefd[_0x254a6c(0x1e0)](BOT_ROOT)){_0x428d66[_0x254a6c(0x34d)]=-0x828+-0x1ffd+0x29b8,_0x428d66[_0x254a6c(0x10f)](JSON['stringify']({'error':'Access\x20den'+'ied'}));return;}const _0x19c31f=[_0x310ac2(0x274),_0x310ac2(0x142)+'on',_0x254a6c(0x111)+_0x310ac2(0x1f3),'ecosystem.'+_0x310ac2(0x2cd)],_0x25872f=_0xd9de36[_0x310ac2(0x28a)](_0x28aefd);if(_0x19c31f[_0x254a6c(0x333)](_0x25872f)){_0x428d66[_0x310ac2(0x34d)]=-0x120a+-0x35e+-0x16fb*-0x1,_0x428d66[_0x254a6c(0x10f)](JSON[_0x310ac2(0x13f)]({'error':_0x25872f+(_0x254a6c(0x172)+'\x20deleted\x20('+'protected)')}));return;}if(!_0x2929a5[_0x310ac2(0x1c0)](_0x28aefd)){_0x428d66['statusCode']=0xfbb+0x22c2+-0x30e9,_0x428d66[_0x310ac2(0x10f)](JSON['stringify']({'error':_0x310ac2(0x36b)+_0x310ac2(0x100)}));return;}const _0x7d237b=_0x2929a5[_0x310ac2(0x331)](_0x28aefd);if(_0x7d237b['isDirector'+'y']()){_0x428d66[_0x254a6c(0x34d)]=-0xd71+0x2573*-0x1+0x3474,_0x428d66[_0x254a6c(0x10f)](JSON[_0x310ac2(0x13f)]({'error':_0x310ac2(0x310)+'s\x20cannot\x20b'+'e\x20deleted'}));return;}_0x2929a5[_0x254a6c(0x14a)](_0x28aefd),_0x428d66[_0x310ac2(0x10f)](JSON[_0x310ac2(0x13f)]({'ok':!![]}));}catch(_0x582754){_0x428d66[_0x310ac2(0x34d)]=0x1b81*0x1+-0x1bb+-0x1836*0x1;const _0x43a911=_0x582754 instanceof Error?_0x582754['message']:_0x254a6c(0x32e)+_0x310ac2(0x1bd);_0x428d66[_0x254a6c(0x10f)](JSON[_0x254a6c(0x13f)]({'error':_0x43a911}));}return;}if(_0x1ec270==='/api/termi'+_0x310ac2(0x1c6)&&_0xbb0ab0[_0x310ac2(0x159)]===_0x254a6c(0x123)){try{const {command:_0x22defd}=JSON['parse'](_0x15594d);if(!_0x22defd){_0x428d66[_0x310ac2(0x34d)]=0x1f94+-0x941*-0x1+0xd17*-0x3,_0x428d66[_0x254a6c(0x10f)](JSON[_0x310ac2(0x13f)]({'error':_0x310ac2(0x1aa)}));return;}if(_0x22defd[_0x310ac2(0x37f)]>-0x13*0x296+0xff*-0x2b+0x8307){_0x428d66[_0x310ac2(0x34d)]=-0x2675+0x1*-0x11d1+0x39d6*0x1,_0x428d66[_0x254a6c(0x10f)](JSON[_0x254a6c(0x13f)]({'error':'Command\x20to'+'o\x20long\x20(ma'+'x\x2010000\x20ch'+'ars)'}));return;}const _0x41f5a9=typeof JSON['parse'](_0x15594d)[_0x254a6c(0x192)]==='string'?resolve(JSON[_0x310ac2(0x302)](_0x15594d)[_0x310ac2(0x192)]):BOT_ROOT,_0x2101c2=execSync(_0x22defd,{'cwd':_0x41f5a9,'stdio':'pipe','timeout':0x1d4c0,'env':{...process[_0x310ac2(0x1c8)],'PATH':process['env'][_0x254a6c(0x31b)]+(_0x310ac2(0x25c)+_0x310ac2(0x29c)+'usr/local/'+'bin')}})[_0x310ac2(0x12c)]();_0x428d66[_0x254a6c(0x10f)](JSON[_0x254a6c(0x13f)]({'output':_0x2101c2[_0x254a6c(0x124)](0x1b7d+0x1082+-0x2bff,-0xe2a9+0x2*0x521e+0x1c50d*0x1)}));}catch(_0x108450){const _0x2b8ced=_0x108450,_0x3f5bec=_0x2b8ced['stderr']?.[_0x310ac2(0x12c)]()?.[_0x310ac2(0x1e7)]()||'';_0x428d66[_0x310ac2(0x10f)](JSON[_0x254a6c(0x13f)]({'output':_0x3f5bec||_0x2b8ced['message'],'exitCode':0x1}));}return;}if(_0x1ec270==='/api/env'){try{const _0x4cefa3=_0x2929a5[_0x310ac2(0x1c0)](ENV_FILE)?_0x2929a5['readFileSy'+'nc'](ENV_FILE,_0x254a6c(0x240)):'',_0x1a7b27=_0x4cefa3[_0x310ac2(0x27a)]('\x0a')['filter'](_0x144c80=>_0x144c80['includes']('=')&&!_0x144c80[_0x254a6c(0x1e0)]('#')),_0x24989d=_0x1a7b27['map'](_0x6d082a=>{const _0x23ecfc=_0x254a6c,_0x103b37=_0x254a6c,[_0x4208d2,..._0x40d3c8]=_0x6d082a['split']('='),_0x43c254=_0x40d3c8['join']('=')[_0x23ecfc(0x1e7)](),_0x4f486b=_0x4208d2[_0x23ecfc(0x333)]('KEY')||_0x4208d2[_0x23ecfc(0x333)](_0x103b37(0x2d8))||_0x4208d2['includes'](_0x23ecfc(0x140))||_0x4208d2[_0x23ecfc(0x333)]('SECRET')?_0x43c254['length']>0xf9c+0xd3b+0x1cd3*-0x1?_0x43c254[_0x23ecfc(0x124)](-0x6e3+0x4cb*0x1+-0x2*-0x10c,0x132e+-0x2394+0x106a)+'...'+_0x43c254[_0x103b37(0x124)](-(0x79f*0x3+0x4c9+0x189*-0x12)):_0x103b37(0x2dc):_0x43c254;return{'key':_0x4208d2[_0x23ecfc(0x1e7)](),'value':_0x4f486b,'hasValue':_0x43c254[_0x23ecfc(0x37f)]>-0x3d*-0x29+0x1012+0x1b9*-0xf};});_0x428d66[_0x310ac2(0x10f)](JSON[_0x310ac2(0x13f)]({'vars':_0x24989d}));}catch{_0x428d66['end'](JSON['stringify']({'vars':[]}));}return;}if(_0x1ec270===_0x310ac2(0x316)+'et'&&_0xbb0ab0[_0x254a6c(0x159)]===_0x254a6c(0x123)){try{const {key:_0x1a7bd9,value:_0x414b08}=JSON[_0x310ac2(0x302)](_0x15594d);if(!_0x1a7bd9||typeof _0x1a7bd9!==_0x254a6c(0x2c8)||!_0x1a7bd9[_0x254a6c(0x216)](/^[A-Z_][A-Z0-9_]*$/)){_0x428d66[_0x254a6c(0x34d)]=0x1f96+-0x2*0x8e+-0x1cea,_0x428d66[_0x254a6c(0x10f)](JSON[_0x254a6c(0x13f)]({'error':_0x254a6c(0x1fd)+_0x310ac2(0x1ee)}));return;}if(typeof _0x414b08===_0x254a6c(0x2c8)&&/[\n\r]/['test'](_0x414b08)){_0x428d66[_0x254a6c(0x34d)]=-0x1*0x1f9a+-0x14f3+0x361d,_0x428d66[_0x310ac2(0x10f)](JSON['stringify']({'error':_0x254a6c(0x2c4)+_0x310ac2(0x328)+_0x254a6c(0x357)+_0x310ac2(0x30f)+_0x254a6c(0x1ba)}));return;}let _0x3061eb=_0x2929a5[_0x310ac2(0x1c0)](ENV_FILE)?_0x2929a5[_0x310ac2(0x271)+'nc'](ENV_FILE,_0x310ac2(0x240)):'';const _0x5dc7e2=new RegExp('^'+_0x1a7bd9+'=.*$','m');_0x5dc7e2[_0x254a6c(0x352)](_0x3061eb)?_0x3061eb=_0x3061eb[_0x254a6c(0x2f6)](_0x5dc7e2,_0x1a7bd9+'='+_0x414b08):_0x3061eb=_0x3061eb[_0x254a6c(0x22e)]()+('\x0a'+_0x1a7bd9+'='+_0x414b08+'\x0a'),writeSecure(ENV_FILE,_0x3061eb),_0x428d66['end'](JSON[_0x310ac2(0x13f)]({'ok':!![],'note':_0x254a6c(0x2e9)+'quired\x20for'+_0x254a6c(0x1a1)+_0x254a6c(0x253)+'ect'}));}catch{_0x428d66[_0x310ac2(0x34d)]=-0x9cd+0x180f+0x145*-0xa,_0x428d66['end'](JSON[_0x254a6c(0x13f)]({'error':'Invalid\x20re'+_0x254a6c(0x1bd)}));}return;}if(_0x1ec270==='/api/soul'){const _0x160fa2=getSoulContent();_0x428d66[_0x310ac2(0x10f)](JSON[_0x310ac2(0x13f)]({'content':_0x160fa2}));return;}if(_0x1ec270===_0x254a6c(0x276)+_0x310ac2(0x16d)&&_0xbb0ab0['method']==='POST'){try{const {content:_0x5aa558}=JSON[_0x254a6c(0x302)](_0x15594d),_0x5f3baa=SOUL_FILE;_0x2929a5[_0x254a6c(0x37d)+_0x310ac2(0x1f9)](_0x5f3baa,_0x5aa558),reloadSoul(),_0x428d66['end'](JSON[_0x254a6c(0x13f)]({'ok':!![]}));}catch{_0x428d66[_0x254a6c(0x34d)]=-0xa*0x21e+0x1bc*-0x8+0x249c,_0x428d66['end'](JSON[_0x310ac2(0x13f)]({'error':_0x310ac2(0x32e)+_0x254a6c(0x1bd)}));}return;}if(_0x1ec270===_0x310ac2(0x202)+_0x254a6c(0x1e5)){const _0x3a266f=[{'name':_0x310ac2(0x2f1),'key':'BOT_TOKEN','icon':'📱','configured':!!process[_0x254a6c(0x1c8)][_0x310ac2(0x198)]},{'name':_0x254a6c(0x1ae),'key':'DISCORD_TO'+_0x310ac2(0x26a),'icon':'🎮','configured':!!process[_0x254a6c(0x1c8)][_0x310ac2(0x2a3)+'KEN']},{'name':_0x254a6c(0x1db),'key':_0x254a6c(0x268)+'NABLED','icon':'💬','configured':process[_0x254a6c(0x1c8)][_0x310ac2(0x268)+_0x254a6c(0x30b)]==='true'},{'name':'Signal','key':_0x254a6c(0x16a)+_0x254a6c(0x2f4),'icon':'🔒','configured':!!process[_0x254a6c(0x1c8)][_0x254a6c(0x16a)+_0x254a6c(0x2f4)]},{'name':_0x254a6c(0x224),'key':_0x254a6c(0x10e),'icon':'🌐','configured':!![]}];_0x428d66[_0x254a6c(0x10f)](JSON['stringify']({'platforms':_0x3a266f}));return;}if(_0x1ec270===_0x310ac2(0x27c)+'rt'&&_0xbb0ab0['method']===_0x254a6c(0x123)){const {scheduleGracefulRestart:_0x2bfa86}=await import(_0x254a6c(0x370)+_0x310ac2(0x1a5)+'js');_0x428d66[_0x310ac2(0x10f)](JSON['stringify']({'ok':!![],'note':_0x254a6c(0x18b)+_0x310ac2(0x2b6)})),_0x2bfa86(0x1*-0xf61+0xf73+0x1e2);return;}if(_0x1ec270===_0x310ac2(0x325)+_0x310ac2(0x1d7)&&_0xbb0ab0[_0x310ac2(0x159)]===_0x310ac2(0x123)){try{const {messages:_0x5777fa,format:_0x4867a4}=JSON['parse'](_0x15594d);if(_0x4867a4===_0x254a6c(0x255))_0x428d66[_0x254a6c(0x376)](_0x254a6c(0x341)+'pe',_0x310ac2(0x263)+_0x254a6c(0x24b)),_0x428d66['end'](JSON[_0x254a6c(0x13f)]({'export':_0x5777fa},null,-0x1814+0x10f9*0x1+0x25f*0x3));else{const _0xc4ad9a=_0x5777fa[_0x254a6c(0x1e6)](_0x5d7e54=>{const _0x53e470=_0x310ac2,_0x392073=_0x310ac2,_0x1aed9a=_0x5d7e54[_0x53e470(0x2e1)]===_0x53e470(0x2cf)?'**Du:**':_0x5d7e54[_0x392073(0x2e1)]===_0x392073(0x27f)?_0x53e470(0x164)+_0x392073(0x36c):'*System:*',_0x2cf888=_0x5d7e54[_0x53e470(0x19c)]?_0x53e470(0x324)+_0x5d7e54[_0x53e470(0x19c)]+')_':'';return''+_0x1aed9a+_0x2cf888+'\x0a'+_0x5d7e54[_0x53e470(0x362)]+'\x0a';})[_0x254a6c(0x17b)](_0x254a6c(0x37b));_0x428d66[_0x310ac2(0x376)]('Content-Ty'+'pe','text/markd'+_0x310ac2(0x280)),_0x428d66[_0x254a6c(0x10f)](_0x310ac2(0x20f)+'ort\x20—\x20Alvi'+_0x310ac2(0x155)+new Date()[_0x310ac2(0x2f5)+_0x310ac2(0x1bb)](_0x254a6c(0x11c))+_0x310ac2(0x2d1)+_0xc4ad9a);}}catch{_0x428d66[_0x310ac2(0x34d)]=0x395+-0x1fa6+-0x1*-0x1da1,_0x428d66[_0x254a6c(0x10f)](JSON[_0x310ac2(0x13f)]({'error':'Invalid\x20re'+_0x310ac2(0x1bd)}));}return;}if(_0x1ec270===_0x310ac2(0x15b)+_0x254a6c(0x137)&&_0xbb0ab0[_0x254a6c(0x159)]==='GET'){try{const {getWhatsAppAdapter:_0x1ab316}=await import('../platfor'+_0x310ac2(0x157)+_0x310ac2(0x294)),_0x544237=_0x1ab316();if(!_0x544237){_0x428d66['end'](JSON[_0x310ac2(0x13f)]({'groups':[],'error':_0x254a6c(0x1fa)+'icht\x20verbu'+'nden'}));return;}const _0x447a15=await _0x544237[_0x310ac2(0x121)]();_0x428d66['end'](JSON[_0x310ac2(0x13f)]({'groups':_0x447a15}));}catch(_0x2d8fcd){_0x428d66['end'](JSON[_0x254a6c(0x13f)]({'groups':[],'error':String(_0x2d8fcd)}));}return;}if(_0x1ec270[_0x254a6c(0x216)](/^\/api\/whatsapp\/groups\/[^/]+\/participants$/)){try{const _0x27f697=decodeURIComponent(_0x1ec270[_0x310ac2(0x27a)]('/')[-0x116*0x23+0x219c+-0x5*-0xe2]),{getWhatsAppAdapter:_0x3d41bc}=await import('../platfor'+_0x254a6c(0x157)+_0x310ac2(0x294)),_0x3c0ebe=_0x3d41bc();if(!_0x3c0ebe){_0x428d66['end'](JSON[_0x310ac2(0x13f)]({'participants':[],'error':_0x254a6c(0x1fa)+'icht\x20verbu'+_0x310ac2(0x11d)}));return;}const _0x569d05=await _0x3c0ebe[_0x254a6c(0x35f)+_0x254a6c(0x308)](_0x27f697);_0x428d66[_0x310ac2(0x10f)](JSON[_0x310ac2(0x13f)]({'participants':_0x569d05}));}catch(_0x4aec0e){_0x428d66[_0x310ac2(0x10f)](JSON[_0x254a6c(0x13f)]({'participants':[],'error':String(_0x4aec0e)}));}return;}if(_0x1ec270==='/api/whats'+_0x310ac2(0x32d)+_0x310ac2(0x110)&&_0xbb0ab0[_0x254a6c(0x159)]===_0x254a6c(0x34a)){const {getGroupRules:_0x1fc91b}=await import(_0x310ac2(0x1cd)+_0x254a6c(0x157)+'p.js');_0x428d66[_0x254a6c(0x10f)](JSON[_0x254a6c(0x13f)]({'rules':_0x1fc91b()}));return;}if(_0x1ec270===_0x254a6c(0x15b)+_0x310ac2(0x32d)+_0x254a6c(0x110)&&_0xbb0ab0['method']===_0x310ac2(0x123)){try{const _0x5ce708=JSON[_0x310ac2(0x302)](_0x15594d);if(!_0x5ce708[_0x254a6c(0x20c)]){_0x428d66['statusCode']=0x251b+0x119*0xd+0x8*-0x63a,_0x428d66[_0x254a6c(0x10f)](JSON['stringify']({'error':'groupId\x20is'+'t\x20erforder'+_0x254a6c(0x311)}));return;}const {upsertGroupRule:_0x2a4264}=await import(_0x254a6c(0x1cd)+_0x254a6c(0x157)+_0x254a6c(0x294)),_0x211734=_0x2a4264(_0x5ce708);_0x428d66['end'](JSON[_0x310ac2(0x13f)]({'ok':!![],'rule':_0x211734}));}catch(_0x577d8c){_0x428d66[_0x310ac2(0x34d)]=0x124a+-0x21b6+0x10fc,_0x428d66['end'](JSON[_0x310ac2(0x13f)]({'error':String(_0x577d8c)}));}return;}if(_0x1ec270['match'](/^\/api\/whatsapp\/group-rules\//)&&_0xbb0ab0[_0x310ac2(0x159)]==='DELETE'){if(isExposedWithoutPassword()){_0x428d66[_0x254a6c(0x34d)]=0x265d+-0x17d*0x4+-0x1ed6*0x1,_0x428d66[_0x254a6c(0x10f)](JSON[_0x254a6c(0x13f)]({'error':_0x254a6c(0x160)+_0x310ac2(0x25d)+_0x310ac2(0x33b)+_0x310ac2(0x296)+'D'}));return;}const _0x14d351=decodeURIComponent(_0x1ec270[_0x310ac2(0x27a)]('/')[_0x254a6c(0x124)](0x1*-0x17cf+0x8d7+0xefc)[_0x254a6c(0x17b)]('/')),{deleteGroupRule:_0x373d99}=await import('../platfor'+_0x310ac2(0x157)+'p.js'),_0x811f42=_0x373d99(_0x14d351);_0x428d66['end'](JSON['stringify']({'ok':_0x811f42}));return;}_0x428d66['statusCode']=0x1*0x2429+-0xa86+0x180f*-0x1,_0x428d66[_0x254a6c(0x10f)](JSON['stringify']({'error':_0x310ac2(0x1f6)}));}const chatClients=new Set();broadcast['on'](_0x217a38(0x332),_0x4a5fe9=>{const _0x410e44=_0x217a38,_0x113967=_0x3da471;if(_0x4a5fe9[_0x410e44(0x22d)]!==_0x113967(0x2d2))return;const _0x8802e6=JSON['stringify']({'type':'mirror:use'+_0x410e44(0x145),'text':_0x4a5fe9[_0x410e44(0x362)],'platform':_0x4a5fe9['platform'],'userName':_0x4a5fe9['userName'],'ts':_0x4a5fe9['ts']});for(const _0x313d5b of chatClients){if(_0x313d5b[_0x113967(0x1c5)]===WebSocket[_0x410e44(0x2de)])_0x313d5b[_0x113967(0x266)](_0x8802e6);}}),broadcast['on']('response_s'+'tart',_0x20b0de=>{const _0x42db7e=_0x3da471,_0x89c228=_0x217a38;if(_0x20b0de[_0x42db7e(0x22d)]!==_0x42db7e(0x2d2))return;const _0x117353=JSON[_0x89c228(0x13f)]({'type':'mirror:res'+_0x89c228(0x1ff)+'t','platform':_0x20b0de['platform'],'ts':_0x20b0de['ts']});for(const _0x2b4fbd of chatClients){if(_0x2b4fbd['readyState']===WebSocket[_0x89c228(0x2de)])_0x2b4fbd['send'](_0x117353);}}),broadcast['on'](_0x3da471(0x28d)+_0x217a38(0x1f4),_0x2f5b99=>{const _0x1873ac=_0x217a38,_0x3d4d51=_0x217a38;if(_0x2f5b99[_0x1873ac(0x22d)]!=='telegram')return;const _0x2b0ca7=JSON[_0x1873ac(0x13f)]({'type':_0x1873ac(0x13a)+_0x1873ac(0x1d4)+'a','delta':_0x2f5b99[_0x1873ac(0x293)],'platform':_0x2f5b99[_0x1873ac(0x22d)],'ts':_0x2f5b99['ts']});for(const _0x23efa2 of chatClients){if(_0x23efa2[_0x1873ac(0x1c5)]===WebSocket[_0x3d4d51(0x2de)])_0x23efa2[_0x3d4d51(0x266)](_0x2b0ca7);}}),broadcast['on'](_0x217a38(0x28d)+'one',_0x29d102=>{const _0xc066f=_0x217a38,_0x3e0985=_0x217a38;if(_0x29d102[_0xc066f(0x22d)]!==_0x3e0985(0x2d2))return;const _0xb26cf2=JSON[_0xc066f(0x13f)]({'type':'mirror:res'+_0x3e0985(0x225),'cost':_0x29d102[_0x3e0985(0x379)],'platform':_0x29d102[_0x3e0985(0x22d)],'ts':_0x29d102['ts']});for(const _0x5f50a2 of chatClients){if(_0x5f50a2[_0xc066f(0x1c5)]===WebSocket[_0x3e0985(0x2de)])_0x5f50a2[_0xc066f(0x266)](_0xb26cf2);}});function handleWebSocket(_0x51d436){_0x51d436['on']('connection',(_0x26769b,_0x331571)=>{const _0x5e5722=_0x4a44,_0x1c302d=_0x4a44;if(WEB_PASSWORD&&!checkAuth(_0x331571)){_0x26769b['close'](0x1d31+-0x4*0x64e+0xba8,_0x5e5722(0x2e0)+'ticated');return;}const _0x2ec326=_0x331571[_0x1c302d(0x181)]||'/';if(_0x2ec326===_0x1c302d(0x1c9)){addCanvasClient(_0x26769b);return;}console[_0x5e5722(0x2bf)](_0x1c302d(0x37c)+_0x1c302d(0x285)+_0x5e5722(0x234)),chatClients[_0x5e5722(0x2e2)](_0x26769b),_0x26769b['on'](_0x5e5722(0x350),async _0x17514a=>{const _0x5612a7=_0x1c302d,_0xd80e73=_0x1c302d;try{const _0x2e0383=JSON['parse'](_0x17514a[_0x5612a7(0x12c)]());if(_0x2e0383[_0xd80e73(0x11b)]===_0x5612a7(0x32f)){let {text:_0x27d8d6,effort:_0x581fa9,file:_0x4448b6}=_0x2e0383;const _0x2b5134=_0x2e0383[_0xd80e73(0x239)],_0x29c455=config[_0x5612a7(0x318)+'rs'][0x49*-0x3f+-0x2160+0x3357*0x1]||0x1*-0x2670+-0xf3e+0x35ae;let _0x219d19;if(_0x2b5134==='tui'&&typeof _0x2e0383[_0x5612a7(0x2c9)]===_0xd80e73(0x2c8)&&_0x2e0383[_0xd80e73(0x2c9)][_0x5612a7(0x1e0)](_0xd80e73(0x14d)))_0x219d19=_0x2e0383[_0x5612a7(0x2c9)];else _0x2b5134==='telegram'?_0x219d19=_0x29c455:_0x219d19=_0x29c455;if(_0x4448b6?.[_0xd80e73(0x109)]&&_0x4448b6?.[_0xd80e73(0x2f2)])try{const _0x269ec1=resolve(DATA_DIR,_0xd80e73(0x281)+'s');if(!_0x2929a5['existsSync'](_0x269ec1))_0x2929a5[_0xd80e73(0x2ed)](_0x269ec1,{'recursive':!![]});const _0x56cfce=_0x4448b6['name'][_0x5612a7(0x2f6)](/[^a-zA-Z0-9._-]/g,'_'),_0xb60c53=resolve(_0x269ec1,Date[_0x5612a7(0x315)]()+'_'+_0x56cfce),_0x55b051=_0x4448b6[_0xd80e73(0x109)][_0xd80e73(0x27a)](',')[0x23f8+0x1226*0x1+0x1*-0x361d]||_0x4448b6[_0x5612a7(0x109)];_0x2929a5[_0xd80e73(0x37d)+_0x5612a7(0x1f9)](_0xb60c53,Buffer[_0xd80e73(0x213)](_0x55b051,_0xd80e73(0x104))),_0x27d8d6=_0x27d8d6[_0x5612a7(0x2f6)](/\[File attached:.*?\]/,'[File\x20save'+_0x5612a7(0x2f9)+_0xb60c53+']');}catch(_0xd01a28){console['error'](_0x5612a7(0x2f8)+_0x5612a7(0x191)+_0xd80e73(0x193),_0xd01a28);}const _0x42acff=getRegistry(),_0x40e1ac=_0x42acff[_0x5612a7(0x139)](),_0x23400a=_0x40e1ac[_0x5612a7(0x1a4)][_0xd80e73(0x11b)]===_0x5612a7(0x187),_0x482c1a=getSession(_0x219d19),_0x1ebe5b={'prompt':_0x27d8d6,'systemPrompt':buildSystemPrompt(_0x23400a,_0x482c1a[_0xd80e73(0x2e7)],_0x2b5134===_0xd80e73(0x2d2)?'telegram':_0xd80e73(0x299)+_0x5612a7(0x232)),'workingDir':_0x482c1a['workingDir'],'effort':_0x581fa9||_0x482c1a[_0x5612a7(0x162)],'sessionId':_0x23400a?_0x482c1a[_0xd80e73(0x13e)]:null,'history':!_0x23400a?_0x482c1a['history']:undefined};let _0x439962=![],_0x2413bc='';try{for await(const _0x21d0c9 of _0x42acff[_0xd80e73(0x184)+_0xd80e73(0x244)](_0x1ebe5b)){if(_0x26769b['readyState']!==WebSocket['OPEN'])break;switch(_0x21d0c9[_0xd80e73(0x11b)]){case _0x5612a7(0x362):if(_0x21d0c9['text'])_0x2413bc=_0x21d0c9[_0xd80e73(0x362)];_0x26769b[_0x5612a7(0x266)](JSON['stringify']({'type':_0x5612a7(0x362),'text':_0x21d0c9[_0xd80e73(0x362)],'delta':_0x21d0c9[_0x5612a7(0x293)]}));break;case _0xd80e73(0x222):_0x26769b['send'](JSON[_0xd80e73(0x13f)]({'type':_0x5612a7(0x360),'name':_0x21d0c9[_0x5612a7(0x14f)],'input':_0x21d0c9['toolInput']}));break;case _0xd80e73(0x2cc):_0x439962=!![];if(_0x21d0c9['text'])_0x2413bc=_0x21d0c9[_0x5612a7(0x362)];if(_0x21d0c9[_0xd80e73(0x13e)])_0x482c1a['sessionId']=_0x21d0c9['sessionId'];if(_0x21d0c9[_0xd80e73(0x16b)])_0x482c1a[_0x5612a7(0x18d)]+=_0x21d0c9['costUsd'];if(_0x21d0c9['inputToken'+'s'])_0x482c1a[_0x5612a7(0x347)+'Tokens']=(_0x482c1a['totalInput'+_0x5612a7(0x214)]||0x1bf*0x6+0x17b2+-0x222c)+_0x21d0c9[_0xd80e73(0x165)+'s'];if(_0x21d0c9[_0xd80e73(0x23c)+'ns'])_0x482c1a[_0xd80e73(0x128)+_0xd80e73(0x14c)]=(_0x482c1a[_0xd80e73(0x128)+_0x5612a7(0x14c)]||0xca1+0x15df+-0x8*0x450)+_0x21d0c9[_0x5612a7(0x23c)+'ns'];_0x26769b['send'](JSON[_0xd80e73(0x13f)]({'type':_0xd80e73(0x2cc),'cost':_0x21d0c9['costUsd'],'sessionId':_0x21d0c9[_0xd80e73(0x13e)],'inputTokens':_0x21d0c9[_0x5612a7(0x165)+'s'],'outputTokens':_0x21d0c9[_0xd80e73(0x23c)+'ns'],'sessionTokens':{'input':_0x482c1a['totalInput'+_0xd80e73(0x214)]||0x2409+0x1*0x19a9+-0x1ed9*0x2,'output':_0x482c1a[_0xd80e73(0x128)+_0x5612a7(0x14c)]||0x67f+0x70*0x30+-0x1b7f}}));break;case _0x5612a7(0x26c):_0x26769b[_0x5612a7(0x266)](JSON[_0xd80e73(0x13f)]({'type':_0x5612a7(0x26c),'error':_0x21d0c9[_0xd80e73(0x26c)]})),_0x439962=!![];break;case'fallback':_0x26769b['send'](JSON[_0xd80e73(0x13f)]({'type':'fallback','from':_0x21d0c9['failedProv'+'ider'],'to':_0x21d0c9[_0x5612a7(0x1e9)+'me']}));break;}}!_0x439962&&_0x26769b[_0xd80e73(0x1c5)]===WebSocket[_0xd80e73(0x2de)]&&_0x26769b[_0xd80e73(0x266)](JSON[_0xd80e73(0x13f)]({'type':_0xd80e73(0x2cc),'cost':0x0}));if(_0x2b5134===_0x5612a7(0x2d2)&&_0x2413bc[_0xd80e73(0x1e7)]())try{const _0x5778b9=await import(_0xd80e73(0x370)+_0xd80e73(0x367)+_0xd80e73(0x23f));_0x5778b9['enqueue'](_0xd80e73(0x2d2),String(_0x29c455),_0x2413bc);}catch(_0x3d3f6f){console['error'](_0x5612a7(0x158)+'legram\x20rel'+_0xd80e73(0x2ac),_0x3d3f6f);}}catch(_0x1f3892){const _0x29a7cd=_0x1f3892 instanceof Error?_0x1f3892[_0xd80e73(0x350)]:String(_0x1f3892);console[_0xd80e73(0x26c)]('WebUI\x20stre'+_0xd80e73(0x306),_0x29a7cd),_0x26769b[_0xd80e73(0x1c5)]===WebSocket[_0x5612a7(0x2de)]&&(_0x26769b[_0x5612a7(0x266)](JSON[_0x5612a7(0x13f)]({'type':_0xd80e73(0x26c),'error':_0x29a7cd})),!_0x439962&&_0x26769b[_0x5612a7(0x266)](JSON[_0x5612a7(0x13f)]({'type':_0xd80e73(0x2cc),'cost':0x0})));}}if(_0x2e0383[_0xd80e73(0x11b)]===_0x5612a7(0x29b)){const _0x29e43d=_0x2e0383[_0xd80e73(0x239)],_0xdbd994=config['allowedUse'+'rs'][0x90d+0x1*-0x1d+-0x8f0]||0x22be+0x1ed0+-0x418e;let _0x23867c;_0x29e43d==='tui'&&typeof _0x2e0383[_0x5612a7(0x2c9)]===_0xd80e73(0x2c8)&&_0x2e0383['sessionKey'][_0x5612a7(0x1e0)](_0xd80e73(0x14d))?_0x23867c=_0x2e0383['sessionKey']:_0x23867c=_0xdbd994,resetSession(_0x23867c),_0x26769b['send'](JSON[_0x5612a7(0x13f)]({'type':_0x5612a7(0x29b),'ok':!![]}));}}catch(_0x3d798d){const _0x2cfc37=_0x3d798d instanceof Error?_0x3d798d[_0xd80e73(0x350)]:String(_0x3d798d);_0x26769b[_0xd80e73(0x266)](JSON[_0xd80e73(0x13f)]({'type':_0x5612a7(0x26c),'error':_0x2cfc37}));}}),_0x26769b['on']('close',()=>{const _0x3c3376=_0x1c302d,_0x38fde6=_0x1c302d;console[_0x3c3376(0x2bf)](_0x38fde6(0x37c)+_0x3c3376(0x2b0)+_0x38fde6(0x26b)),chatClients[_0x3c3376(0x1b9)](_0x26769b);});});}function handleWebRequest(_0x3214f2,_0x5b326e){const _0x29b4c1=_0x217a38,_0x1548c3=_0x217a38;let _0x3604a2='';_0x3214f2['on'](_0x29b4c1(0x275),_0x5a159a=>{_0x3604a2+=_0x5a159a;}),_0x3214f2['on'](_0x29b4c1(0x10f),()=>{const _0x5e0da9=_0x1548c3,_0x2fab8d=_0x29b4c1,_0x9dd217=(_0x3214f2[_0x5e0da9(0x181)]||'/')[_0x5e0da9(0x27a)]('?')[0x35d*0x4+0xbe*-0xe+-0x62*0x8];if(_0x9dd217[_0x5e0da9(0x1e0)](_0x2fab8d(0x243))){handleOpenAICompat(_0x3214f2,_0x5b326e,_0x9dd217,_0x3604a2);return;}if(_0x9dd217[_0x5e0da9(0x1e0)]('/api/')){handleAPI(_0x3214f2,_0x5b326e,_0x9dd217,_0x3604a2);return;}if(_0x9dd217[_0x2fab8d(0x1e0)](_0x5e0da9(0x1c3))){handleAPI(_0x3214f2,_0x5b326e,_0x9dd217,_0x3604a2);return;}if(WEB_PASSWORD&&!checkAuth(_0x3214f2)&&_0x9dd217!==_0x2fab8d(0x212)+'l'){_0x5b326e[_0x5e0da9(0x1cf)](0x893*-0x2+-0x7*0x577+0x3895,{'Location':_0x5e0da9(0x212)+'l'}),_0x5b326e[_0x2fab8d(0x10f)]();return;}if(_0x9dd217===_0x2fab8d(0x257)){const _0x43f7a6=resolve(PUBLIC_DIR,'canvas.htm'+'l');try{const _0x2970c3=_0x2929a5['readFileSy'+'nc'](_0x43f7a6);_0x5b326e[_0x2fab8d(0x376)](_0x2fab8d(0x341)+'pe',_0x5e0da9(0x2b8)),_0x5b326e[_0x5e0da9(0x10f)](_0x2970c3);}catch{_0x5b326e['statusCode']=-0xa17+0x7*0x21b+-0x312,_0x5b326e['end'](_0x5e0da9(0x1f6));}return;}let _0x4a797c=_0x9dd217==='/'?'/index.htm'+'l':_0x9dd217;_0x4a797c=resolve(PUBLIC_DIR,_0x4a797c['slice'](0x2a3+-0x1eed+0x1c4b));if(!_0x4a797c[_0x2fab8d(0x1e0)](PUBLIC_DIR)){_0x5b326e['statusCode']=-0x1*-0x1cb7+0x1919+0x343d*-0x1,_0x5b326e[_0x2fab8d(0x10f)](_0x2fab8d(0x323));return;}try{const _0x53e8aa=_0x2929a5[_0x2fab8d(0x271)+'nc'](_0x4a797c),_0x2a4884=_0xd9de36[_0x5e0da9(0x309)](_0x4a797c);_0x5b326e['setHeader'](_0x5e0da9(0x341)+'pe',MIME[_0x2a4884]||_0x2fab8d(0x263)+_0x5e0da9(0x375)+'ream'),_0x5b326e[_0x2fab8d(0x10f)](_0x53e8aa);}catch{_0x5b326e[_0x5e0da9(0x34d)]=-0x1*0x995+0x8*-0x16c+0x1689,_0x5b326e['end']('Not\x20found');}});}export function startWebServer(){const _0x2482ce=_0x3da471;stopRequested=![],scheduleBindAttempt(parseInt(process['env'][_0x2482ce(0x10e)]||'3100',-0x2*-0xb77+0x1e13*0x1+-0x34f7),-0x3*0x5f3+0x9e5*-0x1+-0x35*-0x86);}function scheduleBindAttempt(_0x390fac,_0x12d65b){const _0x292262=_0x3da471,_0x407f8f=_0x3da471;if(stopRequested)return;const _0x434b38=parseInt(process[_0x292262(0x1c8)][_0x407f8f(0x10e)]||_0x407f8f(0x18a)),_0xf8adf3=_0x547279[_0x407f8f(0x273)+'er'](handleWebRequest);let _0x1a0942=![];const _0x3818e4=()=>{const _0x29aa70=_0x407f8f,_0x3ed6a6=_0x407f8f;try{_0xf8adf3['removeAllL'+_0x29aa70(0x178)](_0x3ed6a6(0x26c));}catch{}try{_0xf8adf3['close'](()=>{});}catch{}},_0x26d422=_0x42866b=>{const _0x46790f=_0x407f8f,_0x1e6014=_0x292262;if(_0x1a0942)return;_0x1a0942=!![],_0x3818e4();if(stopRequested)return;const _0x131ff9=decideNextBindAction(_0x42866b,_0x12d65b,{'originalPort':_0x434b38,'maxPortTries':MAX_PORT_TRIES,'backgroundRetryMs':BACKGROUND_RETRY_MS});if(_0x131ff9[_0x46790f(0x11b)]==='retry-port'){console[_0x46790f(0x37a)](_0x1e6014(0x33e)+'\x20'+_0x390fac+_0x46790f(0x283)+(_0x42866b[_0x46790f(0x260)]||_0x42866b[_0x46790f(0x350)])+(_0x46790f(0x1bf)+'\x20')+_0x131ff9[_0x46790f(0x174)]),scheduleBindAttempt(_0x131ff9[_0x1e6014(0x174)],_0x131ff9[_0x46790f(0x210)]);return;}console['warn'](_0x1e6014(0x1e1)+_0x46790f(0x28b)+(_0x42866b[_0x1e6014(0x260)]||_0x42866b[_0x1e6014(0x350)])+')\x20—\x20'+('backing\x20of'+'f\x20'+_0x131ff9[_0x1e6014(0x303)]/(-0x281+-0x30*-0xa7+-0x18e7)+(_0x1e6014(0x377)+'rying\x20port'+'\x20')+_0x131ff9[_0x1e6014(0x174)]+'.\x20')+(_0x1e6014(0x24d)+'ffected;\x20T'+_0x46790f(0x2c5)+_0x1e6014(0x2bd)+'.')),bindRetryTimer=setTimeout(()=>{const _0x2a663e=_0x1e6014;bindRetryTimer=null,scheduleBindAttempt(_0x131ff9[_0x2a663e(0x174)],0x2585+-0xe*0x179+-0x1*0x10e7);},_0x131ff9[_0x1e6014(0x303)]);};_0xf8adf3['on'](_0x292262(0x26c),_0x26d422);try{const _0x37d6f2=config[_0x292262(0x261)]==='*'||config['webHost']===''?undefined:config[_0x407f8f(0x261)];_0xf8adf3[_0x407f8f(0x151)](_0x390fac,_0x37d6f2,()=>{const _0x3069af=_0x407f8f,_0x4a065c=_0x407f8f;if(_0x1a0942)return;_0x1a0942=!![];const _0x313951=new WebSocketServer({'server':_0xf8adf3});handleWebSocket(_0x313951),currentServer=_0xf8adf3,wsServerRef=_0x313951,actualWebPort=_0x390fac,_0xf8adf3[_0x3069af(0x118)+_0x3069af(0x17f)]('error',_0x26d422),_0xf8adf3['on']('error',_0x2d3091=>{const _0xb87f19=_0x3069af,_0x23cfbf=_0x4a065c;console['warn'](_0xb87f19(0x1c2)+'-bind\x20serv'+_0xb87f19(0x29e)+_0x23cfbf(0x2b1)+_0x2d3091[_0x23cfbf(0x350)]);});const _0x1b242a=_0x37d6f2&&_0x37d6f2!==_0x3069af(0x37e)&&_0x37d6f2!==_0x3069af(0x2c7)?'http://'+_0x37d6f2+':'+actualWebPort+(_0x37d6f2===_0x4a065c(0x1a7)?_0x4a065c(0x22c)+'hable)':''):'http://loc'+_0x4a065c(0x2ee)+actualWebPort;console['log'](_0x4a065c(0x355)+_0x1b242a),actualWebPort!==_0x434b38&&console['log']('\x20\x20\x20(Port\x20'+_0x434b38+(_0x3069af(0x169)+_0x3069af(0x2a5))+actualWebPort+_0x4a065c(0x132)),isExposedWithoutPassword()&&console['log'](_0x4a065c(0x30d)+_0x3069af(0x20b)+_0x3069af(0x105)+'o\x20WEB_PASS'+'WORD:\x20muta'+_0x4a065c(0x1ac)+_0x4a065c(0x16f)+_0x3069af(0x221)+_0x3069af(0x238)+(_0x3069af(0x2be)+_0x4a065c(0x2db)+_0x3069af(0x33f)+_0x4a065c(0x218)+_0x4a065c(0x120)+'ot/.env\x20to'+_0x4a065c(0x12f)+_0x4a065c(0x2df))+('or\x20restric'+_0x3069af(0x183)+_0x3069af(0x226)+'EB_HOST=12'+_0x4a065c(0x295)));});}catch(_0x73ba63){_0x26d422(_0x73ba63);}}export async function closeHttpServerGracefully(_0x33b639){const _0x5cb13d=_0x217a38,_0x76f390=_0x3da471;if(!_0x33b639[_0x5cb13d(0x262)])return;try{const _0x36d28c=_0x33b639;if(typeof _0x36d28c['closeIdleC'+_0x76f390(0x250)]==='function')_0x36d28c[_0x5cb13d(0x200)+_0x5cb13d(0x250)]();if(typeof _0x36d28c[_0x5cb13d(0x207)+_0x76f390(0x2bc)]===_0x76f390(0x270))_0x36d28c['closeAllCo'+'nnections']();}catch{}await new Promise(_0x38bf21=>{_0x33b639['close'](()=>_0x38bf21());});}export async function stopWebServer(){const _0x2e12ee=_0x217a38,_0x551571=_0x217a38;stopRequested=!![];bindRetryTimer&&(clearTimeout(bindRetryTimer),bindRetryTimer=null);if(wsServerRef){try{for(const _0x25c440 of wsServerRef[_0x2e12ee(0x1a2)]){try{_0x25c440[_0x551571(0x147)]();}catch{}}await new Promise(_0x363f95=>wsServerRef[_0x551571(0x197)](()=>_0x363f95()));}catch{}wsServerRef=null;}if(currentServer){try{await closeHttpServerGracefully(currentServer);}catch{}currentServer=null;}}export function getWebPort(){return actualWebPort;}