@lawaty/lacode 0.2.1 → 0.2.2

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
@@ -324,6 +324,29 @@ request across many providers with fallback and "combos". LaCode can register it
324
324
  as an opencode provider for you. It is **off by default**: if you do not set
325
325
  `omniroute.enabled: true`, nothing below happens and LaCode makes no request.
326
326
 
327
+ **Setting it up on a client machine.** Four steps, in this order:
328
+
329
+ 1. Install OmniRoute, then make it stay up: run `omniroute autostart enable`
330
+ once. That writes a systemd user unit in a headless session, or an XDG
331
+ autostart entry in a graphical one, and OmniRoute supervises itself from
332
+ there on. Its gateway listens on `http://localhost:20128/v1`. The gateway's
333
+ lifecycle is **the client's responsibility**: LaCode never starts, supervises
334
+ or stops it, it only probes it. The gateway must therefore already be
335
+ running **before** you start opencode — one that is down at startup is
336
+ disabled for that run, and the integration is per-run.
337
+ 2. Add the plugin block to your opencode config:
338
+ `["@lawaty/lacode", { "omniroute": { "enabled": true, "apiKey": "<your omniroute key>", "combo": "auto", "models": [] } }]`
339
+ 3. Optionally, to route pooled work through it, make `omniroute/auto 3` the
340
+ **first** line of `~/.config/lacode/pool.txt`. `pool.txt` is optional user
341
+ config; the bundled default preset does **not** include OmniRoute.
342
+ 4. **Restart opencode.** Nothing here hot-reloads.
343
+
344
+ With `models: []` and `combo: "auto"`, the plugin registers exactly one model,
345
+ `omniroute/auto`, which then appears in the model picker for any agent — no
346
+ hand-maintained model list.
347
+
348
+ The full option set, for reference:
349
+
327
350
  ```jsonc
328
351
  {
329
352
  "plugin": [
@@ -357,7 +380,7 @@ as an opencode provider for you. It is **off by default**: if you do not set
357
380
  | `tiers` | — | `{ "primary": [...], "backup": [...] }` preference order over models the allowlist already permits. |
358
381
  | `stickyPerSession` | `true` | Keep a session on one model while that model is healthy. |
359
382
  | `healthCheck` | `true` | Probe `GET /models` at startup; a failed check disables the integration for that run only. |
360
- | `timeoutMs` | `5000` | Budget for LaCode's own calls to the gateway. |
383
+ | `timeoutMs` | `5000` | Budget for LaCode's own probe and model-cache calls to the gateway. It is not written into the provider options, so it does not bound real traffic — opencode's own default applies there. |
361
384
 
362
385
  An enabled but invalid block (bad `baseURL`, a non-array `models`, …) is rejected
363
386
  as a whole: LaCode logs one warning naming the problem and continues exactly as
@@ -372,12 +395,14 @@ already have a `provider.omniroute` block, LaCode leaves it completely untouched
372
395
  and warns; it also never adds a `whitelist`. Because opencode loads
373
396
  `@ai-sdk/openai-compatible` itself, no extra dependency is installed.
374
397
 
375
- **Combo vs. allowlist.** Set `combo` and OmniRoute does the fallback for you (let
376
- it route). Without a combo, LaCode exposes the allowlist ordered by `tiers`
377
- (primary first), tracks a rolling latency and error rate per model, and skips a
378
- model while it is cooling after a transient failure — 429, 408 and 5xx, honouring
379
- `Retry-After` and otherwise using capped exponential backoff. Non-transient
380
- errors (400, 401, 403, 404) are surfaced to you, never failed over.
398
+ **Combo vs. allowlist.** Set `combo` and OmniRoute does the fallback for you
399
+ (let it route): the combo leads the exposed models, so opencode can hand a failed
400
+ request to the gateway's own fallback chain. Without a combo, LaCode exposes the
401
+ allowlist ordered by `tiers` (primary first). Candidate order is `filterModels`
402
+ (combo first, then the hard allowlist) followed by `orderByTier`, and that is
403
+ all — LaCode picks which model is exposed and in what order; it does not measure
404
+ latency or error rate at runtime, and it does not skip a model because it
405
+ failed.
381
406
 
382
407
  **Using OmniRoute in the pool.** The integration only *registers* the provider —
383
408
  it never touches your pool, and it does not place a slot for you. To route pooled
@@ -415,7 +440,9 @@ local instance with `REQUIRE_API_KEY=false`, the literal placeholder
415
440
 
416
441
  | Symptom | Cause | Fix |
417
442
  | --- | --- | --- |
418
- | `omniroute health check failed` at startup | Gateway not running, wrong `baseURL`, or a startup race | Start OmniRoute on `http://localhost:20128` and restart opencode. The integration is disabled for that run only, not permanently. |
443
+ | `omniroute health check failed` at startup | Gateway not running, wrong `baseURL`, or a startup race | Start OmniRoute on `http://localhost:20128` and restart opencode; `omniroute autostart enable` makes that durable across restarts. The integration is disabled for that run only, not permanently. |
444
+ | `omniroute health check failed` at startup, then a repeating `cooling omniroute/<id> (hang)` and `Model not found` churn on one slot | OmniRoute was started *after* opencode, and `pool.txt` names an `omniroute/<id>` slot | The integration is off for that run, but the pool still routes to the slot: the request hangs, the reaper cools it for 600s, and the cycle repeats. Start OmniRoute, then restart opencode. |
445
+ | `ProviderModelNotFoundError: Model not found` on an id the gateway serves | The id is a valid OmniRoute route but is not in the plugin's `models` allowlist, so the plugin never exposed it | Add the id to `models`, or use `combo: "auto"` with `omniroute/auto`. A valid gateway route is not automatically an exposed model. |
419
446
  | `no usable models` | The gateway returned an empty list, or every id was filtered out by `models` | `curl -H "Authorization: Bearer $OMNIROUTE_API_KEY" http://localhost:20128/v1/models` and check `models`/`combo` against the returned ids. |
420
447
  | `401` / `Invalid API key` | `REQUIRE_API_KEY=true` and the key is missing or wrong | Create a key in the dashboard, export `OMNIROUTE_API_KEY`, or set `REQUIRE_API_KEY=false` locally and use `sk_omniroute`. |
421
448
  | `404` on a URL containing `/v1/v1/` | A `baseURL` that already carried `/v1` was given twice | LaCode normalises this, so it should not come from LaCode; check a hand-written `provider.omniroute` block, which LaCode leaves untouched. |
@@ -531,6 +558,7 @@ are reading:
531
558
  /context-init # build the map, or verify and refresh an existing one
532
559
  /context-update # incremental refresh; accepts a focus, e.g. /context-update src/payments
533
560
  /context-review # audit it for staleness, contradictions, duplication and bloat
561
+ /lacode-doctor # check the pool, model resolution and the OmniRoute gateway. Report only
534
562
  ```
535
563
 
536
564
  The map is local and untracked — it describes your checkout on your machine, so
package/dist/commands.js CHANGED
@@ -67,6 +67,36 @@ Audit the five context files for:
67
67
 
68
68
  Fix what you find in \`.opencode/context/\` only, then report: findings, fixes,
69
69
  and anything you could not verify.`
70
+ },
71
+ "lacode-doctor": {
72
+ description: "Check the LaCode setup \u2014 pool slots, model resolution, and the OmniRoute gateway. Report only; changes nothing.",
73
+ template: `Diagnose this machine's LaCode setup. Report only; change nothing.
74
+
75
+ Print one PASS/FAIL line per check below, with the exact fix for any FAIL.
76
+
77
+ 1. Pool status \u2014 call the \`pool_status\` tool. Report the slot list, each
78
+ slot's load against its weight ceiling, and which models are cooling.
79
+
80
+ 2. OmniRoute configured \u2014 inspect the \`@lawaty/lacode\` plugin options in the
81
+ user's opencode config for an \`omniroute\` block with \`enabled: true\`. If it
82
+ is absent or disabled, print the two OmniRoute checks below as "not
83
+ configured" and stop after the pool section.
84
+
85
+ 3. Gateway reachable \u2014 when omniroute is configured, request its model list
86
+ (\`GET <baseURL>/v1/models\`, default baseURL \`http://localhost:20128/v1\`)
87
+ and report reachable or not. On failure, say plainly: start OmniRoute BEFORE
88
+ opencode, then restart opencode \u2014 provider registration is per-run. To make
89
+ that durable, run \`omniroute autostart enable\` (OmniRoute's own autostart;
90
+ LaCode never starts the gateway itself).
91
+
92
+ 4. Slots resolve \u2014 for every pool slot whose provider is \`omniroute\`, confirm
93
+ the model id is one the gateway advertises AND is registered. The known
94
+ trap: an id that is a valid gateway route but is NOT in the plugin's
95
+ \`omniroute.models\` allowlist fails with
96
+ \`ProviderModelNotFoundError: Model not found\`; the fix is to add that id to
97
+ the \`models\` array, or use the always-registered \`auto\`.
98
+
99
+ Change nothing. Print a final one-line summary of failures.`
70
100
  }
71
101
  };
72
102
  function applyCommands(cfg, commands = COMMANDS) {
package/dist/index.js CHANGED
@@ -1272,6 +1272,36 @@ Audit the five context files for:
1272
1272
 
1273
1273
  Fix what you find in \`.opencode/context/\` only, then report: findings, fixes,
1274
1274
  and anything you could not verify.`
1275
+ },
1276
+ "lacode-doctor": {
1277
+ description: "Check the LaCode setup \u2014 pool slots, model resolution, and the OmniRoute gateway. Report only; changes nothing.",
1278
+ template: `Diagnose this machine's LaCode setup. Report only; change nothing.
1279
+
1280
+ Print one PASS/FAIL line per check below, with the exact fix for any FAIL.
1281
+
1282
+ 1. Pool status \u2014 call the \`pool_status\` tool. Report the slot list, each
1283
+ slot's load against its weight ceiling, and which models are cooling.
1284
+
1285
+ 2. OmniRoute configured \u2014 inspect the \`@lawaty/lacode\` plugin options in the
1286
+ user's opencode config for an \`omniroute\` block with \`enabled: true\`. If it
1287
+ is absent or disabled, print the two OmniRoute checks below as "not
1288
+ configured" and stop after the pool section.
1289
+
1290
+ 3. Gateway reachable \u2014 when omniroute is configured, request its model list
1291
+ (\`GET <baseURL>/v1/models\`, default baseURL \`http://localhost:20128/v1\`)
1292
+ and report reachable or not. On failure, say plainly: start OmniRoute BEFORE
1293
+ opencode, then restart opencode \u2014 provider registration is per-run. To make
1294
+ that durable, run \`omniroute autostart enable\` (OmniRoute's own autostart;
1295
+ LaCode never starts the gateway itself).
1296
+
1297
+ 4. Slots resolve \u2014 for every pool slot whose provider is \`omniroute\`, confirm
1298
+ the model id is one the gateway advertises AND is registered. The known
1299
+ trap: an id that is a valid gateway route but is NOT in the plugin's
1300
+ \`omniroute.models\` allowlist fails with
1301
+ \`ProviderModelNotFoundError: Model not found\`; the fix is to add that id to
1302
+ the \`models\` array, or use the always-registered \`auto\`.
1303
+
1304
+ Change nothing. Print a final one-line summary of failures.`
1275
1305
  }
1276
1306
  };
1277
1307
  function applyCommands(cfg, commands = COMMANDS) {
@@ -2617,7 +2647,7 @@ var omniRouteHooks = async (input, options, deps = {}) => {
2617
2647
  });
2618
2648
  if (!healthy) {
2619
2649
  disabled = true;
2620
- await warnOnce(`omniroute health check failed at ${config.baseURL}; falling back to lacode's normal behaviour for this run`);
2650
+ await warnOnce(`omniroute health check failed at ${config.baseURL}; falling back to lacode's normal behaviour for this run; pool slots naming omniroute/<id> will fail until the gateway is up`);
2621
2651
  return;
2622
2652
  }
2623
2653
  }
@@ -2667,8 +2697,7 @@ var omniRouteHooks = async (input, options, deps = {}) => {
2667
2697
  baseURL: config.baseURL,
2668
2698
  // Absent rather than blank: an empty bearer on a keyless gateway is a
2669
2699
  // 401 that looks like a configuration error.
2670
- ...config.apiKey ? { apiKey: config.apiKey } : {},
2671
- timeout: config.timeoutMs
2700
+ ...config.apiKey ? { apiKey: config.apiKey } : {}
2672
2701
  },
2673
2702
  models
2674
2703
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lawaty/lacode",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Ultimate opencode setup for Go users — cost-controlled pooled subagents, hang recovery, and notifications for opencode.",
5
5
  "license": "MIT",
6
6
  "type": "module",