code-abyss 1.6.0 → 1.6.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/README.md +11 -18
- package/bin/install.js +181 -169
- 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.1';
|
|
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,84 @@ 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
373
|
const log = [];
|
|
358
374
|
deepMergeNew(ctx.settings, CCLINE_STATUS_LINE, '', log);
|
|
359
|
-
|
|
375
|
+
printMergeLog(log);
|
|
360
376
|
fs.writeFileSync(ctx.settingsPath, JSON.stringify(ctx.settings, null, 2) + '\n');
|
|
361
377
|
|
|
362
|
-
console.log(
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
console.log('✅ ccline 配置完成');
|
|
378
|
+
console.log('');
|
|
379
|
+
warn(`需要 ${c.b('Nerd Font')} 字体`);
|
|
380
|
+
info(`推荐: FiraCode Nerd Font / JetBrainsMono Nerd Font`);
|
|
381
|
+
info(`下载: ${c.cyn('https://www.nerdfonts.com/')}`);
|
|
382
|
+
info(`配置: ${c.cyn('ccline --config')}`);
|
|
383
|
+
ok('ccline 配置完成');
|
|
369
384
|
}
|
|
370
385
|
|
|
371
|
-
// ── Codex
|
|
386
|
+
// ── Codex 后续 ──
|
|
372
387
|
|
|
373
|
-
async function postCodex(
|
|
388
|
+
async function postCodex() {
|
|
389
|
+
const { select, confirm, input } = require('@inquirer/prompts');
|
|
374
390
|
const cfgPath = path.join(HOME, '.codex', 'config.toml');
|
|
375
391
|
const exists = fs.existsSync(cfgPath);
|
|
376
392
|
|
|
377
|
-
|
|
393
|
+
step(2, 3, '认证检测');
|
|
378
394
|
const auth = detectCodexAuth();
|
|
379
|
-
console.log('── 认证检测 ──');
|
|
380
395
|
if (auth) {
|
|
381
|
-
|
|
396
|
+
ok(`${c.b(auth.type)} → ${auth.detail}`);
|
|
382
397
|
} 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)');
|
|
398
|
+
warn('未检测到 API 认证');
|
|
399
|
+
info(`支持: ${c.cyn('codex login')} | ${c.cyn('OPENAI_API_KEY')} | ${c.cyn('自定义 provider')}`);
|
|
388
400
|
}
|
|
389
401
|
|
|
402
|
+
step(3, 3, '可选配置');
|
|
390
403
|
if (autoYes) {
|
|
391
|
-
// 全自动:不存在则写入模板
|
|
392
404
|
if (!exists) {
|
|
393
405
|
const src = path.join(PKG_ROOT, 'config', 'codex-config.example.toml');
|
|
394
406
|
if (fs.existsSync(src)) {
|
|
395
407
|
fs.copyFileSync(src, cfgPath);
|
|
396
|
-
|
|
397
|
-
|
|
408
|
+
ok('写入: ~/.codex/config.toml (模板)');
|
|
409
|
+
warn('请编辑 base_url 和 model');
|
|
398
410
|
}
|
|
399
411
|
} else {
|
|
400
|
-
|
|
412
|
+
ok('config.toml 已存在');
|
|
401
413
|
}
|
|
402
414
|
return;
|
|
403
415
|
}
|
|
404
416
|
|
|
405
417
|
if (!exists) {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
const answer = (await ask(rl, '\n选择 [1/2] [2]: ')).trim() || '2';
|
|
410
|
-
if (answer === '1') {
|
|
418
|
+
warn('未检测到 ~/.codex/config.toml');
|
|
419
|
+
const doWrite = await confirm({ message: '写入推荐 config.toml (含自定义 provider 模板)?', default: true });
|
|
420
|
+
if (doWrite) {
|
|
411
421
|
const src = path.join(PKG_ROOT, 'config', 'codex-config.example.toml');
|
|
412
422
|
if (fs.existsSync(src)) {
|
|
413
423
|
fs.copyFileSync(src, cfgPath);
|
|
414
|
-
|
|
415
|
-
|
|
424
|
+
ok('写入: ~/.codex/config.toml');
|
|
425
|
+
warn('请编辑 base_url 和 model');
|
|
416
426
|
}
|
|
417
|
-
console.log('✅ Codex 配置完成\n');
|
|
418
427
|
}
|
|
419
428
|
} else {
|
|
420
|
-
|
|
429
|
+
ok('config.toml 已存在');
|
|
421
430
|
}
|
|
422
431
|
}
|
|
423
432
|
|
|
@@ -426,51 +435,54 @@ async function postCodex(rl) {
|
|
|
426
435
|
async function main() {
|
|
427
436
|
if (uninstallTarget) { runUninstall(uninstallTarget); return; }
|
|
428
437
|
|
|
438
|
+
const { select } = require('@inquirer/prompts');
|
|
439
|
+
banner();
|
|
440
|
+
|
|
429
441
|
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 });
|
|
442
|
+
if (!['claude', 'codex'].includes(target)) { fail('--target 必须是 claude 或 codex'); process.exit(1); }
|
|
435
443
|
const ctx = installCore(target);
|
|
436
|
-
if (target === 'claude') await postClaude(
|
|
437
|
-
else await postCodex(
|
|
438
|
-
|
|
439
|
-
finish(target);
|
|
444
|
+
if (target === 'claude') await postClaude(ctx);
|
|
445
|
+
else await postCodex();
|
|
446
|
+
finish(ctx);
|
|
440
447
|
return;
|
|
441
448
|
}
|
|
442
449
|
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
450
|
+
const action = await select({
|
|
451
|
+
message: '请选择操作',
|
|
452
|
+
choices: [
|
|
453
|
+
{ name: `安装到 Claude Code ${c.d('(~/.claude/')}${c.d(')')}`, value: 'install-claude' },
|
|
454
|
+
{ name: `安装到 Codex CLI ${c.d('(~/.codex/')}${c.d(')')}`, value: 'install-codex' },
|
|
455
|
+
{ name: `${c.red('卸载')} Claude Code`, value: 'uninstall-claude' },
|
|
456
|
+
{ name: `${c.red('卸载')} Codex CLI`, value: 'uninstall-codex' },
|
|
457
|
+
],
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
switch (action) {
|
|
461
|
+
case 'install-claude': {
|
|
454
462
|
const ctx = installCore('claude');
|
|
455
|
-
await postClaude(
|
|
456
|
-
|
|
463
|
+
await postClaude(ctx);
|
|
464
|
+
finish(ctx); break;
|
|
457
465
|
}
|
|
458
|
-
case '
|
|
466
|
+
case 'install-codex': {
|
|
459
467
|
const ctx = installCore('codex');
|
|
460
|
-
await postCodex(
|
|
461
|
-
|
|
468
|
+
await postCodex();
|
|
469
|
+
finish(ctx); break;
|
|
462
470
|
}
|
|
463
|
-
case '
|
|
464
|
-
case '
|
|
465
|
-
default: rl.close(); console.error('❌ 无效选择'); process.exit(1);
|
|
471
|
+
case 'uninstall-claude': runUninstall('claude'); break;
|
|
472
|
+
case 'uninstall-codex': runUninstall('codex'); break;
|
|
466
473
|
}
|
|
467
474
|
}
|
|
468
475
|
|
|
469
|
-
function finish(
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
console.log(
|
|
473
|
-
console.log(
|
|
476
|
+
function finish(ctx) {
|
|
477
|
+
const tgt = ctx.manifest.target;
|
|
478
|
+
divider('安装完成');
|
|
479
|
+
console.log('');
|
|
480
|
+
console.log(` ${c.b('目标:')} ${c.cyn(ctx.targetDir)}`);
|
|
481
|
+
console.log(` ${c.b('版本:')} v${VERSION}`);
|
|
482
|
+
console.log(` ${c.b('文件:')} ${ctx.manifest.installed.length} 个安装, ${ctx.manifest.backups.length} 个备份`);
|
|
483
|
+
console.log(` ${c.b('卸载:')} ${c.d(`npx code-abyss --uninstall ${tgt}`)}`);
|
|
484
|
+
console.log('');
|
|
485
|
+
console.log(c.mag(` ⚚ 劫——破——了——!!!\n`));
|
|
474
486
|
}
|
|
475
487
|
|
|
476
|
-
main().catch(err => {
|
|
488
|
+
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.1",
|
|
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
|
}
|