Files
tao.chen c919c95842 Initial commit: opencode_bridge JupyterLab extension (Slices 1-3.5)
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.
2026-07-22 19:07:58 +08:00

106 lines
5.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TODO
剩余开发任务。按依赖顺序切片,每步可独立验证。
---
## Slice 4 — Diff 面板 + Yjs 写入
**目标**:把 `OpenCodeCellFooter._handleResponse` 里的 `Notification.info(...)` 替换成真正的 Diff 面板,支持 Accept / Reject / 微调,写入走 Yjs 事务保护。
**未决问题先回答再写代码**
1. Accept 后是否自动执行 cell?(建议:否,让用户自己决定;slice 5 再说)
2. 多个 cell 同时打开 Diff 面板时如何管理?(每个 footer 实例一个面板 vs 全局单例——建议每 cell 一个)
3. Diff 视图用什么库?JL 自带 `IDiffModel`?还是简单 `diff-match-patch`**零新依赖**优先
**任务**
- [ ]`src/components/diff_panel.ts` 新建 `DiffPanel` widget(继承 `ReactWidget` 或纯 Lumino `Widget`
- [ ] 接受 → `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 pass
- `jest 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 的 testmock 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 提示检查 `opencodeServerPassword` setting
- [ ] **session 失效**OpenCode 返回 404 → 后端 `EditHandler` 调用 `sm.invalidate(notebook_path)` → 下次 `get_or_create` 重建(**后端逻辑已在 v0.2.1 实现**,补前端 Notification 提示)
- [ ] **空 notebook path**`EditHandler` 400 + `{error: "missing context.notebookPath"}`(已实现,前端检查)
- [ ] **长 prompt 截断**:超 4000 chars 的 prompt 提示用户
- [ ] **provider/model 拼错**`/config/providers` 返回 200 但 provider 不存在 → OpenCode 端报错 → 后端 `EditHandler` 502 + 错误信息透传
- [ ] **取消进行中请求**:用户点 ✨ 之后能取消 → AbortController + `client.abort(session_id)` 调 OpenCode `/session/:id/abort`
**验收**
- 每个边界场景有一个对应的 test(pytest 或 jest,看发生在哪层)
- Notification 文案一致
---
## v0.4+(不在本轮迭代范围内)
来自 design.md §5.6
- TTL reapernotebook 关闭后 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` + `/event` SSE 多路复用
- Provider/model 选值 UX:升级为 JupyterLab command + QuickPick,替代 console.log
来自 design.md §6.2
- Settings UI dynamic enum:等 JupyterLab 支持后把 console.log 替换成真正的 settings 渲染器
---
## 当前统计
- **后端**32 pytest testsconfig + client + routes + session_manager
- **前端**23 jest teststypes + cell_context + opencode_client + opencode_cell_footer
- **Schema 字段**63 auth + 1 timeout + 2 provider/model
- **设计文档**`design.md` v0.2.26 节
- **CLAUDE.md** 已建
总进度:Slices 1–3.5 完成,47 待开发。