feat(providers): link web session guide to provider site (#6316)

Add an Open-provider-site link to the web-session credential guide (+host-extraction test). Integrated into release/v3.8.46.
This commit is contained in:
jordansilly77-stack
2026-07-07 04:19:47 +08:00
committed by GitHub
parent fb3da7ce98
commit b796f2b457
6 changed files with 66 additions and 0 deletions

View File

@@ -8,6 +8,7 @@
### ✨ New Features
- **feat(dashboard):** the web-session credential guide now shows an **"Open {host}" link** to the provider's sign-in site (derived from the provider `website` via `getProviderWebsiteHost`), so you can jump straight to the page where the cookie/session must be captured. Regression guard: `tests/unit/web-session-provider-link-6316.test.ts`. (thanks @jordansilly77-stack)
- **feat(cerebras):** add the **Gemma 4 31B** model (`gemma-4-31b`) to the Cerebras registry + pricing table. Regression guard extends `tests/unit/t28-model-catalog-updates.test.ts`. (thanks @backryun)
- **feat(providers):** add **Yuanbao (web)** as a cookie-session provider ([#6196](https://github.com/diegosouzapw/OmniRoute/issues/6196)) — `yuanbao-web` (Tencent Yuanbao, `yuanbao.tencent.com`) with cookie-only auth (`hy_user`/`hy_token` + public agent id), SSE→OpenAI translation incl. `reasoning_content`, exposing DeepSeek V3/R1 + Hunyuan / Hunyuan-T1. Regression guard: `tests/unit/providers-yuanbao-web.test.ts`. `together-web` was **deferred** (no verifiable web-session endpoint — needs a captured request) and `huggingchat-web` **dropped** (the existing `huggingchat` already is a web-cookie provider). (thanks @chirag127)
- **feat(providers):** route the built-in **agentrouter** through the dynamic Claude-Code wire image ([#6056](https://github.com/diegosouzapw/OmniRoute/issues/6056)) — a small static allow-set (`CC_WIRE_IMAGE_BUILTINS` in `open-sse/services/ccWireImageBuiltins.ts`), consulted by `isClaudeCodeCompatible` / `isClaudeCodeCompatibleProvider` / `applyFingerprint`, makes agentrouter adopt the CC wire-image headers + fingerprint **while guarding the CC baseUrl/auth branches** so it keeps its own registry `baseUrl` and `x-api-key` auth. Regression guard: `tests/unit/agentrouter-cc-wire-image.test.ts` (asserts the wire image is applied AND agentrouter's baseUrl/auth are preserved). Live WAF-acceptance against agentrouter.org is a VPS validation follow-up (Hard Rule #18).

View File

@@ -31,6 +31,7 @@ import type { ProviderMessageTranslator } from "../providerPageHelpers";
interface ProviderInfo {
name: string;
riskNoticeVariant?: string;
website?: string;
[key: string]: unknown;
}
@@ -277,6 +278,7 @@ export default function ProviderModalsPanel({
isOpen={showAddApiKeyModal}
provider={providerId}
providerName={providerInfo.name}
providerWebsite={providerInfo.website}
initialBaseUrl={siliconFlowInitialBaseUrl}
isCompatible={isCompatible}
isAnthropic={isAnthropicProtocolCompatible}
@@ -312,6 +314,7 @@ export default function ProviderModalsPanel({
isOpen={showEditModal}
connection={selectedConnection}
providerId={providerId}
providerWebsite={providerInfo.website}
onSave={handleUpdateConnection}
onResyncModels={(id) => handleCompatibleImportWithProgress(id, "sync")}
onClose={() => setShowEditModal(false)}

View File

@@ -10,14 +10,30 @@ import { providerText, type ProviderMessageTranslator } from "../providerPageHel
export interface WebSessionCredentialGuideProps {
requirement: WebSessionCredentialRequirement;
providerName: string;
providerWebsite?: string;
t: ProviderMessageTranslator;
}
export function getProviderWebsiteHost(providerWebsite?: string): string | null {
if (!providerWebsite) {
return null;
}
try {
return new URL(providerWebsite).host;
} catch {
return providerWebsite;
}
}
export default function WebSessionCredentialGuide({
requirement,
providerName,
providerWebsite,
t,
}: WebSessionCredentialGuideProps) {
const providerWebsiteHost = getProviderWebsiteHost(providerWebsite);
if (requirement.kind === "none") {
return (
<div className="rounded-lg border border-emerald-500/25 bg-emerald-500/10 px-3 py-3 text-sm text-text-muted">
@@ -76,6 +92,21 @@ export default function WebSessionCredentialGuide({
{providerText(t, "webSessionGuideStep1", "Sign in to {provider} in your browser.", {
provider: providerName,
})}
{providerWebsite && providerWebsiteHost && (
<a
href={providerWebsite}
target="_blank"
rel="noopener noreferrer"
className="ml-2 inline-flex items-center gap-1 text-primary hover:underline"
>
{providerText(t, "webSessionGuideOpenProvider", "Open {host}", {
host: providerWebsiteHost,
})}
<span className="material-symbols-outlined text-[14px]" aria-hidden="true">
open_in_new
</span>
</a>
)}
</li>
<li>
{providerText(

View File

@@ -34,6 +34,7 @@ export interface AddApiKeyModalProps {
isOpen: boolean;
provider?: string;
providerName?: string;
providerWebsite?: string;
initialBaseUrl?: string;
isCompatible?: boolean;
isAnthropic?: boolean;
@@ -56,6 +57,7 @@ export default function AddApiKeyModal({
isOpen,
provider,
providerName,
providerWebsite,
initialBaseUrl,
isCompatible,
isAnthropic,
@@ -657,6 +659,7 @@ export default function AddApiKeyModal({
<WebSessionCredentialGuide
requirement={webSessionCredential}
providerName={providerDisplayName}
providerWebsite={providerWebsite}
t={t}
/>
)}

View File

@@ -72,6 +72,7 @@ export interface EditConnectionModalProps {
isOpen: boolean;
connection: EditConnectionModalConnection | null;
providerId: string;
providerWebsite?: string;
onSave: (data: unknown) => Promise<void | unknown>;
/** Triggered after a successful save when the "import only free models" flag changed. */
onResyncModels?: (connectionId: string) => void | Promise<void>;
@@ -84,6 +85,7 @@ export default function EditConnectionModal({
isOpen,
connection,
providerId,
providerWebsite,
onSave,
onResyncModels,
onClose,
@@ -795,6 +797,7 @@ export default function EditConnectionModal({
<WebSessionCredentialGuide
requirement={webSessionCredential}
providerName={providerDisplayName}
providerWebsite={providerWebsite}
t={t}
/>
)}

View File

@@ -0,0 +1,25 @@
import { test } from "node:test";
import assert from "node:assert/strict";
// #6316: the web-session credential guide shows an "Open {host}" link to the
// provider site. The host is derived from the provider's website URL via
// getProviderWebsiteHost — a full URL collapses to its host, a bare/invalid
// string falls back to itself, and an empty value yields null (no link).
const { getProviderWebsiteHost } = await import(
"../../src/app/(dashboard)/dashboard/providers/[id]/components/WebSessionCredentialGuide.tsx"
);
test("#6316: full URL collapses to host", () => {
assert.equal(getProviderWebsiteHost("https://chat.qwen.ai/path?x=1"), "chat.qwen.ai");
assert.equal(getProviderWebsiteHost("https://www.kimi.com"), "www.kimi.com");
});
test("#6316: bare/invalid string falls back to itself", () => {
assert.equal(getProviderWebsiteHost("kimi.com"), "kimi.com");
assert.equal(getProviderWebsiteHost("not a url"), "not a url");
});
test("#6316: empty/undefined yields null (no link rendered)", () => {
assert.equal(getProviderWebsiteHost(undefined), null);
assert.equal(getProviderWebsiteHost(""), null);
});