@kilogent/runner 0.1.7 → 0.1.9

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 CHANGED
@@ -1,368 +1,169 @@
1
1
  # @kilogent/runner
2
2
 
3
- The **runner daemon** for [Lumi Crew](https://app.kilogent.com) — your agentic organization
4
- workspace.
3
+ **Kilogent's internal server runner.** It runs the agent jobs of [Kilogent](https://app.kilogent.com)
4
+ workspaces on Kilogent's own servers.
5
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 agent session on the engine the
9
- agent runs on.
6
+ > **This is not something a customer installs.** A workspace never has machines: it asks for work,
7
+ > and Kilogent decides which of its servers runs it (PRD §15.89). There is no customer enrolment, no
8
+ > approval and no per-workspace setup. If you are a Kilogent customer, there is nothing here for you
9
+ > to do.
10
10
 
11
- ```bash
12
- npm i -g @kilogent/runner
13
- kilogent-runner setup
14
- ```
15
-
16
- ## Which package to install
17
-
18
- There are **two**, one per environment. They install side by side — different command names,
19
- different config directories, different service labels — so one machine can run both.
20
-
21
- | Package | Command | Workspace |
22
- | --- | --- | --- |
23
- | `@kilogent/runner` | `kilogent-runner` | production |
24
- | `@kilogent/runner-dev` | `kilogent-runner-dev` | development |
25
-
26
- The Machines page in your workspace prints the one that matches it. **The package IS the channel**:
27
- each publishes only a `latest` tag, and a machine changes environment by installing the other one.
28
-
29
- ## Requirements
30
-
31
- | | |
32
- |---|---|
33
- | **Node.js** | 20 or newer |
34
- | **An engine CLI** | fetched for you — the daemon installs what your crews need (a few hundred MB each). Bring your own with `CREW_<ENGINE>_BIN`, or decline the lot with `config set autoInstallEngines off`. See [Engines](#engines). |
35
- | **git / gh** | only if your agents work on GitHub repositories |
36
- | **A Lumi Crew account** | with at least one Ship — [app.kilogent.com](https://app.kilogent.com) |
37
-
38
- The daemon is a *client*. It signs in as you, and every write it makes is checked server-side
39
- against your Ship membership. No service-account key ever touches your machine.
40
-
41
- ## Engines
42
-
43
- An agent runs on an **engine** — the CLI that actually holds the session. The daemon spawns the
44
- engine's binary per job, and `doctor` checks that every engine a Ship's agents use is installed.
45
-
46
- **You do not have to install them yourself.** The daemon fetches the CLIs its crews need into its
47
- own folder (`~/.kilogent-runner/tools/<engine>/`) and runs agents on that copy. A CLI you installed
48
- yourself is never changed or removed, and the two stay independent: upgrade yours whenever you like,
49
- the crew keeps running on the version the fleet knows works.
50
-
51
- ⚠️ **They are large.** The Codex CLI is about 284 MB installed and Claude Code about 202 MB. Each is
52
- replaced rather than added to on an upgrade, so a machine running both settles around half a
53
- gigabyte. To decline the whole thing and manage them yourself:
54
-
55
- ```
56
- kilogent-runner config set autoInstallEngines off
57
- ```
58
-
59
- Which version a machine installs comes from the fleet, not from this machine: it takes the version
60
- known to work, moves to a newer release only after that release has been out for a day, and never
61
- installs one another machine has proved broken. If a new version breaks a job here, this daemon puts
62
- the working version back, runs the job again, and — only if that succeeds — tells the fleet, so no
63
- other machine installs it.
64
-
65
- ```
66
- kilogent-runner engine list what each engine needs, what this machine has, and where it came from
67
- kilogent-runner engine install [engine...] install now (--check, --dry-run, --force)
68
- kilogent-runner engine uninstall <engine> remove this runner's copy, never yours
69
- ```
70
-
71
- | Engine | Binary | Credential |
72
- |---|---|---|
73
- | Claude Code | `claude` ([install](https://claude.com/claude-code)) | a `claude setup-token`, pasted once per Ship in the Crew app |
74
- | Codex CLI | `codex` ([install](https://developers.openai.com/codex)) | a **machine sign-in**: a captain picks this machine in the Crew app, the daemon runs `codex login --device-auth`, and the captain approves the code on a phone. The login stays on this machine — see [Credentials](#credentials). |
75
-
76
- Each engine's binary can be overridden with `CREW_<ENGINE>_BIN` (`CREW_CLAUDE_BIN`), which wins over
77
- the copy the daemon installed and is the supported way to pin a machine to a particular binary. It
78
- is also how the test suites substitute a deterministic fake.
79
-
80
- ## Connect a machine
11
+ A server is set up by the command the admin console prints for it (see [Adding a server](#adding-a-server)).
12
+ On a Linux server, as root:
81
13
 
82
14
  ```bash
83
- kilogent-runner setup
15
+ apt-get update
16
+ apt-get install -y --no-install-recommends nodejs curl ca-certificates
17
+ curl -fsSLo kg.tgz https://registry.npmjs.org/@kilogent/runner/-/runner-<version>.tgz
18
+ echo "<sha512 from the console> kg.tgz" | sha512sum -c
19
+ tar xzf kg.tgz && node package/dist/provision-root.mjs --project <projectId> --api-key <webApiKey>
84
20
  ```
85
21
 
86
- That's the whole thing. It opens your browser at Crew's `/connect` page, where you pick which Ships
87
- this machine should serve and approve it in one click. The CLI is polling and collects its session,
88
- runner id and Ship list from the approval — nothing is copied or pasted. If you approve as a
89
- **captain**, the machine is authorized on your Ships immediately.
90
-
91
- `setup` then runs `doctor` and offers to install the background service.
92
-
93
- ### A server or VPS
22
+ ## How it works
94
23
 
95
- `setup` is interactive and refuses without a terminal, but it does not need a browser **on that
96
- machine**:
97
-
98
- ```bash
99
- kilogent-runner setup --no-browser
100
24
  ```
101
-
102
- It prints the approval link and an 8-character code. Open the link anywhere you do have a browser,
103
- approve this Ship, and the server finishes on its own nothing is pasted back.
104
-
105
- For CI or unattended provisioning, where nobody is at a terminal at all, paste a Ship key from the
106
- Daemons page instead — that page prints this command with the two values already filled in:
107
-
108
- ```bash
109
- kilogent-runner login --key <shipKey> --api-key <firebaseWebApiKey> --project <projectId>
25
+ key file ──exchangeFleetKey──▶ the server's own session (no workspace in it)
26
+ heartbeat every 30 s ──▶ slots, memory budget, pinned version, which leases are still ours
27
+ wake-up signal / a freed slot / every 15 s ──▶ leaseJobs
28
+ each lease ──▶ its own Firebase app, signed in with a ONE-JOB PASS ──▶ the agent session ──▶ app deleted
110
29
  ```
111
30
 
112
- A key exists only once a captain has approved the machine on that Ship's Daemons page, and it only
113
- appears in that list after one of the two flows above. So this is the **second** visit to a machine,
114
- not the first.
115
-
116
- ### More than one account
31
+ - **The backend places, the server leases.** The scheduler puts each queued job on one server. A
32
+ server can only lease the jobs placed on itself, and each lease carries a one-job pass.
33
+ - **The pass is the whole authority for a job.** Progress, transcript, finalize, notes, the credit
34
+ meter, the AI gateway key, GitHub, MCP connections and the Workspace MCP bearer all use it. It can
35
+ write only its own job, its own task or chat, and its own agent's status.
36
+ - **A job goes back to the queue only through the backend** (`returnLease`): a retry, a release, a
37
+ usage limit, or a shutdown.
38
+ - **It stops itself before the backend gives up on it.** With no accepted heartbeat for
39
+ `staleServerMs − 30 s` (60 s by default), or when a heartbeat is refused, it aborts every session and
40
+ writes nothing more for them. The backend requeues or fails those jobs by its own rules.
117
41
 
118
- One machine can serve Ships belonging to **different people**. Run `setup` again and choose *Add
119
- Ships, from this or another account*; the second person approves in their own browser, with their
120
- own sign-in.
42
+ ## Which package
121
43
 
122
- Nothing is taken away by that. Approving only ever ADDS Ships, so the machine keeps every key and
123
- every Ship it already had the `/connect` page can only see the Ships of whoever is signed in to
124
- it, so it is never allowed to decide that the others should go. To stop serving one, say so
125
- explicitly: `kilogent-runner ship remove <shipId>`.
126
-
127
- A machine ends up with one identity per account that approved it — `status` lists them — because a
128
- runner id belongs to an account rather than to hardware. Only `setup`'s *Disconnect and start over*
129
- removes anything, and it asks twice.
44
+ | Package | Command | Project |
45
+ | --- | --- | --- |
46
+ | `@kilogent/runner` | `kilogent-runner` | production (`kilogent-crew-prod`) |
47
+ | `@kilogent/runner-dev` | `kilogent-runner-dev` | development (`lumi-afb7d`) |
48
+
49
+ They install side by side with different command names, config directories and service labels.
50
+
51
+ ## Adding a server
52
+
53
+ 1. In the **admin console → Fleet**, press **Add server**. It shows a one-time **enrolment code** (valid for 30
54
+ minutes) and the one command for a Linux server or the dev Mac.
55
+ 2. Run that command on the machine and paste the code when asked.
56
+ - **Linux, as root:** it downloads the pinned release, checks its sha512, and runs
57
+ `node package/dist/provision-root.mjs`. That creates the service user, installs Node, Docker, gVisor and
58
+ the session firewall, installs the runner in the user's own prefix, enrols, and starts the service.
59
+ `--plan` shows what it would do and changes nothing.
60
+ - **The dev Mac, as the runner user:** `npx --yes @kilogent/runner-dev@<version> provision`. Before it, install
61
+ Node 22 and Docker Desktop, open Docker Desktop once, and set it to start when you sign in. The command
62
+ installs the runner into `~/.local`, enrols and installs the LaunchAgent; the image is pulled by the server
63
+ itself. The Mac warnings on the Fleet page (FileVault and automatic login, an administrator account, cloud
64
+ logins in the account) are advice for a dedicated machine and do not stop work.
65
+ 3. The server makes its own key; only its hash leaves the machine. If it enrolled from an address the server
66
+ does not list, **Approve** it in Fleet with the last four characters of the key the server printed.
67
+
68
+ `kilogent-runner doctor` and the Fleet page show the same list of what is not ready yet, each with its fix.
69
+ **Retry sandbox** in the console makes the server check again.
70
+
71
+ **A lost, damaged or rotated key** is replaced the same way, never by hand: press **New enrolment code** on the
72
+ server's row and run the command it prints. That command ends in `--re-enrol`, so a machine that already has a
73
+ key enrols again and replaces it. The old key keeps working until then. To cut it off sooner, disable the server,
74
+ and enable it again just before you run the command: a code used while the server is disabled is refused.
75
+
76
+ **Disabling a server** in the console revokes its sessions and recovers its jobs. The daemon stops its
77
+ sessions, keeps retrying the exchange with backoff, and takes work again once it is enabled.
130
78
 
131
79
  ## Commands
132
80
 
133
81
  | Command | What it does |
134
82
  |---|---|
135
- | `setup` | One-time onboarding: login pick Ships doctor background service |
136
- | `login` | Connect this machine (browser approval, `--no-browser` for a server, or `--key` for CI) |
137
- | `ship add [ids…]` / `remove` / `list` | Which Ships this machine serves (omit ids to pick from a list) |
138
- | `doctor` | **Preflight** can this machine actually run a job? Non-zero exit if not |
139
- | `service install` / `uninstall` / `restart` / `status` | Run the daemon in the background, always |
140
- | `uninstall` | Remove the service, then tell you the `npm rm -g` step (`--purge` also clears config) |
141
- | `start` | Run the daemon in the foreground |
142
- | `status` | Ships, approval state, current job, queue depth, today's tokens |
143
- | `logs [-f]` | The daemon's own rotating log file |
144
- | `update [--check]` | Install the latest published version and restart (see **Staying current**) |
145
- | `config list` / `set <key> <value>` | `notifications`, `keepAwake`, `autoUpdate`, `checkEvery`, `parallel` |
146
-
147
- Global flags: `--json` (machine-readable stdout, and never prompts), `-y/--yes`, `--no-color`.
148
-
149
- Without a TTY the CLI does not prompt at all — it fails naming the flag that would have answered the
150
- question, because a wizard blocking on a hidden stdin inside a systemd unit is indistinguishable
151
- from a hang.
152
-
153
- ### `doctor`
154
-
155
- Run this first when something isn't working. Everything it checks used to be discovered *inside a
156
- running job*, surfacing as a failed task on your board minutes later: a missing engine binary,
157
- unsaved Ship credentials, a machine no captain approved. It checks Node 20, your stored session,
158
- per-Ship approval and credentials, the engine binary, `git`/`gh` when an agent uses GitHub, server
159
- reachability, the background service, whether that service still points at a CLI that exists, and
160
- whether the **service's own `PATH`** can still reach the binaries your jobs spawn which is a
161
- different question from whether your shell can (see below).
162
-
163
- ### Background service
164
-
165
- `kilogent-runner service install` writes a real OS service **no root required**:
166
-
167
- | OS | Mechanism | Note |
168
- |---|---|---|
169
- | macOS | `~/Library/LaunchAgents/com.kilogent.runner.plist` (`RunAtLoad` + `KeepAlive`) | |
170
- | Linux | `~/.config/systemd/user/kilogent-runner.service` (`Restart=always`) | run `sudo loginctl enable-linger $USER` or it stops at logout |
171
- | Windows | Task Scheduler, at logon | no auto-restart; use pm2 for a true always-on box |
172
-
173
- Your `PATH` is captured into the unit at install time. launchd and systemd start processes with a
174
- minimal environment, so without that `claude`, `git` and `gh` would not be found.
175
-
176
- **It is captured once, and a restart does not refresh it.** `service restart` re-runs the unit
177
- exactly as written, so a daemon keeps the `PATH` of whatever shell first installed it. Install a
178
- tool somewhere new afterwards — `~/.local/bin`, a Homebrew prefix, an nvm switch — and the daemon
179
- cannot see it, while every check you can run by hand (`which claude`, `kilogent-runner doctor`) is
180
- answered by your *current* shell and looks fine. The symptom is a job that fails minutes later
181
- with `spawn claude ENOENT`.
182
-
183
- Two things close that gap: `doctor` reports it as **Service PATH**, and `kilogent-runner setup` now
184
- reinstalls the service (rather than merely restarting it) when your environment has drifted from
185
- the installed unit. To fix it directly, from a shell where the tool works:
186
-
187
- ```bash
188
- kilogent-runner service install # rewrites the unit with your current PATH
189
- ```
190
-
191
- ## How many jobs at once
192
-
193
- One, until you say otherwise — the same behaviour this daemon has always had.
194
-
195
- ```bash
196
- kilogent-runner config set parallel 3 # this machine, across every Ship
197
- kilogent-runner config set parallel 1 --ship shp_abc123 # cap one Ship's share of it
198
- kilogent-runner config set parallel default --ship shp_abc123 # back to the machine number
199
- kilogent-runner config list # what is set, and what each Ship gets
200
- ```
201
-
202
- The machine number is a ceiling, not a total: three Ships at `2` each on a machine set to `3` will
203
- never run more than three sessions. A Ship with no number of its own may use the whole machine.
204
-
205
- Each job is a separate engine process with its own empty working directory, so they don't share
206
- state — but they do share your CPU, your RAM and your subscription's usage window. Start at 2 and watch a
207
- real job before going higher. Restart the daemon (`kilogent-runner service restart`) to pick up a change.
208
-
209
- ## MCP servers that run on this machine
210
-
211
- A Ship can register MCP connections its agents reach. Most are remote — an `https://` address the
212
- session talks to. Two kinds are **local**: a program this machine starts (`npx -y some-mcp-server`,
213
- `node ./my-server.mjs`), and a server already listening on this machine's own loopback address
214
- (`http://127.0.0.1:3000/mcp`).
83
+ | `provision --project <id> --api-key <key> [--code-stdin] [--re-enrol]` | Set up the dev Mac as this user. A Linux server uses `dist/provision-root.mjs` as root. `--re-enrol` replaces an existing key with the code. |
84
+ | `enrol --project <id> --api-key <key> [--replace]` | Enrol with a one-time code read from stdin; the server makes its own key. `--replace` gives a server that has a key a new one, and puts the old one back if the code is refused. |
85
+ | `connect --key-file <path> --project <id> [--api-key <key>]` | Make this machine a Kilogent server from an existing key file. |
86
+ | `doctor [--fix]` | The readiness list with each fix, and the key exchange, engines and service. Non-zero exit only for what stops work. `--fix` repairs the key file's mode. |
87
+ | `start` | Run the daemon in the foreground. |
88
+ | `service install` / `uninstall` / `restart` / `status` | Run the daemon as an OS service. `restart`, and `install` over a running service, let running jobs finish first; `--now` does not wait. |
89
+ | `status` | What the daemon last wrote about itself (`<configDir>/fleet-state.json`): phase, heartbeat, slots, running jobs, engines, pin. |
90
+ | `logs [-n N] [-f]` | The daemon's rotating log file. |
91
+ | `engine list` / `install` / `uninstall` | The engine CLIs sessions run on. |
92
+ | `update [--check] [--dry-run]` | Install the latest published version by hand. |
93
+ | `config list` / `set <key> on\|off` | `notifications`, `keepAwake`, `autoInstallEngines`. |
94
+ | `uninstall [--purge]` | Remove the service, then print the `npm rm -g` step. `--purge` also deletes the config directory — and the key file only if it lives inside it, which the confirmation says. |
95
+
96
+ Global flags: `--json`, `-y/--yes`, `--no-color`.
97
+
98
+ Everything that decides what a server does how many jobs it runs, its memory budget, draining,
99
+ which version the fleet runs, which workspaces are rolled out — is set in the **admin console**, not
100
+ here, and reaches the server with its heartbeat.
101
+
102
+ ## The sandbox
103
+
104
+ Every Kilogent server runs every workspace's jobs, so each session runs in a container from the image its
105
+ release names, by digest, pulled with a short-lived token the server asks for over its own fleet session. A
106
+ server takes no work until it has pulled that image and a probe container has proved a session can run.
107
+
108
+ On Linux the runner's user cannot reach Docker at all: every session goes through a root-owned launcher, under
109
+ gVisor, on a network that cannot reach the host, the cloud metadata service or private addresses. The dev Mac
110
+ runs sessions with Docker Desktop and claims no such boundary.
111
+
112
+ Where the console turns it on (§15.92), a session starts at a small memory limit and the runner raises it while
113
+ the session runs, up to a max set in the console: the root launcher does it on Linux, the daemon itself on the
114
+ Mac. The heartbeat reports the memory this server has for sessions and what its sessions really use, and the
115
+ backend places work by those numbers. A session stopped at its max fails its job with a sentence saying so; one
116
+ stopped below its max goes back to the queue and runs again from its max.
117
+
118
+ A session runs directly on the machine only in a local emulator stack (both `FIRESTORE_EMULATOR_HOST` and
119
+ `FIREBASE_AUTH_EMULATOR_HOST` set).
215
120
 
216
- **Those are off until you allow them, one Ship at a time.**
217
-
218
- ```bash
219
- kilogent-runner config set local-mcp on --ship shp_abc123 # this Ship may run programs here
220
- kilogent-runner config set local-mcp off --ship shp_abc123 # stop allowing it
221
- kilogent-runner doctor # which Ships have local connections, and whether they may run
222
- ```
223
-
224
- There is no machine-wide form on purpose. A captain registers these in a browser and may not be the
225
- person who owns this computer — and if this daemon serves several Ships, a single switch would enrol
226
- every one of them, including any added later. So it is granted per Ship, by you, here.
227
-
228
- What a Ship gets when you allow it: the connections a captain registered **on that Ship**, activated
229
- on the agent running the job, and nothing else. The daemon writes them into the session's config
230
- file — your own `~/.claude.json` and any `.mcp.json` lying around are still invisible to every agent
231
- session, exactly as before.
121
+ ## Engines
232
122
 
233
- Credentials for these live on the Ship, not here: a captain marks a variable secret and it is stored
234
- where nothing reads it back, then handed to the program at start and scrubbed from the transcript.
235
- Press **Test** on the connection in Ship Settings and this machine runs the test and reports what it
236
- found the Crew backend cannot reach a program on your laptop, so it asks you instead. Restart the
237
- daemon (`kilogent-runner service restart`) after changing this.
123
+ The daemon installs every engine it has an installer for when it starts, reports each engine's state
124
+ on every heartbeat, and installs one on demand when a lease needs it. A binary given by
125
+ `CREW_<ENGINE>_BIN` is the operator's: it is reported as `unmanaged` and never installed over. Deep
126
+ Agents has no installer; point `CREW_DEEPAGENTS_BIN` at it.
238
127
 
239
- ## Staying current
128
+ Which version is installed comes from `crewConfig/engines`. If a new version breaks a job, the
129
+ daemon puts the known-good version back, runs the job again, and — only if that works — reports the
130
+ version so no other server installs it.
240
131
 
241
- **This machine updates itself.** Every half hour the daemon asks npm whether there is a newer
242
- version of the package it was installed from. When there is, it stops claiming new work, lets the
243
- jobs it is already running **finish**, installs the new version, and exits — launchd and systemd
244
- restart it, which is what picks up the new code. A captain sees all of it on the Daemons page.
132
+ ## The pinned version
245
133
 
246
- It only ever does this when the background service is installed and running, because "exit and be
247
- restarted" is the whole mechanism: a daemon you started by hand in a terminal tells you about the
248
- update and leaves it to you. Windows is the same the running file is locked there and the
249
- scheduled task does not restart on exit so `kilogent-runner update` prints the command instead.
134
+ The console pins one exact runner version for the fleet, and records npm's integrity for it. A server whose
135
+ version differs checks that npm still gives the pinned bytes, pulls and probes the next release's image while
136
+ it keeps working, and only then drains (finishes its jobs, takes no new ones), installs **that exact version**
137
+ and exits so the service restarts it. The pin reaches a few servers at a time, so the fleet never stops
138
+ together. When a server cannot move, the reason is shown in the console.
250
139
 
251
- ```bash
252
- kilogent-runner update --check # is there a newer version?
253
- kilogent-runner update # install it now and restart the service
254
- ```
140
+ ## Shutting down
255
141
 
256
- To pin a machine to a version:
257
-
258
- ```bash
259
- npm i -g @kilogent/runner@0.5.7
260
- kilogent-runner config set autoUpdate off
261
- ```
262
-
263
- Other settings, all machine-wide:
264
-
265
- ```bash
266
- kilogent-runner config set checkEvery 360 # ask less often (minutes, 5–1440)
267
- ```
268
-
269
- A version is never installed twice: three failed attempts at the same one and the machine stops
270
- trying until a different version is published. `kilogent-runner doctor` reports whether you are behind,
271
- always as a warning — being out of date never fails the preflight.
272
-
273
- ## While a job runs
274
-
275
- - **Idle sleep is inhibited** (`caffeinate` / `systemd-inhibit`, best-effort on Windows), so a
276
- laptop doesn't suspend mid-session. It cannot veto you choosing Shut Down — no background process
277
- gets that veto on macOS, and it shouldn't.
278
- - **Desktop notifications are off by default.** They fire on job start, finish, terminal failure and
279
- on a daemon stopped with work in flight — which on a busy machine is an interruption every few
280
- minutes carrying nothing the task board and the Daemons live log do not already show. Turn them on
281
- with `kilogent-runner config set notifications on`. (Sleep inhibition is a separate setting and stays
282
- on: "don't close the lid" is `keepAwake`, not a notification.)
283
- - **SIGTERM releases the job.** The daemon aborts every running session, hands each job back to the
284
- queue with its retry budget **unspent**, and only then writes itself offline. Stopping the daemon
285
- never costs you an attempt.
286
-
287
- ## Credentials
288
-
289
- Agent-session credentials — a pasted engine token and, if your agents use GitHub, a token for that —
290
- are **not** stored on this machine. A captain saves them once per Ship in the Crew app's Ship
291
- Settings, and the daemon fetches them at job time, injects them into the session environment, and
292
- redacts them from the transcript before upload. `kilogent-runner doctor` tells you when a Ship is
293
- missing them.
294
-
295
- **Machine sign-ins are the exception, on purpose.** An engine whose account has no static token
296
- (Codex on a ChatGPT plan: its login rotates a refresh token on every use, so a copy on a second
297
- machine kills the first) is signed in ON ONE MACHINE. The daemon keeps that login in a private
298
- directory, `~/.kilogent-runner/engines/<engine>/<ship>/<credential>/`, refreshes it itself, and
299
- never sends it anywhere. Jobs on that credential run on this machine only; the daemon runs one
300
- session per sign-in at a time. Deleting the credential in the app signs this machine out and removes
301
- the directory; so does removing the machine from the Ship, and `kilogent-runner uninstall --purge`.
302
- `doctor` reports each sign-in this machine holds and whether the engine still accepts it.
303
-
304
- The only other thing stored locally is your own session, in `~/.kilogent-runner/config.json`
305
- (mode `0600`).
142
+ SIGTERM aborts every session and hands each job back with its retry unused, then sends a last
143
+ heartbeat that takes the server out of placement at once. Whatever did not get back within the grace
144
+ period, the backend recovers.
306
145
 
307
146
  ## Uninstalling
308
147
 
309
- Remove the service **before** the package:
148
+ Remove the service **before** the package, or the service keeps restarting a binary that no longer
149
+ exists:
310
150
 
311
151
  ```bash
312
152
  kilogent-runner uninstall
313
153
  npm rm -g @kilogent/runner
314
154
  ```
315
155
 
316
- Order matters, and getting it wrong is silent. The launchd plist / systemd unit records an
317
- *absolute path* to the CLI inside `node_modules` — it has to, since the OS needs something to exec.
318
- `npm rm -g` deletes that file but knows nothing about your service, so the service stays registered
319
- and keeps respawning a binary that is no longer there, roughly every ten seconds, for as long as
320
- the machine lives. npm cannot help here: since npm 7 it does not run `preuninstall`/`postuninstall`
321
- scripts at all, so no package can clean up after itself.
322
-
323
- Already removed the package? Reinstall it, uninstall properly, then remove it again:
324
-
325
- ```bash
326
- npm i -g @kilogent/runner && kilogent-runner uninstall && npm rm -g @kilogent/runner
327
- ```
328
-
329
- `kilogent-runner doctor` reports a machine that is already in this state, naming the missing path.
330
-
331
- Neither command touches `~/.kilogent-runner`. That directory is this machine's **identity** — the
332
- runner id each account knows it by, and its Ship keys — so uninstalling and reinstalling keeps the
333
- approvals your captains already granted. Add `--purge` to delete it too, and expect to be approved
334
- again from scratch, by every account.
335
-
336
- ## Upgrading from `crew-runner`
337
-
338
- This package was previously distributed inside the Lumi monorepo as `@lumi/crew-runner`, with a
339
- `crew-runner` command. If that's what you have:
340
-
341
- ```bash
342
- crew-runner service uninstall
343
- npm rm -g @lumi/crew-runner
344
- npm i -g @kilogent/runner
345
- kilogent-runner service install
346
- ```
347
-
348
- You do **not** need to log in again. `~/.crew-runner` is migrated to `~/.kilogent-runner` automatically
349
- on the first run, which keeps your machine's identity — and therefore the approvals your captain
350
- already granted it. `service install` also removes the old `com.lumi.crew-runner` /
351
- `crew-runner.service` unit if the uninstall above was skipped, so you never end up with two daemons
352
- claiming from one queue.
353
-
354
156
  ## Environment variables
355
157
 
356
158
  | Var | Effect |
357
159
  |---|---|
358
- | `LUMI_RUNNER_HOME` | Config + log directory (default `~/.kilogent-runner`) |
359
- | `CREW_<ENGINE>_BIN` | Path to an engine's binary, e.g. `CREW_CLAUDE_BIN`, `CREW_CODEX_BIN` (default: the copy this runner installed, else the engine's own name from `PATH`) |
360
- | `CREW_MACHINE_LOGIN_TIMEOUT_MS` | How long a machine sign-in may wait for approval before it is reported expired (default: 16 minutes; a test seam) |
361
- | `CREW_NO_NOTIFY` | Force desktop notifications off, whatever the config says (they are off by default) |
362
- | `CREW_NO_POWER` | Disable sleep inhibition |
363
- | `LUMI_RUNNER_REGISTRY` | npm registry to check for updates (default `https://registry.npmjs.org`) |
364
-
365
- `CREW_RUNNER_HOME` is still honoured as a fallback for machines configured before the rename.
160
+ | `CREW_SANDBOX_IMAGE` | A locally built session image, read **only in a local emulator stack**. A server runs its release's image. |
161
+ | `CREW_SANDBOX_RUNTIME` / `CREW_SANDBOX_NETWORK` / `CREW_SANDBOX_USER` | Container runtime, network and user when Docker is used directly (the dev Mac, a local stack). |
162
+ | `CREW_<ENGINE>_BIN` | An engine's binary, e.g. `CREW_CLAUDE_BIN`, `CREW_DEEPAGENTS_BIN`. |
163
+ | `LUMI_RUNNER_HOME` | Config and log directory (default `~/.kilogent-runner`). |
164
+ | `CREW_FUNCTIONS_URL` / `CREW_MCP_URL` | Override the Functions and Workspace MCP endpoints (emulators, e2e). |
165
+ | `CREW_NO_NOTIFY` / `CREW_NO_POWER` | Turn off desktop notifications / sleep inhibition. |
166
+ | `LUMI_RUNNER_REGISTRY` | npm registry for `update` (default `https://registry.npmjs.org`). |
366
167
 
367
168
  ## License
368
169