dsh-over-ssh 0.1.0 → 0.1.1
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/README.md +10 -10
- package/agents/agent-manifest.json +1 -1
- package/lib/client.js +363 -314
- package/lib/file-references.js +80 -35
- package/lib/fs.js +158 -75
- package/lib/index.js +4103 -3397
- package/lib/terminal-wrapper.js +9 -4
- package/lib/types/src/config.d.ts +80 -9
- package/lib/types/src/file-references.d.ts +0 -1
- package/lib/types/src/host/app/backend.d.ts +9 -1
- package/lib/types/src/host/app/recycle-bin/recycle-bin-contract.d.ts +0 -1
- package/lib/types/src/host/app/recycle-bin/recycle-bin-service.d.ts +0 -1
- package/lib/types/src/host/app/session-admin/session-admin-contract.d.ts +12 -1
- package/lib/types/src/host/app/session-admin/session-admin-service.d.ts +0 -1
- package/lib/types/src/host/app/transport/transport-selector.d.ts +0 -1
- package/lib/types/src/host/app/transport/transport-service.d.ts +0 -1
- package/lib/types/src/host/domain/capability.d.ts +0 -1
- package/lib/types/src/host/domain/errors.d.ts +0 -1
- package/lib/types/src/host/domain/roots/connection.d.ts +0 -1
- package/lib/types/src/host/domain/roots/root.d.ts +24 -9
- package/lib/types/src/host/domain/roots/session-roots.d.ts +0 -1
- package/lib/types/src/host/domain/roots/ssh-config.d.ts +0 -1
- package/lib/types/src/host/domain/session/session-admin.d.ts +0 -1
- package/lib/types/src/host/domain/transfer/recycle-bin.d.ts +0 -1
- package/lib/types/src/host/domain/transfer/transfer.d.ts +0 -1
- package/lib/types/src/host/infra/transport/agent/agent-installer.d.ts +0 -1
- package/lib/types/src/host/infra/transport/ssh/ssh2-transport.d.ts +0 -1
- package/lib/types/src/host/infra/transport/transport-base.d.ts +0 -1
- package/lib/types/src/host/infra/transport/transport.d.ts +16 -2
- package/lib/types/src/host/integration/agent/agent-preflight.d.ts +0 -1
- package/lib/types/src/host/integration/fs/file-reference-provider.d.ts +0 -1
- package/lib/types/src/host/integration/fs/fs-provider.d.ts +0 -2
- package/lib/types/src/host/integration/transfer/transfer-service.d.ts +0 -1
- package/lib/types/src/index.d.ts +23 -1
- package/lib/types/src/local-bridge-refs.d.ts +0 -1
- package/lib/types/src/local-bridge.d.ts +0 -1
- package/lib/types/src/shared/root-keys.d.ts +120 -0
- package/lib/types/src/wiring/helpers.d.ts +0 -1
- package/package.json +5 -3
- package/lib/types/src/client/controls.d.ts +0 -131
- package/lib/types/src/client/core/card-focus-trap.d.ts +0 -45
- package/lib/types/src/client/core/controls.d.ts +0 -164
- package/lib/types/src/client/core/latest.d.ts +0 -48
- package/lib/types/src/client/core/locale.d.ts +0 -38
- package/lib/types/src/client/core/message.d.ts +0 -37
- package/lib/types/src/client/core/panel-model.d.ts +0 -134
- package/lib/types/src/client/core/portal.d.ts +0 -266
- package/lib/types/src/client/core/remote.d.ts +0 -84
- package/lib/types/src/client/core/styles.d.ts +0 -458
- package/lib/types/src/client/file-tree-io.d.ts +0 -19
- package/lib/types/src/client/file-tree.d.ts +0 -271
- package/lib/types/src/client/flows/directory-browser.d.ts +0 -587
- package/lib/types/src/client/flows/session-directory.d.ts +0 -415
- package/lib/types/src/client/flows/workspace-directory-flow.d.ts +0 -480
- package/lib/types/src/client/locale.d.ts +0 -31
- package/lib/types/src/client/panel-model.d.ts +0 -37
- package/lib/types/src/client/panel.d.ts +0 -77
- package/lib/types/src/client/roots/connection-test.d.ts +0 -69
- package/lib/types/src/client/roots/roots-panel-state.d.ts +0 -307
- package/lib/types/src/client/roots/roots-panel-view.d.ts +0 -280
- package/lib/types/src/client/roots/roots-panel.d.ts +0 -364
- package/lib/types/src/client/roots/transfer-panel-view.d.ts +0 -35
- package/lib/types/src/client/roots/transfer-panel.d.ts +0 -241
- package/lib/types/src/client/roots/transfer-progress.d.ts +0 -67
- package/lib/types/src/client/roots-panel-view.d.ts +0 -236
- package/lib/types/src/client/roots-panel.d.ts +0 -347
- package/lib/types/src/client/session/session-panel-view.d.ts +0 -336
- package/lib/types/src/client/session/session-panel.d.ts +0 -322
- package/lib/types/src/client/session/session-remote-mark.d.ts +0 -110
- package/lib/types/src/client/session/session-row-actions.d.ts +0 -398
- package/lib/types/src/client/session-panel.d.ts +0 -152
- package/lib/types/src/client/styles.d.ts +0 -252
- package/lib/types/src/client/workspace-directory-flow.d.ts +0 -342
- package/lib/types/src/fs.d.ts +0 -46
- package/lib/types/src/host/app/recycle-bin/recycle-bin-move.d.ts +0 -18
- package/lib/types/src/host/app/recycle-bin/recycle-bin-world.d.ts +0 -38
- package/lib/types/src/host/app/recycle-bin-contract.d.ts +0 -215
- package/lib/types/src/host/app/recycle-bin-move.d.ts +0 -18
- package/lib/types/src/host/app/recycle-bin-service.d.ts +0 -45
- package/lib/types/src/host/app/recycle-bin-world.d.ts +0 -35
- package/lib/types/src/host/app/roots/roots-api-connections.d.ts +0 -49
- package/lib/types/src/host/app/roots/roots-api-contract.d.ts +0 -369
- package/lib/types/src/host/app/roots/roots-api-directories.d.ts +0 -13
- package/lib/types/src/host/app/roots/roots-api-http.d.ts +0 -77
- package/lib/types/src/host/app/roots/roots-api-long-poll.d.ts +0 -130
- package/lib/types/src/host/app/roots/roots-api-projections.d.ts +0 -102
- package/lib/types/src/host/app/roots/roots-api-roots.d.ts +0 -13
- package/lib/types/src/host/app/roots/roots-api-state.d.ts +0 -22
- package/lib/types/src/host/app/roots/roots-api-transfers.d.ts +0 -26
- package/lib/types/src/host/app/roots/roots-api.d.ts +0 -55
- package/lib/types/src/host/app/roots/user-roots.d.ts +0 -552
- package/lib/types/src/host/app/roots-api-connections.d.ts +0 -49
- package/lib/types/src/host/app/roots-api-contract.d.ts +0 -339
- package/lib/types/src/host/app/roots-api-directories.d.ts +0 -13
- package/lib/types/src/host/app/roots-api-http.d.ts +0 -77
- package/lib/types/src/host/app/roots-api-projections.d.ts +0 -102
- package/lib/types/src/host/app/roots-api-roots.d.ts +0 -13
- package/lib/types/src/host/app/roots-api-state.d.ts +0 -22
- package/lib/types/src/host/app/roots-api-support.d.ts +0 -182
- package/lib/types/src/host/app/roots-api-transfers.d.ts +0 -13
- package/lib/types/src/host/app/roots-api.d.ts +0 -55
- package/lib/types/src/host/app/routed-fs.d.ts +0 -239
- package/lib/types/src/host/app/serialize.d.ts +0 -37
- package/lib/types/src/host/app/session-admin/session-admin-context.d.ts +0 -36
- package/lib/types/src/host/app/session-admin/session-admin-delete.d.ts +0 -34
- package/lib/types/src/host/app/session-admin-context.d.ts +0 -36
- package/lib/types/src/host/app/session-admin-contract.d.ts +0 -214
- package/lib/types/src/host/app/session-admin-delete.d.ts +0 -34
- package/lib/types/src/host/app/session-admin-service.d.ts +0 -38
- package/lib/types/src/host/app/takeover/takeover-plan.d.ts +0 -227
- package/lib/types/src/host/app/takeover/takeover-rows.d.ts +0 -55
- package/lib/types/src/host/app/transport-selector.d.ts +0 -98
- package/lib/types/src/host/app/transport-service.d.ts +0 -75
- package/lib/types/src/host/app/user-roots.d.ts +0 -461
- package/lib/types/src/host/domain/bin-location.d.ts +0 -79
- package/lib/types/src/host/domain/connection.d.ts +0 -95
- package/lib/types/src/host/domain/glob-pattern.d.ts +0 -62
- package/lib/types/src/host/domain/literal-text.d.ts +0 -63
- package/lib/types/src/host/domain/path-grammar.d.ts +0 -84
- package/lib/types/src/host/domain/recycle-bin.d.ts +0 -337
- package/lib/types/src/host/domain/root.d.ts +0 -74
- package/lib/types/src/host/domain/roots/bin-location.d.ts +0 -91
- package/lib/types/src/host/domain/roots/route-table.d.ts +0 -102
- package/lib/types/src/host/domain/route-table.d.ts +0 -80
- package/lib/types/src/host/domain/session/terminal-target.d.ts +0 -136
- package/lib/types/src/host/domain/session-admin.d.ts +0 -330
- package/lib/types/src/host/domain/shell.d.ts +0 -47
- package/lib/types/src/host/domain/ssh-config.d.ts +0 -73
- package/lib/types/src/host/domain/terminal-target.d.ts +0 -132
- package/lib/types/src/host/domain/tool-requests.d.ts +0 -369
- package/lib/types/src/host/domain/transfer.d.ts +0 -260
- package/lib/types/src/host/infra/backend/local-backend.d.ts +0 -84
- package/lib/types/src/host/infra/backend/local-version.d.ts +0 -43
- package/lib/types/src/host/infra/backend/platform-local-backend.d.ts +0 -89
- package/lib/types/src/host/infra/backend/remote-backend.d.ts +0 -239
- package/lib/types/src/host/infra/cache/lru.d.ts +0 -52
- package/lib/types/src/host/infra/cache/versioned.d.ts +0 -77
- package/lib/types/src/host/infra/temp-name.d.ts +0 -19
- package/lib/types/src/host/infra/transport/agent/agent-protocol-client.d.ts +0 -132
- package/lib/types/src/host/infra/transport/agent/agent-protocol-codec.d.ts +0 -140
- package/lib/types/src/host/infra/transport/agent/agent-protocol-errors.d.ts +0 -41
- package/lib/types/src/host/infra/transport/agent/agent-protocol-frames.d.ts +0 -197
- package/lib/types/src/host/infra/transport/agent/agent-protocol-types.d.ts +0 -564
- package/lib/types/src/host/infra/transport/agent/agent-transport.d.ts +0 -418
- package/lib/types/src/host/infra/transport/agent-installer.d.ts +0 -121
- package/lib/types/src/host/infra/transport/agent-protocol-client.d.ts +0 -123
- package/lib/types/src/host/infra/transport/agent-protocol-codec.d.ts +0 -131
- package/lib/types/src/host/infra/transport/agent-protocol-errors.d.ts +0 -41
- package/lib/types/src/host/infra/transport/agent-protocol-frames.d.ts +0 -197
- package/lib/types/src/host/infra/transport/agent-protocol-types.d.ts +0 -509
- package/lib/types/src/host/infra/transport/agent-transport.d.ts +0 -353
- package/lib/types/src/host/infra/transport/alias-transport.d.ts +0 -206
- package/lib/types/src/host/infra/transport/askpass-fs.d.ts +0 -20
- package/lib/types/src/host/infra/transport/askpass.d.ts +0 -133
- package/lib/types/src/host/infra/transport/command-pool.d.ts +0 -61
- package/lib/types/src/host/infra/transport/fs-commands.d.ts +0 -140
- package/lib/types/src/host/infra/transport/git-status-route.d.ts +0 -63
- package/lib/types/src/host/infra/transport/git-status.d.ts +0 -101
- package/lib/types/src/host/infra/transport/node-spawn.d.ts +0 -26
- package/lib/types/src/host/infra/transport/session-window.d.ts +0 -51
- package/lib/types/src/host/infra/transport/shell-quote.d.ts +0 -59
- package/lib/types/src/host/infra/transport/ssh/alias-transport.d.ts +0 -206
- package/lib/types/src/host/infra/transport/ssh/askpass-fs.d.ts +0 -20
- package/lib/types/src/host/infra/transport/ssh/askpass.d.ts +0 -133
- package/lib/types/src/host/infra/transport/ssh/shell-quote.d.ts +0 -62
- package/lib/types/src/host/infra/transport/ssh/ssh-argv.d.ts +0 -43
- package/lib/types/src/host/infra/transport/ssh-argv.d.ts +0 -43
- package/lib/types/src/host/infra/transport/ssh2-transport.d.ts +0 -302
- package/lib/types/src/host/integration/agent/agent-install-io.d.ts +0 -33
- package/lib/types/src/host/integration/agent/askpass-registry.d.ts +0 -66
- package/lib/types/src/host/integration/agent-install-io.d.ts +0 -33
- package/lib/types/src/host/integration/agent-preflight.d.ts +0 -56
- package/lib/types/src/host/integration/anchor-directory.d.ts +0 -56
- package/lib/types/src/host/integration/askpass-registry.d.ts +0 -59
- package/lib/types/src/host/integration/bin-root-resolver.d.ts +0 -81
- package/lib/types/src/host/integration/connection-test.d.ts +0 -73
- package/lib/types/src/host/integration/file-reference-provider.d.ts +0 -115
- package/lib/types/src/host/integration/fs/git-status-wiring.d.ts +0 -21
- package/lib/types/src/host/integration/fs/platform-errors.d.ts +0 -94
- package/lib/types/src/host/integration/fs/remote-file-tools.d.ts +0 -369
- package/lib/types/src/host/integration/fs/remote-index.d.ts +0 -177
- package/lib/types/src/host/integration/fs-provider.d.ts +0 -226
- package/lib/types/src/host/integration/lsp/lsp-framing.d.ts +0 -38
- package/lib/types/src/host/integration/lsp/lsp-registry.d.ts +0 -67
- package/lib/types/src/host/integration/lsp/lsp-session.d.ts +0 -130
- package/lib/types/src/host/integration/management-tools.d.ts +0 -64
- package/lib/types/src/host/integration/recycle-bin-service.d.ts +0 -247
- package/lib/types/src/host/integration/remote/connection-test.d.ts +0 -73
- package/lib/types/src/host/integration/remote/remote-face.d.ts +0 -127
- package/lib/types/src/host/integration/remote/remote-tool-backend.d.ts +0 -108
- package/lib/types/src/host/integration/remote-face.d.ts +0 -127
- package/lib/types/src/host/integration/remote-file-tools.d.ts +0 -241
- package/lib/types/src/host/integration/remote-home.d.ts +0 -69
- package/lib/types/src/host/integration/remote-index.d.ts +0 -165
- package/lib/types/src/host/integration/remote-job-tools.d.ts +0 -295
- package/lib/types/src/host/integration/remote-tool-backend.d.ts +0 -97
- package/lib/types/src/host/integration/remote-tools.d.ts +0 -206
- package/lib/types/src/host/integration/root-registry.d.ts +0 -115
- package/lib/types/src/host/integration/roots/anchor-directory.d.ts +0 -56
- package/lib/types/src/host/integration/roots/bin-root-resolver.d.ts +0 -81
- package/lib/types/src/host/integration/roots/remote-home.d.ts +0 -69
- package/lib/types/src/host/integration/roots/root-registry.d.ts +0 -144
- package/lib/types/src/host/integration/roots/roots-wiring.d.ts +0 -219
- package/lib/types/src/host/integration/roots/session-root-index.d.ts +0 -52
- package/lib/types/src/host/integration/roots-api.d.ts +0 -407
- package/lib/types/src/host/integration/roots-wiring.d.ts +0 -211
- package/lib/types/src/host/integration/session/session-api.d.ts +0 -88
- package/lib/types/src/host/integration/session/session-bin-lock.d.ts +0 -80
- package/lib/types/src/host/integration/session/session-header.d.ts +0 -151
- package/lib/types/src/host/integration/session/session-labels.d.ts +0 -56
- package/lib/types/src/host/integration/session/session-route-wiring.d.ts +0 -50
- package/lib/types/src/host/integration/session-admin-service.d.ts +0 -236
- package/lib/types/src/host/integration/session-api.d.ts +0 -81
- package/lib/types/src/host/integration/session-bin-lock.d.ts +0 -80
- package/lib/types/src/host/integration/session-header.d.ts +0 -118
- package/lib/types/src/host/integration/session-labels.d.ts +0 -56
- package/lib/types/src/host/integration/session-route-wiring.d.ts +0 -50
- package/lib/types/src/host/integration/session-scope.d.ts +0 -113
- package/lib/types/src/host/integration/session-tools.d.ts +0 -44
- package/lib/types/src/host/integration/strict-arguments.d.ts +0 -79
- package/lib/types/src/host/integration/takeover/takeover-api.d.ts +0 -62
- package/lib/types/src/host/integration/takeover/takeover-route-wiring.d.ts +0 -60
- package/lib/types/src/host/integration/takeover/takeover-service.d.ts +0 -167
- package/lib/types/src/host/integration/terminal-manifest.d.ts +0 -94
- package/lib/types/src/host/integration/tools/management-tools.d.ts +0 -245
- package/lib/types/src/host/integration/tools/remote-job-tools.d.ts +0 -316
- package/lib/types/src/host/integration/tools/remote-lsp-tools.d.ts +0 -43
- package/lib/types/src/host/integration/tools/remote-tools.d.ts +0 -236
- package/lib/types/src/host/integration/tools/session-target.d.ts +0 -52
- package/lib/types/src/host/integration/tools/session-tools.d.ts +0 -46
- package/lib/types/src/host/integration/tools/strict-arguments.d.ts +0 -79
- package/lib/types/src/host/integration/tools/terminal-manifest.d.ts +0 -94
- package/lib/types/src/host/integration/transfer/remote-copy.d.ts +0 -66
- package/lib/types/src/host/integration/transfer/transfer-adapters.d.ts +0 -48
- package/lib/types/src/host/integration/transfer/transfer-tracker.d.ts +0 -106
- package/lib/types/src/host/integration/transfer-adapters.d.ts +0 -44
- package/lib/types/src/host/integration/transfer-service.d.ts +0 -164
- package/lib/types/src/host/integration/transfer-tracker.d.ts +0 -106
- package/lib/types/src/host/integration/user-roots.d.ts +0 -461
- package/lib/types/src/wiring/connections.d.ts +0 -189
- package/lib/types/src/wiring/fs.d.ts +0 -13
- package/lib/types/src/wiring/install-memo.d.ts +0 -32
- package/lib/types/src/wiring/legacy-dirs.d.ts +0 -62
- package/lib/types/src/wiring/package-paths.d.ts +0 -35
- package/lib/types/src/wiring/roots.d.ts +0 -21
- package/lib/types/src/wiring/state-dir.d.ts +0 -29
- package/lib/types/src/wiring/tools.d.ts +0 -35
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The route table: turns a model-facing path into one backend plus a root-relative path.
|
|
3
|
-
*
|
|
4
|
-
* This is the heart of "one workspace, several roots". Resolution order is deliberate, and
|
|
5
|
-
* every branch is a documented rule rather than a heuristic:
|
|
6
|
-
*
|
|
7
|
-
* 1. `ssh://<connection>/abs` — the explicit remote grammar always wins.
|
|
8
|
-
* 2. Anchor match — a path (or the session cwd) under a remote root's local anchor resolves
|
|
9
|
-
* to that remote root. The anchor is what makes a remote root a legal DSH workspace.
|
|
10
|
-
* 3. Local root containment — absolute host paths, and `cwd`-relative spellings.
|
|
11
|
-
* 4. Bare POSIX absolute path matching remote roots — needed because a prompt lists remote
|
|
12
|
-
* roots in POSIX spelling; two equally specific remote roots on different connections
|
|
13
|
-
* are reported as ambiguous instead of being guessed.
|
|
14
|
-
*
|
|
15
|
-
* @module dsh-remote-workspace/domain/route-table
|
|
16
|
-
*/
|
|
17
|
-
import { SshDevError } from './errors.ts';
|
|
18
|
-
import { type RootRef } from './root.ts';
|
|
19
|
-
/** One resolved route. */
|
|
20
|
-
export interface RouteTarget {
|
|
21
|
-
/** The root the path belongs to. */
|
|
22
|
-
readonly root: RootRef;
|
|
23
|
-
/** Root-relative spelling with `/` separators; an empty string means the root itself. */
|
|
24
|
-
readonly relativePath: string;
|
|
25
|
-
/** Stable backend key; one backend serves all paths of one root. */
|
|
26
|
-
readonly backendKey: string;
|
|
27
|
-
}
|
|
28
|
-
/** Per-call resolution options. */
|
|
29
|
-
export interface RouteOptions {
|
|
30
|
-
/** Session working directory used for relative spellings. */
|
|
31
|
-
readonly cwd?: string;
|
|
32
|
-
/** Override host-path case folding (defaults to the platform). */
|
|
33
|
-
readonly caseInsensitive?: boolean;
|
|
34
|
-
}
|
|
35
|
-
/** A path resolved to a root, or a diagnostic reason why it could not be. */
|
|
36
|
-
export type RouteOutcome = {
|
|
37
|
-
readonly ok: true;
|
|
38
|
-
readonly target: RouteTarget;
|
|
39
|
-
} | {
|
|
40
|
-
readonly ok: false;
|
|
41
|
-
readonly error: SshDevError;
|
|
42
|
-
};
|
|
43
|
-
/** Resolution table over an ordered set of roots. */
|
|
44
|
-
export declare class RouteTable {
|
|
45
|
-
#private;
|
|
46
|
-
constructor(roots?: readonly RootRef[], options?: {
|
|
47
|
-
readonly caseInsensitive?: boolean;
|
|
48
|
-
});
|
|
49
|
-
/** Roots in registration order. */
|
|
50
|
-
get roots(): readonly RootRef[];
|
|
51
|
-
/**
|
|
52
|
-
* Register a root.
|
|
53
|
-
*
|
|
54
|
-
* @param root - root to add.
|
|
55
|
-
* @throws SshDevError `ROOT_INVALID` when the root is malformed or already registered.
|
|
56
|
-
*/
|
|
57
|
-
add(root: RootRef): void;
|
|
58
|
-
/** Remove a root by its key; returns whether it was present. */
|
|
59
|
-
remove(key: string): boolean;
|
|
60
|
-
/** Replace every root; throws like {@link add} when any root is invalid. */
|
|
61
|
-
reset(roots: readonly RootRef[]): void;
|
|
62
|
-
/**
|
|
63
|
-
* Resolve a path.
|
|
64
|
-
*
|
|
65
|
-
* @param input - model-facing, host or remote spelling.
|
|
66
|
-
* @param options - session cwd and case-folding override.
|
|
67
|
-
* @returns the resolved route, or a failure carrying `ROOT_OUTSIDE` / `ROOT_AMBIGUOUS`.
|
|
68
|
-
*/
|
|
69
|
-
tryResolve(input: string, options?: RouteOptions): RouteOutcome;
|
|
70
|
-
/**
|
|
71
|
-
* Resolve a path or throw.
|
|
72
|
-
*
|
|
73
|
-
* @param input - path to resolve.
|
|
74
|
-
* @param options - session cwd and case-folding override.
|
|
75
|
-
* @returns the resolved route.
|
|
76
|
-
* @throws SshDevError `ROOT_OUTSIDE` or `ROOT_AMBIGUOUS`.
|
|
77
|
-
*/
|
|
78
|
-
resolve(input: string, options?: RouteOptions): RouteTarget;
|
|
79
|
-
}
|
|
80
|
-
//# sourceMappingURL=route-table.d.ts.map
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Which terminal a session gets: the remote host, or the local machine.
|
|
3
|
-
*
|
|
4
|
-
* The platform's only official extension point for the Web terminal is `terminalController.Config.shell`
|
|
5
|
-
* (`{ path, name, args }`), so this plugin ships a **wrapper** as that shell: the wrapper decides at launch
|
|
6
|
-
* time whether to `exec ssh` or the local login shell. See `doc/02-platform/terminal.md` §5 for the evidence
|
|
7
|
-
* and the accepted costs.
|
|
8
|
-
*
|
|
9
|
-
* This module is the decision itself, kept pure (no filesystem, no spawn) so it can be tested in
|
|
10
|
-
* milliseconds — the wrapper script is a thin `spawn` around it.
|
|
11
|
-
*
|
|
12
|
-
* Two inputs decide it, in this order:
|
|
13
|
-
*
|
|
14
|
-
* 1. **the session's recorded target** (`state/<sessionId>.json`): the host writes it when it resolves a
|
|
15
|
-
* remote root for that session, so it works even when the core hands us an unrelated cwd;
|
|
16
|
-
* 2. **the root manifest** (`roots.json`): a directory→connection map, used when the wrapper has no session
|
|
17
|
-
* id (a terminal opened by hand) — a cwd under a remote root's anchor means the user is working remotely.
|
|
18
|
-
*
|
|
19
|
-
* @module dsh-over-ssh/domain/terminal-target
|
|
20
|
-
*/
|
|
21
|
-
import { quote as posixQuote } from '../shell.ts';
|
|
22
|
-
/** How the wrapper reaches a connection's host: pre-built ssh arguments the host already resolved. */
|
|
23
|
-
export interface SshInvocation {
|
|
24
|
-
/** `user@host` or a `~/.ssh/config` alias. */
|
|
25
|
-
readonly destination: string;
|
|
26
|
-
/** Extra arguments (`-i`, `-o UserKnownHostsFile=…`, `-p`, jump options…), already credential-resolved. */
|
|
27
|
-
readonly args: readonly string[];
|
|
28
|
-
}
|
|
29
|
-
/** One remote root as the wrapper needs it. */
|
|
30
|
-
export interface ManifestRoot {
|
|
31
|
-
readonly connectionId: string;
|
|
32
|
-
/** Backend-absolute remote path (POSIX). */
|
|
33
|
-
readonly remoteRoot: string;
|
|
34
|
-
/** Local anchor directory that maps to this remote root. */
|
|
35
|
-
readonly anchorPath: string;
|
|
36
|
-
/** How to reach the host. */
|
|
37
|
-
readonly ssh: SshInvocation;
|
|
38
|
-
}
|
|
39
|
-
/** The root manifest the host writes for the wrapper. */
|
|
40
|
-
export interface RootManifest {
|
|
41
|
-
/** Manifest format version; a wrapper that does not know the version falls back to local. */
|
|
42
|
-
readonly version: number;
|
|
43
|
-
readonly roots: readonly ManifestRoot[];
|
|
44
|
-
}
|
|
45
|
-
/** One session's recorded remote target. */
|
|
46
|
-
export interface SessionState {
|
|
47
|
-
readonly sessionId: string;
|
|
48
|
-
readonly connectionId: string;
|
|
49
|
-
readonly remoteRoot: string;
|
|
50
|
-
readonly ssh: SshInvocation;
|
|
51
|
-
/** Epoch milliseconds; the wrapper ignores entries older than {@link SESSION_STATE_MAX_AGE_MS}. */
|
|
52
|
-
readonly recordedAt: number;
|
|
53
|
-
}
|
|
54
|
-
/** Current manifest version. */
|
|
55
|
-
export declare const MANIFEST_VERSION = 1;
|
|
56
|
-
/** A session state older than this is ignored (a stale file must not send the user to the wrong host). */
|
|
57
|
-
export declare const SESSION_STATE_MAX_AGE_MS: number;
|
|
58
|
-
/** The decision the wrapper acts on. */
|
|
59
|
-
export type TerminalDecision = {
|
|
60
|
-
readonly kind: 'remote';
|
|
61
|
-
readonly connectionId: string;
|
|
62
|
-
readonly remoteRoot: string;
|
|
63
|
-
/** Full argv to spawn: `[ssh, -tt, …args, destination, --, command]`. */
|
|
64
|
-
readonly argv: readonly string[];
|
|
65
|
-
/** Remote command: `cd <root> && exec <login shell> -l`. */
|
|
66
|
-
readonly remoteCommand: string;
|
|
67
|
-
/** Why this decision was taken (for diagnostics and tests). */
|
|
68
|
-
readonly reason: 'session' | 'anchor';
|
|
69
|
-
} | {
|
|
70
|
-
readonly kind: 'local';
|
|
71
|
-
/** Full argv to spawn: the local login shell. */
|
|
72
|
-
readonly argv: readonly string[];
|
|
73
|
-
readonly reason: 'no-target' | 'state-unusable' | 'manifest-unusable';
|
|
74
|
-
};
|
|
75
|
-
/** Options for {@link decideTerminal}. */
|
|
76
|
-
export interface DecideTerminalOptions {
|
|
77
|
-
/** Working directory the terminal was launched in. */
|
|
78
|
-
readonly cwd: string;
|
|
79
|
-
/** Manifest, when readable and valid. */
|
|
80
|
-
readonly manifest?: RootManifest;
|
|
81
|
-
/** This session's state, when readable and valid. */
|
|
82
|
-
readonly sessionState?: SessionState;
|
|
83
|
-
/** Local login shell to fall back to (platform default resolved by the caller). */
|
|
84
|
-
readonly localShell: string;
|
|
85
|
-
/** Remote login shell; `$SHELL` expands remotely, which is the right default. */
|
|
86
|
-
readonly remoteShell?: string;
|
|
87
|
-
/** Clock, injected for the staleness check. */
|
|
88
|
-
readonly now?: number;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Quote one word for a POSIX shell.
|
|
92
|
-
*
|
|
93
|
-
* Re-exported from {@link ../shell.ts}: this module builds `cd … && exec …`, which is a remote shell command,
|
|
94
|
-
* and the shared implementation is the one that refuses NUL bytes.
|
|
95
|
-
*/
|
|
96
|
-
export { posixQuote };
|
|
97
|
-
/**
|
|
98
|
-
* Parse a root manifest.
|
|
99
|
-
*
|
|
100
|
-
* @param raw - file contents.
|
|
101
|
-
* @returns the manifest, or `undefined` when it is unusable (wrong version, malformed, empty).
|
|
102
|
-
*/
|
|
103
|
-
export declare function parseRootManifest(raw: string): RootManifest | undefined;
|
|
104
|
-
/**
|
|
105
|
-
* Parse one session state file.
|
|
106
|
-
*
|
|
107
|
-
* @param raw - file contents.
|
|
108
|
-
* @returns the state, or `undefined` when malformed.
|
|
109
|
-
*/
|
|
110
|
-
export declare function parseSessionState(raw: string): SessionState | undefined;
|
|
111
|
-
/** Serialize a manifest for the wrapper (stable key order, so the file diff is readable). */
|
|
112
|
-
export declare function serializeRootManifest(manifest: RootManifest): string;
|
|
113
|
-
/** Serialize one session state. */
|
|
114
|
-
export declare function serializeSessionState(state: SessionState): string;
|
|
115
|
-
/** Build the command the remote shell runs: enter the workspace and be a login shell. */
|
|
116
|
-
export declare function remoteCommandFor(remoteRoot: string, remoteShell?: string): string;
|
|
117
|
-
/**
|
|
118
|
-
* Build the ssh argv for one remote target.
|
|
119
|
-
*
|
|
120
|
-
* `-tt` is what makes an interactive terminal work through the wrapper (the child needs a TTY on both
|
|
121
|
-
* sides). `--` ends option parsing before the destination, and the remote command is a single argument.
|
|
122
|
-
*
|
|
123
|
-
* @param ssh - resolved destination and credential arguments.
|
|
124
|
-
* @param remoteCommand - command to run remotely.
|
|
125
|
-
* @returns the argv to spawn.
|
|
126
|
-
*/
|
|
127
|
-
export declare function sshArgvFor(ssh: SshInvocation, remoteCommand: string): readonly string[];
|
|
128
|
-
/**
|
|
129
|
-
* Decide which terminal to open.
|
|
130
|
-
*
|
|
131
|
-
* @param options - cwd, manifest, session state, local shell, clock.
|
|
132
|
-
* @returns the decision, always total (never throws): an unusable input degrades to the local shell, with
|
|
133
|
-
* `reason` naming why.
|
|
134
|
-
*/
|
|
135
|
-
export declare function decideTerminal(options: DecideTerminalOptions): TerminalDecision;
|
|
136
|
-
//# sourceMappingURL=terminal-target.d.ts.map
|
|
@@ -1,330 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Session administration: inventory, hierarchy classification, delete plans and restore plans — pure logic only.
|
|
3
|
-
*
|
|
4
|
-
* Requirement FR-3.3–3.10 asks for a *session* recycle bin, which is a different thing from the existing
|
|
5
|
-
* *file-level* recycle bin in [`recycle-bin.ts`](./recycle-bin.ts) (see ADR-0009): that one moves one path
|
|
6
|
-
* inside a root, this one moves a whole session directory out of the platform's session store.
|
|
7
|
-
*
|
|
8
|
-
* ## The two facts everything here is built on
|
|
9
|
-
*
|
|
10
|
-
* 1. **`projectKey` is lossy.** The platform folds separators and truncates at 251 chars, so a directory
|
|
11
|
-
* name **cannot** be decoded back into a `cwd`. Every classification therefore reads the session
|
|
12
|
-
* **header**, never the directory name. (The encoder is reproduced in {@link projectKey} and was checked
|
|
13
|
-
* against the real store 6/6 — see ADR-0009.)
|
|
14
|
-
* 2. **A bin entry has two directory names.** `segment` is where it lives *inside the bin*; `originalSegment` A top-level session id carries a `session-` prefix while a
|
|
15
|
-
* sub-session id is a bare uuid, but the requirement is explicit that this must **not** be used to infer
|
|
16
|
-
* hierarchy: `origin`/`parentSession` from the header is the only authority.
|
|
17
|
-
*
|
|
18
|
-
* Everything in this module is a pure function over already-read data: no filesystem, no clock, no I/O.
|
|
19
|
-
*
|
|
20
|
-
* @module dsh-remote-workspace/domain/session-admin
|
|
21
|
-
*/
|
|
22
|
-
/**
|
|
23
|
-
* Reproduce the platform's `projectKey(cwd)` (source: `dsh-session-persistence-jsonl` `projectKey`).
|
|
24
|
-
*
|
|
25
|
-
* Rules: runs of `/`, `\` and `:` collapse to one `-`; `[A-Za-z0-9._-]` (except `~`) stay; everything else
|
|
26
|
-
* becomes `~XXXX` (4 uppercase hex digits of the UTF-16 code unit); leading `-` are dropped; empty becomes
|
|
27
|
-
* `root`; the body is truncated to 251 chars; the result is wrapped in `--…--`.
|
|
28
|
-
*
|
|
29
|
-
* **Lossy on purpose** (the platform says so too): two different `cwd` values can share one key. This
|
|
30
|
-
* function exists so the plugin can *compute* the directory a session lives in, never to decode one.
|
|
31
|
-
*
|
|
32
|
-
* @param cwd - the session's project directory (an absolute path).
|
|
33
|
-
* @returns the project directory name.
|
|
34
|
-
* @throws SshDevError `ROOT_INVALID` for an empty path (the platform refuses it too).
|
|
35
|
-
*/
|
|
36
|
-
export declare function projectKey(cwd: string): string;
|
|
37
|
-
/**
|
|
38
|
-
* Reproduce the platform's `encodeSegment(id)` (source: `dsh-session-persistence-jsonl`).
|
|
39
|
-
*
|
|
40
|
-
* Unlike {@link projectKey} this one is **injective**: `.` becomes `~002E` (so `.`/`..` cannot escape the
|
|
41
|
-
* directory), safe characters pass through, everything else becomes `~XXXX`. A `session-` prefix is
|
|
42
|
-
* therefore preserved verbatim — which is exactly why hierarchy must not be read from it.
|
|
43
|
-
*
|
|
44
|
-
* @param id - the session id.
|
|
45
|
-
* @returns the directory name.
|
|
46
|
-
*/
|
|
47
|
-
export declare function encodeSegment(id: string): string;
|
|
48
|
-
/**
|
|
49
|
-
* The directory one session lives in, relative to the sessions root.
|
|
50
|
-
*
|
|
51
|
-
* `cwd === undefined` selects the platform's `_no-cwd` bucket (the platform's own convention).
|
|
52
|
-
*
|
|
53
|
-
* @param cwd - the session's project directory, or `undefined` for the no-cwd bucket.
|
|
54
|
-
* @param id - the session id.
|
|
55
|
-
* @returns the path segments, root-relative.
|
|
56
|
-
*/
|
|
57
|
-
export declare function sessionSegments(cwd: string | undefined, id: string): readonly string[];
|
|
58
|
-
/**
|
|
59
|
-
* Classify one inventory record.
|
|
60
|
-
*
|
|
61
|
-
* The only authority is the header: `parentSessionId` present ⇒ sub-session. A record whose header could not
|
|
62
|
-
* be read is `unreadable`. The directory name is deliberately **not** consulted — `session-`-prefixed ids are
|
|
63
|
-
* top-level sessions, but the requirement forbids inferring hierarchy from that, and a future id scheme would
|
|
64
|
-
* silently break such an inference.
|
|
65
|
-
*
|
|
66
|
-
* @param record - a record without its `classification` field.
|
|
67
|
-
* @returns the class.
|
|
68
|
-
*/
|
|
69
|
-
export declare function classifySession(record: Omit<SessionRecord, 'classification'> & {
|
|
70
|
-
readonly readable?: boolean;
|
|
71
|
-
}): SessionClass;
|
|
72
|
-
/** How one session relates to the rest of the store. */
|
|
73
|
-
export type SessionClass =
|
|
74
|
-
/** A normal, independently-visible session. */
|
|
75
|
-
'normal'
|
|
76
|
-
/** A sub-session: its lifecycle follows its parent, so it must not be deleted on its own. */
|
|
77
|
-
| 'subsession'
|
|
78
|
-
/** A directory whose header could not be read (corrupt or unsupported format). */
|
|
79
|
-
| 'unreadable';
|
|
80
|
-
/** One session as the inventory sees it. */
|
|
81
|
-
export interface SessionRecord {
|
|
82
|
-
readonly id: string;
|
|
83
|
-
/** Project directory the log was found under, as spelled on disk. */
|
|
84
|
-
readonly projectDir: string;
|
|
85
|
-
/** Directory name (`encodeSegment(id)`) the log was found under. */
|
|
86
|
-
readonly segment: string;
|
|
87
|
-
/** `cwd` from the header; `undefined` for the no-cwd bucket. */
|
|
88
|
-
readonly cwd: string | undefined;
|
|
89
|
-
/** The session's own title (the platform's `title` projection), when one was ever generated. */
|
|
90
|
-
readonly title: string | undefined;
|
|
91
|
-
/**
|
|
92
|
-
* Display title of the workspace owning {@link cwd}, resolved from the platform's workspace registry.
|
|
93
|
-
*
|
|
94
|
-
* `undefined` when the caller supplied no resolver or no registered workspace owns the directory. The
|
|
95
|
-
* directory itself is still on the wire (`cwd`), so a reader falls back to its own leaf name.
|
|
96
|
-
*/
|
|
97
|
-
readonly workspaceTitle: string | undefined;
|
|
98
|
-
/** Parent session id from the header, when this is a sub-session. */
|
|
99
|
-
readonly parentSessionId: string | undefined;
|
|
100
|
-
/** Total bytes of the log files in the session directory (0 when unknown). */
|
|
101
|
-
readonly bytes: number;
|
|
102
|
-
/** Modification time of the newest log file, in ms since the epoch. */
|
|
103
|
-
readonly updatedAtMs: number;
|
|
104
|
-
/** True when this session is currently held by a live agent (never deletable). */
|
|
105
|
-
readonly live: boolean;
|
|
106
|
-
/** How this session relates to the store (see {@link classifySession}). */
|
|
107
|
-
readonly classification: SessionClass;
|
|
108
|
-
}
|
|
109
|
-
/** Why one session may not be deleted. */
|
|
110
|
-
export type DeleteRefusal =
|
|
111
|
-
/** A live agent holds it: the platform has no public way to unload one (see FR-3.6). */
|
|
112
|
-
'live'
|
|
113
|
-
/** It is a sub-session: delete its parent instead. */
|
|
114
|
-
| 'subsession'
|
|
115
|
-
/** Its log could not be read, so deleting it would be guesswork. */
|
|
116
|
-
| 'unreadable'
|
|
117
|
-
/** The caller asked to keep a session that this operation would have removed. */
|
|
118
|
-
| 'protected';
|
|
119
|
-
/** The pure decision for one deletion. */
|
|
120
|
-
export interface DeleteDecision {
|
|
121
|
-
/** Ids to delete, **children before parents** (so a parent never outlives its children). */
|
|
122
|
-
readonly delete: readonly string[];
|
|
123
|
-
/** One refusal per skipped id, in the order the ids were considered. */
|
|
124
|
-
readonly refusals: readonly {
|
|
125
|
-
readonly id: string;
|
|
126
|
-
readonly reason: DeleteRefusal;
|
|
127
|
-
}[];
|
|
128
|
-
/** Total bytes the deletion will free, counting only the ids in `delete`. */
|
|
129
|
-
readonly bytes: number;
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Decide what a deletion request would actually do, without touching anything.
|
|
133
|
-
*
|
|
134
|
-
* The rules, in order:
|
|
135
|
-
*
|
|
136
|
-
* 1. an id that is not in the inventory is refused as `unreadable` (it was never a session here);
|
|
137
|
-
* 2. a **live** session is refused (`live`) — FR-3.6 requires blocking rather than killing, because the
|
|
138
|
-
* platform exposes no way to unload a live agent, and the requirement is explicit that
|
|
139
|
-
* "stop and delete" is *not* offered this round;
|
|
140
|
-
* 3. an `unreadable` record is refused (`unreadable`) — deleting a directory we could not identify is
|
|
141
|
-
* exactly the kind of irreversible guess this feature must not make;
|
|
142
|
-
* 4. a sub-session is refused (`subsession`) when it is selected **on its own** (FR-3.3: 子会话随父会话管理,
|
|
143
|
-
* 默认不可被单独删除);
|
|
144
|
-
* 5. selecting a parent **always** takes its whole descendant subtree along, ordered **children first**.
|
|
145
|
-
*
|
|
146
|
-
* Rule 5 is unconditional on purpose. It used to be an opt-in `includeSubsessions` switch, and that was the
|
|
147
|
-
* wrong shape: leaving a parent's children behind orphans their logs — the parent is gone, so nothing
|
|
148
|
-
* navigates to them, and the platform's listing hides them by `parentId` nesting, so the bytes stay on disk
|
|
149
|
-
* and invisible forever. FR-3.3 asks for sub-sessions to be "随父会话管理" and the requirement's own §7
|
|
150
|
-
* recommendation is "删除父会话时提示将一并处理 N 个子会话": always together, with the count shown.
|
|
151
|
-
*
|
|
152
|
-
* A sub-session that was explicitly requested *and* pulled in by its parent is deleted and **not** reported
|
|
153
|
-
* as a refusal — otherwise the caller would see a row refused and deleted in the same answer.
|
|
154
|
-
*
|
|
155
|
-
* **A subtree is atomic.** If any descendant of a requested session is live or protected, the whole request
|
|
156
|
-
* for that session is refused instead of deleting the ancestor and orphaning the descendant: a live child
|
|
157
|
-
* whose parent is gone can never be reached again (the listing nests children under their parent), and a
|
|
158
|
-
* protected one was explicitly asked to be spared. Refusing the parent is the only outcome that keeps both
|
|
159
|
-
* promises (FR-3.6 never kills a live agent; `keep` is honoured).
|
|
160
|
-
*
|
|
161
|
-
* `keep` wins over everything: an id listed there is refused as `protected` even if it is a live-free
|
|
162
|
-
* normal session. It exists so a UI can show a preview and let the user uncheck rows.
|
|
163
|
-
*
|
|
164
|
-
* @param records - the full inventory.
|
|
165
|
-
* @param request - ids to delete, plus the optional keep list.
|
|
166
|
-
* @returns the decision; `delete` is empty when everything was refused.
|
|
167
|
-
*/
|
|
168
|
-
export declare function planDelete(records: readonly SessionRecord[], request: {
|
|
169
|
-
readonly ids: readonly string[];
|
|
170
|
-
readonly keep?: readonly string[];
|
|
171
|
-
}): DeleteDecision;
|
|
172
|
-
/** Index file name inside the session bin root. */
|
|
173
|
-
export declare const SESSION_TRASH_INDEX_FILE = "index.json";
|
|
174
|
-
/** Version stamped into the session bin index; a different version is a hard parse failure. */
|
|
175
|
-
export declare const SESSION_TRASH_INDEX_VERSION = 1;
|
|
176
|
-
/** Opaque identity of one session-bin entry. */
|
|
177
|
-
export type SessionTrashEntryId = string & {
|
|
178
|
-
readonly __sessionTrashEntryId: 'SessionTrashEntryId';
|
|
179
|
-
};
|
|
180
|
-
/** One entry in the session recycle bin: everything a restore needs. */
|
|
181
|
-
export interface SessionTrashEntry {
|
|
182
|
-
readonly id: SessionTrashEntryId;
|
|
183
|
-
/** The deleted session's id. */
|
|
184
|
-
readonly sessionId: string;
|
|
185
|
-
/**
|
|
186
|
-
* The session's `cwd` as recorded in its header, or `undefined` for the no-cwd bucket.
|
|
187
|
-
*
|
|
188
|
-
* Kept **verbatim** rather than as a `projectKey`: the key is lossy, so restoring from it could land the
|
|
189
|
-
* session under a different project directory than it came from.
|
|
190
|
-
*/
|
|
191
|
-
readonly cwd: string | undefined;
|
|
192
|
-
/** Project directory name the session was moved out of. */
|
|
193
|
-
readonly projectDir: string;
|
|
194
|
-
/**
|
|
195
|
-
* Directory name **inside the bin**; the session directory is moved here wholesale.
|
|
196
|
-
*
|
|
197
|
-
* Equals the entry id in practice, and is **not** the store-side name — see `originalSegment`. Keeping the
|
|
198
|
-
* two apart is what makes a restore land back where it came from: using one for both would make the
|
|
199
|
-
* restored session appear under the entry id.
|
|
200
|
-
*/
|
|
201
|
-
readonly segment: string;
|
|
202
|
-
/**
|
|
203
|
-
* The session's directory name **in the store** (`encodeSegment(sessionId)`), captured before the move.
|
|
204
|
-
*
|
|
205
|
-
* Stored rather than recomputed: `encodeSegment` is injective so recomputing would work today, but recording
|
|
206
|
-
* what was actually observed means a restore never depends on the encoder staying byte-identical.
|
|
207
|
-
*/
|
|
208
|
-
readonly originalSegment: string;
|
|
209
|
-
readonly title: string | undefined;
|
|
210
|
-
/** Total bytes of the moved log files, for the retention policy. */
|
|
211
|
-
readonly bytes: number;
|
|
212
|
-
readonly deletedAtMs: number;
|
|
213
|
-
/** How the bytes were moved. `copy` means the move crossed a filesystem boundary. */
|
|
214
|
-
readonly movedBy: 'rename' | 'copy';
|
|
215
|
-
/** Who confirmed the deletion, when the caller supplied it (FR-3.10 auditability). */
|
|
216
|
-
readonly confirmedBy: string | undefined;
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* Derive a stable, filesystem-safe entry id.
|
|
220
|
-
*
|
|
221
|
-
* Deterministic on `(sessionId, deletedAtMs)` so a retried deletion of the same session at the same instant
|
|
222
|
-
* lands on the same entry instead of leaving two copies; {@link allocateSessionTrashEntryId} breaks genuine
|
|
223
|
-
* collisions.
|
|
224
|
-
*
|
|
225
|
-
* @param input - identity inputs.
|
|
226
|
-
* @returns the entry id.
|
|
227
|
-
*/
|
|
228
|
-
export declare function makeSessionTrashEntryId(input: {
|
|
229
|
-
readonly sessionId: string;
|
|
230
|
-
readonly deletedAtMs: number;
|
|
231
|
-
}): SessionTrashEntryId;
|
|
232
|
-
/**
|
|
233
|
-
* Allocate an id that is not already taken, by appending a counter.
|
|
234
|
-
*
|
|
235
|
-
* @param existing - ids already in the bin.
|
|
236
|
-
* @param input - identity inputs.
|
|
237
|
-
* @returns a free id.
|
|
238
|
-
* @throws SshDevError `TRASH_NOT_FOUND` when the counter space is exhausted (never returns a taken id).
|
|
239
|
-
*/
|
|
240
|
-
export declare function allocateSessionTrashEntryId(existing: Iterable<string>, input: {
|
|
241
|
-
readonly sessionId: string;
|
|
242
|
-
readonly deletedAtMs: number;
|
|
243
|
-
}): SessionTrashEntryId;
|
|
244
|
-
/** The persisted session-bin index document. */
|
|
245
|
-
export interface SessionTrashIndex {
|
|
246
|
-
readonly version: number;
|
|
247
|
-
readonly entries: readonly SessionTrashEntry[];
|
|
248
|
-
}
|
|
249
|
-
/** One index record the parser refused. */
|
|
250
|
-
export interface SkippedSessionTrashRecord {
|
|
251
|
-
readonly index: number;
|
|
252
|
-
readonly reason: string;
|
|
253
|
-
}
|
|
254
|
-
/** Result of {@link parseSessionTrashIndex}. */
|
|
255
|
-
export interface ParsedSessionTrashIndex {
|
|
256
|
-
readonly entries: readonly SessionTrashEntry[];
|
|
257
|
-
readonly skipped: readonly SkippedSessionTrashRecord[];
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Serialize the session-bin index.
|
|
261
|
-
*
|
|
262
|
-
* Entries are sorted by id so the same set is byte-identical every time (diffable, testable).
|
|
263
|
-
*
|
|
264
|
-
* @param entries - entries to persist.
|
|
265
|
-
* @returns JSON text.
|
|
266
|
-
*/
|
|
267
|
-
export declare function serializeSessionTrashIndex(entries: readonly SessionTrashEntry[]): string;
|
|
268
|
-
/**
|
|
269
|
-
* Parse the session-bin index.
|
|
270
|
-
*
|
|
271
|
-
* A malformed document or unknown version is a **hard failure** (the bin cannot be trusted, and guessing
|
|
272
|
-
* could delete the wrong directory); a single unusable record is skipped and reported, so one corrupt entry
|
|
273
|
-
* never hides the rest.
|
|
274
|
-
*
|
|
275
|
-
* @param text - index file content.
|
|
276
|
-
* @returns usable entries plus skipped records.
|
|
277
|
-
* @throws SshDevError `TRASH_NOT_FOUND` when the document cannot be read as an index.
|
|
278
|
-
*/
|
|
279
|
-
export declare function parseSessionTrashIndex(text: string): ParsedSessionTrashIndex;
|
|
280
|
-
/** The pure decision for restoring one entry. */
|
|
281
|
-
export type RestoreSessionPlan = {
|
|
282
|
-
readonly kind: 'restore';
|
|
283
|
-
readonly entryId: SessionTrashEntryId;
|
|
284
|
-
readonly sessionId: string;
|
|
285
|
-
readonly projectDir: string;
|
|
286
|
-
/** Where it goes back to in the store (the original directory name). */
|
|
287
|
-
readonly originalSegment: string;
|
|
288
|
-
} | {
|
|
289
|
-
readonly kind: 'refuse';
|
|
290
|
-
readonly reason: 'occupied';
|
|
291
|
-
readonly entryId: SessionTrashEntryId;
|
|
292
|
-
readonly suggestedId: string;
|
|
293
|
-
};
|
|
294
|
-
/**
|
|
295
|
-
* Decide how to restore one entry.
|
|
296
|
-
*
|
|
297
|
-
* Restoring onto an occupied path is **refused** with a suggestion rather than silently restored under a
|
|
298
|
-
* different name: the platform keys a session by `(id, cwd)`, so a renamed directory would either be
|
|
299
|
-
* invisible or (worse) look like a second copy of a session that still exists.
|
|
300
|
-
*
|
|
301
|
-
* @param entry - the bin entry.
|
|
302
|
-
* @param options - whether the original path is currently occupied.
|
|
303
|
-
* @returns the plan.
|
|
304
|
-
*/
|
|
305
|
-
export declare function planRestoreSession(entry: SessionTrashEntry, options: {
|
|
306
|
-
readonly occupied: boolean;
|
|
307
|
-
}): RestoreSessionPlan;
|
|
308
|
-
/**
|
|
309
|
-
* Build one entry from a deletion that has already been performed.
|
|
310
|
-
*
|
|
311
|
-
* Pure: the caller has moved the bytes and passes the observed facts. Keeping this separate from the move
|
|
312
|
-
* means the recorded `bytes`/`movedBy` are what actually happened, not what was planned.
|
|
313
|
-
*
|
|
314
|
-
* @param input - observed facts.
|
|
315
|
-
* @returns the entry.
|
|
316
|
-
*/
|
|
317
|
-
export declare function makeSessionTrashEntry(input: {
|
|
318
|
-
readonly id: SessionTrashEntryId;
|
|
319
|
-
readonly sessionId: string;
|
|
320
|
-
readonly cwd: string | undefined;
|
|
321
|
-
readonly projectDir: string;
|
|
322
|
-
readonly segment: string;
|
|
323
|
-
readonly originalSegment: string;
|
|
324
|
-
readonly title: string | undefined;
|
|
325
|
-
readonly bytes: number;
|
|
326
|
-
readonly deletedAtMs: number;
|
|
327
|
-
readonly movedBy: 'rename' | 'copy';
|
|
328
|
-
readonly confirmedBy?: string | undefined;
|
|
329
|
-
}): SessionTrashEntry;
|
|
330
|
-
//# sourceMappingURL=session-admin.d.ts.map
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* POSIX shell quoting — the **one** implementation.
|
|
3
|
-
*
|
|
4
|
-
* ## Why this module exists
|
|
5
|
-
*
|
|
6
|
-
* The same six-line function had been written three times: here in the domain (for `grep`/shell command
|
|
7
|
-
* construction), in `infra/transport/ssh/shell-quote.ts` (for the ssh transports), and again in
|
|
8
|
-
* `domain/session/terminal-target.ts` (for the terminal wrapper's `cd … && exec …`). Two of the three had
|
|
9
|
-
* **no NUL guard**, and both of those build remote shell commands — a path carrying a NUL byte would have
|
|
10
|
-
* truncated the command instead of being refused.
|
|
11
|
-
*
|
|
12
|
-
* Three copies of a security-relevant function is not a style issue: it means a fix applied to one of them
|
|
13
|
-
* silently does not apply to the other two. `doc/06-audits/log.md` (2026-11-01) records the finding.
|
|
14
|
-
*
|
|
15
|
-
* The domain owns it because infra may depend on domain, never the reverse (red line #2), and because this
|
|
16
|
-
* module is pure — no `node:` imports, no platform types.
|
|
17
|
-
*
|
|
18
|
-
* @module dsh-over-ssh/domain/shell
|
|
19
|
-
*/
|
|
20
|
-
/**
|
|
21
|
-
* Quote one value for a POSIX shell so the remote shell cannot interpret it.
|
|
22
|
-
*
|
|
23
|
-
* Wraps in single quotes and rewrites an embedded `'` as `'\''` (close, escaped quote, reopen). Everything
|
|
24
|
-
* else — `$`, backticks, `*`, `;`, newlines — is inert inside single quotes, which is why this is preferred
|
|
25
|
-
* over escaping: there is no character set to get wrong.
|
|
26
|
-
*
|
|
27
|
-
* @param value - the raw value.
|
|
28
|
-
* @returns a shell-safe literal, including the surrounding quotes.
|
|
29
|
-
* @throws SshDevError when the value contains a NUL byte.
|
|
30
|
-
*
|
|
31
|
-
* @example
|
|
32
|
-
* ```ts
|
|
33
|
-
* quote('/srv/a b'); // "'/srv/a b'"
|
|
34
|
-
* quote("/it's"); // "'/it'\\''s'"
|
|
35
|
-
* quote('a\u0000b'); // throws
|
|
36
|
-
* ```
|
|
37
|
-
*/
|
|
38
|
-
export declare function quote(value: string): string;
|
|
39
|
-
/**
|
|
40
|
-
* Quote several values and join them with spaces.
|
|
41
|
-
*
|
|
42
|
-
* @param values - raw values.
|
|
43
|
-
* @returns a single shell-safe word sequence.
|
|
44
|
-
* @throws SshDevError when any value contains a NUL byte.
|
|
45
|
-
*/
|
|
46
|
-
export declare function quoteAll(values: readonly string[]): string;
|
|
47
|
-
//# sourceMappingURL=shell.d.ts.map
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `~/.ssh/config` reader used by the "import aliases" flow.
|
|
3
|
-
*
|
|
4
|
-
* Only the keys this plugin can represent are read. OpenSSH keeps the first obtained value
|
|
5
|
-
* for a key inside one Host block, so this parser does the same; `Host` blocks accumulate
|
|
6
|
-
* their patterns and later blocks shadow earlier ones for the same literal alias.
|
|
7
|
-
*
|
|
8
|
-
* @module dsh-remote-workspace/domain/ssh-config
|
|
9
|
-
*/
|
|
10
|
-
/** One parsed `Host` block. */
|
|
11
|
-
export interface SshConfigEntry {
|
|
12
|
-
/** Patterns on the `Host` line, in order. */
|
|
13
|
-
readonly patterns: readonly string[];
|
|
14
|
-
/** `HostName` value when present. */
|
|
15
|
-
readonly hostName?: string;
|
|
16
|
-
/** `User` value when present. */
|
|
17
|
-
readonly user?: string;
|
|
18
|
-
/** `Port` value when present and numeric. */
|
|
19
|
-
readonly port?: number;
|
|
20
|
-
/** All `IdentityFile` values, in order. */
|
|
21
|
-
readonly identityFiles: readonly string[];
|
|
22
|
-
/** `ProxyJump` value when present and not `none`. */
|
|
23
|
-
readonly proxyJump?: string;
|
|
24
|
-
/** `Include` patterns (this parser does not read included files). */
|
|
25
|
-
readonly includes: readonly string[];
|
|
26
|
-
/** 1-based line number of the `Host` line. */
|
|
27
|
-
readonly line: number;
|
|
28
|
-
}
|
|
29
|
-
/** A connection the import flow can turn into a profile. */
|
|
30
|
-
export interface SshConfigImportDraft {
|
|
31
|
-
/** The literal alias, also usable as the `ssh` target. */
|
|
32
|
-
readonly alias: string;
|
|
33
|
-
/** Resolved host name (falls back to the alias). */
|
|
34
|
-
readonly hostName: string;
|
|
35
|
-
readonly user?: string;
|
|
36
|
-
readonly port?: number;
|
|
37
|
-
readonly identityFile?: string;
|
|
38
|
-
readonly proxyJump?: string;
|
|
39
|
-
}
|
|
40
|
-
/** One alias the import flow refuses, with the reason shown to the user. */
|
|
41
|
-
export interface SkippedAlias {
|
|
42
|
-
readonly pattern: string;
|
|
43
|
-
readonly reason: string;
|
|
44
|
-
readonly line: number;
|
|
45
|
-
}
|
|
46
|
-
/** Result of selecting importable aliases. */
|
|
47
|
-
export interface SshConfigImportResult {
|
|
48
|
-
readonly drafts: readonly SshConfigImportDraft[];
|
|
49
|
-
readonly skipped: readonly SkippedAlias[];
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Parse the textual content of an OpenSSH client config.
|
|
53
|
-
*
|
|
54
|
-
* @param text - file content.
|
|
55
|
-
* @returns parsed Host blocks in file order.
|
|
56
|
-
*/
|
|
57
|
-
export declare function parseSshConfig(text: string): SshConfigEntry[];
|
|
58
|
-
/** Whether a Host pattern names one concrete host rather than a wildcard set. */
|
|
59
|
-
export declare function isConcretePattern(pattern: string): boolean;
|
|
60
|
-
/** Expand a leading `~` using the host home directory. */
|
|
61
|
-
export declare function expandHome(path: string, home: string): string;
|
|
62
|
-
/**
|
|
63
|
-
* Select the aliases the import flow will offer, and explain what it refuses.
|
|
64
|
-
*
|
|
65
|
-
* @param entries - parsed Host blocks.
|
|
66
|
-
* @param options - `home` expands `~` in identity paths; `excluded` drops known aliases.
|
|
67
|
-
* @returns importable drafts plus skipped patterns.
|
|
68
|
-
*/
|
|
69
|
-
export declare function importableEntries(entries: readonly SshConfigEntry[], options?: {
|
|
70
|
-
readonly home?: string;
|
|
71
|
-
readonly excluded?: readonly string[];
|
|
72
|
-
}): SshConfigImportResult;
|
|
73
|
-
//# sourceMappingURL=ssh-config.d.ts.map
|