@jacobbd/relay-ai 0.4.8 → 0.4.9

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.md CHANGED
@@ -189,7 +189,7 @@ Opens a browser-based dashboard on a random local port. From the UI you can:
189
189
  - **Keep Claude Code's Anthropic login** — on the Claude Code CLI card, check **Keep my Anthropic login and add Relay models** to keep your normal Claude models while adding the selected Relay model and compatible favorites. This option is not shown for Claude Desktop.
190
190
  - **Manage General Favorites** — the sidebar shows your saved favorite models with a slot indicator (Slots used X/20). Favorites launch through all supported agents.
191
191
  - **Manage Antigravity Favorites** — separate favorites panel for Antigravity sessions.
192
- - **Manage providers** — add providers from templates, delete providers, and refresh model lists inline, all without leaving the browser.
192
+ - **Manage providers** — add providers from templates, delete providers, and refresh model lists inline, all without leaving the browser. For GitHub Copilot, ChatGPT, and xAI OAuth, the UI displays a one-time device code with **Copy code** and **Open sign-in page** buttons so you can complete sign-in without using the terminal. Connected Copilot cards also identify the account as Free, Paid, or Plan unverified so the visible model catalog is easier to understand.
193
193
  - **Run the Server tab** — configure and start the same gateway as `relay-ai server` (favorites-only or specific providers, discovery id masking, local/network listen mode) and see the resulting URLs, API key, and model catalog right in the browser. Runs in the same process as the UI, so it stops when you close the dashboard. See [Registry gateway (`relay-ai server`)](#registry-gateway-relay-ai-server) below for what each option does.
194
194
 
195
195
  Press `Ctrl+C` in the terminal where `relay-ai ui` is running to shut down the dashboard server (this also stops the gateway if you started it from the Server tab).
@@ -409,6 +409,8 @@ Read the full setup and risk notes in **[docs/ANTIGRAVITY.md](docs/ANTIGRAVITY.m
409
409
 
410
410
  relay-ai supports OAuth providers that use device-code sign-in, so you can connect an existing subscription without pasting an API key. See **[docs/SUBSCRIPTION-OAUTH.md](docs/SUBSCRIPTION-OAUTH.md)** for setup details.
411
411
 
412
+ In `relay-ai ui`, select one of the OAuth providers and click **Get sign-in code**. Relay AI shows the code with a Copy button; click **Open sign-in page**, paste the code in the provider's page, and return to the UI while it finishes connecting.
413
+
412
414
  Device code flows for existing subscriptions:
413
415
 
414
416
  ```bash
@@ -11,7 +11,7 @@ import { join } from "path";
11
11
  // package.json
12
12
  var package_default = {
13
13
  name: "@jacobbd/relay-ai",
14
- version: "0.4.8",
14
+ version: "0.4.9",
15
15
  publishConfig: {
16
16
  access: "public"
17
17
  },
@@ -2409,7 +2409,8 @@ function decodeAuthEntry(value) {
2409
2409
  refresh: record["refresh"],
2410
2410
  expires: record["expires"],
2411
2411
  accountId: typeof record["accountId"] === "string" ? record["accountId"] : void 0,
2412
- enterpriseUrl: typeof record["enterpriseUrl"] === "string" ? record["enterpriseUrl"] : void 0
2412
+ enterpriseUrl: typeof record["enterpriseUrl"] === "string" ? record["enterpriseUrl"] : void 0,
2413
+ providerData: record["providerData"] && typeof record["providerData"] === "object" && !Array.isArray(record["providerData"]) ? record["providerData"] : void 0
2413
2414
  };
2414
2415
  }
2415
2416
  if (record["type"] === "wellknown" && typeof record["key"] === "string" && typeof record["token"] === "string") {
@@ -2460,13 +2461,14 @@ function oauthCredentialToKeychainJson(cred) {
2460
2461
 
2461
2462
  // src/oauth/types.ts
2462
2463
  function tokensToStoredCredential(tokens, existingRefresh, accountId, providerData) {
2464
+ const mergedProviderData = providerData || tokens.providerData ? { ...providerData, ...tokens.providerData } : void 0;
2463
2465
  return {
2464
2466
  type: "oauth",
2465
2467
  access: tokens.access_token,
2466
2468
  refresh: tokens.refresh_token ?? existingRefresh ?? "",
2467
2469
  expires: Date.now() + (tokens.expires_in ?? 3600) * 1e3,
2468
2470
  ...accountId ? { accountId } : {},
2469
- ...providerData ? { providerData } : {}
2471
+ ...mergedProviderData ? { providerData: mergedProviderData } : {}
2470
2472
  };
2471
2473
  }
2472
2474
  function parseStoredOAuthCredential(raw) {
@@ -2512,10 +2514,16 @@ var CLIENT_ID2 = "Iv1.b507a08c87ecfe98";
2512
2514
  var DEVICE_CODE_URL = "https://github.com/login/device/code";
2513
2515
  var TOKEN_URL2 = "https://github.com/login/oauth/access_token";
2514
2516
  var COPILOT_TOKEN_URL = "https://api.github.com/copilot_internal/v2/token";
2517
+ var COPILOT_USER_URL = "https://api.github.com/copilot_internal/user";
2515
2518
  var SCOPE = "copilot";
2516
2519
  var DEVICE_CODE_DEFAULT_INTERVAL_MS = 5e3;
2517
2520
  var DEVICE_CODE_DEFAULT_EXPIRES_MS2 = 15 * 60 * 1e3;
2518
2521
  var OAUTH_POLLING_SAFETY_MARGIN_MS2 = 1e3;
2522
+ var FREE_COPILOT_SKUS = /* @__PURE__ */ new Set([
2523
+ "free_limited_copilot",
2524
+ "free_educational_quota",
2525
+ "no_auth_limited_copilot"
2526
+ ]);
2519
2527
  function commonHeaders() {
2520
2528
  return {
2521
2529
  Accept: "application/json",
@@ -2523,6 +2531,46 @@ function commonHeaders() {
2523
2531
  "User-Agent": `relay-ai/${VERSION}`
2524
2532
  };
2525
2533
  }
2534
+ function classifyCopilotAccount(user) {
2535
+ const login = typeof user["login"] === "string" && user["login"].trim() ? user["login"].trim() : void 0;
2536
+ const sku = typeof user["access_type_sku"] === "string" && user["access_type_sku"].trim() ? user["access_type_sku"].trim() : void 0;
2537
+ const plan = typeof user["copilot_plan"] === "string" && user["copilot_plan"].trim() ? user["copilot_plan"].trim() : void 0;
2538
+ if (!sku && !plan) {
2539
+ return {
2540
+ ...login ? { login } : {},
2541
+ lookup_status: "unknown"
2542
+ };
2543
+ }
2544
+ const isFree = FREE_COPILOT_SKUS.has(sku?.toLowerCase() ?? "") || plan?.toLowerCase() === "free";
2545
+ return {
2546
+ ...login ? { login } : {},
2547
+ ...sku ? { access_type_sku: sku } : {},
2548
+ ...plan ? { copilot_plan: plan } : {},
2549
+ is_free_plan: isFree,
2550
+ lookup_status: "known"
2551
+ };
2552
+ }
2553
+ async function fetchCopilotAccount(ghuToken) {
2554
+ const response = await fetch(COPILOT_USER_URL, {
2555
+ method: "GET",
2556
+ headers: {
2557
+ Authorization: `Bearer ${ghuToken}`,
2558
+ Accept: "application/json",
2559
+ "User-Agent": `relay-ai/${VERSION}`,
2560
+ "Editor-Version": "vscode/1.85.1",
2561
+ "X-GitHub-Api-Version": "2025-04-01"
2562
+ }
2563
+ });
2564
+ if (!response.ok) {
2565
+ const detail = await response.text().catch(() => "");
2566
+ throw new Error(`GitHub Copilot account lookup failed (${response.status})${detail ? `: ${detail}` : ""}`);
2567
+ }
2568
+ const json = await response.json();
2569
+ if (!json || typeof json !== "object" || Array.isArray(json)) {
2570
+ throw new Error("GitHub Copilot account lookup returned invalid JSON");
2571
+ }
2572
+ return classifyCopilotAccount(json);
2573
+ }
2526
2574
  async function requestGithubDeviceCode() {
2527
2575
  const response = await fetch(DEVICE_CODE_URL, {
2528
2576
  method: "POST",
@@ -2561,7 +2609,16 @@ async function exchangeForCopilotToken(ghuToken) {
2561
2609
  const expiresMs = new Date(json.expires_at).getTime() - Date.now();
2562
2610
  if (expiresMs > 0) expiresIn = Math.floor(expiresMs / 1e3);
2563
2611
  }
2564
- return { access_token: json.token, expires_in: expiresIn };
2612
+ let account = { lookup_status: "unknown" };
2613
+ try {
2614
+ account = await fetchCopilotAccount(ghuToken);
2615
+ } catch {
2616
+ }
2617
+ return {
2618
+ access_token: json.token,
2619
+ expires_in: expiresIn,
2620
+ providerData: { copilot: account }
2621
+ };
2565
2622
  }
2566
2623
  async function refreshGithubCopilotToken(ghuToken) {
2567
2624
  const copilot = await exchangeForCopilotToken(ghuToken);
@@ -2598,7 +2655,8 @@ async function pollGithubDeviceCodeToken(device, opts) {
2598
2655
  access_token: copilot.access_token,
2599
2656
  refresh_token: ghuToken,
2600
2657
  // store ghu_ as refresh for re-exchange later
2601
- expires_in: copilot.expires_in
2658
+ expires_in: copilot.expires_in,
2659
+ providerData: copilot.providerData
2602
2660
  };
2603
2661
  }
2604
2662
  if (error === "authorization_pending") {
@@ -3105,6 +3163,28 @@ async function resolveProviderOAuthProviderData(authRef, diag) {
3105
3163
  const raw = await readKeyringAccount(parsed.account, diag);
3106
3164
  return parseStoredOAuthCredential(raw)?.providerData;
3107
3165
  }
3166
+ async function enrichGithubCopilotOAuthProviderData(authRef, diag) {
3167
+ const parsed = parseAuthRef(authRef);
3168
+ if (!parsed || parsed.kind !== "keyring" || oauthProviderIdFromAccount(parsed.account) !== "github-copilot") {
3169
+ return void 0;
3170
+ }
3171
+ const raw = await readKeyringAccount(parsed.account, diag);
3172
+ const credential = parseStoredOAuthCredential(raw);
3173
+ if (!credential?.refresh) return credential?.providerData;
3174
+ try {
3175
+ const summary = await fetchCopilotAccount(credential.refresh);
3176
+ const providerData = { ...credential.providerData, copilot: summary };
3177
+ await writeKeyringAccount(
3178
+ parsed.account,
3179
+ oauthCredentialToKeychainJson({ ...credential, providerData }),
3180
+ diag
3181
+ );
3182
+ return providerData;
3183
+ } catch (err) {
3184
+ diag?.(`GitHub Copilot plan lookup unavailable \u2014 ${err instanceof Error ? err.message : String(err)}`);
3185
+ return credential.providerData;
3186
+ }
3187
+ }
3108
3188
  function decodeProviderSecret(raw) {
3109
3189
  if (!raw) return null;
3110
3190
  const trimmed = raw.trim();
@@ -6781,6 +6861,106 @@ function materializeRegistry(registry, resolveCredential, opts) {
6781
6861
  return result;
6782
6862
  }
6783
6863
 
6864
+ // src/registry/copilot-models.ts
6865
+ var FREE_MODEL_IDS = /* @__PURE__ */ new Set([
6866
+ "gpt-4.1",
6867
+ "gpt-4o",
6868
+ "gpt-4o-mini",
6869
+ "raptor-mini",
6870
+ "goldeneye"
6871
+ ]);
6872
+ var FREE_CHAT_BLOCKLIST = /* @__PURE__ */ new Set(["gpt-5-mini"]);
6873
+ function copilotPlanTier(providerData) {
6874
+ const copilot = providerData?.["copilot"];
6875
+ if (!copilot || typeof copilot !== "object" || Array.isArray(copilot)) return "unknown";
6876
+ const summary = copilot;
6877
+ if (summary["lookup_status"] === "unknown") return "unknown";
6878
+ if (summary["is_free_plan"] === true) return "free";
6879
+ if (summary["is_free_plan"] === false) return "paid";
6880
+ return "unknown";
6881
+ }
6882
+ function normalizeCopilotModels(rows, tier) {
6883
+ const models = [];
6884
+ for (const value of rows) {
6885
+ if (!value || typeof value !== "object" || Array.isArray(value)) continue;
6886
+ const row = value;
6887
+ const id = typeof row["id"] === "string" ? row["id"].trim() : "";
6888
+ if (!id || !copilotModelAllowed(row, tier)) continue;
6889
+ const lowerId = id.toLowerCase();
6890
+ const isFree = tier !== "paid" || copilotModelIsIncluded(row);
6891
+ const family = lowerId.split(/[-/:]/)[0] ?? lowerId;
6892
+ const contextWindow = numericValue(row["context_length"]) ?? numericValue(row["contextWindow"]) ?? numericValue(row["context_window"]) ?? resolveContextWindow(id);
6893
+ models.push({
6894
+ id,
6895
+ name: `${id} [Copilot]`,
6896
+ upstreamModelId: id,
6897
+ family,
6898
+ brand: deriveBrand(family),
6899
+ contextWindow,
6900
+ isFree,
6901
+ freeStatus: isFree ? "verified_free" : "unknown",
6902
+ modelFormat: "openai",
6903
+ npm: "@ai-sdk/openai-compatible",
6904
+ apiUrl: "https://api.githubcopilot.com"
6905
+ });
6906
+ }
6907
+ return models;
6908
+ }
6909
+ function filterCachedCopilotModels(models, tier) {
6910
+ return models.flatMap((model) => {
6911
+ const id = model.id.toLowerCase();
6912
+ if (!copilotIdIsCallable(id)) return [];
6913
+ if (tier !== "paid" && (!FREE_MODEL_IDS.has(id) || FREE_CHAT_BLOCKLIST.has(id))) return [];
6914
+ if (tier === "paid") return [model];
6915
+ return [{ ...model, isFree: true, freeStatus: "verified_free" }];
6916
+ });
6917
+ }
6918
+ function copilotModelAllowed(row, tier) {
6919
+ const id = String(row["id"] ?? "").toLowerCase();
6920
+ if (!copilotIdIsCallable(id)) return false;
6921
+ if (row["model_picker_enabled"] === false) return false;
6922
+ const policy = row["policy"];
6923
+ if (policy && typeof policy === "object" && !Array.isArray(policy)) {
6924
+ if (String(policy["state"] ?? "").toLowerCase() === "disabled") return false;
6925
+ }
6926
+ const capabilities = row["capabilities"];
6927
+ if (capabilities && typeof capabilities === "object" && !Array.isArray(capabilities)) {
6928
+ const family = String(capabilities["family"] ?? "").toLowerCase();
6929
+ if (family.includes("embedding")) return false;
6930
+ }
6931
+ const endpoints = row["supported_endpoints"];
6932
+ if (Array.isArray(endpoints) && endpoints.length > 0) {
6933
+ const supportsChat = endpoints.some((endpoint) => {
6934
+ const normalized = String(endpoint).toLowerCase().replace(/\/$/, "");
6935
+ return normalized.endsWith("/chat/completions") || normalized === "chat/completions";
6936
+ });
6937
+ if (!supportsChat) return false;
6938
+ }
6939
+ if (tier !== "paid" && (!FREE_MODEL_IDS.has(id) || FREE_CHAT_BLOCKLIST.has(id))) return false;
6940
+ return true;
6941
+ }
6942
+ function copilotIdIsCallable(id) {
6943
+ return id !== "auto" && !id.endsWith("-auto") && !id.includes("embedding");
6944
+ }
6945
+ function copilotModelIsIncluded(row) {
6946
+ const id = String(row["id"] ?? "").toLowerCase();
6947
+ if (FREE_CHAT_BLOCKLIST.has(id) || !copilotIdIsCallable(id)) return false;
6948
+ const billing = row["billing"];
6949
+ if (billing && typeof billing === "object" && !Array.isArray(billing)) {
6950
+ const multiplier = numericValue(billing["multiplier"]);
6951
+ if (multiplier !== void 0) return multiplier === 0;
6952
+ }
6953
+ return FREE_MODEL_IDS.has(id);
6954
+ }
6955
+ function numericValue(value) {
6956
+ if (typeof value === "number" && Number.isFinite(value)) return value;
6957
+ if (typeof value === "string" && value.trim()) {
6958
+ const number = Number(value);
6959
+ if (Number.isFinite(number)) return number;
6960
+ }
6961
+ return void 0;
6962
+ }
6963
+
6784
6964
  // src/registry/load.ts
6785
6965
  async function loadRegistryProviders(diag, opts) {
6786
6966
  const registry = loadRegistry();
@@ -6804,7 +6984,23 @@ async function loadRegistryProviders(diag, opts) {
6804
6984
  }
6805
6985
  }
6806
6986
  }));
6807
- return materializeRegistry(registry, (provider) => keys.get(provider.id) ?? null, opts).map((provider) => ({
6987
+ const runtimeRegistry = {
6988
+ ...registry,
6989
+ providers: registry.providers.map((provider) => {
6990
+ if (provider.id !== "github-copilot" || !provider.modelsCache) return provider;
6991
+ return {
6992
+ ...provider,
6993
+ modelsCache: {
6994
+ ...provider.modelsCache,
6995
+ models: filterCachedCopilotModels(
6996
+ provider.modelsCache.models,
6997
+ copilotPlanTier(oauthProviderData.get(provider.id))
6998
+ )
6999
+ }
7000
+ };
7001
+ })
7002
+ };
7003
+ return materializeRegistry(runtimeRegistry, (provider) => keys.get(provider.id) ?? null, opts).map((provider) => ({
6808
7004
  ...provider,
6809
7005
  oauthAccountId: oauthAccountIds.get(provider.id),
6810
7006
  providerData: oauthProviderData.get(provider.id)
@@ -9308,10 +9504,49 @@ async function refreshAntigravityOAuthModels(accessToken) {
9308
9504
  }
9309
9505
  throw new Error("Antigravity live model refresh failed \u2014 Cloud Code returned no usable models");
9310
9506
  }
9507
+ function buildCopilotFreeFallback() {
9508
+ return normalizeCopilotModels([
9509
+ {
9510
+ id: "gpt-4.1",
9511
+ supported_endpoints: ["/chat/completions"],
9512
+ billing: { multiplier: 0 }
9513
+ },
9514
+ {
9515
+ id: "gpt-4o",
9516
+ supported_endpoints: ["/chat/completions"],
9517
+ billing: { multiplier: 0 }
9518
+ }
9519
+ ], "free");
9520
+ }
9521
+ async function refreshGithubCopilotOAuthModels(accessToken, tier) {
9522
+ const result = await fetchJsonWithAuth(
9523
+ "https://api.githubcopilot.com/models",
9524
+ accessToken,
9525
+ 1e4,
9526
+ { "Editor-Version": "vscode/1.85.1" }
9527
+ );
9528
+ const body = result.body;
9529
+ const rows = Array.isArray(body) ? body : body && typeof body === "object" ? Array.isArray(body["data"]) ? body["data"] : Array.isArray(body["models"]) ? body["models"] : [] : [];
9530
+ const models = normalizeCopilotModels(rows, tier);
9531
+ if (models.length > 0) return { models, source: "live" };
9532
+ return {
9533
+ models: buildCopilotFreeFallback(),
9534
+ source: "seed",
9535
+ failureReason: result.error ?? "GitHub Copilot returned no usable chat models",
9536
+ replaceCacheOnFallback: tier !== "paid"
9537
+ };
9538
+ }
9311
9539
  async function refreshOAuthProvider(provider, accessToken) {
9312
9540
  const tpl = provider.templateId ?? provider.id;
9313
9541
  if (tpl === "openai" || tpl === "openai-oauth") return refreshOpenAiOAuthModels(accessToken);
9314
9542
  if (tpl === "xai" || tpl === "xai-oauth") return refreshXaiOAuthModels(accessToken);
9543
+ if (tpl === "github-copilot") {
9544
+ let providerData = await resolveProviderOAuthProviderData(provider.authRef);
9545
+ if (copilotPlanTier(providerData) === "unknown") {
9546
+ providerData = await enrichGithubCopilotOAuthProviderData(provider.authRef) ?? providerData;
9547
+ }
9548
+ return refreshGithubCopilotOAuthModels(accessToken, copilotPlanTier(providerData));
9549
+ }
9315
9550
  if (tpl === "claude-code") return refreshClaudeCodeOAuthModels(accessToken);
9316
9551
  if (tpl === "antigravity") return refreshAntigravityOAuthModels(accessToken);
9317
9552
  throw new Error(`refreshOAuthProvider: unsupported template "${tpl}"`);
@@ -9369,7 +9604,7 @@ function buildDynamicOAuthModel(entry, seedById) {
9369
9604
  preferWebSockets: entry.preferWebSockets
9370
9605
  };
9371
9606
  }
9372
- async function fetchJsonWithAuth(url, accessToken, timeoutMs) {
9607
+ async function fetchJsonWithAuth(url, accessToken, timeoutMs, extraHeaders = {}) {
9373
9608
  try {
9374
9609
  const controller = new AbortController();
9375
9610
  const timer = setTimeout(() => controller.abort(), timeoutMs);
@@ -9377,7 +9612,8 @@ async function fetchJsonWithAuth(url, accessToken, timeoutMs) {
9377
9612
  headers: {
9378
9613
  Accept: "application/json",
9379
9614
  Authorization: `Bearer ${accessToken}`,
9380
- "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
9615
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
9616
+ ...extraHeaders
9381
9617
  },
9382
9618
  signal: controller.signal
9383
9619
  }).finally(() => clearTimeout(timer));
@@ -9533,7 +9769,7 @@ async function refreshProviderModels(providerId, apiKey, registry = loadRegistry
9533
9769
  let oauthFallbackReason;
9534
9770
  if (source === "zen-go-api") {
9535
9771
  models = await refreshZenGoProvider(provider);
9536
- } else if (provider.authType === "oauth" && (["openai", "xai", "xai-oauth", "claude-code", "antigravity"].includes(provider.templateId ?? provider.id) || provider.id === "openai-oauth" || provider.id === "xai-oauth")) {
9772
+ } else if (provider.authType === "oauth" && (["openai", "xai", "xai-oauth", "github-copilot", "claude-code", "antigravity"].includes(provider.templateId ?? provider.id) || provider.id === "openai-oauth" || provider.id === "xai-oauth")) {
9537
9773
  if (!apiKey) {
9538
9774
  return {
9539
9775
  id: provider.id,
@@ -9544,7 +9780,7 @@ async function refreshProviderModels(providerId, apiKey, registry = loadRegistry
9544
9780
  }
9545
9781
  const oauthResult = await refreshOAuthProvider(provider, apiKey);
9546
9782
  const failureDetail = oauthResult.failureReason ? ` (${oauthResult.failureReason})` : "";
9547
- if (oauthResult.source === "seed" && cachedModelCount(provider) > 0) {
9783
+ if (oauthResult.source === "seed" && cachedModelCount(provider) > 0 && !oauthResult.replaceCacheOnFallback) {
9548
9784
  return skipWithCachedModels(
9549
9785
  provider,
9550
9786
  `Live model discovery failed${failureDetail} \u2014 kept your existing cached model list instead of overwriting it with relay-ai's built-in fallback list. Try refreshing again later.`
@@ -9766,7 +10002,7 @@ var PROVIDER_DISPLAY = {
9766
10002
  "xai-oauth": "xAI Grok (SuperGrok)",
9767
10003
  openai: OPENAI_DISPLAY,
9768
10004
  "openai-oauth": OPENAI_DISPLAY,
9769
- "github-copilot": "GitHub Copilot (Individual / Business)",
10005
+ "github-copilot": "GitHub Copilot",
9770
10006
  "claude-code": "Claude Code (Anthropic subscription)",
9771
10007
  antigravity: "Antigravity (Google Cloud Code Assist)"
9772
10008
  };
@@ -10019,7 +10255,7 @@ function providerAuthHelpText() {
10019
10255
 
10020
10256
  ${pc6.bold("Usage:")}
10021
10257
  relay-ai providers auth <id>
10022
- relay-ai providers auth xai --native
10258
+ relay-ai providers auth xai-oauth --native
10023
10259
  relay-ai providers auth openai --broker
10024
10260
  relay-ai providers auth github-copilot
10025
10261
 
@@ -10028,9 +10264,9 @@ ${pc6.bold("Options:")}
10028
10264
  --broker Delegate to OpenCode auth login
10029
10265
 
10030
10266
  ${pc6.bold("Device code (works on SSH/VPS):")}
10031
- xai SuperGrok / X Premium (device code at x.ai/device)
10032
- openai ChatGPT Plus/Pro (device code at auth.openai.com/codex/device)
10033
- github-copilot GitHub Copilot Individual/Business (device code at github.com/login/device)`;
10267
+ xai-oauth SuperGrok / X Premium (device code at x.ai/device)
10268
+ openai-oauth ChatGPT Plus/Pro (device code at auth.openai.com/codex/device)
10269
+ github-copilot GitHub Copilot Free or paid (device code at github.com/login/device)`;
10034
10270
  }
10035
10271
 
10036
10272
  // src/codex/app-launch.ts
@@ -10618,6 +10854,7 @@ export {
10618
10854
  makeRouteResolver,
10619
10855
  buildCatalogRoutes,
10620
10856
  cachedModelToLocal,
10857
+ copilotPlanTier,
10621
10858
  fetchProviderCatalog,
10622
10859
  providersForPicker,
10623
10860
  resolveLocalProviderApiKey,
@@ -10665,4 +10902,4 @@ export {
10665
10902
  supportsClaudeTransparentMode,
10666
10903
  buildHttpProxyRoutes
10667
10904
  };
10668
- //# sourceMappingURL=chunk-I3SSHXSP.js.map
10905
+ //# sourceMappingURL=chunk-XTBJCRT3.js.map