@monster-orange/den 0.4.20 → 0.4.22
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 +164 -0
- package/README.md +7 -7
- package/docs/distributions.md +167 -19
- package/package.json +1 -1
- package/service/install.sh +213 -63
- package/src/shared/agents/den-claude.ts +13 -1
- package/src/shared/agents/records.ts +6 -0
- package/src/shared/agents/runtime.ts +6 -0
- package/src/shared/agents/session-name.ts +21 -1
- package/src/shared/attest-code.ts +125 -0
- package/src/shared/claim-code.ts +6 -2
- package/src/{ui/server/edge-exposure.ts → shared/edge/exposure.ts} +7 -1
- package/src/shared/github-app.ts +4 -0
- package/src/shared/github-device.ts +38 -1
- package/src/shared/launchd-session.ts +92 -0
- package/src/shared/owner-github.ts +100 -0
- package/src/shared/product.ts +45 -5
- package/src/shared/profile.ts +26 -2
- package/src/shared/sessions/client.ts +24 -0
- package/src/shared/sessions/host.ts +114 -5
- package/src/shared/sessions/types.ts +5 -0
- package/src/shared/web-session.ts +112 -6
- package/src/ui/cli/agent-ops.ts +20 -2
- package/src/ui/cli/agents-command.ts +32 -1
- package/src/ui/cli/create.ts +92 -6
- package/src/ui/cli/doctor-command.ts +126 -15
- package/src/ui/cli/doctor-owner.ts +122 -0
- package/src/ui/cli/doctor-where.ts +203 -0
- package/src/ui/cli/index.ts +1 -1
- package/src/ui/cli/service-command.ts +233 -11
- package/src/ui/cli/sessions-command.ts +46 -12
- package/src/ui/cli/web-command.ts +78 -12
- package/src/ui/server/device-flows.ts +45 -10
- package/src/ui/server/github-routes.ts +169 -16
- package/src/ui/server/index.ts +147 -12
- package/src/ui/server/public/agents-page.js +103 -6
- package/src/ui/server/public/agents.html +30 -0
- package/src/ui/server/public/error-page.js +115 -33
- package/src/ui/server/public/error.html +16 -0
- package/src/ui/server/public/header.js +5 -7
- package/src/ui/server/public/i18n/es.js +24 -1
- package/src/ui/server/public/i18n/ja.js +24 -1
- package/src/ui/server/public/settings-registry.js +1 -1
- package/src/ui/server/public/shell.js +24 -1
- package/src/ui/server/route-classes.ts +7 -0
- package/src/ui/server/session-routes.ts +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,170 @@ 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.22] - 2026-11-10
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Opt-in: agents meant to keep running across logout (D18, D20).**
|
|
12
|
+
`service install --mode always` installs the web server and the session
|
|
13
|
+
host as LaunchDaemons that run as you (`UserName`): they start at boot
|
|
14
|
+
and are meant to keep running when you log out of the desktop, and so
|
|
15
|
+
every terminal and agent. It is not yet proven across a logout: `service
|
|
16
|
+
help`, `--plan`, install, `doctor` and the agents page say so. It takes your admin password once (sudo); `--mode login`, the
|
|
17
|
+
default, keeps LaunchAgents in the desktop session (they stop at
|
|
18
|
+
logout). Without `--mode`, install keeps the mode already installed.
|
|
19
|
+
Switching removes the other mode's jobs.
|
|
20
|
+
- **The session host is its own launchd job** (`<label>` with `.web` →
|
|
21
|
+
`.sessions`), in both modes, so it runs in the mode's session whoever
|
|
22
|
+
starts the first terminal: before, it inherited the launchd session of
|
|
23
|
+
whatever started it, and one started from the desktop ended at logout
|
|
24
|
+
with every agent in it. `<cli> sessions` and agents wait for the job.
|
|
25
|
+
- **Full Disk Access for Den, not for every node program.** The host job
|
|
26
|
+
runs a copy of node Den owns, `<stateDir>/bin/<cli>-sessions-host`
|
|
27
|
+
(0700), the path to grant Full Disk Access to (protected folders: other
|
|
28
|
+
volumes, Desktop, Documents); agents inherit it. Install refreshes the
|
|
29
|
+
copy when node changes and says when the grant must be given again (a
|
|
30
|
+
new signer, an ad-hoc signed node). A node that can't be copied
|
|
31
|
+
(Homebrew's) is run directly, and install says so.
|
|
32
|
+
- **Where it runs, shown honestly.** Every session records the launchd
|
|
33
|
+
session its host ran in (`launchctl managername`). The Agents page tags
|
|
34
|
+
each agent **Stops at logout** (the desktop session; tap for the fix) or
|
|
35
|
+
**No GUI** (kept across logout: no login keychain, no GUI apps; 1Password
|
|
36
|
+
through the service-account token, headless browsers, Full Disk Access
|
|
37
|
+
for protected folders). `doctor` ends with **where it runs**: the
|
|
38
|
+
server's and the host's session, Full Disk Access and the path to grant,
|
|
39
|
+
and the service's mode. `web up` from an ssh (Background) shell says what
|
|
40
|
+
a server started there means.
|
|
41
|
+
- **Rename, visible (D25).** **Rename…** in an agent pane's ⋯ menu and
|
|
42
|
+
**Rename** on a stopped agent's row, by touch or keyboard, and
|
|
43
|
+
`<cli> agents rename <id> <name>` (`--clear`: back to its id). Before,
|
|
44
|
+
only a double-click on an open tab renamed one.
|
|
45
|
+
- **Agents tell apart in a list of sessions (D24).** Den names each
|
|
46
|
+
Claude Code agent's session `<cli>-<ws>:<name, else id>` (`--name`) at
|
|
47
|
+
start and resume, so another session listing its peers sees which agent
|
|
48
|
+
is which, not a generic name. A rename shows at its next start or resume.
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
|
|
52
|
+
- **`service install` said running while its job crash-looped (D26).** A
|
|
53
|
+
plain `web up` server of the same install on the port answered the
|
|
54
|
+
check, while the job failed to start on the taken port. Install now
|
|
55
|
+
stops a plain server of this install first, and says running only when
|
|
56
|
+
the server answering is the job's own process; `service status` says
|
|
57
|
+
when another process holds the port. `--plan` says when install would
|
|
58
|
+
stop one.
|
|
59
|
+
- **`service status` names a job whose plist is present but not loaded**,
|
|
60
|
+
with the `launchctl bootstrap` that loads it (`sudo` for a LaunchDaemon);
|
|
61
|
+
`doctor` shows it too.
|
|
62
|
+
- **Tests never reach this machine's launchctl, gh or vendor CLIs**, even
|
|
63
|
+
through a spawn without `env`: the inert stand-ins are now first on the
|
|
64
|
+
real PATH (jest's globalSetup), not only on each test file's sandboxed
|
|
65
|
+
`process.env`.
|
|
66
|
+
|
|
67
|
+
### Migrations
|
|
68
|
+
|
|
69
|
+
- None required. An existing install keeps its mode (login). To try
|
|
70
|
+
keeping agents running across logout (not yet proven), run
|
|
71
|
+
`<cli> service install --mode always` once (one sudo), then grant Full
|
|
72
|
+
Disk Access to the path `doctor` names if agents read protected folders.
|
|
73
|
+
- `<cli> service` with no subcommand prints its help (before, an error).
|
|
74
|
+
|
|
75
|
+
## [0.4.21] - 2026-11-09
|
|
76
|
+
|
|
77
|
+
### Added
|
|
78
|
+
|
|
79
|
+
- **Every developer connects GitHub from the browser (D17).** A company
|
|
80
|
+
makes its own Den repo, every developer runs their own instance, and
|
|
81
|
+
everyone opens the same edge URL (such as `https://den.local.<zone>`,
|
|
82
|
+
whose DNS says 127.0.0.1). Before, they all got the signed-out page with
|
|
83
|
+
no way in from the browser: behind Docker Desktop, OrbStack or a TCP
|
|
84
|
+
passthrough the edge sees a gateway's address, so Den couldn't tell a
|
|
85
|
+
browser on its own machine; the one-time link needed a terminal, pairing
|
|
86
|
+
needed Settings, and Sign in with GitHub needed an owner already linked.
|
|
87
|
+
Now, while an instance has no owner, the signed-out page leads with
|
|
88
|
+
**Connect your GitHub account**:
|
|
89
|
+
- **This is my computer** (for a member of `web.allowed_github_org`):
|
|
90
|
+
a top-level round trip through `http://localhost:<port>/attest`, which
|
|
91
|
+
only a browser on this machine reaches, back to the edge page with a
|
|
92
|
+
short-lived, single-use code bound to that edge hostname and signed
|
|
93
|
+
with `web.secret` (in the URL fragment, never logged; only to this
|
|
94
|
+
Den's own edge hostnames). The GitHub sign-in that follows makes a
|
|
95
|
+
member of the org the owner and signs the browser in. The proof alone
|
|
96
|
+
never makes a session (any program on the machine can reach
|
|
97
|
+
localhost), so the GitHub approval is always the person's.
|
|
98
|
+
- **Sign in as @login** (`web.owner_github`): that login claims it from
|
|
99
|
+
any browser, such as a laptop reaching a devbox.
|
|
100
|
+
- Anyone else is refused with the reason (not a member; a member but not
|
|
101
|
+
on this computer; not that login; a private membership the GitHub App
|
|
102
|
+
can't read, naming the fix). With neither set, the page names both
|
|
103
|
+
settings and offers no claim.
|
|
104
|
+
- **`create --github-org <org>`** writes `web.allowed_github_org` into the
|
|
105
|
+
team config; without the flag it uses the GitHub owner of the repo the
|
|
106
|
+
new folder is in, else writes a commented line and says to set it
|
|
107
|
+
(`create --json`: `githubOrg`, null when unknown).
|
|
108
|
+
- **`web up`, `web restart` and `service install` record
|
|
109
|
+
`web.owner_github`** in `config/local/user.yml` from `gh api user` when
|
|
110
|
+
`gh` is signed in, no owner is linked and it isn't set, and print it.
|
|
111
|
+
When `gh` isn't signed in, they and `doctor` say so, with the fix
|
|
112
|
+
(`gh auth login`, or set `web.owner_github`).
|
|
113
|
+
- **doctor** shows who may claim the instance (`web:owner` in
|
|
114
|
+
`doctor --json`), and fails when there is no owner, neither setting, and
|
|
115
|
+
the edge is reachable from other machines.
|
|
116
|
+
- **`web.login_link_ttl` (D16)**: how long a sign-in link lasts, 5 minutes
|
|
117
|
+
unless set (it was 60 s, and expired while people switched windows);
|
|
118
|
+
30s to 60m. `web login` shows the time left while it waits.
|
|
119
|
+
|
|
120
|
+
### Fixed
|
|
121
|
+
|
|
122
|
+
- doctor no longer shows `usage (invalid config) ✗ unavailable: no selector
|
|
123
|
+
for this kind` on every fresh install (D9). Usage sources are all read at
|
|
124
|
+
once, so the row names them (`claude-code (implicit)`), as any strategy
|
|
125
|
+
kind with no selector of its own now does; never a failure. The state
|
|
126
|
+
column lines up past 20 characters.
|
|
127
|
+
- **Every page was broken for a distribution whose name has a hyphen**
|
|
128
|
+
(`acme-den`), since 0.4.20 at least. `header.js` put the
|
|
129
|
+
`__DEN_CLI__` placeholder inside a variable name, so the served file
|
|
130
|
+
read `let _acme-denConfig`, a syntax error: no page mounted anything,
|
|
131
|
+
the signed-out page included ("Error / local"), and Settings showed an
|
|
132
|
+
error. A new check fills every served page and script with hyphenated,
|
|
133
|
+
digit-bearing and short names and parses each one, and no placeholder
|
|
134
|
+
may touch a name's letters.
|
|
135
|
+
- `/attest` reached through the edge shows its own "only on the computer
|
|
136
|
+
this Den runs on" page, not the generic signed-out one.
|
|
137
|
+
- doctor, when `web.owner_github` isn't set but `gh` is signed in, says to
|
|
138
|
+
run `web restart` to record it (not that gh isn't signed in).
|
|
139
|
+
|
|
140
|
+
### Changed
|
|
141
|
+
|
|
142
|
+
- The sign-in smoke runs against a remote browser with `DEN_PW_WS` (a
|
|
143
|
+
Playwright run-server): WebKit from Playwright's Linux image when the
|
|
144
|
+
shell can't start a GUI app (CONTRIBUTING.md).
|
|
145
|
+
- The GitHub code on the sign-in page has its copy button right beside it,
|
|
146
|
+
on its line (as every command on that page), at any width.
|
|
147
|
+
- Tests put a stub `launchctl` first on PATH, as for gh and the agent
|
|
148
|
+
CLIs: no test can reach this machine's real launchd.
|
|
149
|
+
- Pages, `--version`, doctor and the server agent's name say the brand's
|
|
150
|
+
name (`brand.name` in the org config: "Sign in to Acme Den"), not the CLI
|
|
151
|
+
name upper-cased ("Acme-den") (D11). A `brand.name` with characters that
|
|
152
|
+
could break a page (quotes, `&`, `<`) falls back to the old title.
|
|
153
|
+
- `product.cli` in den.yml must be lower-case letters, digits and `-`,
|
|
154
|
+
starting with a letter (as `product.name`). `product.config`,
|
|
155
|
+
`manifest`, `services_dir` and `workspaces_dir` may use letters, digits,
|
|
156
|
+
`.`, `_`, `-` and `/` only: the pages carry these names, and a quote or
|
|
157
|
+
`$` would break their scripts. A value outside that is refused at start,
|
|
158
|
+
naming the key.
|
|
159
|
+
- The page behind a `web login` link that asks for its code is a plain
|
|
160
|
+
sign-in page: no shell frame and no request id, the code field focused.
|
|
161
|
+
|
|
162
|
+
### Migrations
|
|
163
|
+
|
|
164
|
+
- A `product.cli` or product path outside the characters above now stops
|
|
165
|
+
the CLI and server with a message naming the key: rename it.
|
|
166
|
+
- To let your team connect GitHub from the browser, set
|
|
167
|
+
`web.allowed_github_org: <your GitHub org>` in the team config (or run
|
|
168
|
+
`web up` with `gh` signed in, for `web.owner_github`). The GitHub App
|
|
169
|
+
needs the org's Members (read) permission to see private memberships.
|
|
170
|
+
|
|
7
171
|
## [0.4.20] - 2026-11-09
|
|
8
172
|
|
|
9
173
|
### Added
|
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.21/hero-dark.png">
|
|
14
|
+
<img src="docs/images/0.4.21/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
|
## Set up by your agent
|
|
@@ -39,7 +39,7 @@ It doesn't replace your tools. Your repos, Docker, your test runners, your secre
|
|
|
39
39
|
yours. Den connects them and turns on good practice from the start.
|
|
40
40
|
|
|
41
41
|
<p align="center">
|
|
42
|
-
<img src="docs/images/0.4.
|
|
42
|
+
<img src="docs/images/0.4.21/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">
|
|
43
43
|
</p>
|
|
44
44
|
|
|
45
45
|
## How it helps
|
|
@@ -69,10 +69,10 @@ yours. Den connects them and turns on good practice from the start.
|
|
|
69
69
|
lives in your own repo.
|
|
70
70
|
|
|
71
71
|
<p align="center">
|
|
72
|
-
<img src="docs/images/0.4.
|
|
73
|
-
<img src="docs/images/0.4.
|
|
74
|
-
<img src="docs/images/0.4.
|
|
75
|
-
<img src="docs/images/0.4.
|
|
72
|
+
<img src="docs/images/0.4.21/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">
|
|
73
|
+
<img src="docs/images/0.4.21/workspaces.png" width="49%" alt="The Workspaces page: the demo workspace's repos with their health, mode and Start, Stop, Restart and Logs actions">
|
|
74
|
+
<img src="docs/images/0.4.21/status-drawer.png" width="49%" alt="The status drawer: nothing needs attention, and every service in the demo workspace, by application, with its latency">
|
|
75
|
+
<img src="docs/images/0.4.21/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">
|
|
76
76
|
</p>
|
|
77
77
|
|
|
78
78
|
## Get started
|
package/docs/distributions.md
CHANGED
|
@@ -1004,15 +1004,21 @@ brand:
|
|
|
1004
1004
|
- " └───────────┘"
|
|
1005
1005
|
```
|
|
1006
1006
|
|
|
1007
|
-
| Field | Used for
|
|
1008
|
-
| --------- |
|
|
1009
|
-
| `name` |
|
|
1010
|
-
| `tagline` | The line under the banner
|
|
1011
|
-
| `banner` | The CLI and web server startup banner
|
|
1007
|
+
| Field | Used for |
|
|
1008
|
+
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
1009
|
+
| `name` | The name people see: page titles and copy ("Sign in to Acme Den"), `--version`, doctor, the server agent's name, the banner fallback, the web route on the edge (`acme`) |
|
|
1010
|
+
| `tagline` | The line under the banner |
|
|
1011
|
+
| `banner` | The CLI and web server startup banner |
|
|
1012
1012
|
|
|
1013
1013
|
Runtime names (env vars, files, directories) always come from `den.yml`,
|
|
1014
1014
|
never from the brand.
|
|
1015
1015
|
|
|
1016
|
+
`brand.name` is used in pages and messages only when it is letters, digits,
|
|
1017
|
+
spaces, `.`, `_` and `-` (up to 40, starting with a letter or digit), so
|
|
1018
|
+
it can't break a page. Anything else (a quote, `&`, `<`) falls back to the
|
|
1019
|
+
product name with its first letter upper-cased ("Acme-den"); the page
|
|
1020
|
+
`<title>` still shows it.
|
|
1021
|
+
|
|
1016
1022
|
## Plugins
|
|
1017
1023
|
|
|
1018
1024
|
A plugin adds to Den without changing it: a theme, a page in the web UI, and
|
|
@@ -1613,7 +1619,10 @@ user, and the CLI does that for you:
|
|
|
1613
1619
|
|
|
1614
1620
|
- **The link** is `http://localhost:<port>/auth?c=<code>`, with `&next=<path>`
|
|
1615
1621
|
for where to land (`web login --next /settings`). The code works once,
|
|
1616
|
-
within
|
|
1622
|
+
within `web.login_link_ttl` (5 minutes unless set; 30s to 60m, as `90s`,
|
|
1623
|
+
`5m` or `1h`, in `config/local/user.yml` or the team config), and only
|
|
1624
|
+
this server's secret makes one. `web login` shows the time left while it
|
|
1625
|
+
waits, on a terminal. It is printed
|
|
1617
1626
|
only on the one line you asked for (`web login`, or `web up` when it
|
|
1618
1627
|
couldn't open the browser), never logged. Through the edge or a LAN name,
|
|
1619
1628
|
the cookie belongs to that name: `web login --host acme.acme.localhost`
|
|
@@ -1853,7 +1862,8 @@ the account's id and login, never a token, and no other account gets in.
|
|
|
1853
1862
|
last seen; "This device" on yours), each with **Revoke**, which ends its session at once (its
|
|
1854
1863
|
open terminals included). **Pair a device** makes a one-time link and a
|
|
1855
1864
|
QR code for an edge hostname you pick (`web login --host <name>` in the
|
|
1856
|
-
browser): scan it on the other device
|
|
1865
|
+
browser): scan it on the other device before it expires
|
|
1866
|
+
(`web.login_link_ttl`; the page counts down).
|
|
1857
1867
|
- **`web.github_client_id`** is the GitHub App's client id (public; no
|
|
1858
1868
|
secret). The default is Den's own App, **Monster Orange Den**
|
|
1859
1869
|
(`Iv23liKL12gnN6WvRV5b`), so Sign in with GitHub works out of the box. A
|
|
@@ -1868,6 +1878,61 @@ the account's id and login, never a token, and no other account gets in.
|
|
|
1868
1878
|
it never prints the token. `GET /api/web/owner` reports it as
|
|
1869
1879
|
`tokenExpiry: true`.
|
|
1870
1880
|
|
|
1881
|
+
### A company's Den: every developer connects GitHub from the browser
|
|
1882
|
+
|
|
1883
|
+
The common shape: a company makes its own Den repo (`create`), pushes it to
|
|
1884
|
+
its GitHub org, and every developer clones it and runs their own instance
|
|
1885
|
+
on their own machine. Everyone opens the same URL (an edge name such as
|
|
1886
|
+
`https://den.local.<your zone>`, whose DNS says 127.0.0.1) and connects
|
|
1887
|
+
their GitHub account there, with no terminal and nothing to explain.
|
|
1888
|
+
|
|
1889
|
+
```yaml
|
|
1890
|
+
# config/<name>.yml (the team's, committed): who may claim an instance
|
|
1891
|
+
web:
|
|
1892
|
+
allowed_github_org: acme
|
|
1893
|
+
```
|
|
1894
|
+
|
|
1895
|
+
- **`create` writes it**: `--github-org <org>`, else the GitHub owner of the
|
|
1896
|
+
repo the new folder is in. Neither: a commented line and a next step.
|
|
1897
|
+
- **`web.owner_github`** (`config/local/user.yml`, per machine) is the
|
|
1898
|
+
GitHub login of whoever runs this instance. `web up`, `web restart` and
|
|
1899
|
+
`service install` record it from `gh` (`gh api user`) when `gh` is signed
|
|
1900
|
+
in, no owner is linked and it isn't set, and print it. Only the login is
|
|
1901
|
+
kept. This is the path most people take: org memberships are private
|
|
1902
|
+
by default. When `gh` isn't signed in, `web up` and `doctor` say so,
|
|
1903
|
+
with the fix (`gh auth login`, then `web restart`; or set
|
|
1904
|
+
`web.owner_github` yourself).
|
|
1905
|
+
- **The signed-out page leads with Connect your GitHub account** while the
|
|
1906
|
+
instance has no owner and one of the two is set. Who may claim it:
|
|
1907
|
+
- **A member of `allowed_github_org`, on this computer.** **This is my
|
|
1908
|
+
computer** navigates to `http://localhost:<port>/attest` and straight
|
|
1909
|
+
back: only a browser on the machine Den runs on reaches it there. It
|
|
1910
|
+
returns with a short-lived (2 minutes), single-use code, signed with
|
|
1911
|
+
`web.secret`, bound to the edge hostname, in the URL fragment (never
|
|
1912
|
+
sent to a server or logged), and only to one of this Den's own edge
|
|
1913
|
+
hostnames. The page then starts Sign in with GitHub with it; when
|
|
1914
|
+
GitHub says the account is a member of the org, it becomes the owner
|
|
1915
|
+
and the browser is signed in. This exists because Docker Desktop,
|
|
1916
|
+
OrbStack and a TCP passthrough show the edge a gateway's address, so
|
|
1917
|
+
Den can't tell a browser on its own machine from another one.
|
|
1918
|
+
- **`owner_github`'s login, from any browser** (a devbox you reach from a
|
|
1919
|
+
laptop).
|
|
1920
|
+
- **Anyone else is refused, with the reason**: not a member of the org;
|
|
1921
|
+
a member, but not on this computer; not the login `owner_github` names.
|
|
1922
|
+
The proof alone never signs a browser in: any program on the machine
|
|
1923
|
+
can reach localhost, so the GitHub approval is always the person's.
|
|
1924
|
+
- **Membership.** Den asks GitHub with the sign-in's own token: the
|
|
1925
|
+
account's own membership (`GET /user/memberships/orgs/<org>`, which needs
|
|
1926
|
+
the App to have the org's Members (read) permission), then the public
|
|
1927
|
+
member list. A private membership the App can't read is refused, saying
|
|
1928
|
+
so: make the membership public, give the App that permission, or set
|
|
1929
|
+
`web.owner_github`.
|
|
1930
|
+
- **Neither set**: the page names both settings and offers no claim.
|
|
1931
|
+
`doctor` shows who may claim the instance, and fails (✗) when neither is
|
|
1932
|
+
set and the edge is reachable from other machines.
|
|
1933
|
+
- Once claimed, it is an ordinary owner: Sign in with GitHub from any
|
|
1934
|
+
browser, as above.
|
|
1935
|
+
|
|
1871
1936
|
### The GitHub App (once, by whoever ships the Den)
|
|
1872
1937
|
|
|
1873
1938
|
Den's default App is Monster Orange Den. A distribution that wants its own
|
|
@@ -1892,16 +1957,88 @@ Apps › New GitHub App):
|
|
|
1892
1957
|
|
|
1893
1958
|
## Running the web UI as a service (macOS)
|
|
1894
1959
|
|
|
1895
|
-
Den ships a launchd installer that runs the web UI
|
|
1896
|
-
|
|
1960
|
+
Den ships a launchd installer that runs the web UI and the session host (every
|
|
1961
|
+
terminal and agent) as launchd jobs, restarted if they exit:
|
|
1897
1962
|
|
|
1898
1963
|
```bash
|
|
1899
|
-
./bin/acme service install
|
|
1964
|
+
./bin/acme service install # install (or refresh) and start them
|
|
1965
|
+
./bin/acme service install --mode always # meant to keep them running across logout (opt-in, not yet proven)
|
|
1900
1966
|
./bin/acme service install --plan # what install would do; writes and loads nothing
|
|
1901
1967
|
./bin/acme service status # launchd's view: state, pid, last exit code
|
|
1902
|
-
./bin/acme service print # the
|
|
1903
|
-
./bin/acme service uninstall # stop
|
|
1904
|
-
```
|
|
1968
|
+
./bin/acme service print # the web plist it would install; installs nothing
|
|
1969
|
+
./bin/acme service uninstall # stop them and remove their plists
|
|
1970
|
+
```
|
|
1971
|
+
|
|
1972
|
+
### Login or always
|
|
1973
|
+
|
|
1974
|
+
| `--mode` | Jobs | At logout |
|
|
1975
|
+
| ----------------- | ----------------------------------------------------- | ------------------------------------ |
|
|
1976
|
+
| `login` (default) | LaunchAgents in the desktop session (`gui/<uid>`) | stop, with every agent |
|
|
1977
|
+
| `always` | LaunchDaemons that run as you (`UserName`), from boot | meant to keep running, and so agents |
|
|
1978
|
+
|
|
1979
|
+
`always` is opt-in and not yet proven across a logout: no logout has
|
|
1980
|
+
shown it keeping an agent on the same conversation yet. Use `login` unless
|
|
1981
|
+
you are testing it.
|
|
1982
|
+
|
|
1983
|
+
`always` takes your admin password once (sudo writes and loads the
|
|
1984
|
+
LaunchDaemons in `/Library/LaunchDaemons`); it is never asked for again.
|
|
1985
|
+
Without `--mode`, install keeps the mode already installed, so re-running it
|
|
1986
|
+
after an upgrade never changes it. Switching mode removes the other mode's
|
|
1987
|
+
jobs: never both. Use `always` on a machine you reach from elsewhere (ssh, a
|
|
1988
|
+
browser on another machine); `login` on a laptop.
|
|
1989
|
+
|
|
1990
|
+
A process outside the desktop session has no login keychain and no GUI. So
|
|
1991
|
+
under `always`, agents use:
|
|
1992
|
+
|
|
1993
|
+
- **1Password**: the service-account token (`secrets.onepassword.token`),
|
|
1994
|
+
not the desktop app. `doctor` warns when this process has none.
|
|
1995
|
+
- **Browsers**: headless Chromium and Firefox. WebKit on macOS needs a
|
|
1996
|
+
desktop session; run it from Playwright's Linux image instead
|
|
1997
|
+
(CONTRIBUTING.md, `DEN_PW_WS`).
|
|
1998
|
+
- **Protected folders** (other volumes, Desktop, Documents): Full Disk
|
|
1999
|
+
Access, granted once (below).
|
|
2000
|
+
|
|
2001
|
+
Each session records the launchd session its host ran in, and the Agents page
|
|
2002
|
+
tags it: **Stops at logout** (the desktop session; tap it for the fix) or
|
|
2003
|
+
**No GUI** (kept across logout). `web up` from an ssh shell (a Background
|
|
2004
|
+
session) says the same before it starts a server there.
|
|
2005
|
+
|
|
2006
|
+
### The session host job and Full Disk Access
|
|
2007
|
+
|
|
2008
|
+
Both modes install the session host as its own job, `<label>` with `.web`
|
|
2009
|
+
replaced by `.sessions` (`local.acme.sessions`), so it runs in the mode's
|
|
2010
|
+
session whoever starts the first terminal; `<cli> sessions` and agents then
|
|
2011
|
+
wait for it rather than starting a host in the caller's session. It runs a
|
|
2012
|
+
copy of node Den owns, `<stateDir>/bin/<cli>-sessions-host` (0700), and
|
|
2013
|
+
macOS grants Full Disk Access to that path: never to the shared node, which
|
|
2014
|
+
would give it to every node program. To grant it: System Settings → Privacy &
|
|
2015
|
+
Security → Full Disk Access → + → that path (⌘⇧G to type it), then
|
|
2016
|
+
`<cli> sessions host --stop` (it restarts with the grant). Agents inherit it.
|
|
2017
|
+
|
|
2018
|
+
Install refreshes the copy when node changes. An official node build
|
|
2019
|
+
(nodejs.org, nvm) is signed by the Node.js project, so the grant holds across
|
|
2020
|
+
upgrades; install says when the signer changed and it must be granted again.
|
|
2021
|
+
An ad-hoc signed node needs a new grant after every change, and Homebrew's
|
|
2022
|
+
node loads libraries beside it and can't be copied: install then runs it
|
|
2023
|
+
directly and says so. `doctor` shows whether the host has Full Disk Access,
|
|
2024
|
+
and the path to grant.
|
|
2025
|
+
|
|
2026
|
+
### What doctor shows
|
|
2027
|
+
|
|
2028
|
+
`doctor` ends with **where it runs**: the web server's and the session host's
|
|
2029
|
+
launchd session (a warning with the fix when it stops at logout), Full Disk
|
|
2030
|
+
Access, and the service: its mode, or `plist present, job not loaded:
|
|
2031
|
+
launchctl bootstrap <domain> <plist>` (`sudo` for a LaunchDaemon) when launchd
|
|
2032
|
+
has dropped a job whose plist is still there.
|
|
2033
|
+
|
|
2034
|
+
### Running means the job's own process
|
|
2035
|
+
|
|
2036
|
+
A plain `web up` server of the same install on the port would answer for the
|
|
2037
|
+
job, which then fails to start on the taken port and restarts forever. So
|
|
2038
|
+
install stops a plain server of this install first (and says so), and says
|
|
2039
|
+
`running` only when the server answering is the job's own process (its pid is
|
|
2040
|
+
launchd's). Otherwise it says `port <N> is served by pid <P>, not the job`
|
|
2041
|
+
and exits 1; `status` says the same.
|
|
1905
2042
|
|
|
1906
2043
|
The label and log directory come from the org config:
|
|
1907
2044
|
|
|
@@ -1946,12 +2083,14 @@ node_modules/@monster-orange/den/service/install.sh uninstall
|
|
|
1946
2083
|
node_modules/@monster-orange/den/service/install.sh print # the plist only
|
|
1947
2084
|
```
|
|
1948
2085
|
|
|
1949
|
-
| Variable
|
|
1950
|
-
|
|
|
1951
|
-
| `DEN_ROOT`
|
|
1952
|
-
| `DEN_SERVICE_LABEL`
|
|
1953
|
-
| `DEN_SERVICE_LOGS`
|
|
1954
|
-
| `DEN_SERVICE_NODE`
|
|
2086
|
+
| Variable | Default |
|
|
2087
|
+
| ---------------------- | ----------------------------------------------------------------------- |
|
|
2088
|
+
| `DEN_ROOT` | required in a distribution |
|
|
2089
|
+
| `DEN_SERVICE_LABEL` | `local.<distribution dir name>.web` |
|
|
2090
|
+
| `DEN_SERVICE_LOGS` | `~/Library/Logs/<distribution dir name>` |
|
|
2091
|
+
| `DEN_SERVICE_NODE` | see below |
|
|
2092
|
+
| `DEN_SERVICE_MODE` | `login` (or `always`) |
|
|
2093
|
+
| `DEN_SERVICE_HOST_EXE` | none: the host runs that node (`<cli> service install` sets Den's copy) |
|
|
1955
2094
|
|
|
1956
2095
|
The service runs the engine's server, from the distribution root, with the
|
|
1957
2096
|
`node` on your `PATH` at install time. A version manager's node (nvm, fnm,
|
|
@@ -2430,6 +2569,15 @@ server scoped to its workspace.
|
|
|
2430
2569
|
start and resume answers). 0.4.12 sent the name shown as `sessionName`;
|
|
2431
2570
|
read `sessionTitle` instead. Records are unchanged, so none written
|
|
2432
2571
|
before needs migrating.
|
|
2572
|
+
- **Renaming.** `agents rename <id> <name>` (or `--clear`, back to its
|
|
2573
|
+
id) names an agent; so do **Rename…** in its pane's ⋯ menu and **Rename**
|
|
2574
|
+
on a stopped agent's row, by touch or keyboard, as well as a double-click
|
|
2575
|
+
on its tab. The id never changes.
|
|
2576
|
+
- **The name other sessions see.** Den names a Claude Code agent's session
|
|
2577
|
+
`<cli>-<ws>:<name, else id>` (`--name`) at every start and resume, so
|
|
2578
|
+
another session listing its peers tells Den's agents apart. A rename
|
|
2579
|
+
shows at its next start or resume. The record's `peerName` is the name
|
|
2580
|
+
last given, so reading it back is never taken for a `/rename`.
|
|
2433
2581
|
- `list` shows agents only: sessions with an agent record. A session from
|
|
2434
2582
|
`sessions start` is not an agent; `sessions list` shows it.
|
|
2435
2583
|
- `stop` sends its session TERM and marks it stopped. `resume` works only
|