@minniexcode/codex-switch 0.1.4 → 0.1.5
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 +5 -4
- package/README.CN.md +9 -8
- package/README.md +9 -8
- package/dist/runtime/copilot-adapter.js +120 -0
- package/dist/runtime/copilot-bridge-worker.js +3 -0
- package/dist/runtime/copilot-bridge.js +109 -0
- package/docs/Design/codex-switch-v0.1.5-design.md +17 -0
- package/docs/PRD/codex-switch-prd-v0.1.5.md +42 -0
- package/docs/Tests/testing.md +2 -2
- package/docs/cli-usage.md +11 -7
- package/docs/codex-switch-product-overview.md +24 -23
- package/docs/codex-switch-technical-architecture.md +2 -2
- package/package.json +2 -2
package/README.AI.md
CHANGED
|
@@ -6,7 +6,7 @@ This file summarizes the current operational contract for AI agents, automation
|
|
|
6
6
|
|
|
7
7
|
- Package: `@minniexcode/codex-switch`
|
|
8
8
|
- CLI name: `codexs`
|
|
9
|
-
- Current repository version: `0.1.
|
|
9
|
+
- Current repository version: `0.1.5`
|
|
10
10
|
- Version status: development line
|
|
11
11
|
- Runtime contract target: Codex `0.134.0+`
|
|
12
12
|
|
|
@@ -22,7 +22,7 @@ Direct provider workflow:
|
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
24
|
codexs init
|
|
25
|
-
codexs add <provider> --model <model> --api-key <key> [--base-url <url>]
|
|
25
|
+
codexs add <provider> --profile <model-provider-id> --model <model> --api-key <key> [--base-url <url>]
|
|
26
26
|
codexs switch <provider>
|
|
27
27
|
codexs status
|
|
28
28
|
codexs doctor
|
|
@@ -33,7 +33,7 @@ GitHub Copilot workflow:
|
|
|
33
33
|
```bash
|
|
34
34
|
codexs init
|
|
35
35
|
codexs login copilot
|
|
36
|
-
codexs add <provider> --copilot --model <model>
|
|
36
|
+
codexs add <provider> --copilot --profile <model-provider-id> --model <model>
|
|
37
37
|
codexs switch <provider>
|
|
38
38
|
codexs status
|
|
39
39
|
codexs doctor
|
|
@@ -130,10 +130,11 @@ Important behavioral constraints:
|
|
|
130
130
|
- `login copilot` requires a real TTY and does not support `--json`.
|
|
131
131
|
- `login copilot` currently installs the local Copilot SDK when needed, tries the bundled runtime CLI first, falls back to `PATH` when necessary, and rechecks auth readiness before reporting success.
|
|
132
132
|
- `add --copilot` assumes SDK install and upstream Copilot auth are already ready.
|
|
133
|
+
- Non-interactive automation should pass `--profile` explicitly. In TTY mode, `add` and `edit` can prompt for missing required fields.
|
|
133
134
|
- `migrate` remains interactive when provider adoption requires human input.
|
|
134
135
|
- `status` is the main dual-path summary command.
|
|
135
136
|
- `doctor` is the deeper repair-oriented diagnostic command.
|
|
136
|
-
- The current `0.1.
|
|
137
|
+
- The current `0.1.5` line focuses on Copilot Bridge process visibility, Responses commentary/reasoning stream events, defensive SDK-event normalization, and unknown-event redaction hardening rather than command-surface expansion.
|
|
137
138
|
|
|
138
139
|
## Safety Notes
|
|
139
140
|
|
package/README.CN.md
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
## 版本定位
|
|
8
8
|
|
|
9
|
-
当前包版本:`0.1.
|
|
9
|
+
当前包版本:`0.1.5`
|
|
10
10
|
|
|
11
|
-
这是当前仓库开发线。`0.1.
|
|
11
|
+
这是当前仓库开发线。`0.1.5` 是 Copilot Bridge 过程可见性补丁,聚焦于 commentary/reasoning 流式信号、SDK 事件防御性归一化,以及未知运行态事件的更安全脱敏,同时不扩展 provider 命令面。
|
|
12
12
|
|
|
13
13
|
## 安装
|
|
14
14
|
|
|
@@ -34,7 +34,7 @@ Direct provider 主路径:
|
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
36
|
codexs init
|
|
37
|
-
codexs add my-provider --model gpt-5.5 --base-url https://gateway.example.com/v1 --api-key sk-xxx
|
|
37
|
+
codexs add my-provider --profile my-provider --model gpt-5.5 --base-url https://gateway.example.com/v1 --api-key sk-xxx
|
|
38
38
|
codexs switch my-provider
|
|
39
39
|
codexs status
|
|
40
40
|
codexs doctor
|
|
@@ -45,7 +45,7 @@ GitHub Copilot 主路径:
|
|
|
45
45
|
```bash
|
|
46
46
|
codexs init
|
|
47
47
|
codexs login copilot
|
|
48
|
-
codexs add copilot-main --copilot --model gpt-4.1
|
|
48
|
+
codexs add copilot-main --copilot --profile copilot-main --model gpt-4.1
|
|
49
49
|
codexs switch copilot-main
|
|
50
50
|
codexs status
|
|
51
51
|
codexs doctor
|
|
@@ -56,6 +56,7 @@ codexs doctor
|
|
|
56
56
|
- `init` 负责初始化 `codex-switch` 的 tool home 与受管状态文件。
|
|
57
57
|
- `login copilot` 负责上游 Copilot onboarding 和登录可用性检查。
|
|
58
58
|
- `add --copilot` 不负责替你登录,它假设上游 Copilot 已经 ready。
|
|
59
|
+
- 非交互调用请显式传入 `--profile`;在 TTY 模式下,`add` 和 `edit` 可以补问缺失的必填项。
|
|
59
60
|
- `switch` 会把选中的 provider 投影到目标 Codex runtime 的顶层 `model` 与 `model_provider`。
|
|
60
61
|
- `status` 是切换后的主读取命令。
|
|
61
62
|
- `doctor` 是主诊断命令,用于解释问题和下一步修复动作。
|
|
@@ -116,8 +117,8 @@ codexs current
|
|
|
116
117
|
codexs status
|
|
117
118
|
codexs config show [profile]
|
|
118
119
|
codexs config list-profiles
|
|
119
|
-
codexs add <provider> --model <model> --api-key <key> [--base-url <url>]
|
|
120
|
-
codexs add <provider> --copilot --model <model>
|
|
120
|
+
codexs add <provider> --profile <model-provider-id> --model <model> --api-key <key> [--base-url <url>]
|
|
121
|
+
codexs add <provider> --copilot --profile <model-provider-id> --model <model>
|
|
121
122
|
codexs edit <provider>
|
|
122
123
|
codexs switch <provider>
|
|
123
124
|
codexs remove <provider> [--force] [--switch-to <provider>]
|
|
@@ -209,8 +210,8 @@ npm pack --dry-run
|
|
|
209
210
|
- [Design 0.1.1](./docs/Design/codex-switch-v0.1.1-design.md)
|
|
210
211
|
- [PRD 0.1.2](./docs/PRD/codex-switch-prd-v0.1.2.md)
|
|
211
212
|
- [Design 0.1.2](./docs/Design/codex-switch-v0.1.2-design.md)
|
|
212
|
-
- [PRD 0.1.
|
|
213
|
-
- [Design 0.1.
|
|
213
|
+
- [PRD 0.1.5](./docs/PRD/codex-switch-prd-v0.1.5.md)
|
|
214
|
+
- [Design 0.1.5](./docs/Design/codex-switch-v0.1.5-design.md)
|
|
214
215
|
|
|
215
216
|
## License
|
|
216
217
|
|
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.
|
|
11
|
+
Current package version: `0.1.5`
|
|
12
12
|
|
|
13
|
-
This is the current repository development line. `0.1.
|
|
13
|
+
This is the current repository development line. `0.1.5` is a Copilot Bridge process-visibility patch, focused on streaming commentary/reasoning signals, defensive SDK-event normalization, and safer redaction for unknown runtime events while keeping the provider surface unchanged.
|
|
14
14
|
|
|
15
15
|
## Install
|
|
16
16
|
|
|
@@ -36,7 +36,7 @@ Direct provider workflow:
|
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
38
|
codexs init
|
|
39
|
-
codexs add my-provider --model gpt-5.5 --base-url https://gateway.example.com/v1 --api-key sk-xxx
|
|
39
|
+
codexs add my-provider --profile my-provider --model gpt-5.5 --base-url https://gateway.example.com/v1 --api-key sk-xxx
|
|
40
40
|
codexs switch my-provider
|
|
41
41
|
codexs status
|
|
42
42
|
codexs doctor
|
|
@@ -47,7 +47,7 @@ GitHub Copilot workflow:
|
|
|
47
47
|
```bash
|
|
48
48
|
codexs init
|
|
49
49
|
codexs login copilot
|
|
50
|
-
codexs add copilot-main --copilot --model gpt-4.1
|
|
50
|
+
codexs add copilot-main --copilot --profile copilot-main --model gpt-4.1
|
|
51
51
|
codexs switch copilot-main
|
|
52
52
|
codexs status
|
|
53
53
|
codexs doctor
|
|
@@ -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
|
+
- For non-interactive use, pass `--profile` explicitly. In TTY mode, `add` and `edit` can prompt for missing required fields.
|
|
61
62
|
- 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.
|
|
62
63
|
- `switch` projects the selected provider into the target Codex runtime as top-level `model` plus `model_provider`.
|
|
63
64
|
- `status` is the main read command after switching.
|
|
@@ -125,8 +126,8 @@ codexs current
|
|
|
125
126
|
codexs status
|
|
126
127
|
codexs config show [profile]
|
|
127
128
|
codexs config list-profiles
|
|
128
|
-
codexs add <provider> --model <model> --api-key <key> [--base-url <url>]
|
|
129
|
-
codexs add <provider> --copilot --model <model>
|
|
129
|
+
codexs add <provider> --profile <model-provider-id> --model <model> --api-key <key> [--base-url <url>]
|
|
130
|
+
codexs add <provider> --copilot --profile <model-provider-id> --model <model>
|
|
130
131
|
codexs edit <provider>
|
|
131
132
|
codexs switch <provider>
|
|
132
133
|
codexs remove <provider> [--force] [--switch-to <provider>]
|
|
@@ -218,10 +219,10 @@ npm pack --dry-run
|
|
|
218
219
|
- [PRD 0.1.1](./docs/PRD/codex-switch-prd-v0.1.1.md)
|
|
219
220
|
- [PRD 0.1.2](./docs/PRD/codex-switch-prd-v0.1.2.md)
|
|
220
221
|
- [PRD 0.1.3](./docs/PRD/codex-switch-prd-v0.1.3.md)
|
|
221
|
-
- [PRD 0.1.
|
|
222
|
+
- [PRD 0.1.5](./docs/PRD/codex-switch-prd-v0.1.5.md)
|
|
222
223
|
- [Design 0.1.2](./docs/Design/codex-switch-v0.1.2-design.md)
|
|
223
224
|
- [Design 0.1.3](./docs/Design/codex-switch-v0.1.3-design.md)
|
|
224
|
-
- [Design 0.1.
|
|
225
|
+
- [Design 0.1.5](./docs/Design/codex-switch-v0.1.5-design.md)
|
|
225
226
|
|
|
226
227
|
## License
|
|
227
228
|
|
|
@@ -174,6 +174,7 @@ async function createCopilotSession(runtimeClient, payload) {
|
|
|
174
174
|
try {
|
|
175
175
|
const session = await Promise.resolve(createSession({
|
|
176
176
|
model: typeof payload.model === "string" ? payload.model : undefined,
|
|
177
|
+
streaming: true,
|
|
177
178
|
...createSessionOptions(runtimeClient.sdk),
|
|
178
179
|
}));
|
|
179
180
|
if (!session || typeof session !== "object") {
|
|
@@ -208,7 +209,16 @@ async function sendSessionRequest(session, prompt, timeoutMs, onStreamEvent) {
|
|
|
208
209
|
onStreamEvent?.({ type: "delta", delta });
|
|
209
210
|
}
|
|
210
211
|
};
|
|
212
|
+
const runtimeHandler = (event) => {
|
|
213
|
+
for (const runtimeEvent of mapCopilotRuntimeEvent(event)) {
|
|
214
|
+
onStreamEvent?.({ type: "runtime", event: runtimeEvent });
|
|
215
|
+
if (runtimeEvent.type === "assistant.message_delta" && runtimeEvent.text.length > 0) {
|
|
216
|
+
onStreamEvent?.({ type: "delta", delta: runtimeEvent.text });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
};
|
|
211
220
|
if (onStreamEvent && session.on) {
|
|
221
|
+
session.on("event", runtimeHandler);
|
|
212
222
|
session.on("data", deltaHandler);
|
|
213
223
|
session.on("message", deltaHandler);
|
|
214
224
|
session.on("delta", deltaHandler);
|
|
@@ -225,6 +235,7 @@ async function sendSessionRequest(session, prompt, timeoutMs, onStreamEvent) {
|
|
|
225
235
|
}
|
|
226
236
|
finally {
|
|
227
237
|
if (onStreamEvent && session.off) {
|
|
238
|
+
session.off("event", runtimeHandler);
|
|
228
239
|
session.off("data", deltaHandler);
|
|
229
240
|
session.off("message", deltaHandler);
|
|
230
241
|
session.off("delta", deltaHandler);
|
|
@@ -395,6 +406,115 @@ function extractDelta(event) {
|
|
|
395
406
|
}
|
|
396
407
|
return null;
|
|
397
408
|
}
|
|
409
|
+
/**
|
|
410
|
+
* Maps SDK session events into the bridge's stable process-event contract.
|
|
411
|
+
*/
|
|
412
|
+
function mapCopilotRuntimeEvent(event) {
|
|
413
|
+
if (!event || typeof event !== "object") {
|
|
414
|
+
return [];
|
|
415
|
+
}
|
|
416
|
+
const record = event;
|
|
417
|
+
const sdkType = readString(record, ["type", "event", "name", "eventName"]) ?? "unknown";
|
|
418
|
+
const normalizedType = sdkType.replace(/_/g, ".").toLowerCase();
|
|
419
|
+
const text = readString(record, ["text", "delta", "content", "message", "summary", "description"]);
|
|
420
|
+
const name = readString(record, ["toolName", "tool", "name"]);
|
|
421
|
+
const requestId = readString(record, ["requestId", "id", "callId"]);
|
|
422
|
+
const kind = readString(record, ["kind", "permission", "permissionKind"]);
|
|
423
|
+
const success = readBoolean(record, ["success", "ok"]);
|
|
424
|
+
const approved = readBoolean(record, ["approved", "allowed", "accepted"]);
|
|
425
|
+
const summary = truncateForBridgeLog(text ?? summarizeUnknownObject(record), 600);
|
|
426
|
+
if (normalizedType === "assistant.intent") {
|
|
427
|
+
return [{ type: "assistant.intent", text: summary }];
|
|
428
|
+
}
|
|
429
|
+
if (normalizedType === "assistant.message.delta" || normalizedType === "assistant.message_delta") {
|
|
430
|
+
return [{ type: "assistant.message_delta", text: text ?? "" }];
|
|
431
|
+
}
|
|
432
|
+
if (normalizedType === "assistant.reasoning.delta" || normalizedType === "assistant.reasoning_delta" || normalizedType === "reasoning.delta") {
|
|
433
|
+
return [{ type: "assistant.reasoning_delta", text: summary }];
|
|
434
|
+
}
|
|
435
|
+
if (normalizedType === "tool.execution.start" || normalizedType === "tool.execution_start") {
|
|
436
|
+
return [{ type: "tool.execution_start", name, requestId, summary: summary || `Tool started: ${name ?? "unknown"}` }];
|
|
437
|
+
}
|
|
438
|
+
if (normalizedType === "tool.execution.progress" || normalizedType === "tool.execution_progress") {
|
|
439
|
+
return [{ type: "tool.execution_progress", name, requestId, summary }];
|
|
440
|
+
}
|
|
441
|
+
if (normalizedType === "tool.execution.partial.result" || normalizedType === "tool.execution.partial_result") {
|
|
442
|
+
return [{ type: "tool.execution_partial_result", name, requestId, summary }];
|
|
443
|
+
}
|
|
444
|
+
if (normalizedType === "tool.execution.complete" || normalizedType === "tool.execution_complete") {
|
|
445
|
+
return [{ type: "tool.execution_complete", name, requestId, success, summary: summary || `Tool completed: ${name ?? "unknown"}` }];
|
|
446
|
+
}
|
|
447
|
+
if (normalizedType === "permission.requested" || normalizedType === "permission.request") {
|
|
448
|
+
return [{ type: "permission.requested", kind, requestId, summary: summary || `Copilot requested permission: ${kind ?? "unknown"}` }];
|
|
449
|
+
}
|
|
450
|
+
if (normalizedType === "permission.completed" || normalizedType === "permission.complete") {
|
|
451
|
+
return [{ type: "permission.completed", kind, requestId, approved, summary }];
|
|
452
|
+
}
|
|
453
|
+
if (normalizedType === "user.input.requested" || normalizedType === "user.input_request" || normalizedType === "user_input.requested") {
|
|
454
|
+
return [{ type: "user_input.requested", requestId, summary }];
|
|
455
|
+
}
|
|
456
|
+
if (normalizedType === "exit.plan.mode.requested" || normalizedType === "exit_plan_mode.requested") {
|
|
457
|
+
return [{ type: "exit_plan_mode.requested", requestId, summary }];
|
|
458
|
+
}
|
|
459
|
+
if (normalizedType === "session.error" || normalizedType === "error") {
|
|
460
|
+
return [{ type: "session.error", summary }];
|
|
461
|
+
}
|
|
462
|
+
if (normalizedType === "session.idle" || normalizedType === "idle") {
|
|
463
|
+
return [{ type: "session.idle", summary: summary || "Copilot session is idle." }];
|
|
464
|
+
}
|
|
465
|
+
return [{ type: "session.unknown", sdkType, summary }];
|
|
466
|
+
}
|
|
467
|
+
function readString(record, keys) {
|
|
468
|
+
for (const key of keys) {
|
|
469
|
+
const value = record[key];
|
|
470
|
+
if (typeof value === "string" && value.length > 0) {
|
|
471
|
+
return value;
|
|
472
|
+
}
|
|
473
|
+
if (value && typeof value === "object") {
|
|
474
|
+
const nested = value;
|
|
475
|
+
for (const nestedKey of ["name", "id", "text", "content", "message", "summary"]) {
|
|
476
|
+
if (typeof nested[nestedKey] === "string" && nested[nestedKey].length > 0) {
|
|
477
|
+
return nested[nestedKey];
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
return undefined;
|
|
483
|
+
}
|
|
484
|
+
function readBoolean(record, keys) {
|
|
485
|
+
for (const key of keys) {
|
|
486
|
+
if (typeof record[key] === "boolean") {
|
|
487
|
+
return record[key];
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return undefined;
|
|
491
|
+
}
|
|
492
|
+
function summarizeUnknownObject(record) {
|
|
493
|
+
return JSON.stringify(record, (key, value) => {
|
|
494
|
+
if (isSensitiveKey(key)) {
|
|
495
|
+
return "[redacted]";
|
|
496
|
+
}
|
|
497
|
+
if (typeof value === "string") {
|
|
498
|
+
return redactSensitiveText(value);
|
|
499
|
+
}
|
|
500
|
+
return value;
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
function redactSensitiveText(value) {
|
|
504
|
+
if (/api[_-]?key|token|authorization|bearer\s+|sk-[a-z0-9_-]+/i.test(value)) {
|
|
505
|
+
return "[redacted]";
|
|
506
|
+
}
|
|
507
|
+
return value;
|
|
508
|
+
}
|
|
509
|
+
function isSensitiveKey(key) {
|
|
510
|
+
return /^(api[_-]?key|token|access[_-]?token|refresh[_-]?token|authorization|secret|password)$/i.test(key);
|
|
511
|
+
}
|
|
512
|
+
function truncateForBridgeLog(value, maxLength) {
|
|
513
|
+
if (value.length <= maxLength) {
|
|
514
|
+
return value;
|
|
515
|
+
}
|
|
516
|
+
return `${value.slice(0, maxLength)}... [truncated]`;
|
|
517
|
+
}
|
|
398
518
|
function isAuthReady(status) {
|
|
399
519
|
if (status === true) {
|
|
400
520
|
return true;
|
|
@@ -403,6 +403,9 @@ function createCopilotBridgeRequestHandler(context) {
|
|
|
403
403
|
writeResponsesTextDelta(response, messageId, doneText);
|
|
404
404
|
}
|
|
405
405
|
},
|
|
406
|
+
onRuntimeEvent: (event) => {
|
|
407
|
+
writeResponsesRuntimeEvent(response, responseId, event);
|
|
408
|
+
},
|
|
406
409
|
});
|
|
407
410
|
clearInterval(heartbeat);
|
|
408
411
|
const outputText = text || getChatCompletionText(payload);
|
|
@@ -790,6 +793,112 @@ function writeResponsesStreamDone(response, responseId, model, messageId, output
|
|
|
790
793
|
},
|
|
791
794
|
});
|
|
792
795
|
}
|
|
796
|
+
function writeResponsesRuntimeEvent(response, responseId, event) {
|
|
797
|
+
if (event.type === "assistant.message_delta") {
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
if (event.type === "assistant.reasoning_delta") {
|
|
801
|
+
writeResponsesReasoningDelta(response, responseId, formatRuntimeEventText(event));
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
if (event.type === "session.unknown") {
|
|
805
|
+
process.stderr.write(`[${new Date().toISOString()}] bridge runtime event ignored type=${event.sdkType} summary=${truncateBridgeText(event.summary, 240)}\n`);
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
const text = formatRuntimeEventText(event);
|
|
809
|
+
if (text.length === 0) {
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
writeResponsesCommentaryItem(response, responseId, text);
|
|
813
|
+
process.stderr.write(`[${new Date().toISOString()}] bridge runtime event type=${event.type} summary=${truncateBridgeText(text, 240)}\n`);
|
|
814
|
+
}
|
|
815
|
+
function writeResponsesReasoningDelta(response, responseId, text) {
|
|
816
|
+
const reasoningId = `${responseId}_rs_0`;
|
|
817
|
+
writeSseEvent(response, "response.reasoning_summary_part.added", {
|
|
818
|
+
type: "response.reasoning_summary_part.added",
|
|
819
|
+
item_id: reasoningId,
|
|
820
|
+
output_index: 0,
|
|
821
|
+
summary_index: 0,
|
|
822
|
+
part: {
|
|
823
|
+
type: "summary_text",
|
|
824
|
+
text: "",
|
|
825
|
+
},
|
|
826
|
+
});
|
|
827
|
+
writeSseEvent(response, "response.reasoning_summary_text.delta", {
|
|
828
|
+
type: "response.reasoning_summary_text.delta",
|
|
829
|
+
item_id: reasoningId,
|
|
830
|
+
output_index: 0,
|
|
831
|
+
summary_index: 0,
|
|
832
|
+
delta: text,
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
function writeResponsesCommentaryItem(response, responseId, text) {
|
|
836
|
+
const itemId = `${responseId}_commentary_${Date.now()}_${Math.floor(Math.random() * 100000)}`;
|
|
837
|
+
writeSseEvent(response, "response.output_item.done", {
|
|
838
|
+
type: "response.output_item.done",
|
|
839
|
+
output_index: 0,
|
|
840
|
+
item: {
|
|
841
|
+
id: itemId,
|
|
842
|
+
type: "message",
|
|
843
|
+
status: "completed",
|
|
844
|
+
role: "assistant",
|
|
845
|
+
phase: "commentary",
|
|
846
|
+
content: [
|
|
847
|
+
{
|
|
848
|
+
type: "output_text",
|
|
849
|
+
text,
|
|
850
|
+
annotations: [],
|
|
851
|
+
},
|
|
852
|
+
],
|
|
853
|
+
},
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
function formatRuntimeEventText(event) {
|
|
857
|
+
switch (event.type) {
|
|
858
|
+
case "assistant.intent":
|
|
859
|
+
return truncateBridgeText(event.text, 600);
|
|
860
|
+
case "assistant.reasoning_delta":
|
|
861
|
+
return truncateBridgeText(event.text, 600);
|
|
862
|
+
case "tool.execution_start":
|
|
863
|
+
return truncateBridgeText(`Copilot started ${formatToolName(event.name)}${formatRequestId(event.requestId)}${formatSummarySuffix(event.summary)}`, 600);
|
|
864
|
+
case "tool.execution_progress":
|
|
865
|
+
return truncateBridgeText(`Copilot progress ${formatToolName(event.name)}${formatRequestId(event.requestId)}${formatSummarySuffix(event.summary)}`, 600);
|
|
866
|
+
case "tool.execution_partial_result":
|
|
867
|
+
return truncateBridgeText(`Copilot partial result ${formatToolName(event.name)}${formatRequestId(event.requestId)}${formatSummarySuffix(event.summary)}`, 600);
|
|
868
|
+
case "tool.execution_complete":
|
|
869
|
+
return truncateBridgeText(`Copilot ${event.success === false ? "failed" : "completed"} ${formatToolName(event.name)}${formatRequestId(event.requestId)}${formatSummarySuffix(event.summary)}`, 600);
|
|
870
|
+
case "permission.requested":
|
|
871
|
+
return truncateBridgeText(`Copilot requested permission${event.kind ? `: ${event.kind}` : ""}${formatRequestId(event.requestId)}${formatSummarySuffix(event.summary)}`, 600);
|
|
872
|
+
case "permission.completed":
|
|
873
|
+
return truncateBridgeText(`Copilot permission ${event.approved === false ? "denied" : "approved"}${event.kind ? `: ${event.kind}` : ""}${formatRequestId(event.requestId)}${formatSummarySuffix(event.summary)}`, 600);
|
|
874
|
+
case "user_input.requested":
|
|
875
|
+
return truncateBridgeText(`Copilot requested user input${formatRequestId(event.requestId)}${formatSummarySuffix(event.summary)}`, 600);
|
|
876
|
+
case "exit_plan_mode.requested":
|
|
877
|
+
return truncateBridgeText(`Copilot requested to exit plan mode${formatRequestId(event.requestId)}${formatSummarySuffix(event.summary)}`, 600);
|
|
878
|
+
case "session.error":
|
|
879
|
+
return truncateBridgeText(`Copilot session error${formatSummarySuffix(event.summary)}`, 600);
|
|
880
|
+
case "session.idle":
|
|
881
|
+
return truncateBridgeText(event.summary, 600);
|
|
882
|
+
case "assistant.message_delta":
|
|
883
|
+
case "session.unknown":
|
|
884
|
+
return "";
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
function formatToolName(name) {
|
|
888
|
+
return name ? `tool ${name}` : "tool";
|
|
889
|
+
}
|
|
890
|
+
function formatRequestId(requestId) {
|
|
891
|
+
return requestId ? ` (${requestId})` : "";
|
|
892
|
+
}
|
|
893
|
+
function formatSummarySuffix(summary) {
|
|
894
|
+
return summary.length > 0 ? `: ${summary}` : "";
|
|
895
|
+
}
|
|
896
|
+
function truncateBridgeText(value, maxLength) {
|
|
897
|
+
if (value.length <= maxLength) {
|
|
898
|
+
return value;
|
|
899
|
+
}
|
|
900
|
+
return `${value.slice(0, maxLength)}... [truncated]`;
|
|
901
|
+
}
|
|
793
902
|
function startSseHeartbeat(response) {
|
|
794
903
|
return setInterval(() => {
|
|
795
904
|
response.write(": keep-alive\n\n");
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# codex-switch v0.1.5 Design
|
|
2
|
+
|
|
3
|
+
`0.1.5` is a Copilot Bridge process-visibility and redaction patch release.
|
|
4
|
+
|
|
5
|
+
## Design Notes
|
|
6
|
+
|
|
7
|
+
- `CopilotBridgeRuntimeEvent` is the stable internal boundary between Copilot SDK session events and the OpenAI-compatible bridge surface.
|
|
8
|
+
- Copilot sessions are created with `streaming: true` and register the generic `session.on("event", handler)` listener alongside the existing `data`, `message`, and `delta` text compatibility listeners.
|
|
9
|
+
- The adapter maps known SDK event names into bridge runtime events for assistant intent, assistant message deltas, assistant reasoning deltas, tool lifecycle, permission lifecycle, user-input requests, exit-plan-mode requests, session idle, and session errors.
|
|
10
|
+
- `assistant.message_delta` is also forwarded as a normal text delta so final response streaming remains compatible with existing Chat Completions and Responses text paths.
|
|
11
|
+
- The bridge worker forwards adapter runtime events through `onRuntimeEvent`, separate from `onTextDelta` and `onTextDone`.
|
|
12
|
+
- Responses streaming projects non-text runtime process events as completed assistant message items with `phase: "commentary"`.
|
|
13
|
+
- Reasoning deltas project as `response.reasoning_summary_part.added` followed by `response.reasoning_summary_text.delta`.
|
|
14
|
+
- Unknown SDK events become `session.unknown`, are omitted from the UI projection path, and are logged with bounded summaries.
|
|
15
|
+
- Unknown summaries redact sensitive key names and obvious token/API-key-like values before truncation.
|
|
16
|
+
- Chat Completions streaming only wires text deltas, so Responses-only commentary events stay out of Chat Completions streams.
|
|
17
|
+
- Internal Copilot tool lifecycle events remain process visibility signals only; they are not emitted as Codex/OpenAI function calls or tool calls.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# codex-switch v0.1.5 PRD
|
|
2
|
+
|
|
3
|
+
## Version
|
|
4
|
+
|
|
5
|
+
- Version line: `0.1.5`
|
|
6
|
+
- Target repository package version: `0.1.5`
|
|
7
|
+
|
|
8
|
+
## Summary
|
|
9
|
+
|
|
10
|
+
`0.1.5` is a Copilot Bridge process-visibility and redaction patch release. It surfaces assistant progress, reasoning summaries, tool lifecycle, permission, user-input, and exit-plan-mode signals through the existing bridge stream while keeping the provider command surface unchanged.
|
|
11
|
+
|
|
12
|
+
## Required Outcome
|
|
13
|
+
|
|
14
|
+
- Copilot SDK streaming sessions must subscribe to the generic session event channel and preserve existing text-delta compatibility listeners.
|
|
15
|
+
- Known SDK process events must map into a stable bridge runtime-event contract instead of leaking SDK-specific shapes.
|
|
16
|
+
- Responses streaming must surface process/status updates as commentary items and reasoning/progress updates as reasoning summary events.
|
|
17
|
+
- Chat Completions streaming must continue to receive text only and must not receive Responses-only commentary events.
|
|
18
|
+
- Unknown SDK events must be ignored by the UI projection path while logging bounded, redacted summaries for diagnostics.
|
|
19
|
+
- Adapter-level tests must cover raw SDK session event normalization, not only bridge-server projection of already-normalized events.
|
|
20
|
+
|
|
21
|
+
## Release Scope
|
|
22
|
+
|
|
23
|
+
- Copilot adapter runtime event contract and SDK event normalization.
|
|
24
|
+
- Bridge worker forwarding of adapter runtime events into request handlers.
|
|
25
|
+
- Responses streaming projection for commentary and reasoning summary events.
|
|
26
|
+
- Unknown-event truncation and redaction hardening.
|
|
27
|
+
- Focused regression coverage for raw SDK event mapping and bridge stream projection.
|
|
28
|
+
|
|
29
|
+
## Non-Goals
|
|
30
|
+
|
|
31
|
+
- No new provider families.
|
|
32
|
+
- No migration or backward-compatibility shims.
|
|
33
|
+
- No expansion of direct-provider workflows.
|
|
34
|
+
- No conversion of internal Copilot tool lifecycle events into Codex/OpenAI function-call or tool-call payloads.
|
|
35
|
+
|
|
36
|
+
## Release Acceptance
|
|
37
|
+
|
|
38
|
+
- `npm test` passes with adapter-level raw event normalization coverage.
|
|
39
|
+
- Responses streaming includes commentary and reasoning summary process events.
|
|
40
|
+
- Final assistant text still streams normally.
|
|
41
|
+
- Unknown events are redacted, truncated, logged for diagnostics, and ignored by the UI projection path.
|
|
42
|
+
- Chat Completions streaming does not emit Responses-only commentary events.
|
package/docs/Tests/testing.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
This guide records the current `0.1.x` verification contract for release and review work.
|
|
4
4
|
|
|
5
|
-
The current repository line is `0.1.
|
|
5
|
+
The current repository line is `0.1.5` and remains an unreleased development line until an explicit release task says otherwise.
|
|
6
6
|
|
|
7
7
|
## Required checks
|
|
8
8
|
|
|
@@ -25,7 +25,7 @@ node dist/cli.js --version
|
|
|
25
25
|
- `--json` envelope: top-level `ok`, `command`, `data`, `warnings`, and `error` must remain stable
|
|
26
26
|
- `migrate`: advanced adopt helper only
|
|
27
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.
|
|
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
29
|
|
|
30
30
|
## Fixture guidance
|
|
31
31
|
|
package/docs/cli-usage.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# codex-switch CLI Usage
|
|
2
2
|
|
|
3
|
-
This document describes the current `0.1.
|
|
3
|
+
This document describes the current `0.1.5` repository development-line CLI contract for `@minniexcode/codex-switch`, including the Copilot Bridge process-visibility boundary.
|
|
4
4
|
|
|
5
5
|
Executable command name:
|
|
6
6
|
|
|
@@ -10,9 +10,9 @@ codexs
|
|
|
10
10
|
|
|
11
11
|
## 1. Version Context
|
|
12
12
|
|
|
13
|
-
The current package version in this repository is `0.1.
|
|
13
|
+
The current package version in this repository is `0.1.5`.
|
|
14
14
|
|
|
15
|
-
This release line targets Codex `0.134.0+`. The public contract assumes runtime routing is selected by top-level `model` plus `model_provider`, while legacy `profile` and `[profiles.*]` remain inspect-and-adopt inputs instead of the recommended runtime path. The current `0.1.
|
|
15
|
+
This release line targets Codex `0.134.0+`. The public contract assumes runtime routing is selected by top-level `model` plus `model_provider`, while legacy `profile` and `[profiles.*]` remain inspect-and-adopt inputs instead of the recommended runtime path. The current `0.1.5` development line adds Copilot Bridge process visibility, defensive SDK-event normalization, and unknown-event redaction hardening without expanding the provider command surface.
|
|
16
16
|
|
|
17
17
|
## 2. Primary Workflows
|
|
18
18
|
|
|
@@ -20,7 +20,7 @@ This release line targets Codex `0.134.0+`. The public contract assumes runtime
|
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
22
|
codexs init
|
|
23
|
-
codexs add <provider> --model <model> --api-key <key> [--base-url <url>]
|
|
23
|
+
codexs add <provider> --profile <model-provider-id> --model <model> --api-key <key> [--base-url <url>]
|
|
24
24
|
codexs switch <provider>
|
|
25
25
|
codexs status
|
|
26
26
|
codexs doctor
|
|
@@ -30,6 +30,7 @@ Intent:
|
|
|
30
30
|
|
|
31
31
|
- `init` prepares the `codex-switch` tool home.
|
|
32
32
|
- `add` creates a managed provider record with a target model and provider route identity.
|
|
33
|
+
- Non-interactive runs should pass `--profile` explicitly; TTY mode can prompt for missing required fields.
|
|
33
34
|
- `switch` projects the selected provider into the target Codex runtime.
|
|
34
35
|
- `status` summarizes tool-home, runtime, provider, and health state.
|
|
35
36
|
- `doctor` gives deeper repair-oriented diagnostics.
|
|
@@ -39,7 +40,7 @@ Intent:
|
|
|
39
40
|
```bash
|
|
40
41
|
codexs init
|
|
41
42
|
codexs login copilot
|
|
42
|
-
codexs add <provider> --copilot --model <model>
|
|
43
|
+
codexs add <provider> --copilot --profile <model-provider-id> --model <model>
|
|
43
44
|
codexs switch <provider>
|
|
44
45
|
codexs status
|
|
45
46
|
codexs doctor
|
|
@@ -51,6 +52,7 @@ Important notes:
|
|
|
51
52
|
- The current implementation prefers the bundled Copilot CLI from the managed runtime and falls back to `PATH` when needed.
|
|
52
53
|
- `login copilot` succeeds only after auth readiness is rechecked.
|
|
53
54
|
- `add --copilot` does not install or log in to Copilot for you.
|
|
55
|
+
- Non-interactive Copilot adds should also pass `--profile` explicitly; TTY mode can still collect missing required fields.
|
|
54
56
|
- Copilot runtime paths require Node.js `>=20`; direct providers remain supported on Node.js `>=18`.
|
|
55
57
|
- The Copilot bridge is experimental and targets simple text-oriented turns through the local OpenAI-compatible bridge.
|
|
56
58
|
|
|
@@ -82,6 +84,7 @@ Copilot bridge projection also writes:
|
|
|
82
84
|
Compatibility notes:
|
|
83
85
|
|
|
84
86
|
- `--profile` is accepted as an alias for the managed `model_provider` id.
|
|
87
|
+
- automation should still pass `--profile` explicitly instead of relying on prompts
|
|
85
88
|
- legacy top-level `profile` and `[profiles.*]` may still appear in existing runtime state
|
|
86
89
|
- `migrate`, `config show`, `config list-profiles`, and `doctor` can still inspect those legacy structures
|
|
87
90
|
- new managed runtime projection should be described as route-first, not profile-first
|
|
@@ -262,6 +265,7 @@ codexs rollback [backup-id]
|
|
|
262
265
|
|
|
263
266
|
- Create or update managed provider records rather than editing runtime files directly.
|
|
264
267
|
- Treat `--profile` only as an alias for the managed `model_provider` id.
|
|
268
|
+
- Require explicit `--profile` in non-interactive usage; allow TTY prompts to fill it when omitted.
|
|
265
269
|
- Clean old `env_key` and `env_key_instructions` fields from managed projection during subsequent switching.
|
|
266
270
|
|
|
267
271
|
### `migrate`
|
|
@@ -288,7 +292,7 @@ codexs rollback [backup-id]
|
|
|
288
292
|
- [PRD 0.1.1](./PRD/codex-switch-prd-v0.1.1.md)
|
|
289
293
|
- [PRD 0.1.2](./PRD/codex-switch-prd-v0.1.2.md)
|
|
290
294
|
- [PRD 0.1.3](./PRD/codex-switch-prd-v0.1.3.md)
|
|
291
|
-
- [PRD 0.1.
|
|
295
|
+
- [PRD 0.1.5](./PRD/codex-switch-prd-v0.1.5.md)
|
|
292
296
|
- [Design 0.1.2](./Design/codex-switch-v0.1.2-design.md)
|
|
293
297
|
- [Design 0.1.3](./Design/codex-switch-v0.1.3-design.md)
|
|
294
|
-
- [Design 0.1.
|
|
298
|
+
- [Design 0.1.5](./Design/codex-switch-v0.1.5-design.md)
|
|
@@ -4,21 +4,21 @@
|
|
|
4
4
|
|
|
5
5
|
这份文档介绍当前活跃产品事实源下的 `codex-switch` 产品定位。
|
|
6
6
|
|
|
7
|
-
当前仓库开发线 fact source 以这些文档为准:
|
|
8
|
-
|
|
9
|
-
- [`cli-usage.md`](./cli-usage.md)
|
|
10
|
-
- [`PRD/codex-switch-prd-v0.1.0.md`](./PRD/codex-switch-prd-v0.1.0.md)
|
|
11
|
-
- [`PRD/codex-switch-prd-v0.1.1.md`](./PRD/codex-switch-prd-v0.1.1.md)
|
|
12
|
-
- [`PRD/codex-switch-prd-v0.1.2.md`](./PRD/codex-switch-prd-v0.1.2.md)
|
|
13
|
-
- [`PRD/codex-switch-prd-v0.1.3.md`](./PRD/codex-switch-prd-v0.1.3.md)
|
|
14
|
-
- [`PRD/codex-switch-prd-v0.1.
|
|
15
|
-
- [`Design/codex-switch-v0.1.2-design.md`](./Design/codex-switch-v0.1.2-design.md)
|
|
16
|
-
- [`Design/codex-switch-v0.1.3-design.md`](./Design/codex-switch-v0.1.3-design.md)
|
|
17
|
-
- [`Design/codex-switch-v0.1.
|
|
7
|
+
当前仓库开发线 fact source 以这些文档为准:
|
|
8
|
+
|
|
9
|
+
- [`cli-usage.md`](./cli-usage.md)
|
|
10
|
+
- [`PRD/codex-switch-prd-v0.1.0.md`](./PRD/codex-switch-prd-v0.1.0.md)
|
|
11
|
+
- [`PRD/codex-switch-prd-v0.1.1.md`](./PRD/codex-switch-prd-v0.1.1.md)
|
|
12
|
+
- [`PRD/codex-switch-prd-v0.1.2.md`](./PRD/codex-switch-prd-v0.1.2.md)
|
|
13
|
+
- [`PRD/codex-switch-prd-v0.1.3.md`](./PRD/codex-switch-prd-v0.1.3.md)
|
|
14
|
+
- [`PRD/codex-switch-prd-v0.1.5.md`](./PRD/codex-switch-prd-v0.1.5.md)
|
|
15
|
+
- [`Design/codex-switch-v0.1.2-design.md`](./Design/codex-switch-v0.1.2-design.md)
|
|
16
|
+
- [`Design/codex-switch-v0.1.3-design.md`](./Design/codex-switch-v0.1.3-design.md)
|
|
17
|
+
- [`Design/codex-switch-v0.1.5-design.md`](./Design/codex-switch-v0.1.5-design.md)
|
|
18
18
|
|
|
19
19
|
## 产品概述
|
|
20
20
|
|
|
21
|
-
`codex-switch` 是一个本地 provider 管理 CLI,用于管理和切换目标 Codex runtime 的 provider/model-provider 路由配置,同时把工具自己的管理态保存在独立 tool home 下。
|
|
21
|
+
`codex-switch` 是一个本地 provider 管理 CLI,用于管理和切换目标 Codex runtime 的 provider/model-provider 路由配置,同时把工具自己的管理态保存在独立 tool home 下。
|
|
22
22
|
|
|
23
23
|
它不是旧 `setup` 小工具,也不是围绕单目录 `~/.codex` 组织全部状态的脚本集合。
|
|
24
24
|
|
|
@@ -54,11 +54,11 @@ target Codex runtime:
|
|
|
54
54
|
|
|
55
55
|
## 核心使用流程
|
|
56
56
|
|
|
57
|
-
Direct 主路径:
|
|
57
|
+
Direct 主路径:
|
|
58
58
|
|
|
59
59
|
```bash
|
|
60
60
|
codexs init
|
|
61
|
-
codexs add <provider> --model <model> --api-key <key> [--base-url <url>]
|
|
61
|
+
codexs add <provider> --profile <model-provider-id> --model <model> --api-key <key> [--base-url <url>]
|
|
62
62
|
codexs switch <provider>
|
|
63
63
|
codexs status
|
|
64
64
|
codexs doctor
|
|
@@ -69,7 +69,7 @@ Copilot 主路径:
|
|
|
69
69
|
```bash
|
|
70
70
|
codexs init
|
|
71
71
|
codexs login copilot
|
|
72
|
-
codexs add <provider> --copilot --model <model>
|
|
72
|
+
codexs add <provider> --copilot --profile <model-provider-id> --model <model>
|
|
73
73
|
codexs switch <provider>
|
|
74
74
|
codexs status
|
|
75
75
|
codexs doctor
|
|
@@ -84,12 +84,13 @@ codexs migrate
|
|
|
84
84
|
|
|
85
85
|
## 当前产品判断
|
|
86
86
|
|
|
87
|
-
`0.1.1` 的重点不是再加新命令,而是让用户在 README、help 和输出第一屏就能理解:
|
|
87
|
+
`0.1.1` 的重点不是再加新命令,而是让用户在 README、help 和输出第一屏就能理解:
|
|
88
88
|
|
|
89
|
-
- fresh install 应先走什么
|
|
90
|
-
- Copilot 路径和 direct 路径有什么区别
|
|
91
|
-
-
|
|
92
|
-
- `
|
|
93
|
-
-
|
|
94
|
-
|
|
95
|
-
|
|
89
|
+
- fresh install 应先走什么
|
|
90
|
+
- Copilot 路径和 direct 路径有什么区别
|
|
91
|
+
- 非交互命令为什么应显式传 `--profile`,以及 TTY 模式下哪些缺失项可以补问
|
|
92
|
+
- `migrate` 何时才该使用
|
|
93
|
+
- `status` / `doctor` 如何帮助定位下一步
|
|
94
|
+
- 当前运行态是用顶层 `model` 与 `model_provider` 选择活动路由
|
|
95
|
+
|
|
96
|
+
`0.1.5` 是当前仓库开发线,重点不是扩展 provider 面,而是让已有 Copilot bridge 实验路径具备过程可见性:SDK 事件被归一化为稳定运行态事件,Responses 流可以显示 commentary/reasoning 信号,未知事件摘要会被脱敏和截断。当前实现边界仍然是:Copilot 路径要求 Node.js `>=20`,受管安装默认固定到 `@github/copilot-sdk@1.0.2`,本地 bridge 仍然只是面向 simple text-oriented turns 的 experimental bridge;Direct provider 路径继续支持 Node.js `>=18`。
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
- [`PRD/codex-switch-prd-v0.1.1.md`](./PRD/codex-switch-prd-v0.1.1.md)
|
|
8
8
|
- [`PRD/codex-switch-prd-v0.1.2.md`](./PRD/codex-switch-prd-v0.1.2.md)
|
|
9
9
|
- [`PRD/codex-switch-prd-v0.1.3.md`](./PRD/codex-switch-prd-v0.1.3.md)
|
|
10
|
-
- [`PRD/codex-switch-prd-v0.1.
|
|
10
|
+
- [`PRD/codex-switch-prd-v0.1.5.md`](./PRD/codex-switch-prd-v0.1.5.md)
|
|
11
11
|
- [`Design/codex-switch-v0.1.0-design.md`](./Design/codex-switch-v0.1.0-design.md)
|
|
12
12
|
- [`Design/codex-switch-v0.1.1-design.md`](./Design/codex-switch-v0.1.1-design.md)
|
|
13
13
|
- [`Design/codex-switch-v0.1.2-design.md`](./Design/codex-switch-v0.1.2-design.md)
|
|
14
14
|
- [`Design/codex-switch-v0.1.3-design.md`](./Design/codex-switch-v0.1.3-design.md)
|
|
15
|
-
- [`Design/codex-switch-v0.1.
|
|
15
|
+
- [`Design/codex-switch-v0.1.5-design.md`](./Design/codex-switch-v0.1.5-design.md)
|
|
16
16
|
|
|
17
17
|
## Layers
|
|
18
18
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@minniexcode/codex-switch",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Local-first CLI for managing and switching Codex provider/model-provider routing.",
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"description": "Local-first CLI for managing and switching Codex provider/model-provider routing.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|
|
7
7
|
"bin": {
|