code-abyss 1.7.4 → 1.7.5

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 CHANGED
@@ -214,7 +214,7 @@ multi_agent = true
214
214
  ### 兼容性说明
215
215
 
216
216
  - 模板已对齐新版 Codex 配置风格:`[tools].web_search` 与 `[features].multi_agent`
217
- - 若你本地已有旧配置,安装器不会强制覆盖已有 `~/.codex/config.toml`
217
+ - 若你本地已有旧配置,安装器不会强制覆盖;会自动做三件事:补齐缺失默认项、清理 removed feature、将 deprecated `web_search_*` 迁移为 `[tools].web_search`
218
218
  - 建议在升级后执行一次 `codex --help` / 启动自检,确认无 deprecation warning
219
219
 
220
220
  ---
@@ -3,6 +3,213 @@
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
 
6
+ const CODEX_DEFAULTS = {
7
+ sandboxMode: 'danger-full-access',
8
+ featureFlag: 'multi_agent',
9
+ };
10
+
11
+ const LEGACY_FEATURES = {
12
+ removed: [
13
+ 'search_tool',
14
+ 'request_rule',
15
+ 'experimental_windows_sandbox',
16
+ 'elevated_windows_sandbox',
17
+ 'remote_models',
18
+ 'collaboration_modes',
19
+ 'steer',
20
+ ],
21
+ deprecated: [
22
+ 'web_search_request',
23
+ 'web_search_cached',
24
+ ],
25
+ };
26
+
27
+ function escapeRegExp(input) {
28
+ return input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
29
+ }
30
+
31
+ function hasTopLevelKey(content, key) {
32
+ const re = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=`, 'm');
33
+ return re.test(content);
34
+ }
35
+
36
+ function hasSection(content, sectionName) {
37
+ const re = new RegExp(`^\\s*\\[${escapeRegExp(sectionName)}\\]\\s*$`, 'm');
38
+ return re.test(content);
39
+ }
40
+
41
+ function hasKeyInSection(content, sectionName, key) {
42
+ const lines = content.split(/\r?\n/);
43
+ const sectionRe = new RegExp(`^\\s*\\[${escapeRegExp(sectionName)}\\]\\s*$`);
44
+ const anySectionRe = /^\s*\[[^\]]+\]\s*$/;
45
+ const keyRe = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=`);
46
+ let inSection = false;
47
+
48
+ for (const line of lines) {
49
+ if (sectionRe.test(line)) {
50
+ inSection = true;
51
+ continue;
52
+ }
53
+ if (inSection && anySectionRe.test(line)) {
54
+ return false;
55
+ }
56
+ if (inSection && keyRe.test(line)) {
57
+ return true;
58
+ }
59
+ }
60
+ return false;
61
+ }
62
+
63
+ function appendLine(content, line, eol) {
64
+ if (!content) return `${line}${eol}`;
65
+ const normalized = content.endsWith('\n') ? content : `${content}${eol}`;
66
+ return `${normalized}${line}${eol}`;
67
+ }
68
+
69
+ function insertLineAfterSectionHeader(content, sectionName, line, eol) {
70
+ const lines = content.split(/\r?\n/);
71
+ const sectionRe = new RegExp(`^\\s*\\[${escapeRegExp(sectionName)}\\]\\s*$`);
72
+ const idx = lines.findIndex((l) => sectionRe.test(l));
73
+ if (idx === -1) return appendLine(content, line, eol);
74
+ lines.splice(idx + 1, 0, line);
75
+ return lines.join(eol);
76
+ }
77
+
78
+ function ensureKeyInSection(content, sectionName, key, valueLiteral, eol) {
79
+ let merged = content;
80
+ let added = false;
81
+
82
+ if (!hasSection(merged, sectionName)) {
83
+ merged = appendLine(merged, `[${sectionName}]`, eol);
84
+ merged = appendLine(merged, `${key} = ${valueLiteral}`, eol);
85
+ added = true;
86
+ } else if (!hasKeyInSection(merged, sectionName, key)) {
87
+ merged = insertLineAfterSectionHeader(merged, sectionName, `${key} = ${valueLiteral}`, eol);
88
+ added = true;
89
+ }
90
+
91
+ return { merged, added };
92
+ }
93
+
94
+ function parseTomlBooleanAssignment(line) {
95
+ const m = line.match(/=\s*(true|false)\b/i);
96
+ if (!m) return null;
97
+ return m[1].toLowerCase() === 'true';
98
+ }
99
+
100
+ function removeFeatureFlagsFromFeaturesSection(content, featureNames) {
101
+ const eol = content.includes('\r\n') ? '\r\n' : '\n';
102
+ const lines = content.split(/\r?\n/);
103
+ const sectionRe = /^\s*\[features\]\s*$/;
104
+ const anySectionRe = /^\s*\[[^\]]+\]\s*$/;
105
+ const assignRe = /^\s*([A-Za-z0-9_.-]+)\s*=/;
106
+ const featureSet = new Set(featureNames);
107
+ const removedEntries = [];
108
+
109
+ let inFeatures = false;
110
+ const kept = [];
111
+
112
+ for (const line of lines) {
113
+ if (sectionRe.test(line)) {
114
+ inFeatures = true;
115
+ kept.push(line);
116
+ continue;
117
+ }
118
+ if (inFeatures && anySectionRe.test(line)) {
119
+ inFeatures = false;
120
+ kept.push(line);
121
+ continue;
122
+ }
123
+ if (inFeatures) {
124
+ const m = line.match(assignRe);
125
+ if (m && featureSet.has(m[1])) {
126
+ removedEntries.push({ key: m[1], enabled: parseTomlBooleanAssignment(line) });
127
+ continue;
128
+ }
129
+ }
130
+ kept.push(line);
131
+ }
132
+
133
+ return { merged: kept.join(eol), removedEntries };
134
+ }
135
+
136
+ function uniq(values) {
137
+ return [...new Set(values)];
138
+ }
139
+
140
+ function cleanupLegacyCodexConfig(content) {
141
+ const eol = content.includes('\r\n') ? '\r\n' : '\n';
142
+ const toRemove = [...LEGACY_FEATURES.removed, ...LEGACY_FEATURES.deprecated];
143
+ const { merged: pruned, removedEntries } = removeFeatureFlagsFromFeaturesSection(content, toRemove);
144
+ let merged = pruned;
145
+ const removed = uniq(removedEntries.map((x) => x.key));
146
+ const migrated = [];
147
+
148
+ const deprecatedRemoved = removedEntries.filter((x) => LEGACY_FEATURES.deprecated.includes(x.key));
149
+ if (deprecatedRemoved.length > 0) {
150
+ const shouldEnableWebSearch = deprecatedRemoved.some((x) => x.enabled !== false);
151
+ const { merged: withTools, added } = ensureKeyInSection(
152
+ merged,
153
+ 'tools',
154
+ 'web_search',
155
+ shouldEnableWebSearch ? 'true' : 'false',
156
+ eol
157
+ );
158
+ merged = withTools;
159
+ if (added) migrated.push(`tools.web_search=${shouldEnableWebSearch ? 'true' : 'false'}`);
160
+ }
161
+
162
+ return { merged, removed, migrated };
163
+ }
164
+
165
+ function mergeCodexConfigDefaults(content) {
166
+ const eol = content.includes('\r\n') ? '\r\n' : '\n';
167
+ let merged = content;
168
+ const added = [];
169
+
170
+ if (!hasTopLevelKey(merged, 'sandbox_mode')) {
171
+ merged = appendLine(merged, `sandbox_mode = "${CODEX_DEFAULTS.sandboxMode}"`, eol);
172
+ added.push('sandbox_mode');
173
+ }
174
+
175
+ if (!hasSection(merged, 'features')) {
176
+ merged = appendLine(merged, '[features]', eol);
177
+ merged = appendLine(merged, `${CODEX_DEFAULTS.featureFlag} = true`, eol);
178
+ added.push('features.multi_agent');
179
+ } else if (!hasKeyInSection(merged, 'features', CODEX_DEFAULTS.featureFlag)) {
180
+ merged = insertLineAfterSectionHeader(merged, 'features', `${CODEX_DEFAULTS.featureFlag} = true`, eol);
181
+ added.push('features.multi_agent');
182
+ }
183
+
184
+ return { merged, added };
185
+ }
186
+
187
+ function patchCodexConfig(cfgPath) {
188
+ const raw = fs.readFileSync(cfgPath, 'utf8');
189
+ const { merged: cleaned, removed, migrated } = cleanupLegacyCodexConfig(raw);
190
+ const { merged, added } = mergeCodexConfigDefaults(cleaned);
191
+ if (merged !== raw) fs.writeFileSync(cfgPath, merged);
192
+ return { added, removed, migrated };
193
+ }
194
+
195
+ function patchCodexConfigDefaults(cfgPath) {
196
+ return patchCodexConfig(cfgPath).added;
197
+ }
198
+
199
+ function patchAndReportCodexDefaults({ cfgPath, ok, warn }) {
200
+ try {
201
+ const { added, removed, migrated } = patchCodexConfig(cfgPath);
202
+ const changes = [];
203
+ if (added.length > 0) changes.push(`补全默认项: ${added.join(', ')}`);
204
+ if (removed.length > 0) changes.push(`清理过时项: ${removed.join(', ')}`);
205
+ if (migrated.length > 0) changes.push(`迁移配置: ${migrated.join(', ')}`);
206
+ if (changes.length > 0) ok(`config.toml 已存在,${changes.join(';')}`);
207
+ else ok('config.toml 已存在');
208
+ } catch (e) {
209
+ warn(`config.toml 读取失败,跳过补全: ${e.message}`);
210
+ }
211
+ }
212
+
6
213
  function detectCodexAuth({
7
214
  HOME,
8
215
  env = process.env,
@@ -68,7 +275,7 @@ async function postCodex({
68
275
  warn('请编辑 base_url 和 model');
69
276
  }
70
277
  } else {
71
- ok('config.toml 已存在');
278
+ patchAndReportCodexDefaults({ cfgPath, ok, warn });
72
279
  }
73
280
  return;
74
281
  }
@@ -85,13 +292,16 @@ async function postCodex({
85
292
  }
86
293
  }
87
294
  } else {
88
- ok('config.toml 已存在');
295
+ patchAndReportCodexDefaults({ cfgPath, ok, warn });
89
296
  }
90
297
  }
91
298
 
92
299
  module.exports = {
300
+ cleanupLegacyCodexConfig,
301
+ mergeCodexConfigDefaults,
302
+ patchCodexConfig,
303
+ patchCodexConfigDefaults,
93
304
  detectCodexAuth,
94
305
  getCodexCoreFiles,
95
306
  postCodex,
96
307
  };
97
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-abyss",
3
- "version": "1.7.4",
3
+ "version": "1.7.5",
4
4
  "description": "邪修红尘仙·宿命深渊 - 一键为 Claude Code / Codex CLI 注入邪修人格与安全工程知识体系",
5
5
  "keywords": [
6
6
  "claude",