@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
@@ -18,25 +18,15 @@ import {
18
18
  formatError,
19
19
  formatTable,
20
20
  formatJson,
21
+ getFormatOptions,
21
22
  } from "../../utils/formatters.js";
22
23
  import { CLIError, ExitCode } from "../../utils/errors.js";
23
24
  import type { FormatOptions } from "../../utils/formatters.js";
24
25
 
25
- function getFormatOptions(cmd: Command): FormatOptions {
26
- const rootCmd = cmd.parent?.parent as Command | undefined;
27
- return {
28
- json: Boolean(rootCmd?.optsWithGlobals()?.json),
29
- quiet: Boolean(rootCmd?.optsWithGlobals()?.quiet),
30
- };
31
- }
32
-
33
26
  /**
34
27
  * Resolve database name from config or --database flag.
35
28
  */
36
- async function resolveDb(
37
- cmd: Command,
38
- svc: DbService,
39
- ): Promise<string> {
29
+ async function resolveDb(cmd: Command, svc: DbService): Promise<string> {
40
30
  const opts = cmd.optsWithGlobals<{ database?: string }>();
41
31
  return await svc.resolveDbName(opts.database);
42
32
  }
@@ -49,13 +39,13 @@ async function handleApply(
49
39
  opts: FormatOptions,
50
40
  dbName: string,
51
41
  remote: boolean,
52
- file?: string,
42
+ file?: string
53
43
  ): Promise<void> {
54
44
  const svc = new DbService();
55
45
  const output = await svc.apply(dbName, remote, file);
56
46
  formatSuccess(
57
47
  `Schema applied to ${dbName}${remote ? " (remote)" : " (local)"}`,
58
- opts,
48
+ opts
59
49
  );
60
50
  if (!opts.quiet && output) {
61
51
  process.stdout.write(`${output}\n`);
@@ -69,13 +59,13 @@ async function handleApply(
69
59
  async function handleMigrate(
70
60
  opts: FormatOptions,
71
61
  dbName: string,
72
- remote: boolean,
62
+ remote: boolean
73
63
  ): Promise<void> {
74
64
  const svc = new DbService();
75
65
  const output = await svc.migrate(dbName, remote);
76
66
  formatSuccess(
77
67
  `Migrations applied to ${dbName}${remote ? " (remote)" : " (local)"}`,
78
- opts,
68
+ opts
79
69
  );
80
70
  if (!opts.quiet && output) {
81
71
  process.stdout.write(`${output}\n`);
@@ -89,7 +79,7 @@ async function handleMigrate(
89
79
  async function handleList(
90
80
  opts: FormatOptions,
91
81
  dbName: string,
92
- remote: boolean,
82
+ remote: boolean
93
83
  ): Promise<void> {
94
84
  const svc = new DbService();
95
85
  const tables = await svc.listTables(dbName, remote);
@@ -114,7 +104,7 @@ async function handleQuery(
114
104
  opts: FormatOptions,
115
105
  dbName: string,
116
106
  sql: string,
117
- remote: boolean,
107
+ remote: boolean
118
108
  ): Promise<void> {
119
109
  const svc = new DbService();
120
110
  const output = await svc.query(dbName, sql, remote);
@@ -139,7 +129,7 @@ async function handleQuery(
139
129
  async function handleExport(
140
130
  opts: FormatOptions,
141
131
  dbName: string,
142
- outputPath?: string,
132
+ outputPath?: string
143
133
  ): Promise<void> {
144
134
  const svc = new DbService();
145
135
  const path = await svc.export(dbName, outputPath);
@@ -153,7 +143,7 @@ async function handleExport(
153
143
  async function handleReset(
154
144
  opts: FormatOptions,
155
145
  dbName: string,
156
- confirmed: boolean,
146
+ confirmed: boolean
157
147
  ): Promise<void> {
158
148
  if (!confirmed) {
159
149
  const answer = await p.confirm({
@@ -204,7 +194,7 @@ EXAMPLES:
204
194
  hoox db list
205
195
  hoox db query "SELECT * FROM trade_signals LIMIT 5"
206
196
  hoox db export
207
- hoox db reset`,
197
+ hoox db reset`
208
198
  )
209
199
  .option("--database <name>", "Database name (auto-detected if omitted)")
210
200
  .option("--remote", "Operate on production (remote) database");
@@ -219,7 +209,9 @@ EXAMPLES:
219
209
  try {
220
210
  const svc = new DbService();
221
211
  const dbName = await resolveDb(cmd, svc);
222
- const remote = Boolean(cmd.optsWithGlobals<{ remote?: boolean }>().remote);
212
+ const remote = Boolean(
213
+ cmd.optsWithGlobals<{ remote?: boolean }>().remote
214
+ );
223
215
  await handleApply(opts, dbName, remote, options.file);
224
216
  } catch (err) {
225
217
  formatError(err instanceof Error ? err : String(err), opts);
@@ -236,7 +228,9 @@ EXAMPLES:
236
228
  try {
237
229
  const svc = new DbService();
238
230
  const dbName = await resolveDb(cmd, svc);
239
- const remote = Boolean(cmd.optsWithGlobals<{ remote?: boolean }>().remote);
231
+ const remote = Boolean(
232
+ cmd.optsWithGlobals<{ remote?: boolean }>().remote
233
+ );
240
234
  await handleMigrate(opts, dbName, remote);
241
235
  } catch (err) {
242
236
  formatError(err instanceof Error ? err : String(err), opts);
@@ -253,7 +247,9 @@ EXAMPLES:
253
247
  try {
254
248
  const svc = new DbService();
255
249
  const dbName = await resolveDb(cmd, svc);
256
- const remote = Boolean(cmd.optsWithGlobals<{ remote?: boolean }>().remote);
250
+ const remote = Boolean(
251
+ cmd.optsWithGlobals<{ remote?: boolean }>().remote
252
+ );
257
253
  await handleList(opts, dbName, remote);
258
254
  } catch (err) {
259
255
  formatError(err instanceof Error ? err : String(err), opts);
@@ -270,7 +266,9 @@ EXAMPLES:
270
266
  try {
271
267
  const svc = new DbService();
272
268
  const dbName = await resolveDb(cmd, svc);
273
- const remote = Boolean(cmd.optsWithGlobals<{ remote?: boolean }>().remote);
269
+ const remote = Boolean(
270
+ cmd.optsWithGlobals<{ remote?: boolean }>().remote
271
+ );
274
272
  await handleQuery(opts, dbName, sql, remote);
275
273
  } catch (err) {
276
274
  formatError(err instanceof Error ? err : String(err), opts);
@@ -16,6 +16,7 @@ import {
16
16
  formatSuccess,
17
17
  formatError,
18
18
  type FormatOptions,
19
+ getFormatOptions,
19
20
  } from "../../utils/formatters.js";
20
21
  import { CLIError, ExitCode } from "../../utils/errors.js";
21
22
  import type { DeployResult } from "./types.js";
@@ -106,14 +107,6 @@ async function promptRebuildDecision(buildInfo: {
106
107
  return choice as "rebuild" | "deploy";
107
108
  }
108
109
 
109
- /**
110
- * Build the format options for output, reading global --json / --quiet flags.
111
- */
112
- function getFormatOptions(cmd: Command) {
113
- const opts = cmd.optsWithGlobals();
114
- return { json: Boolean(opts.json), quiet: Boolean(opts.quiet) };
115
- }
116
-
117
110
  /**
118
111
  * Deploy a single worker via CloudflareService.deploy().
119
112
  * Returns a DeployResult summarizing the outcome.
@@ -137,17 +130,17 @@ async function deploySingle(
137
130
  const result = await cf.deploy(workerConfig.path, env);
138
131
 
139
132
  if (result.ok) {
140
- const rawLine = result.data.rawOutput
133
+ const rawLine = result.value.rawOutput
141
134
  ?.split("\n")
142
135
  .find((l) => l.trim())
143
136
  ?.trim();
144
137
  return {
145
138
  worker: workerName,
146
- url: result.data.url,
139
+ url: result.value.url,
147
140
  success: true,
148
- size: result.data.size,
149
- startupTime: result.data.startupTime,
150
- versionId: result.data.versionId,
141
+ size: result.value.size,
142
+ startupTime: result.value.startupTime,
143
+ versionId: result.value.versionId,
151
144
  rawOutput: rawLine,
152
145
  };
153
146
  }
@@ -242,7 +235,9 @@ async function deployAll(
242
235
  log.step(` ${theme.dim("Startup:")} ${result.startupTime}`);
243
236
  }
244
237
  if (result.versionId) {
245
- log.step(` ${theme.dim("Version:")} ${result.versionId.slice(0, 8)}...`);
238
+ log.step(
239
+ ` ${theme.dim("Version:")} ${result.versionId.slice(0, 8)}...`
240
+ );
246
241
  }
247
242
  if (
248
243
  !result.url &&
@@ -267,7 +262,9 @@ async function deployAll(
267
262
  const succeeded = results.filter((r) => r.success).length;
268
263
  const failed = results.filter((r) => !r.success).length;
269
264
  if (failed > 0) {
270
- log.warn(`Summary: ${succeeded}/${allItems.length} deployed (${failed} failed)`);
265
+ log.warn(
266
+ `Summary: ${succeeded}/${allItems.length} deployed (${failed} failed)`
267
+ );
271
268
  }
272
269
 
273
270
  return results;
@@ -532,7 +529,7 @@ async function doTelegramWebhook(
532
529
  fmt: FormatOptions,
533
530
  token?: string,
534
531
  secretToken?: string,
535
- subdomain?: string,
532
+ subdomain?: string
536
533
  ): Promise<void> {
537
534
  try {
538
535
  // Resolve subdomain from flag or config
@@ -551,7 +548,13 @@ async function doTelegramWebhook(
551
548
  botToken = envVars["TELEGRAM_BOT_TOKEN"];
552
549
  }
553
550
  if (!botToken) {
554
- formatError(new CLIError("Telegram bot token not found. Provide --token or set TELEGRAM_BOT_TOKEN in .env.local", ExitCode.ERROR), fmt);
551
+ formatError(
552
+ new CLIError(
553
+ "Telegram bot token not found. Provide --token or set TELEGRAM_BOT_TOKEN in .env.local",
554
+ ExitCode.ERROR
555
+ ),
556
+ fmt
557
+ );
555
558
  process.exitCode = ExitCode.ERROR;
556
559
  return;
557
560
  }
@@ -563,7 +566,13 @@ async function doTelegramWebhook(
563
566
  webhookSecret = envVars["TELEGRAM_SECRET_TOKEN"];
564
567
  }
565
568
  if (!webhookSecret) {
566
- formatError(new CLIError("Telegram secret token not found. Provide --secret-token or set TELEGRAM_SECRET_TOKEN in .env.local", ExitCode.ERROR), fmt);
569
+ formatError(
570
+ new CLIError(
571
+ "Telegram secret token not found. Provide --secret-token or set TELEGRAM_SECRET_TOKEN in .env.local",
572
+ ExitCode.ERROR
573
+ ),
574
+ fmt
575
+ );
567
576
  process.exitCode = ExitCode.ERROR;
568
577
  return;
569
578
  }
@@ -576,19 +585,34 @@ async function doTelegramWebhook(
576
585
  if (info.ok && info.url) {
577
586
  process.stdout.write(`${theme.dim("Current webhook:")} ${info.url}\n`);
578
587
  if (info.pending_update_count !== undefined) {
579
- process.stdout.write(`${theme.dim("Pending updates:")} ${info.pending_update_count}\n`);
588
+ process.stdout.write(
589
+ `${theme.dim("Pending updates:")} ${info.pending_update_count}\n`
590
+ );
580
591
  }
581
592
  }
582
593
 
583
594
  // Set webhook
584
- process.stdout.write(`${theme.info("Setting webhook to:")} ${webhookUrl}\n`);
585
- const result = await telegram.setWebhook(botToken, webhookUrl, webhookSecret);
595
+ process.stdout.write(
596
+ `${theme.info("Setting webhook to:")} ${webhookUrl}\n`
597
+ );
598
+ const result = await telegram.setWebhook(
599
+ botToken,
600
+ webhookUrl,
601
+ webhookSecret
602
+ );
586
603
 
587
604
  if (result.ok) {
588
605
  formatSuccess("Telegram webhook set successfully", fmt);
589
- if (result.description) process.stdout.write(` ${theme.dim(result.description)}\n`);
606
+ if (result.description)
607
+ process.stdout.write(` ${theme.dim(result.description)}\n`);
590
608
  } else {
591
- formatError(new CLIError(`Telegram webhook failed: ${result.error || result.description || "Unknown error"}`, ExitCode.ERROR), fmt);
609
+ formatError(
610
+ new CLIError(
611
+ `Telegram webhook failed: ${result.error || result.description || "Unknown error"}`,
612
+ ExitCode.ERROR
613
+ ),
614
+ fmt
615
+ );
592
616
  process.exitCode = ExitCode.ERROR;
593
617
  }
594
618
  } catch (err) {
@@ -610,12 +634,28 @@ async function doUpdateInternalUrls(fmt: FormatOptions): Promise<void> {
610
634
  const workers = config.listEnabledWorkers();
611
635
 
612
636
  // Try pages/dashboard first, fall back to workers/dashboard
613
- let filePath = resolve(process.cwd(), "pages", "dashboard", "wrangler.jsonc");
637
+ let filePath = resolve(
638
+ process.cwd(),
639
+ "pages",
640
+ "dashboard",
641
+ "wrangler.jsonc"
642
+ );
614
643
  if (!existsSync(filePath)) {
615
- filePath = resolve(process.cwd(), "workers", "dashboard", "wrangler.jsonc");
644
+ filePath = resolve(
645
+ process.cwd(),
646
+ "workers",
647
+ "dashboard",
648
+ "wrangler.jsonc"
649
+ );
616
650
  }
617
651
  if (!existsSync(filePath)) {
618
- formatError(new CLIError("Dashboard wrangler.jsonc not found (checked pages/dashboard and workers/dashboard)", ExitCode.ERROR), fmt);
652
+ formatError(
653
+ new CLIError(
654
+ "Dashboard wrangler.jsonc not found (checked pages/dashboard and workers/dashboard)",
655
+ ExitCode.ERROR
656
+ ),
657
+ fmt
658
+ );
619
659
  process.exitCode = ExitCode.ERROR;
620
660
  return;
621
661
  }
@@ -624,7 +664,13 @@ async function doUpdateInternalUrls(fmt: FormatOptions): Promise<void> {
624
664
  const errors: jsonc.ParseError[] = [];
625
665
  const parsed = jsonc.parse(content, errors) as Record<string, unknown>;
626
666
  if (errors.length > 0) {
627
- formatError(new CLIError("Invalid JSONC in dashboard wrangler.jsonc", ExitCode.ERROR), fmt);
667
+ formatError(
668
+ new CLIError(
669
+ "Invalid JSONC in dashboard wrangler.jsonc",
670
+ ExitCode.ERROR
671
+ ),
672
+ fmt
673
+ );
628
674
  process.exitCode = ExitCode.ERROR;
629
675
  return;
630
676
  }
@@ -637,7 +683,9 @@ async function doUpdateInternalUrls(fmt: FormatOptions): Promise<void> {
637
683
  const newUrl = `https://${name}.${prefix}.workers.dev`;
638
684
  if (vars[key] !== newUrl) {
639
685
  if (!fmt.quiet) {
640
- process.stdout.write(` ${theme.info("→")} ${key}: ${vars[key] ?? "(not set)"} ${theme.dim("→")} ${newUrl}\n`);
686
+ process.stdout.write(
687
+ ` ${theme.info("→")} ${key}: ${vars[key] ?? "(not set)"} ${theme.dim("→")} ${newUrl}\n`
688
+ );
641
689
  }
642
690
  vars[key] = newUrl;
643
691
  changesCount++;
@@ -653,7 +701,10 @@ async function doUpdateInternalUrls(fmt: FormatOptions): Promise<void> {
653
701
  formattingOptions: { tabSize: 2, insertSpaces: true },
654
702
  });
655
703
  writeFileSync(filePath, jsonc.applyEdits(content, edits), "utf-8");
656
- formatSuccess(`Updated ${changesCount} service URL(s) in dashboard wrangler.jsonc`, fmt);
704
+ formatSuccess(
705
+ `Updated ${changesCount} service URL(s) in dashboard wrangler.jsonc`,
706
+ fmt
707
+ );
657
708
  } catch (err) {
658
709
  formatError(err instanceof Error ? err.message : String(err), fmt);
659
710
  process.exitCode = ExitCode.ERROR;
@@ -666,7 +717,8 @@ async function doUpdateInternalUrls(fmt: FormatOptions): Promise<void> {
666
717
 
667
718
  async function doKvConfig(fmt: FormatOptions): Promise<void> {
668
719
  try {
669
- const { KvSyncService } = await import("../../services/kv/kv-sync-service.js");
720
+ const { KvSyncService } =
721
+ await import("../../services/kv/kv-sync-service.js");
670
722
  const kvSync = new KvSyncService();
671
723
 
672
724
  process.stdout.write(`${theme.info("Resolving CONFIG_KV namespace...")}\n`);
@@ -679,7 +731,9 @@ async function doKvConfig(fmt: FormatOptions): Promise<void> {
679
731
  const value = entry.default;
680
732
  if (!value || value === "") {
681
733
  if (!fmt.quiet) {
682
- process.stdout.write(` ${theme.dim("·")} ${entry.key} ${theme.dim("(no default, skipping)")}\n`);
734
+ process.stdout.write(
735
+ ` ${theme.dim("·")} ${entry.key} ${theme.dim("(no default, skipping)")}\n`
736
+ );
683
737
  }
684
738
  continue;
685
739
  }
@@ -690,7 +744,9 @@ async function doKvConfig(fmt: FormatOptions): Promise<void> {
690
744
  }
691
745
  setCount++;
692
746
  } catch (err) {
693
- process.stdout.write(` ${theme.error("✗")} ${entry.key}: ${err instanceof Error ? err.message : String(err)}\n`);
747
+ process.stdout.write(
748
+ ` ${theme.error("✗")} ${entry.key}: ${err instanceof Error ? err.message : String(err)}\n`
749
+ );
694
750
  errorCount++;
695
751
  }
696
752
  }
@@ -756,22 +812,33 @@ EXAMPLES:
756
812
  )
757
813
  .option("--env <env>", "Cloudflare environment (e.g. production, staging)")
758
814
  .option("--rebuild", "Force rebuild of dashboard before deploying")
759
- .option("--auto", "Skip dashboard rebuild prompt, use existing build if available")
760
- .action(async (options: { env?: string; rebuild?: boolean; auto?: boolean }) => {
761
- const fmt = getFormatOptions(program);
762
- try {
763
- const configService = new ConfigService();
764
- await configService.load();
765
- const cf = new CloudflareService();
766
-
767
- // Deploy all (workers + dashboard) in one go
768
- await deployAll(configService, cf, options.env, options.rebuild ?? false, options.auto ?? false);
769
- } catch (err) {
770
- const message = err instanceof Error ? err.message : String(err);
771
- formatError(message, fmt);
772
- process.exitCode = ExitCode.ERROR;
815
+ .option(
816
+ "--auto",
817
+ "Skip dashboard rebuild prompt, use existing build if available"
818
+ )
819
+ .action(
820
+ async (options: { env?: string; rebuild?: boolean; auto?: boolean }) => {
821
+ const fmt = getFormatOptions(program);
822
+ try {
823
+ const configService = new ConfigService();
824
+ await configService.load();
825
+ const cf = new CloudflareService();
826
+
827
+ // Deploy all (workers + dashboard) in one go
828
+ await deployAll(
829
+ configService,
830
+ cf,
831
+ options.env,
832
+ options.rebuild ?? false,
833
+ options.auto ?? false
834
+ );
835
+ } catch (err) {
836
+ const message = err instanceof Error ? err.message : String(err);
837
+ formatError(message, fmt);
838
+ process.exitCode = ExitCode.ERROR;
839
+ }
773
840
  }
774
- });
841
+ );
775
842
 
776
843
  // -- deploy workers ------------------------------------------------------
777
844
  deployCmd
@@ -1007,11 +1074,25 @@ EXAMPLES:
1007
1074
  )
1008
1075
  .option("--token <token>", "Telegram bot token (from @BotFather)")
1009
1076
  .option("--secret-token <secret>", "Telegram webhook secret token")
1010
- .option("--subdomain <prefix>", "Worker subdomain prefix (default: from config)")
1011
- .action(async (options: { token?: string; secretToken?: string; subdomain?: string }) => {
1012
- const fmt = getFormatOptions(program);
1013
- await doTelegramWebhook(fmt, options.token, options.secretToken, options.subdomain);
1014
- });
1077
+ .option(
1078
+ "--subdomain <prefix>",
1079
+ "Worker subdomain prefix (default: from config)"
1080
+ )
1081
+ .action(
1082
+ async (options: {
1083
+ token?: string;
1084
+ secretToken?: string;
1085
+ subdomain?: string;
1086
+ }) => {
1087
+ const fmt = getFormatOptions(program);
1088
+ await doTelegramWebhook(
1089
+ fmt,
1090
+ options.token,
1091
+ options.secretToken,
1092
+ options.subdomain
1093
+ );
1094
+ }
1095
+ );
1015
1096
 
1016
1097
  // -- deploy update-internal-urls ---------------------------------------
1017
1098
 
@@ -11,7 +11,7 @@ export class TelegramService {
11
11
  async setWebhook(
12
12
  botToken: string,
13
13
  webhookUrl: string,
14
- secretToken: string,
14
+ secretToken: string
15
15
  ): Promise<{ ok: boolean; description?: string; error?: string }> {
16
16
  try {
17
17
  const url = `https://api.telegram.org/bot${botToken}/setWebhook`;
@@ -20,10 +20,16 @@ export class TelegramService {
20
20
  headers: { "Content-Type": "application/json" },
21
21
  body: JSON.stringify({ url: webhookUrl, secret_token: secretToken }),
22
22
  });
23
- const data = (await response.json()) as { ok: boolean; description?: string };
23
+ const data = (await response.json()) as {
24
+ ok: boolean;
25
+ description?: string;
26
+ };
24
27
  return { ok: data.ok, description: data.description };
25
28
  } catch (err) {
26
- return { ok: false, error: err instanceof Error ? err.message : String(err) };
29
+ return {
30
+ ok: false,
31
+ error: err instanceof Error ? err.message : String(err),
32
+ };
27
33
  }
28
34
  }
29
35
 
@@ -39,10 +45,16 @@ export class TelegramService {
39
45
  error?: string;
40
46
  }> {
41
47
  try {
42
- const response = await fetch(`https://api.telegram.org/bot${botToken}/getWebhookInfo`);
48
+ const response = await fetch(
49
+ `https://api.telegram.org/bot${botToken}/getWebhookInfo`
50
+ );
43
51
  const data = (await response.json()) as {
44
52
  ok: boolean;
45
- result?: { url: string; has_custom_certificate: boolean; pending_update_count: number };
53
+ result?: {
54
+ url: string;
55
+ has_custom_certificate: boolean;
56
+ pending_update_count: number;
57
+ };
46
58
  description?: string;
47
59
  };
48
60
  if (data.ok && data.result) {
@@ -55,7 +67,10 @@ export class TelegramService {
55
67
  }
56
68
  return { ok: false, error: data.description };
57
69
  } catch (err) {
58
- return { ok: false, error: err instanceof Error ? err.message : String(err) };
70
+ return {
71
+ ok: false,
72
+ error: err instanceof Error ? err.message : String(err),
73
+ };
59
74
  }
60
75
  }
61
76
  }
@@ -14,22 +14,13 @@ import { ConfigService } from "../../services/config/index.js";
14
14
  import { CloudflareService } from "../../services/cloudflare/index.js";
15
15
  import { PrerequisitesService } from "../../services/prerequisites/index.js";
16
16
  import { DockerService } from "../../services/docker/index.js";
17
- import { formatSuccess, formatError } from "../../utils/formatters.js";
17
+ import {
18
+ formatSuccess,
19
+ formatError,
20
+ getFormatOptions,
21
+ } from "../../utils/formatters.js";
18
22
  import { CLIError, ExitCode } from "../../utils/errors.js";
19
23
 
20
- // ---------------------------------------------------------------------------
21
- // Helpers
22
- // ---------------------------------------------------------------------------
23
-
24
- /**
25
- * Build the format options for output, reading global --json / --quiet flags.
26
- * Uses `optsWithGlobals()` to include options inherited from the top-level program.
27
- */
28
- function getFormatOptions(cmd: Command) {
29
- const opts = cmd.optsWithGlobals();
30
- return { json: Boolean(opts.json), quiet: Boolean(opts.quiet) };
31
- }
32
-
33
24
  // ---------------------------------------------------------------------------
34
25
  // Command registration
35
26
  // ---------------------------------------------------------------------------
@@ -233,7 +224,7 @@ EXAMPLES:
233
224
  failed++;
234
225
  } else {
235
226
  formatSuccess(
236
- `Worker "${name}" running on http://localhost:${result.data.port}`,
227
+ `Worker "${name}" running on http://localhost:${result.value.port}`,
237
228
  fmt
238
229
  );
239
230
  started++;
@@ -339,7 +330,7 @@ EXAMPLES:
339
330
  }
340
331
 
341
332
  formatSuccess(
342
- `Worker "${name}" running on http://localhost:${result.data.port}`,
333
+ `Worker "${name}" running on http://localhost:${result.value.port}`,
343
334
  fmt
344
335
  );
345
336
  } catch (err) {
@@ -55,8 +55,8 @@ function captureStdout(): { output: () => string; restore: () => void } {
55
55
  // ---------------------------------------------------------------------------
56
56
 
57
57
  /** Successful wrangler result with data. */
58
- function okResult<T>(data: T): WranglerResult<T> {
59
- return { ok: true, data } as WranglerResult<T>;
58
+ function okResult<T>(value: T): WranglerResult<T> {
59
+ return { ok: true, value } as WranglerResult<T>;
60
60
  }
61
61
 
62
62
  /** Failed wrangler result with error message. */