@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.
Files changed (114) hide show
  1. package/bin/hoox.js +3 -4
  2. package/dist/index.js +32989 -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 -1087
  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 -97
  9. package/src/commands/check/types.ts +0 -103
  10. package/src/commands/clone/clone-command.test.ts +0 -440
  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 -896
  15. package/src/commands/config/env-command.test.ts +0 -43
  16. package/src/commands/config/env-command.ts +0 -292
  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 -130
  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 -311
  25. package/src/commands/db/index.ts +0 -1
  26. package/src/commands/deploy/deploy-command.test.ts +0 -324
  27. package/src/commands/deploy/deploy-command.ts +0 -1054
  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 -405
  32. package/src/commands/dev/dev-command.ts +0 -398
  33. package/src/commands/dev/index.ts +0 -1
  34. package/src/commands/disclaimer/disclaimer-command.ts +0 -21
  35. package/src/commands/disclaimer/index.ts +0 -1
  36. package/src/commands/infra/index.ts +0 -5
  37. package/src/commands/infra/infra-command.test.ts +0 -719
  38. package/src/commands/infra/infra-command.ts +0 -974
  39. package/src/commands/infra/types.ts +0 -23
  40. package/src/commands/init/__tests__/cli-provisioner.test.ts +0 -17
  41. package/src/commands/init/cli-provisioner.ts +0 -71
  42. package/src/commands/init/index.ts +0 -1
  43. package/src/commands/init/init-command.test.ts +0 -854
  44. package/src/commands/init/init-command.ts +0 -597
  45. package/src/commands/init/types.ts +0 -23
  46. package/src/commands/logs/index.ts +0 -1
  47. package/src/commands/logs/logs-command.test.ts +0 -293
  48. package/src/commands/logs/logs-command.ts +0 -452
  49. package/src/commands/monitor/index.ts +0 -2
  50. package/src/commands/monitor/monitor-command.test.ts +0 -271
  51. package/src/commands/monitor/monitor-command.ts +0 -269
  52. package/src/commands/monitor/monitor-service.ts +0 -65
  53. package/src/commands/monitor/types.ts +0 -13
  54. package/src/commands/repair/index.ts +0 -2
  55. package/src/commands/repair/repair-command.test.ts +0 -243
  56. package/src/commands/repair/repair-command.ts +0 -220
  57. package/src/commands/repair/repair-service.ts +0 -164
  58. package/src/commands/repair/types.ts +0 -13
  59. package/src/commands/schema/index.ts +0 -1
  60. package/src/commands/schema/schema-command.ts +0 -137
  61. package/src/commands/test/index.ts +0 -2
  62. package/src/commands/test/test-command.test.ts +0 -318
  63. package/src/commands/test/test-command.ts +0 -406
  64. package/src/commands/tui/index.ts +0 -1
  65. package/src/commands/tui/tui-command.ts +0 -87
  66. package/src/commands/update/index.ts +0 -1
  67. package/src/commands/update/update-command.ts +0 -109
  68. package/src/commands/waf/index.ts +0 -2
  69. package/src/commands/waf/types.ts +0 -48
  70. package/src/commands/waf/waf-command.test.ts +0 -506
  71. package/src/commands/waf/waf-command.ts +0 -545
  72. package/src/index.ts +0 -261
  73. package/src/services/cloudflare/cloudflare-service.test.ts +0 -658
  74. package/src/services/cloudflare/cloudflare-service.ts +0 -433
  75. package/src/services/cloudflare/index.ts +0 -2
  76. package/src/services/cloudflare/types.ts +0 -30
  77. package/src/services/config/config-service.test.ts +0 -395
  78. package/src/services/config/config-service.ts +0 -202
  79. package/src/services/config/index.ts +0 -9
  80. package/src/services/config/types.ts +0 -66
  81. package/src/services/db/db-service.test.ts +0 -63
  82. package/src/services/db/db-service.ts +0 -131
  83. package/src/services/db/index.ts +0 -1
  84. package/src/services/docker/docker-service.ts +0 -155
  85. package/src/services/docker/index.ts +0 -1
  86. package/src/services/env/env-service.test.ts +0 -259
  87. package/src/services/env/env-service.ts +0 -372
  88. package/src/services/env/index.ts +0 -1
  89. package/src/services/kv/index.ts +0 -1
  90. package/src/services/kv/kv-sync-service.test.ts +0 -38
  91. package/src/services/kv/kv-sync-service.ts +0 -243
  92. package/src/services/prerequisites/index.ts +0 -1
  93. package/src/services/prerequisites/prerequisites-service.test.ts +0 -89
  94. package/src/services/prerequisites/prerequisites-service.ts +0 -377
  95. package/src/services/prerequisites/types.ts +0 -48
  96. package/src/services/schema/index.ts +0 -1
  97. package/src/services/schema/schema-service.ts +0 -99
  98. package/src/services/secrets/index.ts +0 -11
  99. package/src/services/secrets/secrets-service.test.ts +0 -502
  100. package/src/services/secrets/secrets-service.ts +0 -287
  101. package/src/services/secrets/types.ts +0 -50
  102. package/src/services/update/index.ts +0 -2
  103. package/src/services/update/update-service.test.ts +0 -76
  104. package/src/services/update/update-service.ts +0 -193
  105. package/src/ui/banner.ts +0 -141
  106. package/src/ui/index.ts +0 -8
  107. package/src/ui/menu.ts +0 -490
  108. package/src/utils/error-handler.ts +0 -62
  109. package/src/utils/errors.test.ts +0 -69
  110. package/src/utils/errors.ts +0 -24
  111. package/src/utils/formatters.test.ts +0 -180
  112. package/src/utils/formatters.ts +0 -269
  113. package/src/utils/git.ts +0 -134
  114. package/src/utils/theme.ts +0 -92
@@ -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";