fix: close mdtodo source modal in web probe
This commit is contained in:
@@ -119,6 +119,7 @@ bun scripts/cli.ts web-probe observe command webobs-xxxx --type gotoProjectMdtod
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type configureMdtodoHwpodSource --hwpod-id d601-f103-v2 --node-id node-d601-f103-v2 --root docs/MDTODO/
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type probeMdtodoSource
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type reindexMdtodoSource
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type closeMdtodoSourceConfig
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type selectMdtodoSource --source-id <opaque-source-id>
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type selectMdtodoFile --file-ref <opaque-file-ref>
|
||||
bun scripts/cli.ts web-probe observe command webobs-xxxx --type selectMdtodoFile --filename <direct-mdtodo-file-name.md>
|
||||
@@ -145,7 +146,7 @@ bun scripts/cli.ts web-probe observe analyze webobs-xxxx
|
||||
- `web-probe script` 不运行默认探针,必须通过 stdin heredoc 或 `--script-file <path>` 提供脚本;只需要 repo-owned 标准 DOM probe 时使用 `web-probe run`。
|
||||
- `web-probe run|script|observe start` 的默认 URL、browser proxy mode、observe/analyze 报警阈值和 project-management 采样/命令 allowlist 必须来自 `config/hwlab-node-lanes.yaml` 的 `webProbe`;需要排除公网/FRP/跨国 proxy 抖动时,在 YAML 里把目标 node/lane 的 `webProbe.defaultOrigin` 配成内部 Service ClusterIP origin,不要在命令行长期手写 `--url` 或裸 Playwright。
|
||||
- `web-probe observe start` 默认是被动观测:记录 DOM 摘要、自然页面 request/response/requestfailed、截图和 performance 样本,不主动 fetch Workbench API、不切换 control session、不拦截路由、不调用 repair helper。长程 Workbench 观测必须保留 control/observer 双页面模型:control 页面执行显式 command,observer 页面只同步到同一 session URL 后被动采样,并按默认 180000ms 周期整页刷新同一 session 来模拟用户往返;周期刷新只作用于 observer,不得改变 control active session 或作为通过条件。两页的 `pageRole`、`pageId`、`sampleGroupSeq` 必须进入样本和 analyzer 报表。任何 `newSession`、`selectProvider`、`sendPrompt`、`steer`、`cancel`、`goto`、`screenshot`、`mark`、`stop` 都必须通过 `observe command` 显式下发,并进入 `control.jsonl`;长 prompt 必须优先用 `sendPrompt --text-stdin` 或 `steer --text-stdin`,不要为了绕开 shell quoting 退回裸 Playwright 或临时脚本。MDTODO 高频操作也必须优先沉淀为 `observe command`;同类动作第二次出现时不要继续写临时 `web-probe script`。
|
||||
- `observe command --type steer` 和 `--type cancel` 是显式用户/control action:steer 复用当前 Workbench composer 的运行中 turn 引导路径,cancel 复用同一 composer 主按钮的取消路径。二者必须进入 `control.jsonl`,不能用后端私有 API、AgentRun direct cancel 或测试后门替代。`configureMdtodoHwpodSource`、`probeMdtodoSource`、`reindexMdtodoSource`、`selectMdtodoSource`、`selectMdtodoFile`、`selectMdtodoTask`、`editMdtodoTaskTitle`、`editMdtodoTaskBody`、`toggleMdtodoTaskStatus`、`addMdtodoSubTask`、`continueMdtodoTask`、`deleteMdtodoTask` 和 `launchWorkbenchFromMdtodo` 也是显式用户/control action,只能使用页面公开 `data-*` id、正式按钮和 YAML 允许的自然 API;它们通过 public source/file/task id 与 Workbench 关联,不能读取内部 store、私有后端或把 MDTODO 页面包含进 Workbench。
|
||||
- `observe command --type steer` 和 `--type cancel` 是显式用户/control action:steer 复用当前 Workbench composer 的运行中 turn 引导路径,cancel 复用同一 composer 主按钮的取消路径。二者必须进入 `control.jsonl`,不能用后端私有 API、AgentRun direct cancel 或测试后门替代。`configureMdtodoHwpodSource`、`probeMdtodoSource`、`reindexMdtodoSource`、`closeMdtodoSourceConfig`、`selectMdtodoSource`、`selectMdtodoFile`、`selectMdtodoTask`、`editMdtodoTaskTitle`、`editMdtodoTaskBody`、`toggleMdtodoTaskStatus`、`addMdtodoSubTask`、`continueMdtodoTask`、`deleteMdtodoTask` 和 `launchWorkbenchFromMdtodo` 也是显式用户/control action,只能使用页面公开 `data-*` id、正式按钮和 YAML 允许的自然 API;它们通过 public source/file/task id 与 Workbench 关联,不能读取内部 store、私有后端或把 MDTODO 页面包含进 Workbench。
|
||||
- `observe collect --view turn-summary` 是第一层 CLI 阅读视图:只从 `samples.jsonl`、`control.jsonl` 和已有 `analysis/report.json` 按需渲染同一 session 的多 turn 摘要,包含用户消息 preview/hash、traceId、状态、耗时/最近更新时间、steer/cancel 标记和 Final Response 摘要。`observe collect --view trace-frame --trace-id <id> --sample-seq <n>` 是第二层 CLI 阅读视图:从同一采样帧渲染单帧 trace 文字截图,并固定输出 `Final Response` 区块。`observe collect --view project-summary|project-mdtodo-summary` 从同一 artifact 渲染项目管理 / MDTODO DOM 采样、Source/File/Task 计数、command/mutation 结果、Workbench launch、捕获到的 `x-hwlab-otel-trace-id` 和 OTel/Tempo drill-down 线索;project collect 的远端 payload 必须保持 bounded compact rows,由本地 renderer 生成表格,避免 `trans` stdout 截断后 JSON parse 失败。collect 视图不是采样器新增保存物,不构成第二事实源。
|
||||
- `observe start/status/command/collect/analyze` 默认输出包含 `Wrapper contract` 区块;该区块证明 Web 哨兵只能 wrap 现有 observe CLI verb、现有 runner/analyzer 和既有 artifact contract,不新增第二套 Playwright runner、analyzer、状态机或私有 web-probe API。
|
||||
- `web-probe sentinel plan|status` 只读取 `observability.webProbe.sentinel.enabled/configRefs` 和 owning YAML,渲染 redacted 配置引用图、文件 hash、缺失字段和跨 ref 冲突;`web-probe sentinel image|control-plane` 继续从 owning YAML 渲染 image、GitOps、Argo 和 manifest 计划,并在远端 publish job 接通前拒绝报告部署 mutation。它不启动浏览器、不读取 Secret 值、不保存采样结果,也不是第二套 runner/analyzer。真正的采样和判定仍以 `observe start|command|collect|analyze` artifacts 为准。
|
||||
|
||||
@@ -206,6 +206,7 @@ lanes:
|
||||
commandAllowlist:
|
||||
- gotoProjectMdtodo
|
||||
- openMdtodoSourceConfig
|
||||
- closeMdtodoSourceConfig
|
||||
- configureMdtodoHwpodSource
|
||||
- probeMdtodoSource
|
||||
- reindexMdtodoSource
|
||||
|
||||
@@ -49,6 +49,7 @@ export function hwlabNodeWebProbeHelp(): Record<string, unknown> {
|
||||
"bun scripts/cli.ts web-probe observe start --node D601 --lane v03 --target-path /workbench --sample-interval-ms 5000",
|
||||
"bun scripts/cli.ts web-probe observe start --node D601 --lane v03 --target-path /projects/mdtodo --sample-interval-ms 5000",
|
||||
"bun scripts/cli.ts web-probe observe command webobs-xxxx --type sendPrompt --text 'ping'",
|
||||
"bun scripts/cli.ts web-probe observe command webobs-xxxx --type closeMdtodoSourceConfig",
|
||||
"bun scripts/cli.ts web-probe observe command webobs-xxxx --type selectMdtodoFile --filename 20260609_频率判断_用户反馈.md",
|
||||
"bun scripts/cli.ts web-probe observe command webobs-xxxx --type openMdtodoReportPreview --task R1 --link R1.1",
|
||||
"bun scripts/cli.ts web-probe observe command webobs-xxxx --type toggleMdtodoReportFullscreen --text toggle",
|
||||
|
||||
@@ -532,7 +532,7 @@ function projectSummaryFromSamples(){
|
||||
const projectSamples=samples.filter((sample)=>sample?.projectManagement&&typeof sample.projectManagement==='object');
|
||||
const latest=projectSamples[projectSamples.length-1]||null;
|
||||
const latestProject=latest?.projectManagement||null;
|
||||
const projectCommandTypes=new Set(['gotoProjectMdtodo','openMdtodoSourceConfig','configureMdtodoHwpodSource','probeMdtodoSource','reindexMdtodoSource','selectProjectSource','selectMdtodoSource','selectMdtodoFile','selectMdtodoTask','expandMdtodoTask','openMdtodoReportPreview','toggleMdtodoReportFullscreen','editMdtodoTaskInline','editMdtodoTaskTitle','editMdtodoTaskBody','toggleMdtodoTaskStatus','addMdtodoRootTask','addMdtodoSubTask','continueMdtodoTask','deleteMdtodoTask','launchWorkbenchFromTask','launchWorkbenchFromMdtodo']);
|
||||
const projectCommandTypes=new Set(['gotoProjectMdtodo','openMdtodoSourceConfig','closeMdtodoSourceConfig','configureMdtodoHwpodSource','probeMdtodoSource','reindexMdtodoSource','selectProjectSource','selectMdtodoSource','selectMdtodoFile','selectMdtodoTask','expandMdtodoTask','openMdtodoReportPreview','toggleMdtodoReportFullscreen','editMdtodoTaskInline','editMdtodoTaskTitle','editMdtodoTaskBody','toggleMdtodoTaskStatus','addMdtodoRootTask','addMdtodoSubTask','continueMdtodoTask','deleteMdtodoTask','launchWorkbenchFromTask','launchWorkbenchFromMdtodo']);
|
||||
const mutationTypes=new Set(['configureMdtodoHwpodSource','probeMdtodoSource','reindexMdtodoSource','editMdtodoTaskInline','editMdtodoTaskTitle','editMdtodoTaskBody','toggleMdtodoTaskStatus','addMdtodoRootTask','addMdtodoSubTask','continueMdtodoTask','deleteMdtodoTask']);
|
||||
const commandRows=control.filter((item)=>projectCommandTypes.has(item.type)&&(item.phase==='completed'||item.phase==='failed')).map((item)=>{
|
||||
const detail=item.detail&&typeof item.detail==='object'?item.detail:{};
|
||||
|
||||
@@ -364,6 +364,7 @@ async function processCommand(command) {
|
||||
case "assertSessionInvariant": return withObserverSync(await assertSessionInvariant(command), "assertSessionInvariant");
|
||||
case "gotoProjectMdtodo": return withObserverSync(await gotoProjectMdtodo(), "gotoProjectMdtodo");
|
||||
case "openMdtodoSourceConfig": return openMdtodoSourceConfig(command);
|
||||
case "closeMdtodoSourceConfig": return closeMdtodoSourceConfig(command);
|
||||
case "configureMdtodoHwpodSource": return configureMdtodoHwpodSource(command);
|
||||
case "probeMdtodoSource": return probeMdtodoSource(command);
|
||||
case "reindexMdtodoSource": return reindexMdtodoSource(command);
|
||||
@@ -2141,10 +2142,14 @@ async function selectMdtodoTask(command) {
|
||||
const clicked = await target.locator.evaluate((element) => ({
|
||||
taskRef: element.getAttribute("data-task-ref") || null,
|
||||
taskId: element.getAttribute("data-task-id") || element.getAttribute("data-rxx-id") || null,
|
||||
status: element.getAttribute("data-task-status") || null
|
||||
status: element.getAttribute("data-task-status") || null,
|
||||
selected: element.getAttribute("data-selected") === "true" || element.getAttribute("aria-selected") === "true"
|
||||
})).catch(() => ({ taskRef: target.taskRef || null, taskId: target.taskId || null, status: null }));
|
||||
await target.locator.click();
|
||||
await page.waitForTimeout(700);
|
||||
if (!clicked.selected) {
|
||||
await target.locator.scrollIntoViewIfNeeded().catch(() => null);
|
||||
await target.locator.click();
|
||||
await page.waitForTimeout(700);
|
||||
}
|
||||
const afterProject = await projectManagementCommandSnapshot();
|
||||
return {
|
||||
beforeUrl,
|
||||
@@ -2154,6 +2159,7 @@ async function selectMdtodoTask(command) {
|
||||
selectedTask: opaqueIdSummary(clicked.taskRef || target.taskRef),
|
||||
selectedTaskId: clicked.taskId || target.taskId || null,
|
||||
selectedTaskStatus: clicked.status || null,
|
||||
alreadySelected: clicked.selected === true,
|
||||
beforeProject,
|
||||
afterProject,
|
||||
pageId,
|
||||
@@ -2224,6 +2230,49 @@ async function ensureMdtodoSourceConfigOpen() {
|
||||
return { opened: true };
|
||||
}
|
||||
|
||||
async function closeMdtodoSourceConfig(command) {
|
||||
ensureProjectManagementCommand("closeMdtodoSourceConfig");
|
||||
const beforeUrl = currentPageUrl();
|
||||
const beforeProject = await projectManagementCommandSnapshot();
|
||||
const close = await closeMdtodoSourceConfigIfOpen({ required: true });
|
||||
return {
|
||||
beforeUrl,
|
||||
afterUrl: currentPageUrl(),
|
||||
type: "closeMdtodoSourceConfig",
|
||||
close,
|
||||
beforeProject,
|
||||
afterProject: await projectManagementCommandSnapshot(),
|
||||
pageId,
|
||||
valuesRedacted: true
|
||||
};
|
||||
}
|
||||
|
||||
async function closeMdtodoSourceConfigIfOpen(options = {}) {
|
||||
const dialog = page.locator('[data-testid="mdtodo-source-config-dialog"], [role="dialog"]').filter({
|
||||
has: page.locator('[data-testid="mdtodo-source-form-hwpod"], [data-testid="mdtodo-source-form-node"], [data-testid="mdtodo-source-form-root"], [data-testid="mdtodo-source-reindex-dialog"]')
|
||||
}).first();
|
||||
if (!await visibleLocator(dialog)) return { wasOpen: false, stillVisible: false };
|
||||
const closeButton = dialog.locator('[data-testid="mdtodo-source-config-close"], [aria-label="关闭配置"], [aria-label*="关闭"], [aria-label="Close"], button:has-text("关闭"), button:has-text("Cancel"), button:has-text("取消")').first();
|
||||
let closeClick = null;
|
||||
try {
|
||||
await closeButton.waitFor({ state: "visible", timeout: 5000 });
|
||||
await closeButton.click({ timeout: 5000 });
|
||||
closeClick = { attempted: true, ok: true };
|
||||
} catch (error) {
|
||||
closeClick = { attempted: true, ok: false, error: errorSummary(error) };
|
||||
await page.keyboard.press("Escape").catch(() => null);
|
||||
}
|
||||
await page.waitForTimeout(400);
|
||||
const stillVisible = await visibleLocator(dialog);
|
||||
const result = { wasOpen: true, closeClick, stillVisible, valuesRedacted: true };
|
||||
if (options.required && stillVisible) {
|
||||
const error = new Error("closeMdtodoSourceConfig dialog remained visible after close attempt");
|
||||
error.details = result;
|
||||
throw error;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async function fillMdtodoField(testId, value) {
|
||||
if (typeof value !== "string" || !value.trim()) return { testId, filled: false };
|
||||
const locator = page.locator('[data-testid="' + cssEscape(testId) + '"]').first();
|
||||
@@ -2267,6 +2316,7 @@ async function configureMdtodoHwpodSource(command) {
|
||||
await fillMdtodoField("mdtodo-source-form-root", commandValue(command, ["root", "path"])),
|
||||
];
|
||||
const save = await clickProjectButtonAndMaybeWait("mdtodo-source-save", /^\/v1\/project-management\/mdtodo\/sources/u);
|
||||
const close = await closeMdtodoSourceConfigIfOpen();
|
||||
return {
|
||||
beforeUrl,
|
||||
afterUrl: currentPageUrl(),
|
||||
@@ -2274,6 +2324,7 @@ async function configureMdtodoHwpodSource(command) {
|
||||
dialog,
|
||||
fields,
|
||||
save,
|
||||
close,
|
||||
beforeProject,
|
||||
afterProject: await projectManagementCommandSnapshot(),
|
||||
pageId,
|
||||
@@ -2287,7 +2338,8 @@ async function probeMdtodoSource(command) {
|
||||
const beforeProject = await projectManagementCommandSnapshot();
|
||||
await ensureMdtodoSourceConfigOpen();
|
||||
const probe = await clickProjectButtonAndMaybeWait("mdtodo-source-probe", /^\/v1\/project-management\/mdtodo\/sources/u);
|
||||
return { beforeUrl, afterUrl: currentPageUrl(), type: "probeMdtodoSource", probe, beforeProject, afterProject: await projectManagementCommandSnapshot(), pageId, valuesRedacted: true };
|
||||
const close = await closeMdtodoSourceConfigIfOpen();
|
||||
return { beforeUrl, afterUrl: currentPageUrl(), type: "probeMdtodoSource", probe, close, beforeProject, afterProject: await projectManagementCommandSnapshot(), pageId, valuesRedacted: true };
|
||||
}
|
||||
|
||||
async function reindexMdtodoSource(command) {
|
||||
@@ -2298,7 +2350,8 @@ async function reindexMdtodoSource(command) {
|
||||
const dialogButton = page.locator('[data-testid="mdtodo-source-reindex-dialog"]').first();
|
||||
const buttonTestId = await visibleLocator(dialogButton) ? "mdtodo-source-reindex-dialog" : "mdtodo-source-reindex";
|
||||
const reindex = await clickProjectButtonAndMaybeWait(buttonTestId, /^\/v1\/project-management\/mdtodo\/sources/u);
|
||||
return { beforeUrl, afterUrl: currentPageUrl(), type: "reindexMdtodoSource", buttonTestId, reindex, beforeProject, afterProject: await projectManagementCommandSnapshot(), pageId, valuesRedacted: true };
|
||||
const close = await closeMdtodoSourceConfigIfOpen();
|
||||
return { beforeUrl, afterUrl: currentPageUrl(), type: "reindexMdtodoSource", buttonTestId, reindex, close, beforeProject, afterProject: await projectManagementCommandSnapshot(), pageId, valuesRedacted: true };
|
||||
}
|
||||
|
||||
async function selectTaskIfCommandTargetsOne(command) {
|
||||
@@ -2430,6 +2483,7 @@ async function openMdtodoReportPreview(command) {
|
||||
return false;
|
||||
}
|
||||
}, { timeout: 30000 }).then((response) => ({ observed: true, status: response.status(), path: new URL(response.url()).pathname })).catch((error) => ({ observed: false, waitError: errorSummary(error) }));
|
||||
await link.scrollIntoViewIfNeeded().catch(() => null);
|
||||
await link.click();
|
||||
const response = await responsePromise;
|
||||
await page.locator('[data-testid="mdtodo-report-preview"], [data-testid="mdtodo-report-error"]').first().waitFor({ state: "visible", timeout: 10000 }).catch(() => null);
|
||||
|
||||
@@ -138,6 +138,7 @@ export type NodeWebProbeObserveCommandType =
|
||||
| "openMdtodoReportPreview"
|
||||
| "toggleMdtodoReportFullscreen"
|
||||
| "openMdtodoSourceConfig"
|
||||
| "closeMdtodoSourceConfig"
|
||||
| "configureMdtodoHwpodSource"
|
||||
| "probeMdtodoSource"
|
||||
| "reindexMdtodoSource"
|
||||
|
||||
@@ -430,6 +430,7 @@ export function parseNodeWebProbeObserveCommandType(value: string): NodeWebProbe
|
||||
|| value === "openMdtodoReportPreview"
|
||||
|| value === "toggleMdtodoReportFullscreen"
|
||||
|| value === "openMdtodoSourceConfig"
|
||||
|| value === "closeMdtodoSourceConfig"
|
||||
|| value === "configureMdtodoHwpodSource"
|
||||
|| value === "probeMdtodoSource"
|
||||
|| value === "reindexMdtodoSource"
|
||||
@@ -447,7 +448,7 @@ export function parseNodeWebProbeObserveCommandType(value: string): NodeWebProbe
|
||||
|| value === "mark"
|
||||
|| value === "stop"
|
||||
) return value;
|
||||
throw new Error(`web-probe observe command --type must be login, loginAccount, logout, listSessions, switchSessions, preflight, goto, gotoProjectMdtodo, newSession, sendPrompt, steer, cancel, selectProvider, clickSession, refreshCurrentSession, switchAwayAndBack, assertSessionInvariant, selectProjectSource, selectMdtodoSource, selectMdtodoFile, selectMdtodoTask, expandMdtodoTask, openMdtodoReportPreview, toggleMdtodoReportFullscreen, openMdtodoSourceConfig, configureMdtodoHwpodSource, probeMdtodoSource, reindexMdtodoSource, editMdtodoTaskInline, editMdtodoTaskTitle, editMdtodoTaskBody, toggleMdtodoTaskStatus, addMdtodoRootTask, addMdtodoSubTask, continueMdtodoTask, deleteMdtodoTask, launchWorkbenchFromTask, launchWorkbenchFromMdtodo, screenshot, mark, or stop; got ${value}`);
|
||||
throw new Error(`web-probe observe command --type must be login, loginAccount, logout, listSessions, switchSessions, preflight, goto, gotoProjectMdtodo, newSession, sendPrompt, steer, cancel, selectProvider, clickSession, refreshCurrentSession, switchAwayAndBack, assertSessionInvariant, selectProjectSource, selectMdtodoSource, selectMdtodoFile, selectMdtodoTask, expandMdtodoTask, openMdtodoReportPreview, toggleMdtodoReportFullscreen, openMdtodoSourceConfig, closeMdtodoSourceConfig, configureMdtodoHwpodSource, probeMdtodoSource, reindexMdtodoSource, editMdtodoTaskInline, editMdtodoTaskTitle, editMdtodoTaskBody, toggleMdtodoTaskStatus, addMdtodoRootTask, addMdtodoSubTask, continueMdtodoTask, deleteMdtodoTask, launchWorkbenchFromTask, launchWorkbenchFromMdtodo, screenshot, mark, or stop; got ${value}`);
|
||||
}
|
||||
|
||||
export function parseWebProbeBrowserProxyMode(value: string | undefined): WebProbeBrowserProxyMode {
|
||||
|
||||
Reference in New Issue
Block a user