@jango-blockchained/hoox-cli 0.5.1 → 0.5.6
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/bin/hoox.js +3 -4
- package/dist/index.js +32545 -0
- package/package.json +10 -15
- package/src/commands/check/check-command.test.ts +0 -476
- package/src/commands/check/check-command.ts +0 -1144
- package/src/commands/check/index.ts +0 -10
- package/src/commands/check/prerequisites-command.test.ts +0 -19
- package/src/commands/check/prerequisites-command.ts +0 -96
- package/src/commands/check/types.ts +0 -103
- package/src/commands/clone/clone-command.test.ts +0 -442
- package/src/commands/clone/clone-command.ts +0 -431
- package/src/commands/clone/index.ts +0 -1
- package/src/commands/config/config-command.test.ts +0 -583
- package/src/commands/config/config-command.ts +0 -897
- package/src/commands/config/env-command.test.ts +0 -43
- package/src/commands/config/env-command.ts +0 -295
- package/src/commands/config/index.ts +0 -3
- package/src/commands/config/kv-command.test.ts +0 -14
- package/src/commands/config/kv-command.ts +0 -290
- package/src/commands/dashboard/dashboard-command.test.ts +0 -47
- package/src/commands/dashboard/dashboard-command.ts +0 -128
- package/src/commands/dashboard/index.ts +0 -1
- package/src/commands/db/db-command.test.ts +0 -21
- package/src/commands/db/db-command.ts +0 -312
- package/src/commands/db/index.ts +0 -1
- package/src/commands/deploy/deploy-command.test.ts +0 -304
- package/src/commands/deploy/deploy-command.ts +0 -1135
- package/src/commands/deploy/index.ts +0 -2
- package/src/commands/deploy/telegram-service.ts +0 -76
- package/src/commands/deploy/types.ts +0 -34
- package/src/commands/dev/dev-command.test.ts +0 -383
- package/src/commands/dev/dev-command.ts +0 -398
- package/src/commands/dev/index.ts +0 -1
- package/src/commands/infra/index.ts +0 -5
- package/src/commands/infra/infra-command.test.ts +0 -719
- package/src/commands/infra/infra-command.ts +0 -974
- package/src/commands/infra/types.ts +0 -23
- package/src/commands/init/__tests__/cli-provisioner.test.ts +0 -17
- package/src/commands/init/cli-provisioner.ts +0 -71
- package/src/commands/init/index.ts +0 -1
- package/src/commands/init/init-command.test.ts +0 -876
- package/src/commands/init/init-command.ts +0 -593
- package/src/commands/init/types.ts +0 -23
- package/src/commands/logs/index.ts +0 -1
- package/src/commands/logs/logs-command.test.ts +0 -294
- package/src/commands/logs/logs-command.ts +0 -452
- package/src/commands/monitor/index.ts +0 -2
- package/src/commands/monitor/monitor-command.test.ts +0 -263
- package/src/commands/monitor/monitor-command.ts +0 -269
- package/src/commands/monitor/monitor-service.ts +0 -65
- package/src/commands/monitor/types.ts +0 -13
- package/src/commands/repair/index.ts +0 -2
- package/src/commands/repair/repair-command.test.ts +0 -224
- package/src/commands/repair/repair-command.ts +0 -220
- package/src/commands/repair/repair-service.ts +0 -138
- package/src/commands/repair/types.ts +0 -13
- package/src/commands/test/index.ts +0 -2
- package/src/commands/test/test-command.test.ts +0 -319
- package/src/commands/test/test-command.ts +0 -406
- package/src/commands/tui/index.ts +0 -1
- package/src/commands/tui/tui-command.ts +0 -87
- package/src/commands/update/index.ts +0 -1
- package/src/commands/update/update-command.ts +0 -168
- package/src/commands/waf/index.ts +0 -2
- package/src/commands/waf/types.ts +0 -48
- package/src/commands/waf/waf-command.test.ts +0 -506
- package/src/commands/waf/waf-command.ts +0 -548
- package/src/index.ts +0 -269
- package/src/services/cloudflare/cloudflare-service.test.ts +0 -658
- package/src/services/cloudflare/cloudflare-service.ts +0 -433
- package/src/services/cloudflare/index.ts +0 -2
- package/src/services/cloudflare/types.ts +0 -30
- package/src/services/config/config-service.test.ts +0 -395
- package/src/services/config/config-service.ts +0 -207
- package/src/services/config/index.ts +0 -9
- package/src/services/config/types.ts +0 -66
- package/src/services/db/db-service.test.ts +0 -51
- package/src/services/db/db-service.ts +0 -131
- package/src/services/db/index.ts +0 -1
- package/src/services/docker/docker-service.ts +0 -155
- package/src/services/docker/index.ts +0 -1
- package/src/services/env/env-service.test.ts +0 -236
- package/src/services/env/env-service.ts +0 -386
- package/src/services/env/index.ts +0 -1
- package/src/services/kv/index.ts +0 -1
- package/src/services/kv/kv-sync-service.test.ts +0 -38
- package/src/services/kv/kv-sync-service.ts +0 -234
- package/src/services/prerequisites/index.ts +0 -1
- package/src/services/prerequisites/prerequisites-service.test.ts +0 -89
- package/src/services/prerequisites/prerequisites-service.ts +0 -377
- package/src/services/prerequisites/types.ts +0 -48
- package/src/services/secrets/index.ts +0 -11
- package/src/services/secrets/secrets-service.test.ts +0 -495
- package/src/services/secrets/secrets-service.ts +0 -287
- package/src/services/secrets/types.ts +0 -50
- package/src/services/update/index.ts +0 -2
- package/src/services/update/update-service.test.ts +0 -76
- package/src/services/update/update-service.ts +0 -193
- package/src/ui/banner.ts +0 -141
- package/src/ui/index.ts +0 -8
- package/src/ui/menu.ts +0 -490
- package/src/utils/errors.test.ts +0 -69
- package/src/utils/errors.ts +0 -23
- package/src/utils/formatters.test.ts +0 -180
- package/src/utils/formatters.ts +0 -269
- package/src/utils/theme.ts +0 -94
package/src/ui/banner.ts
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hoox ASCII banner — variants for the interactive TUI.
|
|
3
|
-
* Each variant provides a different visual style while maintaining
|
|
4
|
-
* consistent branding and theme coloring.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { theme } from "../utils/theme.js";
|
|
8
|
-
|
|
9
|
-
const TAGLINE = "Cloudflare Workers Platform";
|
|
10
|
-
const VERSION = "0.3.0";
|
|
11
|
-
|
|
12
|
-
// ── Shared constants ──────────────────────────────────────────────
|
|
13
|
-
|
|
14
|
-
/** Disclaimer line rendered below the banner and in the footer. */
|
|
15
|
-
export const DISCLAIMER =
|
|
16
|
-
"DISCLAIMER: Trading cryptocurrencies involves substantial risk of loss. Use at your own risk.";
|
|
17
|
-
|
|
18
|
-
// ── Variant 0 — Default (legacy) ──────────────────────────────────
|
|
19
|
-
|
|
20
|
-
const LEGACY_LINES = [
|
|
21
|
-
"██╗ ██╗ ██████╗ ██████╗ ██╗ ██╗",
|
|
22
|
-
"██║ ██║██╔═══██╗██╔═══██╗╚██╗██╔╝",
|
|
23
|
-
"███████║██║ ██║██║ ██║ ╚███╔╝ ",
|
|
24
|
-
"██╔══██║██║ ██║██║ ██║ ██╔██╗ ",
|
|
25
|
-
"██║ ██║╚██████╔╝╚██████╔╝██╔╝ ██╗",
|
|
26
|
-
"╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝",
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
function renderLegacy(): string {
|
|
30
|
-
const bw = 52;
|
|
31
|
-
const line = ` ${theme.dim("─").repeat(bw - 2)}`;
|
|
32
|
-
const top = ` ${theme.dim("┌")}${line.slice(2)}${theme.dim("┐")}`;
|
|
33
|
-
const bottom = ` ${theme.dim("└")}${line.slice(2)}${theme.dim("┘")}`;
|
|
34
|
-
const ascii = LEGACY_LINES.map((l) => ` ${theme.heading(l)}`);
|
|
35
|
-
const gap = Math.floor((bw - TAGLINE.length - VERSION.length - 2) / 2);
|
|
36
|
-
const tag = ` ${" ".repeat(gap)}${theme.dim(TAGLINE)} ${theme.dim(`v${VERSION}`)}`;
|
|
37
|
-
return [top, ...ascii, line, tag, bottom].join("\n");
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// ── Variant 1 — "Horizon" (architectural) ─────────────────────────
|
|
41
|
-
// Uses double-line frame characters and a more open, architectural feel.
|
|
42
|
-
// The "HOOX" wordmark is built from simple geometric blocks.
|
|
43
|
-
|
|
44
|
-
const HORIZON_LINES = [
|
|
45
|
-
"╔═══╗ ╔═══╗ ╔═══╗ ╔═══╗",
|
|
46
|
-
"║ ║ ║ ║ ║ ║ ║ ║ ║ ║",
|
|
47
|
-
"║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║",
|
|
48
|
-
"║ ╚═╝ ║ ╚═╝ ║ ║ ║ ║ ╚═╝",
|
|
49
|
-
"║ ║ ║ ╚═╝ ║ ",
|
|
50
|
-
"╚═════╝ ╚═════╝ ╚═══╝ ╚═════╝",
|
|
51
|
-
];
|
|
52
|
-
|
|
53
|
-
export function renderBannerHorizon(): string {
|
|
54
|
-
const bw = 56;
|
|
55
|
-
const inner = theme.dim("─").repeat(bw - 2);
|
|
56
|
-
const top = ` ${theme.dim("╭")}${inner}${theme.dim("╮")}`;
|
|
57
|
-
const bottom = ` ${theme.dim("╰")}${inner}${theme.dim("╯")}`;
|
|
58
|
-
const ascii = HORIZON_LINES.map((l) => ` ${theme.accent(l)}`);
|
|
59
|
-
const gap = Math.floor((bw - TAGLINE.length - VERSION.length - 4) / 2);
|
|
60
|
-
const tag = ` ${" ".repeat(gap)}${theme.dim(TAGLINE)} ${theme.dim(`v${VERSION}`)}`;
|
|
61
|
-
return [top, ...ascii, theme.dim("─").repeat(bw), tag, bottom].join("\n");
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// ── Variant 2 — "Signal" (data / waveform) ────────────────────────
|
|
65
|
-
// Evokes trading signals and monitoring. Uses a smaller wordmark
|
|
66
|
-
// with a dynamic waveform motif beneath it.
|
|
67
|
-
|
|
68
|
-
const SIGNAL_LINES = [
|
|
69
|
-
" _ _ _ _ ",
|
|
70
|
-
" | | | | ___ __| | | | ",
|
|
71
|
-
" | |_| |/ _ \\ / _` | | | ",
|
|
72
|
-
" | _ | (_) | (_| | | | ",
|
|
73
|
-
" |_| |_|\\___/ \\__,_| |_| ",
|
|
74
|
-
];
|
|
75
|
-
|
|
76
|
-
export function renderBannerSignal(): string {
|
|
77
|
-
const bw = 54;
|
|
78
|
-
const line = theme.dim("─").repeat(bw);
|
|
79
|
-
const top = ` ${theme.dim("┌")}${line.slice(2)}${theme.dim("┐")}`;
|
|
80
|
-
const bottom = ` ${theme.dim("└")}${line.slice(2)}${theme.dim("┘")}`;
|
|
81
|
-
|
|
82
|
-
const wordmark = SIGNAL_LINES.map((l) => {
|
|
83
|
-
// Colour the letters H O O X, dim the rest
|
|
84
|
-
return ` ${theme.heading(l.slice(0, 26))}${theme.dim(l.slice(26))}`;
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
// Waveform line — sine-wave art
|
|
88
|
-
const wave = ` ${theme.accent("~~")}${theme.dim("~")}${theme.accent("_")}${theme.dim(".")}${theme.accent("/\\")}${theme.dim("~")}${theme.accent("\\/")}${theme.dim("..")}${theme.accent("/~~\\")}${theme.dim("~")} ${theme.dim(TAGLINE)} ${theme.dim(`v${VERSION}`)}`;
|
|
89
|
-
|
|
90
|
-
return [top, ...wordmark, line, wave, bottom].join("\n");
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// ── Variant 3 — "Minimal" (clean badge) ───────────────────────────
|
|
94
|
-
// No ASCII art — just the project name, version, and a clean
|
|
95
|
-
// double-rule header. Professional and understated.
|
|
96
|
-
|
|
97
|
-
export function renderBannerMinimal(): string {
|
|
98
|
-
const bw = 50;
|
|
99
|
-
const rule = theme.dim("━").repeat(bw);
|
|
100
|
-
const inner = theme.dim("─").repeat(bw);
|
|
101
|
-
|
|
102
|
-
const leftPad = Math.floor((bw - TAGLINE.length - VERSION.length - 8) / 2);
|
|
103
|
-
const titleLine =
|
|
104
|
-
" ".repeat(leftPad) +
|
|
105
|
-
theme.heading("H O O X") +
|
|
106
|
-
" " +
|
|
107
|
-
theme.dim(TAGLINE) +
|
|
108
|
-
" " +
|
|
109
|
-
theme.dim(`v${VERSION}`);
|
|
110
|
-
|
|
111
|
-
return [
|
|
112
|
-
` ${rule}`,
|
|
113
|
-
` ${theme.dim("│")}${" ".repeat(bw - 2)}${theme.dim("│")}`,
|
|
114
|
-
`${theme.dim("│")}${titleLine}${" ".repeat(Math.max(0, bw - titleLine.length - 2))}${theme.dim("│")}`,
|
|
115
|
-
` ${theme.dim("│")}${" ".repeat(bw - 2)}${theme.dim("│")}`,
|
|
116
|
-
` ${rule}`,
|
|
117
|
-
].join("\n");
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// ── Exports ───────────────────────────────────────────────────────
|
|
121
|
-
|
|
122
|
-
export const BANNER_VARIANTS = {
|
|
123
|
-
legacy: renderLegacy,
|
|
124
|
-
horizon: renderBannerHorizon,
|
|
125
|
-
signal: renderBannerSignal,
|
|
126
|
-
minimal: renderBannerMinimal,
|
|
127
|
-
} as const;
|
|
128
|
-
|
|
129
|
-
export type BannerVariant = keyof typeof BANNER_VARIANTS;
|
|
130
|
-
|
|
131
|
-
/** Default banner — the legacy ASCII block style. */
|
|
132
|
-
export function renderBanner(variant?: BannerVariant): string {
|
|
133
|
-
return variant ? BANNER_VARIANTS[variant]() : renderLegacy();
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Render a compact one-line banner for inline display.
|
|
138
|
-
*/
|
|
139
|
-
export function renderCompactBanner(): string {
|
|
140
|
-
return `${theme.heading("Hoox CLI")} ${theme.dim(`${TAGLINE} ${VERSION}`)}`;
|
|
141
|
-
}
|
package/src/ui/index.ts
DELETED
package/src/ui/menu.ts
DELETED
|
@@ -1,490 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Interactive TUI loop for the Hoox CLI.
|
|
3
|
-
*
|
|
4
|
-
* When `hoox` is called with no arguments, this module takes over:
|
|
5
|
-
* shows the ASCII banner, an interactive clack menu, dispatches to
|
|
6
|
-
* the appropriate Commander action, and returns to the menu.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { Command } from "commander";
|
|
10
|
-
import {
|
|
11
|
-
intro,
|
|
12
|
-
outro,
|
|
13
|
-
select,
|
|
14
|
-
text,
|
|
15
|
-
confirm,
|
|
16
|
-
log,
|
|
17
|
-
isCancel,
|
|
18
|
-
cancel,
|
|
19
|
-
} from "@clack/prompts";
|
|
20
|
-
import { renderBanner, renderCompactBanner, DISCLAIMER } from "./banner.js";
|
|
21
|
-
import { CLIError } from "../utils/errors.js";
|
|
22
|
-
import { theme } from "../utils/theme.js";
|
|
23
|
-
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
// Main entry
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Run the interactive TUI loop.
|
|
30
|
-
* - Shows the banner once
|
|
31
|
-
* - Shows a category-selection menu
|
|
32
|
-
* - Each category leads to sub-actions
|
|
33
|
-
* - After each action, returns to the menu
|
|
34
|
-
* - Exit or Ctrl+C to quit
|
|
35
|
-
*/
|
|
36
|
-
export async function runInteractiveTUI(program: Command): Promise<void> {
|
|
37
|
-
// Print banner directly, then start clack prompt session
|
|
38
|
-
process.stdout.write(renderBanner() + "\n");
|
|
39
|
-
intro("hoox");
|
|
40
|
-
|
|
41
|
-
while (true) {
|
|
42
|
-
const category = await showMainMenu();
|
|
43
|
-
if (category === "__exit") break;
|
|
44
|
-
|
|
45
|
-
const result = await handleCategory(category, program);
|
|
46
|
-
if (result === "exit") break;
|
|
47
|
-
// "back" or "continue" → loop back to main menu
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Legal footer
|
|
51
|
-
process.stdout.write(
|
|
52
|
-
theme.dim("─".repeat(50)) + "\n" + theme.muted(DISCLAIMER) + "\n"
|
|
53
|
-
);
|
|
54
|
-
outro("See you later!");
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// ---------------------------------------------------------------------------
|
|
58
|
-
// Main menu
|
|
59
|
-
// ---------------------------------------------------------------------------
|
|
60
|
-
|
|
61
|
-
const MAIN_CATEGORIES = [
|
|
62
|
-
{
|
|
63
|
-
value: "init",
|
|
64
|
-
label: "⚡ Setup Wizard",
|
|
65
|
-
hint: "bootstrap or reconfigure project",
|
|
66
|
-
},
|
|
67
|
-
{ value: "deploy", label: "Deploy", hint: "workers, dashboard" },
|
|
68
|
-
{
|
|
69
|
-
value: "manage",
|
|
70
|
-
label: "Manage",
|
|
71
|
-
hint: "infrastructure, config, secrets",
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
value: "monitor",
|
|
75
|
-
label: "Monitor",
|
|
76
|
-
hint: "diagnostics, health, logs, tests",
|
|
77
|
-
},
|
|
78
|
-
{ value: "tools", label: "Tools", hint: "WAF, clone worker, dashboard UI" },
|
|
79
|
-
{ value: "develop", label: "Develop", hint: "dev server, start project" },
|
|
80
|
-
{ value: "__exit", label: "Exit" },
|
|
81
|
-
] as const;
|
|
82
|
-
|
|
83
|
-
async function showMainMenu(): Promise<string> {
|
|
84
|
-
const choice = await select({
|
|
85
|
-
message: "What would you like to do?",
|
|
86
|
-
options: [...MAIN_CATEGORIES],
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
if (isCancel(choice)) {
|
|
90
|
-
cancel("Operation cancelled.");
|
|
91
|
-
return "__exit";
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return choice;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// ---------------------------------------------------------------------------
|
|
98
|
-
// Category handlers
|
|
99
|
-
// ---------------------------------------------------------------------------
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Handle a selected category. Returns "back", "exit", or "continue".
|
|
103
|
-
*/
|
|
104
|
-
async function handleCategory(
|
|
105
|
-
category: string,
|
|
106
|
-
program: Command
|
|
107
|
-
): Promise<"back" | "exit" | "continue"> {
|
|
108
|
-
switch (category) {
|
|
109
|
-
case "init":
|
|
110
|
-
await runCommand(program, "init");
|
|
111
|
-
return "continue";
|
|
112
|
-
case "deploy":
|
|
113
|
-
return showDeployMenu(program);
|
|
114
|
-
case "develop":
|
|
115
|
-
return showDevelopMenu(program);
|
|
116
|
-
case "manage":
|
|
117
|
-
return showManageMenu(program);
|
|
118
|
-
case "monitor":
|
|
119
|
-
return showMonitorMenu(program);
|
|
120
|
-
case "tools":
|
|
121
|
-
return showToolsMenu(program);
|
|
122
|
-
default:
|
|
123
|
-
return "back";
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// ---------------------------------------------------------------------------
|
|
128
|
-
// Deploy sub-menu
|
|
129
|
-
// ---------------------------------------------------------------------------
|
|
130
|
-
|
|
131
|
-
async function showDeployMenu(
|
|
132
|
-
program: Command
|
|
133
|
-
): Promise<"back" | "exit" | "continue"> {
|
|
134
|
-
while (true) {
|
|
135
|
-
const choice = await select({
|
|
136
|
-
message: "Deploy",
|
|
137
|
-
options: [
|
|
138
|
-
{ value: "all", label: "All workers + dashboard", hint: "recommended" },
|
|
139
|
-
{ value: "workers", label: "Workers only" },
|
|
140
|
-
{ value: "single", label: "Single worker..." },
|
|
141
|
-
{ value: "dashboard", label: "Dashboard only" },
|
|
142
|
-
{ value: "__back", label: "◀ Back to main menu" },
|
|
143
|
-
],
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
if (isCancel(choice)) return "back";
|
|
147
|
-
if (choice === "__back") return "continue";
|
|
148
|
-
|
|
149
|
-
if (choice === "single") {
|
|
150
|
-
const name = await text({
|
|
151
|
-
message: "Which worker?",
|
|
152
|
-
placeholder: "e.g. hoox, trade-worker, d1-worker",
|
|
153
|
-
validate: (v) =>
|
|
154
|
-
v === undefined || v.length === 0
|
|
155
|
-
? "Worker name is required"
|
|
156
|
-
: undefined,
|
|
157
|
-
});
|
|
158
|
-
if (isCancel(name)) continue;
|
|
159
|
-
if (!name) continue;
|
|
160
|
-
await runCommand(program, `deploy worker ${name}`);
|
|
161
|
-
} else {
|
|
162
|
-
const cmd = choice === "all" ? "deploy all" : `deploy ${choice}`;
|
|
163
|
-
await runCommand(program, cmd);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// ---------------------------------------------------------------------------
|
|
169
|
-
// Develop sub-menu
|
|
170
|
-
// ---------------------------------------------------------------------------
|
|
171
|
-
|
|
172
|
-
async function showDevelopMenu(
|
|
173
|
-
program: Command
|
|
174
|
-
): Promise<"back" | "exit" | "continue"> {
|
|
175
|
-
while (true) {
|
|
176
|
-
const choice = await select({
|
|
177
|
-
message: "Develop",
|
|
178
|
-
options: [
|
|
179
|
-
{
|
|
180
|
-
value: "dev start",
|
|
181
|
-
label: "Start dev server",
|
|
182
|
-
hint: "runs all workers locally",
|
|
183
|
-
},
|
|
184
|
-
{ value: "__back", label: "◀ Back to main menu" },
|
|
185
|
-
],
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
if (isCancel(choice)) return "back";
|
|
189
|
-
if (choice === "__back") return "continue";
|
|
190
|
-
|
|
191
|
-
await runCommand(program, choice);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// ---------------------------------------------------------------------------
|
|
196
|
-
// Manage sub-menu
|
|
197
|
-
// ---------------------------------------------------------------------------
|
|
198
|
-
|
|
199
|
-
async function showManageMenu(
|
|
200
|
-
program: Command
|
|
201
|
-
): Promise<"back" | "exit" | "continue"> {
|
|
202
|
-
while (true) {
|
|
203
|
-
const choice = await select({
|
|
204
|
-
message: "Manage",
|
|
205
|
-
options: [
|
|
206
|
-
{ value: "infra", label: "Infrastructure", hint: "D1, R2, KV, Queues" },
|
|
207
|
-
{
|
|
208
|
-
value: "config",
|
|
209
|
-
label: "Configuration",
|
|
210
|
-
hint: "show / set wrangler.jsonc",
|
|
211
|
-
},
|
|
212
|
-
{ value: "secrets", label: "Secrets", hint: "list, set, sync" },
|
|
213
|
-
{ value: "keys", label: "Auth keys", hint: "generate, list" },
|
|
214
|
-
{ value: "waf", label: "WAF rules" },
|
|
215
|
-
{ value: "__back", label: "◀ Back to main menu" },
|
|
216
|
-
],
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
if (isCancel(choice)) return "back";
|
|
220
|
-
if (choice === "__back") return "continue";
|
|
221
|
-
|
|
222
|
-
switch (choice) {
|
|
223
|
-
case "infra":
|
|
224
|
-
await runCommand(program, "infra");
|
|
225
|
-
break;
|
|
226
|
-
case "config":
|
|
227
|
-
await showConfigSubMenu(program);
|
|
228
|
-
break;
|
|
229
|
-
case "secrets":
|
|
230
|
-
await showSecretsSubMenu(program);
|
|
231
|
-
break;
|
|
232
|
-
case "keys":
|
|
233
|
-
await showKeysSubMenu(program);
|
|
234
|
-
break;
|
|
235
|
-
case "waf":
|
|
236
|
-
await runCommand(program, "waf");
|
|
237
|
-
break;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// ---------------------------------------------------------------------------
|
|
243
|
-
// Config sub-sub-menu
|
|
244
|
-
// ---------------------------------------------------------------------------
|
|
245
|
-
|
|
246
|
-
async function showConfigSubMenu(program: Command): Promise<void> {
|
|
247
|
-
const choice = await select({
|
|
248
|
-
message: "Configuration",
|
|
249
|
-
options: [
|
|
250
|
-
{ value: "show", label: "Show config", hint: "display wrangler.jsonc" },
|
|
251
|
-
{ value: "set", label: "Set a value..." },
|
|
252
|
-
{ value: "__back", label: "◀ Back to Manage" },
|
|
253
|
-
],
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
if (isCancel(choice) || choice === "__back") return;
|
|
257
|
-
|
|
258
|
-
if (choice === "set") {
|
|
259
|
-
const key = await text({
|
|
260
|
-
message: "Config key (dot-separated path)",
|
|
261
|
-
placeholder: "e.g. global.cloudflare_account_id",
|
|
262
|
-
validate: (v) =>
|
|
263
|
-
v === undefined || v.length === 0 ? "Key is required" : undefined,
|
|
264
|
-
});
|
|
265
|
-
if (isCancel(key) || !key) return;
|
|
266
|
-
|
|
267
|
-
const value = await text({
|
|
268
|
-
message: `Value for "${key}"`,
|
|
269
|
-
placeholder: "enter value",
|
|
270
|
-
});
|
|
271
|
-
if (isCancel(value) || value === undefined) return;
|
|
272
|
-
|
|
273
|
-
await runCommand(program, `config set ${key} ${value ?? ""}`);
|
|
274
|
-
} else {
|
|
275
|
-
await runCommand(program, "config show");
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// ---------------------------------------------------------------------------
|
|
280
|
-
// Secrets sub-sub-menu
|
|
281
|
-
// ---------------------------------------------------------------------------
|
|
282
|
-
|
|
283
|
-
async function showSecretsSubMenu(program: Command): Promise<void> {
|
|
284
|
-
const choice = await select({
|
|
285
|
-
message: "Secrets",
|
|
286
|
-
options: [
|
|
287
|
-
{ value: "list", label: "List secrets" },
|
|
288
|
-
{ value: "set", label: "Set a secret..." },
|
|
289
|
-
{ value: "sync", label: "Sync to Cloudflare" },
|
|
290
|
-
{ value: "__back", label: "◀ Back to Manage" },
|
|
291
|
-
],
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
if (isCancel(choice) || choice === "__back") return;
|
|
295
|
-
|
|
296
|
-
switch (choice) {
|
|
297
|
-
case "list":
|
|
298
|
-
await runCommand(program, "config secrets list");
|
|
299
|
-
break;
|
|
300
|
-
case "set": {
|
|
301
|
-
const worker = await text({
|
|
302
|
-
message: "Worker name",
|
|
303
|
-
placeholder: "e.g. hoox, trade-worker",
|
|
304
|
-
validate: (v) =>
|
|
305
|
-
v === undefined || v.length === 0
|
|
306
|
-
? "Worker name is required"
|
|
307
|
-
: undefined,
|
|
308
|
-
});
|
|
309
|
-
if (isCancel(worker) || !worker) return;
|
|
310
|
-
|
|
311
|
-
const secretName = await text({
|
|
312
|
-
message: "Secret name",
|
|
313
|
-
placeholder: "e.g. API_KEY, DATABASE_URL",
|
|
314
|
-
validate: (v) =>
|
|
315
|
-
v === undefined || v.length === 0
|
|
316
|
-
? "Secret name is required"
|
|
317
|
-
: undefined,
|
|
318
|
-
});
|
|
319
|
-
if (isCancel(secretName) || !secretName) return;
|
|
320
|
-
|
|
321
|
-
await runCommand(program, `config secrets set ${worker} ${secretName}`);
|
|
322
|
-
break;
|
|
323
|
-
}
|
|
324
|
-
case "sync":
|
|
325
|
-
await runCommand(program, "config secrets sync");
|
|
326
|
-
break;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// ---------------------------------------------------------------------------
|
|
331
|
-
// Keys sub-sub-menu
|
|
332
|
-
// ---------------------------------------------------------------------------
|
|
333
|
-
|
|
334
|
-
async function showKeysSubMenu(program: Command): Promise<void> {
|
|
335
|
-
const choice = await select({
|
|
336
|
-
message: "Auth Keys",
|
|
337
|
-
options: [
|
|
338
|
-
{ value: "generate", label: "Generate new keys" },
|
|
339
|
-
{ value: "list", label: "List existing keys" },
|
|
340
|
-
{ value: "__back", label: "◀ Back to Manage" },
|
|
341
|
-
],
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
if (isCancel(choice) || choice === "__back") return;
|
|
345
|
-
|
|
346
|
-
if (choice === "generate") {
|
|
347
|
-
const proceed = await confirm({
|
|
348
|
-
message:
|
|
349
|
-
"Generate new internal auth keys? This will create .keys/*.env files.",
|
|
350
|
-
initialValue: false,
|
|
351
|
-
});
|
|
352
|
-
if (isCancel(proceed) || !proceed) return;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
await runCommand(program, `config keys ${choice}`);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// ---------------------------------------------------------------------------
|
|
359
|
-
// Monitor sub-menu
|
|
360
|
-
// ---------------------------------------------------------------------------
|
|
361
|
-
|
|
362
|
-
async function showMonitorMenu(
|
|
363
|
-
program: Command
|
|
364
|
-
): Promise<"back" | "exit" | "continue"> {
|
|
365
|
-
while (true) {
|
|
366
|
-
const choice = await select({
|
|
367
|
-
message: "Monitor",
|
|
368
|
-
options: [
|
|
369
|
-
{
|
|
370
|
-
value: "check setup",
|
|
371
|
-
label: "Run diagnostics",
|
|
372
|
-
hint: "full system validation",
|
|
373
|
-
},
|
|
374
|
-
{
|
|
375
|
-
value: "check health",
|
|
376
|
-
label: "Health check",
|
|
377
|
-
hint: "worker connectivity",
|
|
378
|
-
},
|
|
379
|
-
{ value: "check fix", label: "Auto-repair", hint: "fix common issues" },
|
|
380
|
-
{ value: "__logs", label: "View logs", hint: "real-time log tailing" },
|
|
381
|
-
{ value: "test", label: "Run tests" },
|
|
382
|
-
{ value: "__back", label: "◀ Back to main menu" },
|
|
383
|
-
],
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
if (isCancel(choice)) return "back";
|
|
387
|
-
if (choice === "__back") return "continue";
|
|
388
|
-
|
|
389
|
-
if (choice === "__logs") {
|
|
390
|
-
const target = await select({
|
|
391
|
-
message: "Tail logs for",
|
|
392
|
-
options: [
|
|
393
|
-
{ value: "all", label: "All workers" },
|
|
394
|
-
{ value: "single", label: "A specific worker..." },
|
|
395
|
-
{ value: "__back", label: "◀ Back" },
|
|
396
|
-
],
|
|
397
|
-
});
|
|
398
|
-
if (isCancel(target) || target === "__back") continue;
|
|
399
|
-
if (target === "single") {
|
|
400
|
-
const name = await text({
|
|
401
|
-
message: "Worker name",
|
|
402
|
-
placeholder: "e.g. hoox, trade-worker, d1-worker",
|
|
403
|
-
});
|
|
404
|
-
if (isCancel(name) || !name) continue;
|
|
405
|
-
await runCommand(program, `logs worker ${name}`);
|
|
406
|
-
} else {
|
|
407
|
-
await runCommand(program, "logs all");
|
|
408
|
-
}
|
|
409
|
-
continue;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
await runCommand(program, choice);
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// ---------------------------------------------------------------------------
|
|
417
|
-
// Tools sub-menu
|
|
418
|
-
// ---------------------------------------------------------------------------
|
|
419
|
-
|
|
420
|
-
async function showToolsMenu(
|
|
421
|
-
program: Command
|
|
422
|
-
): Promise<"back" | "exit" | "continue"> {
|
|
423
|
-
while (true) {
|
|
424
|
-
const choice = await select({
|
|
425
|
-
message: "Tools",
|
|
426
|
-
options: [
|
|
427
|
-
{
|
|
428
|
-
value: "clone",
|
|
429
|
-
label: "Clone worker",
|
|
430
|
-
hint: "scaffold from template",
|
|
431
|
-
},
|
|
432
|
-
{
|
|
433
|
-
value: "dashboard",
|
|
434
|
-
label: "Dashboard UI",
|
|
435
|
-
hint: "manage dashboard URLs",
|
|
436
|
-
},
|
|
437
|
-
{
|
|
438
|
-
value: "tui",
|
|
439
|
-
label: "Launch TUI Dashboard",
|
|
440
|
-
hint: "OpenTUI terminal operations center",
|
|
441
|
-
},
|
|
442
|
-
{ value: "__back", label: "◀ Back to main menu" },
|
|
443
|
-
],
|
|
444
|
-
});
|
|
445
|
-
|
|
446
|
-
if (isCancel(choice)) return "back";
|
|
447
|
-
if (choice === "__back") return "continue";
|
|
448
|
-
|
|
449
|
-
await runCommand(program, choice);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
// ---------------------------------------------------------------------------
|
|
454
|
-
// Command execution
|
|
455
|
-
// ---------------------------------------------------------------------------
|
|
456
|
-
|
|
457
|
-
/**
|
|
458
|
-
* Run a Commander command string inside the TUI loop.
|
|
459
|
-
*
|
|
460
|
-
* Delegates to `program.parseAsync()` with the given arguments.
|
|
461
|
-
* Wraps in try-catch so errors don't terminate the loop.
|
|
462
|
-
*/
|
|
463
|
-
async function runCommand(program: Command, commandStr: string): Promise<void> {
|
|
464
|
-
const args = commandStr.split(" ");
|
|
465
|
-
|
|
466
|
-
try {
|
|
467
|
-
// from: "user" means Commander expects raw user arguments only —
|
|
468
|
-
// NOT [node, script, ...args] format. So just pass the args directly.
|
|
469
|
-
await program.parseAsync(args, {
|
|
470
|
-
from: "user",
|
|
471
|
-
});
|
|
472
|
-
} catch (err) {
|
|
473
|
-
// Check if it's a Commander informational exit (help/version)
|
|
474
|
-
if (
|
|
475
|
-
err instanceof Error &&
|
|
476
|
-
"code" in err &&
|
|
477
|
-
(err as { code: string }).code === "commander.helpDisplayed"
|
|
478
|
-
) {
|
|
479
|
-
return;
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
const message =
|
|
483
|
-
err instanceof CLIError
|
|
484
|
-
? err.message
|
|
485
|
-
: err instanceof Error
|
|
486
|
-
? err.message
|
|
487
|
-
: String(err);
|
|
488
|
-
log.error(`Command failed: ${message}`);
|
|
489
|
-
}
|
|
490
|
-
}
|