alvin-bot 5.7.0 → 5.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +25 -31
  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 -174
  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 -583
  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 -86
  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 -1902
  135. package/dist/web/setup-api.js +1 -1101
  136. package/package.json +5 -2
  137. package/dist/.metadata_never_index +0 -0
@@ -1,1902 +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 crypto from "crypto";
16
- import { WebSocketServer, WebSocket } from "ws";
17
- import { getRegistry } from "../engine.js";
18
- import { getSession, resetSession, getAllSessions } from "../services/session.js";
19
- import { getMemoryStats, loadLongTermMemory, loadDailyLog } from "../services/memory.js";
20
- import { getIndexStats } from "../services/embeddings.js";
21
- import { getLoadedPlugins } from "../services/plugins.js";
22
- import { getMCPStatus } from "../services/mcp.js";
23
- import { listProfiles } from "../services/users.js";
24
- import { listCustomTools, getCustomTools, executeCustomTool } from "../services/custom-tools.js";
25
- import { buildSystemPrompt, reloadSoul, getSoulContent } from "../services/personality.js";
26
- import { config } from "../config.js";
27
- import { handleSetupAPI } from "./setup-api.js";
28
- import { handleDoctorAPI } from "./doctor-api.js";
29
- import { handleOpenAICompat } from "./openai-compat.js";
30
- import { addCanvasClient } from "./canvas.js";
31
- import { BOT_ROOT, ENV_FILE, PUBLIC_DIR, MEMORY_DIR, MEMORY_FILE, SOUL_FILE, DATA_DIR, MCP_CONFIG, SKILLS_DIR } from "../paths.js";
32
- import { writeSecure } from "../services/file-permissions.js";
33
- import { timingSafeBearerMatch } from "../services/timing-safe-bearer.js";
34
- import { broadcast } from "../services/broadcast.js";
35
- import { BOT_VERSION } from "../version.js";
36
- import { decideNextBindAction } from "./bind-strategy.js";
37
- const WEB_PORT = parseInt(process.env.WEB_PORT || "3100");
38
- /** Tuning for the bind loop. Walk the port ladder `MAX_PORT_TRIES` times
39
- * then fall back to a `BACKGROUND_RETRY_MS` idle loop — the bot keeps
40
- * running on Telegram either way; see bind-strategy.ts for the pure
41
- * decision logic. */
42
- const MAX_PORT_TRIES = 20;
43
- const BACKGROUND_RETRY_MS = 30_000;
44
- /** Current live http.Server, if one has successfully bound. */
45
- let currentServer = null;
46
- /** Current live WebSocketServer attached to currentServer. */
47
- let wsServerRef = null;
48
- /** Background-retry timer handle — set when the bind loop is in its
49
- * idle wait between cycles, cleared when stopWebServer() cancels. */
50
- let bindRetryTimer = null;
51
- /** Flag flipped by stopWebServer(). Every bind-loop callback checks
52
- * this and exits silently if set, so stop is truly terminal. */
53
- let stopRequested = false;
54
- const WEB_PASSWORD = process.env.WEB_PASSWORD || "";
55
- /**
56
- * v5.7.0 — Per-boot random token guarding POST /internal/subagent-exit.
57
- * Generated once at module load; never persisted, never logged. The
58
- * detached-agent exit hook reads it in-process via getInternalToken().
59
- * The route is loopback-bound by default; this token additionally
60
- * defends the opt-in WEB_HOST=0.0.0.0 (LAN-exposed) case. 48 hex chars.
61
- */
62
- const INTERNAL_TOKEN = crypto.randomBytes(24).toString("hex");
63
- /** In-process accessor for the per-boot internal-route token. */
64
- export function getInternalToken() {
65
- return INTERNAL_TOKEN;
66
- }
67
- /** The actual port the Web UI is running on (may differ from WEB_PORT if busy). */
68
- let actualWebPort = WEB_PORT;
69
- // ── MIME Types ──────────────────────────────────────────
70
- const MIME = {
71
- ".html": "text/html",
72
- ".css": "text/css",
73
- ".js": "application/javascript",
74
- ".json": "application/json",
75
- ".png": "image/png",
76
- ".jpg": "image/jpeg",
77
- ".svg": "image/svg+xml",
78
- ".ico": "image/x-icon",
79
- };
80
- // ── Auth ────────────────────────────────────────────────
81
- const activeSessions = new Set();
82
- function generateToken() {
83
- return Array.from(crypto.getRandomValues(new Uint8Array(32)))
84
- .map(b => b.toString(16).padStart(2, "0")).join("");
85
- }
86
- function checkAuth(req) {
87
- if (!WEB_PASSWORD)
88
- return true; // No password = open access
89
- const cookie = req.headers.cookie || "";
90
- const token = cookie.match(/alvinbot_token=([a-f0-9]+)/)?.[1];
91
- return token ? activeSessions.has(token) : false;
92
- }
93
- /** Returns true when the server is exposed to non-loopback addresses but
94
- * WEB_PASSWORD is empty. In that state every mutating / exec endpoint is
95
- * hard-refused regardless of any session cookie. */
96
- function isExposedWithoutPassword() {
97
- if (WEB_PASSWORD)
98
- return false; // password set → normal auth path
99
- const h = config.webHost;
100
- // loopback addresses are safe even without a password
101
- if (!h || h === "127.0.0.1" || h === "::1" || h === "localhost")
102
- return false;
103
- return true; // non-loopback + no password = exposed
104
- }
105
- // ── REST API ────────────────────────────────────────────
106
- async function handleAPI(req, res, urlPath, body) {
107
- res.setHeader("Content-Type", "application/json");
108
- // POST /api/login
109
- if (urlPath === "/api/login" && req.method === "POST") {
110
- try {
111
- const { password } = JSON.parse(body);
112
- // v5.x — refuse login entirely when WEB_PASSWORD is not set.
113
- // Previously `!WEB_PASSWORD || password === WEB_PASSWORD` minted a
114
- // session for ANY input (including "") when the env var was absent,
115
- // effectively making every unauthenticated request a valid session.
116
- // timingSafeBearerMatch already rejects empty expectedToken → reuse it
117
- // by wrapping the provided password as a synthetic "Bearer <pw>" header.
118
- const authOk = timingSafeBearerMatch(`Bearer ${password}`, WEB_PASSWORD);
119
- if (authOk) {
120
- const token = generateToken();
121
- activeSessions.add(token);
122
- res.setHeader("Set-Cookie", `alvinbot_token=${token}; Path=/; HttpOnly; SameSite=Strict; Max-Age=86400`);
123
- res.end(JSON.stringify({ ok: true }));
124
- }
125
- else {
126
- res.statusCode = 401;
127
- res.end(JSON.stringify({ error: "Wrong password" }));
128
- }
129
- }
130
- catch {
131
- res.statusCode = 400;
132
- res.end(JSON.stringify({ error: "Invalid request" }));
133
- }
134
- return;
135
- }
136
- // POST /api/webhook — external trigger endpoint with bearer auth (no cookie auth needed)
137
- if (urlPath === "/api/webhook" && req.method === "POST") {
138
- if (!config.webhookEnabled) {
139
- res.writeHead(404);
140
- res.end(JSON.stringify({ error: "Webhooks disabled" }));
141
- return;
142
- }
143
- // v4.12.2 — timing-safe bearer token comparison. Previously used
144
- // naive !== which leaks comparison position via timing side-channel.
145
- if (!timingSafeBearerMatch(req.headers.authorization, config.webhookToken ?? "")) {
146
- res.writeHead(401);
147
- res.end(JSON.stringify({ error: "Unauthorized" }));
148
- return;
149
- }
150
- try {
151
- const payload = JSON.parse(body);
152
- if (!payload.message) {
153
- res.writeHead(400);
154
- res.end(JSON.stringify({ error: "Missing message field" }));
155
- return;
156
- }
157
- const channel = payload.channel || "telegram";
158
- const chatId = payload.chatId || String(config.allowedUsers[0] || "");
159
- const { enqueue } = await import("../services/delivery-queue.js");
160
- const id = enqueue(channel, chatId, `[Webhook: ${payload.event || "unknown"}] ${payload.message}`);
161
- res.writeHead(200);
162
- res.end(JSON.stringify({ ok: true, queued: id }));
163
- }
164
- catch {
165
- res.writeHead(400);
166
- res.end(JSON.stringify({ error: "Invalid JSON body" }));
167
- }
168
- return;
169
- }
170
- // POST /internal/subagent-exit — detached sub-agent exit push (v5.7.0).
171
- // Always available (NO WEBHOOK_ENABLED dependency, so it works for every
172
- // install with zero config); loopback-bound by default; per-boot bearer
173
- // token. Reads the agent's jsonl, classifies, delivers immediately
174
- // (claim-gated). Idempotent — a repeat POST is a 404 no-op.
175
- if (urlPath === "/internal/subagent-exit" && req.method === "POST") {
176
- // The legitimate payload is ~80 bytes ({agentId, exitCode}). Reject
177
- // anything absurd before the auth compare so an unauthenticated
178
- // caller (only reachable at all under the opt-in WEB_HOST=0.0.0.0)
179
- // cannot push large bodies through this always-on route. (A deeper
180
- // streaming cap on the shared body accumulator — also used by
181
- // /api/ and /v1/ — is a separate pre-existing hardening item.)
182
- if (body.length > 8 * 1024) {
183
- res.statusCode = 413;
184
- res.end(JSON.stringify({ error: "Payload too large" }));
185
- return;
186
- }
187
- if (!timingSafeBearerMatch(req.headers.authorization, INTERNAL_TOKEN)) {
188
- res.statusCode = 401;
189
- res.end(JSON.stringify({ error: "Unauthorized" }));
190
- return;
191
- }
192
- let agentId;
193
- try {
194
- const payload = JSON.parse(body);
195
- agentId = typeof payload.agentId === "string" ? payload.agentId : "";
196
- }
197
- catch {
198
- res.statusCode = 400;
199
- res.end(JSON.stringify({ error: "Invalid JSON body" }));
200
- return;
201
- }
202
- if (!agentId) {
203
- res.statusCode = 400;
204
- res.end(JSON.stringify({ error: "Missing agentId" }));
205
- return;
206
- }
207
- try {
208
- const { deliverByAgentId } = await import("../services/async-agent-watcher.js");
209
- const outcome = await deliverByAgentId(agentId);
210
- res.statusCode = outcome === "unknown" ? 404 : outcome === "pending" ? 202 : 200;
211
- res.end(JSON.stringify({ ok: outcome !== "unknown", outcome }));
212
- }
213
- catch (err) {
214
- res.statusCode = 500;
215
- res.end(JSON.stringify({ error: "delivery failed" }));
216
- console.error("[internal/subagent-exit] delivery error:", err);
217
- }
218
- return;
219
- }
220
- // Auth check for all other API routes
221
- if (!checkAuth(req)) {
222
- res.statusCode = 401;
223
- res.end(JSON.stringify({ error: "Not authenticated" }));
224
- return;
225
- }
226
- // Hard gate: when the web server is reachable from non-loopback addresses
227
- // and WEB_PASSWORD is empty, mutating/exec endpoints are refused outright.
228
- // Local dev (127.0.0.1 + no password) is intentionally unaffected.
229
- // Endpoints that write files, execute commands, or modify configuration
230
- // are in scope; read-only status/config endpoints are not gated.
231
- const EXPOSED_DANGEROUS_ROUTES = new Set([
232
- "/api/terminal",
233
- "/api/skills/create",
234
- "/api/skills/update",
235
- "/api/skills/delete",
236
- "/api/files/save",
237
- "/api/files/delete",
238
- "/api/env/set",
239
- "/api/setup-wizard",
240
- "/api/soul/save",
241
- "/api/memory/save",
242
- "/api/memory/delete",
243
- "/api/session/reset",
244
- // cron — /api/cron/add was wrong; real route is /api/cron/create
245
- "/api/cron/create",
246
- "/api/cron/update",
247
- "/api/cron/delete",
248
- "/api/cron/toggle",
249
- "/api/cron/run",
250
- "/api/plugin/install",
251
- "/api/plugin/uninstall",
252
- // shell / process exec
253
- "/api/tools/execute",
254
- "/api/sudo/exec",
255
- "/api/sudo/setup",
256
- "/api/sudo/admin-dialog",
257
- "/api/sudo/revoke",
258
- // key / env writes
259
- "/api/providers/set-key",
260
- "/api/providers/set-primary",
261
- "/api/providers/set-fallbacks",
262
- "/api/providers/add-custom",
263
- "/api/providers/remove-custom",
264
- // platform config writes (writes ENV vars / runs npm install)
265
- "/api/platforms/configure",
266
- "/api/platforms/install-deps",
267
- // provider / fallback order writes
268
- "/api/fallback",
269
- "/api/fallback/move",
270
- // MCP config writes
271
- "/api/mcp/add",
272
- "/api/mcp/remove",
273
- // process restart (DoS vector)
274
- "/api/restart",
275
- // model switching (affects all users)
276
- "/api/models/switch",
277
- // WhatsApp auth / config writes
278
- "/api/whatsapp/disconnect",
279
- "/api/whatsapp/group-rules",
280
- ]);
281
- if (isExposedWithoutPassword() && EXPOSED_DANGEROUS_ROUTES.has(urlPath)) {
282
- res.statusCode = 403;
283
- res.end(JSON.stringify({ error: "refused: web exposed without WEB_PASSWORD" }));
284
- return;
285
- }
286
- // ── Setup APIs (platforms + models) ─────────────────
287
- const handled = await handleSetupAPI(req, res, urlPath, body);
288
- if (handled)
289
- return;
290
- // ── Doctor & Backup APIs ──────────────────────────
291
- const doctorHandled = await handleDoctorAPI(req, res, urlPath, body);
292
- if (doctorHandled)
293
- return;
294
- // GET /api/setup-check — is the bot fully configured?
295
- if (urlPath === "/api/setup-check") {
296
- const envPath = ENV_FILE;
297
- let env = {};
298
- try {
299
- const lines = fs.readFileSync(envPath, "utf-8").split("\n");
300
- for (const line of lines) {
301
- if (line.startsWith("#") || !line.includes("="))
302
- continue;
303
- const idx = line.indexOf("=");
304
- env[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
305
- }
306
- }
307
- catch { }
308
- const hasBotToken = !!(env.BOT_TOKEN || process.env.BOT_TOKEN);
309
- const hasAllowedUsers = !!(env.ALLOWED_USERS || process.env.ALLOWED_USERS);
310
- const hasPrimaryProvider = !!(env.PRIMARY_PROVIDER || process.env.PRIMARY_PROVIDER);
311
- // Check which providers have keys
312
- const providerKeys = {
313
- groq: !!(env.GROQ_API_KEY || process.env.GROQ_API_KEY),
314
- openai: !!(env.OPENAI_API_KEY || process.env.OPENAI_API_KEY),
315
- google: !!(env.GOOGLE_API_KEY || process.env.GOOGLE_API_KEY),
316
- nvidia: !!(env.NVIDIA_API_KEY || process.env.NVIDIA_API_KEY),
317
- anthropic: !!(env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_API_KEY),
318
- openrouter: !!(env.OPENROUTER_API_KEY || process.env.OPENROUTER_API_KEY),
319
- };
320
- const hasAnyProvider = hasPrimaryProvider || Object.values(providerKeys).some(Boolean);
321
- // Check Claude CLI
322
- let claudeCliInstalled = false;
323
- try {
324
- const { execSync } = await import("child_process");
325
- execSync("claude --version", { timeout: 5000, stdio: "pipe" });
326
- claudeCliInstalled = true;
327
- }
328
- catch { }
329
- const isComplete = hasBotToken && hasAllowedUsers && hasAnyProvider;
330
- res.end(JSON.stringify({
331
- isComplete,
332
- steps: {
333
- telegram: { done: hasBotToken && hasAllowedUsers, botToken: hasBotToken, allowedUsers: hasAllowedUsers },
334
- provider: { done: hasAnyProvider, primary: env.PRIMARY_PROVIDER || process.env.PRIMARY_PROVIDER || "", keys: providerKeys, claudeCli: claudeCliInstalled },
335
- },
336
- }));
337
- return;
338
- }
339
- // POST /api/setup-wizard — save all setup data at once (first-run wizard)
340
- if (urlPath === "/api/setup-wizard" && req.method === "POST") {
341
- try {
342
- const data = JSON.parse(body);
343
- const envPath = ENV_FILE;
344
- let content = fs.existsSync(envPath) ? fs.readFileSync(envPath, "utf-8") : "";
345
- const setEnv = (key, value) => {
346
- // M6 (centralized): reject values containing newline characters
347
- if (/[\n\r]/.test(value))
348
- throw new Error("env value must not contain newline characters");
349
- const regex = new RegExp(`^${key}=.*$`, "m");
350
- if (regex.test(content)) {
351
- content = content.replace(regex, `${key}=${value}`);
352
- }
353
- else {
354
- content = content.trimEnd() + `\n${key}=${value}\n`;
355
- }
356
- process.env[key] = value;
357
- };
358
- // Step 1: Telegram
359
- if (data.botToken)
360
- setEnv("BOT_TOKEN", data.botToken);
361
- if (data.allowedUsers)
362
- setEnv("ALLOWED_USERS", data.allowedUsers);
363
- // Step 2: Provider
364
- if (data.primaryProvider)
365
- setEnv("PRIMARY_PROVIDER", data.primaryProvider);
366
- if (data.apiKey && data.apiKeyEnv)
367
- setEnv(data.apiKeyEnv, data.apiKey);
368
- // Step 3: Optional
369
- if (data.webPassword)
370
- setEnv("WEB_PASSWORD", data.webPassword);
371
- fs.writeFileSync(envPath, content);
372
- res.end(JSON.stringify({ ok: true, note: "Setup complete! Restart needed." }));
373
- }
374
- catch (e) {
375
- res.statusCode = 400;
376
- res.end(JSON.stringify({ error: e instanceof Error ? e.message : String(e) }));
377
- }
378
- return;
379
- }
380
- // POST /api/validate-bot-token — validate a Telegram bot token
381
- if (urlPath === "/api/validate-bot-token" && req.method === "POST") {
382
- try {
383
- const { token } = JSON.parse(body);
384
- if (!token || !token.includes(":")) {
385
- res.end(JSON.stringify({ ok: false, error: "Invalid token format" }));
386
- return;
387
- }
388
- const tgRes = await fetch(`https://api.telegram.org/bot${token}/getMe`);
389
- const tgData = await tgRes.json();
390
- if (tgData.ok) {
391
- res.end(JSON.stringify({ ok: true, bot: { username: tgData.result.username, firstName: tgData.result.first_name, id: tgData.result.id } }));
392
- }
393
- else {
394
- res.end(JSON.stringify({ ok: false, error: tgData.description || "Invalid token" }));
395
- }
396
- }
397
- catch (e) {
398
- res.end(JSON.stringify({ ok: false, error: e instanceof Error ? e.message : String(e) }));
399
- }
400
- return;
401
- }
402
- // GET /api/status
403
- if (urlPath === "/api/status") {
404
- let modelInfo = { name: "Not configured", model: "none", status: "unconfigured" };
405
- try {
406
- const registry = getRegistry();
407
- const active = registry.getActive().getInfo();
408
- modelInfo = { name: active.name, model: active.model, status: active.status };
409
- }
410
- catch { /* engine not initialized — no provider configured */ }
411
- const memory = getMemoryStats();
412
- const index = getIndexStats();
413
- const plugins = getLoadedPlugins();
414
- const mcp = getMCPStatus();
415
- const users = listProfiles();
416
- const tools = listCustomTools();
417
- // Aggregate token usage across all sessions
418
- const { getAllSessions } = await import("../services/session.js");
419
- const allSessions = getAllSessions();
420
- let totalInputTokens = 0, totalOutputTokens = 0, totalCost = 0;
421
- for (const s of allSessions.values()) {
422
- totalInputTokens += s.totalInputTokens || 0;
423
- totalOutputTokens += s.totalOutputTokens || 0;
424
- totalCost += s.totalCost || 0;
425
- }
426
- const { config: appConfig } = await import("../config.js");
427
- res.end(JSON.stringify({
428
- bot: { version: BOT_VERSION, uptime: process.uptime() },
429
- model: modelInfo,
430
- memory: { ...memory, vectors: index.entries, indexSize: index.sizeBytes },
431
- plugins: plugins.length,
432
- mcp: mcp.length,
433
- users: users.length,
434
- tools: tools.length,
435
- tokens: {
436
- totalInput: totalInputTokens,
437
- totalOutput: totalOutputTokens,
438
- total: totalInputTokens + totalOutputTokens,
439
- totalCost,
440
- },
441
- setup: {
442
- telegram: !!appConfig.botToken,
443
- provider: modelInfo.status !== "unconfigured",
444
- },
445
- }));
446
- return;
447
- }
448
- // GET /api/models
449
- if (urlPath === "/api/models") {
450
- const registry = getRegistry();
451
- registry.listAll().then(models => {
452
- res.end(JSON.stringify({ models, active: registry.getActiveKey() }));
453
- });
454
- return;
455
- }
456
- // POST /api/models/switch
457
- if (urlPath === "/api/models/switch" && req.method === "POST") {
458
- try {
459
- const { key } = JSON.parse(body);
460
- const registry = getRegistry();
461
- const ok = registry.switchTo(key);
462
- res.end(JSON.stringify({ ok, active: registry.getActiveKey() }));
463
- }
464
- catch {
465
- res.statusCode = 400;
466
- res.end(JSON.stringify({ error: "Invalid request" }));
467
- }
468
- return;
469
- }
470
- // GET /api/fallback — Get fallback order + health
471
- if (urlPath === "/api/fallback" && req.method === "GET") {
472
- try {
473
- const { getFallbackOrder } = await import("../services/fallback-order.js");
474
- const { getHealthStatus, isFailedOver } = await import("../services/heartbeat.js");
475
- const registry = getRegistry();
476
- const providers = await registry.listAll();
477
- res.end(JSON.stringify({
478
- order: getFallbackOrder(),
479
- health: getHealthStatus(),
480
- failedOver: isFailedOver(),
481
- activeProvider: registry.getActiveKey(),
482
- availableProviders: providers.map(p => ({ key: p.key, name: p.name, status: p.status })),
483
- }));
484
- }
485
- catch (err) {
486
- res.end(JSON.stringify({ error: String(err) }));
487
- }
488
- return;
489
- }
490
- // POST /api/fallback — Set fallback order
491
- if (urlPath === "/api/fallback" && req.method === "POST") {
492
- try {
493
- const { primary, fallbacks } = JSON.parse(body);
494
- const { setFallbackOrder } = await import("../services/fallback-order.js");
495
- const result = setFallbackOrder(primary, fallbacks, "webui");
496
- res.end(JSON.stringify({ ok: true, order: result }));
497
- }
498
- catch (err) {
499
- res.statusCode = 400;
500
- res.end(JSON.stringify({ error: String(err) }));
501
- }
502
- return;
503
- }
504
- // POST /api/fallback/move — Move provider up/down
505
- if (urlPath === "/api/fallback/move" && req.method === "POST") {
506
- try {
507
- const { key, direction } = JSON.parse(body);
508
- const fb = await import("../services/fallback-order.js");
509
- const result = direction === "up" ? fb.moveUp(key, "webui") : fb.moveDown(key, "webui");
510
- res.end(JSON.stringify({ ok: true, order: result }));
511
- }
512
- catch (err) {
513
- res.statusCode = 400;
514
- res.end(JSON.stringify({ error: String(err) }));
515
- }
516
- return;
517
- }
518
- // GET /api/heartbeat — Health status
519
- if (urlPath === "/api/heartbeat") {
520
- try {
521
- const { getHealthStatus, isFailedOver } = await import("../services/heartbeat.js");
522
- res.end(JSON.stringify({
523
- health: getHealthStatus(),
524
- failedOver: isFailedOver(),
525
- }));
526
- }
527
- catch (err) {
528
- res.end(JSON.stringify({ health: [], failedOver: false }));
529
- }
530
- return;
531
- }
532
- // GET /api/memory
533
- if (urlPath === "/api/memory") {
534
- const ltm = loadLongTermMemory();
535
- const todayLog = loadDailyLog();
536
- const stats = getMemoryStats();
537
- const index = getIndexStats();
538
- // List daily log files
539
- let dailyFiles = [];
540
- try {
541
- dailyFiles = fs.readdirSync(MEMORY_DIR)
542
- .filter(f => f.endsWith(".md") && !f.startsWith("."))
543
- .sort()
544
- .reverse();
545
- }
546
- catch { /* empty */ }
547
- res.end(JSON.stringify({
548
- longTermMemory: ltm,
549
- todayLog,
550
- dailyFiles,
551
- stats,
552
- index: { entries: index.entries, files: index.files, sizeBytes: index.sizeBytes },
553
- }));
554
- return;
555
- }
556
- // GET /api/memory/:file
557
- if (urlPath.startsWith("/api/memory/")) {
558
- const file = urlPath.slice(12);
559
- if (file.includes("..") || !file.endsWith(".md")) {
560
- res.statusCode = 400;
561
- res.end(JSON.stringify({ error: "Invalid file" }));
562
- return;
563
- }
564
- try {
565
- const content = fs.readFileSync(resolve(MEMORY_DIR, file), "utf-8");
566
- res.end(JSON.stringify({ file, content }));
567
- }
568
- catch {
569
- res.statusCode = 404;
570
- res.end(JSON.stringify({ error: "File not found" }));
571
- }
572
- return;
573
- }
574
- // POST /api/memory/save
575
- if (urlPath === "/api/memory/save" && req.method === "POST") {
576
- try {
577
- const { file, content } = JSON.parse(body);
578
- if (file === "MEMORY.md") {
579
- fs.writeFileSync(MEMORY_FILE, content);
580
- }
581
- else if (file.endsWith(".md") && !file.includes("..")) {
582
- fs.writeFileSync(resolve(MEMORY_DIR, file), content);
583
- }
584
- else {
585
- res.statusCode = 400;
586
- res.end(JSON.stringify({ error: "Invalid file" }));
587
- return;
588
- }
589
- res.end(JSON.stringify({ ok: true }));
590
- }
591
- catch {
592
- res.statusCode = 400;
593
- res.end(JSON.stringify({ error: "Invalid request" }));
594
- }
595
- return;
596
- }
597
- // GET /api/plugins
598
- if (urlPath === "/api/plugins") {
599
- res.end(JSON.stringify({ plugins: getLoadedPlugins() }));
600
- return;
601
- }
602
- // v4.12.0 — Workspace overview: registry + per-workspace cost breakdown
603
- if (urlPath === "/api/workspaces") {
604
- try {
605
- const { listWorkspaces, getDefaultWorkspace } = await import("../services/workspaces.js");
606
- const { getCostByWorkspace } = await import("../services/session.js");
607
- const costs = getCostByWorkspace();
608
- const registered = listWorkspaces();
609
- const all = [getDefaultWorkspace(), ...registered];
610
- const payload = all.map((ws) => ({
611
- name: ws.name,
612
- purpose: ws.purpose,
613
- emoji: ws.emoji ?? null,
614
- color: ws.color ?? null,
615
- cwd: ws.cwd,
616
- channels: ws.channels,
617
- stats: costs[ws.name] ?? { totalCost: 0, sessionCount: 0, messageCount: 0, toolUseCount: 0 },
618
- }));
619
- res.end(JSON.stringify({ workspaces: payload }));
620
- }
621
- catch (err) {
622
- res.statusCode = 500;
623
- res.end(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }));
624
- }
625
- return;
626
- }
627
- // GET /api/users — Enhanced with session data
628
- if (urlPath === "/api/users" && req.method === "GET") {
629
- const { getAllSessions } = await import("../services/session.js");
630
- const profiles = listProfiles();
631
- const sessions = getAllSessions();
632
- const sessionMap = new Map(Array.from(sessions.entries()).map(([k, s]) => [Number(k), s]));
633
- const enriched = profiles.map(p => {
634
- const session = sessionMap.get(p.userId);
635
- return {
636
- ...p,
637
- session: session ? {
638
- isProcessing: session.isProcessing,
639
- totalCost: session.totalCost,
640
- historyLength: session.history.length,
641
- effort: session.effort,
642
- voiceReply: session.voiceReply,
643
- startedAt: session.startedAt,
644
- messageCount: session.messageCount,
645
- toolUseCount: session.toolUseCount,
646
- workingDir: session.workingDir,
647
- hasActiveQuery: !!session.abortController,
648
- queuedMessages: session.messageQueue.length,
649
- } : null,
650
- };
651
- });
652
- res.end(JSON.stringify({ users: enriched }));
653
- return;
654
- }
655
- // DELETE /api/users/:id — Kill session + delete user data
656
- if (urlPath.startsWith("/api/users/") && req.method === "DELETE") {
657
- // Pattern route — gate here since EXPOSED_DANGEROUS_ROUTES can't express :id
658
- if (isExposedWithoutPassword()) {
659
- res.statusCode = 403;
660
- res.end(JSON.stringify({ error: "refused: web exposed without WEB_PASSWORD" }));
661
- return;
662
- }
663
- const userId = parseInt(urlPath.split("/").pop() || "0");
664
- if (!userId) {
665
- res.statusCode = 400;
666
- res.end(JSON.stringify({ error: "Invalid user ID" }));
667
- return;
668
- }
669
- const { deleteUser } = await import("../services/users.js");
670
- const result = deleteUser(userId);
671
- res.end(JSON.stringify({ ok: true, ...result }));
672
- return;
673
- }
674
- // GET /api/tools
675
- if (urlPath === "/api/tools") {
676
- const tools = getCustomTools();
677
- res.end(JSON.stringify({ tools }));
678
- return;
679
- }
680
- // POST /api/tools/execute — run a tool by name
681
- if (urlPath === "/api/tools/execute" && req.method === "POST") {
682
- try {
683
- const { name, params } = JSON.parse(body);
684
- if (!name) {
685
- res.statusCode = 400;
686
- res.end(JSON.stringify({ error: "No tool name" }));
687
- return;
688
- }
689
- const output = await executeCustomTool(name, params || {});
690
- res.end(JSON.stringify({ ok: true, output }));
691
- }
692
- catch (err) {
693
- const error = err instanceof Error ? err.message : String(err);
694
- res.end(JSON.stringify({ error }));
695
- }
696
- return;
697
- }
698
- // ── MCP Management ─────────────────────────────────────
699
- // GET /api/mcp — list MCP servers + tools
700
- if (urlPath === "/api/mcp") {
701
- const { getMCPStatus, getMCPTools, hasMCPConfig } = await import("../services/mcp.js");
702
- const servers = getMCPStatus();
703
- const tools = getMCPTools();
704
- // Read raw config for editing
705
- const configPath = MCP_CONFIG;
706
- let rawConfig = { servers: {} };
707
- try {
708
- rawConfig = JSON.parse(fs.readFileSync(configPath, "utf-8"));
709
- }
710
- catch { }
711
- res.end(JSON.stringify({ servers, tools, config: rawConfig, hasConfig: hasMCPConfig() }));
712
- return;
713
- }
714
- // POST /api/mcp/add — add a new MCP server
715
- if (urlPath === "/api/mcp/add" && req.method === "POST") {
716
- try {
717
- const { name, command, args, url: serverUrl, env, headers } = JSON.parse(body);
718
- if (!name) {
719
- res.statusCode = 400;
720
- res.end(JSON.stringify({ error: "Name required" }));
721
- return;
722
- }
723
- const configPath = MCP_CONFIG;
724
- let config = { servers: {} };
725
- try {
726
- config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
727
- }
728
- catch { }
729
- const entry = {};
730
- if (command) {
731
- entry.command = command;
732
- entry.args = args || [];
733
- if (env)
734
- entry.env = env;
735
- }
736
- else if (serverUrl) {
737
- entry.url = serverUrl;
738
- if (headers)
739
- entry.headers = headers;
740
- }
741
- else {
742
- res.statusCode = 400;
743
- res.end(JSON.stringify({ error: "command or url required" }));
744
- return;
745
- }
746
- config.servers[name] = entry;
747
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
748
- res.end(JSON.stringify({ ok: true, note: "Restart needed to connect." }));
749
- }
750
- catch (e) {
751
- res.statusCode = 400;
752
- res.end(JSON.stringify({ error: e instanceof Error ? e.message : String(e) }));
753
- }
754
- return;
755
- }
756
- // POST /api/mcp/remove — remove an MCP server
757
- if (urlPath === "/api/mcp/remove" && req.method === "POST") {
758
- try {
759
- const { name } = JSON.parse(body);
760
- const configPath = MCP_CONFIG;
761
- let config = { servers: {} };
762
- try {
763
- config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
764
- }
765
- catch { }
766
- delete config.servers[name];
767
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
768
- res.end(JSON.stringify({ ok: true }));
769
- }
770
- catch (e) {
771
- res.statusCode = 400;
772
- res.end(JSON.stringify({ error: e instanceof Error ? e.message : String(e) }));
773
- }
774
- return;
775
- }
776
- // GET /api/mcp/discover — auto-discover MCP servers on the system
777
- if (urlPath === "/api/mcp/discover") {
778
- const discovered = [];
779
- const { execSync } = await import("child_process");
780
- // Check for common MCP server npm packages
781
- const knownServers = [
782
- { pkg: "@modelcontextprotocol/server-filesystem", name: "filesystem", args: ["/tmp"] },
783
- { pkg: "@modelcontextprotocol/server-brave-search", name: "brave-search", args: [] },
784
- { pkg: "@modelcontextprotocol/server-github", name: "github", args: [] },
785
- { pkg: "@modelcontextprotocol/server-postgres", name: "postgres", args: [] },
786
- { pkg: "@modelcontextprotocol/server-sqlite", name: "sqlite", args: [] },
787
- { pkg: "@modelcontextprotocol/server-slack", name: "slack", args: [] },
788
- { pkg: "@modelcontextprotocol/server-memory", name: "memory", args: [] },
789
- { pkg: "@modelcontextprotocol/server-puppeteer", name: "puppeteer", args: [] },
790
- { pkg: "@modelcontextprotocol/server-fetch", name: "web-fetch", args: [] },
791
- { pkg: "@anthropic/mcp-server-sequential-thinking", name: "sequential-thinking", args: [] },
792
- ];
793
- for (const s of knownServers) {
794
- try {
795
- execSync(`npx --yes ${s.pkg} --help`, { timeout: 5000, stdio: "pipe", env: { ...process.env, PATH: process.env.PATH + ":/opt/homebrew/bin:/usr/local/bin" } });
796
- discovered.push({ name: s.name, command: "npx", args: ["-y", s.pkg, ...s.args], source: "npm" });
797
- }
798
- catch {
799
- // Not installed — try checking if globally available
800
- try {
801
- execSync(`npm list -g ${s.pkg} --depth=0`, { timeout: 5000, stdio: "pipe" });
802
- discovered.push({ name: s.name, command: "npx", args: ["-y", s.pkg, ...s.args], source: "npm-global" });
803
- }
804
- catch { /* not installed */ }
805
- }
806
- }
807
- // Check for Claude Desktop MCP config
808
- const homeDir = process.env.HOME || process.env.USERPROFILE || "";
809
- const claudeConfigPaths = [
810
- resolve(homeDir, ".config/claude/claude_desktop_config.json"),
811
- resolve(homeDir, "Library/Application Support/Claude/claude_desktop_config.json"),
812
- resolve(homeDir, "AppData/Roaming/Claude/claude_desktop_config.json"),
813
- ];
814
- for (const cfgPath of claudeConfigPaths) {
815
- try {
816
- const cfg = JSON.parse(fs.readFileSync(cfgPath, "utf-8"));
817
- if (cfg.mcpServers) {
818
- for (const [name, srv] of Object.entries(cfg.mcpServers)) {
819
- if (srv.command) {
820
- discovered.push({ name: `claude-${name}`, command: srv.command, args: srv.args || [], source: "claude-desktop" });
821
- }
822
- }
823
- }
824
- }
825
- catch { /* not found */ }
826
- }
827
- res.end(JSON.stringify({ discovered }));
828
- return;
829
- }
830
- // ── Skills Management ─────────────────────────────────
831
- // GET /api/skills — already in setup-api.ts, but add full CRUD here
832
- // GET /api/skills/detail/:id — get full skill content
833
- if (urlPath?.match(/^\/api\/skills\/detail\//) && req.method === "GET") {
834
- const skillId = urlPath.split("/").pop();
835
- const { getSkills } = await import("../services/skills.js");
836
- const skill = getSkills().find(s => s.id === skillId);
837
- if (skill) {
838
- res.end(JSON.stringify({ ok: true, skill }));
839
- }
840
- else {
841
- res.statusCode = 404;
842
- res.end(JSON.stringify({ error: "Skill not found" }));
843
- }
844
- return;
845
- }
846
- // POST /api/skills/create — create a new skill
847
- if (urlPath === "/api/skills/create" && req.method === "POST") {
848
- try {
849
- const { id, name, description, triggers, category, content, priority } = JSON.parse(body);
850
- if (!id || !name) {
851
- res.statusCode = 400;
852
- res.end(JSON.stringify({ error: "id and name required" }));
853
- return;
854
- }
855
- // Security: reject id values that could escape SKILLS_DIR.
856
- // Mirror the resolve+startsWith containment pattern used in /api/files/save (server.ts ~:921).
857
- // Also guard against ids with path separators or absolute paths before
858
- // resolve() ever runs, so the raw string never reaches the filesystem.
859
- if (typeof id !== "string" ||
860
- id.includes("..") ||
861
- id.includes("/") ||
862
- id.includes("\\") ||
863
- path.isAbsolute(id)) {
864
- res.statusCode = 400;
865
- res.end(JSON.stringify({ error: "Invalid skill id" }));
866
- return;
867
- }
868
- const skillsDir = SKILLS_DIR;
869
- const skillDir = resolve(skillsDir, id);
870
- // Belt-and-suspenders: resolved path must still be inside SKILLS_DIR
871
- if (!skillDir.startsWith(skillsDir)) {
872
- res.statusCode = 400;
873
- res.end(JSON.stringify({ error: "Invalid skill id" }));
874
- return;
875
- }
876
- if (!fs.existsSync(skillDir))
877
- fs.mkdirSync(skillDir, { recursive: true });
878
- const frontmatter = [
879
- "---",
880
- `name: ${name}`,
881
- description ? `description: ${description}` : "",
882
- triggers ? `triggers: ${Array.isArray(triggers) ? triggers.join(", ") : triggers}` : "",
883
- `priority: ${priority || 3}`,
884
- `category: ${category || "custom"}`,
885
- "---",
886
- ].filter(Boolean).join("\n");
887
- fs.writeFileSync(resolve(skillDir, "SKILL.md"), `${frontmatter}\n\n${content || ""}`);
888
- // Force reload
889
- const { loadSkills } = await import("../services/skills.js");
890
- loadSkills();
891
- res.end(JSON.stringify({ ok: true }));
892
- }
893
- catch (e) {
894
- res.statusCode = 400;
895
- res.end(JSON.stringify({ error: e instanceof Error ? e.message : String(e) }));
896
- }
897
- return;
898
- }
899
- // POST /api/skills/update — update an existing skill
900
- if (urlPath === "/api/skills/update" && req.method === "POST") {
901
- try {
902
- const { id, content } = JSON.parse(body);
903
- // Security: same id containment as /api/skills/create
904
- if (typeof id !== "string" ||
905
- id.includes("..") ||
906
- id.includes("/") ||
907
- id.includes("\\") ||
908
- path.isAbsolute(id)) {
909
- res.statusCode = 400;
910
- res.end(JSON.stringify({ error: "Invalid skill id" }));
911
- return;
912
- }
913
- // Belt-and-suspenders: resolved path must still be inside SKILLS_DIR
914
- if (!resolve(SKILLS_DIR, id).startsWith(SKILLS_DIR)) {
915
- res.statusCode = 400;
916
- res.end(JSON.stringify({ error: "Invalid skill id" }));
917
- return;
918
- }
919
- const skillPath = resolve(SKILLS_DIR, id, "SKILL.md");
920
- if (!fs.existsSync(skillPath)) {
921
- // Try flat file
922
- const flatPath = resolve(SKILLS_DIR, id + ".md");
923
- if (fs.existsSync(flatPath)) {
924
- fs.writeFileSync(flatPath, content);
925
- }
926
- else {
927
- res.statusCode = 404;
928
- res.end(JSON.stringify({ error: "Skill not found" }));
929
- return;
930
- }
931
- }
932
- else {
933
- fs.writeFileSync(skillPath, content);
934
- }
935
- const { loadSkills } = await import("../services/skills.js");
936
- loadSkills();
937
- res.end(JSON.stringify({ ok: true }));
938
- }
939
- catch (e) {
940
- res.statusCode = 400;
941
- res.end(JSON.stringify({ error: e instanceof Error ? e.message : String(e) }));
942
- }
943
- return;
944
- }
945
- // POST /api/skills/delete — delete a skill
946
- if (urlPath === "/api/skills/delete" && req.method === "POST") {
947
- try {
948
- const { id } = JSON.parse(body);
949
- // Security: same raw-string id containment as /api/skills/create + /api/skills/update
950
- if (typeof id !== "string" ||
951
- id.includes("..") ||
952
- id.includes("/") ||
953
- id.includes("\\") ||
954
- path.isAbsolute(id)) {
955
- res.statusCode = 400;
956
- res.end(JSON.stringify({ error: "Invalid skill id" }));
957
- return;
958
- }
959
- const skillDir = resolve(SKILLS_DIR, id);
960
- const flatFile = resolve(SKILLS_DIR, id + ".md");
961
- if (fs.existsSync(skillDir)) {
962
- fs.rmSync(skillDir, { recursive: true });
963
- }
964
- else if (fs.existsSync(flatFile)) {
965
- fs.unlinkSync(flatFile);
966
- }
967
- else {
968
- res.statusCode = 404;
969
- res.end(JSON.stringify({ error: "Skill not found" }));
970
- return;
971
- }
972
- const { loadSkills } = await import("../services/skills.js");
973
- loadSkills();
974
- res.end(JSON.stringify({ ok: true }));
975
- }
976
- catch (e) {
977
- res.statusCode = 400;
978
- res.end(JSON.stringify({ error: e instanceof Error ? e.message : String(e) }));
979
- }
980
- return;
981
- }
982
- // GET /api/config
983
- if (urlPath === "/api/config") {
984
- res.end(JSON.stringify({
985
- providers: config.fallbackProviders,
986
- primaryProvider: config.primaryProvider,
987
- allowedUsers: config.allowedUsers,
988
- hasKeys: {
989
- groq: !!config.apiKeys.groq,
990
- openai: !!config.apiKeys.openai,
991
- google: !!config.apiKeys.google,
992
- nvidia: !!config.apiKeys.nvidia,
993
- openrouter: !!config.apiKeys.openrouter,
994
- },
995
- }));
996
- return;
997
- }
998
- // GET /api/sessions
999
- if (urlPath === "/api/sessions") {
1000
- const sessions = getAllSessions();
1001
- const profiles = listProfiles();
1002
- const data = Array.from(sessions.entries()).map(([key, session]) => {
1003
- const userId = Number(key.split(":").pop());
1004
- const profile = profiles.find(p => p.userId === userId);
1005
- return {
1006
- userId: key,
1007
- name: profile?.name || `User ${key}`,
1008
- username: profile?.username,
1009
- messageCount: session.messageCount,
1010
- toolUseCount: session.toolUseCount,
1011
- totalCost: session.totalCost,
1012
- totalInputTokens: session.totalInputTokens || 0,
1013
- totalOutputTokens: session.totalOutputTokens || 0,
1014
- effort: session.effort,
1015
- startedAt: session.startedAt,
1016
- lastActivity: session.lastActivity,
1017
- historyLength: session.history.length,
1018
- isProcessing: session.isProcessing,
1019
- provider: Object.keys(session.queriesByProvider).join(", ") || "none",
1020
- };
1021
- });
1022
- res.end(JSON.stringify({ sessions: data }));
1023
- return;
1024
- }
1025
- // GET /api/sessions/:userId/history
1026
- if (urlPath.match(/^\/api\/sessions\/\d+\/history$/)) {
1027
- const userId = parseInt(urlPath.split("/")[3]);
1028
- const session = getSession(userId);
1029
- res.end(JSON.stringify({
1030
- userId,
1031
- history: session.history.map(h => ({ role: h.role, content: h.content.slice(0, 2000) })),
1032
- }));
1033
- return;
1034
- }
1035
- // GET /api/files?path=...
1036
- if (urlPath === "/api/files") {
1037
- const params = new URLSearchParams((req.url || "").split("?")[1] || "");
1038
- const reqPath = params.get("path") || "";
1039
- const basePath = resolve(BOT_ROOT, reqPath || ".");
1040
- // Security: must be within BOT_ROOT
1041
- if (!basePath.startsWith(BOT_ROOT)) {
1042
- res.statusCode = 403;
1043
- res.end(JSON.stringify({ error: "Access denied" }));
1044
- return;
1045
- }
1046
- try {
1047
- const stat = fs.statSync(basePath);
1048
- if (stat.isDirectory()) {
1049
- const entries = fs.readdirSync(basePath, { withFileTypes: true })
1050
- .filter(e => !e.name.startsWith(".") && e.name !== "node_modules")
1051
- .map(e => ({
1052
- name: e.name,
1053
- type: e.isDirectory() ? "dir" : "file",
1054
- size: e.isFile() ? fs.statSync(resolve(basePath, e.name)).size : 0,
1055
- modified: fs.statSync(resolve(basePath, e.name)).mtimeMs,
1056
- }))
1057
- .sort((a, b) => {
1058
- if (a.type !== b.type)
1059
- return a.type === "dir" ? -1 : 1;
1060
- return a.name.localeCompare(b.name);
1061
- });
1062
- res.end(JSON.stringify({ path: reqPath || ".", entries }));
1063
- }
1064
- else {
1065
- // Read file content — text files up to 500KB
1066
- const ext = path.extname(basePath).toLowerCase();
1067
- const textExts = new Set([
1068
- ".md", ".txt", ".json", ".js", ".ts", ".jsx", ".tsx", ".css", ".html", ".htm",
1069
- ".xml", ".svg", ".yml", ".yaml", ".toml", ".ini", ".cfg", ".conf", ".env",
1070
- ".sh", ".bash", ".zsh", ".fish", ".py", ".rb", ".go", ".rs", ".java", ".kt",
1071
- ".c", ".cpp", ".h", ".hpp", ".cs", ".php", ".sql", ".graphql", ".prisma",
1072
- ".dockerfile", ".gitignore", ".gitattributes", ".editorconfig", ".prettierrc",
1073
- ".eslintrc", ".babelrc", ".npmrc", ".nvmrc", ".lock", ".log", ".csv", ".tsv",
1074
- ".mjs", ".cjs", ".mts", ".cts", ".vue", ".svelte", ".astro",
1075
- ]);
1076
- // Files without extension that match known names are always text
1077
- const textNames = new Set([
1078
- "dockerfile", "makefile", "procfile", "gemfile", "rakefile",
1079
- "vagrantfile", "brewfile", "justfile", "taskfile", "cakefile",
1080
- "license", "licence", "readme", "changelog", "authors", "contributors",
1081
- ]);
1082
- const baseName = path.basename(basePath).toLowerCase();
1083
- const isKnownTextName = textNames.has(baseName);
1084
- const isText = textExts.has(ext) || isKnownTextName || (!ext && stat.size < 100_000);
1085
- if (stat.size > 500_000) {
1086
- res.end(JSON.stringify({ path: reqPath, content: `[File too large: ${(stat.size / 1024).toFixed(1)} KB — max 500 KB]`, size: stat.size }));
1087
- }
1088
- else if (isText) {
1089
- try {
1090
- const content = fs.readFileSync(basePath, "utf-8");
1091
- // Quick binary check: if >10% null bytes, it's binary
1092
- const nullCount = [...content.slice(0, 1000)].filter(c => c === "\0").length;
1093
- if (nullCount > 100) {
1094
- res.end(JSON.stringify({ path: reqPath, content: null, size: stat.size, binary: true }));
1095
- }
1096
- else {
1097
- res.end(JSON.stringify({ path: reqPath, content, size: stat.size }));
1098
- }
1099
- }
1100
- catch {
1101
- res.end(JSON.stringify({ path: reqPath, content: null, size: stat.size, binary: true }));
1102
- }
1103
- }
1104
- else {
1105
- res.end(JSON.stringify({ path: reqPath, content: null, size: stat.size, binary: true }));
1106
- }
1107
- }
1108
- }
1109
- catch {
1110
- res.statusCode = 404;
1111
- res.end(JSON.stringify({ error: "Not found" }));
1112
- }
1113
- return;
1114
- }
1115
- // POST /api/files/save
1116
- if (urlPath === "/api/files/save" && req.method === "POST") {
1117
- try {
1118
- const { path: filePath, content } = JSON.parse(body);
1119
- const absPath = resolve(BOT_ROOT, filePath);
1120
- if (!absPath.startsWith(BOT_ROOT)) {
1121
- res.statusCode = 403;
1122
- res.end(JSON.stringify({ error: "Access denied" }));
1123
- return;
1124
- }
1125
- fs.writeFileSync(absPath, content);
1126
- res.end(JSON.stringify({ ok: true }));
1127
- }
1128
- catch (err) {
1129
- res.statusCode = 400;
1130
- const error = err instanceof Error ? err.message : "Invalid request";
1131
- res.end(JSON.stringify({ error }));
1132
- }
1133
- return;
1134
- }
1135
- // POST /api/files/delete
1136
- if (urlPath === "/api/files/delete" && req.method === "POST") {
1137
- try {
1138
- const { path: filePath } = JSON.parse(body);
1139
- const absPath = resolve(BOT_ROOT, filePath);
1140
- if (!absPath.startsWith(BOT_ROOT)) {
1141
- res.statusCode = 403;
1142
- res.end(JSON.stringify({ error: "Access denied" }));
1143
- return;
1144
- }
1145
- // Safety: don't allow deleting critical files
1146
- const critical = [".env", "package.json", "tsconfig.json", "ecosystem.config.cjs"];
1147
- const baseName = path.basename(absPath);
1148
- if (critical.includes(baseName)) {
1149
- res.statusCode = 403;
1150
- res.end(JSON.stringify({ error: `${baseName} cannot be deleted (protected)` }));
1151
- return;
1152
- }
1153
- if (!fs.existsSync(absPath)) {
1154
- res.statusCode = 404;
1155
- res.end(JSON.stringify({ error: "File not found" }));
1156
- return;
1157
- }
1158
- const stat = fs.statSync(absPath);
1159
- if (stat.isDirectory()) {
1160
- res.statusCode = 400;
1161
- res.end(JSON.stringify({ error: "Directories cannot be deleted" }));
1162
- return;
1163
- }
1164
- fs.unlinkSync(absPath);
1165
- res.end(JSON.stringify({ ok: true }));
1166
- }
1167
- catch (err) {
1168
- res.statusCode = 400;
1169
- const error = err instanceof Error ? err.message : "Invalid request";
1170
- res.end(JSON.stringify({ error }));
1171
- }
1172
- return;
1173
- }
1174
- // POST /api/terminal
1175
- if (urlPath === "/api/terminal" && req.method === "POST") {
1176
- try {
1177
- const { command } = JSON.parse(body);
1178
- if (!command) {
1179
- res.statusCode = 400;
1180
- res.end(JSON.stringify({ error: "No command" }));
1181
- return;
1182
- }
1183
- // Security: limit command length
1184
- if (command.length > 10000) {
1185
- res.statusCode = 400;
1186
- res.end(JSON.stringify({ error: "Command too long (max 10000 chars)" }));
1187
- return;
1188
- }
1189
- const cwd = typeof (JSON.parse(body)).cwd === "string" ? resolve(JSON.parse(body).cwd) : BOT_ROOT;
1190
- const output = execSync(command, {
1191
- cwd,
1192
- stdio: "pipe",
1193
- timeout: 120000,
1194
- env: { ...process.env, PATH: process.env.PATH + ":/opt/homebrew/bin:/usr/local/bin" },
1195
- }).toString();
1196
- res.end(JSON.stringify({ output: output.slice(0, 100000) }));
1197
- }
1198
- catch (err) {
1199
- const error = err;
1200
- const stderr = error.stderr?.toString()?.trim() || "";
1201
- res.end(JSON.stringify({ output: stderr || error.message, exitCode: 1 }));
1202
- }
1203
- return;
1204
- }
1205
- // GET /api/env — read .env keys (names only, values masked)
1206
- if (urlPath === "/api/env") {
1207
- try {
1208
- const envContent = fs.existsSync(ENV_FILE) ? fs.readFileSync(ENV_FILE, "utf-8") : "";
1209
- const lines = envContent.split("\n").filter(l => l.includes("=") && !l.startsWith("#"));
1210
- const vars = lines.map(l => {
1211
- const [key, ...rest] = l.split("=");
1212
- const value = rest.join("=").trim();
1213
- // Mask sensitive values
1214
- const masked = key.includes("KEY") || key.includes("TOKEN") || key.includes("PASSWORD") || key.includes("SECRET")
1215
- ? (value.length > 4 ? value.slice(0, 4) + "..." + value.slice(-4) : "****")
1216
- : value;
1217
- return { key: key.trim(), value: masked, hasValue: value.length > 0 };
1218
- });
1219
- res.end(JSON.stringify({ vars }));
1220
- }
1221
- catch {
1222
- res.end(JSON.stringify({ vars: [] }));
1223
- }
1224
- return;
1225
- }
1226
- // POST /api/env/set — update an env var
1227
- if (urlPath === "/api/env/set" && req.method === "POST") {
1228
- try {
1229
- const { key, value } = JSON.parse(body);
1230
- if (!key || typeof key !== "string" || !key.match(/^[A-Z_][A-Z0-9_]*$/)) {
1231
- res.statusCode = 400;
1232
- res.end(JSON.stringify({ error: "Invalid key name" }));
1233
- return;
1234
- }
1235
- // M6: reject values containing newline characters — they allow injecting
1236
- // extra .env lines (e.g. value="good\nEVIL=injected").
1237
- if (typeof value === "string" && /[\n\r]/.test(value)) {
1238
- res.statusCode = 400;
1239
- res.end(JSON.stringify({ error: "Invalid value: newline characters not allowed" }));
1240
- return;
1241
- }
1242
- let envContent = fs.existsSync(ENV_FILE) ? fs.readFileSync(ENV_FILE, "utf-8") : "";
1243
- const regex = new RegExp(`^${key}=.*$`, "m");
1244
- if (regex.test(envContent)) {
1245
- envContent = envContent.replace(regex, `${key}=${value}`);
1246
- }
1247
- else {
1248
- envContent = envContent.trimEnd() + `\n${key}=${value}\n`;
1249
- }
1250
- // v4.12.2 — enforce 0o600 on .env
1251
- writeSecure(ENV_FILE, envContent);
1252
- res.end(JSON.stringify({ ok: true, note: "Restart required for changes to take effect" }));
1253
- }
1254
- catch {
1255
- res.statusCode = 400;
1256
- res.end(JSON.stringify({ error: "Invalid request" }));
1257
- }
1258
- return;
1259
- }
1260
- // GET /api/soul — read SOUL.md
1261
- if (urlPath === "/api/soul") {
1262
- const content = getSoulContent();
1263
- res.end(JSON.stringify({ content }));
1264
- return;
1265
- }
1266
- // POST /api/soul/save — update SOUL.md
1267
- if (urlPath === "/api/soul/save" && req.method === "POST") {
1268
- try {
1269
- const { content } = JSON.parse(body);
1270
- const soulPath = SOUL_FILE;
1271
- fs.writeFileSync(soulPath, content);
1272
- reloadSoul();
1273
- res.end(JSON.stringify({ ok: true }));
1274
- }
1275
- catch {
1276
- res.statusCode = 400;
1277
- res.end(JSON.stringify({ error: "Invalid request" }));
1278
- }
1279
- return;
1280
- }
1281
- // GET /api/platforms — platform adapter status
1282
- if (urlPath === "/api/platforms") {
1283
- const platforms = [
1284
- { name: "Telegram", key: "BOT_TOKEN", icon: "📱", configured: !!process.env.BOT_TOKEN },
1285
- { name: "Discord", key: "DISCORD_TOKEN", icon: "🎮", configured: !!process.env.DISCORD_TOKEN },
1286
- { name: "WhatsApp", key: "WHATSAPP_ENABLED", icon: "💬", configured: process.env.WHATSAPP_ENABLED === "true" },
1287
- { name: "Signal", key: "SIGNAL_API_URL", icon: "🔒", configured: !!process.env.SIGNAL_API_URL },
1288
- { name: "Web UI", key: "WEB_PORT", icon: "🌐", configured: true },
1289
- ];
1290
- res.end(JSON.stringify({ platforms }));
1291
- return;
1292
- }
1293
- // POST /api/restart — restart the bot process
1294
- if (urlPath === "/api/restart" && req.method === "POST") {
1295
- const { scheduleGracefulRestart } = await import("../services/restart.js");
1296
- res.end(JSON.stringify({ ok: true, note: "Restarting..." }));
1297
- scheduleGracefulRestart(500);
1298
- return;
1299
- }
1300
- // POST /api/chat/export — export chat history
1301
- if (urlPath === "/api/chat/export" && req.method === "POST") {
1302
- try {
1303
- const { messages, format } = JSON.parse(body);
1304
- if (format === "json") {
1305
- res.setHeader("Content-Type", "application/json");
1306
- res.end(JSON.stringify({ export: messages }, null, 2));
1307
- }
1308
- else {
1309
- // Markdown
1310
- const md = messages.map((m) => {
1311
- const prefix = m.role === "user" ? "**Du:**" : m.role === "assistant" ? "**Alvin Bot:**" : "*System:*";
1312
- const time = m.time ? ` _(${m.time})_` : "";
1313
- return `${prefix}${time}\n${m.text}\n`;
1314
- }).join("\n---\n\n");
1315
- res.setHeader("Content-Type", "text/markdown");
1316
- res.end(`# Chat Export — Alvin Bot\n_${new Date().toLocaleString("de-DE")}_\n\n---\n\n${md}`);
1317
- }
1318
- }
1319
- catch {
1320
- res.statusCode = 400;
1321
- res.end(JSON.stringify({ error: "Invalid request" }));
1322
- }
1323
- return;
1324
- }
1325
- // ── WhatsApp Group Management API ────────────────────────────────────
1326
- // GET /api/whatsapp/groups — list all WhatsApp groups (live from WA)
1327
- if (urlPath === "/api/whatsapp/groups" && req.method === "GET") {
1328
- try {
1329
- const { getWhatsAppAdapter } = await import("../platforms/whatsapp.js");
1330
- const adapter = getWhatsAppAdapter();
1331
- if (!adapter) {
1332
- res.end(JSON.stringify({ groups: [], error: "WhatsApp nicht verbunden" }));
1333
- return;
1334
- }
1335
- const groups = await adapter.getGroups();
1336
- res.end(JSON.stringify({ groups }));
1337
- }
1338
- catch (err) {
1339
- res.end(JSON.stringify({ groups: [], error: String(err) }));
1340
- }
1341
- return;
1342
- }
1343
- // GET /api/whatsapp/groups/:id/participants — fetch group participants
1344
- if (urlPath.match(/^\/api\/whatsapp\/groups\/[^/]+\/participants$/)) {
1345
- try {
1346
- const groupId = decodeURIComponent(urlPath.split("/")[4]);
1347
- const { getWhatsAppAdapter } = await import("../platforms/whatsapp.js");
1348
- const adapter = getWhatsAppAdapter();
1349
- if (!adapter) {
1350
- res.end(JSON.stringify({ participants: [], error: "WhatsApp nicht verbunden" }));
1351
- return;
1352
- }
1353
- const participants = await adapter.getGroupParticipants(groupId);
1354
- res.end(JSON.stringify({ participants }));
1355
- }
1356
- catch (err) {
1357
- res.end(JSON.stringify({ participants: [], error: String(err) }));
1358
- }
1359
- return;
1360
- }
1361
- // GET /api/whatsapp/group-rules — get all configured group rules
1362
- if (urlPath === "/api/whatsapp/group-rules" && req.method === "GET") {
1363
- const { getGroupRules } = await import("../platforms/whatsapp.js");
1364
- res.end(JSON.stringify({ rules: getGroupRules() }));
1365
- return;
1366
- }
1367
- // POST /api/whatsapp/group-rules — create or update a group rule
1368
- if (urlPath === "/api/whatsapp/group-rules" && req.method === "POST") {
1369
- try {
1370
- const rule = JSON.parse(body);
1371
- if (!rule.groupId) {
1372
- res.statusCode = 400;
1373
- res.end(JSON.stringify({ error: "groupId ist erforderlich" }));
1374
- return;
1375
- }
1376
- const { upsertGroupRule } = await import("../platforms/whatsapp.js");
1377
- const saved = upsertGroupRule(rule);
1378
- res.end(JSON.stringify({ ok: true, rule: saved }));
1379
- }
1380
- catch (err) {
1381
- res.statusCode = 400;
1382
- res.end(JSON.stringify({ error: String(err) }));
1383
- }
1384
- return;
1385
- }
1386
- // DELETE /api/whatsapp/group-rules/:id — delete a group rule
1387
- if (urlPath.match(/^\/api\/whatsapp\/group-rules\//) && req.method === "DELETE") {
1388
- // Pattern route — gate here since EXPOSED_DANGEROUS_ROUTES can't express :id
1389
- if (isExposedWithoutPassword()) {
1390
- res.statusCode = 403;
1391
- res.end(JSON.stringify({ error: "refused: web exposed without WEB_PASSWORD" }));
1392
- return;
1393
- }
1394
- const groupId = decodeURIComponent(urlPath.split("/").slice(4).join("/"));
1395
- const { deleteGroupRule } = await import("../platforms/whatsapp.js");
1396
- const ok = deleteGroupRule(groupId);
1397
- res.end(JSON.stringify({ ok }));
1398
- return;
1399
- }
1400
- res.statusCode = 404;
1401
- res.end(JSON.stringify({ error: "Not found" }));
1402
- }
1403
- // ── WebSocket Chat ──────────────────────────────────────
1404
- // Set of all currently connected chat WebSocket clients (excluding canvas).
1405
- // Populated on connect, cleaned up on close. Used to forward Telegram
1406
- // activity to every observer.
1407
- const chatClients = new Set();
1408
- /**
1409
- * Wire the broadcast bus once at module load. The bus is singleton, so
1410
- * subscribing here means every Telegram message fan-outs to every connected
1411
- * chat client — without any per-connection re-subscription.
1412
- */
1413
- broadcast.on("user_msg", (payload) => {
1414
- if (payload.platform !== "telegram")
1415
- return; // v4.5.0: telegram only for now
1416
- const json = JSON.stringify({
1417
- type: "mirror:user_msg",
1418
- text: payload.text,
1419
- platform: payload.platform,
1420
- userName: payload.userName,
1421
- ts: payload.ts,
1422
- });
1423
- for (const client of chatClients) {
1424
- if (client.readyState === WebSocket.OPEN)
1425
- client.send(json);
1426
- }
1427
- });
1428
- broadcast.on("response_start", (payload) => {
1429
- if (payload.platform !== "telegram")
1430
- return;
1431
- const json = JSON.stringify({ type: "mirror:response_start", platform: payload.platform, ts: payload.ts });
1432
- for (const client of chatClients) {
1433
- if (client.readyState === WebSocket.OPEN)
1434
- client.send(json);
1435
- }
1436
- });
1437
- broadcast.on("response_delta", (payload) => {
1438
- if (payload.platform !== "telegram")
1439
- return;
1440
- const json = JSON.stringify({ type: "mirror:response_delta", delta: payload.delta, platform: payload.platform, ts: payload.ts });
1441
- for (const client of chatClients) {
1442
- if (client.readyState === WebSocket.OPEN)
1443
- client.send(json);
1444
- }
1445
- });
1446
- broadcast.on("response_done", (payload) => {
1447
- if (payload.platform !== "telegram")
1448
- return;
1449
- const json = JSON.stringify({ type: "mirror:response_done", cost: payload.cost, platform: payload.platform, ts: payload.ts });
1450
- for (const client of chatClients) {
1451
- if (client.readyState === WebSocket.OPEN)
1452
- client.send(json);
1453
- }
1454
- });
1455
- function handleWebSocket(wss) {
1456
- wss.on("connection", (ws, req) => {
1457
- // Auth check
1458
- if (WEB_PASSWORD && !checkAuth(req)) {
1459
- ws.close(4001, "Not authenticated");
1460
- return;
1461
- }
1462
- // Canvas WebSocket — separate handler
1463
- const wsUrl = req.url || "/";
1464
- if (wsUrl === "/canvas/ws") {
1465
- addCanvasClient(ws);
1466
- return;
1467
- }
1468
- console.log("WebUI: client connected");
1469
- chatClients.add(ws);
1470
- ws.on("message", async (data) => {
1471
- try {
1472
- const msg = JSON.parse(data.toString());
1473
- if (msg.type === "chat") {
1474
- let { text, effort, file } = msg;
1475
- // v4.5.0: session routing. The client (TUI/WebUI) tells us which
1476
- // session it wants its message to go into. Supported targets:
1477
- // - "tui" → use msg.sessionKey (e.g. "tui:local" or
1478
- // "tui:ephemeral:…"). Isolated from Telegram.
1479
- // - "telegram" → route into the primary Telegram user's session.
1480
- // Responses go back to the client AND to the
1481
- // actual Telegram chat via the broadcast bus.
1482
- // - undefined → backwards-compatible: default to the primary
1483
- // allowed user's session (old behavior).
1484
- const target = msg.target;
1485
- const telegramUserId = config.allowedUsers[0] || 0;
1486
- let sessionKey;
1487
- if (target === "tui" && typeof msg.sessionKey === "string" && msg.sessionKey.startsWith("tui:")) {
1488
- sessionKey = msg.sessionKey;
1489
- }
1490
- else if (target === "telegram") {
1491
- sessionKey = telegramUserId;
1492
- }
1493
- else {
1494
- sessionKey = telegramUserId; // backwards compat
1495
- }
1496
- // Handle file upload — save to temp and reference in prompt
1497
- if (file?.dataUrl && file?.name) {
1498
- try {
1499
- const dataDir = resolve(DATA_DIR, "web-uploads");
1500
- if (!fs.existsSync(dataDir))
1501
- fs.mkdirSync(dataDir, { recursive: true });
1502
- const safeName = file.name.replace(/[^a-zA-Z0-9._-]/g, "_");
1503
- const filePath = resolve(dataDir, `${Date.now()}_${safeName}`);
1504
- const base64Data = file.dataUrl.split(",")[1] || file.dataUrl;
1505
- fs.writeFileSync(filePath, Buffer.from(base64Data, "base64"));
1506
- // Replace placeholder with actual file path
1507
- text = text.replace(/\[File attached:.*?\]/, `[File saved: ${filePath}]`);
1508
- }
1509
- catch (err) {
1510
- console.error("WebUI file upload error:", err);
1511
- }
1512
- }
1513
- const registry = getRegistry();
1514
- const activeProvider = registry.getActive();
1515
- const isSDK = activeProvider.config.type === "claude-sdk";
1516
- const session = getSession(sessionKey);
1517
- const queryOpts = {
1518
- prompt: text,
1519
- systemPrompt: buildSystemPrompt(isSDK, session.language, target === "telegram" ? "telegram" : "web-dashboard"),
1520
- workingDir: session.workingDir,
1521
- effort: effort || session.effort,
1522
- sessionId: isSDK ? session.sessionId : null,
1523
- history: !isSDK ? session.history : undefined,
1524
- };
1525
- let gotDone = false;
1526
- let finalText = ""; // v4.5.0: capture the final response for target=telegram relay
1527
- try {
1528
- // Stream response
1529
- for await (const chunk of registry.queryWithFallback(queryOpts)) {
1530
- if (ws.readyState !== WebSocket.OPEN)
1531
- break;
1532
- switch (chunk.type) {
1533
- case "text":
1534
- if (chunk.text)
1535
- finalText = chunk.text;
1536
- ws.send(JSON.stringify({ type: "text", text: chunk.text, delta: chunk.delta }));
1537
- break;
1538
- case "tool_use":
1539
- ws.send(JSON.stringify({ type: "tool", name: chunk.toolName, input: chunk.toolInput }));
1540
- break;
1541
- case "done":
1542
- gotDone = true;
1543
- if (chunk.text)
1544
- finalText = chunk.text;
1545
- if (chunk.sessionId)
1546
- session.sessionId = chunk.sessionId;
1547
- if (chunk.costUsd)
1548
- session.totalCost += chunk.costUsd;
1549
- if (chunk.inputTokens)
1550
- session.totalInputTokens = (session.totalInputTokens || 0) + chunk.inputTokens;
1551
- if (chunk.outputTokens)
1552
- session.totalOutputTokens = (session.totalOutputTokens || 0) + chunk.outputTokens;
1553
- ws.send(JSON.stringify({
1554
- type: "done", cost: chunk.costUsd, sessionId: chunk.sessionId,
1555
- inputTokens: chunk.inputTokens, outputTokens: chunk.outputTokens,
1556
- sessionTokens: { input: session.totalInputTokens || 0, output: session.totalOutputTokens || 0 },
1557
- }));
1558
- break;
1559
- case "error":
1560
- ws.send(JSON.stringify({ type: "error", error: chunk.error }));
1561
- gotDone = true; // error counts as done
1562
- break;
1563
- case "fallback":
1564
- ws.send(JSON.stringify({ type: "fallback", from: chunk.failedProvider, to: chunk.providerName }));
1565
- break;
1566
- }
1567
- }
1568
- // Ensure we always send done (in case stream ended without done/error chunk)
1569
- if (!gotDone && ws.readyState === WebSocket.OPEN) {
1570
- ws.send(JSON.stringify({ type: "done", cost: 0 }));
1571
- }
1572
- // v4.5.0: if the user typed in the TUI with target=telegram, we
1573
- // must also post the bot's final response to the actual Telegram
1574
- // chat so the continuity is preserved from the Telegram side.
1575
- // (Telegram bots cannot forge user messages, so only the
1576
- // response lands in the chat — the user prompt itself stays
1577
- // in the TUI.)
1578
- if (target === "telegram" && finalText.trim()) {
1579
- try {
1580
- const dq = await import("../services/delivery-queue.js");
1581
- dq.enqueue("telegram", String(telegramUserId), finalText);
1582
- }
1583
- catch (err) {
1584
- console.error("WebUI → Telegram relay failed:", err);
1585
- }
1586
- }
1587
- }
1588
- catch (streamErr) {
1589
- const errMsg = streamErr instanceof Error ? streamErr.message : String(streamErr);
1590
- console.error("WebUI stream error:", errMsg);
1591
- if (ws.readyState === WebSocket.OPEN) {
1592
- ws.send(JSON.stringify({ type: "error", error: errMsg }));
1593
- if (!gotDone) {
1594
- ws.send(JSON.stringify({ type: "done", cost: 0 }));
1595
- }
1596
- }
1597
- }
1598
- }
1599
- if (msg.type === "reset") {
1600
- // v4.5.0: reset the target session, not a hardcoded one.
1601
- const target = msg.target;
1602
- const telegramUserId = config.allowedUsers[0] || 0;
1603
- let resetKey;
1604
- if (target === "tui" && typeof msg.sessionKey === "string" && msg.sessionKey.startsWith("tui:")) {
1605
- resetKey = msg.sessionKey;
1606
- }
1607
- else {
1608
- resetKey = telegramUserId;
1609
- }
1610
- resetSession(resetKey);
1611
- ws.send(JSON.stringify({ type: "reset", ok: true }));
1612
- }
1613
- }
1614
- catch (err) {
1615
- const error = err instanceof Error ? err.message : String(err);
1616
- ws.send(JSON.stringify({ type: "error", error }));
1617
- }
1618
- });
1619
- ws.on("close", () => {
1620
- console.log("WebUI: client disconnected");
1621
- chatClients.delete(ws);
1622
- });
1623
- });
1624
- }
1625
- // ── Start Server ────────────────────────────────────────
1626
- /**
1627
- * HTTP request handler for the web UI. Hoisted to a top-level function
1628
- * so every bind attempt can create a fresh http.Server without
1629
- * rebuilding the handler closure.
1630
- */
1631
- function handleWebRequest(req, res) {
1632
- let body = "";
1633
- req.on("data", (chunk) => { body += chunk; });
1634
- req.on("end", () => {
1635
- const urlPath = (req.url || "/").split("?")[0];
1636
- // OpenAI-compatible API (/v1/chat/completions, /v1/models)
1637
- if (urlPath.startsWith("/v1/")) {
1638
- handleOpenAICompat(req, res, urlPath, body);
1639
- return;
1640
- }
1641
- // API routes
1642
- if (urlPath.startsWith("/api/")) {
1643
- handleAPI(req, res, urlPath, body);
1644
- return;
1645
- }
1646
- // v5.7.0 — internal bot-to-bot routes (detached sub-agent exit
1647
- // push). Dispatched through handleAPI, which bearer-auths it and
1648
- // returns before the cookie-auth gate. Kept off the /api/ prefix so
1649
- // it is never surfaced in the Web UI route surface.
1650
- if (urlPath.startsWith("/internal/")) {
1651
- handleAPI(req, res, urlPath, body);
1652
- return;
1653
- }
1654
- // Auth page (if password set and not authenticated)
1655
- if (WEB_PASSWORD && !checkAuth(req) && urlPath !== "/login.html") {
1656
- res.writeHead(302, { Location: "/login.html" });
1657
- res.end();
1658
- return;
1659
- }
1660
- // Canvas UI
1661
- if (urlPath === "/canvas") {
1662
- const canvasFile = resolve(PUBLIC_DIR, "canvas.html");
1663
- try {
1664
- const content = fs.readFileSync(canvasFile);
1665
- res.setHeader("Content-Type", "text/html");
1666
- res.end(content);
1667
- }
1668
- catch {
1669
- res.statusCode = 404;
1670
- res.end("Not found");
1671
- }
1672
- return;
1673
- }
1674
- // Static files
1675
- let filePath = urlPath === "/" ? "/index.html" : urlPath;
1676
- filePath = resolve(PUBLIC_DIR, filePath.slice(1));
1677
- // Security: prevent path traversal
1678
- if (!filePath.startsWith(PUBLIC_DIR)) {
1679
- res.statusCode = 403;
1680
- res.end("Forbidden");
1681
- return;
1682
- }
1683
- try {
1684
- const content = fs.readFileSync(filePath);
1685
- const ext = path.extname(filePath);
1686
- res.setHeader("Content-Type", MIME[ext] || "application/octet-stream");
1687
- res.end(content);
1688
- }
1689
- catch {
1690
- res.statusCode = 404;
1691
- res.end("Not found");
1692
- }
1693
- });
1694
- }
1695
- /**
1696
- * Kick off the web-UI bind loop. NEVER throws, NEVER blocks.
1697
- *
1698
- * History: earlier versions returned an http.Server synchronously and
1699
- * let listen() errors bubble up as uncaught exceptions — a colleague
1700
- * flagged this on 2026-04-13 after spending months fighting the exact
1701
- * same bug on a parallel OpenClaw fork. Their resolution: "the gateway
1702
- * is a feature, not core. Decouple it."
1703
- *
1704
- * New contract:
1705
- * - Returns `void` immediately. The actual bind happens asynchronously.
1706
- * - If port 3100 is busy, tries 3101…3119 in sequence (same as before).
1707
- * - If ALL 20 ports are busy, schedules a background retry at 3100
1708
- * in `BACKGROUND_RETRY_MS` — keeps trying forever until success
1709
- * or stopWebServer() is called.
1710
- * - Any non-EADDRINUSE error also falls through to background retry.
1711
- * - Each attempt uses a FRESH http.Server to avoid node's fragile
1712
- * "listen-called-twice" state-recycling behaviour.
1713
- * - The main Telegram bot is completely independent of this — if the
1714
- * web UI never binds, the bot still answers messages.
1715
- */
1716
- export function startWebServer() {
1717
- stopRequested = false;
1718
- scheduleBindAttempt(parseInt(process.env.WEB_PORT || "3100", 10), 0);
1719
- }
1720
- function scheduleBindAttempt(port, attempt) {
1721
- if (stopRequested)
1722
- return;
1723
- // Read WEB_PORT live every time rather than closing over the
1724
- // module-load value, so tests that change process.env.WEB_PORT
1725
- // between runs see the new port.
1726
- const originalPort = parseInt(process.env.WEB_PORT || "3100");
1727
- // Fresh server for each attempt. Recycling a server that has already
1728
- // emitted an EADDRINUSE error has produced "Listen method has been
1729
- // called more than once" crashes in the wild.
1730
- //
1731
- // IMPORTANT: do NOT attach the WebSocketServer yet. The `ws` library
1732
- // installs its own event plumbing on the http.Server in its
1733
- // constructor, which causes bind errors to escape as uncaught
1734
- // exceptions. We only attach it AFTER listen() has succeeded.
1735
- const server = http.createServer(handleWebRequest);
1736
- // Double-invocation guard: on some Node versions `server.listen`
1737
- // both throws synchronously AND emits an `error` event for the same
1738
- // bind failure. Without the guard we'd climb the ladder twice in
1739
- // parallel and end up with two retry cascades racing each other.
1740
- let handled = false;
1741
- const cleanupDeadAttempt = () => {
1742
- try {
1743
- server.removeAllListeners("error");
1744
- }
1745
- catch { /* ignore */ }
1746
- try {
1747
- server.close(() => { });
1748
- }
1749
- catch { /* ignore */ }
1750
- };
1751
- const handleBindFailure = (err) => {
1752
- if (handled)
1753
- return;
1754
- handled = true;
1755
- cleanupDeadAttempt();
1756
- if (stopRequested)
1757
- return;
1758
- const action = decideNextBindAction(err, attempt, {
1759
- originalPort,
1760
- maxPortTries: MAX_PORT_TRIES,
1761
- backgroundRetryMs: BACKGROUND_RETRY_MS,
1762
- });
1763
- if (action.type === "retry-port") {
1764
- console.warn(`[web] port ${port} busy (${err.code || err.message}) — trying ${action.port}`);
1765
- scheduleBindAttempt(action.port, action.attempt);
1766
- return;
1767
- }
1768
- // action.type === "retry-background"
1769
- console.warn(`[web] bind failed (${err.code || err.message}) — ` +
1770
- `backing off ${action.delayMs / 1000}s then retrying port ${action.port}. ` +
1771
- `Bot is unaffected; Telegram remains live.`);
1772
- bindRetryTimer = setTimeout(() => {
1773
- bindRetryTimer = null;
1774
- scheduleBindAttempt(action.port, 0);
1775
- }, action.delayMs);
1776
- };
1777
- // Use `on` (not `once`) so a pathological server that emits two
1778
- // error events for a single failure doesn't leave the second one
1779
- // uncaught. The `handled` guard makes the handler idempotent.
1780
- server.on("error", handleBindFailure);
1781
- // Defensive try/catch — `server.listen()` usually emits async errors,
1782
- // but certain Node versions + edge cases (already-listening server,
1783
- // invalid backlog, kernel hiccup) can throw synchronously. Catch here
1784
- // so the main routine never crashes during web-UI bind.
1785
- try {
1786
- // v4.20.2 — bind to config.webHost (default 127.0.0.1) so the Web UI
1787
- // is loopback-only unless the operator opts in by setting WEB_HOST=0.0.0.0.
1788
- // Empty/"*" maps to all interfaces.
1789
- const bindHost = (config.webHost === "*" || config.webHost === "") ? undefined : config.webHost;
1790
- server.listen(port, bindHost, () => {
1791
- if (handled)
1792
- return; // Should be impossible; paranoia.
1793
- handled = true;
1794
- // Now — and only now — attach the WebSocketServer. Before the
1795
- // bind succeeded, the ws library's constructor would hijack the
1796
- // http.Server's error event chain and let EADDRINUSE escape as
1797
- // uncaught. Post-bind is safe.
1798
- const wss = new WebSocketServer({ server });
1799
- handleWebSocket(wss);
1800
- currentServer = server;
1801
- wsServerRef = wss;
1802
- actualWebPort = port;
1803
- // Remove the bind error handler — post-listen errors (socket
1804
- // errors, close events) should not kick off a spurious retry
1805
- // cycle. Install a quiet logger for any stray error events so
1806
- // they can't escape as uncaught.
1807
- server.removeListener("error", handleBindFailure);
1808
- server.on("error", (err) => {
1809
- console.warn(`[web] post-bind server error (ignored): ${err.message}`);
1810
- });
1811
- const bindLabel = bindHost && bindHost !== "127.0.0.1" && bindHost !== "::1"
1812
- ? `http://${bindHost}:${actualWebPort}` + (bindHost === "0.0.0.0" ? " (LAN-reachable)" : "")
1813
- : `http://localhost:${actualWebPort}`;
1814
- console.log(`🌐 Web UI: ${bindLabel}`);
1815
- if (actualWebPort !== originalPort) {
1816
- console.log(` (Port ${originalPort} was busy, using ${actualWebPort} instead)`);
1817
- }
1818
- if (isExposedWithoutPassword()) {
1819
- // Upgrade from warn to a clear one-time log: the hard gate (above in
1820
- // handleAPI) already blocks every mutating/exec route in this state,
1821
- // so this message explains why those calls are being refused.
1822
- console.log("[web] Non-loopback host with no WEB_PASSWORD: mutating/exec endpoints are disabled " +
1823
- "(hard-refused 403). Set WEB_PASSWORD in ~/.alvin-bot/.env to unlock them, " +
1824
- "or restrict to loopback with WEB_HOST=127.0.0.1.");
1825
- }
1826
- });
1827
- }
1828
- catch (err) {
1829
- handleBindFailure(err);
1830
- }
1831
- }
1832
- /**
1833
- * Gracefully close a specific http.Server — the low-level building
1834
- * block. Exported for tests and for any future callers that manage
1835
- * their own servers. Production bot code uses `stopWebServer()` below
1836
- * which operates on the module-global current server instead.
1837
- *
1838
- * What this does:
1839
- * 1. Force-close idle keep-alive sockets (Node 18.2+).
1840
- * 2. Force-close active open requests (long-poll clients).
1841
- * 3. Await `server.close()` so the listening socket is truly freed.
1842
- *
1843
- * Safe to call on already-closed, never-listened, or mid-listen servers.
1844
- * Never throws.
1845
- */
1846
- export async function closeHttpServerGracefully(server) {
1847
- if (!server.listening)
1848
- return;
1849
- try {
1850
- const s = server;
1851
- if (typeof s.closeIdleConnections === "function")
1852
- s.closeIdleConnections();
1853
- if (typeof s.closeAllConnections === "function")
1854
- s.closeAllConnections();
1855
- }
1856
- catch { /* ignore */ }
1857
- await new Promise((resolve) => {
1858
- server.close(() => resolve());
1859
- });
1860
- }
1861
- /**
1862
- * Stop the web server: cancel any pending background-retry, close
1863
- * WebSocket clients, then gracefully close the HTTP server.
1864
- *
1865
- * Idempotent — safe to call multiple times, and safe to call before
1866
- * startWebServer() ever successfully bound. Never throws.
1867
- */
1868
- export async function stopWebServer() {
1869
- stopRequested = true;
1870
- // Cancel any pending background-retry timer so a late retry doesn't
1871
- // grab the port AFTER we thought we'd shut everything down.
1872
- if (bindRetryTimer) {
1873
- clearTimeout(bindRetryTimer);
1874
- bindRetryTimer = null;
1875
- }
1876
- // Tear down the WebSocket server first so its sockets can't keep
1877
- // the underlying http.Server alive.
1878
- if (wsServerRef) {
1879
- try {
1880
- for (const client of wsServerRef.clients) {
1881
- try {
1882
- client.terminate();
1883
- }
1884
- catch { /* ignore */ }
1885
- }
1886
- await new Promise((resolve) => wsServerRef.close(() => resolve()));
1887
- }
1888
- catch { /* ignore */ }
1889
- wsServerRef = null;
1890
- }
1891
- if (currentServer) {
1892
- try {
1893
- await closeHttpServerGracefully(currentServer);
1894
- }
1895
- catch { /* ignore */ }
1896
- currentServer = null;
1897
- }
1898
- }
1899
- /** Get the actual port the Web UI is running on. */
1900
- export function getWebPort() {
1901
- return actualWebPort;
1902
- }
1
+ const _0x406ff9=_0x51ee,_0x5471aa=_0x51ee;(function(_0x50c314,_0x50b686){const _0xa38023=_0x51ee,_0x212872=_0x51ee,_0x40d45e=_0x50c314();while(!![]){try{const _0x1ff22f=parseInt(_0xa38023(0x140))/(-0x290*0x2+-0x1*-0x51+0x2c*0x1c)+-parseInt(_0xa38023(0x122))/(0xcab+-0x5*-0x60a+0x45*-0x9f)+-parseInt(_0x212872(0x84))/(-0x23*0x67+-0x1435*-0x1+0x139*-0x5)*(-parseInt(_0x212872(0xc4))/(-0x14*0x4f+-0x1d7+0x807))+parseInt(_0xa38023(0x14a))/(0xb8c+0x2301+-0x2*0x1744)+-parseInt(_0xa38023(0x167))/(-0x2c9+0x60c+0x1*-0x33d)*(parseInt(_0x212872(0x26a))/(0x1a8a+0x1d*-0xc+0x1927*-0x1))+-parseInt(_0xa38023(0x221))/(-0x13ed+0x176b+0x1*-0x376)*(-parseInt(_0x212872(0x163))/(0x116*0x19+0x921+-0x243e))+-parseInt(_0xa38023(0x87))/(0x6b*0x1f+0xeb9+-0x1ba4)*(-parseInt(_0x212872(0x262))/(0x1711+0x1*0x24e4+-0x2*0x1df5));if(_0x1ff22f===_0x50b686)break;else _0x40d45e['push'](_0x40d45e['shift']());}catch(_0x3f7576){_0x40d45e['push'](_0x40d45e['shift']());}}}(_0xf829,0x179ba+0x2d355*0x1+0x67e66));const _0x14bdc8=(function(){let _0x441868=!![];return function(_0x32c7f9,_0x5e2642){const _0x14f0a4=_0x441868?function(){const _0x5e00a4=_0x51ee;if(_0x5e2642){const _0x1f0278=_0x5e2642[_0x5e00a4(0x2b5)](_0x32c7f9,arguments);return _0x5e2642=null,_0x1f0278;}}:function(){};return _0x441868=![],_0x14f0a4;};}()),_0x5f40a4=_0x14bdc8(this,function(){const _0xe7f396=_0x51ee,_0x5da6e0=_0x51ee;return _0x5f40a4[_0xe7f396(0x131)]()[_0x5da6e0(0x205)](_0x5da6e0(0xd7)+'+$')['toString']()[_0x5da6e0(0x2bb)+'r'](_0x5f40a4)[_0xe7f396(0x205)]('(((.+)+)+)'+'+$');});_0x5f40a4();import _0x14bdfc from'http';import _0x1a4331 from'fs';import _0xfff6a9 from'path';import{resolve}from'path';import{execSync}from'child_process';import _0x44ee9b 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';function _0xf829(){const _0x57ce16=['zxiGsuq','y29UDgvUDa','sw52ywXPzcbZAW','B3zPzgvYCW','B3jTCW','BgLZDefSBa','Aw9U','Cg9YDa','zgf0yvvYBa','DxnLCM5HBwu','kgHHCMqTCMvMDq','AwnODcb2zxjIDq','mZeWma','cI0TlqOk','Dg9tDhjPBMC','CMvK','DgvK','zw50igrPC2nVBG','l3nHDMu','BNb4','BwLYCM9YoNjLCW','DxnLCG','Bg93zwq','ChvZAa','yxbWl2DYB3vWlq','lNPZAa','zwrLzcb0BYbJBW','v2vIvuK6ignSAq','y29UBMvJDgLVBG','mJK4mJe5uNr5r09V','BNbTlwDSB2jHBa','igj1C3KGka','sw52ywXPzcbkuW','Dg90ywXdB3n0','zgvYCY9HzgqTyW','zgvYCY9ZzxqTzG','zMzLy3rLzdSGva','r09pr0Xfx0fqsq','l2fWAs9ZzxnZAq','mtyYntyWnwvMswjxyW','w0zPBguGDg9Via','CMvHzg1L','CgfJA2fNzs5QCW','CM9YoG','lNjI','ihvYBcbYzxf1Aq','l2fWAs9Ty3aVyq','lNbYAxnTyq','y2HHDeLK','DhjPz2DLCNm6ia','tM8GDg9VBcbUyq','r1jpuv9bueLFsW','DxnLCL9TC2C','DhLWzq','BwfRzwzPBgu','lNbYzxr0AwvYCG','y29TBwfUzcbVCG','tKfcteve','C2v0sgvHzgvY','zsbKzwXLDgvK','y2XPzw50CW','lMfZDhjV','lMH0Bq','Dc5QCW','nZe2mZu3n1jtANbWEG','l2fWAs9MAwXLCW','lw9YzgvYlMPZ','x1vsta','mJa0ntm0uxbpr0nq','Dg90ywXjBNb1Da','lxrOAw5RAw5N','lMjHyMvSCMm','Dg9VBfvZzunVDq','ywnRihDPDgGGvW','Dgv4Dc9TyxjRza','D2fYBG','tM90igzVDw5K','Bg9JywXOB3n0','l2LUDgvYBMfSlW','DgLUzY9LEgvJia','y29SB3i','BxmVD2HHDhnHCa','zwqG','zMfSBgjHy2TqCG','w3DLyL0GCg9YDa','ic0TzgvWDgG9ma','s0vo','x0Tfwq','rgLYzwn0B3jPzq','Dw5SAw5Ru3LUyW','AxrODwi','mc4WlJaUma','u0TjteWUBwq','y2HHBM5LBa','igzHAwXLzcaO','y29Kzq','y2fUDMfZlMH0Bq','zxjZAw9U','Aw5KzxHpzG','v3jVBMCGCgfZCW','zNjVBq','z3vYzq','l2fWAs9Ty3a','if8O','C2L6zuj5DgvZ','lNnXBa','rujFueftu1DpuG','Cg9UC2vFzg9Uzq','zxH0ChjVDg9JBW','r0vu','l21JCc1Zzxj2zq','CMfRzwzPBgu','qwnJzxnZigrLBG','Bc9Zzxj2zxiTyG','C3rKzxjY','Dg90ywXpDxrWDq','q29UDgvUDc1uEq','zw1VDMu','Agv4','lI4VC2vYDMLJzq','BI9QyxzHC2nYAq','z2v0qwn0AxzL','yw1PBMCVq2XHDq','DhjPy3q7ie1HEa','DhvPoG','C3DPDgnOvg8','AgLZDg9YEq','u2TPBgWGBM90ia','ywDLBNrjza','AgfIBguP','CY91CgrHDgu','t04GyM9KEq','BhvLoIbUzxDSAq','ChjVy2zPBgu','Cg9UC2vFzgvSDa','CMvTB3zLtgLZDa','lxDPEMfYza','icaGkfbVCNqG','z2v0qwn0AxzLsW','x0fqsv9lrvK','lM5WBxjJ','DgvZDa','zxmUANm','l2fWAs9WBhvNAq','lNn2zW','zw1VAMK','zsbYzxf1AxjLza','Cc5QCW','Dgv4Dc9ODg1S','C2L6zq','ywrTAw4TzgLHBa','BwvZC2fNzunVDq','Bc9Zzxj2zxiTzG','qK9ux1rps0vo','y2XHDwrLlwrLCW','DhnJB25MAwCUAG','C3rYAw5N','BYbSB25NicHTyq','lMnMzW','zM91BMq','ywrK','rgLZy29Yza','uMvZDgfYDgLUzW','Dg9VBe5HBwu','yM90vg9Rzw4','Bc9Zzxj2zxiTCW','y3DK','CYb0AgvUihjLDa','CMvHBq','Dxb0Aw1L','l2fWAs90zxjTAq','D2vIlwrHC2HIBW','z2v0uMfUzg9TvG','C3rHDhvZq29Kzq','Cg9ZDgDYzxm','BM5Ly3qU','Dg9mB3DLCKnHCW','vgvSzwDYyw0','BM5Ly3rPB25Z','lNLHBwW','C3vIywDLBNqTzq','lMPZB24','Aw1Hz2uVANbLzW','z2v0r3jVDxbqyq','yxjNCW','lMPZ','C3nHz2uGzMLLBa','DMfNCMfUDgzPBa','DhjPBuvUza','y29UzMLNlMnQCW','BI9QC29U','yxv0Ag9YCW','Bg9VCgjHy2SGAa','B3bLBNjVDxrLCG','ywXSB3DLzfvZzq','zNvUy3rPB24','Dg9VBf91C2u','oJOX','lMjHC2G','qu5usfjpueLdxW','oI9VChqVAg9Tzq','quXmt1Dfrf9vuW','BNb4ic0TEwvZia','lMn0CW','EgL0xsbKzwXPDG','ChjVDMLKzxjoyq','mti3lJaUmc4X','Bwf0y2G','yxbPs2v5','DhvP','CMLUzW','EcaXmdaWmcbJAa','ieTciokaLcbTyxGG','l2rLBgv0zq','DMfSDwvZ','DfrVA2vUCW','DxjLza','l2fWAs9JAgf0lW','y2XVC2u','BMfTztOG','D29Yza','D3jPDgvizwfK','zMLSzxn5C3rLBq','zwnVC3LZDgvTlG','ANnVBG','DxrMltG','C2vHCMnO','CMvHzhLtDgf0zq','Ahr0CdOVlW','qg1VzgvSy29UDa','l2fWAs9WBgf0zG','l2fWAs9Ty3aVCG','D2vIlxvWBg9Hza','l2fWAs9MywXSyG','CgfKu3rHCNq','ChvWCgv0zwvY','BgvUz3rO','D2vIsg9ZDa','zw0Sia','lM52BxjJ','y2XHDwrLlxnKAW','D2vIAg9VA1rVAW','u2v0ifDfqL9qqq','Axn0zw5LCNm','y29ZDfvZza','BMfTzq','zxj5igvYCM9YoG','lxf1zxvLlMPZ','u2fTzvnPDgu9uW','zwzMB3j0','vg9Rzw5Z','C3rHCNrZv2L0Aa','ChjVDgvJDgvKkq','y29TBwfUza','oeTJthn4zG','t1bftLjpvvrfuG','l2fWAs93Agf0CW','B3iGCMvZDhjPyW','yxvKzs9JBgf1za','l2fWAs9JCM9UlW','BMvJDa','CY91C2vYCY5QCW','l2fWAs9TB2rLBa','CxvLCNLxAxrOrG','CgXHDgzVCM0','l2fWAs9Ty3aVza','DgvSzwDYyw0','zg9JA2vYzMLSzq','kLn5C3rLBtOQ','ntaWieTcxq','FI8UywX2Aw4TyG','C2vXDwvUDgLHBa','w1DLyMHVB2S6ia','l2fWAs92ywXPza','yxjLigrPC2fIBa','CNrPy2LWyw50CW','B3DU','yNjLDY9IAw46lW','lMnWCa','Dxn0B20','v0vcx1bbu1nxtW','ywX2Aw5IB3rFDa','BMfS','CMvHzezPBgvtEq','l2v4zwn1Dgu','igLUC3rLywqP','DxnLCKLK','t1bftG','CMvHzgrPCLn5BG','yxbPs2v5CW','DgLTzq','B25Uzwn0Aw9UCW','zxnZ','A2v5CW','C3rHDfn5BMm','yNjLD2zPBgu','Aw1Hz2uVCg5N','DxbWzxrLzxi','zxHLyW','BxvZDcbUB3qGyW','reLtq09srf9utW','lMH0BwW','y2f0zwDVCNK6ia','ksdIGjqGDhj5Aw5N','C2f2zq','CgvUzgLUzW','Aw1Hz2uVEc1PyW','yMXLza','DgfYz2v0','yNjHDMuTC2vHCG','zMfPBgvKuhjVDG','BM9Uzq','rvjt','DhjPBq','y2fRzwzPBgu','CMf2zs1ZzwfYyW','CMvMDxnLzdOGDW','CgTN','yxjZkq','mJK1ndKXownYt1D6sG','lNn2zwX0zq','xWOkls0TcGO','zxrJAa','CY9MywXSyMfJAW','BgfUz3vHz2u','l2fWAs9Yzxn0yq','EgL0','nZDgD0fQs2i','BYbxrujFueftuW','BMuGy2HHCMfJDa','A2vU','l3yXlW','BwvTB3j5','AwXSigLK','AwXLC3LZDgvT','lM10CW','BwTKAxjtEw5J','C3rYAw5NAwz5','lMDPDgLNBM9Yzq','lM9YzY9IB3q','CY9JCMvHDgu','sw52ywXPzcbMAq','yMfJA2LUzYbVzG','y2HPBgrFChjVyW','B3v0Chv0vg9Rzq','l3rTCa','y2HHDa','u0vduKvu','ywnR','Bc9Zzxj2zxiTCa','CY93B3jRC3bHyW','lNnO','zMLSzxm','ksdIGjqG','CgfYC2u','C29YDa','zgvSyxLnCW','zgLY','DxnYl2XVy2fSlW','Dg9VBeLUChv0','l2nHBNzHCW','Ew5J','yxbWl2rPC2nVBG','B25Z','Dhj1zq','zv9KzxnRDg9WxW','B2TLBJ0','zg9Uzq','BM93','yxbWl2DYB3vWCW','lMLUAq','zMLUza','Aw1Hz2uVC3zNkW','AxnqCM9JzxnZAq','ignOyw5NzxmGDa','revmrvrf','BM9Kzv9TB2r1Ba','u2LNBMfS','B3bLBMfP','DxjS','lMDPDgf0DhjPyG','l2fWAs90B29SCW','Dgv4Dc9JC3m','CM9Szq','l2nHBNzHCY93CW','zw5KC1DPDgG','B25L','D29YA2LUz0rPCG','AM9PBG','yxbPs2v5rw52','BwvZC2fNzq','u2v0lunVB2TPzq','CMv2B2TL','BgWTzgvWCW','Cgf0Aa','y3jLyxrLu2vYDG','yxj0ig5LzwrLza','CMvZDwX0','Aw5WDxruB2TLBG','lMPHDMe','ChjPBwfYEvbYBW','CNvU','yxbWBhK','lMnZ','lI4U','BMrLBG','BYb0ywTLigvMzG','yxv0Ag9YAxPHDa','y29UC3rYDwn0BW','lNb5','BNbT','z2L0AhvI','zwX0yq','zxHPC3rZu3LUyW','Dcb0BYbSB29WyG','lMnVBMzPzY9JBa','v2vIvuKGC3rYzq','z29Vz2XL','DgvYBwLUyxrL','ywXSyMfJAW','CNLPBMCGCg9YDa','z2v0','u2v0DxaGy29TCa','lwjPBMqGC2vYDG','lNjZ','AwvK','zgvSzxrL','CY9ZA2LSBhmUAG','z2vTzMLSzq','l2fWAs9ZA2LSBa','lNrZ','BNzPzgLH','ihDPDgHVDxqGvW','B2XSzxi','sw52ywXPzcb0BW','lMHWCa','tM90igf1DgHLBG','lNr4Da','CgLWzq','igrLBgv0zwqGka','lMvZBgLUDhjJ','BwLYCM9YoNvZzq','z2v0r3jVDxbZ','C3rHCNrLzef0','tLzjreLbx0fqsq','yw0GzxjYB3i6','lMT0','zwXLz3jHBsbYzq','y2XVC2vbBgXdBW','iYbdAgf0iev4Ca','l2LUzgv4lMH0Bq','BwvZC2fNzvf1zq','vvnfuLbst0zjta','q29TBwfUzcb0BW','l2fWAs9LBNyVCW','l2XVz2LUlMH0Bq','zw50CMLLCW','lMDYyxbOCwW','w0zPBguGC2f2zq','zw52ihzHBhvLia','AxnHyMXLza','ufjjtufswv9quG','yxjL','zxjZig5VDcbHBa','se9nrq','Bg9N','ywWTDgHPBMTPBG','C3bSAxq','l2fWAs9JB25MAq','lNrVBwW','yxjK','ihDHCYbIDxn5la','D2vIAg9VA0vUyq','zMLSDgvY','CYbJyw5UB3qGyG','tM8Gy29TBwfUza','ywLSzwq','AxnbyNnVBhv0zq','sw52ywXPzcbRzq','zw52','ignHBM5VDcbIzq','CgXPy2f0Aw9Uia','lI4Vy29UzMLNlG','z3jVDxbjza','otuYodzWvwPQt0S','CMvZCg9UC2vFza','AgvHzgvYCW','mtbiyMniDvC','DMLKzxi','tM90ignVBMzPzW','Bg9JywXLq29TCa','lMzPC2G','yxbWBgLJyxrPBW','CMv0CNKTCg9YDa','zgvSDge','yMfZzty0','zMLYC3rFBMfTzq','ps4Qja','AxnbCNjHEq','ywX1zxm','l2fWAs9Zzxr1Ca','l2fWAs9SB2DPBG','Dg9mB2nHBgvtDa','AgfZ','Dw5RBM93BG','Eg1S','zxjYB3i','zw5K','l2fWAs9Tzw1VCG','DgfYDa','twLZC2LUzYbTzq','tMfTzsbYzxf1Aq','Cg9UC2vFC3rHCG','l2fWAs91C2vYCW','lMnVBMy','B24VCMvZzxq','ihvZAw5Nia','lI4VCgXHDgzVCG','Dgv4Da','zgvYCY9Yzw1VDG','sw52ywXPzcb1CW','CI5QCW','zguVy2XHDwrLxW','zxH0BMfTzq','l2fWAs9WCM92Aq','ihvWBg9HzcbLCG','D3jPDgvgAwXLuW','ls0T','yxr0zw1WDa','uefusa','AxneAxjLy3rVCG','yMfZzw5HBwu','CMvWBgfJzq','CY9KzwXLDgu','yxKGzMfPBgvKoG','s0vz','lMXVzW','Bw92zurVD24','zwn0','y2XHDwrLlq','v2vIvuKG4OAsifrL','DcbLCMzVCMrLCG','zgvZy3jPChrPBW','w2LUDgvYBMfSlW','8j+mKcbxzwiGvuK6ia','AwrLCG','zxzLBNq','C2vYDMvYCW','mJbgtg1vA1C','CY9OzwfYDgjLyq','l2fWAs9ZDgf0Dq','CMvZzxq','BYbSyxjNzq','CY9ZD2L0y2G','CM92AwrLCG','oYbqyxrOps87ia','w3DLyL0GtM9Ulq','B3jTCY9JB25MAq','lNLTBa','qM90igLZihvUyq','Cg9W','CxvLC3q','As50zwXLz3jHBq','CI1Zzxf1zw50Aq','lMvUDG','kIPeDtOQkG','ve9lru4','kcGOlISPkYKRkq','CM1tEw5J','zxiGzxjYB3iGka','vw5HDxrOB3jPEG','C2XPy2u','zxHWB3j0','ywXSyMfJA3m','l2fWAs9ZB3vSlW','zwiGzxHWB3nLza','zw50lxDHDgnOzq','A2v5','BI9Vy3rLDc1ZDa','C29Tzq','CMvZCg9UC2vFCW','sw52ywXPzcbYzq','u0LhtKfmx0fqsq','lM1QCW','l2fWAs9ZDwrVlW','y2XVC2vjzgXLqW','B3jTCY9PBNn0yq','qgfUDgHYB3bPyW','lMXVy2S','Bw9KzwW','BwfW','CxvLCMLLC0j5ua','zgvSAxzLCNKGzG','zgvYCY9ZzxqTCa','B3n0ihDPDgGGBG','B3qVlMvUDIb0BW','ue9tva','A3rVCa','zw5LCG','CgfJzxm','CNvSzxm','CMv2zxjZzq','lNbOCa','lwnOzwnR','z3jVDxbjzcbPCW','zw5XDwv1zq','u1nxt1jeigLUia','qxbWrgf0ys9sBW','sw52ywXPzcb2yq','ic0TAgvSCa','tuvnt1jzlM1K','CMfUzg9TqNL0zq','BMvJDgvK','Aw5JBhvKzxm','C3rHDhvZ','lM1K','ywXOB3n0oG','D2vIugfZC3DVCG','C2vUza','Ahr0Chm6lY9HCa','B3vUza','CY9KzwXPDMvYEq','CY9ZzxnZAw9UlG','v2HHDhnbCha','BgLUzsbJAgfYyq','t1zjrevs','lMnQCW','CMvTB3zLqwXSta','t1bftKfjx0fqsq','C2vZC2LVBKTLEq','lufNzt04nJqWma','DgfZA2zPBgu','C2vZC2LVBKLK','BwnWu2vYDMvYCW','Bwv0Ag9K','y29VA2LL','qvbjx0Tfwq','zw50ignVBM5LyW','v0vcx1bpuLq','BJOG','zMLN','BMzPzY5QC29U','mtuYodyXnLvAtfPiCa'];_0xf829=function(){return _0x57ce16;};return _0xf829();}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';const WEB_PORT=parseInt(process[_0x406ff9(0x302)]['WEB_PORT']||_0x5471aa(0x12f)),MAX_PORT_TRIES=-0x133*-0x13+0x1bbb+-0x3270,BACKGROUND_RETRY_MS=-0x4b2*-0x2+0x17*-0x26b+-0xa369*-0x1;let currentServer=null,wsServerRef=null,bindRetryTimer=null,stopRequested=![];const WEB_PASSWORD=process[_0x5471aa(0x302)]['WEB_PASSWO'+'RD']||'',INTERNAL_TOKEN=_0x44ee9b[_0x5471aa(0x103)+'s'](-0x1*-0x1a21+-0x10ad+-0x95c*0x1)[_0x406ff9(0x131)](_0x406ff9(0x199));export function getInternalToken(){return INTERNAL_TOKEN;}let actualWebPort=WEB_PORT;const MIME={'.html':'text/html','.css':_0x406ff9(0x2a1),'.js':_0x5471aa(0x8c)+_0x5471aa(0x19b)+'pt','.json':_0x5471aa(0x8c)+_0x406ff9(0x1e1),'.png':_0x406ff9(0x24b),'.jpg':_0x5471aa(0x1d9),'.svg':_0x5471aa(0x297)+_0x5471aa(0x99),'.ico':_0x406ff9(0x255)+'on'},activeSessions=new Set();function generateToken(){const _0x470138=_0x5471aa,_0x1c87f6=_0x5471aa;return Array['from'](_0x44ee9b[_0x470138(0x1cf)+_0x1c87f6(0x93)](new Uint8Array(-0x22d5+-0x14b+0x740*0x5)))['map'](_0x3db2f0=>_0x3db2f0[_0x1c87f6(0x131)](0xba*-0xb+-0xf58+0x1766)[_0x1c87f6(0x20d)](-0x14e8+-0x1476+0x20*0x14b,'0'))[_0x470138(0x2a7)]('');}function checkAuth(_0x51220a){const _0x5c6deb=_0x5471aa,_0x1cec21=_0x406ff9;if(!WEB_PASSWORD)return!![];const _0x187b0e=_0x51220a[_0x5c6deb(0x86)][_0x5c6deb(0x11b)]||'',_0x407da1=_0x187b0e[_0x5c6deb(0x1f2)](/alvinbot_token=([a-f0-9]+)/)?.[0x25*-0xcc+0x21b8+-0x43b];return _0x407da1?activeSessions['has'](_0x407da1):![];}function isExposedWithoutPassword(){const _0x116bac=_0x406ff9,_0x25f1c9=_0x5471aa;if(WEB_PASSWORD)return![];const _0x451bdb=config[_0x116bac(0x210)];if(!_0x451bdb||_0x451bdb==='127.0.0.1'||_0x451bdb==='::1'||_0x451bdb===_0x116bac(0x170))return![];return!![];}async function handleAPI(_0x424589,_0xd11ab9,_0xaf266c,_0x341686){const _0x173281=_0x406ff9,_0x537513=_0x5471aa;_0xd11ab9[_0x173281(0x15d)](_0x537513(0x197)+'pe',_0x537513(0x8c)+_0x537513(0x1e1));if(_0xaf266c===_0x537513(0x95)&&_0x424589[_0x173281(0x11a)]===_0x173281(0xf4)){try{const {password:_0x45372d}=JSON['parse'](_0x341686),_0x4c2554=timingSafeBearerMatch('Bearer\x20'+_0x45372d,WEB_PASSWORD);if(_0x4c2554){const _0x17babd=generateToken();activeSessions[_0x173281(0x1c3)](_0x17babd),_0xd11ab9['setHeader'](_0x537513(0x2aa),_0x173281(0x23c)+_0x173281(0x291)+_0x17babd+(_0x173281(0xcb)+'HttpOnly;\x20'+_0x537513(0x21b)+_0x537513(0x19e)+_0x537513(0x116))),_0xd11ab9[_0x173281(0x9b)](JSON[_0x537513(0x274)]({'ok':!![]}));}else _0xd11ab9['statusCode']=-0x4bc+0x268d+-0x2040,_0xd11ab9[_0x173281(0x9b)](JSON[_0x537513(0x274)]({'error':_0x537513(0x186)+_0x537513(0x1ff)}));}catch{_0xd11ab9['statusCode']=0x5*-0xc7+0x33b+0x2*0x11c,_0xd11ab9['end'](JSON['stringify']({'error':_0x537513(0xe5)+'quest'}));}return;}if(_0xaf266c==='/api/webho'+'ok'&&_0x424589[_0x173281(0x11a)]===_0x537513(0xf4)){if(!config[_0x173281(0x2fb)+_0x537513(0x256)]){_0xd11ab9['writeHead'](0xeaa+0x5b2+-0x12c8),_0xd11ab9['end'](JSON[_0x537513(0x274)]({'error':'Webhooks\x20d'+_0x537513(0x2ef)}));return;}if(!timingSafeBearerMatch(_0x424589[_0x537513(0x86)][_0x537513(0x2ba)+_0x173281(0x129)],config[_0x537513(0x214)+'en']??'')){_0xd11ab9[_0x537513(0x200)](-0x10be+-0x1*0xf32+0x3b9*0x9),_0xd11ab9[_0x537513(0x9b)](JSON[_0x173281(0x274)]({'error':_0x173281(0xda)+'ed'}));return;}try{const _0x261d2b=JSON[_0x173281(0x285)](_0x341686);if(!_0x261d2b[_0x173281(0x2a9)]){_0xd11ab9[_0x173281(0x200)](0x89b+0x4*0x104+0xb1b*-0x1),_0xd11ab9[_0x173281(0x9b)](JSON['stringify']({'error':_0x537513(0x9e)+_0x173281(0x1dd)+'d'}));return;}const _0x10ed1c=_0x261d2b[_0x537513(0x180)]||'telegram',_0x1b7353=_0x261d2b[_0x173281(0x153)]||String(config['allowedUse'+'rs'][-0xb7c+0x2*-0xae4+-0x1*-0x2144]||''),{enqueue:_0x32e887}=await import(_0x537513(0x19a)+_0x173281(0x10d)+_0x173281(0x21a)),_0x5acf8e=_0x32e887(_0x10ed1c,_0x1b7353,_0x173281(0x233)+(_0x261d2b[_0x173281(0xc2)]||_0x173281(0x98))+']\x20'+_0x261d2b[_0x537513(0x2a9)]);_0xd11ab9['writeHead'](0x1*0x9e8+0x101*0x17+-0x2037),_0xd11ab9[_0x173281(0x9b)](JSON[_0x537513(0x274)]({'ok':!![],'queued':_0x5acf8e}));}catch{_0xd11ab9[_0x173281(0x200)](-0x41d+0x4*-0x560+0x1b2d),_0xd11ab9['end'](JSON[_0x173281(0x274)]({'error':_0x537513(0x143)+_0x173281(0x1a6)}));}return;}if(_0xaf266c==='/internal/'+_0x537513(0x1d7)+_0x173281(0x269)&&_0x424589[_0x173281(0x11a)]===_0x173281(0xf4)){if(_0x341686['length']>(0x173a*-0x1+-0x1023+0x2765)*(0x1778+-0x228a+0x506*0x3)){_0xd11ab9[_0x537513(0x1d0)]=-0xdb6+0x11ed+-0x29a,_0xd11ab9[_0x537513(0x9b)](JSON[_0x173281(0x274)]({'error':'Payload\x20to'+_0x173281(0xc8)}));return;}if(!timingSafeBearerMatch(_0x424589[_0x537513(0x86)][_0x537513(0x2ba)+_0x173281(0x129)],INTERNAL_TOKEN)){_0xd11ab9['statusCode']=0x7f4+0x1*-0x157a+-0x1*-0xf17,_0xd11ab9[_0x173281(0x9b)](JSON['stringify']({'error':_0x173281(0xda)+'ed'}));return;}let _0x4f5ede;try{const _0x1735e3=JSON['parse'](_0x341686);_0x4f5ede=typeof _0x1735e3[_0x537513(0x1a3)]===_0x173281(0x1bf)?_0x1735e3['agentId']:'';}catch{_0xd11ab9['statusCode']=0x1ee5+-0x1*0x10a1+-0x32d*0x4,_0xd11ab9[_0x537513(0x9b)](JSON[_0x173281(0x274)]({'error':_0x173281(0x143)+'ON\x20body'}));return;}if(!_0x4f5ede){_0xd11ab9[_0x537513(0x1d0)]=-0x18a+-0x931+0xc4b,_0xd11ab9['end'](JSON[_0x173281(0x274)]({'error':'Missing\x20ag'+'entId'}));return;}try{const {deliverByAgentId:_0x49e9f8}=await import(_0x173281(0x19a)+'s/async-ag'+_0x537513(0xe0)+_0x173281(0xa9)),_0x4a4148=await _0x49e9f8(_0x4f5ede);_0xd11ab9[_0x173281(0x1d0)]=_0x4a4148===_0x537513(0x98)?0x1908+0xb3*-0x24+0x1b8:_0x4a4148===_0x173281(0x254)?-0x3ac*-0x9+0xe7b+-0x2ebd:-0x1587+-0x236b+-0x335*-0x12,_0xd11ab9['end'](JSON['stringify']({'ok':_0x4a4148!==_0x173281(0x98),'outcome':_0x4a4148}));}catch(_0x2d8fe1){_0xd11ab9[_0x173281(0x1d0)]=0x1da1+-0x76f*0x4+-0x1*-0x20f,_0xd11ab9[_0x173281(0x9b)](JSON[_0x173281(0x274)]({'error':_0x537513(0xf0)+_0x173281(0x2ff)})),console[_0x537513(0x9a)](_0x537513(0xbf)+_0x537513(0x1d7)+_0x537513(0x1ef)+_0x173281(0x219),_0x2d8fe1);}return;}if(!checkAuth(_0x424589)){_0xd11ab9[_0x173281(0x1d0)]=-0x2*0xa97+-0x9*-0x389+-0x912,_0xd11ab9['end'](JSON[_0x173281(0x274)]({'error':_0x537513(0x2d7)+'ticated'}));return;}const _0x213195=new Set([_0x537513(0x1cd)+_0x537513(0x23d),'/api/skill'+_0x537513(0x277),_0x537513(0x2d0)+_0x537513(0x1a5),_0x173281(0x2d0)+_0x537513(0xb5),_0x537513(0x164)+'/save','/api/files'+_0x173281(0x1f8),_0x537513(0x2e9)+'et',_0x537513(0x94)+_0x173281(0x1ab),_0x173281(0xde)+'save',_0x537513(0x9c)+'y/save',_0x173281(0x9c)+'y/delete',_0x537513(0x149)+_0x173281(0xa3),_0x537513(0x226)+'create',_0x537513(0x226)+'update','/api/cron/'+_0x173281(0x2cd),_0x537513(0x226)+'toggle',_0x173281(0x226)+_0x537513(0x2b4),_0x537513(0x1b2)+'n/install',_0x173281(0x1b2)+'n/uninstal'+'l',_0x537513(0x2a0)+_0x173281(0x23f),_0x173281(0xe8)+_0x537513(0x24d),_0x173281(0xe8)+'setup',_0x537513(0xe8)+_0x537513(0x1b9)+'og',_0x173281(0xe8)+_0x537513(0x2ab),'/api/provi'+'ders/set-k'+'ey',_0x537513(0xac)+_0x537513(0xf1)+'rimary',_0x173281(0xac)+_0x173281(0x146)+_0x173281(0xdd),_0x537513(0xac)+_0x537513(0x145)+_0x537513(0x23a),_0x537513(0xac)+_0x537513(0xa7)+'e-custom',_0x537513(0x209)+_0x537513(0xcd)+_0x537513(0x188),'/api/platf'+_0x173281(0xea)+_0x537513(0x2ac),_0x537513(0x20c)+'ack','/api/fallb'+'ack/move',_0x173281(0x151)+'dd',_0x537513(0x20a)+_0x537513(0x198),_0x173281(0x268)+'rt',_0x173281(0x229)+_0x173281(0xc9),_0x537513(0x223)+_0x537513(0x28d)+_0x537513(0x227),_0x173281(0x223)+_0x173281(0x13b)+_0x173281(0xf8)]);if(isExposedWithoutPassword()&&_0x213195['has'](_0xaf266c)){_0xd11ab9['statusCode']=0x2*-0x116f+0xd5f+0x1712,_0xd11ab9[_0x537513(0x9b)](JSON['stringify']({'error':'refused:\x20w'+_0x537513(0xdf)+'\x20without\x20W'+_0x537513(0x18d)+'D'}));return;}const _0x5ab187=await handleSetupAPI(_0x424589,_0xd11ab9,_0xaf266c,_0x341686);if(_0x5ab187)return;const _0x403387=await handleDoctorAPI(_0x424589,_0xd11ab9,_0xaf266c,_0x341686);if(_0x403387)return;if(_0xaf266c===_0x173281(0x94)+_0x537513(0xfb)){const _0x2a0fed=ENV_FILE;let _0x4d2d1f={};try{const _0x327004=_0x1a4331[_0x173281(0x23e)+'nc'](_0x2a0fed,_0x537513(0x204))[_0x537513(0x2f6)]('\x0a');for(const _0x5aec5f of _0x327004){if(_0x5aec5f[_0x173281(0x21e)]('#')||!_0x5aec5f['includes']('='))continue;const _0x398d44=_0x5aec5f[_0x537513(0x185)]('=');_0x4d2d1f[_0x5aec5f[_0x173281(0xdb)](0x23*-0x67+0x2697+-0x1882,_0x398d44)['trim']()]=_0x5aec5f[_0x537513(0xdb)](_0x398d44+(0x1*0xbfd+0x2*0x89+-0xd0e))['trim']();}}catch{}const _0x27aff7=!!(_0x4d2d1f[_0x537513(0x1bc)]||process[_0x537513(0x302)][_0x537513(0x1bc)]),_0x2f3e3c=!!(_0x4d2d1f['ALLOWED_US'+'ERS']||process[_0x537513(0x302)][_0x537513(0x1ec)+'ERS']),_0x3dde6c=!!(_0x4d2d1f['PRIMARY_PR'+'OVIDER']||process[_0x537513(0x302)][_0x537513(0x2f0)+_0x537513(0x111)]),_0x159bbd={'groq':!!(_0x4d2d1f[_0x537513(0x156)+'EY']||process[_0x173281(0x302)]['GROQ_API_K'+'EY']),'openai':!!(_0x4d2d1f[_0x173281(0x114)+_0x173281(0x17a)]||process['env'][_0x173281(0x114)+_0x537513(0x17a)]),'google':!!(_0x4d2d1f[_0x173281(0x148)+_0x537513(0x17a)]||process[_0x173281(0x302)][_0x537513(0x148)+_0x173281(0x17a)]),'nvidia':!!(_0x4d2d1f[_0x173281(0x2df)+_0x537513(0x17a)]||process[_0x537513(0x302)]['NVIDIA_API'+_0x537513(0x17a)]),'anthropic':!!(_0x4d2d1f[_0x173281(0x1ea)+_0x537513(0x11c)]||process['env'][_0x173281(0x1ea)+_0x537513(0x11c)]),'openrouter':!!(_0x4d2d1f[_0x173281(0x222)+_0x537513(0x1ae)]||process[_0x173281(0x302)][_0x537513(0x222)+'_API_KEY'])},_0x2019e6=_0x3dde6c||Object[_0x173281(0x1f9)](_0x159bbd)[_0x173281(0xe3)](Boolean);let _0x4d38d2=![];try{const {execSync:_0x5c57ff}=await import(_0x173281(0x27a)+'ess');_0x5c57ff('claude\x20--v'+_0x537513(0x184),{'timeout':0x1388,'stdio':_0x173281(0x2d9)}),_0x4d38d2=!![];}catch{}const _0x3e3381=_0x27aff7&&_0x2f3e3c&&_0x2019e6;_0xd11ab9[_0x173281(0x9b)](JSON[_0x537513(0x274)]({'isComplete':_0x3e3381,'steps':{'telegram':{'done':_0x27aff7&&_0x2f3e3c,'botToken':_0x27aff7,'allowedUsers':_0x2f3e3c},'provider':{'done':_0x2019e6,'primary':_0x4d2d1f[_0x173281(0x2f0)+_0x537513(0x111)]||process[_0x173281(0x302)]['PRIMARY_PR'+'OVIDER']||'','keys':_0x159bbd,'claudeCli':_0x4d38d2}}}));return;}if(_0xaf266c===_0x537513(0x94)+_0x537513(0x1ab)&&_0x424589[_0x173281(0x11a)]===_0x173281(0xf4)){try{const _0x38a9f7=JSON[_0x173281(0x285)](_0x341686),_0x4e5e1a=ENV_FILE;let _0x8ca491=_0x1a4331['existsSync'](_0x4e5e1a)?_0x1a4331[_0x537513(0x23e)+'nc'](_0x4e5e1a,_0x537513(0x204)):'';const _0x25be68=(_0x4bd50f,_0xd8c444)=>{const _0x23c9f1=_0x537513,_0x36bd65=_0x173281;if(/[\n\r]/[_0x23c9f1(0x1b0)](_0xd8c444))throw new Error(_0x23c9f1(0x2ee)+_0x36bd65(0x24e)+'ontain\x20new'+_0x36bd65(0x110)+'cters');const _0x2f1ed8=new RegExp('^'+_0x4bd50f+_0x36bd65(0x91),'m');_0x2f1ed8[_0x23c9f1(0x1b0)](_0x8ca491)?_0x8ca491=_0x8ca491[_0x23c9f1(0xb4)](_0x2f1ed8,_0x4bd50f+'='+_0xd8c444):_0x8ca491=_0x8ca491[_0x23c9f1(0x1df)]()+('\x0a'+_0x4bd50f+'='+_0xd8c444+'\x0a'),process['env'][_0x4bd50f]=_0xd8c444;};if(_0x38a9f7['botToken'])_0x25be68('BOT_TOKEN',_0x38a9f7['botToken']);if(_0x38a9f7[_0x537513(0x1e5)+'rs'])_0x25be68(_0x537513(0x1ec)+_0x173281(0x25b),_0x38a9f7[_0x173281(0x1e5)+'rs']);if(_0x38a9f7[_0x537513(0x2b3)+_0x173281(0x88)])_0x25be68(_0x537513(0x2f0)+_0x173281(0x111),_0x38a9f7[_0x173281(0x2b3)+_0x537513(0x88)]);if(_0x38a9f7['apiKey']&&_0x38a9f7[_0x537513(0x2a8)])_0x25be68(_0x38a9f7['apiKeyEnv'],_0x38a9f7[_0x173281(0x1f3)]);if(_0x38a9f7[_0x537513(0x109)+'d'])_0x25be68(_0x537513(0x23b)+'RD',_0x38a9f7[_0x173281(0x109)+'d']);_0x1a4331['writeFileS'+'ync'](_0x4e5e1a,_0x8ca491),_0xd11ab9['end'](JSON[_0x537513(0x274)]({'ok':!![],'note':_0x537513(0x2c9)+'lete!\x20Rest'+_0x537513(0x2af)+'.'}));}catch(_0x48fe28){_0xd11ab9['statusCode']=-0x2026+0x248d+-0x2d7,_0xd11ab9[_0x173281(0x9b)](JSON[_0x173281(0x274)]({'error':_0x48fe28 instanceof Error?_0x48fe28[_0x537513(0x2a9)]:String(_0x48fe28)}));}return;}if(_0xaf266c===_0x173281(0x234)+'ate-bot-to'+_0x173281(0x26d)&&_0x424589[_0x537513(0x11a)]===_0x537513(0xf4)){try{const {token:_0x9f6c7e}=JSON[_0x537513(0x285)](_0x341686);if(!_0x9f6c7e||!_0x9f6c7e[_0x173281(0x105)](':')){_0xd11ab9[_0x537513(0x9b)](JSON[_0x537513(0x274)]({'ok':![],'error':'Invalid\x20to'+'ken\x20format'}));return;}const _0xf2f784=await fetch(_0x173281(0x10b)+_0x173281(0xd2)+_0x173281(0x276)+_0x9f6c7e+'/getMe'),_0x5ce8f5=await _0xf2f784[_0x537513(0x203)]();_0x5ce8f5['ok']?_0xd11ab9[_0x537513(0x9b)](JSON[_0x173281(0x274)]({'ok':!![],'bot':{'username':_0x5ce8f5[_0x537513(0x2b0)]['username'],'firstName':_0x5ce8f5[_0x537513(0x2b0)][_0x173281(0x90)],'id':_0x5ce8f5[_0x173281(0x2b0)]['id']}})):_0xd11ab9[_0x537513(0x9b)](JSON['stringify']({'ok':![],'error':_0x5ce8f5[_0x537513(0xbe)+'n']||_0x173281(0x2d5)+_0x537513(0x26d)}));}catch(_0x2ebb0f){_0xd11ab9[_0x173281(0x9b)](JSON[_0x537513(0x274)]({'ok':![],'error':_0x2ebb0f instanceof Error?_0x2ebb0f[_0x173281(0x2a9)]:String(_0x2ebb0f)}));}return;}if(_0xaf266c===_0x537513(0xc6)+'s'){let _0x1d1273={'name':_0x537513(0x89)+_0x173281(0x1fb),'model':_0x537513(0x25a),'status':'unconfigur'+'ed'};try{const _0x201520=getRegistry(),_0x493c57=_0x201520[_0x537513(0x19c)]()['getInfo']();_0x1d1273={'name':_0x493c57[_0x537513(0x218)],'model':_0x493c57[_0x173281(0xed)],'status':_0x493c57['status']};}catch{}const _0x944a76=getMemoryStats(),_0x402153=getIndexStats(),_0x28d3f0=getLoadedPlugins(),_0x435a06=getMCPStatus(),_0x15523e=listProfiles(),_0x4d2d3b=listCustomTools(),{getAllSessions:_0x1f1567}=await import(_0x173281(0x19a)+_0x537513(0x10e)+'js'),_0x3acf92=_0x1f1567();let _0x23e7ab=-0x2*0x136+0x10d0+-0xe64,_0x3fe8f4=-0x63*0x17+0x4de+-0x407*-0x1,_0x40d681=0x1c43+0x23*0x1+-0x1c66;for(const _0x236d54 of _0x3acf92['values']()){_0x23e7ab+=_0x236d54[_0x173281(0x168)+_0x173281(0x21d)]||-0x25b5+0x7*-0x35b+-0x1*-0x3d32,_0x3fe8f4+=_0x236d54[_0x173281(0x196)+_0x537513(0x1fa)]||-0x27*0xb+0x1847+-0x169a,_0x40d681+=_0x236d54['totalCost']||0x590+0x1f7b*0x1+-0x147*0x1d;}const {config:_0x30dca2}=await import(_0x173281(0x82)+'js');_0xd11ab9[_0x537513(0x9b)](JSON[_0x537513(0x274)]({'bot':{'version':BOT_VERSION,'uptime':process[_0x537513(0x1cc)]()},'model':_0x1d1273,'memory':{..._0x944a76,'vectors':_0x402153['entries'],'indexSize':_0x402153[_0x173281(0x18b)]},'plugins':_0x28d3f0[_0x537513(0x20f)],'mcp':_0x435a06[_0x537513(0x20f)],'users':_0x15523e[_0x173281(0x20f)],'tools':_0x4d2d3b[_0x537513(0x20f)],'tokens':{'totalInput':_0x23e7ab,'totalOutput':_0x3fe8f4,'total':_0x23e7ab+_0x3fe8f4,'totalCost':_0x40d681},'setup':{'telegram':!!_0x30dca2[_0x173281(0x1c7)],'provider':_0x1d1273[_0x173281(0x106)]!=='unconfigur'+'ed'}}));return;}if(_0xaf266c===_0x173281(0x229)+'s'){const _0x1b7adf=getRegistry();_0x1b7adf[_0x537513(0x128)]()['then'](_0x11a80b=>{const _0x47d2e9=_0x537513,_0x1387cc=_0x537513;_0xd11ab9[_0x47d2e9(0x9b)](JSON[_0x1387cc(0x274)]({'models':_0x11a80b,'active':_0x1b7adf[_0x47d2e9(0x1ad)+'ey']()}));});return;}if(_0xaf266c===_0x537513(0x229)+_0x537513(0xc9)&&_0x424589[_0x173281(0x11a)]===_0x537513(0xf4)){try{const {key:_0x5e1861}=JSON[_0x537513(0x285)](_0x341686),_0x1f5069=getRegistry(),_0x1829c0=_0x1f5069[_0x537513(0x1a0)](_0x5e1861);_0xd11ab9[_0x537513(0x9b)](JSON[_0x173281(0x274)]({'ok':_0x1829c0,'active':_0x1f5069[_0x173281(0x1ad)+'ey']()}));}catch{_0xd11ab9[_0x537513(0x1d0)]=0x1*0x1a10+0x27*-0x2b+-0x11f3,_0xd11ab9[_0x537513(0x9b)](JSON['stringify']({'error':_0x537513(0xe5)+'quest'}));}return;}if(_0xaf266c===_0x173281(0x20c)+'ack'&&_0x424589[_0x537513(0x11a)]===_0x173281(0x190)){try{const {getFallbackOrder:_0x2010dc}=await import(_0x537513(0x19a)+_0x173281(0x266)+_0x537513(0x165)),{getHealthStatus:_0x4f6b6c,isFailedOver:_0x1612c0}=await import('../service'+_0x537513(0xc5)+_0x173281(0x162)),_0x160861=getRegistry(),_0x414ca7=await _0x160861['listAll']();_0xd11ab9[_0x173281(0x9b)](JSON[_0x173281(0x274)]({'order':_0x2010dc(),'health':_0x4f6b6c(),'failedOver':_0x1612c0(),'activeProvider':_0x160861[_0x537513(0x1ad)+'ey'](),'availableProviders':_0x414ca7[_0x537513(0xee)](_0x55ae0b=>({'key':_0x55ae0b[_0x173281(0xe1)],'name':_0x55ae0b[_0x173281(0x218)],'status':_0x55ae0b[_0x173281(0x106)]}))}));}catch(_0x2a8d47){_0xd11ab9['end'](JSON['stringify']({'error':String(_0x2a8d47)}));}return;}if(_0xaf266c===_0x537513(0x20c)+_0x173281(0x27f)&&_0x424589[_0x537513(0x11a)]==='POST'){try{const {primary:_0x15180b,fallbacks:_0x1c48ab}=JSON[_0x173281(0x285)](_0x341686),{setFallbackOrder:_0x3a0b7f}=await import(_0x173281(0x19a)+_0x173281(0x266)+_0x537513(0x165)),_0xa6a315=_0x3a0b7f(_0x15180b,_0x1c48ab,'webui');_0xd11ab9[_0x537513(0x9b)](JSON[_0x173281(0x274)]({'ok':!![],'order':_0xa6a315}));}catch(_0x48e552){_0xd11ab9[_0x173281(0x1d0)]=0x1*-0x76b+0x1ce7*-0x1+0x25e2,_0xd11ab9[_0x173281(0x9b)](JSON[_0x537513(0x274)]({'error':String(_0x48e552)}));}return;}if(_0xaf266c==='/api/fallb'+'ack/move'&&_0x424589[_0x537513(0x11a)]===_0x537513(0xf4)){try{const {key:_0x2a6804,direction:_0x119464}=JSON[_0x173281(0x285)](_0x341686),_0x5d639c=await import(_0x537513(0x19a)+'s/fallback'+'-order.js'),_0x4d3d14=_0x119464==='up'?_0x5d639c['moveUp'](_0x2a6804,'webui'):_0x5d639c[_0x173281(0xb9)](_0x2a6804,'webui');_0xd11ab9['end'](JSON[_0x173281(0x274)]({'ok':!![],'order':_0x4d3d14}));}catch(_0x53abf7){_0xd11ab9['statusCode']=0x14d2*-0x1+-0x2669+0x3ccb,_0xd11ab9[_0x173281(0x9b)](JSON[_0x173281(0x274)]({'error':String(_0x53abf7)}));}return;}if(_0xaf266c==='/api/heart'+'beat'){try{const {getHealthStatus:_0x426583,isFailedOver:_0x4aaca3}=await import('../service'+_0x173281(0xc5)+_0x537513(0x162));_0xd11ab9[_0x173281(0x9b)](JSON[_0x173281(0x274)]({'health':_0x426583(),'failedOver':_0x4aaca3()}));}catch(_0x310ad1){_0xd11ab9[_0x537513(0x9b)](JSON[_0x173281(0x274)]({'health':[],'failedOver':![]}));}return;}if(_0xaf266c===_0x537513(0x9c)+'y'){const _0x398d9e=loadLongTermMemory(),_0x2a97d2=loadDailyLog(),_0x3c44df=getMemoryStats(),_0x34b113=getIndexStats();let _0x2833a0=[];try{_0x2833a0=_0x1a4331[_0x537513(0x243)+'c'](MEMORY_DIR)[_0x537513(0x2fc)](_0xfcf857=>_0xfcf857[_0x537513(0x2a4)](_0x537513(0x107))&&!_0xfcf857[_0x173281(0x21e)]('.'))[_0x173281(0x286)]()[_0x537513(0xf9)]();}catch{}_0xd11ab9[_0x537513(0x9b)](JSON[_0x537513(0x274)]({'longTermMemory':_0x398d9e,'todayLog':_0x2a97d2,'dailyFiles':_0x2833a0,'stats':_0x3c44df,'index':{'entries':_0x34b113[_0x173281(0x2eb)],'files':_0x34b113[_0x537513(0x283)],'sizeBytes':_0x34b113[_0x537513(0x18b)]}}));return;}if(_0xaf266c['startsWith'](_0x173281(0x9c)+'y/')){const _0x3d1c07=_0xaf266c['slice'](-0x3*0x4b+-0x2423+0x10*0x251);if(_0x3d1c07['includes']('..')||!_0x3d1c07[_0x537513(0x2a4)](_0x537513(0x107))){_0xd11ab9['statusCode']=0x1a2+-0x1481+-0x146f*-0x1,_0xd11ab9[_0x537513(0x9b)](JSON[_0x537513(0x274)]({'error':_0x173281(0x278)+'le'}));return;}try{const _0x30054d=_0x1a4331[_0x173281(0x23e)+'nc'](resolve(MEMORY_DIR,_0x3d1c07),_0x537513(0x204));_0xd11ab9['end'](JSON['stringify']({'file':_0x3d1c07,'content':_0x30054d}));}catch{_0xd11ab9[_0x173281(0x1d0)]=0x253e+-0x1*-0x135f+0x3709*-0x1,_0xd11ab9[_0x537513(0x9b)](JSON[_0x537513(0x274)]({'error':'File\x20not\x20f'+'ound'}));}return;}if(_0xaf266c===_0x173281(0x9c)+'y/save'&&_0x424589[_0x173281(0x11a)]===_0x537513(0xf4)){try{const {file:_0x14ec18,content:_0x535de6}=JSON[_0x173281(0x285)](_0x341686);if(_0x14ec18===_0x173281(0x102))_0x1a4331[_0x173281(0xae)+_0x537513(0x28c)](MEMORY_FILE,_0x535de6);else{if(_0x14ec18[_0x537513(0x2a4)](_0x173281(0x107))&&!_0x14ec18['includes']('..'))_0x1a4331[_0x173281(0xae)+'ync'](resolve(MEMORY_DIR,_0x14ec18),_0x535de6);else{_0xd11ab9[_0x173281(0x1d0)]=-0x1ba0+-0xe3e*-0x1+-0x2*-0x779,_0xd11ab9[_0x537513(0x9b)](JSON[_0x173281(0x274)]({'error':'Invalid\x20fi'+'le'}));return;}}_0xd11ab9[_0x537513(0x9b)](JSON[_0x537513(0x274)]({'ok':!![]}));}catch{_0xd11ab9[_0x173281(0x1d0)]=-0x15ce+-0x155c+0x2cba,_0xd11ab9['end'](JSON['stringify']({'error':_0x173281(0xe5)+_0x173281(0xd1)}));}return;}if(_0xaf266c===_0x537513(0x1b2)+'ns'){_0xd11ab9[_0x173281(0x9b)](JSON[_0x173281(0x274)]({'plugins':getLoadedPlugins()}));return;}if(_0xaf266c==='/api/works'+_0x173281(0xf7)){try{const {listWorkspaces:_0x4c4024,getDefaultWorkspace:_0x43be01}=await import('../service'+_0x537513(0x281)+_0x537513(0x1b1)),{getCostByWorkspace:_0x5e792a}=await import(_0x173281(0x19a)+_0x537513(0x10e)+'js'),_0x5e526a=_0x5e792a(),_0x5ee900=_0x4c4024(),_0x135749=[_0x43be01(),..._0x5ee900],_0x1db293=_0x135749[_0x173281(0xee)](_0x4b3fa4=>({'name':_0x4b3fa4[_0x537513(0x218)],'purpose':_0x4b3fa4['purpose'],'emoji':_0x4b3fa4[_0x537513(0x1b4)]??null,'color':_0x4b3fa4[_0x173281(0x173)]??null,'cwd':_0x4b3fa4[_0x537513(0x1c9)],'channels':_0x4b3fa4['channels'],'stats':_0x5e526a[_0x4b3fa4['name']]??{'totalCost':0x0,'sessionCount':0x0,'messageCount':0x0,'toolUseCount':0x0}}));_0xd11ab9['end'](JSON[_0x173281(0x274)]({'workspaces':_0x1db293}));}catch(_0x341a1b){_0xd11ab9[_0x173281(0x1d0)]=0x3d7*-0x7+0x24f*0xf+-0x1c*0x35,_0xd11ab9[_0x173281(0x9b)](JSON[_0x537513(0x274)]({'error':_0x341a1b instanceof Error?_0x341a1b[_0x173281(0x2a9)]:String(_0x341a1b)}));}return;}if(_0xaf266c===_0x537513(0xa1)&&_0x424589[_0x537513(0x11a)]===_0x173281(0x190)){const {getAllSessions:_0x2cf960}=await import(_0x173281(0x19a)+_0x537513(0x10e)+'js'),_0x43c85b=listProfiles(),_0x38a2ae=_0x2cf960(),_0x1280ac=new Map(Array[_0x173281(0x187)](_0x38a2ae['entries']())[_0x173281(0xee)](([_0x5465c8,_0x48d651])=>[Number(_0x5465c8),_0x48d651])),_0x5ba1b6=_0x43c85b['map'](_0x5fce9e=>{const _0x3be677=_0x537513,_0x39da92=_0x537513,_0x193c99=_0x1280ac['get'](_0x5fce9e[_0x3be677(0x241)]);return{..._0x5fce9e,'session':_0x193c99?{'isProcessing':_0x193c99['isProcessi'+'ng'],'totalCost':_0x193c99['totalCost'],'historyLength':_0x193c99[_0x3be677(0x1a1)]['length'],'effort':_0x193c99[_0x39da92(0x21c)],'voiceReply':_0x193c99['voiceReply'],'startedAt':_0x193c99['startedAt'],'messageCount':_0x193c99['messageCou'+'nt'],'toolUseCount':_0x193c99[_0x3be677(0x16b)+'nt'],'workingDir':_0x193c99[_0x3be677(0x2a6)],'hasActiveQuery':!!_0x193c99['abortContr'+_0x39da92(0x2d4)],'queuedMessages':_0x193c99[_0x39da92(0x2e6)+'ue'][_0x39da92(0x20f)]}:null};});_0xd11ab9[_0x537513(0x9b)](JSON[_0x173281(0x274)]({'users':_0x5ba1b6}));return;}if(_0xaf266c[_0x173281(0x21e)]('/api/users'+'/')&&_0x424589[_0x537513(0x11a)]===_0x173281(0x29a)){if(isExposedWithoutPassword()){_0xd11ab9[_0x173281(0x1d0)]=0x162c+-0x1870+0x3d7,_0xd11ab9[_0x173281(0x9b)](JSON[_0x173281(0x274)]({'error':_0x537513(0x25f)+'eb\x20exposed'+_0x173281(0x2d3)+_0x173281(0x18d)+'D'}));return;}const _0xd56ff8=parseInt(_0xaf266c[_0x537513(0x2f6)]('/')['pop']()||'0');if(!_0xd56ff8){_0xd11ab9[_0x173281(0x1d0)]=-0x1*0x16bb+0x2ef*-0x1+0x1b3a,_0xd11ab9['end'](JSON[_0x537513(0x274)]({'error':_0x173281(0xa8)+_0x537513(0x123)}));return;}const {deleteUser:_0x16122f}=await import(_0x173281(0x19a)+_0x537513(0x228)),_0x5f4fc9=_0x16122f(_0xd56ff8);_0xd11ab9[_0x173281(0x9b)](JSON['stringify']({'ok':!![],..._0x5f4fc9}));return;}if(_0xaf266c===_0x173281(0x2a0)){const _0x360210=getCustomTools();_0xd11ab9[_0x537513(0x9b)](JSON[_0x537513(0x274)]({'tools':_0x360210}));return;}if(_0xaf266c===_0x173281(0x2a0)+_0x173281(0x23f)&&_0x424589['method']===_0x537513(0xf4)){try{const {name:_0x486e02,params:_0x2075b0}=JSON[_0x173281(0x285)](_0x341686);if(!_0x486e02){_0xd11ab9['statusCode']=0x3*-0x30c+0xa1a+0x9a,_0xd11ab9[_0x537513(0x9b)](JSON['stringify']({'error':_0x537513(0x155)+'me'}));return;}const _0x7ab5cc=await executeCustomTool(_0x486e02,_0x2075b0||{});_0xd11ab9[_0x173281(0x9b)](JSON[_0x537513(0x274)]({'ok':!![],'output':_0x7ab5cc}));}catch(_0x5a027a){const _0x3a6db0=_0x5a027a instanceof Error?_0x5a027a[_0x173281(0x2a9)]:String(_0x5a027a);_0xd11ab9[_0x537513(0x9b)](JSON['stringify']({'error':_0x3a6db0}));}return;}if(_0xaf266c===_0x173281(0x189)){const {getMCPStatus:_0x53f0b8,getMCPTools:_0x2e064,hasMCPConfig:_0x4b44f8}=await import(_0x537513(0x19a)+'s/mcp.js'),_0x4948a0=_0x53f0b8(),_0x19ce2d=_0x2e064(),_0x3f99ad=MCP_CONFIG;let _0x43c9f2={'servers':{}};try{_0x43c9f2=JSON[_0x173281(0x285)](_0x1a4331[_0x537513(0x23e)+'nc'](_0x3f99ad,_0x173281(0x204)));}catch{}_0xd11ab9['end'](JSON[_0x537513(0x274)]({'servers':_0x4948a0,'tools':_0x19ce2d,'config':_0x43c9f2,'hasConfig':_0x4b44f8()}));return;}if(_0xaf266c===_0x173281(0x151)+'dd'&&_0x424589[_0x173281(0x11a)]===_0x173281(0xf4)){try{const {name:_0x4d725a,command:_0x11fea6,args:_0x30082e,url:_0xebdd5c,env:_0x531527,headers:_0x7c0ba7}=JSON[_0x173281(0x285)](_0x341686);if(!_0x4d725a){_0xd11ab9[_0x537513(0x1d0)]=-0x19*0x155+0x2*-0x3c6+0x7*0x60f,_0xd11ab9[_0x537513(0x9b)](JSON[_0x173281(0x274)]({'error':_0x173281(0x9f)+_0x537513(0x132)}));return;}const _0x1e9869=MCP_CONFIG;let _0x10ccaa={'servers':{}};try{_0x10ccaa=JSON[_0x537513(0x285)](_0x1a4331[_0x537513(0x23e)+'nc'](_0x1e9869,_0x537513(0x204)));}catch{}const _0x4b49d4={};if(_0x11fea6){_0x4b49d4[_0x537513(0x220)]=_0x11fea6,_0x4b49d4[_0x537513(0x1db)]=_0x30082e||[];if(_0x531527)_0x4b49d4['env']=_0x531527;}else{if(_0xebdd5c){_0x4b49d4['url']=_0xebdd5c;if(_0x7c0ba7)_0x4b49d4[_0x173281(0x86)]=_0x7c0ba7;}else{_0xd11ab9['statusCode']=-0x6*-0x5cb+0x1535*-0x1+-0xbfd,_0xd11ab9['end'](JSON[_0x537513(0x274)]({'error':_0x173281(0x15b)+_0x537513(0x150)+_0x173281(0x132)}));return;}}_0x10ccaa[_0x537513(0xc3)][_0x4d725a]=_0x4b49d4,_0x1a4331['writeFileS'+'ync'](_0x1e9869,JSON[_0x173281(0x274)](_0x10ccaa,null,-0x15*-0x35+-0x45b+-0x1*-0x4)),_0xd11ab9[_0x173281(0x9b)](JSON[_0x173281(0x274)]({'ok':!![],'note':'Restart\x20ne'+_0x173281(0x13d)+_0x173281(0x1d2)}));}catch(_0x333b02){_0xd11ab9[_0x537513(0x1d0)]=0x1*-0x2125+-0x1227+-0x11*-0x31c,_0xd11ab9[_0x173281(0x9b)](JSON[_0x537513(0x274)]({'error':_0x333b02 instanceof Error?_0x333b02[_0x537513(0x2a9)]:String(_0x333b02)}));}return;}if(_0xaf266c===_0x537513(0x20a)+_0x173281(0x198)&&_0x424589['method']===_0x537513(0xf4)){try{const {name:_0x47512d}=JSON[_0x537513(0x285)](_0x341686),_0x59a380=MCP_CONFIG;let _0x79cd29={'servers':{}};try{_0x79cd29=JSON['parse'](_0x1a4331[_0x537513(0x23e)+'nc'](_0x59a380,_0x537513(0x204)));}catch{}delete _0x79cd29[_0x173281(0xc3)][_0x47512d],_0x1a4331[_0x173281(0xae)+_0x537513(0x28c)](_0x59a380,JSON[_0x537513(0x274)](_0x79cd29,null,-0x9*0x2a1+0x1145+-0x2*-0x333)),_0xd11ab9[_0x173281(0x9b)](JSON[_0x173281(0x274)]({'ok':!![]}));}catch(_0x4de911){_0xd11ab9[_0x537513(0x1d0)]=-0x2149*-0x1+-0x1d*0x71+-0x12ec,_0xd11ab9['end'](JSON[_0x537513(0x274)]({'error':_0x4de911 instanceof Error?_0x4de911['message']:String(_0x4de911)}));}return;}if(_0xaf266c===_0x173281(0x22c)+'iscover'){const _0x5d99a9=[],{execSync:_0x31a393}=await import(_0x173281(0x27a)+_0x537513(0x247)),_0x1bb48b=[{'pkg':_0x173281(0x208)+_0x537513(0x18f)+_0x537513(0x1bb)+_0x537513(0x271),'name':_0x537513(0x201),'args':[_0x537513(0x27c)]},{'pkg':_0x537513(0x208)+_0x537513(0x18f)+_0x537513(0x194)+_0x173281(0x25e)+'h','name':_0x537513(0x258)+'ch','args':[]},{'pkg':_0x173281(0x208)+'extprotoco'+'l/server-g'+_0x173281(0x17d),'name':_0x173281(0x2be),'args':[]},{'pkg':'@modelcont'+_0x537513(0x18f)+'l/server-p'+'ostgres','name':_0x537513(0x1d1),'args':[]},{'pkg':_0x537513(0x208)+_0x173281(0x18f)+_0x537513(0x1c8)+'qlite','name':'sqlite','args':[]},{'pkg':_0x537513(0x208)+_0x173281(0x18f)+_0x537513(0x1c8)+'lack','name':'slack','args':[]},{'pkg':_0x537513(0x208)+_0x537513(0x18f)+'l/server-m'+'emory','name':_0x173281(0x26f),'args':[]},{'pkg':_0x173281(0x208)+_0x173281(0x18f)+_0x537513(0x280)+_0x537513(0x24c),'name':_0x173281(0x20e),'args':[]},{'pkg':_0x173281(0x208)+_0x173281(0x18f)+'l/server-f'+_0x173281(0x265),'name':'web-fetch','args':[]},{'pkg':_0x537513(0xeb)+_0x173281(0x191)+_0x173281(0xd3)+_0x173281(0x2f5)+'g','name':_0x173281(0x232)+_0x537513(0x169),'args':[]}];for(const _0x2ad9ef of _0x1bb48b){try{_0x31a393(_0x173281(0x1ed)+_0x2ad9ef[_0x537513(0x260)]+_0x537513(0x101),{'timeout':0x1388,'stdio':'pipe','env':{...process[_0x173281(0x302)],'PATH':process[_0x173281(0x302)]['PATH']+(_0x173281(0x1eb)+_0x537513(0x238)+'usr/local/'+'bin')}}),_0x5d99a9[_0x173281(0x13a)]({'name':_0x2ad9ef[_0x173281(0x218)],'command':_0x173281(0x136),'args':['-y',_0x2ad9ef[_0x537513(0x260)],..._0x2ad9ef[_0x173281(0x1db)]],'source':_0x173281(0x2bd)});}catch{try{_0x31a393('npm\x20list\x20-'+'g\x20'+_0x2ad9ef[_0x537513(0x260)]+_0x173281(0x178),{'timeout':0x1388,'stdio':_0x537513(0x2d9)}),_0x5d99a9[_0x173281(0x13a)]({'name':_0x2ad9ef[_0x173281(0x218)],'command':'npx','args':['-y',_0x2ad9ef['pkg'],..._0x2ad9ef[_0x537513(0x1db)]],'source':_0x537513(0x141)});}catch{}}}const _0x5f40c0=process['env'][_0x173281(0x2f3)]||process[_0x537513(0x302)][_0x173281(0x2e7)+'E']||'',_0x381453=[resolve(_0x5f40c0,_0x173281(0x2c2)+_0x173281(0x225)+_0x537513(0x290)+'config.jso'+'n'),resolve(_0x5f40c0,'Library/Ap'+_0x173281(0x81)+'Support/Cl'+_0x173281(0x225)+_0x173281(0x290)+'config.jso'+'n'),resolve(_0x5f40c0,_0x537513(0xff)+_0x537513(0x19d)+_0x537513(0xaa)+'desktop_co'+_0x173281(0x121))];for(const _0x2faacf of _0x381453){try{const _0x6a0780=JSON[_0x173281(0x285)](_0x1a4331['readFileSy'+'nc'](_0x2faacf,_0x537513(0x204)));if(_0x6a0780[_0x537513(0x119)])for(const [_0x45e985,_0x152b38]of Object['entries'](_0x6a0780[_0x537513(0x119)])){_0x152b38[_0x537513(0x220)]&&_0x5d99a9[_0x173281(0x13a)]({'name':_0x537513(0xbb)+_0x45e985,'command':_0x152b38[_0x173281(0x220)],'args':_0x152b38[_0x537513(0x1db)]||[],'source':_0x173281(0x1bd)+_0x537513(0xf5)});}}catch{}}_0xd11ab9[_0x173281(0x9b)](JSON[_0x537513(0x274)]({'discovered':_0x5d99a9}));return;}if(_0xaf266c?.[_0x537513(0x1f2)](/^\/api\/skills\/detail\//)&&_0x424589[_0x537513(0x11a)]===_0x537513(0x190)){const _0x58a813=_0xaf266c[_0x537513(0x2f6)]('/')[_0x173281(0xd0)](),{getSkills:_0x39289a}=await import(_0x537513(0x19a)+_0x173281(0x2ce)+'s'),_0x5dbc93=_0x39289a()['find'](_0x5eb8aa=>_0x5eb8aa['id']===_0x58a813);_0x5dbc93?_0xd11ab9[_0x537513(0x9b)](JSON[_0x173281(0x274)]({'ok':!![],'skill':_0x5dbc93})):(_0xd11ab9[_0x537513(0x1d0)]=0xe4d+0x1ccc+-0x2985,_0xd11ab9[_0x173281(0x9b)](JSON[_0x537513(0x274)]({'error':_0x173281(0x1a2)+_0x537513(0x1c2)})));return;}if(_0xaf266c===_0x537513(0x2d0)+_0x173281(0x277)&&_0x424589[_0x173281(0x11a)]===_0x537513(0xf4)){try{const {id:_0x359ea4,name:_0xdbd93c,description:_0x6d5d07,triggers:_0x4315d0,category:_0x53f6d1,content:_0x3b396a,priority:_0x384387}=JSON['parse'](_0x341686);if(!_0x359ea4||!_0xdbd93c){_0xd11ab9[_0x173281(0x1d0)]=0x1*0x13ed+-0x61f*0x1+0x61f*-0x2,_0xd11ab9['end'](JSON[_0x537513(0x274)]({'error':'id\x20and\x20nam'+_0x537513(0x1b5)}));return;}if(typeof _0x359ea4!==_0x173281(0x1bf)||_0x359ea4[_0x537513(0x105)]('..')||_0x359ea4[_0x173281(0x105)]('/')||_0x359ea4[_0x173281(0x105)]('\x5c')||_0xfff6a9[_0x173281(0x300)](_0x359ea4)){_0xd11ab9['statusCode']=-0x1*0x1cd9+-0x836+0x269f,_0xd11ab9['end'](JSON[_0x173281(0x274)]({'error':_0x173281(0x125)+_0x173281(0x270)}));return;}const _0x34db3d=SKILLS_DIR,_0x1cb89d=resolve(_0x34db3d,_0x359ea4);if(!_0x1cb89d['startsWith'](_0x34db3d)){_0xd11ab9[_0x537513(0x1d0)]=-0x58*-0xc+0x13*-0x1ce+-0x1fba*-0x1,_0xd11ab9[_0x173281(0x9b)](JSON[_0x173281(0x274)]({'error':_0x537513(0x125)+_0x173281(0x270)}));return;}if(!_0x1a4331[_0x537513(0x2c0)](_0x1cb89d))_0x1a4331[_0x173281(0x273)](_0x1cb89d,{'recursive':!![]});const _0x549167=[_0x173281(0xaf),_0x537513(0x1fe)+_0xdbd93c,_0x6d5d07?_0x537513(0xbe)+_0x537513(0x11f)+_0x6d5d07:'',_0x4315d0?_0x173281(0x154)+(Array[_0x173281(0x92)](_0x4315d0)?_0x4315d0[_0x537513(0x2a7)](',\x20'):_0x4315d0):'','priority:\x20'+(_0x384387||0x1cf7+-0x5*0x635+0x215),_0x173281(0x251)+(_0x53f6d1||'custom'),_0x537513(0xaf)][_0x537513(0x2fc)](Boolean)[_0x537513(0x2a7)]('\x0a');_0x1a4331['writeFileS'+_0x537513(0x28c)](resolve(_0x1cb89d,_0x537513(0x17f)),_0x549167+'\x0a\x0a'+(_0x3b396a||''));const {loadSkills:_0x52b45a}=await import('../service'+_0x537513(0x2ce)+'s');_0x52b45a(),_0xd11ab9[_0x537513(0x9b)](JSON[_0x173281(0x274)]({'ok':!![]}));}catch(_0x393824){_0xd11ab9['statusCode']=0x9f6+-0x9b5+0x14f,_0xd11ab9['end'](JSON[_0x537513(0x274)]({'error':_0x393824 instanceof Error?_0x393824[_0x173281(0x2a9)]:String(_0x393824)}));}return;}if(_0xaf266c===_0x173281(0x2d0)+_0x173281(0x1a5)&&_0x424589[_0x173281(0x11a)]==='POST'){try{const {id:_0x3c35a5,content:_0x10de95}=JSON[_0x537513(0x285)](_0x341686);if(typeof _0x3c35a5!==_0x173281(0x1bf)||_0x3c35a5[_0x173281(0x105)]('..')||_0x3c35a5[_0x537513(0x105)]('/')||_0x3c35a5[_0x173281(0x105)]('\x5c')||_0xfff6a9[_0x173281(0x300)](_0x3c35a5)){_0xd11ab9[_0x537513(0x1d0)]=-0x1057+0x581*-0x1+0x5da*0x4,_0xd11ab9[_0x537513(0x9b)](JSON[_0x173281(0x274)]({'error':'Invalid\x20sk'+_0x173281(0x270)}));return;}if(!resolve(SKILLS_DIR,_0x3c35a5)[_0x173281(0x21e)](SKILLS_DIR)){_0xd11ab9[_0x537513(0x1d0)]=0x53*0x3+-0x3*-0x55d+0x4*-0x3e0,_0xd11ab9[_0x173281(0x9b)](JSON[_0x173281(0x274)]({'error':'Invalid\x20sk'+_0x537513(0x270)}));return;}const _0x525057=resolve(SKILLS_DIR,_0x3c35a5,_0x537513(0x17f));if(!_0x1a4331[_0x537513(0x2c0)](_0x525057)){const _0x39710d=resolve(SKILLS_DIR,_0x3c35a5+_0x173281(0x107));if(_0x1a4331[_0x173281(0x2c0)](_0x39710d))_0x1a4331['writeFileS'+'ync'](_0x39710d,_0x10de95);else{_0xd11ab9[_0x537513(0x1d0)]=-0x1afa+-0x1e19+-0x8f*-0x69,_0xd11ab9['end'](JSON[_0x173281(0x274)]({'error':_0x173281(0x1a2)+'found'}));return;}}else _0x1a4331[_0x173281(0xae)+_0x537513(0x28c)](_0x525057,_0x10de95);const {loadSkills:_0x4c85ae}=await import(_0x173281(0x19a)+_0x537513(0x2ce)+'s');_0x4c85ae(),_0xd11ab9['end'](JSON[_0x537513(0x274)]({'ok':!![]}));}catch(_0x35a643){_0xd11ab9[_0x173281(0x1d0)]=0x12*-0x1c0+0x1e45+0x2cb,_0xd11ab9[_0x537513(0x9b)](JSON[_0x173281(0x274)]({'error':_0x35a643 instanceof Error?_0x35a643[_0x173281(0x2a9)]:String(_0x35a643)}));}return;}if(_0xaf266c===_0x173281(0x2d0)+_0x173281(0xb5)&&_0x424589[_0x537513(0x11a)]===_0x173281(0xf4)){try{const {id:_0x35e3e8}=JSON[_0x173281(0x285)](_0x341686);if(typeof _0x35e3e8!==_0x537513(0x1bf)||_0x35e3e8[_0x173281(0x105)]('..')||_0x35e3e8[_0x173281(0x105)]('/')||_0x35e3e8[_0x173281(0x105)]('\x5c')||_0xfff6a9[_0x173281(0x300)](_0x35e3e8)){_0xd11ab9['statusCode']=0x133*-0x2+-0x261d*0x1+0x2a13,_0xd11ab9['end'](JSON[_0x173281(0x274)]({'error':'Invalid\x20sk'+_0x537513(0x270)}));return;}const _0x47def4=resolve(SKILLS_DIR,_0x35e3e8),_0x22c317=resolve(SKILLS_DIR,_0x35e3e8+_0x537513(0x107));if(_0x1a4331[_0x173281(0x2c0)](_0x47def4))_0x1a4331[_0x173281(0xd8)](_0x47def4,{'recursive':!![]});else{if(_0x1a4331[_0x173281(0x2c0)](_0x22c317))_0x1a4331[_0x173281(0x17c)](_0x22c317);else{_0xd11ab9['statusCode']=0x2*0x7aa+0x12f8*-0x1+0x538*0x1,_0xd11ab9['end'](JSON['stringify']({'error':_0x173281(0x1a2)+_0x537513(0x1c2)}));return;}}const {loadSkills:_0x5d6d2a}=await import('../service'+_0x537513(0x2ce)+'s');_0x5d6d2a(),_0xd11ab9['end'](JSON[_0x173281(0x274)]({'ok':!![]}));}catch(_0xb79ee5){_0xd11ab9[_0x173281(0x1d0)]=0x1e6e+-0x2*-0x72e+-0x2b3a,_0xd11ab9['end'](JSON[_0x173281(0x274)]({'error':_0xb79ee5 instanceof Error?_0xb79ee5[_0x173281(0x2a9)]:String(_0xb79ee5)}));}return;}if(_0xaf266c===_0x173281(0x2f7)+'g'){_0xd11ab9[_0x173281(0x9b)](JSON[_0x173281(0x274)]({'providers':config[_0x173281(0x176)+_0x173281(0x126)],'primaryProvider':config[_0x173281(0x2b3)+_0x173281(0x88)],'allowedUsers':config[_0x537513(0x1e5)+'rs'],'hasKeys':{'groq':!!config[_0x537513(0x244)]['groq'],'openai':!!config[_0x173281(0x244)][_0x173281(0x29d)],'google':!!config[_0x173281(0x244)][_0x537513(0x2c4)],'nvidia':!!config['apiKeys'][_0x537513(0x2d2)],'openrouter':!!config[_0x537513(0x244)][_0x173281(0x1e4)]}}));return;}if(_0xaf266c==='/api/sessi'+_0x173281(0x28e)){const _0x500710=getAllSessions(),_0x87d5cf=listProfiles(),_0x22e4b8=Array['from'](_0x500710['entries']())[_0x173281(0xee)](([_0x97c4c3,_0xc3a356])=>{const _0x163e61=_0x173281,_0x3c4ef4=_0x537513,_0x334257=Number(_0x97c4c3[_0x163e61(0x2f6)](':')['pop']()),_0x20ce85=_0x87d5cf[_0x163e61(0x296)](_0xd07db9=>_0xd07db9[_0x3c4ef4(0x241)]===_0x334257);return{'userId':_0x97c4c3,'name':_0x20ce85?.['name']||'User\x20'+_0x97c4c3,'username':_0x20ce85?.[_0x163e61(0x12c)],'messageCount':_0xc3a356[_0x3c4ef4(0x1ba)+'nt'],'toolUseCount':_0xc3a356[_0x163e61(0x16b)+'nt'],'totalCost':_0xc3a356['totalCost'],'totalInputTokens':_0xc3a356['totalInput'+_0x3c4ef4(0x21d)]||-0x1cab+-0x7*-0x452+-0x193,'totalOutputTokens':_0xc3a356[_0x163e61(0x196)+_0x3c4ef4(0x1fa)]||0x19*-0x18+-0x12dd+0x1535,'effort':_0xc3a356[_0x3c4ef4(0x21c)],'startedAt':_0xc3a356[_0x163e61(0x2de)],'lastActivity':_0xc3a356['lastActivi'+'ty'],'historyLength':_0xc3a356[_0x163e61(0x1a1)][_0x163e61(0x20f)],'isProcessing':_0xc3a356[_0x3c4ef4(0x298)+'ng'],'provider':Object[_0x163e61(0x248)](_0xc3a356[_0x163e61(0xef)+_0x163e61(0xca)])['join'](',\x20')||_0x3c4ef4(0x25a)};});_0xd11ab9[_0x537513(0x9b)](JSON[_0x537513(0x274)]({'sessions':_0x22e4b8}));return;}if(_0xaf266c[_0x173281(0x1f2)](/^\/api\/sessions\/\d+\/history$/)){const _0x194274=parseInt(_0xaf266c[_0x173281(0x2f6)]('/')[-0xd5d*0x1+0x1dc3*-0x1+-0x4cb*-0x9]),_0x5765e1=getSession(_0x194274);_0xd11ab9[_0x173281(0x9b)](JSON['stringify']({'userId':_0x194274,'history':_0x5765e1[_0x173281(0x1a1)][_0x173281(0xee)](_0x5ed05c=>({'role':_0x5ed05c[_0x173281(0x2a2)],'content':_0x5ed05c[_0x537513(0x124)]['slice'](0x29a+-0x35*-0x7c+0xe23*-0x2,0x2482+-0x455*-0x1+-0x2107*0x1)}))}));return;}if(_0xaf266c==='/api/files'){const _0x3b8a8c=new URLSearchParams((_0x424589[_0x537513(0x29e)]||'')[_0x537513(0x2f6)]('?')[0x134d+-0x464*-0x2+0x3*-0x95c]||''),_0x1d3a88=_0x3b8a8c[_0x173281(0x2c8)](_0x537513(0x2ad))||'',_0x16b2e3=resolve(BOT_ROOT,_0x1d3a88||'.');if(!_0x16b2e3[_0x173281(0x21e)](BOT_ROOT)){_0xd11ab9[_0x173281(0x1d0)]=0x2*0x949+0x1f25*0x1+-0x13c*0x27,_0xd11ab9['end'](JSON['stringify']({'error':_0x173281(0x193)+_0x537513(0x2cc)}));return;}try{const _0x58e09f=_0x1a4331[_0x173281(0x249)](_0x16b2e3);if(_0x58e09f[_0x173281(0xb2)+'y']()){const _0x2979b3=_0x1a4331[_0x173281(0x243)+'c'](_0x16b2e3,{'withFileTypes':!![]})['filter'](_0x1efa69=>!_0x1efa69[_0x537513(0x218)][_0x537513(0x21e)]('.')&&_0x1efa69[_0x173281(0x218)]!==_0x537513(0x29b)+'es')['map'](_0x2daaac=>({'name':_0x2daaac['name'],'type':_0x2daaac['isDirector'+'y']()?_0x173281(0x288):'file','size':_0x2daaac['isFile']()?_0x1a4331[_0x537513(0x249)](resolve(_0x16b2e3,_0x2daaac['name']))[_0x537513(0x1b8)]:0x2319+0x22df+-0x4*0x117e,'modified':_0x1a4331[_0x537513(0x249)](resolve(_0x16b2e3,_0x2daaac[_0x173281(0x218)]))['mtimeMs']}))[_0x537513(0x286)]((_0x587cc7,_0xc5989a)=>{const _0x57cce1=_0x537513,_0x14f789=_0x537513;if(_0x587cc7['type']!==_0xc5989a['type'])return _0x587cc7[_0x57cce1(0x158)]===_0x14f789(0x288)?-(0x7*0x6b+-0x13*0x1c9+-0x17*-0x159):-0xce8+-0xc20+-0x1d*-0xdd;return _0x587cc7[_0x57cce1(0x218)][_0x14f789(0x8a)+_0x57cce1(0x2f1)](_0xc5989a[_0x57cce1(0x218)]);});_0xd11ab9['end'](JSON[_0x537513(0x274)]({'path':_0x1d3a88||'.','entries':_0x2979b3}));}else{const _0x4fca19=_0xfff6a9[_0x537513(0xab)](_0x16b2e3)[_0x537513(0x1d3)+'e'](),_0x252719=new Set([_0x537513(0x107),_0x537513(0x2d8),_0x537513(0x1d8),_0x173281(0x1dc),_0x173281(0x2d1),'.jsx','.tsx','.css',_0x173281(0x250),_0x537513(0x161),'.xml',_0x173281(0x1b3),_0x537513(0xce),_0x537513(0x1d6),_0x173281(0x2f8),_0x173281(0x295),_0x173281(0x1c1),_0x537513(0xa2),_0x537513(0xd4),_0x173281(0x282),_0x173281(0x1e9),_0x537513(0x13c),_0x173281(0x8b),_0x537513(0x2bc),_0x537513(0x14f),'.go',_0x537513(0x2cb),_0x537513(0x2b2),_0x173281(0x2e1),'.c',_0x537513(0x239),'.h',_0x173281(0x2d6),_0x537513(0x2b6),_0x537513(0xfa),_0x537513(0x18c),_0x173281(0x2ec),_0x537513(0x152),'.dockerfil'+'e',_0x537513(0x275),_0x537513(0x29f)+'utes','.editorcon'+_0x173281(0x120),_0x537513(0x15a)+'c',_0x537513(0x2db),_0x173281(0x16a),_0x537513(0x1af),_0x537513(0x212),_0x537513(0xec),_0x537513(0xb8),'.csv','.tsv',_0x537513(0xe7),_0x173281(0x112),_0x173281(0x272),_0x537513(0x1ee),'.vue',_0x173281(0x263),_0x173281(0x160)]),_0x5026e7=new Set([_0x173281(0x22e),_0x173281(0x159),_0x173281(0x1a8),_0x537513(0x2cf),_0x173281(0x192),_0x173281(0x1de)+'e',_0x173281(0x24a),'justfile',_0x173281(0x117),_0x537513(0x25d),'license','licence',_0x173281(0x14c),'changelog',_0x173281(0x1e2),'contributo'+'rs']),_0x2bfe82=_0xfff6a9[_0x173281(0xb3)](_0x16b2e3)[_0x173281(0x1d3)+'e'](),_0x3650cc=_0x5026e7[_0x537513(0x97)](_0x2bfe82),_0x2d2f18=_0x252719[_0x537513(0x97)](_0x4fca19)||_0x3650cc||!_0x4fca19&&_0x58e09f[_0x537513(0x1b8)]<-0xd282+0x9f76*-0x1+0x2f898;if(_0x58e09f[_0x173281(0x1b8)]>0x30833*-0x1+0x777f+0xa31d4)_0xd11ab9[_0x537513(0x9b)](JSON['stringify']({'path':_0x1d3a88,'content':_0x537513(0x14b)+'large:\x20'+(_0x58e09f['size']/(-0x26e+0xc9f+-0x631))['toFixed'](0x4bb*0x2+-0x634*0x6+0x1bc3)+(_0x537513(0x1f7)+_0x537513(0x230)),'size':_0x58e09f[_0x173281(0x1b8)]}));else{if(_0x2d2f18)try{const _0x5f21b1=_0x1a4331['readFileSy'+'nc'](_0x16b2e3,_0x173281(0x204)),_0x524221=[..._0x5f21b1[_0x173281(0xdb)](0x18f3+0x22f1+-0x3be4,0x1cad+-0x3*0x87+-0x35*0x70)]['filter'](_0x67a086=>_0x67a086==='\x00')['length'];_0x524221>0x883*-0x3+0x228f+-0xd*0xaa?_0xd11ab9['end'](JSON[_0x537513(0x274)]({'path':_0x1d3a88,'content':null,'size':_0x58e09f[_0x537513(0x1b8)],'binary':!![]})):_0xd11ab9[_0x537513(0x9b)](JSON[_0x173281(0x274)]({'path':_0x1d3a88,'content':_0x5f21b1,'size':_0x58e09f['size']}));}catch{_0xd11ab9['end'](JSON[_0x173281(0x274)]({'path':_0x1d3a88,'content':null,'size':_0x58e09f[_0x173281(0x1b8)],'binary':!![]}));}else _0xd11ab9[_0x173281(0x9b)](JSON[_0x537513(0x274)]({'path':_0x1d3a88,'content':null,'size':_0x58e09f[_0x537513(0x1b8)],'binary':!![]}));}}}catch{_0xd11ab9['statusCode']=0x764*-0x1+-0x2b*-0x4a+-0x376,_0xd11ab9['end'](JSON[_0x173281(0x274)]({'error':_0x537513(0x16f)}));}return;}if(_0xaf266c===_0x173281(0x164)+_0x537513(0x135)&&_0x424589[_0x173281(0x11a)]===_0x173281(0xf4)){try{const {path:_0x5e3293,content:_0x239dc4}=JSON[_0x173281(0x285)](_0x341686),_0x2c401b=resolve(BOT_ROOT,_0x5e3293);if(!_0x2c401b[_0x537513(0x21e)](BOT_ROOT)){_0xd11ab9[_0x173281(0x1d0)]=-0x11*0x1f6+0xade+0x4cf*0x5,_0xd11ab9[_0x173281(0x9b)](JSON['stringify']({'error':_0x537513(0x193)+_0x537513(0x2cc)}));return;}_0x1a4331[_0x537513(0xae)+_0x173281(0x28c)](_0x2c401b,_0x239dc4),_0xd11ab9['end'](JSON[_0x537513(0x274)]({'ok':!![]}));}catch(_0x1a9e4d){_0xd11ab9[_0x537513(0x1d0)]=0x218d+0xf22+-0x2f1f;const _0x2b3426=_0x1a9e4d instanceof Error?_0x1a9e4d[_0x173281(0x2a9)]:_0x537513(0xe5)+_0x537513(0xd1);_0xd11ab9['end'](JSON['stringify']({'error':_0x2b3426}));}return;}if(_0xaf266c===_0x173281(0x164)+_0x537513(0x1f8)&&_0x424589[_0x537513(0x11a)]==='POST'){try{const {path:_0x1bd03f}=JSON[_0x173281(0x285)](_0x341686),_0x1375a6=resolve(BOT_ROOT,_0x1bd03f);if(!_0x1375a6[_0x173281(0x21e)](BOT_ROOT)){_0xd11ab9[_0x173281(0x1d0)]=-0x2f*0x7f+-0x16*0xb5+0x2872,_0xd11ab9[_0x173281(0x9b)](JSON[_0x537513(0x274)]({'error':_0x173281(0x193)+_0x537513(0x2cc)}));return;}const _0x31ee98=['.env',_0x173281(0x14d)+'on',_0x173281(0x1be)+'son',_0x173281(0x202)+_0x537513(0x1e0)],_0x31c2e9=_0xfff6a9['basename'](_0x1375a6);if(_0x31ee98[_0x173281(0x105)](_0x31c2e9)){_0xd11ab9['statusCode']=-0x2338+-0x57b+0x2*0x1523,_0xd11ab9[_0x173281(0x9b)](JSON[_0x537513(0x274)]({'error':_0x31c2e9+(_0x537513(0x303)+_0x173281(0x2da)+_0x537513(0x21f))}));return;}if(!_0x1a4331[_0x173281(0x2c0)](_0x1375a6)){_0xd11ab9['statusCode']=0x3fe+0xc79+-0x67*0x25,_0xd11ab9[_0x537513(0x9b)](JSON[_0x537513(0x274)]({'error':'File\x20not\x20f'+_0x173281(0x10c)}));return;}const _0x87b708=_0x1a4331['statSync'](_0x1375a6);if(_0x87b708[_0x173281(0xb2)+'y']()){_0xd11ab9[_0x537513(0x1d0)]=-0x1*0x14dd+-0x4b9+0x116*0x19,_0xd11ab9[_0x173281(0x9b)](JSON[_0x173281(0x274)]({'error':_0x537513(0x17b)+_0x173281(0x2fd)+_0x173281(0x15e)}));return;}_0x1a4331[_0x173281(0x17c)](_0x1375a6),_0xd11ab9[_0x173281(0x9b)](JSON[_0x537513(0x274)]({'ok':!![]}));}catch(_0x480406){_0xd11ab9[_0x537513(0x1d0)]=0x2622+0x221b+-0x178f*0x3;const _0x8e9473=_0x480406 instanceof Error?_0x480406[_0x173281(0x2a9)]:'Invalid\x20re'+_0x537513(0xd1);_0xd11ab9[_0x173281(0x9b)](JSON[_0x537513(0x274)]({'error':_0x8e9473}));}return;}if(_0xaf266c===_0x173281(0x1cd)+_0x537513(0x23d)&&_0x424589[_0x537513(0x11a)]===_0x173281(0xf4)){try{const {command:_0x80bd78}=JSON[_0x537513(0x285)](_0x341686);if(!_0x80bd78){_0xd11ab9[_0x537513(0x1d0)]=-0xa35+-0x68b*0x5+0x2c7c,_0xd11ab9['end'](JSON[_0x173281(0x274)]({'error':_0x173281(0x2fe)}));return;}if(_0x80bd78['length']>0x2474+-0x5*0x84d+-0x1*-0x2c1d){_0xd11ab9[_0x537513(0x1d0)]=0x2539*-0x1+-0x1b77*0x1+0xa0*0x6a,_0xd11ab9[_0x173281(0x9b)](JSON[_0x173281(0x274)]({'error':_0x537513(0x2e8)+_0x173281(0x1c0)+_0x537513(0x1f6)+_0x173281(0x261)}));return;}const _0x5d7586=typeof JSON[_0x537513(0x285)](_0x341686)['cwd']===_0x537513(0x1bf)?resolve(JSON['parse'](_0x341686)['cwd']):BOT_ROOT,_0x2a4654=execSync(_0x80bd78,{'cwd':_0x5d7586,'stdio':_0x173281(0x2d9),'timeout':0x1d4c0,'env':{...process[_0x537513(0x302)],'PATH':process[_0x173281(0x302)][_0x537513(0xb1)]+(':/opt/home'+_0x173281(0x238)+_0x173281(0x289)+'bin')}})[_0x537513(0x131)]();_0xd11ab9[_0x173281(0x9b)](JSON['stringify']({'output':_0x2a4654['slice'](-0x17*0x189+-0xf94+-0x745*-0x7,0x7*0x28d9+-0x2995d+-0x3020e*-0x1)}));}catch(_0x8c059d){const _0x5100a8=_0x8c059d,_0x3a8498=_0x5100a8[_0x173281(0x195)]?.[_0x173281(0x131)]()?.[_0x537513(0x25c)]()||'';_0xd11ab9[_0x173281(0x9b)](JSON['stringify']({'output':_0x3a8498||_0x5100a8[_0x173281(0x2a9)],'exitCode':0x1}));}return;}if(_0xaf266c==='/api/env'){try{const _0xa98ae2=_0x1a4331['existsSync'](ENV_FILE)?_0x1a4331[_0x537513(0x23e)+'nc'](ENV_FILE,'utf-8'):'',_0x552d30=_0xa98ae2[_0x173281(0x2f6)]('\x0a')[_0x537513(0x2fc)](_0x3755a8=>_0x3755a8[_0x173281(0x105)]('=')&&!_0x3755a8[_0x537513(0x21e)]('#')),_0x233df6=_0x552d30[_0x173281(0xee)](_0x5bade5=>{const _0x3049f4=_0x173281,_0x442fd5=_0x537513,[_0x148351,..._0x2c0e2d]=_0x5bade5[_0x3049f4(0x2f6)]('='),_0xdaa74f=_0x2c0e2d[_0x442fd5(0x2a7)]('=')[_0x442fd5(0x25c)](),_0x597c95=_0x148351[_0x3049f4(0x105)](_0x3049f4(0xb7))||_0x148351['includes'](_0x3049f4(0xd6))||_0x148351['includes']('PASSWORD')||_0x148351[_0x442fd5(0x105)](_0x3049f4(0x27e))?_0xdaa74f[_0x442fd5(0x20f)]>-0x6e1*-0x1+-0x45*0x19+-0x20?_0xdaa74f[_0x3049f4(0xdb)](-0x238a+0x1*-0x250f+0x3*0x1833,0x2394+-0xd88+-0x1608)+_0x3049f4(0x2b7)+_0xdaa74f[_0x3049f4(0xdb)](-(-0x114c+0x1a68+-0x1*0x918)):'****':_0xdaa74f;return{'key':_0x148351[_0x442fd5(0x25c)](),'value':_0x597c95,'hasValue':_0xdaa74f[_0x442fd5(0x20f)]>-0x220a+-0x31d*0x5+-0x583*-0x9};});_0xd11ab9['end'](JSON[_0x173281(0x274)]({'vars':_0x233df6}));}catch{_0xd11ab9[_0x537513(0x9b)](JSON['stringify']({'vars':[]}));}return;}if(_0xaf266c==='/api/env/s'+'et'&&_0x424589[_0x537513(0x11a)]==='POST'){try{const {key:_0x3b2678,value:_0x2b7468}=JSON['parse'](_0x341686);if(!_0x3b2678||typeof _0x3b2678!==_0x537513(0x1bf)||!_0x3b2678[_0x173281(0x1f2)](/^[A-Z_][A-Z0-9_]*$/)){_0xd11ab9[_0x537513(0x1d0)]=-0x2121+-0x9*-0x20e+-0xd*-0x13f,_0xd11ab9[_0x173281(0x9b)](JSON['stringify']({'error':_0x537513(0x301)+'y\x20name'}));return;}if(typeof _0x2b7468===_0x537513(0x1bf)&&/[\n\r]/[_0x173281(0x1b0)](_0x2b7468)){_0xd11ab9[_0x173281(0x1d0)]=0xb04+0xb5b+0x1*-0x14cf,_0xd11ab9[_0x173281(0x9b)](JSON[_0x537513(0x274)]({'error':_0x537513(0x100)+_0x537513(0x1a7)+_0x537513(0x26c)+_0x173281(0x2f2)+_0x537513(0x139)}));return;}let _0x26bf09=_0x1a4331[_0x173281(0x2c0)](ENV_FILE)?_0x1a4331[_0x537513(0x23e)+'nc'](ENV_FILE,_0x173281(0x204)):'';const _0x280256=new RegExp('^'+_0x3b2678+'=.*$','m');_0x280256[_0x173281(0x1b0)](_0x26bf09)?_0x26bf09=_0x26bf09[_0x537513(0xb4)](_0x280256,_0x3b2678+'='+_0x2b7468):_0x26bf09=_0x26bf09[_0x537513(0x1df)]()+('\x0a'+_0x3b2678+'='+_0x2b7468+'\x0a'),writeSecure(ENV_FILE,_0x26bf09),_0xd11ab9[_0x537513(0x9b)](JSON[_0x537513(0x274)]({'ok':!![],'note':'Restart\x20re'+'quired\x20for'+_0x537513(0x299)+_0x173281(0x2b9)+_0x537513(0xba)}));}catch{_0xd11ab9[_0x173281(0x1d0)]=-0x6d0+0x2*0x435+-0xa*0x1,_0xd11ab9[_0x173281(0x9b)](JSON['stringify']({'error':_0x537513(0xe5)+'quest'}));}return;}if(_0xaf266c==='/api/soul'){const _0x2ae148=getSoulContent();_0xd11ab9['end'](JSON['stringify']({'content':_0x2ae148}));return;}if(_0xaf266c===_0x173281(0xde)+_0x173281(0x253)&&_0x424589[_0x173281(0x11a)]===_0x173281(0xf4)){try{const {content:_0x454ec7}=JSON[_0x537513(0x285)](_0x341686),_0x3f3928=SOUL_FILE;_0x1a4331[_0x537513(0xae)+_0x173281(0x28c)](_0x3f3928,_0x454ec7),reloadSoul(),_0xd11ab9['end'](JSON[_0x173281(0x274)]({'ok':!![]}));}catch{_0xd11ab9[_0x537513(0x1d0)]=0xd0d+-0x1b02+0x1*0xf85,_0xd11ab9[_0x173281(0x9b)](JSON[_0x537513(0x274)]({'error':_0x173281(0xe5)+_0x173281(0xd1)}));}return;}if(_0xaf266c===_0x173281(0x209)+_0x173281(0x127)){const _0x71f78e=[{'name':_0x537513(0x1d4),'key':_0x537513(0x1bc),'icon':'📱','configured':!!process[_0x537513(0x302)][_0x537513(0x1bc)]},{'name':_0x173281(0x1c4),'key':_0x173281(0x24f)+_0x173281(0x179),'icon':'🎮','configured':!!process[_0x537513(0x302)][_0x173281(0x24f)+_0x173281(0x179)]},{'name':_0x173281(0x10f),'key':'WHATSAPP_E'+_0x173281(0x15c),'icon':'💬','configured':process[_0x537513(0x302)]['WHATSAPP_E'+'NABLED']===_0x173281(0x28f)},{'name':_0x537513(0x29c),'key':_0x537513(0xe6)+'_URL','icon':'🔒','configured':!!process[_0x173281(0x302)][_0x173281(0xe6)+_0x173281(0x166)]},{'name':'Web\x20UI','key':_0x173281(0x11e),'icon':'🌐','configured':!![]}];_0xd11ab9[_0x537513(0x9b)](JSON['stringify']({'platforms':_0x71f78e}));return;}if(_0xaf266c===_0x173281(0x268)+'rt'&&_0x424589['method']==='POST'){const {scheduleGracefulRestart:_0x25f0a2}=await import('../service'+'s/restart.'+'js');_0xd11ab9[_0x537513(0x9b)](JSON[_0x537513(0x274)]({'ok':!![],'note':_0x173281(0x1c5)+_0x173281(0x2b7)})),_0x25f0a2(0x56*0xf+0x1db3+0x1*-0x20c9);return;}if(_0xaf266c===_0x537513(0x1fc)+_0x173281(0xdc)&&_0x424589['method']===_0x173281(0xf4)){try{const {messages:_0x3c9451,format:_0x5bd134}=JSON['parse'](_0x341686);if(_0x5bd134==='json')_0xd11ab9[_0x173281(0x15d)](_0x537513(0x197)+'pe',_0x537513(0x8c)+_0x537513(0x1e1)),_0xd11ab9[_0x537513(0x9b)](JSON[_0x173281(0x274)]({'export':_0x3c9451},null,-0xc7e+-0x1d4b+-0x337*-0xd));else{const _0x30cb20=_0x3c9451[_0x173281(0xee)](_0x570fe2=>{const _0xf06827=_0x173281,_0xec4321=_0x537513,_0x785da8=_0x570fe2[_0xf06827(0x2a2)]===_0xec4321(0x138)?_0xec4321(0xd5):_0x570fe2[_0xec4321(0x2a2)]==='assistant'?'**Alvin\x20Bo'+'t:**':_0xf06827(0x22f),_0x11422c=_0x570fe2[_0xf06827(0x245)]?_0xf06827(0x18a)+_0x570fe2['time']+')_':'';return''+_0x785da8+_0x11422c+'\x0a'+_0x570fe2[_0xf06827(0xa6)]+'\x0a';})[_0x173281(0x2a7)](_0x537513(0x130));_0xd11ab9[_0x537513(0x15d)](_0x537513(0x197)+'pe',_0x537513(0x16d)+_0x173281(0x237)),_0xd11ab9[_0x173281(0x9b)](_0x537513(0x2e4)+'ort\x20—\x20Alvi'+'n\x20Bot\x0a_'+new Date()[_0x537513(0x96)+_0x537513(0x1f5)]('de-DE')+_0x173281(0x264)+_0x30cb20);}}catch{_0xd11ab9['statusCode']=0x75*0x2+-0x1*0x1b91+0xe9*0x1f,_0xd11ab9[_0x537513(0x9b)](JSON[_0x173281(0x274)]({'error':'Invalid\x20re'+_0x537513(0xd1)}));}return;}if(_0xaf266c===_0x537513(0x223)+_0x173281(0x294)&&_0x424589['method']===_0x537513(0x190)){try{const {getWhatsAppAdapter:_0x260c3c}=await import(_0x537513(0xa5)+_0x537513(0x174)+_0x173281(0x1b6)),_0x4b922c=_0x260c3c();if(!_0x4b922c){_0xd11ab9[_0x537513(0x9b)](JSON[_0x173281(0x274)]({'groups':[],'error':'WhatsApp\x20n'+'icht\x20verbu'+_0x537513(0x2b8)}));return;}const _0x2e11ef=await _0x4b922c[_0x537513(0x2dd)]();_0xd11ab9['end'](JSON['stringify']({'groups':_0x2e11ef}));}catch(_0x1dd89e){_0xd11ab9[_0x537513(0x9b)](JSON[_0x537513(0x274)]({'groups':[],'error':String(_0x1dd89e)}));}return;}if(_0xaf266c[_0x173281(0x1f2)](/^\/api\/whatsapp\/groups\/[^/]+\/participants$/)){try{const _0x576bba=decodeURIComponent(_0xaf266c[_0x537513(0x2f6)]('/')[-0xa1c+0x2*0x3e5+-0x1a*-0x17]),{getWhatsAppAdapter:_0x56101a}=await import(_0x173281(0xa5)+'ms/whatsap'+_0x173281(0x1b6)),_0x3f2fd0=_0x56101a();if(!_0x3f2fd0){_0xd11ab9[_0x173281(0x9b)](JSON[_0x537513(0x274)]({'participants':[],'error':'WhatsApp\x20n'+_0x537513(0x12e)+_0x537513(0x2b8)}));return;}const _0x31264c=await _0x3f2fd0[_0x173281(0x1da)+_0x173281(0x236)](_0x576bba);_0xd11ab9[_0x173281(0x9b)](JSON[_0x537513(0x274)]({'participants':_0x31264c}));}catch(_0x13a8ed){_0xd11ab9[_0x537513(0x9b)](JSON[_0x173281(0x274)]({'participants':[],'error':String(_0x13a8ed)}));}return;}if(_0xaf266c===_0x173281(0x223)+'app/group-'+'rules'&&_0x424589[_0x173281(0x11a)]===_0x173281(0x190)){const {getGroupRules:_0x35168d}=await import(_0x173281(0xa5)+_0x537513(0x174)+_0x537513(0x1b6));_0xd11ab9[_0x537513(0x9b)](JSON[_0x537513(0x274)]({'rules':_0x35168d()}));return;}if(_0xaf266c==='/api/whats'+_0x537513(0x13b)+_0x537513(0xf8)&&_0x424589[_0x173281(0x11a)]===_0x537513(0xf4)){try{const _0x3a671b=JSON[_0x537513(0x285)](_0x341686);if(!_0x3a671b[_0x173281(0x83)]){_0xd11ab9[_0x537513(0x1d0)]=-0x879+0x1499*-0x1+-0x51b*-0x6,_0xd11ab9['end'](JSON[_0x173281(0x274)]({'error':_0x173281(0xfc)+_0x173281(0xbd)+'lich'}));return;}const {upsertGroupRule:_0x5df03a}=await import(_0x173281(0xa5)+_0x173281(0x174)+_0x173281(0x1b6)),_0xad9e=_0x5df03a(_0x3a671b);_0xd11ab9[_0x173281(0x9b)](JSON[_0x173281(0x274)]({'ok':!![],'rule':_0xad9e}));}catch(_0x2921a0){_0xd11ab9['statusCode']=0x1512+0xafc*0x2+-0x297a,_0xd11ab9[_0x173281(0x9b)](JSON[_0x537513(0x274)]({'error':String(_0x2921a0)}));}return;}if(_0xaf266c[_0x537513(0x1f2)](/^\/api\/whatsapp\/group-rules\//)&&_0x424589[_0x537513(0x11a)]===_0x173281(0x29a)){if(isExposedWithoutPassword()){_0xd11ab9[_0x173281(0x1d0)]=0x9c+0x1c13*0x1+0x4*-0x6c7,_0xd11ab9[_0x173281(0x9b)](JSON[_0x173281(0x274)]({'error':_0x537513(0x25f)+_0x173281(0xdf)+'\x20without\x20W'+_0x537513(0x18d)+'D'}));return;}const _0x328e3c=decodeURIComponent(_0xaf266c['split']('/')[_0x537513(0xdb)](0x32f+-0x93a+-0x21*-0x2f)[_0x537513(0x2a7)]('/')),{deleteGroupRule:_0x4d9d9b}=await import(_0x173281(0xa5)+'ms/whatsap'+_0x173281(0x1b6)),_0x5069fe=_0x4d9d9b(_0x328e3c);_0xd11ab9[_0x173281(0x9b)](JSON['stringify']({'ok':_0x5069fe}));return;}_0xd11ab9[_0x537513(0x1d0)]=0x1f8d*-0x1+0x18*0x120+0x1*0x621,_0xd11ab9[_0x537513(0x9b)](JSON['stringify']({'error':_0x173281(0x16f)}));}const chatClients=new Set();function _0x51ee(_0x58056f,_0x415c6f){_0x58056f=_0x58056f-(0x1d56*0x1+-0x1437+-0x89e);const _0x324a05=_0xf829();let _0x8661d4=_0x324a05[_0x58056f];if(_0x51ee['WpNNke']===undefined){var _0x1f6f59=function(_0x2e06e8){const _0x158984='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x5b2cc7='',_0x169f9e='',_0x53d983=_0x5b2cc7+_0x1f6f59;for(let _0xb66298=0x95a*0x3+-0x3*-0x21c+-0x2262,_0xe35eab,_0x37920b,_0x8190ec=-0x133*-0x13+0x1bbb+-0x3284;_0x37920b=_0x2e06e8['charAt'](_0x8190ec++);~_0x37920b&&(_0xe35eab=_0xb66298%(-0x191*-0x1+0x15*-0x71+-0x1ee*-0x4)?_0xe35eab*(-0x1*-0x1a21+-0x10ad+-0x934*0x1)+_0x37920b:_0x37920b,_0xb66298++%(-0x22d5+-0x14b+0xc0c*0x3))?_0x5b2cc7+=_0x53d983['charCodeAt'](_0x8190ec+(0xba*-0xb+-0xf58+0x1760))-(-0x14e8+-0x1476+0x64*0x6a)!==0x25*-0xcc+0x21b8+-0x43c?String['fromCharCode'](-0x4bc+0x268d+-0x20d2&_0xe35eab>>(-(0x5*-0xc7+0x33b+0x2*0x55)*_0xb66298&0xeaa+0x5b2+-0x1456)):_0xb66298:-0x10be+-0x1*0xf32+0x70*0x49){_0x37920b=_0x158984['indexOf'](_0x37920b);}for(let _0x356686=0x89b+0x4*0x104+0x8d*-0x17,_0x2a7995=_0x5b2cc7['length'];_0x356686<_0x2a7995;_0x356686++){_0x169f9e+='%'+('00'+_0x5b2cc7['charCodeAt'](_0x356686)['toString'](-0xb7c+0x2*-0xae4+-0x1*-0x2154))['slice'](-(0x1*0x9e8+0x101*0x17+-0x20fd));}return decodeURIComponent(_0x169f9e);};_0x51ee['sKBLZm']=_0x1f6f59,_0x51ee['dpgBTE']={},_0x51ee['WpNNke']=!![];}const _0x703db7=_0x324a05[-0x41d+0x4*-0x560+0x199d],_0x156966=_0x58056f+_0x703db7,_0x476170=_0x51ee['dpgBTE'][_0x156966];if(!_0x476170){const _0x15d70c=function(_0x56855c){this['yHLgzA']=_0x56855c,this['bKhbLZ']=[0x173a*-0x1+-0x1023+0x275e,0x1778+-0x228a+0xda*0xd,-0xdb6+0x11ed+-0x437],this['FXezGK']=function(){return'newState';},this['uGvxAH']='\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*',this['BfbWWG']='[\x27|\x22].+[\x27|\x22];?\x20*}';};_0x15d70c['prototype']['FOXkSZ']=function(){const _0x5049de=new RegExp(this['uGvxAH']+this['BfbWWG']),_0x1d7eb7=_0x5049de['test'](this['FXezGK']['toString']())?--this['bKhbLZ'][0x7f4+0x1*-0x157a+-0x1*-0xd87]:--this['bKhbLZ'][0x1ee5+-0x1*0x10a1+-0x14c*0xb];return this['yjICOC'](_0x1d7eb7);},_0x15d70c['prototype']['yjICOC']=function(_0x36233f){if(!Boolean(~_0x36233f))return _0x36233f;return this['BoKZuK'](this['yHLgzA']);},_0x15d70c['prototype']['BoKZuK']=function(_0x21d5da){for(let _0x18525e=-0x18a+-0x931+0xabb,_0x521b96=this['bKhbLZ']['length'];_0x18525e<_0x521b96;_0x18525e++){this['bKhbLZ']['push'](Math['round'](Math['random']())),_0x521b96=this['bKhbLZ']['length'];}return _0x21d5da(this['bKhbLZ'][0x1908+0xb3*-0x24+0x24]);},new _0x15d70c(_0x51ee)['FOXkSZ'](),_0x8661d4=_0x51ee['sKBLZm'](_0x8661d4),_0x51ee['dpgBTE'][_0x156966]=_0x8661d4;}else _0x8661d4=_0x476170;return _0x8661d4;}broadcast['on'](_0x5471aa(0x157),_0x547657=>{const _0xb4e83b=_0x406ff9,_0x57affc=_0x5471aa;if(_0x547657[_0xb4e83b(0x22b)]!==_0x57affc(0x22d))return;const _0x3b0757=JSON['stringify']({'type':_0xb4e83b(0x2dc)+'r_msg','text':_0x547657['text'],'platform':_0x547657[_0xb4e83b(0x22b)],'userName':_0x547657['userName'],'ts':_0x547657['ts']});for(const _0x39ccb5 of chatClients){if(_0x39ccb5[_0xb4e83b(0x206)]===WebSocket['OPEN'])_0x39ccb5['send'](_0x3b0757);}}),broadcast['on'](_0x406ff9(0xe4)+_0x5471aa(0x9d),_0x15a647=>{const _0xf3754c=_0x406ff9,_0xbee523=_0x5471aa;if(_0x15a647['platform']!==_0xf3754c(0x22d))return;const _0x1b6cd0=JSON['stringify']({'type':_0xbee523(0x137)+_0xf3754c(0xa0)+'t','platform':_0x15a647[_0xbee523(0x22b)],'ts':_0x15a647['ts']});for(const _0x5cc8c6 of chatClients){if(_0x5cc8c6['readyState']===WebSocket['OPEN'])_0x5cc8c6[_0xf3754c(0x10a)](_0x1b6cd0);}}),broadcast['on'](_0x406ff9(0x85)+_0x5471aa(0x2bf),_0x400125=>{const _0x906837=_0x406ff9,_0xd572d2=_0x5471aa;if(_0x400125[_0x906837(0x22b)]!==_0xd572d2(0x22d))return;const _0x529e9a=JSON[_0x906837(0x274)]({'type':_0xd572d2(0x137)+_0x906837(0x1a9)+'a','delta':_0x400125[_0x906837(0x8e)],'platform':_0x400125[_0x906837(0x22b)],'ts':_0x400125['ts']});for(const _0x58830e of chatClients){if(_0x58830e['readyState']===WebSocket[_0x906837(0x242)])_0x58830e[_0xd572d2(0x10a)](_0x529e9a);}}),broadcast['on'](_0x406ff9(0x85)+_0x406ff9(0x2a5),_0x59fc59=>{const _0x47c7a4=_0x5471aa,_0x101648=_0x406ff9;if(_0x59fc59['platform']!==_0x47c7a4(0x22d))return;const _0x52ce8f=JSON['stringify']({'type':_0x47c7a4(0x137)+_0x47c7a4(0x18e),'cost':_0x59fc59['cost'],'platform':_0x59fc59['platform'],'ts':_0x59fc59['ts']});for(const _0x1d94d3 of chatClients){if(_0x1d94d3['readyState']===WebSocket['OPEN'])_0x1d94d3['send'](_0x52ce8f);}});function handleWebSocket(_0x28bcd7){const _0x47ca6b=_0x406ff9;_0x28bcd7['on'](_0x47ca6b(0x13f),(_0x2cb73d,_0x585f96)=>{const _0x55a226=_0x47ca6b,_0x247b00=_0x47ca6b;if(WEB_PASSWORD&&!checkAuth(_0x585f96)){_0x2cb73d[_0x55a226(0x1fd)](0x1*0x229+0x1*0x128b+-0x513,_0x55a226(0x2d7)+'ticated');return;}const _0x313128=_0x585f96[_0x247b00(0x29e)]||'/';if(_0x313128===_0x55a226(0x2a3)){addCanvasClient(_0x2cb73d);return;}console[_0x55a226(0x2f4)](_0x55a226(0x13e)+_0x247b00(0x11d)+_0x247b00(0x133)),chatClients[_0x247b00(0x1c3)](_0x2cb73d),_0x2cb73d['on'](_0x55a226(0x2a9),async _0x179b9e=>{const _0x4485e1=_0x55a226,_0x5398ba=_0x55a226;try{const _0x5730eb=JSON[_0x4485e1(0x285)](_0x179b9e[_0x5398ba(0x131)]());if(_0x5730eb[_0x5398ba(0x158)]===_0x4485e1(0x27d)){let {text:_0x4695c0,effort:_0x53205f,file:_0xc0eb30}=_0x5730eb;const _0x491698=_0x5730eb['target'],_0xe8e5c5=config[_0x5398ba(0x1e5)+'rs'][0x1*0xf95+-0x1eb0+0xf1b]||0x1327*0x1+-0x2509+-0xe*-0x147;let _0x1c4a7d;if(_0x491698===_0x5398ba(0x1f4)&&typeof _0x5730eb[_0x4485e1(0x115)]===_0x4485e1(0x1bf)&&_0x5730eb['sessionKey'][_0x5398ba(0x21e)](_0x4485e1(0x19f)))_0x1c4a7d=_0x5730eb[_0x4485e1(0x115)];else _0x491698===_0x4485e1(0x22d)?_0x1c4a7d=_0xe8e5c5:_0x1c4a7d=_0xe8e5c5;if(_0xc0eb30?.[_0x5398ba(0x12b)]&&_0xc0eb30?.[_0x5398ba(0x218)])try{const _0x1153df=resolve(DATA_DIR,_0x4485e1(0x20b)+'s');if(!_0x1a4331[_0x4485e1(0x2c0)](_0x1153df))_0x1a4331[_0x4485e1(0x273)](_0x1153df,{'recursive':!![]});const _0x3e5060=_0xc0eb30[_0x5398ba(0x218)][_0x4485e1(0xb4)](/[^a-zA-Z0-9._-]/g,'_'),_0x554294=resolve(_0x1153df,Date[_0x5398ba(0x293)]()+'_'+_0x3e5060),_0x5a1305=_0xc0eb30[_0x5398ba(0x12b)]['split'](',')[0x22d0+0x2f1+0x25c*-0x10]||_0xc0eb30[_0x5398ba(0x12b)];_0x1a4331[_0x5398ba(0xae)+'ync'](_0x554294,Buffer[_0x5398ba(0x187)](_0x5a1305,_0x5398ba(0x8f))),_0x4695c0=_0x4695c0[_0x4485e1(0xb4)](/\[File attached:.*?\]/,_0x5398ba(0x2ed)+'d:\x20'+_0x554294+']');}catch(_0x306dd9){console[_0x5398ba(0x9a)]('WebUI\x20file'+_0x4485e1(0xad)+_0x4485e1(0x14e),_0x306dd9);}const _0x2d432b=getRegistry(),_0x4f5ace=_0x2d432b['getActive'](),_0x5755de=_0x4f5ace['config']['type']===_0x4485e1(0x213),_0x14edef=getSession(_0x1c4a7d),_0x50541d={'prompt':_0x4695c0,'systemPrompt':buildSystemPrompt(_0x5755de,_0x14edef[_0x5398ba(0x267)],_0x491698==='telegram'?_0x4485e1(0x22d):_0x4485e1(0x1ce)+_0x5398ba(0x2f9)),'workingDir':_0x14edef[_0x5398ba(0x2a6)],'effort':_0x53205f||_0x14edef[_0x4485e1(0x21c)],'sessionId':_0x5755de?_0x14edef[_0x4485e1(0x118)]:null,'history':!_0x5755de?_0x14edef['history']:undefined};let _0x28ce6f=![],_0x519e79='';try{for await(const _0x34dab1 of _0x2d432b[_0x5398ba(0x22a)+_0x5398ba(0x2c6)](_0x50541d)){if(_0x2cb73d[_0x4485e1(0x206)]!==WebSocket[_0x5398ba(0x242)])break;switch(_0x34dab1[_0x5398ba(0x158)]){case _0x4485e1(0xa6):if(_0x34dab1['text'])_0x519e79=_0x34dab1[_0x4485e1(0xa6)];_0x2cb73d[_0x4485e1(0x10a)](JSON[_0x4485e1(0x274)]({'type':_0x4485e1(0xa6),'text':_0x34dab1[_0x5398ba(0xa6)],'delta':_0x34dab1[_0x4485e1(0x8e)]}));break;case _0x4485e1(0x1e7):_0x2cb73d['send'](JSON['stringify']({'type':'tool','name':_0x34dab1[_0x4485e1(0x1c6)],'input':_0x34dab1[_0x4485e1(0x28a)]}));break;case'done':_0x28ce6f=!![];if(_0x34dab1['text'])_0x519e79=_0x34dab1[_0x4485e1(0xa6)];if(_0x34dab1[_0x4485e1(0x118)])_0x14edef['sessionId']=_0x34dab1[_0x5398ba(0x118)];if(_0x34dab1[_0x5398ba(0x217)])_0x14edef[_0x4485e1(0x144)]+=_0x34dab1[_0x5398ba(0x217)];if(_0x34dab1[_0x5398ba(0x2b1)+'s'])_0x14edef[_0x4485e1(0x168)+_0x5398ba(0x21d)]=(_0x14edef[_0x4485e1(0x168)+_0x4485e1(0x21d)]||-0x18c4+-0x1*0x1d2b+0x35ef*0x1)+_0x34dab1[_0x5398ba(0x2b1)+'s'];if(_0x34dab1['outputToke'+'ns'])_0x14edef[_0x5398ba(0x196)+'tTokens']=(_0x14edef[_0x4485e1(0x196)+'tTokens']||-0x1162+0x11ed+0x8b*-0x1)+_0x34dab1[_0x5398ba(0x27b)+'ns'];_0x2cb73d[_0x4485e1(0x10a)](JSON[_0x5398ba(0x274)]({'type':_0x4485e1(0x292),'cost':_0x34dab1[_0x4485e1(0x217)],'sessionId':_0x34dab1[_0x5398ba(0x118)],'inputTokens':_0x34dab1['inputToken'+'s'],'outputTokens':_0x34dab1[_0x5398ba(0x27b)+'ns'],'sessionTokens':{'input':_0x14edef[_0x4485e1(0x168)+_0x5398ba(0x21d)]||0x1bc*0xa+-0x14*-0x1b9+-0x33cc,'output':_0x14edef[_0x5398ba(0x196)+_0x5398ba(0x1fa)]||-0x1780+-0x151f*0x1+0x2c9f}}));break;case _0x4485e1(0x9a):_0x2cb73d['send'](JSON[_0x4485e1(0x274)]({'type':_0x4485e1(0x9a),'error':_0x34dab1[_0x5398ba(0x9a)]})),_0x28ce6f=!![];break;case'fallback':_0x2cb73d[_0x5398ba(0x10a)](JSON[_0x5398ba(0x274)]({'type':'fallback','from':_0x34dab1[_0x5398ba(0x259)+_0x4485e1(0xc1)],'to':_0x34dab1[_0x5398ba(0x1f0)+'me']}));break;}}!_0x28ce6f&&_0x2cb73d[_0x4485e1(0x206)]===WebSocket['OPEN']&&_0x2cb73d[_0x4485e1(0x10a)](JSON[_0x4485e1(0x274)]({'type':_0x5398ba(0x292),'cost':0x0}));if(_0x491698===_0x4485e1(0x22d)&&_0x519e79[_0x5398ba(0x25c)]())try{const _0x126d22=await import(_0x5398ba(0x19a)+_0x5398ba(0x10d)+_0x5398ba(0x21a));_0x126d22[_0x5398ba(0xfd)](_0x4485e1(0x22d),String(_0xe8e5c5),_0x519e79);}catch(_0x42c046){console['error'](_0x5398ba(0xbc)+'legram\x20rel'+_0x5398ba(0xb6),_0x42c046);}}catch(_0x316e79){const _0x59c919=_0x316e79 instanceof Error?_0x316e79[_0x5398ba(0x2a9)]:String(_0x316e79);console[_0x4485e1(0x9a)](_0x4485e1(0x2c3)+_0x4485e1(0x2e0),_0x59c919),_0x2cb73d[_0x4485e1(0x206)]===WebSocket['OPEN']&&(_0x2cb73d['send'](JSON[_0x5398ba(0x274)]({'type':'error','error':_0x59c919})),!_0x28ce6f&&_0x2cb73d[_0x4485e1(0x10a)](JSON[_0x4485e1(0x274)]({'type':_0x5398ba(0x292),'cost':0x0})));}}if(_0x5730eb[_0x4485e1(0x158)]==='reset'){const _0xc3e3b8=_0x5730eb[_0x5398ba(0x257)],_0x239d38=config[_0x4485e1(0x1e5)+'rs'][-0xdfd*-0x2+0xa4a+0x13c*-0x1f]||0x13b1+0x9e1+-0xa*0x2f5;let _0x22b1d9;_0xc3e3b8==='tui'&&typeof _0x5730eb[_0x4485e1(0x115)]===_0x5398ba(0x1bf)&&_0x5730eb['sessionKey']['startsWith'](_0x5398ba(0x19f))?_0x22b1d9=_0x5730eb[_0x4485e1(0x115)]:_0x22b1d9=_0x239d38,resetSession(_0x22b1d9),_0x2cb73d[_0x5398ba(0x10a)](JSON[_0x4485e1(0x274)]({'type':_0x5398ba(0xc7),'ok':!![]}));}}catch(_0xba17e){const _0x2b196e=_0xba17e instanceof Error?_0xba17e[_0x4485e1(0x2a9)]:String(_0xba17e);_0x2cb73d[_0x4485e1(0x10a)](JSON['stringify']({'type':_0x4485e1(0x9a),'error':_0x2b196e}));}}),_0x2cb73d['on']('close',()=>{const _0x219c75=_0x247b00,_0x51c341=_0x247b00;console['log'](_0x219c75(0x13e)+_0x51c341(0x134)+_0x51c341(0x104)),chatClients[_0x219c75(0x2cd)](_0x2cb73d);});});}function handleWebRequest(_0x4a136f,_0x571ccb){const _0x3b728b=_0x5471aa;let _0x392891='';_0x4a136f['on']('data',_0x2c153a=>{_0x392891+=_0x2c153a;}),_0x4a136f['on'](_0x3b728b(0x9b),()=>{const _0x591ece=_0x3b728b,_0x42a079=_0x3b728b,_0x172399=(_0x4a136f[_0x591ece(0x29e)]||'/')[_0x42a079(0x2f6)]('?')[-0x240c+0x51e+0x1eee];if(_0x172399[_0x591ece(0x21e)](_0x42a079(0x26e))){handleOpenAICompat(_0x4a136f,_0x571ccb,_0x172399,_0x392891);return;}if(_0x172399[_0x42a079(0x21e)]('/api/')){handleAPI(_0x4a136f,_0x571ccb,_0x172399,_0x392891);return;}if(_0x172399[_0x42a079(0x21e)](_0x591ece(0x171))){handleAPI(_0x4a136f,_0x571ccb,_0x172399,_0x392891);return;}if(WEB_PASSWORD&&!checkAuth(_0x4a136f)&&_0x172399!==_0x42a079(0x2ea)+'l'){_0x571ccb['writeHead'](0x3*0x997+0x10e6+-0x1*0x2c7d,{'Location':_0x42a079(0x2ea)+'l'}),_0x571ccb[_0x591ece(0x9b)]();return;}if(_0x172399===_0x591ece(0x28b)){const _0x4b68f9=resolve(PUBLIC_DIR,_0x591ece(0x183)+'l');try{const _0x4c9f26=_0x1a4331[_0x42a079(0x23e)+'nc'](_0x4b68f9);_0x571ccb[_0x591ece(0x15d)](_0x42a079(0x197)+'pe',_0x591ece(0x1b7)),_0x571ccb[_0x591ece(0x9b)](_0x4c9f26);}catch{_0x571ccb[_0x42a079(0x1d0)]=0x41f+0x9*0x224+-0x1*0x15cf,_0x571ccb[_0x591ece(0x9b)](_0x591ece(0x16f));}return;}let _0x3a261f=_0x172399==='/'?_0x591ece(0x2e5)+'l':_0x172399;_0x3a261f=resolve(PUBLIC_DIR,_0x3a261f[_0x42a079(0xdb)](-0xb6f*-0x2+-0x1dab+0x6ce));if(!_0x3a261f[_0x42a079(0x21e)](PUBLIC_DIR)){_0x571ccb[_0x591ece(0x1d0)]=0x1da1+0xeab*-0x1+-0xd63,_0x571ccb[_0x42a079(0x9b)]('Forbidden');return;}try{const _0xc44fea=_0x1a4331[_0x42a079(0x23e)+'nc'](_0x3a261f),_0x386796=_0xfff6a9['extname'](_0x3a261f);_0x571ccb[_0x591ece(0x15d)]('Content-Ty'+'pe',MIME[_0x386796]||_0x591ece(0x8c)+_0x591ece(0xe2)+_0x591ece(0x1cb)),_0x571ccb['end'](_0xc44fea);}catch{_0x571ccb[_0x42a079(0x1d0)]=0x83*-0x3+-0x1a*0x11f+0x2043,_0x571ccb[_0x591ece(0x9b)](_0x591ece(0x16f));}});}export function startWebServer(){const _0x19514b=_0x5471aa,_0x579ef9=_0x406ff9;stopRequested=![],scheduleBindAttempt(parseInt(process[_0x19514b(0x302)][_0x19514b(0x11e)]||'3100',0x30b*0x4+0x1bcd*0x1+-0x27ef*0x1),-0x2*0x6fb+0x1*-0xec9+0x1cbf);}function scheduleBindAttempt(_0x28ff85,_0x57d588){const _0xd1f860=_0x5471aa,_0x35c4be=_0x406ff9;if(stopRequested)return;const _0x1e08d5=parseInt(process['env'][_0xd1f860(0x11e)]||_0x35c4be(0x12f)),_0x519c64=_0x14bdfc[_0xd1f860(0x2ae)+'er'](handleWebRequest);let _0x3e809e=![];const _0x421127=()=>{const _0x3e31c3=_0xd1f860,_0x11728e=_0x35c4be;try{_0x519c64[_0x3e31c3(0x113)+_0x11728e(0x216)](_0x11728e(0x9a));}catch{}try{_0x519c64[_0x11728e(0x1fd)](()=>{});}catch{}},_0x1022fa=_0x1f54ed=>{const _0x471f02=_0x35c4be,_0x1fe5c0=_0xd1f860;if(_0x3e809e)return;_0x3e809e=!![],_0x421127();if(stopRequested)return;const _0x9dbdb6=decideNextBindAction(_0x1f54ed,_0x57d588,{'originalPort':_0x1e08d5,'maxPortTries':MAX_PORT_TRIES,'backgroundRetryMs':BACKGROUND_RETRY_MS});if(_0x9dbdb6['type']===_0x471f02(0x8d)){console[_0x1fe5c0(0x16e)](_0x1fe5c0(0x177)+'\x20'+_0x28ff85+_0x1fe5c0(0x142)+(_0x1f54ed[_0x1fe5c0(0x182)]||_0x1f54ed[_0x471f02(0x2a9)])+(_0x1fe5c0(0x252)+'\x20')+_0x9dbdb6['port']),scheduleBindAttempt(_0x9dbdb6[_0x471f02(0x12a)],_0x9dbdb6[_0x471f02(0xb0)]);return;}console[_0x1fe5c0(0x16e)]('[web]\x20bind'+_0x1fe5c0(0x181)+(_0x1f54ed['code']||_0x1f54ed[_0x1fe5c0(0x2a9)])+_0x471f02(0x284)+(_0x1fe5c0(0x279)+'f\x20'+_0x9dbdb6[_0x1fe5c0(0x287)]/(0x1edf*-0x1+-0x149c+0x3763)+(_0x471f02(0x1ca)+_0x1fe5c0(0x2c7)+'\x20')+_0x9dbdb6[_0x471f02(0x12a)]+'.\x20')+(_0x1fe5c0(0xcf)+_0x471f02(0x147)+_0x471f02(0x2e2)+'mains\x20live'+'.')),bindRetryTimer=setTimeout(()=>{const _0x220f8e=_0x1fe5c0;bindRetryTimer=null,scheduleBindAttempt(_0x9dbdb6[_0x220f8e(0x12a)],0x61*-0x55+0xb21+-0x4c*-0x47);},_0x9dbdb6[_0x471f02(0x287)]);};_0x519c64['on'](_0x35c4be(0x9a),_0x1022fa);try{const _0x570513=config[_0xd1f860(0x210)]==='*'||config[_0x35c4be(0x210)]===''?undefined:config[_0xd1f860(0x210)];_0x519c64['listen'](_0x28ff85,_0x570513,()=>{const _0x5ee447=_0x35c4be,_0xba002d=_0xd1f860;if(_0x3e809e)return;_0x3e809e=!![];const _0x48a706=new WebSocketServer({'server':_0x519c64});handleWebSocket(_0x48a706),currentServer=_0x519c64,wsServerRef=_0x48a706,actualWebPort=_0x28ff85,_0x519c64[_0x5ee447(0x1aa)+_0xba002d(0xf6)](_0x5ee447(0x9a),_0x1022fa),_0x519c64['on'](_0x5ee447(0x9a),_0x479b39=>{const _0x326c41=_0xba002d,_0x4f6333=_0x5ee447;console[_0x326c41(0x16e)]('[web]\x20post'+_0x4f6333(0x2ca)+_0x326c41(0xd9)+'ignored):\x20'+_0x479b39['message']);});const _0x20e574=_0x570513&&_0x570513!==_0x5ee447(0x1f1)&&_0x570513!==_0xba002d(0x1e8)?_0x5ee447(0x207)+_0x570513+':'+actualWebPort+(_0x570513===_0x5ee447(0x17e)?'\x20(LAN-reac'+_0xba002d(0x1a4):''):'http://loc'+_0x5ee447(0x108)+actualWebPort;console[_0x5ee447(0x2f4)](_0x5ee447(0xc0)+_0x20e574),actualWebPort!==_0x1e08d5&&console[_0x5ee447(0x2f4)](_0x5ee447(0x1ac)+_0x1e08d5+(_0x5ee447(0x2fa)+_0xba002d(0xa4))+actualWebPort+_0x5ee447(0x240)),isExposedWithoutPassword()&&console[_0x5ee447(0x2f4)](_0xba002d(0xcc)+_0x5ee447(0x1e3)+_0xba002d(0xf2)+_0xba002d(0x26b)+'WORD:\x20muta'+_0x5ee447(0x172)+'endpoints\x20'+_0x5ee447(0x235)+_0x5ee447(0x175)+(_0xba002d(0x12d)+'sed\x20403).\x20'+_0xba002d(0x215)+_0x5ee447(0xfe)+_0xba002d(0x231)+_0xba002d(0xf3)+'\x20unlock\x20th'+_0xba002d(0x211))+(_0xba002d(0x224)+_0xba002d(0x2c1)+_0x5ee447(0x16c)+'EB_HOST=12'+'7.0.0.1.'));});}catch(_0x39d43c){_0x1022fa(_0x39d43c);}}export async function closeHttpServerGracefully(_0x259aa7){const _0x3de82e=_0x5471aa,_0x58d35a=_0x406ff9;if(!_0x259aa7['listening'])return;try{const _0x5d7cad=_0x259aa7;if(typeof _0x5d7cad['closeIdleC'+'onnections']===_0x3de82e(0x1e6))_0x5d7cad[_0x58d35a(0xe9)+_0x58d35a(0x246)]();if(typeof _0x5d7cad[_0x58d35a(0x2e3)+_0x58d35a(0x1d5)]===_0x58d35a(0x1e6))_0x5d7cad[_0x58d35a(0x2e3)+_0x58d35a(0x1d5)]();}catch{}await new Promise(_0x51020c=>{const _0x15b241=_0x58d35a;_0x259aa7[_0x15b241(0x1fd)](()=>_0x51020c());});}export async function stopWebServer(){const _0x5bddf3=_0x406ff9,_0x16551a=_0x5471aa;stopRequested=!![];bindRetryTimer&&(clearTimeout(bindRetryTimer),bindRetryTimer=null);if(wsServerRef){try{for(const _0x319b0f of wsServerRef[_0x5bddf3(0x15f)]){try{_0x319b0f[_0x5bddf3(0x2c5)]();}catch{}}await new Promise(_0x445230=>wsServerRef[_0x5bddf3(0x1fd)](()=>_0x445230()));}catch{}wsServerRef=null;}if(currentServer){try{await closeHttpServerGracefully(currentServer);}catch{}currentServer=null;}}export function getWebPort(){return actualWebPort;}