diff --git a/.agents/skills/unidesk-webdev/SKILL.md b/.agents/skills/unidesk-webdev/SKILL.md index fe8575fe..5a848302 100644 --- a/.agents/skills/unidesk-webdev/SKILL.md +++ b/.agents/skills/unidesk-webdev/SKILL.md @@ -29,6 +29,10 @@ description: UniDesk Web 开发与受控浏览器验证技能。用户提到 Web - 禁止直接调用 `playwright`、`@playwright/cli`、`playwright-cli`、浏览器 MCP 或仓库外临时浏览器脚本; - `web-probe` 内部可以使用浏览器引擎,但认证、目标解析、截图、脱敏、artifact 和报告必须由 `web-probe` 合同统一管理; - 缺少所需动作时,先在 owning YAML 和 `web-probe` typed command 中补齐通用能力,不得绕过为第二操作面。 +- L1 Workbench 长程 observer 使用 `web-probe observe start --origin native`: + - 固定 HTTPS 入口只从 owning YAML 的 `nativeDevelopment.workbench.publicExposure.publicBaseUrl` 解析; + - 禁止使用部署态 `public` origin、自定义 URL 或 localhost 冒充 L1 observer; + - 后续 command、status、collect、analyze 和 stop 继承启动时冻结的 native origin。 - Web-probe 正式 CLI 入口是 `bun scripts/cli.ts web-probe ...`;旧 `hwlab nodes web-probe` 已移除,只能按 CLI 提示迁移,不要在 issue 或长期文档中继续记录旧入口。 - `web-probe observe stop` 是 observer 生命周期清理入口: - 不得依赖业务页面 readiness 才执行停止; diff --git a/.agents/skills/unidesk-webdev/references/web-probe.md b/.agents/skills/unidesk-webdev/references/web-probe.md index 12553b3d..3fcc2417 100644 --- a/.agents/skills/unidesk-webdev/references/web-probe.md +++ b/.agents/skills/unidesk-webdev/references/web-probe.md @@ -14,6 +14,7 @@ - 已报告的视觉层级问题使用 profile 的 `geometryChecks` 输出匹配数量、实际尺寸和 YAML 预算,截图与几何判定必须同时通过; - 命令不接受 `--url` 或 `--origin`,避免绕过 owning YAML 选择其他运行面。 - L1 浏览器必须打开 owning YAML 解析出的固定 HTTPS origin;`127.0.0.1`、`localhost`、IP、port 和临时 `--url` 只能作为本机 preflight,不能替代 L1 readiness。 + - L1 Workbench 长程观察使用 `bun scripts/cli.ts web-probe observe start --node --lane --origin native --target-path /workbench`;native origin 只读取同一 owning YAML 的 Workbench `publicExposure.publicBaseUrl`,不得回退部署态 public origin。 - Cloud Console 页面矩阵: - 使用 `bun scripts/cli.ts web-probe console-verify --node --lane --origin internal|public --profile `; - 配置真相位于 `config/hwlab-node-lanes.yaml#templates.*.webProbeWorkbench.consoleVerificationProfiles`; diff --git a/project-management/PJ2026-01/specs/PJ2026-010401-web-workbench.md b/project-management/PJ2026-01/specs/PJ2026-010401-web-workbench.md index d8dbe2af..a1a5ac83 100644 --- a/project-management/PJ2026-01/specs/PJ2026-010401-web-workbench.md +++ b/project-management/PJ2026-01/specs/PJ2026-010401-web-workbench.md @@ -497,6 +497,8 @@ mock 数据应优先来自目标 node/lane 的真实受控样本,而不是从 Web 工作台应具备长程 web-probe observer 能力,用真实浏览器在目标 node/lane 的正式公开入口上持续观察同一页面,用于复现和分析长 Trace、session 切换、刷新恢复、滚动跟随、final response 闪烁和无用户新消息时状态变化等瞬态问题。该能力只提供观察和证据,不拥有 Workbench 业务状态、身份鉴权、session lifecycle、projection、监控指标或修复权。 +长程 observer 必须显式选择运行面。L1 native 使用 `--origin native`,并且只从目标 node/lane owning YAML 的 `nativeDevelopment.workbench.publicExposure.publicBaseUrl` 解析固定 HTTPS 入口;L2/L3 继续使用部署态 `internal` 或 `public` semantic origin。CLI 不得用自定义 URL、部署态 public URL、localhost 或隐藏 fallback 冒充 L1 证据,后续 command、status、collect、analyze 和 stop 必须继承 observer 启动时冻结的运行面与 origin。 + web-probe observer 必须保持纯客户端形态。它可以作为目标 host 上的长时间本地进程运行,但不得暴露入站 HTTP/WebSocket/gRPC API、监听控制端口、数据库 schema、消息队列消费者、Kubernetes 常驻服务、平台 daemon 或 Web 管理面。启动、停止、状态查看、命令投递和 artifact 回收只能通过受控 CLI、`trans` 短连接和目标 host 文件系统完成。任何需要长期保留的默认参数必须按 YAML-first 进入 UniDesk 自有配置;一次性控制命令使用命令文件,不得演变为新服务协议。 同一个 observer job 内必须只有一个页面权威。Playwright browser/context/page 由 observer 客户端进程持有;控制循环、采样循环、截图逻辑和网络事件监听共享同一个 `page` 对象。登录、打开 URL、发送 prompt、点击会话、截图和停止等控制动作以命令文件进入 `commands/pending`,处理结果追加到 `control.jsonl`,并带上命令 id、时间戳、操作者来源、执行前后 URL 和页面标识。若浏览器或页面崩溃后必须重建,observer 必须记录 oldPageId/newPageId、原因和连续性中断,不能把重建后的页面当作同一段无缝采样。 diff --git a/scripts/src/hwlab-node-help.ts b/scripts/src/hwlab-node-help.ts index a088ff32..20117c1d 100644 --- a/scripts/src/hwlab-node-help.ts +++ b/scripts/src/hwlab-node-help.ts @@ -86,7 +86,7 @@ export function hwlabNodeWebProbeHelp(): Record { return { ok: true, command: "web-probe", - description: "Run target node/lane HWLAB Cloud Web probes with YAML-selected semantic internal/public origins and one-shot bootstrap Web credentials.", + description: "Run target node/lane HWLAB Cloud Web probes with YAML-selected semantic internal/public/native origins and one-shot bootstrap Web credentials.", examples: [ "bun scripts/cli.ts web-probe run --node --lane --origin internal --wait-messages-ms 1000", "bun scripts/cli.ts web-probe opencode-smoke --node --lane --origin internal --message 'hi'", @@ -102,6 +102,7 @@ export function hwlabNodeWebProbeHelp(): Record { "bun scripts/cli.ts web-probe script --node --lane --origin internal --script-file .state/probes/workbench.mjs", "bun scripts/cli.ts web-probe screenshot --node --lane --origin internal --path /workbench --viewport 1440x900", "bun scripts/cli.ts web-probe observe start --node --lane --origin internal --target-path /workbench --sample-interval-ms 5000", + "bun scripts/cli.ts web-probe observe start --node --lane --origin native --target-path /workbench --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 validateRealtimeFanout --profile pure-kafka-live --provider gpt.pika --text '' --second-text ''", "bun scripts/cli.ts web-probe observe status webobs-xxxx --command-id cmd-xxxx", @@ -138,9 +139,9 @@ export function hwlabNodeWebProbeHelp(): Record { sentinel: "Render and operate the YAML-first web-probe sentinel wrapper, image, GitOps, dashboard verification, maintenance and report views; migrated consumers are delivered only by the automatic PR-merge chain.", }, notes: [ - "Named internal/public origins, default origin, per-origin browser proxy mode, observe/analyze thresholds, and project-management command allowlist come from config/hwlab-node-lanes.yaml webProbe.", - "Use --origin internal for business/feature bug debugging and --origin public for public exposure or public-entry closeout; both resolve only from the owning YAML.", - "--url is a mutually exclusive custom/local one-shot escape hatch; never pass a URL or IP to choose between internal and public origins.", + "Named internal/public origins, the L1 native Workbench origin, default origin, browser proxy mode, observe/analyze thresholds, and project-management command allowlist come from config/hwlab-node-lanes.yaml.", + "Use --origin native for L1 Workbench, --origin internal for deployed feature debugging, and --origin public for deployed public-entry closeout; all resolve only from the owning YAML.", + "--url is a mutually exclusive custom/local one-shot escape hatch; never pass a URL or IP to impersonate an internal, public, or native origin.", "`web-probe script` is an ad-hoc exploration escape hatch; repeated/high-frequency workflows must become `web-probe observe command` types or repo-owned web-probe commands.", "Every script result starts with machine-readable `UNIDESK_WEB_PROBE_COMMAND_PROMOTION_HINT ` guidance; repo-owned generated commands report reuse instead of ad-hoc promotion.", "`web-probe opencode-smoke` is the repo-owned OpenCode smoke; prefer it over repeating one-off OpenCode Playwright snippets.", @@ -177,7 +178,7 @@ export function hwlabNodeWebProbeScreenshotHelp(): Record { "bun scripts/cli.ts web-probe screenshot --node --lane --url 'http://127.0.0.1:4173/workbench' --viewport 1440x900", ], options: { - "--origin": "从 owning YAML 选择 internal 或 public semantic origin;可与 --path 配合。", + "--origin": "从 owning YAML 选择 internal、public 或 native semantic origin;可与 --path 配合。", "--path": "附加到 semantic origin 的绝对路径;与 --url 互斥。", "--url": "custom/local 一次性 URL,必须包含完整路径;与 --origin、--path 互斥。", "--viewport": "截图视口,默认 1440x900。", @@ -186,7 +187,7 @@ export function hwlabNodeWebProbeScreenshotHelp(): Record { }, notes: [ "使用 --url 时不要再传 --path;custom/local URL 已包含完整路径。", - "internal/public 运行面必须通过 owning YAML 的 --origin 选择,禁止用手写 URL 或 IP 替代。", + "internal/public/native 运行面必须通过 owning YAML 的 --origin 选择,禁止用手写 URL 或 IP 替代。", "该命令只采集证据,不修改浏览器能力、资源门禁或运行面。", ], mutation: false, diff --git a/scripts/src/hwlab-node-web-observe-wrapper.ts b/scripts/src/hwlab-node-web-observe-wrapper.ts index 90fb9bd8..8d6b8ce7 100644 --- a/scripts/src/hwlab-node-web-observe-wrapper.ts +++ b/scripts/src/hwlab-node-web-observe-wrapper.ts @@ -12,8 +12,8 @@ export interface WebObserveWrapperInput { readonly jobId: string | null; readonly stateDir: string | null; readonly url: string | null; - readonly originName: "internal" | "public" | "custom" | null; - readonly originMode: "k8s-service-cluster-ip" | "public" | "custom-url" | null; + readonly originName: "internal" | "public" | "native" | "custom" | null; + readonly originMode: "k8s-service-cluster-ip" | "public" | "native-public" | "custom-url" | null; readonly originConfigPath: string | null; readonly browserProxyMode: "auto" | "direct" | null; readonly browserProxyModeSource: "cli" | "yaml-origin" | "yaml-web-probe" | "default" | null; @@ -37,8 +37,8 @@ export interface WebObserveWrapperOptionsLike { readonly node: string; readonly lane: string; readonly url: string; - readonly originName: "internal" | "public" | "custom"; - readonly originMode: "k8s-service-cluster-ip" | "public" | "custom-url"; + readonly originName: "internal" | "public" | "native" | "custom"; + readonly originMode: "k8s-service-cluster-ip" | "public" | "native-public" | "custom-url"; readonly originConfigPath: string | null; readonly browserProxyMode: "auto" | "direct"; readonly browserProxyModeSource: "cli" | "yaml-origin" | "yaml-web-probe" | "default"; @@ -235,7 +235,7 @@ function webObserveWrapperCommandShape(input: WebObserveWrapperInput): string { parts.push("--state-dir", input.stateDir); } if (input.action === "start" && input.targetPath !== null) { - if (input.originName === "internal" || input.originName === "public") parts.push("--origin", input.originName); + if (input.originName === "internal" || input.originName === "public" || input.originName === "native") parts.push("--origin", input.originName); parts.push("--target-path", input.targetPath); } if (input.action === "command" && input.commandType !== null) { diff --git a/scripts/src/hwlab-node/entry.ts b/scripts/src/hwlab-node/entry.ts index 955a7cdb..816b910c 100644 --- a/scripts/src/hwlab-node/entry.ts +++ b/scripts/src/hwlab-node/entry.ts @@ -58,11 +58,11 @@ export type SecretPreset = "openfga" | "master-server-admin-api-key" | "bootstra export type NodeRuntimeRenderLocation = "node-host"; export type WebProbeBrowserProxyMode = "auto" | "direct"; -export type WebProbeOriginName = HwlabRuntimeWebProbeOriginName | "custom"; +export type WebProbeOriginName = HwlabRuntimeWebProbeOriginName | "native" | "custom"; export interface WebProbeOriginSelection { readonly originName: WebProbeOriginName; - readonly originMode: "k8s-service-cluster-ip" | "public" | "custom-url"; + readonly originMode: "k8s-service-cluster-ip" | "public" | "native-public" | "custom-url"; readonly originConfigPath: string | null; readonly originSource?: { readonly namespace: string; diff --git a/scripts/src/hwlab-node/web-observe-render.ts b/scripts/src/hwlab-node/web-observe-render.ts index 00c98d5b..ffc53f51 100644 --- a/scripts/src/hwlab-node/web-observe-render.ts +++ b/scripts/src/hwlab-node/web-observe-render.ts @@ -991,8 +991,8 @@ export function readWebObserveIndex(): Record { workspace, stateDir, url: typeof recordValue.url === "string" ? recordValue.url : "", - originName: recordValue.originName === "internal" || recordValue.originName === "public" || recordValue.originName === "custom" ? recordValue.originName : null, - originMode: recordValue.originMode === "k8s-service-cluster-ip" || recordValue.originMode === "public" || recordValue.originMode === "custom-url" ? recordValue.originMode : null, + originName: recordValue.originName === "internal" || recordValue.originName === "public" || recordValue.originName === "native" || recordValue.originName === "custom" ? recordValue.originName : null, + originMode: recordValue.originMode === "k8s-service-cluster-ip" || recordValue.originMode === "public" || recordValue.originMode === "native-public" || recordValue.originMode === "custom-url" ? recordValue.originMode : null, originConfigPath: typeof recordValue.originConfigPath === "string" ? recordValue.originConfigPath : null, browserProxyMode: recordValue.browserProxyMode === "auto" || recordValue.browserProxyMode === "direct" ? recordValue.browserProxyMode : null, browserProxyModeSource: recordValue.browserProxyModeSource === "cli" || recordValue.browserProxyModeSource === "yaml-origin" || recordValue.browserProxyModeSource === "yaml-web-probe" || recordValue.browserProxyModeSource === "default" ? recordValue.browserProxyModeSource : null, @@ -1121,8 +1121,8 @@ export function discoverWebObserveIndexEntryOnTarget(id: string, node: string, l workspace: spec.workspace, stateDir, url: typeof entry.url === "string" ? entry.url : "", - originName: entry.originName === "internal" || entry.originName === "public" || entry.originName === "custom" ? entry.originName : null, - originMode: entry.originMode === "k8s-service-cluster-ip" || entry.originMode === "public" || entry.originMode === "custom-url" ? entry.originMode : null, + originName: entry.originName === "internal" || entry.originName === "public" || entry.originName === "native" || entry.originName === "custom" ? entry.originName : null, + originMode: entry.originMode === "k8s-service-cluster-ip" || entry.originMode === "public" || entry.originMode === "native-public" || entry.originMode === "custom-url" ? entry.originMode : null, originConfigPath: typeof entry.originConfigPath === "string" ? entry.originConfigPath : null, browserProxyMode: entry.browserProxyMode === "auto" || entry.browserProxyMode === "direct" ? entry.browserProxyMode : null, browserProxyModeSource: entry.browserProxyModeSource === "cli" || entry.browserProxyModeSource === "yaml-origin" || entry.browserProxyModeSource === "yaml-web-probe" || entry.browserProxyModeSource === "default" ? entry.browserProxyModeSource : null, @@ -1203,7 +1203,7 @@ function webObserveOriginName(value: unknown): WebObserveIndexEntry["originName" } function webObserveOriginMode(value: unknown): WebObserveIndexEntry["originMode"] { - return value === "k8s-service-cluster-ip" || value === "public" || value === "custom-url" ? value : null; + return value === "k8s-service-cluster-ip" || value === "public" || value === "native-public" || value === "custom-url" ? value : null; } function webObserveBrowserProxyMode(value: unknown): WebObserveIndexEntry["browserProxyMode"] { diff --git a/scripts/src/hwlab-node/web-observe-scripts.ts b/scripts/src/hwlab-node/web-observe-scripts.ts index dd050080..82ae4535 100644 --- a/scripts/src/hwlab-node/web-observe-scripts.ts +++ b/scripts/src/hwlab-node/web-observe-scripts.ts @@ -906,7 +906,7 @@ export function webProbeScriptPreferredCommands(options: NodeWebProbeScriptOptio } function webProbeSemanticOriginArgs(options: Pick): string { - return options.originName === "internal" || options.originName === "public" ? ` --origin ${options.originName}` : ""; + return options.originName === "internal" || options.originName === "public" || options.originName === "native" ? ` --origin ${options.originName}` : ""; } export function nodeWebProbeScriptRemoteShell( diff --git a/scripts/src/hwlab-node/web-probe-observe-options.test.ts b/scripts/src/hwlab-node/web-probe-observe-options.test.ts index 4525d3f4..8748da47 100644 --- a/scripts/src/hwlab-node/web-probe-observe-options.test.ts +++ b/scripts/src/hwlab-node/web-probe-observe-options.test.ts @@ -171,9 +171,25 @@ test("observe start selects the YAML public origin semantically", () => { assert.equal(options.browserProxyModeSource, "yaml-origin"); }); +test("observe start selects the YAML L1 native Workbench HTTPS origin", () => { + const options = parseNodeWebProbeObserveOptions( + ["start", "--origin", "native"], + "NC01", + "v03", + spec, + null, + null, + ); + + assert.equal(options.originName, "native"); + assert.equal(options.originMode, "native-public"); + assert.equal(options.url, spec.nativeDevelopment?.workbench.publicExposure.publicBaseUrl); + assert.match(options.originConfigPath ?? "", /nativeDevelopment\.workbench\.publicExposure\.publicBaseUrl/u); +}); + test("observe start inherits finite observer lifecycle limits from the owning YAML", () => { const options = parseNodeWebProbeObserveOptions( - ["start"], + ["start", "--origin", "native"], "NC01", "v03", spec, @@ -216,7 +232,7 @@ test("observe start inherits finite observer lifecycle limits from the owning YA test("sentinel observe start preserves the cadence comparator through the child CLI", () => { const options = parseNodeWebProbeObserveOptions( - ["start", "--sentinel-cadence"], + ["start", "--origin", "native", "--sentinel-cadence"], "NC01", "v03", spec, @@ -228,11 +244,11 @@ test("sentinel observe start preserves the cadence comparator through the child test("observe start cannot disable or exceed the YAML lifecycle caps", () => { assert.throws( - () => parseNodeWebProbeObserveOptions(["start", "--max-run-seconds", "0"], "NC01", "v03", spec, null, null), + () => parseNodeWebProbeObserveOptions(["start", "--origin", "native", "--max-run-seconds", "0"], "NC01", "v03", spec, null, null), /must be a positive integer/u, ); assert.throws( - () => parseNodeWebProbeObserveOptions(["start", "--max-samples", "721"], "NC01", "v03", spec, null, null), + () => parseNodeWebProbeObserveOptions(["start", "--origin", "native", "--max-samples", "721"], "NC01", "v03", spec, null, null), /exceeds the owning YAML observer lifecycle cap 720/u, ); }); diff --git a/scripts/src/hwlab-node/web-probe-origin.test.ts b/scripts/src/hwlab-node/web-probe-origin.test.ts index ea1ddf33..f15a5e7b 100644 --- a/scripts/src/hwlab-node/web-probe-origin.test.ts +++ b/scripts/src/hwlab-node/web-probe-origin.test.ts @@ -9,8 +9,9 @@ const spec = hwlabRuntimeLaneSpecForNode("v03", "NC01"); test("web-probe origin names are semantic and bounded", () => { assert.equal(parseWebProbeOriginName("internal"), "internal"); assert.equal(parseWebProbeOriginName("public"), "public"); - assert.throws(() => parseWebProbeOriginName("external"), /internal or public/u); - assert.throws(() => parseWebProbeOriginName("https:\/\/hwlab.example.test"), /internal or public/u); + assert.equal(parseWebProbeOriginName("native"), "native"); + assert.throws(() => parseWebProbeOriginName("external"), /internal, public, or native/u); + assert.throws(() => parseWebProbeOriginName("https:\/\/hwlab.example.test"), /internal, public, or native/u); }); test("NC01 v03 YAML declares internal debugging and public closeout origins", () => { diff --git a/scripts/src/hwlab-node/web-probe-origin.ts b/scripts/src/hwlab-node/web-probe-origin.ts index 9ecb8244..3516d5de 100644 --- a/scripts/src/hwlab-node/web-probe-origin.ts +++ b/scripts/src/hwlab-node/web-probe-origin.ts @@ -1,7 +1,7 @@ // Responsibility: resolve semantic YAML-owned HWLAB web-probe origins without CLI URL/IP selection. -import { hwlabRuntimeLaneConfigPath, type HwlabRuntimeLaneSpec, type HwlabRuntimeWebProbeOriginName } from "../hwlab-node-lanes"; +import { hwlabRuntimeLaneConfigPath, type HwlabRuntimeLaneSpec } from "../hwlab-node-lanes"; -import type { WebProbeBrowserProxyMode, WebProbeOriginSelection } from "./entry"; +import type { WebProbeBrowserProxyMode, WebProbeOriginName, WebProbeOriginSelection } from "./entry"; import { resolveKubernetesServiceClusterIp } from "./web-probe-observe-action-config"; export interface ResolvedNodeWebProbeOrigin extends WebProbeOriginSelection { @@ -27,16 +27,28 @@ type ServiceClusterIpResolver = ( serviceCache: Map, ) => string; -export function parseWebProbeOriginName(value: string): HwlabRuntimeWebProbeOriginName { - if (value === "internal" || value === "public") return value; - throw new Error(`web-probe --origin must be internal or public, got ${value}`); +export function parseWebProbeOriginName(value: string): Exclude { + if (value === "internal" || value === "public" || value === "native") return value; + throw new Error(`web-probe --origin must be internal, public, or native, got ${value}`); } export function resolveNodeWebProbeOrigin( spec: HwlabRuntimeLaneSpec, - name: HwlabRuntimeWebProbeOriginName, + name: Exclude, serviceResolver: ServiceClusterIpResolver = resolveKubernetesServiceClusterIp, ): ResolvedNodeWebProbeOrigin { + if (name === "native") { + const configPath = `${hwlabRuntimeLaneConfigPath()} node=${spec.nodeId} lane=${spec.lane}#nativeDevelopment.workbench.publicExposure.publicBaseUrl`; + const publicBaseUrl = spec.nativeDevelopment?.workbench.publicExposure.publicBaseUrl; + if (publicBaseUrl === undefined) throw new Error(`${configPath} must declare the L1 native Workbench HTTPS origin`); + return { + originName: name, + originMode: "native-public", + originConfigPath: configPath, + url: normalizeHttpOrigin(publicBaseUrl, configPath), + ...resolvedOriginBrowserProxyMode(spec, undefined), + }; + } const origin = spec.webProbe?.origins?.[name]; const configPath = `${hwlabRuntimeLaneConfigPath()} node=${spec.nodeId} lane=${spec.lane}#webProbe.origins.${name}`; if (origin === undefined) throw new Error(`${configPath} must declare the semantic ${name} web-probe origin`); @@ -85,7 +97,7 @@ export function resolveNodeWebProbeCliOrigin( requestedBrowserProxyMode: WebProbeBrowserProxyMode | undefined, ): ResolvedNodeWebProbeOrigin { if (requestedOrigin !== undefined && customUrl !== undefined) { - throw new Error("web-probe accepts --origin or --url, not both; use --origin internal|public for HWLAB network selection"); + throw new Error("web-probe accepts --origin or --url, not both; use --origin internal|public|native for HWLAB runtime selection"); } const selected = customUrl === undefined ? resolveNodeWebProbeOrigin(spec, parseWebProbeOriginName(requestedOrigin ?? spec.webProbe?.defaultOrigin ?? "public")) diff --git a/scripts/src/hwlab-node/web-probe.ts b/scripts/src/hwlab-node/web-probe.ts index 050b3f2d..18f6dcd0 100644 --- a/scripts/src/hwlab-node/web-probe.ts +++ b/scripts/src/hwlab-node/web-probe.ts @@ -2092,7 +2092,7 @@ export function rewriteDelegatedNodeString(value: string, scoped: ReturnType