@jango-blockchained/hoox-cli 0.5.2 → 0.5.7
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 +32989 -0
- package/package.json +10 -15
- package/src/commands/check/check-command.test.ts +0 -476
- package/src/commands/check/check-command.ts +0 -1087
- 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 -97
- package/src/commands/check/types.ts +0 -103
- package/src/commands/clone/clone-command.test.ts +0 -440
- 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 -896
- package/src/commands/config/env-command.test.ts +0 -43
- package/src/commands/config/env-command.ts +0 -292
- 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 -130
- 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 -311
- package/src/commands/db/index.ts +0 -1
- package/src/commands/deploy/deploy-command.test.ts +0 -324
- package/src/commands/deploy/deploy-command.ts +0 -1054
- 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 -405
- package/src/commands/dev/dev-command.ts +0 -398
- package/src/commands/dev/index.ts +0 -1
- package/src/commands/disclaimer/disclaimer-command.ts +0 -21
- package/src/commands/disclaimer/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 -854
- package/src/commands/init/init-command.ts +0 -597
- 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 -293
- 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 -271
- 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 -243
- package/src/commands/repair/repair-command.ts +0 -220
- package/src/commands/repair/repair-service.ts +0 -164
- package/src/commands/repair/types.ts +0 -13
- package/src/commands/schema/index.ts +0 -1
- package/src/commands/schema/schema-command.ts +0 -137
- package/src/commands/test/index.ts +0 -2
- package/src/commands/test/test-command.test.ts +0 -318
- 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 -109
- 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 -545
- package/src/index.ts +0 -261
- 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 -202
- 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 -63
- 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 -259
- package/src/services/env/env-service.ts +0 -372
- 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 -243
- 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/schema/index.ts +0 -1
- package/src/services/schema/schema-service.ts +0 -99
- package/src/services/secrets/index.ts +0 -11
- package/src/services/secrets/secrets-service.test.ts +0 -502
- 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/error-handler.ts +0 -62
- package/src/utils/errors.test.ts +0 -69
- package/src/utils/errors.ts +0 -24
- package/src/utils/formatters.test.ts +0 -180
- package/src/utils/formatters.ts +0 -269
- package/src/utils/git.ts +0 -134
- package/src/utils/theme.ts +0 -92
|
@@ -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
|
-
TRADE_INTERNAL_KEY: "trade-key",
|
|
18
|
-
AGENT_INTERNAL_KEY: "agent-key",
|
|
19
|
-
WEBHOOK_API_KEY_BINDING: "webhook-key",
|
|
20
|
-
INTERNAL_KEY_BINDING: "inter-key",
|
|
21
|
-
API_SERVICE_KEY_BINDING: "api-key",
|
|
22
|
-
TELEGRAM_INTERNAL_KEY: "tg-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,292 +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 { formatSuccess, formatJson } from "../../utils/formatters.js";
|
|
17
|
-
import { withErrorHandling } from "../../utils/error-handler.js";
|
|
18
|
-
import type { FormatOptions } from "../../utils/formatters.js";
|
|
19
|
-
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
// Helpers
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Get FormatOptions from the command opts using optsWithGlobals() so that
|
|
26
|
-
* globally defined flags (--json, --quiet) are resolved correctly even when
|
|
27
|
-
* called from deeply nested subcommands.
|
|
28
|
-
*/
|
|
29
|
-
function formatOpts(cmd: Command): FormatOptions {
|
|
30
|
-
const opts = cmd.optsWithGlobals<{ json?: boolean; quiet?: boolean }>();
|
|
31
|
-
return { json: Boolean(opts.json), quiet: Boolean(opts.quiet) };
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// ---------------------------------------------------------------------------
|
|
35
|
-
// env init — interactive wizard
|
|
36
|
-
// ---------------------------------------------------------------------------
|
|
37
|
-
|
|
38
|
-
async function handleInit(opts: FormatOptions): Promise<void> {
|
|
39
|
-
p.intro("Environment Setup");
|
|
40
|
-
|
|
41
|
-
// Warn if .env.local already exists
|
|
42
|
-
const existingEnv = Bun.file(".env.local");
|
|
43
|
-
if (await existingEnv.exists()) {
|
|
44
|
-
const overwrite = await p.confirm({
|
|
45
|
-
message: ".env.local already exists. Overwrite?",
|
|
46
|
-
initialValue: false,
|
|
47
|
-
});
|
|
48
|
-
if (p.isCancel(overwrite)) {
|
|
49
|
-
p.cancel("Setup cancelled.");
|
|
50
|
-
process.exitCode = 0;
|
|
51
|
-
}
|
|
52
|
-
if (!overwrite) {
|
|
53
|
-
p.outro("Setup cancelled. Existing .env.local preserved.");
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const collected: Record<string, string> = {};
|
|
59
|
-
const sections = EnvService.getSections();
|
|
60
|
-
|
|
61
|
-
for (const section of sections) {
|
|
62
|
-
const sectionDefs = EnvService.getDefinitions().filter(
|
|
63
|
-
(d) => d.section === section
|
|
64
|
-
);
|
|
65
|
-
if (sectionDefs.length === 0) continue;
|
|
66
|
-
|
|
67
|
-
p.log.step(`Configuring ${section}...`);
|
|
68
|
-
|
|
69
|
-
for (const def of sectionDefs) {
|
|
70
|
-
let value: string | symbol;
|
|
71
|
-
if (def.secret) {
|
|
72
|
-
value = await p.password({
|
|
73
|
-
message: `${def.name}:${def.hint ? ` (${def.hint})` : ""}`,
|
|
74
|
-
validate: def.required
|
|
75
|
-
? (v: string | undefined) => (v ? undefined : "Required")
|
|
76
|
-
: undefined,
|
|
77
|
-
});
|
|
78
|
-
} else {
|
|
79
|
-
value = await p.text({
|
|
80
|
-
message: `${def.name}:${def.hint ? ` (${def.hint})` : ""}`,
|
|
81
|
-
defaultValue: def.default ?? "",
|
|
82
|
-
validate: def.required
|
|
83
|
-
? (v: string | undefined) => (v ? undefined : "Required")
|
|
84
|
-
: undefined,
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
if (p.isCancel(value)) {
|
|
88
|
-
p.cancel("Setup cancelled.");
|
|
89
|
-
process.exitCode = 0;
|
|
90
|
-
}
|
|
91
|
-
collected[def.name] = typeof value === "string" ? value : "";
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
p.log.step("Writing .env.local...");
|
|
96
|
-
const envContent = EnvService.generateEnvLocal(collected);
|
|
97
|
-
await Bun.write(".env.local", envContent);
|
|
98
|
-
if (!opts.quiet) formatSuccess(".env.local written", opts);
|
|
99
|
-
|
|
100
|
-
p.log.step("Writing .dev.vars files...");
|
|
101
|
-
const devVars = EnvService.getWorkerDevVars(collected);
|
|
102
|
-
for (const [workerPath, vars] of Object.entries(devVars)) {
|
|
103
|
-
const content = [
|
|
104
|
-
`# .dev.vars - local secrets for ${workerPath}`,
|
|
105
|
-
"# Generated by `hoox config env init`. NEVER commit this file.",
|
|
106
|
-
"",
|
|
107
|
-
...Object.entries(vars).map(([k, v]) => `${k}=${v}`),
|
|
108
|
-
"",
|
|
109
|
-
].join("\n");
|
|
110
|
-
await Bun.write(`${workerPath}/.dev.vars`, content);
|
|
111
|
-
if (!opts.quiet) formatSuccess(`Created ${workerPath}/.dev.vars`, opts);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
p.outro("Environment setup complete!");
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// ---------------------------------------------------------------------------
|
|
118
|
-
// env show
|
|
119
|
-
// ---------------------------------------------------------------------------
|
|
120
|
-
|
|
121
|
-
async function handleShow(opts: FormatOptions): Promise<void> {
|
|
122
|
-
const file = Bun.file(".env.local");
|
|
123
|
-
if (!(await file.exists())) {
|
|
124
|
-
throw new CLIError(
|
|
125
|
-
".env.local not found. Run `hoox config env init`.",
|
|
126
|
-
ExitCode.INVALID_USAGE
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const vars = await EnvService.loadDotEnvAsync(".env.local");
|
|
131
|
-
|
|
132
|
-
if (opts.json) {
|
|
133
|
-
formatJson(vars, opts);
|
|
134
|
-
} else {
|
|
135
|
-
process.stdout.write(EnvService.show(vars));
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// ---------------------------------------------------------------------------
|
|
140
|
-
// env validate
|
|
141
|
-
// ---------------------------------------------------------------------------
|
|
142
|
-
|
|
143
|
-
async function handleValidate(opts: FormatOptions): Promise<void> {
|
|
144
|
-
let vars: Record<string, string> = {};
|
|
145
|
-
const file = Bun.file(".env.local");
|
|
146
|
-
if (await file.exists()) {
|
|
147
|
-
vars = await EnvService.loadDotEnvAsync(".env.local");
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const result = EnvService.validate(vars);
|
|
151
|
-
|
|
152
|
-
if (opts.json) {
|
|
153
|
-
formatJson(result, opts);
|
|
154
|
-
} else if (!opts.quiet) {
|
|
155
|
-
if (!(await file.exists())) {
|
|
156
|
-
process.stdout.write(
|
|
157
|
-
".env.local not found. Run `hoox config env init` first.\n\n"
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
if (result.missing.length > 0) {
|
|
161
|
-
process.stdout.write("Missing required variables:\n");
|
|
162
|
-
for (const v of result.missing) {
|
|
163
|
-
process.stdout.write(` ${v}\n`);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
if (result.warnings.length > 0) {
|
|
167
|
-
process.stdout.write("Warnings:\n");
|
|
168
|
-
for (const w of result.warnings) {
|
|
169
|
-
process.stdout.write(` ${w}\n`);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
if (result.missing.length === 0 && result.warnings.length === 0) {
|
|
173
|
-
formatSuccess("All required environment variables are set", opts);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (result.missing.length > 0) {
|
|
178
|
-
process.exitCode = ExitCode.ERROR;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// ---------------------------------------------------------------------------
|
|
183
|
-
// env generate-dev-vars
|
|
184
|
-
// ---------------------------------------------------------------------------
|
|
185
|
-
|
|
186
|
-
async function handleGenerateDevVars(opts: FormatOptions): Promise<void> {
|
|
187
|
-
const file = Bun.file(".env.local");
|
|
188
|
-
if (!(await file.exists())) {
|
|
189
|
-
throw new CLIError(
|
|
190
|
-
".env.local not found. Run `hoox config env init`.",
|
|
191
|
-
ExitCode.INVALID_USAGE
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const vars = await EnvService.loadDotEnvAsync(".env.local");
|
|
196
|
-
const devVars = EnvService.getWorkerDevVars(vars);
|
|
197
|
-
|
|
198
|
-
let count = 0;
|
|
199
|
-
for (const [workerPath, workerVars] of Object.entries(devVars)) {
|
|
200
|
-
const content = [
|
|
201
|
-
`# .dev.vars - local secrets for ${workerPath}`,
|
|
202
|
-
"# Generated by `hoox config env generate-dev-vars`. NEVER commit this file.",
|
|
203
|
-
"",
|
|
204
|
-
...Object.entries(workerVars).map(([k, v]) => `${k}=${v}`),
|
|
205
|
-
"",
|
|
206
|
-
].join("\n");
|
|
207
|
-
await Bun.write(`${workerPath}/.dev.vars`, content);
|
|
208
|
-
count++;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
formatSuccess(`Generated .dev.vars for ${count} worker(s)`, opts);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// ---------------------------------------------------------------------------
|
|
215
|
-
// Registration
|
|
216
|
-
// ---------------------------------------------------------------------------
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Register the `hoox config env` subcommand group.
|
|
220
|
-
*/
|
|
221
|
-
export function registerEnvCommand(parentCmd: Command): void {
|
|
222
|
-
const envCmd = parentCmd
|
|
223
|
-
.command("env")
|
|
224
|
-
.summary("Manage environment variables")
|
|
225
|
-
.description(
|
|
226
|
-
`Manage environment variables for the Hoox trading system.
|
|
227
|
-
|
|
228
|
-
SUBCOMMANDS:
|
|
229
|
-
init Interactive wizard to generate .env.local and .dev.vars
|
|
230
|
-
show Display current .env.local (secrets redacted)
|
|
231
|
-
validate Check required environment variables
|
|
232
|
-
generate-dev-vars Create per-worker .dev.vars from .env.local
|
|
233
|
-
|
|
234
|
-
EXAMPLES:
|
|
235
|
-
hoox config env init
|
|
236
|
-
hoox config env show
|
|
237
|
-
hoox config env validate
|
|
238
|
-
hoox config env generate-dev-vars`
|
|
239
|
-
);
|
|
240
|
-
|
|
241
|
-
envCmd
|
|
242
|
-
.command("init")
|
|
243
|
-
.description("Interactive wizard to generate .env.local and .dev.vars")
|
|
244
|
-
.action(
|
|
245
|
-
withErrorHandling(
|
|
246
|
-
async (_, cmd: Command) => {
|
|
247
|
-
const opts = formatOpts(cmd);
|
|
248
|
-
await handleInit(opts);
|
|
249
|
-
},
|
|
250
|
-
{ service: "env" }
|
|
251
|
-
)
|
|
252
|
-
);
|
|
253
|
-
|
|
254
|
-
envCmd
|
|
255
|
-
.command("show")
|
|
256
|
-
.description("Display current .env.local (secrets redacted)")
|
|
257
|
-
.action(
|
|
258
|
-
withErrorHandling(
|
|
259
|
-
async (_, cmd: Command) => {
|
|
260
|
-
const opts = formatOpts(cmd);
|
|
261
|
-
await handleShow(opts);
|
|
262
|
-
},
|
|
263
|
-
{ service: "env" }
|
|
264
|
-
)
|
|
265
|
-
);
|
|
266
|
-
|
|
267
|
-
envCmd
|
|
268
|
-
.command("validate")
|
|
269
|
-
.description("Check required environment variables")
|
|
270
|
-
.action(
|
|
271
|
-
withErrorHandling(
|
|
272
|
-
async (_, cmd: Command) => {
|
|
273
|
-
const opts = formatOpts(cmd);
|
|
274
|
-
await handleValidate(opts);
|
|
275
|
-
},
|
|
276
|
-
{ service: "env" }
|
|
277
|
-
)
|
|
278
|
-
);
|
|
279
|
-
|
|
280
|
-
envCmd
|
|
281
|
-
.command("generate-dev-vars")
|
|
282
|
-
.description("Create per-worker .dev.vars from .env.local")
|
|
283
|
-
.action(
|
|
284
|
-
withErrorHandling(
|
|
285
|
-
async (_, cmd: Command) => {
|
|
286
|
-
const opts = formatOpts(cmd);
|
|
287
|
-
await handleGenerateDevVars(opts);
|
|
288
|
-
},
|
|
289
|
-
{ service: "env" }
|
|
290
|
-
)
|
|
291
|
-
);
|
|
292
|
-
}
|
|
@@ -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
|
-
formatTable,
|
|
18
|
-
formatJson,
|
|
19
|
-
} from "../../utils/formatters.js";
|
|
20
|
-
import { CLIError, ExitCode } from "../../utils/errors.js";
|
|
21
|
-
import { withErrorHandling } from "../../utils/error-handler.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(
|
|
207
|
-
withErrorHandling(
|
|
208
|
-
async (_, cmd: Command) => {
|
|
209
|
-
const opts = formatOpts(cmd);
|
|
210
|
-
const nsId = await resolveNs(cmd);
|
|
211
|
-
await handleList(opts, nsId);
|
|
212
|
-
},
|
|
213
|
-
{ service: "kv" }
|
|
214
|
-
)
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
// -- get
|
|
218
|
-
kvCmd
|
|
219
|
-
.command("get <key>")
|
|
220
|
-
.description("Get a key's value from the KV namespace")
|
|
221
|
-
.action(
|
|
222
|
-
withErrorHandling(
|
|
223
|
-
async (key: string, _, cmd: Command) => {
|
|
224
|
-
const opts = formatOpts(cmd);
|
|
225
|
-
const nsId = await resolveNs(cmd);
|
|
226
|
-
await handleGet(opts, nsId, key);
|
|
227
|
-
},
|
|
228
|
-
{ service: "kv" }
|
|
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(
|
|
237
|
-
withErrorHandling(
|
|
238
|
-
async (key: string, value: string, _, cmd: Command) => {
|
|
239
|
-
const opts = formatOpts(cmd);
|
|
240
|
-
const nsId = await resolveNs(cmd);
|
|
241
|
-
await handleSet(opts, nsId, key, value);
|
|
242
|
-
},
|
|
243
|
-
{ service: "kv" }
|
|
244
|
-
)
|
|
245
|
-
);
|
|
246
|
-
|
|
247
|
-
// -- delete
|
|
248
|
-
kvCmd
|
|
249
|
-
.command("delete <key>")
|
|
250
|
-
.description("Delete a key from the KV namespace")
|
|
251
|
-
.action(
|
|
252
|
-
withErrorHandling(
|
|
253
|
-
async (key: string, _, cmd: Command) => {
|
|
254
|
-
const opts = formatOpts(cmd);
|
|
255
|
-
const nsId = await resolveNs(cmd);
|
|
256
|
-
await handleDelete(opts, nsId, key);
|
|
257
|
-
},
|
|
258
|
-
{ service: "kv" }
|
|
259
|
-
)
|
|
260
|
-
);
|
|
261
|
-
|
|
262
|
-
// -- apply-manifest
|
|
263
|
-
kvCmd
|
|
264
|
-
.command("apply-manifest")
|
|
265
|
-
.description("Apply manifest key defaults to the KV namespace")
|
|
266
|
-
.action(
|
|
267
|
-
withErrorHandling(
|
|
268
|
-
async (_, cmd: Command) => {
|
|
269
|
-
const opts = formatOpts(cmd);
|
|
270
|
-
const nsId = await resolveNs(cmd);
|
|
271
|
-
await handleApplyManifest(opts, nsId);
|
|
272
|
-
},
|
|
273
|
-
{ service: "kv" }
|
|
274
|
-
)
|
|
275
|
-
);
|
|
276
|
-
|
|
277
|
-
// -- manifest
|
|
278
|
-
kvCmd
|
|
279
|
-
.command("manifest")
|
|
280
|
-
.description("Show expected KV keys from manifest")
|
|
281
|
-
.action(
|
|
282
|
-
withErrorHandling(
|
|
283
|
-
async (_, cmd: Command) => {
|
|
284
|
-
const opts = formatOpts(cmd);
|
|
285
|
-
await handleManifest(opts);
|
|
286
|
-
},
|
|
287
|
-
{ service: "kv" }
|
|
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
|
-
});
|