@tiphareth/dsh-hardssh 0.1.2 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/LICENSE +30 -0
  2. package/README.md +49 -25
  3. package/SKILLS.md +140 -0
  4. package/lib/base/index.js +95 -0
  5. package/lib/client.js +1171 -1321
  6. package/lib/fs.js +307 -416
  7. package/lib/index.js +5428 -2991
  8. package/lib/ledger-B-LXlftp.js +43 -0
  9. package/lib/ledger-D2ezq1iW.js +376 -0
  10. package/lib/model-Cp7f70Mb.js +5 -0
  11. package/lib/registry-CViuzKYI.js +421 -0
  12. package/lib/subprocess.js +92 -549
  13. package/lib/types/backend.d.ts +73 -78
  14. package/lib/types/base/capability.d.ts +4 -0
  15. package/lib/types/base/ledger-router.d.ts +42 -26
  16. package/lib/types/base/ledger.d.ts +30 -19
  17. package/lib/types/base/model.d.ts +17 -8
  18. package/lib/types/base/plugin.d.ts +11 -0
  19. package/lib/types/base/registry.d.ts +28 -31
  20. package/lib/types/base/router.d.ts +0 -7
  21. package/lib/types/client/api.d.ts +8 -8
  22. package/lib/types/client/connect-host.d.ts +13 -0
  23. package/lib/types/client/index.d.ts +6 -4
  24. package/lib/types/client/locales.d.ts +15 -1
  25. package/lib/types/client/session-connect-gate.d.ts +71 -0
  26. package/lib/types/client/ssh/api.d.ts +11 -9
  27. package/lib/types/client/ssh/apply.d.ts +14 -8
  28. package/lib/types/client/ssh/locales.d.ts +8 -1
  29. package/lib/types/client/ssh/ops-tab.d.ts +34 -0
  30. package/lib/types/client/ssh/panel/ClusterTab.d.ts +3 -1
  31. package/lib/types/client/ssh/panel/ConnectionErrorDialog.d.ts +8 -0
  32. package/lib/types/client/ssh/panel/SessionSecretDialog.d.ts +4 -1
  33. package/lib/types/client/ssh/panel/SshPanel.d.ts +8 -8
  34. package/lib/types/client/ssh/panel/TerminalTab.d.ts +3 -5
  35. package/lib/types/client/ssh/panel/TransferTab.d.ts +5 -1
  36. package/lib/types/client/ssh/panel/TunnelsTab.d.ts +3 -1
  37. package/lib/types/client/ssh/session-target.d.ts +17 -0
  38. package/lib/types/client/state.d.ts +4 -1
  39. package/lib/types/client/workspace-panel-entry.d.ts +30 -0
  40. package/lib/types/client/workspace-panel.d.ts +24 -0
  41. package/lib/types/client-http.d.ts +77 -6
  42. package/lib/types/core.d.ts +5 -16
  43. package/lib/types/fs.d.ts +18 -25
  44. package/lib/types/index.d.ts +138 -9
  45. package/lib/types/ledger.d.ts +16 -111
  46. package/lib/types/protocol.d.ts +0 -5
  47. package/lib/types/providers/index.d.ts +6 -3
  48. package/lib/types/providers/local/provider.d.ts +83 -23
  49. package/lib/types/providers/ssh/provider.d.ts +24 -57
  50. package/lib/types/remote/environment.d.ts +2 -0
  51. package/lib/types/remote/remote-fs.d.ts +12 -1
  52. package/lib/types/remote/remote-process.d.ts +22 -1
  53. package/lib/types/remote/remote-subprocess.d.ts +13 -1
  54. package/lib/types/remote/remote-terminal.d.ts +4 -0
  55. package/lib/types/remote-search.d.ts +3 -3
  56. package/lib/types/routes.d.ts +36 -8
  57. package/lib/types/runtime/dsh-capabilities.d.ts +18 -0
  58. package/lib/types/runtime/workspace-core.d.ts +73 -38
  59. package/lib/types/runtime/workspace-migration.d.ts +113 -0
  60. package/lib/types/ssh/connection/lease.d.ts +23 -0
  61. package/lib/types/ssh/connection/manager.d.ts +359 -0
  62. package/lib/types/ssh/connection/pool.d.ts +8 -1
  63. package/lib/types/ssh/engine.d.ts +97 -267
  64. package/lib/types/ssh/known-hosts.d.ts +22 -5
  65. package/lib/types/ssh/local-transfer-policy.d.ts +20 -0
  66. package/lib/types/ssh/plugin.d.ts +10 -15
  67. package/lib/types/ssh/protocol.d.ts +18 -0
  68. package/lib/types/ssh/routes.d.ts +15 -7
  69. package/lib/types/ssh/sftp/service.d.ts +216 -0
  70. package/lib/types/ssh/store.d.ts +20 -8
  71. package/lib/types/ssh/terminal/service.d.ts +49 -0
  72. package/lib/types/ssh/tunnel/service.d.ts +26 -0
  73. package/lib/types/ssh/vault.d.ts +51 -4
  74. package/lib/types/subprocess.d.ts +13 -21
  75. package/lib/types/switch/switch-fs.d.ts +52 -2
  76. package/lib/types/switch/switch-subprocess.d.ts +32 -3
  77. package/lib/types/tools.d.ts +7 -6
  78. package/lib/types/workspace-tool-ops.d.ts +33 -0
  79. package/lib/types/workspace.d.ts +18 -0
  80. package/lib/vault-3gpWct2Q.js +559 -0
  81. package/lib/workspace.js +2 -0
  82. package/package.json +58 -25
  83. package/scripts/export-legacy-workspaces.mjs +136 -0
  84. package/src/backend.ts +155 -483
  85. package/src/base/capability.ts +4 -0
  86. package/src/base/ledger-router.ts +214 -65
  87. package/src/base/ledger.ts +175 -48
  88. package/src/base/model.ts +18 -8
  89. package/src/base/plugin.ts +15 -4
  90. package/src/base/registry.ts +49 -41
  91. package/src/base/router.ts +0 -8
  92. package/src/client/api.ts +8 -36
  93. package/src/client/connect-host.ts +199 -0
  94. package/src/client/directory-flow.tsx +91 -36
  95. package/src/client/index.ts +56 -53
  96. package/src/client/locales.ts +14 -0
  97. package/src/client/session-connect-gate.ts +163 -0
  98. package/src/client/ssh/api.ts +245 -108
  99. package/src/client/ssh/apply.ts +23 -24
  100. package/src/client/ssh/locales.ts +16 -2
  101. package/src/client/ssh/ops-tab.tsx +79 -0
  102. package/src/client/ssh/panel/ClusterTab.tsx +9 -20
  103. package/src/client/ssh/panel/ConnectionErrorDialog.tsx +33 -0
  104. package/src/client/ssh/panel/SessionSecretDialog.tsx +5 -1
  105. package/src/client/ssh/panel/SshPanel.tsx +81 -77
  106. package/src/client/ssh/panel/TerminalTab.tsx +18 -37
  107. package/src/client/ssh/panel/TransferTab.tsx +43 -33
  108. package/src/client/ssh/panel/TunnelsTab.tsx +9 -26
  109. package/src/client/ssh/panel/panel.module.css +90 -127
  110. package/src/client/ssh/session-target.ts +69 -0
  111. package/src/client/state.ts +9 -1
  112. package/src/client/workspace-badges.ts +99 -96
  113. package/src/client/workspace-panel-entry.tsx +91 -0
  114. package/src/client/workspace-panel.tsx +227 -0
  115. package/src/client/workspace.module.css +76 -2
  116. package/src/client-http.ts +118 -18
  117. package/src/core.ts +37 -47
  118. package/src/fs.ts +137 -85
  119. package/src/index.ts +535 -244
  120. package/src/ledger.ts +62 -416
  121. package/src/protocol.ts +0 -6
  122. package/src/providers/index.ts +8 -4
  123. package/src/providers/local/provider.ts +259 -87
  124. package/src/providers/ssh/provider.ts +158 -155
  125. package/src/remote/environment.ts +22 -1
  126. package/src/remote/remote-fs.ts +123 -34
  127. package/src/remote/remote-process.ts +265 -203
  128. package/src/remote/remote-subprocess.ts +61 -18
  129. package/src/remote/remote-terminal.ts +106 -21
  130. package/src/remote-search.ts +122 -26
  131. package/src/routes.ts +416 -395
  132. package/src/runtime/dsh-capabilities.ts +19 -0
  133. package/src/runtime/workspace-core.ts +171 -88
  134. package/src/runtime/workspace-migration.ts +472 -0
  135. package/src/ssh/connection/lease.ts +35 -0
  136. package/src/ssh/connection/manager.ts +1083 -0
  137. package/src/ssh/connection/pool.ts +341 -275
  138. package/src/ssh/engine.ts +269 -1477
  139. package/src/ssh/known-hosts.ts +42 -18
  140. package/src/ssh/local-transfer-policy.ts +83 -0
  141. package/src/ssh/plugin.ts +20 -19
  142. package/src/ssh/protocol.ts +22 -1
  143. package/src/ssh/routes.ts +419 -187
  144. package/src/ssh/sftp/service.ts +967 -0
  145. package/src/ssh/store.ts +169 -72
  146. package/src/ssh/terminal/service.ts +177 -0
  147. package/src/ssh/tools.ts +42 -20
  148. package/src/ssh/tunnel/service.ts +217 -0
  149. package/src/ssh/vault.ts +245 -91
  150. package/src/subprocess.ts +88 -71
  151. package/src/switch/switch-fs.ts +141 -10
  152. package/src/switch/switch-subprocess.ts +80 -6
  153. package/src/tools.ts +217 -221
  154. package/src/workspace-tool-ops.ts +101 -0
  155. package/src/workspace.ts +52 -0
  156. package/lib/environment-BL1jddfB.js +0 -449
  157. package/lib/switch-fs-CAJpFY9C.js +0 -193
  158. package/lib/switch-fs-RrZtG2gv.js +0 -210
  159. package/lib/types/client/manager-button.d.ts +0 -32
  160. package/lib/types/client/ssh/mount.d.ts +0 -13
  161. package/lib/types/client/ssh/panel/HostsTab.d.ts +0 -10
  162. package/lib/types/client/ssh/panel/controller.d.ts +0 -23
  163. package/lib/types/client/ssh/sidebar-entry.d.ts +0 -25
  164. package/lib/types/client/workspace-gate.d.ts +0 -15
  165. package/lib/types/remote-runner.d.ts +0 -83
  166. package/lib/types/seam-state.d.ts +0 -69
  167. package/src/client/manager-button.tsx +0 -269
  168. package/src/client/ssh/mount.tsx +0 -83
  169. package/src/client/ssh/panel/HostsTab.tsx +0 -236
  170. package/src/client/ssh/panel/controller.ts +0 -48
  171. package/src/client/ssh/sidebar-entry.ts +0 -123
  172. package/src/client/workspace-gate.ts +0 -225
  173. package/src/remote-runner.ts +0 -201
  174. package/src/seam-state.ts +0 -185
package/src/seam-state.ts DELETED
@@ -1,185 +0,0 @@
1
- /**
2
- * The shared derived seam state: ONE ledger-derived routing snapshot
3
- * (records + normalized anchor index) plus the per-record fs/subprocess
4
- * instances, owned by the hardssh core and consumed by BOTH switch rows
5
- * (./fs and ./subprocess). Because both seams read the SAME state, they can
6
- * never disagree with each other or with the ledger's own index for a given
7
- * session cwd — eliminating the split-brain window of the old per-seam
8
- * async refresh caches.
9
- *
10
- * The state re-applies SYNCHRONOUSLY on every ledger commit: ledger
11
- * listeners run synchronously AFTER the commit already swapped the
12
- * in-memory records, so a synchronous re-apply is always current — no async
13
- * refresh pipeline, no stale-snapshot race, no revision-guard interleaving.
14
- * The initial file load is awaited before the state is marked ready; until
15
- * then routing degrades to local with a one-time warning from the rows.
16
- *
17
- * Per-record instances are built lazily on first route and kept alive
18
- * across refreshes (connections survive); removing a workspace drops its
19
- * instances so stale target keys resolve to NOTHING (fail closed), never to
20
- * the local backend.
21
- *
22
- * @module dsh-hardssh/seam-state
23
- */
24
-
25
- import type { SshWorkspaceRecord } from './protocol.ts'
26
- import { isPathUnderAnchor, normalizeAnchorPath, type SshWorkspaceLedger } from './ledger.ts'
27
- import { REMOTE_PREFIX, type WorkspaceWorld } from './switch/switch-fs.ts'
28
- import type { SshSubprocessRuntime } from './remote/remote-subprocess.ts'
29
-
30
- /** Per-record seam instances, built lazily by the owning rows. */
31
- interface SeamInstances {
32
- fs?: WorkspaceWorld
33
- subprocess?: SshSubprocessRuntime
34
- }
35
-
36
- /** One anchor-index entry: a normalized anchor plus its lowercased record id. */
37
- interface AnchorEntry {
38
- anchor: string
39
- id: string
40
- }
41
-
42
- /** Shared routing state for the fs/subprocess switch rows. */
43
- export class WorkspaceSeamState {
44
- private readonly records = new Map<string, SshWorkspaceRecord>()
45
- private readonly instances = new Map<string, SeamInstances>()
46
- private anchors: AnchorEntry[] = []
47
- private appliedRevision = -1
48
- private ready = false
49
- private readyResolvers: Array<() => void> = []
50
- private fsFactory: ((record: SshWorkspaceRecord) => WorkspaceWorld) | undefined
51
- private subFactory: ((record: SshWorkspaceRecord) => SshSubprocessRuntime) | undefined
52
-
53
- constructor(private readonly ledger: SshWorkspaceLedger) {}
54
-
55
- /** Bind the fs-row's per-record backend builder (called once at apply). */
56
- bindFs(factory: (record: SshWorkspaceRecord) => WorkspaceWorld): void {
57
- this.fsFactory = factory
58
- }
59
-
60
- /** Bind the subprocess-row's per-record runtime builder (called once at apply). */
61
- bindSub(factory: (record: SshWorkspaceRecord) => SshSubprocessRuntime): void {
62
- this.subFactory = factory
63
- }
64
-
65
- /** True once the initial ledger snapshot has been applied. */
66
- isReady(): boolean {
67
- return this.ready
68
- }
69
-
70
- /** Resolves once the initial ledger snapshot has been applied. */
71
- whenReady(): Promise<void> {
72
- if (this.ready) return Promise.resolve()
73
- return new Promise((resolve) => {
74
- this.readyResolvers.push(resolve)
75
- })
76
- }
77
-
78
- /**
79
- * Subscribe to ledger commits and kick the initial load. Returns the
80
- * subscription disposer (register it through ctx.effect for HMR cleanup).
81
- */
82
- attach(): () => void {
83
- const unsubscribe = this.ledger.subscribe(() => {
84
- this.applySync()
85
- })
86
- void this.ledger.load()
87
- .then(() => {
88
- this.applySync()
89
- this.markReady()
90
- })
91
- .catch((error: unknown) => {
92
- console.warn('[dsh-hardssh] failed to load the workspace ledger:', error instanceof Error ? error.message : String(error))
93
- })
94
- return unsubscribe
95
- }
96
-
97
- /** Re-apply the ledger's current in-memory state (idempotent, guarded). */
98
- applySync(): void {
99
- const snapshot = this.ledger.snapshotSync()
100
- if (snapshot.revision < this.appliedRevision) return
101
-
102
- const next = new Map<string, SshWorkspaceRecord>()
103
- for (const record of snapshot.records) next.set(record.id.toLowerCase(), record)
104
- // Drop instances of removed workspaces so stale routing can never reach them.
105
- for (const id of this.records.keys()) {
106
- if (!next.has(id)) this.instances.delete(id)
107
- }
108
- this.records.clear()
109
- for (const [id, record] of next) this.records.set(id, record)
110
- this.anchors = [...this.records.entries()]
111
- .map(([id, record]) => ({ anchor: normalizeAnchorPath(record.anchorPath), id }))
112
- .sort((a, b) => b.anchor.length - a.anchor.length) // longest prefix first
113
- this.appliedRevision = snapshot.revision
114
- }
115
-
116
- /** The record owning a session cwd, or undefined when the cwd is local. */
117
- private recordFor(cwd: string | undefined): SshWorkspaceRecord | undefined {
118
- if (cwd === undefined || cwd === '') return undefined
119
- for (const { anchor, id } of this.anchors) {
120
- if (isPathUnderAnchor(anchor, cwd)) {
121
- const record = this.records.get(id)
122
- if (record !== undefined) return record
123
- }
124
- }
125
- return undefined
126
- }
127
-
128
- private ensureFs(record: SshWorkspaceRecord): WorkspaceWorld | undefined {
129
- if (this.fsFactory === undefined) return undefined
130
- const id = record.id.toLowerCase()
131
- let entry = this.instances.get(id)
132
- if (entry === undefined) {
133
- entry = {}
134
- this.instances.set(id, entry)
135
- }
136
- if (entry.fs === undefined) entry.fs = this.fsFactory(record)
137
- return entry.fs
138
- }
139
-
140
- private ensureSubprocess(record: SshWorkspaceRecord): SshSubprocessRuntime | undefined {
141
- if (this.subFactory === undefined) return undefined
142
- const id = record.id.toLowerCase()
143
- let entry = this.instances.get(id)
144
- if (entry === undefined) {
145
- entry = {}
146
- this.instances.set(id, entry)
147
- }
148
- if (entry.subprocess === undefined) entry.subprocess = this.subFactory(record)
149
- return entry.subprocess
150
- }
151
-
152
- /** The fs world owning a session cwd, or undefined (local). */
153
- worldForFs(cwd: string | undefined): WorkspaceWorld | undefined {
154
- const record = this.recordFor(cwd)
155
- return record === undefined ? undefined : this.ensureFs(record)
156
- }
157
-
158
- /** The fs world for one namespaced target key (`wfs://<id>/…` or legacy
159
- * `ssh:<id>:`), or undefined when the owning workspace no longer exists
160
- * (stale key → fail closed). */
161
- worldForFsNamespace(namespace: string): WorkspaceWorld | undefined {
162
- let id: string | undefined
163
- if (namespace.startsWith('wfs://')) {
164
- id = namespace.slice('wfs://'.length, -1) // strip trailing '/'
165
- } else if (namespace.startsWith(REMOTE_PREFIX)) {
166
- id = namespace.slice(REMOTE_PREFIX.length, -1)
167
- }
168
- if (id === undefined || id === '') return undefined
169
- const record = this.records.get(id)
170
- return record === undefined ? undefined : this.ensureFs(record)
171
- }
172
-
173
- /** The subprocess runtime owning a session cwd, or undefined (local). */
174
- runtimeForSub(cwd: string | undefined): SshSubprocessRuntime | undefined {
175
- const record = this.recordFor(cwd)
176
- return record === undefined ? undefined : this.ensureSubprocess(record)
177
- }
178
-
179
- private markReady(): void {
180
- this.ready = true
181
- const resolvers = this.readyResolvers
182
- this.readyResolvers = []
183
- for (const resolve of resolvers) resolve()
184
- }
185
- }