@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.
Files changed (106) hide show
  1. package/bin/hoox.js +3 -4
  2. package/dist/index.js +32545 -0
  3. package/package.json +10 -15
  4. package/src/commands/check/check-command.test.ts +0 -476
  5. package/src/commands/check/check-command.ts +0 -1144
  6. package/src/commands/check/index.ts +0 -10
  7. package/src/commands/check/prerequisites-command.test.ts +0 -19
  8. package/src/commands/check/prerequisites-command.ts +0 -96
  9. package/src/commands/check/types.ts +0 -103
  10. package/src/commands/clone/clone-command.test.ts +0 -442
  11. package/src/commands/clone/clone-command.ts +0 -431
  12. package/src/commands/clone/index.ts +0 -1
  13. package/src/commands/config/config-command.test.ts +0 -583
  14. package/src/commands/config/config-command.ts +0 -897
  15. package/src/commands/config/env-command.test.ts +0 -43
  16. package/src/commands/config/env-command.ts +0 -295
  17. package/src/commands/config/index.ts +0 -3
  18. package/src/commands/config/kv-command.test.ts +0 -14
  19. package/src/commands/config/kv-command.ts +0 -290
  20. package/src/commands/dashboard/dashboard-command.test.ts +0 -47
  21. package/src/commands/dashboard/dashboard-command.ts +0 -128
  22. package/src/commands/dashboard/index.ts +0 -1
  23. package/src/commands/db/db-command.test.ts +0 -21
  24. package/src/commands/db/db-command.ts +0 -312
  25. package/src/commands/db/index.ts +0 -1
  26. package/src/commands/deploy/deploy-command.test.ts +0 -304
  27. package/src/commands/deploy/deploy-command.ts +0 -1135
  28. package/src/commands/deploy/index.ts +0 -2
  29. package/src/commands/deploy/telegram-service.ts +0 -76
  30. package/src/commands/deploy/types.ts +0 -34
  31. package/src/commands/dev/dev-command.test.ts +0 -383
  32. package/src/commands/dev/dev-command.ts +0 -398
  33. package/src/commands/dev/index.ts +0 -1
  34. package/src/commands/infra/index.ts +0 -5
  35. package/src/commands/infra/infra-command.test.ts +0 -719
  36. package/src/commands/infra/infra-command.ts +0 -974
  37. package/src/commands/infra/types.ts +0 -23
  38. package/src/commands/init/__tests__/cli-provisioner.test.ts +0 -17
  39. package/src/commands/init/cli-provisioner.ts +0 -71
  40. package/src/commands/init/index.ts +0 -1
  41. package/src/commands/init/init-command.test.ts +0 -876
  42. package/src/commands/init/init-command.ts +0 -593
  43. package/src/commands/init/types.ts +0 -23
  44. package/src/commands/logs/index.ts +0 -1
  45. package/src/commands/logs/logs-command.test.ts +0 -294
  46. package/src/commands/logs/logs-command.ts +0 -452
  47. package/src/commands/monitor/index.ts +0 -2
  48. package/src/commands/monitor/monitor-command.test.ts +0 -263
  49. package/src/commands/monitor/monitor-command.ts +0 -269
  50. package/src/commands/monitor/monitor-service.ts +0 -65
  51. package/src/commands/monitor/types.ts +0 -13
  52. package/src/commands/repair/index.ts +0 -2
  53. package/src/commands/repair/repair-command.test.ts +0 -224
  54. package/src/commands/repair/repair-command.ts +0 -220
  55. package/src/commands/repair/repair-service.ts +0 -138
  56. package/src/commands/repair/types.ts +0 -13
  57. package/src/commands/test/index.ts +0 -2
  58. package/src/commands/test/test-command.test.ts +0 -319
  59. package/src/commands/test/test-command.ts +0 -406
  60. package/src/commands/tui/index.ts +0 -1
  61. package/src/commands/tui/tui-command.ts +0 -87
  62. package/src/commands/update/index.ts +0 -1
  63. package/src/commands/update/update-command.ts +0 -168
  64. package/src/commands/waf/index.ts +0 -2
  65. package/src/commands/waf/types.ts +0 -48
  66. package/src/commands/waf/waf-command.test.ts +0 -506
  67. package/src/commands/waf/waf-command.ts +0 -548
  68. package/src/index.ts +0 -269
  69. package/src/services/cloudflare/cloudflare-service.test.ts +0 -658
  70. package/src/services/cloudflare/cloudflare-service.ts +0 -433
  71. package/src/services/cloudflare/index.ts +0 -2
  72. package/src/services/cloudflare/types.ts +0 -30
  73. package/src/services/config/config-service.test.ts +0 -395
  74. package/src/services/config/config-service.ts +0 -207
  75. package/src/services/config/index.ts +0 -9
  76. package/src/services/config/types.ts +0 -66
  77. package/src/services/db/db-service.test.ts +0 -51
  78. package/src/services/db/db-service.ts +0 -131
  79. package/src/services/db/index.ts +0 -1
  80. package/src/services/docker/docker-service.ts +0 -155
  81. package/src/services/docker/index.ts +0 -1
  82. package/src/services/env/env-service.test.ts +0 -236
  83. package/src/services/env/env-service.ts +0 -386
  84. package/src/services/env/index.ts +0 -1
  85. package/src/services/kv/index.ts +0 -1
  86. package/src/services/kv/kv-sync-service.test.ts +0 -38
  87. package/src/services/kv/kv-sync-service.ts +0 -234
  88. package/src/services/prerequisites/index.ts +0 -1
  89. package/src/services/prerequisites/prerequisites-service.test.ts +0 -89
  90. package/src/services/prerequisites/prerequisites-service.ts +0 -377
  91. package/src/services/prerequisites/types.ts +0 -48
  92. package/src/services/secrets/index.ts +0 -11
  93. package/src/services/secrets/secrets-service.test.ts +0 -495
  94. package/src/services/secrets/secrets-service.ts +0 -287
  95. package/src/services/secrets/types.ts +0 -50
  96. package/src/services/update/index.ts +0 -2
  97. package/src/services/update/update-service.test.ts +0 -76
  98. package/src/services/update/update-service.ts +0 -193
  99. package/src/ui/banner.ts +0 -141
  100. package/src/ui/index.ts +0 -8
  101. package/src/ui/menu.ts +0 -490
  102. package/src/utils/errors.test.ts +0 -69
  103. package/src/utils/errors.ts +0 -23
  104. package/src/utils/formatters.test.ts +0 -180
  105. package/src/utils/formatters.ts +0 -269
  106. package/src/utils/theme.ts +0 -94
@@ -1,43 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
- import { EnvService } from "../../services/env/index.js";
3
-
4
- describe("env command", () => {
5
- describe("EnvService integration", () => {
6
- it("validate catches missing required vars", () => {
7
- const result = EnvService.validate({});
8
- expect(result.missing.length).toBeGreaterThan(0);
9
- expect(result.missing).toContain("CLOUDFLARE_API_TOKEN");
10
- });
11
-
12
- it("validate passes with all required vars", () => {
13
- const vars: Record<string, string> = {
14
- CLOUDFLARE_API_TOKEN: "cfut_xxx",
15
- CLOUDFLARE_ACCOUNT_ID: "abc123",
16
- SUBDOMAIN_PREFIX: "myapp",
17
- D1_INTERNAL_KEY: "d1-key",
18
- TRADE_INTERNAL_KEY: "trade-key",
19
- AGENT_INTERNAL_KEY: "agent-key",
20
- WEBHOOK_API_KEY_BINDING: "webhook-key",
21
- INTERNAL_KEY_BINDING: "inter-key",
22
- API_SERVICE_KEY: "api-key",
23
- DASHBOARD_USER: "admin",
24
- DASHBOARD_PASS: "pass123",
25
- SESSION_SECRET: "a".repeat(32),
26
- };
27
- const result = EnvService.validate(vars);
28
- expect(result.missing.length).toBe(0);
29
- });
30
-
31
- it("generateEnvLocal produces valid output", () => {
32
- const content = EnvService.generateEnvLocal();
33
- expect(content).toContain("CLOUDFLARE_API_TOKEN");
34
- expect(content).toContain("# NEVER commit this file");
35
- });
36
-
37
- it("show redacts secrets", () => {
38
- const output = EnvService.show({ CLOUDFLARE_API_TOKEN: "s3kr3t" });
39
- expect(output).toContain("********");
40
- expect(output).not.toContain("s3kr3t");
41
- });
42
- });
43
- });
@@ -1,295 +0,0 @@
1
- /**
2
- * `hoox config env` subcommand group — environment variable management.
3
- *
4
- * Subcommands:
5
- * init — Interactive wizard to generate .env.local and .dev.vars
6
- * show — Display current .env.local (secrets redacted)
7
- * validate — Check required vars are set
8
- * generate-dev-vars — Create/update per-worker .dev.vars from .env.local
9
- */
10
-
11
- import { Command } from "commander";
12
- import * as p from "@clack/prompts";
13
-
14
- import { EnvService } from "../../services/env/index.js";
15
- import { CLIError, ExitCode } from "../../utils/errors.js";
16
- import {
17
- formatSuccess,
18
- formatError,
19
- formatJson,
20
- } from "../../utils/formatters.js";
21
- import type { FormatOptions } from "../../utils/formatters.js";
22
-
23
- // ---------------------------------------------------------------------------
24
- // Helpers
25
- // ---------------------------------------------------------------------------
26
-
27
- /**
28
- * Get FormatOptions from the command opts using optsWithGlobals() so that
29
- * globally defined flags (--json, --quiet) are resolved correctly even when
30
- * called from deeply nested subcommands.
31
- */
32
- function formatOpts(cmd: Command): FormatOptions {
33
- const opts = cmd.optsWithGlobals<{ json?: boolean; quiet?: boolean }>();
34
- return { json: Boolean(opts.json), quiet: Boolean(opts.quiet) };
35
- }
36
-
37
- // ---------------------------------------------------------------------------
38
- // env init — interactive wizard
39
- // ---------------------------------------------------------------------------
40
-
41
- async function handleInit(opts: FormatOptions): Promise<void> {
42
- p.intro("Environment Setup");
43
-
44
- // Warn if .env.local already exists
45
- const existingEnv = Bun.file(".env.local");
46
- if (await existingEnv.exists()) {
47
- const overwrite = await p.confirm({
48
- message: ".env.local already exists. Overwrite?",
49
- initialValue: false,
50
- });
51
- if (p.isCancel(overwrite)) {
52
- p.cancel("Setup cancelled.");
53
- process.exit(0);
54
- }
55
- if (!overwrite) {
56
- p.outro("Setup cancelled. Existing .env.local preserved.");
57
- return;
58
- }
59
- }
60
-
61
- const collected: Record<string, string> = {};
62
- const sections = EnvService.getSections();
63
-
64
- for (const section of sections) {
65
- const sectionDefs = EnvService.getDefinitions().filter(
66
- (d) => d.section === section
67
- );
68
- if (sectionDefs.length === 0) continue;
69
-
70
- p.log.step(`Configuring ${section}...`);
71
-
72
- for (const def of sectionDefs) {
73
- let value: string | symbol;
74
- if (def.secret) {
75
- value = await p.password({
76
- message: `${def.name}:${def.hint ? ` (${def.hint})` : ""}`,
77
- validate: def.required
78
- ? (v: string | undefined) => (v ? undefined : "Required")
79
- : undefined,
80
- });
81
- } else {
82
- value = await p.text({
83
- message: `${def.name}:${def.hint ? ` (${def.hint})` : ""}`,
84
- defaultValue: def.default ?? "",
85
- validate: def.required
86
- ? (v: string | undefined) => (v ? undefined : "Required")
87
- : undefined,
88
- });
89
- }
90
- if (p.isCancel(value)) {
91
- p.cancel("Setup cancelled.");
92
- process.exit(0);
93
- }
94
- collected[def.name] = typeof value === "string" ? value : "";
95
- }
96
- }
97
-
98
- p.log.step("Writing .env.local...");
99
- const envContent = EnvService.generateEnvLocal(collected);
100
- await Bun.write(".env.local", envContent);
101
- if (!opts.quiet) formatSuccess(".env.local written", opts);
102
-
103
- p.log.step("Writing .dev.vars files...");
104
- const devVars = EnvService.getWorkerDevVars(collected);
105
- for (const [workerPath, vars] of Object.entries(devVars)) {
106
- const content = [
107
- `# .dev.vars - local secrets for ${workerPath}`,
108
- "# Generated by `hoox config env init`. NEVER commit this file.",
109
- "",
110
- ...Object.entries(vars).map(([k, v]) => `${k}=${v}`),
111
- "",
112
- ].join("\n");
113
- await Bun.write(`${workerPath}/.dev.vars`, content);
114
- if (!opts.quiet) formatSuccess(`Created ${workerPath}/.dev.vars`, opts);
115
- }
116
-
117
- p.outro("Environment setup complete!");
118
- }
119
-
120
- // ---------------------------------------------------------------------------
121
- // env show
122
- // ---------------------------------------------------------------------------
123
-
124
- async function handleShow(opts: FormatOptions): Promise<void> {
125
- const file = Bun.file(".env.local");
126
- if (!(await file.exists())) {
127
- throw new CLIError(
128
- ".env.local not found. Run `hoox config env init`.",
129
- ExitCode.INVALID_USAGE
130
- );
131
- }
132
-
133
- const vars = await EnvService.loadDotEnvAsync(".env.local");
134
-
135
- if (opts.json) {
136
- formatJson(vars, opts);
137
- } else {
138
- process.stdout.write(EnvService.show(vars));
139
- }
140
- }
141
-
142
- // ---------------------------------------------------------------------------
143
- // env validate
144
- // ---------------------------------------------------------------------------
145
-
146
- async function handleValidate(opts: FormatOptions): Promise<void> {
147
- let vars: Record<string, string> = {};
148
- const file = Bun.file(".env.local");
149
- if (await file.exists()) {
150
- vars = await EnvService.loadDotEnvAsync(".env.local");
151
- }
152
-
153
- const result = EnvService.validate(vars);
154
-
155
- if (opts.json) {
156
- formatJson(result, opts);
157
- } else if (!opts.quiet) {
158
- if (!(await file.exists())) {
159
- process.stdout.write(
160
- ".env.local not found. Run `hoox config env init` first.\n\n"
161
- );
162
- }
163
- if (result.missing.length > 0) {
164
- process.stdout.write("Missing required variables:\n");
165
- for (const v of result.missing) {
166
- process.stdout.write(` ${v}\n`);
167
- }
168
- }
169
- if (result.warnings.length > 0) {
170
- process.stdout.write("Warnings:\n");
171
- for (const w of result.warnings) {
172
- process.stdout.write(` ${w}\n`);
173
- }
174
- }
175
- if (result.missing.length === 0 && result.warnings.length === 0) {
176
- formatSuccess("All required environment variables are set", opts);
177
- }
178
- }
179
-
180
- if (result.missing.length > 0) {
181
- process.exitCode = ExitCode.ERROR;
182
- }
183
- }
184
-
185
- // ---------------------------------------------------------------------------
186
- // env generate-dev-vars
187
- // ---------------------------------------------------------------------------
188
-
189
- async function handleGenerateDevVars(opts: FormatOptions): Promise<void> {
190
- const file = Bun.file(".env.local");
191
- if (!(await file.exists())) {
192
- throw new CLIError(
193
- ".env.local not found. Run `hoox config env init`.",
194
- ExitCode.INVALID_USAGE
195
- );
196
- }
197
-
198
- const vars = await EnvService.loadDotEnvAsync(".env.local");
199
- const devVars = EnvService.getWorkerDevVars(vars);
200
-
201
- let count = 0;
202
- for (const [workerPath, workerVars] of Object.entries(devVars)) {
203
- const content = [
204
- `# .dev.vars - local secrets for ${workerPath}`,
205
- "# Generated by `hoox config env generate-dev-vars`. NEVER commit this file.",
206
- "",
207
- ...Object.entries(workerVars).map(([k, v]) => `${k}=${v}`),
208
- "",
209
- ].join("\n");
210
- await Bun.write(`${workerPath}/.dev.vars`, content);
211
- count++;
212
- }
213
-
214
- formatSuccess(`Generated .dev.vars for ${count} worker(s)`, opts);
215
- }
216
-
217
- // ---------------------------------------------------------------------------
218
- // Registration
219
- // ---------------------------------------------------------------------------
220
-
221
- /**
222
- * Register the `hoox config env` subcommand group.
223
- */
224
- export function registerEnvCommand(parentCmd: Command): void {
225
- const envCmd = parentCmd
226
- .command("env")
227
- .summary("Manage environment variables")
228
- .description(
229
- `Manage environment variables for the Hoox trading system.
230
-
231
- SUBCOMMANDS:
232
- init Interactive wizard to generate .env.local and .dev.vars
233
- show Display current .env.local (secrets redacted)
234
- validate Check required environment variables
235
- generate-dev-vars Create per-worker .dev.vars from .env.local
236
-
237
- EXAMPLES:
238
- hoox config env init
239
- hoox config env show
240
- hoox config env validate
241
- hoox config env generate-dev-vars`
242
- );
243
-
244
- envCmd
245
- .command("init")
246
- .description("Interactive wizard to generate .env.local and .dev.vars")
247
- .action(async (_, cmd: Command) => {
248
- const opts = formatOpts(cmd);
249
- try {
250
- await handleInit(opts);
251
- } catch (err) {
252
- formatError(err instanceof Error ? err : String(err), opts);
253
- process.exit(err instanceof CLIError ? err.code : ExitCode.ERROR);
254
- }
255
- });
256
-
257
- envCmd
258
- .command("show")
259
- .description("Display current .env.local (secrets redacted)")
260
- .action(async (_, cmd: Command) => {
261
- const opts = formatOpts(cmd);
262
- try {
263
- await handleShow(opts);
264
- } catch (err) {
265
- formatError(err instanceof Error ? err : String(err), opts);
266
- process.exit(err instanceof CLIError ? err.code : ExitCode.ERROR);
267
- }
268
- });
269
-
270
- envCmd
271
- .command("validate")
272
- .description("Check required environment variables")
273
- .action(async (_, cmd: Command) => {
274
- const opts = formatOpts(cmd);
275
- try {
276
- await handleValidate(opts);
277
- } catch (err) {
278
- formatError(err instanceof Error ? err : String(err), opts);
279
- process.exit(err instanceof CLIError ? err.code : ExitCode.ERROR);
280
- }
281
- });
282
-
283
- envCmd
284
- .command("generate-dev-vars")
285
- .description("Create per-worker .dev.vars from .env.local")
286
- .action(async (_, cmd: Command) => {
287
- const opts = formatOpts(cmd);
288
- try {
289
- await handleGenerateDevVars(opts);
290
- } catch (err) {
291
- formatError(err instanceof Error ? err : String(err), opts);
292
- process.exit(err instanceof CLIError ? err.code : ExitCode.ERROR);
293
- }
294
- });
295
- }
@@ -1,3 +0,0 @@
1
- export { registerConfigCommand } from "./config-command.js";
2
- export { registerEnvCommand } from "./env-command.js";
3
- export { registerKvCommand } from "./kv-command.js";
@@ -1,14 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
- import { KvSyncService } from "../../services/kv/index.js";
3
-
4
- describe("kv command", () => {
5
- describe("manifest", () => {
6
- it("returns known KV keys from manifest", () => {
7
- const keys = KvSyncService.getManifestKeys();
8
- expect(keys.length).toBe(16);
9
- expect(keys.some((k) => k.key === "trade:kill_switch")).toBe(true);
10
- expect(keys.some((k) => k.key === "agent:openai_key")).toBe(true);
11
- expect(keys.some((k) => k.key === "email:use_imap")).toBe(true);
12
- });
13
- });
14
- });
@@ -1,290 +0,0 @@
1
- /**
2
- * `hoox config kv` subcommand group — KV namespace key management.
3
- *
4
- * Subcommands:
5
- * list — List all keys
6
- * get <key> — Get a key's value
7
- * set <key> <value> — Set a key's value
8
- * delete <key> — Delete a key
9
- * apply-manifest — Apply manifest defaults
10
- * manifest — Show expected manifest keys
11
- */
12
-
13
- import { Command } from "commander";
14
- import { KvSyncService } from "../../services/kv/index.js";
15
- import {
16
- formatSuccess,
17
- formatError,
18
- formatTable,
19
- formatJson,
20
- } from "../../utils/formatters.js";
21
- import { CLIError, ExitCode } from "../../utils/errors.js";
22
- import { theme } from "../../utils/theme.js";
23
- import type { FormatOptions } from "../../utils/formatters.js";
24
-
25
- function formatOpts(cmd: Command): FormatOptions {
26
- const opts = cmd.optsWithGlobals<{ json?: boolean; quiet?: boolean }>();
27
- return { json: Boolean(opts.json), quiet: Boolean(opts.quiet) };
28
- }
29
-
30
- async function resolveNs(cmd: Command): Promise<string> {
31
- const svc = new KvSyncService();
32
- const opts = cmd.optsWithGlobals<{ namespaceId?: string }>();
33
- return await svc.resolveNamespaceId(opts.namespaceId);
34
- }
35
-
36
- // ---------------------------------------------------------------------------
37
- // kv list
38
- // ---------------------------------------------------------------------------
39
-
40
- async function handleList(opts: FormatOptions, nsId: string): Promise<void> {
41
- const svc = new KvSyncService();
42
- const keys = await svc.list(nsId);
43
-
44
- if (opts.json) {
45
- formatJson(keys, opts);
46
- } else if (!opts.quiet) {
47
- if (keys.length === 0) {
48
- process.stdout.write("No keys found in KV namespace.\n");
49
- return;
50
- }
51
- const rows = keys.map((k) => ({ Key: k.name }));
52
- formatTable(rows, opts);
53
- }
54
- }
55
-
56
- // ---------------------------------------------------------------------------
57
- // kv get
58
- // ---------------------------------------------------------------------------
59
-
60
- async function handleGet(
61
- opts: FormatOptions,
62
- nsId: string,
63
- key: string
64
- ): Promise<void> {
65
- const svc = new KvSyncService();
66
- const value = await svc.get(nsId, key);
67
-
68
- if (value === null) {
69
- throw new CLIError(
70
- `Key "${key}" not found in KV namespace`,
71
- ExitCode.ERROR
72
- );
73
- }
74
-
75
- if (opts.json) {
76
- formatJson({ key, value }, opts);
77
- } else {
78
- process.stdout.write(`${value}\n`);
79
- }
80
- }
81
-
82
- // ---------------------------------------------------------------------------
83
- // kv set
84
- // ---------------------------------------------------------------------------
85
-
86
- async function handleSet(
87
- opts: FormatOptions,
88
- nsId: string,
89
- key: string,
90
- value: string
91
- ): Promise<void> {
92
- const svc = new KvSyncService();
93
- await svc.set(nsId, key, value);
94
- formatSuccess(`Set "${key}" in KV namespace`, opts);
95
- }
96
-
97
- // ---------------------------------------------------------------------------
98
- // kv delete
99
- // ---------------------------------------------------------------------------
100
-
101
- async function handleDelete(
102
- opts: FormatOptions,
103
- nsId: string,
104
- key: string
105
- ): Promise<void> {
106
- const svc = new KvSyncService();
107
- await svc.delete(nsId, key);
108
- formatSuccess(`Deleted "${key}" from KV namespace`, opts);
109
- }
110
-
111
- // ---------------------------------------------------------------------------
112
- // kv apply-manifest
113
- // ---------------------------------------------------------------------------
114
-
115
- async function handleApplyManifest(
116
- opts: FormatOptions,
117
- nsId: string
118
- ): Promise<void> {
119
- const svc = new KvSyncService();
120
- const manifest = KvSyncService.getManifest();
121
- let setCount = 0;
122
- const errors: string[] = [];
123
-
124
- for (const keyDef of manifest.keys) {
125
- if (keyDef.default !== undefined && keyDef.default !== "") {
126
- try {
127
- await svc.set(nsId, keyDef.key, keyDef.default);
128
- setCount++;
129
- } catch (err) {
130
- errors.push(
131
- `${keyDef.key}: ${err instanceof Error ? err.message : String(err)}`
132
- );
133
- }
134
- }
135
- }
136
-
137
- formatSuccess(
138
- `Applied ${setCount}/${manifest.keys.length} manifest keys (${errors.length} errors)`,
139
- opts
140
- );
141
- }
142
-
143
- // ---------------------------------------------------------------------------
144
- // kv manifest
145
- // ---------------------------------------------------------------------------
146
-
147
- async function handleManifest(opts: FormatOptions): Promise<void> {
148
- const manifest = KvSyncService.getManifest();
149
-
150
- if (opts.json) {
151
- formatJson(manifest, opts);
152
- } else if (!opts.quiet) {
153
- process.stdout.write(
154
- `${theme.heading(`KV Manifest: ${manifest.namespace}`)}\n\n`
155
- );
156
- const rows = manifest.keys.map((k) => ({
157
- Key: k.key,
158
- Type: k.type,
159
- Default: k.default || "-",
160
- Secret: k.secret ? "yes" : "no",
161
- Description: k.description,
162
- }));
163
- formatTable(rows, opts);
164
- }
165
- }
166
-
167
- // ---------------------------------------------------------------------------
168
- // Registration
169
- // ---------------------------------------------------------------------------
170
-
171
- export function registerKvCommand(parentCmd: Command): void {
172
- const kvCmd = parentCmd
173
- .command("kv")
174
- .summary("Manage KV namespace keys")
175
- .description(
176
- `Manage Cloudflare KV namespace keys and apply the key manifest.
177
-
178
- SUBCOMMANDS:
179
- list List all keys in the KV namespace
180
- get <key> Get a key's value
181
- set <key> <value> Set a key's value
182
- delete <key> Delete a key
183
- apply-manifest Apply manifest key defaults to KV
184
- manifest Show expected KV keys from manifest
185
-
186
- OPTIONS:
187
- --namespace-id <id> KV namespace ID (auto-detected from wrangler if omitted)
188
-
189
- EXAMPLES:
190
- hoox config kv list
191
- hoox config kv set trade:kill_switch true
192
- hoox config kv get trade:kill_switch
193
- hoox config kv delete trade:kill_switch
194
- hoox config kv apply-manifest
195
- hoox config kv manifest`
196
- )
197
- .option(
198
- "--namespace-id <id>",
199
- "KV namespace ID (auto-detected if omitted)"
200
- );
201
-
202
- // -- list
203
- kvCmd
204
- .command("list")
205
- .description("List all keys in the KV namespace")
206
- .action(async (_, cmd: Command) => {
207
- const opts = formatOpts(cmd);
208
- try {
209
- const nsId = await resolveNs(cmd);
210
- await handleList(opts, nsId);
211
- } catch (err) {
212
- formatError(err instanceof Error ? err : String(err), opts);
213
- process.exit(err instanceof CLIError ? err.code : ExitCode.ERROR);
214
- }
215
- });
216
-
217
- // -- get
218
- kvCmd
219
- .command("get <key>")
220
- .description("Get a key's value from the KV namespace")
221
- .action(async (key: string, _, cmd: Command) => {
222
- const opts = formatOpts(cmd);
223
- try {
224
- const nsId = await resolveNs(cmd);
225
- await handleGet(opts, nsId, key);
226
- } catch (err) {
227
- formatError(err instanceof Error ? err : String(err), opts);
228
- process.exit(err instanceof CLIError ? err.code : ExitCode.ERROR);
229
- }
230
- });
231
-
232
- // -- set
233
- kvCmd
234
- .command("set <key> <value>")
235
- .description("Set a key's value in the KV namespace")
236
- .action(async (key: string, value: string, _, cmd: Command) => {
237
- const opts = formatOpts(cmd);
238
- try {
239
- const nsId = await resolveNs(cmd);
240
- await handleSet(opts, nsId, key, value);
241
- } catch (err) {
242
- formatError(err instanceof Error ? err : String(err), opts);
243
- process.exit(err instanceof CLIError ? err.code : ExitCode.ERROR);
244
- }
245
- });
246
-
247
- // -- delete
248
- kvCmd
249
- .command("delete <key>")
250
- .description("Delete a key from the KV namespace")
251
- .action(async (key: string, _, cmd: Command) => {
252
- const opts = formatOpts(cmd);
253
- try {
254
- const nsId = await resolveNs(cmd);
255
- await handleDelete(opts, nsId, key);
256
- } catch (err) {
257
- formatError(err instanceof Error ? err : String(err), opts);
258
- process.exit(err instanceof CLIError ? err.code : ExitCode.ERROR);
259
- }
260
- });
261
-
262
- // -- apply-manifest
263
- kvCmd
264
- .command("apply-manifest")
265
- .description("Apply manifest key defaults to the KV namespace")
266
- .action(async (_, cmd: Command) => {
267
- const opts = formatOpts(cmd);
268
- try {
269
- const nsId = await resolveNs(cmd);
270
- await handleApplyManifest(opts, nsId);
271
- } catch (err) {
272
- formatError(err instanceof Error ? err : String(err), opts);
273
- process.exit(err instanceof CLIError ? err.code : ExitCode.ERROR);
274
- }
275
- });
276
-
277
- // -- manifest
278
- kvCmd
279
- .command("manifest")
280
- .description("Show expected KV keys from manifest")
281
- .action(async (_, cmd: Command) => {
282
- const opts = formatOpts(cmd);
283
- try {
284
- await handleManifest(opts);
285
- } catch (err) {
286
- formatError(err instanceof Error ? err : String(err), opts);
287
- process.exit(err instanceof CLIError ? err.code : ExitCode.ERROR);
288
- }
289
- });
290
- }
@@ -1,47 +0,0 @@
1
- import { describe, it, expect } from "bun:test";
2
- import type { Command } from "commander";
3
-
4
- const { registerDashboardCommand } = await import("./dashboard-command.js");
5
-
6
- describe("registerDashboardCommand", () => {
7
- it("registers dashboard command group on program", () => {
8
- const commands: { name: string; description: string }[] = [];
9
- const subcommands: { parent: string; name: string; description: string }[] =
10
- [];
11
-
12
- const mockProgram = {
13
- command: (name: string) => {
14
- commands.push({ name, description: "" });
15
- const desc = (d: string) => {
16
- commands[commands.length - 1].description = d;
17
- return {
18
- command: (subName: string) => {
19
- subcommands.push({
20
- parent: name,
21
- name: subName,
22
- description: "",
23
- });
24
- return {
25
- description: (d2: string) => {
26
- subcommands[subcommands.length - 1].description = d2;
27
- return {
28
- option: () => ({ action: () => {} }),
29
- };
30
- },
31
- };
32
- },
33
- };
34
- };
35
- return { description: desc };
36
- },
37
- opts: () => ({ json: false, quiet: false }),
38
- } as unknown as Command;
39
-
40
- registerDashboardCommand(mockProgram);
41
-
42
- expect(commands.length).toBe(1);
43
- expect(commands[0].name).toBe("dashboard");
44
- expect(subcommands.length).toBe(1);
45
- expect(subcommands[0].name).toBe("update-urls");
46
- });
47
- });