chromancer
Version:
A powerful command-line interface for automating Chrome browser using Playwright. Perfect for web scraping, automation, testing, and browser workflows.
157 lines (156 loc) • 5.96 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.RunLogManager = void 0;
const tslib_1 = require("tslib");
const promises_1 = tslib_1.__importDefault(require("fs/promises"));
const path_1 = tslib_1.__importDefault(require("path"));
const uuid_1 = require("uuid");
class RunLogManager {
logsDir;
constructor(baseDir = '.chromancer') {
this.logsDir = path_1.default.join(baseDir, 'run-logs');
}
async init() {
await promises_1.default.mkdir(this.logsDir, { recursive: true });
}
async createRunLog(executionResult, options) {
const runLog = {
id: (0, uuid_1.v4)(),
workflowId: options.workflowId,
timestamp: Date.now(),
url: options.url,
steps: this.convertStepsToLogs(executionResult.steps),
elapsedMs: executionResult.totalDuration,
domDigest: options.domDigest,
claudeAnalysis: options.claudeAnalysis,
success: executionResult.failedSteps === 0,
failureReason: executionResult.failedSteps > 0
? this.getFailureReason(executionResult.steps)
: undefined
};
await this.saveRunLog(runLog);
return runLog;
}
convertStepsToLogs(steps) {
return steps.map(step => {
const log = {
n: step.stepNumber,
cmd: step.command,
ok: step.success,
duration: step.duration || 0
};
if (!step.success && step.error) {
log.why = step.error;
}
// Extract selector and digest info from evaluate commands
if (step.command === 'evaluate' && step.args) {
log.selector = this.extractSelector(step.args);
if (step.output) {
try {
const result = JSON.parse(step.output);
if (Array.isArray(result)) {
log.digest = {
count: result.length,
samples: result.slice(0, 3).map(item => typeof item === 'string' ? item : JSON.stringify(item))
};
}
else {
log.digest = { count: 1, samples: [step.output.substring(0, 100)] };
}
}
catch {
// For non-JSON output
if (step.output.includes('0 items') || step.output === '[]') {
log.digest = { count: 0 };
}
}
}
}
return log;
});
}
extractSelector(args) {
if (args.expression) {
// Try to extract selector from expression
const match = args.expression.match(/querySelectorAll\(['"]([^'"]+)['"]\)/);
if (match)
return match[1];
const match2 = args.expression.match(/querySelector\(['"]([^'"]+)['"]\)/);
if (match2)
return match2[1];
}
return undefined;
}
getFailureReason(steps) {
const failedSteps = steps.filter(s => !s.success);
if (failedSteps.length === 0)
return 'Unknown';
const reasons = failedSteps.map(s => `Step ${s.stepNumber} (${s.command}): ${s.error || 'Failed'}`);
return reasons.join('; ');
}
async saveRunLog(runLog) {
const filename = `${runLog.id}.json`;
const filepath = path_1.default.join(this.logsDir, filename);
await promises_1.default.writeFile(filepath, JSON.stringify(runLog, null, 2), 'utf8');
}
async getRunLog(id) {
try {
const filepath = path_1.default.join(this.logsDir, `${id}.json`);
const content = await promises_1.default.readFile(filepath, 'utf8');
return JSON.parse(content);
}
catch {
return null;
}
}
async listRunLogs(workflowId) {
try {
const files = await promises_1.default.readdir(this.logsDir);
const logs = [];
for (const file of files) {
if (file.endsWith('.json')) {
const content = await promises_1.default.readFile(path_1.default.join(this.logsDir, file), 'utf8');
const log = JSON.parse(content);
if (!workflowId || log.workflowId === workflowId) {
logs.push(log);
}
}
}
return logs.sort((a, b) => b.timestamp - a.timestamp);
}
catch {
return [];
}
}
formatRunLogForClaude(runLog) {
const lines = [
`Run ID: ${runLog.id}`,
`URL: ${runLog.url}`,
`Success: ${runLog.success}`,
`Duration: ${runLog.elapsedMs}ms`,
'',
'Steps:'
];
runLog.steps.forEach(step => {
const status = step.ok ? '✅' : '❌';
lines.push(` ${status} Step ${step.n}: ${step.cmd}`);
if (!step.ok && step.why) {
lines.push(` Error: ${step.why}`);
}
if (step.selector) {
lines.push(` Selector: ${step.selector}`);
}
if (step.digest) {
lines.push(` Found: ${step.digest.count} items`);
if (step.digest.samples && step.digest.samples.length > 0) {
lines.push(` Samples: ${step.digest.samples.slice(0, 2).join(', ')}`);
}
}
});
if (runLog.failureReason) {
lines.push('', `Failure: ${runLog.failureReason}`);
}
return lines.join('\n');
}
}
exports.RunLogManager = RunLogManager;