You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maka.apache.org by as...@apache.org on 2026/08/25 07:01:50 UTC

(maka) branch main updated: feat(desktop): show effective provider endpoints (#3639)

This is an automated email from the ASF dual-hosted git repository.

Astro-Han pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/maka.git


The following commit(s) were added to refs/heads/main by this push:
     new 131eec2e6 feat(desktop): show effective provider endpoints (#3639)
131eec2e6 is described below

commit 131eec2e63d2de2f26bde89b99b315f087c0d1c5
Author: Hu Yang <47...@users.noreply.github.com>
AuthorDate: Tue Aug 25 15:01:45 2026 +0800

    feat(desktop): show effective provider endpoints (#3639)
    
    * feat(desktop): show effective provider endpoints
    
    Generated-by: OpenAI Codex
    
    * fix(desktop): address endpoint presentation review findings
    
    - Mask every query value in displayed endpoints, not just known secret
      key names: a relay may carry credentials under arbitrary keys.
    - Gate the endpoint editor behind the masked-by-default PasswordInput
      when the saved URL embeds userinfo or query credentials.
    - Note model-level endpoint overrides under connection-level defaults,
      and only then: a configured baseUrl wins over every override.
---
 .../provider-endpoint-presentation.test.ts         | 204 +++++++++++++++++++++
 .../src/renderer/locales/settings-provider-copy.ts |  10 +-
 .../settings/provider-connection-detail.tsx        |  82 ++++++---
 .../settings/provider-endpoint-presentation.ts     | 156 ++++++++++++++++
 .../renderer/styles/settings/provider-editor.css   |  16 ++
 .../stories/settings/provider-settings.stories.tsx |  46 ++++-
 6 files changed, 483 insertions(+), 31 deletions(-)

diff --git a/apps/desktop/src/main/__tests__/provider-endpoint-presentation.test.ts b/apps/desktop/src/main/__tests__/provider-endpoint-presentation.test.ts
new file mode 100644
index 000000000..0db9c21c9
--- /dev/null
+++ b/apps/desktop/src/main/__tests__/provider-endpoint-presentation.test.ts
@@ -0,0 +1,204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import assert from 'node:assert/strict';
+import test from 'node:test';
+import { PROVIDER_DEFAULTS } from '@maka/core/llm-connections';
+import {
+  endpointCarriesCredentials,
+  providerEndpointPresentation,
+} from '../../renderer/settings/provider-endpoint-presentation.js';
+
+// A 40-char hex-shaped run, built rather than written: long enough to trip
+// the display redactor's long-opaque-token rule wherever it is left alone.
+const longOpaqueToken = 'ab01'.repeat(10);
+
+test('fixed Alibaba access paths expose their distinct effective endpoints read-only', () => {
+  const api = providerEndpointPresentation({ providerType: 'alibaba' });
+  const tokenPlanChina = providerEndpointPresentation({ providerType: 'alibaba-token-plan-cn' });
+
+  assert.deepEqual(api, {
+    value: PROVIDER_DEFAULTS.alibaba.baseUrl,
+    editable: false,
+    emptyState: 'missing',
+  });
+  assert.deepEqual(tokenPlanChina, {
+    value: PROVIDER_DEFAULTS['alibaba-token-plan-cn'].baseUrl,
+    editable: false,
+    emptyState: 'missing',
+  });
+  assert.match(api.value!, /^https:\/\//);
+  assert.match(tokenPlanChina.value!, /^https:\/\//);
+  assert.notEqual(api.value, tokenPlanChina.value);
+});
+
+test('a persisted override is the displayed effective endpoint', () => {
+  assert.deepEqual(
+    providerEndpointPresentation({
+      providerType: 'alibaba',
+      baseUrl: '  https://relay.example.com/alibaba/v1/  ',
+    }),
+    {
+      value: 'https://relay.example.com/alibaba/v1/',
+      editable: false,
+      emptyState: 'missing',
+    },
+  );
+});
+
+test('displaying a custom endpoint masks userinfo and every query value without hiding its route', () => {
+  assert.deepEqual(
+    providerEndpointPresentation({
+      providerType: 'openai-compatible',
+      baseUrl:
+        `https://relay-user:relay-password@relay.example.com/v1?api-version=2026-08-01&api_key=${longOpaqueToken}`,
+    }),
+    {
+      value:
+        'https://<redacted>@relay.example.com/v1?api-version=<redacted>&api_key=<redacted>',
+      editable: true,
+      emptyState: 'missing',
+    },
+  );
+});
+
+test('query values are masked under arbitrary key names, not just known ones', () => {
+  assert.deepEqual(
+    providerEndpointPresentation({
+      providerType: 'openai-compatible',
+      baseUrl: `https://relay.example.com/v1?key=${longOpaqueToken}`,
+    }),
+    {
+      value: 'https://relay.example.com/v1?key=<redacted>',
+      editable: true,
+      emptyState: 'missing',
+    },
+  );
+  assert.deepEqual(
+    providerEndpointPresentation({
+      providerType: 'openai-compatible',
+      baseUrl: `https://relay.example.com/v1?client_secret=${longOpaqueToken}`,
+    }),
+    {
+      value: 'https://relay.example.com/v1?client_secret=<redacted>',
+      editable: true,
+      emptyState: 'missing',
+    },
+  );
+});
+
+test('custom relays and local runtimes retain endpoint editing', () => {
+  assert.deepEqual(
+    providerEndpointPresentation({
+      providerType: 'openai-compatible',
+      baseUrl: 'https://relay.example.com/v1',
+    }),
+    {
+      value: 'https://relay.example.com/v1',
+      editable: true,
+      emptyState: 'missing',
+    },
+  );
+  assert.deepEqual(
+    providerEndpointPresentation({ providerType: 'ollama' }),
+    {
+      value: PROVIDER_DEFAULTS.ollama.baseUrl,
+      editable: true,
+      emptyState: 'missing',
+    },
+  );
+});
+
+test('derived and OAuth endpoints remain visible but read-only', () => {
+  assert.deepEqual(
+    providerEndpointPresentation({
+      providerType: 'cloudflare-workers-ai',
+      baseUrl: 'https://api.cloudflare.com/client/v4/accounts/example/ai/v1',
+    }),
+    {
+      value: 'https://api.cloudflare.com/client/v4/accounts/example/ai/v1',
+      editable: false,
+      emptyState: 'missing',
+    },
+  );
+  assert.deepEqual(
+    providerEndpointPresentation({ providerType: 'openai-codex' }),
+    {
+      value: PROVIDER_DEFAULTS['openai-codex'].baseUrl,
+      editable: false,
+      emptyState: 'managed',
+    },
+  );
+});
+
+test('an absent custom endpoint remains visible as a missing editable value', () => {
+  assert.deepEqual(
+    providerEndpointPresentation({ providerType: 'openai-compatible' }),
+    { value: null, editable: true, emptyState: 'missing' },
+  );
+});
+
+test('providers with model-level endpoint overrides say so when showing the default', () => {
+  // ZenMux routes Anthropic-family models through .../api/anthropic/v1 and
+  // Cohere routes north-mini-code-1-0 through the compatibility endpoint, so
+  // the connection-level default is not the whole truth for every model.
+  assert.deepEqual(
+    providerEndpointPresentation({ providerType: 'zenmux' }),
+    {
+      value: PROVIDER_DEFAULTS.zenmux.baseUrl,
+      editable: false,
+      emptyState: 'missing',
+      modelOverrides: true,
+    },
+  );
+  assert.deepEqual(
+    providerEndpointPresentation({ providerType: 'cohere' }),
+    {
+      value: PROVIDER_DEFAULTS.cohere.baseUrl,
+      editable: false,
+      emptyState: 'missing',
+      modelOverrides: true,
+    },
+  );
+});
+
+test('a configured endpoint wins over model overrides, so no caveat applies', () => {
+  // resolveModelRuntime uses a configured baseUrl for every model; the row is
+  // then the exact truth and must not carry the override note.
+  assert.deepEqual(
+    providerEndpointPresentation({
+      providerType: 'zenmux',
+      baseUrl: 'https://relay.example.com/v1',
+    }),
+    {
+      value: 'https://relay.example.com/v1',
+      editable: false,
+      emptyState: 'missing',
+    },
+  );
+});
+
+test('endpointCarriesCredentials gates userinfo and query-bearing endpoints', () => {
+  assert.equal(endpointCarriesCredentials('https://relay-user:relay-password@relay.example.com/v1'), true);
+  assert.equal(endpointCarriesCredentials('https://relay.example.com/v1?api-version=2026-08-01'), true);
+  assert.equal(endpointCarriesCredentials('https://relay.example.com/v1'), false);
+  assert.equal(endpointCarriesCredentials(''), false);
+  assert.equal(endpointCarriesCredentials(undefined), false);
+  assert.equal(endpointCarriesCredentials('not a url'), false);
+});
diff --git a/apps/desktop/src/renderer/locales/settings-provider-copy.ts b/apps/desktop/src/renderer/locales/settings-provider-copy.ts
index cd36f4e91..20a7c71ce 100644
--- a/apps/desktop/src/renderer/locales/settings-provider-copy.ts
+++ b/apps/desktop/src/renderer/locales/settings-provider-copy.ts
@@ -120,7 +120,10 @@ const zhCopy = {
     // Row affordances (settings-sidebar 的 InfoRow / ExpandableRow 语言):一行
     // 只报状态,改的时候才展开成输入框。
     change: '更换', set: '设置', edit: '编辑', save: '保存',
-    endpointDefault: '服务商默认地址',
+    endpointManaged: '由账号登录或服务商管理',
+    endpointMissing: '尚未配置服务地址',
+    endpointModelOverridesNote: '部分模型会通过其他服务端点发送请求',
+    endpointCredentialsMasked: '已保存的地址内嵌凭据,编辑时默认隐藏',
     modelManagement: '模型',
     disconnectAndDelete: '退出账号并删除',
     copilotImportFailed: '导入 GitHub Copilot 登录失败', copilotLoggedIn: 'GitHub Copilot 已登录', copilotWaiting: '等待兼容 GitHub 凭据',
@@ -267,7 +270,10 @@ const enCopy: ProviderSettingsCopy = {
     ...enCapabilitiesCopy,
     capabilitiesHelp: 'Declares thinking levels, vision, and context window per enabled model; applies on save.',
     change: 'Change', set: 'Set', edit: 'Edit', save: 'Save',
-    endpointDefault: 'The provider default',
+    endpointManaged: 'Managed by account sign-in or the provider',
+    endpointMissing: 'No service URL configured',
+    endpointModelOverridesNote: 'Some models send requests through a different endpoint',
+    endpointCredentialsMasked: 'The saved URL embeds credentials and stays masked while editing',
     modelManagement: 'Models',
     disconnectAndDelete: 'Sign out and delete',
     copilotImportFailed: 'Failed to import GitHub Copilot sign-in', copilotLoggedIn: 'GitHub Copilot signed in', copilotWaiting: 'Waiting for compatible GitHub credentials',
diff --git a/apps/desktop/src/renderer/settings/provider-connection-detail.tsx b/apps/desktop/src/renderer/settings/provider-connection-detail.tsx
index 5b3ffa1b0..a004f541f 100644
--- a/apps/desktop/src/renderer/settings/provider-connection-detail.tsx
+++ b/apps/desktop/src/renderer/settings/provider-connection-detail.tsx
@@ -51,6 +51,7 @@ import {
 } from '@maka/ui';
 import { PasswordInput } from './password-input';
 import { SettingsExpandableRow } from './settings-expandable-row';
+import { SettingsRow } from './settings-section';
 import { getProviderSettingsCopy } from '../locales/settings-provider-copy';
 import { providerDisplay } from './provider-display';
 import { AddModelDialog } from './provider-add-model-dialog';
@@ -82,6 +83,7 @@ import {
   type RequestHeaderDraft,
 } from './request-customization-editor';
 import { bulkThinkingLevelStates } from './relay-thinking-bulk';
+import { endpointCarriesCredentials, providerEndpointPresentation } from './provider-endpoint-presentation';
 
 export function ConnectionDetail(props: ConnectionDetailProps) {
   const defaults = PROVIDER_DEFAULTS[props.connection.providerType];
@@ -343,15 +345,26 @@ function ConnectionDetailInner(props: ConnectionDetailProps) {
       if (mounted.current) setRequestCustomizationBusy(false);
     }
   }
-  // Most of the 60 providers publish a fixed endpoint; editing it there can
-  // only break the connection, and a proxy belongs in 设置 · 通用 · 网络, not in
-  // a per-connection URL. So the row exists only where the address is genuinely
-  // the user's: a service with no published endpoint (the *-compatible ones), or
-  // a local runtime whose port is a convention rather than a fact. A derived
-  // endpoint (Cloudflare builds one from the account id) is nobody's to type.
-  const showsEndpoint = !needsOAuth
-    && !defaults.baseUrlTemplate
-    && (!defaults.baseUrl || defaults.category === 'local');
+  // Every known connection reports where requests go. Editability remains the
+  // narrower authority: built-in and derived endpoints are visible but fixed,
+  // while custom relays and local runtimes keep their existing editor.
+  const endpoint = providerEndpointPresentation(connection);
+  const endpointValue = endpoint.value
+    ? <code className="settingsReadOnlyValue providerEndpointValue" data-mono="true">{endpoint.value}</code>
+    : endpoint.emptyState === 'managed'
+      ? copy.endpointManaged
+      : copy.endpointMissing;
+  // Model-level endpoint overrides mean the connection-level base is not the
+  // whole truth for every model; say so under the value rather than implying
+  // one address serves all models.
+  const endpointNote = endpoint.modelOverrides
+    ? <span className="providerEndpointNote">{copy.endpointModelOverridesNote}</span>
+    : null;
+  const endpointDisplay = endpointNote ? <>{endpointValue}{endpointNote}</> : endpointValue;
+  // A credential-bearing saved endpoint must not prefill a plain text input:
+  // the editor falls back to the masked-by-default PasswordInput, which the
+  // user can deliberately reveal.
+  const endpointHasCredentials = endpointCarriesCredentials(savedBaseUrl);
 
   return (
     /* Three sections, each a heading with a sentence beside its controls, one
@@ -457,8 +470,7 @@ function ConnectionDetailInner(props: ConnectionDetailProps) {
             </SettingsExpandableRow>
           </VStack>
         )}
-        {(supportsApiKey || showsEndpoint) && (
-          <VStack gap={0}>
+        <VStack gap={0}>
             <Divider />
             {supportsApiKey && (
               <>
@@ -497,11 +509,11 @@ function ConnectionDetailInner(props: ConnectionDetailProps) {
               <Divider />
               </>
             )}
-            {showsEndpoint && (
+            {endpoint.editable ? (
               <>
               <SettingsExpandableRow
                 label={copy.endpoint}
-                value={savedBaseUrl || copy.endpointDefault}
+                value={endpointDisplay}
                 actionLabel={copy.edit}
                 actionAriaLabel={`${copy.edit}: ${copy.endpoint}`}
                 isEditing={editingRow === 'endpoint'}
@@ -513,25 +525,41 @@ function ConnectionDetailInner(props: ConnectionDetailProps) {
                 onCancel={() => { setBaseUrl(savedBaseUrl); setEditingRow(null); }}
                 onSave={async () => { if (await save('endpoint')) setEditingRow(null); }}
               >
-                <TextInput
-                  label={copy.endpoint}
-                  isLabelHidden
-                  value={baseUrl}
-                  onChange={setBaseUrl}
-                  placeholder={defaults.baseUrl}
-                  isDisabled={allActionsBusy}
-                />
+                {endpointHasCredentials ? (
+                  <PasswordInput
+                    value={baseUrl}
+                    onChange={setBaseUrl}
+                    placeholder={defaults.baseUrl}
+                    label={copy.endpoint}
+                    isLabelHidden
+                    description={copy.endpointCredentialsMasked}
+                    isDisabled={allActionsBusy}
+                  />
+                ) : (
+                  <TextInput
+                    label={copy.endpoint}
+                    isLabelHidden
+                    value={baseUrl}
+                    onChange={setBaseUrl}
+                    placeholder={defaults.baseUrl}
+                    isDisabled={allActionsBusy}
+                  />
+                )}
               </SettingsExpandableRow>
               <Divider />
               </>
+            ) : (
+              <>
+                <SettingsRow
+                  label={copy.endpoint}
+                  description={endpointDisplay}
+                  align="start"
+                />
+                <Divider />
+              </>
             )}
-          </VStack>
-        )}
+        </VStack>
       </DetailSection>
-      {/* The rows draw the closing rule themselves; without them the section
-          still needs one. Two rules with a gap between them read as an empty
-          row, so only ever one. */}
-      {!supportsApiKey && !showsEndpoint && !retired && <Divider />}
       {/* Everything below writes to the connection, and a retired one accepts
           no writes: the catalog refuses a model or request-body change, and the
           credential vault refuses a request header. Rendering the editors would
diff --git a/apps/desktop/src/renderer/settings/provider-endpoint-presentation.ts b/apps/desktop/src/renderer/settings/provider-endpoint-presentation.ts
new file mode 100644
index 000000000..d01d86281
--- /dev/null
+++ b/apps/desktop/src/renderer/settings/provider-endpoint-presentation.ts
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {
+  effectiveBaseUrl,
+  PROVIDER_DEFAULTS,
+  type LlmConnection,
+} from '@maka/core/llm-connections';
+import {
+  lookupModelProviderOverride,
+  modelMetadataIdsForProvider,
+} from '@maka/core/model-metadata';
+import { redactSecrets } from '@maka/core/display-redaction';
+
+export interface ProviderEndpointPresentation {
+  /** The effective base endpoint, with any display-facing credentials masked. */
+  value: string | null;
+  /** Whether the connection detail page should offer an endpoint editor. */
+  editable: boolean;
+  /** Explains an absent concrete value without making the row disappear. */
+  emptyState: 'managed' | 'missing';
+  /**
+   * The displayed value is the connection-level base endpoint, but some models
+   * route through model-level endpoint overrides before a request, so an
+   * individual model may use a different address. Present only when the
+   * connection itself configures no baseUrl — a configured baseUrl wins over
+   * every model-level override at runtime, so then the row is the exact truth.
+   */
+  modelOverrides?: true;
+}
+
+/**
+ * Resolve the endpoint fact shown on a provider connection detail page.
+ *
+ * Visibility and editability are deliberately separate. Built-in providers
+ * still own their fixed URL, but the user needs to see it to distinguish
+ * similarly named access paths and regions. Custom relays and local runtimes
+ * keep the existing editor because their address genuinely belongs to the
+ * connection. Derived endpoints (for example Cloudflare account URLs) are
+ * concrete once persisted, but are never hand-edited here.
+ */
+export function providerEndpointPresentation(
+  connection: {
+    providerType: LlmConnection['providerType'];
+    baseUrl?: string;
+  },
+): ProviderEndpointPresentation {
+  const defaults = PROVIDER_DEFAULTS[connection.providerType];
+  const effective = effectiveBaseUrl(connection).trim();
+  const value = endpointForDisplay(effective);
+  const editable = defaults.authKind !== 'oauth_token'
+    && !defaults.baseUrlTemplate
+    && (!defaults.baseUrl || defaults.category === 'local');
+
+  return {
+    value: value || null,
+    editable,
+    emptyState: defaults.authKind === 'oauth_token' ? 'managed' : 'missing',
+    ...(providerRoutesModelsElsewhere(connection) ? { modelOverrides: true as const } : {}),
+  };
+}
+
+/** The override table is immutable generated data, so a per-provider verdict is stable. */
+const modelOverrideRouteCache = new Map<LlmConnection['providerType'], boolean>();
+
+/**
+ * Whether the runtime may route some of this connection's models through a
+ * different endpoint than the connection-level base. Mirrors
+ * `resolveModelRuntime`: a configured baseUrl always wins, and only without
+ * one can a model-level override replace the provider default.
+ */
+function providerRoutesModelsElsewhere(
+  connection: { providerType: LlmConnection['providerType']; baseUrl?: string },
+): boolean {
+  if (connection.baseUrl?.trim()) return false;
+  const defaultBaseUrl = PROVIDER_DEFAULTS[connection.providerType]?.baseUrl;
+  if (!defaultBaseUrl) return false;
+  const cached = modelOverrideRouteCache.get(connection.providerType);
+  if (cached !== undefined) return cached;
+  let routes = false;
+  for (const modelId of modelMetadataIdsForProvider(connection.providerType)) {
+    const api = lookupModelProviderOverride(connection.providerType, modelId)?.api;
+    if (api && api !== defaultBaseUrl) {
+      routes = true;
+      break;
+    }
+  }
+  modelOverrideRouteCache.set(connection.providerType, routes);
+  return routes;
+}
+
+/**
+ * Whether a persisted endpoint embeds credentials: URL userinfo, or any query
+ * parameter at all — a relay may name a credential parameter arbitrarily, so
+ * the mere presence of a query string makes the value credential-shaped for
+ * display. The endpoint editor uses this to gate its input behind a masked
+ * state instead of prefilling the raw value into a plain text field.
+ */
+export function endpointCarriesCredentials(value: string | null | undefined): boolean {
+  if (!value) return false;
+  try {
+    const parsed = new URL(value.trim());
+    return Boolean(parsed.username || parsed.password) || parsed.search !== '';
+  } catch {
+    return false;
+  }
+}
+
+function endpointForDisplay(value: string): string {
+  if (!value) return value;
+  // Base URL secrets hide in three places: userinfo, query values under
+  // arbitrary key names, and (rarely) path-embedded tokens. URL parsing
+  // redacts the first two deterministically; the shared redactor sweeps
+  // whatever remains.
+  try {
+    const parsed = new URL(value);
+    const carriesUserinfo = Boolean(parsed.username || parsed.password);
+    parsed.username = '';
+    parsed.password = '';
+    // A base URL's query string has no display-safe semantics: a relay may put
+    // a credential under any key name (`key`, `client_secret`, ...), so mask
+    // every value and keep the key names to preserve the endpoint's shape.
+    // Rebuild by hand — URLSearchParams would percent-encode `<redacted>`.
+    const queryKeys = [...parsed.searchParams.keys()];
+    if (queryKeys.length > 0) parsed.search = '';
+    // Keep one marker so the user can still tell that the saved endpoint
+    // carries embedded credentials.
+    let display = carriesUserinfo
+      ? parsed.href.replace(`${parsed.protocol}//`, `${parsed.protocol}//<redacted>@`)
+      : parsed.href;
+    if (queryKeys.length > 0) {
+      display += `?${queryKeys.map((key) => `${key}=<redacted>`).join('&')}`;
+    }
+    return redactSecrets(display);
+  } catch {
+    // Persistence already validates provider base URLs. A legacy malformed
+    // value still gets best-effort masking rather than disappearing.
+    return redactSecrets(value);
+  }
+}
diff --git a/apps/desktop/src/renderer/styles/settings/provider-editor.css b/apps/desktop/src/renderer/styles/settings/provider-editor.css
index 1a04c0f55..1e3fe1653 100644
--- a/apps/desktop/src/renderer/styles/settings/provider-editor.css
+++ b/apps/desktop/src/renderer/styles/settings/provider-editor.css
@@ -28,6 +28,22 @@
 /* .settingsRow label/value typography moved to rows.css
    (PR-SETTINGS-ROWS-CONVERGENCE-0). */
 
+/* Endpoint values are machine text, but unlike opaque filesystem paths they
+   carry useful URL boundaries. Prefer a slash/punctuation break before the
+   fallback anywhere-wrap so a long address does not split its hostname just
+   because the first line still had room for two more characters. */
+.settingsReadOnlyValue.providerEndpointValue[data-mono="true"] {
+  word-break: normal;
+}
+
+/* The model-override note is a second sentence under the endpoint's machine
+   text: block layout keeps it off the URL line, muted color keeps the URL
+   the primary fact. */
+.providerEndpointNote {
+  display: block;
+  color: var(--muted-foreground);
+}
+
 @media (max-width: 980px) {
   .app {
     grid-template-columns: 1fr;
diff --git a/apps/desktop/stories/settings/provider-settings.stories.tsx b/apps/desktop/stories/settings/provider-settings.stories.tsx
index ce688f99b..3c0835f15 100644
--- a/apps/desktop/stories/settings/provider-settings.stories.tsx
+++ b/apps/desktop/stories/settings/provider-settings.stories.tsx
@@ -50,6 +50,7 @@ export default meta;
 type Story = StoryObj<typeof meta>;
 type AutoOpenTarget =
   | 'detail'
+  | 'detail-alibaba'
   | 'detail-static'
   | 'detail-relay'
   | 'add'
@@ -127,6 +128,21 @@ const configuredConnections = [
   }),
 ];
 
+const alibabaTokenPlanConnections = [
+  makeConnection({
+    slug: 'alibaba-token-plan-cn',
+    name: 'Alibaba Token Plan(团队版)',
+    providerType: 'alibaba-token-plan-cn',
+    defaultModel: 'qwen3.8-max',
+    lastTestStatus: 'verified',
+    models: [
+      { id: 'qwen3.8-max', displayName: 'Qwen3.8 Max' },
+      { id: 'qwen3.7-max', displayName: 'Qwen3.7 Max' },
+    ],
+    modelSource: 'fetched',
+  }),
+];
+
 // A provider whose key cannot call a model-list endpoint: refresh replays the
 // array this build shipped, so 添加模型 replaces 更新模型目录 as the only way the
 // catalog can grow. `deepseek-v4-pro-beta` is a model added that way — absent
@@ -426,11 +442,22 @@ function reachCatalog(root: HTMLElement): HTMLElement | null {
 }
 
 function clickAutoOpenTarget(root: HTMLElement, target: AutoOpenTarget): boolean {
-  if (target === 'detail' || target === 'detail-static' || target === 'detail-relay') {
+  if (
+    target === 'detail'
+    || target === 'detail-alibaba'
+    || target === 'detail-static'
+    || target === 'detail-relay'
+  ) {
     // ListItem's clickable surface is an invisible button inside the row, so
     // the row is located by its slug hook and the button taken from within it.
     const slug =
-      target === 'detail' ? 'zai-live' : target === 'detail-static' ? 'ark-plan' : 'relay-house';
+      target === 'detail'
+        ? 'zai-live'
+        : target === 'detail-alibaba'
+          ? 'alibaba-token-plan-cn'
+          : target === 'detail-static'
+            ? 'ark-plan'
+            : 'relay-house';
     const row = root.querySelector<HTMLElement>(`[data-connection-slug="${slug}"]`);
     const detailButton = row?.querySelector('button') ?? null;
     detailButton?.click();
@@ -496,6 +523,21 @@ export const ConnectionDetailPage: Story = {
   ),
 };
 
+// Fixed endpoints are inspectable but not editable. Alibaba is the high-signal
+// case because several catalog entries share one brand while routing to
+// different products and regions (#3636).
+export const AlibabaConnectionDetailPage: Story = {
+  render: () => (
+    <ProviderStory
+      bridge={createBridge({
+        connections: alibabaTokenPlanConnections,
+        defaultSlug: 'alibaba-token-plan-cn',
+      })}
+      autoOpen="detail-alibaba"
+    />
+  ),
+};
+
 // Real path: 设置 → 模型 → click a connection whose provider has no model-list
 // endpoint — 添加模型 stands where 更新模型目录 would, and the capability section
 // lists the models Maka's bundled metadata cannot describe.


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org