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 +1 -1
- package/bin/adapters/codex.js +213 -3
- package/package.json +1 -1
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
|
-
-
|
|
217
|
+
- 若你本地已有旧配置,安装器不会强制覆盖;会自动做三件事:补齐缺失默认项、清理 removed feature、将 deprecated `web_search_*` 迁移为 `[tools].web_search`
|
|
218
218
|
- 建议在升级后执行一次 `codex --help` / 启动自检,确认无 deprecation warning
|
|
219
219
|
|
|
220
220
|
---
|
package/bin/adapters/codex.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|