@jango-blockchained/hoox-shared 1.0.8 → 1.0.9

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 (61) hide show
  1. package/dist/analytics.d.ts +0 -1
  2. package/dist/analytics.d.ts.map +1 -1
  3. package/dist/colors.d.ts +0 -1
  4. package/dist/colors.d.ts.map +1 -1
  5. package/dist/colors.js +0 -1
  6. package/dist/cron-handler.d.ts +34 -0
  7. package/dist/cron-handler.d.ts.map +1 -0
  8. package/dist/cron-handler.js +48 -0
  9. package/dist/d1/index.js +49 -6
  10. package/dist/d1/repository.d.ts +5 -0
  11. package/dist/d1/repository.d.ts.map +1 -1
  12. package/dist/errors.d.ts +1 -0
  13. package/dist/errors.d.ts.map +1 -1
  14. package/dist/errors.js +48 -5
  15. package/dist/exchanges/base-exchange-client.d.ts +68 -0
  16. package/dist/exchanges/base-exchange-client.d.ts.map +1 -0
  17. package/dist/exchanges/index.d.ts +3 -0
  18. package/dist/exchanges/index.d.ts.map +1 -0
  19. package/dist/exchanges/index.js +120 -0
  20. package/dist/exchanges/types.d.ts +19 -0
  21. package/dist/exchanges/types.d.ts.map +1 -0
  22. package/dist/health.js +48 -5
  23. package/dist/index.d.ts +11 -2
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +480 -15
  26. package/dist/kvUtils.js +48 -5
  27. package/dist/middleware/auth.d.ts +6 -3
  28. package/dist/middleware/auth.d.ts.map +1 -1
  29. package/dist/middleware/index.js +193 -18
  30. package/dist/middleware/logger.d.ts +2 -1
  31. package/dist/middleware/logger.d.ts.map +1 -1
  32. package/dist/middleware/rate-limit.d.ts +3 -1
  33. package/dist/middleware/rate-limit.d.ts.map +1 -1
  34. package/dist/path-utils.d.ts +147 -0
  35. package/dist/path-utils.d.ts.map +1 -0
  36. package/dist/path-utils.js +101 -0
  37. package/dist/queue-handler.d.ts +43 -0
  38. package/dist/queue-handler.d.ts.map +1 -0
  39. package/dist/queue-handler.js +46 -0
  40. package/dist/router.d.ts +1 -1
  41. package/dist/router.d.ts.map +1 -1
  42. package/dist/router.js +15 -8
  43. package/dist/service-bindings.d.ts +5 -28
  44. package/dist/service-bindings.d.ts.map +1 -1
  45. package/dist/service-bindings.js +3 -1
  46. package/dist/session.d.ts +9 -5
  47. package/dist/session.d.ts.map +1 -1
  48. package/dist/session.js +83 -3
  49. package/dist/sse.js +1 -1
  50. package/dist/stores/service-store.d.ts +50 -1
  51. package/dist/stores/service-store.d.ts.map +1 -1
  52. package/dist/stores/service-store.js +67 -3
  53. package/dist/test-utils.d.ts +132 -0
  54. package/dist/test-utils.d.ts.map +1 -0
  55. package/dist/test-utils.js +128 -0
  56. package/dist/types/router.d.ts +18 -8
  57. package/dist/types/router.d.ts.map +1 -1
  58. package/dist/types.d.ts +59 -5
  59. package/dist/types.d.ts.map +1 -1
  60. package/dist/wizard/engine.d.ts.map +1 -1
  61. package/package.json +11 -3
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -161,7 +161,7 @@ function subscribeSSE(path, callback, onStatus) {
161
161
  }
162
162
  }
163
163
  }
164
- } catch (error) {
164
+ } catch (_error) {
165
165
  if (aborted)
166
166
  break;
167
167
  onStatus?.("reconnecting");
@@ -219,7 +219,6 @@ var Colors = {
219
219
  info: "#4488FF",
220
220
  text: "#EEEEEE",
221
221
  "text-muted": "#A0A0A0",
222
- "text-dim": "#6E6E6E",
223
222
  panel: "#1C1C1F",
224
223
  divider: "#484848",
225
224
  highlight: "#E8780A"
@@ -293,6 +292,35 @@ function validateConfig(config) {
293
292
  return errors;
294
293
  }
295
294
 
295
+ // src/cron-handler.ts
296
+ function createCronHandler(options) {
297
+ const { name, handler, logger } = options;
298
+ return async (event, env, ctx) => {
299
+ const startTime = Date.now();
300
+ logger?.info(`${name} cron triggered`, {
301
+ cron: event.cron,
302
+ scheduledTime: event.scheduledTime
303
+ });
304
+ try {
305
+ await handler(event, env, ctx);
306
+ const durationMs = Date.now() - startTime;
307
+ logger?.info(`${name} cron handler completed successfully`, {
308
+ cron: event.cron,
309
+ durationMs
310
+ });
311
+ } catch (error) {
312
+ const durationMs = Date.now() - startTime;
313
+ const errorMsg = error instanceof Error ? error.message : String(error);
314
+ logger?.error(`${name} cron handler failed`, {
315
+ cron: event.cron,
316
+ durationMs,
317
+ error: errorMsg
318
+ });
319
+ throw error;
320
+ }
321
+ };
322
+ }
323
+
296
324
  // src/errors.ts
297
325
  function toError(err, fallback = "Unknown error") {
298
326
  if (err instanceof Error)
@@ -310,22 +338,65 @@ function toError(err, fallback = "Unknown error") {
310
338
  return fallback;
311
339
  }
312
340
  }
341
+ var SENSITIVE_FIELDS = new Set([
342
+ "password",
343
+ "token",
344
+ "secret",
345
+ "api_key",
346
+ "apiKey",
347
+ "authorization"
348
+ ]);
349
+ function hasSensitiveFields(obj) {
350
+ for (const key of Object.keys(obj)) {
351
+ if (SENSITIVE_FIELDS.has(key))
352
+ return true;
353
+ }
354
+ return false;
355
+ }
356
+ function hasErrorValues(obj) {
357
+ for (const v of Object.values(obj)) {
358
+ if (v instanceof Error)
359
+ return true;
360
+ }
361
+ return false;
362
+ }
313
363
  function sanitizeOutput(data) {
364
+ if (data === null || data === undefined)
365
+ return data;
366
+ const t = typeof data;
367
+ if (t === "string" || t === "number" || t === "boolean")
368
+ return data;
314
369
  if (data instanceof Error) {
315
370
  return { name: data.name, message: data.message };
316
371
  }
317
- if (data && typeof data === "object" && !Array.isArray(data)) {
372
+ if (Array.isArray(data)) {
373
+ return data.map(sanitizeOutput);
374
+ }
375
+ if (t === "object") {
376
+ const obj = data;
377
+ const keys = Object.keys(obj);
378
+ if (keys.length < 10) {
379
+ const sanitized2 = {};
380
+ for (const k of keys) {
381
+ if (k === "stack" || k === "cause")
382
+ continue;
383
+ const v = obj[k];
384
+ sanitized2[k] = v instanceof Error ? { name: v.name, message: v.message } : sanitizeOutput(v);
385
+ }
386
+ return sanitized2;
387
+ }
388
+ if (!hasSensitiveFields(obj) && !hasErrorValues(obj)) {
389
+ return obj;
390
+ }
318
391
  const sanitized = {};
319
- for (const [k, v] of Object.entries(data)) {
392
+ for (const k of keys) {
320
393
  if (k === "stack" || k === "cause")
321
394
  continue;
395
+ const v = obj[k];
322
396
  sanitized[k] = v instanceof Error ? { name: v.name, message: v.message } : sanitizeOutput(v);
323
397
  }
324
398
  return sanitized;
325
399
  }
326
- if (Array.isArray(data)) {
327
- return data.map(sanitizeOutput);
328
- }
329
400
  return data;
330
401
  }
331
402
  function createJsonResponse(data, status = 200) {
@@ -421,7 +492,7 @@ class D1Repository {
421
492
  async batch(statements) {
422
493
  try {
423
494
  const prepared = statements.map((s) => s.params && s.params.length > 0 ? this.db.prepare(s.query).bind(...s.params) : this.db.prepare(s.query));
424
- const results = await this.db.batch(...prepared);
495
+ const results = await this.db.batch(prepared);
425
496
  return results.map((r) => ({
426
497
  success: r.success,
427
498
  meta: r.meta ? { last_row_id: r.meta.last_row_id, changes: r.meta.changes } : undefined,
@@ -668,6 +739,106 @@ class BaseExchangeClient {
668
739
  }
669
740
  }
670
741
 
742
+ // src/exchanges/base-exchange-client.ts
743
+ class BaseExchangeClient2 {
744
+ apiKey;
745
+ apiSecret;
746
+ baseUrl;
747
+ isTestnet;
748
+ importedKeyPromise;
749
+ constructor(apiKey, apiSecret) {
750
+ if (!apiKey || !apiSecret) {
751
+ throw new Error(this.getErrorMessagePrefix() + "API key and secret are required.");
752
+ }
753
+ this.apiKey = apiKey;
754
+ this.apiSecret = apiSecret;
755
+ this.baseUrl = this.getDefaultBaseUrl();
756
+ this.isTestnet = false;
757
+ this.importedKeyPromise = this.importKey();
758
+ }
759
+ getErrorMessagePrefix() {
760
+ return "";
761
+ }
762
+ generateSignature(_params) {
763
+ throw new Error("generateSignature must be implemented by subclass");
764
+ }
765
+ buildHeaders(_method, _path, _params) {
766
+ return new Headers;
767
+ }
768
+ async importKey() {
769
+ const encoder = new TextEncoder;
770
+ return crypto.subtle.importKey("raw", encoder.encode(this.apiSecret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
771
+ }
772
+ async cryptoSign(data) {
773
+ const encoder = new TextEncoder;
774
+ const key = await this.importedKeyPromise;
775
+ const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(data));
776
+ return Array.from(new Uint8Array(signature)).map((b) => b.toString(16).padStart(2, "0")).join("");
777
+ }
778
+ async fetch(method, path, params) {
779
+ const url = `${this.baseUrl}${path}`;
780
+ const headers = this.buildHeaders(method, path, params);
781
+ const body = params ? new URLSearchParams(params).toString() : undefined;
782
+ const response = await fetch(url, {
783
+ method,
784
+ headers,
785
+ body: method !== "GET" ? body : undefined
786
+ });
787
+ if (!response.ok) {
788
+ const error = await response.text();
789
+ throw new Error(`Exchange API error: ${response.status} - ${error}`);
790
+ }
791
+ return response.json();
792
+ }
793
+ setLeverage(_symbol, _leverage) {
794
+ throw new Error("setLeverage must be implemented by subclass");
795
+ }
796
+ executeTrade(_params) {
797
+ throw new Error("executeTrade must be implemented by subclass");
798
+ }
799
+ getAccountInfo() {
800
+ throw new Error("getAccountInfo must be implemented by subclass");
801
+ }
802
+ getPositions(_symbol) {
803
+ throw new Error("getPositions must be implemented by subclass");
804
+ }
805
+ async openLong(symbol, quantity, price, orderType = "MARKET") {
806
+ return this.executeTrade({
807
+ symbol,
808
+ side: "BUY",
809
+ orderType,
810
+ quantity,
811
+ price
812
+ });
813
+ }
814
+ async openShort(symbol, quantity, price, orderType = "MARKET") {
815
+ return this.executeTrade({
816
+ symbol,
817
+ side: "SELL",
818
+ orderType,
819
+ quantity,
820
+ price
821
+ });
822
+ }
823
+ async closeLong(symbol, quantity) {
824
+ return this.executeTrade({
825
+ symbol,
826
+ side: "SELL",
827
+ orderType: "MARKET",
828
+ quantity,
829
+ reduceOnly: true
830
+ });
831
+ }
832
+ async closeShort(symbol, quantity) {
833
+ return this.executeTrade({
834
+ symbol,
835
+ side: "BUY",
836
+ orderType: "MARKET",
837
+ quantity,
838
+ reduceOnly: true
839
+ });
840
+ }
841
+ }
671
842
  // src/format-time.ts
672
843
  function formatRelativeTime(timestampMs, nowMs) {
673
844
  const now = nowMs ?? Date.now();
@@ -991,22 +1162,229 @@ function headersToObject(headers) {
991
1162
  return result;
992
1163
  }
993
1164
 
1165
+ // src/queue-handler.ts
1166
+ function createQueueHandler(options) {
1167
+ const { maxRetries, backoffDelays, onMessage, onRetry, onDLQ, logger } = options;
1168
+ return async (batch) => {
1169
+ for (const msg of batch.messages) {
1170
+ const attemptNumber = msg.attempts || 0;
1171
+ const logId = `[${msg.id}]`;
1172
+ try {
1173
+ logger?.info(`${logId} Processing message (attempt ${attemptNumber + 1})`);
1174
+ await onMessage(msg.body, attemptNumber);
1175
+ logger?.info(`${logId} Message processed successfully`);
1176
+ } catch (error) {
1177
+ const errorMsg = error instanceof Error ? error.message : String(error);
1178
+ if (attemptNumber < maxRetries) {
1179
+ const delaySeconds = backoffDelays[attemptNumber] ?? backoffDelays[backoffDelays.length - 1];
1180
+ logger?.info(`${logId} Retrying in ${delaySeconds}s (attempt ${attemptNumber + 2}/${maxRetries + 1})`);
1181
+ await onRetry?.(msg.body, attemptNumber, errorMsg, delaySeconds);
1182
+ msg.retry({ delaySeconds });
1183
+ } else {
1184
+ logger?.error(`${logId} Max retries exceeded (${maxRetries + 1} attempts), moving to DLQ`);
1185
+ await onDLQ?.(msg.body, attemptNumber, errorMsg);
1186
+ }
1187
+ }
1188
+ }
1189
+ };
1190
+ }
1191
+
994
1192
  // src/service-bindings.ts
995
1193
  function serviceFetch(binding, path, body, options) {
1194
+ const timeout = options?.timeout ?? 30000;
996
1195
  return binding.fetch(`http://internal${path}`, {
997
1196
  method: options?.method ?? "POST",
998
1197
  headers: {
999
1198
  "Content-Type": "application/json",
1000
1199
  ...options?.headers
1001
1200
  },
1002
- body: body != null ? JSON.stringify(body) : undefined
1201
+ body: body != null ? JSON.stringify(body) : undefined,
1202
+ signal: AbortSignal.timeout(timeout)
1003
1203
  });
1004
1204
  }
1005
1205
 
1006
- // src/session.ts
1206
+ // src/test-utils.ts
1207
+ function createMockKV(overrides) {
1208
+ return {
1209
+ get: async (_key) => null,
1210
+ put: async (_key, _value, _options) => {},
1211
+ delete: async (_key) => {},
1212
+ list: async (_options) => ({
1213
+ keys: []
1214
+ }),
1215
+ ...overrides
1216
+ };
1217
+ }
1218
+ function createMockR2(overrides) {
1219
+ return {
1220
+ put: async (key, _value, _options) => ({
1221
+ key
1222
+ }),
1223
+ get: async (_key) => null,
1224
+ delete: async (_key) => {},
1225
+ list: async (_options) => ({
1226
+ objects: []
1227
+ }),
1228
+ ...overrides
1229
+ };
1230
+ }
1231
+ function createMockPreparedStatement() {
1232
+ return {
1233
+ bind: (..._params) => createMockPreparedStatement(),
1234
+ run: async () => ({ success: true }),
1235
+ all: async () => ({ success: true, results: [] }),
1236
+ first: async () => null
1237
+ };
1238
+ }
1239
+ function createMockD1(overrides) {
1240
+ return {
1241
+ prepare: (_sql) => createMockPreparedStatement(),
1242
+ dump: async () => new ArrayBuffer(0),
1243
+ batch: async (_statements) => [],
1244
+ exec: async (_sql) => ({ success: true }),
1245
+ ...overrides
1246
+ };
1247
+ }
1248
+ function createMockFetcher(response) {
1249
+ const defaultResponse = response ?? new Response("OK", { status: 200 });
1250
+ return {
1251
+ fetch: async (_request, _init) => defaultResponse
1252
+ };
1253
+ }
1254
+ function createMockAnalyticsEngine() {
1255
+ return {
1256
+ writeDataPoint: (_data) => {}
1257
+ };
1258
+ }
1259
+ function createMockAnalyticsEngineDataset() {
1260
+ return createMockAnalyticsEngine();
1261
+ }
1262
+ function createMockQueue() {
1263
+ return {
1264
+ send: async (_body, _options) => {},
1265
+ sendBatch: async (_messages) => {}
1266
+ };
1267
+ }
1268
+ function createMockVectorizeIndex(overrides) {
1269
+ return {
1270
+ query: async (_vector, _options) => ({
1271
+ matches: []
1272
+ }),
1273
+ upsert: async (_vectors) => {},
1274
+ deleteByIds: async (_ids) => {},
1275
+ describe: async () => ({ dimension: 384, totalVectors: 0 }),
1276
+ ...overrides
1277
+ };
1278
+ }
1279
+ function createMockAi(overrides) {
1280
+ return {
1281
+ run: async (_model, _input) => ({}),
1282
+ ...overrides
1283
+ };
1284
+ }
1285
+ function createMockExecutionContext(overrides) {
1286
+ return {
1287
+ waitUntil: (_promise) => {},
1288
+ passThroughOnException: () => {},
1289
+ ...overrides
1290
+ };
1291
+ }
1292
+ function createMockSecretsStore(overrides) {
1293
+ return {
1294
+ get: async (_key) => null,
1295
+ put: async (_key, _value) => {},
1296
+ delete: async (_key) => {},
1297
+ ...overrides
1298
+ };
1299
+ }
1300
+ function createMockEnv(overrides) {
1301
+ return overrides;
1302
+ }
1303
+
1304
+ // src/path-utils.ts
1007
1305
  import { homedir as homedir2 } from "os";
1008
- import { join as join2 } from "path";
1009
- var SESSION_FILE = join2(homedir2(), ".hoox", "session.json");
1306
+ import { join as join2, resolve } from "path";
1307
+ function createHooxPath(path) {
1308
+ return path;
1309
+ }
1310
+ function getHooxHome() {
1311
+ try {
1312
+ const home = homedir2();
1313
+ if (!home || home.length === 0) {
1314
+ return createHooxPath(resolve(process.cwd(), ".hoox"));
1315
+ }
1316
+ return createHooxPath(join2(home, ".hoox"));
1317
+ } catch {
1318
+ return createHooxPath(resolve(process.cwd(), ".hoox"));
1319
+ }
1320
+ }
1321
+ function resolveHooxPath(relativePath) {
1322
+ if (!relativePath || typeof relativePath !== "string") {
1323
+ throw new Error("relativePath must be a non-empty string");
1324
+ }
1325
+ if (relativePath.includes("..")) {
1326
+ throw new Error(`Path traversal detected in relativePath: "${relativePath}"`);
1327
+ }
1328
+ const normalizedPath = relativePath.replace(/\\/g, "/");
1329
+ const hooxHome = getHooxHome();
1330
+ const fullPath = join2(hooxHome, normalizedPath);
1331
+ const resolvedPath = resolve(fullPath);
1332
+ const resolvedHome = resolve(hooxHome);
1333
+ if (!resolvedPath.startsWith(resolvedHome)) {
1334
+ throw new Error(`Resolved path "${resolvedPath}" is outside Hoox home directory`);
1335
+ }
1336
+ return createHooxPath(resolvedPath);
1337
+ }
1338
+ function isWithinHooxHome(path) {
1339
+ try {
1340
+ const hooxHome = getHooxHome();
1341
+ const resolvedPath = resolve(path);
1342
+ const resolvedHome = resolve(hooxHome);
1343
+ return resolvedPath.startsWith(resolvedHome);
1344
+ } catch {
1345
+ return false;
1346
+ }
1347
+ }
1348
+ function getRelativeHooxPath(absolutePath) {
1349
+ try {
1350
+ if (!isWithinHooxHome(absolutePath)) {
1351
+ return null;
1352
+ }
1353
+ const hooxHome = getHooxHome();
1354
+ const resolvedPath = resolve(absolutePath);
1355
+ const resolvedHome = resolve(hooxHome);
1356
+ const relativePath = resolvedPath.slice(resolvedHome.length);
1357
+ return relativePath.startsWith("/") || relativePath.startsWith("\\") ? relativePath.slice(1) : relativePath;
1358
+ } catch {
1359
+ return null;
1360
+ }
1361
+ }
1362
+ function getHooxRepoPath() {
1363
+ return resolveHooxPath("repo");
1364
+ }
1365
+ function getHooxConfigDir() {
1366
+ return resolveHooxPath("config");
1367
+ }
1368
+ function getHooxDataDir() {
1369
+ return resolveHooxPath("data");
1370
+ }
1371
+ function getHooxWranglerPath() {
1372
+ return resolveHooxPath("config/wrangler.jsonc");
1373
+ }
1374
+ function getHooxStatePath() {
1375
+ return resolveHooxPath("data/state.json");
1376
+ }
1377
+
1378
+ // src/session.ts
1379
+ var TUI_STATE_DIR = ".tui-state";
1380
+ var SESSION_FILE = (() => {
1381
+ try {
1382
+ const hooxHome = getHooxHome();
1383
+ return `${hooxHome}/${TUI_STATE_DIR}/session.json`;
1384
+ } catch {
1385
+ return `${process.cwd()}/${TUI_STATE_DIR}/session.json`;
1386
+ }
1387
+ })();
1010
1388
  var DEFAULT_SESSION = {
1011
1389
  activeView: "dashboard",
1012
1390
  sidebarExpanded: true,
@@ -2226,6 +2604,13 @@ var useUIStore = create()(immer((set) => ({
2226
2604
  // src/stores/service-store.ts
2227
2605
  import { create as create2 } from "zustand";
2228
2606
  import { immer as immer2 } from "zustand/middleware/immer";
2607
+ function summarizeCliError(details) {
2608
+ const raw = details.stderr.trim() || details.stdout.trim() || details.command || "Unknown CLI error";
2609
+ const firstLine = raw.split(`
2610
+ `, 1)[0] ?? "";
2611
+ const trimmed = firstLine.slice(0, 120).trimEnd();
2612
+ return trimmed.length > 0 ? trimmed : details.command;
2613
+ }
2229
2614
  var BACKOFF_SEQUENCE = [1000, 2000, 4000, 8000, 16000];
2230
2615
  var MAX_RETRIES2 = 5;
2231
2616
  function ringPush(arr, item, max) {
@@ -2246,6 +2631,7 @@ var initialState2 = {
2246
2631
  selectedWorkerId: null,
2247
2632
  retryCount: 0,
2248
2633
  lastError: null,
2634
+ lastErrorDetails: null,
2249
2635
  lastSuccessfulFetch: 0,
2250
2636
  reconnectDelay: 0,
2251
2637
  disconnectedAt: null
@@ -2270,12 +2656,21 @@ var useServiceStore = create2()(immer2((set, get) => ({
2270
2656
  state.connectionStatus = "connected";
2271
2657
  state.retryCount = 0;
2272
2658
  state.lastError = null;
2659
+ state.lastErrorDetails = null;
2273
2660
  state.reconnectDelay = 0;
2274
2661
  state.disconnectedAt = null;
2275
2662
  }
2276
2663
  });
2277
2664
  } catch (error) {
2278
2665
  const msg = error instanceof Error ? error.message : "Unknown fetch error";
2666
+ get().addAlert({
2667
+ id: crypto.randomUUID(),
2668
+ type: "connection",
2669
+ severity: "error",
2670
+ message: msg,
2671
+ timestamp: Date.now(),
2672
+ acknowledged: false
2673
+ });
2279
2674
  set((state) => {
2280
2675
  if (state.connectionStatus === "connected" || state.connectionStatus === "polling") {
2281
2676
  state.connectionStatus = "reconnecting";
@@ -2301,7 +2696,18 @@ var useServiceStore = create2()(immer2((set, get) => ({
2301
2696
  await subscribeSSE2("/trades/stream", (trade) => {
2302
2697
  get().pushTrade(trade);
2303
2698
  });
2304
- } catch {}
2699
+ } catch (error) {
2700
+ const msg = error instanceof Error ? error.message : "SSE trade stream failed";
2701
+ get().addAlert({
2702
+ id: crypto.randomUUID(),
2703
+ type: "connection",
2704
+ severity: "error",
2705
+ message: msg,
2706
+ timestamp: Date.now(),
2707
+ acknowledged: false
2708
+ });
2709
+ get().handleConnectionFailure(msg);
2710
+ }
2305
2711
  },
2306
2712
  streamLogs: async () => {
2307
2713
  const { subscribeSSE: subscribeSSE2 } = await Promise.resolve().then(() => (init_sse(), exports_sse));
@@ -2309,7 +2715,18 @@ var useServiceStore = create2()(immer2((set, get) => ({
2309
2715
  await subscribeSSE2("/logs/stream", (log) => {
2310
2716
  get().pushLog(log);
2311
2717
  });
2312
- } catch {}
2718
+ } catch (error) {
2719
+ const msg = error instanceof Error ? error.message : "SSE log stream failed";
2720
+ get().addAlert({
2721
+ id: crypto.randomUUID(),
2722
+ type: "connection",
2723
+ severity: "error",
2724
+ message: msg,
2725
+ timestamp: Date.now(),
2726
+ acknowledged: false
2727
+ });
2728
+ get().handleConnectionFailure(msg);
2729
+ }
2313
2730
  },
2314
2731
  addAlert: (alert) => set((state) => {
2315
2732
  state.alerts = ringPush(state.alerts, alert, MAX_ALERTS);
@@ -2322,6 +2739,7 @@ var useServiceStore = create2()(immer2((set, get) => ({
2322
2739
  if (status === "connected") {
2323
2740
  state.retryCount = 0;
2324
2741
  state.lastError = null;
2742
+ state.lastErrorDetails = null;
2325
2743
  state.reconnectDelay = 0;
2326
2744
  state.disconnectedAt = null;
2327
2745
  }
@@ -2351,6 +2769,7 @@ var useServiceStore = create2()(immer2((set, get) => ({
2351
2769
  state.lastSuccessfulFetch = Date.now();
2352
2770
  state.retryCount = 0;
2353
2771
  state.lastError = null;
2772
+ state.lastErrorDetails = null;
2354
2773
  state.reconnectDelay = 0;
2355
2774
  state.disconnectedAt = null;
2356
2775
  if (state.connectionStatus === "reconnecting" || state.connectionStatus === "polling") {
@@ -2378,6 +2797,7 @@ var useServiceStore = create2()(immer2((set, get) => ({
2378
2797
  resetRetries: () => set((state) => {
2379
2798
  state.retryCount = 0;
2380
2799
  state.lastError = null;
2800
+ state.lastErrorDetails = null;
2381
2801
  state.reconnectDelay = 0;
2382
2802
  state.disconnectedAt = null;
2383
2803
  }),
@@ -2386,8 +2806,30 @@ var useServiceStore = create2()(immer2((set, get) => ({
2386
2806
  state.connectionStatus = "polling";
2387
2807
  state.retryCount = 0;
2388
2808
  state.lastError = null;
2809
+ state.lastErrorDetails = null;
2389
2810
  state.reconnectDelay = 0;
2390
2811
  }
2812
+ }),
2813
+ setLastErrorDetails: (details) => set((state) => {
2814
+ state.lastErrorDetails = details;
2815
+ state.lastError = details === null ? null : summarizeCliError(details);
2816
+ }),
2817
+ clearError: () => set((state) => {
2818
+ state.lastError = null;
2819
+ state.lastErrorDetails = null;
2820
+ }),
2821
+ addCliErrorAlert: (details) => set((state) => {
2822
+ state.lastErrorDetails = details;
2823
+ state.lastError = summarizeCliError(details);
2824
+ const summary = summarizeCliError(details);
2825
+ state.alerts = ringPush(state.alerts, {
2826
+ id: crypto.randomUUID(),
2827
+ type: "connection",
2828
+ severity: "error",
2829
+ message: `CLI failure: ${summary}`,
2830
+ timestamp: Date.now(),
2831
+ acknowledged: false
2832
+ }, MAX_ALERTS);
2391
2833
  })
2392
2834
  })));
2393
2835
 
@@ -2525,13 +2967,22 @@ export {
2525
2967
  serializeState,
2526
2968
  saveSession,
2527
2969
  restoreSession,
2970
+ resolveHooxPath,
2528
2971
  resolveDependencies,
2529
2972
  readConfigSync,
2530
2973
  readConfig,
2531
2974
  logKvTimestamp,
2975
+ isWithinHooxHome,
2532
2976
  hooxFetch,
2533
2977
  healthCheck,
2534
2978
  headersToObject,
2979
+ getRelativeHooxPath,
2980
+ getHooxWranglerPath,
2981
+ getHooxStatePath,
2982
+ getHooxRepoPath,
2983
+ getHooxHome,
2984
+ getHooxDataDir,
2985
+ getHooxConfigDir,
2535
2986
  generateWranglerJsonc,
2536
2987
  generateDevVars,
2537
2988
  formatUptime,
@@ -2550,8 +3001,22 @@ export {
2550
3001
  formatCompactCurrency,
2551
3002
  deserializeState,
2552
3003
  createSuccessResponse,
3004
+ createQueueHandler,
3005
+ createMockVectorizeIndex,
3006
+ createMockSecretsStore,
3007
+ createMockR2,
3008
+ createMockQueue,
3009
+ createMockKV,
3010
+ createMockFetcher,
3011
+ createMockExecutionContext,
3012
+ createMockEnv,
3013
+ createMockD1,
3014
+ createMockAnalyticsEngineDataset,
3015
+ createMockAnalyticsEngine,
3016
+ createMockAi,
2553
3017
  createJsonResponse,
2554
3018
  createErrorResponse,
3019
+ createCronHandler,
2555
3020
  WorkerAPIError,
2556
3021
  WizardEngine,
2557
3022
  WebhookPayloadSchema,
@@ -2576,6 +3041,6 @@ export {
2576
3041
  Colors,
2577
3042
  COPYRIGHT,
2578
3043
  CALLED_BY,
2579
- BaseExchangeClient,
3044
+ BaseExchangeClient2 as BaseExchangeClient,
2580
3045
  BalanceSchema
2581
3046
  };