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,239 +0,0 @@
1
- /**
2
- * The remote backend: one remote directory served over a {@link Transport}.
3
- *
4
- * This is the Tier 1 shape — no agent on the remote host, only POSIX tools the server already has
5
- * (`sh`, `wc`, `stat`, `base64`). Directory listing is therefore one bounded shell script rather
6
- * than a protocol method, and the script is built from a fixed template with a single quoted path,
7
- * so there is no injection surface.
8
- *
9
- * Version tokens are whatever **one** transport reports for the same file state, and this backend never
10
- * mixes two sources. On Tier 1 that is the shell `size:mtimeSeconds` shape (comparable with the ssh2
11
- * transport); on Tier 2 it is the agent's `size:mtimeNs`, obtained from {@link Transport.statPath} so that a
12
- * token read by `readText` and a token compared by `writeText` come from the same producer. Deriving one
13
- * from the other is what made every read→write guard fail on Tier 2 (audit 0055).
14
- *
15
- * @module dsh-over-ssh/infra/backend/remote-backend
16
- */
17
- import type { Backend, BackendEntry, BackendRead, BackendSandboxMode, BackendStat, BackendText, BackendWrite } from '../../app/backend.ts';
18
- import type { RemoteCapabilities } from '../../domain/capability.ts';
19
- import { type VersionedCacheStats } from '../cache/versioned.ts';
20
- import type { Transport } from '../transport/transport.ts';
21
- /** Options for {@link RemoteBackend}. */
22
- export interface RemoteBackendOptions {
23
- /** Backend id; the router namespaces target keys with it (equals the root key). */
24
- readonly id: string;
25
- /** A ready transport. Mutually exclusive with {@link getTransport}. */
26
- readonly transport?: Transport;
27
- /**
28
- * Lazy transport resolver, called once on first use.
29
- *
30
- * The root registry's backend factory is synchronous while acquiring a transport is not, so the
31
- * connection is established when the first operation needs it and reused afterwards.
32
- */
33
- readonly getTransport?: () => Promise<Transport>;
34
- /** Read cap applied by the backend itself; the router passes a smaller one when it wants to. */
35
- readonly maxReadBytes?: number;
36
- /**
37
- * Freshness cache policy for the read path (FR-2.7). Omitted means {@link DEFAULT_CACHE}.
38
- *
39
- * This is the switch that makes "re-opening the same file within the TTL costs **zero** round trips"
40
- * true. Before it existed, `VersionedCache` was written, benchmarked and then never constructed by
41
- * any production code: every read went to the host, and the numbers in `baseline.md` measured a
42
- * component nobody called (audit 0044).
43
- */
44
- readonly cache?: RemoteBackendCacheOptions;
45
- }
46
- /** Cache policy for one remote backend. */
47
- export interface RemoteBackendCacheOptions {
48
- /** Freshness window; the FR-2.7 budget is 5 s. `0` disables retention (every read goes remote). */
49
- readonly ttlMs?: number;
50
- /** Bound on retained entries. */
51
- readonly maxEntries?: number;
52
- /** Bound on retained payload bytes. */
53
- readonly maxBytes?: number;
54
- /** Injectable clock, so a TTL can be tested without sleeping. */
55
- readonly now?: () => number;
56
- }
57
- /** Filesystem access for one remote root over a transport. */
58
- export declare class RemoteBackend implements Backend {
59
- #private;
60
- readonly id: string;
61
- readonly sandboxMode: BackendSandboxMode | undefined;
62
- /**
63
- * Whether this root has a **native** watcher: the transport is one that speaks `watch.*` *and* its handshake
64
- * advertised `watch === "native"`. It reads `false` until the transport is known — the capability is a fact
65
- * about the connected agent, and claiming it early would advertise a watcher for a Tier 1 host that can
66
- * never serve one.
67
- */
68
- get watches(): boolean;
69
- constructor(options: RemoteBackendOptions);
70
- /**
71
- * Read-path cache counters, for diagnostics and the performance baseline.
72
- *
73
- * Reported rather than inferred: the FR-2.7 claims ("0 round trips inside the TTL", "≈5× on
74
- * revalidation") are only checkable if the counters behind them are reachable from outside.
75
- */
76
- get cacheStats(): {
77
- readonly reads: VersionedCacheStats;
78
- readonly listings: VersionedCacheStats;
79
- };
80
- /** Remote capabilities discovered so far (probe results live on the transport). */
81
- capabilities(): Promise<RemoteCapabilities>;
82
- /**
83
- * Stat a path, preferring the transport's **own** token source when it has one.
84
- *
85
- * This is where the two tiers are reconciled, and it is the *only* place that decides. A transport that
86
- * exposes {@link Transport.statPath} (Tier 2: the agent's `fs.stat`) reports the very same token its
87
- * `readFile` reports (`size:mtimeNs`), so a version read by one and compared by the other is comparing
88
- * like with like. Tier 1 has no native stat, so the shell script below runs exactly as it always has and
89
- * its long-standing `size:mtimeSeconds` tokens are untouched.
90
- *
91
- * Before this, `stat` always ran the shell script: on Tier 2 it produced seconds while every read produced
92
- * nanoseconds, so the `remote_read` → `remote_write`/`remote_edit` version guard could never pass and the
93
- * FR-2.7 stale-revalidation branch never matched either.
94
- */
95
- stat(absolute: string, signal?: AbortSignal): Promise<BackendStat>;
96
- list(absolute: string, signal?: AbortSignal): Promise<readonly BackendEntry[]>;
97
- /**
98
- * Read a file, going through the FR-2.7 freshness cache.
99
- *
100
- * The three states the budget is written in terms of:
101
- * - **fresh** (inside the TTL): returned from memory, **zero** round trips;
102
- * - **stale**: one cheap `stat` decides. If the version still matches, the retained body is reused and
103
- * only the timestamp is refreshed — this is the ≈5× revalidation path;
104
- * - **gone or changed**: re-read and replace.
105
- *
106
- * A read with a different cap is a different cache entry (see the field notes), so a capped probe can
107
- * never satisfy a full read.
108
- */
109
- readBytes(absolute: string, maxBytes: number, signal?: AbortSignal): Promise<BackendRead>;
110
- /**
111
- * Atomic in-place edit through the agent's `fs.edit` (审计 0030).
112
- *
113
- * The caller passes the version it just read; the agent re-checks it, so a concurrent modification
114
- * between our read and this call is refused rather than clobbered. Throws `REMOTE_UNSUPPORTED` when the
115
- * agent predates the method, which is what makes `ctx.fs` fall back to read → replace → write.
116
- */
117
- editText(absolute: string, oldString: string, newString: string, replaceAll: boolean, expectedVersion: string | undefined, signal?: AbortSignal): Promise<BackendWrite & {
118
- readonly replaced: number;
119
- }>;
120
- /**
121
- * Apply a unified diff server-side: every hunk in one round trip, all-or-nothing.
122
- *
123
- * No client-side fallback on purpose. Reading the file, patching it here and writing it back would cost
124
- * three round trips for two hunks — more than the `remote_edit` calls it is meant to replace — and would
125
- * need a second implementation of the hunk-matching rules.
126
- */
127
- patchText(absolute: string, diff: string, expectedVersion: string | undefined, signal?: AbortSignal): Promise<BackendWrite & {
128
- readonly hunks: number;
129
- }>;
130
- readText(absolute: string, maxBytes: number, signal?: AbortSignal): Promise<BackendText>;
131
- /**
132
- * Cache-aware wrapper around {@link readManyUncached}, so a batch sweep leaves the cache warm.
133
- *
134
- * Satisfying the whole batch from cache is deliberately **not** attempted: the batch exists to collapse
135
- * N round trips into one, and splitting it into cached and uncached halves would give back the round trips
136
- * it was built to save. Cached entries are therefore refreshed from the batch's answer, and files already
137
- * fresh are still fetched (one shared round trip) rather than removed from the batch.
138
- */
139
- readMany(absolutePaths: readonly string[], options: {
140
- readonly maxBytesEach: number;
141
- readonly totalBytesCap?: number;
142
- readonly signal?: AbortSignal;
143
- }): Promise<{
144
- readonly chunks: readonly (BackendText | undefined)[];
145
- readonly missing: readonly string[];
146
- }>;
147
- /**
148
- * Write text remotely.
149
- *
150
- * **The version guard is tier-aware, and that is the point.** The token a caller passes back is one this
151
- * backend just handed out, so the guard must be verified by the *same* token source:
152
- *
153
- * - a transport with a native version token (Tier 2) verifies the token itself through
154
- * {@link WriteFileRequest.expectedVersion}, which is what its own `fs.write` guard compares — no digest
155
- * is involved and none could be, because the agent advertises no sha256 tool;
156
- * - a Tier 1 transport verifies a content digest, so the current content is hashed and handed over as
157
- * `expectedSha256`. That path is unchanged.
158
- *
159
- * A plain write is one round trip plus one `stat` for the new version.
160
- */
161
- writeText(absolute: string, text: string, expectedVersion: string | undefined, signal?: AbortSignal): Promise<BackendWrite>;
162
- /**
163
- * Watch one remote path (directory, or one file) through the agent's native watcher.
164
- *
165
- * The agent watches **directories**: `watch.start` registers recursively from the given roots, and each
166
- * reported event carries the path of the changed entry. A file target therefore watches its parent directory
167
- * and filters events by exact path — the same shape the platform's own local watcher uses, and the only way
168
- * inotify can report changes to one file.
169
- *
170
- * Degradation is **not** silent:
171
- * - `watch.start` reporting `truncated` (maxWatches / `max_user_watches` cut registration short) means the
172
- * tree is only partially covered; the watcher is stopped and this call fails rather than promising
173
- * notifications it cannot deliver;
174
- * - a poll whose cumulative `dropped` grew means events were lost (queue limit / kernel overflow); the
175
- * owner is told through `changed(error)` and the watcher stops, so the caller rescans instead of trusting
176
- * an incomplete stream.
177
- *
178
- * `changed()` also covers "nothing to re-stat but something happened" — the platform's contract is
179
- * "re-stat this path", so events are coalesced per poll.
180
- *
181
- * @param absolute - root-absolute remote path (directory or file).
182
- * @param changed - platform callback: no argument = re-stat; an error = the subscription is dead.
183
- * @param signal - cancels the subscription; stopping is immediate and idempotent.
184
- * @returns the disposer (stops the watcher; idempotent).
185
- * @throws SshDevError `REMOTE_UNSUPPORTED` when this connection has no native watcher, `REMOTE_NOT_FOUND`
186
- * when a file target's parent directory does not exist, `CANCELLED` when aborted during setup.
187
- */
188
- watch(absolute: string, changed: (error?: Error) => void, signal: AbortSignal): Promise<() => Promise<void>>;
189
- /**
190
- * Stop every watcher this backend started.
191
- *
192
- * Not part of {@link Backend}: the wiring owns backend teardown and is outside this change's scope. It is
193
- * public so whoever retires the backend can end the poll loops, and so tests can prove they stop.
194
- */
195
- dispose(): Promise<void>;
196
- /**
197
- * Create a directory (FR-2.2.2).
198
- *
199
- * Prefers the agent's `fs.mkdir` (Tier 2) and falls back to one bounded shell script. The shell path is not
200
- * dead weight: Tier 1 has no agent, and an older agent answers `E_METHOD`, which
201
- * {@link Transport.makeDir} reports as `REMOTE_UNSUPPORTED`.
202
- *
203
- * **Why the protocol path is worth preferring**: the shell version depends on the remote having a POSIX
204
- * `mkdir`, and its refusals have to be parsed back out of the script's own `DSH_ERROR=` line. Windows
205
- * targets have neither. The product rule — `recursive: false` refuses a name that is already taken, so a
206
- * 「新建目录」 dialog cannot silently "succeed" into someone else's folder — is applied **here**, once, for
207
- * both paths.
208
- */
209
- mkdir(absolute: string, options: {
210
- readonly recursive: boolean;
211
- }, signal?: AbortSignal): Promise<void>;
212
- /**
213
- * Rename/move a path (FR-2.2.2), refusing to clobber.
214
- *
215
- * The no-clobber rule lives in {@link renameCommand}'s script (see its notes): `mv` would either replace a
216
- * file or move the source *inside* an existing directory, and both are silent.
217
- */
218
- rename(from: string, to: string, options: {
219
- readonly createParents: boolean;
220
- }, signal?: AbortSignal): Promise<void>;
221
- /** Remove a path (FR-2.2.2); `recursive: false` refuses a directory. */
222
- remove(absolute: string, options: {
223
- readonly recursive: boolean;
224
- }, signal?: AbortSignal): Promise<void>;
225
- /**
226
- * Change one path's permission bits.
227
- *
228
- * Runs through `exec` rather than a transport primitive because the agent has no `fs.chmod`; the same
229
- * script therefore works on both tiers, which is what keeps `remote_write`'s `mode` from meaning two
230
- * different things depending on the host (audit 0064 P4). `chmodCommand` re-validates the octal digits, so
231
- * this is not a place where model-supplied text reaches a shell unchecked.
232
- */
233
- chmod(absolute: string, mode: string, signal?: AbortSignal): Promise<void>;
234
- /** Whether this backend's root contains a path (used by the router's `contains`). */
235
- contains(parent: string, candidate: string): boolean;
236
- }
237
- /** Parse `KEY=value` header lines into a map. */
238
- export declare function parseHeaders(stdout: string): Map<string, string>;
239
- //# sourceMappingURL=remote-backend.d.ts.map
@@ -1,52 +0,0 @@
1
- /**
2
- * A bounded LRU cache: at most `maxEntries` entries and at most `maxBytes` of payload.
3
- *
4
- * Both bounds are enforced because "number of open files" and "memory held by cached file
5
- * bodies" fail differently: a repository of tiny files hits the entry bound, a few large
6
- * generated files hit the byte bound.
7
- *
8
- * @module dsh-over-ssh/infra/cache/lru
9
- */
10
- /** Bounds for one cache. */
11
- export interface LruCacheOptions {
12
- /** Maximum number of retained entries; at least 1. */
13
- readonly maxEntries: number;
14
- /** Maximum total payload bytes; at least 1. */
15
- readonly maxBytes: number;
16
- }
17
- /** Observable cache counters. */
18
- export interface LruStats {
19
- readonly entries: number;
20
- readonly bytes: number;
21
- readonly hits: number;
22
- readonly misses: number;
23
- readonly evictions: number;
24
- /** Values rejected because a single value exceeded `maxBytes`. */
25
- readonly rejectedOversize: number;
26
- }
27
- /** A size-bounded, recency-ordered map. */
28
- export declare class LruCache<V> {
29
- #private;
30
- constructor(options: LruCacheOptions, sizeOf: (value: V) => number);
31
- /** Look up a value and mark it most-recently used. */
32
- get(key: string): V | undefined;
33
- /** Look up a value without changing recency or counters. */
34
- peek(key: string): V | undefined;
35
- /** Whether the key is retained. */
36
- has(key: string): boolean;
37
- /**
38
- * Insert or replace a value.
39
- *
40
- * @returns `false` when the value alone exceeds `maxBytes` and was therefore not stored.
41
- */
42
- set(key: string, value: V): boolean;
43
- /** Remove a key, returning whether it was present. */
44
- delete(key: string): boolean;
45
- /** Remove everything. */
46
- clear(): void;
47
- /** Keys from least to most recently used. */
48
- keys(): readonly string[];
49
- /** Current counters. */
50
- get stats(): LruStats;
51
- }
52
- //# sourceMappingURL=lru.d.ts.map
@@ -1,77 +0,0 @@
1
- /**
2
- * TTL + version cache used by the remote read path.
3
- *
4
- * The performance budget depends on this three-state behaviour:
5
- * - within TTL: a hit with zero remote round trips;
6
- * - past TTL: a *stale* hit whose body may still be reused if a cheap version check matches
7
- * (this is what makes revalidation ≈5× cheaper than a first open);
8
- * - invalidated: no usable body.
9
- *
10
- * @module dsh-over-ssh/infra/cache/versioned
11
- */
12
- /** Cache policy for one cache instance. */
13
- export interface VersionedCacheOptions {
14
- /** Freshness window in milliseconds. */
15
- readonly ttlMs: number;
16
- readonly maxEntries: number;
17
- readonly maxBytes: number;
18
- /** Monotonic-or-wall clock; injectable so tests are deterministic. */
19
- readonly now?: () => number;
20
- }
21
- /** One lookup result. */
22
- export interface VersionedHit<V> {
23
- readonly value: V;
24
- /** The version token recorded when the body was cached. */
25
- readonly version: string;
26
- /** Whether the entry is still inside its freshness window. */
27
- readonly fresh: boolean;
28
- readonly ageMs: number;
29
- }
30
- /** Counters reported for diagnostics and the performance baseline. */
31
- export interface VersionedCacheStats {
32
- readonly entries: number;
33
- readonly bytes: number;
34
- readonly hits: number;
35
- readonly misses: number;
36
- readonly freshHits: number;
37
- readonly staleHits: number;
38
- /** Version checks that reused an existing body. */
39
- readonly revalidations: number;
40
- readonly evictions: number;
41
- }
42
- /** Freshness-aware cache keyed by route key. */
43
- export declare class VersionedCache<V> {
44
- #private;
45
- constructor(options: VersionedCacheOptions, sizeOf: (value: V) => number);
46
- /**
47
- * Look up an entry.
48
- *
49
- * @param key - route key.
50
- * @returns the entry with its freshness, or `undefined` when nothing is retained.
51
- */
52
- get(key: string): VersionedHit<V> | undefined;
53
- /** Insert or replace an entry, stamping it as just-read. */
54
- set(key: string, value: V, version: string): boolean;
55
- /**
56
- * Refresh an entry's timestamp after a cheap remote version check matched.
57
- *
58
- * @param key - route key.
59
- * @param version - the version observed by the check; a mismatch refuses the touch.
60
- * @returns whether the entry was refreshed.
61
- */
62
- touch(key: string, version: string): boolean;
63
- /** Drop one entry. */
64
- invalidate(key: string): boolean;
65
- /**
66
- * Drop every entry whose key starts with the prefix, used when a directory epoch changes.
67
- *
68
- * @param prefix - key prefix.
69
- * @returns the number of removed entries.
70
- */
71
- invalidatePrefix(prefix: string): number;
72
- /** Remove everything. */
73
- clear(): void;
74
- /** Current counters. */
75
- get stats(): VersionedCacheStats;
76
- }
77
- //# sourceMappingURL=versioned.d.ts.map
@@ -1,19 +0,0 @@
1
- /**
2
- * A temporary file name that is unique per **write**, not just per process.
3
- *
4
- * The pid alone is not enough: two concurrent writes to the same target from one process picked the same
5
- * temp name, so A's rename could publish B's bytes while reporting A's byte count, and B's rename then
6
- * failed because the file was already gone (audit 0097). A per-process sequence closes that window, and the
7
- * pid keeps the name distinct from another host process writing the same file.
8
- *
9
- * @module dsh-over-ssh/infra/temp-name
10
- */
11
- /**
12
- * Build a temp path next to `path` that no other in-flight write in this process will reuse.
13
- *
14
- * @param path - the target the temp file is later renamed onto.
15
- * @returns `path` plus a `.dsh-tmp-<pid>-<n>` suffix, keeping the file in the same directory so the rename
16
- * stays a rename and not a cross-device copy.
17
- */
18
- export declare function temporaryNameFor(path: string): string;
19
- //# sourceMappingURL=temp-name.d.ts.map
@@ -1,132 +0,0 @@
1
- /**
2
- * 代理协议的**客户端层**:请求/响应配对、在飞窗口、blob 通道与关闭时清场。
3
- *
4
- * 线上格式的读写移到 `agent-protocol-codec.ts`,错误与截止移到 `agent-protocol-errors.ts`
5
- * (审计 0079)。本文件只剩状态机本身:发请求、配对响应、管理 blob 通道、关闭时清场。
6
- *
7
- * @module dsh-over-ssh/infra/transport/agent-protocol-client
8
- */
9
- import { SshDevError } from '../../../domain/errors.ts';
10
- import type { AgentSession } from './agent-protocol-types.ts';
11
- /**
12
- * 代理协议的**客户端层**:请求/响应配对、在飞窗口、blob 通道、截止与取消、参数压缩与结果读取。
13
- *
14
- * 从 `agent-transport.ts` 拆出(审计 0077)。它只依赖帧层与类型层,**不依赖**任何 Transport 实现,
15
- * 因此可以脱离传输单独测试(既有测试正是这么用的)。
16
- *
17
- * @module dsh-over-ssh/infra/transport/agent-protocol-client
18
- */
19
- export declare class AgentProtocolClient {
20
- #private;
21
- constructor(session: AgentSession, options?: {
22
- readonly requestTimeoutMs?: number;
23
- readonly maxInFlight?: number;
24
- readonly now?: () => number;
25
- readonly epoch?: number;
26
- readonly epochNow?: () => number;
27
- /**
28
- * Called once when the connection is declared dead.
29
- *
30
- * Without it a client-side death is invisible to the owner: a per-request deadline that expires
31
- * poisons this client permanently (`#lost` is never cleared), yet the transport still reports
32
- * `state === 'ready'`, so the transport service keeps handing the same dead client to every later
33
- * call. Every one of them then fails instantly with the *first* timeout's message and numbers.
34
- */
35
- readonly onFatal?: () => void;
36
- /**
37
- * Default session for every request on this connection, sent as `hello.params.sessionId`.
38
- *
39
- * Omitted (the default) means the field is not sent at all — byte-for-byte the pre-session
40
- * handshake, which the agent answers with `sessionId: "default"`.
41
- */
42
- readonly sessionId?: string;
43
- });
44
- /** Whether the `hello` handshake already succeeded. */
45
- get handshaked(): boolean;
46
- /** Whether the connection has been declared dead. */
47
- get lost(): boolean;
48
- /** Whether the agent accepted request-level cancellation (`capabilities.cancel === true`). */
49
- get cancelSupported(): boolean;
50
- /**
51
- * Record the cancellation capability the handshake reported.
52
- *
53
- * Called by the owning transport after it adopts `hello`/`capabilities`; a client that is never told keeps
54
- * the old behaviour. `true` only when the agent literally sent `true` — a missing bit is not a maybe.
55
- */
56
- setCancelSupport(supported: boolean): void;
57
- /** Whether {@link close} already ran. */
58
- get closed(): boolean;
59
- /** Feed stdout bytes; called by the transport's data handler. */
60
- handleData(chunk: Uint8Array): void;
61
- /**
62
- * Handshake. The first method on a fresh session must be `hello`; a version mismatch is fatal and is
63
- * **not** retried with a guessed version.
64
- */
65
- handshake(signal?: AbortSignal, clientId?: string): Promise<unknown>;
66
- /**
67
- * Session the agent echoed for this connection, or `undefined` when it did not send one.
68
- *
69
- * `undefined` is meaningful: it means the agent predates session support, so this client must not claim
70
- * the requests are isolated from one another.
71
- */
72
- get negotiatedSessionId(): string | undefined;
73
- /**
74
- * Whether the agent reported that jobs outlive this connection (`hello.result.daemon`).
75
- *
76
- * Defaults to `false`, which is the honest reading of "the agent said nothing": today's agent does tie
77
- * job lifetime to the connection, so assuming otherwise would make callers skip cleanup that is still
78
- * required.
79
- */
80
- get daemon(): boolean;
81
- /** `capabilities`. Requires a completed handshake. */
82
- capabilities(signal?: AbortSignal): Promise<unknown>;
83
- /**
84
- * Send one control-frame request (plus an optional blob frame) and await its response.
85
- *
86
- * @throws SshDevError `CONNECTION_LOST` when the session is gone, the deadline expired mid-request, or
87
- * the stream desynchronised; `CANCELLED` when the caller's signal aborted.
88
- * @throws AgentProtocolError when the agent answered with a structured error.
89
- */
90
- request(method: string, params: Readonly<Record<string, unknown>>, options?: RequestOptions): Promise<unknown>;
91
- /**
92
- * The blob payload belonging to `control`.
93
- *
94
- * v1 semantics: a response with `result.blob === true` is followed by exactly one blob frame, so the
95
- * blob cannot be awaited *before* the control response arrives; it is persisted on arrival instead
96
- * (a fast agent may deliver it before this call is even entered).
97
- *
98
- * Under pipelining several requests may have declared a blob at once, so the channel is resolved by the
99
- * identity of the control promise instead of from one global "current blob" slot.
100
- *
101
- * Bounded on purpose: the blob shares the request deadline, so an agent that declares a blob and then
102
- * goes silent produces one failed request instead of a promise that never settles.
103
- *
104
- * @throws AgentProtocolError `E_PROTOCOL` when the response carried no blob within the deadline.
105
- */
106
- readBlob(control: Promise<unknown>, options?: {
107
- readonly signal?: AbortSignal;
108
- readonly timeoutMs?: number;
109
- }): Promise<Uint8Array>;
110
- /** Declare the connection dead: reject everything outstanding and stop accepting work. */
111
- failConnection(error: SshDevError): void;
112
- /** Stop the client; idempotent. */
113
- close(): void;
114
- }
115
- interface RequestOptions {
116
- readonly signal?: AbortSignal;
117
- readonly timeoutMs?: number;
118
- /** Blob payload to send immediately after the control frame (`fs.write`). */
119
- readonly blob?: Uint8Array;
120
- /**
121
- * Session this request belongs to; overrides the client's {@link AgentProtocolClientOptions.sessionId}
122
- * default for this one request.
123
- *
124
- * The agent gives each session its own share of the per-connection in-flight window, so naming the
125
- * session is what keeps one session's long request from queueing another's short one behind it.
126
- * Measured on a real host before the agent grew sessions (audit 0021): a `fs.stat` on session B waited
127
- * **3502 ms** behind eight long requests on session A, and **0.6 ms** when the window was not full.
128
- */
129
- readonly session?: string;
130
- }
131
- export {};
132
- //# sourceMappingURL=agent-protocol-client.d.ts.map
@@ -1,140 +0,0 @@
1
- /**
2
- * 代理协议的**编解码层**:请求参数压缩与响应结果读取。
3
- *
4
- * 从 `agent-protocol-client.ts` 拆出(审计 0079)。全部是纯函数——无 I/O、无类、无状态,
5
- * 因此可以脱离客户端单独测试。客户端只负责"发出去、等回来、配对",
6
- * 把线上格式(ns→ms、缺省值、字符串枚举)留给这一层。
7
- *
8
- * @module dsh-over-ssh/infra/transport/agent-protocol-codec
9
- */
10
- import { type ExecRequest } from '../transport.ts';
11
- import type { AgentIndexEntry, AgentIndexScanResult, AgentProcStartRequest, AgentJobStatus, AgentJobOutput, AgentLspStartRequest, AgentLspStatus, AgentLspRead, AgentIndexScanRequest, AgentSearchMatch, AgentSearchResult, AgentWatchEvent, AgentWatchStartResult, AgentWatchPollResult, AgentWatchStopResult, AgentGitStatusResult, AgentCapabilities, AgentHelloExtras } from './agent-protocol-types.ts';
12
- export declare const AGENT_MAX_JOB_WAIT_MS = 30000;
13
- /**
14
- * Add `waitMs` to a `proc.*` request only when it is positive.
15
- *
16
- * Omitted means "answer immediately", which is what an agent that predates the field does anyway —
17
- * so a client that never passes a wait is byte-for-byte the pre-long-poll request.
18
- *
19
- * @param params - the method's own parameters.
20
- * @param waitMs - requested server-side wait, or `undefined` for none.
21
- * @returns the parameters to send.
22
- */
23
- export declare function waitParams(params: Record<string, unknown>, waitMs: number | undefined): Record<string, unknown>;
24
- /**
25
- * Client deadline for a long-polled request.
26
- *
27
- * The server may legitimately block for `waitMs`, so the request budget must outlive it: a per-request
28
- * deadline here is **fatal** to the whole connection (see `isFatalAgentFailure`), so an under-sized
29
- * budget would turn a normal wait into a reconnect.
30
- *
31
- * @param waitMs - requested server-side wait, or `undefined` for none.
32
- * @returns the request budget, or `undefined` to keep the transport default.
33
- */
34
- export declare function waitBudget(waitMs: number | undefined): number | undefined;
35
- export declare function asRecord(value: unknown): Record<string, unknown> | undefined;
36
- /** Read the `capabilities` object out of a `hello`/`capabilities` result, dropping wrong-typed fields. */
37
- export declare function readAgentCaps(result: Record<string, unknown> | undefined): AgentCapabilities | undefined;
38
- /** Read the `agent` object of a `hello` result. */
39
- export declare function readAgentExtras(result: Record<string, unknown> | undefined): AgentHelloExtras;
40
- /** Drop `undefined` request fields so a control frame stays minimal (and deterministic in tests). */
41
- export declare function compactParams(request: ExecRequest): Readonly<Record<string, unknown>>;
42
- /** Read a finite number field, or `null` when the agent sent nothing usable. */
43
- export declare function readOptionalNumber(source: Record<string, unknown>, key: string): number | null;
44
- /**
45
- * A boolean field, falling back only when the field is **absent**.
46
- *
47
- * `??`-style semantics rather than `||`: an explicit `false` from the agent is a real answer ("this stream
48
- * was not cut") and must not be replaced by the fallback. Using `||` here would silently convert every
49
- * `false` into the legacy value, which is exactly how a complete stderr gets reported as truncated.
50
- */
51
- export declare function booleanOr(value: unknown, fallback: boolean): boolean;
52
- /** A non-empty string field, else `undefined` (an empty spill path is not a readable path). */
53
- export declare function nonEmptyString(value: unknown): string | undefined;
54
- /**
55
- * Convert Unix nanoseconds from the wire into epoch milliseconds.
56
- *
57
- * @returns `null` when the agent sent nothing usable, so a caller can tell "no timestamp" apart from
58
- * "the epoch". A negative value is passed through as a negative time rather than clamped.
59
- */
60
- export declare function nsToMs(value: number | null): number | null;
61
- /** Narrow a `proc.*` job status; unknown fields are dropped, missing numbers become 0. */
62
- export declare function readJobStatus(raw: unknown): AgentJobStatus;
63
- /** Narrow a `proc.output` result. */
64
- export declare function readJobOutput(raw: unknown): AgentJobOutput;
65
- /** Narrow an `lsp.*` session status. */
66
- export declare function readLspStatus(raw: unknown): AgentLspStatus;
67
- /** Narrow an `lsp.read` result. */
68
- export declare function readLspRead(raw: unknown): AgentLspRead;
69
- /**
70
- * Build the `proc.start` frame.
71
- *
72
- * Everything the caller omits is **omitted**, not defaulted: the agent's own defaults apply. The one
73
- * conversion is `stdinBytes` → base64, because the wire has no byte type.
74
- */
75
- export declare function compactProcParams(request: AgentProcStartRequest): Record<string, unknown>;
76
- /** Build the `lsp.start` frame, omitting what the caller did not set. */
77
- export declare function compactLspParams(request: AgentLspStartRequest): Record<string, unknown>;
78
- /** Narrow one `index.scan` entry (`{path,size,mtimeNs,isDir?}`); unknown fields are dropped. */
79
- export declare function readIndexEntry(value: unknown): AgentIndexEntry;
80
- /**
81
- * Narrow one `index.scan` result.
82
- *
83
- * Defaults follow the protocol: `strategy` `"unknown"` when the agent sent nothing usable (an unknown
84
- * strategy is *not* silently mapped onto a known one), `filesScanned` `0`, `warnings` empty, and
85
- * `truncated` `false` — the last one is the only optimistic default in this block, and it means
86
- * "the agent did not report truncation", not "the result is complete".
87
- */
88
- export declare function readIndexScanResult(value: unknown): AgentIndexScanResult;
89
- /** Narrow one `search.grep` match; `column` defaults to `0` and the context arrays to empty. */
90
- export declare function readSearchMatch(value: unknown): AgentSearchMatch;
91
- /**
92
- * Narrow one `search.grep` result.
93
- *
94
- * `dropped` is not a `search.grep` field in protocol v1; it is left at `0` and documented as
95
- * meaningless here rather than being invented from `truncated`.
96
- */
97
- export declare function readSearchResult(value: unknown): AgentSearchResult;
98
- /** Narrow one `watch.start` result; `watched` lists the **roots** the agent really registered. */
99
- export declare function readWatchStartResult(value: unknown): AgentWatchStartResult;
100
- /**
101
- * Narrow one `git.status` result.
102
- *
103
- * Unknown `status` values and empty paths are **dropped**, not coerced: the consumer draws a badge per entry,
104
- * and a made-up status would be a claim the agent never made (the UI rule is "unknown status → no badge").
105
- * A missing `changes` array reads as empty, and a non-boolean `truncated` reads as `false` — claiming
106
- * truncation would tell the caller to rescan for no reason.
107
- */
108
- export declare function readGitStatusResult(value: unknown): AgentGitStatusResult;
109
- /** Narrow one `watch.poll` event; `atNs` is converted to `atMs`. */
110
- export declare function readWatchEvent(value: unknown): AgentWatchEvent;
111
- /**
112
- * Narrow one `watch.poll` result.
113
- *
114
- * `dropped` is passed through untouched because it is **cumulative**; the client never derives a delta
115
- * from it (doing so would under-report drops exactly when the stream is already incomplete).
116
- */
117
- export declare function readWatchPollResult(value: unknown): AgentWatchPollResult;
118
- /** Narrow one `watch.stop` result; a missing `stopped` counts as `false`, which is not a failure. */
119
- export declare function readWatchStopResult(value: unknown): AgentWatchStopResult;
120
- /**
121
- * Drop `undefined` request fields so a control frame stays minimal (and deterministic in tests).
122
- *
123
- * The same rule is applied inline by `search.grep` / `watch.*`: an omitted option means "let the agent
124
- * use its own default", never "send this client's idea of the default". Only `index.scan` needs a helper
125
- * because it has more optional fields than the others put together.
126
- */
127
- export declare function compactIndexParams(request: AgentIndexScanRequest): Readonly<Record<string, unknown>>;
128
- /**
129
- * The agent transport: one stdio session, serialised requests, explicit degradation.
130
- *
131
- * Lifecycle matches the other two implementations (`idle` → `connecting` → `ready`, `lost` on an
132
- * unexpected death) so callers cannot tell them apart except through `id` and `capabilities`.
133
- */
134
- /**
135
- * Whether a plugin error means the frame stream can no longer be trusted.
136
- *
137
- * Only these close the connection: everything else (a missing file, a stale version, an unsupported
138
- * method) is a normal answered request and leaves the session healthy.
139
- */
140
- //# sourceMappingURL=agent-protocol-codec.d.ts.map