fix: require observer workbench readiness (#688)
Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
@@ -7866,7 +7866,7 @@ function renderWebObserveCollectTable(value: Record<string, unknown>): string {
|
||||
if (jsonlTail.length > 0) {
|
||||
lines.push(
|
||||
"JSONL tail:",
|
||||
webObserveTable(["TS", "TYPE", "COMMAND", "ATTEMPTS", "READY", "DOM", "PATH", "MESSAGE"], jsonlTail.map((item) => {
|
||||
webObserveTable(["TS", "ROLE", "TYPE", "COMMAND", "ATTEMPTS", "READY", "DOM", "PATH", "MESSAGE"], jsonlTail.map((item) => {
|
||||
const error = record(item.error);
|
||||
const attempts = Array.isArray(error.attempts) ? error.attempts : [];
|
||||
const lastAttempt = attempts.length > 0 ? record(attempts[attempts.length - 1]) : {};
|
||||
@@ -7881,6 +7881,7 @@ function renderWebObserveCollectTable(value: Record<string, unknown>): string {
|
||||
].join(" ");
|
||||
return [
|
||||
webObserveShort(webObserveText(item.ts), 24),
|
||||
webObserveShort(webObserveText(item.pageRole ?? item.role), 12),
|
||||
webObserveShort(webObserveText(item.type), 20),
|
||||
webObserveShort(webObserveText(item.commandId), 28),
|
||||
webObserveText(attempts.length),
|
||||
|
||||
@@ -76,7 +76,13 @@ try {
|
||||
await runControlCommand({ id: "startup-goto", type: "goto", path: targetPath, createdAt: new Date().toISOString(), source: "startup" }, async () => gotoTarget(targetPath));
|
||||
observerPage = await context.newPage();
|
||||
attachPassiveListeners(observerPage, "observer", observerPageId);
|
||||
await runControlCommand({ id: "startup-observer-goto", type: "observerGoto", path: targetPath, createdAt: new Date().toISOString(), source: "startup" }, async () => syncObserverPageToControlSession("startup"));
|
||||
await runControlCommand({ id: "startup-observer-goto", type: "observerGoto", path: targetPath, createdAt: new Date().toISOString(), source: "startup" }, async () => {
|
||||
const result = await syncObserverPageToControlSession("startup");
|
||||
if (!result?.ok) {
|
||||
throw Object.assign(new Error("observer startup did not reach the target workbench route: " + (result?.failureKind || result?.reason || "observer-not-ready")), { details: result });
|
||||
}
|
||||
return result;
|
||||
});
|
||||
terminalStatus = "running";
|
||||
await writeManifest({ status: "running", auth: publicAuth(auth) });
|
||||
await writeHeartbeat({ status: "running" });
|
||||
@@ -269,18 +275,24 @@ async function syncObserverPageToControlSession(reason, explicitSessionId = null
|
||||
const snapshot = await workbenchSessionSnapshot();
|
||||
const sessionId = explicitSessionId || snapshot?.activeSessionId || snapshot?.routeSessionId || routeSessionIdFromUrl(currentPageUrl());
|
||||
const target = sessionId ? "/workbench/sessions/" + encodeURIComponent(sessionId) : targetPath;
|
||||
const targetUrl = new URL(target, baseUrl).toString();
|
||||
const beforeUrl = pageUrl(observerPage);
|
||||
const beforeSessionId = routeSessionIdFromUrl(beforeUrl);
|
||||
if (sessionId && beforeSessionId === sessionId && !forceRefresh) return { ok: true, reason, changed: false, observerRoundTrip: false, sessionId, beforeUrl, afterUrl: beforeUrl, pageRole: "observer", pageId: observerPageId };
|
||||
let status = null;
|
||||
let statusText = null;
|
||||
const response = await observerPage.goto(new URL(target, baseUrl).toString(), { waitUntil: "domcontentloaded", timeout: 45000 }).catch((error) => ({ observerGotoError: errorSummary(error) }));
|
||||
const response = await observerPage.goto(targetUrl, { waitUntil: "domcontentloaded", timeout: 45000 }).catch((error) => ({ observerGotoError: errorSummary(error) }));
|
||||
if (response?.observerGotoError) return { ok: false, reason, changed: false, sessionId: sessionId ?? null, targetPath: target, beforeUrl, afterUrl: pageUrl(observerPage), pageRole: "observer", pageId: observerPageId, error: response.observerGotoError, valuesRedacted: true };
|
||||
status = typeof response?.status === "function" ? response.status() : null;
|
||||
statusText = typeof response?.statusText === "function" ? response.statusText() : null;
|
||||
const readiness = await waitForTargetPageReady(observerPage, targetUrl, { timeoutMs: 15000 });
|
||||
if (!readiness.ok) {
|
||||
lastObserverRefreshAtMs = Date.now();
|
||||
return { ok: false, reason, changed: true, observerRoundTrip: forceRefresh, sessionId: sessionId ?? null, targetPath: target, beforeUrl, afterUrl: pageUrl(observerPage), pageRole: "observer", pageId: observerPageId, httpStatus: status, statusText, readiness, hydration: null, failureKind: readiness.reason || "observer-target-not-ready", valuesRedacted: true };
|
||||
}
|
||||
const hydration = await waitForWorkbenchSessionHydrated(observerPage, sessionId, { timeoutMs: 15000 });
|
||||
lastObserverRefreshAtMs = Date.now();
|
||||
return { ok: true, reason, changed: true, observerRoundTrip: forceRefresh, sessionId: sessionId ?? null, targetPath: target, beforeUrl, afterUrl: pageUrl(observerPage), pageRole: "observer", pageId: observerPageId, httpStatus: status, statusText, hydration, valuesRedacted: true };
|
||||
return { ok: hydration.ok === true, reason, changed: true, observerRoundTrip: forceRefresh, sessionId: sessionId ?? null, targetPath: target, beforeUrl, afterUrl: pageUrl(observerPage), pageRole: "observer", pageId: observerPageId, httpStatus: status, statusText, readiness, hydration, failureKind: hydration.ok === true ? null : hydration.reason || "observer-session-hydration-failed", valuesRedacted: true };
|
||||
}
|
||||
|
||||
async function waitForWorkbenchSessionHydrated(targetPage, sessionId, options = {}) {
|
||||
@@ -291,8 +303,9 @@ async function waitForWorkbenchSessionHydrated(targetPage, sessionId, options =
|
||||
while (Date.now() <= deadline) {
|
||||
last = await workbenchSessionSnapshot(targetPage);
|
||||
const expected = String(sessionId || "").trim();
|
||||
const routeOk = !expected || last?.routeSessionId === expected;
|
||||
const activeOk = !expected || last?.activeSessionId === expected;
|
||||
const observedPath = safeUrlPath(last?.url || pageUrl(targetPage));
|
||||
const routeOk = expected ? last?.routeSessionId === expected : isWorkbenchPathname(observedPath || "");
|
||||
const activeOk = expected ? last?.activeSessionId === expected : isWorkbenchPathname(observedPath || "");
|
||||
if (routeOk && activeOk) return { ok: true, durationMs: Date.now() - started, snapshot: last, valuesRedacted: true };
|
||||
await targetPage.waitForTimeout(250).catch(() => {});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user