Repository navigation
OriginRouter CLI v0.4.9
OriginRouter CLI v0.4.9
English
This release fixes a class of failures where a session's route or environment was correct but something else silently replaced it, and it rebuilds shell completion so that it describes the whole command surface — on Windows, including the PowerShell that plain powershell actually starts.
Session environment and settings files
- Claude Code merges its own settings files above the subprocess environment, so a stale
~/.claude/settings.jsonenv block outranked the resolved route and replaced it — with nothing reported. The route is now pinned through the--settings(flagSettings) layer, which outranks every filesystem settings layer except enterprise managed settings. The SDK launch path and the PTY launch path now pin the same key set; the SDK path previously named five env keys by hand and leftCLAUDE_CODE_SUBAGENT_MODELand the entireANTHROPIC_DEFAULT_*_MODELfamily — the keys a stale settings file is most likely to hold — unpinned. - Report settings conflicts instead of resolving them silently. A conflicting layer is named at launch and by
originrouter env print, so a correct route that a settings file was overriding no longer looks like a routing failure. - Fix
originrouter env printdescribing only the process environment. It printed "(unset)" for the very keys a settings file was forcing. It now lists the settings layers that outrank the process environment, the transport keys each one sets, and whether OriginRouter overrides them. Credential keys are named, never their values; enterprise managed settings are identified as outranking OriginRouter.
Shell completion
- Derive completion from a single command catalog instead of a hand-maintained subset, so subcommands, options, and value completions can no longer drift apart from the command tree they describe.
- Refresh completion after a successful
originrouter update. A new version can generate a different script, and on Windows an older version may have written into a profile the running PowerShell never reads. Only profiles that already carry a completion block are touched — updating the CLI is never consent to configure a shell that was never configured. A failure in the refresh cannot turn a successful upgrade into a failed command.
Windows
- Install PowerShell completion into both profiles. Windows ships two PowerShells that read different files: Windows PowerShell 5.1 (what plain
powershellstarts) readsDocuments\WindowsPowerShell\Microsoft.PowerShell_profile.ps1, and PowerShell 7+ (pwsh) readsDocuments\PowerShell\Microsoft.PowerShell_profile.ps1. Only the 7+ path was written, so completion was installed yet never loaded for 5.1 users. Uninstall now clears both. - Fix completion for an empty word. PowerShell 5.1 silently drops a trailing empty argument when it invokes a native command, so
provider <TAB>reached the CLI asprovider, and the CLI echoed that word back instead of offering its subcommands. An empty string cannot be passed at all in 5.1 — a literal, an empty variable,String.Empty, and a splat all arrive as nothing, while" "survives — so the generated script sends a sentinel that the CLI maps back to an empty word. Only the last word is translated; anything before it is complete typed input. - Fix
originrouter loginandoriginrouter run --failing to execute npm-installed commands when the CLI is installed byscripts/install.ps1.
Security
- Make the Codex app-server provider id unique per process. Codex merges its config layers by key, and a table merges field by field rather than replacing wholesale. Our
-coverrides land in the SessionFlags layer and beat a userconfig.tomlfor every field we set — but any field we do not set under the same provider id survives from the lower layer. Measured against Codex 0.156.1 with a fixed id, a[model_providers.<id>]block in the user'sconfig.tomlsmuggledenv_http_headers,http_headers, andquery_paramsonto the very request our own credential authorized, so the user's key left the machine to whatever endpoint the route pointed at. An empty-table override does not clear them — the merge walks the overlay's keys, so an empty overlay contributes nothing. Choosing an id the user cannot have written is what removes the merge partner, and it costs nothing because the id is an internal handle Codex only resolves againstmodel_provider. - Pin both Anthropic credential variables when a route is applied, not only the unused one. A route that sets
ANTHROPIC_API_KEYleftANTHROPIC_AUTH_TOKENabsent, and an absent key is supplied by a lower settings layer instead. Measured against Claude Code 2.1.283, a staleANTHROPIC_AUTH_TOKENin~/.claude/settings.jsonthen rode out as aBearertoken alongside our ownANTHROPIC_API_KEY, and Claude Code prefers that bearer token — so the proxy was handed the user's stale credential and the user's own key left the machine. Pinning both also keeps Claude Code's "both ANTHROPIC_AUTH_TOKEN and ANTHROPIC_API_KEY set" warning from returning.
Session and App state
- Stop reporting an active turn for a session that is online but idle.
session.statusdescribes the long-lived CLI process — a healthy idle Agent sits atrunningfor hours — so treating it as turn activity opened the App with a stop button and flickered against every refresh. Turn activity is latched by turn events alone. - Make the control snapshot and the session projection agree. The snapshot published the raw turn latch while the session published a gated projection, so the App alternated between two answers on every refresh.
- Keep a waiting state alive across an approval that expires without an event. Pending interactions are now the authoritative source for the waiting states, which a turn-event latch cannot observe.
- Stop resurrecting a finished turn when an Agent reconnects. Registration is treated as a fresh attach, so a turn that ended while the App was away no longer inherits the previous
runninglatch and shows a stop button until a terminal event that never comes.
Command discovery
- Document the
local apifamily.originrouter helpomitted it entirely, includinglocal api pair, which pairs an App with this machine's Local API — so the pairing flow had no discoverable entry point. The reference now listsstatus,pair,connect,set-host, andset-port, and explains that the pairing line never carries the access key. - Stop offering a subcommand the CLI rejects.
auth logoutwas declared in the command catalog but never implemented: TAB offered it, and choosing it failed with "Unknown auth subcommand". Signing out isoriginrouter logout. - Check catalog subcommands for reachability, not just declaration. A name could be declared and offered by completion while no dispatch branch accepted it. Each entry is now resolved against the module that owns its branch, and an exemption has to state its reason. The help text stays hand-authored (its sections are grouped by concept, not tree position) but is checked in both directions, so a documented-but-absent command and an undocumented-but-present one both fail.
Validation: the full CLI test suite passed, including new tests for the command catalog, completion (the sentinel and the dual-profile install), settings overrides, Codex provider isolation, env print settings layers, and turn-state projection. The catalog check was mutation-tested — re-adding the removed auth logout entry, adding an unimplemented subcommand, deleting a help section, and un-hiding an internal command each made it fail. The refresh-on-upgrade path is covered by a test that proves a failure inside it can never turn a successful upgrade into a failed command. Shell completion was exercised end to end in real Bash, Zsh, and Windows PowerShell 5.1 sessions. git diff --check passed.
After publishing @originrouter/cli@0.4.9 to npm, existing users can check and install the update with:
originrouter update check
originrouter update installInteractive startup also checks for updates. The default mode prompts the user after a newer npm version is cached; users who selected automatic updates install it when the CLI next starts and the system is idle. update also refreshes shell completion for shells that already have it installed.
中文
本次修复了一类「线路或环境本身是对的、却被别的东西悄悄替换掉」的故障,并重建了 shell 补全,使其覆盖完整命令面 —— 在 Windows 上包括直接运行 powershell 时实际启动的那个 PowerShell。
会话环境与 settings 文件
- Claude Code 的 settings 文件合并层级高于子进程环境变量,因此
~/.claude/settings.json中陈旧的env块会压过已解析的线路并将其替换,而且没有任何提示。现在线路通过--settings(flagSettings)层固定,该层优先级高于除企业托管设置之外的所有文件系统 settings 层。SDK 启动路径与 PTY 启动路径现在固定同一组 key;此前 SDK 路径手工写了 5 个环境变量,导致CLAUDE_CODE_SUBAGENT_MODEL和整个ANTHROPIC_DEFAULT_*_MODEL家族(恰恰是陈旧 settings 文件最可能持有的 key)处于未固定状态。 - 冲突不再被静默解决,而是被报告出来。冲突的 settings 层会在启动时以及
originrouter env print中被点名,因此「线路是对的、但被 settings 文件覆盖了」不再表现为一个路由故障。 - 修复
originrouter env print只描述进程环境的问题。此前它会对被 settings 文件强行设定的 key 打印 "(unset)"。现在它会列出优先级高于进程环境的各 settings 层、每一层设置的传输类 key,以及 OriginRouter 是否覆盖了它们。凭据只报 key 名,绝不报值;企业托管设置会明确标注为优先级高于 OriginRouter。
Shell 补全
- 补全改为由单一命令目录派生,而不是手工维护的子集,因此子命令、选项和取值补全不会再与它们所描述的命令树脱节。
originrouter update成功升级后会刷新补全。新版本可能生成不同的脚本,而在 Windows 上旧版本可能写入了一个当前 PowerShell 根本不会读取的 profile。只有已经带有补全块的 profile 才会被改动 —— 升级 CLI 绝不等于同意去配置一个从未配置过的 shell。刷新过程中的失败不会把一次成功的升级变成失败。
Windows
- PowerShell 补全同时安装到两个 profile。Windows 自带两个读取不同文件的 PowerShell:Windows PowerShell 5.1(直接运行
powershell时启动的就是它)读取Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1,PowerShell 7+(pwsh)读取Documents\PowerShell\Microsoft.PowerShell_profile.ps1。此前只写了 7+ 的路径,导致 5.1 用户「补全已安装却从未加载」。卸载现在会同时清理两者。 - 修复补全空词时的行为。PowerShell 5.1 在调用原生命令时会静默丢弃末尾的空参数,于是
provider <TAB>到达 CLI 时变成provider,CLI 就把这个词回显出来,而不是给出它的子命令。在 5.1 中空字符串根本无法传递 —— 字面量、空变量、String.Empty、splat 全都变成空,只有" "能存活 —— 因此生成的脚本改发一个哨兵值,由 CLI 还原为空词。只翻译最后一个词;它之前的都是完整输入。
安全
- Codex app-server 的 provider id 改为每进程唯一。Codex 按 key 合并配置层,而 table 是逐字段合并而非整体替换。我们的
-c覆盖位于 SessionFlags 层,我们设置的每个字段都胜过用户的config.toml—— 但同一 provider id 下我们没有设置的字段会从底层保留下来。针对 Codex 0.156.1 实测:使用固定 id 时,用户config.toml里的[model_providers.<id>]块会把env_http_headers、http_headers和query_params夹带到由我们自己凭据授权的那次请求上,于是用户的钱包密钥随线路指向的任意端点离开了本机。空 table 覆盖无法清除它们 —— 合并只遍历覆盖层的 key,空覆盖层不贡献任何东西;真正移除合并对象的办法是选一个用户不可能写过的 id,而这没有任何代价,因为该 id 只是 Codex 用来解析model_provider的内部句柄。 - 应用线路时同时固定两个 Anthropic 凭据变量,而不只是未使用的那个。此前设置了
ANTHROPIC_API_KEY的线路会让ANTHROPIC_AUTH_TOKEN保持缺失,而缺失的 key 会被底层 settings 层补上。针对 Claude Code 2.1.283 实测:~/.claude/settings.json中陈旧的ANTHROPIC_AUTH_TOKEN会作为Bearertoken 与我们的ANTHROPIC_API_KEY一同发出,而 Claude Code 优先使用该 bearer token —— 于是代理拿到的是用户陈旧的凭据,用户自己的密钥离开了本机。同时固定两者也能避免 Claude Code「同时设置了 ANTHROPIC_AUTH_TOKEN 和 ANTHROPIC_API_KEY」的警告再次出现。
会话与 App 状态
- 不再把「在线但空闲」的会话报告为有活跃回合。
session.status描述的是长驻 CLI 进程 —— 一个健康的空闲 Agent 会连续数小时停在running—— 把它当作回合活动会让 App 一打开就显示停止按钮,并在每次刷新时闪烁。回合活动现在只由回合事件latch。 - 控制快照与会话投影不再互相矛盾。快照此前发布的是原始回合 latch,而会话发布的是投影后的值,导致 App 每次刷新都在两个答案之间来回切换。
- 审批在无事件情况下过期时,等待状态不再丢失。等待态现在以 pending interactions 为权威来源 —— 这是回合事件 latch 无法观测到的。
- Agent 重连时不再「复活」已结束的回合。注册被视为一次全新 attach,因此「App 离开期间已结束的回合」不再继承此前的
runninglatch、显示一个永远不会到来的终止事件之前的停止按钮。
命令可发现性
- 补齐
local api整族的文档。originrouter help此前完全没有它,包括local api pair—— 而这是把 App 与本机 Local API 配对的唯一入口,也就是说整个配对流程没有可发现的入口。参考手册现在列出status、pair、connect、set-host、set-port,并说明配对行不携带访问密钥。 - 不再推荐一个 CLI 会拒绝的子命令。
auth logout在命令目录里声明了却从未实现:TAB 会推荐它,选中后报Unknown auth subcommand。退出登录是originrouter logout。 - 目录里的子命令现在检查可达性,而不只是"声明过"。此前一个名字可以在目录里声明、被补全推荐,却没有任何 dispatch 分支接受它。现在每个条目都会解析到负责其分支的模块,豁免必须写明理由。help 文本仍为手工撰写(其分组按概念而非树结构),但改为双向校验,因此"文档里有、实际不存在"和"实际存在、文档里没有"都会失败。
验证:CLI 完整测试套件通过,包含针对命令目录、补全(哨兵值与双 profile 安装)、settings 覆盖、Codex provider 隔离、env print settings 分层、以及回合状态投影的新增测试。命令目录检查经过变异测试——把删掉的 auth logout 加回目录、添加一个未实现的子命令、删除一段 help、取消一个内部命令的 hidden 标记,四种变异都会让它失败。升级后刷新补全这一路径由一项测试覆盖,该测试证明其中的失败绝不会把一次成功的升级变成失败的升级。Shell 补全已在真实的 Bash、Zsh 和 Windows PowerShell 5.1 会话中端到端验证。git diff --check 通过。
将 @originrouter/cli@0.4.9 发布到 npm 后,已安装用户可以执行:
originrouter update check
originrouter update install交互式启动也会检查更新。默认模式会在缓存到新版 npm 包后提示用户;选择自动更新模式的用户会在下次启动且系统空闲时安装。update 还会为已安装补全的 shell 刷新补全。