Merge pull request #1117 from pikasTech/feat/1115-workbench-store-trace-artificer
docs: 定义 Workbench Vue 迁移语义合同
This commit is contained in:
@@ -97,6 +97,7 @@ HWLAB 是硬件实验室运行面和控制面项目。本文是 agent、指挥
|
||||
- v0.2 CI/CD 加法 lane、`v0.2-gitops`、`hwlab-v02` 和 `19666/19667` 规格:[docs/reference/spec-v02-cicd.md](docs/reference/spec-v02-cicd.md)。
|
||||
- v0.2 `hwlab-cloud-api` API 核心服务规格:[docs/reference/spec-v02-hwlab-cloud-api.md](docs/reference/spec-v02-hwlab-cloud-api.md)。
|
||||
- v0.2 `hwlab-cloud-web` 浏览器工作台规格:[docs/reference/spec-v02-hwlab-cloud-web.md](docs/reference/spec-v02-hwlab-cloud-web.md)。
|
||||
- v0.3 Workbench Vue 迁移 API/store/trace 语义合同:[docs/reference/spec-v03-workbench-vue-migration.md](docs/reference/spec-v03-workbench-vue-migration.md)。
|
||||
- v0.2 Provider API Key 管理页、HWLAB 鉴权后委托 AgentRun 后端和 DeepSeek 官方链路验证规格:[docs/reference/spec-v02-provider-management.md](docs/reference/spec-v02-provider-management.md)。
|
||||
- v0.2 Code Agent 由 `hwlab-cloud-api` 接入 AgentRun v0.1 共享执行基础设施,不再保留 HWLAB 自有 agent manager/worker 控制面:[docs/reference/agentrun-code-agent-dispatch.md](docs/reference/agentrun-code-agent-dispatch.md)。
|
||||
- v0.2 Code Agent trace/result/final response 的权威数据源、派生缓存边界和 Web/CLI trace 展示路径:[docs/reference/spec-v02-code-agent-trace.md](docs/reference/spec-v02-code-agent-trace.md)。
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
# 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 形态 | 可吸收部分 | 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。
|
||||
|
||||
## 迁移顺序
|
||||
|
||||
1. 先迁 `types/domain.ts`、trace merge 纯函数和 `api/client.ts`,用无浏览器单测锁住 `activityRef` inactivity 行为。
|
||||
2. 再迁 `api/workbench.ts` 与 `stores/auth.ts`,确认同源 session contract 未引入 bearer token。
|
||||
3. 迁 `useTraceSubscription` 和 `useTraceReattach`,用 fake API 断言无 total-timeout、成功 poll 刷新 activity、5xx 不刷新 activity、terminal result 合并 trace。
|
||||
4. 迁 `stores/workbench.ts`,把 React reducer 的 action 语义变成 Pinia action/getter;组件只读 getter 和调 action。
|
||||
5. 最后迁 UI 组件,HWPOD、trace panel、session sidebar 和 composer 只消费 store/composable,不内联业务推导。
|
||||
|
||||
## 验证要求
|
||||
|
||||
- 源码层:Vue 迁移 PR 至少覆盖 `api/client` inactivity、`useTraceSubscription` polling、`stores/workbench.composer`、`useTraceReattach` placeholder/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。
|
||||
Reference in New Issue
Block a user