|
|
|
@@ -390,7 +390,7 @@ async function processCommand(command) {
|
|
|
|
|
case "deleteMdtodoTask": return deleteMdtodoTask(command);
|
|
|
|
|
case "launchWorkbenchFromTask": return withObserverSync(await launchWorkbenchFromTask(command), "launchWorkbenchFromTask");
|
|
|
|
|
case "launchWorkbenchFromMdtodo": return withObserverSync(await launchWorkbenchFromMdtodo(command), "launchWorkbenchFromMdtodo");
|
|
|
|
|
case "screenshot": return captureScreenshot(command.reason || "manual", command.imageType || "png");
|
|
|
|
|
case "screenshot": return captureCommandScreenshot(command);
|
|
|
|
|
case "mark": return { mark: truncate(command.label || command.text || "mark", 200), currentUrl: currentPageUrl(), pageId };
|
|
|
|
|
case "stop": stopping = true; return { stopping: true, currentUrl: currentPageUrl(), pageId };
|
|
|
|
|
default: throw new Error("unsupported observer command type: " + command.type);
|
|
|
|
@@ -1251,6 +1251,28 @@ async function projectManagementReadinessSnapshot(targetPage) {
|
|
|
|
|
}, { selectors }).catch((error) => ({ error: errorSummary(error), valuesRedacted: true }));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function waitForProjectManagementCommandReady(options = {}) {
|
|
|
|
|
const timeoutMs = Number.isFinite(Number(options.timeoutMs)) ? Math.max(1, Number(options.timeoutMs)) : 15000;
|
|
|
|
|
const started = Date.now();
|
|
|
|
|
const deadline = started + timeoutMs;
|
|
|
|
|
let last = null;
|
|
|
|
|
while (Date.now() <= deadline) {
|
|
|
|
|
last = await projectManagementCommandSnapshot();
|
|
|
|
|
const path = String(last?.path || safeUrlPath(currentPageUrl()) || "");
|
|
|
|
|
const baseReady = last?.pageKind === "project-management-mdtodo"
|
|
|
|
|
&& Number(last?.sourceCount || 0) > 0
|
|
|
|
|
&& Number(last?.fileCount || 0) > 0
|
|
|
|
|
&& Number(last?.taskCount || 0) > 0;
|
|
|
|
|
const needsTask = /\/tasks\//u.test(path);
|
|
|
|
|
const taskReady = !needsTask || Boolean(last?.selectedTaskId || last?.selectedTaskRef?.hash || last?.taskBodyVisible === true || last?.launchButtonVisible === true);
|
|
|
|
|
const needsReport = /\/reports\//u.test(path);
|
|
|
|
|
const reportReady = !needsReport || last?.reportPreviewVisible === true || last?.reportFullscreenVisible === true || Number(last?.reportLinkCount || 0) > 0;
|
|
|
|
|
if (baseReady && taskReady && reportReady) return { ok: true, reason: "project-management-command-ready", durationMs: Date.now() - started, snapshot: last, valuesRedacted: true };
|
|
|
|
|
await page.waitForTimeout(250).catch(() => {});
|
|
|
|
|
}
|
|
|
|
|
return { ok: false, reason: "project-management-command-not-ready", durationMs: Date.now() - started, snapshot: last, valuesRedacted: true };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isWorkbenchPathname(value) {
|
|
|
|
|
const pathname = String(value || "");
|
|
|
|
|
return pathname === "/workbench" || pathname === "/workspace" || pathname.startsWith("/workbench/") || pathname.startsWith("/workspace/");
|
|
|
|
@@ -3793,6 +3815,18 @@ async function captureScreenshot(reason, imageType = "png") {
|
|
|
|
|
return artifact;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function captureCommandScreenshot(command) {
|
|
|
|
|
const shouldWaitProject = command.waitProjectManagementReady === true;
|
|
|
|
|
const readiness = shouldWaitProject ? await waitForProjectManagementCommandReady({ timeoutMs: 15000 }) : null;
|
|
|
|
|
if (readiness && readiness.ok !== true) {
|
|
|
|
|
const error = new Error("screenshot project-management readiness wait failed: " + (readiness.reason || "not-ready"));
|
|
|
|
|
error.details = { readiness, currentUrl: currentPageUrl(), pageId, valuesRedacted: true };
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
const artifact = await captureScreenshot(command.reason || command.label || "manual", command.imageType || "png");
|
|
|
|
|
return { ...artifact, readiness, valuesRedacted: true };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function eventRecord(type, data) {
|
|
|
|
|
const clean = sanitize(data) || {};
|
|
|
|
|
return { ts: new Date().toISOString(), type, jobId, pageId: clean.pageId ?? pageId, pageRole: clean.pageRole ?? "control", sampleSeq, commandId: activeCommandId, ...clean };
|
|
|
|
@@ -3838,6 +3872,7 @@ function commandInputSummary(command) {
|
|
|
|
|
expectedSentinelRange: command.expectedSentinelRange || null,
|
|
|
|
|
expectedActionWaitMs: command.expectedActionWaitMs === null || command.expectedActionWaitMs === undefined || command.expectedActionWaitMs === "" ? null : Number(command.expectedActionWaitMs),
|
|
|
|
|
requireComposerReady: command.requireComposerReady === true,
|
|
|
|
|
waitProjectManagementReady: command.waitProjectManagementReady === true,
|
|
|
|
|
findingId: command.findingId || null,
|
|
|
|
|
blocking: command.blocking === true ? true : command.blocking === false ? false : null,
|
|
|
|
|
sourceId: opaque(command.sourceId),
|
|
|
|
|