diff --git a/scripts/src/hwlab-node-web-observe-runner-source.ts b/scripts/src/hwlab-node-web-observe-runner-source.ts index 5a82eabf..474f73cd 100644 --- a/scripts/src/hwlab-node-web-observe-runner-source.ts +++ b/scripts/src/hwlab-node-web-observe-runner-source.ts @@ -861,6 +861,8 @@ async function selectProvider(provider) { const target = String(provider || "").trim(); if (!target) throw new Error("selectProvider requires provider name"); const beforeUrl = currentPageUrl(); + const beforePath = safeUrlPath(beforeUrl); + if (!String(beforePath || "").startsWith("/workbench")) throw new Error("selectProvider requires a Workbench page; currentPath=" + (beforePath || "-") + "; run observe command --type goto --path /workbench or --type newSession first"); const nativeSelect = await page.evaluate((name) => { const normalized = String(name).toLowerCase(); const visible = (element) => { @@ -891,16 +893,13 @@ async function selectProvider(provider) { } const control = page.locator([ '[data-testid*="provider" i]', - '[data-testid*="profile" i]', '[data-testid*="model" i]', '[aria-label*="provider" i]', - '[aria-label*="profile" i]', '[aria-label*="model" i]', '[aria-label*="模型"]', '[aria-label*="提供"]', '[role="combobox"]', '[aria-haspopup="listbox"]', - 'button' ].join(", ")); const count = Math.min(await control.count().catch(() => 0), 60); const attempts = []; @@ -909,6 +908,7 @@ async function selectProvider(provider) { const visible = await item.isVisible().catch(() => false); if (!visible) continue; const label = await item.evaluate((element) => String(element.getAttribute("aria-label") || element.getAttribute("data-testid") || element.textContent || "").slice(0, 200)).catch(() => ""); + if (isProviderNavigationLabel(label)) continue; if (!/provider|profile|model|模型|提供|codex|openai|moon|api/iu.test(label)) continue; attempts.push({ index, label }); await item.click({ timeout: 3000 }).catch(() => null); @@ -966,6 +966,7 @@ async function collectProviderCandidates() { const ariaLabel = String(element.getAttribute("aria-label") || ""); const testId = String(element.getAttribute("data-testid") || ""); const haystack = text + " " + ariaLabel + " " + testId; + if (isProviderNavigationLabel(haystack)) continue; if (!/provider|profile|model|模型|提供|codex|openai|deepseek|gpt|api|flash|spark|claude|gemini|moon/iu.test(haystack)) continue; push("visible-control", element, element.getAttribute("value") || "", text || ariaLabel || testId); } @@ -973,6 +974,13 @@ async function collectProviderCandidates() { }).catch((error) => [{ kind: "candidate-scan-error", value: "", text: String(error?.message || error).slice(0, 240), testId: "", ariaLabel: "" }]); } +function isProviderNavigationLabel(value) { + const text = String(value || "").replace(/\s+/gu, " ").trim().toLowerCase(); + if (!text) return false; + if (text === "api keys" || text === "kapi keys" || text === "profiles" || text === "rprofiles") return true; + return /^(?:api keys|profiles)$/iu.test(text.replace(/^[a-z]\s*/iu, "")); +} + async function clickSession(sessionId) { if (!sessionId) throw new Error("clickSession requires --session-id or --value"); const beforeUrl = currentPageUrl();