coding-tool-x 3.3.7 → 3.3.9
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/CHANGELOG.md +20 -0
- package/README.md +253 -326
- package/dist/web/assets/{Analytics-IW6eAy9u.js → Analytics-D6LzK9hk.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-BPtkTMSc.js → ConfigTemplates-BUDYuxRi.js} +1 -1
- package/dist/web/assets/Home-BQxQ1LhR.css +1 -0
- package/dist/web/assets/Home-D7KX7iF8.js +1 -0
- package/dist/web/assets/{PluginManager-BGx9MSDV.js → PluginManager-DTgQ--vB.js} +1 -1
- package/dist/web/assets/{ProjectList-BCn-mrCx.js → ProjectList-DMCiGmCT.js} +1 -1
- package/dist/web/assets/{SessionList-CzLfebJQ.js → SessionList-CRBsdVRe.js} +1 -1
- package/dist/web/assets/{SkillManager-CXz2vBQx.js → SkillManager-DMwx2Q4k.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-CHtgMfKc.js → WorkspaceManager-DapB4ljL.js} +1 -1
- package/dist/web/assets/{icons-B29onFfZ.js → icons-B5Pl4lrD.js} +1 -1
- package/dist/web/assets/index-CL-qpoJ_.js +2 -0
- package/dist/web/assets/index-D_5dRFOL.css +1 -0
- package/dist/web/assets/{markdown-C9MYpaSi.js → markdown-DyTJGI4N.js} +1 -1
- package/dist/web/assets/{naive-ui-CxpuzdjU.js → naive-ui-Bdxp09n2.js} +1 -1
- package/dist/web/assets/{vendors-DMjSfzlv.js → vendors-CKPV1OAU.js} +2 -2
- package/dist/web/assets/{vue-vendor-DET08QYg.js → vue-vendor-3bf-fPGP.js} +1 -1
- package/dist/web/index.html +7 -7
- package/docs/home.png +0 -0
- package/package.json +14 -5
- package/src/commands/daemon.js +3 -2
- package/src/commands/security.js +1 -2
- package/src/commands/toggle-proxy.js +100 -5
- package/src/config/paths.js +718 -90
- package/src/server/api/agents.js +1 -1
- package/src/server/api/channels.js +9 -0
- package/src/server/api/claude-hooks.js +13 -8
- package/src/server/api/codex-channels.js +9 -0
- package/src/server/api/codex-proxy.js +27 -15
- package/src/server/api/gemini-proxy.js +22 -11
- package/src/server/api/hooks.js +45 -0
- package/src/server/api/oauth-credentials.js +163 -0
- package/src/server/api/opencode-proxy.js +22 -10
- package/src/server/api/plugins.js +2 -1
- package/src/server/api/proxy.js +39 -44
- package/src/server/api/skills.js +91 -13
- package/src/server/api/ui-config.js +5 -0
- package/src/server/codex-proxy-server.js +90 -70
- package/src/server/gemini-proxy-server.js +107 -88
- package/src/server/index.js +2 -0
- package/src/server/opencode-proxy-server.js +381 -225
- package/src/server/proxy-server.js +86 -60
- package/src/server/services/alias.js +3 -3
- package/src/server/services/channels.js +21 -24
- package/src/server/services/codex-channels.js +158 -255
- package/src/server/services/codex-config.js +2 -5
- package/src/server/services/codex-env-manager.js +423 -0
- package/src/server/services/codex-settings-manager.js +21 -357
- package/src/server/services/codex-statistics-service.js +3 -27
- package/src/server/services/config-export-service.js +43 -9
- package/src/server/services/config-registry-service.js +3 -2
- package/src/server/services/config-sync-manager.js +1 -1
- package/src/server/services/favorites.js +4 -3
- package/src/server/services/gemini-channels.js +14 -12
- package/src/server/services/gemini-statistics-service.js +3 -25
- package/src/server/services/mcp-service.js +35 -19
- package/src/server/services/model-detector.js +4 -3
- package/src/server/services/native-keychain.js +243 -0
- package/src/server/services/native-oauth-adapters.js +891 -0
- package/src/server/services/network-access.js +39 -1
- package/src/server/services/notification-hooks.js +951 -0
- package/src/server/services/oauth-credentials-service.js +786 -0
- package/src/server/services/oauth-utils.js +49 -0
- package/src/server/services/opencode-channels.js +19 -15
- package/src/server/services/opencode-sessions.js +2 -2
- package/src/server/services/opencode-settings-manager.js +169 -16
- package/src/server/services/opencode-statistics-service.js +3 -27
- package/src/server/services/plugins-service.js +115 -15
- package/src/server/services/prompts-service.js +2 -3
- package/src/server/services/proxy-log-helper.js +242 -0
- package/src/server/services/proxy-runtime.js +6 -4
- package/src/server/services/repo-scanner-base.js +12 -4
- package/src/server/services/request-logger.js +7 -7
- package/src/server/services/security-config.js +4 -4
- package/src/server/services/session-cache.js +2 -2
- package/src/server/services/sessions.js +2 -2
- package/src/server/services/settings-manager.js +13 -0
- package/src/server/services/skill-service.js +867 -368
- package/src/server/services/statistics-service.js +5 -5
- package/src/server/services/ui-config.js +4 -3
- package/src/server/services/workspace-service.js +1 -1
- package/src/server/websocket-server.js +5 -4
- package/dist/web/assets/Home-BsSioaaB.css +0 -1
- package/dist/web/assets/Home-obifg_9E.js +0 -1
- package/dist/web/assets/index-C7LPdVsN.js +0 -2
- package/dist/web/assets/index-eEmjZKWP.css +0 -1
- package/docs/bannel.png +0 -0
- package/docs/model-redirection.md +0 -251
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { execFileSync } = require('child_process');
|
|
4
|
+
const { PATHS, HOME_DIR } = require('../../config/paths');
|
|
5
|
+
|
|
6
|
+
const PROFILE_MARKER_START = '# >>> coding-tool codex env >>>';
|
|
7
|
+
const PROFILE_MARKER_END = '# <<< coding-tool codex env <<<';
|
|
8
|
+
|
|
9
|
+
function defaultEnvFilePath(configDir) {
|
|
10
|
+
return path.join(configDir, 'codex-env.sh');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function defaultStateFilePath(configDir) {
|
|
14
|
+
return path.join(configDir, 'codex-env-state.json');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function ensureDir(dirPath) {
|
|
18
|
+
if (!fs.existsSync(dirPath)) {
|
|
19
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function hasValue(value) {
|
|
24
|
+
return String(value || '').trim() !== '';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function shellQuote(value) {
|
|
28
|
+
return `'${String(value).replace(/'/g, `'\"'\"'`)}'`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function powershellQuote(value) {
|
|
32
|
+
return `'${String(value).replace(/'/g, "''")}'`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function buildHomeRelativeShellPath(filePath, homeDir) {
|
|
36
|
+
const normalizedHome = path.resolve(homeDir);
|
|
37
|
+
const normalizedFilePath = path.resolve(filePath);
|
|
38
|
+
if (normalizedFilePath === normalizedHome) {
|
|
39
|
+
return '$HOME';
|
|
40
|
+
}
|
|
41
|
+
if (normalizedFilePath.startsWith(`${normalizedHome}${path.sep}`)) {
|
|
42
|
+
const relativePath = normalizedFilePath.slice(normalizedHome.length).replace(/\\/g, '/');
|
|
43
|
+
return `$HOME${relativePath}`;
|
|
44
|
+
}
|
|
45
|
+
return normalizedFilePath.replace(/\\/g, '/');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function buildSourceSnippet(envFilePath, homeDir) {
|
|
49
|
+
const shellPath = buildHomeRelativeShellPath(envFilePath, homeDir);
|
|
50
|
+
return [
|
|
51
|
+
PROFILE_MARKER_START,
|
|
52
|
+
`[ -f "${shellPath}" ] && . "${shellPath}"`,
|
|
53
|
+
PROFILE_MARKER_END
|
|
54
|
+
].join('\n');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function stripManagedBlock(content = '') {
|
|
58
|
+
if (!content.includes(PROFILE_MARKER_START)) {
|
|
59
|
+
return content;
|
|
60
|
+
}
|
|
61
|
+
const pattern = new RegExp(
|
|
62
|
+
`\\n?${escapeRegex(PROFILE_MARKER_START)}[\\s\\S]*?${escapeRegex(PROFILE_MARKER_END)}\\n?`,
|
|
63
|
+
'g'
|
|
64
|
+
);
|
|
65
|
+
return content.replace(pattern, '\n').replace(/\n{3,}/g, '\n\n').trimEnd();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function escapeRegex(value) {
|
|
69
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function upsertManagedBlock(content, snippet) {
|
|
73
|
+
const stripped = stripManagedBlock(content);
|
|
74
|
+
if (!stripped.trim()) {
|
|
75
|
+
return `${snippet}\n`;
|
|
76
|
+
}
|
|
77
|
+
return `${stripped.trimEnd()}\n\n${snippet}\n`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function readJsonFile(filePath, fallbackValue) {
|
|
81
|
+
try {
|
|
82
|
+
if (!fs.existsSync(filePath)) {
|
|
83
|
+
return fallbackValue;
|
|
84
|
+
}
|
|
85
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
86
|
+
} catch {
|
|
87
|
+
return fallbackValue;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function writeJsonFile(filePath, payload) {
|
|
92
|
+
ensureDir(path.dirname(filePath));
|
|
93
|
+
fs.writeFileSync(filePath, JSON.stringify(payload, null, 2), 'utf8');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function readState(stateFilePath) {
|
|
97
|
+
const state = readJsonFile(stateFilePath, {
|
|
98
|
+
version: 1,
|
|
99
|
+
values: {},
|
|
100
|
+
profiles: []
|
|
101
|
+
});
|
|
102
|
+
return {
|
|
103
|
+
version: 1,
|
|
104
|
+
values: state && typeof state.values === 'object' ? state.values : {},
|
|
105
|
+
profiles: Array.isArray(state?.profiles) ? state.profiles : []
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function normalizeEnvMap(envMap = {}) {
|
|
110
|
+
const normalized = {};
|
|
111
|
+
for (const [key, value] of Object.entries(envMap || {})) {
|
|
112
|
+
if (!key || !hasValue(value)) continue;
|
|
113
|
+
normalized[String(key).trim()] = String(value);
|
|
114
|
+
}
|
|
115
|
+
return normalized;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function buildNextEnvValues(previousValues, envMap, { replace = true, removeKeys = [] } = {}) {
|
|
119
|
+
const nextValues = replace
|
|
120
|
+
? {}
|
|
121
|
+
: { ...previousValues };
|
|
122
|
+
|
|
123
|
+
if (replace) {
|
|
124
|
+
Object.assign(nextValues, normalizeEnvMap(envMap));
|
|
125
|
+
} else {
|
|
126
|
+
const normalizedIncoming = normalizeEnvMap(envMap);
|
|
127
|
+
for (const [key, value] of Object.entries(normalizedIncoming)) {
|
|
128
|
+
nextValues[key] = value;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
for (const key of removeKeys || []) {
|
|
133
|
+
delete nextValues[key];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
for (const [key, value] of Object.entries(nextValues)) {
|
|
137
|
+
if (!hasValue(value)) {
|
|
138
|
+
delete nextValues[key];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return nextValues;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function getPosixProfileCandidates(homeDir, shellEnv = process.env) {
|
|
146
|
+
const candidates = [
|
|
147
|
+
'.bashrc',
|
|
148
|
+
'.bash_profile',
|
|
149
|
+
'.bash_login',
|
|
150
|
+
'.zshrc',
|
|
151
|
+
'.zshenv',
|
|
152
|
+
'.zprofile',
|
|
153
|
+
'.zlogin',
|
|
154
|
+
'.profile'
|
|
155
|
+
].map(fileName => path.join(homeDir, fileName));
|
|
156
|
+
|
|
157
|
+
const shell = String(shellEnv.SHELL || '').toLowerCase();
|
|
158
|
+
const preferred = shell.includes('zsh')
|
|
159
|
+
? path.join(homeDir, '.zshrc')
|
|
160
|
+
: shell.includes('bash')
|
|
161
|
+
? path.join(homeDir, '.bashrc')
|
|
162
|
+
: path.join(homeDir, '.profile');
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
preferred,
|
|
166
|
+
candidates
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function writeTextFileIfChanged(filePath, content) {
|
|
171
|
+
const previous = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : null;
|
|
172
|
+
if (previous === content) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
ensureDir(path.dirname(filePath));
|
|
176
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function syncPosixEnvironment(nextValues, previousState, options) {
|
|
181
|
+
const {
|
|
182
|
+
runtime,
|
|
183
|
+
homeDir,
|
|
184
|
+
envFilePath,
|
|
185
|
+
stateFilePath,
|
|
186
|
+
shellEnv,
|
|
187
|
+
execSync
|
|
188
|
+
} = options;
|
|
189
|
+
const { preferred, candidates } = getPosixProfileCandidates(homeDir, shellEnv);
|
|
190
|
+
const nextKeys = Object.keys(nextValues).sort();
|
|
191
|
+
const previousProfiles = Array.isArray(previousState.profiles) ? previousState.profiles : [];
|
|
192
|
+
const managedProfiles = new Set(previousProfiles);
|
|
193
|
+
const sourceSnippet = buildSourceSnippet(envFilePath, homeDir);
|
|
194
|
+
let changed = false;
|
|
195
|
+
|
|
196
|
+
if (nextKeys.length > 0) {
|
|
197
|
+
const envContent = [
|
|
198
|
+
'# Managed by Coding-Tool for Codex env_key providers',
|
|
199
|
+
...nextKeys.map(key => `export ${key}=${shellQuote(nextValues[key])}`),
|
|
200
|
+
''
|
|
201
|
+
].join('\n');
|
|
202
|
+
changed = writeTextFileIfChanged(envFilePath, envContent) || changed;
|
|
203
|
+
|
|
204
|
+
const existingProfiles = candidates.filter(filePath => fs.existsSync(filePath));
|
|
205
|
+
if (managedProfiles.size === 0) {
|
|
206
|
+
if (existingProfiles.length > 0) {
|
|
207
|
+
for (const profilePath of existingProfiles) {
|
|
208
|
+
managedProfiles.add(profilePath);
|
|
209
|
+
}
|
|
210
|
+
} else {
|
|
211
|
+
managedProfiles.add(preferred);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
for (const profilePath of managedProfiles) {
|
|
216
|
+
const currentContent = fs.existsSync(profilePath) ? fs.readFileSync(profilePath, 'utf8') : '';
|
|
217
|
+
const nextContent = upsertManagedBlock(currentContent, sourceSnippet);
|
|
218
|
+
changed = writeTextFileIfChanged(profilePath, nextContent) || changed;
|
|
219
|
+
}
|
|
220
|
+
} else {
|
|
221
|
+
if (fs.existsSync(envFilePath)) {
|
|
222
|
+
fs.unlinkSync(envFilePath);
|
|
223
|
+
changed = true;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const cleanupTargets = new Set([
|
|
227
|
+
...previousProfiles,
|
|
228
|
+
...candidates.filter(filePath => fs.existsSync(filePath))
|
|
229
|
+
]);
|
|
230
|
+
|
|
231
|
+
for (const profilePath of cleanupTargets) {
|
|
232
|
+
if (!fs.existsSync(profilePath)) continue;
|
|
233
|
+
const currentContent = fs.readFileSync(profilePath, 'utf8');
|
|
234
|
+
const nextContent = stripManagedBlock(currentContent);
|
|
235
|
+
const finalContent = nextContent ? `${nextContent}\n` : '';
|
|
236
|
+
changed = writeTextFileIfChanged(profilePath, finalContent) || changed;
|
|
237
|
+
}
|
|
238
|
+
managedProfiles.clear();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
for (const key of Object.keys(previousState.values || {})) {
|
|
242
|
+
if (!Object.prototype.hasOwnProperty.call(nextValues, key)) {
|
|
243
|
+
delete process.env[key];
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
for (const [key, value] of Object.entries(nextValues)) {
|
|
247
|
+
process.env[key] = value;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (runtime === 'darwin') {
|
|
251
|
+
applyLaunchctlEnvironment(previousState.values || {}, nextValues, execSync);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (nextKeys.length > 0) {
|
|
255
|
+
writeJsonFile(stateFilePath, {
|
|
256
|
+
version: 1,
|
|
257
|
+
values: nextValues,
|
|
258
|
+
profiles: Array.from(managedProfiles)
|
|
259
|
+
});
|
|
260
|
+
} else if (fs.existsSync(stateFilePath)) {
|
|
261
|
+
fs.unlinkSync(stateFilePath);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
changed,
|
|
266
|
+
reloadRequired: changed,
|
|
267
|
+
isFirstTime: Object.keys(previousState.values || {}).length === 0 && nextKeys.length > 0,
|
|
268
|
+
sourceCommand: nextKeys.length > 0 ? `source "${buildHomeRelativeShellPath(envFilePath, homeDir)}"` : null,
|
|
269
|
+
shellConfigPath: managedProfiles.size > 0 ? Array.from(managedProfiles)[0] : null,
|
|
270
|
+
shellConfigPaths: Array.from(managedProfiles),
|
|
271
|
+
envFilePath: nextKeys.length > 0 ? envFilePath : null,
|
|
272
|
+
managedKeys: nextKeys
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function applyLaunchctlEnvironment(previousValues, nextValues, execSync) {
|
|
277
|
+
const previousKeys = new Set(Object.keys(previousValues || {}));
|
|
278
|
+
for (const [key, value] of Object.entries(nextValues || {})) {
|
|
279
|
+
if (previousValues[key] === value) {
|
|
280
|
+
previousKeys.delete(key);
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
runLaunchctlCommand(['setenv', key, value], execSync);
|
|
284
|
+
previousKeys.delete(key);
|
|
285
|
+
}
|
|
286
|
+
for (const key of previousKeys) {
|
|
287
|
+
runLaunchctlCommand(['unsetenv', key], execSync);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function runLaunchctlCommand(args, execSync) {
|
|
292
|
+
try {
|
|
293
|
+
execSync('launchctl', args, {
|
|
294
|
+
stdio: ['ignore', 'ignore', 'ignore'],
|
|
295
|
+
timeout: 3000
|
|
296
|
+
});
|
|
297
|
+
} catch {
|
|
298
|
+
// ignore launchctl failures; shell profile remains the durable source
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function syncWindowsEnvironment(nextValues, previousState, options) {
|
|
303
|
+
const { stateFilePath, execSync } = options;
|
|
304
|
+
const nextKeys = Object.keys(nextValues).sort();
|
|
305
|
+
const previousValues = previousState.values || {};
|
|
306
|
+
let changed = false;
|
|
307
|
+
|
|
308
|
+
for (const [key, value] of Object.entries(nextValues)) {
|
|
309
|
+
if (previousValues[key] === value) {
|
|
310
|
+
process.env[key] = value;
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
setWindowsUserEnv(key, value, execSync);
|
|
314
|
+
process.env[key] = value;
|
|
315
|
+
changed = true;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
for (const key of Object.keys(previousValues)) {
|
|
319
|
+
if (Object.prototype.hasOwnProperty.call(nextValues, key)) {
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
removeWindowsUserEnv(key, execSync);
|
|
323
|
+
delete process.env[key];
|
|
324
|
+
changed = true;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (nextKeys.length > 0) {
|
|
328
|
+
writeJsonFile(stateFilePath, {
|
|
329
|
+
version: 1,
|
|
330
|
+
values: nextValues,
|
|
331
|
+
profiles: []
|
|
332
|
+
});
|
|
333
|
+
} else if (fs.existsSync(stateFilePath)) {
|
|
334
|
+
fs.unlinkSync(stateFilePath);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return {
|
|
338
|
+
changed,
|
|
339
|
+
reloadRequired: changed,
|
|
340
|
+
isFirstTime: Object.keys(previousValues).length === 0 && nextKeys.length > 0,
|
|
341
|
+
sourceCommand: null,
|
|
342
|
+
shellConfigPath: null,
|
|
343
|
+
shellConfigPaths: [],
|
|
344
|
+
envFilePath: null,
|
|
345
|
+
managedKeys: nextKeys
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function runWindowsEnvCommand(script, execSync) {
|
|
350
|
+
const candidates = ['powershell', 'pwsh'];
|
|
351
|
+
let lastError = null;
|
|
352
|
+
for (const command of candidates) {
|
|
353
|
+
try {
|
|
354
|
+
execSync(command, ['-NoProfile', '-NonInteractive', '-Command', script], {
|
|
355
|
+
stdio: ['ignore', 'ignore', 'ignore'],
|
|
356
|
+
timeout: 5000
|
|
357
|
+
});
|
|
358
|
+
return;
|
|
359
|
+
} catch (error) {
|
|
360
|
+
lastError = error;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
throw lastError || new Error('No PowerShell executable available');
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function setWindowsUserEnv(key, value, execSync) {
|
|
367
|
+
runWindowsEnvCommand(
|
|
368
|
+
`[Environment]::SetEnvironmentVariable(${powershellQuote(key)}, ${powershellQuote(value)}, 'User')`,
|
|
369
|
+
execSync
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function removeWindowsUserEnv(key, execSync) {
|
|
374
|
+
runWindowsEnvCommand(
|
|
375
|
+
`[Environment]::SetEnvironmentVariable(${powershellQuote(key)}, $null, 'User')`,
|
|
376
|
+
execSync
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function syncCodexUserEnvironment(envMap = {}, options = {}) {
|
|
381
|
+
const configDir = options.configDir || PATHS.config;
|
|
382
|
+
const homeDir = options.homeDir || HOME_DIR;
|
|
383
|
+
const envFilePath = options.envFilePath || defaultEnvFilePath(configDir);
|
|
384
|
+
const stateFilePath = options.stateFilePath || defaultStateFilePath(configDir);
|
|
385
|
+
const runtime = options.runtime || process.platform;
|
|
386
|
+
const shellEnv = options.shellEnv || process.env;
|
|
387
|
+
const execSync = options.execFileSync || execFileSync;
|
|
388
|
+
const previousState = readState(stateFilePath);
|
|
389
|
+
const nextValues = buildNextEnvValues(previousState.values, envMap, {
|
|
390
|
+
replace: options.replace !== false,
|
|
391
|
+
removeKeys: options.removeKeys || []
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
const syncOptions = {
|
|
395
|
+
runtime,
|
|
396
|
+
homeDir,
|
|
397
|
+
envFilePath,
|
|
398
|
+
stateFilePath,
|
|
399
|
+
shellEnv,
|
|
400
|
+
execSync
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
if (runtime === 'win32') {
|
|
404
|
+
return syncWindowsEnvironment(nextValues, previousState, syncOptions);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return syncPosixEnvironment(nextValues, previousState, syncOptions);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
module.exports = {
|
|
411
|
+
syncCodexUserEnvironment,
|
|
412
|
+
_test: {
|
|
413
|
+
buildHomeRelativeShellPath,
|
|
414
|
+
buildNextEnvValues,
|
|
415
|
+
buildSourceSnippet,
|
|
416
|
+
getPosixProfileCandidates,
|
|
417
|
+
readState,
|
|
418
|
+
shellQuote,
|
|
419
|
+
stripManagedBlock,
|
|
420
|
+
syncCodexUserEnvironment,
|
|
421
|
+
upsertManagedBlock
|
|
422
|
+
}
|
|
423
|
+
};
|