@deepseek-ai/dsh-lsp-stdio 0.0.1-rc.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +28 -0
- package/README.i18n.yaml +6 -0
- package/README.md +60 -0
- package/README.zh.md +60 -0
- package/lib/index.js +1188 -0
- package/lib/invariant.js +23 -0
- package/lib/types/abort.d.ts +24 -0
- package/lib/types/connection.d.ts +119 -0
- package/lib/types/framing.d.ts +35 -0
- package/lib/types/host.d.ts +39 -0
- package/lib/types/index.d.ts +63 -0
- package/lib/types/instance.d.ts +89 -0
- package/lib/types/invariant.d.ts +16 -0
- package/lib/types/protocol.d.ts +68 -0
- package/lib/types/translate.d.ts +55 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, DeepSeek
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.i18n.yaml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
|
2
|
+
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
|
+
# after editing either side, bring the other along and re-record with:
|
|
4
|
+
# pnpm run verify-translation-pairing --write packages/lsp/lsp-stdio/README.md
|
|
5
|
+
README.md: beadc34ec738ae3f6511cae764240c0e9d8c61d7
|
|
6
|
+
README.zh.md: 2d6516a25147fb7593e8d45d843315098cdff7d3
|
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# @deepseek-ai/dsh-lsp-stdio
|
|
2
|
+
|
|
3
|
+
English | [中文](README.zh.md)
|
|
4
|
+
|
|
5
|
+
A **generic stdio language-server backend** for `ctx.lsp`. One plugin instance accepts a named server table and registers one isolated provider per entry. It reads through `ctx.fs` and launches through `ctx.subprocess`, so the server and source always inhabit the mounted execution world. This is a generic host, not a language-server catalog or installer — deployments configure commands and mappings explicitly; presets belong in `cordis.yml` overlays.
|
|
6
|
+
|
|
7
|
+
Namespace plugin (`name` / `inject` / `Config` / `apply`, no default export).
|
|
8
|
+
|
|
9
|
+
## What it does
|
|
10
|
+
|
|
11
|
+
- 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.
|
|
12
|
+
- Lazily single-flights one server process per `(server id, canonical workspace target)`. A live server error is not replayed; if the selected pooled transport fails before or during a read-only query, the provider awaits its disposal and retries that query once on a fresh process.
|
|
13
|
+
- Uses a compatibility-first **transient-open** sequence per query: resolve and byte-bound the source while streaming it through `ctx.fs`, `textDocument/didOpen` (version 1, full text), the requested request, then `textDocument/didClose` in `finally`. A failed or canceled `didOpen` write terminates the instance before the pool can reuse it. Documents close after each call, so the first version needs no `didChange`, content cache, or document LRU.
|
|
14
|
+
- Serializes each source-read/open/query/close lifecycle through one abortable per-workspace queue so queued calls read current source only when their turn starts; distinct workspaces run in parallel. Provider disposal aborts filesystem and protocol work, awaits workspace lookups that have not entered a queue, then drains every queue and server.
|
|
15
|
+
- After protocol shutdown fails, terminates the server's descendant tree through the subprocess seam (POSIX process-group signaling; Windows `taskkill /T /F`). Tree-kill delivery is contained like every group signal — it races server exit — and quiescence is confirmed by the handle's tree-liveness wait rather than by the kill's own outcome.
|
|
16
|
+
- Resolves the server executable, cwd, process, and protocol streams through `ctx.subprocess`; `initialize.processId` is `null` because another machine or PID namespace must not monitor the harness process.
|
|
17
|
+
- Uses `ctx.fs` canonical containment, file URIs, and streamed text validation, but emits no `fs/observed`: only the LSP result is model-visible, so a query does not satisfy read-before-write policy.
|
|
18
|
+
|
|
19
|
+
## Configuration
|
|
20
|
+
|
|
21
|
+
The `servers` record key is the stable provider id reserved on `ctx.lsp`; each value has this shape:
|
|
22
|
+
|
|
23
|
+
| Server key | Default | Meaning |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| `command` | (required) | Executable to spawn — absolute, or resolved on the child PATH at load. Launch uses no shell. |
|
|
26
|
+
| `args` | `[]` | Arguments passed to the executable. |
|
|
27
|
+
| `env` | `{}` | Extra env merged on top of the credential-scrubbed ambient env (vars matching `KEY`/`PASSWORD`/`SECRET`/`TOKEN` are not forwarded); an explicit `DSH_*` entry merges after the seam's scrub of ambient ones. |
|
|
28
|
+
| `extensionToLanguage` | (required) | Lowercase leading-dot extension → LSP language id (e.g. `{ '.ts': 'typescript' }`). |
|
|
29
|
+
| `initializationOptions` | `null` | Static `initialize` options forwarded to the server. |
|
|
30
|
+
| `configuration` | `null` | Static answer to every `workspace/configuration` item. |
|
|
31
|
+
| `maxMessageBytes` | `16000000` | Largest single framed message accepted from the server. |
|
|
32
|
+
| `maxStderrBytes` | `1000000` | Largest stderr tail retained for diagnostics. |
|
|
33
|
+
| `maxDocumentBytes` | `4000000` | Largest source file this host will open. |
|
|
34
|
+
| `shutdownTimeoutMs` | `5000` | Graceful `shutdown`/`exit` budget before escalation. |
|
|
35
|
+
| `killGraceMs` | `2000` | Grace for request cancellation and for SIGTERM→SIGKILL escalation. |
|
|
36
|
+
|
|
37
|
+
`servers` must contain at least one entry, and every id must be non-empty. Timer budgets must be positive integers no greater than Node's `2_147_483_647` ms timer limit. 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.
|
|
38
|
+
|
|
39
|
+
## Protocol behavior
|
|
40
|
+
|
|
41
|
+
Initialization advertises `general.positionEncodings: ['utf-16']`, `workspace: { workspaceFolders: true, configuration: true }`, `textDocument.hover.contentFormat: ['markdown', 'plaintext']`, and `linkSupport: true` for definition and implementation, with no dynamic registration. The server's returned capabilities are authoritative: an unsupported operation, or synchronization without transient open/close, fails the query. An omitted server `positionEncoding` defaults to `utf-16`; any other value is a protocol error. The client answers `workspace/configuration` from static config, accepts lifecycle bookkeeping requests, and rejects `workspace/applyEdit` — it never applies edits or runs commands. Navigation maps `Location` directly and `LocationLink` from `targetUri` + `targetSelectionRange`; hover normalization takes valid `MarkupContent.value`, preserves string `MarkedString`s, renders language-tagged values as fenced code, and joins arrays with one blank line. Missing results, malformed ranges or positions, and malformed hover encodings fail as structured `LSP_MALFORMED_RESPONSE` errors.
|
|
42
|
+
|
|
43
|
+
## Security boundary
|
|
44
|
+
|
|
45
|
+
The provider trusts its configured server and claims no sandbox confinement. It delegates canonical identity, containment, regular-file streaming, UTF-8 validation, and file-URI encoding to `ctx.fs`; it rejects missing, non-regular, non-UTF-8, oversized, or canonically out-of-workspace query sources before server startup. Containment is evaluated before the stream opens and does not promise stable-handle identity across concurrent path replacement. Result locations may be external, but an external path cannot become a query source. A deployment must mount filesystem and subprocess providers for the same execution world; split-world composition is invalid.
|
|
46
|
+
|
|
47
|
+
## Model Experience
|
|
48
|
+
|
|
49
|
+
Indirectly, through `dsh-tool-lsp`, which surfaces this provider's normalized results; this host contributes no prompt or schema itself.
|
|
50
|
+
|
|
51
|
+
#### KV Cache effect
|
|
52
|
+
|
|
53
|
+
No direct invalidation; `dsh-tool-lsp` owns request-prefix changes.
|
|
54
|
+
|
|
55
|
+
## Known Limitations and Deferred Work
|
|
56
|
+
|
|
57
|
+
- **No confinement policy** — this package trusts the configured server and does not sandbox its process; a restricted deployment must supply appropriate process/filesystem providers or a same-world sandbox wrapper.
|
|
58
|
+
- **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.
|
|
59
|
+
- **Per-server/workspace serialization latency** — parallel agents sharing one server and workspace queue behind one process; long-lived workspace processes consume memory until disposal.
|
|
60
|
+
- **A hard-killed harness orphans language servers** — `initialize.processId: null` removes server-side client-PID monitoring, so servers are cleaned only by graceful service disposal; a SIGKILL'd harness leaves them running until they exit on their own.
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# @deepseek-ai/dsh-lsp-stdio
|
|
2
|
+
|
|
3
|
+
[English](README.md) | 中文
|
|
4
|
+
|
|
5
|
+
`ctx.lsp` 的**通用 stdio 语言服务器后端**。一个插件实例接受一张命名服务器表,并逐配置项注册一个隔离的提供方。它通过 `ctx.fs` 读取,并通过 `ctx.subprocess` 启动,因此服务器与源文件始终位于已挂载的执行世界中。这是通用主机,而不是语言服务器目录或安装器:部署需要显式配置命令与映射,预设应放在 `cordis.yml` overlay 中。
|
|
6
|
+
|
|
7
|
+
Namespace 插件(`name`/`inject`/`Config`/`apply`,无默认导出)。
|
|
8
|
+
|
|
9
|
+
## 功能
|
|
10
|
+
|
|
11
|
+
- 在注册前解析每项服务器局部设置;无效映射或注册冲突会回滚较早配置项,因此加载失败不会留下提供方路由。
|
|
12
|
+
- 每个 `(server id, canonical workspace target)` 惰性 single-flight 一个服务器进程。服务器仍存活时返回的错误不会触发重试;如果选中的池化传输在只读查询之前或期间发生故障,提供方会等待其 dispose(资源释放)完成,并在新进程上重试该查询一次。
|
|
13
|
+
- 每次查询都使用兼容性优先的**临时打开**序列:通过 `ctx.fs` 流式读取源文件,同时解析并限制其字节数;随后执行 `textDocument/didOpen`(版本 1、完整文本)、所请求操作,再执行位于 `finally` 中的 `textDocument/didClose`。写入 `didOpen` 失败或取消时,会在池复用该实例前将其终止。文档在每次调用后关闭,因此第一版不需要 `didChange`、内容 cache 或文档 LRU。
|
|
14
|
+
- 通过一条逐 Workspace、可中止的队列,串行执行每个源读取/打开/查询/关闭生命周期,因此排队调用只会在轮到自身时读取当前源;不同 Workspace 并行运行。提供方 dispose 会中止文件系统与协议工作,等待尚未进入队列的 Workspace 查找完成,随后排空每条队列与每个服务器。
|
|
15
|
+
- 协议 shutdown 失败后,经由子进程 seam 终止服务器后代树(POSIX 进程组信号;Windows `taskkill /T /F`)。树终止的投递结果与所有进程组信号一样被就地吸收,不向外抛出(投递与服务器退出存在竞态);服务器是否完全停稳,由句柄的进程树存活等待确认,而非由这次终止自身的结果确认。
|
|
16
|
+
- 通过 `ctx.subprocess` 解析服务器可执行文件、cwd、进程和协议流;`initialize.processId` 为 `null`,因为另一台机器或 PID namespace 不得监视 harness 进程。
|
|
17
|
+
- 使用 `ctx.fs` 提供的规范化包含关系、文件 URI 与流式文本验证,但不发出 `fs/observed`:只有 LSP 结果对模型可见,因此查询不满足先读后写策略。
|
|
18
|
+
|
|
19
|
+
## 配置
|
|
20
|
+
|
|
21
|
+
`servers` 记录的 key 是在 `ctx.lsp` 上保留的稳定提供方 id;每个值具有以下形状:
|
|
22
|
+
|
|
23
|
+
| 服务器 key | 默认值 | 含义 |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| `command` | (必填) | 要 spawn 的可执行文件:绝对路径,或在加载时从子进程 PATH 解析。不使用 shell 启动。 |
|
|
26
|
+
| `args` | `[]` | 传给可执行文件的参数。 |
|
|
27
|
+
| `env` | `{}` | 合并到已清理 credential 的环境之上的额外 env(匹配 `KEY`/`PASSWORD`/`SECRET`/`TOKEN` 的变量不会转发);显式 `DSH_*` 条目在 seam 清除环境中同名值之后合并。 |
|
|
28
|
+
| `extensionToLanguage` | (必填) | 小写、以点开头的扩展名 → LSP language id(例如 `{ '.ts': 'typescript' }`)。 |
|
|
29
|
+
| `initializationOptions` | `null` | 转发给服务器的静态 `initialize` 选项。 |
|
|
30
|
+
| `configuration` | `null` | 每个 `workspace/configuration` 配置项的静态答案。 |
|
|
31
|
+
| `maxMessageBytes` | `16000000` | 从服务器接受的单条 framed 消息最大大小。 |
|
|
32
|
+
| `maxStderrBytes` | `1000000` | 为诊断保留的 stderr 尾部最大大小。 |
|
|
33
|
+
| `maxDocumentBytes` | `4000000` | 该主机可打开的源文件大小上限。 |
|
|
34
|
+
| `shutdownTimeoutMs` | `5000` | 升级前用于优雅 `shutdown`/`exit` 的预算。 |
|
|
35
|
+
| `killGraceMs` | `2000` | 请求取消及 SIGTERM→SIGKILL 升级的宽限期。 |
|
|
36
|
+
|
|
37
|
+
`servers` 必须至少包含一个配置项,每个 id 都必须非空。定时器预算必须是正整数,且不超过 Node 的 `2_147_483_647` ms 定时器上限。所有可执行文件都会在清理 credential 后于加载时解析;后面的坏配置项会阻止所有提供方注册。进程在第一次匹配查询时惰性启动。
|
|
38
|
+
|
|
39
|
+
## 协议行为
|
|
40
|
+
|
|
41
|
+
初始化会声明 `general.positionEncodings: ['utf-16']`、`workspace: { workspaceFolders: true, configuration: true }`、`textDocument.hover.contentFormat: ['markdown', 'plaintext']`,以及定义与实现使用的 `linkSupport: true`,且不进行动态注册。服务器返回的能力具有最终决定权:不受支持的操作,或缺少临时打开/关闭的同步方式,会使查询失败。服务器省略 `positionEncoding` 时默认为 `utf-16`;其他值都属于协议错误。客户端通过静态配置回答 `workspace/configuration`,接受生命周期记账请求,并拒绝 `workspace/applyEdit`:它绝不应用编辑或运行命令。导航直接映射 `Location`,并从 `LocationLink` 的 `targetUri` + `targetSelectionRange` 映射;hover 规范化会取得有效的 `MarkupContent.value`,保留 string `MarkedString`,把带 language tag 的值渲染为围栏代码,并用一个空行连接数组。缺失结果、格式错误的范围或位置,以及格式错误的 hover 编码,都会以结构化 `LSP_MALFORMED_RESPONSE` 错误的形式失败。
|
|
42
|
+
|
|
43
|
+
## 安全边界
|
|
44
|
+
|
|
45
|
+
提供方信任其配置的服务器,不提供任何沙箱隔离。它把规范化身份、包含关系、普通文件流式读取、UTF-8 验证和文件 URI 编码委托给 `ctx.fs`;并在服务器启动前拒绝缺失、非普通文件、非 UTF-8、过大,或规范化后位于 Workspace 外部的查询源。包含关系在打开流之前评估,不承诺在并发路径替换期间保持稳定句柄身份。结果位置可以在外部,但外部路径不能成为查询源。部署必须挂载描述同一执行世界的文件系统与进程管理提供方;分裂世界组合无效。
|
|
46
|
+
|
|
47
|
+
## 模型体验
|
|
48
|
+
|
|
49
|
+
通过 `dsh-tool-lsp` 间接影响;该工具呈现此提供方的规范化结果,该主机自身不贡献提示词或 schema。
|
|
50
|
+
|
|
51
|
+
#### KV Cache 影响
|
|
52
|
+
|
|
53
|
+
不会直接失效;请求前缀变更由 `dsh-tool-lsp` 负责。
|
|
54
|
+
|
|
55
|
+
## 已知限制与暂缓事项
|
|
56
|
+
|
|
57
|
+
- **不提供隔离策略**:本包(package)信任所配置的服务器,不对其进程实施沙箱;受限部署必须提供适当的进程/文件系统提供方,或使用同一执行世界的沙箱包装层。
|
|
58
|
+
- **临时打开兼容性下限**:同步能力省略打开/关闭(或声明 `None`)的服务器不受支持,即使关闭文档查询能够工作;固定的 TypeScript e2e 只建立一项兼容性下限,不代表跨语言承诺。
|
|
59
|
+
- **逐服务器/Workspace 串行化延迟**:共享同一个服务器与 Workspace 的并行 agent(智能体)会在一个进程后排队;长生命周期 Workspace 进程会占用内存直到 dispose。
|
|
60
|
+
- **被强制杀死的 harness 会遗留语言服务器**:`initialize.processId: null` 取消了服务器侧的客户端 PID 监视,因此服务器只能由服务的优雅 dispose 清理;被 SIGKILL 的 harness 会让它们继续运行,直到自行退出。
|