@minniexcode/codex-switch 0.0.8 → 0.0.10

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.
Files changed (44) hide show
  1. package/README.AI.md +5 -3
  2. package/README.CN.md +25 -3
  3. package/README.md +3 -2
  4. package/dist/app/add-provider.js +1 -12
  5. package/dist/app/bridge.js +295 -0
  6. package/dist/app/edit-provider.js +1 -17
  7. package/dist/app/get-status.js +32 -2
  8. package/dist/app/list-providers.js +0 -1
  9. package/dist/app/run-doctor.js +45 -38
  10. package/dist/app/setup-codex.js +27 -17
  11. package/dist/app/show-config.js +1 -5
  12. package/dist/app/switch-provider.js +33 -20
  13. package/dist/cli/output.js +4 -6
  14. package/dist/cli.js +1 -1
  15. package/dist/commands/handlers.js +223 -39
  16. package/dist/commands/help.js +1 -0
  17. package/dist/commands/registry.js +48 -4
  18. package/dist/domain/config.js +4 -68
  19. package/dist/domain/providers.js +0 -5
  20. package/dist/domain/runtime-state.js +2 -1
  21. package/dist/domain/setup.js +58 -3
  22. package/dist/interaction/add-interactive.js +55 -1
  23. package/dist/interaction/interactive.js +1 -5
  24. package/dist/runtime/copilot-adapter.js +44 -1
  25. package/dist/runtime/copilot-bridge-worker.js +1 -1
  26. package/dist/runtime/copilot-bridge.js +60 -19
  27. package/dist/runtime/copilot-cli.js +70 -0
  28. package/dist/runtime/copilot-installer.js +49 -2
  29. package/dist/storage/auth-repo.js +28 -77
  30. package/dist/storage/config-repo.js +1 -36
  31. package/dist/storage/runtime-state-repo.js +32 -0
  32. package/docs/Design/codex-switch-copilot-integration-design.md +517 -0
  33. package/docs/Design/codex-switch-v0.0.10-design.md +669 -0
  34. package/docs/Design/codex-switch-v0.0.9-design.md +182 -0
  35. package/docs/PRD/codex-switch-prd-v0.0.10.md +406 -0
  36. package/docs/PRD/codex-switch-prd-v0.0.9.md +166 -0
  37. package/docs/Tests/testing-bridge-v0.0.9.md +367 -0
  38. package/docs/cli-usage.md +38 -14
  39. package/docs/codex-switch-product-overview.md +2 -2
  40. package/docs/codex-switch-technical-architecture.md +6 -5
  41. package/package.json +1 -1
  42. /package/docs/{test-report-0.0.5.md → Tests/test-report-0.0.5.md} +0 -0
  43. /package/docs/{test-report-0.0.7.md → Tests/test-report-0.0.7.md} +0 -0
  44. /package/docs/{testing.md → Tests/testing.md} +0 -0
@@ -34,15 +34,10 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.readAuthFileIfExists = readAuthFileIfExists;
37
- exports.buildManagedAuthPayload = buildManagedAuthPayload;
38
- exports.buildManagedAuthJson = buildManagedAuthJson;
39
- exports.writeAuthFile = writeAuthFile;
40
- exports.extractManagedAuthFingerprint = extractManagedAuthFingerprint;
41
- exports.readManagedAuthState = readManagedAuthState;
37
+ exports.readAuthFileState = readAuthFileState;
38
+ exports.writeOpenAiApiKeyAuth = writeOpenAiApiKeyAuth;
42
39
  const fs = __importStar(require("node:fs"));
43
40
  const errors_1 = require("../domain/errors");
44
- const fs_utils_1 = require("./fs-utils");
45
- const LEGACY_MANAGED_SECRET_KEYS = new Set(["api_key"]);
46
41
  /**
47
42
  * Reads auth.json when it exists and returns null otherwise.
48
43
  */
@@ -61,84 +56,27 @@ function readAuthFileIfExists(authPath) {
61
56
  }
62
57
  }
63
58
  /**
64
- * Builds the stable managed auth payload for one provider.
59
+ * Reads auth.json into a neutral file-state summary for status and doctor.
65
60
  */
66
- function buildManagedAuthPayload(provider) {
67
- return {
68
- auth_mode: "apikey",
69
- [provider.envKey]: provider.apiKey,
70
- };
71
- }
72
- /**
73
- * Builds the next auth.json object while preserving unmanaged metadata.
74
- */
75
- function buildManagedAuthJson(provider, existingAuthJson) {
76
- const nextManaged = buildManagedAuthPayload(provider);
77
- const result = {};
78
- if (existingAuthJson && typeof existingAuthJson === "object" && !Array.isArray(existingAuthJson)) {
79
- for (const [key, value] of Object.entries(existingAuthJson)) {
80
- if (key === "auth_mode" || LEGACY_MANAGED_SECRET_KEYS.has(key) || looksLikeManagedSecretKey(key)) {
81
- continue;
82
- }
83
- result[key] = value;
84
- }
85
- }
86
- result.auth_mode = nextManaged.auth_mode;
87
- result[provider.envKey] = provider.apiKey;
88
- return result;
89
- }
90
- /**
91
- * Writes auth.json atomically using the managed mirror strategy.
92
- */
93
- function writeAuthFile(authPath, provider, existingAuthJson) {
94
- (0, fs_utils_1.writeTextFileAtomic)(authPath, `${JSON.stringify(buildManagedAuthJson(provider, existingAuthJson), null, 2)}\n`);
95
- }
96
- /**
97
- * Extracts a lightweight fingerprint used by doctor/status.
98
- */
99
- function extractManagedAuthFingerprint(input) {
100
- if (!input || typeof input !== "object" || Array.isArray(input)) {
101
- return {
102
- authMode: null,
103
- managedSecretKeys: [],
104
- payload: null,
105
- };
106
- }
107
- const payload = input;
108
- const authMode = typeof payload.auth_mode === "string" ? payload.auth_mode : null;
109
- const managedSecretKeys = Object.keys(payload)
110
- .filter((key) => key !== "auth_mode" && looksLikeManagedSecretKey(key))
111
- .sort();
112
- return {
113
- authMode,
114
- managedSecretKeys,
115
- payload,
116
- };
117
- }
118
- /**
119
- * Reads auth.json into a doctor-friendly managed state summary.
120
- */
121
- function readManagedAuthState(authPath) {
61
+ function readAuthFileState(authPath) {
122
62
  if (!fs.existsSync(authPath)) {
123
63
  return {
124
64
  exists: false,
125
65
  valid: false,
126
66
  parseError: null,
127
67
  authMode: null,
128
- managedSecretKeys: [],
129
- payload: null,
130
68
  };
131
69
  }
132
70
  try {
133
71
  const payload = readAuthFileIfExists(authPath);
134
- const fingerprint = extractManagedAuthFingerprint(payload);
72
+ const authMode = payload && typeof payload === "object" && !Array.isArray(payload) && typeof payload.auth_mode === "string"
73
+ ? String(payload.auth_mode)
74
+ : null;
135
75
  return {
136
76
  exists: true,
137
- valid: Boolean(fingerprint.payload),
77
+ valid: Boolean(payload && typeof payload === "object" && !Array.isArray(payload)),
138
78
  parseError: null,
139
- authMode: fingerprint.authMode,
140
- managedSecretKeys: fingerprint.managedSecretKeys,
141
- payload: fingerprint.payload,
79
+ authMode,
142
80
  };
143
81
  }
144
82
  catch (error) {
@@ -147,14 +85,27 @@ function readManagedAuthState(authPath) {
147
85
  valid: false,
148
86
  parseError: (0, errors_1.normalizeError)(error).message,
149
87
  authMode: null,
150
- managedSecretKeys: [],
151
- payload: null,
152
88
  };
153
89
  }
154
90
  }
155
- function looksLikeManagedSecretKey(key) {
156
- if (LEGACY_MANAGED_SECRET_KEYS.has(key)) {
157
- return true;
91
+ /**
92
+ * Writes the active direct-provider auth projection expected by Codex.
93
+ * Invalid or missing existing auth.json content is replaced with a minimal valid object.
94
+ */
95
+ function writeOpenAiApiKeyAuth(authPath, apiKey) {
96
+ let next = {};
97
+ if (fs.existsSync(authPath)) {
98
+ try {
99
+ const payload = JSON.parse(fs.readFileSync(authPath, "utf8"));
100
+ if (payload && typeof payload === "object" && !Array.isArray(payload)) {
101
+ next = { ...payload };
102
+ }
103
+ }
104
+ catch {
105
+ next = {};
106
+ }
158
107
  }
159
- return /^[A-Z0-9_]+$/.test(key);
108
+ next.auth_mode = "apikey";
109
+ next.OPENAI_API_KEY = apiKey;
110
+ fs.writeFileSync(authPath, `${JSON.stringify(next, null, 2)}\n`, "utf8");
160
111
  }
@@ -40,7 +40,6 @@ exports.listConfigProfiles = listConfigProfiles;
40
40
  exports.ensureProfileExists = ensureProfileExists;
41
41
  exports.requireManagedProfileRuntime = requireManagedProfileRuntime;
42
42
  exports.requireModelProviderRuntimeSection = requireModelProviderRuntimeSection;
43
- exports.requireRuntimeEnvKey = requireRuntimeEnvKey;
44
43
  exports.resolveActiveProviderName = resolveActiveProviderName;
45
44
  exports.updateTopLevelProfile = updateTopLevelProfile;
46
45
  exports.createConfigMutationPlan = createConfigMutationPlan;
@@ -143,13 +142,6 @@ function requireManagedProfileRuntime(document, providers, profile) {
143
142
  missingFields: ["base_url"],
144
143
  });
145
144
  }
146
- if (!modelProviderSection.envKey) {
147
- throw (0, errors_1.cliError)("MODEL_PROVIDER_ENV_KEY_MISSING", `Model provider "${view.modelProvider}" requires env_key.`, {
148
- profile,
149
- modelProvider: view.modelProvider,
150
- missingFields: ["env_key"],
151
- });
152
- }
153
145
  return view;
154
146
  }
155
147
  /**
@@ -170,33 +162,6 @@ function requireModelProviderRuntimeSection(document, profile) {
170
162
  missingFields: ["base_url"],
171
163
  });
172
164
  }
173
- if (!modelProviderSection.envKey) {
174
- throw (0, errors_1.cliError)("MODEL_PROVIDER_ENV_KEY_MISSING", `Model provider "${profile}" requires env_key.`, {
175
- profile,
176
- modelProvider: profile,
177
- missingFields: ["env_key"],
178
- });
179
- }
180
- }
181
- /**
182
- * Returns the runtime env_key for one profile or throws a typed error.
183
- */
184
- function requireRuntimeEnvKey(document, profile) {
185
- const modelProviderSection = document.modelProviders.find((entry) => entry.name === profile);
186
- if (!modelProviderSection) {
187
- throw (0, errors_1.cliError)("PROFILE_NOT_FOUND", `Model provider "${profile}" does not exist in config.toml.`, {
188
- profile,
189
- modelProvider: profile,
190
- });
191
- }
192
- if (!modelProviderSection.envKey) {
193
- throw (0, errors_1.cliError)("MODEL_PROVIDER_ENV_KEY_MISSING", `Model provider "${profile}" requires env_key.`, {
194
- profile,
195
- modelProvider: profile,
196
- missingFields: ["env_key"],
197
- });
198
- }
199
- return modelProviderSection.envKey;
200
165
  }
201
166
  /**
202
167
  * Resolves the current active provider and requires the mapping to be unique.
@@ -212,7 +177,7 @@ function resolveActiveProviderName(document, providers) {
212
177
  });
213
178
  }
214
179
  if (matches.length > 1) {
215
- throw (0, errors_1.cliError)("ACTIVE_PROVIDER_UNRESOLVED", `Active profile "${document.activeProfile}" maps to multiple providers.`, {
180
+ throw (0, errors_1.cliError)("ACTIVE_PROVIDER_UNRESOLVED", `Active profile "${document.activeProfile}" maps to multiple providers, so the active managed provider is ambiguous.`, {
216
181
  profile: document.activeProfile,
217
182
  providers: matches,
218
183
  });
@@ -35,11 +35,13 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.getCopilotBridgeStatePath = getCopilotBridgeStatePath;
37
37
  exports.readCopilotBridgeState = readCopilotBridgeState;
38
+ exports.inspectCopilotBridgeState = inspectCopilotBridgeState;
38
39
  exports.writeCopilotBridgeState = writeCopilotBridgeState;
39
40
  exports.clearCopilotBridgeState = clearCopilotBridgeState;
40
41
  const fs = __importStar(require("node:fs"));
41
42
  const os = __importStar(require("node:os"));
42
43
  const path = __importStar(require("node:path"));
44
+ const errors_1 = require("../domain/errors");
43
45
  const fs_utils_1 = require("./fs-utils");
44
46
  /**
45
47
  * Returns the user-level runtime state file used by Copilot bridge helpers.
@@ -61,6 +63,36 @@ function readCopilotBridgeState() {
61
63
  }
62
64
  return JSON.parse(fs.readFileSync(statePath, "utf8"));
63
65
  }
66
+ /**
67
+ * Safely inspects the runtime-state file for status/doctor style read paths.
68
+ */
69
+ function inspectCopilotBridgeState() {
70
+ const statePath = getCopilotBridgeStatePath();
71
+ if (!fs.existsSync(statePath)) {
72
+ return {
73
+ exists: false,
74
+ valid: false,
75
+ parseError: null,
76
+ state: null,
77
+ };
78
+ }
79
+ try {
80
+ return {
81
+ exists: true,
82
+ valid: true,
83
+ parseError: null,
84
+ state: readCopilotBridgeState(),
85
+ };
86
+ }
87
+ catch (error) {
88
+ return {
89
+ exists: true,
90
+ valid: false,
91
+ parseError: (0, errors_1.normalizeError)(error).message,
92
+ state: null,
93
+ };
94
+ }
95
+ }
64
96
  /**
65
97
  * Persists the Copilot bridge state manifest.
66
98
  */