omnilane 0.8.0 → 0.8.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/CHANGELOG.md +27 -1
- package/README.ja.md +26 -2
- package/README.ko.md +25 -3
- package/README.md +25 -2
- package/README.zh-CN.md +21 -2
- package/README.zh-TW.md +21 -2
- package/VERSION +1 -1
- package/completions/_omnilane +1 -1
- package/completions/omnilane.bash +1 -1
- package/package.json +1 -1
- package/routing.yaml +4 -2
- package/scripts/configure.sh +7 -1
- package/scripts/dispatch.sh +4 -4
- package/scripts/jobs.sh +3 -3
- package/scripts/lib/common.sh +6 -0
- package/scripts/runners/run-opencode.sh +51 -0
- package/scripts/runners/run-openrouter.sh +86 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,29 @@ semantic version tags.
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.8.2] - 2026-07-18
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `openrouter` vendor: direct OpenRouter API dispatch (curl + `OPENROUTER_API_KEY`,
|
|
14
|
+
no CLI dependency). Advise/consult only — work mode fails hard with guidance —
|
|
15
|
+
and the model slug is mandatory. Hundreds of hosted models become reachable
|
|
16
|
+
from any omnilane install without adding a coding-agent CLI.
|
|
17
|
+
- `opencode` vendor: headless `opencode run` dispatch through the OpenCode
|
|
18
|
+
multi-provider aggregator CLI. Advise mode pins OpenCode's built-in read-only
|
|
19
|
+
`plan` agent; work mode uses `--auto`. Appended as the last fallback in the
|
|
20
|
+
default `coding-overflow` chain.
|
|
21
|
+
|
|
22
|
+
## [0.8.1] - 2026-07-18
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- Claude Code plugin `SessionStart` hook (`hooks/hooks.json`): plugin installs
|
|
27
|
+
now auto-inject the routing reminder (`hooks/routing-instruction.md`) at
|
|
28
|
+
session open (`startup|resume|clear`) via `${CLAUDE_PLUGIN_ROOT}`, with no
|
|
29
|
+
edit to the user's `~/.claude/CLAUDE.md`. The `install.sh` instruction-file
|
|
30
|
+
reminder remains the path for the other CLIs.
|
|
31
|
+
|
|
9
32
|
## [0.8.0] - 2026-07-18
|
|
10
33
|
|
|
11
34
|
### Added
|
|
@@ -229,7 +252,10 @@ semantic version tags.
|
|
|
229
252
|
- Initial shared routing table, cross-vendor dispatcher, runners, installer,
|
|
230
253
|
and baseline lint fixes.
|
|
231
254
|
|
|
232
|
-
[Unreleased]: https://github.com/Seraphim0916/omnilane/compare/v0.8.
|
|
255
|
+
[Unreleased]: https://github.com/Seraphim0916/omnilane/compare/v0.8.2...HEAD
|
|
256
|
+
[0.8.2]: https://github.com/Seraphim0916/omnilane/compare/v0.8.1...v0.8.2
|
|
257
|
+
|
|
258
|
+
[0.8.1]: https://github.com/Seraphim0916/omnilane/compare/v0.8.0...v0.8.1
|
|
233
259
|
|
|
234
260
|
[0.8.0]: https://github.com/Seraphim0916/omnilane/compare/v0.7.1...v0.8.0
|
|
235
261
|
[0.7.1]: https://github.com/Seraphim0916/omnilane/compare/v0.7.0...v0.7.1
|
package/README.ja.md
CHANGED
|
@@ -20,6 +20,28 @@
|
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
+
## v0.8.2 の新機能
|
|
24
|
+
|
|
25
|
+
- **`openrouter` vendor** — `curl` と `OPENROUTER_API_KEY` だけで
|
|
26
|
+
OpenRouter API に直接ディスパッチ。どの omnilane インストールからでも
|
|
27
|
+
数百のホスト型モデルに到達でき、コーディングエージェント CLI の追加は
|
|
28
|
+
不要。advise/consult 専用(ファイル編集不可、work モードは明確に
|
|
29
|
+
エラーで案内)、モデル slug は必須。例:
|
|
30
|
+
`dispatch.sh --vendor openrouter --model anthropic/claude-sonnet-5 consult "..."`
|
|
31
|
+
- **`opencode` vendor** — マルチプロバイダ集約 CLI OpenCode 経由の
|
|
32
|
+
ヘッドレスディスパッチ(`opencode run`)。advise モードは組み込みの
|
|
33
|
+
読み取り専用 `plan` エージェントを使用し、work モードは `--auto`。
|
|
34
|
+
デフォルトの `coding-overflow` チェーンの最終フォールバックに追加。
|
|
35
|
+
|
|
36
|
+
## v0.8.1 の新機能
|
|
37
|
+
|
|
38
|
+
- **Claude Code プラグインがルーティングリマインダーを自動読み込み** —
|
|
39
|
+
プラグインに `SessionStart` フック(`hooks/hooks.json`)を同梱。
|
|
40
|
+
セッション開始時(`startup|resume|clear`)にルーティングリマインダーを
|
|
41
|
+
自動注入するため、`~/.claude/CLAUDE.md` を編集せずプラグインの
|
|
42
|
+
インストールだけで有効になります。他の CLI は引き続き `install.sh` の
|
|
43
|
+
指示ファイル方式です。
|
|
44
|
+
|
|
23
45
|
## v0.8.0 の新機能
|
|
24
46
|
|
|
25
47
|
- **2 つの新ディスパッチベンダー** — `kimi`(Moonshot Kimi Code CLI)と
|
|
@@ -140,7 +162,7 @@ flowchart LR
|
|
|
140
162
|
| 📚 long-context | Gemini 3.1 Pro (High) | Claude Opus 4.8 (high) | 100 万トークン級の長文統合——分析専用、agentic ループ禁止 |
|
|
141
163
|
| ⚡ fast-agentic | Gemini 3.5 Flash (High) | GPT-5.6 Luna (high) | 高速なマルチステップ agentic ループ、マルチモーダル確認 |
|
|
142
164
|
| 📡 live-search | Grok 4.5 | —(off) | リアルタイム X/ウェブ検索とソーシャル文脈 |
|
|
143
|
-
| 🚰 coding-overflow | Grok 4.5 | Kimi K3 → Qwen3 Coder Plus | Codex クォータ逼迫時の中級コーディング逃し弁 |
|
|
165
|
+
| 🚰 coding-overflow | Grok 4.5 | Kimi K3 → Qwen3 Coder Plus → OpenCode | Codex クォータ逼迫時の中級コーディング逃し弁 |
|
|
144
166
|
| 🗳️ arbitrate | off(オプトイン) | — | 内蔵オピニオンパネル(重大な判断用)——デフォルト無効。`routing.local.yaml` で有効化;投票者×ラウンドごとに 1 コール消費 |
|
|
145
167
|
|
|
146
168
|
**バックアップ**はチェーンの次の候補——第一候補のベンダー CLI が未インストールの
|
|
@@ -241,7 +263,9 @@ CLI バージョンにより異なる場合あり)へマーカー付きの可逆
|
|
|
241
263
|
`./install.sh --uninstall` を実行します。
|
|
242
264
|
|
|
243
265
|
- **Claude Code**:プラグインとしてインストール(`/route`、`/route-jobs`
|
|
244
|
-
|
|
266
|
+
コマンドに加え、セッション開始時にルーティングリマインダーを自動注入する
|
|
267
|
+
`SessionStart` フック付き。CLAUDE.md の編集は不要)、または
|
|
268
|
+
`skills/omnilane` を `~/.claude/skills/` へ。
|
|
245
269
|
- **Codex**:`skills/omnilane` を `~/.codex/skills/` へ配置/リンク。
|
|
246
270
|
- **Grok Build**:`grok plugin install <このリポジトリ> --trust`
|
|
247
271
|
- **Antigravity**:`agy plugin install <このリポジトリ>`(先に
|
package/README.ko.md
CHANGED
|
@@ -20,6 +20,27 @@
|
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
+
## v0.8.2 새 기능
|
|
24
|
+
|
|
25
|
+
- **`openrouter` vendor** — `curl`과 `OPENROUTER_API_KEY`만으로
|
|
26
|
+
OpenRouter API에 직접 디스패치합니다. 어떤 omnilane 설치에서도 수백 개의
|
|
27
|
+
호스팅 모델에 접근할 수 있으며 코딩 에이전트 CLI를 추가로 설치할 필요가
|
|
28
|
+
없습니다. advise/consult 전용(파일 편집 불가, work 모드는 명확한 오류로
|
|
29
|
+
안내)이며 모델 slug는 필수입니다. 예:
|
|
30
|
+
`dispatch.sh --vendor openrouter --model anthropic/claude-sonnet-5 consult "..."`
|
|
31
|
+
- **`opencode` vendor** — 멀티 프로바이더 집합 CLI OpenCode를 통한
|
|
32
|
+
헤드리스 디스패치(`opencode run`). advise 모드는 내장 읽기 전용 `plan`
|
|
33
|
+
에이전트를 사용하고 work 모드는 `--auto`를 사용합니다. 기본
|
|
34
|
+
`coding-overflow` 체인의 마지막 폴백으로 추가되었습니다.
|
|
35
|
+
|
|
36
|
+
## v0.8.1 새 기능
|
|
37
|
+
|
|
38
|
+
- **Claude Code 플러그인이 라우팅 리마인더를 자동 로드** — 플러그인에
|
|
39
|
+
`SessionStart` 훅(`hooks/hooks.json`)이 포함되어 세션 시작 시
|
|
40
|
+
(`startup|resume|clear`) 라우팅 리마인더를 자동 주입합니다.
|
|
41
|
+
`~/.claude/CLAUDE.md` 수정 없이 플러그인 설치만으로 적용됩니다.
|
|
42
|
+
다른 CLI는 기존대로 `install.sh` 지침 파일 방식을 사용합니다.
|
|
43
|
+
|
|
23
44
|
## v0.8.0 새 기능
|
|
24
45
|
|
|
25
46
|
- **새 디스패치 벤더 2종** — `kimi`(Moonshot Kimi Code CLI)와
|
|
@@ -140,7 +161,7 @@ flowchart LR
|
|
|
140
161
|
| 📚 long-context | Gemini 3.1 Pro (High) | Claude Opus 4.8 (high) | 100만 토큰급 장문 통합——분석 전용, agentic 루프 금지 |
|
|
141
162
|
| ⚡ fast-agentic | Gemini 3.5 Flash (High) | GPT-5.6 Luna (high) | 빠른 멀티스텝 agentic 루프, 멀티모달 확인 |
|
|
142
163
|
| 📡 live-search | Grok 4.5 | —(off) | 실시간 X/웹 검색과 소셜 맥락 |
|
|
143
|
-
| 🚰 coding-overflow | Grok 4.5 | Kimi K3 → Qwen3 Coder Plus | Codex 쿼터 소진 시 중급 코딩 안전 밸브 |
|
|
164
|
+
| 🚰 coding-overflow | Grok 4.5 | Kimi K3 → Qwen3 Coder Plus → OpenCode | Codex 쿼터 소진 시 중급 코딩 안전 밸브 |
|
|
144
165
|
| 🗳️ arbitrate | off(옵트인) | — | 내장 의견 패널(중대한 결정용)——기본 비활성. `routing.local.yaml` 에서 활성화;투표자×라운드마다 1콜 소모 |
|
|
145
166
|
|
|
146
167
|
**백업**은 체인의 다음 후보입니다——1순위 벤더 CLI 가 설치되지 않았을 때
|
|
@@ -236,8 +257,9 @@ omnilane ui stop # 정상 중지
|
|
|
236
257
|
설치 프로그램이 소유한 링크와 표시된 알림을 되돌리려면
|
|
237
258
|
`./install.sh --uninstall` 을 실행합니다.
|
|
238
259
|
|
|
239
|
-
- **Claude Code**: 플러그인으로 설치(`/route`, `/route-jobs`
|
|
240
|
-
|
|
260
|
+
- **Claude Code**: 플러그인으로 설치(`/route`, `/route-jobs` 명령과 함께
|
|
261
|
+
세션 시작 시 라우팅 리마인더를 자동 주입하는 `SessionStart` 훅 포함,
|
|
262
|
+
CLAUDE.md 수정 불필요), 또는 `skills/omnilane` 을 `~/.claude/skills/` 에 배치.
|
|
241
263
|
- **Codex**: `skills/omnilane` 을 `~/.codex/skills/` 에 배치/링크.
|
|
242
264
|
- **Grok Build**: `grok plugin install <이 저장소> --trust`
|
|
243
265
|
- **Antigravity**: `agy plugin install <이 저장소>`(먼저
|
package/README.md
CHANGED
|
@@ -20,6 +20,27 @@ Every subtask goes to the model that is actually best at it — across<br/>
|
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
+
## What's new in v0.8.2
|
|
24
|
+
|
|
25
|
+
- **`openrouter` vendor** — dispatch straight to the OpenRouter API with
|
|
26
|
+
nothing but `curl` and an `OPENROUTER_API_KEY`: hundreds of hosted models
|
|
27
|
+
become reachable from any omnilane install, no coding-agent CLI required.
|
|
28
|
+
Advise/consult only (it cannot edit files; work mode fails with guidance)
|
|
29
|
+
and the model slug is mandatory, e.g.
|
|
30
|
+
`dispatch.sh --vendor openrouter --model anthropic/claude-sonnet-5 consult "..."`.
|
|
31
|
+
- **`opencode` vendor** — headless dispatch through the OpenCode
|
|
32
|
+
multi-provider aggregator CLI (`opencode run`). Advise mode pins OpenCode's
|
|
33
|
+
built-in read-only `plan` agent; work mode uses `--auto`. Joins the default
|
|
34
|
+
`coding-overflow` chain as its last fallback.
|
|
35
|
+
|
|
36
|
+
## What's new in v0.8.1
|
|
37
|
+
|
|
38
|
+
- **Claude Code plugin auto-loads the routing reminder** — the plugin now
|
|
39
|
+
ships a `SessionStart` hook (`hooks/hooks.json`) that injects the routing
|
|
40
|
+
reminder at session open (`startup|resume|clear`), so plugin installs get
|
|
41
|
+
the persistent reminder with no edit to `~/.claude/CLAUDE.md`. The
|
|
42
|
+
`install.sh` instruction-file reminder still covers the other CLIs.
|
|
43
|
+
|
|
23
44
|
## What's new in v0.8.0
|
|
24
45
|
|
|
25
46
|
- **Two new dispatch vendors** — `kimi` (Moonshot Kimi Code CLI) and `qwen`
|
|
@@ -142,7 +163,7 @@ flowchart LR
|
|
|
142
163
|
| 📚 long-context | Gemini 3.1 Pro (High) | Claude Opus 4.8 (high) | 1M-token synthesis — analysis only, never agentic loops |
|
|
143
164
|
| ⚡ fast-agentic | Gemini 3.5 Flash (High) | GPT-5.6 Luna (high) | Fast multi-step agentic loops, multimodal checks |
|
|
144
165
|
| 📡 live-search | Grok 4.5 | — (off) | Realtime X/web search and social context |
|
|
145
|
-
| 🚰 coding-overflow | Grok 4.5 | Kimi K3 → Qwen3 Coder Plus | Codex-quota relief valve for mid-tier coding |
|
|
166
|
+
| 🚰 coding-overflow | Grok 4.5 | Kimi K3 → Qwen3 Coder Plus → OpenCode | Codex-quota relief valve for mid-tier coding |
|
|
146
167
|
| 🗳️ arbitrate | off (opt-in vote panel) | — | Built-in opinion panel for big calls — disabled by default; enable it in `routing.local.yaml`, one call per voter per round |
|
|
147
168
|
|
|
148
169
|
The **backup** is the next candidate in the lane's `routing.yaml` chain — what
|
|
@@ -243,7 +264,9 @@ Rollback the installer-owned links and marked reminders with
|
|
|
243
264
|
`./install.sh --uninstall`.
|
|
244
265
|
|
|
245
266
|
- **Claude Code**: install as a plugin (ships the skill + `/route`,
|
|
246
|
-
`/route-jobs` commands
|
|
267
|
+
`/route-jobs` commands, and a `SessionStart` hook that auto-injects the
|
|
268
|
+
routing reminder at session open — no CLAUDE.md edit needed), or drop
|
|
269
|
+
`skills/omnilane` into `~/.claude/skills/`.
|
|
247
270
|
- **Codex**: drop/symlink `skills/omnilane` into `~/.codex/skills/`.
|
|
248
271
|
- **Grok Build**: `grok plugin install <this repo> --trust`
|
|
249
272
|
- **Antigravity**: `agy plugin install <this repo>` (check first with
|
package/README.zh-CN.md
CHANGED
|
@@ -20,6 +20,24 @@
|
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
+
## v0.8.2 新功能
|
|
24
|
+
|
|
25
|
+
- **`openrouter` vendor** — 只需 `curl` 加一个 `OPENROUTER_API_KEY`,
|
|
26
|
+
即可直连 OpenRouter API 派工:任何 omnilane 安装都能访问数百个
|
|
27
|
+
托管模型,无需再装任何代理 CLI。仅限 advise/consult(不能改文件,
|
|
28
|
+
work 模式会明确报错指路),模型 slug 必填,例如
|
|
29
|
+
`dispatch.sh --vendor openrouter --model anthropic/claude-sonnet-5 consult "..."`。
|
|
30
|
+
- **`opencode` vendor** — 通过 OpenCode 多供应商聚合 CLI 无头派工
|
|
31
|
+
(`opencode run`)。advise 模式锁定内置只读 `plan` agent;work 模式
|
|
32
|
+
用 `--auto`。加入默认 `coding-overflow` 链作为最后回退。
|
|
33
|
+
|
|
34
|
+
## v0.8.1 新功能
|
|
35
|
+
|
|
36
|
+
- **Claude Code 插件开场自动载入路由提醒** — 插件新增 `SessionStart`
|
|
37
|
+
hook(`hooks/hooks.json`),在会话开始(`startup|resume|clear`)时自动
|
|
38
|
+
注入路由提醒,安装插件即生效,无需修改 `~/.claude/CLAUDE.md`。其他
|
|
39
|
+
CLI 仍使用 `install.sh` 的指令文件提醒。
|
|
40
|
+
|
|
23
41
|
## v0.8.0 新功能
|
|
24
42
|
|
|
25
43
|
- **两个新派工 vendor** — `kimi`(Moonshot Kimi Code CLI)与 `qwen`
|
|
@@ -127,7 +145,7 @@ flowchart LR
|
|
|
127
145
|
| 📚 long-context | Gemini 3.1 Pro (High) | Claude Opus 4.8 (high) | 百万 token 长文整合——仅限分析,不派 agentic 长链 |
|
|
128
146
|
| ⚡ fast-agentic | Gemini 3.5 Flash (High) | GPT-5.6 Luna (high) | 快速多步骤 agentic 循环、多模态检查 |
|
|
129
147
|
| 📡 live-search | Grok 4.5 | —(off) | 实时 X/网络搜索与社群脉络 |
|
|
130
|
-
| 🚰 coding-overflow | Grok 4.5 | Kimi K3 → Qwen3 Coder Plus | Codex 额度吃紧时的中量级编码溢流道;事实性声明须另行查证 |
|
|
148
|
+
| 🚰 coding-overflow | Grok 4.5 | Kimi K3 → Qwen3 Coder Plus → OpenCode | Codex 额度吃紧时的中量级编码溢流道;事实性声明须另行查证 |
|
|
131
149
|
| 🗳️ arbitrate | off(可选评审团) | — | 内置意见评审团,重大决定用——默认关闭,要用在 `routing.local.yaml` 打开;每评审每轮烧一次额度 |
|
|
132
150
|
|
|
133
151
|
**备选模型**是候选链的下一位——首选那家的厂商 CLI 没装时,派发就降到它。
|
|
@@ -212,7 +230,8 @@ Esc。服务器发送事件(SSE)会实时更新,又不会重建当前聚焦的
|
|
|
212
230
|
`--dry-run`,可先预览每个由这份 checkout 拥有的文件动作。
|
|
213
231
|
要回滚安装器拥有的链接与标记提示,执行 `./install.sh --uninstall`。
|
|
214
232
|
|
|
215
|
-
- **Claude Code**:以插件安装(附 `/route`、`/route-jobs`
|
|
233
|
+
- **Claude Code**:以插件安装(附 `/route`、`/route-jobs` 命令,并内置
|
|
234
|
+
`SessionStart` hook,会话开始时自动注入路由提醒,无需修改 CLAUDE.md),
|
|
216
235
|
或把 `skills/omnilane` 放进 `~/.claude/skills/`。
|
|
217
236
|
- **Codex**:把 `skills/omnilane` 放进或链接到 `~/.codex/skills/`。
|
|
218
237
|
- **Grok Build**:`grok plugin install <本仓库路径> --trust`
|
package/README.zh-TW.md
CHANGED
|
@@ -20,6 +20,24 @@
|
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
+
## v0.8.2 新功能
|
|
24
|
+
|
|
25
|
+
- **`openrouter` vendor** — 只要 `curl` 加一把 `OPENROUTER_API_KEY`,
|
|
26
|
+
就能直連 OpenRouter API 派工:任何 omnilane 安裝都摸得到數百個
|
|
27
|
+
託管模型,不必再裝任何代理 CLI。僅限 advise/consult(不能改檔,
|
|
28
|
+
work 模式會明確報錯指路),模型 slug 必填,例如
|
|
29
|
+
`dispatch.sh --vendor openrouter --model anthropic/claude-sonnet-5 consult "..."`。
|
|
30
|
+
- **`opencode` vendor** — 透過 OpenCode 多供應商聚合 CLI 無頭派工
|
|
31
|
+
(`opencode run`)。advise 模式鎖定內建唯讀 `plan` agent;work 模式
|
|
32
|
+
用 `--auto`。加入預設 `coding-overflow` 鏈作為最後備援。
|
|
33
|
+
|
|
34
|
+
## v0.8.1 新功能
|
|
35
|
+
|
|
36
|
+
- **Claude Code 外掛開場自動載入路由提醒** — 外掛新增 `SessionStart`
|
|
37
|
+
hook(`hooks/hooks.json`),於開場(`startup|resume|clear`)自動注入
|
|
38
|
+
路由提醒,裝外掛即生效,不必修改 `~/.claude/CLAUDE.md`。其他 CLI
|
|
39
|
+
仍走 `install.sh` 的指令檔提醒。
|
|
40
|
+
|
|
23
41
|
## v0.8.0 新功能
|
|
24
42
|
|
|
25
43
|
- **兩個新派工 vendor** — `kimi`(Moonshot Kimi Code CLI)與 `qwen`
|
|
@@ -127,7 +145,7 @@ flowchart LR
|
|
|
127
145
|
| 📚 long-context | Gemini 3.1 Pro (High) | Claude Opus 4.8 (high) | 百萬 token 長文整合——僅限分析,不派 agentic 長鏈 |
|
|
128
146
|
| ⚡ fast-agentic | Gemini 3.5 Flash (High) | GPT-5.6 Luna (high) | 快速多步驟 agentic 迴圈、多模態檢查 |
|
|
129
147
|
| 📡 live-search | Grok 4.5 | —(off) | 即時 X/網路搜尋與社群脈絡 |
|
|
130
|
-
| 🚰 coding-overflow | Grok 4.5 | Kimi K3 → Qwen3 Coder Plus | Codex 額度吃緊時的中量級編碼溢流道;事實性宣稱須另行查證 |
|
|
148
|
+
| 🚰 coding-overflow | Grok 4.5 | Kimi K3 → Qwen3 Coder Plus → OpenCode | Codex 額度吃緊時的中量級編碼溢流道;事實性宣稱須另行查證 |
|
|
131
149
|
| 🗳️ arbitrate | off(選配評審團) | — | 內建意見評審團,重大決定用——預設關閉,要用在 `routing.local.yaml` 開;每評審每輪燒一次額度 |
|
|
132
150
|
|
|
133
151
|
**備選模型**是候選鏈的下一位——首選那家的廠商 CLI 沒裝時,派工就降到它。
|
|
@@ -213,7 +231,8 @@ omnilane ui stop # 正常停止
|
|
|
213
231
|
|
|
214
232
|
要回滾安裝器擁有的連結與標記提示,執行 `./install.sh --uninstall`。
|
|
215
233
|
|
|
216
|
-
- **Claude Code**:以外掛安裝(附 `/route`、`/route-jobs`
|
|
234
|
+
- **Claude Code**:以外掛安裝(附 `/route`、`/route-jobs` 指令,並內建
|
|
235
|
+
`SessionStart` hook,開場自動注入路由提醒,不必修改 CLAUDE.md),
|
|
217
236
|
或把 `skills/omnilane` 放進 `~/.claude/skills/`。
|
|
218
237
|
- **Codex**:把 `skills/omnilane` 放進或連結到 `~/.codex/skills/`。
|
|
219
238
|
- **Grok Build**:`grok plugin install <本 repo 路徑> --trust`
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.8.
|
|
1
|
+
0.8.2
|
package/completions/_omnilane
CHANGED
|
@@ -64,7 +64,7 @@ _omnilane() {
|
|
|
64
64
|
'--dry-run[resolve and print the plan without side effects]' \
|
|
65
65
|
'--mode[execution mode]:mode:(advise work)' \
|
|
66
66
|
'--workdir[target directory]:directory:_directories' \
|
|
67
|
-
'--vendor[vendor override]:vendor:(codex claude grok gemini exec vote)' \
|
|
67
|
+
'--vendor[vendor override]:vendor:(codex claude grok gemini kimi qwen opencode openrouter exec vote)' \
|
|
68
68
|
'--model[model override]:model:' \
|
|
69
69
|
'--effort[reasoning effort]:effort:(low medium high xhigh max)' \
|
|
70
70
|
'--timeout[per-call seconds]:seconds:' \
|
|
@@ -64,7 +64,7 @@ _omnilane() {
|
|
|
64
64
|
route|dispatch)
|
|
65
65
|
case "$prev" in
|
|
66
66
|
--mode) words="advise work" ;;
|
|
67
|
-
--vendor) words="codex claude grok gemini kimi qwen exec vote" ;;
|
|
67
|
+
--vendor) words="codex claude grok gemini kimi qwen opencode openrouter exec vote" ;;
|
|
68
68
|
--effort) words="low medium high xhigh max" ;;
|
|
69
69
|
--workdir)
|
|
70
70
|
COMPREPLY=()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omnilane",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "One routing table, every harness — classify subtasks into lanes and dispatch each lane to the best vendor's agentic CLI (Codex, Claude, Gemini, Grok) using your existing subscription logins.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"omnilane": "bin/omnilane"
|
package/routing.yaml
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# omnilane — single routing table shared by every harness (CC / Codex / Grok / Antigravity)
|
|
2
2
|
# Format: one lane per line -> lane: vendor model effort [| vendor model effort | off]
|
|
3
|
-
# vendor = codex | claude | grok | gemini
|
|
3
|
+
# vendor = codex | claude | grok | gemini | kimi | qwen | opencode | openrouter
|
|
4
|
+
# openrouter is direct-API (needs only OPENROUTER_API_KEY + curl) and advise/consult
|
|
5
|
+
# only — it cannot edit files, so it is not in the default work-capable chains.
|
|
4
6
|
# effort = codex reasoning effort / claude --effort / agy thinking suffix / (grok: ignored)
|
|
5
7
|
# Fallback chain: the first candidate whose vendor CLI is installed wins, so the
|
|
6
8
|
# same table degrades gracefully when you only subscribe to one or two vendors.
|
|
@@ -22,7 +24,7 @@ ui-draft: codex gpt-5.6-sol xhigh | claude claude-opus-4-8 high # only w
|
|
|
22
24
|
long-context: gemini "Gemini 3.1 Pro (High)" - | claude claude-opus-4-8 high | codex gpt-5.6-sol high # 1M-token synthesis ONLY — never agentic loops (AA agentic 21.4)
|
|
23
25
|
fast-agentic: gemini "Gemini 3.5 Flash (High)" - | codex gpt-5.6-luna high # speed + agentic tool loops
|
|
24
26
|
live-search: grok grok-4.5 - | off # native X/web search lane; no real substitute
|
|
25
|
-
coding-overflow: grok grok-4.5 - | kimi kimi-k3 - | qwen qwen3-coder-plus - | off # codex-quota relief valve: mid-tier coding; verify factual claims (grok hallucination-prone). kimi/qwen model fields are CLI aliases — adjust to your login.
|
|
27
|
+
coding-overflow: grok grok-4.5 - | kimi kimi-k3 - | qwen qwen3-coder-plus - | opencode - - | off # codex-quota relief valve: mid-tier coding; verify factual claims (grok hallucination-prone). kimi/qwen model fields are CLI aliases — adjust to your login. opencode "-" model = its own configured default.
|
|
26
28
|
arbitrate: off - - # opinion panel is OPT-IN: it costs one call per voter per round.
|
|
27
29
|
# Enable: `arbitrate: vote codex,claude,grok -` (any 1-4 of codex/claude/grok/gemini)
|
|
28
30
|
# Debate round (each voter rebuts the others): set the effort field to 2.
|
package/scripts/configure.sh
CHANGED
|
@@ -18,6 +18,9 @@ GEMINI_MODELS=("Gemini 3.1 Pro (High)" "Gemini 3.1 Pro (Low)" "Gemini 3.5 Flash
|
|
|
18
18
|
GROK_MODELS=("grok-4.5" "grok-4.3")
|
|
19
19
|
KIMI_MODELS=("kimi-k3" "kimi-k2.7-code")
|
|
20
20
|
QWEN_MODELS=("qwen3-coder-plus" "qwen3-coder-flash")
|
|
21
|
+
# OpenCode models use provider/model form; OpenRouter models are catalog slugs.
|
|
22
|
+
OPENCODE_MODELS=("openrouter/anthropic/claude-sonnet-5" "openrouter/openai/gpt-5.6-sol" "opencode/default (leave model to opencode)")
|
|
23
|
+
OPENROUTER_MODELS=("anthropic/claude-sonnet-5" "openai/gpt-5.6-sol" "moonshotai/kimi-k3" "qwen/qwen3-coder-plus")
|
|
21
24
|
|
|
22
25
|
custom_value_is_safe() {
|
|
23
26
|
case "$1" in
|
|
@@ -76,7 +79,7 @@ while true; do
|
|
|
76
79
|
echo "$(msgf cfg_pick_range "${#LANES[@]}")"; continue
|
|
77
80
|
fi
|
|
78
81
|
lane="${LANES[$((n - 1))]}"
|
|
79
|
-
vendor="$(pick "$(msgf cfg_vendor_for "$lane")" codex claude grok gemini kimi qwen "vote (multi-model panel)" "exec (your own script/gate)" off)"
|
|
82
|
+
vendor="$(pick "$(msgf cfg_vendor_for "$lane")" codex claude grok gemini kimi qwen opencode openrouter "vote (multi-model panel)" "exec (your own script/gate)" off)"
|
|
80
83
|
[[ "$vendor" == exec* ]] && vendor="exec"
|
|
81
84
|
[[ "$vendor" == vote* ]] && vendor="vote"
|
|
82
85
|
if [[ "$vendor" == "off" ]]; then
|
|
@@ -90,6 +93,9 @@ while true; do
|
|
|
90
93
|
grok) model="$(pick "$(msg cfg_model)" "${GROK_MODELS[@]}")"; effort="-" ;;
|
|
91
94
|
kimi) model="$(pick "$(msg cfg_model)" "${KIMI_MODELS[@]}")"; effort="-" ;;
|
|
92
95
|
qwen) model="$(pick "$(msg cfg_model)" "${QWEN_MODELS[@]}")"; effort="-" ;;
|
|
96
|
+
opencode) model="$(pick "$(msg cfg_model)" "${OPENCODE_MODELS[@]}")"; effort="-"
|
|
97
|
+
[[ "$model" == "opencode/default"* ]] && model="-" ;;
|
|
98
|
+
openrouter) model="$(pick "$(msg cfg_model)" "${OPENROUTER_MODELS[@]}")"; effort="-" ;;
|
|
93
99
|
vote) while true; do
|
|
94
100
|
count="$(pick "$(msg cfg_voters_count)" "1" "2" "3" "4")"
|
|
95
101
|
[[ "$count" =~ ^[1-4]$ ]] && break
|
package/scripts/dispatch.sh
CHANGED
|
@@ -52,7 +52,7 @@ flags:
|
|
|
52
52
|
before any provider call or job state
|
|
53
53
|
--mode advise|work advise (read-only, default) or work (may edit files)
|
|
54
54
|
--workdir DIR working directory handed to the vendor CLI
|
|
55
|
-
--vendor V pin one configured vendor (codex|claude|grok|gemini|kimi|qwen)
|
|
55
|
+
--vendor V pin one configured vendor (codex|claude|grok|gemini|kimi|qwen|opencode|openrouter)
|
|
56
56
|
--model M override the routed model
|
|
57
57
|
--effort E override the routed effort
|
|
58
58
|
--timeout SECONDS cap each CLI call (default 600)
|
|
@@ -363,7 +363,7 @@ validate_routing() {
|
|
|
363
363
|
lane_invalid=1
|
|
364
364
|
break
|
|
365
365
|
fi
|
|
366
|
-
if ! [[ "$vendor" =~ ^(codex|claude|grok|gemini|kimi|qwen|exec|off)$ ]]; then
|
|
366
|
+
if ! [[ "$vendor" =~ ^(codex|claude|grok|gemini|kimi|qwen|opencode|openrouter|exec|off)$ ]]; then
|
|
367
367
|
printf 'FAIL %s unknown-vendor=%s\n' "$lane" "$vendor"
|
|
368
368
|
lane_invalid=1
|
|
369
369
|
break
|
|
@@ -489,8 +489,8 @@ TASK="$2"
|
|
|
489
489
|
# A typo like --mode advice must not fall through to the write-enabled branch.
|
|
490
490
|
[[ "$MODE" == "advise" || "$MODE" == "work" ]] || { echo "omnilane: invalid --mode (advise|work)" >&2; exit 2; }
|
|
491
491
|
if [[ -n "$OVERRIDE_VENDOR" ]] &&
|
|
492
|
-
! [[ "$OVERRIDE_VENDOR" =~ ^(codex|claude|grok|gemini|kimi|qwen)$ ]]; then
|
|
493
|
-
echo "omnilane: invalid vendor (codex|claude|grok|gemini|kimi|qwen)" >&2
|
|
492
|
+
! [[ "$OVERRIDE_VENDOR" =~ ^(codex|claude|grok|gemini|kimi|qwen|opencode|openrouter)$ ]]; then
|
|
493
|
+
echo "omnilane: invalid vendor (codex|claude|grok|gemini|kimi|qwen|opencode|openrouter)" >&2
|
|
494
494
|
exit 2
|
|
495
495
|
fi
|
|
496
496
|
|
package/scripts/jobs.sh
CHANGED
|
@@ -132,7 +132,7 @@ read_job_pid() {
|
|
|
132
132
|
|
|
133
133
|
parse_stats_metadata() {
|
|
134
134
|
local value="$1"
|
|
135
|
-
local metadata_re='^\{"lane":"([a-z][a-z0-9-]*)","vendor":"(codex|claude|grok|gemini|kimi|qwen|exec)"(,|})'
|
|
135
|
+
local metadata_re='^\{"lane":"([a-z][a-z0-9-]*)","vendor":"(codex|claude|grok|gemini|kimi|qwen|opencode|openrouter|exec)"(,|})'
|
|
136
136
|
STATS_LANE=""
|
|
137
137
|
STATS_VENDOR=""
|
|
138
138
|
[[ "$value" =~ $metadata_re ]] || return 1
|
|
@@ -143,7 +143,7 @@ parse_stats_metadata() {
|
|
|
143
143
|
parse_audit_metadata() {
|
|
144
144
|
local value="$1" metadata_re
|
|
145
145
|
local json_string='([^"\\]|\\["\\/bfnrt]|\\u[0-9a-fA-F]{4})*'
|
|
146
|
-
metadata_re="^\\{\"lane\":\"[a-z][a-z0-9-]*\",\"vendor\":\"(codex|claude|grok|gemini|kimi|qwen|exec)\",\"model\":\"${json_string}\",\"effort\":\"${json_string}\",\"timeout\":(0|[1-9][0-9]*),\"job_timeout\":(null|0|[1-9][0-9]*),\"mode\":\"(advise|work)\",\"workdir\":\"${json_string}\",\"candidate\":\"[1-9][0-9]*/[1-9][0-9]*\",\"started\":\"[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z\"\\}$"
|
|
146
|
+
metadata_re="^\\{\"lane\":\"[a-z][a-z0-9-]*\",\"vendor\":\"(codex|claude|grok|gemini|kimi|qwen|opencode|openrouter|exec)\",\"model\":\"${json_string}\",\"effort\":\"${json_string}\",\"timeout\":(0|[1-9][0-9]*),\"job_timeout\":(null|0|[1-9][0-9]*),\"mode\":\"(advise|work)\",\"workdir\":\"${json_string}\",\"candidate\":\"[1-9][0-9]*/[1-9][0-9]*\",\"started\":\"[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z\"\\}$"
|
|
147
147
|
[[ "$value" =~ $metadata_re ]]
|
|
148
148
|
}
|
|
149
149
|
|
|
@@ -252,7 +252,7 @@ case "${1:-}" in
|
|
|
252
252
|
read_public_metadata "$JOB_DIR/meta.json" || {
|
|
253
253
|
die 1 "cannot retry: unreadable job metadata"
|
|
254
254
|
}
|
|
255
|
-
retry_re='^\{"lane":"([a-z][a-z0-9-]*)","vendor":"(codex|claude|grok|gemini|kimi|qwen|exec)","model":"([^"\\]*)","effort":"([^"\\]*)","timeout":([1-9][0-9]*),"job_timeout":([1-9][0-9]*|null),"mode":"(advise|work)","workdir":"([^"\\]*)",'
|
|
255
|
+
retry_re='^\{"lane":"([a-z][a-z0-9-]*)","vendor":"(codex|claude|grok|gemini|kimi|qwen|opencode|openrouter|exec)","model":"([^"\\]*)","effort":"([^"\\]*)","timeout":([1-9][0-9]*),"job_timeout":([1-9][0-9]*|null),"mode":"(advise|work)","workdir":"([^"\\]*)",'
|
|
256
256
|
[[ "$PUBLIC_METADATA" =~ $retry_re ]] || {
|
|
257
257
|
die 1 "cannot retry: job metadata is not safely parseable"
|
|
258
258
|
}
|
package/scripts/lib/common.sh
CHANGED
|
@@ -72,6 +72,7 @@ vendor_bin() { # vendor -> binary (honors local.sh overrides)
|
|
|
72
72
|
gemini) echo "${AGY_BIN:-agy}" ;;
|
|
73
73
|
kimi) echo "${KIMI_BIN:-kimi}" ;;
|
|
74
74
|
qwen) echo "${QWEN_BIN:-qwen}" ;;
|
|
75
|
+
opencode) echo "${OPENCODE_BIN:-opencode}" ;;
|
|
75
76
|
*) echo "" ;;
|
|
76
77
|
esac
|
|
77
78
|
}
|
|
@@ -81,6 +82,11 @@ vendor_available() {
|
|
|
81
82
|
# checked at dispatch time (the chain resolver cannot see it here).
|
|
82
83
|
# "vote" is the built-in multi-model panel: needs >=2 voters, checked at dispatch.
|
|
83
84
|
[[ "$1" == "exec" || "$1" == "vote" ]] && return 0
|
|
85
|
+
# "openrouter" is direct-API (no CLI): available iff curl and a key exist.
|
|
86
|
+
if [[ "$1" == "openrouter" ]]; then
|
|
87
|
+
command -v curl >/dev/null 2>&1 && [[ -n "${OPENROUTER_API_KEY:-}" ]]
|
|
88
|
+
return
|
|
89
|
+
fi
|
|
84
90
|
local b; b="$(vendor_bin "$1")"
|
|
85
91
|
[[ -n "$b" ]] && command -v "$b" >/dev/null 2>&1
|
|
86
92
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# omnilane runner: OpenCode CLI (multi-provider aggregator)
|
|
5
|
+
#
|
|
6
|
+
# Usage: run-opencode.sh MODE WORKDIR MODEL EFFORT PROMPT_FILE OUTPUT_FILE
|
|
7
|
+
# MODE = advise (built-in read-only plan agent) | work (may edit files)
|
|
8
|
+
#
|
|
9
|
+
# MODEL is OpenCode's provider/model form (e.g. openrouter/moonshotai/kimi-k2);
|
|
10
|
+
# "-" lets OpenCode use its own configured default. OpenCode's --variant flag
|
|
11
|
+
# is provider-specific, so EFFORT maps onto it only when set.
|
|
12
|
+
|
|
13
|
+
source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
|
|
14
|
+
|
|
15
|
+
MODE="$1"; WORKDIR="$2"; MODEL="$3"; EFFORT="$4"; PROMPT_FILE="$5"; OUTPUT_FILE="$6"
|
|
16
|
+
|
|
17
|
+
OPENCODE_BIN="${OPENCODE_BIN:-opencode}"
|
|
18
|
+
RUN_TIMEOUT="${OMNILANE_TIMEOUT:-600}"
|
|
19
|
+
|
|
20
|
+
truncate_payload "$PROMPT_FILE" 140000
|
|
21
|
+
|
|
22
|
+
# --agent plan is OpenCode's built-in restricted agent (edits/bash gated);
|
|
23
|
+
# --auto only in work mode, and only for permissions not explicitly denied.
|
|
24
|
+
ARGS=(run "$(cat "$PROMPT_FILE")" --format default)
|
|
25
|
+
[[ -n "$MODEL" && "$MODEL" != "-" ]] && ARGS+=(-m "$MODEL")
|
|
26
|
+
[[ -n "$EFFORT" && "$EFFORT" != "-" ]] && ARGS+=(--variant "$EFFORT")
|
|
27
|
+
if [[ "$MODE" == "advise" ]]; then
|
|
28
|
+
ARGS+=(--agent plan)
|
|
29
|
+
else
|
|
30
|
+
ARGS+=(--auto)
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
set +e
|
|
34
|
+
(
|
|
35
|
+
cd "$WORKDIR" || exit 127
|
|
36
|
+
OMNILANE_DEPTH=1 run_with_timeout "$RUN_TIMEOUT" \
|
|
37
|
+
"$OPENCODE_BIN" "${ARGS[@]}" \
|
|
38
|
+
> "${OUTPUT_FILE}.tmp" 2> "${OUTPUT_FILE}.stderr.log"
|
|
39
|
+
)
|
|
40
|
+
RC=$?
|
|
41
|
+
set -e
|
|
42
|
+
|
|
43
|
+
# Empty output is a failure, not a silent rc=0 success.
|
|
44
|
+
if ! grep -q '[^[:space:]]' "${OUTPUT_FILE}.tmp" 2>/dev/null; then
|
|
45
|
+
echo "omnilane: opencode produced no output" >> "${OUTPUT_FILE}.stderr.log"
|
|
46
|
+
[[ "$RC" -eq 0 ]] && RC=1
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
[[ -f "${OUTPUT_FILE}.tmp" ]] && mv "${OUTPUT_FILE}.tmp" "$OUTPUT_FILE"
|
|
50
|
+
[[ -s "${OUTPUT_FILE}.stderr.log" ]] || rm "${OUTPUT_FILE}.stderr.log" 2>/dev/null || true
|
|
51
|
+
exit "$RC"
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# omnilane runner: OpenRouter direct API (no CLI dependency)
|
|
5
|
+
#
|
|
6
|
+
# Usage: run-openrouter.sh MODE WORKDIR MODEL EFFORT PROMPT_FILE OUTPUT_FILE
|
|
7
|
+
# MODE = advise only. This vendor is pure inference over HTTPS — it has no
|
|
8
|
+
# agentic loop and cannot edit files, so work mode is a hard error rather
|
|
9
|
+
# than a silent read-only downgrade.
|
|
10
|
+
#
|
|
11
|
+
# MODEL is an OpenRouter model slug (e.g. anthropic/claude-sonnet-4). EFFORT
|
|
12
|
+
# is accepted for interface parity only. Needs OPENROUTER_API_KEY and curl;
|
|
13
|
+
# python3 builds/parses the JSON so prompt content never needs shell escaping.
|
|
14
|
+
|
|
15
|
+
source "$(dirname "${BASH_SOURCE[0]}")/../lib/common.sh"
|
|
16
|
+
|
|
17
|
+
MODE="$1"; WORKDIR="$2"; MODEL="$3"; EFFORT="$4"; PROMPT_FILE="$5"; OUTPUT_FILE="$6"
|
|
18
|
+
: "$WORKDIR" "$EFFORT" # parity with the uniform runner interface
|
|
19
|
+
|
|
20
|
+
OPENROUTER_BASE_URL="${OPENROUTER_BASE_URL:-https://openrouter.ai/api/v1}"
|
|
21
|
+
RUN_TIMEOUT="${OMNILANE_TIMEOUT:-600}"
|
|
22
|
+
|
|
23
|
+
fail() { echo "omnilane: $1" > "${OUTPUT_FILE}.stderr.log"; exit 2; }
|
|
24
|
+
|
|
25
|
+
[[ "$MODE" == "advise" ]] || fail "openrouter is inference-only: use --mode advise (work mode needs an agentic CLI vendor like opencode)"
|
|
26
|
+
[[ -n "${OPENROUTER_API_KEY:-}" ]] || fail "OPENROUTER_API_KEY is not set"
|
|
27
|
+
[[ -n "$MODEL" && "$MODEL" != "-" ]] || fail "openrouter needs an explicit model slug (e.g. anthropic/claude-sonnet-4)"
|
|
28
|
+
command -v curl >/dev/null 2>&1 || fail "curl not found"
|
|
29
|
+
command -v python3 >/dev/null 2>&1 || fail "python3 not found"
|
|
30
|
+
|
|
31
|
+
truncate_payload "$PROMPT_FILE" 140000
|
|
32
|
+
|
|
33
|
+
REQUEST_FILE="${OUTPUT_FILE}.request.json"
|
|
34
|
+
RESPONSE_FILE="${OUTPUT_FILE}.response.json"
|
|
35
|
+
cleanup() { rm "$REQUEST_FILE" "$RESPONSE_FILE" 2>/dev/null || true; }
|
|
36
|
+
trap cleanup EXIT
|
|
37
|
+
|
|
38
|
+
python3 - "$MODEL" "$PROMPT_FILE" > "$REQUEST_FILE" <<'PY'
|
|
39
|
+
import json, sys
|
|
40
|
+
model, prompt_file = sys.argv[1], sys.argv[2]
|
|
41
|
+
with open(prompt_file, encoding="utf-8", errors="replace") as f:
|
|
42
|
+
prompt = f.read()
|
|
43
|
+
json.dump({"model": model, "messages": [{"role": "user", "content": prompt}]},
|
|
44
|
+
sys.stdout)
|
|
45
|
+
PY
|
|
46
|
+
|
|
47
|
+
set +e
|
|
48
|
+
run_with_timeout "$RUN_TIMEOUT" curl -sS --fail-with-body \
|
|
49
|
+
--max-time "$RUN_TIMEOUT" \
|
|
50
|
+
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
|
|
51
|
+
-H "Content-Type: application/json" \
|
|
52
|
+
-X POST "$OPENROUTER_BASE_URL/chat/completions" \
|
|
53
|
+
--data-binary "@$REQUEST_FILE" \
|
|
54
|
+
> "$RESPONSE_FILE" 2> "${OUTPUT_FILE}.stderr.log"
|
|
55
|
+
RC=$?
|
|
56
|
+
set -e
|
|
57
|
+
|
|
58
|
+
if [[ "$RC" -ne 0 ]]; then
|
|
59
|
+
# Surface the API error body (never the key) alongside curl's own stderr.
|
|
60
|
+
[[ -s "$RESPONSE_FILE" ]] && cat "$RESPONSE_FILE" >> "${OUTPUT_FILE}.stderr.log"
|
|
61
|
+
exit "$RC"
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
set +e
|
|
65
|
+
python3 - "$RESPONSE_FILE" > "${OUTPUT_FILE}.tmp" 2>> "${OUTPUT_FILE}.stderr.log" <<'PY'
|
|
66
|
+
import json, sys
|
|
67
|
+
with open(sys.argv[1], encoding="utf-8", errors="replace") as f:
|
|
68
|
+
data = json.load(f)
|
|
69
|
+
if "error" in data:
|
|
70
|
+
print(f"omnilane: openrouter API error: {data['error']}", file=sys.stderr)
|
|
71
|
+
sys.exit(1)
|
|
72
|
+
content = data["choices"][0]["message"]["content"]
|
|
73
|
+
sys.stdout.write(content if isinstance(content, str) else json.dumps(content))
|
|
74
|
+
PY
|
|
75
|
+
RC=$?
|
|
76
|
+
set -e
|
|
77
|
+
|
|
78
|
+
# Empty output is a failure, not a silent rc=0 success.
|
|
79
|
+
if ! grep -q '[^[:space:]]' "${OUTPUT_FILE}.tmp" 2>/dev/null; then
|
|
80
|
+
echo "omnilane: openrouter produced no output" >> "${OUTPUT_FILE}.stderr.log"
|
|
81
|
+
[[ "$RC" -eq 0 ]] && RC=1
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
[[ -f "${OUTPUT_FILE}.tmp" ]] && mv "${OUTPUT_FILE}.tmp" "$OUTPUT_FILE"
|
|
85
|
+
[[ -s "${OUTPUT_FILE}.stderr.log" ]] || rm "${OUTPUT_FILE}.stderr.log" 2>/dev/null || true
|
|
86
|
+
exit "$RC"
|