@monster-orange/den 0.4.1 → 0.4.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.
Files changed (36) hide show
  1. package/CHANGELOG.md +142 -0
  2. package/README.md +88 -158
  3. package/docs/distributions.md +151 -28
  4. package/docs/reference.md +177 -0
  5. package/package.json +1 -1
  6. package/src/shared/agents/den-claude.ts +57 -12
  7. package/src/shared/agents/generic.ts +1 -0
  8. package/src/shared/agents/runtime-search.ts +416 -0
  9. package/src/shared/agents/runtime.ts +26 -1
  10. package/src/shared/agents/runtimes.ts +30 -3
  11. package/src/shared/den-config.ts +88 -3
  12. package/src/shared/edge/render.ts +31 -0
  13. package/src/shared/sessions/client.ts +7 -0
  14. package/src/shared/sessions/den-pty.ts +10 -2
  15. package/src/shared/sessions/host.ts +33 -8
  16. package/src/shared/sessions/types.ts +8 -0
  17. package/src/ui/cli/agent-ops.ts +109 -21
  18. package/src/ui/cli/agents-command.ts +100 -4
  19. package/src/ui/cli/index.ts +33 -1
  20. package/src/ui/cli/sessions-command.ts +26 -9
  21. package/src/ui/cli/web-command.ts +60 -9
  22. package/src/ui/server/agent-routes.ts +11 -3
  23. package/src/ui/server/edge-exposure.ts +143 -0
  24. package/src/ui/server/index.ts +105 -23
  25. package/src/ui/server/public/agent-start.js +202 -0
  26. package/src/ui/server/public/agents-page.js +90 -18
  27. package/src/ui/server/public/agents-section.js +6 -18
  28. package/src/ui/server/public/agents.html +70 -0
  29. package/src/ui/server/public/i18n/es.js +11 -0
  30. package/src/ui/server/public/i18n/ja.js +11 -0
  31. package/src/ui/server/public/local-dev.html +43 -14
  32. package/src/ui/server/public/settings-registry.js +1 -1
  33. package/src/ui/server/public/shell.js +40 -0
  34. package/src/ui/server/public/topbar.js +127 -28
  35. package/src/ui/server/request-guard.ts +122 -7
  36. package/src/ui/server/ui-assets.ts +2 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,148 @@ 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.3] - 2026-11-06
8
+
9
+ Agents start, and the edge counts as local.
10
+
11
+ ### Fixed
12
+
13
+ - **The Start agent dialog can be submitted (AG-10.0).** A persona's whole
14
+ role sentence was in its option, so the select grew past the dialog and
15
+ pushed Cancel and Start out of sight. A persona option now shows its id,
16
+ with its role on a help line under the select that follows the pick. The
17
+ runtime's first option names what it resolves to ("Claude Code (persona
18
+ default)"), a runtime that can't run here says why, and Enter starts
19
+ from any field. The Workspaces page's agents form gets the same choices.
20
+
21
+ - **Agents start on a Den run as a service (AG-0.1).** Starting any agent
22
+ failed with "den-claude is unavailable: claude is not on PATH" when the
23
+ web server ran under launchd: its PATH leaves out `~/.local/bin`, where
24
+ Claude Code's native installer puts `claude`, and Den searched only that
25
+ PATH. Den now finds a runtime's CLI as your terminal would:
26
+ `agents.runtimes.<id>.bin` in `config/local/user.yml` wins (an absolute
27
+ path, checked; a bad one is refused with its reason), then the login
28
+ shell's PATH (`$SHELL -lc`, asked once at server start in the
29
+ background, 3-second timeout, falling back to the current PATH), the
30
+ server's PATH, and the runtime's install directories (`~/.local/bin`,
31
+ `~/.claude/local`, `/opt/homebrew/bin`, `/usr/local/bin`, npm's global
32
+ bin). Agents and plain sessions get the same directories on their PATH.
33
+ - **The lock is always reachable (LK-1 to LK-3).** Start agent opened its
34
+ dialog and only then asked for the lock, whose popover sat under the
35
+ dialog where nothing could reach it. The lock is now checked before the
36
+ dialog opens, and the dialog asks for the admin password at its top:
37
+ Start unlocks, then starts. The lock popover is in the top layer, so no
38
+ dialog covers it. A page that thinks it's locked asks the server before
39
+ asking you (another tab or device may have unlocked it), and, when a
40
+ password is stored in the vault, the first action that needs the lock
41
+ loads it from there on every page, not only Local Dev. A page view still
42
+ never wakes the vault.
43
+
44
+ ### Changed
45
+
46
+ - **The edge from this machine needs no sign-in.** A request for the edge
47
+ hostname from an edge peer is treated as loopback: no sign-in on any
48
+ route, agents and the terminal included (0.4.2 asked for one on every
49
+ route). Host, Origin, CSRF and the agent-token rules stay. The client is
50
+ the left-most `X-Forwarded-For` entry: loopback or one of this machine's
51
+ own interface addresses (a Tailscale `100.x` included) is local; missing,
52
+ or a Docker/VM network's address (what Docker Desktop and OrbStack hand
53
+ the edge; the subnets of the edge's own Docker networks, `<org>-edge`
54
+ and those it joins, read once at start with `docker network inspect`;
55
+ OrbStack's host address `0.250.250.254`), is "can't tell", also local, so
56
+ a TCP-passthrough proxy container on the edge's network is no other
57
+ machine; any other address is another
58
+ machine and needs a sign-in. A non-loopback `DEN_BIND_HOST` bind (other
59
+ than the Linux edge's Docker bridge) and `web.allow_plain_http_remote`'s
60
+ LAN names still need one. See docs/distributions.md, "Signing in".
61
+
62
+ ### Added
63
+
64
+ - **The Start dialog says before Start whether the agent can run
65
+ (AG-0.2).** Under the runtime a line names what Start will run, with its
66
+ version and path ("Starts Claude Code 2.1.0 from …"); the runtime's
67
+ option shows its version. When the runtime can't run here, Start is
68
+ disabled and the line says why and how to fix it: where Den looked, then
69
+ install it, or set `agents.runtimes.<id>.bin` in the user.yml it names,
70
+ or run `<cli> agents doctor`. The Workspaces page's agents form does the
71
+ same. `GET /api/personas` gives each runtime `version` (its version
72
+ command, run once under a timeout and kept), `path`, `source`
73
+ (`setting`, `login-shell`, `well-known` or `PATH`), `searched` and
74
+ `setting`, and the response a `userConfig` path.
75
+ - **`<cli> agents doctor [--json]` (AG-0.3)**: each runtime's status, the
76
+ CLI it runs and where it came from, its version, the directories
77
+ searched (names only, never an environment value), and the fix when it
78
+ is missing. It exits 1 when the default runtime is unavailable.
79
+
80
+ - **"Your edge is reachable from other machines".** When an edge hostname
81
+ resolves to an address that isn't loopback, or the edge container
82
+ publishes a port on every interface (`docker inspect`), Den says so once
83
+ in `web.log` at start and in a banner on every page (dismissed per
84
+ browser): "Your edge is reachable from other machines; anyone who can
85
+ reach it can use terminals. Auth is coming."
86
+ - **`<cli> web login --host <name>`** mints the one-time link for an edge
87
+ hostname (an edge domain or alias, a VPN name among them) at its edge
88
+ URL, instead of localhost. A name the edge doesn't route to this server
89
+ is refused, naming the hostnames it does and what to add. `--origin`
90
+ stays.
91
+
92
+ ### Migrations
93
+
94
+ - **A Den run as a service whose agents' CLI is somewhere unusual**: set
95
+ `agents.runtimes.<id>.bin` (an absolute path) in `config/local/user.yml`.
96
+ Nothing else changes: a runtime on the server's PATH is found as before.
97
+ - **A page script that called `requireUnlock()`** (header.js): it now
98
+ returns a promise of the answer; `await` it.
99
+
100
+ ## [0.4.2] - 2026-11-06
101
+
102
+ The 0.4.1 carry-overs.
103
+
104
+ ### Added
105
+
106
+ - **`sessions.max_concurrent`** (default 8): the cap on plain sessions
107
+ (`sessions start`) running at once. Past it, `sessions start` is refused
108
+ as the agents' cap is: "2 sessions are running and
109
+ sessions.max_concurrent is 2", then "Stop one first (`<cli> sessions
110
+ kill <name>`), or raise sessions.max_concurrent." `<cli> validate` names
111
+ a wrong value of it or of `agents.max_concurrent`.
112
+
113
+ ### Changed
114
+
115
+ - **`agents.max_concurrent` counts agents only.** Plain sessions count
116
+ against `sessions.max_concurrent` instead, so a few plain shells never
117
+ stop an agent from starting (from the CLI, the Workspaces page, the
118
+ Agents tab or MCP). Its refusal says "1 agent is running and
119
+ agents.max_concurrent is 1".
120
+ - **The Agents tab's pane title and Close** name the agent by its id
121
+ (its display name once AG-10 adds one), never its persona; the list row
122
+ still shows the persona, with the id beside it.
123
+ - **den-claude's deny list, in the docs**: it is a convenience, not the
124
+ boundary. It binds cooperating agents only and does not catch every
125
+ spelling (a path relative to the agent's cwd, another program); the
126
+ server's token rules are the boundary.
127
+
128
+ ### Fixed
129
+
130
+ - **A distribution config's `web.allow_plain_http_remote`** (ignored: it is
131
+ read only from `config/local/user.yml`) was warned about only in the
132
+ in-app server log. It is now in `web.log` too (before a bind refusal it
133
+ often explains), in the `web up` and `web restart` output, and in
134
+ `<cli> validate`.
135
+ - **`up --help`**: `-w, --workspace <ws>` ran into its text ("<ws>act in
136
+ that…"); there are two spaces now. `down`, `restart` and `setup --help`
137
+ were already spaced.
138
+
139
+
140
+ ### Migrations
141
+
142
+ - **More than 4 plain sessions at once**, or a script that relied on
143
+ `agents.max_concurrent` to cap plain sessions: set
144
+ `sessions.max_concurrent` (default 8). `agents.max_concurrent` no longer
145
+ counts them. A session host already running from 0.4.1 keeps counting
146
+ every session until it restarts (`sessions host --stop`, which ends its
147
+ sessions, or a reboot).
148
+
7
149
  ## [0.4.1] - 2026-11-06
8
150
 
9
151
  ### Added
package/README.md CHANGED
@@ -1,169 +1,99 @@
1
- # Den
2
-
3
- Den is a config-driven local development appliance. It starts the repos you
4
- work on in dependency order, puts them behind a local HTTPS edge, gives you a
5
- web UI and a CLI over all of it, and runs smoke, browser, API, journey,
6
- security and performance suites against the environments you declare.
7
-
8
- Den knows nothing about any particular organisation. Everything specific to
9
- yours — product name, repos, environments, hosts, secrets backend, test
10
- accounts — lives in **your distribution**, not in Den.
11
-
12
- > **Status: 0.2.0**: `npm install @monster-orange/den`. Den is pre-1.0; each release lists the changes a distribution makes under **Migrations** in [CHANGELOG.md](CHANGELOG.md).
13
-
14
- ## Make it your own
15
-
16
- 1. **Create** a distribution named after your product:
17
-
18
- ```sh
19
- npx @monster-orange/den create acme # → ./acme
20
- cd acme && npm install
21
- ```
22
-
23
- 2. **Name** it: `den.yml` holds `product: { name: acme }`, so the CLI is
24
- `./bin/acme`, env vars are `ACME_*`, state lives in `~/.acme`. Pass
25
- `--cli`, `--env-prefix` or `--title` to `create`, or edit `den.yml`.
26
- 3. **Brand** it: `brand:` in `config/acme.yml` sets the name, tagline and
27
- banner people see in the CLI and web UI.
28
- 4. **Point it at your repos**: set `github_org` in `config/acme.yml`, then
29
- `./bin/acme register ../shop-api` (or add a manifest per repo under
30
- `manifests/`).
31
- 5. **Run** it: `./bin/acme up`, `./bin/acme web`, `make test`.
32
-
33
- Every field, the bin shim, the make fragment and upgrading are in
34
- [docs/distributions.md](docs/distributions.md);
35
- [examples/distribution/](examples/distribution) is what `create acme` writes.
36
-
37
- ## Distributions
38
-
39
- You run Den through a small repo of your own (a _distribution_):
40
-
41
- ```
42
- my-dev/
43
- ├── den.yml # who this distribution is (below)
44
- ├── package.json # pins an exact @monster-orange/den version
45
- ├── config/<name>.yml # your org config
46
- ├── config/environments/ # one <env>.json per environment
47
- ├── manifests/ # manifests for repos that don't carry their own
48
- ├── tests/pillars/<pillar>/ # your own suites (e.g. tests/pillars/perf/<module>/test.js)
49
- ├── bin/<cli> # shim: DEN_ROOT=<this dir> exec node_modules/@monster-orange/den/bin/den
50
- └── Makefile # include node_modules/@monster-orange/den/make/den.mk
1
+ <p align="center">
2
+ <img src="docs/images/preview/mark.svg" width="72" alt="Den">
3
+ </p>
4
+ <h1 align="center">Den</h1>
5
+ <p align="center"><b>Your whole stack, up and green, in one place. Built so you and your agents work by the same rules.</b></p>
6
+ <p align="center">
7
+ <a href="https://www.npmjs.com/package/@monster-orange/den"><img src="https://img.shields.io/npm/v/@monster-orange/den" alt="npm"></a>
8
+ <img src="https://img.shields.io/npm/l/@monster-orange/den" alt="licence">
9
+ <a href="https://github.com/Monster-Orange/den/actions/workflows/ci.yml"><img src="https://github.com/Monster-Orange/den/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
10
+ </p>
11
+
12
+ <picture>
13
+ <source media="(prefers-color-scheme: dark)" srcset="docs/images/0.4.3/hero-dark.png">
14
+ <img src="docs/images/0.4.3/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
+ </picture>
16
+
17
+ ## What Den is
18
+
19
+ Den is an opinionated local development framework for solo developers and small teams. You point it at the
20
+ repos you work on. Den starts them in the right order, puts them behind a local edge, and shows you the
21
+ whole thing in one web shell and one CLI: what's up, what's down, why, and the fix.
22
+
23
+ It doesn't replace your tools. Your repos, Docker, your test runners, your secrets store and your agents stay
24
+ yours. Den connects them and turns on good practice from the start.
25
+
26
+ <p align="center">
27
+ <img src="docs/images/0.4.3/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
+ </p>
29
+
30
+ ## How it helps
31
+
32
+ - **"How do I run this?" goes away.** Each repo declares how it starts, what it needs and how to check it's
33
+ healthy. Den works out the order and brings everything up, the same way on every machine.
34
+ - **One place to look.** Health, logs, runs, routes and certificates for every service, in one shell, instead
35
+ of eight terminals and a wiki page.
36
+ - **Real hostnames locally.** Every service gets a proper hostname behind a local edge, over HTTPS when you
37
+ turn it on (a local CA with mkcert, or certificates you bring). Cookies, OAuth redirects and CORS behave as
38
+ they do in production.
39
+ - **Several workspaces at once.** Keep feature branches side by side as workspaces. Health covers every active
40
+ one, and `up`, `down`, `restart` and `setup` take `-w <workspace>`, or find the service you name across them.
41
+ - **Tests are part of the loop.** Smoke, browser, API, user journeys, security and performance suites run against
42
+ the environments you declare.
43
+ - **Agents work by the same rules as you.** Start an agent from a persona (planner, orchestrator, developers,
44
+ devops, reviewer, UX designer, or your own) in a workspace. Its terminal opens in the Agents tab and keeps
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. Acting is opt-in,
47
+ and the lock still guards starting and stopping things.
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: `acme web login` prints a
50
+ one-time link.
51
+ - **Opinionated, not locked in.** Good defaults are switched on, and almost everything can be switched off. The
52
+ safety guards stay on. Den never needs our accounts, secrets or certificates. Everything specific to you
53
+ lives in your own repo.
54
+
55
+ <p align="center">
56
+ <img src="docs/images/0.4.3/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">
57
+ <img src="docs/images/0.4.3/workspaces.png" width="49%" alt="The Workspaces page: the demo workspace's repos with their health, mode and Start, Stop, Restart and Logs actions">
58
+ <img src="docs/images/0.4.3/status-drawer.png" width="49%" alt="The status drawer: nothing needs attention, and every service in the demo workspace, by application, with its latency">
59
+ <img src="docs/images/0.4.3/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
+ </p>
61
+
62
+ ## Get started
63
+
64
+ You need Node 22.13 or later, and Docker for anything that runs in containers. mkcert is only needed if you want Den to issue local HTTPS certificates.
65
+
66
+ ```sh
67
+ npx @monster-orange/den create acme # your own Den, named after your product
68
+ cd acme && npm install
69
+ ./bin/acme register ../shop-api # add a repo (repeat for each one)
70
+ ./bin/acme up # start everything, in order
71
+ ./bin/acme web # open the shell
51
72
  ```
52
73
 
53
- `den.yml` names the product. Every runtime name derives from it:
74
+ That's it: `acme` is now your tool, with your name, your CLI and your config, in a repo you own.
75
+ [Step by step →](docs/reference.md#make-it-your-own)
54
76
 
55
- ```yaml
56
- product:
57
- name:
58
- acme # CLI "acme", env vars ACME_*, config/acme.yml,
59
- # manifests .acme.yml, state ~/.acme,
60
- # managed clones .acme-services/, workspaces acme-workspaces/
61
- # Override any of them: cli, env_prefix, config, manifest, state_dir,
62
- # services_dir, workspaces_dir
63
- ```
77
+ ## How it fits together
64
78
 
65
- Den's own checkout is a distribution named `den`, so in this repo the CLI is
66
- `./bin/den`, the config is `config/den.yml`, and env vars are `DEN_*`.
67
-
68
- ## Repos opt in with a manifest
69
-
70
- A repo joins by carrying a manifest at its root (`.<name>.yml`, e.g.
71
- `.den.yml`) whose commands delegate to the repo's own targets. Den never
72
- reaches inside a repo beyond that. When a repo has no manifest, the
73
- distribution can hold one for it in `manifests/<repo>.<name>.yml`; the repo's
74
- own manifest always wins.
75
-
76
- ```yaml
77
- # demo-api: a self-contained service. Nothing it calls, so it starts alone.
78
- version: 1
79
- name: demo-api
80
- application: shop # the application this repo belongs to (den apps)
81
- compose:
82
- files: [docker-compose.yml]
83
- routes:
84
- api: api:8080 # https://api.demo-api.<edge domain>
85
- services:
86
- demo-api:
87
- commands:
88
- local:
89
- setup: make setup
90
- preflight: make test
91
- # OWASP ZAP target for the security pillar (`make test-sec-api env=local`).
92
- security:
93
- api:
94
- hosts:
95
- local: ["https://api.demo-api.globex.localhost"]
96
- ```
79
+ <p align="center"><img src="docs/images/preview/how-it-fits.svg" width="100%" alt="Den, the engine, runs your distribution, which holds your config; the distribution groups repos into workspaces, and each repo declares its services"></p>
97
80
 
98
- That is `examples/demo/demo-api/.den.yml`; the other demo repos are in
99
- [`examples/demo/`](examples/demo/).
100
-
101
- ## What it does
102
-
103
- - **Workspaces.** A workspace is a set of repos. Each is either
104
- _dev-managed_ (your checkout, your branch) or _managed_ (cloned by Den and
105
- kept on a tracking branch).
106
- - **Derived dependency graph.** Start and stop order comes from real signals in
107
- each repo — SDK imports, `*_URL` env vars, IaC references, event rules,
108
- readiness probes — tuned in config with `graph.aliases`, `graph.edges` and
109
- an optional shared local-infra repo (`graph.infra`).
110
- - **Local HTTPS edge.** One local proxy routes `https://<route>.<domain>` to
111
- each service, with an optional project CA.
112
- - **Test pillars.** `smoke`, `browser` (synthetic Playwright), `api`,
113
- `journeys` (agent-driven), `perf` (k6), `security` (OWASP ZAP against hosts
114
- you own) and `suites` (a repo's own commands).
115
- - **Releases.** A release view over the repos and deploy workflows your config
116
- lists.
117
- - **Web UI and CLI** over the same state.
118
-
119
- ## Strategies
120
-
121
- Concerns that vary by org are strategies selected in config, never hard-wired:
122
-
123
- | Concern | Built-in choices | Chosen in |
124
- | ---------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
125
- | Secrets backend | `none`, `env`, `doppler`, `1password` | `secrets.provider` in the org config; per engineer in `config/local/user.yml`; per shell `<PREFIX>_SECRETS_PROVIDER` |
126
- | Test-user tokens | `local-jwt`, `cognito`, `auth0`, `none` | `auth.provider` in `config/environments/<env>.json` |
127
-
128
- A new secrets backend implements `SecretsProvider`
129
- (`src/shared/secrets/types.ts`) and registers as a strategy
130
- (`src/shared/secrets/strategies.ts`); a distribution needs no store at all
131
- (`none`, see "Secrets" in `docs/distributions.md`). A new identity provider implements a token
132
- provider in `src/shared/auth/token-providers/`.
133
-
134
- ## CLI
135
-
136
- ```bash
137
- den up [repo] # start the repo's derived dependency closure (-x deps only, -n dry run)
138
- den down [repo] # stop it cleanly (--with-deps: and what it sits on)
139
- den status # service health for the active workspace
140
- den apps # Org > Application > Repo > Services
141
- den graph [repo] # the derived dependency graph
142
- den logs [repo] # tail a repo's local logs
143
- den use-local [repo] # use your checkout · use-managed [repo] to switch back
144
- den workspace list # workspaces (activate · deactivate · use · create · delete)
145
- den run # run test suites (interactive, or with options)
146
- den web # the web UI
147
- den --help # everything else
148
- ```
81
+ - **Den** is the engine, from npm. It knows nothing about your company.
82
+ - **Your distribution** (`acme`) is a small repo you own. It holds your product name, brand, environments,
83
+ hosts and secrets setup.
84
+ - **Workspaces** group the repos you work on together. **Manifests** in each repo say how its services start,
85
+ connect and get checked.
149
86
 
150
- In a distribution the command is that distribution's `cli` name.
87
+ ## Coming next
151
88
 
152
- ## Developing Den
89
+ One workspace focus with Switch, agent pane layouts, and a Start dialog with personas, roles and a choice of
90
+ runtime and model.
153
91
 
154
- ```bash
155
- npm ci
156
- npm run typecheck
157
- npm run lint
158
- npm test
159
- DEN_ORG_NAMES_FILE=<patterns file> node scripts/org-name-gate.mjs
160
- ```
92
+ ## Docs
161
93
 
162
- CI runs the same gates. The org-name gate fails the build when any file names
163
- an organisation, product, host or account; its patterns are kept out of this
164
- repo (a CI secret) so the list itself is never published. See
165
- [CONTRIBUTING.md](CONTRIBUTING.md).
94
+ [Reference](docs/reference.md) (configuration, manifests, strategies, CLI) · [Distributions](docs/distributions.md) ·
95
+ [Changelog](CHANGELOG.md)
166
96
 
167
- ## License
97
+ Found a bug or have an idea? [Open an issue](https://github.com/Monster-Orange/den/issues/new).
168
98
 
169
- [MIT](LICENSE)
99
+ MIT licence.