@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
@@ -0,0 +1,181 @@
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/middleware/rate-limit.ts
19
+ function createStorage(kv) {
20
+ if (!kv) {
21
+ const memory = new Map;
22
+ return {
23
+ async get(key) {
24
+ const entry = memory.get(key);
25
+ if (!entry)
26
+ return null;
27
+ if (Date.now() > entry.expiresAt) {
28
+ memory.delete(key);
29
+ return null;
30
+ }
31
+ return entry.value;
32
+ },
33
+ async put(key, value, opts) {
34
+ const ttl = opts?.expirationTtl ?? 0;
35
+ const expiresAt = ttl > 0 ? Date.now() + ttl * 1000 : Infinity;
36
+ memory.set(key, { value, expiresAt });
37
+ },
38
+ async incr(key, opts) {
39
+ let entry = memory.get(key);
40
+ let newValue;
41
+ let attempts = 0;
42
+ const maxAttempts = 100;
43
+ while (true) {
44
+ const count = entry ? parseInt(entry.value, 10) : 0;
45
+ newValue = count + 1;
46
+ if (!entry) {
47
+ const ttl = opts?.expirationTtl ?? 0;
48
+ const expiresAt = ttl > 0 ? Date.now() + ttl * 1000 : Infinity;
49
+ if (memory.get(key) === undefined) {
50
+ memory.set(key, { value: String(newValue), expiresAt });
51
+ return newValue;
52
+ }
53
+ entry = memory.get(key);
54
+ } else {
55
+ if (Date.now() > entry.expiresAt) {
56
+ memory.delete(key);
57
+ entry = undefined;
58
+ continue;
59
+ }
60
+ const currentEntry = memory.get(key);
61
+ if (currentEntry === entry) {
62
+ const ttl = opts?.expirationTtl ?? 0;
63
+ const expiresAt = ttl > 0 ? Date.now() + ttl * 1000 : Infinity;
64
+ memory.set(key, { value: String(newValue), expiresAt });
65
+ return newValue;
66
+ }
67
+ entry = currentEntry;
68
+ }
69
+ attempts++;
70
+ if (attempts >= maxAttempts) {
71
+ const current = await this.get(key);
72
+ const count2 = current ? parseInt(current, 10) : 0;
73
+ newValue = count2 + 1;
74
+ const ttl = opts?.expirationTtl ?? 0;
75
+ const expiresAt = ttl > 0 ? Date.now() + ttl * 1000 : Infinity;
76
+ memory.set(key, { value: String(newValue), expiresAt });
77
+ return newValue;
78
+ }
79
+ }
80
+ }
81
+ };
82
+ }
83
+ return {
84
+ async get(key) {
85
+ return kv.get(key);
86
+ },
87
+ async put(key, value, opts) {
88
+ await kv.put(key, value, opts);
89
+ },
90
+ async incr(key, opts) {
91
+ const current = await kv.get(key);
92
+ const count = current ? parseInt(current, 10) : 0;
93
+ const next = count + 1;
94
+ await kv.put(key, String(next), opts);
95
+ return next;
96
+ }
97
+ };
98
+ }
99
+ function createRateLimiter(kv, config) {
100
+ const prefix = config.keyPrefix ?? "rate-limit";
101
+ const storage = createStorage(kv);
102
+ function getClientIp(request) {
103
+ return request.headers.get("CF-Connecting-IP") ?? "unknown";
104
+ }
105
+ function getWindowKey(ip) {
106
+ const windowStart = Math.floor(Date.now() / (config.windowSeconds * 1000));
107
+ return `${prefix}:${ip}:${windowStart}`;
108
+ }
109
+ async function checkWithKey(fullKey) {
110
+ const current = await storage.get(fullKey);
111
+ const count = current ? parseInt(current, 10) : 0;
112
+ if (count >= config.maxRequests) {
113
+ const retryAfter = Math.ceil(config.windowSeconds - Date.now() % (config.windowSeconds * 1000) / 1000);
114
+ return {
115
+ allowed: false,
116
+ remaining: 0,
117
+ retryAfter
118
+ };
119
+ }
120
+ const newCount = await storage.incr(fullKey, {
121
+ expirationTtl: config.windowSeconds
122
+ });
123
+ if (newCount > config.maxRequests) {
124
+ const retryAfter = Math.ceil(config.windowSeconds - Date.now() % (config.windowSeconds * 1000) / 1000);
125
+ return {
126
+ allowed: false,
127
+ remaining: 0,
128
+ retryAfter
129
+ };
130
+ }
131
+ return {
132
+ allowed: true,
133
+ remaining: config.maxRequests - newCount
134
+ };
135
+ }
136
+ async function check(request) {
137
+ const ip = getClientIp(request);
138
+ const key = getWindowKey(ip);
139
+ return checkWithKey(key);
140
+ }
141
+ async function enforce(request) {
142
+ const result = await check(request);
143
+ if (!result.allowed) {
144
+ return new Response(JSON.stringify({
145
+ error: "Rate limit exceeded",
146
+ retryAfter: result.retryAfter
147
+ }), {
148
+ status: 429,
149
+ headers: {
150
+ "Content-Type": "application/json",
151
+ "Retry-After": String(result.retryAfter ?? config.windowSeconds)
152
+ }
153
+ });
154
+ }
155
+ return null;
156
+ }
157
+ async function checkKey(key) {
158
+ const fullKey = `${prefix}:${key}`;
159
+ return checkWithKey(fullKey);
160
+ }
161
+ async function enforceKey(key) {
162
+ const result = await checkKey(key);
163
+ if (!result.allowed) {
164
+ return new Response(JSON.stringify({
165
+ error: "Rate limit exceeded",
166
+ retryAfter: result.retryAfter
167
+ }), {
168
+ status: 429,
169
+ headers: {
170
+ "Content-Type": "application/json",
171
+ "Retry-After": String(result.retryAfter ?? config.windowSeconds)
172
+ }
173
+ });
174
+ }
175
+ return null;
176
+ }
177
+ return { check, enforce, checkKey, enforceKey };
178
+ }
179
+ export {
180
+ createRateLimiter
181
+ };
@@ -0,0 +1,58 @@
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/middleware/security-headers.ts
19
+ var SECURITY_HEADERS_DEFAULTS = {
20
+ xContentTypeOptions: "nosniff",
21
+ xFrameOptions: "DENY",
22
+ xXssProtection: "1; mode=block",
23
+ referrerPolicy: "strict-origin-when-cross-origin",
24
+ permissionsPolicy: "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()",
25
+ strictTransportSecurity: "max-age=31536000; includeSubDomains",
26
+ contentSecurityPolicy: "default-src 'self'"
27
+ };
28
+ function secureHeaders(options) {
29
+ const opts = { ...SECURITY_HEADERS_DEFAULTS, ...options };
30
+ const headers = {
31
+ "X-Content-Type-Options": opts.xContentTypeOptions,
32
+ "X-Frame-Options": opts.xFrameOptions,
33
+ "X-XSS-Protection": opts.xXssProtection,
34
+ "Referrer-Policy": opts.referrerPolicy,
35
+ "Permissions-Policy": opts.permissionsPolicy,
36
+ "Strict-Transport-Security": opts.strictTransportSecurity
37
+ };
38
+ if (opts.contentSecurityPolicy) {
39
+ headers["Content-Security-Policy"] = opts.contentSecurityPolicy;
40
+ }
41
+ return headers;
42
+ }
43
+ function wrapWithSecurityHeaders(response, options) {
44
+ const headers = new Headers(response.headers);
45
+ for (const [key, value] of Object.entries(secureHeaders(options))) {
46
+ headers.set(key, value);
47
+ }
48
+ return new Response(response.body, {
49
+ status: response.status,
50
+ statusText: response.statusText,
51
+ headers
52
+ });
53
+ }
54
+ export {
55
+ wrapWithSecurityHeaders,
56
+ secureHeaders,
57
+ SECURITY_HEADERS_DEFAULTS
58
+ };
@@ -0,0 +1,56 @@
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/middleware/validate.ts
19
+ function validateJson(schema, data) {
20
+ const result = schema.safeParse(data);
21
+ if (!result.success) {
22
+ return {
23
+ ok: false,
24
+ error: result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")
25
+ };
26
+ }
27
+ return { ok: true, value: result.data };
28
+ }
29
+ async function validateJsonLegacy(request) {
30
+ try {
31
+ const body = await request.json();
32
+ if (typeof body !== "object" || body === null || Array.isArray(body)) {
33
+ return { ok: false, error: "Request body must be a JSON object" };
34
+ }
35
+ return { ok: true, value: body };
36
+ } catch {
37
+ return { ok: false, error: "Invalid JSON in request body" };
38
+ }
39
+ }
40
+ function requireField(body, field) {
41
+ if (!(field in body)) {
42
+ return { ok: false, error: `Missing required field: ${field}` };
43
+ }
44
+ return { ok: true, value: body[field] };
45
+ }
46
+ function optionalField(body, field, defaultValue) {
47
+ if (!(field in body))
48
+ return defaultValue;
49
+ return body[field];
50
+ }
51
+ export {
52
+ validateJsonLegacy,
53
+ validateJson,
54
+ requireField,
55
+ optionalField
56
+ };
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Operator transport profile — how CLI/TUI clients reach the management plane.
3
+ *
4
+ * Transports:
5
+ * public — HTTPS + optional Bearer (default)
6
+ * access — Cloudflare Access service-token headers (+ optional Bearer)
7
+ * mtls — reserved (Enterprise); headers same as public until TLS certs land
8
+ * tunnel — reserved (private hostname); headers same as public
9
+ *
10
+ * Client maps HOOX_API_TOKEN → Authorization: Bearer …
11
+ * Server maps OPERATOR_API_KEY (preferred) or INTERNAL_API_KEY → requireOperatorAuth
12
+ */
13
+ export type OperatorTransport = "public" | "access" | "mtls" | "tunnel";
14
+ export interface OperatorTransportProfile {
15
+ transport: OperatorTransport;
16
+ /** API base URL without trailing slash */
17
+ apiBase: string;
18
+ /** Bearer token (HOOX_API_TOKEN); empty if unset */
19
+ bearerToken: string;
20
+ /** CF Access service token client id */
21
+ accessClientId: string;
22
+ /** CF Access service token client secret */
23
+ accessClientSecret: string;
24
+ }
25
+ export interface OperatorTransportEnv {
26
+ HOOX_API_URL?: string;
27
+ HOOX_API_TOKEN?: string;
28
+ HOOX_TRANSPORT?: string;
29
+ CF_ACCESS_CLIENT_ID?: string;
30
+ CF_ACCESS_CLIENT_SECRET?: string;
31
+ [key: string]: string | undefined;
32
+ }
33
+ export interface ResolveOperatorTransportOptions {
34
+ /**
35
+ * Fallback transport when HOOX_TRANSPORT is unset
36
+ * (e.g. from ~/.hoox/config.json).
37
+ */
38
+ configTransport?: string;
39
+ /** Fallback API base when HOOX_API_URL is unset. */
40
+ configApiUrl?: string;
41
+ /** Fallback bearer when HOOX_API_TOKEN is unset. */
42
+ configApiToken?: string;
43
+ }
44
+ /**
45
+ * Resolve operator transport from env (process.env by default).
46
+ * Pure + injectable for tests. Optional config fallbacks for file-backed prefs.
47
+ */
48
+ export declare function resolveOperatorTransportProfile(env?: OperatorTransportEnv, options?: ResolveOperatorTransportOptions): OperatorTransportProfile;
49
+ /**
50
+ * Build HTTP headers for operator management requests.
51
+ * Never logs secrets; callers must not print the result in debug without redaction.
52
+ */
53
+ export declare function buildOperatorAuthHeaders(profile: OperatorTransportProfile): Record<string, string>;
54
+ /** True when the profile has any client-side operator credential. */
55
+ export declare function hasOperatorClientCredentials(profile: OperatorTransportProfile): boolean;
56
+ /**
57
+ * Join base URL and path without double slashes.
58
+ * Paths should start with `/`.
59
+ */
60
+ export declare function operatorUrl(profile: OperatorTransportProfile, path: string): string;
61
+ //# sourceMappingURL=operator-transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operator-transport.d.ts","sourceRoot":"","sources":["../src/operator-transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;AAExE,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,iBAAiB,CAAC;IAC7B,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,cAAc,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAoB;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACnC;AAwBD,MAAM,WAAW,+BAA+B;IAC9C;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oDAAoD;IACpD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,+BAA+B,CAC7C,GAAG,GAAE,oBAA0D,EAC/D,OAAO,GAAE,+BAAoC,GAC5C,wBAAwB,CAyB1B;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,wBAAwB,GAChC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAuBxB;AAED,qEAAqE;AACrE,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAGT;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,wBAAwB,EACjC,IAAI,EAAE,MAAM,GACX,MAAM,CAIR"}
@@ -0,0 +1,87 @@
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
+ init_operator_transport();
81
+
82
+ export {
83
+ resolveOperatorTransportProfile,
84
+ operatorUrl,
85
+ hasOperatorClientCredentials,
86
+ buildOperatorAuthHeaders
87
+ };
@@ -5,6 +5,17 @@
5
5
  * and constructing type-safe paths within it.
6
6
  *
7
7
  * Supports macOS, Linux, and Windows with proper fallback handling.
8
+ *
9
+ * Runtime layout:
10
+ * $HOME/.hoox/ — getHooxHome() (override with HOOX_HOME)
11
+ * $HOME/.hoox/repo/ — managed clone of hoox-setup (getHooxRepoPath)
12
+ * $HOME/.hoox/config/ — user config
13
+ * $HOME/.hoox/data/ — persistent state
14
+ *
15
+ * Tool/runtime resolution (resolveHooxRuntimeRoot):
16
+ * 1. HOOX_REPO env (explicit monorepo path)
17
+ * 2. Walk up from cwd for a local hoox-setup checkout
18
+ * 3. $HOME/.hoox/repo (global managed clone)
8
19
  */
9
20
  /**
10
21
  * Branded type for Hoox paths to prevent accidental string usage.
@@ -13,11 +24,27 @@
13
24
  export type HooxPath = string & {
14
25
  readonly __brand: "HooxPath";
15
26
  };
27
+ /** Where resolveHooxRuntimeRoot found (or failed to find) a setup monorepo. */
28
+ export type RuntimeRootSource = "env" | "cwd" | "global" | "none";
29
+ /** Result of resolveHooxRuntimeRoot(). */
30
+ export interface RuntimeRootResult {
31
+ /** Absolute monorepo root, or null if none found. */
32
+ root: string | null;
33
+ /** Which resolution step produced the result. */
34
+ source: RuntimeRootSource;
35
+ /** Paths inspected (for doctor / error messages). */
36
+ checked: {
37
+ env?: string;
38
+ cwd: string | null;
39
+ global: string;
40
+ };
41
+ }
16
42
  /**
17
43
  * Gets the Hoox home directory location: $HOME/.hoox
18
44
  *
19
45
  * Behavior:
20
- * - Returns $HOME/.hoox on macOS, Linux, Windows
46
+ * - HOOX_HOME env wins when set (absolute or relative, then resolved)
47
+ * - Else $HOME/.hoox on macOS, Linux, Windows
21
48
  * - Falls back to current working directory if HOME is not available
22
49
  * - Resolves to absolute path
23
50
  *
@@ -33,6 +60,37 @@ export type HooxPath = string & {
33
60
  * ```
34
61
  */
35
62
  export declare function getHooxHome(): HooxPath;
63
+ /**
64
+ * True when `dir` looks like a hoox-setup monorepo root.
65
+ *
66
+ * Markers match CLI verifyRepoRoot: root wrangler.jsonc + packages/cli package.
67
+ */
68
+ export declare function isHooxSetupRoot(dir: string): boolean;
69
+ /**
70
+ * Walk up from `startDir` looking for a hoox-setup monorepo root.
71
+ *
72
+ * @returns Absolute root path, or null if not found
73
+ */
74
+ export declare function findHooxSetupRoot(startDir?: string): string | null;
75
+ /**
76
+ * Resolve the Hoox tool/runtime monorepo root (local checkout or global clone).
77
+ *
78
+ * Order:
79
+ * 1. HOOX_REPO — must pass isHooxSetupRoot or result is source "env" with root null
80
+ * 2. Walk up from cwd
81
+ * 3. getHooxRepoPath() ($HOME/.hoox/repo or $HOOX_HOME/repo)
82
+ *
83
+ * Project cwd and tool root are intentionally separate: a random project
84
+ * directory can still use the global runtime for TUI / templates.
85
+ */
86
+ export declare function resolveHooxRuntimeRoot(options?: {
87
+ cwd?: string;
88
+ env?: NodeJS.ProcessEnv;
89
+ }): RuntimeRootResult;
90
+ /**
91
+ * Candidate TUI entry files under a monorepo root (source first, then dist).
92
+ */
93
+ export declare function getTuiEntryCandidates(runtimeRoot: string): string[];
36
94
  /**
37
95
  * Resolves a relative path within the Hoox home directory.
38
96
  *
@@ -1 +1 @@
1
- {"version":3,"file":"path-utils.d.ts","sourceRoot":"","sources":["../src/path-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAA;CAAE,CAAC;AAUjE;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,WAAW,IAAI,QAAQ,CAYtC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,QAAQ,CAiC9D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAStD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkBvE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,IAAI,QAAQ,CAE1C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,IAAI,QAAQ,CAE3C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,IAAI,QAAQ,CAEzC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,IAAI,QAAQ,CAE9C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,IAAI,QAAQ,CAE3C"}
1
+ {"version":3,"file":"path-utils.d.ts","sourceRoot":"","sources":["../src/path-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAMH;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAA;CAAE,CAAC;AAEjE,+EAA+E;AAC/E,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAElE,0CAA0C;AAC1C,MAAM,WAAW,iBAAiB;IAChC,qDAAqD;IACrD,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,iDAAiD;IACjD,MAAM,EAAE,iBAAiB,CAAC;IAC1B,qDAAqD;IACrD,OAAO,EAAE;QACP,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAUD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,WAAW,IAAI,QAAQ,CAgBtC;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAWpD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,GAAE,MAAsB,GAC/B,MAAM,GAAG,IAAI,CAQf;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,CAAC,EAAE;IAC/C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB,GAAG,iBAAiB,CAoDpB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAOnE;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,QAAQ,CAiC9D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAStD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkBvE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,IAAI,QAAQ,CAE1C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,IAAI,QAAQ,CAE3C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,IAAI,QAAQ,CAEzC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,IAAI,QAAQ,CAE9C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,IAAI,QAAQ,CAE3C"}
@@ -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");
@@ -89,13 +172,17 @@ function getHooxStatePath() {
89
172
  return resolveHooxPath("data/state.json");
90
173
  }
91
174
  export {
175
+ resolveHooxRuntimeRoot,
92
176
  resolveHooxPath,
93
177
  isWithinHooxHome,
178
+ isHooxSetupRoot,
179
+ getTuiEntryCandidates,
94
180
  getRelativeHooxPath,
95
181
  getHooxWranglerPath,
96
182
  getHooxStatePath,
97
183
  getHooxRepoPath,
98
184
  getHooxHome,
99
185
  getHooxDataDir,
100
- getHooxConfigDir
186
+ getHooxConfigDir,
187
+ findHooxSetupRoot
101
188
  };
@@ -5,15 +5,15 @@ export interface QueueHandlerOptions<T> {
5
5
  /** Array of delay times in seconds for exponential backoff */
6
6
  backoffDelays: number[];
7
7
  /** Handler function called for each message */
8
- onMessage: (message: T, attemptNumber: number) => Promise<any> | any;
8
+ onMessage: (message: T, attemptNumber: number) => Promise<unknown> | unknown;
9
9
  /** Called when a message fails and will be retried */
10
10
  onRetry?: (message: T, attemptNumber: number, error: string, delaySeconds: number) => void | Promise<void>;
11
11
  /** Called when message is moved to DLQ (max retries exceeded) */
12
12
  onDLQ?: (message: T, attemptNumber: number, error: string) => void | Promise<void>;
13
13
  /** Logger function for debugging */
14
14
  logger?: {
15
- info(msg: string, data?: any): void;
16
- error(msg: string, data?: any): void;
15
+ info(msg: string, data?: unknown): void;
16
+ error(msg: string, data?: unknown): void;
17
17
  };
18
18
  }
19
19
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"queue-handler.d.ts","sourceRoot":"","sources":["../src/queue-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE9D,MAAM,WAAW,mBAAmB,CAAC,CAAC;IACpC,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,+CAA+C;IAC/C,SAAS,EAAE,CAAC,OAAO,EAAE,CAAC,EAAE,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IACrE,sDAAsD;IACtD,OAAO,CAAC,EAAE,CACR,OAAO,EAAE,CAAC,EACV,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,KACjB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,iEAAiE;IACjE,KAAK,CAAC,EAAE,CACN,OAAO,EAAE,CAAC,EACV,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,MAAM,KACV,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,oCAAoC;IACpC,MAAM,CAAC,EAAE;QACP,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;QACpC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;KACtC,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,IAIrD,OAAO,YAAY,CAAC,CAAC,CAAC,KAAG,OAAO,CAAC,IAAI,CAAC,CAmCrD"}
1
+ {"version":3,"file":"queue-handler.d.ts","sourceRoot":"","sources":["../src/queue-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE9D,MAAM,WAAW,mBAAmB,CAAC,CAAC;IACpC,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,+CAA+C;IAC/C,SAAS,EAAE,CAAC,OAAO,EAAE,CAAC,EAAE,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IAC7E,sDAAsD;IACtD,OAAO,CAAC,EAAE,CACR,OAAO,EAAE,CAAC,EACV,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,KACjB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,iEAAiE;IACjE,KAAK,CAAC,EAAE,CACN,OAAO,EAAE,CAAC,EACV,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,MAAM,KACV,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,oCAAoC;IACpC,MAAM,CAAC,EAAE;QACP,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;QACxC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;KAC1C,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,IAIrD,OAAO,YAAY,CAAC,CAAC,CAAC,KAAG,OAAO,CAAC,IAAI,CAAC,CAmCrD"}