code-abyss 1.7.6 → 1.7.7

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
- - 若你本地已有旧配置,安装器不会强制覆盖;会自动做三件事:补齐缺失默认项、清理 removed feature、将 deprecated `web_search_*` 迁移为 `[tools].web_search`
217
+ - 若你本地已有旧配置,安装器不会强制覆盖;会自动补齐 root 默认项(正确写入首段)、清理 removed feature、迁移 deprecated `web_search_*` `[tools].web_search`,并在 `danger-full-access` 下清理 `projects.*.trust_level`
218
218
  - 建议在升级后执行一次 `codex --help` / 启动自检,确认无 deprecation warning
219
219
 
220
220
  ---
@@ -4,6 +4,7 @@ const fs = require('fs');
4
4
  const path = require('path');
5
5
 
6
6
  const CODEX_DEFAULTS = {
7
+ approvalPolicy: 'never',
7
8
  sandboxMode: 'danger-full-access',
8
9
  featureFlag: 'multi_agent',
9
10
  };
@@ -28,9 +29,50 @@ function escapeRegExp(input) {
28
29
  return input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
29
30
  }
30
31
 
31
- function hasTopLevelKey(content, key) {
32
- const re = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=`, 'm');
33
- return re.test(content);
32
+ function isTableHeader(line) {
33
+ return /^\s*\[[^\]]+\]\s*$/.test(line);
34
+ }
35
+
36
+ function isProjectTableHeader(line) {
37
+ return /^\s*\[projects\."[^"]+"\]\s*$/.test(line);
38
+ }
39
+
40
+ function isAssignmentForKey(line, key) {
41
+ const re = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=`);
42
+ return re.test(line);
43
+ }
44
+
45
+ function hasRootKey(content, key) {
46
+ const lines = content.split(/\r?\n/);
47
+ let inRoot = true;
48
+
49
+ for (const line of lines) {
50
+ if (isTableHeader(line)) {
51
+ inRoot = false;
52
+ continue;
53
+ }
54
+ if (inRoot && isAssignmentForKey(line, key)) {
55
+ return true;
56
+ }
57
+ }
58
+ return false;
59
+ }
60
+
61
+ function readRootStringKey(content, key) {
62
+ const lines = content.split(/\r?\n/);
63
+ const re = new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*"([^"]*)"`);
64
+ let inRoot = true;
65
+
66
+ for (const line of lines) {
67
+ if (isTableHeader(line)) {
68
+ inRoot = false;
69
+ continue;
70
+ }
71
+ if (!inRoot) continue;
72
+ const m = line.match(re);
73
+ if (m) return m[1];
74
+ }
75
+ return null;
34
76
  }
35
77
 
36
78
  function hasSection(content, sectionName) {
@@ -66,6 +108,20 @@ function appendLine(content, line, eol) {
66
108
  return `${normalized}${line}${eol}`;
67
109
  }
68
110
 
111
+ function insertRootLine(content, line, eol) {
112
+ if (!content) return `${line}${eol}`;
113
+ const lines = content.split(/\r?\n/);
114
+ const firstSection = lines.findIndex((l) => isTableHeader(l));
115
+ const idx = firstSection === -1 ? lines.length : firstSection;
116
+ lines.splice(idx, 0, line);
117
+ return lines.join(eol);
118
+ }
119
+
120
+ function ensureRootKey(content, key, valueLiteral, eol) {
121
+ if (hasRootKey(content, key)) return { merged: content, added: false };
122
+ return { merged: insertRootLine(content, `${key} = ${valueLiteral}`, eol), added: true };
123
+ }
124
+
69
125
  function insertLineAfterSectionHeader(content, sectionName, line, eol) {
70
126
  const lines = content.split(/\r?\n/);
71
127
  const sectionRe = new RegExp(`^\\s*\\[${escapeRegExp(sectionName)}\\]\\s*$`);
@@ -97,6 +153,59 @@ function parseTomlBooleanAssignment(line) {
97
153
  return m[1].toLowerCase() === 'true';
98
154
  }
99
155
 
156
+ function removeKeyAssignmentsInNonRootSections(content, key) {
157
+ const eol = content.includes('\r\n') ? '\r\n' : '\n';
158
+ const lines = content.split(/\r?\n/);
159
+ const kept = [];
160
+ let inRoot = true;
161
+ let removed = false;
162
+
163
+ for (const line of lines) {
164
+ if (isTableHeader(line)) {
165
+ inRoot = false;
166
+ kept.push(line);
167
+ continue;
168
+ }
169
+ if (!inRoot && isAssignmentForKey(line, key)) {
170
+ removed = true;
171
+ continue;
172
+ }
173
+ kept.push(line);
174
+ }
175
+
176
+ return { merged: kept.join(eol), removed };
177
+ }
178
+
179
+ function removeProjectTrustSectionsForFullAccess(content) {
180
+ const eol = content.includes('\r\n') ? '\r\n' : '\n';
181
+ const sandboxMode = readRootStringKey(content, 'sandbox_mode');
182
+ if (sandboxMode !== CODEX_DEFAULTS.sandboxMode) {
183
+ return { merged: content, removed: false };
184
+ }
185
+
186
+ const lines = content.split(/\r?\n/);
187
+ const kept = [];
188
+ let inProjectSection = false;
189
+ let removed = false;
190
+
191
+ for (const line of lines) {
192
+ if (isTableHeader(line)) {
193
+ if (isProjectTableHeader(line)) {
194
+ inProjectSection = true;
195
+ removed = true;
196
+ continue;
197
+ }
198
+ inProjectSection = false;
199
+ kept.push(line);
200
+ continue;
201
+ }
202
+ if (inProjectSection) continue;
203
+ kept.push(line);
204
+ }
205
+
206
+ return { merged: kept.join(eol), removed };
207
+ }
208
+
100
209
  function removeFeatureFlagsFromFeaturesSection(content, featureNames) {
101
210
  const eol = content.includes('\r\n') ? '\r\n' : '\n';
102
211
  const lines = content.split(/\r?\n/);
@@ -167,8 +276,28 @@ function mergeCodexConfigDefaults(content) {
167
276
  let merged = content;
168
277
  const added = [];
169
278
 
170
- if (!hasTopLevelKey(merged, 'sandbox_mode')) {
171
- merged = appendLine(merged, `sandbox_mode = "${CODEX_DEFAULTS.sandboxMode}"`, eol);
279
+ const rootKeys = [
280
+ 'approval_policy',
281
+ 'sandbox_mode',
282
+ 'model_reasoning_effort',
283
+ 'disable_response_storage',
284
+ 'personality',
285
+ ];
286
+
287
+ for (const key of rootKeys) {
288
+ const cleaned = removeKeyAssignmentsInNonRootSections(merged, key);
289
+ merged = cleaned.merged;
290
+ }
291
+
292
+ const approval = ensureRootKey(merged, 'approval_policy', `"${CODEX_DEFAULTS.approvalPolicy}"`, eol);
293
+ merged = approval.merged;
294
+ if (approval.added) {
295
+ added.push('approval_policy');
296
+ }
297
+
298
+ const sandbox = ensureRootKey(merged, 'sandbox_mode', `"${CODEX_DEFAULTS.sandboxMode}"`, eol);
299
+ merged = sandbox.merged;
300
+ if (sandbox.added) {
172
301
  added.push('sandbox_mode');
173
302
  }
174
303
 
@@ -187,9 +316,12 @@ function mergeCodexConfigDefaults(content) {
187
316
  function patchCodexConfig(cfgPath) {
188
317
  const raw = fs.readFileSync(cfgPath, 'utf8');
189
318
  const { merged: cleaned, removed, migrated } = cleanupLegacyCodexConfig(raw);
190
- const { merged, added } = mergeCodexConfigDefaults(cleaned);
319
+ const { merged: mergedDefaults, added } = mergeCodexConfigDefaults(cleaned);
320
+ const { merged, removed: removedProjectTrust } = removeProjectTrustSectionsForFullAccess(mergedDefaults);
321
+ const removedAll = removedProjectTrust ? [...removed, 'projects.*.trust_level'] : removed;
322
+
191
323
  if (merged !== raw) fs.writeFileSync(cfgPath, merged);
192
- return { added, removed, migrated };
324
+ return { added, removed: removedAll, migrated };
193
325
  }
194
326
 
195
327
  function patchCodexConfigDefaults(cfgPath) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-abyss",
3
- "version": "1.7.6",
3
+ "version": "1.7.7",
4
4
  "description": "邪修红尘仙·宿命深渊 - 一键为 Claude Code / Codex CLI 注入邪修人格与安全工程知识体系",
5
5
  "keywords": [
6
6
  "claude",