dsh-home-hosted 0.2.1 → 0.3.1
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 +107 -14
- package/docs/DESIGN.md +36 -0
- package/lib/client.js +89 -6
- package/lib/index.js +785 -183
- package/lib/index.js.map +4 -4
- package/lib/types/home-hosted/dsh-entry.d.ts +65 -2
- package/lib/types/home-hosted/entries.d.ts +18 -4
- package/lib/types/home-hosted/launcher.d.ts +48 -0
- package/lib/types/home-hosted/panel.d.ts +12 -0
- package/lib/types/home-hosted/resolve.d.ts +3 -1
- package/lib/types/home-hosted/token.d.ts +21 -0
- package/lib/types/home-hosted/web-notice.d.ts +18 -0
- package/lib/types/service.d.ts +33 -0
- package/lib/types/shared/contracts.d.ts +33 -2
- package/package.json +18 -4
package/README.md
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# 📌 dsh-home-hosted
|
|
4
|
+
|
|
5
|
+
**Your whole home stack, up after every reboot — your `dsh` web server included.**
|
|
6
|
+
|
|
7
|
+
A [DeepSeek Harness](https://github.com/deepseek-ai/dsh) plugin for
|
|
8
|
+
[home-hosted](https://github.com/NamesMT/home-hosted), the panel that supervises your services.
|
|
9
|
+
Declare the servers once; this page manages the panel, the boot entry and the entries.
|
|
10
|
+
<sub>Nothing is installed or started until you say so.</sub>
|
|
4
11
|
|
|
5
12
|
[](https://www.npmjs.com/package/dsh-home-hosted)
|
|
6
13
|
[](https://github.com/NamesMT/dsh-home-hosted/actions/workflows/ci.yml)
|
|
@@ -8,24 +15,72 @@
|
|
|
8
15
|
[](https://nodejs.org)
|
|
9
16
|
[](https://github.com/deepseek-ai/dsh)
|
|
10
17
|
|
|
11
|
-
|
|
18
|
+
[🚀 Quick start](#-quick-start) · [🎛️ What the plugin does](#-what-the-plugin-does) · [🤖 Agent tools](#-agent-tools) · [🧩 Depth](#-depth)
|
|
19
|
+
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<div align="center">
|
|
23
|
+
|
|
24
|
+

|
|
25
|
+
|
|
26
|
+
<sub>The Detailed style, then the same sections in Compact — both switchable from the page header.</sub>
|
|
27
|
+
|
|
28
|
+
</div>
|
|
12
29
|
|
|
13
|
-
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 🚀 Quick start
|
|
14
33
|
|
|
15
34
|
```sh
|
|
16
35
|
dsh plugin --profile web add dsh-home-hosted
|
|
17
36
|
```
|
|
18
37
|
|
|
19
|
-
|
|
38
|
+
Open **Settings → Home Hosted**, turn on **Manage dsh**, and enable autostart.
|
|
39
|
+
<sub>Reboot once; `dsh` comes back without you touching anything.</sub>
|
|
40
|
+
|
|
41
|
+
## 🤔 Why
|
|
42
|
+
|
|
43
|
+
A panel restart shouldn't kill a prompt mid-flight, and a reboot shouldn't cost you an SSH session.
|
|
44
|
+
|
|
45
|
+
```text
|
|
46
|
+
reboot
|
|
47
|
+
│
|
|
48
|
+
▼
|
|
49
|
+
OS boot entry ──▶ home-hosted panel ──▶ ┌─────────────┐
|
|
50
|
+
systemd · │ │ dsh web ✓ │
|
|
51
|
+
launchd · │ └─────────────┘
|
|
52
|
+
XDG · Run key │
|
|
53
|
+
├────────▶ ┌─────────────┐
|
|
54
|
+
│ │ docker │
|
|
55
|
+
│ │ compose │
|
|
56
|
+
│ └─────────────┘
|
|
57
|
+
│
|
|
58
|
+
└────────▶ ┌─────────────┐
|
|
59
|
+
│ postgres │
|
|
60
|
+
│ :5432 │
|
|
61
|
+
└─────────────┘
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**`dsh` is one entry in the panel, not the whole product.** The panel is the supervisor: it starts
|
|
65
|
+
each entry, watches it, restarts what dies and reclaims its port. Add your compose stack, your
|
|
66
|
+
database, your bot — manage them from the page, or hand the entry ids to an agent.
|
|
67
|
+
|
|
68
|
+
A plugin cannot run at boot, so this one writes the OS entry while dsh runs and the OS takes over.
|
|
69
|
+
One managed entry (`dsh`) is kept alive under home-hosted's nanny, so a panel restart leaves it
|
|
70
|
+
running.
|
|
71
|
+
|
|
72
|
+
## 🎛️ What the plugin does
|
|
20
73
|
|
|
21
74
|
| | |
|
|
22
75
|
|---|---|
|
|
23
|
-
| 🚀 **Boot autostart** | Installs, verifies and removes the OS entry
|
|
76
|
+
| 🚀 **Boot autostart** | Installs, verifies and removes the OS entry. Opt in per machine. |
|
|
77
|
+
| 🛡️ **Survives a panel restart** | The managed entry runs under home-hosted's nanny, so restarting the panel leaves it running. |
|
|
24
78
|
| 📦 **Ships its own panel** | The pinned `home-hosted` is a dependency, so boot runs the version this plugin is tested against. |
|
|
25
79
|
| 🖥️ **Server control** | Add, edit, start, stop and restart entries — written through the panel's API, so nothing restarts behind your back. |
|
|
26
|
-
|
|
|
80
|
+
| 🧭 **Token warnings that mean something** | A missing or refused API token is called out on the page, with one click to mint a working one. |
|
|
81
|
+
| 🤖 **Agent tools** | On by default, session permissions still gate every write. |
|
|
27
82
|
|
|
28
|
-
## Agent tools
|
|
83
|
+
## 🤖 Agent tools
|
|
29
84
|
|
|
30
85
|
| Tool | Does |
|
|
31
86
|
|---|---|
|
|
@@ -37,6 +92,9 @@ Then open **Settings → Home Hosted**. Nothing is installed or started until yo
|
|
|
37
92
|
| `home_hosted_ui_manage` | `status` · `update` · `revert` · `switch` the panel's own UI |
|
|
38
93
|
|
|
39
94
|
A tool that changes something asks for approval **only** when the session is not already Full access.
|
|
95
|
+
<sub>A refused token is re-enrolled on the spot and the tool retries — see **Auto reclaim** below.</sub>
|
|
96
|
+
|
|
97
|
+
## 🧩 Depth
|
|
40
98
|
|
|
41
99
|
<details>
|
|
42
100
|
<summary><b>Boot autostart, per platform</b></summary>
|
|
@@ -47,26 +105,55 @@ A tool that changes something asks for approval **only** when the session is not
|
|
|
47
105
|
| macOS | `launchd-agent`, `launchd-daemon` | login, or boot with the daemon (one-time `sudo`) |
|
|
48
106
|
| Windows | Run key, Task Scheduler | login |
|
|
49
107
|
|
|
50
|
-
|
|
108
|
+
When the process cannot elevate, the page prints the exact commands instead — including the
|
|
109
|
+
`launchd-daemon` that starts a Mac **before** login.
|
|
51
110
|
|
|
52
111
|
</details>
|
|
53
112
|
|
|
54
113
|
<details>
|
|
55
114
|
<summary><b>Which home-hosted runs</b></summary>
|
|
56
115
|
|
|
57
|
-
The pinned dependency by default
|
|
116
|
+
The pinned dependency by default; the page can switch to a global install, or install the pinned
|
|
117
|
+
range globally for you. Boot entries run a small stable launcher the plugin writes, so a
|
|
118
|
+
`node_modules` path that moves never breaks boot.
|
|
119
|
+
|
|
120
|
+
</details>
|
|
121
|
+
|
|
122
|
+
<details>
|
|
123
|
+
<summary><b>dsh from a local clone</b></summary>
|
|
124
|
+
|
|
125
|
+
A `dsh` you cloned and built yourself is supported: the managed entry starts a stable launcher that
|
|
126
|
+
re-finds your build at boot, so a rebuild, a moved checkout or a fresh profile does not strand it.
|
|
127
|
+
<sub>Falls back to whatever `dsh` is on PATH, and fails with the reason when it finds neither.</sub>
|
|
58
128
|
|
|
59
129
|
</details>
|
|
60
130
|
|
|
61
131
|
<details>
|
|
62
132
|
<summary><b>Managed entry and port policy</b></summary>
|
|
63
133
|
|
|
64
|
-
One entry (`dsh`)
|
|
134
|
+
One entry (`dsh`), one toggle: the panel keeps it alive, restarts it and reclaims its port. A
|
|
135
|
+
detached restart is recognised on macOS and Linux (`follow`); Windows cannot prove identity through
|
|
136
|
+
a `.cmd` shim, so it uses `onPortConflict: kill` — needing home-hosted 0.6.0+, and the plugin
|
|
137
|
+
refuses that write against an older panel rather than producing a config it cannot parse.
|
|
65
138
|
|
|
66
139
|
</details>
|
|
67
140
|
|
|
68
141
|
<details>
|
|
69
|
-
<summary><b>
|
|
142
|
+
<summary><b>API tokens and Auto reclaim</b></summary>
|
|
143
|
+
|
|
144
|
+
The plugin needs its own home-hosted API token to read and write the panel. home-hosted keeps only
|
|
145
|
+
the token's hash, so a token it did not mint can never be recovered — `<stateDir>/panel-token` is
|
|
146
|
+
`0600`, never rendered or logged, and **Regenerate** clears the old hash and enrols a new one.
|
|
147
|
+
|
|
148
|
+
A token that stops working (someone ran `home-hosted set-token`, or cleared it) shows on the page as
|
|
149
|
+
a warning with that button. **Auto reclaim**, on by default, does the same thing automatically when a
|
|
150
|
+
panel call is refused: the tool re-enrols and retries once instead of failing. Turn it off to be
|
|
151
|
+
asked instead of having a working token silently replaced.
|
|
152
|
+
|
|
153
|
+
</details>
|
|
154
|
+
|
|
155
|
+
<details>
|
|
156
|
+
<summary><b>Operator config</b></summary>
|
|
70
157
|
|
|
71
158
|
The Cordis row config is for operator overrides only:
|
|
72
159
|
|
|
@@ -79,7 +166,7 @@ The Cordis row config is for operator overrides only:
|
|
|
79
166
|
defaultEntryId: dsh
|
|
80
167
|
```
|
|
81
168
|
|
|
82
|
-
Everything a person toggles lives in `<stateDir>/settings.json`.
|
|
169
|
+
Everything a person toggles lives in `<stateDir>/settings.json`.
|
|
83
170
|
|
|
84
171
|
</details>
|
|
85
172
|
|
|
@@ -91,8 +178,14 @@ pnpm install
|
|
|
91
178
|
pnpm typecheck && pnpm test && pnpm build
|
|
92
179
|
```
|
|
93
180
|
|
|
94
|
-
Node 24+, pnpm
|
|
181
|
+
Node 24+, pnpm. The same commands CI runs.
|
|
95
182
|
|
|
96
183
|
</details>
|
|
97
184
|
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
<div align="center">
|
|
188
|
+
|
|
98
189
|
MIT · [npm](https://www.npmjs.com/package/dsh-home-hosted) · [releases](https://github.com/NamesMT/dsh-home-hosted/releases) · [issues](https://github.com/NamesMT/dsh-home-hosted/issues) · built on [home-hosted](https://github.com/NamesMT/home-hosted)
|
|
190
|
+
|
|
191
|
+
</div>
|
package/docs/DESIGN.md
CHANGED
|
@@ -47,6 +47,26 @@ the rest from the platform default policy; removing one restores an entry this
|
|
|
47
47
|
plugin merely adopted, and deletes one it created. Removing an entry the panel
|
|
48
48
|
supervises stops that process — which may be the session asking for it.
|
|
49
49
|
|
|
50
|
+
## The sign-in page points at the log
|
|
51
|
+
|
|
52
|
+
dsh web's unauthenticated 401 is a plain-text line written by the in-box
|
|
53
|
+
connection plugin; it has no config and no event. The plugin wraps that one
|
|
54
|
+
internal writer (`connection.browserAuth.writeUnauthorized`) and appends where
|
|
55
|
+
the tokenised URL is — `<panel url>/logs?server=<entry>`. It rewrites **only** the
|
|
56
|
+
exact stock body, guards every assignment, and replaces the response's `end` for
|
|
57
|
+
the single call, so an authentication plugin's own page (or an exotic response)
|
|
58
|
+
is passed through untouched rather than fought over. `authNotice` turns it off;
|
|
59
|
+
`{url}` is read per 401.
|
|
60
|
+
|
|
61
|
+
## Persistence keeps dsh alive across a panel restart
|
|
62
|
+
|
|
63
|
+
A managed entry asks for `persistent: true`: home-hosted then runs it under its
|
|
64
|
+
nanny, which owns the child and outlives `stopAll()`/`dispose()` — so restarting
|
|
65
|
+
or stopping the *panel* no longer ends a prompt mid-flight. The key arrived in
|
|
66
|
+
home-hosted 0.6.3, so the write and the drift check both consult the panel's
|
|
67
|
+
version: an older panel gets no such key (it would only be dropped and warned),
|
|
68
|
+
while the stored intent keeps what the person asked for.
|
|
69
|
+
|
|
50
70
|
## Approvals follow the session's sandbox
|
|
51
71
|
|
|
52
72
|
A mutating agent tool asks the approval service only when the calling session is
|
|
@@ -144,6 +164,22 @@ one, passes it to `home-hosted set-token` through `HHOSTED_TOKEN` (the CLI print
|
|
|
144
164
|
nothing), and keeps it 0600 in its own state directory. It is never rendered,
|
|
145
165
|
logged, or embedded in a unit file.
|
|
146
166
|
|
|
167
|
+
The token state the page shows is measured, not assumed. When the panel answers,
|
|
168
|
+
the plugin proves its token with a non-mutating `listServers()`: a refusal is
|
|
169
|
+
`stale` and the write path drops to the config file, while a panel that never
|
|
170
|
+
answered leaves the state `unknown` — a timeout is not a refusal, and
|
|
171
|
+
`tokenVerified` records whether a proof actually happened.
|
|
172
|
+
|
|
173
|
+
A refused token, or a hash this plugin never had, is replaced by
|
|
174
|
+
`panel.reclaimToken`: it clears the old hash first (`set-token --clear`, without
|
|
175
|
+
which `set-token` refuses to overwrite one), mints and enrols a fresh token
|
|
176
|
+
through the same `HHOSTED_TOKEN` path, and proves it against the panel before
|
|
177
|
+
returning the refreshed status. Every token operation on one state directory is
|
|
178
|
+
serialised; two concurrent mints used to leave the stored plaintext and the
|
|
179
|
+
panel's hash disagreeing, and nothing could ever recover. If the CLI cannot run
|
|
180
|
+
nothing is written; if it fails after the clear, the stored token is removed,
|
|
181
|
+
because home-hosted no longer accepts it and a stored plaintext would only lie.
|
|
182
|
+
|
|
147
183
|
## Privilege
|
|
148
184
|
|
|
149
185
|
Boot scope needs privilege somewhere on every platform: a system unit, or
|