code-abyss 1.6.0 → 1.6.2
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/README.md +11 -18
- package/bin/install.js +182 -171
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -20,18 +20,16 @@
|
|
|
20
20
|
npx code-abyss
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
交互式菜单(方向键选择,回车确认):
|
|
24
24
|
|
|
25
25
|
```
|
|
26
|
-
☠️ Code Abyss v1.6.
|
|
26
|
+
☠️ Code Abyss v1.6.1
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
选择 [1/2/3/4]:
|
|
28
|
+
? 请选择操作 (Use arrow keys)
|
|
29
|
+
❯ 安装到 Claude Code (~/.claude/)
|
|
30
|
+
安装到 Codex CLI (~/.codex/)
|
|
31
|
+
卸载 Claude Code
|
|
32
|
+
卸载 Codex CLI
|
|
35
33
|
```
|
|
36
34
|
|
|
37
35
|
也可以直接指定:
|
|
@@ -61,17 +59,12 @@ npx code-abyss --uninstall codex # 卸载 Codex CLI
|
|
|
61
59
|
|
|
62
60
|
未检测到认证时会提示配置,可交互输入或跳过。
|
|
63
61
|
|
|
64
|
-
|
|
62
|
+
然后进入可选配置(空格选择,回车确认):
|
|
65
63
|
|
|
66
64
|
```
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
[1] 写入推荐 settings.json (精细合并,保留现有配置)
|
|
71
|
-
[2] 安装 ccline 状态栏 (需要 Nerd Font 字体)
|
|
72
|
-
[3] 全部跳过
|
|
73
|
-
|
|
74
|
-
选择 (多选用逗号分隔,如 1,2) [3]:
|
|
65
|
+
? 选择要安装的配置 (Press <space> to select, <enter> to submit)
|
|
66
|
+
◉ 精细合并推荐 settings.json (保留现有配置)
|
|
67
|
+
◯ 安装 ccline 状态栏 (需要 Nerd Font)
|
|
75
68
|
```
|
|
76
69
|
|
|
77
70
|
- **settings.json 精细合并**:逐项合并推荐配置,已有的 key 不覆盖,缺失的 key 补上
|
package/bin/install.js
CHANGED
|
@@ -3,14 +3,54 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const os = require('os');
|
|
6
|
-
const readline = require('readline');
|
|
7
6
|
|
|
8
|
-
const VERSION = '1.6.
|
|
7
|
+
const VERSION = '1.6.2';
|
|
9
8
|
const HOME = os.homedir();
|
|
10
9
|
const SKIP = ['__pycache__', '.pyc', '.pyo', '.egg-info', '.DS_Store', 'Thumbs.db', '.git'];
|
|
11
10
|
const PKG_ROOT = path.join(__dirname, '..');
|
|
12
11
|
|
|
13
|
-
// ──
|
|
12
|
+
// ── ANSI ──
|
|
13
|
+
|
|
14
|
+
const c = {
|
|
15
|
+
b: s => `\x1b[1m${s}\x1b[0m`,
|
|
16
|
+
d: s => `\x1b[2m${s}\x1b[0m`,
|
|
17
|
+
red: s => `\x1b[31m${s}\x1b[0m`,
|
|
18
|
+
grn: s => `\x1b[32m${s}\x1b[0m`,
|
|
19
|
+
ylw: s => `\x1b[33m${s}\x1b[0m`,
|
|
20
|
+
blu: s => `\x1b[34m${s}\x1b[0m`,
|
|
21
|
+
mag: s => `\x1b[35m${s}\x1b[0m`,
|
|
22
|
+
cyn: s => `\x1b[36m${s}\x1b[0m`,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function banner() {
|
|
26
|
+
console.log(c.mag(`
|
|
27
|
+
██████╗ ██████╗ ██████╗ ███████╗
|
|
28
|
+
██╔════╝██╔═══██╗██╔══██╗██╔════╝
|
|
29
|
+
██║ ██║ ██║██║ ██║█████╗
|
|
30
|
+
██║ ██║ ██║██║ ██║██╔══╝
|
|
31
|
+
╚██████╗╚██████╔╝██████╔╝███████╗
|
|
32
|
+
╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝
|
|
33
|
+
█████╗ ██████╗ ██╗ ██╗███████╗███████╗
|
|
34
|
+
██╔══██╗██╔══██╗╚██╗ ██╔╝██╔════╝██╔════╝
|
|
35
|
+
███████║██████╔╝ ╚████╔╝ ███████╗███████╗
|
|
36
|
+
██╔══██║██╔══██╗ ╚██╔╝ ╚════██║╚════██║
|
|
37
|
+
██║ ██║██████╔╝ ██║ ███████║███████║
|
|
38
|
+
╚═╝ ╚═╝╚═════╝ ╚═╝ ╚══════╝╚══════╝`));
|
|
39
|
+
console.log(c.d(` ☠️ 邪修红尘仙 · 宿命深渊 v${VERSION}\n`));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function divider(title) {
|
|
43
|
+
const line = '─'.repeat(44);
|
|
44
|
+
console.log(`\n${c.d('┌' + line + '┐')}\n${c.d('│')} ${c.b(title)}${' '.repeat(Math.max(0, 43 - title.length))}${c.d('│')}\n${c.d('└' + line + '┘')}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function step(n, total, msg) { console.log(`\n ${c.cyn(`[${n}/${total}]`)} ${c.b(msg)}`); }
|
|
48
|
+
function ok(msg) { console.log(` ${c.grn('✔')} ${msg}`); }
|
|
49
|
+
function warn(msg) { console.log(` ${c.ylw('⚠')} ${msg}`); }
|
|
50
|
+
function info(msg) { console.log(` ${c.blu('ℹ')} ${msg}`); }
|
|
51
|
+
function fail(msg) { console.log(` ${c.red('✘')} ${msg}`); }
|
|
52
|
+
|
|
53
|
+
// ── 工具 ──
|
|
14
54
|
|
|
15
55
|
function shouldSkip(name) { return SKIP.some(p => name.includes(p)); }
|
|
16
56
|
|
|
@@ -32,62 +72,59 @@ function rmSafe(p) {
|
|
|
32
72
|
if (fs.existsSync(p)) fs.rmSync(p, { recursive: true, force: true });
|
|
33
73
|
}
|
|
34
74
|
|
|
35
|
-
function ask(rl, q) {
|
|
36
|
-
return new Promise(r => rl.question(q, r));
|
|
37
|
-
}
|
|
38
|
-
|
|
39
75
|
function deepMergeNew(target, source, prefix, log) {
|
|
40
76
|
for (const key of Object.keys(source)) {
|
|
41
77
|
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
42
78
|
if (typeof source[key] === 'object' && source[key] !== null && !Array.isArray(source[key])) {
|
|
43
79
|
if (!target[key] || typeof target[key] !== 'object') {
|
|
44
80
|
target[key] = {};
|
|
45
|
-
log.push(
|
|
81
|
+
log.push({ k: fullKey, a: 'new', v: '{}' });
|
|
46
82
|
}
|
|
47
83
|
deepMergeNew(target[key], source[key], fullKey, log);
|
|
48
84
|
} else if (Array.isArray(source[key]) && Array.isArray(target[key])) {
|
|
49
85
|
const added = source[key].filter(v => !target[key].includes(v));
|
|
50
86
|
if (added.length > 0) {
|
|
51
87
|
target[key] = [...target[key], ...added];
|
|
52
|
-
log.push(
|
|
88
|
+
log.push({ k: fullKey, a: 'add', v: `+${added.length}` });
|
|
53
89
|
} else {
|
|
54
|
-
log.push(
|
|
90
|
+
log.push({ k: fullKey, a: 'keep', v: '完整' });
|
|
55
91
|
}
|
|
56
92
|
} else if (key in target) {
|
|
57
|
-
log.push(
|
|
93
|
+
log.push({ k: fullKey, a: 'keep', v: JSON.stringify(target[key]) });
|
|
58
94
|
} else {
|
|
59
95
|
target[key] = source[key];
|
|
60
|
-
log.push(
|
|
96
|
+
log.push({ k: fullKey, a: 'set', v: JSON.stringify(source[key]) });
|
|
61
97
|
}
|
|
62
98
|
}
|
|
63
99
|
return target;
|
|
64
100
|
}
|
|
65
101
|
|
|
66
|
-
|
|
102
|
+
function printMergeLog(log) {
|
|
103
|
+
log.forEach(({ k, a, v }) => {
|
|
104
|
+
if (a === 'keep') console.log(` ${c.d('·')} ${c.d(`${k} (保留: ${v})`)}`);
|
|
105
|
+
else console.log(` ${c.grn('+')} ${c.cyn(k)} = ${v}`);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ── 认证 ──
|
|
67
110
|
|
|
68
111
|
function detectClaudeAuth(settings) {
|
|
69
|
-
// 1. settings.json 中有自定义 provider
|
|
70
112
|
const env = settings.env || {};
|
|
71
113
|
if (env.ANTHROPIC_BASE_URL && env.ANTHROPIC_AUTH_TOKEN) return { type: 'custom', detail: env.ANTHROPIC_BASE_URL };
|
|
72
|
-
// 2. 环境变量中有官方 key
|
|
73
114
|
if (process.env.ANTHROPIC_API_KEY) return { type: 'env', detail: 'ANTHROPIC_API_KEY' };
|
|
74
|
-
// 3. 环境变量中有自定义 provider
|
|
75
115
|
if (process.env.ANTHROPIC_BASE_URL && process.env.ANTHROPIC_AUTH_TOKEN) return { type: 'env-custom', detail: process.env.ANTHROPIC_BASE_URL };
|
|
76
|
-
// 4. 已通过 claude login 登录
|
|
77
116
|
const cred = path.join(HOME, '.claude', '.credentials.json');
|
|
78
117
|
if (fs.existsSync(cred)) {
|
|
79
118
|
try {
|
|
80
|
-
const
|
|
81
|
-
if (
|
|
119
|
+
const cc = JSON.parse(fs.readFileSync(cred, 'utf8'));
|
|
120
|
+
if (cc.claudeAiOauth || cc.apiKey) return { type: 'login', detail: 'claude login' };
|
|
82
121
|
} catch (e) {}
|
|
83
122
|
}
|
|
84
123
|
return null;
|
|
85
124
|
}
|
|
86
125
|
|
|
87
126
|
function detectCodexAuth() {
|
|
88
|
-
// 1. 环境变量
|
|
89
127
|
if (process.env.OPENAI_API_KEY) return { type: 'env', detail: 'OPENAI_API_KEY' };
|
|
90
|
-
// 2. auth.json
|
|
91
128
|
const auth = path.join(HOME, '.codex', 'auth.json');
|
|
92
129
|
if (fs.existsSync(auth)) {
|
|
93
130
|
try {
|
|
@@ -95,11 +132,9 @@ function detectCodexAuth() {
|
|
|
95
132
|
if (a.token || a.api_key) return { type: 'login', detail: 'codex login' };
|
|
96
133
|
} catch (e) {}
|
|
97
134
|
}
|
|
98
|
-
// 3. config.toml 中有自定义 provider
|
|
99
135
|
const cfg = path.join(HOME, '.codex', 'config.toml');
|
|
100
136
|
if (fs.existsSync(cfg)) {
|
|
101
|
-
|
|
102
|
-
if (content.includes('base_url')) return { type: 'custom', detail: 'config.toml' };
|
|
137
|
+
if (fs.readFileSync(cfg, 'utf8').includes('base_url')) return { type: 'custom', detail: 'config.toml' };
|
|
103
138
|
}
|
|
104
139
|
return null;
|
|
105
140
|
}
|
|
@@ -145,22 +180,19 @@ for (let i = 0; i < args.length; i++) {
|
|
|
145
180
|
else if (args[i] === '--uninstall' && args[i + 1]) { uninstallTarget = args[++i]; }
|
|
146
181
|
else if (args[i] === '--yes' || args[i] === '-y') { autoYes = true; }
|
|
147
182
|
else if (args[i] === '--help' || args[i] === '-h') {
|
|
148
|
-
|
|
149
|
-
|
|
183
|
+
banner();
|
|
184
|
+
console.log(`${c.b('用法:')} npx code-abyss [选项]
|
|
150
185
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
--target <claude|codex> 安装目标
|
|
156
|
-
--uninstall <claude|codex> 卸载目标
|
|
157
|
-
--yes, -y 全自动模式 (跳过所有可选提示)
|
|
186
|
+
${c.b('选项:')}
|
|
187
|
+
--target ${c.cyn('<claude|codex>')} 安装目标
|
|
188
|
+
--uninstall ${c.cyn('<claude|codex>')} 卸载目标
|
|
189
|
+
--yes, -y 全自动模式
|
|
158
190
|
--help, -h 显示帮助
|
|
159
191
|
|
|
160
|
-
示例:
|
|
161
|
-
npx code-abyss # 交互菜单
|
|
162
|
-
npx code-abyss --target claude -y # 零配置一键安装
|
|
163
|
-
npx code-abyss --uninstall claude # 直接卸载
|
|
192
|
+
${c.b('示例:')}
|
|
193
|
+
npx code-abyss ${c.d('# 交互菜单')}
|
|
194
|
+
npx code-abyss --target claude -y ${c.d('# 零配置一键安装')}
|
|
195
|
+
npx code-abyss --uninstall claude ${c.d('# 直接卸载')}
|
|
164
196
|
`);
|
|
165
197
|
process.exit(0);
|
|
166
198
|
}
|
|
@@ -169,36 +201,30 @@ for (let i = 0; i < args.length; i++) {
|
|
|
169
201
|
// ── 卸载 ──
|
|
170
202
|
|
|
171
203
|
function runUninstall(tgt) {
|
|
172
|
-
if (!['claude', 'codex'].includes(tgt)) {
|
|
173
|
-
console.error('❌ --uninstall 必须是 claude 或 codex');
|
|
174
|
-
process.exit(1);
|
|
175
|
-
}
|
|
204
|
+
if (!['claude', 'codex'].includes(tgt)) { fail('--uninstall 必须是 claude 或 codex'); process.exit(1); }
|
|
176
205
|
const targetDir = path.join(HOME, `.${tgt}`);
|
|
177
206
|
const backupDir = path.join(targetDir, '.sage-backup');
|
|
178
207
|
const manifestPath = path.join(backupDir, 'manifest.json');
|
|
179
|
-
|
|
180
|
-
if (!fs.existsSync(manifestPath)) {
|
|
181
|
-
console.error(`❌ 未找到安装记录: ${manifestPath}`);
|
|
182
|
-
process.exit(1);
|
|
183
|
-
}
|
|
208
|
+
if (!fs.existsSync(manifestPath)) { fail(`未找到安装记录: ${manifestPath}`); process.exit(1); }
|
|
184
209
|
|
|
185
210
|
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
186
|
-
|
|
211
|
+
divider(`卸载 Code Abyss v${manifest.version}`);
|
|
187
212
|
|
|
188
213
|
(manifest.installed || []).forEach(f => {
|
|
189
214
|
const p = path.join(targetDir, f);
|
|
190
|
-
if (fs.existsSync(p)) { rmSafe(p); console.log(
|
|
215
|
+
if (fs.existsSync(p)) { rmSafe(p); console.log(` ${c.red('✘')} ${f}`); }
|
|
191
216
|
});
|
|
192
217
|
(manifest.backups || []).forEach(f => {
|
|
193
218
|
const bp = path.join(backupDir, f);
|
|
194
219
|
const tp = path.join(targetDir, f);
|
|
195
|
-
if (fs.existsSync(bp)) { fs.renameSync(bp, tp);
|
|
220
|
+
if (fs.existsSync(bp)) { fs.renameSync(bp, tp); ok(`恢复: ${f}`); }
|
|
196
221
|
});
|
|
197
222
|
|
|
198
223
|
rmSafe(backupDir);
|
|
199
224
|
const us = path.join(targetDir, '.sage-uninstall.js');
|
|
200
225
|
if (fs.existsSync(us)) fs.unlinkSync(us);
|
|
201
|
-
console.log('
|
|
226
|
+
console.log('');
|
|
227
|
+
ok(c.b('卸载完成\n'));
|
|
202
228
|
}
|
|
203
229
|
|
|
204
230
|
// ── 安装核心 ──
|
|
@@ -208,7 +234,7 @@ function installCore(tgt) {
|
|
|
208
234
|
const backupDir = path.join(targetDir, '.sage-backup');
|
|
209
235
|
const manifestPath = path.join(backupDir, 'manifest.json');
|
|
210
236
|
|
|
211
|
-
|
|
237
|
+
step(1, 3, `安装核心文件 → ${c.cyn(targetDir)}`);
|
|
212
238
|
fs.mkdirSync(backupDir, { recursive: true });
|
|
213
239
|
|
|
214
240
|
const filesToInstall = [
|
|
@@ -218,26 +244,21 @@ function installCore(tgt) {
|
|
|
218
244
|
{ src: 'skills', dest: 'skills' }
|
|
219
245
|
].filter(f => f.dest !== null);
|
|
220
246
|
|
|
221
|
-
const manifest = {
|
|
222
|
-
version: VERSION, target: tgt,
|
|
223
|
-
timestamp: new Date().toISOString(),
|
|
224
|
-
installed: [], backups: []
|
|
225
|
-
};
|
|
247
|
+
const manifest = { version: VERSION, target: tgt, timestamp: new Date().toISOString(), installed: [], backups: [] };
|
|
226
248
|
|
|
227
249
|
filesToInstall.forEach(({ src, dest }) => {
|
|
228
250
|
const srcPath = path.join(PKG_ROOT, src);
|
|
229
251
|
const destPath = path.join(targetDir, dest);
|
|
230
|
-
if (!fs.existsSync(srcPath)) {
|
|
252
|
+
if (!fs.existsSync(srcPath)) { warn(`跳过: ${src}`); return; }
|
|
231
253
|
if (fs.existsSync(destPath)) {
|
|
232
254
|
const bp = path.join(backupDir, dest);
|
|
233
|
-
console.log(`📦 备份: ${dest}`);
|
|
234
255
|
rmSafe(bp); copyRecursive(destPath, bp); manifest.backups.push(dest);
|
|
256
|
+
info(`备份: ${c.d(dest)}`);
|
|
235
257
|
}
|
|
236
|
-
|
|
258
|
+
ok(dest);
|
|
237
259
|
rmSafe(destPath); copyRecursive(srcPath, destPath); manifest.installed.push(dest);
|
|
238
260
|
});
|
|
239
261
|
|
|
240
|
-
// settings.json 最小写入
|
|
241
262
|
const settingsPath = path.join(targetDir, 'settings.json');
|
|
242
263
|
let settings = {};
|
|
243
264
|
if (fs.existsSync(settingsPath)) {
|
|
@@ -247,84 +268,79 @@ function installCore(tgt) {
|
|
|
247
268
|
}
|
|
248
269
|
if (tgt === 'claude') {
|
|
249
270
|
settings.outputStyle = 'abyss-cultivator';
|
|
250
|
-
|
|
271
|
+
ok(`outputStyle = ${c.mag('abyss-cultivator')}`);
|
|
251
272
|
}
|
|
252
273
|
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
253
274
|
manifest.installed.push('settings.json');
|
|
254
|
-
|
|
255
275
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
|
|
256
276
|
|
|
257
|
-
// 备用卸载脚本
|
|
258
277
|
const uSrc = path.join(PKG_ROOT, 'bin', 'uninstall.js');
|
|
259
278
|
const uDest = path.join(targetDir, '.sage-uninstall.js');
|
|
260
279
|
if (fs.existsSync(uSrc)) { fs.copyFileSync(uSrc, uDest); fs.chmodSync(uDest, '755'); }
|
|
261
280
|
|
|
262
|
-
console.log(`\n✅ 核心文件安装完成\n`);
|
|
263
281
|
return { targetDir, settingsPath, settings, manifest, manifestPath };
|
|
264
282
|
}
|
|
265
283
|
|
|
266
|
-
// ── Claude
|
|
284
|
+
// ── Claude 后续 ──
|
|
285
|
+
|
|
286
|
+
async function postClaude(ctx) {
|
|
287
|
+
const { select, checkbox, confirm, input } = require('@inquirer/prompts');
|
|
267
288
|
|
|
268
|
-
|
|
269
|
-
// 认证检测
|
|
289
|
+
step(2, 3, '认证检测');
|
|
270
290
|
const auth = detectClaudeAuth(ctx.settings);
|
|
271
|
-
console.log('── 认证检测 ──');
|
|
272
291
|
if (auth) {
|
|
273
|
-
|
|
292
|
+
ok(`${c.b(auth.type)} → ${auth.detail}`);
|
|
274
293
|
} else {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
console.log(' a) claude login (官方账号)');
|
|
278
|
-
console.log(' b) 环境变量 ANTHROPIC_API_KEY');
|
|
279
|
-
console.log(' c) 自定义 provider (base_url + token)');
|
|
294
|
+
warn('未检测到 API 认证');
|
|
295
|
+
info(`支持: ${c.cyn('claude login')} | ${c.cyn('ANTHROPIC_API_KEY')} | ${c.cyn('自定义 provider')}`);
|
|
280
296
|
if (!autoYes) {
|
|
281
|
-
const
|
|
282
|
-
if (
|
|
297
|
+
const doCfg = await confirm({ message: '配置自定义 provider?', default: false });
|
|
298
|
+
if (doCfg) {
|
|
283
299
|
if (!ctx.settings.env) ctx.settings.env = {};
|
|
284
|
-
const url =
|
|
285
|
-
const token =
|
|
300
|
+
const url = await input({ message: 'ANTHROPIC_BASE_URL:' });
|
|
301
|
+
const token = await input({ message: 'ANTHROPIC_AUTH_TOKEN:' });
|
|
286
302
|
if (url) ctx.settings.env.ANTHROPIC_BASE_URL = url;
|
|
287
303
|
if (token) ctx.settings.env.ANTHROPIC_AUTH_TOKEN = token;
|
|
288
304
|
fs.writeFileSync(ctx.settingsPath, JSON.stringify(ctx.settings, null, 2) + '\n');
|
|
289
|
-
|
|
305
|
+
ok('provider 已配置');
|
|
290
306
|
}
|
|
291
307
|
}
|
|
292
308
|
}
|
|
293
309
|
|
|
294
|
-
|
|
310
|
+
step(3, 3, '可选配置');
|
|
295
311
|
if (autoYes) {
|
|
296
|
-
|
|
297
|
-
console.log('\n── 自动配置 (--yes) ──');
|
|
312
|
+
info('自动模式: 合并推荐配置');
|
|
298
313
|
const log = [];
|
|
299
314
|
deepMergeNew(ctx.settings, SETTINGS_TEMPLATE, '', log);
|
|
300
|
-
|
|
315
|
+
printMergeLog(log);
|
|
301
316
|
fs.writeFileSync(ctx.settingsPath, JSON.stringify(ctx.settings, null, 2) + '\n');
|
|
302
|
-
|
|
317
|
+
ok('settings.json 合并完成');
|
|
303
318
|
return;
|
|
304
319
|
}
|
|
305
320
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
321
|
+
const choices = await checkbox({
|
|
322
|
+
message: '选择要安装的配置 (空格选择, 回车确认)',
|
|
323
|
+
choices: [
|
|
324
|
+
{ name: '精细合并推荐 settings.json (保留现有配置)', value: 'settings', checked: true },
|
|
325
|
+
{ name: '安装 ccline 状态栏 (需要 Nerd Font)', value: 'ccline' },
|
|
326
|
+
],
|
|
327
|
+
});
|
|
312
328
|
|
|
313
|
-
if (choices.includes('
|
|
314
|
-
console.log('\n📋 精细合并 settings.json...\n');
|
|
329
|
+
if (choices.includes('settings')) {
|
|
315
330
|
const log = [];
|
|
316
331
|
deepMergeNew(ctx.settings, SETTINGS_TEMPLATE, '', log);
|
|
317
|
-
|
|
332
|
+
printMergeLog(log);
|
|
318
333
|
fs.writeFileSync(ctx.settingsPath, JSON.stringify(ctx.settings, null, 2) + '\n');
|
|
319
|
-
|
|
334
|
+
ok('settings.json 合并完成');
|
|
320
335
|
}
|
|
321
|
-
if (choices.includes('
|
|
336
|
+
if (choices.includes('ccline')) {
|
|
322
337
|
await installCcline(ctx);
|
|
323
338
|
}
|
|
324
339
|
}
|
|
325
340
|
|
|
326
341
|
async function installCcline(ctx) {
|
|
327
|
-
console.log('
|
|
342
|
+
console.log('');
|
|
343
|
+
info('安装 ccline 状态栏...');
|
|
328
344
|
const { execSync } = require('child_process');
|
|
329
345
|
const cclineBin = path.join(HOME, '.claude', 'ccline', 'ccline');
|
|
330
346
|
|
|
@@ -333,91 +349,83 @@ async function installCcline(ctx) {
|
|
|
333
349
|
if (!installed && fs.existsSync(cclineBin)) installed = true;
|
|
334
350
|
|
|
335
351
|
if (!installed) {
|
|
336
|
-
|
|
352
|
+
info('ccline 未检测到,正在安装...');
|
|
337
353
|
try {
|
|
338
354
|
execSync('npm install -g @cometix/ccline', { stdio: 'inherit' });
|
|
339
355
|
installed = true;
|
|
340
|
-
|
|
356
|
+
ok('ccline 安装成功');
|
|
341
357
|
} catch (e) {
|
|
342
|
-
|
|
343
|
-
|
|
358
|
+
warn('npm install -g @cometix/ccline 失败');
|
|
359
|
+
info(`手动: ${c.cyn('https://github.com/Haleclipse/CCometixLine/releases')}`);
|
|
344
360
|
}
|
|
345
361
|
} else {
|
|
346
|
-
|
|
362
|
+
ok('ccline 已安装');
|
|
347
363
|
}
|
|
348
364
|
|
|
349
365
|
const cclineConfig = path.join(HOME, '.claude', 'ccline', 'config.toml');
|
|
350
366
|
if (installed && !fs.existsSync(cclineConfig)) {
|
|
351
|
-
try { execSync('ccline --init', { stdio: 'inherit' });
|
|
352
|
-
catch (e) {
|
|
367
|
+
try { execSync('ccline --init', { stdio: 'inherit' }); ok('ccline 默认配置已生成'); }
|
|
368
|
+
catch (e) { warn('ccline --init 失败,可手动运行'); }
|
|
353
369
|
} else if (fs.existsSync(cclineConfig)) {
|
|
354
|
-
|
|
370
|
+
ok('ccline/config.toml (已存在)');
|
|
355
371
|
}
|
|
356
372
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
log.forEach(l => console.log(l));
|
|
373
|
+
ctx.settings.statusLine = CCLINE_STATUS_LINE.statusLine;
|
|
374
|
+
ok(`statusLine → ${c.cyn(CCLINE_STATUS_LINE.statusLine.command)}`);
|
|
360
375
|
fs.writeFileSync(ctx.settingsPath, JSON.stringify(ctx.settings, null, 2) + '\n');
|
|
361
376
|
|
|
362
|
-
console.log(
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
console.log('✅ ccline 配置完成');
|
|
377
|
+
console.log('');
|
|
378
|
+
warn(`需要 ${c.b('Nerd Font')} 字体`);
|
|
379
|
+
info(`推荐: FiraCode Nerd Font / JetBrainsMono Nerd Font`);
|
|
380
|
+
info(`下载: ${c.cyn('https://www.nerdfonts.com/')}`);
|
|
381
|
+
info(`配置: ${c.cyn('ccline --config')}`);
|
|
382
|
+
ok('ccline 配置完成');
|
|
369
383
|
}
|
|
370
384
|
|
|
371
|
-
// ── Codex
|
|
385
|
+
// ── Codex 后续 ──
|
|
372
386
|
|
|
373
|
-
async function postCodex(
|
|
387
|
+
async function postCodex() {
|
|
388
|
+
const { select, confirm, input } = require('@inquirer/prompts');
|
|
374
389
|
const cfgPath = path.join(HOME, '.codex', 'config.toml');
|
|
375
390
|
const exists = fs.existsSync(cfgPath);
|
|
376
391
|
|
|
377
|
-
|
|
392
|
+
step(2, 3, '认证检测');
|
|
378
393
|
const auth = detectCodexAuth();
|
|
379
|
-
console.log('── 认证检测 ──');
|
|
380
394
|
if (auth) {
|
|
381
|
-
|
|
395
|
+
ok(`${c.b(auth.type)} → ${auth.detail}`);
|
|
382
396
|
} else {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
console.log(' a) codex login (官方账号)');
|
|
386
|
-
console.log(' b) 环境变量 OPENAI_API_KEY');
|
|
387
|
-
console.log(' c) 自定义 provider (config.toml 中配置 base_url)');
|
|
397
|
+
warn('未检测到 API 认证');
|
|
398
|
+
info(`支持: ${c.cyn('codex login')} | ${c.cyn('OPENAI_API_KEY')} | ${c.cyn('自定义 provider')}`);
|
|
388
399
|
}
|
|
389
400
|
|
|
401
|
+
step(3, 3, '可选配置');
|
|
390
402
|
if (autoYes) {
|
|
391
|
-
// 全自动:不存在则写入模板
|
|
392
403
|
if (!exists) {
|
|
393
404
|
const src = path.join(PKG_ROOT, 'config', 'codex-config.example.toml');
|
|
394
405
|
if (fs.existsSync(src)) {
|
|
395
406
|
fs.copyFileSync(src, cfgPath);
|
|
396
|
-
|
|
397
|
-
|
|
407
|
+
ok('写入: ~/.codex/config.toml (模板)');
|
|
408
|
+
warn('请编辑 base_url 和 model');
|
|
398
409
|
}
|
|
399
410
|
} else {
|
|
400
|
-
|
|
411
|
+
ok('config.toml 已存在');
|
|
401
412
|
}
|
|
402
413
|
return;
|
|
403
414
|
}
|
|
404
415
|
|
|
405
416
|
if (!exists) {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
const answer = (await ask(rl, '\n选择 [1/2] [2]: ')).trim() || '2';
|
|
410
|
-
if (answer === '1') {
|
|
417
|
+
warn('未检测到 ~/.codex/config.toml');
|
|
418
|
+
const doWrite = await confirm({ message: '写入推荐 config.toml (含自定义 provider 模板)?', default: true });
|
|
419
|
+
if (doWrite) {
|
|
411
420
|
const src = path.join(PKG_ROOT, 'config', 'codex-config.example.toml');
|
|
412
421
|
if (fs.existsSync(src)) {
|
|
413
422
|
fs.copyFileSync(src, cfgPath);
|
|
414
|
-
|
|
415
|
-
|
|
423
|
+
ok('写入: ~/.codex/config.toml');
|
|
424
|
+
warn('请编辑 base_url 和 model');
|
|
416
425
|
}
|
|
417
|
-
console.log('✅ Codex 配置完成\n');
|
|
418
426
|
}
|
|
419
427
|
} else {
|
|
420
|
-
|
|
428
|
+
ok('config.toml 已存在');
|
|
421
429
|
}
|
|
422
430
|
}
|
|
423
431
|
|
|
@@ -426,51 +434,54 @@ async function postCodex(rl) {
|
|
|
426
434
|
async function main() {
|
|
427
435
|
if (uninstallTarget) { runUninstall(uninstallTarget); return; }
|
|
428
436
|
|
|
437
|
+
const { select } = require('@inquirer/prompts');
|
|
438
|
+
banner();
|
|
439
|
+
|
|
429
440
|
if (target) {
|
|
430
|
-
if (!['claude', 'codex'].includes(target)) {
|
|
431
|
-
console.error('❌ --target 必须是 claude 或 codex');
|
|
432
|
-
process.exit(1);
|
|
433
|
-
}
|
|
434
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
441
|
+
if (!['claude', 'codex'].includes(target)) { fail('--target 必须是 claude 或 codex'); process.exit(1); }
|
|
435
442
|
const ctx = installCore(target);
|
|
436
|
-
if (target === 'claude') await postClaude(
|
|
437
|
-
else await postCodex(
|
|
438
|
-
|
|
439
|
-
finish(target);
|
|
443
|
+
if (target === 'claude') await postClaude(ctx);
|
|
444
|
+
else await postCodex();
|
|
445
|
+
finish(ctx);
|
|
440
446
|
return;
|
|
441
447
|
}
|
|
442
448
|
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
449
|
+
const action = await select({
|
|
450
|
+
message: '请选择操作',
|
|
451
|
+
choices: [
|
|
452
|
+
{ name: `安装到 Claude Code ${c.d('(~/.claude/')}${c.d(')')}`, value: 'install-claude' },
|
|
453
|
+
{ name: `安装到 Codex CLI ${c.d('(~/.codex/')}${c.d(')')}`, value: 'install-codex' },
|
|
454
|
+
{ name: `${c.red('卸载')} Claude Code`, value: 'uninstall-claude' },
|
|
455
|
+
{ name: `${c.red('卸载')} Codex CLI`, value: 'uninstall-codex' },
|
|
456
|
+
],
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
switch (action) {
|
|
460
|
+
case 'install-claude': {
|
|
454
461
|
const ctx = installCore('claude');
|
|
455
|
-
await postClaude(
|
|
456
|
-
|
|
462
|
+
await postClaude(ctx);
|
|
463
|
+
finish(ctx); break;
|
|
457
464
|
}
|
|
458
|
-
case '
|
|
465
|
+
case 'install-codex': {
|
|
459
466
|
const ctx = installCore('codex');
|
|
460
|
-
await postCodex(
|
|
461
|
-
|
|
467
|
+
await postCodex();
|
|
468
|
+
finish(ctx); break;
|
|
462
469
|
}
|
|
463
|
-
case '
|
|
464
|
-
case '
|
|
465
|
-
default: rl.close(); console.error('❌ 无效选择'); process.exit(1);
|
|
470
|
+
case 'uninstall-claude': runUninstall('claude'); break;
|
|
471
|
+
case 'uninstall-codex': runUninstall('codex'); break;
|
|
466
472
|
}
|
|
467
473
|
}
|
|
468
474
|
|
|
469
|
-
function finish(
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
console.log(
|
|
473
|
-
console.log(
|
|
475
|
+
function finish(ctx) {
|
|
476
|
+
const tgt = ctx.manifest.target;
|
|
477
|
+
divider('安装完成');
|
|
478
|
+
console.log('');
|
|
479
|
+
console.log(` ${c.b('目标:')} ${c.cyn(ctx.targetDir)}`);
|
|
480
|
+
console.log(` ${c.b('版本:')} v${VERSION}`);
|
|
481
|
+
console.log(` ${c.b('文件:')} ${ctx.manifest.installed.length} 个安装, ${ctx.manifest.backups.length} 个备份`);
|
|
482
|
+
console.log(` ${c.b('卸载:')} ${c.d(`npx code-abyss --uninstall ${tgt}`)}`);
|
|
483
|
+
console.log('');
|
|
484
|
+
console.log(c.mag(` ⚚ 劫——破——了——!!!\n`));
|
|
474
485
|
}
|
|
475
486
|
|
|
476
|
-
main().catch(err => {
|
|
487
|
+
main().catch(err => { fail(err.message); process.exit(1); });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-abyss",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.2",
|
|
4
4
|
"description": "邪修红尘仙·宿命深渊 - 一键为 Claude Code / Codex CLI 注入邪修人格与安全工程知识体系",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
@@ -37,5 +37,8 @@
|
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"test": "echo \"No tests yet\" && exit 0"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@inquirer/prompts": "^8.2.0"
|
|
40
43
|
}
|
|
41
44
|
}
|