@shi_zhen/code-helper 0.1.0
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/LICENSE +21 -0
- package/README.md +222 -0
- package/bin/code-helper.js +9 -0
- package/dist/cli/index.d.mts +2 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +3687 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/index.mjs +3680 -0
- package/dist/cli/index.mjs.map +1 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +8 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,3680 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
6
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
7
|
+
}) : x)(function(x) {
|
|
8
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
9
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
10
|
+
});
|
|
11
|
+
var __esm = (fn, res) => function __init() {
|
|
12
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
13
|
+
};
|
|
14
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
15
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
16
|
+
};
|
|
17
|
+
var __export = (target, all) => {
|
|
18
|
+
for (var name in all)
|
|
19
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
20
|
+
};
|
|
21
|
+
var __copyProps = (to, from, except, desc) => {
|
|
22
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
23
|
+
for (let key of __getOwnPropNames(from))
|
|
24
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
25
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
26
|
+
}
|
|
27
|
+
return to;
|
|
28
|
+
};
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
|
|
31
|
+
// package.json
|
|
32
|
+
var require_package = __commonJS({
|
|
33
|
+
"package.json"(exports, module) {
|
|
34
|
+
module.exports = {
|
|
35
|
+
name: "@shi_zhen/code-helper",
|
|
36
|
+
version: "0.1.0",
|
|
37
|
+
description: "Code Helper - A unified tool to manage your Claude Code",
|
|
38
|
+
repository: {
|
|
39
|
+
type: "git",
|
|
40
|
+
url: "https://github.com/shirenchuang/code-helper.git"
|
|
41
|
+
},
|
|
42
|
+
author: "shirenchuang",
|
|
43
|
+
module: "dist/index.js",
|
|
44
|
+
typings: "dist/index.d.ts",
|
|
45
|
+
bin: {
|
|
46
|
+
ch: "./bin/code-helper.js",
|
|
47
|
+
"code-helper": "./bin/code-helper.js"
|
|
48
|
+
},
|
|
49
|
+
files: [
|
|
50
|
+
"bin",
|
|
51
|
+
"dist",
|
|
52
|
+
"LEGAL.md"
|
|
53
|
+
],
|
|
54
|
+
scripts: {
|
|
55
|
+
build: "tsup",
|
|
56
|
+
"build:watch": "tsup --watch",
|
|
57
|
+
dev: "tsup --watch",
|
|
58
|
+
lint: "eslint src --ext .ts",
|
|
59
|
+
test: "jest",
|
|
60
|
+
prepublishOnly: "npm run build"
|
|
61
|
+
},
|
|
62
|
+
dependencies: {
|
|
63
|
+
"@babel/runtime": "^7.18.0",
|
|
64
|
+
boxen: "^5.1.2",
|
|
65
|
+
chalk: "^4.1.2",
|
|
66
|
+
execa: "^5.1.1",
|
|
67
|
+
figlet: "^1.7.0",
|
|
68
|
+
inquirer: "^8.2.6",
|
|
69
|
+
ora: "^5.4.1"
|
|
70
|
+
},
|
|
71
|
+
devDependencies: {
|
|
72
|
+
"@types/figlet": "^1.5.8",
|
|
73
|
+
"@types/inquirer": "^8.2.10",
|
|
74
|
+
"@types/node": "^20.0.0",
|
|
75
|
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
76
|
+
"@typescript-eslint/parser": "^6.0.0",
|
|
77
|
+
eslint: "^8.0.0",
|
|
78
|
+
tsup: "^8.0.0",
|
|
79
|
+
typescript: "^5.0.0"
|
|
80
|
+
},
|
|
81
|
+
engines: {
|
|
82
|
+
node: ">=20"
|
|
83
|
+
},
|
|
84
|
+
keywords: [
|
|
85
|
+
"claude",
|
|
86
|
+
"claude-code",
|
|
87
|
+
"mcp",
|
|
88
|
+
"ai",
|
|
89
|
+
"cli",
|
|
90
|
+
"tools"
|
|
91
|
+
],
|
|
92
|
+
license: "MIT"
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// src/cli/utils/exec.ts
|
|
98
|
+
import execa from "execa";
|
|
99
|
+
async function executeCommand(command, envVars) {
|
|
100
|
+
try {
|
|
101
|
+
let finalCommand = command;
|
|
102
|
+
if (command.includes("|")) {
|
|
103
|
+
finalCommand = `set -o pipefail && ${command}`;
|
|
104
|
+
}
|
|
105
|
+
const { stdout } = await execa.command(finalCommand, {
|
|
106
|
+
shell: true,
|
|
107
|
+
stdio: "inherit",
|
|
108
|
+
env: envVars ? { ...process.env, ...envVars } : void 0
|
|
109
|
+
});
|
|
110
|
+
return stdout || "";
|
|
111
|
+
} catch (error) {
|
|
112
|
+
const execError = error;
|
|
113
|
+
throw new Error(execError.stderr || execError.message || "\u547D\u4EE4\u6267\u884C\u5931\u8D25");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async function executeCommandQuiet(command) {
|
|
117
|
+
try {
|
|
118
|
+
const { stdout } = await execa.command(command, {
|
|
119
|
+
shell: true
|
|
120
|
+
});
|
|
121
|
+
return stdout || "";
|
|
122
|
+
} catch (error) {
|
|
123
|
+
const execError = error;
|
|
124
|
+
throw new Error(execError.stderr || execError.message || "\u547D\u4EE4\u6267\u884C\u5931\u8D25");
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async function commandExists(command) {
|
|
128
|
+
try {
|
|
129
|
+
await execa.command(`which ${command}`, { shell: true });
|
|
130
|
+
return true;
|
|
131
|
+
} catch {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
var init_exec = __esm({
|
|
136
|
+
"src/cli/utils/exec.ts"() {
|
|
137
|
+
"use strict";
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// src/cli/utils/webui.ts
|
|
142
|
+
var webui_exports = {};
|
|
143
|
+
__export(webui_exports, {
|
|
144
|
+
launchWebUI: () => launchWebUI
|
|
145
|
+
});
|
|
146
|
+
import chalk2 from "chalk";
|
|
147
|
+
import inquirer8 from "inquirer";
|
|
148
|
+
async function launchWebUI() {
|
|
149
|
+
console.log(chalk2.cyan("\n\u{1F680} ClaudeCode UI \u542F\u52A8\u6307\u5357\n"));
|
|
150
|
+
console.log(chalk2.yellow("\u8BF7\u5728\u7EC8\u7AEF\u4E2D\u624B\u52A8\u6267\u884C\u4EE5\u4E0B\u547D\u4EE4\u6765\u542F\u52A8 Web UI\uFF1A\n"));
|
|
151
|
+
console.log(chalk2.green.bold(" npx @siteboon/claude-code-ui\n"));
|
|
152
|
+
console.log(
|
|
153
|
+
chalk2.dim("\u8BF4\u660E: \u4F7F\u7528\u5F00\u6E90\u7248\u672C\u7684 Claude Code UI")
|
|
154
|
+
);
|
|
155
|
+
console.log(chalk2.dim(" \u8FD9\u6837\u53EF\u4EE5\u786E\u4FDD\u4F7F\u7528\u6B63\u786E\u7684 Node.js \u7248\u672C\u548C\u4F9D\u8D56"));
|
|
156
|
+
console.log();
|
|
157
|
+
await inquirer8.prompt([
|
|
158
|
+
{
|
|
159
|
+
type: "input",
|
|
160
|
+
name: "confirm",
|
|
161
|
+
message: "\u6309 Enter \u952E\u8FD4\u56DE\u4E3B\u83DC\u5355"
|
|
162
|
+
}
|
|
163
|
+
]);
|
|
164
|
+
}
|
|
165
|
+
var init_webui = __esm({
|
|
166
|
+
"src/cli/utils/webui.ts"() {
|
|
167
|
+
"use strict";
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// src/cli/utils/version.ts
|
|
172
|
+
var version_exports = {};
|
|
173
|
+
__export(version_exports, {
|
|
174
|
+
checkAndUpdate: () => checkAndUpdate,
|
|
175
|
+
testUpdate: () => testUpdate
|
|
176
|
+
});
|
|
177
|
+
import chalk3 from "chalk";
|
|
178
|
+
import ora7 from "ora";
|
|
179
|
+
function getLocalVersion() {
|
|
180
|
+
const pkg3 = require_package();
|
|
181
|
+
return pkg3.version;
|
|
182
|
+
}
|
|
183
|
+
async function getRemoteVersion() {
|
|
184
|
+
try {
|
|
185
|
+
const result = await executeCommandQuiet(
|
|
186
|
+
"npm view code-helper version"
|
|
187
|
+
);
|
|
188
|
+
return result.trim();
|
|
189
|
+
} catch (error) {
|
|
190
|
+
console.error(chalk3.yellow("\u26A0\uFE0F \u65E0\u6CD5\u68C0\u67E5\u7248\u672C\u66F4\u65B0"));
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function readCache() {
|
|
195
|
+
try {
|
|
196
|
+
if (!fs9.existsSync(CACHE_FILE)) {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
const content = fs9.readFileSync(CACHE_FILE, "utf-8");
|
|
200
|
+
return JSON.parse(content);
|
|
201
|
+
} catch {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function writeCache(data) {
|
|
206
|
+
try {
|
|
207
|
+
if (!fs9.existsSync(CACHE_DIR)) {
|
|
208
|
+
fs9.mkdirSync(CACHE_DIR, { recursive: true });
|
|
209
|
+
}
|
|
210
|
+
fs9.writeFileSync(CACHE_FILE, JSON.stringify(data, null, 2));
|
|
211
|
+
} catch (error) {
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function compareVersions(v1, v2) {
|
|
215
|
+
const parts1 = v1.split(".").map(Number);
|
|
216
|
+
const parts2 = v2.split(".").map(Number);
|
|
217
|
+
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
218
|
+
const part1 = parts1[i] || 0;
|
|
219
|
+
const part2 = parts2[i] || 0;
|
|
220
|
+
if (part1 > part2) return 1;
|
|
221
|
+
if (part1 < part2) return -1;
|
|
222
|
+
}
|
|
223
|
+
return 0;
|
|
224
|
+
}
|
|
225
|
+
async function updateToLatest() {
|
|
226
|
+
const spinner = ora7("\u6B63\u5728\u66F4\u65B0\u5230\u6700\u65B0\u7248\u672C...").start();
|
|
227
|
+
try {
|
|
228
|
+
await executeCommand("npm install -g code-helper@latest");
|
|
229
|
+
spinner.succeed(chalk3.green("\u2705 \u66F4\u65B0\u6210\u529F\uFF01"));
|
|
230
|
+
return true;
|
|
231
|
+
} catch (error) {
|
|
232
|
+
spinner.fail(chalk3.red("\u274C \u66F4\u65B0\u5931\u8D25"));
|
|
233
|
+
console.error(chalk3.red(`\u9519\u8BEF: ${error}`));
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
async function checkAndUpdate(forceCheck = false) {
|
|
238
|
+
const localVersion = getLocalVersion();
|
|
239
|
+
const cache = readCache();
|
|
240
|
+
const now = Date.now();
|
|
241
|
+
if (!forceCheck && cache && now - cache.lastCheck < CHECK_INTERVAL) {
|
|
242
|
+
if (compareVersions(cache.latestVersion, localVersion) > 0) {
|
|
243
|
+
console.log(
|
|
244
|
+
chalk3.yellow(
|
|
245
|
+
`
|
|
246
|
+
\u{1F4A1} \u63D0\u793A: \u6709\u65B0\u7248\u672C ${cache.latestVersion} \u53EF\u7528 (\u5F53\u524D\u7248\u672C: ${localVersion})`
|
|
247
|
+
)
|
|
248
|
+
);
|
|
249
|
+
console.log(chalk3.gray(" \u4E0B\u6B21\u542F\u52A8\u65F6\u5C06\u81EA\u52A8\u66F4\u65B0...\n"));
|
|
250
|
+
}
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const spinner = ora7("\u6B63\u5728\u68C0\u67E5\u7248\u672C\u66F4\u65B0...").start();
|
|
254
|
+
const remoteVersion = await getRemoteVersion();
|
|
255
|
+
if (!remoteVersion) {
|
|
256
|
+
spinner.stop();
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
writeCache({
|
|
260
|
+
lastCheck: now,
|
|
261
|
+
latestVersion: remoteVersion
|
|
262
|
+
});
|
|
263
|
+
if (compareVersions(remoteVersion, localVersion) > 0) {
|
|
264
|
+
spinner.succeed(
|
|
265
|
+
chalk3.cyan(`\u53D1\u73B0\u65B0\u7248\u672C: ${remoteVersion} (\u5F53\u524D\u7248\u672C: ${localVersion})`)
|
|
266
|
+
);
|
|
267
|
+
console.log(chalk3.gray("\u6B63\u5728\u81EA\u52A8\u66F4\u65B0...\n"));
|
|
268
|
+
const success = await updateToLatest();
|
|
269
|
+
if (success) {
|
|
270
|
+
console.log(
|
|
271
|
+
chalk3.green("\n\u2728 \u66F4\u65B0\u5B8C\u6210\uFF01\u8BF7\u91CD\u65B0\u8FD0\u884C\u547D\u4EE4\u4EE5\u4F7F\u7528\u6700\u65B0\u7248\u672C\u3002\n")
|
|
272
|
+
);
|
|
273
|
+
process.exit(0);
|
|
274
|
+
} else {
|
|
275
|
+
console.log(
|
|
276
|
+
chalk3.yellow(
|
|
277
|
+
"\n\u26A0\uFE0F \u81EA\u52A8\u66F4\u65B0\u5931\u8D25\uFF0C\u4F60\u53EF\u4EE5\u624B\u52A8\u8FD0\u884C: npm install -g code-helper@latest\n"
|
|
278
|
+
)
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
} else {
|
|
282
|
+
spinner.succeed(chalk3.green(`\u5F53\u524D\u5DF2\u662F\u6700\u65B0\u7248\u672C v${localVersion}`));
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
async function testUpdate() {
|
|
286
|
+
const localVersion = getLocalVersion();
|
|
287
|
+
console.log(chalk3.cyan("\n\u{1F9EA} \u6D4B\u8BD5\u6A21\u5F0F: \u6A21\u62DF\u7248\u672C\u66F4\u65B0\u6D41\u7A0B\n"));
|
|
288
|
+
console.log(chalk3.gray(`\u5F53\u524D\u7248\u672C: ${localVersion}`));
|
|
289
|
+
console.log(chalk3.gray("\u6B63\u5728\u68C0\u67E5\u8FDC\u7A0B\u7248\u672C...\n"));
|
|
290
|
+
const remoteVersion = await getRemoteVersion();
|
|
291
|
+
if (!remoteVersion) {
|
|
292
|
+
console.log(chalk3.red("\u274C \u65E0\u6CD5\u83B7\u53D6\u8FDC\u7A0B\u7248\u672C"));
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
console.log(chalk3.gray(`\u8FDC\u7A0B\u7248\u672C: ${remoteVersion}
|
|
296
|
+
`));
|
|
297
|
+
if (compareVersions(remoteVersion, localVersion) > 0) {
|
|
298
|
+
console.log(chalk3.green("\u2705 \u8FDC\u7A0B\u7248\u672C\u66F4\u65B0\uFF0C\u4F1A\u89E6\u53D1\u81EA\u52A8\u66F4\u65B0"));
|
|
299
|
+
} else if (compareVersions(remoteVersion, localVersion) === 0) {
|
|
300
|
+
console.log(chalk3.yellow("\u26A0\uFE0F \u8FDC\u7A0B\u7248\u672C\u4E0E\u672C\u5730\u7248\u672C\u76F8\u540C"));
|
|
301
|
+
console.log(chalk3.gray(" \u771F\u5B9E\u4F7F\u7528\u65F6\u4E0D\u4F1A\u89E6\u53D1\u66F4\u65B0"));
|
|
302
|
+
} else {
|
|
303
|
+
console.log(chalk3.yellow("\u26A0\uFE0F \u672C\u5730\u7248\u672C\u9AD8\u4E8E\u8FDC\u7A0B\u7248\u672C"));
|
|
304
|
+
console.log(chalk3.gray(" \u8FD9\u901A\u5E38\u53D1\u751F\u5728\u672C\u5730\u5F00\u53D1\u65F6"));
|
|
305
|
+
}
|
|
306
|
+
console.log(
|
|
307
|
+
chalk3.cyan("\n\u{1F4A1} \u63D0\u793A: \u53D1\u5E03\u65B0\u7248\u672C\u540E\uFF0C\u7528\u6237\u9996\u6B21\u542F\u52A8\u65F6\u4F1A\u81EA\u52A8\u66F4\u65B0\n")
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
var os10, path10, fs9, CACHE_DIR, CACHE_FILE, CHECK_INTERVAL;
|
|
311
|
+
var init_version = __esm({
|
|
312
|
+
"src/cli/utils/version.ts"() {
|
|
313
|
+
"use strict";
|
|
314
|
+
init_exec();
|
|
315
|
+
os10 = __require("os");
|
|
316
|
+
path10 = __require("path");
|
|
317
|
+
fs9 = __require("fs");
|
|
318
|
+
CACHE_DIR = path10.join(os10.homedir(), ".code-helper");
|
|
319
|
+
CACHE_FILE = path10.join(CACHE_DIR, "version-check.json");
|
|
320
|
+
CHECK_INTERVAL = 24 * 60 * 60 * 1e3;
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// src/cli/menus/main.ts
|
|
325
|
+
import inquirer9 from "inquirer";
|
|
326
|
+
|
|
327
|
+
// src/cli/ui.ts
|
|
328
|
+
import boxen from "boxen";
|
|
329
|
+
import chalk from "chalk";
|
|
330
|
+
import figlet from "figlet";
|
|
331
|
+
var pkg = require_package();
|
|
332
|
+
var VERSION = pkg.version;
|
|
333
|
+
var theme = {
|
|
334
|
+
primary: chalk.hex("#FF8C00"),
|
|
335
|
+
// 深橙色
|
|
336
|
+
secondary: chalk.hex("#FFA500"),
|
|
337
|
+
// 橙色
|
|
338
|
+
success: chalk.green,
|
|
339
|
+
error: chalk.red,
|
|
340
|
+
warning: chalk.yellow,
|
|
341
|
+
info: chalk.cyan,
|
|
342
|
+
dim: chalk.gray
|
|
343
|
+
};
|
|
344
|
+
function generateTitle() {
|
|
345
|
+
try {
|
|
346
|
+
const title = figlet.textSync("CODE HELPER", {
|
|
347
|
+
font: "Standard",
|
|
348
|
+
horizontalLayout: "default"
|
|
349
|
+
});
|
|
350
|
+
return theme.primary(title);
|
|
351
|
+
} catch {
|
|
352
|
+
return theme.primary("CODE HELPER");
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
function showHeader() {
|
|
356
|
+
console.clear();
|
|
357
|
+
const title = generateTitle();
|
|
358
|
+
const subtitle = `Code Helper v${VERSION}
|
|
359
|
+
A unified tool to manage your Claude Code`;
|
|
360
|
+
const header = boxen(
|
|
361
|
+
`${title}
|
|
362
|
+
|
|
363
|
+
${theme.secondary(subtitle)}`,
|
|
364
|
+
{
|
|
365
|
+
padding: 1,
|
|
366
|
+
margin: 1,
|
|
367
|
+
borderStyle: "round",
|
|
368
|
+
borderColor: "yellow",
|
|
369
|
+
textAlignment: "center"
|
|
370
|
+
}
|
|
371
|
+
);
|
|
372
|
+
console.log(header);
|
|
373
|
+
}
|
|
374
|
+
function showSuccess(message) {
|
|
375
|
+
console.log(theme.success(`\u2713 ${message}`));
|
|
376
|
+
}
|
|
377
|
+
function showError(message) {
|
|
378
|
+
console.log(theme.error(`\u2717 ${message}`));
|
|
379
|
+
}
|
|
380
|
+
function showWarning(message) {
|
|
381
|
+
console.log(theme.warning(`\u26A0 ${message}`));
|
|
382
|
+
}
|
|
383
|
+
function showInfo(message) {
|
|
384
|
+
console.log(theme.info(`\u2139 ${message}`));
|
|
385
|
+
}
|
|
386
|
+
function createBoxTitle(title) {
|
|
387
|
+
return boxen(theme.primary(title), {
|
|
388
|
+
padding: { left: 2, right: 2, top: 0, bottom: 0 },
|
|
389
|
+
borderStyle: "round",
|
|
390
|
+
borderColor: "yellow",
|
|
391
|
+
textAlignment: "center"
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// src/cli/utils/claude.ts
|
|
396
|
+
import { execSync } from "child_process";
|
|
397
|
+
import fs from "fs";
|
|
398
|
+
import os from "os";
|
|
399
|
+
import path from "path";
|
|
400
|
+
var cachedInfo = null;
|
|
401
|
+
var lastCheckTime = 0;
|
|
402
|
+
var CACHE_DURATION = 5e3;
|
|
403
|
+
function execSyncWithTimeout(command, timeoutMs = 2e3) {
|
|
404
|
+
try {
|
|
405
|
+
return execSync(command, {
|
|
406
|
+
encoding: "utf-8",
|
|
407
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
408
|
+
timeout: timeoutMs
|
|
409
|
+
}).trim();
|
|
410
|
+
} catch {
|
|
411
|
+
return "";
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
function detectClaudeInstallationSync() {
|
|
415
|
+
try {
|
|
416
|
+
const whichResult = execSyncWithTimeout("which claude", 1e3);
|
|
417
|
+
if (!whichResult) {
|
|
418
|
+
return { installed: false };
|
|
419
|
+
}
|
|
420
|
+
let installMethod = "unknown";
|
|
421
|
+
const officialPaths = [
|
|
422
|
+
path.join(os.homedir(), ".local/bin/claude"),
|
|
423
|
+
path.join(os.homedir(), ".claude/bin/claude")
|
|
424
|
+
];
|
|
425
|
+
for (const officialPath of officialPaths) {
|
|
426
|
+
if (whichResult === officialPath || fs.existsSync(officialPath)) {
|
|
427
|
+
installMethod = "official";
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
if (installMethod === "unknown" && whichResult.includes("node_modules")) {
|
|
432
|
+
installMethod = "npm";
|
|
433
|
+
}
|
|
434
|
+
if (installMethod === "unknown" && process.platform === "darwin" && whichResult.includes("homebrew")) {
|
|
435
|
+
installMethod = "homebrew";
|
|
436
|
+
}
|
|
437
|
+
let version;
|
|
438
|
+
try {
|
|
439
|
+
const versionOutput = execSyncWithTimeout("claude --version", 1500);
|
|
440
|
+
if (versionOutput) {
|
|
441
|
+
const versionMatch = versionOutput.match(/(\d+\.\d+\.\d+)/);
|
|
442
|
+
version = versionMatch ? versionMatch[1] : versionOutput;
|
|
443
|
+
}
|
|
444
|
+
} catch {
|
|
445
|
+
version = void 0;
|
|
446
|
+
}
|
|
447
|
+
return {
|
|
448
|
+
installed: true,
|
|
449
|
+
version,
|
|
450
|
+
installMethod,
|
|
451
|
+
binaryPath: whichResult
|
|
452
|
+
};
|
|
453
|
+
} catch {
|
|
454
|
+
return { installed: false };
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
function formatInstallMethod(method) {
|
|
458
|
+
switch (method) {
|
|
459
|
+
case "npm":
|
|
460
|
+
return "npm \u5168\u5C40\u5B89\u88C5";
|
|
461
|
+
case "official":
|
|
462
|
+
return "\u5B98\u65B9\u811A\u672C\u5B89\u88C5";
|
|
463
|
+
case "homebrew":
|
|
464
|
+
return "Homebrew \u5B89\u88C5";
|
|
465
|
+
case "unknown":
|
|
466
|
+
default:
|
|
467
|
+
return "\u672A\u77E5\u65B9\u5F0F";
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
function detectClaudeInstallation() {
|
|
471
|
+
const now = Date.now();
|
|
472
|
+
if (cachedInfo && now - lastCheckTime < CACHE_DURATION) {
|
|
473
|
+
return cachedInfo;
|
|
474
|
+
}
|
|
475
|
+
cachedInfo = detectClaudeInstallationSync();
|
|
476
|
+
lastCheckTime = now;
|
|
477
|
+
return cachedInfo;
|
|
478
|
+
}
|
|
479
|
+
function getCachedClaudeInfo() {
|
|
480
|
+
return cachedInfo;
|
|
481
|
+
}
|
|
482
|
+
function clearClaudeCache() {
|
|
483
|
+
cachedInfo = null;
|
|
484
|
+
lastCheckTime = 0;
|
|
485
|
+
}
|
|
486
|
+
function formatClaudeStatus(info) {
|
|
487
|
+
if (!info.installed) {
|
|
488
|
+
return "\u672A\u5B89\u88C5";
|
|
489
|
+
}
|
|
490
|
+
const parts = ["\u5DF2\u5B89\u88C5"];
|
|
491
|
+
if (info.version) {
|
|
492
|
+
parts.push(`v${info.version}`);
|
|
493
|
+
}
|
|
494
|
+
if (info.installMethod) {
|
|
495
|
+
parts.push(`(${formatInstallMethod(info.installMethod)})`);
|
|
496
|
+
}
|
|
497
|
+
return parts.join(" ");
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// src/cli/utils/config.ts
|
|
501
|
+
import fs2 from "fs";
|
|
502
|
+
import os2 from "os";
|
|
503
|
+
import path2 from "path";
|
|
504
|
+
var CLAUDE_DIR = path2.join(os2.homedir(), ".claude");
|
|
505
|
+
var SETTINGS_FILE = path2.join(CLAUDE_DIR, "settings.json");
|
|
506
|
+
var CODE_HELPER_CONFIG_FILE = path2.join(CLAUDE_DIR, "code-helper.json");
|
|
507
|
+
var CODE_HELPER_DIR = path2.join(os2.homedir(), ".code-helper");
|
|
508
|
+
var PROFILES_FILE = path2.join(CODE_HELPER_DIR, "profiles.json");
|
|
509
|
+
var GLM_BASE_URL = "https://open.bigmodel.cn/api/anthropic";
|
|
510
|
+
var KIMI_BASE_URL = "https://api.kimi.com/coding";
|
|
511
|
+
var DEFAULT_BASE_URL = "https://api.anthropic.com";
|
|
512
|
+
function ensureClaudeDir() {
|
|
513
|
+
if (!fs2.existsSync(CLAUDE_DIR)) {
|
|
514
|
+
fs2.mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
function readClaudeSettings() {
|
|
518
|
+
try {
|
|
519
|
+
if (fs2.existsSync(SETTINGS_FILE)) {
|
|
520
|
+
const content = fs2.readFileSync(SETTINGS_FILE, "utf-8");
|
|
521
|
+
return JSON.parse(content);
|
|
522
|
+
}
|
|
523
|
+
} catch {
|
|
524
|
+
}
|
|
525
|
+
return {};
|
|
526
|
+
}
|
|
527
|
+
function writeClaudeSettings(settings) {
|
|
528
|
+
ensureClaudeDir();
|
|
529
|
+
fs2.writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2), "utf-8");
|
|
530
|
+
}
|
|
531
|
+
function readCodeHelperConfig() {
|
|
532
|
+
try {
|
|
533
|
+
if (fs2.existsSync(CODE_HELPER_CONFIG_FILE)) {
|
|
534
|
+
const content = fs2.readFileSync(CODE_HELPER_CONFIG_FILE, "utf-8");
|
|
535
|
+
return JSON.parse(content);
|
|
536
|
+
}
|
|
537
|
+
} catch {
|
|
538
|
+
}
|
|
539
|
+
return {
|
|
540
|
+
version: "0.1.0",
|
|
541
|
+
installedMcps: [],
|
|
542
|
+
installedPlugins: [],
|
|
543
|
+
preferences: {
|
|
544
|
+
defaultBaseUrl: DEFAULT_BASE_URL
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
function writeCodeHelperConfig(config) {
|
|
549
|
+
ensureClaudeDir();
|
|
550
|
+
fs2.writeFileSync(CODE_HELPER_CONFIG_FILE, JSON.stringify(config, null, 2), "utf-8");
|
|
551
|
+
}
|
|
552
|
+
function getApiKeyStatus() {
|
|
553
|
+
const settings = readClaudeSettings();
|
|
554
|
+
const apiKey = settings.env?.ANTHROPIC_AUTH_TOKEN;
|
|
555
|
+
if (apiKey && apiKey.length > 0) {
|
|
556
|
+
const masked = apiKey.substring(0, 4) + "****";
|
|
557
|
+
return theme.success(`\u5DF2\u8BBE\u7F6E (${masked})`);
|
|
558
|
+
}
|
|
559
|
+
return theme.dim("\u672A\u8BBE\u7F6E");
|
|
560
|
+
}
|
|
561
|
+
function getBaseUrlStatus() {
|
|
562
|
+
const settings = readClaudeSettings();
|
|
563
|
+
const baseUrl = settings.env?.ANTHROPIC_BASE_URL;
|
|
564
|
+
if (baseUrl && baseUrl.length > 0) {
|
|
565
|
+
try {
|
|
566
|
+
const url = new URL(baseUrl);
|
|
567
|
+
return theme.success(url.host);
|
|
568
|
+
} catch {
|
|
569
|
+
return theme.success(baseUrl);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
return theme.dim("\u672A\u8BBE\u7F6E");
|
|
573
|
+
}
|
|
574
|
+
async function saveApiConfig(baseUrl, apiKey, options) {
|
|
575
|
+
const settings = readClaudeSettings();
|
|
576
|
+
if (!settings.env) {
|
|
577
|
+
settings.env = {};
|
|
578
|
+
}
|
|
579
|
+
delete settings.env.ANTHROPIC_MODEL;
|
|
580
|
+
delete settings.env.ANTHROPIC_SMALL_FAST_MODEL;
|
|
581
|
+
delete settings.env.ANTHROPIC_DEFAULT_HAIKU_MODEL;
|
|
582
|
+
delete settings.env.ANTHROPIC_DEFAULT_OPUS_MODEL;
|
|
583
|
+
delete settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL;
|
|
584
|
+
delete settings.env.API_TIMEOUT_MS;
|
|
585
|
+
delete settings.skipWebFetchPreflight;
|
|
586
|
+
settings.env.ANTHROPIC_AUTH_TOKEN = apiKey;
|
|
587
|
+
settings.env.ANTHROPIC_BASE_URL = baseUrl;
|
|
588
|
+
settings.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = "1";
|
|
589
|
+
if (options?.model) {
|
|
590
|
+
settings.env.ANTHROPIC_MODEL = options.model;
|
|
591
|
+
}
|
|
592
|
+
if (options?.smallFastModel) {
|
|
593
|
+
settings.env.ANTHROPIC_SMALL_FAST_MODEL = options.smallFastModel;
|
|
594
|
+
}
|
|
595
|
+
if (options?.haikuModel) {
|
|
596
|
+
settings.env.ANTHROPIC_DEFAULT_HAIKU_MODEL = options.haikuModel;
|
|
597
|
+
}
|
|
598
|
+
if (options?.opusModel) {
|
|
599
|
+
settings.env.ANTHROPIC_DEFAULT_OPUS_MODEL = options.opusModel;
|
|
600
|
+
}
|
|
601
|
+
if (options?.sonnetModel) {
|
|
602
|
+
settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL = options.sonnetModel;
|
|
603
|
+
}
|
|
604
|
+
if (options?.skipWebFetchPreflight) {
|
|
605
|
+
settings.skipWebFetchPreflight = true;
|
|
606
|
+
}
|
|
607
|
+
if (options?.timeout) {
|
|
608
|
+
settings.env.API_TIMEOUT_MS = options.timeout;
|
|
609
|
+
}
|
|
610
|
+
writeClaudeSettings(settings);
|
|
611
|
+
const helperConfig = readCodeHelperConfig();
|
|
612
|
+
helperConfig.preferences.defaultBaseUrl = baseUrl;
|
|
613
|
+
writeCodeHelperConfig(helperConfig);
|
|
614
|
+
}
|
|
615
|
+
function ensureCodeHelperDir() {
|
|
616
|
+
if (!fs2.existsSync(CODE_HELPER_DIR)) {
|
|
617
|
+
fs2.mkdirSync(CODE_HELPER_DIR, { recursive: true });
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
function readProfiles() {
|
|
621
|
+
try {
|
|
622
|
+
if (fs2.existsSync(PROFILES_FILE)) {
|
|
623
|
+
const content = fs2.readFileSync(PROFILES_FILE, "utf-8");
|
|
624
|
+
return JSON.parse(content);
|
|
625
|
+
}
|
|
626
|
+
} catch {
|
|
627
|
+
}
|
|
628
|
+
return {
|
|
629
|
+
version: "1.0.0",
|
|
630
|
+
currentProfileId: null,
|
|
631
|
+
profiles: []
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
function writeProfiles(config) {
|
|
635
|
+
ensureCodeHelperDir();
|
|
636
|
+
fs2.writeFileSync(PROFILES_FILE, JSON.stringify(config, null, 2), "utf-8");
|
|
637
|
+
}
|
|
638
|
+
function getAllProfiles() {
|
|
639
|
+
return readProfiles().profiles;
|
|
640
|
+
}
|
|
641
|
+
function getCurrentProfile() {
|
|
642
|
+
const config = readProfiles();
|
|
643
|
+
if (!config.currentProfileId) return null;
|
|
644
|
+
return config.profiles.find((p) => p.id === config.currentProfileId) || null;
|
|
645
|
+
}
|
|
646
|
+
function addProfile(profile) {
|
|
647
|
+
const config = readProfiles();
|
|
648
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
649
|
+
const newProfile = {
|
|
650
|
+
...profile,
|
|
651
|
+
id: `profile_${Date.now()}`,
|
|
652
|
+
createdAt: now,
|
|
653
|
+
updatedAt: now
|
|
654
|
+
};
|
|
655
|
+
config.profiles.push(newProfile);
|
|
656
|
+
writeProfiles(config);
|
|
657
|
+
return newProfile;
|
|
658
|
+
}
|
|
659
|
+
function deleteProfile(id) {
|
|
660
|
+
const config = readProfiles();
|
|
661
|
+
const index = config.profiles.findIndex((p) => p.id === id);
|
|
662
|
+
if (index === -1) return false;
|
|
663
|
+
config.profiles.splice(index, 1);
|
|
664
|
+
if (config.currentProfileId === id) {
|
|
665
|
+
config.currentProfileId = null;
|
|
666
|
+
}
|
|
667
|
+
writeProfiles(config);
|
|
668
|
+
return true;
|
|
669
|
+
}
|
|
670
|
+
function switchProfile(id) {
|
|
671
|
+
const config = readProfiles();
|
|
672
|
+
const profile = config.profiles.find((p) => p.id === id);
|
|
673
|
+
if (!profile) return false;
|
|
674
|
+
const settings = readClaudeSettings();
|
|
675
|
+
if (!settings.env) {
|
|
676
|
+
settings.env = {};
|
|
677
|
+
}
|
|
678
|
+
delete settings.env.ANTHROPIC_MODEL;
|
|
679
|
+
delete settings.env.ANTHROPIC_SMALL_FAST_MODEL;
|
|
680
|
+
delete settings.env.ANTHROPIC_DEFAULT_HAIKU_MODEL;
|
|
681
|
+
delete settings.env.ANTHROPIC_DEFAULT_OPUS_MODEL;
|
|
682
|
+
delete settings.env.ANTHROPIC_DEFAULT_SONNET_MODEL;
|
|
683
|
+
delete settings.env.API_TIMEOUT_MS;
|
|
684
|
+
delete settings.skipWebFetchPreflight;
|
|
685
|
+
settings.env.ANTHROPIC_AUTH_TOKEN = profile.apiKey;
|
|
686
|
+
settings.env.ANTHROPIC_BASE_URL = profile.baseUrl;
|
|
687
|
+
settings.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = "1";
|
|
688
|
+
if (profile.model) {
|
|
689
|
+
settings.env.ANTHROPIC_MODEL = profile.model;
|
|
690
|
+
}
|
|
691
|
+
if (profile.timeout) {
|
|
692
|
+
settings.env.API_TIMEOUT_MS = profile.timeout;
|
|
693
|
+
}
|
|
694
|
+
writeClaudeSettings(settings);
|
|
695
|
+
config.currentProfileId = id;
|
|
696
|
+
writeProfiles(config);
|
|
697
|
+
return true;
|
|
698
|
+
}
|
|
699
|
+
function getCurrentProfileStatus() {
|
|
700
|
+
const current = getCurrentProfile();
|
|
701
|
+
if (current) {
|
|
702
|
+
return theme.success(`${current.name}`);
|
|
703
|
+
}
|
|
704
|
+
return theme.dim("\u672A\u9009\u62E9\u914D\u7F6E");
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// src/cli/menus/auth.ts
|
|
708
|
+
import inquirer from "inquirer";
|
|
709
|
+
import ora from "ora";
|
|
710
|
+
|
|
711
|
+
// src/cli/utils/api.ts
|
|
712
|
+
import http from "http";
|
|
713
|
+
import https from "https";
|
|
714
|
+
async function verifyApiKey(baseUrl, apiKey) {
|
|
715
|
+
return new Promise((resolve) => {
|
|
716
|
+
try {
|
|
717
|
+
const url = new URL(baseUrl);
|
|
718
|
+
const isHttps = url.protocol === "https:";
|
|
719
|
+
const client = isHttps ? https : http;
|
|
720
|
+
const testPayload = JSON.stringify({
|
|
721
|
+
model: "claude-3-5-sonnet-20241022",
|
|
722
|
+
max_tokens: 1,
|
|
723
|
+
messages: [{ role: "user", content: "test" }]
|
|
724
|
+
});
|
|
725
|
+
const requestOptions = {
|
|
726
|
+
hostname: url.hostname,
|
|
727
|
+
port: url.port || (isHttps ? 443 : 80),
|
|
728
|
+
path: `${url.pathname}/v1/messages`.replace(/\/+/g, "/"),
|
|
729
|
+
method: "POST",
|
|
730
|
+
headers: {
|
|
731
|
+
"x-api-key": apiKey,
|
|
732
|
+
"anthropic-version": "2023-06-01",
|
|
733
|
+
"Content-Type": "application/json",
|
|
734
|
+
"Content-Length": Buffer.byteLength(testPayload)
|
|
735
|
+
},
|
|
736
|
+
timeout: 15e3
|
|
737
|
+
// 15秒超时
|
|
738
|
+
};
|
|
739
|
+
const req = client.request(requestOptions, (res) => {
|
|
740
|
+
let responseData = "";
|
|
741
|
+
res.on("data", (chunk) => {
|
|
742
|
+
responseData += chunk;
|
|
743
|
+
});
|
|
744
|
+
res.on("end", () => {
|
|
745
|
+
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
746
|
+
resolve({ valid: true });
|
|
747
|
+
} else if (res.statusCode === 401 || res.statusCode === 403) {
|
|
748
|
+
resolve({ valid: false, error: "API Key \u65E0\u6548\u6216\u672A\u6388\u6743" });
|
|
749
|
+
} else if (res.statusCode === 400) {
|
|
750
|
+
try {
|
|
751
|
+
const errorData = JSON.parse(responseData);
|
|
752
|
+
if (errorData.error?.message) {
|
|
753
|
+
if (errorData.error.message.includes("model") || errorData.error.message.includes("\u4E0D\u652F\u6301")) {
|
|
754
|
+
resolve({ valid: true });
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
} catch {
|
|
759
|
+
}
|
|
760
|
+
resolve({ valid: true });
|
|
761
|
+
} else {
|
|
762
|
+
resolve({
|
|
763
|
+
valid: false,
|
|
764
|
+
error: `\u670D\u52A1\u5668\u8FD4\u56DE\u9519\u8BEF (${res.statusCode})`
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
});
|
|
768
|
+
});
|
|
769
|
+
req.on("error", (error) => {
|
|
770
|
+
if (error.message.includes("ENOTFOUND")) {
|
|
771
|
+
resolve({
|
|
772
|
+
valid: false,
|
|
773
|
+
error: "Base URL \u65E0\u6CD5\u8BBF\u95EE\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u6216 URL \u662F\u5426\u6B63\u786E"
|
|
774
|
+
});
|
|
775
|
+
} else if (error.message.includes("ETIMEDOUT") || error.message.includes("ECONNREFUSED")) {
|
|
776
|
+
resolve({ valid: false, error: "\u8FDE\u63A5\u8D85\u65F6\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u6216\u4EE3\u7406\u8BBE\u7F6E" });
|
|
777
|
+
} else {
|
|
778
|
+
resolve({ valid: false, error: `\u7F51\u7EDC\u9519\u8BEF: ${error.message}` });
|
|
779
|
+
}
|
|
780
|
+
});
|
|
781
|
+
req.on("timeout", () => {
|
|
782
|
+
req.destroy();
|
|
783
|
+
resolve({ valid: false, error: "\u8BF7\u6C42\u8D85\u65F6\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u8FDE\u63A5" });
|
|
784
|
+
});
|
|
785
|
+
req.write(testPayload);
|
|
786
|
+
req.end();
|
|
787
|
+
} catch (error) {
|
|
788
|
+
resolve({
|
|
789
|
+
valid: false,
|
|
790
|
+
error: `\u9A8C\u8BC1\u5931\u8D25: ${error.message}`
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// src/cli/menus/auth.ts
|
|
797
|
+
async function showAuthMenu() {
|
|
798
|
+
while (true) {
|
|
799
|
+
showHeader();
|
|
800
|
+
console.log(createBoxTitle("\u6388\u6743\u767B\u5F55"));
|
|
801
|
+
console.log();
|
|
802
|
+
const currentProfile = getCurrentProfile();
|
|
803
|
+
if (currentProfile) {
|
|
804
|
+
console.log(
|
|
805
|
+
`${theme.primary("\u5F53\u524D\u914D\u7F6E\uFF1A")} ${theme.success(currentProfile.name)}`
|
|
806
|
+
);
|
|
807
|
+
console.log(`${theme.dim(" Base URL:")} ${currentProfile.baseUrl}`);
|
|
808
|
+
console.log(
|
|
809
|
+
`${theme.dim(" API Key:")} ${maskApiKey(currentProfile.apiKey)}`
|
|
810
|
+
);
|
|
811
|
+
console.log();
|
|
812
|
+
}
|
|
813
|
+
const savedProfiles = getAllProfiles();
|
|
814
|
+
const choices = [
|
|
815
|
+
{
|
|
816
|
+
name: `Claude API ${theme.dim("(\u63A8\u8350 - \u5B98\u65B9 API)")}`,
|
|
817
|
+
value: { type: "claude" }
|
|
818
|
+
},
|
|
819
|
+
{
|
|
820
|
+
name: `\u667A\u8C31 GLM AI ${theme.dim("(\u5F00\u653E\u5E73\u53F0)")}`,
|
|
821
|
+
value: { type: "glm" }
|
|
822
|
+
},
|
|
823
|
+
{
|
|
824
|
+
name: `Kimi AI ${theme.dim("(\u6708\u4E4B\u6697\u9762)")}`,
|
|
825
|
+
value: { type: "kimi" }
|
|
826
|
+
},
|
|
827
|
+
{
|
|
828
|
+
name: `\u624B\u52A8\u8F93\u5165\u5BC6\u94A5 ${theme.dim("(\u81EA\u5B9A\u4E49 Base URL)")}`,
|
|
829
|
+
value: { type: "manual" }
|
|
830
|
+
}
|
|
831
|
+
];
|
|
832
|
+
if (savedProfiles.length > 0) {
|
|
833
|
+
choices.push(new inquirer.Separator());
|
|
834
|
+
choices.push(new inquirer.Separator(theme.dim("\u2500\u2500 \u5DF2\u4FDD\u5B58\u7684\u914D\u7F6E \u2500\u2500")));
|
|
835
|
+
for (const profile of savedProfiles) {
|
|
836
|
+
const isCurrent = currentProfile?.id === profile.id;
|
|
837
|
+
if (isCurrent) {
|
|
838
|
+
choices.push({
|
|
839
|
+
name: `${theme.success("\u2713")} ${theme.info("\u6062\u590D")} ${profile.name} ${theme.dim("(\u5F53\u524D)")}`,
|
|
840
|
+
value: { type: "restore", profile }
|
|
841
|
+
});
|
|
842
|
+
} else {
|
|
843
|
+
choices.push({
|
|
844
|
+
name: ` ${theme.info("\u6062\u590D")} ${profile.name}`,
|
|
845
|
+
value: { type: "restore", profile }
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
choices.push({
|
|
850
|
+
name: ` ${theme.error("\u2715")} \u7BA1\u7406\u5DF2\u4FDD\u5B58\u7684\u914D\u7F6E`,
|
|
851
|
+
value: { type: "manage" }
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
choices.push(
|
|
855
|
+
new inquirer.Separator(),
|
|
856
|
+
{
|
|
857
|
+
name: `${theme.dim("<-")} \u8FD4\u56DE`,
|
|
858
|
+
value: { type: "back" }
|
|
859
|
+
},
|
|
860
|
+
{
|
|
861
|
+
name: `${theme.dim("X")} \u9000\u51FA`,
|
|
862
|
+
value: { type: "exit" }
|
|
863
|
+
}
|
|
864
|
+
);
|
|
865
|
+
const { choice } = await inquirer.prompt([
|
|
866
|
+
{
|
|
867
|
+
type: "list",
|
|
868
|
+
name: "choice",
|
|
869
|
+
message: "\u8BF7\u9009\u62E9\u6388\u6743\u65B9\u5F0F\uFF1A",
|
|
870
|
+
pageSize: 15,
|
|
871
|
+
choices
|
|
872
|
+
}
|
|
873
|
+
]);
|
|
874
|
+
switch (choice.type) {
|
|
875
|
+
case "claude":
|
|
876
|
+
await handleClaudeAuth();
|
|
877
|
+
break;
|
|
878
|
+
case "glm":
|
|
879
|
+
await handleGlmAuth();
|
|
880
|
+
break;
|
|
881
|
+
case "kimi":
|
|
882
|
+
await handleKimiAuth();
|
|
883
|
+
break;
|
|
884
|
+
case "manual":
|
|
885
|
+
await handleManualAuth();
|
|
886
|
+
break;
|
|
887
|
+
case "restore":
|
|
888
|
+
await handleRestoreProfile(choice.profile);
|
|
889
|
+
break;
|
|
890
|
+
case "manage":
|
|
891
|
+
await handleManageProfiles();
|
|
892
|
+
break;
|
|
893
|
+
case "back":
|
|
894
|
+
return;
|
|
895
|
+
case "exit":
|
|
896
|
+
process.exit(0);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
async function handleClaudeAuth() {
|
|
901
|
+
console.log();
|
|
902
|
+
console.log(theme.primary("Claude API \u6388\u6743"));
|
|
903
|
+
console.log();
|
|
904
|
+
console.log(theme.dim("Base URL: ") + DEFAULT_BASE_URL);
|
|
905
|
+
console.log();
|
|
906
|
+
showInfo("\u83B7\u53D6 API Key \u65B9\u5F0F\uFF1A");
|
|
907
|
+
console.log(
|
|
908
|
+
theme.dim(" 1. \u8BBF\u95EE https://console.anthropic.com/settings/keys")
|
|
909
|
+
);
|
|
910
|
+
console.log(theme.dim(" 2. \u521B\u5EFA\u6216\u590D\u5236 API Key"));
|
|
911
|
+
console.log(theme.dim(" 3. \u7C98\u8D34\u5230\u4E0B\u65B9"));
|
|
912
|
+
console.log();
|
|
913
|
+
console.log(theme.dim(" (\u76F4\u63A5\u6309\u56DE\u8F66\u53EF\u8FD4\u56DE)"));
|
|
914
|
+
console.log();
|
|
915
|
+
const { apiKey } = await inquirer.prompt([
|
|
916
|
+
{
|
|
917
|
+
type: "password",
|
|
918
|
+
name: "apiKey",
|
|
919
|
+
message: "\u8BF7\u8F93\u5165 Claude API Key:",
|
|
920
|
+
mask: "*"
|
|
921
|
+
}
|
|
922
|
+
]);
|
|
923
|
+
if (!apiKey || apiKey.trim().length === 0) {
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
console.log();
|
|
927
|
+
const spinner = ora("\u6B63\u5728\u9A8C\u8BC1 API Key...").start();
|
|
928
|
+
const verification = await verifyApiKey(DEFAULT_BASE_URL, apiKey.trim());
|
|
929
|
+
if (!verification.valid) {
|
|
930
|
+
spinner.fail("API Key \u9A8C\u8BC1\u5931\u8D25");
|
|
931
|
+
console.log();
|
|
932
|
+
showError(verification.error || "\u672A\u77E5\u9519\u8BEF");
|
|
933
|
+
await waitForEnter();
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
spinner.succeed("API Key \u9A8C\u8BC1\u901A\u8FC7");
|
|
937
|
+
try {
|
|
938
|
+
await saveApiConfig(DEFAULT_BASE_URL, apiKey.trim());
|
|
939
|
+
console.log();
|
|
940
|
+
showSuccess("Claude API \u6388\u6743\u914D\u7F6E\u5DF2\u751F\u6548");
|
|
941
|
+
displaySavedConfig();
|
|
942
|
+
await askToSaveProfile(
|
|
943
|
+
"Claude API",
|
|
944
|
+
DEFAULT_BASE_URL,
|
|
945
|
+
apiKey.trim()
|
|
946
|
+
);
|
|
947
|
+
} catch (error) {
|
|
948
|
+
console.log();
|
|
949
|
+
showError(`\u4FDD\u5B58\u914D\u7F6E\u5931\u8D25: ${error.message}`);
|
|
950
|
+
await waitForEnter();
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
async function handleGlmAuth() {
|
|
954
|
+
console.log();
|
|
955
|
+
console.log(theme.primary("\u667A\u8C31 GLM AI \u6388\u6743"));
|
|
956
|
+
console.log();
|
|
957
|
+
console.log(theme.dim("Base URL: ") + GLM_BASE_URL);
|
|
958
|
+
console.log();
|
|
959
|
+
showInfo("\u83B7\u53D6 API Key \u65B9\u5F0F\uFF1A");
|
|
960
|
+
console.log(
|
|
961
|
+
theme.dim(" 1. \u8BBF\u95EE https://bigmodel.cn/usercenter/proj-mgmt/apikeys")
|
|
962
|
+
);
|
|
963
|
+
console.log(theme.dim(" 2. \u521B\u5EFA\u6216\u590D\u5236 API Key"));
|
|
964
|
+
console.log(theme.dim(" 3. \u7C98\u8D34\u5230\u4E0B\u65B9"));
|
|
965
|
+
console.log();
|
|
966
|
+
console.log(theme.dim(" (\u76F4\u63A5\u6309\u56DE\u8F66\u53EF\u8FD4\u56DE)"));
|
|
967
|
+
console.log();
|
|
968
|
+
const { apiKey } = await inquirer.prompt([
|
|
969
|
+
{
|
|
970
|
+
type: "password",
|
|
971
|
+
name: "apiKey",
|
|
972
|
+
message: "\u8BF7\u8F93\u5165\u667A\u8C31 API Key:",
|
|
973
|
+
mask: "*"
|
|
974
|
+
}
|
|
975
|
+
]);
|
|
976
|
+
if (!apiKey || apiKey.trim().length === 0) {
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
979
|
+
console.log();
|
|
980
|
+
const spinner = ora("\u6B63\u5728\u9A8C\u8BC1 API Key...").start();
|
|
981
|
+
const verification = await verifyApiKey(GLM_BASE_URL, apiKey.trim());
|
|
982
|
+
if (!verification.valid) {
|
|
983
|
+
spinner.fail("API Key \u9A8C\u8BC1\u5931\u8D25");
|
|
984
|
+
console.log();
|
|
985
|
+
showError(verification.error || "\u672A\u77E5\u9519\u8BEF");
|
|
986
|
+
await waitForEnter();
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
spinner.succeed("API Key \u9A8C\u8BC1\u901A\u8FC7");
|
|
990
|
+
try {
|
|
991
|
+
await saveApiConfig(GLM_BASE_URL, apiKey.trim(), {
|
|
992
|
+
timeout: "3000000"
|
|
993
|
+
});
|
|
994
|
+
console.log();
|
|
995
|
+
showSuccess("\u667A\u8C31 GLM AI \u6388\u6743\u914D\u7F6E\u5DF2\u751F\u6548");
|
|
996
|
+
displaySavedConfig();
|
|
997
|
+
await askToSaveProfile(
|
|
998
|
+
"\u667A\u8C31 GLM",
|
|
999
|
+
GLM_BASE_URL,
|
|
1000
|
+
apiKey.trim(),
|
|
1001
|
+
void 0,
|
|
1002
|
+
"3000000"
|
|
1003
|
+
);
|
|
1004
|
+
} catch (error) {
|
|
1005
|
+
console.log();
|
|
1006
|
+
showError(`\u4FDD\u5B58\u914D\u7F6E\u5931\u8D25: ${error.message}`);
|
|
1007
|
+
await waitForEnter();
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
async function handleKimiAuth() {
|
|
1011
|
+
console.log();
|
|
1012
|
+
console.log(theme.primary("Kimi AI \u6388\u6743"));
|
|
1013
|
+
console.log();
|
|
1014
|
+
console.log(theme.dim("Base URL: ") + KIMI_BASE_URL);
|
|
1015
|
+
console.log();
|
|
1016
|
+
showInfo("\u83B7\u53D6 API Key \u65B9\u5F0F\uFF1A");
|
|
1017
|
+
console.log(theme.dim(" 1. \u8BBF\u95EE Kimi AI \u5B98\u7F51\u83B7\u53D6 API Key"));
|
|
1018
|
+
console.log(theme.dim(" 2. \u521B\u5EFA\u6216\u590D\u5236 API Key"));
|
|
1019
|
+
console.log(theme.dim(" 3. \u7C98\u8D34\u5230\u4E0B\u65B9"));
|
|
1020
|
+
console.log();
|
|
1021
|
+
console.log(theme.dim(" (\u76F4\u63A5\u6309\u56DE\u8F66\u53EF\u8FD4\u56DE)"));
|
|
1022
|
+
console.log();
|
|
1023
|
+
const { apiKey } = await inquirer.prompt([
|
|
1024
|
+
{
|
|
1025
|
+
type: "password",
|
|
1026
|
+
name: "apiKey",
|
|
1027
|
+
message: "\u8BF7\u8F93\u5165 Kimi API Key:",
|
|
1028
|
+
mask: "*"
|
|
1029
|
+
}
|
|
1030
|
+
]);
|
|
1031
|
+
if (!apiKey || apiKey.trim().length === 0) {
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
console.log();
|
|
1035
|
+
const spinner = ora("\u6B63\u5728\u9A8C\u8BC1 API Key...").start();
|
|
1036
|
+
const verification = await verifyApiKey(KIMI_BASE_URL, apiKey.trim());
|
|
1037
|
+
if (!verification.valid) {
|
|
1038
|
+
spinner.fail("API Key \u9A8C\u8BC1\u5931\u8D25");
|
|
1039
|
+
console.log();
|
|
1040
|
+
showError(verification.error || "\u672A\u77E5\u9519\u8BEF");
|
|
1041
|
+
await waitForEnter();
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
spinner.succeed("API Key \u9A8C\u8BC1\u901A\u8FC7");
|
|
1045
|
+
try {
|
|
1046
|
+
await saveApiConfig(KIMI_BASE_URL, apiKey.trim(), {
|
|
1047
|
+
timeout: "3000000"
|
|
1048
|
+
});
|
|
1049
|
+
console.log();
|
|
1050
|
+
showSuccess("Kimi AI \u6388\u6743\u914D\u7F6E\u5DF2\u751F\u6548");
|
|
1051
|
+
displaySavedConfig();
|
|
1052
|
+
await askToSaveProfile(
|
|
1053
|
+
"Kimi AI",
|
|
1054
|
+
KIMI_BASE_URL,
|
|
1055
|
+
apiKey.trim(),
|
|
1056
|
+
void 0,
|
|
1057
|
+
"3000000"
|
|
1058
|
+
);
|
|
1059
|
+
} catch (error) {
|
|
1060
|
+
console.log();
|
|
1061
|
+
showError(`\u4FDD\u5B58\u914D\u7F6E\u5931\u8D25: ${error.message}`);
|
|
1062
|
+
await waitForEnter();
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
async function handleManualAuth() {
|
|
1066
|
+
console.log();
|
|
1067
|
+
console.log(theme.primary("\u624B\u52A8\u8F93\u5165\u914D\u7F6E"));
|
|
1068
|
+
console.log();
|
|
1069
|
+
console.log(theme.dim("\u53EA\u9700\u914D\u7F6E\u4EE5\u4E0B\u5FC5\u8981\u5B57\u6BB5\uFF1A"));
|
|
1070
|
+
console.log(theme.dim(" - ANTHROPIC_AUTH_TOKEN"));
|
|
1071
|
+
console.log(theme.dim(" - ANTHROPIC_BASE_URL"));
|
|
1072
|
+
console.log(theme.dim(" - API_TIMEOUT_MS (\u53EF\u9009)"));
|
|
1073
|
+
console.log(
|
|
1074
|
+
theme.dim(" - CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC (\u9ED8\u8BA4\u7981\u7528)")
|
|
1075
|
+
);
|
|
1076
|
+
console.log();
|
|
1077
|
+
console.log(theme.dim("(\u4EFB\u610F\u6B65\u9AA4\u76F4\u63A5\u6309\u56DE\u8F66\u53EF\u8FD4\u56DE)"));
|
|
1078
|
+
console.log();
|
|
1079
|
+
const { baseUrl } = await inquirer.prompt([
|
|
1080
|
+
{
|
|
1081
|
+
type: "input",
|
|
1082
|
+
name: "baseUrl",
|
|
1083
|
+
message: "\u8BF7\u8F93\u5165 ANTHROPIC_BASE_URL:",
|
|
1084
|
+
default: DEFAULT_BASE_URL
|
|
1085
|
+
}
|
|
1086
|
+
]);
|
|
1087
|
+
if (!baseUrl || baseUrl.trim().length === 0) {
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
const { apiKey } = await inquirer.prompt([
|
|
1091
|
+
{
|
|
1092
|
+
type: "password",
|
|
1093
|
+
name: "apiKey",
|
|
1094
|
+
message: "\u8BF7\u8F93\u5165 ANTHROPIC_AUTH_TOKEN:",
|
|
1095
|
+
mask: "*"
|
|
1096
|
+
}
|
|
1097
|
+
]);
|
|
1098
|
+
if (!apiKey || apiKey.trim().length === 0) {
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
const { timeout } = await inquirer.prompt([
|
|
1102
|
+
{
|
|
1103
|
+
type: "input",
|
|
1104
|
+
name: "timeout",
|
|
1105
|
+
message: "\u8BF7\u8F93\u5165 API_TIMEOUT_MS\uFF08\u6BEB\u79D2\uFF0C\u76F4\u63A5\u56DE\u8F66\u8DF3\u8FC7\uFF09:",
|
|
1106
|
+
default: "",
|
|
1107
|
+
validate: (input) => {
|
|
1108
|
+
if (!input || input.trim() === "") return true;
|
|
1109
|
+
const num = parseInt(input.trim(), 10);
|
|
1110
|
+
return !isNaN(num) && num > 0 || "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u6570\u5B57";
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
]);
|
|
1114
|
+
console.log();
|
|
1115
|
+
const spinner = ora("\u6B63\u5728\u9A8C\u8BC1 API Key...").start();
|
|
1116
|
+
const verification = await verifyApiKey(baseUrl.trim(), apiKey.trim());
|
|
1117
|
+
if (!verification.valid) {
|
|
1118
|
+
spinner.fail("API Key \u9A8C\u8BC1\u5931\u8D25");
|
|
1119
|
+
console.log();
|
|
1120
|
+
showError(verification.error || "\u672A\u77E5\u9519\u8BEF");
|
|
1121
|
+
await waitForEnter();
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
spinner.succeed("API Key \u9A8C\u8BC1\u901A\u8FC7");
|
|
1125
|
+
try {
|
|
1126
|
+
const options = {};
|
|
1127
|
+
if (timeout && timeout.trim()) {
|
|
1128
|
+
options.timeout = timeout.trim();
|
|
1129
|
+
}
|
|
1130
|
+
await saveApiConfig(baseUrl.trim(), apiKey.trim(), options);
|
|
1131
|
+
console.log();
|
|
1132
|
+
showSuccess("\u914D\u7F6E\u5DF2\u751F\u6548");
|
|
1133
|
+
displaySavedConfig();
|
|
1134
|
+
await askToSaveProfile(
|
|
1135
|
+
"\u81EA\u5B9A\u4E49\u914D\u7F6E",
|
|
1136
|
+
baseUrl.trim(),
|
|
1137
|
+
apiKey.trim(),
|
|
1138
|
+
void 0,
|
|
1139
|
+
timeout && timeout.trim() ? timeout.trim() : void 0
|
|
1140
|
+
);
|
|
1141
|
+
} catch (error) {
|
|
1142
|
+
console.log();
|
|
1143
|
+
showError(`\u4FDD\u5B58\u914D\u7F6E\u5931\u8D25: ${error.message}`);
|
|
1144
|
+
await waitForEnter();
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
async function askToSaveProfile(defaultName, baseUrl, apiKey, model, timeout) {
|
|
1148
|
+
console.log();
|
|
1149
|
+
const { save } = await inquirer.prompt([
|
|
1150
|
+
{
|
|
1151
|
+
type: "confirm",
|
|
1152
|
+
name: "save",
|
|
1153
|
+
message: "\u662F\u5426\u4FDD\u5B58\u6B64\u914D\u7F6E\u4EE5\u4FBF\u4E0B\u6B21\u5FEB\u901F\u6062\u590D\uFF1F",
|
|
1154
|
+
default: true
|
|
1155
|
+
}
|
|
1156
|
+
]);
|
|
1157
|
+
if (!save) {
|
|
1158
|
+
await waitForEnter();
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
const { name } = await inquirer.prompt([
|
|
1162
|
+
{
|
|
1163
|
+
type: "input",
|
|
1164
|
+
name: "name",
|
|
1165
|
+
message: "\u8BF7\u8F93\u5165\u914D\u7F6E\u540D\u79F0\uFF1A",
|
|
1166
|
+
default: defaultName,
|
|
1167
|
+
validate: (input) => input.trim() !== "" || "\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A"
|
|
1168
|
+
}
|
|
1169
|
+
]);
|
|
1170
|
+
try {
|
|
1171
|
+
const newProfile = addProfile({
|
|
1172
|
+
name: name.trim(),
|
|
1173
|
+
baseUrl,
|
|
1174
|
+
apiKey,
|
|
1175
|
+
model,
|
|
1176
|
+
timeout
|
|
1177
|
+
});
|
|
1178
|
+
switchProfile(newProfile.id);
|
|
1179
|
+
showSuccess(`\u914D\u7F6E "${name.trim()}" \u5DF2\u4FDD\u5B58`);
|
|
1180
|
+
showInfo('\u4E0B\u6B21\u53EF\u4EE5\u76F4\u63A5\u9009\u62E9"\u6062\u590D"\u6765\u5FEB\u901F\u5207\u6362');
|
|
1181
|
+
} catch (error) {
|
|
1182
|
+
showError(`\u4FDD\u5B58\u5931\u8D25: ${error.message}`);
|
|
1183
|
+
}
|
|
1184
|
+
await waitForEnter();
|
|
1185
|
+
}
|
|
1186
|
+
async function handleRestoreProfile(profile) {
|
|
1187
|
+
console.log();
|
|
1188
|
+
try {
|
|
1189
|
+
const success = switchProfile(profile.id);
|
|
1190
|
+
if (success) {
|
|
1191
|
+
showSuccess(`\u5DF2\u5207\u6362\u5230\uFF1A${profile.name}`);
|
|
1192
|
+
showInfo("\u914D\u7F6E\u5DF2\u751F\u6548");
|
|
1193
|
+
} else {
|
|
1194
|
+
showError("\u5207\u6362\u5931\u8D25");
|
|
1195
|
+
}
|
|
1196
|
+
} catch (error) {
|
|
1197
|
+
showError(`\u5207\u6362\u5931\u8D25: ${error.message}`);
|
|
1198
|
+
}
|
|
1199
|
+
await waitForEnter();
|
|
1200
|
+
}
|
|
1201
|
+
async function handleManageProfiles() {
|
|
1202
|
+
while (true) {
|
|
1203
|
+
const profiles = getAllProfiles();
|
|
1204
|
+
const currentProfile = getCurrentProfile();
|
|
1205
|
+
if (profiles.length === 0) {
|
|
1206
|
+
console.log();
|
|
1207
|
+
showWarning("\u6CA1\u6709\u5DF2\u4FDD\u5B58\u7684\u914D\u7F6E");
|
|
1208
|
+
await waitForEnter();
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
showHeader();
|
|
1212
|
+
console.log(createBoxTitle("\u7BA1\u7406\u5DF2\u4FDD\u5B58\u7684\u914D\u7F6E"));
|
|
1213
|
+
console.log();
|
|
1214
|
+
const choices = profiles.map((p) => {
|
|
1215
|
+
const isCurrent = currentProfile?.id === p.id;
|
|
1216
|
+
const suffix = isCurrent ? theme.warning(" (\u5F53\u524D\u4F7F\u7528\u4E2D)") : "";
|
|
1217
|
+
return {
|
|
1218
|
+
name: `${theme.error("\u2715")} \u5220\u9664 ${p.name}${suffix} ${theme.dim(
|
|
1219
|
+
`- ${p.baseUrl}`
|
|
1220
|
+
)}`,
|
|
1221
|
+
value: p
|
|
1222
|
+
};
|
|
1223
|
+
});
|
|
1224
|
+
choices.push(new inquirer.Separator(), {
|
|
1225
|
+
name: `${theme.dim("<-")} \u8FD4\u56DE`,
|
|
1226
|
+
value: null
|
|
1227
|
+
});
|
|
1228
|
+
const { profile } = await inquirer.prompt([
|
|
1229
|
+
{
|
|
1230
|
+
type: "list",
|
|
1231
|
+
name: "profile",
|
|
1232
|
+
message: "\u8BF7\u9009\u62E9\u8981\u5220\u9664\u7684\u914D\u7F6E\uFF1A",
|
|
1233
|
+
pageSize: 10,
|
|
1234
|
+
choices
|
|
1235
|
+
}
|
|
1236
|
+
]);
|
|
1237
|
+
if (!profile) return;
|
|
1238
|
+
const { confirm } = await inquirer.prompt([
|
|
1239
|
+
{
|
|
1240
|
+
type: "confirm",
|
|
1241
|
+
name: "confirm",
|
|
1242
|
+
message: `\u786E\u5B9A\u8981\u5220\u9664\u914D\u7F6E "${profile.name}" \u5417\uFF1F`,
|
|
1243
|
+
default: false
|
|
1244
|
+
}
|
|
1245
|
+
]);
|
|
1246
|
+
if (!confirm) {
|
|
1247
|
+
continue;
|
|
1248
|
+
}
|
|
1249
|
+
try {
|
|
1250
|
+
deleteProfile(profile.id);
|
|
1251
|
+
showSuccess(`\u914D\u7F6E "${profile.name}" \u5DF2\u5220\u9664`);
|
|
1252
|
+
} catch (error) {
|
|
1253
|
+
showError(`\u5220\u9664\u5931\u8D25: ${error.message}`);
|
|
1254
|
+
}
|
|
1255
|
+
await waitForEnter();
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
function maskApiKey(apiKey) {
|
|
1259
|
+
if (apiKey.length <= 8) {
|
|
1260
|
+
return "*".repeat(apiKey.length);
|
|
1261
|
+
}
|
|
1262
|
+
return apiKey.substring(0, 4) + "****" + apiKey.substring(apiKey.length - 4);
|
|
1263
|
+
}
|
|
1264
|
+
function displaySavedConfig() {
|
|
1265
|
+
const settings = readClaudeSettings();
|
|
1266
|
+
console.log();
|
|
1267
|
+
console.log(theme.dim("\u914D\u7F6E\u5DF2\u540C\u6B65\uFF1A"));
|
|
1268
|
+
console.log(JSON.stringify(settings, null, 2));
|
|
1269
|
+
}
|
|
1270
|
+
async function waitForEnter() {
|
|
1271
|
+
console.log();
|
|
1272
|
+
await inquirer.prompt([
|
|
1273
|
+
{ type: "input", name: "continue", message: "\u6309\u56DE\u8F66\u952E\u7EE7\u7EED..." }
|
|
1274
|
+
]);
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
// src/cli/menus/manage.ts
|
|
1278
|
+
import inquirer4 from "inquirer";
|
|
1279
|
+
import ora4 from "ora";
|
|
1280
|
+
|
|
1281
|
+
// src/cli/menus/install.ts
|
|
1282
|
+
import { execSync as execSync2 } from "child_process";
|
|
1283
|
+
import inquirer2 from "inquirer";
|
|
1284
|
+
import net from "net";
|
|
1285
|
+
import ora2 from "ora";
|
|
1286
|
+
init_exec();
|
|
1287
|
+
|
|
1288
|
+
// src/cli/utils/platform.ts
|
|
1289
|
+
import os3 from "os";
|
|
1290
|
+
function getOsName(platform) {
|
|
1291
|
+
switch (platform) {
|
|
1292
|
+
case "darwin":
|
|
1293
|
+
return "macOS";
|
|
1294
|
+
case "win32":
|
|
1295
|
+
return "Windows";
|
|
1296
|
+
case "linux":
|
|
1297
|
+
return "Linux";
|
|
1298
|
+
default:
|
|
1299
|
+
return platform;
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
function getArchName(arch) {
|
|
1303
|
+
switch (arch) {
|
|
1304
|
+
case "x64":
|
|
1305
|
+
return "x64";
|
|
1306
|
+
case "arm64":
|
|
1307
|
+
return "arm64";
|
|
1308
|
+
case "ia32":
|
|
1309
|
+
return "x86";
|
|
1310
|
+
default:
|
|
1311
|
+
return arch;
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
function detectPlatform() {
|
|
1315
|
+
const platform = os3.platform();
|
|
1316
|
+
const arch = os3.arch();
|
|
1317
|
+
return {
|
|
1318
|
+
os: platform,
|
|
1319
|
+
osName: getOsName(platform),
|
|
1320
|
+
arch: getArchName(arch),
|
|
1321
|
+
isWindows: platform === "win32",
|
|
1322
|
+
isMac: platform === "darwin",
|
|
1323
|
+
isLinux: platform === "linux"
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
// src/cli/menus/install.ts
|
|
1328
|
+
var COMMON_PROXY_PORTS = [
|
|
1329
|
+
{ port: 7890, name: "Clash" },
|
|
1330
|
+
{ port: 7891, name: "Clash (SOCKS)" },
|
|
1331
|
+
{ port: 1087, name: "V2RayU" },
|
|
1332
|
+
{ port: 1086, name: "V2RayU (SOCKS)" },
|
|
1333
|
+
{ port: 8118, name: "Privoxy" },
|
|
1334
|
+
{ port: 1080, name: "SOCKS5" },
|
|
1335
|
+
{ port: 8080, name: "HTTP Proxy" },
|
|
1336
|
+
{ port: 9090, name: "Clash Dashboard" }
|
|
1337
|
+
];
|
|
1338
|
+
var INSTALL_METHODS = [
|
|
1339
|
+
{
|
|
1340
|
+
name: "\u5B98\u65B9\u811A\u672C\u5B89\u88C5",
|
|
1341
|
+
command: "curl -fsSL https://claude.ai/install.sh | bash",
|
|
1342
|
+
description: "\u9700\u8981\u4EE3\u7406\u6216\u5916\u7F51\u8BBF\u95EE",
|
|
1343
|
+
platforms: ["darwin", "linux"],
|
|
1344
|
+
recommended: true
|
|
1345
|
+
},
|
|
1346
|
+
{
|
|
1347
|
+
name: "npm \u5B89\u88C5",
|
|
1348
|
+
command: "npm install -g @anthropic-ai/claude-code",
|
|
1349
|
+
description: "\u65E0\u9700\u7FFB\u5899",
|
|
1350
|
+
platforms: ["darwin", "linux", "win32"]
|
|
1351
|
+
},
|
|
1352
|
+
{
|
|
1353
|
+
name: "\u5B98\u65B9\u811A\u672C\u5B89\u88C5 (Windows)",
|
|
1354
|
+
command: "irm https://claude.ai/install.ps1 | iex",
|
|
1355
|
+
description: "\u9700\u8981\u4EE3\u7406\u6216\u5916\u7F51\u8BBF\u95EE",
|
|
1356
|
+
platforms: ["win32"],
|
|
1357
|
+
recommended: true
|
|
1358
|
+
},
|
|
1359
|
+
{
|
|
1360
|
+
name: "WinGet \u5B89\u88C5",
|
|
1361
|
+
command: "winget install Anthropic.ClaudeCode",
|
|
1362
|
+
description: "\u9002\u7528\u4E8E Windows",
|
|
1363
|
+
platforms: ["win32"]
|
|
1364
|
+
}
|
|
1365
|
+
];
|
|
1366
|
+
function getAvailableMethods(platform) {
|
|
1367
|
+
return INSTALL_METHODS.filter(
|
|
1368
|
+
(method) => method.platforms.includes(platform.os)
|
|
1369
|
+
);
|
|
1370
|
+
}
|
|
1371
|
+
function checkPort(port, host = "127.0.0.1") {
|
|
1372
|
+
return new Promise((resolve) => {
|
|
1373
|
+
const socket = new net.Socket();
|
|
1374
|
+
socket.setTimeout(500);
|
|
1375
|
+
socket.on("connect", () => {
|
|
1376
|
+
socket.destroy();
|
|
1377
|
+
resolve(true);
|
|
1378
|
+
});
|
|
1379
|
+
socket.on("timeout", () => {
|
|
1380
|
+
socket.destroy();
|
|
1381
|
+
resolve(false);
|
|
1382
|
+
});
|
|
1383
|
+
socket.on("error", () => {
|
|
1384
|
+
socket.destroy();
|
|
1385
|
+
resolve(false);
|
|
1386
|
+
});
|
|
1387
|
+
socket.connect(port, host);
|
|
1388
|
+
});
|
|
1389
|
+
}
|
|
1390
|
+
function getSystemProxy() {
|
|
1391
|
+
const httpProxy = process.env.http_proxy || process.env.HTTP_PROXY;
|
|
1392
|
+
const httpsProxy = process.env.https_proxy || process.env.HTTPS_PROXY;
|
|
1393
|
+
const proxy = httpsProxy || httpProxy;
|
|
1394
|
+
if (proxy) {
|
|
1395
|
+
try {
|
|
1396
|
+
const url = new URL(proxy);
|
|
1397
|
+
return { host: url.hostname, port: parseInt(url.port) || 80 };
|
|
1398
|
+
} catch {
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
if (process.platform === "darwin") {
|
|
1402
|
+
try {
|
|
1403
|
+
const output = execSync2('networksetup -getwebproxy "Wi-Fi"', {
|
|
1404
|
+
encoding: "utf-8",
|
|
1405
|
+
timeout: 3e3
|
|
1406
|
+
});
|
|
1407
|
+
const enabledMatch = output.match(/Enabled:\s*(Yes|No)/i);
|
|
1408
|
+
const serverMatch = output.match(/Server:\s*(\S+)/);
|
|
1409
|
+
const portMatch = output.match(/Port:\s*(\d+)/);
|
|
1410
|
+
if (enabledMatch?.[1].toLowerCase() === "yes" && serverMatch && portMatch) {
|
|
1411
|
+
return { host: serverMatch[1], port: parseInt(portMatch[1]) };
|
|
1412
|
+
}
|
|
1413
|
+
} catch {
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
return null;
|
|
1417
|
+
}
|
|
1418
|
+
async function detectProxyPorts() {
|
|
1419
|
+
const availablePorts = [];
|
|
1420
|
+
const results = await Promise.all(
|
|
1421
|
+
COMMON_PROXY_PORTS.map(async ({ port, name }) => {
|
|
1422
|
+
const isOpen = await checkPort(port);
|
|
1423
|
+
return { port, name, isOpen };
|
|
1424
|
+
})
|
|
1425
|
+
);
|
|
1426
|
+
for (const result of results) {
|
|
1427
|
+
if (result.isOpen) {
|
|
1428
|
+
availablePorts.push({ port: result.port, name: result.name });
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
return availablePorts;
|
|
1432
|
+
}
|
|
1433
|
+
function isNetworkError(errorMessage) {
|
|
1434
|
+
const networkErrorPatterns = [
|
|
1435
|
+
"syntax error",
|
|
1436
|
+
"<!DOCTYPE",
|
|
1437
|
+
"<html",
|
|
1438
|
+
"Could not resolve host",
|
|
1439
|
+
"Connection refused",
|
|
1440
|
+
"Network is unreachable",
|
|
1441
|
+
"Operation timed out",
|
|
1442
|
+
"SSL",
|
|
1443
|
+
"SSL_connect",
|
|
1444
|
+
"SSL_ERROR",
|
|
1445
|
+
"LibreSSL",
|
|
1446
|
+
"certificate",
|
|
1447
|
+
"ENOTFOUND",
|
|
1448
|
+
"ECONNREFUSED",
|
|
1449
|
+
"ETIMEDOUT",
|
|
1450
|
+
"ECONNRESET"
|
|
1451
|
+
];
|
|
1452
|
+
return networkErrorPatterns.some(
|
|
1453
|
+
(pattern) => errorMessage.toLowerCase().includes(pattern.toLowerCase())
|
|
1454
|
+
);
|
|
1455
|
+
}
|
|
1456
|
+
function formatErrorMessage(error) {
|
|
1457
|
+
const errorMsg = error.message;
|
|
1458
|
+
if (isNetworkError(errorMsg)) {
|
|
1459
|
+
return "\u7F51\u7EDC\u8FDE\u63A5\u5931\u8D25\uFF0C\u8BE5\u5B89\u88C5\u65B9\u5F0F\u9700\u8981\u80FD\u591F\u8BBF\u95EE\u5916\u7F51";
|
|
1460
|
+
}
|
|
1461
|
+
const lines = errorMsg.split("\n").filter((line) => line.trim());
|
|
1462
|
+
if (lines.length > 2) {
|
|
1463
|
+
return lines.slice(0, 2).join("\n") + `
|
|
1464
|
+
${theme.dim(`... \u7701\u7565 ${lines.length - 2} \u884C`)}`;
|
|
1465
|
+
}
|
|
1466
|
+
return errorMsg;
|
|
1467
|
+
}
|
|
1468
|
+
function needsExternalNetwork(command) {
|
|
1469
|
+
return command.includes("claude.ai");
|
|
1470
|
+
}
|
|
1471
|
+
async function showInstallMenu() {
|
|
1472
|
+
showHeader();
|
|
1473
|
+
const platform = detectPlatform();
|
|
1474
|
+
console.log(createBoxTitle("\u5B89\u88C5 Claude Code"));
|
|
1475
|
+
console.log();
|
|
1476
|
+
showInfo(`\u68C0\u6D4B\u5230\u60A8\u7684\u7CFB\u7EDF\uFF1A${platform.osName} ${platform.arch}`);
|
|
1477
|
+
console.log();
|
|
1478
|
+
const availableMethods = getAvailableMethods(platform);
|
|
1479
|
+
const choices = availableMethods.map((method2) => ({
|
|
1480
|
+
name: `${method2.name}${method2.recommended ? theme.success(" (\u63A8\u8350)") : ""} - ${theme.dim(method2.description)}`,
|
|
1481
|
+
value: method2
|
|
1482
|
+
}));
|
|
1483
|
+
choices.push(new inquirer2.Separator());
|
|
1484
|
+
choices.push({
|
|
1485
|
+
name: `${theme.dim("<-")} \u8FD4\u56DE`,
|
|
1486
|
+
value: null
|
|
1487
|
+
});
|
|
1488
|
+
choices.push({
|
|
1489
|
+
name: `${theme.dim("X")} \u9000\u51FA`,
|
|
1490
|
+
value: "exit"
|
|
1491
|
+
});
|
|
1492
|
+
const { method } = await inquirer2.prompt([
|
|
1493
|
+
{
|
|
1494
|
+
type: "list",
|
|
1495
|
+
name: "method",
|
|
1496
|
+
message: "\u8BF7\u9009\u62E9\u5B89\u88C5\u65B9\u5F0F\uFF1A",
|
|
1497
|
+
choices
|
|
1498
|
+
}
|
|
1499
|
+
]);
|
|
1500
|
+
if (!method) {
|
|
1501
|
+
return;
|
|
1502
|
+
}
|
|
1503
|
+
if (method === "exit") {
|
|
1504
|
+
process.exit(0);
|
|
1505
|
+
}
|
|
1506
|
+
let proxyUrl = null;
|
|
1507
|
+
let finalMethod = method;
|
|
1508
|
+
if (needsExternalNetwork(method.command)) {
|
|
1509
|
+
console.log();
|
|
1510
|
+
const spinner2 = ora2("\u68C0\u6D4B\u672C\u673A\u4EE3\u7406...").start();
|
|
1511
|
+
const systemProxy = getSystemProxy();
|
|
1512
|
+
const detectedPorts = await detectProxyPorts();
|
|
1513
|
+
spinner2.stop();
|
|
1514
|
+
const proxyChoices = [];
|
|
1515
|
+
if (systemProxy) {
|
|
1516
|
+
proxyChoices.push({
|
|
1517
|
+
name: `${theme.success("\u2713")} \u7CFB\u7EDF\u4EE3\u7406 ${theme.dim(
|
|
1518
|
+
`(${systemProxy.host}:${systemProxy.port})`
|
|
1519
|
+
)}`,
|
|
1520
|
+
value: `http://${systemProxy.host}:${systemProxy.port}`
|
|
1521
|
+
});
|
|
1522
|
+
}
|
|
1523
|
+
for (const { port, name } of detectedPorts) {
|
|
1524
|
+
if (systemProxy && systemProxy.port === port) continue;
|
|
1525
|
+
proxyChoices.push({
|
|
1526
|
+
name: `${theme.success("\u2713")} ${name} ${theme.dim(
|
|
1527
|
+
`(127.0.0.1:${port})`
|
|
1528
|
+
)}`,
|
|
1529
|
+
value: `http://127.0.0.1:${port}`
|
|
1530
|
+
});
|
|
1531
|
+
}
|
|
1532
|
+
const hasDetectedProxy = proxyChoices.length > 0;
|
|
1533
|
+
if (hasDetectedProxy) {
|
|
1534
|
+
showInfo("\u68C0\u6D4B\u5230\u672C\u673A\u4EE3\u7406\uFF1A");
|
|
1535
|
+
for (const choice of proxyChoices) {
|
|
1536
|
+
console.log(` ${choice.name}`);
|
|
1537
|
+
}
|
|
1538
|
+
console.log();
|
|
1539
|
+
const { useProxy } = await inquirer2.prompt([
|
|
1540
|
+
{
|
|
1541
|
+
type: "confirm",
|
|
1542
|
+
name: "useProxy",
|
|
1543
|
+
message: "\u662F\u5426\u4F7F\u7528\u4EE3\u7406\uFF1F\uFF08\u5EFA\u8BAE\u5F00\u542F\uFF09",
|
|
1544
|
+
default: true
|
|
1545
|
+
}
|
|
1546
|
+
]);
|
|
1547
|
+
if (useProxy) {
|
|
1548
|
+
if (proxyChoices.length === 1) {
|
|
1549
|
+
proxyUrl = proxyChoices[0].value;
|
|
1550
|
+
} else {
|
|
1551
|
+
proxyChoices.push(new inquirer2.Separator());
|
|
1552
|
+
proxyChoices.push({
|
|
1553
|
+
name: `${theme.dim("\u2715")} \u4E0D\u4F7F\u7528\u4EE3\u7406`,
|
|
1554
|
+
value: null
|
|
1555
|
+
});
|
|
1556
|
+
const { selectedProxy } = await inquirer2.prompt([
|
|
1557
|
+
{
|
|
1558
|
+
type: "list",
|
|
1559
|
+
name: "selectedProxy",
|
|
1560
|
+
message: "\u8BF7\u9009\u62E9\u4EE3\u7406\uFF1A",
|
|
1561
|
+
choices: proxyChoices
|
|
1562
|
+
}
|
|
1563
|
+
]);
|
|
1564
|
+
if (selectedProxy) {
|
|
1565
|
+
proxyUrl = selectedProxy;
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
} else {
|
|
1570
|
+
showWarning("\u672A\u68C0\u6D4B\u5230\u672C\u673A\u4EE3\u7406");
|
|
1571
|
+
showInfo("\u81EA\u52A8\u5207\u6362\u5230 npm \u5B89\u88C5\u65B9\u5F0F\uFF08\u65E0\u9700\u7FFB\u5899\uFF09");
|
|
1572
|
+
console.log();
|
|
1573
|
+
const npmMethod = availableMethods.find(
|
|
1574
|
+
(m) => m.command.includes("npm install")
|
|
1575
|
+
);
|
|
1576
|
+
if (npmMethod) {
|
|
1577
|
+
finalMethod = npmMethod;
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
let finalCommand = finalMethod.command;
|
|
1582
|
+
let envVars = {};
|
|
1583
|
+
if (proxyUrl) {
|
|
1584
|
+
if (finalCommand.includes("curl")) {
|
|
1585
|
+
finalCommand = finalCommand.replace("curl ", `curl --proxy ${proxyUrl} `);
|
|
1586
|
+
} else {
|
|
1587
|
+
envVars = {
|
|
1588
|
+
http_proxy: proxyUrl,
|
|
1589
|
+
https_proxy: proxyUrl,
|
|
1590
|
+
HTTP_PROXY: proxyUrl,
|
|
1591
|
+
HTTPS_PROXY: proxyUrl
|
|
1592
|
+
};
|
|
1593
|
+
}
|
|
1594
|
+
showInfo(`\u4F7F\u7528\u4EE3\u7406: ${proxyUrl}`);
|
|
1595
|
+
}
|
|
1596
|
+
console.log();
|
|
1597
|
+
showInfo(`\u5C06\u6267\u884C\u547D\u4EE4: ${theme.secondary(finalCommand)}`);
|
|
1598
|
+
console.log();
|
|
1599
|
+
const { confirm } = await inquirer2.prompt([
|
|
1600
|
+
{
|
|
1601
|
+
type: "confirm",
|
|
1602
|
+
name: "confirm",
|
|
1603
|
+
message: "\u786E\u8BA4\u6267\u884C\u5B89\u88C5\uFF1F",
|
|
1604
|
+
default: true
|
|
1605
|
+
}
|
|
1606
|
+
]);
|
|
1607
|
+
if (!confirm) {
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1610
|
+
const spinner = ora2("\u6B63\u5728\u5B89\u88C5 Claude Code...").start();
|
|
1611
|
+
try {
|
|
1612
|
+
await executeCommand(finalCommand, envVars);
|
|
1613
|
+
spinner.text = "\u9A8C\u8BC1\u5B89\u88C5...";
|
|
1614
|
+
const installed = await commandExists("claude");
|
|
1615
|
+
if (installed) {
|
|
1616
|
+
spinner.succeed("Claude Code \u5B89\u88C5\u6210\u529F\uFF01");
|
|
1617
|
+
showSuccess("\u60A8\u73B0\u5728\u53EF\u4EE5\u5728\u7EC8\u7AEF\u4E2D\u8FD0\u884C claude \u547D\u4EE4");
|
|
1618
|
+
console.log();
|
|
1619
|
+
showInfo("\u4E0B\u4E00\u6B65\uFF1A\u914D\u7F6E API \u5BC6\u94A5");
|
|
1620
|
+
console.log(
|
|
1621
|
+
theme.dim(' \u8FD4\u56DE\u4E3B\u83DC\u5355 -> \u9009\u62E9 "\u6388\u6743\u767B\u5F55" -> \u914D\u7F6E\u60A8\u7684 API Key')
|
|
1622
|
+
);
|
|
1623
|
+
console.log(theme.dim(" \u914D\u7F6E\u5B8C\u6210\u540E\u5373\u53EF\u5F00\u59CB\u4F7F\u7528 Claude Code"));
|
|
1624
|
+
} else {
|
|
1625
|
+
spinner.fail("\u5B89\u88C5\u5931\u8D25");
|
|
1626
|
+
showError("\u5B89\u88C5\u547D\u4EE4\u6267\u884C\u5B8C\u6210\uFF0C\u4F46\u672A\u627E\u5230 claude \u547D\u4EE4");
|
|
1627
|
+
console.log();
|
|
1628
|
+
showInfo("\u53EF\u80FD\u7684\u539F\u56E0\uFF1A");
|
|
1629
|
+
console.log(theme.dim(" 1. \u5B89\u88C5\u8FC7\u7A0B\u4E2D\u51FA\u73B0\u7F51\u7EDC\u9519\u8BEF"));
|
|
1630
|
+
console.log(theme.dim(" 2. \u9700\u8981\u91CD\u65B0\u6253\u5F00\u7EC8\u7AEF\u624D\u80FD\u4F7F\u7528 claude \u547D\u4EE4"));
|
|
1631
|
+
console.log(theme.dim(" 3. \u73AF\u5883\u53D8\u91CF PATH \u672A\u6B63\u786E\u8BBE\u7F6E"));
|
|
1632
|
+
console.log();
|
|
1633
|
+
showInfo("\u5EFA\u8BAE\uFF1A");
|
|
1634
|
+
if (proxyUrl) {
|
|
1635
|
+
console.log(
|
|
1636
|
+
theme.dim(
|
|
1637
|
+
" 1. \u68C0\u67E5\u4EE3\u7406\u8F6F\u4EF6\u662F\u5426\u8BBE\u7F6E\u4E3A\u3010\u5168\u5C40\u6A21\u5F0F\u3011\uFF08\u89C4\u5219\u6A21\u5F0F\u53EF\u80FD\u62E6\u622A claude.ai\uFF09"
|
|
1638
|
+
)
|
|
1639
|
+
);
|
|
1640
|
+
console.log(theme.dim(' 2. \u6362\u7528 "npm \u5B89\u88C5" \u65B9\u5F0F\uFF08\u65E0\u9700\u7FFB\u5899\uFF09'));
|
|
1641
|
+
} else {
|
|
1642
|
+
console.log(
|
|
1643
|
+
theme.dim(' 1. \u4F7F\u7528\u4EE3\u7406\u91CD\u8BD5\uFF08\u9009\u62E9"\u5B98\u65B9\u811A\u672C\u5B89\u88C5"\u540E\u5F00\u542F\u4EE3\u7406\u9009\u9879\uFF09')
|
|
1644
|
+
);
|
|
1645
|
+
console.log(theme.dim(' 2. \u6362\u7528 "npm \u5B89\u88C5" \u65B9\u5F0F\uFF08\u65E0\u9700\u7FFB\u5899\uFF09'));
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
} catch (error) {
|
|
1649
|
+
spinner.fail("\u5B89\u88C5\u5931\u8D25");
|
|
1650
|
+
const friendlyError = formatErrorMessage(error);
|
|
1651
|
+
showError(friendlyError);
|
|
1652
|
+
if (isNetworkError(error.message)) {
|
|
1653
|
+
console.log();
|
|
1654
|
+
showInfo("\u5EFA\u8BAE\uFF1A");
|
|
1655
|
+
if (proxyUrl) {
|
|
1656
|
+
console.log(
|
|
1657
|
+
theme.dim(
|
|
1658
|
+
" 1. \u68C0\u67E5\u4EE3\u7406\u8F6F\u4EF6\u662F\u5426\u8BBE\u7F6E\u4E3A\u3010\u5168\u5C40\u6A21\u5F0F\u3011\uFF08\u89C4\u5219\u6A21\u5F0F\u53EF\u80FD\u62E6\u622A claude.ai\uFF09"
|
|
1659
|
+
)
|
|
1660
|
+
);
|
|
1661
|
+
console.log(theme.dim(" 2. \u5C1D\u8BD5\u66F4\u6362\u4EE3\u7406\u7AEF\u53E3\u6216\u4EE3\u7406\u8F6F\u4EF6"));
|
|
1662
|
+
console.log(theme.dim(' 3. \u6362\u7528 "npm \u5B89\u88C5" \u65B9\u5F0F\uFF08\u65E0\u9700\u7FFB\u5899\uFF09'));
|
|
1663
|
+
} else {
|
|
1664
|
+
console.log(
|
|
1665
|
+
theme.dim(' 1. \u4F7F\u7528\u4EE3\u7406\u91CD\u8BD5\uFF08\u9009\u62E9"\u5B98\u65B9\u811A\u672C\u5B89\u88C5"\u540E\u5F00\u542F\u4EE3\u7406\u9009\u9879\uFF09')
|
|
1666
|
+
);
|
|
1667
|
+
console.log(theme.dim(' 2. \u6362\u7528 "npm \u5B89\u88C5" \u65B9\u5F0F\uFF08\u65E0\u9700\u7FFB\u5899\uFF09'));
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
await inquirer2.prompt([
|
|
1672
|
+
{
|
|
1673
|
+
type: "input",
|
|
1674
|
+
name: "continue",
|
|
1675
|
+
message: "\u6309\u56DE\u8F66\u952E\u8FD4\u56DE\u4E3B\u83DC\u5355..."
|
|
1676
|
+
}
|
|
1677
|
+
]);
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
// src/cli/menus/uninstall.ts
|
|
1681
|
+
import fs3 from "fs";
|
|
1682
|
+
import inquirer3 from "inquirer";
|
|
1683
|
+
import ora3 from "ora";
|
|
1684
|
+
import os4 from "os";
|
|
1685
|
+
import path3 from "path";
|
|
1686
|
+
init_exec();
|
|
1687
|
+
async function showUninstallMenu() {
|
|
1688
|
+
showHeader();
|
|
1689
|
+
console.log(createBoxTitle("\u5378\u8F7D Claude Code"));
|
|
1690
|
+
console.log();
|
|
1691
|
+
const { choice } = await inquirer3.prompt([
|
|
1692
|
+
{
|
|
1693
|
+
type: "list",
|
|
1694
|
+
name: "choice",
|
|
1695
|
+
message: "\u8BF7\u9009\u62E9\u5378\u8F7D\u65B9\u5F0F\uFF1A",
|
|
1696
|
+
choices: [
|
|
1697
|
+
{
|
|
1698
|
+
name: `\u4EC5\u5378\u8F7D\u7A0B\u5E8F ${theme.dim("(\u4FDD\u7559\u914D\u7F6E\u3001\u4F1A\u8BDD\u548C\u63D2\u4EF6)")}`,
|
|
1699
|
+
value: "program_only" /* PROGRAM_ONLY */
|
|
1700
|
+
},
|
|
1701
|
+
{
|
|
1702
|
+
name: `${theme.error("\u5B8C\u6574\u5378\u8F7D")} ${theme.dim(
|
|
1703
|
+
"(\u5220\u9664\u7A0B\u5E8F\u3001\u914D\u7F6E\u3001\u4F1A\u8BDD\u548C\u63D2\u4EF6)"
|
|
1704
|
+
)}`,
|
|
1705
|
+
value: "complete" /* COMPLETE */
|
|
1706
|
+
},
|
|
1707
|
+
new inquirer3.Separator(),
|
|
1708
|
+
{
|
|
1709
|
+
name: `${theme.dim("<-")} \u8FD4\u56DE`,
|
|
1710
|
+
value: "back" /* BACK */
|
|
1711
|
+
},
|
|
1712
|
+
{
|
|
1713
|
+
name: `${theme.dim("X")} \u9000\u51FA`,
|
|
1714
|
+
value: "exit" /* EXIT */
|
|
1715
|
+
}
|
|
1716
|
+
]
|
|
1717
|
+
}
|
|
1718
|
+
]);
|
|
1719
|
+
switch (choice) {
|
|
1720
|
+
case "program_only" /* PROGRAM_ONLY */:
|
|
1721
|
+
await handleProgramOnlyUninstall();
|
|
1722
|
+
break;
|
|
1723
|
+
case "complete" /* COMPLETE */:
|
|
1724
|
+
await handleCompleteUninstall();
|
|
1725
|
+
break;
|
|
1726
|
+
case "back" /* BACK */:
|
|
1727
|
+
return;
|
|
1728
|
+
case "exit" /* EXIT */:
|
|
1729
|
+
process.exit(0);
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
async function handleProgramOnlyUninstall() {
|
|
1733
|
+
console.log();
|
|
1734
|
+
showInfo("\u5C06\u4EC5\u5378\u8F7D Claude Code \u7A0B\u5E8F\uFF0C\u4FDD\u7559\u4EE5\u4E0B\u5185\u5BB9\uFF1A");
|
|
1735
|
+
console.log(theme.dim(" - ~/.claude/ \u76EE\u5F55\uFF08\u914D\u7F6E\u548C\u4F1A\u8BDD\uFF09"));
|
|
1736
|
+
console.log(theme.dim(" - ~/.claude.json\uFF08MCP \u914D\u7F6E\uFF09"));
|
|
1737
|
+
console.log(theme.dim(" - \u5DF2\u5B89\u88C5\u7684\u63D2\u4EF6"));
|
|
1738
|
+
console.log();
|
|
1739
|
+
const { confirm } = await inquirer3.prompt([
|
|
1740
|
+
{
|
|
1741
|
+
type: "confirm",
|
|
1742
|
+
name: "confirm",
|
|
1743
|
+
message: "\u786E\u8BA4\u5378\u8F7D Claude Code \u7A0B\u5E8F\uFF1F",
|
|
1744
|
+
default: false
|
|
1745
|
+
}
|
|
1746
|
+
]);
|
|
1747
|
+
if (!confirm) {
|
|
1748
|
+
return;
|
|
1749
|
+
}
|
|
1750
|
+
const spinner = ora3("\u6B63\u5728\u5378\u8F7D Claude Code...").start();
|
|
1751
|
+
try {
|
|
1752
|
+
const platform = detectPlatform();
|
|
1753
|
+
let uninstalled = false;
|
|
1754
|
+
try {
|
|
1755
|
+
await executeCommand("npm uninstall -g @anthropic-ai/claude-code");
|
|
1756
|
+
uninstalled = true;
|
|
1757
|
+
} catch {
|
|
1758
|
+
}
|
|
1759
|
+
if (platform.isMac && !uninstalled) {
|
|
1760
|
+
try {
|
|
1761
|
+
await executeCommand("brew uninstall --cask claude 2>/dev/null");
|
|
1762
|
+
uninstalled = true;
|
|
1763
|
+
} catch {
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
const binaryPaths = [
|
|
1767
|
+
"/usr/local/bin/claude",
|
|
1768
|
+
path3.join(os4.homedir(), ".local/bin/claude"),
|
|
1769
|
+
path3.join(os4.homedir(), ".claude/bin/claude")
|
|
1770
|
+
];
|
|
1771
|
+
for (const binPath of binaryPaths) {
|
|
1772
|
+
if (fs3.existsSync(binPath)) {
|
|
1773
|
+
try {
|
|
1774
|
+
fs3.unlinkSync(binPath);
|
|
1775
|
+
uninstalled = true;
|
|
1776
|
+
} catch {
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
if (uninstalled) {
|
|
1781
|
+
spinner.succeed("Claude Code \u7A0B\u5E8F\u5DF2\u5378\u8F7D");
|
|
1782
|
+
showSuccess("\u914D\u7F6E\u548C\u4F1A\u8BDD\u5DF2\u4FDD\u7559\uFF0C\u91CD\u65B0\u5B89\u88C5\u540E\u53EF\u7EE7\u7EED\u4F7F\u7528");
|
|
1783
|
+
} else {
|
|
1784
|
+
spinner.warn("\u672A\u627E\u5230\u5DF2\u5B89\u88C5\u7684 Claude Code");
|
|
1785
|
+
showInfo("\u53EF\u80FD\u5DF2\u7ECF\u5378\u8F7D\u6216\u4F7F\u7528\u4E86\u5176\u4ED6\u5B89\u88C5\u65B9\u5F0F");
|
|
1786
|
+
}
|
|
1787
|
+
} catch (error) {
|
|
1788
|
+
spinner.fail("\u5378\u8F7D\u5931\u8D25");
|
|
1789
|
+
showError(error.message);
|
|
1790
|
+
}
|
|
1791
|
+
console.log();
|
|
1792
|
+
await inquirer3.prompt([
|
|
1793
|
+
{
|
|
1794
|
+
type: "input",
|
|
1795
|
+
name: "continue",
|
|
1796
|
+
message: "\u6309\u56DE\u8F66\u952E\u8FD4\u56DE..."
|
|
1797
|
+
}
|
|
1798
|
+
]);
|
|
1799
|
+
}
|
|
1800
|
+
async function handleCompleteUninstall() {
|
|
1801
|
+
console.log();
|
|
1802
|
+
showWarning("\u26A0\uFE0F \u5B8C\u6574\u5378\u8F7D\u5C06\u5220\u9664\u4EE5\u4E0B\u6240\u6709\u5185\u5BB9\uFF1A");
|
|
1803
|
+
console.log(theme.error(" - Claude Code \u7A0B\u5E8F"));
|
|
1804
|
+
console.log(theme.error(" - ~/.claude/ \u76EE\u5F55\uFF08\u914D\u7F6E\u548C\u4F1A\u8BDD\uFF09"));
|
|
1805
|
+
console.log(theme.error(" - ~/.claude.json\uFF08MCP \u914D\u7F6E\uFF09"));
|
|
1806
|
+
console.log(theme.error(" - \u5DF2\u5B89\u88C5\u7684\u63D2\u4EF6"));
|
|
1807
|
+
console.log();
|
|
1808
|
+
showWarning("\u6B64\u64CD\u4F5C\u4E0D\u53EF\u6062\u590D\uFF01");
|
|
1809
|
+
console.log();
|
|
1810
|
+
const { confirmFirst } = await inquirer3.prompt([
|
|
1811
|
+
{
|
|
1812
|
+
type: "confirm",
|
|
1813
|
+
name: "confirmFirst",
|
|
1814
|
+
message: "\u786E\u8BA4\u8981\u5B8C\u6574\u5378\u8F7D Claude Code\uFF1F",
|
|
1815
|
+
default: false
|
|
1816
|
+
}
|
|
1817
|
+
]);
|
|
1818
|
+
if (!confirmFirst) {
|
|
1819
|
+
return;
|
|
1820
|
+
}
|
|
1821
|
+
const { confirmSecond } = await inquirer3.prompt([
|
|
1822
|
+
{
|
|
1823
|
+
type: "input",
|
|
1824
|
+
name: "confirmSecond",
|
|
1825
|
+
message: '\u8BF7\u8F93\u5165 "DELETE" \u786E\u8BA4\u5220\u9664\uFF1A'
|
|
1826
|
+
}
|
|
1827
|
+
]);
|
|
1828
|
+
if (confirmSecond !== "DELETE") {
|
|
1829
|
+
showInfo("\u5DF2\u53D6\u6D88\u5378\u8F7D");
|
|
1830
|
+
return;
|
|
1831
|
+
}
|
|
1832
|
+
const spinner = ora3("\u6B63\u5728\u5B8C\u6574\u5378\u8F7D Claude Code...").start();
|
|
1833
|
+
try {
|
|
1834
|
+
const platform = detectPlatform();
|
|
1835
|
+
spinner.text = "\u5378\u8F7D\u7A0B\u5E8F...";
|
|
1836
|
+
try {
|
|
1837
|
+
await executeCommand(
|
|
1838
|
+
"npm uninstall -g @anthropic-ai/claude-code 2>/dev/null || true"
|
|
1839
|
+
);
|
|
1840
|
+
} catch {
|
|
1841
|
+
}
|
|
1842
|
+
if (platform.isMac) {
|
|
1843
|
+
try {
|
|
1844
|
+
await executeCommand(
|
|
1845
|
+
"brew uninstall --cask claude 2>/dev/null || true"
|
|
1846
|
+
);
|
|
1847
|
+
} catch {
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
const binaryPaths = [
|
|
1851
|
+
"/usr/local/bin/claude",
|
|
1852
|
+
path3.join(os4.homedir(), ".local/bin/claude"),
|
|
1853
|
+
path3.join(os4.homedir(), ".claude/bin/claude")
|
|
1854
|
+
];
|
|
1855
|
+
for (const binPath of binaryPaths) {
|
|
1856
|
+
if (fs3.existsSync(binPath)) {
|
|
1857
|
+
try {
|
|
1858
|
+
fs3.unlinkSync(binPath);
|
|
1859
|
+
} catch {
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
spinner.text = "\u5220\u9664\u914D\u7F6E\u548C\u4F1A\u8BDD...";
|
|
1864
|
+
const claudeDir = path3.join(os4.homedir(), ".claude");
|
|
1865
|
+
if (fs3.existsSync(claudeDir)) {
|
|
1866
|
+
fs3.rmSync(claudeDir, { recursive: true, force: true });
|
|
1867
|
+
}
|
|
1868
|
+
spinner.text = "\u5220\u9664 MCP \u914D\u7F6E...";
|
|
1869
|
+
const claudeJson = path3.join(os4.homedir(), ".claude.json");
|
|
1870
|
+
if (fs3.existsSync(claudeJson)) {
|
|
1871
|
+
fs3.unlinkSync(claudeJson);
|
|
1872
|
+
}
|
|
1873
|
+
spinner.succeed("Claude Code \u5DF2\u5B8C\u6574\u5378\u8F7D");
|
|
1874
|
+
showSuccess("\u6240\u6709\u7A0B\u5E8F\u3001\u914D\u7F6E\u548C\u6570\u636E\u5DF2\u5220\u9664");
|
|
1875
|
+
} catch (error) {
|
|
1876
|
+
spinner.fail("\u5378\u8F7D\u8FC7\u7A0B\u4E2D\u51FA\u73B0\u9519\u8BEF");
|
|
1877
|
+
showError(error.message);
|
|
1878
|
+
}
|
|
1879
|
+
console.log();
|
|
1880
|
+
await inquirer3.prompt([
|
|
1881
|
+
{
|
|
1882
|
+
type: "input",
|
|
1883
|
+
name: "continue",
|
|
1884
|
+
message: "\u6309\u56DE\u8F66\u952E\u8FD4\u56DE..."
|
|
1885
|
+
}
|
|
1886
|
+
]);
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
// src/cli/menus/manage.ts
|
|
1890
|
+
async function showManageMenu() {
|
|
1891
|
+
let claudeInfo = getCachedClaudeInfo();
|
|
1892
|
+
if (!claudeInfo) {
|
|
1893
|
+
const spinner = ora4("\u6B63\u5728\u68C0\u6D4B Claude Code...").start();
|
|
1894
|
+
claudeInfo = detectClaudeInstallation();
|
|
1895
|
+
spinner.stop();
|
|
1896
|
+
}
|
|
1897
|
+
while (true) {
|
|
1898
|
+
showHeader();
|
|
1899
|
+
console.log(createBoxTitle("Claude Code \u7BA1\u7406"));
|
|
1900
|
+
console.log();
|
|
1901
|
+
console.log(theme.primary("\u5F53\u524D\u72B6\u6001\uFF1A"));
|
|
1902
|
+
console.log();
|
|
1903
|
+
if (claudeInfo.installed) {
|
|
1904
|
+
showInfo(`\u2713 \u5DF2\u5B89\u88C5`);
|
|
1905
|
+
if (claudeInfo.version) {
|
|
1906
|
+
console.log(` \u7248\u672C\uFF1A${theme.success(claudeInfo.version)}`);
|
|
1907
|
+
}
|
|
1908
|
+
if (claudeInfo.installMethod) {
|
|
1909
|
+
console.log(
|
|
1910
|
+
` \u5B89\u88C5\u65B9\u5F0F\uFF1A${theme.info(
|
|
1911
|
+
formatInstallMethod(claudeInfo.installMethod)
|
|
1912
|
+
)}`
|
|
1913
|
+
);
|
|
1914
|
+
}
|
|
1915
|
+
if (claudeInfo.binaryPath) {
|
|
1916
|
+
console.log(` \u8DEF\u5F84\uFF1A${theme.dim(claudeInfo.binaryPath)}`);
|
|
1917
|
+
}
|
|
1918
|
+
} else {
|
|
1919
|
+
showInfo(`\u2717 \u672A\u5B89\u88C5`);
|
|
1920
|
+
}
|
|
1921
|
+
console.log();
|
|
1922
|
+
const { choice } = await inquirer4.prompt([
|
|
1923
|
+
{
|
|
1924
|
+
type: "list",
|
|
1925
|
+
name: "choice",
|
|
1926
|
+
message: "\u8BF7\u9009\u62E9\u64CD\u4F5C\uFF1A",
|
|
1927
|
+
choices: [
|
|
1928
|
+
{
|
|
1929
|
+
name: `${theme.primary(">")} \u5B89\u88C5 Claude Code`,
|
|
1930
|
+
value: "install" /* INSTALL */
|
|
1931
|
+
},
|
|
1932
|
+
{
|
|
1933
|
+
name: `${theme.error("*")} \u5378\u8F7D Claude Code`,
|
|
1934
|
+
value: "uninstall" /* UNINSTALL */,
|
|
1935
|
+
disabled: !claudeInfo.installed ? "\u672A\u5B89\u88C5" : false
|
|
1936
|
+
},
|
|
1937
|
+
{
|
|
1938
|
+
name: `${theme.info("\u21BB")} \u5237\u65B0\u72B6\u6001`,
|
|
1939
|
+
value: "refresh" /* REFRESH */
|
|
1940
|
+
},
|
|
1941
|
+
new inquirer4.Separator(),
|
|
1942
|
+
{
|
|
1943
|
+
name: `${theme.dim("<-")} \u8FD4\u56DE`,
|
|
1944
|
+
value: "back" /* BACK */
|
|
1945
|
+
},
|
|
1946
|
+
{
|
|
1947
|
+
name: `${theme.dim("X")} \u9000\u51FA`,
|
|
1948
|
+
value: "exit" /* EXIT */
|
|
1949
|
+
}
|
|
1950
|
+
]
|
|
1951
|
+
}
|
|
1952
|
+
]);
|
|
1953
|
+
switch (choice) {
|
|
1954
|
+
case "install" /* INSTALL */:
|
|
1955
|
+
await showInstallMenu();
|
|
1956
|
+
clearClaudeCache();
|
|
1957
|
+
claudeInfo = detectClaudeInstallation();
|
|
1958
|
+
break;
|
|
1959
|
+
case "uninstall" /* UNINSTALL */:
|
|
1960
|
+
await showUninstallMenu();
|
|
1961
|
+
clearClaudeCache();
|
|
1962
|
+
claudeInfo = detectClaudeInstallation();
|
|
1963
|
+
break;
|
|
1964
|
+
case "refresh" /* REFRESH */: {
|
|
1965
|
+
const spinner = ora4("\u6B63\u5728\u5237\u65B0\u72B6\u6001...").start();
|
|
1966
|
+
clearClaudeCache();
|
|
1967
|
+
claudeInfo = detectClaudeInstallation();
|
|
1968
|
+
spinner.succeed("\u72B6\u6001\u5DF2\u5237\u65B0");
|
|
1969
|
+
await new Promise((resolve) => {
|
|
1970
|
+
setTimeout(resolve, 500);
|
|
1971
|
+
});
|
|
1972
|
+
break;
|
|
1973
|
+
}
|
|
1974
|
+
case "back" /* BACK */:
|
|
1975
|
+
return;
|
|
1976
|
+
case "exit" /* EXIT */:
|
|
1977
|
+
process.exit(0);
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
// src/cli/menus/mcp.ts
|
|
1983
|
+
import inquirer5 from "inquirer";
|
|
1984
|
+
import ora5 from "ora";
|
|
1985
|
+
|
|
1986
|
+
// src/cli/services/mcp.ts
|
|
1987
|
+
import fs5 from "fs";
|
|
1988
|
+
import os6 from "os";
|
|
1989
|
+
import path6 from "path";
|
|
1990
|
+
|
|
1991
|
+
// src/cli/config/constants.ts
|
|
1992
|
+
import path4 from "path";
|
|
1993
|
+
var CONFIG_DIR = path4.resolve(__dirname, "../../../config");
|
|
1994
|
+
var MARKETS_CONFIG_FILE = path4.join(CONFIG_DIR, "markets.json");
|
|
1995
|
+
var MCP_SERVERS_CONFIG_FILE = path4.join(CONFIG_DIR, "mcpServers.json");
|
|
1996
|
+
var CACHE_TTL = 6 * 60 * 60 * 1e3;
|
|
1997
|
+
var BUILTIN_DEFAULT_MARKETS = [
|
|
1998
|
+
{
|
|
1999
|
+
name: "macopowers",
|
|
2000
|
+
source: "https://github.com/macopowers-dev/macopowers.git",
|
|
2001
|
+
description: "MacoPowers Official Plugin Marketplace",
|
|
2002
|
+
category: "official",
|
|
2003
|
+
recommended: true,
|
|
2004
|
+
default: true
|
|
2005
|
+
}
|
|
2006
|
+
];
|
|
2007
|
+
var BUILTIN_DEFAULT_MCPS = [
|
|
2008
|
+
{
|
|
2009
|
+
id: "context7",
|
|
2010
|
+
name: "Context7",
|
|
2011
|
+
description: "Get real-time documentation and code examples for popular libraries like React, Next.js, Node.js",
|
|
2012
|
+
usage: 'Query the latest API docs, usage examples and best practices for any library in Claude. Example: "How to use Next.js 14 Server Actions?", "React useEffect latest usage".',
|
|
2013
|
+
transport: "http",
|
|
2014
|
+
url: "https://mcp.context7.com/mcp",
|
|
2015
|
+
headers: {
|
|
2016
|
+
CONTEXT7_API_KEY: "${CONTEXT7_API_KEY}"
|
|
2017
|
+
},
|
|
2018
|
+
requiredEnv: ["CONTEXT7_API_KEY"],
|
|
2019
|
+
apiKeyUrl: "https://context7.com/dashboard",
|
|
2020
|
+
category: "builtin",
|
|
2021
|
+
recommended: true,
|
|
2022
|
+
source: "local"
|
|
2023
|
+
}
|
|
2024
|
+
];
|
|
2025
|
+
|
|
2026
|
+
// src/cli/utils/cache.ts
|
|
2027
|
+
import fs4 from "fs";
|
|
2028
|
+
import os5 from "os";
|
|
2029
|
+
import path5 from "path";
|
|
2030
|
+
var CacheManager = class {
|
|
2031
|
+
cacheDir;
|
|
2032
|
+
cacheFile;
|
|
2033
|
+
constructor(cacheName) {
|
|
2034
|
+
this.cacheDir = path5.join(os5.homedir(), ".code-helper", "cache");
|
|
2035
|
+
this.cacheFile = path5.join(this.cacheDir, `${cacheName}.json`);
|
|
2036
|
+
this.ensureCacheDir();
|
|
2037
|
+
}
|
|
2038
|
+
/**
|
|
2039
|
+
* 加载缓存
|
|
2040
|
+
*/
|
|
2041
|
+
load() {
|
|
2042
|
+
try {
|
|
2043
|
+
if (!fs4.existsSync(this.cacheFile)) {
|
|
2044
|
+
return null;
|
|
2045
|
+
}
|
|
2046
|
+
const content = fs4.readFileSync(this.cacheFile, "utf-8");
|
|
2047
|
+
return JSON.parse(content);
|
|
2048
|
+
} catch (error) {
|
|
2049
|
+
return null;
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
/**
|
|
2053
|
+
* 保存缓存
|
|
2054
|
+
*/
|
|
2055
|
+
save(data) {
|
|
2056
|
+
const cacheData = {
|
|
2057
|
+
data,
|
|
2058
|
+
timestamp: Date.now()
|
|
2059
|
+
};
|
|
2060
|
+
fs4.writeFileSync(this.cacheFile, JSON.stringify(cacheData, null, 2));
|
|
2061
|
+
}
|
|
2062
|
+
/**
|
|
2063
|
+
* 检查缓存是否过期
|
|
2064
|
+
*/
|
|
2065
|
+
isExpired(ttl) {
|
|
2066
|
+
try {
|
|
2067
|
+
if (!fs4.existsSync(this.cacheFile)) {
|
|
2068
|
+
return true;
|
|
2069
|
+
}
|
|
2070
|
+
const content = fs4.readFileSync(this.cacheFile, "utf-8");
|
|
2071
|
+
const { timestamp } = JSON.parse(content);
|
|
2072
|
+
return Date.now() - timestamp > ttl;
|
|
2073
|
+
} catch (error) {
|
|
2074
|
+
return true;
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
/**
|
|
2078
|
+
* 清除缓存
|
|
2079
|
+
*/
|
|
2080
|
+
clear() {
|
|
2081
|
+
if (fs4.existsSync(this.cacheFile)) {
|
|
2082
|
+
fs4.unlinkSync(this.cacheFile);
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
/**
|
|
2086
|
+
* 确保缓存目录存在
|
|
2087
|
+
*/
|
|
2088
|
+
ensureCacheDir() {
|
|
2089
|
+
if (!fs4.existsSync(this.cacheDir)) {
|
|
2090
|
+
fs4.mkdirSync(this.cacheDir, { recursive: true });
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
};
|
|
2094
|
+
|
|
2095
|
+
// src/cli/services/mcp.ts
|
|
2096
|
+
var CLAUDE_JSON_FILE = path6.join(os6.homedir(), ".claude.json");
|
|
2097
|
+
var McpService = class {
|
|
2098
|
+
cacheManager;
|
|
2099
|
+
constructor() {
|
|
2100
|
+
this.cacheManager = new CacheManager("mcp-servers");
|
|
2101
|
+
}
|
|
2102
|
+
/**
|
|
2103
|
+
* 获取所有 MCP 推荐列表(从本地配置文件读取)
|
|
2104
|
+
*/
|
|
2105
|
+
async fetchMcpServers() {
|
|
2106
|
+
try {
|
|
2107
|
+
if (fs5.existsSync(MCP_SERVERS_CONFIG_FILE)) {
|
|
2108
|
+
const content = fs5.readFileSync(MCP_SERVERS_CONFIG_FILE, "utf-8");
|
|
2109
|
+
const data = JSON.parse(content);
|
|
2110
|
+
if (data.mcpServers && Array.isArray(data.mcpServers)) {
|
|
2111
|
+
const mcpServers = this.normalizeServers(data.mcpServers);
|
|
2112
|
+
return this.mergeServers(BUILTIN_DEFAULT_MCPS, mcpServers);
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
showWarning("\u4F7F\u7528\u5185\u7F6E\u9ED8\u8BA4 MCP \u914D\u7F6E");
|
|
2116
|
+
return BUILTIN_DEFAULT_MCPS;
|
|
2117
|
+
} catch (error) {
|
|
2118
|
+
showWarning("\u8BFB\u53D6 MCP \u914D\u7F6E\u6587\u4EF6\u5931\u8D25\uFF0C\u4F7F\u7528\u5185\u7F6E\u9ED8\u8BA4\u914D\u7F6E");
|
|
2119
|
+
return BUILTIN_DEFAULT_MCPS;
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
/**
|
|
2123
|
+
* 刷新 MCP 列表(清除缓存并重新获取)
|
|
2124
|
+
*/
|
|
2125
|
+
async refreshMcpServers() {
|
|
2126
|
+
this.cacheManager.clear();
|
|
2127
|
+
return this.fetchMcpServers();
|
|
2128
|
+
}
|
|
2129
|
+
/**
|
|
2130
|
+
* 标准化 MCP 服务器数据
|
|
2131
|
+
*/
|
|
2132
|
+
normalizeServers(rawServers) {
|
|
2133
|
+
return rawServers.map((server) => ({
|
|
2134
|
+
id: server.id,
|
|
2135
|
+
name: server.name,
|
|
2136
|
+
description: server.description,
|
|
2137
|
+
usage: server.usage,
|
|
2138
|
+
transport: server.transport || "stdio",
|
|
2139
|
+
// 默认 stdio 传输
|
|
2140
|
+
package: server.package,
|
|
2141
|
+
command: server.command,
|
|
2142
|
+
args: server.args || [],
|
|
2143
|
+
url: server.url,
|
|
2144
|
+
headers: server.headers,
|
|
2145
|
+
env: server.env,
|
|
2146
|
+
requiredEnv: server.requiredEnv,
|
|
2147
|
+
apiKeyUrl: server.apiKeyUrl,
|
|
2148
|
+
category: server.category || "remote",
|
|
2149
|
+
recommended: server.recommended || false,
|
|
2150
|
+
source: server.source || "marketplace"
|
|
2151
|
+
}));
|
|
2152
|
+
}
|
|
2153
|
+
/**
|
|
2154
|
+
* 合并本地和远程 MCP 列表(去重)
|
|
2155
|
+
*/
|
|
2156
|
+
mergeServers(localServers, remoteServers) {
|
|
2157
|
+
const serverMap = /* @__PURE__ */ new Map();
|
|
2158
|
+
for (const server of localServers) {
|
|
2159
|
+
serverMap.set(server.id, server);
|
|
2160
|
+
}
|
|
2161
|
+
for (const server of remoteServers) {
|
|
2162
|
+
if (!serverMap.has(server.id)) {
|
|
2163
|
+
serverMap.set(server.id, server);
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
return Array.from(serverMap.values());
|
|
2167
|
+
}
|
|
2168
|
+
/**
|
|
2169
|
+
* 降级处理
|
|
2170
|
+
*/
|
|
2171
|
+
handleFallback(cached) {
|
|
2172
|
+
if (cached) {
|
|
2173
|
+
showWarning("\u4F7F\u7528\u7F13\u5B58\u7684 MCP \u5217\u8868\uFF08API \u6682\u65F6\u4E0D\u53EF\u7528\uFF09");
|
|
2174
|
+
return this.mergeServers(BUILTIN_DEFAULT_MCPS, cached.data.mcpServers);
|
|
2175
|
+
}
|
|
2176
|
+
showWarning("\u4F7F\u7528\u5185\u7F6E\u9ED8\u8BA4 MCP \u914D\u7F6E");
|
|
2177
|
+
return BUILTIN_DEFAULT_MCPS;
|
|
2178
|
+
}
|
|
2179
|
+
/**
|
|
2180
|
+
* 读取 MCP 配置
|
|
2181
|
+
*/
|
|
2182
|
+
readMcpConfig() {
|
|
2183
|
+
try {
|
|
2184
|
+
if (fs5.existsSync(CLAUDE_JSON_FILE)) {
|
|
2185
|
+
const content = fs5.readFileSync(CLAUDE_JSON_FILE, "utf-8");
|
|
2186
|
+
return JSON.parse(content);
|
|
2187
|
+
}
|
|
2188
|
+
} catch {
|
|
2189
|
+
}
|
|
2190
|
+
return { mcpServers: {} };
|
|
2191
|
+
}
|
|
2192
|
+
/**
|
|
2193
|
+
* 写入 MCP 配置
|
|
2194
|
+
*/
|
|
2195
|
+
writeMcpConfig(config) {
|
|
2196
|
+
fs5.writeFileSync(
|
|
2197
|
+
CLAUDE_JSON_FILE,
|
|
2198
|
+
JSON.stringify(config, null, 2),
|
|
2199
|
+
"utf-8"
|
|
2200
|
+
);
|
|
2201
|
+
}
|
|
2202
|
+
/**
|
|
2203
|
+
* 获取已安装的 MCP 服务列表
|
|
2204
|
+
*/
|
|
2205
|
+
getInstalledServers() {
|
|
2206
|
+
const config = this.readMcpConfig();
|
|
2207
|
+
return Object.keys(config.mcpServers || {});
|
|
2208
|
+
}
|
|
2209
|
+
/**
|
|
2210
|
+
* 检查 MCP 服务器是否已安装
|
|
2211
|
+
*/
|
|
2212
|
+
isServerInstalled(serverId) {
|
|
2213
|
+
return this.getInstalledServers().includes(serverId);
|
|
2214
|
+
}
|
|
2215
|
+
/**
|
|
2216
|
+
* 安装单个 MCP 服务器
|
|
2217
|
+
* @param server MCP 服务器配置
|
|
2218
|
+
* @param userEnv 用户提供的环境变量(如 API Key)
|
|
2219
|
+
*/
|
|
2220
|
+
installServer(server, userEnv) {
|
|
2221
|
+
const config = this.readMcpConfig();
|
|
2222
|
+
if (!config.mcpServers) {
|
|
2223
|
+
config.mcpServers = {};
|
|
2224
|
+
}
|
|
2225
|
+
if (server.transport === "http" || server.transport === "sse") {
|
|
2226
|
+
const headers = {};
|
|
2227
|
+
if (server.headers) {
|
|
2228
|
+
for (const [key, value] of Object.entries(server.headers)) {
|
|
2229
|
+
const match = value.match(/^\$\{(.+)\}$/);
|
|
2230
|
+
if (match && userEnv) {
|
|
2231
|
+
const varName = match[1];
|
|
2232
|
+
headers[key] = userEnv[varName] || value;
|
|
2233
|
+
} else {
|
|
2234
|
+
headers[key] = value;
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
config.mcpServers[server.id] = {
|
|
2239
|
+
transport: server.transport,
|
|
2240
|
+
url: server.url,
|
|
2241
|
+
...Object.keys(headers).length > 0 ? { headers } : {}
|
|
2242
|
+
};
|
|
2243
|
+
} else {
|
|
2244
|
+
config.mcpServers[server.id] = {
|
|
2245
|
+
command: server.command,
|
|
2246
|
+
args: server.args,
|
|
2247
|
+
...server.env || userEnv ? { env: { ...server.env, ...userEnv } } : {}
|
|
2248
|
+
};
|
|
2249
|
+
}
|
|
2250
|
+
this.writeMcpConfig(config);
|
|
2251
|
+
}
|
|
2252
|
+
/**
|
|
2253
|
+
* 卸载单个 MCP 服务器
|
|
2254
|
+
*/
|
|
2255
|
+
uninstallServer(serverId) {
|
|
2256
|
+
const config = this.readMcpConfig();
|
|
2257
|
+
delete config.mcpServers[serverId];
|
|
2258
|
+
this.writeMcpConfig(config);
|
|
2259
|
+
}
|
|
2260
|
+
/**
|
|
2261
|
+
* 批量安装 MCP 服务器(不提示输入 API Key)
|
|
2262
|
+
*/
|
|
2263
|
+
installServers(servers) {
|
|
2264
|
+
const config = this.readMcpConfig();
|
|
2265
|
+
if (!config.mcpServers) {
|
|
2266
|
+
config.mcpServers = {};
|
|
2267
|
+
}
|
|
2268
|
+
for (const server of servers) {
|
|
2269
|
+
if (server.transport === "http" || server.transport === "sse") {
|
|
2270
|
+
config.mcpServers[server.id] = {
|
|
2271
|
+
transport: server.transport,
|
|
2272
|
+
url: server.url,
|
|
2273
|
+
...server.headers ? { headers: server.headers } : {}
|
|
2274
|
+
};
|
|
2275
|
+
} else {
|
|
2276
|
+
config.mcpServers[server.id] = {
|
|
2277
|
+
command: server.command,
|
|
2278
|
+
args: server.args,
|
|
2279
|
+
...server.env ? { env: server.env } : {}
|
|
2280
|
+
};
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
this.writeMcpConfig(config);
|
|
2284
|
+
}
|
|
2285
|
+
/**
|
|
2286
|
+
* 批量卸载 MCP 服务器
|
|
2287
|
+
*/
|
|
2288
|
+
uninstallServers(serverIds) {
|
|
2289
|
+
const config = this.readMcpConfig();
|
|
2290
|
+
for (const serverId of serverIds) {
|
|
2291
|
+
delete config.mcpServers[serverId];
|
|
2292
|
+
}
|
|
2293
|
+
this.writeMcpConfig(config);
|
|
2294
|
+
}
|
|
2295
|
+
};
|
|
2296
|
+
|
|
2297
|
+
// src/cli/menus/mcp.ts
|
|
2298
|
+
var mcpService = new McpService();
|
|
2299
|
+
async function showMcpMenu() {
|
|
2300
|
+
showHeader();
|
|
2301
|
+
console.log(createBoxTitle("MCP \u670D\u52A1\u7BA1\u7406"));
|
|
2302
|
+
console.log();
|
|
2303
|
+
console.log();
|
|
2304
|
+
console.log(theme.info(" \u{1F680} \u656C\u8BF7\u671F\u5F85 \u{1F680}"));
|
|
2305
|
+
console.log();
|
|
2306
|
+
console.log(theme.dim(" MCP \u670D\u52A1\u7BA1\u7406\u529F\u80FD\u5373\u5C06\u4E0A\u7EBF"));
|
|
2307
|
+
console.log(theme.dim(" \u652F\u6301\u672C\u5730+\u4E91\u7AEF MCP \u670D\u52A1\u7BA1\u7406"));
|
|
2308
|
+
console.log(theme.dim(" \u656C\u8BF7\u671F\u5F85..."));
|
|
2309
|
+
console.log();
|
|
2310
|
+
console.log();
|
|
2311
|
+
await inquirer5.prompt([
|
|
2312
|
+
{ type: "input", name: "continue", message: "\u6309\u56DE\u8F66\u952E\u8FD4\u56DE..." }
|
|
2313
|
+
]);
|
|
2314
|
+
return;
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
// src/cli/menus/plugins.ts
|
|
2318
|
+
import { execSync as execSync4 } from "child_process";
|
|
2319
|
+
import fs7 from "fs";
|
|
2320
|
+
import inquirer6 from "inquirer";
|
|
2321
|
+
import ora6 from "ora";
|
|
2322
|
+
import os8 from "os";
|
|
2323
|
+
import path8 from "path";
|
|
2324
|
+
|
|
2325
|
+
// src/cli/services/marketplace.ts
|
|
2326
|
+
import { execSync as execSync3 } from "child_process";
|
|
2327
|
+
import fs6 from "fs";
|
|
2328
|
+
import os7 from "os";
|
|
2329
|
+
import path7 from "path";
|
|
2330
|
+
var MarketplaceService = class {
|
|
2331
|
+
cacheManager;
|
|
2332
|
+
constructor() {
|
|
2333
|
+
this.cacheManager = new CacheManager("markets");
|
|
2334
|
+
}
|
|
2335
|
+
/**
|
|
2336
|
+
* 获取市场列表(从本地配置文件读取)
|
|
2337
|
+
*/
|
|
2338
|
+
async fetchMarkets() {
|
|
2339
|
+
try {
|
|
2340
|
+
if (fs6.existsSync(MARKETS_CONFIG_FILE)) {
|
|
2341
|
+
const content = fs6.readFileSync(MARKETS_CONFIG_FILE, "utf-8");
|
|
2342
|
+
const data = JSON.parse(content);
|
|
2343
|
+
if (data.markets && Array.isArray(data.markets)) {
|
|
2344
|
+
return this.normalizeMarkets(data.markets);
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
showWarning("\u4F7F\u7528\u5185\u7F6E\u9ED8\u8BA4\u5E02\u573A\u914D\u7F6E");
|
|
2348
|
+
return BUILTIN_DEFAULT_MARKETS;
|
|
2349
|
+
} catch (error) {
|
|
2350
|
+
showWarning("\u8BFB\u53D6\u914D\u7F6E\u6587\u4EF6\u5931\u8D25\uFF0C\u4F7F\u7528\u5185\u7F6E\u9ED8\u8BA4\u5E02\u573A\u914D\u7F6E");
|
|
2351
|
+
return BUILTIN_DEFAULT_MARKETS;
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
/**
|
|
2355
|
+
* 标准化市场数据(移除内部字段)
|
|
2356
|
+
*/
|
|
2357
|
+
normalizeMarkets(rawMarkets) {
|
|
2358
|
+
return rawMarkets.map((market) => ({
|
|
2359
|
+
name: market.name,
|
|
2360
|
+
source: market.source,
|
|
2361
|
+
description: market.description,
|
|
2362
|
+
category: market.category,
|
|
2363
|
+
recommended: market.recommended || false,
|
|
2364
|
+
default: market.default || false
|
|
2365
|
+
}));
|
|
2366
|
+
}
|
|
2367
|
+
/**
|
|
2368
|
+
* 获取已安装的市场列表
|
|
2369
|
+
*/
|
|
2370
|
+
async getInstalledMarkets() {
|
|
2371
|
+
try {
|
|
2372
|
+
const output = this.execClaude("plugin marketplace list");
|
|
2373
|
+
return this.parseMarketplaceList(output);
|
|
2374
|
+
} catch (error) {
|
|
2375
|
+
return [];
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
/**
|
|
2379
|
+
* 检查市场是否已安装(通过源地址或名称匹配)
|
|
2380
|
+
*/
|
|
2381
|
+
async isMarketInstalled(marketSource, marketName) {
|
|
2382
|
+
const installed = await this.getInstalledMarkets();
|
|
2383
|
+
const normalizedSource = marketSource.replace(/\.git$/, "");
|
|
2384
|
+
return installed.some((m) => {
|
|
2385
|
+
if (m.source) {
|
|
2386
|
+
const normalizedInstalled = m.source.replace(/\.git$/, "");
|
|
2387
|
+
if (normalizedInstalled === normalizedSource) {
|
|
2388
|
+
return true;
|
|
2389
|
+
}
|
|
2390
|
+
if (marketName && normalizedInstalled.includes(marketName)) {
|
|
2391
|
+
return true;
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
if (marketName) {
|
|
2395
|
+
const baseMarketName = marketName.replace(/-dev$/, "");
|
|
2396
|
+
const baseInstalledName = m.name.replace(/-dev$/, "");
|
|
2397
|
+
if (baseMarketName === baseInstalledName) {
|
|
2398
|
+
return true;
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
return false;
|
|
2402
|
+
});
|
|
2403
|
+
}
|
|
2404
|
+
/**
|
|
2405
|
+
* 根据源地址或名称获取已安装市场的名称
|
|
2406
|
+
*/
|
|
2407
|
+
async getInstalledMarketName(marketSource, marketName) {
|
|
2408
|
+
const installed = await this.getInstalledMarkets();
|
|
2409
|
+
const normalizedSource = marketSource.replace(/\.git$/, "");
|
|
2410
|
+
const found = installed.find((m) => {
|
|
2411
|
+
if (m.source) {
|
|
2412
|
+
const normalizedInstalled = m.source.replace(/\.git$/, "");
|
|
2413
|
+
if (normalizedInstalled === normalizedSource) {
|
|
2414
|
+
return true;
|
|
2415
|
+
}
|
|
2416
|
+
if (marketName && normalizedInstalled.includes(marketName)) {
|
|
2417
|
+
return true;
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
if (marketName) {
|
|
2421
|
+
const baseMarketName = marketName.replace(/-dev$/, "");
|
|
2422
|
+
const baseInstalledName = m.name.replace(/-dev$/, "");
|
|
2423
|
+
if (baseMarketName === baseInstalledName) {
|
|
2424
|
+
return true;
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
return false;
|
|
2428
|
+
});
|
|
2429
|
+
return found ? found.name : null;
|
|
2430
|
+
}
|
|
2431
|
+
/**
|
|
2432
|
+
* 添加市场
|
|
2433
|
+
*/
|
|
2434
|
+
async addMarket(market) {
|
|
2435
|
+
try {
|
|
2436
|
+
this.execClaude(`plugin marketplace add "${market.source}"`);
|
|
2437
|
+
return true;
|
|
2438
|
+
} catch (error) {
|
|
2439
|
+
if (error.message.includes("already exists") || error.message.includes("already configured") || error.message.includes("already installed")) {
|
|
2440
|
+
return true;
|
|
2441
|
+
}
|
|
2442
|
+
throw error;
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
/**
|
|
2446
|
+
* 从市场获取插件列表(带版本和更新检测)
|
|
2447
|
+
*/
|
|
2448
|
+
async getMarketPlugins(marketName) {
|
|
2449
|
+
try {
|
|
2450
|
+
const marketplacePath = this.getMarketplacePath(marketName);
|
|
2451
|
+
const manifestPath = path7.join(
|
|
2452
|
+
marketplacePath,
|
|
2453
|
+
".claude-plugin",
|
|
2454
|
+
"marketplace.json"
|
|
2455
|
+
);
|
|
2456
|
+
if (!fs6.existsSync(manifestPath)) {
|
|
2457
|
+
return [];
|
|
2458
|
+
}
|
|
2459
|
+
const manifest = JSON.parse(fs6.readFileSync(manifestPath, "utf-8"));
|
|
2460
|
+
if (!manifest.plugins || !Array.isArray(manifest.plugins)) {
|
|
2461
|
+
return [];
|
|
2462
|
+
}
|
|
2463
|
+
const installedVersions = await this.getInstalledPluginVersions();
|
|
2464
|
+
return manifest.plugins.map((plugin) => {
|
|
2465
|
+
const pluginFullId = `${plugin.name}@${marketName}`;
|
|
2466
|
+
const installedVersion = installedVersions.get(pluginFullId);
|
|
2467
|
+
const marketVersion = plugin.version || "1.0.0";
|
|
2468
|
+
return {
|
|
2469
|
+
id: plugin.name,
|
|
2470
|
+
name: plugin.name.charAt(0).toUpperCase() + plugin.name.slice(1),
|
|
2471
|
+
description: plugin.description,
|
|
2472
|
+
marketplace: marketName,
|
|
2473
|
+
category: plugin.category || "development",
|
|
2474
|
+
recommended: true,
|
|
2475
|
+
version: marketVersion,
|
|
2476
|
+
installedVersion,
|
|
2477
|
+
hasUpdate: installedVersion ? this.compareVersions(marketVersion, installedVersion) > 0 : false
|
|
2478
|
+
};
|
|
2479
|
+
});
|
|
2480
|
+
} catch (error) {
|
|
2481
|
+
console.error("\u8BFB\u53D6\u5E02\u573A\u63D2\u4EF6\u5217\u8868\u5931\u8D25:", error);
|
|
2482
|
+
return [];
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
/**
|
|
2486
|
+
* 获取已安装插件的版本信息
|
|
2487
|
+
*/
|
|
2488
|
+
async getInstalledPluginVersions() {
|
|
2489
|
+
const versions = /* @__PURE__ */ new Map();
|
|
2490
|
+
try {
|
|
2491
|
+
const output = this.execClaude("plugin list --json");
|
|
2492
|
+
const data = JSON.parse(output);
|
|
2493
|
+
if (data.installed && Array.isArray(data.installed)) {
|
|
2494
|
+
for (const plugin of data.installed) {
|
|
2495
|
+
versions.set(plugin.id, plugin.version);
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
} catch (error) {
|
|
2499
|
+
}
|
|
2500
|
+
return versions;
|
|
2501
|
+
}
|
|
2502
|
+
/**
|
|
2503
|
+
* 比较版本号
|
|
2504
|
+
* @returns >0 如果 v1 > v2, =0 如果相等, <0 如果 v1 < v2
|
|
2505
|
+
*/
|
|
2506
|
+
compareVersions(v1, v2) {
|
|
2507
|
+
const parts1 = v1.split(".").map(Number);
|
|
2508
|
+
const parts2 = v2.split(".").map(Number);
|
|
2509
|
+
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
2510
|
+
const num1 = parts1[i] || 0;
|
|
2511
|
+
const num2 = parts2[i] || 0;
|
|
2512
|
+
if (num1 > num2) return 1;
|
|
2513
|
+
if (num1 < num2) return -1;
|
|
2514
|
+
}
|
|
2515
|
+
return 0;
|
|
2516
|
+
}
|
|
2517
|
+
/**
|
|
2518
|
+
* 刷新市场列表(清除缓存并重新获取)
|
|
2519
|
+
*/
|
|
2520
|
+
async refreshMarkets() {
|
|
2521
|
+
this.cacheManager.clear();
|
|
2522
|
+
return this.fetchMarkets();
|
|
2523
|
+
}
|
|
2524
|
+
/**
|
|
2525
|
+
* 更新单个市场
|
|
2526
|
+
*/
|
|
2527
|
+
async updateMarket(marketName) {
|
|
2528
|
+
try {
|
|
2529
|
+
this.execClaude(`plugin marketplace update "${marketName}"`);
|
|
2530
|
+
return true;
|
|
2531
|
+
} catch (error) {
|
|
2532
|
+
throw new Error(`\u66F4\u65B0\u5E02\u573A\u5931\u8D25: ${error.message}`);
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
/**
|
|
2536
|
+
* 更新所有市场
|
|
2537
|
+
*/
|
|
2538
|
+
async updateAllMarkets() {
|
|
2539
|
+
try {
|
|
2540
|
+
this.execClaude("plugin marketplace update");
|
|
2541
|
+
return true;
|
|
2542
|
+
} catch (error) {
|
|
2543
|
+
throw new Error(`\u66F4\u65B0\u6240\u6709\u5E02\u573A\u5931\u8D25: ${error.message}`);
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
/**
|
|
2547
|
+
* 更新单个插件
|
|
2548
|
+
*/
|
|
2549
|
+
async updatePlugin(pluginName) {
|
|
2550
|
+
try {
|
|
2551
|
+
this.execClaude(`plugin update "${pluginName}"`);
|
|
2552
|
+
return true;
|
|
2553
|
+
} catch (error) {
|
|
2554
|
+
throw new Error(`\u66F4\u65B0\u63D2\u4EF6\u5931\u8D25: ${error.message}`);
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
/**
|
|
2558
|
+
* 降级处理
|
|
2559
|
+
*/
|
|
2560
|
+
handleFallback(cached) {
|
|
2561
|
+
if (cached) {
|
|
2562
|
+
showWarning("\u4F7F\u7528\u7F13\u5B58\u7684\u5E02\u573A\u5217\u8868\uFF08API \u6682\u65F6\u4E0D\u53EF\u7528\uFF09");
|
|
2563
|
+
return cached.data.markets;
|
|
2564
|
+
}
|
|
2565
|
+
showWarning("\u4F7F\u7528\u5185\u7F6E\u9ED8\u8BA4\u5E02\u573A\u914D\u7F6E");
|
|
2566
|
+
return BUILTIN_DEFAULT_MARKETS;
|
|
2567
|
+
}
|
|
2568
|
+
/**
|
|
2569
|
+
* 解析 marketplace list 输出
|
|
2570
|
+
*/
|
|
2571
|
+
parseMarketplaceList(output) {
|
|
2572
|
+
const markets = [];
|
|
2573
|
+
const lines = output.split("\n");
|
|
2574
|
+
let currentMarket = null;
|
|
2575
|
+
for (const line of lines) {
|
|
2576
|
+
const trimmed = line.trim();
|
|
2577
|
+
if (trimmed.startsWith("\u276F ")) {
|
|
2578
|
+
if (currentMarket) {
|
|
2579
|
+
markets.push(currentMarket);
|
|
2580
|
+
}
|
|
2581
|
+
const name = trimmed.substring(2).trim();
|
|
2582
|
+
currentMarket = { name };
|
|
2583
|
+
} else if (currentMarket && trimmed.startsWith("Source:")) {
|
|
2584
|
+
const match = trimmed.match(/Source:\s+\w+\s+\((.+)\)/);
|
|
2585
|
+
if (match) {
|
|
2586
|
+
currentMarket.source = match[1];
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
if (currentMarket) {
|
|
2591
|
+
markets.push(currentMarket);
|
|
2592
|
+
}
|
|
2593
|
+
return markets;
|
|
2594
|
+
}
|
|
2595
|
+
/**
|
|
2596
|
+
* 获取市场仓库路径
|
|
2597
|
+
*/
|
|
2598
|
+
getMarketplacePath(marketName) {
|
|
2599
|
+
return path7.join(
|
|
2600
|
+
os7.homedir(),
|
|
2601
|
+
".claude",
|
|
2602
|
+
"plugins",
|
|
2603
|
+
"marketplaces",
|
|
2604
|
+
marketName
|
|
2605
|
+
);
|
|
2606
|
+
}
|
|
2607
|
+
/**
|
|
2608
|
+
* 执行 Claude 命令
|
|
2609
|
+
*/
|
|
2610
|
+
execClaude(command) {
|
|
2611
|
+
try {
|
|
2612
|
+
return execSync3(`claude ${command}`, {
|
|
2613
|
+
encoding: "utf-8",
|
|
2614
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2615
|
+
});
|
|
2616
|
+
} catch (error) {
|
|
2617
|
+
throw new Error(`\u6267\u884C\u547D\u4EE4\u5931\u8D25: ${error.message}
|
|
2618
|
+
${error.stderr || ""}`);
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
};
|
|
2622
|
+
|
|
2623
|
+
// src/cli/menus/plugins.ts
|
|
2624
|
+
function execClaude(command) {
|
|
2625
|
+
try {
|
|
2626
|
+
return execSync4(`claude ${command}`, {
|
|
2627
|
+
encoding: "utf-8",
|
|
2628
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2629
|
+
});
|
|
2630
|
+
} catch (error) {
|
|
2631
|
+
throw new Error(`\u6267\u884C\u547D\u4EE4\u5931\u8D25: ${error.message}
|
|
2632
|
+
${error.stderr || ""}`);
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
var marketplaceService = new MarketplaceService();
|
|
2636
|
+
function getInstalledPlugins() {
|
|
2637
|
+
try {
|
|
2638
|
+
const output = execClaude("plugin list");
|
|
2639
|
+
const plugins = [];
|
|
2640
|
+
const lines = output.split("\n");
|
|
2641
|
+
let currentPlugin = {};
|
|
2642
|
+
for (const line of lines) {
|
|
2643
|
+
const trimmed = line.trim();
|
|
2644
|
+
if (trimmed.startsWith("\u276F ")) {
|
|
2645
|
+
if (currentPlugin.name) {
|
|
2646
|
+
plugins.push(currentPlugin);
|
|
2647
|
+
}
|
|
2648
|
+
currentPlugin = { name: trimmed.substring(2).trim() };
|
|
2649
|
+
} else if (trimmed.startsWith("Version:")) {
|
|
2650
|
+
currentPlugin.version = trimmed.substring(8).trim();
|
|
2651
|
+
} else if (trimmed.startsWith("Status:")) {
|
|
2652
|
+
currentPlugin.enabled = trimmed.includes("enabled");
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
if (currentPlugin.name) {
|
|
2656
|
+
plugins.push(currentPlugin);
|
|
2657
|
+
}
|
|
2658
|
+
return plugins;
|
|
2659
|
+
} catch (error) {
|
|
2660
|
+
return [];
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
function getPluginStatus(pluginFullName, installedPlugins) {
|
|
2664
|
+
const installed = installedPlugins.find((p) => p.name === pluginFullName);
|
|
2665
|
+
if (!installed) return "not_installed";
|
|
2666
|
+
return installed.enabled ? "enabled" : "disabled";
|
|
2667
|
+
}
|
|
2668
|
+
function getStatusText(status) {
|
|
2669
|
+
switch (status) {
|
|
2670
|
+
case "not_installed":
|
|
2671
|
+
return theme.dim("[\u672A\u5B89\u88C5]");
|
|
2672
|
+
case "enabled":
|
|
2673
|
+
return theme.success("[\u5DF2\u542F\u7528]");
|
|
2674
|
+
case "disabled":
|
|
2675
|
+
return theme.warning("[\u5DF2\u7981\u7528]");
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
async function ensureDefaultMarkets(markets) {
|
|
2679
|
+
for (const market of markets) {
|
|
2680
|
+
if (market.default) {
|
|
2681
|
+
const isInstalled = await marketplaceService.isMarketInstalled(
|
|
2682
|
+
market.source,
|
|
2683
|
+
market.name
|
|
2684
|
+
);
|
|
2685
|
+
if (!isInstalled) {
|
|
2686
|
+
try {
|
|
2687
|
+
await marketplaceService.addMarket(market);
|
|
2688
|
+
} catch (error) {
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
async function showPluginsMenu() {
|
|
2695
|
+
const spinner = ora6("\u6B63\u5728\u52A0\u8F7D\u63D2\u4EF6\u5E02\u573A...").start();
|
|
2696
|
+
const markets = await marketplaceService.fetchMarkets();
|
|
2697
|
+
await ensureDefaultMarkets(markets);
|
|
2698
|
+
const installedMarkets = await marketplaceService.getInstalledMarkets();
|
|
2699
|
+
spinner.stop();
|
|
2700
|
+
while (true) {
|
|
2701
|
+
showHeader();
|
|
2702
|
+
console.log(createBoxTitle("\u63D2\u4EF6\u5E02\u573A"));
|
|
2703
|
+
console.log();
|
|
2704
|
+
console.log(theme.primary("\u{1F4E6} \u53EF\u7528\u5E02\u573A\uFF1A"));
|
|
2705
|
+
console.log();
|
|
2706
|
+
console.log(theme.dim("\u2191\u2193 \u9009\u62E9 | Enter \u786E\u8BA4"));
|
|
2707
|
+
console.log();
|
|
2708
|
+
const marketChoices = markets.map((market) => {
|
|
2709
|
+
const isInstalled = installedMarkets.some((m) => {
|
|
2710
|
+
if (m.source) {
|
|
2711
|
+
const normalizedSource = market.source.replace(/\.git$/, "");
|
|
2712
|
+
const normalizedInstalled = m.source.replace(/\.git$/, "");
|
|
2713
|
+
if (normalizedInstalled === normalizedSource) {
|
|
2714
|
+
return true;
|
|
2715
|
+
}
|
|
2716
|
+
if (normalizedInstalled.includes(market.name)) {
|
|
2717
|
+
return true;
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
const baseMarketName = market.name.replace(/-dev$/, "");
|
|
2721
|
+
const baseInstalledName = m.name.replace(/-dev$/, "");
|
|
2722
|
+
return baseMarketName === baseInstalledName;
|
|
2723
|
+
});
|
|
2724
|
+
const statusIcon = isInstalled ? theme.success("\u2713") : theme.dim("\u2022");
|
|
2725
|
+
const categoryLabel = getCategoryLabel(market.category);
|
|
2726
|
+
return {
|
|
2727
|
+
name: ` ${statusIcon} [${categoryLabel}] ${market.name} - ${theme.dim(
|
|
2728
|
+
market.description
|
|
2729
|
+
)}`,
|
|
2730
|
+
value: { type: "market", market }
|
|
2731
|
+
};
|
|
2732
|
+
});
|
|
2733
|
+
const choices = [
|
|
2734
|
+
...marketChoices,
|
|
2735
|
+
new inquirer6.Separator(),
|
|
2736
|
+
{
|
|
2737
|
+
name: ` ${theme.info("\u{1F504}")} \u5237\u65B0\u5E02\u573A\u5217\u8868`,
|
|
2738
|
+
value: { type: "refresh" }
|
|
2739
|
+
},
|
|
2740
|
+
{
|
|
2741
|
+
name: ` ${theme.info("\u2B06\uFE0F")} \u66F4\u65B0\u6240\u6709\u5E02\u573A`,
|
|
2742
|
+
value: { type: "update_all_markets" }
|
|
2743
|
+
},
|
|
2744
|
+
new inquirer6.Separator(),
|
|
2745
|
+
{
|
|
2746
|
+
name: ` ${theme.dim("<-")} \u8FD4\u56DE`,
|
|
2747
|
+
value: { type: "back" }
|
|
2748
|
+
},
|
|
2749
|
+
{
|
|
2750
|
+
name: ` ${theme.dim("X")} \u9000\u51FA`,
|
|
2751
|
+
value: { type: "exit" }
|
|
2752
|
+
}
|
|
2753
|
+
];
|
|
2754
|
+
const { selection } = await inquirer6.prompt([
|
|
2755
|
+
{
|
|
2756
|
+
type: "list",
|
|
2757
|
+
name: "selection",
|
|
2758
|
+
message: "\u8BF7\u9009\u62E9\u5E02\u573A\uFF1A",
|
|
2759
|
+
pageSize: 15,
|
|
2760
|
+
choices
|
|
2761
|
+
}
|
|
2762
|
+
]);
|
|
2763
|
+
if (selection.type === "back") {
|
|
2764
|
+
return;
|
|
2765
|
+
}
|
|
2766
|
+
if (selection.type === "exit") {
|
|
2767
|
+
process.exit(0);
|
|
2768
|
+
}
|
|
2769
|
+
if (selection.type === "refresh") {
|
|
2770
|
+
const refreshSpinner = ora6("\u6B63\u5728\u5237\u65B0\u5E02\u573A\u5217\u8868...").start();
|
|
2771
|
+
const newMarkets = await marketplaceService.refreshMarkets();
|
|
2772
|
+
markets.length = 0;
|
|
2773
|
+
markets.push(...newMarkets);
|
|
2774
|
+
refreshSpinner.succeed("\u5E02\u573A\u5217\u8868\u5DF2\u5237\u65B0");
|
|
2775
|
+
await new Promise((resolve) => {
|
|
2776
|
+
setTimeout(() => resolve(), 800);
|
|
2777
|
+
});
|
|
2778
|
+
continue;
|
|
2779
|
+
}
|
|
2780
|
+
if (selection.type === "update_all_markets") {
|
|
2781
|
+
const updateSpinner = ora6("\u6B63\u5728\u66F4\u65B0\u6240\u6709\u5E02\u573A...").start();
|
|
2782
|
+
try {
|
|
2783
|
+
await marketplaceService.updateAllMarkets();
|
|
2784
|
+
updateSpinner.succeed("\u6240\u6709\u5E02\u573A\u5DF2\u66F4\u65B0");
|
|
2785
|
+
await new Promise((resolve) => {
|
|
2786
|
+
setTimeout(() => resolve(), 1e3);
|
|
2787
|
+
});
|
|
2788
|
+
} catch (error) {
|
|
2789
|
+
updateSpinner.fail(`\u66F4\u65B0\u5931\u8D25: ${error.message}`);
|
|
2790
|
+
await new Promise((resolve) => {
|
|
2791
|
+
setTimeout(() => resolve(), 1500);
|
|
2792
|
+
});
|
|
2793
|
+
}
|
|
2794
|
+
continue;
|
|
2795
|
+
}
|
|
2796
|
+
if (selection.type === "market") {
|
|
2797
|
+
await showMarketPlugins(selection.market);
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
function getCategoryLabel(category) {
|
|
2802
|
+
switch (category) {
|
|
2803
|
+
case "official":
|
|
2804
|
+
return theme.success("\u5B98\u65B9");
|
|
2805
|
+
case "team":
|
|
2806
|
+
return theme.info("\u56E2\u961F");
|
|
2807
|
+
case "experimental":
|
|
2808
|
+
return theme.warning("\u5B9E\u9A8C");
|
|
2809
|
+
default:
|
|
2810
|
+
return theme.dim("\u5176\u4ED6");
|
|
2811
|
+
}
|
|
2812
|
+
}
|
|
2813
|
+
async function showMarketPlugins(market) {
|
|
2814
|
+
const isInstalled = await marketplaceService.isMarketInstalled(
|
|
2815
|
+
market.source,
|
|
2816
|
+
market.name
|
|
2817
|
+
);
|
|
2818
|
+
if (!isInstalled) {
|
|
2819
|
+
const addSpinner = ora6("\u6B63\u5728\u6DFB\u52A0\u5E02\u573A...").start();
|
|
2820
|
+
try {
|
|
2821
|
+
await marketplaceService.addMarket(market);
|
|
2822
|
+
addSpinner.succeed("\u5E02\u573A\u6DFB\u52A0\u6210\u529F");
|
|
2823
|
+
} catch (error) {
|
|
2824
|
+
addSpinner.fail(`\u5E02\u573A\u6DFB\u52A0\u5931\u8D25: ${error.message}`);
|
|
2825
|
+
await new Promise((resolve) => {
|
|
2826
|
+
setTimeout(() => resolve(), 2e3);
|
|
2827
|
+
});
|
|
2828
|
+
return;
|
|
2829
|
+
}
|
|
2830
|
+
await new Promise((resolve) => {
|
|
2831
|
+
setTimeout(() => resolve(), 800);
|
|
2832
|
+
});
|
|
2833
|
+
}
|
|
2834
|
+
const installedMarketName = await marketplaceService.getInstalledMarketName(
|
|
2835
|
+
market.source,
|
|
2836
|
+
market.name
|
|
2837
|
+
);
|
|
2838
|
+
if (!installedMarketName) {
|
|
2839
|
+
showWarning("\u65E0\u6CD5\u627E\u5230\u5DF2\u5B89\u88C5\u7684\u5E02\u573A");
|
|
2840
|
+
return;
|
|
2841
|
+
}
|
|
2842
|
+
const loadingSpinner = ora6("\u6B63\u5728\u52A0\u8F7D\u63D2\u4EF6\u5217\u8868...").start();
|
|
2843
|
+
const marketplacePlugins = await marketplaceService.getMarketPlugins(
|
|
2844
|
+
installedMarketName
|
|
2845
|
+
);
|
|
2846
|
+
let installedPlugins = getInstalledPlugins();
|
|
2847
|
+
loadingSpinner.stop();
|
|
2848
|
+
while (true) {
|
|
2849
|
+
showHeader();
|
|
2850
|
+
console.log(createBoxTitle(`${market.name} \u63D2\u4EF6\u5E02\u573A`));
|
|
2851
|
+
console.log();
|
|
2852
|
+
console.log(`${theme.dim("\u63CF\u8FF0:")} ${market.description}`);
|
|
2853
|
+
console.log(`${theme.dim("\u6E90\u5730\u5740:")} ${market.source}`);
|
|
2854
|
+
console.log();
|
|
2855
|
+
console.log(theme.primary("\u{1F4E6} \u53EF\u7528\u63D2\u4EF6\uFF1A"));
|
|
2856
|
+
console.log();
|
|
2857
|
+
console.log(theme.dim("\u2191\u2193 \u9009\u62E9 | Enter \u786E\u8BA4"));
|
|
2858
|
+
console.log();
|
|
2859
|
+
const pluginStatuses = /* @__PURE__ */ new Map();
|
|
2860
|
+
for (const p of marketplacePlugins) {
|
|
2861
|
+
const pluginFullName = `${p.id}@${p.marketplace}`;
|
|
2862
|
+
pluginStatuses.set(
|
|
2863
|
+
pluginFullName,
|
|
2864
|
+
getPluginStatus(pluginFullName, installedPlugins)
|
|
2865
|
+
);
|
|
2866
|
+
}
|
|
2867
|
+
const pluginChoices = marketplacePlugins.map((p) => {
|
|
2868
|
+
const pluginFullName = `${p.id}@${p.marketplace}`;
|
|
2869
|
+
const status = pluginStatuses.get(pluginFullName);
|
|
2870
|
+
const statusText = getStatusText(status);
|
|
2871
|
+
const updateBadge = p.hasUpdate ? theme.warning(" (\u6709\u66F4\u65B0)") : "";
|
|
2872
|
+
return {
|
|
2873
|
+
name: ` ${statusText} ${p.name}${updateBadge} - ${theme.dim(
|
|
2874
|
+
p.description
|
|
2875
|
+
)}`,
|
|
2876
|
+
value: { type: "plugin", plugin: p, status }
|
|
2877
|
+
};
|
|
2878
|
+
});
|
|
2879
|
+
const hasUpdatablePlugins = marketplacePlugins.some(
|
|
2880
|
+
(p) => p.hasUpdate && pluginStatuses.get(`${p.id}@${p.marketplace}`) !== "not_installed"
|
|
2881
|
+
);
|
|
2882
|
+
const choices = [
|
|
2883
|
+
...pluginChoices,
|
|
2884
|
+
new inquirer6.Separator(),
|
|
2885
|
+
{
|
|
2886
|
+
name: ` ${theme.success("\u26A1")} \u4E00\u952E\u5B89\u88C5\u5168\u90E8`,
|
|
2887
|
+
value: { type: "install_all" }
|
|
2888
|
+
},
|
|
2889
|
+
...hasUpdatablePlugins ? [
|
|
2890
|
+
{
|
|
2891
|
+
name: ` ${theme.info("\u2B06\uFE0F")} \u66F4\u65B0\u6240\u6709\u5DF2\u5B89\u88C5\u63D2\u4EF6`,
|
|
2892
|
+
value: { type: "update_all_plugins" }
|
|
2893
|
+
}
|
|
2894
|
+
] : [],
|
|
2895
|
+
{
|
|
2896
|
+
name: ` ${theme.info("\u{1F504}")} \u66F4\u65B0\u6B64\u5E02\u573A`,
|
|
2897
|
+
value: { type: "update_market" }
|
|
2898
|
+
},
|
|
2899
|
+
new inquirer6.Separator(),
|
|
2900
|
+
{
|
|
2901
|
+
name: ` ${theme.dim("<-")} \u8FD4\u56DE`,
|
|
2902
|
+
value: { type: "back" }
|
|
2903
|
+
},
|
|
2904
|
+
{
|
|
2905
|
+
name: ` ${theme.dim("X")} \u9000\u51FA`,
|
|
2906
|
+
value: { type: "exit" }
|
|
2907
|
+
}
|
|
2908
|
+
];
|
|
2909
|
+
const { selection } = await inquirer6.prompt([
|
|
2910
|
+
{
|
|
2911
|
+
type: "list",
|
|
2912
|
+
name: "selection",
|
|
2913
|
+
message: "\u8BF7\u9009\u62E9\u63D2\u4EF6\uFF1A",
|
|
2914
|
+
pageSize: 15,
|
|
2915
|
+
choices
|
|
2916
|
+
}
|
|
2917
|
+
]);
|
|
2918
|
+
if (selection.type === "back") {
|
|
2919
|
+
return;
|
|
2920
|
+
}
|
|
2921
|
+
if (selection.type === "exit") {
|
|
2922
|
+
process.exit(0);
|
|
2923
|
+
}
|
|
2924
|
+
if (selection.type === "install_all") {
|
|
2925
|
+
await handleInstallAll(marketplacePlugins);
|
|
2926
|
+
installedPlugins = getInstalledPlugins();
|
|
2927
|
+
continue;
|
|
2928
|
+
}
|
|
2929
|
+
if (selection.type === "update_all_plugins") {
|
|
2930
|
+
const updateSpinner = ora6("\u6B63\u5728\u66F4\u65B0\u6240\u6709\u5DF2\u5B89\u88C5\u7684\u63D2\u4EF6...").start();
|
|
2931
|
+
try {
|
|
2932
|
+
const pluginsToUpdate = marketplacePlugins.filter(
|
|
2933
|
+
(p) => p.hasUpdate && pluginStatuses.get(`${p.id}@${p.marketplace}`) !== "not_installed"
|
|
2934
|
+
);
|
|
2935
|
+
for (const plugin of pluginsToUpdate) {
|
|
2936
|
+
const pluginFullName = `${plugin.id}@${plugin.marketplace}`;
|
|
2937
|
+
updateSpinner.text = `\u6B63\u5728\u66F4\u65B0 ${plugin.name}...`;
|
|
2938
|
+
await marketplaceService.updatePlugin(pluginFullName);
|
|
2939
|
+
}
|
|
2940
|
+
updateSpinner.succeed("\u6240\u6709\u63D2\u4EF6\u5DF2\u66F4\u65B0");
|
|
2941
|
+
await new Promise((resolve) => {
|
|
2942
|
+
setTimeout(() => resolve(), 1e3);
|
|
2943
|
+
});
|
|
2944
|
+
installedPlugins = getInstalledPlugins();
|
|
2945
|
+
} catch (error) {
|
|
2946
|
+
updateSpinner.fail(`\u66F4\u65B0\u5931\u8D25: ${error.message}`);
|
|
2947
|
+
await new Promise((resolve) => {
|
|
2948
|
+
setTimeout(() => resolve(), 1500);
|
|
2949
|
+
});
|
|
2950
|
+
}
|
|
2951
|
+
continue;
|
|
2952
|
+
}
|
|
2953
|
+
if (selection.type === "update_market") {
|
|
2954
|
+
const updateSpinner = ora6(`\u6B63\u5728\u66F4\u65B0\u5E02\u573A ${market.name}...`).start();
|
|
2955
|
+
try {
|
|
2956
|
+
await marketplaceService.updateMarket(installedMarketName);
|
|
2957
|
+
updateSpinner.succeed("\u5E02\u573A\u5DF2\u66F4\u65B0");
|
|
2958
|
+
await new Promise((resolve) => {
|
|
2959
|
+
setTimeout(() => resolve(), 1e3);
|
|
2960
|
+
});
|
|
2961
|
+
const updatedPlugins = await marketplaceService.getMarketPlugins(
|
|
2962
|
+
installedMarketName
|
|
2963
|
+
);
|
|
2964
|
+
marketplacePlugins.length = 0;
|
|
2965
|
+
marketplacePlugins.push(...updatedPlugins);
|
|
2966
|
+
installedPlugins = getInstalledPlugins();
|
|
2967
|
+
} catch (error) {
|
|
2968
|
+
updateSpinner.fail(`\u66F4\u65B0\u5931\u8D25: ${error.message}`);
|
|
2969
|
+
await new Promise((resolve) => {
|
|
2970
|
+
setTimeout(() => resolve(), 1500);
|
|
2971
|
+
});
|
|
2972
|
+
}
|
|
2973
|
+
continue;
|
|
2974
|
+
}
|
|
2975
|
+
if (selection.type === "plugin") {
|
|
2976
|
+
await showPluginDetail(selection.plugin, selection.status);
|
|
2977
|
+
installedPlugins = getInstalledPlugins();
|
|
2978
|
+
}
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
async function showPluginDetail(plugin, initialStatus) {
|
|
2982
|
+
const pluginFullName = `${plugin.id}@${plugin.marketplace}`;
|
|
2983
|
+
let needRefresh = true;
|
|
2984
|
+
let currentStatus = initialStatus;
|
|
2985
|
+
while (true) {
|
|
2986
|
+
showHeader();
|
|
2987
|
+
console.log(createBoxTitle(`\u63D2\u4EF6\u8BE6\u60C5\uFF1A${plugin.id}`));
|
|
2988
|
+
console.log();
|
|
2989
|
+
console.log(` ${theme.primary("\u540D\u79F0\uFF1A")} ${plugin.name}`);
|
|
2990
|
+
console.log(
|
|
2991
|
+
` ${theme.primary("\u5206\u7C7B\uFF1A")} ${theme.info(
|
|
2992
|
+
plugin.category || "development"
|
|
2993
|
+
)}`
|
|
2994
|
+
);
|
|
2995
|
+
console.log(` ${theme.primary("\u63CF\u8FF0\uFF1A")} ${plugin.description}`);
|
|
2996
|
+
console.log();
|
|
2997
|
+
if (needRefresh) {
|
|
2998
|
+
const spinner = ora6("\u6B63\u5728\u83B7\u53D6\u63D2\u4EF6\u72B6\u6001...").start();
|
|
2999
|
+
const installedPlugins = getInstalledPlugins();
|
|
3000
|
+
currentStatus = getPluginStatus(pluginFullName, installedPlugins);
|
|
3001
|
+
spinner.stop();
|
|
3002
|
+
needRefresh = false;
|
|
3003
|
+
}
|
|
3004
|
+
const status = currentStatus;
|
|
3005
|
+
if (status === "not_installed") {
|
|
3006
|
+
console.log(` ${theme.primary("\u72B6\u6001\uFF1A")} ${theme.dim("[\u672A\u5B89\u88C5]")}`);
|
|
3007
|
+
} else {
|
|
3008
|
+
console.log(` ${theme.primary("\u72B6\u6001\uFF1A")} ${theme.success("[\u5DF2\u5B89\u88C5]")}`);
|
|
3009
|
+
if (plugin.installedVersion) {
|
|
3010
|
+
console.log(
|
|
3011
|
+
` ${theme.primary("\u5F53\u524D\u7248\u672C\uFF1A")} ${plugin.installedVersion}`
|
|
3012
|
+
);
|
|
3013
|
+
}
|
|
3014
|
+
if (plugin.version) {
|
|
3015
|
+
console.log(` ${theme.primary("\u5E02\u573A\u7248\u672C\uFF1A")} ${plugin.version}`);
|
|
3016
|
+
}
|
|
3017
|
+
if (plugin.hasUpdate) {
|
|
3018
|
+
console.log(` ${theme.warning("\u2B06\uFE0F \u6709\u65B0\u7248\u672C\u53EF\u7528")}`);
|
|
3019
|
+
}
|
|
3020
|
+
console.log(
|
|
3021
|
+
` ${theme.primary("\u542F\u7528\u72B6\u6001\uFF1A")} ${status === "enabled" ? theme.success("[\u5DF2\u542F\u7528]") : theme.warning("[\u5DF2\u7981\u7528]")}`
|
|
3022
|
+
);
|
|
3023
|
+
}
|
|
3024
|
+
console.log();
|
|
3025
|
+
console.log(theme.dim("\u2191\u2193 \u9009\u62E9 | Enter \u786E\u8BA4"));
|
|
3026
|
+
console.log();
|
|
3027
|
+
let actionChoices = [];
|
|
3028
|
+
if (status === "not_installed") {
|
|
3029
|
+
actionChoices = [
|
|
3030
|
+
{
|
|
3031
|
+
name: ` ${theme.success(">")} \u5B89\u88C5\u63D2\u4EF6`,
|
|
3032
|
+
value: "install"
|
|
3033
|
+
}
|
|
3034
|
+
];
|
|
3035
|
+
} else if (status === "enabled") {
|
|
3036
|
+
actionChoices = [
|
|
3037
|
+
...plugin.hasUpdate ? [
|
|
3038
|
+
{
|
|
3039
|
+
name: ` ${theme.info("\u2B06\uFE0F")} \u66F4\u65B0\u63D2\u4EF6`,
|
|
3040
|
+
value: "update"
|
|
3041
|
+
}
|
|
3042
|
+
] : [],
|
|
3043
|
+
{
|
|
3044
|
+
name: ` ${theme.warning(">")} \u7981\u7528\u63D2\u4EF6`,
|
|
3045
|
+
value: "disable"
|
|
3046
|
+
},
|
|
3047
|
+
{
|
|
3048
|
+
name: ` ${theme.error("*")} \u5378\u8F7D\u63D2\u4EF6`,
|
|
3049
|
+
value: "uninstall"
|
|
3050
|
+
}
|
|
3051
|
+
];
|
|
3052
|
+
} else if (status === "disabled") {
|
|
3053
|
+
actionChoices = [
|
|
3054
|
+
...plugin.hasUpdate ? [
|
|
3055
|
+
{
|
|
3056
|
+
name: ` ${theme.info("\u2B06\uFE0F")} \u66F4\u65B0\u63D2\u4EF6`,
|
|
3057
|
+
value: "update"
|
|
3058
|
+
}
|
|
3059
|
+
] : [],
|
|
3060
|
+
{
|
|
3061
|
+
name: ` ${theme.success(">")} \u542F\u7528\u63D2\u4EF6`,
|
|
3062
|
+
value: "enable"
|
|
3063
|
+
},
|
|
3064
|
+
{
|
|
3065
|
+
name: ` ${theme.error("*")} \u5378\u8F7D\u63D2\u4EF6`,
|
|
3066
|
+
value: "uninstall"
|
|
3067
|
+
}
|
|
3068
|
+
];
|
|
3069
|
+
}
|
|
3070
|
+
const choices = [
|
|
3071
|
+
...actionChoices,
|
|
3072
|
+
new inquirer6.Separator(),
|
|
3073
|
+
{
|
|
3074
|
+
name: ` ${theme.dim("<-")} \u8FD4\u56DE`,
|
|
3075
|
+
value: "back"
|
|
3076
|
+
},
|
|
3077
|
+
{
|
|
3078
|
+
name: ` ${theme.dim("X")} \u9000\u51FA`,
|
|
3079
|
+
value: "exit"
|
|
3080
|
+
}
|
|
3081
|
+
];
|
|
3082
|
+
const { action } = await inquirer6.prompt([
|
|
3083
|
+
{
|
|
3084
|
+
type: "list",
|
|
3085
|
+
name: "action",
|
|
3086
|
+
message: "\u8BF7\u9009\u62E9\u64CD\u4F5C\uFF1A",
|
|
3087
|
+
pageSize: 10,
|
|
3088
|
+
choices
|
|
3089
|
+
}
|
|
3090
|
+
]);
|
|
3091
|
+
if (action === "back") {
|
|
3092
|
+
return;
|
|
3093
|
+
}
|
|
3094
|
+
if (action === "exit") {
|
|
3095
|
+
process.exit(0);
|
|
3096
|
+
}
|
|
3097
|
+
await executePluginAction(action, plugin, pluginFullName);
|
|
3098
|
+
needRefresh = true;
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
3101
|
+
async function executePluginAction(action, plugin, pluginFullName) {
|
|
3102
|
+
const spinner = ora6();
|
|
3103
|
+
try {
|
|
3104
|
+
switch (action) {
|
|
3105
|
+
case "install":
|
|
3106
|
+
console.log();
|
|
3107
|
+
console.log(`$ claude plugin install ${pluginFullName}`);
|
|
3108
|
+
spinner.start(`${theme.success("+")} \u6B63\u5728\u5B89\u88C5\u63D2\u4EF6...`);
|
|
3109
|
+
execClaude(`plugin install ${pluginFullName}`);
|
|
3110
|
+
spinner.succeed(`${theme.success("+")} \u63D2\u4EF6\u5B89\u88C5\u6210\u529F`);
|
|
3111
|
+
break;
|
|
3112
|
+
case "enable":
|
|
3113
|
+
console.log();
|
|
3114
|
+
console.log(`$ claude plugin enable ${pluginFullName}`);
|
|
3115
|
+
spinner.start(`${theme.success("+")} \u6B63\u5728\u542F\u7528\u63D2\u4EF6...`);
|
|
3116
|
+
execClaude(`plugin enable ${pluginFullName}`);
|
|
3117
|
+
spinner.succeed(`${theme.success("+")} \u63D2\u4EF6\u5DF2\u542F\u7528`);
|
|
3118
|
+
break;
|
|
3119
|
+
case "disable":
|
|
3120
|
+
console.log();
|
|
3121
|
+
console.log(`$ claude plugin disable ${pluginFullName}`);
|
|
3122
|
+
spinner.start(`${theme.warning("*")} \u6B63\u5728\u7981\u7528\u63D2\u4EF6...`);
|
|
3123
|
+
execClaude(`plugin disable ${pluginFullName}`);
|
|
3124
|
+
spinner.succeed(`${theme.warning("*")} \u63D2\u4EF6\u5DF2\u7981\u7528`);
|
|
3125
|
+
break;
|
|
3126
|
+
case "update":
|
|
3127
|
+
console.log();
|
|
3128
|
+
console.log(`$ claude plugin update ${pluginFullName}`);
|
|
3129
|
+
spinner.start(`${theme.info("\u2B06\uFE0F")} \u6B63\u5728\u66F4\u65B0\u63D2\u4EF6...`);
|
|
3130
|
+
await marketplaceService.updatePlugin(pluginFullName);
|
|
3131
|
+
spinner.succeed(`${theme.success("\u2B06\uFE0F")} \u63D2\u4EF6\u5DF2\u66F4\u65B0`);
|
|
3132
|
+
break;
|
|
3133
|
+
case "uninstall": {
|
|
3134
|
+
console.log();
|
|
3135
|
+
const { confirm } = await inquirer6.prompt([
|
|
3136
|
+
{
|
|
3137
|
+
type: "confirm",
|
|
3138
|
+
name: "confirm",
|
|
3139
|
+
message: `\u786E\u5B9A\u8981\u5378\u8F7D\u63D2\u4EF6 "${plugin.name}" \u5417\uFF1F`,
|
|
3140
|
+
default: false
|
|
3141
|
+
}
|
|
3142
|
+
]);
|
|
3143
|
+
if (confirm) {
|
|
3144
|
+
console.log(`$ claude plugin uninstall ${pluginFullName}`);
|
|
3145
|
+
spinner.start(`${theme.error("-")} \u6B63\u5728\u5378\u8F7D\u63D2\u4EF6...`);
|
|
3146
|
+
let uninstalled = false;
|
|
3147
|
+
const scopes = ["user", "project", "local"];
|
|
3148
|
+
for (const scope of scopes) {
|
|
3149
|
+
try {
|
|
3150
|
+
execClaude(`plugin uninstall ${pluginFullName} --scope ${scope}`);
|
|
3151
|
+
uninstalled = true;
|
|
3152
|
+
break;
|
|
3153
|
+
} catch (e) {
|
|
3154
|
+
if (!e.message.includes("scope")) {
|
|
3155
|
+
throw e;
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
if (uninstalled) {
|
|
3160
|
+
spinner.succeed(`${theme.error("-")} \u63D2\u4EF6\u5DF2\u5378\u8F7D`);
|
|
3161
|
+
} else {
|
|
3162
|
+
spinner.fail("CLI \u5378\u8F7D\u5931\u8D25");
|
|
3163
|
+
console.log();
|
|
3164
|
+
showWarning("Claude CLI \u65E0\u6CD5\u5378\u8F7D\u6B64\u63D2\u4EF6\uFF08\u5DF2\u77E5 bug\uFF09");
|
|
3165
|
+
const { manualClean } = await inquirer6.prompt([
|
|
3166
|
+
{
|
|
3167
|
+
type: "confirm",
|
|
3168
|
+
name: "manualClean",
|
|
3169
|
+
message: "\u662F\u5426\u624B\u52A8\u6E05\u7406\u914D\u7F6E\u6587\u4EF6\u6765\u5B8C\u6210\u5378\u8F7D\uFF1F",
|
|
3170
|
+
default: true
|
|
3171
|
+
}
|
|
3172
|
+
]);
|
|
3173
|
+
if (manualClean) {
|
|
3174
|
+
try {
|
|
3175
|
+
const settingsPath = path8.join(
|
|
3176
|
+
os8.homedir(),
|
|
3177
|
+
".claude",
|
|
3178
|
+
"settings.json"
|
|
3179
|
+
);
|
|
3180
|
+
const settings = JSON.parse(
|
|
3181
|
+
fs7.readFileSync(settingsPath, "utf-8")
|
|
3182
|
+
);
|
|
3183
|
+
if (settings.enabledPlugins) {
|
|
3184
|
+
const keysToRemove = Object.keys(
|
|
3185
|
+
settings.enabledPlugins
|
|
3186
|
+
).filter((key) => key.startsWith(`${plugin.id}@`));
|
|
3187
|
+
for (const key of keysToRemove) {
|
|
3188
|
+
delete settings.enabledPlugins[key];
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
fs7.writeFileSync(
|
|
3192
|
+
settingsPath,
|
|
3193
|
+
JSON.stringify(settings, null, 2)
|
|
3194
|
+
);
|
|
3195
|
+
console.log(theme.success("\u2713 \u5DF2\u4ECE\u914D\u7F6E\u4E2D\u79FB\u9664\u63D2\u4EF6"));
|
|
3196
|
+
showInfo("\u91CD\u542F Claude Code \u540E\u751F\u6548");
|
|
3197
|
+
} catch (e) {
|
|
3198
|
+
showWarning("\u624B\u52A8\u6E05\u7406\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u7F16\u8F91 ~/.claude/settings.json");
|
|
3199
|
+
}
|
|
3200
|
+
} else {
|
|
3201
|
+
showInfo('\u5EFA\u8BAE\uFF1A\u4F7F\u7528"\u7981\u7528\u63D2\u4EF6"\u6765\u505C\u7528\u6B64\u63D2\u4EF6');
|
|
3202
|
+
}
|
|
3203
|
+
}
|
|
3204
|
+
}
|
|
3205
|
+
break;
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
} catch (error) {
|
|
3209
|
+
spinner.fail(`\u64CD\u4F5C\u5931\u8D25: ${error.message}`);
|
|
3210
|
+
}
|
|
3211
|
+
await new Promise((resolve) => {
|
|
3212
|
+
setTimeout(() => resolve(), 800);
|
|
3213
|
+
});
|
|
3214
|
+
}
|
|
3215
|
+
async function handleInstallAll(marketplacePlugins) {
|
|
3216
|
+
console.log();
|
|
3217
|
+
const recommendedPlugins = marketplacePlugins.filter((p) => p.recommended);
|
|
3218
|
+
const { confirm } = await inquirer6.prompt([
|
|
3219
|
+
{
|
|
3220
|
+
type: "confirm",
|
|
3221
|
+
name: "confirm",
|
|
3222
|
+
message: `\u786E\u5B9A\u8981\u5B89\u88C5\u5168\u90E8 ${recommendedPlugins.length} \u4E2A\u63A8\u8350\u63D2\u4EF6\u5417\uFF1F`,
|
|
3223
|
+
default: true
|
|
3224
|
+
}
|
|
3225
|
+
]);
|
|
3226
|
+
if (!confirm) return;
|
|
3227
|
+
const spinner = ora6("\u6B63\u5728\u5B89\u88C5\u63D2\u4EF6...").start();
|
|
3228
|
+
try {
|
|
3229
|
+
for (const plugin of recommendedPlugins) {
|
|
3230
|
+
const pluginFullName = `${plugin.id}@${plugin.marketplace}`;
|
|
3231
|
+
spinner.text = `\u6B63\u5728\u5B89\u88C5 ${plugin.name}...`;
|
|
3232
|
+
const installedPlugins = getInstalledPlugins();
|
|
3233
|
+
const installed = installedPlugins.find((p) => p.name === pluginFullName);
|
|
3234
|
+
if (installed) {
|
|
3235
|
+
if (!installed.enabled) {
|
|
3236
|
+
execClaude(`plugin enable ${pluginFullName}`);
|
|
3237
|
+
}
|
|
3238
|
+
} else {
|
|
3239
|
+
execClaude(`plugin install ${pluginFullName}`);
|
|
3240
|
+
}
|
|
3241
|
+
}
|
|
3242
|
+
spinner.succeed(`\u6210\u529F\u5B89\u88C5 ${recommendedPlugins.length} \u4E2A\u63A8\u8350\u63D2\u4EF6`);
|
|
3243
|
+
console.log();
|
|
3244
|
+
showInfo("\u63D2\u4EF6\u5DF2\u751F\u6548\uFF0C\u65E0\u9700\u91CD\u542F Claude Code");
|
|
3245
|
+
} catch (error) {
|
|
3246
|
+
spinner.fail(`\u5B89\u88C5\u5931\u8D25: ${error.message}`);
|
|
3247
|
+
}
|
|
3248
|
+
console.log();
|
|
3249
|
+
await inquirer6.prompt([
|
|
3250
|
+
{ type: "input", name: "continue", message: "\u6309\u56DE\u8F66\u952E\u7EE7\u7EED..." }
|
|
3251
|
+
]);
|
|
3252
|
+
}
|
|
3253
|
+
|
|
3254
|
+
// src/cli/menus/settings.ts
|
|
3255
|
+
import fs8 from "fs";
|
|
3256
|
+
import inquirer7 from "inquirer";
|
|
3257
|
+
import os9 from "os";
|
|
3258
|
+
import path9 from "path";
|
|
3259
|
+
var SETTINGS_FILE2 = path9.join(os9.homedir(), ".claude", "settings.json");
|
|
3260
|
+
var LANGUAGE_OPTIONS = [
|
|
3261
|
+
{ name: "\u4E2D\u6587 (chinese)", value: "chinese" },
|
|
3262
|
+
{ name: "English", value: "english" },
|
|
3263
|
+
{ name: "\u65E5\u672C\u8A9E (japanese)", value: "japanese" },
|
|
3264
|
+
{ name: "\uD55C\uAD6D\uC5B4 (korean)", value: "korean" },
|
|
3265
|
+
{ name: "Espa\xF1ol (spanish)", value: "spanish" },
|
|
3266
|
+
{ name: "Fran\xE7ais (french)", value: "french" },
|
|
3267
|
+
{ name: "Deutsch (german)", value: "german" },
|
|
3268
|
+
{ name: "\u5176\u4ED6\uFF08\u624B\u52A8\u8F93\u5165\uFF09", value: "__custom__" }
|
|
3269
|
+
];
|
|
3270
|
+
async function showSettingsMenu() {
|
|
3271
|
+
while (true) {
|
|
3272
|
+
showHeader();
|
|
3273
|
+
console.log(createBoxTitle("Claude Code \u914D\u7F6E"));
|
|
3274
|
+
console.log();
|
|
3275
|
+
const { choice } = await inquirer7.prompt([
|
|
3276
|
+
{
|
|
3277
|
+
type: "list",
|
|
3278
|
+
name: "choice",
|
|
3279
|
+
message: "\u8BF7\u9009\u62E9\u914D\u7F6E\u7C7B\u522B\uFF1A",
|
|
3280
|
+
choices: [
|
|
3281
|
+
{
|
|
3282
|
+
name: `\u57FA\u7840\u8BBE\u7F6E`,
|
|
3283
|
+
value: "basic" /* BASIC */
|
|
3284
|
+
},
|
|
3285
|
+
{
|
|
3286
|
+
name: `\u67E5\u770B\u914D\u7F6E\u6587\u4EF6`,
|
|
3287
|
+
value: "view_config" /* VIEW_CONFIG */
|
|
3288
|
+
},
|
|
3289
|
+
new inquirer7.Separator(),
|
|
3290
|
+
{
|
|
3291
|
+
name: `${theme.dim("<-")} \u8FD4\u56DE`,
|
|
3292
|
+
value: "back" /* BACK */
|
|
3293
|
+
},
|
|
3294
|
+
{
|
|
3295
|
+
name: `${theme.dim("X")} \u9000\u51FA`,
|
|
3296
|
+
value: "exit" /* EXIT */
|
|
3297
|
+
}
|
|
3298
|
+
]
|
|
3299
|
+
}
|
|
3300
|
+
]);
|
|
3301
|
+
switch (choice) {
|
|
3302
|
+
case "basic" /* BASIC */:
|
|
3303
|
+
await showBasicSettings();
|
|
3304
|
+
break;
|
|
3305
|
+
case "view_config" /* VIEW_CONFIG */:
|
|
3306
|
+
await handleViewConfig();
|
|
3307
|
+
break;
|
|
3308
|
+
case "back" /* BACK */:
|
|
3309
|
+
return;
|
|
3310
|
+
case "exit" /* EXIT */:
|
|
3311
|
+
process.exit(0);
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
}
|
|
3315
|
+
async function showBasicSettings() {
|
|
3316
|
+
while (true) {
|
|
3317
|
+
showHeader();
|
|
3318
|
+
console.log(createBoxTitle("\u57FA\u7840\u8BBE\u7F6E"));
|
|
3319
|
+
console.log();
|
|
3320
|
+
const settings = readClaudeSettings();
|
|
3321
|
+
const currentLanguage = settings.language || "\u672A\u8BBE\u7F6E";
|
|
3322
|
+
const currentCleanupDays = settings.cleanupPeriodDays ?? 30;
|
|
3323
|
+
const currentPlansDir = settings.plansDirectory || "./plans";
|
|
3324
|
+
const currentShowDuration = settings.showTurnDuration ?? false;
|
|
3325
|
+
const currentSpinnerTips = settings.spinnerTipsEnabled ?? true;
|
|
3326
|
+
const { choice } = await inquirer7.prompt([
|
|
3327
|
+
{
|
|
3328
|
+
type: "list",
|
|
3329
|
+
name: "choice",
|
|
3330
|
+
message: "\u8BF7\u9009\u62E9\u8981\u4FEE\u6539\u7684\u914D\u7F6E\uFF1A",
|
|
3331
|
+
choices: [
|
|
3332
|
+
{
|
|
3333
|
+
name: `\u54CD\u5E94\u8BED\u8A00 ${theme.dim(`[\u5F53\u524D: ${currentLanguage}]`)}`,
|
|
3334
|
+
value: "language" /* LANGUAGE */
|
|
3335
|
+
},
|
|
3336
|
+
{
|
|
3337
|
+
name: `\u4F1A\u8BDD\u6E05\u7406\u5468\u671F ${theme.dim(
|
|
3338
|
+
`[\u5F53\u524D: ${currentCleanupDays}\u5929]`
|
|
3339
|
+
)}`,
|
|
3340
|
+
value: "cleanupPeriodDays" /* CLEANUP_DAYS */
|
|
3341
|
+
},
|
|
3342
|
+
{
|
|
3343
|
+
name: `\u8BA1\u5212\u6587\u4EF6\u76EE\u5F55 ${theme.dim(`[\u5F53\u524D: ${currentPlansDir}]`)}`,
|
|
3344
|
+
value: "plansDirectory" /* PLANS_DIR */
|
|
3345
|
+
},
|
|
3346
|
+
{
|
|
3347
|
+
name: `\u663E\u793A\u54CD\u5E94\u8017\u65F6 ${theme.dim(
|
|
3348
|
+
`[\u5F53\u524D: ${currentShowDuration ? "\u5F00\u542F" : "\u5173\u95ED"}]`
|
|
3349
|
+
)}`,
|
|
3350
|
+
value: "showTurnDuration" /* SHOW_DURATION */
|
|
3351
|
+
},
|
|
3352
|
+
{
|
|
3353
|
+
name: `\u663E\u793A\u52A0\u8F7D\u63D0\u793A ${theme.dim(
|
|
3354
|
+
`[\u5F53\u524D: ${currentSpinnerTips ? "\u5F00\u542F" : "\u5173\u95ED"}]`
|
|
3355
|
+
)}`,
|
|
3356
|
+
value: "spinnerTipsEnabled" /* SPINNER_TIPS */
|
|
3357
|
+
},
|
|
3358
|
+
new inquirer7.Separator(),
|
|
3359
|
+
{
|
|
3360
|
+
name: `${theme.dim("<-")} \u8FD4\u56DE`,
|
|
3361
|
+
value: "back" /* BACK */
|
|
3362
|
+
},
|
|
3363
|
+
{
|
|
3364
|
+
name: `${theme.dim("X")} \u9000\u51FA`,
|
|
3365
|
+
value: "exit" /* EXIT */
|
|
3366
|
+
}
|
|
3367
|
+
]
|
|
3368
|
+
}
|
|
3369
|
+
]);
|
|
3370
|
+
switch (choice) {
|
|
3371
|
+
case "language" /* LANGUAGE */:
|
|
3372
|
+
await handleLanguageSetting();
|
|
3373
|
+
break;
|
|
3374
|
+
case "cleanupPeriodDays" /* CLEANUP_DAYS */:
|
|
3375
|
+
await handleCleanupDaysSetting();
|
|
3376
|
+
break;
|
|
3377
|
+
case "plansDirectory" /* PLANS_DIR */:
|
|
3378
|
+
await handlePlansDirSetting();
|
|
3379
|
+
break;
|
|
3380
|
+
case "showTurnDuration" /* SHOW_DURATION */:
|
|
3381
|
+
await handleBooleanSetting("showTurnDuration", "\u663E\u793A\u54CD\u5E94\u8017\u65F6");
|
|
3382
|
+
break;
|
|
3383
|
+
case "spinnerTipsEnabled" /* SPINNER_TIPS */:
|
|
3384
|
+
await handleBooleanSetting("spinnerTipsEnabled", "\u663E\u793A\u52A0\u8F7D\u63D0\u793A");
|
|
3385
|
+
break;
|
|
3386
|
+
case "back" /* BACK */:
|
|
3387
|
+
return;
|
|
3388
|
+
case "exit" /* EXIT */:
|
|
3389
|
+
process.exit(0);
|
|
3390
|
+
}
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
async function handleLanguageSetting() {
|
|
3394
|
+
console.log();
|
|
3395
|
+
const { language } = await inquirer7.prompt([
|
|
3396
|
+
{
|
|
3397
|
+
type: "list",
|
|
3398
|
+
name: "language",
|
|
3399
|
+
message: "\u8BF7\u9009\u62E9\u54CD\u5E94\u8BED\u8A00\uFF1A",
|
|
3400
|
+
choices: LANGUAGE_OPTIONS
|
|
3401
|
+
}
|
|
3402
|
+
]);
|
|
3403
|
+
let finalLanguage = language;
|
|
3404
|
+
if (language === "__custom__") {
|
|
3405
|
+
const { customLanguage } = await inquirer7.prompt([
|
|
3406
|
+
{
|
|
3407
|
+
type: "input",
|
|
3408
|
+
name: "customLanguage",
|
|
3409
|
+
message: "\u8BF7\u8F93\u5165\u8BED\u8A00\u540D\u79F0\uFF08\u82F1\u6587\uFF09\uFF1A",
|
|
3410
|
+
validate: (input) => {
|
|
3411
|
+
if (!input || input.trim().length === 0) {
|
|
3412
|
+
return "\u8BED\u8A00\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A";
|
|
3413
|
+
}
|
|
3414
|
+
return true;
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
]);
|
|
3418
|
+
finalLanguage = customLanguage;
|
|
3419
|
+
}
|
|
3420
|
+
try {
|
|
3421
|
+
const settings = readClaudeSettings();
|
|
3422
|
+
settings.language = finalLanguage;
|
|
3423
|
+
writeClaudeSettings(settings);
|
|
3424
|
+
showSuccess(`\u54CD\u5E94\u8BED\u8A00\u5DF2\u8BBE\u7F6E\u4E3A: ${finalLanguage}`);
|
|
3425
|
+
} catch (error) {
|
|
3426
|
+
showError(`\u4FDD\u5B58\u5931\u8D25: ${error.message}`);
|
|
3427
|
+
}
|
|
3428
|
+
}
|
|
3429
|
+
async function handleCleanupDaysSetting() {
|
|
3430
|
+
console.log();
|
|
3431
|
+
showInfo("\u8BBE\u7F6E\u4E3A 0 \u8868\u793A\u6BCF\u6B21\u542F\u52A8\u90FD\u6E05\u7406\u6240\u6709\u5386\u53F2\u4F1A\u8BDD");
|
|
3432
|
+
console.log();
|
|
3433
|
+
const { days } = await inquirer7.prompt([
|
|
3434
|
+
{
|
|
3435
|
+
type: "number",
|
|
3436
|
+
name: "days",
|
|
3437
|
+
message: "\u8BF7\u8F93\u5165\u4F1A\u8BDD\u6E05\u7406\u5468\u671F\uFF08\u5929\uFF09\uFF1A",
|
|
3438
|
+
default: 30,
|
|
3439
|
+
validate: (input) => {
|
|
3440
|
+
if (isNaN(input) || input < 0) {
|
|
3441
|
+
return "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u5929\u6570\uFF08>=0\uFF09";
|
|
3442
|
+
}
|
|
3443
|
+
return true;
|
|
3444
|
+
}
|
|
3445
|
+
}
|
|
3446
|
+
]);
|
|
3447
|
+
try {
|
|
3448
|
+
const settings = readClaudeSettings();
|
|
3449
|
+
settings.cleanupPeriodDays = days;
|
|
3450
|
+
writeClaudeSettings(settings);
|
|
3451
|
+
showSuccess(`\u4F1A\u8BDD\u6E05\u7406\u5468\u671F\u5DF2\u8BBE\u7F6E\u4E3A: ${days}\u5929`);
|
|
3452
|
+
} catch (error) {
|
|
3453
|
+
showError(`\u4FDD\u5B58\u5931\u8D25: ${error.message}`);
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3456
|
+
async function handlePlansDirSetting() {
|
|
3457
|
+
console.log();
|
|
3458
|
+
showInfo("\u53EF\u4F7F\u7528\u76F8\u5BF9\u8DEF\u5F84\uFF08\u76F8\u5BF9\u4E8E\u9879\u76EE\u6839\u76EE\u5F55\uFF09\u6216\u7EDD\u5BF9\u8DEF\u5F84");
|
|
3459
|
+
console.log();
|
|
3460
|
+
const { dir } = await inquirer7.prompt([
|
|
3461
|
+
{
|
|
3462
|
+
type: "input",
|
|
3463
|
+
name: "dir",
|
|
3464
|
+
message: "\u8BF7\u8F93\u5165\u8BA1\u5212\u6587\u4EF6\u76EE\u5F55\uFF1A",
|
|
3465
|
+
default: "./plans"
|
|
3466
|
+
}
|
|
3467
|
+
]);
|
|
3468
|
+
try {
|
|
3469
|
+
const settings = readClaudeSettings();
|
|
3470
|
+
settings.plansDirectory = dir;
|
|
3471
|
+
writeClaudeSettings(settings);
|
|
3472
|
+
showSuccess(`\u8BA1\u5212\u6587\u4EF6\u76EE\u5F55\u5DF2\u8BBE\u7F6E\u4E3A: ${dir}`);
|
|
3473
|
+
} catch (error) {
|
|
3474
|
+
showError(`\u4FDD\u5B58\u5931\u8D25: ${error.message}`);
|
|
3475
|
+
}
|
|
3476
|
+
}
|
|
3477
|
+
async function handleBooleanSetting(key, label) {
|
|
3478
|
+
console.log();
|
|
3479
|
+
const settings = readClaudeSettings();
|
|
3480
|
+
const currentValue = settings[key] ?? key === "spinnerTipsEnabled";
|
|
3481
|
+
const { value } = await inquirer7.prompt([
|
|
3482
|
+
{
|
|
3483
|
+
type: "list",
|
|
3484
|
+
name: "value",
|
|
3485
|
+
message: `${label}\uFF1A`,
|
|
3486
|
+
choices: [
|
|
3487
|
+
{ name: "\u5F00\u542F", value: true },
|
|
3488
|
+
{ name: "\u5173\u95ED", value: false }
|
|
3489
|
+
],
|
|
3490
|
+
default: currentValue
|
|
3491
|
+
}
|
|
3492
|
+
]);
|
|
3493
|
+
try {
|
|
3494
|
+
settings[key] = value;
|
|
3495
|
+
writeClaudeSettings(settings);
|
|
3496
|
+
showSuccess(`${label}\u5DF2${value ? "\u5F00\u542F" : "\u5173\u95ED"}`);
|
|
3497
|
+
} catch (error) {
|
|
3498
|
+
showError(`\u4FDD\u5B58\u5931\u8D25: ${error.message}`);
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
async function handleViewConfig() {
|
|
3502
|
+
console.log();
|
|
3503
|
+
console.log(theme.primary(`\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84: ${SETTINGS_FILE2}`));
|
|
3504
|
+
console.log();
|
|
3505
|
+
try {
|
|
3506
|
+
if (fs8.existsSync(SETTINGS_FILE2)) {
|
|
3507
|
+
const content = fs8.readFileSync(SETTINGS_FILE2, "utf-8");
|
|
3508
|
+
console.log(theme.dim("\u2500".repeat(50)));
|
|
3509
|
+
console.log(content);
|
|
3510
|
+
console.log(theme.dim("\u2500".repeat(50)));
|
|
3511
|
+
} else {
|
|
3512
|
+
showWarning("\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728");
|
|
3513
|
+
}
|
|
3514
|
+
} catch (error) {
|
|
3515
|
+
showError(`\u8BFB\u53D6\u5931\u8D25: ${error.message}`);
|
|
3516
|
+
}
|
|
3517
|
+
console.log();
|
|
3518
|
+
await inquirer7.prompt([
|
|
3519
|
+
{ type: "input", name: "continue", message: "\u6309\u56DE\u8F66\u952E\u7EE7\u7EED..." }
|
|
3520
|
+
]);
|
|
3521
|
+
}
|
|
3522
|
+
|
|
3523
|
+
// src/cli/menus/main.ts
|
|
3524
|
+
async function showMainMenu() {
|
|
3525
|
+
let claudeInfo = detectClaudeInstallation();
|
|
3526
|
+
while (true) {
|
|
3527
|
+
showHeader();
|
|
3528
|
+
const claudeStatus = formatClaudeStatus(claudeInfo);
|
|
3529
|
+
console.log(
|
|
3530
|
+
`Claude Code: ${claudeInfo.installed ? theme.success(claudeStatus) : theme.dim(claudeStatus)}`
|
|
3531
|
+
);
|
|
3532
|
+
const profileStatus = getCurrentProfileStatus();
|
|
3533
|
+
const apiKeyStatus = getApiKeyStatus();
|
|
3534
|
+
const baseUrlStatus = getBaseUrlStatus();
|
|
3535
|
+
console.log(`\u5F53\u524D\u914D\u7F6E: ${profileStatus}`);
|
|
3536
|
+
console.log(`Base URL: ${baseUrlStatus}`);
|
|
3537
|
+
console.log(`API Key: ${apiKeyStatus}
|
|
3538
|
+
`);
|
|
3539
|
+
console.log(createBoxTitle("\u4E3B\u83DC\u5355"));
|
|
3540
|
+
console.log();
|
|
3541
|
+
const { choice } = await inquirer9.prompt([
|
|
3542
|
+
{
|
|
3543
|
+
type: "list",
|
|
3544
|
+
name: "choice",
|
|
3545
|
+
message: "\u8BF7\u9009\u62E9\u64CD\u4F5C\uFF1A",
|
|
3546
|
+
pageSize: 10,
|
|
3547
|
+
choices: [
|
|
3548
|
+
{
|
|
3549
|
+
name: `${theme.primary("1.")} Claude Code \u7BA1\u7406 ${theme.dim(
|
|
3550
|
+
"(\u5B89\u88C5/\u5378\u8F7D)"
|
|
3551
|
+
)}`,
|
|
3552
|
+
value: "manage" /* MANAGE */
|
|
3553
|
+
},
|
|
3554
|
+
{
|
|
3555
|
+
name: `${theme.primary("2.")} \u6388\u6743\u767B\u5F55`,
|
|
3556
|
+
value: "auth" /* AUTH */
|
|
3557
|
+
},
|
|
3558
|
+
{
|
|
3559
|
+
name: `${theme.primary("3.")} Claude Code \u914D\u7F6E`,
|
|
3560
|
+
value: "settings" /* SETTINGS */
|
|
3561
|
+
},
|
|
3562
|
+
{
|
|
3563
|
+
name: `${theme.primary("4.")} MCP \u670D\u52A1\u7BA1\u7406`,
|
|
3564
|
+
value: "mcp" /* MCP */
|
|
3565
|
+
},
|
|
3566
|
+
{
|
|
3567
|
+
name: `${theme.primary("5.")} \u63D2\u4EF6\u5E02\u573A`,
|
|
3568
|
+
value: "plugins" /* PLUGINS */
|
|
3569
|
+
},
|
|
3570
|
+
{
|
|
3571
|
+
name: `${theme.primary("6.")} \u542F\u52A8 Web UI ${theme.dim(
|
|
3572
|
+
"(\u56FE\u5F62\u5316\u754C\u9762)"
|
|
3573
|
+
)}`,
|
|
3574
|
+
value: "webui" /* WEBUI */
|
|
3575
|
+
},
|
|
3576
|
+
new inquirer9.Separator(),
|
|
3577
|
+
{
|
|
3578
|
+
name: `${theme.dim("0.")} \u9000\u51FA`,
|
|
3579
|
+
value: "exit" /* EXIT */
|
|
3580
|
+
}
|
|
3581
|
+
]
|
|
3582
|
+
}
|
|
3583
|
+
]);
|
|
3584
|
+
switch (choice) {
|
|
3585
|
+
case "manage" /* MANAGE */:
|
|
3586
|
+
await showManageMenu();
|
|
3587
|
+
claudeInfo = detectClaudeInstallation();
|
|
3588
|
+
break;
|
|
3589
|
+
case "auth" /* AUTH */:
|
|
3590
|
+
await showAuthMenu();
|
|
3591
|
+
break;
|
|
3592
|
+
case "settings" /* SETTINGS */:
|
|
3593
|
+
await showSettingsMenu();
|
|
3594
|
+
break;
|
|
3595
|
+
case "mcp" /* MCP */:
|
|
3596
|
+
await showMcpMenu();
|
|
3597
|
+
break;
|
|
3598
|
+
case "plugins" /* PLUGINS */:
|
|
3599
|
+
await showPluginsMenu();
|
|
3600
|
+
break;
|
|
3601
|
+
case "webui" /* WEBUI */: {
|
|
3602
|
+
const { launchWebUI: launchWebUI2 } = (init_webui(), __toCommonJS(webui_exports));
|
|
3603
|
+
await launchWebUI2();
|
|
3604
|
+
break;
|
|
3605
|
+
}
|
|
3606
|
+
case "exit" /* EXIT */:
|
|
3607
|
+
console.log("\n\u518D\u89C1\uFF01");
|
|
3608
|
+
process.exit(0);
|
|
3609
|
+
}
|
|
3610
|
+
}
|
|
3611
|
+
}
|
|
3612
|
+
|
|
3613
|
+
// src/cli/index.ts
|
|
3614
|
+
init_version();
|
|
3615
|
+
var pkg2 = require_package();
|
|
3616
|
+
async function main() {
|
|
3617
|
+
try {
|
|
3618
|
+
const args = process.argv.slice(2);
|
|
3619
|
+
if (args.includes("--version") || args.includes("-v")) {
|
|
3620
|
+
console.log(`v${pkg2.version}`);
|
|
3621
|
+
process.exit(0);
|
|
3622
|
+
}
|
|
3623
|
+
if (args.includes("--test-update")) {
|
|
3624
|
+
const { testUpdate: testUpdate2 } = (init_version(), __toCommonJS(version_exports));
|
|
3625
|
+
await testUpdate2();
|
|
3626
|
+
process.exit(0);
|
|
3627
|
+
}
|
|
3628
|
+
if (args.includes("ui")) {
|
|
3629
|
+
const { launchWebUI: launchWebUI2 } = (init_webui(), __toCommonJS(webui_exports));
|
|
3630
|
+
await launchWebUI2();
|
|
3631
|
+
process.exit(0);
|
|
3632
|
+
}
|
|
3633
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
3634
|
+
console.log(`
|
|
3635
|
+
Code Helper v${pkg2.version}
|
|
3636
|
+
A unified tool to manage your Claude Code
|
|
3637
|
+
|
|
3638
|
+
Usage:
|
|
3639
|
+
code-helper Launch interactive menu
|
|
3640
|
+
code-helper ui Launch Web UI interface
|
|
3641
|
+
code-helper --version Show version number
|
|
3642
|
+
code-helper --help Show help information
|
|
3643
|
+
code-helper --test-update Test version update functionality
|
|
3644
|
+
|
|
3645
|
+
You can also use these command aliases:
|
|
3646
|
+
- ch
|
|
3647
|
+
- maco
|
|
3648
|
+
|
|
3649
|
+
Options:
|
|
3650
|
+
-v, --version Show version number
|
|
3651
|
+
-h, --help Show help information
|
|
3652
|
+
--test-update Test version update functionality
|
|
3653
|
+
ui Launch Web UI interface
|
|
3654
|
+
|
|
3655
|
+
Installation:
|
|
3656
|
+
Recommended global installation for better experience:
|
|
3657
|
+
npm install -g code-helper
|
|
3658
|
+
|
|
3659
|
+
Global installation will automatically check for updates
|
|
3660
|
+
|
|
3661
|
+
Web UI Launch:
|
|
3662
|
+
1. code-helper ui Launch via code-helper command
|
|
3663
|
+
2. npx @siteboon/claude-code-ui Launch UI directly
|
|
3664
|
+
`);
|
|
3665
|
+
process.exit(0);
|
|
3666
|
+
}
|
|
3667
|
+
await checkAndUpdate();
|
|
3668
|
+
showHeader();
|
|
3669
|
+
await showMainMenu();
|
|
3670
|
+
} catch (error) {
|
|
3671
|
+
if (error?.message === "User force closed the prompt") {
|
|
3672
|
+
console.log("\n\u518D\u89C1\uFF01");
|
|
3673
|
+
process.exit(0);
|
|
3674
|
+
}
|
|
3675
|
+
console.error("\u53D1\u751F\u9519\u8BEF:", error);
|
|
3676
|
+
process.exit(1);
|
|
3677
|
+
}
|
|
3678
|
+
}
|
|
3679
|
+
main();
|
|
3680
|
+
//# sourceMappingURL=index.mjs.map
|