@minniexcode/codex-switch 0.2.0 → 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.
Files changed (40) hide show
  1. package/README.AI.md +66 -94
  2. package/README.CN.md +84 -139
  3. package/README.md +91 -151
  4. package/dist/app/add-provider.js +6 -83
  5. package/dist/app/edit-provider.js +9 -29
  6. package/dist/app/get-status.js +1 -77
  7. package/dist/app/list-providers.js +0 -2
  8. package/dist/app/remove-provider.js +3 -5
  9. package/dist/app/run-doctor.js +2 -99
  10. package/dist/app/setup-codex.js +0 -2
  11. package/dist/app/switch-provider.js +1 -74
  12. package/dist/cli/output.js +3 -89
  13. package/dist/commands/handlers.js +20 -172
  14. package/dist/commands/help.js +1 -4
  15. package/dist/commands/registry.js +6 -74
  16. package/dist/domain/config.js +1 -3
  17. package/dist/domain/providers.js +4 -92
  18. package/dist/domain/runtime-state.js +0 -88
  19. package/dist/interaction/add-interactive.js +1 -55
  20. package/dist/interaction/interactive.js +1 -3
  21. package/dist/runtime/codex-probe.js +0 -12
  22. package/dist/storage/codex-paths.js +0 -2
  23. package/docs/Design/codex-switch-v0.2.1-design.md +77 -0
  24. package/docs/PRD/codex-switch-prd-v0.2.1.md +82 -0
  25. package/docs/Tests/testing.md +32 -34
  26. package/docs/cli-usage.md +67 -235
  27. package/docs/codex-switch-command-design.md +1 -1
  28. package/docs/codex-switch-product-overview.md +49 -96
  29. package/docs/codex-switch-technical-architecture.md +37 -52
  30. package/package.json +1 -1
  31. package/dist/app/bridge.js +0 -303
  32. package/dist/runtime/copilot-adapter.js +0 -617
  33. package/dist/runtime/copilot-bridge-worker.js +0 -69
  34. package/dist/runtime/copilot-bridge.js +0 -1351
  35. package/dist/runtime/copilot-cli.js +0 -164
  36. package/dist/runtime/copilot-http-bridge-worker.js +0 -228
  37. package/dist/runtime/copilot-installer.js +0 -231
  38. package/dist/runtime/copilot-sdk-loader.js +0 -62
  39. package/dist/runtime/copilot-token.js +0 -294
  40. 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(options) {
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} | type=${providerType}${currentMarker}${legacyMarker}${ambiguousMarker}`,
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,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.
@@ -1,34 +1,32 @@
1
- # codex-switch Testing Guide
2
-
3
- This guide records the current `0.1.x` verification contract for release and review work.
4
-
5
- The current repository line is `0.1.5` and remains an unreleased development line until an explicit release task says otherwise.
6
-
7
- ## Required checks
8
-
9
- Run these commands before publishing or reviewing release changes:
10
-
11
- ```bash
12
- npm run build
13
- npm test
14
- npx tsc --noEmit
15
- npm pack --dry-run
16
- node dist/cli.js --help
17
- node dist/cli.js --version
18
- ```
19
-
20
- ## Release scenarios
21
-
22
- - Fresh direct-provider flow: `init -> add -> switch -> status -> doctor`
23
- - Fresh Copilot flow: `init -> login copilot -> add --copilot -> switch -> status -> doctor`
24
- - Ambiguous active profile: `list`, `status`, and provider pickers must surface ambiguity instead of inventing a unique current provider
25
- - `--json` envelope: top-level `ok`, `command`, `data`, `warnings`, and `error` must remain stable
26
- - `migrate`: advanced adopt helper only
27
- - `setup`: deprecated entry only
28
- - Release hygiene: `package.json`, `package-lock.json`, current-line docs, changelog top entry, and current PRD/Design fact sources must agree on the `0.1.5` development line
29
-
30
- ## Fixture guidance
31
-
32
- - Prefer isolated temp directories for mutating tests.
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.