dsh-over-ssh 0.0.0-stage → 0.1.0
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 +21 -0
- package/README.md +119 -2
- package/agents/agent-manifest.json +20 -0
- package/agents/linux-amd64/dsh-over-ssh-agent +0 -0
- package/agents/linux-arm64/dsh-over-ssh-agent +0 -0
- package/cordis.patch.yml +84 -0
- package/lib/client.js +6611 -0
- package/lib/file-references.js +366 -0
- package/lib/fs.js +1368 -0
- package/lib/index.js +19034 -0
- package/lib/local-bridge-refs.js +16 -0
- package/lib/local-bridge.js +16 -0
- package/lib/terminal-wrapper.js +294 -0
- package/lib/types/src/client/controls.d.ts +131 -0
- package/lib/types/src/client/core/card-focus-trap.d.ts +45 -0
- package/lib/types/src/client/core/controls.d.ts +164 -0
- package/lib/types/src/client/core/latest.d.ts +48 -0
- package/lib/types/src/client/core/locale.d.ts +38 -0
- package/lib/types/src/client/core/message.d.ts +37 -0
- package/lib/types/src/client/core/panel-model.d.ts +134 -0
- package/lib/types/src/client/core/portal.d.ts +266 -0
- package/lib/types/src/client/core/remote.d.ts +84 -0
- package/lib/types/src/client/core/styles.d.ts +458 -0
- package/lib/types/src/client/file-tree-io.d.ts +19 -0
- package/lib/types/src/client/file-tree.d.ts +271 -0
- package/lib/types/src/client/flows/directory-browser.d.ts +587 -0
- package/lib/types/src/client/flows/session-directory.d.ts +415 -0
- package/lib/types/src/client/flows/workspace-directory-flow.d.ts +480 -0
- package/lib/types/src/client/index.d.ts +49 -0
- package/lib/types/src/client/locale.d.ts +31 -0
- package/lib/types/src/client/panel-model.d.ts +37 -0
- package/lib/types/src/client/panel.d.ts +77 -0
- package/lib/types/src/client/roots/connection-test.d.ts +69 -0
- package/lib/types/src/client/roots/roots-panel-state.d.ts +307 -0
- package/lib/types/src/client/roots/roots-panel-view.d.ts +280 -0
- package/lib/types/src/client/roots/roots-panel.d.ts +364 -0
- package/lib/types/src/client/roots/transfer-panel-view.d.ts +35 -0
- package/lib/types/src/client/roots/transfer-panel.d.ts +241 -0
- package/lib/types/src/client/roots/transfer-progress.d.ts +67 -0
- package/lib/types/src/client/roots-panel-view.d.ts +236 -0
- package/lib/types/src/client/roots-panel.d.ts +347 -0
- package/lib/types/src/client/session/session-panel-view.d.ts +336 -0
- package/lib/types/src/client/session/session-panel.d.ts +322 -0
- package/lib/types/src/client/session/session-remote-mark.d.ts +110 -0
- package/lib/types/src/client/session/session-row-actions.d.ts +398 -0
- package/lib/types/src/client/session-panel.d.ts +152 -0
- package/lib/types/src/client/styles.d.ts +252 -0
- package/lib/types/src/client/workspace-directory-flow.d.ts +342 -0
- package/lib/types/src/config.d.ts +312 -0
- package/lib/types/src/file-references.d.ts +25 -0
- package/lib/types/src/fs.d.ts +46 -0
- package/lib/types/src/host/app/backend.d.ts +170 -0
- package/lib/types/src/host/app/recycle-bin/recycle-bin-contract.d.ts +247 -0
- package/lib/types/src/host/app/recycle-bin/recycle-bin-move.d.ts +18 -0
- package/lib/types/src/host/app/recycle-bin/recycle-bin-service.d.ts +45 -0
- package/lib/types/src/host/app/recycle-bin/recycle-bin-world.d.ts +38 -0
- package/lib/types/src/host/app/recycle-bin-contract.d.ts +215 -0
- package/lib/types/src/host/app/recycle-bin-move.d.ts +18 -0
- package/lib/types/src/host/app/recycle-bin-service.d.ts +45 -0
- package/lib/types/src/host/app/recycle-bin-world.d.ts +35 -0
- package/lib/types/src/host/app/roots/roots-api-connections.d.ts +49 -0
- package/lib/types/src/host/app/roots/roots-api-contract.d.ts +369 -0
- package/lib/types/src/host/app/roots/roots-api-directories.d.ts +13 -0
- package/lib/types/src/host/app/roots/roots-api-http.d.ts +77 -0
- package/lib/types/src/host/app/roots/roots-api-long-poll.d.ts +130 -0
- package/lib/types/src/host/app/roots/roots-api-projections.d.ts +102 -0
- package/lib/types/src/host/app/roots/roots-api-roots.d.ts +13 -0
- package/lib/types/src/host/app/roots/roots-api-state.d.ts +22 -0
- package/lib/types/src/host/app/roots/roots-api-transfers.d.ts +26 -0
- package/lib/types/src/host/app/roots/roots-api.d.ts +55 -0
- package/lib/types/src/host/app/roots/user-roots.d.ts +552 -0
- package/lib/types/src/host/app/roots-api-connections.d.ts +49 -0
- package/lib/types/src/host/app/roots-api-contract.d.ts +339 -0
- package/lib/types/src/host/app/roots-api-directories.d.ts +13 -0
- package/lib/types/src/host/app/roots-api-http.d.ts +77 -0
- package/lib/types/src/host/app/roots-api-projections.d.ts +102 -0
- package/lib/types/src/host/app/roots-api-roots.d.ts +13 -0
- package/lib/types/src/host/app/roots-api-state.d.ts +22 -0
- package/lib/types/src/host/app/roots-api-support.d.ts +182 -0
- package/lib/types/src/host/app/roots-api-transfers.d.ts +13 -0
- package/lib/types/src/host/app/roots-api.d.ts +55 -0
- package/lib/types/src/host/app/routed-fs.d.ts +239 -0
- package/lib/types/src/host/app/serialize.d.ts +37 -0
- package/lib/types/src/host/app/session-admin/session-admin-context.d.ts +36 -0
- package/lib/types/src/host/app/session-admin/session-admin-contract.d.ts +221 -0
- package/lib/types/src/host/app/session-admin/session-admin-delete.d.ts +34 -0
- package/lib/types/src/host/app/session-admin/session-admin-service.d.ts +40 -0
- package/lib/types/src/host/app/session-admin-context.d.ts +36 -0
- package/lib/types/src/host/app/session-admin-contract.d.ts +214 -0
- package/lib/types/src/host/app/session-admin-delete.d.ts +34 -0
- package/lib/types/src/host/app/session-admin-service.d.ts +38 -0
- package/lib/types/src/host/app/takeover/takeover-plan.d.ts +227 -0
- package/lib/types/src/host/app/takeover/takeover-rows.d.ts +55 -0
- package/lib/types/src/host/app/transport/transport-selector.d.ts +98 -0
- package/lib/types/src/host/app/transport/transport-service.d.ts +82 -0
- package/lib/types/src/host/app/transport-selector.d.ts +98 -0
- package/lib/types/src/host/app/transport-service.d.ts +75 -0
- package/lib/types/src/host/app/user-roots.d.ts +461 -0
- package/lib/types/src/host/domain/bin-location.d.ts +79 -0
- package/lib/types/src/host/domain/capability.d.ts +78 -0
- package/lib/types/src/host/domain/connection.d.ts +95 -0
- package/lib/types/src/host/domain/errors.d.ts +55 -0
- package/lib/types/src/host/domain/glob-pattern.d.ts +62 -0
- package/lib/types/src/host/domain/literal-text.d.ts +63 -0
- package/lib/types/src/host/domain/path-grammar.d.ts +84 -0
- package/lib/types/src/host/domain/recycle-bin.d.ts +337 -0
- package/lib/types/src/host/domain/root.d.ts +74 -0
- package/lib/types/src/host/domain/roots/bin-location.d.ts +91 -0
- package/lib/types/src/host/domain/roots/connection.d.ts +106 -0
- package/lib/types/src/host/domain/roots/root.d.ts +108 -0
- package/lib/types/src/host/domain/roots/route-table.d.ts +102 -0
- package/lib/types/src/host/domain/roots/session-roots.d.ts +151 -0
- package/lib/types/src/host/domain/roots/ssh-config.d.ts +73 -0
- package/lib/types/src/host/domain/route-table.d.ts +80 -0
- package/lib/types/src/host/domain/session/session-admin.d.ts +345 -0
- package/lib/types/src/host/domain/session/terminal-target.d.ts +136 -0
- package/lib/types/src/host/domain/session-admin.d.ts +330 -0
- package/lib/types/src/host/domain/shell.d.ts +47 -0
- package/lib/types/src/host/domain/ssh-config.d.ts +73 -0
- package/lib/types/src/host/domain/terminal-target.d.ts +132 -0
- package/lib/types/src/host/domain/tool-requests.d.ts +369 -0
- package/lib/types/src/host/domain/transfer/recycle-bin.d.ts +337 -0
- package/lib/types/src/host/domain/transfer/transfer.d.ts +260 -0
- package/lib/types/src/host/domain/transfer.d.ts +260 -0
- package/lib/types/src/host/infra/backend/local-backend.d.ts +84 -0
- package/lib/types/src/host/infra/backend/local-version.d.ts +43 -0
- package/lib/types/src/host/infra/backend/platform-local-backend.d.ts +89 -0
- package/lib/types/src/host/infra/backend/remote-backend.d.ts +239 -0
- package/lib/types/src/host/infra/cache/lru.d.ts +52 -0
- package/lib/types/src/host/infra/cache/versioned.d.ts +77 -0
- package/lib/types/src/host/infra/temp-name.d.ts +19 -0
- package/lib/types/src/host/infra/transport/agent/agent-installer.d.ts +121 -0
- package/lib/types/src/host/infra/transport/agent/agent-protocol-client.d.ts +132 -0
- package/lib/types/src/host/infra/transport/agent/agent-protocol-codec.d.ts +140 -0
- package/lib/types/src/host/infra/transport/agent/agent-protocol-errors.d.ts +41 -0
- package/lib/types/src/host/infra/transport/agent/agent-protocol-frames.d.ts +197 -0
- package/lib/types/src/host/infra/transport/agent/agent-protocol-types.d.ts +564 -0
- package/lib/types/src/host/infra/transport/agent/agent-transport.d.ts +418 -0
- package/lib/types/src/host/infra/transport/agent-installer.d.ts +121 -0
- package/lib/types/src/host/infra/transport/agent-protocol-client.d.ts +123 -0
- package/lib/types/src/host/infra/transport/agent-protocol-codec.d.ts +131 -0
- package/lib/types/src/host/infra/transport/agent-protocol-errors.d.ts +41 -0
- package/lib/types/src/host/infra/transport/agent-protocol-frames.d.ts +197 -0
- package/lib/types/src/host/infra/transport/agent-protocol-types.d.ts +509 -0
- package/lib/types/src/host/infra/transport/agent-transport.d.ts +353 -0
- package/lib/types/src/host/infra/transport/alias-transport.d.ts +206 -0
- package/lib/types/src/host/infra/transport/askpass-fs.d.ts +20 -0
- package/lib/types/src/host/infra/transport/askpass.d.ts +133 -0
- package/lib/types/src/host/infra/transport/command-pool.d.ts +61 -0
- package/lib/types/src/host/infra/transport/fs-commands.d.ts +140 -0
- package/lib/types/src/host/infra/transport/git-status-route.d.ts +63 -0
- package/lib/types/src/host/infra/transport/git-status.d.ts +101 -0
- package/lib/types/src/host/infra/transport/node-spawn.d.ts +26 -0
- package/lib/types/src/host/infra/transport/session-window.d.ts +51 -0
- package/lib/types/src/host/infra/transport/shell-quote.d.ts +59 -0
- package/lib/types/src/host/infra/transport/ssh/alias-transport.d.ts +206 -0
- package/lib/types/src/host/infra/transport/ssh/askpass-fs.d.ts +20 -0
- package/lib/types/src/host/infra/transport/ssh/askpass.d.ts +133 -0
- package/lib/types/src/host/infra/transport/ssh/shell-quote.d.ts +62 -0
- package/lib/types/src/host/infra/transport/ssh/ssh-argv.d.ts +43 -0
- package/lib/types/src/host/infra/transport/ssh/ssh2-transport.d.ts +328 -0
- package/lib/types/src/host/infra/transport/ssh-argv.d.ts +43 -0
- package/lib/types/src/host/infra/transport/ssh2-transport.d.ts +302 -0
- package/lib/types/src/host/infra/transport/transport-base.d.ts +34 -0
- package/lib/types/src/host/infra/transport/transport.d.ts +527 -0
- package/lib/types/src/host/integration/agent/agent-install-io.d.ts +33 -0
- package/lib/types/src/host/integration/agent/agent-preflight.d.ts +56 -0
- package/lib/types/src/host/integration/agent/askpass-registry.d.ts +66 -0
- package/lib/types/src/host/integration/agent-install-io.d.ts +33 -0
- package/lib/types/src/host/integration/agent-preflight.d.ts +56 -0
- package/lib/types/src/host/integration/anchor-directory.d.ts +56 -0
- package/lib/types/src/host/integration/askpass-registry.d.ts +59 -0
- package/lib/types/src/host/integration/bin-root-resolver.d.ts +81 -0
- package/lib/types/src/host/integration/connection-test.d.ts +73 -0
- package/lib/types/src/host/integration/file-reference-provider.d.ts +115 -0
- package/lib/types/src/host/integration/fs/file-reference-provider.d.ts +142 -0
- package/lib/types/src/host/integration/fs/fs-provider.d.ts +368 -0
- package/lib/types/src/host/integration/fs/git-status-wiring.d.ts +21 -0
- package/lib/types/src/host/integration/fs/platform-errors.d.ts +94 -0
- package/lib/types/src/host/integration/fs/remote-file-tools.d.ts +369 -0
- package/lib/types/src/host/integration/fs/remote-index.d.ts +177 -0
- package/lib/types/src/host/integration/fs-provider.d.ts +226 -0
- package/lib/types/src/host/integration/lsp/lsp-framing.d.ts +38 -0
- package/lib/types/src/host/integration/lsp/lsp-registry.d.ts +67 -0
- package/lib/types/src/host/integration/lsp/lsp-session.d.ts +130 -0
- package/lib/types/src/host/integration/management-tools.d.ts +64 -0
- package/lib/types/src/host/integration/recycle-bin-service.d.ts +247 -0
- package/lib/types/src/host/integration/remote/connection-test.d.ts +73 -0
- package/lib/types/src/host/integration/remote/remote-face.d.ts +127 -0
- package/lib/types/src/host/integration/remote/remote-tool-backend.d.ts +108 -0
- package/lib/types/src/host/integration/remote-face.d.ts +127 -0
- package/lib/types/src/host/integration/remote-file-tools.d.ts +241 -0
- package/lib/types/src/host/integration/remote-home.d.ts +69 -0
- package/lib/types/src/host/integration/remote-index.d.ts +165 -0
- package/lib/types/src/host/integration/remote-job-tools.d.ts +295 -0
- package/lib/types/src/host/integration/remote-tool-backend.d.ts +97 -0
- package/lib/types/src/host/integration/remote-tools.d.ts +206 -0
- package/lib/types/src/host/integration/root-registry.d.ts +115 -0
- package/lib/types/src/host/integration/roots/anchor-directory.d.ts +56 -0
- package/lib/types/src/host/integration/roots/bin-root-resolver.d.ts +81 -0
- package/lib/types/src/host/integration/roots/remote-home.d.ts +69 -0
- package/lib/types/src/host/integration/roots/root-registry.d.ts +144 -0
- package/lib/types/src/host/integration/roots/roots-wiring.d.ts +219 -0
- package/lib/types/src/host/integration/roots/session-root-index.d.ts +52 -0
- package/lib/types/src/host/integration/roots-api.d.ts +407 -0
- package/lib/types/src/host/integration/roots-wiring.d.ts +211 -0
- package/lib/types/src/host/integration/session/session-api.d.ts +88 -0
- package/lib/types/src/host/integration/session/session-bin-lock.d.ts +80 -0
- package/lib/types/src/host/integration/session/session-header.d.ts +151 -0
- package/lib/types/src/host/integration/session/session-labels.d.ts +56 -0
- package/lib/types/src/host/integration/session/session-route-wiring.d.ts +50 -0
- package/lib/types/src/host/integration/session-admin-service.d.ts +236 -0
- package/lib/types/src/host/integration/session-api.d.ts +81 -0
- package/lib/types/src/host/integration/session-bin-lock.d.ts +80 -0
- package/lib/types/src/host/integration/session-header.d.ts +118 -0
- package/lib/types/src/host/integration/session-labels.d.ts +56 -0
- package/lib/types/src/host/integration/session-route-wiring.d.ts +50 -0
- package/lib/types/src/host/integration/session-scope.d.ts +113 -0
- package/lib/types/src/host/integration/session-tools.d.ts +44 -0
- package/lib/types/src/host/integration/strict-arguments.d.ts +79 -0
- package/lib/types/src/host/integration/takeover/takeover-api.d.ts +62 -0
- package/lib/types/src/host/integration/takeover/takeover-route-wiring.d.ts +60 -0
- package/lib/types/src/host/integration/takeover/takeover-service.d.ts +167 -0
- package/lib/types/src/host/integration/terminal-manifest.d.ts +94 -0
- package/lib/types/src/host/integration/tools/management-tools.d.ts +245 -0
- package/lib/types/src/host/integration/tools/remote-job-tools.d.ts +316 -0
- package/lib/types/src/host/integration/tools/remote-lsp-tools.d.ts +43 -0
- package/lib/types/src/host/integration/tools/remote-tools.d.ts +236 -0
- package/lib/types/src/host/integration/tools/session-target.d.ts +52 -0
- package/lib/types/src/host/integration/tools/session-tools.d.ts +46 -0
- package/lib/types/src/host/integration/tools/strict-arguments.d.ts +79 -0
- package/lib/types/src/host/integration/tools/terminal-manifest.d.ts +94 -0
- package/lib/types/src/host/integration/transfer/remote-copy.d.ts +66 -0
- package/lib/types/src/host/integration/transfer/transfer-adapters.d.ts +48 -0
- package/lib/types/src/host/integration/transfer/transfer-service.d.ts +212 -0
- package/lib/types/src/host/integration/transfer/transfer-tracker.d.ts +106 -0
- package/lib/types/src/host/integration/transfer-adapters.d.ts +44 -0
- package/lib/types/src/host/integration/transfer-service.d.ts +164 -0
- package/lib/types/src/host/integration/transfer-tracker.d.ts +106 -0
- package/lib/types/src/host/integration/user-roots.d.ts +461 -0
- package/lib/types/src/index.d.ts +66 -0
- package/lib/types/src/local-bridge-refs.d.ts +26 -0
- package/lib/types/src/local-bridge.d.ts +36 -0
- package/lib/types/src/shared/format.d.ts +13 -0
- package/lib/types/src/wiring/connections.d.ts +189 -0
- package/lib/types/src/wiring/fs.d.ts +13 -0
- package/lib/types/src/wiring/helpers.d.ts +144 -0
- package/lib/types/src/wiring/install-memo.d.ts +32 -0
- package/lib/types/src/wiring/legacy-dirs.d.ts +62 -0
- package/lib/types/src/wiring/package-paths.d.ts +35 -0
- package/lib/types/src/wiring/roots.d.ts +21 -0
- package/lib/types/src/wiring/state-dir.d.ts +29 -0
- package/lib/types/src/wiring/tools.d.ts +35 -0
- package/package.json +120 -4
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `ctx.fs` provider: the core filesystem seam served by this plugin's router.
|
|
3
|
+
*
|
|
4
|
+
* Core components (the model's `read`/`write`/`edit` tools, the workspace file panel, agent
|
|
5
|
+
* instructions) reach the filesystem only through `ctx.fs`, so registering this provider is what
|
|
6
|
+
* makes remote roots usable everywhere without touching a single consumer.
|
|
7
|
+
*
|
|
8
|
+
* Mapping rules that matter:
|
|
9
|
+
*
|
|
10
|
+
* - `targetKey` is the router's namespaced key, wrapped in the core's branded type. Consumers treat
|
|
11
|
+
* it as opaque; this provider is the owner that may parse it back.
|
|
12
|
+
* - Core error codes are produced here, not leaked from the domain layer: `FS_STALE_VERSION`,
|
|
13
|
+
* `FS_NOT_OBSERVED`, `FS_EDIT_NOT_FOUND`, `FS_AMBIGUOUS_EDIT`, `FS_NOT_TEXT`, `FS_TOO_LARGE`,
|
|
14
|
+
* `FS_SANDBOX_DENIED`, `FS_IO_ERROR`.
|
|
15
|
+
* - The fence is the plugin's own multi-root confinement (ADR-0003): `read-only` denies every
|
|
16
|
+
* mutation, a root registered `writable: false` is denied, and `workspace-write` admits every
|
|
17
|
+
* registered root — the core's single-root fence cannot express several roots.
|
|
18
|
+
*
|
|
19
|
+
* @module dsh-remote-workspace/integration/fs-provider
|
|
20
|
+
*/
|
|
21
|
+
import { Context } from '@deepseek-ai/cordis';
|
|
22
|
+
import { FileSystem, FsError, FsVersion } from '@deepseek-ai/dsh-fs';
|
|
23
|
+
import type { FsDirEntry, FsEditOutcome, FsEditRequest, FsInfo, FsPathInfo, FsTarget, FsWriteIntent, FsWriteOutcome } from '@deepseek-ai/dsh-fs';
|
|
24
|
+
import type { SandboxExecutionPolicy, SandboxMode } from '@deepseek-ai/dsh-sandbox';
|
|
25
|
+
import type { Backend } from '../app/backend.ts';
|
|
26
|
+
import { SshDevError, type SshDevErrorCode } from '../domain/errors.ts';
|
|
27
|
+
/**
|
|
28
|
+
* One true window read: the bytes at `[offset, offset + length)`, fetched by the backend that owns
|
|
29
|
+
* the path **without** buffering the prefix before `offset` (the defect recorded in audit 0016's Lead
|
|
30
|
+
* list, item 1: `readByteRange` used to read `[0, end)` and slice).
|
|
31
|
+
*/
|
|
32
|
+
export interface RangeWindow {
|
|
33
|
+
readonly bytes: Uint8Array;
|
|
34
|
+
/** Full size of the file when the backend can report it cheaply; `undefined` when it cannot. */
|
|
35
|
+
readonly size?: number;
|
|
36
|
+
/**
|
|
37
|
+
* Whether `bytes` stops before the end of the file.
|
|
38
|
+
*
|
|
39
|
+
* `false` means the window reached EOF and is the **only** signal
|
|
40
|
+
* {@link RoutedFsProvider.readByteRange} needs to stop paging: without it a window that ends exactly
|
|
41
|
+
* at EOF would cost one extra empty round trip, and a window that ends past EOF would look identical
|
|
42
|
+
* to one that stopped short. Every reader must report it truthfully — a reader that keeps saying
|
|
43
|
+
* `true` makes the provider page on past the end.
|
|
44
|
+
*/
|
|
45
|
+
readonly truncated: boolean;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Offset-read seam.
|
|
49
|
+
*
|
|
50
|
+
* This is an **injected optional function**, not a `Backend` method: `Backend` (and
|
|
51
|
+
* `RemoteBackend`, the only backend that could implement it) is deliberately outside this change's
|
|
52
|
+
* write scope, so the capability arrives as a constructor dependency instead. It is handed the
|
|
53
|
+
* resolved backend and its root-absolute path, so one injected function can serve every root.
|
|
54
|
+
*
|
|
55
|
+
* Return `null` — never throw — to say "this backend/transport has no offset read". The provider then
|
|
56
|
+
* falls back to the bounded prefix read and does **not** pretend the read was chunked.
|
|
57
|
+
*/
|
|
58
|
+
export type BackendRangeReader = (backend: Backend, request: {
|
|
59
|
+
readonly absolute: string;
|
|
60
|
+
readonly offset: number;
|
|
61
|
+
readonly length: number;
|
|
62
|
+
}, signal?: AbortSignal) => Promise<RangeWindow | null>;
|
|
63
|
+
/** Options for {@link RoutedFsProvider}. */
|
|
64
|
+
export interface RoutedFsProviderOptions {
|
|
65
|
+
/**
|
|
66
|
+
* How to read a byte window where the backend can seek.
|
|
67
|
+
*
|
|
68
|
+
* Omitted (the default, and what `ctx.plugin(RoutedFsProvider)` gets) means every read keeps the
|
|
69
|
+
* old semantics. Wire it at the point the remote backend is built (`src/index.ts`
|
|
70
|
+
* `createRemoteBackend`, currently lines 355-365), where the transport — and therefore
|
|
71
|
+
* `Transport.readFileRange` — is reachable; see {@link createBackendRangeReader}.
|
|
72
|
+
*/
|
|
73
|
+
readonly readBytesRange?: BackendRangeReader;
|
|
74
|
+
/** Largest window one underlying request may ask for; the memory bound for a chunked read. */
|
|
75
|
+
readonly rangeChunkBytes?: number;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Filesystem provider over the plugin's roots.
|
|
79
|
+
*
|
|
80
|
+
* Registers as `ctx.fs`; the composition replaces the core `fs-sandbox` row with this one so the
|
|
81
|
+
* model's file tools follow the router (see `cordis.patch.yml`).
|
|
82
|
+
*/
|
|
83
|
+
export declare class RoutedFsProvider extends FileSystem {
|
|
84
|
+
#private;
|
|
85
|
+
static inject: string[];
|
|
86
|
+
protected readonly rangeReader: BackendRangeReader | undefined;
|
|
87
|
+
protected readonly rangeChunkBytes: number;
|
|
88
|
+
constructor(ctx: Context, options?: RoutedFsProviderOptions);
|
|
89
|
+
/** This provider confines writes to the registered roots. */
|
|
90
|
+
get sandboxMode(): SandboxMode;
|
|
91
|
+
/** Resolve a model-facing path to the router's target and wrap it for the core. */
|
|
92
|
+
resolve(path: string, opts?: {
|
|
93
|
+
cwd?: string;
|
|
94
|
+
signal?: AbortSignal;
|
|
95
|
+
}): Promise<FsTarget>;
|
|
96
|
+
/** Path a subprocess in the target's world can open (the remote absolute path for remote roots). */
|
|
97
|
+
processPath(target: FsTarget): string;
|
|
98
|
+
/** Map a host path onto the local world when one of the registered roots covers it. */
|
|
99
|
+
processPathFromHostPath(hostPath: string): string | undefined;
|
|
100
|
+
/** Canonical `file:` URI in the target's world. */
|
|
101
|
+
fileUrl(target: FsTarget): string;
|
|
102
|
+
/** Containment; two targets in different worlds never contain one another. */
|
|
103
|
+
contains(parent: FsTarget, child: FsTarget): boolean;
|
|
104
|
+
/** Metadata, or `undefined` for an absent target. */
|
|
105
|
+
stat(target: FsTarget, signal?: AbortSignal): Promise<FsInfo | undefined>;
|
|
106
|
+
/** Path metadata without following a final symlink, where the backend can tell. */
|
|
107
|
+
lstat(path: string, opts?: {
|
|
108
|
+
cwd?: string;
|
|
109
|
+
}, signal?: AbortSignal): Promise<FsPathInfo | undefined>;
|
|
110
|
+
/** Whole-file UTF-8 text; binary and oversize files fail with typed codes. */
|
|
111
|
+
readText(target: FsTarget, signal?: AbortSignal): Promise<string>;
|
|
112
|
+
/**
|
|
113
|
+
* Stream text in chunks.
|
|
114
|
+
*
|
|
115
|
+
* Backends currently hand back the whole bounded body, so this yields it as one chunk; a
|
|
116
|
+
* transport-level chunked read is a follow-up, and the bound keeps memory finite meanwhile.
|
|
117
|
+
*/
|
|
118
|
+
streamText(target: FsTarget, signal?: AbortSignal): Promise<AsyncIterable<string>>;
|
|
119
|
+
/** Whole-file bytes, refusing oversized targets instead of truncating. */
|
|
120
|
+
readBytes(target: FsTarget, signal: AbortSignal | undefined, maxBytes: number): Promise<Uint8Array>;
|
|
121
|
+
/**
|
|
122
|
+
* One byte window, bounded by the caller's window rather than the file size.
|
|
123
|
+
*
|
|
124
|
+
* Two paths, and they are deliberately distinguishable:
|
|
125
|
+
*
|
|
126
|
+
* - the backend has an offset read (the injected {@link BackendRangeReader} answers with a window):
|
|
127
|
+
* the window is fetched as a sequence of requests of at most the configured chunk size, each
|
|
128
|
+
* starting at the file offset it needs. Nothing before `offset` is ever transferred or held, so
|
|
129
|
+
* the memory bound is the chunk size plus the returned window, not the file size;
|
|
130
|
+
* - the backend has none (a local backend, a transport without `readFileRange`, or no injected
|
|
131
|
+
* reader at all): the previous semantics are kept exactly — one bounded prefix read `[0, end)`
|
|
132
|
+
* capped at 8 MiB, then a slice — and a window past that cap still fails with `FS_TOO_LARGE`
|
|
133
|
+
* instead of silently buffering the file.
|
|
134
|
+
*/
|
|
135
|
+
readByteRange(target: FsTarget, range: {
|
|
136
|
+
offset: number;
|
|
137
|
+
length: number;
|
|
138
|
+
}, signal?: AbortSignal): Promise<Uint8Array>;
|
|
139
|
+
/** Direct children, in stable name order, with resolved child targets. */
|
|
140
|
+
listDir(target: FsTarget, signal?: AbortSignal): Promise<FsDirEntry[]>;
|
|
141
|
+
/** Atomic write, honouring the write intent and the plugin's fence. */
|
|
142
|
+
writeText(target: FsTarget, content: string, expected?: FsWriteIntent, signal?: AbortSignal, sandboxPolicy?: SandboxExecutionPolicy): Promise<FsWriteOutcome>;
|
|
143
|
+
/** Literal edit with the core's three-state semantics and an optional version guard. */
|
|
144
|
+
editText(target: FsTarget, edit: FsEditRequest, expected?: {
|
|
145
|
+
version: FsVersion;
|
|
146
|
+
}, signal?: AbortSignal, sandboxPolicy?: SandboxExecutionPolicy): Promise<FsEditOutcome>;
|
|
147
|
+
/**
|
|
148
|
+
* Observation is not available yet.
|
|
149
|
+
*
|
|
150
|
+
* Tier 1 has no remote watcher, and the router deliberately reports `watches: false` instead of
|
|
151
|
+
* pretending: the caller (observation policy) falls back to revalidation on read.
|
|
152
|
+
*
|
|
153
|
+
* @param target - the target whose changes were requested.
|
|
154
|
+
* @param _changed - invalidation callback the base class would call; unused while unsupported.
|
|
155
|
+
* @param _signal - cancels watcher initialization; unused while unsupported.
|
|
156
|
+
* @returns never, because no watcher can be created yet.
|
|
157
|
+
* @throws FsError with `FS_IO_ERROR` until a watcher exists.
|
|
158
|
+
*/
|
|
159
|
+
watch(target: FsTarget, _changed: (error?: Error) => void, _signal: AbortSignal): Promise<() => Promise<void>>;
|
|
160
|
+
private rootsService;
|
|
161
|
+
private router;
|
|
162
|
+
private routedTarget;
|
|
163
|
+
/** Enforce the per-call policy and the root's own writability. */
|
|
164
|
+
private assertWritable;
|
|
165
|
+
/** Previous text content, when it is readable text inside the cap; `undefined` otherwise. */
|
|
166
|
+
private readPriorText;
|
|
167
|
+
/** Run a backend call, translating domain errors into core codes. */
|
|
168
|
+
private call;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* The plugin callback {@link createRoutedFsProvider} answers.
|
|
172
|
+
*
|
|
173
|
+
* `inject` is part of the shape because Cordis reads it off the plugin it is handed; typing it here keeps
|
|
174
|
+
* the factory's contract honest instead of relying on a cast at the call site.
|
|
175
|
+
*/
|
|
176
|
+
export interface FsProviderPlugin {
|
|
177
|
+
(ctx: Context): void;
|
|
178
|
+
inject: readonly string[];
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Build the `ctx.fs` plugin with an injected offset-read seam.
|
|
182
|
+
*
|
|
183
|
+
* Cordis registers a plugin by calling it with the context, so a deployment that has the range reader
|
|
184
|
+
* available can register `createRoutedFsProvider({ readBytesRange })` instead of the bare class and
|
|
185
|
+
* get true chunked window reads; registering the class itself keeps the old semantics. Both are the
|
|
186
|
+
* same provider, so nothing else in the composition changes.
|
|
187
|
+
*
|
|
188
|
+
* Two details are load-bearing, and both were wrong before (found by loading the real `./fs` row in a
|
|
189
|
+
* packaged host — the provider never activated, so every `fs` consumer stayed pending):
|
|
190
|
+
*
|
|
191
|
+
* - the callback must **not return the instance**. Cordis treats a function plugin's return value as
|
|
192
|
+
* an effect to apply, and a provider instance is not one: `ctx.plugin(fn)` fails with
|
|
193
|
+
* `Invalid effect` and the service is never provided. Constructing the instance inside the call is
|
|
194
|
+
* what registers it (it extends `FileSystem`, which registers itself in the constructor).
|
|
195
|
+
* - the callback must **carry the class's `inject`**. Cordis reads `inject` off the plugin it is
|
|
196
|
+
* given, and a bare arrow function has none, so the provider would install before `sshDevRoots`
|
|
197
|
+
* exists and silently read an undefined service.
|
|
198
|
+
*
|
|
199
|
+
* @param options - the seam plus the chunk size; see {@link RoutedFsProviderOptions}.
|
|
200
|
+
* @returns a plugin callback that constructs the provider on registration.
|
|
201
|
+
*/
|
|
202
|
+
export declare function createRoutedFsProvider(options: RoutedFsProviderOptions): FsProviderPlugin;
|
|
203
|
+
/**
|
|
204
|
+
* Build the {@link BackendRangeReader} the provider injects.
|
|
205
|
+
*
|
|
206
|
+
* The Lead wires the pieces that live outside this change's write scope: a store that already holds a
|
|
207
|
+
* backend's bytes (tests), or — in production — the transport behind the backend, calling
|
|
208
|
+
* `Transport.readFileRange({ path, offset, maxBytes })` and returning `null` when that optional method
|
|
209
|
+
* is absent. The provider owns everything else: chunking, the memory bound, EOF short-circuiting.
|
|
210
|
+
*
|
|
211
|
+
* @param store - reads `[offset, offset + length)` from a backend, or `undefined` for a missing path.
|
|
212
|
+
* @param options - `chunkSizeHint` advertises the largest window worth asking for; it is **not** how the
|
|
213
|
+
* provider bounds memory (the provider chunks by its own `rangeChunkBytes`).
|
|
214
|
+
* @returns a reader that answers `null` (never throws) when the store has no offset read.
|
|
215
|
+
*/
|
|
216
|
+
export declare function createBackendRangeReader(store: (backend: Backend, offset: number, length: number) => {
|
|
217
|
+
readonly bytes: Uint8Array;
|
|
218
|
+
readonly size: number;
|
|
219
|
+
} | undefined, options?: {
|
|
220
|
+
readonly chunkSizeHint?: number;
|
|
221
|
+
}): BackendRangeReader;
|
|
222
|
+
/** Translate a domain failure into the core's filesystem vocabulary. */
|
|
223
|
+
export declare function toFsError(error: SshDevError): FsError;
|
|
224
|
+
/** Codes this provider can surface, exported for tests that assert the mapping table. */
|
|
225
|
+
export declare const MAPPED_DOMAIN_CODES: readonly SshDevErrorCode[];
|
|
226
|
+
//# sourceMappingURL=fs-provider.d.ts.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LSP base protocol framing: `Content-Length: <bytes>\r\n\r\n<json>`.
|
|
3
|
+
*
|
|
4
|
+
* The agent is a **byte pipe** — it spawns the language server and moves bytes, but does not parse
|
|
5
|
+
* JSON-RPC ([protocol.md §lsp.*](../../../../dsh-over-ssh-agent/doc/01-product/protocol.md)). Everything in
|
|
6
|
+
* this module is therefore the plugin's job.
|
|
7
|
+
*
|
|
8
|
+
* `Content-Length` counts **bytes**, not characters. A parser that slices by string length passes every
|
|
9
|
+
* ASCII test and corrupts the first frame containing a multi-byte character — which in practice means the
|
|
10
|
+
* first diagnostic message written in Chinese. This module works on bytes throughout for that reason.
|
|
11
|
+
*
|
|
12
|
+
* @module dsh-over-ssh/host/integration/lsp/lsp-framing
|
|
13
|
+
*/
|
|
14
|
+
/** A cap on one frame's declared length. A corrupt header must not make us allocate without bound. */
|
|
15
|
+
export declare const MAX_LSP_FRAME_BYTES: number;
|
|
16
|
+
/** Encode one JSON-RPC message as an LSP frame. */
|
|
17
|
+
export declare function encodeLspFrame(message: unknown): Uint8Array;
|
|
18
|
+
/** What one parsed frame carried. */
|
|
19
|
+
export type LspMessage = Record<string, unknown>;
|
|
20
|
+
/**
|
|
21
|
+
* Incremental parser for a language server's stdout.
|
|
22
|
+
*
|
|
23
|
+
* Bytes arrive in whatever chunks the agent's ring buffer happens to hand over, so a frame routinely
|
|
24
|
+
* straddles two reads. {@link feed} returns every message that became complete and keeps the remainder.
|
|
25
|
+
*/
|
|
26
|
+
export declare class LspFrameParser {
|
|
27
|
+
#private;
|
|
28
|
+
/**
|
|
29
|
+
* Append bytes and return every message that is now complete.
|
|
30
|
+
*
|
|
31
|
+
* A malformed header is **not** skipped silently: the stream is no longer parseable (we cannot know
|
|
32
|
+
* where the next frame starts), so this throws and the caller must restart the session.
|
|
33
|
+
*/
|
|
34
|
+
feed(chunk: Uint8Array | string): LspMessage[];
|
|
35
|
+
/** Bytes held back waiting for the rest of a frame. Diagnostics only. */
|
|
36
|
+
get pending(): number;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=lsp-framing.d.ts.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-connection language-server sessions, shared across tool calls.
|
|
3
|
+
*
|
|
4
|
+
* A language server takes seconds to index a project, so starting one per tool call would make every query
|
|
5
|
+
* useless. This keeps one session per (connection, root, language) and reuses it.
|
|
6
|
+
*
|
|
7
|
+
* The registry also owns the **default server table**: which executable to run for a language. Nothing is
|
|
8
|
+
* probed here — if the executable is missing, `lsp.start` fails and the error says which command was tried.
|
|
9
|
+
*
|
|
10
|
+
* @module dsh-over-ssh/host/integration/lsp/lsp-registry
|
|
11
|
+
*/
|
|
12
|
+
import { LspSession, type LspDiagnostic, type LspTransportLike } from './lsp-session.ts';
|
|
13
|
+
/** Which remote root a session belongs to. */
|
|
14
|
+
export interface LspTarget {
|
|
15
|
+
readonly connectionId: string;
|
|
16
|
+
readonly root: string;
|
|
17
|
+
}
|
|
18
|
+
/** One server to run for a language. */
|
|
19
|
+
export interface LspServerCommand {
|
|
20
|
+
readonly command: string;
|
|
21
|
+
readonly args?: readonly string[];
|
|
22
|
+
}
|
|
23
|
+
/** Language id for a path, matching the LSP `languageId` convention. */
|
|
24
|
+
export declare function languageForPath(path: string): string;
|
|
25
|
+
/** The default server for a language, or `undefined` when the table has no entry. */
|
|
26
|
+
export declare function defaultServerFor(language: string): LspServerCommand | undefined;
|
|
27
|
+
/** A session as reported to a caller. */
|
|
28
|
+
export interface LspSessionInfo {
|
|
29
|
+
readonly connectionId: string;
|
|
30
|
+
readonly root: string;
|
|
31
|
+
readonly language: string;
|
|
32
|
+
readonly command: string;
|
|
33
|
+
readonly id: string;
|
|
34
|
+
readonly stopped: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A transport as seen by the registry: **possibly** LSP-capable.
|
|
38
|
+
*
|
|
39
|
+
* Tier 1 (plain `ssh host 'cmd'`) has no LSP methods at all, and the registry must say so instead of
|
|
40
|
+
* assuming. Callers get a clear "this connection cannot run a language server" rather than a TypeError on
|
|
41
|
+
* an undefined method.
|
|
42
|
+
*/
|
|
43
|
+
export type MaybeLspTransport = Partial<LspTransportLike>;
|
|
44
|
+
export interface LspRegistryDeps {
|
|
45
|
+
/** Acquire the transport for a connection; same shape the other tools use. */
|
|
46
|
+
acquire(connectionId: string): Promise<MaybeLspTransport>;
|
|
47
|
+
/** Server log lines (`window/logMessage`), for the host log. */
|
|
48
|
+
onLog?(connectionId: string, message: string): void;
|
|
49
|
+
}
|
|
50
|
+
export interface LspRegistry {
|
|
51
|
+
/**
|
|
52
|
+
* The session for this target+language, started and initialized on first use.
|
|
53
|
+
*
|
|
54
|
+
* `override` replaces the default executable — the escape hatch for a language not in the table, or for a
|
|
55
|
+
* server the caller wants to run with different arguments.
|
|
56
|
+
*/
|
|
57
|
+
session(target: LspTarget, language: string, override: LspServerCommand | undefined, signal: AbortSignal): Promise<LspSession>;
|
|
58
|
+
list(): readonly LspSessionInfo[];
|
|
59
|
+
/** Close one session. Returns false when there was nothing to close. */
|
|
60
|
+
stop(target: LspTarget, language: string, signal: AbortSignal): Promise<boolean>;
|
|
61
|
+
/** Close every session. Called on plugin teardown. */
|
|
62
|
+
disposeAll(): Promise<void>;
|
|
63
|
+
}
|
|
64
|
+
export declare function createLspRegistry(deps: LspRegistryDeps): LspRegistry;
|
|
65
|
+
/** Diagnostics grouped for rendering. Re-exported so the tool does not import the session module directly. */
|
|
66
|
+
export type { LspDiagnostic };
|
|
67
|
+
//# sourceMappingURL=lsp-registry.d.ts.map
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One language-server session, driven over the agent's byte pipe.
|
|
3
|
+
*
|
|
4
|
+
* The agent spawns the server and moves bytes; **it does not parse JSON-RPC**
|
|
5
|
+
* ([protocol.md §lsp.*](../../../../dsh-over-ssh-agent/doc/01-product/protocol.md), ADR-0006). Everything
|
|
6
|
+
* LSP-aware lives here: the `initialize` handshake, request/response correlation, server→client requests,
|
|
7
|
+
* and diagnostics collection.
|
|
8
|
+
*
|
|
9
|
+
* The transport is poll-based (`lsp.read` with an offset), so this class owns a pump loop. It backs off
|
|
10
|
+
* while the server is quiet — a language server is idle most of the time, and polling it at full speed
|
|
11
|
+
* would spend the SSH connection on nothing.
|
|
12
|
+
*
|
|
13
|
+
* @module dsh-over-ssh/host/integration/lsp/lsp-session
|
|
14
|
+
*/
|
|
15
|
+
import type { AgentLspRead, AgentLspStartRequest, AgentLspStatus } from '../../infra/transport/agent/agent-protocol-types.ts';
|
|
16
|
+
/** The slice of an agent transport one session needs. */
|
|
17
|
+
export interface LspTransportLike {
|
|
18
|
+
/**
|
|
19
|
+
* Host capabilities, when the transport exposes them (`Transport.capabilities`).
|
|
20
|
+
*
|
|
21
|
+
* Optional because the byte pipe itself does not need them — only the registry's "is this server
|
|
22
|
+
* installed" check does.
|
|
23
|
+
*/
|
|
24
|
+
readonly capabilities?: {
|
|
25
|
+
readonly lspServers?: readonly {
|
|
26
|
+
readonly command: string;
|
|
27
|
+
readonly language: string;
|
|
28
|
+
readonly path: string;
|
|
29
|
+
}[];
|
|
30
|
+
};
|
|
31
|
+
lspStart(request: AgentLspStartRequest, signal?: AbortSignal): Promise<AgentLspStatus>;
|
|
32
|
+
lspWrite(id: string, bytes: Uint8Array, signal?: AbortSignal): Promise<{
|
|
33
|
+
readonly id: string;
|
|
34
|
+
readonly written: number;
|
|
35
|
+
readonly state: string;
|
|
36
|
+
}>;
|
|
37
|
+
lspRead(id: string, offset?: number, signal?: AbortSignal): Promise<AgentLspRead>;
|
|
38
|
+
lspStop(id: string, signal?: AbortSignal): Promise<AgentLspStatus>;
|
|
39
|
+
/**
|
|
40
|
+
* Release a finished session's agent-side record and output buffer.
|
|
41
|
+
*
|
|
42
|
+
* Optional: only the agent transport has it. Absent means the agent will evict the row by itself later,
|
|
43
|
+
* which is correct — just slower to reclaim.
|
|
44
|
+
*/
|
|
45
|
+
lspForget?(id: string, signal?: AbortSignal): Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
/** One diagnostic, flattened to what a caller needs. */
|
|
48
|
+
export interface LspDiagnostic {
|
|
49
|
+
readonly line: number;
|
|
50
|
+
readonly character: number;
|
|
51
|
+
readonly endLine: number;
|
|
52
|
+
readonly endCharacter: number;
|
|
53
|
+
/** `error` / `warning` / `information` / `hint`. */
|
|
54
|
+
readonly severity: string;
|
|
55
|
+
readonly message: string;
|
|
56
|
+
/** `file` / `warning` / `info` / `hint`, when the server sent one. */
|
|
57
|
+
readonly source?: string;
|
|
58
|
+
}
|
|
59
|
+
/** A server that failed to answer in time, or exited, must not leave callers hanging. */
|
|
60
|
+
export declare class LspSessionError extends Error {
|
|
61
|
+
}
|
|
62
|
+
export interface LspSessionOptions {
|
|
63
|
+
/** Executable name on PATH or absolute path. */
|
|
64
|
+
readonly command: string;
|
|
65
|
+
readonly args?: readonly string[];
|
|
66
|
+
/** Working directory; language servers use it to find project config. */
|
|
67
|
+
readonly cwd?: string;
|
|
68
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
69
|
+
/** Called for `window/logMessage` and similar, for diagnostics. */
|
|
70
|
+
readonly onLog?: (message: string) => void;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* One running language server.
|
|
74
|
+
*
|
|
75
|
+
* Lifecycle: {@link start} → {@link initialize} → queries → {@link close}.
|
|
76
|
+
*/
|
|
77
|
+
export declare class LspSession {
|
|
78
|
+
#private;
|
|
79
|
+
constructor(transport: LspTransportLike, options: LspSessionOptions);
|
|
80
|
+
/** The agent's session id. Empty until {@link start}. */
|
|
81
|
+
get id(): string;
|
|
82
|
+
/** True once the server has exited or the session was stopped. */
|
|
83
|
+
get stopped(): boolean;
|
|
84
|
+
/** Start the server process and begin pumping its output. */
|
|
85
|
+
start(signal: AbortSignal): Promise<void>;
|
|
86
|
+
/** The `initialize` / `initialized` handshake. */
|
|
87
|
+
initialize(rootPath: string, signal: AbortSignal): Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Ask for diagnostics on one document.
|
|
90
|
+
*
|
|
91
|
+
* Diagnostics are **pushed**: the server sends `textDocument/publishDiagnostics` after `didOpen` or
|
|
92
|
+
* `didChange`, with no request to correlate against. So this opens the document, then waits for a
|
|
93
|
+
* notification for that URI (or the timeout). A server that never publishes yields an empty list rather
|
|
94
|
+
* than an error — "no diagnostics" and "did not answer" look the same to a caller that only wants to
|
|
95
|
+
* know whether the file is clean.
|
|
96
|
+
*/
|
|
97
|
+
diagnostics(uri: string, text: string, signal: AbortSignal, timeoutMs?: number): Promise<{
|
|
98
|
+
readonly diagnostics: readonly LspDiagnostic[];
|
|
99
|
+
readonly answered: boolean;
|
|
100
|
+
}>;
|
|
101
|
+
/**
|
|
102
|
+
* Open a document, or send the new text if it is already open.
|
|
103
|
+
*
|
|
104
|
+
* Position-based queries (`hover`, `definition`) need this first: a server answers about documents it
|
|
105
|
+
* knows, and an unopened file gets an empty result rather than an error — which reads as "no symbol here".
|
|
106
|
+
*/
|
|
107
|
+
openDocument(uri: string, text: string): void;
|
|
108
|
+
/** Diagnostics already received for a URI, without asking again. */
|
|
109
|
+
cachedDiagnostics(uri: string): readonly LspDiagnostic[] | undefined;
|
|
110
|
+
/** One `textDocument/hover` query. */
|
|
111
|
+
hover(uri: string, line: number, character: number, signal: AbortSignal): Promise<unknown>;
|
|
112
|
+
/** One `textDocument/definition` query. */
|
|
113
|
+
definition(uri: string, line: number, character: number, signal: AbortSignal): Promise<unknown>;
|
|
114
|
+
/** Send one JSON-RPC request and wait for its response. */
|
|
115
|
+
request(method: string, params: unknown, signal: AbortSignal, timeoutMs?: number): Promise<unknown>;
|
|
116
|
+
/** Send one JSON-RPC notification (no response expected). */
|
|
117
|
+
notify(method: string, params: unknown): void;
|
|
118
|
+
/** Shut the server down politely, then stop it. */
|
|
119
|
+
close(signal: AbortSignal): Promise<void>;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Absolute path → `file://` URI.
|
|
123
|
+
*
|
|
124
|
+
* Percent-encoding matters: a path with a space or a Chinese character becomes a URI the server will not
|
|
125
|
+
* match against its own, and every subsequent `didOpen` silently does nothing.
|
|
126
|
+
*/
|
|
127
|
+
export declare function pathToUri(absolute: string): string;
|
|
128
|
+
/** `file://` URI → absolute path. */
|
|
129
|
+
export declare function uriToPath(uri: string): string;
|
|
130
|
+
//# sourceMappingURL=lsp-session.d.ts.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The management tool surface: `remote_transfer` (upload/download) and `remote_delete` (recycle/permanent).
|
|
3
|
+
*
|
|
4
|
+
* `remote_delete` is the reason this module exists separately from `remote-tools.ts`: a **permanent** delete
|
|
5
|
+
* bypasses `ctx.fs` and cannot be undone, so it goes through the platform's pre-execute waterfall and is
|
|
6
|
+
* answered with `ask` (which the registry turns into a denial when the composition has no approval surface —
|
|
7
|
+
* fail closed). Recycle mode is an ordinary call: it is recoverable and `undo` exists.
|
|
8
|
+
*
|
|
9
|
+
* @module dsh-remote-workspace/integration/management-tools
|
|
10
|
+
*/
|
|
11
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
12
|
+
import { type ToolDefinition } from '@deepseek-ai/dsh-tools';
|
|
13
|
+
import type { DiscardResult } from '../app/recycle-bin-service.ts';
|
|
14
|
+
import type { TransferResult } from './transfer-service.ts';
|
|
15
|
+
/** Where one call lands. */
|
|
16
|
+
export interface ManagementTarget {
|
|
17
|
+
readonly connectionId: string;
|
|
18
|
+
/** Backend-absolute root path. */
|
|
19
|
+
readonly root: string;
|
|
20
|
+
}
|
|
21
|
+
/** What the tools need, injected so the definitions stay testable without transports. */
|
|
22
|
+
export interface ManagementToolBackend {
|
|
23
|
+
/** Resolve the calling agent's working directory to a remote root. */
|
|
24
|
+
resolve(cwd: string): ManagementTarget | undefined;
|
|
25
|
+
/** Run one upload or download. */
|
|
26
|
+
transfer(request: {
|
|
27
|
+
readonly target: ManagementTarget;
|
|
28
|
+
readonly direction: 'upload' | 'download';
|
|
29
|
+
readonly localPath: string;
|
|
30
|
+
readonly remotePath: string;
|
|
31
|
+
readonly signal: AbortSignal;
|
|
32
|
+
}): Promise<TransferResult>;
|
|
33
|
+
/** Discard one path, into the bin or permanently. */
|
|
34
|
+
discard(request: {
|
|
35
|
+
readonly target: ManagementTarget;
|
|
36
|
+
readonly path: string;
|
|
37
|
+
readonly mode: 'recycle' | 'permanent';
|
|
38
|
+
readonly signal: AbortSignal;
|
|
39
|
+
}): Promise<DiscardResult>;
|
|
40
|
+
}
|
|
41
|
+
/** Human summary of a finished transfer. */
|
|
42
|
+
export declare function formatTransfer(result: TransferResult): string;
|
|
43
|
+
/** Human summary of a discard. */
|
|
44
|
+
export declare function formatDiscard(result: DiscardResult): string;
|
|
45
|
+
/**
|
|
46
|
+
* Build the two definitions.
|
|
47
|
+
*
|
|
48
|
+
* @param backend - injected implementations.
|
|
49
|
+
* @returns definitions ready for `ctx.tools.register`.
|
|
50
|
+
*/
|
|
51
|
+
export declare function managementToolDefinitions(backend: ManagementToolBackend): ToolDefinition[];
|
|
52
|
+
/**
|
|
53
|
+
* Gate irreversible deletions behind the platform's approval surface.
|
|
54
|
+
*
|
|
55
|
+
* A **denial by default** is deliberate: when the composition has no approval support, the registry turns
|
|
56
|
+
* `ask` into a denial, which is the right outcome for an operation that bypasses `ctx.fs` and cannot be undone.
|
|
57
|
+
*
|
|
58
|
+
* @param ctx - host context with the `tools` service.
|
|
59
|
+
* @returns a disposer removing the gate.
|
|
60
|
+
*/
|
|
61
|
+
export declare function permanentDeleteGate(ctx: Context): () => void;
|
|
62
|
+
/** Register both tools and the gate. */
|
|
63
|
+
export declare function registerManagementTools(ctx: Context, backend: ManagementToolBackend): () => void;
|
|
64
|
+
//# sourceMappingURL=management-tools.d.ts.map
|