@jango-blockchained/hoox-cli 0.3.4 → 0.3.5

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 (48) hide show
  1. package/README.md +2 -0
  2. package/package.json +5 -3
  3. package/src/commands/check/check-command.test.ts +28 -20
  4. package/src/commands/check/prerequisites-command.ts +9 -5
  5. package/src/commands/clone/clone-command.ts +1 -10
  6. package/src/commands/config/config-command.ts +7 -11
  7. package/src/commands/config/env-command.ts +16 -35
  8. package/src/commands/config/kv-command.ts +33 -72
  9. package/src/commands/db/db-command.ts +23 -25
  10. package/src/commands/deploy/deploy-command.ts +133 -52
  11. package/src/commands/deploy/telegram-service.ts +21 -6
  12. package/src/commands/dev/dev-command.ts +7 -16
  13. package/src/commands/infra/infra-command.test.ts +2 -2
  14. package/src/commands/infra/infra-command.ts +45 -11
  15. package/src/commands/init/init-command.test.ts +2 -2
  16. package/src/commands/monitor/monitor-command.test.ts +44 -16
  17. package/src/commands/monitor/monitor-command.ts +45 -21
  18. package/src/commands/monitor/monitor-service.ts +19 -4
  19. package/src/commands/repair/repair-command.test.ts +35 -15
  20. package/src/commands/repair/repair-command.ts +39 -18
  21. package/src/commands/repair/repair-service.ts +48 -12
  22. package/src/commands/test/test-command.ts +4 -10
  23. package/src/commands/tui/index.ts +1 -0
  24. package/src/commands/tui/tui-command.ts +87 -0
  25. package/src/commands/update/index.ts +1 -0
  26. package/src/commands/update/update-command.ts +51 -0
  27. package/src/commands/waf/waf-command.test.ts +1 -1
  28. package/src/commands/waf/waf-command.ts +11 -11
  29. package/src/index.ts +52 -1
  30. package/src/services/cloudflare/cloudflare-service.test.ts +12 -8
  31. package/src/services/cloudflare/cloudflare-service.ts +8 -10
  32. package/src/services/cloudflare/types.ts +6 -5
  33. package/src/services/db/db-service.ts +8 -17
  34. package/src/services/env/env-service.test.ts +41 -15
  35. package/src/services/env/env-service.ts +276 -46
  36. package/src/services/kv/kv-sync-service.ts +117 -34
  37. package/src/services/prerequisites/prerequisites-service.ts +137 -29
  38. package/src/services/secrets/index.ts +0 -1
  39. package/src/services/secrets/secrets-service.test.ts +34 -25
  40. package/src/services/secrets/secrets-service.ts +10 -16
  41. package/src/services/secrets/types.ts +4 -11
  42. package/src/services/update/index.ts +2 -0
  43. package/src/services/update/update-service.test.ts +76 -0
  44. package/src/services/update/update-service.ts +193 -0
  45. package/src/ui/banner.ts +5 -5
  46. package/src/ui/menu.ts +6 -1
  47. package/src/utils/formatters.ts +21 -4
  48. package/src/utils/theme.ts +1 -1
@@ -52,7 +52,7 @@ function displayListResult(
52
52
  return;
53
53
  }
54
54
 
55
- const data = result.data;
55
+ const data = result.value;
56
56
 
57
57
  // Try JSON parse for structured output
58
58
  try {
@@ -486,33 +486,61 @@ async function doProvision(
486
486
  // Vectorize handlers
487
487
  // ---------------------------------------------------------------------------
488
488
 
489
- async function doVectorizeList(opts: InfraOptions, cf?: CloudflareService): Promise<void> {
489
+ async function doVectorizeList(
490
+ opts: InfraOptions,
491
+ cf?: CloudflareService
492
+ ): Promise<void> {
490
493
  const cloudflare = cf ?? new CloudflareService();
491
494
  const result = await cloudflare.vectorizeList();
492
495
  displayListResult(result, opts, ["name", "id", "dimensions", "metric"]);
493
496
  }
494
497
 
495
- async function doVectorizeCreate(name: string, opts: InfraOptions, cf?: CloudflareService): Promise<void> {
498
+ async function doVectorizeCreate(
499
+ name: string,
500
+ opts: InfraOptions,
501
+ cf?: CloudflareService
502
+ ): Promise<void> {
496
503
  const cloudflare = cf ?? new CloudflareService();
497
- await handleCreate(name, "Vectorize index", (n) => cloudflare.vectorizeCreate(n), opts);
504
+ await handleCreate(
505
+ name,
506
+ "Vectorize index",
507
+ (n) => cloudflare.vectorizeCreate(n),
508
+ opts
509
+ );
498
510
  }
499
511
 
500
- async function doVectorizeDelete(name: string, opts: InfraOptions, cf?: CloudflareService): Promise<void> {
512
+ async function doVectorizeDelete(
513
+ name: string,
514
+ opts: InfraOptions,
515
+ cf?: CloudflareService
516
+ ): Promise<void> {
501
517
  const cloudflare = cf ?? new CloudflareService();
502
- await handleDelete(name, "Vectorize index", (n) => cloudflare.vectorizeDelete(n), opts);
518
+ await handleDelete(
519
+ name,
520
+ "Vectorize index",
521
+ (n) => cloudflare.vectorizeDelete(n),
522
+ opts
523
+ );
503
524
  }
504
525
 
505
526
  // ---------------------------------------------------------------------------
506
527
  // Analytics handlers
507
528
  // ---------------------------------------------------------------------------
508
529
 
509
- async function doAnalyticsList(opts: InfraOptions, cf?: CloudflareService): Promise<void> {
530
+ async function doAnalyticsList(
531
+ opts: InfraOptions,
532
+ cf?: CloudflareService
533
+ ): Promise<void> {
510
534
  const cloudflare = cf ?? new CloudflareService();
511
535
  const result = await cloudflare.analyticsList();
512
536
  displayListResult(result, opts);
513
537
  }
514
538
 
515
- async function doAnalyticsCreate(name: string, opts: InfraOptions, cf?: CloudflareService): Promise<void> {
539
+ async function doAnalyticsCreate(
540
+ name: string,
541
+ opts: InfraOptions,
542
+ cf?: CloudflareService
543
+ ): Promise<void> {
516
544
  const cloudflare = cf ?? new CloudflareService();
517
545
  const result = await cloudflare.analyticsCreate(name);
518
546
  if (result.ok) {
@@ -541,7 +569,9 @@ async function doAnalyticsCreate(name: string, opts: InfraOptions, cf?: Cloudfla
541
569
  export function registerInfraCommand(program: Command): void {
542
570
  const infraCmd = program
543
571
  .command("infra")
544
- .summary("Manage Cloudflare infrastructure (D1, KV, R2, Queues, Vectorize, Analytics)")
572
+ .summary(
573
+ "Manage Cloudflare infrastructure (D1, KV, R2, Queues, Vectorize, Analytics)"
574
+ )
545
575
  .description(
546
576
  `Provision and manage Cloudflare infrastructure resources.
547
577
 
@@ -861,7 +891,9 @@ EXAMPLES:
861
891
  vectorizeCmd
862
892
  .command("create <name>")
863
893
  .summary("Create a new Vectorize index")
864
- .description("Create a new Vectorize index with default dimensions (768) and cosine metric.")
894
+ .description(
895
+ "Create a new Vectorize index with default dimensions (768) and cosine metric."
896
+ )
865
897
  .action(async function (this: Command, name: string) {
866
898
  const opts = getOptions(this);
867
899
  await doVectorizeCreate(name, opts);
@@ -904,7 +936,9 @@ EXAMPLES:
904
936
  analyticsCmd
905
937
  .command("create <name>")
906
938
  .summary("Show instructions for creating an Analytics Engine dataset")
907
- .description("Analytics Engine datasets must be created via Cloudflare Dashboard.")
939
+ .description(
940
+ "Analytics Engine datasets must be created via Cloudflare Dashboard."
941
+ )
908
942
  .action(async function (this: Command, name: string) {
909
943
  const opts = getOptions(this);
910
944
  await doAnalyticsCreate(name, opts);
@@ -120,10 +120,10 @@ const origWhoami = CloudflareService.prototype.whoami;
120
120
 
121
121
  const mockWhoami = mock(
122
122
  async (): Promise<
123
- { ok: true; data: string } | { ok: false; error: string }
123
+ { ok: true; value: string } | { ok: false; error: string }
124
124
  > => ({
125
125
  ok: true,
126
- data: "user@example.com",
126
+ value: "user@example.com",
127
127
  })
128
128
  );
129
129
 
@@ -35,11 +35,14 @@ beforeEach(() => {
35
35
  process.exitCode = 0;
36
36
 
37
37
  // Restore originals
38
- (MonitorService.prototype as Record<string, unknown>).checkAllWorkerHealth = origCheckAll;
39
- (DbService.prototype as Record<string, unknown>).resolveDbName = origResolveDbName;
38
+ (MonitorService.prototype as Record<string, unknown>).checkAllWorkerHealth =
39
+ origCheckAll;
40
+ (DbService.prototype as Record<string, unknown>).resolveDbName =
41
+ origResolveDbName;
40
42
  (DbService.prototype as Record<string, unknown>).query = origQuery;
41
43
  (DbService.prototype as Record<string, unknown>).export = origExport;
42
- (KvSyncService.prototype as Record<string, unknown>).resolveNamespaceId = origResolveNs;
44
+ (KvSyncService.prototype as Record<string, unknown>).resolveNamespaceId =
45
+ origResolveNs;
43
46
  (KvSyncService.prototype as Record<string, unknown>).get = origGet;
44
47
  (KvSyncService.prototype as Record<string, unknown>).set = origSet;
45
48
 
@@ -55,29 +58,38 @@ beforeEach(() => {
55
58
  }));
56
59
 
57
60
  resolveDbNameMock = mock(async () => "trade-data-db");
58
- queryMock = mock(async (_dbName: string, _sql: string, _remote: boolean) => "[mock query result]");
61
+ queryMock = mock(
62
+ async (_dbName: string, _sql: string, _remote: boolean) =>
63
+ "[mock query result]"
64
+ );
59
65
  exportMock = mock(async (_dbName: string) => "backup-2026-05-13.sql");
60
66
  resolveNamespaceIdMock = mock(async () => "ns-id-123");
61
67
  getMock = mock(async (_nsId: string, _key: string) => "false");
62
68
  setMock = mock(async (_nsId: string, _key: string, _value: string) => {});
63
69
 
64
70
  // Install mocks on prototypes
65
- (MonitorService.prototype as Record<string, unknown>).checkAllWorkerHealth = checkAllWorkerHealthMock;
66
- (DbService.prototype as Record<string, unknown>).resolveDbName = resolveDbNameMock;
71
+ (MonitorService.prototype as Record<string, unknown>).checkAllWorkerHealth =
72
+ checkAllWorkerHealthMock;
73
+ (DbService.prototype as Record<string, unknown>).resolveDbName =
74
+ resolveDbNameMock;
67
75
  (DbService.prototype as Record<string, unknown>).query = queryMock;
68
76
  (DbService.prototype as Record<string, unknown>).export = exportMock;
69
- (KvSyncService.prototype as Record<string, unknown>).resolveNamespaceId = resolveNamespaceIdMock;
77
+ (KvSyncService.prototype as Record<string, unknown>).resolveNamespaceId =
78
+ resolveNamespaceIdMock;
70
79
  (KvSyncService.prototype as Record<string, unknown>).get = getMock;
71
80
  (KvSyncService.prototype as Record<string, unknown>).set = setMock;
72
81
  });
73
82
 
74
83
  afterEach(() => {
75
84
  mock.restore();
76
- (MonitorService.prototype as Record<string, unknown>).checkAllWorkerHealth = origCheckAll;
77
- (DbService.prototype as Record<string, unknown>).resolveDbName = origResolveDbName;
85
+ (MonitorService.prototype as Record<string, unknown>).checkAllWorkerHealth =
86
+ origCheckAll;
87
+ (DbService.prototype as Record<string, unknown>).resolveDbName =
88
+ origResolveDbName;
78
89
  (DbService.prototype as Record<string, unknown>).query = origQuery;
79
90
  (DbService.prototype as Record<string, unknown>).export = origExport;
80
- (KvSyncService.prototype as Record<string, unknown>).resolveNamespaceId = origResolveNs;
91
+ (KvSyncService.prototype as Record<string, unknown>).resolveNamespaceId =
92
+ origResolveNs;
81
93
  (KvSyncService.prototype as Record<string, unknown>).get = origGet;
82
94
  (KvSyncService.prototype as Record<string, unknown>).set = origSet;
83
95
  });
@@ -172,7 +184,9 @@ describe("registerMonitorCommand", () => {
172
184
  checkAllWorkerHealthMock = mock(async () => {
173
185
  throw new Error("Connection failed");
174
186
  });
175
- (MonitorService.prototype as Record<string, unknown>).checkAllWorkerHealth = checkAllWorkerHealthMock;
187
+ (
188
+ MonitorService.prototype as Record<string, unknown>
189
+ ).checkAllWorkerHealth = checkAllWorkerHealthMock;
176
190
 
177
191
  const program = await createProgram();
178
192
  await program.parseAsync(["monitor", "status"], { from: "user" });
@@ -206,20 +220,34 @@ describe("registerMonitorCommand", () => {
206
220
  describe("monitor kill-switch", () => {
207
221
  it("shows kill switch status", async () => {
208
222
  const program = await createProgram();
209
- await program.parseAsync(["monitor", "kill-switch", "show"], { from: "user" });
223
+ await program.parseAsync(["monitor", "kill-switch", "show"], {
224
+ from: "user",
225
+ });
210
226
  expect(getMock).toHaveBeenCalledWith("ns-id-123", "trade:kill_switch");
211
227
  });
212
228
 
213
229
  it("turns kill switch on", async () => {
214
230
  const program = await createProgram();
215
- await program.parseAsync(["monitor", "kill-switch", "on"], { from: "user" });
216
- expect(setMock).toHaveBeenCalledWith("ns-id-123", "trade:kill_switch", "true");
231
+ await program.parseAsync(["monitor", "kill-switch", "on"], {
232
+ from: "user",
233
+ });
234
+ expect(setMock).toHaveBeenCalledWith(
235
+ "ns-id-123",
236
+ "trade:kill_switch",
237
+ "true"
238
+ );
217
239
  });
218
240
 
219
241
  it("turns kill switch off", async () => {
220
242
  const program = await createProgram();
221
- await program.parseAsync(["monitor", "kill-switch", "off"], { from: "user" });
222
- expect(setMock).toHaveBeenCalledWith("ns-id-123", "trade:kill_switch", "false");
243
+ await program.parseAsync(["monitor", "kill-switch", "off"], {
244
+ from: "user",
245
+ });
246
+ expect(setMock).toHaveBeenCalledWith(
247
+ "ns-id-123",
248
+ "trade:kill_switch",
249
+ "false"
250
+ );
223
251
  });
224
252
  });
225
253
 
@@ -2,16 +2,16 @@ import { Command } from "commander";
2
2
  import { DbService } from "../../services/db/index.js";
3
3
  import { MonitorService } from "./monitor-service.js";
4
4
  import { KvSyncService } from "../../services/kv/kv-sync-service.js";
5
- import { formatSuccess, formatError, formatTable } from "../../utils/formatters.js";
5
+ import {
6
+ formatSuccess,
7
+ formatError,
8
+ formatTable,
9
+ getFormatOptions,
10
+ } from "../../utils/formatters.js";
6
11
  import type { FormatOptions } from "../../utils/formatters.js";
7
12
  import { ExitCode } from "../../utils/errors.js";
8
13
  import { theme } from "../../utils/theme.js";
9
14
 
10
- function getFormatOptions(cmd: Command): FormatOptions {
11
- const opts = cmd.optsWithGlobals();
12
- return { json: Boolean(opts.json), quiet: Boolean(opts.quiet) };
13
- }
14
-
15
15
  async function doMonitorStatus(fmt: FormatOptions): Promise<void> {
16
16
  try {
17
17
  const monitor = new MonitorService();
@@ -39,7 +39,10 @@ async function doMonitorStatus(fmt: FormatOptions): Promise<void> {
39
39
  }
40
40
  }
41
41
 
42
- async function doMonitorTrades(limit: number, fmt: FormatOptions): Promise<void> {
42
+ async function doMonitorTrades(
43
+ limit: number,
44
+ fmt: FormatOptions
45
+ ): Promise<void> {
43
46
  try {
44
47
  const db = new DbService();
45
48
  const dbName = await db.resolveDbName();
@@ -52,7 +55,10 @@ async function doMonitorTrades(limit: number, fmt: FormatOptions): Promise<void>
52
55
  }
53
56
  }
54
57
 
55
- async function doMonitorLogs(workerName: string | undefined, fmt: FormatOptions): Promise<void> {
58
+ async function doMonitorLogs(
59
+ workerName: string | undefined,
60
+ fmt: FormatOptions
61
+ ): Promise<void> {
56
62
  try {
57
63
  const db = new DbService();
58
64
  const dbName = await db.resolveDbName();
@@ -72,7 +78,7 @@ async function doMonitorLogs(workerName: string | undefined, fmt: FormatOptions)
72
78
 
73
79
  async function doMonitorKillSwitch(
74
80
  action: "show" | "on" | "off",
75
- fmt: FormatOptions,
81
+ fmt: FormatOptions
76
82
  ): Promise<void> {
77
83
  try {
78
84
  const kv = new KvSyncService();
@@ -81,16 +87,20 @@ async function doMonitorKillSwitch(
81
87
 
82
88
  if (action === "show") {
83
89
  const value = await kv.get(namespaceId, key);
84
- const status = value === "true"
85
- ? "KILL SWITCH IS ON (trading halted)"
86
- : value === "false"
87
- ? "Kill switch is off (trading active)"
88
- : `Kill switch value: ${value ?? "(not set)"}`;
90
+ const status =
91
+ value === "true"
92
+ ? "KILL SWITCH IS ON (trading halted)"
93
+ : value === "false"
94
+ ? "Kill switch is off (trading active)"
95
+ : `Kill switch value: ${value ?? "(not set)"}`;
89
96
  process.stdout.write(`${theme.info(status)}\n`);
90
97
  } else {
91
98
  const newValue = action === "on" ? "true" : "false";
92
99
  await kv.set(namespaceId, key, newValue);
93
- formatSuccess(`Kill switch turned ${action === "on" ? "ON" : "OFF"}`, fmt);
100
+ formatSuccess(
101
+ `Kill switch turned ${action === "on" ? "ON" : "OFF"}`,
102
+ fmt
103
+ );
94
104
  }
95
105
  } catch (err) {
96
106
  formatError(err instanceof Error ? err.message : String(err), fmt);
@@ -170,7 +180,11 @@ EXAMPLES:
170
180
  .command("trades")
171
181
  .summary("Show recent trades from D1")
172
182
  .description("Query the trades table for the most recent entries.")
173
- .argument("[limit]", "Number of trades to show (default: 10, max: 100)", "10")
183
+ .argument(
184
+ "[limit]",
185
+ "Number of trades to show (default: 10, max: 100)",
186
+ "10"
187
+ )
174
188
  .action(async function (this: Command, limit: string) {
175
189
  const fmt = getFormatOptions(this);
176
190
  await doMonitorTrades(parseInt(limit, 10) || 10, fmt);
@@ -179,7 +193,9 @@ EXAMPLES:
179
193
  monitorCmd
180
194
  .command("logs")
181
195
  .summary("Show recent system logs from D1")
182
- .description("Query the system_logs table. Optionally filter by worker name.")
196
+ .description(
197
+ "Query the system_logs table. Optionally filter by worker name."
198
+ )
183
199
  .argument("[worker]", "Worker name to filter (optional)")
184
200
  .action(async function (this: Command, worker?: string) {
185
201
  const fmt = getFormatOptions(this);
@@ -210,7 +226,9 @@ Commands:
210
226
  ksCmd
211
227
  .command("on")
212
228
  .summary("Halt all trading")
213
- .description("Set trade:kill_switch=true to stop all trading operations. WARNING: This halts ALL trading activity immediately.")
229
+ .description(
230
+ "Set trade:kill_switch=true to stop all trading operations. WARNING: This halts ALL trading activity immediately."
231
+ )
214
232
  .action(async function (this: Command) {
215
233
  const fmt = getFormatOptions(this);
216
234
  await doMonitorKillSwitch("on", fmt);
@@ -219,7 +237,9 @@ Commands:
219
237
  ksCmd
220
238
  .command("off")
221
239
  .summary("Resume trading")
222
- .description("Set trade:kill_switch=false to resume normal trading operations.")
240
+ .description(
241
+ "Set trade:kill_switch=false to resume normal trading operations."
242
+ )
223
243
  .action(async function (this: Command) {
224
244
  const fmt = getFormatOptions(this);
225
245
  await doMonitorKillSwitch("off", fmt);
@@ -228,7 +248,9 @@ Commands:
228
248
  monitorCmd
229
249
  .command("queue-depth")
230
250
  .summary("Show queue details")
231
- .description("List queues via wrangler queues list to show configured queues.")
251
+ .description(
252
+ "List queues via wrangler queues list to show configured queues."
253
+ )
232
254
  .action(async function (this: Command) {
233
255
  const fmt = getFormatOptions(this);
234
256
  await doMonitorQueueDepth(fmt);
@@ -237,7 +259,9 @@ Commands:
237
259
  monitorCmd
238
260
  .command("backup")
239
261
  .summary("Export D1 database to .sql file")
240
- .description("Export the D1 database to a timestamped .sql backup file via wrangler d1 export.")
262
+ .description(
263
+ "Export the D1 database to a timestamped .sql backup file via wrangler d1 export."
264
+ )
241
265
  .action(async function (this: Command) {
242
266
  const fmt = getFormatOptions(this);
243
267
  await doMonitorBackup(fmt);
@@ -27,12 +27,22 @@ export class MonitorService {
27
27
  for (const name of enabled) {
28
28
  const url = `https://${name}.${prefix}.workers.dev/health`;
29
29
  try {
30
- const response = await fetch(url, { signal: AbortSignal.timeout(5000) });
30
+ const response = await fetch(url, {
31
+ signal: AbortSignal.timeout(5000),
32
+ });
31
33
  if (response.ok) {
32
- results.push({ worker: name, status: "healthy", statusCode: response.status });
34
+ results.push({
35
+ worker: name,
36
+ status: "healthy",
37
+ statusCode: response.status,
38
+ });
33
39
  healthy++;
34
40
  } else {
35
- results.push({ worker: name, status: "degraded", statusCode: response.status });
41
+ results.push({
42
+ worker: name,
43
+ status: "degraded",
44
+ statusCode: response.status,
45
+ });
36
46
  degraded++;
37
47
  }
38
48
  } catch (err) {
@@ -45,6 +55,11 @@ export class MonitorService {
45
55
  }
46
56
  }
47
57
 
48
- return { workers: results, healthyCount: healthy, degradedCount: degraded, unreachableCount: unreachable };
58
+ return {
59
+ workers: results,
60
+ healthyCount: healthy,
61
+ degradedCount: degraded,
62
+ unreachableCount: unreachable,
63
+ };
49
64
  }
50
65
  }
@@ -40,18 +40,22 @@ beforeEach(() => {
40
40
  process.exitCode = 0;
41
41
 
42
42
  // Restore originals
43
- (RepairService.prototype as Record<string, unknown>).runSystemCheck = origRunSystemCheck;
43
+ (RepairService.prototype as Record<string, unknown>).runSystemCheck =
44
+ origRunSystemCheck;
44
45
  (CloudflareService.prototype as Record<string, unknown>).deploy = origDeploy;
45
46
  (CloudflareService.prototype as Record<string, unknown>).d1List = origD1List;
46
47
  (CloudflareService.prototype as Record<string, unknown>).kvList = origKvList;
47
48
  (CloudflareService.prototype as Record<string, unknown>).r2List = origR2List;
48
- (CloudflareService.prototype as Record<string, unknown>).queueList = origQueueList;
49
- (DbService.prototype as Record<string, unknown>).resolveDbName = origResolveDbName;
49
+ (CloudflareService.prototype as Record<string, unknown>).queueList =
50
+ origQueueList;
51
+ (DbService.prototype as Record<string, unknown>).resolveDbName =
52
+ origResolveDbName;
50
53
  (DbService.prototype as Record<string, unknown>).apply = origApply;
51
54
  (DbService.prototype as Record<string, unknown>).migrate = origMigrate;
52
55
  (DbService.prototype as Record<string, unknown>).export = origExport;
53
56
  (DbService.prototype as Record<string, unknown>).reset = origReset;
54
- (KvSyncService.prototype as Record<string, unknown>).resolveNamespaceId = origResolveNs;
57
+ (KvSyncService.prototype as Record<string, unknown>).resolveNamespaceId =
58
+ origResolveNs;
55
59
  (KvSyncService.prototype as Record<string, unknown>).set = origKvSet;
56
60
 
57
61
  // Fresh mocks
@@ -73,24 +77,29 @@ beforeEach(() => {
73
77
  data: { url: "https://test-worker.cryptolinx.workers.dev" },
74
78
  }));
75
79
 
76
- (RepairService.prototype as Record<string, unknown>).runSystemCheck = runSystemCheckMock;
80
+ (RepairService.prototype as Record<string, unknown>).runSystemCheck =
81
+ runSystemCheckMock;
77
82
  (CloudflareService.prototype as Record<string, unknown>).deploy = deployMock;
78
83
  });
79
84
 
80
85
  afterEach(() => {
81
86
  mock.restore();
82
- (RepairService.prototype as Record<string, unknown>).runSystemCheck = origRunSystemCheck;
87
+ (RepairService.prototype as Record<string, unknown>).runSystemCheck =
88
+ origRunSystemCheck;
83
89
  (CloudflareService.prototype as Record<string, unknown>).deploy = origDeploy;
84
90
  (CloudflareService.prototype as Record<string, unknown>).d1List = origD1List;
85
91
  (CloudflareService.prototype as Record<string, unknown>).kvList = origKvList;
86
92
  (CloudflareService.prototype as Record<string, unknown>).r2List = origR2List;
87
- (CloudflareService.prototype as Record<string, unknown>).queueList = origQueueList;
88
- (DbService.prototype as Record<string, unknown>).resolveDbName = origResolveDbName;
93
+ (CloudflareService.prototype as Record<string, unknown>).queueList =
94
+ origQueueList;
95
+ (DbService.prototype as Record<string, unknown>).resolveDbName =
96
+ origResolveDbName;
89
97
  (DbService.prototype as Record<string, unknown>).apply = origApply;
90
98
  (DbService.prototype as Record<string, unknown>).migrate = origMigrate;
91
99
  (DbService.prototype as Record<string, unknown>).export = origExport;
92
100
  (DbService.prototype as Record<string, unknown>).reset = origReset;
93
- (KvSyncService.prototype as Record<string, unknown>).resolveNamespaceId = origResolveNs;
101
+ (KvSyncService.prototype as Record<string, unknown>).resolveNamespaceId =
102
+ origResolveNs;
94
103
  (KvSyncService.prototype as Record<string, unknown>).set = origKvSet;
95
104
  });
96
105
 
@@ -127,7 +136,9 @@ describe("registerRepairCommand", () => {
127
136
  const repairCmd = program.commands.find((c) => c.name() === "repair")!;
128
137
  const workerCmd = repairCmd.commands.find((c) => c.name() === "worker");
129
138
  expect(workerCmd).toBeDefined();
130
- expect(workerCmd!.registeredArguments.some((a) => a.name() === "name")).toBe(true);
139
+ expect(
140
+ workerCmd!.registeredArguments.some((a) => a.name() === "name")
141
+ ).toBe(true);
131
142
  });
132
143
 
133
144
  it("registers 'repair infra' subcommand", async () => {
@@ -139,7 +150,9 @@ describe("registerRepairCommand", () => {
139
150
  it("registers 'repair secrets' subcommand", async () => {
140
151
  const program = await createProgram();
141
152
  const repairCmd = program.commands.find((c) => c.name() === "repair")!;
142
- expect(repairCmd.commands.find((c) => c.name() === "secrets")).toBeDefined();
153
+ expect(
154
+ repairCmd.commands.find((c) => c.name() === "secrets")
155
+ ).toBeDefined();
143
156
  });
144
157
 
145
158
  it("registers 'repair kv' subcommand", async () => {
@@ -157,7 +170,9 @@ describe("registerRepairCommand", () => {
157
170
  it("registers 'repair rebuild' subcommand", async () => {
158
171
  const program = await createProgram();
159
172
  const repairCmd = program.commands.find((c) => c.name() === "repair")!;
160
- expect(repairCmd.commands.find((c) => c.name() === "rebuild")).toBeDefined();
173
+ expect(
174
+ repairCmd.commands.find((c) => c.name() === "rebuild")
175
+ ).toBeDefined();
161
176
  });
162
177
 
163
178
  // -- repair check ---------------------------------------------------------
@@ -176,8 +191,11 @@ describe("registerRepairCommand", () => {
176
191
  });
177
192
 
178
193
  it("sets exitCode on failure", async () => {
179
- runSystemCheckMock = mock(async () => { throw new Error("Check failed"); });
180
- (RepairService.prototype as Record<string, unknown>).runSystemCheck = runSystemCheckMock;
194
+ runSystemCheckMock = mock(async () => {
195
+ throw new Error("Check failed");
196
+ });
197
+ (RepairService.prototype as Record<string, unknown>).runSystemCheck =
198
+ runSystemCheckMock;
181
199
 
182
200
  const program = await createProgram();
183
201
  await program.parseAsync(["repair", "check"], { from: "user" });
@@ -197,7 +215,9 @@ describe("registerRepairCommand", () => {
197
215
  it("handles unknown worker name", async () => {
198
216
  // Mock getWorker to return undefined
199
217
  const program = await createProgram();
200
- await program.parseAsync(["repair", "worker", "nonexistent"], { from: "user" });
218
+ await program.parseAsync(["repair", "worker", "nonexistent"], {
219
+ from: "user",
220
+ });
201
221
  expect(process.exitCode).toBe(1);
202
222
  });
203
223
  });
@@ -4,17 +4,14 @@ import { CloudflareService } from "../../services/cloudflare/index.js";
4
4
  import { DbService } from "../../services/db/index.js";
5
5
  import { KvSyncService } from "../../services/kv/kv-sync-service.js";
6
6
  import { SecretsService } from "../../services/secrets/index.js";
7
- import { formatError, formatSuccess, type FormatOptions } from "../../utils/formatters.js";
7
+ import {
8
+ formatError,
9
+ formatSuccess,
10
+ type FormatOptions,
11
+ getFormatOptions,
12
+ } from "../../utils/formatters.js";
8
13
  import { CLIError, ExitCode } from "../../utils/errors.js";
9
14
 
10
- function getFormatOptions(cmd: Command): FormatOptions {
11
- const rootCmd = cmd.parent?.parent as Command | undefined;
12
- return {
13
- json: Boolean(rootCmd?.optsWithGlobals()?.json),
14
- quiet: Boolean(rootCmd?.optsWithGlobals()?.quiet),
15
- };
16
- }
17
-
18
15
  async function handleCheck(fmt: FormatOptions): Promise<void> {
19
16
  try {
20
17
  const svc = new RepairService();
@@ -22,7 +19,10 @@ async function handleCheck(fmt: FormatOptions): Promise<void> {
22
19
  if (result.allPassed) {
23
20
  formatSuccess("All checks passed", fmt);
24
21
  } else {
25
- formatError(new CLIError(`${result.failedCount} check(s) failed`, ExitCode.ERROR), fmt);
22
+ formatError(
23
+ new CLIError(`${result.failedCount} check(s) failed`, ExitCode.ERROR),
24
+ fmt
25
+ );
26
26
  process.exitCode = ExitCode.ERROR;
27
27
  }
28
28
  } catch (err) {
@@ -38,16 +38,25 @@ async function handleWorker(name: string, fmt: FormatOptions): Promise<void> {
38
38
  await config.load();
39
39
  const workerConfig = config.getWorker(name);
40
40
  if (!workerConfig) {
41
- formatError(new CLIError(`Worker "${name}" not found`, ExitCode.ERROR), fmt);
41
+ formatError(
42
+ new CLIError(`Worker "${name}" not found`, ExitCode.ERROR),
43
+ fmt
44
+ );
42
45
  process.exitCode = ExitCode.ERROR;
43
46
  return;
44
47
  }
45
48
  const cf = new CloudflareService();
46
49
  const result = await cf.deploy(workerConfig.path);
47
50
  if (result.ok) {
48
- formatSuccess(`Worker "${name}" deployed — ${result.data.url}`, fmt);
51
+ formatSuccess(`Worker "${name}" deployed — ${result.value.url}`, fmt);
49
52
  } else {
50
- formatError(new CLIError(`Failed to deploy "${name}": ${result.error}`, ExitCode.ERROR), fmt);
53
+ formatError(
54
+ new CLIError(
55
+ `Failed to deploy "${name}": ${result.error}`,
56
+ ExitCode.ERROR
57
+ ),
58
+ fmt
59
+ );
51
60
  process.exitCode = ExitCode.ERROR;
52
61
  }
53
62
  } catch (err) {
@@ -63,7 +72,10 @@ async function handleInfra(fmt: FormatOptions): Promise<void> {
63
72
  const kv = await cf.kvList();
64
73
  const r2 = await cf.r2List();
65
74
  const q = await cf.queueList();
66
- formatSuccess(`D1: ${d1.ok ? "ok" : "fail"}, KV: ${kv.ok ? "ok" : "fail"}, R2: ${r2.ok ? "ok" : "fail"}, Queues: ${q.ok ? "ok" : "fail"}`, fmt);
75
+ formatSuccess(
76
+ `D1: ${d1.ok ? "ok" : "fail"}, KV: ${kv.ok ? "ok" : "fail"}, R2: ${r2.ok ? "ok" : "fail"}, Queues: ${q.ok ? "ok" : "fail"}`,
77
+ fmt
78
+ );
67
79
  } catch (err) {
68
80
  formatError(err instanceof Error ? err : String(err), fmt);
69
81
  process.exitCode = ExitCode.ERROR;
@@ -80,7 +92,10 @@ async function handleSecrets(fmt: FormatOptions): Promise<void> {
80
92
  missing += check.missing.length;
81
93
  }
82
94
  if (missing > 0) {
83
- formatError(new CLIError(`${missing} secret(s) missing`, ExitCode.ERROR), fmt);
95
+ formatError(
96
+ new CLIError(`${missing} secret(s) missing`, ExitCode.ERROR),
97
+ fmt
98
+ );
84
99
  process.exitCode = ExitCode.ERROR;
85
100
  } else {
86
101
  formatSuccess("All secrets present", fmt);
@@ -139,11 +154,15 @@ export function registerRepairCommand(program: Command): void {
139
154
  const repairCmd = program
140
155
  .command("repair")
141
156
  .summary("Diagnose and repair the Hoox system")
142
- .description("Run checks, deploy workers, or fix infrastructure, secrets, KV, and DB issues.");
157
+ .description(
158
+ "Run checks, deploy workers, or fix infrastructure, secrets, KV, and DB issues."
159
+ );
143
160
 
144
161
  repairCmd
145
162
  .command("check")
146
- .description("Run system diagnostics (workers, deps, types, infra, secrets)")
163
+ .description(
164
+ "Run system diagnostics (workers, deps, types, infra, secrets)"
165
+ )
147
166
  .action(async () => {
148
167
  const fmt = getFormatOptions(repairCmd);
149
168
  await handleCheck(fmt);
@@ -191,7 +210,9 @@ export function registerRepairCommand(program: Command): void {
191
210
 
192
211
  repairCmd
193
212
  .command("rebuild")
194
- .description("Full rebuild: check, deploy all workers, fix infra/secrets/db")
213
+ .description(
214
+ "Full rebuild: check, deploy all workers, fix infra/secrets/db"
215
+ )
195
216
  .action(async () => {
196
217
  const fmt = getFormatOptions(repairCmd);
197
218
  await handleRebuild(fmt);