@cc-x/cc-x 0.4.7 → 0.4.8
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 +13 -0
- package/README.md +10 -0
- package/dist/env/default.js +15 -5
- package/dist/i18n/messages.js +1 -0
- package/dist/ui/menus.js +29 -18
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -89,6 +89,19 @@ xx DeepSeek -s # Use this session, launch Claude now
|
|
|
89
89
|
xx DeepSeek # Set as default for new terminals
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
+
### Updating
|
|
93
|
+
|
|
94
|
+
Updating just means **re-running the install command** — the installer downloads the latest
|
|
95
|
+
release and overwrites the old binary in place, no uninstall needed. **Open a new terminal**
|
|
96
|
+
afterward; `xx --version` should show the new version.
|
|
97
|
+
|
|
98
|
+
- **Windows**: `irm https://github.com/becomeless/cc-x/releases/latest/download/install.ps1 | iex`
|
|
99
|
+
- **macOS / Linux**: `curl -fsSL https://github.com/becomeless/cc-x/releases/latest/download/install.sh | sh`
|
|
100
|
+
- **npm**: `npm i -g @cc-x/cc-x@latest`
|
|
101
|
+
|
|
102
|
+
> With the menu's "Update check" set to "notify", CC-X shows a banner at the top of the menu
|
|
103
|
+
> when a new version is out, with the matching upgrade command for your platform.
|
|
104
|
+
|
|
92
105
|
---
|
|
93
106
|
|
|
94
107
|
## 60-second quick start
|
package/README.md
CHANGED
|
@@ -87,6 +87,16 @@ xx DeepSeek -s # 本次启用,立即启动 Claude
|
|
|
87
87
|
xx DeepSeek # 设为默认,以后新终端自动生效
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
+
### 更新到新版本
|
|
91
|
+
|
|
92
|
+
更新就是**重新跑一遍安装命令**——安装器会下载最新版覆盖旧的,不用先卸载。跑完**新开一个终端**,`xx --version` 即为新版本。
|
|
93
|
+
|
|
94
|
+
- **Windows**:`irm https://github.com/becomeless/cc-x/releases/latest/download/install.ps1 | iex`
|
|
95
|
+
- **macOS / Linux**:`curl -fsSL https://github.com/becomeless/cc-x/releases/latest/download/install.sh | sh`
|
|
96
|
+
- **npm**:`npm i -g @cc-x/cc-x@latest`
|
|
97
|
+
|
|
98
|
+
> 把菜单里的「更新检查」开到「提醒」后,有新版时 CC-X 会在菜单顶部横幅提示,并直接给出上面对应平台的升级命令。
|
|
99
|
+
|
|
90
100
|
---
|
|
91
101
|
|
|
92
102
|
## 60 秒上手
|
package/dist/env/default.js
CHANGED
|
@@ -18,23 +18,33 @@ export function computeManagedVals(p) {
|
|
|
18
18
|
}
|
|
19
19
|
return vals;
|
|
20
20
|
}
|
|
21
|
-
export function
|
|
21
|
+
export function persistDefaultEnv(p, scope) {
|
|
22
22
|
const vals = computeManagedVals(p);
|
|
23
23
|
const dryRun = scope === 'process';
|
|
24
24
|
const result = { scope, dryRun };
|
|
25
|
-
let persisted = true;
|
|
26
25
|
if (!dryRun) {
|
|
27
26
|
if (process.platform === 'win32') {
|
|
28
27
|
result.windows = persistWindows(vals);
|
|
29
|
-
persisted = result.windows.ok;
|
|
30
28
|
}
|
|
31
29
|
else {
|
|
32
30
|
result.unix = persistUnix(vals);
|
|
33
|
-
persisted = !result.unix.unsupported; // fish 未写入 → 不算持久化成功
|
|
34
31
|
}
|
|
35
32
|
}
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
function envPersisted(result) {
|
|
36
|
+
if (result.dryRun)
|
|
37
|
+
return true;
|
|
38
|
+
if (result.windows)
|
|
39
|
+
return result.windows.ok;
|
|
40
|
+
if (result.unix)
|
|
41
|
+
return !result.unix.unsupported; // fish 未写入 → 不算持久化成功
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
export function setDefault(paths, store, p, scope) {
|
|
45
|
+
const result = persistDefaultEnv(p, scope);
|
|
36
46
|
// [P1] 仅当持久化成功(或 dry-run)才改默认指向并落盘,避免「报失败却已改默认」的不一致。
|
|
37
|
-
if (
|
|
47
|
+
if (envPersisted(result)) {
|
|
38
48
|
store.current = p.name;
|
|
39
49
|
saveStore(paths, store);
|
|
40
50
|
}
|
package/dist/i18n/messages.js
CHANGED
|
@@ -121,6 +121,7 @@ export const messages = {
|
|
|
121
121
|
'error.storeRead': { zh: '配置文件读取失败:{0}', en: 'Failed to read config file: {0}' },
|
|
122
122
|
'error.storeCorrupt': { zh: '配置文件解析失败(JSON 语法错误):{0}', en: 'Failed to parse config file (invalid JSON): {0}' },
|
|
123
123
|
'error.storeFormat': { zh: '配置文件结构不正确(顶层须为对象、providers 须为数组且条目结构合法):{0}', en: 'Config file has invalid structure (top-level must be an object, providers must be an array with valid profile entries): {0}' },
|
|
124
|
+
'error.storeSave': { zh: '配置文件保存失败:{0}', en: 'Failed to save config file: {0}' },
|
|
124
125
|
'error.storeCorruptHint': {
|
|
125
126
|
zh: '为避免误删,未对它做任何改动。请修复后重试;或删除该文件以重新生成默认配置(会丢失已填的密钥)。',
|
|
126
127
|
en: 'Left untouched to avoid data loss. Fix it and retry; or delete the file to regenerate defaults (loses any saved keys).',
|
package/dist/ui/menus.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { launchSession } from '../actions.js';
|
|
9
9
|
import { checkProfile } from '../check.js';
|
|
10
10
|
import { getProviderEnvMap, getProviderState, isOfficial, reconcileCurrent, saveStore } from '../config/store.js';
|
|
11
|
-
import { setDefault } from '../env/default.js';
|
|
11
|
+
import { persistDefaultEnv, setDefault } from '../env/default.js';
|
|
12
12
|
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';
|
|
@@ -91,9 +91,7 @@ export async function openMenu(paths, store, scope, version, catalog) {
|
|
|
91
91
|
if (shortcut === 'e') {
|
|
92
92
|
const old = target.name;
|
|
93
93
|
if (await editForm(target, store, catalog)) {
|
|
94
|
-
|
|
95
|
-
store.current = target.name;
|
|
96
|
-
saveStore(paths, store);
|
|
94
|
+
({ warn: warnFlash, toast: flash } = saveEditedProfile(paths, store, target, old, scope));
|
|
97
95
|
}
|
|
98
96
|
}
|
|
99
97
|
else if (shortcut === 's') {
|
|
@@ -137,9 +135,7 @@ export async function openMenu(paths, store, scope, version, catalog) {
|
|
|
137
135
|
// #9:无密钥的第三方配置,Enter 直达编辑并聚焦密钥行(铺平首次成功路径)。
|
|
138
136
|
const old = target.name;
|
|
139
137
|
if (await editForm(target, store, catalog, true)) {
|
|
140
|
-
|
|
141
|
-
store.current = target.name;
|
|
142
|
-
saveStore(paths, store);
|
|
138
|
+
({ warn: warnFlash, toast: flash } = saveEditedProfile(paths, store, target, old, scope));
|
|
143
139
|
}
|
|
144
140
|
}
|
|
145
141
|
else {
|
|
@@ -187,9 +183,7 @@ async function actionMenu(paths, store, p, scope, catalog) {
|
|
|
187
183
|
else if (sel === 3) {
|
|
188
184
|
const old = p.name;
|
|
189
185
|
if (await editForm(p, store, catalog)) {
|
|
190
|
-
|
|
191
|
-
store.current = p.name; // 改了名/供应商时同步默认指向
|
|
192
|
-
saveStore(paths, store);
|
|
186
|
+
({ warn: warnFlash, toast: flash } = saveEditedProfile(paths, store, p, old, scope));
|
|
193
187
|
}
|
|
194
188
|
}
|
|
195
189
|
else if (sel === 4) {
|
|
@@ -212,14 +206,19 @@ function defaultDisplayName(store) {
|
|
|
212
206
|
return '—';
|
|
213
207
|
return providerDisplayName(store.providers.find((p) => p.name === store.current) ?? { name: store.current, env: {} });
|
|
214
208
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
209
|
+
function saveEditedProfile(paths, store, p, oldName, scope) {
|
|
210
|
+
const wasDefault = store.current === oldName;
|
|
211
|
+
if (wasDefault)
|
|
212
|
+
store.current = p.name; // 改了名/供应商时同步默认指向
|
|
213
|
+
saveStore(paths, store);
|
|
214
|
+
if (wasDefault)
|
|
215
|
+
return syncDefaultEnv(p, scope);
|
|
216
|
+
return {};
|
|
217
|
+
}
|
|
218
|
+
function defaultWarning(p) {
|
|
219
|
+
return getProviderState(p).key === 'noKey' ? T('default.noKey', providerDisplayName(p)) : '';
|
|
220
|
+
}
|
|
221
|
+
function defaultResultMessage(warn, name, r) {
|
|
223
222
|
if (r.dryRun)
|
|
224
223
|
return { warn, toast: `${T('default.done', name)} ${T('default.dryRun')}` };
|
|
225
224
|
if (r.windows && !r.windows.ok)
|
|
@@ -228,6 +227,18 @@ function applyDefault(paths, store, p, scope) {
|
|
|
228
227
|
return { warn, toast: T('default.fishUnsupported') };
|
|
229
228
|
return { warn, toast: T('default.done', name) };
|
|
230
229
|
}
|
|
230
|
+
function syncDefaultEnv(p, scope) {
|
|
231
|
+
const name = providerDisplayName(p);
|
|
232
|
+
return defaultResultMessage(defaultWarning(p), name, persistDefaultEnv(p, scope));
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* 设为默认,返回 { warn, toast }:warn 为黄字警告(缺密钥),toast 为绿色结果。
|
|
236
|
+
* 分开返回让调用方各自上色,避免警告被染成「成功」绿。
|
|
237
|
+
*/
|
|
238
|
+
function applyDefault(paths, store, p, scope) {
|
|
239
|
+
const name = providerDisplayName(p);
|
|
240
|
+
return defaultResultMessage(defaultWarning(p), name, setDefault(paths, store, p, scope));
|
|
241
|
+
}
|
|
231
242
|
// hostSuffix 返回行尾的灰字 host(如 ` · api.deepseek.com`);无 base(官方/未填)返回空。
|
|
232
243
|
// 超宽时由 selectMenu 的 ANSI-aware 截断从行尾裁掉,不会切坏颜色。
|
|
233
244
|
function hostSuffix(p) {
|