@hasna/logs 0.3.3 → 0.3.5
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/dist/cli/index.js +59 -22
- package/package.json +1 -1
- package/src/cli/index.ts +50 -23
package/dist/cli/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
ingestLog,
|
|
11
11
|
listPages,
|
|
12
12
|
listProjects,
|
|
13
|
+
resolveProjectId,
|
|
13
14
|
summarizeLogs
|
|
14
15
|
} from "../index-5tvnhvgr.js";
|
|
15
16
|
import {
|
|
@@ -2136,12 +2137,48 @@ var {
|
|
|
2136
2137
|
} = import__.default;
|
|
2137
2138
|
|
|
2138
2139
|
// src/cli/index.ts
|
|
2140
|
+
var C = {
|
|
2141
|
+
reset: "\x1B[0m",
|
|
2142
|
+
bold: "\x1B[1m",
|
|
2143
|
+
dim: "\x1B[2m",
|
|
2144
|
+
red: "\x1B[31m",
|
|
2145
|
+
yellow: "\x1B[33m",
|
|
2146
|
+
cyan: "\x1B[36m",
|
|
2147
|
+
gray: "\x1B[90m",
|
|
2148
|
+
bgRed: "\x1B[41m\x1B[97m",
|
|
2149
|
+
magenta: "\x1B[35m"
|
|
2150
|
+
};
|
|
2151
|
+
var LEVEL_COLOR = {
|
|
2152
|
+
fatal: C.bgRed,
|
|
2153
|
+
error: C.red,
|
|
2154
|
+
warn: C.yellow,
|
|
2155
|
+
info: "",
|
|
2156
|
+
debug: C.gray
|
|
2157
|
+
};
|
|
2158
|
+
function colorRow(ts, level, svc, msg) {
|
|
2159
|
+
const lc = LEVEL_COLOR[level.toLowerCase()] ?? "";
|
|
2160
|
+
const isTTY = process.stdout.isTTY;
|
|
2161
|
+
if (!isTTY)
|
|
2162
|
+
return `${ts} ${pad(level.toUpperCase(), 5)} ${pad(svc, 12)} ${msg}`;
|
|
2163
|
+
return `${C.dim}${ts}${C.reset} ${lc}${C.bold}${pad(level.toUpperCase(), 5)}${C.reset} ${C.cyan}${pad(svc, 12)}${C.reset} ${msg}`;
|
|
2164
|
+
}
|
|
2165
|
+
function colorLevel(level) {
|
|
2166
|
+
if (!process.stdout.isTTY)
|
|
2167
|
+
return pad(level.toUpperCase(), 5);
|
|
2168
|
+
const lc = LEVEL_COLOR[level.toLowerCase()] ?? "";
|
|
2169
|
+
return `${lc}${C.bold}${pad(level.toUpperCase(), 5)}${C.reset}`;
|
|
2170
|
+
}
|
|
2171
|
+
function resolveProject(nameOrId) {
|
|
2172
|
+
if (!nameOrId)
|
|
2173
|
+
return;
|
|
2174
|
+
return resolveProjectId(getDb(), nameOrId) ?? nameOrId;
|
|
2175
|
+
}
|
|
2139
2176
|
var program2 = new Command().name("logs").description("@hasna/logs \u2014 log aggregation and monitoring").version("0.0.1");
|
|
2140
|
-
program2.command("list").description("Search and list logs").option("--project <id>", "Filter by project ID").option("--page <id>", "Filter by page ID").option("--level <levels>", "Comma-separated levels (error,warn,info,debug,fatal)").option("--service <name>", "Filter by service").option("--since <iso>", "Since timestamp or relative (1h, 24h, 7d)").option("--text <query>", "Full-text search").option("--limit <n>", "Max results", "100").option("--format <fmt>", "Output format: table|json|compact", "table").action((opts) => {
|
|
2177
|
+
program2.command("list").description("Search and list logs").option("--project <name|id>", "Filter by project name or ID").option("--page <id>", "Filter by page ID").option("--level <levels>", "Comma-separated levels (error,warn,info,debug,fatal)").option("--service <name>", "Filter by service").option("--since <iso>", "Since timestamp or relative (1h, 24h, 7d)").option("--text <query>", "Full-text search").option("--limit <n>", "Max results", "100").option("--format <fmt>", "Output format: table|json|compact", "table").action((opts) => {
|
|
2141
2178
|
const db = getDb();
|
|
2142
2179
|
const since = parseRelativeTime(opts.since);
|
|
2143
2180
|
const rows = searchLogs(db, {
|
|
2144
|
-
project_id: opts.project,
|
|
2181
|
+
project_id: resolveProject(opts.project),
|
|
2145
2182
|
page_id: opts.page,
|
|
2146
2183
|
level: opts.level ? opts.level.split(",") : undefined,
|
|
2147
2184
|
service: opts.service,
|
|
@@ -2160,27 +2197,27 @@ program2.command("list").description("Search and list logs").option("--project <
|
|
|
2160
2197
|
}
|
|
2161
2198
|
for (const r of rows) {
|
|
2162
2199
|
const meta = r.metadata ? ` ${r.metadata}` : "";
|
|
2163
|
-
console.log(`${r.timestamp
|
|
2200
|
+
console.log(`${colorRow(r.timestamp, r.level, r.service ?? "-", r.message)}${meta}`);
|
|
2164
2201
|
}
|
|
2165
2202
|
console.log(`
|
|
2166
2203
|
${rows.length} log(s)`);
|
|
2167
2204
|
});
|
|
2168
|
-
program2.command("tail").description("Show most recent logs").option("--project <id>").option("--n <count>", "Number of logs", "50").action((opts) => {
|
|
2169
|
-
const rows = tailLogs(getDb(), opts.project, Number(opts.n));
|
|
2205
|
+
program2.command("tail").description("Show most recent logs").option("--project <name|id>", "Project name or ID").option("--n <count>", "Number of logs", "50").action((opts) => {
|
|
2206
|
+
const rows = tailLogs(getDb(), resolveProject(opts.project), Number(opts.n));
|
|
2170
2207
|
for (const r of rows)
|
|
2171
|
-
console.log(
|
|
2208
|
+
console.log(colorRow(r.timestamp, r.level, r.service ?? "-", r.message));
|
|
2172
2209
|
});
|
|
2173
|
-
program2.command("summary").description("Error/warn summary by service").option("--project <id>").option("--since <time>", "Relative time (1h, 24h, 7d)", "24h").action((opts) => {
|
|
2174
|
-
const summary = summarizeLogs(getDb(), opts.project, parseRelativeTime(opts.since));
|
|
2210
|
+
program2.command("summary").description("Error/warn summary by service").option("--project <name|id>", "Project name or ID").option("--since <time>", "Relative time (1h, 24h, 7d)", "24h").action((opts) => {
|
|
2211
|
+
const summary = summarizeLogs(getDb(), resolveProject(opts.project), parseRelativeTime(opts.since));
|
|
2175
2212
|
if (!summary.length) {
|
|
2176
2213
|
console.log("No errors/warnings in this window.");
|
|
2177
2214
|
return;
|
|
2178
2215
|
}
|
|
2179
2216
|
for (const s of summary)
|
|
2180
|
-
console.log(`${
|
|
2217
|
+
console.log(`${colorLevel(s.level)} ${C.cyan}${pad(s.service ?? "-", 15)}${C.reset} count=${s.count} latest=${s.latest}`);
|
|
2181
2218
|
});
|
|
2182
|
-
program2.command("push <message>").description("Push a log entry").option("--level <level>", "Log level", "info").option("--service <name>").option("--project <id>").option("--trace <id>", "Trace ID").action((message, opts) => {
|
|
2183
|
-
const row = ingestLog(getDb(), { level: opts.level, message, service: opts.service, project_id: opts.project, trace_id: opts.trace });
|
|
2219
|
+
program2.command("push <message>").description("Push a log entry").option("--level <level>", "Log level", "info").option("--service <name>").option("--project <name|id>", "Project name or ID").option("--trace <id>", "Trace ID").action((message, opts) => {
|
|
2220
|
+
const row = ingestLog(getDb(), { level: opts.level, message, service: opts.service, project_id: resolveProject(opts.project), trace_id: opts.trace });
|
|
2184
2221
|
console.log(`Logged: ${row.id}`);
|
|
2185
2222
|
});
|
|
2186
2223
|
var projectCmd = program2.command("project").description("Manage projects");
|
|
@@ -2198,38 +2235,38 @@ projectCmd.command("list").action(() => {
|
|
|
2198
2235
|
console.log(`${p.id} ${p.name} ${p.base_url ?? ""} ${p.github_repo ?? ""}`);
|
|
2199
2236
|
});
|
|
2200
2237
|
var pageCmd = program2.command("page").description("Manage pages");
|
|
2201
|
-
pageCmd.command("add").option("--project <id>").option("--url <url>").option("--name <name>").action((opts) => {
|
|
2238
|
+
pageCmd.command("add").option("--project <name|id>", "Project name or ID").option("--url <url>").option("--name <name>").action((opts) => {
|
|
2202
2239
|
if (!opts.project || !opts.url) {
|
|
2203
2240
|
console.error("--project and --url required");
|
|
2204
2241
|
process.exit(1);
|
|
2205
2242
|
}
|
|
2206
|
-
const p = createPage(getDb(), { project_id: opts.project, url: opts.url, name: opts.name });
|
|
2243
|
+
const p = createPage(getDb(), { project_id: resolveProject(opts.project), url: opts.url, name: opts.name });
|
|
2207
2244
|
console.log(`Page registered: ${p.id} \u2014 ${p.url}`);
|
|
2208
2245
|
});
|
|
2209
|
-
pageCmd.command("list").option("--project <id>").action((opts) => {
|
|
2246
|
+
pageCmd.command("list").option("--project <name|id>", "Project name or ID").action((opts) => {
|
|
2210
2247
|
if (!opts.project) {
|
|
2211
2248
|
console.error("--project required");
|
|
2212
2249
|
process.exit(1);
|
|
2213
2250
|
}
|
|
2214
|
-
const pages = listPages(getDb(), opts.project);
|
|
2251
|
+
const pages = listPages(getDb(), resolveProject(opts.project));
|
|
2215
2252
|
for (const p of pages)
|
|
2216
2253
|
console.log(`${p.id} ${p.url} last=${p.last_scanned_at ?? "never"}`);
|
|
2217
2254
|
});
|
|
2218
2255
|
var jobCmd = program2.command("job").description("Manage scan jobs");
|
|
2219
|
-
jobCmd.command("create").option("--project <id>").option("--schedule <cron>", "Cron expression", "*/30 * * * *").action((opts) => {
|
|
2256
|
+
jobCmd.command("create").option("--project <name|id>", "Project name or ID").option("--schedule <cron>", "Cron expression", "*/30 * * * *").action((opts) => {
|
|
2220
2257
|
if (!opts.project) {
|
|
2221
2258
|
console.error("--project required");
|
|
2222
2259
|
process.exit(1);
|
|
2223
2260
|
}
|
|
2224
|
-
const j = createJob(getDb(), { project_id: opts.project, schedule: opts.schedule });
|
|
2261
|
+
const j = createJob(getDb(), { project_id: resolveProject(opts.project), schedule: opts.schedule });
|
|
2225
2262
|
console.log(`Job created: ${j.id} \u2014 ${j.schedule}`);
|
|
2226
2263
|
});
|
|
2227
|
-
jobCmd.command("list").option("--project <id>").action((opts) => {
|
|
2228
|
-
const jobs = listJobs(getDb(), opts.project);
|
|
2264
|
+
jobCmd.command("list").option("--project <name|id>", "Project name or ID").action((opts) => {
|
|
2265
|
+
const jobs = listJobs(getDb(), resolveProject(opts.project));
|
|
2229
2266
|
for (const j of jobs)
|
|
2230
2267
|
console.log(`${j.id} ${j.schedule} enabled=${j.enabled} last=${j.last_run_at ?? "never"}`);
|
|
2231
2268
|
});
|
|
2232
|
-
program2.command("scan").description("Run an immediate scan for a job").option("--job <id>").option("--project <id>").action(async (opts) => {
|
|
2269
|
+
program2.command("scan").description("Run an immediate scan for a job").option("--job <id>").option("--project <name|id>", "Project name or ID").action(async (opts) => {
|
|
2233
2270
|
if (!opts.job) {
|
|
2234
2271
|
console.error("--job required");
|
|
2235
2272
|
process.exit(1);
|
|
@@ -2302,12 +2339,12 @@ Errors: ${errorCount} Warnings: ${warnCount}`);
|
|
|
2302
2339
|
process.exit(0);
|
|
2303
2340
|
});
|
|
2304
2341
|
});
|
|
2305
|
-
program2.command("export").description("Export logs to JSON or CSV").option("--project <id>").option("--since <time>", "Relative time or ISO").option("--level <level>").option("--service <name>").option("--format <fmt>", "json or csv", "json").option("--output <file>", "Output file (default: stdout)").option("--limit <n>", "Max rows", "100000").action(async (opts) => {
|
|
2342
|
+
program2.command("export").description("Export logs to JSON or CSV").option("--project <name|id>", "Project name or ID").option("--since <time>", "Relative time or ISO").option("--level <level>").option("--service <name>").option("--format <fmt>", "json or csv", "json").option("--output <file>", "Output file (default: stdout)").option("--limit <n>", "Max rows", "100000").action(async (opts) => {
|
|
2306
2343
|
const { exportToCsv, exportToJson } = await import("../export-c3eqjste.js");
|
|
2307
2344
|
const { createWriteStream } = await import("fs");
|
|
2308
2345
|
const db = getDb();
|
|
2309
2346
|
const options = {
|
|
2310
|
-
project_id: opts.project,
|
|
2347
|
+
project_id: resolveProject(opts.project),
|
|
2311
2348
|
since: parseRelativeTime(opts.since),
|
|
2312
2349
|
level: opts.level,
|
|
2313
2350
|
service: opts.service,
|
package/package.json
CHANGED
package/src/cli/index.ts
CHANGED
|
@@ -5,10 +5,37 @@ import { ingestLog } from "../lib/ingest.ts"
|
|
|
5
5
|
import { searchLogs, tailLogs } from "../lib/query.ts"
|
|
6
6
|
import { summarizeLogs } from "../lib/summarize.ts"
|
|
7
7
|
import { createJob, listJobs } from "../lib/jobs.ts"
|
|
8
|
-
import { createPage, createProject, listPages, listProjects } from "../lib/projects.ts"
|
|
8
|
+
import { createPage, createProject, listPages, listProjects, resolveProjectId } from "../lib/projects.ts"
|
|
9
9
|
import { runJob } from "../lib/scheduler.ts"
|
|
10
10
|
import type { LogLevel } from "../types/index.ts"
|
|
11
11
|
|
|
12
|
+
// ── Color helpers ──────────────────────────────────────────
|
|
13
|
+
const C = {
|
|
14
|
+
reset: "\x1b[0m", bold: "\x1b[1m", dim: "\x1b[2m",
|
|
15
|
+
red: "\x1b[31m", yellow: "\x1b[33m", cyan: "\x1b[36m", gray: "\x1b[90m",
|
|
16
|
+
bgRed: "\x1b[41m\x1b[97m", magenta: "\x1b[35m",
|
|
17
|
+
};
|
|
18
|
+
const LEVEL_COLOR: Record<string, string> = {
|
|
19
|
+
fatal: C.bgRed, error: C.red, warn: C.yellow, info: "", debug: C.gray,
|
|
20
|
+
};
|
|
21
|
+
function colorRow(ts: string, level: string, svc: string, msg: string): string {
|
|
22
|
+
const lc = LEVEL_COLOR[level.toLowerCase()] ?? "";
|
|
23
|
+
const isTTY = process.stdout.isTTY;
|
|
24
|
+
if (!isTTY) return `${ts} ${pad(level.toUpperCase(), 5)} ${pad(svc, 12)} ${msg}`;
|
|
25
|
+
return `${C.dim}${ts}${C.reset} ${lc}${C.bold}${pad(level.toUpperCase(), 5)}${C.reset} ${C.cyan}${pad(svc, 12)}${C.reset} ${msg}`;
|
|
26
|
+
}
|
|
27
|
+
function colorLevel(level: string): string {
|
|
28
|
+
if (!process.stdout.isTTY) return pad(level.toUpperCase(), 5);
|
|
29
|
+
const lc = LEVEL_COLOR[level.toLowerCase()] ?? "";
|
|
30
|
+
return `${lc}${C.bold}${pad(level.toUpperCase(), 5)}${C.reset}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Resolve a project name or ID from CLI --project flag */
|
|
34
|
+
function resolveProject(nameOrId: string | undefined): string | undefined {
|
|
35
|
+
if (!nameOrId) return undefined;
|
|
36
|
+
return resolveProjectId(getDb(), nameOrId) ?? nameOrId;
|
|
37
|
+
}
|
|
38
|
+
|
|
12
39
|
const program = new Command()
|
|
13
40
|
.name("logs")
|
|
14
41
|
.description("@hasna/logs — log aggregation and monitoring")
|
|
@@ -17,7 +44,7 @@ const program = new Command()
|
|
|
17
44
|
// ── logs list ──────────────────────────────────────────────
|
|
18
45
|
program.command("list")
|
|
19
46
|
.description("Search and list logs")
|
|
20
|
-
.option("--project <id>", "Filter by project ID")
|
|
47
|
+
.option("--project <name|id>", "Filter by project name or ID")
|
|
21
48
|
.option("--page <id>", "Filter by page ID")
|
|
22
49
|
.option("--level <levels>", "Comma-separated levels (error,warn,info,debug,fatal)")
|
|
23
50
|
.option("--service <name>", "Filter by service")
|
|
@@ -29,7 +56,7 @@ program.command("list")
|
|
|
29
56
|
const db = getDb()
|
|
30
57
|
const since = parseRelativeTime(opts.since)
|
|
31
58
|
const rows = searchLogs(db, {
|
|
32
|
-
project_id: opts.project,
|
|
59
|
+
project_id: resolveProject(opts.project),
|
|
33
60
|
page_id: opts.page,
|
|
34
61
|
level: opts.level ? (opts.level.split(",") as LogLevel[]) : undefined,
|
|
35
62
|
service: opts.service,
|
|
@@ -44,7 +71,7 @@ program.command("list")
|
|
|
44
71
|
}
|
|
45
72
|
for (const r of rows) {
|
|
46
73
|
const meta = r.metadata ? ` ${r.metadata}` : ""
|
|
47
|
-
console.log(`${r.timestamp
|
|
74
|
+
console.log(`${colorRow(r.timestamp, r.level, r.service ?? "-", r.message)}${meta}`)
|
|
48
75
|
}
|
|
49
76
|
console.log(`\n${rows.length} log(s)`)
|
|
50
77
|
})
|
|
@@ -52,22 +79,22 @@ program.command("list")
|
|
|
52
79
|
// ── logs tail ──────────────────────────────────────────────
|
|
53
80
|
program.command("tail")
|
|
54
81
|
.description("Show most recent logs")
|
|
55
|
-
.option("--project <id>")
|
|
82
|
+
.option("--project <name|id>", "Project name or ID")
|
|
56
83
|
.option("--n <count>", "Number of logs", "50")
|
|
57
84
|
.action((opts) => {
|
|
58
|
-
const rows = tailLogs(getDb(), opts.project, Number(opts.n))
|
|
59
|
-
for (const r of rows) console.log(
|
|
85
|
+
const rows = tailLogs(getDb(), resolveProject(opts.project), Number(opts.n))
|
|
86
|
+
for (const r of rows) console.log(colorRow(r.timestamp, r.level, r.service ?? "-", r.message))
|
|
60
87
|
})
|
|
61
88
|
|
|
62
89
|
// ── logs summary ──────────────────────────────────────────
|
|
63
90
|
program.command("summary")
|
|
64
91
|
.description("Error/warn summary by service")
|
|
65
|
-
.option("--project <id>")
|
|
92
|
+
.option("--project <name|id>", "Project name or ID")
|
|
66
93
|
.option("--since <time>", "Relative time (1h, 24h, 7d)", "24h")
|
|
67
94
|
.action((opts) => {
|
|
68
|
-
const summary = summarizeLogs(getDb(), opts.project, parseRelativeTime(opts.since))
|
|
95
|
+
const summary = summarizeLogs(getDb(), resolveProject(opts.project), parseRelativeTime(opts.since))
|
|
69
96
|
if (!summary.length) { console.log("No errors/warnings in this window."); return }
|
|
70
|
-
for (const s of summary) console.log(`${
|
|
97
|
+
for (const s of summary) console.log(`${colorLevel(s.level)} ${C.cyan}${pad(s.service ?? "-", 15)}${C.reset} count=${s.count} latest=${s.latest}`)
|
|
71
98
|
})
|
|
72
99
|
|
|
73
100
|
// ── logs push ─────────────────────────────────────────────
|
|
@@ -75,10 +102,10 @@ program.command("push <message>")
|
|
|
75
102
|
.description("Push a log entry")
|
|
76
103
|
.option("--level <level>", "Log level", "info")
|
|
77
104
|
.option("--service <name>")
|
|
78
|
-
.option("--project <id>")
|
|
105
|
+
.option("--project <name|id>", "Project name or ID")
|
|
79
106
|
.option("--trace <id>", "Trace ID")
|
|
80
107
|
.action((message, opts) => {
|
|
81
|
-
const row = ingestLog(getDb(), { level: opts.level as LogLevel, message, service: opts.service, project_id: opts.project, trace_id: opts.trace })
|
|
108
|
+
const row = ingestLog(getDb(), { level: opts.level as LogLevel, message, service: opts.service, project_id: resolveProject(opts.project), trace_id: opts.trace })
|
|
82
109
|
console.log(`Logged: ${row.id}`)
|
|
83
110
|
})
|
|
84
111
|
|
|
@@ -104,18 +131,18 @@ projectCmd.command("list").action(() => {
|
|
|
104
131
|
const pageCmd = program.command("page").description("Manage pages")
|
|
105
132
|
|
|
106
133
|
pageCmd.command("add")
|
|
107
|
-
.option("--project <id>")
|
|
134
|
+
.option("--project <name|id>", "Project name or ID")
|
|
108
135
|
.option("--url <url>")
|
|
109
136
|
.option("--name <name>")
|
|
110
137
|
.action((opts) => {
|
|
111
138
|
if (!opts.project || !opts.url) { console.error("--project and --url required"); process.exit(1) }
|
|
112
|
-
const p = createPage(getDb(), { project_id: opts.project, url: opts.url, name: opts.name })
|
|
139
|
+
const p = createPage(getDb(), { project_id: resolveProject(opts.project), url: opts.url, name: opts.name })
|
|
113
140
|
console.log(`Page registered: ${p.id} — ${p.url}`)
|
|
114
141
|
})
|
|
115
142
|
|
|
116
|
-
pageCmd.command("list").option("--project <id>").action((opts) => {
|
|
143
|
+
pageCmd.command("list").option("--project <name|id>", "Project name or ID").action((opts) => {
|
|
117
144
|
if (!opts.project) { console.error("--project required"); process.exit(1) }
|
|
118
|
-
const pages = listPages(getDb(), opts.project)
|
|
145
|
+
const pages = listPages(getDb(), resolveProject(opts.project))
|
|
119
146
|
for (const p of pages) console.log(`${p.id} ${p.url} last=${p.last_scanned_at ?? "never"}`)
|
|
120
147
|
})
|
|
121
148
|
|
|
@@ -123,16 +150,16 @@ pageCmd.command("list").option("--project <id>").action((opts) => {
|
|
|
123
150
|
const jobCmd = program.command("job").description("Manage scan jobs")
|
|
124
151
|
|
|
125
152
|
jobCmd.command("create")
|
|
126
|
-
.option("--project <id>")
|
|
153
|
+
.option("--project <name|id>", "Project name or ID")
|
|
127
154
|
.option("--schedule <cron>", "Cron expression", "*/30 * * * *")
|
|
128
155
|
.action((opts) => {
|
|
129
156
|
if (!opts.project) { console.error("--project required"); process.exit(1) }
|
|
130
|
-
const j = createJob(getDb(), { project_id: opts.project, schedule: opts.schedule })
|
|
157
|
+
const j = createJob(getDb(), { project_id: resolveProject(opts.project), schedule: opts.schedule })
|
|
131
158
|
console.log(`Job created: ${j.id} — ${j.schedule}`)
|
|
132
159
|
})
|
|
133
160
|
|
|
134
|
-
jobCmd.command("list").option("--project <id>").action((opts) => {
|
|
135
|
-
const jobs = listJobs(getDb(), opts.project)
|
|
161
|
+
jobCmd.command("list").option("--project <name|id>", "Project name or ID").action((opts) => {
|
|
162
|
+
const jobs = listJobs(getDb(), resolveProject(opts.project))
|
|
136
163
|
for (const j of jobs) console.log(`${j.id} ${j.schedule} enabled=${j.enabled} last=${j.last_run_at ?? "never"}`)
|
|
137
164
|
})
|
|
138
165
|
|
|
@@ -140,7 +167,7 @@ jobCmd.command("list").option("--project <id>").action((opts) => {
|
|
|
140
167
|
program.command("scan")
|
|
141
168
|
.description("Run an immediate scan for a job")
|
|
142
169
|
.option("--job <id>")
|
|
143
|
-
.option("--project <id>")
|
|
170
|
+
.option("--project <name|id>", "Project name or ID")
|
|
144
171
|
.action(async (opts) => {
|
|
145
172
|
if (!opts.job) { console.error("--job required"); process.exit(1) }
|
|
146
173
|
const db = getDb()
|
|
@@ -216,7 +243,7 @@ program.command("watch")
|
|
|
216
243
|
// ── logs export ───────────────────────────────────────────
|
|
217
244
|
program.command("export")
|
|
218
245
|
.description("Export logs to JSON or CSV")
|
|
219
|
-
.option("--project <id>")
|
|
246
|
+
.option("--project <name|id>", "Project name or ID")
|
|
220
247
|
.option("--since <time>", "Relative time or ISO")
|
|
221
248
|
.option("--level <level>")
|
|
222
249
|
.option("--service <name>")
|
|
@@ -228,7 +255,7 @@ program.command("export")
|
|
|
228
255
|
const { createWriteStream } = await import("node:fs")
|
|
229
256
|
const db = getDb()
|
|
230
257
|
const options = {
|
|
231
|
-
project_id: opts.project,
|
|
258
|
+
project_id: resolveProject(opts.project),
|
|
232
259
|
since: parseRelativeTime(opts.since),
|
|
233
260
|
level: opts.level,
|
|
234
261
|
service: opts.service,
|