@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
|
@@ -1,12 +1,55 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.readConfigFile = readConfigFile;
|
|
37
|
+
exports.readStructuredConfig = readStructuredConfig;
|
|
4
38
|
exports.readCurrentProfile = readCurrentProfile;
|
|
5
39
|
exports.listConfigProfiles = listConfigProfiles;
|
|
6
40
|
exports.ensureProfileExists = ensureProfileExists;
|
|
41
|
+
exports.requireManagedProfileRuntime = requireManagedProfileRuntime;
|
|
42
|
+
exports.requireModelProviderRuntimeSection = requireModelProviderRuntimeSection;
|
|
7
43
|
exports.updateTopLevelProfile = updateTopLevelProfile;
|
|
8
|
-
|
|
44
|
+
exports.createConfigMutationPlan = createConfigMutationPlan;
|
|
45
|
+
exports.applyConfigMutation = applyConfigMutation;
|
|
46
|
+
exports.findCodexDirCandidates = findCodexDirCandidates;
|
|
47
|
+
const fs = __importStar(require("node:fs"));
|
|
48
|
+
const os = __importStar(require("node:os"));
|
|
49
|
+
const path = __importStar(require("node:path"));
|
|
9
50
|
const config_1 = require("../domain/config");
|
|
51
|
+
const errors_1 = require("../domain/errors");
|
|
52
|
+
const codex_paths_1 = require("./codex-paths");
|
|
10
53
|
const fs_utils_1 = require("./fs-utils");
|
|
11
54
|
/**
|
|
12
55
|
* Reads config.toml and throws a typed error when the file is missing.
|
|
@@ -14,12 +57,26 @@ const fs_utils_1 = require("./fs-utils");
|
|
|
14
57
|
function readConfigFile(configPath) {
|
|
15
58
|
return (0, fs_utils_1.readRequiredFile)(configPath, "CONFIG_NOT_FOUND", "config.toml");
|
|
16
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Reads and parses config.toml into the managed structured document shape.
|
|
62
|
+
*/
|
|
63
|
+
function readStructuredConfig(configPath) {
|
|
64
|
+
const content = readConfigFile(configPath);
|
|
65
|
+
try {
|
|
66
|
+
return (0, config_1.parseStructuredConfig)(content);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
throw (0, errors_1.cliError)("CONFIG_PARSE_ERROR", "Failed to parse config.toml.", {
|
|
70
|
+
file: configPath,
|
|
71
|
+
cause: (0, errors_1.normalizeError)(error).message,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
17
75
|
/**
|
|
18
76
|
* Reads the active top-level profile from config.toml.
|
|
19
77
|
*/
|
|
20
78
|
function readCurrentProfile(configPath) {
|
|
21
|
-
const
|
|
22
|
-
const profile = (0, config_1.parseTopLevelProfile)(content);
|
|
79
|
+
const profile = readStructuredConfig(configPath).activeProfile ?? (0, config_1.parseTopLevelProfile)(readConfigFile(configPath));
|
|
23
80
|
if (!profile) {
|
|
24
81
|
throw (0, errors_1.cliError)("PROFILE_NOT_FOUND", "No top-level profile is set in config.toml.", {
|
|
25
82
|
file: configPath,
|
|
@@ -31,26 +88,121 @@ function readCurrentProfile(configPath) {
|
|
|
31
88
|
* Lists all named profile sections declared in config.toml.
|
|
32
89
|
*/
|
|
33
90
|
function listConfigProfiles(configPath) {
|
|
34
|
-
return
|
|
91
|
+
return new Set(readStructuredConfig(configPath).profiles.map((profile) => profile.name));
|
|
35
92
|
}
|
|
36
93
|
/**
|
|
37
94
|
* Verifies that a provider's target profile exists before a switch operation proceeds.
|
|
38
95
|
*/
|
|
39
96
|
function ensureProfileExists(configPath, profile, provider) {
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
if (!profiles.has(profile)) {
|
|
97
|
+
const document = readStructuredConfig(configPath);
|
|
98
|
+
if (!document.profiles.some((entry) => entry.name === profile)) {
|
|
43
99
|
throw (0, errors_1.cliError)("PROFILE_NOT_FOUND", `Profile "${profile}" does not exist in config.toml.`, {
|
|
44
100
|
file: configPath,
|
|
45
101
|
provider,
|
|
46
102
|
profile,
|
|
47
103
|
});
|
|
48
104
|
}
|
|
49
|
-
return
|
|
105
|
+
return document;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Resolves one profile view and enforces the managed model_provider contract.
|
|
109
|
+
*/
|
|
110
|
+
function requireManagedProfileRuntime(document, providers, profile) {
|
|
111
|
+
const view = (0, config_1.buildManagedProfileViews)(document, providers).find((entry) => entry.name === profile);
|
|
112
|
+
if (!view) {
|
|
113
|
+
throw (0, errors_1.cliError)("PROFILE_NOT_FOUND", `Profile "${profile}" does not exist in config.toml.`, {
|
|
114
|
+
profile,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
if (!view.modelProvider) {
|
|
118
|
+
throw (0, errors_1.cliError)("MANAGED_PROFILE_FIELDS_MISSING", `Managed profile "${profile}" requires model_provider.`, {
|
|
119
|
+
profile,
|
|
120
|
+
missingFields: ["model_provider"],
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
if (view.modelProvider !== profile) {
|
|
124
|
+
throw (0, errors_1.cliError)("INVALID_ARGUMENT", `Managed profile "${profile}" must use the same model_provider name.`, {
|
|
125
|
+
profile,
|
|
126
|
+
modelProvider: view.modelProvider,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
const modelProviderSection = document.modelProviders.find((entry) => entry.name === view.modelProvider);
|
|
130
|
+
if (!modelProviderSection) {
|
|
131
|
+
throw (0, errors_1.cliError)("PROFILE_NOT_FOUND", `Model provider "${view.modelProvider}" does not exist in config.toml.`, {
|
|
132
|
+
profile,
|
|
133
|
+
modelProvider: view.modelProvider,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
if (!modelProviderSection.baseUrl) {
|
|
137
|
+
throw (0, errors_1.cliError)("MANAGED_PROFILE_FIELDS_MISSING", `Model provider "${view.modelProvider}" requires base_url.`, {
|
|
138
|
+
profile,
|
|
139
|
+
modelProvider: view.modelProvider,
|
|
140
|
+
missingFields: ["base_url"],
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
return view;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Verifies that a same-named model_provider runtime section exists and has base_url.
|
|
147
|
+
*/
|
|
148
|
+
function requireModelProviderRuntimeSection(document, profile) {
|
|
149
|
+
const modelProviderSection = document.modelProviders.find((entry) => entry.name === profile);
|
|
150
|
+
if (!modelProviderSection) {
|
|
151
|
+
throw (0, errors_1.cliError)("PROFILE_NOT_FOUND", `Model provider "${profile}" does not exist in config.toml.`, {
|
|
152
|
+
profile,
|
|
153
|
+
modelProvider: profile,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
if (!modelProviderSection.baseUrl) {
|
|
157
|
+
throw (0, errors_1.cliError)("MANAGED_PROFILE_FIELDS_MISSING", `Model provider "${profile}" requires base_url.`, {
|
|
158
|
+
profile,
|
|
159
|
+
modelProvider: profile,
|
|
160
|
+
missingFields: ["base_url"],
|
|
161
|
+
});
|
|
162
|
+
}
|
|
50
163
|
}
|
|
51
164
|
/**
|
|
52
165
|
* Rewrites config.toml so the requested profile becomes the active top-level profile.
|
|
53
166
|
*/
|
|
54
167
|
function updateTopLevelProfile(configPath, configContent, profile) {
|
|
55
|
-
(0, fs_utils_1.writeTextFileAtomic)(configPath, (0, config_1.
|
|
168
|
+
(0, fs_utils_1.writeTextFileAtomic)(configPath, (0, config_1.applyPatchOperations)(configContent, (0, config_1.planConfigMutation)((0, config_1.parseStructuredConfig)(configContent), {
|
|
169
|
+
setActiveProfile: profile,
|
|
170
|
+
}).operations));
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Exposes the config mutation planner to application services.
|
|
174
|
+
*/
|
|
175
|
+
function createConfigMutationPlan(document, args) {
|
|
176
|
+
return (0, config_1.planConfigMutation)(document, args);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Applies a previously generated mutation plan to config.toml in one write.
|
|
180
|
+
*/
|
|
181
|
+
function applyConfigMutation(configPath, document, plan) {
|
|
182
|
+
(0, fs_utils_1.writeTextFileAtomic)(configPath, (0, config_1.applyPatchOperations)(document.rawText, plan.operations));
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Finds candidate Codex directories in a stable, non-recursive order.
|
|
186
|
+
*/
|
|
187
|
+
function findCodexDirCandidates(explicitCodexDir) {
|
|
188
|
+
if (explicitCodexDir) {
|
|
189
|
+
return [(0, codex_paths_1.resolveCodexDir)(explicitCodexDir)];
|
|
190
|
+
}
|
|
191
|
+
const candidates = new Set();
|
|
192
|
+
const ordered = [];
|
|
193
|
+
const envCandidate = process.env[codex_paths_1.CODEX_DIR_ENV_NAME];
|
|
194
|
+
if (envCandidate) {
|
|
195
|
+
ordered.push((0, codex_paths_1.resolveCodexDir)(envCandidate));
|
|
196
|
+
}
|
|
197
|
+
if (process.env.NODE_ENV === "development") {
|
|
198
|
+
ordered.push(path.resolve(process.cwd(), "dev-codex", "local-sandbox"));
|
|
199
|
+
}
|
|
200
|
+
ordered.push(path.join(os.homedir(), ".codex"));
|
|
201
|
+
for (const candidate of ordered) {
|
|
202
|
+
if (!candidate || candidates.has(candidate) || !fs.existsSync(candidate)) {
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
candidates.add(candidate);
|
|
206
|
+
}
|
|
207
|
+
return [...candidates];
|
|
56
208
|
}
|
package/dist/infra/fs-utils.js
CHANGED
|
@@ -1,97 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.ensureDir =
|
|
37
|
-
|
|
38
|
-
exports.
|
|
39
|
-
exports
|
|
40
|
-
exports
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const errors_1 = require("../domain/errors");
|
|
44
|
-
/**
|
|
45
|
-
* Creates a directory tree when it does not already exist.
|
|
46
|
-
*/
|
|
47
|
-
function ensureDir(directoryPath) {
|
|
48
|
-
fs.mkdirSync(directoryPath, { recursive: true });
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Writes a text file via a temporary sibling file and atomic rename.
|
|
52
|
-
*/
|
|
53
|
-
function writeTextFileAtomic(filePath, contents) {
|
|
54
|
-
ensureDir(path.dirname(filePath));
|
|
55
|
-
// Use the current process id in the temp name to reduce collision risk.
|
|
56
|
-
const tempPath = `${filePath}.tmp-${process.pid}`;
|
|
57
|
-
fs.writeFileSync(tempPath, contents, "utf8");
|
|
58
|
-
fs.renameSync(tempPath, filePath);
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Reads a required text file and throws a typed error when it is missing.
|
|
62
|
-
*/
|
|
63
|
-
function readRequiredFile(filePath, code, label) {
|
|
64
|
-
if (!fs.existsSync(filePath)) {
|
|
65
|
-
throw (0, errors_1.cliError)(code, `${label} does not exist.`, { file: filePath });
|
|
66
|
-
}
|
|
67
|
-
return fs.readFileSync(filePath, "utf8");
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Formats arbitrary error detail values for human-readable output.
|
|
71
|
-
*/
|
|
72
|
-
function formatDetail(value) {
|
|
73
|
-
if (Array.isArray(value)) {
|
|
74
|
-
return value.join(", ");
|
|
75
|
-
}
|
|
76
|
-
if (value && typeof value === "object") {
|
|
77
|
-
return JSON.stringify(value);
|
|
78
|
-
}
|
|
79
|
-
return String(value);
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Renders structured error details while suppressing secret-looking API key fields.
|
|
83
|
-
*/
|
|
84
|
-
function printErrorDetails(error) {
|
|
85
|
-
if (!error.details) {
|
|
86
|
-
return [];
|
|
87
|
-
}
|
|
88
|
-
const lines = [];
|
|
89
|
-
for (const [key, value] of Object.entries(error.details)) {
|
|
90
|
-
if (typeof value === "string" && key.toLowerCase().includes("apikey")) {
|
|
91
|
-
// Do not leak API keys back into the terminal if one appears in error details.
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
lines.push(` ${key}: ${formatDetail(value)}`);
|
|
95
|
-
}
|
|
96
|
-
return lines;
|
|
97
|
-
}
|
|
3
|
+
exports.writeTextFileAtomic = exports.readRequiredFile = exports.printErrorDetails = exports.formatDetail = exports.ensureDir = void 0;
|
|
4
|
+
var fs_utils_1 = require("../storage/fs-utils");
|
|
5
|
+
Object.defineProperty(exports, "ensureDir", { enumerable: true, get: function () { return fs_utils_1.ensureDir; } });
|
|
6
|
+
Object.defineProperty(exports, "formatDetail", { enumerable: true, get: function () { return fs_utils_1.formatDetail; } });
|
|
7
|
+
Object.defineProperty(exports, "printErrorDetails", { enumerable: true, get: function () { return fs_utils_1.printErrorDetails; } });
|
|
8
|
+
Object.defineProperty(exports, "readRequiredFile", { enumerable: true, get: function () { return fs_utils_1.readRequiredFile; } });
|
|
9
|
+
Object.defineProperty(exports, "writeTextFileAtomic", { enumerable: true, get: function () { return fs_utils_1.writeTextFileAtomic; } });
|
package/dist/infra/lock-repo.js
CHANGED
|
@@ -1,99 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.withCodexLock =
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const errors_1 = require("../domain/errors");
|
|
40
|
-
const fs_utils_1 = require("./fs-utils");
|
|
41
|
-
/**
|
|
42
|
-
* Executes a mutation while holding an exclusive codex-switch lock file.
|
|
43
|
-
*/
|
|
44
|
-
function withCodexLock(codexDir, operation, run) {
|
|
45
|
-
(0, fs_utils_1.ensureDir)(codexDir);
|
|
46
|
-
const lockPath = path.join(codexDir, ".codex-switch.lock");
|
|
47
|
-
acquireLock(lockPath, operation);
|
|
48
|
-
try {
|
|
49
|
-
return run();
|
|
50
|
-
}
|
|
51
|
-
finally {
|
|
52
|
-
releaseLock(lockPath);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Acquires the lock file using exclusive create semantics.
|
|
57
|
-
*/
|
|
58
|
-
function acquireLock(lockPath, operation) {
|
|
59
|
-
const payload = {
|
|
60
|
-
pid: process.pid,
|
|
61
|
-
operation,
|
|
62
|
-
createdAt: new Date().toISOString(),
|
|
63
|
-
};
|
|
64
|
-
try {
|
|
65
|
-
fs.writeFileSync(lockPath, `${JSON.stringify(payload, null, 2)}\n`, { encoding: "utf8", flag: "wx" });
|
|
66
|
-
}
|
|
67
|
-
catch {
|
|
68
|
-
const existing = readLockRecord(lockPath);
|
|
69
|
-
throw (0, errors_1.cliError)("LOCK_CONFLICT", "Another codex-switch write operation is already running.", {
|
|
70
|
-
file: lockPath,
|
|
71
|
-
activeOperation: existing?.operation ?? "unknown",
|
|
72
|
-
activePid: existing?.pid ?? null,
|
|
73
|
-
activeSince: existing?.createdAt ?? null,
|
|
74
|
-
requestedOperation: operation,
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Removes the lock file when the mutation completes.
|
|
80
|
-
*/
|
|
81
|
-
function releaseLock(lockPath) {
|
|
82
|
-
if (fs.existsSync(lockPath)) {
|
|
83
|
-
fs.rmSync(lockPath, { force: true });
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Reads the current lock metadata when a lock conflict occurs.
|
|
88
|
-
*/
|
|
89
|
-
function readLockRecord(lockPath) {
|
|
90
|
-
if (!fs.existsSync(lockPath)) {
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
try {
|
|
94
|
-
return JSON.parse(fs.readFileSync(lockPath, "utf8"));
|
|
95
|
-
}
|
|
96
|
-
catch {
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
3
|
+
exports.withCodexLock = void 0;
|
|
4
|
+
var lock_repo_1 = require("../storage/lock-repo");
|
|
5
|
+
Object.defineProperty(exports, "withCodexLock", { enumerable: true, get: function () { return lock_repo_1.withCodexLock; } });
|
|
@@ -1,98 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.readProvidersFile =
|
|
37
|
-
|
|
38
|
-
exports.
|
|
39
|
-
exports
|
|
40
|
-
exports.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const providers_1 = require("../domain/providers");
|
|
44
|
-
const fs_utils_1 = require("./fs-utils");
|
|
45
|
-
/**
|
|
46
|
-
* Reads and validates providers.json from disk.
|
|
47
|
-
*/
|
|
48
|
-
function readProvidersFile(providersPath) {
|
|
49
|
-
const raw = (0, fs_utils_1.readRequiredFile)(providersPath, "PROVIDERS_NOT_FOUND", "providers.json");
|
|
50
|
-
try {
|
|
51
|
-
return (0, providers_1.validateProvidersShape)(JSON.parse(raw));
|
|
52
|
-
}
|
|
53
|
-
catch (error) {
|
|
54
|
-
throw (0, errors_1.cliError)("PROVIDERS_PARSE_ERROR", "Failed to parse providers.json.", {
|
|
55
|
-
file: providersPath,
|
|
56
|
-
cause: (0, errors_1.normalizeError)(error).message,
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Reads providers.json when it exists, otherwise returns an empty registry.
|
|
62
|
-
*/
|
|
63
|
-
function readProvidersFileIfExists(providersPath) {
|
|
64
|
-
return fs.existsSync(providersPath) ? readProvidersFile(providersPath) : { providers: {} };
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Persists providers.json using deterministic key ordering.
|
|
68
|
-
*/
|
|
69
|
-
function writeProvidersFile(providersPath, providers) {
|
|
70
|
-
(0, fs_utils_1.writeTextFileAtomic)(providersPath, `${JSON.stringify((0, providers_1.sortProviders)(providers), null, 2)}\n`);
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Returns a single provider record or throws a typed not-found error.
|
|
74
|
-
*/
|
|
75
|
-
function readProviderRecord(providersPath, providerName) {
|
|
76
|
-
const providers = readProvidersFile(providersPath);
|
|
77
|
-
const record = providers.providers[providerName];
|
|
78
|
-
if (!record) {
|
|
79
|
-
throw (0, errors_1.cliError)("PROVIDER_NOT_FOUND", `Provider "${providerName}" was not found.`, {
|
|
80
|
-
provider: providerName,
|
|
81
|
-
file: providersPath,
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
return record;
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Merges imported providers into the current registry, preferring the imported side on conflicts.
|
|
88
|
-
*/
|
|
89
|
-
function mergeProviders(current, imported) {
|
|
90
|
-
const providers = {};
|
|
91
|
-
for (const [name, record] of Object.entries(current.providers)) {
|
|
92
|
-
providers[name] = (0, providers_1.cleanProviderRecord)(record);
|
|
93
|
-
}
|
|
94
|
-
for (const [name, record] of Object.entries(imported.providers)) {
|
|
95
|
-
providers[name] = (0, providers_1.cleanProviderRecord)(record);
|
|
96
|
-
}
|
|
97
|
-
return (0, providers_1.sortProviders)({ providers });
|
|
98
|
-
}
|
|
3
|
+
exports.writeProvidersFile = exports.readProvidersFileIfExists = exports.readProvidersFile = exports.readProviderRecord = exports.mergeProviders = void 0;
|
|
4
|
+
var providers_repo_1 = require("../storage/providers-repo");
|
|
5
|
+
Object.defineProperty(exports, "mergeProviders", { enumerable: true, get: function () { return providers_repo_1.mergeProviders; } });
|
|
6
|
+
Object.defineProperty(exports, "readProviderRecord", { enumerable: true, get: function () { return providers_repo_1.readProviderRecord; } });
|
|
7
|
+
Object.defineProperty(exports, "readProvidersFile", { enumerable: true, get: function () { return providers_repo_1.readProvidersFile; } });
|
|
8
|
+
Object.defineProperty(exports, "readProvidersFileIfExists", { enumerable: true, get: function () { return providers_repo_1.readProvidersFileIfExists; } });
|
|
9
|
+
Object.defineProperty(exports, "writeProvidersFile", { enumerable: true, get: function () { return providers_repo_1.writeProvidersFile; } });
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.COMMON_TAG_CHOICES = void 0;
|
|
4
|
+
exports.collectAddInput = collectAddInput;
|
|
5
|
+
exports.createNonInteractiveAddError = createNonInteractiveAddError;
|
|
6
|
+
exports.promptTags = promptTags;
|
|
7
|
+
exports.parseTags = parseTags;
|
|
8
|
+
const errors_1 = require("../domain/errors");
|
|
9
|
+
exports.COMMON_TAG_CHOICES = ["free", "paid", "daily", "backup"];
|
|
10
|
+
/**
|
|
11
|
+
* Collects add command inputs interactively when required values are missing.
|
|
12
|
+
*/
|
|
13
|
+
async function collectAddInput(runtime, defaults, providerExists) {
|
|
14
|
+
runtime.writeLine("Interactive add mode");
|
|
15
|
+
runtime.writeLine("Provide the missing required fields. Press Enter to skip optional fields.");
|
|
16
|
+
const providerName = defaults.providerName
|
|
17
|
+
? normalizeRequiredValue(defaults.providerName)
|
|
18
|
+
: await promptProviderName(runtime, providerExists);
|
|
19
|
+
const profile = defaults.profile ? normalizeRequiredValue(defaults.profile) : await promptRequiredValue(runtime, "Profile");
|
|
20
|
+
const apiKey = defaults.apiKey
|
|
21
|
+
? normalizeRequiredValue(defaults.apiKey)
|
|
22
|
+
: await promptConfirmedSecret(runtime, "API key", "Confirm API key");
|
|
23
|
+
const baseUrl = defaults.baseUrl ?? normalizeOptionalValue(await runtime.inputText("Base URL (optional)"));
|
|
24
|
+
const note = defaults.note ?? normalizeOptionalValue(await runtime.inputText("Note (optional)"));
|
|
25
|
+
const tags = defaults.tags.length > 0 ? defaults.tags : await promptTags(runtime);
|
|
26
|
+
return {
|
|
27
|
+
providerName,
|
|
28
|
+
profile,
|
|
29
|
+
apiKey,
|
|
30
|
+
baseUrl,
|
|
31
|
+
note,
|
|
32
|
+
tags,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Throws a consistent error when interactive add is unavailable.
|
|
37
|
+
*/
|
|
38
|
+
function createNonInteractiveAddError() {
|
|
39
|
+
return (0, errors_1.cliError)("INVALID_ARGUMENT", "add requires <provider>, --profile, and --api-key when running without an interactive TTY.", {
|
|
40
|
+
suggestion: "Run in a terminal TTY or pass all required values explicitly.",
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async function promptProviderName(runtime, providerExists) {
|
|
44
|
+
while (true) {
|
|
45
|
+
const providerName = await promptRequiredValue(runtime, "Provider name");
|
|
46
|
+
if (providerExists(providerName)) {
|
|
47
|
+
runtime.writeLine(`Provider "${providerName}" already exists. Choose a different name.`);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
return providerName;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function promptRequiredValue(runtime, label) {
|
|
54
|
+
while (true) {
|
|
55
|
+
const value = normalizeRequiredValue(await runtime.inputText(label));
|
|
56
|
+
if (value.length > 0) {
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
runtime.writeLine(`${label} is required.`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async function promptConfirmedSecret(runtime, label, confirmationLabel) {
|
|
63
|
+
while (true) {
|
|
64
|
+
const first = normalizeRequiredValue(await runtime.inputSecret(label));
|
|
65
|
+
if (first.length === 0) {
|
|
66
|
+
runtime.writeLine(`${label} is required.`);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const second = normalizeRequiredValue(await runtime.inputSecret(confirmationLabel));
|
|
70
|
+
if (second.length === 0) {
|
|
71
|
+
runtime.writeLine(`${confirmationLabel} is required.`);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (first !== second) {
|
|
75
|
+
runtime.writeLine("API key entries did not match. Try again.");
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
return first;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function normalizeRequiredValue(value) {
|
|
82
|
+
return value.trim();
|
|
83
|
+
}
|
|
84
|
+
function normalizeOptionalValue(value) {
|
|
85
|
+
const normalized = value.trim();
|
|
86
|
+
return normalized === "" ? null : normalized;
|
|
87
|
+
}
|
|
88
|
+
async function promptTags(runtime, defaults = []) {
|
|
89
|
+
const defaultPresetTags = defaults.filter(isCommonTag);
|
|
90
|
+
const defaultCustomTags = defaults.filter((tag) => !isCommonTag(tag));
|
|
91
|
+
const presetTags = await runtime.selectMany("Select tags (optional)", exports.COMMON_TAG_CHOICES.map((tag) => ({ value: tag, label: tag })), { defaultValues: defaultPresetTags });
|
|
92
|
+
const customTags = parseTags(await runtime.inputText("Custom tags (optional, comma-separated)", {
|
|
93
|
+
defaultValue: defaultCustomTags.join(", "),
|
|
94
|
+
}));
|
|
95
|
+
return dedupeTags([...presetTags, ...customTags]);
|
|
96
|
+
}
|
|
97
|
+
function parseTags(value) {
|
|
98
|
+
return dedupeTags(value
|
|
99
|
+
.split(",")
|
|
100
|
+
.map((tag) => tag.trim())
|
|
101
|
+
.filter((tag) => tag.length > 0));
|
|
102
|
+
}
|
|
103
|
+
function isCommonTag(tag) {
|
|
104
|
+
return exports.COMMON_TAG_CHOICES.includes(tag);
|
|
105
|
+
}
|
|
106
|
+
function dedupeTags(tags) {
|
|
107
|
+
return Array.from(new Set(tags));
|
|
108
|
+
}
|