@aron98/load-lock 1.3.0 → 1.5.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/dist/adapters/opencode/plugin.d.ts +2 -1
- package/dist/adapters/opencode/plugin.js +100 -6
- package/dist/adapters/opencode/tui.js +7 -2
- package/dist/cli/copies.d.ts +1 -9
- package/dist/cli/copies.js +2 -43
- package/dist/cli/install.js +1 -1
- package/dist/cli/main.js +82 -217
- package/dist/cli/service.d.ts +56 -0
- package/dist/cli/service.js +261 -0
- package/dist/cli/update.d.ts +53 -0
- package/dist/cli/update.js +274 -0
- package/dist/core/daemon.d.ts +24 -0
- package/dist/core/daemon.js +221 -0
- package/dist/core/state.d.ts +5 -0
- package/dist/core/state.js +16 -0
- package/dist/dashboard/feed.d.ts +23 -0
- package/dist/dashboard/feed.js +64 -2
- package/dist/dashboard/frame.js +2 -0
- package/dist/dashboard/view.d.ts +11 -0
- package/dist/dashboard/view.js +13 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.js +117 -8
- package/dist/policy/text.d.ts +1 -1
- package/dist/policy/text.js +6 -0
- package/dist/shared/client.js +11 -1
- package/dist/shared/drift.d.ts +21 -0
- package/dist/shared/drift.js +77 -0
- package/dist/shared/installs.d.ts +39 -0
- package/dist/shared/installs.js +101 -0
- package/dist/shared/pkginfo.d.ts +16 -0
- package/dist/shared/pkginfo.js +54 -0
- package/dist/shared/protocol.d.ts +32 -0
- package/dist/shared/wait-completion.d.ts +67 -0
- package/dist/shared/wait-completion.js +505 -0
- package/dist/test/mcp.test.js +115 -1
- package/dist/test/reconnect.test.d.ts +1 -0
- package/dist/test/reconnect.test.js +389 -0
- package/dist/test/update-command.test.d.ts +1 -0
- package/dist/test/update-command.test.js +608 -0
- package/dist/test/wait-completion.test.d.ts +1 -0
- package/dist/test/wait-completion.test.js +418 -0
- package/docs/ADAPTERS.md +3 -2
- package/docs/AGENT_GUIDE.md +61 -3
- package/docs/ARCHITECTURE.md +15 -0
- package/docs/COMPATIBILITY.md +16 -2
- package/docs/OPERATIONS.md +118 -9
- package/package.json +1 -1
package/docs/OPERATIONS.md
CHANGED
|
@@ -102,6 +102,7 @@ uninstall — see issue #19); not live-verified inside the real CLIs.
|
|
|
102
102
|
load-lock service status # hello + pid + daemon version
|
|
103
103
|
load-lock service start # ensure running (idempotent); refusals name the conflicting pid
|
|
104
104
|
load-lock service stop # SIGTERM → bounded wait → SIGKILL; verifies the socket is free
|
|
105
|
+
load-lock service restart # stop then start; both phases reported; failures name the phase
|
|
105
106
|
```
|
|
106
107
|
|
|
107
108
|
`service stop` is honest, not fire-and-forget: it signals the daemon
|
|
@@ -110,6 +111,11 @@ socket still answers, and only reports success once the endpoint is
|
|
|
110
111
|
verifiably free (stale `daemon.json`/socket files are cleaned up either way).
|
|
111
112
|
`service start` refuses loudly when something else owns the endpoint —
|
|
112
113
|
naming the conflicting pid — instead of a bare "did not come up within 10s".
|
|
114
|
+
`service restart` (since #38) composes the same two hardened phases and
|
|
115
|
+
reports both lines; a failure names the phase and reason
|
|
116
|
+
(`restart failed in the STOP phase: ...` / `... in the START phase ...`).
|
|
117
|
+
Stopping when nothing runs is not an error: the start phase simply spawns a
|
|
118
|
+
fresh daemon.
|
|
113
119
|
|
|
114
120
|
## Update lifecycle
|
|
115
121
|
|
|
@@ -139,6 +145,48 @@ code. The **version handshake** closes that gap (v1.3.0+):
|
|
|
139
145
|
pending self-restart; `doctor` actively waits a few seconds to confirm the
|
|
140
146
|
daemon came back on the new version.
|
|
141
147
|
|
|
148
|
+
### `load-lock update` (since #38)
|
|
149
|
+
|
|
150
|
+
The update command checks npm for a newer version, classifies where *this*
|
|
151
|
+
copy lives **before touching anything**, and applies the update via the
|
|
152
|
+
detected package manager — or refuses honestly rather than guessing:
|
|
153
|
+
|
|
154
|
+
| this copy is... | `load-lock update` does |
|
|
155
|
+
| ------------------------------------- | ------------------------------------------------------------------ |
|
|
156
|
+
| bun global (`~/.bun/install/global/…`) | `bun i -g @aron98/load-lock@latest` |
|
|
157
|
+
| npm global (under `npm root -g`) | `npm i -g @aron98/load-lock@latest` |
|
|
158
|
+
| opencode's npm-cache copy | refuses — opencode manages this copy and refreshes it on its own schedule |
|
|
159
|
+
| a git checkout (nearest `.git`) | refuses — update it via your git workflow (`git pull` etc.) |
|
|
160
|
+
| anything else | refuses — "cannot tell how this copy was installed" |
|
|
161
|
+
|
|
162
|
+
After a successful apply it runs `service restart` explicitly, so the
|
|
163
|
+
daemon moves onto the new code immediately (the version handshake remains
|
|
164
|
+
the safety net, not the primary means). The package manager's output is
|
|
165
|
+
shown live. `--check` is a dry run that touches nothing and uses
|
|
166
|
+
script-friendly exit codes:
|
|
167
|
+
|
|
168
|
+
- exit `0` — up to date (or this copy is ahead of the registry)
|
|
169
|
+
- exit `1` — an update is available (including the honest refusals above:
|
|
170
|
+
the update *exists*, this copy just cannot self-apply it)
|
|
171
|
+
- exit `70` — failure (registry unreachable, package manager failed, or a
|
|
172
|
+
refusal in apply mode)
|
|
173
|
+
|
|
174
|
+
Reports are prose for humans and `--json` structured objects for agents
|
|
175
|
+
(`state`: `up-to-date` | `update-available` | `refused` | `updated`).
|
|
176
|
+
|
|
177
|
+
### Adapter-init drift notice
|
|
178
|
+
|
|
179
|
+
The mirror problem is a *newer adapter copy* with an *older CLI on PATH*
|
|
180
|
+
(typically: opencode refreshed its private cache copy, the global install
|
|
181
|
+
was never updated). At init, the OpenCode plugin and the MCP server compare
|
|
182
|
+
their own version with the PATH-resolved CLI copy's `package.json` and
|
|
183
|
+
surface **one clear notice** naming the exact command (`load-lock update`),
|
|
184
|
+
on stderr and in the injected context / MCP `instructions`. Notify only —
|
|
185
|
+
**nothing is ever auto-installed**. Honest limitation: a harness session
|
|
186
|
+
that was already running keeps the *old* adapter code in memory until the
|
|
187
|
+
harness itself restarts, so the notice fires from the new copy at its init
|
|
188
|
+
— sessions started before a refresh see it on their next restart.
|
|
189
|
+
|
|
142
190
|
Limits, stated honestly: the respawn re-runs the **same argv** — it picks up
|
|
143
191
|
an updated copy only if the daemon's launching copy was updated *in place*
|
|
144
192
|
(the normal `npm i -g` case). A daemon spawned from a different copy (e.g.
|
|
@@ -146,10 +194,61 @@ opencode's private cache copy) keeps running that copy's code after a
|
|
|
146
194
|
restart; see the multi-copy notes in
|
|
147
195
|
[COMPATIBILITY.md](COMPATIBILITY.md#multiple-copies-of-load-lock-opencode).
|
|
148
196
|
Pre-1.3.0 daemons do not answer the handshake and never self-restart —
|
|
149
|
-
`service stop` followed by any load-lock command
|
|
150
|
-
code. The restart behavior itself is live-verified
|
|
151
|
-
suite (`src/test/update.test.ts`); the in-place-update
|
|
152
|
-
same-argv respawn is inspected, not live-tested.
|
|
197
|
+
`service restart` (or `service stop` followed by any load-lock command)
|
|
198
|
+
moves them onto current code. The restart behavior itself is live-verified
|
|
199
|
+
on Linux by the test suite (`src/test/update.test.ts`); the in-place-update
|
|
200
|
+
path beyond same-argv respawn is inspected, not live-tested. The `update`
|
|
201
|
+
command's stop→start compositing, its refusals, `--check` exit codes, and
|
|
202
|
+
the adapter-init drift notice are live-verified on Linux by
|
|
203
|
+
`src/test/update-command.test.ts` — with a **stubbed npm on PATH and a
|
|
204
|
+
planted copy**; no test ever contacts the real npm registry or installs
|
|
205
|
+
anything, so the genuine `npm i -g` round trip is contract-tested, not
|
|
206
|
+
live-tested.
|
|
207
|
+
|
|
208
|
+
### Plugin in-memory staleness (issue #39)
|
|
209
|
+
|
|
210
|
+
The daemon heals itself via the handshake, but the **opencode plugin does
|
|
211
|
+
not**: opencode loads plugin code **once per process at startup** and never
|
|
212
|
+
hot-reloads it. After `npm i -g @aron98/load-lock@latest` (or opencode
|
|
213
|
+
refreshing its plugin cache for *new* sessions), an already-running opencode
|
|
214
|
+
session keeps executing the **old copy in memory** — even though the panel
|
|
215
|
+
and dashboards reconnect to the daemon just fine. A stale in-memory plugin
|
|
216
|
+
is not a reconnect failure; restarting opencode is the only way to pick up
|
|
217
|
+
the new plugin code.
|
|
218
|
+
|
|
219
|
+
To make that visible instead of mysterious (v1.4.1+):
|
|
220
|
+
|
|
221
|
+
- The dashboards (TUI panel and `load-lock dashboard`) periodically compare
|
|
222
|
+
the running copy's version with the PATH-resolved `load-lock` install.
|
|
223
|
+
When the PATH CLI is newer, the panel shows
|
|
224
|
+
`note: plugin updated — restart opencode to pick up vX (this session
|
|
225
|
+
still runs vY; opencode does not hot-reload plugins)`; the CLI surface
|
|
226
|
+
says the running copy is stale instead.
|
|
227
|
+
- When the daemon itself restarts onto a **different version** (update
|
|
228
|
+
self-restart, or a manual restart of a newer install), the reconnect note
|
|
229
|
+
names it: `daemon restarted onto v1.5.0 (was v1.4.0; pid N); event
|
|
230
|
+
history re-synced`.
|
|
231
|
+
- If a harness pruned the running copy's plugin-cache directory mid-session
|
|
232
|
+
(making the on-disk daemon entry vanish), client auto-spawn falls back to
|
|
233
|
+
the PATH-installed CLI's daemon instead of failing with ENOENT forever.
|
|
234
|
+
- Recovery story: the daemon side is automatic (handshake self-restart);
|
|
235
|
+
the plugin side needs exactly one action — **restart opencode**. Until
|
|
236
|
+
then everything keeps working; only the plugin's code (and its notes)
|
|
237
|
+
is one version behind.
|
|
238
|
+
|
|
239
|
+
The reverse direction — a *newer* adapter copy with an *older* CLI on PATH —
|
|
240
|
+
is #38's [adapter-init drift notice](#adapter-init-drift-notice), above.
|
|
241
|
+
|
|
242
|
+
Verification split (honest): live-verified on Linux by
|
|
243
|
+
`src/test/reconnect.test.ts` against real daemons — the daemon-kill
|
|
244
|
+
auto-respawn reconnect (the H1 repro), the failed-initial-connect recovery,
|
|
245
|
+
and the vanished daemon entry not crashing the host (real ENOENT spawn
|
|
246
|
+
through the compiled client). Contract-tested — mock client / planted
|
|
247
|
+
fixture PATH layouts, no version is ever really upgraded in a test — the
|
|
248
|
+
"restarted onto vX (was vY)" note wording, install-drift detection and its
|
|
249
|
+
per-surface hints, and the `resolveDaemonEntry` fallback table. The genuine
|
|
250
|
+
`npm i -g` + opencode-cache-refresh round trip is inspected, not
|
|
251
|
+
live-tested.
|
|
153
252
|
|
|
154
253
|
## Configuration
|
|
155
254
|
|
|
@@ -166,7 +265,8 @@ load-lock get-config
|
|
|
166
265
|
```
|
|
167
266
|
|
|
168
267
|
Defaults: one pool `heavy` capacity 1 · offer window 30s · blocking wait 5s ·
|
|
169
|
-
turn-wait cap (`maxTurnWaitMs`, bounds `wait_for_turn
|
|
268
|
+
turn-wait cap (`maxTurnWaitMs`, bounds `wait_for_turn`, `wait_for_completion`
|
|
269
|
+
and `load-lock wait --id/--job`) 10min ·
|
|
170
270
|
start deadline 120s · untracked lease 15min. Valid ranges:
|
|
171
271
|
offer window 1s–10min, wait 0–10min, turn-wait cap 1s–1h, start deadline 1s–1h, lease 10s–24h.
|
|
172
272
|
|
|
@@ -239,6 +339,14 @@ companion to the main plugin, which is unchanged). Inside the opencode TUI:
|
|
|
239
339
|
palette — is open, so Esc keeps cancelling the dialog instead of
|
|
240
340
|
closing the panel underneath it.
|
|
241
341
|
|
|
342
|
+
The panel surfaces two kinds of operational notes inline (issue #39): a
|
|
343
|
+
`daemon restarted (onto vX)` line after the daemon restarts underneath the
|
|
344
|
+
session (event history is re-synced automatically), and — because opencode
|
|
345
|
+
never hot-reloads plugins — a `plugin updated — restart opencode to pick
|
|
346
|
+
up vX` hint whenever the PATH `load-lock` install is newer than the copy
|
|
347
|
+
this session is running. See "Plugin in-memory staleness" under
|
|
348
|
+
[Update lifecycle](#update-lifecycle).
|
|
349
|
+
|
|
242
350
|
The open command deliberately ships with **no global key binding** (issue
|
|
243
351
|
#28). Never bind unmodified single letters or letter sequences (the old
|
|
244
352
|
`gl`) at global scope: while the keymap engine waits for the next key of a
|
|
@@ -696,9 +804,10 @@ unless you want the grants in your real profile):
|
|
|
696
804
|
# GOTCHA 2: the machine's real daemon was still v1.1.0 — a daemon
|
|
697
805
|
# started before v1.2.0 silently strips the unknown labelPrefix field
|
|
698
806
|
# (zod), so prefixed labels disappear without any error. Upgrade and
|
|
699
|
-
# restart: load-lock service stop && load-lock service start
|
|
700
|
-
# verification
|
|
701
|
-
#
|
|
807
|
+
# restart: load-lock service stop && load-lock service start (at
|
|
808
|
+
# verification time there was no `service restart` subcommand; it exists
|
|
809
|
+
# since issue #38 and is the shorter spelling today). This verification
|
|
810
|
+
# deliberately used its own daemon on a temp state dir.
|
|
702
811
|
LOAD_LOCK_STATE_DIR=/tmp/ll-32-test node dist/cli/main.js service start
|
|
703
812
|
|
|
704
813
|
# 1. install the grants (merge-only; prints exactly what it merged)
|
|
@@ -762,7 +871,7 @@ The two gotchas that shaped the installer:
|
|
|
762
871
|
2. **Label prefixes need a current daemon on the host.** A daemon
|
|
763
872
|
started before v1.2.0 strips the unknown `labelPrefix` field silently
|
|
764
873
|
(zod); the dashboard shows the bare label with no error anywhere.
|
|
765
|
-
`load-lock service
|
|
874
|
+
`load-lock service restart` after upgrading.
|
|
766
875
|
|
|
767
876
|
What was NOT verified: omac's macOS seatbelt path (same profile schema,
|
|
768
877
|
per omac's docs, but the verification machine is Linux) and omac's agent
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aron98/load-lock",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Machine-wide counting semaphore coordinating resource-intensive work across parallel coding agents (OpenCode, Codex CLI, Claude Code, ...)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|