feat: initial public release (MAESTRO)

This commit is contained in:
oss-sync
2026-06-03 05:08:00 +00:00
commit f5c7666f6b
823 changed files with 184150 additions and 0 deletions
+103
View File
@@ -0,0 +1,103 @@
/**
* recording-flush.ts
*
* Called at task end to:
* 1. Flush any buffered recorder actions to recordings/{recordTo}.json.
* 2. If recordTo ends with ".next", compile a candidate patch script at
* scripts/{baseName}.next.js using the source script's description and
* params as hints (self-healing patch staging).
*
* This helper is intentionally non-throwing — all errors are logged and
* swallowed so a flush bug cannot crash the agent loop.
*/
import { readFileSync, writeFileSync, renameSync, existsSync, mkdirSync } from 'fs';
import { join, dirname } from 'path';
import { recorder } from '../engine/browser-recorder.js';
import { parseScript, serializeScript } from './frontmatter.js';
import { compileScriptBody } from './script-compiler.js';
import { resolveUserSubdir } from './paths.js';
import { logger } from '../logger.js';
import type { RecordedAction } from '../engine/browser-recorder.js';
export function flushAndStageRecording(
taskId: string,
ownerId: string | undefined,
userFolderRoot: string,
): void {
if (!ownerId) return;
const recordTo = recorder.recordTo(taskId);
if (!recordTo) return;
const flushedPath = recorder.flush(taskId, userFolderRoot, ownerId);
if (!flushedPath) return;
if (!recordTo.endsWith('.next')) {
logger.debug(`[recording] flushed regular recording: ${recordTo}.json`);
return;
}
// Self-healing patch: compile a candidate .next.js from the flushed trace.
const baseName = recordTo.slice(0, -'.next'.length);
try {
const sourceScriptPath = resolveUserSubdir(
userFolderRoot,
ownerId,
'browser-macros',
`${baseName}.js`,
);
if (!existsSync(sourceScriptPath)) {
logger.warn(
`[recording] cannot stage patch — source script ${baseName}.js missing`,
);
return;
}
const sourceText = readFileSync(sourceScriptPath, 'utf-8');
const sourceParsed = parseScript(sourceText);
const recordingPayload = JSON.parse(readFileSync(flushedPath, 'utf-8')) as {
actions: RecordedAction[];
};
// Compile just the body (we build frontmatter ourselves to control timestamps)
const { body, paramSpecs } = compileScriptBody({
recording: recordingPayload.actions,
description: sourceParsed.frontmatter.description,
sessionProfileId: sourceParsed.frontmatter.sessionProfileId,
// We don't know which fill values map to which params after a recovery session.
// Pass no paramHints so the patch contains literal values; the user will
// manually re-parameterize during diff review.
paramHints: [],
recordingSource: `${recordTo}.json`,
});
const now = new Date().toISOString();
const patched = serializeScript({
frontmatter: {
description: sourceParsed.frontmatter.description,
params: paramSpecs,
sessionProfileId: sourceParsed.frontmatter.sessionProfileId,
recordingSource: `${recordTo}.json`,
// Preserve original createdAt; stamp fresh updatedAt.
createdAt: sourceParsed.frontmatter.createdAt ?? now,
updatedAt: now,
},
body,
});
const targetPath = resolveUserSubdir(
userFolderRoot,
ownerId,
'browser-macros',
`${baseName}.next.js`,
);
const tmpPath = `${targetPath}.tmp-${process.pid}-${Date.now()}`;
mkdirSync(dirname(targetPath), { recursive: true });
writeFileSync(tmpPath, patched, { encoding: 'utf-8', mode: 0o600 });
renameSync(tmpPath, targetPath);
logger.info(`[user-folder] staged self-healing patch: browser-macros/${baseName}.next.js`);
} catch (e) {
logger.warn(`[recording] failed to stage patch: ${(e as Error).message}`);
}
}