@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/session.js CHANGED
@@ -16,13 +16,18 @@ var __export = (target, all) => {
16
16
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
17
 
18
18
  // src/path-utils.ts
19
+ import { existsSync } from "fs";
19
20
  import { homedir } from "os";
20
- import { join, resolve } from "path";
21
+ import { dirname, join, resolve } from "path";
21
22
  function createHooxPath(path) {
22
23
  return path;
23
24
  }
24
25
  function getHooxHome() {
25
26
  try {
27
+ const override = process.env.HOOX_HOME?.trim();
28
+ if (override) {
29
+ return createHooxPath(resolve(override));
30
+ }
26
31
  const home = homedir();
27
32
  if (!home || home.length === 0) {
28
33
  return createHooxPath(resolve(process.cwd(), ".hoox"));
@@ -32,6 +37,84 @@ function getHooxHome() {
32
37
  return createHooxPath(resolve(process.cwd(), ".hoox"));
33
38
  }
34
39
  }
40
+ function isHooxSetupRoot(dir) {
41
+ if (!dir)
42
+ return false;
43
+ try {
44
+ const root = resolve(dir);
45
+ return existsSync(join(root, "wrangler.jsonc")) && existsSync(join(root, "packages", "cli", "package.json"));
46
+ } catch {
47
+ return false;
48
+ }
49
+ }
50
+ function findHooxSetupRoot(startDir = process.cwd()) {
51
+ let dir = resolve(startDir);
52
+ for (;; ) {
53
+ if (isHooxSetupRoot(dir))
54
+ return dir;
55
+ const parent = dirname(dir);
56
+ if (parent === dir)
57
+ return null;
58
+ dir = parent;
59
+ }
60
+ }
61
+ function resolveHooxRuntimeRoot(options) {
62
+ const env = options?.env ?? process.env;
63
+ const cwd = resolve(options?.cwd ?? process.cwd());
64
+ const globalRepo = getHooxRepoPath();
65
+ const envRepo = env.HOOX_REPO?.trim();
66
+ if (envRepo) {
67
+ const resolved = resolve(envRepo);
68
+ if (isHooxSetupRoot(resolved)) {
69
+ return {
70
+ root: resolved,
71
+ source: "env",
72
+ checked: {
73
+ env: resolved,
74
+ cwd: findHooxSetupRoot(cwd),
75
+ global: globalRepo
76
+ }
77
+ };
78
+ }
79
+ return {
80
+ root: null,
81
+ source: "env",
82
+ checked: {
83
+ env: resolved,
84
+ cwd: findHooxSetupRoot(cwd),
85
+ global: globalRepo
86
+ }
87
+ };
88
+ }
89
+ const local = findHooxSetupRoot(cwd);
90
+ if (local) {
91
+ return {
92
+ root: local,
93
+ source: "cwd",
94
+ checked: { cwd: local, global: globalRepo }
95
+ };
96
+ }
97
+ if (isHooxSetupRoot(globalRepo)) {
98
+ return {
99
+ root: globalRepo,
100
+ source: "global",
101
+ checked: { cwd: null, global: globalRepo }
102
+ };
103
+ }
104
+ return {
105
+ root: null,
106
+ source: "none",
107
+ checked: { cwd: null, global: globalRepo }
108
+ };
109
+ }
110
+ function getTuiEntryCandidates(runtimeRoot) {
111
+ const root = resolve(runtimeRoot);
112
+ return [
113
+ join(root, "packages", "tui", "src", "main.tsx"),
114
+ join(root, "packages", "tui", "dist", "main.js"),
115
+ join(root, "packages", "tui", "src", "main.ts")
116
+ ];
117
+ }
35
118
  function resolveHooxPath(relativePath) {
36
119
  if (!relativePath || typeof relativePath !== "string") {
37
120
  throw new Error("relativePath must be a non-empty string");
@@ -90,6 +173,17 @@ function getHooxStatePath() {
90
173
  }
91
174
 
92
175
  // src/session.ts
176
+ import { z } from "zod";
177
+ var SessionStateFileSchema = z.object({
178
+ activeView: z.string().optional(),
179
+ sidebarExpanded: z.boolean().optional(),
180
+ windowSize: z.object({
181
+ cols: z.number().int().positive(),
182
+ rows: z.number().int().positive()
183
+ }).optional(),
184
+ lastData: z.number().nonnegative().optional(),
185
+ savedAt: z.string().optional()
186
+ }).strict();
93
187
  var TUI_STATE_DIR = ".tui-state";
94
188
  var SESSION_FILE = (() => {
95
189
  try {
@@ -127,7 +221,8 @@ async function restoreSession() {
127
221
  if (!exists)
128
222
  return { ...DEFAULT_SESSION };
129
223
  const raw = await file.text();
130
- const parsed = JSON.parse(raw);
224
+ const result = SessionStateFileSchema.safeParse(JSON.parse(raw));
225
+ const parsed = result.success ? result.data : {};
131
226
  return {
132
227
  activeView: validateViewId(parsed.activeView) ? parsed.activeView : DEFAULT_SESSION.activeView,
133
228
  sidebarExpanded: typeof parsed.sidebarExpanded === "boolean" ? parsed.sidebarExpanded : DEFAULT_SESSION.sidebarExpanded,
package/dist/sse.d.ts CHANGED
@@ -1,26 +1,35 @@
1
1
  /**
2
- * SSE Streaming Client — subscribes to Server-Sent Events from hoox-setup API.
2
+ * SSE Streaming Client — subscribes to Server-Sent Events from the Hoox
3
+ * operator management API.
3
4
  *
4
5
  * Uses Bun's native fetch with ReadableStream reader for SSE parsing.
5
6
  * Supports reconnection with exponential backoff.
6
7
  * Events must be JSON-encoded in the "data:" field.
8
+ *
9
+ * Auth: operator transport profile (Bearer + optional Access service token).
7
10
  */
11
+ import { type OperatorTransportEnv, type OperatorTransportProfile } from "./operator-transport";
8
12
  export type SSECallback<T> = (event: T) => void;
9
13
  export type SSEStatusCallback = (status: "connected" | "reconnecting" | "disconnected") => void;
10
14
  interface SSESubscription {
11
15
  abort: () => void;
12
16
  }
17
+ export interface SubscribeSSEOptions {
18
+ transport?: OperatorTransportProfile;
19
+ transportEnv?: OperatorTransportEnv;
20
+ }
13
21
  /**
14
22
  * Subscribe to an SSE endpoint and invoke the callback for each parsed event.
15
23
  *
16
24
  * Automatically reconnects on connection loss with exponential backoff.
17
25
  * Returns an object with an `abort()` method to cancel the subscription.
18
26
  *
19
- * @param path API path (e.g. "/trades/stream")
27
+ * @param path API path (e.g. "/v1/trades/stream")
20
28
  * @param callback Called with each parsed event of type T
21
29
  * @param onStatus Optional callback for connection status updates
30
+ * @param options Optional transport profile override
22
31
  * @returns Subscription controller with abort method
23
32
  */
24
- export declare function subscribeSSE<T>(path: string, callback: SSECallback<T>, onStatus?: SSEStatusCallback): SSESubscription;
33
+ export declare function subscribeSSE<T>(path: string, callback: SSECallback<T>, onStatus?: SSEStatusCallback, options?: SubscribeSSEOptions): SSESubscription;
25
34
  export {};
26
35
  //# sourceMappingURL=sse.d.ts.map
package/dist/sse.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAYH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;AAChD,MAAM,MAAM,iBAAiB,GAAG,CAC9B,MAAM,EAAE,WAAW,GAAG,cAAc,GAAG,cAAc,KAClD,IAAI,CAAC;AAEV,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAC5B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,EACxB,QAAQ,CAAC,EAAE,iBAAiB,GAC3B,eAAe,CAgFjB"}
1
+ {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAIL,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC9B,MAAM,sBAAsB,CAAC;AAS9B,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;AAChD,MAAM,MAAM,iBAAiB,GAAG,CAC9B,MAAM,EAAE,WAAW,GAAG,cAAc,GAAG,cAAc,KAClD,IAAI,CAAC;AAEV,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,wBAAwB,CAAC;IACrC,YAAY,CAAC,EAAE,oBAAoB,CAAC;CACrC;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAC5B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,EACxB,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,OAAO,CAAC,EAAE,mBAAmB,GAC5B,eAAe,CAsFjB"}
package/dist/sse.js CHANGED
@@ -15,22 +15,86 @@ 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/sse.ts
19
82
  var exports_sse = {};
20
83
  __export(exports_sse, {
21
84
  subscribeSSE: () => subscribeSSE
22
85
  });
23
- function subscribeSSE(path, callback, onStatus) {
86
+ function subscribeSSE(path, callback, onStatus, options) {
24
87
  let aborted = false;
88
+ const profile = options?.transport ?? (options?.transportEnv ? resolveOperatorTransportProfile(options.transportEnv) : resolveOperatorTransportProfile());
25
89
  const run = async () => {
26
90
  let attempt = 0;
27
91
  while (!aborted && attempt < MAX_RECONNECT_ATTEMPTS) {
28
92
  try {
29
- const url = `${API_BASE}${path}`;
93
+ const url = operatorUrl(profile, path);
30
94
  const response = await fetch(url, {
31
95
  headers: {
32
96
  Accept: "text/event-stream",
33
- ...API_TOKEN ? { Authorization: `Bearer ${API_TOKEN}` } : {}
97
+ ...buildOperatorAuthHeaders(profile)
34
98
  }
35
99
  });
36
100
  if (!response.ok || !response.body) {
@@ -59,7 +123,7 @@ function subscribeSSE(path, callback, onStatus) {
59
123
  }
60
124
  }
61
125
  }
62
- } catch (_error) {
126
+ } catch {
63
127
  if (aborted)
64
128
  break;
65
129
  onStatus?.("reconnecting");
@@ -79,10 +143,9 @@ function subscribeSSE(path, callback, onStatus) {
79
143
  }
80
144
  };
81
145
  }
82
- var API_BASE, API_TOKEN, RECONNECT_BASE_MS = 1000, RECONNECT_MAX_MS = 16000, MAX_RECONNECT_ATTEMPTS = 10;
146
+ var RECONNECT_BASE_MS = 1000, RECONNECT_MAX_MS = 16000, MAX_RECONNECT_ATTEMPTS = 10;
83
147
  var init_sse = __esm(() => {
84
- API_BASE = process.env.HOOX_API_URL || "http://localhost:8787";
85
- API_TOKEN = process.env.HOOX_API_TOKEN || "";
148
+ init_operator_transport();
86
149
  });
87
150
  init_sse();
88
151
 
@@ -1 +1 @@
1
- {"version":3,"file":"config-store.d.ts","sourceRoot":"","sources":["../../src/stores/config-store.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AA2E3E,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,UAAU,EAAE,SAAS,CAAC;IACtB,aAAa,EAAE,uBAAuB,CAAC;IACvC,YAAY,EAAE,OAAO,CAAC;CACvB;AAID,UAAU,aAAa;IACrB,+DAA+D;IAC/D,YAAY,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IACtD,8CAA8C;IAC9C,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,yCAAyC;IACzC,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,8CAA8C;IAC9C,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,uBAAuB,KAAK,IAAI,CAAC;CACtE;AAiBD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;eAtClB,MAAM,GAAG,OAAO;2BACJ,MAAM;qBACZ,MAAM;;;;;;;;;;;;;;;;sBAKL,OAAO;sBAOP,CAAC,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI;uBAEtC,MAAM,IAAI;qBAEZ,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI;4BAE9B,CAAC,OAAO,EAAE,MAAM,uBAAuB,KAAK,IAAI;;;eApB7D,MAAM,GAAG,OAAO;2BACJ,MAAM;qBACZ,MAAM;;;;;;;;;;;;;;;;sBAKL,OAAO;sBAOP,CAAC,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI;uBAEtC,MAAM,IAAI;qBAEZ,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI;4BAE9B,CAAC,OAAO,EAAE,MAAM,uBAAuB,KAAK,IAAI;;EA+ErE,CAAC"}
1
+ {"version":3,"file":"config-store.d.ts","sourceRoot":"","sources":["../../src/stores/config-store.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAoF3E,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,UAAU,EAAE,SAAS,CAAC;IACtB,aAAa,EAAE,uBAAuB,CAAC;IACvC,YAAY,EAAE,OAAO,CAAC;CACvB;AAID,UAAU,aAAa;IACrB,+DAA+D;IAC/D,YAAY,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IACtD,8CAA8C;IAC9C,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,yCAAyC;IACzC,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,8CAA8C;IAC9C,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,uBAAuB,KAAK,IAAI,CAAC;CACtE;AAiBD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;eAtClB,MAAM,GAAG,OAAO;2BACJ,MAAM;qBACZ,MAAM;;;;;;;;;;;;;;;;sBAKL,OAAO;sBAOP,CAAC,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI;uBAEtC,MAAM,IAAI;qBAEZ,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI;4BAE9B,CAAC,OAAO,EAAE,MAAM,uBAAuB,KAAK,IAAI;;;eApB7D,MAAM,GAAG,OAAO;2BACJ,MAAM;qBACZ,MAAM;;;;;;;;;;;;;;;;sBAKL,OAAO;sBAOP,CAAC,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI;uBAEtC,MAAM,IAAI;qBAEZ,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI;4BAE9B,CAAC,OAAO,EAAE,MAAM,uBAAuB,KAAK,IAAI;;EA+ErE,CAAC"}
@@ -15,16 +15,133 @@ var __export = (target, all) => {
15
15
  };
16
16
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
17
 
18
+ // src/config.ts
19
+ import {
20
+ readFileSync,
21
+ writeFileSync,
22
+ existsSync,
23
+ mkdirSync,
24
+ chmodSync,
25
+ statSync
26
+ } from "fs";
27
+ import { join } from "path";
28
+ import { homedir } from "os";
29
+ var HOX_DIR = join(homedir(), ".hoox");
30
+ var CONFIG_PATH = join(HOX_DIR, "config.json");
31
+ var HOOX_DIR_MODE = 448;
32
+ var HOOX_CONFIG_FILE_MODE = 384;
33
+ var DEFAULT_CONFIG = {
34
+ apiUrl: "http://localhost:8787",
35
+ apiToken: "",
36
+ transport: "public",
37
+ refreshIntervalMs: 500,
38
+ theme: "dark",
39
+ activeExchanges: ["binance", "bybit", "mexc"],
40
+ notifications: {
41
+ alerts: true,
42
+ trades: true,
43
+ debug: false,
44
+ system: true
45
+ },
46
+ soundEnabled: true,
47
+ defaultView: "dashboard"
48
+ };
49
+ function readConfigSync() {
50
+ const envConfig = {
51
+ apiUrl: process.env.HOOX_API_URL,
52
+ apiToken: process.env.HOOX_API_TOKEN
53
+ };
54
+ let fileConfig = {};
55
+ try {
56
+ if (existsSync(CONFIG_PATH)) {
57
+ fileConfig = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
58
+ }
59
+ } catch {}
60
+ return {
61
+ ...DEFAULT_CONFIG,
62
+ ...fileConfig,
63
+ ...Object.fromEntries(Object.entries(envConfig).filter(([_, v]) => v !== undefined))
64
+ };
65
+ }
66
+ async function readConfig() {
67
+ return readConfigSync();
68
+ }
69
+ function ensureHooxDirSecure(dir = HOX_DIR) {
70
+ if (!existsSync(dir)) {
71
+ mkdirSync(dir, { recursive: true, mode: HOOX_DIR_MODE });
72
+ }
73
+ try {
74
+ chmodSync(dir, HOOX_DIR_MODE);
75
+ } catch {}
76
+ }
77
+ function writeSecureFileSync(filePath, contents, mode = HOOX_CONFIG_FILE_MODE) {
78
+ writeFileSync(filePath, contents, { encoding: "utf-8", mode });
79
+ try {
80
+ chmodSync(filePath, mode);
81
+ } catch {}
82
+ }
83
+ function isUnixModeGroupOrWorldAccessible(mode) {
84
+ return (mode & 63) !== 0;
85
+ }
86
+ function isConfigWorldOrGroupReadable(filePath = CONFIG_PATH) {
87
+ try {
88
+ if (!existsSync(filePath))
89
+ return false;
90
+ return isUnixModeGroupOrWorldAccessible(statSync(filePath).mode & 511);
91
+ } catch {
92
+ return false;
93
+ }
94
+ }
95
+ function formatConfigPermissionWarning(filePath = CONFIG_PATH) {
96
+ if (!isConfigWorldOrGroupReadable(filePath))
97
+ return null;
98
+ 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.`;
99
+ }
100
+ function writeConfigSync(config) {
101
+ try {
102
+ ensureHooxDirSecure(HOX_DIR);
103
+ writeSecureFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), HOOX_CONFIG_FILE_MODE);
104
+ } catch (err) {
105
+ console.error("Failed to write config:", err);
106
+ }
107
+ }
108
+ async function writeConfig(config) {
109
+ writeConfigSync(config);
110
+ }
111
+ var VALID_TRANSPORTS = new Set([
112
+ "public",
113
+ "access",
114
+ "mtls",
115
+ "tunnel"
116
+ ]);
117
+ function validateConfig(config) {
118
+ const errors = [];
119
+ if (config.apiUrl && !config.apiUrl.startsWith("http")) {
120
+ errors.push("apiUrl must start with http:// or https://");
121
+ }
122
+ if (config.refreshIntervalMs !== undefined && config.refreshIntervalMs < 100) {
123
+ errors.push("refreshIntervalMs must be >= 100ms");
124
+ }
125
+ if (config.theme && !["dark", "light"].includes(config.theme)) {
126
+ errors.push('theme must be "dark" or "light"');
127
+ }
128
+ if (config.transport !== undefined && !VALID_TRANSPORTS.has(config.transport)) {
129
+ errors.push('transport must be "public" | "access" | "mtls" | "tunnel"');
130
+ }
131
+ return errors;
132
+ }
133
+
18
134
  // src/stores/config-store.ts
19
135
  import { create } from "zustand";
20
136
  import { immer } from "zustand/middleware/immer";
21
137
  import { persist, createJSONStorage } from "zustand/middleware";
22
- import { homedir } from "os";
23
- import { join } from "path";
138
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync2, existsSync as existsSync2 } from "fs";
139
+ import { homedir as homedir2 } from "os";
140
+ import { join as join2 } from "path";
24
141
  var bunConfigStorage = {
25
142
  getItem: async (_name) => {
26
143
  try {
27
- const file = Bun.file(join(homedir(), ".hoox", "config.json"));
144
+ const file = Bun.file(join2(homedir2(), ".hoox", "config.json"));
28
145
  const exists = await file.exists();
29
146
  if (!exists)
30
147
  return null;
@@ -34,12 +151,18 @@ var bunConfigStorage = {
34
151
  }
35
152
  },
36
153
  setItem: async (_name, value) => {
37
- const dir = join(homedir(), ".hoox");
154
+ const dir = join2(homedir2(), ".hoox");
155
+ if (!existsSync2(dir)) {
156
+ mkdirSync2(dir, { recursive: true, mode: HOOX_DIR_MODE });
157
+ }
38
158
  try {
39
- await Bun.file(dir).exists();
159
+ chmodSync2(dir, HOOX_DIR_MODE);
40
160
  } catch {}
41
- const filePath = join(dir, "config.json");
161
+ const filePath = join2(dir, "config.json");
42
162
  await Bun.write(filePath, value);
163
+ try {
164
+ chmodSync2(filePath, HOOX_CONFIG_FILE_MODE);
165
+ } catch {}
43
166
  },
44
167
  removeItem: async (_name) => {}
45
168
  };
@@ -1 +1 @@
1
- {"version":3,"file":"service-store.d.ts","sourceRoot":"","sources":["../../src/stores/service-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"service-store.d.ts","sourceRoot":"","sources":["../../src/stores/service-store.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EACV,UAAU,EACV,KAAK,EACL,KAAK,EACL,QAAQ,EACR,aAAa,EACb,gBAAgB,EAChB,eAAe,EAChB,MAAM,UAAU,CAAC;AAwClB,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,WAAW,EAAE,KAAK,EAAE,CAAC;IACrB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC;IAC9B,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAGhC,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB;;;;;OAKG;IACH,gBAAgB,EAAE,eAAe,GAAG,IAAI,CAAC;IACzC,sDAAsD;IACtD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,4DAA4D;IAC5D,cAAc,EAAE,MAAM,CAAC;IACvB,wEAAwE;IACxE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAID,UAAU,cAAc;IACtB,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC;IACrC,mBAAmB,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACxD,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC1C,UAAU,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,IAAI,CAAC;IAC5C,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAClC,OAAO,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,IAAI,CAAC;IACjC,UAAU,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;IAG7C,6DAA6D;IAC7D,uBAAuB,EAAE,MAAM,IAAI,CAAC;IACpC,gEAAgE;IAChE,uBAAuB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACxD,0DAA0D;IAC1D,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,kDAAkD;IAClD,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB;;;;OAIG;IACH,mBAAmB,EAAE,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,KAAK,IAAI,CAAC;IAC/D;;;;OAIG;IACH,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB;;;;OAIG;IACH,gBAAgB,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;CACtD;AA0CD,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BA5GR,gBAAgB;qBACrB,MAAM;0BACD,MAAM,GAAG,IAAI;oBAInB,MAAM;mBAEP,MAAM,GAAG,IAAI;;;;;;;;;;6BASH,MAAM;wBAEX,MAAM;wBAEN,MAAM,GAAG,IAAI;sBAMf,MAAM,OAAO,CAAC,IAAI,CAAC;sBACnB,MAAM,OAAO,CAAC,IAAI,CAAC;oBACrB,MAAM,OAAO,CAAC,IAAI,CAAC;kBACrB,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI;mBACrB,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,IAAI;6BACf,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI;sBACzC,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI;oBAC7B,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,IAAI;mBAChC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI;iBACxB,CAAC,GAAG,EAAE,QAAQ,KAAK,IAAI;oBACpB,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI;iCAInB,MAAM,IAAI;iCAEV,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI;sBAEzC,MAAM,IAAI;oBAEZ,MAAM,IAAI;6BAMD,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,KAAK,IAAI;oBAMlD,MAAM,IAAI;0BAMJ,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAjElC,gBAAgB;qBACrB,MAAM;0BACD,MAAM,GAAG,IAAI;oBAInB,MAAM;mBAEP,MAAM,GAAG,IAAI;;;;;;;;;;6BASH,MAAM;wBAEX,MAAM;wBAEN,MAAM,GAAG,IAAI;sBAMf,MAAM,OAAO,CAAC,IAAI,CAAC;sBACnB,MAAM,OAAO,CAAC,IAAI,CAAC;oBACrB,MAAM,OAAO,CAAC,IAAI,CAAC;kBACrB,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI;mBACrB,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,IAAI;6BACf,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI;sBACzC,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI;oBAC7B,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,IAAI;mBAChC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI;iBACxB,CAAC,GAAG,EAAE,QAAQ,KAAK,IAAI;oBACpB,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI;iCAInB,MAAM,IAAI;iCAEV,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI;sBAEzC,MAAM,IAAI;oBAEZ,MAAM,IAAI;6BAMD,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,KAAK,IAAI;oBAMlD,MAAM,IAAI;0BAMJ,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI;;EA4UrD,CAAC"}