155 lines
22 KiB
Markdown
155 lines
22 KiB
Markdown
# 实施进度
|
||
|
||
> 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 2-4 句:做了啥 + 关键判断 + 没动什么;细节查 `git log` / `git diff`。
|
||
|
||
最后更新:2026-05-19(0006 模型切换 + usage_events v2 表:task 级模型 PATCH / `GET /v1/models` / 前端顶栏下拉 + 历史小标 / chat usage 落 messages 双写 + usage_events 一行,A 粒度下条 send 生效)
|
||
|
||
---
|
||
|
||
## 状态
|
||
|
||
| Phase | 标题 | 状态 | 备注 |
|
||
|---|---|---|---|
|
||
| 1-3 | 骨架 + Skill + run_python | ✅ | 三个 skill;CoreCoder 唯一匹配 edit;敏感 env 过滤 |
|
||
| 4 | 演化性能力 | 🟡 | Model Profile + Probing ✅;版本化 prompt 未做 |
|
||
| 5 | Eval Suite | ⏸ 不做 | dogfooding 替代,probe 覆盖健康检查 |
|
||
| 6 | 长任务工程化 | 🟡 | task + 恢复 ✅;双层记忆 ✅;context 压缩未做 |
|
||
| 7 | 打磨 | ❌ | Docker 沙盒 / 更多 skill |
|
||
| §7 SaaS | DESIGN §7 路线 | 🟡 | A 事件流化 ✅;B 完工 ✅;**D `/v1` JSON API ✅**;**D' 过渡 auth(邮箱密码 + platform_key → JWT)+ dev SPA ✅**;**单活 run 锁 + cancel ✅**;**0004 schema 瘦身 ✅**(删 runs/usage_events);**入口归位 ✅**(`cli.py`→`main.py`,装配 lib 挪 `core/agent_builder.py`);真 OIDC 待;C(Executor)待。 |
|
||
|
||
---
|
||
|
||
## 已完成关键能力
|
||
|
||
### 2026-05-19
|
||
|
||
- **0006 模型切换(c 模式 task 级 A 粒度)+ usage_events v2 表**:`tasks.model_profile` 从死字段变 source-of-truth,顶栏下拉 → `PATCH /v1/tasks/{id}` 即换,**A 粒度下条 send 生效**(当前 run 不受影响;running 中切 UI 提示"跑完后生效")。`build_agent` resume 时优先 task.model_profile,新建 task POST body 加可选 `model_profile`(留空 → `cfg["default_model"]`)。`GET /v1/models` 扫 `config/models/*.yaml` 列可选项(含 display_name / thinking_mode / is_default),`ModelCapabilities` 加 `display_name` 字段,deepseek_v4.yaml 两 variant 各填名。**前端**:chat-meta 加下拉(切了 PATCH+提示)、新建对话框 modal 加 `<select id="nt-model">`、message 历史按 `messages.model_profile` 切换点画小标(`── DeepSeek V4 Pro ──`,连续同 model 不重复)。**统计 schema**:0004 删掉的简陋 usage_events 字段不够多态,本次重建 v2 形态(`event_id/user_id/task_id/message_id/kind/model_profile/units jsonb/cost_usd`),chat 已接入(`core/storage/usage.py::record_chat_usage`,`loop.py` 在 assistant message 入库后调,litellm cost map 算钱),媒体扩展位(image/video/audio kind)预留不动 schema。**双写**:同时回填 `messages.tokens_in/out/model_profile`,查 message 详情时不需 JOIN。**索引**:`(user_id, created_at)` / `(task_id)` / `(model_profile, created_at)`,用户级配额 query JOIN-free。**没动**:CLI / RUN.md(无 env / 命令变化)、`tasks.tokens_prompt/completion/cost_usd` 保留作 task 级粗概览。
|
||
- **dev SPA 登录撤回 邮箱+密码,删 invites 表**:前两条"邀请码 env → invites 表(0005)"一日游撤回,复用 users 表本来就有的 email/password_hash 列(0001 schema)+ 0005 加 UNIQUE(email)。`bcrypt` 哈希,新 `/v1/auth/login_password` 路由,新 `main.py user add --email --password` CLI 发用户。dev SPA 登录两 tab(邮箱密码 默认 / UUID+PLATFORM_KEY 备用,last-used 持久化 LS)。判定:邀请码 uuid5(NS,name) 推导对外是黑盒(改 name = 换身份),复用 users 列语义清晰也对齐生产路径。**没动**:JWT 签发 / platform_key 路径 / DB users 表列结构。
|
||
- **邀请码后端 env → invites 表(0005)** _(已撤,见上条;原条目已删,有需要看 git history)_
|
||
- **SENTINEL user 彻底撤(数据 + 代码)**:`SENTINEL_USER_ID = UUID('00000000-...')` 在 web 必从 JWT 拿 user_id 后已无角色,按 CLAUDE.md "不写兼容层" 连根拔。DB CASCADE 删 sentinel user + workspace dotfile 目录;代码 10 处删 import / 默认参数 / fallback,`utils.py` 三函数和 `build_agent` 的 `user_id` 从可选变必填(`build_agent` 加 `*,` 转 KEYWORD_ONLY 规避默认参数顺序)。**Bonus**:把"操作 user 数据的函数必须显式传 user_id"作为 Python 必填参数固化,以后多 user 函数 typechecker 会拦到。
|
||
- **dev SPA 邀请码登录(env 形态)** _(已撤,见 SENTINEL user 撤之后两条,路径整体改邮箱密码)_
|
||
- **任务/文件行 `⋯` 下拉菜单 + 文件顶栏长名截断 + 聊天框上传按钮 + 工具调用 debounce 刷新右侧**:单例浮层菜单(`#floating-menu` position:fixed)避开 pane overflow 裁剪。任务行 4 项(complete/abandon/export/delete,不同颜色,非 active 自动 disable);文件行 3 项(改名/下载/删除);聊天框加上传按钮共用 `<input type="file">`;`tool_result` 事件 debounce 500ms 刷新文件 panel。仅前端,不动后端 / DESIGN / RUN。
|
||
- **proposal skill mermaid hash→caption + quality_check 加图相关 4 拦截 + SKILL.md 精简 + `/v1/files/download` 加 `Cache-Control: no-cache`**:用户反馈"申报 skill 图没渲染到 docx",诊断双层 bug:① 模型写满 ASCII 字符画从未用 mermaid + `![]()`;② SPA 预览命中浏览器启发式缓存(Starlette FileResponse 无 Cache-Control)。修法:render_diagrams 改 caption 强制必填 + 同 task 唯一(撞名退 2);quality_check 加 4 条(figures/ 有 png 但 sections 0 引用 / 围栏含 box-drawing 字符 / mermaid 缺首行 `%% caption:` / caption 撞名);SKILL.md ~193→~160 行。
|
||
- **dev SPA 文件预览弹框**:点击文件不再直接下载,弹 90vw 模态按扩展名分派(image/pdf/text/md→已有 renderMd / docx 用 docx-preview / xlsx 用 SheetJS / pptx 等 fallback "请下载查看")。库懒加载 + blob URL 全局 track + 弹框关时 revoke 防漏;vendor 入 git(jszip / docx-preview / xlsx,~1MB,无 npm 链路就直 vendor 锁版本)。**没动**:后端 app.py(blob URL 路径足够)。
|
||
|
||
### 2026-05-18
|
||
|
||
- **入口归位:`cli.py`→`main.py`,原 `main.py`→`core/agent_builder.py`,删 CLI REPL,§7 E 撤**:`main.py` 混三角色(装配 lib + utility + cli/web 共 import 的事实入口),按 SoC 拆。`git mv` 两次(覆盖)+ 5 处 `from main import` → `from core.agent_builder import`。删 `chat / tasks / export` 三命令 + REPL 主循环 + 内部 helpers(~400 行);新 `main.py` 只剩 `db / probe / web`(后来再加 `user`)。失:CLI 无 auth 直跑 core 通道;补:dev SPA 走同条 web 路径,临时调试写几行 ad-hoc script。
|
||
- **0004 schema 大瘦身:删 runs / usage_events,合 run_status / run_error 入 tasks;路由 run_id → task_id**:`usage_events` 全代码库零写零读,`runs` 表 tokens_p/c 写但从未读(真 tokens 走 tasks 累计),started_at/finished_at/error 也只写不读,`run_id` 唯二实用是 broker 频道键 + cancel 参数 — 单活 run 形态下客户端只需 task_id 就够。`tasks` 加 `run_status text default 'idle'`(idle/running/cancelling/error,error 是唯一持久终态)+ `run_error text`。Broker 全 task_id 索引 + 加 `start(task_id)` 清上轮 done 标记。**dev SPA**:`state.currentRunId` → `state.streaming` bool;cancel POST `/v1/tasks/{tid}/cancel` 去 `/runs/{rid}/`。
|
||
- **`POST /v1/files/rename` + 顶层目录 delete 加 task 引用闸**:**`/v1/files/*` 升格为唯一目录树 mutation 入口,DB-FS 一致性作服务端不变量内化**;`GET /v1/folders` 定位"项目聚合视图",只读。顶层目录(`target.parent.resolve() == root.resolve() and is_dir()`)走 DB-aware 分支:事务内 `SELECT ... FOR UPDATE` 锁关联 task + 任一 running/cancelling → 409 + `check_no_subtask(exclude=被改名 tids)` 防嵌套 + UPDATE 在 FS rename 之前(FS 失败可回滚)。**架构教训(§7.9)**:此前提的双命名空间 `/v1/folders/rename` vs `/v1/files/rename` 反了 — `is_top_level` 分支是**从数据状态派生**(path 恰好是 working_dir),不是客户端意图派生,放服务端是更安全的位置。
|
||
- **task-level cancel + AgentLoop 协作式 cancel + dev SPA stop 按钮**:Broker 加 `request_cancel / is_cancelled / clear_cancel`(per-task `threading.Event`,`setdefault` 保证 BG 还没 register 也能 set)。Loop 加 `cancel_check` callable + `_fill_cancelled_tool_results` 给未执行 tool_call 补 `[cancelled]` tool message(LiteLLM 协议要求 assistant tool_call 必须有匹配 tool result,否则 resume 报错)。**LLM 同步 call 本身不可中断**(litellm 阻塞,无原生 cancel)— 最坏等当前一轮跑完几十秒。Gate 同步扩:`post_message` 单活 run 检查 `status in ('running', 'cancelling')` 避免新旧 BG 撞 messages.idx。
|
||
- **`POST /v1/tasks/{id}/messages` 单活 run 锁 + 孤儿 reaper**:同事务 `SELECT Task ... FOR UPDATE` + 活跃状态检查 + 标 running,三步原子完成避免 TOCTOU(用户连点 send / 多 tab 同时发 → 两 BG 线程争 `messages.idx`)。lifespan 加 reaper:启动时 `UPDATE Task SET run_status='error' WHERE run_status IN ('running','cancelling')` 清进程 crash 留下的孤儿。**未来 TODO**:multi-worker 部署 reaper 不能简单全表清(会误清其他 worker 的真在跑),换 heartbeat + lease。
|
||
- **proposal skill 流程图/结构图管线**:`render_diagrams.py` 扫 sections/*.md mermaid 块 → mmdc(本地)或 mermaid.ink(公网) → png;render_docx 加 `add_picture` 识别 `` 单行 + mermaid 围栏特判;templates 三处占位换成完整 mermaid 例子。图编号 `ctx['fig_no']` 调用链递增不重不漏;mmdc/网络都没的极端环境 docx 仍能产(ASCII 退化)。
|
||
- **system prompt skill 机制改"可选辅助"**:接 GET /v1/skills + 下拉落地后,prompt 第 14 行从 `"永远 load 一下"` 改 `"简单问答/读代码/改 bug/文件操作直接用通用工具就够,不必为每个任务硬套 skill"`;一旦决定要用仍 load 完整指引。**Tradeoff**:边缘场景(用户提"整理大纲")agent 偏向不 load 可能漏掉好的模板,比"什么都套 coding"的噪音更可接受。
|
||
- **`GET /v1/skills` + dev SPA skill 字段改下拉**:lifespan 启动 `SkillRegistry` 扫一次挂 `app.state`(FS 静态运行中不变);返 `{skills:[{name,description}]}` 按 name 升序。前端 `<input>` 换 `<select>` + 首项 `(默认 · 不限定)` 空值;option 文案 `name — description`,失败静默退化为只剩默认项。**没动**:`POST /v1/tasks` body 不校验 `skill ∈ registry`(留空 / 任意串都允许)。
|
||
- **dev SPA 全套 UI 中文化**:静态文案(login / header / pane-head / 操作按钮 / new task modal)+ 动态文案(status badges / role 标签 / SSE 流式提示 / confirm/alert)全面本地化。技术字段(user_id / UUID / token / SSE event 名 / API 字段名)不动 — 都是 schema 层不影响 UI 中文。
|
||
|
||
### 2026-05-17
|
||
|
||
- **0003 schema:name + working_dir + skill 三件套**:用户要任务标识和工作目录解耦(原 name 实际是目录名)。`TRUNCATE tasks CASCADE` + `task_dir → working_dir` + `mode → skill` + 加 `name TEXT NOT NULL`(空表 NOT NULL 不需要 backfill)。新建必传 `name`(显示名,DB NOT NULL,UI 标题用);`working_dir` 可选(留空 fallback 用 name);两者都过 `validate_task_name`。新增 `GET /v1/folders`(FS 非 dotfile 子目录 + 关联 task 计数 + 最后使用时间)给 dev SPA modal 的 datalist 补全用。
|
||
- **`GET /v1/tasks` 分页 + 多维筛选 + ordering(DRF 风格)**:标准分页壳 `{page,page_size,count,results}`;6 个 query(page/page_size/status/skill/working_dir 末段名/q ILIKE name+desc/ordering);`-field` 倒序,allowlist `created_at/updated_at/name/status`,非法字段静默忽略,**默认 `-created_at`**(用户要求,创建时间倒序更稳)。dev SPA 加翻页按钮 + 搜索 debounce 300ms + working_dir datalist autocomplete。
|
||
- **task 硬删 API + dev SPA delete 按钮 + 文件 per-row 删**:`DELETE /v1/tasks/{id}` user_id ownership 校验 + DB 行删(messages CASCADE)+ **FS task_dir 不动**(同 name 多 task 共享时"最后删了顺便 rmtree"易擦用户素材,经 /files/delete 显式清更安全)。dev SPA chat 面板加 `btn-delete-task`(任何 status 都可删,confirm 带项目名 + 消息条数二次确认);file 面板 per-row 加红 `×`。
|
||
- **files API 全面 user-rooted(去掉 task_id 前置)**:原 API 用 task_id 拐杖间接拿 working_dir,迫使前端先选 task。`/v1/files/*` 4 路由改 user-rooted(`workspace/users/<uid>/` 为边界),`_safe_join` 边界改 user_root + 加 dotfile 过滤(`.memory/` 隐藏);dev SPA `loadFiles()` 不再 gate on task_id,enterApp 时直接拉。**架构**:与 §7.1 "task / dir 双视图正交"心智对齐,files 操作不该依赖 task。
|
||
- **files 面板 UX 项目名 + 修 root crumb bug**:用户混淆"空目录"为"看不到文件夹本身",修两处:① 后端 `cur_rel == "."` 不再追加无意义 "." crumb;② 前端 crumbs 第一格 label 从 "/" 改项目名,整条路径直观 `水泥申报 / 草稿 / draft.md`。
|
||
- **task_dir 改 eager mkdir**:原"懒 mkdir(skill 首次写产物时建)"是 UUID-named 时代设计,现在 task_dir 是用户给的项目名,**name = 项目声明**,目录就该 task 创建时存在(用户可立刻塞素材文件)。`build_agent` 新建分支 + `web/app.py::create_task` 都加 `mkdir(parents=True, exist_ok=True)`;同 name 多 task 共享 + 已有内容不被擦。
|
||
- **task = name-based 项目目录 + memory dotfile**:废弃自动 UUID 派生 + `tasks/` 中间层。新建必给 `name`(简单名,项目目录名);`task_dir = workspace/users/<uid>/<name>/`;同 name 多 task 自动共享同目录(§7.1)。memory 搬 dotfile(`workspace/users/<uid>/.memory/{core.md, extended/}`)跟项目目录扁平共存不撞名;`validate_task_name` 拒 `.` 起头双向防呆。`_cleanup_if_empty` 简化:FS 一律不动(跨 task 复用绝不 rmtree),空 task 只删 DB 行。
|
||
|
||
### 2026-05-15
|
||
|
||
- **§7 D 阶段:`/v1` JSON API 落地;Phase G Jinja2/HTMX UI 路线撤**:用户决定与已有 platform 联调,前端用 platform 框架,本仓库再维护 HTML/CSS 就是双套浪费。删 `web/templates/*` + `web/static/*` CSS + jinja2/markdown-it-py/pygments 依赖;重写 `web/app.py` 全 `/v1/` 前缀 JSON;SSE event payload 由 HTML 片段切 JSON(`event: <type>` + `data: <JSON>`)。**沉淀**:G 阶段的 sink 协议 / RunBroker fan-out / no-subtask / files 路径安全归一 / task_dir 相对存储全部保留,不被 UI 层牵连。dev SPA `web/static/dev.html` 留一份升级为本地 dogfood 主路径(单文件 vanilla JS,3 栏)。
|
||
- **§7 D' 过渡 auth(PLATFORM_KEY → JWT)+ dev SPA**:`pyjwt` HS256,`AuthConfig.from_env()` 启动校验 `PLATFORM_KEY` / `JWT_SECRET` 必填(任一缺失 fail-fast);`HTTPBearer` Depends + `make_require_user(cfg)` 工厂闭包持 cfg。数据隔离全 `Task.user_id == user_id` + `_assert_owns_task` helper;跨 user 视为 404 不暴露存在性。**SSE 走 fetch + ReadableStream**(`EventSource` 不支持自定义 header,token 没法塞,手解 SSE frame)。**没动 core**(本地 CLI 路径不进 web auth);**TODO**:真 OIDC 接入(替换 /v1/auth/login 内部为 ID token 校验,路由层不动)。
|
||
- **task_dir 改相对存储**:DB `tasks.task_dir` 原存绝对(`D:\projects\...`),改为 **ROOT 内→相对 posix、ROOT 外→保留绝对**(用户 `--task-dir` 指外部项目场景)。新增 `core/paths.py::{ROOT, to_db_path, from_db_path}` 三出口,所有读写边界统一过这里;alembic 0002 一次 UPDATE 把现有 ROOT-prefix 行转相对。`CLAUDE.md` 加"开发阶段不写兼容层"心智(用户指示)。
|
||
- **workspace 布局统一 per-user**:`workspace/tasks/<uuid>/` + 全局 `workspace/memory/` 改 **`workspace/users/<user_id>/{tasks/<uuid>,memory/}/`**。`build_agent` / memory / web `create_task` 全程透传 user_id;**清旧数据不留兼容**(DELETE tasks CASCADE + `rm -rf workspace/tasks/`)。
|
||
- **litellm 启动 cost map 网络警告兜底**:`import litellm` 之前 `os.environ.setdefault("LITELLM_LOCAL_MODEL_COST_MAP", "True")` 走打包的本地 cost map,跳过 httpx.get;冷启动从 ~5s SSL 超时降到 <1s。
|
||
- **Phase G G1-G6 Jinja2/HTMX Web UI(05-14→05-15)** _(全撤,被 D 阶段 `/v1` JSON API + dev SPA 替换;沉淀的 sink / broker / no-subtask / files 安全归一保留)_
|
||
|
||
### 2026-05-14
|
||
|
||
- **§7.1 心智模型修正:Folder-centric → Task 一等公民 + Dir 文件副视图**:dir 不是 task 父容器;双视图正交。task_dir 留空 = 一次性对话 / 指定 = 项目化 — 这条二分语义入文。
|
||
- **§7 B Steps 1-4 + 6(基建 + Session/TaskState ORM + task_dir 双形态 + no-subtask)**:`core/storage/{engine,models}.py` SQLAlchemy 2.x ORM(5 表)+ alembic + `cli db {upgrade,downgrade,current}` + `ZCBOT_DB_URL` 必填;`core/session.py` messages 走 PG(append-only,jsonb,idx 递增);`core/task.py` TaskState 保留内存 DTO 落地走 PG;`state.json` 全废;`check_no_subtask` 同 user 下查前缀嵌套(Python 端 fetch 后归一比对,跨 OS 分隔符容差)。**取消** Step 5 migrate-from-fs(用户决定不兼容旧 workspace)。
|
||
|
||
### 2026-05-12
|
||
|
||
- **§7 改写**:platform/core 多租户方案废弃,改 user-direct(folder-centric → 后续 §7.1 修为 task-primary;task/messages 入 PG;no-subtask;hard cascade)。
|
||
|
||
### 历史(2026-Q1 → 05-11)
|
||
|
||
- **Phase 1-4**:骨架 / 三 skill / run_python / Model Profile + Probing。ppt v3 加商务红 + apply_brand + Iconify;素材摄取改 markitdown CLI。
|
||
- **05-06 Phase 6 部分**:task + state.json + tokens 累计;CLI `tasks` + REPL `/status /done /abandon /desc`;移除 legacy `workspace/sessions/`。
|
||
- **05-07 TUI + task_dir**:rich Markdown 渲染;spinner 显实时耗时 + 累计 token;system prompt 注入 task_dir 绝对路径,产物收敛 `workspace/tasks/<id>/`。
|
||
- **05-08 REPL 切换 + 懒创建**:`/resume [last|<id>]`;`build_agent` 不预占文件;`_cleanup_if_empty` 三条件守门。
|
||
- **05-09→05-10 §7 草案 + 导出**:DESIGN §7 初版(05-12 重写);`cli.py export <task_id>` + `core/export_docx.py`。
|
||
- **05-11 原子写 + 双层记忆 + §7 A**:`atomic_write_text` 接管 save;`core/memory.py`(core.md 入 prompt,extended/* 走索引);loop 事件流化(`sink.emit`)铺 SSE 路。
|
||
|
||
---
|
||
|
||
## 关键决策与偏差
|
||
|
||
| 项 | 决策 | 备注 |
|
||
|---|---|---|
|
||
| 工具基目录 | cwd(读)+ working_dir(写) | system prompt 同时注入两者绝对路径 |
|
||
| Workspace 布局 | `workspace/users/<user_id>/{.memory/, <name>/}` | per-user 隔离;memory dotfile 防撞;`<name>` 用户起项目名,同 name 多 task 共享 |
|
||
| Eval Suite | 不做 | 个人工具 dogfooding |
|
||
| 版本化 prompt | 直接 `general_v1.md` | Windows 软链接麻烦,真要切再做 |
|
||
| run_python 沙盒 | subprocess + env 过滤 | Docker 在 §7 C 阶段 |
|
||
| 兼容层 | 开发期不写 | DB schema / 字段 / API 改动直接切,见 CLAUDE.md |
|
||
| `/v1/files/*` 与 DB | files API 作目录树唯一 mutation 入口,DB-FS 一致性服务端内化 | rename / delete 顶层目录 DB-aware(SELECT FOR UPDATE + check_no_subtask + 事务回滚) |
|
||
| 单活 run | task 同时最多 1 个活 run | gate 在 `post_message` 同事务 `SELECT FOR UPDATE`,挡连点 send / 多 tab |
|
||
| LLM 同步 call 不可中断 | cancel 协作式 check 在 LLM 之间 + tool_call 之间 | 最坏等当前一轮跑完(几十秒) |
|
||
|
||
---
|
||
|
||
## 文件清单
|
||
|
||
```
|
||
core/capabilities.py 71
|
||
core/llm.py 93 ← litellm 离线 cost map env
|
||
core/loop.py 182 ← §7 A sink.emit + cancel_check 协作式 cancel
|
||
core/sinks.py 101 ← §7 A
|
||
core/ui.py 38
|
||
core/paths.py 50 ← task_dir db form 归一(to_db_path / from_db_path)
|
||
core/probe.py 243
|
||
core/session.py 153 ← §7 B Step 2-3: ORM
|
||
core/skills.py 81
|
||
core/task.py 82 ← §7 B Step 3: PG-backed TaskState
|
||
core/memory.py 81 ← per-user `.memory/` dotfile
|
||
core/export_docx.py 383
|
||
core/storage/__init__.py 29 ← record_chat_usage 出口(0006)
|
||
core/storage/engine.py 80
|
||
core/storage/models.py 130 ← 4 表(0004 删 runs;0005 email UNIQUE;0006 加 usage_events v2 + messages.model_profile)
|
||
core/storage/usage.py 70 ← 0006:record_chat_usage(litellm cost map + 双写 messages + insert usage_events)
|
||
core/storage/utils.py 136
|
||
core/agent_builder.py 307 ← 装配 lib(原 main.py 内容,05-18 改名归位)
|
||
tools/{base,fs,shell,run_python,skill_tool}.py ~440 行
|
||
main.py ~210 ← 入口:web / db / probe / user(05-19 加 user)
|
||
db/migrations/env.py 61
|
||
db/migrations/versions/
|
||
0001_initial_schema.py 125
|
||
0002_task_dir_relative.py 61
|
||
0003_task_name_and_working_dir.py 51
|
||
0004_drop_runs_usage_events.py 77
|
||
0005_users_email_unique.py 28 ← 0005 一日游 invites 已撤,接 users.email UNIQUE
|
||
0006_usage_events_v2_and_message_model.py 60 ← messages.model_profile 列 + usage_events v2 表(多态 units jsonb)
|
||
web/__init__.py 5
|
||
web/app.py ~890 ← /v1 JSON API + user_id 隔离 + run lock + task-level cancel
|
||
web/auth.py ~190 ← D' 过渡:邮箱密码 + platform_key → JWT
|
||
web/broker.py 121 ← in-process pub/sub + cancel signal(全 task_id 索引)
|
||
web/sinks.py 21
|
||
web/static/dev.html ~1700 ← D' dev SPA(3 栏 + 文件预览弹框 + 两 tab 登录)
|
||
web/static/vendor/ ~1 MB ← jszip / docx-preview / xlsx(office 预览)
|
||
─────────────────────────────────
|
||
Python 合计 ~3400 行(+ dev.html 1700 静态 + vendor 1MB)
|
||
```
|
||
|
||
加 `skills/ppt|proposal|coding/` 脚本 ~600 行 + SKILL.md / references / config / prompts + alembic.ini,总仓库约 3500 行。
|
||
|
||
---
|
||
|
||
## 下一步候选(性价比排序)
|
||
|
||
1. **真 OIDC 接入 + CORS 收紧**(~1 天)—— `/v1/auth/login` 内部从 platform_key 校验换成 OIDC ID token 校验(路由层 Depends 不动);CORS 改成 platform 域名 allowlist。**真发布给真实用户前必做**。
|
||
2. **§7 C Executor + sandbox**(~2-3 天)—— `run_python`/`shell` → `Executor.run(...)`,本地保留 subprocess、SaaS 走 docker;`api_key_env` → `KeyProvider` 运行时注入。多用户在线跑代码前置。
|
||
3. **Phase 6 context 三层压缩**(~1 天)—— 兜底,V4 长上下文一般用不到。
|
||
|
||
> §7 B + D + D'(过渡 auth)+ 单活 run 锁 + cancel + 0004 schema 瘦身 + 入口归位 主体已完工。剩余路线:真 OIDC → C(Executor)→ F(deploy / billing)。**§7 E CLI 双模式撤**(2026-05-18,§7.9):dev SPA 已是本地 dogfood 主路径,CLI REPL 删,无 `--remote` 双 transport 维护税。原 Phase G Web UI 路线撤(§7.9),UI 改 platform 端实现;`web/static/dev.html` 是开发期单文件 SPA,跟 platform UI 并存不冲突。
|