ccman 1.0.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.development +3 -0
- package/.env.production +3 -0
- package/.github/workflows/release.yml +5 -5
- package/CLAUDE.md +246 -185
- package/README.md +282 -249
- package/README_zh.md +283 -250
- package/dev-test.sh +40 -0
- package/dist/cli.js +425 -369
- package/dist/cli.js.map +1 -1
- package/dist/commands/lang.d.ts +3 -0
- package/dist/commands/lang.d.ts.map +1 -0
- package/dist/commands/lang.js +99 -0
- package/dist/commands/lang.js.map +1 -0
- package/dist/config/static-env.d.ts +14 -0
- package/dist/config/static-env.d.ts.map +1 -0
- package/dist/config/static-env.js +17 -0
- package/dist/config/static-env.js.map +1 -0
- package/dist/core/CCMConfigManager.d.ts +52 -0
- package/dist/core/CCMConfigManager.d.ts.map +1 -0
- package/dist/core/CCMConfigManager.js +203 -0
- package/dist/core/CCMConfigManager.js.map +1 -0
- package/dist/core/ClaudeConfigManager.d.ts +35 -0
- package/dist/core/ClaudeConfigManager.d.ts.map +1 -0
- package/dist/core/ClaudeConfigManager.js +149 -0
- package/dist/core/ClaudeConfigManager.js.map +1 -0
- package/dist/i18n/LanguageManager.d.ts +43 -0
- package/dist/i18n/LanguageManager.d.ts.map +1 -0
- package/dist/i18n/LanguageManager.js +157 -0
- package/dist/i18n/LanguageManager.js.map +1 -0
- package/dist/i18n/messages.d.ts +65 -0
- package/dist/i18n/messages.d.ts.map +1 -0
- package/dist/i18n/messages.js +144 -0
- package/dist/i18n/messages.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -8
- package/dist/index.js.map +1 -1
- package/dist/providers/ProviderManager.d.ts +58 -0
- package/dist/providers/ProviderManager.d.ts.map +1 -0
- package/dist/providers/ProviderManager.js +335 -0
- package/dist/providers/ProviderManager.js.map +1 -0
- package/dist/types/index.d.ts +78 -38
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/utils/env-config.d.ts +27 -0
- package/dist/utils/env-config.d.ts.map +1 -0
- package/dist/{config/constants.js → utils/env-config.js} +36 -50
- package/dist/utils/env-config.js.map +1 -0
- package/dist/utils/version.d.ts +2 -64
- package/dist/utils/version.d.ts.map +1 -1
- package/dist/utils/version.js +12 -158
- package/dist/utils/version.js.map +1 -1
- package/package.json +17 -16
- package/release-temp/README.md +282 -249
- package/release-temp/package.json +17 -16
- package/scripts/build-env.js +75 -0
- package/scripts/modules/create-tag.sh +53 -10
- package/scripts/modules/monitor-release.sh +40 -12
- package/scripts/modules/version-bump.sh +14 -17
- package/scripts/smart-release-v3.sh +20 -26
- package/src/cli.ts +462 -394
- package/src/commands/lang.ts +105 -0
- package/src/core/CCMConfigManager.ts +185 -0
- package/src/core/ClaudeConfigManager.ts +125 -0
- package/src/i18n/LanguageManager.ts +169 -0
- package/src/i18n/messages.ts +233 -0
- package/src/index.ts +4 -5
- package/src/providers/ProviderManager.ts +393 -0
- package/src/types/index.ts +80 -39
- package/src/utils/env-config.ts +53 -0
- package/src/utils/version.ts +11 -184
- package/dist/config/ConfigManager.d.ts +0 -67
- package/dist/config/ConfigManager.d.ts.map +0 -1
- package/dist/config/ConfigManager.js +0 -226
- package/dist/config/ConfigManager.js.map +0 -1
- package/dist/config/EnvironmentManager.d.ts +0 -83
- package/dist/config/EnvironmentManager.d.ts.map +0 -1
- package/dist/config/EnvironmentManager.js +0 -280
- package/dist/config/EnvironmentManager.js.map +0 -1
- package/dist/config/constants.d.ts +0 -40
- package/dist/config/constants.d.ts.map +0 -1
- package/dist/config/constants.js.map +0 -1
- package/dist/shell/ShellManager.d.ts +0 -81
- package/dist/shell/ShellManager.d.ts.map +0 -1
- package/dist/shell/ShellManager.js +0 -490
- package/dist/shell/ShellManager.js.map +0 -1
- package/src/config/ConfigManager.ts +0 -227
- package/src/config/EnvironmentManager.ts +0 -327
- package/src/config/constants.ts +0 -64
- package/src/shell/ShellManager.ts +0 -526
|
@@ -1,526 +0,0 @@
|
|
|
1
|
-
import * as fse from 'fs-extra';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
import * as os from 'os';
|
|
4
|
-
import { ShellEnvVars, ShellWriteResult, ShellType } from '../types';
|
|
5
|
-
import { CONFIG, getConfigDir, getShellRCFile } from '../config/constants';
|
|
6
|
-
|
|
7
|
-
export class ShellManager {
|
|
8
|
-
private readonly homeDir: string;
|
|
9
|
-
private readonly ccmanDir: string;
|
|
10
|
-
private readonly ccmanrcPath: string;
|
|
11
|
-
|
|
12
|
-
constructor() {
|
|
13
|
-
this.homeDir = os.homedir();
|
|
14
|
-
this.ccmanDir = getConfigDir();
|
|
15
|
-
this.ccmanrcPath = getShellRCFile();
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* 写入环境变量到 CCMan 配置文件并更新 shell 引用
|
|
20
|
-
*/
|
|
21
|
-
async writeToShell(envVars: ShellEnvVars, envName?: string): Promise<ShellWriteResult> {
|
|
22
|
-
try {
|
|
23
|
-
// 1. 写入环境变量到独立的 ccmanrc 文件(通常不会有权限问题)
|
|
24
|
-
await this.writeCCMANRC(envVars, envName);
|
|
25
|
-
|
|
26
|
-
// 2. 检查shell配置文件权限
|
|
27
|
-
const shellPermissionCheck = this.checkShellWritePermissions();
|
|
28
|
-
|
|
29
|
-
// 3. 尝试更新 shell 配置文件引用
|
|
30
|
-
if (shellPermissionCheck.hasWritableShellConfig) {
|
|
31
|
-
try {
|
|
32
|
-
const shellUpdateResult = await this.ensureShellReference();
|
|
33
|
-
return {
|
|
34
|
-
success: true,
|
|
35
|
-
filePath: this.ccmanrcPath,
|
|
36
|
-
message: `环境变量已写入 ${this.ccmanrcPath}${shellUpdateResult.updated ? ` 并${shellUpdateResult.action}shell引用` : ''}`
|
|
37
|
-
};
|
|
38
|
-
} catch (error) {
|
|
39
|
-
// Shell引用失败但有可写配置文件时,提供具体的手动指导
|
|
40
|
-
return this.createManualConfigResult(shellPermissionCheck, String(error));
|
|
41
|
-
}
|
|
42
|
-
} else {
|
|
43
|
-
// 没有可写的shell配置文件,提供完整的手动配置指导
|
|
44
|
-
return this.createManualConfigResult(shellPermissionCheck);
|
|
45
|
-
}
|
|
46
|
-
} catch (error) {
|
|
47
|
-
return {
|
|
48
|
-
success: false,
|
|
49
|
-
filePath: this.ccmanrcPath,
|
|
50
|
-
message: '写入环境变量失败',
|
|
51
|
-
error: String(error)
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* 创建手动配置结果
|
|
58
|
-
*/
|
|
59
|
-
private createManualConfigResult(
|
|
60
|
-
shellPermissionCheck: { shellConfigAccess: { file: string; writable: boolean; error?: string }[] },
|
|
61
|
-
shellError?: string
|
|
62
|
-
): ShellWriteResult {
|
|
63
|
-
const reference = this.generateShellReference().trim();
|
|
64
|
-
const writableFiles = shellPermissionCheck.shellConfigAccess.filter(f => f.writable);
|
|
65
|
-
const nonWritableFiles = shellPermissionCheck.shellConfigAccess.filter(f => !f.writable);
|
|
66
|
-
|
|
67
|
-
let message = `环境变量已写入 ${this.ccmanrcPath},但需要手动配置shell引用。\n\n`;
|
|
68
|
-
|
|
69
|
-
if (writableFiles.length > 0) {
|
|
70
|
-
message += `推荐添加到以下文件之一:\n`;
|
|
71
|
-
writableFiles.forEach(f => {
|
|
72
|
-
message += ` ✅ ${f.file}\n`;
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (nonWritableFiles.length > 0) {
|
|
77
|
-
message += `以下文件无写入权限:\n`;
|
|
78
|
-
nonWritableFiles.forEach(f => {
|
|
79
|
-
message += ` ❌ ${f.file} (${f.error})\n`;
|
|
80
|
-
});
|
|
81
|
-
message += `\n可尝试修复权限:\n`;
|
|
82
|
-
nonWritableFiles.forEach(f => {
|
|
83
|
-
if (f.error === '无写入权限') {
|
|
84
|
-
message += ` chmod 644 ${f.file}\n`;
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
message += `\n需要手动添加的内容:\n${reference}`;
|
|
90
|
-
|
|
91
|
-
return {
|
|
92
|
-
success: true, // ccmanrc写入成功,只是需要手动配置
|
|
93
|
-
filePath: this.ccmanrcPath,
|
|
94
|
-
message,
|
|
95
|
-
error: shellError ? `Shell配置自动更新失败: ${shellError}` : '所有shell配置文件都无写入权限'
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* 检查shell配置文件写入权限
|
|
101
|
-
*/
|
|
102
|
-
private checkShellWritePermissions(): {
|
|
103
|
-
hasWritableShellConfig: boolean;
|
|
104
|
-
shellConfigAccess: { file: string; writable: boolean; error?: string }[];
|
|
105
|
-
} {
|
|
106
|
-
const result = {
|
|
107
|
-
hasWritableShellConfig: false,
|
|
108
|
-
shellConfigAccess: [] as { file: string; writable: boolean; error?: string }[]
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
const shellType = this.detectShell();
|
|
112
|
-
const configFiles = this.getShellConfigFiles(shellType);
|
|
113
|
-
|
|
114
|
-
for (const configFile of configFiles) {
|
|
115
|
-
const fileCheck = { file: configFile, writable: false, error: undefined as string | undefined };
|
|
116
|
-
|
|
117
|
-
try {
|
|
118
|
-
if (fse.pathExistsSync(configFile)) {
|
|
119
|
-
// 文件存在,检查写入权限
|
|
120
|
-
fse.accessSync(configFile, fse.constants.W_OK);
|
|
121
|
-
fileCheck.writable = true;
|
|
122
|
-
result.hasWritableShellConfig = true;
|
|
123
|
-
} else {
|
|
124
|
-
// 文件不存在,检查父目录权限(能否创建文件)
|
|
125
|
-
const dir = path.dirname(configFile);
|
|
126
|
-
if (fse.pathExistsSync(dir)) {
|
|
127
|
-
fse.accessSync(dir, fse.constants.W_OK);
|
|
128
|
-
fileCheck.writable = true;
|
|
129
|
-
result.hasWritableShellConfig = true;
|
|
130
|
-
} else {
|
|
131
|
-
fileCheck.error = '目录不存在';
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
} catch (error: any) {
|
|
135
|
-
fileCheck.error = `无写入权限`;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
result.shellConfigAccess.push(fileCheck);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return result;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* 写入 ccmanrc 文件
|
|
146
|
-
*/
|
|
147
|
-
private async writeCCMANRC(envVars: ShellEnvVars, envName?: string): Promise<void> {
|
|
148
|
-
// 确保 .ccman 目录存在
|
|
149
|
-
fse.ensureDirSync(this.ccmanDir);
|
|
150
|
-
|
|
151
|
-
const content = this.generateExportStatements(envVars, envName);
|
|
152
|
-
await fse.writeFile(this.ccmanrcPath, content, 'utf8');
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* 确保 shell 配置文件中有对 ccmanrc 的引用
|
|
157
|
-
*/
|
|
158
|
-
private async ensureShellReference(): Promise<{ updated: boolean; action: string; filePath?: string }> {
|
|
159
|
-
const shellType = this.detectShell();
|
|
160
|
-
const configFiles = this.getShellConfigFiles(shellType);
|
|
161
|
-
|
|
162
|
-
// 检查是否已经有引用
|
|
163
|
-
for (const configFile of configFiles) {
|
|
164
|
-
if (fse.pathExistsSync(configFile)) {
|
|
165
|
-
const content = fse.readFileSync(configFile, 'utf8');
|
|
166
|
-
if (this.hasShellReference(content)) {
|
|
167
|
-
return { updated: false, action: 'already exists' };
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// 添加引用到主配置文件
|
|
173
|
-
const primaryConfigFile = configFiles[0];
|
|
174
|
-
try {
|
|
175
|
-
await this.addShellReference(primaryConfigFile);
|
|
176
|
-
return {
|
|
177
|
-
updated: true,
|
|
178
|
-
action: 'added',
|
|
179
|
-
filePath: primaryConfigFile
|
|
180
|
-
};
|
|
181
|
-
} catch (error) {
|
|
182
|
-
// 尝试其他配置文件
|
|
183
|
-
for (let i = 1; i < configFiles.length; i++) {
|
|
184
|
-
try {
|
|
185
|
-
await this.addShellReference(configFiles[i]);
|
|
186
|
-
return {
|
|
187
|
-
updated: true,
|
|
188
|
-
action: 'added (fallback)',
|
|
189
|
-
filePath: configFiles[i]
|
|
190
|
-
};
|
|
191
|
-
} catch (fallbackError) {
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
throw new Error('Failed to add shell reference to any configuration file');
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* 添加 shell 引用到配置文件
|
|
201
|
-
*/
|
|
202
|
-
private async addShellReference(configFilePath: string): Promise<void> {
|
|
203
|
-
// 确保目录存在
|
|
204
|
-
const dir = path.dirname(configFilePath);
|
|
205
|
-
fse.ensureDirSync(dir);
|
|
206
|
-
|
|
207
|
-
let content = '';
|
|
208
|
-
if (fse.pathExistsSync(configFilePath)) {
|
|
209
|
-
try {
|
|
210
|
-
content = fse.readFileSync(configFilePath, 'utf8');
|
|
211
|
-
} catch (error: any) {
|
|
212
|
-
if (error.code === 'EACCES' || error.code === 'EPERM') {
|
|
213
|
-
throw new Error(`无权限读取shell配置文件 ${configFilePath}`);
|
|
214
|
-
}
|
|
215
|
-
throw error;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// 添加对 ccmanrc 的引用
|
|
220
|
-
const reference = this.generateShellReference();
|
|
221
|
-
content += reference;
|
|
222
|
-
|
|
223
|
-
try {
|
|
224
|
-
await fse.writeFile(configFilePath, content, 'utf8');
|
|
225
|
-
} catch (error: any) {
|
|
226
|
-
if (error.code === 'EACCES' || error.code === 'EPERM') {
|
|
227
|
-
throw new Error(`无权限修改shell配置文件 ${configFilePath}。\n建议:\n 1. 检查文件权限:chmod 644 ${configFilePath}\n 2. 或手动添加以下内容到该文件:\n${reference.trim()}`);
|
|
228
|
-
}
|
|
229
|
-
throw error;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* 生成 shell 引用代码
|
|
235
|
-
*/
|
|
236
|
-
private generateShellReference(): string {
|
|
237
|
-
return `
|
|
238
|
-
# ${CONFIG.APP_FULL_NAME} - Auto Generated Reference
|
|
239
|
-
# This line sources ${CONFIG.APP_NAME} environment variables from ${this.ccmanrcPath}
|
|
240
|
-
[ -f "${this.ccmanrcPath}" ] && source "${this.ccmanrcPath}"
|
|
241
|
-
# End ${CONFIG.APP_NAME} Reference
|
|
242
|
-
`;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* 检查是否已经有 shell 引用
|
|
247
|
-
*/
|
|
248
|
-
private hasShellReference(content: string): boolean {
|
|
249
|
-
return content.includes(`# ${CONFIG.APP_FULL_NAME} - Auto Generated Reference`) ||
|
|
250
|
-
content.includes(this.ccmanrcPath);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* 从 shell 配置文件中清除 ccmanrc 引用和 ccmanrc 文件
|
|
255
|
-
*/
|
|
256
|
-
async clearFromShell(): Promise<ShellWriteResult> {
|
|
257
|
-
let clearedAny = false;
|
|
258
|
-
let lastError: string | undefined;
|
|
259
|
-
|
|
260
|
-
// 1. 删除 ccmanrc 文件
|
|
261
|
-
if (fse.pathExistsSync(this.ccmanrcPath)) {
|
|
262
|
-
try {
|
|
263
|
-
fse.removeSync(this.ccmanrcPath);
|
|
264
|
-
clearedAny = true;
|
|
265
|
-
} catch (error) {
|
|
266
|
-
lastError = String(error);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// 2. 从 shell 配置文件中移除引用
|
|
271
|
-
const shellType = this.detectShell();
|
|
272
|
-
const configFiles = this.getShellConfigFiles(shellType);
|
|
273
|
-
|
|
274
|
-
for (const configFile of configFiles) {
|
|
275
|
-
try {
|
|
276
|
-
if (fse.pathExistsSync(configFile)) {
|
|
277
|
-
await this.removeShellReference(configFile);
|
|
278
|
-
clearedAny = true;
|
|
279
|
-
}
|
|
280
|
-
} catch (error) {
|
|
281
|
-
lastError = String(error);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
if (clearedAny) {
|
|
286
|
-
return {
|
|
287
|
-
success: true,
|
|
288
|
-
filePath: this.ccmanrcPath,
|
|
289
|
-
message: 'Environment variables and shell references cleared'
|
|
290
|
-
};
|
|
291
|
-
} else {
|
|
292
|
-
return {
|
|
293
|
-
success: false,
|
|
294
|
-
filePath: this.ccmanrcPath,
|
|
295
|
-
message: 'Failed to clear environment variables',
|
|
296
|
-
error: lastError
|
|
297
|
-
};
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* 从配置文件中移除 shell 引用
|
|
303
|
-
*/
|
|
304
|
-
private async removeShellReference(filePath: string): Promise<void> {
|
|
305
|
-
if (!fse.pathExistsSync(filePath)) {
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
const content = fse.readFileSync(filePath, 'utf8');
|
|
310
|
-
const cleanedContent = this.removeShellReferenceFromContent(content);
|
|
311
|
-
|
|
312
|
-
await fse.writeFile(filePath, cleanedContent, 'utf8');
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* 从内容中移除 shell 引用部分
|
|
317
|
-
*/
|
|
318
|
-
private removeShellReferenceFromContent(content: string): string {
|
|
319
|
-
const startMarker = `# ${CONFIG.APP_FULL_NAME} - Auto Generated Reference`;
|
|
320
|
-
const endMarker = `# End ${CONFIG.APP_NAME} Reference`;
|
|
321
|
-
|
|
322
|
-
const lines = content.split('\n');
|
|
323
|
-
const filteredLines: string[] = [];
|
|
324
|
-
let inCCMSection = false;
|
|
325
|
-
|
|
326
|
-
for (const line of lines) {
|
|
327
|
-
if (line.includes(startMarker)) {
|
|
328
|
-
inCCMSection = true;
|
|
329
|
-
continue;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
if (line.includes(endMarker)) {
|
|
333
|
-
inCCMSection = false;
|
|
334
|
-
continue;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
if (!inCCMSection) {
|
|
338
|
-
filteredLines.push(line);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
return filteredLines.join('\n').replace(/\n{3,}/g, '\n\n');
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* 检测当前使用的 shell 类型
|
|
347
|
-
*/
|
|
348
|
-
detectShell(): ShellType {
|
|
349
|
-
const shell = process.env.SHELL || '';
|
|
350
|
-
|
|
351
|
-
if (shell.includes('zsh')) {
|
|
352
|
-
return 'zsh';
|
|
353
|
-
} else if (shell.includes('bash')) {
|
|
354
|
-
return 'bash';
|
|
355
|
-
} else if (shell.includes('fish')) {
|
|
356
|
-
return 'fish';
|
|
357
|
-
} else {
|
|
358
|
-
return 'unknown';
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
/**
|
|
363
|
-
* 获取 shell 配置文件路径列表
|
|
364
|
-
*/
|
|
365
|
-
getShellConfigFiles(shellType: ShellType): string[] {
|
|
366
|
-
const configFiles: string[] = [];
|
|
367
|
-
|
|
368
|
-
switch (shellType) {
|
|
369
|
-
case 'zsh':
|
|
370
|
-
configFiles.push(
|
|
371
|
-
path.join(this.homeDir, '.zshrc'),
|
|
372
|
-
path.join(this.homeDir, '.zprofile')
|
|
373
|
-
);
|
|
374
|
-
break;
|
|
375
|
-
case 'bash':
|
|
376
|
-
configFiles.push(
|
|
377
|
-
path.join(this.homeDir, '.bashrc'),
|
|
378
|
-
path.join(this.homeDir, '.bash_profile'),
|
|
379
|
-
path.join(this.homeDir, '.profile')
|
|
380
|
-
);
|
|
381
|
-
break;
|
|
382
|
-
case 'fish':
|
|
383
|
-
configFiles.push(
|
|
384
|
-
path.join(this.homeDir, '.config/fish/config.fish')
|
|
385
|
-
);
|
|
386
|
-
break;
|
|
387
|
-
default:
|
|
388
|
-
// 默认尝试常见的配置文件
|
|
389
|
-
configFiles.push(
|
|
390
|
-
path.join(this.homeDir, '.zshrc'),
|
|
391
|
-
path.join(this.homeDir, '.bashrc'),
|
|
392
|
-
path.join(this.homeDir, '.profile')
|
|
393
|
-
);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
return configFiles;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
/**
|
|
400
|
-
* 生成环境变量导出语句
|
|
401
|
-
*/
|
|
402
|
-
generateExportStatements(envVars: ShellEnvVars, envName?: string): string {
|
|
403
|
-
const now = new Date();
|
|
404
|
-
const timestamp = now.getFullYear() + '-' +
|
|
405
|
-
String(now.getMonth() + 1).padStart(2, '0') + '-' +
|
|
406
|
-
String(now.getDate()).padStart(2, '0') + ' ' +
|
|
407
|
-
String(now.getHours()).padStart(2, '0') + ':' +
|
|
408
|
-
String(now.getMinutes()).padStart(2, '0') + ':' +
|
|
409
|
-
String(now.getSeconds()).padStart(2, '0');
|
|
410
|
-
const nameComment = envName ? `# Environment: ${envName}` : '';
|
|
411
|
-
|
|
412
|
-
return `
|
|
413
|
-
# ${CONFIG.APP_FULL_NAME} Environment Variables - Auto Generated
|
|
414
|
-
# Generated at: ${timestamp}${nameComment ? '\n' + nameComment : ''}
|
|
415
|
-
export ${CONFIG.ENV_VARS.BASE_URL}="${envVars.ANTHROPIC_BASE_URL}"
|
|
416
|
-
export ${CONFIG.ENV_VARS.AUTH_TOKEN}="${envVars.ANTHROPIC_AUTH_TOKEN}"
|
|
417
|
-
# End ${CONFIG.APP_NAME} Environment Variables
|
|
418
|
-
`;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
/**
|
|
422
|
-
* 检查是否已经写入了环境变量
|
|
423
|
-
*/
|
|
424
|
-
hasEnvVarsInShell(): boolean {
|
|
425
|
-
// 检查 ccmanrc 文件是否存在
|
|
426
|
-
if (fse.pathExistsSync(this.ccmanrcPath)) {
|
|
427
|
-
return true;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// 检查 shell 配置文件中是否有引用
|
|
431
|
-
const shellType = this.detectShell();
|
|
432
|
-
const configFiles = this.getShellConfigFiles(shellType);
|
|
433
|
-
|
|
434
|
-
for (const configFile of configFiles) {
|
|
435
|
-
if (fse.pathExistsSync(configFile)) {
|
|
436
|
-
const content = fse.readFileSync(configFile, 'utf8');
|
|
437
|
-
if (this.hasShellReference(content)) {
|
|
438
|
-
return true;
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
return false;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
/**
|
|
447
|
-
* 自动 source shell 配置文件
|
|
448
|
-
*/
|
|
449
|
-
async autoSourceShell(): Promise<ShellWriteResult> {
|
|
450
|
-
const shellType = this.detectShell();
|
|
451
|
-
const configFiles = this.getShellConfigFiles(shellType);
|
|
452
|
-
|
|
453
|
-
// 找到第一个存在的配置文件
|
|
454
|
-
const activeConfigFile = configFiles.find(file => fse.pathExistsSync(file));
|
|
455
|
-
|
|
456
|
-
if (!activeConfigFile) {
|
|
457
|
-
return {
|
|
458
|
-
success: false,
|
|
459
|
-
filePath: configFiles.join(', '),
|
|
460
|
-
message: 'No shell configuration file found to source',
|
|
461
|
-
error: 'Configuration file not found'
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
try {
|
|
466
|
-
// 使用子进程执行 source 命令
|
|
467
|
-
const { exec } = await import('child_process');
|
|
468
|
-
const { promisify } = await import('util');
|
|
469
|
-
const execAsync = promisify(exec);
|
|
470
|
-
|
|
471
|
-
// 根据不同 shell 类型使用不同的 source 命令
|
|
472
|
-
let sourceCommand: string;
|
|
473
|
-
switch (shellType) {
|
|
474
|
-
case 'zsh':
|
|
475
|
-
sourceCommand = `zsh -c "source ${activeConfigFile}"`;
|
|
476
|
-
break;
|
|
477
|
-
case 'bash':
|
|
478
|
-
sourceCommand = `bash -c "source ${activeConfigFile}"`;
|
|
479
|
-
break;
|
|
480
|
-
case 'fish':
|
|
481
|
-
sourceCommand = `fish -c "source ${activeConfigFile}"`;
|
|
482
|
-
break;
|
|
483
|
-
default:
|
|
484
|
-
sourceCommand = `bash -c "source ${activeConfigFile}"`;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
await execAsync(sourceCommand);
|
|
488
|
-
|
|
489
|
-
return {
|
|
490
|
-
success: true,
|
|
491
|
-
filePath: activeConfigFile,
|
|
492
|
-
message: `Successfully sourced ${activeConfigFile}`
|
|
493
|
-
};
|
|
494
|
-
} catch (error) {
|
|
495
|
-
return {
|
|
496
|
-
success: false,
|
|
497
|
-
filePath: activeConfigFile,
|
|
498
|
-
message: 'Failed to source shell configuration file',
|
|
499
|
-
error: String(error)
|
|
500
|
-
};
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
/**
|
|
505
|
-
* 获取当前 shell 信息
|
|
506
|
-
*/
|
|
507
|
-
getShellInfo(): {
|
|
508
|
-
shellType: ShellType;
|
|
509
|
-
shellPath: string;
|
|
510
|
-
configFiles: string[];
|
|
511
|
-
activeConfigFile?: string;
|
|
512
|
-
} {
|
|
513
|
-
const shellType = this.detectShell();
|
|
514
|
-
const configFiles = this.getShellConfigFiles(shellType);
|
|
515
|
-
|
|
516
|
-
// 找到第一个存在的配置文件作为活动配置文件
|
|
517
|
-
const activeConfigFile = configFiles.find(file => fse.pathExistsSync(file));
|
|
518
|
-
|
|
519
|
-
return {
|
|
520
|
-
shellType,
|
|
521
|
-
shellPath: process.env.SHELL || 'unknown',
|
|
522
|
-
configFiles,
|
|
523
|
-
activeConfigFile
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
}
|