1(() => { 2 const AGENT_ID_LOCALES = { 3 en:{label:"English",patent:"Patent Pending",title:"xAgent ID | A verifiable Agent identity for every website",copy:{}}, 4 "zh-Hant":{label:"ç¹é«ä¸æ",patent:"å°å©ç³è«ä¸",title:"xAgent ID | æ¯åç¶²ç«çå¯é©è Agent 身å",copy:{}}, 5 "zh-Hans":{label:"ç®ä½ä¸æ",patent:"ä¸å©ç³è¯·ä¸",title:"xAgent ID | æ¯ä¸ªç½ç«çå¯éªè¯ Agent 身份",copy:{ 6 "Benefits":"ä»·å¼","How it works":"è¿ä½æ¹å¼","Pricing":"æ¹æ¡","Get started":"å¼å§ä½¿ç¨","Technical docs":"ææ¯æä»¶","A verifiable Agent identityfor every website.":"让æ¯ä¸ªç½ç«ï¼é½æ¥æå¯éªè¯ç Agent 身份ã","Let your website communicate with AI, buyers, and customers. Trusted identity comes before discovery, answers, recommendations, and actions.":"è®©ä½ çç½ç«ä¸»å¨ä¸ AIãä¹°å®¶åå®¢æ·æ²éã身份å 被信任ï¼å å®¹æææºä¼è¢«æ¾å°ãåçãæ¨è䏿ä½ã","See installation":"æ¥çå®è£ æ¹å¼","View results":"æ¥çæææ¥è¡¨","Let AI find you":"让 AI æ¾å°ä½ ","A verified website identity makes your business easier for Agents to recognize":"ç½ç«èº«ä»½éè¿éªè¯åï¼Agent æ´å®¹æè¯å«ä½ çä¼ä¸","Your website":"ä½ çç½ç«","Searchable":"å¯è¢«æç´¢","Answerable":"å¯è¢«åç","Recommendable":"å¯è¢«æ¨è","Actionable":"å¯è¢«æä½","Customers now ask AI.Is your website ready?":"客æ·å¼å§ç¨ AI æ¾çæ¡ï¼ä½ çç½ç«åå¤å¥½äºåï¼","Three controls.Proof the improvement is live.":"ä¸ä¸ªæ§å¶ç¯èï¼ç¡®è®¤æ¹åçççæã","Start with identity.Add only what your website needs.":"å 建ç«ç½ç«èº«ä»½ï¼åæéè¦æç»å级ã","Website Identity":"ç½ç«èº«ä»½","Continuous Monitor":"æç»çæµ","Active Optimization":"主å¨ä¼å","Agent Operations":"Agent å
6¯æä½","Create identity free":"å 费建ç«èº«ä»½","Activate after verification":"éªè¯èº«ä»½åå¯ç¨","Choose Optimize":"éæ© Optimize","Choose Operate":"éæ© Operate","Cards are processed by Stripe":"ä¿¡ç¨å¡ç± Stripe å¤ç","Agents use scoped prepaid credits":"Agent ä½¿ç¨æèå´çé¢ä»ç¹æ°","One installation byyour engineer or Agent.":"交ç»å·¥ç¨äººåæ Agentï¼ä¸æ¬¡å®æå®è£ ã","Choose modules and permissions":"éæ©éè¦ç模å䏿é","Identity is required for connected services":"Identity æ¯æç»æå¡çå¿ è¦åºç¡","Prepare safe preview":"çæå®å ¨é¢è§","View results demo":"æ¥çæææ¥è¡¨ç¤ºè","Product":"产å","Plans and pricing":"æ¹æ¡ä¸ä»·æ ¼","Privacy":"éç§","Terms":"ä½¿ç¨æ¡æ¬¾","Cancellation & refunds":"åæ¶ä¸é款","Contact":"èç³»æä»¬"}}, 7 ja:{label:"æ¥æ¬èª",patent:"ç¹è¨±åºé¡ä¸",title:"xAgent ID | ãã¹ã¦ã®ã¦ã§ããµã¤ãã«æ¤è¨¼å¯è½ãªAgent IDã",copy:{ 8 "Benefits":"ã§ãããã¨","How it works":"ä»çµã¿","Pricing":"æé","Get started":"å§ãã","Technical docs":"æè¡è³æ","A verifiable Agent identityfor every website.":"ãã¹ã¦ã®ã¦ã§ããµã¤ãã«ãæ¤è¨¼å¯è½ãªAgent IDãã","Let your website communicate with AI, buyers, and customers. Trusted identity comes before discovery, answers, recommendations, and actions.":"ã¦ã§ããµã¤ããAIãè²·ãæã顧客ã¨ç´æ¥ããåãã§ããããã«ãã¾ããä¿¡é ¼ãããIDããçºè¦ã»åçã»æ¨è¦ã»æä½ã®åºçºç¹ã§ãã","See installation":"å°å ¥æ¹æ³ãè¦ã","View results":"ææãè¦ã","Let AI find you":"AIã«è¦ã¤ãã¦ããã","A verified website identity makes your business easier for Agents to recognize":"æ¤è¨¼æ¸ã¿ã®ã¦ã§ããµã¤ãIDã«ãããAgentã伿¥ãæ£ããèªèãããããªãã¾ã","Your website":"ããªãã®ãµã¤ã","Searchable":"æ¤ç´¢ããã","Answerable":"åçããã","Recommendable":"æ¨è¦ããã","Actionable":"æä½ã§ãã","Customers now ask AI.Is your website ready?":"顧客ã¯AIã«è³ªåãã¦ãã¾ããããªãã®ãµã¤ãã¯æºåã§ãã¦ãã¾ããï¼","Three controls.Proof the improvement is live.":"3ã¤ã®ç®¡çã§ãæ¹åãåæ ããããã¨ã証æã","Start with identity.Add only what your website needs.":"ã¾ãIDãä½ããå¿ è¦ãªæ©è½ã ã追å ã","Website Identity":"ã¦ã§ããµã¤ãID","Continuous Monitor":"ç¶ç¶ç£è¦","Active Optimization":"ç¶ç¶æé©å","Agent Operations":"Agentæä½","Create identity free":"ç¡æã§IDã使","Activate after verification":"æ¤è¨¼å¾ã«æå¹å","Choose Optimize":"Optimizeã鏿","Choose Operate":"Operateã鏿","Cards are processed by Stripe":"ã«ã¼ã決æ¸ã¯Stripeãå¦ç","Agents use scoped prepaid credits":"Agentã¯ç¨ééå®ã®ããªãã¤ãã¯ã¬ã¸ããã使ç¨","One installation byyour engineer or Agent.":"ã¨ã³ã¸ãã¢ã¾ãã¯Agentãä¸åº¦ã§å°å ¥ã","Choose modules and permissions":"ã¢ã¸ã¥ã¼ã«ã¨æ¨©éã鏿","Identity is required for connected services":"æ¥ç¶ãµã¼ãã¹ã«ã¯Identityãå¿ è¦ã§ã","Prepare safe preview":"å®å ¨ãªãã¬ãã¥ã¼ã使","View results demo":"ææãã¢ãè¦ã","Product":"製å","Plans and pricing":"ãã©ã³ã¨æé","Privacy":"ãã©ã¤ãã·ã¼","Terms":"å©ç¨è¦ç´","Cancellation & refunds":"è§£ç´ã¨è¿é","Contact":"ãåãåãã"}}, 9 ko:{label:"íêµì´",patent:"í¹í ì¶ì ì¤",title:"xAgent ID | 모ë ì¹ì¬ì´í¸ë¥¼ ìí ê²ì¦ ê°ë¥í Agent ì ì",copy:{ 10 "Benefits":"주ì ê°ì¹","How it works":"ìë ë°©ì","Pricing":"ìê¸ì ","Get started":"ììí기","Technical docs":"기ì 문ì","A verifiable Agent identityfor every website.":"모ë ì¹ì¬ì´í¸ì ê²ì¦ ê°ë¥í Agent ì ìì ì ê³µí©ëë¤.","Let your website communicate with AI, buyers, and customers. Trusted identity comes before discovery, answers, recommendations, and actions.":"ì¹ì¬ì´í¸ê° AI, 구매ì, ê³ ê°ê³¼ ì§ì ìíµíê² íì¸ì. ì 뢰í ì ìë ì ìì´ ê²ì, ëµë³, ì¶ì², ì¤íì ì¶ë°ì ì ëë¤.","See installation":"ì¤ì¹ ë°©ë² ë³´ê¸°","View results":"ì±ê³¼ 보기","Let AI find you":"AIê° ë¹ì ì ì°¾ê² íì¸ì","A verified website identity makes your business easier for Agents to recognize":"ê²ì¦ë ì¹ì¬ì´í¸ ì ìì Agentê° ê¸°ì ì ì íí ì¸ìíëë¡ ëìµëë¤","Your website":"ë´ ì¹ì¬ì´í¸","Searchable":"ê²ì ê°ë¥","Answerable":"ëµë³ ê°ë¥","Recommendable":"ì¶ì² ê°ë¥","Actionable":"ì¤í ê°ë¥","Customers now ask AI.Is your website ready?":"ê³ ê°ì ì´ì AIìê² ë¬»ìµëë¤. ì¹ì¬ì´í¸ë ì¤ë¹ëëì?","Three controls.Proof the improvement is live.":"ì¸ ê°ì§ íµì ë¡ ê°ì ì´ ì¤ì ë°ìëìì ì¦ëª í©ëë¤.","Start with i
10dentity.Add only what your website needs.":"ì ìë¶í° ììíê³ íìí 기ë¥ë§ ì¶ê°íì¸ì.","Website Identity":"ì¹ì¬ì´í¸ ì ì","Continuous Monitor":"ì§ì 모ëí°ë§","Active Optimization":"ë¥ë ìµì í","Agent Operations":"Agent ì´ì","Create identity free":"ë¬´ë£ ì ì ë§ë¤ê¸°","Activate after verification":"ê²ì¦ í íì±í","Choose Optimize":"Optimize ì í","Choose Operate":"Operate ì í","Cards are processed by Stripe":"ì¹´ë ê²°ì ë Stripeê° ì²ë¦¬í©ëë¤","Agents use scoped prepaid credits":"Agentë ë²ìê° ì íë ì ë¶ í¬ë ë§ì ì¬ì©í©ëë¤","One installation byyour engineer or Agent.":"ìì§ëì´ë Agentê° í ë²ì ì¤ì¹í©ëë¤.","Choose modules and permissions":"모ëê³¼ ê¶í ì í","Identity is required for connected services":"ì°ê²°í ìë¹ì¤ìë Identityê° íìí©ëë¤","Prepare safe preview":"ìì í 미리보기 ìì±","View results demo":"ì±ê³¼ ë°ëª¨ 보기","Product":"ì í","Plans and pricing":"ìê¸ì ","Privacy":"ê°ì¸ì ë³´","Terms":"ì´ì©ì½ê´","Cancellation & refunds":"ì·¨ì ë° íë¶","Contact":"문ì"}}, 11 es:{label:"Español",patent:"Patente en trámite",title:"xAgent ID | Una identidad Agent verificable para cada sitio web",copy:{ 12 "Benefits":"Beneficios","How it works":"Cómo funciona","Pricing":"Precios","Get started":"Empezar","Technical docs":"Documentación técnica","A verifiable Agent identityfor every website.":"Una identidad Agent verificable para cada sitio web.","Let your website communicate with AI, buyers, and customers. Trusted identity comes before discovery, answers, recommendations, and actions.":"Permite que tu sitio se comunique con la IA, compradores y clientes. Una identidad confiable es la base para ser encontrado, respondido, recomendado y operado.","See installation":"Ver instalación","View results":"Ver resultados","Let AI find you":"Haz que la IA te encuentre","A verified website identity makes your business easier for Agents to recognize":"Una identidad web verificada ayuda a los Agents a reconocer tu empresa","Your website":"Tu sitio web","Searchable":"Encontrable","Answerable":"Respondible","Recommendable":"Recomendable","Actionable":"Operable","Customers now ask AI.Is your website ready?":"Los clientes ya preguntan a la IA. ¿Tu sitio está preparado?","Three controls.Proof the improvement is live.":"Tres controles para demostrar que la mejora está activa.","Start with identity.Add only what your website needs.":"Empieza con la identidad y añade solo lo necesario.","Website Identity":"Identidad web","Continuous Monitor":"Monitoreo continuo","Active Optimization":"Optimización activa","Agent Operations":"Operaciones Agent","Create identity free":"Crear identidad gratis","Activate after verification":"Activar tras verificar","Choose Optimize":"Elegir Optimize","Choose Operate":"Elegir Operate","Cards are processed by Stripe":"Stripe procesa las tarjetas","Agents use scoped prepaid credits":"Los Agents usan créditos prepagados con alcance limitado","One installation byyour engineer or Agent.":"Una instalación por tu ingeniero o Agent.","Choose modules and permissions":"Elige módulos y permisos","Identity is required for connected services":"Identity es obligatorio para servicios conectados","Prepare safe preview":"Preparar vista segura","View results demo":"Ver demo de resultados","Product":"Producto","Plans and pricing":"Planes y precios","Privacy":"Privacidad","Terms":"Términos","Cancellation & refunds":"Cancelación y reembolsos","Contact":"Contacto"}}, 13 fr:{label:"Français",patent:"Brevet en instance",title:"xAgent ID | Une identité Agent vérifiable pour chaque site",copy:{ 14 "Benefits":"Avantages","How it works":"Fonctionnement","Pricing":"Tarifs","Get started":"Commencer","Technical docs":"Documentation technique","A verifiable Agent identityfor every website.":"Une identité Agent vérifiable pour chaque site web.","Let your website communicate with AI, buyers, and customers. Trusted identity comes before discovery, answers, recommendations, and actions.":"Permettez à votre site d'échanger avec l'IA, les acheteurs et les clients. Une identité fiable précède la découverte, les réponses, les recommandations et les actions.","See installation":"Voir l'installation","Vie
14w results":"Voir les résultats","Let AI find you":"Aidez l'IA à vous trouver","A verified website identity makes your business easier for Agents to recognize":"Une identité de site vérifiée aide les Agents à reconnaître votre entreprise","Your website":"Votre site","Searchable":"Trouvable","Answerable":"Répondable","Recommendable":"Recommandable","Actionable":"Opérable","Customers now ask AI.Is your website ready?":"Les clients interrogent désormais l'IA. Votre site est-il prêt ?","Three controls.Proof the improvement is live.":"Trois contrôles prouvent que l'amélioration est en ligne.","Start with identity.Add only what your website needs.":"Commencez par l'identité, puis ajoutez le nécessaire.","Website Identity":"Identité du site","Continuous Monitor":"Suivi continu","Active Optimization":"Optimisation active","Agent Operations":"Opérations Agent","Create identity free":"Créer l'identité gratuitement","Activate after verification":"Activer après vérification","Choose Optimize":"Choisir Optimize","Choose Operate":"Choisir Operate","Cards are processed by Stripe":"Les cartes sont traitées par Stripe","Agents use scoped prepaid credits":"Les Agents utilisent des crédits prépayés limités","One installation byyour engineer or Agent.":"Une seule installation par votre ingénieur ou Agent.","Choose modules and permissions":"Choisir modules et autorisations","Identity is required for connected services":"Identity est requis pour les services connectés","Prepare safe preview":"Préparer l'aperçu sécurisé","View results demo":"Voir la démo des résultats","Product":"Produit","Plans and pricing":"Offres et tarifs","Privacy":"Confidentialité","Terms":"Conditions","Cancellation & refunds":"Annulation et remboursement","Contact":"Contact"}}, 15 de:{label:"Deutsch",patent:"Patent angemeldet",title:"xAgent ID | Eine verifizierbare Agent-Identität für jede Website",copy:{ 16 "Benefits":"Vorteile","How it works":"So funktioniert es","Pricing":"Preise","Get started":"Starten","Technical docs":"Technische Dokumentation","A verifiable Agent identityfor every website.":"Eine verifizierbare Agent-Identität für jede Website.","Let your website communicate with AI, buyers, and customers. Trusted identity comes before discovery, answers, recommendations, and actions.":"Ihre Website kommuniziert mit KI, Käufern und Kunden. Eine vertrauenswürdige Identität ist die Grundlage für Auffindbarkeit, Antworten, Empfehlungen und Aktionen.","See installation":"Installation ansehen","View results":"Ergebnisse ansehen","Let AI find you":"Werden Sie von KI gefunden","A verified website identity makes your business easier for Agents to recognize":"Eine verifizierte Website-Identität hilft Agents, Ihr Unternehmen zu erkennen","Your website":"Ihre Website","Searchable":"Auffindbar","Answerable":"Antwortfähig","Recommendable":"Empfehlbar","Actionable":"Ausführbar","Customers now ask AI.Is your website ready?":"Kunden fragen heute KI. Ist Ihre Website bereit?","Three controls.Proof the improvement is live.":"Drei Kontrollen belegen, dass die Verbesserung live ist.","Start with identity.Add only what your website needs.":"Mit Identität starten und nur Nötiges ergänzen.","Website Identity":"Website-Identität","Continuous Monitor":"Laufende Ãberwachung","Active Optimization":"Aktive Optimierung","Agent Operations":"Agent-Betrieb","Create identity free":"Identität kostenlos erstellen","Activate after verification":"Nach Verifizierung aktivieren","Choose Optimize":"Optimize wählen","Choose Operate":"Operate wählen","Cards are processed by Stripe":"Kartenzahlungen verarbeitet Stripe","Agents use scoped prepaid credits":"Agents nutzen zweckgebundene Prepaid-Guthaben","One installation byyour engineer or Agent.":"Eine Installation durch Ihren Entwickler oder Agent.","Choose modules and permissions":"Module und Berechtigungen wählen","Identity is required for connected services":"Identity ist für verbundene Dienste erforderlich","Prepare safe preview":"Sichere Vorschau erstellen","View results demo":"Ergebnisdemo ansehen","Product":"Produkt","Plans and pricing":"Pläne und Preise","Privacy":"Datenschutz","Terms":"Bedingungen","Cancellation & refunds":"Kündigung und Erstattung","Contact":"Kontakt"}} 17 }; 18 const EXPANDED_LOCALE_COPY = { 19 "Skip to main content":["è·³è³ä¸»è¦å 容","ã¡ã¤ã³ã³ã³ãã³ãã¸","주ì ì½í ì¸ ë¡ ì´ë","Ir al contenido principal","Aller au contenu principal","Zum Hauptinhalt"], 20 "xAgent ID binds to your domain, works through your own Agent, and continuously improves SEO, AEO, GEO, and AGO.":["xAgent ID ç»å®ä½ çååï¼éè¿ä½ èªå·±ç Agent è¿ä½ï¼å¹¶æç»æ¹å SEOãAEOãGEO ä¸ AGOã","xAgent IDã¯ãã¡ã¤ã³ã«ç´ã¥ããèªç¤¾ã®Agentãéãã¦SEOã»AEOã»GEOã»AGOãç¶ç¶æ¹åãã¾ãã","xAgent IDë ëë©ì¸ì ì°ê²°ëì´ ìì²´ Agent를 íµí´ SEO, AEO, GEO, AGO를 ì§ì ê°ì í©ëë¤.","xAgent ID se vincula a tu dominio, funciona mediante tu propio Agent y mejora continuamente SEO, AEO, GEO y AGO.","xAgent ID est lié à votre domaine, fonctionne via votre propre Agent et améliore en continu le SEO, lâAEO, le GEO et lâAGO.","xAgent ID wird mit Ihrer Domain verknüpft, arbeitet über Ihren eigenen Agent und verbessert SEO, AEO, GEO und AGO fortlaufend."], 21 "No admin access required":["æ éç½ç«åå°æé","管çè æ¨©éã¯ä¸è¦","ê´ë¦¬ì ì ê·¼ ê¶í ë¶íì","Sin acceso de administrador","Aucun accès administrateur","Kein Administratorzugriff"], 22 "Every change needs approval":["æ¯æ¬¡ä¿®æ¹é½éæ ¸å","夿´ãã¨ã«æ¿èªãå¿ è¦","모ë ë³ê²½ì ì¹ì¸ íì","Cada camb
22io requiere aprobación","Chaque modification doit être approuvée","Jede Ãnderung braucht Freigabe"], 23 "Pause or roll back anytime":["å¯éæ¶æåæåæ»","ãã¤ã§ã忢ã»ãã¼ã«ããã¯","ì¸ì ë ì¤ì§ ëë 롤백","Pausa o revierte cuando quieras","Pause ou retour arrière à tout moment","Jederzeit pausieren oder zurücksetzen"], 24 "Results are verifiable":["æ¹åç»æå¯éªè¯","ææãæ¤è¨¼å¯è½","ê²°ê³¼ ê²ì¦ ê°ë¥","Resultados verificables","Résultats vérifiables","Ergebnisse sind überprüfbar"], 25 "Finds your website through search and AI answers":["éè¿æç´¢ä¸ AI åçæ¾å°ä½ çç½ç«","æ¤ç´¢ãAIåçãããµã¤ããè¦ã¤ãã¾ã","ê²ìê³¼ AI ëµë³ì íµí´ ì¹ì¬ì´í¸ë¥¼ ì°¾ìµëë¤","Encuentra tu sitio mediante búsquedas y respuestas de IA","Trouve votre site via la recherche et les réponses de lâIA","Findet Ihre Website über Suche und KI-Antworten"], 26 "Enterprise, buyer, search, and service Agents":["ä¼ä¸ãä¹°å®¶ãæç´¢ä¸æå¡ Agent","伿¥ã»è³¼è²·ã»æ¤ç´¢ã»ãµã¼ãã¹ã®Agent","기ì , 구매, ê²ì ë° ìë¹ì¤ Agent","Agents empresariales, de compra, búsqueda y servicio","Agents dâentreprise, dâachat, de recherche et de service","Unternehmens-, Käufer-, Such- und Service-Agents"], 27 "We assess whether your website can be found, answer questions, earn recommendations, and support future AI-assisted actions.":["æä»¬è¯ä¼°ç½ç«è½å¦è¢«æ¾å°ãåçé®é¢ãè·å¾æ¨èï¼å¹¶æ¯ææªæ¥ç± AI åå©çæä½ã","ãµã¤ããçºè¦ããã質åã«çããæ¨è¦ãããå°æ¥ã®AIæä½ã«å¯¾å¿ã§ããããè©ä¾¡ãã¾ãã","ì¹ì¬ì´í¸ê° ê²ìëê³ ì§ë¬¸ì ëµíë©° ì¶ì²ë°ê³ í¥í AI ìì ì ì§ìí ì ìëì§ íê°í©ëë¤.","Evaluamos si tu sitio puede encontrarse, responder, ser recomendado y admitir futuras acciones con IA.","Nous évaluons si votre site peut être trouvé, répondre, être recommandé et permettre de futures actions assistées par lâIA.","Wir prüfen, ob Ihre Website gefunden wird, Fragen beantwortet, empfohlen wird und künftige KI-Aktionen unterstützt."], 28 "Confirm that search engines and AI can access your key products, services, and company information.":["确认æç´¢å¼æä¸ AI è½è¯»åéè¦äº§åãæå¡ä¸å ¬å¸ä¿¡æ¯ã","æ¤ç´¢ã¨ã³ã¸ã³ã¨AIã主è¦ãªè£½åã»ãµã¼ãã¹ã»ä¼æ¥æ å ±ãåå¾ã§ããã確èªãã¾ãã","ê²ììì§ê³¼ AIê° ì£¼ì ì í, ìë¹ì¤, 기ì ì ë³´ì ì ê·¼í ì ìëì§ íì¸í©ëë¤.","Confirma que buscadores e IA acceden a productos, servicios e información clave.","Vérifiez que les moteurs et lâIA accèdent à vos produits, services et informations clés.","Prüfen Sie, ob Suchmaschinen und KI auf wichtige Produkt-, Service- und Unternehmensdaten zugreifen können."], 29 "Turn your expertise, products, and FAQs into answers AI can understand and cite.":["å°ä¸ä¸ç¥è¯ã产åä¸å¸¸è§é®é¢æ´çæ AI è½çè§£åå¼ç¨ççæ¡ã","å°éç¥èã»è£½åã»FAQããAIãçè§£ãå¼ç¨ã§ããåçã«æ´ãã¾ãã","ì 문ì±, ì í, FAQ를 AIê° ì´í´íê³ ì¸ì©í ì ìë ëµë³ì¼ë¡ 구ì±í©ëë¤.","Convierte experiencia, productos y preguntas frecuentes en respuestas que la IA pueda entender y citar.","Transformez expertise, produits et FAQ en réponses comprises et citées par lâIA.","Machen Sie aus Fachwissen, Produkten und FAQs Antworten, die KI versteht und zitiert."], 30 "Track whether AI represents your brand accurately across recommendations, citations, and market conversations.":["追踪 AI 卿¨èãå¼ç¨ä¸å¸åºè®¨è®ºä¸æ¯å¦æ£ç¡®åç°ä½ çåçã","æ¨è¦ã»å¼ç¨ã»å¸å ´ä¼è©±ã§AIããã©ã³ããæ£ãã表ç¾ãã¦ããã追跡ãã¾ãã","ì¶ì², ì¸ì©, ìì¥ ëíìì AIê° ë¸ëë를 ì íí íííëì§ ì¶ì í©ëë¤.","Mide si la IA representa correctamente tu marca en recomendaciones, citas y conversaciones.","Suivez la représentation correcte de votre marque dans les recommandations, citations et conversations.","Verfolgen Sie, ob KI Ihre Marke in Empfehlungen, Zitaten und Gesprächen korrekt darstellt."], 31 "Prepare safe, controlled ways for AI to enquire, complete forms, and perform customer tasks.":["为 AI æ¥è¯¢ã填表ä¸å®æå®¢æ·ä»»å¡åå¤å®å ¨ä¸å¯æ§çæ¹å¼ã","AIãåãåããããã©ã¼ã å ¥åã顧客æ¥åãå®å ¨ã«å®è¡ã§ããä»çµã¿ãæ´ãã¾ãã","AIê° ë¬¸ì, ìì ìì±, ê³ ê° ìì ì ìì íê² ìííëë¡ ì¤ë¹í©ëë¤.","Prepara formas seguras y controladas para que la IA consulte, complete formularios y ejecute tareas.","Préparez des moyens sûrs et contrôlés pour que lâIA interroge, remplisse des formulaires et réalise des tâches.","Bereiten Sie sichere, kontrollierte Wege für Anfragen, Formulare und Kundenaufgaben durch KI vor."], 32 "You remain in control. xAgent ID does not need credentials to your CMS, repository, deployment pipeline, or database.":["ç½ç«å§ç»ç±ä½ æ§å¶ãxAgent ID ä¸éè¦å 容系ç»ã代ç åºãé¨ç½²æµç¨ææ°æ®åºåè¯ã","ç®¡çæ¨©éã¯å¸¸ã«ã客æ§ã«ããã¾ããxAgent IDã¯CMSããªãã¸ããªããããã¤ãDBã®èªè¨¼æ å ±ãå¿ è¦ã¨ãã¾ããã","íµì ê¶ì íì ê³ ê°ìê² ììµëë¤. xAgent IDë CMS, ì ì¥ì, ë°°í¬ íì´íë¼ì¸, ë°ì´í°ë² ì´ì¤ ì격 ì¦ëª ì´ íì ììµëë¤.","Tú mantienes el control. xAgent ID no necesita credenciales de CMS, repositorio, despliegue ni base de datos.","Vous gardez le contrôle. xAgent ID nâa besoin dâaucun identifiant CMS, dépôt, déploiement ou base de données.","Sie behalten die Kontrolle. xAgent ID benötigt keine Zugangsdaten für CMS, Repository, Deployment oder Datenbank."], 33 "You approve the allowed scope":["ä½ æ ¸å坿§è¡èå´","許å¯ç¯å²ãæ¿èª","íì© ë²ì를 ì¹ì¸","Apruebas el alcance permitido","Vous approuvez le périmètre","Sie genehmigen den erlaubten Umfang"], 34 "You choose the AI-facing files that may change. Pages, prices, legal content, source code, and databases stay excluded.":["ä½ å³å®åªäº AI 设置æä»¶å¯ä¿®æ¹ï¼é¡µé¢ãä»·æ ¼ãæ³å¡å å®¹ãæºä»£ç 䏿°æ®åºä¸å¾æé¤ã","夿´å¯è½ãªAIåããã¡ã¤ã«ãé¸ã³ã¾ãããã¼ã¸ãä¾¡æ ¼ãæ³åãã½ã¼ã¹ã³ã¼ããDBã¯å¯¾è±¡å¤ã§ãã","ë³ê²½ ê°ë¥í AIì© íì¼ì
34 ì íí©ëë¤. íì´ì§, ê°ê²©, ë²ë¥ 문구, ìì¤ ì½ë, DBë ì ì¸ë©ëë¤.","Eliges los archivos para IA que pueden cambiar. Páginas, precios, textos legales, código y bases de datos quedan excluidos.","Vous choisissez les fichiers destinés à lâIA modifiables. Pages, prix, textes juridiques, code et bases restent exclus.","Sie wählen die änderbaren KI-Dateien. Seiten, Preise, Rechtstexte, Quellcode und Datenbanken bleiben ausgeschlossen."], 35 "Your website Agent applies it":["ç±ä½ ç½ç«å ç Agent æ§è¡","ãµã¤ãå ã®Agentãé©ç¨","ì¹ì¬ì´í¸ Agentê° ì ì©","El Agent de tu sitio lo aplica","LâAgent de votre site lâapplique","Ihr Website-Agent wendet es an"], 36 "The change runs in your environment. Keys and credentials stay with you, and anything outside policy is blocked.":["ä¿®æ¹å¨ä½ çç¯å¢ä¸æ§è¡ï¼éé¥ä¸åè¯çå¨ä½ æä¸ï¼è¶ åºæ¿çèå´å³è¢«é»æ¡ã","夿´ã¯èªç¤¾ç°å¢ã§å®è¡ãããéµã¨èªè¨¼æ å ±ã¯æå ã«æ®ããããªã·ã¼å¤ã¯é®æããã¾ãã","ë³ê²½ì ê³ ê° íê²½ìì ì¤íëë©° í¤ì ì격 ì¦ëª ì ê³ ê°ì´ ë³´ê´íê³ ì ì± ë° ìì ì ì°¨ë¨ë©ëë¤.","El cambio se ejecuta en tu entorno; conservas claves y credenciales, y se bloquea todo lo que exceda la polÃtica.","La modification sâexécute dans votre environnement ; vous gardez clés et identifiants, tout hors politique est bloqué.","Die Ãnderung läuft in Ihrer Umgebung; Schlüssel und Zugangsdaten bleiben bei Ihnen, alles auÃerhalb der Richtlinie wird blockiert."], 37 "Public re-check confirms it":["å ¬å¼éæ°æ£æ¥ç¡®è®¤ç»æ","å ¬éååå¾ã§ç¢ºèª","ê³µê° ì¬ê²ì¬ë¡ íì¸","Una revisión pública lo confirma","Une nouvelle vérification publique confirme","Ãffentliche Nachprüfung bestätigt"], 38 "A separate verifier re-reads the public URL. The result counts only when public content and the website receipt agree.":["ç¬ç«éªè¯ç¨åºéæ°è¯»åå ¬å¼ç½åï¼å ¬å¼å 容ä¸ç½ç«æ¶æ®ä¸è´æ¶æè®¡å ¥ææã","ç¬ç«æ¤è¨¼ãå ¬éURLãååå¾ããå ¬éå 容ã¨ãµã¤ãã®åé 証ãä¸è´ããå ´åã®ã¿ææã«è¨ä¸ãã¾ãã","ë 립 ê²ì¦ê¸°ê° ê³µê° URLì ë¤ì ì½ê³ ê³µê° ì½í ì¸ ì ì¹ì¬ì´í¸ ììì¦ì´ ì¼ì¹í ëë§ ì±ê³¼ë¡ ì¸ì í©ëë¤.","Un verificador independiente vuelve a leer la URL pública; solo cuenta si el contenido y el recibo del sitio coinciden.","Un vérificateur indépendant relit lâURL publique ; le résultat ne compte que si contenu et reçu concordent.","Ein unabhängiger Prüfer liest die öffentliche URL erneut; das Ergebnis zählt nur bei Ãbereinstimmung mit dem Website-Beleg."], 39 "Until public verification succeeds, the improvement is not counted.":["å ¬å¼éªè¯æååï¼æ¬æ¬¡æ¹åä¸è®¡å ¥ææã","å ¬éæ¤è¨¼ãæåããã¾ã§ææã«ã¯å«ãã¾ããã","ê³µê° ê²ì¦ì´ ì±ê³µí기 ì ìë ê°ì ì¼ë¡ ì§ê³íì§ ììµëë¤.","La mejora no se contabiliza hasta superar la verificación pública.","Lâamélioration nâest comptée quâaprès vérification publique réussie.","Die Verbesserung zählt erst nach erfolgreicher öffentlicher Prüfung."], 40 "The dashboard shows Pending verification instead of a misleading score.":["æ¥è¡¨ä¼æ¾ç¤ºâå¾ éªè¯âï¼ä¸ä¼äº§çè¯¯å¯¼åæ°ã","ããã·ã¥ãã¼ãã¯èª¤è§£ãæãç¹æ°ã§ã¯ãªããæ¤è¨¼å¾ ã¡ãã¨è¡¨ç¤ºãã¾ãã","ëìë³´ëë ì¤í´ë¥¼ ë¶ë¥´ë ì ì ëì âê²ì¦ ë기â를 íìí©ëë¤.","El panel muestra «Verificación pendiente» en lugar de una puntuación engañosa.","Le tableau affiche « Vérification en attente » plutôt quâun score trompeur.","Das Dashboard zeigt âPrüfung ausstehendâ statt einer irreführenden Punktzahl."], 41 "Technical proof":["ææ¯è¯æ","æè¡ç証æ","기ì ì¦ëª ","Prueba técnica","Preuve technique","Technischer Nachweis"], 42 "You keep ownership and final control":["ç½ç«ä¸æç»å³å®æé½å±äºä½ ","æææ¨©ã¨æçµæ±ºå®æ¨©ãç¶æ","ìì ê¶ê³¼ ìµì¢ íµì ê¶ ì ì§","Mantienes la propiedad y el control final","Vous gardez la propriété et le contrôle final","Sie behalten Eigentum und letzte Kontrolle"], 43 "Pause, revoke access, or roll back at any time.":["å¯éæ¶æåãæ¤éæææåæ»ã","ãã¤ã§ãåæ¢ãæ¨©éåæ¶ããã¼ã«ããã¯ãå¯è½ã§ãã","ì¸ì ë ì¤ì§, ê¶í ì² í, ë¡¤ë°±ì´ ê°ë¥í©ëë¤.","Pausa, revoca el acceso o revierte en cualquier momento.","Mettez en pause, révoquez ou revenez en arrière à tout moment.","Jederzeit pausieren, Zugriff widerrufen oder zurücksetzen."], 44 "Audits, recommendations, and verification":["æ£æ¥ãå»ºè®®ä¸ææéªè¯","ç£æ»ã»ææ¡ã»æææ¤è¨¼","ì§ë¨, ê¶ê³ , ì±ê³¼ ê²ì¦","AuditorÃas, recomendaciones y verificación","Audits, recommandations et vérification","Prüfungen, Empfehlungen und Verifizierung"], 45 "No source code, database, or full-site access is required.":["æ éæºä»£ç ãæ°æ®åºææ´ä¸ªç½ç«çæ§å¶æã","ã½ã¼ã¹ã³ã¼ããDBããµã¤ãå ¨ä½ã¸ã®ã¢ã¯ã»ã¹ã¯ä¸è¦ã§ãã","ìì¤ ì½ë, ë°ì´í°ë² ì´ì¤, ì ì²´ ì¬ì´í¸ ì ê·¼ì´ íì ììµëë¤.","No se requiere acceso al código, base de datos ni a todo el sitio.","Aucun accès au code source, à la base de données ou au site complet nâest requis.","Kein Zugriff auf Quellcode, Datenbank oder die gesamte Website erforderlich."], 46 "Advanced technology.A process you can understand.":["ä¸ä¸ææ¯ï¼æ¾è¿çå¾æçæµç¨ã","é«åº¦ãªæè¡ããçè§£ã§ããæé ã«ã","ê³ ê¸ ê¸°ì ì ì´í´í기 ì¬ì´ ê³¼ì ì¼ë¡.","TecnologÃa avanzada en un proceso comprensible.","Une technologie avancée dans un processus compréhensible.","Fortschrittliche Technik in einem verständlichen Ablauf."], 47 "Owners see findings, recommendations, and results. Engineers and Agents receive the full protocols, permissions, and evidence.":["ç½ç«ä¸»çå°é®é¢ã建议ä¸ç»æï¼å·¥ç¨äººåä¸ Agent ååå¾å®æ´åè®®ãæéä¸è¯æ®ã","ãµã¤ãææè ã¯èª²é¡ã»ææ¡ã»ææã確èªããã¨ã³ã¸ãã¢ã¨Agentã¯å®å ¨ãªãããã³ã«ã»æ¨©éã
47»è¨¼æ ãåãåãã¾ãã","ì¹ì¬ì´í¸ ìì ìë 문ì , ê¶ê³ , 결과를 ë³´ê³ ìì§ëì´ì Agentë ì ì²´ íë¡í ì½, ê¶í, ì¦ê±°ë¥¼ ë°ìµëë¤.","Los propietarios ven hallazgos, recomendaciones y resultados; ingenieros y Agents reciben protocolos, permisos y evidencias.","Les propriétaires voient constats, recommandations et résultats ; ingénieurs et Agents reçoivent protocoles, autorisations et preuves.","Betreiber sehen Befunde, Empfehlungen und Ergebnisse; Entwickler und Agents erhalten Protokolle, Rechte und Nachweise."], 48 "Verify the website and its owner":["确认ç½ç«ä¸ç®¡çè 身份","ãµã¤ãã¨ææè ã確èª","ì¹ì¬ì´í¸ì ìì ì ê²ì¦","Verifica el sitio y su propietario","Vérifier le site et son propriétaire","Website und Betreiber verifizieren"], 49 "Help AI find the right information and services":["让 AI æ¾å°æ£ç¡®ä¿¡æ¯ä¸æå¡å ¥å£","AIãæ£ããæ å ±ã¨ãµã¼ãã¹ãçºè¦","AIê° ì¬ë°ë¥¸ ì ë³´ì ìë¹ì¤ë¥¼ ì°¾ëë¡ ì§ì","Ayuda a la IA a encontrar información y servicios correctos","Aider lâIA à trouver les bonnes informations et services","KI findet die richtigen Informationen und Dienste"], 50 "Turn findings into approved improvements":["æé®é¢è½¬æå¯æ ¸åçæ¹åå·¥ä½","課é¡ãæ¿èªå¯è½ãªæ¹åã«å¤æ","문ì 를 ì¹ì¸ ê°ë¥í ê°ì ìì ì¼ë¡ ì í","Convierte hallazgos en mejoras aprobables","Transformer les constats en améliorations approuvables","Befunde in freigabefähige Verbesserungen umwandeln"], 51 "Track whether results actually improve":["æç»è¿½è¸ªæ¯å¦ççæ¹å","å®éã®æ¹åãç¶ç¶è¿½è·¡","ì¤ì ê°ì ì¬ë¶ë¥¼ ì§ì ì¶ì ","Comprueba si los resultados mejoran de verdad","Suivre si les résultats sâaméliorent réellement","Verfolgen, ob sich Ergebnisse wirklich verbessern"], 52 "Live":["å¯ç¨","å©ç¨å¯è½","ì¬ì© ê°ë¥","Disponible","Disponible","Verfügbar"], 53 "Pilot":["è¯ç¹","試é¨éç¨","íì¼ë¿","Piloto","Pilote","Pilot"], 54 "Connecting":["æ¥å ¥ä¸","æ¥ç¶ä¸","ì°ê²° ì¤","En conexión","Connexion en cours","Wird angebunden"], 55 "AI reads":["AI 读å","AIèªå","AI ì½ê¸°","Lecturas de IA","Lectures IA","KI-Lesezugriffe"], 56 "AI citations":["AI å¼ç¨","AIå¼ç¨","AI ì¸ì©","Citas de IA","Citations IA","KI-Zitate"], 57 "Conversions":["转åä¸æäº¤","ã³ã³ãã¼ã¸ã§ã³","ì í","Conversiones","Conversions","Conversions"], 58 "Log connector":["æ¥å¿ä¸²æ¥","ãã°é£æº","ë¡ê·¸ ì°ë","Conector de registros","Connecteur de journaux","Log-Anbindung"], 59 "Probe set":["åºå®é¢ç»","åºå®ããã¼ã","ê³ ì ì§ë¬¸ ì¸í¸","Conjunto de pruebas","Jeu de sondes","Festes Prüfset"], 60 "Analytics data":["åæèµæ","åæãã¼ã¿","ë¶ì ë°ì´í°","Datos analÃticos","Données analytiques","Analysedaten"], 61 "Evidence rule":["è¯æ®è§å","証æ ã«ã¼ã«","ì¦ê±° ê·ì¹","Regla de evidencia","Règle de preuve","Nachweisregel"], 62 "Without CDN logs, fixed probes, or Analytics and CRM evidence, the status remains pending. Missing data is never converted to zero or synthetic outcomes.":["没æ CDN æ¥å¿ãåºå®é®é¢æµè¯æ Analyticsï¼CRM è¯æ®ï¼å°±ç»´æå¾ åå¾ï¼ç¼ºå°èµæä¸ä¼è¢«åæ 0ï¼ä¹ä¸ä¼ç¨æ¨¡æç»æè¡¥è¶³ã","CDNãã°ãåºå®ããã¼ããAnalyticsï¼CRM証æ ããªããã°ä¿çã§ããæ¬ æãã¼ããæ¨¡æ¬çµæã«ç½®ãæãã¾ããã","CDN ë¡ê·¸, ê³ ì ì§ë¬¸ í ì¤í¸, Analytics/CRM ì¦ê±°ê° ìì¼ë©´ ë기 ìíë¡ ë¨ìµëë¤. ëë½ ìë£ë¥¼ 0ì´ë ê°ì ê²°ê³¼ë¡ ëì²´íì§ ììµëë¤.","Sin registros CDN, pruebas fijas o evidencia de Analytics/CRM, el estado queda pendiente. Los datos ausentes no se convierten en cero ni en resultados simulados.","Sans journaux CDN, sondes fixes ou preuves Analytics/CRM, le statut reste en attente. Les données absentes ne deviennent ni zéro ni résultats simulés.","Ohne CDN-Logs, feste Prüfsets oder Analytics-/CRM-Nachweise bleibt der Status offen. Fehlende Daten werden weder zu null noch zu simulierten Ergebnissen."], 63 "A public audit needs no installation. Ongoing monitoring, optimization, and Agent operations require the free verified website identity.":["å ¬å¼æ£æµæ éå®è£ ï¼æç»çæµãä¼åä¸ Agent æä½éå 宿å è´¹çç½å身份éªè¯ã","å ¬éç£æ»ã¯å°å ¥ä¸è¦ã§ããç¶ç¶ç£è¦ã»æé©åã»Agentæä½ã«ã¯ç¡æã®ãµã¤ãIDæ¤è¨¼ãå¿ è¦ã§ãã","ê³µê° ì§ë¨ì ì¤ì¹ê° íì ììµëë¤. ì§ì 모ëí°ë§, ìµ
63ì í, Agent ì´ììë ë¬´ë£ ì¹ì¬ì´í¸ ì ì ê²ì¦ì´ íìí©ëë¤.","La auditorÃa pública no requiere instalación. El seguimiento, la optimización y las operaciones Agent requieren identidad web verificada gratuita.","Lâaudit public ne nécessite aucune installation. Le suivi, lâoptimisation et les opérations Agent exigent lâidentité de site vérifiée gratuite.","Der öffentliche Audit braucht keine Installation. Laufende Ãberwachung, Optimierung und Agent-Betrieb erfordern die kostenlose verifizierte Website-Identität."], 64 "Website identity":["ç½ç«èº«ä»½","ãµã¤ãID","ì¹ì¬ì´í¸ ì ì","Identidad web","Identité du site","Website-Identität"], 65 "Continuous evidence":["æç»è¯æ®","ç¶ç¶çãªè¨¼æ ","ì§ì ì¦ê±°","Evidencia continua","Preuves continues","Fortlaufende Nachweise"], 66 "Approved improvements":["æ ¸ååæ¹å","æ¿èªæ¸ã¿æ¹å","ì¹ì¸ë ê°ì ","Mejoras aprobadas","Améliorations approuvées","Freigegebene Verbesserungen"], 67 "Agent operations":["Agent æä½","Agentæä½","Agent ì´ì","Operaciones Agent","Opérations Agent","Agent-Betrieb"], 68 "Required foundation":["å¿ è¦åºç¡","å¿ é åºç¤","íì 기ë°","Base obligatoria","Base requise","Erforderliche Grundlage"], 69 "per verified domain":["æ¯ä¸ªéªè¯ç½å","æ¤è¨¼æ¸ã¿ãã¡ã¤ã³ãã¨","ê²ì¦ ëë©ì¸ë¹","por dominio verificado","par domaine vérifié","pro verifizierter Domain"], 70 "Domain ownership proof":["ç½åæææè¯æ","ãã¡ã¤ã³ææè¨¼æ","ëë©ì¸ ìì ê¶ ì¦ëª ","Prueba de propiedad del dominio","Preuve de propriété du domaine","Nachweis des Domain-Eigentums"], 71 "One public baseline audit":["䏿¬¡å ¬å¼åºåæ£æµ","å ¬éãã¼ã¹ã©ã¤ã³ç£æ»1å","ê³µê° ê¸°ì¤ ì§ë¨ 1í","Una auditorÃa pública inicial","Un audit public initial","Ein öffentlicher Basis-Audit"], 72 "Includes Identity":["å å« Identity","Identityãå«ã","Identity í¬í¨","Incluye Identity","Identity inclus","Identity enthalten"], 73 "monthly / domain":["æ¯æï¼æ¯ç½å","æé¡ï¼ãã¡ã¤ã³","ì / ëë©ì¸","mensual / dominio","mensuel / domaine","monatlich / Domain"], 74 "AI reads, citations, conversion connectors":["AI 读åãå¼ç¨ä¸è½¬å串æ¥","AIèªåã»å¼ç¨ã»ã³ã³ãã¼ã¸ã§ã³æ¥ç¶","AI ì½ê¸°, ì¸ì©, ì í ì°ë","Lecturas, citas y conectores de conversión de IA","Lectures, citations et connecteurs de conversion IA","KI-Lesezugriffe, Zitate und Conversion-Anbindungen"], 75 "Evidence history and alerts":["è¯æ®åå²ä¸å¼å¸¸æé","証æ å±¥æ´ã¨ã¢ã©ã¼ã","ì¦ê±° ì´ë ¥ ë° ì림","Historial de evidencias y alertas","Historique des preuves et alertes","Nachweishistorie und Warnungen"], 76 "Best for ongoing growth":["æéåæç»æ¹å","ç¶ç¶æ¹åã«æé©","ì§ì ê°ì ì ìµì ","Ideal para mejora continua","Idéal pour lâamélioration continue","Ideal für laufende Verbesserung"], 77 "Everything in Monitor":["å å« Monitor å ¨é¨åè½","Monitorã®å ¨æ©è½ãå«ã","Monitor ì ì²´ í¬í¨","Todo lo de Monitor","Tout Monitor inclus","Alles aus Monitor"], 78 "Prioritized, approved updates":["ä¼å æ¹åæ¸ å䏿 ¸ååæ´æ°","åªå é ä½ä»ãã»æ¿èªå¾ã®æ´æ°","ì°ì ìì ë° ì¹ì¸ë ì ë°ì´í¸","Actualizaciones priorizadas y aprobadas","Mises à jour priorisées et approuvées","Priorisierte, freigegebene Updates"], 79 "Public re-fetch and rollback proof":["å ¬å¼éæä¸åæ»è¯æ","å ¬éååå¾ã¨ãã¼ã«ããã¯è¨¼æ","ê³µê° ì¬ìì§ ë° ë¡¤ë°± ì¦ëª ","Relectura pública y prueba de reversión","Nouvelle lecture publique et preuve de retour arrière","Ãffentliche Neuladung und Rollback-Nachweis"], 80 "Includes prior layers":["å å«åä¸å±","åæ®µéããã¹ã¦å«ã","ì´ì ë¨ê³ í¬í¨","Incluye las capas anteriores","Inclut les niveaux précédents","Enthält alle vorherigen Stufen"], 81 "Scoped automation and payment readiness":["æèå´çèªå¨åä¸ä»æ¬¾åå¤","ç¯å²éå®ã®èªååã¨æ±ºæ¸æºå","ë²ì ì í ìëí ë° ê²°ì ì¤ë¹","Automatización limitada y preparación de pagos","Automatisation limitée et préparation au paiement","Begrenzte Automatisierung und Zahlungsvorbereitung"], 82 "xAgent ID never receives full card details. Entitlements activate only after a verified Stripe payment notification.":["xAgent ID 䏿¥è§¦å®æ´å¡å·ï¼åªæéªè¯ Stripe 仿¬¾éç¥åæå¯ç¨æå¡ã","xAgent IDã¯å®å ¨ãªã«ã¼ãæ å ±ãåãåãã¾ãããStripeã®æ¯æéç¥ãæ¤è¨¼å¾ã«ã®ã¿å©ç¨æ¨©ãæå¹åãã¾ãã","xAgent IDë ì ì²´ ì¹´ë ì 보를 ë°ì§ ììµëë¤. Stripe ê²°ì ì림 ê²ì¦ íìë§ ê¶íì´ íì
82±íë©ëë¤.","xAgent ID no recibe los datos completos de la tarjeta. El servicio se activa solo tras verificar la notificación de Stripe.","xAgent ID ne reçoit jamais les données complètes de carte. Le service sâactive uniquement après notification Stripe vérifiée.","xAgent ID erhält keine vollständigen Kartendaten. Leistungen werden erst nach geprüfter Stripe-Zahlungsmitteilung aktiviert."], 83 "Machine-readable billing guide":["æºå¨å¯è¯»ä»æ¬¾è§æ ¼","æ©æ¢°å¯èªã®æ¯æä»æ§","ê¸°ê³ íë ê²°ì ìë´","GuÃa de pago legible por máquina","Guide de paiement lisible par machine","Maschinenlesbare Zahlungsanleitung"], 84 "Verify your website identity first":["请å éªè¯ç½ç«èº«ä»½","ã¾ããµã¤ãIDãæ¤è¨¼","먼ì ì¹ì¬ì´í¸ ì ìì ê²ì¦íì¸ì","Verifica primero la identidad del sitio","Vérifiez dâabord lâidentité du site","Website-Identität zuerst verifizieren"], 85 "Checkout is bound to a verified domain and Agent ID to prevent impersonation or purchases for someone else's website.":["仿¬¾ä¼ç»å®å·²éªè¯ç½åä¸ Agent IDï¼é¿å åç¨èº«ä»½ææ¿ä»äººç½ç«è´ä¹°ã","決æ¸ã¯æ¤è¨¼æ¸ã¿ãã¡ã¤ã³ã¨Agent IDã«ç´ã¥ãããªããã¾ããä»ãµã¤ãåãè³¼å ¥ãé²ãã¾ãã","ê²°ì ë ê²ì¦ë ëë©ì¸ê³¼ Agent IDì ì°ê²°ëì´ ì¬ì¹ì´ë í ì¬ì´í¸ 구매를 ë°©ì§í©ëë¤.","El pago se vincula al dominio verificado y Agent ID para evitar suplantaciones o compras para otro sitio.","Le paiement est lié au domaine vérifié et à lâAgent ID pour éviter usurpation ou achat pour un autre site.","Der Checkout wird an verifizierte Domain und Agent ID gebunden, um Identitätsmissbrauch und Käufe für fremde Websites zu verhindern."], 86 "Go to safe installation":["åå¾å®å ¨å®è£ ","å®å ¨ãªå°å ¥ã¸","ìì ì¤ì¹ë¡ ì´ë","Ir a instalación segura","Aller à lâinstallation sécurisée","Zur sicheren Installation"], 87 "Capabilities and permissions are shown before installation. Your website key stays in your system, and xAgent ID never takes control of the site.":["å®è£ åä¼ååºåè½ä¸æéï¼ç½ç«éé¥çå¨ä½ çç³»ç»ï¼xAgent ID 䏿¥ç®¡ç½ç«ã","å°å ¥åã«æ©è½ã¨æ¨©éã表示ãã¾ãããµã¤ãéµã¯èªç¤¾ç°å¢ã«æ®ããxAgent IDããµã¤ããæ¯é ãããã¨ã¯ããã¾ããã","ì¤ì¹ ì ì 기ë¥ê³¼ ê¶íì íìí©ëë¤. ì¹ì¬ì´í¸ í¤ë ê³ ê° ìì¤í ì ë¨ê³ xAgent IDë ì¬ì´í¸ë¥¼ íµì íì§ ììµëë¤.","Las funciones y permisos se muestran antes de instalar. La clave queda en tu sistema y xAgent ID nunca controla el sitio.","Fonctions et autorisations sont affichées avant installation. La clé reste chez vous et xAgent ID ne prend jamais le contrôle du site.","Funktionen und Rechte werden vorab angezeigt. Der Website-Schlüssel bleibt in Ihrem System; xAgent ID übernimmt nie die Kontrolle."], 88 "Website":["ç½ç«","ã¦ã§ããµã¤ã","ì¹ì¬ì´í¸","Sitio web","Site web","Website"], 89 "Set domain":["æå®ç½å","ãã¡ã¤ã³æå®","ëë©ì¸ ì§ì ","Definir dominio","Définir le domaine","Domain festlegen"], 90 "Permissions":["æé","権é","ê¶í","Permisos","Autorisations","Berechtigungen"], 91 "Set boundary":["åå®èå´","ç¯å²è¨å®","ë²ì ì¤ì ","Definir lÃmites","Définir les limites","Grenzen festlegen"], 92 "Install":["å®è£ ","å°å ¥","ì¤ì¹","Instalar","Installer","Installieren"], 93 "Preview first":["å é¢è§","å ã«ãã¬ãã¥ã¼","먼ì 미리보기","Vista previa primero","Prévisualiser dâabord","Zuerst Vorschau"], 94 "Verify":["éªè¯","æ¤è¨¼","ê²ì¦","Verificar","Vérifier","Verifizieren"], 95 "Public proof":["å ¬å¼è¯æ","å ¬é証æ","ê³µê° ì¦ëª ","Prueba pública","Preuve publique","Ãffentlicher Nachweis"], 96 "Dashboard":["æ¥è¡¨","ããã·ã¥ãã¼ã","ëìë³´ë","Panel","Tableau de bord","Dashboard"], 97 "Track results":["追踪ææ","ææè¿½è·¡","ì±ê³¼ ì¶ì ","Seguir resultados","Suivre les résultats","Ergebnisse verfolgen"], 98 "Only one npm package is installed. Modules enable functions; grants define authority.":["åªå®è£ ä¸ä¸ª npm å¥ä»¶ï¼æ¨¡åå³å®åè½ï¼ææå³å®å®
98è½åä»ä¹ã","npmããã±ã¼ã¸ã¯1ã¤ã ãã§ããã¢ã¸ã¥ã¼ã«ãæ©è½ããæ¨©éãå®è¡ç¯å²ã決ãã¾ãã","npm í¨í¤ì§ë íëë§ ì¤ì¹í©ëë¤. 모ëì 기ë¥ì, ê¶íì ì¤í ë²ì를 ì í©ëë¤.","Se instala un solo paquete npm. Los módulos activan funciones y los permisos definen autoridad.","Un seul paquet npm est installé. Les modules activent les fonctions, les autorisations définissent le pouvoir.","Es wird nur ein npm-Paket installiert. Module aktivieren Funktionen, Freigaben bestimmen die Befugnisse."], 99 "The public audit works without installation. History, recommendations, and approved updates require a verified website identity.":["å ¬å¼æ£æµå¯åç¬ä½¿ç¨ï¼ä¿ååå²ãæ¥æ¶å»ºè®®ä¸æ ¸ååæ´æ°éå éªè¯ç½ç«èº«ä»½ã","å ¬éç£æ»ã¯å°å ¥ä¸è¦ã§ããå±¥æ´ãææ¡ãæ¿èªæ¸ã¿æ´æ°ã«ã¯æ¤è¨¼æ¸ã¿ãµã¤ãIDãå¿ è¦ã§ãã","ê³µê° ì§ë¨ì ì¤ì¹ ìì´ ì¬ì©í ì ììµëë¤. ì´ë ¥, ê¶ê³ , ì¹ì¸ë ì ë°ì´í¸ìë ê²ì¦ë ì¹ì¬ì´í¸ ì ìì´ íìí©ëë¤.","La auditorÃa pública funciona sin instalar. Historial, recomendaciones y cambios aprobados requieren identidad web verificada.","Lâaudit public fonctionne sans installation. Historique, recommandations et mises à jour approuvées exigent une identité vérifiée.","Der öffentliche Audit funktioniert ohne Installation. Verlauf, Empfehlungen und freigegebene Updates brauchen eine verifizierte Website-Identität."], 100 "Domain identity and Agent Card":["ç½åèº«ä»½ä¸ Agent Card","ãã¡ã¤ã³IDã¨Agent Card","ëë©ì¸ ì ì ë° Agent Card","Identidad de dominio y Agent Card","Identité de domaine et Agent Card","Domain-Identität und Agent Card"], 101 "AI reads, citations, conversions":["AI 读åãå¼ç¨ä¸è½¬å","AIèªåã»å¼ç¨ã»ã³ã³ãã¼ã¸ã§ã³","AI ì½ê¸°, ì¸ì©, ì í","Lecturas, citas y conversiones de IA","Lectures, citations et conversions IA","KI-Lesezugriffe, Zitate und Conversions"], 102 "Evidence-bound insights":["æè¯æ®çæ¹å建议","証æ ã«åºã¥ãæ¹åææ¡","ì¦ê±° ê¸°ë° ê°ì ì ì","Recomendaciones basadas en evidencia","Recommandations fondées sur des preuves","Evidenzgebundene Empfehlungen"], 103 "Approved AI files only, with rollback":["åªä¿®æ¹æ ¸åç AI 设置æä»¶ï¼å¹¶å¯åæ»","æ¿èªæ¸ã¿AIãã¡ã¤ã«ã®ã¿ããã¼ã«ããã¯å¯è½","ì¹ì¸ë AI íì¼ë§ ìì , 롤백 ì§ì","Solo archivos de IA aprobados, con reversión","Fichiers IA approuvés uniquement, avec retour arrière","Nur freigegebene KI-Dateien, mit Rollback"], 104 "Guided installation":["鿥å®è£ ä¸éªè¯","ã¬ã¤ãä»ãå°å ¥","ë¨ê³ë³ ì¤ì¹","Instalación guiada","Installation guidée","Geführte Installation"], 105 "Customer website":["客æ·ç½ç«","顧客ãµã¤ã","ê³ ê° ì¹ì¬ì´í¸","Sitio del cliente","Site client","Kunden-Website"], 106 "Preview first: inspect environment, files, and permissions without writing":["å é¢è§ï¼åªæ£æ¥ç¯å¢ãæä»¶ä¸æéï¼ä¸åå ¥ç½ç«","å ã«ãã¬ãã¥ã¼ï¼ç°å¢ã»ãã¡ã¤ã«ã»æ¨©éã確èªããæ¸ãè¾¼ã¿ã¾ãã","먼ì 미리보기: íê²½, íì¼, ê¶íì íì¸íë©° ì°ì§ ììµëë¤","Primero previsualiza: revisa entorno, archivos y permisos sin escribir","Prévisualisez dâabord : contrôlez environnement, fichiers et droits sans écrire","Zuerst Vorschau: Umgebung, Dateien und Rechte prüfen, ohne zu schreiben"], 107 "Install after approval: create only the public identity and selected modules":["æ ¸ååå®è£ ï¼åªå»ºç«å ¬å¼èº«ä»½ä¸æé模å","æ¿èªå¾ã«å°å ¥ï¼å ¬éIDã¨é¸æã¢ã¸ã¥ã¼ã«ã®ã¿ä½æ","ì¹ì¸ í ì¤ì¹: ê³µê° ì ìê³¼ ì í 모ëë§ ìì±","Instala tras aprobar: crea solo identidad pública y módulos elegidos","Installez après approbation : créez uniquement lâidentité publique et les modules choisis","Nach Freigabe installieren: nur öffentliche Identität und gewählte Module erstellen"], 108 "Verify domain ownership after deploying the public proof":["é¨ç½²å ¬å¼è¯æåéªè¯ç½åæææ","å ¬é証æã®ãããã¤å¾ã
108«ãã¡ã¤ã³æææ¨©ãæ¤è¨¼","ê³µê° ì¦ëª ë°°í¬ í ëë©ì¸ ìì ê¶ ê²ì¦","Verifica el dominio tras publicar la prueba","Vérifiez le domaine après publication de la preuve","Domain-Eigentum nach Veröffentlichung des Nachweises prüfen"], 109 "After verification, create a one-time Dashboard login URL":["éªè¯æåå产ç䏿¬¡æ§æ¥è¡¨ç»å ¥ç½å","æ¤è¨¼å¾ã«ä½¿ãæ¨ã¦ããã·ã¥ãã¼ãURLã使","ê²ì¦ í ì¼íì© ëìë³´ë ë¡ê·¸ì¸ URL ìì±","Tras verificar, crea una URL de acceso de un solo uso","Après vérification, créez une URL de connexion à usage unique","Nach Verifizierung einmalige Dashboard-Anmelde-URL erstellen"], 110 "Execution boundary":["æ§è¡èå´","å®è¡ç¯å²","ì¤í ë²ì","LÃmite de ejecución","Périmètre dâexécution","Ausführungsgrenze"], 111 "Package version and integrity come from the signed Install Manifest. Only the public identity and selected modules are created. Automation is off by default, and the site private key is never uploaded.":["软件å çæ¬å宿´æ§ç±å·²ç¾åç Install Manifest å³å®ãåªå»ºç«å ¬å¼èº«ä»½ä¸æé模åï¼Automation é»è®¤å ³éï¼ç½ç«ç§é¥ä¸ä¼ä¸ä¼ ã","ããã±ã¼ã¸ã®ãã¼ã¸ã§ã³ã¨æ´åæ§ã¯ç½²åæ¸ã¿ Install Manifest ã§æ±ºã¾ãã¾ããå ¬éIDã¨é¸æã¢ã¸ã¥ã¼ã«ã®ã¿ä½æããAutomation ã¯æ¢å®ã§ç¡å¹ããµã¤ãç§å¯éµã¯éä¿¡ãã¾ããã","í¨í¤ì§ ë²ì ê³¼ 무결ì±ì ìëª ë Install Manifestìì ê²°ì ë©ëë¤. ê³µê° ì ìê³¼ ì í 모ëë§ ìì±íë©° Automationì 기본 ë¹íì±íëê³ ì¬ì´í¸ ê°ì¸ í¤ë ì ë¡ëëì§ ììµëë¤.","La versión y la integridad del paquete proceden del Install Manifest firmado. Solo se crean la identidad pública y los módulos elegidos. Automation está desactivado y la clave privada nunca se sube.","La version et lâintégrité du paquet proviennent de lâInstall Manifest signé. Seuls lâidentité publique et les modules choisis sont créés. Automation est désactivé et la clé privée nâest jamais envoyée.","Paketversion und Integrität stammen aus dem signierten Install Manifest. Es werden nur die öffentliche Identität und gewählte Module erstellt. Automation ist standardmäÃig aus; der private Schlüssel wird nie hochgeladen."], 112 "Roll back an approved bounded change when required":["å¿ è¦æ¶åæ»å·²æ ¸åçæéä¿®æ¹","å¿ è¦ã«å¿ãã¦æ¿èªæ¸ã¿ã®éå®å¤æ´ããã¼ã«ããã¯","íìí ë ì¹ì¸ë ì í ë³ê²½ì 롤백","Revierte un cambio limitado y aprobado cuando sea necesario","Rétablir une modification limitée et approuvée si nécessaire","Eine genehmigte, begrenzte Ãnderung bei Bedarf zurücksetzen"], 113 "Approved, show install command":["å·²ç¡®è®¤ï¼æ¾ç¤ºå®è£ æä»¤","æ¿èªæ¸ã¿ãå°å ¥ã³ãã³ãã表示","ì¹ì¸ ìë£, ì¤ì¹ ëª ë ¹ íì","Aprobado, mostrar comando","Approuvé, afficher la commande","Freigegeben, Installationsbefehl anzeigen"], 114 "Verify current progress":["éªè¯ç®åè¿åº¦","ç¾å¨ã®é²æãæ¤è¨¼","íì¬ ì§í ìí© ê²ì¦","Verificar progreso","Vérifier la progression","Aktuellen Fortschritt prüfen"], 115 "Identity created":["身份已建ç«","ID使æ¸ã¿","ì ì ìì±ë¨","Identidad creada","Identité créée","Identität erstellt"], 116 "Public proof deployed":["å ¬å¼è¯æå·²é¨ç½²","å ¬é証æãé ä¿¡æ¸ã¿","ê³µê° ì¦ëª ë°°í¬ë¨","Prueba pública publicada","Preuve publique publiée","Ãffentlicher Nachweis bereitgestellt"], 117 "Domain verified":["ç½åå·²éªè¯","ãã¡ã¤ã³æ¤è¨¼æ¸ã¿","ëë©ì¸ ê²ì¦ë¨","Dominio verificado","Domaine vérifié","Domain verifiziert"], 118 "SDK connected":["SDK å·²è¿æ¥","SDKæ¥ç¶æ¸ã¿","SDK ì°ê²°ë¨","SDK conectado","SDK connecté","SDK verbunden"], 119 "The website is the account. After domain verification, the customer-held key creates a one-time Dashboard login URL. The mother service never stores the customer's private key. Automation is off by default and remains restricted to locally approved AI files.":["ç½ç«å°±æ¯è´¦å·ãç½åéªè¯åï¼ç±å®¢æ·èªæéé¥äº§ç䏿¬¡æ§æ¥è¡¨ç»å ¥ç½åï¼æ¯ä½ä¸ä¿åç§é¥ï¼Automation é»è®¤å ³éä¸ä» 鿬尿 ¸åç AI æä»¶ã","ãµã¤ãèªä½ãã¢ã«ã¦ã³ãã§ãããã¡ã¤ã³æ¤è¨¼å¾ãèªç¤¾ä¿æéµã§ä¸æãã°ã¤ã³URLã使ãã¾ããæ¯ä½ã¯ç§å¯éµãä¿åãããAutomationã¯æ¢å®ã§ç¡å¹ãã¤ãã¼ã«ã«æ¿èªæ¸ã¿AIãã¡ã¤ã«ã«éå
119®ããã¾ãã","ì¹ì¬ì´í¸ê° ê³ì ì ëë¤. ëë©ì¸ ê²ì¦ í ê³ ê° ë³´ì í¤ë¡ ì¼íì© ëìë³´ë URLì ìì±í©ëë¤. 모체ë ê°ì¸ í¤ë¥¼ ì ì¥íì§ ìì¼ë©° Automationì 기본 ë¹íì±íëê³ ë¡ì»¬ ì¹ì¸ AI íì¼ë¡ ì íë©ëë¤.","El sitio es la cuenta. Tras verificar el dominio, la clave del cliente crea una URL de acceso única. El servicio no guarda la clave privada; Automation está apagado y limitado a archivos de IA aprobados localmente.","Le site est le compte. Après vérification, la clé détenue par le client crée une URL unique. Le service ne conserve pas la clé privée ; Automation reste désactivé et limité aux fichiers IA approuvés localement.","Die Website ist das Konto. Nach Domain-Prüfung erstellt der kundeneigene Schlüssel eine einmalige Dashboard-URL. Der Dienst speichert keinen privaten Schlüssel; Automation ist aus und auf lokal freigegebene KI-Dateien beschränkt."], 120 "Quick public audit":["å¿«éå ¬å¼æ£æµ","å ¬éã¯ã¤ãã¯ç£æ»","ë¹ ë¥¸ ê³µê° ì§ë¨","AuditorÃa pública rápida","Audit public rapide","Schneller öffentlicher Audit"], 121 "Refetching public website signalsâ¦":["æ£å¨éæ°è¯»åç½ç«å ¬å¼ä¿¡å·â¦","å ¬éãµã¤ãã·ã°ãã«ãååå¾ä¸â¦","ê³µê° ì¹ì¬ì´í¸ ì í¸ë¥¼ ë¤ì ìì§ ì¤â¦","Releyendo señales públicas del sitioâ¦","Nouvelle lecture des signaux publicsâ¦","Ãffentliche Website-Signale werden neu geladenâ¦"], 122 "View verified findings and evidence":["æ¥çç宿£æ¥è¯æ®ä¸æ¹å项ç®","æ¤è¨¼æ¸ã¿ã®æè¦ã¨è¨¼æ ãè¦ã","ê²ì¦ë ê²°ê³¼ì ì¦ê±° 보기","Ver hallazgos y evidencias verificadas","Voir les constats et preuves vérifiés","Geprüfte Befunde und Nachweise ansehen"], 123 "See the difference after every improvement.":["æ¯æ¬¡æ¹åï¼é½çå¾å°ååå·®å¼ã","æ¹åãã¨ã®å·®ã確èªã§ãã¾ãã","ê°ì ì í ì°¨ì´ë¥¼ íì¸íì¸ì.","Comprueba la diferencia tras cada mejora.","Voyez la différence après chaque amélioration.","Sehen Sie den Unterschied nach jeder Verbesserung."], 124 "The dashboard brings together readiness scores, findings, improvements, AI reads, citations, and conversions without substituting missing evidence.":["æ¥è¡¨æ´ååå¤åº¦åæ°ãé®é¢ãæ¹å纪å½ãAI 读åãå¼ç¨ä¸è½¬åï¼ç¼ºå°çè¯æ®ä¸ä¼è¢«æ¨¡ææ°åå代ã","ããã·ã¥ãã¼ãã¯æºå度ãæè¦ãæ¹åãAIèªåãå¼ç¨ãã³ã³ãã¼ã¸ã§ã³ãçµ±åããæ¬ æè¨¼æ ãæ¨¡æ¬å¤ã§è£ãã¾ããã","ëìë³´ëë ì¤ë¹ë ì ì, 문ì , ê°ì , AI ì½ê¸°, ì¸ì©, ì íì íµí©íë©° ëë½ë ì¦ê±°ë¥¼ ê°ì ìì¹ë¡ ëì²´íì§ ììµëë¤.","El panel reúne preparación, hallazgos, mejoras, lecturas, citas y conversiones sin sustituir evidencia ausente.","Le tableau réunit préparation, constats, améliorations, lectures, citations et conversions sans remplacer les preuves absentes.","Das Dashboard bündelt Reifegrad, Befunde, Verbesserungen, KI-Lesezugriffe, Zitate und Conversions, ohne fehlende Nachweise zu ersetzen."], 125 "A verified website Agent identity for safe discovery, answers, recommendations, and operations.":["让ç½ç«æ¥æå¯éªè¯ç Agent 身份ï¼å®å ¨å°è¢«æç´¢ãåçãæ¨è䏿ä½ã","å®å ¨ã«çºè¦ã»åçã»æ¨è¦ã»æä½ãããããã®æ¤è¨¼å¯è½ãªWebsite Agent IDã","ìì í ê²ì, ëµë³, ì¶ì², ì´ìì ìí ê²ì¦ ê°ë¥í ì¹ì¬ì´í¸ Agent ì ì.","Una identidad Agent web verificable para ser encontrado, respondido, recomendado y operado con seguridad.","Une identité Agent de site vérifiable pour une découverte, des réponses, recommandations et opérations sûres.","Eine verifizierbare Website-Agent-Identität für sichere Auffindbarkeit, Antworten, Empfehlungen und Aktionen."], 126 "Capabilities":["åè½","æ©è½","기ë¥","Funciones","Fonctions","Funktionen"], 127 "Results demo":["æææ¥è¡¨ç¤ºè","ææãã¢","ì±ê³¼ ë°ëª¨","Demo de resultados","Démo des résultats","Ergebnisdemo"], 128 "Patents":["æºæ §è´¢äº§","ç¥ç財ç£","ì§ìì¬ì°","Propiedad intelectual","Propriété intellectuelle","Geistiges Eigentum"],
129 "For Agents":["ç» Agent","Agentåã","Agentì©","Para Agents","Pour les Agents","Für Agents"], 130 "Billing guide":["仿¬¾è§æ ¼","æ¯æä»æ§","ê²°ì ìë´","GuÃa de pago","Guide de paiement","Zahlungsanleitung"], 131 "Company":["æå¡æ¡æ¬¾","éå¶æ å ±","ìë¹ì¤ ì ë³´","Empresa","Entreprise","Unternehmen"], 132 "Secure payment via Stripe Checkout":["仿¬¾ç± Stripe Checkout å®å ¨å¤ç","Stripe Checkoutã«ããå®å ¨ãªæ±ºæ¸","Stripe Checkout ìì ê²°ì ","Pago seguro con Stripe Checkout","Paiement sécurisé via Stripe Checkout","Sichere Zahlung über Stripe Checkout"], 133 "Copy and run the first installation command.":["å¤å¶å¹¶æ§è¡ç¬¬ä¸ä¸ªå®è£ æä»¤ã","æåã®å°å ¥ã³ãã³ããã³ãã¼ãã¦å®è¡ãã¦ãã ããã","첫 ë²ì§¸ ì¤ì¹ ëª ë ¹ì ë³µì¬í´ ì¤ííì¸ì.","Copia y ejecuta el primer comando de instalación.","Copiez et exécutez la première commande dâinstallation.","Kopieren Sie den ersten Installationsbefehl und führen Sie ihn aus."], 134 "Deploy the public identity proof generated by the installer.":["é¨ç½²å®è£ ç¨åºäº§ççå ¬å¼èº«ä»½è¯æã","ã¤ã³ã¹ãã¼ã©ã¼ãçæããå ¬éID証æãé ä¿¡ãã¦ãã ããã","ì¤ì¹ íë¡ê·¸ë¨ì´ ë§ë ê³µê° ì ì ì¦ëª ì ë°°í¬íì¸ì.","Publica la prueba de identidad generada por el instalador.","Publiez la preuve dâidentité générée par lâinstallateur.","Veröffentlichen Sie den vom Installer erzeugten Identitätsnachweis."], 135 "Public proof found. Run the second command to complete domain verification.":["å·²æ¾å°å ¬å¼è¯æï¼è¯·æ§è¡ç¬¬äºä¸ªæä»¤å®æç½åéªè¯ã","å ¬é証æãè¦ã¤ããã¾ããã2çªç®ã®ã³ãã³ãã§ãã¡ã¤ã³æ¤è¨¼ãå®äºãã¦ãã ããã","ê³µê° ì¦ëª ì ì°¾ììµëë¤. ë ë²ì§¸ ëª ë ¹ì¼ë¡ ëë©ì¸ ê²ì¦ì ìë£íì¸ì.","Se encontró la prueba pública. Ejecuta el segundo comando para verificar el dominio.","Preuve publique trouvée. Exécutez la deuxième commande pour vérifier le domaine.","Ãffentlicher Nachweis gefunden. Führen Sie den zweiten Befehl zur Domain-Prüfung aus."], 136 "Domain verified. Run the third command to open the Dashboard.":["ç½åå·²éªè¯ï¼è¯·æ§è¡ç¬¬ä¸ä¸ªæä»¤æå¼æ¥è¡¨ã","ãã¡ã¤ã³æ¤è¨¼æ¸ã¿ã§ãã3çªç®ã®ã³ãã³ãã§ããã·ã¥ãã¼ããéãã¦ãã ããã","ëë©ì¸ì´ ê²ì¦ëììµëë¤. ì¸ ë²ì§¸ ëª ë ¹ì¼ë¡ ëìë³´ë를 ì¬ì¸ì.","Dominio verificado. Ejecuta el tercer comando para abrir el panel.","Domaine vérifié. Exécutez la troisième commande pour ouvrir le tableau de bord.","Domain verifiziert. Ãffnen Sie das Dashboard mit dem dritten Befehl."], 137 "SDK connected. Open the Dashboard to review inspections and insights.":["SDK å·²è¿æ¥ï¼è¯·è¿å ¥æ¥è¡¨æ¥çæ£æµä¸æ¹å建议ã","SDKæ¥ç¶æ¸ã¿ã§ããããã·ã¥ãã¼ãã§ç£æ»ã¨æ¹åææ¡ã確èªãã¦ãã ããã","SDKê° ì°ê²°ëììµëë¤. ëìë³´ëìì ì§ë¨ê³¼ ê°ì ì ìì íì¸íì¸ì.","SDK conectado. Abre el panel para revisar auditorÃas y recomendaciones.","SDK connecté. Ouvrez le tableau pour consulter audits et recommandations.","SDK verbunden. Ãffnen Sie das Dashboard für Prüfungen und Empfehlungen."], 138 "Checking public proofâ¦":["æ£å¨æ£æ¥å ¬å¼è¯æâ¦","å ¬é証æã確èªä¸â¦","ê³µê° ì¦ëª íì¸ ì¤â¦","Comprobando la prueba públicaâ¦","Vérification de la preuve publiqueâ¦","Ãffentlicher Nachweis wird geprüftâ¦"], 139 "Not installed":["å°æªå®è£ ","æªå°å ¥","ì¤ì¹ëì§ ìì","No instalado","Non installé","Nicht installiert"], 140 "Waiting for public proof":["çå¾ å ¬å¼è¯æ","å ¬é証æãå¾ æ©ä¸","ê³µê° ì¦ëª ë기 ì¤","Esperando la prueba pública","En attente de la preuve publique","Warten auf öffentlichen Nachweis"], 141 "Public proof found":["å·²æ¾å°å ¬å¼è¯æ","å ¬é証æã確èª","ê³µê° ì¦ëª íì¸ë¨","Prueba pública encontrada","Preuve publique trouvée","Ãffentlicher Nachweis gefunden"], 142 "Installation ready":["å®è£ æ¹æ¡å·²åå¤","å°å ¥æºåå®äº","ì¤ì¹ ì¤ë¹ ìë£","Instalación preparada","Installation prête","Installation bereit"],
143 "Approved. Run the displayed install command, then deploy the generated public proof.":["å·²å®ææ ¸åã请æ§è¡æ¾ç¤ºçå®è£ æä»¤ï¼åé¨ç½²äº§ççå ¬å¼è¯æã","æ¿èªæ¸ã¿ã§ãã表示ãããå°å ¥ã³ãã³ããå®è¡ããçæãããå ¬é証æãé ä¿¡ãã¦ãã ããã","ì¹ì¸ëììµëë¤. íìë ì¤ì¹ ëª ë ¹ì ì¤ííê³ ìì±ë ê³µê° ì¦ëª ì ë°°í¬íì¸ì.","Aprobado. Ejecuta el comando mostrado y publica la prueba generada.","Approuvé. Exécutez la commande affichée puis publiez la preuve générée.","Freigegeben. Führen Sie den angezeigten Befehl aus und veröffentlichen Sie den erzeugten Nachweis."], 144 "Refetching /.well-known/agent-id.jsonâ¦":["æ£å¨éæ°è¯»å /.well-known/agent-id.jsonâ¦","/.well-known/agent-id.json ãååå¾ä¸â¦","/.well-known/agent-id.json ë¤ì ì½ë ì¤â¦","Releyendo /.well-known/agent-id.jsonâ¦","Nouvelle lecture de /.well-known/agent-id.jsonâ¦","/.well-known/agent-id.json wird neu geladenâ¦"], 145 "No pending findings.":["ç®å没æå¾ æ¹å项ç®ã","æªå¯¾å¿ã
145®æ¹åé ç®ã¯ããã¾ããã","ë기 ì¤ì¸ ê°ì íëª©ì´ ììµëë¤.","No hay hallazgos pendientes.","Aucun point en attente.","Keine offenen Befunde."], 146 "Audit failed":["æ£æµå¤±è´¥","ç£æ»ã«å¤±æ","ì§ë¨ ì¤í¨","Falló la auditorÃa","Ãchec de lâaudit","Audit fehlgeschlagen"], 147 "Connected":["å·²æ¥å ¥","æ¥ç¶æ¸ã¿","ì°ê²°ë¨","Conectado","Connecté","Verbunden"], 148 "Pending":["å¾ æ¥å ¥","ä¿ç","ë기","Pendiente","En attente","Ausstehend"], 149 "observed records":["ç¬çå®è¯æ®","ä»¶ã®è¦³æ¸¬è¨é²","ê±´ì ê´ì¸¡ 기ë¡","registros observados","enregistrements observés","beobachtete Datensätze"], 150 "No observed evidence yet; not counted as zero":["å°æªåå¾çå®è¯æ®ï¼ä¸è®¡ä¸º 0","観測証æ ã¯æªåå¾ã§ãã¼ãã¨ã¯æ±ãã¾ãã","ê´ì¸¡ ì¦ê±°ê° ìì§ ìì¼ë©° 0ì¼ë¡ ì§ê³íì§ ììµëë¤","Aún no hay evidencia observada; no se cuenta como cero","Aucune preuve observée ; ce nâest pas compté comme zéro","Noch kein beobachteter Nachweis; wird nicht als null gezählt"], 151 "pass":["éè¿","åæ ¼","íµê³¼","aprobado","réussi","bestanden"], 152 "partial":["é¨å","ä¸é¨","ë¶ë¶","parcial","partiel","teilweise"], 153 "pending":["å¾ ç¡®è®¤","ä¿ç","ë기","pendiente","en attente","ausstehend"], 154 }; 155 const expandedLocaleOrder = ["zh-Hans","ja","ko","es","fr","de"]; 156 Object.entries(EXPANDED_LOCALE_COPY).forEach(([source, translations]) => { 157 expandedLocaleOrder.forEach((locale, index) => { AGENT_ID_LOCALES[locale].copy[source] = translations[index]; }); 158 }); 159 const sharedCatalog = window.XAgentI18nCatalog; 160 const supported = new Set(Object.keys(AGENT_ID_LOCALES)); 161 let dashboardData = null; 162 let dashboardConnectors = null; 163 let dashboardTasks = null; 164 let dashboardBilling = null; 165 let dashboardDecision = null; 166 const normalizeLocale = (raw) => { 167 const value = String(raw || "").toLowerCase(); 168 if (value.startsWith("zh-tw") || value.startsWith("zh-hk") || value.startsWith("zh-mo") || value === "zh-hant") return "zh-Hant"; 169 if (value.startsWith("zh")) return "zh-Hans"; 170 return ["ja","ko","es","fr","de","en"].find((locale) => value.startsWith(locale)) || "en"; 171 }; 172 const storedLanguage = (() => { try { return localStorage.getItem("agent-id-lang"); } catch (_) { return ""; } })(); 173 const requestedLanguage = new URLSearchParams(location.search).get("lang"); 174 const preferred = requestedLanguage || storedLanguage || (navigator.languages || [navigator.language]).map(normalizeLocale).find((locale) => supported.has(locale)) || "en"; 175 const localized = (source, traditionalChinese="") => { 176 const language = document.body.dataset.lang || preferred; 177 const canonical = sharedCatalog?.aliases?.[source] || sharedCatalog?.aliases?.[traditionalChinese] || source; 178 const shared = sharedCatalog?.messages?.[canonical]?.[language]; 179 if (shared) return shared; 180 if (language === "zh-Hant") return traditionalChinese || source; 181 if (language === "en") return source; 182 return AGENT_ID_LOCALES[language]?.copy[source] || source; 183 }; 184 const translatableNodes = [...document.querySelectorAll(".lang-en")]; 185 translatableNodes.forEach((node) => { node.dataset.i18nSource = node.textContent.replace(/\s+/g," ").trim(); node.dataset.i18nHtml = node.innerHTML; }); 186 const setLanguage = (language) => { 187 const lang = supported.has(language) ? language : normalizeLocale(language); 188 document.body.dataset.lang = lang; 189 document.documentElement.lang = lang; 190 const locale = AGENT_ID_LOCALES[lang] || AGENT_ID_LOCALES.en; 191 translatableNodes.forEach((node) => { 192 const translated = window.XAgentI18nCatalog?.messages?.[node.dataset.i18nSource]?.[lang] || locale.copy[node.dataset.i18nSource]; 193 if (lang === "en" || !translated) node.innerHTML = node.dataset.i18nHtml; 194 else node.textContent = translated; 195 }); 196 const select = document.querySelector("[data-language-select]"); if (select) select.value = lang; 197 const patent = document.querySelector("[data-patent-label]"); if (patent) patent.textContent = locale.patent; 198 try { localStorage.setItem("agent-id-lang", lang); } catch (_) {} 199 if (dashboardData) render(dashboardData); 200 if (dashboardConnectors) renderConnectors(dashboardConnectors); 201 if (dashboardTasks) renderTasks(dashboardTasks); 202 if (dashboardBilling) renderBilling(dashboardBilling); 203 if (dashboardDecision) renderDecision(dashboardDecision); 204 }; 205 document.querySelector("[data-language-select]")?.addEventListener("change", (event) => setLanguage(event.target.value)); 206 setLanguage(preferred); 207 208 const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; 209 const agentWord = document.querySelector("[data-agent-word]"); 210 if (!reducedMotion && agentWord) { 211 const agentTypes = ["AI agent", "enterprise agent", "buyer agent", "search agent", "service agent"]; 212 let agentIndex = 0; 213 window.setInterval(() => { 214 agentWord.classList.add("is-changing"); 215 window.setTimeout(() => { 216 agentIndex = (agentIndex + 1) % agentTypes.length; 217 agentWord.textContent = agentTypes[agentIndex]; 218 agentWord.classList.remove("is-changing"); 219 }, 180); 220 }, 1900); 221 } 222 const runtimeLines = [...document.querySelectorAll(".agent-runtime .runtime-line")]; 223 if (!reducedMotion && runtimeLines.length) { 224 let runtimeStep = 0; 225 window.setInterval(() => { 226 runtimeStep = (runtimeStep + 1) % runtimeLines.length;
227 runtimeLines.forEach((line, index) => line.classList.toggle("is-active", index === runtimeStep)); 228 }, 1650); 229 } 230 if (!reducedMotion && "IntersectionObserver" in window) { 231 const revealTargets = document.querySelectorAll("main > section:not(.hero), .wide-panel"); 232 revealTargets.forEach((node) => node.setAttribute("data-reveal", "")); 233 document.documentElement.classList.add("motion-ready"); 234 const revealObserver = new IntersectionObserver((entries) => { 235 entries.forEach((entry) => { 236 if (!entry.isIntersecting) return; 237 entry.target.classList.add("is-visible"); 238 revealObserver.unobserve(entry.target); 239 }); 240 }, {rootMargin:"0px 0px -10%",threshold:.08}); 241 revealTargets.forEach((node) => revealObserver.observe(node)); 242 } 243 244 const installInput = document.querySelector("[data-install-url]"); 245 // Shown in place of a real address until the visitor supplies one. It must not 246 // be a resolvable URL: these commands are rendered into a copyable console, and 247 // an agent reading the page does not pause to notice that "example.com" was 248 // meant as an illustration -- it is a valid target, so it gets installed to. 249 // The angle brackets make a shell reject the line immediately, which is the 250 // failure we want: loud, at the moment of the mistake, instead of a silent 251 // install against somebody else's domain. 252 const URL_PLACEHOLDER = "<YOUR-WEBSITE>"; 253 const normalizeUrl = (raw) => { 254 try { 255 const value = /^https?:\/\//i.test(raw) ? raw : `https://${raw}`; 256 const parsed = new URL(value); 257 return parsed.protocol === "https:" ? `https://${parsed.hostname}` : URL_PLACEHOLDER; 258 } catch (_) { return URL_PLACEHOLDER; } 259 }; 260 const escapeHtml = (value) => String(value ?? "").replace(/[&<>'"]/g, (char) => ({"&":"&","<":"<",">":">","'":"'",'"':"""})[char]); 261 let installManifestRequest = 0; 262 const refreshCommands = async () => { 263 if (!installInput) return false; 264 const url = normalizeUrl(installInput.value.trim()); 265 document.querySelectorAll("[data-command-template]").forEach((node) => { 266 node.textContent = node.dataset.commandTemplate.replaceAll("{url}", url); 267 }); 268 const selected = [...document.querySelectorAll("[data-install-module]:checked")]; 269 const modules = selected.map((node) => node.dataset.installModule); 270 const grants = selected.map((node) => node.dataset.permission); 271 const guide = document.querySelector("[data-install-guide-link]"); 272 if (guide) guide.href = `/api/site_agents/install_guide?url=${encodeURIComponent(url)}`; 273 const manifestLink = document.querySelector("[data-install-manifest-link]"); 274 const manifestUrl = `/.well-known/xagent-install.json?url=${encodeURIComponent(url)}`; 275 if (manifestLink) manifestLink.href = manifestUrl; 276 const requestId = ++installManifestRequest; 277 const commandNodes = ["cmd-dry-run", "cmd-bundle", "cmd-activate", "cmd-dashboard", "cmd-rollback"] 278 .map((id) => document.getElementById(id)).filter(Boolean); 279 const releaseNode = document.querySelector("[data-release-version]"); 280 commandNodes.forEach((node) => { node.textContent = "Loading signed Install Manifest"; }); 281 document.querySelectorAll("[data-copy-target^='cmd-']").forEach((button) => { button.disabled = true; }); 282 try { 283 const response = await fetch(manifestUrl, {headers:{Accept:"application/json"}, cache:"no-store"}); 284 const manifest = await response.json(); 285 if (requestId !== installManifestRequest) return false; 286 const release = manifest.release || {}; 287 if (!response.ok || release.ready !== true || !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(release.version || "") || !/^sha512-[A-Za-z0-9+/=]{80,120}$/.test(release.integrity || "")) { 288 throw new Error("release_not_ready"); 289 } 290 const workflow = Object.fromEntries((manifest.workflow || []).map((step) => [step.id, step])); 291 for (const id of ["dry_run", "apply", "domain_verify", "dashboard_login", "rollback"]) { 292 if (!workflow[id]?.command || workflow[id].command === "release_not_ready") throw new Error("release_not_ready"); 293 } 294 const declaredModules = new Map((manifest.modules || []).map((item) => [item.id, item])); 295 if (modules.some((id, index) => declaredModules.get(id)?.grant !== grants[index])) { 296 throw new Error("manifest_contract_mismatch"); 297 } 298 const packageRef = `@twin3-ai/agent-id@${release.version}`; 299 const installBase = `npx --yes ${packageRef} install ${url} --modules ${modules.join(",")} --grant ${grants.join(",")} --project-root . --public-root ./public`; 300 document.getElementById("cmd-dry-run").textContent = `${installBase} --dry-run`; 301 document.getElementById("cmd-bundle").textContent = `${installBase} --apply`; 302 document.getElementById("cmd-activate").textContent = workflow.domain_verify.command; 303 document.getElementById("cmd-dashboard").textContent = workflow.dashboard_login.command; 304 document.getElementById("cmd-rollback").textContent = workflow.rollback.command;
305 if (releaseNode) releaseNode.textContent = `npm ${release.version} · ${release.integrity.slice(0, 20)}â¦`; 306 document.querySelectorAll("[data-copy-target^='cmd-']").forEach((button) => { button.disabled = false; }); 307 return true; 308 } catch (_) { 309 if (requestId !== installManifestRequest) return false; 310 commandNodes.forEach((node) => { node.textContent = "release_not_ready"; }); 311 if (releaseNode) releaseNode.textContent = "Release unavailable"; 312 return false; 313 } 314 }; 315 let installRefreshTimer; 316 installInput?.addEventListener("input", () => { 317 window.clearTimeout(installRefreshTimer); 318 installRefreshTimer = window.setTimeout(refreshCommands, 250); 319 }); 320 const moduleInputs = [...document.querySelectorAll("[data-install-module]")]; 321 const moduleById = new Map(moduleInputs.map((node) => [node.dataset.installModule,node])); 322 const enforceModuleDependencies = (changed) => { 323 if (changed?.checked) { 324 String(changed.dataset.requires || "").split(",").filter(Boolean).forEach((required) => { 325 const input = moduleById.get(required); if (input) input.checked = true; 326 }); 327 } else if (changed) { 328 moduleInputs.filter((input) => String(input.dataset.requires || "").split(",").includes(changed.dataset.installModule)).forEach((dependent) => { 329 dependent.checked = false; 330 enforceModuleDependencies(dependent); 331 }); 332 } 333 moduleInputs.forEach((input) => input.closest("label")?.classList.toggle("selected", input.checked)); 334 refreshCommands(); 335 }; 336 moduleInputs.forEach((node) => node.addEventListener("change", () => enforceModuleDependencies(node))); 337 enforceModuleDependencies(); 338 refreshCommands(); 339 340 const checkoutNotice = document.querySelector("[data-checkout-notice]"); 341 document.querySelectorAll("[data-checkout-plan]").forEach((button) => button.addEventListener("click", () => { 342 if (!checkoutNotice) return; 343 checkoutNotice.hidden = false; 344 checkoutNotice.dataset.plan = button.dataset.checkoutPlan; 345 checkoutNotice.scrollIntoView({behavior:reducedMotion ? "auto" : "smooth",block:"center"}); 346 })); 347 348 const wizardSteps = [...document.querySelectorAll("[data-wizard-step]")]; 349 const commandList = document.querySelector("[data-command-list]"); 350 const approveInstall = document.querySelector("[data-approve-install]"); 351 const showCommandPhase = (phase, allowApproval=false) => { 352 if (commandList) commandList.hidden = !phase; 353 document.querySelectorAll("[data-command-phase]").forEach((node) => { node.hidden = node.dataset.commandPhase !== phase; }); 354 if (approveInstall) approveInstall.hidden = !allowApproval; 355 }; 356 const setWizardProgress = (active, completed=[]) => { 357 wizardSteps.forEach((node) => { 358 node.classList.toggle("active", node.dataset.wizardStep === active); 359 node.classList.toggle("done", completed.includes(node.dataset.wizardStep)); 360 if (node.dataset.wizardStep === active) node.setAttribute("aria-current", "step"); 361 else node.removeAttribute("aria-current"); 362 }); 363 }; 364 const installStatusCopy = { 365 run_install_command:{zh:"è«è¤è£½ä¸¦å·è¡ç¬¬ä¸åå®è£æä»¤ã",en:"Copy and run the first installation command."}, 366 deploy_public_proof:{zh:"è«é¨ç½²å®è£ç¨å¼ç¢ççå ¬é身åèæã",en:"Deploy the public identity proof generated by the installer."}, 367 run_enterprise_domain_verify:{zh:"å ¬éèæå·²æ¾å°ãè«å·è¡ç¬¬äºåæä»¤å®æç¶²åé©èã",en:"Public proof found. Run the second command to complete domain verification."}, 368 run_dashboard_login_or_enable_managed_inspection:{zh:"ç¶²åå·²é©èãå¯å·è¡ç¬¬ä¸åæä»¤ç»å ¥ Dashboardã",en:"Domain verified. Run the third command to open the Dashboard."}, 369 review_dashboard_and_insights:{zh:"SDK å·²é£ç·ãè«é²å ¥ Dashboard æ¥çå·¡æª¢èæ¹å建è°ã",en:"SDK connected. Open the Dashboard to review inspections and insights."}, 370 }; 371 const renderInstallStatus = (data) => { 372 const panel = document.querySelector("[data-install-status]"); 373 if (!panel) return; 374 panel.removeAttribute("hidden"); 375 const titles = {checking:{zh:"æ£å¨æª¢æ¥å ¬éèæâ¦",en:"Checking public proofâ¦"},not_installed:{zh:"å°æªå®è£",en:"Not installed"},pending_verification:{zh:"çå¾ å ¬éèæ",en:"Waiting for public proof"},proof_published:{zh:"å ¬éèæå·²æ¾å°",en:"Public proof found"},verified_domain:{zh:"ç¶²åå·²é©è",en:"Domain verified"},sdk_active:{zh:"SDK å·²é£ç·",en:"SDK connected"},command_ready:{zh:"å®è£æ¹æ¡å·²æºå",en:"Installation ready"}}; 376 const titleCopy = titles[data.status] || {zh:data.status,en:data.status}; 377 const title = panel.querySelector("[data-install-status-title]"); if (title) title.textContent = localized(titleCopy.en,titleCopy.zh); 378 const host = panel.querySelector("[data-install-status-host]"); if (host) host.textContent = data.host || normalizeUrl(installInput?.value || "").replace("https://","");
379 Object.entries(data.steps || {}).forEach(([key,value]) => panel.querySelector(`[data-status-key="${key}"]`)?.classList.toggle("done", Boolean(value))); 380 const next = panel.querySelector("[data-install-next]"); 381 const copy = installStatusCopy[data.next_action] || {zh:data.next_action || "",en:data.next_action || ""}; 382 if (next) next.textContent = localized(copy.en,copy.zh); 383 if (data.status === "sdk_active") { setWizardProgress("dashboard",["website","permissions","install","verify","dashboard"]); showCommandPhase("dashboard"); } 384 else if (data.status === "verified_domain") { setWizardProgress("dashboard",["website","permissions","install","verify"]); showCommandPhase("dashboard"); } 385 else if (data.status === "proof_published") { setWizardProgress("verify",["website","permissions","install"]); showCommandPhase("verify"); } 386 else if (data.status === "pending_verification") { setWizardProgress("install",["website","permissions"]); showCommandPhase("apply"); } 387 else if (data.status === "command_ready" || data.status === "not_installed") { setWizardProgress("install",["website","permissions"]); showCommandPhase("dry-run",true); } 388 }; 389 document.querySelector("[data-prepare-install]")?.addEventListener("click", async () => { 390 if (!await refreshCommands()) { 391 renderInstallStatus({status:"release_not_ready",host:normalizeUrl(installInput?.value || "").replace("https://","") ,steps:{},next_action:"release_not_ready"}); 392 return; 393 } 394 const host = normalizeUrl(installInput?.value || "").replace("https://",""); 395 renderInstallStatus({status:"command_ready",host,steps:{identity_created:false,public_proof:false,domain_verified:false,sdk_active:false},next_action:"run_install_command"}); 396 setWizardProgress("install",["website","permissions"]); 397 }); 398 approveInstall?.addEventListener("click", () => { 399 showCommandPhase("apply"); 400 setWizardProgress("install",["website","permissions"]); 401 const next = document.querySelector("[data-install-next]"); 402 if (next) next.textContent = localized("Approved. Run the displayed install command, then deploy the generated public proof.","å·²å®ææ ¸åãè«å·è¡ç®å顯示çå®è£æä»¤ï¼åé¨ç½²ç¢ççå ¬éèæã"); 403 }); 404 document.querySelector("[data-check-install]")?.addEventListener("click", async () => { 405 const button = document.querySelector("[data-check-install]"); 406 button.disabled = true; 407 renderInstallStatus({status:"checking",host:normalizeUrl(installInput?.value || "").replace("https://",""),steps:{},next_action:"checking"}); 408 const checkingCopy = localized("Refetching /.well-known/agent-id.jsonâ¦","æ£å¨éæ°è®å /.well-known/agent-id.jsonâ¦"); 409 const checkingNode = document.querySelector("[data-install-next]"); if (checkingNode) checkingNode.textContent = checkingCopy; 410 try { 411 const url = normalizeUrl(installInput?.value || ""); 412 const response = await fetch(`/api/site_agents/install_status?url=${encodeURIComponent(url)}`,{headers:{Accept:"application/json"}}); 413 const data = await response.json(); 414 if (!response.ok || !data.ok) throw new Error(data.error || `HTTP ${response.status}`); 415 renderInstallStatus(data); 416 } catch (error) { 417 renderInstallStatus({status:"not_installed",host:"",steps:{},next_action:"run_install_command"}); 418 const next = document.querySelector("[data-install-next]"); if (next) next.textContent = error.message; 419 } finally { button.disabled = false; } 420 }); 421 422 const measurementFailed = (value) => value?.measurement?.status === "measurement_failed"; 423 const measurementFailureCopy = (value) => { 424 const reason = value?.measurement?.reason || localized("The primary public page could not be measured.", "ç¡æ³éæ¸¬å ¬éé¦é ã"); 425 return localized( 426 `We could not measure this website yet. No readiness score was calculated. ${reason}`, 427 `ç®åç¡æ³é測éåç¶²ç«ï¼æªè¨ç®æºååº¦åæ¸ã${reason}`, 428 ); 429 }; 430 const query = new URLSearchParams(window.location.search); 431 const auditTarget = query.get("url"); 432 const auditResults = document.getElementById("audit-results"); 433 if (auditTarget && auditResults) { 434 auditResults.hidden = false;
435 fetch(`/api/agent/audit_report?url=${encodeURIComponent(auditTarget)}`, {headers:{Accept:"application/json"}}) 436 .then(async (response) => { const data = await response.json(); if (!response.ok || !data.ok) throw new Error(data.error || `HTTP ${response.status}`); return data; }) 437 .then((data) => { 438 const report = data.report || data; 439 const scoreNode = document.querySelector("[data-quick-score]"); 440 const summaryNode = document.querySelector("[data-quick-summary]"); 441 if (measurementFailed(report)) { 442 if (scoreNode) scoreNode.textContent = "â"; 443 if (summaryNode) summaryNode.textContent = measurementFailureCopy(report); 444 const axesNode = document.querySelector("[data-audit-axes]"); 445 if (axesNode) axesNode.innerHTML = `<article><small>${escapeHtml(localized("Measurement status","鿏¬çæ "))}</small><b>â</b><span>${escapeHtml(report.measurement?.state || "measurement_failed")}</span></article>`; 446 const evidenceNode = document.querySelector("[data-audit-evidence]"); 447 if (evidenceNode) evidenceNode.innerHTML = `<article><header><b>${escapeHtml(localized("No score issued","æªç¼åºåæ¸"))}</b></header><p>${escapeHtml(report.measurement?.reason || "")}</p><strong>${escapeHtml(report.measurement?.next_action || "")}</strong></article>`; 448 const hashNode = document.querySelector("[data-audit-hash]"); 449 if (hashNode) hashNode.textContent = report.content_hash || "pending"; 450 auditResults.scrollIntoView({block:"start"}); 451 return; 452 } 453 if (scoreNode) scoreNode.textContent = `${report.overall_score ?? report.score ?? "â"} / 100`; 454 const scorecard = Array.isArray(report.scorecard) ? report.scorecard : Object.entries(report.scorecard || {}).map(([area,value]) => ({area:area.toUpperCase(),...(value || {})})); 455 const axisScore = (name) => scorecard.find((axis) => String(axis.area || "").toUpperCase().startsWith(name))?.score ?? report.dimensions?.[name.toLowerCase()]?.score ?? "â"; 456 if (summaryNode) summaryNode.textContent = `${report.subject || report.url || auditTarget} · SEO ${axisScore("SEO")} · AEO ${axisScore("AEO")} · GEO ${axisScore("GEO")} · AGO ${axisScore("AGO")}`; 457 const axesNode = document.querySelector("[data-audit-axes]"); 458 if (axesNode) axesNode.innerHTML = scorecard.filter((axis) => ["SEO","AEO","GEO","AGO"].some((name) => String(axis.area || "").toUpperCase().startsWith(name))).map((axis) => `<article><small>${escapeHtml(axis.area)}</small><b>${escapeHtml(axis.score ?? "â")}</b><span>${escapeHtml(axis.pass ?? 0)} ${escapeHtml(localized("pass","éé"))} · ${escapeHtml(axis.partial ?? 0)} ${escapeHtml(localized("partial","é¨å"))} · ${escapeHtml(axis.pending ?? 0)} ${escapeHtml(localized("pending","å¾ ç¢ºèª"))}</span></article>`).join(""); 459 const evidenceNode = document.querySelector("[data-audit-evidence]"); 460 const buckets = report.priority_action_plan || {}; 461 const findings = [...(buckets.today || []),...(buckets.this_week || []),...(buckets.this_month || [])]; 462 if (evidenceNode) evidenceNode.innerHTML = findings.length ? findings.map((item) => `<article><header><b>${escapeHtml(item.priority || "P2")} · ${escapeHtml(item.issue || item.source_check_id)}</b><span>${escapeHtml(item.source_check_id || "public_check")}</span></header><p>${escapeHtml(item.evidence || "Evidence not available")}</p><strong>${escapeHtml(item.fix || "Review the public signal.")}</strong></article>`).join("") : `<p>${escapeHtml(localized("No pending findings.","ç®åæ²æå¾ æ¹åé ç®ã"))}</p>`; 463 const hashNode = document.querySelector("[data-audit-hash]"); 464 if (hashNode) hashNode.textContent = report.content_hash || "pending"; 465 auditResults.scrollIntoView({block:"start"}); 466 }) 467 .catch((error) => { 468 const summaryNode = document.querySelector("[data-quick-summary]"); 469 if (summaryNode) summaryNode.textContent = `${localized("Audit failed","巡檢失æ")}: ${error.message}`; 470 }); 471 } 472 473 document.querySelectorAll("[data-copy-target]").forEach((button) => button.addEventListener("click", async () => { 474 const target = document.getElementById(button.dataset.copyTarget); 475 if (!target) return; 476 await navigator.clipboard.writeText(target.textContent.trim()); 477 const original = button.textContent; 478 button.textContent = "â"; 479 window.setTimeout(() => { button.textContent = original; }, 1200); 480 })); 481 482 const dashboard = document.querySelector("[data-dashboard-root]"); 483 if (!dashboard) return; 484 let dashboardHistory = null; 485 let activeDashboardHost = ""; 486 let publicDemoDashboard = false;
487 let dashboardClient = null; 488 let dashboardGeneration = 0; 489 const clearCustomerView = () => { 490 activeDashboardHost = ""; 491 window.AgentIdReportApi?.clear(); 492 const content = document.querySelector("[data-dashboard-content]"); 493 render({readiness: {measurement: {status: "measurement_failed"}}}); 494 renderConnectors({connectors: ["a1", "a2", "b1"].map(id => ({id, status: "pending"}))}); 495 renderTasks({tasks: []}); renderBilling(null); renderDecision(null); 496 renderHistory({snapshots: [], comparison: {status: "unavailable"}}); 497 dashboardData = dashboardConnectors = dashboardTasks = dashboardBilling = dashboardDecision = dashboardHistory = null; 498 document.querySelector("[data-public-identity]")?.removeAttribute("href"); 499 document.querySelectorAll("[data-metric]").forEach((node) => { 500 node.querySelectorAll("[data-value], [data-source], [data-state]").forEach((cell) => { cell.textContent = "â"; }); 501 }); 502 document.querySelectorAll("dialog[open]").forEach((dialog) => { dialog.close(); }); 503 document.querySelectorAll("dialog:not([data-connector-dialog])").forEach((dialog) => dialog.remove()); 504 const setupCommand = document.querySelector("[data-setup-command]"); 505 if (setupCommand) setupCommand.textContent = ""; 506 const input = document.getElementById("dashboard-audit-url"); 507 if (input) input.value = ""; 508 const quick = document.querySelector("[data-audit-quick-result]"); 509 if (quick) { quick.textContent = ""; quick.hidden = true; } 510 content?.setAttribute("hidden", ""); 511 }; 512 const text = (selector, value) => { 513 const node = document.querySelector(selector); 514 if (node) node.textContent = value == null || value === "" ? "â" : String(value); 515 }; 516 const score = (value) => Number.isFinite(Number(value)) ? Math.max(0, Math.min(100, Number(value))) : null; 517 const formatDate = (value) => { 518 if (!value) return "â"; 519 const normalized = typeof value === "number" && value < 1e12 ? value * 1000 : value; 520 const date = new Date(normalized); 521 return Number.isNaN(date.valueOf()) ? String(value) : new Intl.DateTimeFormat(document.documentElement.lang || "en", {dateStyle:"medium",timeStyle:"short"}).format(date); 522 }; 523 const dateKey = (value) => { 524 if (!value) return ""; 525 const normalized = typeof value === "number" && value < 1e12 ? value * 1000 : value; 526 const date = new Date(normalized); 527 return Number.isNaN(date.valueOf()) ? "" : date.toISOString().slice(0,10); 528 }; 529 const renderMetric = (id, metric) => { 530 const row = document.querySelector(`[data-metric="${id}"]`); 531 if (!row) return; 532 const state = row.querySelector("[data-state]"); 533 const value = row.querySelector("[data-value]"); 534 const source = row.querySelector("[data-source]"); 535 const ready = metric?.status === "ready"; 536 state.classList.toggle("ready", ready); 537 state.textContent = ready ? localized("Connected", "å·²æ¥å ¥") : localized("Pending", "å¾ æ¥å ¥"); 538 value.textContent = ready ? String(metric.value) : "â"; 539 source.textContent = metric?.proof_level || "not_observed"; 540 }; 541 const renderConnectors = (payload) => { 542 dashboardConnectors = payload; 543 (payload?.connectors || []).forEach((connector) => { 544 const card = document.querySelector(`[data-connector="${connector.id}"]`); 545 if (!card) return; 546 const ready = connector.status === "ready"; 547 const state = card.querySelector("[data-connector-state]"); 548 const detail = card.querySelector("[data-connector-detail]"); 549 if (state) { 550 state.textContent = ready ? localized("Connected", "å·²æ¥å ¥") : localized("Pending", "å¾ æ¥å ¥"); 551 state.classList.toggle("positive", ready); 552 } 553 if (detail) detail.textContent = ready 554 ? `${connector.observed_records} ${localized("observed records", "çç實èæ")}` 555 : localized("No observed evidence yet; not counted as zero", "å°æªåå¾çå¯¦è³æï¼ä¸è¨çº 0"); 556 }); 557 }; 558 const fallbackConnectors = (data) => ({connectors:[ 559 {id:"a1",status:data.outcomes?.ai_crawler_reads?.status,observed_records:data.outcome_details?.ai_reads?.total || 0}, 560 {id:"a2",status:data.outcomes?.ai_citation_rate?.status,observed_records:data.outcome_details?.ai_citations?.total || 0}, 561 {id:"b1",status:data.outcomes?.ai_attributed_conversions?.status,observed_records:data.outcome_details?.conversions?.total || 0}, 562 ]}); 563 // Keyed by the coverage plan's stable source ids. The server sends English; 564 // the ids let this render Chinese without the server knowing about locales. 565 const COVERAGE_COPY = { 566 readiness_audit: [ 567 "A readiness audit", "䏿¬¡å¯è®æ§ç¨½æ ¸", 568 "Nothing to connect. Every audit of a verified host produces this.", 569 "ä¸ç¨æ¥ä»»ä½æ±è¥¿ãæ¯ä¸æ¬¡å°å·²é©èç¶²ç«çç¨½æ ¸é½æç¢çéä¸çã", 570 ], 571 server_log_connector: [ 572 "CDN or server access logs", "CDN æä¼ºæå¨ååç´é", 573 "Connect the log source that already records AI crawler requests.",
574 "æå·²ç¶å¨è¨é AI ç¬è²è«æ±çç´é便ºæ¥ä¸ä¾ã", 575 ], 576 citation_connector: [ 577 "An existing citation export", "æ¢æçå¼ç¨è³æå¯åº", 578 "Import observations you already collect elsewhere.", 579 "æä½ å¨å¥èå·²ç¶å¨æ¶çè§æ¸¬è³æå¯é²ä¾ã", 580 ], 581 answer_engine_probe: [ 582 "An answer-engine probe run", "å°çæ¡å¼æè·ä¸æ¬¡æ¢æ¸¬", 583 "An operator selects the engines and runs it explicitly.", 584 "ç±æä½è é¸å®å¼æä¸¦æç¢ºå·è¡ã", 585 ], 586 provider_decision: [ 587 "A provider decision report", "便åçæ±ºçåå ±", 588 "An AI provider reports its decision through the provider decision endpoint.", 589 "ç± AI 便åééæ±ºçåå ±ç«¯é»éé²ä¾ã", 590 ], 591 conversion_connector: [ 592 "CRM or analytics conversion events", "CRM æåæå·¥å ·çè½æäºä»¶", 593 "Connect the system that already records conversions.", 594 "æå·²ç¶å¨è¨éè½æç系統æ¥ä¸ä¾ã", 595 ], 596 }; 597 598 /* The connect dialog. Three steps for a two-step connector, two for an 599 import. The copy-the-request step exists because this service holds no 600 customer credential: the request has to be run by the customer, against 601 their own account. The dialog says that rather than leaving it to be 602 guessed. */ 603 let connectSession = null; 604 605 const connectRequest = async (path, options) => { 606 const response = await fetch(path, { 607 method: options.method || "POST", 608 headers: {"Content-Type": "application/json"}, 609 credentials: "same-origin", 610 body: JSON.stringify(options.body || {}), 611 }); 612 const payload = await response.json().catch(() => ({})); 613 if (!response.ok || payload.ok === false) { 614 throw new Error(payload.error || `request_failed_${response.status}`); 615 } 616 return payload; 617 }; 618 619 /* The probe dialog. Two gates before anything is spent: an engine has to be 620 chosen, and the cost has to be acknowledged. Neither is pre-ticked, and 621 the button stays disabled until both are answered â a billable action must 622 not be reachable by pressing return on a form. */ 623 const openProbeDialog = () => { 624 const host = activeDashboardHost; 625 if (!host) return; 626 const dialog = document.createElement("dialog"); 627 dialog.className = "connect-dialog"; 628 const close = () => { dialog.close(); dialog.remove(); }; 629 630 const head = document.createElement("header"); 631 const title = document.createElement("h3"); 632 title.textContent = localized("Run an answer-engine probe", "è·ä¸æ¬¡çæ¡å¼ææ¢æ¸¬"); 633 const stage = document.createElement("p"); 634 stage.className = "connect-stage"; 635 stage.textContent = localized("A2 · Observed AI citation", "A2 · 實éå¼ç¨"); 636 const dismiss = document.createElement("button"); 637 dismiss.type = "button"; dismiss.className = "connect-close"; 638 dismiss.textContent = "Ã"; dismiss.addEventListener("click", close); 639 head.append(title, stage, dismiss); 640 dialog.append(head); 641 642 const note = document.createElement("p"); 643 note.className = "connect-note"; 644 note.textContent = localized( 645 "This queries external answer engines. Every run costs money, and nothing here runs on a schedule.", 646 "éæå»åå¤é¨ççæ¡å¼æãæ¯ä¸æ¬¡å·è¡é½æç¢çè²»ç¨ï¼èä¸éè£¡ä¸ææä»»ä½æç¨èªåå·è¡ã", 647 ); 648 dialog.append(note); 649 650 const form = document.createElement("form"); 651 form.className = "connect-form"; 652 const error = document.createElement("p"); 653 error.className = "connect-error"; 654 error.hidden = true; 655 const engines = document.createElement("div"); 656 engines.className = "probe-engines"; 657 const acknowledge = document.createElement("label"); 658 acknowledge.className = "probe-acknowledge"; 659 const ack = document.createElement("input"); 660 ack.type = "checkbox"; 661 const ackText = document.createElement("span"); 662 ackText.textContent = localized( 663 "I understand this run is billable.", "æäºè§£é次å·è¡æç¢çè²»ç¨ã", 664 ); 665 acknowledge.append(ack, ackText); 666 const go = document.createElement("button"); 667 go.type = "submit"; 668 go.disabled = true; 669 go.textContent = localized("Run it", "å·è¡"); 670 671 const refreshEnabled = () => { 672 const chosen = engines.querySelector("input[type=checkbox]:checked"); 673 go.disabled = !(chosen && ack.checked); 674 }; 675 ack.addEventListener("change", refreshEnabled); 676 677 form.append(engines, acknowledge, go, error); 678 dialog.append(form); 679 680 connectRequest("/api/dashboard/connector/probe-engines", {body: {url: `https://${host}`}}) 681 .then((payload) => { 682 const rows = (payload && payload.engines) || []; 683 if (!rows.length) { 684 const empty = document.createElement("p"); 685 empty.className = "decision-empty"; 686 empty.textContent = localized( 687 "No answer engine is configured yet. An operator has to add one before a probe can run.", 688 "ç®åæ²æè¨å®ä»»ä½çæ¡å¼æãè¦å ç±æä½è å ä¸ï¼æè½å·è¡æ¢æ¸¬ã", 689 ); 690 engines.append(empty); 691 return; 692 } 693 rows.forEach((engine) => { 694 const label = document.createElement("label"); 695 const box = document.createElement("input"); 696 box.type = "checkbox"; box.value = engine.id; 697 box.addEventListener("change", refreshEnabled); 698 const text = document.createElement("span"); 699 text.textContent = engine.label || engine.id; 700 label.append(box, text); 701 engines.append(label); 702 }); 703 }) 704 .catch(() => { 705 error.textContent = localized("Could not read the engine list.", "è®ä¸å°å¼ææ¸ å®ã"); 706 error.hidden = false;
707 }); 708 709 form.addEventListener("submit", async (event) => { 710 event.preventDefault(); 711 error.hidden = true; 712 const chosen = [...engines.querySelectorAll("input[type=checkbox]:checked")].map((b) => b.value); 713 if (!chosen.length || !ack.checked) return; 714 go.disabled = true; 715 go.textContent = localized("Runningâ¦", "å·è¡ä¸â¦"); 716 try { 717 const result = await connectRequest("/api/dashboard/connector/probe", { 718 body: {url: `https://${host}`, run: true, engines: chosen}, 719 }); 720 close(); 721 if (result && result.intelligence) { 722 renderDecision({ok: true, intelligence: result.intelligence}); 723 } 724 } catch (err) { 725 go.disabled = false; 726 go.textContent = localized("Run it", "å·è¡"); 727 error.textContent = localized("The run failed: ", "å·è¡å¤±æï¼") + err.message; 728 error.hidden = false; 729 } 730 }); 731 732 document.body.append(dialog); 733 dialog.showModal(); 734 }; 735 736 const openConnectDialog = (plan) => { 737 const host = activeDashboardHost || document.body.dataset.host || ""; 738 if (!host) return; 739 const dialog = document.createElement("dialog"); 740 dialog.className = "connect-dialog"; 741 const close = () => { dialog.close(); dialog.remove(); connectSession = null; }; 742 743 const render = (step, context) => { 744 dialog.replaceChildren(); 745 const head = document.createElement("header"); 746 const title = document.createElement("h3"); 747 title.textContent = localized("Connect a source", "æ¥ä¸è³æä¾æº"); 748 const stage = document.createElement("p"); 749 stage.className = "connect-stage"; 750 stage.textContent = plan.label || plan.stage; 751 const dismiss = document.createElement("button");
752 dismiss.type = "button"; dismiss.className = "connect-close"; 753 dismiss.textContent = "Ã"; dismiss.addEventListener("click", close); 754 head.append(title, stage, dismiss); 755 dialog.append(head); 756 757 const error = document.createElement("p"); 758 error.className = "connect-error"; 759 error.hidden = true; 760 const fail = (message) => { error.textContent = message; error.hidden = false; }; 761 762 if (step === "choose") { 763 const list = document.createElement("div"); 764 list.className = "connect-choices"; 765 plan.connectors.forEach((conn) => { 766 const choice = document.createElement("button"); 767 choice.type = "button"; 768 choice.className = `connect-choice${conn.billable ? " connect-choice-billable" : ""}`; 769 const name = document.createElement("b"); name.textContent = conn.label; 770 const need = document.createElement("span"); need.textContent = conn.you_need; 771 choice.append(name, need); 772 if (conn.billable) { 773 const cost = document.createElement("em"); 774 cost.textContent = localized("Costs money each run", "æ¯æ¬¡å·è¡é½æè¨è²»"); 775 choice.append(cost); 776 } 777 choice.addEventListener("click", () => { 778 connectSession = window.AgentIdConnectSource.createConnectSource({ 779 request: connectRequest, localized, 780 }); 781 connectSession.reset(host, conn.connector); 782 render("describe", conn); 783 }); 784 list.append(choice); 785 }); 786 dialog.append(list); 787 } 788 789 if (step === "describe") { 790 const conn = context; 791 const form = document.createElement("form"); 792 form.className = "connect-form"; 793 connectSession.fieldsFor(conn.connector).forEach((field) => { 794 const label = document.createElement("label"); 795 const caption = document.createElement("span"); 796 caption.textContent = localized(field.label[0], field.label[1]); 797 const input = document.createElement(field.type === "json" ? "textarea" : "input"); 798 input.name = field.name; 799 if (field.type !== "json") input.type = "text"; 800 const hint = document.createElement("small"); 801 hint.textContent = localized(field.hint[0], field.hint[1]); 802 label.append(caption, input, hint); 803 form.append(label); 804 }); 805 if (connectSession.needsWindow(conn.connector)) { 806 ["window_start", "window_end"].forEach((name, index) => { 807 const label = document.createElement("label"); 808 const caption = document.createElement("span"); 809 caption.textContent = index === 0 ? localized("From", "èµ·") : localized("To", "è¿"); 810 const input = document.createElement("input"); 811 input.type = "date"; input.name = name; 812 label.append(caption, input); 813 form.append(label); 814 }); 815 } 816 const go = document.createElement("button"); 817 go.type = "submit"; 818 go.textContent = connectSession.needsPlan(conn.connector) 819 ? localized("Build the request", "ç¢çè«æ±") 820 : localized("Read it", "è®åå §å®¹"); 821 form.append(go, error); 822 form.addEventListener("submit", async (event) => { 823 event.preventDefault(); 824 error.hidden = true; 825 const values = Object.fromEntries(new FormData(form).entries()); 826 try { 827 if (connectSession.needsPlan(conn.connector)) { 828 await connectSession.plan(values); 829 render("fetch", conn); 830 } else { 831 await connectSession.plan(values); 832 await connectSession.normalize(); 833 render("confirm", conn); 834 } 835 } catch (err) { 836 fail(localized("Could not read that: ", "è®ä¸å°ï¼") + err.message); 837 } 838 }); 839 dialog.append(form); 840 } 841 842 if (step === "fetch") { 843 const conn = context; 844 const note = document.createElement("p"); 845 note.className = "connect-note"; 846 note.textContent = localized( 847 "Run this against your own account. We never see your credential, which is why you run it and not us.", 848 "è«ç¨ä½ èªå·±ç帳èå·è¡éåè«æ±ãæå䏿çå°ä½ çæèï¼æä»¥ç±ä½ å·è¡ã䏿¯æåã", 849 ); 850 const pre = document.createElement("pre"); 851 pre.className = "connect-request"; 852 pre.textContent = connectSession.requestPreview(); 853 const copy = document.createElement("button"); 854 copy.type = "button"; 855 copy.textContent = localized("Copy", "è¤è£½"); 856 copy.addEventListener("click", () => navigator.clipboard?.writeText(pre.textContent)); 857 const form = document.createElement("form"); 858 form.className = "connect-form"; 859 const label = document.createElement("label"); 860 const caption = document.createElement("span"); 861 caption.textContent = localized("Paste what came back", "æåå³çµæè²¼ä¸"); 862 const area = document.createElement("textarea"); 863 area.name = "response"; 864 label.append(caption, area); 865 const go = document.createElement("button"); 866 go.type = "submit"; 867 go.textContent = localized("Read it", "è®åå §å®¹"); 868 form.append(label, go, error); 869 form.addEventListener("submit", async (event) => { 870 event.preventDefault(); 871 error.hidden = true; 872 try { 873 await connectSession.normalize(area.value); 874 render("confirm", conn); 875 } catch (err) { 876 fail(localized("Could not read that: ", "è®ä¸å°ï¼") + err.message); 877 } 878 }); 879 dialog.append(note, pre, copy, form); 880 } 881 882 if (step === "confirm") { 883 const note = document.createElement("p"); 884 note.className = "connect-note"; 885 note.textContent = localized( 886 "This is what was extracted. Nothing is stored until you add it.", 887 "以䏿¯è®åºä¾çå §å®¹ãå¨ä½ æä¸å å ¥ä¹åï¼ä¸æå任使±è¥¿ã", 888 ); 889 const table = document.createElement("dl"); 890 table.className = "connect-summary";
891 connectSession.summary().forEach((row) => { 892 const dt = document.createElement("dt"); dt.textContent = row.label; 893 const dd = document.createElement("dd"); dd.textContent = String(row.value); 894 table.append(dt, dd); 895 }); 896 const go = document.createElement("button"); 897 go.type = "button"; 898 go.textContent = localized("Add to evidence", "å å ¥èæ"); 899 go.addEventListener("click", async () => { 900 error.hidden = true; 901 try { 902 const result = await connectSession.submit(); 903 close(); 904 if (result && result.intelligence) { 905 renderDecision({ok: true, intelligence: result.intelligence}); 906 } 907 } catch (err) { 908 fail(localized("Could not store that: ", "åä¸é²å»ï¼") + err.message); 909 } 910 }); 911 dialog.append(note, table, go, error); 912 } 913 }; 914 915 render("choose", null); 916 document.body.append(dialog); 917 dialog.showModal(); 918 }; 919 920 const renderDecision = (payload) => { 921 dashboardDecision = payload || {ok:true,intelligence:null}; 922 const intelligence = dashboardDecision.intelligence; 923 const grid = document.querySelector("[data-decision-grid]"); 924 if (!grid) return; 925 grid.replaceChildren(); 926 const stageCopy = { 927 A0_crawlability:["å ¬éåå","Public access","å ¬éç AI æ·å系統è½å¦è®åç¶²ç«ï¼","Can public AI retrieval infrastructure access the website?"], 928 A1_retrieval:["實éè®å","Observed retrieval","æ¯å¦è§æ¸¬å°å·²èå¥ç AI ç¬è²è®åç¶²ç«ï¼","Did an identified AI crawler retrieve the website?"], 929 A2_citation:["實éå¼ç¨","Observed citation","已鿏¬ç AI åçæ¯å¦å¼ç¨ç¶²ç«ï¼","Did a measured AI answer cite the website?"], 930 A3_consideration:["ç´å ¥èé","Consideration","æ¯å¦è¢«åå ¥ä¾æåææ¹æ¡çèéåå®ï¼","Was the website included in a measured consideration set?"], 931 A4_recommendation:["被æ¨è¦","Recommendation","æ¯å¦å¨é測åçä¸è¢«æ¨è¦ï¼","Was the website recommended in a measured answer?"], 932 A5_preference_reason:["å好åå ","Preference reason","AI æ¯å¦æä¾å好ææé¤ç¶²ç«çæç¢ºçç±ï¼","Was an explicit reason for or against preference observed?"], 933 B1_conversion:["åæ¥çµæ","Business outcome","æ¯å¦è§æ¸¬å°å¯æ¸å çè©¢å¹ãè¨å®æå ¶ä»åæ¥çµæï¼","Was an AI-attributed business outcome observed?"], 934 }; 935 const stages = Object.keys(stageCopy); 936 const rows = intelligence?.stages || {}; 937 const measuredCount = stages.filter((id) => rows[id]?.status === "measured").length; 938 const observedCount = stages.filter((id) => rows[id]?.status && rows[id].status !== "pending").length; 939 text("[data-decision-status]", intelligence 940 ? `${localized("Observed", "å·²è§æ¸¬")} ${observedCount} / ${stages.length} · ${localized("sample gate met", "鿍£æ¬é檻")} ${measuredCount}` 941 : localized("Tenant evidence appears after sign-in", "ç»å ¥å¾é¡¯ç¤ºç§æ¶èæ")); 942 const statusCopy = { 943 measured: localized("Measured", "已鿏¬"), 944 insufficient_sample: localized("More samples needed", "樣æ¬ä¸è¶³"), 945 pending: localized("Pending", "å¾ åå¾"), 946 };
947 stages.forEach((id) => { 948 const stage = rows[id] || {status:"pending",numerator:0,denominator:0,sample_size:0,evidence:[]}; 949 const status = statusCopy[stage.status] ? stage.status : "pending"; 950 const details = document.createElement("details"); 951 details.className = `decision-stage state-${status}`; 952 const summary = document.createElement("summary"); 953 const head = document.createElement("div"); 954 const code = document.createElement("span"); code.className = "decision-code"; code.textContent = id.split("_")[0]; 955 const title = document.createElement("b"); title.textContent = localized(stageCopy[id][1], stageCopy[id][0]); 956 const state = document.createElement("em"); state.textContent = localized(statusCopy[status]); 957 head.append(code,title,state); 958 const question = document.createElement("p"); question.textContent = localized(stageCopy[id][3], stageCopy[id][2]); 959 const metric = document.createElement("strong"); 960 metric.textContent = status === "measured" && Number.isFinite(Number(stage.rate)) 961 ? `${Math.round(Number(stage.rate) * 100)}%` 962 : status === "insufficient_sample" 963 ? `${stage.numerator || 0}/${stage.denominator || 0} · â¥${stage.minimum_sample_size || 3}` 964 : "â"; 965 summary.append(head,question,metric); 966 const evidenceList = document.createElement("div"); evidenceList.className = "decision-evidence-list"; 967 const evidence = Array.isArray(stage.evidence) ? stage.evidence : []; 968 if (!evidence.length) { 969 const empty = document.createElement("p"); empty.className = "decision-empty"; 970 empty.textContent = localized("No contract-valid evidence has been observed.", "å°æªåå¾ç¬¦åå¥ç´çèæã"); 971 evidenceList.append(empty); 972 // "Nothing observed" is true and useless on its own: a customer cannot 973 // tell whether the product is broken, whether they have to do 974 // something, or whether they simply have to wait. The server's coverage 975 // plan answers that, and the source ids are stable so the copy can be 976 // localized here. 977 const plan = (intelligence?.coverage?.stages || []).find((row) => row.stage === id); 978 if (plan && plan.status !== "measured") { 979 const guide = document.createElement("div"); 980 guide.className = `decision-coverage ${plan.customer_can_act ? "coverage-actionable" : "coverage-waiting"}`; 981 const lede = document.createElement("p"); 982 lede.className = "decision-coverage-lede"; 983 lede.textContent = plan.customer_can_act 984 ? localized("You can switch this on.", "éä¸é ä½ å¯ä»¥èªå·±éåã") 985 : localized("This one waits on an AI provider reporting a decision.", "éä¸é è¦ç AI 便ååå ±æ±ºçã"); 986 guide.append(lede); 987 const list = document.createElement("ul"); 988 list.className = "decision-coverage-sources"; 989 (plan.fills_from || []).forEach((src) => { 990 const item = document.createElement("li"); 991 const label = document.createElement("b"); 992 label.textContent = localized(COVERAGE_COPY[src.source]?.[0] || src.label, COVERAGE_COPY[src.source]?.[1] || src.label); 993 const how = document.createElement("span"); 994 how.textContent = localized(COVERAGE_COPY[src.source]?.[2] || src.requires, COVERAGE_COPY[src.source]?.[3] || src.requires); 995 item.append(label, how); 996 if (src.billable) { 997 const cost = document.createElement("em"); 998 cost.className = "decision-coverage-cost"; 999 cost.textContent = localized("Billable, never runs on its own", "æè¨è²»ï¼ä¸æèªåå·è¡"); 1000 item.append(cost); 1001 } 1002 // Naming the connector and what it asks for turns "connect a log 1003 // source" into something the tenant can go and do today. 1004 (src.connectors || []).forEach((conn) => { 1005 const detail = document.createElement("span"); 1006 detail.className = "decision-coverage-connector"; 1007 detail.textContent = `${conn.label} · ${conn.you_need}`; 1008 item.append(detail); 1009 }); 1010 list.append(item); 1011 }); 1012 guide.append(list); 1013 if (!publicDemoDashboard && activeDashboardHost 1014 && (plan.fills_from || []).some((src) => src.billable)) { 1015 const probe = document.createElement("button"); 1016 probe.type = "button"; 1017 probe.className = "decision-coverage-connect decision-coverage-billable"; 1018 probe.textContent = localized("Run a probe (billable)", "
1018è·ä¸æ¬¡æ¢æ¸¬ï¼æè¨è²»ï¼"); 1019 probe.addEventListener("click", () => openProbeDialog()); 1020 guide.append(probe); 1021 } 1022 // Some stages need no connector at all: the product can observe the 1023 // customer's own verified host by itself. Those get one button, not a 1024 // dialog, because there is nothing to configure or paste. 1025 if (!publicDemoDashboard && activeDashboardHost && (plan.actions || []).includes("public_audit")) { 1026 const run = document.createElement("button"); 1027 run.type = "button"; 1028 run.className = "decision-coverage-connect"; 1029 run.textContent = localized("Observe it now", "ç¾å¨è§æ¸¬ä¸æ¬¡"); 1030 run.addEventListener("click", async () => { 1031 const previous = run.textContent; 1032 run.disabled = true; 1033 run.textContent = localized("Observingâ¦", "è§æ¸¬ä¸â¦"); 1034 try { 1035 const result = await connectRequest("/api/dashboard/connector/submit", { 1036 body: {url: `https://${activeDashboardHost}`, kind: "public_audit"}, 1037 }); 1038 if (result && result.intelligence) { 1039 renderDecision({ok: true, intelligence: result.intelligence}); 1040 return; 1041 } 1042 } catch (_error) { 1043 run.textContent = localized("Could not observe it", "鿬¡è§æ¸¬å¤±æ"); 1044 return; 1045 } finally { 1046 run.disabled = false; 1047 if (run.textContent === localized("Observingâ¦", "è§æ¸¬ä¸â¦")) run.textContent = previous; 1048 } 1049 }); 1050 guide.append(run); 1051 } 1052 // Not on the public demo: there is no tenant there to store evidence for. 1053 if (plan.customer_can_act && (plan.connectors || []).length 1054 && !publicDemoDashboard && activeDashboardHost && window.AgentIdConnectSource) { 1055 const connect = document.createElement("button"); 1056 connect.type = "button"; 1057 connect.className = "decision-coverage-connect"; 1058 connect.textContent = localized("Connect a source", "æ¥ä¸è³æä¾æº"); 1059 connect.addEventListener("click", () => openConnectDialog(plan)); 1060 guide.append(connect); 1061 } 1062 evidenceList.append(guide); 1063 } 1064 } 1065 evidence.forEach((row, index) => { 1066 const article = document.createElement("article"); 1067 const heading = document.createElement("header"); 1068 const source = document.createElement("b"); source.textContent = row.source || localized("Source unavailable", "æªæ¨ç¤ºä¾æº"); 1069 const result = document.createElement("span"); result.textContent = row.value === true ? localized("Observed", "æç«") : localized("Not observed", "æªæç«"); 1070 heading.append(source,result); 1071 const facts = document.createElement("dl"); 1072 const windowValue = row.window ? `${formatDate(row.window.start)} â ${formatDate(row.window.end)}` : "â"; 1073 const factRows = [ 1074 [localized("Method", "æ¹æ³"), row.method || "â"], 1075 [localized("Window", "鿏¬æé"), windowValue], 1076 [localized("Numerator / denominator", "åå / 忝"), `${row.numerator ?? "â"} / ${row.denominator ?? "â"}`], 1077 [localized("Sample size", "æ¨£æ¬æ¸"), row.sample_size ?? "â"], 1078 [localized("Observed at", "è§æ¸¬æé"), formatDate(row.observed_at)], 1079 [localized("Attestation", "èæçæ "), row.attestation_status || "â"], 1080 ]; 1081 factRows.forEach(([label,value]) => { 1082 const dt = document.createElement("dt"); dt.textContent = label; 1083 const dd = document.createElement("dd"); dd.textContent = String(value); 1084 facts.append(dt,dd); 1085 }); 1086 const receipt = document.createElement("code"); receipt.textContent = row.receipt_hash || "â"; 1087 const receiptLabel = document.createElement("small"); receiptLabel.textContent = localized("Receipt hash", "æ¶æéæ¹"); 1088 const limitations = document.createElement("ul");
1089 (row.limitations || []).forEach((item) => { const li = document.createElement("li"); li.textContent = item; limitations.append(li); }); 1090 article.append(heading,facts,receiptLabel,receipt,limitations); 1091 evidenceList.append(article); 1092 }); 1093 details.append(summary,evidenceList); 1094 grid.append(details); 1095 }); 1096 }; 1097 const renderTasks = (payload) => { 1098 dashboardTasks = payload; 1099 const grid = document.querySelector("[data-task-grid]"); 1100 if (!grid) return; 1101 grid.replaceChildren(); 1102 const tasks = payload?.tasks || []; 1103 if (!tasks.length) { 1104 const empty = document.createElement("p"); 1105 empty.className = "task-empty"; 1106 empty.textContent = localized("No bounded file changes are waiting for action.", "ç®åæ²æçå¾ èççé宿ªæ¡è®æ´ã"); 1107 grid.append(empty); 1108 return; 1109 } 1110 tasks.forEach((task) => { 1111 const article = document.createElement("article"); 1112 article.className = "task-item"; 1113 const header = document.createElement("header"); 1114 const path = document.createElement("b"); path.textContent = task.path || "â"; 1115 const state = document.createElement("span"); state.textContent = task.state || "â"; 1116 header.append(path, state); 1117 const action = document.createElement("p"); 1118 action.textContent = task.action || localized("Review bounded file change", "檢è¦é宿ªæ¡è®æ´"); 1119 article.append(header, action); 1120 if (task.customer_command) { 1121 const command = document.createElement("pre"); command.textContent = task.customer_command; 1122 const copy = document.createElement("button"); 1123 copy.type = "button"; copy.className = "task-copy"; 1124 copy.textContent = localized("Copy customer command", "è¤è£½å®¢æ¶ç«¯å½ä»¤"); 1125 copy.addEventListener("click", async () => { 1126 await navigator.clipboard.writeText(task.customer_command); 1127 copy.textContent = "â"; 1128 window.setTimeout(() => { copy.textContent = localized("Copy customer command", "è¤è£½å®¢æ¶ç«¯å½ä»¤"); }, 1200); 1129 }); 1130 article.append(command, copy); 1131 } 1132 grid.append(article); 1133 }); 1134 }; 1135 const safeStripeCheckoutUrl = (result, planId, host) => { 1136 const sessionId = String(result?.checkout_session_id || ""); 1137 let destination; 1138 try { 1139 destination = new URL(result?.checkout_url || ""); 1140 } catch { 1141 return ""; 1142 } 1143 if ( 1144 result?.ok !== true || 1145 result.provider !== "stripe" || 1146 result.plan_id !== planId || 1147 result.host !== host || 1148 result.payment_state !== "pending_provider_confirmation" || 1149 !/^cs_[A-Za-z0-9_]+$/.test(sessionId) || 1150 destination.protocol !== "https:" || 1151 destination.hostname !== "checkout.stripe.com" || 1152 destination.port || 1153 destination.username || 1154 destination.password || 1155 destination.pathname === "/" 1156 ) return ""; 1157 return destination.href; 1158 }; 1159 const renderBilling = (payload) => { 1160 dashboardBilling = payload; 1161 const panel = document.querySelector("[data-billing-panel]"); 1162 const planGrid = document.querySelector("[data-billing-plan-grid]"); 1163 const list = document.querySelector("[data-billing-list]"); 1164 const manage = document.querySelector("[data-billing-manage]"); 1165 const message = document.querySelector("[data-billing-message]"); 1166 if (!panel || !planGrid || !list || !manage || !message) return; 1167 const hidden = publicDemoDashboard || !payload; 1168 panel.toggleAttribute("hidden", hidden); 1169 planGrid.replaceChildren(); 1170 list.replaceChildren(); 1171 message.textContent = ""; 1172 if (hidden) return; 1173 const plans = payload.plans || []; 1174 const accessCopy = { 1175 included: localized("Included", "å·²å å«"), 1176 active: localized("Active", "使ç¨ä¸"), 1177 payment_recovery_required: localized("Payment needs attention", "仿¬¾éè¦èç"), 1178 checkout_available: localized("Available", "å¯åç¨"), 1179 approval_required: localized("Not available", "æ«ä¸å¯ç¨"), 1180 requested: localized("Not available", "æ«ä¸å¯ç¨"), 1181 configuration_pending: localized("Coming soon", "è¨å®ä¸"), 1182 }; 1183 const maturityCopy = { 1184 GA: localized("Generally available", "æ£å¼æä¾"), 1185 "Early Access": localized("Early Access", "æ¶å 使ç¨"), 1186 "Enterprise Pilot": localized("Enterprise Pilot", "伿¥è©¦é»"), 1187 }; 1188 const runPlanAction = async (plan, button) => { 1189 if (!activeDashboardHost || publicDemoDashboard || button.disabled || plan.action !== "checkout") return; 1190 const epoch = dashboardGeneration; 1191 const host = activeDashboardHost; 1192 button.disabled = true; 1193 message.textContent = localized("Creating a secure Stripe Checkout sessionâ¦", "æ£å¨å»ºç« Stripe å®å ¨ä»æ¬¾é â¦");
1194 try { 1195 const result = await fetchJson("/api/dashboard/billing/checkout", { 1196 method:"POST", 1197 headers:{"Content-Type":"application/json"}, 1198 body:JSON.stringify({ 1199 url:`https://${host}`, 1200 plan:plan.id, 1201 source:"dashboard_pricing", 1202 }), 1203 }); 1204 if (epoch !== dashboardGeneration) return; 1205 const checkoutUrl = safeStripeCheckoutUrl(result, plan.id, host); 1206 if (!checkoutUrl) { 1207 message.textContent = localized("Stripe returned an unusable checkout link. No checkout page was opened and no payment is confirmed. Refresh billing status before trying again.", "Stripe åå³ç仿¬¾é£çµç¡æ³å®å ¨ä½¿ç¨ï¼æªéå仿¬¾é ï¼ä»æ¬¾ä¹å°æªç¢ºèªãè«éæ°æ´ç帳åçæ å¾å試ã"); 1208 button.disabled = false; 1209 return; 1210 } 1211 message.textContent = localized("Opening Stripe. The plan changes only after a signed provider confirmation is received.", "å³å°åå¾ Stripeãåªææ¶å°ç°½ç« 仿¬¾ç¢ºèªå¾ï¼æ¹æ¡çæ ææè®æ´ã"); 1212 window.location.assign(checkoutUrl); 1213 } catch (error) { 1214 if (epoch !== dashboardGeneration) return; 1215 if (error.status === 401) { showDashboardError(error); return; } 1216 const managementRequired = ["active_subscription_exists", "subscription_requires_management"].includes(error.code); 1217 message.textContent = managementRequired 1218 ? localized("An existing subscription needs billing management. No new checkout page was opened.", "ç¾æè¨é±éè¦éé帳å管çèçï¼æªéåæ°ç仿¬¾é ã") 1219 : localized("The server did not provide an available checkout session. No payment is confirmed. Refresh billing status before trying again.", "伺æå¨æªæä¾å¯ç¨ä»æ¬¾å·¥ä½é段ï¼ä»æ¬¾å°æªç¢ºèªãè«éæ°æ´ç帳åçæ å¾å試ã"); 1220 button.disabled = false; 1221 } 1222 }; 1223 plans.forEach((plan) => { 1224 const article = document.createElement("article"); 1225 article.className = `billing-plan${plan.recommended ? " recommended" : ""}`; 1226 article.dataset.planId = plan.id; 1227 const head = document.createElement("header"); 1228 const title = document.createElement("div"); 1229 const name = document.createElement("h3"); name.textContent = plan.name; 1230 const maturity = document.createElement("span"); 1231 maturity.className = "billing-maturity"; 1232 maturity.textContent = maturityCopy[plan.maturity] || plan.maturity || ""; 1233 title.append(name, maturity); 1234 const state = document.createElement("strong"); 1235 state.className = `billing-access-state state-${plan.access_state || "unknown"}`; 1236 state.textContent = accessCopy[plan.access_state] || plan.access_state || "â"; 1237 head.append(title, state); 1238 const price = document.createElement("p"); price.className = "billing-price"; 1239 price.textContent = Number(plan.price || 0) === 0 1240 ? (localized("Free", "å è²»")) 1241 : `US$${Number(plan.price).toFixed(0)} / ${localized("month", "æ")}`; 1242 const unit = document.createElement("small"); 1243 unit.textContent = localized("per verified domain", "æ¯åå·²é©èç¶²å"); 1244 price.append(document.createElement("br"), unit); 1245 const features = document.createElement("ul"); features.className = "billing-features"; 1246 (plan.includes || []).forEach((feature) => { 1247 const item = document.createElement("li"); item.textContent = feature; features.append(item); 1248 }); 1249 article.append(head, price, features); 1250 if (!["included", "active"].includes(plan.access_state)) { 1251 const action = document.createElement("button"); 1252 action.type = "button"; 1253 action.className = `btn ${plan.action === "manage" ? "primary" : "secondary"} billing-plan-action`; 1254 action.textContent = plan.action === "checkout" 1255 ? localized("Continue to Stripe", "éé Stripe åç¨") 1256 : plan.action === "manage" 1257 ? localized("Resolve payment in Stripe", "å¨ Stripe èç仿¬¾") 1258 : localized("Not available", "æ«ä¸å¯ç¨"); 1259 action.disabled = plan.access_state === "configuration_pending" || !["checkout", "manage"].includes(plan.action); 1260 action.addEventListener("click", () => { 1261 if (plan.action === "manage") { 1262 manage.click(); 1263 return; 1264 } 1265 if (plan.action === "checkout") runPlanAction(plan, action); 1266 }); 1267 article.append(action); 1268 } 1269 planGrid.append(article); 1270 }); 1271 const subscriptions = payload.subscriptions || []; 1272 if (!subscriptions.length) { 1273 const empty = document.createElement("p"); 1274 empty.className = "billing-empty"; 1275 empty.textContent = localized("No paid subscription is active. Public audits remain available; a verified website identity needs the Identity plan.", "ç®åæ²æä»è²»è¨é±ãå ¬éæª¢æ¸¬ä»å¯ä½¿ç¨ï¼é©èç¶²ç«èº«åéè¦ Identity æ¹æ¡ã"); 1276 list.append(empty); 1277 } 1278 const stateCopy = { 1279 renews: localized("Renews", "æçºçºè¨"), 1280 payment_issue: localized("Payment needs attention", "仿¬¾éè¦èç"), 1281 scheduled_to_cancel: localized("Cancellation scheduled", "å·²æå®åæ¶"), 1282 canceled: localized("Canceled", "已忶"), 1283 not_renewing: localized("Not renewing", "ä¸çºè¨"), 1284 }; 1285 const providerCopy = {
1286 stripe: "Stripe", 1287 google_marketplace: "Google Cloud Marketplace", 1288 }; 1289 subscriptions.forEach((subscription) => { 1290 const article = document.createElement("article"); 1291 article.className = "billing-row"; 1292 const head = document.createElement("header"); 1293 const title = document.createElement("div"); 1294 const name = document.createElement("h3"); 1295 name.textContent = String(subscription.plan_id || "plan").replaceAll("_", " "); 1296 const provider = document.createElement("span"); 1297 provider.textContent = providerCopy[subscription.provider] || subscription.provider || (localized("Billing provider", "仿¬¾å¹³å°")); 1298 title.append(name, provider); 1299 const status = document.createElement("strong"); 1300 status.className = `billing-state state-${subscription.renewal_state || "unknown"}`; 1301 status.textContent = stateCopy[subscription.renewal_state] || subscription.status || (localized("Pending", "å¾ ç¢ºèª")); 1302 head.append(title, status); 1303 const detail = document.createElement("dl"); 1304 const detailRows = [ 1305 [localized("Access status", "æåçæ "), subscription.status || "â"], 1306 [localized("Current period ends", "ç®åé±æçµæ"), formatDate(subscription.current_period_end)], 1307 [localized("Access ends", "æ¬é忢æé"), formatDate(subscription.access_ends_at)], 1308 ]; 1309 detailRows.forEach(([label, value]) => { 1310 const dt = document.createElement("dt"); dt.textContent = label; 1311 const dd = document.createElement("dd"); dd.textContent = value; 1312 detail.append(dt, dd); 1313 }); 1314 article.append(head, detail); 1315 if (subscription.payment_recovery_required) { 1316 const recovery = document.createElement("p"); 1317 recovery.className = "billing-recovery"; 1318 const deadline = subscription.payment_recovery_deadline 1319 ? formatDate(subscription.payment_recovery_deadline) 1320 : ""; 1321 recovery.textContent = subscription.status === "suspended" 1322 ? localized( 1323 "Payment still needs attention. Update the payment method and pay the latest invoice in Stripe if requested. Service resumes after Stripe confirms the subscription is active.", 1324 "仿¬¾ä»éèçãè«å¨ Stripe æ´æ°ä»æ¬¾æ¹å¼ï¼ä¸¦ä¾æç¤ºæ¯ä»ææ°å¸³å®ï¼Stripe 確èªè¨é±æ¢å¾©ææå¾ï¼æåæææ¢å¾©ã", 1325 ) 1326 : deadline 1327 ? localized( 1328 "Payment was not completed. Resolve it in Stripe by {deadline} to avoid interruption.", 1329 "仿¬¾æªå®æãè«å¨ {deadline} åæ¼ Stripe 宿èçï¼é¿å æå䏿·ã", 1330 ).replace("{deadline}", deadline) 1331 : localized( 1332 "Payment was not completed. Resolve it in Stripe to avoid interruption.", 1333 "仿¬¾æªå®æãè«åå¾ Stripe 宿èçï¼é¿å æå䏿·ã", 1334 ); 1335 article.append(recovery); 1336 } 1337 const features = subscription.entitled_features || []; 1338 if (features.length) { 1339 const featureList = document.createElement("ul"); 1340 featureList.className = "billing-features"; 1341 features.forEach((feature) => { 1342 const item = document.createElement("li"); item.textContent = feature; featureList.append(item); 1343 }); 1344 article.append(featureList); 1345 } 1346 if (subscription.provider === "google_marketplace") { 1347 const note = document.createElement("p"); 1348 note.className = "billing-provider-note"; 1349 note.textContent = localized("This plan is managed in Google Cloud Marketplace. Change or cancel it from Google Cloud Billing.", "æ¤æ¹æ¡ç± Google Cloud Marketplace 管çï¼è«å¨ Google Cloud 帳åä¸èª¿æ´æåæ¶ã"); 1350 article.append(note); 1351 } 1352 list.append(article); 1353 }); 1354 const paymentRecoveryRequired = subscriptions.some((item) => item.payment_recovery_required); 1355 manage.toggleAttribute("hidden", !payload.manage_available); 1356 manage.classList.toggle("primary", paymentRecoveryRequired); 1357 manage.classList.toggle("secondary", !paymentRecoveryRequired); 1358 manage.textContent = paymentRecoveryRequired
1359 ? localized("Resolve payment in Stripe", "å¨ Stripe èç仿¬¾") 1360 : (localized("Manage or cancel Stripe subscription", "管çæåæ¶ Stripe è¨é±")); 1361 manage.onclick = async () => { 1362 if (!activeDashboardHost || publicDemoDashboard) return; 1363 const epoch = dashboardGeneration; 1364 manage.disabled = true; 1365 message.textContent = localized("Creating a secure billing management linkâ¦", "æ£å¨å»ºç«å®å ¨ç仿¬¾ç®¡çé£çµâ¦"); 1366 try { 1367 const result = await fetchJson("/api/dashboard/billing/portal", { 1368 method:"POST", 1369 headers:{"Content-Type":"application/json"}, 1370 body:JSON.stringify({url:`https://${activeDashboardHost}`}), 1371 }); 1372 if (epoch !== dashboardGeneration) return; 1373 message.textContent = paymentRecoveryRequired 1374 ? localized( 1375 "Opening Stripe to resolve the payment. Access resumes only after Stripe confirms the subscription is active.", 1376 "å³å°åå¾ Stripe èç仿¬¾ãåªæå¨ Stripe 確èªè¨é±æ¢å¾©ææå¾ï¼æ¬éæææ¢å¾©ã", 1377 ) 1378 : (localized("Opening Stripe. Access changes only after the provider confirms the update.", "å³å°åå¾ Stripeã宿å¾ä»é çå¾ ä»æ¬¾å¹³å°ç¢ºèªï¼æ¬éçæ æææ´æ°ã")); 1379 window.location.assign(result.portal_url); 1380 } catch (error) { 1381 if (epoch !== dashboardGeneration) return; 1382 if (error.status === 401 || error.status === 403) { showDashboardError(error); return; } 1383 message.textContent = `${localized("Unable to open billing management", "ç¡æ³éå仿¬¾ç®¡ç")}: ${error.message}`; 1384 manage.disabled = false; 1385 } 1386 }; 1387 }; 1388 const metricValue = (snapshot, group, id) => snapshot?.[group]?.[id]?.value; 1389 const fallbackHistory = (data) => { 1390 const axes = ["seo","aeo","geo","ago"]; 1391 const baselineScores = Object.fromEntries(axes.map((axis) => [axis, {value: score(data.historical_baseline?.dimensions?.[axis]?.score),status:"ready",proof_level:data.historical_baseline?.claim_level || "observed"}])); 1392 const currentScores = Object.fromEntries(axes.map((axis) => [axis, {value: score(data.readiness?.dimensions?.[axis]?.score),status:"ready",proof_level:"observed_public_fetch"}])); 1393 const comparisonScores = Object.fromEntries(axes.map((axis) => { 1394 const before = baselineScores[axis].value; const after = currentScores[axis].value; 1395 const comparable = Number.isFinite(before) && Number.isFinite(after); 1396 return [axis,{baseline:before,current:after,delta:comparable ? after-before : null,comparable}]; 1397 })); 1398 return {ok:true,schema:"agentx-customer-dashboard-history-v1",host:data.customer?.host || "",missing_data_policy:"pending_not_zero",snapshots:[ 1399 {snapshot_id:data.historical_baseline?.run_id || "baseline",observed_at:data.historical_baseline?.captured_at || "",scores:baselineScores,outcomes:{}}, 1400 {snapshot_id:"live-public-fetch",observed_at:dateKey(data.readiness?.observed_at),scores:currentScores,outcomes:{}}, 1401 ],comparison:{status:"ready",scores:comparisonScores,outcomes:{}},non_claims:["does_not_prove_incrementality_or_causality"]}; 1402 }; 1403 const renderHistory = (history) => { 1404 dashboardHistory = history; 1405 const snapshots = history?.snapshots || []; 1406 const content = document.querySelector("[data-history-content]"); 1407 const empty = document.querySelector("[data-history-empty]"); 1408 const available = snapshots.length > 0; 1409 const comparableWindow = snapshots.length >= 2; 1410 content?.toggleAttribute("hidden", !available); 1411 empty?.toggleAttribute("hidden", comparableWindow); 1412 if (empty) empty.textContent = snapshots.length === 0 1413 ? (localized("No measurements exist in the selected dates.", "æé¸æ¥æå §æ²æéæ¸¬è³æã")) 1414 : (localized("Only one period is available. A second inspection is required for comparison.", "ç®ååªæä¸æè³æï¼ä¸ä¸æ¬¡å·¡æª¢å¾æè½è¨ç®åå¾è®åã")); 1415 const trendGrid = document.querySelector("[data-trend-grid]"); 1416 trendGrid?.replaceChildren(); 1417 const comparisonGrid = document.querySelector("[data-comparison-grid]"); 1418 comparisonGrid?.replaceChildren(); 1419 comparisonGrid?.toggleAttribute("hidden", !comparableWindow); 1420 if (!available) return; 1421 ["seo","aeo","geo","ago"].forEach((axis) => { 1422 const article = document.createElement("article"); article.className = "trend-card"; 1423 const header = document.createElement("header"); 1424 const heading = document.createElement("h3"); heading.textContent = axis.toUpperCase(); 1425 const current = metricValue(snapshots.at(-1), "scores", axis); 1426 const currentText = document.createElement("b"); currentText.textContent = Number.isFinite(current) ? String(current) : "â"; 1427 header.append(heading,currentText); 1428 const bars = document.createElement("div"); bars.className = "trend-bars";
1429 snapshots.forEach((snapshot) => { 1430 const value = metricValue(snapshot,"scores",axis); 1431 const bar = document.createElement("i"); bar.className = "trend-point"; 1432 bar.style.setProperty("--value", Number.isFinite(value) ? Math.max(0,Math.min(100,value)) : 0); 1433 bar.title = `${snapshot.observed_at || (localized("Baseline", "åºæº"))}: ${Number.isFinite(value) ? value : (localized("Pending", "å¾ åå¾"))}`; 1434 bars.append(bar); 1435 }); 1436 const labels = document.createElement("div"); labels.className = "trend-labels"; 1437 const first = document.createElement("span"); first.textContent = snapshots[0]?.observed_at || (localized("Baseline", "åºæº")); 1438 const last = document.createElement("span"); last.textContent = snapshots.at(-1)?.observed_at || (localized("Current", "ç®å")); 1439 labels.append(first,last); article.append(header,bars,labels); trendGrid?.append(article); 1440 }); 1441 ["seo","aeo","geo","ago"].forEach((axis) => { 1442 const row = history.comparison?.scores?.[axis] || {}; 1443 const item = document.createElement("article"); item.className = "comparison-item"; 1444 const label = document.createElement("span"); label.textContent = `${axis.toUpperCase()} ${localized("change", "åå¾è®å")}`; 1445 const value = document.createElement("b"); 1446 value.textContent = row.comparable ? `${row.baseline} â ${row.current} (${row.delta > 0 ? "+" : ""}${row.delta})` : (localized("Not comparable yet", "å°ä¸å¯æ¯è¼")); 1447 if (row.comparable && row.delta > 0) value.className = "positive"; 1448 if (row.comparable && row.delta < 0) value.className = "negative"; 1449 item.append(label,value); comparisonGrid?.append(item); 1450 }); 1451 }; 1452 const render = (data) => { 1453 dashboardData = data; 1454 const failedMeasurement = data.readiness?.measurement?.status === "measurement_failed"; 1455 const current = score(data.readiness?.overall); 1456 const baseline = score(data.historical_baseline?.overall); 1457 const delta = data.comparison?.overall_delta; 1458 const ring = document.querySelector("[data-overall-ring]"); 1459 if (ring) ring.style.setProperty("--score", current ?? 0); 1460 text("[data-overall]", failedMeasurement ? null : current); 1461 text("[data-baseline]", baseline); 1462 text("[data-delta]", Number.isFinite(delta) ? `${delta > 0 ? "+" : ""}${delta}` : "â"); 1463 text("[data-observed-at]", formatDate(data.readiness?.observed_at)); 1464 const maturity = failedMeasurement 1465 ? localized("We could not measure this website yet", "ç®åç¡æ³é測網ç«") 1466 : current >= 85 ? localized("AI foundations are mature", "AI åºç¤æç") : current >= 60 ? localized("AI foundations exist with clear gaps", "AI åºç¤å·²å»ºç«ï¼ä»ææç¢ºç¼ºå£") : localized("AI foundations need priority work", "AI åºç¤ä»éåªå è£å¼·"); 1467 text("[data-summary-title]", maturity); 1468 const customerName = data.customer?.name || data.customer?.host || "the website"; 1469 text("[data-summary-copy]", failedMeasurement 1470 ? measurementFailureCopy({measurement:data.readiness?.measurement}) 1471 : localized("The mother service refetches {customer}'s public website. The score measures technical readiness, not AI ranking or conversion causality.", "忏ä¾èªæ¯é«éæ°è®å {customer} çå ¬éç¶²ç«ï¼ä¸ä½¿ç¨æå填寫ãå®è¡¡éæè¡æºå度ï¼ä¸ä»£è¡¨ AI æåææäº¤å æã").replace("{customer}", customerName)); 1472 text("[data-agent-id]", data.customer?.agent_id); 1473 text("[data-customer-name]", data.customer?.name || data.customer?.host); 1474 text("[data-identity-status]", data.customer?.verification_status); 1475 text("[data-dashboard-status]", data.customer?.verification_status || (localized("Identity pending", "身åå¾ ç¢ºèª"))); 1476 const identityLink = document.querySelector("[data-public-identity]"); 1477 if (identityLink && data.customer?.public_identity_url) identityLink.href = data.customer.public_identity_url; 1478 text("[data-run-id]", data.historical_baseline?.run_id); 1479 text("[data-result-hash]", data.historical_baseline?.result_hash); 1480 text("[data-captured-at]", formatDate(data.historical_baseline?.captured_at)); 1481 ["seo","aeo","geo","ago"].forEach((axis) => {
1482 const row = data.readiness?.dimensions?.[axis] || {}; 1483 const value = failedMeasurement ? null : score(row.score); 1484 text(`[data-axis-score="${axis}"]`, value); 1485 const fill = document.querySelector(`[data-axis-fill="${axis}"]`); 1486 if (fill) fill.style.width = `${value ?? 0}%`; 1487 text(`[data-axis-detail="${axis}"]`, failedMeasurement ? localized("Not measured","æªé測") : `${row.pass || 0} / ${row.partial || 0} / ${row.pending || 0}`); 1488 }); 1489 Object.entries(data.outcomes || {}).forEach(([id, metric]) => renderMetric(id, metric)); 1490 text("[data-read-total]", data.outcome_details?.ai_reads?.total ?? "â"); 1491 text("[data-citation-total]", data.outcome_details?.ai_citations?.total ?? "â"); 1492 text("[data-conversion-total]", data.outcome_details?.conversions?.ai_attributed ?? "â"); 1493 const tabs = [...document.querySelectorAll("[data-check-axis]")]; 1494 const renderChecks = (axis) => { 1495 tabs.forEach((tab) => tab.setAttribute("aria-selected", String(tab.dataset.checkAxis === axis))); 1496 const container = document.querySelector("[data-check-details]"); 1497 if (!container) return; 1498 container.replaceChildren(); 1499 const rows = data.checks_by_axis?.[axis] || []; 1500 text("[data-check-count]", `${rows.length} ${localized(rows.length === 1 ? "check" : "checks")}`); 1501 rows.forEach((item) => { 1502 const details = document.createElement("details"); 1503 details.className = `check-item status-${item.status || "pending"}`; 1504 const summary = document.createElement("summary"); 1505 const state = document.createElement("span"); state.className = "check-state"; state.textContent = localized(item.status || "pending"); 1506 const title = document.createElement("b"); title.textContent = localized(item.label || item.id); 1507 const importance = document.createElement("em"); importance.textContent = localized(item.importance || "medium"); 1508 summary.append(state, title, importance); 1509 const body = document.createElement("div"); body.className = "check-body"; 1510 const evidence = document.createElement("p"); evidence.innerHTML = `<strong>${localized("Evidence", "檢è")}</strong>`; 1511 evidence.append(document.createTextNode(` ${item.evidence || "No public evidence observed"}`)); 1512 const fix = document.createElement("p"); fix.innerHTML = `<strong>${localized("Action", "æ¹å")}</strong>`; 1513 fix.append(document.createTextNode(` ${item.recommended_fix || localized("No action required", "ç®åä¸é調æ´")}`)); 1514 body.append(evidence, fix); details.append(summary, body);
1514 container.append(details); 1515 }); 1516 }; 1517 tabs.forEach((tab) => { tab.onclick = () => renderChecks(tab.dataset.checkAxis); }); 1518 renderChecks(tabs.find((tab) => tab.getAttribute("aria-selected") === "true")?.dataset.checkAxis || "seo"); 1519 const list = document.querySelector("[data-priorities]"); 1520 list.replaceChildren(); 1521 (data.priorities || []).slice(0, 5).forEach((item) => { 1522 const article = document.createElement("article"); 1523 article.className = "priority"; 1524 const axis = document.createElement("span"); axis.className = "priority-axis"; axis.textContent = String(item.axis || "").toUpperCase(); 1525 const copy = document.createElement("div"); 1526 const title = document.createElement("b"); title.textContent = localized(item.label || item.id); 1527 const detail = document.createElement("p"); detail.textContent = item.recommended_fix || item.evidence || "Review evidence"; 1528 copy.append(title, detail); 1529 const importance = document.createElement("span"); importance.className = "importance"; importance.textContent = localized(item.importance || "medium"); 1530 article.append(axis, copy, importance); list.append(article); 1531 }); 1532 document.querySelector("[data-dashboard-loading]")?.setAttribute("hidden", ""); 1533 document.querySelector("[data-dashboard-content]")?.removeAttribute("hidden"); 1534 }; 1535 const fetchJson = async (path, options={}) => { 1536 const response = await fetch(path, {...options,headers:{Accept:"application/json",...(options.headers || {})}}); 1537 const data = await response.json(); 1538 if (!response.ok || !data.ok) { 1539 const error = new Error(data.error || `HTTP ${response.status}`); 1540 error.status = response.status; 1541 error.code = data.error || ""; 1542 throw error; 1543 } 1544 return data; 1545 }; 1546 const showHistoryError = (error) => { 1547 renderHistory({snapshots:[],comparison:{status:"unavailable"}}); 1548 const empty = document.querySelector("[data-history-empty]"); 1549 if (empty) empty.textContent = `${localized("Unable to load history", "æ·å²è³æè¼å ¥å¤±æ")}: ${error.message}`; 1550 }; 1551 const loadCustomerSite = async (host) => { 1552 if (!dashboardClient) throw new Error("dashboard_client_not_ready"); 1553 const epoch = ++dashboardGeneration; 1554 clearCustomerView(); 1555 document.querySelector("[data-dashboard-loading]")?.removeAttribute("hidden"); 1556 let bundle; 1557 try { bundle = await dashboardClient.loadSite(host); } 1558 catch (error) { if (epoch === dashboardGeneration) throw error; return; } 1559 if (epoch !== dashboardGeneration) return; 1560 const page = new URL(window.location.href); 1561 page.searchParams.set("site", bundle.host); 1562 page.searchParams.delete("login"); 1563 window.history.replaceState({}, "", page); 1564 return renderCustomerBundle(bundle); 1565 }; 1566 const historyQuery = (host, format="") => { 1567 const params = new URLSearchParams({url:`https://${host}`}); 1568 const from = document.querySelector("[data-history-from]")?.value; 1569 const to = document.querySelector("[data-history-to]")?.value; 1570 if (from) params.set("from",from); if (to) params.set("to",to); if (format) params.set("format",format); 1571 return params.toString(); 1572 }; 1573 const loadHistory = async (host) => { 1574 const epoch = dashboardGeneration; 1575 if (!host) return; 1576 try { 1577 const payload = await fetchJson(`/api/dashboard/history?${historyQuery(host)}`); 1578 if (epoch === dashboardGeneration && host === activeDashboardHost) renderHistory(payload); 1579 } catch (error) { 1580 if (epoch === dashboardGeneration && host === activeDashboardHost) { 1581 if (error.status === 401 || error.status === 403) showDashboardError(error); 1582 else showHistoryError(error); 1583 } 1584 } 1585 }; 1586 const dashboardReasonText = (detail="") => { 1587 const source = /dashboard_session_invalid_or_expired|HTTP 401/i.test(String(detail || "")) 1588 ? "This sign-in link has expired. Request a new one-time link." 1589 : "We could not confirm this sign-in. Request a new one-time link."; 1590 return window.XAgentI18n?.t ? window.XAgentI18n.t(source) : source; 1591 }; 1592 const setDashboardMode = (mode, detail="") => { 1593 document.body.dataset.dashboardMode = mode; 1594 const label = document.querySelector("[data-dashboard-mode-label]"); 1595 if (label) label.textContent = { 1596 demo: localized("PUBLIC DEMO", "å ¬é示ç¯"), 1597 customer: localized("CUSTOMER", "å®¢æ¶æ¨¡å¼"), 1598 login_required: localized("SIGN-IN REQUIRED", "éè¦ç»å ¥"), 1599 initializing: localized("INITIALIZING", "確èªä¸"), 1600 }[mode] || mode.toUpperCase(); 1601 document.querySelector("[data-dashboard-demo-banner]")?.toggleAttribute("hidden", mode !== "demo"); 1602 document.querySelector("[data-dashboard-auth-state]")?.toggleAttribute("hidden", mode !== "login_required"); 1603 document.querySelector("[data-dashboard-logout]")?.toggleAttribute("hidden", mode !== "customer"); 1604 document.querySelector("[data-site-picker-wrap]")?.toggleAttribute("hidden", mode !== "customer"); 1605 document.querySelector("[data-dashboard-status]")?.toggleAttribute("hidden", mode !== "customer"); 1606 if (mode === "login_required") { 1607 document.querySelector("[data-dashboard-content]")?.setAttribute("hidden", ""); 1608 document.querySelector("[data-dashboard-loading]")?.setAttribute("hidden", ""); 1609 const reason = document.querySelector("[data-dashboard-auth-reason]"); 1610 if (reason) reason.textContent = dashboardReasonText(detail); 1611 activeDashboardHost = ""; 1612 dashboardHistory = null; 1613 } 1614 }; 1615 const renderCustomerBundle = (bundle) => { 1616 activeDashboardHost = bundle.host; 1617 render(bundle.data); 1618 renderConnectors(bundle.connectors); 1619 renderTasks(bundle.tasks); 1620 renderBilling(bundle.billing); 1621 renderDecision(bundle.decision); 1622 window.AgentIdReportApi.publish("customer", bundle.host, bundle.data); 1623 const auditInput = document.getElementById("dashboard-audit-url"); 1624 if (auditInput) auditInput.value = `https://${bundle.host}`; 1625 return loadHistory(bundle.host); 1626 };
1627 const loadDashboard = async () => { 1628 const epoch = ++dashboardGeneration; 1629 clearCustomerView(); 1630 if (!window.AgentIdDashboardClient) throw new Error("dashboard_client_not_loaded"); 1631 dashboardClient = window.AgentIdDashboardClient.createDashboardClient({request:fetchJson}); 1632 const requestedSite = new URLSearchParams(window.location.search).get("site") || ""; 1633 let result; 1634 try { result = await dashboardClient.bootstrap(window.location.pathname, requestedSite); } 1635 catch (error) { if (epoch === dashboardGeneration) throw error; return; } 1636 if (epoch !== dashboardGeneration) return; 1637 if (result.mode === "demo") { 1638 setDashboardMode("demo"); 1639 publicDemoDashboard = true; 1640 activeDashboardHost = result.selectedHost; 1641 document.querySelectorAll("[data-history-form] input, [data-history-form] button[type=submit]").forEach((node) => { node.disabled = true; }); 1642 document.querySelector("[data-history-demo-note]")?.removeAttribute("hidden"); 1643 const auditInput = document.getElementById("dashboard-audit-url"); 1644 if (auditInput) auditInput.value = `https://${activeDashboardHost}`; 1645 render(result.data); renderConnectors(fallbackConnectors(result.data)); renderTasks({ok:true,tasks:[]}); renderBilling(null); renderDecision(null); renderHistory(fallbackHistory(result.data)); 1646 window.AgentIdReportApi.publish("demo", result.selectedHost, result.data); 1647 return; 1648 } 1649 if (result.mode === "login_required") { 1650 setDashboardMode("login_required", result.reason); 1651 return; 1652 } 1653 if (result.mode !== "customer") throw new Error("dashboard_mode_invalid"); 1654 setDashboardMode("customer"); 1655 publicDemoDashboard = false; 1656 const sites = result.sites || []; 1657 const picker = document.querySelector("[data-site-picker]"); 1658 picker.replaceChildren(...sites.map((site) => { 1659 const option = document.createElement("option"); 1660 option.value = site.host; option.textContent = site.name || site.host; return option; 1661 })); 1662 picker.value = result.selectedHost; 1663 picker.onchange = () => loadCustomerSite(picker.value).catch(showDashboardError); 1664 if (result.requestedSiteRejected) { 1665 const page = new URL(window.location.href); 1666 page.searchParams.set("site", result.selectedHost); 1667 window.history.replaceState({}, "", page); 1668 } 1669 return renderCustomerBundle(result.bundle); 1670 }; 1671 const showDashboardError = (error) => { 1672 if (error?.code === "dashboard_request_superseded") return; 1673 ++dashboardGeneration; 1674 clearCustomerView(); 1675 const loading = document.querySelector("[data-dashboard-loading]"); 1676 if (!loading) return; 1677 const expired = error?.status === 401 || ["dashboard_session_invalid_or_expired", "HTTP 401"].some((value) => String(error.message).includes(value)); 1678 if (expired) { 1679 setDashboardMode("login_required"); 1680 return; 1681 } 1682 loading.removeAttribute("hidden"); 1683 loading.textContent = `${localized("Unable to load evidence", "è³æè¼å ¥å¤±æ")}: ${error.message}`; 1684 }; 1685 loadDashboard().catch(showDashboardError); 1686 window.addEventListener("pagehide", () => { ++dashboardGeneration; clearCustomerView(); }); 1687 window.addEventListener("pageshow", (event) => { 1688 if (event.persisted) loadDashboard().catch(showDashboardError); 1689 }); 1690 1691 document.querySelector("[data-dashboard-logout]")?.addEventListener("click", async () => { 1692 ++dashboardGeneration; 1693 clearCustomerView(); 1694 try { await dashboardClient?.logout(); } finally { window.location.replace("/dashboard?logout=1"); } 1695 }); 1696 1697 document.querySelector("[data-history-form]")?.addEventListener("submit", (event) => { 1698 event.preventDefault(); 1699 loadHistory(activeDashboardHost).catch(showDashboardError); 1700 }); 1701 document.querySelectorAll("[data-export]").forEach((button) => button.addEventListener("click", () => { 1702 const format = button.dataset.export; 1703 if (!publicDemoDashboard) { 1704 window.location.href = `/api/dashboard/export?${historyQuery(activeDashboardHost,format)}`; 1705 return; 1706 } 1707 const body = format === "json" ? JSON.stringify(dashboardHistory,null,2) : [ 1708 "snapshot_id,observed_at,seo,aeo,geo,ago",
1709 ...(dashboardHistory?.snapshots || []).map((row) => [row.snapshot_id,row.observed_at,...["seo","aeo","geo","ago"].map((axis) => metricValue(row,"scores",axis) ?? "")].join(",")), 1710 ].join("\n"); 1711 const blob = new Blob([body],{type:format === "json" ? "application/json" : "text/csv"}); 1712 const link = document.createElement("a"); link.href = URL.createObjectURL(blob); link.download = `twin3-agent-id-report.${format}`; link.click(); URL.revokeObjectURL(link.href); 1713 })); 1714 document.querySelector("[data-print-report]")?.addEventListener("click", () => window.print()); 1715 1716 const setupDialog = document.querySelector("[data-connector-dialog]"); 1717 const setupCatalog = { 1718 a1: { title:"AI ç¬è²è®åç´é / AI crawler reads", copy:"è® Cloudflare æ¯ 15 åéæå·²å»é¤æ¥è©¢åæ¸ç AI ç¬è²çµ±è¨é忝é«ã", requirements:["Cloudflare Zone ID","å¯è® Analytics Token","Site Agent Key"], command:(host)=>`SITE_AGENT_KEY=... CLOUDFLARE_API_TOKEN=... CLOUDFLARE_ZONE_ID=... npx --package @twin3-ai/agent-id agent-id-cloudflare-a1 --url https://${host} --once` }, 1719 a2: { title:"AI å¼ç¨æ¸¬è©¦ / AI citation probes", copy:"åºå®æ¡è³¼åé¡è模åï¼æè½æ¯è¼æ¯ææ¯å¦çç被æåèå¼ç¨ã", requirements:["æ¡è³¼åé¡ä¸»é¡","è¦æ¸¬è©¦ç AI 弿","Site Agent Key"], command:(host)=>`npx --package @twin3-ai/agent-id agent-id a2-probe-set https://${host} --topic "your buyer question" --engine ChatGPT,Gemini,Claude` }, 1720 b1: { title:"GA4 æè¨å®æ¸å / Conversion attribution", copy:"GA4 顯示 AI 便ºééµäºä»¶ï¼CRM/è¨å®å¯åºæç¢ºèªè©¢å¹èæäº¤ã", requirements:["GA4 Property ID æ CRM JSON å¯åº","å¯è® OAuth Token","Site Agent Key"], command:(host)=>`SITE_AGENT_KEY=... GA4_PROPERTY_ID=... GA4_ACCESS_TOKEN=... npx --package @twin3-ai/agent-id agent-id-b1-sync --ga4 --url https://${host}` } 1721 }; 1722 document.querySelectorAll("[data-connector-setup]").forEach((button)=>button.addEventListener("click",()=>{ 1723 const item=setupCatalog[button.dataset.connectorSetup]; if(!item||!setupDialog)return; 1724 setupDialog.querySelector("[data-setup-title]").textContent=item.title; 1725 setupDialog.querySelector("[data-setup-copy]").textContent=item.copy; 1726 setupDialog.querySelector("[data-setup-requirements]").innerHTML=item.requirements.map((value,index)=>`<div>${index+1}. ${value}</div>`).join(""); 1727 setupDialog.querySelector("[data-setup-command]").textContent=item.command(activeDashboardHost || URL_PLACEHOLDER); 1728 setupDialog.showModal(); 1729 })); 1730 document.querySelector("[data-copy-setup]")?.addEventListener("click",async()=>{const command=document.querySelector("[data-setup-command]")?.textContent||"";await navigator.clipboard.writeText(command);}); 1731 document.querySelector("[data-check-setup]")?.addEventListener("click",()=>loadDashboard().catch(showDashboardError)); 1732 1733 const auditForm = document.querySelector("[data-audit-form]"); 1734 auditForm?.addEventListener("submit", async (event) => { 1735 event.preventDefault(); 1736 const epoch = dashboardGeneration; 1737 const target = normalizeUrl(document.getElementById("dashboard-audit-url")?.value || ""); 1738 const progress = document.querySelector("[data-audit-progress]"); 1739 const progressText = document.querySelector("[data-audit-progress-text]"); 1740 const result = document.querySelector("[data-audit-quick-result]"); 1741 progress?.removeAttribute("hidden"); result?.setAttribute("hidden", ""); 1742 if (progressText) progressText.textContent = localized("Reading public pages, structured data, and Agent filesâ¦", "æ£å¨è®åå ¬éé é¢ãçµæ§åè³æè Agent è¨å®æªâ¦"); 1743 try { 1744 const response = await fetch(`/api/agent/audit_report?url=${encodeURIComponent(target)}`, {headers:{Accept:"application/json"}}); 1745 const payload = await response.json(); 1746 if (epoch !== dashboardGeneration) return; 1747 const report = payload.report || payload; 1748 if (!response.ok || payload.ok === false) throw new Error(payload.error || `HTTP ${response.status}`); 1749 if (measurementFailed(report)) { 1750 result.textContent = `${target} · ${measurementFailureCopy(report)}`; 1751 result.removeAttribute("hidden"); 1752 return; 1753 } 1754 const axes = report.scorecard || {}; 1755 result.textContent = `${target} · ${report.overall_score ?? "â"}/100 · SEO ${axes.seo?.score ?? "â"} · AEO ${axes.aeo?.score ?? "â"} · GEO ${axes.geo?.score ?? "â"} · AGO ${axes.ago?.score ?? "â"}`; 1756 result.removeAttribute("hidden"); 1757 } catch (error) { 1758 if (epoch !== dashboardGeneration) return; 1759 result.textContent = `${localized("Audit failed", "檢測失æ")}: ${error.message}`; 1760 result.removeAttribute("hidden"); 1761 } finally { if (epoch === dashboardGeneration) progress?.setAttribute("hidden", ""); } 1762 }); 1763})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.