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.
This commit is contained in:
tao.chen
2026-07-22 19:07:58 +08:00
commit c919c95842
61 changed files with 26642 additions and 0 deletions
+167
View File
@@ -0,0 +1,167 @@
# Integration Testing
This folder contains the integration tests of the extension.
They are defined using [Playwright](https://playwright.dev/docs/intro) test runner
and [Galata](https://github.com/jupyterlab/jupyterlab/tree/main/galata) helper.
The Playwright configuration is defined in [playwright.config.js](./playwright.config.js).
The JupyterLab server configuration to use for the integration test is defined
in [jupyter_server_test_config.py](./jupyter_server_test_config.py).
The default configuration will produce video for failing tests and an HTML report.
> There is a UI mode that you may like; see [that video](https://www.youtube.com/watch?v=jF0yA-JLQW0).
## Run the tests
> All commands are assumed to be executed from the root directory
To run the tests, you need to:
1. Compile the extension:
```sh
jlpm install
jlpm build:prod
```
> Check the extension is installed in JupyterLab.
2. Install test dependencies (needed only once):
```sh
cd ./ui-tests
jlpm install
jlpm playwright install
cd ..
```
3. Execute the [Playwright](https://playwright.dev/docs/intro) tests:
```sh
cd ./ui-tests
jlpm playwright test
```
Test results will be shown in the terminal. In case of any test failures, the test report
will be opened in your browser at the end of the tests execution; see
[Playwright documentation](https://playwright.dev/docs/test-reporters#html-reporter)
for configuring that behavior.
## Update the tests snapshots
> All commands are assumed to be executed from the root directory
If you are comparing snapshots to validate your tests, you may need to update
the reference snapshots stored in the repository. To do that, you need to:
1. Compile the extension:
```sh
jlpm install
jlpm build:prod
```
> Check the extension is installed in JupyterLab.
2. Install test dependencies (needed only once):
```sh
cd ./ui-tests
jlpm install
jlpm playwright install
cd ..
```
3. Execute the [Playwright](https://playwright.dev/docs/intro) command:
```sh
cd ./ui-tests
jlpm playwright test -u
```
> Some discrepancy may occurs between the snapshots generated on your computer and
> the one generated on the CI. To ease updating the snapshots on a PR, you can
> type `please update playwright snapshots` to trigger the update by a bot on the CI.
> Once the bot has computed new snapshots, it will commit them to the PR branch.
## Create tests
> All commands are assumed to be executed from the root directory
To create tests, the easiest way is to use the code generator tool of playwright:
1. Compile the extension:
```sh
jlpm install
jlpm build:prod
```
> Check the extension is installed in JupyterLab.
2. Install test dependencies (needed only once):
```sh
cd ./ui-tests
jlpm install
jlpm playwright install
cd ..
```
3. Start the server:
```sh
cd ./ui-tests
jlpm start
```
4. Execute the [Playwright code generator](https://playwright.dev/docs/codegen) in **another terminal**:
```sh
cd ./ui-tests
jlpm playwright codegen localhost:8888
```
## Debug tests
> All commands are assumed to be executed from the root directory
To debug tests, a good way is to use the inspector tool of playwright:
1. Compile the extension:
```sh
jlpm install
jlpm build:prod
```
> Check the extension is installed in JupyterLab.
2. Install test dependencies (needed only once):
```sh
cd ./ui-tests
jlpm install
jlpm playwright install
cd ..
```
3. Execute the Playwright tests in [debug mode](https://playwright.dev/docs/debug):
```sh
cd ./ui-tests
jlpm playwright test --debug
```
## Upgrade Playwright and the browsers
To update the web browser versions, you must update the package `@playwright/test`:
```sh
cd ./ui-tests
jlpm up "@playwright/test"
jlpm playwright install
```
+12
View File
@@ -0,0 +1,12 @@
"""Server configuration for integration tests.
!! Never use this configuration in production because it
opens the server to the world and provide access to JupyterLab
JavaScript objects through the global window variable.
"""
from jupyterlab.galata import configure_jupyter_server
configure_jupyter_server(c)
# Uncomment to set server log level to debug level
# c.ServerApp.log_level = "DEBUG"
+15
View File
@@ -0,0 +1,15 @@
{
"name": "opencode_bridge-ui-tests",
"version": "1.0.0",
"description": "JupyterLab opencode_bridge Integration Tests",
"private": true,
"scripts": {
"start": "jupyter lab --config jupyter_server_test_config.py",
"test": "jlpm playwright test",
"test:update": "jlpm playwright test --update-snapshots"
},
"devDependencies": {
"@jupyterlab/galata": "^5.0.5",
"@playwright/test": "^1.60.0"
}
}
+14
View File
@@ -0,0 +1,14 @@
/**
* Configuration for Playwright using default from @jupyterlab/galata
*/
const baseConfig = require('@jupyterlab/galata/lib/playwright-config');
module.exports = {
...baseConfig,
webServer: {
command: 'jlpm start',
url: 'http://localhost:8888/lab',
timeout: 120 * 1000,
reuseExistingServer: !process.env.CI
}
};
+21
View File
@@ -0,0 +1,21 @@
import { expect, test } from '@jupyterlab/galata';
/**
* Don't load JupyterLab webpage before running the tests.
* This is required to ensure we capture all log messages.
*/
test.use({ autoGoto: false });
test('should emit an activation console message', async ({ page }) => {
const logs: string[] = [];
page.on('console', message => {
logs.push(message.text());
});
await page.goto();
expect(
logs.filter(s => s === 'JupyterLab extension opencode_bridge is activated!')
).toHaveLength(1);
});
View File