@hoox-sh/hoox-shared 1.2.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 (161) hide show
  1. package/README.md +44 -0
  2. package/dist/agent-config-fields.d.ts +32 -0
  3. package/dist/agent-config-fields.d.ts.map +1 -0
  4. package/dist/agent-config-fields.js +220 -0
  5. package/dist/analytics.d.ts +47 -0
  6. package/dist/analytics.d.ts.map +1 -0
  7. package/dist/analytics.js +45 -0
  8. package/dist/api-client.d.ts +48 -0
  9. package/dist/api-client.d.ts.map +1 -0
  10. package/dist/api-client.js +191 -0
  11. package/dist/colors.d.ts +76 -0
  12. package/dist/colors.d.ts.map +1 -0
  13. package/dist/colors.js +82 -0
  14. package/dist/config.d.ts +61 -0
  15. package/dist/config.d.ts.map +1 -0
  16. package/dist/config.js +146 -0
  17. package/dist/cron-handler.d.ts +38 -0
  18. package/dist/cron-handler.d.ts.map +1 -0
  19. package/dist/cron-handler.js +48 -0
  20. package/dist/d1/index.d.ts +9 -0
  21. package/dist/d1/index.d.ts.map +1 -0
  22. package/dist/d1/index.js +360 -0
  23. package/dist/d1/repository.d.ts +86 -0
  24. package/dist/d1/repository.d.ts.map +1 -0
  25. package/dist/d1/repository.js +297 -0
  26. package/dist/d1/schemas.d.ts +89 -0
  27. package/dist/d1/schemas.d.ts.map +1 -0
  28. package/dist/d1/schemas.js +81 -0
  29. package/dist/dashboard-manifest.d.ts +135 -0
  30. package/dist/dashboard-manifest.d.ts.map +1 -0
  31. package/dist/dashboard-manifest.js +401 -0
  32. package/dist/errors.d.ts +44 -0
  33. package/dist/errors.d.ts.map +1 -0
  34. package/dist/errors.js +149 -0
  35. package/dist/exchange-client.d.ts +98 -0
  36. package/dist/exchange-client.d.ts.map +1 -0
  37. package/dist/exchange-client.js +107 -0
  38. package/dist/exchanges/base-exchange-client.d.ts +81 -0
  39. package/dist/exchanges/base-exchange-client.d.ts.map +1 -0
  40. package/dist/exchanges/base-exchange-client.js +123 -0
  41. package/dist/exchanges/index.d.ts +7 -0
  42. package/dist/exchanges/index.d.ts.map +1 -0
  43. package/dist/exchanges/index.js +123 -0
  44. package/dist/exchanges/types.d.ts +23 -0
  45. package/dist/exchanges/types.d.ts.map +1 -0
  46. package/dist/exchanges/types.js +1 -0
  47. package/dist/format-time.d.ts +28 -0
  48. package/dist/format-time.d.ts.map +1 -0
  49. package/dist/format-time.js +57 -0
  50. package/dist/formatters.d.ts +36 -0
  51. package/dist/formatters.d.ts.map +1 -0
  52. package/dist/formatters.js +155 -0
  53. package/dist/health.d.ts +19 -0
  54. package/dist/health.d.ts.map +1 -0
  55. package/dist/health.js +160 -0
  56. package/dist/index.d.ts +72 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +4198 -0
  59. package/dist/kvKeys.d.ts +76 -0
  60. package/dist/kvKeys.d.ts.map +1 -0
  61. package/dist/kvKeys.js +107 -0
  62. package/dist/kvUtils.d.ts +30 -0
  63. package/dist/kvUtils.d.ts.map +1 -0
  64. package/dist/kvUtils.js +170 -0
  65. package/dist/legal.d.ts +15 -0
  66. package/dist/legal.d.ts.map +1 -0
  67. package/dist/legal.js +40 -0
  68. package/dist/middleware/auth.d.ts +102 -0
  69. package/dist/middleware/auth.d.ts.map +1 -0
  70. package/dist/middleware/auth.js +151 -0
  71. package/dist/middleware/cors.d.ts +58 -0
  72. package/dist/middleware/cors.d.ts.map +1 -0
  73. package/dist/middleware/cors.js +77 -0
  74. package/dist/middleware/index.d.ts +14 -0
  75. package/dist/middleware/index.d.ts.map +1 -0
  76. package/dist/middleware/index.js +635 -0
  77. package/dist/middleware/logger.d.ts +17 -0
  78. package/dist/middleware/logger.d.ts.map +1 -0
  79. package/dist/middleware/logger.js +202 -0
  80. package/dist/middleware/rate-limit.d.ts +30 -0
  81. package/dist/middleware/rate-limit.d.ts.map +1 -0
  82. package/dist/middleware/rate-limit.js +181 -0
  83. package/dist/middleware/security-headers.d.ts +43 -0
  84. package/dist/middleware/security-headers.d.ts.map +1 -0
  85. package/dist/middleware/security-headers.js +58 -0
  86. package/dist/middleware/validate.d.ts +25 -0
  87. package/dist/middleware/validate.d.ts.map +1 -0
  88. package/dist/middleware/validate.js +56 -0
  89. package/dist/operator-transport.d.ts +65 -0
  90. package/dist/operator-transport.d.ts.map +1 -0
  91. package/dist/operator-transport.js +87 -0
  92. package/dist/path-utils.d.ts +190 -0
  93. package/dist/path-utils.d.ts.map +1 -0
  94. package/dist/path-utils.js +188 -0
  95. package/dist/queue-handler.d.ts +47 -0
  96. package/dist/queue-handler.d.ts.map +1 -0
  97. package/dist/queue-handler.js +46 -0
  98. package/dist/router.d.ts +15 -0
  99. package/dist/router.d.ts.map +1 -0
  100. package/dist/router.js +130 -0
  101. package/dist/schemas/index.d.ts +8 -0
  102. package/dist/schemas/index.d.ts.map +1 -0
  103. package/dist/schemas/index.js +705 -0
  104. package/dist/schemas/registry.d.ts +12 -0
  105. package/dist/schemas/registry.d.ts.map +1 -0
  106. package/dist/schemas/registry.js +433 -0
  107. package/dist/schemas/types.d.ts +83 -0
  108. package/dist/schemas/types.d.ts.map +1 -0
  109. package/dist/schemas/types.js +1 -0
  110. package/dist/schemas/validators.d.ts +39 -0
  111. package/dist/schemas/validators.d.ts.map +1 -0
  112. package/dist/schemas/validators.js +290 -0
  113. package/dist/service-bindings.d.ts +78 -0
  114. package/dist/service-bindings.d.ts.map +1 -0
  115. package/dist/service-bindings.js +248 -0
  116. package/dist/session.d.ts +39 -0
  117. package/dist/session.d.ts.map +1 -0
  118. package/dist/session.js +261 -0
  119. package/dist/sse.d.ts +39 -0
  120. package/dist/sse.d.ts.map +1 -0
  121. package/dist/sse.js +154 -0
  122. package/dist/stores/config-store.d.ts +91 -0
  123. package/dist/stores/config-store.d.ts.map +1 -0
  124. package/dist/stores/config-store.js +255 -0
  125. package/dist/stores/service-store.d.ts +266 -0
  126. package/dist/stores/service-store.d.ts.map +1 -0
  127. package/dist/stores/service-store.js +516 -0
  128. package/dist/stores/ui-store.d.ts +67 -0
  129. package/dist/stores/ui-store.d.ts.map +1 -0
  130. package/dist/stores/ui-store.js +62 -0
  131. package/dist/test-utils.d.ts +136 -0
  132. package/dist/test-utils.d.ts.map +1 -0
  133. package/dist/test-utils.js +128 -0
  134. package/dist/types/errors.d.ts +20 -0
  135. package/dist/types/errors.d.ts.map +1 -0
  136. package/dist/types/errors.js +1 -0
  137. package/dist/types/router.d.ts +36 -0
  138. package/dist/types/router.d.ts.map +1 -0
  139. package/dist/types/router.js +1 -0
  140. package/dist/types.d.ts +442 -0
  141. package/dist/types.d.ts.map +1 -0
  142. package/dist/types.js +83 -0
  143. package/dist/wizard/engine.d.ts +46 -0
  144. package/dist/wizard/engine.d.ts.map +1 -0
  145. package/dist/wizard/engine.js +525 -0
  146. package/dist/wizard/index.d.ts +11 -0
  147. package/dist/wizard/index.d.ts.map +1 -0
  148. package/dist/wizard/index.js +544 -0
  149. package/dist/wizard/persistence.d.ts +18 -0
  150. package/dist/wizard/persistence.d.ts.map +1 -0
  151. package/dist/wizard/persistence.js +31 -0
  152. package/dist/wizard/presets.d.ts +38 -0
  153. package/dist/wizard/presets.d.ts.map +1 -0
  154. package/dist/wizard/presets.js +223 -0
  155. package/dist/wizard/provisioner.d.ts +15 -0
  156. package/dist/wizard/provisioner.d.ts.map +1 -0
  157. package/dist/wizard/provisioner.js +1 -0
  158. package/dist/wizard/types.d.ts +76 -0
  159. package/dist/wizard/types.d.ts.map +1 -0
  160. package/dist/wizard/types.js +1 -0
  161. package/package.json +117 -0
package/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # @hoox/shared
2
+
3
+ Shared types, custom router with path parameter support (`:param` syntax), middleware (auth, CORS, rate-limit, logger), error factories (`Errors.*`), generic exchange provider factory (`ExchangeRouter`, `IExchangeProvider`), and utilities.
4
+
5
+ ## For CLI Users
6
+
7
+ This is an internal package — installed automatically as a workspace dependency. No CLI commands needed.
8
+
9
+ → [Architecture Overview](../../docs/devops/architecture/overview.md)
10
+
11
+ ## For Operators
12
+
13
+ This package provides the shared foundation for all Hoox workers. It includes:
14
+
15
+ - **Custom Router** — Lightweight routing with exact-match fast path and `:param` path parameter support. Handlers receive extracted params as an optional 4th argument.
16
+ - **Error Factories** — `Errors.*` helpers (`badRequest`, `unauthorized`, `forbidden`, `notFound`, `internal`, etc.) for consistent JSON error responses across all workers.
17
+ - **Exchange Provider Factory** — Generic `ExchangeRouter<TClient, TEnv>` and `IExchangeProvider<TClient, TEnv>` for registering and resolving exchange clients by name.
18
+ - **Middleware** — Logger (`createLogger`, `withRequestLog`), auth (`requireInternalAuth`), CORS, rate limiting, and Zod validation.
19
+ - **Utilities** — `toError`, `serviceFetch`, `createJsonResponse`, KV helpers, health check, analytics tracking.
20
+
21
+ → [Architecture Overview](../../docs/devops/architecture/overview.md)
22
+
23
+ ## Public API
24
+
25
+ This package is published as `@hoox-sh/hoox-shared` and exposes a layered import surface via the package `exports` map. All subpaths resolve to compiled `dist/*.js` and `dist/*.d.ts` files — the package tarball does **not** include TypeScript sources.
26
+
27
+ | Import | Resolves to | Notes |
28
+ | -------------------------------------- | ------------------------- | ------------------------------------------------------------------- |
29
+ | `@hoox-sh/hoox-shared` | `dist/index.js` | Main barrel — everything in `src/index.ts` |
30
+ | `@hoox-sh/hoox-shared/<subdir>` | `dist/<subdir>/index.js` | Sub-barrel for the named subdirectory |
31
+ | `@hoox-sh/hoox-shared/<subdir>/<file>` | `dist/<subdir>/<file>.js` | Specific submodule file |
32
+ | `@hoox-sh/hoox-shared/stores/<store>` | `dist/stores/<store>.js` | Zustand stores — deep-import only (no barrel) |
33
+ | `@hoox-sh/hoox-shared/types` | `dist/types/index.d.ts` | Type-only re-exports |
34
+ | `@hoox-sh/hoox-shared/<anything-else>` | `dist/<anything-else>.js` | Catch-all for top-level modules (e.g. `errors`, `router`, `kvKeys`) |
35
+
36
+ The named subdirectories with deep-import support are: `middleware`, `d1`, `schemas`, `wizard`, `exchanges`. Each one re-exports its submodules from its `index.ts` barrel, so the bare-subdir form (`<subdir>`) and the deep form (`<subdir>/<file>`) return the same symbols — the bare form is just the pre-bundled rollup.
37
+
38
+ **Recommendation:** prefer the bare form for subdirectories (`hoox-shared/middleware`) unless you specifically need a single submodule to keep the import graph small. The bare form gives you a stable, curated surface; the deep form is for tree-shaking-conscious or very specific use cases.
39
+
40
+ ## Development
41
+
42
+ ```bash
43
+ bun test packages/shared
44
+ ```
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Copyright (c) 2026 HOOX · HOOX · jango-blockchained (hoox-sh)
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ /** CONFIG_KV key for the agent JSON config document. */
6
+ export declare const AGENT_CONFIG_KV_KEY = "agent:config";
7
+ export type AgentConfigFieldSpec = {
8
+ kind: "top";
9
+ key: "defaultProvider" | "fallbackChain" | "timeoutMs" | "retryCount" | "maxDailyDrawdownPercent" | "trailingStopPercent" | "takeProfitPercent";
10
+ } | {
11
+ kind: "model";
12
+ provider: string;
13
+ };
14
+ /**
15
+ * Composite field keys (`section:field` from dashboard.jsonc) that embed
16
+ * into agent:config rather than flat CONFIG_KV keys.
17
+ */
18
+ export declare const AGENT_CONFIG_EMBEDDED_FIELDS: Readonly<Record<string, AgentConfigFieldSpec>>;
19
+ /** Sections whose fields are edited via agent:config (not flat KV). */
20
+ export declare const DASHBOARD_SECTIONS_AGENT_CONFIG: ReadonlySet<string>;
21
+ export declare function isAgentConfigEmbeddedField(fieldKey: string): boolean;
22
+ export declare function isAgentConfigSection(sectionId: string): boolean;
23
+ /** Parse agent:config KV payload (handles double-encoded JSON strings). */
24
+ export declare function parseAgentConfigJson(raw: string | null | undefined): Record<string, unknown>;
25
+ export declare function serializeAgentConfigForKv(config: Record<string, unknown>): string;
26
+ export declare function getAgentConfigEmbeddedValue(config: Record<string, unknown>, fieldKey: string): string | number | boolean | undefined;
27
+ export declare function setAgentConfigEmbeddedValue(config: Record<string, unknown>, fieldKey: string, value: string | number | boolean): Record<string, unknown>;
28
+ /** Expand agent:config into dashboard composite field keys for UI forms. */
29
+ export declare function expandAgentConfigToFieldMap(config: Record<string, unknown>): Record<string, string | number | boolean>;
30
+ /** Apply multiple embedded field updates onto an agent:config object. */
31
+ export declare function applyAgentConfigFieldUpdates(config: Record<string, unknown>, updates: Record<string, string | number | boolean>): Record<string, unknown>;
32
+ //# sourceMappingURL=agent-config-fields.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-config-fields.d.ts","sourceRoot":"","sources":["../src/agent-config-fields.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,wDAAwD;AACxD,eAAO,MAAM,mBAAmB,iBAAkB,CAAC;AAEnD,MAAM,MAAM,oBAAoB,GAC5B;IACE,IAAI,EAAE,KAAK,CAAC;IACZ,GAAG,EACC,iBAAiB,GACjB,eAAe,GACf,WAAW,GACX,YAAY,GACZ,yBAAyB,GACzB,qBAAqB,GACrB,mBAAmB,CAAC;CACzB,GACD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC;;;GAGG;AACH,eAAO,MAAM,4BAA4B,EAAE,QAAQ,CACjD,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAiBrC,CAAC;AAEF,uEAAuE;AACvE,eAAO,MAAM,+BAA+B,EAAE,WAAW,CAAC,MAAM,CAG9D,CAAC;AAEH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAKpE;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED,2EAA2E;AAC3E,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC7B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAkBzB;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,MAAM,CAER;AAUD,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CA+BvC;AAED,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAC/B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAsCzB;AAED,4EAA4E;AAC5E,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAO3C;AAED,yEAAyE;AACzE,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GACjD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQzB"}
@@ -0,0 +1,220 @@
1
+ // @bun
2
+ var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, {
10
+ get: all[name],
11
+ enumerable: true,
12
+ configurable: true,
13
+ set: __exportSetter.bind(all, name)
14
+ });
15
+ };
16
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
+
18
+ // src/kvKeys.ts
19
+ var KV_TRADE_DEFAULT_LEVERAGE = "trade:default_leverage";
20
+ var KV_TRADE_MAX_POSITION_SIZE = "trade:max_position_size";
21
+ var KV_TRADE_MAX_DAILY_DRAWDOWN_PERCENT = "trade:max_daily_drawdown_percent";
22
+ var KV_TRADE_TRAILING_STOP_PERCENT = "trade:trailing_stop_percent";
23
+ var KV_TRADE_KILL_SWITCH = "trade:kill_switch";
24
+ var KV_TRADE_ROUTING = "trade:routing";
25
+ var KV_BOT_ENABLED = "bot:enabled";
26
+ var KV_BOT_DEFAULT_CHAT_ID = "bot:default_chat_id";
27
+ var KV_BOT_NOTIFY_ON_EXECUTION = "bot:notify_on_execution";
28
+ var KV_BOT_NOTIFY_ON_ERROR = "bot:notify_on_error";
29
+ var KV_WEBHOOK_IP_CHECK_ENABLED = "webhook:tradingview:ip_check_enabled";
30
+ var KV_WEBHOOK_ALLOWED_IPS = "webhook:tradingview:allowed_ips";
31
+ var KV_WEBHOOK_QUEUE_MODE = "webhook:queue_mode";
32
+ var KV_EMAIL_SCAN_SUBJECT = "email:scan_subject";
33
+ var KV_EMAIL_USE_IMAP = "email:use_imap";
34
+ var KV_EMAIL_COIN_PATTERN = "email:coin_pattern";
35
+ var KV_EMAIL_ACTION_PATTERN = "email:action_pattern";
36
+ var KV_EMAIL_QUANTITY_MULTIPLIER = "email:quantity_multiplier";
37
+ var KV_AGENT_CONFIG = "agent:config";
38
+ var KV_AGENT_OPENAI_KEY = "agent:openai_key";
39
+ var KV_AGENT_ANTHROPIC_KEY = "agent:anthropic_key";
40
+ var KV_AGENT_GOOGLE_KEY = "agent:google_key";
41
+ var KV_DASHBOARD_AI_HEALTH_SUMMARY = "dashboard:ai_health_summary";
42
+ var KV_HEALTH_CHECK = "health_check";
43
+ var KV_HOUSEKEEPING_LAST_CHECK = "housekeeping:last_check";
44
+ var QUEUE_MODE_FAILOVER = "queue_failover";
45
+ var QUEUE_MODE_EVERYWHERE = "queue_everywhere";
46
+ var QUEUE_MODE_DISABLED = "queue_disabled";
47
+ var KVKeys = {
48
+ KV_TRADE_DEFAULT_LEVERAGE,
49
+ KV_TRADE_MAX_POSITION_SIZE,
50
+ KV_TRADE_MAX_DAILY_DRAWDOWN_PERCENT,
51
+ KV_TRADE_TRAILING_STOP_PERCENT,
52
+ KV_TRADE_KILL_SWITCH,
53
+ KV_TRADE_ROUTING,
54
+ KV_BOT_ENABLED,
55
+ KV_BOT_DEFAULT_CHAT_ID,
56
+ KV_BOT_NOTIFY_ON_EXECUTION,
57
+ KV_BOT_NOTIFY_ON_ERROR,
58
+ KV_WEBHOOK_IP_CHECK_ENABLED,
59
+ KV_WEBHOOK_ALLOWED_IPS,
60
+ KV_WEBHOOK_QUEUE_MODE,
61
+ KV_EMAIL_SCAN_SUBJECT,
62
+ KV_EMAIL_USE_IMAP,
63
+ KV_EMAIL_COIN_PATTERN,
64
+ KV_EMAIL_ACTION_PATTERN,
65
+ KV_EMAIL_QUANTITY_MULTIPLIER,
66
+ KV_AGENT_CONFIG,
67
+ KV_AGENT_OPENAI_KEY,
68
+ KV_AGENT_ANTHROPIC_KEY,
69
+ KV_AGENT_GOOGLE_KEY,
70
+ KV_DASHBOARD_AI_HEALTH_SUMMARY,
71
+ KV_HEALTH_CHECK,
72
+ KV_HOUSEKEEPING_LAST_CHECK,
73
+ QUEUE_MODE_FAILOVER,
74
+ QUEUE_MODE_EVERYWHERE,
75
+ QUEUE_MODE_DISABLED
76
+ };
77
+
78
+ // src/agent-config-fields.ts
79
+ var AGENT_CONFIG_KV_KEY = KV_AGENT_CONFIG;
80
+ var AGENT_CONFIG_EMBEDDED_FIELDS = {
81
+ "providers:default_provider": { kind: "top", key: "defaultProvider" },
82
+ "providers:fallback_chain": { kind: "top", key: "fallbackChain" },
83
+ "providers:timeout_ms": { kind: "top", key: "timeoutMs" },
84
+ "providers:retry_count": { kind: "top", key: "retryCount" },
85
+ "models:workers_ai_model": { kind: "model", provider: "workers-ai" },
86
+ "models:openai_model": { kind: "model", provider: "openai" },
87
+ "models:anthropic_model": { kind: "model", provider: "anthropic" },
88
+ "models:google_model": { kind: "model", provider: "google" },
89
+ "risk:max_daily_drawdown_percent": {
90
+ kind: "top",
91
+ key: "maxDailyDrawdownPercent"
92
+ },
93
+ "risk:trailing_stop_percent": { kind: "top", key: "trailingStopPercent" },
94
+ "risk:take_profit_percent": { kind: "top", key: "takeProfitPercent" }
95
+ };
96
+ var DASHBOARD_SECTIONS_AGENT_CONFIG = new Set([
97
+ "providers",
98
+ "models"
99
+ ]);
100
+ function isAgentConfigEmbeddedField(fieldKey) {
101
+ return Object.prototype.hasOwnProperty.call(AGENT_CONFIG_EMBEDDED_FIELDS, fieldKey);
102
+ }
103
+ function isAgentConfigSection(sectionId) {
104
+ return DASHBOARD_SECTIONS_AGENT_CONFIG.has(sectionId);
105
+ }
106
+ function parseAgentConfigJson(raw) {
107
+ if (raw === null || raw === undefined || raw === "")
108
+ return {};
109
+ try {
110
+ let v = JSON.parse(raw);
111
+ if (typeof v === "string") {
112
+ try {
113
+ v = JSON.parse(v);
114
+ } catch {
115
+ return {};
116
+ }
117
+ }
118
+ if (v && typeof v === "object" && !Array.isArray(v)) {
119
+ return { ...v };
120
+ }
121
+ } catch {}
122
+ return {};
123
+ }
124
+ function serializeAgentConfigForKv(config) {
125
+ return JSON.stringify(config);
126
+ }
127
+ var NUMBER_TOP_KEYS = new Set([
128
+ "timeoutMs",
129
+ "retryCount",
130
+ "maxDailyDrawdownPercent",
131
+ "trailingStopPercent",
132
+ "takeProfitPercent"
133
+ ]);
134
+ function getAgentConfigEmbeddedValue(config, fieldKey) {
135
+ const spec = AGENT_CONFIG_EMBEDDED_FIELDS[fieldKey];
136
+ if (!spec)
137
+ return;
138
+ if (spec.kind === "model") {
139
+ const map = config.modelMap;
140
+ if (!map || typeof map !== "object" || Array.isArray(map))
141
+ return;
142
+ const v2 = map[spec.provider];
143
+ if (typeof v2 === "string" || typeof v2 === "number" || typeof v2 === "boolean")
144
+ return v2;
145
+ return;
146
+ }
147
+ const v = config[spec.key];
148
+ if (spec.key === "fallbackChain") {
149
+ if (Array.isArray(v))
150
+ return JSON.stringify(v);
151
+ if (typeof v === "string")
152
+ return v;
153
+ return;
154
+ }
155
+ if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
156
+ return v;
157
+ }
158
+ return;
159
+ }
160
+ function setAgentConfigEmbeddedValue(config, fieldKey, value) {
161
+ const spec = AGENT_CONFIG_EMBEDDED_FIELDS[fieldKey];
162
+ if (!spec)
163
+ return { ...config };
164
+ const next = { ...config };
165
+ if (spec.kind === "model") {
166
+ const prev = next.modelMap && typeof next.modelMap === "object" && !Array.isArray(next.modelMap) ? next.modelMap : {};
167
+ next.modelMap = { ...prev, [spec.provider]: String(value) };
168
+ return next;
169
+ }
170
+ if (spec.key === "fallbackChain") {
171
+ if (typeof value === "string") {
172
+ try {
173
+ next.fallbackChain = JSON.parse(value);
174
+ } catch {
175
+ next.fallbackChain = value;
176
+ }
177
+ } else {
178
+ next.fallbackChain = value;
179
+ }
180
+ return next;
181
+ }
182
+ if (NUMBER_TOP_KEYS.has(spec.key)) {
183
+ const n = typeof value === "number" ? value : Number(value);
184
+ next[spec.key] = Number.isFinite(n) ? n : value;
185
+ return next;
186
+ }
187
+ next[spec.key] = value;
188
+ return next;
189
+ }
190
+ function expandAgentConfigToFieldMap(config) {
191
+ const out = {};
192
+ for (const fieldKey of Object.keys(AGENT_CONFIG_EMBEDDED_FIELDS)) {
193
+ const v = getAgentConfigEmbeddedValue(config, fieldKey);
194
+ if (v !== undefined)
195
+ out[fieldKey] = v;
196
+ }
197
+ return out;
198
+ }
199
+ function applyAgentConfigFieldUpdates(config, updates) {
200
+ let next = { ...config };
201
+ for (const [fieldKey, value] of Object.entries(updates)) {
202
+ if (isAgentConfigEmbeddedField(fieldKey)) {
203
+ next = setAgentConfigEmbeddedValue(next, fieldKey, value);
204
+ }
205
+ }
206
+ return next;
207
+ }
208
+ export {
209
+ setAgentConfigEmbeddedValue,
210
+ serializeAgentConfigForKv,
211
+ parseAgentConfigJson,
212
+ isAgentConfigSection,
213
+ isAgentConfigEmbeddedField,
214
+ getAgentConfigEmbeddedValue,
215
+ expandAgentConfigToFieldMap,
216
+ applyAgentConfigFieldUpdates,
217
+ DASHBOARD_SECTIONS_AGENT_CONFIG,
218
+ AGENT_CONFIG_KV_KEY,
219
+ AGENT_CONFIG_EMBEDDED_FIELDS
220
+ };
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Copyright (c) 2026 HOOX · HOOX · jango-blockchained (hoox-sh)
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ /**
6
+ * Analytics tracking utilities for Cloudflare Workers
7
+ * Centralizes the trackAnalytics helper used across all workers
8
+ */
9
+ /**
10
+ * Environment with optional analytics service binding.
11
+ * Workers that support analytics tracking should extend this type.
12
+ *
13
+ * Note on auth: `trackAnalytics` reads `INTERNAL_KEY_BINDING` from the
14
+ * passed env to authenticate with the analytics worker (its `/track/*`
15
+ * routes are gated by `requireInternalAuth`). We don't declare the
16
+ * property here because Cloudflare's generated `Env` type already
17
+ * includes it as a required string, and declaring it again as
18
+ * `INTERNAL_KEY_BINDING?: string` would conflict with workers that
19
+ * `extends Cloudflare.Env, AnalyticsEnv` (TS2320). The property is
20
+ * accessed dynamically; production envs from wrangler always have it,
21
+ * and test mocks add it explicitly via spread.
22
+ */
23
+ export interface AnalyticsEnv {
24
+ ANALYTICS_SERVICE?: Fetcher;
25
+ }
26
+ /**
27
+ * Optional configuration for trackAnalytics.
28
+ */
29
+ export interface TrackAnalyticsOptions {
30
+ /**
31
+ * Custom indexes to attach to the Analytics Engine data point.
32
+ * Use this to tag rows with stable identifiers (e.g. probe_id)
33
+ * so they can be SQL-queried later.
34
+ */
35
+ indexes?: readonly string[];
36
+ }
37
+ /**
38
+ * Track an analytics event by forwarding to the analytics worker.
39
+ * Non-blocking: failures are logged but never throw.
40
+ *
41
+ * @param env - Worker environment with optional ANALYTICS_SERVICE binding
42
+ * @param endpoint - Analytics endpoint path (e.g., "/track/api-call")
43
+ * @param body - JSON-serializable payload to send
44
+ * @param options - Optional config (currently supports `indexes`)
45
+ */
46
+ export declare function trackAnalytics(env: AnalyticsEnv, endpoint: string, body: Record<string, unknown>, options?: TrackAnalyticsOptions): Promise<void>;
47
+ //# sourceMappingURL=analytics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../src/analytics.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AAEH;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,YAAY;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B;AAED;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,YAAY,EACjB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,IAAI,CAAC,CA8Bf"}
@@ -0,0 +1,45 @@
1
+ // @bun
2
+ var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, {
10
+ get: all[name],
11
+ enumerable: true,
12
+ configurable: true,
13
+ set: __exportSetter.bind(all, name)
14
+ });
15
+ };
16
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
+
18
+ // src/analytics.ts
19
+ async function trackAnalytics(env, endpoint, body, options) {
20
+ if (!env.ANALYTICS_SERVICE)
21
+ return;
22
+ try {
23
+ const payload = {
24
+ ...body,
25
+ ...options?.indexes ? { indexes: [...options.indexes] } : {}
26
+ };
27
+ const headers = {
28
+ "Content-Type": "application/json"
29
+ };
30
+ const internalKey = env.INTERNAL_KEY_BINDING;
31
+ if (internalKey) {
32
+ headers["X-Internal-Auth-Key"] = internalKey;
33
+ }
34
+ await env.ANALYTICS_SERVICE.fetch(new Request(`http://localhost${endpoint}`, {
35
+ method: "POST",
36
+ headers,
37
+ body: JSON.stringify(payload)
38
+ }));
39
+ } catch (e) {
40
+ console.error("Analytics tracking failed:", e);
41
+ }
42
+ }
43
+ export {
44
+ trackAnalytics
45
+ };
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Copyright (c) 2026 HOOX · HOOX · jango-blockchained (hoox-sh)
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ /**
6
+ * API Client — Bun fetch wrapper with AbortController timeouts, retry logic,
7
+ * and connection-aware error handling for the Hoox TUI dashboard.
8
+ *
9
+ * Auth headers come from the operator transport profile (Bearer + optional
10
+ * Cloudflare Access service-token headers). See `operator-transport.ts`.
11
+ *
12
+ * All fetch calls include a 5s timeout via AbortController.
13
+ * Network errors (fetch failures, timeouts) are retried with exponential backoff.
14
+ * HTTP 401/403 errors are NOT retried — they indicate auth/config issues.
15
+ * HTTP 429 (rate limit) triggers a backing-off warning.
16
+ */
17
+ import { type OperatorTransportEnv, type OperatorTransportProfile } from "./operator-transport";
18
+ export declare class WorkerAPIError extends Error {
19
+ status: number;
20
+ retryable: boolean;
21
+ constructor(message: string, options?: {
22
+ cause?: unknown;
23
+ status?: number;
24
+ retryable?: boolean;
25
+ });
26
+ }
27
+ export interface HooxFetchOptions extends RequestInit {
28
+ /** Injected transport profile (tests / multi-tenant callers). */
29
+ transport?: OperatorTransportProfile;
30
+ /** Env override when resolving transport (tests). */
31
+ transportEnv?: OperatorTransportEnv;
32
+ }
33
+ /**
34
+ * Perform an API fetch with timeout, retry logic, and error handling.
35
+ *
36
+ * Retry behavior:
37
+ * - Network errors (connection refused, DNS, timeout): up to MAX_RETRIES,
38
+ * with exponential backoff: 1s → 2s → 4s → ... capped at 16s.
39
+ * - HTTP 401/403: NO retry — fail immediately.
40
+ * - HTTP 429 (rate limited): NO retry — fail with rate limit info.
41
+ * - HTTP 5xx: retry once.
42
+ *
43
+ * @param path API path (e.g. "/v1/workers")
44
+ * @param options Fetch options (merged with auth header and timeout signal)
45
+ * @returns Parsed JSON response body
46
+ */
47
+ export declare function hooxFetch<T = unknown>(path: string, options?: HooxFetchOptions): Promise<T>;
48
+ //# sourceMappingURL=api-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;GAWG;AAEH,OAAO,EAIL,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC9B,MAAM,sBAAsB,CAAC;AAgB9B,qBAAa,cAAe,SAAQ,KAAK;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;gBAGjB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE;CAOtE;AA+CD,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACnD,iEAAiE;IACjE,SAAS,CAAC,EAAE,wBAAwB,CAAC;IACrC,qDAAqD;IACrD,YAAY,CAAC,EAAE,oBAAoB,CAAC;CACrC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,SAAS,CAAC,CAAC,GAAG,OAAO,EACzC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,CAAC,CAAC,CAsGZ"}
@@ -0,0 +1,191 @@
1
+ // @bun
2
+ var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, {
10
+ get: all[name],
11
+ enumerable: true,
12
+ configurable: true,
13
+ set: __exportSetter.bind(all, name)
14
+ });
15
+ };
16
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
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
+
81
+ // src/api-client.ts
82
+ var exports_api_client = {};
83
+ __export(exports_api_client, {
84
+ hooxFetch: () => hooxFetch,
85
+ WorkerAPIError: () => WorkerAPIError
86
+ });
87
+ function isRetryableStatus(status) {
88
+ if (status === 401 || status === 403 || status === 429)
89
+ return false;
90
+ if (status >= 500)
91
+ return true;
92
+ if (status === 408)
93
+ return true;
94
+ return false;
95
+ }
96
+ function isNetworkError(error) {
97
+ if (error instanceof Error) {
98
+ const msg = error.message.toLowerCase();
99
+ return error.name === "AbortError" || error.name === "TimeoutError" || msg.includes("fetch") || msg.includes("network") || msg.includes("econnrefused") || msg.includes("enotfound") || msg.includes("dns") || msg.includes("timeout");
100
+ }
101
+ return false;
102
+ }
103
+ function sleep(ms) {
104
+ return new Promise((resolve) => setTimeout(resolve, ms));
105
+ }
106
+ function activeProfile(override) {
107
+ return override ?? resolveOperatorTransportProfile();
108
+ }
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 ?? {};
112
+ let lastError;
113
+ for (let attempt = 0;attempt <= MAX_RETRIES; attempt++) {
114
+ const controller = new AbortController;
115
+ const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
116
+ try {
117
+ const url = operatorUrl(profile, path);
118
+ const headers = {
119
+ "Content-Type": "application/json",
120
+ ...buildOperatorAuthHeaders(profile),
121
+ ...fetchInit.headers
122
+ };
123
+ const response = await fetch(url, {
124
+ ...fetchInit,
125
+ headers,
126
+ signal: controller.signal
127
+ });
128
+ if (response.status === 429) {
129
+ throw new WorkerAPIError("API rate limited \u2014 backing off", {
130
+ status: 429,
131
+ retryable: false
132
+ });
133
+ }
134
+ if (response.status === 401 || response.status === 403) {
135
+ throw new WorkerAPIError(`Authentication failed (HTTP ${response.status})`, { status: response.status, retryable: false });
136
+ }
137
+ if (!response.ok) {
138
+ if (attempt < MAX_RETRIES && isRetryableStatus(response.status)) {
139
+ const delay = Math.min(BASE_RETRY_DELAY_MS * Math.pow(2, attempt), MAX_RETRY_DELAY_MS);
140
+ await sleep(delay);
141
+ continue;
142
+ }
143
+ throw new WorkerAPIError(`HTTP ${response.status}: ${response.statusText}`, {
144
+ status: response.status,
145
+ retryable: isRetryableStatus(response.status)
146
+ });
147
+ }
148
+ return await response.json();
149
+ } catch (error) {
150
+ lastError = error;
151
+ if (error instanceof WorkerAPIError && !error.retryable) {
152
+ throw error;
153
+ }
154
+ if (attempt < MAX_RETRIES && isNetworkError(error)) {
155
+ const delay = Math.min(BASE_RETRY_DELAY_MS * Math.pow(2, attempt), MAX_RETRY_DELAY_MS);
156
+ await sleep(delay);
157
+ continue;
158
+ }
159
+ if (!(error instanceof WorkerAPIError)) {
160
+ throw new WorkerAPIError(`Network request failed: ${error.message}`, { cause: error, retryable: false, status: 0 });
161
+ }
162
+ throw error;
163
+ } finally {
164
+ clearTimeout(timeoutId);
165
+ }
166
+ }
167
+ throw new WorkerAPIError("Max retries exhausted", {
168
+ cause: lastError,
169
+ retryable: false
170
+ });
171
+ }
172
+ var FETCH_TIMEOUT_MS = 5000, MAX_RETRIES = 3, BASE_RETRY_DELAY_MS = 1000, MAX_RETRY_DELAY_MS = 16000, WorkerAPIError;
173
+ var init_api_client = __esm(() => {
174
+ init_operator_transport();
175
+ WorkerAPIError = class WorkerAPIError extends Error {
176
+ status;
177
+ retryable;
178
+ constructor(message, options) {
179
+ super(message, { cause: options?.cause });
180
+ this.name = "WorkerAPIError";
181
+ this.status = options?.status ?? 0;
182
+ this.retryable = options?.retryable ?? false;
183
+ }
184
+ };
185
+ });
186
+ init_api_client();
187
+
188
+ export {
189
+ hooxFetch,
190
+ WorkerAPIError
191
+ };