161 lines
5.4 KiB
Python
161 lines
5.4 KiB
Python
"""Configuration resolution for the opencode-bridge extension.
|
|
|
|
Connection fields (URL / user / password / request timeout) are resolved
|
|
in the following priority order, highest first:
|
|
|
|
1. ``--OpencodeConfig.<trait>`` CLI flag (e.g. ``--OpencodeConfig.url=...``)
|
|
2. ``c.OpencodeConfig.<trait>`` line in a Jupyter config file
|
|
3. ``OPENCODE_BRIDGE_*`` environment variable
|
|
4. Built-in default
|
|
|
|
The ``OpencodeConfig`` class is a ``traitlets.config.Configurable``; the
|
|
Jupyter server extension instantiates it with ``parent=server_app`` and
|
|
stashes it on ``web_app.settings["opencode_bridge"]`` so handlers can pick
|
|
it up via ``resolve_config``.
|
|
|
|
Env vars (kept for backward compatibility — also the default-value source
|
|
for the traitlets so a fresh ``OpencodeConfig()`` with no overrides still
|
|
honours them):
|
|
|
|
OPENCODE_BRIDGE_URL — OpenCode Serve base URL (default http://127.0.0.1:4096)
|
|
OPENCODE_BRIDGE_USER — HTTP Basic Auth username (default 'opencode')
|
|
OPENCODE_BRIDGE_PASSWORD — HTTP Basic Auth password (default '' = no auth)
|
|
OPENCODE_BRIDGE_TIMEOUT — request timeout in seconds (default 120)
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from typing import NamedTuple, Optional, Tuple
|
|
|
|
from traitlets import Int, Unicode, default
|
|
from traitlets.config import Configurable
|
|
|
|
|
|
ENV_URL = "OPENCODE_BRIDGE_URL"
|
|
ENV_USER = "OPENCODE_BRIDGE_USER"
|
|
ENV_PASSWORD = "OPENCODE_BRIDGE_PASSWORD"
|
|
ENV_TIMEOUT = "OPENCODE_BRIDGE_TIMEOUT"
|
|
|
|
DEFAULT_URL = "http://127.0.0.1:4096"
|
|
DEFAULT_USER = "opencode"
|
|
DEFAULT_REQUEST_TIMEOUT = 120
|
|
|
|
|
|
class OpenCodeConfig(NamedTuple):
|
|
url: str
|
|
user: str
|
|
password: str
|
|
request_timeout_seconds: int
|
|
|
|
@property
|
|
def auth(self) -> Optional[Tuple[str, str]]:
|
|
"""Return (user, password) for HTTP Basic Auth, or None if no password set."""
|
|
if not self.password:
|
|
return None
|
|
return (self.user, self.password)
|
|
|
|
|
|
class OpencodeConfig(Configurable):
|
|
"""Traitlets-configurable connection settings for the opencode-bridge server extension.
|
|
|
|
Exposed to ``jupyter lab`` / ``jupyter server`` as ``--OpencodeConfig.<trait>``
|
|
and to ``jupyter_server_config.py`` as ``c.OpencodeConfig.<trait>``. Defaults
|
|
fall back to the ``OPENCODE_BRIDGE_*`` env vars and then to built-in defaults,
|
|
so the legacy env-var-only flow keeps working unchanged.
|
|
"""
|
|
|
|
url = Unicode(
|
|
DEFAULT_URL,
|
|
config=True,
|
|
help=(
|
|
"Base URL of the locally-running OpenCode Serve process. "
|
|
"Overrides the OPENCODE_BRIDGE_URL env var."
|
|
),
|
|
)
|
|
|
|
user = Unicode(
|
|
DEFAULT_USER,
|
|
config=True,
|
|
help=(
|
|
"HTTP Basic Auth username for OpenCode Serve. "
|
|
"Overrides the OPENCODE_BRIDGE_USER env var."
|
|
),
|
|
)
|
|
|
|
password = Unicode(
|
|
"",
|
|
config=True,
|
|
help=(
|
|
"HTTP Basic Auth password for OpenCode Serve (empty = no auth). "
|
|
"Overrides the OPENCODE_BRIDGE_PASSWORD env var."
|
|
),
|
|
)
|
|
|
|
request_timeout_seconds = Int(
|
|
DEFAULT_REQUEST_TIMEOUT,
|
|
config=True,
|
|
help=(
|
|
"Per-request timeout in seconds when calling OpenCode Serve. "
|
|
"Overrides the OPENCODE_BRIDGE_TIMEOUT env var."
|
|
),
|
|
)
|
|
|
|
@default("url")
|
|
def _default_url(self) -> str:
|
|
return os.environ.get(ENV_URL) or DEFAULT_URL
|
|
|
|
@default("user")
|
|
def _default_user(self) -> str:
|
|
return os.environ.get(ENV_USER) or DEFAULT_USER
|
|
|
|
@default("password")
|
|
def _default_password(self) -> str:
|
|
return os.environ.get(ENV_PASSWORD) or ""
|
|
|
|
@default("request_timeout_seconds")
|
|
def _default_request_timeout_seconds(self) -> int:
|
|
return int(os.environ.get(ENV_TIMEOUT) or DEFAULT_REQUEST_TIMEOUT)
|
|
|
|
|
|
def _to_opencode_config(bridge) -> OpenCodeConfig:
|
|
"""Project an :class:`OpencodeConfig` (or duck-typed equivalent) to the
|
|
internal ``OpenCodeConfig`` NamedTuple consumed by ``OpenCodeClient``.
|
|
"""
|
|
return OpenCodeConfig(
|
|
url=bridge.url,
|
|
user=bridge.user,
|
|
password=bridge.password,
|
|
request_timeout_seconds=bridge.request_timeout_seconds,
|
|
)
|
|
|
|
|
|
def resolve_config(jupyter_settings) -> OpenCodeConfig:
|
|
"""Resolve OpenCode connection config into an ``OpenCodeConfig`` NamedTuple.
|
|
|
|
Accepts either a settings dict (legacy) or an :class:`OpencodeConfig`
|
|
instance directly, because request handlers call
|
|
``resolve_config(handler.settings.get("opencode_bridge", {}))`` and pass
|
|
the inner value (which is now an ``OpencodeConfig`` instance after the
|
|
server extension's load hook populates it).
|
|
|
|
Source order (highest priority first):
|
|
|
|
1. The argument itself is an :class:`OpencodeConfig` instance.
|
|
2. ``jupyter_settings["opencode_bridge"]`` is an :class:`OpencodeConfig`
|
|
instance (legacy call style).
|
|
3. Otherwise fall back to env vars + built-in defaults.
|
|
"""
|
|
if isinstance(jupyter_settings, OpencodeConfig):
|
|
return _to_opencode_config(jupyter_settings)
|
|
bridge = (jupyter_settings or {}).get("opencode_bridge")
|
|
if isinstance(bridge, OpencodeConfig):
|
|
return _to_opencode_config(bridge)
|
|
return OpenCodeConfig(
|
|
url=os.environ.get(ENV_URL) or DEFAULT_URL,
|
|
user=os.environ.get(ENV_USER) or DEFAULT_USER,
|
|
password=os.environ.get(ENV_PASSWORD) or "",
|
|
request_timeout_seconds=int(
|
|
os.environ.get(ENV_TIMEOUT) or DEFAULT_REQUEST_TIMEOUT
|
|
),
|
|
)
|