refactor: move all server config to startup env vars; drop plugin settings
This commit is contained in:
@@ -1,33 +0,0 @@
|
||||
import { DEFAULT_OPENCODE_SETTINGS, readOpenCodeSettings } from '../types';
|
||||
|
||||
describe('opencode_bridge types', () => {
|
||||
it('returns defaults when given empty composite', () => {
|
||||
const s = readOpenCodeSettings({});
|
||||
expect(s).toEqual(DEFAULT_OPENCODE_SETTINGS);
|
||||
expect(s.opencodeProvider).toBe('');
|
||||
expect(s.opencodeModel).toBe('');
|
||||
});
|
||||
|
||||
it('overrides defaults with user values', () => {
|
||||
const s = readOpenCodeSettings({
|
||||
opencodeServerUrl: 'http://x:1',
|
||||
opencodeProvider: 'anthropic',
|
||||
opencodeModel: 'claude-sonnet-4-20250514'
|
||||
});
|
||||
expect(s.opencodeServerUrl).toBe('http://x:1');
|
||||
expect(s.opencodeProvider).toBe('anthropic');
|
||||
expect(s.opencodeModel).toBe('claude-sonnet-4-20250514');
|
||||
expect(s.opencodeServerUser).toBe(
|
||||
DEFAULT_OPENCODE_SETTINGS.opencodeServerUser
|
||||
);
|
||||
});
|
||||
|
||||
it('treats empty string provider/model as "use default"', () => {
|
||||
const s = readOpenCodeSettings({
|
||||
opencodeProvider: '',
|
||||
opencodeModel: ''
|
||||
});
|
||||
expect(s.opencodeProvider).toBe('');
|
||||
expect(s.opencodeModel).toBe('');
|
||||
});
|
||||
});
|
||||
+33
-43
@@ -5,32 +5,38 @@ import {
|
||||
|
||||
import { IToolbarWidgetRegistry } from '@jupyterlab/apputils';
|
||||
import { Cell, CodeCell } from '@jupyterlab/cells';
|
||||
import { ISettingRegistry } from '@jupyterlab/settingregistry';
|
||||
import { Widget } from '@lumino/widgets';
|
||||
|
||||
import {
|
||||
OpenCodeCellActions,
|
||||
setOpenCodeRuntime
|
||||
setOpenCodeProviders,
|
||||
setOpenCodeServerSettings
|
||||
} from './components/opencode_cell_actions';
|
||||
import { requestAPI } from './request';
|
||||
import { readOpenCodeSettings } from './types';
|
||||
import { callOpenCodeProviders } from './api/opencode_client';
|
||||
|
||||
/**
|
||||
* Initialization data for the opencode_bridge extension.
|
||||
*
|
||||
* v3-final: no JupyterLab plugin settings. Connection config is read from
|
||||
* environment variables by the server extension; the model is picked
|
||||
* dynamically in the inline prompt.
|
||||
*/
|
||||
const plugin: JupyterFrontEndPlugin<void> = {
|
||||
id: 'opencode_bridge:plugin',
|
||||
description: 'A JupyterLab extension bridging the UI to OpenCode Serve.',
|
||||
autoStart: true,
|
||||
optional: [ISettingRegistry, IToolbarWidgetRegistry],
|
||||
optional: [IToolbarWidgetRegistry],
|
||||
activate: (
|
||||
app: JupyterFrontEnd,
|
||||
settingRegistry: ISettingRegistry | null,
|
||||
toolbarRegistry: IToolbarWidgetRegistry | null
|
||||
) => {
|
||||
console.log('JupyterLab extension opencode_bridge is activated!');
|
||||
|
||||
// Push the Jupyter server settings into the actions module so that
|
||||
// callOpenCodeEdit can build the right base URL.
|
||||
setOpenCodeServerSettings(app.serviceManager.serverSettings);
|
||||
|
||||
// Register the per-cell AI actions into the native Cell toolbar
|
||||
// (top-right of the active cell, next to move up/down). Non-code
|
||||
// cells get an empty widget so they show no AI buttons.
|
||||
@@ -47,44 +53,28 @@ const plugin: JupyterFrontEndPlugin<void> = {
|
||||
);
|
||||
}
|
||||
|
||||
// Load settings + push into the actions module.
|
||||
if (settingRegistry) {
|
||||
void settingRegistry
|
||||
.load(plugin.id)
|
||||
.then(settings => {
|
||||
const apply = () => {
|
||||
const bridge = readOpenCodeSettings(settings.composite);
|
||||
setOpenCodeRuntime({
|
||||
settings: bridge,
|
||||
serverSettings: app.serviceManager.serverSettings
|
||||
});
|
||||
console.log('opencode_bridge settings applied:', bridge);
|
||||
};
|
||||
apply();
|
||||
settings.changed.connect(apply);
|
||||
|
||||
void callOpenCodeProviders(app.serviceManager.serverSettings)
|
||||
.then(data => {
|
||||
const lines: string[] = [
|
||||
'[opencode_bridge] Available OpenCode providers:'
|
||||
];
|
||||
for (const p of data.providers) {
|
||||
const models = p.models.map(m => m.id).join(', ');
|
||||
lines.push(` - ${p.id}: ${models || '(no models)'}`);
|
||||
}
|
||||
console.log(lines.join('\n'));
|
||||
})
|
||||
.catch(reason => {
|
||||
console.warn(
|
||||
'[opencode_bridge] Could not fetch providers (is the opencode-bridge server extension enabled and opencode serve running?):',
|
||||
reason
|
||||
);
|
||||
});
|
||||
})
|
||||
.catch(reason => {
|
||||
console.error('Failed to load settings for opencode_bridge.', reason);
|
||||
});
|
||||
}
|
||||
// Fetch available providers once at activation and cache them for the
|
||||
// inline prompt's model picker. Failure is non-fatal (the picker hides).
|
||||
void callOpenCodeProviders(app.serviceManager.serverSettings)
|
||||
.then(data => {
|
||||
setOpenCodeProviders(data);
|
||||
const lines: string[] = [
|
||||
'[opencode_bridge] Available OpenCode providers:'
|
||||
];
|
||||
for (const p of data.providers) {
|
||||
const models = p.models.map(m => m.id).join(', ');
|
||||
lines.push(` - ${p.id}: ${models || '(no models)'}`);
|
||||
}
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(lines.join('\n'));
|
||||
})
|
||||
.catch(reason => {
|
||||
// eslint-disable-next-line no-console
|
||||
+ console.warn(
|
||||
'[opencode_bridge] Could not fetch providers (is the opencode-bridge server extension enabled and opencode serve running?):',
|
||||
reason
|
||||
);
|
||||
});
|
||||
|
||||
requestAPI<unknown>('hello', app.serviceManager.serverSettings)
|
||||
.then(data => {
|
||||
|
||||
+4
-34
@@ -3,6 +3,10 @@
|
||||
*
|
||||
* These mirror the Python server's `CellContext` / `OpenCodeRequest` / `OpenCodeResponse`
|
||||
* shapes in `opencode_bridge/routes.py`. Keep them in sync.
|
||||
*
|
||||
* v3-final: the JupyterLab plugin settings have been removed entirely.
|
||||
* All connection config lives in startup environment variables (see
|
||||
* `opencode_bridge/config.py`); the inline model picker is fully dynamic.
|
||||
*/
|
||||
|
||||
export interface ErrorOutput {
|
||||
@@ -43,40 +47,6 @@ export interface OpenCodeFailure {
|
||||
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user