maesttro 0.1.2 → 0.2.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 +127 -51
- package/dist/maesttro.js +2431 -0
- package/docker/Dockerfile +49 -13
- package/docker/configs/opencode/config.json +1 -1
- package/docker/entrypoint.sh +18 -0
- package/docker/gitconfig +8 -0
- package/docker/runner-marker +1 -0
- package/package.json +12 -5
- package/bin/maesttro.js +0 -49
- package/docker/configs/claude/settings.json +0 -3
- package/docker/configs/codex/config.json +0 -3
- package/src/clients.js +0 -26
- package/src/doctor.js +0 -78
- package/src/launch.js +0 -68
- package/src/paths.js +0 -12
- package/src/setup.js +0 -101
- package/src/update.js +0 -22
package/README.md
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# maesttro
|
|
2
2
|
|
|
3
|
-
Isolated Docker
|
|
4
|
-
and others
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
Isolated Docker runner for local coding agents — `opencode`, `claude`,
|
|
4
|
+
and others. Inside the runner, `gh` and `git` act as the Maesttro GitHub App's
|
|
5
|
+
bot, using a token that is scoped to the current repo and lasts at most an hour.
|
|
6
|
+
Your personal `~/.ssh` and `~/.config/gh` are never used. Install once, then
|
|
7
|
+
`maesttro` just works from any project.
|
|
7
8
|
|
|
8
9
|
Companion piece to Maesttro's orchestration side: this is the local, single-
|
|
9
10
|
machine entry point — a curated, versioned way to run any supported client
|
|
@@ -39,9 +40,10 @@ resolves against the same registry:
|
|
|
39
40
|
npm i -g @esatto/maesttro
|
|
40
41
|
```
|
|
41
42
|
|
|
42
|
-
**From source** (the package lives in the `stagehand/` directory of the Maesttro monorepo
|
|
43
|
+
**From source** (the package lives in the `stagehand/` directory of the Maesttro monorepo;
|
|
44
|
+
the CLI is TypeScript bundled to `dist/` with Bun):
|
|
43
45
|
```bash
|
|
44
|
-
npm i -g
|
|
46
|
+
cd stagehand && bun run build && npm i -g .
|
|
45
47
|
```
|
|
46
48
|
|
|
47
49
|
---
|
|
@@ -49,60 +51,116 @@ npm i -g ./stagehand
|
|
|
49
51
|
## Usage
|
|
50
52
|
|
|
51
53
|
```bash
|
|
52
|
-
maesttro setup
|
|
54
|
+
maesttro setup # one-time: build image, create volume, log in to GitHub
|
|
55
|
+
maesttro login / logout / status # manage the GitHub login
|
|
53
56
|
maesttro list # see available clients
|
|
54
57
|
maesttro doctor # health check anytime
|
|
55
58
|
|
|
56
|
-
maesttro #
|
|
57
|
-
maesttro opencode #
|
|
58
|
-
maesttro
|
|
59
|
-
maesttro
|
|
59
|
+
maesttro # from a repo root: new session clone + default client (opencode)
|
|
60
|
+
maesttro opencode # same, with opencode
|
|
61
|
+
maesttro bash # plain shell inside the runner, for debugging
|
|
62
|
+
maesttro sessions # list this repo's kept sessions
|
|
63
|
+
maesttro resume <id> # continue one
|
|
64
|
+
maesttro push [branch] # push a branch a session created (shows what goes out, asks first)
|
|
60
65
|
|
|
61
66
|
maesttro update # bump the package + rebuild image with refreshed configs
|
|
62
67
|
```
|
|
63
68
|
|
|
64
69
|
---
|
|
65
70
|
|
|
71
|
+
## Sessions: what an agent can and can't touch
|
|
72
|
+
|
|
73
|
+
Run `maesttro` from the root of a repository (git 2.48+ required). Each run is a
|
|
74
|
+
**session** with its own isolated clone, `.worktrees/maesttro-<id>`, detached at
|
|
75
|
+
`origin/<default branch>` and mounted at `/workspace`. That clone is the only
|
|
76
|
+
part of the repo the container sees:
|
|
77
|
+
|
|
78
|
+
- Your real `.git` is never mounted, and neither is your main checkout or any
|
|
79
|
+
uncommitted work there. The container works in a `git clone --shared` that
|
|
80
|
+
borrows the real object store read-only.
|
|
81
|
+
- The host never runs git inside the clone, so agent-authored git config, hooks
|
|
82
|
+
or attributes can't make your host git run anything.
|
|
83
|
+
- The agent commits on its own branch in the clone. It **cannot push**:
|
|
84
|
+
the runner's GitHub token is read-only for code.
|
|
85
|
+
- When a branch is ready, the agent asks you to run `maesttro push <branch>`
|
|
86
|
+
from the repo root. That command pushes only branches a session created,
|
|
87
|
+
never the default branch or branches that existed before. It shows you the
|
|
88
|
+
commits and diff first.
|
|
89
|
+
- To review a session's work, read it through the main repo
|
|
90
|
+
(`git -C <repo> log refs/maesttro/<id>/<branch>`) rather than `cd`-ing into
|
|
91
|
+
the clone, so your shell prompt or IDE doesn't run git on agent-authored config.
|
|
92
|
+
- Every launch backs up the repo's branches and tags into a single bundle file
|
|
93
|
+
under `~/.config/maesttro/backups/`, so any earlier state can be restored.
|
|
94
|
+
|
|
95
|
+
`maesttro push` is plain git, so it works the same for Azure DevOps, GitLab and
|
|
96
|
+
other hosts.
|
|
97
|
+
|
|
98
|
+
## GitHub access
|
|
99
|
+
|
|
100
|
+
Inside a session, `gh` works for **that repo only** (issues, pull requests,
|
|
101
|
+
comments). It acts as the Maesttro GitHub App's bot:
|
|
102
|
+
|
|
103
|
+
- `maesttro login` runs once. It's a GitHub device flow, and your token is kept in the OS keychain.
|
|
104
|
+
- Each launch asks the Maesttro broker for a token that is scoped to the repo and lasts at most an hour. The CLI refreshes it quietly and revokes it when you exit.
|
|
105
|
+
- The token can read code and write issues and PRs, but can't push, merge or delete branches.
|
|
106
|
+
- Commits are authored by you (your repo's `user.name` / `user.email`) and committed by the bot.
|
|
107
|
+
- The broker is reached over the tailnet. Off the tailnet, the session still starts, just without `gh`.
|
|
108
|
+
|
|
109
|
+
## Your instructions come along
|
|
110
|
+
|
|
111
|
+
At every container start, `maesttro configure` copies your host
|
|
112
|
+
`~/.claude/CLAUDE.md`, `~/.config/opencode/AGENTS.md` and
|
|
113
|
+
`~/.claude/{agents,commands,skills}` into the runner, read-only at the
|
|
114
|
+
source, and adds a short section explaining the runner rules. Credentials and
|
|
115
|
+
settings files are never copied.
|
|
116
|
+
|
|
117
|
+
Upgrading from 0.1.x: the first launch removes the token you pasted before;
|
|
118
|
+
revoke that token on GitHub as well.
|
|
119
|
+
|
|
66
120
|
## Curated clients
|
|
67
121
|
|
|
68
|
-
Defined in `src/clients.
|
|
122
|
+
Defined in `src/clients.ts`, installed in `docker/Dockerfile`, configured via
|
|
69
123
|
`docker/configs/<client>/`:
|
|
70
124
|
|
|
71
125
|
| Key | Client | Package |
|
|
72
126
|
|---|---|---|
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
75
|
-
| `codex` | OpenAI Codex CLI | `@openai/codex` |
|
|
127
|
+
| `opencode` (default) | opencode | `opencode-ai` |
|
|
128
|
+
| `claude` | Claude Code | `@anthropic-ai/claude-code` |
|
|
76
129
|
|
|
77
|
-
Adding a new client: add an entry to `src/clients.
|
|
130
|
+
Adding a new client: add an entry to `src/clients.ts`, an install line in
|
|
78
131
|
`docker/Dockerfile`, and a `docker/configs/<key>/` directory with any default
|
|
79
|
-
config — then `maesttro update` rebuilds the image with it included.
|
|
132
|
+
config — then `maesttro update` rebuilds the runner image with it included.
|
|
80
133
|
|
|
81
|
-
|
|
134
|
+
### Maesttro provider
|
|
82
135
|
|
|
83
|
-
|
|
136
|
+
Every launch builds opencode's config on your machine: your own
|
|
137
|
+
`~/.config/opencode/opencode.json(c)`, with secrets removed, and Maesttro's
|
|
138
|
+
config on top (`$MAESTTRO_URL`, default `http://100.64.0.2`). Only the result
|
|
139
|
+
is mounted into the runner, read-only, and copied into place at start. Your
|
|
140
|
+
raw config never enters the container, and your host's config is never written.
|
|
84
141
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
142
|
+
Injected: a `maesttro` provider (Orchestrate) at `/v1`, a `dgx-spark` provider
|
|
143
|
+
(lorem direct on the DGX), compaction, default model `maesttro/maesttro`, and
|
|
144
|
+
thinking blocks shown. The two providers replace yours of the same name; the
|
|
145
|
+
rest (`mcp`, `plugin`, other providers) is merged. Removed secrets are listed
|
|
146
|
+
at launch by path. `{env:X}` / `$X` references are kept.
|
|
88
147
|
|
|
89
|
-
|
|
90
|
-
maesttro update
|
|
91
|
-
```
|
|
148
|
+
Claude Code gets no Maesttro config and uses its own defaults and login.
|
|
92
149
|
|
|
93
|
-
|
|
94
|
-
1. `npm i -g maesttro@latest` — refreshes the installed package files
|
|
95
|
-
(including the bundled `docker/` directory) in place
|
|
96
|
-
2. Rebuilds the sandbox image from that same, now-refreshed, `docker/`
|
|
97
|
-
directory
|
|
150
|
+
---
|
|
98
151
|
|
|
99
|
-
|
|
100
|
-
install location, step 2 automatically picks up whatever step 1 just wrote —
|
|
101
|
-
no extra plumbing needed to keep the two in sync.
|
|
152
|
+
## How `maesttro update` keeps configs current
|
|
102
153
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
154
|
+
`docker/configs/<client>/` holds the curated client defaults. They aren't baked
|
|
155
|
+
into the runner image: at every container start, `maesttro configure` runs from your
|
|
156
|
+
installed package, which is mounted read-only, and merges them into each client's
|
|
157
|
+
config, keeping your own keys. So `maesttro update` has two steps:
|
|
158
|
+
1. `npm i -g maesttro@latest` refreshes the package, including `docker/configs/`,
|
|
159
|
+
which the next session picks up.
|
|
160
|
+
2. It rebuilds the runner image, for new client and tool versions.
|
|
161
|
+
|
|
162
|
+
Your session state (in the `maesttro-home` volume) and your project files are
|
|
163
|
+
untouched by an update.
|
|
106
164
|
|
|
107
165
|
---
|
|
108
166
|
|
|
@@ -110,8 +168,11 @@ gets refreshed.
|
|
|
110
168
|
|
|
111
169
|
| Mount | Source | Purpose |
|
|
112
170
|
|---|---|---|
|
|
113
|
-
| `/home/agent` | `maesttro-home` named volume |
|
|
114
|
-
| `/
|
|
171
|
+
| `/home/agent` | `maesttro-home` named volume | Per-client session state. Survives across runs, lives only in Docker. Holds no GitHub credential. |
|
|
172
|
+
| `/home/agent/.config/gh-maesttro` (read-only) | per-session temp dir on host | The session's GitHub token (`hosts.yml`). The CLI refreshes it before its 1 h expiry and revokes it on exit. |
|
|
173
|
+
| `<repo>/.git/objects` (read-only, same absolute path) | your repo's object store | Base history, borrowed by the clone through its `alternates`. The real `.git` is never mounted. |
|
|
174
|
+
| `/workspace` | the session clone (`.worktrees/<id>`) | The working directory: a `git clone --shared` detached at the default branch. |
|
|
175
|
+
| `/opt/maesttro`, `/opt/maesttro-host` (read-only) | this package; your instruction files | Used by `maesttro configure` at container start. |
|
|
115
176
|
| `/home/agent/.claude/skills` (read-only) | `~/agent-shared/skills` on host, if present | Reusable skills/house-style files shared across sessions. Read-only. |
|
|
116
177
|
|
|
117
178
|
Nothing from your personal `~/.ssh` or `~/.config/gh` is ever mounted.
|
|
@@ -123,22 +184,39 @@ Nothing from your personal `~/.ssh` or `~/.config/gh` is ever mounted.
|
|
|
123
184
|
```
|
|
124
185
|
maesttro/
|
|
125
186
|
├── package.json
|
|
126
|
-
├── bin/maesttro.js # entry point, dispatches subcommands
|
|
127
187
|
├── src/
|
|
128
|
-
│ ├──
|
|
129
|
-
│ ├──
|
|
130
|
-
│ ├──
|
|
131
|
-
│ ├──
|
|
132
|
-
│ ├──
|
|
133
|
-
│
|
|
188
|
+
│ ├── cli.ts # entry point, bundled to dist/maesttro.js
|
|
189
|
+
│ ├── main.ts # dispatches subcommands
|
|
190
|
+
│ ├── clients.ts # curated client registry
|
|
191
|
+
│ ├── setup.ts # `maesttro setup`
|
|
192
|
+
│ ├── update.ts # `maesttro update`
|
|
193
|
+
│ ├── doctor.ts # `maesttro doctor`
|
|
194
|
+
│ ├── launch.ts # `maesttro` / `maesttro <client>` / `maesttro list`
|
|
195
|
+
│ ├── paths.ts # locates bundled Dockerfile wherever npm installs the package
|
|
196
|
+
│ ├── account.ts, device-flow.ts, credential-store.ts, github-login.ts
|
|
197
|
+
│ │ # `maesttro login` / `logout` / `status`, keychain storage
|
|
198
|
+
│ └── github-session.ts, broker-client.ts, session-dir.ts, refresher.ts
|
|
199
|
+
│ # per-session GitHub token: mint, mount, refresh, revoke
|
|
200
|
+
├── broker/ # server-side token broker (not published; see libretto)
|
|
201
|
+
├── tests/
|
|
134
202
|
└── docker/
|
|
135
203
|
├── Dockerfile
|
|
204
|
+
├── gitconfig # → /etc/gitconfig: SSH→HTTPS, gh as credential helper
|
|
136
205
|
└── configs/
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
206
|
+
└── opencode/
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Trying it locally
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
bun run stagehand:sandbox
|
|
140
213
|
```
|
|
141
214
|
|
|
215
|
+
This starts a self-contained sandbox (Docker-in-Docker, the real broker, a fake
|
|
216
|
+
GitHub, a demo repo, a fake login) and drops you into a shell where `maesttro`
|
|
217
|
+
is this checkout's build. No credentials needed. See libretto
|
|
218
|
+
`Modules/stagehand.md` → "Local sandbox".
|
|
219
|
+
|
|
142
220
|
## Publishing
|
|
143
221
|
|
|
144
222
|
Released by CI, not by hand: bumping `version` in `package.json` and merging
|
|
@@ -148,8 +226,6 @@ registry it was installed from.
|
|
|
148
226
|
|
|
149
227
|
## Open items
|
|
150
228
|
|
|
151
|
-
- Fill in real curated defaults in `docker/configs/*` (currently placeholders)
|
|
152
229
|
- Pin exact client CLI versions in the Dockerfile once stable
|
|
153
|
-
- Decide whether each client gets its own
|
|
154
|
-
trail, or shares one (current setup: shared)
|
|
230
|
+
- Decide whether each client gets its own volume, or shares one (current setup: shared)
|
|
155
231
|
- Homebrew tap as an alternative install path for non-Node users — optional
|