6771464b30
Co-authored-by: Codex <codex@noreply.local>
74 lines
3.4 KiB
TypeScript
74 lines
3.4 KiB
TypeScript
// SPEC: PJ2026-01040111 长程观测 draft-2026-06-20-p0-passive-web-probe-observer.
|
|
// Responsibility: Extra control actions injected into the pure-client web-probe observer runner.
|
|
|
|
export function nodeWebObserveRunnerCommandActionsSource(): string {
|
|
return String.raw`
|
|
async function cancelRunningTurn() {
|
|
const beforeUrl = currentPageUrl();
|
|
const editor = page.locator("#command-input").last();
|
|
if (await editor.isVisible().catch(() => false)) {
|
|
const tag = await editor.evaluate((element) => element.tagName.toLowerCase()).catch(() => "");
|
|
const editable = await editor.evaluate((element) => element.getAttribute("contenteditable") === "true").catch(() => false);
|
|
if (tag === "textarea" || tag === "input") await editor.fill("");
|
|
else if (editable) {
|
|
await editor.click();
|
|
await page.keyboard.press(process.platform === "darwin" ? "Meta+A" : "Control+A").catch(() => {});
|
|
await page.keyboard.press("Backspace").catch(() => {});
|
|
}
|
|
}
|
|
const cancelSelectors = [
|
|
'button:has-text("取消")',
|
|
'button:has-text("Cancel")',
|
|
'[data-testid*="cancel" i]',
|
|
'[aria-label*="cancel" i]',
|
|
'[aria-label*="取消"]'
|
|
].join(", ");
|
|
const primaryButton = page.locator('#command-submit, [data-testid="command-submit"], [data-testid="composer-submit"], [data-testid="send-command"]').last();
|
|
const explicitCancelButton = page.locator(cancelSelectors).last();
|
|
let button = null;
|
|
if (await primaryButton.isVisible().catch(() => false)) {
|
|
const primaryLooksCancelable = await primaryButton.evaluate((element) => {
|
|
const parts = [
|
|
element.textContent || "",
|
|
element.getAttribute("aria-label") || "",
|
|
element.getAttribute("title") || "",
|
|
element.getAttribute("data-testid") || "",
|
|
element.getAttribute("data-state") || "",
|
|
element.getAttribute("data-action") || "",
|
|
element.className || ""
|
|
];
|
|
return /cancel|取消|停止|stop|abort/i.test(parts.join(" "));
|
|
}).catch(() => false);
|
|
if (primaryLooksCancelable) button = primaryButton;
|
|
}
|
|
if (button === null && await explicitCancelButton.isVisible().catch(() => false)) button = explicitCancelButton;
|
|
if (button === null) {
|
|
return {
|
|
beforeUrl,
|
|
afterUrl: currentPageUrl(),
|
|
cancelSubmit: { status: null, statusText: null, skipped: true, reason: "cancel-button-not-visible", responseObserved: false },
|
|
pageId
|
|
};
|
|
}
|
|
await button.waitFor({ state: "visible", timeout: 15000 });
|
|
const cancelResponsePromise = page.waitForResponse((response) => {
|
|
const request = response.request();
|
|
if (request.method().toUpperCase() !== "POST") return false;
|
|
try {
|
|
const pathname = new URL(response.url()).pathname;
|
|
return pathname === "/v1/agent/chat/cancel" || /\/cancel$/u.test(pathname);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}, { timeout: 10000 }).catch((error) => ({ waitError: errorSummary(error) }));
|
|
await button.click();
|
|
const cancelResponse = await cancelResponsePromise;
|
|
await page.waitForTimeout(500);
|
|
if (cancelResponse?.waitError) {
|
|
return { beforeUrl, afterUrl: currentPageUrl(), cancelSubmit: { status: null, statusText: null, waitError: cancelResponse.waitError, responseObserved: false }, pageId };
|
|
}
|
|
return { beforeUrl, afterUrl: currentPageUrl(), cancelSubmit: { status: cancelResponse.status(), statusText: cancelResponse.statusText(), urlPath: safeUrlPath(cancelResponse.url()), responseObserved: true }, pageId };
|
|
}
|
|
`;
|
|
}
|