@adversity/coding-tool-x 2.5.0 → 2.6.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/CHANGELOG.md +22 -0
- package/dist/web/assets/icons-CNM9_Fh0.js +1 -0
- package/dist/web/assets/index-BcmuQT-z.css +41 -0
- package/dist/web/assets/index-Ej0MPDUI.js +14 -0
- package/dist/web/index.html +3 -3
- package/package.json +4 -2
- package/src/commands/plugin.js +585 -0
- package/src/config/default.js +22 -3
- package/src/config/loader.js +6 -1
- package/src/index.js +229 -1
- package/src/server/api/config-export.js +122 -32
- package/src/server/api/dashboard.js +4 -3
- package/src/server/api/mcp.js +63 -0
- package/src/server/api/plugins.js +276 -0
- package/src/server/index.js +1 -0
- package/src/server/proxy-server.js +6 -3
- package/src/server/services/config-export-service.js +331 -5
- package/src/server/services/mcp-client.js +775 -0
- package/src/server/services/mcp-service.js +203 -0
- package/src/server/services/model-detector.js +350 -0
- package/src/server/services/plugins-service.js +177 -0
- package/src/server/services/pty-manager.js +65 -2
- package/src/server/services/speed-test.js +68 -37
- package/src/server/services/ui-config.js +2 -0
- package/src/server/utils/pricing.js +32 -1
- package/src/ui/menu.js +1 -0
- package/dist/web/assets/icons-BALJo7bE.js +0 -1
- package/dist/web/assets/index-CcYz-Mcz.css +0 -41
- package/dist/web/assets/index-k9b43kTe.js +0 -14
package/dist/web/index.html
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
<link rel="icon" href="/favicon.ico">
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
7
|
<title>CC-TOOL - ClaudeCode增强工作助手</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-Ej0MPDUI.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/vue-vendor-CEeI-Azr.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/vendors-CzcvkTIS.js">
|
|
11
|
-
<link rel="modulepreload" crossorigin href="/assets/icons-
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/icons-CNM9_Fh0.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/naive-ui-sh0u_0bf.js">
|
|
13
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
13
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BcmuQT-z.css">
|
|
14
14
|
</head>
|
|
15
15
|
<body>
|
|
16
16
|
<div id="app"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adversity/coding-tool-x",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"description": "Vibe Coding 增强工作助手 - 智能会话管理、动态渠道切换、全局搜索、实时监控",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"@lydell/node-pty": "^1.1.0",
|
|
50
50
|
"@xterm/headless": "^6.0.0",
|
|
51
51
|
"adm-zip": "^0.5.16",
|
|
52
|
+
"ajv": "^8.12.0",
|
|
52
53
|
"cc-tool-web": "file:src/web",
|
|
53
54
|
"chalk": "^4.1.2",
|
|
54
55
|
"express": "^4.18.2",
|
|
@@ -58,6 +59,7 @@
|
|
|
58
59
|
"open": "^8.4.2",
|
|
59
60
|
"ora": "^5.4.1",
|
|
60
61
|
"pm2": "^5.4.3",
|
|
62
|
+
"semver": "^7.6.0",
|
|
61
63
|
"toml": "^3.0.0",
|
|
62
64
|
"ws": "^8.18.3"
|
|
63
65
|
},
|
|
@@ -72,4 +74,4 @@
|
|
|
72
74
|
"url": "https://github.com/ZeaoZhang/coding-tool/issues"
|
|
73
75
|
},
|
|
74
76
|
"homepage": "https://github.com/ZeaoZhang/coding-tool#readme"
|
|
75
|
-
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,585 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const inquirer = require('inquirer');
|
|
3
|
+
const { installPlugin, uninstallPlugin, updatePlugin, updateAllPlugins } = require('../plugins/plugin-installer');
|
|
4
|
+
const { listPlugins, getPlugin, updatePlugin: updatePluginRegistry } = require('../plugins/registry');
|
|
5
|
+
const { createPluginContext } = require('../plugins/plugin-api');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { INSTALLED_DIR } = require('../plugins/constants');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Main plugin command handler
|
|
12
|
+
* @param {Array} args - Command line arguments
|
|
13
|
+
*/
|
|
14
|
+
async function handlePluginCommand(args) {
|
|
15
|
+
const [subcommand, ...subArgs] = args;
|
|
16
|
+
|
|
17
|
+
if (!subcommand) {
|
|
18
|
+
showHelp();
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
switch (subcommand) {
|
|
23
|
+
case 'install':
|
|
24
|
+
await handleInstall(subArgs);
|
|
25
|
+
break;
|
|
26
|
+
case 'remove':
|
|
27
|
+
case 'uninstall':
|
|
28
|
+
await handleRemove(subArgs);
|
|
29
|
+
break;
|
|
30
|
+
case 'list':
|
|
31
|
+
handleList(subArgs);
|
|
32
|
+
break;
|
|
33
|
+
case 'enable':
|
|
34
|
+
handleEnable(subArgs);
|
|
35
|
+
break;
|
|
36
|
+
case 'disable':
|
|
37
|
+
handleDisable(subArgs);
|
|
38
|
+
break;
|
|
39
|
+
case 'info':
|
|
40
|
+
handleInfo(subArgs);
|
|
41
|
+
break;
|
|
42
|
+
case 'config':
|
|
43
|
+
handleConfig(subArgs);
|
|
44
|
+
break;
|
|
45
|
+
case 'update':
|
|
46
|
+
await handleUpdate(subArgs);
|
|
47
|
+
break;
|
|
48
|
+
case 'help':
|
|
49
|
+
showHelp();
|
|
50
|
+
break;
|
|
51
|
+
default:
|
|
52
|
+
console.error(chalk.red(`\n❌ Unknown subcommand: ${subcommand}\n`));
|
|
53
|
+
showHelp();
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Handle plugin install command
|
|
60
|
+
* @param {Array} args - Subcommand arguments
|
|
61
|
+
*/
|
|
62
|
+
async function handleInstall(args) {
|
|
63
|
+
const url = args[0];
|
|
64
|
+
|
|
65
|
+
if (!url) {
|
|
66
|
+
console.error(chalk.red('\n❌ Git URL is required\n'));
|
|
67
|
+
console.log(chalk.gray('Usage: ctx plugin install <git-url>\n'));
|
|
68
|
+
console.log(chalk.gray('Example: ctx plugin install https://github.com/user/ctx-plugin.git\n'));
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log(chalk.cyan(`\n📦 Installing plugin from ${url}...\n`));
|
|
73
|
+
|
|
74
|
+
const result = await installPlugin(url);
|
|
75
|
+
|
|
76
|
+
if (result.success) {
|
|
77
|
+
console.log(chalk.green('\n✅ Plugin installed successfully!\n'));
|
|
78
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
79
|
+
console.log(chalk.cyan('Name: ') + chalk.white(result.plugin.name));
|
|
80
|
+
console.log(chalk.cyan('Version: ') + chalk.white(result.plugin.version));
|
|
81
|
+
console.log(chalk.cyan('Author: ') + chalk.white(result.plugin.author || 'N/A'));
|
|
82
|
+
console.log(chalk.cyan('Commands: ') + chalk.white(result.plugin.commands || 0));
|
|
83
|
+
console.log(chalk.cyan('Hooks: ') + chalk.white(result.plugin.hooks || 0));
|
|
84
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
85
|
+
console.log(chalk.gray(`\n💡 Run ${chalk.cyan(`ctx plugin info ${result.plugin.name}`)} for more details\n`));
|
|
86
|
+
} else {
|
|
87
|
+
console.error(chalk.red('\n❌ Installation failed:\n'));
|
|
88
|
+
console.error(chalk.red(result.error));
|
|
89
|
+
console.log();
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Handle plugin remove command
|
|
96
|
+
* @param {Array} args - Subcommand arguments
|
|
97
|
+
*/
|
|
98
|
+
async function handleRemove(args) {
|
|
99
|
+
const name = args[0];
|
|
100
|
+
|
|
101
|
+
if (!name) {
|
|
102
|
+
console.error(chalk.red('\n❌ Plugin name is required\n'));
|
|
103
|
+
console.log(chalk.gray('Usage: ctx plugin remove <name>\n'));
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Check if plugin exists
|
|
108
|
+
const plugin = getPlugin(name);
|
|
109
|
+
if (!plugin) {
|
|
110
|
+
console.error(chalk.red(`\n❌ Plugin "${name}" is not installed\n`));
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Confirm uninstall
|
|
115
|
+
const { confirmed } = await inquirer.prompt([
|
|
116
|
+
{
|
|
117
|
+
type: 'confirm',
|
|
118
|
+
name: 'confirmed',
|
|
119
|
+
message: `Are you sure you want to uninstall "${name}"?`,
|
|
120
|
+
default: false
|
|
121
|
+
}
|
|
122
|
+
]);
|
|
123
|
+
|
|
124
|
+
if (!confirmed) {
|
|
125
|
+
console.log(chalk.yellow('\n⚠️ Uninstall cancelled\n'));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log(chalk.cyan(`\n🗑️ Uninstalling plugin "${name}"...\n`));
|
|
130
|
+
|
|
131
|
+
const result = uninstallPlugin(name);
|
|
132
|
+
|
|
133
|
+
if (result.success) {
|
|
134
|
+
console.log(chalk.green(`\n✅ ${result.message}\n`));
|
|
135
|
+
} else {
|
|
136
|
+
console.error(chalk.red('\n❌ Uninstall failed:\n'));
|
|
137
|
+
console.error(chalk.red(result.error));
|
|
138
|
+
console.log();
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Handle plugin list command
|
|
145
|
+
* @param {Array} args - Subcommand arguments
|
|
146
|
+
*/
|
|
147
|
+
function handleList(args) {
|
|
148
|
+
const plugins = listPlugins();
|
|
149
|
+
|
|
150
|
+
if (plugins.length === 0) {
|
|
151
|
+
console.log(chalk.yellow('\n⚠️ No plugins installed\n'));
|
|
152
|
+
console.log(chalk.gray('💡 Install a plugin with: ') + chalk.cyan('ctx plugin install <git-url>\n'));
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
console.log(chalk.cyan(`\n📦 Installed Plugins (${plugins.length})\n`));
|
|
157
|
+
console.log(chalk.gray('═'.repeat(80)));
|
|
158
|
+
|
|
159
|
+
// Sort by name
|
|
160
|
+
plugins.sort((a, b) => a.name.localeCompare(b.name));
|
|
161
|
+
|
|
162
|
+
for (const plugin of plugins) {
|
|
163
|
+
const status = plugin.enabled
|
|
164
|
+
? chalk.green('✓ enabled ')
|
|
165
|
+
: chalk.gray('✗ disabled');
|
|
166
|
+
|
|
167
|
+
console.log('\n' + chalk.white.bold(plugin.name) + ' ' + chalk.gray(`v${plugin.version}`) + ' ' + status);
|
|
168
|
+
|
|
169
|
+
// Try to read manifest for description
|
|
170
|
+
const manifestPath = path.join(INSTALLED_DIR, plugin.name, 'plugin.json');
|
|
171
|
+
if (fs.existsSync(manifestPath)) {
|
|
172
|
+
try {
|
|
173
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
174
|
+
if (manifest.description) {
|
|
175
|
+
console.log(chalk.gray(' ' + manifest.description));
|
|
176
|
+
}
|
|
177
|
+
if (manifest.author) {
|
|
178
|
+
console.log(chalk.gray(` Author: ${manifest.author}`));
|
|
179
|
+
}
|
|
180
|
+
} catch (err) {
|
|
181
|
+
// Ignore read errors
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (plugin.source) {
|
|
186
|
+
console.log(chalk.gray(` Source: ${plugin.source}`));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
console.log(chalk.gray(` Installed: ${new Date(plugin.installedAt).toLocaleDateString()}`));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
console.log('\n' + chalk.gray('═'.repeat(80)));
|
|
193
|
+
console.log(chalk.gray(`\n💡 Use ${chalk.cyan('ctx plugin info <name>')} for detailed information\n`));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Handle plugin enable command
|
|
198
|
+
* @param {Array} args - Subcommand arguments
|
|
199
|
+
*/
|
|
200
|
+
function handleEnable(args) {
|
|
201
|
+
const name = args[0];
|
|
202
|
+
|
|
203
|
+
if (!name) {
|
|
204
|
+
console.error(chalk.red('\n❌ Plugin name is required\n'));
|
|
205
|
+
console.log(chalk.gray('Usage: ctx plugin enable <name>\n'));
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const plugin = getPlugin(name);
|
|
210
|
+
if (!plugin) {
|
|
211
|
+
console.error(chalk.red(`\n❌ Plugin "${name}" is not installed\n`));
|
|
212
|
+
process.exit(1);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (plugin.enabled) {
|
|
216
|
+
console.log(chalk.yellow(`\n⚠️ Plugin "${name}" is already enabled\n`));
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
updatePluginRegistry(name, { enabled: true });
|
|
222
|
+
console.log(chalk.green(`\n✅ Plugin "${name}" has been enabled\n`));
|
|
223
|
+
console.log(chalk.gray('💡 Restart ctx for changes to take effect\n'));
|
|
224
|
+
} catch (error) {
|
|
225
|
+
console.error(chalk.red(`\n❌ Failed to enable plugin: ${error.message}\n`));
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Handle plugin disable command
|
|
232
|
+
* @param {Array} args - Subcommand arguments
|
|
233
|
+
*/
|
|
234
|
+
function handleDisable(args) {
|
|
235
|
+
const name = args[0];
|
|
236
|
+
|
|
237
|
+
if (!name) {
|
|
238
|
+
console.error(chalk.red('\n❌ Plugin name is required\n'));
|
|
239
|
+
console.log(chalk.gray('Usage: ctx plugin disable <name>\n'));
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const plugin = getPlugin(name);
|
|
244
|
+
if (!plugin) {
|
|
245
|
+
console.error(chalk.red(`\n❌ Plugin "${name}" is not installed\n`));
|
|
246
|
+
process.exit(1);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (!plugin.enabled) {
|
|
250
|
+
console.log(chalk.yellow(`\n⚠️ Plugin "${name}" is already disabled\n`));
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
try {
|
|
255
|
+
updatePluginRegistry(name, { enabled: false });
|
|
256
|
+
console.log(chalk.green(`\n✅ Plugin "${name}" has been disabled\n`));
|
|
257
|
+
console.log(chalk.gray('💡 Restart ctx for changes to take effect\n'));
|
|
258
|
+
} catch (error) {
|
|
259
|
+
console.error(chalk.red(`\n❌ Failed to disable plugin: ${error.message}\n`));
|
|
260
|
+
process.exit(1);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Handle plugin info command
|
|
266
|
+
* @param {Array} args - Subcommand arguments
|
|
267
|
+
*/
|
|
268
|
+
function handleInfo(args) {
|
|
269
|
+
const name = args[0];
|
|
270
|
+
|
|
271
|
+
if (!name) {
|
|
272
|
+
console.error(chalk.red('\n❌ Plugin name is required\n'));
|
|
273
|
+
console.log(chalk.gray('Usage: ctx plugin info <name>\n'));
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const plugin = getPlugin(name);
|
|
278
|
+
if (!plugin) {
|
|
279
|
+
console.error(chalk.red(`\n❌ Plugin "${name}" is not installed\n`));
|
|
280
|
+
process.exit(1);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Read manifest
|
|
284
|
+
const manifestPath = path.join(INSTALLED_DIR, name, 'plugin.json');
|
|
285
|
+
if (!fs.existsSync(manifestPath)) {
|
|
286
|
+
console.error(chalk.red(`\n❌ Plugin manifest not found: ${manifestPath}\n`));
|
|
287
|
+
process.exit(1);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
let manifest;
|
|
291
|
+
try {
|
|
292
|
+
manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
293
|
+
} catch (error) {
|
|
294
|
+
console.error(chalk.red(`\n❌ Failed to read plugin manifest: ${error.message}\n`));
|
|
295
|
+
process.exit(1);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Display plugin information
|
|
299
|
+
console.log(chalk.cyan(`\n📦 Plugin Information: ${name}\n`));
|
|
300
|
+
console.log(chalk.gray('═'.repeat(80)));
|
|
301
|
+
|
|
302
|
+
console.log(chalk.white('\nBasic Info:'));
|
|
303
|
+
console.log(chalk.cyan(' Name: ') + chalk.white(manifest.name));
|
|
304
|
+
console.log(chalk.cyan(' Version: ') + chalk.white(manifest.version));
|
|
305
|
+
console.log(chalk.cyan(' Description: ') + chalk.white(manifest.description || 'N/A'));
|
|
306
|
+
console.log(chalk.cyan(' Author: ') + chalk.white(manifest.author || 'N/A'));
|
|
307
|
+
console.log(chalk.cyan(' License: ') + chalk.white(manifest.license || 'N/A'));
|
|
308
|
+
|
|
309
|
+
console.log(chalk.white('\nStatus:'));
|
|
310
|
+
console.log(chalk.cyan(' Enabled: ') + (plugin.enabled ? chalk.green('Yes') : chalk.gray('No')));
|
|
311
|
+
console.log(chalk.cyan(' Load Order: ') + chalk.white(plugin.loadOrder || 10));
|
|
312
|
+
console.log(chalk.cyan(' Installed: ') + chalk.white(new Date(plugin.installedAt).toLocaleString()));
|
|
313
|
+
|
|
314
|
+
if (plugin.updatedAt) {
|
|
315
|
+
console.log(chalk.cyan(' Updated: ') + chalk.white(new Date(plugin.updatedAt).toLocaleString()));
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (plugin.source) {
|
|
319
|
+
console.log(chalk.cyan(' Source: ') + chalk.white(plugin.source));
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (manifest.minVersion) {
|
|
323
|
+
console.log(chalk.cyan(' Min Version: ') + chalk.white(manifest.minVersion));
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Commands
|
|
327
|
+
if (manifest.commands && manifest.commands.length > 0) {
|
|
328
|
+
console.log(chalk.white('\nCommands:'));
|
|
329
|
+
manifest.commands.forEach(cmd => {
|
|
330
|
+
const cmdName = typeof cmd === 'string' ? cmd : cmd.name;
|
|
331
|
+
const cmdDesc = typeof cmd === 'object' ? cmd.description : '';
|
|
332
|
+
console.log(chalk.cyan(' • ') + chalk.white(cmdName) + (cmdDesc ? chalk.gray(` - ${cmdDesc}`) : ''));
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Hooks
|
|
337
|
+
if (manifest.hooks && manifest.hooks.length > 0) {
|
|
338
|
+
console.log(chalk.white('\nHooks:'));
|
|
339
|
+
manifest.hooks.forEach(hook => {
|
|
340
|
+
console.log(chalk.cyan(' • ') + chalk.white(hook));
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Configuration
|
|
345
|
+
const configFile = path.join(require('../plugins/constants').CONFIG_DIR, `${name}.json`);
|
|
346
|
+
if (fs.existsSync(configFile)) {
|
|
347
|
+
try {
|
|
348
|
+
const config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
|
|
349
|
+
const keys = Object.keys(config);
|
|
350
|
+
if (keys.length > 0) {
|
|
351
|
+
console.log(chalk.white('\nConfiguration:'));
|
|
352
|
+
keys.forEach(key => {
|
|
353
|
+
const value = config[key];
|
|
354
|
+
const displayValue = typeof value === 'object' ? JSON.stringify(value) : value;
|
|
355
|
+
console.log(chalk.cyan(` ${key}: `) + chalk.white(displayValue));
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
} catch (err) {
|
|
359
|
+
// Ignore read errors
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
console.log('\n' + chalk.gray('═'.repeat(80)) + '\n');
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Handle plugin config command
|
|
368
|
+
* @param {Array} args - Subcommand arguments
|
|
369
|
+
*/
|
|
370
|
+
function handleConfig(args) {
|
|
371
|
+
const [name, key, ...valueParts] = args;
|
|
372
|
+
|
|
373
|
+
if (!name) {
|
|
374
|
+
console.error(chalk.red('\n❌ Plugin name is required\n'));
|
|
375
|
+
console.log(chalk.gray('Usage: ctx plugin config <name> [key] [value]\n'));
|
|
376
|
+
console.log(chalk.gray('Examples:\n'));
|
|
377
|
+
console.log(chalk.gray(' ctx plugin config my-plugin # View all config'));
|
|
378
|
+
console.log(chalk.gray(' ctx plugin config my-plugin apiKey # View specific key'));
|
|
379
|
+
console.log(chalk.gray(' ctx plugin config my-plugin apiKey xyz # Set key\n'));
|
|
380
|
+
process.exit(1);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const plugin = getPlugin(name);
|
|
384
|
+
if (!plugin) {
|
|
385
|
+
console.error(chalk.red(`\n❌ Plugin "${name}" is not installed\n`));
|
|
386
|
+
process.exit(1);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const configFile = path.join(require('../plugins/constants').CONFIG_DIR, `${name}.json`);
|
|
390
|
+
|
|
391
|
+
// Ensure config directory exists
|
|
392
|
+
const configDir = path.dirname(configFile);
|
|
393
|
+
if (!fs.existsSync(configDir)) {
|
|
394
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Load existing config
|
|
398
|
+
let config = {};
|
|
399
|
+
if (fs.existsSync(configFile)) {
|
|
400
|
+
try {
|
|
401
|
+
config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
|
|
402
|
+
} catch (error) {
|
|
403
|
+
console.error(chalk.red(`\n❌ Failed to read config: ${error.message}\n`));
|
|
404
|
+
process.exit(1);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// No key provided - view all config
|
|
409
|
+
if (!key) {
|
|
410
|
+
console.log(chalk.cyan(`\n⚙️ Configuration for "${name}"\n`));
|
|
411
|
+
console.log(chalk.gray('═'.repeat(80)));
|
|
412
|
+
|
|
413
|
+
const keys = Object.keys(config);
|
|
414
|
+
if (keys.length === 0) {
|
|
415
|
+
console.log(chalk.gray('\n No configuration set\n'));
|
|
416
|
+
} else {
|
|
417
|
+
console.log();
|
|
418
|
+
keys.forEach(k => {
|
|
419
|
+
const value = config[k];
|
|
420
|
+
const displayValue = typeof value === 'object'
|
|
421
|
+
? JSON.stringify(value, null, 2).split('\n').map((line, i) => i === 0 ? line : ' ' + line).join('\n')
|
|
422
|
+
: value;
|
|
423
|
+
console.log(chalk.cyan(` ${k}: `) + chalk.white(displayValue));
|
|
424
|
+
});
|
|
425
|
+
console.log();
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
console.log(chalk.gray('═'.repeat(80)) + '\n');
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// No value provided - view specific key
|
|
433
|
+
if (valueParts.length === 0) {
|
|
434
|
+
if (config[key] === undefined) {
|
|
435
|
+
console.log(chalk.yellow(`\n⚠️ Key "${key}" is not set\n`));
|
|
436
|
+
} else {
|
|
437
|
+
console.log(chalk.cyan(`\n⚙️ ${key}: `) + chalk.white(JSON.stringify(config[key], null, 2)) + '\n');
|
|
438
|
+
}
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// Set value
|
|
443
|
+
const value = valueParts.join(' ');
|
|
444
|
+
|
|
445
|
+
// Try to parse as JSON if it looks like JSON
|
|
446
|
+
let parsedValue = value;
|
|
447
|
+
if (value.startsWith('{') || value.startsWith('[') || value === 'true' || value === 'false' || !isNaN(value)) {
|
|
448
|
+
try {
|
|
449
|
+
parsedValue = JSON.parse(value);
|
|
450
|
+
} catch (err) {
|
|
451
|
+
// Keep as string if not valid JSON
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
config[key] = parsedValue;
|
|
456
|
+
|
|
457
|
+
try {
|
|
458
|
+
fs.writeFileSync(configFile, JSON.stringify(config, null, 2), 'utf8');
|
|
459
|
+
console.log(chalk.green(`\n✅ Configuration updated: ${key} = ${JSON.stringify(parsedValue)}\n`));
|
|
460
|
+
} catch (error) {
|
|
461
|
+
console.error(chalk.red(`\n❌ Failed to save config: ${error.message}\n`));
|
|
462
|
+
process.exit(1);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Handle plugin update command
|
|
468
|
+
* @param {Array} args - Subcommand arguments
|
|
469
|
+
*/
|
|
470
|
+
async function handleUpdate(args) {
|
|
471
|
+
const name = args[0];
|
|
472
|
+
const isUpdateAll = name === '--all' || name === '-a';
|
|
473
|
+
|
|
474
|
+
if (!name) {
|
|
475
|
+
console.error(chalk.red('\n❌ Plugin name is required\n'));
|
|
476
|
+
console.log(chalk.gray('Usage: ctx plugin update <name>\n'));
|
|
477
|
+
console.log(chalk.gray(' ctx plugin update --all\n'));
|
|
478
|
+
process.exit(1);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (isUpdateAll) {
|
|
482
|
+
console.log(chalk.cyan('\n🔄 Updating all plugins...\n'));
|
|
483
|
+
|
|
484
|
+
const result = await updateAllPlugins();
|
|
485
|
+
|
|
486
|
+
console.log(chalk.gray('═'.repeat(80)));
|
|
487
|
+
|
|
488
|
+
for (const pluginResult of result.results) {
|
|
489
|
+
if (pluginResult.success) {
|
|
490
|
+
if (pluginResult.plugin && pluginResult.plugin.updated) {
|
|
491
|
+
console.log(chalk.green(`✓ ${pluginResult.name}: `) +
|
|
492
|
+
chalk.gray(`v${pluginResult.plugin.oldVersion}`) +
|
|
493
|
+
chalk.white(' → ') +
|
|
494
|
+
chalk.green(`v${pluginResult.plugin.newVersion}`));
|
|
495
|
+
} else {
|
|
496
|
+
console.log(chalk.gray(`○ ${pluginResult.name}: up to date`));
|
|
497
|
+
}
|
|
498
|
+
} else {
|
|
499
|
+
console.log(chalk.red(`✗ ${pluginResult.name}: ${pluginResult.error}`));
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
console.log(chalk.gray('═'.repeat(80)));
|
|
504
|
+
console.log(chalk.white('\nSummary:'));
|
|
505
|
+
console.log(chalk.green(` Updated: ${result.summary.updated}`));
|
|
506
|
+
console.log(chalk.gray(` Unchanged: ${result.summary.unchanged}`));
|
|
507
|
+
if (result.summary.failed > 0) {
|
|
508
|
+
console.log(chalk.red(` Failed: ${result.summary.failed}`));
|
|
509
|
+
}
|
|
510
|
+
console.log();
|
|
511
|
+
|
|
512
|
+
if (!result.success) {
|
|
513
|
+
process.exit(1);
|
|
514
|
+
}
|
|
515
|
+
} else {
|
|
516
|
+
// Update single plugin
|
|
517
|
+
const plugin = getPlugin(name);
|
|
518
|
+
if (!plugin) {
|
|
519
|
+
console.error(chalk.red(`\n❌ Plugin "${name}" is not installed\n`));
|
|
520
|
+
process.exit(1);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
console.log(chalk.cyan(`\n🔄 Updating plugin "${name}"...\n`));
|
|
524
|
+
|
|
525
|
+
const result = await updatePlugin(name);
|
|
526
|
+
|
|
527
|
+
if (result.success) {
|
|
528
|
+
console.log(chalk.green(`\n✅ ${result.message}\n`));
|
|
529
|
+
if (result.plugin && result.plugin.updated) {
|
|
530
|
+
console.log(chalk.gray(` ${result.plugin.oldVersion} → ${result.plugin.newVersion}\n`));
|
|
531
|
+
}
|
|
532
|
+
} else {
|
|
533
|
+
console.error(chalk.red('\n❌ Update failed:\n'));
|
|
534
|
+
console.error(chalk.red(result.error));
|
|
535
|
+
console.log();
|
|
536
|
+
process.exit(1);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Show plugin command help
|
|
543
|
+
*/
|
|
544
|
+
function showHelp() {
|
|
545
|
+
console.log(chalk.cyan('\n📦 Plugin Management Commands\n'));
|
|
546
|
+
console.log(chalk.gray('═'.repeat(80)));
|
|
547
|
+
|
|
548
|
+
console.log(chalk.white('\nUsage: ') + chalk.cyan('ctx plugin <subcommand> [options]\n'));
|
|
549
|
+
|
|
550
|
+
console.log(chalk.white('Subcommands:\n'));
|
|
551
|
+
|
|
552
|
+
const commands = [
|
|
553
|
+
['install <url>', 'Install plugin from Git URL'],
|
|
554
|
+
['remove <name>', 'Uninstall plugin'],
|
|
555
|
+
['list', 'List installed plugins with status'],
|
|
556
|
+
['enable <name>', 'Enable disabled plugin'],
|
|
557
|
+
['disable <name>', 'Disable plugin without removing'],
|
|
558
|
+
['info <name>', 'Show plugin manifest, config, and hooks'],
|
|
559
|
+
['config <name> [key] [value]', 'View or set plugin configuration'],
|
|
560
|
+
['update <name>', 'Update plugin to latest version'],
|
|
561
|
+
['update --all', 'Update all installed plugins'],
|
|
562
|
+
['help', 'Show this help message']
|
|
563
|
+
];
|
|
564
|
+
|
|
565
|
+
commands.forEach(([cmd, desc]) => {
|
|
566
|
+
console.log(' ' + chalk.cyan(cmd.padEnd(30)) + chalk.gray(desc));
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
console.log('\n' + chalk.white('Examples:\n'));
|
|
570
|
+
|
|
571
|
+
console.log(chalk.gray(' ctx plugin install https://github.com/user/ctx-plugin.git'));
|
|
572
|
+
console.log(chalk.gray(' ctx plugin list'));
|
|
573
|
+
console.log(chalk.gray(' ctx plugin info my-plugin'));
|
|
574
|
+
console.log(chalk.gray(' ctx plugin config my-plugin apiKey abc123'));
|
|
575
|
+
console.log(chalk.gray(' ctx plugin update my-plugin'));
|
|
576
|
+
console.log(chalk.gray(' ctx plugin update --all'));
|
|
577
|
+
console.log(chalk.gray(' ctx plugin disable my-plugin'));
|
|
578
|
+
console.log(chalk.gray(' ctx plugin remove my-plugin\n'));
|
|
579
|
+
|
|
580
|
+
console.log(chalk.gray('═'.repeat(80)) + '\n');
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
module.exports = {
|
|
584
|
+
handlePluginCommand
|
|
585
|
+
};
|
package/src/config/default.js
CHANGED
|
@@ -22,17 +22,36 @@ const DEFAULT_CONFIG = {
|
|
|
22
22
|
input: 3,
|
|
23
23
|
output: 15,
|
|
24
24
|
cacheCreation: 3.75,
|
|
25
|
-
cacheRead: 0.30
|
|
25
|
+
cacheRead: 0.30,
|
|
26
|
+
models: {
|
|
27
|
+
'claude-sonnet-4-20250514': { mode: 'auto' },
|
|
28
|
+
'claude-haiku-3-5-20241022': {
|
|
29
|
+
mode: 'custom',
|
|
30
|
+
input: 0.8,
|
|
31
|
+
output: 4,
|
|
32
|
+
cacheCreation: 1,
|
|
33
|
+
cacheRead: 0.08
|
|
34
|
+
},
|
|
35
|
+
'claude-opus-4-20250514': { mode: 'auto' }
|
|
36
|
+
}
|
|
26
37
|
},
|
|
27
38
|
codex: {
|
|
28
39
|
mode: 'auto',
|
|
29
40
|
input: 2.5,
|
|
30
|
-
output: 10
|
|
41
|
+
output: 10,
|
|
42
|
+
models: {
|
|
43
|
+
'gpt-5-codex': { mode: 'auto' },
|
|
44
|
+
'gpt-4o-mini': { mode: 'auto' }
|
|
45
|
+
}
|
|
31
46
|
},
|
|
32
47
|
gemini: {
|
|
33
48
|
mode: 'auto',
|
|
34
49
|
input: 1.25,
|
|
35
|
-
output: 5
|
|
50
|
+
output: 5,
|
|
51
|
+
models: {
|
|
52
|
+
'gemini-2.5-pro': { mode: 'auto' },
|
|
53
|
+
'gemini-2.5-flash': { mode: 'auto' }
|
|
54
|
+
}
|
|
36
55
|
}
|
|
37
56
|
}
|
|
38
57
|
};
|
package/src/config/loader.js
CHANGED
|
@@ -3,6 +3,7 @@ const fs = require('fs');
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const os = require('os');
|
|
5
5
|
const DEFAULT_CONFIG = require('./default');
|
|
6
|
+
const eventBus = require('../plugins/event-bus');
|
|
6
7
|
|
|
7
8
|
const CONFIG_FILE = path.join(__dirname, '../../config.json');
|
|
8
9
|
|
|
@@ -49,12 +50,15 @@ function loadConfig() {
|
|
|
49
50
|
config.currentProject = config.defaultProject;
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
eventBus.emitSync('config:loaded', { config });
|
|
52
54
|
return config;
|
|
53
55
|
}
|
|
54
56
|
} catch (error) {
|
|
55
57
|
console.error('加载配置文件失败,使用默认配置');
|
|
56
58
|
}
|
|
57
|
-
|
|
59
|
+
const defaultConfig = { ...DEFAULT_CONFIG, currentProject: DEFAULT_CONFIG.defaultProject };
|
|
60
|
+
eventBus.emitSync('config:loaded', { config: defaultConfig });
|
|
61
|
+
return defaultConfig;
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
/**
|
|
@@ -63,6 +67,7 @@ function loadConfig() {
|
|
|
63
67
|
function saveConfig(config) {
|
|
64
68
|
try {
|
|
65
69
|
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
70
|
+
eventBus.emitSync('config:saved', { config });
|
|
66
71
|
} catch (error) {
|
|
67
72
|
console.error('保存配置失败:', error.message);
|
|
68
73
|
}
|