dsh-remote-vs 0.0.0-stage → 0.8.3
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.
- package/LICENSE +21 -0
- package/README.en.md +385 -0
- package/README.md +347 -2
- package/cordis.patch.yml +259 -0
- package/icon.svg +7 -0
- package/lib/client.js +62602 -0
- package/lib/index.js +4287 -0
- package/lib/p2-bridge-refs.js +10 -0
- package/lib/p2-bridge.js +16 -0
- package/lib/p2-router-fs.js +252 -0
- package/lib/p2-router-refs.js +466 -0
- package/lib/p2-router-spill.js +62 -0
- package/lib/p2-router-subprocess.js +64 -0
- package/lib/p2-shell-bash.js +24 -0
- package/lib/ssh-proc-arI5lm4W.js +632 -0
- package/lib/types/changes/recorder.d.ts +240 -0
- package/lib/types/changes/status.d.ts +201 -0
- package/lib/types/changes/text-diff.d.ts +65 -0
- package/lib/types/client/CodeEditor.d.ts +125 -0
- package/lib/types/client/Dialog.d.ts +14 -0
- package/lib/types/client/DirectoryFlow.d.ts +30 -0
- package/lib/types/client/EditorPane.d.ts +30 -0
- package/lib/types/client/HostsView.d.ts +10 -0
- package/lib/types/client/NativeExplorer.d.ts +39 -0
- package/lib/types/client/RemoteDirectoryPicker.d.ts +16 -0
- package/lib/types/client/RemoteEditorView.d.ts +20 -0
- package/lib/types/client/RemoteExplorer.d.ts +18 -0
- package/lib/types/client/RemoteRoot.d.ts +16 -0
- package/lib/types/client/SearchView.d.ts +14 -0
- package/lib/types/client/SettingsHosts.d.ts +15 -0
- package/lib/types/client/TerminalView.d.ts +16 -0
- package/lib/types/client/WorkspaceWizard.d.ts +16 -0
- package/lib/types/client/WorkspacesView.d.ts +14 -0
- package/lib/types/client/XTerm.d.ts +15 -0
- package/lib/types/client/api.d.ts +162 -0
- package/lib/types/client/changes/ChangesDock.d.ts +35 -0
- package/lib/types/client/changes/model.d.ts +36 -0
- package/lib/types/client/changes/undo-affordance.d.ts +48 -0
- package/lib/types/client/changes/undo-context.d.ts +25 -0
- package/lib/types/client/composer.d.ts +165 -0
- package/lib/types/client/i18n.d.ts +17 -0
- package/lib/types/client/index.d.ts +14 -0
- package/lib/types/client/remote-file-open-target.d.ts +247 -0
- package/lib/types/client/remote-file-open.d.ts +35 -0
- package/lib/types/client/reveal.d.ts +15 -0
- package/lib/types/client/scm/DiffView.d.ts +24 -0
- package/lib/types/client/scm/HistoryPanel.d.ts +12 -0
- package/lib/types/client/scm/RepoPicker.d.ts +24 -0
- package/lib/types/client/scm/ScmPanel.d.ts +15 -0
- package/lib/types/client/scm/actions.d.ts +170 -0
- package/lib/types/client/scm/diff-model.d.ts +29 -0
- package/lib/types/client/scm/stage-queue.d.ts +99 -0
- package/lib/types/client/scm/status-map.d.ts +157 -0
- package/lib/types/client/scm/untracked-dirs.d.ts +72 -0
- package/lib/types/client/selection-popup.d.ts +61 -0
- package/lib/types/client/store.d.ts +437 -0
- package/lib/types/client/theme.d.ts +15 -0
- package/lib/types/client/ui.d.ts +46 -0
- package/lib/types/client/upload-plan.d.ts +78 -0
- package/lib/types/client/upload-source.d.ts +46 -0
- package/lib/types/client/use-remote-chat-target.d.ts +17 -0
- package/lib/types/client/vsc/FindWidget.d.ts +81 -0
- package/lib/types/client/vsc/change-marks.d.ts +150 -0
- package/lib/types/client/vsc/editor-langs.d.ts +5 -0
- package/lib/types/client/vsc/editor-palette.d.ts +37 -0
- package/lib/types/client/vsc/editor-theme.d.ts +32 -0
- package/lib/types/client/vsc/fallback-highlight.d.ts +26 -0
- package/lib/types/client/vsc/kit.d.ts +195 -0
- package/lib/types/client/vsc/markdown.d.ts +11 -0
- package/lib/types/client/vsc/reveal-line.d.ts +38 -0
- package/lib/types/client/vsc/tokenize.d.ts +104 -0
- package/lib/types/deploy.d.ts +94 -0
- package/lib/types/fs-ops.d.ts +8 -0
- package/lib/types/git-format.d.ts +34 -0
- package/lib/types/git-ops.d.ts +183 -0
- package/lib/types/http.d.ts +14 -0
- package/lib/types/importers.d.ts +20 -0
- package/lib/types/index.d.ts +87 -0
- package/lib/types/keygen.d.ts +19 -0
- package/lib/types/loopback.d.ts +7 -0
- package/lib/types/p2/bridge-refs.d.ts +21 -0
- package/lib/types/p2/bridge.d.ts +24 -0
- package/lib/types/p2/file-refs.d.ts +73 -0
- package/lib/types/p2/helper-rpc.d.ts +63 -0
- package/lib/types/p2/in-flight-gate.d.ts +25 -0
- package/lib/types/p2/router-fs.d.ts +92 -0
- package/lib/types/p2/router-refs.d.ts +45 -0
- package/lib/types/p2/router-spill.d.ts +11 -0
- package/lib/types/p2/router-subprocess.d.ts +23 -0
- package/lib/types/p2/shell-bash.d.ts +16 -0
- package/lib/types/p2/ssh-proc.d.ts +22 -0
- package/lib/types/p2/worlds.d.ts +179 -0
- package/lib/types/paths.d.ts +12 -0
- package/lib/types/pool.d.ts +105 -0
- package/lib/types/protocol.d.ts +452 -0
- package/lib/types/routes.d.ts +27 -0
- package/lib/types/search-ops.d.ts +68 -0
- package/lib/types/sftp.d.ts +90 -0
- package/lib/types/store.d.ts +66 -0
- package/lib/types/terminal.d.ts +51 -0
- package/lib/types/timeouts.d.ts +83 -0
- package/lib/types/tools.d.ts +41 -0
- package/lib/worlds-PUumRIh7.js +18765 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +189 -4
- package/src/changes/recorder.ts +902 -0
- package/src/changes/status.ts +448 -0
- package/src/changes/text-diff.ts +377 -0
- package/src/client/CodeEditor.tsx +923 -0
- package/src/client/Dialog.tsx +76 -0
- package/src/client/DirectoryFlow.tsx +431 -0
- package/src/client/EditorPane.tsx +512 -0
- package/src/client/HostsView.tsx +312 -0
- package/src/client/NativeExplorer.tsx +121 -0
- package/src/client/RemoteDirectoryPicker.tsx +157 -0
- package/src/client/RemoteEditorView.tsx +54 -0
- package/src/client/RemoteExplorer.tsx +1398 -0
- package/src/client/RemoteRoot.tsx +389 -0
- package/src/client/SearchView.tsx +407 -0
- package/src/client/SettingsHosts.tsx +136 -0
- package/src/client/TerminalView.tsx +308 -0
- package/src/client/WorkspaceWizard.tsx +199 -0
- package/src/client/WorkspacesView.tsx +93 -0
- package/src/client/XTerm.tsx +308 -0
- package/src/client/api.ts +331 -0
- package/src/client/changes/ChangesDock.tsx +348 -0
- package/src/client/changes/changes.css +230 -0
- package/src/client/changes/model.ts +347 -0
- package/src/client/changes/undo-affordance.ts +389 -0
- package/src/client/changes/undo-context.ts +34 -0
- package/src/client/composer.ts +436 -0
- package/src/client/css.d.ts +7 -0
- package/src/client/i18n.ts +702 -0
- package/src/client/index.tsx +286 -0
- package/src/client/remote-file-open-target.ts +553 -0
- package/src/client/remote-file-open.ts +261 -0
- package/src/client/reveal.ts +33 -0
- package/src/client/scm/DiffView.tsx +570 -0
- package/src/client/scm/HistoryPanel.tsx +353 -0
- package/src/client/scm/RepoPicker.tsx +238 -0
- package/src/client/scm/ScmPanel.tsx +857 -0
- package/src/client/scm/actions.ts +460 -0
- package/src/client/scm/diff-model.ts +206 -0
- package/src/client/scm/stage-queue.ts +264 -0
- package/src/client/scm/status-map.ts +381 -0
- package/src/client/scm/untracked-dirs.ts +139 -0
- package/src/client/selection-popup.ts +157 -0
- package/src/client/store.tsx +1670 -0
- package/src/client/theme.ts +54 -0
- package/src/client/ui.tsx +136 -0
- package/src/client/upload-plan.ts +153 -0
- package/src/client/upload-source.ts +145 -0
- package/src/client/use-remote-chat-target.ts +153 -0
- package/src/client/vsc/FindWidget.tsx +446 -0
- package/src/client/vsc/change-marks.ts +586 -0
- package/src/client/vsc/editor-langs.ts +137 -0
- package/src/client/vsc/editor-palette.ts +107 -0
- package/src/client/vsc/editor-theme.ts +93 -0
- package/src/client/vsc/fallback-highlight.ts +145 -0
- package/src/client/vsc/kit.tsx +777 -0
- package/src/client/vsc/markdown.tsx +172 -0
- package/src/client/vsc/reveal-line.ts +142 -0
- package/src/client/vsc/tokenize.ts +569 -0
- package/src/client/vsc/vsc.css +2669 -0
- package/src/deploy.ts +481 -0
- package/src/fs-ops.ts +57 -0
- package/src/git-format.ts +205 -0
- package/src/git-ops.ts +693 -0
- package/src/http.ts +55 -0
- package/src/importers.ts +141 -0
- package/src/index.ts +442 -0
- package/src/keygen.ts +77 -0
- package/src/loopback.ts +47 -0
- package/src/p2/bridge-refs.ts +27 -0
- package/src/p2/bridge.ts +32 -0
- package/src/p2/file-refs.ts +392 -0
- package/src/p2/helper-rpc.ts +394 -0
- package/src/p2/in-flight-gate.ts +63 -0
- package/src/p2/router-fs.ts +327 -0
- package/src/p2/router-refs.ts +166 -0
- package/src/p2/router-spill.ts +75 -0
- package/src/p2/router-subprocess.ts +92 -0
- package/src/p2/shell-bash.ts +30 -0
- package/src/p2/ssh-proc.ts +802 -0
- package/src/p2/worlds.ts +735 -0
- package/src/paths.ts +50 -0
- package/src/pool.ts +811 -0
- package/src/protocol.ts +460 -0
- package/src/routes.ts +802 -0
- package/src/search-ops.ts +220 -0
- package/src/sftp.ts +677 -0
- package/src/store.ts +364 -0
- package/src/terminal.ts +291 -0
- package/src/timeouts.ts +122 -0
- package/src/tools.ts +320 -0
- package/vendor/dsh-remote-helpers/dsh-ssh-helper-0.2.1-alpha.2-linux-x64.helper.json +16 -0
- package/vendor/dsh-remote-helpers/dsh-ssh-helper-0.2.1-alpha.2-linux-x64.tar.gz +4 -0
- package/vendor/dsh-remote-helpers/dsh-ssh-helper-0.2.1-alpha.2-linux-x64.tar.gz.sha256 +1 -0
- package/vendor/dsh-remote-helpers/glibc-ceiling.patch +31 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 xiray-xia
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.en.md
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
# dsh-remote-vs · VSCode-style remote development for DSH
|
|
2
|
+
|
|
3
|
+
[](./LICENSE)
|
|
4
|
+
|
|
5
|
+
[中文](./README.md) | English
|
|
6
|
+
|
|
7
|
+
SSH host management inside the DSH Host process + helper push pipeline + a **VS Code-style remote
|
|
8
|
+
workbench** (explorer / search / source control / commit graph / multi-tab editor / side-by-side diff /
|
|
9
|
+
multiple terminals). Hosts live in DSH Settings ("Remote hosts"); with `dsh-better-sidebar` installed
|
|
10
|
+
there is an extra standalone "Remote" tab, otherwise it falls back to a native right-Sidebar "Remote"
|
|
11
|
+
page (same content, self-opened from the guide, per-session instances). The editor is ALSO mounted into
|
|
12
|
+
the conversation view slot as a "远程编辑器" (remote editor) tab next to "对话 / 轨迹", so the code area is
|
|
13
|
+
full width; the sidebar copy is the narrow fallback. Since P2, native tools inside remote workspaces
|
|
14
|
+
transparently run remotely (see below).
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
┌─ Local DSH Host ─────────────────────────┐
|
|
18
|
+
│ Plugin host half │ Client half │
|
|
19
|
+
│ hosts/keys/push/ │ settings + native │
|
|
20
|
+
│ routing │ entry takeover │
|
|
21
|
+
│ /api/dsh-remote/* │ explorer page │
|
|
22
|
+
└─────────────────────┴────────────────────┘
|
|
23
|
+
│ ssh2 (files/proc/terminal) + helper RPC (remote fs)
|
|
24
|
+
▼
|
|
25
|
+
┌─ Remote Linux ─────────────────────────────┐
|
|
26
|
+
│ ~/.dsh-helpers/<ver>/<arch>/dsh-ssh-helper│
|
|
27
|
+
│ user-chosen remote working directory │
|
|
28
|
+
└────────────────────────────────────────────┘
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
dsh plugin --profile web add dsh-remote-vs
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
(From npm, recommended; installing straight from the repository also works:
|
|
38
|
+
`dsh plugin --profile web add github:xiray-xia/dsh-remote-vs`.)
|
|
39
|
+
|
|
40
|
+
Requires DSH `0.2.1-alpha.3` or newer (`engines.dsh` enforces the range; **the tested
|
|
41
|
+
baseline is `0.2.1-rc.2`**). `--profile web` selects which profile to install into —
|
|
42
|
+
this is a Web plugin, so it has to go into the profile your Web GUI runs from (`web` is
|
|
43
|
+
the common name; use your own profile's name).
|
|
44
|
+
|
|
45
|
+
**That one command is all it takes — no build scripts have to be approved.** The package
|
|
46
|
+
has no `prepare`/`postinstall` and no package in the dependency tree carries an install
|
|
47
|
+
script (`ssh2` is **bundled into the artifact**, not depended on); the repository route
|
|
48
|
+
also ships the built `lib/`. The old "allow the printed build scripts in that profile's
|
|
49
|
+
`pnpm-workspace.yaml` and re-run" step was pnpm 12's hard policy gate for dependencies
|
|
50
|
+
with lifecycle scripts (`ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED` / `ERR_PNPM_IGNORED_BUILDS`);
|
|
51
|
+
it cannot be waived from the dependency side, only removed from the tree. Then restart
|
|
52
|
+
`dsh web` and open Settings → Remote hosts to configure.
|
|
53
|
+
|
|
54
|
+
> The package carries the prebuilt helper (`vendor/dsh-remote-helpers/`, ~45 MB), so the
|
|
55
|
+
> first install is a large download — it is the **only** channel that delivers a helper
|
|
56
|
+
> (the official releases ship no helper executable), and dropping it would leave new hosts
|
|
57
|
+
> undeployable.
|
|
58
|
+
|
|
59
|
+
## Capabilities
|
|
60
|
+
|
|
61
|
+
### Remote workbench (VS Code-style UI)
|
|
62
|
+
|
|
63
|
+
| Capability | Notes |
|
|
64
|
+
| --- | --- |
|
|
65
|
+
| Explorer | Lazy tree + expand/collapse + hidden-files toggle; context menu: new / rename / delete / copy / move / paste / find in folder / insert `@` reference; git status colours and folder roll-up marks |
|
|
66
|
+
| Search | Two modes: **file names** (remote `find`) and **contents** (remote `grep`). Content mode has a scope folder, `Aa` (case) / `ab_` (whole word) / `.*` (regex), results grouped per file with match highlights, and clicking a hit jumps to that line |
|
|
67
|
+
| Source control | **Repository-folder picker** (for workspaces whose git repository lives in a sub-folder: browse the remote tree, one-click jump to any repository discovered inside the workspace, type a relative or absolute path, and see which repository each folder resolves to); changes / staged / untracked / ignored buckets; **untracked directory rows expand on click**; per-file AND **per-hunk** stage / unstage / discard (behind a confirm) / commit; branches, remotes and stash panes; sync (fetch/pull/push) |
|
|
68
|
+
| Session change review | The **collapsible** panel above the composer: what THIS SESSION changed (one row per file, file-type icon, `+a −b` on the right); clicking a file opens it in the remote editor with **line shading plus character-level inline diff**, `n/N ↑↓` jumps between change points, and hovering a changed line offers **Undo this change** (remote `git apply -R`). See "Session change review" below |
|
|
69
|
+
| Side-by-side diff | Panes stay **exactly equal** + per-hunk buttons in the middle gutter + a **top horizontal scrollbar** (a very long line can no longer break the symmetry) + pinned line numbers, **rendering only the rows inside the viewport** (a 3000-line file goes from 9000 row containers to ~174, with exactly the same scroll range as a full render); switch between "before / after", and the after side is editable and savable |
|
|
70
|
+
| Commit graph | SVG branch/merge graph + commit details + click a commit to expand its changed files + open the diff |
|
|
71
|
+
| Multi-tab editor | Open/save/reload, dirty marks, several files at once; image preview, Markdown/HTML preview (source vs preview) |
|
|
72
|
+
| Syntax highlighting | VS Code **Dark+ / Light+** palettes; covers java/aidl/smali, c/c++, go, rust, js/ts, json, py, yaml, xml, html/css, md, sh, properties/rc/toml, diff …; a **fallback colorer** takes over when no grammar matches, so a file is never left monochrome. Honest caveat: kotlin / gradle / smali are coloured by the Java grammar (no dedicated package), which gets keywords, strings and comments right but does not split their own syntax |
|
|
73
|
+
| Find | In-editor `Ctrl+F` (`Aa` / `ab_` / `.*`, `3/17` count, ↑↓, `F3`/`Shift+F3`, `Esc`); **selected text is carried into the box by `Ctrl+F`**; the widget is pinned to the top-right and does not scroll away |
|
|
74
|
+
| Jump | Clicking a search hit opens the file at that line, centres it and **lights the landing line** (fades after ~2.6s); repeat jumps in the same file work |
|
|
75
|
+
| Terminals | Tabbed multi-terminal (`+` to create, `✕`/middle-click to close behind a confirm), across hosts; session ids are persisted so a page refresh reattaches to the running shells, and a session is kept for **10 minutes after its last socket goes away** (enough for a refresh or a browser-tab switch) before the remote shell is closed |
|
|
76
|
+
| Workspaces | Binding list + routing-mode dropdown + the dual-mode (local / remote) new-workspace wizard |
|
|
77
|
+
|
|
78
|
+
### Hosts and transport
|
|
79
|
+
|
|
80
|
+
| Capability | Notes |
|
|
81
|
+
| --- | --- |
|
|
82
|
+
| Host management | CRUD + connectivity test; key / password auth; config in `$DSH_HOME/dsh-remote.json` (0600), independent from `dsh-ssh.json` |
|
|
83
|
+
| Key bootstrap | One click generates a dedicated keypair on password hosts and deploys `authorized_keys`, key auth from then on |
|
|
84
|
+
| Push pipeline | Remote arch/OS probe → version compare → skip-if-same / else push; the executable channel pushes a self-built helper. A failed deploy no longer retries on every remote file operation: it backs off from **5 seconds up to 5 minutes**, and the Settings "Push" button forces an immediate retry |
|
|
85
|
+
| Remote files | SFTP browse / read / write / create / delete / rename. Boundaries hardened: 0-byte files no longer deadlock, and every SFTP operation times out at 60s and releases its channel; **a single directory listing is capped at 20000 entries** (the first 20000 after sorting — directories first, then names — announced in the host log; the protocol carries no "truncated" field, so the UI has no marker) |
|
|
86
|
+
| Remote terminal | ssh2 PTY + WebSocket, xterm rendering (copy-paste/theme-follow/reconnect-on-refresh); **a reattach only takes over, it does not resize** (a page reload no longer shrinks a running shell to 80×24); an output backlog applies real backpressure to the far end; **frames are capped at 1 MiB**, and pastes past 64 Ki characters are chunked on code points by the client instead of dropping the connection |
|
|
87
|
+
| Concurrent channels | At most **6** remote channels per host at a time (no longer colliding with the far end's sshd `MaxSessions`); the 7th queues for up to **30 seconds** and then fails with `remote host <alias> is busy` instead of piling on connections |
|
|
88
|
+
| Timeouts and process cleanup | exec runs in its own session (`setsid --wait`); on timeout it sends TERM and **escalates to KILL after 3 seconds**, reaping the child processes with it; a host without `setsid --wait` is detected by a one-shot probe and falls back to the old behaviour, with a host-log line saying the timed-out remote command may still be running |
|
|
89
|
+
| Git graph | Remote repo status + branches + SVG commit graph + commit details (exec + frontend parsing, zero Host additions) |
|
|
90
|
+
| Import | One-click import from `~/.ssh/config` and from an existing `dsh-ssh.json` (read once, no coupling) |
|
|
91
|
+
|
|
92
|
+
### Agent capabilities
|
|
93
|
+
|
|
94
|
+
| Capability | Notes |
|
|
95
|
+
| --- | --- |
|
|
96
|
+
| Agent tools | `remote_hosts` / `remote_exec` / `remote_read` / `remote_write` / `remote_ls` |
|
|
97
|
+
| **P2 transparent remote execution** | Native `read/write/edit/bash/glob/grep/jobs` inside remote workspaces run directly on the far end |
|
|
98
|
+
| **P3 remote @ references** | `@` in remote sessions completes remote files (fileReferences router: scoped live listings / bare queries via a remote rg index) |
|
|
99
|
+
|
|
100
|
+
## Project structure
|
|
101
|
+
|
|
102
|
+
```text
|
|
103
|
+
dsh_remote_vs/
|
|
104
|
+
├── cordis.patch.yml # composition: plugin row + private local realm + routers + preset-remote
|
|
105
|
+
├── src/
|
|
106
|
+
│ ├── index.ts # plugin entry: store/pool/worlds/routes/tools/preset-switch wiring
|
|
107
|
+
│ ├── protocol.ts # frontend/backend wire contract (route table + pure types, shared)
|
|
108
|
+
│ ├── paths.ts # four-piece addressing under $DSH_HOME (store/keys/build cache/alias dirs)
|
|
109
|
+
│ ├── store.ts # host + binding + tombstone persistence (0600 JSON, atomic writes)
|
|
110
|
+
│ ├── pool.ts # ssh2 long-lived pool (exec/shell/sftp/putFile, TOFU host keys)
|
|
111
|
+
│ ├── sftp.ts # remote file engine (SFTP windowed reads-writes/tree ops)
|
|
112
|
+
│ ├── keygen.ts # password-host key bootstrap (generate→deploy→flip)
|
|
113
|
+
│ ├── deploy.ts # push pipeline (probe/compare/ship/double-verify/manifest)
|
|
114
|
+
│ ├── importers.ts # ssh_config / dsh-ssh.json parsing importers
|
|
115
|
+
│ ├── routes.ts # /api/dsh-remote/* route table + terminal WebSocket upgrade
|
|
116
|
+
│ ├── terminal.ts # PTY session registry (reconnect-on-refresh/disconnect cleanup)
|
|
117
|
+
│ ├── tools.ts # the 5 Agent tool definitions
|
|
118
|
+
│ ├── http.ts / loopback.ts # JSON body IO + loopback trust fence
|
|
119
|
+
│ ├── git-format.ts # git command builders + pure frontend parsing (zero Host additions)
|
|
120
|
+
│ ├── git-ops.ts # the full remote git surface: status/diff/branch/remote/stash/commit/hunk patch builders + parsers
|
|
121
|
+
│ ├── fs-ops.ts # remote file-operation command builders (copy/move/remove/stat)
|
|
122
|
+
│ ├── search-ops.ts # remote content search (grep builder + NUL-separated parser + match splitting)
|
|
123
|
+
│ ├── p2/ # ── P2 transparent routing ──
|
|
124
|
+
│ │ ├── worlds.ts # world decisions (cwd key/alias/tombstones) + remote RPC/FS/proc contexts
|
|
125
|
+
│ │ ├── helper-rpc.ts # helper wire protocol over an ssh2 exec channel (handshake/heartbeat)
|
|
126
|
+
│ │ ├── router-fs.ts # transparent fs router (target→world map/path translation/policy rewrite)
|
|
127
|
+
│ │ ├── router-subprocess.ts # transparent process router (argv0 reduction/pwsh gate)
|
|
128
|
+
│ │ ├── ssh-proc.ts # ssh2 process runtime (setsid group kill/marker/stdin/spill)
|
|
129
|
+
│ │ ├── shell-bash.ts # routed bash executor (local verbatim/remote strips DSH_*)
|
|
130
|
+
│ │ ├── router-spill.ts # transparent spill router (session world key/remote dir)
|
|
131
|
+
│ │ ├── router-refs.ts # transparent @ reference router (agent world key, P3)
|
|
132
|
+
│ │ ├── file-refs.ts # remote @ search (live fs-ssh listings/rg index/ported stock scoring)
|
|
133
|
+
│ │ ├── bridge.ts # private-realm local publishes (localFs and other early services)
|
|
134
|
+
│ │ └── bridge-refs.ts # standalone refs bridge (local-refs needs workingDirectory: must split)
|
|
135
|
+
│ └── client/ # ── browser half (React, bundled into lib/client.js) ──
|
|
136
|
+
│ ├── index.tsx # surface decision (better-first/settle-boost/native fallback) + settings + directoryFlow takeover
|
|
137
|
+
│ ├── store.tsx # module-level state shared by both surfaces (useSyncExternalStore; open files/terminals/view live here)
|
|
138
|
+
│ ├── RemoteRoot.tsx # sidebar surface: VS Code activity rail (explorer/search/SCM/history/editor/terminal/workspaces)
|
|
139
|
+
│ ├── RemoteEditorView.tsx # conversation-view surface (the "remote editor" tab next to 对话/轨迹, full width)
|
|
140
|
+
│ ├── RemoteExplorer.tsx # explorer: tree + context menu + file ops + @ references + status marks
|
|
141
|
+
│ ├── SearchView.tsx # search panel: name / content modes + Aa/ab_/.* + grouped hits + jump
|
|
142
|
+
│ ├── EditorPane.tsx # editor pane: tabs + info bar (build stamp / tok count) + image/MD/HTML preview
|
|
143
|
+
│ ├── CodeEditor.tsx # CodeMirror wrapper: grammar table/theme/find widget/jump/fallback wiring
|
|
144
|
+
│ ├── scm/ # ── source control ──
|
|
145
|
+
│ │ ├── ScmPanel.tsx # changes/staged/untracked + per-file and per-hunk actions + commit box
|
|
146
|
+
│ │ ├── DiffView.tsx # side-by-side diff (top scrollbar/hunk buttons/editable after side)
|
|
147
|
+
│ │ ├── HistoryPanel.tsx # SVG commit graph + commit details + expanded changed files
|
|
148
|
+
│ │ ├── actions.ts # remote git action layer (over the exec channel)
|
|
149
|
+
│ │ ├── diff-model.ts # unified diff → side-by-side row projection (pure)
|
|
150
|
+
│ │ └── status-map.ts # status bucketing + tree marks (pure)
|
|
151
|
+
│ ├── vsc/ # ── VS Code skin and controls (zero new runtime deps) ──
|
|
152
|
+
│ │ ├── kit.tsx / vsc.css # icons/dialogs/context menu/segmented control + all styles (scoped to .rvs-root)
|
|
153
|
+
│ │ ├── editor-palette.ts # Dark+/Light+ palette + fixed class names (rvs-ck-*)
|
|
154
|
+
│ │ ├── editor-theme.ts # editor chrome theme (live light/dark swap)
|
|
155
|
+
│ │ ├── editor-langs.ts # file extension → CodeMirror grammar
|
|
156
|
+
│ │ ├── fallback-highlight.ts# coloring used when no grammar matched (via tokenize.ts)
|
|
157
|
+
│ │ ├── FindWidget.tsx # Ctrl+F find widget (pinned top-right)
|
|
158
|
+
│ │ ├── reveal-line.ts # jump + landing-line highlight
|
|
159
|
+
│ │ ├── tokenize.ts / markdown.tsx # read-only pane tokenizer / dependency-free markdown renderer
|
|
160
|
+
│ ├── NativeExplorer.tsx# native page type definition + seat body (no-sidebar fallback)
|
|
161
|
+
│ ├── composer.ts / use-remote-chat-target.ts / selection-popup.ts # remote @refs/selection-to-chat
|
|
162
|
+
│ ├── api.ts # typed fetch wrapper over /api/dsh-remote
|
|
163
|
+
│ ├── HostsView / WorkspacesView / WorkspaceWizard / TerminalView / XTerm / Dialog
|
|
164
|
+
│ ├── SettingsHosts.tsx # settings occupants (host mgmt/binding list, self-fetching)
|
|
165
|
+
│ ├── DirectoryFlow.tsx # native entry occupant (local dual-mode/remote wizard)
|
|
166
|
+
│ ├── RemoteDirectoryPicker.tsx# remote directory browser dialog
|
|
167
|
+
│ └── i18n.ts / theme.ts / ui.tsx # bilingual dictionary/theme signal/shared styles
|
|
168
|
+
├── vendor/dsh-remote-helpers/ # helper seeds (shipped, seeded into cache at startup, see below)
|
|
169
|
+
├── docs/ # local-only working docs (never pushed to GitHub)
|
|
170
|
+
│ ├── P2-PLAN.md # P2 architecture/phases/acceptance matrix (with per-item status)
|
|
171
|
+
│ └── MAINTENANCE.md # local-only (.gitignore), not shipped: DSH upgrade impact + helper procedure
|
|
172
|
+
├── locale/ # bilingual plugin metadata
|
|
173
|
+
└── lib/ # build output (gitignored, restart after link install to take effect)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`vendor/dsh-remote-helpers/` (committed) holds helper seeds: at startup the plugin spreads them into
|
|
177
|
+
`$DSH_HOME/dsh-remote-helpers/` (copy-if-absent, never overwrites), so fresh installs can push right away.
|
|
178
|
+
When a new helper is built, swap the 4-piece set (tarball + sha256 + helper.json + patch notes)
|
|
179
|
+
into vendor and commit (details live in the local-only `docs/MAINTENANCE.md` §4, which is gitignored and not distributed).
|
|
180
|
+
|
|
181
|
+
`.spike/` (gitignored) holds live-host verification scripts plus the local self-checks:
|
|
182
|
+
`p2-router.ts` main regression (real remote + real Cordis + real router classes) and probes like
|
|
183
|
+
`grep-probe.mjs` (build with `tsconfig.scratch.json`), plus `model-check.mjs` / `reveal-check.mjs` /
|
|
184
|
+
`fallback-check.mjs` / `client-load-check.mjs` / `tool-schema-check.mjs` which need no host at all
|
|
185
|
+
(see "Development").
|
|
186
|
+
|
|
187
|
+
## Remote workbench (VS Code style)
|
|
188
|
+
|
|
189
|
+

|
|
190
|
+
|
|
191
|
+
Both surfaces share one state module (`store.tsx`): **the sidebar "Remote" page** (a standalone tab when
|
|
192
|
+
`dsh-better-sidebar` is installed, otherwise the native right-Sidebar page) and **the conversation-view
|
|
193
|
+
"remote editor" tab** (next to 对话/轨迹, giving the code area full width).
|
|
194
|
+
|
|
195
|
+
- **Activity rail**: explorer / search / source control / commit graph / editor / terminal / workspaces.
|
|
196
|
+
A panel stays mounted after its first visit, so switching back neither refetches nor flashes. While it is
|
|
197
|
+
hidden it does **no remote work** (directory listings, the SCM stash/branch/remote queries and editor
|
|
198
|
+
image reads all wait for the next foreground pass), and visibility is MEASURED on the plugin root
|
|
199
|
+
(IntersectionObserver) instead of being taken from a host prop.
|
|
200
|
+
- **Editor**: tabs plus an info bar (path, language, **build stamp**, `tok N` coloured-token count,
|
|
201
|
+
saved state, reload/save). Those three marks exist for troubleshooting: the stamp answers "is the
|
|
202
|
+
browser running the bundle I just built?", `tok 0` answers "did a grammar match this extension?".
|
|
203
|
+
- **Find and jump**: `Ctrl+F` opens a widget pinned to the editor's top-right corner
|
|
204
|
+
(`position: fixed` anchored to the editor's rect, so no ancestor scroller can carry it away);
|
|
205
|
+
a selection is carried into it by `Ctrl+F`; a jump from the search results centres the line and
|
|
206
|
+
lights it up.
|
|
207
|
+
- **Terminals**: tabbed sessions with the session id persisted one by one, so a page refresh reattaches
|
|
208
|
+
to the shells that are still running; **a reattach only takes over, it does not resize** (a live shell is
|
|
209
|
+
never shrunk to 80×24 — the true size comes from the one tab that can measure itself, sent as a `resize`
|
|
210
|
+
frame); a session is kept for **10 minutes** after its last socket goes away before the remote shell is
|
|
211
|
+
closed (it used to be kept forever, so closing the browser left the shell holding an sshd session slot).
|
|
212
|
+
- **Known limits (stated honestly)**:
|
|
213
|
+
- There is **no public API to select the "remote editor" view**; the plugin can only click the shell's
|
|
214
|
+
own tab by its label and toasts a hint when that fails (`revealEditor()`).
|
|
215
|
+
- Untracked directories arrive as a **single `dir/` row**; clicking it **expands** the files inside
|
|
216
|
+
(one path-limited `git status -uall -- dir/`, so the cost tracks the directory, not the repository;
|
|
217
|
+
click again to collapse). An expansion is a snapshot of the moment it was opened — collapse and
|
|
218
|
+
re-expand to refresh it.
|
|
219
|
+
- Opening a file is capped at 8MiB (official helper constant); beyond that it is shown read-only.
|
|
220
|
+
- The find widget floats above the code (like VS Code) and takes no layout space.
|
|
221
|
+
- Output backpressure is **per shell**: one tab that cannot keep up pauses every tab attached to that
|
|
222
|
+
shell — a deliberate trade, preferring to hold the remote command back on behalf of the slowest
|
|
223
|
+
consumer over letting host memory grow without bound.
|
|
224
|
+
- A directory listing past 20000 entries is truncated, and since the protocol has no "truncated" field
|
|
225
|
+
the UI shows **no marker at all** — only the host log line
|
|
226
|
+
`listing <path> on <alias> shows 20000 of N entries`.
|
|
227
|
+
|
|
228
|
+
## Session change review (Cursor style)
|
|
229
|
+
|
|
230
|
+

|
|
231
|
+
|
|
232
|
+
The **collapsible** panel above the composer (titled "本次改动") answers, per **session**, "what did this turn
|
|
233
|
+
change?". Its data comes from the plugin's own change ledger (`/api/dsh-remote/changes`), not from DSH's
|
|
234
|
+
`workspaceChanges` — the latter cannot see a routed session at all (the probe measured 58 remote sessions
|
|
235
|
+
with zero change events).
|
|
236
|
+
|
|
237
|
+
- **The panel**: one row per file (file-type icon + relative path + `+a −b` on the right), aligned left and
|
|
238
|
+
right with the composer card; click the header to collapse/expand, `⟳` forces an immediate rescan. With no
|
|
239
|
+
changes it says "this session has not changed any file yet"; when it cannot compute, it states the
|
|
240
|
+
**specific reason** (see the limits below) instead of handing you an empty list to guess about.
|
|
241
|
+
- **Clicking a file** opens it in the remote editor with change marks: added lines get a **green line
|
|
242
|
+
background**, deleted characters are drawn as an **inline struck-through chip** (a deleted character does
|
|
243
|
+
not exist in the new file, so it can only be an inserted widget), and the left gutter carries a change bar.
|
|
244
|
+
- **Jumping between change points**: the floating `n/N ↑↓` control at the editor's top-right
|
|
245
|
+
(`Alt+Shift+Cmd/Ctrl+↓/↑`) cycles through them; typing in the editor invalidates the marks and says
|
|
246
|
+
"position may be out of date".
|
|
247
|
+
- **Undo this change**: hover a changed line and click → remote `git apply -R`. When the context no longer
|
|
248
|
+
matches, the far end refuses outright (not one byte of the file changes); a tab with unsaved edits is
|
|
249
|
+
refused first, so a remote patch can never overwrite your buffer; on success the caret stays where it was
|
|
250
|
+
and the marks are re-read (they do not simply vanish because something was undone).
|
|
251
|
+
- **Follow the session's workspace**: the sidebar, the editor's dropdown and the file tree all follow the
|
|
252
|
+
workspace the **current session** lives in; after a manual switch they are not pulled back within that
|
|
253
|
+
session, and a new session follows again. The capability lives in the store, so it works even when **the
|
|
254
|
+
sidebar was never opened**; clicking a changed file is "show me this result", not a manual switch.
|
|
255
|
+
- **Repository directory**: the one the user picked under Source control → repository directory always wins;
|
|
256
|
+
when the session root is not a repository, a single candidate one level below is adopted automatically,
|
|
257
|
+
while several candidates produce an explicit hint to set it in Source control.
|
|
258
|
+
|
|
259
|
+
**Known limits (stated honestly)**:
|
|
260
|
+
|
|
261
|
+
- The baseline is the **session start**, not HEAD: untracked files that already existed before the session
|
|
262
|
+
began are not counted as this session's work.
|
|
263
|
+
- Tracked files refresh when a **turn ends** (the panel polls every 3 seconds as a backstop, so the numbers
|
|
264
|
+
usually catch up within a second or two); **new files** created in a shell are found by a **slow scan**
|
|
265
|
+
whose single-run budget is up to **3 minutes**, re-run at a self-tuning interval (at least 5 seconds,
|
|
266
|
+
naturally longer on a huge checkout). `⟳` forces an immediate rescan.
|
|
267
|
+
- A non-git workspace, or one holding several repositories with no repository directory set, shows the exact
|
|
268
|
+
reason (`no git repository at the workspace root, and N candidates below it — set the repository directory
|
|
269
|
+
in Source Control`) rather than an empty list.
|
|
270
|
+
- Binary and oversized files are marked "binary / too large" on the row and get no character-level diff.
|
|
271
|
+
|
|
272
|
+
## P2 transparent remote execution (Windows native)
|
|
273
|
+
|
|
274
|
+
Single-Host mixed routing: native tools dispatch by which world the session cwd lands in — remote aliases go
|
|
275
|
+
over helper RPC (fs) / ssh2 (processes), local stays untouched; both share one Host and one GUI.
|
|
276
|
+
|
|
277
|
+
- Alias dirs: `$DSH_HOME/remote-ssh/aliases/<id>` (empty shells, routing keys only), auto-registered into the native workspace list as `<server> > <base>`
|
|
278
|
+
- Routing switch (dropdown at the top of Settings → Remote hosts; underneath it is profile patch `- id: remote-vs`, `config.routingMode`, volatile, no restart):
|
|
279
|
+
`off` (default, local passthrough) / `shadow` (logs, still local) / `enforce` (real routing)
|
|
280
|
+
- New workspace: the native "Add workspace…" entry is adopted (local/remote dual tab; remote leg = host→push→bind wizard)
|
|
281
|
+
- Presets: a `Remote` preset (bash toolset); under enforce, new remote sessions switch to it automatically (default preset only, never overrides an explicit choice)
|
|
282
|
+
- Ground truths (measured, not assumed):
|
|
283
|
+
- Remote `grep/glob` call remote `rg` through routing (both measured hosts ship rg 13; no remote rg means no hits)
|
|
284
|
+
- Whole reads >8MiB report `FS_TOO_LARGE` (official helper ceiling) — use streaming/paged reads; **writes are uncapped** (a 46MiB whole write passed live)
|
|
285
|
+
- Oversized process output lands in a Host temp spill file (`readFrom` returns tail + path, full text lives in the file — same as stock)
|
|
286
|
+
- After unbinding, new calls are explicitly refused (fail-closed tombstones), history stays readable
|
|
287
|
+
- Disconnects: in-flight calls are not replayed; new calls reconnect automatically
|
|
288
|
+
- Verify: `npx tsc -p tsconfig.scratch.json && node .spike/build/.spike/p2-router.js`
|
|
289
|
+
(real remote + real Cordis + real router classes, 61/61; P0 routing and GUI need a Host restart + smoke pass)
|
|
290
|
+
- Details: `docs/P2-PLAN.md` (§8 acceptance matrix with per-item status)
|
|
291
|
+
|
|
292
|
+
## Self-built helper (P1)
|
|
293
|
+
|
|
294
|
+
Upstream never published helper binaries (all Release assets are empty), so this plugin builds its own:
|
|
295
|
+
|
|
296
|
+
- Build machine: WSL2 Ubuntu-22.04 (glibc 2.35, matching the remote targets), Node v24.21.0 + pnpm 11.28.5
|
|
297
|
+
- Sources: the `dsh-v0.2.1-alpha.2` tag (the **build baseline**: what must match is the wire protocol version, not the Host version — it is still 2, so a Host upgrade to alpha.4 or alpha.5 does not require a rebuild)
|
|
298
|
+
- Command: stock script `pnpm exec tsx scripts/build-exe-for-ssh-helper.ts --target=node24-linux-x64`
|
|
299
|
+
- Only deviation: no docker for the manylinux 2.28 rebuild, so a `DSH_GLIBC_CEILING`
|
|
300
|
+
env override was added (default stays 2.28); this build targets 2.35 (see `glibc-ceiling.patch` in the cache dir)
|
|
301
|
+
- Checks: stock `verify-ssh-helper-artifact.ts --sandbox=required`, all 8 green
|
|
302
|
+
- Cache: `$DSH_HOME/dsh-remote-helpers/` (`*.tar.gz` + `.sha256` + `.helper.json`
|
|
303
|
+
with binarySha256, feeding P2's helperHash)
|
|
304
|
+
- Full rebuild procedure for upgrades: the local-only `docs/MAINTENANCE.md` §4 (gitignored, not distributed)
|
|
305
|
+
|
|
306
|
+
## Security model
|
|
307
|
+
|
|
308
|
+
- All `/api/dsh-remote/*` routes are loopback + same-origin only.
|
|
309
|
+
- Passwords / key passphrases are stored in plaintext in `dsh-remote.json` (0600) — same trust model as the existing SSH plugin.
|
|
310
|
+
- Pushed artifacts are sha256-verified both ways (tarball in, binary out); the helper install dir is separate from workspaces.
|
|
311
|
+
- Remote sessions may read explicit local absolute paths (the tool spill-file contract requires it); acceptable under the same model trust — see `docs/P2-PLAN.md` §4 note.
|
|
312
|
+
|
|
313
|
+
## Development
|
|
314
|
+
|
|
315
|
+
```sh
|
|
316
|
+
pnpm install
|
|
317
|
+
pnpm run typecheck
|
|
318
|
+
pnpm run build
|
|
319
|
+
dsh plugin --profile web add link:<absolute path of this directory>
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**How a change goes live: `pnpm run build`, then restart `dsh web` — for the host half and the client half alike.**
|
|
323
|
+
**`lib/` is committed.** The package has no `prepare` script (pnpm 12 refuses to run one for a git
|
|
324
|
+
dependency), so a `dsh plugin add github:…` user receives the repository as it is: **after editing
|
|
325
|
+
source you must `npm run build` and commit `lib/` with it.** The first step of `pnpm check`
|
|
326
|
+
(`scripts/verify-lib.mjs`) fails when `lib/` is untracked or throws while loading.
|
|
327
|
+
The Host reads plugin bundles into memory at startup (`initialBundleSnapshot`: it stats mtime/ctime/size into a
|
|
328
|
+
revision, then reads the bytes; the response carries `Cache-Control: public, max-age=31536000, immutable`), so a
|
|
329
|
+
**client-only** change (`src/client/**`) behaves exactly the same way: a browser refresh — `Ctrl+Shift+R`
|
|
330
|
+
included — only re-fetches the bytes captured at startup, under the same `rev`. How to tell:
|
|
331
|
+
|
|
332
|
+
1. Each build injects a stamp (`MMDD-HHmm`) into `lib/client.js`, shown at the right of the editor info bar;
|
|
333
|
+
2. Restart `dsh web` → refresh the page: **the stamp must change** (if it does not, you are still on the bytes
|
|
334
|
+
captured at startup);
|
|
335
|
+
3. For colour issues, read `tok N`: `0` means no grammar matched the extension AND the fallback colorer
|
|
336
|
+
did not take over (the console line next to it names the file).
|
|
337
|
+
|
|
338
|
+
> The one exception is DSH's own development watcher (`pnpm run dev:web`, via `ClientModuleRegistry.rebuilt`);
|
|
339
|
+
> there is no watcher on a plugin user's side and `dsh web --help` has no watch option, so "restart" is the rule.
|
|
340
|
+
|
|
341
|
+
Self-checks (no host, no DSH, a few seconds each):
|
|
342
|
+
|
|
343
|
+
```sh
|
|
344
|
+
node .spike/client-load-check.mjs # really evaluates the client modules: load-time errors tsc/bundler/other harnesses miss
|
|
345
|
+
node .spike/tool-schema-check.mjs # all 5 agent tools: run execute, then validate the result with the RUNTIME's own validator
|
|
346
|
+
node .spike/model-check.mjs # 55 checks: real git output → parsers, diff model, hunk patches, class↔CSS contract
|
|
347
|
+
node .spike/reveal-check.mjs # 5 checks: jump target, landing highlight, repeat jump in one editor
|
|
348
|
+
node .spike/fallback-check.mjs # 12 checks: the fallback colorer colours java/cpp/shell in a real DOM
|
|
349
|
+
node .spike/editor-dom-check.mjs # 12 checks: a real CodeMirror in jsdom emits colour spans for java/shell/cpp
|
|
350
|
+
node .spike/i18n-check.mjs # zh/en dictionary consistency: no missing key, no orphan, matching placeholders
|
|
351
|
+
node .spike/ref-check.mjs # 32 cases: assertRef accepts real revision suffixes, rejects every injection surface
|
|
352
|
+
node .spike/status-check.mjs # runs the generated git commands in a real shell to prove exit codes are no longer masked
|
|
353
|
+
node .spike/cache-check.mjs # incremental token cache vs whole-document tokenizing, 9 languages × 8 edit shapes
|
|
354
|
+
node .spike/layout-check.mjs # height chain + scrollbar contract: header rows stay pinned, the bar is wide enough to grab
|
|
355
|
+
node .spike/viewstate-check.mjs # scroll+caret round trip across a remount (stateful scrollTop: tells "restored" from "reset")
|
|
356
|
+
node .spike/tabswitch-check.mjs # real React drives three wirings: both ref-reading ones must fail, reporting must pass
|
|
357
|
+
node .spike/letter-check.mjs # status letters: untracked `?` no longer collides with conflict `U`, plus both render sites
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
`.spike/layout-fixture.html` (generated by `build-fixture.mjs`) is the fixture for
|
|
361
|
+
measuring layout in a browser: it mechanically inlines the **host's real slot CSS**
|
|
362
|
+
and the **shipped vsc.css**, so after a CSS change you re-run
|
|
363
|
+
`node .spike/build-fixture.mjs` and can measure the height chain, the scroll
|
|
364
|
+
container and the scrollbar width for real. Pure-CSS defects are invisible to
|
|
365
|
+
tsc, to the bundler and to jsdom — this fixture is the only place they reproduce.
|
|
366
|
+
|
|
367
|
+
`markdown-check.ts` has to be compiled first (it imports a `.tsx`, which Node cannot load):
|
|
368
|
+
`npx tsc -p tsconfig.scratch.json && node .spike/build/.spike/markdown-check.js`.
|
|
369
|
+
|
|
370
|
+
> `.spike/` is git-ignored, so these checks are local only and run in no CI. **Run them before quoting any
|
|
371
|
+
> number** rather than copying a figure out of any file — one `model-check` count drifted across the README,
|
|
372
|
+
> the local maintenance doc and the review reports as 44 / 48 / 55, while the harness actually prints 55/55
|
|
373
|
+
> (its source holds 57 `check(` call sites, 2 of them inside loops and reachable only on failure, which is
|
|
374
|
+
> exactly why "57 call sites" and "55 checks run" differ by 2).
|
|
375
|
+
|
|
376
|
+
The live-host regression is still `npx tsc -p tsconfig.scratch.json && node .spike/build/.spike/p2-router.js`.
|
|
377
|
+
Before touching anything for a new DSH release, read the local-only `docs/MAINTENANCE.md` (§2 coupling points, §3 checklist) — it is gitignored, so a clone will not have it. The mandatory steps after a host-half change are `pnpm run verify:lib` plus the load self-check.
|
|
378
|
+
|
|
379
|
+
## License
|
|
380
|
+
|
|
381
|
+
MIT © 2026 xiray-xia — see [LICENSE](./LICENSE).
|
|
382
|
+
|
|
383
|
+
The `vendor/` helper seed is built from DeepSeek Harness sources (MIT) at tag `dsh-v0.2.1-alpha.2`
|
|
384
|
+
with the stock build script; see "Self-built helper" above for the exact procedure and the single
|
|
385
|
+
documented deviation (`glibc-ceiling.patch` in the seed set).
|