A JupyterLab extension that bridges the cell UI to a local OpenCode Serve process. The extension is a dual package: a Python server extension exposed under /opencode-bridge/*, plus a TypeScript frontend that registers per-cell toolbars. Backend (Python, tornado) - Slice 1: config + auth + OpenCode HTTP client (tornado.httpclient, no aiohttp). 4 settings in schema/plugin.json (url, user, password, request timeout). - Slice 2: handlers for /hello, /health, /providers, /edit. - Slice 2.1 (correction): SessionManager with 1 notebook = 1 session mapping, async-safe via per-path locks, 404 recovery via invalidate(). Two new endpoints: GET /sessions, DELETE /session?notebook=<path>. - 32 pytest tests pass. Frontend (TypeScript, JupyterLab 4.6) - src/types.ts: CellContext, OpenCodeRequest/Response, OpenCodeSettings. - src/context/cell_context.ts: extract CellContext from a CodeCell + its parent NotebookPanel, structured error collection. - src/api/opencode_client.ts: callOpenCodeEdit, callOpenCodeProviders. - src/components/opencode_cell_footer.ts: OpenCodeCellFooter Widget implementing ICellFooter with 3 buttons (optimize / fix / edit), resolved via this.parent instanceof CodeCell. NOT cellToolbar (does not exist in JL 4.6) and NOT Widget.findParent (removed in @lumino/widgets 2.x). - src/components/opencode_cell_factory.ts: Cell.ContentFactory subclass returning the OpenCodeCellFooter. - src/components/opencode_installer.ts: installOpenCodeEverywhere patches every notebook (existing + new) to use the custom factory. - src/index.ts: registers the factory, loads settings, fetches /providers on activation and logs the list to the console. - 23 jest tests pass (mocked JupyterLab boundary, pnpm path safe). Settings - 6 fields: 3 auth (url/user/password) + 1 timeout + 2 model selection (provider/model). Provider list is fetched at startup from /opencode-bridge/providers and printed to the browser console so users can copy values into Settings Editor. Docs - design.md: 6 sections covering architecture, UI flow, API contract, TS skeletons, session management (v0.2.1 correction), and provider/model selection (v0.2.2 addition). - CLAUDE.md: agent guidance for working in this repo. - TODO.md: remaining work for Slices 4-7 + v0.4+ backlog. CI - Gitea release workflow at .github/workflows/build.yml. - Bark notification helper (non-fatal on failure). Generated artefacts ignored: opencode_bridge/labextension/, _version.py, *.tsbuildinfo, junit.xml, test.ipynb scratch notebook.
5.1 KiB
5.1 KiB
TODO
剩余开发任务。按依赖顺序切片,每步可独立验证。
Slice 4 — Diff 面板 + Yjs 写入
目标:把 OpenCodeCellFooter._handleResponse 里的 Notification.info(...) 替换成真正的 Diff 面板,支持 Accept / Reject / 微调,写入走 Yjs 事务保护。
未决问题先回答再写代码:
- Accept 后是否自动执行 cell?(建议:否,让用户自己决定;slice 5 再说)
- 多个 cell 同时打开 Diff 面板时如何管理?(每个 footer 实例一个面板 vs 全局单例——建议每 cell 一个)
- Diff 视图用什么库?JL 自带
IDiffModel?还是简单diff-match-patch?零新依赖优先
任务:
- 在
src/components/diff_panel.ts新建DiffPanelwidget(继承ReactWidget或纯 LuminoWidget) - 接受 →
cell.model.sharedModel.transact(() => cell.model.sharedModel.setSource(finalSource), 'opencode-bridge-accept') - 拒绝 → 关闭面板
- 微调 → 进入 textarea 编辑模式
- 在
_handleResponse里替换Notification.info调用,改为打开DiffPanel - 新增
src/__tests__/diff_panel.spec.ts测试 accept/reject 路径
验收:
pytest opencode_bridge/tests/仍 32/32 passjest src/__tests__/仍 23/23+ pass(+ diff_panel 新测试)tsc --noEmit干净
Slice 5 — Inline prompt + Edit flow
目标:替换 opencode_cell_footer.ts:127 里的 window.prompt(...),做内嵌 prompt 框,跟 design.md §2 一致。
任务:
- 在
opencode_cell_footer.ts加内嵌 prompt 状态机:closed → open → streaming → done / error 🪄 编辑按钮点击 → 状态变open→ footer 下方滑出 textarea + 发送/取消按钮- 提交 → 状态变
streaming→ 显示进度(用现有 loading 状态) → 完成后切到done→ Diff 面板出现 - 取消 → 状态回到
closed - 多个 cell 同时 open 的 prompt 互相独立(每个 footer 自己的状态)
验收:
- 所有现有测试 pass
- 新增 1 个状态机集成测试
Slice 6 — Fix flow 完整闭环
目标:🐛 排错按钮从"灰着"变成真能用——点击后调用 /edit 带 mode: 'fix',自动注入 traceback 到 context(后端 _build_request_body 已支持,前端只发就行)。
任务:
- 验证 cell 报错后 → fix 按钮可点(已实现)
- 点击 → 后端在
_build_request_body里用error字段拼 traceback(已实现) - 流式回包后 → Diff 面板显示修复版代码(Slice 4 完成后顺带支持)
- 验证 acceptance 后
cell.model.sharedModel.setSource(...)后用户能直接重跑
验收:
pytest opencode_bridge/tests/加一个 fix-mode 的 test(mock OpenCode client 验证 traceback 被拼进 parts)
Slice 7 — 边界场景
目标:把所有 v0.2/v0.2.1/v0.2.2 设计里"未解决"和"边界"集中处理。
任务:
- OpenCode 不可达:前端
HealthHandler返回 503 → 3 个按钮全 disabled + Notification 显示 - 401/403:OpenCode 拒绝 Basic Auth → Notification 提示检查
opencodeServerPasswordsetting - session 失效:OpenCode 返回 404 → 后端
EditHandler调用sm.invalidate(notebook_path)→ 下次get_or_create重建(后端逻辑已在 v0.2.1 实现,补前端 Notification 提示) - 空 notebook path:
EditHandler400 +{error: "missing context.notebookPath"}(已实现,前端检查) - 长 prompt 截断:超 4000 chars 的 prompt 提示用户
- provider/model 拼错:
/config/providers返回 200 但 provider 不存在 → OpenCode 端报错 → 后端EditHandler502 + 错误信息透传 - 取消进行中请求:用户点 ✨ 之后能取消 → AbortController +
client.abort(session_id)调 OpenCode/session/:id/abort
验收:
- 每个边界场景有一个对应的 test(pytest 或 jest,看发生在哪层)
- Notification 文案一致
v0.4+(不在本轮迭代范围内)
来自 design.md §5.6:
- TTL reaper:notebook 关闭后 30 分钟 idle 自动 release session
- 多 server 横向扩展:in-memory state → Redis / sticky session
- 启动清理:Jupyter 重启后调
GET /session列出 OpenCode 端孤儿删除 - 并发 LLM 调用:
asyncio.Semaphore(per_session=1)显式控制 - 流式 UX:v0.3 砍掉流式(同步版),v0.4 加
prompt_async+/eventSSE 多路复用 - Provider/model 选值 UX:升级为 JupyterLab command + QuickPick,替代 console.log
来自 design.md §6.2:
- Settings UI dynamic enum:等 JupyterLab 支持后把 console.log 替换成真正的 settings 渲染器
当前统计
- 后端:32 pytest tests(config + client + routes + session_manager)
- 前端:23 jest tests(types + cell_context + opencode_client + opencode_cell_footer)
- Schema 字段:6(3 auth + 1 timeout + 2 provider/model)
- 设计文档:
design.mdv0.2.2,6 节 - CLAUDE.md 已建
总进度:Slices 1–3.5 完成,4–7 待开发。