@hoox-sh/hoox-shared 1.3.0 → 1.4.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 (55) hide show
  1. package/dist/agent-config-fields.js +2 -0
  2. package/dist/d1/index.js +4 -2
  3. package/dist/d1/repository.js +4 -2
  4. package/dist/errors.d.ts.map +1 -1
  5. package/dist/errors.js +4 -2
  6. package/dist/health.js +4 -2
  7. package/dist/index.d.ts +3 -3
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +209 -80
  10. package/dist/kill-switch.js +2 -0
  11. package/dist/kvKeys.d.ts +3 -0
  12. package/dist/kvKeys.d.ts.map +1 -1
  13. package/dist/kvKeys.js +3 -0
  14. package/dist/kvUtils.d.ts +34 -1
  15. package/dist/kvUtils.d.ts.map +1 -1
  16. package/dist/kvUtils.js +56 -3
  17. package/dist/middleware/auth.d.ts +6 -3
  18. package/dist/middleware/auth.d.ts.map +1 -1
  19. package/dist/middleware/auth.js +19 -5
  20. package/dist/middleware/index.d.ts +3 -2
  21. package/dist/middleware/index.d.ts.map +1 -1
  22. package/dist/middleware/index.js +88 -8
  23. package/dist/middleware/logger.js +4 -2
  24. package/dist/middleware/validate.d.ts +16 -0
  25. package/dist/middleware/validate.d.ts.map +1 -1
  26. package/dist/middleware/validate.js +37 -1
  27. package/dist/middleware/wait-until.d.ts +32 -0
  28. package/dist/middleware/wait-until.d.ts.map +1 -0
  29. package/dist/middleware/wait-until.js +46 -0
  30. package/dist/queue-handler.d.ts +6 -0
  31. package/dist/queue-handler.d.ts.map +1 -1
  32. package/dist/queue-handler.js +51 -20
  33. package/dist/router.d.ts.map +1 -1
  34. package/dist/router.js +12 -2
  35. package/dist/schemas/index.d.ts +2 -1
  36. package/dist/schemas/index.d.ts.map +1 -1
  37. package/dist/schemas/index.js +58 -13
  38. package/dist/schemas/registry.d.ts +13 -0
  39. package/dist/schemas/registry.d.ts.map +1 -1
  40. package/dist/schemas/registry.js +55 -12
  41. package/dist/schemas/types.d.ts +5 -0
  42. package/dist/schemas/types.d.ts.map +1 -1
  43. package/dist/schemas/validators.d.ts.map +1 -1
  44. package/dist/schemas/validators.js +3 -1
  45. package/dist/service-bindings.js +18 -5
  46. package/dist/stores/service-store.js +1 -1
  47. package/dist/types.d.ts +22 -4
  48. package/dist/types.d.ts.map +1 -1
  49. package/dist/types.js +8 -4
  50. package/dist/wizard/engine.d.ts.map +1 -1
  51. package/dist/wizard/engine.js +18 -35
  52. package/dist/wizard/index.js +18 -35
  53. package/dist/wizard/presets.d.ts.map +1 -1
  54. package/dist/wizard/presets.js +6 -32
  55. package/package.json +1 -1
@@ -16,29 +16,60 @@ var __export = (target, all) => {
16
16
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
17
17
 
18
18
  // src/queue-handler.ts
19
+ async function mapPool(items, concurrency, fn) {
20
+ if (items.length === 0)
21
+ return;
22
+ const limit = Math.min(Math.max(1, concurrency), items.length);
23
+ if (limit === 1) {
24
+ for (const item of items) {
25
+ await fn(item);
26
+ }
27
+ return;
28
+ }
29
+ let nextIndex = 0;
30
+ async function worker() {
31
+ while (true) {
32
+ const i = nextIndex++;
33
+ if (i >= items.length)
34
+ return;
35
+ await fn(items[i]);
36
+ }
37
+ }
38
+ await Promise.all(Array.from({ length: limit }, () => worker()));
39
+ }
19
40
  function createQueueHandler(options) {
20
- const { maxRetries, backoffDelays, onMessage, onRetry, onDLQ, logger } = options;
21
- return async (batch) => {
22
- for (const msg of batch.messages) {
23
- const attemptNumber = msg.attempts || 0;
24
- const logId = `[${msg.id}]`;
25
- try {
26
- logger?.info(`${logId} Processing message (attempt ${attemptNumber + 1})`);
27
- await onMessage(msg.body, attemptNumber);
28
- logger?.info(`${logId} Message processed successfully`);
29
- } catch (error) {
30
- const errorMsg = error instanceof Error ? error.message : String(error);
31
- if (attemptNumber < maxRetries) {
32
- const delaySeconds = backoffDelays[attemptNumber] ?? backoffDelays[backoffDelays.length - 1];
33
- logger?.info(`${logId} Retrying in ${delaySeconds}s (attempt ${attemptNumber + 2}/${maxRetries + 1})`);
34
- await onRetry?.(msg.body, attemptNumber, errorMsg, delaySeconds);
35
- msg.retry({ delaySeconds });
36
- } else {
37
- logger?.error(`${logId} Max retries exceeded (${maxRetries + 1} attempts), moving to DLQ`);
38
- await onDLQ?.(msg.body, attemptNumber, errorMsg);
39
- }
41
+ const {
42
+ maxRetries,
43
+ backoffDelays,
44
+ onMessage,
45
+ onRetry,
46
+ onDLQ,
47
+ logger,
48
+ concurrency: concurrencyOpt
49
+ } = options;
50
+ const concurrency = typeof concurrencyOpt === "number" && Number.isFinite(concurrencyOpt) && concurrencyOpt >= 1 ? Math.floor(concurrencyOpt) : 1;
51
+ async function processMessage(msg) {
52
+ const attemptNumber = msg.attempts || 0;
53
+ const logId = `[${msg.id}]`;
54
+ try {
55
+ logger?.info(`${logId} Processing message (attempt ${attemptNumber + 1})`);
56
+ await onMessage(msg.body, attemptNumber);
57
+ logger?.info(`${logId} Message processed successfully`);
58
+ } catch (error) {
59
+ const errorMsg = error instanceof Error ? error.message : String(error);
60
+ if (attemptNumber < maxRetries) {
61
+ const delaySeconds = backoffDelays[attemptNumber] ?? backoffDelays[backoffDelays.length - 1] ?? 0;
62
+ logger?.info(`${logId} Retrying in ${delaySeconds}s (attempt ${attemptNumber + 2}/${maxRetries + 1})`);
63
+ await onRetry?.(msg.body, attemptNumber, errorMsg, delaySeconds);
64
+ msg.retry({ delaySeconds });
65
+ } else {
66
+ logger?.error(`${logId} Max retries exceeded (${maxRetries + 1} attempts), moving to DLQ`);
67
+ await onDLQ?.(msg.body, attemptNumber, errorMsg);
40
68
  }
41
69
  }
70
+ }
71
+ return async (batch) => {
72
+ await mapPool(batch.messages, concurrency, processMessage);
42
73
  };
43
74
  }
44
75
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;GAMG;AAEH,OAAO,KAAK,EAKV,MAAM,EACP,MAAM,gBAAgB,CAAC;AAGxB,YAAY,EAAE,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE9E,wBAAgB,YAAY,CAAC,IAAI,GAAG,OAAO,KAAK,MAAM,CAAC,IAAI,CAAC,CAsM3D"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;GAMG;AAEH,OAAO,KAAK,EAKV,MAAM,EACP,MAAM,gBAAgB,CAAC;AAGxB,YAAY,EAAE,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE9E,wBAAgB,YAAY,CAAC,IAAI,GAAG,OAAO,KAAK,MAAM,CAAC,IAAI,CAAC,CA6M3D"}
package/dist/router.js CHANGED
@@ -23,7 +23,9 @@ function createRouter() {
23
23
  const regex = /:([a-zA-Z_][a-zA-Z0-9_]*)/g;
24
24
  let match;
25
25
  while ((match = regex.exec(pattern)) !== null) {
26
- params.push(match[1]);
26
+ const name = match[1];
27
+ if (name !== undefined)
28
+ params.push(name);
27
29
  }
28
30
  return params;
29
31
  }
@@ -38,7 +40,11 @@ function createRouter() {
38
40
  return null;
39
41
  const params = {};
40
42
  for (let i = 0;i < paramNames.length; i++) {
41
- params[paramNames[i]] = m[i + 1];
43
+ const key = paramNames[i];
44
+ const value = m[i + 1];
45
+ if (key !== undefined && value !== undefined) {
46
+ params[key] = value;
47
+ }
42
48
  }
43
49
  return params;
44
50
  }
@@ -57,6 +63,8 @@ function createRouter() {
57
63
  return { route: exact };
58
64
  for (let i = routes.length - 1;i >= 0; i--) {
59
65
  const route = routes[i];
66
+ if (!route)
67
+ continue;
60
68
  if (route.params && route.params.length > 0 && route.regex) {
61
69
  const matched = matchPattern(path, route.regex, route.params);
62
70
  if (matched && route.method === method) {
@@ -88,6 +96,8 @@ function createRouter() {
88
96
  let pathExists = false;
89
97
  for (let i = routes.length - 1;i >= 0; i--) {
90
98
  const r = routes[i];
99
+ if (!r)
100
+ continue;
91
101
  if (r.path === path) {
92
102
  pathExists = true;
93
103
  break;
@@ -3,6 +3,7 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  export type { WorkerManifest, VarDef, ServiceBindingDef, InfraBindings, KVBindingDef, D1BindingDef, R2BindingDef, QueueBindingDef, VectorizeBindingDef, DOBindingDef, ValidationError, ValidationReport, } from "./types.js";
6
- export { WORKER_MANIFESTS, WORKER_NAMES, CALLED_BY } from "./registry.js";
6
+ export { WORKER_MANIFESTS, WORKER_NAMES, WORKER_CATALOG, CALLED_BY, getWorkerDefaultEnabled, } from "./registry.js";
7
+ export type { WorkerCatalogEntry } from "./registry.js";
7
8
  export { validateWranglerJsonc, validateRootSecrets, validateDevVars, validateAll, generateWranglerJsonc, generateDevVars, } from "./validators.js";
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,YAAY,EACV,cAAc,EACd,MAAM,EACN,iBAAiB,EACjB,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1E,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,eAAe,EACf,WAAW,EACX,qBAAqB,EACrB,eAAe,GAChB,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,YAAY,EACV,cAAc,EACd,MAAM,EACN,iBAAiB,EACjB,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,SAAS,EACT,uBAAuB,GACxB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAExD,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,eAAe,EACf,WAAW,EACX,qBAAqB,EACrB,eAAe,GAChB,MAAM,iBAAiB,CAAC"}
@@ -22,9 +22,12 @@ function deriveCalledBy(manifests) {
22
22
  if (!calledBy[workerName])
23
23
  calledBy[workerName] = new Set;
24
24
  for (const svc of manifest.services) {
25
- if (!calledBy[svc.service])
26
- calledBy[svc.service] = new Set;
27
- calledBy[svc.service].add(workerName);
25
+ let callers = calledBy[svc.service];
26
+ if (!callers) {
27
+ callers = new Set;
28
+ calledBy[svc.service] = callers;
29
+ }
30
+ callers.add(workerName);
28
31
  }
29
32
  }
30
33
  const result = {};
@@ -37,6 +40,7 @@ var manifests = {
37
40
  hoox: {
38
41
  name: "hoox",
39
42
  path: "workers/hoox-worker",
43
+ defaultEnabled: true,
40
44
  vars: {
41
45
  WEBHOOK_API_KEY_BINDING: {
42
46
  type: "secret",
@@ -46,7 +50,11 @@ var manifests = {
46
50
  type: "secret",
47
51
  description: "Inter-worker auth key"
48
52
  },
49
- HA_TOKEN_BINDING: { type: "secret", description: "Home Assistant token" }
53
+ HA_TOKEN_BINDING: { type: "secret", description: "Home Assistant token" },
54
+ TELEGRAM_ALLOWED_CHAT_IDS: {
55
+ type: "secret",
56
+ description: "Comma-separated chat IDs for webhook notify (fail-closed when unset); alias AUTHORIZED_CHAT_IDS"
57
+ }
50
58
  },
51
59
  services: [
52
60
  {
@@ -72,7 +80,8 @@ var manifests = {
72
80
  ai: true,
73
81
  queues: { producer: ["trade-execution"] },
74
82
  durableObjects: [
75
- { name: "IDEMPOTENCY_STORE", className: "IdempotencyStore" }
83
+ { name: "IDEMPOTENCY_STORE", className: "IdempotencyStore" },
84
+ { name: "RATE_LIMITER", className: "RateLimiterStore" }
76
85
  ]
77
86
  },
78
87
  middleware: [
@@ -87,6 +96,7 @@ var manifests = {
87
96
  "trade-worker": {
88
97
  name: "trade-worker",
89
98
  path: "workers/trade-worker",
99
+ defaultEnabled: true,
90
100
  vars: {
91
101
  INTERNAL_KEY_BINDING: {
92
102
  type: "secret",
@@ -96,15 +106,22 @@ var manifests = {
96
106
  type: "secret",
97
107
  description: "Telegram outbound auth"
98
108
  },
99
- BINANCE_KEY_BINDING: { type: "secret", description: "Binance API key" },
100
- BINANCE_SECRET_BINDING: {
109
+ EXCHANGE_KEY_BINDING: {
110
+ type: "secret",
111
+ description: "Unified exchange API key (Binance / Bybit / MEXC)"
112
+ },
113
+ EXCHANGE_SECRET_BINDING: {
114
+ type: "secret",
115
+ description: "Unified exchange API secret"
116
+ },
117
+ EXCHANGE_TESTNET_KEY_BINDING: {
101
118
  type: "secret",
102
- description: "Binance API secret"
119
+ description: "Optional unified testnet API key"
103
120
  },
104
- MEXC_KEY_BINDING: { type: "secret", description: "MEXC API key" },
105
- MEXC_SECRET_BINDING: { type: "secret", description: "MEXC API secret" },
106
- BYBIT_KEY_BINDING: { type: "secret", description: "Bybit API key" },
107
- BYBIT_SECRET_BINDING: { type: "secret", description: "Bybit API secret" }
121
+ EXCHANGE_TESTNET_SECRET_BINDING: {
122
+ type: "secret",
123
+ description: "Optional unified testnet API secret"
124
+ }
108
125
  },
109
126
  services: [
110
127
  {
@@ -146,6 +163,7 @@ var manifests = {
146
163
  "telegram-worker": {
147
164
  name: "telegram-worker",
148
165
  path: "workers/telegram-worker",
166
+ defaultEnabled: true,
149
167
  vars: {
150
168
  INTERNAL_KEY_BINDING: {
151
169
  type: "secret",
@@ -159,6 +177,10 @@ var manifests = {
159
177
  TELEGRAM_SECRET_TOKEN: {
160
178
  type: "secret",
161
179
  description: "Webhook verification token"
180
+ },
181
+ AUTHORIZED_CHAT_IDS: {
182
+ type: "secret",
183
+ description: "Comma-separated chat IDs; inbound webhook fail-closed when unset; outbound /alert allowlist"
162
184
  }
163
185
  },
164
186
  services: [],
@@ -179,6 +201,7 @@ var manifests = {
179
201
  "d1-worker": {
180
202
  name: "d1-worker",
181
203
  path: "workers/d1-worker",
204
+ defaultEnabled: true,
182
205
  vars: {
183
206
  INTERNAL_KEY_BINDING: {
184
207
  type: "secret",
@@ -201,6 +224,7 @@ var manifests = {
201
224
  "web3-wallet-worker": {
202
225
  name: "web3-wallet-worker",
203
226
  path: "workers/web3-wallet-worker",
227
+ defaultEnabled: false,
204
228
  vars: {
205
229
  WALLET_PK_SECRET: { type: "secret", description: "Wallet private key" },
206
230
  WALLET_MNEMONIC_SECRET: {
@@ -221,6 +245,7 @@ var manifests = {
221
245
  "agent-worker": {
222
246
  name: "agent-worker",
223
247
  path: "workers/agent-worker",
248
+ defaultEnabled: true,
224
249
  vars: {
225
250
  AGENT_INTERNAL_KEY: { type: "secret", description: "Agent worker auth" },
226
251
  INTERNAL_KEY_BINDING: {
@@ -257,6 +282,7 @@ var manifests = {
257
282
  "email-worker": {
258
283
  name: "email-worker",
259
284
  path: "workers/email-worker",
285
+ defaultEnabled: false,
260
286
  vars: {
261
287
  INTERNAL_KEY_BINDING: {
262
288
  type: "secret",
@@ -294,6 +320,7 @@ var manifests = {
294
320
  "analytics-worker": {
295
321
  name: "analytics-worker",
296
322
  path: "workers/analytics-worker",
323
+ defaultEnabled: true,
297
324
  vars: {
298
325
  CLOUDFLARE_API_TOKEN: {
299
326
  type: "secret",
@@ -313,6 +340,7 @@ var manifests = {
313
340
  "report-worker": {
314
341
  name: "report-worker",
315
342
  path: "workers/report-worker",
343
+ defaultEnabled: false,
316
344
  vars: {
317
345
  CF_API_TOKEN_BINDING: {
318
346
  type: "secret",
@@ -347,6 +375,7 @@ var manifests = {
347
375
  "pyne-worker": {
348
376
  name: "pyne-worker",
349
377
  path: "workers/pyne-worker",
378
+ defaultEnabled: true,
350
379
  vars: {
351
380
  API_KEY: {
352
381
  type: "secret",
@@ -384,6 +413,7 @@ var manifests = {
384
413
  dashboard: {
385
414
  name: "dashboard",
386
415
  path: "workers/dashboard",
416
+ defaultEnabled: true,
387
417
  vars: {
388
418
  DASHBOARD_USER: {
389
419
  type: "secret",
@@ -426,6 +456,17 @@ var manifests = {
426
456
  var CALLED_BY = deriveCalledBy(manifests);
427
457
  var WORKER_MANIFESTS = manifests;
428
458
  var WORKER_NAMES = Object.keys(manifests).sort();
459
+ var WORKER_CATALOG = WORKER_NAMES.map((name) => {
460
+ const m = manifests[name];
461
+ return {
462
+ name: m.name,
463
+ path: m.path,
464
+ defaultEnabled: m.defaultEnabled
465
+ };
466
+ });
467
+ function getWorkerDefaultEnabled(name) {
468
+ return manifests[name]?.defaultEnabled;
469
+ }
429
470
 
430
471
  // src/schemas/validators.ts
431
472
  import { parse } from "jsonc-parser";
@@ -465,7 +506,7 @@ function validateWranglerJsonc(workerName, manifest, jsoncContent) {
465
506
  }
466
507
  }
467
508
  for (const [name, def] of Object.entries(manifest.vars)) {
468
- if (def.type === "secret" && !name.endsWith("_BINDING") && !name.endsWith("_SECRET") && !name.startsWith("CLOUDFLARE_") && name !== "TELEGRAM_SECRET_TOKEN" && name !== "MAILGUN_API_KEY" && name !== "EMAIL_SCAN_SUBJECT" && name !== "DASHBOARD_USER" && name !== "DASHBOARD_PASS" && name !== "SESSION_SECRET" && name !== "AGENT_INTERNAL_KEY") {
509
+ if (def.type === "secret" && !name.endsWith("_BINDING") && !name.endsWith("_SECRET") && !name.startsWith("CLOUDFLARE_") && name !== "TELEGRAM_SECRET_TOKEN" && name !== "TELEGRAM_ALLOWED_CHAT_IDS" && name !== "AUTHORIZED_CHAT_IDS" && name !== "MAILGUN_API_KEY" && name !== "EMAIL_SCAN_SUBJECT" && name !== "DASHBOARD_USER" && name !== "DASHBOARD_PASS" && name !== "SESSION_SECRET" && name !== "AGENT_INTERNAL_KEY") {
469
510
  errors.push({
470
511
  worker: workerName,
471
512
  severity: "warning",
@@ -582,6 +623,8 @@ function generateWranglerJsonc(manifest) {
582
623
  parts.push(` "vars": {`);
583
624
  for (const [i, name] of varKeys.entries()) {
584
625
  const def = manifest.vars[name];
626
+ if (!def)
627
+ continue;
585
628
  const comma = i < varKeys.length - 1 ? "," : "";
586
629
  const val = def.type === "secret" ? "__SECRET__" : JSON.stringify(def.default ?? "");
587
630
  parts.push(` "${name}": ${val}${comma}`);
@@ -697,9 +740,11 @@ export {
697
740
  validateRootSecrets,
698
741
  validateDevVars,
699
742
  validateAll,
743
+ getWorkerDefaultEnabled,
700
744
  generateWranglerJsonc,
701
745
  generateDevVars,
702
746
  WORKER_NAMES,
703
747
  WORKER_MANIFESTS,
748
+ WORKER_CATALOG,
704
749
  CALLED_BY
705
750
  };
@@ -9,4 +9,17 @@ export declare const CALLED_BY: Record<string, string[]>;
9
9
  export declare const WORKER_MANIFESTS: Record<string, WorkerManifest>;
10
10
  /** List of all worker names in the registry. */
11
11
  export declare const WORKER_NAMES: string[];
12
+ /**
13
+ * Catalog entry for CLI worker enable/disable — one row per known worker.
14
+ * `defaultEnabled` is the seed value when the worker is first added to config.
15
+ */
16
+ export interface WorkerCatalogEntry {
17
+ name: string;
18
+ path: string;
19
+ defaultEnabled: boolean;
20
+ }
21
+ /** Full catalog of known workers with default enable flags (sorted by name). */
22
+ export declare const WORKER_CATALOG: WorkerCatalogEntry[];
23
+ /** Lookup defaultEnabled for a known worker; undefined if not in catalog. */
24
+ export declare function getWorkerDefaultEnabled(name: string): boolean | undefined;
12
25
  //# sourceMappingURL=registry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/schemas/registry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AA2ajD,qEAAqE;AACrE,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAA6B,CAAC;AAE7E,sDAAsD;AACtD,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAa,CAAC;AAE1E,gDAAgD;AAChD,eAAO,MAAM,YAAY,EAAE,MAAM,EAAkC,CAAC"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/schemas/registry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AA4cjD,qEAAqE;AACrE,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAA6B,CAAC;AAE7E,sDAAsD;AACtD,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAa,CAAC;AAE1E,gDAAgD;AAChD,eAAO,MAAM,YAAY,EAAE,MAAM,EAAkC,CAAC;AAEpE;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,gFAAgF;AAChF,eAAO,MAAM,cAAc,EAAE,kBAAkB,EAO7C,CAAC;AAEH,6EAA6E;AAC7E,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAEzE"}
@@ -22,9 +22,12 @@ function deriveCalledBy(manifests) {
22
22
  if (!calledBy[workerName])
23
23
  calledBy[workerName] = new Set;
24
24
  for (const svc of manifest.services) {
25
- if (!calledBy[svc.service])
26
- calledBy[svc.service] = new Set;
27
- calledBy[svc.service].add(workerName);
25
+ let callers = calledBy[svc.service];
26
+ if (!callers) {
27
+ callers = new Set;
28
+ calledBy[svc.service] = callers;
29
+ }
30
+ callers.add(workerName);
28
31
  }
29
32
  }
30
33
  const result = {};
@@ -37,6 +40,7 @@ var manifests = {
37
40
  hoox: {
38
41
  name: "hoox",
39
42
  path: "workers/hoox-worker",
43
+ defaultEnabled: true,
40
44
  vars: {
41
45
  WEBHOOK_API_KEY_BINDING: {
42
46
  type: "secret",
@@ -46,7 +50,11 @@ var manifests = {
46
50
  type: "secret",
47
51
  description: "Inter-worker auth key"
48
52
  },
49
- HA_TOKEN_BINDING: { type: "secret", description: "Home Assistant token" }
53
+ HA_TOKEN_BINDING: { type: "secret", description: "Home Assistant token" },
54
+ TELEGRAM_ALLOWED_CHAT_IDS: {
55
+ type: "secret",
56
+ description: "Comma-separated chat IDs for webhook notify (fail-closed when unset); alias AUTHORIZED_CHAT_IDS"
57
+ }
50
58
  },
51
59
  services: [
52
60
  {
@@ -72,7 +80,8 @@ var manifests = {
72
80
  ai: true,
73
81
  queues: { producer: ["trade-execution"] },
74
82
  durableObjects: [
75
- { name: "IDEMPOTENCY_STORE", className: "IdempotencyStore" }
83
+ { name: "IDEMPOTENCY_STORE", className: "IdempotencyStore" },
84
+ { name: "RATE_LIMITER", className: "RateLimiterStore" }
76
85
  ]
77
86
  },
78
87
  middleware: [
@@ -87,6 +96,7 @@ var manifests = {
87
96
  "trade-worker": {
88
97
  name: "trade-worker",
89
98
  path: "workers/trade-worker",
99
+ defaultEnabled: true,
90
100
  vars: {
91
101
  INTERNAL_KEY_BINDING: {
92
102
  type: "secret",
@@ -96,15 +106,22 @@ var manifests = {
96
106
  type: "secret",
97
107
  description: "Telegram outbound auth"
98
108
  },
99
- BINANCE_KEY_BINDING: { type: "secret", description: "Binance API key" },
100
- BINANCE_SECRET_BINDING: {
109
+ EXCHANGE_KEY_BINDING: {
110
+ type: "secret",
111
+ description: "Unified exchange API key (Binance / Bybit / MEXC)"
112
+ },
113
+ EXCHANGE_SECRET_BINDING: {
114
+ type: "secret",
115
+ description: "Unified exchange API secret"
116
+ },
117
+ EXCHANGE_TESTNET_KEY_BINDING: {
101
118
  type: "secret",
102
- description: "Binance API secret"
119
+ description: "Optional unified testnet API key"
103
120
  },
104
- MEXC_KEY_BINDING: { type: "secret", description: "MEXC API key" },
105
- MEXC_SECRET_BINDING: { type: "secret", description: "MEXC API secret" },
106
- BYBIT_KEY_BINDING: { type: "secret", description: "Bybit API key" },
107
- BYBIT_SECRET_BINDING: { type: "secret", description: "Bybit API secret" }
121
+ EXCHANGE_TESTNET_SECRET_BINDING: {
122
+ type: "secret",
123
+ description: "Optional unified testnet API secret"
124
+ }
108
125
  },
109
126
  services: [
110
127
  {
@@ -146,6 +163,7 @@ var manifests = {
146
163
  "telegram-worker": {
147
164
  name: "telegram-worker",
148
165
  path: "workers/telegram-worker",
166
+ defaultEnabled: true,
149
167
  vars: {
150
168
  INTERNAL_KEY_BINDING: {
151
169
  type: "secret",
@@ -159,6 +177,10 @@ var manifests = {
159
177
  TELEGRAM_SECRET_TOKEN: {
160
178
  type: "secret",
161
179
  description: "Webhook verification token"
180
+ },
181
+ AUTHORIZED_CHAT_IDS: {
182
+ type: "secret",
183
+ description: "Comma-separated chat IDs; inbound webhook fail-closed when unset; outbound /alert allowlist"
162
184
  }
163
185
  },
164
186
  services: [],
@@ -179,6 +201,7 @@ var manifests = {
179
201
  "d1-worker": {
180
202
  name: "d1-worker",
181
203
  path: "workers/d1-worker",
204
+ defaultEnabled: true,
182
205
  vars: {
183
206
  INTERNAL_KEY_BINDING: {
184
207
  type: "secret",
@@ -201,6 +224,7 @@ var manifests = {
201
224
  "web3-wallet-worker": {
202
225
  name: "web3-wallet-worker",
203
226
  path: "workers/web3-wallet-worker",
227
+ defaultEnabled: false,
204
228
  vars: {
205
229
  WALLET_PK_SECRET: { type: "secret", description: "Wallet private key" },
206
230
  WALLET_MNEMONIC_SECRET: {
@@ -221,6 +245,7 @@ var manifests = {
221
245
  "agent-worker": {
222
246
  name: "agent-worker",
223
247
  path: "workers/agent-worker",
248
+ defaultEnabled: true,
224
249
  vars: {
225
250
  AGENT_INTERNAL_KEY: { type: "secret", description: "Agent worker auth" },
226
251
  INTERNAL_KEY_BINDING: {
@@ -257,6 +282,7 @@ var manifests = {
257
282
  "email-worker": {
258
283
  name: "email-worker",
259
284
  path: "workers/email-worker",
285
+ defaultEnabled: false,
260
286
  vars: {
261
287
  INTERNAL_KEY_BINDING: {
262
288
  type: "secret",
@@ -294,6 +320,7 @@ var manifests = {
294
320
  "analytics-worker": {
295
321
  name: "analytics-worker",
296
322
  path: "workers/analytics-worker",
323
+ defaultEnabled: true,
297
324
  vars: {
298
325
  CLOUDFLARE_API_TOKEN: {
299
326
  type: "secret",
@@ -313,6 +340,7 @@ var manifests = {
313
340
  "report-worker": {
314
341
  name: "report-worker",
315
342
  path: "workers/report-worker",
343
+ defaultEnabled: false,
316
344
  vars: {
317
345
  CF_API_TOKEN_BINDING: {
318
346
  type: "secret",
@@ -347,6 +375,7 @@ var manifests = {
347
375
  "pyne-worker": {
348
376
  name: "pyne-worker",
349
377
  path: "workers/pyne-worker",
378
+ defaultEnabled: true,
350
379
  vars: {
351
380
  API_KEY: {
352
381
  type: "secret",
@@ -384,6 +413,7 @@ var manifests = {
384
413
  dashboard: {
385
414
  name: "dashboard",
386
415
  path: "workers/dashboard",
416
+ defaultEnabled: true,
387
417
  vars: {
388
418
  DASHBOARD_USER: {
389
419
  type: "secret",
@@ -426,8 +456,21 @@ var manifests = {
426
456
  var CALLED_BY = deriveCalledBy(manifests);
427
457
  var WORKER_MANIFESTS = manifests;
428
458
  var WORKER_NAMES = Object.keys(manifests).sort();
459
+ var WORKER_CATALOG = WORKER_NAMES.map((name) => {
460
+ const m = manifests[name];
461
+ return {
462
+ name: m.name,
463
+ path: m.path,
464
+ defaultEnabled: m.defaultEnabled
465
+ };
466
+ });
467
+ function getWorkerDefaultEnabled(name) {
468
+ return manifests[name]?.defaultEnabled;
469
+ }
429
470
  export {
471
+ getWorkerDefaultEnabled,
430
472
  WORKER_NAMES,
431
473
  WORKER_MANIFESTS,
474
+ WORKER_CATALOG,
432
475
  CALLED_BY
433
476
  };
@@ -10,6 +10,11 @@ export interface WorkerManifest {
10
10
  name: string;
11
11
  /** Path relative to project root (e.g. "workers/trade-worker") */
12
12
  path: string;
13
+ /**
14
+ * Default for `workers.<name>.enabled` in the root wrangler.jsonc catalog.
15
+ * Used by `hoox workers enable|disable` and when seeding missing workers.
16
+ */
17
+ defaultEnabled: boolean;
13
18
  /** Vars the worker declares in its wrangler.jsonc `vars` section */
14
19
  vars: Record<string, VarDef>;
15
20
  /** Service bindings this worker calls */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/schemas/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AAEH,MAAM,WAAW,cAAc;IAC7B,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,yCAAyC;IACzC,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,gDAAgD;IAChD,cAAc,EAAE,aAAa,CAAC;IAC9B,+DAA+D;IAC/D,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,QAAQ,GAAG,WAAW,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC;IACpB,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC;IACpB,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC;IACpB,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,SAAS,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAClC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,YAAY,EAAE,CAAC;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/schemas/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AAEH,MAAM,WAAW,cAAc;IAC7B,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,cAAc,EAAE,OAAO,CAAC;IACxB,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,yCAAyC;IACzC,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,gDAAgD;IAChD,cAAc,EAAE,aAAa,CAAC;IAC9B,+DAA+D;IAC/D,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,QAAQ,GAAG,WAAW,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC;IACpB,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC;IACpB,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC;IACpB,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,SAAS,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAClC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,YAAY,EAAE,CAAC;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B"}
@@ -1 +1 @@
1
- {"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../src/schemas/validators.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAalE;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,cAAc,EACxB,YAAY,EAAE,MAAM,GACnB,eAAe,EAAE,CA4GnB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,cAAc,EACxB,gBAAgB,EAAE,MAAM,GACvB,eAAe,EAAE,CA2CnB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,cAAc,EACxB,cAAc,EAAE,MAAM,GACrB,eAAe,EAAE,CA6BnB;AAID;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAmItE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAYhE;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE;IACL,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;CACjB,GACA;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,eAAe,EAAE,CAAA;CAAE,CAWhE"}
1
+ {"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../src/schemas/validators.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAalE;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,cAAc,EACxB,YAAY,EAAE,MAAM,GACnB,eAAe,EAAE,CA8GnB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,cAAc,EACxB,gBAAgB,EAAE,MAAM,GACvB,eAAe,EAAE,CA2CnB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,cAAc,EACxB,cAAc,EAAE,MAAM,GACrB,eAAe,EAAE,CA6BnB;AAID;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAoItE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAYhE;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE;IACL,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;CACjB,GACA;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,eAAe,EAAE,CAAA;CAAE,CAWhE"}
@@ -53,7 +53,7 @@ function validateWranglerJsonc(workerName, manifest, jsoncContent) {
53
53
  }
54
54
  }
55
55
  for (const [name, def] of Object.entries(manifest.vars)) {
56
- if (def.type === "secret" && !name.endsWith("_BINDING") && !name.endsWith("_SECRET") && !name.startsWith("CLOUDFLARE_") && name !== "TELEGRAM_SECRET_TOKEN" && name !== "MAILGUN_API_KEY" && name !== "EMAIL_SCAN_SUBJECT" && name !== "DASHBOARD_USER" && name !== "DASHBOARD_PASS" && name !== "SESSION_SECRET" && name !== "AGENT_INTERNAL_KEY") {
56
+ if (def.type === "secret" && !name.endsWith("_BINDING") && !name.endsWith("_SECRET") && !name.startsWith("CLOUDFLARE_") && name !== "TELEGRAM_SECRET_TOKEN" && name !== "TELEGRAM_ALLOWED_CHAT_IDS" && name !== "AUTHORIZED_CHAT_IDS" && name !== "MAILGUN_API_KEY" && name !== "EMAIL_SCAN_SUBJECT" && name !== "DASHBOARD_USER" && name !== "DASHBOARD_PASS" && name !== "SESSION_SECRET" && name !== "AGENT_INTERNAL_KEY") {
57
57
  errors.push({
58
58
  worker: workerName,
59
59
  severity: "warning",
@@ -170,6 +170,8 @@ function generateWranglerJsonc(manifest) {
170
170
  parts.push(` "vars": {`);
171
171
  for (const [i, name] of varKeys.entries()) {
172
172
  const def = manifest.vars[name];
173
+ if (!def)
174
+ continue;
173
175
  const comma = i < varKeys.length - 1 ? "," : "";
174
176
  const val = def.type === "secret" ? "__SECRET__" : JSON.stringify(def.default ?? "");
175
177
  parts.push(` "${name}": ${val}${comma}`);
@@ -23,12 +23,24 @@ function timingSafeEqual(a, b) {
23
23
  const len = Math.max(aBuf.length, bBuf.length);
24
24
  let result = aBuf.length === bBuf.length ? 0 : 1;
25
25
  for (let i = 0;i < len; i++) {
26
- const av = i < aBuf.length ? aBuf[i] : 0;
27
- const bv = i < bBuf.length ? bBuf[i] : 0;
26
+ const av = aBuf[i] ?? 0;
27
+ const bv = bBuf[i] ?? 0;
28
28
  result |= av ^ bv;
29
29
  }
30
30
  return result === 0;
31
31
  }
32
+ async function timingSafeEqualAsync(a, b) {
33
+ const subtle = globalThis.crypto?.subtle;
34
+ if (!subtle || typeof subtle.digest !== "function" || typeof subtle.timingSafeEqual !== "function") {
35
+ return timingSafeEqual(a, b);
36
+ }
37
+ const encoder = new TextEncoder;
38
+ const [providedHash, expectedHash] = await Promise.all([
39
+ subtle.digest("SHA-256", encoder.encode(a)),
40
+ subtle.digest("SHA-256", encoder.encode(b))
41
+ ]);
42
+ return subtle.timingSafeEqual(providedHash, expectedHash);
43
+ }
32
44
  function resolveOperatorApiKey(env) {
33
45
  const preferred = env.OPERATOR_API_KEY;
34
46
  if (typeof preferred === "string" && preferred.length > 0) {
@@ -53,7 +65,7 @@ async function requireOperatorAuth(request, env) {
53
65
  }
54
66
  const authHeader = request.headers.get("Authorization");
55
67
  const expectedHeader = `Bearer ${apiKey}`;
56
- if (!authHeader || !timingSafeEqual(authHeader, expectedHeader)) {
68
+ if (!authHeader || !await timingSafeEqualAsync(authHeader, expectedHeader)) {
57
69
  return new Response(JSON.stringify({ error: "Unauthorized" }), {
58
70
  status: 401,
59
71
  headers: { "Content-Type": "application/json" }
@@ -86,12 +98,13 @@ function collectInternalAuthKeys(env, keyNames) {
86
98
  return keys;
87
99
  }
88
100
  function matchesAnyInternalAuthKey(providedKey, expectedKeys) {
101
+ let matched = false;
89
102
  for (const expected of expectedKeys) {
90
103
  if (timingSafeEqual(providedKey, expected)) {
91
- return true;
104
+ matched = true;
92
105
  }
93
106
  }
94
- return false;
107
+ return matched;
95
108
  }
96
109
  function internalAuthNotConfiguredResponse(keyNames) {
97
110
  const label = normalizeKeyNames(keyNames).join(" | ");