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.
@@ -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.