fix(web-probe): add observe new session command
This commit is contained in:
@@ -68,6 +68,7 @@ export function hwlabNodeWebProbeHelp(): Record<string, unknown> {
|
||||
"bun scripts/cli.ts hwlab nodes web-probe run --node D601 --lane v03 --url https://hwlab.pikapython.com --fresh-session --message 'ping'",
|
||||
"bun scripts/cli.ts hwlab nodes web-probe script --node D601 --lane v03 --script-file .state/probes/workbench.mjs",
|
||||
"bun scripts/cli.ts hwlab nodes web-probe observe start --node D601 --lane v03 --target-path /workbench --sample-interval-ms 5000",
|
||||
"bun scripts/cli.ts hwlab nodes web-probe observe command webobs-xxxx --type newSession",
|
||||
"bun scripts/cli.ts hwlab nodes web-probe observe command webobs-xxxx --type selectProvider --provider codex-api",
|
||||
"bun scripts/cli.ts hwlab nodes web-probe observe command webobs-xxxx --type sendPrompt --text 'ping'",
|
||||
"bun scripts/cli.ts hwlab nodes web-probe observe status webobs-xxxx",
|
||||
@@ -84,7 +85,7 @@ export function hwlabNodeWebProbeHelp(): Record<string, unknown> {
|
||||
"Issue-ready evidence is available under issueEvidence and summary.issueEvidence; full script report is persisted under probe.reportPath with a SHA-256 fingerprint.",
|
||||
"observe sampling is passive by default: it records DOM summaries and natural page request/response/requestfailed events with observerInitiated=false; it does not actively fetch Workbench APIs, reload, switch sessions, route/intercept, or call repair helpers.",
|
||||
"observe start registers a local UniDesk-side observer id under .state/web-observe/index.json; after start, prefer observe status|command|stop|collect|analyze <id> instead of repeating --node/--lane/--state-dir.",
|
||||
"observe command actions are explicit user/control actions and are appended to control.jsonl; use --type selectProvider/sendPrompt/goto/screenshot/mark/stop and keep prompt text out of issue comments by citing textHash/textBytes.",
|
||||
"observe command actions are explicit user/control actions and are appended to control.jsonl; use --type newSession/selectProvider/sendPrompt/goto/screenshot/mark/stop and keep prompt text out of issue comments by citing textHash/textBytes.",
|
||||
"observe analyze is offline-only: it reads artifact JSONL and writes analysis/report.md plus analysis/report.json without accessing Workbench APIs or driving the browser.",
|
||||
"Use recordStep(name, data) or fetchApiMatrix(paths) to keep structured partial evidence when a later step fails.",
|
||||
"Use reloadStable(), gotoCurrentStable(), or safeReload() for bounded retries around page reload/current-URL navigation jitter such as ERR_NETWORK_CHANGED.",
|
||||
|
||||
@@ -58,7 +58,7 @@ interface NodeWebProbeScriptOptions {
|
||||
}
|
||||
|
||||
type NodeWebProbeObserveAction = "start" | "status" | "command" | "stop" | "collect" | "analyze";
|
||||
type NodeWebProbeObserveCommandType = "login" | "preflight" | "goto" | "sendPrompt" | "selectProvider" | "clickSession" | "screenshot" | "mark" | "stop";
|
||||
type NodeWebProbeObserveCommandType = "login" | "preflight" | "goto" | "newSession" | "sendPrompt" | "selectProvider" | "clickSession" | "screenshot" | "mark" | "stop";
|
||||
|
||||
interface NodeWebProbeObserveOptions {
|
||||
action: "observe";
|
||||
@@ -6152,6 +6152,7 @@ function parseNodeWebProbeObserveCommandType(value: string): NodeWebProbeObserve
|
||||
value === "login"
|
||||
|| value === "preflight"
|
||||
|| value === "goto"
|
||||
|| value === "newSession"
|
||||
|| value === "sendPrompt"
|
||||
|| value === "selectProvider"
|
||||
|| value === "clickSession"
|
||||
@@ -6159,7 +6160,7 @@ function parseNodeWebProbeObserveCommandType(value: string): NodeWebProbeObserve
|
||||
|| value === "mark"
|
||||
|| value === "stop"
|
||||
) return value;
|
||||
throw new Error(`web-probe observe command --type must be login, preflight, goto, sendPrompt, selectProvider, clickSession, screenshot, mark, or stop; got ${value}`);
|
||||
throw new Error(`web-probe observe command --type must be login, preflight, goto, newSession, sendPrompt, selectProvider, clickSession, screenshot, mark, or stop; got ${value}`);
|
||||
}
|
||||
|
||||
function nodeWebProbeAutoCommandTimeoutSeconds(input: {
|
||||
|
||||
@@ -224,6 +224,7 @@ async function processCommand(command) {
|
||||
case "login": return authenticate(context);
|
||||
case "preflight": return preflightSummary();
|
||||
case "goto": return gotoTarget(command.path || command.url || targetPath);
|
||||
case "newSession": return createSessionFromUi();
|
||||
case "sendPrompt": return sendPrompt(String(command.text || ""));
|
||||
case "selectProvider": return selectProvider(String(command.provider || command.value || command.text || ""));
|
||||
case "clickSession": return clickSession(String(command.sessionId || command.value || ""));
|
||||
@@ -286,6 +287,50 @@ async function gotoTarget(rawTarget) {
|
||||
return { beforeUrl, afterUrl: currentPageUrl(), httpStatus: response ? response.status() : null, pageId };
|
||||
}
|
||||
|
||||
async function createSessionFromUi() {
|
||||
const beforeUrl = currentPageUrl();
|
||||
const before = await workbenchSessionSnapshot();
|
||||
const create = page.locator("#session-create").first();
|
||||
await create.waitFor({ state: "visible", timeout: 15000 });
|
||||
await create.click();
|
||||
await page.waitForFunction((initial) => {
|
||||
const activeTab = document.querySelector(".session-tab[data-active='true'], .session-tab[aria-selected='true']");
|
||||
const sessionId = activeTab?.getAttribute("data-session-id") || "";
|
||||
const warning = document.querySelector(".composer-warning")?.textContent?.trim() || "";
|
||||
const input = document.querySelector("#command-input");
|
||||
return Boolean(activeTab && sessionId && sessionId !== initial.sessionId && input && !input.disabled && !warning);
|
||||
}, { sessionId: before?.activeSessionId || "" }, { timeout: 30000 }).catch(() => null);
|
||||
const after = await workbenchSessionSnapshot();
|
||||
return {
|
||||
beforeUrl,
|
||||
afterUrl: currentPageUrl(),
|
||||
ok: Boolean(after?.activeSessionId && after.activeSessionId !== before?.activeSessionId),
|
||||
before,
|
||||
after,
|
||||
sessionId: after?.activeSessionId || null,
|
||||
pageId
|
||||
};
|
||||
}
|
||||
|
||||
async function workbenchSessionSnapshot() {
|
||||
return page.evaluate(() => {
|
||||
const activeTab = document.querySelector(".session-tab[data-active='true'], .session-tab[aria-selected='true']");
|
||||
const routeMatch = window.location.pathname.match(/\/workbench\/sessions\/([^/]+)/u) || window.location.pathname.match(/\/workspace\/sessions\/([^/]+)/u);
|
||||
const input = document.querySelector("#command-input");
|
||||
const warning = document.querySelector(".composer-warning")?.textContent?.trim() || null;
|
||||
return {
|
||||
url: window.location.href,
|
||||
routeSessionId: routeMatch ? decodeURIComponent(routeMatch[1] || "") : null,
|
||||
activeSessionId: activeTab?.getAttribute("data-session-id") || null,
|
||||
activeConversationId: activeTab?.getAttribute("data-conversation-id") || null,
|
||||
activeStatus: activeTab?.getAttribute("data-status") || null,
|
||||
tabCount: document.querySelectorAll(".session-tab").length,
|
||||
composerReady: Boolean(activeTab && input && !input.disabled && !warning),
|
||||
warning
|
||||
};
|
||||
}).catch(() => null);
|
||||
}
|
||||
|
||||
async function sendPrompt(text) {
|
||||
if (text.trim().length === 0) throw new Error("sendPrompt requires non-empty text");
|
||||
const beforeUrl = currentPageUrl();
|
||||
|
||||
Reference in New Issue
Block a user