@jango-blockchained/hoox-shared 1.0.9 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +17 -0
  2. package/dist/analytics.d.ts +23 -1
  3. package/dist/analytics.d.ts.map +1 -1
  4. package/dist/analytics.js +14 -3
  5. package/dist/api-client.d.ts +12 -2
  6. package/dist/api-client.d.ts.map +1 -1
  7. package/dist/api-client.js +74 -7
  8. package/dist/colors.d.ts +63 -24
  9. package/dist/colors.d.ts.map +1 -1
  10. package/dist/colors.js +60 -19
  11. package/dist/config.d.ts +34 -1
  12. package/dist/config.d.ts.map +1 -1
  13. package/dist/config.js +61 -6
  14. package/dist/cron-handler.d.ts +2 -2
  15. package/dist/cron-handler.d.ts.map +1 -1
  16. package/dist/d1/index.js +1 -0
  17. package/dist/d1/repository.js +297 -0
  18. package/dist/d1/schemas.js +81 -0
  19. package/dist/exchanges/base-exchange-client.js +120 -0
  20. package/dist/exchanges/types.js +1 -0
  21. package/dist/index.d.ts +21 -11
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +580 -60
  24. package/dist/middleware/auth.d.ts +42 -6
  25. package/dist/middleware/auth.d.ts.map +1 -1
  26. package/dist/middleware/auth.js +151 -0
  27. package/dist/middleware/cors.d.ts +28 -2
  28. package/dist/middleware/cors.d.ts.map +1 -1
  29. package/dist/middleware/cors.js +77 -0
  30. package/dist/middleware/index.d.ts +2 -2
  31. package/dist/middleware/index.d.ts.map +1 -1
  32. package/dist/middleware/index.js +192 -107
  33. package/dist/middleware/logger.js +202 -0
  34. package/dist/middleware/rate-limit.d.ts.map +1 -1
  35. package/dist/middleware/rate-limit.js +181 -0
  36. package/dist/middleware/security-headers.js +58 -0
  37. package/dist/middleware/validate.js +56 -0
  38. package/dist/operator-transport.d.ts +61 -0
  39. package/dist/operator-transport.d.ts.map +1 -0
  40. package/dist/operator-transport.js +87 -0
  41. package/dist/path-utils.d.ts +59 -1
  42. package/dist/path-utils.d.ts.map +1 -1
  43. package/dist/path-utils.js +89 -2
  44. package/dist/queue-handler.d.ts +3 -3
  45. package/dist/queue-handler.d.ts.map +1 -1
  46. package/dist/router.d.ts +2 -2
  47. package/dist/router.d.ts.map +1 -1
  48. package/dist/schemas/index.js +7 -4
  49. package/dist/schemas/registry.js +387 -0
  50. package/dist/schemas/types.js +1 -0
  51. package/dist/schemas/validators.d.ts.map +1 -1
  52. package/dist/schemas/validators.js +290 -0
  53. package/dist/service-bindings.d.ts +65 -0
  54. package/dist/service-bindings.d.ts.map +1 -1
  55. package/dist/service-bindings.js +216 -1
  56. package/dist/session.d.ts.map +1 -1
  57. package/dist/session.js +97 -2
  58. package/dist/sse.d.ts +12 -3
  59. package/dist/sse.d.ts.map +1 -1
  60. package/dist/sse.js +70 -7
  61. package/dist/stores/config-store.d.ts.map +1 -1
  62. package/dist/stores/config-store.js +129 -6
  63. package/dist/stores/service-store.d.ts.map +1 -1
  64. package/dist/stores/service-store.js +96 -25
  65. package/dist/types.d.ts +66 -1
  66. package/dist/types.d.ts.map +1 -1
  67. package/dist/types.js +11 -0
  68. package/dist/wizard/engine.js +501 -0
  69. package/dist/wizard/index.js +2 -1
  70. package/dist/wizard/persistence.js +31 -0
  71. package/dist/wizard/presets.js +199 -0
  72. package/dist/wizard/provisioner.js +1 -0
  73. package/dist/wizard/types.js +1 -0
  74. package/package.json +38 -12
package/dist/index.js CHANGED
@@ -15,6 +15,69 @@ var __export = (target, all) => {
15
15
  };
16
16
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
17
 
18
+ // src/operator-transport.ts
19
+ function stripTrailingSlashes(url) {
20
+ return url.replace(/\/+$/, "");
21
+ }
22
+ function parseTransport(raw) {
23
+ if (!raw)
24
+ return null;
25
+ const v = raw.trim().toLowerCase();
26
+ if (VALID_TRANSPORTS.has(v)) {
27
+ return v;
28
+ }
29
+ return null;
30
+ }
31
+ function resolveOperatorTransportProfile(env = process.env, options = {}) {
32
+ const accessClientId = env.CF_ACCESS_CLIENT_ID?.trim() ?? "";
33
+ const accessClientSecret = env.CF_ACCESS_CLIENT_SECRET?.trim() ?? "";
34
+ const hasAccess = Boolean(accessClientId && accessClientSecret);
35
+ const explicit = parseTransport(env.HOOX_TRANSPORT) ?? parseTransport(options.configTransport);
36
+ const transport = explicit ?? (hasAccess ? "access" : "public");
37
+ const apiFromEnv = env.HOOX_API_URL?.trim();
38
+ const apiFromConfig = options.configApiUrl?.trim();
39
+ const tokenFromEnv = env.HOOX_API_TOKEN?.trim();
40
+ const tokenFromConfig = options.configApiToken?.trim();
41
+ return {
42
+ transport,
43
+ apiBase: stripTrailingSlashes(apiFromEnv || apiFromConfig || DEFAULT_API_BASE),
44
+ bearerToken: tokenFromEnv || tokenFromConfig || "",
45
+ accessClientId,
46
+ accessClientSecret
47
+ };
48
+ }
49
+ function buildOperatorAuthHeaders(profile) {
50
+ const headers = {};
51
+ if (profile.bearerToken) {
52
+ headers.Authorization = `Bearer ${profile.bearerToken}`;
53
+ }
54
+ const useAccessHeaders = profile.transport === "access" || Boolean(profile.accessClientId) && Boolean(profile.accessClientSecret);
55
+ if (useAccessHeaders && profile.accessClientId && profile.accessClientSecret) {
56
+ headers["CF-Access-Client-Id"] = profile.accessClientId;
57
+ headers["CF-Access-Client-Secret"] = profile.accessClientSecret;
58
+ }
59
+ return headers;
60
+ }
61
+ function hasOperatorClientCredentials(profile) {
62
+ if (profile.bearerToken)
63
+ return true;
64
+ return Boolean(profile.accessClientId && profile.accessClientSecret);
65
+ }
66
+ function operatorUrl(profile, path) {
67
+ const base = profile.apiBase.replace(/\/+$/, "");
68
+ const p = path.startsWith("/") ? path : `/${path}`;
69
+ return `${base}${p}`;
70
+ }
71
+ var DEFAULT_API_BASE = "http://localhost:8787", VALID_TRANSPORTS;
72
+ var init_operator_transport = __esm(() => {
73
+ VALID_TRANSPORTS = new Set([
74
+ "public",
75
+ "access",
76
+ "mtls",
77
+ "tunnel"
78
+ ]);
79
+ });
80
+
18
81
  // src/api-client.ts
19
82
  var exports_api_client = {};
20
83
  __export(exports_api_client, {
@@ -40,20 +103,25 @@ function isNetworkError(error) {
40
103
  function sleep(ms) {
41
104
  return new Promise((resolve) => setTimeout(resolve, ms));
42
105
  }
106
+ function activeProfile(override) {
107
+ return override ?? resolveOperatorTransportProfile();
108
+ }
43
109
  async function hooxFetch(path, options) {
110
+ const profile = options?.transport ?? (options?.transportEnv ? resolveOperatorTransportProfile(options.transportEnv) : activeProfile());
111
+ const { transport: _t, transportEnv: _e, ...fetchInit } = options ?? {};
44
112
  let lastError;
45
113
  for (let attempt = 0;attempt <= MAX_RETRIES; attempt++) {
46
114
  const controller = new AbortController;
47
115
  const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
48
116
  try {
49
- const url = `${API_BASE}${path}`;
117
+ const url = operatorUrl(profile, path);
50
118
  const headers = {
51
119
  "Content-Type": "application/json",
52
- ...API_TOKEN ? { Authorization: `Bearer ${API_TOKEN}` } : {},
53
- ...options?.headers
120
+ ...buildOperatorAuthHeaders(profile),
121
+ ...fetchInit.headers
54
122
  };
55
123
  const response = await fetch(url, {
56
- ...options,
124
+ ...fetchInit,
57
125
  headers,
58
126
  signal: controller.signal
59
127
  });
@@ -101,10 +169,9 @@ async function hooxFetch(path, options) {
101
169
  retryable: false
102
170
  });
103
171
  }
104
- var API_BASE, API_TOKEN, FETCH_TIMEOUT_MS = 5000, MAX_RETRIES = 3, BASE_RETRY_DELAY_MS = 1000, MAX_RETRY_DELAY_MS = 16000, WorkerAPIError;
172
+ var FETCH_TIMEOUT_MS = 5000, MAX_RETRIES = 3, BASE_RETRY_DELAY_MS = 1000, MAX_RETRY_DELAY_MS = 16000, WorkerAPIError;
105
173
  var init_api_client = __esm(() => {
106
- API_BASE = process.env.HOOX_API_URL || "http://localhost:8787";
107
- API_TOKEN = process.env.HOOX_API_TOKEN || "";
174
+ init_operator_transport();
108
175
  WorkerAPIError = class WorkerAPIError extends Error {
109
176
  status;
110
177
  retryable;
@@ -122,17 +189,18 @@ var exports_sse = {};
122
189
  __export(exports_sse, {
123
190
  subscribeSSE: () => subscribeSSE
124
191
  });
125
- function subscribeSSE(path, callback, onStatus) {
192
+ function subscribeSSE(path, callback, onStatus, options) {
126
193
  let aborted = false;
194
+ const profile = options?.transport ?? (options?.transportEnv ? resolveOperatorTransportProfile(options.transportEnv) : resolveOperatorTransportProfile());
127
195
  const run = async () => {
128
196
  let attempt = 0;
129
197
  while (!aborted && attempt < MAX_RECONNECT_ATTEMPTS) {
130
198
  try {
131
- const url = `${API_BASE2}${path}`;
199
+ const url = operatorUrl(profile, path);
132
200
  const response = await fetch(url, {
133
201
  headers: {
134
202
  Accept: "text/event-stream",
135
- ...API_TOKEN2 ? { Authorization: `Bearer ${API_TOKEN2}` } : {}
203
+ ...buildOperatorAuthHeaders(profile)
136
204
  }
137
205
  });
138
206
  if (!response.ok || !response.body) {
@@ -161,7 +229,7 @@ function subscribeSSE(path, callback, onStatus) {
161
229
  }
162
230
  }
163
231
  }
164
- } catch (_error) {
232
+ } catch {
165
233
  if (aborted)
166
234
  break;
167
235
  onStatus?.("reconnecting");
@@ -181,21 +249,31 @@ function subscribeSSE(path, callback, onStatus) {
181
249
  }
182
250
  };
183
251
  }
184
- var API_BASE2, API_TOKEN2, RECONNECT_BASE_MS = 1000, RECONNECT_MAX_MS = 16000, MAX_RECONNECT_ATTEMPTS = 10;
252
+ var RECONNECT_BASE_MS = 1000, RECONNECT_MAX_MS = 16000, MAX_RECONNECT_ATTEMPTS = 10;
185
253
  var init_sse = __esm(() => {
186
- API_BASE2 = process.env.HOOX_API_URL || "http://localhost:8787";
187
- API_TOKEN2 = process.env.HOOX_API_TOKEN || "";
254
+ init_operator_transport();
188
255
  });
189
256
 
190
257
  // src/analytics.ts
191
- async function trackAnalytics(env, endpoint, body) {
258
+ async function trackAnalytics(env, endpoint, body, options) {
192
259
  if (!env.ANALYTICS_SERVICE)
193
260
  return;
194
261
  try {
262
+ const payload = {
263
+ ...body,
264
+ ...options?.indexes ? { indexes: [...options.indexes] } : {}
265
+ };
266
+ const headers = {
267
+ "Content-Type": "application/json"
268
+ };
269
+ const internalKey = env.INTERNAL_KEY_BINDING;
270
+ if (internalKey) {
271
+ headers["X-Internal-Auth-Key"] = internalKey;
272
+ }
195
273
  await env.ANALYTICS_SERVICE.fetch(new Request(`http://localhost${endpoint}`, {
196
274
  method: "POST",
197
- headers: { "Content-Type": "application/json" },
198
- body: JSON.stringify(body)
275
+ headers,
276
+ body: JSON.stringify(payload)
199
277
  }));
200
278
  } catch (e) {
201
279
  console.error("Analytics tracking failed:", e);
@@ -204,35 +282,81 @@ async function trackAnalytics(env, endpoint, body) {
204
282
 
205
283
  // src/colors.ts
206
284
  var Colors = {
207
- background: "#0D1117",
208
- foreground: "#EEEEEE",
209
- card: "#1C1C1F",
210
- border: "#484848",
211
- muted: "#A0A0A0",
212
- "muted-foreground": "#6E6E6E",
213
- dim: "#3B3B3D",
214
- accent: "#E8780A",
215
- "accent-dim": "#B85E08",
216
- success: "#00FF88",
217
- warning: "#FFAA00",
218
- error: "#FF4444",
219
- info: "#4488FF",
220
- text: "#EEEEEE",
221
- "text-muted": "#A0A0A0",
222
- panel: "#1C1C1F",
223
- divider: "#484848",
224
- highlight: "#E8780A"
285
+ background: "#050508",
286
+ foreground: "#E8E8F0",
287
+ card: "#0A0A0F",
288
+ border: "#232330",
289
+ muted: "#8B8B9E",
290
+ "muted-foreground": "#6E6E84",
291
+ dim: "#3C3C50",
292
+ accent: "#818CF8",
293
+ "accent-dim": "#6366F1",
294
+ success: "#34D399",
295
+ warning: "#FBBF24",
296
+ error: "#FB7185",
297
+ info: "#38BDF8",
298
+ text: "#E8E8F0",
299
+ "text-muted": "#8B8B9E",
300
+ panel: "#0A0A0F",
301
+ divider: "#232330",
302
+ highlight: "#22D3EE",
303
+ backdrop: "#000000"
304
+ };
305
+ var CoolBracketPalette = [
306
+ "#22D3EE",
307
+ "#38BDF8",
308
+ "#60A5FA",
309
+ "#818CF8",
310
+ "#A78BFA",
311
+ "#C084FC",
312
+ "#E879F9",
313
+ "#A78BFA",
314
+ "#818CF8",
315
+ "#38BDF8"
316
+ ];
317
+ var ConnectionStatusColor = {
318
+ connected: Colors.success,
319
+ polling: Colors.highlight,
320
+ reconnecting: Colors.warning,
321
+ offline: Colors.error
322
+ };
323
+ var WorkerStatusColor = {
324
+ operational: Colors.success,
325
+ degraded: Colors.warning,
326
+ down: Colors.error
327
+ };
328
+ var LogLevelColor = {
329
+ error: Colors.error,
330
+ warn: Colors.warning,
331
+ info: Colors.foreground,
332
+ debug: Colors.muted
333
+ };
334
+ var AlertSeverityColor = {
335
+ info: Colors.info,
336
+ warning: Colors.warning,
337
+ error: Colors.error,
338
+ critical: Colors.error
225
339
  };
226
340
 
227
341
  // src/config.ts
228
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
342
+ import {
343
+ readFileSync,
344
+ writeFileSync,
345
+ existsSync,
346
+ mkdirSync,
347
+ chmodSync,
348
+ statSync
349
+ } from "fs";
229
350
  import { join } from "path";
230
351
  import { homedir } from "os";
231
352
  var HOX_DIR = join(homedir(), ".hoox");
232
353
  var CONFIG_PATH = join(HOX_DIR, "config.json");
354
+ var HOOX_DIR_MODE = 448;
355
+ var HOOX_CONFIG_FILE_MODE = 384;
233
356
  var DEFAULT_CONFIG = {
234
357
  apiUrl: "http://localhost:8787",
235
358
  apiToken: "",
359
+ transport: "public",
236
360
  refreshIntervalMs: 500,
237
361
  theme: "dark",
238
362
  activeExchanges: ["binance", "bybit", "mexc"],
@@ -265,12 +389,41 @@ function readConfigSync() {
265
389
  async function readConfig() {
266
390
  return readConfigSync();
267
391
  }
392
+ function ensureHooxDirSecure(dir = HOX_DIR) {
393
+ if (!existsSync(dir)) {
394
+ mkdirSync(dir, { recursive: true, mode: HOOX_DIR_MODE });
395
+ }
396
+ try {
397
+ chmodSync(dir, HOOX_DIR_MODE);
398
+ } catch {}
399
+ }
400
+ function writeSecureFileSync(filePath, contents, mode = HOOX_CONFIG_FILE_MODE) {
401
+ writeFileSync(filePath, contents, { encoding: "utf-8", mode });
402
+ try {
403
+ chmodSync(filePath, mode);
404
+ } catch {}
405
+ }
406
+ function isUnixModeGroupOrWorldAccessible(mode) {
407
+ return (mode & 63) !== 0;
408
+ }
409
+ function isConfigWorldOrGroupReadable(filePath = CONFIG_PATH) {
410
+ try {
411
+ if (!existsSync(filePath))
412
+ return false;
413
+ return isUnixModeGroupOrWorldAccessible(statSync(filePath).mode & 511);
414
+ } catch {
415
+ return false;
416
+ }
417
+ }
418
+ function formatConfigPermissionWarning(filePath = CONFIG_PATH) {
419
+ if (!isConfigWorldOrGroupReadable(filePath))
420
+ return null;
421
+ return `Warning: ${filePath} is group/world-accessible. ` + `Run: chmod 600 ${filePath} (and chmod 700 on the parent dir). ` + `This file may contain apiToken / operator secrets.`;
422
+ }
268
423
  function writeConfigSync(config) {
269
424
  try {
270
- if (!existsSync(HOX_DIR)) {
271
- mkdirSync(HOX_DIR, { recursive: true });
272
- }
273
- writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
425
+ ensureHooxDirSecure(HOX_DIR);
426
+ writeSecureFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), HOOX_CONFIG_FILE_MODE);
274
427
  } catch (err) {
275
428
  console.error("Failed to write config:", err);
276
429
  }
@@ -278,6 +431,12 @@ function writeConfigSync(config) {
278
431
  async function writeConfig(config) {
279
432
  writeConfigSync(config);
280
433
  }
434
+ var VALID_TRANSPORTS2 = new Set([
435
+ "public",
436
+ "access",
437
+ "mtls",
438
+ "tunnel"
439
+ ]);
281
440
  function validateConfig(config) {
282
441
  const errors = [];
283
442
  if (config.apiUrl && !config.apiUrl.startsWith("http")) {
@@ -289,6 +448,9 @@ function validateConfig(config) {
289
448
  if (config.theme && !["dark", "light"].includes(config.theme)) {
290
449
  errors.push('theme must be "dark" or "light"');
291
450
  }
451
+ if (config.transport !== undefined && !VALID_TRANSPORTS2.has(config.transport)) {
452
+ errors.push('transport must be "public" | "access" | "mtls" | "tunnel"');
453
+ }
292
454
  return errors;
293
455
  }
294
456
 
@@ -598,6 +760,7 @@ class D1Repository {
598
760
  }
599
761
  }
600
762
  }
763
+
601
764
  // src/d1/schemas.ts
602
765
  import { z } from "zod";
603
766
  var TradeRecordSchema = z.object({
@@ -1048,6 +1211,16 @@ var WebhookPayloadSchema = z2.object({
1048
1211
  orderType: z2.string().optional(),
1049
1212
  leverage: z2.number().int().positive().optional()
1050
1213
  }).strict();
1214
+ var TradeQueueMessageSchema = z2.object({
1215
+ requestId: z2.string().min(1).max(128),
1216
+ exchange: z2.string().min(1).max(64),
1217
+ action: TradeActionSchema,
1218
+ symbol: z2.string().min(1).max(32),
1219
+ quantity: z2.number().positive().finite(),
1220
+ price: z2.number().positive().finite().optional(),
1221
+ leverage: z2.number().int().positive().max(125).optional(),
1222
+ queuedAt: z2.string().min(1).max(64)
1223
+ }).strict();
1051
1224
  var TradeSignalSchema = z2.object({
1052
1225
  id: z2.number().int().positive().optional(),
1053
1226
  source: z2.string().min(1),
@@ -1189,7 +1362,209 @@ function createQueueHandler(options) {
1189
1362
  };
1190
1363
  }
1191
1364
 
1365
+ // src/middleware/auth.ts
1366
+ function timingSafeEqual(a, b) {
1367
+ const encoder = new TextEncoder;
1368
+ const aBuf = encoder.encode(a);
1369
+ const bBuf = encoder.encode(b);
1370
+ const len = Math.max(aBuf.length, bBuf.length);
1371
+ let result = aBuf.length === bBuf.length ? 0 : 1;
1372
+ for (let i = 0;i < len; i++) {
1373
+ const av = i < aBuf.length ? aBuf[i] : 0;
1374
+ const bv = i < bBuf.length ? bBuf[i] : 0;
1375
+ result |= av ^ bv;
1376
+ }
1377
+ return result === 0;
1378
+ }
1379
+ function resolveOperatorApiKey(env) {
1380
+ const preferred = env.OPERATOR_API_KEY;
1381
+ if (typeof preferred === "string" && preferred.length > 0) {
1382
+ return preferred;
1383
+ }
1384
+ const legacy = env.INTERNAL_API_KEY;
1385
+ if (typeof legacy === "string" && legacy.length > 0) {
1386
+ return legacy;
1387
+ }
1388
+ return;
1389
+ }
1390
+ async function requireAuth(request, env) {
1391
+ return requireOperatorAuth(request, env);
1392
+ }
1393
+ async function requireOperatorAuth(request, env) {
1394
+ const apiKey = resolveOperatorApiKey(env);
1395
+ if (!apiKey) {
1396
+ return new Response(JSON.stringify({
1397
+ error: "Operator API key not configured",
1398
+ hint: "Set OPERATOR_API_KEY (preferred) or INTERNAL_API_KEY on the Worker"
1399
+ }), { status: 401, headers: { "Content-Type": "application/json" } });
1400
+ }
1401
+ const authHeader = request.headers.get("Authorization");
1402
+ const expectedHeader = `Bearer ${apiKey}`;
1403
+ if (!authHeader || !timingSafeEqual(authHeader, expectedHeader)) {
1404
+ return new Response(JSON.stringify({ error: "Unauthorized" }), {
1405
+ status: 401,
1406
+ headers: { "Content-Type": "application/json" }
1407
+ });
1408
+ }
1409
+ return null;
1410
+ }
1411
+ function createOperatorAuthMiddleware() {
1412
+ return async (request, env, _ctx) => {
1413
+ const denied = await requireOperatorAuth(request, env);
1414
+ if (denied)
1415
+ return denied;
1416
+ return;
1417
+ };
1418
+ }
1419
+ function normalizeKeyNames(keyName) {
1420
+ if (typeof keyName === "string") {
1421
+ return [keyName];
1422
+ }
1423
+ return [...keyName];
1424
+ }
1425
+ function collectInternalAuthKeys(env, keyNames) {
1426
+ const keys = [];
1427
+ for (const name of normalizeKeyNames(keyNames)) {
1428
+ const value = env[name];
1429
+ if (typeof value === "string" && value.length > 0) {
1430
+ keys.push(value);
1431
+ }
1432
+ }
1433
+ return keys;
1434
+ }
1435
+ function matchesAnyInternalAuthKey(providedKey, expectedKeys) {
1436
+ for (const expected of expectedKeys) {
1437
+ if (timingSafeEqual(providedKey, expected)) {
1438
+ return true;
1439
+ }
1440
+ }
1441
+ return false;
1442
+ }
1443
+ function internalAuthNotConfiguredResponse(keyNames) {
1444
+ const label = normalizeKeyNames(keyNames).join(" | ");
1445
+ return new Response(JSON.stringify({
1446
+ success: false,
1447
+ error: `Internal auth key(s) not configured: ${label}`
1448
+ }), { status: 401, headers: { "Content-Type": "application/json" } });
1449
+ }
1450
+ function requireInternalAuth(request, env, keyName = "INTERNAL_KEY_BINDING") {
1451
+ const expectedKeys = collectInternalAuthKeys(env, keyName);
1452
+ if (expectedKeys.length === 0) {
1453
+ return internalAuthNotConfiguredResponse(keyName);
1454
+ }
1455
+ const providedKey = request.headers.get("X-Internal-Auth-Key");
1456
+ if (!providedKey || !matchesAnyInternalAuthKey(providedKey, expectedKeys)) {
1457
+ return new Response(JSON.stringify({ success: false, error: "Unauthorized" }), { status: 401, headers: { "Content-Type": "application/json" } });
1458
+ }
1459
+ return null;
1460
+ }
1461
+ function createInternalAuthMiddleware(keyName = "INTERNAL_KEY_BINDING") {
1462
+ return async (request, env, _ctx) => {
1463
+ const expectedKeys = collectInternalAuthKeys(env, keyName);
1464
+ if (expectedKeys.length === 0) {
1465
+ return internalAuthNotConfiguredResponse(keyName);
1466
+ }
1467
+ const providedKey = request.headers.get("X-Internal-Auth-Key");
1468
+ if (!providedKey || !matchesAnyInternalAuthKey(providedKey, expectedKeys)) {
1469
+ return new Response(JSON.stringify({ success: false, error: "Unauthorized" }), { status: 401, headers: { "Content-Type": "application/json" } });
1470
+ }
1471
+ return;
1472
+ };
1473
+ }
1474
+ function checkInternalAuth(request, env, keyName = "INTERNAL_KEY_BINDING") {
1475
+ const expectedKeys = collectInternalAuthKeys(env, keyName);
1476
+ if (expectedKeys.length === 0) {
1477
+ return {
1478
+ authorized: false,
1479
+ error: `${normalizeKeyNames(keyName).join(" | ")} not configured`
1480
+ };
1481
+ }
1482
+ const providedKey = request.headers.get("X-Internal-Auth-Key");
1483
+ if (!providedKey || !matchesAnyInternalAuthKey(providedKey, expectedKeys)) {
1484
+ return { authorized: false, error: "Unauthorized" };
1485
+ }
1486
+ return { authorized: true };
1487
+ }
1488
+
1192
1489
  // src/service-bindings.ts
1490
+ var InternalAuthKeyFields = {
1491
+ DEFAULT: "INTERNAL_KEY_BINDING",
1492
+ D1_READ: "D1_READ_KEY_BINDING",
1493
+ D1_WRITE: "D1_WRITE_KEY_BINDING",
1494
+ TRADE_EXECUTE: "TRADE_EXECUTE_KEY_BINDING",
1495
+ TRADE_READ: "TRADE_READ_KEY_BINDING",
1496
+ TELEGRAM_ALERT: "TELEGRAM_INTERNAL_KEY_BINDING",
1497
+ WALLET_EXECUTE: "WALLET_EXECUTE_KEY_BINDING",
1498
+ AGENT: "AGENT_INTERNAL_KEY",
1499
+ D1_INTERNAL_ALIAS: "D1_INTERNAL_KEY",
1500
+ TRADE_INTERNAL_ALIAS: "TRADE_INTERNAL_KEY",
1501
+ TELEGRAM_INTERNAL_ALIAS: "TELEGRAM_INTERNAL_KEY"
1502
+ };
1503
+ var D1_READ_AUTH_KEY_FIELDS = [
1504
+ InternalAuthKeyFields.D1_READ,
1505
+ InternalAuthKeyFields.DEFAULT
1506
+ ];
1507
+ var D1_WRITE_AUTH_KEY_FIELDS = [
1508
+ InternalAuthKeyFields.D1_WRITE,
1509
+ InternalAuthKeyFields.DEFAULT
1510
+ ];
1511
+ var TRADE_EXECUTE_AUTH_KEY_FIELDS = [
1512
+ InternalAuthKeyFields.TRADE_EXECUTE,
1513
+ InternalAuthKeyFields.DEFAULT,
1514
+ InternalAuthKeyFields.AGENT
1515
+ ];
1516
+ var TRADE_READ_AUTH_KEY_FIELDS = [
1517
+ InternalAuthKeyFields.TRADE_READ,
1518
+ InternalAuthKeyFields.DEFAULT
1519
+ ];
1520
+ var TELEGRAM_ALERT_AUTH_KEY_FIELDS = [
1521
+ InternalAuthKeyFields.TELEGRAM_ALERT,
1522
+ InternalAuthKeyFields.DEFAULT
1523
+ ];
1524
+ var WALLET_EXECUTE_AUTH_KEY_FIELDS = [
1525
+ InternalAuthKeyFields.WALLET_EXECUTE,
1526
+ InternalAuthKeyFields.DEFAULT
1527
+ ];
1528
+ var DASHBOARD_D1_READ_AUTH_KEY_FIELDS = [
1529
+ InternalAuthKeyFields.D1_READ,
1530
+ InternalAuthKeyFields.D1_INTERNAL_ALIAS,
1531
+ InternalAuthKeyFields.DEFAULT
1532
+ ];
1533
+ var DASHBOARD_TRADE_EXECUTE_AUTH_KEY_FIELDS = [
1534
+ InternalAuthKeyFields.TRADE_EXECUTE,
1535
+ InternalAuthKeyFields.TRADE_INTERNAL_ALIAS,
1536
+ InternalAuthKeyFields.DEFAULT
1537
+ ];
1538
+ var DASHBOARD_TELEGRAM_ALERT_AUTH_KEY_FIELDS = [
1539
+ InternalAuthKeyFields.TELEGRAM_ALERT,
1540
+ InternalAuthKeyFields.TELEGRAM_INTERNAL_ALIAS,
1541
+ InternalAuthKeyFields.DEFAULT
1542
+ ];
1543
+ function resolveInternalAuthKey(env, keyFields) {
1544
+ return collectInternalAuthKeys(env, keyFields)[0];
1545
+ }
1546
+
1547
+ class ServiceAuthError extends Error {
1548
+ constructor(message) {
1549
+ super(message);
1550
+ this.name = "ServiceAuthError";
1551
+ }
1552
+ }
1553
+ function authenticatedServiceFetch(binding, env, path, body, options) {
1554
+ const key = options?.internalKey ?? (options?.internalKeyFields ? resolveInternalAuthKey(env, options.internalKeyFields) : env.INTERNAL_KEY_BINDING);
1555
+ if (!key) {
1556
+ const label = options?.internalKeyFields ? Array.isArray(options.internalKeyFields) ? options.internalKeyFields.join(" | ") : options.internalKeyFields : "INTERNAL_KEY_BINDING";
1557
+ return Promise.reject(new ServiceAuthError(`${label} not configured`));
1558
+ }
1559
+ return serviceFetch(binding, path, body, {
1560
+ method: options?.method,
1561
+ timeout: options?.timeout,
1562
+ headers: {
1563
+ ...options?.headers,
1564
+ "X-Internal-Auth-Key": key
1565
+ }
1566
+ });
1567
+ }
1193
1568
  function serviceFetch(binding, path, body, options) {
1194
1569
  const timeout = options?.timeout ?? 30000;
1195
1570
  return binding.fetch(`http://internal${path}`, {
@@ -1302,13 +1677,18 @@ function createMockEnv(overrides) {
1302
1677
  }
1303
1678
 
1304
1679
  // src/path-utils.ts
1680
+ import { existsSync as existsSync2 } from "fs";
1305
1681
  import { homedir as homedir2 } from "os";
1306
- import { join as join2, resolve } from "path";
1682
+ import { dirname, join as join2, resolve } from "path";
1307
1683
  function createHooxPath(path) {
1308
1684
  return path;
1309
1685
  }
1310
1686
  function getHooxHome() {
1311
1687
  try {
1688
+ const override = process.env.HOOX_HOME?.trim();
1689
+ if (override) {
1690
+ return createHooxPath(resolve(override));
1691
+ }
1312
1692
  const home = homedir2();
1313
1693
  if (!home || home.length === 0) {
1314
1694
  return createHooxPath(resolve(process.cwd(), ".hoox"));
@@ -1318,6 +1698,84 @@ function getHooxHome() {
1318
1698
  return createHooxPath(resolve(process.cwd(), ".hoox"));
1319
1699
  }
1320
1700
  }
1701
+ function isHooxSetupRoot(dir) {
1702
+ if (!dir)
1703
+ return false;
1704
+ try {
1705
+ const root = resolve(dir);
1706
+ return existsSync2(join2(root, "wrangler.jsonc")) && existsSync2(join2(root, "packages", "cli", "package.json"));
1707
+ } catch {
1708
+ return false;
1709
+ }
1710
+ }
1711
+ function findHooxSetupRoot(startDir = process.cwd()) {
1712
+ let dir = resolve(startDir);
1713
+ for (;; ) {
1714
+ if (isHooxSetupRoot(dir))
1715
+ return dir;
1716
+ const parent = dirname(dir);
1717
+ if (parent === dir)
1718
+ return null;
1719
+ dir = parent;
1720
+ }
1721
+ }
1722
+ function resolveHooxRuntimeRoot(options) {
1723
+ const env = options?.env ?? process.env;
1724
+ const cwd = resolve(options?.cwd ?? process.cwd());
1725
+ const globalRepo = getHooxRepoPath();
1726
+ const envRepo = env.HOOX_REPO?.trim();
1727
+ if (envRepo) {
1728
+ const resolved = resolve(envRepo);
1729
+ if (isHooxSetupRoot(resolved)) {
1730
+ return {
1731
+ root: resolved,
1732
+ source: "env",
1733
+ checked: {
1734
+ env: resolved,
1735
+ cwd: findHooxSetupRoot(cwd),
1736
+ global: globalRepo
1737
+ }
1738
+ };
1739
+ }
1740
+ return {
1741
+ root: null,
1742
+ source: "env",
1743
+ checked: {
1744
+ env: resolved,
1745
+ cwd: findHooxSetupRoot(cwd),
1746
+ global: globalRepo
1747
+ }
1748
+ };
1749
+ }
1750
+ const local = findHooxSetupRoot(cwd);
1751
+ if (local) {
1752
+ return {
1753
+ root: local,
1754
+ source: "cwd",
1755
+ checked: { cwd: local, global: globalRepo }
1756
+ };
1757
+ }
1758
+ if (isHooxSetupRoot(globalRepo)) {
1759
+ return {
1760
+ root: globalRepo,
1761
+ source: "global",
1762
+ checked: { cwd: null, global: globalRepo }
1763
+ };
1764
+ }
1765
+ return {
1766
+ root: null,
1767
+ source: "none",
1768
+ checked: { cwd: null, global: globalRepo }
1769
+ };
1770
+ }
1771
+ function getTuiEntryCandidates(runtimeRoot) {
1772
+ const root = resolve(runtimeRoot);
1773
+ return [
1774
+ join2(root, "packages", "tui", "src", "main.tsx"),
1775
+ join2(root, "packages", "tui", "dist", "main.js"),
1776
+ join2(root, "packages", "tui", "src", "main.ts")
1777
+ ];
1778
+ }
1321
1779
  function resolveHooxPath(relativePath) {
1322
1780
  if (!relativePath || typeof relativePath !== "string") {
1323
1781
  throw new Error("relativePath must be a non-empty string");
@@ -1376,6 +1834,17 @@ function getHooxStatePath() {
1376
1834
  }
1377
1835
 
1378
1836
  // src/session.ts
1837
+ import { z as z3 } from "zod";
1838
+ var SessionStateFileSchema = z3.object({
1839
+ activeView: z3.string().optional(),
1840
+ sidebarExpanded: z3.boolean().optional(),
1841
+ windowSize: z3.object({
1842
+ cols: z3.number().int().positive(),
1843
+ rows: z3.number().int().positive()
1844
+ }).optional(),
1845
+ lastData: z3.number().nonnegative().optional(),
1846
+ savedAt: z3.string().optional()
1847
+ }).strict();
1379
1848
  var TUI_STATE_DIR = ".tui-state";
1380
1849
  var SESSION_FILE = (() => {
1381
1850
  try {
@@ -1413,7 +1882,8 @@ async function restoreSession() {
1413
1882
  if (!exists)
1414
1883
  return { ...DEFAULT_SESSION };
1415
1884
  const raw = await file.text();
1416
- const parsed = JSON.parse(raw);
1885
+ const result = SessionStateFileSchema.safeParse(JSON.parse(raw));
1886
+ const parsed = result.success ? result.data : {};
1417
1887
  return {
1418
1888
  activeView: validateViewId(parsed.activeView) ? parsed.activeView : DEFAULT_SESSION.activeView,
1419
1889
  sidebarExpanded: typeof parsed.sidebarExpanded === "boolean" ? parsed.sidebarExpanded : DEFAULT_SESSION.sidebarExpanded,
@@ -1601,7 +2071,7 @@ var BASE_WORKERS = {
1601
2071
  path: "workers/d1-worker",
1602
2072
  vars: { database_name: "hoox-db" }
1603
2073
  },
1604
- hoox: { enabled: true, path: "workers/hoox", vars: {} },
2074
+ hoox: { enabled: true, path: "workers/hoox-worker", vars: {} },
1605
2075
  "agent-worker": { enabled: true, path: "workers/agent-worker", vars: {} },
1606
2076
  "analytics-worker": {
1607
2077
  enabled: true,
@@ -1921,6 +2391,7 @@ class WizardEngine {
1921
2391
  }
1922
2392
  }
1923
2393
  }
2394
+
1924
2395
  // src/wizard/persistence.ts
1925
2396
  function serializeState(state) {
1926
2397
  return JSON.stringify(state, null, 2);
@@ -1951,7 +2422,7 @@ function deriveCalledBy(manifests) {
1951
2422
  var manifests = {
1952
2423
  hoox: {
1953
2424
  name: "hoox",
1954
- path: "workers/hoox",
2425
+ path: "workers/hoox-worker",
1955
2426
  vars: {
1956
2427
  WEBHOOK_API_KEY_BINDING: {
1957
2428
  type: "secret",
@@ -2295,6 +2766,7 @@ var manifests = {
2295
2766
  var CALLED_BY = deriveCalledBy(manifests);
2296
2767
  var WORKER_MANIFESTS = manifests;
2297
2768
  var WORKER_NAMES = Object.keys(manifests).sort();
2769
+
2298
2770
  // src/schemas/validators.ts
2299
2771
  import { parse } from "jsonc-parser";
2300
2772
  function validateWranglerJsonc(workerName, manifest, jsoncContent) {
@@ -2320,7 +2792,8 @@ function validateWranglerJsonc(workerName, manifest, jsoncContent) {
2320
2792
  });
2321
2793
  return errors;
2322
2794
  }
2323
- const declaredVars = parsed?.vars ?? {};
2795
+ const wrangler = parsed;
2796
+ const declaredVars = wrangler.vars ?? {};
2324
2797
  for (const [name, def] of Object.entries(manifest.vars)) {
2325
2798
  if (!(name in declaredVars)) {
2326
2799
  errors.push({
@@ -2341,7 +2814,7 @@ function validateWranglerJsonc(workerName, manifest, jsoncContent) {
2341
2814
  });
2342
2815
  }
2343
2816
  }
2344
- const declaredServices = parsed?.services ?? [];
2817
+ const declaredServices = wrangler.services ?? [];
2345
2818
  for (const expected of manifest.services) {
2346
2819
  const match = declaredServices.find((s) => s.binding === expected.binding && s.service === expected.service);
2347
2820
  if (!match) {
@@ -2397,7 +2870,8 @@ function validateRootSecrets(workerName, manifest, rootJsoncContent) {
2397
2870
  });
2398
2871
  return errors;
2399
2872
  }
2400
- const rootSecrets = parsed?.workers?.[workerName]?.secrets ?? [];
2873
+ const rootWrangler = parsed;
2874
+ const rootSecrets = rootWrangler.workers?.[workerName]?.secrets ?? [];
2401
2875
  const expectedSecrets = Object.entries(manifest.vars).filter(([_, def]) => def.type === "secret").map(([name]) => name);
2402
2876
  for (const expected of expectedSecrets) {
2403
2877
  if (!rootSecrets.includes(expected)) {
@@ -2647,7 +3121,7 @@ var useServiceStore = create2()(immer2((set, get) => ({
2647
3121
  fetchWorkers: async () => {
2648
3122
  const { hooxFetch: hooxFetch2 } = await Promise.resolve().then(() => (init_api_client(), exports_api_client));
2649
3123
  try {
2650
- const data = await hooxFetch2("/workers");
3124
+ const data = await hooxFetch2("/v1/workers");
2651
3125
  set((state) => {
2652
3126
  state.workers = data;
2653
3127
  state.lastUpdated = Date.now();
@@ -2672,20 +3146,26 @@ var useServiceStore = create2()(immer2((set, get) => ({
2672
3146
  acknowledged: false
2673
3147
  });
2674
3148
  set((state) => {
3149
+ state.lastError = msg;
3150
+ state.disconnectedAt = state.disconnectedAt ?? Date.now();
2675
3151
  if (state.connectionStatus === "connected" || state.connectionStatus === "polling") {
2676
3152
  state.connectionStatus = "reconnecting";
2677
3153
  state.retryCount = state.retryCount + 1;
2678
- state.lastError = msg;
2679
- state.disconnectedAt = state.disconnectedAt ?? Date.now();
2680
3154
  if (state.retryCount >= MAX_RETRIES2) {
2681
3155
  state.connectionStatus = "offline";
2682
3156
  state.lastError = `Connection lost after ${MAX_RETRIES2} retries`;
2683
3157
  } else {
2684
3158
  state.reconnectDelay = getBackoffDelay(state.retryCount);
2685
3159
  }
2686
- }
2687
- if (state.connectionStatus === "reconnecting") {
2688
- state.lastError = msg;
3160
+ } else if (state.connectionStatus === "reconnecting") {
3161
+ state.retryCount = state.retryCount + 1;
3162
+ if (state.retryCount >= MAX_RETRIES2) {
3163
+ state.connectionStatus = "offline";
3164
+ state.lastError = `Connection lost after ${MAX_RETRIES2} retries`;
3165
+ state.reconnectDelay = 0;
3166
+ } else {
3167
+ state.reconnectDelay = getBackoffDelay(state.retryCount);
3168
+ }
2689
3169
  }
2690
3170
  });
2691
3171
  }
@@ -2693,7 +3173,7 @@ var useServiceStore = create2()(immer2((set, get) => ({
2693
3173
  streamTrades: async () => {
2694
3174
  const { subscribeSSE: subscribeSSE2 } = await Promise.resolve().then(() => (init_sse(), exports_sse));
2695
3175
  try {
2696
- await subscribeSSE2("/trades/stream", (trade) => {
3176
+ await subscribeSSE2("/v1/trades/stream", (trade) => {
2697
3177
  get().pushTrade(trade);
2698
3178
  });
2699
3179
  } catch (error) {
@@ -2712,7 +3192,7 @@ var useServiceStore = create2()(immer2((set, get) => ({
2712
3192
  streamLogs: async () => {
2713
3193
  const { subscribeSSE: subscribeSSE2 } = await Promise.resolve().then(() => (init_sse(), exports_sse));
2714
3194
  try {
2715
- await subscribeSSE2("/logs/stream", (log) => {
3195
+ await subscribeSSE2("/v1/logs/stream", (log) => {
2716
3196
  get().pushLog(log);
2717
3197
  });
2718
3198
  } catch (error) {
@@ -2772,9 +3252,7 @@ var useServiceStore = create2()(immer2((set, get) => ({
2772
3252
  state.lastErrorDetails = null;
2773
3253
  state.reconnectDelay = 0;
2774
3254
  state.disconnectedAt = null;
2775
- if (state.connectionStatus === "reconnecting" || state.connectionStatus === "polling") {
2776
- state.connectionStatus = "connected";
2777
- }
3255
+ state.connectionStatus = "connected";
2778
3256
  }),
2779
3257
  handleConnectionFailure: (errorMessage) => set((state) => {
2780
3258
  state.lastError = errorMessage;
@@ -2837,6 +3315,7 @@ var useServiceStore = create2()(immer2((set, get) => ({
2837
3315
  import { create as create3 } from "zustand";
2838
3316
  import { immer as immer3 } from "zustand/middleware/immer";
2839
3317
  import { persist, createJSONStorage } from "zustand/middleware";
3318
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync2, existsSync as existsSync3 } from "fs";
2840
3319
  import { homedir as homedir3 } from "os";
2841
3320
  import { join as join3 } from "path";
2842
3321
  var bunConfigStorage = {
@@ -2853,11 +3332,17 @@ var bunConfigStorage = {
2853
3332
  },
2854
3333
  setItem: async (_name, value) => {
2855
3334
  const dir = join3(homedir3(), ".hoox");
3335
+ if (!existsSync3(dir)) {
3336
+ mkdirSync2(dir, { recursive: true, mode: HOOX_DIR_MODE });
3337
+ }
2856
3338
  try {
2857
- await Bun.file(dir).exists();
3339
+ chmodSync2(dir, HOOX_DIR_MODE);
2858
3340
  } catch {}
2859
3341
  const filePath = join3(dir, "config.json");
2860
3342
  await Bun.write(filePath, value);
3343
+ try {
3344
+ chmodSync2(filePath, HOOX_CONFIG_FILE_MODE);
3345
+ } catch {}
2861
3346
  },
2862
3347
  removeItem: async (_name) => {}
2863
3348
  };
@@ -2949,7 +3434,9 @@ var useConfigStore = create3()(persist(immer3((set) => ({
2949
3434
  // src/index.ts
2950
3435
  init_api_client();
2951
3436
  init_sse();
3437
+ init_operator_transport();
2952
3438
  export {
3439
+ writeSecureFileSync,
2953
3440
  writeConfigSync,
2954
3441
  writeConfig,
2955
3442
  validateWranglerJsonc,
@@ -2967,15 +3454,24 @@ export {
2967
3454
  serializeState,
2968
3455
  saveSession,
2969
3456
  restoreSession,
3457
+ resolveOperatorTransportProfile,
3458
+ resolveInternalAuthKey,
3459
+ resolveHooxRuntimeRoot,
2970
3460
  resolveHooxPath,
2971
3461
  resolveDependencies,
2972
3462
  readConfigSync,
2973
3463
  readConfig,
3464
+ operatorUrl,
2974
3465
  logKvTimestamp,
2975
3466
  isWithinHooxHome,
3467
+ isUnixModeGroupOrWorldAccessible,
3468
+ isHooxSetupRoot,
3469
+ isConfigWorldOrGroupReadable,
2976
3470
  hooxFetch,
2977
3471
  healthCheck,
2978
3472
  headersToObject,
3473
+ hasOperatorClientCredentials,
3474
+ getTuiEntryCandidates,
2979
3475
  getRelativeHooxPath,
2980
3476
  getHooxWranglerPath,
2981
3477
  getHooxStatePath,
@@ -2998,7 +3494,10 @@ export {
2998
3494
  formatDuration2 as formatDuration,
2999
3495
  formatCurrency,
3000
3496
  formatCpu,
3497
+ formatConfigPermissionWarning,
3001
3498
  formatCompactCurrency,
3499
+ findHooxSetupRoot,
3500
+ ensureHooxDirSecure,
3002
3501
  deserializeState,
3003
3502
  createSuccessResponse,
3004
3503
  createQueueHandler,
@@ -3017,6 +3516,9 @@ export {
3017
3516
  createJsonResponse,
3018
3517
  createErrorResponse,
3019
3518
  createCronHandler,
3519
+ buildOperatorAuthHeaders,
3520
+ authenticatedServiceFetch,
3521
+ WorkerStatusColor,
3020
3522
  WorkerAPIError,
3021
3523
  WizardEngine,
3022
3524
  WebhookPayloadSchema,
@@ -3024,23 +3526,41 @@ export {
3024
3526
  WORKER_MANIFESTS,
3025
3527
  WORKER_DEPENDENCIES,
3026
3528
  WIZARD_STATE_PATH,
3529
+ WALLET_EXECUTE_AUTH_KEY_FIELDS,
3027
3530
  TradeSignalSchema,
3531
+ TradeQueueMessageSchema,
3028
3532
  TradeActionSchema,
3533
+ TRADE_READ_AUTH_KEY_FIELDS,
3534
+ TRADE_EXECUTE_AUTH_KEY_FIELDS,
3029
3535
  TRADEMARK_NOTICE,
3030
3536
  TRADEMARKS,
3537
+ TELEGRAM_ALERT_AUTH_KEY_FIELDS,
3538
+ ServiceAuthError,
3031
3539
  PositionSchema,
3032
3540
  PRESETS,
3541
+ LogLevelColor,
3033
3542
  KVKeys,
3543
+ InternalAuthKeyFields,
3034
3544
  INTEGRATIONS,
3545
+ HOOX_DIR_MODE,
3546
+ HOOX_CONFIG_FILE_MODE,
3035
3547
  FULL_LEGAL_NOTICE,
3036
3548
  ExchangeRouter,
3037
3549
  Errors,
3038
3550
  DISCLAIMER_HEADER,
3039
3551
  DISCLAIMER,
3552
+ DASHBOARD_TRADE_EXECUTE_AUTH_KEY_FIELDS,
3553
+ DASHBOARD_TELEGRAM_ALERT_AUTH_KEY_FIELDS,
3554
+ DASHBOARD_D1_READ_AUTH_KEY_FIELDS,
3555
+ D1_WRITE_AUTH_KEY_FIELDS,
3556
+ D1_READ_AUTH_KEY_FIELDS,
3040
3557
  D1Repository,
3558
+ CoolBracketPalette,
3559
+ ConnectionStatusColor,
3041
3560
  Colors,
3042
3561
  COPYRIGHT,
3043
3562
  CALLED_BY,
3044
3563
  BaseExchangeClient2 as BaseExchangeClient,
3045
- BalanceSchema
3564
+ BalanceSchema,
3565
+ AlertSeverityColor
3046
3566
  };