@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.
- package/README.md +2 -0
- package/package.json +5 -3
- package/src/commands/check/check-command.test.ts +28 -20
- package/src/commands/check/prerequisites-command.ts +9 -5
- package/src/commands/clone/clone-command.ts +1 -10
- package/src/commands/config/config-command.ts +7 -11
- package/src/commands/config/env-command.ts +16 -35
- package/src/commands/config/kv-command.ts +33 -72
- package/src/commands/db/db-command.ts +23 -25
- package/src/commands/deploy/deploy-command.ts +133 -52
- package/src/commands/deploy/telegram-service.ts +21 -6
- package/src/commands/dev/dev-command.ts +7 -16
- package/src/commands/infra/infra-command.test.ts +2 -2
- package/src/commands/infra/infra-command.ts +45 -11
- package/src/commands/init/init-command.test.ts +2 -2
- package/src/commands/monitor/monitor-command.test.ts +44 -16
- package/src/commands/monitor/monitor-command.ts +45 -21
- package/src/commands/monitor/monitor-service.ts +19 -4
- package/src/commands/repair/repair-command.test.ts +35 -15
- package/src/commands/repair/repair-command.ts +39 -18
- package/src/commands/repair/repair-service.ts +48 -12
- package/src/commands/test/test-command.ts +4 -10
- package/src/commands/tui/index.ts +1 -0
- package/src/commands/tui/tui-command.ts +87 -0
- package/src/commands/update/index.ts +1 -0
- package/src/commands/update/update-command.ts +51 -0
- package/src/commands/waf/waf-command.test.ts +1 -1
- package/src/commands/waf/waf-command.ts +11 -11
- package/src/index.ts +52 -1
- package/src/services/cloudflare/cloudflare-service.test.ts +12 -8
- package/src/services/cloudflare/cloudflare-service.ts +8 -10
- package/src/services/cloudflare/types.ts +6 -5
- package/src/services/db/db-service.ts +8 -17
- package/src/services/env/env-service.test.ts +41 -15
- package/src/services/env/env-service.ts +276 -46
- package/src/services/kv/kv-sync-service.ts +117 -34
- package/src/services/prerequisites/prerequisites-service.ts +137 -29
- package/src/services/secrets/index.ts +0 -1
- package/src/services/secrets/secrets-service.test.ts +34 -25
- package/src/services/secrets/secrets-service.ts +10 -16
- package/src/services/secrets/types.ts +4 -11
- package/src/services/update/index.ts +2 -0
- package/src/services/update/update-service.test.ts +76 -0
- package/src/services/update/update-service.ts +193 -0
- package/src/ui/banner.ts +5 -5
- package/src/ui/menu.ts +6 -1
- package/src/utils/formatters.ts +21 -4
- 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(
|
|
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(
|
|
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(
|
|
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(
|
|
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.
|
|
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.
|
|
139
|
+
url: result.value.url,
|
|
147
140
|
success: true,
|
|
148
|
-
size: result.
|
|
149
|
-
startupTime: result.
|
|
150
|
-
versionId: result.
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
585
|
-
|
|
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)
|
|
606
|
+
if (result.description)
|
|
607
|
+
process.stdout.write(` ${theme.dim(result.description)}\n`);
|
|
590
608
|
} else {
|
|
591
|
-
formatError(
|
|
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(
|
|
637
|
+
let filePath = resolve(
|
|
638
|
+
process.cwd(),
|
|
639
|
+
"pages",
|
|
640
|
+
"dashboard",
|
|
641
|
+
"wrangler.jsonc"
|
|
642
|
+
);
|
|
614
643
|
if (!existsSync(filePath)) {
|
|
615
|
-
filePath = resolve(
|
|
644
|
+
filePath = resolve(
|
|
645
|
+
process.cwd(),
|
|
646
|
+
"workers",
|
|
647
|
+
"dashboard",
|
|
648
|
+
"wrangler.jsonc"
|
|
649
|
+
);
|
|
616
650
|
}
|
|
617
651
|
if (!existsSync(filePath)) {
|
|
618
|
-
formatError(
|
|
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(
|
|
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(
|
|
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(
|
|
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 } =
|
|
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(
|
|
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(
|
|
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(
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
const
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
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(
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
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 {
|
|
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 {
|
|
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(
|
|
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?: {
|
|
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 {
|
|
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 {
|
|
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.
|
|
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.
|
|
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>(
|
|
59
|
-
return { ok: true,
|
|
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. */
|