dsh-over-ssh 0.1.0 → 0.2.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 (250) hide show
  1. package/README.md +19 -17
  2. package/agents/agent-manifest.json +6 -6
  3. package/agents/linux-amd64/dsh-over-ssh-agent +0 -0
  4. package/agents/linux-arm64/dsh-over-ssh-agent +0 -0
  5. package/lib/client.js +422 -337
  6. package/lib/file-references.js +92 -35
  7. package/lib/fs.js +295 -113
  8. package/lib/index.js +6912 -4330
  9. package/lib/terminal-wrapper.js +9 -4
  10. package/lib/types/src/config.d.ts +156 -9
  11. package/lib/types/src/file-references.d.ts +0 -1
  12. package/lib/types/src/host/app/backend.d.ts +44 -3
  13. package/lib/types/src/host/app/recycle-bin/recycle-bin-contract.d.ts +0 -1
  14. package/lib/types/src/host/app/recycle-bin/recycle-bin-service.d.ts +0 -1
  15. package/lib/types/src/host/app/session-admin/session-admin-contract.d.ts +12 -1
  16. package/lib/types/src/host/app/session-admin/session-admin-service.d.ts +0 -1
  17. package/lib/types/src/host/app/transport/transport-selector.d.ts +0 -1
  18. package/lib/types/src/host/app/transport/transport-service.d.ts +0 -1
  19. package/lib/types/src/host/domain/capability.d.ts +0 -1
  20. package/lib/types/src/host/domain/errors.d.ts +0 -1
  21. package/lib/types/src/host/domain/roots/connection.d.ts +0 -1
  22. package/lib/types/src/host/domain/roots/root.d.ts +24 -9
  23. package/lib/types/src/host/domain/roots/session-roots.d.ts +23 -1
  24. package/lib/types/src/host/domain/roots/ssh-config.d.ts +0 -1
  25. package/lib/types/src/host/domain/session/session-admin.d.ts +0 -1
  26. package/lib/types/src/host/domain/transfer/recycle-bin.d.ts +0 -1
  27. package/lib/types/src/host/domain/transfer/transfer.d.ts +0 -1
  28. package/lib/types/src/host/infra/transport/agent/agent-installer.d.ts +0 -1
  29. package/lib/types/src/host/infra/transport/forward.d.ts +75 -0
  30. package/lib/types/src/host/infra/transport/ssh/ssh2-transport.d.ts +5 -1
  31. package/lib/types/src/host/infra/transport/transport-base.d.ts +0 -1
  32. package/lib/types/src/host/infra/transport/transport.d.ts +42 -2
  33. package/lib/types/src/host/integration/agent/agent-preflight.d.ts +0 -1
  34. package/lib/types/src/host/integration/fs/file-reference-provider.d.ts +0 -1
  35. package/lib/types/src/host/integration/fs/fs-provider.d.ts +18 -3
  36. package/lib/types/src/host/integration/transfer/transfer-service.d.ts +0 -1
  37. package/lib/types/src/index.d.ts +23 -1
  38. package/lib/types/src/local-bridge-refs.d.ts +0 -1
  39. package/lib/types/src/local-bridge.d.ts +0 -1
  40. package/lib/types/src/shared/root-keys.d.ts +120 -0
  41. package/lib/types/src/wiring/helpers.d.ts +0 -1
  42. package/package.json +5 -3
  43. package/lib/types/src/client/controls.d.ts +0 -131
  44. package/lib/types/src/client/core/card-focus-trap.d.ts +0 -45
  45. package/lib/types/src/client/core/controls.d.ts +0 -164
  46. package/lib/types/src/client/core/latest.d.ts +0 -48
  47. package/lib/types/src/client/core/locale.d.ts +0 -38
  48. package/lib/types/src/client/core/message.d.ts +0 -37
  49. package/lib/types/src/client/core/panel-model.d.ts +0 -134
  50. package/lib/types/src/client/core/portal.d.ts +0 -266
  51. package/lib/types/src/client/core/remote.d.ts +0 -84
  52. package/lib/types/src/client/core/styles.d.ts +0 -458
  53. package/lib/types/src/client/file-tree-io.d.ts +0 -19
  54. package/lib/types/src/client/file-tree.d.ts +0 -271
  55. package/lib/types/src/client/flows/directory-browser.d.ts +0 -587
  56. package/lib/types/src/client/flows/session-directory.d.ts +0 -415
  57. package/lib/types/src/client/flows/workspace-directory-flow.d.ts +0 -480
  58. package/lib/types/src/client/locale.d.ts +0 -31
  59. package/lib/types/src/client/panel-model.d.ts +0 -37
  60. package/lib/types/src/client/panel.d.ts +0 -77
  61. package/lib/types/src/client/roots/connection-test.d.ts +0 -69
  62. package/lib/types/src/client/roots/roots-panel-state.d.ts +0 -307
  63. package/lib/types/src/client/roots/roots-panel-view.d.ts +0 -280
  64. package/lib/types/src/client/roots/roots-panel.d.ts +0 -364
  65. package/lib/types/src/client/roots/transfer-panel-view.d.ts +0 -35
  66. package/lib/types/src/client/roots/transfer-panel.d.ts +0 -241
  67. package/lib/types/src/client/roots/transfer-progress.d.ts +0 -67
  68. package/lib/types/src/client/roots-panel-view.d.ts +0 -236
  69. package/lib/types/src/client/roots-panel.d.ts +0 -347
  70. package/lib/types/src/client/session/session-panel-view.d.ts +0 -336
  71. package/lib/types/src/client/session/session-panel.d.ts +0 -322
  72. package/lib/types/src/client/session/session-remote-mark.d.ts +0 -110
  73. package/lib/types/src/client/session/session-row-actions.d.ts +0 -398
  74. package/lib/types/src/client/session-panel.d.ts +0 -152
  75. package/lib/types/src/client/styles.d.ts +0 -252
  76. package/lib/types/src/client/workspace-directory-flow.d.ts +0 -342
  77. package/lib/types/src/fs.d.ts +0 -46
  78. package/lib/types/src/host/app/recycle-bin/recycle-bin-move.d.ts +0 -18
  79. package/lib/types/src/host/app/recycle-bin/recycle-bin-world.d.ts +0 -38
  80. package/lib/types/src/host/app/recycle-bin-contract.d.ts +0 -215
  81. package/lib/types/src/host/app/recycle-bin-move.d.ts +0 -18
  82. package/lib/types/src/host/app/recycle-bin-service.d.ts +0 -45
  83. package/lib/types/src/host/app/recycle-bin-world.d.ts +0 -35
  84. package/lib/types/src/host/app/roots/roots-api-connections.d.ts +0 -49
  85. package/lib/types/src/host/app/roots/roots-api-contract.d.ts +0 -369
  86. package/lib/types/src/host/app/roots/roots-api-directories.d.ts +0 -13
  87. package/lib/types/src/host/app/roots/roots-api-http.d.ts +0 -77
  88. package/lib/types/src/host/app/roots/roots-api-long-poll.d.ts +0 -130
  89. package/lib/types/src/host/app/roots/roots-api-projections.d.ts +0 -102
  90. package/lib/types/src/host/app/roots/roots-api-roots.d.ts +0 -13
  91. package/lib/types/src/host/app/roots/roots-api-state.d.ts +0 -22
  92. package/lib/types/src/host/app/roots/roots-api-transfers.d.ts +0 -26
  93. package/lib/types/src/host/app/roots/roots-api.d.ts +0 -55
  94. package/lib/types/src/host/app/roots/user-roots.d.ts +0 -552
  95. package/lib/types/src/host/app/roots-api-connections.d.ts +0 -49
  96. package/lib/types/src/host/app/roots-api-contract.d.ts +0 -339
  97. package/lib/types/src/host/app/roots-api-directories.d.ts +0 -13
  98. package/lib/types/src/host/app/roots-api-http.d.ts +0 -77
  99. package/lib/types/src/host/app/roots-api-projections.d.ts +0 -102
  100. package/lib/types/src/host/app/roots-api-roots.d.ts +0 -13
  101. package/lib/types/src/host/app/roots-api-state.d.ts +0 -22
  102. package/lib/types/src/host/app/roots-api-support.d.ts +0 -182
  103. package/lib/types/src/host/app/roots-api-transfers.d.ts +0 -13
  104. package/lib/types/src/host/app/roots-api.d.ts +0 -55
  105. package/lib/types/src/host/app/routed-fs.d.ts +0 -239
  106. package/lib/types/src/host/app/serialize.d.ts +0 -37
  107. package/lib/types/src/host/app/session-admin/session-admin-context.d.ts +0 -36
  108. package/lib/types/src/host/app/session-admin/session-admin-delete.d.ts +0 -34
  109. package/lib/types/src/host/app/session-admin-context.d.ts +0 -36
  110. package/lib/types/src/host/app/session-admin-contract.d.ts +0 -214
  111. package/lib/types/src/host/app/session-admin-delete.d.ts +0 -34
  112. package/lib/types/src/host/app/session-admin-service.d.ts +0 -38
  113. package/lib/types/src/host/app/takeover/takeover-plan.d.ts +0 -227
  114. package/lib/types/src/host/app/takeover/takeover-rows.d.ts +0 -55
  115. package/lib/types/src/host/app/transport-selector.d.ts +0 -98
  116. package/lib/types/src/host/app/transport-service.d.ts +0 -75
  117. package/lib/types/src/host/app/user-roots.d.ts +0 -461
  118. package/lib/types/src/host/domain/bin-location.d.ts +0 -79
  119. package/lib/types/src/host/domain/connection.d.ts +0 -95
  120. package/lib/types/src/host/domain/glob-pattern.d.ts +0 -62
  121. package/lib/types/src/host/domain/literal-text.d.ts +0 -63
  122. package/lib/types/src/host/domain/path-grammar.d.ts +0 -84
  123. package/lib/types/src/host/domain/recycle-bin.d.ts +0 -337
  124. package/lib/types/src/host/domain/root.d.ts +0 -74
  125. package/lib/types/src/host/domain/roots/bin-location.d.ts +0 -91
  126. package/lib/types/src/host/domain/roots/route-table.d.ts +0 -102
  127. package/lib/types/src/host/domain/route-table.d.ts +0 -80
  128. package/lib/types/src/host/domain/session/terminal-target.d.ts +0 -136
  129. package/lib/types/src/host/domain/session-admin.d.ts +0 -330
  130. package/lib/types/src/host/domain/shell.d.ts +0 -47
  131. package/lib/types/src/host/domain/ssh-config.d.ts +0 -73
  132. package/lib/types/src/host/domain/terminal-target.d.ts +0 -132
  133. package/lib/types/src/host/domain/tool-requests.d.ts +0 -369
  134. package/lib/types/src/host/domain/transfer.d.ts +0 -260
  135. package/lib/types/src/host/infra/backend/local-backend.d.ts +0 -84
  136. package/lib/types/src/host/infra/backend/local-version.d.ts +0 -43
  137. package/lib/types/src/host/infra/backend/platform-local-backend.d.ts +0 -89
  138. package/lib/types/src/host/infra/backend/remote-backend.d.ts +0 -239
  139. package/lib/types/src/host/infra/cache/lru.d.ts +0 -52
  140. package/lib/types/src/host/infra/cache/versioned.d.ts +0 -77
  141. package/lib/types/src/host/infra/temp-name.d.ts +0 -19
  142. package/lib/types/src/host/infra/transport/agent/agent-protocol-client.d.ts +0 -132
  143. package/lib/types/src/host/infra/transport/agent/agent-protocol-codec.d.ts +0 -140
  144. package/lib/types/src/host/infra/transport/agent/agent-protocol-errors.d.ts +0 -41
  145. package/lib/types/src/host/infra/transport/agent/agent-protocol-frames.d.ts +0 -197
  146. package/lib/types/src/host/infra/transport/agent/agent-protocol-types.d.ts +0 -564
  147. package/lib/types/src/host/infra/transport/agent/agent-transport.d.ts +0 -418
  148. package/lib/types/src/host/infra/transport/agent-installer.d.ts +0 -121
  149. package/lib/types/src/host/infra/transport/agent-protocol-client.d.ts +0 -123
  150. package/lib/types/src/host/infra/transport/agent-protocol-codec.d.ts +0 -131
  151. package/lib/types/src/host/infra/transport/agent-protocol-errors.d.ts +0 -41
  152. package/lib/types/src/host/infra/transport/agent-protocol-frames.d.ts +0 -197
  153. package/lib/types/src/host/infra/transport/agent-protocol-types.d.ts +0 -509
  154. package/lib/types/src/host/infra/transport/agent-transport.d.ts +0 -353
  155. package/lib/types/src/host/infra/transport/alias-transport.d.ts +0 -206
  156. package/lib/types/src/host/infra/transport/askpass-fs.d.ts +0 -20
  157. package/lib/types/src/host/infra/transport/askpass.d.ts +0 -133
  158. package/lib/types/src/host/infra/transport/command-pool.d.ts +0 -61
  159. package/lib/types/src/host/infra/transport/fs-commands.d.ts +0 -140
  160. package/lib/types/src/host/infra/transport/git-status-route.d.ts +0 -63
  161. package/lib/types/src/host/infra/transport/git-status.d.ts +0 -101
  162. package/lib/types/src/host/infra/transport/node-spawn.d.ts +0 -26
  163. package/lib/types/src/host/infra/transport/session-window.d.ts +0 -51
  164. package/lib/types/src/host/infra/transport/shell-quote.d.ts +0 -59
  165. package/lib/types/src/host/infra/transport/ssh/alias-transport.d.ts +0 -206
  166. package/lib/types/src/host/infra/transport/ssh/askpass-fs.d.ts +0 -20
  167. package/lib/types/src/host/infra/transport/ssh/askpass.d.ts +0 -133
  168. package/lib/types/src/host/infra/transport/ssh/shell-quote.d.ts +0 -62
  169. package/lib/types/src/host/infra/transport/ssh/ssh-argv.d.ts +0 -43
  170. package/lib/types/src/host/infra/transport/ssh-argv.d.ts +0 -43
  171. package/lib/types/src/host/infra/transport/ssh2-transport.d.ts +0 -302
  172. package/lib/types/src/host/integration/agent/agent-install-io.d.ts +0 -33
  173. package/lib/types/src/host/integration/agent/askpass-registry.d.ts +0 -66
  174. package/lib/types/src/host/integration/agent-install-io.d.ts +0 -33
  175. package/lib/types/src/host/integration/agent-preflight.d.ts +0 -56
  176. package/lib/types/src/host/integration/anchor-directory.d.ts +0 -56
  177. package/lib/types/src/host/integration/askpass-registry.d.ts +0 -59
  178. package/lib/types/src/host/integration/bin-root-resolver.d.ts +0 -81
  179. package/lib/types/src/host/integration/connection-test.d.ts +0 -73
  180. package/lib/types/src/host/integration/file-reference-provider.d.ts +0 -115
  181. package/lib/types/src/host/integration/fs/git-status-wiring.d.ts +0 -21
  182. package/lib/types/src/host/integration/fs/platform-errors.d.ts +0 -94
  183. package/lib/types/src/host/integration/fs/remote-file-tools.d.ts +0 -369
  184. package/lib/types/src/host/integration/fs/remote-index.d.ts +0 -177
  185. package/lib/types/src/host/integration/fs-provider.d.ts +0 -226
  186. package/lib/types/src/host/integration/lsp/lsp-framing.d.ts +0 -38
  187. package/lib/types/src/host/integration/lsp/lsp-registry.d.ts +0 -67
  188. package/lib/types/src/host/integration/lsp/lsp-session.d.ts +0 -130
  189. package/lib/types/src/host/integration/management-tools.d.ts +0 -64
  190. package/lib/types/src/host/integration/recycle-bin-service.d.ts +0 -247
  191. package/lib/types/src/host/integration/remote/connection-test.d.ts +0 -73
  192. package/lib/types/src/host/integration/remote/remote-face.d.ts +0 -127
  193. package/lib/types/src/host/integration/remote/remote-tool-backend.d.ts +0 -108
  194. package/lib/types/src/host/integration/remote-face.d.ts +0 -127
  195. package/lib/types/src/host/integration/remote-file-tools.d.ts +0 -241
  196. package/lib/types/src/host/integration/remote-home.d.ts +0 -69
  197. package/lib/types/src/host/integration/remote-index.d.ts +0 -165
  198. package/lib/types/src/host/integration/remote-job-tools.d.ts +0 -295
  199. package/lib/types/src/host/integration/remote-tool-backend.d.ts +0 -97
  200. package/lib/types/src/host/integration/remote-tools.d.ts +0 -206
  201. package/lib/types/src/host/integration/root-registry.d.ts +0 -115
  202. package/lib/types/src/host/integration/roots/anchor-directory.d.ts +0 -56
  203. package/lib/types/src/host/integration/roots/bin-root-resolver.d.ts +0 -81
  204. package/lib/types/src/host/integration/roots/remote-home.d.ts +0 -69
  205. package/lib/types/src/host/integration/roots/root-registry.d.ts +0 -144
  206. package/lib/types/src/host/integration/roots/roots-wiring.d.ts +0 -219
  207. package/lib/types/src/host/integration/roots/session-root-index.d.ts +0 -52
  208. package/lib/types/src/host/integration/roots-api.d.ts +0 -407
  209. package/lib/types/src/host/integration/roots-wiring.d.ts +0 -211
  210. package/lib/types/src/host/integration/session/session-api.d.ts +0 -88
  211. package/lib/types/src/host/integration/session/session-bin-lock.d.ts +0 -80
  212. package/lib/types/src/host/integration/session/session-header.d.ts +0 -151
  213. package/lib/types/src/host/integration/session/session-labels.d.ts +0 -56
  214. package/lib/types/src/host/integration/session/session-route-wiring.d.ts +0 -50
  215. package/lib/types/src/host/integration/session-admin-service.d.ts +0 -236
  216. package/lib/types/src/host/integration/session-api.d.ts +0 -81
  217. package/lib/types/src/host/integration/session-bin-lock.d.ts +0 -80
  218. package/lib/types/src/host/integration/session-header.d.ts +0 -118
  219. package/lib/types/src/host/integration/session-labels.d.ts +0 -56
  220. package/lib/types/src/host/integration/session-route-wiring.d.ts +0 -50
  221. package/lib/types/src/host/integration/session-scope.d.ts +0 -113
  222. package/lib/types/src/host/integration/session-tools.d.ts +0 -44
  223. package/lib/types/src/host/integration/strict-arguments.d.ts +0 -79
  224. package/lib/types/src/host/integration/takeover/takeover-api.d.ts +0 -62
  225. package/lib/types/src/host/integration/takeover/takeover-route-wiring.d.ts +0 -60
  226. package/lib/types/src/host/integration/takeover/takeover-service.d.ts +0 -167
  227. package/lib/types/src/host/integration/terminal-manifest.d.ts +0 -94
  228. package/lib/types/src/host/integration/tools/management-tools.d.ts +0 -245
  229. package/lib/types/src/host/integration/tools/remote-job-tools.d.ts +0 -316
  230. package/lib/types/src/host/integration/tools/remote-lsp-tools.d.ts +0 -43
  231. package/lib/types/src/host/integration/tools/remote-tools.d.ts +0 -236
  232. package/lib/types/src/host/integration/tools/session-target.d.ts +0 -52
  233. package/lib/types/src/host/integration/tools/session-tools.d.ts +0 -46
  234. package/lib/types/src/host/integration/tools/strict-arguments.d.ts +0 -79
  235. package/lib/types/src/host/integration/tools/terminal-manifest.d.ts +0 -94
  236. package/lib/types/src/host/integration/transfer/remote-copy.d.ts +0 -66
  237. package/lib/types/src/host/integration/transfer/transfer-adapters.d.ts +0 -48
  238. package/lib/types/src/host/integration/transfer/transfer-tracker.d.ts +0 -106
  239. package/lib/types/src/host/integration/transfer-adapters.d.ts +0 -44
  240. package/lib/types/src/host/integration/transfer-service.d.ts +0 -164
  241. package/lib/types/src/host/integration/transfer-tracker.d.ts +0 -106
  242. package/lib/types/src/host/integration/user-roots.d.ts +0 -461
  243. package/lib/types/src/wiring/connections.d.ts +0 -189
  244. package/lib/types/src/wiring/fs.d.ts +0 -13
  245. package/lib/types/src/wiring/install-memo.d.ts +0 -32
  246. package/lib/types/src/wiring/legacy-dirs.d.ts +0 -62
  247. package/lib/types/src/wiring/package-paths.d.ts +0 -35
  248. package/lib/types/src/wiring/roots.d.ts +0 -21
  249. package/lib/types/src/wiring/state-dir.d.ts +0 -29
  250. package/lib/types/src/wiring/tools.d.ts +0 -35
@@ -1,260 +0,0 @@
1
- /**
2
- * Pure transfer arithmetic: chunk plans, progress maths, root-confined remote targets and file-name
3
- * validation.
4
- *
5
- * Kept in the domain layer on purpose (AGENTS.md 红线 2): no `@deepseek-ai/*` import, no process, no
6
- * socket and no clock — every number it needs is a parameter. That is what lets the whole transfer
7
- * policy (offsets, percentages, resume decisions, `..` folding rules) be tested offline in
8
- * milliseconds, and it is why the seam in `integration/transfer-service.ts` only has to move bytes.
9
- *
10
- * Three behaviours are deliberately *deterministic* rather than "reasonable":
11
- *
12
- * - **A zero-byte file has zero chunks** but still needs one empty write to create the target
13
- * (`needsEmptyWrite`), so "upload an empty file" is a real operation rather than a no-op.
14
- * - **A plan is bounded**: the chunk list is materialised, so `MAX_TRANSFER_CHUNKS` caps it and an
15
- * absurd `chunkBytes` fails loudly instead of exhausting memory.
16
- * - **`..` never escapes the root**: the spelling is folded the way `joinUnderRoot` folds it, and the
17
- * result is re-checked against the root, so a future change to the folder cannot silently open a
18
- * hole (see {@link assertInsideRoot}).
19
- *
20
- * @module dsh-remote-workspace/domain/transfer
21
- */
22
- import { formatBytes, formatDuration } from '../../shared/format.ts';
23
- import { SshDevError, type SshDevErrorCode } from './errors.ts';
24
- /**
25
- * Upper bound on the number of chunks one plan may contain.
26
- *
27
- * This is **not** a deployment knob — the deployment knob is `chunkBytes`. It exists because
28
- * {@link planTransfer} materialises the chunk list, so a tiny `chunkBytes` on a huge file would
29
- * otherwise allocate until the process dies.
30
- */
31
- export declare const MAX_TRANSFER_CHUNKS = 100000;
32
- /** One slice of a transfer: bytes `[offset, offset + length)` of the source. */
33
- export interface TransferChunk {
34
- /** 0-based position of this chunk inside the plan it was produced by. */
35
- readonly index: number;
36
- /** Byte offset in the source. */
37
- readonly offset: number;
38
- /** Number of bytes to move; always ≥ 1. */
39
- readonly length: number;
40
- }
41
- /** A complete, finite plan for moving `totalBytes`. */
42
- export interface TransferPlan {
43
- readonly totalBytes: number;
44
- readonly chunkBytes: number;
45
- /** Chunks in increasing offset order; empty for a zero-byte source. */
46
- readonly chunks: readonly TransferChunk[];
47
- /** Whether the target still has to be created with one write of zero bytes. */
48
- readonly needsEmptyWrite: boolean;
49
- }
50
- /** Why a transfer could not do something the caller asked for, or could not report a digest. */
51
- export interface TransferDegradation {
52
- /** Stable code, so a caller can branch without reading the hint. */
53
- readonly code: 'resume-unsupported' | 'resume-unusable' | 'digest-unavailable';
54
- /** Actionable explanation for the user. */
55
- readonly hint: string;
56
- }
57
- /** What to do about a possibly-partial target before starting. */
58
- export interface ResumeDecision {
59
- /** Byte offset the transfer starts at. */
60
- readonly startOffset: number;
61
- /** Whether bytes already present on the target are being kept. */
62
- readonly resumed: boolean;
63
- /** Reasons the transfer is not resuming; empty when it resumes or was never asked to. */
64
- readonly degradations: readonly TransferDegradation[];
65
- }
66
- /**
67
- * Cut `totalBytes` into contiguous chunks of at most `chunkBytes`.
68
- *
69
- * @param totalBytes - source length; 0 produces an empty plan plus `needsEmptyWrite`.
70
- * @param chunkBytes - per-chunk cap; must be a safe integer ≥ 1.
71
- * @returns the plan, with offsets starting at 0 and covering exactly `totalBytes`.
72
- * @throws SshDevError `ROOT_INVALID` when either argument is not a usable integer, or when the plan
73
- * would exceed {@link MAX_TRANSFER_CHUNKS}.
74
- */
75
- export declare function planTransfer(totalBytes: number, chunkBytes: number): TransferPlan;
76
- /**
77
- * The chunks still to move when the target already holds `startOffset` bytes.
78
- *
79
- * The first returned chunk is trimmed to start exactly at `startOffset`, and indices are renumbered
80
- * from 0 because the caller reports "how many writes did *this* transfer make".
81
- *
82
- * @param plan - the full plan.
83
- * @param startOffset - bytes already present; ≤ 0 means "nothing", ≥ `totalBytes` means "nothing left".
84
- * @returns the remaining chunks; the input plan is not mutated.
85
- */
86
- export declare function remainingChunks(plan: TransferPlan, startOffset: number): readonly TransferChunk[];
87
- /**
88
- * Decide where a transfer starts, given what the target already holds.
89
- *
90
- * Precedence (each rule is a test): no resume requested → 0; zero-byte source → 0; the injected
91
- * implementation cannot resume → 0 with `resume-unsupported`; nothing usable on the target → 0;
92
- * target is at least as long as the source → 0 with `resume-unusable` (a same-size target is **never**
93
- * assumed complete, because nothing here can verify its content); otherwise the target's length.
94
- *
95
- * @param input - source length, target length (`undefined` when absent/unknown) and the two flags.
96
- * @returns the decision, including why resume did not happen.
97
- */
98
- export declare function decideStartOffset(input: {
99
- readonly totalBytes: number;
100
- readonly partialBytes: number | undefined;
101
- readonly resumeRequested: boolean;
102
- readonly resumeSupported: boolean;
103
- }): ResumeDecision;
104
- /** Progress of one transfer at one instant. */
105
- export interface TransferProgress {
106
- readonly transferredBytes: number;
107
- readonly totalBytes: number;
108
- /** 0–100, rounded to one decimal; a zero-byte transfer is 100 (nothing left to do). */
109
- readonly percent: number;
110
- /** Average rate since the transfer started; 0 when no time has passed. */
111
- readonly bytesPerSecond: number;
112
- readonly elapsedMs: number;
113
- /** Estimated time left; `null` when no rate is known yet, 0 when finished. */
114
- readonly remainingMs: number | null;
115
- }
116
- /**
117
- * Compute progress from the two byte counts and elapsed time.
118
- *
119
- * Pure and monotonic: for a fixed `totalBytes` and a non-decreasing `transferredBytes`, every field
120
- * is non-decreasing.
121
- *
122
- * @param input - bytes moved, bytes expected and milliseconds since the start.
123
- * @returns the progress snapshot.
124
- */
125
- export declare function progressOf(input: {
126
- readonly transferredBytes: number;
127
- readonly totalBytes: number;
128
- readonly elapsedMs: number;
129
- }): TransferProgress;
130
- /** Binary byte units, largest last. */
131
- /**
132
- * One-line progress text for the UI or a CLI.
133
- *
134
- * @param progress - a snapshot from {@link progressOf}.
135
- * @param options - `label` names the direction (`上传`/`下载`); defaults to `传输`.
136
- * @returns e.g. `上传 40.0%(4 B/10 B,2 B/s,剩余约 3.0s)`, or `…,耗时 350ms)` once finished.
137
- */
138
- export declare function formatProgress(progress: TransferProgress, options?: {
139
- readonly label?: string;
140
- }): string;
141
- /** A remote target resolved inside one root. */
142
- export interface RemoteTarget {
143
- /** Normalized root the target lives in. */
144
- readonly root: string;
145
- /** Root-absolute POSIX path of the target. */
146
- readonly absolute: string;
147
- /** The spelling requested, with `.`/`..` folded (may differ from `absolute`). */
148
- readonly relative: string;
149
- /** Final path segment. */
150
- readonly name: string;
151
- }
152
- /**
153
- * Fold `root + requested` into a root-absolute path that cannot leave the root.
154
- *
155
- * The folding rule is exactly `joinUnderRoot`'s (imported, not re-implemented): a leading `/` is
156
- * treated as root-relative, `.` is dropped, and `..` pops within the *requested* part only — extra
157
- * `..` at the top are ignored rather than climbing out of the root. The result is re-checked with
158
- * {@link assertInsideRoot} so a future change to the folder cannot silently open a hole.
159
- *
160
- * @param root - absolute POSIX root directory.
161
- * @param requested - remote spelling relative to the root (absolute spellings are root-relative too).
162
- * @returns the resolved target.
163
- * @throws SshDevError `ROOT_INVALID` for an unusable root/request, `ROOT_OUTSIDE` when the request
164
- * names the root itself, ends with `/` (a directory spelling) or somehow escapes the root.
165
- */
166
- export declare function resolveRemoteTarget(root: string, requested: string): RemoteTarget;
167
- /**
168
- * Check that a root-absolute path really is inside its root.
169
- *
170
- * @param root - absolute POSIX root directory.
171
- * @param absolute - root-absolute candidate.
172
- * @throws SshDevError `ROOT_OUTSIDE` when the candidate is not the root or below it.
173
- */
174
- export declare function assertInsideRoot(root: string, absolute: string): void;
175
- /**
176
- * Validate one remote file name (a single path segment).
177
- *
178
- * @param name - the segment to check.
179
- * @returns the name, unchanged.
180
- * @throws SshDevError `ROOT_INVALID` for an empty/blank name, one containing `/` or NUL;
181
- * `ROOT_OUTSIDE` for `.`/`..`, which name a directory rather than a file.
182
- */
183
- export declare function validateRemoteFileName(name: string): string;
184
- /**
185
- * Validate one local file name for the platform the download will land on.
186
- *
187
- * The platform is a parameter rather than `process.platform` so the Windows rules stay testable on
188
- * any host (see [`run-tests.md`] on platform-dependent assertions).
189
- *
190
- * @param name - the segment to check (a base name, never a path).
191
- * @param options - `platform` selects the rule set; defaults to the running platform.
192
- * @returns the name, unchanged.
193
- * @throws SshDevError `ROOT_INVALID` for an empty/blank name, a name containing a path separator or
194
- * (on win32) a forbidden character, a reserved device name or a trailing dot/space;
195
- * `ROOT_OUTSIDE` for `.`/`..`.
196
- */
197
- export declare function validateLocalFileName(name: string, options?: {
198
- readonly platform?: 'win32' | 'posix';
199
- }): string;
200
- /**
201
- * The final segment of a local path, in either separator spelling.
202
- *
203
- * @param path - host path.
204
- * @returns the base name; empty when the path has no segment.
205
- */
206
- export declare function localFileBasename(path: string): string;
207
- /**
208
- * What went wrong in a transfer, precisely.
209
- *
210
- * This is **not** an error code (the code table lives in `domain/errors.ts` and is unchanged by this
211
- * module). It is the `details.kind` of the thrown {@link SshDevError}, the same shape
212
- * `sshFailureToError` uses to keep "which precise cause" separate from "which stable code".
213
- */
214
- export type TransferFailureKind =
215
- /** Arguments or spelling are unusable (empty root, empty name, bad chunk cap). */
216
- 'invalid-request'
217
- /** The target names a directory, the root itself, or leaves the root. */
218
- | 'outside-root' | 'local-stat-failed' | 'local-read-failed' | 'local-write-failed' | 'remote-stat-failed' | 'remote-not-found' | 'remote-read-failed' | 'remote-short-read' | 'remote-write-failed'
219
- /** The source changed size while it was being read. */
220
- | 'source-changed' | 'verify-failed' | 'cancelled';
221
- /** Every {@link TransferFailureKind}, for exhaustive checks and tests. */
222
- export declare const TRANSFER_FAILURE_KINDS: readonly ["invalid-request", "outside-root", "local-stat-failed", "local-read-failed", "local-write-failed", "remote-stat-failed", "remote-not-found", "remote-read-failed", "remote-short-read", "remote-write-failed", "source-changed", "verify-failed", "cancelled"];
223
- /**
224
- * Map a precise transfer failure onto one of the **existing** plugin error codes.
225
- *
226
- * No code is invented here (see the audit): a local I/O failure has no dedicated code in
227
- * `SSH_DEV_ERROR_CODES`, so it maps to `ROOT_INVALID` ("this path is unusable") and stays
228
- * distinguishable through `details.kind`.
229
- *
230
- * @param kind - the precise failure.
231
- * @returns the stable code a caller can branch on.
232
- */
233
- export declare function failureCodeFor(kind: TransferFailureKind): SshDevErrorCode;
234
- /**
235
- * Build the plugin failure for one transfer step.
236
- *
237
- * @param kind - precise failure kind, recorded as `details.kind`.
238
- * @param message - actionable Chinese message (what failed and what to do).
239
- * @param details - structured context; must not contain secrets.
240
- * @returns an {@link SshDevError} carrying the mapped code.
241
- */
242
- export declare function transferFailure(kind: TransferFailureKind, message: string, details?: Readonly<Record<string, unknown>>): SshDevError;
243
- /**
244
- * Test whether a value is a transfer failure (optionally of one precise kind).
245
- *
246
- * @param error - candidate.
247
- * @param kind - when given, also require `details.kind` to equal it.
248
- * @returns whether the value is a plugin error produced by this module.
249
- */
250
- export declare function isTransferFailure(error: unknown, kind?: TransferFailureKind): boolean;
251
- /**
252
- * Compare two digests tolerantly (hex digests arrive with mixed case and stray whitespace).
253
- *
254
- * @param left - one digest, or `undefined`.
255
- * @param right - the other digest, or `undefined`.
256
- * @returns whether both are present and equal ignoring case and surrounding whitespace.
257
- */
258
- export declare function digestsMatch(left: string | undefined, right: string | undefined): boolean;
259
- export { formatBytes, formatDuration };
260
- //# sourceMappingURL=transfer.d.ts.map
@@ -1,84 +0,0 @@
1
- /**
2
- * The local backend: one host directory served through `node:fs`.
3
- *
4
- * Reads are capped before allocating, writes go through a temporary file plus `rename` so a crash
5
- * cannot leave a half-written file, and a version token is derived from size + mtime so the router
6
- * can revalidate a cached body with a single `stat`.
7
- *
8
- * @module dsh-over-ssh/infra/backend/local-backend
9
- */
10
- import type { Backend, BackendEntry, BackendRead, BackendSandboxMode, BackendStat, BackendText, BackendWrite } from '../../app/backend.ts';
11
- /** Options for the local backend. */
12
- export interface LocalBackendOptions {
13
- /** Backend id (the router namespaces target keys with it). */
14
- readonly id: string;
15
- readonly sandboxMode?: BackendSandboxMode;
16
- /** Hard cap for a single read; the router passes its own, this is the floor. */
17
- readonly maxReadBytes?: number;
18
- /**
19
- * The per-entry `stat` used by {@link LocalBackend.list}, injected so a test can observe the loop's shape
20
- * (how many stats are in flight at once) without timing anything. Defaults to `node:fs/promises.stat`.
21
- */
22
- readonly statEntry?: (path: string) => Promise<{
23
- readonly size: number;
24
- readonly mtimeMs: number;
25
- readonly ino?: number;
26
- }>;
27
- }
28
- /** Local filesystem access for one root. */
29
- export declare class LocalBackend implements Backend {
30
- #private;
31
- readonly id: string;
32
- readonly sandboxMode: BackendSandboxMode | undefined;
33
- readonly watches = false;
34
- constructor(options: LocalBackendOptions);
35
- stat(absolute: string, signal?: AbortSignal): Promise<BackendStat>;
36
- /** Stat the entry itself, so a symlink is reported as a symlink rather than its target. */
37
- lstat(absolute: string, signal?: AbortSignal): Promise<BackendStat>;
38
- list(absolute: string, signal?: AbortSignal): Promise<readonly BackendEntry[]>;
39
- readBytes(absolute: string, maxBytes: number, signal?: AbortSignal): Promise<BackendRead>;
40
- readText(absolute: string, maxBytes: number, signal?: AbortSignal): Promise<BackendText>;
41
- writeText(absolute: string, text: string, expectedVersion: string | undefined, signal?: AbortSignal): Promise<BackendWrite>;
42
- /** Ensure a directory exists (used when creating roots and parents). */
43
- ensureDirectory(absolute: string): Promise<void>;
44
- /**
45
- * Create a directory (FR-2.2.2).
46
- *
47
- * `recursive: false` maps onto `node:fs`'s own `recursive: false`, which already fails with `EEXIST` on an
48
- * existing path and `ENOENT` on a missing parent — the two refusals the contract promises.
49
- */
50
- mkdir(absolute: string, options: {
51
- readonly recursive: boolean;
52
- }, _signal?: AbortSignal): Promise<void>;
53
- /**
54
- * Rename/move a path (FR-2.2.2), refusing to clobber.
55
- *
56
- * `node:fs`'s `rename` **silently replaces** the destination, which the contract forbids, so existence is
57
- * checked first. The check-then-rename window is inherent to POSIX `rename(2)`; on Windows `rename` itself
58
- * fails with `EEXIST`/`EPERM`, so the guard closes it there and narrows it elsewhere.
59
- */
60
- rename(from: string, to: string, options: {
61
- readonly createParents: boolean;
62
- }, _signal?: AbortSignal): Promise<void>;
63
- /**
64
- * Remove a path (FR-2.2.2). `recursive: false` refuses a directory outright.
65
- *
66
- * The non-recursive branch checks the type first rather than relying on `unlink`, whose errno for a
67
- * directory differs across platforms (Windows reports `EPERM` where POSIX reports `EISDIR`).
68
- */
69
- remove(absolute: string, options: {
70
- readonly recursive: boolean;
71
- }, _signal?: AbortSignal): Promise<void>;
72
- /**
73
- * Change one path's permission bits.
74
- *
75
- * `mode` is the same 3-4 digit octal string the tool layer validated, converted here with base 8 — passing
76
- * it straight to `fs.chmod` as a number would silently reinterpret `"755"` as decimal 755.
77
- */
78
- chmod(absolute: string, mode: string, _signal?: AbortSignal): Promise<void>;
79
- processPath(absolute: string): string;
80
- fileUrl(absolute: string): string;
81
- }
82
- /** Exported for tests that need a raw full read. */
83
- export declare function readFileFully(path: string): Promise<Buffer>;
84
- //# sourceMappingURL=local-backend.d.ts.map
@@ -1,43 +0,0 @@
1
- /**
2
- * 本地路径的版本令牌:`size:mtimeMs:ino`。
3
- *
4
- * ## 为什么不能只用 `size:mtime`
5
- *
6
- * mtime 的精度和两次写入之间的间隔不匹配。本仓库原来有三种写法,一种比一种粗:
7
- *
8
- * | 位置 | 原写法 | 粒度 |
9
- * |---|---|---|
10
- * | `fs-provider` 列目录 | `size:秒` | **秒** |
11
- * | `transfer-adapters` 本地 stat | `size:秒` | **秒** |
12
- * | `local-backend` stat | `size:毫秒` | 毫秒 |
13
- *
14
- * 秒级意味着同一秒内的两次写入令牌完全相同——版本守卫拿「读到的」和「写之前的」比对,比对不出来
15
- * 就等于**放过了一次真实修改**。代理侧(Tier 2)撞上的是同一类缺陷,修法也一样(审计 0086)。
16
- *
17
- * ## 为什么加 ino
18
- *
19
- * 原子写走临时文件 + rename,必然换一个 inode。这是与 mtime 精度无关的、决定性的「换过文件」信号。
20
- *
21
- * ## 为什么保留 mtimeMs 的小数
22
- *
23
- * Node 的 `Stats.mtimeMs` 是浮点,小数部分带亚毫秒信息,`Math.floor` 会把它丢掉。当前纪元的纳秒数
24
- * 超出双精度能精确表示的范围,所以这里实际拿到的是微秒级精度——够用,且不必为此把整条 stat 调用
25
- * 改成 `bigint: true`(那会让所有数值字段变成 BigInt,波及面远大于收益)。
26
- *
27
- * @module dsh-over-ssh/infra/backend/local-version
28
- */
29
- /** The subset of `fs.Stats` this token needs; structural so tests can pass a literal. */
30
- export interface LocalVersionInput {
31
- readonly size: number;
32
- readonly mtimeMs: number;
33
- /** File index. `undefined` (or 0) degrades the token to `size:mtimeMs`. */
34
- readonly ino?: number | undefined;
35
- }
36
- /**
37
- * Build the token for a local path.
38
- *
39
- * @param info - Anything with size, mtime and (ideally) inode.
40
- * @returns The opaque version string.
41
- */
42
- export declare function localVersion(info: LocalVersionInput): string;
43
- //# sourceMappingURL=local-version.d.ts.map
@@ -1,89 +0,0 @@
1
- /**
2
- * 本地世界作为一个 {@link Backend}:**写入**与**监听**交给平台的原版 `fs-sandbox`(住在本插件的私有域里),
3
- * 其余留在 {@link LocalBackend}。
4
- *
5
- * ## 为什么只交写入与监听
6
- *
7
- * 沙箱围栏只作用于写入——读没有围栏可拿。把读也转过去只有代价:平台 seam 是「先 `resolve` 再操作」的
8
- * 形状,而 `resolve` 要做规范化与祖先包含检查,我们分块读大文件时每块都要多做一次,属于纯亏。
9
- *
10
- * 写入不一样:`fs-sandbox` 的 `workspace-write` / `read-only` 判断是平台自己的语义,由它执行就不必在本
11
- * 插件里维护第二份。这是这个适配器要换来的第一件东西。
12
- *
13
- * 监听同理:平台的 `FileSystem.watch()` **本来就是 chokidar 实现**(`SandboxedFileSystem extends
14
- * LocalFileSystem`),而本插件的 `LocalBackend` 没有监听能力。接管 `ctx.fs` 之后如果继续对本地根抛
15
- * `FS_IO_ERROR`,连本地文件的变更通知都会消失(`dsh-api-workspace-files` 会得到
16
- * `workspace-file/watch-unsupported`)——这是接管造成的回退。委托给平台是唯一不重复实现 chokidar 的
17
- * 做法,而且文件目标的"监听父目录并过滤同名条目"语义在平台实现里已经写好,照抄一遍只会漂移。
18
- *
19
- * ## 为什么其余不交
20
- *
21
- * 平台的 `ctx.fs` 比本插件的 {@link Backend} **窄**:没有 `mkdir` / `rename` / `remove` / `chmod`,
22
- * `FsInfo` 与 `FsDirEntry` 也不带 `mtime`。文件树的「新建目录 / 重命名 / 删除」和版本令牌都依赖这些,
23
- * 所以它们继续走 {@link LocalBackend}——平台从来没有这些操作,谈不上「重复实现」。
24
- *
25
- * @module dsh-over-ssh/infra/backend/platform-local-backend
26
- */
27
- import type { FileSystem } from '@deepseek-ai/dsh-fs';
28
- import type { Backend, BackendEntry, BackendRead, BackendSandboxMode, BackendStat, BackendText, BackendWrite } from '../../app/backend.ts';
29
- /** Everything {@link PlatformLocalBackend} needs; each field is injected so tests need no platform. */
30
- export interface PlatformLocalBackendOptions {
31
- /** Stable id, also the target-key namespace. */
32
- readonly id: string;
33
- /** The platform's stock filesystem, captured from this plugin's private realm. */
34
- readonly fs: FileSystem;
35
- /** The backend that keeps the operations and metadata the platform seam does not expose. */
36
- readonly fallback: Backend;
37
- }
38
- /**
39
- * A local root whose writes are fenced by the platform's own sandbox.
40
- *
41
- * Reads, listing, metadata and the mutation primitives stay on the fallback: see the module comment for why
42
- * each of those is deliberately **not** delegated.
43
- */
44
- export declare class PlatformLocalBackend implements Backend {
45
- #private;
46
- readonly id: string;
47
- /** The platform's `FileSystem.watch` (chokidar) is the watcher this backend serves. */
48
- readonly watches = true;
49
- constructor(options: PlatformLocalBackendOptions);
50
- /**
51
- * The platform's mode wins: it is the thing actually enforcing the fence now.
52
- *
53
- * Falling back keeps the provider's own gate meaningful when the platform declines to report a mode.
54
- */
55
- get sandboxMode(): BackendSandboxMode | undefined;
56
- stat(absolute: string, signal?: AbortSignal): Promise<BackendStat>;
57
- lstat(absolute: string, signal?: AbortSignal): Promise<BackendStat>;
58
- list(absolute: string, signal?: AbortSignal): Promise<readonly BackendEntry[]>;
59
- readBytes(absolute: string, maxBytes: number, signal?: AbortSignal): Promise<BackendRead>;
60
- readText(absolute: string, maxBytes: number, signal?: AbortSignal): Promise<BackendText>;
61
- /**
62
- * Delegate to the platform's own watcher.
63
- *
64
- * `watch` is part of `FileSystem` (the base class implements it with chokidar and the sandbox does not
65
- * override it), so this backend does not reimplement anything — it only maps "absolute path" to the
66
- * platform's target, exactly like {@link writeText} does. The platform's implementation already handles the
67
- * file-vs-directory difference (a file target watches its parent with a path filter).
68
- */
69
- watch(absolute: string, changed: (error?: Error) => void, signal: AbortSignal): Promise<() => Promise<void>>;
70
- /** Fenced by the platform. An empty `expectedVersion` means "no guard", as the backend contract says. */
71
- writeText(absolute: string, text: string, expectedVersion: string | undefined, signal?: AbortSignal): Promise<BackendWrite>;
72
- /** Fenced by the platform; the three-state semantics are the platform's, which is what the core expects. */
73
- editText(absolute: string, oldString: string, newString: string, replaceAll: boolean, expectedVersion: string | undefined, signal?: AbortSignal): Promise<BackendWrite & {
74
- readonly replaced: number;
75
- }>;
76
- mkdir(absolute: string, options: {
77
- readonly recursive: boolean;
78
- }, signal?: AbortSignal): Promise<void>;
79
- rename(from: string, to: string, options: {
80
- readonly createParents: boolean;
81
- }, signal?: AbortSignal): Promise<void>;
82
- remove(absolute: string, options: {
83
- readonly recursive: boolean;
84
- }, signal?: AbortSignal): Promise<void>;
85
- chmod(absolute: string, mode: string, signal?: AbortSignal): Promise<void>;
86
- processPath(absolute: string): string | undefined;
87
- fileUrl(absolute: string): string;
88
- }
89
- //# sourceMappingURL=platform-local-backend.d.ts.map