@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.
- package/dist/analytics.d.ts +0 -1
- package/dist/analytics.d.ts.map +1 -1
- package/dist/colors.d.ts +0 -1
- package/dist/colors.d.ts.map +1 -1
- package/dist/colors.js +0 -1
- package/dist/cron-handler.d.ts +34 -0
- package/dist/cron-handler.d.ts.map +1 -0
- package/dist/cron-handler.js +48 -0
- package/dist/d1/index.js +49 -6
- package/dist/d1/repository.d.ts +5 -0
- package/dist/d1/repository.d.ts.map +1 -1
- package/dist/errors.d.ts +1 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +48 -5
- package/dist/exchanges/base-exchange-client.d.ts +68 -0
- package/dist/exchanges/base-exchange-client.d.ts.map +1 -0
- package/dist/exchanges/index.d.ts +3 -0
- package/dist/exchanges/index.d.ts.map +1 -0
- package/dist/exchanges/index.js +120 -0
- package/dist/exchanges/types.d.ts +19 -0
- package/dist/exchanges/types.d.ts.map +1 -0
- package/dist/health.js +48 -5
- package/dist/index.d.ts +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +480 -15
- package/dist/kvUtils.js +48 -5
- package/dist/middleware/auth.d.ts +6 -3
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/index.js +193 -18
- package/dist/middleware/logger.d.ts +2 -1
- package/dist/middleware/logger.d.ts.map +1 -1
- package/dist/middleware/rate-limit.d.ts +3 -1
- package/dist/middleware/rate-limit.d.ts.map +1 -1
- package/dist/path-utils.d.ts +147 -0
- package/dist/path-utils.d.ts.map +1 -0
- package/dist/path-utils.js +101 -0
- package/dist/queue-handler.d.ts +43 -0
- package/dist/queue-handler.d.ts.map +1 -0
- package/dist/queue-handler.js +46 -0
- package/dist/router.d.ts +1 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +15 -8
- package/dist/service-bindings.d.ts +5 -28
- package/dist/service-bindings.d.ts.map +1 -1
- package/dist/service-bindings.js +3 -1
- package/dist/session.d.ts +9 -5
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +83 -3
- package/dist/sse.js +1 -1
- package/dist/stores/service-store.d.ts +50 -1
- package/dist/stores/service-store.d.ts.map +1 -1
- package/dist/stores/service-store.js +67 -3
- package/dist/test-utils.d.ts +132 -0
- package/dist/test-utils.d.ts.map +1 -0
- package/dist/test-utils.js +128 -0
- package/dist/types/router.d.ts +18 -8
- package/dist/types/router.d.ts.map +1 -1
- package/dist/types.d.ts +59 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/wizard/engine.d.ts.map +1 -1
- package/package.json +11 -3
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EACL,SAAS,EACT,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,SAAS,CAAC;AAIjB,YAAY,EACV,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,WAAW,EACX,oBAAoB,EACpB,eAAe,EACf,yBAAyB,EACzB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,EACf,kBAAkB,EAClB,OAAO,EACP,MAAM,GACP,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,cAAc,EACd,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACxE,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EACL,OAAO,EACP,MAAM,EACN,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,
|
|
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 (
|
|
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 (
|
|
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
|
|
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(
|
|
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/
|
|
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
|
-
|
|
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
|
};
|