@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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +317 -0
  3. package/dist/cli.js +6867 -0
  4. 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).