Surface — agent UI'sini AFAI ichida ko'rsatish

Agentingizning o'z sahifasi AFAI interfeysi ichida ochiladi: sozlamalar formasi, hisobot paneli, suhbat yoni — manifestda qayerda ko'rinishini o'zingiz tanlaysiz.


1. E'lon qilish


"surfaces": [
  { "id": "main",   "location": "agent.page",     "url": "https://acme.uz/panel" },
  { "id": "widget", "location": "dashboard.card", "url": "https://acme.uz/card", "height": 240 }
]
locationQayerda
agent.pageAgentning to'liq sahifasi
dashboard.cardBosh sahifadagi karta
workspace.sidebarO'ng paneldagi yon oyna
chat.panelSuhbat yonidagi panel
settings.tabSozlamalar ichidagi tab
office.deskPiksel ofisdagi stolni bosganda

url HTTPS bo'lishi shart (ishlab chiqishda http://localhost:* mumkin).


2. Nima uchun iframe

amoCRM widget'i mijoz sahifasiga to'g'ridan-to'g'ri JS qo'yadi. Qulay, lekin: bitta buzuq widget butun CRM'ni sindiradi, boshqa widget'ning ma'lumotini o'qiy oladi va sahifadagi tokenlarga kira oladi.

Bizda sahifangiz sandbox iframe'da ochiladi:


<iframe sandbox="allow-scripts allow-forms" referrerpolicy="no-referrer" …>

Ya'ni: cookie yo'q, AFAI DOM'iga kirish yo'q, top-level navigatsiya yo'q. Aloqa faqat postMessage orqali.


3. Brauzer SDK


<script type="module">
  import { afai } from "https://cdn.afai.uz/sdk/afai-surface.js";

  const ctx = await afai.ready();
  // { user: {id, name, email}, workspace: {id}, installation_id, locale, theme }

  document.body.dataset.theme = ctx.theme;      // AFAI mavzusiga moslashing
  afai.autoResize();                            // balandlikni o'zi kuzatadi

  const { token } = await afai.getToken();      // 5 daqiqalik token
  const response = await fetch("/api/my-data", {
    headers: { "X-AFAI-Surface-Token": token },
  });

  afai.notify("Saqlandi", "success");
  afai.navigate("/agents");                     // AFAI ichida o'tish
</script>
MetodQaytaradiIzoh
ready()kontekstBirinchi chaqiruv. Host'ga «tayyorman» deydi
getToken(){token, expires_in}Har chaqiruvda yangi, faqat o'qish
resize(px)—Iframe balandligi (maks. 2000)
autoResize()tozalash funksiyasiResizeObserver bilan avtomatik
notify(msg, level)—AFAI toast'i
navigate(path)—Faqat ichki yo'llar (/…)
on(event, handler)unsubscribeHost hodisalari

4. Token qanday ishlaydi


iframe → afai.getToken()
       → AFAI host → POST /console/installations/{id}/surface-token
       → 5 daqiqalik JWT (faqat `:read` scope'lar)
       → iframe → sizning backend
       → POST /oauth/introspect  ← siz tekshirasiz

Backend tomonda:


info = await client.post(f"{AFAI}/oauth/introspect", data={"token": token})
if not info.json()["active"]:
    raise Unauthorized()
user_id = info.json()["sub"]
workspace_id = info.json()["workspace_id"]

Brauzerda hech qanday doimiy sir saqlanmaydi. Token o'g'irlansa ham 5 daqiqadan keyin o'ladi va u bilan yozib bo'lmaydi (403 disposable_token_readonly).


5. Dizayn tavsiyalari

  • Mavzuga moslashing. ctx.theme → light yoki dark.
  • O'z skrollingizni yasamang. autoResize() ishlating — sahifa ichida
  • sahifa eng yomon UX.

  • Tilni oling. ctx.locale (uz / ru / en).
  • Yuklanish holatini ko'rsating. iframe sekin yuklansa mijoz bo'sh
  • to'rtburchak ko'radi.

  • Kichik boshlang. dashboard.card — 240px atrofida, faqat eng muhim
  • ko'rsatkich.


    6. Lokal sinash

    
    # Sahifangizni ko'taring
    python -m http.server 8300
    
    # Manifestda
    "surfaces": [{ "id": "main", "location": "agent.page", "url": "http://localhost:8300/panel.html" }]
    

    AFAI /agents sahifasida agentni oching. Konsolda postMessage xatolari chiqsa — event.origin mos kelmayapti, url ni manifestdagi bilan bir xil qiling (port ham).