The inline prompt now renders two <select> boxes:
- Provider: lists every provider from the cached /config/providers
response. Default = first provider.
- Model: lists that provider's model keys (Object.keys of the Record).
Default = default[providerID] (from the same response) when that
modelID is present in the provider's models; otherwise the first
model. Changing the provider select rebuilds the model select with
the new provider's models and its own default[provider].
On submit the two select values are read separately and passed as
providerId / modelId to the OpenCodeRequest (no more '|' delimiter).
The /opencode-bridge/edit body shape is unchanged (providerId, modelId).
Also fix the index.ts startup console log (p.models is a Record, use
Object.values), and drop the unused _cell field on the prompt widget
(now an unused param).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per the OpenCode server docs (GET /config/providers), each Provider's
'models' is { [modelID: string]: Model } (a Record), so model IDs can be
referenced as strings (matching the 'default' map of providerID ->
modelID). Our code treated it as Model[], which:
1. crashed with 'p.models is not iterable' on real responses, and
2. after the previous Array.isArray guard, skipped EVERY provider
(a Record is never an array), so the inline picker showed nothing.
Update types.ts (OpenCodeProvider.models -> Record, add OpenCodeModel,
add optional 'default' map to the response) and rewrite
flattenProviders to iterate Object.keys(p.models). Update test fixture
to the Record shape.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Some OpenCode Serve /config/providers responses include providers
without a 'models' field (or with a non-array value). The un-guarded
for-of on p.models then throws 'p.models is not iterable' and crashes
the inline prompt construction. Skip such providers instead.
Also re-assert the button label as 'AI' (matches the df2f3df label
simplification, which the 02cedac revert had bundled away).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Previously the inline prompt was Widget.attach'd to cell.node, which
in JupyterLab places it at the very bottom of the cell (after the
output area and In/Out prompt) — so the user couldn't see it, especially
on taller cells. Attaching to cell.inputArea.node puts the prompt
directly under the editor, which is the natural inline position.
Falls back to cell.node if inputArea is unexpectedly null.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tornado rejects fetch(request, **kwargs) when the first arg is already an
HTTPRequest and the kwargs overlap request construction (auth_username /
auth_password). Move auth onto the HTTPRequest. Add OpenCodeClient.close()
to release its AsyncHTTPClient; module-level atexit closes the tornado
singleton so 'jupyter lab' can exit cleanly after SIGINT instead of
hanging on 'received signal 2, stopping'.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Removes the pnpm node-linker so 'jlpm test' / 'jlpm build' run directly
without the YARN_NODE_LINKER=node-modules env override. Re-run 'jlpm
install' to regenerate node_modules in the flat layout (the in-flight
yarn.lock migration in package.json already targets node-modules).
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.