dsh-over-ssh 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +119 -2
  3. package/agents/agent-manifest.json +20 -0
  4. package/agents/linux-amd64/dsh-over-ssh-agent +0 -0
  5. package/agents/linux-arm64/dsh-over-ssh-agent +0 -0
  6. package/cordis.patch.yml +84 -0
  7. package/lib/client.js +6611 -0
  8. package/lib/file-references.js +366 -0
  9. package/lib/fs.js +1368 -0
  10. package/lib/index.js +19034 -0
  11. package/lib/local-bridge-refs.js +16 -0
  12. package/lib/local-bridge.js +16 -0
  13. package/lib/terminal-wrapper.js +294 -0
  14. package/lib/types/src/client/controls.d.ts +131 -0
  15. package/lib/types/src/client/core/card-focus-trap.d.ts +45 -0
  16. package/lib/types/src/client/core/controls.d.ts +164 -0
  17. package/lib/types/src/client/core/latest.d.ts +48 -0
  18. package/lib/types/src/client/core/locale.d.ts +38 -0
  19. package/lib/types/src/client/core/message.d.ts +37 -0
  20. package/lib/types/src/client/core/panel-model.d.ts +134 -0
  21. package/lib/types/src/client/core/portal.d.ts +266 -0
  22. package/lib/types/src/client/core/remote.d.ts +84 -0
  23. package/lib/types/src/client/core/styles.d.ts +458 -0
  24. package/lib/types/src/client/file-tree-io.d.ts +19 -0
  25. package/lib/types/src/client/file-tree.d.ts +271 -0
  26. package/lib/types/src/client/flows/directory-browser.d.ts +587 -0
  27. package/lib/types/src/client/flows/session-directory.d.ts +415 -0
  28. package/lib/types/src/client/flows/workspace-directory-flow.d.ts +480 -0
  29. package/lib/types/src/client/index.d.ts +49 -0
  30. package/lib/types/src/client/locale.d.ts +31 -0
  31. package/lib/types/src/client/panel-model.d.ts +37 -0
  32. package/lib/types/src/client/panel.d.ts +77 -0
  33. package/lib/types/src/client/roots/connection-test.d.ts +69 -0
  34. package/lib/types/src/client/roots/roots-panel-state.d.ts +307 -0
  35. package/lib/types/src/client/roots/roots-panel-view.d.ts +280 -0
  36. package/lib/types/src/client/roots/roots-panel.d.ts +364 -0
  37. package/lib/types/src/client/roots/transfer-panel-view.d.ts +35 -0
  38. package/lib/types/src/client/roots/transfer-panel.d.ts +241 -0
  39. package/lib/types/src/client/roots/transfer-progress.d.ts +67 -0
  40. package/lib/types/src/client/roots-panel-view.d.ts +236 -0
  41. package/lib/types/src/client/roots-panel.d.ts +347 -0
  42. package/lib/types/src/client/session/session-panel-view.d.ts +336 -0
  43. package/lib/types/src/client/session/session-panel.d.ts +322 -0
  44. package/lib/types/src/client/session/session-remote-mark.d.ts +110 -0
  45. package/lib/types/src/client/session/session-row-actions.d.ts +398 -0
  46. package/lib/types/src/client/session-panel.d.ts +152 -0
  47. package/lib/types/src/client/styles.d.ts +252 -0
  48. package/lib/types/src/client/workspace-directory-flow.d.ts +342 -0
  49. package/lib/types/src/config.d.ts +312 -0
  50. package/lib/types/src/file-references.d.ts +25 -0
  51. package/lib/types/src/fs.d.ts +46 -0
  52. package/lib/types/src/host/app/backend.d.ts +170 -0
  53. package/lib/types/src/host/app/recycle-bin/recycle-bin-contract.d.ts +247 -0
  54. package/lib/types/src/host/app/recycle-bin/recycle-bin-move.d.ts +18 -0
  55. package/lib/types/src/host/app/recycle-bin/recycle-bin-service.d.ts +45 -0
  56. package/lib/types/src/host/app/recycle-bin/recycle-bin-world.d.ts +38 -0
  57. package/lib/types/src/host/app/recycle-bin-contract.d.ts +215 -0
  58. package/lib/types/src/host/app/recycle-bin-move.d.ts +18 -0
  59. package/lib/types/src/host/app/recycle-bin-service.d.ts +45 -0
  60. package/lib/types/src/host/app/recycle-bin-world.d.ts +35 -0
  61. package/lib/types/src/host/app/roots/roots-api-connections.d.ts +49 -0
  62. package/lib/types/src/host/app/roots/roots-api-contract.d.ts +369 -0
  63. package/lib/types/src/host/app/roots/roots-api-directories.d.ts +13 -0
  64. package/lib/types/src/host/app/roots/roots-api-http.d.ts +77 -0
  65. package/lib/types/src/host/app/roots/roots-api-long-poll.d.ts +130 -0
  66. package/lib/types/src/host/app/roots/roots-api-projections.d.ts +102 -0
  67. package/lib/types/src/host/app/roots/roots-api-roots.d.ts +13 -0
  68. package/lib/types/src/host/app/roots/roots-api-state.d.ts +22 -0
  69. package/lib/types/src/host/app/roots/roots-api-transfers.d.ts +26 -0
  70. package/lib/types/src/host/app/roots/roots-api.d.ts +55 -0
  71. package/lib/types/src/host/app/roots/user-roots.d.ts +552 -0
  72. package/lib/types/src/host/app/roots-api-connections.d.ts +49 -0
  73. package/lib/types/src/host/app/roots-api-contract.d.ts +339 -0
  74. package/lib/types/src/host/app/roots-api-directories.d.ts +13 -0
  75. package/lib/types/src/host/app/roots-api-http.d.ts +77 -0
  76. package/lib/types/src/host/app/roots-api-projections.d.ts +102 -0
  77. package/lib/types/src/host/app/roots-api-roots.d.ts +13 -0
  78. package/lib/types/src/host/app/roots-api-state.d.ts +22 -0
  79. package/lib/types/src/host/app/roots-api-support.d.ts +182 -0
  80. package/lib/types/src/host/app/roots-api-transfers.d.ts +13 -0
  81. package/lib/types/src/host/app/roots-api.d.ts +55 -0
  82. package/lib/types/src/host/app/routed-fs.d.ts +239 -0
  83. package/lib/types/src/host/app/serialize.d.ts +37 -0
  84. package/lib/types/src/host/app/session-admin/session-admin-context.d.ts +36 -0
  85. package/lib/types/src/host/app/session-admin/session-admin-contract.d.ts +221 -0
  86. package/lib/types/src/host/app/session-admin/session-admin-delete.d.ts +34 -0
  87. package/lib/types/src/host/app/session-admin/session-admin-service.d.ts +40 -0
  88. package/lib/types/src/host/app/session-admin-context.d.ts +36 -0
  89. package/lib/types/src/host/app/session-admin-contract.d.ts +214 -0
  90. package/lib/types/src/host/app/session-admin-delete.d.ts +34 -0
  91. package/lib/types/src/host/app/session-admin-service.d.ts +38 -0
  92. package/lib/types/src/host/app/takeover/takeover-plan.d.ts +227 -0
  93. package/lib/types/src/host/app/takeover/takeover-rows.d.ts +55 -0
  94. package/lib/types/src/host/app/transport/transport-selector.d.ts +98 -0
  95. package/lib/types/src/host/app/transport/transport-service.d.ts +82 -0
  96. package/lib/types/src/host/app/transport-selector.d.ts +98 -0
  97. package/lib/types/src/host/app/transport-service.d.ts +75 -0
  98. package/lib/types/src/host/app/user-roots.d.ts +461 -0
  99. package/lib/types/src/host/domain/bin-location.d.ts +79 -0
  100. package/lib/types/src/host/domain/capability.d.ts +78 -0
  101. package/lib/types/src/host/domain/connection.d.ts +95 -0
  102. package/lib/types/src/host/domain/errors.d.ts +55 -0
  103. package/lib/types/src/host/domain/glob-pattern.d.ts +62 -0
  104. package/lib/types/src/host/domain/literal-text.d.ts +63 -0
  105. package/lib/types/src/host/domain/path-grammar.d.ts +84 -0
  106. package/lib/types/src/host/domain/recycle-bin.d.ts +337 -0
  107. package/lib/types/src/host/domain/root.d.ts +74 -0
  108. package/lib/types/src/host/domain/roots/bin-location.d.ts +91 -0
  109. package/lib/types/src/host/domain/roots/connection.d.ts +106 -0
  110. package/lib/types/src/host/domain/roots/root.d.ts +108 -0
  111. package/lib/types/src/host/domain/roots/route-table.d.ts +102 -0
  112. package/lib/types/src/host/domain/roots/session-roots.d.ts +151 -0
  113. package/lib/types/src/host/domain/roots/ssh-config.d.ts +73 -0
  114. package/lib/types/src/host/domain/route-table.d.ts +80 -0
  115. package/lib/types/src/host/domain/session/session-admin.d.ts +345 -0
  116. package/lib/types/src/host/domain/session/terminal-target.d.ts +136 -0
  117. package/lib/types/src/host/domain/session-admin.d.ts +330 -0
  118. package/lib/types/src/host/domain/shell.d.ts +47 -0
  119. package/lib/types/src/host/domain/ssh-config.d.ts +73 -0
  120. package/lib/types/src/host/domain/terminal-target.d.ts +132 -0
  121. package/lib/types/src/host/domain/tool-requests.d.ts +369 -0
  122. package/lib/types/src/host/domain/transfer/recycle-bin.d.ts +337 -0
  123. package/lib/types/src/host/domain/transfer/transfer.d.ts +260 -0
  124. package/lib/types/src/host/domain/transfer.d.ts +260 -0
  125. package/lib/types/src/host/infra/backend/local-backend.d.ts +84 -0
  126. package/lib/types/src/host/infra/backend/local-version.d.ts +43 -0
  127. package/lib/types/src/host/infra/backend/platform-local-backend.d.ts +89 -0
  128. package/lib/types/src/host/infra/backend/remote-backend.d.ts +239 -0
  129. package/lib/types/src/host/infra/cache/lru.d.ts +52 -0
  130. package/lib/types/src/host/infra/cache/versioned.d.ts +77 -0
  131. package/lib/types/src/host/infra/temp-name.d.ts +19 -0
  132. package/lib/types/src/host/infra/transport/agent/agent-installer.d.ts +121 -0
  133. package/lib/types/src/host/infra/transport/agent/agent-protocol-client.d.ts +132 -0
  134. package/lib/types/src/host/infra/transport/agent/agent-protocol-codec.d.ts +140 -0
  135. package/lib/types/src/host/infra/transport/agent/agent-protocol-errors.d.ts +41 -0
  136. package/lib/types/src/host/infra/transport/agent/agent-protocol-frames.d.ts +197 -0
  137. package/lib/types/src/host/infra/transport/agent/agent-protocol-types.d.ts +564 -0
  138. package/lib/types/src/host/infra/transport/agent/agent-transport.d.ts +418 -0
  139. package/lib/types/src/host/infra/transport/agent-installer.d.ts +121 -0
  140. package/lib/types/src/host/infra/transport/agent-protocol-client.d.ts +123 -0
  141. package/lib/types/src/host/infra/transport/agent-protocol-codec.d.ts +131 -0
  142. package/lib/types/src/host/infra/transport/agent-protocol-errors.d.ts +41 -0
  143. package/lib/types/src/host/infra/transport/agent-protocol-frames.d.ts +197 -0
  144. package/lib/types/src/host/infra/transport/agent-protocol-types.d.ts +509 -0
  145. package/lib/types/src/host/infra/transport/agent-transport.d.ts +353 -0
  146. package/lib/types/src/host/infra/transport/alias-transport.d.ts +206 -0
  147. package/lib/types/src/host/infra/transport/askpass-fs.d.ts +20 -0
  148. package/lib/types/src/host/infra/transport/askpass.d.ts +133 -0
  149. package/lib/types/src/host/infra/transport/command-pool.d.ts +61 -0
  150. package/lib/types/src/host/infra/transport/fs-commands.d.ts +140 -0
  151. package/lib/types/src/host/infra/transport/git-status-route.d.ts +63 -0
  152. package/lib/types/src/host/infra/transport/git-status.d.ts +101 -0
  153. package/lib/types/src/host/infra/transport/node-spawn.d.ts +26 -0
  154. package/lib/types/src/host/infra/transport/session-window.d.ts +51 -0
  155. package/lib/types/src/host/infra/transport/shell-quote.d.ts +59 -0
  156. package/lib/types/src/host/infra/transport/ssh/alias-transport.d.ts +206 -0
  157. package/lib/types/src/host/infra/transport/ssh/askpass-fs.d.ts +20 -0
  158. package/lib/types/src/host/infra/transport/ssh/askpass.d.ts +133 -0
  159. package/lib/types/src/host/infra/transport/ssh/shell-quote.d.ts +62 -0
  160. package/lib/types/src/host/infra/transport/ssh/ssh-argv.d.ts +43 -0
  161. package/lib/types/src/host/infra/transport/ssh/ssh2-transport.d.ts +328 -0
  162. package/lib/types/src/host/infra/transport/ssh-argv.d.ts +43 -0
  163. package/lib/types/src/host/infra/transport/ssh2-transport.d.ts +302 -0
  164. package/lib/types/src/host/infra/transport/transport-base.d.ts +34 -0
  165. package/lib/types/src/host/infra/transport/transport.d.ts +527 -0
  166. package/lib/types/src/host/integration/agent/agent-install-io.d.ts +33 -0
  167. package/lib/types/src/host/integration/agent/agent-preflight.d.ts +56 -0
  168. package/lib/types/src/host/integration/agent/askpass-registry.d.ts +66 -0
  169. package/lib/types/src/host/integration/agent-install-io.d.ts +33 -0
  170. package/lib/types/src/host/integration/agent-preflight.d.ts +56 -0
  171. package/lib/types/src/host/integration/anchor-directory.d.ts +56 -0
  172. package/lib/types/src/host/integration/askpass-registry.d.ts +59 -0
  173. package/lib/types/src/host/integration/bin-root-resolver.d.ts +81 -0
  174. package/lib/types/src/host/integration/connection-test.d.ts +73 -0
  175. package/lib/types/src/host/integration/file-reference-provider.d.ts +115 -0
  176. package/lib/types/src/host/integration/fs/file-reference-provider.d.ts +142 -0
  177. package/lib/types/src/host/integration/fs/fs-provider.d.ts +368 -0
  178. package/lib/types/src/host/integration/fs/git-status-wiring.d.ts +21 -0
  179. package/lib/types/src/host/integration/fs/platform-errors.d.ts +94 -0
  180. package/lib/types/src/host/integration/fs/remote-file-tools.d.ts +369 -0
  181. package/lib/types/src/host/integration/fs/remote-index.d.ts +177 -0
  182. package/lib/types/src/host/integration/fs-provider.d.ts +226 -0
  183. package/lib/types/src/host/integration/lsp/lsp-framing.d.ts +38 -0
  184. package/lib/types/src/host/integration/lsp/lsp-registry.d.ts +67 -0
  185. package/lib/types/src/host/integration/lsp/lsp-session.d.ts +130 -0
  186. package/lib/types/src/host/integration/management-tools.d.ts +64 -0
  187. package/lib/types/src/host/integration/recycle-bin-service.d.ts +247 -0
  188. package/lib/types/src/host/integration/remote/connection-test.d.ts +73 -0
  189. package/lib/types/src/host/integration/remote/remote-face.d.ts +127 -0
  190. package/lib/types/src/host/integration/remote/remote-tool-backend.d.ts +108 -0
  191. package/lib/types/src/host/integration/remote-face.d.ts +127 -0
  192. package/lib/types/src/host/integration/remote-file-tools.d.ts +241 -0
  193. package/lib/types/src/host/integration/remote-home.d.ts +69 -0
  194. package/lib/types/src/host/integration/remote-index.d.ts +165 -0
  195. package/lib/types/src/host/integration/remote-job-tools.d.ts +295 -0
  196. package/lib/types/src/host/integration/remote-tool-backend.d.ts +97 -0
  197. package/lib/types/src/host/integration/remote-tools.d.ts +206 -0
  198. package/lib/types/src/host/integration/root-registry.d.ts +115 -0
  199. package/lib/types/src/host/integration/roots/anchor-directory.d.ts +56 -0
  200. package/lib/types/src/host/integration/roots/bin-root-resolver.d.ts +81 -0
  201. package/lib/types/src/host/integration/roots/remote-home.d.ts +69 -0
  202. package/lib/types/src/host/integration/roots/root-registry.d.ts +144 -0
  203. package/lib/types/src/host/integration/roots/roots-wiring.d.ts +219 -0
  204. package/lib/types/src/host/integration/roots/session-root-index.d.ts +52 -0
  205. package/lib/types/src/host/integration/roots-api.d.ts +407 -0
  206. package/lib/types/src/host/integration/roots-wiring.d.ts +211 -0
  207. package/lib/types/src/host/integration/session/session-api.d.ts +88 -0
  208. package/lib/types/src/host/integration/session/session-bin-lock.d.ts +80 -0
  209. package/lib/types/src/host/integration/session/session-header.d.ts +151 -0
  210. package/lib/types/src/host/integration/session/session-labels.d.ts +56 -0
  211. package/lib/types/src/host/integration/session/session-route-wiring.d.ts +50 -0
  212. package/lib/types/src/host/integration/session-admin-service.d.ts +236 -0
  213. package/lib/types/src/host/integration/session-api.d.ts +81 -0
  214. package/lib/types/src/host/integration/session-bin-lock.d.ts +80 -0
  215. package/lib/types/src/host/integration/session-header.d.ts +118 -0
  216. package/lib/types/src/host/integration/session-labels.d.ts +56 -0
  217. package/lib/types/src/host/integration/session-route-wiring.d.ts +50 -0
  218. package/lib/types/src/host/integration/session-scope.d.ts +113 -0
  219. package/lib/types/src/host/integration/session-tools.d.ts +44 -0
  220. package/lib/types/src/host/integration/strict-arguments.d.ts +79 -0
  221. package/lib/types/src/host/integration/takeover/takeover-api.d.ts +62 -0
  222. package/lib/types/src/host/integration/takeover/takeover-route-wiring.d.ts +60 -0
  223. package/lib/types/src/host/integration/takeover/takeover-service.d.ts +167 -0
  224. package/lib/types/src/host/integration/terminal-manifest.d.ts +94 -0
  225. package/lib/types/src/host/integration/tools/management-tools.d.ts +245 -0
  226. package/lib/types/src/host/integration/tools/remote-job-tools.d.ts +316 -0
  227. package/lib/types/src/host/integration/tools/remote-lsp-tools.d.ts +43 -0
  228. package/lib/types/src/host/integration/tools/remote-tools.d.ts +236 -0
  229. package/lib/types/src/host/integration/tools/session-target.d.ts +52 -0
  230. package/lib/types/src/host/integration/tools/session-tools.d.ts +46 -0
  231. package/lib/types/src/host/integration/tools/strict-arguments.d.ts +79 -0
  232. package/lib/types/src/host/integration/tools/terminal-manifest.d.ts +94 -0
  233. package/lib/types/src/host/integration/transfer/remote-copy.d.ts +66 -0
  234. package/lib/types/src/host/integration/transfer/transfer-adapters.d.ts +48 -0
  235. package/lib/types/src/host/integration/transfer/transfer-service.d.ts +212 -0
  236. package/lib/types/src/host/integration/transfer/transfer-tracker.d.ts +106 -0
  237. package/lib/types/src/host/integration/transfer-adapters.d.ts +44 -0
  238. package/lib/types/src/host/integration/transfer-service.d.ts +164 -0
  239. package/lib/types/src/host/integration/transfer-tracker.d.ts +106 -0
  240. package/lib/types/src/host/integration/user-roots.d.ts +461 -0
  241. package/lib/types/src/index.d.ts +66 -0
  242. package/lib/types/src/local-bridge-refs.d.ts +26 -0
  243. package/lib/types/src/local-bridge.d.ts +36 -0
  244. package/lib/types/src/shared/format.d.ts +13 -0
  245. package/lib/types/src/wiring/connections.d.ts +189 -0
  246. package/lib/types/src/wiring/fs.d.ts +13 -0
  247. package/lib/types/src/wiring/helpers.d.ts +144 -0
  248. package/lib/types/src/wiring/install-memo.d.ts +32 -0
  249. package/lib/types/src/wiring/legacy-dirs.d.ts +62 -0
  250. package/lib/types/src/wiring/package-paths.d.ts +35 -0
  251. package/lib/types/src/wiring/roots.d.ts +21 -0
  252. package/lib/types/src/wiring/state-dir.d.ts +29 -0
  253. package/lib/types/src/wiring/tools.d.ts +35 -0
  254. package/package.json +120 -4
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Root references: one project root is either a local directory or a remote directory on
3
+ * one connection. Remote roots additionally carry a local *anchor* directory, because DSH
4
+ * requires a workspace path that exists on the host filesystem.
5
+ *
6
+ * @module dsh-over-ssh/domain/root
7
+ */
8
+ /** Where a root lives. */
9
+ export type RootLocation = 'local' | 'remote';
10
+ /** One project root. */
11
+ export interface RootRef {
12
+ /** Local or remote. */
13
+ readonly location: RootLocation;
14
+ /** Connection id; present exactly for remote roots. */
15
+ readonly connectionId?: string;
16
+ /** Absolute local path, or absolute POSIX path for a remote root. */
17
+ readonly path: string;
18
+ /** Whether the sandbox fence admits writes under this root. */
19
+ readonly writable: boolean;
20
+ /** Local anchor directory for a remote root (empty directory, never mirrored content). */
21
+ readonly anchorPath?: string;
22
+ /** Display label; defaults to the final path segment. */
23
+ readonly label?: string;
24
+ /**
25
+ * The workspaces (session cwd paths) that added this root.
26
+ *
27
+ * **Absent means "no session added it"**, which under session isolation means "no session may use it"
28
+ * ({@link rootVisibleToSession} in `./session-roots.ts`). It is not a default-allow: the user-roots file has
29
+ * carried this field since the 「添加目录」 grouping landed, and the settings panel writes unscoped roots on
30
+ * purpose (its form has no scope field).
31
+ */
32
+ readonly workspaces?: readonly string[];
33
+ /**
34
+ * The **sessions** (conversation ids) that added this root — the strict identity behind {@link workspaces}.
35
+ *
36
+ * `workspaces` answers "which workspace added it", which cannot separate two conversations opened on the
37
+ * same directory; this field can. When it is present **and** the caller carries a session id, the route
38
+ * table decides by session and never consults the cwd (ADR-0013, `domain/roots/session-roots.ts`). Absent
39
+ * means "written before ADR-0013" — such a root keeps the workspace-level rule, so existing data does not
40
+ * lose access on upgrade.
41
+ */
42
+ readonly sessions?: readonly string[];
43
+ /**
44
+ * Whether this root is **deployment-wide** rather than a session artifact.
45
+ *
46
+ * True for roots declared in the plugin's `config.roots`: they are infrastructure the deployment declares once
47
+ * (the stage-2 requirement "a local root covering the current workspace" is one of them), so every session may
48
+ * use them. False/absent for roots the user added through the panel or 「添加目录」 — those are per-session and
49
+ * are gated by {@link workspaces} (see `rootVisibleToSession`).
50
+ */
51
+ readonly shared?: boolean;
52
+ }
53
+ /** Options shared by both root kinds. */
54
+ export interface RootOptions {
55
+ /** Whether writes under this root are allowed; defaults to true. */
56
+ readonly writable?: boolean;
57
+ /** Display label override. */
58
+ readonly label?: string;
59
+ /** Workspaces that added this root; see {@link RootRef.workspaces}. */
60
+ readonly workspaces?: readonly string[];
61
+ /** Sessions that added this root; see {@link RootRef.sessions}. */
62
+ readonly sessions?: readonly string[];
63
+ /** Deployment-wide root; see {@link RootRef.shared}. */
64
+ readonly shared?: boolean;
65
+ }
66
+ /** Create a remote root, validating the connection id and remote path. */
67
+ export declare function makeRemoteRoot(connectionId: string, remotePath: string, options?: RootOptions & {
68
+ readonly anchorPath?: string;
69
+ }): RootRef;
70
+ /**
71
+ * Create a local root from an absolute host path.
72
+ *
73
+ * The stored path **preserves the caller's case** (only separators, `.`/`..` and the drive letter
74
+ * are canonicalised). Case folding belongs to comparison and keying — `rootKey` folds, and
75
+ * `localPathContains` folds on request — because the root's path is also what the UI shows and what
76
+ * a child process receives via `processPath`.
77
+ */
78
+ export declare function makeLocalRoot(path: string, options?: RootOptions): RootRef;
79
+ /** Whether a host path is absolute in either the Windows or the POSIX spelling. */
80
+ export declare function isAbsoluteLocalPath(path: string): boolean;
81
+ /**
82
+ * Normalize a host path for comparison: separators to `/`, `.`/`..` resolved, trailing
83
+ * separators dropped, and (optionally) the drive letter and case folded.
84
+ *
85
+ * @param path - host path in either separator spelling.
86
+ * @param options - `caseInsensitive` folds case for comparison keys.
87
+ * @returns the normalized path.
88
+ */
89
+ export declare function normalizeLocalPath(path: string, options?: {
90
+ readonly caseInsensitive?: boolean;
91
+ }): string;
92
+ /** Whether a host path is `root` itself or a descendant of it. */
93
+ export declare function localPathContains(root: string, candidate: string, options?: {
94
+ readonly caseInsensitive?: boolean;
95
+ }): boolean;
96
+ /** Stable identity of a root, used as a cache/route key. */
97
+ export declare function rootKey(root: RootRef): string;
98
+ /** The model-facing and UI-facing spelling of a root's directory. */
99
+ export declare function rootDisplay(root: RootRef): string;
100
+ /** The display label of a root: explicit label, else the final path segment. */
101
+ export declare function rootLabel(root: RootRef): string;
102
+ /** Whether two roots of the same location overlap (one contains the other). */
103
+ export declare function rootsOverlap(left: RootRef, right: RootRef): boolean;
104
+ /** Validate a root reference produced outside this module. */
105
+ export declare function validateRoot(root: RootRef): void;
106
+ /** FNV-1a (32-bit) over UTF-16 code units, rendered as 8 lowercase hex characters. */
107
+ export declare function fnv1aHex(input: string): string;
108
+ //# sourceMappingURL=root.d.ts.map
@@ -0,0 +1,102 @@
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-over-ssh/domain/route-table
16
+ */
17
+ import { SshDevError } from '../errors.ts';
18
+ import { type RootRef } from './root.ts';
19
+ import { type SessionIdentity } from './session-roots.ts';
20
+ /** One resolved route. */
21
+ export interface RouteTarget {
22
+ /** The root the path belongs to. */
23
+ readonly root: RootRef;
24
+ /** Root-relative spelling with `/` separators; an empty string means the root itself. */
25
+ readonly relativePath: string;
26
+ /** Stable backend key; one backend serves all paths of one root. */
27
+ readonly backendKey: string;
28
+ }
29
+ /** Per-call resolution options. */
30
+ export interface RouteOptions {
31
+ /** Session working directory used for relative spellings. */
32
+ readonly cwd?: string;
33
+ /**
34
+ * Who is asking, when the caller knows more than its cwd (ADR-0013).
35
+ *
36
+ * The route table never invents this: the fs provider reads it from the session scope the tool dispatcher
37
+ * carries down (`integration/session-scope.ts`) and hands it over. `undefined` means "no session identity" —
38
+ * the workspace-level rule then stands, which is exactly the behaviour before ADR-0013.
39
+ */
40
+ readonly session?: SessionIdentity;
41
+ /**
42
+ * The asking session's own working directory, when the caller did not supply a `cwd`.
43
+ *
44
+ * Used **only** by the implicit session root (a session's own directory is always readable to it) — never by
45
+ * the gate below, which must keep seeing exactly what the platform passed. Synthesising a `cwd` for the gate
46
+ * would apply the workspace rule to calls that legitimately carry none, which is the failure ADR-0012 §4
47
+ * boundary 2 records (a whole turn lost before the model was called).
48
+ */
49
+ readonly sessionCwd?: string;
50
+ /** Override host-path case folding (defaults to the platform). */
51
+ readonly caseInsensitive?: boolean;
52
+ }
53
+ /** A path resolved to a root, or a diagnostic reason why it could not be. */
54
+ export type RouteOutcome = {
55
+ readonly ok: true;
56
+ readonly target: RouteTarget;
57
+ } | {
58
+ readonly ok: false;
59
+ readonly error: SshDevError;
60
+ };
61
+ /** Resolution table over an ordered set of roots. */
62
+ export declare class RouteTable {
63
+ #private;
64
+ constructor(roots?: readonly RootRef[], options?: {
65
+ readonly caseInsensitive?: boolean;
66
+ });
67
+ /** Roots in registration order. */
68
+ get roots(): readonly RootRef[];
69
+ /**
70
+ * Register a root.
71
+ *
72
+ * @param root - root to add.
73
+ * @throws SshDevError `ROOT_INVALID` when the root is malformed or already registered.
74
+ */
75
+ add(root: RootRef): void;
76
+ /** Remove a root by its key; returns whether it was present. */
77
+ remove(key: string): boolean;
78
+ /** Replace every root; throws like {@link add} when any root is invalid. */
79
+ reset(roots: readonly RootRef[]): void;
80
+ /**
81
+ * Resolve a path, then apply the **session gate**.
82
+ *
83
+ * Two failures wear the same code on purpose: "no root covers this path" and "the root that covers it belongs
84
+ * to another session" are both `ROOT_OUTSIDE`, because from the caller's side they are the same fact — this
85
+ * session cannot use that path. The message and the `root` detail say which one it was.
86
+ *
87
+ * @param input - model-facing, host or remote spelling.
88
+ * @param options - session cwd (also the session identity, see {@link rootVisibleToSession}) and case folding.
89
+ * @returns the resolved route, or a failure carrying `ROOT_OUTSIDE` / `ROOT_AMBIGUOUS`.
90
+ */
91
+ tryResolve(input: string, options?: RouteOptions): RouteOutcome;
92
+ /**
93
+ * Resolve a path or throw.
94
+ *
95
+ * @param input - path to resolve.
96
+ * @param options - session cwd and case-folding override.
97
+ * @returns the resolved route.
98
+ * @throws SshDevError `ROOT_OUTSIDE` or `ROOT_AMBIGUOUS`.
99
+ */
100
+ resolve(input: string, options?: RouteOptions): RouteTarget;
101
+ }
102
+ //# sourceMappingURL=route-table.d.ts.map
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Session-scoped remote roots: which remote directories **this** conversation may target.
3
+ *
4
+ * The problem this solves. 「添加目录」 registers a directory for one session (the entry's `workspaces` list
5
+ * holds that session's workspace path), and until now that was a **visibility** rule only: the client card
6
+ * listed the directory, but every `remote_*` tool picked its root from the agent's session cwd alone
7
+ * (`remote-tools.ts` `targetFor`). A session opened on a local folder — `D:\project\x` — therefore could add
8
+ * `165服务器:/home1/me/work/android14`, see it in the card, and still get
9
+ * `工作目录不在任何远端根内:D:\project\x` from every remote tool. The user's own words:
10
+ * 「我一个本地目录的会话,我添加远程的路径,这个会话也能正常访问远程路径」.
11
+ *
12
+ * The rule implemented here is the narrow one that makes that work without turning 「添加目录」 into a
13
+ * process-wide switch:
14
+ *
15
+ * 1. A cwd that resolves to a remote root **wins** (the pre-existing behaviour, unchanged). A session living
16
+ * on a remote workspace keeps targeting it, and nothing about `ssh://` resolution changes.
17
+ * 2. Otherwise, the roots **this session added** are candidates. Exactly one candidate is used; zero is the
18
+ * old `ROOT_OUTSIDE` error; two or more is refused by name instead of guessed — the same stance the
19
+ * route table already takes for two remote roots with the same spelling (`ROOT_AMBIGUOUS`).
20
+ *
21
+ * Why "scoped to this session" was read from the **workspace path** rather than from a session id: that is
22
+ * what `add-root` wrote (`session-directory.ts` passes `workspaces: [state.workspacePath]`), and it was the
23
+ * only field the persisted file carried. **ADR-0013 changes that**: the platform does not hand a session id to
24
+ * `ctx.fs`, but it does hand one to every tool execution, and `integration/session-scope.ts` carries it down.
25
+ * Entries now also record `sessions`, and when both the root and the caller have one, **the session decides**
26
+ * and the workspace path is not consulted. A fork inherits its ancestors' entries ({@link SessionIdentity}).
27
+ * Entries written before ADR-0013 carry no `sessions` and keep the workspace-level rule unchanged.
28
+ *
29
+ * Pure: no filesystem, no transport, no platform types — the caller injects the entries. That is what makes
30
+ * the rule testable offline (红线 2/3).
31
+ *
32
+ * @module dsh-over-ssh/domain/roots/session-roots
33
+ */
34
+ import { type RootRef } from './root.ts';
35
+ /** One persisted root entry, as far as this module needs to read it. */
36
+ export interface SessionRootEntry {
37
+ readonly location: 'local' | 'remote';
38
+ readonly path: string;
39
+ readonly connectionId?: string;
40
+ /** The workspaces (session cwd paths) that added this root. Absent means "no session added it". */
41
+ readonly workspaces?: readonly string[];
42
+ /** The **sessions** (conversation ids) that added this root. Absent on entries written before ADR-0013. */
43
+ readonly sessions?: readonly string[];
44
+ }
45
+ /**
46
+ * Who is asking: the cwd every caller already carries, plus the session identity when one is available.
47
+ *
48
+ * `sessionId` is the strict identity — two conversations in the same workspace differ here and nowhere else.
49
+ * `ancestors` are that session's fork lineage, nearest first: a fork inherits the directories its parent added
50
+ * (the product decision recorded in ADR-0013), so a root listed for any ancestor is listed for the fork too.
51
+ * Both are optional because an agentless call (a CLI, a config replay) has neither.
52
+ */
53
+ export interface SessionIdentity {
54
+ /** The asking session's id, when one is known. */
55
+ readonly sessionId?: string | undefined;
56
+ /** The asking session's fork ancestors, nearest first. */
57
+ readonly ancestors?: readonly string[] | undefined;
58
+ }
59
+ /** The target shape the tools consume; kept structural so this module imports no integration code. */
60
+ export interface SessionRootTarget {
61
+ readonly connectionId: string;
62
+ readonly root: string;
63
+ }
64
+ /** Outcome of {@link resolveSessionRemoteRoot}. */
65
+ export type SessionRootOutcome = {
66
+ readonly ok: true;
67
+ readonly target: SessionRootTarget;
68
+ }
69
+ /** Nothing this session added is a remote root: the caller keeps its own error. */
70
+ | {
71
+ readonly ok: false;
72
+ readonly reason: 'none';
73
+ }
74
+ /** Several added remote roots: refused by name rather than guessed. */
75
+ | {
76
+ readonly ok: false;
77
+ readonly reason: 'ambiguous';
78
+ readonly candidates: readonly SessionRootTarget[];
79
+ };
80
+ /**
81
+ * Compare two host paths the way the persisted `workspaces` field is written.
82
+ *
83
+ * Windows paths are case-insensitive, and the file stores whatever the platform reported at add time while
84
+ * the session header may spell the same directory with different case. Folding here is what keeps a
85
+ * `D:\Project\X` session from losing the directory it added as `d:\project\x`.
86
+ *
87
+ * @param left - one host path.
88
+ * @param right - the other host path.
89
+ * @returns whether they name the same directory.
90
+ */
91
+ export declare function sameWorkspacePath(left: string, right: string): boolean;
92
+ /**
93
+ * The remote roots one session added, in file order.
94
+ *
95
+ * @param entries - the persisted root entries.
96
+ * @param workspacePath - the session's workspace path (its cwd), or `undefined` when the platform gave none.
97
+ * @param identity - the session identity, when the platform could supply one (ADR-0013).
98
+ * @returns the added remote roots, each with its connection and absolute remote path.
99
+ */
100
+ export declare function remoteRootsAddedBy(entries: readonly SessionRootEntry[], workspacePath: string | undefined, identity?: SessionIdentity): readonly SessionRootTarget[];
101
+ /**
102
+ * Pick the remote root a session should target when its cwd names none.
103
+ *
104
+ * @param entries - the persisted root entries.
105
+ * @param workspacePath - the session's workspace path (its cwd).
106
+ * @param identity - the session identity, when the platform could supply one (ADR-0013).
107
+ * @returns the single added remote root, a refusal, or `none` when the session added no remote directory.
108
+ */
109
+ export declare function resolveSessionRemoteRoot(entries: readonly SessionRootEntry[], workspacePath: string | undefined, identity?: SessionIdentity): SessionRootOutcome;
110
+ /**
111
+ * The model-facing text for a session that added more than one remote directory.
112
+ *
113
+ * @param candidates - the added remote roots.
114
+ * @returns one sentence naming every candidate and the way to disambiguate.
115
+ */
116
+ export declare function ambiguousSessionRootMessage(candidates: readonly SessionRootTarget[]): string;
117
+ /**
118
+ * Whether one root may serve one session — the **access** rule, not the view rule.
119
+ *
120
+ * 「添加目录」 scopes a root to the workspace that added it (`workspaces`). Until now that was a visibility fact
121
+ * only: the panel listed the session's own roots while the router answered **any** registered root for **any**
122
+ * caller (`routed-fs.ts`: "Filtering is a view fact, not a sandbox"). With the routed filesystem mounted
123
+ * (`远程工作区-路由fs`, stage 2) that gap is an access gap — session B could read a directory only session A
124
+ * added, merely by spelling its path. This predicate closes it, and it is the one place the rule lives: the route
125
+ * table calls it for every resolution, so every file tool inherits it.
126
+ *
127
+ * **Two identities, in this order (ADR-0013).** When the root carries `sessions` **and** the caller has a
128
+ * session id, the session decides — the workspace path is not consulted. That is what separates two
129
+ * conversations opened on the same directory: they share a cwd and differ only here. Otherwise (an entry from
130
+ * before ADR-0013, or an agentless call) the workspace path stands in, and the root belongs to a session in
131
+ * exactly two ways:
132
+ *
133
+ * 1. it was **added to this workspace** — `workspaces` names the session's cwd ({@link sameWorkspacePath});
134
+ * 2. the session **lives on** the root — a remote root's anchor directory *is* a workspace path, so a session
135
+ * opened on that anchor (or inside it) is that workspace's session. This keeps a remote workspace usable
136
+ * when its root was registered before the `workspaces` grouping existed.
137
+ *
138
+ * Everything else is refused, **including a root with no `workspaces` at all**: the settings panel's unscoped
139
+ * roots are "added to no session" (its own copy says so), so a session has to opt in with 「添加目录」 first.
140
+ * That is a deliberate behaviour change — see ADR-0012 for the migration note.
141
+ *
142
+ * A caller with neither cwd nor session is not a session call — agentless config replay, a CLI — and keeps the
143
+ * plugin-wide view; widening a *session* silently would be the bug this function exists to stop.
144
+ *
145
+ * @param root - one registered root.
146
+ * @param cwd - the asking session's cwd, or `undefined` for an agentless call.
147
+ * @param identity - the asking session's identity, when the platform could supply one (ADR-0013).
148
+ * @returns whether the root may serve that caller.
149
+ */
150
+ export declare function rootVisibleToSession(root: RootRef, cwd: string | undefined, identity?: SessionIdentity): boolean;
151
+ //# sourceMappingURL=session-roots.d.ts.map
@@ -0,0 +1,73 @@
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-over-ssh/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
@@ -0,0 +1,80 @@
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