@notiamo/runner 0.1.1 → 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 +87 -6
- package/dist/cli.js +1342 -467
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,7 +10,9 @@ npx @notiamo/runner init
|
|
|
10
10
|
and a name for the runner. It checks that `claude --version` works, runs `claude auth login` into the runner's own config
|
|
11
11
|
folder, then installs the service and starts it. When it ends, the runner is running and starts again by itself.
|
|
12
12
|
|
|
13
|
-
Needs Node.js 22 or later and [Claude Code](https://docs.claude.com/en/docs/claude-code) installed on
|
|
13
|
+
Needs Node.js 22 or later and [Claude Code](https://docs.claude.com/en/docs/claude-code) 2.1.314 or newer installed on
|
|
14
|
+
the machine. On Linux and WSL2 the shell sandbox also needs `bubblewrap` and `socat` (Debian/Ubuntu:
|
|
15
|
+
`sudo apt install bubblewrap socat`); `init` checks for them. On macOS there is nothing more to install.
|
|
14
16
|
|
|
15
17
|
From a checkout of the Notiamo repository: `pnpm --filter @notiamo/runner build`, then
|
|
16
18
|
`node packages/runner/dist/cli.js init`.
|
|
@@ -30,6 +32,14 @@ The service is a launchd agent `com.notiamo.runner` on macOS, a Scheduled Task "
|
|
|
30
32
|
Windows, and a systemd user unit `notiamo-runner` on Linux. After `npx`, use `npx @notiamo/runner status` (or install
|
|
31
33
|
the package globally to have `notiamo-runner` on the path).
|
|
32
34
|
|
|
35
|
+
### Two runners on one machine (`--profile`)
|
|
36
|
+
|
|
37
|
+
`npx @notiamo/runner init --profile staging` sets up a second, separate runner: its own folder
|
|
38
|
+
(`~/.notiamo-runner/profiles/staging`: config, Claude sign-in, projects, log) and its own service
|
|
39
|
+
(`com.notiamo.runner.staging`, "Notiamo runner (staging)", `notiamo-runner-staging`). Give `--profile staging` to every
|
|
40
|
+
command of that runner: `status`, `logs`, `stop`, `uninstall`. A name has letters, digits and dashes (1 to 32). Without
|
|
41
|
+
`--profile`, everything is as before.
|
|
42
|
+
|
|
33
43
|
## What it is
|
|
34
44
|
|
|
35
45
|
A small program that waits for agent runs your organization queues in Notiamo, then does each run here: it clones
|
|
@@ -46,6 +56,69 @@ Notiamo's MCP server, sends Notiamo what the agent does, and reports when it end
|
|
|
46
56
|
claude.ai login or rate limits for their products." The runner only runs your own Claude Code, on your machine, for
|
|
47
57
|
your own work.
|
|
48
58
|
|
|
59
|
+
## What the agent can and cannot touch on this machine
|
|
60
|
+
|
|
61
|
+
Each run gets its own Claude Code settings file, and Claude Code runs with `--permission-mode acceptEdits`. Three
|
|
62
|
+
things do the work:
|
|
63
|
+
|
|
64
|
+
- **File tools stay in the run folder.** `permissions.blockReadsOutsideWorkingDirectories` and
|
|
65
|
+
`blockWritesOutsideWorkingDirectories` make Claude's own file tools (read, edit, write, search) refuse anything
|
|
66
|
+
outside the folder of the run. The permission mode accepts edits only inside that folder; any other call that would
|
|
67
|
+
ask for permission is refused (the runner runs Claude Code in print mode, where nobody can answer). Edits are also
|
|
68
|
+
denied by rule in the runner's own files, your credential and config folders (`~/.ssh`, `~/.aws`, `~/.config`,
|
|
69
|
+
`~/.npm`, `~/.local`, `~/.claude`, `~/Library` on macOS, …), your shell start files, and every folder holding a
|
|
70
|
+
program the runner itself runs (each folder on the `PATH`, the folders of `git`, `claude` and `node`, the runner's
|
|
71
|
+
own install).
|
|
72
|
+
- **Shell commands run in the OS sandbox.** On macOS (Seatbelt) and on Linux or WSL2 (bubblewrap and socat), every
|
|
73
|
+
command the agent runs in a shell is confined. If the sandbox cannot start, the run does not start.
|
|
74
|
+
- **No git config, credential helper, credential file or token of the machine reaches the agent.** Claude Code gets
|
|
75
|
+
only a short list of the runner's environment variables (`PATH`, `HOME`, `USER`, `LOGNAME`, `SHELL`, `LANG`,
|
|
76
|
+
`LC_ALL`, `LC_CTYPE`, `TERM`, `TMPDIR`, `TZ`, `XDG_*`, the proxy settings `HTTP_PROXY`/`HTTPS_PROXY`/`NO_PROXY`, and
|
|
77
|
+
`NODE_EXTRA_CA_CERTS`, `SSL_CERT_FILE`, `SSL_CERT_DIR`) plus the run's own: never `SSH_AUTH_SOCK`, `GH_TOKEN`,
|
|
78
|
+
`GITHUB_TOKEN`, `NPM_TOKEN`, `AWS_*` or `ANTHROPIC_*`. The agent's git sees no global or system git config
|
|
79
|
+
(`GIT_CONFIG_GLOBAL=/dev/null`, `GIT_CONFIG_NOSYSTEM=1`) and commits as "Notiamo agent". Commands that name
|
|
80
|
+
`credential`, `osxkeychain`, `libsecret`, `security find-`, `secret-tool`, `gh auth` or `ssh-add` are denied.
|
|
81
|
+
- **The keychain service itself is reachable from the sandbox on macOS.** The programs that read it are also denied
|
|
82
|
+
to the shell as defense in depth: git's credential helpers (`git-credential-*` in `git --exec-path`),
|
|
83
|
+
`/usr/bin/security`, `secret-tool` and `gnome-keyring` on Linux, and `gh`. That is not a wall: a script the agent
|
|
84
|
+
writes can still start system programs such as `/usr/bin/security`. This is why the runner needs no credential of
|
|
85
|
+
the machine: keep no GitHub login in this user's keychain (`notiamo-runner init` warns when it finds one), or run
|
|
86
|
+
the runner as a dedicated macOS user with its own keychain, holding nothing but what the runner needs.
|
|
87
|
+
|
|
88
|
+
What the shell sandbox allows and denies:
|
|
89
|
+
|
|
90
|
+
- **Reads denied:** the other projects' folders (other runs at the same time included), the runner's own files
|
|
91
|
+
(`config.json` with the runner token, `state.json`, `runs/`, `claude/`, `git/`, the log) and your credential files
|
|
92
|
+
and folders that exist: `~/.ssh`, `~/.aws`, `~/.gnupg`, `~/.netrc`, `~/.docker`, `~/.config/gh`,
|
|
93
|
+
`~/.git-credentials`, `~/.config/git`, `~/.npmrc`, `~/.pypirc`, `~/.config/gcloud`, `~/.config/hub`.
|
|
94
|
+
- **Writes allowed:** only in the run folder. The run's `.git/hooks` and `.git/config` are read-only.
|
|
95
|
+
- **Network allowed:** GitHub, npm, Notiamo, the WordPress site (for a WordPress run) and any extra hosts you add on
|
|
96
|
+
the Runners card in Notiamo. Everything else is refused.
|
|
97
|
+
|
|
98
|
+
The agent never pushes and holds no credentials. The runner pushes the run branch itself after the agent's work.
|
|
99
|
+
|
|
100
|
+
When a run ends, the runner kills every process left in its process group (a dev server or a watcher the agent
|
|
101
|
+
started). A process that detached itself with `setsid` has left that group and can outlive the run.
|
|
102
|
+
|
|
103
|
+
**Windows.** Plain Windows has no shell sandbox: only the file-tool rules above apply, and `init` says so. For the
|
|
104
|
+
full sandbox, run the runner inside WSL2.
|
|
105
|
+
|
|
106
|
+
## Several runs at once
|
|
107
|
+
|
|
108
|
+
On the Runners card in Notiamo you set how many runs this runner does at the same time (1 to 3). Notiamo hands the
|
|
109
|
+
number to the runner when it connects. Each run uses your Claude plan, so keep it to what the plan has room for. One
|
|
110
|
+
project never runs twice at once: a second run on the same project waits for the first (and the runner refuses one
|
|
111
|
+
that arrives while the first still runs here).
|
|
112
|
+
|
|
113
|
+
Each run at the same time has its own slot folder, `projects/0`, `projects/1` or `projects/2`, made when the runner
|
|
114
|
+
starts: a run's sandbox cannot read the other slots at all, even a project cloned there after the run began.
|
|
115
|
+
|
|
116
|
+
## A runner for one client
|
|
117
|
+
|
|
118
|
+
When you make a runner in Notiamo, you can limit it to one client (workspace). It then takes only that client's runs.
|
|
119
|
+
To set it up on the client's PC: install Claude Code there, run `npx @notiamo/runner init`, and paste the runner's
|
|
120
|
+
token. The Claude login on that PC is the one that pays for the runs, so sign in with the account that should pay.
|
|
121
|
+
|
|
49
122
|
## WordPress sites
|
|
50
123
|
|
|
51
124
|
The connection is on the project's Website access card in Notiamo: an application password, and optionally SSH or
|
|
@@ -81,17 +154,25 @@ Everything lives in `~/.notiamo-runner` (or the folder in `NOTIAMO_RUNNER_HOME`)
|
|
|
81
154
|
| --- | --- |
|
|
82
155
|
| `config.json` | Notiamo's address, the runner's name and the runner token (`ntr_…`), nothing else. Readable by you only (mode 0600). |
|
|
83
156
|
| `claude/` | Claude Code's config folder for the runner (`CLAUDE_CONFIG_DIR`): its sign-in and sessions. |
|
|
84
|
-
| `projects
|
|
157
|
+
| `projects/<slot>/<project>/` | The working copies of your projects, one folder per project in each slot (`0`, `1`, `2`: one per run at the same time). A WordPress run works in a temporary folder inside it, removed after the run. A project not used for the number of days set on the Runners card is removed; the next run clones it again. |
|
|
158
|
+
| `git/` | The runner's own copies of your repositories: `<project>.git` (a bare mirror, the only copy that talks to GitHub; removed with the project's last working copy), `<project>.<slot>.worktree.json` (what the working copy's `.git` looked like after the runner's own git, checked before the runner uses it again) and `<project>.last-run` (its time is the project's last run). The agent can neither read nor write this folder. |
|
|
159
|
+
| `runs/<id>.json` | The Claude Code settings of a run in progress (permission rules, the sandbox's folders and hosts; no secret). Deleted when the run ends. |
|
|
85
160
|
| `runner.log` | What the runner did (no secrets), rotated at 10 MB. |
|
|
86
|
-
| `state.json` | The
|
|
161
|
+
| `state.json` | The ids of the runs in progress. After a restart, each is reported to Notiamo as failed (`runner_restarted`). |
|
|
87
162
|
|
|
88
|
-
The secrets of a run (WordPress passwords, the run's MCP token) come from Notiamo with the run and
|
|
163
|
+
The secrets of a run (WordPress passwords, the run's MCP token, its GitHub token) come from Notiamo with the run and
|
|
89
164
|
live only in memory: the agent gets them as environment variables (the MCP config on its command line only names them),
|
|
90
165
|
never in a file. The two MCP tokens are `NOTIAMO_RUN_TOKEN` and `NOTIAMO_WP_TOKEN`. What the agent prints is scrubbed
|
|
91
166
|
of them before the transcript is uploaded.
|
|
92
167
|
|
|
93
|
-
The
|
|
94
|
-
|
|
168
|
+
The agent has no git credentials at all, and the runner never uses the machine's git credentials: its own git runs
|
|
169
|
+
with no user or system git config (`GIT_CONFIG_GLOBAL=/dev/null`, `GIT_CONFIG_NOSYSTEM=1`), so no credential helper or
|
|
170
|
+
keychain of the machine is asked. Instead each run gets from Notiamo a one-hour GitHub App token for the run's
|
|
171
|
+
repository only. The runner keeps it in memory and uses it for its own fetch and push, handed to git through its
|
|
172
|
+
environment (never on a command line, never in a file); close to the hour's end it asks Notiamo for a fresh one. The
|
|
173
|
+
runner clones only from `https://github.com/…` and pushes the run branch itself after the agent's work. It refuses to
|
|
174
|
+
push if the run changed the clone's `.git` folder. Notiamo opens and merges the pull request through its GitHub App.
|
|
175
|
+
Keep no GitHub login in this user's keychain: the runner does not need it, and a run could reach it.
|
|
95
176
|
|
|
96
177
|
The SSH host key of a WordPress server is not verified yet.
|
|
97
178
|
|