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:
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* Shared types for the opencode-bridge frontend.
|
||||
*
|
||||
* These mirror the Python server's `CellContext` / `OpenCodeRequest` / `OpenCodeResponse`
|
||||
* shapes in `opencode_bridge/routes.py`. Keep them in sync.
|
||||
*/
|
||||
|
||||
export interface ErrorOutput {
|
||||
ename: string;
|
||||
evalue: string;
|
||||
traceback: string[];
|
||||
}
|
||||
|
||||
export interface CellContext {
|
||||
notebookPath: string;
|
||||
cellId: string;
|
||||
language: string;
|
||||
cellIndex: number;
|
||||
totalCells: number;
|
||||
source: string;
|
||||
previousCode: string | null;
|
||||
error: ErrorOutput | null;
|
||||
}
|
||||
|
||||
export type OpenCodeMode = 'optimize' | 'fix' | 'edit';
|
||||
|
||||
export interface OpenCodeRequest {
|
||||
mode: OpenCodeMode;
|
||||
prompt: string;
|
||||
context: CellContext;
|
||||
providerId?: string;
|
||||
modelId?: string;
|
||||
}
|
||||
|
||||
export interface OpenCodeSuccess {
|
||||
ok: true;
|
||||
mode: OpenCodeMode;
|
||||
finalSource: string;
|
||||
sessionId: string;
|
||||
notebookPath: string;
|
||||
}
|
||||
|
||||
export interface OpenCodeFailure {
|
||||
ok: false;
|
||||
error: string;
|
||||
}
|
||||
|
||||
export type OpenCodeResponse = OpenCodeSuccess | OpenCodeFailure;
|
||||
|
||||
/** Frontend view of the 4 schema/plugin.json fields. */
|
||||
export interface OpenCodeSettings {
|
||||
opencodeServerUrl: string;
|
||||
opencodeServerUser: string;
|
||||
opencodeServerPassword: string;
|
||||
requestTimeoutSeconds: number;
|
||||
opencodeProvider: string;
|
||||
opencodeModel: string;
|
||||
}
|
||||
|
||||
export const DEFAULT_OPENCODE_SETTINGS: OpenCodeSettings = {
|
||||
opencodeServerUrl: 'http://127.0.0.1:4096',
|
||||
opencodeServerUser: 'opencode',
|
||||
opencodeServerPassword: '',
|
||||
requestTimeoutSeconds: 120,
|
||||
opencodeProvider: '',
|
||||
opencodeModel: '',
|
||||
};
|
||||
|
||||
export function readOpenCodeSettings(composite: unknown): OpenCodeSettings {
|
||||
const c = (composite ?? {}) as Partial<OpenCodeSettings>;
|
||||
return {
|
||||
opencodeServerUrl: c.opencodeServerUrl || DEFAULT_OPENCODE_SETTINGS.opencodeServerUrl,
|
||||
opencodeServerUser: c.opencodeServerUser || DEFAULT_OPENCODE_SETTINGS.opencodeServerUser,
|
||||
opencodeServerPassword: c.opencodeServerPassword || '',
|
||||
requestTimeoutSeconds:
|
||||
typeof c.requestTimeoutSeconds === 'number'
|
||||
? c.requestTimeoutSeconds
|
||||
: DEFAULT_OPENCODE_SETTINGS.requestTimeoutSeconds,
|
||||
opencodeProvider: c.opencodeProvider || '',
|
||||
opencodeModel: c.opencodeModel || '',
|
||||
};
|
||||
}
|
||||
|
||||
/** Response from GET /opencode-bridge/providers. */
|
||||
export interface OpenCodeProvider {
|
||||
id: string;
|
||||
models: { id: string; [k: string]: unknown }[];
|
||||
}
|
||||
|
||||
export interface OpenCodeProvidersResponse {
|
||||
providers: OpenCodeProvider[];
|
||||
}
|
||||
Reference in New Issue
Block a user