@fudrouter/fsrouter 0.6.77 → 0.6.78
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/dist/lib/oauth/constants/oauth.js +2 -0
- package/dist/lib/oauth/providers.js +72 -0
- package/dist/open-sse/providers/index.js +1 -1
- package/dist/open-sse/providers/registry/nous-research.js +10 -2
- package/dist/routes/headroom/extras/route.js +2 -2
- package/dist/shared/constants/providers.js +1 -1
- package/open-sse/providers/index.js +1 -1
- package/open-sse/providers/registry/nous-research.js +10 -2
- package/package.json +1 -1
- package/public/.vite/manifest.json +197 -85
- package/public/assets/EndpointPageClient.BNiu_vhL.js +6 -0
- package/public/assets/Loading.CRvAR61e.js +1 -0
- package/public/assets/NoAuthProxyCard.BARTPHmg.js +89 -0
- package/public/assets/index.BbQSujX0.js +89 -0
- package/public/assets/page.2cfQ0soc.js +5 -0
- package/public/assets/page.Azr84Ml-.js +1 -0
- package/public/assets/page.B57i00nL.js +1 -0
- package/public/assets/page.BTTzRlp6.js +1 -0
- package/public/assets/page.BY5IysJR.js +2 -0
- package/public/assets/page.BYy5vdYY.js +1 -0
- package/public/assets/page.Bkhxy2VJ.js +1 -0
- package/public/assets/page.BrLVxn10.js +1 -0
- package/public/assets/page.C0TLs5yW.js +1 -0
- package/public/assets/page.C1MGHZJw.js +1 -0
- package/public/assets/page.C5qOhBBX.js +1 -0
- package/public/assets/page.CL7pbImn.js +1 -0
- package/public/assets/page.CLbi_S1p.js +1 -0
- package/public/assets/page.CNUurvtj.js +23 -0
- package/public/assets/page.CWyZKaVw.js +31 -0
- package/public/assets/page.CXKvW0hn.js +1 -0
- package/public/assets/page.CXXS5Pj6.js +4 -0
- package/public/assets/page.CavjztiR.js +1 -0
- package/public/assets/page.CiURc2ed.js +64 -0
- package/public/assets/page.CjbJHAqY.js +1 -0
- package/public/assets/page.CqKdcCXs.js +5 -0
- package/public/assets/page.CuZp1ItQ.js +1 -0
- package/public/assets/page.Cx7iFgCR.js +1 -0
- package/public/assets/page.D023izTv.js +6 -0
- package/public/assets/page.D5dHsYMc.js +1 -0
- package/public/assets/page.DIBe8_S7.js +1 -0
- package/public/assets/page.DOS_9ovb.js +1 -0
- package/public/assets/page.DTfhMnYX.js +1 -0
- package/public/assets/page.D_dG62bf.js +1 -0
- package/public/assets/page.DjsFF4p7.js +1 -0
- package/public/assets/page.DmjQJ44n.js +6 -0
- package/public/assets/page.Dsd5yK6z.js +1 -0
- package/public/assets/page.EBdicyw3.js +2 -0
- package/public/assets/page.K07l9G5d.js +1 -0
- package/public/assets/page.N_CNyJfs.js +1 -0
- package/public/assets/page.cIm7s2zC.js +1 -0
- package/public/assets/page.u8Bt3ap8.js +1 -0
- package/public/index.html +1 -1
|
@@ -118,6 +118,8 @@ export const KIMCHI_CONFIG = { ...PROVIDER_OAUTH["kimchi"] };
|
|
|
118
118
|
// Endpoint: cli-chat-proxy.grok.com — same client_id as xai, different flow + scopes
|
|
119
119
|
export const GROK_CLI_CONFIG = { ...PROVIDER_OAUTH["grok-cli"] };
|
|
120
120
|
|
|
121
|
+
export const NOUS_CONFIG = { ...PROVIDER_OAUTH["nous-research"] };
|
|
122
|
+
|
|
121
123
|
// OAuth timeout (5 minutes)
|
|
122
124
|
export const OAUTH_TIMEOUT = 300000;
|
|
123
125
|
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
CODEBUDDY_CONFIG,
|
|
29
29
|
KIMCHI_CONFIG,
|
|
30
30
|
GROK_CLI_CONFIG,
|
|
31
|
+
NOUS_CONFIG,
|
|
31
32
|
getOAuthClientMetadata,
|
|
32
33
|
} from "./constants/oauth.js";
|
|
33
34
|
import { XAI_CONFIG, XAI_PKCE_VERIFIER_BYTES } from "./constants/xai.js";
|
|
@@ -372,6 +373,77 @@ const PROVIDERS = {
|
|
|
372
373
|
},
|
|
373
374
|
},
|
|
374
375
|
|
|
376
|
+
// Nous Research (Nous Portal) — OAuth device code flow.
|
|
377
|
+
// Portal has no manual API-key page; auth is device-code only (client_id hermes-cli).
|
|
378
|
+
"nous-research": {
|
|
379
|
+
config: NOUS_CONFIG,
|
|
380
|
+
flowType: "device_code",
|
|
381
|
+
requestDeviceCode: async (config) => {
|
|
382
|
+
const body = new URLSearchParams({ client_id: config.clientId });
|
|
383
|
+
if (config.scope) body.set("scope", config.scope);
|
|
384
|
+
const response = await fetch(config.deviceCodeUrl, {
|
|
385
|
+
method: "POST",
|
|
386
|
+
headers: {
|
|
387
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
388
|
+
Accept: "application/json",
|
|
389
|
+
},
|
|
390
|
+
body,
|
|
391
|
+
});
|
|
392
|
+
if (!response.ok) {
|
|
393
|
+
const error = await response.text();
|
|
394
|
+
throw new Error(`Nous device code request failed: ${error}`);
|
|
395
|
+
}
|
|
396
|
+
return await response.json();
|
|
397
|
+
},
|
|
398
|
+
pollToken: async (config, deviceCode) => {
|
|
399
|
+
const response = await fetch(config.tokenUrl, {
|
|
400
|
+
method: "POST",
|
|
401
|
+
headers: {
|
|
402
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
403
|
+
Accept: "application/json",
|
|
404
|
+
},
|
|
405
|
+
body: new URLSearchParams({
|
|
406
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
407
|
+
device_code: deviceCode,
|
|
408
|
+
client_id: config.clientId,
|
|
409
|
+
}),
|
|
410
|
+
});
|
|
411
|
+
let data;
|
|
412
|
+
try {
|
|
413
|
+
data = await response.json();
|
|
414
|
+
} catch {
|
|
415
|
+
const text = await response.text();
|
|
416
|
+
data = { error: "invalid_response", error_description: text };
|
|
417
|
+
}
|
|
418
|
+
const pending =
|
|
419
|
+
data?.error === "authorization_pending" || data?.error === "slow_down";
|
|
420
|
+
return { ok: response.ok || pending, data };
|
|
421
|
+
},
|
|
422
|
+
mapTokens: (tokens) => {
|
|
423
|
+
let email = null;
|
|
424
|
+
try {
|
|
425
|
+
const parts = String(tokens.access_token || "").split(".");
|
|
426
|
+
if (parts.length >= 2) {
|
|
427
|
+
const b64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
428
|
+
const padded = b64 + "=".repeat((4 - (b64.length % 4)) % 4);
|
|
429
|
+
const claims = JSON.parse(Buffer.from(padded, "base64").toString("utf8"));
|
|
430
|
+
email = claims.email || claims.sub || null;
|
|
431
|
+
}
|
|
432
|
+
} catch { /* ignore */ }
|
|
433
|
+
return {
|
|
434
|
+
accessToken: tokens.access_token,
|
|
435
|
+
refreshToken: tokens.refresh_token || null,
|
|
436
|
+
expiresIn: tokens.expires_in,
|
|
437
|
+
scope: tokens.scope,
|
|
438
|
+
email: email || undefined,
|
|
439
|
+
providerSpecificData: {
|
|
440
|
+
authMethod: "device_code",
|
|
441
|
+
email: email || null,
|
|
442
|
+
},
|
|
443
|
+
};
|
|
444
|
+
},
|
|
445
|
+
},
|
|
446
|
+
|
|
375
447
|
"gemini-cli": {
|
|
376
448
|
config: GEMINI_CONFIG,
|
|
377
449
|
flowType: "authorization_code",
|
|
@@ -6,7 +6,7 @@ import { buildTtsProviderModels } from "../config/ttsModels.js";
|
|
|
6
6
|
|
|
7
7
|
// oauth block is canonical for these fields; inject into transport so executors reading
|
|
8
8
|
// this.config.{clientId,clientSecret,tokenUrl} keep working without duplicating in transport
|
|
9
|
-
const OAUTH_INJECT_FIELDS = ["clientId", "clientSecret", "tokenUrl"];
|
|
9
|
+
const OAUTH_INJECT_FIELDS = ["clientId", "clientSecret", "tokenUrl", "refreshUrl"];
|
|
10
10
|
|
|
11
11
|
// transport: re-apply shared default (format:"openai") + inject oauth-canonical fields
|
|
12
12
|
function buildTransport(transport, oauth) {
|
|
@@ -14,8 +14,16 @@ export default {
|
|
|
14
14
|
apiKeyUrl: "https://portal.nousresearch.com",
|
|
15
15
|
},
|
|
16
16
|
},
|
|
17
|
-
category: "
|
|
18
|
-
authModes: ["
|
|
17
|
+
category: "oauth",
|
|
18
|
+
authModes: ["oauth"],
|
|
19
|
+
oauth: {
|
|
20
|
+
clientId: "hermes-cli",
|
|
21
|
+
deviceCodeUrl: "https://portal.nousresearch.com/api/oauth/device/code",
|
|
22
|
+
tokenUrl: "https://portal.nousresearch.com/api/oauth/token",
|
|
23
|
+
refreshUrl: "https://portal.nousresearch.com/api/oauth/token",
|
|
24
|
+
scope: "inference:invoke",
|
|
25
|
+
refreshLeadMs: 5 * 60 * 1000,
|
|
26
|
+
},
|
|
19
27
|
transport: {
|
|
20
28
|
baseUrl: "https://inference-api.nousresearch.com/v1/chat/completions",
|
|
21
29
|
format: "openai",
|
|
@@ -16,7 +16,7 @@ export async function GET(req, res) {
|
|
|
16
16
|
}
|
|
17
17
|
export async function POST(req, res) {
|
|
18
18
|
try {
|
|
19
|
-
const body =
|
|
19
|
+
const body = req.body || {};
|
|
20
20
|
const extras = Array.isArray(body.extras) ? body.extras : [];
|
|
21
21
|
if (extras.length === 0) {
|
|
22
22
|
return res.status(400).json({ error: "No extras requested" });
|
|
@@ -37,7 +37,7 @@ export async function POST(req, res) {
|
|
|
37
37
|
}
|
|
38
38
|
export async function DELETE(req, res) {
|
|
39
39
|
try {
|
|
40
|
-
const body =
|
|
40
|
+
const body = req.body || {};
|
|
41
41
|
const extras = Array.isArray(body.extras) ? body.extras : [];
|
|
42
42
|
if (extras.length === 0) {
|
|
43
43
|
return res.status(400).json({ error: "No extras requested" });
|
|
@@ -147,7 +147,7 @@ export const APIKEY_PROVIDERS = {
|
|
|
147
147
|
// nscale: { id: "nscale", alias: "nscale", name: "nScale", icon: "token", color: "#0891B2", textIcon: "NS", website: "https://nscale.com", notice: { text: "$5 free credits on signup.", apiKeyUrl: "https://console.nscale.com/api-keys" }, serviceKinds: ["llm"] },
|
|
148
148
|
// baseten: { id: "baseten", alias: "baseten", name: "Baseten", icon: "deployed_code", color: "#111827", textIcon: "BT", website: "https://baseten.co", notice: { text: "$30 free trial credits for GPU inference.", apiKeyUrl: "https://app.baseten.co/settings/api_keys" }, serviceKinds: ["llm"] },
|
|
149
149
|
// publicai: { id: "publicai", alias: "publicai", name: "PublicAI", icon: "public", color: "#059669", textIcon: "PA", website: "https://publicai.co", notice: { text: "Free community inference tier.", apiKeyUrl: "https://publicai.co" }, serviceKinds: ["llm"] },
|
|
150
|
-
"nous-research": { id: "nous-research", alias: "nous", name: "Nous Research", icon: "hub", color: "#2563EB", textIcon: "NO", website: "https://portal.nousresearch.com", notice: { text: "Free tier: 50 RPM, 500K TPM — no credit card.",
|
|
150
|
+
"nous-research": { id: "nous-research", alias: "nous", name: "Nous Research", icon: "hub", color: "#2563EB", textIcon: "NO", website: "https://portal.nousresearch.com", notice: { text: "Free tier: 50 RPM, 500K TPM — no credit card. Sign in with your Nous Portal account (device login, no API key needed).", signupUrl: "https://portal.nousresearch.com" }, serviceKinds: ["llm"], authModes: ["oauth"], hasOAuth: true },
|
|
151
151
|
// glhf: { id: "glhf", alias: "glhf", name: "GLHF Chat", icon: "hub", color: "#10B981", textIcon: "GH", website: "https://glhf.chat", notice: { text: "Free tier for open-source model inference.", apiKeyUrl: "https://glhf.chat/users/settings/api" }, passthroughModels: true, serviceKinds: ["llm"] },
|
|
152
152
|
"ollama-local": { id: "ollama-local", alias: "ollama-local", name: "Ollama Local", icon: "cloud", color: "#ffffffff", textIcon: "OL", website: "https://ollama.com" },
|
|
153
153
|
"vertex-partner": { id: "vertex-partner", alias: "vxp", name: "Vertex Partner", icon: "cloud", color: "#34A853", textIcon: "VP", website: "https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-partner-models", notice: { apiKeyUrl: "https://console.cloud.google.com/iam-admin/serviceaccounts" } },
|
|
@@ -6,7 +6,7 @@ import { buildTtsProviderModels } from "../config/ttsModels.js";
|
|
|
6
6
|
|
|
7
7
|
// oauth block is canonical for these fields; inject into transport so executors reading
|
|
8
8
|
// this.config.{clientId,clientSecret,tokenUrl} keep working without duplicating in transport
|
|
9
|
-
const OAUTH_INJECT_FIELDS = ["clientId", "clientSecret", "tokenUrl"];
|
|
9
|
+
const OAUTH_INJECT_FIELDS = ["clientId", "clientSecret", "tokenUrl", "refreshUrl"];
|
|
10
10
|
|
|
11
11
|
// transport: re-apply shared default (format:"openai") + inject oauth-canonical fields
|
|
12
12
|
function buildTransport(transport, oauth) {
|
|
@@ -14,8 +14,16 @@ export default {
|
|
|
14
14
|
apiKeyUrl: "https://portal.nousresearch.com",
|
|
15
15
|
},
|
|
16
16
|
},
|
|
17
|
-
category: "
|
|
18
|
-
authModes: ["
|
|
17
|
+
category: "oauth",
|
|
18
|
+
authModes: ["oauth"],
|
|
19
|
+
oauth: {
|
|
20
|
+
clientId: "hermes-cli",
|
|
21
|
+
deviceCodeUrl: "https://portal.nousresearch.com/api/oauth/device/code",
|
|
22
|
+
tokenUrl: "https://portal.nousresearch.com/api/oauth/token",
|
|
23
|
+
refreshUrl: "https://portal.nousresearch.com/api/oauth/token",
|
|
24
|
+
scope: "inference:invoke",
|
|
25
|
+
refreshLeadMs: 5 * 60 * 1000,
|
|
26
|
+
},
|
|
19
27
|
transport: {
|
|
20
28
|
baseUrl: "https://inference-api.nousresearch.com/v1/chat/completions",
|
|
21
29
|
format: "openai",
|