pi-ccswitch-auto-switch 0.3.4 → 0.3.5

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
@@ -38,7 +38,7 @@ All knobs below are optional and have sensible defaults:
38
38
  | Setting | Scope | Default | Notes |
39
39
  | --- | --- | --- | --- |
40
40
  | `PI_CODING_AGENT_DIR` | Extension state | `~/.pi/agent` | Where health state, logs, and failure reports are stored. Only relevant if you relocated your Pi agent directory; the extension follows Pi's own convention. |
41
- | `PI_BIN` | Headless runner only | `pi` on `PATH` | Must point to the real `pi` executable, never to `pi-ccswitch-run`. |
41
+ | `PI_BIN` | Headless runner only | `pi` on `PATH` | Must point to the real `pi` executable. Legacy self-references to `pi-ccswitch-run` or `ccswitch-run` are ignored with a warning and fall back to `pi`; other invalid commands remain configuration errors. |
42
42
  | Model scope (`/model` etc.) | Failover candidates | Full registry | When Pi has an active model scope, failover only considers models inside that scope; otherwise the full registry is used. The status bar shows which source is active. |
43
43
  | `baseUrl` metadata | Endpoint isolation | provider key | Endpoint-level platform isolation groups models by `baseUrl` (provided by CC Switch `3.20+`). Without it, isolation degrades to provider-level grouping, which still works. |
44
44
 
@@ -93,7 +93,7 @@ cd ~/.pi/agent/npm && npm install pi-ccswitch-auto-switch@latest
93
93
  pi-ccswitch-run --no-tools --no-context-files @prompt.md "Summarize the attached text"
94
94
  ```
95
95
 
96
- The runner starts one `pi --mode rpc --no-session --no-extensions --extension <this package>/index.ts` process, so only CCSwitch is loaded. `PI_BIN` may point to the real Pi executable; it must not point to `pi-ccswitch-run`. The default deadline is ten minutes and can be changed with `--timeout-ms`.
96
+ The runner starts one `pi --mode rpc --no-session --no-extensions --extension <this package>/index.ts` process, so only CCSwitch is loaded. `PI_BIN` may point to the real Pi executable. For migration safety, an old `PI_BIN=pi-ccswitch-run` or `PI_BIN=ccswitch-run` value emits a warning and falls back to `pi` on `PATH` instead of recursing or terminating every downstream job. Arbitrary commands are still rejected. The default deadline is ten minutes and can be changed with `--timeout-ms`.
97
97
 
98
98
  Exit codes are `0` for success, `1` when candidates are exhausted, `2` for invocation/RPC configuration failures, `124` for timeout, and `130`/`143` for interruption. Text `@files` are supported in the first release; images and stdin input are intentionally not.
99
99
 
@@ -121,10 +121,10 @@ Examples:
121
121
  ## Status bar
122
122
 
123
123
  ```text
124
- CCS v0.3.1 ✓70/131 · ⏳61 · ⛔0 · 🔄3 · provider/model-id
124
+ CCS v0.3.5 ✓70/131 · ⏳61 · ⛔0 · 🔄3 · provider/model-id
125
125
  ```
126
126
 
127
- - `v0.3.1`: the installed CCSwitch extension version.
127
+ - `v0.3.5`: the installed CCSwitch extension version.
128
128
  - `✓70/131`: 70 healthy models out of 131 unique `provider/model` combinations in Pi's effective scope. Duplicate scoped entries are counted once.
129
129
  - `⏳61`: 61 models are currently affected by automatic model, provider, or endpoint cooldowns. One provider breaker can account for many affected models.
130
130
  - `⛔0`: no manually disabled models.
package/README.zh-CN.md CHANGED
@@ -36,7 +36,7 @@
36
36
  | 设置项 | 作用域 | 默认值 | 说明 |
37
37
  | --- | --- | --- | --- |
38
38
  | `PI_CODING_AGENT_DIR` | 扩展状态 | `~/.pi/agent` | 健康状态、日志与失败报告的存放目录;仅在你迁移了 Pi agent 目录时需要,插件跟随 Pi 自身约定。 |
39
- | `PI_BIN` | 仅 headless runner | `PATH` 中的 `pi` | 必须指向真实 `pi` 可执行文件,不能指向 `pi-ccswitch-run`。 |
39
+ | `PI_BIN` | 仅 headless runner | `PATH` 中的 `pi` | 必须指向真实 `pi` 可执行文件。历史自引用值 `pi-ccswitch-run` 或 `ccswitch-run` 会警告并降级到 `pi`;其他非法命令仍按配置错误处理。 |
40
40
  | 模型 scope(`/model` 等) | 故障转移候选 | 全量注册表 | Pi 启用了 model scope 时,只在 scope 内模型间切换;否则使用全量注册表。状态栏会显示当前数据源。 |
41
41
  | `baseUrl` 元数据 | 端点隔离 | provider key | 端点级平台隔离按 `baseUrl` 分组模型(由 CC Switch `3.20+` 提供);缺失时退化为 provider 级分组,仍然可用。 |
42
42
 
@@ -89,7 +89,7 @@ cd ~/.pi/agent/npm && npm install pi-ccswitch-auto-switch@latest
89
89
  pi-ccswitch-run --no-tools --no-context-files @prompt.md "请总结附件"
90
90
  ```
91
91
 
92
- runner 内部只显式加载本扩展并启动一次 Pi RPC;`PI_BIN` 只能指定内部真实 `pi`,不能指向 runner。默认总超时为 10 分钟,可用 `--timeout-ms` 调整。退出码:成功 `0`、候选耗尽 `1`、参数/RPC 配置错误 `2`、超时 `124`、中断 `130/143`。首版只支持文本 `@文件`,不支持图片和 stdin。
92
+ runner 内部只显式加载本扩展并启动一次 Pi RPC;`PI_BIN` 应指定内部真实 `pi`。为兼容旧配置,`PI_BIN=pi-ccswitch-run` 或 `PI_BIN=ccswitch-run` 会输出警告并降级到 `PATH` 中的 `pi`,避免递归和长驻消费方连续失败;其他任意命令仍会被拒绝。默认总超时为 10 分钟,可用 `--timeout-ms` 调整。退出码:成功 `0`、候选耗尽 `1`、参数/RPC 配置错误 `2`、超时 `124`、中断 `130/143`。首版只支持文本 `@文件`,不支持图片和 stdin。
93
93
 
94
94
  正常使用 CC Switch 配置 Provider 即可;本插件不会修改 Pi 模型设置或 CC Switch 数据。
95
95
 
@@ -109,10 +109,10 @@ runner 内部只显式加载本扩展并启动一次 Pi RPC;`PI_BIN` 只能指
109
109
  ## 状态栏
110
110
 
111
111
  ```text
112
- CCS v0.3.1 ✓70/131 · ⏳61 · ⛔0 · 🔄3 · provider/model-id
112
+ CCS v0.3.5 ✓70/131 · ⏳61 · ⛔0 · 🔄3 · provider/model-id
113
113
  ```
114
114
 
115
- - `v0.3.1`:当前安装的 CCSwitch 扩展版本。
115
+ - `v0.3.5`:当前安装的 CCSwitch 扩展版本。
116
116
  - `✓70/131`:Pi 有效范围内共有 131 个去重后的 `provider/model` 组合,其中 70 个健康;重复的 scope 条目只计一次。
117
117
  - `⏳61`:有 61 个模型正受模型、Provider 或端点自动冷却影响;一条 Provider 熔断记录可能同时影响许多模型。
118
118
  - `⛔0`:没有被手动禁用的模型。
package/index.ts CHANGED
@@ -13,7 +13,7 @@ const STREAM_IDLE_TIMEOUT = 120_000
13
13
  const MAX_ATTEMPTS = 5
14
14
  const ROUND_LIMIT = 8 * 60_000
15
15
  const RPC_PROTOCOL_VERSION = 1
16
- const EXTENSION_VERSION = '0.3.4'
16
+ const EXTENSION_VERSION = '0.3.5'
17
17
  // 同端点(BaseURL 相同)连续失败达到该次数即隔离该端点,避免同一个平台的多个模型逐个试错耗尽本轮切换
18
18
  const ENDPOINT_FAIL_THRESHOLD = 3
19
19
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-ccswitch-auto-switch",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
4
4
  "description": "Provider-first automatic model failover extension for Pi and CC Switch",
5
5
  "license": "MIT",
6
6
  "keywords": [
package/runner.mjs CHANGED
@@ -7,7 +7,7 @@ import { spawn } from 'node:child_process'
7
7
  import { access, readFile, stat } from 'node:fs/promises'
8
8
  import { existsSync } from 'node:fs'
9
9
  import { fileURLToPath } from 'node:url'
10
- import { basename, delimiter, dirname, extname, isAbsolute, join, resolve } from 'node:path'
10
+ import { delimiter, dirname, extname, isAbsolute, join, resolve } from 'node:path'
11
11
 
12
12
  export const EXIT = Object.freeze({ SUCCESS: 0, EXHAUSTED: 1, CONFIG: 2, TIMEOUT: 124, INTERRUPTED: 130, TERMINATED: 143 })
13
13
  export const DEFAULT_TIMEOUT_MS = 600_000
@@ -87,9 +87,24 @@ export function buildMessage(fileText, prompt) {
87
87
  return fileText ? `${fileText}\n${prompt}` : prompt
88
88
  }
89
89
 
90
+ const LEGACY_RUNNER_STEMS = new Set(['pi-ccswitch-run', 'ccswitch-run'])
91
+
92
+ function executableStem(value) {
93
+ return String(value).split(/[\\/]/).pop().toLowerCase().replace(/\.(cmd|exe|bat)$/, '')
94
+ }
95
+
90
96
  function validPiBinary(value) {
91
- const normalized = basename(value).toLowerCase().replace(/\.(cmd|exe|bat)$/, '')
92
- return normalized === 'pi'
97
+ return executableStem(value) === 'pi'
98
+ }
99
+
100
+ export function selectPiBinary(configured, warn = () => {}) {
101
+ if (!configured) return 'pi'
102
+ if (validPiBinary(configured)) return configured
103
+ if (LEGACY_RUNNER_STEMS.has(executableStem(configured))) {
104
+ warn("PI_BIN points to a legacy runner wrapper; ignoring it and falling back to 'pi' on PATH")
105
+ return 'pi'
106
+ }
107
+ throw new Error('PI_BIN must name the real pi executable, not an arbitrary command')
93
108
  }
94
109
 
95
110
  async function resolveWindowsShim(command) {
@@ -115,8 +130,7 @@ async function resolveWindowsShim(command) {
115
130
 
116
131
  async function resolvePiCommand() {
117
132
  const configured = String(process.env.PI_BIN || '').trim()
118
- if (configured && !validPiBinary(configured)) throw new Error('PI_BIN must name the real pi executable, not an arbitrary command')
119
- const command = configured || 'pi'
133
+ const command = selectPiBinary(configured, message => process.stderr.write(`pi-ccswitch-run: warning: ${message}\n`))
120
134
  if (process.platform === 'win32') return resolveWindowsShim(command)
121
135
  return { command, prefix: [] }
122
136
  }