yingclaw 2.0.9 → 2.1.1
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/bin/cli.js +77 -1
- package/lib/desktop.js +14 -9
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -30,7 +30,7 @@ async function getBanner() {
|
|
|
30
30
|
const chalk = (await import('chalk')).default;
|
|
31
31
|
const figlet = require('figlet');
|
|
32
32
|
const boxen = (await import('boxen')).default;
|
|
33
|
-
const title = figlet.textSync('
|
|
33
|
+
const title = figlet.textSync('yingclaw', { font: 'Small', horizontalLayout: 'fitted' });
|
|
34
34
|
const subtitle = chalk.dim('Claude Code × 国产大模型 一键接入') + ' ' + chalk.cyan(`v${pkg.version}`);
|
|
35
35
|
return boxen(
|
|
36
36
|
chalk.cyan.bold(title) + '\n' + subtitle,
|
|
@@ -825,6 +825,56 @@ program
|
|
|
825
825
|
}
|
|
826
826
|
});
|
|
827
827
|
|
|
828
|
+
program
|
|
829
|
+
.command('update')
|
|
830
|
+
.description('检查并更新 yingclaw 到最新版本')
|
|
831
|
+
.action(async () => {
|
|
832
|
+
const chalk = (await import('chalk')).default;
|
|
833
|
+
const ora = (await import('ora')).default;
|
|
834
|
+
const boxen = (await import('boxen')).default;
|
|
835
|
+
|
|
836
|
+
console.log(await getBanner());
|
|
837
|
+
|
|
838
|
+
const spinner = ora('检查最新版本...').start();
|
|
839
|
+
const latest = await checkForUpdate();
|
|
840
|
+
|
|
841
|
+
if (!latest) {
|
|
842
|
+
spinner.warn(chalk.yellow('无法获取版本信息,请检查网络'));
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
const current = pkg.version;
|
|
847
|
+
if (compareVersions(latest, current) <= 0) {
|
|
848
|
+
spinner.succeed(chalk.green(`已是最新版本 v${current}`));
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
spinner.succeed(chalk.green(`发现新版本 v${latest}(当前 v${current})`));
|
|
853
|
+
|
|
854
|
+
const yes = await confirm({ message: `升级到 v${latest}?`, default: true });
|
|
855
|
+
if (!yes) {
|
|
856
|
+
console.log(chalk.dim('已取消'));
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
const installCmd = buildClaudeInstallCommand('vpn');
|
|
861
|
+
const upgradeCmd = { command: installCmd.command, args: [...installCmd.args.slice(0, -1), 'yingclaw@latest'] };
|
|
862
|
+
|
|
863
|
+
console.log(chalk.dim('\n升级中...\n'));
|
|
864
|
+
const result = spawnSync(upgradeCmd.command, upgradeCmd.args, { stdio: 'inherit' });
|
|
865
|
+
|
|
866
|
+
if (result.status === 0) {
|
|
867
|
+
console.log(boxen(
|
|
868
|
+
chalk.bold(`yingclaw 已升级到 v${latest}\n\n`) +
|
|
869
|
+
chalk.dim('重新运行 ') + chalk.cyan('claw') + chalk.dim(' 使新版本生效'),
|
|
870
|
+
{ padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'green', margin: { top: 1, bottom: 1 } }
|
|
871
|
+
));
|
|
872
|
+
} else {
|
|
873
|
+
console.log(chalk.red('\n升级失败,请手动运行:'));
|
|
874
|
+
console.log(chalk.cyan('npm install -g yingclaw@latest'));
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
|
|
828
878
|
async function renderStatusBar(apiStatus) {
|
|
829
879
|
const chalk = (await import('chalk')).default;
|
|
830
880
|
const config = loadConfig();
|
|
@@ -855,6 +905,30 @@ async function renderStatusBar(apiStatus) {
|
|
|
855
905
|
return config ? ` ${cfgPart}` : ` ${claudeIcon} ${claudeText} ${cfgPart}`;
|
|
856
906
|
}
|
|
857
907
|
|
|
908
|
+
async function checkForUpdate() {
|
|
909
|
+
try {
|
|
910
|
+
const controller = new AbortController();
|
|
911
|
+
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
912
|
+
const res = await fetch(`https://registry.npmjs.org/yingclaw/latest`, { signal: controller.signal });
|
|
913
|
+
clearTimeout(timeout);
|
|
914
|
+
if (!res.ok) return null;
|
|
915
|
+
const data = await res.json();
|
|
916
|
+
return data.version || null;
|
|
917
|
+
} catch {
|
|
918
|
+
return null;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
function compareVersions(a, b) {
|
|
923
|
+
const pa = a.split('.').map(Number);
|
|
924
|
+
const pb = b.split('.').map(Number);
|
|
925
|
+
for (let i = 0; i < 3; i++) {
|
|
926
|
+
if ((pa[i] || 0) > (pb[i] || 0)) return 1;
|
|
927
|
+
if ((pa[i] || 0) < (pb[i] || 0)) return -1;
|
|
928
|
+
}
|
|
929
|
+
return 0;
|
|
930
|
+
}
|
|
931
|
+
|
|
858
932
|
// 缓存上次校验的 config 哈希和结果,避免每次回菜单都重检
|
|
859
933
|
let lastCheckedHash = null;
|
|
860
934
|
let lastCheckResult; // undefined / true / false / null
|
|
@@ -885,6 +959,7 @@ async function runAdvancedMenu(chalk, hasConfig) {
|
|
|
885
959
|
{ name: '↩️ 恢复 Claude Code 终端默认', value: 'code-reset' },
|
|
886
960
|
{ name: '↩️ 恢复 Claude 桌面默认', value: 'desktop-reset' },
|
|
887
961
|
{ name: '🗑 清除所有 yingclaw 配置', value: 'reset' },
|
|
962
|
+
{ name: '⬆️ 检查更新', value: 'update' },
|
|
888
963
|
{ name: chalk.dim('↩ 返回主菜单'), value: '__BACK__' },
|
|
889
964
|
],
|
|
890
965
|
});
|
|
@@ -986,6 +1061,7 @@ async function runMenu() {
|
|
|
986
1061
|
'desktop-reset': 'desktop-reset',
|
|
987
1062
|
status: 'status',
|
|
988
1063
|
reset: 'reset',
|
|
1064
|
+
update: 'update',
|
|
989
1065
|
};
|
|
990
1066
|
|
|
991
1067
|
// 执行子命令(用 spawn 隔离,避免 commander 对 program 的副作用)
|
package/lib/desktop.js
CHANGED
|
@@ -22,25 +22,29 @@ function getClaudeDesktopDataDir(options = {}) {
|
|
|
22
22
|
const homeDir = options.homeDir || os.homedir();
|
|
23
23
|
|
|
24
24
|
if (platform === 'darwin') {
|
|
25
|
-
return
|
|
25
|
+
return [homeDir, 'Library', 'Application Support', 'Claude-3p'].join('/');
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
if (platform === 'win32') {
|
|
29
|
-
const appData = options.appData || process.env.APPDATA || options.localAppData ||
|
|
30
|
-
return
|
|
29
|
+
const appData = options.appData || process.env.APPDATA || options.localAppData || [homeDir, 'AppData', 'Roaming'].join('\\');
|
|
30
|
+
return appData + '\\Claude-3p';
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
return null;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
function getClaudeDesktopConfigPath(options = {}) {
|
|
37
|
+
const platform = options.platform || process.platform;
|
|
37
38
|
const dataDir = options.dataDir || getClaudeDesktopDataDir(options);
|
|
38
|
-
|
|
39
|
+
if (!dataDir) return null;
|
|
40
|
+
return dataDir + (platform === 'win32' ? '\\' : '/') + 'claude_desktop_config.json';
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
function getClaudeDesktopConfigLibraryDir(options = {}) {
|
|
44
|
+
const platform = options.platform || process.platform;
|
|
42
45
|
const dataDir = options.dataDir || getClaudeDesktopDataDir(options);
|
|
43
|
-
|
|
46
|
+
if (!dataDir) return null;
|
|
47
|
+
return dataDir + (platform === 'win32' ? '\\' : '/') + 'configLibrary';
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
function readJsonFile(file) {
|
|
@@ -115,6 +119,11 @@ function clearClaudeDesktopConfigLibrary(options = {}) {
|
|
|
115
119
|
|
|
116
120
|
// 写入 Claude-3p/configLibrary/ 下的 enterprise config 条目(主进程从此处读取)
|
|
117
121
|
function writeClaudeDesktopConfig(config, options = {}) {
|
|
122
|
+
const baseUrl = normalizeAnthropicBaseUrl(config.baseUrl);
|
|
123
|
+
if (!baseUrl.startsWith('https://')) {
|
|
124
|
+
throw new Error('Claude 桌面应用要求 Gateway Base URL 使用 HTTPS');
|
|
125
|
+
}
|
|
126
|
+
|
|
118
127
|
const dataDir = options.dataDir || getClaudeDesktopDataDir(options);
|
|
119
128
|
if (!dataDir) {
|
|
120
129
|
return { result: 'unsupported', file: null };
|
|
@@ -126,10 +135,6 @@ function writeClaudeDesktopConfig(config, options = {}) {
|
|
|
126
135
|
const uuid = existingMeta.appliedId || options.uuid || crypto.randomUUID();
|
|
127
136
|
|
|
128
137
|
const models = collectModels(config);
|
|
129
|
-
const baseUrl = normalizeAnthropicBaseUrl(config.baseUrl);
|
|
130
|
-
if (!baseUrl.startsWith('https://')) {
|
|
131
|
-
throw new Error('Claude 桌面应用要求 Gateway Base URL 使用 HTTPS');
|
|
132
|
-
}
|
|
133
138
|
|
|
134
139
|
const entry = {
|
|
135
140
|
inferenceProvider: 'gateway',
|