@litmers/cursorflow-orchestrator 0.1.13 → 0.1.15
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/CHANGELOG.md +37 -0
- package/README.md +83 -2
- package/commands/cursorflow-clean.md +20 -6
- package/commands/cursorflow-prepare.md +1 -1
- package/commands/cursorflow-resume.md +127 -6
- package/commands/cursorflow-run.md +2 -2
- package/commands/cursorflow-signal.md +11 -4
- package/dist/cli/clean.js +164 -12
- package/dist/cli/clean.js.map +1 -1
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +6 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/logs.d.ts +8 -0
- package/dist/cli/logs.js +759 -0
- package/dist/cli/logs.js.map +1 -0
- package/dist/cli/monitor.js +113 -30
- package/dist/cli/monitor.js.map +1 -1
- package/dist/cli/prepare.js +1 -1
- package/dist/cli/resume.js +367 -18
- package/dist/cli/resume.js.map +1 -1
- package/dist/cli/run.js +9 -0
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/signal.js +34 -20
- package/dist/cli/signal.js.map +1 -1
- package/dist/core/orchestrator.d.ts +13 -1
- package/dist/core/orchestrator.js +396 -35
- package/dist/core/orchestrator.js.map +1 -1
- package/dist/core/reviewer.d.ts +2 -0
- package/dist/core/reviewer.js +24 -2
- package/dist/core/reviewer.js.map +1 -1
- package/dist/core/runner.d.ts +9 -3
- package/dist/core/runner.js +266 -61
- package/dist/core/runner.js.map +1 -1
- package/dist/utils/config.js +38 -1
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/enhanced-logger.d.ts +210 -0
- package/dist/utils/enhanced-logger.js +1030 -0
- package/dist/utils/enhanced-logger.js.map +1 -0
- package/dist/utils/events.d.ts +59 -0
- package/dist/utils/events.js +37 -0
- package/dist/utils/events.js.map +1 -0
- package/dist/utils/git.d.ts +11 -0
- package/dist/utils/git.js +40 -0
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/logger.d.ts +2 -0
- package/dist/utils/logger.js +4 -1
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/types.d.ts +132 -1
- package/dist/utils/webhook.d.ts +5 -0
- package/dist/utils/webhook.js +109 -0
- package/dist/utils/webhook.js.map +1 -0
- package/examples/README.md +1 -1
- package/package.json +2 -1
- package/scripts/patches/test-cursor-agent.js +1 -1
- package/scripts/simple-logging-test.sh +97 -0
- package/scripts/test-real-cursor-lifecycle.sh +289 -0
- package/scripts/test-real-logging.sh +289 -0
- package/scripts/test-streaming-multi-task.sh +247 -0
- package/src/cli/clean.ts +170 -13
- package/src/cli/index.ts +4 -1
- package/src/cli/logs.ts +863 -0
- package/src/cli/monitor.ts +123 -30
- package/src/cli/prepare.ts +1 -1
- package/src/cli/resume.ts +463 -22
- package/src/cli/run.ts +10 -0
- package/src/cli/signal.ts +43 -27
- package/src/core/orchestrator.ts +458 -36
- package/src/core/reviewer.ts +40 -4
- package/src/core/runner.ts +293 -60
- package/src/utils/config.ts +41 -1
- package/src/utils/enhanced-logger.ts +1166 -0
- package/src/utils/events.ts +117 -0
- package/src/utils/git.ts +40 -0
- package/src/utils/logger.ts +4 -1
- package/src/utils/types.ts +160 -1
- package/src/utils/webhook.ts +85 -0
package/dist/cli/logs.js
ADDED
|
@@ -0,0 +1,759 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CursorFlow logs command - View and export logs
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const logger = __importStar(require("../utils/logger"));
|
|
41
|
+
const config_1 = require("../utils/config");
|
|
42
|
+
const enhanced_logger_1 = require("../utils/enhanced-logger");
|
|
43
|
+
function printHelp() {
|
|
44
|
+
console.log(`
|
|
45
|
+
Usage: cursorflow logs [run-dir] [options]
|
|
46
|
+
|
|
47
|
+
View and export lane logs.
|
|
48
|
+
|
|
49
|
+
Options:
|
|
50
|
+
[run-dir] Run directory (default: latest)
|
|
51
|
+
--lane <name> Filter to specific lane
|
|
52
|
+
--all, -a View all lanes merged (sorted by timestamp)
|
|
53
|
+
--format <fmt> Output format: text, json, markdown, html (default: text)
|
|
54
|
+
--output <path> Write output to file instead of stdout
|
|
55
|
+
--tail <n> Show last n lines/entries (default: all)
|
|
56
|
+
--follow, -f Follow log output in real-time
|
|
57
|
+
--filter <pattern> Filter entries by regex pattern
|
|
58
|
+
--level <level> Filter by log level: stdout, stderr, info, error, debug
|
|
59
|
+
--readable, -r Show readable log (parsed AI output) (default)
|
|
60
|
+
--clean Show clean terminal logs without ANSI codes
|
|
61
|
+
--raw Show raw terminal logs with ANSI codes
|
|
62
|
+
--help, -h Show help
|
|
63
|
+
|
|
64
|
+
Examples:
|
|
65
|
+
cursorflow logs # View latest run logs summary
|
|
66
|
+
cursorflow logs --lane api-setup # View readable parsed log (default)
|
|
67
|
+
cursorflow logs --lane api-setup --clean # View clean terminal logs
|
|
68
|
+
cursorflow logs --all # View all lanes merged by time
|
|
69
|
+
cursorflow logs --all --follow # Follow all lanes in real-time
|
|
70
|
+
cursorflow logs --all --format json # Export all lanes as JSON
|
|
71
|
+
cursorflow logs --all --filter "error" # Filter all lanes for errors
|
|
72
|
+
cursorflow logs --format json --output out.json # Export as JSON
|
|
73
|
+
`);
|
|
74
|
+
}
|
|
75
|
+
function parseArgs(args) {
|
|
76
|
+
const laneIdx = args.indexOf('--lane');
|
|
77
|
+
const formatIdx = args.indexOf('--format');
|
|
78
|
+
const outputIdx = args.indexOf('--output');
|
|
79
|
+
const tailIdx = args.indexOf('--tail');
|
|
80
|
+
const filterIdx = args.indexOf('--filter');
|
|
81
|
+
const levelIdx = args.indexOf('--level');
|
|
82
|
+
// Find run directory (first non-option argument)
|
|
83
|
+
const runDir = args.find((arg, i) => {
|
|
84
|
+
if (arg.startsWith('--') || arg.startsWith('-'))
|
|
85
|
+
return false;
|
|
86
|
+
// Skip values for options
|
|
87
|
+
const prevArg = args[i - 1];
|
|
88
|
+
if (prevArg && ['--lane', '--format', '--output', '--tail', '--filter', '--level'].includes(prevArg)) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
return true;
|
|
92
|
+
});
|
|
93
|
+
const raw = args.includes('--raw');
|
|
94
|
+
const clean = args.includes('--clean');
|
|
95
|
+
const readable = args.includes('--readable') || args.includes('-r');
|
|
96
|
+
return {
|
|
97
|
+
runDir,
|
|
98
|
+
lane: laneIdx >= 0 ? args[laneIdx + 1] : undefined,
|
|
99
|
+
all: args.includes('--all') || args.includes('-a'),
|
|
100
|
+
format: (formatIdx >= 0 ? args[formatIdx + 1] : 'text'),
|
|
101
|
+
output: outputIdx >= 0 ? args[outputIdx + 1] : undefined,
|
|
102
|
+
tail: tailIdx >= 0 ? parseInt(args[tailIdx + 1] || '50') : undefined,
|
|
103
|
+
follow: args.includes('--follow') || args.includes('-f'),
|
|
104
|
+
filter: filterIdx >= 0 ? args[filterIdx + 1] : undefined,
|
|
105
|
+
level: levelIdx >= 0 ? args[levelIdx + 1] : undefined,
|
|
106
|
+
raw,
|
|
107
|
+
clean,
|
|
108
|
+
// Default to readable if no other format is specified
|
|
109
|
+
readable: readable || (!raw && !clean),
|
|
110
|
+
help: args.includes('--help') || args.includes('-h'),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Find the latest run directory
|
|
115
|
+
*/
|
|
116
|
+
function findLatestRunDir(logsDir) {
|
|
117
|
+
const runsDir = path.join(logsDir, 'runs');
|
|
118
|
+
if (!fs.existsSync(runsDir))
|
|
119
|
+
return null;
|
|
120
|
+
const runs = fs.readdirSync(runsDir)
|
|
121
|
+
.filter(d => d.startsWith('run-'))
|
|
122
|
+
.map(d => ({
|
|
123
|
+
name: d,
|
|
124
|
+
path: path.join(runsDir, d),
|
|
125
|
+
mtime: fs.statSync(path.join(runsDir, d)).mtime.getTime()
|
|
126
|
+
}))
|
|
127
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
128
|
+
return runs.length > 0 ? runs[0].path : null;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* List lanes in a run directory
|
|
132
|
+
*/
|
|
133
|
+
function listLanes(runDir) {
|
|
134
|
+
const lanesDir = path.join(runDir, 'lanes');
|
|
135
|
+
if (!fs.existsSync(lanesDir))
|
|
136
|
+
return [];
|
|
137
|
+
return fs.readdirSync(lanesDir)
|
|
138
|
+
.filter(d => fs.statSync(path.join(lanesDir, d)).isDirectory());
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Read and display text logs
|
|
142
|
+
*/
|
|
143
|
+
function displayTextLogs(laneDir, options) {
|
|
144
|
+
let logFile;
|
|
145
|
+
const readableLog = path.join(laneDir, 'terminal-readable.log');
|
|
146
|
+
const rawLog = path.join(laneDir, 'terminal-raw.log');
|
|
147
|
+
const cleanLog = path.join(laneDir, 'terminal.log');
|
|
148
|
+
if (options.raw) {
|
|
149
|
+
logFile = rawLog;
|
|
150
|
+
}
|
|
151
|
+
else if (options.clean) {
|
|
152
|
+
logFile = cleanLog;
|
|
153
|
+
}
|
|
154
|
+
else if (options.readable && fs.existsSync(readableLog)) {
|
|
155
|
+
logFile = readableLog;
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
// Default or fallback to clean log
|
|
159
|
+
logFile = cleanLog;
|
|
160
|
+
}
|
|
161
|
+
if (!fs.existsSync(logFile)) {
|
|
162
|
+
console.log('No log file found.');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
let content = fs.readFileSync(logFile, 'utf8');
|
|
166
|
+
let lines = content.split('\n');
|
|
167
|
+
// Apply filter
|
|
168
|
+
if (options.filter) {
|
|
169
|
+
const regex = new RegExp(options.filter, 'i');
|
|
170
|
+
lines = lines.filter(line => regex.test(line));
|
|
171
|
+
}
|
|
172
|
+
// Apply tail
|
|
173
|
+
if (options.tail && lines.length > options.tail) {
|
|
174
|
+
lines = lines.slice(-options.tail);
|
|
175
|
+
}
|
|
176
|
+
// Clean ANSI if needed (for clean mode or default fallback)
|
|
177
|
+
if (!options.raw) {
|
|
178
|
+
lines = lines.map(line => (0, enhanced_logger_1.stripAnsi)(line));
|
|
179
|
+
}
|
|
180
|
+
console.log(lines.join('\n'));
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Read and display JSON logs
|
|
184
|
+
*/
|
|
185
|
+
function displayJsonLogs(laneDir, options) {
|
|
186
|
+
const logFile = path.join(laneDir, 'terminal.jsonl');
|
|
187
|
+
if (!fs.existsSync(logFile)) {
|
|
188
|
+
console.log('No JSON log file found.');
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
let entries = (0, enhanced_logger_1.readJsonLog)(logFile);
|
|
192
|
+
// Apply level filter
|
|
193
|
+
if (options.level) {
|
|
194
|
+
entries = entries.filter(e => e.level === options.level);
|
|
195
|
+
}
|
|
196
|
+
// Apply regex filter
|
|
197
|
+
if (options.filter) {
|
|
198
|
+
const regex = new RegExp(options.filter, 'i');
|
|
199
|
+
entries = entries.filter(e => regex.test(e.message) || regex.test(e.task || ''));
|
|
200
|
+
}
|
|
201
|
+
// Apply tail
|
|
202
|
+
if (options.tail && entries.length > options.tail) {
|
|
203
|
+
entries = entries.slice(-options.tail);
|
|
204
|
+
}
|
|
205
|
+
if (options.format === 'json') {
|
|
206
|
+
console.log(JSON.stringify(entries, null, 2));
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
// Display as formatted text
|
|
210
|
+
for (const entry of entries) {
|
|
211
|
+
const levelColor = getLevelColor(entry.level);
|
|
212
|
+
const ts = new Date(entry.timestamp).toLocaleTimeString();
|
|
213
|
+
console.log(`${levelColor}[${ts}] [${entry.level.toUpperCase().padEnd(6)}]${logger.COLORS.reset} ${entry.message}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Get color for log level
|
|
219
|
+
*/
|
|
220
|
+
function getLevelColor(level) {
|
|
221
|
+
switch (level) {
|
|
222
|
+
case 'error':
|
|
223
|
+
return logger.COLORS.red;
|
|
224
|
+
case 'stderr':
|
|
225
|
+
return logger.COLORS.yellow;
|
|
226
|
+
case 'info':
|
|
227
|
+
case 'session':
|
|
228
|
+
return logger.COLORS.cyan;
|
|
229
|
+
case 'debug':
|
|
230
|
+
return logger.COLORS.gray;
|
|
231
|
+
default:
|
|
232
|
+
return logger.COLORS.reset;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Lane color palette for distinguishing lanes in merged view
|
|
237
|
+
*/
|
|
238
|
+
const LANE_COLORS = [
|
|
239
|
+
'\x1b[38;5;39m', // Blue
|
|
240
|
+
'\x1b[38;5;208m', // Orange
|
|
241
|
+
'\x1b[38;5;156m', // Light Green
|
|
242
|
+
'\x1b[38;5;213m', // Pink
|
|
243
|
+
'\x1b[38;5;87m', // Cyan
|
|
244
|
+
'\x1b[38;5;228m', // Yellow
|
|
245
|
+
'\x1b[38;5;183m', // Light Purple
|
|
246
|
+
'\x1b[38;5;121m', // Sea Green
|
|
247
|
+
];
|
|
248
|
+
/**
|
|
249
|
+
* Get consistent color for a lane name
|
|
250
|
+
*/
|
|
251
|
+
function getLaneColor(laneName, laneIndex) {
|
|
252
|
+
return LANE_COLORS[laneIndex % LANE_COLORS.length];
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Read and merge all lane logs
|
|
256
|
+
*/
|
|
257
|
+
function readAllLaneLogs(runDir) {
|
|
258
|
+
const lanes = listLanes(runDir);
|
|
259
|
+
const allEntries = [];
|
|
260
|
+
lanes.forEach((laneName, index) => {
|
|
261
|
+
const laneDir = path.join(runDir, 'lanes', laneName);
|
|
262
|
+
const jsonLogPath = path.join(laneDir, 'terminal.jsonl');
|
|
263
|
+
if (fs.existsSync(jsonLogPath)) {
|
|
264
|
+
const entries = (0, enhanced_logger_1.readJsonLog)(jsonLogPath);
|
|
265
|
+
const laneColor = getLaneColor(laneName, index);
|
|
266
|
+
for (const entry of entries) {
|
|
267
|
+
allEntries.push({
|
|
268
|
+
...entry,
|
|
269
|
+
laneName,
|
|
270
|
+
laneColor,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
// Sort by timestamp
|
|
276
|
+
allEntries.sort((a, b) => {
|
|
277
|
+
const timeA = new Date(a.timestamp).getTime();
|
|
278
|
+
const timeB = new Date(b.timestamp).getTime();
|
|
279
|
+
return timeA - timeB;
|
|
280
|
+
});
|
|
281
|
+
return allEntries;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Display merged logs from all lanes
|
|
285
|
+
*/
|
|
286
|
+
function displayMergedLogs(runDir, options) {
|
|
287
|
+
let entries = readAllLaneLogs(runDir);
|
|
288
|
+
if (entries.length === 0) {
|
|
289
|
+
console.log('No log entries found in any lane.');
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
// Apply level filter
|
|
293
|
+
if (options.level) {
|
|
294
|
+
entries = entries.filter(e => e.level === options.level);
|
|
295
|
+
}
|
|
296
|
+
// Apply regex filter
|
|
297
|
+
if (options.filter) {
|
|
298
|
+
const regex = new RegExp(options.filter, 'i');
|
|
299
|
+
entries = entries.filter(e => regex.test(e.message) ||
|
|
300
|
+
regex.test(e.task || '') ||
|
|
301
|
+
regex.test(e.laneName));
|
|
302
|
+
}
|
|
303
|
+
// Apply tail
|
|
304
|
+
if (options.tail && entries.length > options.tail) {
|
|
305
|
+
entries = entries.slice(-options.tail);
|
|
306
|
+
}
|
|
307
|
+
// Get unique lanes for legend
|
|
308
|
+
const lanes = [...new Set(entries.map(e => e.laneName))];
|
|
309
|
+
// Print header
|
|
310
|
+
console.log(`\n${logger.COLORS.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${logger.COLORS.reset}`);
|
|
311
|
+
console.log(`${logger.COLORS.cyan} 🔀 Merged Logs - ${path.basename(runDir)} (${entries.length} entries from ${lanes.length} lanes)${logger.COLORS.reset}`);
|
|
312
|
+
console.log(`${logger.COLORS.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${logger.COLORS.reset}`);
|
|
313
|
+
// Print lane legend
|
|
314
|
+
console.log('\n Lanes: ' + lanes.map((lane, i) => {
|
|
315
|
+
const color = getLaneColor(lane, lanes.indexOf(lane));
|
|
316
|
+
return `${color}■${logger.COLORS.reset} ${lane}`;
|
|
317
|
+
}).join(' '));
|
|
318
|
+
console.log('');
|
|
319
|
+
// Format output based on format option
|
|
320
|
+
if (options.format === 'json') {
|
|
321
|
+
console.log(JSON.stringify(entries, null, 2));
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
// Display entries
|
|
325
|
+
for (const entry of entries) {
|
|
326
|
+
const ts = new Date(entry.timestamp).toLocaleTimeString('en-US', { hour12: false });
|
|
327
|
+
const levelColor = getLevelColor(entry.level);
|
|
328
|
+
const laneColor = entry.laneColor;
|
|
329
|
+
const lanePad = entry.laneName.substring(0, 12).padEnd(12);
|
|
330
|
+
const levelPad = entry.level.toUpperCase().padEnd(6);
|
|
331
|
+
// Skip session entries for cleaner output unless they're important
|
|
332
|
+
if (entry.level === 'session' && entry.message === 'Session started') {
|
|
333
|
+
console.log(`${logger.COLORS.gray}[${ts}]${logger.COLORS.reset} ${laneColor}[${lanePad}]${logger.COLORS.reset} ${logger.COLORS.cyan}── Session Started ──${logger.COLORS.reset}`);
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
if (entry.level === 'session' && entry.message === 'Session ended') {
|
|
337
|
+
console.log(`${logger.COLORS.gray}[${ts}]${logger.COLORS.reset} ${laneColor}[${lanePad}]${logger.COLORS.reset} ${logger.COLORS.cyan}── Session Ended ──${logger.COLORS.reset}`);
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
console.log(`${logger.COLORS.gray}[${ts}]${logger.COLORS.reset} ${laneColor}[${lanePad}]${logger.COLORS.reset} ${levelColor}[${levelPad}]${logger.COLORS.reset} ${entry.message}`);
|
|
341
|
+
}
|
|
342
|
+
console.log(`\n${logger.COLORS.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${logger.COLORS.reset}`);
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Follow all lanes in real-time
|
|
346
|
+
*/
|
|
347
|
+
function followAllLogs(runDir, options) {
|
|
348
|
+
const lanes = listLanes(runDir);
|
|
349
|
+
if (lanes.length === 0) {
|
|
350
|
+
console.log('No lanes found.');
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
// Track last read position for each lane
|
|
354
|
+
const lastPositions = {};
|
|
355
|
+
const laneColors = {};
|
|
356
|
+
lanes.forEach((lane, index) => {
|
|
357
|
+
lastPositions[lane] = 0;
|
|
358
|
+
laneColors[lane] = getLaneColor(lane, index);
|
|
359
|
+
});
|
|
360
|
+
// Print header
|
|
361
|
+
console.log(`\n${logger.COLORS.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${logger.COLORS.reset}`);
|
|
362
|
+
console.log(`${logger.COLORS.cyan} 🔴 Following All Lanes - ${path.basename(runDir)} (Ctrl+C to stop)${logger.COLORS.reset}`);
|
|
363
|
+
console.log(`${logger.COLORS.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${logger.COLORS.reset}`);
|
|
364
|
+
console.log('\n Lanes: ' + lanes.map((lane, i) => {
|
|
365
|
+
return `${laneColors[lane]}■${logger.COLORS.reset} ${lane}`;
|
|
366
|
+
}).join(' '));
|
|
367
|
+
console.log('');
|
|
368
|
+
const checkInterval = setInterval(() => {
|
|
369
|
+
const newEntries = [];
|
|
370
|
+
for (const lane of lanes) {
|
|
371
|
+
const laneDir = path.join(runDir, 'lanes', lane);
|
|
372
|
+
const jsonLogPath = path.join(laneDir, 'terminal.jsonl');
|
|
373
|
+
if (!fs.existsSync(jsonLogPath))
|
|
374
|
+
continue;
|
|
375
|
+
try {
|
|
376
|
+
const stats = fs.statSync(jsonLogPath);
|
|
377
|
+
if (stats.size > lastPositions[lane]) {
|
|
378
|
+
const fd = fs.openSync(jsonLogPath, 'r');
|
|
379
|
+
const buffer = Buffer.alloc(stats.size - lastPositions[lane]);
|
|
380
|
+
fs.readSync(fd, buffer, 0, buffer.length, lastPositions[lane]);
|
|
381
|
+
fs.closeSync(fd);
|
|
382
|
+
const content = buffer.toString();
|
|
383
|
+
const lines = content.split('\n').filter(l => l.trim());
|
|
384
|
+
for (const line of lines) {
|
|
385
|
+
try {
|
|
386
|
+
const entry = JSON.parse(line);
|
|
387
|
+
newEntries.push({
|
|
388
|
+
...entry,
|
|
389
|
+
laneName: lane,
|
|
390
|
+
laneColor: laneColors[lane],
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
catch {
|
|
394
|
+
// Skip invalid lines
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
lastPositions[lane] = stats.size;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
catch {
|
|
401
|
+
// Ignore errors
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
// Sort new entries by timestamp
|
|
405
|
+
newEntries.sort((a, b) => {
|
|
406
|
+
const timeA = new Date(a.timestamp).getTime();
|
|
407
|
+
const timeB = new Date(b.timestamp).getTime();
|
|
408
|
+
return timeA - timeB;
|
|
409
|
+
});
|
|
410
|
+
// Apply filters and display
|
|
411
|
+
for (let entry of newEntries) {
|
|
412
|
+
// Apply level filter
|
|
413
|
+
if (options.level && entry.level !== options.level)
|
|
414
|
+
continue;
|
|
415
|
+
// Apply regex filter
|
|
416
|
+
if (options.filter) {
|
|
417
|
+
const regex = new RegExp(options.filter, 'i');
|
|
418
|
+
if (!regex.test(entry.message) && !regex.test(entry.task || '') && !regex.test(entry.laneName)) {
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
const ts = new Date(entry.timestamp).toLocaleTimeString('en-US', { hour12: false });
|
|
423
|
+
const levelColor = getLevelColor(entry.level);
|
|
424
|
+
const lanePad = entry.laneName.substring(0, 12).padEnd(12);
|
|
425
|
+
const levelPad = entry.level.toUpperCase().padEnd(6);
|
|
426
|
+
// Skip verbose session entries
|
|
427
|
+
if (entry.level === 'session') {
|
|
428
|
+
if (entry.message === 'Session started') {
|
|
429
|
+
console.log(`${logger.COLORS.gray}[${ts}]${logger.COLORS.reset} ${entry.laneColor}[${lanePad}]${logger.COLORS.reset} ${logger.COLORS.cyan}── Session Started ──${logger.COLORS.reset}`);
|
|
430
|
+
}
|
|
431
|
+
else if (entry.message === 'Session ended') {
|
|
432
|
+
console.log(`${logger.COLORS.gray}[${ts}]${logger.COLORS.reset} ${entry.laneColor}[${lanePad}]${logger.COLORS.reset} ${logger.COLORS.cyan}── Session Ended ──${logger.COLORS.reset}`);
|
|
433
|
+
}
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
console.log(`${logger.COLORS.gray}[${ts}]${logger.COLORS.reset} ${entry.laneColor}[${lanePad}]${logger.COLORS.reset} ${levelColor}[${levelPad}]${logger.COLORS.reset} ${entry.message}`);
|
|
437
|
+
}
|
|
438
|
+
}, 100);
|
|
439
|
+
// Handle Ctrl+C
|
|
440
|
+
process.on('SIGINT', () => {
|
|
441
|
+
clearInterval(checkInterval);
|
|
442
|
+
console.log('\n\nStopped following logs.');
|
|
443
|
+
process.exit(0);
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Export merged logs to various formats
|
|
448
|
+
*/
|
|
449
|
+
function exportMergedLogs(runDir, format, outputPath) {
|
|
450
|
+
let entries = readAllLaneLogs(runDir);
|
|
451
|
+
let output = '';
|
|
452
|
+
switch (format) {
|
|
453
|
+
case 'json':
|
|
454
|
+
output = JSON.stringify(entries, null, 2);
|
|
455
|
+
break;
|
|
456
|
+
case 'markdown':
|
|
457
|
+
output = exportMergedToMarkdown(entries, runDir);
|
|
458
|
+
break;
|
|
459
|
+
case 'html':
|
|
460
|
+
output = exportMergedToHtml(entries, runDir);
|
|
461
|
+
break;
|
|
462
|
+
default:
|
|
463
|
+
// Text format
|
|
464
|
+
for (const entry of entries) {
|
|
465
|
+
const ts = new Date(entry.timestamp).toISOString();
|
|
466
|
+
output += `[${ts}] [${entry.laneName}] [${entry.level.toUpperCase()}] ${entry.message}\n`;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
if (outputPath) {
|
|
470
|
+
fs.writeFileSync(outputPath, output, 'utf8');
|
|
471
|
+
}
|
|
472
|
+
return output;
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Export merged logs to Markdown
|
|
476
|
+
*/
|
|
477
|
+
function exportMergedToMarkdown(entries, runDir) {
|
|
478
|
+
const lanes = [...new Set(entries.map(e => e.laneName))];
|
|
479
|
+
let md = `# CursorFlow Merged Logs\n\n`;
|
|
480
|
+
md += `**Run:** ${path.basename(runDir)}\n`;
|
|
481
|
+
md += `**Lanes:** ${lanes.join(', ')}\n`;
|
|
482
|
+
md += `**Entries:** ${entries.length}\n\n`;
|
|
483
|
+
md += `## Timeline\n\n`;
|
|
484
|
+
md += '| Time | Lane | Level | Message |\n';
|
|
485
|
+
md += '|------|------|-------|--------|\n';
|
|
486
|
+
for (const entry of entries) {
|
|
487
|
+
const ts = new Date(entry.timestamp).toLocaleTimeString();
|
|
488
|
+
const message = entry.message.replace(/\|/g, '\\|').substring(0, 80);
|
|
489
|
+
md += `| ${ts} | ${entry.laneName} | ${entry.level} | ${message} |\n`;
|
|
490
|
+
}
|
|
491
|
+
return md;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Export merged logs to HTML
|
|
495
|
+
*/
|
|
496
|
+
function exportMergedToHtml(entries, runDir) {
|
|
497
|
+
const lanes = [...new Set(entries.map(e => e.laneName))];
|
|
498
|
+
let html = `<!DOCTYPE html>
|
|
499
|
+
<html>
|
|
500
|
+
<head>
|
|
501
|
+
<title>CursorFlow Merged Logs - ${path.basename(runDir)}</title>
|
|
502
|
+
<style>
|
|
503
|
+
body { font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace; margin: 20px; background: #1e1e1e; color: #d4d4d4; }
|
|
504
|
+
h1, h2 { color: #569cd6; }
|
|
505
|
+
.legend { margin: 10px 0; padding: 10px; background: #252526; border-radius: 4px; }
|
|
506
|
+
.legend-item { display: inline-block; margin-right: 15px; }
|
|
507
|
+
.legend-color { display: inline-block; width: 12px; height: 12px; margin-right: 5px; border-radius: 2px; }
|
|
508
|
+
.entry { padding: 4px 8px; margin: 2px 0; border-radius: 4px; display: flex; }
|
|
509
|
+
.entry .time { color: #808080; width: 80px; flex-shrink: 0; }
|
|
510
|
+
.entry .lane { width: 120px; flex-shrink: 0; font-weight: bold; }
|
|
511
|
+
.entry .level { width: 70px; flex-shrink: 0; }
|
|
512
|
+
.entry .message { flex: 1; white-space: pre-wrap; }
|
|
513
|
+
.entry.stdout { background: #252526; }
|
|
514
|
+
.entry.stderr { background: #3c1f1f; }
|
|
515
|
+
.entry.stderr .level { color: #f48771; }
|
|
516
|
+
.entry.info { background: #1e3a5f; }
|
|
517
|
+
.entry.error { background: #5f1e1e; }
|
|
518
|
+
.entry.session { background: #1e4620; color: #6a9955; }
|
|
519
|
+
</style>
|
|
520
|
+
</head>
|
|
521
|
+
<body>
|
|
522
|
+
<h1>🔀 CursorFlow Merged Logs</h1>
|
|
523
|
+
<p><strong>Run:</strong> ${path.basename(runDir)} | <strong>Entries:</strong> ${entries.length}</p>
|
|
524
|
+
|
|
525
|
+
<div class="legend">
|
|
526
|
+
<strong>Lanes:</strong> `;
|
|
527
|
+
const colors = ['#5dade2', '#f39c12', '#58d68d', '#af7ac5', '#48c9b0', '#f7dc6f', '#bb8fce', '#76d7c4'];
|
|
528
|
+
lanes.forEach((lane, i) => {
|
|
529
|
+
const color = colors[i % colors.length];
|
|
530
|
+
html += `<span class="legend-item"><span class="legend-color" style="background: ${color}"></span>${lane}</span>`;
|
|
531
|
+
});
|
|
532
|
+
html += `</div>\n`;
|
|
533
|
+
for (const entry of entries) {
|
|
534
|
+
const ts = new Date(entry.timestamp).toLocaleTimeString();
|
|
535
|
+
const laneIndex = lanes.indexOf(entry.laneName);
|
|
536
|
+
const color = colors[laneIndex % colors.length];
|
|
537
|
+
html += ` <div class="entry ${entry.level}">
|
|
538
|
+
<span class="time">${ts}</span>
|
|
539
|
+
<span class="lane" style="color: ${color}">${entry.laneName}</span>
|
|
540
|
+
<span class="level">[${entry.level.toUpperCase()}]</span>
|
|
541
|
+
<span class="message">${escapeHtml(entry.message)}</span>
|
|
542
|
+
</div>\n`;
|
|
543
|
+
}
|
|
544
|
+
html += '</body></html>';
|
|
545
|
+
return html;
|
|
546
|
+
}
|
|
547
|
+
function escapeHtml(text) {
|
|
548
|
+
return text
|
|
549
|
+
.replace(/&/g, '&')
|
|
550
|
+
.replace(/</g, '<')
|
|
551
|
+
.replace(/>/g, '>')
|
|
552
|
+
.replace(/"/g, '"')
|
|
553
|
+
.replace(/'/g, ''');
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Follow logs in real-time
|
|
557
|
+
*/
|
|
558
|
+
function followLogs(laneDir, options) {
|
|
559
|
+
let logFile;
|
|
560
|
+
const readableLog = path.join(laneDir, 'terminal-readable.log');
|
|
561
|
+
const rawLog = path.join(laneDir, 'terminal-raw.log');
|
|
562
|
+
const cleanLog = path.join(laneDir, 'terminal.log');
|
|
563
|
+
if (options.raw) {
|
|
564
|
+
logFile = rawLog;
|
|
565
|
+
}
|
|
566
|
+
else if (options.clean) {
|
|
567
|
+
logFile = cleanLog;
|
|
568
|
+
}
|
|
569
|
+
else if (options.readable && fs.existsSync(readableLog)) {
|
|
570
|
+
logFile = readableLog;
|
|
571
|
+
}
|
|
572
|
+
else {
|
|
573
|
+
// Default or fallback to clean log
|
|
574
|
+
logFile = cleanLog;
|
|
575
|
+
}
|
|
576
|
+
if (!fs.existsSync(logFile)) {
|
|
577
|
+
console.log('Waiting for log file...');
|
|
578
|
+
}
|
|
579
|
+
let lastSize = 0;
|
|
580
|
+
try {
|
|
581
|
+
lastSize = fs.existsSync(logFile) ? fs.statSync(logFile).size : 0;
|
|
582
|
+
}
|
|
583
|
+
catch {
|
|
584
|
+
// Ignore
|
|
585
|
+
}
|
|
586
|
+
console.log(`${logger.COLORS.cyan}Following ${logFile}... (Ctrl+C to stop)${logger.COLORS.reset}\n`);
|
|
587
|
+
const checkInterval = setInterval(() => {
|
|
588
|
+
try {
|
|
589
|
+
if (!fs.existsSync(logFile))
|
|
590
|
+
return;
|
|
591
|
+
const stats = fs.statSync(logFile);
|
|
592
|
+
if (stats.size > lastSize) {
|
|
593
|
+
const fd = fs.openSync(logFile, 'r');
|
|
594
|
+
const buffer = Buffer.alloc(stats.size - lastSize);
|
|
595
|
+
fs.readSync(fd, buffer, 0, buffer.length, lastSize);
|
|
596
|
+
fs.closeSync(fd);
|
|
597
|
+
let content = buffer.toString();
|
|
598
|
+
// Apply filter
|
|
599
|
+
if (options.filter) {
|
|
600
|
+
const regex = new RegExp(options.filter, 'i');
|
|
601
|
+
const lines = content.split('\n');
|
|
602
|
+
content = lines.filter(line => regex.test(line)).join('\n');
|
|
603
|
+
}
|
|
604
|
+
// Clean ANSI if needed (unless raw mode)
|
|
605
|
+
if (!options.raw) {
|
|
606
|
+
content = (0, enhanced_logger_1.stripAnsi)(content);
|
|
607
|
+
}
|
|
608
|
+
if (content.trim()) {
|
|
609
|
+
process.stdout.write(content);
|
|
610
|
+
}
|
|
611
|
+
lastSize = stats.size;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
catch (e) {
|
|
615
|
+
// Ignore errors (file might be rotating)
|
|
616
|
+
}
|
|
617
|
+
}, 100);
|
|
618
|
+
// Handle Ctrl+C
|
|
619
|
+
process.on('SIGINT', () => {
|
|
620
|
+
clearInterval(checkInterval);
|
|
621
|
+
console.log('\n\nStopped following logs.');
|
|
622
|
+
process.exit(0);
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Display logs summary for all lanes
|
|
627
|
+
*/
|
|
628
|
+
function displaySummary(runDir) {
|
|
629
|
+
const lanes = listLanes(runDir);
|
|
630
|
+
console.log(`\n${logger.COLORS.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${logger.COLORS.reset}`);
|
|
631
|
+
console.log(`${logger.COLORS.cyan} 📋 Logs Summary - ${path.basename(runDir)}${logger.COLORS.reset}`);
|
|
632
|
+
console.log(`${logger.COLORS.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${logger.COLORS.reset}\n`);
|
|
633
|
+
if (lanes.length === 0) {
|
|
634
|
+
console.log(' No lanes found.');
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
for (const lane of lanes) {
|
|
638
|
+
const laneDir = path.join(runDir, 'lanes', lane);
|
|
639
|
+
const cleanLog = path.join(laneDir, 'terminal.log');
|
|
640
|
+
const rawLog = path.join(laneDir, 'terminal-raw.log');
|
|
641
|
+
const jsonLog = path.join(laneDir, 'terminal.jsonl');
|
|
642
|
+
const readableLog = path.join(laneDir, 'terminal-readable.log');
|
|
643
|
+
console.log(` ${logger.COLORS.green}📁 ${lane}${logger.COLORS.reset}`);
|
|
644
|
+
if (fs.existsSync(cleanLog)) {
|
|
645
|
+
const stats = fs.statSync(cleanLog);
|
|
646
|
+
console.log(` └─ terminal.log ${formatSize(stats.size)}`);
|
|
647
|
+
}
|
|
648
|
+
if (fs.existsSync(rawLog)) {
|
|
649
|
+
const stats = fs.statSync(rawLog);
|
|
650
|
+
console.log(` └─ terminal-raw.log ${formatSize(stats.size)}`);
|
|
651
|
+
}
|
|
652
|
+
if (fs.existsSync(readableLog)) {
|
|
653
|
+
const stats = fs.statSync(readableLog);
|
|
654
|
+
console.log(` └─ terminal-readable.log ${formatSize(stats.size)} ${logger.COLORS.yellow}(parsed AI output)${logger.COLORS.reset}`);
|
|
655
|
+
}
|
|
656
|
+
if (fs.existsSync(jsonLog)) {
|
|
657
|
+
const stats = fs.statSync(jsonLog);
|
|
658
|
+
const entries = (0, enhanced_logger_1.readJsonLog)(jsonLog);
|
|
659
|
+
const errors = entries.filter(e => e.level === 'error' || e.level === 'stderr').length;
|
|
660
|
+
console.log(` └─ terminal.jsonl ${formatSize(stats.size)} (${entries.length} entries, ${errors} errors)`);
|
|
661
|
+
}
|
|
662
|
+
console.log('');
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Format file size
|
|
667
|
+
*/
|
|
668
|
+
function formatSize(bytes) {
|
|
669
|
+
if (bytes < 1024)
|
|
670
|
+
return `${bytes} B`;
|
|
671
|
+
if (bytes < 1024 * 1024)
|
|
672
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
673
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* Main logs command
|
|
677
|
+
*/
|
|
678
|
+
async function logs(args) {
|
|
679
|
+
const options = parseArgs(args);
|
|
680
|
+
if (options.help) {
|
|
681
|
+
printHelp();
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
const config = (0, config_1.loadConfig)();
|
|
685
|
+
// Find run directory
|
|
686
|
+
let runDir = options.runDir;
|
|
687
|
+
if (!runDir || runDir === 'latest') {
|
|
688
|
+
runDir = findLatestRunDir(config.logsDir) || undefined;
|
|
689
|
+
if (!runDir) {
|
|
690
|
+
throw new Error('No run directories found');
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
if (!fs.existsSync(runDir)) {
|
|
694
|
+
throw new Error(`Run directory not found: ${runDir}`);
|
|
695
|
+
}
|
|
696
|
+
// Handle --all option (view all lanes merged)
|
|
697
|
+
if (options.all) {
|
|
698
|
+
// Handle follow mode for all lanes
|
|
699
|
+
if (options.follow) {
|
|
700
|
+
followAllLogs(runDir, options);
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
// Handle export for all lanes
|
|
704
|
+
if (options.output) {
|
|
705
|
+
exportMergedLogs(runDir, options.format, options.output);
|
|
706
|
+
console.log(`Exported merged logs to: ${options.output}`);
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
// Display merged logs
|
|
710
|
+
if (options.format === 'json') {
|
|
711
|
+
const entries = readAllLaneLogs(runDir);
|
|
712
|
+
console.log(JSON.stringify(entries, null, 2));
|
|
713
|
+
}
|
|
714
|
+
else if (options.format === 'markdown' || options.format === 'html') {
|
|
715
|
+
const content = exportMergedLogs(runDir, options.format);
|
|
716
|
+
console.log(content);
|
|
717
|
+
}
|
|
718
|
+
else {
|
|
719
|
+
displayMergedLogs(runDir, options);
|
|
720
|
+
}
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
// If no lane specified, show summary
|
|
724
|
+
if (!options.lane) {
|
|
725
|
+
displaySummary(runDir);
|
|
726
|
+
console.log(`${logger.COLORS.gray}Use --lane <name> to view logs (default: readable), --clean for terminal logs, or --all to view all lanes merged${logger.COLORS.reset}`);
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
// Find lane directory
|
|
730
|
+
const laneDir = path.join(runDir, 'lanes', options.lane);
|
|
731
|
+
if (!fs.existsSync(laneDir)) {
|
|
732
|
+
const lanes = listLanes(runDir);
|
|
733
|
+
throw new Error(`Lane not found: ${options.lane}\nAvailable lanes: ${lanes.join(', ')}`);
|
|
734
|
+
}
|
|
735
|
+
// Handle follow mode
|
|
736
|
+
if (options.follow) {
|
|
737
|
+
followLogs(laneDir, options);
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
// Handle export
|
|
741
|
+
if (options.output) {
|
|
742
|
+
const content = (0, enhanced_logger_1.exportLogs)(laneDir, options.format, options.output);
|
|
743
|
+
console.log(`Exported logs to: ${options.output}`);
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
// Display logs
|
|
747
|
+
if (options.format === 'json' || options.level) {
|
|
748
|
+
displayJsonLogs(laneDir, options);
|
|
749
|
+
}
|
|
750
|
+
else if (options.format === 'markdown' || options.format === 'html') {
|
|
751
|
+
const content = (0, enhanced_logger_1.exportLogs)(laneDir, options.format);
|
|
752
|
+
console.log(content);
|
|
753
|
+
}
|
|
754
|
+
else {
|
|
755
|
+
displayTextLogs(laneDir, options);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
module.exports = logs;
|
|
759
|
+
//# sourceMappingURL=logs.js.map
|