@minniexcode/codex-switch 0.0.5 → 0.0.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.AI.md +5 -2
- package/README.md +44 -100
- package/dist/app/add-provider.js +28 -4
- package/dist/app/edit-provider.js +47 -19
- package/dist/app/export-providers.js +2 -2
- package/dist/app/get-current-profile.js +1 -1
- package/dist/app/get-status.js +10 -3
- package/dist/app/import-providers.js +15 -7
- package/dist/app/init-codex.js +68 -0
- package/dist/app/list-backups.js +1 -1
- package/dist/app/list-config-profiles.js +3 -2
- package/dist/app/list-providers.js +2 -1
- package/dist/app/remove-provider.js +2 -2
- package/dist/app/rollback-backup.js +1 -1
- package/dist/app/rollback-latest.js +1 -1
- package/dist/app/run-doctor.js +83 -6
- package/dist/app/run-mutation.js +2 -2
- package/dist/app/setup-codex.js +21 -12
- package/dist/app/show-config.js +11 -3
- package/dist/app/show-provider.js +1 -1
- package/dist/app/switch-provider.js +16 -9
- package/dist/cli/add-interactive.js +7 -104
- package/dist/cli/args.js +6 -135
- package/dist/cli/help.js +8 -313
- package/dist/cli/interactive.js +17 -225
- package/dist/cli/output.js +21 -6
- package/dist/cli/prompt.js +4 -106
- package/dist/cli.js +10 -404
- package/dist/commands/args.js +132 -0
- package/dist/commands/dispatch.js +16 -0
- package/dist/commands/handlers.js +460 -0
- package/dist/commands/help.js +120 -0
- package/dist/commands/registry.js +351 -0
- package/dist/commands/types.js +2 -0
- package/dist/domain/config.js +235 -21
- package/dist/domain/providers.js +16 -2
- package/dist/domain/setup.js +1 -0
- package/dist/infra/backup-repo.js +9 -206
- package/dist/infra/codex-cli.js +9 -126
- package/dist/infra/codex-paths.js +6 -67
- package/dist/infra/config-repo.js +59 -0
- package/dist/infra/fs-utils.js +8 -93
- package/dist/infra/lock-repo.js +4 -95
- package/dist/infra/providers-repo.js +8 -94
- package/dist/interaction/add-interactive.js +99 -0
- package/dist/interaction/interactive.js +289 -0
- package/dist/interaction/prompt.js +110 -0
- package/dist/runtime/codex-cli.js +130 -0
- package/dist/runtime/codex-probe.js +57 -0
- package/dist/runtime/types.js +2 -0
- package/dist/storage/auth-repo.js +160 -0
- package/dist/storage/backup-repo.js +210 -0
- package/dist/storage/codex-paths.js +71 -0
- package/dist/storage/config-repo.js +266 -0
- package/dist/storage/fs-utils.js +97 -0
- package/dist/storage/lock-repo.js +99 -0
- package/dist/storage/providers-repo.js +98 -0
- package/docs/Design/codex-switch-v0.0.5-design.md +32 -22
- package/docs/Design/codex-switch-v0.0.6-design.md +708 -0
- package/docs/Design/codex-switch-v0.0.7-design.md +862 -0
- package/docs/PRD/codex-switch-prd-v0.0.5-to-v0.1.0.md +227 -89
- package/docs/PRD/codex-switch-prd-v0.1.0.md +200 -226
- package/docs/PRD/codex-switch-prd.md +1 -1
- package/docs/Reference/codex-config-reference.md +604 -0
- package/docs/Reference/codex-config-reference.zh-CN.md +633 -0
- package/docs/cli-usage.md +78 -29
- package/docs/codex-switch-technical-architecture.md +73 -4
- package/docs/test-report-0.0.5.md +163 -0
- package/docs/test-report-0.0.7.md +118 -0
- package/docs/testing.md +151 -0
- package/package.json +1 -1
package/dist/domain/config.js
CHANGED
|
@@ -41,6 +41,7 @@ exports.parseStructuredConfig = parseStructuredConfig;
|
|
|
41
41
|
exports.buildManagedProfileViews = buildManagedProfileViews;
|
|
42
42
|
exports.collectConfigConsistencyIssues = collectConfigConsistencyIssues;
|
|
43
43
|
exports.validateManagedProfileCreation = validateManagedProfileCreation;
|
|
44
|
+
exports.buildManagedProfileEnvKey = buildManagedProfileEnvKey;
|
|
44
45
|
exports.planProfileLifecycleOutcome = planProfileLifecycleOutcome;
|
|
45
46
|
exports.planConfigMutation = planConfigMutation;
|
|
46
47
|
exports.applyPatchOperations = applyPatchOperations;
|
|
@@ -74,7 +75,9 @@ function parseStructuredConfig(configContent) {
|
|
|
74
75
|
let activeProfile = null;
|
|
75
76
|
let activeProfileRange = null;
|
|
76
77
|
const profiles = [];
|
|
78
|
+
const modelProviders = [];
|
|
77
79
|
let currentProfile = null;
|
|
80
|
+
let currentModelProvider = null;
|
|
78
81
|
let inRoot = true;
|
|
79
82
|
for (const line of lines) {
|
|
80
83
|
const trimmed = line.content.trim();
|
|
@@ -83,6 +86,10 @@ function parseStructuredConfig(configContent) {
|
|
|
83
86
|
if (currentProfile) {
|
|
84
87
|
currentProfile.sectionEnd = line.start;
|
|
85
88
|
}
|
|
89
|
+
if (currentModelProvider) {
|
|
90
|
+
currentModelProvider.sectionEnd = line.start;
|
|
91
|
+
currentModelProvider = null;
|
|
92
|
+
}
|
|
86
93
|
currentProfile = {
|
|
87
94
|
name: headerMatch[1],
|
|
88
95
|
headerStart: line.start,
|
|
@@ -90,19 +97,45 @@ function parseStructuredConfig(configContent) {
|
|
|
90
97
|
sectionEnd: configContent.length,
|
|
91
98
|
managedFieldInsertIndex: configContent.length,
|
|
92
99
|
modelValueRange: null,
|
|
93
|
-
|
|
100
|
+
modelProviderValueRange: null,
|
|
94
101
|
model: null,
|
|
95
|
-
|
|
102
|
+
modelProvider: null,
|
|
96
103
|
};
|
|
97
104
|
profiles.push(currentProfile);
|
|
98
105
|
inRoot = false;
|
|
99
106
|
continue;
|
|
100
107
|
}
|
|
108
|
+
const modelProviderHeaderMatch = trimmed.match(/^\[model_providers\.([^\]]+)\]$/);
|
|
109
|
+
if (modelProviderHeaderMatch) {
|
|
110
|
+
if (currentProfile) {
|
|
111
|
+
currentProfile.sectionEnd = line.start;
|
|
112
|
+
currentProfile = null;
|
|
113
|
+
}
|
|
114
|
+
if (currentModelProvider) {
|
|
115
|
+
currentModelProvider.sectionEnd = line.start;
|
|
116
|
+
}
|
|
117
|
+
currentModelProvider = {
|
|
118
|
+
name: modelProviderHeaderMatch[1],
|
|
119
|
+
sectionStart: line.start,
|
|
120
|
+
sectionEnd: configContent.length,
|
|
121
|
+
baseUrlValueRange: null,
|
|
122
|
+
baseUrl: null,
|
|
123
|
+
envKeyValueRange: null,
|
|
124
|
+
envKey: null,
|
|
125
|
+
};
|
|
126
|
+
modelProviders.push(currentModelProvider);
|
|
127
|
+
inRoot = false;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
101
130
|
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
102
131
|
if (currentProfile) {
|
|
103
132
|
currentProfile.sectionEnd = line.start;
|
|
104
133
|
currentProfile = null;
|
|
105
134
|
}
|
|
135
|
+
if (currentModelProvider) {
|
|
136
|
+
currentModelProvider.sectionEnd = line.start;
|
|
137
|
+
currentModelProvider = null;
|
|
138
|
+
}
|
|
106
139
|
inRoot = false;
|
|
107
140
|
continue;
|
|
108
141
|
}
|
|
@@ -125,14 +158,32 @@ function parseStructuredConfig(configContent) {
|
|
|
125
158
|
end: line.start + modelMatch.valueEnd,
|
|
126
159
|
};
|
|
127
160
|
}
|
|
161
|
+
const modelProviderMatch = matchKeyValueLine(line.content, "model_provider");
|
|
162
|
+
if (modelProviderMatch) {
|
|
163
|
+
currentProfile.modelProvider = modelProviderMatch.value;
|
|
164
|
+
currentProfile.modelProviderValueRange = {
|
|
165
|
+
start: line.start + modelProviderMatch.valueStart,
|
|
166
|
+
end: line.start + modelProviderMatch.valueEnd,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (currentModelProvider) {
|
|
128
171
|
const baseUrlMatch = matchKeyValueLine(line.content, "base_url");
|
|
129
172
|
if (baseUrlMatch) {
|
|
130
|
-
|
|
131
|
-
|
|
173
|
+
currentModelProvider.baseUrl = baseUrlMatch.value;
|
|
174
|
+
currentModelProvider.baseUrlValueRange = {
|
|
132
175
|
start: line.start + baseUrlMatch.valueStart,
|
|
133
176
|
end: line.start + baseUrlMatch.valueEnd,
|
|
134
177
|
};
|
|
135
178
|
}
|
|
179
|
+
const envKeyMatch = matchKeyValueLine(line.content, "env_key");
|
|
180
|
+
if (envKeyMatch) {
|
|
181
|
+
currentModelProvider.envKey = envKeyMatch.value;
|
|
182
|
+
currentModelProvider.envKeyValueRange = {
|
|
183
|
+
start: line.start + envKeyMatch.valueStart,
|
|
184
|
+
end: line.start + envKeyMatch.valueEnd,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
136
187
|
}
|
|
137
188
|
}
|
|
138
189
|
return {
|
|
@@ -144,6 +195,7 @@ function parseStructuredConfig(configContent) {
|
|
|
144
195
|
...profile,
|
|
145
196
|
managedFieldInsertIndex: findManagedFieldInsertIndex(configContent, profile.sectionStart, profile.sectionEnd),
|
|
146
197
|
})),
|
|
198
|
+
modelProviders,
|
|
147
199
|
};
|
|
148
200
|
}
|
|
149
201
|
/**
|
|
@@ -151,10 +203,12 @@ function parseStructuredConfig(configContent) {
|
|
|
151
203
|
*/
|
|
152
204
|
function buildManagedProfileViews(document, providers) {
|
|
153
205
|
const linkMap = buildProfileLinkMap(providers);
|
|
206
|
+
const modelProviderMap = new Map(document.modelProviders.map((provider) => [provider.name, provider]));
|
|
154
207
|
const views = [];
|
|
155
208
|
const seen = new Set();
|
|
156
209
|
for (const section of document.profiles) {
|
|
157
210
|
const linkInfo = linkMap.get(section.name) ?? { linkedProviders: [], managed: false };
|
|
211
|
+
const modelProviderSection = section.modelProvider ? modelProviderMap.get(section.modelProvider) ?? null : null;
|
|
158
212
|
seen.add(section.name);
|
|
159
213
|
views.push({
|
|
160
214
|
name: section.name,
|
|
@@ -162,8 +216,10 @@ function buildManagedProfileViews(document, providers) {
|
|
|
162
216
|
isActive: document.activeProfile === section.name,
|
|
163
217
|
linkedProviders: [...linkInfo.linkedProviders].sort(),
|
|
164
218
|
model: section.model,
|
|
165
|
-
|
|
166
|
-
|
|
219
|
+
modelProvider: section.modelProvider,
|
|
220
|
+
baseUrl: modelProviderSection?.baseUrl ?? null,
|
|
221
|
+
envKey: modelProviderSection?.envKey ?? null,
|
|
222
|
+
managedFields: collectManagedFields(section.model, section.modelProvider),
|
|
167
223
|
source: linkInfo.managed ? "managed" : "unmanaged",
|
|
168
224
|
});
|
|
169
225
|
}
|
|
@@ -177,7 +233,9 @@ function buildManagedProfileViews(document, providers) {
|
|
|
177
233
|
isActive: document.activeProfile === profile,
|
|
178
234
|
linkedProviders: [...linkInfo.linkedProviders].sort(),
|
|
179
235
|
model: null,
|
|
236
|
+
modelProvider: null,
|
|
180
237
|
baseUrl: null,
|
|
238
|
+
envKey: null,
|
|
181
239
|
managedFields: [],
|
|
182
240
|
source: "orphaned-reference",
|
|
183
241
|
});
|
|
@@ -210,6 +268,60 @@ function collectConfigConsistencyIssues(document, providers) {
|
|
|
210
268
|
providers: [...view.linkedProviders],
|
|
211
269
|
});
|
|
212
270
|
}
|
|
271
|
+
if (view.source !== "orphaned-reference") {
|
|
272
|
+
if (!view.modelProvider) {
|
|
273
|
+
issues.push({
|
|
274
|
+
code: "MODEL_PROVIDER_MISSING",
|
|
275
|
+
profile: view.name,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
if (view.modelProvider !== view.name) {
|
|
280
|
+
issues.push({
|
|
281
|
+
code: "MODEL_PROVIDER_NAME_MISMATCH",
|
|
282
|
+
profile: view.name,
|
|
283
|
+
modelProvider: view.modelProvider,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
const modelProviderSection = document.modelProviders.find((entry) => entry.name === view.modelProvider);
|
|
287
|
+
if (!modelProviderSection) {
|
|
288
|
+
issues.push({
|
|
289
|
+
code: "MODEL_PROVIDER_SECTION_MISSING",
|
|
290
|
+
profile: view.name,
|
|
291
|
+
modelProvider: view.modelProvider,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
else if (!modelProviderSection.baseUrl) {
|
|
295
|
+
issues.push({
|
|
296
|
+
code: "MODEL_PROVIDER_BASE_URL_MISSING",
|
|
297
|
+
profile: view.name,
|
|
298
|
+
modelProvider: view.modelProvider,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
else if (!modelProviderSection.envKey) {
|
|
302
|
+
issues.push({
|
|
303
|
+
code: "MODEL_PROVIDER_ENV_KEY_MISSING",
|
|
304
|
+
profile: view.name,
|
|
305
|
+
modelProvider: view.modelProvider,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
for (const providerName of view.linkedProviders) {
|
|
310
|
+
const provider = providers?.providers[providerName];
|
|
311
|
+
if (!provider) {
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (provider.envKey !== view.envKey) {
|
|
315
|
+
issues.push({
|
|
316
|
+
code: "PROVIDER_ENV_KEY_MISMATCH",
|
|
317
|
+
provider: providerName,
|
|
318
|
+
profile: view.name,
|
|
319
|
+
providerEnvKey: provider.envKey,
|
|
320
|
+
runtimeEnvKey: view.envKey,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
213
325
|
}
|
|
214
326
|
if (document.activeProfile) {
|
|
215
327
|
const activeLinkInfo = buildProfileLinkMap(providers).get(document.activeProfile);
|
|
@@ -219,6 +331,13 @@ function collectConfigConsistencyIssues(document, providers) {
|
|
|
219
331
|
profile: document.activeProfile,
|
|
220
332
|
});
|
|
221
333
|
}
|
|
334
|
+
else if (activeLinkInfo.linkedProviders.length > 1) {
|
|
335
|
+
issues.push({
|
|
336
|
+
code: "ACTIVE_PROVIDER_UNRESOLVED",
|
|
337
|
+
profile: document.activeProfile,
|
|
338
|
+
providers: [...activeLinkInfo.linkedProviders],
|
|
339
|
+
});
|
|
340
|
+
}
|
|
222
341
|
}
|
|
223
342
|
return issues.sort((left, right) => {
|
|
224
343
|
if (left.profile === right.profile) {
|
|
@@ -232,21 +351,32 @@ function collectConfigConsistencyIssues(document, providers) {
|
|
|
232
351
|
*/
|
|
233
352
|
function validateManagedProfileCreation(profile, fields) {
|
|
234
353
|
const model = fields.model?.trim() ?? "";
|
|
235
|
-
const
|
|
236
|
-
if (!model || !
|
|
237
|
-
throw (0, errors_1.cliError)("MANAGED_PROFILE_FIELDS_MISSING", `Managed profile "${profile}" requires both model and
|
|
354
|
+
const modelProvider = fields.modelProvider?.trim() ?? "";
|
|
355
|
+
if (!model || !modelProvider) {
|
|
356
|
+
throw (0, errors_1.cliError)("MANAGED_PROFILE_FIELDS_MISSING", `Managed profile "${profile}" requires both model and model_provider.`, {
|
|
238
357
|
profile,
|
|
239
358
|
missingFields: [
|
|
240
359
|
!model ? "model" : null,
|
|
241
|
-
!
|
|
360
|
+
!modelProvider ? "model_provider" : null,
|
|
242
361
|
].filter((value) => Boolean(value)),
|
|
243
362
|
});
|
|
244
363
|
}
|
|
245
364
|
return {
|
|
246
365
|
model,
|
|
247
|
-
|
|
366
|
+
modelProvider,
|
|
248
367
|
};
|
|
249
368
|
}
|
|
369
|
+
/**
|
|
370
|
+
* Normalizes a profile name into the default env_key used for generated runtime sections.
|
|
371
|
+
*/
|
|
372
|
+
function buildManagedProfileEnvKey(profile) {
|
|
373
|
+
const normalized = profile
|
|
374
|
+
.trim()
|
|
375
|
+
.replace(/[^A-Za-z0-9]+/g, "_")
|
|
376
|
+
.replace(/^_+|_+$/g, "")
|
|
377
|
+
.toUpperCase();
|
|
378
|
+
return `${normalized || "PROVIDER"}_API_KEY`;
|
|
379
|
+
}
|
|
250
380
|
/**
|
|
251
381
|
* Computes keep/delete/switch outcomes when a provider leaves or changes profiles.
|
|
252
382
|
*/
|
|
@@ -297,9 +427,12 @@ function planProfileLifecycleOutcome(args) {
|
|
|
297
427
|
function planConfigMutation(document, args) {
|
|
298
428
|
const operations = [];
|
|
299
429
|
const createdProfileSections = [];
|
|
430
|
+
const createdModelProviderSections = [];
|
|
300
431
|
const deletedProfileSections = [];
|
|
301
432
|
const updatedProfiles = [];
|
|
433
|
+
const updatedModelProviders = [];
|
|
302
434
|
const sectionMap = new Map(document.profiles.map((profile) => [profile.name, profile]));
|
|
435
|
+
const modelProviderSectionMap = new Map(document.modelProviders.map((entry) => [entry.name, entry]));
|
|
303
436
|
if (args.setActiveProfile && args.setActiveProfile !== document.activeProfile) {
|
|
304
437
|
const quoted = `"${args.setActiveProfile}"`;
|
|
305
438
|
if (document.activeProfileRange) {
|
|
@@ -344,7 +477,7 @@ function planConfigMutation(document, args) {
|
|
|
344
477
|
index: document.rawText.length,
|
|
345
478
|
text: `${prefix}[profiles.${profileName}]${document.lineEnding}` +
|
|
346
479
|
`model = ${JSON.stringify(requiredFields.model)}${document.lineEnding}` +
|
|
347
|
-
`
|
|
480
|
+
`model_provider = ${JSON.stringify(requiredFields.modelProvider)}${document.lineEnding}`,
|
|
348
481
|
});
|
|
349
482
|
createdProfileSections.push(profileName);
|
|
350
483
|
continue;
|
|
@@ -354,11 +487,46 @@ function planConfigMutation(document, args) {
|
|
|
354
487
|
updatedProfiles.push(profileName);
|
|
355
488
|
}
|
|
356
489
|
}
|
|
490
|
+
for (const [profileName, fields] of Object.entries(args.upsertModelProviders ?? {}).sort(([left], [right]) => left.localeCompare(right))) {
|
|
491
|
+
const section = modelProviderSectionMap.get(profileName);
|
|
492
|
+
if (!section) {
|
|
493
|
+
const baseUrl = fields.baseUrl?.trim() ?? "";
|
|
494
|
+
const envKey = fields.envKey?.trim() ?? "";
|
|
495
|
+
if (!baseUrl || !envKey) {
|
|
496
|
+
throw (0, errors_1.cliError)("MANAGED_PROFILE_FIELDS_MISSING", `Model provider "${profileName}" requires both base_url and env_key.`, {
|
|
497
|
+
profile: profileName,
|
|
498
|
+
modelProvider: profileName,
|
|
499
|
+
missingFields: [
|
|
500
|
+
!baseUrl ? "base_url" : null,
|
|
501
|
+
!envKey ? "env_key" : null,
|
|
502
|
+
].filter((value) => Boolean(value)),
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
const prefix = document.rawText.length > 0 && !document.rawText.endsWith(document.lineEnding)
|
|
506
|
+
? document.lineEnding
|
|
507
|
+
: "";
|
|
508
|
+
operations.push({
|
|
509
|
+
kind: "insert-at",
|
|
510
|
+
index: document.rawText.length,
|
|
511
|
+
text: `${prefix}[model_providers.${profileName}]${document.lineEnding}` +
|
|
512
|
+
`base_url = ${JSON.stringify(baseUrl)}${document.lineEnding}` +
|
|
513
|
+
`env_key = ${JSON.stringify(envKey)}${document.lineEnding}`,
|
|
514
|
+
});
|
|
515
|
+
createdModelProviderSections.push(profileName);
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
const sectionUpdated = planModelProviderFieldMutation(section, fields, operations);
|
|
519
|
+
if (sectionUpdated) {
|
|
520
|
+
updatedModelProviders.push(profileName);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
357
523
|
return {
|
|
358
524
|
operations,
|
|
359
525
|
createdProfileSections,
|
|
526
|
+
createdModelProviderSections,
|
|
360
527
|
deletedProfileSections,
|
|
361
528
|
updatedProfiles,
|
|
529
|
+
updatedModelProviders,
|
|
362
530
|
switchedActiveProfile: Boolean(args.setActiveProfile && args.setActiveProfile !== document.activeProfile),
|
|
363
531
|
};
|
|
364
532
|
}
|
|
@@ -384,7 +552,7 @@ function applyPatchOperations(rawText, operations) {
|
|
|
384
552
|
function planSectionFieldMutation(document, section, fields, operations) {
|
|
385
553
|
let updated = false;
|
|
386
554
|
const modelText = fields.model !== undefined ? JSON.stringify(fields.model) : null;
|
|
387
|
-
const
|
|
555
|
+
const modelProviderText = fields.modelProvider !== undefined ? JSON.stringify(fields.modelProvider) : null;
|
|
388
556
|
const inserts = [];
|
|
389
557
|
if (modelText !== null && section.modelValueRange) {
|
|
390
558
|
if (section.model !== fields.model) {
|
|
@@ -401,6 +569,38 @@ function planSectionFieldMutation(document, section, fields, operations) {
|
|
|
401
569
|
inserts.push(`model = ${modelText}${document.lineEnding}`);
|
|
402
570
|
updated = true;
|
|
403
571
|
}
|
|
572
|
+
if (modelProviderText !== null && section.modelProviderValueRange) {
|
|
573
|
+
if (section.modelProvider !== fields.modelProvider) {
|
|
574
|
+
operations.push({
|
|
575
|
+
kind: "replace-range",
|
|
576
|
+
start: section.modelProviderValueRange.start,
|
|
577
|
+
end: section.modelProviderValueRange.end,
|
|
578
|
+
text: modelProviderText,
|
|
579
|
+
});
|
|
580
|
+
updated = true;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
else if (modelProviderText !== null && !section.modelProviderValueRange) {
|
|
584
|
+
inserts.push(`model_provider = ${modelProviderText}${document.lineEnding}`);
|
|
585
|
+
updated = true;
|
|
586
|
+
}
|
|
587
|
+
if (inserts.length > 0) {
|
|
588
|
+
operations.push({
|
|
589
|
+
kind: "insert-at",
|
|
590
|
+
index: section.managedFieldInsertIndex,
|
|
591
|
+
text: inserts.join(""),
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
return updated;
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Plans base_url/env_key updates for one model_providers section.
|
|
598
|
+
*/
|
|
599
|
+
function planModelProviderFieldMutation(section, fields, operations) {
|
|
600
|
+
let updated = false;
|
|
601
|
+
const baseUrlText = fields.baseUrl !== undefined ? JSON.stringify(fields.baseUrl) : null;
|
|
602
|
+
const envKeyText = fields.envKey !== undefined ? JSON.stringify(fields.envKey) : null;
|
|
603
|
+
const inserts = [];
|
|
404
604
|
if (baseUrlText !== null && section.baseUrlValueRange) {
|
|
405
605
|
if (section.baseUrl !== fields.baseUrl) {
|
|
406
606
|
operations.push({
|
|
@@ -412,16 +612,30 @@ function planSectionFieldMutation(document, section, fields, operations) {
|
|
|
412
612
|
updated = true;
|
|
413
613
|
}
|
|
414
614
|
}
|
|
415
|
-
else if (baseUrlText !== null
|
|
416
|
-
inserts.push(`base_url = ${baseUrlText}
|
|
417
|
-
|
|
615
|
+
else if (baseUrlText !== null) {
|
|
616
|
+
inserts.push(`base_url = ${baseUrlText}`);
|
|
617
|
+
}
|
|
618
|
+
if (envKeyText !== null && section.envKeyValueRange) {
|
|
619
|
+
if (section.envKey !== fields.envKey) {
|
|
620
|
+
operations.push({
|
|
621
|
+
kind: "replace-range",
|
|
622
|
+
start: section.envKeyValueRange.start,
|
|
623
|
+
end: section.envKeyValueRange.end,
|
|
624
|
+
text: envKeyText,
|
|
625
|
+
});
|
|
626
|
+
updated = true;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
else if (envKeyText !== null) {
|
|
630
|
+
inserts.push(`env_key = ${envKeyText}`);
|
|
418
631
|
}
|
|
419
632
|
if (inserts.length > 0) {
|
|
420
633
|
operations.push({
|
|
421
634
|
kind: "insert-at",
|
|
422
|
-
index: section.
|
|
423
|
-
text: inserts.join("")
|
|
635
|
+
index: section.sectionEnd,
|
|
636
|
+
text: `${inserts.join("\n")}\n`,
|
|
424
637
|
});
|
|
638
|
+
updated = true;
|
|
425
639
|
}
|
|
426
640
|
return updated;
|
|
427
641
|
}
|
|
@@ -483,13 +697,13 @@ function findManagedFieldInsertIndex(rawText, sectionStart, sectionEnd) {
|
|
|
483
697
|
}
|
|
484
698
|
return sectionStart + lines[lastMeaningfulIndex].end;
|
|
485
699
|
}
|
|
486
|
-
function collectManagedFields(model,
|
|
700
|
+
function collectManagedFields(model, modelProvider) {
|
|
487
701
|
const fields = [];
|
|
488
702
|
if (model !== null) {
|
|
489
703
|
fields.push("model");
|
|
490
704
|
}
|
|
491
|
-
if (
|
|
492
|
-
fields.push("
|
|
705
|
+
if (modelProvider !== null) {
|
|
706
|
+
fields.push("model_provider");
|
|
493
707
|
}
|
|
494
708
|
return fields;
|
|
495
709
|
}
|
package/dist/domain/providers.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.validateProvidersShape = validateProvidersShape;
|
|
|
4
4
|
exports.cleanProviderRecord = cleanProviderRecord;
|
|
5
5
|
exports.sortProviders = sortProviders;
|
|
6
6
|
exports.findProviderByProfile = findProviderByProfile;
|
|
7
|
+
exports.findProvidersByProfile = findProvidersByProfile;
|
|
7
8
|
exports.maskSecret = maskSecret;
|
|
8
9
|
/**
|
|
9
10
|
* Validates and normalizes unknown JSON into the providers.json domain model.
|
|
@@ -28,6 +29,9 @@ function validateProvidersShape(input) {
|
|
|
28
29
|
if (typeof provider.apiKey !== "string" || provider.apiKey.trim() === "") {
|
|
29
30
|
throw new Error(`Provider "${name}" is missing a valid apiKey.`);
|
|
30
31
|
}
|
|
32
|
+
if (typeof provider.envKey !== "string" || provider.envKey.trim() === "") {
|
|
33
|
+
throw new Error(`Provider "${name}" is missing a valid envKey.`);
|
|
34
|
+
}
|
|
31
35
|
if (provider.baseUrl !== undefined && typeof provider.baseUrl !== "string") {
|
|
32
36
|
throw new Error(`Provider "${name}" has an invalid baseUrl.`);
|
|
33
37
|
}
|
|
@@ -42,6 +46,7 @@ function validateProvidersShape(input) {
|
|
|
42
46
|
providers[name] = cleanProviderRecord({
|
|
43
47
|
profile: provider.profile,
|
|
44
48
|
apiKey: provider.apiKey,
|
|
49
|
+
envKey: provider.envKey,
|
|
45
50
|
baseUrl: provider.baseUrl,
|
|
46
51
|
note: provider.note,
|
|
47
52
|
tags: provider.tags,
|
|
@@ -56,6 +61,7 @@ function cleanProviderRecord(record) {
|
|
|
56
61
|
const next = {
|
|
57
62
|
profile: record.profile.trim(),
|
|
58
63
|
apiKey: record.apiKey.trim(),
|
|
64
|
+
envKey: record.envKey.trim(),
|
|
59
65
|
};
|
|
60
66
|
if (record.baseUrl && record.baseUrl.trim() !== "") {
|
|
61
67
|
next.baseUrl = record.baseUrl.trim();
|
|
@@ -84,12 +90,20 @@ function sortProviders(providers) {
|
|
|
84
90
|
* Finds the provider name associated with a given Codex profile.
|
|
85
91
|
*/
|
|
86
92
|
function findProviderByProfile(providers, profile) {
|
|
93
|
+
const matches = findProvidersByProfile(providers, profile);
|
|
94
|
+
return matches.length > 0 ? matches[0] : null;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Returns all provider names associated with a given Codex profile.
|
|
98
|
+
*/
|
|
99
|
+
function findProvidersByProfile(providers, profile) {
|
|
100
|
+
const matches = [];
|
|
87
101
|
for (const [name, provider] of Object.entries(providers.providers)) {
|
|
88
102
|
if (provider.profile === profile) {
|
|
89
|
-
|
|
103
|
+
matches.push(name);
|
|
90
104
|
}
|
|
91
105
|
}
|
|
92
|
-
return
|
|
106
|
+
return matches.sort();
|
|
93
107
|
}
|
|
94
108
|
/**
|
|
95
109
|
* Masks a secret for human-readable output while preserving a short fingerprint.
|
package/dist/domain/setup.js
CHANGED