@tiphareth/dsh-hardssh 0.1.2 → 0.2.2
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 +30 -0
- package/README.md +49 -25
- package/SKILLS.md +140 -0
- package/lib/base/index.js +95 -0
- package/lib/client.js +1171 -1321
- package/lib/fs.js +307 -416
- package/lib/index.js +5428 -2991
- package/lib/ledger-B-LXlftp.js +43 -0
- package/lib/ledger-D2ezq1iW.js +376 -0
- package/lib/model-Cp7f70Mb.js +5 -0
- package/lib/registry-CViuzKYI.js +421 -0
- package/lib/subprocess.js +92 -549
- package/lib/types/backend.d.ts +73 -78
- package/lib/types/base/capability.d.ts +4 -0
- package/lib/types/base/ledger-router.d.ts +42 -26
- package/lib/types/base/ledger.d.ts +30 -19
- package/lib/types/base/model.d.ts +17 -8
- package/lib/types/base/plugin.d.ts +11 -0
- package/lib/types/base/registry.d.ts +28 -31
- package/lib/types/base/router.d.ts +0 -7
- package/lib/types/client/api.d.ts +8 -8
- package/lib/types/client/connect-host.d.ts +13 -0
- package/lib/types/client/index.d.ts +6 -4
- package/lib/types/client/locales.d.ts +15 -1
- package/lib/types/client/session-connect-gate.d.ts +71 -0
- package/lib/types/client/ssh/api.d.ts +11 -9
- package/lib/types/client/ssh/apply.d.ts +14 -8
- package/lib/types/client/ssh/locales.d.ts +8 -1
- package/lib/types/client/ssh/ops-tab.d.ts +34 -0
- package/lib/types/client/ssh/panel/ClusterTab.d.ts +3 -1
- package/lib/types/client/ssh/panel/ConnectionErrorDialog.d.ts +8 -0
- package/lib/types/client/ssh/panel/SessionSecretDialog.d.ts +4 -1
- package/lib/types/client/ssh/panel/SshPanel.d.ts +8 -8
- package/lib/types/client/ssh/panel/TerminalTab.d.ts +3 -5
- package/lib/types/client/ssh/panel/TransferTab.d.ts +5 -1
- package/lib/types/client/ssh/panel/TunnelsTab.d.ts +3 -1
- package/lib/types/client/ssh/session-target.d.ts +17 -0
- package/lib/types/client/state.d.ts +4 -1
- package/lib/types/client/workspace-panel-entry.d.ts +30 -0
- package/lib/types/client/workspace-panel.d.ts +24 -0
- package/lib/types/client-http.d.ts +77 -6
- package/lib/types/core.d.ts +5 -16
- package/lib/types/fs.d.ts +18 -25
- package/lib/types/index.d.ts +138 -9
- package/lib/types/ledger.d.ts +16 -111
- package/lib/types/protocol.d.ts +0 -5
- package/lib/types/providers/index.d.ts +6 -3
- package/lib/types/providers/local/provider.d.ts +83 -23
- package/lib/types/providers/ssh/provider.d.ts +24 -57
- package/lib/types/remote/environment.d.ts +2 -0
- package/lib/types/remote/remote-fs.d.ts +12 -1
- package/lib/types/remote/remote-process.d.ts +22 -1
- package/lib/types/remote/remote-subprocess.d.ts +13 -1
- package/lib/types/remote/remote-terminal.d.ts +4 -0
- package/lib/types/remote-search.d.ts +3 -3
- package/lib/types/routes.d.ts +36 -8
- package/lib/types/runtime/dsh-capabilities.d.ts +18 -0
- package/lib/types/runtime/workspace-core.d.ts +73 -38
- package/lib/types/runtime/workspace-migration.d.ts +113 -0
- package/lib/types/ssh/connection/lease.d.ts +23 -0
- package/lib/types/ssh/connection/manager.d.ts +359 -0
- package/lib/types/ssh/connection/pool.d.ts +8 -1
- package/lib/types/ssh/engine.d.ts +97 -267
- package/lib/types/ssh/known-hosts.d.ts +22 -5
- package/lib/types/ssh/local-transfer-policy.d.ts +20 -0
- package/lib/types/ssh/plugin.d.ts +10 -15
- package/lib/types/ssh/protocol.d.ts +18 -0
- package/lib/types/ssh/routes.d.ts +15 -7
- package/lib/types/ssh/sftp/service.d.ts +216 -0
- package/lib/types/ssh/store.d.ts +20 -8
- package/lib/types/ssh/terminal/service.d.ts +49 -0
- package/lib/types/ssh/tunnel/service.d.ts +26 -0
- package/lib/types/ssh/vault.d.ts +51 -4
- package/lib/types/subprocess.d.ts +13 -21
- package/lib/types/switch/switch-fs.d.ts +52 -2
- package/lib/types/switch/switch-subprocess.d.ts +32 -3
- package/lib/types/tools.d.ts +7 -6
- package/lib/types/workspace-tool-ops.d.ts +33 -0
- package/lib/types/workspace.d.ts +18 -0
- package/lib/vault-3gpWct2Q.js +559 -0
- package/lib/workspace.js +2 -0
- package/package.json +58 -25
- package/scripts/export-legacy-workspaces.mjs +136 -0
- package/src/backend.ts +155 -483
- package/src/base/capability.ts +4 -0
- package/src/base/ledger-router.ts +214 -65
- package/src/base/ledger.ts +175 -48
- package/src/base/model.ts +18 -8
- package/src/base/plugin.ts +15 -4
- package/src/base/registry.ts +49 -41
- package/src/base/router.ts +0 -8
- package/src/client/api.ts +8 -36
- package/src/client/connect-host.ts +199 -0
- package/src/client/directory-flow.tsx +91 -36
- package/src/client/index.ts +56 -53
- package/src/client/locales.ts +14 -0
- package/src/client/session-connect-gate.ts +163 -0
- package/src/client/ssh/api.ts +245 -108
- package/src/client/ssh/apply.ts +23 -24
- package/src/client/ssh/locales.ts +16 -2
- package/src/client/ssh/ops-tab.tsx +79 -0
- package/src/client/ssh/panel/ClusterTab.tsx +9 -20
- package/src/client/ssh/panel/ConnectionErrorDialog.tsx +33 -0
- package/src/client/ssh/panel/SessionSecretDialog.tsx +5 -1
- package/src/client/ssh/panel/SshPanel.tsx +81 -77
- package/src/client/ssh/panel/TerminalTab.tsx +18 -37
- package/src/client/ssh/panel/TransferTab.tsx +43 -33
- package/src/client/ssh/panel/TunnelsTab.tsx +9 -26
- package/src/client/ssh/panel/panel.module.css +90 -127
- package/src/client/ssh/session-target.ts +69 -0
- package/src/client/state.ts +9 -1
- package/src/client/workspace-badges.ts +99 -96
- package/src/client/workspace-panel-entry.tsx +91 -0
- package/src/client/workspace-panel.tsx +227 -0
- package/src/client/workspace.module.css +76 -2
- package/src/client-http.ts +118 -18
- package/src/core.ts +37 -47
- package/src/fs.ts +137 -85
- package/src/index.ts +535 -244
- package/src/ledger.ts +62 -416
- package/src/protocol.ts +0 -6
- package/src/providers/index.ts +8 -4
- package/src/providers/local/provider.ts +259 -87
- package/src/providers/ssh/provider.ts +158 -155
- package/src/remote/environment.ts +22 -1
- package/src/remote/remote-fs.ts +123 -34
- package/src/remote/remote-process.ts +265 -203
- package/src/remote/remote-subprocess.ts +61 -18
- package/src/remote/remote-terminal.ts +106 -21
- package/src/remote-search.ts +122 -26
- package/src/routes.ts +416 -395
- package/src/runtime/dsh-capabilities.ts +19 -0
- package/src/runtime/workspace-core.ts +171 -88
- package/src/runtime/workspace-migration.ts +472 -0
- package/src/ssh/connection/lease.ts +35 -0
- package/src/ssh/connection/manager.ts +1083 -0
- package/src/ssh/connection/pool.ts +341 -275
- package/src/ssh/engine.ts +269 -1477
- package/src/ssh/known-hosts.ts +42 -18
- package/src/ssh/local-transfer-policy.ts +83 -0
- package/src/ssh/plugin.ts +20 -19
- package/src/ssh/protocol.ts +22 -1
- package/src/ssh/routes.ts +419 -187
- package/src/ssh/sftp/service.ts +967 -0
- package/src/ssh/store.ts +169 -72
- package/src/ssh/terminal/service.ts +177 -0
- package/src/ssh/tools.ts +42 -20
- package/src/ssh/tunnel/service.ts +217 -0
- package/src/ssh/vault.ts +245 -91
- package/src/subprocess.ts +88 -71
- package/src/switch/switch-fs.ts +141 -10
- package/src/switch/switch-subprocess.ts +80 -6
- package/src/tools.ts +217 -221
- package/src/workspace-tool-ops.ts +101 -0
- package/src/workspace.ts +52 -0
- package/lib/environment-BL1jddfB.js +0 -449
- package/lib/switch-fs-CAJpFY9C.js +0 -193
- package/lib/switch-fs-RrZtG2gv.js +0 -210
- package/lib/types/client/manager-button.d.ts +0 -32
- package/lib/types/client/ssh/mount.d.ts +0 -13
- package/lib/types/client/ssh/panel/HostsTab.d.ts +0 -10
- package/lib/types/client/ssh/panel/controller.d.ts +0 -23
- package/lib/types/client/ssh/sidebar-entry.d.ts +0 -25
- package/lib/types/client/workspace-gate.d.ts +0 -15
- package/lib/types/remote-runner.d.ts +0 -83
- package/lib/types/seam-state.d.ts +0 -69
- package/src/client/manager-button.tsx +0 -269
- package/src/client/ssh/mount.tsx +0 -83
- package/src/client/ssh/panel/HostsTab.tsx +0 -236
- package/src/client/ssh/panel/controller.ts +0 -48
- package/src/client/ssh/sidebar-entry.ts +0 -123
- package/src/client/workspace-gate.ts +0 -225
- package/src/remote-runner.ts +0 -201
- package/src/seam-state.ts +0 -185
|
@@ -1,101 +1,50 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The SSH engine: a per-alias persistent connection pool (ssh2) with
|
|
2
|
+
* The SSH engine facade: a per-alias persistent connection pool (ssh2) with
|
|
3
3
|
* multi-hop jump support, command execution, PTY shells, SFTP transfers,
|
|
4
4
|
* local port-forward tunnels and cluster execution —the DSH counterpart of
|
|
5
5
|
* ssh-skill's daemon + scripts, living entirely in the host process.
|
|
6
|
+
*
|
|
7
|
+
* This module stays the public entry point (its export surface and the
|
|
8
|
+
* SshEngine class identity are load-bearing: production keys caches on the
|
|
9
|
+
* engine object in a WeakMap). The implementation lives in focused internal
|
|
10
|
+
* components, each owning one resource family:
|
|
11
|
+
*
|
|
12
|
+
* connection/manager.ts pooled connections, connect chains, secrets,
|
|
13
|
+
* retry/replay policy, exec/cluster/test, redaction
|
|
14
|
+
* sftp/service.ts SFTP subsystem channel cache + every SFTP operation
|
|
15
|
+
* terminal/service.ts standalone PTY shell / streaming exec transports
|
|
16
|
+
* tunnel/service.ts local listeners, forwarded sockets, tunnel leases
|
|
17
|
+
*
|
|
18
|
+
* Dependency direction is one-way (sftp/terminal/tunnel → connection); no
|
|
19
|
+
* component imports this facade back, so there is no cycle.
|
|
6
20
|
*/
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
21
|
+
import type { Writable } from 'node:stream';
|
|
22
|
+
import type { SshConnectionService } from './connection/pool.ts';
|
|
23
|
+
import { type EngineDeps, type EngineOptions, type ExecOptions, type SshInvalidateOptions } from './connection/manager.ts';
|
|
24
|
+
import type { SshHostSummary, TransferProgress, TunnelInfo } from './protocol.ts';
|
|
10
25
|
import type { HostStoreView } from '../core.ts';
|
|
11
|
-
import { HostKeyPolicy, type HostKeyCheck, type KnownHostsStore } from './known-hosts.ts';
|
|
12
|
-
/** Options used when retiring pooled connections after host configuration changes. */
|
|
13
|
-
export interface SshInvalidateOptions {
|
|
14
|
-
/**
|
|
15
|
-
* Also invalidate hosts whose ProxyJump chain depends, directly or
|
|
16
|
-
* transitively, on the changed alias.
|
|
17
|
-
*/
|
|
18
|
-
includeDependents?: boolean;
|
|
19
|
-
/**
|
|
20
|
-
* drain: reject/reconnect subsequent acquisitions, allow existing leases
|
|
21
|
-
* to complete before closing their transport.
|
|
22
|
-
* force: close the current transport immediately.
|
|
23
|
-
*/
|
|
24
|
-
mode?: 'drain' | 'force';
|
|
25
|
-
}
|
|
26
|
-
/** Default engine knobs. */
|
|
27
|
-
export interface EngineOptions {
|
|
28
|
-
/** Connections idle longer than this are closed (ms). */
|
|
29
|
-
idleTimeoutMs?: number;
|
|
30
|
-
/** SSH handshake timeout (ms). */
|
|
31
|
-
connectTimeoutMs?: number;
|
|
32
|
-
/** Keepalive ping interval (ms). */
|
|
33
|
-
keepaliveIntervalMs?: number;
|
|
34
|
-
/** Cap on captured stdout/stderr bytes per exec (ms). */
|
|
35
|
-
maxOutputBytes?: number;
|
|
36
|
-
/** Default exec timeout (ms). */
|
|
37
|
-
defaultExecTimeoutMs?: number;
|
|
38
|
-
/** Default cluster concurrency. */
|
|
39
|
-
defaultMaxWorkers?: number;
|
|
40
|
-
/** SFTP concurrent channel count for transfers. */
|
|
41
|
-
sftpConcurrency?: number;
|
|
42
|
-
/** Optional server-host-key algorithm whitelist (e.g. ['ssh-ed25519']). */
|
|
43
|
-
hostKeyAlgorithms?: string[];
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Optional engine dependencies for host-key TOFU and secret resolution.
|
|
47
|
-
* All fields are optional and their absence preserves the pre-security
|
|
48
|
-
* behavior exactly (plaintext inline auth, no host verification) — the
|
|
49
|
-
* existing tests and call sites keep working unchanged.
|
|
50
|
-
*/
|
|
51
|
-
export interface EngineDeps {
|
|
52
|
-
/** Known-hosts trust store; when set, connections require a trusted host key. */
|
|
53
|
-
knownHosts?: KnownHostsStore;
|
|
54
|
-
/** Fingerprint check policy (defaults to a HostKeyPolicy over knownHosts). */
|
|
55
|
-
hostKeyPolicy?: HostKeyPolicy;
|
|
56
|
-
/**
|
|
57
|
-
* Secret resolution for one entry. Absent: read `password`/`passphrase`
|
|
58
|
-
* inline from the entry (plaintext store / test compatibility).
|
|
59
|
-
*/
|
|
60
|
-
resolveSecrets?: (entry: SshHostEntry) => Promise<ResolvedAuthDeps>;
|
|
61
|
-
/** Optional server-host-key algorithm whitelist override. */
|
|
62
|
-
hostKeyAlgorithms?: string[];
|
|
63
|
-
}
|
|
64
|
-
/** The resolved-auth shape passed into connect config building (vault-aware). */
|
|
65
|
-
export interface ResolvedAuthDeps {
|
|
66
|
-
kind: SshHostEntry['auth']['kind'];
|
|
67
|
-
keyPath?: string;
|
|
68
|
-
password?: string;
|
|
69
|
-
passphrase?: string;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Thrown when a connection needs a password/passphrase that is not yet
|
|
73
|
-
* available in this session (secretStorage='none' and the user hasn't entered
|
|
74
|
-
* it yet). The GUI intercepts this and prompts for the credential, then
|
|
75
|
-
* injects it via engine.setSessionPassword and retries.
|
|
76
|
-
*/
|
|
77
|
-
export declare class NeedsPasswordError extends Error {
|
|
78
|
-
/** Which secret the connection needs: 'password' or 'passphrase'. */
|
|
79
|
-
readonly secret: 'password' | 'passphrase';
|
|
80
|
-
constructor(alias: string, secret: 'password' | 'passphrase');
|
|
81
|
-
}
|
|
82
26
|
/**
|
|
83
|
-
*
|
|
84
|
-
* - never: one acquisition + one operation attempt.
|
|
85
|
-
* - connect-only: connection acquisition may be retried, but once the
|
|
86
|
-
* operation function starts it is invoked at most once (default).
|
|
87
|
-
* - idempotent: the operation may also be retried until it calls
|
|
88
|
-
* markCommitted() (i.e. while the exec channel is still opening).
|
|
27
|
+
* Enforce an exec budget ON THE SERVER as well as in the client.
|
|
89
28
|
*
|
|
90
|
-
*
|
|
91
|
-
* a
|
|
29
|
+
* The client-side deadline only stops WAITING: measured against a real host
|
|
30
|
+
* (a CentOS 7 host running OpenSSH 7.4), a timed-out exec left BOTH the wrapper
|
|
31
|
+
* shell and its child running — `bash -c sleep 30` plus `sleep 30` were still
|
|
32
|
+
* alive 20s later — because ssh2's channel `signal` request was not delivered
|
|
33
|
+
* to the remote process, and closing the channel does not kill it either.
|
|
34
|
+
* Wrapping the command in coreutils `timeout` moves the deadline INSIDE the
|
|
35
|
+
* session, which does reap the whole process group (verified on the same host:
|
|
36
|
+
* exit 124 within the budget, zero orphans).
|
|
37
|
+
*
|
|
38
|
+
* The wrapper degrades to running the command verbatim when the server has no
|
|
39
|
+
* `timeout` (busybox or a minimal image), so no host becomes unusable.
|
|
40
|
+
*
|
|
41
|
+
* @param command - the caller's command, run unchanged inside the wrapper.
|
|
42
|
+
* @param budgetMs - the client-side budget this mirrors.
|
|
43
|
+
* @returns the command to hand to the remote shell.
|
|
92
44
|
*/
|
|
93
|
-
export
|
|
94
|
-
|
|
95
|
-
export
|
|
96
|
-
timeoutMs?: number;
|
|
97
|
-
retry?: RetryPolicy;
|
|
98
|
-
}
|
|
45
|
+
export declare function wrapCommandWithServerBudget(command: string, budgetMs: number): string;
|
|
46
|
+
export { buildConnectConfig, DEFAULTS, NeedsPasswordError, sshAgentConfig, } from './connection/manager.ts';
|
|
47
|
+
export type { ConnectionManager, EngineDeps, EngineOptions, ExecOptions, ResolvedAuthDeps, RetryPolicy, SshInvalidateOptions, } from './connection/manager.ts';
|
|
99
48
|
/** A live PTY shell session. */
|
|
100
49
|
export interface ShellSession {
|
|
101
50
|
/** Assign to receive remote output. */
|
|
@@ -121,82 +70,37 @@ export interface ShellSession {
|
|
|
121
70
|
* input burst. Used by the subprocess capability seam's remote provider.
|
|
122
71
|
*/
|
|
123
72
|
export interface ExecSession extends ShellSession {
|
|
73
|
+
/** Native ssh2 channel input. Using its real Writable contract preserves
|
|
74
|
+
* bytes, backpressure, errors and finish semantics for subprocess stdin. */
|
|
75
|
+
readonly stdin: Writable;
|
|
124
76
|
/** Assign to receive the remote stderr stream. */
|
|
125
77
|
onErrData?: (data: Buffer) => void;
|
|
126
78
|
/** Send an SSH signal (e.g. 'TERM', 'KILL') to the remote process. */
|
|
127
79
|
signal(name: string): void;
|
|
128
|
-
/**
|
|
80
|
+
/** Compatibility half-close for non-streaming callers. */
|
|
129
81
|
end(data?: string): void;
|
|
130
82
|
}
|
|
131
|
-
/** One host-key verification outcome captured during a connect attempt (used
|
|
132
|
-
* to rewrite the generic handshake failure into a typed host-key error). */
|
|
133
|
-
interface HostKeyOutcome {
|
|
134
|
-
alias: string;
|
|
135
|
-
check: HostKeyCheck;
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Resolve the ssh-agent socket to offer to ssh2 (zero-input key auth,
|
|
139
|
-
* VSCode-style): `$SSH_AUTH_SOCK` — the standard OpenSSH agent socket (also
|
|
140
|
-
* exported by Git for Windows' ssh-agent and WSL). Deliberately NOT probing
|
|
141
|
-
* named pipes (Pageant / Windows OpenSSH agent): an absent pipe makes ssh2's
|
|
142
|
-
* agent query stall the whole handshake until readyTimeout instead of
|
|
143
|
-
* falling through to the next method. Keep Pageant compatibility for a
|
|
144
|
-
* future explicit opt-in. An agent that yields no keys makes ssh2 fall
|
|
145
|
-
* through to the configured methods (privateKey → password), so enabling it
|
|
146
|
-
* when a socket is present is safe. Exported for tests.
|
|
147
|
-
*/
|
|
148
|
-
export declare function sshAgentConfig(): string | undefined;
|
|
149
|
-
/** Build the ssh2 connect config for one entry (key read from disk). The
|
|
150
|
-
* timeout/keepalive knobs come from EngineOptions so they actually take
|
|
151
|
-
* effect instead of being hard-coded. Exported for tests. */
|
|
152
|
-
export declare function buildConnectConfig(entry: SshHostEntry, options: Pick<Required<EngineOptions>, 'connectTimeoutMs' | 'keepaliveIntervalMs'>, sock?: ConnectConfig['sock'], buildContext?: {
|
|
153
|
-
hostKeyPolicy?: HostKeyPolicy;
|
|
154
|
-
hostKeyAlgorithms?: string[];
|
|
155
|
-
/** Writes the verified/refused outcome back to the caller's capture slot. */
|
|
156
|
-
setOutcome?: (value: HostKeyOutcome) => void;
|
|
157
|
-
/** Vault-resolved authentication (overrides entry.auth secrets). */
|
|
158
|
-
authOverride?: ResolvedAuthDeps;
|
|
159
|
-
}): ConnectConfig;
|
|
160
83
|
/**
|
|
161
|
-
* The engine. Owns the pool, tunnels,
|
|
162
|
-
*
|
|
84
|
+
* The SSH engine. Owns the connection pool, tunnels, standalone terminal
|
|
85
|
+
* transports and SFTP channels. One instance per plugin apply.
|
|
86
|
+
*
|
|
87
|
+
* Kept as the public facade over ConnectionManager/TunnelService/
|
|
88
|
+
* TerminalService/SftpService: every method here is a thin delegation, so the
|
|
89
|
+
* class identity (and therefore WeakMap cache keys in production) is stable.
|
|
163
90
|
*/
|
|
164
91
|
export declare class SshEngine {
|
|
165
|
-
private readonly
|
|
166
|
-
private readonly
|
|
167
|
-
private readonly
|
|
168
|
-
|
|
169
|
-
* One cached SFTP subsystem channel per live client. `Client.sftp()` opens a
|
|
170
|
-
* NEW subsystem channel on every call and OpenSSH caps open sessions per
|
|
171
|
-
* connection (MaxSessions, default 10) —reopening SFTP per operation lets
|
|
172
|
-
* channels pile up on the pooled long-lived connection until listing/reading
|
|
173
|
-
* fails intermittently. Caching one channel per client fixes that; the pool
|
|
174
|
-
* drops the cache via onDispose when a connection is torn down.
|
|
175
|
-
*/
|
|
176
|
-
private readonly sftpChannels;
|
|
177
|
-
private readonly connectionPool;
|
|
178
|
-
private readonly deps;
|
|
179
|
-
private readonly hostKeyPolicy;
|
|
180
|
-
private nextTunnelId;
|
|
181
|
-
/**
|
|
182
|
-
* Session-scoped secrets (secretStorage='none'): keyed by alias, populated
|
|
183
|
-
* by the GUI on first connect, used by connectChain's resolve step, and
|
|
184
|
-
* cleared on dispose. Never persisted.
|
|
185
|
-
*/
|
|
186
|
-
private readonly sessionPasswords;
|
|
92
|
+
private readonly manager;
|
|
93
|
+
private readonly sftpService;
|
|
94
|
+
private readonly tunnelService;
|
|
95
|
+
private readonly terminalService;
|
|
187
96
|
/**
|
|
188
97
|
* @param store - the host config store.
|
|
189
|
-
* @param options - engine knobs (defaults applied).
|
|
98
|
+
* @param options - engine knobs (defaults applied after validation).
|
|
190
99
|
* @param deps - optional security deps (host-key TOFU, secret resolution).
|
|
191
100
|
* Absent → pre-security behavior (inline auth, no host verification).
|
|
192
101
|
*/
|
|
193
102
|
constructor(store: HostStoreView, options?: EngineOptions, deps?: EngineDeps);
|
|
194
|
-
/**
|
|
195
|
-
* Provide a secret for `alias` for THIS session only (never persisted).
|
|
196
|
-
* Used by the GUI when a connection needs a password/passphrase under
|
|
197
|
-
* secretStorage='none'. Once set, pooled connections reuse it until the
|
|
198
|
-
* session ends or clearSessionSecrets() is called.
|
|
199
|
-
*/
|
|
103
|
+
/** Provide a secret for `alias` for THIS session only (never persisted). */
|
|
200
104
|
setSessionPassword(alias: string, secret: {
|
|
201
105
|
password?: string;
|
|
202
106
|
passphrase?: string;
|
|
@@ -208,6 +112,8 @@ export declare class SshEngine {
|
|
|
208
112
|
} | undefined;
|
|
209
113
|
/** Drop every session secret (e.g. on secretStorage change / lock). */
|
|
210
114
|
clearSessionSecrets(): void;
|
|
115
|
+
/** Redact credentials known to this session and the optional vault guard. */
|
|
116
|
+
redact(text: string): string;
|
|
211
117
|
/** Secret-free host list (filtered by the optional query). */
|
|
212
118
|
list(query?: string): SshHostSummary[];
|
|
213
119
|
/** One host summary by alias. */
|
|
@@ -223,45 +129,24 @@ export declare class SshEngine {
|
|
|
223
129
|
/** Aliases with a live pooled transport right now (for connection-state
|
|
224
130
|
* indicators — the GUI badge colors bound workspaces by it). */
|
|
225
131
|
connectedAliases(): string[];
|
|
226
|
-
/**
|
|
227
|
-
* Retire the pooled connection for one alias.
|
|
228
|
-
*
|
|
229
|
-
* ConnectionPool knows nothing about HostStore or ProxyJump configuration
|
|
230
|
-
* (every target owns its complete jump chain, no hop records are shared),
|
|
231
|
-
* so dependent-host expansion belongs here: with includeDependents the
|
|
232
|
-
* transitive reverse ProxyJump closure of `alias` is invalidated too.
|
|
233
|
-
*/
|
|
132
|
+
/** Retire the pooled connection for one alias (optionally its dependents). */
|
|
234
133
|
invalidate(alias: string, options?: SshInvalidateOptions): void;
|
|
235
134
|
/**
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
* - connect-only: acquire may be retried, but fn is invoked at most once —
|
|
241
|
-
* once fn starts, the remote may already have observed the request, so
|
|
242
|
-
* replay could duplicate non-idempotent work.
|
|
243
|
-
* - idempotent: fn may be retried until it calls control.markCommitted()
|
|
244
|
-
* (exec marks this when the server accepted the channel).
|
|
245
|
-
*
|
|
246
|
-
* A failed operation retires the transport via lease.markBroken(); the
|
|
247
|
-
* lease is always released before the next acquire attempt.
|
|
135
|
+
* Record one channel that never acknowledged close after a hard timeout.
|
|
136
|
+
* Exposed for the exec grace path and for diagnostics/tests.
|
|
137
|
+
* @param alias - the host the channel belonged to.
|
|
138
|
+
* @returns true when the pooled transport was drained.
|
|
248
139
|
*/
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
140
|
+
noteLeakedChannel(alias: string): boolean;
|
|
141
|
+
/** A late channel close: un-count one half-open channel for this alias. */
|
|
142
|
+
noteChannelClosed(alias: string): void;
|
|
143
|
+
/** Run one command on `alias` (reusing the pooled connection). */
|
|
144
|
+
exec(alias: string, command: string, timeoutMs?: number): Promise<import('./protocol.ts').ExecResult>;
|
|
145
|
+
exec(alias: string, command: string, options: ExecOptions): Promise<import('./protocol.ts').ExecResult>;
|
|
255
146
|
/**
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
* target client. Shared by the pool and standalone shell sessions.
|
|
147
|
+
* Run one command against many hosts concurrently. Per-host failures carry
|
|
148
|
+
* the typed, secret-free fields declared on `ClusterResult`.
|
|
259
149
|
*/
|
|
260
|
-
private connectChain;
|
|
261
|
-
/** Run one command on `alias` (reusing the pooled connection). */
|
|
262
|
-
exec(alias: string, command: string, timeoutMs?: number): Promise<ExecResult>;
|
|
263
|
-
exec(alias: string, command: string, options: ExecOptions): Promise<ExecResult>;
|
|
264
|
-
/** Run one command against many hosts concurrently. */
|
|
265
150
|
cluster(options: {
|
|
266
151
|
command: string;
|
|
267
152
|
aliases?: string[];
|
|
@@ -269,14 +154,9 @@ export declare class SshEngine {
|
|
|
269
154
|
tags?: string[];
|
|
270
155
|
timeoutMs?: number;
|
|
271
156
|
maxWorkers?: number;
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
* Shared by openShell/openExec (P1-18): alias lookup, jump chain,
|
|
276
|
-
* channel-open timeout, idempotent teardown, and late-callback cleanup live
|
|
277
|
-
* here, so the two public methods keep only their session-specific assembly.
|
|
278
|
-
*/
|
|
279
|
-
private openStandaloneChannel;
|
|
157
|
+
/** Aborts every per-host run when the caller disconnects. */
|
|
158
|
+
signal?: AbortSignal;
|
|
159
|
+
}): Promise<import('./protocol.ts').ClusterResult[]>;
|
|
280
160
|
/** Open a PTY shell session for the web terminal (standalone connection). */
|
|
281
161
|
openShell(alias: string, size: {
|
|
282
162
|
cols: number;
|
|
@@ -288,36 +168,22 @@ export declare class SshEngine {
|
|
|
288
168
|
* can never tear down a pooled exec/tunnel sharing the alias.
|
|
289
169
|
*/
|
|
290
170
|
openExec(alias: string, command: string): Promise<ExecSession>;
|
|
291
|
-
/** Upload one local file (or directory tree) to a remote path. */
|
|
171
|
+
/** Upload one local file (or directory tree) to a remote path. */
|
|
172
|
+
upload(alias: string, localPath: string, remotePath: string, recursive: boolean, onProgress?: (progress: TransferProgress) => void, signal?: AbortSignal): Promise<{
|
|
292
173
|
bytes: number;
|
|
293
174
|
files: number;
|
|
294
175
|
}>;
|
|
295
176
|
/** Download one remote file to a local path. */
|
|
296
|
-
download(alias: string, remotePath: string, localPath: string, onProgress?: (progress: TransferProgress) => void): Promise<{
|
|
177
|
+
download(alias: string, remotePath: string, localPath: string, onProgress?: (progress: TransferProgress) => void, signal?: AbortSignal): Promise<{
|
|
297
178
|
bytes: number;
|
|
298
179
|
}>;
|
|
299
180
|
/** List a remote directory (file browser). Bounded by a timeout so a
|
|
300
181
|
* stalled SFTP request fails instead of leaving the file tree spinning. */
|
|
301
|
-
ls(alias: string, path: string): Promise<import('./protocol.ts').RemoteDirEntry[]>;
|
|
302
|
-
/**
|
|
303
|
-
|
|
304
|
-
* (P1-26): one lease + one batch of `sftp.realpath` calls instead of N
|
|
305
|
-
* `realpath` execs. A path that cannot resolve (dangling symlink, vanished
|
|
306
|
-
* entry) fails the whole batch — callers treat an unresolvable listing as
|
|
307
|
-
* an error rather than silently using an uncanonical path.
|
|
308
|
-
*/
|
|
309
|
-
realpaths(alias: string, remotePaths: readonly string[]): Promise<string[]>;
|
|
310
|
-
/**
|
|
311
|
-
* Classify readdir entries, following symlinks so a link to a directory
|
|
312
|
-
* (e.g. AutoDL's /root/autodl-tmp) lists as a directory instead of 'other'.
|
|
313
|
-
* Symlinks are stat'd in PARALLEL batches: serializing them turns a conda /
|
|
314
|
-
* venv bin full of links into N round-trips (seconds to tens of seconds on a
|
|
315
|
-
* slow link) — batching keeps it to a handful of round-trips. The whole pass
|
|
316
|
-
* is bounded by ls()'s timeout.
|
|
317
|
-
*/
|
|
318
|
-
private classifyEntries;
|
|
182
|
+
ls(alias: string, path: string, signal?: AbortSignal): Promise<import('./protocol.ts').RemoteDirEntry[]>;
|
|
183
|
+
/** Resolve many remote paths to their canonical form in one SFTP pass. */
|
|
184
|
+
realpaths(alias: string, remotePaths: readonly string[], signal?: AbortSignal): Promise<string[]>;
|
|
319
185
|
/** Stat one remote path (file browser / conflict checks). Bounded by a timeout. */
|
|
320
|
-
stat(alias: string, remotePath: string): Promise<{
|
|
186
|
+
stat(alias: string, remotePath: string, signal?: AbortSignal): Promise<{
|
|
321
187
|
type: 'dir' | 'file' | 'other';
|
|
322
188
|
size: number;
|
|
323
189
|
mtimeMs: number;
|
|
@@ -327,7 +193,7 @@ export declare class SshEngine {
|
|
|
327
193
|
* Lstat one remote path without following the final symlink. Returns
|
|
328
194
|
* undefined when the path is absent (the fs seam's lstat contract).
|
|
329
195
|
*/
|
|
330
|
-
lstat(alias: string, remotePath: string): Promise<{
|
|
196
|
+
lstat(alias: string, remotePath: string, signal?: AbortSignal): Promise<{
|
|
331
197
|
type: 'file' | 'directory' | 'symlink' | 'other';
|
|
332
198
|
size: number;
|
|
333
199
|
mtimeMs: number;
|
|
@@ -336,20 +202,15 @@ export declare class SshEngine {
|
|
|
336
202
|
/**
|
|
337
203
|
* Open a remote file read stream (the fs seam's streamText). The returned
|
|
338
204
|
* stream must be consumed or destroyed; the pooled connection stays busy
|
|
339
|
-
* for the stream's lifetime
|
|
340
|
-
*/
|
|
341
|
-
/**
|
|
342
|
-
* Open a remote file read stream (the fs seam's streamText). The returned
|
|
343
|
-
* stream must be consumed or destroyed; the pooled connection stays busy
|
|
344
|
-
* for the stream's lifetime (P0-10: a 'stream' lease, released on
|
|
205
|
+
* for the stream's lifetime (a 'stream' lease, released on
|
|
345
206
|
* end/close/error/destroy — not when this function returns).
|
|
346
207
|
*/
|
|
347
|
-
readStream(alias: string, remotePath: string): Promise<import('node:stream').Readable>;
|
|
208
|
+
readStream(alias: string, remotePath: string, signal?: AbortSignal): Promise<import('node:stream').Readable>;
|
|
348
209
|
/**
|
|
349
210
|
* Read one remote file fully into memory (text or binary) with its mtime.
|
|
350
211
|
* The workspace plugin's text gate (UTF-8 + size caps) lives on its caller.
|
|
351
212
|
*/
|
|
352
|
-
readFile(alias: string, remotePath: string): Promise<{
|
|
213
|
+
readFile(alias: string, remotePath: string, maxBytes?: number, signal?: AbortSignal): Promise<{
|
|
353
214
|
content: Buffer;
|
|
354
215
|
mtime: number;
|
|
355
216
|
size: number;
|
|
@@ -357,14 +218,13 @@ export declare class SshEngine {
|
|
|
357
218
|
/**
|
|
358
219
|
* Write one remote file from memory (parents are created). When
|
|
359
220
|
* `expectedMtime` is given, a stat-then-write conflict check throws before
|
|
360
|
-
* any byte is written (the GUI and
|
|
361
|
-
* overwrite protection).
|
|
221
|
+
* any byte is written (overwrite protection for the GUI and workspace tools).
|
|
362
222
|
*/
|
|
363
|
-
writeFile(alias: string, remotePath: string, content: Buffer, expectedMtime?: number): Promise<{
|
|
223
|
+
writeFile(alias: string, remotePath: string, content: Buffer, expectedMtime?: number, signal?: AbortSignal): Promise<{
|
|
364
224
|
mtime: number;
|
|
365
225
|
}>;
|
|
366
226
|
/** Create a remote directory chain (mkdir -p semantics). */
|
|
367
|
-
mkdir(alias: string, remotePath: string): Promise<void>;
|
|
227
|
+
mkdir(alias: string, remotePath: string, signal?: AbortSignal): Promise<void>;
|
|
368
228
|
/**
|
|
369
229
|
* Remove a remote file or directory. Directories require `recursive: true`
|
|
370
230
|
* and are walked depth-first (children first, then the directory itself).
|
|
@@ -374,61 +234,31 @@ export declare class SshEngine {
|
|
|
374
234
|
* recursed into — the old stat/readdir-attr check could delete the link
|
|
375
235
|
* target's contents.
|
|
376
236
|
*/
|
|
377
|
-
rm(alias: string, remotePath: string, recursive?: boolean): Promise<void>;
|
|
237
|
+
rm(alias: string, remotePath: string, recursive?: boolean, signal?: AbortSignal): Promise<void>;
|
|
378
238
|
/** Rename / move a remote path (mv semantics, same filesystem). */
|
|
379
|
-
rename(alias: string, fromPath: string, toPath: string): Promise<void>;
|
|
380
|
-
/** Reject a promise after `ms` (unref'd so it never keeps the process alive).
|
|
381
|
-
* `onTimeout` (when given) runs right before the rejection: ssh2 SFTP
|
|
382
|
-
* requests have no cancel API, so callers that hold an abort handle (e.g.
|
|
383
|
-
* a read stream) destroy it here — otherwise the underlying transfer would
|
|
384
|
-
* keep running (and, for reads, keep buffering) after the caller was told
|
|
385
|
-
* it timed out. */
|
|
386
|
-
private withTimeout;
|
|
387
|
-
/** Stat wrapper (one SFTP stat call). */
|
|
388
|
-
private sftpStat;
|
|
389
|
-
/** Lstat wrapper (does NOT follow symlinks — the deletion safety gate). */
|
|
390
|
-
private sftpLstat;
|
|
391
|
-
/**
|
|
392
|
-
* The (cached) SFTP channel for a pooled client. `Client.sftp()` opens a new
|
|
393
|
-
* subsystem channel per call, so this memoizes one channel per live client;
|
|
394
|
-
* when the channel closes the cache entry is dropped so the next call opens
|
|
395
|
-
* SFTP on the replacement connection. Failed opens are also evicted so a
|
|
396
|
-
* transient channel failure can be retried.
|
|
397
|
-
*/
|
|
398
|
-
private sftpFor;
|
|
399
|
-
/** Create a remote directory chain (stat-then-mkdir per segment). */
|
|
400
|
-
private ensureRemoteDir;
|
|
401
|
-
private fastPut;
|
|
402
|
-
private fastGet;
|
|
403
|
-
/** Remove one tunnel's transport-failure listener (both events). */
|
|
404
|
-
private removeTunnelClientFailureListener;
|
|
405
|
-
/**
|
|
406
|
-
* Mark every tunnel sharing a failed physical SSH client as failed. Failed
|
|
407
|
-
* records stay in this.tunnels so listTunnels() exposes the terminal state
|
|
408
|
-
* and stopTunnel() remains the single place that deletes + releases.
|
|
409
|
-
*/
|
|
410
|
-
private markTunnelsFailedForClient;
|
|
411
|
-
/** Start a local port-forward tunnel (listens on 127.0.0.1 only). */
|
|
239
|
+
rename(alias: string, fromPath: string, toPath: string, signal?: AbortSignal): Promise<void>;
|
|
412
240
|
startTunnel(alias: string, options: {
|
|
413
241
|
remotePort: number;
|
|
414
242
|
remoteHost?: string;
|
|
415
243
|
localPort?: number;
|
|
416
244
|
}): Promise<TunnelInfo>;
|
|
417
|
-
/** All active tunnels. */
|
|
418
245
|
listTunnels(): TunnelInfo[];
|
|
419
|
-
/** Stop one tunnel (closes listener and live sockets, releases its lease). */
|
|
420
246
|
stopTunnel(id: string): boolean;
|
|
421
|
-
/** Stop all tunnels (optionally for one alias). */
|
|
422
247
|
stopAllTunnels(alias?: string): number;
|
|
423
248
|
/** Probe connectivity: connect, run `true`, close. Typed errors the GUI
|
|
424
249
|
* must react to (host-key TOFU, session password) are NOT flattened into
|
|
425
250
|
* a plain message — callers (routes → panel / workspace gate) key their
|
|
426
251
|
* interactive dialogs on the typed error. Everything else (unreachable,
|
|
427
252
|
* timeout, auth failure) returns a failed result. */
|
|
428
|
-
test(alias: string): Promise<TestResult>;
|
|
429
|
-
/**
|
|
430
|
-
*
|
|
253
|
+
test(alias: string, signal?: AbortSignal): Promise<import('./protocol.ts').TestResult>;
|
|
254
|
+
/**
|
|
255
|
+
* Close every resource this engine owns and wipe the in-memory session
|
|
256
|
+
* password table (secrets are never persisted anywhere).
|
|
257
|
+
*
|
|
258
|
+
* Order: tunnel → terminal → SFTP → connection, so a resource is always
|
|
259
|
+
* closed before the transport that carries it, and each resource has exactly
|
|
260
|
+
* one dispose point.
|
|
261
|
+
*/
|
|
431
262
|
dispose(): void;
|
|
432
263
|
}
|
|
433
|
-
export {};
|
|
434
264
|
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -55,15 +55,20 @@ export declare class KnownHostsStore {
|
|
|
55
55
|
private load;
|
|
56
56
|
private save;
|
|
57
57
|
lookup(alias: string): KnownHostRecord | undefined;
|
|
58
|
-
/** First encounter: record as pending
|
|
58
|
+
/** First encounter: record as pending. Re-observing keeps the recorded
|
|
59
|
+
* status/fingerprint but backfills a previously empty host/port. */
|
|
59
60
|
observe(alias: string, meta: {
|
|
60
61
|
host: string;
|
|
61
62
|
port: number;
|
|
62
63
|
keyType: string;
|
|
63
64
|
fingerprintSha256: string;
|
|
64
65
|
}): void;
|
|
65
|
-
/** pending → trusted (must match the recorded fingerprint).
|
|
66
|
-
|
|
66
|
+
/** pending → trusted (must match the recorded fingerprint). Optional
|
|
67
|
+
* host/port backfill completes a record whose first observe had no target. */
|
|
68
|
+
trust(alias: string, target?: {
|
|
69
|
+
host?: string;
|
|
70
|
+
port?: number;
|
|
71
|
+
}): void;
|
|
67
72
|
/** Forget a host (key rotation /误信 reset → next connect re-TOFU). */
|
|
68
73
|
forget(alias: string): void;
|
|
69
74
|
list(): KnownHostRecord[];
|
|
@@ -72,9 +77,21 @@ export declare class KnownHostsStore {
|
|
|
72
77
|
export declare class HostKeyPolicy {
|
|
73
78
|
private readonly knownHosts;
|
|
74
79
|
constructor(knownHosts: KnownHostsStore);
|
|
75
|
-
/** Evaluate one server key: trusted / unknown (records pending) / mismatch.
|
|
76
|
-
|
|
80
|
+
/** Evaluate one server key: trusted / unknown (records pending) / mismatch.
|
|
81
|
+
* `target` supplies the host/port the engine is actually connecting to so
|
|
82
|
+
* the audit record is complete instead of `''`/0. */
|
|
83
|
+
check(alias: string, rawKey: Buffer, target?: {
|
|
84
|
+
host?: string;
|
|
85
|
+
port?: number;
|
|
86
|
+
}): HostKeyCheck;
|
|
77
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Parse the key type out of a real SSH wire public-key blob. The blob is
|
|
90
|
+
* `string algorithm, byte[] key` where every string is a 4-byte big-endian
|
|
91
|
+
* length followed by raw bytes — NOT the `ssh-ed25519 AAAA…` authorized_keys
|
|
92
|
+
* text form, so a text-oriented decode would always yield 'ssh-unknown'.
|
|
93
|
+
*/
|
|
94
|
+
export declare function keyTypeOf(rawKey: Buffer): string;
|
|
78
95
|
/** Error: the host key is not trusted yet (first encounter, awaiting confirm). */
|
|
79
96
|
export declare class HostKeyUnknownError extends Error {
|
|
80
97
|
readonly fingerprintSha256: string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-side path policy for ssh_upload / ssh_download.
|
|
3
|
+
*
|
|
4
|
+
* These tools bridge remote I/O to the DSH host, so accepting an arbitrary
|
|
5
|
+
* absolute local path would bypass the normal FileSystem sandbox. The only
|
|
6
|
+
* trusted per-call root available to a tool body is the validated absolute cwd
|
|
7
|
+
* stored on the invoking Agent's immutable SessionHeader.
|
|
8
|
+
*/
|
|
9
|
+
import type { ToolRunContext } from '@deepseek-ai/dsh-tools';
|
|
10
|
+
export type LocalTransferDirection = 'upload-source' | 'download-destination';
|
|
11
|
+
/**
|
|
12
|
+
* Validate and canonicalize one local transfer path.
|
|
13
|
+
*
|
|
14
|
+
* Uploads must name an existing regular file. Downloads may create the final
|
|
15
|
+
* file and missing parent directories, but their nearest existing ancestor is
|
|
16
|
+
* canonicalized so an in-workspace symlink/junction cannot redirect the write
|
|
17
|
+
* outside the session root.
|
|
18
|
+
*/
|
|
19
|
+
export declare function secureLocalTransferPath(exec: ToolRunContext, localPath: string, direction: LocalTransferDirection): string;
|
|
20
|
+
//# sourceMappingURL=local-transfer-policy.d.ts.map
|
|
@@ -15,27 +15,20 @@ import type { SshEngine } from './engine.ts';
|
|
|
15
15
|
/** Settings namespace of the SSH capability. Kept as 'dsh-ssh' so existing
|
|
16
16
|
* user settings survive the package merge unchanged. */
|
|
17
17
|
export declare const SSH_SETTINGS_NAMESPACE = "dsh-ssh";
|
|
18
|
-
/** SSH-capability config, validated by the same-named schemastery schema.
|
|
18
|
+
/** SSH-capability config, validated by the same-named schemastery schema.
|
|
19
|
+
* `secretStorage` is deliberately NOT a key here: the mode decides which
|
|
20
|
+
* objects are constructed at plugin load, so it is a single source in the
|
|
21
|
+
* top-level plugin config (`Config.secretStorage`). A value left in this
|
|
22
|
+
* namespace by hand is reported as drift by src/index.ts, never applied. */
|
|
19
23
|
export interface SshConfig {
|
|
20
24
|
/** When true (default), a system-prompt section announces the SSH capability. */
|
|
21
25
|
announceToAgent?: boolean;
|
|
22
26
|
/** Master switch for the SSH surfaces (routes, tools, prompt section). */
|
|
23
27
|
enabled?: boolean;
|
|
24
|
-
/**
|
|
25
|
-
* Secret storage mode:
|
|
26
|
-
* - 'none' (default): passwords/passphrases are NEVER persisted; each
|
|
27
|
-
* session's first connection prompts for them (VSCode Remote-SSH style)
|
|
28
|
-
* and they live in memory (session password table) for the connection
|
|
29
|
-
* pool lifetime (idle 30 min auto-disconnect; new terminals reuse it).
|
|
30
|
-
* - 'vault': store secrets encrypted at rest (AES-256-GCM) under a master
|
|
31
|
-
* password / DSH_CREDENTIAL_PASSWORD; for headless agents that must run
|
|
32
|
-
* password hosts unattended.
|
|
33
|
-
*/
|
|
34
|
-
secretStorage?: 'none' | 'vault';
|
|
35
28
|
}
|
|
36
29
|
export declare const SshConfig: z<SshConfig>;
|
|
37
30
|
/** Model-facing announcement: SSH operations capability, capabilities, limits. */
|
|
38
|
-
export declare const SSH_GUIDANCE = "\u672C\u673A\u5DF2\u5B89\u88C5 dsh-hardssh \u63D2\u4EF6\uFF08\u5185\u5EFA DSH \u8FDC\u7A0B SSH \u8FD0\u7EF4\uFF09\uFF1A\u4FA7\u8FB9\u680F\u300CSSH\u300D\u5165\u53E3\uFF1BSSH \u8FD0\u7EF4\u4E0E SSH \u5DE5\u4F5C\u533A\u5728 dsh-hardssh \u4E2D\u7EDF\u4E00\u7EF4\u62A4\u3002\u80FD\u529B\uFF1A\u4E3B\u673A\u914D\u7F6E\u5B58 ~/.dsh/dsh-ssh.json\uFF08\u53EF\u4ECE ~/.ssh/config \u5BFC\u5165\uFF09\uFF1B\u6301\u4E45\u8FDE\u63A5\u6C60\u590D\u7528\u957F\u8FDE\u63A5\uFF08\u7A7A\u95F2 30 \u5206\u949F\u81EA\u52A8\u65AD\u5F00\uFF09\uFF1Bssh_list \u5217\u51FA\u4E3B\u673A\u3001ssh_exec \u6267\u884C\u8FDC\u7A0B\u547D\u4EE4\u3001ssh_upload/ssh_download \u4F20\u8F93\u6587\u4EF6\u3001ssh_tunnel \u672C\u5730\u7AEF\u53E3\u8F6C\u53D1\uFF08\u8BBF\u95EE\u8FDC\u7A0B\u6570\u636E\u5E93/\u5185\u7F51\u670D\u52A1\uFF09\u3001ssh_cluster \u96C6\u7FA4\u5E76\u53D1\u6267\u884C\uFF1B\u652F\u6301\u5BC6\u94A5/\u5BC6\u7801\u8BA4\u8BC1\u3001passphrase \u5BC6\u94A5\u4E0E ProxyJump \u8DF3\u677F\u673A\uFF1BWeb \u7EC8\u7AEF\u8D70 WebSocket\u3002\u9650\u5236\uFF1A\u4E3B\u673A\u64CD\u4F5C\u7531\u7528\u6237\u5728 GUI \u4E2D\u914D\u7F6E\u540E agent \u65B9\u53EF\u4F7F\u7528\uFF1B\
|
|
31
|
+
export declare const SSH_GUIDANCE = "\u672C\u673A\u5DF2\u5B89\u88C5 dsh-hardssh \u63D2\u4EF6\uFF08\u5185\u5EFA DSH \u8FDC\u7A0B SSH \u8FD0\u7EF4\uFF09\uFF1A\u4FA7\u8FB9\u680F\u300CSSH\u300D\u5165\u53E3\uFF1BSSH \u8FD0\u7EF4\u4E0E SSH \u5DE5\u4F5C\u533A\u5728 dsh-hardssh \u4E2D\u7EDF\u4E00\u7EF4\u62A4\u3002\u80FD\u529B\uFF1A\u4E3B\u673A\u914D\u7F6E\u5B58 ~/.dsh/dsh-ssh.json\uFF08\u53EF\u4ECE ~/.ssh/config \u5BFC\u5165\uFF09\uFF1B\u6301\u4E45\u8FDE\u63A5\u6C60\u590D\u7528\u957F\u8FDE\u63A5\uFF08\u7A7A\u95F2 30 \u5206\u949F\u81EA\u52A8\u65AD\u5F00\uFF09\uFF1Bssh_list \u5217\u51FA\u4E3B\u673A\u3001ssh_exec \u6267\u884C\u8FDC\u7A0B\u547D\u4EE4\u3001ssh_upload/ssh_download \u4F20\u8F93\u6587\u4EF6\u3001ssh_tunnel \u672C\u5730\u7AEF\u53E3\u8F6C\u53D1\uFF08\u8BBF\u95EE\u8FDC\u7A0B\u6570\u636E\u5E93/\u5185\u7F51\u670D\u52A1\uFF09\u3001ssh_cluster \u96C6\u7FA4\u5E76\u53D1\u6267\u884C\uFF1B\u652F\u6301\u5BC6\u94A5/\u5BC6\u7801\u8BA4\u8BC1\u3001passphrase \u5BC6\u94A5\u4E0E ProxyJump \u8DF3\u677F\u673A\uFF1BWeb \u7EC8\u7AEF\u8D70 WebSocket\u3002\u9650\u5236\uFF1A\u4E3B\u673A\u64CD\u4F5C\u7531\u7528\u6237\u5728 GUI \u4E2D\u914D\u7F6E\u540E agent \u65B9\u53EF\u4F7F\u7528\uFF1B\u51ED\u636E\u9ED8\u8BA4\u4E0D\u843D\u76D8\uFF08secretStorage=none \u65F6\u4EC5\u5B58\u4E8E\u672C\u8FDB\u7A0B\u5185\u5B58\uFF0C\u6309\u8FDE\u63A5\u5B58\u6D3B\u671F\u590D\u7528\u3001\u8FDE\u63A5\u6C60\u56DE\u6536\u5373\u5931\u6548\uFF1BsecretStorage=vault \u65F6\u4EE5 AES-256-GCM \u52A0\u5BC6\u5B58\u4E8E\u672C\u673A vault \u6587\u4EF6\uFF0C`DSH_CREDENTIAL_PASSWORD` \u81EA\u52A8\u89E3\u9501\u9ED8\u8BA4\u5173\u95ED\uFF0C\u9700 vaultAutoUnlock: env \u663E\u5F0F\u5F00\u542F\uFF09\uFF1B\u547D\u4EE4\u8F93\u51FA\u4E0E\u9519\u8BEF\u7ECF\u8FC7\u51ED\u636E\u8131\u654F\uFF08\u5DF2\u63ED\u793A/\u4F1A\u8BDD\u5185\u7684\u5BC6\u7801\u4F1A\u88AB\u66FF\u6362\uFF09\uFF0C\u4F46\u4ECD\u53EF\u80FD\u542B\u5176\u5B83\u654F\u611F\u4FE1\u606F\uFF1B\u8FDE\u63A5\u5EFA\u7ACB\u5931\u8D25\u4F1A\u81EA\u52A8\u91CD\u8FDE\uFF0C\u4F46\u547D\u4EE4\u6216\u6587\u4EF6\u64CD\u4F5C\u4E00\u65E6\u63D0\u4EA4\u5C31\u4E0D\u4F1A\u81EA\u52A8\u91CD\u8BD5\u2014\u2014\u7ED3\u679C\u4E0D\u660E\u786E\u65F6\u5E94\u5148\u6838\u5B9E\u8FDC\u7AEF\u72B6\u6001\uFF0C\u518D\u51B3\u5B9A\u662F\u5426\u4EBA\u5DE5\u91CD\u8BD5\uFF1B\u4F20\u8F93/\u6267\u884C\u6D88\u8017\u771F\u5B9E\u8FDC\u7A0B\u8D44\u6E90\uFF0C\u5148\u786E\u8BA4\u518D\u64CD\u4F5C\u3002\u7528\u6237\u63D0\u5230\u300CSSH / \u8FDC\u7A0B\u670D\u52A1\u5668 / \u670D\u52A1\u5668\u64CD\u4F5C / \u8DF3\u677F\u673A / \u96A7\u9053 / \u90E8\u7F72 / \u4E0A\u4F20\u4E0B\u8F7D\u300D\u65F6\u5373\u6307\u672C\u63D2\u4EF6\uFF0C\u8BF7\u636E\u6B64\u534F\u4F5C\u3002";
|
|
39
32
|
/** Shared instances the SSH surfaces run on. */
|
|
40
33
|
export interface SshCapabilityDeps {
|
|
41
34
|
store: import('./store.ts').SecureHostStore;
|
|
@@ -44,8 +37,10 @@ export interface SshCapabilityDeps {
|
|
|
44
37
|
knownHosts?: import('./known-hosts.ts').KnownHostsStore;
|
|
45
38
|
/** Credential vault; absent → vault endpoints disabled. */
|
|
46
39
|
vault?: import('./vault.ts').Vault;
|
|
47
|
-
/** SSH-bound workspace
|
|
48
|
-
|
|
40
|
+
/** SSH-bound workspace record source (host-delete reference guard). The
|
|
41
|
+
* legacy ledger in legacy mode, the generic WorkspaceCore-backed projection
|
|
42
|
+
* store in the default generic mode. */
|
|
43
|
+
ledger?: import('../backend.ts').WorkspaceStoreView;
|
|
49
44
|
}
|
|
50
45
|
/**
|
|
51
46
|
* Mount the SSH operations surfaces (routes, tools, settings, prompt) against
|