switchboard-fyi 0.1.0
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/LICENSE +21 -0
- package/README.md +538 -0
- package/bin/switchboard-gateway.mjs +5543 -0
- package/bin/switchboard-inspector.mjs +814 -0
- package/bin/switchboard.mjs +6936 -0
- package/docs/codex-subscription-provider-proxy.md +133 -0
- package/docs/known-limitations.md +69 -0
- package/docs/mvp-usage.md +207 -0
- package/docs/routing-api.md +197 -0
- package/docs/smoke-test.md +190 -0
- package/lib/switchboard-core.mjs +779 -0
- package/package.json +50 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# Switchboard Gateway Smoke Test
|
|
2
|
+
|
|
3
|
+
This smoke test checks whether Claude Code and Codex can send model requests
|
|
4
|
+
through a local Switchboard gateway while preserving their existing
|
|
5
|
+
authentication path.
|
|
6
|
+
|
|
7
|
+
The wrapper starts the gateway in forwarding mode for normal use. Standalone
|
|
8
|
+
gateway smoke tests can run with or without `--forward`; forwarding mode proxies
|
|
9
|
+
requests upstream while preserving the existing auth path.
|
|
10
|
+
|
|
11
|
+
## Start the gateway
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm run gateway
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The event log is written to:
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
~/.switchboard/harnesses/<harness>/events.jsonl
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Forwarding test
|
|
24
|
+
|
|
25
|
+
Forwarding mode proxies the request upstream using the auth headers supplied by
|
|
26
|
+
Claude Code or Codex:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
node ./bin/switchboard-gateway.mjs start --port 8787 --forward
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Current result from local testing:
|
|
33
|
+
|
|
34
|
+
- Claude Code subscription OAuth successfully forwards to Anthropic through the gateway.
|
|
35
|
+
- Codex ChatGPT bearer auth reaches the gateway but is rejected by the public OpenAI Responses API with `401 Missing scopes: api.responses.write`.
|
|
36
|
+
|
|
37
|
+
That means Codex subscription auth must not be forwarded to
|
|
38
|
+
`https://api.openai.com/v1/responses`.
|
|
39
|
+
Switchboard v1 also rejects OpenAI API-key Codex auth locally before routing,
|
|
40
|
+
so API-billed requests do not spend Switchboard request credits or fall through
|
|
41
|
+
to Platform billing.
|
|
42
|
+
|
|
43
|
+
For Codex subscription routing, use the Codex-specific ChatGPT backend:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
node ./bin/switchboard-gateway.mjs start \
|
|
47
|
+
--port 8787 \
|
|
48
|
+
--forward \
|
|
49
|
+
--codex-chatgpt-upstream
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
This maps local Codex `/v1/responses` traffic to:
|
|
53
|
+
|
|
54
|
+
```text
|
|
55
|
+
https://chatgpt.com/backend-api/codex/responses
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Codex subscription provider test
|
|
59
|
+
|
|
60
|
+
Run this in another terminal while the gateway is running:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
codex exec \
|
|
64
|
+
-c 'model_provider="switchboard"' \
|
|
65
|
+
-c 'model="gpt-5.5"' \
|
|
66
|
+
-c 'model_providers.switchboard.name="Switchboard"' \
|
|
67
|
+
-c 'model_providers.switchboard.base_url="http://127.0.0.1:8787/v1"' \
|
|
68
|
+
-c 'model_providers.switchboard.wire_api="responses"' \
|
|
69
|
+
-c 'model_providers.switchboard.requires_openai_auth=true' \
|
|
70
|
+
"Reply with one short sentence."
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Then inspect the latest event:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
tail -n 5 ~/.switchboard/harnesses/codex/events.jsonl
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
What matters:
|
|
80
|
+
|
|
81
|
+
- `harness` should be `codex`
|
|
82
|
+
- `auth.authorization.present` should be `true`
|
|
83
|
+
- `auth.apiKey.present` should usually be `false` if Codex is using ChatGPT sign-in
|
|
84
|
+
- `requestedModel` should be `gpt-5.5`, unless you intentionally override it
|
|
85
|
+
- `forward_attempt.path` should be `/backend-api/codex/responses` when
|
|
86
|
+
`--codex-chatgpt-upstream` is enabled
|
|
87
|
+
- `forward_result.status` should be `200`
|
|
88
|
+
- `upstream_response.ok` should be `true`; when the stream exposes it, this
|
|
89
|
+
event also includes response ids, usage, event counts, and an output preview
|
|
90
|
+
|
|
91
|
+
## Codex per-internal-call proof
|
|
92
|
+
|
|
93
|
+
Use a task that forces tool use and a follow-up model call:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
tmpdir=$(mktemp -d)
|
|
97
|
+
cat > "$tmpdir/math.mjs" <<'EOF'
|
|
98
|
+
export function add(a, b) {
|
|
99
|
+
return a - b;
|
|
100
|
+
}
|
|
101
|
+
EOF
|
|
102
|
+
cat > "$tmpdir/math.test.mjs" <<'EOF'
|
|
103
|
+
import assert from "node:assert/strict";
|
|
104
|
+
import { add } from "./math.mjs";
|
|
105
|
+
assert.equal(add(2, 3), 5);
|
|
106
|
+
console.log("ok");
|
|
107
|
+
EOF
|
|
108
|
+
|
|
109
|
+
codex exec \
|
|
110
|
+
--skip-git-repo-check \
|
|
111
|
+
--dangerously-bypass-approvals-and-sandbox \
|
|
112
|
+
-C "$tmpdir" \
|
|
113
|
+
-c 'model_provider="switchboard"' \
|
|
114
|
+
-c 'model="gpt-5.5"' \
|
|
115
|
+
-c 'model_providers.switchboard.name="Switchboard"' \
|
|
116
|
+
-c 'model_providers.switchboard.base_url="http://127.0.0.1:8787/v1"' \
|
|
117
|
+
-c 'model_providers.switchboard.wire_api="responses"' \
|
|
118
|
+
-c 'model_providers.switchboard.requires_openai_auth=true' \
|
|
119
|
+
-c 'model_providers.switchboard.request_max_retries=0' \
|
|
120
|
+
-c 'model_providers.switchboard.stream_max_retries=0' \
|
|
121
|
+
"Run the test in this directory, fix the bug, run the test again, and then reply with exactly: fixed-subscription-probe"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Local result on 2026-05-18:
|
|
125
|
+
|
|
126
|
+
- One user prompt produced 6 `model_request` events for Codex.
|
|
127
|
+
- All 6 forwarded to `/backend-api/codex/responses`.
|
|
128
|
+
- All 6 returned `status: 200`.
|
|
129
|
+
- A forced forwarded model change from `gpt-5.5` to `gpt-5.4-mini` also returned
|
|
130
|
+
`status: 200`.
|
|
131
|
+
|
|
132
|
+
## Routing API proof
|
|
133
|
+
|
|
134
|
+
Run Codex through the wrapper with the Switchboard API classifier enabled:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
codex \
|
|
138
|
+
--port 8916 \
|
|
139
|
+
exec --skip-git-repo-check --dangerously-bypass-approvals-and-sandbox \
|
|
140
|
+
"Reply with exactly: full-router-ok"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Then inspect:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
tail -n 20 ~/.switchboard/harnesses/codex/events.jsonl
|
|
147
|
+
switchboard dashboard codex
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
What matters:
|
|
151
|
+
|
|
152
|
+
- `classification_api_payload` contains the compact packet sent to the Switchboard API.
|
|
153
|
+
- `classification_api_result` contains the API route, reason code, and dashboard
|
|
154
|
+
reason mapped by the API.
|
|
155
|
+
- `route_applied.route.source` should be `switchboard`.
|
|
156
|
+
- `route_applied.route.targetTier` should be `lower`, `mid`, or `best`.
|
|
157
|
+
- `forward_attempt.path` should still be `/backend-api/codex/responses` for
|
|
158
|
+
Codex subscription routing.
|
|
159
|
+
- `forward_result.status` should be `200`.
|
|
160
|
+
- `upstream_response.ok` should be `true` and should carry the same
|
|
161
|
+
`decisionId` as the route, so the inspector can tie the result back to the
|
|
162
|
+
model choice.
|
|
163
|
+
|
|
164
|
+
## Claude subscription-auth test
|
|
165
|
+
|
|
166
|
+
Run this in another terminal while the gateway is running:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
ANTHROPIC_BASE_URL=http://127.0.0.1:8787 claude -p "Reply with one short sentence." --model sonnet
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Then inspect the latest event:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
tail -n 5 ~/.switchboard/harnesses/claude/events.jsonl
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
What matters:
|
|
179
|
+
|
|
180
|
+
- `harness` should be `claude`
|
|
181
|
+
- `auth.authorization.present` should be `true` for subscription OAuth
|
|
182
|
+
- `auth.apiKey.present` should be `false` unless `ANTHROPIC_API_KEY` is set
|
|
183
|
+
- `requestedModel` should be the Claude model/alias sent by Claude Code
|
|
184
|
+
|
|
185
|
+
## Safety notes
|
|
186
|
+
|
|
187
|
+
The log stores only redacted auth fingerprints, not token values.
|
|
188
|
+
|
|
189
|
+
If either tool sends an API key instead of subscription auth, unset the relevant
|
|
190
|
+
API key environment variable and retry.
|