@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,452 +0,0 @@
1
- /**
2
- * `hoox logs` command group — real-time log tailing for Cloudflare Workers.
3
- *
4
- * Subcommands:
5
- * worker <name> — Tail logs for a specific worker
6
- * all — Tail logs from all enabled workers concurrently
7
- *
8
- * Uses Bun.spawn to run `wrangler tail` with streaming output.
9
- * Supports --level (log/info/warn/error), --follow/--no-follow, and --json flags.
10
- */
11
-
12
- import { Command } from "commander";
13
- import { ConfigService } from "../../services/config/index.js";
14
- import { CLIError, ExitCode } from "../../utils/errors.js";
15
- import {
16
- formatError,
17
- formatSuccess,
18
- getFormatOptions,
19
- } from "../../utils/formatters.js";
20
- import { theme } from "../../utils/theme.js";
21
- import type { FormatOptions } from "../../utils/formatters.js";
22
-
23
- // ---------------------------------------------------------------------------
24
- // Types
25
- // ---------------------------------------------------------------------------
26
-
27
- /** Valid log levels for filtering */
28
- const VALID_LEVELS = ["log", "info", "warn", "error", "debug", "all"] as const;
29
- type LogLevel = (typeof VALID_LEVELS)[number];
30
-
31
- interface WorkerLogOptions {
32
- level: LogLevel;
33
- follow: boolean;
34
- json: boolean;
35
- }
36
-
37
- interface WranglerLogEntry {
38
- outcome: string;
39
- scriptName?: string;
40
- logs?: Array<{
41
- message: string[];
42
- level: string;
43
- timestamp: number;
44
- }>;
45
- exceptions?: Array<{
46
- name: string;
47
- message: string;
48
- }>;
49
- eventTimestamp?: number;
50
- event?: unknown;
51
- }
52
-
53
- // ---------------------------------------------------------------------------
54
- // Helpers
55
- // ---------------------------------------------------------------------------
56
-
57
- /**
58
- * Validate and normalize the log level string.
59
- * Defaults to "all" for unknown values.
60
- */
61
- function normalizeLevel(raw: string): LogLevel {
62
- const lower = raw.toLowerCase();
63
- if (VALID_LEVELS.includes(lower as LogLevel)) {
64
- return lower as LogLevel;
65
- }
66
- return "all";
67
- }
68
-
69
- /**
70
- * Check whether a log entry matches the requested level filter.
71
- * "all" matches everything; otherwise, the entry's level must equal the filter.
72
- */
73
- function matchesLevel(entry: WranglerLogEntry, level: LogLevel): boolean {
74
- if (level === "all") return true;
75
- const entryLogs = entry.logs ?? [];
76
- return entryLogs.some((l) => l.level === level);
77
- }
78
-
79
- /**
80
- * Format a single JSON log line from wrangler for human-readable output.
81
- * Extracts timestamp, level, and message fields.
82
- */
83
- function formatLogLine(entry: WranglerLogEntry): string {
84
- const timestamp = entry.eventTimestamp
85
- ? new Date(entry.eventTimestamp).toISOString()
86
- : new Date().toISOString();
87
-
88
- const logs = entry.logs ?? [];
89
- if (logs.length === 0) {
90
- // Non-log events (e.g. request outcome)
91
- return theme.dim(`[${timestamp}]`) + ` ${entry.outcome}`;
92
- }
93
-
94
- const parts = logs.map((l) => {
95
- const levelColor =
96
- l.level === "error"
97
- ? theme.error
98
- : l.level === "warn"
99
- ? theme.warning
100
- : l.level === "info"
101
- ? theme.info
102
- : theme.dim;
103
- const msg = l.message.join(" ");
104
- return `${theme.dim(`[${timestamp}]`)} ${levelColor(`[${l.level.toUpperCase()}]`)} ${msg}`;
105
- });
106
-
107
- return parts.join("\n");
108
- }
109
-
110
- /**
111
- * Parse a line from wrangler tail --format json output.
112
- * Returns null if the line is not valid JSON.
113
- */
114
- function parseLogLine(line: string): WranglerLogEntry | null {
115
- const trimmed = line.trim();
116
- if (!trimmed) return null;
117
- try {
118
- return JSON.parse(trimmed) as WranglerLogEntry;
119
- } catch {
120
- // Non-JSON lines are passed through as-is (e.g. wrangler status messages)
121
- return null;
122
- }
123
- }
124
-
125
- // ---------------------------------------------------------------------------
126
- // Tail implementation
127
- // ---------------------------------------------------------------------------
128
-
129
- /**
130
- * Tail logs for a single worker using wrangler.
131
- *
132
- * Spawns `wrangler tail <workerName>` and streams stdout to the terminal.
133
- * With --follow (default), runs until interrupted (Ctrl+C).
134
- * With --no-follow, captures output for 5 seconds then exits.
135
- * With --level, filters JSON log entries by log level.
136
- * With --json, passes through wrangler's JSON output directly.
137
- */
138
- async function tailWorker(
139
- workerName: string,
140
- opts: WorkerLogOptions,
141
- fmt: FormatOptions
142
- ): Promise<void> {
143
- const wranglerArgs = ["wrangler", "tail", workerName];
144
-
145
- // Use JSON format when level filtering or --json output is requested
146
- if (opts.level !== "all" || opts.json) {
147
- wranglerArgs.push("--format", "json");
148
- }
149
-
150
- let proc: Bun.Subprocess | null = null;
151
-
152
- try {
153
- proc = Bun.spawn(wranglerArgs, {
154
- stdout: "pipe",
155
- stderr: "pipe",
156
- });
157
-
158
- if (!proc.stdout || typeof proc.stdout === "number") {
159
- throw new CLIError(
160
- "Failed to get stdout stream from wrangler tail",
161
- ExitCode.ERROR
162
- );
163
- }
164
-
165
- const reader = (proc.stdout as ReadableStream<Uint8Array>).getReader();
166
- const decoder = new TextDecoder();
167
- let buffer = "";
168
-
169
- const readLoop = async () => {
170
- while (true) {
171
- const { done, value } = await reader.read();
172
- if (done) break;
173
-
174
- buffer += decoder.decode(value, { stream: true });
175
-
176
- // Process complete lines
177
- const lines = buffer.split("\n");
178
- buffer = lines.pop() ?? ""; // Keep incomplete line in buffer
179
-
180
- for (const line of lines) {
181
- if (!line.trim()) continue;
182
-
183
- if (opts.level !== "all" || opts.json) {
184
- const entry = parseLogLine(line);
185
-
186
- if (entry !== null) {
187
- // JSON parse succeeded
188
- if (!matchesLevel(entry, opts.level)) continue;
189
-
190
- if (opts.json) {
191
- process.stdout.write(JSON.stringify(entry) + "\n");
192
- } else {
193
- process.stdout.write(formatLogLine(entry) + "\n");
194
- }
195
- } else {
196
- // Non-JSON line (wrangler status message) — pass through
197
- if (opts.level === "all") {
198
- process.stdout.write(line + "\n");
199
- }
200
- }
201
- } else {
202
- // Raw pass-through mode
203
- process.stdout.write(line + "\n");
204
- }
205
- }
206
- }
207
-
208
- // Flush remaining buffer
209
- if (buffer.trim()) {
210
- process.stdout.write(buffer + "\n");
211
- }
212
- };
213
-
214
- if (opts.follow) {
215
- // Continuous streaming — run until interrupted
216
- await readLoop();
217
- } else {
218
- // Show recent and exit after a short timeout
219
- const timeout = 5000;
220
- const timeoutPromise = new Promise<void>((resolve) =>
221
- setTimeout(resolve, timeout)
222
- );
223
-
224
- await Promise.race([readLoop(), timeoutPromise]);
225
-
226
- // Kill the wrangler process after timeout
227
- if (proc && !proc.killed) {
228
- proc.kill();
229
- }
230
- }
231
- } catch (err) {
232
- const message = err instanceof Error ? err.message : String(err);
233
- formatError(
234
- new CLIError(
235
- `Failed to tail logs for "${workerName}": ${message}`,
236
- ExitCode.ERROR
237
- ),
238
- fmt
239
- );
240
- process.exitCode = ExitCode.ERROR;
241
- } finally {
242
- // Ensure the process is killed on cleanup
243
- if (proc && !proc.killed) {
244
- proc.kill();
245
- }
246
- }
247
- }
248
-
249
- /**
250
- * Tail logs for all enabled workers concurrently.
251
- *
252
- * Loads ConfigService.listEnabledWorkers(), spawns a tail process for each,
253
- * and prefixes output with the worker name.
254
- */
255
- async function tailAllWorkers(
256
- opts: WorkerLogOptions,
257
- fmt: FormatOptions
258
- ): Promise<void> {
259
- const configService = new ConfigService();
260
- await configService.load();
261
- const workers = configService.listEnabledWorkers();
262
-
263
- if (workers.length === 0) {
264
- formatSuccess("No enabled workers found to tail", fmt);
265
- return;
266
- }
267
-
268
- const wranglerArgsBase = ["wrangler", "tail"];
269
- if (opts.level !== "all" || opts.json) {
270
- wranglerArgsBase.push("--format", "json");
271
- }
272
-
273
- const processes: Array<{
274
- name: string;
275
- proc: Bun.Subprocess;
276
- reader: ReadableStreamDefaultReader<Uint8Array>;
277
- }> = [];
278
-
279
- try {
280
- // Spawn a tail process for each worker
281
- for (const workerName of workers) {
282
- const proc = Bun.spawn(
283
- ["wrangler", "tail", workerName, ...wranglerArgsBase.slice(2)],
284
- {
285
- stdout: "pipe",
286
- stderr: "pipe",
287
- }
288
- );
289
- processes.push({
290
- name: workerName,
291
- proc,
292
- reader: proc.stdout.getReader(),
293
- });
294
- }
295
-
296
- // Read from all workers concurrently, prefixing with worker name
297
- const decoder = new TextDecoder();
298
- const buffers = new Map<string, string>();
299
-
300
- const readFromWorker = async (p: (typeof processes)[0]) => {
301
- let buffer = buffers.get(p.name) ?? "";
302
- while (true) {
303
- const { done, value } = await p.reader.read();
304
- if (done) break;
305
-
306
- buffer += decoder.decode(value, { stream: true });
307
- const lines = buffer.split("\n");
308
- buffer = lines.pop() ?? "";
309
-
310
- for (const line of lines) {
311
- if (!line.trim()) continue;
312
-
313
- const prefix = theme.bold(`[${p.name}]`);
314
-
315
- if (opts.level !== "all" || opts.json) {
316
- const entry = parseLogLine(line);
317
- if (entry !== null) {
318
- if (!matchesLevel(entry, opts.level)) continue;
319
- if (opts.json) {
320
- process.stdout.write(
321
- JSON.stringify({ worker: p.name, ...entry }) + "\n"
322
- );
323
- } else {
324
- process.stdout.write(`${prefix} ${formatLogLine(entry)}\n`);
325
- }
326
- } else if (opts.level === "all") {
327
- process.stdout.write(`${prefix} ${line}\n`);
328
- }
329
- } else {
330
- process.stdout.write(`${prefix} ${line}\n`);
331
- }
332
- }
333
- }
334
-
335
- // Flush remaining buffer
336
- if (buffer.trim()) {
337
- process.stdout.write(`${theme.bold(`[${p.name}]`)} ${buffer}\n`);
338
- }
339
-
340
- buffers.set(p.name, buffer);
341
- };
342
-
343
- if (opts.follow) {
344
- // Run all readers concurrently until interrupted
345
- await Promise.all(processes.map((p) => readFromWorker(p)));
346
- } else {
347
- // Show recent and exit after timeout
348
- const timeout = 5000;
349
- const timeoutPromise = new Promise<void>((resolve) =>
350
- setTimeout(resolve, timeout)
351
- );
352
-
353
- await Promise.race([
354
- Promise.all(processes.map((p) => readFromWorker(p))),
355
- timeoutPromise,
356
- ]);
357
- }
358
- } catch (err) {
359
- const message = err instanceof Error ? err.message : String(err);
360
- formatError(
361
- new CLIError(`Failed to tail workers: ${message}`, ExitCode.ERROR),
362
- fmt
363
- );
364
- process.exitCode = ExitCode.ERROR;
365
- } finally {
366
- // Kill all processes on cleanup
367
- for (const p of processes) {
368
- if (!p.proc.killed) {
369
- p.proc.kill();
370
- }
371
- }
372
- }
373
- }
374
-
375
- // ---------------------------------------------------------------------------
376
- // Command registration
377
- // ---------------------------------------------------------------------------
378
-
379
- /**
380
- * Register the `hoox logs` command group with subcommands:
381
- * worker <name> and all.
382
- */
383
- export function registerLogsCommand(program: Command): void {
384
- const logsCmd = program
385
- .command("logs")
386
- .description("Tail and view Cloudflare Worker logs in real-time");
387
-
388
- // -- logs worker <name> ---------------------------------------------------
389
- logsCmd
390
- .command("worker <name>")
391
- .description("Tail logs for a specific Cloudflare Worker")
392
- .option(
393
- "--level <level>",
394
- "Filter by log level (log, info, warn, error, debug, all)",
395
- "all"
396
- )
397
- .option("--follow", "Continuously stream logs (default)", true)
398
- .option("--no-follow", "Show recent logs and exit after 5 seconds")
399
- .option("--json", "Output logs in JSON format")
400
- .action(
401
- async (
402
- name: string,
403
- options: { level?: string; follow?: boolean; json?: boolean }
404
- ) => {
405
- const fmt = getFormatOptions(program);
406
- const workerOpts: WorkerLogOptions = {
407
- level: normalizeLevel(options.level ?? "all"),
408
- follow: options.follow !== false, // --no-follow sets follow to false
409
- json: Boolean(options.json),
410
- };
411
-
412
- try {
413
- await tailWorker(name, workerOpts, fmt);
414
- } catch (err) {
415
- const message = err instanceof Error ? err.message : String(err);
416
- formatError(message, fmt);
417
- process.exitCode = ExitCode.ERROR;
418
- }
419
- }
420
- );
421
-
422
- // -- logs all -------------------------------------------------------------
423
- logsCmd
424
- .command("all")
425
- .description("Tail logs from all enabled workers concurrently")
426
- .option(
427
- "--level <level>",
428
- "Filter by log level (log, info, warn, error, debug, all)",
429
- "all"
430
- )
431
- .option("--follow", "Continuously stream logs (default)", true)
432
- .option("--no-follow", "Show recent logs and exit after 5 seconds")
433
- .option("--json", "Output logs in JSON format")
434
- .action(
435
- async (options: { level?: string; follow?: boolean; json?: boolean }) => {
436
- const fmt = getFormatOptions(program);
437
- const workerOpts: WorkerLogOptions = {
438
- level: normalizeLevel(options.level ?? "all"),
439
- follow: options.follow !== false,
440
- json: Boolean(options.json),
441
- };
442
-
443
- try {
444
- await tailAllWorkers(workerOpts, fmt);
445
- } catch (err) {
446
- const message = err instanceof Error ? err.message : String(err);
447
- formatError(message, fmt);
448
- process.exitCode = ExitCode.ERROR;
449
- }
450
- }
451
- );
452
- }
@@ -1,2 +0,0 @@
1
- export { registerMonitorCommand } from "./monitor-command.js";
2
- export type { WorkerHealthResult, MonitorStatusResult } from "./types.js";