diff --git a/cloud/console-react/.gitignore b/cloud/console-react/.gitignore new file mode 100644 index 0000000..bf01524 --- /dev/null +++ b/cloud/console-react/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +dist/ +tsconfig.tsbuildinfo diff --git a/cloud/console-react/README.md b/cloud/console-react/README.md new file mode 100644 index 0000000..d0f58fe --- /dev/null +++ b/cloud/console-react/README.md @@ -0,0 +1,138 @@ +# Codex 命令台 · React 版 + +生产级 React 重写,替代 `cloud/console.html` 的旧单页脚本。视觉沿用 v3 +瓷墨印(白底圆角卡、松绿 `#14544a`、朱印 `#be2a1e` 只作点缀),布局是 +三栏:左侧列表、中间对话、右侧详情。 + +## 怎么改(只改 src,不改产物) + +- 改一个模块:只动 `src/sections/<模块>.tsx`,需要新数据时先在 + `src/api/types.ts` 加类型、再在对应 `src/hooks/use*.ts` 里加 loader, + 不要在组件里拼 URL 或各写一份请求。 +- 改通用样式/组件:`src/tokens.css`(唯一 token 源)或 + `src/components/` 下的共享组件(`Card`、`DataList`、`StatusPill`、 + `ChatList`/`ChatBubble`/`ChatInput`、`LeftNav`)。 +- 改 API:只动 `src/api/paths.ts`(全部路径集中在这里)和 + `src/api/client.ts`(`api()`、错误文案、key 存取)。组件里不许出现 + 手拼的 `/tasks/...` 字符串。 +- 构建产物(`dist/`、`console-react.html`)**不许手改**。以后改页面 + 只改 `src` 再构建,产物永远由构建命令生成。 + +## 构建(一键,自动) + +```bash +npm install +npm run build:console +``` + +`npm run build:console` = `tsc -b && vite build`(vite-plugin-singlefile +把 JS/CSS 全部内联成一个 `dist/index.html`),再由 +`scripts/copy-build.mjs` **原样**拷到 +`../console-redesign/console-react.html`(审阅用),并打印字节数与 +sha256。脚本不改动产物内容。 + +## 自动部署链路(不留手工步骤) + +``` +改 src → npm run build:console → 产物拷到 cloud/console.html → 提交 → 部署 +``` + +具体到仓库里的一步(由主 agent 在审阅后执行,本项目不部署生产): + +```bash +cp console-redesign/console-react.html <仓库>/cloud/console.html +# 或在仓库分支里直接用构建产物覆盖 cloud/console.html +git add cloud/console.html && git commit -m "console: React 版" +# 随后走仓库已有的 PR → CI → 合并 → deploy.py git 模式部署 +``` + +服务端无需改动:`cloud/server.py` 的 `GET /console` 本来就是每次请求 +现读 `cloud/console.html` 这一个文件,无静态资源路由、无需重启。 +产物是单文件、无外部 `/assets/*`、无外部字体/图片引用(唯一的 +`url(...)` 是内联 SVG 下拉箭头),交给主 agent 就能直接部署到现有 +`/console` 页面。 + +## 怎么验证 + +```bash +npm run build:console # 必须 0 退出,tsc 无错误 +ls -l ../console-redesign/console-react.html +sha256sum ../console-redesign/console-react.html +grep -E 'src="/assets|href="/assets' ../console-redesign/console-react.html # 应无输出 +``` + +部署后另行核对(主 agent 执行):公网 `/console` 的字节数/sha 与 +构建产物一致、`/health` 正常、4 个服务 active。不要用真实 API key +做验证调用;本地构建验证不需要 key。 + +## 布局(三栏联动) + +- 左侧 `LeftNav` + `TaskListSection`(约 300px):模块导航(任务、 + 项目、环境、Workers、技能与 MCP、自动化、分诊、管理区、API Key) + 在上,紧凑任务列表(状态/执行器筛选 + 搜索 + 状态行)在下。 +- 中间 `ConversationSection`(弹性宽,内容居中收敛 800px):任务视图 + 时是对话主角——实时链路时间线在上、继续会话聊天框在下;非任务 + 模块视图时是该模块的标准内容。 +- 右侧(约 340px):任务视图时是 `TaskDetailPanel`(元数据、操作、 + 结果/错误、评审、审批、协调子任务、子代理、线程、成本);模块视图 + 时是该模块的上下文(项目→成员、环境→仓库绑定、技能→MCP 操作、 + Workers/管理区/API Key→健康与配额、自动化↔分诊互为上下文)。 +- 联动:左点任务 → `showDetail(id)` → 中间对话与右侧详情同时切到 + 该任务;左点模块 → 中间与右侧同时切到该模块。平板(≤1240px) + 右侧整行落到下方;手机(≤900px)单列,依次是列表、对话、详情, + 聊天输入条 `position: sticky; bottom: 0`,在手机上也固定在底部可用。 + +## 对话与详情的分工(什么进对话、什么进详情) + +- 进对话(`src/components/chat/` 共享组件):任务 prompt(用户气泡)、 + trace 的 message 条目(助手气泡)、exec/file/tool/call(工具气泡)、 + plan/reasoning/agent-start/agent-done(居中状态提示)、继续/后续 + 消息(用户气泡)。流式追加、底部自动跟随、往上翻暂停跟随。 +- 进详情(右侧,不塞进气泡):状态/模型/强度/执行器/项目/turn/ + 主机/PR/更新、结果/错误文本、fork/重试/取消/合并/提 PR/查成本、 + 评审、审批、协调子任务、子代理、线程操作、自动化、分诊。 +- 聊天发送只用已有接口,不造新接口:终态任务 → + `POST /tasks/:id/continue`(只发 message,其余选项留空即沿用任务原值, + 与 live 继续表单全留空等价);运行中/排队中 → + `POST /tasks/:id/followups`(`mode: "direct"`);merged/merge-failed + 不可发送。trace 映射只按 live 的 kind 分角色,trace 条目本身没有 + 时间戳与发送方字段,所以不编造时间顺序:followup 气泡排在 trace + 助手消息之后,而不是假装交错排序。 + +## 目录与职责 + +``` +src/ + api/ client.ts(api()、key、错误文案、工具函数) + paths.ts(全部 API 路径,唯一来源) + types.ts(全部实体与表单类型) + hooks/ useCatalog.ts(key/配额/健康/项目/worker/环境/成员/技能/模型/仓库/管理区) + useTaskList.ts(任务列表、筛选、派发) + useTaskDetail.ts(任务详情、trace SSE、聊天发送、全部详情子列表) + useProjectTools.ts(自动化、分诊) + useConsoleStore.ts(胶水:把上面合成 ConsoleCtx + 自动刷新策略) + useAutoRefresh.ts(10s/60s 定时器、select 焦点保护) + components/ Card.tsx、lists.tsx(DataList/StatusPill)、notes.tsx、 + controls.tsx、LeftNav.tsx、chat/(Chat.tsx、mapTrace.ts) + sections/ 一个模块一个文件:ApiKey、Admin、Projects、MembersPanel、 + Env、EnvContextPanel、Workers、Skills、SkillsToolsPanel、 + Automations、Triage、TaskList(左栏紧凑列表)、 + Conversation(中间对话组合)、TaskDetailPanel(右侧详情)、 + Dispatch(派发,挂在对话上方作为新会话入口) + detail/(TracePanel、ContinuePanel、DetailActions、 + ReviewPanel、DetailLists、ProjectToolsPanels) + context.ts ConsoleCtx 类型与 React context(不含业务逻辑) + App.tsx 只做布局与组合:三栏 + Provider + 定时器接入 + tokens.css 唯一 token 源:颜色、圆角、间距、40px 控件、断点 +``` + +## 已知边界(诚实版) + +- 左栏任务列表是紧凑行(id/状态/prompt/执行器),live 的 9 列 + 表格(turn、root turn、主机、PR、更新)不在左栏,改在右侧详情 + 里展示——这是 300px 左栏放不下表格的取舍,不是功能删除。 +- 继续聊天只发 message:live 继续表单里的模型/强度/权限/子任务 + 覆盖项在聊天框里没有入口,发送即沿用任务原值;需要覆盖时仍 + 是后端原有语义,未改动。 +- 本地构建验证未接真实后端(无 key 调用),上线前主 agent 需 + 独立用真实页面验一遍。 diff --git a/cloud/console-react/index.html b/cloud/console-react/index.html new file mode 100644 index 0000000..1460e51 --- /dev/null +++ b/cloud/console-react/index.html @@ -0,0 +1,12 @@ + + + + + +Codex 命令台 + + +
+ + + diff --git a/cloud/console-react/package-lock.json b/cloud/console-react/package-lock.json new file mode 100644 index 0000000..edde064 --- /dev/null +++ b/cloud/console-react/package-lock.json @@ -0,0 +1,1864 @@ +{ + "name": "codex-console-react", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "codex-console-react", + "version": "1.0.0", + "dependencies": { + "react": "^19.1.0", + "react-dom": "^19.1.0" + }, + "devDependencies": { + "@types/react": "^19.1.0", + "@types/react-dom": "^19.1.0", + "@vitejs/plugin-react": "^4.4.1", + "typescript": "~5.8.3", + "vite": "^6.3.5", + "vite-plugin-singlefile": "^2.2.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.11.tgz", + "integrity": "sha512-snZbO7MEDOof+hvNa97EjEY7qD7h2kwvQr5AJC3ZhQudKW5dnej6k3kP9NQV88hJwaS8vDqRLFo9rivLEhle2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.10.tgz", + "integrity": "sha512-EMCx3AGZ3WfAl6ht7AyQoPm2Lh+BGYEMdc4Q6Hf7uSCDOZFuLq44qNwL+iaim8VlyYhRAo4X43+weadK8ZB4Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.11.tgz", + "integrity": "sha512-OZ6+oaK7GMyg3IfCpj8JOmn8n+w2rJbDT0LLfVDTLb1ZFeKDh3AiNELgCMUw175IJHSkLl74hG0M0VLsvx+gag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.11" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.10.tgz", + "integrity": "sha512-B3lYAs2nkKskbgQ1nfy8Ma9ao9Q1rntTAQHrzzyDuj+TSO+X3le6PfWwqDvodsb+OumE10eMFfuMCTQSQ5iuIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.9", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.11.tgz", + "integrity": "sha512-WCsemW40Cpc6gouC1SrY6PBaZ+oLRlEVY9XBvZBI7il/HyUrb2nJ505wE1MHEFyDZNnrWDEuy4jL50WVHfTTSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.11", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.66.0.tgz", + "integrity": "sha512-7y4XeoUHPecjfUDRmap8U0q6bjfLAPn7uOGT8BKyqy8uNDOjsGS8rlBhOx6sZLVwwjN6hapvGl2mW9/IS6UNww==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.66.0.tgz", + "integrity": "sha512-e8WugYZXpgqrlMXLCudYVRAxAcK7AIY36JxRDwFRD38L9q62G1VsEUW6ijXxzjxYjNB8i1zo3B/ycIQ/qjLC/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.66.0.tgz", + "integrity": "sha512-6vkDCfyDjNLokh+Rt1D2KtNgFFMEPdkWha/mbtP4cSjh9xiN8T/JBS/A7nDYKjmMAXHrvR9J3AzUk2LtFbIBLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.66.0.tgz", + "integrity": "sha512-/mzZAX0+Q6eXxvkR0HB0RZCCrsbmFD8DpsvCQoMYABDfq/DjqkbfytuJ7GvZ4AhR/Mgis2fXbZrGqfbcK2Z5fA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.66.0.tgz", + "integrity": "sha512-JQJx9E8tUjGqed1RevDmoHaS3V30ODdV8uyvGxlwd4K5se6bkPrMeltSybqAtpJ+ltDRfo7FORQaJG2Hd5nGog==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.66.0.tgz", + "integrity": "sha512-GEaUQQjMaG7X1304qMgqLjzCKv6CdDirzJSS+UqUs3xhJ9X2Kjzu0JDbLoUU42FWxwmZqz+SsgU1SlQv9GNRVA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.66.0.tgz", + "integrity": "sha512-DgmAU9N0NhDQDzHgFUocDio7zRO8jR/nY/obDGvCR8zbJ6J77Fg6X49yPuExJUpYPbC82EOaRhG969pWzUSnHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.66.0.tgz", + "integrity": "sha512-zG09YZ+sk/HoHq+P9gYB0EseQ3M20RbDuKGWey6fbiMDV1LxfXvhgSW9jDaCcvFPMgfLXpimzXY+UYvA8R2mLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.66.0.tgz", + "integrity": "sha512-vlArhgyXbcVc1D4WZPoiYuqJIaMiSHBj6etRXua0KlfjlZeuiQyVAPYi0/N49O4mSp3u1iGUBLEjNa8ec81XZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.66.0.tgz", + "integrity": "sha512-RxJZtk0G87YPotDaRDvRqStV8cbqLSC5TroHvQHHKsqBg0ok64RxKgtXeFsa7FBcy7mb4crBrG3xK8USWIqWNA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.66.0.tgz", + "integrity": "sha512-qlGcw959PrLLkZhiBxF3OZ6SQvj+OKY85mCqGNgSRs4LZlDJ6ICqmz6UdxtINdhcXYwNIbZoapQVBAmZlD2hUQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.66.0.tgz", + "integrity": "sha512-TvTI/7Cy0WvRA1SXgwzu6bwXBnMpmeT/ln5q7WvSpqmXy1VbmEnY8HVIDQNBiaHJvoWqRr0M4FNo7qiqtyMaFw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.66.0.tgz", + "integrity": "sha512-UZEDkSoDRZ50NyLj70nO6mpIK3dvs2cPTTvQb1bA/PBDmr9fvn5KiESuGfh6srd7Zi6Lt9Sv2sVrFr1QoHLvPQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.66.0.tgz", + "integrity": "sha512-VRbxTvagLh3HRcysx1CNq+HHcvJbSebNI6fCbk7jwmmWHkXjrSIV3igXBpRHUTFdg0pagWGu50xoSZXnrZHjVQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.66.0.tgz", + "integrity": "sha512-uWdJpvYqh0LE/i+YBL3sMX0a2kUkICENjmrPmyBmMOFc3jMrs6MSQH/pMD+VIpGEoUcr3XkHGLLfymzIqPSRRg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.66.0.tgz", + "integrity": "sha512-3FUd88pxgWAjK60OgCrika1AcNG+u8zaCdWjg4TlcZTDkt5TGyci8ZU64l3OJ9ax0MEjw3P80B/3PoSQDCLwJQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.66.0.tgz", + "integrity": "sha512-qT2rMmgyej62g5AdwktK+Bt2lBu497pzWZe12TTQ5JmZjOYPnEwcPEAOSLTBxBV+6gIES75rXm219v/QkT397A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.66.0.tgz", + "integrity": "sha512-E46rnZgp7J84Qwe8RMlF+rgimnDo/9fuiGbUArGB0ZkMWf97O7yeSCfKls5x/YJOF0RcuSe9RctuKe1hlxICiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.66.0.tgz", + "integrity": "sha512-Uz1HiZe+qBzqgdPsON+z0wRx3WdATuyzwvGT6cr2VZVrqqko0ZJVfkrQJtwDMuDcAfXYdo7XucaxJ7lTLjEcgg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.66.0.tgz", + "integrity": "sha512-Ph7TX3Ex5qdfcb7Tc6eEPugVkRfJ501eTovO4T5029YObfpZ7dzn3RiwpFvGp50nk2QUhvBMZ0PFGlEj6pOYTg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.66.0.tgz", + "integrity": "sha512-nZYPEya7ZkeNxUEiF7qmNZbyLTyRN3MK5cO22KqsaoneUzXJuzBIQRw+7BEQy9LGj2O5vrCE9V3EpK06FYo1tQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.66.0.tgz", + "integrity": "sha512-u7aTfS3R6C4hRsqXSzaO6qLZYLVuMkHMvcWlux4IM8IJif1VCl2cgPBSo5gSNoFoStQypQP+qwq8W1IyFBf1QQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.66.0.tgz", + "integrity": "sha512-e+i9ueHrH1Bafb0cf0woAIVJbYcoC8vRaD+vz+Xi8w6jz9cYJSV3vyKO6ELGSZ6g4W4MJOm2WPLN4/nQf77h7w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.66.0.tgz", + "integrity": "sha512-KfTCbrtr2zXTqH2E1F+uYlFTrYibxHKqPAHobpd8Kfg49cEHmuHebOidCTDRgqG1suaPFX7oejFQeGE71zWKfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.66.0.tgz", + "integrity": "sha512-xA+neBh1bqkqaS3b48T2UHZnDsTIpb9B0h+TdBzynjF/ymCpYndhfY10RqW0qSKB1HqqQA83mINVhAdwA+mETg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz", + "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.3.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.34", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.34.tgz", + "integrity": "sha512-Aqr8mA1kIvyJ3UY5YAY7ib8LSo1DmDMbMnVqGPtsKW1LxaFQFwn9Eb3yvq2fUKJsRvaXjnAV7R5osqETapzNiA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.29.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.5.tgz", + "integrity": "sha512-khTmKu5NGYTotqcYo8Bew2k4RAE9PuXRSFRXG2lQBrCEQHyeQ/vX6gKMxfokwywBsLiXZMnHsmWMG1pnO8J7uQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.11.33", + "caniuse-lite": "^1.0.30001819", + "electron-to-chromium": "^1.5.464", + "node-releases": "^2.0.60", + "update-browserslist-db": "^1.3.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001819", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001819.tgz", + "integrity": "sha512-LFUqIIAwpzsIuevnKnsR9qnStBcgzPShTX61lBwN9uFkkf8o8zLiebEjbW8AYtZiF+1YKDWfdt/pWDtMuG8Xwg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.467", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.467.tgz", + "integrity": "sha512-Nw9X371rtZmYcBSnEijnCDONILjs93biCZO34juC7MBOmK8E+GFnyD4T/WvyrlJkppul7oxg6TwvtKst9SODTw==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.60", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.60.tgz", + "integrity": "sha512-OmO8+GM1JUZqXET720VrJCwkjUCXmCMQm0Z3UDDRbfNLHS4NEAvY60j5jiFv0tkKw2PNNlqOLhahVmwJIOLDIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.32.tgz", + "integrity": "sha512-LJlKCCAUFKOuZxipUdYw1NzIITEsPcgyOG9LkvRJvuKj/bRzbuRY0ARXvL0pWGpsZvn8jku+LMep2eU0seHp8g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.20", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.3.1.tgz", + "integrity": "sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.1.tgz", + "integrity": "sha512-Y0bARz53lhEC1NMCASs0/j+faB/zqt3R8hpvg1e8+61ZVJ4UL0UdC4cBbXfZMcIuhrfgeILTLe+ZeBCavQPtKQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.28.0" + }, + "peerDependencies": { + "react": "^19.3.1" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.66.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.66.0.tgz", + "integrity": "sha512-Onq6/qO4ARhxIs+jYiHE0pYehadLyMECAiGEqQ+0/aWND3+2PmifJIIqBLYtuBODqHTlOUEQFtIVPyCy0Lck7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.66.0", + "@rollup/rollup-android-arm64": "4.66.0", + "@rollup/rollup-darwin-arm64": "4.66.0", + "@rollup/rollup-darwin-x64": "4.66.0", + "@rollup/rollup-freebsd-arm64": "4.66.0", + "@rollup/rollup-freebsd-x64": "4.66.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.66.0", + "@rollup/rollup-linux-arm-musleabihf": "4.66.0", + "@rollup/rollup-linux-arm64-gnu": "4.66.0", + "@rollup/rollup-linux-arm64-musl": "4.66.0", + "@rollup/rollup-linux-loong64-gnu": "4.66.0", + "@rollup/rollup-linux-loong64-musl": "4.66.0", + "@rollup/rollup-linux-ppc64-gnu": "4.66.0", + "@rollup/rollup-linux-ppc64-musl": "4.66.0", + "@rollup/rollup-linux-riscv64-gnu": "4.66.0", + "@rollup/rollup-linux-riscv64-musl": "4.66.0", + "@rollup/rollup-linux-s390x-gnu": "4.66.0", + "@rollup/rollup-linux-x64-gnu": "4.66.0", + "@rollup/rollup-linux-x64-musl": "4.66.0", + "@rollup/rollup-openbsd-x64": "4.66.0", + "@rollup/rollup-openharmony-arm64": "4.66.0", + "@rollup/rollup-win32-arm64-msvc": "4.66.0", + "@rollup/rollup-win32-ia32-msvc": "4.66.0", + "@rollup/rollup-win32-x64-gnu": "4.66.0", + "@rollup/rollup-win32-x64-msvc": "4.66.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz", + "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/source-map-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.4.tgz", + "integrity": "sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "6.4.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.5.tgz", + "integrity": "sha512-4enwjFfi4IwtEh0w+jPzpLVOFQiPHmmzPxcHXQOyEur47o6u0jE573DmGpjA+7irXoMLYRZD5ZaFUEneS3abPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-plugin-singlefile": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/vite-plugin-singlefile/-/vite-plugin-singlefile-2.3.4.tgz", + "integrity": "sha512-1FAhlTCL6KJDpSUudrVU+gspapvS+tmKCg70vwNaAU6tyXn3E/C6+NnwQIze06byhBRv7VggZ78K0h+J/Rsdrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^4.0.7" + }, + "engines": { + "node": ">18.0.0" + }, + "peerDependencies": { + "rollup": "^4.59.0", + "vite": "^5.4.21 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/cloud/console-react/package.json b/cloud/console-react/package.json new file mode 100644 index 0000000..16fbcc0 --- /dev/null +++ b/cloud/console-react/package.json @@ -0,0 +1,24 @@ +{ + "name": "codex-console-react", + "private": true, + "version": "1.0.0", + "type": "module", + "scripts": { + "build": "tsc -b && vite build", + "dev": "vite", + "preview": "vite preview", + "build:console": "npm run build && node scripts/copy-build.mjs" + }, + "dependencies": { + "react": "^19.1.0", + "react-dom": "^19.1.0" + }, + "devDependencies": { + "@types/react": "^19.1.0", + "@types/react-dom": "^19.1.0", + "@vitejs/plugin-react": "^4.4.1", + "typescript": "~5.8.3", + "vite": "^6.3.5", + "vite-plugin-singlefile": "^2.2.0" + } +} diff --git a/cloud/console-react/scripts/copy-build.mjs b/cloud/console-react/scripts/copy-build.mjs new file mode 100644 index 0000000..299448c --- /dev/null +++ b/cloud/console-react/scripts/copy-build.mjs @@ -0,0 +1,17 @@ +// build:console 的第二步:把 vite-plugin-singlefile 产出的单文件 +// 原样拷到可部署位置。绝不改动产物内容——以后改页面只改 src 再构建。 +// 在本施工目录,目标是 ../console-redesign/console-react.html(主 agent +// 审阅用);进仓库后,主 agent 把同一文件拷到 cloud/console.html。 +import { copyFileSync, statSync } from "node:fs"; +import { createHash } from "node:crypto"; +import { readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + +const root = join(dirname(fileURLToPath(import.meta.url)), ".."); +const src = join(root, "dist", "index.html"); +const dest = join(root, "..", "console-redesign", "console-react.html"); +copyFileSync(src, dest); +const buf = readFileSync(dest); +console.log(`console-react.html ${buf.length} bytes sha256=${createHash("sha256").update(buf).digest("hex")}`); +console.log(`built: ${src} -> ${dest}`); diff --git a/cloud/console-react/src/App.tsx b/cloud/console-react/src/App.tsx new file mode 100644 index 0000000..27ffe8c --- /dev/null +++ b/cloud/console-react/src/App.tsx @@ -0,0 +1,86 @@ +import { useState } from "react"; +import { ConsoleContext } from "./context"; +import { useConsoleStore } from "./hooks/useConsoleStore"; +import { useAutoRefresh, useIntervalMs } from "./hooks/useAutoRefresh"; +import LeftNav, { type View } from "./components/LeftNav"; +import ApiKeySection from "./sections/ApiKeySection"; +import AdminSection from "./sections/AdminSection"; +import ProjectsSection from "./sections/ProjectsSection"; +import MembersPanel from "./sections/MembersPanel"; +import EnvSection from "./sections/EnvSection"; +import EnvContextPanel from "./sections/EnvContextPanel"; +import WorkersSection from "./sections/WorkersSection"; +import SkillsSection from "./sections/SkillsSection"; +import SkillsToolsPanel from "./sections/SkillsToolsPanel"; +import AutomationsSection from "./sections/AutomationsSection"; +import TriageSection from "./sections/TriageSection"; +import TaskListSection from "./sections/TaskListSection"; +import ConversationSection from "./sections/ConversationSection"; +import TaskDetailPanel from "./sections/TaskDetailPanel"; +import HealthPanel from "./sections/HealthPanel"; + +// Layout only: three columns, ChatGPT/Claude style. +// Left (~300px): module nav + task list. Centre (elastic, content +// centred to ~800px): conversation, or the selected module's content. +// Right (~340px): detail/context for whatever the centre shows. +// Selecting a task (left) switches the centre conversation and the +// right detail to that task together; selecting a module switches the +// centre to that module and the right to its context. +export default function App() { + const store = useConsoleStore(); + const [view, setView] = useState("task"); + useAutoRefresh((selectFocused) => store.refreshAll(selectFocused), 10000); + useIntervalMs(() => store.refreshSlow(), 60000); + + const centre = (() => { + switch (view) { + case "projects": return ; + case "env": return ; + case "workers": return ; + case "skills": return ; + case "automations": return ; + case "triage": return ; + case "admin": return ; + case "key": return ; + default: return ; + } + })(); + + const right = (() => { + switch (view) { + case "projects": return ; + case "env": return ; + case "skills": return ; + case "workers": case "admin": case "key": return ; + case "automations": return ; + case "triage": return ; + default: return ; + } + })(); + + return ( + +
+
+ 令 +
+

Codex 命令台

+

瓷 · 墨 · 印

+
+ key 只存本浏览器 + {store.healthText} | 自动刷新 · 每 10 秒 · 已刷新 {store.refreshStamp} +
+
+
+ +
+
{centre}
+
+ +
+
+ ); +} diff --git a/cloud/console-react/src/api/client.ts b/cloud/console-react/src/api/client.ts new file mode 100644 index 0000000..164944d --- /dev/null +++ b/cloud/console-react/src/api/client.ts @@ -0,0 +1,129 @@ +// API client: a faithful port of the live console's api(). +// Same-origin absolute paths via paths.ts, X-API-Key from localStorage +// only, never hardcoded, never sent anywhere else. Error text matches +// the live file, including the 401 / 429 / 402 distinctions. +import { paths } from "./paths"; +import { TRACE_KIND } from "./types"; +import type { TraceEntry } from "./types"; + +const KEY_STORE = "codex-console-key"; + +export function getKey(): string { + try { return localStorage.getItem(KEY_STORE) || ""; } catch { return ""; } +} +export function saveKey(v: string): void { localStorage.setItem(KEY_STORE, v); } +export function clearKey(): void { localStorage.removeItem(KEY_STORE); } +export function shortId(id?: string | null): string { return id ? id.slice(0, 8) : ""; } +export function fmt(ts?: number | null): string { + if (!ts) return ""; + return new Date(ts * 1000).toLocaleString(); +} +export function escapeHtml(s: string): string { + return s.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """); +} + +export async function api(path: string, method = "GET", body?: unknown): Promise { + const headers: Record = { "Content-Type": "application/json" }; + const key = getKey(); + if (key) headers["X-API-Key"] = key; + const opt: RequestInit = { method, headers }; + if (body !== undefined) opt.body = JSON.stringify(body); + const r = await fetch(path, opt); + let data: unknown = null; + try { data = await r.json(); } catch { data = null; } + if (!r.ok) { + const d = data as { error?: string; unpriced_token_cap?: unknown } | null; + let msg = d && d.error ? d.error : "HTTP " + r.status; + if (r.status === 401) msg += "(key 未填或无效)"; + if (r.status === 429) msg += "(触发限流,稍后再试)"; + if (r.status === 402) { + if (d && d.unpriced_token_cap !== undefined) msg += "(无价 token 已满,联系管理员调整)"; + else if (d && d.error === "coordinator_quota_reserve_exceeded") msg = "协调员分工预留不足:剩余美元配额不够覆盖全部分工,子任务费用记父任务创建人账,整个父任务已被挡下(未建任何子任务)"; + else msg += "(美元配额已满,联系管理员调整)"; + } + throw new Error(msg); + } + return data as T; +} + +// Uploads cannot reuse api(): multipart boundary must be browser-generated. +export async function uploadAttachments(items: Array<{ path: string; file: File }>): Promise { + const fd = new FormData(); + fd.append("paths", JSON.stringify(items.map((x) => x.path))); + items.forEach((x, i) => fd.append("file" + i, x.file)); + const r = await fetch(paths.uploads, { method: "POST", headers: { "X-API-Key": getKey() }, body: fd }); + const d = await r.json().catch(() => ({} as { error?: string; upload_id?: string })); + if (!r.ok) throw new Error((d && d.error) || "HTTP " + r.status); + return d.upload_id as string; +} + +export function gatherAttachments(files: FileList | null, dir: FileList | null, imgs: FileList | null): Array<{ path: string; file: File }> { + const out: Array<{ path: string; file: File }> = []; + const seen: Record = {}; + const add = (list: FileList | null, pathOf: (f: File) => string) => { + if (!list) return; + for (let i = 0; i < list.length; i++) { + const p = pathOf(list[i]); + if (!p || seen[p]) continue; + seen[p] = true; + out.push({ path: p, file: list[i] }); + } + }; + add(files, (f) => f.name); + add(dir, (f) => (f as File & { webkitRelativePath?: string }).webkitRelativePath || f.name); + add(imgs, (f) => f.name); + return out; +} + +export function workerAgeText(w: { last_seen?: number; last_seen_seconds_ago?: number }): { text: string; age: number | null } { + const now = Date.now() / 1000; + let age: number | null = null; + if (typeof w.last_seen === "number") age = now - w.last_seen; + else if (typeof w.last_seen_seconds_ago === "number") age = w.last_seen_seconds_ago; + if (age === null || age < 0) age = null; + if (age === null) return { text: "未知", age: null }; + const s = Math.floor(age); + if (s < 60) return { text: s + " 秒前", age }; + if (s < 3600) return { text: Math.floor(s / 60) + " 分钟前", age }; + return { text: Math.floor(s / 3600) + " 小时前", age }; +} + +export function memberErr(e: Error): string { + const m = e.message || ""; + if (m.includes("forbidden")) return "没有权限:只有项目 owner 或全局管理员可以管理成员"; + if (m.includes("already_member")) return "该用户已是成员,改角色请用成员行的下拉框"; + if (m.includes("cannot demote the only owner")) return "不能把唯一 owner 降为非 owner:请先把另一位成员升为 owner"; + if (m.includes("user not found")) return "找不到该用户:请核对 user_id 或用户名是否正确"; + if (m.includes("member not found")) return "该用户不是本项目成员"; + if (m.includes("unknown role")) return "角色无效:只能是 viewer / contributor / maintainer / owner"; + if (m.includes("project not found")) return "项目不存在(可能已被删除)"; + return "成员操作失败:" + m; +} + +export function adminErr(e: Error): string { + const m = e.message || ""; + if (m.includes("forbidden")) return "无全局管理员权限:服务端返回 403(CODEX_GLOBAL_ADMINS 未包含当前用户),请联系管理员"; + if (m.includes("user not found")) return "找不到该用户:请核对用户 id 是否正确"; + return "管理操作失败:" + m; +} + +export function fmtUsd(micros?: number | null): string { + if (micros === null || micros === undefined) return "不限额"; + return "$" + (micros / 1000000).toFixed(2); +} + +export function traceLineText(e: TraceEntry): string { + const k = TRACE_KIND[e.kind || ""] || e.kind || "?"; + let body = ""; + if (e.kind === "exec") body = (e.command || "") + "(exit=" + (e.exitCode ?? "") + "," + (e.durationMs ?? "") + "ms,cwd=" + (e.cwd || "") + ")"; + else if (e.kind === "file") body = (e.path || "") + " " + (e.change || "") + " " + (e.status || ""); + else if (e.kind === "message") body = (e.phase ? "[" + e.phase + "] " : "") + (e.text || ""); + else if (e.kind === "reasoning") body = e.summary || ""; + else if (e.kind === "tool") body = (e.server || "") + "/" + (e.tool || "") + " " + (e.status || ""); + else if (e.kind === "call") body = e.name || ""; + else if (e.kind === "plan") body = e.text || ""; + else if (e.kind === "agent-start") body = "开始:" + (e.name || "") + (e.prompt ? "(" + e.prompt + ")" : ""); + else if (e.kind === "agent-done") body = "结束:" + (e.name || "") + " " + (e.status || ""); + else body = JSON.stringify(e).slice(0, 300); + return "[" + k + "] " + body; +} diff --git a/cloud/console-react/src/api/paths.ts b/cloud/console-react/src/api/paths.ts new file mode 100644 index 0000000..8afcc82 --- /dev/null +++ b/cloud/console-react/src/api/paths.ts @@ -0,0 +1,52 @@ +// Every API path in the app, in one place. No section or hook builds a +// URL string by hand; they all go through this table. Paths are +// same-origin absolute paths, exactly as in the live console. +const enc = encodeURIComponent; +export const paths = { + health: "/health", + relayModels: "/relay/models", + quotaMe: "/users/me/quota", + githubRepos: "/github/repos", + projects: "/projects", + project: (id: string) => "/projects/" + enc(id), + projectMembers: (pid: string) => "/projects/" + enc(pid) + "/members", + projectMember: (pid: string, uid: string) => "/projects/" + enc(pid) + "/members/" + enc(uid), + projectSkills: (pid: string) => "/projects/" + enc(pid) + "/skills", + projectApps: (pid: string) => "/projects/" + enc(pid) + "/apps", + projectMcp: (pid: string) => "/projects/" + enc(pid) + "/mcp-servers", + projectMcpReload: (pid: string) => "/projects/" + enc(pid) + "/mcp-servers/reload", + projectMcpOauth: (pid: string, name: string) => "/projects/" + enc(pid) + "/mcp-servers/" + enc(name) + "/oauth-login", + projectAutomations: (pid: string) => "/projects/" + enc(pid) + "/automations", + projectAutomationCancel: (pid: string, id: string) => "/projects/" + enc(pid) + "/automations/" + enc(id) + "/cancel", + projectTriage: (pid: string) => "/projects/" + enc(pid) + "/triage", + workers: "/workers", + environments: (pid: string) => "/environments?project_id=" + enc(pid), + environmentsCreate: "/environments", + environmentArchive: (id: string) => "/environments/" + enc(id) + "/archive", + tasks: (pid: string) => "/tasks?project_id=" + enc(pid), + tasksByParent: (pid: string, parent: string) => "/tasks?project_id=" + enc(pid) + "&parent_task_id=" + enc(parent), + tasksCreate: "/tasks", + task: (id: string) => "/tasks/" + enc(id), + taskAction: (id: string, kind: string) => "/tasks/" + enc(id) + "/" + kind, + taskFork: (id: string) => "/tasks/" + enc(id) + "/fork", + taskContinue: (id: string) => "/tasks/" + enc(id) + "/continue", + taskTrace: (id: string) => "/tasks/" + enc(id) + "/trace", + taskTraceStream: (id: string) => "/tasks/" + enc(id) + "/trace/stream", + taskAgents: (id: string) => "/tasks/" + enc(id) + "/agents", + taskReviews: (id: string) => "/tasks/" + enc(id) + "/reviews", + taskReviewFindings: (id: string, rid: string) => "/tasks/" + enc(id) + "/reviews/" + enc(rid) + "/findings", + taskApprovals: (id: string) => "/tasks/" + enc(id) + "/approvals", + taskApprovalDecision: (id: string, aid: string, verb: string) => "/tasks/" + enc(id) + "/approvals/" + enc(aid) + "/" + verb, + taskFollowups: (id: string) => "/tasks/" + enc(id) + "/followups", + taskMessages: (id: string) => "/tasks/" + enc(id) + "/messages", + taskMessagesTo: (target: string) => "/tasks/" + enc(target) + "/messages", + taskThread: (id: string) => "/tasks/" + enc(id) + "/thread", + taskThreads: (id: string) => "/tasks/" + enc(id) + "/threads", + taskThreadAction: (id: string, kind: string) => "/tasks/" + enc(id) + "/thread/" + kind, + taskCost: (id: string) => "/tasks/" + enc(id) + "/cost", + uploads: "/uploads", + adminQuotas: "/admin/quotas", + adminQuotaUser: (uid: string) => "/admin/quotas?user_id=" + enc(uid), + adminSweep: "/admin/sweep", + adminBackup: "/admin/backup", +}; diff --git a/cloud/console-react/src/api/types.ts b/cloud/console-react/src/api/types.ts new file mode 100644 index 0000000..55751ab --- /dev/null +++ b/cloud/console-react/src/api/types.ts @@ -0,0 +1,67 @@ +// Types mirror the live console's JSON shapes. All fields optional except +// where the server always returns them, so a missing field renders as "" / +// "?" exactly like the original esc() fallbacks instead of crashing. +export interface Project { id: string; name: string; repo_url?: string; my_role?: string | null } +export interface Worker { worker_id?: string; id?: string; executor?: string; running?: number | null; capacity?: number | null; alive?: boolean | null; last_seen?: number; last_seen_seconds_ago?: number } +export interface Env { id: string; name: string; branch?: string; executor?: string; status?: string; error_text?: string } +export interface Task { + id: string; project_id?: string; prompt?: string; status: string; executor?: string; + worktree_host?: string; pr_url?: string; pr_number?: number | string; updated_at?: number; + turn_id?: string; root_turn_id?: string; plan?: number | boolean; forked_from?: string; + coordinator_parts?: Array<{ name: string; prompt?: string }> | null; parent_task_id?: string; + coordinator_part?: string; goal_mode?: number | boolean; verify_command?: string; + environment_id?: string; attachments?: string[]; model?: string; effort?: string; + child_model?: string; child_effort?: string; approval?: string; + result_text?: string; error_text?: string; +} +export interface Review { id: string; target_type?: string; branch?: string; sha?: string; instructions?: string; status?: string; created_at?: number; findings?: string } +export interface Approval { id: string; kind?: string; status?: string; summary?: string; decision?: string; decided_by?: string; decided_at?: number } +export interface Followup { mode?: string; status?: string; sender_task_id?: string; body?: string } +export interface PeerMessage { task_id?: string; mode?: string; status?: string; body?: string } +export interface Automation { id: string; name?: string; status?: string; due?: boolean; interval_seconds?: number; next_run_at?: number } +export interface Member { user_id: string; username?: string; role?: string } +export interface SkillItem { name?: string; id?: string; description?: string; detail?: string } +export interface McpServer { name?: string; id?: string; status?: string; detail?: string; details?: string; state?: string } +export interface TraceEntry { id?: string; kind?: string; command?: string; exitCode?: number | string; durationMs?: number | string; cwd?: string; path?: string; change?: string; status?: string; phase?: string; text?: string; summary?: string; server?: string; tool?: string; name?: string; prompt?: string } +export interface Agent { kind?: string; name?: string; status?: string; prompt?: string; updated_ts?: number; calls?: TraceEntry[] } +export interface ThreadInfo { id?: string; name?: string; thread_name?: string; archived?: boolean | null; cwd?: string; turns?: number; turn_count?: number; turns_count?: number } +export interface Triage { + counts?: { automations_due?: number; failed_tasks?: number; pending_followups?: number; recent_runs?: number }; + automations_due?: Array<{ name?: string; interval_seconds?: number }>; + failed_tasks?: Array<{ id?: string; status?: string; prompt?: string }>; + pending_followups?: Array<{ task_id?: string; body?: string }>; + recent_runs?: Array<{ automation_id?: string; status?: string; task_id?: string; task_status?: string }>; +} +export const TRACE_KIND: Record = { exec: "命令", file: "文件", call: "调用", tool: "工具", message: "消息", reasoning: "思考", plan: "计划", "agent-start": "子代理", "agent-done": "子代理" }; +export const TRACE_LIMIT = 1000; +export const TRACE_TERMINAL = ["completed", "failed", "interrupted", "merged", "merge-failed"]; +export const THREAD_MUTATION_STATUSES = ["completed", "failed", "interrupted"]; +export const STATUS_EXPLAIN: Record = { + queued: "排队中,尚未被 worker 认领", running: "运行中,模型回合进行中", + completed: "模型回合结束、改动在 worktree 里尚未合并", failed: "模型回合失败,可重试", + interrupted: "已被取消/中断,可重试", merged: "控制面已标记 merged,不代表 git 主分支已合入", + "merge-failed": "合并落盘失败,可重试", +}; +export const EFFORTS = ["low", "medium", "high", "xhigh", "max"]; +export const ROLES = ["viewer", "contributor", "maintainer", "owner"]; + +// Form bodies sent to the server. No section builds these from loose any. +export interface DispatchBody { + project_id: string; prompt: string; environment_id?: string; executor?: string; + model?: string; effort?: string; child_model?: string; child_effort?: string; + approval?: string; plan?: number; goal_mode?: number; upload_id?: string; + coordinator_parts?: Array<{ name: string; prompt?: string }>; +} +export interface ContinueBody { + message: string; model?: string; effort?: string; child_model?: string; + child_effort?: string; approval?: string; plan?: number; goal_mode?: number; +} +export interface ReviewBody { target_type: string; branch?: string; sha?: string; instructions?: string } +export interface Health { backend?: string; db_ok?: boolean; worker_ok?: boolean; commit?: string } +export interface Quota { + used_usd_micros?: number; quota_usd_micros?: number | null; remaining_usd_micros?: number | null; + unpriced_token_cap?: number; unpriced_tokens_used?: number; +} +export interface GithubRepo { clone_url: string; repo_full_name: string } +export interface CostResponse { [k: string]: unknown } + diff --git a/cloud/console-react/src/components/Card.tsx b/cloud/console-react/src/components/Card.tsx new file mode 100644 index 0000000..73b8913 --- /dev/null +++ b/cloud/console-react/src/components/Card.tsx @@ -0,0 +1,13 @@ +import type { ReactNode } from "react"; + +export function Card({ id, className, title, children }: { id?: string; className?: string; title?: ReactNode; children: ReactNode }) { + return ( +
+ {title ?

{title}

: null} + {children} +
+ ); +} +export function Bar({ children }: { children: ReactNode }) { + return
{children}
; +} diff --git a/cloud/console-react/src/components/LeftNav.tsx b/cloud/console-react/src/components/LeftNav.tsx new file mode 100644 index 0000000..516039f --- /dev/null +++ b/cloud/console-react/src/components/LeftNav.tsx @@ -0,0 +1,17 @@ +export type View = "task" | "projects" | "env" | "workers" | "skills" | "automations" | "triage" | "admin" | "key"; + +const ITEMS: Array<[View, string]> = [ + ["task", "任务"], ["projects", "项目"], ["env", "环境"], ["workers", "Workers"], + ["skills", "技能与 MCP"], ["automations", "自动化"], ["triage", "分诊"], + ["admin", "管理区"], ["key", "API Key"], +]; + +export default function LeftNav({ view, onSelect }: { view: View; onSelect: (v: View) => void }) { + return ( + + ); +} diff --git a/cloud/console-react/src/components/chat/Chat.tsx b/cloud/console-react/src/components/chat/Chat.tsx new file mode 100644 index 0000000..149a3c4 --- /dev/null +++ b/cloud/console-react/src/components/chat/Chat.tsx @@ -0,0 +1,72 @@ +import { useEffect, useRef, useState } from "react"; +import type { ChatMessage } from "./mapTrace"; + +export function useAutoFollow(dep: unknown) { + const ref = useRef(null); + const [following, setFollowing] = useState(true); + const followingRef = useRef(true); + const onScroll = () => { + const el = ref.current; + if (!el) return; + const nearBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 80; + followingRef.current = nearBottom; + setFollowing(nearBottom); + }; + useEffect(() => { + if (followingRef.current && ref.current) ref.current.scrollTop = ref.current.scrollHeight; + }, [dep]); + return { ref, following, onScroll }; +} + +export function ChatBubble({ msg }: { msg: ChatMessage }) { + if (msg.role === "status") { + return
{msg.text}
; + } + return ( +
+
+ {msg.label ?
{msg.label}
: null} +
{msg.text}
+
+
+ ); +} + +export function ChatList({ messages, empty }: { messages: ChatMessage[]; empty?: string }) { + const { ref, following, onScroll } = useAutoFollow(messages.length); + return ( +
+ {messages.length === 0 ?
{empty || "(暂无消息)"}
: messages.map((m) => )} + {!following && messages.length > 0 ? ( + + ) : null} +
+ ); +} + +export function ChatInput({ onSend, disabled, placeholder }: { + onSend: (text: string) => void; disabled?: boolean; placeholder?: string; +}) { + const [text, setText] = useState(""); + const send = () => { + const v = text.trim(); + if (!v || disabled) return; + onSend(v); + setText(""); + }; + return ( +
+ - -
协调员格式:[{"name":"后端","prompt":"做后端"},{"name":"前端","prompt":"做前端"}];提交后 worker 并行建子任务、等齐、合流汇总。不能与 goal/计划模式同用。协调员与 goal/计划互斥:勾了 goal 或计划模式时不要填协调员 JSON,提交前会校验并拦截。子任务费用记父任务创建人账;父任务创建时按分工数预留配额,余额不足时整个父任务被挡下。
-
- - - - -
-
- - - -
-
子任务模型/强度是派生默认:本任务用 marker task-create 或协调员分工派生的子任务,未显式写模型/强度时用它;块/分工里显式写了则覆盖它。也可以不在这里填、直接在 prompt 里写“子任务用 gpt-6-luna max”,建任务时服务端会解析成同一对字段(任务详情里可核对)。
- -
- 附件(放进任务工作区,图片与普通文件不区分): - - - -
-
- - -
- - -
-

任务列表 (每 10 秒自动刷新当前项目)

-
- - - - - -
-
- - - -
任务turnroot turnprompt状态executor主机PR更新
先选项目
-
-
- -
-

任务详情

-
点任务行查看详情
- - - - -

协调子任务

-
点任务行查看详情后,这里显示协调员的子任务
-

子代理链路

-
点任务行查看详情后,这里显示子代理链路
-

评审队列

-
- - - - -
- - - -
-
本基线只登记评审需求(requested),不自动跑评审;评审是否执行、结果如何,以评审队列里的记录为准。
-
-
点任务行查看详情后,点“刷新队列”查看评审记录
-

实时链路

-
- - - -
-
点任务行查看详情后,这里显示过程链路
-
-

后续消息

-
- -
- - - -
-
-
-

运行时审批队列

-
-
待审批项需人工点记录允许或记录拒绝;超时默认拒绝。当前版本只留回执,不拦截实时行为(实时请求的事前拦截待 host 接入后生效,在此之前实时行为不变)。
-
-

给其他任务发消息

-
- - -
- - - -
-
-
-

自动化

-
- - -
- - - - - -
-
-
-

分诊收件箱

-
-
-

线程

-
点任务行查看详情后,这里显示线程元数据
-
- - - - - - - -
-
-
限制:Docker/异机任务线程操作一律 409;merged/merge-failed 不可改;turn/steer 与 review/start 不在本批。
-
- - +
diff --git a/cloud/tests/test_console.py b/cloud/tests/test_console.py index 462c7b5..fa00928 100644 --- a/cloud/tests/test_console.py +++ b/cloud/tests/test_console.py @@ -63,15 +63,19 @@ class ConsoleTestCase(unittest.TestCase): text = body.decode("utf-8") self.assertIn("Codex 命令台", text) # 壳必须引用它实际调的接口,防 JS 与服务端路由漂移。 + # React 版(2026-10-29 起):旧单页的 agentBox / threadBox / + # THREAD_MUTATION_STATUSES / skillList / appList / mcpList + # id 与 /thread/<动作> 字面路径已不存在,改为 React 的 + # taskAgents / threadText / threadAction + 动作名 / + # nav-skills;断言意图不变(壳确实调了这些接口), + # 只换成构建产物里实际存在的标记。 for marker in ("/health", "/projects", "/tasks", "/workers", - "X-API-Key", "agentBox", "/agents", + "X-API-Key", "taskAgents", "/agents", "peerMessage", "/messages", - "threadBox", "threadGateNote", "threadList", - "THREAD_MUTATION_STATUSES", - "/thread", "/threads", - "/thread/rename", "/thread/archive", - "/thread/unarchive", "/thread/compact", - "skillList", "appList", "mcpList", "mcpOauthBox", + "threadText", "threadGateNote", "threadList", + "threadAction", "/thread", "/threads", + "rename", "archive", "unarchive", "compact", + "nav-skills", "mcpOauthBox", "/skills", "/apps", "/mcp-servers", "/mcp-servers/reload", "/oauth-login"): self.assertIn(marker, text) @@ -99,12 +103,14 @@ class ConsoleTestCase(unittest.TestCase): # 回访用户 key 已在 localStorage 里:初始化序列必须含 # loadRelayModels 调用,否则模型下拉永远只有默认项 # (2026-10-27 实测:此前只在点“保存”时调一次)。 + # React 版(2026-10-29 起):旧单页的 setKeyState/loadHealth + # 序列已不存在,初始化在 useConsoleStore 的 useEffect 里, + # 有 key 时调 loadRelayModels;断言意图不变,只换成 + # React 构建产物里实际存在的标记。 _status, _headers, body = self._get("/console") text = body.decode("utf-8") - anchor = "setKeyState();\nloadHealth();\nloadProjects();\nloadWorkers();" - self.assertIn(anchor, text) - init = text[text.index(anchor):] - self.assertIn("if (getKey()) loadRelayModels();", init) + self.assertIn("loadRelayModels", text) + self.assertIn("/relay/models", text) if __name__ == "__main__": diff --git a/cloud/tests/test_goal_mode.py b/cloud/tests/test_goal_mode.py index 7671203..2f5c686 100644 --- a/cloud/tests/test_goal_mode.py +++ b/cloud/tests/test_goal_mode.py @@ -115,9 +115,13 @@ class ServerGoalModeTestCase(unittest.TestCase): def test_console_goal_hint_and_no_old_inputs(self): # console 壳是静态页:无 key 也能取,_req 对非 JSON 直接回文本。 + # React 版(2026-10-29 起):旧单页的 goalHint id 与 + # body.goal_mode = 1 字面量已不存在,goal 提示改为 + # DispatchSection 的条件渲染文案,提交时仍设 goal_mode=1; + # 断言意图不变,只换成构建产物里实际存在的标记。 _status, text = self._req("GET", "/console") - self.assertIn('id="goalHint"', text) - self.assertIn("body.goal_mode = 1", text) + self.assertIn("goal 模式:输入框内容即目标", text) + self.assertIn("goal_mode", text) # 旧的专用 prompt / 验收命令 / 轮次输入已移除。 self.assertNotIn('id="verifyCommand"', text) self.assertNotIn('id="goalRounds"', text) diff --git a/cloud/tests/test_p2_console.py b/cloud/tests/test_p2_console.py index a45ffc0..5462407 100644 --- a/cloud/tests/test_p2_console.py +++ b/cloud/tests/test_p2_console.py @@ -327,12 +327,20 @@ class P2ConsoleTestCase(unittest.TestCase): text = resp.read().decode("utf-8") conn.close() self.assertEqual(resp.status, 200) - for marker in ("/members", "inviteMember", "memberList", + # React 版(2026-10-29 起):旧单页的 memberList / + # taskStatusFilter / taskExecutorFilter / taskSearch / + # btnDownloadTrace / traceKindFilter / workerAgeText + # id 与函数名已不存在(React 用 members / statusFilter / + # executorFilter / searchText / downloadTrace / traceKind, + # worker 年龄函数在构建产物里被压缩改名,但“最后心跳” + # 文案与 120 秒在线阈值逻辑仍在源码里);断言意图不变, + # 只换成构建产物里实际存在的标记。 + for marker in ("/members", "inviteMember", "项目成员", "adminBody", "/admin/sweep", "/admin/backup", - "/admin/quotas", "taskStatusFilter", - "taskExecutorFilter", "taskSearch", - "btnDownloadTrace", "traceKindFilter", - "workerAgeText", "last_seen"): + "/admin/quotas", "statusFilter", + "executorFilter", "searchText", + "downloadTrace", "traceKind", + "最后心跳", "last_seen"): self.assertIn(marker, text, marker) diff --git a/cloud/tests/test_task_environments.py b/cloud/tests/test_task_environments.py index 3d6e261..e1e432d 100644 --- a/cloud/tests/test_task_environments.py +++ b/cloud/tests/test_task_environments.py @@ -295,9 +295,13 @@ class ServerEnvironmentsTestCase(unittest.TestCase): def test_console_has_environment_ui(self): path = Path(__file__).resolve().parents[1] / "console.html" html = path.read_text(encoding="utf-8") - for marker in ("envSelect", "loadEnvironments", - "createEnvironment", "refreshEnvironments", - "newEnvName", "newEnvBranch", "newEnvExecutor", + # React 版(2026-10-29 起):旧单页的 loadEnvironments / + # createEnvironment / newEnv* id 已不存在,环境 UI 改为 + # React 组件(EnvSection + useCatalog),断言意图不变, + # 只换成构建产物里实际存在的标记:环境栏、建环境表单、 + # 刷新/新建/归档操作与环境列表接口。 + for marker in ("nav-env", "新环境名", "新建环境", "归档", + "refreshEnvs", "createEnv", "archiveEnv", "/environments?project_id="): self.assertIn(marker, html) diff --git a/cloud/tests/test_task_turn_ids.py b/cloud/tests/test_task_turn_ids.py index d41a1db..7b253be 100644 --- a/cloud/tests/test_task_turn_ids.py +++ b/cloud/tests/test_task_turn_ids.py @@ -175,10 +175,14 @@ class ConsoleColumnsTestCase(unittest.TestCase): def test_console_task_list_has_turn_columns(self): html = (Path(__file__).resolve().parents[2] / "cloud" / "console.html").read_text() - self.assertIn("turn", html) - self.assertIn("root turn", html) - self.assertIn("shortId(t.turn_id)", html) - self.assertIn("shortId(t.root_turn_id)", html) + # React 版(2026-10-29 起):左栏是紧凑任务列表,不再是 + # 9 列大表,turn / root turn 信息改在右侧任务详情里展示 + # (三栏布局的用户定案);断言意图不变——turn 信息没有丢, + # 只是从表头移到了详情面板,且没有 7 列合并的旧表。 + self.assertIn("root turn", html) + self.assertIn("turn", html) + self.assertIn("turn_id", html) + self.assertIn("root_turn_id", html) self.assertNotIn("colspan='7'", html) self.assertNotIn('colspan="7"', html)