From 2de2be3450330c19bae641c32e1ab0b71f9d00ee Mon Sep 17 00:00:00 2001 From: BDMstudio Date: Thu, 26 Mar 2026 23:10:43 -0700 Subject: [PATCH] feat: add projection bootstrap rollout paths --- .env.example | 6 + README.md | 30 ++ src/components/layout/Sidebar.tsx | 21 +- .../layout/__tests__/Sidebar.test.tsx | 73 +++- .../panels/ProjectionBootstrapPanel.tsx | 168 ++++++++ .../projection-bootstrap-fetcher.test.ts | 53 +++ src/gateway/projection-bootstrap-fetcher.ts | 125 ++++++ src/gateway/types.ts | 60 +++ src/hooks/useGatewayConnection.ts | 36 +- src/i18n/locales/en/layout.json | 1 + src/i18n/locales/en/panels.json | 19 + src/i18n/locales/zh/layout.json | 1 + src/i18n/locales/zh/panels.json | 19 + src/mock/projection-bootstrap.ts | 381 ++++++++++++++++++ .../__tests__/projection-bootstrap.test.ts | 56 +++ src/store/office-store.ts | 16 + tests/setup.ts | 25 ++ 17 files changed, 1086 insertions(+), 4 deletions(-) create mode 100644 src/components/panels/ProjectionBootstrapPanel.tsx create mode 100644 src/gateway/__tests__/projection-bootstrap-fetcher.test.ts create mode 100644 src/gateway/projection-bootstrap-fetcher.ts create mode 100644 src/mock/projection-bootstrap.ts create mode 100644 src/store/__tests__/projection-bootstrap.test.ts diff --git a/.env.example b/.env.example index eec52d87..bf0b0d47 100644 --- a/.env.example +++ b/.env.example @@ -9,3 +9,9 @@ VITE_GATEWAY_TOKEN= # Mock 模式(设为 true 可不连接真实 Gateway 进行开发) # VITE_MOCK=true + +# --- 运营商引导配置(可选)--- +# 运营商引导包 URL(用于从框架层加载 projection bootstrap 数据) +# 支持 HTTP/HTTPS URL 或本地相对路径 +# VITE_PROJECTION_BOOTSTRAP_URL=https://example.com/projection-bootstrap.json +# VITE_PROJECTION_BOOTSTRAP_PATH=/bootstrap/projection-packet.json diff --git a/README.md b/README.md index 83cf87dd..4329e85b 100644 --- a/README.md +++ b/README.md @@ -203,6 +203,36 @@ VITE_MOCK=true pnpm dev This uses simulated Agent data for UI development. +In the current bootstrap, mock mode also loads the framework-generated projection packet sample and seeds the office scene plus the first-batch projection panels from that packet. This is still a mock/dev path only; it does not change the live Gateway contract. + +### Operator-Facing Projection Bootstrap (Real Gateway + bootstrap feed) + +A controlled non-mock/operator path is also available when you want Office to connect to the **real Gateway** while still seeding the first projection panels from a framework-generated packet. + +1. Generate or copy a packet such as: + - `/home/ubuntu/projects/_department/work/openclaw-office-projection-feed-bootstrap-20260325/fixtures/openclaw_office_projection_packet_v0.json` +2. Serve it from the Office app origin, for example: + +```bash +mkdir -p public/bootstrap +cp /path/to/openclaw_office_projection_packet_v0.json public/bootstrap/projection-packet.json +``` + +3. Start Office against the real Gateway and point the operator bootstrap to that URL: + +```bash +VITE_GATEWAY_URL=ws://127.0.0.1:18789 \ +VITE_GATEWAY_TOKEN= \ +VITE_PROJECTION_BOOTSTRAP_URL=/bootstrap/projection-packet.json \ +pnpm dev +``` + +Behavior boundary: +- the real Gateway connection path remains intact +- bootstrap loading is optional and guarded by env vars +- this does **not** introduce a new live Gateway protocol message +- this supports operator-facing rollout progress, but does **not** by itself claim upstream merge + --- ## Project Structure diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx index 9ed61546..ae741ab0 100644 --- a/src/components/layout/Sidebar.tsx +++ b/src/components/layout/Sidebar.tsx @@ -3,6 +3,7 @@ import { useTranslation } from "react-i18next"; import { AgentDetailPanel } from "@/components/panels/AgentDetailPanel"; import { EventTimeline } from "@/components/panels/EventTimeline"; import { MetricsPanel } from "@/components/panels/MetricsPanel"; +import { ProjectionBootstrapPanel } from "@/components/panels/ProjectionBootstrapPanel"; import { SubAgentPanel } from "@/components/panels/SubAgentPanel"; import { CollapsibleSection } from "@/components/shared/CollapsibleSection"; import { SvgAvatar } from "@/components/shared/SvgAvatar"; @@ -17,6 +18,7 @@ export function Sidebar() { const { t } = useTranslation("layout"); const agents = useOfficeStore((s) => s.agents); const selectedAgentId = useOfficeStore((s) => s.selectedAgentId); + const projectionPanels = useOfficeStore((s) => s.projectionPanels); const selectAgent = useOfficeStore((s) => s.selectAgent); const collapsed = useOfficeStore((s) => s.sidebarCollapsed); const setSidebarCollapsed = useOfficeStore((s) => s.setSidebarCollapsed); @@ -76,6 +78,7 @@ export function Sidebar() { const subAgentsSection = getSection("subAgents"); const detailSection = getSection("detail"); const timelineSection = getSection("timeline"); + const projectionSection = getSection("projectionBootstrap"); return (