@fudrouter/fsrouter 0.6.84 → 0.6.85
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/open-sse/handlers/ttsProviders/index.js +1 -1
- package/dist/open-sse/services/usage/nous.js +36 -0
- package/dist/open-sse/services/usage.js +2 -0
- package/dist/open-sse/shared/clineAuth.js +21 -2
- package/dist/open-sse/utils/error.js +14 -0
- package/dist/routes/providers/[id]/test/testUtils.js +11 -11
- package/dist/routes/usage/[connectionId]/route.js +1 -1
- package/open-sse/handlers/ttsProviders/index.js +1 -1
- package/open-sse/services/usage/nous.js +36 -0
- package/open-sse/services/usage.js +2 -0
- package/open-sse/shared/clineAuth.js +21 -2
- package/open-sse/utils/error.js +14 -0
- package/package.json +1 -1
- package/public/.vite/manifest.json +85 -85
- package/public/assets/EndpointPageClient.B6118Vl0.js +6 -0
- package/public/assets/Loading.CKcPqzyp.js +1 -0
- package/public/assets/NoAuthProxyCard.C8gs3p9D.js +89 -0
- package/public/assets/index.CemlR_k0.js +89 -0
- package/public/assets/page.B8uovCa-.js +2 -0
- package/public/assets/page.B9AYvEAq.js +1 -0
- package/public/assets/page.BC8_Skab.js +1 -0
- package/public/assets/page.BGiDmMTs.js +1 -0
- package/public/assets/page.BH0jk80k.js +1 -0
- package/public/assets/page.BIo8aNfA.js +64 -0
- package/public/assets/page.BJ6h2zDl.js +1 -0
- package/public/assets/page.BO2F41Lc.js +23 -0
- package/public/assets/page.BPX2jyWY.js +1 -0
- package/public/assets/page.BSjBIFVt.js +1 -0
- package/public/assets/page.B_Tgnl-b.js +5 -0
- package/public/assets/page.BaHkKogf.js +1 -0
- package/public/assets/page.BdfPcUya.js +1 -0
- package/public/assets/page.BrhMwL3Z.js +2 -0
- package/public/assets/page.BwjqeY_m.js +6 -0
- package/public/assets/page.CDVWVylS.js +1 -0
- package/public/assets/page.CEw2SuPk.js +4 -0
- package/public/assets/page.CQU7BVUj.js +1 -0
- package/public/assets/page.CvBBDiKu.js +1 -0
- package/public/assets/page.CzOg3TqA.js +5 -0
- package/public/assets/page.D2Y19JAi.js +1 -0
- package/public/assets/page.DAzdg8fd.js +1 -0
- package/public/assets/page.DIb0IloM.js +1 -0
- package/public/assets/page.DO1n3ZJL.js +1 -0
- package/public/assets/page.DOsZhkAT.js +1 -0
- package/public/assets/page.DP_7ZXFk.js +1 -0
- package/public/assets/page.DliO3TaK.js +1 -0
- package/public/assets/page.DnJi9BLz.js +1 -0
- package/public/assets/page.DomMS7Cy.js +1 -0
- package/public/assets/page.DraAfhp6.js +31 -0
- package/public/assets/page.FJ3Dx8uu.js +1 -0
- package/public/assets/page.LtaYaOOQ.js +6 -0
- package/public/assets/page.SRSYeHUs.js +1 -0
- package/public/assets/page.rQUdj203.js +1 -0
- package/public/assets/page.sVpnihMe.js +1 -0
- package/public/assets/page.x5y6h1mx.js +1 -0
- package/public/assets/page.z06UnjTW.js +1 -0
- package/public/index.html +1 -1
|
@@ -33,7 +33,7 @@ export async function synthesizeViaConfig(provider, text, model, credentials) {
|
|
|
33
33
|
if (!handler) return null;
|
|
34
34
|
const apiKey = credentials?.apiKey;
|
|
35
35
|
if (cfg.authType !== "none" && !apiKey) throw new Error(`${provider} API key required`);
|
|
36
|
-
const { PROVIDER_MODELS } = await import(
|
|
36
|
+
const { PROVIDER_MODELS } = await import('../../../open-sse/config/providerModels.js');
|
|
37
37
|
const ttsModels = (PROVIDER_MODELS[provider] || []).filter(m => (m.kind || m.type) === "tts");
|
|
38
38
|
const defaultModel = ttsModels[0]?.id || "";
|
|
39
39
|
const { modelId, voiceId } = parseModelVoice(model, defaultModel, "", ttsModels);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nous Research usage/quota fetcher.
|
|
3
|
+
*
|
|
4
|
+
* Nous Portal free tier (no credit card): 50 RPM, 500K TPM.
|
|
5
|
+
* Nous does not expose a public per-account usage/quota API, so we surface the
|
|
6
|
+
* published free-tier limits as a static quota entry. Requests count against
|
|
7
|
+
* these limits and reset on a rolling window; without a live counter we report
|
|
8
|
+
* the plan cap and leave `remaining` open (Infinity) so the UI shows the tier.
|
|
9
|
+
*
|
|
10
|
+
* @param {string} accessToken - OAuth Bearer token (unused for static tier info)
|
|
11
|
+
* @returns {Promise<{quotas: Array, plan: string}>}
|
|
12
|
+
*/
|
|
13
|
+
export async function getNousUsage(accessToken) {
|
|
14
|
+
void accessToken; // reserved for future live quota endpoint
|
|
15
|
+
return {
|
|
16
|
+
plan: "Free Tier (Nous Portal)",
|
|
17
|
+
quotas: [
|
|
18
|
+
{
|
|
19
|
+
label: "Requests / min",
|
|
20
|
+
total: 50,
|
|
21
|
+
used: 0,
|
|
22
|
+
remaining: 50,
|
|
23
|
+
unit: "rpm",
|
|
24
|
+
resetAt: null,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
label: "Tokens / min",
|
|
28
|
+
total: 500000,
|
|
29
|
+
used: 0,
|
|
30
|
+
remaining: 500000,
|
|
31
|
+
unit: "tpm",
|
|
32
|
+
resetAt: null,
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -12,6 +12,7 @@ import { getKiroUsage } from "./usage/kiro.js";
|
|
|
12
12
|
import { getMiniMaxUsage } from "./usage/minimax.js";
|
|
13
13
|
import { getCodeBuddyCnUsage } from "./usage/codebuddy-cn.js";
|
|
14
14
|
import { getGrokCliUsage } from "./usage/grok-cli.js";
|
|
15
|
+
import { getNousUsage } from "./usage/nous.js";
|
|
15
16
|
import {
|
|
16
17
|
getQwenUsage,
|
|
17
18
|
getIflowUsage,
|
|
@@ -45,6 +46,7 @@ const USAGE_HANDLERS = {
|
|
|
45
46
|
"vercel-ai-gateway": (c) => getVercelAiGatewayUsage(c.apiKey, c.proxyOptions),
|
|
46
47
|
"codebuddy-cn": (c) => getCodeBuddyCnUsage(c.accessToken, c.apiKey, c.providerSpecificData, c.proxyOptions),
|
|
47
48
|
"grok-cli": (c) => getGrokCliUsage(c.accessToken, c.providerSpecificData, c.proxyOptions),
|
|
49
|
+
"nous-research": (c) => getNousUsage(c.accessToken),
|
|
48
50
|
};
|
|
49
51
|
|
|
50
52
|
export async function getUsageForProvider(connection, proxyOptions = null) {
|
|
@@ -1,6 +1,25 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { createRequire } from "module";
|
|
2
|
+
import { fileURLToPath } from "url";
|
|
3
|
+
import { dirname } from "path";
|
|
4
|
+
import { existsSync } from "fs";
|
|
2
5
|
|
|
3
|
-
const
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
|
|
8
|
+
// Resolve package.json by walking up — works in both src (open-sse/shared)
|
|
9
|
+
// and dist (dist/open-sse/shared) layouts.
|
|
10
|
+
function resolvePkgPath() {
|
|
11
|
+
let d = __dirname;
|
|
12
|
+
for (let i = 0; i < 6; i++) {
|
|
13
|
+
const p = `${d}/package.json`;
|
|
14
|
+
if (existsSync(p)) return p;
|
|
15
|
+
d = dirname(d);
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const _require = createRequire(import.meta.url);
|
|
21
|
+
const _pkgPath = resolvePkgPath();
|
|
22
|
+
const APP_VERSION = _pkgPath ? _require(_pkgPath).version || "0.0.0" : "0.0.0";
|
|
4
23
|
|
|
5
24
|
export function getClineAccessToken(token) {
|
|
6
25
|
if (typeof token !== "string") return "";
|
|
@@ -145,3 +145,17 @@ export function formatProviderError(error, provider, model, statusCode) {
|
|
|
145
145
|
const causeStr = causeCode || causeMsg ? ` (cause: ${[causeCode, causeMsg].filter(Boolean).join(": ")})` : "";
|
|
146
146
|
return `[${code}]: ${message}${causeStr}`;
|
|
147
147
|
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Strip internal details from an error message so they don't leak upstream paths.
|
|
151
|
+
* @param {unknown} message
|
|
152
|
+
* @returns {string}
|
|
153
|
+
*/
|
|
154
|
+
export function sanitizeErrorMessage(message) {
|
|
155
|
+
if (message == null) return "";
|
|
156
|
+
const str = typeof message === "string" ? message : String(message);
|
|
157
|
+
// Remove absolute filesystem paths (e.g. /usr/local/lib/node_modules/.../file.js)
|
|
158
|
+
return str.replace(/\/[\w./-]+\.(js|ts|mjs|cjs)(:\d+:\d+)?/g, (m) =>
|
|
159
|
+
m.length > 40 ? "[internal]" : m,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { getProviderConnectionById, updateProviderConnection } from '
|
|
2
|
-
import { resolveConnectionProxyConfig } from '
|
|
3
|
-
import { testProxyUrl } from '
|
|
4
|
-
import { isOpenAICompatibleProvider, isAnthropicCompatibleProvider } from '
|
|
5
|
-
import { PROVIDER_ENDPOINTS } from '
|
|
1
|
+
import { getProviderConnectionById, updateProviderConnection } from '../../../../../dist/lib/localDb.js';
|
|
2
|
+
import { resolveConnectionProxyConfig } from '../../../../../dist/lib/network/connectionProxy.js';
|
|
3
|
+
import { testProxyUrl } from '../../../../../dist/lib/network/proxyTest.js';
|
|
4
|
+
import { isOpenAICompatibleProvider, isAnthropicCompatibleProvider } from '../../../../../dist/shared/constants/providers.js';
|
|
5
|
+
import { PROVIDER_ENDPOINTS } from '../../../../../dist/shared/constants/config.js';
|
|
6
6
|
import { getDefaultModel } from '../../../../../open-sse/config/providerModels.js';
|
|
7
7
|
import { resolveOllamaLocalHost } from '../../../../../open-sse/config/providers.js';
|
|
8
8
|
import {
|
|
@@ -17,8 +17,8 @@ import {
|
|
|
17
17
|
CLAUDE_CONFIG,
|
|
18
18
|
CLINE_CONFIG,
|
|
19
19
|
KILOCODE_CONFIG,
|
|
20
|
-
} from '
|
|
21
|
-
import { buildClineHeaders } from '
|
|
20
|
+
} from '../../../../../dist/lib/oauth/constants/oauth.js';
|
|
21
|
+
import { buildClineHeaders } from '../../../../../dist/shared/utils/clineAuth.js';
|
|
22
22
|
|
|
23
23
|
// OAuth provider test endpoints
|
|
24
24
|
const OAUTH_TEST_CONFIG = {
|
|
@@ -334,7 +334,7 @@ async function testOAuthConnection(connection, effectiveProxy = null) {
|
|
|
334
334
|
async function fetchWithConnectionProxy(url, options = {}, effectiveProxy = null) {
|
|
335
335
|
// Vercel relay: forward via relay URL
|
|
336
336
|
if (effectiveProxy?.vercelRelayUrl) {
|
|
337
|
-
const { proxyAwareFetch } = await import(
|
|
337
|
+
const { proxyAwareFetch } = await import('../../../../../open-sse/utils/proxyFetch.js');
|
|
338
338
|
return proxyAwareFetch(url, options, {
|
|
339
339
|
vercelRelayUrl: effectiveProxy.vercelRelayUrl,
|
|
340
340
|
});
|
|
@@ -344,7 +344,7 @@ async function fetchWithConnectionProxy(url, options = {}, effectiveProxy = null
|
|
|
344
344
|
return fetch(url, options);
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
-
const { proxyAwareFetch } = await import(
|
|
347
|
+
const { proxyAwareFetch } = await import('../../../../../open-sse/utils/proxyFetch.js');
|
|
348
348
|
return proxyAwareFetch(url, options, {
|
|
349
349
|
connectionProxyEnabled: true,
|
|
350
350
|
connectionProxyUrl: effectiveProxy.connectionProxyUrl,
|
|
@@ -694,7 +694,7 @@ async function testApiKeyConnection(connection, effectiveProxy = null) {
|
|
|
694
694
|
return { valid, error: errorMessage };
|
|
695
695
|
}
|
|
696
696
|
case "leonardo": {
|
|
697
|
-
const { refreshLeonardoToken } = await import(
|
|
697
|
+
const { refreshLeonardoToken } = await import('../../../../../open-sse/services/tokenRefresh.js');
|
|
698
698
|
const cookie = connection.apiKey || connection.cookie;
|
|
699
699
|
if (!cookie) return { valid: false, error: "Cookie tidak ditemukan" };
|
|
700
700
|
const result = await refreshLeonardoToken(cookie, console);
|
|
@@ -715,7 +715,7 @@ async function testApiKeyConnection(connection, effectiveProxy = null) {
|
|
|
715
715
|
}
|
|
716
716
|
}
|
|
717
717
|
case "weavy": {
|
|
718
|
-
const { refreshWeavyToken } = await import(
|
|
718
|
+
const { refreshWeavyToken } = await import('../../../../../open-sse/services/tokenRefresh.js');
|
|
719
719
|
const email = connection.email || connection.name;
|
|
720
720
|
if (!email) return { valid: false, error: "Email/name tidak ditemukan" };
|
|
721
721
|
const result = await refreshWeavyToken(email, connection, console);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Ensure proxyFetch is loaded to patch globalThis.fetch
|
|
2
|
-
import
|
|
2
|
+
import '../../../../open-sse/index.js';
|
|
3
3
|
import { getProviderConnectionById, updateProviderConnection } from "../../../lib/localDb.js";
|
|
4
4
|
import { getUsageForProvider } from "../../../open-sse/services/usage.js"; // Watcher trigger comment
|
|
5
5
|
import { getExecutor } from "../../../open-sse/executors/index.js";
|
|
@@ -33,7 +33,7 @@ export async function synthesizeViaConfig(provider, text, model, credentials) {
|
|
|
33
33
|
if (!handler) return null;
|
|
34
34
|
const apiKey = credentials?.apiKey;
|
|
35
35
|
if (cfg.authType !== "none" && !apiKey) throw new Error(`${provider} API key required`);
|
|
36
|
-
const { PROVIDER_MODELS } = await import(
|
|
36
|
+
const { PROVIDER_MODELS } = await import('../../../open-sse/config/providerModels.js');
|
|
37
37
|
const ttsModels = (PROVIDER_MODELS[provider] || []).filter(m => (m.kind || m.type) === "tts");
|
|
38
38
|
const defaultModel = ttsModels[0]?.id || "";
|
|
39
39
|
const { modelId, voiceId } = parseModelVoice(model, defaultModel, "", ttsModels);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nous Research usage/quota fetcher.
|
|
3
|
+
*
|
|
4
|
+
* Nous Portal free tier (no credit card): 50 RPM, 500K TPM.
|
|
5
|
+
* Nous does not expose a public per-account usage/quota API, so we surface the
|
|
6
|
+
* published free-tier limits as a static quota entry. Requests count against
|
|
7
|
+
* these limits and reset on a rolling window; without a live counter we report
|
|
8
|
+
* the plan cap and leave `remaining` open (Infinity) so the UI shows the tier.
|
|
9
|
+
*
|
|
10
|
+
* @param {string} accessToken - OAuth Bearer token (unused for static tier info)
|
|
11
|
+
* @returns {Promise<{quotas: Array, plan: string}>}
|
|
12
|
+
*/
|
|
13
|
+
export async function getNousUsage(accessToken) {
|
|
14
|
+
void accessToken; // reserved for future live quota endpoint
|
|
15
|
+
return {
|
|
16
|
+
plan: "Free Tier (Nous Portal)",
|
|
17
|
+
quotas: [
|
|
18
|
+
{
|
|
19
|
+
label: "Requests / min",
|
|
20
|
+
total: 50,
|
|
21
|
+
used: 0,
|
|
22
|
+
remaining: 50,
|
|
23
|
+
unit: "rpm",
|
|
24
|
+
resetAt: null,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
label: "Tokens / min",
|
|
28
|
+
total: 500000,
|
|
29
|
+
used: 0,
|
|
30
|
+
remaining: 500000,
|
|
31
|
+
unit: "tpm",
|
|
32
|
+
resetAt: null,
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -12,6 +12,7 @@ import { getKiroUsage } from "./usage/kiro.js";
|
|
|
12
12
|
import { getMiniMaxUsage } from "./usage/minimax.js";
|
|
13
13
|
import { getCodeBuddyCnUsage } from "./usage/codebuddy-cn.js";
|
|
14
14
|
import { getGrokCliUsage } from "./usage/grok-cli.js";
|
|
15
|
+
import { getNousUsage } from "./usage/nous.js";
|
|
15
16
|
import {
|
|
16
17
|
getQwenUsage,
|
|
17
18
|
getIflowUsage,
|
|
@@ -45,6 +46,7 @@ const USAGE_HANDLERS = {
|
|
|
45
46
|
"vercel-ai-gateway": (c) => getVercelAiGatewayUsage(c.apiKey, c.proxyOptions),
|
|
46
47
|
"codebuddy-cn": (c) => getCodeBuddyCnUsage(c.accessToken, c.apiKey, c.providerSpecificData, c.proxyOptions),
|
|
47
48
|
"grok-cli": (c) => getGrokCliUsage(c.accessToken, c.providerSpecificData, c.proxyOptions),
|
|
49
|
+
"nous-research": (c) => getNousUsage(c.accessToken),
|
|
48
50
|
};
|
|
49
51
|
|
|
50
52
|
export async function getUsageForProvider(connection, proxyOptions = null) {
|
|
@@ -1,6 +1,25 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { createRequire } from "module";
|
|
2
|
+
import { fileURLToPath } from "url";
|
|
3
|
+
import { dirname } from "path";
|
|
4
|
+
import { existsSync } from "fs";
|
|
2
5
|
|
|
3
|
-
const
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
|
|
8
|
+
// Resolve package.json by walking up — works in both src (open-sse/shared)
|
|
9
|
+
// and dist (dist/open-sse/shared) layouts.
|
|
10
|
+
function resolvePkgPath() {
|
|
11
|
+
let d = __dirname;
|
|
12
|
+
for (let i = 0; i < 6; i++) {
|
|
13
|
+
const p = `${d}/package.json`;
|
|
14
|
+
if (existsSync(p)) return p;
|
|
15
|
+
d = dirname(d);
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const _require = createRequire(import.meta.url);
|
|
21
|
+
const _pkgPath = resolvePkgPath();
|
|
22
|
+
const APP_VERSION = _pkgPath ? _require(_pkgPath).version || "0.0.0" : "0.0.0";
|
|
4
23
|
|
|
5
24
|
export function getClineAccessToken(token) {
|
|
6
25
|
if (typeof token !== "string") return "";
|
package/open-sse/utils/error.js
CHANGED
|
@@ -145,3 +145,17 @@ export function formatProviderError(error, provider, model, statusCode) {
|
|
|
145
145
|
const causeStr = causeCode || causeMsg ? ` (cause: ${[causeCode, causeMsg].filter(Boolean).join(": ")})` : "";
|
|
146
146
|
return `[${code}]: ${message}${causeStr}`;
|
|
147
147
|
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Strip internal details from an error message so they don't leak upstream paths.
|
|
151
|
+
* @param {unknown} message
|
|
152
|
+
* @returns {string}
|
|
153
|
+
*/
|
|
154
|
+
export function sanitizeErrorMessage(message) {
|
|
155
|
+
if (message == null) return "";
|
|
156
|
+
const str = typeof message === "string" ? message : String(message);
|
|
157
|
+
// Remove absolute filesystem paths (e.g. /usr/local/lib/node_modules/.../file.js)
|
|
158
|
+
return str.replace(/\/[\w./-]+\.(js|ts|mjs|cjs)(:\d+:\d+)?/g, (m) =>
|
|
159
|
+
m.length > 40 ? "[internal]" : m,
|
|
160
|
+
);
|
|
161
|
+
}
|