Адаптер Web Shell для демона
Цель
Веб-чат и клиенты веб-терминала должны потреблять qwen serve через HTTP/SSE API демона и отрисовывать транскрипт на стороне клиента. Нативные локальные TUI, канальные и IDE-интеграции пока сохраняют свои существующие пути по умолчанию.
Общий контракт UI
Используйте экспорты UI демона из TypeScript SDK как общую границу:
import {
DaemonClient,
DaemonSessionClient,
createDaemonTranscriptStore,
normalizeDaemonEvent,
} from ' @qwen-code/sdk/daemon';Разделение:
DaemonClientобрабатывает HTTP-маршруты демона.DaemonSessionClientотвечает за создание/присоединение к сессии и воспроизведение SSE.normalizeDaemonEvent()преобразует сетевые события демона в события UI.createDaemonTranscriptStore()сворачивает события UI в блоки транскрипта.
React-клиенты могут использовать привязку, экспортируемую из Web Shell:
import {
DaemonSessionProvider,
useActions,
useConnection,
usePendingPermissions,
useTranscriptBlocks,
} from ' @qwen-code/web-shell/daemon-react-sdk';Минимальная React-форма:
function App() {
return (
<DaemonSessionProvider baseUrl="http://127.0.0.1:4170">
<Transcript />
<PromptBox />
</DaemonSessionProvider>
);
}
function Transcript() {
const blocks = useTranscriptBlocks();
return blocks.map((block) => <RenderBlock key={block.id} block={block} />);
}Провайдер создаёт или присоединяется к сессии демона, подписывается на SSE, хранит последний event id на DaemonSessionClient и по умолчанию переподключает поток. Вызывающий код может отключить это через autoReconnect={false} для тестов или собственного управления подключением.
Варианты развёртывания в браузере
Локальный POC с одним источником
Страница, обслуживаемая демоном, может обращаться к демону напрямую, так как страница и API имеют один источник. Это предпочтительная форма раннего POC для валидации локального веб-чата и веб-терминала.
Удалённый веб-чат / веб-терминал
Продакшен-удалённое веб-приложение обычно должно обращаться к backend-for-frontend. BFF владеет URL демона, токеном, маршрутизацией рабочих пространств и метаданными сессии, а затем перенаправляет безопасные для браузера события приложения в браузер. Это исключает хранение bearer-токенов в браузере и позволяет развёртыванию определять, к какому демону/рабочему пространству пользователь может обращаться.
Локальный браузер против локального демона
Отдельный локальный dev-сервер имеет другой источник, чем qwen serve; он должен либо проксировать маршруты демона через тот же источник, либо обслуживаться самим демоном. Демон намеренно отклоняет произвольные запросы с браузерным Origin.
Ответственности рендеринга
Общая модель транскрипта семантическая, а не визуальная. UI-клиенты сами решают, как отрисовывать:
- блоки сообщений пользователя и ассистента
- свёрнутые блоки рассуждений
- карточки статуса инструментов
- блоки вывода оболочки
- элементы управления запросами разрешений
- блоки статуса/ошибок/отладки
Веб-терминал — это браузерный семантический рендерер. Он должен выглядеть и ощущаться как терминал с моноширинной раскладкой, прокруткой, вводом промпта, горячими клавишами и потоковыми блоками, но он не является прямым PTY-прокси и не требует серверного рендеринга Ink.
Безопасность слияния
- Нативный TUI
qwenостаётся прямым и неизменным. - Пути
--acp, каналов и IDE остаются неизменными по умолчанию. - Ядро UI SDK является аддитивным.
- Привязка Web Shell React опциональна и работает только в клиентах, которые её импортируют.
- Удалённый код демонстрационного TUI демона не должен рассматриваться как миграция продукта.
Дальнейшие шаги
- Синхронизировать поведение Web Shell, обслуживаемого демоном, и встроенного хоста IDE.
- Продолжить создание первоклассных рендереров чата и терминала на блоках транскрипта.
- Добавлять более богатые типизированные события только там, где существующие события демона слишком низкоуровневые для стабильного поведения UI в браузере.
- Рассмотреть выделенный пакет
@qwen-code/daemon-ui-core, если потребители вне SDK нуждаются в ядре UI как в независимой зависимости.