@ia-ccun/code-agent-cli 0.0.51-beta.1 → 0.0.51-beta.10
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 +6 -5
- package/config/agent/extensions/aicode-plan.ts +897 -0
- package/config/agent/extensions/custom-footer.ts +19 -0
- package/config/agent/extensions/custom.ts +1 -4
- package/config/agent/extensions/goal.ts +450 -0
- package/config/agent/help.md +2 -0
- package/dist/config-loader.d.ts +2 -0
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/config-loader.js +81 -7
- package/dist/config-loader.js.map +1 -1
- package/dist/security-logger.d.ts +69 -0
- package/dist/security-logger.d.ts.map +1 -0
- package/dist/security-logger.js +178 -0
- package/dist/security-logger.js.map +1 -0
- package/package.json +12 -2
- package/scripts/postinstall.js +126 -46
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 安全日志模块
|
|
3
|
+
* 🔒 Security: Structured logging for security events
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 日志级别
|
|
7
|
+
*/
|
|
8
|
+
export declare enum LogLevel {
|
|
9
|
+
INFO = "info",
|
|
10
|
+
WARN = "warn",
|
|
11
|
+
ERROR = "error"
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 安全事件类型
|
|
15
|
+
*/
|
|
16
|
+
export declare enum SecurityEventType {
|
|
17
|
+
CONFIG_LOAD = "config_load",
|
|
18
|
+
CONFIG_ERROR = "config_error",
|
|
19
|
+
PATH_VALIDATION = "path_validation",
|
|
20
|
+
PATH_TRAVERSAL_BLOCKED = "path_traversal_blocked",
|
|
21
|
+
FILE_MODIFIED = "file_modified",
|
|
22
|
+
FILE_ACCESS = "file_access",
|
|
23
|
+
DEPENDENCY_CHECK = "dependency_check",
|
|
24
|
+
VERSION_CHECK = "version_check",
|
|
25
|
+
INSTALL_START = "install_start",
|
|
26
|
+
INSTALL_COMPLETE = "install_complete",
|
|
27
|
+
SYMLINK_CREATE = "symlink_create",
|
|
28
|
+
SUSPICIOUS_ACTIVITY = "suspicious_activity"
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 日志条目接口
|
|
32
|
+
*/
|
|
33
|
+
export interface LogEntry {
|
|
34
|
+
timestamp: string;
|
|
35
|
+
level: LogLevel;
|
|
36
|
+
event?: SecurityEventType;
|
|
37
|
+
message: string;
|
|
38
|
+
details?: Record<string, unknown>;
|
|
39
|
+
pid: number;
|
|
40
|
+
version?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 记录安全事件
|
|
44
|
+
* @param event 事件类型
|
|
45
|
+
* @param details 事件详情
|
|
46
|
+
* @param level 日志级别(默认 info)
|
|
47
|
+
*/
|
|
48
|
+
export declare function logSecurity(event: SecurityEventType, details: Record<string, unknown>, level?: LogLevel): void;
|
|
49
|
+
/**
|
|
50
|
+
* 记录错误
|
|
51
|
+
* @param message 错误消息
|
|
52
|
+
* @param error 错误对象
|
|
53
|
+
* @param context 上下文信息
|
|
54
|
+
*/
|
|
55
|
+
export declare function logError(message: string, error: Error, context?: Record<string, unknown>): void;
|
|
56
|
+
/**
|
|
57
|
+
* 获取日志文件路径
|
|
58
|
+
*/
|
|
59
|
+
export declare function getLogPaths(): {
|
|
60
|
+
security: string;
|
|
61
|
+
error: string;
|
|
62
|
+
dir: string;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* 清除旧日志(可选的维护功能)
|
|
66
|
+
* @param maxAge 最大保留天数(默认 30 天)
|
|
67
|
+
*/
|
|
68
|
+
export declare function rotateLogs(maxAge?: number): void;
|
|
69
|
+
//# sourceMappingURL=security-logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-logger.d.ts","sourceRoot":"","sources":["../src/security-logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH;;GAEG;AACH,oBAAY,QAAQ;IAClB,IAAI,SAAS;IACb,IAAI,SAAS;IACb,KAAK,UAAU;CAChB;AAED;;GAEG;AACH,oBAAY,iBAAiB;IAE3B,WAAW,gBAAgB;IAC3B,YAAY,iBAAiB;IAG7B,eAAe,oBAAoB;IACnC,sBAAsB,2BAA2B;IAGjD,aAAa,kBAAkB;IAC/B,WAAW,gBAAgB;IAG3B,gBAAgB,qBAAqB;IACrC,aAAa,kBAAkB;IAG/B,aAAa,kBAAkB;IAC/B,gBAAgB,qBAAqB;IACrC,cAAc,mBAAmB;IAGjC,mBAAmB,wBAAwB;CAC5C;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,QAAQ,CAAC;IAChB,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAyCD;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,iBAAiB,EACxB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,KAAK,GAAE,QAAwB,GAC9B,IAAI,CAmBN;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CACtB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,IAAI,CAuBN;AAoBD;;GAEG;AACH,wBAAgB,WAAW,IAAI;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAM9E;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,GAAE,MAAW,GAAG,IAAI,CAoBpD"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 安全日志模块
|
|
3
|
+
* 🔒 Security: Structured logging for security events
|
|
4
|
+
*/
|
|
5
|
+
import { createWriteStream, existsSync, mkdirSync, readdirSync, statSync, unlinkSync } from 'fs';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
import { homedir } from 'os';
|
|
8
|
+
// 日志目录和文件
|
|
9
|
+
const LOG_DIR = join(homedir(), '.aicode-cli', 'logs');
|
|
10
|
+
const SECURITY_LOG = join(LOG_DIR, 'security.log');
|
|
11
|
+
const ERROR_LOG = join(LOG_DIR, 'error.log');
|
|
12
|
+
/**
|
|
13
|
+
* 日志级别
|
|
14
|
+
*/
|
|
15
|
+
export var LogLevel;
|
|
16
|
+
(function (LogLevel) {
|
|
17
|
+
LogLevel["INFO"] = "info";
|
|
18
|
+
LogLevel["WARN"] = "warn";
|
|
19
|
+
LogLevel["ERROR"] = "error";
|
|
20
|
+
})(LogLevel || (LogLevel = {}));
|
|
21
|
+
/**
|
|
22
|
+
* 安全事件类型
|
|
23
|
+
*/
|
|
24
|
+
export var SecurityEventType;
|
|
25
|
+
(function (SecurityEventType) {
|
|
26
|
+
// 配置相关
|
|
27
|
+
SecurityEventType["CONFIG_LOAD"] = "config_load";
|
|
28
|
+
SecurityEventType["CONFIG_ERROR"] = "config_error";
|
|
29
|
+
// 路径验证
|
|
30
|
+
SecurityEventType["PATH_VALIDATION"] = "path_validation";
|
|
31
|
+
SecurityEventType["PATH_TRAVERSAL_BLOCKED"] = "path_traversal_blocked";
|
|
32
|
+
// 文件操作
|
|
33
|
+
SecurityEventType["FILE_MODIFIED"] = "file_modified";
|
|
34
|
+
SecurityEventType["FILE_ACCESS"] = "file_access";
|
|
35
|
+
// 依赖和更新
|
|
36
|
+
SecurityEventType["DEPENDENCY_CHECK"] = "dependency_check";
|
|
37
|
+
SecurityEventType["VERSION_CHECK"] = "version_check";
|
|
38
|
+
// 安装和设置
|
|
39
|
+
SecurityEventType["INSTALL_START"] = "install_start";
|
|
40
|
+
SecurityEventType["INSTALL_COMPLETE"] = "install_complete";
|
|
41
|
+
SecurityEventType["SYMLINK_CREATE"] = "symlink_create";
|
|
42
|
+
// 通用
|
|
43
|
+
SecurityEventType["SUSPICIOUS_ACTIVITY"] = "suspicious_activity";
|
|
44
|
+
})(SecurityEventType || (SecurityEventType = {}));
|
|
45
|
+
/**
|
|
46
|
+
* 日志流(懒加载)
|
|
47
|
+
*/
|
|
48
|
+
let securityLogStream = null;
|
|
49
|
+
let errorLogStream = null;
|
|
50
|
+
/**
|
|
51
|
+
* 初始化日志目录和流
|
|
52
|
+
*/
|
|
53
|
+
function initLogs() {
|
|
54
|
+
// 确保日志目录存在
|
|
55
|
+
if (!existsSync(LOG_DIR)) {
|
|
56
|
+
mkdirSync(LOG_DIR, { recursive: true, mode: 0o700 });
|
|
57
|
+
}
|
|
58
|
+
// 创建安全日志流(追加模式,仅所有者可读写)
|
|
59
|
+
if (!securityLogStream) {
|
|
60
|
+
securityLogStream = createWriteStream(SECURITY_LOG, {
|
|
61
|
+
flags: 'a',
|
|
62
|
+
mode: 0o600,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
// 创建错误日志流
|
|
66
|
+
if (!errorLogStream) {
|
|
67
|
+
errorLogStream = createWriteStream(ERROR_LOG, {
|
|
68
|
+
flags: 'a',
|
|
69
|
+
mode: 0o600,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* 格式化日志条目为 JSON
|
|
75
|
+
*/
|
|
76
|
+
function formatLog(entry) {
|
|
77
|
+
return JSON.stringify(entry) + '\n';
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* 记录安全事件
|
|
81
|
+
* @param event 事件类型
|
|
82
|
+
* @param details 事件详情
|
|
83
|
+
* @param level 日志级别(默认 info)
|
|
84
|
+
*/
|
|
85
|
+
export function logSecurity(event, details, level = LogLevel.INFO) {
|
|
86
|
+
initLogs();
|
|
87
|
+
const entry = {
|
|
88
|
+
timestamp: new Date().toISOString(),
|
|
89
|
+
level,
|
|
90
|
+
event,
|
|
91
|
+
message: `Security event: ${event}`,
|
|
92
|
+
details: sanitizeDetails(details),
|
|
93
|
+
pid: process.pid,
|
|
94
|
+
version: process.version,
|
|
95
|
+
};
|
|
96
|
+
securityLogStream?.write(formatLog(entry));
|
|
97
|
+
// 开发环境下输出到控制台
|
|
98
|
+
if (process.env.DEBUG || process.env.NODE_ENV === 'development') {
|
|
99
|
+
console.error(`[🔒 安全] ${event}:`, details);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* 记录错误
|
|
104
|
+
* @param message 错误消息
|
|
105
|
+
* @param error 错误对象
|
|
106
|
+
* @param context 上下文信息
|
|
107
|
+
*/
|
|
108
|
+
export function logError(message, error, context) {
|
|
109
|
+
initLogs();
|
|
110
|
+
const entry = {
|
|
111
|
+
timestamp: new Date().toISOString(),
|
|
112
|
+
level: LogLevel.ERROR,
|
|
113
|
+
message,
|
|
114
|
+
details: {
|
|
115
|
+
name: error.name,
|
|
116
|
+
message: error.message,
|
|
117
|
+
stack: error.stack,
|
|
118
|
+
...sanitizeDetails(context || {}),
|
|
119
|
+
},
|
|
120
|
+
pid: process.pid,
|
|
121
|
+
version: process.version,
|
|
122
|
+
};
|
|
123
|
+
errorLogStream?.write(formatLog(entry));
|
|
124
|
+
// 开发环境下输出到控制台
|
|
125
|
+
if (process.env.DEBUG || process.env.NODE_ENV === 'development') {
|
|
126
|
+
console.error(`[❌ 错误] ${message}:`, error);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* 脱敏处理(移除敏感信息)
|
|
131
|
+
*/
|
|
132
|
+
function sanitizeDetails(details) {
|
|
133
|
+
const sensitiveKeys = ['apiKey', 'api_key', 'token', 'password', 'secret', 'auth'];
|
|
134
|
+
const sanitized = { ...details };
|
|
135
|
+
for (const key of sensitiveKeys) {
|
|
136
|
+
if (sanitized[key]) {
|
|
137
|
+
// 仅保留前 4 个字符用于识别
|
|
138
|
+
const value = String(sanitized[key]);
|
|
139
|
+
sanitized[key] = value.length > 8 ? value.substring(0, 4) + '***' : '***';
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return sanitized;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* 获取日志文件路径
|
|
146
|
+
*/
|
|
147
|
+
export function getLogPaths() {
|
|
148
|
+
return {
|
|
149
|
+
security: SECURITY_LOG,
|
|
150
|
+
error: ERROR_LOG,
|
|
151
|
+
dir: LOG_DIR,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* 清除旧日志(可选的维护功能)
|
|
156
|
+
* @param maxAge 最大保留天数(默认 30 天)
|
|
157
|
+
*/
|
|
158
|
+
export function rotateLogs(maxAge = 30) {
|
|
159
|
+
const now = Date.now();
|
|
160
|
+
const maxAgeMs = maxAge * 24 * 60 * 60 * 1000;
|
|
161
|
+
try {
|
|
162
|
+
if (!existsSync(LOG_DIR))
|
|
163
|
+
return;
|
|
164
|
+
const files = readdirSync(LOG_DIR);
|
|
165
|
+
for (const file of files) {
|
|
166
|
+
const filePath = join(LOG_DIR, file);
|
|
167
|
+
const stats = statSync(filePath);
|
|
168
|
+
if (stats.isFile() && now - stats.mtimeMs > maxAgeMs) {
|
|
169
|
+
unlinkSync(filePath);
|
|
170
|
+
console.log(`[日志轮转] 已删除旧日志:${file}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
logError('日志轮转失败', error, { maxAge });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=security-logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-logger.js","sourceRoot":"","sources":["../src/security-logger.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ia-ccun/code-agent-cli",
|
|
3
|
-
"version": "0.0.51-beta.
|
|
3
|
+
"version": "0.0.51-beta.10",
|
|
4
4
|
"description": "AI Coding Agent CLI - 基于OpenClaw🦞底层Agent(pi)原理封装的编码智能体(供学习使用)。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -42,9 +42,19 @@
|
|
|
42
42
|
},
|
|
43
43
|
"overrides": {
|
|
44
44
|
"glob": "^11.0.0",
|
|
45
|
-
"node-domexception": "^2.0.0"
|
|
45
|
+
"node-domexception": "^2.0.0",
|
|
46
|
+
"protobufjs": "^7.5.5",
|
|
47
|
+
"basic-ftp": "^6.0.1",
|
|
48
|
+
"flatted": "^3.4.2",
|
|
49
|
+
"undici": "^7.24.8",
|
|
50
|
+
"picomatch": "^4.0.4",
|
|
51
|
+
"brace-expansion": "^5.0.5",
|
|
52
|
+
"file-type": "^21.3.2",
|
|
53
|
+
"ip-address": "^10.1.1",
|
|
54
|
+
"yaml": "^2.8.3"
|
|
46
55
|
},
|
|
47
56
|
"devDependencies": {
|
|
57
|
+
"@devkade/pi-plan": "^0.2.2",
|
|
48
58
|
"@types/node": "^22.10.0",
|
|
49
59
|
"@typescript-eslint/eslint-plugin": "^8.57.0",
|
|
50
60
|
"@typescript-eslint/parser": "^8.57.0",
|
package/scripts/postinstall.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// Post-install script for aicode-cli
|
|
4
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync, readdirSync, statSync } from 'fs';
|
|
5
|
-
import {
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync, readdirSync, statSync, createWriteStream } from 'fs';
|
|
5
|
+
import { symlink as fsSymlink } from 'fs/promises';
|
|
6
6
|
import { join, dirname, relative } from 'path';
|
|
7
7
|
import { homedir } from 'os';
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
@@ -11,8 +11,55 @@ import { fileURLToPath } from 'url';
|
|
|
11
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
12
12
|
const __dirname = dirname(__filename);
|
|
13
13
|
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Security Logger (inline for postinstall script)
|
|
16
|
+
// ============================================================================
|
|
17
|
+
const LOG_DIR = join(homedir(), '.aicode-cli', 'logs');
|
|
18
|
+
const SECURITY_LOG = join(LOG_DIR, 'security.log');
|
|
19
|
+
|
|
20
|
+
function ensureLogDir() {
|
|
21
|
+
if (!existsSync(LOG_DIR)) {
|
|
22
|
+
mkdirSync(LOG_DIR, { recursive: true, mode: 0o700 });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function logSecurity(event, details, level = 'info') {
|
|
27
|
+
ensureLogDir();
|
|
28
|
+
const entry = {
|
|
29
|
+
timestamp: new Date().toISOString(),
|
|
30
|
+
level,
|
|
31
|
+
event,
|
|
32
|
+
message: `PostInstall: ${event}`,
|
|
33
|
+
details,
|
|
34
|
+
pid: process.pid,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const stream = createWriteStream(SECURITY_LOG, { flags: 'a', mode: 0o600 });
|
|
39
|
+
stream.write(JSON.stringify(entry) + '\n');
|
|
40
|
+
stream.end();
|
|
41
|
+
} catch (e) {
|
|
42
|
+
// Ignore logging errors
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (process.env.DEBUG) {
|
|
46
|
+
console.error(`[🔒 安全] ${event}:`, details);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ============================================================================
|
|
51
|
+
// Main Installation Logic
|
|
52
|
+
// ============================================================================
|
|
53
|
+
|
|
14
54
|
console.log('\n🚀 Setting up aicode-cli...');
|
|
15
55
|
|
|
56
|
+
// Record installation start
|
|
57
|
+
logSecurity('install_start', {
|
|
58
|
+
version: '0.0.51-beta.1',
|
|
59
|
+
homeDir: homedir(),
|
|
60
|
+
timestamp: new Date().toISOString(),
|
|
61
|
+
});
|
|
62
|
+
|
|
16
63
|
// Get the current user's home directory
|
|
17
64
|
const homeDir = homedir();
|
|
18
65
|
const aicodeCliDir = join(homeDir, '.aicode-cli');
|
|
@@ -33,8 +80,19 @@ if (existsSync(piAgentPackageJson)) {
|
|
|
33
80
|
piPkg.piConfig.configDir = '.aicode-cli';
|
|
34
81
|
writeFileSync(piAgentPackageJson, JSON.stringify(piPkg, null, 2) + '\n');
|
|
35
82
|
console.log(`✓ Updated pi-coding-agent app name: ${oldName} -> aicode-cli`);
|
|
83
|
+
|
|
84
|
+
logSecurity('file_modified', {
|
|
85
|
+
file: piAgentPackageJson,
|
|
86
|
+
action: 'Updated piConfig',
|
|
87
|
+
changes: ['name', 'configDir'],
|
|
88
|
+
});
|
|
36
89
|
} catch (e) {
|
|
37
90
|
console.log(`⚠ Could not update pi-coding-agent config: ${e.message}`);
|
|
91
|
+
logSecurity('file_modified', {
|
|
92
|
+
file: piAgentPackageJson,
|
|
93
|
+
action: 'Update failed',
|
|
94
|
+
error: e.message,
|
|
95
|
+
}, 'warn');
|
|
38
96
|
}
|
|
39
97
|
}
|
|
40
98
|
|
|
@@ -47,6 +105,11 @@ if (existsSync(piAgentCliJs)) {
|
|
|
47
105
|
cliContent = cliContent.replace(/process\.title = "pi"/g, 'process.title = "aicode-cli"');
|
|
48
106
|
writeFileSync(piAgentCliJs, cliContent);
|
|
49
107
|
console.log(`✓ Updated pi-coding-agent process.title`);
|
|
108
|
+
|
|
109
|
+
logSecurity('file_modified', {
|
|
110
|
+
file: piAgentCliJs,
|
|
111
|
+
action: 'Updated process.title',
|
|
112
|
+
});
|
|
50
113
|
}
|
|
51
114
|
} catch (e) {
|
|
52
115
|
console.log(`⚠ Could not update pi-coding-agent cli.js: ${e.message}`);
|
|
@@ -62,6 +125,11 @@ if (existsSync(piAgentConfigJs)) {
|
|
|
62
125
|
configContent = configContent.replace(/pkg\.piConfig\?\.name \|\| "pi"/g, 'pkg.piConfig?.name || "aicode-cli"');
|
|
63
126
|
writeFileSync(piAgentConfigJs, configContent);
|
|
64
127
|
console.log(`✓ Updated pi-coding-agent config.js APP_NAME`);
|
|
128
|
+
|
|
129
|
+
logSecurity('file_modified', {
|
|
130
|
+
file: piAgentConfigJs,
|
|
131
|
+
action: 'Updated APP_NAME',
|
|
132
|
+
});
|
|
65
133
|
}
|
|
66
134
|
} catch (e) {
|
|
67
135
|
console.log(`⚠ Could not update pi-coding-agent config.js: ${e.message}`);
|
|
@@ -77,6 +145,11 @@ if (existsSync(slashCommandsJs)) {
|
|
|
77
145
|
content = content.replace(/Quit pi/g, 'Quit aicode-cli');
|
|
78
146
|
writeFileSync(slashCommandsJs, content);
|
|
79
147
|
console.log(`✓ Updated slash command description`);
|
|
148
|
+
|
|
149
|
+
logSecurity('file_modified', {
|
|
150
|
+
file: slashCommandsJs,
|
|
151
|
+
action: 'Updated slash commands',
|
|
152
|
+
});
|
|
80
153
|
}
|
|
81
154
|
} catch (e) {
|
|
82
155
|
console.log(`⚠ Could not update slash commands: ${e.message}`);
|
|
@@ -103,7 +176,6 @@ function copyDirRecursive(src, dest) {
|
|
|
103
176
|
const isDirectory = exists && stats.isDirectory();
|
|
104
177
|
|
|
105
178
|
if (isDirectory) {
|
|
106
|
-
// Create directory if it doesn't exist
|
|
107
179
|
if (!existsSync(dest)) {
|
|
108
180
|
mkdirSync(dest, { recursive: true });
|
|
109
181
|
}
|
|
@@ -115,9 +187,7 @@ function copyDirRecursive(src, dest) {
|
|
|
115
187
|
copyDirRecursive(srcPath, destPath);
|
|
116
188
|
});
|
|
117
189
|
} else {
|
|
118
|
-
// Only copy if source exists AND destination doesn't exist (preserve user files)
|
|
119
190
|
if (exists && !existsSync(dest)) {
|
|
120
|
-
// Ensure parent directory exists
|
|
121
191
|
const parentDir = dirname(dest);
|
|
122
192
|
if (!existsSync(parentDir)) {
|
|
123
193
|
mkdirSync(parentDir, { recursive: true });
|
|
@@ -171,19 +241,14 @@ const FORCE_OVERWRITE_LIST = [
|
|
|
171
241
|
'SYSTEM.md',
|
|
172
242
|
'dangerous-patterns.json',
|
|
173
243
|
'settings.json',
|
|
174
|
-
'
|
|
175
|
-
'extensions
|
|
176
|
-
'extensions/custom.ts',
|
|
177
|
-
'extensions/bash-check.ts',
|
|
178
|
-
'extensions/aicode-tips.ts',
|
|
179
|
-
// Force overwrite all prompts .md files
|
|
244
|
+
'help.md',
|
|
245
|
+
'extensions/*.ts',
|
|
180
246
|
'prompts/*.md',
|
|
181
|
-
// Force overwrite all skills SKILL.md files
|
|
182
247
|
'skills/*/SKILL.md',
|
|
183
|
-
|
|
248
|
+
'skills/*/*/SKILL.md',
|
|
184
249
|
];
|
|
185
250
|
|
|
186
|
-
// Function to expand glob patterns
|
|
251
|
+
// Function to expand glob patterns
|
|
187
252
|
function expandGlob(pattern) {
|
|
188
253
|
const results = [];
|
|
189
254
|
|
|
@@ -201,21 +266,17 @@ function expandGlob(pattern) {
|
|
|
201
266
|
if (!existsSync(cp)) continue;
|
|
202
267
|
|
|
203
268
|
if (hasWildcard) {
|
|
204
|
-
// Handle wildcard patterns like * or *.md
|
|
205
269
|
const entries = readdirSync(cp);
|
|
206
|
-
// Convert glob pattern to regex: *.md -> ^.*\.md$
|
|
207
270
|
const regex = new RegExp('^' + part.replace(/\*/g, '.*') + '$');
|
|
208
271
|
|
|
209
272
|
for (const entry of entries) {
|
|
210
273
|
if (regex.test(entry)) {
|
|
211
274
|
const fullPath = join(cp, entry);
|
|
212
275
|
if (isLastPart) {
|
|
213
|
-
// Last part - match files (not directories)
|
|
214
276
|
if (statSync(fullPath).isFile()) {
|
|
215
277
|
nextPaths.push(fullPath);
|
|
216
278
|
}
|
|
217
279
|
} else {
|
|
218
|
-
// Not the last part - only directories
|
|
219
280
|
if (statSync(fullPath).isDirectory()) {
|
|
220
281
|
nextPaths.push(fullPath);
|
|
221
282
|
}
|
|
@@ -243,7 +304,6 @@ function expandGlob(pattern) {
|
|
|
243
304
|
|
|
244
305
|
// Function to force overwrite specific files/folders
|
|
245
306
|
function forceOverwriteList(relativePaths) {
|
|
246
|
-
// Expand glob patterns first
|
|
247
307
|
const expandedPaths = [];
|
|
248
308
|
for (const relativePath of relativePaths) {
|
|
249
309
|
expandedPaths.push(...expandGlob(relativePath));
|
|
@@ -257,14 +317,12 @@ function forceOverwriteList(relativePaths) {
|
|
|
257
317
|
try {
|
|
258
318
|
const stats = statSync(srcPath);
|
|
259
319
|
if (stats.isDirectory()) {
|
|
260
|
-
// Force copy directory
|
|
261
320
|
if (!existsSync(destPath)) {
|
|
262
321
|
mkdirSync(destPath, { recursive: true });
|
|
263
322
|
}
|
|
264
323
|
forceCopyDirRecursive(srcPath, destPath);
|
|
265
324
|
console.log(`✓ Force overwritten (dir): ${relativePath}`);
|
|
266
325
|
} else {
|
|
267
|
-
// Force copy file
|
|
268
326
|
const parentDir = dirname(destPath);
|
|
269
327
|
if (!existsSync(parentDir)) {
|
|
270
328
|
mkdirSync(parentDir, { recursive: true });
|
|
@@ -281,20 +339,17 @@ function forceOverwriteList(relativePaths) {
|
|
|
281
339
|
});
|
|
282
340
|
}
|
|
283
341
|
|
|
284
|
-
// Copy config/agent contents to ~/.aicode-cli/agent
|
|
342
|
+
// Copy config/agent contents to ~/.aicode-cli/agent
|
|
285
343
|
if (existsSync(packageConfigDir)) {
|
|
286
344
|
console.log(`📁 Syncing default config files to ${agentDir} (preserving user customizations)`);
|
|
287
345
|
|
|
288
|
-
// Copy directory recursively (only if destination doesn't exist)
|
|
289
346
|
copyDirRecursive(packageConfigDir, agentDir);
|
|
290
347
|
console.log('✓ Default config files synced successfully');
|
|
291
348
|
|
|
292
|
-
// Force overwrite specified files/folders
|
|
293
349
|
if (FORCE_OVERWRITE_LIST.length > 0) {
|
|
294
350
|
forceOverwriteList(FORCE_OVERWRITE_LIST);
|
|
295
351
|
}
|
|
296
352
|
|
|
297
|
-
// Check for extensions
|
|
298
353
|
const extensionsDir = join(agentDir, 'extensions');
|
|
299
354
|
if (existsSync(extensionsDir)) {
|
|
300
355
|
try {
|
|
@@ -311,19 +366,17 @@ if (existsSync(packageConfigDir)) {
|
|
|
311
366
|
}
|
|
312
367
|
|
|
313
368
|
// ============================================================================
|
|
314
|
-
// Reset User-Agent placeholder in models.json
|
|
369
|
+
// Reset User-Agent placeholder in models.json
|
|
315
370
|
// ============================================================================
|
|
316
371
|
const modelsPath = join(agentDir, 'models.json');
|
|
317
372
|
if (existsSync(modelsPath)) {
|
|
318
373
|
try {
|
|
319
374
|
let content = readFileSync(modelsPath, 'utf-8');
|
|
320
|
-
// 只重置 headers.User-Agent 下的版本号为占位符
|
|
321
375
|
try {
|
|
322
376
|
const config = JSON.parse(content);
|
|
323
377
|
for (const provider of Object.values(config.providers || {})) {
|
|
324
378
|
if (provider.headers && provider.headers['User-Agent']) {
|
|
325
379
|
const ua = provider.headers['User-Agent'];
|
|
326
|
-
// 如果是以 aicode-cli/ 开头,替换为占位符
|
|
327
380
|
if (ua && ua.startsWith('aicode-cli/')) {
|
|
328
381
|
provider.headers['User-Agent'] = '${VERSION}';
|
|
329
382
|
}
|
|
@@ -334,6 +387,11 @@ if (existsSync(modelsPath)) {
|
|
|
334
387
|
if (newContent !== content) {
|
|
335
388
|
writeFileSync(modelsPath, newContent, 'utf-8');
|
|
336
389
|
console.log('✓ Reset User-Agent placeholder in models.json');
|
|
390
|
+
|
|
391
|
+
logSecurity('file_modified', {
|
|
392
|
+
file: modelsPath,
|
|
393
|
+
action: 'Reset User-Agent placeholder',
|
|
394
|
+
});
|
|
337
395
|
}
|
|
338
396
|
} catch (e) {
|
|
339
397
|
console.log(`⚠ Could not reset User-Agent placeholder: ${e.message}`);
|
|
@@ -349,24 +407,38 @@ if (existsSync(modelsPath)) {
|
|
|
349
407
|
}
|
|
350
408
|
|
|
351
409
|
// Create symlink ~/.pi linking to ~/.aicode-cli (if not exists)
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
410
|
+
(async () => {
|
|
411
|
+
try {
|
|
412
|
+
if (!existsSync(piDir)) {
|
|
413
|
+
await fsSymlink(aicodeCliDir, piDir, 'dir');
|
|
414
|
+
console.log(`✓ Created symbolic link: ${piDir} -> ${aicodeCliDir}`);
|
|
415
|
+
|
|
416
|
+
logSecurity('symlink_create', {
|
|
417
|
+
target: piDir,
|
|
418
|
+
source: aicodeCliDir,
|
|
419
|
+
success: true,
|
|
420
|
+
});
|
|
421
|
+
} else {
|
|
422
|
+
console.log(`✓ ${piDir} already exists`);
|
|
423
|
+
}
|
|
424
|
+
} catch (e) {
|
|
425
|
+
if (e.code === 'EEXIST') {
|
|
426
|
+
console.log(`✓ Symbolic link already exists: ${piDir}`);
|
|
427
|
+
} else {
|
|
428
|
+
console.log(`⚠ Could not create symbolic link: ${e.message}`);
|
|
429
|
+
console.log('\n📌 To fix this, please run manually:');
|
|
430
|
+
console.log(' ln -s ~/.aicode-cli ~/.pi');
|
|
431
|
+
console.log('\n (If ~/.pi exists, run: rm -rf ~/.pi && ln -s ~/.aicode-cli ~/.pi)');
|
|
432
|
+
|
|
433
|
+
logSecurity('symlink_create', {
|
|
434
|
+
target: piDir,
|
|
435
|
+
source: aicodeCliDir,
|
|
436
|
+
success: false,
|
|
437
|
+
error: e.message,
|
|
438
|
+
}, 'warn');
|
|
439
|
+
}
|
|
368
440
|
}
|
|
369
|
-
}
|
|
441
|
+
})();
|
|
370
442
|
|
|
371
443
|
// Check and display models config
|
|
372
444
|
if (existsSync(modelsPath)) {
|
|
@@ -383,8 +455,16 @@ if (existsSync(modelsPath)) {
|
|
|
383
455
|
console.log(`⚠ No models.json found at ${modelsPath}`);
|
|
384
456
|
}
|
|
385
457
|
|
|
458
|
+
// Record installation complete
|
|
459
|
+
logSecurity('install_complete', {
|
|
460
|
+
success: true,
|
|
461
|
+
aicodeCliDir,
|
|
462
|
+
agentDir,
|
|
463
|
+
piDir,
|
|
464
|
+
});
|
|
465
|
+
|
|
386
466
|
console.log('\n✨ Setup complete!');
|
|
387
467
|
console.log('📝 Configuration files are in ~/.aicode-cli/');
|
|
388
468
|
console.log('🔗 ~/.pi is linked to ~/.aicode-cli (ensuring model configs work)');
|
|
389
469
|
console.log('🚀 Run with: aicode-cli');
|
|
390
|
-
console.log('\n📖 For more information, see: https://www.npmjs.com/package/@ia-ccun/code-agent-cli');
|
|
470
|
+
console.log('\n📖 For more information, see: https://www.npmjs.com/package/@ia-ccun/code-agent-cli');
|