@cc-x/cc-x 0.4.10 → 0.4.11

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.en.md CHANGED
@@ -178,7 +178,7 @@ cc-switch is an excellent full-featured GUI; CC-X takes the opposite, minimal ap
178
178
 
179
179
  > CC-X cares more about boundaries than features.
180
180
 
181
- Claude Code already has its own config system, MCP ecosystem, and session state. CC-X is not trying to become a control panel above it, or to copy user config into another database. It stands at one narrow point before Claude Code starts: prepare the 9 managed environment variables, then let Claude Code run.
181
+ Claude Code already has its own config system, MCP ecosystem, and session state. CC-X is not trying to become a control panel above it, or to copy user config into another database. It stands at one narrow point before Claude Code starts: prepare the 8 managed environment variables, then let Claude Code run.
182
182
 
183
183
  That constraint is deliberate: no writes to Claude Code config files, no MCP management, no automatic migration, no resident background controller. If process environment variables can solve it, CC-X avoids global files; if a choice matters, the user makes it explicitly. Doing less keeps the failure surface small.
184
184
 
@@ -200,7 +200,6 @@ Issues / PRs are welcome, but the direction is clear: **make switching steadier,
200
200
  | haiku → model | `ANTHROPIC_DEFAULT_HAIKU_MODEL` | |
201
201
  | effort level | `CLAUDE_CODE_EFFORT_LEVEL` | `low`–`max`; `auto` = model default; empty = unset. Third parties may not honor it |
202
202
  | Disable nonessential traffic | `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` | `1` disables nonessential Claude Code traffic; empty = unset. Zhipu GLM defaults to `1` |
203
- | Context window | `CLAUDE_CODE_AUTO_COMPACT_WINDOW` | Fill with a Claude Code-supported window value; empty = unset |
204
203
 
205
204
  > CC-X **deliberately does not set** `ANTHROPIC_MODEL`. Use `/model opus|sonnet|haiku` in-session;
206
205
  > the mapping table translates to the provider's real model name.
@@ -249,10 +248,10 @@ Issues / PRs are welcome, but the direction is clear: **make switching steadier,
249
248
  - **No Claude Code config file is ever modified.** Before third-party launches, CC-X only reads
250
249
  the onboarding field in `~/.claude.json` to decide whether to print a hint.
251
250
 
252
- CC-X only touches these 9 "managed" variables (and clears the ones a target profile doesn't use):
251
+ CC-X only touches these 8 "managed" variables (and clears the ones a target profile doesn't use):
253
252
  `ANTHROPIC_BASE_URL`, `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_API_KEY`, `ANTHROPIC_DEFAULT_OPUS_MODEL`,
254
253
  `ANTHROPIC_DEFAULT_SONNET_MODEL`, `ANTHROPIC_DEFAULT_HAIKU_MODEL`, `CLAUDE_CODE_EFFORT_LEVEL`,
255
- `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`, `CLAUDE_CODE_AUTO_COMPACT_WINDOW`.
254
+ `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`.
256
255
 
257
256
  > 💡 To change `settings.json`, use Claude Code's own `/update-config` and describe what you want
258
257
  > in natural language (e.g. "allow npm commands") — safer than letting an external tool rewrite it.
package/README.md CHANGED
@@ -168,7 +168,7 @@ cc-switch 是优秀的全能 GUI;CC-X 走相反的极简路线。
168
168
 
169
169
  > CC-X 的边界比功能更重要。
170
170
 
171
- Claude Code 已经有自己的配置系统、MCP 生态和会话状态。CC-X 不想再造一个"上层控制台",也不想把用户的配置收编进自己的数据库。它只站在 Claude Code 进程启动前的那一小步:把 9 个受管环境变量准备好,然后让 Claude Code 自己工作。
171
+ Claude Code 已经有自己的配置系统、MCP 生态和会话状态。CC-X 不想再造一个"上层控制台",也不想把用户的配置收编进自己的数据库。它只站在 Claude Code 进程启动前的那一小步:把 8 个受管环境变量准备好,然后让 Claude Code 自己工作。
172
172
 
173
173
  所以它的取舍是有意的:不写 Claude Code 配置文件,不接管 MCP,不做自动迁移,不做后台常驻管理。能用进程环境变量解决,就不碰全局文件;能让用户显式选择,就不替用户自动决定。少做一点,是为了把风险面压到足够小。
174
174
 
@@ -190,7 +190,6 @@ Claude Code 已经有自己的配置系统、MCP 生态和会话状态。CC-X
190
190
  | haiku → 模型 | `ANTHROPIC_DEFAULT_HAIKU_MODEL` | |
191
191
  | effort 思考档 | `CLAUDE_CODE_EFFORT_LEVEL` | `low` ~ `max`;`auto`=模型默认;留空=不设。第三方不一定生效 |
192
192
  | 禁用非核心流量 | `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` | `1`=禁用 Claude Code 非核心流量;留空=不设。GLM 预置为 `1` |
193
- | 上下文窗口大小 | `CLAUDE_CODE_AUTO_COMPACT_WINDOW` | 按 Claude Code 支持的窗口值填写;留空=不设 |
194
193
 
195
194
  > CC-X **刻意不设** `ANTHROPIC_MODEL`。在会话里用 `/model opus|sonnet|haiku` 选档,映射表负责翻译成对应供应商的模型名。
196
195
 
@@ -231,8 +230,8 @@ Claude Code 已经有自己的配置系统、MCP 生态和会话状态。CC-X
231
230
  - 语义一致:**只影响新终端**;切到「官方」会清除全部受管变量
232
231
  - **不修改任何 Claude Code 配置文件。** 启动第三方前只读探测一次 `~/.claude.json` 的 onboarding 字段,用于提示。
233
232
 
234
- CC-X 只动这 9 个「受管」环境变量,切换时清掉目标不用的:
235
- `ANTHROPIC_BASE_URL`、`ANTHROPIC_AUTH_TOKEN`、`ANTHROPIC_API_KEY`、`ANTHROPIC_DEFAULT_OPUS_MODEL`、`ANTHROPIC_DEFAULT_SONNET_MODEL`、`ANTHROPIC_DEFAULT_HAIKU_MODEL`、`CLAUDE_CODE_EFFORT_LEVEL`、`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`、`CLAUDE_CODE_AUTO_COMPACT_WINDOW`。
233
+ CC-X 只动这 8 个「受管」环境变量,切换时清掉目标不用的:
234
+ `ANTHROPIC_BASE_URL`、`ANTHROPIC_AUTH_TOKEN`、`ANTHROPIC_API_KEY`、`ANTHROPIC_DEFAULT_OPUS_MODEL`、`ANTHROPIC_DEFAULT_SONNET_MODEL`、`ANTHROPIC_DEFAULT_HAIKU_MODEL`、`CLAUDE_CODE_EFFORT_LEVEL`、`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`。
236
235
 
237
236
  > 💡 需要改 `settings.json`?直接用 Claude Code 的 `/update-config` 说需求(如"允许 npm 命令"),比让外部工具改可靠。
238
237
 
package/dist/index.js CHANGED
@@ -14,9 +14,7 @@ import { loadPresets } from './config/presets.js';
14
14
  import { setDefault } from './env/default.js';
15
15
  import { providerDisplayName, resolveLang, setLang, T } from './i18n/index.js';
16
16
  import { currentTerminalLine } from './runtime-info.js';
17
- import { noteSuffix, stateLabel } from './ui/format.js';
18
- import { openMenu } from './ui/menus.js';
19
- import { padDisplay } from './utils/display.js';
17
+ import { openMenu, profileRows } from './ui/menus.js';
20
18
  const require = createRequire(import.meta.url);
21
19
  const pkg = require('../package.json');
22
20
  /** 从 argv 里轻量预读一个带值参数(`--name v` 或 `--name=v`),用于 parse 前定语言。 */
@@ -116,9 +114,8 @@ function runList(store) {
116
114
  console.log('');
117
115
  console.log(` ${T('list.default', cur ? providerDisplayName(cur) : store.current)}`);
118
116
  console.log(` ${currentTerminalLine(store)}`);
119
- for (const p of store.providers) {
120
- const mark = p.name === store.current ? '▶' : ' ';
121
- console.log(` ${mark} ${padDisplay(providerDisplayName(p), 18)}[${stateLabel(p)}]${noteSuffix(p)}`);
117
+ for (const line of profileRows(store.providers, store.current)) {
118
+ console.log(` ${line}`);
122
119
  }
123
120
  console.log('');
124
121
  }
package/dist/ui/menus.js CHANGED
@@ -13,7 +13,7 @@ import { getLang, providerDisplayName, setLang, T } from '../i18n/index.js';
13
13
  import { currentTerminalLine, hostOf } from '../runtime-info.js';
14
14
  import { banner as updateBanner, maybeRefresh, MODE_NOTIFY, upgradeCommand } from '../update/update.js';
15
15
  import { paint } from '../utils/ansi.js';
16
- import { padDisplay } from '../utils/display.js';
16
+ import { displayWidth, padDisplay } from '../utils/display.js';
17
17
  import { editForm } from './edit.js';
18
18
  import { noteSuffix, stateLabel } from './format.js';
19
19
  import { confirmKey, selectMenu } from './select.js';
@@ -42,10 +42,7 @@ export async function openMenu(paths, store, scope, version, catalog) {
42
42
  }
43
43
  const updLabel = store.update === MODE_NOTIFY ? T('menu.updateNotify') : T('menu.updateOff');
44
44
  const buildItems = () => {
45
- const labels = store.providers.map((p) => {
46
- const dft = p.name === store.current ? T('menu.default') : '';
47
- return `${padDisplay(providerDisplayName(p), 16)}${padDisplay(dft, 8)}[${stateLabel(p)}]${noteSuffix(p)}${hostSuffix(p)}`;
48
- });
45
+ const labels = profileRows(store.providers, store.current);
49
46
  return [...labels, '', T('menu.newProfile'), T('menu.language'), updLabel, '', T('menu.exit')];
50
47
  };
51
48
  const onMove = (from, to) => {
@@ -239,13 +236,45 @@ function applyDefault(paths, store, p, scope) {
239
236
  const name = providerDisplayName(p);
240
237
  return defaultResultMessage(defaultWarning(p), name, setDefault(paths, store, p, scope));
241
238
  }
242
- // hostSuffix 返回行尾的灰字 host(如 ` · api.deepseek.com`);无 base(官方/未填)返回空。
243
- // 超宽时由 selectMenu 的 ANSI-aware 截断从行尾裁掉,不会切坏颜色。
244
- function hostSuffix(p) {
239
+ const ROW_NAME_W = 13; // 名字列宽(显示宽度,CJK 2);状态/备注列宽按内容动态算
240
+ // profileRows 把所有配置格式化成定宽分栏的菜单行:名字为主信息(默认项加粗),
241
+ // 状态/备注/host 一律 dim 退为次要信息,让备注、host 各自对齐成竖列。
242
+ // 状态、备注列宽按当前配置实际内容动态取最大值——自动兜住任何语言/文案。
243
+ // TUI 主菜单与 xx --list 共用此函数,保证两处呈现一致。与 Go 版 menu.go ProfileRows 对齐。
244
+ export function profileRows(providers, current) {
245
+ let stateW = 0;
246
+ let noteW = 0;
247
+ for (const p of providers) {
248
+ stateW = Math.max(stateW, displayWidth(stateLabel(p)));
249
+ noteW = Math.max(noteW, displayWidth(p.note ?? ''));
250
+ }
251
+ stateW += 2; // 状态列尾留出与备注列的间距(最宽行否则会与备注贴死)
252
+ if (noteW > 0)
253
+ noteW += 2; // 有备注时留出与 host 的列间距
254
+ return providers.map((p) => profileRow(p, p.name === current, stateW, noteW));
255
+ }
256
+ // profileRow 组成一行:定宽分栏 + 次要信息 dim + 默认项加粗。
257
+ // 加粗用 1m/22m(reset 不动颜色),不会破坏 selectMenu 选中行的外层绿色。
258
+ function profileRow(p, isDefault, stateW, noteW) {
259
+ let name = padDisplay(providerDisplayName(p), ROW_NAME_W);
260
+ let marker = ' ';
261
+ if (isDefault) {
262
+ marker = paint('● ', 'bold');
263
+ name = paint(name, 'bold');
264
+ }
265
+ const state = paint(` · ${padDisplay(stateLabel(p), stateW)}`, 'dim');
266
+ // 空备注=定宽空白,撑出 host 前的列间距
267
+ const note = (p.note ?? '').trim()
268
+ ? paint(padDisplay(p.note ?? '', noteW), 'dim')
269
+ : padDisplay('', noteW);
270
+ return `${marker}${name}${state}${note}${hostCol(p)}`;
271
+ }
272
+ // hostCol 返回行尾 dim 的 host(无 base 返回空)。超宽时由 selectMenu 的 ANSI-aware 截断裁掉。
273
+ function hostCol(p) {
245
274
  const base = (getProviderEnvMap(p).ANTHROPIC_BASE_URL ?? '').trim();
246
275
  if (!base)
247
276
  return '';
248
- return paint(` · ${hostOf(base)}`, 'dim');
277
+ return paint(hostOf(base), 'dim');
249
278
  }
250
279
  function needsFirstRunHint(store) {
251
280
  let hasThirdParty = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cc-x/cc-x",
3
- "version": "0.4.10",
3
+ "version": "0.4.11",
4
4
  "description": "Claude Code API 切换器(命令 xx):在官方账号与第三方 Anthropic 兼容 API 间切换,纯环境变量、不碰 Claude Code 配置文件。",
5
5
  "keywords": [
6
6
  "claude",