feat: refresh observer page during web-probe observe (#669)
Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
@@ -20,6 +20,7 @@ const targetPath = process.env.UNIDESK_WEB_OBSERVE_TARGET_PATH || "/workbench";
|
||||
const sampleIntervalMs = positiveInteger(process.env.UNIDESK_WEB_OBSERVE_SAMPLE_INTERVAL_MS, 5000);
|
||||
const screenshotIntervalMs = positiveInteger(process.env.UNIDESK_WEB_OBSERVE_SCREENSHOT_INTERVAL_MS, 300000);
|
||||
const maxSamples = positiveInteger(process.env.UNIDESK_WEB_OBSERVE_MAX_SAMPLES, 0);
|
||||
const observerRefreshIntervalMs = positiveInteger(process.env.UNIDESK_WEB_OBSERVE_OBSERVER_REFRESH_INTERVAL_MS, 180000);
|
||||
const viewport = parseViewport(process.env.UNIDESK_WEB_OBSERVE_VIEWPORT || "1440x900");
|
||||
const playwrightProxy = proxyConfigFromEnv(baseUrl);
|
||||
const chromiumLaunchOptions = chromiumLaunchOptionsForProxy(playwrightProxy);
|
||||
@@ -48,6 +49,7 @@ let browser;
|
||||
let context;
|
||||
let page;
|
||||
let observerPage;
|
||||
let lastObserverRefreshAtMs = Date.now();
|
||||
let sampleSeq = 0;
|
||||
let commandSeq = 0;
|
||||
let artifactSeq = 0;
|
||||
@@ -119,7 +121,7 @@ async function writeManifest(extra = {}) {
|
||||
network: publicNetwork(playwrightProxy),
|
||||
pageAuthority: { browser: "chromium", context: "shared-auth", pageMode: "dual-control-observer", controlPageId: pageId, observerPageId, continuityBreaksRecorded: true },
|
||||
pageProvenance: compactPageProvenance(currentPageProvenance),
|
||||
sampling: { mode: "passive", sampleIntervalMs, screenshotIntervalMs, maxSamples, observerInitiatedDefault: false, responseBodyReadDefault: false },
|
||||
sampling: { mode: "passive", sampleIntervalMs, screenshotIntervalMs, maxSamples, observerRefreshIntervalMs, observerInitiatedDefault: false, responseBodyReadDefault: false },
|
||||
commandDirs: dirs,
|
||||
artifacts: files,
|
||||
safety: { pureClient: true, inboundApi: false, database: false, queueConsumer: false, k8sWorkload: false, valuesRedacted: true, secretValuesPrinted: false },
|
||||
@@ -141,6 +143,8 @@ async function writeHeartbeat(extra = {}) {
|
||||
baseUrl,
|
||||
currentUrl: currentPageUrl(),
|
||||
observerUrl: pageUrl(observerPage),
|
||||
observerRefreshIntervalMs,
|
||||
lastObserverRefreshAt: Number.isFinite(lastObserverRefreshAtMs) ? new Date(lastObserverRefreshAtMs).toISOString() : null,
|
||||
pageProvenance: compactPageProvenance(currentPageProvenance),
|
||||
sampleSeq,
|
||||
commandSeq,
|
||||
@@ -259,14 +263,15 @@ async function withObserverSync(result, reason) {
|
||||
return { ...result, observer: await syncObserverPageToControlSession(reason, result?.sessionId ?? null) };
|
||||
}
|
||||
|
||||
async function syncObserverPageToControlSession(reason, explicitSessionId = null) {
|
||||
async function syncObserverPageToControlSession(reason, explicitSessionId = null, options = {}) {
|
||||
if (!observerPage || observerPage.isClosed()) return { ok: false, reason, pageRole: "observer", pageId: observerPageId, failureKind: "observer-page-unavailable" };
|
||||
const forceRefresh = options?.forceRefresh === true;
|
||||
const snapshot = await workbenchSessionSnapshot();
|
||||
const sessionId = explicitSessionId || snapshot?.activeSessionId || snapshot?.routeSessionId || routeSessionIdFromUrl(currentPageUrl());
|
||||
const target = sessionId ? "/workbench/sessions/" + encodeURIComponent(sessionId) : targetPath;
|
||||
const beforeUrl = pageUrl(observerPage);
|
||||
const beforeSessionId = routeSessionIdFromUrl(beforeUrl);
|
||||
if (sessionId && beforeSessionId === sessionId) return { ok: true, reason, changed: false, sessionId, beforeUrl, afterUrl: beforeUrl, pageRole: "observer", pageId: observerPageId };
|
||||
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) }));
|
||||
@@ -274,7 +279,24 @@ async function syncObserverPageToControlSession(reason, explicitSessionId = null
|
||||
status = typeof response?.status === "function" ? response.status() : null;
|
||||
statusText = typeof response?.statusText === "function" ? response.statusText() : null;
|
||||
await observerPage.waitForTimeout(1000);
|
||||
return { ok: true, reason, changed: true, sessionId: sessionId ?? null, targetPath: target, beforeUrl, afterUrl: pageUrl(observerPage), pageRole: "observer", pageId: observerPageId, httpStatus: status, statusText, valuesRedacted: true };
|
||||
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, valuesRedacted: true };
|
||||
}
|
||||
|
||||
async function maybeRefreshObserverPage(reason) {
|
||||
if (!observerPage || observerPage.isClosed()) return null;
|
||||
if (!observerRefreshIntervalMs || observerRefreshIntervalMs <= 0) return null;
|
||||
if (Date.now() - lastObserverRefreshAtMs < observerRefreshIntervalMs) return null;
|
||||
const result = await syncObserverPageToControlSession("observer-periodic-refresh", null, { forceRefresh: true });
|
||||
await appendJsonl(files.control, eventRecord("observer-periodic-refresh", {
|
||||
pageRole: "observer",
|
||||
pageId: observerPageId,
|
||||
reason,
|
||||
intervalMs: observerRefreshIntervalMs,
|
||||
result,
|
||||
valuesRedacted: true
|
||||
}));
|
||||
return result;
|
||||
}
|
||||
|
||||
async function runControlCommand(command, fn) {
|
||||
@@ -918,6 +940,7 @@ async function preflightSummary() {
|
||||
}
|
||||
|
||||
async function samplePage(reason) {
|
||||
await maybeRefreshObserverPage(reason);
|
||||
const groupSeq = sampleSeq + 1;
|
||||
if (page && !page.isClosed()) await sampleOnePage(page, { reason, groupSeq, pageRole: "control", targetPageId: pageId });
|
||||
if (observerPage && !observerPage.isClosed()) {
|
||||
@@ -3825,7 +3848,7 @@ function compactManifest(value) {
|
||||
|
||||
function compactHeartbeat(value) {
|
||||
if (!value) return null;
|
||||
return { jobId: value.jobId, pid: value.pid, status: value.status, sampleSeq: value.sampleSeq, commandSeq: value.commandSeq, pageId: value.pageId ?? null, observerPageId: value.observerPageId ?? null, currentUrl: value.currentUrl, observerUrl: value.observerUrl ?? null, pageProvenance: value.pageProvenance ?? null, updatedAt: value.updatedAt, uptimeMs: value.uptimeMs };
|
||||
return { jobId: value.jobId, pid: value.pid, status: value.status, sampleSeq: value.sampleSeq, commandSeq: value.commandSeq, pageId: value.pageId ?? null, observerPageId: value.observerPageId ?? null, currentUrl: value.currentUrl, observerUrl: value.observerUrl ?? null, observerRefreshIntervalMs: value.observerRefreshIntervalMs ?? null, lastObserverRefreshAt: value.lastObserverRefreshAt ?? null, pageProvenance: value.pageProvenance ?? null, updatedAt: value.updatedAt, uptimeMs: value.uptimeMs };
|
||||
}
|
||||
|
||||
function renderTurnTimingTable(sampleMetrics) {
|
||||
|
||||
Reference in New Issue
Block a user