From 17a371c208519ec395257e14d05547cb700678d2 Mon Sep 17 00:00:00 2001 From: pikastech Date: Sat, 18 Jul 2026 03:49:46 +0200 Subject: [PATCH 1/4] fix: configure L1 Workbench for AgentRun v0.2 --- config/hwlab-node-lanes.yaml | 5 +++++ scripts/src/hwlab-native-development.test.ts | 3 +++ scripts/src/hwlab-native-development.ts | 11 +++++++++++ scripts/src/hwlab-node-lanes.ts | 16 ++++++++++++++++ 4 files changed, 35 insertions(+) diff --git a/config/hwlab-node-lanes.yaml b/config/hwlab-node-lanes.yaml index 8b095054..1f7f6285 100644 --- a/config/hwlab-node-lanes.yaml +++ b/config/hwlab-node-lanes.yaml @@ -115,6 +115,11 @@ lanes: workbench: publicHostRef: config/unidesk-host-k8s.yaml#runtime.publicHost stateDir: .state/workbench-native/services + mode: temporal + cloudApiUrlRef: config/hwlab-node-lanes.yaml#lanes.v03.public.apiUrl + cloudApiAuthorization: + sourceRef: hwlab/nc01-v03-admin.env + sourceKey: HWLAB_API_KEY api: bindHost: 0.0.0.0 probeHost: 127.0.0.1 diff --git a/scripts/src/hwlab-native-development.test.ts b/scripts/src/hwlab-native-development.test.ts index 08cb231c..9a6b5890 100644 --- a/scripts/src/hwlab-native-development.test.ts +++ b/scripts/src/hwlab-native-development.test.ts @@ -8,6 +8,9 @@ test("L1 Workbench exposure uses YAML public IP and separate bind/probe addresse expect(native).toBeDefined(); const env = hwlabNativeDevelopmentEnvironment(native!); expect(env.WORKBENCH_NATIVE_SERVICE_STATE_DIR).toBe(native!.stateDir); + expect(env.WORKBENCH_MODE).toBe("temporal"); + expect(env.WORKBENCH_CLOUD_API_URL).toBe(native!.cloudApiUrl); + expect(env.WORKBENCH_CLOUD_API_AUTHORIZATION).toMatch(/^Bearer hwl_live_/u); expect(env.WORKBENCH_API_BIND_HOST).toBe(native!.api.bindHost); expect(env.WORKBENCH_API_PROBE_HOST).toBe(native!.api.probeHost); expect(env.WORKBENCH_API_PUBLIC_HOST).toBe(native!.publicHost); diff --git a/scripts/src/hwlab-native-development.ts b/scripts/src/hwlab-native-development.ts index 4426c3c5..08a40126 100644 --- a/scripts/src/hwlab-native-development.ts +++ b/scripts/src/hwlab-native-development.ts @@ -1,4 +1,5 @@ import { runCommand } from "./command"; +import { readEnvSourceFile, requiredEnvValue } from "./secrets"; import { hwlabRuntimeLaneConfigPath, hwlabRuntimeLaneSpecForNode, isHwlabRuntimeLane, type HwlabRuntimeNativeDevelopmentSpec } from "./hwlab-node-lanes"; type Service = "api" | "worker" | "web"; @@ -53,7 +54,17 @@ export function runHwlabNativeDevelopmentCommand(args: string[]): Record Date: Sat, 18 Jul 2026 04:42:06 +0200 Subject: [PATCH 2/4] fix: connect L1 Workbench directly to AgentRun --- config/hwlab-node-lanes.yaml | 21 +++++-- scripts/src/hwlab-native-development.test.ts | 15 +++-- scripts/src/hwlab-native-development.ts | 45 +++++++++++--- scripts/src/hwlab-node-lanes.ts | 65 +++++++++++++++----- 4 files changed, 113 insertions(+), 33 deletions(-) diff --git a/config/hwlab-node-lanes.yaml b/config/hwlab-node-lanes.yaml index 1f7f6285..4884c545 100644 --- a/config/hwlab-node-lanes.yaml +++ b/config/hwlab-node-lanes.yaml @@ -115,11 +115,22 @@ lanes: workbench: publicHostRef: config/unidesk-host-k8s.yaml#runtime.publicHost stateDir: .state/workbench-native/services - mode: temporal - cloudApiUrlRef: config/hwlab-node-lanes.yaml#lanes.v03.public.apiUrl - cloudApiAuthorization: - sourceRef: hwlab/nc01-v03-admin.env - sourceKey: HWLAB_API_KEY + mode: agentrun-native + temporal: + address: 10.43.145.243:7233 + namespace: unidesk + taskQueue: hwlab-v03-workbench + activity: + startToCloseTimeoutMs: 900000 + retryInitialIntervalMs: 1000 + retryMaximumIntervalMs: 10000 + retryMaximumAttempts: 1 + agentRun: + managerUrl: http://10.43.171.18:8080 + runtimeApiUrl: http://hwlab-cloud-api.hwlab-v03.svc.cluster.local:6667 + runtimeWebUrl: http://hwlab-cloud-web.hwlab-v03.svc.cluster.local:8080 + terminalTimeoutMs: 600000 + pollIntervalMs: 1000 api: bindHost: 0.0.0.0 probeHost: 127.0.0.1 diff --git a/scripts/src/hwlab-native-development.test.ts b/scripts/src/hwlab-native-development.test.ts index 9a6b5890..2f1cfe69 100644 --- a/scripts/src/hwlab-native-development.test.ts +++ b/scripts/src/hwlab-native-development.test.ts @@ -4,13 +4,18 @@ import { hwlabNativeDevelopmentEnvironment, hwlabNativeDevelopmentHelp } from ". import { hwlabRuntimeLaneSpecForNode } from "./hwlab-node-lanes"; test("L1 Workbench exposure uses YAML public IP and separate bind/probe addresses", () => { - const native = hwlabRuntimeLaneSpecForNode("v03", "NC01").nativeDevelopment?.workbench; + const spec = hwlabRuntimeLaneSpecForNode("v03", "NC01"); + const native = spec.nativeDevelopment?.workbench; expect(native).toBeDefined(); - const env = hwlabNativeDevelopmentEnvironment(native!); + const env = hwlabNativeDevelopmentEnvironment(native!, spec, "0123456789abcdef0123456789abcdef01234567"); expect(env.WORKBENCH_NATIVE_SERVICE_STATE_DIR).toBe(native!.stateDir); - expect(env.WORKBENCH_MODE).toBe("temporal"); - expect(env.WORKBENCH_CLOUD_API_URL).toBe(native!.cloudApiUrl); - expect(env.WORKBENCH_CLOUD_API_AUTHORIZATION).toMatch(/^Bearer hwl_live_/u); + expect(env.WORKBENCH_MODE).toBe("agentrun-native"); + expect(env.WORKBENCH_CLOUD_API_URL).toBeUndefined(); + expect(env.WORKBENCH_CLOUD_API_AUTHORIZATION).toBeUndefined(); + expect(env.AGENTRUN_MGR_URL).toBe(native!.agentRun.managerUrl); + expect(env.AGENTRUN_API_KEY).toMatch(/^hwl_live_/u); + expect(env.HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE).toBe("gpt.pika"); + expect(env.HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT).toBe("0123456789abcdef0123456789abcdef01234567"); expect(env.WORKBENCH_API_BIND_HOST).toBe(native!.api.bindHost); expect(env.WORKBENCH_API_PROBE_HOST).toBe(native!.api.probeHost); expect(env.WORKBENCH_API_PUBLIC_HOST).toBe(native!.publicHost); diff --git a/scripts/src/hwlab-native-development.ts b/scripts/src/hwlab-native-development.ts index 08a40126..730f723f 100644 --- a/scripts/src/hwlab-native-development.ts +++ b/scripts/src/hwlab-native-development.ts @@ -1,6 +1,6 @@ import { runCommand } from "./command"; import { readEnvSourceFile, requiredEnvValue } from "./secrets"; -import { hwlabRuntimeLaneConfigPath, hwlabRuntimeLaneSpecForNode, isHwlabRuntimeLane, type HwlabRuntimeNativeDevelopmentSpec } from "./hwlab-node-lanes"; +import { hwlabRuntimeLaneConfigPath, hwlabRuntimeLaneSpecForNode, isHwlabRuntimeLane, type HwlabRuntimeLaneSpec, type HwlabRuntimeNativeDevelopmentSpec } from "./hwlab-node-lanes"; type Service = "api" | "worker" | "web"; type Action = "start" | "stop" | "restart" | "status" | "logs"; @@ -29,7 +29,11 @@ export function runHwlabNativeDevelopmentCommand(args: string[]): Record, key: string, path: string): string { + const value = stringField(raw, key, path); + try { + const parsed = new URL(value); + if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new Error("unsupported protocol"); + } catch { + throw new Error(`${path}.${key} must be an http(s) URL`); + } + return value; +} + function sourceWorkspaceHostDependenciesConfig(value: unknown, path: string): HwlabRuntimeSourceWorkspaceHostDependenciesSpec | undefined { if (value === undefined) return undefined; const raw = asRecord(value, path); From d0ef4d2e4a51419cc3a10c1b50394fa7269b991b Mon Sep 17 00:00:00 2001 From: pikastech Date: Sat, 18 Jul 2026 04:55:03 +0200 Subject: [PATCH 3/4] =?UTF-8?q?docs:=20=E5=9B=BA=E5=8C=96=20L1=20=E7=AB=AF?= =?UTF-8?q?=E5=8F=A3=E5=86=B2=E7=AA=81=E9=80=80=E8=AE=A9=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/skills/unidesk-webdev/SKILL.md | 10 +++++++++- config/hwlab-node-lanes.yaml | 2 +- docs/reference/hwlab.md | 8 ++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.agents/skills/unidesk-webdev/SKILL.md b/.agents/skills/unidesk-webdev/SKILL.md index d4c079ad..61c075e9 100644 --- a/.agents/skills/unidesk-webdev/SKILL.md +++ b/.agents/skills/unidesk-webdev/SKILL.md @@ -75,7 +75,15 @@ description: UniDesk Web 开发与受控浏览器验证技能。用户提到 Web - 通过 native auth/access profile 只授权当前调试微服务的 `navId`; - 左侧根导航必须存在,但只能显示当前微服务的一个页面入口; - 只代理当前微服务 API 路径,禁止为了渲染产品壳层启动其他 HWLAB 后端; - - 当前微服务前端、API 和 Worker 使用独立端口与生命周期,不得抢占或停止其他 L1 会话; + - 当前微服务前端、API 和 Worker 使用 YAML-first 固定端口与独立生命周期; + - YAML 声明的 L1 端口被占用时: + - 同一服务的旧 L1 进程只通过项目 CLI 停止或重启; + - 其他服务不得停止、复用或接管; + - 先用有界监听状态确认一个空闲端口; + - 再修改本 L1 服务的 owning YAML 端口并通过 parser/CLI 读取新值; + - 禁止用命令行覆盖、临时环境变量或隐藏 fallback 绕过 YAML; + - 端口退让后继续启动,并从 YAML 新端口执行 `web-probe native-readiness`; + - L1 进程的启动、停止、重启、状态和日志只通过项目 CLI 管理; - Vite 必须绑定可访问的 native 端口并保持 HMR,浏览器端 HMR 地址不得写成 `0.0.0.0`。 - L1 公网调试入口交给用户前必须完成浏览器 readiness 验收: - 使用 `bun scripts/cli.ts web-probe native-readiness --node --lane --profile ` 一次完成验收; diff --git a/config/hwlab-node-lanes.yaml b/config/hwlab-node-lanes.yaml index 4884c545..5f0307f9 100644 --- a/config/hwlab-node-lanes.yaml +++ b/config/hwlab-node-lanes.yaml @@ -142,7 +142,7 @@ lanes: web: bindHost: 0.0.0.0 probeHost: 127.0.0.1 - port: 5173 + port: 5174 runtimeConfig: displayTime: timeZone: Asia/Shanghai diff --git a/docs/reference/hwlab.md b/docs/reference/hwlab.md index 99c59e62..c6a0b5cf 100644 --- a/docs/reference/hwlab.md +++ b/docs/reference/hwlab.md @@ -180,6 +180,14 @@ AgentRun terminal `failed`、`blocked` 或 `canceled` 也是最终结果,不 Workbench 浏览器回归需求以 UniDesk OA [PJ2026-010401 Web工作台](../../project-management/PJ2026-01/specs/PJ2026-010401-web-workbench.md) 为权威;具体 Web 开发、受控 fake-server、fixture 采集脱敏、移动端断言、截图 artifact 和线上 web-probe 闭环统一见 `$unidesk-webdev`。HWLAB repo 只保留入口边界:专项代码位于 `web/hwlab-cloud-web`,命令必须在 issue/CLI 选中的目标 node/lane workspace 或独立 worktree 上运行,不得在 master server 本地跑浏览器或仓库级前端 check。 +- L1 Native 端口冲突处理: + - 端口被同一 L1 服务的旧进程占用时,只能通过项目 CLI 停止或重启该服务; + - 端口被其他服务占用时,禁止停止、接管或复用其他服务; + - 必须先确认空闲端口,再修改 `config/hwlab-node-lanes.yaml#lanes..targets..nativeDevelopment.workbench` 中本服务的端口; + - API、Worker、Web 的启动、状态和公网入口继续由同一 YAML 与 `hwlab nodes native-development workbench` 解析; + - 禁止用命令行参数、临时环境变量或代码 fallback 形成第二端口真相; + - 端口退让后必须从 YAML 新端口执行 `web-probe native-readiness`,确认公网页面、DOM、交互和浏览器错误均通过后再向用户披露入口。 + MDTODO/Project Management Web 的重写或布局类 issue 以 UniDesk OA 对应 SPEC 为权威;关闭时必须用选中 node/lane 的 public origin 和 `$unidesk-webdev` 的 `observe command` 证据证明默认任务正文、Rxx 树、Source/File 选择、报告侧栏和报告全屏状态。`observe` 同时采集 control/observer 页面时,显式 command result、control URL 和截图是用户动作证据;被动 observer 周期刷新或 stop 后根路由空态只能作为对照,不能覆盖用户入口截图和 command JSON。 Workbench session lifecycle、空 session 后台回收、archived/deleted deep link 和 GET 纯读投影的判定口径统一见 `$unidesk-webdev`。指挥侧关闭这类 issue 时只记录选中 node/lane、受控配置来源、后台 mutation/GC 证据、list/detail/messages/deep link 摘要和截图 SHA;TTL、interval、batch 等可调数值以选中 lane 的受控配置为准,不在本参考维护第二份数值真相。 From c89390ba9351885dd66ae51415e519800f90e469 Mon Sep 17 00:00:00 2001 From: pikastech Date: Sat, 18 Jul 2026 05:04:38 +0200 Subject: [PATCH 4/4] fix: isolate L1 Workbench Temporal task queue --- config/hwlab-node-lanes.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/hwlab-node-lanes.yaml b/config/hwlab-node-lanes.yaml index 5f0307f9..74d4cde2 100644 --- a/config/hwlab-node-lanes.yaml +++ b/config/hwlab-node-lanes.yaml @@ -119,7 +119,7 @@ lanes: temporal: address: 10.43.145.243:7233 namespace: unidesk - taskQueue: hwlab-v03-workbench + taskQueue: hwlab-v03-workbench-native-l1 activity: startToCloseTimeoutMs: 900000 retryInitialIntervalMs: 1000