@dsh-xhl/dsh-live-inspector 1.0.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.
- package/README.md +163 -0
- package/cordis.patch.yml +3 -0
- package/lib/client.js +3019 -0
- package/lib/index.js +274 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# @dsh-xhl/dsh-live-inspector
|
|
2
|
+
|
|
3
|
+
DeepSeek Harness (DSH) Web plugin that provides a lightweight, real-time **Git Tree & Live Changes Inspector** in the right sidebar (`sidebar.right` tab), with **turn-scoped and hunk-level undo**.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Single Tab Architecture (Zero RAM Bloat)**: Instead of opening heavy editor/viewer tabs for every accessed file, it keeps everything inside a single, high-performance **Git Tree** tab (`~10 KB` memory footprint).
|
|
8
|
+
- **Live File Status Badges**:
|
|
9
|
+
- `[M]` (Amber): Modified files (`edit`, `str_replace_editor`).
|
|
10
|
+
- `[A]` (Emerald): Created / added files (`write`, `write_file`).
|
|
11
|
+
- `[R]` (Sky Blue): Read / inspected files (`read`).
|
|
12
|
+
- `[D]` (Red): Deleted in this session by an undo.
|
|
13
|
+
- **Real-Time Active Indicator**: Displays `⚡ Agent active on: <path>` with a pulsing live status on the exact file currently being touched.
|
|
14
|
+
- **Interactive Controls**:
|
|
15
|
+
- Filter by `All`, `Changes (M/A)`, or `Reads (R)`.
|
|
16
|
+
- Instant text filter search.
|
|
17
|
+
- Optional `View` button to open a single file preview only when explicitly desired.
|
|
18
|
+
- `Clear` button to reset the session file list.
|
|
19
|
+
|
|
20
|
+
## Undo
|
|
21
|
+
|
|
22
|
+
Undo is scoped to a **turn**. There is no file-level or session-level undo: undoing turn N
|
|
23
|
+
restores the state each file entered turn N with, so earlier turns always survive.
|
|
24
|
+
|
|
25
|
+
### Only the newest turn is undoable
|
|
26
|
+
|
|
27
|
+
**Undo controls appear only while the newest turn is selected.** On any older turn they are
|
|
28
|
+
absent — not greyed out — because an older turn has already been reviewed, and a visible dead
|
|
29
|
+
button would imply an action the panel is going to refuse.
|
|
30
|
+
|
|
31
|
+
Today's turn is the default, so the panel opens ready to undo what the agent just did.
|
|
32
|
+
|
|
33
|
+
The selected turn scopes the **whole panel**:
|
|
34
|
+
|
|
35
|
+
- the **file list** shows only the files that turn changed (`All (n)` counts that scope),
|
|
36
|
+
- the **diff inspector** follows, re-selecting a file when the turn changes,
|
|
37
|
+
- the **`⟲ Undo Turn N (n)`** button undoes that turn across every file it changed.
|
|
38
|
+
|
|
39
|
+
`All turns` is available in the selector for reading history across the whole session; it is a
|
|
40
|
+
view rather than an undo scope, so no undo controls appear while it is selected.
|
|
41
|
+
|
|
42
|
+
- Each file row shows its turns (`Turn 3`, or `Turn 1–4`) and carries `↶ Undo Turn N`.
|
|
43
|
+
- A file's detail header carries `↶ Undo change`, which reverts just the selected hunk.
|
|
44
|
+
- Older turns' change counts are labelled `reviewed`, and hunks outside the selected turn are
|
|
45
|
+
dimmed and not individually revertible.
|
|
46
|
+
|
|
47
|
+
### The turn snapshot
|
|
48
|
+
|
|
49
|
+
A file's text is captured immediately before that turn's first mutation of it, so the snapshot
|
|
50
|
+
is exactly what the file inherited — including every earlier turn's work. Writing it back is
|
|
51
|
+
what makes a turn-scoped undo unable to reach past its own turn. A `read` before the first
|
|
52
|
+
mutation counts as untouched too, which is what catches a write-after-read.
|
|
53
|
+
|
|
54
|
+
### Line / diff-level undo
|
|
55
|
+
|
|
56
|
+
Reverts **one contiguous change** — one tool call's hunk — leaving the file's other changes
|
|
57
|
+
in place.
|
|
58
|
+
|
|
59
|
+
- Every hunk the diff view shows is listed as a `#n` chip above the diff, each with its
|
|
60
|
+
own `↶ Undo change` button.
|
|
61
|
+
- A hunk is undone by replacing its recorded post-change text with its recorded original
|
|
62
|
+
text, so unrelated edits in the same file survive.
|
|
63
|
+
|
|
64
|
+
### Deleting and restoring
|
|
65
|
+
|
|
66
|
+
Deleting is the **only destructive action**, so it is the only button rendered as a solid red
|
|
67
|
+
fill. Every restorative action — hunk undo, turn undo, `⤴ Restore` — is amber, because it
|
|
68
|
+
writes content back rather than removing it.
|
|
69
|
+
|
|
70
|
+
Two kinds of deletion are covered:
|
|
71
|
+
|
|
72
|
+
- **A file the agent created and you delete** with `🗑 Delete file`. The row becomes a
|
|
73
|
+
strike-through `[D]` and `⤴ Restore` writes it back from the recorded content.
|
|
74
|
+
- **A file deleted by a shell command** (`rm`, `del`, …). The file tools have no delete verb,
|
|
75
|
+
so the panel cannot see the operation directly. After any shell call it re-probes the paths
|
|
76
|
+
it already knows about, and a path that vanished becomes a `[D]` row you can restore from
|
|
77
|
+
the newest recorded content — or from the turn snapshot if the file was only read.
|
|
78
|
+
|
|
79
|
+
A deleted file with no recorded content cannot be restored, and its `⤴ Restore` says so rather
|
|
80
|
+
than failing on click.
|
|
81
|
+
|
|
82
|
+
### Safety rules
|
|
83
|
+
|
|
84
|
+
Undo refuses rather than guesses:
|
|
85
|
+
|
|
86
|
+
- **Drift**: if the file changed since the edit, the recorded text no longer matches and the
|
|
87
|
+
undo is refused.
|
|
88
|
+
- **Ambiguity**: if the recorded text now occurs more than once, the undo is refused.
|
|
89
|
+
- **No artifact**: a change with no exact recorded before/after pair (replayed history, or a
|
|
90
|
+
tool other than `edit`/`write`) is never offered as individually revertible. The panel says
|
|
91
|
+
so instead of showing a button that would corrupt the file.
|
|
92
|
+
- **Older turn**: undo is not offered at all outside the newest turn.
|
|
93
|
+
- **Mid-write**: a file the agent is currently writing is not undoable until the step ends.
|
|
94
|
+
- **Oversized**: files above 512 KB get no text snapshot. They are still recognized by digest,
|
|
95
|
+
but a restore is refused.
|
|
96
|
+
- **Roots**: paths that resolve to a filesystem root, a drive root, or a UNC share root are
|
|
97
|
+
rejected outright.
|
|
98
|
+
|
|
99
|
+
Destructive actions need a second click (the button switches to a `⚠ Confirm …` state).
|
|
100
|
+
|
|
101
|
+
## Architecture
|
|
102
|
+
|
|
103
|
+
- `lib/index.js` — Host half. Owns real filesystem access and publishes a **Typert Remote
|
|
104
|
+
namespace** named `inspectorFs` with four methods: `readText`, `writeText`, `deleteFile`,
|
|
105
|
+
and `statPath` (the last detects deletions performed outside the file tools). Paths resolve
|
|
106
|
+
through `ctx.fs.processPathFromHostPath` when the backend provides a mapping, and by joining
|
|
107
|
+
the session working directory otherwise.
|
|
108
|
+
- `lib/client.js` — Browser half. Records file activity, diff artifacts, and per-turn
|
|
109
|
+
snapshots from session events, then renders the panel and undo controls.
|
|
110
|
+
|
|
111
|
+
### Why a Remote namespace, not `host.call`
|
|
112
|
+
|
|
113
|
+
`host.call(method, args)` is an API for **dynamic packages** — the ones an agent authors as a
|
|
114
|
+
code string. There, `host` is injected as a closure parameter by the runner.
|
|
115
|
+
|
|
116
|
+
An installed bundle's Client module runs under `window.__ModuleLoader__`, which resolves only a
|
|
117
|
+
fixed seed list (`react`, `react-dom`, `@deepseek-ai/cordis`, `@deepseek-ai/dsh-client-store`,
|
|
118
|
+
the `dsh-client-ui-*` primitives) and traps `require` itself. **`host` does not exist there**, so
|
|
119
|
+
`host.call` throws `host is not defined`. A bundle reaches its Host half through a Typert Remote
|
|
120
|
+
namespace consumed as `ctx.inject(['remote.<namespace>'])`.
|
|
121
|
+
|
|
122
|
+
This package has no build step, so `lib/index.js` writes the protocol's Remote-method descriptor
|
|
123
|
+
onto the service prototype by hand instead of using the build-time `@Remote` decorator. The
|
|
124
|
+
Gateway's source-mode (`src-json`) fallback accepts it: parameter names come from the function
|
|
125
|
+
signature and arguments travel as plain JSON, so no generated schema is required.
|
|
126
|
+
|
|
127
|
+
Method failures **reject** across the boundary. The Browser half converts a rejection into the
|
|
128
|
+
`{ ok: false, error }` shape it reasons about, so a refused path surfaces as one toast rather
|
|
129
|
+
than an unhandled throw.
|
|
130
|
+
|
|
131
|
+
Per-turn snapshots are keyed by file and turn. A snapshot is read immediately before that
|
|
132
|
+
turn's first mutation of the file, so it captures exactly what the file inherited — including
|
|
133
|
+
every earlier turn's work. That is what makes a turn-scoped undo unable to reach past its own
|
|
134
|
+
turn.
|
|
135
|
+
|
|
136
|
+
The pre-edit baseline read is authorized by this plugin's own `approval/request` listener,
|
|
137
|
+
which allows `fs-read` **only**; every other request is left untouched for the normal
|
|
138
|
+
approval chain.
|
|
139
|
+
|
|
140
|
+
## Installation
|
|
141
|
+
|
|
142
|
+
Published to npm as `@dsh-xhl/dsh-live-inspector`:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
dsh plugin --profile web add @dsh-xhl/dsh-live-inspector
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
From a checkout, `dsh plugin --profile web add <path-to-this-directory>` installs it as a
|
|
149
|
+
local link, which is what the `test` profile does.
|
|
150
|
+
|
|
151
|
+
### Manifest notes
|
|
152
|
+
|
|
153
|
+
- `dsh.bundle.patch` points at `cordis.patch.yml`, whose row inserts the plugin under the
|
|
154
|
+
id `dsh-live-inspector` with `name: '@dsh-xhl/dsh-live-inspector'`.
|
|
155
|
+
- `dsh.client` declares `platform: web` and the two sidebar Client packages this UI orders
|
|
156
|
+
itself against.
|
|
157
|
+
- `lib/client.js` registers into `window.__ModuleLoader__` under an `id` that **must equal the
|
|
158
|
+
package name**. The loader derives the expected module id from the manifest and reports
|
|
159
|
+
`loaded without registering "<id>"` when the two disagree, so renaming the package without
|
|
160
|
+
updating that id silently drops the panel.
|
|
161
|
+
- `files` ships only `lib/` plus the patch and README. The former root-level `client.js` and
|
|
162
|
+
`index.js` copies were removed; `exports` has pointed at `lib/` for some time, and keeping
|
|
163
|
+
two near-identical copies on disk invited editing the wrong one.
|
package/cordis.patch.yml
ADDED