@lawaty/lacode 0.4.0 → 0.4.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.
@@ -0,0 +1,209 @@
1
+ # Install and troubleshooting
2
+
3
+ Everything about getting LaCode loaded, and what to do when it silently is not. The one-line install is in the [README](../README.md#getting-started); this page is for the rest of it.
4
+
5
+ A plugin that fails to load is silent — opencode starts normally, prints nothing, logs nothing, and the agents are simply not there. Start at [If the agents are missing](#if-the-agents-are-missing).
6
+
7
+ ## Where it is installed
8
+
9
+ opencode installs npm plugins itself, into one directory per specifier under its
10
+ cache — not into your project's `node_modules`, and not from anywhere a global
11
+ npm install put them. Ask opencode where that cache is instead of assuming,
12
+ because where it lives differs by platform:
13
+
14
+ ```bash
15
+ opencode debug paths
16
+ ```
17
+
18
+ It prints the directories opencode is using on this machine — config, data, log
19
+ and the rest. Find the **cache** line and append
20
+ `/packages/<specifier>/node_modules/` to the path it reports. On Linux with no
21
+ `XDG_CACHE_HOME` set, that comes out as, for example:
22
+
23
+ ```
24
+ ~/.cache/opencode/packages/@lawaty/lacode@latest/node_modules/
25
+ ```
26
+
27
+ **That last line is an illustration of one platform's layout, not a path to
28
+ copy.** Where the same cache root sits on another operating system is not
29
+ something this document should guess at — read it from `opencode debug paths`
30
+ and use what it prints.
31
+
32
+ To inspect what a specifier actually resolved to, list its directory under that
33
+ cache root. An **empty** directory with no `node_modules` inside it means the
34
+ install failed — almost always an unpublished or misspelled package — and
35
+ opencode starts normally **without** the plugin. A **populated** directory there
36
+ means the install succeeded and tells you nothing at all about whether the
37
+ plugin was loaded; that is the next section, and telling those two apart is the
38
+ whole reason for reading it.
39
+
40
+
41
+ ## If the agents are missing
42
+
43
+ A plugin that fails to load is silent: opencode starts normally, prints nothing,
44
+ logs nothing, and the agents are simply not there. Work down this. Every step
45
+ below is written to hold whichever opencode version you are running — none of
46
+ them depends on knowing the answer in advance.
47
+
48
+ **1 — Write the bare specifier and restart.**
49
+
50
+ ```json
51
+ { "plugin": ["@lawaty/lacode"] }
52
+ ```
53
+
54
+ Nothing here hot-reloads, so the restart is part of the step, not a footnote.
55
+
56
+ **2 — Check whether the plugin loaded.**
57
+
58
+ ```bash
59
+ opencode agent list | grep -E '^(explore|implement|context)'
60
+ ```
61
+
62
+ A working install prints `explore`, `implement` and `context`, each with the
63
+ hidden `-1`..`-4` pool variants alongside. If the
64
+ grep returns nothing and the list holds only opencode's own built-ins, the plugin
65
+ did not load. Run it in the same shell environment opencode runs in — an
66
+ `agent list` under a different `HOME` or `XDG_CONFIG_HOME` tests a different
67
+ machine. **If the agents are listed, stop here: the one line above is the whole
68
+ install**, and nothing further in this section applies to you.
69
+
70
+ **3 — Decide which failure you are looking at.** Inspect the package directory
71
+ under the cache root from
72
+ [Where it is installed](#where-it-is-installed), and also check that `plugin`
73
+ really is in the config file opencode reads.
74
+
75
+ - **Empty** (no `node_modules` inside): the install failed — check the package
76
+ name, and that it is published. There is nothing to load yet.
77
+ - **Populated**: the install worked and the loader dropped the plugin. Go to
78
+ step 4. These two look identical from the outside, which is why this step
79
+ exists.
80
+
81
+ **4 — If the install worked and the agents are still missing, point the loader
82
+ at the built file.** Add a second entry; leave the bare name in place:
83
+
84
+ ```json
85
+ {
86
+ "plugin": [
87
+ "@lawaty/lacode",
88
+ "file:///ABSOLUTE-PATH-FROM-debug-paths/packages/@lawaty/lacode@latest/node_modules/@lawaty/lacode/dist/index.js"
89
+ ]
90
+ }
91
+ ```
92
+
93
+ `ABSOLUTE-PATH-FROM-debug-paths` is a placeholder to be replaced, not a
94
+ directory: take the cache path `opencode debug paths` reports for **your**
95
+ machine and put it in front of `/packages/…`. It must be an absolute path with
96
+ `$HOME` already expanded and no `~` left in it. Re-derive it after a cache wipe
97
+ or a reinstall — the directory is keyed by specifier, so it moves when the
98
+ version does.
99
+
100
+ Why keep both entries instead of swapping the bare name out:
101
+
102
+ - The bare name is what makes opencode install the package into its cache, and
103
+ therefore what creates the path the `file://` entry points at.
104
+ - The `file://` entry is what actually loads the plugin.
105
+ - They **cannot double-inject** on opencode 1.18.35, where the bare name
106
+ contributes nothing at all: the pair loads the plugin exactly once. On any
107
+ other version this is untested, which is precisely why step 4 is something you
108
+ take *because* step 2 showed the agents missing, and never on the expectation
109
+ that it would help.
110
+
111
+ **5 — Restart, and expect two restarts after a cache wipe.** The first start
112
+ installs the package and creates the path the `file://` entry points at, but a
113
+ path that does not exist at startup is a path that cannot be loaded in that same
114
+ run. The second start finds it and loads the plugin. After that it is steady
115
+ state.
116
+
117
+ This package also publishes `main` and an `exports["./server"]` entry pointing at
118
+ the same built file, because those are two of the shapes a plugin loader
119
+ resolving an entry point consults. They are forward-compatibility metadata and
120
+ they cost nothing — but they do **not** fix this on 1.18.35: adding either one to
121
+ the *installed* cache copy was tested there and the bare specifier still loaded
122
+ nothing. No other version has been observed where they help either. Use the
123
+ `file://` entry.
124
+
125
+
126
+ ## Which opencode versions this is known on
127
+
128
+ Exactly one version has been measured, and the procedure above is deliberately
129
+ not built around that fact. Treat this table as the evidence it is, which is one
130
+ row wide.
131
+
132
+ | opencode version | bare spec loads? | `file://` needed? | evidence |
133
+ | --- | --- | --- | --- |
134
+ | 1.18.35 | No — installs the package, then silently drops it | Yes | **Verified** on 1.18.35: a populated cache directory, zero agents injected, no error and no log line; the `file://` entry loads the full agent set. |
135
+ | Any other version | **Unknown** | **Unknown** | **Untested.** No other opencode version has been run against this package in either direction. |
136
+
137
+ Every command quoted on this page was run against 1.18.35. **If your version is
138
+ not the one measured here, follow the procedure above rather than reading this
139
+ table as coverage of your build**: each step stands on its own, and step 2 tells
140
+ you which situation you are in before any version-specific advice is relevant.
141
+
142
+
143
+ ## macOS
144
+
145
+ The plugin runs on macOS. It is plain JavaScript with no native dependency, and
146
+ what the package publishes is the built JavaScript rather than TypeScript
147
+ sources, so nothing about loading it is Linux-only. Use the procedure above
148
+ unchanged.
149
+
150
+ Three things differ, and all three are worth reading rather than assuming:
151
+
152
+ - **Paths.** Do not copy a cache path out of this document — the example under
153
+ [Where it is installed](#where-it-is-installed) is one platform's layout. Run
154
+ `opencode debug paths` and build the `file://` entry from what it reports.
155
+ This page deliberately does not name a macOS cache root: where opencode keeps
156
+ it on a Mac has not been established here, and a plausible-looking guess is
157
+ worse than an instruction to look.
158
+ - **Notifications** are delivered through `osascript`, or through
159
+ `terminal-notifier` when `osascript` is not installed, rather than through the
160
+ Linux `notify-send` (US-26). `lacode doctor` reports which channel your machine
161
+ would actually use.
162
+ - **The repository's shell scripts are Linux-only by design**, and are not in the
163
+ published package. They drive one person's SSH topology, so each is on the side
164
+ it belongs to:
165
+
166
+ - **Remote hosts are plain npm clients.** A remote host installs the plugin the
167
+ same way (`opencode plugin @lawaty/lacode -g`); there is nothing to rsync. The
168
+ notification hop is a direct ssh (`ControlMaster` + `ControlPersist`) set up
169
+ once with `lacode tunnel setup` — no reverse forward, no listening port, no
170
+ systemd unit. See [Remote hosts](../README.md#remote-hosts) and docs/TUNNEL.md.
171
+ - The repo still carries the author's personal, Linux-only tooling from the older
172
+ reverse-forward setup (`bin/oc`, `bin/oc-notify`, `bin/oc-notify-receiver`,
173
+ `bin/oc-sync`). None of it is in the published package, and none of it is needed
174
+ for the npm-client path above.
175
+
176
+ **The test suite runs on macOS.** It is written to pass on a Mac as well as on
177
+ Linux, so "it works on a Mac" is a supported claim rather than an intention. The
178
+ few checks that exercise the Linux-only shell scripts above skip themselves on a
179
+ machine that cannot run them, naming the reason — a bash 3.2 with no `mapfile`,
180
+ or no GNU `timeout` — rather than passing silently or failing confusingly.
181
+
182
+
183
+ ## The `lacode` CLI
184
+
185
+ The package also ships the `lacode` CLI (`notify`, `forward`, `doctor`, `receive`, `tunnel setup`) — see
186
+ [Remote hosts](../README.md#remote-hosts). The plugin install does **not** put it on your
187
+ `PATH`; run it from the package's `node_modules/.bin`, or install just the CLI
188
+ globally with `npm i -g @lawaty/lacode` if you want the bare command. (That
189
+ global install is for the command only — it is still never what makes the plugin
190
+ load.)
191
+
192
+
193
+ ## What the install gives you
194
+
195
+ Two things are worth knowing about what npm actually hands you:
196
+
197
+ - **`@opencode-ai/plugin` is a peer dependency.** It is not vendored into this
198
+ package and never will be: a second copy of the SDK inside the tarball would
199
+ mean opencode and the plugin holding two different module instances, and
200
+ instance identity is what plugin state hangs on. npm installs the peer for you;
201
+ if you install with a tool that does not, install `@opencode-ai/plugin` at the
202
+ same version yourself.
203
+ - **You get compiled JavaScript, not TypeScript sources.** The tarball ships
204
+ `dist/`, built from `src/` at pack time (`prepack` runs the build, so a stale
205
+ `dist/` cannot be published). This is not a preference: node strips types
206
+ natively from 22.18 but **refuses to for files under `node_modules`**, so a
207
+ package that shipped `.ts` worked fine in a checkout and failed on install —
208
+ `lacode --help` died with `ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`. A
209
+ published package cannot depend on its consumer's loader being lenient.
@@ -0,0 +1,232 @@
1
+ # OmniRoute gateway integration
2
+
3
+ [OmniRoute](https://github.com/diegosouzapw/OmniRoute) is a local OpenAI-compatible gateway that routes a request across many providers with fallback and "combos". LaCode can register it as an opencode provider. It is **off by default**. The summary is in the [README](../README.md#configure); this page is the full reference, including a latency-measured combo that is worth applying on a new client.
4
+
5
+ ## Configuring OmniRoute
6
+
7
+ [OmniRoute](https://github.com/diegosouzapw/OmniRoute) is a local
8
+ OpenAI-compatible AI gateway (default `http://localhost:20128/v1`) that routes a
9
+ request across many providers with fallback and "combos". LaCode can register it
10
+ as an opencode provider for you. It is **off by default**: if you do not set
11
+ `omniroute.enabled: true`, nothing below happens and LaCode makes no request.
12
+
13
+ **Setting it up on a client machine.** Four steps, in this order:
14
+
15
+ 1. Install OmniRoute, then make it stay up: run `omniroute autostart enable`
16
+ once. That writes a systemd user unit in a headless session, or an XDG
17
+ autostart entry in a graphical one, and OmniRoute supervises itself from
18
+ there on. Its gateway listens on `http://localhost:20128/v1`. The gateway's
19
+ lifecycle is **the client's responsibility**: LaCode never starts, supervises
20
+ or stops it, it only probes it. The gateway must therefore already be
21
+ running **before** you start opencode — one that is down at startup is
22
+ disabled for that run, and the integration is per-run.
23
+ 2. Add the plugin block to your opencode config:
24
+ `["@lawaty/lacode", { "omniroute": { "enabled": true, "apiKey": "<your omniroute key>", "combo": "auto", "models": [] } }]`
25
+ `auto` is the zero-config default, but on a free tier it often resolves to a
26
+ slow chain. [A tested fast combo](#a-tested-fast-combo-free-mix) below is
27
+ ordered for latency and is worth applying on a new client.
28
+ 3. Optionally, to route pooled work through it, make `omniroute/auto 3` the
29
+ **first** line of `~/.config/lacode/pool.txt`. `pool.txt` is optional user
30
+ config; the bundled default preset does **not** include OmniRoute.
31
+ 4. **Restart opencode.** Nothing here hot-reloads.
32
+
33
+ With `models: []` and `combo: "auto"`, the plugin registers exactly one model,
34
+ `omniroute/auto`, which then appears in the model picker for any agent — no
35
+ hand-maintained model list.
36
+
37
+ The full option set, for reference:
38
+
39
+ ```jsonc
40
+ {
41
+ "plugin": [
42
+ ["@lawaty/lacode", {
43
+ "omniroute": {
44
+ "enabled": true,
45
+ "baseURL": "http://localhost:20128/v1",
46
+ "apiKey": "sk_omniroute", // optional; OMNIROUTE_API_KEY overrides it
47
+ "combo": "static-best-free", // optional: let OmniRoute do the routing
48
+ "models": ["cc/claude-sonnet-4-6", "gemini-3-flash"], // hard allowlist
49
+ "tiers": {
50
+ "primary": ["cc/claude-sonnet-4-6"],
51
+ "backup": ["gemini-3-flash"]
52
+ },
53
+ "stickyPerSession": true,
54
+ "healthCheck": true,
55
+ "timeoutMs": 5000
56
+ }
57
+ }]
58
+ ]
59
+ }
60
+ ```
61
+
62
+ | Field | Default | Meaning |
63
+ | --- | --- | --- |
64
+ | `enabled` | `false` | Turns the integration on. |
65
+ | `baseURL` | `http://localhost:20128/v1` | Gateway base URL, normalised to exactly one `/v1`. |
66
+ | `apiKey` | — | Bearer key. **`OMNIROUTE_API_KEY` overrides this value.** The key is never logged. |
67
+ | `combo` | — | An OmniRoute combo id (or `auto`) to expose as a selectable model even though the gateway does not list it. Exposed first. |
68
+ | `models` | — | A **hard allowlist**: only these ids are ever exposed, even if the gateway lists more. |
69
+ | `tiers` | — | `{ "primary": [...], "backup": [...] }` preference order over models the allowlist already permits. |
70
+ | `stickyPerSession` | `true` | Keep a session on one model while that model is healthy. |
71
+ | `healthCheck` | `true` | Probe `GET /models` at startup; a failed check disables the integration for that run only. |
72
+ | `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. |
73
+
74
+ An enabled but invalid block (bad `baseURL`, a non-array `models`, …) is rejected
75
+ as a whole: LaCode logs one warning naming the problem and continues exactly as
76
+ if the integration were off. It never crashes opencode.
77
+
78
+ **What it registers.** On the first `config` pass LaCode health-checks the
79
+ gateway, reads `GET /models`, filters it through your `models` allowlist (or
80
+ exposes everything the gateway lists when no allowlist is set), and writes a
81
+ `provider.omniroute` entry — `npm: "@ai-sdk/openai-compatible"` with your
82
+ `baseURL` / `apiKey` — so `omniroute/<model-id>` becomes selectable. If you
83
+ already have a `provider.omniroute` block, LaCode leaves it completely untouched
84
+ and warns; it also never adds a `whitelist`. Because opencode loads
85
+ `@ai-sdk/openai-compatible` itself, no extra dependency is installed.
86
+
87
+ **Combo vs. allowlist.** Set `combo` and OmniRoute does the fallback for you
88
+ (let it route): the combo leads the exposed models, so opencode can hand a failed
89
+ request to the gateway's own fallback chain. Without a combo, LaCode exposes the
90
+ allowlist ordered by `tiers` (primary first). Candidate order is `filterModels`
91
+ (combo first, then the hard allowlist) followed by `orderByTier`, and that is
92
+ all — LaCode picks which model is exposed and in what order; it does not measure
93
+ latency or error rate at runtime, and it does not skip a model because it
94
+ failed.
95
+
96
+ **Using OmniRoute in the pool.** The integration only *registers* the provider —
97
+ it never touches your pool, and it does not place a slot for you. To route pooled
98
+ work (exploration, implementation, cartography) through the gateway, list it in
99
+ your pool like any other model:
100
+
101
+ ```
102
+ # ~/.config/lacode/pool.txt
103
+ omniroute/auto 3
104
+ opencode/space-bunny-free 2
105
+ opencode-go/step-5-preview-free 1
106
+ opencode/big-pickle 1
107
+ ```
108
+
109
+ `omniroute/auto` then takes slot 1 (weight 3) and drains pooled mechanical work
110
+ before it reaches the bundled opencode/opencode-go ("zen") models. Any
111
+ `omniroute/<id>` works, provided the id is exposed — by `combo`/`models`, or the
112
+ whole catalog when neither is set. Because the plugin writes the pool nothing,
113
+ the only thing that changes when the gateway is down is that `omniroute/<id>`
114
+ fails; every other slot keeps working. See
115
+ [docs/USER-STORIES.md](USER-STORIES.md) US-28.
116
+
117
+
118
+ ## A tested fast combo: `free-mix`
119
+
120
+ `combo: "auto"` is the zero-configuration default, but `auto` inherits whatever
121
+ the gateway chooses, and on a busy free tier that is frequently the slowest
122
+ chain available. The combo below was **measured** against a live free-tier
123
+ gateway and is ordered for latency rather than for breadth. Apply it during the
124
+ "Setting it up on a client machine" steps above and pooled work is usable
125
+ straight away, instead of starting from a gateway that queues for half a minute
126
+ per call.
127
+
128
+ ```jsonc
129
+ {
130
+ "name": "free-mix",
131
+ // Order decides. Do NOT use "weighted" here — weights let the gateway
132
+ // re-shuffle a model that is returning 429 into first position again.
133
+ "strategy": "priority",
134
+ "models": [
135
+ { "model": "gemini/gemini-3.1-flash-lite", "providerId": "gemini" },
136
+ { "model": "gemini/gemini-3.8-flash", "providerId": "gemini" },
137
+ { "model": "gemini/gemini-3.7-flash", "providerId": "gemini" },
138
+ { "model": "gemini/gemini-3-flash-preview", "providerId": "gemini" },
139
+ { "model": "openrouter/nvidia/nemotron-3-super-120b-a12b:free", "providerId": "openrouter" },
140
+ { "model": "openrouter/nvidia/nemotron-3-ultra-550b-a55b:free", "providerId": "openrouter" },
141
+ { "model": "openrouter/nvidia/nemotron-3.5-lightning:free", "providerId": "openrouter" },
142
+ { "model": "openrouter/cohere/north-mini-code:free", "providerId": "openrouter" },
143
+ { "model": "openrouter/poolside/laguna-s-2.1:free", "providerId": "openrouter" },
144
+ { "model": "openrouter/dots-studio/dots-3-note-preview:free", "providerId": "openrouter" }
145
+ ]
146
+ }
147
+ ```
148
+
149
+ Why this order, from 859 calls over a single day against that gateway:
150
+
151
+ | Model | Observed behaviour | Position |
152
+ | --- | --- | --- |
153
+ | `gemini/gemini-3.1-flash-lite` | **165/172 succeeded (96%)**, ~4.5s median | **first** |
154
+ | `gemini-3.8-flash` / `3.7-flash` / `3-flash-preview` | succeed intermittently, but **burn exactly 30.0s** on a `503` queue-budget timeout when they do not | next |
155
+ | `openrouter/*:free` | **~90% `429`**, but fails *fast* (~1.8s) and occasionally succeeds | last, as a cheap final fallback |
156
+ | `gemini-2.5-flash`, `gemini-2.5-flash-lite` | **`404` on every attempt** — dead ids | **omitted entirely** |
157
+
158
+ The ordering matters because a combo pays for *every* step it walks past. Put the
159
+ 96%-success model first and the common case is a single hop; the 30s models sit
160
+ behind it where they cost nothing, and the 429s sit last where they give up in
161
+ under two seconds.
162
+
163
+ Measured on the same gateway, same day, `max_tokens: 10`:
164
+
165
+ | | Before | After |
166
+ | --- | --- | --- |
167
+ | `free-mix` response | 42.7s / 41.8s / 34.7s | 4.1s / 3.1s / 3.2s / 4.0s / 3.2s |
168
+ | Median | **~41.8s** | **~3.2s** |
169
+ | Provider time burned on calls >25s | **56.9 of 102.4 min (56%)** | — |
170
+
171
+ Before the change, the real call sequence was a `429` after 2.7s, then a `503`
172
+ after 30.0s, then the actual work in 5.7s — roughly **33 seconds of dead time in
173
+ front of every successful request**, and a p90 of 100s when three 30s steps
174
+ chained.
175
+
176
+ These free-tier endpoints change availability constantly, so treat the ordering
177
+ as a **starting point to re-measure, not a permanent truth**. Re-check it with
178
+ OmniRoute's own call logs before assuming it still holds:
179
+
180
+ ```bash
181
+ # one timed request through the gateway
182
+ curl -o /dev/null -w '%{http_code} %{time_starttransfer}s\n' \
183
+ -X POST http://localhost:20128/v1/chat/completions \
184
+ -H "Authorization: Bearer $OMNIROUTE_API_KEY" -H 'Content-Type: application/json' \
185
+ -d '{"model":"free-mix","messages":[{"role":"user","content":"Say OK"}],"max_tokens":10}'
186
+
187
+ # per-model status + duration, straight from OmniRoute's logs
188
+ python3 - <<'PY'
189
+ import json, glob, collections
190
+ d = "/home/<user>/.omniroute/call_logs/<YYYY-MM-DD>"
191
+ g = collections.defaultdict(collections.Counter)
192
+ for f in glob.glob(d + "/*.json"):
193
+ try: s = json.load(open(f))["summary"]
194
+ except Exception: continue
195
+ g[s.get("comboStepId")][s.get("status")] += 1
196
+ for k, v in sorted(g.items(), key=lambda x: str(x[0])):
197
+ print(f"{str(k)[:44]:44} {dict(v)}")
198
+ PY
199
+ ```
200
+
201
+ Apply it with the bundled CLI (`PUT /api/combos/<id>` updates in place, so the
202
+ combo is never briefly missing):
203
+
204
+ ```bash
205
+ omniroute api combos get-api-combos-id- --id <combo-id> --json > free-mix.json
206
+ # edit name/strategy/models in free-mix.json, then:
207
+ omniroute api combos put-api-combos-id- --id <combo-id> --body @free-mix.json
208
+ ```
209
+
210
+ **A note on mid-session switching.** opencode 1.18.34 exposes no hook to change
211
+ an agent's main model mid-request, so LaCode cannot swap models inside a running
212
+ session; use an OmniRoute `combo` (or `auto`) if you want per-request fallback.
213
+ LaCode's selection engine decides which models are exposed and in what order, and
214
+ its cooldown/backoff rules are unit-tested.
215
+
216
+ **Getting a key.** Open the OmniRoute dashboard, go to API Manager / API Keys and
217
+ copy a key into `OMNIROUTE_API_KEY` (recommended) or `omniroute.apiKey`. On a
218
+ local instance with `REQUIRE_API_KEY=false`, the literal placeholder
219
+ `sk_omniroute` is accepted.
220
+
221
+ **Troubleshooting**
222
+
223
+ | Symptom | Cause | Fix |
224
+ | --- | --- | --- |
225
+ | `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. |
226
+ | `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. |
227
+ | `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 set it as `combo` — `combo` is registered whether or not the gateway advertises it, so a valid gateway route works there too. A valid gateway route is not automatically an exposed model. |
228
+ | `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. |
229
+ | `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`. |
230
+ | `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. |
231
+ | Provider registered but models are missing | You already had `provider.omniroute` | Remove it and let LaCode write it, or add the models yourself — LaCode never modifies an existing provider. |
232
+ | Pooled work still runs on the bundled models | The gateway was unhealthy/disabled at startup, or you have no `omniroute/…` slot in your pool | Check the startup log for `omniroute health check failed` / `no usable models`, then add `omniroute/<model>` to `pool.txt` (or `pool.json`) to route pooled work through the gateway. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lawaty/lacode",
3
- "version": "0.4.0",
3
+ "version": "0.4.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",
@@ -45,6 +45,7 @@
45
45
  "presets",
46
46
  "sounds",
47
47
  "LICENSE",
48
+ "CHANGELOG.md",
48
49
  "README.md",
49
50
  "docs"
50
51
  ],