Control UI (เบราว์เซอร์)

Control UI เป็นแอป single-page ขนาดเล็ก Vite + Lit ที่เสิร์ฟโดย Gateway:

  • ค่าเริ่มต้น: http://<host>:18789/
  • คำนำหน้าตัวเลือก: ตั้งค่า gateway.controlUi.basePath (เช่น /mayros)

มันพูดคุย โดยตรงกับ Gateway WebSocket บนพอร์ตเดียวกัน

เปิดอย่างรวดเร็ว (โลคัล)

หาก Gateway ทำงานบนคอมพิวเตอร์เดียวกัน ให้เปิด:

หากหน้าเว็บโหลดไม่ได้ ให้เริ่ม Gateway ก่อน: mayros gateway

การพิสูจน์ตัวตนถูกจัดหาระหว่างการจับมือ WebSocket ผ่าน:

  • connect.params.auth.token
  • connect.params.auth.password แผงการตั้งค่าแดชบอร์ดให้คุณเก็บโทเค็น รหัสผ่านไม่ถูกเก็บถาวร วิซาร์ด onboarding สร้างโทเค็น gateway ตามค่าเริ่มต้น ดังนั้นวางมันที่นี่ในการเชื่อมต่อครั้งแรก

การจับคู่อุปกรณ์ (การเชื่อมต่อครั้งแรก)

เมื่อคุณเชื่อมต่อกับ Control UI จากเบราว์เซอร์หรืออุปกรณ์ใหม่ Gateway ต้องการ การอนุมัติการจับคู่ครั้งเดียว — แม้ว่าคุณจะอยู่บน Tailnet เดียวกัน กับ gateway.auth.allowTailscale: true นี่เป็นมาตรการรักษาความปลอดภัยเพื่อป้องกัน การเข้าถึงที่ไม่ได้รับอนุญาต

สิ่งที่คุณจะเห็น: "disconnected (1008): pairing required"

เพื่ออนุมัติอุปกรณ์:

bash
# แสดงคำขอที่รอดำเนินการ
mayros devices list

# อนุมัติโดย request ID
mayros devices approve <requestId>

เมื่ออนุมัติแล้ว อุปกรณ์จะถูกจดจำและจะไม่ต้องการการอนุมัติอีกเว้นแต่ คุณเพิกถอนมันด้วย mayros devices revoke --device <id> --role <role> ดู Devices CLI สำหรับการหมุนเวียนโทเค็นและการเพิกถอน

หมายเหตุ:

  • การเชื่อมต่อโลคัล (127.0.0.1) ได้รับการอนุมัติอัตโนมัติ
  • การเชื่อมต่อระยะไกล (LAN, Tailnet, ฯลฯ) ต้องการการอนุมัติที่ชัดเจน
  • แต่ละโปรไฟล์เบราว์เซอร์สร้าง device ID ที่ไม่ซ้ำ ดังนั้นการสลับเบราว์เซอร์หรือ ล้างข้อมูลเบราว์เซอร์จะต้องการการจับคู่อีกครั้ง

สิ่งที่มันสามารถทำได้ (วันนี้)

  • แชทกับโมเดลผ่าน Gateway WS (chat.history, chat.send, chat.abort, chat.inject)
  • สตรีมการเรียกเครื่องมือ + การ์ดเอาต์พุตเครื่องมือสดใน Chat (เหตุการณ์ agent)
  • ช่องทาง: WhatsApp/Telegram/Discord/Slack + ช่องทางปลั๊กอิน (Mattermost, ฯลฯ) สถานะ + การล็อกอิน QR + การตั้งค่าต่อช่องทาง (channels.status, web.login.*, config.patch)
  • Instances: รายการ presence + รีเฟรช (system-presence)
  • เซสชัน: รายการ + การแทนที่ thinking/verbose ต่อเซสชัน (sessions.list, sessions.patch)
  • งาน Cron: รายการ/เพิ่ม/รัน/เปิดใช้งาน/ปิดใช้งาน + ประวัติการรัน (cron.*)
  • Skills: สถานะ เปิดใช้งาน/ปิดใช้งาน ติดตั้ง การอัปเดต API key (skills.*)
  • โหนด: รายการ + capabilities (node.list)
  • การอนุมัติ Exec: แก้ไข allowlists ของ gateway หรือโหนด + นโยบายการขอสำหรับ exec host=gateway/node (exec.approvals.*)
  • การตั้งค่า: ดู/แก้ไข ~/.mayros/mayros.json (config.get, config.set)
  • การตั้งค่า: ใช้ + รีสตาร์ทพร้อมการตรวจสอบความถูกต้อง (config.apply) และปลุกเซสชันที่ active ล่าสุด
  • การเขียนการตั้งค่ามี base-hash guard เพื่อป้องกันการเขียนทับการแก้ไขที่เกิดขึ้นพร้อมกัน
  • สคีมาการตั้งค่า + การเรนเดอร์ฟอร์ม (config.schema รวมถึงสคีมาปลั๊กอินและช่องทาง); ตัวแก้ไข Raw JSON ยังคงใช้งานได้
  • Debug: สถานะ/สุขภาพ/โมเดล snapshots + บันทึกเหตุการณ์ + การเรียก RPC ด้วยตนเอง (status, health, models.list)
  • บันทึก: ติดตามสดของบันทึกไฟล์ gateway พร้อมตัวกรอง/ส่งออก (logs.tail)
  • อัปเดต: รันการอัปเดต package/git + รีสตาร์ท (update.run) พร้อมรายงานการรีสตาร์ท

หมายเหตุแผง Cron jobs:

  • สำหรับ isolated jobs delivery จะ default เป็นการประกาศสรุป คุณสามารถเปลี่ยนเป็น none ได้หากต้องการรันภายในเท่านั้น
  • ฟิลด์ช่องทาง/เป้าหมายจะปรากฏเมื่อเลือก announce
  • โหมด Webhook ใช้ delivery.mode = "webhook" กับ delivery.to ที่ตั้งค่าเป็น URL webhook HTTP(S) ที่ถูกต้อง
  • สำหรับ main-session jobs โหมดการส่ง webhook และ none พร้อมใช้งาน
  • ตั้งค่า cron.webhookToken เพื่อส่ง bearer token เฉพาะ หากไม่ระบุ webhook จะถูกส่งโดยไม่มี auth header
  • Deprecated fallback: stored legacy jobs กับ notify: true ยังคงใช้ cron.webhook ได้จนกว่าจะย้าย

พฤติกรรมแชท

  • chat.send เป็น non-blocking: มันตอบรับทันทีด้วย { runId, status: "started" } และการตอบกลับสตรีมผ่านเหตุการณ์ chat
  • การส่งซ้ำด้วย idempotencyKey เดียวกันส่งคืน { status: "in_flight" } ในขณะที่รัน และ { status: "ok" } หลังจากเสร็จสิ้น
  • chat.history responses มีขนาดจำกัดเพื่อความปลอดภัยของ UI เมื่อ transcript entries ใหญ่เกินไป Gateway อาจตัดฟิลด์ข้อความยาว ลบบล็อก metadata หนัก และแทนที่ข้อความขนาดใหญ่เกินไปด้วย placeholder ([chat.history omitted: message too large])
  • chat.inject แนบโน้ต assistant ไปยัง transcript เซสชันและออกอากาศเหตุการณ์ chat สำหรับการอัปเดต UI เท่านั้น (ไม่มีการรัน agent ไม่มีการส่งช่องทาง)
  • หยุด:
    • คลิก Stop (เรียก chat.abort)
    • พิมพ์ /stop (หรือ stop|esc|abort|wait|exit|interrupt) เพื่อยกเลิก out-of-band
    • chat.abort รองรับ { sessionKey } (ไม่มี runId) เพื่อยกเลิกการรันที่ active ทั้งหมดสำหรับเซสชันนั้น
  • การเก็บรักษาบางส่วนเมื่อยกเลิก:
    • เมื่อการรันถูกยกเลิก ข้อความ assistant บางส่วนยังคงแสดงใน UI ได้
    • Gateway เก็บข้อความ assistant บางส่วนที่ถูกยกเลิกลงในประวัติ transcript เมื่อมี buffered output
    • รายการที่เก็บรวมเมตาดาต้าการยกเลิกเพื่อให้ผู้บริโภค transcript สามารถแยกแยะ abort partials จากผลลัพธ์การเสร็จสิ้นปกติ

การเข้าถึง Tailnet (แนะนำ)

Integrated Tailscale Serve (ต้องการ)

เก็บ Gateway ไว้ที่ loopback และให้ Tailscale Serve พร็อกซีมันด้วย HTTPS:

bash
mayros gateway --tailscale serve

เปิด:

  • https://<magicdns>/ (หรือ gateway.controlUi.basePath ที่คุณกำหนดค่า)

ตามค่าเริ่มต้น Control UI/WebSocket Serve requests สามารถพิสูจน์ตัวตนผ่าน Tailscale identity headers (tailscale-user-login) เมื่อ gateway.auth.allowTailscale เป็น true Mayros ตรวจสอบ identity โดยการ resolve ที่อยู่ x-forwarded-for ด้วย tailscale whois และจับคู่กับ header และยอมรับเหล่านี้เฉพาะเมื่อ request มาถึง loopback พร้อมกับ Tailscale x-forwarded-* headers ตั้งค่า gateway.auth.allowTailscale: false (หรือบังคับ gateway.auth.mode: "password") หากคุณต้องการให้ต้องมีโทเค็น/รหัสผ่านแม้สำหรับ Serve traffic Tokenless Serve auth ถือว่า gateway host เชื่อถือได้ หากโค้ดโลคัลที่ไม่เชื่อถือ อาจรันบนโฮสต์นั้น ให้ต้องการ token/password auth

ผูกกับ tailnet + โทเค็น

bash
mayros gateway --bind tailnet --token "$(openssl rand -hex 32)"

จากนั้นเปิด:

  • http://<tailscale-ip>:18789/ (หรือ gateway.controlUi.basePath ที่คุณกำหนดค่า)

วางโทเค็นลงในการตั้งค่า UI (ส่งเป็น connect.params.auth.token)

HTTP ที่ไม่ปลอดภัย

หากคุณเปิดแดชบอร์ดผ่าน HTTP ธรรมดา (http://<lan-ip> หรือ http://<tailscale-ip>) เบราว์เซอร์ทำงานใน non-secure context และบล็อก WebCrypto ตามค่าเริ่มต้น Mayros บล็อก การเชื่อมต่อ Control UI โดยไม่มีอัตลักษณ์อุปกรณ์

แก้ไขที่แนะนำ: ใช้ HTTPS (Tailscale Serve) หรือเปิด UI ในโลคัล:

  • https://<magicdns>/ (Serve)
  • http://127.0.0.1:18789/ (บนโฮสต์ gateway)

พฤติกรรม insecure-auth toggle:

json5
{
  gateway: {
    controlUi: { allowInsecureAuth: true },
    bind: "tailnet",
    auth: { mode: "token", token: "replace-me" },
  },
}

allowInsecureAuth ไม่ bypass Control UI device identity หรือการตรวจสอบการจับคู่

Break-glass เท่านั้น:

json5
{
  gateway: {
    controlUi: { dangerouslyDisableDeviceAuth: true },
    bind: "tailnet",
    auth: { mode: "token", token: "replace-me" },
  },
}

dangerouslyDisableDeviceAuth ปิดใช้งานการตรวจสอบ device identity ของ Control UI และเป็น ความปลอดภัยที่ลดระดับลงอย่างรุนแรง คืนค่าอย่างรวดเร็วหลังจากการใช้งานฉุกเฉิน

ดู Tailscale สำหรับคำแนะนำการตั้งค่า HTTPS

การสร้าง UI

Gateway เสิร์ฟไฟล์แบบคงที่จาก dist/control-ui สร้างพวกเขาด้วย:

bash
pnpm ui:build # ติดตั้ง UI deps อัตโนมัติในการรันครั้งแรก

Base สัมบูรณ์ตัวเลือก (เมื่อคุณต้องการ URL asset คงที่):

bash
MAYROS_CONTROL_UI_BASE_PATH=/mayros/ pnpm ui:build

สำหรับการพัฒนาในโลคัล (เซิร์ฟเวอร์พัฒนาแยก):

bash
pnpm ui:dev # ติดตั้ง UI deps อัตโนมัติในการรันครั้งแรก

จากนั้นชี้ UI ไปที่ Gateway WS URL ของคุณ (เช่น ws://127.0.0.1:18789)

การดีบัก/ทดสอบ: dev server + Gateway ระยะไกล

Control UI เป็นไฟล์แบบคงที่; เป้าหมาย WebSocket สามารถกำหนดค่าได้และอาจ แตกต่างจาก HTTP origin นี่สะดวกเมื่อคุณต้องการ Vite dev server ในโลคัลแต่ Gateway ทำงานที่อื่น

  1. เริ่ม UI dev server: pnpm ui:dev
  2. เปิด URL เช่น:
text
http://localhost:5173/?gatewayUrl=ws://<gateway-host>:18789

การพิสูจน์ตัวตนครั้งเดียว (หากจำเป็น):

text
http://localhost:5173/?gatewayUrl=wss://<gateway-host>:18789&token=<gateway-token>

หมายเหตุ:

  • gatewayUrl ถูกเก็บใน localStorage หลังจากโหลดและถูกลบออกจาก URL
  • token ถูกเก็บใน localStorage; password ถูกเก็บในหน่วยความจำเท่านั้น
  • เมื่อตั้งค่า gatewayUrl UI จะไม่ fall back ไปที่ config หรือ environment credentials ให้ระบุ token (หรือ password) อย่างชัดเจน credentials ที่ขาดหายไปที่ชัดเจนคือ error
  • ใช้ wss:// เมื่อ Gateway อยู่หลัง TLS (Tailscale Serve, HTTPS proxy, ฯลฯ)
  • gatewayUrl ยอมรับเฉพาะในหน้าต่างระดับบนสุด (ไม่ฝัง) เพื่อป้องกัน clickjacking
  • สำหรับ cross-origin dev setups (เช่น pnpm ui:dev ไปยัง Gateway ระยะไกล) เพิ่ม UI origin ไปที่ gateway.controlUi.allowedOrigins

ตัวอย่าง:

json5
{
  gateway: {
    controlUi: {
      allowedOrigins: ["http://localhost:5173"],
    },
  },
}

รายละเอียดการตั้งค่าการเข้าถึงระยะไกล: การเข้าถึงระยะไกล