Agentingizning o'z sahifasi AFAI interfeysi ichida ochiladi: sozlamalar formasi, hisobot paneli, suhbat yoni — manifestda qayerda ko'rinishini o'zingiz tanlaysiz.
"surfaces": [
{ "id": "main", "location": "agent.page", "url": "https://acme.uz/panel" },
{ "id": "widget", "location": "dashboard.card", "url": "https://acme.uz/card", "height": 240 }
]
location | Qayerda |
agent.page | Agentning to'liq sahifasi |
dashboard.card | Bosh sahifadagi karta |
workspace.sidebar | O'ng paneldagi yon oyna |
chat.panel | Suhbat yonidagi panel |
settings.tab | Sozlamalar ichidagi tab |
office.desk | Piksel ofisdagi stolni bosganda |
url HTTPS bo'lishi shart (ishlab chiqishda http://localhost:* mumkin).
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.
<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>
| Metod | Qaytaradi | Izoh |
ready() | kontekst | Birinchi chaqiruv. Host'ga «tayyorman» deydi |
getToken() | {token, expires_in} | Har chaqiruvda yangi, faqat o'qish |
resize(px) | — | Iframe balandligi (maks. 2000) |
autoResize() | tozalash funksiyasi | ResizeObserver bilan avtomatik |
notify(msg, level) | — | AFAI toast'i |
navigate(path) | — | Faqat ichki yo'llar (/…) |
on(event, handler) | unsubscribe | Host hodisalari |
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).
ctx.theme → light yoki dark.autoResize() ishlating — sahifa ichidasahifa eng yomon UX.
ctx.locale (uz / ru / en).to'rtburchak ko'radi.
dashboard.card — 240px atrofida, faqat eng muhimko'rsatkich.
# 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).