@minniexcode/codex-switch 0.0.4 → 0.0.6
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 +35 -97
- package/dist/app/add-provider.js +40 -3
- package/dist/app/edit-provider.js +76 -3
- 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 +47 -3
- package/dist/app/list-backups.js +1 -1
- package/dist/app/list-config-profiles.js +30 -0
- package/dist/app/list-providers.js +1 -1
- package/dist/app/remove-provider.js +35 -3
- package/dist/app/rollback-backup.js +1 -1
- package/dist/app/rollback-latest.js +1 -1
- package/dist/app/run-doctor.js +44 -26
- package/dist/app/run-mutation.js +2 -2
- package/dist/app/setup-codex.js +37 -20
- package/dist/app/show-config.js +34 -0
- package/dist/app/show-provider.js +1 -1
- package/dist/app/switch-provider.js +8 -5
- package/dist/cli/add-interactive.js +7 -106
- package/dist/cli/args.js +5 -126
- package/dist/cli/help.js +5 -276
- package/dist/cli/interactive.js +16 -171
- package/dist/cli/output.js +23 -1
- package/dist/cli/prompt.js +3 -108
- package/dist/cli.js +10 -315
- package/dist/commands/args.js +132 -0
- package/dist/commands/dispatch.js +16 -0
- package/dist/commands/handlers.js +391 -0
- package/dist/commands/help.js +119 -0
- package/dist/commands/registry.js +291 -0
- package/dist/commands/types.js +2 -0
- package/dist/domain/config.js +548 -39
- package/dist/infra/backup-repo.js +8 -208
- package/dist/infra/codex-cli.js +8 -113
- package/dist/infra/codex-discovery.js +3 -41
- package/dist/infra/codex-paths.js +5 -69
- package/dist/infra/config-repo.js +161 -9
- package/dist/infra/fs-utils.js +7 -95
- package/dist/infra/lock-repo.js +3 -97
- package/dist/infra/providers-repo.js +7 -96
- package/dist/interaction/add-interactive.js +108 -0
- package/dist/interaction/interactive.js +216 -0
- package/dist/interaction/prompt.js +110 -0
- package/dist/runtime/codex-cli.js +130 -0
- package/dist/runtime/codex-probe.js +50 -0
- package/dist/runtime/types.js +2 -0
- package/dist/storage/backup-repo.js +210 -0
- package/dist/storage/codex-paths.js +71 -0
- package/dist/storage/config-repo.js +208 -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 +932 -0
- package/docs/Design/codex-switch-v0.0.6-design.md +708 -0
- package/docs/PRD/codex-switch-prd-v0.0.5-to-v0.1.0.md +340 -0
- package/docs/PRD/codex-switch-prd-v0.1.0.md +215 -291
- package/docs/PRD/codex-switch-prd.md +1 -1
- package/docs/cli-usage.md +2 -1
- package/docs/codex-switch-technical-architecture.md +73 -4
- package/docs/test-report-0.0.5.md +163 -0
- package/docs/testing.md +131 -0
- package/package.json +1 -1
- /package/docs/{codex-switch-v0.0.4-design.md → Design/codex-switch-v0.0.4-design.md} +0 -0
package/README.md
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
## @minniexcode/codex-switch
|
|
2
2
|
|
|
3
3
|
`codex-switch` is a local-first CLI for managing and switching Codex provider/profile configuration safely.
|
|
4
4
|
|
|
5
|
-
`codex-switch` 是一个本地优先的 CLI,用来安全地管理和切换 Codex 的 provider/profile 配置。
|
|
6
|
-
|
|
7
5
|
It is designed for users who work with multiple Codex providers, API keys, or profiles and want a repeatable, backup-first workflow instead of manually editing files under `~/.codex/`.
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
中文版: [README.CN.md](./README.CN.md)
|
|
10
8
|
|
|
11
|
-
## Overview
|
|
9
|
+
## Overview
|
|
12
10
|
|
|
13
11
|
What it does:
|
|
14
12
|
|
|
@@ -17,22 +15,11 @@ What it does:
|
|
|
17
15
|
- Switch the active provider/profile safely
|
|
18
16
|
- Import and export provider definitions
|
|
19
17
|
- Run diagnostics and detect local drift
|
|
20
|
-
- List backups and
|
|
21
|
-
|
|
22
|
-
它可以完成的事情:
|
|
23
|
-
|
|
24
|
-
- 从现有 Codex 目录初始化 `providers.json`
|
|
25
|
-
- 查看、新增、编辑、删除 provider 记录
|
|
26
|
-
- 安全切换当前激活的 provider/profile
|
|
27
|
-
- 导入和导出 provider 配置
|
|
28
|
-
- 运行诊断并识别本地配置漂移
|
|
29
|
-
- 查看备份并回滚到之前的受管状态
|
|
30
|
-
|
|
31
|
-
Current version: `0.0.4`
|
|
18
|
+
- List backups and roll back to a previous managed state
|
|
32
19
|
|
|
33
|
-
|
|
20
|
+
Current version: `0.0.6`
|
|
34
21
|
|
|
35
|
-
## Install
|
|
22
|
+
## Install
|
|
36
23
|
|
|
37
24
|
Install globally:
|
|
38
25
|
|
|
@@ -46,31 +33,13 @@ Or run directly:
|
|
|
46
33
|
npx @minniexcode/codex-switch --help
|
|
47
34
|
```
|
|
48
35
|
|
|
49
|
-
全局安装:
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
npm install -g @minniexcode/codex-switch
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
或者直接运行:
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
npx @minniexcode/codex-switch --help
|
|
59
|
-
```
|
|
60
|
-
|
|
61
36
|
CLI entry:
|
|
62
37
|
|
|
63
38
|
```bash
|
|
64
39
|
codexs --help
|
|
65
40
|
```
|
|
66
41
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
codexs --help
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## Quick Start | 快速开始
|
|
42
|
+
## Quick Start
|
|
74
43
|
|
|
75
44
|
Take over an existing Codex directory:
|
|
76
45
|
|
|
@@ -78,12 +47,6 @@ Take over an existing Codex directory:
|
|
|
78
47
|
codexs setup
|
|
79
48
|
```
|
|
80
49
|
|
|
81
|
-
接管当前已有的 Codex 目录:
|
|
82
|
-
|
|
83
|
-
```bash
|
|
84
|
-
codexs setup
|
|
85
|
-
```
|
|
86
|
-
|
|
87
50
|
Inspect managed providers:
|
|
88
51
|
|
|
89
52
|
```bash
|
|
@@ -91,13 +54,6 @@ codexs list
|
|
|
91
54
|
codexs show my-provider
|
|
92
55
|
```
|
|
93
56
|
|
|
94
|
-
查看已管理的 provider:
|
|
95
|
-
|
|
96
|
-
```bash
|
|
97
|
-
codexs list
|
|
98
|
-
codexs show my-provider
|
|
99
|
-
```
|
|
100
|
-
|
|
101
57
|
Add and switch:
|
|
102
58
|
|
|
103
59
|
```bash
|
|
@@ -105,13 +61,6 @@ codexs add my-provider --profile my-provider --api-key sk-xxx
|
|
|
105
61
|
codexs switch my-provider
|
|
106
62
|
```
|
|
107
63
|
|
|
108
|
-
新增并切换:
|
|
109
|
-
|
|
110
|
-
```bash
|
|
111
|
-
codexs add my-provider --profile my-provider --api-key sk-xxx
|
|
112
|
-
codexs switch my-provider
|
|
113
|
-
```
|
|
114
|
-
|
|
115
64
|
Check runtime state:
|
|
116
65
|
|
|
117
66
|
```bash
|
|
@@ -120,15 +69,7 @@ codexs status
|
|
|
120
69
|
codexs doctor
|
|
121
70
|
```
|
|
122
71
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
```bash
|
|
126
|
-
codexs current
|
|
127
|
-
codexs status
|
|
128
|
-
codexs doctor
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
## Common Commands | 常用命令
|
|
72
|
+
## Common Commands
|
|
132
73
|
|
|
133
74
|
```bash
|
|
134
75
|
codexs setup
|
|
@@ -154,19 +95,10 @@ codexs help switch
|
|
|
154
95
|
codexs help setup
|
|
155
96
|
```
|
|
156
97
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
```bash
|
|
160
|
-
codexs help switch
|
|
161
|
-
codexs help setup
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
## How It Works | 工作方式
|
|
98
|
+
## How It Works
|
|
165
99
|
|
|
166
100
|
By default, `codex-switch` operates on `~/.codex/`, and you can override the target with `--codex-dir`.
|
|
167
101
|
|
|
168
|
-
`codex-switch` 默认围绕 `~/.codex/` 工作,也可以通过 `--codex-dir` 指向其他目录。
|
|
169
|
-
|
|
170
102
|
Managed files:
|
|
171
103
|
|
|
172
104
|
```text
|
|
@@ -177,23 +109,19 @@ Managed files:
|
|
|
177
109
|
backups/
|
|
178
110
|
```
|
|
179
111
|
|
|
180
|
-
|
|
112
|
+
Notes:
|
|
181
113
|
|
|
182
114
|
- `providers.json` is the managed provider registry
|
|
183
115
|
- `config.toml` and `auth.json` represent runtime state
|
|
184
116
|
- mutating commands back up before writing
|
|
185
117
|
- rollback is available after failed or undesired changes
|
|
186
118
|
|
|
187
|
-
|
|
188
|
-
- `config.toml` 和 `auth.json` 代表当前运行态
|
|
189
|
-
- 所有写操作都会先备份再写入
|
|
190
|
-
- 变更失败或结果不符合预期时可以回滚
|
|
191
|
-
|
|
192
|
-
## Automation | 自动化
|
|
119
|
+
## Automation
|
|
193
120
|
|
|
194
121
|
This CLI supports both human TTY use and non-interactive automation.
|
|
195
122
|
|
|
196
|
-
|
|
123
|
+
Current exception:
|
|
124
|
+
- `setup` in `0.0.6` is intentionally TTY-only for adopt initialization. It requires interactive profile selection and provider detail collection, and non-interactive/`--json` runs fail fast with a structured error.
|
|
197
125
|
|
|
198
126
|
Recommended global flags:
|
|
199
127
|
|
|
@@ -204,28 +132,38 @@ Recommended global flags:
|
|
|
204
132
|
--version
|
|
205
133
|
```
|
|
206
134
|
|
|
207
|
-
|
|
135
|
+
Recommendations:
|
|
208
136
|
|
|
209
137
|
- use `--json` for stable machine-readable output
|
|
210
138
|
- pass all required arguments explicitly in scripts or CI
|
|
211
139
|
- use `--codex-dir <path>` for sandbox or test environments
|
|
212
140
|
|
|
213
|
-
|
|
214
|
-
- 在非交互环境中显式传入所有必需参数
|
|
215
|
-
- 在测试环境中优先配合 `--codex-dir <path>` 使用
|
|
141
|
+
## Testing
|
|
216
142
|
|
|
217
|
-
|
|
143
|
+
Build and test locally:
|
|
218
144
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
145
|
+
```bash
|
|
146
|
+
npm run build
|
|
147
|
+
npm test
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The repository includes a development fixture under `dev-codex/local-sandbox` plus dedicated test docs:
|
|
151
|
+
|
|
152
|
+
- [Testing Guide](./docs/testing.md)
|
|
153
|
+
- [Test Report for 0.0.5](./docs/test-report-0.0.5.md)
|
|
154
|
+
|
|
155
|
+
## Documentation
|
|
156
|
+
|
|
157
|
+
- [Chinese README](./README.CN.md)
|
|
223
158
|
- [AI README](./README.AI.md)
|
|
224
|
-
- [
|
|
159
|
+
- [Detailed CLI Usage](./docs/cli-usage.md)
|
|
160
|
+
- [Testing Guide](./docs/testing.md)
|
|
161
|
+
- [Test Report for 0.0.5](./docs/test-report-0.0.5.md)
|
|
225
162
|
- [Product Overview](./docs/codex-switch-product-overview.md)
|
|
226
163
|
- [Technical Architecture](./docs/codex-switch-technical-architecture.md)
|
|
227
|
-
- [0.0.
|
|
164
|
+
- [Design Doc 0.0.5](./docs/Design/codex-switch-v0.0.5-design.md)
|
|
165
|
+
- [Design Doc 0.0.4](./docs/Design/codex-switch-v0.0.4-design.md)
|
|
228
166
|
|
|
229
|
-
## License
|
|
167
|
+
## License
|
|
230
168
|
|
|
231
169
|
MIT
|
package/dist/app/add-provider.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.addProvider = addProvider;
|
|
4
|
+
const config_1 = require("../domain/config");
|
|
4
5
|
const providers_1 = require("../domain/providers");
|
|
5
6
|
const errors_1 = require("../domain/errors");
|
|
6
|
-
const
|
|
7
|
-
const
|
|
7
|
+
const config_repo_1 = require("../storage/config-repo");
|
|
8
|
+
const fs_utils_1 = require("../storage/fs-utils");
|
|
9
|
+
const providers_repo_1 = require("../storage/providers-repo");
|
|
8
10
|
const run_mutation_1 = require("./run-mutation");
|
|
9
11
|
/**
|
|
10
12
|
* Adds a new provider record to the managed providers registry.
|
|
@@ -15,6 +17,28 @@ function addProvider(args) {
|
|
|
15
17
|
if (providers.providers[args.providerName]) {
|
|
16
18
|
throw (0, errors_1.cliError)("INVALID_IMPORT_FILE", `Provider "${args.providerName}" already exists.`);
|
|
17
19
|
}
|
|
20
|
+
const document = (0, config_repo_1.readStructuredConfig)(args.configPath);
|
|
21
|
+
const existingProfile = document.profiles.find((profile) => profile.name === args.profile);
|
|
22
|
+
if (!existingProfile && !args.createProfile) {
|
|
23
|
+
throw (0, errors_1.cliError)("PROFILE_NOT_FOUND", `Profile "${args.profile}" does not exist in config.toml.`, {
|
|
24
|
+
profile: args.profile,
|
|
25
|
+
provider: args.providerName,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
const upsertProfiles = !existingProfile && args.createProfile
|
|
29
|
+
? {
|
|
30
|
+
[args.profile]: (0, config_1.validateManagedProfileCreation)(args.profile, {
|
|
31
|
+
model: args.model ?? undefined,
|
|
32
|
+
modelProvider: args.profile,
|
|
33
|
+
}),
|
|
34
|
+
}
|
|
35
|
+
: undefined;
|
|
36
|
+
if (!existingProfile && args.createProfile) {
|
|
37
|
+
(0, config_repo_1.requireModelProviderRuntimeSection)(document, args.profile);
|
|
38
|
+
}
|
|
39
|
+
if (existingProfile) {
|
|
40
|
+
(0, config_repo_1.requireManagedProfileRuntime)(document, providers, args.profile);
|
|
41
|
+
}
|
|
18
42
|
const next = {
|
|
19
43
|
providers: {
|
|
20
44
|
...providers.providers,
|
|
@@ -32,13 +56,26 @@ function addProvider(args) {
|
|
|
32
56
|
backupsDir: args.backupsDir,
|
|
33
57
|
latestBackupPath: args.latestBackupPath,
|
|
34
58
|
operation: "add",
|
|
35
|
-
files: [
|
|
59
|
+
files: [
|
|
60
|
+
{ absolutePath: args.providersPath, relativePath: "providers.json" },
|
|
61
|
+
{ absolutePath: args.configPath, relativePath: "config.toml" },
|
|
62
|
+
],
|
|
36
63
|
mutate: () => {
|
|
64
|
+
const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
|
|
65
|
+
upsertProfiles,
|
|
66
|
+
});
|
|
37
67
|
// Persist only the normalized provider payload so later reads are deterministic.
|
|
38
68
|
(0, providers_repo_1.writeProvidersFile)(args.providersPath, next);
|
|
69
|
+
(0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
|
|
39
70
|
return {
|
|
40
71
|
provider: args.providerName,
|
|
41
72
|
profile: args.profile,
|
|
73
|
+
createdProfileSections: configPlan.createdProfileSections,
|
|
74
|
+
deletedProfileSections: configPlan.deletedProfileSections,
|
|
75
|
+
keptSharedProfiles: [],
|
|
76
|
+
switchedActiveProfile: configPlan.switchedActiveProfile,
|
|
77
|
+
adoptedProfiles: [],
|
|
78
|
+
repairedProfiles: [],
|
|
42
79
|
};
|
|
43
80
|
},
|
|
44
81
|
});
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.editProvider = editProvider;
|
|
4
4
|
const errors_1 = require("../domain/errors");
|
|
5
|
+
const config_1 = require("../domain/config");
|
|
5
6
|
const providers_1 = require("../domain/providers");
|
|
6
|
-
const
|
|
7
|
-
const
|
|
7
|
+
const config_repo_1 = require("../storage/config-repo");
|
|
8
|
+
const fs_utils_1 = require("../storage/fs-utils");
|
|
9
|
+
const providers_repo_1 = require("../storage/providers-repo");
|
|
8
10
|
const run_mutation_1 = require("./run-mutation");
|
|
9
11
|
/**
|
|
10
12
|
* Updates selected fields on a single managed provider.
|
|
@@ -12,6 +14,7 @@ const run_mutation_1 = require("./run-mutation");
|
|
|
12
14
|
function editProvider(args) {
|
|
13
15
|
(0, fs_utils_1.ensureDir)(args.codexDir);
|
|
14
16
|
const providers = (0, providers_repo_1.readProvidersFile)(args.providersPath);
|
|
17
|
+
const document = (0, config_repo_1.readStructuredConfig)(args.configPath);
|
|
15
18
|
const current = providers.providers[args.providerName];
|
|
16
19
|
if (!current) {
|
|
17
20
|
throw (0, errors_1.cliError)("PROVIDER_NOT_FOUND", `Provider "${args.providerName}" was not found.`, {
|
|
@@ -42,22 +45,92 @@ function editProvider(args) {
|
|
|
42
45
|
if (args.tags !== undefined) {
|
|
43
46
|
updatedFields.push("tags");
|
|
44
47
|
}
|
|
48
|
+
const oldProfile = current.profile;
|
|
49
|
+
const newProfile = nextRecord.profile;
|
|
50
|
+
const targetSection = document.profiles.find((profile) => profile.name === newProfile) ?? null;
|
|
51
|
+
const targetProfileExists = Boolean(targetSection);
|
|
52
|
+
let upsertProfiles;
|
|
53
|
+
if (!targetProfileExists) {
|
|
54
|
+
if (!args.createProfile) {
|
|
55
|
+
throw (0, errors_1.cliError)("PROFILE_NOT_FOUND", `Profile "${newProfile}" does not exist in config.toml.`, {
|
|
56
|
+
profile: newProfile,
|
|
57
|
+
provider: args.providerName,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
upsertProfiles = {
|
|
61
|
+
[newProfile]: (0, config_1.validateManagedProfileCreation)(newProfile, {
|
|
62
|
+
model: args.model ?? undefined,
|
|
63
|
+
modelProvider: newProfile,
|
|
64
|
+
}),
|
|
65
|
+
};
|
|
66
|
+
(0, config_repo_1.requireModelProviderRuntimeSection)(document, newProfile);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
(0, config_repo_1.requireManagedProfileRuntime)(document, providers, newProfile);
|
|
70
|
+
}
|
|
71
|
+
if (targetProfileExists && args.model !== undefined) {
|
|
72
|
+
upsertProfiles = {
|
|
73
|
+
[newProfile]: {
|
|
74
|
+
...(args.model !== undefined && args.model !== null ? { model: args.model } : {}),
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
if (args.model !== undefined && (targetSection?.model !== args.model) && !updatedFields.includes("model")) {
|
|
78
|
+
updatedFields.push("model");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const remainingLinksByProfile = new Map();
|
|
82
|
+
for (const [name, provider] of Object.entries(providers.providers)) {
|
|
83
|
+
if (name === args.providerName) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const list = remainingLinksByProfile.get(provider.profile) ?? [];
|
|
87
|
+
list.push(name);
|
|
88
|
+
remainingLinksByProfile.set(provider.profile, list);
|
|
89
|
+
}
|
|
90
|
+
if (newProfile !== oldProfile) {
|
|
91
|
+
const list = remainingLinksByProfile.get(newProfile) ?? [];
|
|
92
|
+
list.push(args.providerName);
|
|
93
|
+
remainingLinksByProfile.set(newProfile, list);
|
|
94
|
+
}
|
|
95
|
+
const lifecycle = (0, config_1.planProfileLifecycleOutcome)({
|
|
96
|
+
providerName: args.providerName,
|
|
97
|
+
oldProfile,
|
|
98
|
+
newProfile,
|
|
99
|
+
activeProfile: document.activeProfile,
|
|
100
|
+
remainingLinksByProfile,
|
|
101
|
+
switchToProfile: args.switchToProfile ?? null,
|
|
102
|
+
});
|
|
45
103
|
return (0, run_mutation_1.runMutation)({
|
|
46
104
|
codexDir: args.codexDir,
|
|
47
105
|
backupsDir: args.backupsDir,
|
|
48
106
|
latestBackupPath: args.latestBackupPath,
|
|
49
107
|
operation: "edit",
|
|
50
|
-
files: [
|
|
108
|
+
files: [
|
|
109
|
+
{ absolutePath: args.providersPath, relativePath: "providers.json" },
|
|
110
|
+
{ absolutePath: args.configPath, relativePath: "config.toml" },
|
|
111
|
+
],
|
|
51
112
|
mutate: () => {
|
|
113
|
+
const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
|
|
114
|
+
upsertProfiles,
|
|
115
|
+
deleteProfiles: lifecycle.deletedProfileSections,
|
|
116
|
+
setActiveProfile: lifecycle.nextActiveProfile,
|
|
117
|
+
});
|
|
52
118
|
(0, providers_repo_1.writeProvidersFile)(args.providersPath, {
|
|
53
119
|
providers: {
|
|
54
120
|
...providers.providers,
|
|
55
121
|
[args.providerName]: nextRecord,
|
|
56
122
|
},
|
|
57
123
|
});
|
|
124
|
+
(0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
|
|
58
125
|
return {
|
|
59
126
|
provider: args.providerName,
|
|
60
127
|
updatedFields,
|
|
128
|
+
createdProfileSections: configPlan.createdProfileSections,
|
|
129
|
+
deletedProfileSections: configPlan.deletedProfileSections,
|
|
130
|
+
keptSharedProfiles: lifecycle.keptSharedProfiles,
|
|
131
|
+
switchedActiveProfile: lifecycle.switchedActiveProfile,
|
|
132
|
+
adoptedProfiles: [],
|
|
133
|
+
repairedProfiles: [],
|
|
61
134
|
};
|
|
62
135
|
},
|
|
63
136
|
});
|
|
@@ -37,8 +37,8 @@ exports.exportProviders = exportProviders;
|
|
|
37
37
|
const fs = __importStar(require("node:fs"));
|
|
38
38
|
const path = __importStar(require("node:path"));
|
|
39
39
|
const errors_1 = require("../domain/errors");
|
|
40
|
-
const fs_utils_1 = require("../
|
|
41
|
-
const providers_repo_1 = require("../
|
|
40
|
+
const fs_utils_1 = require("../storage/fs-utils");
|
|
41
|
+
const providers_repo_1 = require("../storage/providers-repo");
|
|
42
42
|
/**
|
|
43
43
|
* Exports the current providers registry to a user-specified file.
|
|
44
44
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getCurrentProfile = getCurrentProfile;
|
|
4
|
-
const config_repo_1 = require("../
|
|
4
|
+
const config_repo_1 = require("../storage/config-repo");
|
|
5
5
|
/**
|
|
6
6
|
* Returns the currently active top-level Codex profile.
|
|
7
7
|
*/
|
package/dist/app/get-status.js
CHANGED
|
@@ -37,7 +37,8 @@ exports.getStatus = getStatus;
|
|
|
37
37
|
const fs = __importStar(require("node:fs"));
|
|
38
38
|
const config_1 = require("../domain/config");
|
|
39
39
|
const runtime_state_1 = require("../domain/runtime-state");
|
|
40
|
-
const
|
|
40
|
+
const config_repo_1 = require("../storage/config-repo");
|
|
41
|
+
const providers_repo_1 = require("../storage/providers-repo");
|
|
41
42
|
/**
|
|
42
43
|
* Reports the current on-disk runtime state and how it maps back to managed providers.
|
|
43
44
|
*/
|
|
@@ -47,9 +48,13 @@ function getStatus(codexDir, configPath, providersPath) {
|
|
|
47
48
|
let currentProfile = null;
|
|
48
49
|
const warnings = [];
|
|
49
50
|
const providers = providersExists ? (0, providers_repo_1.readProvidersFile)(providersPath) : null;
|
|
51
|
+
let configViews = [];
|
|
52
|
+
let consistencyIssues = [];
|
|
50
53
|
if (configExists) {
|
|
51
|
-
const
|
|
52
|
-
currentProfile =
|
|
54
|
+
const document = (0, config_repo_1.readStructuredConfig)(configPath);
|
|
55
|
+
currentProfile = document.activeProfile;
|
|
56
|
+
configViews = (0, config_1.buildManagedProfileViews)(document, providers);
|
|
57
|
+
consistencyIssues = (0, config_1.collectConfigConsistencyIssues)(document, providers);
|
|
53
58
|
if (!currentProfile) {
|
|
54
59
|
warnings.push("config.toml exists but has no top-level profile.");
|
|
55
60
|
}
|
|
@@ -70,6 +75,8 @@ function getStatus(codexDir, configPath, providersPath) {
|
|
|
70
75
|
currentProfileMapped: liveState.profileMapped,
|
|
71
76
|
provider: liveState.mappedProvider,
|
|
72
77
|
liveState,
|
|
78
|
+
configProfiles: configViews,
|
|
79
|
+
issues: consistencyIssues,
|
|
73
80
|
},
|
|
74
81
|
};
|
|
75
82
|
}
|
|
@@ -36,10 +36,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.importProviders = importProviders;
|
|
37
37
|
const fs = __importStar(require("node:fs"));
|
|
38
38
|
const path = __importStar(require("node:path"));
|
|
39
|
+
const config_1 = require("../domain/config");
|
|
39
40
|
const providers_1 = require("../domain/providers");
|
|
40
41
|
const errors_1 = require("../domain/errors");
|
|
41
|
-
const
|
|
42
|
-
const
|
|
42
|
+
const config_repo_1 = require("../storage/config-repo");
|
|
43
|
+
const fs_utils_1 = require("../storage/fs-utils");
|
|
44
|
+
const providers_repo_1 = require("../storage/providers-repo");
|
|
43
45
|
const run_mutation_1 = require("./run-mutation");
|
|
44
46
|
/**
|
|
45
47
|
* Imports provider definitions from an external JSON file into the managed registry.
|
|
@@ -58,16 +60,52 @@ function importProviders(args) {
|
|
|
58
60
|
});
|
|
59
61
|
}
|
|
60
62
|
(0, fs_utils_1.ensureDir)(args.codexDir);
|
|
63
|
+
const document = (0, config_repo_1.readStructuredConfig)(args.configPath);
|
|
61
64
|
return (0, run_mutation_1.runMutation)({
|
|
62
65
|
codexDir: args.codexDir,
|
|
63
66
|
backupsDir: args.backupsDir,
|
|
64
67
|
latestBackupPath: args.latestBackupPath,
|
|
65
68
|
operation: "import",
|
|
66
|
-
files: [
|
|
69
|
+
files: [
|
|
70
|
+
{ absolutePath: args.providersPath, relativePath: "providers.json" },
|
|
71
|
+
{ absolutePath: args.configPath, relativePath: "config.toml" },
|
|
72
|
+
],
|
|
67
73
|
mutate: () => {
|
|
68
74
|
const current = (0, providers_repo_1.readProvidersFileIfExists)(args.providersPath);
|
|
69
75
|
const next = args.merge ? (0, providers_repo_1.mergeProviders)(current, imported) : imported;
|
|
76
|
+
const currentViews = (0, config_1.buildManagedProfileViews)(document, current);
|
|
77
|
+
const nextViews = (0, config_1.buildManagedProfileViews)(document, next);
|
|
78
|
+
const adoptedProfiles = currentViews
|
|
79
|
+
.filter((view) => view.source === "unmanaged" && view.linkedProviders.length === 0)
|
|
80
|
+
.filter((view) => nextViews.some((nextView) => nextView.name === view.name && nextView.managed))
|
|
81
|
+
.map((view) => view.name)
|
|
82
|
+
.sort();
|
|
83
|
+
const missingViews = nextViews.filter((view) => view.source === "orphaned-reference");
|
|
84
|
+
const repairedProfiles = [];
|
|
85
|
+
const upsertProfiles = missingViews.reduce((accumulator, view) => {
|
|
86
|
+
const sourceView = currentViews.find((entry) => entry.name === view.name) ?? null;
|
|
87
|
+
if (!sourceView?.model) {
|
|
88
|
+
throw (0, errors_1.cliError)("MANAGED_PROFILE_FIELDS_MISSING", "Import would create provider references to missing config profiles that need model and matching model_provider sections.", {
|
|
89
|
+
profilesNeedingRepair: missingViews.map((entry) => entry.name).sort(),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
if (sourceView.modelProvider !== view.name || !sourceView.baseUrl) {
|
|
93
|
+
throw (0, errors_1.cliError)("MANAGED_PROFILE_FIELDS_MISSING", "Import would create provider references to missing config profiles without matching model_provider runtime sections.", {
|
|
94
|
+
profilesNeedingRepair: missingViews.map((entry) => entry.name).sort(),
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
accumulator[view.name] = (0, config_1.validateManagedProfileCreation)(view.name, {
|
|
98
|
+
model: sourceView.model,
|
|
99
|
+
modelProvider: view.name,
|
|
100
|
+
});
|
|
101
|
+
repairedProfiles.push(view.name);
|
|
102
|
+
return accumulator;
|
|
103
|
+
}, {});
|
|
104
|
+
const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
|
|
105
|
+
upsertProfiles,
|
|
106
|
+
});
|
|
70
107
|
(0, providers_repo_1.writeProvidersFile)(args.providersPath, next);
|
|
108
|
+
(0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
|
|
71
109
|
const replacedProviders = args.merge
|
|
72
110
|
? Object.keys(imported.providers).filter((name) => current.providers[name]).sort()
|
|
73
111
|
: [];
|
|
@@ -77,6 +115,12 @@ function importProviders(args) {
|
|
|
77
115
|
importedCount: Object.keys(imported.providers).length,
|
|
78
116
|
mergedCount: Object.keys(next.providers).length,
|
|
79
117
|
replacedProviders,
|
|
118
|
+
createdProfileSections: configPlan.createdProfileSections,
|
|
119
|
+
deletedProfileSections: [],
|
|
120
|
+
keptSharedProfiles: nextViews.filter((view) => view.linkedProviders.length > 1).map((view) => view.name),
|
|
121
|
+
switchedActiveProfile: false,
|
|
122
|
+
adoptedProfiles,
|
|
123
|
+
repairedProfiles: repairedProfiles.sort(),
|
|
80
124
|
};
|
|
81
125
|
},
|
|
82
126
|
});
|
package/dist/app/list-backups.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.listBackupEntries = listBackupEntries;
|
|
4
|
-
const backup_repo_1 = require("../
|
|
4
|
+
const backup_repo_1 = require("../storage/backup-repo");
|
|
5
5
|
/**
|
|
6
6
|
* Lists backup manifests available under the managed Codex backups directory.
|
|
7
7
|
*/
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.listConfigProfilesView = listConfigProfilesView;
|
|
4
|
+
const config_1 = require("../domain/config");
|
|
5
|
+
const config_repo_1 = require("../storage/config-repo");
|
|
6
|
+
const providers_repo_1 = require("../storage/providers-repo");
|
|
7
|
+
/**
|
|
8
|
+
* Returns the lightweight config profile listing.
|
|
9
|
+
*/
|
|
10
|
+
function listConfigProfilesView(args) {
|
|
11
|
+
const document = (0, config_repo_1.readStructuredConfig)(args.configPath);
|
|
12
|
+
const providers = (0, providers_repo_1.readProvidersFileIfExists)(args.providersPath);
|
|
13
|
+
const profiles = (0, config_1.buildManagedProfileViews)(document, providers).map((profile) => ({
|
|
14
|
+
name: profile.name,
|
|
15
|
+
managed: profile.managed,
|
|
16
|
+
isActive: profile.isActive,
|
|
17
|
+
linkedProviders: profile.linkedProviders,
|
|
18
|
+
model: profile.model,
|
|
19
|
+
modelProvider: profile.modelProvider,
|
|
20
|
+
baseUrl: profile.baseUrl,
|
|
21
|
+
source: profile.source,
|
|
22
|
+
}));
|
|
23
|
+
return {
|
|
24
|
+
data: {
|
|
25
|
+
activeProfile: document.activeProfile,
|
|
26
|
+
profiles,
|
|
27
|
+
count: profiles.length,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.listProviders = listProviders;
|
|
4
|
-
const providers_repo_1 = require("../
|
|
4
|
+
const providers_repo_1 = require("../storage/providers-repo");
|
|
5
5
|
/**
|
|
6
6
|
* Returns the sorted list of configured providers for display.
|
|
7
7
|
*/
|
|
@@ -2,29 +2,61 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.removeProvider = removeProvider;
|
|
4
4
|
const errors_1 = require("../domain/errors");
|
|
5
|
-
const
|
|
5
|
+
const config_1 = require("../domain/config");
|
|
6
|
+
const config_repo_1 = require("../storage/config-repo");
|
|
7
|
+
const providers_repo_1 = require("../storage/providers-repo");
|
|
6
8
|
const run_mutation_1 = require("./run-mutation");
|
|
7
9
|
/**
|
|
8
10
|
* Removes a provider from the managed providers registry.
|
|
9
11
|
*/
|
|
10
12
|
function removeProvider(args) {
|
|
11
13
|
const providers = (0, providers_repo_1.readProvidersFile)(args.providersPath);
|
|
12
|
-
|
|
14
|
+
const document = (0, config_repo_1.readStructuredConfig)(args.configPath);
|
|
15
|
+
const current = providers.providers[args.providerName];
|
|
16
|
+
if (!current) {
|
|
13
17
|
throw (0, errors_1.cliError)("PROVIDER_NOT_FOUND", `Provider "${args.providerName}" was not found.`);
|
|
14
18
|
}
|
|
15
19
|
const nextProviders = { ...providers.providers };
|
|
16
20
|
// Delete against a copied object so the original parsed state stays untouched.
|
|
17
21
|
delete nextProviders[args.providerName];
|
|
22
|
+
const remainingLinksByProfile = new Map();
|
|
23
|
+
for (const [name, provider] of Object.entries(nextProviders)) {
|
|
24
|
+
const list = remainingLinksByProfile.get(provider.profile) ?? [];
|
|
25
|
+
list.push(name);
|
|
26
|
+
remainingLinksByProfile.set(provider.profile, list);
|
|
27
|
+
}
|
|
28
|
+
const lifecycle = (0, config_1.planProfileLifecycleOutcome)({
|
|
29
|
+
providerName: args.providerName,
|
|
30
|
+
oldProfile: current.profile,
|
|
31
|
+
newProfile: null,
|
|
32
|
+
activeProfile: document.activeProfile,
|
|
33
|
+
remainingLinksByProfile,
|
|
34
|
+
switchToProfile: args.switchToProfile ?? null,
|
|
35
|
+
});
|
|
18
36
|
return (0, run_mutation_1.runMutation)({
|
|
19
37
|
codexDir: args.codexDir,
|
|
20
38
|
backupsDir: args.backupsDir,
|
|
21
39
|
latestBackupPath: args.latestBackupPath,
|
|
22
40
|
operation: "remove",
|
|
23
|
-
files: [
|
|
41
|
+
files: [
|
|
42
|
+
{ absolutePath: args.providersPath, relativePath: "providers.json" },
|
|
43
|
+
{ absolutePath: args.configPath, relativePath: "config.toml" },
|
|
44
|
+
],
|
|
24
45
|
mutate: () => {
|
|
46
|
+
const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
|
|
47
|
+
deleteProfiles: lifecycle.deletedProfileSections,
|
|
48
|
+
setActiveProfile: lifecycle.nextActiveProfile,
|
|
49
|
+
});
|
|
25
50
|
(0, providers_repo_1.writeProvidersFile)(args.providersPath, { providers: nextProviders });
|
|
51
|
+
(0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
|
|
26
52
|
return {
|
|
27
53
|
provider: args.providerName,
|
|
54
|
+
createdProfileSections: configPlan.createdProfileSections,
|
|
55
|
+
deletedProfileSections: configPlan.deletedProfileSections,
|
|
56
|
+
keptSharedProfiles: lifecycle.keptSharedProfiles,
|
|
57
|
+
switchedActiveProfile: lifecycle.switchedActiveProfile,
|
|
58
|
+
adoptedProfiles: [],
|
|
59
|
+
repairedProfiles: [],
|
|
28
60
|
};
|
|
29
61
|
},
|
|
30
62
|
});
|