feat(muse): add Meta Muse provider with OAuth login and model catalog
This commit is contained in:
1 parent
2310ad4b14
commit
2880980709
15 files changed
+317
-12
No files matched your search
@@ -41,6 +41,9 @@ function applyAuth(headers, desc, credentials) {
|
|||||||
const HEADER_HOOKS = {
|
const HEADER_HOOKS = {
|
||||||
// Stable device_id from OAuth connection (CLIProxyAPI KimiTokenStorage.DeviceID)
|
// Stable device_id from OAuth connection (CLIProxyAPI KimiTokenStorage.DeviceID)
|
||||||
kimiHeaders: (h, c) => Object.assign(h, buildKimiHeaders(c?.providerSpecificData?.deviceId)),
|
kimiHeaders: (h, c) => Object.assign(h, buildKimiHeaders(c?.providerSpecificData?.deviceId)),
|
||||||
|
// Muse: x-api-version only on subscription (minted key) requests — plain
|
||||||
|
// Model API keys already work without it
|
||||||
|
museHeaders: (h, c) => { if (c?.accessToken && !c?.apiKey) h["x-api-version"] = "1.0.0"; },
|
||||||
clineHeaders: (h, c) => Object.assign(h, buildClineHeaders(c.apiKey || c.accessToken)),
|
clineHeaders: (h, c) => Object.assign(h, buildClineHeaders(c.apiKey || c.accessToken)),
|
||||||
kilocodeOrg: (h, c) => { if (c.providerSpecificData?.orgId) h["X-Kilocode-OrganizationID"] = c.providerSpecificData.orgId; },
|
kilocodeOrg: (h, c) => { if (c.providerSpecificData?.orgId) h["X-Kilocode-OrganizationID"] = c.providerSpecificData.orgId; },
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -156,6 +156,15 @@ export const MODEL_PRICING = {
|
|||||||
// === Grok ===
|
// === Grok ===
|
||||||
"grok-code-fast-1": { input: 0.50, output: 2.00, cached: 0.25, reasoning: 3.00, cache_creation: 0.50 },
|
"grok-code-fast-1": { input: 0.50, output: 2.00, cached: 0.25, reasoning: 3.00, cache_creation: 0.50 },
|
||||||
|
|
||||||
|
// === Muse (Meta Model API) ===
|
||||||
|
// Rates from https://dev.meta.ai/docs/pricing-rate-limits (contributor tier:
|
||||||
|
// cheaper, Meta may train on the data).
|
||||||
|
"muse-spark-1.3": { input: 1.25, output: 4.25, cached: 0.15, reasoning: 4.25, cache_creation: 0 },
|
||||||
|
"muse-spark-1.2": { input: 1.25, output: 4.25, cached: 0.15, reasoning: 4.25, cache_creation: 0 },
|
||||||
|
"muse-spark-1.1": { input: 1.25, output: 4.25, cached: 0.15, reasoning: 4.25, cache_creation: 0 },
|
||||||
|
"muse-spark-1.3-contributor": { input: 0.10, output: 0.20, cached: 0.002, reasoning: 0.20, cache_creation: 0 },
|
||||||
|
"muse-spark-1.2-contributor": { input: 0.10, output: 0.20, cached: 0.002, reasoning: 0.20, cache_creation: 0 },
|
||||||
|
|
||||||
// === OpenRouter fallback ===
|
// === OpenRouter fallback ===
|
||||||
"auto": { input: 2.00, output: 8.00, cached: 1.00, reasoning: 12.00, cache_creation: 2.00 },
|
"auto": { input: 2.00, output: 8.00, cached: 1.00, reasoning: 12.00, cache_creation: 2.00 },
|
||||||
|
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ import p129 from "./agnes.js";
|
|||||||
import p130 from "./bai.js";
|
import p130 from "./bai.js";
|
||||||
import p131 from "./tinyfish.js";
|
import p131 from "./tinyfish.js";
|
||||||
import p132 from "./v1m.js";
|
import p132 from "./v1m.js";
|
||||||
|
import p133 from "./muse.js";
|
||||||
export default [
|
export default [
|
||||||
p0,
|
p0,
|
||||||
p1,
|
p1,
|
||||||
@@ -264,4 +265,5 @@ export default [
|
|||||||
p130,
|
p130,
|
||||||
p131,
|
p131,
|
||||||
p132,
|
p132,
|
||||||
|
p133,
|
||||||
];
|
];
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
// Muse (Meta Model API) — dual auth (same pattern as kimi):
|
||||||
|
// oauth = Muse Code subscription (Meta account device code, mints an LLM|… key)
|
||||||
|
// apikey = pay-as-you-go Model API key from dev.meta.ai
|
||||||
|
// Transport is shared; oauth accounts get x-api-version via the museHeaders hook.
|
||||||
|
// Meta issues no refresh token for the subscription flow → re-login on 401.
|
||||||
|
export default {
|
||||||
|
id: "muse",
|
||||||
|
priority: 120,
|
||||||
|
alias: "muse",
|
||||||
|
aliases: [
|
||||||
|
"muse-ai",
|
||||||
|
"meta-model-api",
|
||||||
|
"muse-code",
|
||||||
|
"muse-subscription",
|
||||||
|
],
|
||||||
|
uiAlias: "muse",
|
||||||
|
display: {
|
||||||
|
name: "Muse (Meta Model API)",
|
||||||
|
icon: "auto_awesome",
|
||||||
|
color: "#0866FF",
|
||||||
|
textIcon: "MU",
|
||||||
|
website: "https://muse.ai",
|
||||||
|
notice: {
|
||||||
|
text: "Sign in with your Meta account (Muse Code subscription) or paste a Model API key from dev.meta.ai. Subscription keys are minted per account; Meta may train on contributor-tier data.",
|
||||||
|
apiKeyUrl: "https://dev.meta.ai",
|
||||||
|
signupUrl: "https://muse.ai",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
category: "oauth",
|
||||||
|
authModes: ["oauth", "apikey"],
|
||||||
|
hasOAuth: true,
|
||||||
|
transport: {
|
||||||
|
baseUrl: "https://api.meta.ai/v1/chat/completions",
|
||||||
|
validateUrl: "https://api.meta.ai/v1/models",
|
||||||
|
modelsUrl: "https://api.meta.ai/v1/models",
|
||||||
|
auth: { combined: true, header: "Authorization", scheme: "bearer", hooks: ["museHeaders"] },
|
||||||
|
},
|
||||||
|
// Multi-endpoint: Meta accepts Chat Completions and Responses wire formats on
|
||||||
|
// the same key (https://dev.meta.ai/docs/protocols). Muse Spark reasoning
|
||||||
|
// (incl. encrypted_content replay) only round-trips on Responses, so models
|
||||||
|
// pin targetFormat there.
|
||||||
|
transports: [
|
||||||
|
{
|
||||||
|
format: "openai",
|
||||||
|
baseUrl: "https://api.meta.ai/v1/chat/completions",
|
||||||
|
auth: { combined: true, header: "Authorization", scheme: "bearer", hooks: ["museHeaders"] },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
format: "openai-responses",
|
||||||
|
baseUrl: "https://api.meta.ai/v1/responses",
|
||||||
|
auth: { combined: true, header: "Authorization", scheme: "bearer", hooks: ["museHeaders"] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
models: [
|
||||||
|
{ id: "muse-spark-1.3", name: "Muse Spark 1.3", targetFormat: "openai-responses", supportedFormats: ["openai-responses"] },
|
||||||
|
{ id: "muse-spark-1.2", name: "Muse Spark 1.2", targetFormat: "openai-responses", supportedFormats: ["openai-responses"] },
|
||||||
|
{ id: "muse-spark-1.1", name: "Muse Spark 1.1", targetFormat: "openai-responses", supportedFormats: ["openai-responses"] },
|
||||||
|
{ id: "muse-spark-1.3-contributor", name: "Muse Spark 1.3 Contributor", targetFormat: "openai-responses", supportedFormats: ["openai-responses"] },
|
||||||
|
{ id: "muse-spark-1.2-contributor", name: "Muse Spark 1.2 Contributor", targetFormat: "openai-responses", supportedFormats: ["openai-responses"] },
|
||||||
|
],
|
||||||
|
passthroughModels: true,
|
||||||
|
oauth: {
|
||||||
|
clientId: "1031625952748946",
|
||||||
|
deviceCodeUrl: "https://auth.meta.com/oidc/device/authorization/",
|
||||||
|
tokenUrl: "https://auth.meta.com/oidc/device/token/",
|
||||||
|
},
|
||||||
|
};
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
@@ -265,6 +265,7 @@ export async function GET(request, { params }) {
|
|||||||
"qoder",
|
"qoder",
|
||||||
"qoder-cn",
|
"qoder-cn",
|
||||||
"grok-cli",
|
"grok-cli",
|
||||||
|
"muse",
|
||||||
];
|
];
|
||||||
let deviceData;
|
let deviceData;
|
||||||
if (noPkceDeviceProviders.includes(provider)) {
|
if (noPkceDeviceProviders.includes(provider)) {
|
||||||
@@ -552,6 +553,8 @@ export async function POST(request, { params }) {
|
|||||||
error: result.error,
|
error: result.error,
|
||||||
errorDescription: result.errorDescription,
|
errorDescription: result.errorDescription,
|
||||||
pending: isPending,
|
pending: isPending,
|
||||||
|
// fatal: unrecoverable (e.g. post-exchange failure) — client must stop polling and show it
|
||||||
|
...(result.fatal ? { fatal: true } : {}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -169,6 +169,14 @@ function buildQoderModelsResolver(providerId) {
|
|||||||
|
|
||||||
// Provider models endpoints configuration
|
// Provider models endpoints configuration
|
||||||
const PROVIDER_MODELS_CONFIG = {
|
const PROVIDER_MODELS_CONFIG = {
|
||||||
|
"muse": {
|
||||||
|
url: "https://api.meta.ai/v1/models",
|
||||||
|
method: "GET",
|
||||||
|
headers: { "Content-Type": "application/json", "x-api-version": "1.0.0" },
|
||||||
|
authHeader: "Authorization",
|
||||||
|
authPrefix: "Bearer ",
|
||||||
|
parseResponse: (data) => data.data || [],
|
||||||
|
},
|
||||||
claude: {
|
claude: {
|
||||||
url: "https://api.anthropic.com/v1/models",
|
url: "https://api.anthropic.com/v1/models",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
|||||||
@@ -137,6 +137,15 @@ const OAUTH_TEST_CONFIG = {
|
|||||||
402: "Connected, but Grok Build credits are exhausted (spending limit). Add credits or upgrade SuperGrok.",
|
402: "Connected, but Grok Build credits are exhausted (spending limit). Add credits or upgrade SuperGrok.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// Muse Code subscription — probe /v1/models with the minted LLM|… key
|
||||||
|
"muse": {
|
||||||
|
url: "https://api.meta.ai/v1/models",
|
||||||
|
method: "GET",
|
||||||
|
authHeader: "Authorization",
|
||||||
|
authPrefix: "Bearer ",
|
||||||
|
extraHeaders: { "x-api-version": "1.0.0" },
|
||||||
|
refreshable: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -703,7 +712,8 @@ async function testApiKeyConnection(connection, effectiveProxy = null) {
|
|||||||
case "dahl":
|
case "dahl":
|
||||||
case "atria":
|
case "atria":
|
||||||
case "agnes":
|
case "agnes":
|
||||||
case "bai": {
|
case "bai":
|
||||||
|
case "muse": {
|
||||||
const cfg = PROVIDERS[connection.provider];
|
const cfg = PROVIDERS[connection.provider];
|
||||||
const res = await fetchWithConnectionProxy(cfg.validateUrl, { headers: { Authorization: `Bearer ${connection.apiKey}` } }, effectiveProxy);
|
const res = await fetchWithConnectionProxy(cfg.validateUrl, { headers: { Authorization: `Bearer ${connection.apiKey}` } }, effectiveProxy);
|
||||||
return { valid: res.ok, error: res.ok ? null : "Invalid API key" };
|
return { valid: res.ok, error: res.ok ? null : "Invalid API key" };
|
||||||
|
|||||||
@@ -127,6 +127,10 @@ export const KIMCHI_CONFIG = { ...PROVIDER_OAUTH["kimchi"] };
|
|||||||
// Endpoint: cli-chat-proxy.grok.com — same client_id as xai, different flow + scopes
|
// Endpoint: cli-chat-proxy.grok.com — same client_id as xai, different flow + scopes
|
||||||
export const GROK_CLI_CONFIG = { ...PROVIDER_OAUTH["grok-cli"] };
|
export const GROK_CLI_CONFIG = { ...PROVIDER_OAUTH["grok-cli"] };
|
||||||
|
|
||||||
|
// Muse — subscription device code flow to auth.meta.com, no refresh
|
||||||
|
// (Meta rejects refresh_token grants; the minted Model API key never expires).
|
||||||
|
export const MUSE_CONFIG = { ...PROVIDER_OAUTH["muse"] };
|
||||||
|
|
||||||
// Trae (ByteDance marscode) OAuth — authorization_code flow with local callback.
|
// Trae (ByteDance marscode) OAuth — authorization_code flow with local callback.
|
||||||
// 1) POST GetLoginGuidance {loginTraceID} → {Result.LoginHost}
|
// 1) POST GetLoginGuidance {loginTraceID} → {Result.LoginHost}
|
||||||
// 2) Browser opens ${loginHost}/authorization?client_id=...&login_trace_id=...&auth_callback_url=${cb}
|
// 2) Browser opens ${loginHost}/authorization?client_id=...&login_trace_id=...&auth_callback_url=${cb}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import claude from "./claude.js";
|
|||||||
import codex from "./codex.js";
|
import codex from "./codex.js";
|
||||||
import xai from "./xai.js";
|
import xai from "./xai.js";
|
||||||
import grokCli from "./grok-cli.js";
|
import grokCli from "./grok-cli.js";
|
||||||
|
import muse from "./muse.js";
|
||||||
import geminiCli from "./gemini-cli.js";
|
import geminiCli from "./gemini-cli.js";
|
||||||
import antigravity from "./antigravity.js";
|
import antigravity from "./antigravity.js";
|
||||||
import iflow from "./iflow.js";
|
import iflow from "./iflow.js";
|
||||||
@@ -34,6 +35,7 @@ const PROVIDERS = {
|
|||||||
codex,
|
codex,
|
||||||
xai,
|
xai,
|
||||||
"grok-cli": grokCli,
|
"grok-cli": grokCli,
|
||||||
|
muse,
|
||||||
"gemini-cli": geminiCli,
|
"gemini-cli": geminiCli,
|
||||||
antigravity,
|
antigravity,
|
||||||
iflow,
|
iflow,
|
||||||
@@ -174,7 +176,15 @@ export async function pollForToken(providerName, deviceCode, codeVerifier, extra
|
|||||||
// Call postExchange to get additional data (copilotToken, userInfo, etc.)
|
// Call postExchange to get additional data (copilotToken, userInfo, etc.)
|
||||||
let extra = null;
|
let extra = null;
|
||||||
if (provider.postExchange) {
|
if (provider.postExchange) {
|
||||||
|
try {
|
||||||
extra = await provider.postExchange(result.data);
|
extra = await provider.postExchange(result.data);
|
||||||
|
} catch (err) {
|
||||||
|
// The grant succeeded but post-login exchange failed (e.g. Muse key
|
||||||
|
// mint). The device code is one-shot, so re-polling can never
|
||||||
|
// recover — surface as fatal so the client stops and shows the error.
|
||||||
|
console.warn(`[oauth] ${providerName} postExchange failed:`, err?.message || err);
|
||||||
|
return { success: false, error: "exchange_failed", errorDescription: err.message, fatal: true };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const tokens = provider.mapTokens(result.data, extra);
|
const tokens = provider.mapTokens(result.data, extra);
|
||||||
// Kiro IDC/Builder-ID tokens lack profileArn; resolve it to avoid 403
|
// Kiro IDC/Builder-ID tokens lack profileArn; resolve it to avoid 403
|
||||||
|
|||||||
@@ -0,0 +1,132 @@
|
|||||||
|
import { MUSE_CONFIG } from "../constants/oauth.js";
|
||||||
|
|
||||||
|
// Muse Code subscription — Meta account device code flow to auth.meta.com,
|
||||||
|
// then mint the Model API key (LLM|…) the chat transport actually uses.
|
||||||
|
const MUSE_KEY_URL = "https://api.meta.ai/muse-code/key";
|
||||||
|
const API_VERSION = "1.0.0";
|
||||||
|
|
||||||
|
const muse = {
|
||||||
|
config: MUSE_CONFIG,
|
||||||
|
flowType: "device_code",
|
||||||
|
requestDeviceCode: async (config) => {
|
||||||
|
const response = await fetch(config.deviceCodeUrl, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
Accept: "application/json",
|
||||||
|
"x-api-version": API_VERSION,
|
||||||
|
},
|
||||||
|
body: new URLSearchParams({ client_id: config.clientId }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const error = await response.text();
|
||||||
|
throw new Error(`Muse Code device code request failed: ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
},
|
||||||
|
pollToken: async (config, deviceCode) => {
|
||||||
|
const response = await fetch(config.tokenUrl, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
Accept: "application/json",
|
||||||
|
"x-api-version": API_VERSION,
|
||||||
|
},
|
||||||
|
body: new URLSearchParams({
|
||||||
|
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
||||||
|
device_code: deviceCode,
|
||||||
|
client_id: config.clientId,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
let data;
|
||||||
|
try {
|
||||||
|
data = await response.json();
|
||||||
|
} catch {
|
||||||
|
const text = await response.text();
|
||||||
|
data = { error: "invalid_response", error_description: text };
|
||||||
|
}
|
||||||
|
|
||||||
|
const pending =
|
||||||
|
data?.error === "authorization_pending" ||
|
||||||
|
data?.error === "slow_down";
|
||||||
|
return { ok: response.ok || pending, data };
|
||||||
|
},
|
||||||
|
postExchange: async (tokens) => {
|
||||||
|
// Mint the subscription API key; onboard:true enrolls the account on first
|
||||||
|
// login. The endpoint is aggressively rate-limited (429) and the device code
|
||||||
|
// is one-shot, so retry transient failures here instead of failing the login.
|
||||||
|
let response;
|
||||||
|
for (let attempt = 0; ; attempt++) {
|
||||||
|
response = await fetch(MUSE_KEY_URL, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Accept: "application/json",
|
||||||
|
Authorization: `Bearer ${tokens.access_token}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"x-api-version": API_VERSION,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ onboard: true }),
|
||||||
|
redirect: "error",
|
||||||
|
});
|
||||||
|
const transient = response.status === 429 || response.status >= 500;
|
||||||
|
if (!transient || attempt >= 2) break;
|
||||||
|
await new Promise((r) => setTimeout(r, 5000 * (attempt + 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = await response.text();
|
||||||
|
if (!response.ok) {
|
||||||
|
// Meta's error envelope (e.g. 403 code 4705002) carries the fix-it URL
|
||||||
|
let msg = `${response.status} ${text.slice(0, 200)}`;
|
||||||
|
try {
|
||||||
|
const err = JSON.parse(text);
|
||||||
|
if (err?.title || err?.detail) {
|
||||||
|
msg = [err.title, err.detail].filter(Boolean).join(": ");
|
||||||
|
if (err.action_url) msg += ` — ${err.action_url}`;
|
||||||
|
}
|
||||||
|
} catch { /* non-JSON error body */ }
|
||||||
|
throw new Error(`Muse Code key mint failed: ${msg}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let payload;
|
||||||
|
try {
|
||||||
|
payload = JSON.parse(text);
|
||||||
|
} catch {
|
||||||
|
throw new Error("Muse Code key mint returned invalid JSON");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload.is_subs_active === false) {
|
||||||
|
throw new Error("Muse Code subscription is inactive — activate it on muse.ai first");
|
||||||
|
}
|
||||||
|
const actionUrl = payload.action_url || payload.require_payment_action_url;
|
||||||
|
if (!payload.api_key && (payload.require_payment || actionUrl)) {
|
||||||
|
throw new Error(`Muse Code subscription required${actionUrl ? `: ${actionUrl}` : ""}`);
|
||||||
|
}
|
||||||
|
if (!payload.api_key) {
|
||||||
|
throw new Error("Muse Code key response is missing api_key");
|
||||||
|
}
|
||||||
|
|
||||||
|
return { key: payload };
|
||||||
|
},
|
||||||
|
mapTokens: (tokens, extra) => {
|
||||||
|
const payload = extra?.key || {};
|
||||||
|
// Chat requests carry the minted Model API key, not the Meta account token.
|
||||||
|
// No expiry/refresh from Meta — a dead key means re-login.
|
||||||
|
return {
|
||||||
|
accessToken: payload.api_key,
|
||||||
|
refreshToken: null,
|
||||||
|
expiresIn: null,
|
||||||
|
email: payload.user_email?.trim().toLowerCase() || undefined,
|
||||||
|
providerSpecificData: {
|
||||||
|
authMethod: "device_code",
|
||||||
|
// Kept so a future re-mint can run without another device login
|
||||||
|
oauthAccessToken: tokens.access_token,
|
||||||
|
subscriptionTier: payload.subs_tier_name || null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default muse;
|
||||||
@@ -175,7 +175,7 @@ export default function OAuthModal({ isOpen, provider, providerInfo, onSuccess,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.error === "expired_token" || data.error === "access_denied") {
|
if (data.error === "expired_token" || data.error === "access_denied" || data.fatal) {
|
||||||
throw new Error(data.errorDescription || data.error);
|
throw new Error(data.errorDescription || data.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,6 +291,7 @@ export default function OAuthModal({ isOpen, provider, providerInfo, onSuccess,
|
|||||||
"qoder",
|
"qoder",
|
||||||
"qoder-cn",
|
"qoder-cn",
|
||||||
"grok-cli",
|
"grok-cli",
|
||||||
|
"muse",
|
||||||
];
|
];
|
||||||
if (deviceCodeProviders.includes(provider)) {
|
if (deviceCodeProviders.includes(provider)) {
|
||||||
setIsDeviceCode(true);
|
setIsDeviceCode(true);
|
||||||
|
|||||||
@@ -116,7 +116,12 @@
|
|||||||
"devin": "devin",
|
"devin": "devin",
|
||||||
"devin-cli": "devin-cli",
|
"devin-cli": "devin-cli",
|
||||||
"morph": "morph",
|
"morph": "morph",
|
||||||
"morphllm": "morph"
|
"morphllm": "morph",
|
||||||
|
"muse": "muse",
|
||||||
|
"muse-ai": "muse",
|
||||||
|
"meta-model-api": "muse",
|
||||||
|
"muse-code": "muse",
|
||||||
|
"muse-subscription": "muse"
|
||||||
},
|
},
|
||||||
"idToAlias": {
|
"idToAlias": {
|
||||||
"agnes": "agnes",
|
"agnes": "agnes",
|
||||||
@@ -176,6 +181,7 @@
|
|||||||
"mistral": "mistral",
|
"mistral": "mistral",
|
||||||
"mmf": "mmf",
|
"mmf": "mmf",
|
||||||
"morph": "morph",
|
"morph": "morph",
|
||||||
|
"muse": "muse",
|
||||||
"nanobanana": "nanobanana",
|
"nanobanana": "nanobanana",
|
||||||
"nebius": "nebius",
|
"nebius": "nebius",
|
||||||
"nvidia": "nvidia",
|
"nvidia": "nvidia",
|
||||||
@@ -211,6 +217,7 @@
|
|||||||
"modelKeys": [
|
"modelKeys": [
|
||||||
"af",
|
"af",
|
||||||
"ag",
|
"ag",
|
||||||
|
"agnes",
|
||||||
"alicode",
|
"alicode",
|
||||||
"alicode-intl",
|
"alicode-intl",
|
||||||
"alims-intl",
|
"alims-intl",
|
||||||
@@ -270,6 +277,7 @@
|
|||||||
"mistral",
|
"mistral",
|
||||||
"mmf",
|
"mmf",
|
||||||
"morph",
|
"morph",
|
||||||
|
"muse",
|
||||||
"nanobanana",
|
"nanobanana",
|
||||||
"nebius",
|
"nebius",
|
||||||
"nvidia",
|
"nvidia",
|
||||||
@@ -302,6 +310,7 @@
|
|||||||
"together",
|
"together",
|
||||||
"tokenharbor",
|
"tokenharbor",
|
||||||
"tokenrouter",
|
"tokenrouter",
|
||||||
|
"v1m",
|
||||||
"venice",
|
"venice",
|
||||||
"vertex",
|
"vertex",
|
||||||
"vertex-partner",
|
"vertex-partner",
|
||||||
|
|||||||
@@ -121,7 +121,9 @@
|
|||||||
"usage": {
|
"usage": {
|
||||||
"oauthUrl": "https://api.anthropic.com/api/oauth/usage",
|
"oauthUrl": "https://api.anthropic.com/api/oauth/usage",
|
||||||
"orgUrl": "https://api.anthropic.com/v1/organizations/{org_id}/usage",
|
"orgUrl": "https://api.anthropic.com/v1/organizations/{org_id}/usage",
|
||||||
"settingsUrl": "https://api.anthropic.com/v1/settings"
|
"settingsUrl": "https://api.anthropic.com/v1/settings",
|
||||||
|
"profileUrl": "https://api.anthropic.com/api/oauth/profile",
|
||||||
|
"resetUrl": "https://api.anthropic.com/api/organizations/{org_id}/reset_rate_limits"
|
||||||
},
|
},
|
||||||
"clientId": "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
|
"clientId": "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
|
||||||
"tokenUrl": "https://api.anthropic.com/v1/oauth/token"
|
"tokenUrl": "https://api.anthropic.com/v1/oauth/token"
|
||||||
@@ -199,10 +201,11 @@
|
|||||||
"baseUrl": "https://chatgpt.com/backend-api/codex/responses",
|
"baseUrl": "https://chatgpt.com/backend-api/codex/responses",
|
||||||
"format": "openai-responses",
|
"format": "openai-responses",
|
||||||
"forceStream": true,
|
"forceStream": true,
|
||||||
"cliVersion": "0.154.0",
|
"cliVersion": "0.155.0",
|
||||||
"headers": {
|
"headers": {
|
||||||
"originator": "codex_cli_rs",
|
"originator": "codex_cli_rs",
|
||||||
"User-Agent": "codex_cli_rs/0.154.0"
|
"User-Agent": "codex_cli_rs/0.155.0",
|
||||||
|
"version": "0.155.0"
|
||||||
},
|
},
|
||||||
"usage": {
|
"usage": {
|
||||||
"url": "https://chatgpt.com/backend-api/wham/usage",
|
"url": "https://chatgpt.com/backend-api/wham/usage",
|
||||||
@@ -1086,6 +1089,14 @@
|
|||||||
},
|
},
|
||||||
"format": "openai"
|
"format": "openai"
|
||||||
},
|
},
|
||||||
|
"tokenharbor": {
|
||||||
|
"baseUrl": "https://tokenharbor.ai/v1/chat/completions",
|
||||||
|
"validateUrl": "https://tokenharbor.ai/v1/models",
|
||||||
|
"retry": {
|
||||||
|
"429": 2
|
||||||
|
},
|
||||||
|
"format": "openai"
|
||||||
|
},
|
||||||
"dahl": {
|
"dahl": {
|
||||||
"baseUrl": "https://inference.dahl.global/v1/chat/completions",
|
"baseUrl": "https://inference.dahl.global/v1/chat/completions",
|
||||||
"validateUrl": "https://inference.dahl.global/v1/models",
|
"validateUrl": "https://inference.dahl.global/v1/models",
|
||||||
@@ -1106,12 +1117,46 @@
|
|||||||
"validateUrl": "https://api.b.ai/v1/models",
|
"validateUrl": "https://api.b.ai/v1/models",
|
||||||
"format": "openai"
|
"format": "openai"
|
||||||
},
|
},
|
||||||
"tokenharbor": {
|
"muse": {
|
||||||
"baseUrl": "https://tokenharbor.ai/v1/chat/completions",
|
"baseUrl": "https://api.meta.ai/v1/chat/completions",
|
||||||
"validateUrl": "https://tokenharbor.ai/v1/models",
|
"validateUrl": "https://api.meta.ai/v1/models",
|
||||||
"retry": {
|
"modelsUrl": "https://api.meta.ai/v1/models",
|
||||||
"429": 2
|
"auth": {
|
||||||
|
"combined": true,
|
||||||
|
"header": "Authorization",
|
||||||
|
"scheme": "bearer",
|
||||||
|
"hooks": [
|
||||||
|
"museHeaders"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"format": "openai"
|
"format": "openai",
|
||||||
|
"clientId": "1031625952748946",
|
||||||
|
"tokenUrl": "https://auth.meta.com/oidc/device/token/",
|
||||||
|
"transports": [
|
||||||
|
{
|
||||||
|
"format": "openai",
|
||||||
|
"baseUrl": "https://api.meta.ai/v1/chat/completions",
|
||||||
|
"auth": {
|
||||||
|
"combined": true,
|
||||||
|
"header": "Authorization",
|
||||||
|
"scheme": "bearer",
|
||||||
|
"hooks": [
|
||||||
|
"museHeaders"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"format": "openai-responses",
|
||||||
|
"baseUrl": "https://api.meta.ai/v1/responses",
|
||||||
|
"auth": {
|
||||||
|
"combined": true,
|
||||||
|
"header": "Authorization",
|
||||||
|
"scheme": "bearer",
|
||||||
|
"hooks": [
|
||||||
|
"museHeaders"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,6 +23,8 @@ const ALIAS_TOKENS = [
|
|||||||
"af","airforce","api-airforce","llm7","llm-7","samba","sambanova","bm","bluesminds",
|
"af","airforce","api-airforce","llm7","llm-7","samba","sambanova","bm","bluesminds",
|
||||||
"bzl","bazaarlink","kgw","kilo-gateway","hunyuan","tencent","qianfan","baidu","ernie",
|
"bzl","bazaarlink","kgw","kilo-gateway","hunyuan","tencent","qianfan","baidu","ernie",
|
||||||
"dv","devin","devin-cli","morph","morphllm",
|
"dv","devin","devin-cli","morph","morphllm",
|
||||||
|
"muse","muse-ai","meta-model-api",
|
||||||
|
"muse-code","muse-subscription",
|
||||||
];
|
];
|
||||||
|
|
||||||
// Sort idToAlias by key — runtime accesses by key, order is irrelevant (content-based)
|
// Sort idToAlias by key — runtime accesses by key, order is irrelevant (content-based)
|
||||||
|
|||||||
Reference in new issue
Block a user