@minniexcode/codex-switch 0.1.1 → 0.1.3

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 (50) hide show
  1. package/README.CN.md +6 -4
  2. package/README.md +15 -4
  3. package/dist/app/add-provider.js +1 -0
  4. package/dist/app/bridge.js +2 -1
  5. package/dist/app/switch-provider.js +2 -1
  6. package/dist/commands/handlers.js +1 -0
  7. package/dist/domain/config.js +45 -1
  8. package/dist/domain/providers.js +1 -0
  9. package/dist/runtime/copilot-adapter.js +346 -70
  10. package/dist/runtime/copilot-bridge-worker.js +27 -2
  11. package/dist/runtime/copilot-bridge.js +192 -10
  12. package/dist/runtime/copilot-cli.js +22 -0
  13. package/dist/runtime/copilot-installer.js +59 -1
  14. package/dist/runtime/copilot-sdk-loader.js +4 -1
  15. package/docs/Design/codex-switch-v0.1.0-design.md +32 -152
  16. package/docs/Design/codex-switch-v0.1.1-design.md +15 -26
  17. package/docs/Design/codex-switch-v0.1.2-design.md +65 -0
  18. package/docs/Design/codex-switch-v0.1.3-design.md +10 -0
  19. package/docs/PRD/codex-switch-prd-v0.1.0.md +65 -217
  20. package/docs/PRD/codex-switch-prd-v0.1.1.md +26 -0
  21. package/docs/PRD/codex-switch-prd-v0.1.2.md +41 -0
  22. package/docs/PRD/codex-switch-prd-v0.1.3.md +22 -0
  23. package/docs/Tests/testing.md +1 -1
  24. package/docs/cli-usage.md +14 -4
  25. package/docs/codex-switch-command-design.md +1 -1
  26. package/docs/codex-switch-product-overview.md +7 -3
  27. package/docs/codex-switch-product-research.md +2 -2
  28. package/docs/codex-switch-technical-architecture.md +86 -1115
  29. package/package.json +1 -1
  30. package/docs/Design/codex-switch-copilot-integration-design.md +0 -517
  31. package/docs/Design/codex-switch-v0.0.10-design.md +0 -669
  32. package/docs/Design/codex-switch-v0.0.11-design.md +0 -824
  33. package/docs/Design/codex-switch-v0.0.12-design.md +0 -343
  34. package/docs/Design/codex-switch-v0.0.4-design.md +0 -874
  35. package/docs/Design/codex-switch-v0.0.5-design.md +0 -932
  36. package/docs/Design/codex-switch-v0.0.6-design.md +0 -708
  37. package/docs/Design/codex-switch-v0.0.7-design.md +0 -862
  38. package/docs/Design/codex-switch-v0.0.8-design.md +0 -132
  39. package/docs/Design/codex-switch-v0.0.9-design.md +0 -182
  40. package/docs/Design/codex-switch-v0.0.9-to-v0.0.12-roadmap.md +0 -413
  41. package/docs/PRD/codex-switch-prd-v0.0.10.md +0 -406
  42. package/docs/PRD/codex-switch-prd-v0.0.11.md +0 -577
  43. package/docs/PRD/codex-switch-prd-v0.0.12.md +0 -279
  44. package/docs/PRD/codex-switch-prd-v0.0.5-to-v0.1.0.md +0 -446
  45. package/docs/PRD/codex-switch-prd-v0.0.8.md +0 -62
  46. package/docs/PRD/codex-switch-prd-v0.0.9.md +0 -166
  47. package/docs/PRD/codex-switch-prd.md +0 -650
  48. package/docs/Tests/test-report-0.0.5.md +0 -163
  49. package/docs/Tests/test-report-0.0.7.md +0 -118
  50. package/docs/Tests/testing-bridge-v0.0.9.md +0 -367
package/README.CN.md CHANGED
@@ -6,9 +6,9 @@
6
6
 
7
7
  ## 版本定位
8
8
 
9
- 当前包版本:`0.1.1`
9
+ 当前包版本:`0.1.2`
10
10
 
11
- 这是当前稳定发布线。`0.1.1` 的目标是把公开文档和 Codex `0.134.0+` runtime contract 对齐,也就是用顶层 `model` `model_provider` 选择活动路由。
11
+ 这是当前稳定发布线。`0.1.2` Copilot runtime 修复版本,包含受管 SDK 固定版本与 Copilot 专用的 `stream_idle_timeout_ms = 300000` 投影,用于避免长 prompt 的空闲超时。
12
12
 
13
13
  ## 安装
14
14
 
@@ -205,8 +205,10 @@ npm pack --dry-run
205
205
  - [详细 CLI 文档](./docs/cli-usage.md)
206
206
  - [产品概览](./docs/codex-switch-product-overview.md)
207
207
  - [测试说明](./docs/Tests/testing.md)
208
- - [Release PRD 0.1.1](./docs/PRD/codex-switch-prd-v0.1.1.md)
209
- - [Release Design 0.1.1](./docs/Design/codex-switch-v0.1.1-design.md)
208
+ - [PRD 0.1.1](./docs/PRD/codex-switch-prd-v0.1.1.md)
209
+ - [Design 0.1.1](./docs/Design/codex-switch-v0.1.1-design.md)
210
+ - [PRD 0.1.2](./docs/PRD/codex-switch-prd-v0.1.2.md)
211
+ - [Design 0.1.2](./docs/Design/codex-switch-v0.1.2-design.md)
210
212
 
211
213
  ## License
212
214
 
package/README.md CHANGED
@@ -8,9 +8,9 @@ Chinese version: [README.CN.md](./README.CN.md)
8
8
 
9
9
  ## Version
10
10
 
11
- Current package version: `0.1.1`
11
+ Current package version: `0.1.3`
12
12
 
13
- This is the current stable release line. `0.1.1` aligns the public docs with the Codex `0.134.0+` runtime contract where top-level `model` and `model_provider` select the active route.
13
+ This is the current stable documentation line. `0.1.3` is the Copilot login hotfix release, repairing the managed SDK client construction against the current official Copilot SDK runtime while keeping the `stream_idle_timeout_ms = 300000` Copilot projection unchanged.
14
14
 
15
15
  ## Install
16
16
 
@@ -58,6 +58,7 @@ Notes:
58
58
  - `init` prepares the `codex-switch` tool home and managed state.
59
59
  - `login copilot` handles upstream Copilot onboarding and auth readiness.
60
60
  - `add --copilot` does not perform login for you; it assumes Copilot login is already ready.
61
+ - Copilot support is an experimental local bridge. The managed installer defaults to `@github/copilot-sdk@1.0.2`, Copilot runtime paths require Node.js `>=20`, and runtime checks separately reject older or prerelease SDK installs while validating API shape when the client or session is used.
61
62
  - `switch` projects the selected provider into the target Codex runtime as top-level `model` plus `model_provider`.
62
63
  - `status` is the main read command after switching.
63
64
  - `doctor` is the main repair-oriented diagnostic command.
@@ -95,6 +96,12 @@ wire_api = "responses"
95
96
  requires_openai_auth = true
96
97
  ```
97
98
 
99
+ Managed Copilot projection additionally writes:
100
+
101
+ ```toml
102
+ stream_idle_timeout_ms = 300000
103
+ ```
104
+
98
105
  ## Advanced Adopt Workflow
99
106
 
100
107
  Use `migrate` only when you already have Codex runtime state that should be adopted into managed `providers.json` state:
@@ -207,8 +214,12 @@ npm pack --dry-run
207
214
  - [Detailed CLI Usage](./docs/cli-usage.md)
208
215
  - [Testing Guide](./docs/Tests/testing.md)
209
216
  - [Product Overview](./docs/codex-switch-product-overview.md)
210
- - [Release PRD 0.1.1](./docs/PRD/codex-switch-prd-v0.1.1.md)
211
- - [Release Design 0.1.1](./docs/Design/codex-switch-v0.1.1-design.md)
217
+ - [PRD 0.1.0](./docs/PRD/codex-switch-prd-v0.1.0.md)
218
+ - [PRD 0.1.1](./docs/PRD/codex-switch-prd-v0.1.1.md)
219
+ - [PRD 0.1.2](./docs/PRD/codex-switch-prd-v0.1.2.md)
220
+ - [PRD 0.1.3](./docs/PRD/codex-switch-prd-v0.1.3.md)
221
+ - [Design 0.1.2](./docs/Design/codex-switch-v0.1.2-design.md)
222
+ - [Design 0.1.3](./docs/Design/codex-switch-v0.1.3-design.md)
212
223
 
213
224
  ## License
214
225
 
@@ -67,6 +67,7 @@ async function addProvider(args) {
67
67
  }
68
68
  : undefined;
69
69
  if (args.copilot) {
70
+ (0, copilot_installer_1.assertCopilotNodeRuntimeSupported)();
70
71
  const installStatus = (0, copilot_installer_1.probeCopilotSdkInstall)(args.runtimesDir);
71
72
  if (!installStatus.installed) {
72
73
  throw (0, errors_1.cliError)("COPILOT_SDK_MISSING", "The optional Copilot SDK runtime is not installed. Run `codexs login copilot` first.", {
@@ -29,7 +29,7 @@ async function startBridge(args) {
29
29
  preferRuntimeState: false,
30
30
  });
31
31
  await requireBridgeRuntimeReadiness(args.runtimesDir);
32
- const bridge = await (0, copilot_bridge_1.ensureCopilotBridge)(target.providerName, target.provider, args.runtimeDir);
32
+ const bridge = await (0, copilot_bridge_1.ensureCopilotBridge)(target.providerName, target.provider, args.runtimeDir, args.runtimesDir);
33
33
  const nextProvider = bridge.portChanged ? rewriteBridgeProviderPort(target.provider, bridge.port) : target.provider;
34
34
  if (bridge.portChanged) {
35
35
  try {
@@ -242,6 +242,7 @@ async function promptForCopilotBridgeSelection(runtime, targets, commandName) {
242
242
  * Verifies that the local Copilot bridge prerequisites are available before startup.
243
243
  */
244
244
  async function requireBridgeRuntimeReadiness(runtimesDir) {
245
+ (0, copilot_installer_1.assertCopilotNodeRuntimeSupported)();
245
246
  const installStatus = (0, copilot_installer_1.probeCopilotSdkInstall)(runtimesDir);
246
247
  if (!installStatus.installed) {
247
248
  throw (0, errors_1.cliError)("COPILOT_SDK_MISSING", "The optional Copilot SDK runtime is not installed.", {
@@ -31,6 +31,7 @@ async function switchProvider(args) {
31
31
  });
32
32
  }
33
33
  if ((0, providers_1.isCopilotBridgeProvider)(provider)) {
34
+ (0, copilot_installer_1.assertCopilotNodeRuntimeSupported)();
34
35
  const installStatus = (0, copilot_installer_1.probeCopilotSdkInstall)(args.runtimesDir);
35
36
  if (!installStatus.installed) {
36
37
  throw (0, errors_1.cliError)("COPILOT_SDK_MISSING", "The optional Copilot SDK runtime is not installed.", {
@@ -39,7 +40,7 @@ async function switchProvider(args) {
39
40
  });
40
41
  }
41
42
  await (0, copilot_adapter_1.readCopilotAuthState)(args.runtimesDir);
42
- const bridge = await (0, copilot_bridge_1.ensureCopilotBridge)(args.providerName, provider, args.runtimeDir);
43
+ const bridge = await (0, copilot_bridge_1.ensureCopilotBridge)(args.providerName, provider, args.runtimeDir, args.runtimesDir);
43
44
  const nextProvider = bridge.portChanged
44
45
  ? (0, providers_1.cleanProviderRecord)({
45
46
  ...provider,
@@ -156,6 +156,7 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
156
156
  supportedUpstreams: ["copilot", "github-copilot"],
157
157
  });
158
158
  }
159
+ (0, copilot_installer_1.assertCopilotNodeRuntimeSupported)();
159
160
  const installed = (0, copilot_installer_1.probeCopilotSdkInstall)(paths.runtimesDir);
160
161
  let installedNow = false;
161
162
  if (!installed.installed) {
@@ -133,6 +133,8 @@ function parseStructuredConfig(configContent) {
133
133
  requiresOpenAiAuth: null,
134
134
  wireApiValueRange: null,
135
135
  wireApi: null,
136
+ streamIdleTimeoutMsValueRange: null,
137
+ streamIdleTimeoutMs: null,
136
138
  envKeyValueRange: null,
137
139
  envKey: null,
138
140
  envKeyInstructionsValueRange: null,
@@ -209,6 +211,11 @@ function parseStructuredConfig(configContent) {
209
211
  currentModelProviderSection.wireApi = wireApiMatch.value;
210
212
  currentModelProviderSection.wireApiValueRange = toAbsoluteRange(line.start, wireApiMatch.valueStart, wireApiMatch.valueEnd);
211
213
  }
214
+ const streamIdleTimeoutMsMatch = matchNumberKeyValueLine(line.content, "stream_idle_timeout_ms");
215
+ if (streamIdleTimeoutMsMatch) {
216
+ currentModelProviderSection.streamIdleTimeoutMs = streamIdleTimeoutMsMatch.value;
217
+ currentModelProviderSection.streamIdleTimeoutMsValueRange = toAbsoluteRange(line.start, streamIdleTimeoutMsMatch.valueStart, streamIdleTimeoutMsMatch.valueEnd);
218
+ }
212
219
  const envKeyMatch = matchKeyValueLine(line.content, "env_key");
213
220
  if (envKeyMatch) {
214
221
  currentModelProviderSection.envKey = envKeyMatch.value;
@@ -481,7 +488,10 @@ function planConfigMutation(document, args) {
481
488
  `base_url = ${JSON.stringify(normalizedFields.baseUrl)}${document.lineEnding}` +
482
489
  `name = ${JSON.stringify(normalizedFields.name)}${document.lineEnding}` +
483
490
  `requires_openai_auth = ${String(normalizedFields.requiresOpenAiAuth)}${document.lineEnding}` +
484
- `wire_api = ${JSON.stringify(normalizedFields.wireApi)}${document.lineEnding}`,
491
+ `wire_api = ${JSON.stringify(normalizedFields.wireApi)}${document.lineEnding}` +
492
+ (normalizedFields.streamIdleTimeoutMs !== undefined
493
+ ? `stream_idle_timeout_ms = ${String(normalizedFields.streamIdleTimeoutMs)}${document.lineEnding}`
494
+ : ""),
485
495
  });
486
496
  createdModelProviderSections.push(profileName);
487
497
  continue;
@@ -597,6 +607,7 @@ function planModelProviderFieldMutation(document, section, fields, operations) {
597
607
  const nameText = JSON.stringify(fields.name);
598
608
  const requiresOpenAiAuthText = String(fields.requiresOpenAiAuth);
599
609
  const wireApiText = JSON.stringify(fields.wireApi);
610
+ const streamIdleTimeoutMsText = fields.streamIdleTimeoutMs !== undefined ? String(fields.streamIdleTimeoutMs) : null;
600
611
  const inserts = [];
601
612
  if (section.baseUrlValueRange) {
602
613
  if (section.baseUrl !== fields.baseUrl) {
@@ -658,6 +669,23 @@ function planModelProviderFieldMutation(document, section, fields, operations) {
658
669
  inserts.push(`wire_api = ${wireApiText}`);
659
670
  updated = true;
660
671
  }
672
+ if (streamIdleTimeoutMsText !== null) {
673
+ if (section.streamIdleTimeoutMsValueRange) {
674
+ if (section.streamIdleTimeoutMs !== fields.streamIdleTimeoutMs) {
675
+ operations.push({
676
+ kind: "replace-range",
677
+ start: section.streamIdleTimeoutMsValueRange.start,
678
+ end: section.streamIdleTimeoutMsValueRange.end,
679
+ text: streamIdleTimeoutMsText,
680
+ });
681
+ updated = true;
682
+ }
683
+ }
684
+ else {
685
+ inserts.push(`stream_idle_timeout_ms = ${streamIdleTimeoutMsText}`);
686
+ updated = true;
687
+ }
688
+ }
661
689
  if (inserts.length > 0) {
662
690
  operations.push({
663
691
  kind: "insert-at",
@@ -713,6 +741,7 @@ function normalizeManagedModelProviderFields(profileName, fields) {
713
741
  name: fields.name?.trim() || profileName,
714
742
  requiresOpenAiAuth: fields.requiresOpenAiAuth ?? true,
715
743
  wireApi: fields.wireApi?.trim() || "responses",
744
+ streamIdleTimeoutMs: fields.streamIdleTimeoutMs,
716
745
  };
717
746
  }
718
747
  function splitWithOffsets(value) {
@@ -773,6 +802,21 @@ function matchBooleanKeyValueLine(line, key) {
773
802
  valueEnd,
774
803
  };
775
804
  }
805
+ function matchNumberKeyValueLine(line, key) {
806
+ const match = line.match(new RegExp(`^\\s*${escapeRegExp(key)}\\s*=\\s*(\\d+)\\s*(#.*)?$`));
807
+ if (!match || match.index === undefined) {
808
+ return null;
809
+ }
810
+ const valueStart = line.indexOf(match[1], match.index);
811
+ if (valueStart === -1) {
812
+ return null;
813
+ }
814
+ return {
815
+ value: Number(match[1]),
816
+ valueStart,
817
+ valueEnd: valueStart + match[1].length,
818
+ };
819
+ }
776
820
  function findManagedFieldInsertIndex(rawText, sectionStart, sectionEnd) {
777
821
  const sectionText = rawText.slice(sectionStart, sectionEnd);
778
822
  const lines = splitWithOffsets(sectionText);
@@ -168,6 +168,7 @@ function buildCopilotModelProviderProjection(runtime) {
168
168
  name: "copilot",
169
169
  requiresOpenAiAuth: true,
170
170
  wireApi: "responses",
171
+ streamIdleTimeoutMs: 300000,
171
172
  };
172
173
  }
173
174
  /**