14 KiB
14 KiB
v0.3 Workbench Vue 迁移语义合同
本文定义 Cloud Workbench 从 React useWorkbenchStore 迁移到 Vue/Pinia 时必须保留的 API、store 和 trace 语义。Vue 实现可以复用 Sub2API frontend 的 api/store/composable 分层形态,但不能照搬 Sub2API 的 bearer token 鉴权和通用刷新模型。
基线来源
| 来源 | 基线文件 | 可迁移结论 |
|---|---|---|
| HWLAB React Workbench | web/hwlab-cloud-web/src/state/workbench.ts、runner-trace.ts、trace-reattach.ts、services/api/client.ts、types/domain.ts |
当前语义权威。Vue 迁移必须保持显式 session、inactivity-timeout、trace reattach、result/trace 同源 API 和 HWPOD evidence label。 |
| HWLAB Web 规格 | docs/reference/spec-v02-hwlab-cloud-web.md |
Code Agent 没有浏览器侧 total-timeout / attempt cap;唯一 abort 信号是 activityRef 驱动的 inactivity-timeout。 |
Sub2API frontend v0.1.136 |
src/api/client.ts、src/stores/auth.ts、src/stores/app.ts、src/composables/useAutoRefresh.ts、src/composables/useClipboard.ts |
可复用 Composition API Pinia store、独立 api module、toast/clipboard/auto-refresh composable 的组织方式;不可复用 localStorage bearer token 鉴权。 |
React Workbench 语义提取
| React 模块 | 当前语义 | Vue 迁移归属 |
|---|---|---|
services/api/client.ts::fetchJson / fetchText |
同源 fetch、credentials=same-origin、统一 ApiResult<T>、recordApiTiming;activityRef 每秒重算 idle window,超时错误必须说明 waitingFor 和 lastEventLabel。 |
src/api/client.ts 保持 fetch client,不换成 Sub2API axios bearer client。 |
services/api/client.ts::api |
同源代理覆盖 auth bootstrap、live probe、workspace、conversation、Code Agent send/steer/result/cancel、HWPOD node-ops、skills 等端点。 | 拆成 src/api/workbench.ts、src/api/auth.ts;只拆模块,不改 endpoint path 和 timeout contract。 |
state/workbench.ts::initialState |
providerProfile、codeAgentTimeoutMs、gatewayShellTimeoutMs 从 localStorage 读取并 clamp;默认 Code Agent inactivity budget 为 1_800_000ms。 |
stores/workbench state 初始化;持久化 key 保持 hwlab.workbench.*.v1。 |
state/workbench.ts::hydrate |
并发读取 /v1/workbench/workspace 和 /v1/agent/conversations,用 workspace selected conversation 生成 message 列表并记住 projectId。 |
stores/workbench.hydrate();首屏可由 useWorkspaceBootstrap 提供 workspace 快照,后续再补 conversation list。 |
state/workbench.ts::refreshLive |
每 30s 读取 health/live、health、REST index、adapter、HWPOD specs、HWPOD node-ops、live-builds,并推导 Code Agent availability。 | stores/workbench.refreshLive() + useAutoRefresh;只能在 workspace 入口启用,性能页不得初始化。 |
state/workbench.ts::createSession / selectConversation / deleteCurrentSession |
session 由用户显式创建、选择、删除;普通 send 不隐式创建 session;select payload 带 updatedByClient。 |
stores/workbench actions;Vue UI 必须先暴露 session 创建/选择入口,再允许普通 turn。 |
state/workbench-reducer.ts::composerFromState |
通过当前 request、running agent message、workspace active trace 推导 composer;running turn 时 submit mode 为 steer 且 route 为 /v1/agent/chat/steer。 |
stores/workbench getter composer;不要在组件中重复推导。 |
state/workbench.ts::submitMessage |
生成 user/pending message,先 persistConversation,再 POST send/steer;submit kickoff 调 recordActivity(),POST 使用同一个 activityRef;terminal 后 merge trace/result 并再次持久化。 |
stores/workbench.submitMessage() 调 useTraceSubscription;message mutation 和 persistence 保持 store 内聚。 |
state/runner-trace.ts::subscribeToTrace |
result 和 trace 两路同源 poll;每次成功 poll 调 onActivity;5xx 不刷新 activity 且继续 poll;非 5xx 失败走 infrastructure error;外层 for(;;) 没有 total timeout。 |
composables/useTraceSubscription.ts 的唯一 active polling 入口。 |
state/runner-trace.ts::mergeTraceResults |
terminal result 与 trace snapshot 合并;优先使用 finalResponse、terminal assistant event、result text,保留 agentRun、events、traceSummary、terminalEvidence。 |
api/workbench 或 composables/useTraceSubscription 附近的纯函数;必须有单元测试。 |
state/trace-reattach.ts |
hydrate 后发现 activeTraceId 且当前没有 pending,就创建 placeholder 并主动订阅到 terminal;完成/失败都写回 conversation。 |
composables/useTraceReattach.ts watch store.activeTraceId 与 store.chatPending。 |
components/hwpod/HwpodNodeOpsSidebar.tsx |
evidence label 为 Contract、Specs、Spec、Node、Ops、per-op label 和 probe;status/freshness/blocker 来源必须是同源 /v1/hwpod-node-ops 与 /v1/hwpod/specs?probe=1。 |
Vue HWPOD panel 保留同名 label、data-hwpod-detail 语义和 blocker/status 规则。 |
Sub2API 分层对照
Sub2API v0.1.136 对照必须来自真实 frontend 源码,不允许凭记忆复写。当前迁移基线读取过以下文件:
| Sub2API 文件 | HWLAB 吸收结论 |
|---|---|
frontend/package.json、frontend/vite.config.ts |
Vue 3、Pinia、Vue Router、Vue I18n、Tailwind/PostCSS 作为单一前端栈;HWLAB 删除 React 入口和 React 依赖,不做长期双栈。 |
frontend/src/main.ts、frontend/src/App.vue |
main.ts 只负责安装 Pinia/router/i18n 并挂载 app;App.vue 保持全局 shell、route outlet、toast/弹窗类横切组件,不承载业务状态机。 |
frontend/src/router/index.ts、frontend/src/router/title.ts |
路由按 public/user/admin 分段懒加载,metadata 表达 auth/admin/title;HWLAB 保留默认首屏 workbench,并保持 Cloud Web 同源入口。 |
frontend/src/components/layout/AppLayout.vue、AppSidebar.vue、AppHeader.vue |
平台壳拆成 layout 组件和导航数据;HWLAB 采用同类分层,但导航语义改为 Workbench、Code Agent、HWPOD、Access、Provider。 |
frontend/src/components/common/NavigationProgress.vue、StatusBadge.vue、Skeleton.vue、EmptyState.vue |
common 组件只承载视觉/反馈原语,可复用为 HWLAB 的状态标记、加载态、空态、导航进度。 |
frontend/src/views/user/DashboardView.vue、frontend/src/views/admin/DashboardView.vue |
user/admin view 只组合 layout、common 和业务 store,不把 API fetch 散落到 app shell;HWLAB 的 workbench/dashboard/admin 页面遵循同样边界。 |
frontend/src/i18n/index.ts、frontend/src/style.css、frontend/tailwind.config.js |
i18n 初始化、Tailwind base/components/utilities、颜色和字体 token 集中管理;HWLAB 保留自己的文案和 Web session 约束,不照搬 Sub2API token/Secret/刷新策略。 |
| Sub2API 形态 | 可吸收部分 | HWLAB 必须改写部分 |
|---|---|---|
api/client.ts axios instance、request/response interceptor、统一 unwrap { code, data } |
api client 是独立模块,业务 endpoint 放在 src/api/*.ts;请求取消保持原错误,便于调用方忽略。 |
HWLAB 不使用 localStorage auth_token、refresh_token 或 401 token refresh queue;保留 same-origin cookie session、ApiResult<T> 和 activityRef inactivity-timeout。 |
stores/auth.ts Composition API Pinia:ref state、computed getter、actions、定时器 cleanup |
auth store 的组织方式、启动时 checkAuth/bootstrap、loading/error state 可复用。 |
HWLAB stores/auth 只消费 /auth/bootstrap、/auth/session、/auth/login、/auth/logout;不得持久化 bearer token 或引入 Sub2API refresh token。 |
stores/app.ts toast、global loading、cached settings |
toast/loading 与 useClipboard 依赖 app store 的方式可复用。 |
Workbench live refresh、workspace hydrate 不放进 app store;app store 只保留 UI 横切状态。 |
composables/useAutoRefresh.ts localStorage 保存 enabled/interval/countdown/fetching,组件卸载清 timer |
可作为 Workbench live refresh 和 HWPOD panel refresh 控件基础。 | 不得用 auto-refresh 替代 Code Agent trace subscription;trace 仍由 useTraceSubscription 无限 poll 并使用 activityRef。 |
composables/useClipboard.ts Clipboard API + textarea fallback + toast |
可直接迁移为工作台复制 trace/result/session id 的通用 composable。 | 成功/失败文案走 HWLAB app store/i18n;不要把复制行为和业务状态 mutation 耦合。 |
Vue Pinia / Composable 目标映射
| 目标文件 | 责任边界 | 必须暴露的接口 |
|---|---|---|
src/api/client.ts |
fetch 基础设施、ApiResult<T>、ActivityRef、inactivity-timeout、RUM timing。 |
fetchJson<T>(path, options)、fetchText(path, options)、type ActivityRef、type ActivityRefSource。 |
src/api/workbench.ts |
Workbench 业务 endpoint wrapper;只表达 HTTP path 和 timeout,不持有 UI state。 | workspace、updateWorkspace、selectConversation、conversations、saveConversation、deleteConversation、sendAgentMessage、steerAgentMessage、getAgentChatResult、cancelAgentMessage、liveSurface、hwpodSpecs、hwpodNodeOpsHealth。 |
src/api/auth.ts |
Web session bootstrap/login/logout wrapper。 | bootstrap()、session()、login(username,password)、logout();返回同源 session 状态。 |
src/stores/auth.ts |
Web session 状态。 | state: authenticated、user、actor、expiresAt、loading、error; actions: bootstrap、login、logout; getter: isAuthenticated。 |
src/stores/workbench.ts |
Workbench durable state、derived composer、session lifecycle、message mutation、conversation persistence。 | state: workspace、conversations、messages、providerProfile、timeouts、live、codeAgentAvailability、chatPending、currentRequest、lastActivityAt; getters: activeProjectId、activeConversationId、sessionTabs、composer、activeTraceId; actions: hydrate、refreshLive、createSession、selectConversation、deleteCurrentSession、submitMessage、cancelAgentMessage、retryAgentMessage、replayAgentTrace、recordActivity、timeout/profile setters。 |
src/composables/useTraceSubscription.ts |
active trace polling lifecycle;替代 React subscribeToTrace。 |
start(config)、stop()、running; config 包含 traceId、projectId、initial、inactivityTimeoutMs、onActivity、onSnapshot、onComplete、onInfrastructureError。 |
src/composables/useTraceReattach.ts |
watch store active trace and reattach after hydrate/refresh。 | 自动 watch enabled、store.activeTraceId、store.chatPending; 创建 placeholder、订阅、完成/失败后 persist。 |
src/composables/useWorkspaceBootstrap.ts |
首屏 bootstrap 合并 auth/workspace,避免重复请求。 | bootstrapWorkspace(projectId) 返回 auth state + workspace snapshot;必须消费 /auth/workspace-bootstrap?projectId=...。 |
src/composables/useAutoRefresh.ts |
UI refresh control for live surface only。 | 复用 Sub2API enabled、intervalSeconds、countdown、fetching、setEnabled、setInterval;shouldPause 默认在 hidden/performance route 时暂停。 |
src/composables/useClipboard.ts |
文本复制和 toast。 | copyToClipboard(text, successMessage?)、copied。 |
Trace / Result 不变量
submitMessage和useTraceReattach必须共享useTraceSubscription,不得拆成 submit-only 和 hydrate-only 两套 polling。getAgentChatResult(resultUrl, inactivityTimeoutMs, activityRef)的 timeout 入参不随 wall-clock 缩小;外层循环没有累计耗时上限、attempt cap 或timeoutMs * N兜底。- 每一次成功 result poll 或 trace poll 都调用
store.recordActivity();5xx retry 不调用,非 5xx 业务失败进入 infrastructure error。 - terminal 完成前至少尝试合并 trace snapshot;final assistant text 优先级保持
finalResponse、terminal assistant event、result text、普通 assistant event。 - steer POST 如果 timeout/ECONNRESET 但目标 trace 仍 running,Vue store 必须释放本次 submit lock、保留原 agent message running/steerable,不得把原 turn 标 failed。
session_failed、thread-resume-failed、provider continuation 失效、运行面中断或用户取消后,不得自动滚动新 session;下一步必须由用户显式创建或选择 session。
迁移顺序
- 先迁
types/domain.ts、trace merge 纯函数和api/client.ts,用无浏览器单测锁住activityRefinactivity 行为。 - 再迁
api/workbench.ts与stores/auth.ts,确认同源 session contract 未引入 bearer token。 - 迁
useTraceSubscription和useTraceReattach,用 fake API 断言无 total-timeout、成功 poll 刷新 activity、5xx 不刷新 activity、terminal result 合并 trace。 - 迁
stores/workbench.ts,把 React reducer 的 action 语义变成 Pinia action/getter;组件只读 getter 和调 action。 - 最后迁 UI 组件,HWPOD、trace panel、session sidebar 和 composer 只消费 store/composable,不内联业务推导。
验证要求
- 源码层:Vue 迁移 PR 至少覆盖
api/clientinactivity、useTraceSubscriptionpolling、stores/workbench.composer、useTraceReattachplaceholder/terminal persistence、HWPOD label mapping。 - 静态检查:保留
bun run --cwd web/hwlab-cloud-web check或 Vue 等价入口;不得恢复旧 quick prompt、旧 layout smoke 或与当前契约无关的门禁。 - 同路径验收:显式创建或选择 Code Agent session 后,通过 Web 同源 path 提交 turn,
result/trace --render web使用同一 trace row;未选择 session 的普通 send 必须返回session_required。 - HWPOD 验收:Vue 面板必须从
/v1/hwpod-node-ops与/v1/hwpod/specs?probe=1显示 status/freshness/blocker,不得用 fixture 或 blocked fallback 声称真实硬件 DEV-LIVE。