refactor(lsp): configure local servers together
This commit is contained in:
@@ -1,21 +1,23 @@
|
||||
# @deepseek-ai/dsh-lsp-local
|
||||
|
||||
A **generic stdio language-server provider** for `ctx.lsp`. One plugin instance configures one server command and its extension-to-language-id map; load multiple instances for multiple servers. This is a generic host, not a language-server catalog or installer — deployments configure commands and mappings explicitly; presets belong in composition plugins or `cordis.yml` overlays.
|
||||
A **generic local stdio language-server backend** for `ctx.lsp`. One plugin instance accepts a named server table and registers one isolated provider per entry. This is a generic host, not a language-server catalog or installer — deployments configure commands and mappings explicitly; presets belong in `cordis.yml` overlays.
|
||||
|
||||
Namespace plugin (`name` / `inject` / `Config` / `apply`, no default export).
|
||||
|
||||
## What it does
|
||||
|
||||
- Lazily single-flights one server process per `(provider id, canonical workspace realpath)`. A crash fails the active query without replay; a later query may replace the process.
|
||||
- Resolves every server-local setting before registration; an invalid mapping or registration conflict rolls back earlier entries, so a failed load leaves no provider routes.
|
||||
- Lazily single-flights one server process per `(server id, canonical workspace realpath)`. A crash fails the active query without replay; a later query may replace the process.
|
||||
- Uses a compatibility-first **transient-open** sequence per query: canonicalize and read the source with Node APIs, `textDocument/didOpen` (version 1, full text), the requested request, then `textDocument/didClose` in `finally`. Documents close after each call, so the first version needs no `didChange`, content cache, or document LRU.
|
||||
- Serializes queries through one abortable per-instance queue so a cancellation that fails to stop the server can terminate it without killing unrelated work; distinct instances run in parallel.
|
||||
- Reads sources through Node filesystem APIs in the subprocess's host namespace — NOT `ctx.fs`, and emits no `fs/observed`: only the LSP result is model-visible, so a query does not satisfy read-before-write policy.
|
||||
|
||||
## Configuration
|
||||
|
||||
| Key | Default | Meaning |
|
||||
The `servers` record key is the stable provider id reserved on `ctx.lsp`; each value has this shape:
|
||||
|
||||
| Server key | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `providerId` | (required) | Stable provider id reserved on `ctx.lsp` with the extensions. |
|
||||
| `command` | (required) | Executable to spawn — absolute, or resolved on the child PATH at load. Launch uses no shell. |
|
||||
| `args` | `[]` | Arguments passed to the executable. |
|
||||
| `env` | `{}` | Extra env merged on top of the credential-scrubbed ambient env (vars matching `KEY`/`SECRET`/`TOKEN` are not forwarded). |
|
||||
@@ -28,7 +30,7 @@ Namespace plugin (`name` / `inject` / `Config` / `apply`, no default export).
|
||||
| `shutdownTimeoutMs` | `5000` | Graceful `shutdown`/`exit` budget before escalation. |
|
||||
| `killGraceMs` | `2000` | SIGTERM→SIGKILL grace after graceful shutdown fails. |
|
||||
|
||||
The executable is resolved at load (after credential scrubbing); a missing command fails before registration. The process itself launches lazily on the first matching query.
|
||||
`servers` must contain at least one entry, and every id must be non-empty. All executables resolve at load after credential scrubbing; a bad later entry prevents every provider from registering. Processes launch lazily on the first matching query.
|
||||
|
||||
## Protocol behavior
|
||||
|
||||
@@ -46,4 +48,4 @@ Indirectly, through `dsh-tool-lsp`, which surfaces this provider's normalized re
|
||||
|
||||
- **Trusted host-local only** — no sandbox confinement, no private cache/temp write contract; supporting untrusted binaries or restricted/remote/virtual workspaces requires a later process/filesystem contract and a different provider ([seam RFC](../../../docs/rfc/implemented/architecture/2026-07-15-lsp-capability-seam.md)). Containment resolves `realpath`, then opens the source through one handle with `O_NOFOLLOW` (final-component symlink guard) and a bounded read; a concurrent mutator that swaps an *ancestor* directory for a symlink between the resolve and the open is an accepted residual TOCTOU under this trusted-deployment model, not closed with non-portable `openat` segment walks.
|
||||
- **Transient-open compatibility floor** — servers whose synchronization omits open/close (or advertise `None`) are unsupported even if closed-document queries would work; the pinned TypeScript e2e establishes one compatibility floor, not a cross-language claim.
|
||||
- **Per-instance serialization latency** — parallel agents sharing a workspace queue behind one process; long-lived workspace processes consume memory until disposal.
|
||||
- **Per-server/workspace serialization latency** — parallel agents sharing one server and workspace queue behind one process; long-lived workspace processes consume memory until disposal.
|
||||
|
||||
Reference in New Issue
Block a user