free-coding-models 0.5.89 → 0.5.91

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.
@@ -27,7 +27,7 @@
27
27
  * runProviderKeyTest, PROVIDER_AUTH_ENDPOINTS
28
28
  */
29
29
 
30
- import { ping } from './ping.js'
30
+ import { ping, getProviderSessionHeaders, resolveCloudflareUrl } from './ping.js'
31
31
  import { sleep } from './shared-helpers.js'
32
32
 
33
33
  // ─── Constants ────────────────────────────────────────────────────────────────
@@ -295,12 +295,19 @@ export async function runProviderKeyTest(apiKey, providerKey, source, options =
295
295
 
296
296
  // 📖 Slow path: ping-based verification (providers without auth endpoint or timeouts).
297
297
  const discoveredModelIds = []
298
- const modelsUrl = buildProviderModelsUrl(source?.url)
298
+ // 📖 Cloudflare's models URL is account-scoped (issue #181): resolve the
299
+ // 📖 {$CLOUDFLARE_ACCOUNT_ID} placeholder so discovery reaches the real API
300
+ // 📖 instead of 404ing on the raw catalog URL and silently falling back.
301
+ const modelsUrl = providerKey === 'cloudflare'
302
+ ? resolveCloudflareUrl(buildProviderModelsUrl(source?.url) || '')
303
+ : buildProviderModelsUrl(source?.url)
299
304
  let discoveryNote = ''
300
305
 
301
306
  if (modelsUrl) {
302
307
  try {
303
- const headers = { Authorization: `Bearer ${apiKey}` }
308
+ // 📖 Mandatory per-provider headers (issue #181): OpenCode Zen gates every
309
+ // 📖 request (model discovery included) behind `x-opencode-session`.
310
+ const headers = { Authorization: `Bearer ${apiKey}`, ...getProviderSessionHeaders(providerKey) }
304
311
  if (providerKey === 'openrouter') {
305
312
  headers['HTTP-Referer'] = 'https://github.com/vava-nessa/free-coding-models'
306
313
  headers['X-Title'] = 'free-coding-models'
@@ -228,7 +228,7 @@ export const PROVIDER_METADATA = {
228
228
  label: 'Cloudflare Workers AI',
229
229
  color: chalk.rgb(255, 204, 128),
230
230
  signupUrl: 'https://dash.cloudflare.com',
231
- signupHint: 'Create AI API token + set CLOUDFLARE_ACCOUNT_ID',
231
+ signupHint: 'Create AI API token (account id auto-discovered; CLOUDFLARE_ACCOUNT_ID optional)',
232
232
  rateLimits: 'Free: 10k neurons/day, text-gen 300 RPM',
233
233
  },
234
234
  perplexity: {
@@ -47,7 +47,7 @@ import {
47
47
  normalizeRouterConfig,
48
48
  saveConfig,
49
49
  } from './config.js'
50
- import { buildChatCompletionPingBody, ping, resolveCloudflareUrl, shouldUseDisabledThinkingForProvider } from './ping.js'
50
+ import { buildChatCompletionPingBody, ping, resolveCloudflareUrl, shouldUseDisabledThinkingForProvider, getProviderSessionHeaders } from './ping.js'
51
51
  import { benchmarkModel, BENCHMARK_TIMEOUT_MS } from './benchmark.js'
52
52
  import { loadChangelog } from './changelog-loader.js'
53
53
  import { sendUsageTelemetry } from './telemetry.js'
@@ -689,6 +689,11 @@ export function cloneHeadersForUpstream(reqHeaders, apiKey, providerKey) {
689
689
  headers['HTTP-Referer'] = 'https://github.com/vava-nessa/free-coding-models'
690
690
  headers['X-Title'] = 'free-coding-models'
691
691
  }
692
+ // 📖 Mandatory per-provider headers (issue #181): OpenCode Zen rejects every
693
+ // 📖 request without `x-opencode-session` with HTTP 400 MissingSessionID.
694
+ // 📖 This function builds headers for BOTH health probes and real forwarded
695
+ // 📖 traffic, so applying it here covers every upstream call in one place.
696
+ Object.assign(headers, getProviderSessionHeaders(providerKey))
692
697
  return headers
693
698
  }
694
699
 
@@ -4378,6 +4383,9 @@ export function createDefaultProbeFn(apiKeys) {
4378
4383
  headers['HTTP-Referer'] = 'https://github.com/vava-nessa/free-coding-models'
4379
4384
  headers['X-Title'] = 'free-coding-models'
4380
4385
  }
4386
+ // 📖 Mandatory per-provider headers (issue #181): OpenCode Zen 400s without
4387
+ // 📖 `x-opencode-session`, so default-set probes must carry it too.
4388
+ Object.assign(headers, getProviderSessionHeaders(provider))
4381
4389
  }
4382
4390
  const started = Date.now()
4383
4391
  try {
@@ -25,6 +25,7 @@ import { renderRouterV2Dashboard as renderRouterV2DashboardOverlay } from '../co
25
25
  import { renderPlayground as renderPlaygroundOverlay } from '../core/playground.js'
26
26
  import { themeColors, getThemeStatusLabel, getProviderRgb } from './theme.js'
27
27
  import { getProviderBillingNote, getProviderLabelWithBilling } from '../core/provider-metadata.js'
28
+ import { pickAccountIdFromSettings } from '../core/cloudflare-account.js'
28
29
  import { detectTerminalCapabilities } from '../core/utils.js'
29
30
  import { truncateAnsiWidth, loadChangelogCached } from './render-helpers.js'
30
31
  import { COMMAND_PALETTE_MAX_RESULTS } from './command-palette.js'
@@ -222,9 +223,14 @@ export function createOverlayRenderers(state, deps) {
222
223
  lines.push(themeColors.dim(` 2) ${selectedMeta.signupHint || 'Generate an API key and paste it with Enter on this row'}`))
223
224
  lines.push(themeColors.dim(` 3) Press ${themeColors.hotkey('T')} to test your key. Status: ${setupStatus}`))
224
225
  if (selectedProviderKey === 'cloudflare') {
225
- const hasAccountId = Boolean((process.env.CLOUDFLARE_ACCOUNT_ID || '').trim())
226
- const accountIdStatus = hasAccountId ? themeColors.success('CLOUDFLARE_ACCOUNT_ID detected ✅') : themeColors.warning('Set CLOUDFLARE_ACCOUNT_ID ⚠')
227
- lines.push(themeColors.dim(` 4) Export ${themeColors.hotkey('CLOUDFLARE_ACCOUNT_ID')} in your shell. Status: ${accountIdStatus}`))
226
+ // 📖 Account id resolution has fallbacks since issue #181: env var,
227
+ // 📖 stored config setting, or zero-setup auto-discovery from the API key.
228
+ const hasEnvAccountId = Boolean((process.env.CLOUDFLARE_ACCOUNT_ID || '').trim())
229
+ const hasStoredAccountId = Boolean(pickAccountIdFromSettings(state.config?.settings))
230
+ const accountIdStatus = (hasEnvAccountId || hasStoredAccountId)
231
+ ? themeColors.success('account id detected ✅')
232
+ : themeColors.warning('will be auto-discovered from your API key (or export CLOUDFLARE_ACCOUNT_ID) ⚠')
233
+ lines.push(themeColors.dim(` 4) Cloudflare's endpoint is account-scoped. Account id: ${accountIdStatus}`))
228
234
  }
229
235
  const testDetail = state.settingsTestDetails?.[selectedProviderKey]
230
236
  if (testDetail) {