@monster-orange/den 0.4.3 → 0.4.5
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/CHANGELOG.md +348 -0
- package/README.md +12 -11
- package/docs/distributions.md +438 -91
- package/docs/reference.md +8 -3
- package/package.json +1 -1
- package/src/shared/agents/catalog-runtime.ts +178 -0
- package/src/shared/agents/catalog.ts +343 -0
- package/src/shared/agents/den-claude.ts +14 -18
- package/src/shared/agents/runtime-search.ts +142 -15
- package/src/shared/agents/runtime.ts +18 -1
- package/src/shared/agents/runtimes.ts +23 -1
- package/src/shared/auth/sudo-vault.ts +131 -11
- package/src/shared/cli-command.ts +186 -0
- package/src/shared/confirm-code.ts +34 -0
- package/src/shared/den-config.ts +64 -3
- package/src/shared/dev-metrics/emit.ts +31 -1
- package/src/shared/edge/config.ts +39 -0
- package/src/shared/edge/render.ts +92 -18
- package/src/shared/edge/runtime.ts +57 -1
- package/src/shared/edge/status.ts +134 -0
- package/src/shared/edge/types.ts +11 -1
- package/src/shared/environment-manager/health.ts +82 -8
- package/src/shared/environment-manager/workspaces.ts +54 -0
- package/src/shared/github-app.ts +94 -0
- package/src/shared/github-device.ts +286 -0
- package/src/shared/health/not-checked.ts +42 -0
- package/src/shared/health/rollup.ts +4 -0
- package/src/shared/process/spawn-streaming.ts +8 -3
- package/src/shared/secrets/onepassword.ts +27 -4
- package/src/shared/sessions/client.ts +5 -1
- package/src/shared/sessions/host.ts +29 -1
- package/src/shared/sessions/protocol.ts +6 -1
- package/src/shared/sessions/types.ts +6 -0
- package/src/shared/stacks/status.ts +24 -0
- package/src/shared/strategies/registry.ts +5 -0
- package/src/shared/types/index.ts +6 -0
- package/src/shared/web-owner.ts +91 -0
- package/src/shared/web-session.ts +135 -1
- package/src/ui/cli/agent-ops.ts +210 -19
- package/src/ui/cli/agents-command.ts +50 -21
- package/src/ui/cli/edge-command.ts +16 -3
- package/src/ui/cli/flag-parser.ts +1 -1
- package/src/ui/cli/health-display.ts +4 -1
- package/src/ui/cli/web-command.ts +512 -33
- package/src/ui/mcp/scope.ts +23 -3
- package/src/ui/mcp/server.ts +31 -7
- package/src/ui/mcp/tools.ts +327 -20
- package/src/ui/mcp/views.ts +450 -0
- package/src/ui/server/agent-routes.ts +118 -28
- package/src/ui/server/device-flows.ts +274 -0
- package/src/ui/server/edge-exposure.ts +16 -4
- package/src/ui/server/github-routes.ts +321 -0
- package/src/ui/server/http-errors.ts +10 -0
- package/src/ui/server/index.ts +540 -33
- package/src/ui/server/login-waits.ts +203 -0
- package/src/ui/server/pair-requests.ts +237 -0
- package/src/ui/server/pair-routes.ts +196 -0
- package/src/ui/server/public/agent-start.js +46 -7
- package/src/ui/server/public/agents-page.js +232 -97
- package/src/ui/server/public/agents-section.js +3 -5
- package/src/ui/server/public/agents.html +12 -28
- package/src/ui/server/public/error-page.js +452 -40
- package/src/ui/server/public/error.html +59 -0
- package/src/ui/server/public/graph-model.js +2 -0
- package/src/ui/server/public/header.js +2 -3
- package/src/ui/server/public/health-rollup.js +20 -8
- package/src/ui/server/public/i18n/es.js +89 -21
- package/src/ui/server/public/i18n/ja.js +88 -21
- package/src/ui/server/public/index.html +17 -4
- package/src/ui/server/public/local-dev.html +44 -21
- package/src/ui/server/public/qr.js +317 -0
- package/src/ui/server/public/services.html +21 -5
- package/src/ui/server/public/settings-page.js +10 -2
- package/src/ui/server/public/settings-registry.js +617 -1
- package/src/ui/server/public/settings.html +90 -1
- package/src/ui/server/public/shell.js +151 -33
- package/src/ui/server/public/topbar.js +170 -206
- package/src/ui/server/public/workspace-picker.js +1 -1
- package/src/ui/server/readonly-routes.ts +29 -5
- package/src/ui/server/request-guard.ts +103 -36
- package/src/ui/server/route-classes.ts +116 -4
- package/src/ui/server/session-routes.ts +9 -1
- package/src/ui/server/sudo-store.ts +170 -6
- package/src/ui/server/ui-assets.ts +2 -0
- package/src/ui/server/workspace-routes.ts +10 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,354 @@ All notable changes to Den are documented here. Each release lists the
|
|
|
4
4
|
config and manifest changes a distribution must make to move to it under
|
|
5
5
|
**Migrations**.
|
|
6
6
|
|
|
7
|
+
## [0.4.5] - 2026-11-07
|
|
8
|
+
|
|
9
|
+
Sign in with GitHub as the owner, from your other devices; approve a
|
|
10
|
+
computer with a code; see that you're signed in (SEC-2a). Save the admin
|
|
11
|
+
password without editing YAML (LK-4). Dev-metrics stay on this machine
|
|
12
|
+
unless you opt in.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **Sign in with GitHub.** A Den has one owner, a GitHub account:
|
|
17
|
+
`<cli> web owner link` runs GitHub's device flow in the terminal (a code
|
|
18
|
+
to enter at https://github.com/login/device) and stores only
|
|
19
|
+
`github:<id>` and the login (`web-owner.json`, 0600), never the token.
|
|
20
|
+
`web owner show` and `web owner unlink` too. The signed-out page then
|
|
21
|
+
has **Sign in with GitHub**: the server runs the flow, and the owner's
|
|
22
|
+
approval sets the same never-expiring session a one-time link does;
|
|
23
|
+
any other account sees "@<login> isn't allowed on this Den". At most
|
|
24
|
+
three sign-ins wait at once. Before an owner is linked the page says to
|
|
25
|
+
run `web owner link`. See docs/distributions.md, "Signing in with GitHub".
|
|
26
|
+
- **Settings › Access.** The owner (link and unlink: shell-grade, so a
|
|
27
|
+
signed-in browser and never an agent's token), the
|
|
28
|
+
linked devices (browser, how it signed in, the client address the edge
|
|
29
|
+
vouched for, first and last seen) each with **Revoke**, which ends that
|
|
30
|
+
session at once, and **Pair a device**: a one-time link and its QR code
|
|
31
|
+
for an edge hostname you pick.
|
|
32
|
+
- **`web.github_client_id`**: the GitHub App's client id (public). The
|
|
33
|
+
default is Den's own App, "Monster Orange Den" (`Iv23liKL12gnN6WvRV5b`);
|
|
34
|
+
a distribution sets its own App's id here, and an empty value turns Sign
|
|
35
|
+
in with GitHub off (the page then offers only the code and the link).
|
|
36
|
+
The App needs "Enable Device Flow" ticked and "Expire user authorization
|
|
37
|
+
tokens" off (docs/distributions.md, "The GitHub App").
|
|
38
|
+
- **Expiring GitHub tokens are noticed.** If the App hands out expiring
|
|
39
|
+
tokens (`expires_in` or `refresh_token` in the answer), sign-in still
|
|
40
|
+
completes, and Den says once to switch off token expiry in the App's
|
|
41
|
+
settings (never with the token). `GET /api/web/owner` reports it as
|
|
42
|
+
`tokenExpiry`. Den never asks for `offline_access`.
|
|
43
|
+
- **Approve this computer with a code.** The signed-out page shows a
|
|
44
|
+
six-character code (`XXX-XXX`, about 30 bits; the page polls with its own
|
|
45
|
+
128-bit id) and the command that approves it on the machine Den runs on:
|
|
46
|
+
`<cli> web approve <code>` (any case, dash optional; `--list` shows what
|
|
47
|
+
waits). The page signs itself in when it's approved, onto the page it
|
|
48
|
+
asked for. From a machine with SSH access the page gives the one-liner
|
|
49
|
+
(`ssh <host> '<cli> web approve <code>'`) and, for a host name, an Open
|
|
50
|
+
Terminal link. Codes expire after 10 minutes, are single-use, at most
|
|
51
|
+
five wait at once (the oldest drops) and are kept in memory only;
|
|
52
|
+
approvals are rate-limited and audited with who approved. The device is
|
|
53
|
+
labelled "approved with a code".
|
|
54
|
+
- **Settings › Access › Waiting devices**: the computers waiting with a
|
|
55
|
+
code (browser and OS, address, when), each with **Approve** and **Deny**.
|
|
56
|
+
- **`web.machine_name`** (Settings › Access › Machine name, in user.yml):
|
|
57
|
+
the name the signed-out page gives this machine. Default: the short
|
|
58
|
+
hostname. It's never taken from the browser's Host; the ssh one-liner's
|
|
59
|
+
target is the Host the browser used (without the port).
|
|
60
|
+
- **The signed-out page says where to act.** "Sign in to <product>", which
|
|
61
|
+
machine Den runs on and why it asks (once per computer), Sign in with
|
|
62
|
+
GitHub (only with a client id), Approve this computer, and the one-time
|
|
63
|
+
link under "Other ways". Every command it prints names the machine it
|
|
64
|
+
runs on, has a copy button, and is `<cli>` when that's on PATH and this
|
|
65
|
+
distribution's, else the absolute path of its bin. English, Japanese and
|
|
66
|
+
Spanish.
|
|
67
|
+
- **See that you're signed in.** A chip at the foot of the rail says
|
|
68
|
+
"Local", "Signed in · this device" or "@<login>" and opens Settings ›
|
|
69
|
+
Access. The first page after a sign-in says "This device is signed in
|
|
70
|
+
to <product> on <machine>", once. Linked devices lists every session
|
|
71
|
+
(GitHub, a code, a one-time link) with how it signed in, when, from
|
|
72
|
+
where, last seen and Revoke, and marks the one you're using as "This
|
|
73
|
+
device".
|
|
74
|
+
- **Save the admin password without editing YAML (LK-4).** When a secrets
|
|
75
|
+
backend that keeps secrets (1Password, Doppler) is available and no
|
|
76
|
+
`secrets.refs.sudo` is set, the admin password prompt (shown when a
|
|
77
|
+
command is about to run sudo, LK-0) still offers "Save to <backend>".
|
|
78
|
+
Saving creates the item (1Password: "<Product> admin password" in your
|
|
79
|
+
default vault; Doppler: `<PREFIX>_LOCAL_ADMIN_PWD`) and writes
|
|
80
|
+
`secrets.refs.sudo` to your `config/local/user.yml`, so the password
|
|
81
|
+
survives restarts. A ref in the distribution's config still wins. The
|
|
82
|
+
vault status says so as `canSave`. A save error no longer carries the
|
|
83
|
+
backend's command line.
|
|
84
|
+
- **`edge.proxy_protocol.trusted_ips`** (empty by default): the front
|
|
85
|
+
proxies (a TCP passthrough in front of the edge) whose PROXY protocol
|
|
86
|
+
every entrypoint accepts, so Den sees the real client through them.
|
|
87
|
+
- **`web.edge_unattested: local | signin`** (default `signin`, read only
|
|
88
|
+
from `config/local/user.yml`): whether an edge client Den can't place
|
|
89
|
+
signs in. `local` keeps 0.4.4's (and 0.4.3's) behaviour.
|
|
90
|
+
|
|
91
|
+
### Changed
|
|
92
|
+
|
|
93
|
+
- **Dev-metrics are local unless you opt in.** Den sent its dev-metrics
|
|
94
|
+
whenever a DSN was present (in the env or a synced
|
|
95
|
+
`dev-metrics.config`). Sending now needs `metrics: { share: true }` in
|
|
96
|
+
`config/local/user.yml` (the boolean only); a DSN alone never sends. The
|
|
97
|
+
local `events.jsonl` is unchanged, and `CI=true` and
|
|
98
|
+
`DEV_METRICS_DISABLED=1` still stop sending.
|
|
99
|
+
- **`<cli> web login` waits.** It prints the link and an 8-character code,
|
|
100
|
+
and blocks until the link is used ("Signed in: <browser> (<address>) at
|
|
101
|
+
<time>") or expires ("Expired: nobody used the link", exit 1). The link
|
|
102
|
+
asks for the code before it signs in; a wrong code is refused (five burn
|
|
103
|
+
the link, and wrong codes are rate-limited) and the link stays
|
|
104
|
+
single-use. `--no-wait` prints the link alone, as before, and asks for no
|
|
105
|
+
code; with no server running it behaves like `--no-wait` and says so.
|
|
106
|
+
- **Edge visitors Den can't place sign in.** A client the edge reports
|
|
107
|
+
with no `X-Forwarded-For`, or with a Docker/VM gateway or bridge address
|
|
108
|
+
(what a TCP passthrough or Docker Desktop and OrbStack hand it), now sees
|
|
109
|
+
the signed-out page with Sign in with GitHub, unless
|
|
110
|
+
`web.edge_unattested: local`. Loopback and this machine's own addresses,
|
|
111
|
+
as the edge reports them, stay local, and localhost straight to the
|
|
112
|
+
server's port never signs in.
|
|
113
|
+
- **The client is the right-most `X-Forwarded-For` entry**, the one the
|
|
114
|
+
edge appended (0.4.4 and earlier read the left-most, which a client could
|
|
115
|
+
write).
|
|
116
|
+
- **The edge believes forwarded headers only from its own networks.** Den
|
|
117
|
+
renders `forwardedHeaders.trustedIPs` (loopback and the Docker ranges)
|
|
118
|
+
on every entrypoint, never `forwardedHeaders.insecure`. An `edge.args`
|
|
119
|
+
entry that sets `forwardedHeaders` or `proxyProtocol` is dropped with a
|
|
120
|
+
notice from `edge up`.
|
|
121
|
+
- **"Your edge is reachable from other machines"** now reads "A browser it
|
|
122
|
+
can't place as this one signs in first (Sign in with GitHub, or a
|
|
123
|
+
pairing link)." (with `web.edge_unattested: local`, that anyone it can't
|
|
124
|
+
place can use terminals).
|
|
125
|
+
- **`edge up` says when the running edge's command differs** from the one
|
|
126
|
+
it renders, and why, before recreating it.
|
|
127
|
+
|
|
128
|
+
### Fixed
|
|
129
|
+
|
|
130
|
+
- **The workspace commands run in is named: "CLI default".** Local Dev's
|
|
131
|
+
workspace list marked it with a bare accent dot and no label, and
|
|
132
|
+
picking a workspace in the top bar never moved the dot (by design: that
|
|
133
|
+
picks the view), so it looked broken. It now says "CLI default", with
|
|
134
|
+
a tooltip: CLI commands run there when you don't name a workspace, and
|
|
135
|
+
the top bar only changes what pages show. The top bar's Workspace menu
|
|
136
|
+
tags it "CLI default" (was "focus") and offers "Make <ws> the CLI
|
|
137
|
+
default"; the Workspaces pane's badge reads "active · CLI default" and
|
|
138
|
+
its button "Make CLI default". Nothing behaves differently.
|
|
139
|
+
|
|
140
|
+
- **A browser signs in on a plain-http LAN bind** (`DEN_BIND_HOST` with
|
|
141
|
+
`web.allow_plain_http_remote`). A `web login --origin http://<LAN IP>:<port>`
|
|
142
|
+
link got 403 `origin_rejected`: the browser sends a plain-http origin that
|
|
143
|
+
isn't localhost no `Sec-Fetch-*` headers, and a navigation sends no
|
|
144
|
+
Origin. The sign-in GETs (`/auth` and the GitHub sign-in poll), which
|
|
145
|
+
carry their own one-time credential, now pass with neither header. Either
|
|
146
|
+
header, when present, is still checked, and every other GET that runs
|
|
147
|
+
something still needs this server's page or a bearer.
|
|
148
|
+
- **`web up` with `DEN_BIND_HOST` on one address is ready when it serves.**
|
|
149
|
+
Its readiness probe (and `web sessions`/`web logout`'s calls) asked
|
|
150
|
+
127.0.0.1, so a server bound to a LAN IP or `::1` printed "did not become
|
|
151
|
+
ready" and exited non-zero. They ask the bound address now (loopback for
|
|
152
|
+
0.0.0.0 and `::`), with a Host the allowlist accepts and the CLI's
|
|
153
|
+
bearer: a LAN address answers an unsigned probe 401 `signin_required`.
|
|
154
|
+
`web up` names the bound address (`http://<LAN IP>:<port>`), not
|
|
155
|
+
localhost, which a LAN-only bind doesn't answer.
|
|
156
|
+
|
|
157
|
+
### Migrations
|
|
158
|
+
|
|
159
|
+
- **One edge recreate.** The edge's command changes (forwarded-header
|
|
160
|
+
trust), so the first `<cli> edge up` (or `stack up`) after upgrading
|
|
161
|
+
recreates it once, and says so: "<org>-edge's settings changed
|
|
162
|
+
(forwarded-header trust): recreating it once." Routes drop only while it
|
|
163
|
+
restarts.
|
|
164
|
+
- **Edge visitors now see Sign in with GitHub** where 0.4.4 let a client
|
|
165
|
+
it couldn't place in. Link an owner (`<cli> web owner link`), approve
|
|
166
|
+
the computer with its code (`<cli> web approve <code>`) or pair with
|
|
167
|
+
`<cli> web login --host <name>`;
|
|
168
|
+
or set `web.edge_unattested: local` in `config/local/user.yml` to keep
|
|
169
|
+
0.4.4's behaviour on that machine.
|
|
170
|
+
- **Scripts that run `<cli> web login` and read its output** should pass
|
|
171
|
+
`--no-wait`: it now waits for the link to be used.
|
|
172
|
+
- **Remove `forwardedHeaders.*` and `proxyProtocol.*` from `edge.args`.**
|
|
173
|
+
They are ignored now; use `edge.proxy_protocol.trusted_ips` for a front
|
|
174
|
+
proxy.
|
|
175
|
+
|
|
176
|
+
## [0.4.4] - 2026-11-07
|
|
177
|
+
|
|
178
|
+
The admin password is for sudo, and nothing else (LK-0). An agent's
|
|
179
|
+
terminal uses its pane's width, and Den knows Codex, Gemini and Grok as
|
|
180
|
+
well as Claude Code (AG-0.5). A scoped agent gets a whole, honest picture
|
|
181
|
+
through Den's MCP (MCP-2).
|
|
182
|
+
|
|
183
|
+
### Changed
|
|
184
|
+
|
|
185
|
+
- **Nothing asks for the admin password up front.** It was a lock: agent
|
|
186
|
+
start, stop and resume answered `423 locked` without it, and the pages
|
|
187
|
+
asked for it before agent actions, service actions and syncs. It is no
|
|
188
|
+
access check (that is sign-in, the Host/Origin/CSRF checks and agent
|
|
189
|
+
tokens), and Den now asks for it only when a command it runs is about to
|
|
190
|
+
run sudo. Agents, sessions, terminals and services whose commands run no
|
|
191
|
+
sudo never ask. The agent routes' `423 locked` is gone, and so is the
|
|
192
|
+
Start agent dialog's password field.
|
|
193
|
+
- **A run that needs sudo says so, and the page asks then.** Every service
|
|
194
|
+
command (up, down, restart, setup, sync) gets a `sudo` shim first on its
|
|
195
|
+
PATH. With the password held, it gives it to sudo, as before. Without
|
|
196
|
+
it, sudo that needs no password (NOPASSWD, a warm cache) runs as it
|
|
197
|
+
would; anything else never prompts: the run's events carry
|
|
198
|
+
`{ "type": "needs-sudo", "command": "sudo <command>", "step": "<service>" }`
|
|
199
|
+
(sudo's own "a password is required" counts too, without the command).
|
|
200
|
+
The page then says "`sudo <command>` needs your admin password", in the
|
|
201
|
+
top layer, and runs the action again after it. Before asking, it asks
|
|
202
|
+
the server (another tab or device may have set it) and, when one is
|
|
203
|
+
stored, the vault. Local Dev no longer loads it from the vault on page
|
|
204
|
+
load.
|
|
205
|
+
- **No Locked/Unlocked chip.** The top bar shows "🔑 Admin password
|
|
206
|
+
needed" only while a run waits for it. Settings › Command line › Admin
|
|
207
|
+
password says "Saved in 1Password", "Saved for this session" or "Not
|
|
208
|
+
saved", with Forget (`POST /api/sudo-password/forget`, which also clears
|
|
209
|
+
the vault's copy when this server saved or loaded it there).
|
|
210
|
+
`GET /api/sudo-password/status` adds `saved` and `vault`.
|
|
211
|
+
- **Scoped answers say so (MCP-2.2).** `status`, `services`,
|
|
212
|
+
`applications`, `graph`, `stacks` and `workspaces` on a scoped server
|
|
213
|
+
carry a `scope` block: the workspace shown and what is left out
|
|
214
|
+
(`hidden: { repos: 6 }`), pointing at `org_summary`. Their descriptions
|
|
215
|
+
say "This workspace only", and a pinned server registers them without
|
|
216
|
+
the `workspace` parameter (with grants it stays, as the scope's own or a
|
|
217
|
+
granted one). A scoped `graph` is the workspace's own repos and the edges
|
|
218
|
+
out of them; unscoped it is labelled `map: "org-wide"`. Before, `graph`
|
|
219
|
+
listed every repo while every other tool showed one workspace's.
|
|
220
|
+
- **Refusals say how to get more (MCP-2.3).** A call naming another
|
|
221
|
+
workspace says a person adds it to `agents.<id>.grants` in the org config
|
|
222
|
+
and restarts the agent; a refused log read says logs stay inside each
|
|
223
|
+
workspace and a grant does not widen them.
|
|
224
|
+
- **`status` (MCP tool) is an object:** `{ env, counts, services }`, each
|
|
225
|
+
service with a `state` and, when it has no plain verdict, `why`. It was
|
|
226
|
+
the bare `/api/health` array.
|
|
227
|
+
|
|
228
|
+
### Added
|
|
229
|
+
|
|
230
|
+
- **The runtime catalog (AG-0.5).** Den ships, as data, the vendor CLIs it
|
|
231
|
+
can find: Claude Code (`den-claude`), OpenAI's Codex CLI (`codex`),
|
|
232
|
+
Google's Gemini CLI (`gemini`) and xAI's Grok Build (`grok`), each with
|
|
233
|
+
its binary names, version command, install directories and launch
|
|
234
|
+
command, checked against the vendor's docs. Installed runtimes are
|
|
235
|
+
listed first, with their versions, in the Start agent dialog, Settings
|
|
236
|
+
and `agents doctor`. A runtime other than Claude Code runs its own
|
|
237
|
+
interactive CLI in the workspace's directory, tagged "Den tools:
|
|
238
|
+
coming"; nothing is offered as startable unless it can start here.
|
|
239
|
+
When a catalog runtime is still not found, Den asks your interactive
|
|
240
|
+
shell (`$SHELL -ilc`) once, and says so (`interactive-shell`).
|
|
241
|
+
- **Runtime paths per workspace.** `<ws>.agents.runtimes.<id>.bin` in
|
|
242
|
+
`config/local/workspaces.yml` wins over `agents.runtimes.<id>.bin` in
|
|
243
|
+
`config/local/user.yml`, which wins over what Den finds. **Settings ›
|
|
244
|
+
Agents › Runtimes** shows each runtime's status, version, path and its
|
|
245
|
+
source; **Set path…** runs the version command and refuses a path that
|
|
246
|
+
isn't that runtime, and a selector edits one workspace's paths.
|
|
247
|
+
`agents doctor -w <ws>` shows that workspace's.
|
|
248
|
+
- **The Start agent dialog is the approved width** (the mock's 600px of
|
|
249
|
+
content: 638px wide, never wider than the window less 16px a side).
|
|
250
|
+
- **`org_summary`, the org at a glance for every agent (MCP-2.1).** A new
|
|
251
|
+
read tool: every workspace and repo with status counts (running, partial,
|
|
252
|
+
stopped, inactive; up, down, redirect, not checked), the dependency graph
|
|
253
|
+
(names and edge kinds), the edge (up, TLS, whether HTTPS serves) and the
|
|
254
|
+
environments. Scoped, `yours` adds the agent's own workspace's services
|
|
255
|
+
in full, so "what are our servers?" is answered without `docker ps`. For
|
|
256
|
+
other workspaces it carries names and counts only: never logs, env
|
|
257
|
+
values, commands, URLs, paths or check output.
|
|
258
|
+
- **`edge_status.https` (MCP-2.5)**: the TLS strategy, who supplies the
|
|
259
|
+
cert (`den` or `distribution`) and whether HTTPS is serving, from a TLS
|
|
260
|
+
handshake on the edge's https port. `edge status` prints it too.
|
|
261
|
+
|
|
262
|
+
### Fixed
|
|
263
|
+
|
|
264
|
+
- **A service's first lines reach the page.** A started service writes to
|
|
265
|
+
its log, and Den tails that log back to the page. The tail started at the
|
|
266
|
+
log's end, so a command that wrote and exited before the tail opened the
|
|
267
|
+
file (on Linux, a refused sudo) showed nothing, and the page never asked
|
|
268
|
+
for the admin password. The tail now reads from the start of this run.
|
|
269
|
+
|
|
270
|
+
- **An agent's terminal uses the pane's width.** A session has one PTY
|
|
271
|
+
size, and the last viewer to resize it won: a second, narrower browser
|
|
272
|
+
(or a hidden tab) left the TUI wrapping at its width in every other
|
|
273
|
+
viewer. The viewer in use now wins: focusing a pane, typing in it after
|
|
274
|
+
another viewer changed the size, and the page being shown again each
|
|
275
|
+
send that pane's size; a hidden pane never resizes the PTY, and no
|
|
276
|
+
resize under 2×2 is sent (the server refuses one too). The server tells
|
|
277
|
+
every viewer the PTY's size on attach and on each change (a `size`
|
|
278
|
+
frame), so a viewer knows when it is out of step. A session host
|
|
279
|
+
that outlived an upgrade sends no such frame, so then every keystroke
|
|
280
|
+
says the typing viewer's size (the same size again redraws nothing). The cells are also
|
|
281
|
+
measured again once fonts finish loading and after a theme is applied,
|
|
282
|
+
so a theme's web font no longer leaves the terminal fitted to the
|
|
283
|
+
fallback font's width.
|
|
284
|
+
- **A Settings row with its own control is drawn.** A custom row without a
|
|
285
|
+
write was drawn as an empty read-only value, so Browser sign-in showed
|
|
286
|
+
nothing; it now shows its state and Sign out.
|
|
287
|
+
- **A redirect is not down (MCP-2.4).** A health check answered `302 →
|
|
288
|
+
/login` (a production app's login page) was reported down. The bug was
|
|
289
|
+
in the shared HTTP check, so the web health row and `<cli> status` had
|
|
290
|
+
it too. A 3xx is now `redirect`: ok null, with its status and target (the
|
|
291
|
+
query dropped), never green, so an ALB's 301 for a stopped service is
|
|
292
|
+
still not taken for up. `target.expect` listing the code counts it as up.
|
|
293
|
+
`<cli> status` shows `redirect 302 → /login`, and `up` and `restart` no
|
|
294
|
+
longer wait out a service that answers with a redirect.
|
|
295
|
+
- **An unchecked service says why (MCP-2.4).** `ok: null` with no reason
|
|
296
|
+
(`runs: none` in beta and prod) now carries `why` on the web health row
|
|
297
|
+
and in `status`: no health check for the env, remote with no check, not
|
|
298
|
+
deployed in the env, or no manifest.
|
|
299
|
+
- **An empty `stacks` explains itself (MCP-2.4):** `/api/stacks` says the
|
|
300
|
+
org config declares no `stacks:` and no repo of the workspace has a
|
|
301
|
+
`compose:` block (or the workspace doesn't exist); scoped, that the
|
|
302
|
+
stacks listed belong to repos outside it.
|
|
303
|
+
- **`certIssued: false` while HTTPS works (MCP-2.5).** It meant "Den minted
|
|
304
|
+
the cert", which is false under `tls: files`. Renamed `certIssuedByDen`.
|
|
305
|
+
- **Security: a scoped `mcp --call` goes through the scoped path, and
|
|
306
|
+
says so.** A review reported that `mcp --workspace A --actor agent:<id>
|
|
307
|
+
--call services '{"workspace":"B"}'` could read workspace B's services
|
|
308
|
+
(paths and commands included). A one-shot now lists and validates
|
|
309
|
+
against the pinned tools exactly as the stdio server registers them, a
|
|
310
|
+
`workspace` naming another workspace always reaches Den's refusal, and
|
|
311
|
+
`--call` prints its scope on stderr (`<cli> mcp --call: scoped to A as
|
|
312
|
+
agent:<id>`), so an unscoped run can't pass for a scoped one. Tests now
|
|
313
|
+
drive the real `bin/den mcp --call` entry.
|
|
314
|
+
- **An ungranted workspace gets Den's refusal (MCP-2.3).** On a scoped
|
|
315
|
+
server with grants, naming a workspace that is not granted answered
|
|
316
|
+
`MCP error -32602 … expected one of "A"|"C"` (the schema). The
|
|
317
|
+
`workspace` parameter is now a name whose description lists the allowed
|
|
318
|
+
ones, and any other gets the refusal naming `agents.<id>.grants` and how
|
|
319
|
+
a person grants it.
|
|
320
|
+
- **The org-wide map is org-wide.** `org_summary.graph` and an unscoped
|
|
321
|
+
`graph` held only the focused workspace's repos, so a repo only in
|
|
322
|
+
another active workspace was missing. They now merge every active
|
|
323
|
+
workspace's graph (`workspaces` names them; `order`, a startup order
|
|
324
|
+
within one workspace, only when one is active); `org_summary` keeps
|
|
325
|
+
names and edge kinds only. `graph` with a `workspace` is labelled
|
|
326
|
+
`map: "workspace"`.
|
|
327
|
+
- **A service's command runs in its own workspace.** The Services page's
|
|
328
|
+
and the tests page's setup, up, down and restart sent no workspace, so a
|
|
329
|
+
service only in a non-focused active workspace failed: `Command "setup"
|
|
330
|
+
not found for <repo>/<svc> … Active workspace: <focus>`. They now send
|
|
331
|
+
the workspace the row was built from. (0.4.3 had the same bug.)
|
|
332
|
+
- **`agents doctor -w <ws>` works.** It answered `workspaces not loaded`;
|
|
333
|
+
it now loads the workspaces config first, as `agents start` does.
|
|
334
|
+
- **A redirect is a redirect in `status`.** A repo row's reason counted a
|
|
335
|
+
service that answered with a redirect as "1 not checked"; it now says
|
|
336
|
+
"1 redirect" (the web header and graph cards too).
|
|
337
|
+
|
|
338
|
+
### Migrations
|
|
339
|
+
|
|
340
|
+
- A page script that imported `requireUnlock`, `checkUnlock`,
|
|
341
|
+
`isUnlocked` or `onLockChange` from `header.js` or `topbar.js`: they are
|
|
342
|
+
gone. Run the action; when its run's events carry `needs-sudo`, the
|
|
343
|
+
shell asks (`sudoRun()` from `header.js`: `see(msg)` on each event,
|
|
344
|
+
`done(retry)` when the run ends).
|
|
345
|
+
- A client that handled `423 locked` from the agent routes: it no longer
|
|
346
|
+
happens.
|
|
347
|
+
- A distribution may add runtimes with `agents.runtime_catalog` (see
|
|
348
|
+
Runtimes in docs/distributions.md).
|
|
349
|
+
- A script reading `<cli> mcp --call status` gets `{ env, counts, services
|
|
350
|
+
}` in place of the array: read `.services`.
|
|
351
|
+
- `edge_status.certIssued` (and `/api/edge`'s) is deprecated: read
|
|
352
|
+
`certIssuedByDen`, or `https.serving` for whether HTTPS works. It goes in
|
|
353
|
+
the release after 0.4.4.
|
|
354
|
+
|
|
7
355
|
## [0.4.3] - 2026-11-06
|
|
8
356
|
|
|
9
357
|
Agents start, and the edge counts as local.
|
package/README.md
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
</p>
|
|
11
11
|
|
|
12
12
|
<picture>
|
|
13
|
-
<source media="(prefers-color-scheme: dark)" srcset="docs/images/0.4.
|
|
14
|
-
<img src="docs/images/0.4.
|
|
13
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/images/0.4.5/hero-dark.png">
|
|
14
|
+
<img src="docs/images/0.4.5/hero-light.png" width="100%" alt="Den's Graph page: the demo workspace's services grouped by application, all healthy, with the status chip and the left rail">
|
|
15
15
|
</picture>
|
|
16
16
|
|
|
17
17
|
## What Den is
|
|
@@ -24,7 +24,7 @@ It doesn't replace your tools. Your repos, Docker, your test runners, your secre
|
|
|
24
24
|
yours. Den connects them and turns on good practice from the start.
|
|
25
25
|
|
|
26
26
|
<p align="center">
|
|
27
|
-
<img src="docs/images/0.4.
|
|
27
|
+
<img src="docs/images/0.4.5/up.gif" width="100%" alt="make up in the Graph page: every stack down, then demo-api, demo-web and demo-notes come up in dependency order until the status chip reads All 3 healthy">
|
|
28
28
|
</p>
|
|
29
29
|
|
|
30
30
|
## How it helps
|
|
@@ -43,20 +43,21 @@ yours. Den connects them and turns on good practice from the start.
|
|
|
43
43
|
- **Agents work by the same rules as you.** Start an agent from a persona (planner, orchestrator, developers,
|
|
44
44
|
devops, reviewer, UX designer, or your own) in a workspace. Its terminal opens in the Agents tab and keeps
|
|
45
45
|
running when you close the page or restart the web server. Over MCP it sees your stack with the same guards
|
|
46
|
-
and audit trail as a person, under a token of its own. Agents can read and investigate
|
|
47
|
-
|
|
46
|
+
and audit trail as a person, under a token of its own. Agents can read and investigate, and acting is opt-in.
|
|
47
|
+
Starting an agent never asks for your admin password: Den asks for it only when a command it runs needs sudo.
|
|
48
48
|
- **Safe on localhost, signed in beyond it.** On localhost there is nothing to sign in to. Reached through the
|
|
49
|
-
edge or your network, every page and call needs a sign-in, and there is no password:
|
|
50
|
-
|
|
49
|
+
edge from another machine, or over your network, every page and call needs a sign-in, and there is no password:
|
|
50
|
+
Sign in with GitHub as the Den's owner, approve the computer with a code (`acme web approve <code>`), or open a
|
|
51
|
+
one-time link from `acme web login`.
|
|
51
52
|
- **Opinionated, not locked in.** Good defaults are switched on, and almost everything can be switched off. The
|
|
52
53
|
safety guards stay on. Den never needs our accounts, secrets or certificates. Everything specific to you
|
|
53
54
|
lives in your own repo.
|
|
54
55
|
|
|
55
56
|
<p align="center">
|
|
56
|
-
<img src="docs/images/0.4.
|
|
57
|
-
<img src="docs/images/0.4.
|
|
58
|
-
<img src="docs/images/0.4.
|
|
59
|
-
<img src="docs/images/0.4.
|
|
57
|
+
<img src="docs/images/0.4.5/agents.png" width="49%" alt="The Agents tab: the demo workspace's sessions on the left, and one open in a terminal pane on the right, running demo-api's test and then the service">
|
|
58
|
+
<img src="docs/images/0.4.5/workspaces.png" width="49%" alt="The Workspaces page: the demo workspace's repos with their health, mode and Start, Stop, Restart and Logs actions">
|
|
59
|
+
<img src="docs/images/0.4.5/status-drawer.png" width="49%" alt="The status drawer: nothing needs attention, and every service in the demo workspace, by application, with its latency">
|
|
60
|
+
<img src="docs/images/0.4.5/edge.png" width="49%" alt="The Edge page: the edge container up, its ports and TLS, and every route it serves, Den's own and each stack's, with its upstream">
|
|
60
61
|
</p>
|
|
61
62
|
|
|
62
63
|
## Get started
|