@jango-blockchained/hoox-cli 0.5.1 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/hoox.js +3 -4
- package/dist/index.js +32545 -0
- package/package.json +10 -15
- package/src/commands/check/check-command.test.ts +0 -476
- package/src/commands/check/check-command.ts +0 -1144
- package/src/commands/check/index.ts +0 -10
- package/src/commands/check/prerequisites-command.test.ts +0 -19
- package/src/commands/check/prerequisites-command.ts +0 -96
- package/src/commands/check/types.ts +0 -103
- package/src/commands/clone/clone-command.test.ts +0 -442
- package/src/commands/clone/clone-command.ts +0 -431
- package/src/commands/clone/index.ts +0 -1
- package/src/commands/config/config-command.test.ts +0 -583
- package/src/commands/config/config-command.ts +0 -897
- package/src/commands/config/env-command.test.ts +0 -43
- package/src/commands/config/env-command.ts +0 -295
- package/src/commands/config/index.ts +0 -3
- package/src/commands/config/kv-command.test.ts +0 -14
- package/src/commands/config/kv-command.ts +0 -290
- package/src/commands/dashboard/dashboard-command.test.ts +0 -47
- package/src/commands/dashboard/dashboard-command.ts +0 -128
- package/src/commands/dashboard/index.ts +0 -1
- package/src/commands/db/db-command.test.ts +0 -21
- package/src/commands/db/db-command.ts +0 -312
- package/src/commands/db/index.ts +0 -1
- package/src/commands/deploy/deploy-command.test.ts +0 -304
- package/src/commands/deploy/deploy-command.ts +0 -1135
- package/src/commands/deploy/index.ts +0 -2
- package/src/commands/deploy/telegram-service.ts +0 -76
- package/src/commands/deploy/types.ts +0 -34
- package/src/commands/dev/dev-command.test.ts +0 -383
- package/src/commands/dev/dev-command.ts +0 -398
- package/src/commands/dev/index.ts +0 -1
- package/src/commands/infra/index.ts +0 -5
- package/src/commands/infra/infra-command.test.ts +0 -719
- package/src/commands/infra/infra-command.ts +0 -974
- package/src/commands/infra/types.ts +0 -23
- package/src/commands/init/__tests__/cli-provisioner.test.ts +0 -17
- package/src/commands/init/cli-provisioner.ts +0 -71
- package/src/commands/init/index.ts +0 -1
- package/src/commands/init/init-command.test.ts +0 -876
- package/src/commands/init/init-command.ts +0 -593
- package/src/commands/init/types.ts +0 -23
- package/src/commands/logs/index.ts +0 -1
- package/src/commands/logs/logs-command.test.ts +0 -294
- package/src/commands/logs/logs-command.ts +0 -452
- package/src/commands/monitor/index.ts +0 -2
- package/src/commands/monitor/monitor-command.test.ts +0 -263
- package/src/commands/monitor/monitor-command.ts +0 -269
- package/src/commands/monitor/monitor-service.ts +0 -65
- package/src/commands/monitor/types.ts +0 -13
- package/src/commands/repair/index.ts +0 -2
- package/src/commands/repair/repair-command.test.ts +0 -224
- package/src/commands/repair/repair-command.ts +0 -220
- package/src/commands/repair/repair-service.ts +0 -138
- package/src/commands/repair/types.ts +0 -13
- package/src/commands/test/index.ts +0 -2
- package/src/commands/test/test-command.test.ts +0 -319
- package/src/commands/test/test-command.ts +0 -406
- package/src/commands/tui/index.ts +0 -1
- package/src/commands/tui/tui-command.ts +0 -87
- package/src/commands/update/index.ts +0 -1
- package/src/commands/update/update-command.ts +0 -168
- package/src/commands/waf/index.ts +0 -2
- package/src/commands/waf/types.ts +0 -48
- package/src/commands/waf/waf-command.test.ts +0 -506
- package/src/commands/waf/waf-command.ts +0 -548
- package/src/index.ts +0 -269
- package/src/services/cloudflare/cloudflare-service.test.ts +0 -658
- package/src/services/cloudflare/cloudflare-service.ts +0 -433
- package/src/services/cloudflare/index.ts +0 -2
- package/src/services/cloudflare/types.ts +0 -30
- package/src/services/config/config-service.test.ts +0 -395
- package/src/services/config/config-service.ts +0 -207
- package/src/services/config/index.ts +0 -9
- package/src/services/config/types.ts +0 -66
- package/src/services/db/db-service.test.ts +0 -51
- package/src/services/db/db-service.ts +0 -131
- package/src/services/db/index.ts +0 -1
- package/src/services/docker/docker-service.ts +0 -155
- package/src/services/docker/index.ts +0 -1
- package/src/services/env/env-service.test.ts +0 -236
- package/src/services/env/env-service.ts +0 -386
- package/src/services/env/index.ts +0 -1
- package/src/services/kv/index.ts +0 -1
- package/src/services/kv/kv-sync-service.test.ts +0 -38
- package/src/services/kv/kv-sync-service.ts +0 -234
- package/src/services/prerequisites/index.ts +0 -1
- package/src/services/prerequisites/prerequisites-service.test.ts +0 -89
- package/src/services/prerequisites/prerequisites-service.ts +0 -377
- package/src/services/prerequisites/types.ts +0 -48
- package/src/services/secrets/index.ts +0 -11
- package/src/services/secrets/secrets-service.test.ts +0 -495
- package/src/services/secrets/secrets-service.ts +0 -287
- package/src/services/secrets/types.ts +0 -50
- package/src/services/update/index.ts +0 -2
- package/src/services/update/update-service.test.ts +0 -76
- package/src/services/update/update-service.ts +0 -193
- package/src/ui/banner.ts +0 -141
- package/src/ui/index.ts +0 -8
- package/src/ui/menu.ts +0 -490
- package/src/utils/errors.test.ts +0 -69
- package/src/utils/errors.ts +0 -23
- package/src/utils/formatters.test.ts +0 -180
- package/src/utils/formatters.ts +0 -269
- package/src/utils/theme.ts +0 -94
|
@@ -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
|
-
}
|