@nofendian17/opencode-relay-plugin 2.0.6 → 2.0.7

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 (3) hide show
  1. package/README.md +1 -1
  2. package/index.ts +12 -10
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -16,7 +16,7 @@ gateways and are left untouched (no rewrite, no forced retries).
16
16
  V1 patched `provider.options.fetch` + `globalThis.fetch`. V2 providers no longer expose `fetch`,
17
17
  so this plugin uses the documented v2 interception points:
18
18
 
19
- - `ctx.session.hook("http.request")` — rewrite provider HTTP → `POST <relay>` with
19
+ - `ctx.session.hook("http.request")` — rewrite provider HTTP → `<relay>` (original method/body/headers preserved) with
20
20
  `x-relay-target: <origin>` + `x-relay-path: <path+query>` (round-robin)
21
21
  - `ctx.session.hook("http.response")` — on relay `429 / 5xx`, try remaining relays, then direct
22
22
  - `ctx.session.hook("retry")` — relay network errors never reach `http.response`, so force a
package/index.ts CHANGED
@@ -50,15 +50,6 @@ function normalizeRelays(raw: string | string[] | undefined): string[] {
50
50
  return [...seen]
51
51
  }
52
52
 
53
- function createRoundRobin(count: number): () => number {
54
- let idx = 0
55
- return () => {
56
- const cur = idx
57
- idx = (idx + 1) % count
58
- return cur
59
- }
60
- }
61
-
62
53
  function buildRelayOrigins(relays: readonly string[]): ReadonlySet<string> {
63
54
  const origins = new Set<string>()
64
55
  for (const relay of relays) {
@@ -242,6 +233,7 @@ function parseOptions(options: Record<string, unknown> | undefined): {
242
233
  }
243
234
 
244
235
  const PATCH_MARKER = "__opencodeRelayZenPatched"
236
+ const ROUND_ROBIN_KEY = "__opencodeRelayRoundRobinIdx"
245
237
 
246
238
  export default Plugin.define({
247
239
  id: "relay",
@@ -307,7 +299,15 @@ export default Plugin.define({
307
299
  // Bind the pristine fetch, not a possibly already-patched one from a
308
300
  // prior setup without cleanup (avoids wrapping the patch recursively).
309
301
  const directFetch = originalForPatch.bind(globalThis)
310
- const pickIndex = createRoundRobin(relays.length)
302
+ // Round-robin index is global (module/process-wide) so that setups created
303
+ // per session/conversation still rotate through the pool instead of every
304
+ // one starting at relay 0. Stored next to the patch marker.
305
+ if (g[ROUND_ROBIN_KEY] === undefined) g[ROUND_ROBIN_KEY] = 0
306
+ const pickIndex = () => {
307
+ const cur = (g[ROUND_ROBIN_KEY] as number) ?? 0
308
+ g[ROUND_ROBIN_KEY] = cur + 1
309
+ return cur % relays.length
310
+ }
311
311
  const relayOrigins = buildRelayOrigins(relays)
312
312
  const relayFetch = createRelayFetch(relays, pickIndex, directFetch, debug, log, emit)
313
313
 
@@ -316,6 +316,7 @@ export default Plugin.define({
316
316
  // else that calls fetch("https://opencode.ai/...") directly.
317
317
  if (!g[PATCH_MARKER]) {
318
318
  g[PATCH_MARKER] = originalForPatch
319
+ g[ROUND_ROBIN_KEY] = 0
319
320
  globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => {
320
321
  const urlStr =
321
322
  typeof input === "string" ? input : input instanceof URL ? input.href : input.url
@@ -515,6 +516,7 @@ export default Plugin.define({
515
516
  // ignore
516
517
  }
517
518
  delete g[PATCH_MARKER]
519
+ delete g[ROUND_ROBIN_KEY]
518
520
  }
519
521
  },
520
522
  })
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nofendian17/opencode-relay-plugin",
3
- "version": "2.0.6",
4
- "description": "Route OpenCode v2 Zen (opencode.ai) traffic through relay endpoint(s) with round-robin, failover, and direct fallback. Other providers are left untouched.",
3
+ "version": "2.0.7",
4
+ "description": "Route OpenCode v2 opencode.ai traffic through relay endpoint(s) with round-robin, failover, and direct fallback. Other providers are left untouched.",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
7
7
  "exports": {