@ia-ccun/code-agent-cli 0.0.16 → 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +109 -93
- package/config/agent/extensions/review.ts +3 -0
- package/config/agent/models.json +13 -39
- package/package.json +1 -4
- package/scripts/postinstall.js +135 -11
package/bin/cli.js
CHANGED
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* aicode-cli
|
|
5
|
-
* AI Coding Agent CLI
|
|
5
|
+
* AI Coding Agent CLI
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
// 设置终端标题
|
|
9
|
+
process.title = 'aicode-cli';
|
|
10
|
+
|
|
11
|
+
import { spawn } from 'child_process';
|
|
9
12
|
import { fileURLToPath } from 'url';
|
|
10
13
|
import { dirname, join } from 'path';
|
|
11
|
-
import { existsSync, readFileSync } from 'fs';
|
|
14
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
12
15
|
import { getConfig, getConfigDir, loadConfig } from '../dist/config-loader.js';
|
|
13
16
|
import { generateBanner } from '../dist/banner.js';
|
|
14
17
|
|
|
@@ -19,30 +22,30 @@ const __dirname = dirname(__filename);
|
|
|
19
22
|
function parseArgs() {
|
|
20
23
|
const args = process.argv.slice(2);
|
|
21
24
|
let configDir;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
|
|
26
|
+
// 处理 --version 参数,显示 CLI 自己的版本
|
|
27
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
28
|
+
const pkgPath = join(__dirname, '..', 'package.json');
|
|
29
|
+
if (existsSync(pkgPath)) {
|
|
30
|
+
const { version } = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
31
|
+
console.log(version);
|
|
32
|
+
}
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
|
|
25
36
|
for (let i = 0; i < args.length; i++) {
|
|
26
37
|
const arg = args[i];
|
|
27
38
|
if (arg === '--config' || arg === '-c') {
|
|
28
39
|
configDir = args[i + 1];
|
|
29
40
|
args.splice(i, 2);
|
|
30
|
-
|
|
31
|
-
} else if (arg === '--web' || arg === '--webui') {
|
|
32
|
-
webMode = true;
|
|
33
|
-
args.splice(i, 1);
|
|
34
|
-
i--;
|
|
35
|
-
} else if (arg === '--port' || arg === '-p') {
|
|
36
|
-
webPort = parseInt(args[i + 1], 10);
|
|
37
|
-
args.splice(i, 2);
|
|
38
|
-
i--;
|
|
41
|
+
break;
|
|
39
42
|
}
|
|
40
43
|
}
|
|
41
|
-
|
|
42
|
-
return { remainingArgs: args, configDir
|
|
44
|
+
|
|
45
|
+
return { remainingArgs: args, configDir };
|
|
43
46
|
}
|
|
44
47
|
|
|
45
|
-
const { remainingArgs, configDir: cliConfigDir
|
|
48
|
+
const { remainingArgs, configDir: cliConfigDir } = parseArgs();
|
|
46
49
|
|
|
47
50
|
// 优先使用命令行参数,其次使用环境变量,最后使用默认值
|
|
48
51
|
const effectiveConfigDir = cliConfigDir || process.env.AICODE_CLI_CONFIG_DIR;
|
|
@@ -57,91 +60,106 @@ if (effectiveConfigDir) {
|
|
|
57
60
|
config = getConfig();
|
|
58
61
|
}
|
|
59
62
|
|
|
63
|
+
// ============================================================================
|
|
64
|
+
// 替换 models.json 中的 ${CLI_VERSION} 占位符
|
|
65
|
+
// ============================================================================
|
|
66
|
+
function replaceCLIVersionPlaceholder() {
|
|
67
|
+
try {
|
|
68
|
+
// 找到项目根目录的 package.json
|
|
69
|
+
let rootDir = __dirname;
|
|
70
|
+
while (rootDir !== dirname(rootDir)) {
|
|
71
|
+
const pkgPath = join(rootDir, 'package.json');
|
|
72
|
+
if (existsSync(pkgPath)) {
|
|
73
|
+
const { version } = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
74
|
+
const versionString = `aicode-cli/${version}`;
|
|
75
|
+
|
|
76
|
+
// 查找配置目录下的 models.json
|
|
77
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE;
|
|
78
|
+
const configDirs = [
|
|
79
|
+
join(homeDir, '.aicode-cli', 'agent'),
|
|
80
|
+
join(homeDir, '.pi', 'agent'),
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
for (const dir of configDirs) {
|
|
84
|
+
const modelsPath = join(dir, 'models.json');
|
|
85
|
+
if (!existsSync(modelsPath)) continue;
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
let content = readFileSync(modelsPath, 'utf-8');
|
|
89
|
+
const config = JSON.parse(content);
|
|
90
|
+
|
|
91
|
+
// 遍历所有 provider,替换 headers.User-Agent 中的占位符
|
|
92
|
+
for (const provider of Object.values(config.providers || {})) {
|
|
93
|
+
if (provider.headers && provider.headers['User-Agent']) {
|
|
94
|
+
const ua = provider.headers['User-Agent'];
|
|
95
|
+
if (ua && ua.startsWith('${') && ua.endsWith('}')) {
|
|
96
|
+
provider.headers['User-Agent'] = versionString;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const newContent = JSON.stringify(config, null, 2);
|
|
102
|
+
if (newContent !== content) {
|
|
103
|
+
writeFileSync(modelsPath, newContent, 'utf-8');
|
|
104
|
+
}
|
|
105
|
+
} catch (e) {
|
|
106
|
+
// 忽略错误
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
rootDir = dirname(rootDir);
|
|
112
|
+
}
|
|
113
|
+
} catch (e) {
|
|
114
|
+
// 忽略错误
|
|
115
|
+
}
|
|
116
|
+
}
|
|
60
117
|
|
|
118
|
+
// 执行占位符替换
|
|
119
|
+
replaceCLIVersionPlaceholder();
|
|
61
120
|
|
|
62
121
|
// 显示 Banner
|
|
63
122
|
if (config.banner === '__default__' || config.banner !== '') {
|
|
64
123
|
console.log(generateBanner(config.themeColor));
|
|
65
124
|
}
|
|
66
125
|
|
|
67
|
-
//
|
|
68
|
-
function checkForUpdates() {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
// 比较版本号
|
|
80
|
-
const localParts = localVersion.split('.').map(Number);
|
|
81
|
-
const latestParts = latestVersion.split('.').map(Number);
|
|
82
|
-
const needsUpdate = latestParts[0] > localParts[0] ||
|
|
83
|
-
(latestParts[0] === localParts[0] && latestParts[1] > localParts[1]) ||
|
|
84
|
-
(latestParts[0] === localParts[0] && latestParts[1] === localParts[1] && latestParts[2] > localParts[2]);
|
|
85
|
-
|
|
86
|
-
if (needsUpdate) {
|
|
87
|
-
console.log('\n\x1b[33m⚠️ 发现新版本 v%s → v%s\x1b[0m', localVersion, latestVersion);
|
|
88
|
-
console.log('\x1b[36m 运行以下命令更新:\x1b[0m');
|
|
89
|
-
console.log(' npm install -g @nucc/code-agent-cli\n');
|
|
126
|
+
// 检查新版本
|
|
127
|
+
async function checkForUpdates() {
|
|
128
|
+
try {
|
|
129
|
+
const { execSync } = await import('child_process');
|
|
130
|
+
// 读取本地版本 - 向上查找 package.json
|
|
131
|
+
let rootDir = __dirname;
|
|
132
|
+
let localVersion = null;
|
|
133
|
+
while (rootDir !== dirname(rootDir)) {
|
|
134
|
+
const pkgPath = join(rootDir, 'package.json');
|
|
135
|
+
if (existsSync(pkgPath)) {
|
|
136
|
+
localVersion = JSON.parse(readFileSync(pkgPath, 'utf-8')).version;
|
|
137
|
+
break;
|
|
90
138
|
}
|
|
91
|
-
|
|
92
|
-
|
|
139
|
+
rootDir = dirname(rootDir);
|
|
140
|
+
}
|
|
141
|
+
if (!localVersion) return;
|
|
142
|
+
|
|
143
|
+
// 获取当前 registry
|
|
144
|
+
const registry = execSync('npm config get registry', { encoding: 'utf8', stdio: 'pipe' }).trim();
|
|
145
|
+
const latestVersion = execSync(`npm view @nucc/code-agent-cli version --registry=${registry}`, { encoding: 'utf8', stdio: 'pipe' }).trim();
|
|
146
|
+
|
|
147
|
+
// 比较版本号
|
|
148
|
+
const localParts = localVersion.split('.').map(Number);
|
|
149
|
+
const latestParts = latestVersion.split('.').map(Number);
|
|
150
|
+
const needsUpdate = latestParts[0] > localParts[0] ||
|
|
151
|
+
(latestParts[0] === localParts[0] && latestParts[1] > localParts[1]) ||
|
|
152
|
+
(latestParts[0] === localParts[0] && latestParts[1] === localParts[1] && latestParts[2] > localParts[2]);
|
|
153
|
+
|
|
154
|
+
if (needsUpdate) {
|
|
155
|
+
console.log('\n⚠️ 发现新版本 v%s → v%s', localVersion, latestVersion);
|
|
156
|
+
console.log('\n 运行以下命令更新:');
|
|
157
|
+
console.log(' npm install -g @nucc/code-agent-cli\n');
|
|
93
158
|
}
|
|
94
|
-
}, 100); // 延迟 100ms 确保不影响主流程
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// WebUI 模式
|
|
98
|
-
if (webMode) {
|
|
99
|
-
const webuiDir = join(__dirname, '..', 'webui', 'server');
|
|
100
|
-
|
|
101
|
-
// 先构建 webui
|
|
102
|
-
console.log('🔨 Building WebUI...');
|
|
103
|
-
try {
|
|
104
|
-
execSync('npm run build', { cwd: webuiDir, stdio: 'inherit' });
|
|
105
159
|
} catch (e) {
|
|
106
|
-
|
|
107
|
-
process.exit(1);
|
|
160
|
+
// 忽略检查失败
|
|
108
161
|
}
|
|
109
|
-
|
|
110
|
-
// 启动 WebUI 服务器
|
|
111
|
-
const portArgs = webPort ? ['--port', String(webPort)] : [];
|
|
112
|
-
const webuiProcess = spawn('node', ['dist/index.js', ...portArgs], {
|
|
113
|
-
cwd: webuiDir,
|
|
114
|
-
stdio: 'inherit',
|
|
115
|
-
env: {
|
|
116
|
-
...process.env,
|
|
117
|
-
AICODE_CLI_CONFIG_DIR: config.configDir,
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
// 自动打开浏览器
|
|
122
|
-
const port = webPort || 3002;
|
|
123
|
-
setTimeout(() => {
|
|
124
|
-
try {
|
|
125
|
-
execSync(`open http://localhost:${port}`, { stdio: 'ignore' });
|
|
126
|
-
} catch (e) {
|
|
127
|
-
// 忽略打开浏览器失败
|
|
128
|
-
}
|
|
129
|
-
}, 2000);
|
|
130
|
-
|
|
131
|
-
webuiProcess.on('exit', (code) => {
|
|
132
|
-
process.exit(code ?? 0);
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
webuiProcess.on('error', (err) => {
|
|
136
|
-
console.error('Failed to start WebUI:', err);
|
|
137
|
-
process.exit(1);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
// WebUI 模式下不执行后续的 TUI 逻辑
|
|
141
|
-
checkForUpdates();
|
|
142
|
-
// 不阻塞,让 webui 进程继续运行
|
|
143
162
|
}
|
|
144
|
-
|
|
145
163
|
checkForUpdates();
|
|
146
164
|
|
|
147
165
|
// 获取 node_modules 中 pi-coding-agent 的 CLI 路径
|
|
@@ -162,8 +180,6 @@ const fullPath = join(process.env.HOME || process.env.USERPROFILE, config.config
|
|
|
162
180
|
const agentDir = join(fullPath, 'agent');
|
|
163
181
|
env.AICODE_CLI_CODING_AGENT_DIR = agentDir;
|
|
164
182
|
|
|
165
|
-
|
|
166
|
-
|
|
167
183
|
// 启动 pi-coding-agent
|
|
168
184
|
const child = spawn('node', [piAgentPath.pathname, ...remainingArgs], {
|
|
169
185
|
stdio: 'inherit',
|
|
@@ -916,6 +916,9 @@ export default function reviewExtension(pi: ExtensionAPI) {
|
|
|
916
916
|
fullPrompt += `\n\nThis project has additional instructions for code reviews:\n\n${projectGuidelines}`;
|
|
917
917
|
}
|
|
918
918
|
|
|
919
|
+
// 要求 AI 使用中文输出
|
|
920
|
+
fullPrompt += `\n\n---\n\n**重要:请使用中文撰写审查反馈。**`;
|
|
921
|
+
|
|
919
922
|
const modeHint = useFreshSession ? " (fresh session)" : "";
|
|
920
923
|
ctx.ui.notify(`Starting review: ${hint}${modeHint}`, "info");
|
|
921
924
|
|
package/config/agent/models.json
CHANGED
|
@@ -2,52 +2,26 @@
|
|
|
2
2
|
"providers": {
|
|
3
3
|
"openai": {
|
|
4
4
|
"name": "OpenAI",
|
|
5
|
-
"baseUrl": "https://
|
|
5
|
+
"baseUrl": "https://ai-llm.xxx.com/v1",
|
|
6
6
|
"api": "openai-completions",
|
|
7
7
|
"apiKey": "YOUR_API_KEY_HERE",
|
|
8
|
+
"headers": {
|
|
9
|
+
"User-Agent": "aicode-cli",
|
|
10
|
+
"sourceIp": "!hostname -I 2>/dev/null | awk '{print $1}' || ipconfig getifaddr en0 2>/dev/null || echo ''"
|
|
11
|
+
},
|
|
8
12
|
"models": [
|
|
9
13
|
{
|
|
10
|
-
"id": "
|
|
11
|
-
"name": "
|
|
12
|
-
"contextWindow": 128000,
|
|
13
|
-
"maxTokens": 16384,
|
|
14
|
-
"reasoning": false,
|
|
15
|
-
"input": ["text"],
|
|
16
|
-
"cost": {
|
|
17
|
-
"input": 2.5,
|
|
18
|
-
"output": 10
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
"id": "gpt-4o-mini",
|
|
23
|
-
"name": "GPT-4o Mini",
|
|
24
|
-
"contextWindow": 128000,
|
|
25
|
-
"maxTokens": 16384,
|
|
26
|
-
"reasoning": false,
|
|
27
|
-
"input": ["text"],
|
|
28
|
-
"cost": {
|
|
29
|
-
"input": 0.075,
|
|
30
|
-
"output": 0.3
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
]
|
|
34
|
-
},
|
|
35
|
-
"anthropic": {
|
|
36
|
-
"name": "Anthropic",
|
|
37
|
-
"baseUrl": "https://api.anthropic.com",
|
|
38
|
-
"api": "anthropic-messages",
|
|
39
|
-
"apiKey": "YOUR_API_KEY_HERE",
|
|
40
|
-
"models": [
|
|
41
|
-
{
|
|
42
|
-
"id": "claude-sonnet-4-20250514",
|
|
43
|
-
"name": "Claude Sonnet 4",
|
|
14
|
+
"id": "GLM-4.7-W8A8",
|
|
15
|
+
"name": "GLM4.7",
|
|
44
16
|
"contextWindow": 200000,
|
|
45
|
-
"maxTokens":
|
|
46
|
-
"reasoning":
|
|
17
|
+
"maxTokens": 100000,
|
|
18
|
+
"reasoning": false,
|
|
47
19
|
"input": ["text"],
|
|
48
20
|
"cost": {
|
|
49
|
-
"input":
|
|
50
|
-
"output":
|
|
21
|
+
"input": 5,
|
|
22
|
+
"output": 5,
|
|
23
|
+
"cacheRead": 0,
|
|
24
|
+
"cacheWrite": 0
|
|
51
25
|
}
|
|
52
26
|
}
|
|
53
27
|
]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ia-ccun/code-agent-cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.18",
|
|
4
4
|
"description": "AI Coding Agent CLI - 基于OpenClaw🦞底层Agent原理实现的的编码智能体(供学习使用)。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -32,9 +32,6 @@
|
|
|
32
32
|
"postinstall": "node scripts/postinstall.js",
|
|
33
33
|
"test": "echo \"Tests skipped\" && exit 0",
|
|
34
34
|
"release": "npm version patch",
|
|
35
|
-
"webui:build": "cd webui/server && npm run build",
|
|
36
|
-
"webui:dev": "cd webui/server && npm run dev",
|
|
37
|
-
"webui": "npm run webui:build && node webui/server/dist/index.js",
|
|
38
35
|
"prepare": "husky"
|
|
39
36
|
},
|
|
40
37
|
"dependencies": {
|
package/scripts/postinstall.js
CHANGED
|
@@ -68,6 +68,21 @@ if (existsSync(piAgentConfigJs)) {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
// Fix pi-coding-agent slash commands - change "Quit pi" to "Quit aicode-cli"
|
|
72
|
+
const slashCommandsJs = join(__dirname, '..', 'node_modules', '@mariozechner', 'pi-coding-agent', 'dist', 'core', 'slash-commands.js');
|
|
73
|
+
if (existsSync(slashCommandsJs)) {
|
|
74
|
+
try {
|
|
75
|
+
let content = readFileSync(slashCommandsJs, 'utf-8');
|
|
76
|
+
if (content.includes('Quit pi')) {
|
|
77
|
+
content = content.replace(/Quit pi/g, 'Quit aicode-cli');
|
|
78
|
+
writeFileSync(slashCommandsJs, content);
|
|
79
|
+
console.log(`✓ Updated slash command description`);
|
|
80
|
+
}
|
|
81
|
+
} catch (e) {
|
|
82
|
+
console.log(`⚠ Could not update slash commands: ${e.message}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
71
86
|
// Ensure .aicode-cli directory exists
|
|
72
87
|
if (!existsSync(aicodeCliDir)) {
|
|
73
88
|
mkdirSync(aicodeCliDir, { recursive: true });
|
|
@@ -114,6 +129,27 @@ function copyDirRecursive(src, dest) {
|
|
|
114
129
|
}
|
|
115
130
|
}
|
|
116
131
|
|
|
132
|
+
// Force copy directory recursively (always overwrite destination)
|
|
133
|
+
function forceCopyDirRecursive(src, dest) {
|
|
134
|
+
if (!existsSync(src)) return;
|
|
135
|
+
|
|
136
|
+
const stats = statSync(src);
|
|
137
|
+
if (stats.isDirectory()) {
|
|
138
|
+
if (!existsSync(dest)) {
|
|
139
|
+
mkdirSync(dest, { recursive: true });
|
|
140
|
+
}
|
|
141
|
+
readdirSync(src).forEach(file => {
|
|
142
|
+
forceCopyDirRecursive(join(src, file), join(dest, file));
|
|
143
|
+
});
|
|
144
|
+
} else {
|
|
145
|
+
const parentDir = dirname(dest);
|
|
146
|
+
if (!existsSync(parentDir)) {
|
|
147
|
+
mkdirSync(parentDir, { recursive: true });
|
|
148
|
+
}
|
|
149
|
+
copyFileSync(src, dest);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
117
153
|
// Copy config.json if it doesn't exist
|
|
118
154
|
const packageConfigFile = join(__dirname, '..', 'config.json');
|
|
119
155
|
const userConfigFile = join(aicodeCliDir, 'config.json');
|
|
@@ -125,8 +161,58 @@ if (existsSync(packageConfigFile) && !existsSync(userConfigFile)) {
|
|
|
125
161
|
console.log('⚠ config.json not found in package');
|
|
126
162
|
}
|
|
127
163
|
|
|
128
|
-
//
|
|
164
|
+
// ============================================================================
|
|
165
|
+
// Files/folders that must be force-overwritten on every install
|
|
166
|
+
// ============================================================================
|
|
129
167
|
const packageConfigDir = join(__dirname, '..', 'config', 'agent');
|
|
168
|
+
|
|
169
|
+
const FORCE_OVERWRITE_LIST = [
|
|
170
|
+
'extensions/working-msg.ts',
|
|
171
|
+
// Add more files or folders here to force overwrite on every install
|
|
172
|
+
// Examples:
|
|
173
|
+
// 'skills/some-skill',
|
|
174
|
+
// 'prompts/some-prompt.md',
|
|
175
|
+
// 'themes/dark.json',
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
// Function to force overwrite specific files/folders
|
|
179
|
+
function forceOverwriteList(relativePaths) {
|
|
180
|
+
relativePaths.forEach(relativePath => {
|
|
181
|
+
const srcPath = join(packageConfigDir, relativePath);
|
|
182
|
+
const destPath = join(agentDir, relativePath);
|
|
183
|
+
|
|
184
|
+
if (existsSync(srcPath)) {
|
|
185
|
+
try {
|
|
186
|
+
const stats = statSync(srcPath);
|
|
187
|
+
if (stats.isDirectory()) {
|
|
188
|
+
// Force copy directory
|
|
189
|
+
if (!existsSync(destPath)) {
|
|
190
|
+
mkdirSync(destPath, { recursive: true });
|
|
191
|
+
}
|
|
192
|
+
forceCopyDirRecursive(srcPath, destPath);
|
|
193
|
+
console.log(`✓ Force overwritten (dir): ${relativePath}`);
|
|
194
|
+
} else {
|
|
195
|
+
// Force copy file
|
|
196
|
+
const parentDir = dirname(destPath);
|
|
197
|
+
if (!existsSync(parentDir)) {
|
|
198
|
+
mkdirSync(parentDir, { recursive: true });
|
|
199
|
+
}
|
|
200
|
+
copyFileSync(srcPath, destPath);
|
|
201
|
+
console.log(`✓ Force overwritten: ${relativePath}`);
|
|
202
|
+
}
|
|
203
|
+
} catch (e) {
|
|
204
|
+
console.log(`⚠ Could not force overwrite ${relativePath}: ${e.message}`);
|
|
205
|
+
if (e.stack) {
|
|
206
|
+
console.log(e.stack);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
console.log(`⚠ Source path does not exist: ${relativePath}`);
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Copy config/agent contents to ~/.aicode-cli/agent only if they don't exist (preserve user customizations)
|
|
130
216
|
if (existsSync(packageConfigDir)) {
|
|
131
217
|
console.log(`📁 Syncing default config files to ${agentDir} (preserving user customizations)`);
|
|
132
218
|
|
|
@@ -134,6 +220,11 @@ if (existsSync(packageConfigDir)) {
|
|
|
134
220
|
copyDirRecursive(packageConfigDir, agentDir);
|
|
135
221
|
console.log('✓ Default config files synced successfully');
|
|
136
222
|
|
|
223
|
+
// Force overwrite specified files/folders
|
|
224
|
+
if (FORCE_OVERWRITE_LIST.length > 0) {
|
|
225
|
+
forceOverwriteList(FORCE_OVERWRITE_LIST);
|
|
226
|
+
}
|
|
227
|
+
|
|
137
228
|
// Check for extensions
|
|
138
229
|
const extensionsDir = join(agentDir, 'extensions');
|
|
139
230
|
if (existsSync(extensionsDir)) {
|
|
@@ -150,6 +241,44 @@ if (existsSync(packageConfigDir)) {
|
|
|
150
241
|
console.log('⚠ Configuration directory not found in package');
|
|
151
242
|
}
|
|
152
243
|
|
|
244
|
+
// ============================================================================
|
|
245
|
+
// Reset User-Agent placeholder in models.json (so CLI can replace it on each run)
|
|
246
|
+
// ============================================================================
|
|
247
|
+
const modelsPath = join(agentDir, 'models.json');
|
|
248
|
+
if (existsSync(modelsPath)) {
|
|
249
|
+
try {
|
|
250
|
+
let content = readFileSync(modelsPath, 'utf-8');
|
|
251
|
+
// 只重置 headers.User-Agent 下的版本号为占位符
|
|
252
|
+
try {
|
|
253
|
+
const config = JSON.parse(content);
|
|
254
|
+
for (const provider of Object.values(config.providers || {})) {
|
|
255
|
+
if (provider.headers && provider.headers['User-Agent']) {
|
|
256
|
+
const ua = provider.headers['User-Agent'];
|
|
257
|
+
// 如果是以 aicode-cli/ 开头,替换为占位符
|
|
258
|
+
if (ua && ua.startsWith('aicode-cli/')) {
|
|
259
|
+
provider.headers['User-Agent'] = '${VERSION}';
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const newContent = JSON.stringify(config, null, 2);
|
|
264
|
+
|
|
265
|
+
if (newContent !== content) {
|
|
266
|
+
writeFileSync(modelsPath, newContent, 'utf-8');
|
|
267
|
+
console.log('✓ Reset User-Agent placeholder in models.json');
|
|
268
|
+
}
|
|
269
|
+
} catch (e) {
|
|
270
|
+
console.log(`⚠ Could not reset User-Agent placeholder: ${e.message}`);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (newContent !== content) {
|
|
274
|
+
writeFileSync(modelsPath, newContent, 'utf-8');
|
|
275
|
+
console.log('✓ Reset User-Agent placeholder in models.json');
|
|
276
|
+
}
|
|
277
|
+
} catch (e) {
|
|
278
|
+
console.log(`⚠ Could not reset User-Agent placeholder: ${e.message}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
153
282
|
// Create symlink ~/.pi linking to ~/.aicode-cli (if not exists)
|
|
154
283
|
try {
|
|
155
284
|
if (!existsSync(piDir)) {
|
|
@@ -170,24 +299,19 @@ try {
|
|
|
170
299
|
}
|
|
171
300
|
}
|
|
172
301
|
|
|
173
|
-
// Check
|
|
174
|
-
const modelsPath = join(agentDir, 'models.json');
|
|
302
|
+
// Check and display models config
|
|
175
303
|
if (existsSync(modelsPath)) {
|
|
176
304
|
try {
|
|
177
|
-
const models = JSON.parse(readFileSync(modelsPath, '
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
console.log(`✓ Found ${
|
|
181
|
-
console.log(` - ${Object.keys(models).join(', ')}`);
|
|
182
|
-
} else {
|
|
183
|
-
console.log(`✓ Config file exists but no models configured`);
|
|
305
|
+
const models = JSON.parse(readFileSync(modelsPath, 'utf-8'));
|
|
306
|
+
if (models.providers) {
|
|
307
|
+
const providerCount = Object.keys(models.providers).length;
|
|
308
|
+
console.log(`✓ Found ${providerCount} configured provider(s)`);
|
|
184
309
|
}
|
|
185
310
|
} catch (e) {
|
|
186
311
|
console.log(`⚠ Error reading models.json: ${e.message}`);
|
|
187
312
|
}
|
|
188
313
|
} else {
|
|
189
314
|
console.log(`⚠ No models.json found at ${modelsPath}`);
|
|
190
|
-
console.log('\n📌 To configure models, create ~/.aicode-cli/agent/models.json');
|
|
191
315
|
}
|
|
192
316
|
|
|
193
317
|
console.log('\n✨ Setup complete!');
|