@tiphareth/dsh-hardssh 0.1.2-alpha → 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.
Files changed (174) hide show
  1. package/LICENSE +30 -0
  2. package/README.md +49 -25
  3. package/SKILLS.md +140 -0
  4. package/lib/base/index.js +95 -0
  5. package/lib/client.js +1165 -1329
  6. package/lib/fs.js +307 -416
  7. package/lib/index.js +5428 -2991
  8. package/lib/ledger-B-LXlftp.js +43 -0
  9. package/lib/ledger-D2ezq1iW.js +376 -0
  10. package/lib/model-Cp7f70Mb.js +5 -0
  11. package/lib/registry-CViuzKYI.js +421 -0
  12. package/lib/subprocess.js +92 -549
  13. package/lib/types/backend.d.ts +73 -78
  14. package/lib/types/base/capability.d.ts +4 -0
  15. package/lib/types/base/ledger-router.d.ts +42 -26
  16. package/lib/types/base/ledger.d.ts +30 -19
  17. package/lib/types/base/model.d.ts +17 -8
  18. package/lib/types/base/plugin.d.ts +11 -0
  19. package/lib/types/base/registry.d.ts +28 -31
  20. package/lib/types/base/router.d.ts +0 -7
  21. package/lib/types/client/api.d.ts +8 -8
  22. package/lib/types/client/connect-host.d.ts +13 -0
  23. package/lib/types/client/index.d.ts +6 -4
  24. package/lib/types/client/locales.d.ts +15 -1
  25. package/lib/types/client/session-connect-gate.d.ts +71 -0
  26. package/lib/types/client/ssh/api.d.ts +11 -9
  27. package/lib/types/client/ssh/apply.d.ts +14 -8
  28. package/lib/types/client/ssh/locales.d.ts +8 -1
  29. package/lib/types/client/ssh/ops-tab.d.ts +34 -0
  30. package/lib/types/client/ssh/panel/ClusterTab.d.ts +3 -1
  31. package/lib/types/client/ssh/panel/ConnectionErrorDialog.d.ts +8 -0
  32. package/lib/types/client/ssh/panel/SessionSecretDialog.d.ts +4 -1
  33. package/lib/types/client/ssh/panel/SshPanel.d.ts +8 -8
  34. package/lib/types/client/ssh/panel/TerminalTab.d.ts +3 -5
  35. package/lib/types/client/ssh/panel/TransferTab.d.ts +5 -1
  36. package/lib/types/client/ssh/panel/TunnelsTab.d.ts +3 -1
  37. package/lib/types/client/ssh/session-target.d.ts +17 -0
  38. package/lib/types/client/state.d.ts +4 -1
  39. package/lib/types/client/workspace-panel-entry.d.ts +30 -0
  40. package/lib/types/client/workspace-panel.d.ts +24 -0
  41. package/lib/types/client-http.d.ts +77 -6
  42. package/lib/types/core.d.ts +5 -16
  43. package/lib/types/fs.d.ts +18 -25
  44. package/lib/types/index.d.ts +138 -9
  45. package/lib/types/ledger.d.ts +16 -111
  46. package/lib/types/protocol.d.ts +0 -5
  47. package/lib/types/providers/index.d.ts +6 -3
  48. package/lib/types/providers/local/provider.d.ts +83 -23
  49. package/lib/types/providers/ssh/provider.d.ts +24 -57
  50. package/lib/types/remote/environment.d.ts +2 -0
  51. package/lib/types/remote/remote-fs.d.ts +12 -1
  52. package/lib/types/remote/remote-process.d.ts +22 -1
  53. package/lib/types/remote/remote-subprocess.d.ts +13 -1
  54. package/lib/types/remote/remote-terminal.d.ts +4 -0
  55. package/lib/types/remote-search.d.ts +3 -3
  56. package/lib/types/routes.d.ts +36 -8
  57. package/lib/types/runtime/dsh-capabilities.d.ts +18 -0
  58. package/lib/types/runtime/workspace-core.d.ts +73 -38
  59. package/lib/types/runtime/workspace-migration.d.ts +113 -0
  60. package/lib/types/ssh/connection/lease.d.ts +23 -0
  61. package/lib/types/ssh/connection/manager.d.ts +359 -0
  62. package/lib/types/ssh/connection/pool.d.ts +8 -1
  63. package/lib/types/ssh/engine.d.ts +97 -267
  64. package/lib/types/ssh/known-hosts.d.ts +22 -5
  65. package/lib/types/ssh/local-transfer-policy.d.ts +20 -0
  66. package/lib/types/ssh/plugin.d.ts +10 -15
  67. package/lib/types/ssh/protocol.d.ts +18 -0
  68. package/lib/types/ssh/routes.d.ts +15 -7
  69. package/lib/types/ssh/sftp/service.d.ts +216 -0
  70. package/lib/types/ssh/store.d.ts +20 -8
  71. package/lib/types/ssh/terminal/service.d.ts +49 -0
  72. package/lib/types/ssh/tunnel/service.d.ts +26 -0
  73. package/lib/types/ssh/vault.d.ts +51 -4
  74. package/lib/types/subprocess.d.ts +13 -21
  75. package/lib/types/switch/switch-fs.d.ts +52 -2
  76. package/lib/types/switch/switch-subprocess.d.ts +32 -3
  77. package/lib/types/tools.d.ts +7 -6
  78. package/lib/types/workspace-tool-ops.d.ts +33 -0
  79. package/lib/types/workspace.d.ts +18 -0
  80. package/lib/vault-3gpWct2Q.js +559 -0
  81. package/lib/workspace.js +2 -0
  82. package/package.json +36 -11
  83. package/scripts/export-legacy-workspaces.mjs +136 -0
  84. package/src/backend.ts +155 -483
  85. package/src/base/capability.ts +4 -0
  86. package/src/base/ledger-router.ts +214 -65
  87. package/src/base/ledger.ts +175 -48
  88. package/src/base/model.ts +18 -8
  89. package/src/base/plugin.ts +15 -4
  90. package/src/base/registry.ts +49 -41
  91. package/src/base/router.ts +0 -8
  92. package/src/client/api.ts +8 -36
  93. package/src/client/connect-host.ts +199 -0
  94. package/src/client/directory-flow.tsx +91 -36
  95. package/src/client/index.ts +56 -53
  96. package/src/client/locales.ts +14 -0
  97. package/src/client/session-connect-gate.ts +163 -0
  98. package/src/client/ssh/api.ts +245 -108
  99. package/src/client/ssh/apply.ts +23 -24
  100. package/src/client/ssh/locales.ts +16 -2
  101. package/src/client/ssh/ops-tab.tsx +79 -0
  102. package/src/client/ssh/panel/ClusterTab.tsx +9 -20
  103. package/src/client/ssh/panel/ConnectionErrorDialog.tsx +33 -0
  104. package/src/client/ssh/panel/SessionSecretDialog.tsx +5 -1
  105. package/src/client/ssh/panel/SshPanel.tsx +81 -77
  106. package/src/client/ssh/panel/TerminalTab.tsx +18 -37
  107. package/src/client/ssh/panel/TransferTab.tsx +43 -33
  108. package/src/client/ssh/panel/TunnelsTab.tsx +9 -26
  109. package/src/client/ssh/panel/panel.module.css +90 -127
  110. package/src/client/ssh/session-target.ts +69 -0
  111. package/src/client/state.ts +9 -1
  112. package/src/client/workspace-badges.ts +99 -99
  113. package/src/client/workspace-panel-entry.tsx +91 -0
  114. package/src/client/workspace-panel.tsx +227 -0
  115. package/src/client/workspace.module.css +76 -2
  116. package/src/client-http.ts +118 -18
  117. package/src/core.ts +37 -47
  118. package/src/fs.ts +137 -85
  119. package/src/index.ts +535 -244
  120. package/src/ledger.ts +62 -416
  121. package/src/protocol.ts +0 -6
  122. package/src/providers/index.ts +8 -4
  123. package/src/providers/local/provider.ts +259 -87
  124. package/src/providers/ssh/provider.ts +158 -155
  125. package/src/remote/environment.ts +22 -1
  126. package/src/remote/remote-fs.ts +123 -34
  127. package/src/remote/remote-process.ts +265 -203
  128. package/src/remote/remote-subprocess.ts +61 -18
  129. package/src/remote/remote-terminal.ts +106 -21
  130. package/src/remote-search.ts +122 -26
  131. package/src/routes.ts +416 -395
  132. package/src/runtime/dsh-capabilities.ts +19 -0
  133. package/src/runtime/workspace-core.ts +171 -88
  134. package/src/runtime/workspace-migration.ts +472 -0
  135. package/src/ssh/connection/lease.ts +35 -0
  136. package/src/ssh/connection/manager.ts +1083 -0
  137. package/src/ssh/connection/pool.ts +341 -275
  138. package/src/ssh/engine.ts +269 -1477
  139. package/src/ssh/known-hosts.ts +42 -18
  140. package/src/ssh/local-transfer-policy.ts +83 -0
  141. package/src/ssh/plugin.ts +20 -19
  142. package/src/ssh/protocol.ts +22 -1
  143. package/src/ssh/routes.ts +419 -187
  144. package/src/ssh/sftp/service.ts +967 -0
  145. package/src/ssh/store.ts +169 -72
  146. package/src/ssh/terminal/service.ts +177 -0
  147. package/src/ssh/tools.ts +42 -20
  148. package/src/ssh/tunnel/service.ts +217 -0
  149. package/src/ssh/vault.ts +245 -91
  150. package/src/subprocess.ts +88 -71
  151. package/src/switch/switch-fs.ts +141 -10
  152. package/src/switch/switch-subprocess.ts +80 -6
  153. package/src/tools.ts +217 -221
  154. package/src/workspace-tool-ops.ts +101 -0
  155. package/src/workspace.ts +52 -0
  156. package/lib/environment-BL1jddfB.js +0 -449
  157. package/lib/switch-fs-CAJpFY9C.js +0 -193
  158. package/lib/switch-fs-RrZtG2gv.js +0 -210
  159. package/lib/types/client/manager-button.d.ts +0 -32
  160. package/lib/types/client/ssh/mount.d.ts +0 -13
  161. package/lib/types/client/ssh/panel/HostsTab.d.ts +0 -10
  162. package/lib/types/client/ssh/panel/controller.d.ts +0 -23
  163. package/lib/types/client/ssh/sidebar-entry.d.ts +0 -25
  164. package/lib/types/client/workspace-gate.d.ts +0 -15
  165. package/lib/types/remote-runner.d.ts +0 -83
  166. package/lib/types/seam-state.d.ts +0 -69
  167. package/src/client/manager-button.tsx +0 -269
  168. package/src/client/ssh/mount.tsx +0 -83
  169. package/src/client/ssh/panel/HostsTab.tsx +0 -236
  170. package/src/client/ssh/panel/controller.ts +0 -48
  171. package/src/client/ssh/sidebar-entry.ts +0 -123
  172. package/src/client/workspace-gate.ts +0 -232
  173. package/src/remote-runner.ts +0 -201
  174. package/src/seam-state.ts +0 -185
@@ -1,210 +0,0 @@
1
- import { FileSystem } from "@deepseek-ai/dsh-fs";
2
- //#region src/switch/switch-fs.ts
3
- /**
4
- * The `ctx.fs` switching facade: routes every filesystem call to the backend
5
- * of the SESSION's workspace — a local session operates on the local sandbox,
6
- * an SSH-bound workspace on the remote host the workspace is bound to.
7
- *
8
- * Routing anchor: the cwd the tool call was made under (`resolve`'s
9
- * `opts.cwd`, the write/edit `sandboxPolicy.workspaceRoot`). The resolver
10
- * ("which SSH workspace owns this cwd?") lives in the deps; a hit routes to
11
- * that workspace's remote backend, a miss to the local backend. Later
12
- * operations (stat/read/write) receive only the resolved `FsTarget`, so the
13
- * backend identity is encoded into the target key — `ssh:<recordId>:<remoteKey>`
14
- * for remote targets, bare local keys for local ones — and decoded on every
15
- * dispatch. This keeps two SSH workspaces on DIFFERENT hosts (or different
16
- * directories on the SAME host) from colliding, and never misroutes a local
17
- * target.
18
- *
19
- * @module dsh-hardssh/switch-fs
20
- */
21
- /** Key namespace: 'ssh:<recordId>:' (legacy) or 'wfs://<id>/' (current)
22
- * for remote/workspace-bound targets. Both decode; new keys emit wfs://. */
23
- const REMOTE_PREFIX = "ssh:";
24
- /** Strict WFS key prefix (includes the double slash + separator). */
25
- const WFS_NAMESPACE_MARKER = `wfs://`;
26
- /** The routing filesystem facade. */
27
- var SwitchFileSystem = class extends FileSystem {
28
- deps;
29
- local;
30
- constructor(ctx, deps) {
31
- super(ctx);
32
- this.deps = deps;
33
- this.local = deps.local;
34
- }
35
- /** Sandbox default from the local backend (remote worlds are unconfined —
36
- * the docs/doc of the tool layer reads this once at mount). */
37
- get sandboxMode() {
38
- return this.local.sandboxMode;
39
- }
40
- /** Encode a raw key into the world's namespace. */
41
- encode(rawKey, namespace) {
42
- return namespace === "" ? rawKey : `${namespace}${rawKey}`;
43
- }
44
- /** Decode a namespaced key back to (rawKey, world). Supports both the
45
- * current `wfs://<id>/…` form and the legacy `ssh:<recordId>:…` form. */
46
- decode(key) {
47
- if (key.startsWith(WFS_NAMESPACE_MARKER)) {
48
- const rest = key.slice(WFS_NAMESPACE_MARKER.length);
49
- const end = rest.indexOf("/");
50
- if (end > 0) {
51
- const namespace = `wfs://${rest.slice(0, end + 1)}`;
52
- const world = this.deps.worldForNamespace(namespace);
53
- if (world !== void 0) return {
54
- rawKey: rest.slice(end + 1),
55
- world
56
- };
57
- }
58
- return;
59
- }
60
- if (key.startsWith("ssh:")) {
61
- const end = key.indexOf(":", 4);
62
- if (end > 0) {
63
- const namespace = key.slice(0, end + 1);
64
- const world = this.deps.worldForNamespace(namespace);
65
- if (world !== void 0) return {
66
- rawKey: key.slice(namespace.length),
67
- world
68
- };
69
- }
70
- return;
71
- }
72
- return {
73
- rawKey: key,
74
- world: {
75
- backend: this.local,
76
- namespace: ""
77
- }
78
- };
79
- }
80
- async resolve(path, opts) {
81
- const world = this.deps.worldFor(opts?.cwd);
82
- const effectivePath = world.anchorPath !== void 0 && world.remoteRoot !== void 0 ? this.translateAnchorPath(world.anchorPath, world.remoteRoot, path) : path;
83
- const raw = await world.backend.resolve(effectivePath, opts);
84
- return {
85
- targetKey: this.encode(String(raw.targetKey), world.namespace),
86
- displayPath: raw.displayPath
87
- };
88
- }
89
- /** Rewrite `<anchor>/<rest>` to `<remoteRoot>/<rest>` when `path` is the
90
- * anchor or under it; otherwise return `path` unchanged (remote absolute
91
- * paths, relative paths, and foreign local paths all pass through). */
92
- translateAnchorPath(anchor, remoteRoot, path) {
93
- const norm = (value) => value.replace(/[\\/]+$/, "");
94
- const a = norm(anchor);
95
- const isWin = a.includes("\\");
96
- const normPath = (value) => {
97
- let out = norm(value);
98
- if (isWin) out = out.replace(/\//g, "\\");
99
- return out;
100
- };
101
- const p = normPath(path);
102
- const aa = isWin ? a.toLowerCase() : a;
103
- const pp = isWin ? p.toLowerCase() : p;
104
- if (pp === aa) return remoteRoot;
105
- if (pp.startsWith(`${aa}\\`) || pp.startsWith(`${aa}/`)) {
106
- const tail = p.slice(a.length).replace(/^[\\/]+/, "").replace(/\\/g, "/");
107
- return tail === "" ? remoteRoot : `${remoteRoot.replace(/\/+$/, "")}/${tail}`;
108
- }
109
- return path;
110
- }
111
- processPath(target) {
112
- const key = String(target.targetKey);
113
- const decoded = this.decode(key);
114
- return decoded?.world.backend.processPath({
115
- targetKey: decoded.rawKey,
116
- displayPath: target.displayPath
117
- }) ?? key;
118
- }
119
- fileUrl(target) {
120
- const decoded = this.decode(String(target.targetKey));
121
- if (decoded === void 0) return this.local.fileUrl(target);
122
- return decoded.world.backend.fileUrl({
123
- targetKey: decoded.rawKey,
124
- displayPath: target.displayPath
125
- });
126
- }
127
- contains(parent, child) {
128
- const p = this.decode(String(parent.targetKey));
129
- const c = this.decode(String(child.targetKey));
130
- if (p === void 0 || c === void 0 || p.world.backend !== c.world.backend) return false;
131
- return p.world.backend.contains({
132
- targetKey: p.rawKey,
133
- displayPath: parent.displayPath
134
- }, {
135
- targetKey: c.rawKey,
136
- displayPath: child.displayPath
137
- });
138
- }
139
- async stat(target, signal) {
140
- const decoded = this.decode(String(target.targetKey));
141
- if (decoded === void 0) return void 0;
142
- return decoded.world.backend.stat({
143
- targetKey: decoded.rawKey,
144
- displayPath: target.displayPath
145
- }, signal);
146
- }
147
- async lstat(path, opts, signal) {
148
- const world = this.deps.worldFor(opts?.cwd);
149
- const effectivePath = world.anchorPath !== void 0 && world.remoteRoot !== void 0 ? this.translateAnchorPath(world.anchorPath, world.remoteRoot, path) : path;
150
- return world.backend.lstat(effectivePath, opts, signal);
151
- }
152
- async readText(target, signal) {
153
- const decoded = this.decode(String(target.targetKey));
154
- if (decoded === void 0) throw new Error("fs-ssh: cannot route target");
155
- return decoded.world.backend.readText({
156
- targetKey: decoded.rawKey,
157
- displayPath: target.displayPath
158
- }, signal);
159
- }
160
- async streamText(target, signal) {
161
- const decoded = this.decode(String(target.targetKey));
162
- if (decoded === void 0) throw new Error("fs-ssh: cannot route target");
163
- return decoded.world.backend.streamText({
164
- targetKey: decoded.rawKey,
165
- displayPath: target.displayPath
166
- }, signal);
167
- }
168
- async readBytes(target, signal, maxBytes) {
169
- const decoded = this.decode(String(target.targetKey));
170
- if (decoded === void 0) throw new Error("fs-ssh: cannot route target");
171
- return decoded.world.backend.readBytes({
172
- targetKey: decoded.rawKey,
173
- displayPath: target.displayPath
174
- }, signal, maxBytes);
175
- }
176
- async listDir(target, signal) {
177
- const decoded = this.decode(String(target.targetKey));
178
- if (decoded === void 0) throw new Error("fs-ssh: cannot route target");
179
- const entries = await decoded.world.backend.listDir({
180
- targetKey: decoded.rawKey,
181
- displayPath: target.displayPath
182
- }, signal);
183
- if (decoded.world.namespace === "") return entries;
184
- return entries.map((entry) => ({
185
- ...entry,
186
- target: {
187
- targetKey: this.encode(String(entry.target.targetKey), decoded.world.namespace),
188
- displayPath: entry.target.displayPath
189
- }
190
- }));
191
- }
192
- async writeText(target, content, expected, signal, sandboxPolicy) {
193
- const decoded = this.decode(String(target.targetKey));
194
- if (decoded === void 0) throw new Error("fs-ssh: cannot route target");
195
- return decoded.world.backend.writeText({
196
- targetKey: decoded.rawKey,
197
- displayPath: target.displayPath
198
- }, content, expected, signal, sandboxPolicy);
199
- }
200
- async editText(target, edit, expected, signal, sandboxPolicy) {
201
- const decoded = this.decode(String(target.targetKey));
202
- if (decoded === void 0) throw new Error("fs-ssh: cannot route target");
203
- return decoded.world.backend.editText({
204
- targetKey: decoded.rawKey,
205
- displayPath: target.displayPath
206
- }, edit, expected, signal, sandboxPolicy);
207
- }
208
- };
209
- //#endregion
210
- export { SwitchFileSystem as n, WFS_NAMESPACE_MARKER as r, REMOTE_PREFIX as t };
@@ -1,32 +0,0 @@
1
- import { type ReactElement } from 'react';
2
- import type { WorkspaceManager } from './state.ts';
3
- import type { SshApi } from './ssh/api.ts';
4
- /** The session-header button. Slot-injected props: manager + sshApi. */
5
- export declare function WorkspaceManagerButton(props: {
6
- manager: WorkspaceManager;
7
- /** Full SSH host API (list/create/update/delete) for server management. */
8
- sshApi?: SshApi;
9
- sessionId?: unknown;
10
- useSession?: unknown;
11
- useSessions?: unknown;
12
- useWorkspaces?: unknown;
13
- useProjection?: unknown;
14
- useInput?: unknown;
15
- inputActions?: unknown;
16
- renderSlot?: unknown;
17
- renderSlotChain?: unknown;
18
- t?: unknown;
19
- }): ReactElement;
20
- /** Open the manager dropdown, anchored under the trigger button. */
21
- export declare function openManager(manager: WorkspaceManager, sshApi?: SshApi, trigger?: HTMLElement): void;
22
- /** The manager dropdown: server + workspace rows, with server CRUD. */
23
- export declare function ManagerOverlay(props: {
24
- manager: WorkspaceManager;
25
- sshApi?: SshApi;
26
- onClose: () => void;
27
- anchor?: {
28
- bottom: number;
29
- left: number;
30
- };
31
- }): ReactElement;
32
- //# sourceMappingURL=manager-button.d.ts.map
@@ -1,13 +0,0 @@
1
- import type { SshApi } from './api.ts';
2
- import type { PanelController } from './panel/controller.ts';
3
- /** The injected panel container (kept in the DOM, hidden when inactive). */
4
- export declare const PANEL_VIEW_SELECTOR = "[data-dsh-ssh-view]";
5
- /**
6
- * Mount the panel React tree into the center column and bind its visibility
7
- * to the controller's panelOpen state.
8
- * @param controller - the panel controller driving the view.
9
- * @param api - the SSH API client the tabs operate through.
10
- * @returns disposer unmounting the tree and restoring the column.
11
- */
12
- export declare function mountPanel(controller: PanelController, api: SshApi): () => void;
13
- //# sourceMappingURL=mount.d.ts.map
@@ -1,10 +0,0 @@
1
- import type { SshApi } from '../api.ts';
2
- /** Hosts tab props. */
3
- export interface HostsTabProps {
4
- api: SshApi;
5
- /** Connect the given alias in the terminal tab. */
6
- onConnect: (alias: string) => void;
7
- }
8
- /** The hosts table plus its toolbar and dialogs. */
9
- export declare function HostsTab({ api, onConnect }: HostsTabProps): import("react").JSX.Element;
10
- //# sourceMappingURL=HostsTab.d.ts.map
@@ -1,23 +0,0 @@
1
- /**
2
- * SSH panel controller: the single owner of the panel's open/closed state.
3
- *
4
- * Framework-free (structural runtime faces, task-board core/controller.ts
5
- * style) so the DOM mounts and the React panel share one tiny subscription
6
- * surface. The state lives only for the browser session (no persistence).
7
- */
8
- /** Immutable controller snapshot for UI subscriptions. */
9
- export interface PanelControllerSnapshot {
10
- panelOpen: boolean;
11
- }
12
- /** The panel state owner the sidebar entry toggles and the view renders from. */
13
- export declare class PanelController {
14
- private panelOpen;
15
- private listeners;
16
- getSnapshot(): PanelControllerSnapshot;
17
- subscribe(fn: () => void): () => void;
18
- open(): void;
19
- close(): void;
20
- toggle(): void;
21
- private notify;
22
- }
23
- //# sourceMappingURL=controller.d.ts.map
@@ -1,25 +0,0 @@
1
- /**
2
- * Sidebar entry injection.
3
- *
4
- * dsh's sidebar shell exposes no slot an external plugin can register into,
5
- * so — following the task-board precedent of DOM-level extension — the entry
6
- * row is injected between the shell's New Session button and the workspace
7
- * browser. The injection self-heals: a MutationObserver watches the sidebar
8
- * root and re-inserts the row whenever a React re-render displaces it
9
- * (re-insertion happens in the same frame, before paint, so no flicker).
10
- *
11
- * The row is plain DOM (no React tree) so it can never disturb the shell's
12
- * reconciliation; the panel view it toggles is a separate React root mounted
13
- * in the center column (see mount.tsx).
14
- */
15
- import type { PanelController } from './panel/controller.ts';
16
- /** Stable data attribute identifying the injected entry row. */
17
- export declare const ENTRY_SELECTOR = "[data-dsh-ssh-entry]";
18
- /**
19
- * Mount the sidebar entry, waiting for the shell to render and self-healing
20
- * on later React re-renders.
21
- * @param controller - the panel controller the entry toggles.
22
- * @returns disposer removing the entry and its observers.
23
- */
24
- export declare function mountSidebarEntry(controller: PanelController): () => void;
25
- //# sourceMappingURL=sidebar-entry.d.ts.map
@@ -1,15 +0,0 @@
1
- import type { SshApi } from './ssh/api.ts';
2
- /** Silent prompt-only connection gate (no row / banner) for flows that
3
- * surface their own errors (e.g. the directory browser). */
4
- export declare function connectHost(api: SshApi, alias: string): Promise<boolean>;
5
- /**
6
- * Mount the click gate over every sidebar workspace row whose title belongs
7
- * to an SSH-bound workspace. Self-heals via MutationObserver. Returns a
8
- * disposer that removes listeners and injected state.
9
- */
10
- export declare function mountWorkspaceGates(workspaces: Array<{
11
- id: string;
12
- title: string;
13
- alias: string;
14
- }>, api: SshApi): () => void;
15
- //# sourceMappingURL=workspace-gate.d.ts.map
@@ -1,83 +0,0 @@
1
- /**
2
- * Remote-workspace runner: lets other plugins (dsh-workbench-tiphareth) run
3
- * git/files/terminals against an SSH-bound workspace. The workbench host asks
4
- * `resolveRemote(root)` for a local anchor path; a hit returns the remote
5
- * context + callable channels (git exec via the engine, SFTP ops, PTY).
6
- * Kept duck-typed so the workbench never needs a compile-time dependency.
7
- */
8
- import type { SshEngine } from './ssh/engine.ts';
9
- import type { SshWorkspaceLedger } from './ledger.ts';
10
- /** The remote context one SSH-bound workspace resolves to. */
11
- export interface RemoteWorkspaceContext {
12
- alias: string;
13
- remoteRoot: string;
14
- /** Run `git [args...]` inside remoteRoot. Mirrors runGit's result shape. */
15
- git(args: readonly string[], opts?: {
16
- timeoutMs?: number;
17
- input?: string;
18
- allowNonZero?: boolean;
19
- }): Promise<{
20
- stdout: string;
21
- stderr: string;
22
- exitCode: number;
23
- }>;
24
- /** Run one arbitrary command (used by tools that shell out). */
25
- run(command: string, opts?: {
26
- timeoutMs?: number;
27
- }): Promise<{
28
- stdout: string;
29
- stderr: string;
30
- exitCode: number;
31
- }>;
32
- /** SFTP operations. */
33
- list(path: string): Promise<Array<{
34
- name: string;
35
- type: 'dir' | 'file' | 'other';
36
- size: number;
37
- mtimeMs: number;
38
- }>>;
39
- stat(path: string): Promise<{
40
- type: 'dir' | 'file' | 'other';
41
- size: number;
42
- mtimeMs: number;
43
- mode: number;
44
- }>;
45
- readFile(path: string): Promise<{
46
- content: Buffer;
47
- mtime: number;
48
- size: number;
49
- }>;
50
- writeFile(path: string, content: Buffer, expectedMtime?: number): Promise<{
51
- mtime: number;
52
- }>;
53
- mkdir(path: string): Promise<void>;
54
- rm(path: string, recursive: boolean): Promise<void>;
55
- rename(from: string, to: string): Promise<void>;
56
- /** Open an interactive SSH PTY in remoteRoot (columns/rows for the channel). */
57
- openTerminal(cols: number, rows: number): Promise<RemotePtyHandle>;
58
- }
59
- /** Minimal PTY handle (duck-typed for consumers like dsh-workbench-tiphareth). */
60
- export interface RemotePtyHandle {
61
- shell: string;
62
- write(data: string): void;
63
- resize(cols: number, rows: number): void;
64
- kill(): void;
65
- onData(handler: (data: string) => void): {
66
- dispose(): void;
67
- };
68
- onExit(handler: (event: {
69
- exitCode: number;
70
- }) => void): {
71
- dispose(): void;
72
- };
73
- }
74
- /** Builds remote-workspace contexts off the shared ledger + engine. */
75
- export declare class RemoteWorkspaceRunner {
76
- private readonly engine;
77
- private readonly ledger;
78
- constructor(engine: SshEngine, ledger: SshWorkspaceLedger);
79
- /** Resolve a LOCAL anchor path to a remote context, or null if not SSH-bound. */
80
- resolveRemote(root: string): RemoteWorkspaceContext | null;
81
- private build;
82
- }
83
- //# sourceMappingURL=remote-runner.d.ts.map
@@ -1,69 +0,0 @@
1
- /**
2
- * The shared derived seam state: ONE ledger-derived routing snapshot
3
- * (records + normalized anchor index) plus the per-record fs/subprocess
4
- * instances, owned by the hardssh core and consumed by BOTH switch rows
5
- * (./fs and ./subprocess). Because both seams read the SAME state, they can
6
- * never disagree with each other or with the ledger's own index for a given
7
- * session cwd — eliminating the split-brain window of the old per-seam
8
- * async refresh caches.
9
- *
10
- * The state re-applies SYNCHRONOUSLY on every ledger commit: ledger
11
- * listeners run synchronously AFTER the commit already swapped the
12
- * in-memory records, so a synchronous re-apply is always current — no async
13
- * refresh pipeline, no stale-snapshot race, no revision-guard interleaving.
14
- * The initial file load is awaited before the state is marked ready; until
15
- * then routing degrades to local with a one-time warning from the rows.
16
- *
17
- * Per-record instances are built lazily on first route and kept alive
18
- * across refreshes (connections survive); removing a workspace drops its
19
- * instances so stale target keys resolve to NOTHING (fail closed), never to
20
- * the local backend.
21
- *
22
- * @module dsh-hardssh/seam-state
23
- */
24
- import type { SshWorkspaceRecord } from './protocol.ts';
25
- import { type SshWorkspaceLedger } from './ledger.ts';
26
- import { type WorkspaceWorld } from './switch/switch-fs.ts';
27
- import type { SshSubprocessRuntime } from './remote/remote-subprocess.ts';
28
- /** Shared routing state for the fs/subprocess switch rows. */
29
- export declare class WorkspaceSeamState {
30
- private readonly ledger;
31
- private readonly records;
32
- private readonly instances;
33
- private anchors;
34
- private appliedRevision;
35
- private ready;
36
- private readyResolvers;
37
- private fsFactory;
38
- private subFactory;
39
- constructor(ledger: SshWorkspaceLedger);
40
- /** Bind the fs-row's per-record backend builder (called once at apply). */
41
- bindFs(factory: (record: SshWorkspaceRecord) => WorkspaceWorld): void;
42
- /** Bind the subprocess-row's per-record runtime builder (called once at apply). */
43
- bindSub(factory: (record: SshWorkspaceRecord) => SshSubprocessRuntime): void;
44
- /** True once the initial ledger snapshot has been applied. */
45
- isReady(): boolean;
46
- /** Resolves once the initial ledger snapshot has been applied. */
47
- whenReady(): Promise<void>;
48
- /**
49
- * Subscribe to ledger commits and kick the initial load. Returns the
50
- * subscription disposer (register it through ctx.effect for HMR cleanup).
51
- */
52
- attach(): () => void;
53
- /** Re-apply the ledger's current in-memory state (idempotent, guarded). */
54
- applySync(): void;
55
- /** The record owning a session cwd, or undefined when the cwd is local. */
56
- private recordFor;
57
- private ensureFs;
58
- private ensureSubprocess;
59
- /** The fs world owning a session cwd, or undefined (local). */
60
- worldForFs(cwd: string | undefined): WorkspaceWorld | undefined;
61
- /** The fs world for one namespaced target key (`wfs://<id>/…` or legacy
62
- * `ssh:<id>:`), or undefined when the owning workspace no longer exists
63
- * (stale key → fail closed). */
64
- worldForFsNamespace(namespace: string): WorkspaceWorld | undefined;
65
- /** The subprocess runtime owning a session cwd, or undefined (local). */
66
- runtimeForSub(cwd: string | undefined): SshSubprocessRuntime | undefined;
67
- private markReady;
68
- }
69
- //# sourceMappingURL=seam-state.d.ts.map