@sleep2agi/agent-network-dashboard 0.6.3-preview.8 → 0.6.3-preview.9
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/.next/BUILD_COMMIT +1 -1
- package/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +3 -3
- package/.next/diagnostics/route-bundle-stats.json +5 -5
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +1 -1
- package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/admin.html +1 -1
- package/.next/server/app/admin.rsc +1 -1
- package/.next/server/app/admin.segments/_full.segment.rsc +1 -1
- package/.next/server/app/admin.segments/_head.segment.rsc +1 -1
- package/.next/server/app/admin.segments/_index.segment.rsc +1 -1
- package/.next/server/app/admin.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/admin.segments/admin.segment.rsc +1 -1
- package/.next/server/app/index.html +2 -2
- package/.next/server/app/index.rsc +2 -2
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/index.segments/_full.segment.rsc +2 -2
- package/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login.html +2 -2
- package/.next/server/app/login.rsc +2 -2
- package/.next/server/app/login.segments/_full.segment.rsc +2 -2
- package/.next/server/app/login.segments/_head.segment.rsc +1 -1
- package/.next/server/app/login.segments/_index.segment.rsc +1 -1
- package/.next/server/app/login.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/login.segments/login.segment.rsc +1 -1
- package/.next/server/app/logs.html +1 -1
- package/.next/server/app/logs.rsc +1 -1
- package/.next/server/app/logs.segments/_full.segment.rsc +1 -1
- package/.next/server/app/logs.segments/_head.segment.rsc +1 -1
- package/.next/server/app/logs.segments/_index.segment.rsc +1 -1
- package/.next/server/app/logs.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/logs.segments/logs.segment.rsc +1 -1
- package/.next/server/app/messages.html +1 -1
- package/.next/server/app/messages.rsc +1 -1
- package/.next/server/app/messages.segments/_full.segment.rsc +1 -1
- package/.next/server/app/messages.segments/_head.segment.rsc +1 -1
- package/.next/server/app/messages.segments/_index.segment.rsc +1 -1
- package/.next/server/app/messages.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/messages.segments/messages.segment.rsc +1 -1
- package/.next/server/app/node.html +1 -1
- package/.next/server/app/node.rsc +1 -1
- package/.next/server/app/node.segments/_full.segment.rsc +1 -1
- package/.next/server/app/node.segments/_head.segment.rsc +1 -1
- package/.next/server/app/node.segments/_index.segment.rsc +1 -1
- package/.next/server/app/node.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/node.segments/node.segment.rsc +1 -1
- package/.next/server/app/nodes.html +1 -1
- package/.next/server/app/nodes.rsc +1 -1
- package/.next/server/app/nodes.segments/_full.segment.rsc +1 -1
- package/.next/server/app/nodes.segments/_head.segment.rsc +1 -1
- package/.next/server/app/nodes.segments/_index.segment.rsc +1 -1
- package/.next/server/app/nodes.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/nodes.segments/nodes.segment.rsc +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/providers/page_client-reference-manifest.js +1 -1
- package/.next/server/app/providers.html +2 -2
- package/.next/server/app/providers.rsc +2 -2
- package/.next/server/app/providers.segments/_full.segment.rsc +2 -2
- package/.next/server/app/providers.segments/_head.segment.rsc +1 -1
- package/.next/server/app/providers.segments/_index.segment.rsc +1 -1
- package/.next/server/app/providers.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/providers.segments/providers/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/providers.segments/providers.segment.rsc +1 -1
- package/.next/server/app/server-logs.html +1 -1
- package/.next/server/app/server-logs.rsc +1 -1
- package/.next/server/app/server-logs.segments/_full.segment.rsc +1 -1
- package/.next/server/app/server-logs.segments/_head.segment.rsc +1 -1
- package/.next/server/app/server-logs.segments/_index.segment.rsc +1 -1
- package/.next/server/app/server-logs.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/server-logs.segments/server-logs.segment.rsc +1 -1
- package/.next/server/app/servers.html +1 -1
- package/.next/server/app/servers.rsc +1 -1
- package/.next/server/app/servers.segments/_full.segment.rsc +1 -1
- package/.next/server/app/servers.segments/_head.segment.rsc +1 -1
- package/.next/server/app/servers.segments/_index.segment.rsc +1 -1
- package/.next/server/app/servers.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/servers.segments/servers/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/servers.segments/servers.segment.rsc +1 -1
- package/.next/server/app/settings/networks.html +1 -1
- package/.next/server/app/settings/networks.rsc +1 -1
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/_index.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +1 -1
- package/.next/server/app/settings/networks.segments/settings.segment.rsc +1 -1
- package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens.html +1 -1
- package/.next/server/app/settings/tokens.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/_index.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +1 -1
- package/.next/server/app/settings/tokens.segments/settings.segment.rsc +1 -1
- package/.next/server/app/settings.html +2 -2
- package/.next/server/app/settings.rsc +2 -2
- package/.next/server/app/settings.segments/_full.segment.rsc +2 -2
- package/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_index.segment.rsc +1 -1
- package/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings.segment.rsc +1 -1
- package/.next/server/app/tasks.html +1 -1
- package/.next/server/app/tasks.rsc +1 -1
- package/.next/server/app/tasks.segments/_full.segment.rsc +1 -1
- package/.next/server/app/tasks.segments/_head.segment.rsc +1 -1
- package/.next/server/app/tasks.segments/_index.segment.rsc +1 -1
- package/.next/server/app/tasks.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/tasks.segments/tasks.segment.rsc +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__05kf31s._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__05kf31s._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_providers_page_tsx_0hw2n0v._.js +1 -1
- package/.next/server/chunks/ssr/agent-network-dashboard_app_providers_page_tsx_0hw2n0v._.js.map +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/static/chunks/{155d7_jkfvgom.js → 03go1u-9yogqz.js} +1 -1
- package/.next/static/chunks/03x~qkzp0slbo.js +1 -0
- package/.next/static/chunks/{0f~m1e-5.0ubb.js → 0sw5a620xl~sv.js} +1 -1
- package/.next/static/chunks/{0948l13vd6tpk.js → 10rjjsjdw~wuk.js} +1 -1
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/app/components/ProviderFormModal.tsx +3 -3
- package/package.json +1 -1
- package/.next/static/chunks/0x4-u86z6_lmd.js +0 -1
- /package/.next/static/{wJ59_ueg_SHOXmnLpiO_E → XerGwQyUEDjwBTySHen4x}/_buildManifest.js +0 -0
- /package/.next/static/{wJ59_ueg_SHOXmnLpiO_E → XerGwQyUEDjwBTySHen4x}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{wJ59_ueg_SHOXmnLpiO_E → XerGwQyUEDjwBTySHen4x}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../agent-network-dashboard/package.json.%5Bjson%5D.mjs","../../../../../agent-network-dashboard/app/lib/version.ts","../../../../../agent-network-dashboard/app/login/page.tsx"],"sourcesContent":["\"use turbopack no side effects\";\nexport default {\"name\":\"@sleep2agi/agent-network-dashboard\",\"version\":\"0.6.3-preview.8\",\"description\":\"Agent Network Dashboard — Web UI for managing AI Agent networks\",\"main\":\"apps/desktop/electron/main.cjs\",\"scripts\":{\"dev\":\"next dev\",\"build\":\"next build\",\"postbuild\":\"node scripts/stamp-build-commit.mjs\",\"start\":\"next start\",\"lint\":\"eslint\",\"app:android:init\":\"npx cap add android\",\"app:android:sync\":\"npx cap sync android\",\"app:android:open\":\"npx cap open android\",\"app:ios:init\":\"npx cap add ios\",\"app:ios:sync\":\"npx cap sync ios\",\"app:ios:open\":\"npx cap open ios\",\"app:desktop\":\"electron apps/desktop/electron/main.cjs\",\"app:desktop:pack\":\"electron-builder --config electron-builder.json\",\"prepublishOnly\":\"[ -f .next/BUILD_ID ] || (echo 'prepublishOnly: .next/BUILD_ID missing — run npm run build first (see commit 05c1ebf body for R224 chunk-500 root cause)' >&2 && exit 1)\"},\"bin\":{\"agent-network-dashboard\":\"./bin/start.js\"},\"homepage\":\"https://anet.sh\",\"repository\":{\"type\":\"git\",\"url\":\"https://github.com/sleep2agi/agent-network-dashboard\"},\"bugs\":{\"url\":\"https://github.com/sleep2agi/agent-network-dashboard/issues\"},\"author\":\"sleep2agi\",\"license\":\"Apache-2.0\",\"publishConfig\":{\"access\":\"public\"},\"dependencies\":{\"@capacitor/core\":\"^7.4.4\",\"next\":\"16.2.3\",\"react\":\"19.2.4\",\"react-dom\":\"19.2.4\",\"react-markdown\":\"^10.1.0\",\"remark-gfm\":\"^4.0.1\",\"swr\":\"^2.4.1\"},\"devDependencies\":{\"@capacitor/android\":\"^7.4.4\",\"@capacitor/cli\":\"^7.4.4\",\"@capacitor/ios\":\"^7.4.4\",\"@playwright/test\":\"^1.59.1\",\"@tailwindcss/postcss\":\"^4\",\"@types/node\":\"^20\",\"@types/react\":\"^19\",\"@types/react-dom\":\"^19\",\"eslint\":\"^9\",\"electron\":\"^39.2.6\",\"electron-builder\":\"^26.0.12\",\"eslint-config-next\":\"16.2.3\",\"tailwindcss\":\"^4\",\"typescript\":\"^5\"}};\n//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInR1cmJvcGFjazovLy9hZ2VudC1uZXR3b3JrLWRhc2hib2FyZC9wYWNrYWdlLmpzb24iXSwic291cmNlc0NvbnRlbnQiOlsie1wibmFtZVwiOlwiQHNsZWVwMmFnaS9hZ2VudC1uZXR3b3JrLWRhc2hib2FyZFwiLFwidmVyc2lvblwiOlwiMC42LjMtcHJldmlldy44XCIsXCJkZXNjcmlwdGlvblwiOlwiQWdlbnQgTmV0d29yayBEYXNoYm9hcmQg4oCUIFdlYiBVSSBmb3IgbWFuYWdpbmcgQUkgQWdlbnQgbmV0d29ya3NcIixcIm1haW5cIjpcImFwcHMvZGVza3RvcC9lbGVjdHJvbi9tYWluLmNqc1wiLFwic2NyaXB0c1wiOntcImRldlwiOlwibmV4dCBkZXZcIixcImJ1aWxkXCI6XCJuZXh0IGJ1aWxkXCIsXCJwb3N0YnVpbGRcIjpcIm5vZGUgc2NyaXB0cy9zdGFtcC1idWlsZC1jb21taXQubWpzXCIsXCJzdGFydFwiOlwibmV4dCBzdGFydFwiLFwibGludFwiOlwiZXNsaW50XCIsXCJhcHA6YW5kcm9pZDppbml0XCI6XCJucHggY2FwIGFkZCBhbmRyb2lkXCIsXCJhcHA6YW5kcm9pZDpzeW5jXCI6XCJucHggY2FwIHN5bmMgYW5kcm9pZFwiLFwiYXBwOmFuZHJvaWQ6b3BlblwiOlwibnB4IGNhcCBvcGVuIGFuZHJvaWRcIixcImFwcDppb3M6aW5pdFwiOlwibnB4IGNhcCBhZGQgaW9zXCIsXCJhcHA6aW9zOnN5bmNcIjpcIm5weCBjYXAgc3luYyBpb3NcIixcImFwcDppb3M6b3BlblwiOlwibnB4IGNhcCBvcGVuIGlvc1wiLFwiYXBwOmRlc2t0b3BcIjpcImVsZWN0cm9uIGFwcHMvZGVza3RvcC9lbGVjdHJvbi9tYWluLmNqc1wiLFwiYXBwOmRlc2t0b3A6cGFja1wiOlwiZWxlY3Ryb24tYnVpbGRlciAtLWNvbmZpZyBlbGVjdHJvbi1idWlsZGVyLmpzb25cIixcInByZXB1Ymxpc2hPbmx5XCI6XCJbIC1mIC5uZXh0L0JVSUxEX0lEIF0gfHwgKGVjaG8gJ3ByZXB1Ymxpc2hPbmx5OiAubmV4dC9CVUlMRF9JRCBtaXNzaW5nIOKAlCBydW4gbnBtIHJ1biBidWlsZCBmaXJzdCAoc2VlIGNvbW1pdCAwNWMxZWJmIGJvZHkgZm9yIFIyMjQgY2h1bmstNTAwIHJvb3QgY2F1c2UpJyA+JjIgJiYgZXhpdCAxKVwifSxcImJpblwiOntcImFnZW50LW5ldHdvcmstZGFzaGJvYXJkXCI6XCIuL2Jpbi9zdGFydC5qc1wifSxcImhvbWVwYWdlXCI6XCJodHRwczovL2FuZXQuc2hcIixcInJlcG9zaXRvcnlcIjp7XCJ0eXBlXCI6XCJnaXRcIixcInVybFwiOlwiaHR0cHM6Ly9naXRodWIuY29tL3NsZWVwMmFnaS9hZ2VudC1uZXR3b3JrLWRhc2hib2FyZFwifSxcImJ1Z3NcIjp7XCJ1cmxcIjpcImh0dHBzOi8vZ2l0aHViLmNvbS9zbGVlcDJhZ2kvYWdlbnQtbmV0d29yay1kYXNoYm9hcmQvaXNzdWVzXCJ9LFwiYXV0aG9yXCI6XCJzbGVlcDJhZ2lcIixcImxpY2Vuc2VcIjpcIkFwYWNoZS0yLjBcIixcInB1Ymxpc2hDb25maWdcIjp7XCJhY2Nlc3NcIjpcInB1YmxpY1wifSxcImRlcGVuZGVuY2llc1wiOntcIkBjYXBhY2l0b3IvY29yZVwiOlwiXjcuNC40XCIsXCJuZXh0XCI6XCIxNi4yLjNcIixcInJlYWN0XCI6XCIxOS4yLjRcIixcInJlYWN0LWRvbVwiOlwiMTkuMi40XCIsXCJyZWFjdC1tYXJrZG93blwiOlwiXjEwLjEuMFwiLFwicmVtYXJrLWdmbVwiOlwiXjQuMC4xXCIsXCJzd3JcIjpcIl4yLjQuMVwifSxcImRldkRlcGVuZGVuY2llc1wiOntcIkBjYXBhY2l0b3IvYW5kcm9pZFwiOlwiXjcuNC40XCIsXCJAY2FwYWNpdG9yL2NsaVwiOlwiXjcuNC40XCIsXCJAY2FwYWNpdG9yL2lvc1wiOlwiXjcuNC40XCIsXCJAcGxheXdyaWdodC90ZXN0XCI6XCJeMS41OS4xXCIsXCJAdGFpbHdpbmRjc3MvcG9zdGNzc1wiOlwiXjRcIixcIkB0eXBlcy9ub2RlXCI6XCJeMjBcIixcIkB0eXBlcy9yZWFjdFwiOlwiXjE5XCIsXCJAdHlwZXMvcmVhY3QtZG9tXCI6XCJeMTlcIixcImVzbGludFwiOlwiXjlcIixcImVsZWN0cm9uXCI6XCJeMzkuMi42XCIsXCJlbGVjdHJvbi1idWlsZGVyXCI6XCJeMjYuMC4xMlwiLFwiZXNsaW50LWNvbmZpZy1uZXh0XCI6XCIxNi4yLjNcIixcInRhaWx3aW5kY3NzXCI6XCJeNFwiLFwidHlwZXNjcmlwdFwiOlwiXjVcIn19Il0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBIn0=","/** Dashboard package version sourced from package.json at build time.\n * Importing JSON requires `resolveJsonModule: true` in tsconfig, which\n * Next sets by default. Bumping is automated by the publish flow, so this\n * surfaces the real preview number in Settings → Dashboard. */\nimport pkg from '../../package.json' assert { type: 'json' };\n\nexport const DASHBOARD_VERSION: string = pkg.version;\n","'use client';\n\nimport { FormEvent, useState } from 'react';\nimport { DASHBOARD_VERSION } from '../lib/version';\n\nexport default function LoginPage() {\n const [mode, setMode] = useState<'login' | 'register'>('login');\n const [username, setUsername] = useState('');\n const [password, setPassword] = useState('');\n const [showPassword, setShowPassword] = useState(false);\n const [error, setError] = useState('');\n const [pending, setPending] = useState(false);\n\n const submit = async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n setPending(true);\n setError('');\n\n try {\n const res = await fetch('/api/auth/v3', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ action: mode, username, password }),\n });\n const data = await res.json();\n\n if (data.ok) {\n // Store V3 auth data for client-side features\n if (data.token) {\n sessionStorage.setItem('anet_v3_auth', JSON.stringify({\n user: data.user,\n token: data.token,\n networks: data.networks || [],\n currentNetwork: data.network_id || data.networks?.[0]?.network_id || '',\n }));\n }\n window.location.assign('/');\n return;\n }\n\n setError(data.error || 'Login failed');\n } catch {\n setError('Connection failed');\n }\n setPending(false);\n };\n\n return (\n <main className=\"min-h-screen bg-[#0b0b0d] text-gray-100 flex items-center justify-center relative overflow-hidden px-4 py-10 sm:py-16\">\n {/* Subtle off-grid background — restraint over AI-glow.\n Dark themes get a faint radial wash; light themes show a low-contrast\n dotted grid for surface texture without noise. */}\n <div className=\"absolute inset-0 pointer-events-none anet-login-bg\" aria-hidden />\n\n <div className=\"relative w-full max-w-sm\">\n <div className=\"text-center mb-7\">\n {/* Brand mark — uses the favicon 3-node mesh SVG which IS the\n agent-network concept (nodes + edges). Replaces the previous\n flat emerald square placeholder. */}\n <div className=\"inline-flex items-center justify-center w-16 h-16 mb-4 rounded-2xl bg-[#161618] border border-[#26262b] anet-login-mark\">\n <svg className=\"w-9 h-9\" viewBox=\"0 0 32 32\" aria-hidden>\n <circle cx=\"16\" cy=\"16\" r=\"10\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" opacity=\"0.25\" className=\"text-cyan-400 anet-login-mark-ring\" />\n <line x1=\"16\" y1=\"10\" x2=\"10\" y2=\"20\" stroke=\"currentColor\" strokeWidth=\"1\" opacity=\"0.5\" className=\"text-cyan-400 anet-login-mark-edge\" />\n <line x1=\"16\" y1=\"10\" x2=\"22\" y2=\"20\" stroke=\"currentColor\" strokeWidth=\"1\" opacity=\"0.5\" className=\"text-cyan-400 anet-login-mark-edge\" />\n <line x1=\"10\" y1=\"20\" x2=\"22\" y2=\"20\" stroke=\"currentColor\" strokeWidth=\"1\" opacity=\"0.5\" className=\"text-cyan-400 anet-login-mark-edge\" />\n <circle cx=\"16\" cy=\"10\" r=\"3\" fill=\"currentColor\" className=\"text-cyan-400 anet-login-mark-node-a\" />\n <circle cx=\"10\" cy=\"20\" r=\"3\" fill=\"currentColor\" className=\"text-green-400 anet-login-mark-node-b\" />\n <circle cx=\"22\" cy=\"20\" r=\"3\" fill=\"currentColor\" className=\"text-violet-400 anet-login-mark-node-c\" />\n </svg>\n </div>\n <h1 className=\"text-[22px] sm:text-2xl font-semibold text-white tracking-tight\">Agent Network</h1>\n <p className=\"text-[13px] text-gray-500 mt-1.5 leading-snug\">\n {mode === 'login'\n ? <>Welcome back · <span className=\"text-gray-600\">Tasks · Mesh · Messages</span></>\n : <>Create your account · <span className=\"text-gray-600\">Free preview · no credit card</span></>}\n </p>\n </div>\n\n {/* Mode toggle */}\n <div className=\"flex rounded-lg border border-[#26262b] bg-[#161618] p-1 mb-4\">\n {(['login', 'register'] as const).map(m => (\n <button key={m} type=\"button\" onClick={() => { setMode(m); setError(''); }}\n className={`flex-1 rounded-md px-3 py-2 text-sm transition-colors ${mode === m ? 'bg-cyan-500/10 text-cyan-300' : 'text-gray-500 hover:text-gray-200'}`}>\n {m === 'login' ? 'Sign in' : 'Register'}\n </button>\n ))}\n </div>\n\n <form onSubmit={submit} className=\"border border-[#26262b] bg-[#161618]/80 backdrop-blur-sm rounded-xl p-6 shadow-2xl shadow-black/30\">\n <label htmlFor=\"username\" className=\"block text-xs text-gray-500 mb-2 uppercase tracking-wider\">\n Username\n </label>\n <input id=\"username\" type=\"text\" value={username} onChange={e => setUsername(e.target.value)} autoFocus\n placeholder=\"Enter username\"\n className=\"w-full bg-[#0e0e10] border border-[#26262b] rounded-lg px-4 py-3 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:ring-1 focus:ring-cyan-500/20 focus:outline-none transition-all mb-4\" />\n\n <label htmlFor=\"password\" className=\"block text-xs text-gray-500 mb-2 uppercase tracking-wider\">\n Password\n </label>\n <div className=\"relative\">\n <input id=\"password\" type={showPassword ? 'text' : 'password'} value={password} onChange={e => setPassword(e.target.value)}\n placeholder=\"Enter password\"\n className=\"w-full bg-[#0e0e10] border border-[#26262b] rounded-lg px-4 pr-11 py-3 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:ring-1 focus:ring-cyan-500/20 focus:outline-none transition-all\" />\n <button\n type=\"button\"\n onClick={() => setShowPassword(s => !s)}\n tabIndex={-1}\n aria-label={showPassword ? 'Hide password' : 'Show password'}\n className=\"absolute inset-y-0 right-0 flex items-center justify-center min-w-[44px] px-3 text-gray-600 hover:text-gray-300 transition-colors\"\n >\n {showPassword ? (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24\" />\n <line x1=\"1\" y1=\"1\" x2=\"23\" y2=\"23\" />\n </svg>\n ) : (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\" />\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n </svg>\n )}\n </button>\n </div>\n\n {error && (\n <div className=\"mt-3 text-sm text-red-300 bg-red-500/5 border border-red-500/10 rounded-lg px-3 py-2\">{error}</div>\n )}\n\n <button type=\"submit\" disabled={pending || !password || !username.trim()}\n className=\"mt-5 w-full flex items-center justify-center gap-2 px-4 py-3 bg-gradient-to-r from-cyan-600 to-blue-600 hover:from-cyan-500 hover:to-blue-500 disabled:from-gray-800 disabled:to-gray-800 disabled:text-gray-600 text-white text-sm font-medium rounded-lg transition-all shadow-lg shadow-cyan-500/10 hover:shadow-cyan-500/20 disabled:shadow-none\">\n {pending && (\n <svg aria-hidden className=\"w-4 h-4 animate-spin\" viewBox=\"0 0 24 24\" fill=\"none\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"3\" opacity=\"0.25\" />\n <path d=\"M4 12a8 8 0 018-8\" stroke=\"currentColor\" strokeWidth=\"3\" strokeLinecap=\"round\" />\n </svg>\n )}\n <span>{pending ? (mode === 'login' ? 'Signing in…' : 'Registering…') : (mode === 'login' ? 'Sign in' : 'Create account')}</span>\n </button>\n </form>\n\n <p className=\"text-center text-[11px] text-gray-700 mt-6\">\n Sleep2AGI · Apache-2.0 · <a href=\"https://anet.sh\" className=\"hover:text-gray-500 transition-colors\">anet.sh</a>\n <span className=\"mx-1 text-gray-800\">·</span>\n <span className=\"font-mono\" title=\"@sleep2agi/agent-network-dashboard\">v{DASHBOARD_VERSION}</span>\n </p>\n </div>\n </main>\n );\n}\n"],"names":["LoginPage","mode","setMode","username","setUsername","password","setPassword","showPassword","setShowPassword","error","setError","pending","setPending","submit","event","preventDefault","res","fetch","method","headers","body","JSON","stringify","action","data","json","ok","token","sessionStorage","setItem","user","networks","currentNetwork","network_id","window","location","assign","className","viewBox","cx","cy","r","fill","stroke","strokeWidth","opacity","x1","y1","x2","y2","map","m","type","onClick","onSubmit","htmlFor","id","value","onChange","e","target","autoFocus","placeholder","s","tabIndex","width","height","strokeLinecap","strokeLinejoin","d","disabled","trim","href","title"],"mappings":"kEACsE,mEECtE,EAAA,EAAA,CAAA,CAAA,OACA,EAAA,EAAA,CAAA,CAAA,wBAEe,SAASA,EACtB,GAAM,CAACC,EAAMC,EAAQ,CAAG,CAAA,EAAA,EAAA,QAAA,AAAQ,EAAuB,SACjD,CAACC,EAAUC,EAAY,CAAG,CAAA,EAAA,EAAA,QAAA,AAAQ,EAAC,IACnC,CAACC,EAAUC,EAAY,CAAG,CAAA,EAAA,EAAA,QAAA,AAAQ,EAAC,IACnC,CAACC,EAAcC,EAAgB,CAAG,CAAA,EAAA,EAAA,QAAA,AAAQ,GAAC,GAC3C,CAACC,EAAOC,EAAS,CAAG,CAAA,EAAA,EAAA,QAAA,AAAQ,EAAC,IAC7B,CAACC,EAASC,EAAW,CAAG,CAAA,EAAA,EAAA,QAAA,AAAQ,GAAC,GAEjCC,EAAS,MAAOC,IACpBA,EAAMC,cAAc,GACpBH,GAAW,GACXF,EAAS,IAET,GAAI,CACF,IAAMM,EAAM,MAAMC,MAAM,eAAgB,CACtCC,OAAQ,OACRC,QAAS,CAAE,eAAgB,kBAAmB,EAC9CC,KAAMC,KAAKC,SAAS,CAAC,CAAEC,OAAQtB,WAAME,WAAUE,CAAS,EAC1D,GACMmB,EAAO,MAAMR,EAAIS,IAAI,GAE3B,GAAID,EAAKE,EAAE,CAAE,CAEPF,EAAKG,KAAK,EAAE,AACdC,eAAeC,OAAO,CAAC,eAAgBR,KAAKC,SAAS,CAAC,CACpDQ,KAAMN,EAAKM,IAAI,CACfH,MAAOH,EAAKG,KAAK,CACjBI,SAAUP,EAAKO,QAAQ,EAAI,EAAE,CAC7BC,eAAgBR,EAAKS,UAAU,EAAIT,EAAKO,QAAQ,EAAE,CAAC,EAAE,EAAEE,YAAc,EACvE,IAEFC,OAAOC,QAAQ,CAACC,MAAM,CAAC,KACvB,MACF,CAEA1B,EAASc,EAAKf,KAAK,EAAI,eACzB,CAAE,KAAM,CACNC,EAAS,oBACX,CACAE,GAAW,EACb,EAEA,MACE,CAAA,EAAA,EAAA,IAAA,EAAC,OAAA,CAAKyB,UAAU,kIAId,CAAA,EAAA,EAAA,GAAA,EAAC,MAAA,CAAIA,UAAU,qDAAqD,aAAW,CAAA,CAAA,IAE/E,CAAA,EAAA,EAAA,IAAA,EAAC,MAAA,CAAIA,UAAU,qCACb,CAAA,EAAA,EAAA,IAAA,EAAC,MAAA,CAAIA,UAAU,6BAIb,CAAA,EAAA,EAAA,GAAA,EAAC,MAAA,CAAIA,UAAU,mIACb,CAAA,EAAA,EAAA,IAAA,EAAC,MAAA,CAAIA,UAAU,UAAUC,QAAQ,YAAY,aAAW,CAAA,CAAA,YACtD,CAAA,EAAA,EAAA,GAAA,EAAC,SAAA,CAAOC,GAAG,KAAKC,GAAG,KAAKC,EAAE,KAAKC,KAAK,OAAOC,OAAO,eAAeC,YAAY,MAAMC,QAAQ,OAAOR,UAAU,uCAC5G,CAAA,EAAA,EAAA,GAAA,EAAC,OAAA,CAAKS,GAAG,KAAKC,GAAG,KAAKC,GAAG,KAAKC,GAAG,KAAKN,OAAO,eAAeC,YAAY,IAAIC,QAAQ,MAAMR,UAAU,uCACpG,CAAA,EAAA,EAAA,GAAA,EAAC,OAAA,CAAKS,GAAG,KAAKC,GAAG,KAAKC,GAAG,KAAKC,GAAG,KAAKN,OAAO,eAAeC,YAAY,IAAIC,QAAQ,MAAMR,UAAU,uCACpG,CAAA,EAAA,EAAA,GAAA,EAAC,OAAA,CAAKS,GAAG,KAAKC,GAAG,KAAKC,GAAG,KAAKC,GAAG,KAAKN,OAAO,eAAeC,YAAY,IAAIC,QAAQ,MAAMR,UAAU,uCACpG,CAAA,EAAA,EAAA,GAAA,EAAC,SAAA,CAAOE,GAAG,KAAKC,GAAG,KAAKC,EAAE,IAAIC,KAAK,eAAeL,UAAU,yCAC5D,CAAA,EAAA,EAAA,GAAA,EAAC,SAAA,CAAOE,GAAG,KAAKC,GAAG,KAAKC,EAAE,IAAIC,KAAK,eAAeL,UAAU,0CAC5D,CAAA,EAAA,EAAA,GAAA,EAAC,SAAA,CAAOE,GAAG,KAAKC,GAAG,KAAKC,EAAE,IAAIC,KAAK,eAAeL,UAAU,gDAGhE,CAAA,EAAA,EAAA,GAAA,EAAC,KAAA,CAAGA,UAAU,2EAAkE,kBAChF,CAAA,EAAA,EAAA,GAAA,EAAC,IAAA,CAAEA,UAAU,yDACVpC,AAAS,YACN,CAAA,EAAA,EAAA,IAAA,EAAA,EAAA,QAAA,CAAA,WAAE,kBAAe,CAAA,EAAA,EAAA,GAAA,EAAC,OAAA,CAAKoC,UAAU,yBAAgB,+BACjD,CAAA,EAAA,EAAA,IAAA,EAAA,EAAA,QAAA,CAAA,WAAE,yBAAsB,CAAA,EAAA,EAAA,GAAA,EAAC,OAAA,CAAKA,UAAU,yBAAgB,0CAKhE,CAAA,EAAA,EAAA,GAAA,EAAC,MAAA,CAAIA,UAAU,yEACX,CAAC,QAAS,WAAW,CAAWa,GAAG,CAACC,GACpC,CAAA,EAAA,EAAA,GAAA,EAAC,SAAA,CAAeC,KAAK,SAASC,QAAS,KAAQnD,EAAQiD,GAAIzC,EAAS,GAAK,EACvE2B,UAAW,CAAC,sDAAsD,EAAEpC,IAASkD,EAAI,+BAAiC,oCAAA,CAAqC,UAChJ,UAANA,EAAgB,UAAY,YAFlBA,MAOjB,CAAA,EAAA,EAAA,IAAA,EAAC,OAAA,CAAKG,SAAUzC,EAAQwB,UAAU,+GAChC,CAAA,EAAA,EAAA,GAAA,EAAC,QAAA,CAAMkB,QAAQ,WAAWlB,UAAU,qEAA4D,aAGhG,CAAA,EAAA,EAAA,GAAA,EAAC,QAAA,CAAMmB,GAAG,WAAWJ,KAAK,OAAOK,MAAOtD,EAAUuD,SAAUC,GAAKvD,EAAYuD,EAAEC,MAAM,CAACH,KAAK,EAAGI,SAAS,CAAA,CAAA,EACrGC,YAAY,iBACZzB,UAAU,8NAEZ,CAAA,EAAA,EAAA,GAAA,EAAC,QAAA,CAAMkB,QAAQ,WAAWlB,UAAU,qEAA4D,aAGhG,CAAA,EAAA,EAAA,IAAA,EAAC,MAAA,CAAIA,UAAU,qBACb,CAAA,EAAA,EAAA,GAAA,EAAC,QAAA,CAAMmB,GAAG,WAAWJ,KAAM7C,EAAe,OAAS,WAAYkD,MAAOpD,EAAUqD,SAAUC,GAAKrD,EAAYqD,EAAEC,MAAM,CAACH,KAAK,EACvHK,YAAY,iBACZzB,UAAU,+NACZ,CAAA,EAAA,EAAA,GAAA,EAAC,SAAA,CACCe,KAAK,SACLC,QAAS,IAAM7C,EAAgBuD,GAAK,CAACA,GACrCC,SAAU,CAAC,EACX,aAAYzD,EAAe,gBAAkB,gBAC7C8B,UAAU,6IAET9B,EACC,CAAA,EAAA,EAAA,IAAA,EAAC,MAAA,CAAI0D,MAAM,KAAKC,OAAO,KAAK5B,QAAQ,YAAYI,KAAK,OAAOC,OAAO,eAAeC,YAAY,MAAMuB,cAAc,QAAQC,eAAe,kBACvI,CAAA,EAAA,EAAA,GAAA,EAAC,OAAA,CAAKC,EAAE,yLACR,CAAA,EAAA,EAAA,GAAA,EAAC,OAAA,CAAKvB,GAAG,IAAIC,GAAG,IAAIC,GAAG,KAAKC,GAAG,UAGjC,CAAA,EAAA,EAAA,IAAA,EAAC,MAAA,CAAIgB,MAAM,KAAKC,OAAO,KAAK5B,QAAQ,YAAYI,KAAK,OAAOC,OAAO,eAAeC,YAAY,MAAMuB,cAAc,QAAQC,eAAe,kBACvI,CAAA,EAAA,EAAA,GAAA,EAAC,OAAA,CAAKC,EAAE,iDACR,CAAA,EAAA,EAAA,GAAA,EAAC,SAAA,CAAO9B,GAAG,KAAKC,GAAG,KAAKC,EAAE,cAMjChC,GACC,CAAA,EAAA,EAAA,GAAA,EAAC,MAAA,CAAI4B,UAAU,gGAAwF5B,IAGzG,CAAA,EAAA,EAAA,IAAA,EAAC,SAAA,CAAO2C,KAAK,SAASkB,SAAU3D,GAAW,CAACN,GAAY,CAACF,EAASoE,IAAI,GACpElC,UAAU,iWACT1B,GACC,CAAA,EAAA,EAAA,IAAA,EAAC,MAAA,CAAI,aAAW,CAAA,CAAA,EAAC0B,UAAU,uBAAuBC,QAAQ,YAAYI,KAAK,iBACzE,CAAA,EAAA,EAAA,GAAA,EAAC,SAAA,CAAOH,GAAG,KAAKC,GAAG,KAAKC,EAAE,KAAKE,OAAO,eAAeC,YAAY,IAAIC,QAAQ,SAC7E,CAAA,EAAA,EAAA,GAAA,EAAC,OAAA,CAAKwB,EAAE,oBAAoB1B,OAAO,eAAeC,YAAY,IAAIuB,cAAc,aAGpF,CAAA,EAAA,EAAA,GAAA,EAAC,OAAA,UAAMxD,EAAoB,UAATV,EAAmB,cAAgB,eAA4B,UAATA,EAAmB,UAAY,yBAI3G,CAAA,EAAA,EAAA,IAAA,EAAC,IAAA,CAAEoC,UAAU,uDAA6C,4BAC/B,CAAA,EAAA,EAAA,GAAA,EAAC,IAAA,CAAEmC,KAAK,kBAAkBnC,UAAU,iDAAwC,YACrG,CAAA,EAAA,EAAA,GAAA,EAAC,OAAA,CAAKA,UAAU,8BAAqB,MACrC,CAAA,EAAA,EAAA,IAAA,EAAC,OAAA,CAAKA,UAAU,YAAYoC,MAAM,+CAAqC,IAAE,EAAA,iBAAiB,YAKpG"}
|
|
1
|
+
{"version":3,"sources":["../../../../../agent-network-dashboard/package.json.%5Bjson%5D.mjs","../../../../../agent-network-dashboard/app/lib/version.ts","../../../../../agent-network-dashboard/app/login/page.tsx"],"sourcesContent":["\"use turbopack no side effects\";\nexport default {\"name\":\"@sleep2agi/agent-network-dashboard\",\"version\":\"0.6.3-preview.9\",\"description\":\"Agent Network Dashboard — Web UI for managing AI Agent networks\",\"main\":\"apps/desktop/electron/main.cjs\",\"scripts\":{\"dev\":\"next dev\",\"build\":\"next build\",\"postbuild\":\"node scripts/stamp-build-commit.mjs\",\"start\":\"next start\",\"lint\":\"eslint\",\"app:android:init\":\"npx cap add android\",\"app:android:sync\":\"npx cap sync android\",\"app:android:open\":\"npx cap open android\",\"app:ios:init\":\"npx cap add ios\",\"app:ios:sync\":\"npx cap sync ios\",\"app:ios:open\":\"npx cap open ios\",\"app:desktop\":\"electron apps/desktop/electron/main.cjs\",\"app:desktop:pack\":\"electron-builder --config electron-builder.json\",\"prepublishOnly\":\"[ -f .next/BUILD_ID ] || (echo 'prepublishOnly: .next/BUILD_ID missing — run npm run build first (see commit 05c1ebf body for R224 chunk-500 root cause)' >&2 && exit 1)\"},\"bin\":{\"agent-network-dashboard\":\"./bin/start.js\"},\"homepage\":\"https://anet.sh\",\"repository\":{\"type\":\"git\",\"url\":\"https://github.com/sleep2agi/agent-network-dashboard\"},\"bugs\":{\"url\":\"https://github.com/sleep2agi/agent-network-dashboard/issues\"},\"author\":\"sleep2agi\",\"license\":\"Apache-2.0\",\"publishConfig\":{\"access\":\"public\"},\"dependencies\":{\"@capacitor/core\":\"^7.4.4\",\"next\":\"16.2.3\",\"react\":\"19.2.4\",\"react-dom\":\"19.2.4\",\"react-markdown\":\"^10.1.0\",\"remark-gfm\":\"^4.0.1\",\"swr\":\"^2.4.1\"},\"devDependencies\":{\"@capacitor/android\":\"^7.4.4\",\"@capacitor/cli\":\"^7.4.4\",\"@capacitor/ios\":\"^7.4.4\",\"@playwright/test\":\"^1.59.1\",\"@tailwindcss/postcss\":\"^4\",\"@types/node\":\"^20\",\"@types/react\":\"^19\",\"@types/react-dom\":\"^19\",\"eslint\":\"^9\",\"electron\":\"^39.2.6\",\"electron-builder\":\"^26.0.12\",\"eslint-config-next\":\"16.2.3\",\"tailwindcss\":\"^4\",\"typescript\":\"^5\"}};\n//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInR1cmJvcGFjazovLy9hZ2VudC1uZXR3b3JrLWRhc2hib2FyZC9wYWNrYWdlLmpzb24iXSwic291cmNlc0NvbnRlbnQiOlsie1wibmFtZVwiOlwiQHNsZWVwMmFnaS9hZ2VudC1uZXR3b3JrLWRhc2hib2FyZFwiLFwidmVyc2lvblwiOlwiMC42LjMtcHJldmlldy45XCIsXCJkZXNjcmlwdGlvblwiOlwiQWdlbnQgTmV0d29yayBEYXNoYm9hcmQg4oCUIFdlYiBVSSBmb3IgbWFuYWdpbmcgQUkgQWdlbnQgbmV0d29ya3NcIixcIm1haW5cIjpcImFwcHMvZGVza3RvcC9lbGVjdHJvbi9tYWluLmNqc1wiLFwic2NyaXB0c1wiOntcImRldlwiOlwibmV4dCBkZXZcIixcImJ1aWxkXCI6XCJuZXh0IGJ1aWxkXCIsXCJwb3N0YnVpbGRcIjpcIm5vZGUgc2NyaXB0cy9zdGFtcC1idWlsZC1jb21taXQubWpzXCIsXCJzdGFydFwiOlwibmV4dCBzdGFydFwiLFwibGludFwiOlwiZXNsaW50XCIsXCJhcHA6YW5kcm9pZDppbml0XCI6XCJucHggY2FwIGFkZCBhbmRyb2lkXCIsXCJhcHA6YW5kcm9pZDpzeW5jXCI6XCJucHggY2FwIHN5bmMgYW5kcm9pZFwiLFwiYXBwOmFuZHJvaWQ6b3BlblwiOlwibnB4IGNhcCBvcGVuIGFuZHJvaWRcIixcImFwcDppb3M6aW5pdFwiOlwibnB4IGNhcCBhZGQgaW9zXCIsXCJhcHA6aW9zOnN5bmNcIjpcIm5weCBjYXAgc3luYyBpb3NcIixcImFwcDppb3M6b3BlblwiOlwibnB4IGNhcCBvcGVuIGlvc1wiLFwiYXBwOmRlc2t0b3BcIjpcImVsZWN0cm9uIGFwcHMvZGVza3RvcC9lbGVjdHJvbi9tYWluLmNqc1wiLFwiYXBwOmRlc2t0b3A6cGFja1wiOlwiZWxlY3Ryb24tYnVpbGRlciAtLWNvbmZpZyBlbGVjdHJvbi1idWlsZGVyLmpzb25cIixcInByZXB1Ymxpc2hPbmx5XCI6XCJbIC1mIC5uZXh0L0JVSUxEX0lEIF0gfHwgKGVjaG8gJ3ByZXB1Ymxpc2hPbmx5OiAubmV4dC9CVUlMRF9JRCBtaXNzaW5nIOKAlCBydW4gbnBtIHJ1biBidWlsZCBmaXJzdCAoc2VlIGNvbW1pdCAwNWMxZWJmIGJvZHkgZm9yIFIyMjQgY2h1bmstNTAwIHJvb3QgY2F1c2UpJyA+JjIgJiYgZXhpdCAxKVwifSxcImJpblwiOntcImFnZW50LW5ldHdvcmstZGFzaGJvYXJkXCI6XCIuL2Jpbi9zdGFydC5qc1wifSxcImhvbWVwYWdlXCI6XCJodHRwczovL2FuZXQuc2hcIixcInJlcG9zaXRvcnlcIjp7XCJ0eXBlXCI6XCJnaXRcIixcInVybFwiOlwiaHR0cHM6Ly9naXRodWIuY29tL3NsZWVwMmFnaS9hZ2VudC1uZXR3b3JrLWRhc2hib2FyZFwifSxcImJ1Z3NcIjp7XCJ1cmxcIjpcImh0dHBzOi8vZ2l0aHViLmNvbS9zbGVlcDJhZ2kvYWdlbnQtbmV0d29yay1kYXNoYm9hcmQvaXNzdWVzXCJ9LFwiYXV0aG9yXCI6XCJzbGVlcDJhZ2lcIixcImxpY2Vuc2VcIjpcIkFwYWNoZS0yLjBcIixcInB1Ymxpc2hDb25maWdcIjp7XCJhY2Nlc3NcIjpcInB1YmxpY1wifSxcImRlcGVuZGVuY2llc1wiOntcIkBjYXBhY2l0b3IvY29yZVwiOlwiXjcuNC40XCIsXCJuZXh0XCI6XCIxNi4yLjNcIixcInJlYWN0XCI6XCIxOS4yLjRcIixcInJlYWN0LWRvbVwiOlwiMTkuMi40XCIsXCJyZWFjdC1tYXJrZG93blwiOlwiXjEwLjEuMFwiLFwicmVtYXJrLWdmbVwiOlwiXjQuMC4xXCIsXCJzd3JcIjpcIl4yLjQuMVwifSxcImRldkRlcGVuZGVuY2llc1wiOntcIkBjYXBhY2l0b3IvYW5kcm9pZFwiOlwiXjcuNC40XCIsXCJAY2FwYWNpdG9yL2NsaVwiOlwiXjcuNC40XCIsXCJAY2FwYWNpdG9yL2lvc1wiOlwiXjcuNC40XCIsXCJAcGxheXdyaWdodC90ZXN0XCI6XCJeMS41OS4xXCIsXCJAdGFpbHdpbmRjc3MvcG9zdGNzc1wiOlwiXjRcIixcIkB0eXBlcy9ub2RlXCI6XCJeMjBcIixcIkB0eXBlcy9yZWFjdFwiOlwiXjE5XCIsXCJAdHlwZXMvcmVhY3QtZG9tXCI6XCJeMTlcIixcImVzbGludFwiOlwiXjlcIixcImVsZWN0cm9uXCI6XCJeMzkuMi42XCIsXCJlbGVjdHJvbi1idWlsZGVyXCI6XCJeMjYuMC4xMlwiLFwiZXNsaW50LWNvbmZpZy1uZXh0XCI6XCIxNi4yLjNcIixcInRhaWx3aW5kY3NzXCI6XCJeNFwiLFwidHlwZXNjcmlwdFwiOlwiXjVcIn19Il0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBIn0=","/** Dashboard package version sourced from package.json at build time.\n * Importing JSON requires `resolveJsonModule: true` in tsconfig, which\n * Next sets by default. Bumping is automated by the publish flow, so this\n * surfaces the real preview number in Settings → Dashboard. */\nimport pkg from '../../package.json' assert { type: 'json' };\n\nexport const DASHBOARD_VERSION: string = pkg.version;\n","'use client';\n\nimport { FormEvent, useState } from 'react';\nimport { DASHBOARD_VERSION } from '../lib/version';\n\nexport default function LoginPage() {\n const [mode, setMode] = useState<'login' | 'register'>('login');\n const [username, setUsername] = useState('');\n const [password, setPassword] = useState('');\n const [showPassword, setShowPassword] = useState(false);\n const [error, setError] = useState('');\n const [pending, setPending] = useState(false);\n\n const submit = async (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n setPending(true);\n setError('');\n\n try {\n const res = await fetch('/api/auth/v3', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ action: mode, username, password }),\n });\n const data = await res.json();\n\n if (data.ok) {\n // Store V3 auth data for client-side features\n if (data.token) {\n sessionStorage.setItem('anet_v3_auth', JSON.stringify({\n user: data.user,\n token: data.token,\n networks: data.networks || [],\n currentNetwork: data.network_id || data.networks?.[0]?.network_id || '',\n }));\n }\n window.location.assign('/');\n return;\n }\n\n setError(data.error || 'Login failed');\n } catch {\n setError('Connection failed');\n }\n setPending(false);\n };\n\n return (\n <main className=\"min-h-screen bg-[#0b0b0d] text-gray-100 flex items-center justify-center relative overflow-hidden px-4 py-10 sm:py-16\">\n {/* Subtle off-grid background — restraint over AI-glow.\n Dark themes get a faint radial wash; light themes show a low-contrast\n dotted grid for surface texture without noise. */}\n <div className=\"absolute inset-0 pointer-events-none anet-login-bg\" aria-hidden />\n\n <div className=\"relative w-full max-w-sm\">\n <div className=\"text-center mb-7\">\n {/* Brand mark — uses the favicon 3-node mesh SVG which IS the\n agent-network concept (nodes + edges). Replaces the previous\n flat emerald square placeholder. */}\n <div className=\"inline-flex items-center justify-center w-16 h-16 mb-4 rounded-2xl bg-[#161618] border border-[#26262b] anet-login-mark\">\n <svg className=\"w-9 h-9\" viewBox=\"0 0 32 32\" aria-hidden>\n <circle cx=\"16\" cy=\"16\" r=\"10\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" opacity=\"0.25\" className=\"text-cyan-400 anet-login-mark-ring\" />\n <line x1=\"16\" y1=\"10\" x2=\"10\" y2=\"20\" stroke=\"currentColor\" strokeWidth=\"1\" opacity=\"0.5\" className=\"text-cyan-400 anet-login-mark-edge\" />\n <line x1=\"16\" y1=\"10\" x2=\"22\" y2=\"20\" stroke=\"currentColor\" strokeWidth=\"1\" opacity=\"0.5\" className=\"text-cyan-400 anet-login-mark-edge\" />\n <line x1=\"10\" y1=\"20\" x2=\"22\" y2=\"20\" stroke=\"currentColor\" strokeWidth=\"1\" opacity=\"0.5\" className=\"text-cyan-400 anet-login-mark-edge\" />\n <circle cx=\"16\" cy=\"10\" r=\"3\" fill=\"currentColor\" className=\"text-cyan-400 anet-login-mark-node-a\" />\n <circle cx=\"10\" cy=\"20\" r=\"3\" fill=\"currentColor\" className=\"text-green-400 anet-login-mark-node-b\" />\n <circle cx=\"22\" cy=\"20\" r=\"3\" fill=\"currentColor\" className=\"text-violet-400 anet-login-mark-node-c\" />\n </svg>\n </div>\n <h1 className=\"text-[22px] sm:text-2xl font-semibold text-white tracking-tight\">Agent Network</h1>\n <p className=\"text-[13px] text-gray-500 mt-1.5 leading-snug\">\n {mode === 'login'\n ? <>Welcome back · <span className=\"text-gray-600\">Tasks · Mesh · Messages</span></>\n : <>Create your account · <span className=\"text-gray-600\">Free preview · no credit card</span></>}\n </p>\n </div>\n\n {/* Mode toggle */}\n <div className=\"flex rounded-lg border border-[#26262b] bg-[#161618] p-1 mb-4\">\n {(['login', 'register'] as const).map(m => (\n <button key={m} type=\"button\" onClick={() => { setMode(m); setError(''); }}\n className={`flex-1 rounded-md px-3 py-2 text-sm transition-colors ${mode === m ? 'bg-cyan-500/10 text-cyan-300' : 'text-gray-500 hover:text-gray-200'}`}>\n {m === 'login' ? 'Sign in' : 'Register'}\n </button>\n ))}\n </div>\n\n <form onSubmit={submit} className=\"border border-[#26262b] bg-[#161618]/80 backdrop-blur-sm rounded-xl p-6 shadow-2xl shadow-black/30\">\n <label htmlFor=\"username\" className=\"block text-xs text-gray-500 mb-2 uppercase tracking-wider\">\n Username\n </label>\n <input id=\"username\" type=\"text\" value={username} onChange={e => setUsername(e.target.value)} autoFocus\n placeholder=\"Enter username\"\n className=\"w-full bg-[#0e0e10] border border-[#26262b] rounded-lg px-4 py-3 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:ring-1 focus:ring-cyan-500/20 focus:outline-none transition-all mb-4\" />\n\n <label htmlFor=\"password\" className=\"block text-xs text-gray-500 mb-2 uppercase tracking-wider\">\n Password\n </label>\n <div className=\"relative\">\n <input id=\"password\" type={showPassword ? 'text' : 'password'} value={password} onChange={e => setPassword(e.target.value)}\n placeholder=\"Enter password\"\n className=\"w-full bg-[#0e0e10] border border-[#26262b] rounded-lg px-4 pr-11 py-3 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/50 focus:ring-1 focus:ring-cyan-500/20 focus:outline-none transition-all\" />\n <button\n type=\"button\"\n onClick={() => setShowPassword(s => !s)}\n tabIndex={-1}\n aria-label={showPassword ? 'Hide password' : 'Show password'}\n className=\"absolute inset-y-0 right-0 flex items-center justify-center min-w-[44px] px-3 text-gray-600 hover:text-gray-300 transition-colors\"\n >\n {showPassword ? (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24\" />\n <line x1=\"1\" y1=\"1\" x2=\"23\" y2=\"23\" />\n </svg>\n ) : (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\" />\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n </svg>\n )}\n </button>\n </div>\n\n {error && (\n <div className=\"mt-3 text-sm text-red-300 bg-red-500/5 border border-red-500/10 rounded-lg px-3 py-2\">{error}</div>\n )}\n\n <button type=\"submit\" disabled={pending || !password || !username.trim()}\n className=\"mt-5 w-full flex items-center justify-center gap-2 px-4 py-3 bg-gradient-to-r from-cyan-600 to-blue-600 hover:from-cyan-500 hover:to-blue-500 disabled:from-gray-800 disabled:to-gray-800 disabled:text-gray-600 text-white text-sm font-medium rounded-lg transition-all shadow-lg shadow-cyan-500/10 hover:shadow-cyan-500/20 disabled:shadow-none\">\n {pending && (\n <svg aria-hidden className=\"w-4 h-4 animate-spin\" viewBox=\"0 0 24 24\" fill=\"none\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"3\" opacity=\"0.25\" />\n <path d=\"M4 12a8 8 0 018-8\" stroke=\"currentColor\" strokeWidth=\"3\" strokeLinecap=\"round\" />\n </svg>\n )}\n <span>{pending ? (mode === 'login' ? 'Signing in…' : 'Registering…') : (mode === 'login' ? 'Sign in' : 'Create account')}</span>\n </button>\n </form>\n\n <p className=\"text-center text-[11px] text-gray-700 mt-6\">\n Sleep2AGI · Apache-2.0 · <a href=\"https://anet.sh\" className=\"hover:text-gray-500 transition-colors\">anet.sh</a>\n <span className=\"mx-1 text-gray-800\">·</span>\n <span className=\"font-mono\" title=\"@sleep2agi/agent-network-dashboard\">v{DASHBOARD_VERSION}</span>\n </p>\n </div>\n </main>\n );\n}\n"],"names":["LoginPage","mode","setMode","username","setUsername","password","setPassword","showPassword","setShowPassword","error","setError","pending","setPending","submit","event","preventDefault","res","fetch","method","headers","body","JSON","stringify","action","data","json","ok","token","sessionStorage","setItem","user","networks","currentNetwork","network_id","window","location","assign","className","viewBox","cx","cy","r","fill","stroke","strokeWidth","opacity","x1","y1","x2","y2","map","m","type","onClick","onSubmit","htmlFor","id","value","onChange","e","target","autoFocus","placeholder","s","tabIndex","width","height","strokeLinecap","strokeLinejoin","d","disabled","trim","href","title"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
module.exports=[85888,a=>{"use strict";var b=a.i(64247),c=a.i(54413),d=a.i(19595);let e=[{id:"anthropic",label:"Anthropic 兼容端点(Claude / DeepSeek / MiniMax 等)"},{id:"openai-compatible",label:"OpenAI-compatible(native,即将支持)",disabled:!0},{id:"grok",label:"Grok(native,即将支持)",disabled:!0}],f=[{id:"deepseek",label:"DeepSeek",type:"anthropic",base_url:"https://api.deepseek.com/anthropic",models:["deepseek-chat","deepseek-reasoner"],keyPlaceholder:"sk-…"},{id:"minimax",label:"MiniMax",type:"anthropic",base_url:"https://api.minimax.chat/anthropic",models:["MiniMax-M3","MiniMax-M2"],keyPlaceholder:"MiniMax API key"},{id:"glm",label:"智谱 GLM",type:"anthropic",base_url:"https://open.bigmodel.cn/api/anthropic",models:["glm-4.6","glm-4.5","glm-4-plus"],keyPlaceholder:"{id}.{secret}"},{id:"claude",label:"Claude(Anthropic 官方)",type:"anthropic",base_url:"https://api.anthropic.com",models:["claude-sonnet-4-6","claude-opus-4-1","claude-haiku-4-5"],keyPlaceholder:"sk-ant-…"},{id:"custom",label:"自定义(手动填 URL / 模型)",type:"anthropic",base_url:"",models:[]}];function g(a){if(!a)return"custom";let b=f.find(b=>"custom"!==b.id&&b.base_url===a.trim());return b?b.id:"custom"}let h="w-full rounded-md border border-[#26262b] bg-[#0e0e10] px-3 py-2 text-sm text-gray-200 focus:border-cyan-600 focus:outline-none";function i({initial:a,onClose:d,onSaved:j}){let k=!!a?.provider_id,[l,m]=(0,c.useState)(()=>g(a?.base_url)),[n,o]=(0,c.useState)(a?.name??""),[p,q]=(0,c.useState)(a?.type??e[0].id),[r,s]=(0,c.useState)(a?.base_url??""),[t,u]=(0,c.useState)(a?.models??[]),[v,w]=(0,c.useState)(()=>{let b=g(a?.base_url);if("custom"===b)return(a?.models??[]).join(", ");let c=f.find(a=>a.id===b);return(a?.models??[]).filter(a=>!c?.models.includes(a)).join(", ")}),[x,y]=(0,c.useState)(""),[z,A]=(0,c.useState)(a?.enabled??!0),[B,C]=(0,c.useState)("form"),[D,E]=(0,c.useState)(""),F=(0,c.useMemo)(()=>f.find(a=>a.id===l)??f[f.length-1],[l]),G="custom"===l;(0,c.useEffect)(()=>{let a=a=>{"Escape"===a.key&&"saving"!==B&&d()};return window.addEventListener("keydown",a),()=>window.removeEventListener("keydown",a)},[d,B]);let H=(0,c.useMemo)(()=>{let a=v.split(",").map(a=>a.trim()).filter(Boolean);return G?a:Array.from(new Set([...t,...a]))},[G,t,v]),I=n.trim().length>0,J=r.trim().length>0,K=k||x.trim().length>0,L=H.length>0,M=I&&J&&K&&L&&"saving"!==B;async function N(){C("saving"),E("");let b={...a?.provider_id?{provider_id:a.provider_id}:{},name:n.trim(),type:p,base_url:r.trim(),models:H,enabled:z,...x?{apiKey:x}:{}};try{let a=await fetch("/api/anet/providers",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(b)}),c=await a.json().catch(()=>({}));a.ok&&c?.ok?(C("done"),j?.(),setTimeout(d,350)):c?.unconfirmed?(C("unconfirmed"),E(`后端 RFC-028 未就绪:${c?.error||"不可用"}(hub 需 #308 + 升级)`)):(C("error"),E(c?.error?`保存失败:${c.error}`:"保存失败"))}catch(a){C("error"),E(`保存失败:${a instanceof Error?a.message:"网络错误"}`)}}return(0,b.jsxs)(b.Fragment,{children:[(0,b.jsx)("div",{className:"fixed inset-0 bg-black/50 z-40",onClick:()=>"saving"!==B&&d(),"aria-hidden":!0}),(0,b.jsxs)("div",{role:"dialog","aria-label":k?"编辑供应商":"新增供应商",className:"fixed left-1/2 top-1/2 z-50 max-h-[92vh] w-[min(94vw,480px)] -translate-x-1/2 -translate-y-1/2 overflow-y-auto rounded-xl border border-[#26262b] bg-[#161618] shadow-2xl",children:[(0,b.jsxs)("div",{className:"sticky top-0 flex items-center justify-between border-b border-[#26262b] bg-[#161618] px-4 py-3",children:[(0,b.jsx)("div",{className:"text-sm font-semibold text-white",children:k?"编辑供应商":"新增供应商"}),(0,b.jsx)("button",{onClick:d,"aria-label":"关闭",className:"inline-flex h-8 w-8 items-center justify-center rounded-md text-gray-500 hover:bg-white/5 hover:text-white",children:(0,b.jsx)("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round","aria-hidden":!0,children:(0,b.jsx)("path",{d:"M18 6 6 18M6 6l12 12"})})})]}),(0,b.jsxs)("div",{className:"space-y-3 px-4 py-4",children:["unconfirmed"===B||"error"===B?(0,b.jsx)("div",{className:`rounded-md border px-3 py-2 text-xs ${"unconfirmed"===B?"border-amber-500/30 bg-amber-500/5 text-amber-300":"border-red-500/30 bg-red-500/5 text-red-300"}`,children:D}):null,!k&&(0,b.jsxs)("label",{className:"block space-y-1",children:[(0,b.jsxs)("span",{className:"text-xs text-gray-400",children:["供应商 ",(0,b.jsx)("span",{className:"text-red-400/70",children:"*"})]}),(0,b.jsx)("select",{value:l,onChange:a=>{var b;let c;return m(b=a.target.value),void((c=f.find(a=>a.id===b))&&("custom"!==b?(q(c.type),s(c.base_url),u(c.models),(!n.trim()||f.some(a=>a.label===n.trim()))&&o(c.label)):(s(""),u([]))))},className:h,children:f.map(a=>(0,b.jsx)("option",{value:a.id,children:a.label},a.id))}),!G&&(0,b.jsx)("span",{className:"text-[11px] text-gray-600",children:"已选官方供应商:Base URL 自动填好,勾选模型 + 填 API Key 即可。"})]}),(0,b.jsxs)("label",{className:"block space-y-1",children:[(0,b.jsxs)("span",{className:"text-xs text-gray-400",children:["名称 ",(0,b.jsx)("span",{className:"text-red-400/70",children:"*"})]}),(0,b.jsx)("input",{autoFocus:!0,value:n,onChange:a=>o(a.target.value),placeholder:"例如 DeepSeek-V4-Pro",className:h})]}),(G||k)&&(0,b.jsxs)("label",{className:"block space-y-1",children:[(0,b.jsxs)("span",{className:"text-xs text-gray-400",children:["类型",k?"(不可修改)":""]}),(0,b.jsx)("select",{value:p,onChange:a=>q(a.target.value),disabled:k,className:`${h} ${k?"opacity-60 cursor-not-allowed":""}`,children:e.map(a=>(0,b.jsx)("option",{value:a.id,disabled:a.disabled,children:a.label},a.id))})]}),(0,b.jsxs)("label",{className:"block space-y-1",children:[(0,b.jsxs)("span",{className:"text-xs text-gray-400",children:["Base URL ",(0,b.jsx)("span",{className:"text-red-400/70",children:"*"}),!G&&!k&&(0,b.jsx)("span",{className:"text-gray-600",children:"(已自动填充)"})]}),(0,b.jsx)("input",{value:r,onChange:a=>s(a.target.value),placeholder:"https://api.anthropic.com",disabled:!G&&!k,className:`${h} ${!G&&!k?"opacity-60 cursor-not-allowed":""}`}),G&&!k&&(0,b.jsxs)("span",{className:"text-[11px] text-gray-600",children:["Anthropic 兼容端点:Claude ",(0,b.jsx)("span",{className:"font-mono",children:"api.anthropic.com"})," · DeepSeek ",(0,b.jsx)("span",{className:"font-mono",children:"api.deepseek.com/anthropic"})," · MiniMax ",(0,b.jsx)("span",{className:"font-mono",children:"api.minimax.chat/anthropic"}),"。"]})]}),G||k?(0,b.jsxs)("label",{className:"block space-y-1",children:[(0,b.jsxs)("span",{className:"text-xs text-gray-400",children:["模型(逗号分隔)",k?"":(0,b.jsx)("span",{className:"text-red-400/70",children:" *"})]}),(0,b.jsx)("input",{value:v,onChange:a=>w(a.target.value),placeholder:"claude-sonnet-4-6, deepseek-chat",className:h})]}):(0,b.jsxs)("div",{className:"block space-y-1",children:[(0,b.jsxs)("span",{className:"text-xs text-gray-400",children:["模型 ",(0,b.jsx)("span",{className:"text-red-400/70",children:"*"}),(0,b.jsx)("span",{className:"text-gray-600",children:"(勾选,可多选)"})]}),(0,b.jsx)("div",{className:"flex flex-wrap gap-2",children:F.models.map(a=>{let c=t.includes(a);return(0,b.jsxs)("button",{type:"button",onClick:()=>{u(b=>b.includes(a)?b.filter(b=>b!==a):[...b,a])},className:`rounded-md border px-2.5 py-1 text-xs font-mono transition-colors ${c?"border-cyan-600 bg-cyan-600/15 text-cyan-300":"border-[#26262b] bg-[#0e0e10] text-gray-400 hover:border-gray-600"}`,children:[c?"✓ ":"",a]},a)})}),(0,b.jsx)("input",{value:v,onChange:a=>w(a.target.value),placeholder:"其他模型(逗号分隔,可留空)",className:`${h} mt-1`})]}),(0,b.jsxs)("label",{className:"block space-y-1",children:[(0,b.jsxs)("span",{className:"text-xs text-gray-400",children:["API Key ",k?(0,b.jsx)("span",{className:"text-gray-600",children:"(不可在此修改)"}):(0,b.jsx)("span",{className:"text-red-400/70",children:"*"})]}),(0,b.jsx)("input",{type:"password",value:x,onChange:a=>y(a.target.value),placeholder:k?"•••• configured(密钥库已存)":F.keyPlaceholder??"sk-…",className:`${h} ${k?"opacity-60 cursor-not-allowed":""}`,autoComplete:"off",disabled:k}),(0,b.jsx)("span",{className:"text-[11px] text-gray-600",children:k?"密钥不可在此改;如需轮换 key 走单独的密钥库流程。":"写入即存入密钥库,UI 不回显 key 值。"})]}),(0,b.jsxs)("label",{className:"flex items-center gap-2 pt-1",children:[(0,b.jsx)("input",{type:"checkbox",checked:z,onChange:a=>A(a.target.checked),className:"accent-cyan-500"}),(0,b.jsx)("span",{className:"text-xs text-gray-300",children:"启用"})]})]}),(0,b.jsxs)("div",{className:"sticky bottom-0 flex items-center justify-end gap-2 border-t border-[#26262b] bg-[#161618] px-4 py-3",children:[(0,b.jsx)("button",{type:"button",onClick:d,className:"rounded-md px-3 py-1.5 text-sm text-gray-400 hover:text-gray-200",children:"取消"}),(0,b.jsx)("button",{type:"button",disabled:!M,onClick:N,className:`rounded-md px-4 py-1.5 text-sm font-medium ${M?"bg-cyan-600 text-white hover:bg-cyan-500":"bg-[#1c1c1f] text-gray-600 cursor-not-allowed"}`,children:"saving"===B?"保存中…":"done"===B?"✓ 已保存":k?"保存":"新增"})]})]})]})}let j={ok:{glyph:"✓",cls:"text-green-400 border-green-500/30 bg-green-500/5",label:"ok",tag:"ok"},auth_fail:{glyph:"⚷",cls:"text-amber-400 border-amber-500/30 bg-amber-500/5",label:"auth",tag:"401"},unreachable:{glyph:"✕",cls:"text-red-400 border-red-500/30 bg-red-500/5",label:"unreachable",tag:"down"},incompatible:{glyph:"—",cls:"text-gray-500 border-gray-600/20",label:"incompatible",tag:"n/a"},error:{glyph:"!",cls:"text-red-400 border-red-500/30 bg-red-500/5",label:"error",tag:"err"}},k=["ok","auth_fail","unreachable","incompatible"];async function l(a,b){let c=0;await Promise.all(Array.from({length:Math.min(6,a.length)},async()=>{for(;c<a.length;){let d=c++;await b(a[d])}}))}let m=a=>"ok"===a||"auth_fail"===a||"unreachable"===a||"incompatible"===a||"pending"===a?a:"timeout"===a||"dns_fail"===a||"conn_fail"===a?"unreachable":"error";function n({providerId:a,models:d}){let[e,f]=(0,c.useState)([]),[g,h]=(0,c.useState)({}),[i,o]=(0,c.useState)(!0),[p,q]=(0,c.useState)(!1),[r,s]=(0,c.useState)(null),t=(0,c.useRef)(!0);(0,c.useEffect)(()=>()=>{t.current=!1},[]);let u=(a,b)=>`${a}@@${b}`,v=(0,c.useCallback)(async b=>{if(!d.length||!b.length||!a)return;q(!0),s(null);let c=d.flatMap(a=>b.map(b=>({m:a,s:b})));h(Object.fromEntries(c.map(({m:a,s:b})=>[u(a,b.daemon_node_id),{status:"pending"}]))),await l(c,async({m:b,s:c})=>{try{let d=await fetch("/api/anet/providers/probe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({provider_id:a,model_name:b,daemon_node_id:c.daemon_node_id})}),e=await d.json().catch(()=>({}));e?.unconfirmed&&t.current&&s(e?.error||"backend not available")}catch{}});let e=Date.now()+24e3,f=new Set(c.map(({m:a,s:b})=>u(a,b.daemon_node_id)));for(;t.current&&Date.now()<e&&(await new Promise(a=>setTimeout(a,1500)),t.current);)try{let b=await fetch(`/api/anet/providers/probe-results?provider_id=${encodeURIComponent(a)}`,{cache:"no-store"}),c=await b.json().catch(()=>({}));if(c?.unconfirmed){t.current&&s(c?.error||"backend not available");break}let d=Array.isArray(c?.results)?c.results:[],e=new Map;for(let a of d){let b=u(String(a.model_name),String(a.daemon_node_id));if(!f.has(b))continue;let c=e.get(b);(!c||(a.probed_at||0)>=(c.probed_at||0))&&e.set(b,a)}if(t.current&&h(a=>{let b={...a};for(let[a,c]of e)b[a]={status:m(String(c.status)),latency_ms:c.latency_ms,reason:c.error_label};return b}),[...f].every(a=>e.get(a)&&"pending"!==e.get(a).status))break}catch{}t.current&&q(!1)},[d,a]);(0,c.useEffect)(()=>{let a=!0;return(async()=>{try{let b=await fetch("/api/anet/providers/probe",{cache:"no-store"}),c=await b.json().catch(()=>({})),d=Array.isArray(c?.servers)?c.servers:[];if(!a)return;f(d),o(!1),d.length&&v(d)}catch{a&&o(!1)}})(),()=>{a=!1}},[v]);let w=d.length*e.length,x=Object.values(g).filter(a=>"ok"===a.status).length;return i?(0,b.jsx)("div",{className:"px-3 py-3 text-[11px] text-gray-500",children:"解析服务器集合…"}):r?(0,b.jsxs)("div",{className:"mx-3 my-2 rounded-md border border-amber-500/30 bg-amber-500/5 px-3 py-2 text-[11px] text-amber-300",children:["连通性 probe 后端尚未就绪:",r,"。矩阵 UI 已就位,待 hub 升级到含 RFC-028 接真 probe。"]}):e.length?(0,b.jsxs)("div",{className:"border-t border-[#1c1c1f] px-3 py-3","data-reachability-matrix":!0,children:[(0,b.jsxs)("div",{className:"mb-2 flex items-center justify-between",children:[(0,b.jsxs)("span",{className:"text-[11px] text-gray-400",children:["可达矩阵 · ",(0,b.jsxs)("span",{className:"tabular-nums text-gray-300",children:[x,"/",w]})," reachable",p&&(0,b.jsx)("span",{className:"ml-2 text-gray-600",children:"probing…"})]}),(0,b.jsx)("button",{type:"button",onClick:()=>v(e),disabled:p,className:"rounded border border-[#26262b] px-2 py-0.5 text-[11px] text-gray-400 hover:text-gray-200 disabled:opacity-50",children:"重新测试"})]}),(0,b.jsx)("div",{className:"overflow-x-auto",children:(0,b.jsxs)("table",{className:"border-collapse text-[11px]",children:[(0,b.jsx)("thead",{children:(0,b.jsxs)("tr",{children:[(0,b.jsx)("th",{className:"px-2 py-1 text-left font-medium text-gray-500",children:"model \\\\ server"}),e.map(a=>(0,b.jsx)("th",{className:"px-2 py-1 text-center font-mono font-normal text-gray-400",title:a.daemon_node_id,children:a.hostname},a.daemon_node_id))]})}),(0,b.jsx)("tbody",{children:d.map(a=>(0,b.jsxs)("tr",{children:[(0,b.jsx)("td",{className:"px-2 py-1 font-mono text-gray-300 whitespace-nowrap",children:a}),e.map(c=>{let d=g[u(a,c.daemon_node_id)];if(!d||"pending"===d.status)return(0,b.jsx)("td",{className:"px-2 py-1 text-center",children:(0,b.jsx)("span",{className:"inline-block h-3 w-3 animate-spin rounded-full border border-gray-600 border-t-transparent","aria-label":"probing"})},c.daemon_node_id);let e=j[d.status]||j.error;return(0,b.jsx)("td",{className:"px-2 py-1 text-center",children:(0,b.jsxs)("span",{className:`inline-flex min-w-[2.75rem] items-center justify-center gap-1 rounded border px-1.5 py-0.5 ${e.cls}`,title:d.reason?`${e.label}: ${d.reason}`:e.label,"data-cell-status":d.status,children:[(0,b.jsx)("span",{"aria-hidden":!0,children:e.glyph}),"ok"===d.status?null!=d.latency_ms&&(0,b.jsxs)("span",{className:"tabular-nums text-[10px] opacity-80",children:[d.latency_ms,"ms"]}):(0,b.jsx)("span",{className:"text-[10px] opacity-80",children:e.tag})]})},c.daemon_node_id)})]},a))})]})}),(0,b.jsxs)("div",{className:"mt-2.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-[10px] text-gray-500",children:[k.map(a=>{let c=j[a];return(0,b.jsxs)("span",{className:"inline-flex items-center gap-1",children:[(0,b.jsx)("span",{className:`inline-flex h-3.5 w-3.5 items-center justify-center rounded border ${c.cls}`,"aria-hidden":!0,children:c.glyph}),c.label]},a)}),(0,b.jsx)("span",{className:"text-gray-600",children:"· 实时探测(模型→服务器真实请求往返)"})]})]}):(0,b.jsx)("div",{className:"px-3 py-3 text-[11px] text-gray-500",children:"未发现可达的 host_supervisor 服务器(probe 目标集为空)。"})}let o={anthropic:"Anthropic 兼容","openai-compatible":"OpenAI 兼容",grok:"Grok",custom:"Custom"};a.s(["default",0,function(){let[a,e]=(0,c.useState)([]),[f,g]=(0,c.useState)(!0),[h,j]=(0,c.useState)(null),[k,l]=(0,c.useState)(!1),[m,p]=(0,c.useState)(null),[q,r]=(0,c.useState)(!1),[s,t]=(0,c.useState)(null),u=(0,c.useCallback)(async()=>{try{let a=await fetch("/api/anet/providers",{cache:"no-store"}),b=await a.json().catch(()=>({}));e(Array.isArray(b?.providers)?b.providers:[]),j(b?.unconfirmed?b?.error||"backend not available":null),l(!!b?.mock)}catch{j("request failed")}finally{g(!1)}},[]);(0,c.useEffect)(()=>{u()},[u]);let v=()=>{p(null),r(!0)},w=async a=>{e(b=>b.map(b=>(b.provider_id||b.name)===(a.provider_id||a.name)?{...b,enabled:!b.enabled}:b)),await fetch("/api/anet/providers",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({provider_id:a.provider_id,enabled:!a.enabled})}).catch(()=>{}),u()};return(0,b.jsxs)("div",{className:"min-h-screen bg-[#0b0b0d] text-gray-100 p-4 sm:p-6",children:[(0,b.jsxs)("div",{className:"flex items-center gap-4 mb-6",children:[(0,b.jsx)("h1",{className:"text-2xl font-bold text-white lg:ml-0 ml-10",children:"Model Providers"}),k&&(0,b.jsx)("span",{className:"text-[10px] px-2 py-0.5 rounded-full border border-amber-500/30 bg-amber-500/5 text-amber-300",children:"preview fixture"}),(0,b.jsxs)("button",{onClick:v,className:"ml-auto inline-flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-md bg-cyan-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-cyan-500",children:[(0,b.jsx)("svg",{width:"15",height:"15",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.2",strokeLinecap:"round","aria-hidden":!0,children:(0,b.jsx)("path",{d:"M12 5v14M5 12h14"})}),"新增供应商"]})]}),(0,b.jsxs)("div",{className:"max-w-3xl space-y-4",children:[(0,b.jsx)("p",{className:"text-sm text-gray-500",children:"Configure model providers and per-server reachability. API keys are stored write-only in the secret vault and never displayed."}),h&&(0,b.jsxs)("div",{className:"rounded-xl border border-amber-500/30 bg-amber-500/5 px-4 py-3 text-xs text-amber-300",children:["供应商后端(RFC-028)尚未在当前 hub 就绪:",h,"。UI 已就位;待 #308 合并 + hub 升级后接真后端。",(0,b.jsx)("span",{className:"text-amber-400/70",children:"(开发预览可设 MOCK_PROVIDERS=1 看填充态。)"})]}),(0,b.jsx)("section",{className:"bg-[#161618] border border-[#26262b] rounded-xl p-5",children:f?(0,b.jsx)("div",{className:"animate-pulse space-y-2",children:[1,2,3].map(a=>(0,b.jsx)("div",{className:"h-14 bg-gray-800/20 rounded"},a))}):0===a.length?(0,b.jsx)(d.EmptyState,{variant:"generic",title:"No model providers",sub:"Add a provider to register its models and test per-server reachability.",cta:{label:"新增供应商",onClick:v}}):(0,b.jsx)("div",{className:"space-y-2",children:a.map(a=>{let c,d=a.provider_id||a.name,e=s===d;return(0,b.jsxs)("div",{className:`rounded-lg border border-[#1c1c1f] bg-[#0e0e10] ${a.enabled?"":"opacity-60"}`,"data-provider":a.name,children:[(0,b.jsxs)("div",{className:"flex flex-col gap-2 px-3 py-2.5 sm:flex-row sm:items-center sm:gap-3",children:[(0,b.jsxs)("div",{className:"flex min-w-0 flex-1 items-center gap-3",children:[(0,b.jsx)("span",{className:`inline-block h-2 w-2 shrink-0 rounded-full ${a.enabled?"bg-green-500":"bg-gray-600"}`,title:a.enabled?"enabled":"disabled"}),(0,b.jsxs)("div",{className:"min-w-0 flex-1",children:[(0,b.jsxs)("div",{className:"flex flex-wrap items-center gap-x-2 gap-y-1",children:[(0,b.jsx)("span",{className:"text-sm font-semibold text-white truncate",children:a.name}),(0,b.jsx)("span",{className:"text-[10px] px-1.5 py-0.5 rounded border border-gray-600/30 text-gray-400 shrink-0",title:a.type,children:o[c=a.type]||c}),a.hasKey?(0,b.jsx)("span",{className:"text-[10px] px-1.5 py-0.5 rounded border border-green-500/20 bg-green-500/5 text-green-400 shrink-0",children:"key set"}):(0,b.jsx)("span",{className:"text-[10px] px-1.5 py-0.5 rounded border border-gray-600/20 text-gray-500 shrink-0",children:"no key"})]}),(0,b.jsxs)("div",{className:"text-[11px] text-gray-500 truncate",children:[function(a){try{return new URL(a).host}catch{return a}}(a.base_url)," · ",a.models?.length||0," model",1===(a.models?.length||0)?"":"s"]})]})]}),(0,b.jsxs)("div",{className:"flex w-full shrink-0 items-center justify-end gap-1.5 border-t border-[#1c1c1f] pt-2 sm:w-auto sm:justify-normal sm:self-auto sm:border-t-0 sm:pt-0",children:[(0,b.jsxs)("button",{type:"button",onClick:()=>t(e?null:d),disabled:!a.enabled,title:a.enabled?"连通性测试(模型 × 服务器 可达矩阵)":"供应商已停用,启用后可测连通性",className:`rounded border px-2 py-1 text-[11px] transition-colors ${!a.enabled?"border-[#26262b] text-gray-600 cursor-not-allowed":e?"border-cyan-500/40 bg-cyan-500/10 text-cyan-300":"border-[#26262b] text-gray-300 hover:border-cyan-500/30 hover:text-cyan-300"}`,children:["Test",e?" ▾":""]}),(0,b.jsx)("button",{type:"button",onClick:()=>{p({provider_id:a.provider_id,name:a.name,type:a.type,base_url:a.base_url,models:a.models||[],enabled:a.enabled??!0,hasKey:a.hasKey}),r(!0)},title:"编辑(名称 / base_url / 模型)",className:"rounded border border-[#26262b] px-2 py-1 text-[11px] text-gray-300 hover:border-cyan-500/30 hover:text-cyan-300",children:"Edit"}),(0,b.jsx)("button",{type:"button",onClick:()=>w(a),title:a.enabled?"停用(软删,可重新启用)":"启用",className:"rounded border border-[#26262b] px-2 py-1 text-[11px] text-gray-400 hover:text-gray-200",children:a.enabled?"Disable":"Enable"})]})]}),e&&((a.models?.length||0)>0?(0,b.jsx)(n,{providerId:a.provider_id,models:a.models||[]}):(0,b.jsx)("div",{className:"border-t border-[#1c1c1f] px-3 py-3 text-[11px] text-gray-500",children:"该供应商未配置模型——先 Edit 添加模型再测可达性。"}))]},d)})})}),(0,b.jsxs)("p",{className:"text-[11px] text-gray-600",children:["按 ",(0,b.jsx)("span",{className:"text-gray-400",children:"Test"})," 展开「模型 × 服务器」连通性矩阵(对每个模型实时探测可达性)。创建节点向导的模型下拉将从此处的 registry 拉取(即将支持)。"]})]}),q&&(0,b.jsx)(i,{initial:m,onClose:()=>r(!1),onSaved:u})]})}],85888)}];
|
|
1
|
+
module.exports=[85888,a=>{"use strict";var b=a.i(64247),c=a.i(54413),d=a.i(19595);let e=[{id:"anthropic",label:"Anthropic 兼容端点(Claude / DeepSeek / MiniMax 等)"},{id:"openai-compatible",label:"OpenAI-compatible(native,即将支持)",disabled:!0},{id:"grok",label:"Grok(native,即将支持)",disabled:!0}],f=[{id:"deepseek",label:"DeepSeek",type:"anthropic",base_url:"https://api.deepseek.com/anthropic",models:["deepseek-v4-pro","deepseek-v4-flash"],keyPlaceholder:"sk-…"},{id:"minimax",label:"MiniMax",type:"anthropic",base_url:"https://api.minimaxi.com/anthropic",models:["MiniMax-M2.7"],keyPlaceholder:"MiniMax API key"},{id:"glm",label:"智谱 GLM",type:"anthropic",base_url:"https://open.bigmodel.cn/api/anthropic",models:["glm-4.6","glm-4.5","glm-4-plus"],keyPlaceholder:"{id}.{secret}"},{id:"claude",label:"Claude(Anthropic 官方)",type:"anthropic",base_url:"https://api.anthropic.com",models:["claude-opus-4-8","claude-sonnet-5","claude-haiku-4-5"],keyPlaceholder:"sk-ant-…"},{id:"custom",label:"自定义(手动填 URL / 模型)",type:"anthropic",base_url:"",models:[]}];function g(a){if(!a)return"custom";let b=f.find(b=>"custom"!==b.id&&b.base_url===a.trim());return b?b.id:"custom"}let h="w-full rounded-md border border-[#26262b] bg-[#0e0e10] px-3 py-2 text-sm text-gray-200 focus:border-cyan-600 focus:outline-none";function i({initial:a,onClose:d,onSaved:j}){let k=!!a?.provider_id,[l,m]=(0,c.useState)(()=>g(a?.base_url)),[n,o]=(0,c.useState)(a?.name??""),[p,q]=(0,c.useState)(a?.type??e[0].id),[r,s]=(0,c.useState)(a?.base_url??""),[t,u]=(0,c.useState)(a?.models??[]),[v,w]=(0,c.useState)(()=>{let b=g(a?.base_url);if("custom"===b)return(a?.models??[]).join(", ");let c=f.find(a=>a.id===b);return(a?.models??[]).filter(a=>!c?.models.includes(a)).join(", ")}),[x,y]=(0,c.useState)(""),[z,A]=(0,c.useState)(a?.enabled??!0),[B,C]=(0,c.useState)("form"),[D,E]=(0,c.useState)(""),F=(0,c.useMemo)(()=>f.find(a=>a.id===l)??f[f.length-1],[l]),G="custom"===l;(0,c.useEffect)(()=>{let a=a=>{"Escape"===a.key&&"saving"!==B&&d()};return window.addEventListener("keydown",a),()=>window.removeEventListener("keydown",a)},[d,B]);let H=(0,c.useMemo)(()=>{let a=v.split(",").map(a=>a.trim()).filter(Boolean);return G?a:Array.from(new Set([...t,...a]))},[G,t,v]),I=n.trim().length>0,J=r.trim().length>0,K=k||x.trim().length>0,L=H.length>0,M=I&&J&&K&&L&&"saving"!==B;async function N(){C("saving"),E("");let b={...a?.provider_id?{provider_id:a.provider_id}:{},name:n.trim(),type:p,base_url:r.trim(),models:H,enabled:z,...x?{apiKey:x}:{}};try{let a=await fetch("/api/anet/providers",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(b)}),c=await a.json().catch(()=>({}));a.ok&&c?.ok?(C("done"),j?.(),setTimeout(d,350)):c?.unconfirmed?(C("unconfirmed"),E(`后端 RFC-028 未就绪:${c?.error||"不可用"}(hub 需 #308 + 升级)`)):(C("error"),E(c?.error?`保存失败:${c.error}`:"保存失败"))}catch(a){C("error"),E(`保存失败:${a instanceof Error?a.message:"网络错误"}`)}}return(0,b.jsxs)(b.Fragment,{children:[(0,b.jsx)("div",{className:"fixed inset-0 bg-black/50 z-40",onClick:()=>"saving"!==B&&d(),"aria-hidden":!0}),(0,b.jsxs)("div",{role:"dialog","aria-label":k?"编辑供应商":"新增供应商",className:"fixed left-1/2 top-1/2 z-50 max-h-[92vh] w-[min(94vw,480px)] -translate-x-1/2 -translate-y-1/2 overflow-y-auto rounded-xl border border-[#26262b] bg-[#161618] shadow-2xl",children:[(0,b.jsxs)("div",{className:"sticky top-0 flex items-center justify-between border-b border-[#26262b] bg-[#161618] px-4 py-3",children:[(0,b.jsx)("div",{className:"text-sm font-semibold text-white",children:k?"编辑供应商":"新增供应商"}),(0,b.jsx)("button",{onClick:d,"aria-label":"关闭",className:"inline-flex h-8 w-8 items-center justify-center rounded-md text-gray-500 hover:bg-white/5 hover:text-white",children:(0,b.jsx)("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round","aria-hidden":!0,children:(0,b.jsx)("path",{d:"M18 6 6 18M6 6l12 12"})})})]}),(0,b.jsxs)("div",{className:"space-y-3 px-4 py-4",children:["unconfirmed"===B||"error"===B?(0,b.jsx)("div",{className:`rounded-md border px-3 py-2 text-xs ${"unconfirmed"===B?"border-amber-500/30 bg-amber-500/5 text-amber-300":"border-red-500/30 bg-red-500/5 text-red-300"}`,children:D}):null,!k&&(0,b.jsxs)("label",{className:"block space-y-1",children:[(0,b.jsxs)("span",{className:"text-xs text-gray-400",children:["供应商 ",(0,b.jsx)("span",{className:"text-red-400/70",children:"*"})]}),(0,b.jsx)("select",{value:l,onChange:a=>{var b;let c;return m(b=a.target.value),void((c=f.find(a=>a.id===b))&&("custom"!==b?(q(c.type),s(c.base_url),u(c.models),(!n.trim()||f.some(a=>a.label===n.trim()))&&o(c.label)):(s(""),u([]))))},className:h,children:f.map(a=>(0,b.jsx)("option",{value:a.id,children:a.label},a.id))}),!G&&(0,b.jsx)("span",{className:"text-[11px] text-gray-600",children:"已选官方供应商:Base URL 自动填好,勾选模型 + 填 API Key 即可。"})]}),(0,b.jsxs)("label",{className:"block space-y-1",children:[(0,b.jsxs)("span",{className:"text-xs text-gray-400",children:["名称 ",(0,b.jsx)("span",{className:"text-red-400/70",children:"*"})]}),(0,b.jsx)("input",{autoFocus:!0,value:n,onChange:a=>o(a.target.value),placeholder:"例如 DeepSeek-V4-Pro",className:h})]}),(G||k)&&(0,b.jsxs)("label",{className:"block space-y-1",children:[(0,b.jsxs)("span",{className:"text-xs text-gray-400",children:["类型",k?"(不可修改)":""]}),(0,b.jsx)("select",{value:p,onChange:a=>q(a.target.value),disabled:k,className:`${h} ${k?"opacity-60 cursor-not-allowed":""}`,children:e.map(a=>(0,b.jsx)("option",{value:a.id,disabled:a.disabled,children:a.label},a.id))})]}),(0,b.jsxs)("label",{className:"block space-y-1",children:[(0,b.jsxs)("span",{className:"text-xs text-gray-400",children:["Base URL ",(0,b.jsx)("span",{className:"text-red-400/70",children:"*"}),!G&&!k&&(0,b.jsx)("span",{className:"text-gray-600",children:"(已自动填充)"})]}),(0,b.jsx)("input",{value:r,onChange:a=>s(a.target.value),placeholder:"https://api.anthropic.com",disabled:!G&&!k,className:`${h} ${!G&&!k?"opacity-60 cursor-not-allowed":""}`}),G&&!k&&(0,b.jsxs)("span",{className:"text-[11px] text-gray-600",children:["Anthropic 兼容端点:Claude ",(0,b.jsx)("span",{className:"font-mono",children:"api.anthropic.com"})," · DeepSeek ",(0,b.jsx)("span",{className:"font-mono",children:"api.deepseek.com/anthropic"})," · MiniMax ",(0,b.jsx)("span",{className:"font-mono",children:"api.minimax.chat/anthropic"}),"。"]})]}),G||k?(0,b.jsxs)("label",{className:"block space-y-1",children:[(0,b.jsxs)("span",{className:"text-xs text-gray-400",children:["模型(逗号分隔)",k?"":(0,b.jsx)("span",{className:"text-red-400/70",children:" *"})]}),(0,b.jsx)("input",{value:v,onChange:a=>w(a.target.value),placeholder:"claude-sonnet-4-6, deepseek-chat",className:h})]}):(0,b.jsxs)("div",{className:"block space-y-1",children:[(0,b.jsxs)("span",{className:"text-xs text-gray-400",children:["模型 ",(0,b.jsx)("span",{className:"text-red-400/70",children:"*"}),(0,b.jsx)("span",{className:"text-gray-600",children:"(勾选,可多选)"})]}),(0,b.jsx)("div",{className:"flex flex-wrap gap-2",children:F.models.map(a=>{let c=t.includes(a);return(0,b.jsxs)("button",{type:"button",onClick:()=>{u(b=>b.includes(a)?b.filter(b=>b!==a):[...b,a])},className:`rounded-md border px-2.5 py-1 text-xs font-mono transition-colors ${c?"border-cyan-600 bg-cyan-600/15 text-cyan-300":"border-[#26262b] bg-[#0e0e10] text-gray-400 hover:border-gray-600"}`,children:[c?"✓ ":"",a]},a)})}),(0,b.jsx)("input",{value:v,onChange:a=>w(a.target.value),placeholder:"其他模型(逗号分隔,可留空)",className:`${h} mt-1`})]}),(0,b.jsxs)("label",{className:"block space-y-1",children:[(0,b.jsxs)("span",{className:"text-xs text-gray-400",children:["API Key ",k?(0,b.jsx)("span",{className:"text-gray-600",children:"(不可在此修改)"}):(0,b.jsx)("span",{className:"text-red-400/70",children:"*"})]}),(0,b.jsx)("input",{type:"password",value:x,onChange:a=>y(a.target.value),placeholder:k?"•••• configured(密钥库已存)":F.keyPlaceholder??"sk-…",className:`${h} ${k?"opacity-60 cursor-not-allowed":""}`,autoComplete:"off",disabled:k}),(0,b.jsx)("span",{className:"text-[11px] text-gray-600",children:k?"密钥不可在此改;如需轮换 key 走单独的密钥库流程。":"写入即存入密钥库,UI 不回显 key 值。"})]}),(0,b.jsxs)("label",{className:"flex items-center gap-2 pt-1",children:[(0,b.jsx)("input",{type:"checkbox",checked:z,onChange:a=>A(a.target.checked),className:"accent-cyan-500"}),(0,b.jsx)("span",{className:"text-xs text-gray-300",children:"启用"})]})]}),(0,b.jsxs)("div",{className:"sticky bottom-0 flex items-center justify-end gap-2 border-t border-[#26262b] bg-[#161618] px-4 py-3",children:[(0,b.jsx)("button",{type:"button",onClick:d,className:"rounded-md px-3 py-1.5 text-sm text-gray-400 hover:text-gray-200",children:"取消"}),(0,b.jsx)("button",{type:"button",disabled:!M,onClick:N,className:`rounded-md px-4 py-1.5 text-sm font-medium ${M?"bg-cyan-600 text-white hover:bg-cyan-500":"bg-[#1c1c1f] text-gray-600 cursor-not-allowed"}`,children:"saving"===B?"保存中…":"done"===B?"✓ 已保存":k?"保存":"新增"})]})]})]})}let j={ok:{glyph:"✓",cls:"text-green-400 border-green-500/30 bg-green-500/5",label:"ok",tag:"ok"},auth_fail:{glyph:"⚷",cls:"text-amber-400 border-amber-500/30 bg-amber-500/5",label:"auth",tag:"401"},unreachable:{glyph:"✕",cls:"text-red-400 border-red-500/30 bg-red-500/5",label:"unreachable",tag:"down"},incompatible:{glyph:"—",cls:"text-gray-500 border-gray-600/20",label:"incompatible",tag:"n/a"},error:{glyph:"!",cls:"text-red-400 border-red-500/30 bg-red-500/5",label:"error",tag:"err"}},k=["ok","auth_fail","unreachable","incompatible"];async function l(a,b){let c=0;await Promise.all(Array.from({length:Math.min(6,a.length)},async()=>{for(;c<a.length;){let d=c++;await b(a[d])}}))}let m=a=>"ok"===a||"auth_fail"===a||"unreachable"===a||"incompatible"===a||"pending"===a?a:"timeout"===a||"dns_fail"===a||"conn_fail"===a?"unreachable":"error";function n({providerId:a,models:d}){let[e,f]=(0,c.useState)([]),[g,h]=(0,c.useState)({}),[i,o]=(0,c.useState)(!0),[p,q]=(0,c.useState)(!1),[r,s]=(0,c.useState)(null),t=(0,c.useRef)(!0);(0,c.useEffect)(()=>()=>{t.current=!1},[]);let u=(a,b)=>`${a}@@${b}`,v=(0,c.useCallback)(async b=>{if(!d.length||!b.length||!a)return;q(!0),s(null);let c=d.flatMap(a=>b.map(b=>({m:a,s:b})));h(Object.fromEntries(c.map(({m:a,s:b})=>[u(a,b.daemon_node_id),{status:"pending"}]))),await l(c,async({m:b,s:c})=>{try{let d=await fetch("/api/anet/providers/probe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({provider_id:a,model_name:b,daemon_node_id:c.daemon_node_id})}),e=await d.json().catch(()=>({}));e?.unconfirmed&&t.current&&s(e?.error||"backend not available")}catch{}});let e=Date.now()+24e3,f=new Set(c.map(({m:a,s:b})=>u(a,b.daemon_node_id)));for(;t.current&&Date.now()<e&&(await new Promise(a=>setTimeout(a,1500)),t.current);)try{let b=await fetch(`/api/anet/providers/probe-results?provider_id=${encodeURIComponent(a)}`,{cache:"no-store"}),c=await b.json().catch(()=>({}));if(c?.unconfirmed){t.current&&s(c?.error||"backend not available");break}let d=Array.isArray(c?.results)?c.results:[],e=new Map;for(let a of d){let b=u(String(a.model_name),String(a.daemon_node_id));if(!f.has(b))continue;let c=e.get(b);(!c||(a.probed_at||0)>=(c.probed_at||0))&&e.set(b,a)}if(t.current&&h(a=>{let b={...a};for(let[a,c]of e)b[a]={status:m(String(c.status)),latency_ms:c.latency_ms,reason:c.error_label};return b}),[...f].every(a=>e.get(a)&&"pending"!==e.get(a).status))break}catch{}t.current&&q(!1)},[d,a]);(0,c.useEffect)(()=>{let a=!0;return(async()=>{try{let b=await fetch("/api/anet/providers/probe",{cache:"no-store"}),c=await b.json().catch(()=>({})),d=Array.isArray(c?.servers)?c.servers:[];if(!a)return;f(d),o(!1),d.length&&v(d)}catch{a&&o(!1)}})(),()=>{a=!1}},[v]);let w=d.length*e.length,x=Object.values(g).filter(a=>"ok"===a.status).length;return i?(0,b.jsx)("div",{className:"px-3 py-3 text-[11px] text-gray-500",children:"解析服务器集合…"}):r?(0,b.jsxs)("div",{className:"mx-3 my-2 rounded-md border border-amber-500/30 bg-amber-500/5 px-3 py-2 text-[11px] text-amber-300",children:["连通性 probe 后端尚未就绪:",r,"。矩阵 UI 已就位,待 hub 升级到含 RFC-028 接真 probe。"]}):e.length?(0,b.jsxs)("div",{className:"border-t border-[#1c1c1f] px-3 py-3","data-reachability-matrix":!0,children:[(0,b.jsxs)("div",{className:"mb-2 flex items-center justify-between",children:[(0,b.jsxs)("span",{className:"text-[11px] text-gray-400",children:["可达矩阵 · ",(0,b.jsxs)("span",{className:"tabular-nums text-gray-300",children:[x,"/",w]})," reachable",p&&(0,b.jsx)("span",{className:"ml-2 text-gray-600",children:"probing…"})]}),(0,b.jsx)("button",{type:"button",onClick:()=>v(e),disabled:p,className:"rounded border border-[#26262b] px-2 py-0.5 text-[11px] text-gray-400 hover:text-gray-200 disabled:opacity-50",children:"重新测试"})]}),(0,b.jsx)("div",{className:"overflow-x-auto",children:(0,b.jsxs)("table",{className:"border-collapse text-[11px]",children:[(0,b.jsx)("thead",{children:(0,b.jsxs)("tr",{children:[(0,b.jsx)("th",{className:"px-2 py-1 text-left font-medium text-gray-500",children:"model \\\\ server"}),e.map(a=>(0,b.jsx)("th",{className:"px-2 py-1 text-center font-mono font-normal text-gray-400",title:a.daemon_node_id,children:a.hostname},a.daemon_node_id))]})}),(0,b.jsx)("tbody",{children:d.map(a=>(0,b.jsxs)("tr",{children:[(0,b.jsx)("td",{className:"px-2 py-1 font-mono text-gray-300 whitespace-nowrap",children:a}),e.map(c=>{let d=g[u(a,c.daemon_node_id)];if(!d||"pending"===d.status)return(0,b.jsx)("td",{className:"px-2 py-1 text-center",children:(0,b.jsx)("span",{className:"inline-block h-3 w-3 animate-spin rounded-full border border-gray-600 border-t-transparent","aria-label":"probing"})},c.daemon_node_id);let e=j[d.status]||j.error;return(0,b.jsx)("td",{className:"px-2 py-1 text-center",children:(0,b.jsxs)("span",{className:`inline-flex min-w-[2.75rem] items-center justify-center gap-1 rounded border px-1.5 py-0.5 ${e.cls}`,title:d.reason?`${e.label}: ${d.reason}`:e.label,"data-cell-status":d.status,children:[(0,b.jsx)("span",{"aria-hidden":!0,children:e.glyph}),"ok"===d.status?null!=d.latency_ms&&(0,b.jsxs)("span",{className:"tabular-nums text-[10px] opacity-80",children:[d.latency_ms,"ms"]}):(0,b.jsx)("span",{className:"text-[10px] opacity-80",children:e.tag})]})},c.daemon_node_id)})]},a))})]})}),(0,b.jsxs)("div",{className:"mt-2.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-[10px] text-gray-500",children:[k.map(a=>{let c=j[a];return(0,b.jsxs)("span",{className:"inline-flex items-center gap-1",children:[(0,b.jsx)("span",{className:`inline-flex h-3.5 w-3.5 items-center justify-center rounded border ${c.cls}`,"aria-hidden":!0,children:c.glyph}),c.label]},a)}),(0,b.jsx)("span",{className:"text-gray-600",children:"· 实时探测(模型→服务器真实请求往返)"})]})]}):(0,b.jsx)("div",{className:"px-3 py-3 text-[11px] text-gray-500",children:"未发现可达的 host_supervisor 服务器(probe 目标集为空)。"})}let o={anthropic:"Anthropic 兼容","openai-compatible":"OpenAI 兼容",grok:"Grok",custom:"Custom"};a.s(["default",0,function(){let[a,e]=(0,c.useState)([]),[f,g]=(0,c.useState)(!0),[h,j]=(0,c.useState)(null),[k,l]=(0,c.useState)(!1),[m,p]=(0,c.useState)(null),[q,r]=(0,c.useState)(!1),[s,t]=(0,c.useState)(null),u=(0,c.useCallback)(async()=>{try{let a=await fetch("/api/anet/providers",{cache:"no-store"}),b=await a.json().catch(()=>({}));e(Array.isArray(b?.providers)?b.providers:[]),j(b?.unconfirmed?b?.error||"backend not available":null),l(!!b?.mock)}catch{j("request failed")}finally{g(!1)}},[]);(0,c.useEffect)(()=>{u()},[u]);let v=()=>{p(null),r(!0)},w=async a=>{e(b=>b.map(b=>(b.provider_id||b.name)===(a.provider_id||a.name)?{...b,enabled:!b.enabled}:b)),await fetch("/api/anet/providers",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({provider_id:a.provider_id,enabled:!a.enabled})}).catch(()=>{}),u()};return(0,b.jsxs)("div",{className:"min-h-screen bg-[#0b0b0d] text-gray-100 p-4 sm:p-6",children:[(0,b.jsxs)("div",{className:"flex items-center gap-4 mb-6",children:[(0,b.jsx)("h1",{className:"text-2xl font-bold text-white lg:ml-0 ml-10",children:"Model Providers"}),k&&(0,b.jsx)("span",{className:"text-[10px] px-2 py-0.5 rounded-full border border-amber-500/30 bg-amber-500/5 text-amber-300",children:"preview fixture"}),(0,b.jsxs)("button",{onClick:v,className:"ml-auto inline-flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-md bg-cyan-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-cyan-500",children:[(0,b.jsx)("svg",{width:"15",height:"15",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.2",strokeLinecap:"round","aria-hidden":!0,children:(0,b.jsx)("path",{d:"M12 5v14M5 12h14"})}),"新增供应商"]})]}),(0,b.jsxs)("div",{className:"max-w-3xl space-y-4",children:[(0,b.jsx)("p",{className:"text-sm text-gray-500",children:"Configure model providers and per-server reachability. API keys are stored write-only in the secret vault and never displayed."}),h&&(0,b.jsxs)("div",{className:"rounded-xl border border-amber-500/30 bg-amber-500/5 px-4 py-3 text-xs text-amber-300",children:["供应商后端(RFC-028)尚未在当前 hub 就绪:",h,"。UI 已就位;待 #308 合并 + hub 升级后接真后端。",(0,b.jsx)("span",{className:"text-amber-400/70",children:"(开发预览可设 MOCK_PROVIDERS=1 看填充态。)"})]}),(0,b.jsx)("section",{className:"bg-[#161618] border border-[#26262b] rounded-xl p-5",children:f?(0,b.jsx)("div",{className:"animate-pulse space-y-2",children:[1,2,3].map(a=>(0,b.jsx)("div",{className:"h-14 bg-gray-800/20 rounded"},a))}):0===a.length?(0,b.jsx)(d.EmptyState,{variant:"generic",title:"No model providers",sub:"Add a provider to register its models and test per-server reachability.",cta:{label:"新增供应商",onClick:v}}):(0,b.jsx)("div",{className:"space-y-2",children:a.map(a=>{let c,d=a.provider_id||a.name,e=s===d;return(0,b.jsxs)("div",{className:`rounded-lg border border-[#1c1c1f] bg-[#0e0e10] ${a.enabled?"":"opacity-60"}`,"data-provider":a.name,children:[(0,b.jsxs)("div",{className:"flex flex-col gap-2 px-3 py-2.5 sm:flex-row sm:items-center sm:gap-3",children:[(0,b.jsxs)("div",{className:"flex min-w-0 flex-1 items-center gap-3",children:[(0,b.jsx)("span",{className:`inline-block h-2 w-2 shrink-0 rounded-full ${a.enabled?"bg-green-500":"bg-gray-600"}`,title:a.enabled?"enabled":"disabled"}),(0,b.jsxs)("div",{className:"min-w-0 flex-1",children:[(0,b.jsxs)("div",{className:"flex flex-wrap items-center gap-x-2 gap-y-1",children:[(0,b.jsx)("span",{className:"text-sm font-semibold text-white truncate",children:a.name}),(0,b.jsx)("span",{className:"text-[10px] px-1.5 py-0.5 rounded border border-gray-600/30 text-gray-400 shrink-0",title:a.type,children:o[c=a.type]||c}),a.hasKey?(0,b.jsx)("span",{className:"text-[10px] px-1.5 py-0.5 rounded border border-green-500/20 bg-green-500/5 text-green-400 shrink-0",children:"key set"}):(0,b.jsx)("span",{className:"text-[10px] px-1.5 py-0.5 rounded border border-gray-600/20 text-gray-500 shrink-0",children:"no key"})]}),(0,b.jsxs)("div",{className:"text-[11px] text-gray-500 truncate",children:[function(a){try{return new URL(a).host}catch{return a}}(a.base_url)," · ",a.models?.length||0," model",1===(a.models?.length||0)?"":"s"]})]})]}),(0,b.jsxs)("div",{className:"flex w-full shrink-0 items-center justify-end gap-1.5 border-t border-[#1c1c1f] pt-2 sm:w-auto sm:justify-normal sm:self-auto sm:border-t-0 sm:pt-0",children:[(0,b.jsxs)("button",{type:"button",onClick:()=>t(e?null:d),disabled:!a.enabled,title:a.enabled?"连通性测试(模型 × 服务器 可达矩阵)":"供应商已停用,启用后可测连通性",className:`rounded border px-2 py-1 text-[11px] transition-colors ${!a.enabled?"border-[#26262b] text-gray-600 cursor-not-allowed":e?"border-cyan-500/40 bg-cyan-500/10 text-cyan-300":"border-[#26262b] text-gray-300 hover:border-cyan-500/30 hover:text-cyan-300"}`,children:["Test",e?" ▾":""]}),(0,b.jsx)("button",{type:"button",onClick:()=>{p({provider_id:a.provider_id,name:a.name,type:a.type,base_url:a.base_url,models:a.models||[],enabled:a.enabled??!0,hasKey:a.hasKey}),r(!0)},title:"编辑(名称 / base_url / 模型)",className:"rounded border border-[#26262b] px-2 py-1 text-[11px] text-gray-300 hover:border-cyan-500/30 hover:text-cyan-300",children:"Edit"}),(0,b.jsx)("button",{type:"button",onClick:()=>w(a),title:a.enabled?"停用(软删,可重新启用)":"启用",className:"rounded border border-[#26262b] px-2 py-1 text-[11px] text-gray-400 hover:text-gray-200",children:a.enabled?"Disable":"Enable"})]})]}),e&&((a.models?.length||0)>0?(0,b.jsx)(n,{providerId:a.provider_id,models:a.models||[]}):(0,b.jsx)("div",{className:"border-t border-[#1c1c1f] px-3 py-3 text-[11px] text-gray-500",children:"该供应商未配置模型——先 Edit 添加模型再测可达性。"}))]},d)})})}),(0,b.jsxs)("p",{className:"text-[11px] text-gray-600",children:["按 ",(0,b.jsx)("span",{className:"text-gray-400",children:"Test"})," 展开「模型 × 服务器」连通性矩阵(对每个模型实时探测可达性)。创建节点向导的模型下拉将从此处的 registry 拉取(即将支持)。"]})]}),q&&(0,b.jsx)(i,{initial:m,onClose:()=>r(!1),onSaved:u})]})}],85888)}];
|
|
2
2
|
|
|
3
3
|
//# sourceMappingURL=agent-network-dashboard_app_providers_page_tsx_0hw2n0v._.js.map
|
package/.next/server/chunks/ssr/agent-network-dashboard_app_providers_page_tsx_0hw2n0v._.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../agent-network-dashboard/app/providers/page.tsx","../../../../../agent-network-dashboard/app/components/ProviderFormModal.tsx","../../../../../agent-network-dashboard/app/components/ReachabilityMatrix.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useState } from 'react';\nimport { EmptyState } from '../components/EmptyState';\nimport { ProviderFormModal, type ProviderDraft } from '../components/ProviderFormModal';\nimport { ReachabilityMatrix } from '../components/ReachabilityMatrix';\n\n/**\n * Model providers (RFC-028 P1 — CRUD). Lists providers from\n * /api/anet/providers, with create/edit (ProviderFormModal), an enabled\n * toggle, soft-delete (enabled:0), and a per-row \"Test\" placeholder for the\n * P2 connectivity matrix. Keys are write-only — the list only shows a\n * key/no-key indicator, never a value.\n *\n * When the hub predates RFC-028 (#308 not merged / hub not upgraded) the API\n * returns `unconfirmed` — we surface an honest banner instead of faking data.\n * Set MOCK_PROVIDERS=1 on the dashboard server to preview the populated UI.\n */\n\ninterface Provider {\n provider_id?: string;\n name: string;\n type: string;\n base_url: string;\n models?: string[];\n enabled?: boolean;\n hasKey?: boolean;\n}\n\nfunction hostOf(url: string) {\n try { return new URL(url).host; } catch { return url; }\n}\n\n// Humanize the raw vendor enum for the row badge so it matches the create\n// modal's wording. A DeepSeek/MiniMax row tagged bare \"anthropic\" reads as\n// wrong — they're Anthropic-*compatible* endpoints, not Anthropic itself.\nconst TYPE_LABELS: Record<string, string> = {\n anthropic: 'Anthropic 兼容',\n 'openai-compatible': 'OpenAI 兼容',\n grok: 'Grok',\n custom: 'Custom',\n};\nconst typeLabel = (t: string) => TYPE_LABELS[t] || t;\n\nexport default function ProvidersPage() {\n const [providers, setProviders] = useState<Provider[]>([]);\n const [loading, setLoading] = useState(true);\n const [unconfirmed, setUnconfirmed] = useState<string | null>(null);\n const [mock, setMock] = useState(false);\n const [editing, setEditing] = useState<ProviderDraft | null>(null);\n const [showForm, setShowForm] = useState(false);\n const [expandedId, setExpandedId] = useState<string | null>(null);\n\n const fetchProviders = useCallback(async () => {\n try {\n const res = await fetch('/api/anet/providers', { cache: 'no-store' });\n const data = await res.json().catch(() => ({}));\n setProviders(Array.isArray(data?.providers) ? data.providers : []);\n setUnconfirmed(data?.unconfirmed ? (data?.error || 'backend not available') : null);\n setMock(!!data?.mock);\n } catch { setUnconfirmed('request failed'); } finally { setLoading(false); }\n }, []);\n\n useEffect(() => { fetchProviders(); }, [fetchProviders]);\n\n const openCreate = () => { setEditing(null); setShowForm(true); };\n // Edit / enable-disable wired to update_provider (#317). vendor + key are\n // immutable on the backend, so the edit form patches name/base_url/models\n // only (changing the key is a separate secret-first flow).\n const openEdit = (p: Provider) => {\n setEditing({ provider_id: p.provider_id, name: p.name, type: p.type, base_url: p.base_url, models: p.models || [], enabled: p.enabled ?? true, hasKey: p.hasKey });\n setShowForm(true);\n };\n const toggleEnabled = async (p: Provider) => {\n setProviders(prev => prev.map(x => (x.provider_id || x.name) === (p.provider_id || p.name) ? { ...x, enabled: !x.enabled } : x)); // optimistic\n await fetch('/api/anet/providers', {\n method: 'POST', headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ provider_id: p.provider_id, enabled: !p.enabled }),\n }).catch(() => {});\n fetchProviders();\n };\n\n return (\n <div className=\"min-h-screen bg-[#0b0b0d] text-gray-100 p-4 sm:p-6\">\n <div className=\"flex items-center gap-4 mb-6\">\n <h1 className=\"text-2xl font-bold text-white lg:ml-0 ml-10\">Model Providers</h1>\n {mock && <span className=\"text-[10px] px-2 py-0.5 rounded-full border border-amber-500/30 bg-amber-500/5 text-amber-300\">preview fixture</span>}\n <button\n onClick={openCreate}\n className=\"ml-auto inline-flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-md bg-cyan-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-cyan-500\"\n >\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.2\" strokeLinecap=\"round\" aria-hidden><path d=\"M12 5v14M5 12h14\" /></svg>\n 新增供应商\n </button>\n </div>\n\n <div className=\"max-w-3xl space-y-4\">\n <p className=\"text-sm text-gray-500\">\n Configure model providers and per-server reachability. API keys are stored write-only in the secret vault and never displayed.\n </p>\n\n {unconfirmed && (\n <div className=\"rounded-xl border border-amber-500/30 bg-amber-500/5 px-4 py-3 text-xs text-amber-300\">\n 供应商后端(RFC-028)尚未在当前 hub 就绪:{unconfirmed}。UI 已就位;待 #308 合并 + hub 升级后接真后端。\n <span className=\"text-amber-400/70\">(开发预览可设 MOCK_PROVIDERS=1 看填充态。)</span>\n </div>\n )}\n\n <section className=\"bg-[#161618] border border-[#26262b] rounded-xl p-5\">\n {loading ? (\n <div className=\"animate-pulse space-y-2\">{[1, 2, 3].map(i => <div key={i} className=\"h-14 bg-gray-800/20 rounded\" />)}</div>\n ) : providers.length === 0 ? (\n <EmptyState\n variant=\"generic\"\n title=\"No model providers\"\n sub=\"Add a provider to register its models and test per-server reachability.\"\n cta={{ label: '新增供应商', onClick: openCreate }}\n />\n ) : (\n <div className=\"space-y-2\">\n {providers.map(p => {\n const rid = p.provider_id || p.name;\n const isOpen = expandedId === rid;\n return (\n <div key={rid} className={`rounded-lg border border-[#1c1c1f] bg-[#0e0e10] ${p.enabled ? '' : 'opacity-60'}`} data-provider={p.name}>\n <div className=\"flex flex-col gap-2 px-3 py-2.5 sm:flex-row sm:items-center sm:gap-3\">\n <div className=\"flex min-w-0 flex-1 items-center gap-3\">\n <span className={`inline-block h-2 w-2 shrink-0 rounded-full ${p.enabled ? 'bg-green-500' : 'bg-gray-600'}`} title={p.enabled ? 'enabled' : 'disabled'} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1\">\n <span className=\"text-sm font-semibold text-white truncate\">{p.name}</span>\n <span className=\"text-[10px] px-1.5 py-0.5 rounded border border-gray-600/30 text-gray-400 shrink-0\" title={p.type}>{typeLabel(p.type)}</span>\n {p.hasKey\n ? <span className=\"text-[10px] px-1.5 py-0.5 rounded border border-green-500/20 bg-green-500/5 text-green-400 shrink-0\">key set</span>\n : <span className=\"text-[10px] px-1.5 py-0.5 rounded border border-gray-600/20 text-gray-500 shrink-0\">no key</span>}\n </div>\n <div className=\"text-[11px] text-gray-500 truncate\">\n {hostOf(p.base_url)} · {(p.models?.length || 0)} model{(p.models?.length || 0) === 1 ? '' : 's'}\n </div>\n </div>\n </div>\n <div className=\"flex w-full shrink-0 items-center justify-end gap-1.5 border-t border-[#1c1c1f] pt-2 sm:w-auto sm:justify-normal sm:self-auto sm:border-t-0 sm:pt-0\">\n <button\n type=\"button\"\n onClick={() => setExpandedId(isOpen ? null : rid)}\n disabled={!p.enabled}\n title={p.enabled ? '连通性测试(模型 × 服务器 可达矩阵)' : '供应商已停用,启用后可测连通性'}\n className={`rounded border px-2 py-1 text-[11px] transition-colors ${!p.enabled ? 'border-[#26262b] text-gray-600 cursor-not-allowed' : isOpen ? 'border-cyan-500/40 bg-cyan-500/10 text-cyan-300' : 'border-[#26262b] text-gray-300 hover:border-cyan-500/30 hover:text-cyan-300'}`}\n >Test{isOpen ? ' ▾' : ''}</button>\n <button type=\"button\" onClick={() => openEdit(p)} title=\"编辑(名称 / base_url / 模型)\" className=\"rounded border border-[#26262b] px-2 py-1 text-[11px] text-gray-300 hover:border-cyan-500/30 hover:text-cyan-300\">Edit</button>\n <button type=\"button\" onClick={() => toggleEnabled(p)} title={p.enabled ? '停用(软删,可重新启用)' : '启用'} className=\"rounded border border-[#26262b] px-2 py-1 text-[11px] text-gray-400 hover:text-gray-200\">\n {p.enabled ? 'Disable' : 'Enable'}\n </button>\n </div>\n </div>\n {isOpen && (\n (p.models?.length || 0) > 0\n ? <ReachabilityMatrix providerId={p.provider_id} models={p.models || []} />\n : <div className=\"border-t border-[#1c1c1f] px-3 py-3 text-[11px] text-gray-500\">该供应商未配置模型——先 Edit 添加模型再测可达性。</div>\n )}\n </div>\n );\n })}\n </div>\n )}\n </section>\n\n <p className=\"text-[11px] text-gray-600\">\n 按 <span className=\"text-gray-400\">Test</span> 展开「模型 × 服务器」连通性矩阵(对每个模型实时探测可达性)。创建节点向导的模型下拉将从此处的 registry 拉取(即将支持)。\n </p>\n </div>\n\n {showForm && (\n <ProviderFormModal\n initial={editing}\n onClose={() => setShowForm(false)}\n onSaved={fetchProviders}\n />\n )}\n </div>\n );\n}\n","'use client';\n\nimport { useEffect, useMemo, useState } from 'react';\n\n/**\n * Create/edit a model provider (RFC-028 P1 + #393 preset catalog). Single-step\n * modal mirroring the CreateNodeWizard shell (escape-to-close, SVG ✕, cyan\n * primary). The API key field is WRITE-ONLY: on edit of a provider that already\n * has a key it shows a \"•••• configured\" placeholder and an empty value means\n * \"keep existing key\"; the stored value is never fetched or displayed.\n *\n * #393 (Vincent UX): official providers (DeepSeek / MiniMax / GLM / Claude) are\n * offered as PRESETS — picking one auto-fills the Anthropic-compatible base_url\n * and turns the model field into a checkbox list of that provider's known\n * models, so the operator only supplies an API key. \"自定义\" falls back to the\n * manual base_url + comma-separated model entry for any other endpoint.\n */\n\nexport interface ProviderDraft {\n provider_id?: string;\n name: string;\n type: string;\n base_url: string;\n models: string[];\n enabled: boolean;\n hasKey?: boolean;\n}\n\n// Backend (#308) currently supports only the `anthropic` vendor — which means\n// any Anthropic-COMPATIBLE endpoint (Claude api.anthropic.com, DeepSeek\n// api.deepseek.com/anthropic, MiniMax api.minimax.chat/anthropic, …) via a\n// custom base_url, not just Anthropic the company. Native OpenAI/Grok vendors\n// aren't wired yet → shown disabled so users can't pick an erroring option.\nconst TYPES: { id: string; label: string; disabled?: boolean }[] = [\n { id: 'anthropic', label: 'Anthropic 兼容端点(Claude / DeepSeek / MiniMax 等)' },\n { id: 'openai-compatible', label: 'OpenAI-compatible(native,即将支持)', disabled: true },\n { id: 'grok', label: 'Grok(native,即将支持)', disabled: true },\n];\n\n// #393 preset catalog. Each base_url is an Anthropic-compatible endpoint\n// (verified 401-reachable). `models` are sensible defaults the operator can\n// tick on/off; extras can still be typed. Keep `custom` last as the escape\n// hatch to the manual URL/model flow.\ninterface ProviderPreset {\n id: string;\n label: string;\n type: string;\n base_url: string;\n models: string[];\n keyPlaceholder?: string;\n}\nconst PRESETS: ProviderPreset[] = [\n { id: 'deepseek', label: 'DeepSeek', type: 'anthropic', base_url: 'https://api.deepseek.com/anthropic', models: ['deepseek-chat', 'deepseek-reasoner'], keyPlaceholder: 'sk-…' },\n { id: 'minimax', label: 'MiniMax', type: 'anthropic', base_url: 'https://api.minimax.chat/anthropic', models: ['MiniMax-M3', 'MiniMax-M2'], keyPlaceholder: 'MiniMax API key' },\n { id: 'glm', label: '智谱 GLM', type: 'anthropic', base_url: 'https://open.bigmodel.cn/api/anthropic', models: ['glm-4.6', 'glm-4.5', 'glm-4-plus'], keyPlaceholder: '{id}.{secret}' },\n { id: 'claude', label: 'Claude(Anthropic 官方)', type: 'anthropic', base_url: 'https://api.anthropic.com', models: ['claude-sonnet-4-6', 'claude-opus-4-1', 'claude-haiku-4-5'], keyPlaceholder: 'sk-ant-…' },\n { id: 'custom', label: '自定义(手动填 URL / 模型)', type: 'anthropic', base_url: '', models: [] },\n];\n\nfunction detectPreset(baseUrl?: string): string {\n if (!baseUrl) return 'custom';\n const hit = PRESETS.find(p => p.id !== 'custom' && p.base_url === baseUrl.trim());\n return hit ? hit.id : 'custom';\n}\n\ntype Phase = 'form' | 'saving' | 'done' | 'unconfirmed' | 'error';\n\nconst inputCls =\n 'w-full rounded-md border border-[#26262b] bg-[#0e0e10] px-3 py-2 text-sm text-gray-200 focus:border-cyan-600 focus:outline-none';\n\nfunction parseModels(text: string): string[] {\n return text.split(',').map(m => m.trim()).filter(Boolean);\n}\n\nexport function ProviderFormModal({\n initial,\n onClose,\n onSaved,\n}: {\n initial?: ProviderDraft | null;\n onClose: () => void;\n onSaved?: () => void;\n}) {\n const editing = !!initial?.provider_id;\n const [presetId, setPresetId] = useState(() => detectPreset(initial?.base_url));\n const [name, setName] = useState(initial?.name ?? '');\n const [type, setType] = useState(initial?.type ?? TYPES[0].id);\n const [baseUrl, setBaseUrl] = useState(initial?.base_url ?? '');\n // Checked preset models (only used while a non-custom preset is selected).\n const [checkedModels, setCheckedModels] = useState<string[]>(initial?.models ?? []);\n // Free-text models — for `custom` it's the whole list, for a preset it's extras.\n const [modelsText, setModelsText] = useState(() => {\n const p = detectPreset(initial?.base_url);\n if (p === 'custom') return (initial?.models ?? []).join(', ');\n // extras = initial models not in the preset's default list\n const preset = PRESETS.find(x => x.id === p);\n const extras = (initial?.models ?? []).filter(m => !preset?.models.includes(m));\n return extras.join(', ');\n });\n const [apiKey, setApiKey] = useState('');\n const [enabled, setEnabled] = useState(initial?.enabled ?? true);\n const [phase, setPhase] = useState<Phase>('form');\n const [msg, setMsg] = useState('');\n\n const preset = useMemo(() => PRESETS.find(p => p.id === presetId) ?? PRESETS[PRESETS.length - 1], [presetId]);\n const isCustom = presetId === 'custom';\n\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape' && phase !== 'saving') onClose(); };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [onClose, phase]);\n\n function selectPreset(id: string) {\n setPresetId(id);\n const p = PRESETS.find(x => x.id === id);\n if (!p) return;\n if (id !== 'custom') {\n setType(p.type);\n setBaseUrl(p.base_url);\n // default-check all preset models; keep any prior extras in the text box\n setCheckedModels(p.models);\n if (!name.trim() || PRESETS.some(x => x.label === name.trim())) setName(p.label);\n } else {\n setBaseUrl('');\n setCheckedModels([]);\n }\n }\n\n function toggleModel(m: string) {\n setCheckedModels(prev => (prev.includes(m) ? prev.filter(x => x !== m) : [...prev, m]));\n }\n\n const finalModels = useMemo(() => {\n const extras = parseModels(modelsText);\n if (isCustom) return extras;\n return Array.from(new Set([...checkedModels, ...extras]));\n }, [isCustom, checkedModels, modelsText]);\n\n const nameValid = name.trim().length > 0;\n const urlValid = baseUrl.trim().length > 0;\n // Secret-first (RFC-028): a provider can't be created without a vault key,\n // so the API key is required on CREATE. On edit the key + vendor are\n // immutable (update_provider patches name/base_url/models only).\n const keyValid = editing || apiKey.trim().length > 0;\n const modelsValid = finalModels.length > 0;\n const canSave = nameValid && urlValid && keyValid && modelsValid && phase !== 'saving';\n\n async function handleSave() {\n setPhase('saving'); setMsg('');\n const payload = {\n ...(initial?.provider_id ? { provider_id: initial.provider_id } : {}),\n name: name.trim(), type, base_url: baseUrl.trim(), models: finalModels, enabled,\n ...(apiKey ? { apiKey } : {}),\n };\n try {\n const r = await fetch('/api/anet/providers', {\n method: 'POST', headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n });\n const data = await r.json().catch(() => ({}));\n if (r.ok && data?.ok) {\n setPhase('done'); onSaved?.();\n // close shortly after success so the list refresh is visible\n setTimeout(onClose, 350);\n } else if (data?.unconfirmed) {\n setPhase('unconfirmed');\n setMsg(`后端 RFC-028 未就绪:${data?.error || '不可用'}(hub 需 #308 + 升级)`);\n } else {\n setPhase('error');\n setMsg(data?.error ? `保存失败:${data.error}` : '保存失败');\n }\n } catch (e) {\n setPhase('error');\n setMsg(`保存失败:${e instanceof Error ? e.message : '网络错误'}`);\n }\n }\n\n return (\n <>\n <div className=\"fixed inset-0 bg-black/50 z-40\" onClick={() => phase !== 'saving' && onClose()} aria-hidden />\n <div\n role=\"dialog\"\n aria-label={editing ? '编辑供应商' : '新增供应商'}\n className=\"fixed left-1/2 top-1/2 z-50 max-h-[92vh] w-[min(94vw,480px)] -translate-x-1/2 -translate-y-1/2 overflow-y-auto rounded-xl border border-[#26262b] bg-[#161618] shadow-2xl\"\n >\n <div className=\"sticky top-0 flex items-center justify-between border-b border-[#26262b] bg-[#161618] px-4 py-3\">\n <div className=\"text-sm font-semibold text-white\">{editing ? '编辑供应商' : '新增供应商'}</div>\n <button onClick={onClose} aria-label=\"关闭\" className=\"inline-flex h-8 w-8 items-center justify-center rounded-md text-gray-500 hover:bg-white/5 hover:text-white\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <div className=\"space-y-3 px-4 py-4\">\n {phase === 'unconfirmed' || phase === 'error' ? (\n <div className={`rounded-md border px-3 py-2 text-xs ${phase === 'unconfirmed' ? 'border-amber-500/30 bg-amber-500/5 text-amber-300' : 'border-red-500/30 bg-red-500/5 text-red-300'}`}>\n {msg}\n </div>\n ) : null}\n\n {/* #393 preset provider picker (hidden while editing — base_url/vendor are immutable) */}\n {!editing && (\n <label className=\"block space-y-1\">\n <span className=\"text-xs text-gray-400\">供应商 <span className=\"text-red-400/70\">*</span></span>\n <select value={presetId} onChange={e => selectPreset(e.target.value)} className={inputCls}>\n {PRESETS.map(p => <option key={p.id} value={p.id}>{p.label}</option>)}\n </select>\n {!isCustom && <span className=\"text-[11px] text-gray-600\">已选官方供应商:Base URL 自动填好,勾选模型 + 填 API Key 即可。</span>}\n </label>\n )}\n\n <label className=\"block space-y-1\">\n <span className=\"text-xs text-gray-400\">名称 <span className=\"text-red-400/70\">*</span></span>\n <input autoFocus value={name} onChange={e => setName(e.target.value)} placeholder=\"例如 DeepSeek-V4-Pro\" className={inputCls} />\n </label>\n\n {/* 类型 only surfaces in custom / edit — presets imply anthropic. */}\n {(isCustom || editing) && (\n <label className=\"block space-y-1\">\n <span className=\"text-xs text-gray-400\">类型{editing ? '(不可修改)' : ''}</span>\n <select value={type} onChange={e => setType(e.target.value)} disabled={editing} className={`${inputCls} ${editing ? 'opacity-60 cursor-not-allowed' : ''}`}>\n {TYPES.map(t => <option key={t.id} value={t.id} disabled={t.disabled}>{t.label}</option>)}\n </select>\n </label>\n )}\n\n <label className=\"block space-y-1\">\n <span className=\"text-xs text-gray-400\">Base URL <span className=\"text-red-400/70\">*</span>{!isCustom && !editing && <span className=\"text-gray-600\">(已自动填充)</span>}</span>\n <input\n value={baseUrl}\n onChange={e => setBaseUrl(e.target.value)}\n placeholder=\"https://api.anthropic.com\"\n disabled={!isCustom && !editing}\n className={`${inputCls} ${(!isCustom && !editing) ? 'opacity-60 cursor-not-allowed' : ''}`}\n />\n {isCustom && !editing && <span className=\"text-[11px] text-gray-600\">Anthropic 兼容端点:Claude <span className=\"font-mono\">api.anthropic.com</span> · DeepSeek <span className=\"font-mono\">api.deepseek.com/anthropic</span> · MiniMax <span className=\"font-mono\">api.minimax.chat/anthropic</span>。</span>}\n </label>\n\n {/* Models: preset → checkbox list + extras; custom → comma text. */}\n {!isCustom && !editing ? (\n <div className=\"block space-y-1\">\n <span className=\"text-xs text-gray-400\">模型 <span className=\"text-red-400/70\">*</span><span className=\"text-gray-600\">(勾选,可多选)</span></span>\n <div className=\"flex flex-wrap gap-2\">\n {preset.models.map(m => {\n const on = checkedModels.includes(m);\n return (\n <button\n type=\"button\"\n key={m}\n onClick={() => toggleModel(m)}\n className={`rounded-md border px-2.5 py-1 text-xs font-mono transition-colors ${on ? 'border-cyan-600 bg-cyan-600/15 text-cyan-300' : 'border-[#26262b] bg-[#0e0e10] text-gray-400 hover:border-gray-600'}`}\n >\n {on ? '✓ ' : ''}{m}\n </button>\n );\n })}\n </div>\n <input value={modelsText} onChange={e => setModelsText(e.target.value)} placeholder=\"其他模型(逗号分隔,可留空)\" className={`${inputCls} mt-1`} />\n </div>\n ) : (\n <label className=\"block space-y-1\">\n <span className=\"text-xs text-gray-400\">模型(逗号分隔){editing ? '' : <span className=\"text-red-400/70\"> *</span>}</span>\n <input value={modelsText} onChange={e => setModelsText(e.target.value)} placeholder=\"claude-sonnet-4-6, deepseek-chat\" className={inputCls} />\n </label>\n )}\n\n <label className=\"block space-y-1\">\n <span className=\"text-xs text-gray-400\">API Key {editing ? <span className=\"text-gray-600\">(不可在此修改)</span> : <span className=\"text-red-400/70\">*</span>}</span>\n <input\n type=\"password\"\n value={apiKey}\n onChange={e => setApiKey(e.target.value)}\n placeholder={editing ? '•••• configured(密钥库已存)' : (preset.keyPlaceholder ?? 'sk-…')}\n className={`${inputCls} ${editing ? 'opacity-60 cursor-not-allowed' : ''}`}\n autoComplete=\"off\"\n disabled={editing}\n />\n <span className=\"text-[11px] text-gray-600\">{editing ? '密钥不可在此改;如需轮换 key 走单独的密钥库流程。' : '写入即存入密钥库,UI 不回显 key 值。'}</span>\n </label>\n\n <label className=\"flex items-center gap-2 pt-1\">\n <input type=\"checkbox\" checked={enabled} onChange={e => setEnabled(e.target.checked)} className=\"accent-cyan-500\" />\n <span className=\"text-xs text-gray-300\">启用</span>\n </label>\n </div>\n\n <div className=\"sticky bottom-0 flex items-center justify-end gap-2 border-t border-[#26262b] bg-[#161618] px-4 py-3\">\n <button type=\"button\" onClick={onClose} className=\"rounded-md px-3 py-1.5 text-sm text-gray-400 hover:text-gray-200\">取消</button>\n <button\n type=\"button\"\n disabled={!canSave}\n onClick={handleSave}\n className={`rounded-md px-4 py-1.5 text-sm font-medium ${canSave ? 'bg-cyan-600 text-white hover:bg-cyan-500' : 'bg-[#1c1c1f] text-gray-600 cursor-not-allowed'}`}\n >\n {phase === 'saving' ? '保存中…' : phase === 'done' ? '✓ 已保存' : editing ? '保存' : '新增'}\n </button>\n </div>\n </div>\n </>\n );\n}\n","'use client';\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\n\n/**\n * Model × server reachability matrix (RFC-028 P2, aligned to real #308 schema).\n * probe_provider_model is ASYNC: we DISPATCH a probe per (model × server) with\n * bounded concurrency (~6), then POLL get_probe_results until every cell\n * resolves (or times out). Cells: ok (green ✓ + latency) · auth_fail (amber) ·\n * unreachable (red ✕) · incompatible (gray —) · pending (spinner).\n * Rendered as a real <table> for screen-reader columns.\n */\n\ninterface Server { daemon_node_id: string; hostname: string }\ntype CellStatus = 'pending' | 'ok' | 'auth_fail' | 'unreachable' | 'incompatible' | 'error';\ninterface Cell { status: CellStatus; latency_ms?: number; reason?: string }\n\n// `tag` is the short inline label shown in the cell (visible without hover, so\n// a screenshot/glance reads cleanly); `label` is the longer a11y/tooltip term.\nconst CELL_UI: Record<Exclude<CellStatus, 'pending'>, { glyph: string; cls: string; label: string; tag: string }> = {\n ok: { glyph: '✓', cls: 'text-green-400 border-green-500/30 bg-green-500/5', label: 'ok', tag: 'ok' },\n auth_fail: { glyph: '⚷', cls: 'text-amber-400 border-amber-500/30 bg-amber-500/5', label: 'auth', tag: '401' },\n unreachable: { glyph: '✕', cls: 'text-red-400 border-red-500/30 bg-red-500/5', label: 'unreachable', tag: 'down' },\n incompatible: { glyph: '—', cls: 'text-gray-500 border-gray-600/20', label: 'incompatible', tag: 'n/a' },\n error: { glyph: '!', cls: 'text-red-400 border-red-500/30 bg-red-500/5', label: 'error', tag: 'err' },\n};\n\n// Legend entries shown under the matrix so cell glyphs are self-explanatory.\nconst LEGEND: Array<Exclude<CellStatus, 'pending'>> = ['ok', 'auth_fail', 'unreachable', 'incompatible'];\n\nconst CONCURRENCY = 6;\nconst POLL_MS = 1500;\nconst POLL_TIMEOUT_MS = 24000;\n\nasync function pool<T>(items: T[], worker: (t: T) => Promise<void>) {\n let i = 0;\n await Promise.all(Array.from({ length: Math.min(CONCURRENCY, items.length) }, async () => {\n while (i < items.length) { const idx = i++; await worker(items[idx]); }\n }));\n}\n\nconst norm = (s: string): CellStatus => {\n if (s === 'ok' || s === 'auth_fail' || s === 'unreachable' || s === 'incompatible' || s === 'pending') return s;\n if (s === 'timeout' || s === 'dns_fail' || s === 'conn_fail') return 'unreachable';\n return 'error';\n};\n\nexport function ReachabilityMatrix({ providerId, models }: { providerId?: string; models: string[] }) {\n const [servers, setServers] = useState<Server[]>([]);\n const [cells, setCells] = useState<Record<string, Cell>>({});\n const [loading, setLoading] = useState(true);\n const [running, setRunning] = useState(false);\n const [unconfirmed, setUnconfirmed] = useState<string | null>(null);\n const aliveRef = useRef(true);\n useEffect(() => () => { aliveRef.current = false; }, []);\n\n const key = (m: string, s: string) => `${m}@@${s}`;\n\n const run = useCallback(async (srv: Server[]) => {\n if (!models.length || !srv.length || !providerId) return;\n setRunning(true); setUnconfirmed(null);\n const pairs = models.flatMap(m => srv.map(s => ({ m, s })));\n setCells(Object.fromEntries(pairs.map(({ m, s }) => [key(m, s.daemon_node_id), { status: 'pending' as CellStatus }])));\n\n // 1) dispatch all probes (async — backend mints pending rows + doorbells daemons)\n await pool(pairs, async ({ m, s }) => {\n try {\n const r = await fetch('/api/anet/providers/probe', {\n method: 'POST', headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ provider_id: providerId, model_name: m, daemon_node_id: s.daemon_node_id }),\n });\n const d = await r.json().catch(() => ({}));\n if (d?.unconfirmed && aliveRef.current) setUnconfirmed(d?.error || 'backend not available');\n } catch { /* dispatch best-effort; poll surfaces the outcome */ }\n });\n\n // 2) poll get_probe_results until every cell resolves or we time out\n const deadline = Date.now() + POLL_TIMEOUT_MS;\n const want = new Set(pairs.map(({ m, s }) => key(m, s.daemon_node_id)));\n while (aliveRef.current && Date.now() < deadline) {\n await new Promise(res => setTimeout(res, POLL_MS));\n if (!aliveRef.current) break;\n try {\n const r = await fetch(`/api/anet/providers/probe-results?provider_id=${encodeURIComponent(providerId)}`, { cache: 'no-store' });\n const d = await r.json().catch(() => ({}));\n if (d?.unconfirmed) { if (aliveRef.current) setUnconfirmed(d?.error || 'backend not available'); break; }\n const rows = Array.isArray(d?.results) ? d.results : [];\n // latest row per (model,daemon)\n const latest = new Map<string, { status: string; latency_ms?: number; error_label?: string; probed_at?: number }>();\n for (const row of rows) {\n const k = key(String(row.model_name), String(row.daemon_node_id));\n if (!want.has(k)) continue;\n const prev = latest.get(k);\n if (!prev || (row.probed_at || 0) >= (prev.probed_at || 0)) latest.set(k, row);\n }\n if (aliveRef.current) {\n setCells(prev => {\n const next = { ...prev };\n for (const [k, row] of latest) next[k] = { status: norm(String(row.status)), latency_ms: row.latency_ms, reason: row.error_label };\n return next;\n });\n }\n // done when no cell is still pending\n const allResolved = [...want].every(k => latest.get(k) && latest.get(k)!.status !== 'pending');\n if (allResolved) break;\n } catch { /* transient — keep polling */ }\n }\n if (aliveRef.current) setRunning(false);\n }, [models, providerId]);\n\n useEffect(() => {\n let alive = true;\n (async () => {\n try {\n const r = await fetch('/api/anet/providers/probe', { cache: 'no-store' });\n const d = await r.json().catch(() => ({}));\n const srv: Server[] = Array.isArray(d?.servers) ? d.servers : [];\n if (!alive) return;\n setServers(srv); setLoading(false);\n if (srv.length) run(srv);\n } catch { if (alive) setLoading(false); }\n })();\n return () => { alive = false; };\n }, [run]);\n\n const total = models.length * servers.length;\n const okCount = Object.values(cells).filter(c => c.status === 'ok').length;\n\n if (loading) return <div className=\"px-3 py-3 text-[11px] text-gray-500\">解析服务器集合…</div>;\n if (unconfirmed) {\n return <div className=\"mx-3 my-2 rounded-md border border-amber-500/30 bg-amber-500/5 px-3 py-2 text-[11px] text-amber-300\">\n 连通性 probe 后端尚未就绪:{unconfirmed}。矩阵 UI 已就位,待 hub 升级到含 RFC-028 接真 probe。\n </div>;\n }\n if (!servers.length) return <div className=\"px-3 py-3 text-[11px] text-gray-500\">未发现可达的 host_supervisor 服务器(probe 目标集为空)。</div>;\n\n return (\n <div className=\"border-t border-[#1c1c1f] px-3 py-3\" data-reachability-matrix>\n <div className=\"mb-2 flex items-center justify-between\">\n <span className=\"text-[11px] text-gray-400\">\n 可达矩阵 · <span className=\"tabular-nums text-gray-300\">{okCount}/{total}</span> reachable\n {running && <span className=\"ml-2 text-gray-600\">probing…</span>}\n </span>\n <button type=\"button\" onClick={() => run(servers)} disabled={running}\n className=\"rounded border border-[#26262b] px-2 py-0.5 text-[11px] text-gray-400 hover:text-gray-200 disabled:opacity-50\">重新测试</button>\n </div>\n <div className=\"overflow-x-auto\">\n <table className=\"border-collapse text-[11px]\">\n <thead>\n <tr>\n <th className=\"px-2 py-1 text-left font-medium text-gray-500\">model \\\\ server</th>\n {servers.map(s => <th key={s.daemon_node_id} className=\"px-2 py-1 text-center font-mono font-normal text-gray-400\" title={s.daemon_node_id}>{s.hostname}</th>)}\n </tr>\n </thead>\n <tbody>\n {models.map(m => (\n <tr key={m}>\n <td className=\"px-2 py-1 font-mono text-gray-300 whitespace-nowrap\">{m}</td>\n {servers.map(s => {\n const c = cells[key(m, s.daemon_node_id)];\n if (!c || c.status === 'pending') return <td key={s.daemon_node_id} className=\"px-2 py-1 text-center\">\n <span className=\"inline-block h-3 w-3 animate-spin rounded-full border border-gray-600 border-t-transparent\" aria-label=\"probing\" />\n </td>;\n const ui = CELL_UI[c.status as Exclude<CellStatus, 'pending'>] || CELL_UI.error;\n return (\n <td key={s.daemon_node_id} className=\"px-2 py-1 text-center\">\n <span className={`inline-flex min-w-[2.75rem] items-center justify-center gap-1 rounded border px-1.5 py-0.5 ${ui.cls}`}\n title={c.reason ? `${ui.label}: ${c.reason}` : ui.label} data-cell-status={c.status}>\n <span aria-hidden>{ui.glyph}</span>\n {c.status === 'ok'\n ? (c.latency_ms != null && <span className=\"tabular-nums text-[10px] opacity-80\">{c.latency_ms}ms</span>)\n : <span className=\"text-[10px] opacity-80\">{ui.tag}</span>}\n </span>\n </td>\n );\n })}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n <div className=\"mt-2.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-[10px] text-gray-500\">\n {LEGEND.map(s => {\n const ui = CELL_UI[s];\n return (\n <span key={s} className=\"inline-flex items-center gap-1\">\n <span className={`inline-flex h-3.5 w-3.5 items-center justify-center rounded border ${ui.cls}`} aria-hidden>{ui.glyph}</span>\n {ui.label}\n </span>\n );\n })}\n <span className=\"text-gray-600\">· 实时探测(模型→服务器真实请求往返)</span>\n </div>\n </div>\n );\n}\n"],"names":["TYPES","id","label","disabled","PRESETS","type","base_url","models","keyPlaceholder","detectPreset","baseUrl","hit","find","p","trim","inputCls","parseModels","text","split","map","m","filter","Boolean","ProviderFormModal","initial","onClose","onSaved","editing","provider_id","presetId","setPresetId","name","setName","setType","setBaseUrl","checkedModels","setCheckedModels","modelsText","setModelsText","join","preset","x","extras","includes","apiKey","setApiKey","enabled","setEnabled","phase","setPhase","msg","setMsg","length","isCustom","onKey","e","key","window","addEventListener","removeEventListener","selectPreset","some","toggleModel","prev","finalModels","Array","from","Set","nameValid","urlValid","keyValid","modelsValid","canSave","handleSave","payload","r","fetch","method","headers","body","JSON","stringify","data","json","catch","ok","setTimeout","unconfirmed","error","Error","message","className","onClick","role","width","height","viewBox","fill","stroke","strokeWidth","strokeLinecap","d","value","onChange","target","autoFocus","placeholder","t","on","autoComplete","checked"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../agent-network-dashboard/app/providers/page.tsx","../../../../../agent-network-dashboard/app/components/ProviderFormModal.tsx","../../../../../agent-network-dashboard/app/components/ReachabilityMatrix.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useState } from 'react';\nimport { EmptyState } from '../components/EmptyState';\nimport { ProviderFormModal, type ProviderDraft } from '../components/ProviderFormModal';\nimport { ReachabilityMatrix } from '../components/ReachabilityMatrix';\n\n/**\n * Model providers (RFC-028 P1 — CRUD). Lists providers from\n * /api/anet/providers, with create/edit (ProviderFormModal), an enabled\n * toggle, soft-delete (enabled:0), and a per-row \"Test\" placeholder for the\n * P2 connectivity matrix. Keys are write-only — the list only shows a\n * key/no-key indicator, never a value.\n *\n * When the hub predates RFC-028 (#308 not merged / hub not upgraded) the API\n * returns `unconfirmed` — we surface an honest banner instead of faking data.\n * Set MOCK_PROVIDERS=1 on the dashboard server to preview the populated UI.\n */\n\ninterface Provider {\n provider_id?: string;\n name: string;\n type: string;\n base_url: string;\n models?: string[];\n enabled?: boolean;\n hasKey?: boolean;\n}\n\nfunction hostOf(url: string) {\n try { return new URL(url).host; } catch { return url; }\n}\n\n// Humanize the raw vendor enum for the row badge so it matches the create\n// modal's wording. A DeepSeek/MiniMax row tagged bare \"anthropic\" reads as\n// wrong — they're Anthropic-*compatible* endpoints, not Anthropic itself.\nconst TYPE_LABELS: Record<string, string> = {\n anthropic: 'Anthropic 兼容',\n 'openai-compatible': 'OpenAI 兼容',\n grok: 'Grok',\n custom: 'Custom',\n};\nconst typeLabel = (t: string) => TYPE_LABELS[t] || t;\n\nexport default function ProvidersPage() {\n const [providers, setProviders] = useState<Provider[]>([]);\n const [loading, setLoading] = useState(true);\n const [unconfirmed, setUnconfirmed] = useState<string | null>(null);\n const [mock, setMock] = useState(false);\n const [editing, setEditing] = useState<ProviderDraft | null>(null);\n const [showForm, setShowForm] = useState(false);\n const [expandedId, setExpandedId] = useState<string | null>(null);\n\n const fetchProviders = useCallback(async () => {\n try {\n const res = await fetch('/api/anet/providers', { cache: 'no-store' });\n const data = await res.json().catch(() => ({}));\n setProviders(Array.isArray(data?.providers) ? data.providers : []);\n setUnconfirmed(data?.unconfirmed ? (data?.error || 'backend not available') : null);\n setMock(!!data?.mock);\n } catch { setUnconfirmed('request failed'); } finally { setLoading(false); }\n }, []);\n\n useEffect(() => { fetchProviders(); }, [fetchProviders]);\n\n const openCreate = () => { setEditing(null); setShowForm(true); };\n // Edit / enable-disable wired to update_provider (#317). vendor + key are\n // immutable on the backend, so the edit form patches name/base_url/models\n // only (changing the key is a separate secret-first flow).\n const openEdit = (p: Provider) => {\n setEditing({ provider_id: p.provider_id, name: p.name, type: p.type, base_url: p.base_url, models: p.models || [], enabled: p.enabled ?? true, hasKey: p.hasKey });\n setShowForm(true);\n };\n const toggleEnabled = async (p: Provider) => {\n setProviders(prev => prev.map(x => (x.provider_id || x.name) === (p.provider_id || p.name) ? { ...x, enabled: !x.enabled } : x)); // optimistic\n await fetch('/api/anet/providers', {\n method: 'POST', headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ provider_id: p.provider_id, enabled: !p.enabled }),\n }).catch(() => {});\n fetchProviders();\n };\n\n return (\n <div className=\"min-h-screen bg-[#0b0b0d] text-gray-100 p-4 sm:p-6\">\n <div className=\"flex items-center gap-4 mb-6\">\n <h1 className=\"text-2xl font-bold text-white lg:ml-0 ml-10\">Model Providers</h1>\n {mock && <span className=\"text-[10px] px-2 py-0.5 rounded-full border border-amber-500/30 bg-amber-500/5 text-amber-300\">preview fixture</span>}\n <button\n onClick={openCreate}\n className=\"ml-auto inline-flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-md bg-cyan-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-cyan-500\"\n >\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.2\" strokeLinecap=\"round\" aria-hidden><path d=\"M12 5v14M5 12h14\" /></svg>\n 新增供应商\n </button>\n </div>\n\n <div className=\"max-w-3xl space-y-4\">\n <p className=\"text-sm text-gray-500\">\n Configure model providers and per-server reachability. API keys are stored write-only in the secret vault and never displayed.\n </p>\n\n {unconfirmed && (\n <div className=\"rounded-xl border border-amber-500/30 bg-amber-500/5 px-4 py-3 text-xs text-amber-300\">\n 供应商后端(RFC-028)尚未在当前 hub 就绪:{unconfirmed}。UI 已就位;待 #308 合并 + hub 升级后接真后端。\n <span className=\"text-amber-400/70\">(开发预览可设 MOCK_PROVIDERS=1 看填充态。)</span>\n </div>\n )}\n\n <section className=\"bg-[#161618] border border-[#26262b] rounded-xl p-5\">\n {loading ? (\n <div className=\"animate-pulse space-y-2\">{[1, 2, 3].map(i => <div key={i} className=\"h-14 bg-gray-800/20 rounded\" />)}</div>\n ) : providers.length === 0 ? (\n <EmptyState\n variant=\"generic\"\n title=\"No model providers\"\n sub=\"Add a provider to register its models and test per-server reachability.\"\n cta={{ label: '新增供应商', onClick: openCreate }}\n />\n ) : (\n <div className=\"space-y-2\">\n {providers.map(p => {\n const rid = p.provider_id || p.name;\n const isOpen = expandedId === rid;\n return (\n <div key={rid} className={`rounded-lg border border-[#1c1c1f] bg-[#0e0e10] ${p.enabled ? '' : 'opacity-60'}`} data-provider={p.name}>\n <div className=\"flex flex-col gap-2 px-3 py-2.5 sm:flex-row sm:items-center sm:gap-3\">\n <div className=\"flex min-w-0 flex-1 items-center gap-3\">\n <span className={`inline-block h-2 w-2 shrink-0 rounded-full ${p.enabled ? 'bg-green-500' : 'bg-gray-600'}`} title={p.enabled ? 'enabled' : 'disabled'} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1\">\n <span className=\"text-sm font-semibold text-white truncate\">{p.name}</span>\n <span className=\"text-[10px] px-1.5 py-0.5 rounded border border-gray-600/30 text-gray-400 shrink-0\" title={p.type}>{typeLabel(p.type)}</span>\n {p.hasKey\n ? <span className=\"text-[10px] px-1.5 py-0.5 rounded border border-green-500/20 bg-green-500/5 text-green-400 shrink-0\">key set</span>\n : <span className=\"text-[10px] px-1.5 py-0.5 rounded border border-gray-600/20 text-gray-500 shrink-0\">no key</span>}\n </div>\n <div className=\"text-[11px] text-gray-500 truncate\">\n {hostOf(p.base_url)} · {(p.models?.length || 0)} model{(p.models?.length || 0) === 1 ? '' : 's'}\n </div>\n </div>\n </div>\n <div className=\"flex w-full shrink-0 items-center justify-end gap-1.5 border-t border-[#1c1c1f] pt-2 sm:w-auto sm:justify-normal sm:self-auto sm:border-t-0 sm:pt-0\">\n <button\n type=\"button\"\n onClick={() => setExpandedId(isOpen ? null : rid)}\n disabled={!p.enabled}\n title={p.enabled ? '连通性测试(模型 × 服务器 可达矩阵)' : '供应商已停用,启用后可测连通性'}\n className={`rounded border px-2 py-1 text-[11px] transition-colors ${!p.enabled ? 'border-[#26262b] text-gray-600 cursor-not-allowed' : isOpen ? 'border-cyan-500/40 bg-cyan-500/10 text-cyan-300' : 'border-[#26262b] text-gray-300 hover:border-cyan-500/30 hover:text-cyan-300'}`}\n >Test{isOpen ? ' ▾' : ''}</button>\n <button type=\"button\" onClick={() => openEdit(p)} title=\"编辑(名称 / base_url / 模型)\" className=\"rounded border border-[#26262b] px-2 py-1 text-[11px] text-gray-300 hover:border-cyan-500/30 hover:text-cyan-300\">Edit</button>\n <button type=\"button\" onClick={() => toggleEnabled(p)} title={p.enabled ? '停用(软删,可重新启用)' : '启用'} className=\"rounded border border-[#26262b] px-2 py-1 text-[11px] text-gray-400 hover:text-gray-200\">\n {p.enabled ? 'Disable' : 'Enable'}\n </button>\n </div>\n </div>\n {isOpen && (\n (p.models?.length || 0) > 0\n ? <ReachabilityMatrix providerId={p.provider_id} models={p.models || []} />\n : <div className=\"border-t border-[#1c1c1f] px-3 py-3 text-[11px] text-gray-500\">该供应商未配置模型——先 Edit 添加模型再测可达性。</div>\n )}\n </div>\n );\n })}\n </div>\n )}\n </section>\n\n <p className=\"text-[11px] text-gray-600\">\n 按 <span className=\"text-gray-400\">Test</span> 展开「模型 × 服务器」连通性矩阵(对每个模型实时探测可达性)。创建节点向导的模型下拉将从此处的 registry 拉取(即将支持)。\n </p>\n </div>\n\n {showForm && (\n <ProviderFormModal\n initial={editing}\n onClose={() => setShowForm(false)}\n onSaved={fetchProviders}\n />\n )}\n </div>\n );\n}\n","'use client';\n\nimport { useEffect, useMemo, useState } from 'react';\n\n/**\n * Create/edit a model provider (RFC-028 P1 + #393 preset catalog). Single-step\n * modal mirroring the CreateNodeWizard shell (escape-to-close, SVG ✕, cyan\n * primary). The API key field is WRITE-ONLY: on edit of a provider that already\n * has a key it shows a \"•••• configured\" placeholder and an empty value means\n * \"keep existing key\"; the stored value is never fetched or displayed.\n *\n * #393 (Vincent UX): official providers (DeepSeek / MiniMax / GLM / Claude) are\n * offered as PRESETS — picking one auto-fills the Anthropic-compatible base_url\n * and turns the model field into a checkbox list of that provider's known\n * models, so the operator only supplies an API key. \"自定义\" falls back to the\n * manual base_url + comma-separated model entry for any other endpoint.\n */\n\nexport interface ProviderDraft {\n provider_id?: string;\n name: string;\n type: string;\n base_url: string;\n models: string[];\n enabled: boolean;\n hasKey?: boolean;\n}\n\n// Backend (#308) currently supports only the `anthropic` vendor — which means\n// any Anthropic-COMPATIBLE endpoint (Claude api.anthropic.com, DeepSeek\n// api.deepseek.com/anthropic, MiniMax api.minimax.chat/anthropic, …) via a\n// custom base_url, not just Anthropic the company. Native OpenAI/Grok vendors\n// aren't wired yet → shown disabled so users can't pick an erroring option.\nconst TYPES: { id: string; label: string; disabled?: boolean }[] = [\n { id: 'anthropic', label: 'Anthropic 兼容端点(Claude / DeepSeek / MiniMax 等)' },\n { id: 'openai-compatible', label: 'OpenAI-compatible(native,即将支持)', disabled: true },\n { id: 'grok', label: 'Grok(native,即将支持)', disabled: true },\n];\n\n// #393 preset catalog. Each base_url is an Anthropic-compatible endpoint\n// (verified 401-reachable). `models` are sensible defaults the operator can\n// tick on/off; extras can still be typed. Keep `custom` last as the escape\n// hatch to the manual URL/model flow.\ninterface ProviderPreset {\n id: string;\n label: string;\n type: string;\n base_url: string;\n models: string[];\n keyPlaceholder?: string;\n}\nconst PRESETS: ProviderPreset[] = [\n { id: 'deepseek', label: 'DeepSeek', type: 'anthropic', base_url: 'https://api.deepseek.com/anthropic', models: ['deepseek-v4-pro', 'deepseek-v4-flash'], keyPlaceholder: 'sk-…' },\n { id: 'minimax', label: 'MiniMax', type: 'anthropic', base_url: 'https://api.minimaxi.com/anthropic', models: ['MiniMax-M2.7'], keyPlaceholder: 'MiniMax API key' },\n { id: 'glm', label: '智谱 GLM', type: 'anthropic', base_url: 'https://open.bigmodel.cn/api/anthropic', models: ['glm-4.6', 'glm-4.5', 'glm-4-plus'], keyPlaceholder: '{id}.{secret}' },\n { id: 'claude', label: 'Claude(Anthropic 官方)', type: 'anthropic', base_url: 'https://api.anthropic.com', models: ['claude-opus-4-8', 'claude-sonnet-5', 'claude-haiku-4-5'], keyPlaceholder: 'sk-ant-…' },\n { id: 'custom', label: '自定义(手动填 URL / 模型)', type: 'anthropic', base_url: '', models: [] },\n];\n\nfunction detectPreset(baseUrl?: string): string {\n if (!baseUrl) return 'custom';\n const hit = PRESETS.find(p => p.id !== 'custom' && p.base_url === baseUrl.trim());\n return hit ? hit.id : 'custom';\n}\n\ntype Phase = 'form' | 'saving' | 'done' | 'unconfirmed' | 'error';\n\nconst inputCls =\n 'w-full rounded-md border border-[#26262b] bg-[#0e0e10] px-3 py-2 text-sm text-gray-200 focus:border-cyan-600 focus:outline-none';\n\nfunction parseModels(text: string): string[] {\n return text.split(',').map(m => m.trim()).filter(Boolean);\n}\n\nexport function ProviderFormModal({\n initial,\n onClose,\n onSaved,\n}: {\n initial?: ProviderDraft | null;\n onClose: () => void;\n onSaved?: () => void;\n}) {\n const editing = !!initial?.provider_id;\n const [presetId, setPresetId] = useState(() => detectPreset(initial?.base_url));\n const [name, setName] = useState(initial?.name ?? '');\n const [type, setType] = useState(initial?.type ?? TYPES[0].id);\n const [baseUrl, setBaseUrl] = useState(initial?.base_url ?? '');\n // Checked preset models (only used while a non-custom preset is selected).\n const [checkedModels, setCheckedModels] = useState<string[]>(initial?.models ?? []);\n // Free-text models — for `custom` it's the whole list, for a preset it's extras.\n const [modelsText, setModelsText] = useState(() => {\n const p = detectPreset(initial?.base_url);\n if (p === 'custom') return (initial?.models ?? []).join(', ');\n // extras = initial models not in the preset's default list\n const preset = PRESETS.find(x => x.id === p);\n const extras = (initial?.models ?? []).filter(m => !preset?.models.includes(m));\n return extras.join(', ');\n });\n const [apiKey, setApiKey] = useState('');\n const [enabled, setEnabled] = useState(initial?.enabled ?? true);\n const [phase, setPhase] = useState<Phase>('form');\n const [msg, setMsg] = useState('');\n\n const preset = useMemo(() => PRESETS.find(p => p.id === presetId) ?? PRESETS[PRESETS.length - 1], [presetId]);\n const isCustom = presetId === 'custom';\n\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape' && phase !== 'saving') onClose(); };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [onClose, phase]);\n\n function selectPreset(id: string) {\n setPresetId(id);\n const p = PRESETS.find(x => x.id === id);\n if (!p) return;\n if (id !== 'custom') {\n setType(p.type);\n setBaseUrl(p.base_url);\n // default-check all preset models; keep any prior extras in the text box\n setCheckedModels(p.models);\n if (!name.trim() || PRESETS.some(x => x.label === name.trim())) setName(p.label);\n } else {\n setBaseUrl('');\n setCheckedModels([]);\n }\n }\n\n function toggleModel(m: string) {\n setCheckedModels(prev => (prev.includes(m) ? prev.filter(x => x !== m) : [...prev, m]));\n }\n\n const finalModels = useMemo(() => {\n const extras = parseModels(modelsText);\n if (isCustom) return extras;\n return Array.from(new Set([...checkedModels, ...extras]));\n }, [isCustom, checkedModels, modelsText]);\n\n const nameValid = name.trim().length > 0;\n const urlValid = baseUrl.trim().length > 0;\n // Secret-first (RFC-028): a provider can't be created without a vault key,\n // so the API key is required on CREATE. On edit the key + vendor are\n // immutable (update_provider patches name/base_url/models only).\n const keyValid = editing || apiKey.trim().length > 0;\n const modelsValid = finalModels.length > 0;\n const canSave = nameValid && urlValid && keyValid && modelsValid && phase !== 'saving';\n\n async function handleSave() {\n setPhase('saving'); setMsg('');\n const payload = {\n ...(initial?.provider_id ? { provider_id: initial.provider_id } : {}),\n name: name.trim(), type, base_url: baseUrl.trim(), models: finalModels, enabled,\n ...(apiKey ? { apiKey } : {}),\n };\n try {\n const r = await fetch('/api/anet/providers', {\n method: 'POST', headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n });\n const data = await r.json().catch(() => ({}));\n if (r.ok && data?.ok) {\n setPhase('done'); onSaved?.();\n // close shortly after success so the list refresh is visible\n setTimeout(onClose, 350);\n } else if (data?.unconfirmed) {\n setPhase('unconfirmed');\n setMsg(`后端 RFC-028 未就绪:${data?.error || '不可用'}(hub 需 #308 + 升级)`);\n } else {\n setPhase('error');\n setMsg(data?.error ? `保存失败:${data.error}` : '保存失败');\n }\n } catch (e) {\n setPhase('error');\n setMsg(`保存失败:${e instanceof Error ? e.message : '网络错误'}`);\n }\n }\n\n return (\n <>\n <div className=\"fixed inset-0 bg-black/50 z-40\" onClick={() => phase !== 'saving' && onClose()} aria-hidden />\n <div\n role=\"dialog\"\n aria-label={editing ? '编辑供应商' : '新增供应商'}\n className=\"fixed left-1/2 top-1/2 z-50 max-h-[92vh] w-[min(94vw,480px)] -translate-x-1/2 -translate-y-1/2 overflow-y-auto rounded-xl border border-[#26262b] bg-[#161618] shadow-2xl\"\n >\n <div className=\"sticky top-0 flex items-center justify-between border-b border-[#26262b] bg-[#161618] px-4 py-3\">\n <div className=\"text-sm font-semibold text-white\">{editing ? '编辑供应商' : '新增供应商'}</div>\n <button onClick={onClose} aria-label=\"关闭\" className=\"inline-flex h-8 w-8 items-center justify-center rounded-md text-gray-500 hover:bg-white/5 hover:text-white\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <div className=\"space-y-3 px-4 py-4\">\n {phase === 'unconfirmed' || phase === 'error' ? (\n <div className={`rounded-md border px-3 py-2 text-xs ${phase === 'unconfirmed' ? 'border-amber-500/30 bg-amber-500/5 text-amber-300' : 'border-red-500/30 bg-red-500/5 text-red-300'}`}>\n {msg}\n </div>\n ) : null}\n\n {/* #393 preset provider picker (hidden while editing — base_url/vendor are immutable) */}\n {!editing && (\n <label className=\"block space-y-1\">\n <span className=\"text-xs text-gray-400\">供应商 <span className=\"text-red-400/70\">*</span></span>\n <select value={presetId} onChange={e => selectPreset(e.target.value)} className={inputCls}>\n {PRESETS.map(p => <option key={p.id} value={p.id}>{p.label}</option>)}\n </select>\n {!isCustom && <span className=\"text-[11px] text-gray-600\">已选官方供应商:Base URL 自动填好,勾选模型 + 填 API Key 即可。</span>}\n </label>\n )}\n\n <label className=\"block space-y-1\">\n <span className=\"text-xs text-gray-400\">名称 <span className=\"text-red-400/70\">*</span></span>\n <input autoFocus value={name} onChange={e => setName(e.target.value)} placeholder=\"例如 DeepSeek-V4-Pro\" className={inputCls} />\n </label>\n\n {/* 类型 only surfaces in custom / edit — presets imply anthropic. */}\n {(isCustom || editing) && (\n <label className=\"block space-y-1\">\n <span className=\"text-xs text-gray-400\">类型{editing ? '(不可修改)' : ''}</span>\n <select value={type} onChange={e => setType(e.target.value)} disabled={editing} className={`${inputCls} ${editing ? 'opacity-60 cursor-not-allowed' : ''}`}>\n {TYPES.map(t => <option key={t.id} value={t.id} disabled={t.disabled}>{t.label}</option>)}\n </select>\n </label>\n )}\n\n <label className=\"block space-y-1\">\n <span className=\"text-xs text-gray-400\">Base URL <span className=\"text-red-400/70\">*</span>{!isCustom && !editing && <span className=\"text-gray-600\">(已自动填充)</span>}</span>\n <input\n value={baseUrl}\n onChange={e => setBaseUrl(e.target.value)}\n placeholder=\"https://api.anthropic.com\"\n disabled={!isCustom && !editing}\n className={`${inputCls} ${(!isCustom && !editing) ? 'opacity-60 cursor-not-allowed' : ''}`}\n />\n {isCustom && !editing && <span className=\"text-[11px] text-gray-600\">Anthropic 兼容端点:Claude <span className=\"font-mono\">api.anthropic.com</span> · DeepSeek <span className=\"font-mono\">api.deepseek.com/anthropic</span> · MiniMax <span className=\"font-mono\">api.minimax.chat/anthropic</span>。</span>}\n </label>\n\n {/* Models: preset → checkbox list + extras; custom → comma text. */}\n {!isCustom && !editing ? (\n <div className=\"block space-y-1\">\n <span className=\"text-xs text-gray-400\">模型 <span className=\"text-red-400/70\">*</span><span className=\"text-gray-600\">(勾选,可多选)</span></span>\n <div className=\"flex flex-wrap gap-2\">\n {preset.models.map(m => {\n const on = checkedModels.includes(m);\n return (\n <button\n type=\"button\"\n key={m}\n onClick={() => toggleModel(m)}\n className={`rounded-md border px-2.5 py-1 text-xs font-mono transition-colors ${on ? 'border-cyan-600 bg-cyan-600/15 text-cyan-300' : 'border-[#26262b] bg-[#0e0e10] text-gray-400 hover:border-gray-600'}`}\n >\n {on ? '✓ ' : ''}{m}\n </button>\n );\n })}\n </div>\n <input value={modelsText} onChange={e => setModelsText(e.target.value)} placeholder=\"其他模型(逗号分隔,可留空)\" className={`${inputCls} mt-1`} />\n </div>\n ) : (\n <label className=\"block space-y-1\">\n <span className=\"text-xs text-gray-400\">模型(逗号分隔){editing ? '' : <span className=\"text-red-400/70\"> *</span>}</span>\n <input value={modelsText} onChange={e => setModelsText(e.target.value)} placeholder=\"claude-sonnet-4-6, deepseek-chat\" className={inputCls} />\n </label>\n )}\n\n <label className=\"block space-y-1\">\n <span className=\"text-xs text-gray-400\">API Key {editing ? <span className=\"text-gray-600\">(不可在此修改)</span> : <span className=\"text-red-400/70\">*</span>}</span>\n <input\n type=\"password\"\n value={apiKey}\n onChange={e => setApiKey(e.target.value)}\n placeholder={editing ? '•••• configured(密钥库已存)' : (preset.keyPlaceholder ?? 'sk-…')}\n className={`${inputCls} ${editing ? 'opacity-60 cursor-not-allowed' : ''}`}\n autoComplete=\"off\"\n disabled={editing}\n />\n <span className=\"text-[11px] text-gray-600\">{editing ? '密钥不可在此改;如需轮换 key 走单独的密钥库流程。' : '写入即存入密钥库,UI 不回显 key 值。'}</span>\n </label>\n\n <label className=\"flex items-center gap-2 pt-1\">\n <input type=\"checkbox\" checked={enabled} onChange={e => setEnabled(e.target.checked)} className=\"accent-cyan-500\" />\n <span className=\"text-xs text-gray-300\">启用</span>\n </label>\n </div>\n\n <div className=\"sticky bottom-0 flex items-center justify-end gap-2 border-t border-[#26262b] bg-[#161618] px-4 py-3\">\n <button type=\"button\" onClick={onClose} className=\"rounded-md px-3 py-1.5 text-sm text-gray-400 hover:text-gray-200\">取消</button>\n <button\n type=\"button\"\n disabled={!canSave}\n onClick={handleSave}\n className={`rounded-md px-4 py-1.5 text-sm font-medium ${canSave ? 'bg-cyan-600 text-white hover:bg-cyan-500' : 'bg-[#1c1c1f] text-gray-600 cursor-not-allowed'}`}\n >\n {phase === 'saving' ? '保存中…' : phase === 'done' ? '✓ 已保存' : editing ? '保存' : '新增'}\n </button>\n </div>\n </div>\n </>\n );\n}\n","'use client';\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\n\n/**\n * Model × server reachability matrix (RFC-028 P2, aligned to real #308 schema).\n * probe_provider_model is ASYNC: we DISPATCH a probe per (model × server) with\n * bounded concurrency (~6), then POLL get_probe_results until every cell\n * resolves (or times out). Cells: ok (green ✓ + latency) · auth_fail (amber) ·\n * unreachable (red ✕) · incompatible (gray —) · pending (spinner).\n * Rendered as a real <table> for screen-reader columns.\n */\n\ninterface Server { daemon_node_id: string; hostname: string }\ntype CellStatus = 'pending' | 'ok' | 'auth_fail' | 'unreachable' | 'incompatible' | 'error';\ninterface Cell { status: CellStatus; latency_ms?: number; reason?: string }\n\n// `tag` is the short inline label shown in the cell (visible without hover, so\n// a screenshot/glance reads cleanly); `label` is the longer a11y/tooltip term.\nconst CELL_UI: Record<Exclude<CellStatus, 'pending'>, { glyph: string; cls: string; label: string; tag: string }> = {\n ok: { glyph: '✓', cls: 'text-green-400 border-green-500/30 bg-green-500/5', label: 'ok', tag: 'ok' },\n auth_fail: { glyph: '⚷', cls: 'text-amber-400 border-amber-500/30 bg-amber-500/5', label: 'auth', tag: '401' },\n unreachable: { glyph: '✕', cls: 'text-red-400 border-red-500/30 bg-red-500/5', label: 'unreachable', tag: 'down' },\n incompatible: { glyph: '—', cls: 'text-gray-500 border-gray-600/20', label: 'incompatible', tag: 'n/a' },\n error: { glyph: '!', cls: 'text-red-400 border-red-500/30 bg-red-500/5', label: 'error', tag: 'err' },\n};\n\n// Legend entries shown under the matrix so cell glyphs are self-explanatory.\nconst LEGEND: Array<Exclude<CellStatus, 'pending'>> = ['ok', 'auth_fail', 'unreachable', 'incompatible'];\n\nconst CONCURRENCY = 6;\nconst POLL_MS = 1500;\nconst POLL_TIMEOUT_MS = 24000;\n\nasync function pool<T>(items: T[], worker: (t: T) => Promise<void>) {\n let i = 0;\n await Promise.all(Array.from({ length: Math.min(CONCURRENCY, items.length) }, async () => {\n while (i < items.length) { const idx = i++; await worker(items[idx]); }\n }));\n}\n\nconst norm = (s: string): CellStatus => {\n if (s === 'ok' || s === 'auth_fail' || s === 'unreachable' || s === 'incompatible' || s === 'pending') return s;\n if (s === 'timeout' || s === 'dns_fail' || s === 'conn_fail') return 'unreachable';\n return 'error';\n};\n\nexport function ReachabilityMatrix({ providerId, models }: { providerId?: string; models: string[] }) {\n const [servers, setServers] = useState<Server[]>([]);\n const [cells, setCells] = useState<Record<string, Cell>>({});\n const [loading, setLoading] = useState(true);\n const [running, setRunning] = useState(false);\n const [unconfirmed, setUnconfirmed] = useState<string | null>(null);\n const aliveRef = useRef(true);\n useEffect(() => () => { aliveRef.current = false; }, []);\n\n const key = (m: string, s: string) => `${m}@@${s}`;\n\n const run = useCallback(async (srv: Server[]) => {\n if (!models.length || !srv.length || !providerId) return;\n setRunning(true); setUnconfirmed(null);\n const pairs = models.flatMap(m => srv.map(s => ({ m, s })));\n setCells(Object.fromEntries(pairs.map(({ m, s }) => [key(m, s.daemon_node_id), { status: 'pending' as CellStatus }])));\n\n // 1) dispatch all probes (async — backend mints pending rows + doorbells daemons)\n await pool(pairs, async ({ m, s }) => {\n try {\n const r = await fetch('/api/anet/providers/probe', {\n method: 'POST', headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ provider_id: providerId, model_name: m, daemon_node_id: s.daemon_node_id }),\n });\n const d = await r.json().catch(() => ({}));\n if (d?.unconfirmed && aliveRef.current) setUnconfirmed(d?.error || 'backend not available');\n } catch { /* dispatch best-effort; poll surfaces the outcome */ }\n });\n\n // 2) poll get_probe_results until every cell resolves or we time out\n const deadline = Date.now() + POLL_TIMEOUT_MS;\n const want = new Set(pairs.map(({ m, s }) => key(m, s.daemon_node_id)));\n while (aliveRef.current && Date.now() < deadline) {\n await new Promise(res => setTimeout(res, POLL_MS));\n if (!aliveRef.current) break;\n try {\n const r = await fetch(`/api/anet/providers/probe-results?provider_id=${encodeURIComponent(providerId)}`, { cache: 'no-store' });\n const d = await r.json().catch(() => ({}));\n if (d?.unconfirmed) { if (aliveRef.current) setUnconfirmed(d?.error || 'backend not available'); break; }\n const rows = Array.isArray(d?.results) ? d.results : [];\n // latest row per (model,daemon)\n const latest = new Map<string, { status: string; latency_ms?: number; error_label?: string; probed_at?: number }>();\n for (const row of rows) {\n const k = key(String(row.model_name), String(row.daemon_node_id));\n if (!want.has(k)) continue;\n const prev = latest.get(k);\n if (!prev || (row.probed_at || 0) >= (prev.probed_at || 0)) latest.set(k, row);\n }\n if (aliveRef.current) {\n setCells(prev => {\n const next = { ...prev };\n for (const [k, row] of latest) next[k] = { status: norm(String(row.status)), latency_ms: row.latency_ms, reason: row.error_label };\n return next;\n });\n }\n // done when no cell is still pending\n const allResolved = [...want].every(k => latest.get(k) && latest.get(k)!.status !== 'pending');\n if (allResolved) break;\n } catch { /* transient — keep polling */ }\n }\n if (aliveRef.current) setRunning(false);\n }, [models, providerId]);\n\n useEffect(() => {\n let alive = true;\n (async () => {\n try {\n const r = await fetch('/api/anet/providers/probe', { cache: 'no-store' });\n const d = await r.json().catch(() => ({}));\n const srv: Server[] = Array.isArray(d?.servers) ? d.servers : [];\n if (!alive) return;\n setServers(srv); setLoading(false);\n if (srv.length) run(srv);\n } catch { if (alive) setLoading(false); }\n })();\n return () => { alive = false; };\n }, [run]);\n\n const total = models.length * servers.length;\n const okCount = Object.values(cells).filter(c => c.status === 'ok').length;\n\n if (loading) return <div className=\"px-3 py-3 text-[11px] text-gray-500\">解析服务器集合…</div>;\n if (unconfirmed) {\n return <div className=\"mx-3 my-2 rounded-md border border-amber-500/30 bg-amber-500/5 px-3 py-2 text-[11px] text-amber-300\">\n 连通性 probe 后端尚未就绪:{unconfirmed}。矩阵 UI 已就位,待 hub 升级到含 RFC-028 接真 probe。\n </div>;\n }\n if (!servers.length) return <div className=\"px-3 py-3 text-[11px] text-gray-500\">未发现可达的 host_supervisor 服务器(probe 目标集为空)。</div>;\n\n return (\n <div className=\"border-t border-[#1c1c1f] px-3 py-3\" data-reachability-matrix>\n <div className=\"mb-2 flex items-center justify-between\">\n <span className=\"text-[11px] text-gray-400\">\n 可达矩阵 · <span className=\"tabular-nums text-gray-300\">{okCount}/{total}</span> reachable\n {running && <span className=\"ml-2 text-gray-600\">probing…</span>}\n </span>\n <button type=\"button\" onClick={() => run(servers)} disabled={running}\n className=\"rounded border border-[#26262b] px-2 py-0.5 text-[11px] text-gray-400 hover:text-gray-200 disabled:opacity-50\">重新测试</button>\n </div>\n <div className=\"overflow-x-auto\">\n <table className=\"border-collapse text-[11px]\">\n <thead>\n <tr>\n <th className=\"px-2 py-1 text-left font-medium text-gray-500\">model \\\\ server</th>\n {servers.map(s => <th key={s.daemon_node_id} className=\"px-2 py-1 text-center font-mono font-normal text-gray-400\" title={s.daemon_node_id}>{s.hostname}</th>)}\n </tr>\n </thead>\n <tbody>\n {models.map(m => (\n <tr key={m}>\n <td className=\"px-2 py-1 font-mono text-gray-300 whitespace-nowrap\">{m}</td>\n {servers.map(s => {\n const c = cells[key(m, s.daemon_node_id)];\n if (!c || c.status === 'pending') return <td key={s.daemon_node_id} className=\"px-2 py-1 text-center\">\n <span className=\"inline-block h-3 w-3 animate-spin rounded-full border border-gray-600 border-t-transparent\" aria-label=\"probing\" />\n </td>;\n const ui = CELL_UI[c.status as Exclude<CellStatus, 'pending'>] || CELL_UI.error;\n return (\n <td key={s.daemon_node_id} className=\"px-2 py-1 text-center\">\n <span className={`inline-flex min-w-[2.75rem] items-center justify-center gap-1 rounded border px-1.5 py-0.5 ${ui.cls}`}\n title={c.reason ? `${ui.label}: ${c.reason}` : ui.label} data-cell-status={c.status}>\n <span aria-hidden>{ui.glyph}</span>\n {c.status === 'ok'\n ? (c.latency_ms != null && <span className=\"tabular-nums text-[10px] opacity-80\">{c.latency_ms}ms</span>)\n : <span className=\"text-[10px] opacity-80\">{ui.tag}</span>}\n </span>\n </td>\n );\n })}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n <div className=\"mt-2.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-[10px] text-gray-500\">\n {LEGEND.map(s => {\n const ui = CELL_UI[s];\n return (\n <span key={s} className=\"inline-flex items-center gap-1\">\n <span className={`inline-flex h-3.5 w-3.5 items-center justify-center rounded border ${ui.cls}`} aria-hidden>{ui.glyph}</span>\n {ui.label}\n </span>\n );\n })}\n <span className=\"text-gray-600\">· 实时探测(模型→服务器真实请求往返)</span>\n </div>\n </div>\n );\n}\n"],"names":["TYPES","id","label","disabled","PRESETS","type","base_url","models","keyPlaceholder","detectPreset","baseUrl","hit","find","p","trim","inputCls","parseModels","text","split","map","m","filter","Boolean","ProviderFormModal","initial","onClose","onSaved","editing","provider_id","presetId","setPresetId","name","setName","setType","setBaseUrl","checkedModels","setCheckedModels","modelsText","setModelsText","join","preset","x","extras","includes","apiKey","setApiKey","enabled","setEnabled","phase","setPhase","msg","setMsg","length","isCustom","onKey","e","key","window","addEventListener","removeEventListener","selectPreset","some","toggleModel","prev","finalModels","Array","from","Set","nameValid","urlValid","keyValid","modelsValid","canSave","handleSave","payload","r","fetch","method","headers","body","JSON","stringify","data","json","catch","ok","setTimeout","unconfirmed","error","Error","message","className","onClick","role","width","height","viewBox","fill","stroke","strokeWidth","strokeLinecap","d","value","onChange","target","autoFocus","placeholder","t","on","autoComplete","checked"],"mappings":"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"}
|
|
@@ -7,9 +7,9 @@ globalThis.__BUILD_MANIFEST = {
|
|
|
7
7
|
"static/chunks/03~yq9q893hmn.js"
|
|
8
8
|
],
|
|
9
9
|
"lowPriorityFiles": [
|
|
10
|
-
"static/
|
|
11
|
-
"static/
|
|
12
|
-
"static/
|
|
10
|
+
"static/XerGwQyUEDjwBTySHen4x/_buildManifest.js",
|
|
11
|
+
"static/XerGwQyUEDjwBTySHen4x/_ssgManifest.js",
|
|
12
|
+
"static/XerGwQyUEDjwBTySHen4x/_clientMiddlewareManifest.js"
|
|
13
13
|
],
|
|
14
14
|
"rootMainFiles": [
|
|
15
15
|
"static/chunks/05oi25hh7b81r.js",
|