runspec-node 0.24.0 → 0.25.0
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.d.ts.map +1 -1
- package/dist/cli.js +87 -0
- package/dist/cli.js.map +1 -1
- package/dist/logging_setup.d.ts +10 -1
- package/dist/logging_setup.d.ts.map +1 -1
- package/dist/logging_setup.js +86 -3
- package/dist/logging_setup.js.map +1 -1
- package/dist/logs.d.ts +85 -0
- package/dist/logs.d.ts.map +1 -0
- package/dist/logs.js +504 -0
- package/dist/logs.js.map +1 -0
- package/package.json +1 -1
- package/src/cli.ts +86 -0
- package/src/logging_setup.ts +88 -4
- package/src/logs.ts +507 -0
- package/tests/test_logs.test.ts +209 -0
- package/tests/test_print_capture.test.ts +82 -0
package/dist/logs.js
ADDED
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* runspec logs — view / status / prune / compact per-invocation audit logs.
|
|
4
|
+
*
|
|
5
|
+
* The Node port of the Python `runspec/logs.py` engine. The per-run store
|
|
6
|
+
* (`[config.logging] store = "per-run"`) writes one
|
|
7
|
+
* `{runnable}.{utc-ts}.{run_id}.log` per invocation with no in-process rotation;
|
|
8
|
+
* this is the read + maintenance side. Pure functions (collectRecords,
|
|
9
|
+
* inventory, planPrune, planCompact) are kept separate from I/O so they can be
|
|
10
|
+
* unit-tested without a filesystem fixture.
|
|
11
|
+
*/
|
|
12
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
+
}
|
|
18
|
+
Object.defineProperty(o, k2, desc);
|
|
19
|
+
}) : (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
o[k2] = m[k];
|
|
22
|
+
}));
|
|
23
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
+
}) : function(o, v) {
|
|
26
|
+
o["default"] = v;
|
|
27
|
+
});
|
|
28
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
29
|
+
var ownKeys = function(o) {
|
|
30
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
31
|
+
var ar = [];
|
|
32
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
33
|
+
return ar;
|
|
34
|
+
};
|
|
35
|
+
return ownKeys(o);
|
|
36
|
+
};
|
|
37
|
+
return function (mod) {
|
|
38
|
+
if (mod && mod.__esModule) return mod;
|
|
39
|
+
var result = {};
|
|
40
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41
|
+
__setModuleDefault(result, mod);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
})();
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.parseDuration = parseDuration;
|
|
47
|
+
exports.parseSize = parseSize;
|
|
48
|
+
exports.logDirs = logDirs;
|
|
49
|
+
exports.discover = discover;
|
|
50
|
+
exports.isManaged = isManaged;
|
|
51
|
+
exports.collectRecords = collectRecords;
|
|
52
|
+
exports.view = view;
|
|
53
|
+
exports.inventory = inventory;
|
|
54
|
+
exports.status = status;
|
|
55
|
+
exports.planPrune = planPrune;
|
|
56
|
+
exports.prune = prune;
|
|
57
|
+
exports.planCompact = planCompact;
|
|
58
|
+
exports.compact = compact;
|
|
59
|
+
const fs = __importStar(require("fs"));
|
|
60
|
+
const path = __importStar(require("path"));
|
|
61
|
+
const os = __importStar(require("os"));
|
|
62
|
+
const zlib = __importStar(require("zlib"));
|
|
63
|
+
const finder_1 = require("./finder");
|
|
64
|
+
const logging_setup_1 = require("./logging_setup");
|
|
65
|
+
// Filename grammar (under {logs}/), matching the Node + Python write side:
|
|
66
|
+
// {runnable}.log single-mode active file
|
|
67
|
+
// {runnable}.{YYYYMMDDThhmmssZ}.{run_id}.log per-run invocation file
|
|
68
|
+
// {runnable}.archive.{YYYYMMDD}.log[.gz] compacted archive
|
|
69
|
+
const PER_RUN_RE = /\.\d{8}T\d{6}Z\.[0-9a-fA-F-]{8,}\.log$/;
|
|
70
|
+
const ARCHIVE_RE = /\.archive\.\d{8}\.log(?:\.gz)?$/;
|
|
71
|
+
const DUR_MS = { s: 1000, m: 60_000, h: 3_600_000, d: 86_400_000, w: 604_800_000 };
|
|
72
|
+
const SIZE_MULT = { B: 1, KB: 1024, MB: 1024 ** 2, GB: 1024 ** 3 };
|
|
73
|
+
// ── parsing helpers ────────────────────────────────────────────────────────────
|
|
74
|
+
/** '30m' / '24h' / '7d' / '2w' → milliseconds. Throws on a bad value. */
|
|
75
|
+
function parseDuration(s) {
|
|
76
|
+
const m = /^\s*(\d+)\s*([smhdw])\s*$/i.exec(s);
|
|
77
|
+
if (!m)
|
|
78
|
+
throw new Error(`invalid duration ${JSON.stringify(s)} — use e.g. 30m, 24h, 7d, 2w`);
|
|
79
|
+
return parseInt(m[1], 10) * DUR_MS[m[2].toLowerCase()];
|
|
80
|
+
}
|
|
81
|
+
/** '500KB' / '10MB' / '5GB' → bytes. Throws on a bad value. */
|
|
82
|
+
function parseSize(s) {
|
|
83
|
+
const m = /^\s*(\d+(?:\.\d+)?)\s*(B|KB|MB|GB)?\s*$/i.exec(s);
|
|
84
|
+
if (!m)
|
|
85
|
+
throw new Error(`invalid size ${JSON.stringify(s)} — use e.g. 500KB, 10MB, 5GB`);
|
|
86
|
+
return Math.round(parseFloat(m[1]) * SIZE_MULT[(m[2] ?? 'B').toUpperCase()]);
|
|
87
|
+
}
|
|
88
|
+
function iso(mtimeMs) {
|
|
89
|
+
if (mtimeMs === null)
|
|
90
|
+
return null;
|
|
91
|
+
return new Date(mtimeMs).toISOString().replace(/\.\d{3}Z$/, 'Z');
|
|
92
|
+
}
|
|
93
|
+
function humanSize(n) {
|
|
94
|
+
let size = n;
|
|
95
|
+
for (const unit of ['B', 'KB', 'MB', 'GB', 'TB']) {
|
|
96
|
+
if (size < 1024 || unit === 'TB')
|
|
97
|
+
return unit === 'B' ? `${Math.round(size)} B` : `${size.toFixed(1)} ${unit}`;
|
|
98
|
+
size /= 1024;
|
|
99
|
+
}
|
|
100
|
+
return `${size.toFixed(1)} TB`;
|
|
101
|
+
}
|
|
102
|
+
// ── discovery ──────────────────────────────────────────────────────────────────
|
|
103
|
+
/** Existing logs directories, in priority order: the project's logs/, then ~/logs. */
|
|
104
|
+
function logDirs() {
|
|
105
|
+
const candidates = [];
|
|
106
|
+
try {
|
|
107
|
+
const { configPath } = (0, finder_1.findConfig)();
|
|
108
|
+
const root = (0, logging_setup_1.findProjectRoot)(path.dirname(configPath)) ?? path.dirname(configPath);
|
|
109
|
+
candidates.push(path.join(root, 'logs'));
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// no project context (no runspec.toml) — fall back to ~/logs only
|
|
113
|
+
}
|
|
114
|
+
candidates.push(path.join(os.homedir(), 'logs'));
|
|
115
|
+
const seen = new Set();
|
|
116
|
+
const out = [];
|
|
117
|
+
for (const d of candidates) {
|
|
118
|
+
const r = path.resolve(d);
|
|
119
|
+
if (seen.has(r))
|
|
120
|
+
continue;
|
|
121
|
+
seen.add(r);
|
|
122
|
+
try {
|
|
123
|
+
if (fs.statSync(d).isDirectory())
|
|
124
|
+
out.push(d);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// not present — skip
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
function matchesRunnable(name, runnable) {
|
|
133
|
+
if (!(name.endsWith('.log') || name.endsWith('.log.gz')))
|
|
134
|
+
return false;
|
|
135
|
+
if (!runnable)
|
|
136
|
+
return true;
|
|
137
|
+
return name === `${runnable}.log` || name.startsWith(`${runnable}.`);
|
|
138
|
+
}
|
|
139
|
+
function discover(dirs, runnable) {
|
|
140
|
+
const seen = new Set();
|
|
141
|
+
const files = [];
|
|
142
|
+
for (const d of dirs) {
|
|
143
|
+
let entries;
|
|
144
|
+
try {
|
|
145
|
+
entries = fs.readdirSync(d);
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
for (const name of entries) {
|
|
151
|
+
if (!matchesRunnable(name, runnable))
|
|
152
|
+
continue;
|
|
153
|
+
const full = path.join(d, name);
|
|
154
|
+
if (seen.has(full))
|
|
155
|
+
continue;
|
|
156
|
+
try {
|
|
157
|
+
if (!fs.statSync(full).isFile())
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
seen.add(full);
|
|
164
|
+
files.push(full);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return files;
|
|
168
|
+
}
|
|
169
|
+
/** True only for per-run files and archives — never a single {runnable}.log. */
|
|
170
|
+
function isManaged(p) {
|
|
171
|
+
const n = path.basename(p);
|
|
172
|
+
return PER_RUN_RE.test(n) || ARCHIVE_RE.test(n);
|
|
173
|
+
}
|
|
174
|
+
function isArchive(p) {
|
|
175
|
+
return ARCHIVE_RE.test(path.basename(p));
|
|
176
|
+
}
|
|
177
|
+
function runnableOf(p) {
|
|
178
|
+
const n = path.basename(p);
|
|
179
|
+
for (const rx of [PER_RUN_RE, ARCHIVE_RE]) {
|
|
180
|
+
const m = rx.exec(n);
|
|
181
|
+
if (m)
|
|
182
|
+
return n.slice(0, m.index);
|
|
183
|
+
}
|
|
184
|
+
return n.endsWith('.log.gz') ? n.slice(0, -7) : n.slice(0, -4);
|
|
185
|
+
}
|
|
186
|
+
function readLines(p) {
|
|
187
|
+
let text;
|
|
188
|
+
try {
|
|
189
|
+
text = p.endsWith('.gz') ? zlib.gunzipSync(fs.readFileSync(p)).toString('utf-8') : fs.readFileSync(p, 'utf-8');
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
194
|
+
return text.split('\n').filter((l) => l.trim() !== '');
|
|
195
|
+
}
|
|
196
|
+
/** Merge a runnable's files into one timestamp-sorted list of {rec, raw}. Pure. */
|
|
197
|
+
function collectRecords(dirs, runnable, opts = {}) {
|
|
198
|
+
const raws = [];
|
|
199
|
+
for (const f of discover(dirs, runnable))
|
|
200
|
+
raws.push(...readLines(f));
|
|
201
|
+
const parsed = [];
|
|
202
|
+
const userOfRun = {};
|
|
203
|
+
for (const raw of raws) {
|
|
204
|
+
let rec;
|
|
205
|
+
try {
|
|
206
|
+
rec = JSON.parse(raw);
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
const extra = rec && typeof rec.extra === 'object' && rec.extra ? rec.extra : {};
|
|
212
|
+
if (extra.event === 'run_summary' && extra.run_id)
|
|
213
|
+
userOfRun[extra.run_id] = extra.user ?? '';
|
|
214
|
+
parsed.push({ rec, raw });
|
|
215
|
+
}
|
|
216
|
+
const cutoff = opts.since != null ? Date.now() - opts.since : null;
|
|
217
|
+
const keep = (rec) => {
|
|
218
|
+
const extra = rec.extra && typeof rec.extra === 'object' ? rec.extra : {};
|
|
219
|
+
const rid = extra.run_id ?? '';
|
|
220
|
+
if (opts.run && rid !== opts.run)
|
|
221
|
+
return false;
|
|
222
|
+
if (opts.user && (userOfRun[rid] ?? extra.user ?? '') !== opts.user)
|
|
223
|
+
return false;
|
|
224
|
+
if (cutoff != null) {
|
|
225
|
+
const t = Date.parse(rec.ts ?? '');
|
|
226
|
+
if (Number.isNaN(t) || t < cutoff)
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
return true;
|
|
230
|
+
};
|
|
231
|
+
return parsed.filter((p) => keep(p.rec)).sort((a, b) => String(a.rec.ts ?? '').localeCompare(String(b.rec.ts ?? '')));
|
|
232
|
+
}
|
|
233
|
+
function formatLine(rec, userOfRun) {
|
|
234
|
+
const extra = rec.extra && typeof rec.extra === 'object' ? rec.extra : {};
|
|
235
|
+
const rid = extra.run_id ?? '';
|
|
236
|
+
const user = extra.user || userOfRun[rid] || '';
|
|
237
|
+
const ts = rec.ts ?? '';
|
|
238
|
+
const level = String(rec.level ?? '').padEnd(8);
|
|
239
|
+
const msg = rec.message ?? '';
|
|
240
|
+
return `${ts} ${level} run=${(rid || '-').slice(0, 8)} user=${user || '-'} ${msg}`;
|
|
241
|
+
}
|
|
242
|
+
function emit(records, asJson, out) {
|
|
243
|
+
const userOfRun = {};
|
|
244
|
+
for (const { rec } of records) {
|
|
245
|
+
const extra = rec.extra && typeof rec.extra === 'object' ? rec.extra : {};
|
|
246
|
+
if (extra.event === 'run_summary' && extra.run_id)
|
|
247
|
+
userOfRun[extra.run_id] = extra.user ?? '';
|
|
248
|
+
}
|
|
249
|
+
for (const { rec, raw } of records) {
|
|
250
|
+
out(asJson ? raw + '\n' : formatLine(rec, userOfRun) + '\n');
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/** Write the merged stream to `out` (default stdout). Supports `--follow` (poll). */
|
|
254
|
+
function view(runnable, opts = {}) {
|
|
255
|
+
const dirs = opts.dirs ?? logDirs();
|
|
256
|
+
const out = opts.out ?? ((s) => process.stdout.write(s));
|
|
257
|
+
const co = { since: opts.since, run: opts.run, user: opts.user };
|
|
258
|
+
const records = collectRecords(dirs, runnable, co);
|
|
259
|
+
emit(records, !!opts.asJson, out);
|
|
260
|
+
if (!opts.follow)
|
|
261
|
+
return;
|
|
262
|
+
const key = (r) => `${r.ts ?? ''}|${(r.extra && r.extra.run_id) ?? ''}|${r.message ?? ''}`;
|
|
263
|
+
const seen = new Set(records.map((p) => key(p.rec)));
|
|
264
|
+
const interval = setInterval(() => {
|
|
265
|
+
const fresh = collectRecords(dirs, runnable, co).filter((p) => !seen.has(key(p.rec)));
|
|
266
|
+
if (fresh.length) {
|
|
267
|
+
emit(fresh, !!opts.asJson, out);
|
|
268
|
+
for (const p of fresh)
|
|
269
|
+
seen.add(key(p.rec));
|
|
270
|
+
}
|
|
271
|
+
}, opts.pollMs ?? 1000);
|
|
272
|
+
// Don't keep the event loop alive solely for the poll if the process is done.
|
|
273
|
+
if (typeof interval.unref === 'function')
|
|
274
|
+
interval.unref();
|
|
275
|
+
}
|
|
276
|
+
/** Per-runnable inventory of managed files. Pure (stat only). */
|
|
277
|
+
function inventory(dirs, runnable = null) {
|
|
278
|
+
const per = {};
|
|
279
|
+
for (const f of discover(dirs, runnable)) {
|
|
280
|
+
if (!isManaged(f))
|
|
281
|
+
continue;
|
|
282
|
+
let st;
|
|
283
|
+
try {
|
|
284
|
+
st = fs.statSync(f);
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
const name = runnableOf(f);
|
|
290
|
+
const row = (per[name] ??= { runnable: name, per_run_files: 0, archives: 0, total_bytes: 0, oldest: null, newest: null });
|
|
291
|
+
if (isArchive(f))
|
|
292
|
+
row.archives++;
|
|
293
|
+
else
|
|
294
|
+
row.per_run_files++;
|
|
295
|
+
row.total_bytes += st.size;
|
|
296
|
+
if (row.oldest === null || st.mtimeMs < row.oldest)
|
|
297
|
+
row.oldest = st.mtimeMs;
|
|
298
|
+
if (row.newest === null || st.mtimeMs > row.newest)
|
|
299
|
+
row.newest = st.mtimeMs;
|
|
300
|
+
}
|
|
301
|
+
return per;
|
|
302
|
+
}
|
|
303
|
+
function status(runnable = null, opts = {}) {
|
|
304
|
+
const dirs = opts.dirs ?? logDirs();
|
|
305
|
+
const out = opts.out ?? ((s) => process.stdout.write(s));
|
|
306
|
+
const rows = Object.values(inventory(dirs, runnable)).sort((a, b) => a.runnable.localeCompare(b.runnable));
|
|
307
|
+
const totalBytes = rows.reduce((s, r) => s + r.total_bytes, 0);
|
|
308
|
+
const totalFiles = rows.reduce((s, r) => s + r.per_run_files + r.archives, 0);
|
|
309
|
+
if (opts.asJson) {
|
|
310
|
+
out(JSON.stringify({
|
|
311
|
+
dirs,
|
|
312
|
+
runnables: rows.map((r) => ({
|
|
313
|
+
runnable: r.runnable,
|
|
314
|
+
per_run_files: r.per_run_files,
|
|
315
|
+
archives: r.archives,
|
|
316
|
+
total_bytes: r.total_bytes,
|
|
317
|
+
oldest: iso(r.oldest),
|
|
318
|
+
newest: iso(r.newest),
|
|
319
|
+
})),
|
|
320
|
+
total_bytes: totalBytes,
|
|
321
|
+
total_files: totalFiles,
|
|
322
|
+
}) + '\n');
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
if (rows.length === 0) {
|
|
326
|
+
out('No per-invocation logs found.\n');
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
const nameW = Math.max('RUNNABLE'.length, ...rows.map((r) => r.runnable.length));
|
|
330
|
+
out(`${'RUNNABLE'.padEnd(nameW)} ${'RUNS'.padStart(6)} ${'ARCHIVES'.padStart(8)} ${'SIZE'.padStart(10)} NEWEST\n`);
|
|
331
|
+
for (const r of rows) {
|
|
332
|
+
out(`${r.runnable.padEnd(nameW)} ${String(r.per_run_files).padStart(6)} ${String(r.archives).padStart(8)} ${humanSize(r.total_bytes).padStart(10)} ${iso(r.newest) ?? '-'}\n`);
|
|
333
|
+
}
|
|
334
|
+
out(`\n${totalFiles} file(s), ${humanSize(totalBytes)} total across ${rows.length} runnable(s).\n`);
|
|
335
|
+
}
|
|
336
|
+
/** Return the managed files to delete. Pure — touches nothing. Throws if no policy. */
|
|
337
|
+
function planPrune(dirs, runnable, policy, now = Date.now()) {
|
|
338
|
+
const { olderThan = null, maxFiles = null, maxTotalSize = null } = policy;
|
|
339
|
+
if (olderThan == null && maxFiles == null && maxTotalSize == null) {
|
|
340
|
+
throw new Error('prune needs at least one of --older-than / --max-files / --max-total-size');
|
|
341
|
+
}
|
|
342
|
+
const files = discover(dirs, runnable).filter(isManaged);
|
|
343
|
+
const stat = new Map();
|
|
344
|
+
for (const f of files) {
|
|
345
|
+
try {
|
|
346
|
+
stat.set(f, fs.statSync(f));
|
|
347
|
+
}
|
|
348
|
+
catch {
|
|
349
|
+
// skip vanished file
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
const live = files.filter((f) => stat.has(f));
|
|
353
|
+
const doomed = new Set();
|
|
354
|
+
if (olderThan != null) {
|
|
355
|
+
const cut = now - olderThan;
|
|
356
|
+
for (const f of live)
|
|
357
|
+
if (stat.get(f).mtimeMs < cut)
|
|
358
|
+
doomed.add(f);
|
|
359
|
+
}
|
|
360
|
+
if (maxFiles != null) {
|
|
361
|
+
const per = {};
|
|
362
|
+
for (const f of live)
|
|
363
|
+
(per[runnableOf(f)] ??= []).push(f);
|
|
364
|
+
for (const group of Object.values(per)) {
|
|
365
|
+
group.sort((a, b) => stat.get(b).mtimeMs - stat.get(a).mtimeMs); // newest first
|
|
366
|
+
for (const f of group.slice(maxFiles))
|
|
367
|
+
doomed.add(f);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
if (maxTotalSize != null) {
|
|
371
|
+
const ordered = [...live].sort((a, b) => stat.get(b).mtimeMs - stat.get(a).mtimeMs);
|
|
372
|
+
let total = 0;
|
|
373
|
+
for (const f of ordered) {
|
|
374
|
+
total += stat.get(f).size;
|
|
375
|
+
if (total > maxTotalSize)
|
|
376
|
+
doomed.add(f);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return [...doomed].sort((a, b) => stat.get(a).mtimeMs - stat.get(b).mtimeMs);
|
|
380
|
+
}
|
|
381
|
+
function prune(runnable, opts = {}) {
|
|
382
|
+
const dirs = opts.dirs ?? logDirs();
|
|
383
|
+
const out = opts.out ?? ((s) => process.stdout.write(s));
|
|
384
|
+
const targets = planPrune(dirs, runnable, opts);
|
|
385
|
+
let freed = 0;
|
|
386
|
+
const deleted = [];
|
|
387
|
+
for (const f of targets) {
|
|
388
|
+
let size = 0;
|
|
389
|
+
try {
|
|
390
|
+
size = fs.statSync(f).size;
|
|
391
|
+
}
|
|
392
|
+
catch {
|
|
393
|
+
// already gone
|
|
394
|
+
}
|
|
395
|
+
if (!opts.dryRun) {
|
|
396
|
+
try {
|
|
397
|
+
fs.unlinkSync(f);
|
|
398
|
+
}
|
|
399
|
+
catch {
|
|
400
|
+
// ignore
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
freed += size;
|
|
404
|
+
deleted.push({ path: f, bytes: size });
|
|
405
|
+
if (!opts.asJson)
|
|
406
|
+
out(`${opts.dryRun ? 'would delete' : 'deleted'} ${f} (${size} bytes)\n`);
|
|
407
|
+
}
|
|
408
|
+
if (opts.asJson) {
|
|
409
|
+
out(JSON.stringify({ dry_run: !!opts.dryRun, count: targets.length, freed_bytes: freed, deleted }) + '\n');
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
out(`${opts.dryRun ? 'Would free' : 'Freed'} ${freed} bytes across ${targets.length} file(s).\n`);
|
|
413
|
+
}
|
|
414
|
+
return { count: targets.length, freed };
|
|
415
|
+
}
|
|
416
|
+
// ── compact ──────────────────────────────────────────────────────────────────────
|
|
417
|
+
function lineTs(line) {
|
|
418
|
+
try {
|
|
419
|
+
return String(JSON.parse(line).ts ?? '');
|
|
420
|
+
}
|
|
421
|
+
catch {
|
|
422
|
+
return '';
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
/** Map each per-runnable archive target → the per-run files to fold in. Pure. */
|
|
426
|
+
function planCompact(dirs, runnable, olderThan, now = Date.now()) {
|
|
427
|
+
const cut = now - olderThan;
|
|
428
|
+
const today = new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
|
429
|
+
const plan = new Map();
|
|
430
|
+
for (const f of discover(dirs, runnable)) {
|
|
431
|
+
if (!isManaged(f) || isArchive(f))
|
|
432
|
+
continue;
|
|
433
|
+
let st;
|
|
434
|
+
try {
|
|
435
|
+
st = fs.statSync(f);
|
|
436
|
+
}
|
|
437
|
+
catch {
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
if (st.mtimeMs >= cut)
|
|
441
|
+
continue;
|
|
442
|
+
const archive = path.join(path.dirname(f), `${runnableOf(f)}.archive.${today}.log`);
|
|
443
|
+
(plan.get(archive) ?? plan.set(archive, []).get(archive)).push(f);
|
|
444
|
+
}
|
|
445
|
+
return plan;
|
|
446
|
+
}
|
|
447
|
+
function compact(runnable, olderThan, opts = {}) {
|
|
448
|
+
const dirs = opts.dirs ?? logDirs();
|
|
449
|
+
const out = opts.out ?? ((s) => process.stdout.write(s));
|
|
450
|
+
const plan = planCompact(dirs, runnable, olderThan);
|
|
451
|
+
let compacted = 0;
|
|
452
|
+
const archives = [];
|
|
453
|
+
for (const [archive, sources] of plan) {
|
|
454
|
+
const target = opts.gzip ? archive + '.gz' : archive;
|
|
455
|
+
if (opts.dryRun) {
|
|
456
|
+
archives.push({ archive: target, count: sources.length, sources });
|
|
457
|
+
compacted += sources.length;
|
|
458
|
+
if (!opts.asJson)
|
|
459
|
+
out(`would compact ${sources.length} file(s) → ${target}\n`);
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
const lines = [];
|
|
463
|
+
for (const existing of [archive, archive + '.gz']) {
|
|
464
|
+
if (fs.existsSync(existing))
|
|
465
|
+
lines.push(...readLines(existing));
|
|
466
|
+
}
|
|
467
|
+
for (const s of sources)
|
|
468
|
+
lines.push(...readLines(s));
|
|
469
|
+
lines.sort((a, b) => lineTs(a).localeCompare(lineTs(b)));
|
|
470
|
+
const body = lines.join('\n') + '\n';
|
|
471
|
+
if (opts.gzip) {
|
|
472
|
+
fs.writeFileSync(target, zlib.gzipSync(Buffer.from(body, 'utf-8')));
|
|
473
|
+
if (fs.existsSync(archive) && archive !== target) {
|
|
474
|
+
try {
|
|
475
|
+
fs.unlinkSync(archive);
|
|
476
|
+
}
|
|
477
|
+
catch {
|
|
478
|
+
// ignore
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
fs.writeFileSync(target, body, 'utf-8');
|
|
484
|
+
}
|
|
485
|
+
for (const s of sources) {
|
|
486
|
+
try {
|
|
487
|
+
fs.unlinkSync(s);
|
|
488
|
+
}
|
|
489
|
+
catch {
|
|
490
|
+
// ignore
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
archives.push({ archive: target, count: sources.length, sources });
|
|
494
|
+
compacted += sources.length;
|
|
495
|
+
if (!opts.asJson)
|
|
496
|
+
out(`compacted ${sources.length} file(s) → ${target}\n`);
|
|
497
|
+
}
|
|
498
|
+
if (opts.asJson)
|
|
499
|
+
out(JSON.stringify({ dry_run: !!opts.dryRun, compacted, archives }) + '\n');
|
|
500
|
+
else if (plan.size === 0)
|
|
501
|
+
out('nothing to compact.\n');
|
|
502
|
+
return compacted;
|
|
503
|
+
}
|
|
504
|
+
//# sourceMappingURL=logs.js.map
|
package/dist/logs.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logs.js","sourceRoot":"","sources":["../src/logs.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -2,6 +2,7 @@ import * as fs from 'fs';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import * as readline from 'readline';
|
|
4
4
|
import { findConfig } from './finder';
|
|
5
|
+
import * as logs from './logs';
|
|
5
6
|
import { loadRaw } from './loader';
|
|
6
7
|
import { inferScript } from './inference';
|
|
7
8
|
import { parse } from './parser';
|
|
@@ -33,6 +34,7 @@ export function main(): void {
|
|
|
33
34
|
init: cmdInit,
|
|
34
35
|
local: cmdLocal,
|
|
35
36
|
bin: cmdBin,
|
|
37
|
+
logs: cmdLogs,
|
|
36
38
|
jump: cmdJump,
|
|
37
39
|
serve: cmdServe,
|
|
38
40
|
};
|
|
@@ -230,6 +232,69 @@ function toPosix(p: string): string {
|
|
|
230
232
|
return p.split(path.sep).join('/');
|
|
231
233
|
}
|
|
232
234
|
|
|
235
|
+
function cmdLogs(args: string[]): void {
|
|
236
|
+
const has = (name: string): boolean => args.includes(name);
|
|
237
|
+
const valueFlags = new Set(['--since', '--user', '--run', '--older-than', '--max-files', '--max-total-size']);
|
|
238
|
+
const val = (name: string): string | undefined => {
|
|
239
|
+
const i = args.indexOf(name);
|
|
240
|
+
return i >= 0 && i + 1 < args.length ? args[i + 1] : undefined;
|
|
241
|
+
};
|
|
242
|
+
// Positionals are tokens that aren't flags or flag-values.
|
|
243
|
+
const positionals: string[] = [];
|
|
244
|
+
for (let i = 0; i < args.length; i++) {
|
|
245
|
+
const a = args[i];
|
|
246
|
+
if (a.startsWith('-')) {
|
|
247
|
+
if (valueFlags.has(a)) i++; // skip its value
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
positionals.push(a);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// `runspec logs status|prune|compact [runnable]` vs `runspec logs <runnable>`.
|
|
254
|
+
const target = positionals[0];
|
|
255
|
+
const verb = target === 'status' || target === 'prune' || target === 'compact' ? target : 'view';
|
|
256
|
+
const runnable = verb === 'view' ? target : positionals[1] ?? null;
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
if (verb === 'view') {
|
|
260
|
+
if (!runnable) {
|
|
261
|
+
console.log('✗ A runnable is required: runspec logs <runnable>');
|
|
262
|
+
process.exit(1);
|
|
263
|
+
}
|
|
264
|
+
logs.view(runnable, {
|
|
265
|
+
since: val('--since') ? logs.parseDuration(val('--since')!) : null,
|
|
266
|
+
run: val('--run') ?? null,
|
|
267
|
+
user: val('--user') ?? null,
|
|
268
|
+
asJson: has('--json'),
|
|
269
|
+
follow: has('--follow'),
|
|
270
|
+
});
|
|
271
|
+
} else if (verb === 'status') {
|
|
272
|
+
logs.status(runnable, { asJson: has('--json') });
|
|
273
|
+
} else if (verb === 'prune') {
|
|
274
|
+
logs.prune(runnable, {
|
|
275
|
+
olderThan: val('--older-than') ? logs.parseDuration(val('--older-than')!) : null,
|
|
276
|
+
maxFiles: val('--max-files') ? parseInt(val('--max-files')!, 10) : null,
|
|
277
|
+
maxTotalSize: val('--max-total-size') ? logs.parseSize(val('--max-total-size')!) : null,
|
|
278
|
+
dryRun: has('--dry-run'),
|
|
279
|
+
asJson: has('--json'),
|
|
280
|
+
});
|
|
281
|
+
} else if (verb === 'compact') {
|
|
282
|
+
if (!val('--older-than')) {
|
|
283
|
+
console.log('✗ compact requires --older-than (e.g. --older-than 7d)');
|
|
284
|
+
process.exit(1);
|
|
285
|
+
}
|
|
286
|
+
logs.compact(runnable, logs.parseDuration(val('--older-than')!), {
|
|
287
|
+
gzip: has('--gzip'),
|
|
288
|
+
dryRun: has('--dry-run'),
|
|
289
|
+
asJson: has('--json'),
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
} catch (err) {
|
|
293
|
+
console.log(`✗ ${(err as Error).message}`);
|
|
294
|
+
process.exit(1);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
233
298
|
async function cmdJump(args: string[]): Promise<void> {
|
|
234
299
|
const parsed = parse({ scriptName: 'runspec', argv: ['jump', ...args], configPath: _CLI_CONFIG });
|
|
235
300
|
const fmt = String(parsed['format'] ?? 'text');
|
|
@@ -751,6 +816,7 @@ Commands:
|
|
|
751
816
|
init Create runspec.toml and a code stub
|
|
752
817
|
local List runnables and emit tool schemas
|
|
753
818
|
bin Generate a venv-shaped bin/ so a controller can run this folder
|
|
819
|
+
logs View, status, prune, or compact per-invocation audit logs
|
|
754
820
|
jump Execute a runnable on a remote host via SSH
|
|
755
821
|
serve Start the MCP stdio server for local runnables
|
|
756
822
|
|
|
@@ -762,6 +828,7 @@ Examples:
|
|
|
762
828
|
runspec local
|
|
763
829
|
runspec local --format mcp
|
|
764
830
|
runspec bin
|
|
831
|
+
runspec logs deploy
|
|
765
832
|
runspec serve`);
|
|
766
833
|
}
|
|
767
834
|
|
|
@@ -806,6 +873,25 @@ Examples:
|
|
|
806
873
|
npm install runspec-node
|
|
807
874
|
runspec bin`,
|
|
808
875
|
|
|
876
|
+
logs: `runspec logs — View, status, prune, or compact per-invocation audit logs
|
|
877
|
+
|
|
878
|
+
For runnables using [config.logging] store = "per-run" (one file per
|
|
879
|
+
invocation, no in-process rotation). Reads/maintains the project's logs/.
|
|
880
|
+
|
|
881
|
+
View (default):
|
|
882
|
+
runspec logs <runnable> merged, timestamp-sorted stream
|
|
883
|
+
runspec logs <runnable> --follow live tail across invocations
|
|
884
|
+
runspec logs <runnable> --since 1h --user alice --run <id>
|
|
885
|
+
runspec logs <runnable> --json raw JSON lines
|
|
886
|
+
|
|
887
|
+
Status / retention (default to all runnables):
|
|
888
|
+
runspec logs status [runnable] [--json] per-runnable file + disk inventory
|
|
889
|
+
runspec logs compact [runnable] --older-than 7d [--gzip] [--dry-run]
|
|
890
|
+
runspec logs prune [runnable] --older-than 90d | --max-files N | --max-total-size 5GB [--dry-run]
|
|
891
|
+
|
|
892
|
+
prune/compact never touch a single-mode {runnable}.log — only per-run files
|
|
893
|
+
and archives. Add --json to any verb for machine-readable output.`,
|
|
894
|
+
|
|
809
895
|
jump: `runspec jump — Execute a runnable on a remote host via SSH
|
|
810
896
|
|
|
811
897
|
Not yet implemented in the Node package.
|