maesttro 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -42
- package/dist/maesttro.js +2106 -0
- package/docker/Dockerfile +47 -10
- package/docker/entrypoint.sh +18 -0
- package/docker/gitconfig +8 -0
- package/docker/sandbox-marker +1 -0
- package/package.json +11 -4
- package/bin/maesttro.js +0 -49
- 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
3
|
Isolated Docker sandbox for local coding agents — `claude`, `opencode`, `codex`,
|
|
4
|
-
and others
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
and others. Inside the sandbox, `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,23 +51,76 @@ 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 codex #
|
|
59
|
+
maesttro # from a repo root: new session clone + default client (claude)
|
|
60
|
+
maesttro opencode # same, with opencode
|
|
61
|
+
maesttro codex # same, with codex
|
|
59
62
|
maesttro bash # plain shell inside the sandbox, for debugging
|
|
63
|
+
maesttro sessions # list this repo's kept sessions
|
|
64
|
+
maesttro resume <id> # continue one
|
|
65
|
+
maesttro push [branch] # push a branch a session created (shows what goes out, asks first)
|
|
60
66
|
|
|
61
67
|
maesttro update # bump the package + rebuild image with refreshed configs
|
|
62
68
|
```
|
|
63
69
|
|
|
64
70
|
---
|
|
65
71
|
|
|
72
|
+
## Sessions: what an agent can and can't touch
|
|
73
|
+
|
|
74
|
+
Run `maesttro` from the root of a repository (git 2.48+ required). Each run is a
|
|
75
|
+
**session** with its own isolated clone, `.worktrees/maesttro-<id>`, detached at
|
|
76
|
+
`origin/<default branch>` and mounted at `/workspace`. That clone is the only
|
|
77
|
+
part of the repo the container sees:
|
|
78
|
+
|
|
79
|
+
- Your real `.git` is never mounted, and neither is your main checkout or any
|
|
80
|
+
uncommitted work there. The container works in a `git clone --shared` that
|
|
81
|
+
borrows the real object store read-only.
|
|
82
|
+
- The host never runs git inside the clone, so agent-authored git config, hooks
|
|
83
|
+
or attributes can't make your host git run anything.
|
|
84
|
+
- The agent commits on its own branch in the clone. It **cannot push**:
|
|
85
|
+
the sandbox's GitHub token is read-only for code.
|
|
86
|
+
- When a branch is ready, the agent asks you to run `maesttro push <branch>`
|
|
87
|
+
from the repo root. That command pushes only branches a session created,
|
|
88
|
+
never the default branch or branches that existed before. It shows you the
|
|
89
|
+
commits and diff first.
|
|
90
|
+
- To review a session's work, read it through the main repo
|
|
91
|
+
(`git -C <repo> log refs/maesttro/<id>/<branch>`) rather than `cd`-ing into
|
|
92
|
+
the clone, so your shell prompt or IDE doesn't run git on agent-authored config.
|
|
93
|
+
- Every launch backs up the repo's branches and tags into a single bundle file
|
|
94
|
+
under `~/.config/maesttro/backups/`, so any earlier state can be restored.
|
|
95
|
+
|
|
96
|
+
`maesttro push` is plain git, so it works the same for Azure DevOps, GitLab and
|
|
97
|
+
other hosts.
|
|
98
|
+
|
|
99
|
+
## GitHub access
|
|
100
|
+
|
|
101
|
+
Inside a session, `gh` works for **that repo only** (issues, pull requests,
|
|
102
|
+
comments). It acts as the Maesttro GitHub App's bot:
|
|
103
|
+
|
|
104
|
+
- `maesttro login` runs once. It's a GitHub device flow, and your token is kept in the OS keychain.
|
|
105
|
+
- 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.
|
|
106
|
+
- The token can read code and write issues and PRs, but can't push, merge or delete branches.
|
|
107
|
+
- Commits are authored by you (your repo's `user.name` / `user.email`) and committed by the bot.
|
|
108
|
+
- The broker is reached over the tailnet. Off the tailnet, the session still starts, just without `gh`.
|
|
109
|
+
|
|
110
|
+
## Your instructions come along
|
|
111
|
+
|
|
112
|
+
At every container start, `maesttro configure` copies your host
|
|
113
|
+
`~/.claude/CLAUDE.md`, `~/.config/opencode/AGENTS.md`, `~/.codex/AGENTS.md` and
|
|
114
|
+
`~/.claude/{agents,commands,skills}` into the sandbox, read-only at the
|
|
115
|
+
source, and adds a short section explaining the sandbox rules. Credentials and
|
|
116
|
+
settings files are never copied.
|
|
117
|
+
|
|
118
|
+
Upgrading from 0.1.x: the first launch removes the token you pasted before;
|
|
119
|
+
revoke that token on GitHub as well.
|
|
120
|
+
|
|
66
121
|
## Curated clients
|
|
67
122
|
|
|
68
|
-
Defined in `src/clients.
|
|
123
|
+
Defined in `src/clients.ts`, installed in `docker/Dockerfile`, configured via
|
|
69
124
|
`docker/configs/<client>/`:
|
|
70
125
|
|
|
71
126
|
| Key | Client | Package |
|
|
@@ -74,7 +129,7 @@ Defined in `src/clients.js`, installed in `docker/Dockerfile`, configured via
|
|
|
74
129
|
| `opencode` | opencode | `opencode-ai` |
|
|
75
130
|
| `codex` | OpenAI Codex CLI | `@openai/codex` |
|
|
76
131
|
|
|
77
|
-
Adding a new client: add an entry to `src/clients.
|
|
132
|
+
Adding a new client: add an entry to `src/clients.ts`, an install line in
|
|
78
133
|
`docker/Dockerfile`, and a `docker/configs/<key>/` directory with any default
|
|
79
134
|
config — then `maesttro update` rebuilds the image with it included.
|
|
80
135
|
|
|
@@ -82,27 +137,16 @@ config — then `maesttro update` rebuilds the image with it included.
|
|
|
82
137
|
|
|
83
138
|
## How `maesttro update` keeps configs current
|
|
84
139
|
|
|
85
|
-
`docker/configs/<client>/`
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
does two things, in order:
|
|
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
|
|
98
|
-
|
|
99
|
-
Because the Dockerfile path is resolved relative to the running script's own
|
|
100
|
-
install location, step 2 automatically picks up whatever step 1 just wrote —
|
|
101
|
-
no extra plumbing needed to keep the two in sync.
|
|
140
|
+
`docker/configs/<client>/` holds the curated client defaults. They aren't baked
|
|
141
|
+
into the image: at every container start, `maesttro configure` runs from your
|
|
142
|
+
installed package, which is mounted read-only, and merges them into each client's
|
|
143
|
+
config, keeping your own keys. So `maesttro update` has two steps:
|
|
144
|
+
1. `npm i -g maesttro@latest` refreshes the package, including `docker/configs/`,
|
|
145
|
+
which the next session picks up.
|
|
146
|
+
2. It rebuilds the sandbox image, for new client and tool versions.
|
|
102
147
|
|
|
103
|
-
Your
|
|
104
|
-
untouched by an update
|
|
105
|
-
gets refreshed.
|
|
148
|
+
Your session state (in the `maesttro-home` volume) and your project files are
|
|
149
|
+
untouched by an update.
|
|
106
150
|
|
|
107
151
|
---
|
|
108
152
|
|
|
@@ -110,8 +154,11 @@ gets refreshed.
|
|
|
110
154
|
|
|
111
155
|
| Mount | Source | Purpose |
|
|
112
156
|
|---|---|---|
|
|
113
|
-
| `/home/agent` | `maesttro-home` named volume |
|
|
114
|
-
| `/
|
|
157
|
+
| `/home/agent` | `maesttro-home` named volume | Per-client session state. Survives across runs, lives only in Docker. Holds no GitHub credential. |
|
|
158
|
+
| `/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. |
|
|
159
|
+
| `<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. |
|
|
160
|
+
| `/workspace` | the session clone (`.worktrees/<id>`) | The working directory: a `git clone --shared` detached at the default branch. |
|
|
161
|
+
| `/opt/maesttro`, `/opt/maesttro-host` (read-only) | this package; your instruction files | Used by `maesttro configure` at container start. |
|
|
115
162
|
| `/home/agent/.claude/skills` (read-only) | `~/agent-shared/skills` on host, if present | Reusable skills/house-style files shared across sessions. Read-only. |
|
|
116
163
|
|
|
117
164
|
Nothing from your personal `~/.ssh` or `~/.config/gh` is ever mounted.
|
|
@@ -123,22 +170,41 @@ Nothing from your personal `~/.ssh` or `~/.config/gh` is ever mounted.
|
|
|
123
170
|
```
|
|
124
171
|
maesttro/
|
|
125
172
|
├── package.json
|
|
126
|
-
├── bin/maesttro.js # entry point, dispatches subcommands
|
|
127
173
|
├── src/
|
|
128
|
-
│ ├──
|
|
129
|
-
│ ├──
|
|
130
|
-
│ ├──
|
|
131
|
-
│ ├──
|
|
132
|
-
│ ├──
|
|
133
|
-
│
|
|
174
|
+
│ ├── cli.ts # entry point, bundled to dist/maesttro.js
|
|
175
|
+
│ ├── main.ts # dispatches subcommands
|
|
176
|
+
│ ├── clients.ts # curated client registry
|
|
177
|
+
│ ├── setup.ts # `maesttro setup`
|
|
178
|
+
│ ├── update.ts # `maesttro update`
|
|
179
|
+
│ ├── doctor.ts # `maesttro doctor`
|
|
180
|
+
│ ├── launch.ts # `maesttro` / `maesttro <client>` / `maesttro list`
|
|
181
|
+
│ ├── paths.ts # locates bundled Dockerfile wherever npm installs the package
|
|
182
|
+
│ ├── account.ts, device-flow.ts, credential-store.ts, github-login.ts
|
|
183
|
+
│ │ # `maesttro login` / `logout` / `status`, keychain storage
|
|
184
|
+
│ └── github-session.ts, broker-client.ts, session-dir.ts, refresher.ts
|
|
185
|
+
│ # per-session GitHub token: mint, mount, refresh, revoke
|
|
186
|
+
├── broker/ # server-side token broker (not published; see libretto)
|
|
187
|
+
├── tests/
|
|
134
188
|
└── docker/
|
|
135
189
|
├── Dockerfile
|
|
190
|
+
├── gitconfig # → /etc/gitconfig: SSH→HTTPS, gh as credential helper
|
|
136
191
|
└── configs/
|
|
137
192
|
├── claude/
|
|
138
193
|
├── opencode/
|
|
139
194
|
└── codex/
|
|
140
195
|
```
|
|
141
196
|
|
|
197
|
+
## Trying it locally
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
bun run stagehand:sandbox
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
This starts a self-contained sandbox (Docker-in-Docker, the real broker, a fake
|
|
204
|
+
GitHub, a demo repo, a fake login) and drops you into a shell where `maesttro`
|
|
205
|
+
is this checkout's build. No credentials needed. See libretto
|
|
206
|
+
`Modules/stagehand.md` → "Local sandbox".
|
|
207
|
+
|
|
142
208
|
## Publishing
|
|
143
209
|
|
|
144
210
|
Released by CI, not by hand: bumping `version` in `package.json` and merging
|
|
@@ -150,6 +216,5 @@ registry it was installed from.
|
|
|
150
216
|
|
|
151
217
|
- Fill in real curated defaults in `docker/configs/*` (currently placeholders)
|
|
152
218
|
- 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)
|
|
219
|
+
- Decide whether each client gets its own volume, or shares one (current setup: shared)
|
|
155
220
|
- Homebrew tap as an alternative install path for non-Node users — optional
|