@kilogent/runner-dev 0.1.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/LICENSE +21 -0
- package/README.md +317 -0
- package/dist/cli.js +6867 -0
- package/package.json +64 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CodeBridger
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
# @kilogent/runner
|
|
2
|
+
|
|
3
|
+
The **runner daemon** for [Lumi Crew](https://crew.kilogent.com) — your agentic organization
|
|
4
|
+
workspace.
|
|
5
|
+
|
|
6
|
+
Crew agents don't run in the cloud. They run **on your machine**, in your checkouts, with your
|
|
7
|
+
tools. This daemon is what makes that happen: it watches the job queues of the Ships you assign it
|
|
8
|
+
to, claims work oldest-first, and executes each job as a headless Claude session.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm i -g @kilogent/runner
|
|
12
|
+
kilogent-runner setup
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Which package to install
|
|
16
|
+
|
|
17
|
+
There are **two**, one per environment. They install side by side — different command names,
|
|
18
|
+
different config directories, different service labels — so one machine can run both.
|
|
19
|
+
|
|
20
|
+
| Package | Command | Workspace |
|
|
21
|
+
| --- | --- | --- |
|
|
22
|
+
| `@kilogent/runner` | `kilogent-runner` | production |
|
|
23
|
+
| `@kilogent/runner-dev` | `kilogent-runner-dev` | development |
|
|
24
|
+
|
|
25
|
+
The Machines page in your workspace prints the one that matches it. **The package IS the channel**:
|
|
26
|
+
each publishes only a `latest` tag, and a machine changes environment by installing the other one.
|
|
27
|
+
|
|
28
|
+
## Requirements
|
|
29
|
+
|
|
30
|
+
| | |
|
|
31
|
+
|---|---|
|
|
32
|
+
| **Node.js** | 20 or newer |
|
|
33
|
+
| **Claude Code** | the `claude` CLI on your `PATH` ([install](https://claude.com/claude-code)) |
|
|
34
|
+
| **git / gh** | only if your agents work on GitHub repositories |
|
|
35
|
+
| **A Lumi Crew account** | with at least one Ship — [crew.kilogent.com](https://crew.kilogent.com) |
|
|
36
|
+
|
|
37
|
+
The daemon is a *client*. It signs in as you, and every write it makes is checked server-side
|
|
38
|
+
against your Ship membership. No service-account key ever touches your machine.
|
|
39
|
+
|
|
40
|
+
## Connect a machine
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
kilogent-runner setup
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
That's the whole thing. It opens your browser at Crew's `/connect` page, where you pick which Ships
|
|
47
|
+
this machine should serve and approve it in one click. The CLI is polling and collects its session,
|
|
48
|
+
runner id and Ship list from the approval — nothing is copied or pasted. If you approve as a
|
|
49
|
+
**captain**, the machine is authorized on your Ships immediately.
|
|
50
|
+
|
|
51
|
+
`setup` then runs `doctor` and offers to install the background service.
|
|
52
|
+
|
|
53
|
+
### A server or VPS
|
|
54
|
+
|
|
55
|
+
`setup` is interactive and refuses without a terminal, but it does not need a browser **on that
|
|
56
|
+
machine**:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
kilogent-runner setup --no-browser
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
It prints the approval link and an 8-character code. Open the link anywhere you do have a browser,
|
|
63
|
+
approve this Ship, and the server finishes on its own — nothing is pasted back.
|
|
64
|
+
|
|
65
|
+
For CI or unattended provisioning, where nobody is at a terminal at all, paste a Ship key from the
|
|
66
|
+
Daemons page instead — that page prints this command with the two values already filled in:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
kilogent-runner login --key <shipKey> --api-key <firebaseWebApiKey> --project <projectId>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
A key exists only once a captain has approved the machine on that Ship's Daemons page, and it only
|
|
73
|
+
appears in that list after one of the two flows above. So this is the **second** visit to a machine,
|
|
74
|
+
not the first.
|
|
75
|
+
|
|
76
|
+
### More than one account
|
|
77
|
+
|
|
78
|
+
One machine can serve Ships belonging to **different people**. Run `setup` again and choose *Add
|
|
79
|
+
Ships, from this or another account*; the second person approves in their own browser, with their
|
|
80
|
+
own sign-in.
|
|
81
|
+
|
|
82
|
+
Nothing is taken away by that. Approving only ever ADDS Ships, so the machine keeps every key and
|
|
83
|
+
every Ship it already had — the `/connect` page can only see the Ships of whoever is signed in to
|
|
84
|
+
it, so it is never allowed to decide that the others should go. To stop serving one, say so
|
|
85
|
+
explicitly: `kilogent-runner ship remove <shipId>`.
|
|
86
|
+
|
|
87
|
+
A machine ends up with one identity per account that approved it — `status` lists them — because a
|
|
88
|
+
runner id belongs to an account rather than to hardware. Only `setup`'s *Disconnect and start over*
|
|
89
|
+
removes anything, and it asks twice.
|
|
90
|
+
|
|
91
|
+
## Commands
|
|
92
|
+
|
|
93
|
+
| Command | What it does |
|
|
94
|
+
|---|---|
|
|
95
|
+
| `setup` | One-time onboarding: login → pick Ships → doctor → background service |
|
|
96
|
+
| `login` | Connect this machine (browser approval, `--no-browser` for a server, or `--key` for CI) |
|
|
97
|
+
| `ship add [ids…]` / `remove` / `list` | Which Ships this machine serves (omit ids to pick from a list) |
|
|
98
|
+
| `doctor` | **Preflight** — can this machine actually run a job? Non-zero exit if not |
|
|
99
|
+
| `service install` / `uninstall` / `restart` / `status` | Run the daemon in the background, always |
|
|
100
|
+
| `uninstall` | Remove the service, then tell you the `npm rm -g` step (`--purge` also clears config) |
|
|
101
|
+
| `start` | Run the daemon in the foreground |
|
|
102
|
+
| `status` | Ships, approval state, current job, queue depth, today's tokens |
|
|
103
|
+
| `logs [-f]` | The daemon's own rotating log file |
|
|
104
|
+
| `update [--check]` | Install the latest published version and restart (see **Staying current**) |
|
|
105
|
+
| `config list` / `set <key> <value>` | `notifications`, `keepAwake`, `autoUpdate`, `checkEvery`, `parallel` |
|
|
106
|
+
|
|
107
|
+
Global flags: `--json` (machine-readable stdout, and never prompts), `-y/--yes`, `--no-color`.
|
|
108
|
+
|
|
109
|
+
Without a TTY the CLI does not prompt at all — it fails naming the flag that would have answered the
|
|
110
|
+
question, because a wizard blocking on a hidden stdin inside a systemd unit is indistinguishable
|
|
111
|
+
from a hang.
|
|
112
|
+
|
|
113
|
+
### `doctor`
|
|
114
|
+
|
|
115
|
+
Run this first when something isn't working. Everything it checks used to be discovered *inside a
|
|
116
|
+
running job*, surfacing as a failed task on your board minutes later: a missing `claude` binary,
|
|
117
|
+
unsaved Ship credentials, a machine no captain approved. It checks Node ≥ 20, your stored session,
|
|
118
|
+
per-Ship approval and credentials, the engine binary, `git`/`gh` when an agent uses GitHub, server
|
|
119
|
+
reachability, the background service, whether that service still points at a CLI that exists, and
|
|
120
|
+
whether the **service's own `PATH`** can still reach the binaries your jobs spawn — which is a
|
|
121
|
+
different question from whether your shell can (see below).
|
|
122
|
+
|
|
123
|
+
### Background service
|
|
124
|
+
|
|
125
|
+
`kilogent-runner service install` writes a real OS service — **no root required**:
|
|
126
|
+
|
|
127
|
+
| OS | Mechanism | Note |
|
|
128
|
+
|---|---|---|
|
|
129
|
+
| macOS | `~/Library/LaunchAgents/com.kilogent.runner.plist` (`RunAtLoad` + `KeepAlive`) | |
|
|
130
|
+
| Linux | `~/.config/systemd/user/kilogent-runner.service` (`Restart=always`) | run `sudo loginctl enable-linger $USER` or it stops at logout |
|
|
131
|
+
| Windows | Task Scheduler, at logon | no auto-restart; use pm2 for a true always-on box |
|
|
132
|
+
|
|
133
|
+
Your `PATH` is captured into the unit at install time. launchd and systemd start processes with a
|
|
134
|
+
minimal environment, so without that `claude`, `git` and `gh` would not be found.
|
|
135
|
+
|
|
136
|
+
**It is captured once, and a restart does not refresh it.** `service restart` re-runs the unit
|
|
137
|
+
exactly as written, so a daemon keeps the `PATH` of whatever shell first installed it. Install a
|
|
138
|
+
tool somewhere new afterwards — `~/.local/bin`, a Homebrew prefix, an nvm switch — and the daemon
|
|
139
|
+
cannot see it, while every check you can run by hand (`which claude`, `kilogent-runner doctor`) is
|
|
140
|
+
answered by your *current* shell and looks fine. The symptom is a job that fails minutes later
|
|
141
|
+
with `spawn claude ENOENT`.
|
|
142
|
+
|
|
143
|
+
Two things close that gap: `doctor` reports it as **Service PATH**, and `kilogent-runner setup` now
|
|
144
|
+
reinstalls the service (rather than merely restarting it) when your environment has drifted from
|
|
145
|
+
the installed unit. To fix it directly, from a shell where the tool works:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
kilogent-runner service install # rewrites the unit with your current PATH
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## How many jobs at once
|
|
152
|
+
|
|
153
|
+
One, until you say otherwise — the same behaviour this daemon has always had.
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
kilogent-runner config set parallel 3 # this machine, across every Ship
|
|
157
|
+
kilogent-runner config set parallel 1 --ship shp_abc123 # cap one Ship's share of it
|
|
158
|
+
kilogent-runner config set parallel default --ship shp_abc123 # back to the machine number
|
|
159
|
+
kilogent-runner config list # what is set, and what each Ship gets
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The machine number is a ceiling, not a total: three Ships at `2` each on a machine set to `3` will
|
|
163
|
+
never run more than three sessions. A Ship with no number of its own may use the whole machine.
|
|
164
|
+
|
|
165
|
+
Each job is a separate `claude` process with its own empty working directory, so they don't share
|
|
166
|
+
state — but they do share your CPU, your RAM and your Claude usage window. Start at 2 and watch a
|
|
167
|
+
real job before going higher. Restart the daemon (`kilogent-runner service restart`) to pick up a change.
|
|
168
|
+
|
|
169
|
+
## MCP servers that run on this machine
|
|
170
|
+
|
|
171
|
+
A Ship can register MCP connections its agents reach. Most are remote — an `https://` address the
|
|
172
|
+
session talks to. Two kinds are **local**: a program this machine starts (`npx -y some-mcp-server`,
|
|
173
|
+
`node ./my-server.mjs`), and a server already listening on this machine's own loopback address
|
|
174
|
+
(`http://127.0.0.1:3000/mcp`).
|
|
175
|
+
|
|
176
|
+
**Those are off until you allow them, one Ship at a time.**
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
kilogent-runner config set local-mcp on --ship shp_abc123 # this Ship may run programs here
|
|
180
|
+
kilogent-runner config set local-mcp off --ship shp_abc123 # stop allowing it
|
|
181
|
+
kilogent-runner doctor # which Ships have local connections, and whether they may run
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
There is no machine-wide form on purpose. A captain registers these in a browser and may not be the
|
|
185
|
+
person who owns this computer — and if this daemon serves several Ships, a single switch would enrol
|
|
186
|
+
every one of them, including any added later. So it is granted per Ship, by you, here.
|
|
187
|
+
|
|
188
|
+
What a Ship gets when you allow it: the connections a captain registered **on that Ship**, activated
|
|
189
|
+
on the agent running the job, and nothing else. The daemon writes them into the session's config
|
|
190
|
+
file — your own `~/.claude.json` and any `.mcp.json` lying around are still invisible to every agent
|
|
191
|
+
session, exactly as before.
|
|
192
|
+
|
|
193
|
+
Credentials for these live on the Ship, not here: a captain marks a variable secret and it is stored
|
|
194
|
+
where nothing reads it back, then handed to the program at start and scrubbed from the transcript.
|
|
195
|
+
Press **Test** on the connection in Ship Settings and this machine runs the test and reports what it
|
|
196
|
+
found — the Crew backend cannot reach a program on your laptop, so it asks you instead. Restart the
|
|
197
|
+
daemon (`kilogent-runner service restart`) after changing this.
|
|
198
|
+
|
|
199
|
+
## Staying current
|
|
200
|
+
|
|
201
|
+
**This machine updates itself.** Every half hour the daemon asks npm whether there is a newer
|
|
202
|
+
version of the package it was installed from. When there is, it stops claiming new work, lets the
|
|
203
|
+
jobs it is already running **finish**, installs the new version, and exits — launchd and systemd
|
|
204
|
+
restart it, which is what picks up the new code. A captain sees all of it on the Daemons page.
|
|
205
|
+
|
|
206
|
+
It only ever does this when the background service is installed and running, because "exit and be
|
|
207
|
+
restarted" is the whole mechanism: a daemon you started by hand in a terminal tells you about the
|
|
208
|
+
update and leaves it to you. Windows is the same — the running file is locked there and the
|
|
209
|
+
scheduled task does not restart on exit — so `kilogent-runner update` prints the command instead.
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
kilogent-runner update --check # is there a newer version?
|
|
213
|
+
kilogent-runner update # install it now and restart the service
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
To pin a machine to a version:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
npm i -g @kilogent/runner@0.5.7
|
|
220
|
+
kilogent-runner config set autoUpdate off
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Other settings, all machine-wide:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
kilogent-runner config set checkEvery 360 # ask less often (minutes, 5–1440)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
A version is never installed twice: three failed attempts at the same one and the machine stops
|
|
230
|
+
trying until a different version is published. `kilogent-runner doctor` reports whether you are behind,
|
|
231
|
+
always as a warning — being out of date never fails the preflight.
|
|
232
|
+
|
|
233
|
+
## While a job runs
|
|
234
|
+
|
|
235
|
+
- **Idle sleep is inhibited** (`caffeinate` / `systemd-inhibit`, best-effort on Windows), so a
|
|
236
|
+
laptop doesn't suspend mid-session. It cannot veto you choosing Shut Down — no background process
|
|
237
|
+
gets that veto on macOS, and it shouldn't.
|
|
238
|
+
- **Desktop notifications are off by default.** They fire on job start, finish, terminal failure and
|
|
239
|
+
on a daemon stopped with work in flight — which on a busy machine is an interruption every few
|
|
240
|
+
minutes carrying nothing the task board and the Daemons live log do not already show. Turn them on
|
|
241
|
+
with `kilogent-runner config set notifications on`. (Sleep inhibition is a separate setting and stays
|
|
242
|
+
on: "don't close the lid" is `keepAwake`, not a notification.)
|
|
243
|
+
- **SIGTERM releases the job.** The daemon aborts every running session, hands each job back to the
|
|
244
|
+
queue with its retry budget **unspent**, and only then writes itself offline. Stopping the daemon
|
|
245
|
+
never costs you an attempt.
|
|
246
|
+
|
|
247
|
+
## Credentials
|
|
248
|
+
|
|
249
|
+
Agent-session credentials — the Claude token and, if your agents use GitHub, a token for that — are
|
|
250
|
+
**not** stored on this machine. A captain saves them once per Ship in the Crew app's Ship Settings,
|
|
251
|
+
and the daemon fetches them at job time, injects them into the session environment, and redacts them
|
|
252
|
+
from the transcript before upload. `kilogent-runner doctor` tells you when a Ship is missing them.
|
|
253
|
+
|
|
254
|
+
The only thing stored locally is your own session, in `~/.kilogent-runner/config.json` (mode `0600`).
|
|
255
|
+
|
|
256
|
+
## Uninstalling
|
|
257
|
+
|
|
258
|
+
Remove the service **before** the package:
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
kilogent-runner uninstall
|
|
262
|
+
npm rm -g @kilogent/runner
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Order matters, and getting it wrong is silent. The launchd plist / systemd unit records an
|
|
266
|
+
*absolute path* to the CLI inside `node_modules` — it has to, since the OS needs something to exec.
|
|
267
|
+
`npm rm -g` deletes that file but knows nothing about your service, so the service stays registered
|
|
268
|
+
and keeps respawning a binary that is no longer there, roughly every ten seconds, for as long as
|
|
269
|
+
the machine lives. npm cannot help here: since npm 7 it does not run `preuninstall`/`postuninstall`
|
|
270
|
+
scripts at all, so no package can clean up after itself.
|
|
271
|
+
|
|
272
|
+
Already removed the package? Reinstall it, uninstall properly, then remove it again:
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
npm i -g @kilogent/runner && kilogent-runner uninstall && npm rm -g @kilogent/runner
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
`kilogent-runner doctor` reports a machine that is already in this state, naming the missing path.
|
|
279
|
+
|
|
280
|
+
Neither command touches `~/.kilogent-runner`. That directory is this machine's **identity** — the
|
|
281
|
+
runner id each account knows it by, and its Ship keys — so uninstalling and reinstalling keeps the
|
|
282
|
+
approvals your captains already granted. Add `--purge` to delete it too, and expect to be approved
|
|
283
|
+
again from scratch, by every account.
|
|
284
|
+
|
|
285
|
+
## Upgrading from `crew-runner`
|
|
286
|
+
|
|
287
|
+
This package was previously distributed inside the Lumi monorepo as `@lumi/crew-runner`, with a
|
|
288
|
+
`crew-runner` command. If that's what you have:
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
crew-runner service uninstall
|
|
292
|
+
npm rm -g @lumi/crew-runner
|
|
293
|
+
npm i -g @kilogent/runner
|
|
294
|
+
kilogent-runner service install
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
You do **not** need to log in again. `~/.crew-runner` is migrated to `~/.kilogent-runner` automatically
|
|
298
|
+
on the first run, which keeps your machine's identity — and therefore the approvals your captain
|
|
299
|
+
already granted it. `service install` also removes the old `com.lumi.crew-runner` /
|
|
300
|
+
`crew-runner.service` unit if the uninstall above was skipped, so you never end up with two daemons
|
|
301
|
+
claiming from one queue.
|
|
302
|
+
|
|
303
|
+
## Environment variables
|
|
304
|
+
|
|
305
|
+
| Var | Effect |
|
|
306
|
+
|---|---|
|
|
307
|
+
| `LUMI_RUNNER_HOME` | Config + log directory (default `~/.kilogent-runner`) |
|
|
308
|
+
| `CREW_CLAUDE_BIN` | Path to the Claude binary (default: `claude` from `PATH`) |
|
|
309
|
+
| `CREW_NO_NOTIFY` | Force desktop notifications off, whatever the config says (they are off by default) |
|
|
310
|
+
| `CREW_NO_POWER` | Disable sleep inhibition |
|
|
311
|
+
| `LUMI_RUNNER_REGISTRY` | npm registry to check for updates (default `https://registry.npmjs.org`) |
|
|
312
|
+
|
|
313
|
+
`CREW_RUNNER_HOME` is still honoured as a fallback for machines configured before the rename.
|
|
314
|
+
|
|
315
|
+
## License
|
|
316
|
+
|
|
317
|
+
MIT — see [LICENSE](./LICENSE).
|