@minniexcode/codex-switch 0.1.5 → 0.2.2
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 +66 -94
- package/README.CN.md +84 -139
- package/README.md +91 -151
- package/dist/app/add-provider.js +6 -89
- package/dist/app/edit-provider.js +9 -29
- package/dist/app/get-status.js +1 -74
- package/dist/app/list-providers.js +0 -2
- package/dist/app/remove-provider.js +3 -5
- package/dist/app/run-doctor.js +2 -100
- package/dist/app/setup-codex.js +0 -2
- package/dist/app/switch-provider.js +1 -79
- package/dist/cli/output.js +3 -89
- package/dist/commands/handlers.js +20 -209
- package/dist/commands/help.js +1 -4
- package/dist/commands/registry.js +6 -74
- package/dist/domain/config.js +1 -3
- package/dist/domain/providers.js +4 -92
- package/dist/domain/runtime-state.js +0 -88
- package/dist/interaction/add-interactive.js +1 -55
- package/dist/interaction/interactive.js +1 -3
- package/dist/runtime/codex-probe.js +0 -12
- package/dist/storage/codex-paths.js +0 -2
- package/docs/Design/codex-switch-v0.2.0-design.md +56 -0
- package/docs/Design/codex-switch-v0.2.1-design.md +77 -0
- package/docs/PRD/codex-switch-prd-v0.2.1.md +82 -0
- package/docs/Tests/testing.md +32 -34
- package/docs/cli-usage.md +67 -235
- package/docs/codex-switch-command-design.md +1 -1
- package/docs/codex-switch-product-overview.md +49 -96
- package/docs/codex-switch-technical-architecture.md +37 -52
- package/package.json +1 -1
- package/dist/app/bridge.js +0 -308
- package/dist/runtime/copilot-adapter.js +0 -612
- package/dist/runtime/copilot-bridge-worker.js +0 -69
- package/dist/runtime/copilot-bridge.js +0 -1318
- package/dist/runtime/copilot-cli.js +0 -164
- package/dist/runtime/copilot-installer.js +0 -231
- package/dist/runtime/copilot-sdk-loader.js +0 -62
- package/dist/storage/runtime-state-repo.js +0 -121
|
@@ -1,94 +1,6 @@
|
|
|
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.getStorageRoles = getStorageRoles;
|
|
37
3
|
exports.inspectLiveStateDrift = inspectLiveStateDrift;
|
|
38
|
-
const path = __importStar(require("node:path"));
|
|
39
|
-
/**
|
|
40
|
-
* Returns the stable storage contract used by the CLI.
|
|
41
|
-
*/
|
|
42
|
-
function getStorageRoles(args) {
|
|
43
|
-
const toolHomeDir = path.dirname(path.resolve(args.providersPath));
|
|
44
|
-
const backupsDir = path.join(toolHomeDir, "backups");
|
|
45
|
-
const runtimeDir = args.runtimeDir ? path.resolve(args.runtimeDir) : path.join(toolHomeDir, "runtime");
|
|
46
|
-
const runtimesDir = args.runtimesDir ? path.resolve(args.runtimesDir) : path.join(toolHomeDir, "runtimes");
|
|
47
|
-
return {
|
|
48
|
-
toolHome: {
|
|
49
|
-
root: toolHomeDir,
|
|
50
|
-
toolConfig: path.join(toolHomeDir, "codex-switch.json"),
|
|
51
|
-
providers: path.resolve(args.providersPath),
|
|
52
|
-
backupsDir,
|
|
53
|
-
latestBackup: path.join(backupsDir, "latest.json"),
|
|
54
|
-
runtimeStateDir: runtimeDir,
|
|
55
|
-
runtimeInstallDir: runtimesDir,
|
|
56
|
-
},
|
|
57
|
-
targetRuntime: {
|
|
58
|
-
root: path.resolve(args.codexDir),
|
|
59
|
-
config: path.resolve(args.configPath),
|
|
60
|
-
auth: path.resolve(args.authPath),
|
|
61
|
-
},
|
|
62
|
-
managementSSOT: {
|
|
63
|
-
scope: "toolHome",
|
|
64
|
-
path: path.resolve(args.providersPath),
|
|
65
|
-
},
|
|
66
|
-
runtimeMirrors: [
|
|
67
|
-
{
|
|
68
|
-
scope: "targetRuntime",
|
|
69
|
-
path: path.resolve(args.configPath),
|
|
70
|
-
},
|
|
71
|
-
],
|
|
72
|
-
authStateFile: {
|
|
73
|
-
scope: "targetRuntime",
|
|
74
|
-
path: path.resolve(args.authPath),
|
|
75
|
-
},
|
|
76
|
-
rollbackState: {
|
|
77
|
-
scope: "toolHome",
|
|
78
|
-
path: path.join(backupsDir, "latest.json"),
|
|
79
|
-
},
|
|
80
|
-
runtimeState: {
|
|
81
|
-
scope: "toolHome",
|
|
82
|
-
path: runtimeDir,
|
|
83
|
-
managedBackup: false,
|
|
84
|
-
},
|
|
85
|
-
runtimeInstall: {
|
|
86
|
-
scope: "toolHome",
|
|
87
|
-
path: runtimesDir,
|
|
88
|
-
managedBackup: false,
|
|
89
|
-
},
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
4
|
/**
|
|
93
5
|
* Compares the live active model_provider against managed providers to detect drift.
|
|
94
6
|
*/
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.COMMON_TAG_CHOICES = void 0;
|
|
4
4
|
exports.collectAddInput = collectAddInput;
|
|
5
|
-
exports.collectCopilotAddInput = collectCopilotAddInput;
|
|
6
5
|
exports.createNonInteractiveAddError = createNonInteractiveAddError;
|
|
7
6
|
exports.promptTags = promptTags;
|
|
8
7
|
const errors_1 = require("../domain/errors");
|
|
@@ -38,51 +37,10 @@ async function collectAddInput(runtime, defaults, providerExists, profileExists)
|
|
|
38
37
|
tags,
|
|
39
38
|
};
|
|
40
39
|
}
|
|
41
|
-
/**
|
|
42
|
-
* Collects Copilot add command inputs interactively when required values are missing.
|
|
43
|
-
*/
|
|
44
|
-
async function collectCopilotAddInput(runtime, defaults, providerExists, profileExists, options) {
|
|
45
|
-
runtime.writeLine("Interactive add mode");
|
|
46
|
-
runtime.writeLine("Provide the missing Copilot provider fields. Press Enter to keep optional values empty.");
|
|
47
|
-
const providerName = defaults.providerName
|
|
48
|
-
? normalizeRequiredValue(defaults.providerName)
|
|
49
|
-
: await promptProviderName(runtime, providerExists);
|
|
50
|
-
const profile = defaults.profile ? normalizeRequiredValue(defaults.profile) : await promptRequiredValue(runtime, "Profile");
|
|
51
|
-
const createProfile = !profileExists(profile);
|
|
52
|
-
const model = createProfile
|
|
53
|
-
? defaults.model
|
|
54
|
-
? normalizeRequiredValue(defaults.model)
|
|
55
|
-
: await promptRequiredValue(runtime, `Model for new profile "${profile}"`)
|
|
56
|
-
: defaults.model ?? null;
|
|
57
|
-
const note = defaults.note ?? normalizeOptionalValue(await runtime.inputText("Note (optional)"));
|
|
58
|
-
const tags = defaults.tags.length > 0 ? defaults.tags : await promptTags(runtime);
|
|
59
|
-
const bridgeHost = options?.bridgeHost ?? normalizeOptionalValue(await runtime.inputText("Bridge host (optional)", { defaultValue: "127.0.0.1" }));
|
|
60
|
-
const bridgePortText = options?.bridgePort !== undefined && options.bridgePort !== null
|
|
61
|
-
? String(options.bridgePort)
|
|
62
|
-
: await runtime.inputText("Bridge port (optional)", { defaultValue: "41415" });
|
|
63
|
-
const bridgePort = normalizeBridgePort(runtime, bridgePortText);
|
|
64
|
-
const bridgeApiKey = options?.bridgeApiKey ?? normalizeOptionalValue(await runtime.inputSecret("Bridge API key (optional)"));
|
|
65
|
-
return {
|
|
66
|
-
providerName,
|
|
67
|
-
profile,
|
|
68
|
-
createProfile,
|
|
69
|
-
model,
|
|
70
|
-
note,
|
|
71
|
-
tags,
|
|
72
|
-
bridgeApiKey,
|
|
73
|
-
bridgeHost,
|
|
74
|
-
bridgePort,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
40
|
/**
|
|
78
41
|
* Throws a consistent error when interactive add is unavailable.
|
|
79
42
|
*/
|
|
80
|
-
function createNonInteractiveAddError(
|
|
81
|
-
if (options?.copilot) {
|
|
82
|
-
return (0, errors_1.cliError)("INVALID_ARGUMENT", "add --copilot requires <provider> and --profile when running without an interactive TTY.", {
|
|
83
|
-
suggestion: "Run in a terminal TTY or pass <provider>, --profile, and any optional Copilot bridge flags explicitly.",
|
|
84
|
-
});
|
|
85
|
-
}
|
|
43
|
+
function createNonInteractiveAddError() {
|
|
86
44
|
return (0, errors_1.cliError)("INVALID_ARGUMENT", "add requires <provider>, --profile, and --api-key when running without an interactive TTY.", {
|
|
87
45
|
suggestion: "Run in a terminal TTY or pass all required values explicitly.",
|
|
88
46
|
});
|
|
@@ -132,18 +90,6 @@ function normalizeOptionalValue(value) {
|
|
|
132
90
|
const normalized = value.trim();
|
|
133
91
|
return normalized === "" ? null : normalized;
|
|
134
92
|
}
|
|
135
|
-
function normalizeBridgePort(runtime, value) {
|
|
136
|
-
const normalized = value.trim();
|
|
137
|
-
if (normalized === "") {
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
const parsed = Number(normalized);
|
|
141
|
-
if (Number.isInteger(parsed) && parsed > 0) {
|
|
142
|
-
return parsed;
|
|
143
|
-
}
|
|
144
|
-
runtime.writeLine("Bridge port must be a positive integer. Falling back to the default port.");
|
|
145
|
-
return null;
|
|
146
|
-
}
|
|
147
93
|
async function promptTags(runtime, defaults = []) {
|
|
148
94
|
const defaultPresetTags = defaults.filter(isCommonTag);
|
|
149
95
|
return runtime.selectMany("Select tags (optional)", exports.COMMON_TAG_CHOICES.map((tag) => ({ value: tag, label: tag })), { defaultValues: defaultPresetTags });
|
|
@@ -52,7 +52,6 @@ const fs = __importStar(require("node:fs"));
|
|
|
52
52
|
const path = __importStar(require("node:path"));
|
|
53
53
|
const errors_1 = require("../domain/errors");
|
|
54
54
|
const backups_1 = require("../domain/backups");
|
|
55
|
-
const providers_1 = require("../domain/providers");
|
|
56
55
|
const runtime_state_1 = require("../domain/runtime-state");
|
|
57
56
|
const codex_paths_1 = require("../storage/codex-paths");
|
|
58
57
|
const config_repo_1 = require("../storage/config-repo");
|
|
@@ -81,14 +80,13 @@ async function promptForProviderSelection(runtime, providersPath, configPath, me
|
|
|
81
80
|
const choices = Object.entries(providers.providers)
|
|
82
81
|
.sort(([left], [right]) => left.localeCompare(right))
|
|
83
82
|
.map(([providerName, provider]) => {
|
|
84
|
-
const providerType = (0, providers_1.isCopilotBridgeProvider)(provider) ? "copilot" : "direct";
|
|
85
83
|
const currentMarker = liveState.providerResolvable && liveState.mappedProvider === providerName ? " | current" : "";
|
|
86
84
|
const legacyMarker = !currentMarker && legacyCurrentProvider === providerName ? " | current" : "";
|
|
87
85
|
const ambiguousMarker = !liveState.providerResolvable && liveState.mappedProviders.includes(providerName) ? " | current=ambiguous" : "";
|
|
88
86
|
return {
|
|
89
87
|
value: providerName,
|
|
90
88
|
label: providerName,
|
|
91
|
-
hint: `profile=${provider.profile}
|
|
89
|
+
hint: `profile=${provider.profile}${currentMarker}${legacyMarker}${ambiguousMarker}`,
|
|
92
90
|
};
|
|
93
91
|
});
|
|
94
92
|
if (choices.length === 0) {
|
|
@@ -1,19 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.codexRuntimeProbe = void 0;
|
|
4
3
|
exports.probeCodexRuntime = probeCodexRuntime;
|
|
5
4
|
const codex_cli_1 = require("./codex-cli");
|
|
6
|
-
/**
|
|
7
|
-
* Default dependency probe implementation for the local codex CLI runtime.
|
|
8
|
-
*/
|
|
9
|
-
exports.codexRuntimeProbe = {
|
|
10
|
-
probe(options) {
|
|
11
|
-
if (options?.minVersion) {
|
|
12
|
-
return probeCodexRuntime(options.minVersion);
|
|
13
|
-
}
|
|
14
|
-
return probeCodexRuntime();
|
|
15
|
-
},
|
|
16
|
-
};
|
|
17
5
|
/**
|
|
18
6
|
* Checks whether the codex CLI is installed and, optionally, satisfies a minimum version.
|
|
19
7
|
*/
|
|
@@ -88,8 +88,6 @@ function createCodexPaths(args) {
|
|
|
88
88
|
backupsDir: path.join(toolHomeDir, "backups"),
|
|
89
89
|
latestBackupPath: path.join(toolHomeDir, "backups", "latest.json"),
|
|
90
90
|
lockPath: path.join(toolHomeDir, ".codex-switch.lock"),
|
|
91
|
-
runtimeDir: path.join(toolHomeDir, "runtime"),
|
|
92
|
-
runtimesDir: path.join(toolHomeDir, "runtimes"),
|
|
93
91
|
codexDir,
|
|
94
92
|
configPath: path.join(codexDir, "config.toml"),
|
|
95
93
|
authPath: path.join(codexDir, "auth.json"),
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# codex-switch v0.2.0 Design
|
|
2
|
+
|
|
3
|
+
`0.2.0` is a major architecture release that replaces the Copilot SDK-based authentication and runtime model with a direct GitHub device-flow token exchange and HTTP proxy bridge.
|
|
4
|
+
|
|
5
|
+
## Architecture Changes
|
|
6
|
+
|
|
7
|
+
### Authentication: SDK-based to Device-Flow Token
|
|
8
|
+
|
|
9
|
+
- The Copilot SDK (`@github/copilot-sdk`) is no longer required for authentication or session management.
|
|
10
|
+
- Authentication is now handled via GitHub OAuth Device Flow (`login copilot`), which produces a GitHub personal access token stored at `<toolHomeDir>/github-token`.
|
|
11
|
+
- The GitHub PAT is exchanged for a short-lived Copilot API token via `POST /copilot_internal/v2/token` before every bridge start or switch operation.
|
|
12
|
+
- `TokenManager` handles background token refresh and expiry-aware caching with a new `invalidate()` method for forced refresh on upstream 401 responses.
|
|
13
|
+
|
|
14
|
+
### Bridge: SDK Session to HTTP Proxy
|
|
15
|
+
|
|
16
|
+
- The bridge worker (`copilot-http-bridge-worker.ts`) is now a pure HTTP reverse proxy between the local OpenAI-compatible surface and `api.githubcopilot.com`.
|
|
17
|
+
- Copilot token lifecycle (exchange, refresh, invalidation) is managed inside the worker via `createTokenManager`.
|
|
18
|
+
- On upstream 401, the worker invalidates its cached token and retries once with a freshly exchanged token.
|
|
19
|
+
- A `createStaticTokenManager` variant supports test scenarios where the exchange should be bypassed (`CODEX_SWITCH_BRIDGE_COPILOT_TOKEN` env var).
|
|
20
|
+
|
|
21
|
+
### Path Resolution: toolHomeDir Propagation
|
|
22
|
+
|
|
23
|
+
- `toolHomeDir` is now explicitly threaded through all functions that read the GitHub token: `switchProvider`, `startBridge`, `getStatus`, `runDoctor`, and the bridge worker spawn.
|
|
24
|
+
- The bridge worker receives the correct `toolHomeDir` (not `runtimeDir`) via `CODEX_SWITCH_TOOL_HOME_DIR`, ensuring the token is found at `<toolHomeDir>/github-token` rather than a nested runtime subdirectory.
|
|
25
|
+
- `readGithubToken(toolHomeDir?)` resolves the home directory in a consistent order: explicit arg, `CODEXS_HOME` env var, then `~/.config/codex-switch`.
|
|
26
|
+
|
|
27
|
+
### Provider Runtime Kind
|
|
28
|
+
|
|
29
|
+
- New providers created via `add --copilot` now use `kind: "copilot-http-proxy"` instead of `kind: "copilot-sdk-bridge"`.
|
|
30
|
+
- Both kinds are accepted by `isCopilotBridgeProvider` for backward compatibility with existing provider files.
|
|
31
|
+
- The distinction is cosmetic; both route through the same HTTP bridge worker.
|
|
32
|
+
|
|
33
|
+
### Status Contract
|
|
34
|
+
|
|
35
|
+
- `getStatus` output `copilotSdk` field is simplified to `{ installed: boolean, source: string }` reflecting the presence of a GitHub token rather than an SDK install.
|
|
36
|
+
- `copilotAuth` reflects the token exchange readiness rather than SDK session health.
|
|
37
|
+
- Legacy fields (`installDir`, `packageName`, `packageVersion`) are removed from the status contract.
|
|
38
|
+
|
|
39
|
+
## Test Infrastructure
|
|
40
|
+
|
|
41
|
+
- `setCopilotTokenExchangeImplementation` / `resetCopilotTokenExchangeImplementation` provide an in-process mock for the Copilot token exchange, enabling offline test execution.
|
|
42
|
+
- The bridge worker supports `CODEX_SWITCH_BRIDGE_COPILOT_TOKEN` env var to skip the real exchange in spawned child processes during tests.
|
|
43
|
+
- Integration tests that previously relied on fake SDK mock responses now verify state and configuration correctness without making HTTP requests through the bridge (bridge request handling is covered by `copilot-bridge-contract.spec.js`).
|
|
44
|
+
|
|
45
|
+
## Breaking Changes
|
|
46
|
+
|
|
47
|
+
- `copilotSdk` status output no longer includes `installDir`, `packageName`, or `packageVersion`.
|
|
48
|
+
- Provider `runtimeKind` for newly-created Copilot providers is `"copilot-http-proxy"` instead of `"copilot-sdk-bridge"`.
|
|
49
|
+
- `switchProvider` requires `toolHomeDir` to be passed explicitly when operating outside the default env-var-resolved home.
|
|
50
|
+
- The Copilot SDK (`@github/copilot-sdk`) is no longer a runtime dependency for authentication flows.
|
|
51
|
+
|
|
52
|
+
## Non-Goals
|
|
53
|
+
|
|
54
|
+
- The Copilot SDK is not removed from the repository; existing workflows that depend on it for non-auth purposes remain unchanged.
|
|
55
|
+
- No migration of existing `copilot-sdk-bridge` provider records to `copilot-http-proxy`; both are accepted.
|
|
56
|
+
- No changes to direct (non-Copilot) provider workflows.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# codex-switch v0.2.1 Design
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
`0.2.1` narrows the current implementation to provider/model-provider management for Codex. The design removes current-facing bridge/account runtime paths and keeps a direct local-state architecture.
|
|
6
|
+
|
|
7
|
+
## Command Registry
|
|
8
|
+
|
|
9
|
+
The command registry contains only provider-management, config inspection, backup, rollback, and diagnostics commands. Removed command ids such as `login` and `bridge-*` are not part of the internal `CommandId` union.
|
|
10
|
+
|
|
11
|
+
`setup` remains registered only as a deprecated pointer to `init` and `migrate`.
|
|
12
|
+
|
|
13
|
+
## Provider Records
|
|
14
|
+
|
|
15
|
+
`providers.json` remains the managed source of provider records:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"providers": {
|
|
20
|
+
"packycode": {
|
|
21
|
+
"profile": "packycode",
|
|
22
|
+
"apiKey": "sk-...",
|
|
23
|
+
"model": "gpt-5",
|
|
24
|
+
"baseUrl": "https://api.example/v1"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The stored `profile` field is the managed Codex `model_provider` id. Current user-facing docs explain `--profile` as this alias.
|
|
31
|
+
|
|
32
|
+
## Codex Projection
|
|
33
|
+
|
|
34
|
+
Mutating commands project provider state into the target Codex directory:
|
|
35
|
+
|
|
36
|
+
```toml
|
|
37
|
+
model = "gpt-5"
|
|
38
|
+
model_provider = "packycode"
|
|
39
|
+
|
|
40
|
+
[model_providers.packycode]
|
|
41
|
+
name = "packycode"
|
|
42
|
+
base_url = "https://api.example/v1"
|
|
43
|
+
wire_api = "responses"
|
|
44
|
+
requires_openai_auth = true
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Auth projection writes API-key mode into `auth.json`. Managed projection does not create legacy `[profiles.*]` sections for new providers.
|
|
48
|
+
|
|
49
|
+
## Output Contract
|
|
50
|
+
|
|
51
|
+
Human output is provider-management-only:
|
|
52
|
+
|
|
53
|
+
- `list` shows provider name, current marker, model-provider id, model, tags, and note.
|
|
54
|
+
- `status` shows target Codex directory, tool home, current route, mapping state, auth/config health, warnings, and next step.
|
|
55
|
+
- `doctor` shows local config/provider/auth/Codex CLI issues.
|
|
56
|
+
|
|
57
|
+
JSON output keeps the standard envelope:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"ok": true,
|
|
62
|
+
"command": "status",
|
|
63
|
+
"data": {},
|
|
64
|
+
"warnings": [],
|
|
65
|
+
"error": null
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Diagnostics
|
|
70
|
+
|
|
71
|
+
`status` uses route mapping and consistency checks for a lightweight operator view. `doctor` performs issue-first checks across config existence, providers existence, consistency issues, auth projection, route drift, and Codex CLI availability.
|
|
72
|
+
|
|
73
|
+
No optional bridge, SDK, upstream-account, or background-service diagnostics are part of the current design.
|
|
74
|
+
|
|
75
|
+
## Migration Policy
|
|
76
|
+
|
|
77
|
+
Because this repository is still treated as development-version software, `0.2.1` does not add automatic migration shims for old experimental provider or bridge state. Users can manually clean old state or re-add providers.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# codex-switch v0.2.1 PRD
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
`0.2.1` is a provider-management-only consolidation release for `@minniexcode/codex-switch`.
|
|
6
|
+
|
|
7
|
+
The product is a local-first CLI that manages Codex provider/model-provider routing state. It stores local provider records, projects Codex `model_provider` definitions, switches the active top-level `model` / `model_provider` route, and provides backups, diagnostics, import/export, and rollback.
|
|
8
|
+
|
|
9
|
+
## Version
|
|
10
|
+
|
|
11
|
+
- Version line: `0.2.1`
|
|
12
|
+
- Target package version: `0.2.1`
|
|
13
|
+
- Status: current repository development line
|
|
14
|
+
|
|
15
|
+
## Goals
|
|
16
|
+
|
|
17
|
+
- Make the current product positioning explicit: local-first provider/model-provider management.
|
|
18
|
+
- Remove current command and documentation promises for account-login and bridge runtime experiments.
|
|
19
|
+
- Keep direct OpenAI-compatible provider workflows simple and inspectable.
|
|
20
|
+
- Preserve current route-first Codex config projection for Codex `0.134.0+`.
|
|
21
|
+
- Keep `migrate` as an advanced adopt helper for existing Codex state.
|
|
22
|
+
- Keep `setup` only as a deprecated pointer.
|
|
23
|
+
|
|
24
|
+
## Current Command Surface
|
|
25
|
+
|
|
26
|
+
Current public commands are:
|
|
27
|
+
|
|
28
|
+
- `init`
|
|
29
|
+
- `migrate`
|
|
30
|
+
- `list`
|
|
31
|
+
- `show`
|
|
32
|
+
- `current`
|
|
33
|
+
- `status`
|
|
34
|
+
- `config show`
|
|
35
|
+
- `config list-profiles`
|
|
36
|
+
- `add`
|
|
37
|
+
- `edit`
|
|
38
|
+
- `switch`
|
|
39
|
+
- `remove`
|
|
40
|
+
- `import`
|
|
41
|
+
- `export`
|
|
42
|
+
- `backups list`
|
|
43
|
+
- `rollback`
|
|
44
|
+
- `doctor`
|
|
45
|
+
- `setup` as deprecated pointer
|
|
46
|
+
|
|
47
|
+
## Primary Workflow
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
codexs init
|
|
51
|
+
codexs add <provider> --profile <model-provider-id> --model <model> --api-key <key> --base-url <url>
|
|
52
|
+
codexs switch <provider>
|
|
53
|
+
codexs status
|
|
54
|
+
codexs doctor
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Non-Goals
|
|
58
|
+
|
|
59
|
+
`0.2.1` does not implement or reserve runtime code paths for:
|
|
60
|
+
|
|
61
|
+
- GitHub Copilot SDK integration.
|
|
62
|
+
- GitHub device-flow login.
|
|
63
|
+
- `login copilot`.
|
|
64
|
+
- `add --copilot`.
|
|
65
|
+
- HTTP proxy bridge or local bridge worker commands.
|
|
66
|
+
- `bridge start`, `bridge status`, or `bridge stop`.
|
|
67
|
+
- Background runtime services, bridge logs, or bridge runtime state.
|
|
68
|
+
- Built-in third-party router packaging.
|
|
69
|
+
- Account systems or cloud sync.
|
|
70
|
+
- Automatic migration of old Copilot or bridge state.
|
|
71
|
+
|
|
72
|
+
A future release may introduce a third-party router-like integration, but that is outside `0.2.1` and must not be represented as current workflow, schema, or runtime behavior.
|
|
73
|
+
|
|
74
|
+
## Acceptance Criteria
|
|
75
|
+
|
|
76
|
+
- Package metadata reports `0.2.1`.
|
|
77
|
+
- Current docs point to this PRD and the `0.2.1` design doc as fact sources.
|
|
78
|
+
- Help and command registry do not expose removed command ids.
|
|
79
|
+
- Human `list` output does not display provider type.
|
|
80
|
+
- Human `status` and `doctor` output do not report bridge, SDK, upstream auth, or bridge-log state.
|
|
81
|
+
- Interactive add collects only provider-management fields.
|
|
82
|
+
- Tests cover the provider-management-only release contract.
|
package/docs/Tests/testing.md
CHANGED
|
@@ -1,34 +1,32 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
The
|
|
6
|
-
|
|
7
|
-
##
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
npm
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
npm pack --dry-run
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
- Keep fixtures small and realistic.
|
|
34
|
-
- Use `dev-codex/local-sandbox` only when a test needs a representative Codex directory layout.
|
|
1
|
+
# Testing
|
|
2
|
+
|
|
3
|
+
Current version: `0.2.1`
|
|
4
|
+
|
|
5
|
+
The test suite is plain Node.js. `npm test` rebuilds the CLI and runs `tests/run-tests.js`, which discovers `tests/*.spec.js` files.
|
|
6
|
+
|
|
7
|
+
## Commands
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm run build
|
|
11
|
+
npx tsc --noEmit
|
|
12
|
+
npm test
|
|
13
|
+
node dist/cli.js --help
|
|
14
|
+
node dist/cli.js --version
|
|
15
|
+
npm pack --dry-run
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Required Coverage For 0.2.1
|
|
19
|
+
|
|
20
|
+
Focus on the provider-management-only contract:
|
|
21
|
+
|
|
22
|
+
- Version metadata is `0.2.1` in `package.json` and `package-lock.json`.
|
|
23
|
+
- Current docs point to `docs/PRD/codex-switch-prd-v0.2.1.md` and `docs/Design/codex-switch-v0.2.1-design.md`.
|
|
24
|
+
- Help exposes only current commands: `init`, `migrate`, `list`, `show`, `current`, `status`, `config show`, `config list-profiles`, `add`, `edit`, `switch`, `remove`, `import`, `export`, `backups list`, `rollback`, `doctor`, and deprecated `setup`.
|
|
25
|
+
- Fresh provider flow: `init -> add -> switch -> status -> doctor`.
|
|
26
|
+
- Base URL drift diagnostics.
|
|
27
|
+
- Ambiguous active provider mapping.
|
|
28
|
+
- `migrate` remains an advanced adopt helper.
|
|
29
|
+
- `setup` remains a deprecated pointer.
|
|
30
|
+
- JSON output uses the stable envelope.
|
|
31
|
+
|
|
32
|
+
Do not add tests for removed `0.2.1` runtime experiments such as Copilot SDK integration, GitHub login, `add --copilot`, or bridge commands.
|