Skip to Content
Руководство для разработчиковDaemonБыстрый старт и операции

Быстрый старт и операции

На этой странице рассматривается, как запустить qwen serve, как проверить его работоспособность и как выглядит внутренняя цепочка вызовов от qwen serve до слушающего сервера. Архитектура, компоненты и детали сетевого протокола описаны на других страницах с подробным разбором демона.

1. Кратчайший путь

qwen serve

Вывод:

qwen serve listening on http://127.0.0.1:4170 (mode=http-bridge, workspace=/your/cwd) qwen serve: bound to workspace "/your/cwd" qwen serve: bearer auth disabled (loopback default). Set QWEN_SERVER_TOKEN to enable.

Откройте http://127.0.0.1:4170/ в браузере, чтобы получить Web Shell UI: чат, список сессий и инспекцию рабочего пространства. createServeApp() монтирует bundled Web Shell ассеты (packages/cli/src/serve/web-shell-static.ts) до bearerAuth, поэтому сам shell загружается без токена; его собственные API-вызовы передают bearer, если он настроен — запустите демон с --open (который помещает токен в фрагмент URL, никогда не отправляемый на сервер) или добавьте #token=… вручную, если аутентификация включена. --no-web отключает UI и оставляет демон API-only.

2. Рецепты запуска

# 1. Локальная разработка по умолчанию (loopback, без токена) qwen serve # 2. Явное указание рабочего пространства + эфемерный порт qwen serve --workspace /path/to/repo --port 0 # 3. Защищенная loopback-разработка (принудительный bearer даже для loopback) QWEN_SERVER_TOKEN=$(openssl rand -hex 32) qwen serve --require-auth # 4. Доступ из локальной сети (не-loopback требует токен) QWEN_SERVER_TOKEN=$(openssl rand -hex 32) \ qwen serve --hostname 0.0.0.0 --port 4170 # 5. Настройка для множества сессий и увеличенного кольца повтора qwen serve --max-sessions 0 --event-ring-size 32000 # 6. Совместная работа нескольких клиентов + строгий лимит MCP QWEN_SERVER_TOKEN=secret \ qwen serve --require-auth \ --mcp-client-budget 10 \ --mcp-budget-mode enforce # 7. Запуск с политикой консенсуса, настроенной в settings.json # settings.json: { "policy": { "permissionStrategy": "consensus", "consensusQuorum": 2 } } qwen serve # 8. Отладочное логирование QWEN_SERVE_DEBUG=1 qwen serve # 9. Отключение пула F2 (возврат к MCP-клиентам для каждой сессии) QWEN_SERVE_NO_MCP_POOL=1 qwen serve # 10. Разрешение кросс-доменного доступа для веб-интерфейса браузера QWEN_SERVER_TOKEN=secret \ qwen serve --allow-origin 'http://localhost:3000' # 11. Дедлайн промпта + таймаут простоя SSE qwen serve --prompt-deadline-ms 300000 --writer-idle-timeout-ms 600000 # 12. Поддержание ACP-потомка в активном состоянии после закрытия последней сессии qwen serve --channel-idle-timeout-ms 60000 # 13. Включение ограничения частоты HTTP-запросов QWEN_SERVE_RATE_LIMIT=1 qwen serve

При использовании защищенного loopback-рецепта (3) /health регистрируется после bearerAuth, поэтому пробы должны передавать токен, как и любой другой маршрут API (статическая поверхность Web Shell остаётся pre-auth по дизайну; передайте --no-web для API-only демона).

3. Полные флаги запуска

CLI определен в packages/cli/src/commands/serve.ts:

ФлагТипПо умолчаниюОбязательно, когдаЭффект
--port <n>number4170-TCP-порт; 0 означает эфемерный порт, назначенный ОС.
--hostname <host>string127.0.0.1Не-loopback требует токенАдрес привязки. Значения loopback: 127.0.0.1, localhost, ::1, [::1]. Скобки в [::1] удаляются автоматически; ввод host:port отклоняется с подсказкой использовать --port.
--token <s>stringenv / noneНе-loopback и --require-authBearer-токен; обрезается один раз. Он отображается в /proc/<pid>/cmdline, поэтому предпочтительнее использовать QWEN_SERVER_TOKEN. При запуске в stderr также выводится предупреждение об этом.
--max-sessions <n>number32-Лимит активных сессий на рабочее пространство. Превышение лимита при создании возвращает 503. 0 означает без ограничений. Значения NaN / отрицательные значения вызывают ошибку.
--max-total-sessions <n>numberвычисляется для нескольких рабочих пространств при запуске/восстановлении-Лимит активных сессий для всего демона. Если не указан, конечное значение по умолчанию вычисляется один раз из лимита на рабочее пространство и количества рабочих пространств при запуске/восстановлении; динамическая регистрация не пересчитывает его. 0 означает без ограничений.
--memory-budget-mb <n>integer в [1024, 1048576]50% памяти cgroup/хоста-Общий бюджет памяти для дерева процессов демона, ограниченный доступной памятью. Ни один дочерний процесс не sizing из него; единственный потребитель на сегодня — адаптивный пул роста live-journal (см. --max-journal-bytes). Сообщается в limits.memory, включая моделированное разделение на дочерние процессы.
--max-journal-events <n>positive safe integer10000-Базовый лимит на сессию для записей повтора liveJournal в процессе выполнения. Адаптивный рост может увеличить его (см. --max-journal-bytes); фиксация любого из флагов journal отключает рост.
--max-journal-bytes <n>positive safe integer8388608-Базовый байтовый лимит на сессию для liveJournal в процессе выполнения. Превышение увеличивает лимиты по требованию (вдвое, ограничено оставшимся запасом пула) в рамках общесистемного пула 5% от эффективного --memory-budget-mb (ограничено 1024 МБ; 0 — рост отключен — когда эффективный бюджет ниже минимума 1024 МБ), не более 256 МиБ на сессию; фиксация любого из флагов journal отключает рост.
--memory-pressure-mode <mode>off | observeobserveТолько наблюдениеСообщает runtime.memory.pressure в обоих режимах; только observe вызывает проблему daemon_memory_pressure. Только корневой процесс.
--child-heap-mode <mode>off | observeobserveТолько наблюдениеПри observe сообщает моделированное разделение в limits.memory.childHeap; ничего не применяет и ничего не отклоняет. При off оба значения этого блока равны null.
--max-pending-prompts-per-session <n>number5-Лимит принятых, но ожидающих/выполняющихся промптов на сессию. Превышение лимита промптов возвращает 503. 0 / Infinity означает без ограничений. Отрицательные или нецелые значения вызывают ошибку.
--workspace <dir>string / повторяемыйprocess.cwd()-Runtime рабочего пространства при запуске; повторите для регистрации дополнительных изолированных runtime. Первый является основным. Каждое значение должно быть абсолютным путём, должно существовать и быть директорией. При запуске каждое значение канонизируется через canonicalizeWorkspace. POST /session с несовпадающим cwd возвращает 400 workspace_mismatch.
--max-connections <n>number256-server.maxConnections на уровне слушателя. 0 / Infinity означает без ограничений. Значения NaN / отрицательные значения прерывают запуск, чтобы избежать поведения fail-open.
--require-authbooleanfalseТребуется токенРасширяет bearer-аутентификацию на loopback и /health. Запуск прерывается, если токен не указан.
--enable-session-shellbooleanfalseТребуется токенВключает прямое выполнение POST /session/:id/shell. Вызывающая сторона также должна отправить привязанный к сессии X-Qwen-Client-Id.
--event-ring-size <n>number8000-Глубина кольца повтора SSE для каждой сессии. Мягкий лимит — MAX_EVENT_RING_SIZE = 1_000_000; значения вне диапазона вызывают ошибку при создании bridge.
--http-bridgebooleantrue-Режим bridge: в production предпринимается попытка прогрева одного основного дочернего процесса qwen --acp с повтором при первом использовании после сбоя; доверенные вторичные запускают один по требованию, а недоверенные вторичные не могут запускать ACP. Внутрипроцессный режим стадии 2 ещё не реализован; --no-http-bridge использует резервный вариант и выводит сообщение в stderr.
--mcp-client-budget <n>numbernoneТребуется для mcp-budget-mode=enforceЛимит MCP-клиентов рабочего пространства. Должно быть положительным целым числом.
--mcp-budget-mode <m>'enforce' | 'warn' | 'off'warn, если задан бюджет, иначе offenforce требует --mcp-client-budgetenforce отклоняет запросы, warn только предупреждает при достижении 75%, off работает только в режиме наблюдения.
--allow-origin <pattern>repeatable stringnone-Белый список CORS, заменяющий стандартный запрет Origin. Для * требуется токен.
--allow-private-auth-base-urlbooleanfalse-Разрешает установку baseUrl для провайдера аутентификации localhost / частной сети. Используйте только для доверенной локальной разработки.
--prompt-deadline-ms <n>numbernone-Серверный лимит реального времени выполнения промпта в мс; по таймауту промпт прерывается.
--writer-idle-timeout-ms <n>numbernone-Таймаут простоя для каждого SSE-соединения в мс.
--channel-idle-timeout-ms <n>number0-Поддерживает ACP-потомка активным после закрытия последней сессии. 0 означает немедленное освобождение ресурсов.
--initialize-timeout-ms <n>number10000-Таймаут запроса дочернего процесса ACP, включая рукопожатие initialize (мс).
--session-reap-interval-ms <n>number60000-Интервал сканирования сборщиком сессий. 0 отключает его.
--session-idle-timeout-ms <n>number1800000-Таймаут простоя отключенной сессии. 0 отключает его.
--rate-limit / --no-rate-limitbooleanenv / off-Включает или отключает многоуровневое ограничение частоты HTTP-запросов.
--rate-limit-prompt <n>number10--rate-limitЗапросы промптов в окне.
--rate-limit-mutation <n>number30--rate-limitЗапросы мутаций в окне.
--rate-limit-read <n>number120--rate-limitЗапросы на чтение в окне.
--rate-limit-window-ms <n>number60000--rate-limitДлина окна ограничения частоты; должно быть >= 1000.

4. Переменные окружения

Переменная окруженияЭквивалентный флаг / эффект
QWEN_SERVER_TOKENЭквивалентно --token; --token имеет приоритет. При запуске обрезается один раз, чтобы избежать перевода строки в конце из cat token.txt.
QWEN_SERVE_DEBUG1 / true / on / yes (без учета регистра) включает подробные логи в stderr.
QWEN_SERVE_NO_MCP_POOL1 полностью отключает пул MCP рабочего пространства и возвращает использование McpClientManager для каждой сессии. Возможности перестают анонсировать mcp_workspace_pool / mcp_pool_restart.
QWEN_SERVE_MCP_CLIENT_BUDGETВнутренний входной параметр бюджета ACP-потомка. CLI генерирует его из --mcp-client-budget через childEnvOverrides; это не резервная переменная окружения родительского процесса.
QWEN_SERVE_MCP_BUDGET_MODEВнутренний режим бюджета ACP-потомка. CLI генерирует его из --mcp-budget-mode через childEnvOverrides; это не резервная переменная окружения родительского процесса.
QWEN_SERVE_PROMPT_DEADLINE_MSРезервная переменная окружения для --prompt-deadline-ms.
QWEN_SERVE_WRITER_IDLE_TIMEOUT_MSРезервная переменная окружения для --writer-idle-timeout-ms.
QWEN_SERVE_MCP_POOL_TRANSPORTSЧитается ACP-потомком. Разделенный запятыми белый список пулированных транспортов; по умолчанию stdio,websocket.
QWEN_SERVE_MCP_POOL_DRAIN_MSЧитается ACP-потомком. Задержка простоя перед очисткой записи пула; по умолчанию 30000, ограничивается диапазоном 1000..600000 мс.
QWEN_SERVE_RATE_LIMIT1 / true включает ограничение частоты; флаг CLI имеет приоритет.
QWEN_SERVE_RATE_LIMIT_PROMPTРезервная переменная окружения для --rate-limit-prompt.
QWEN_SERVE_RATE_LIMIT_MUTATIONРезервная переменная окружения для --rate-limit-mutation.
QWEN_SERVE_RATE_LIMIT_READРезервная переменная окружения для --rate-limit-read.
QWEN_SERVE_RATE_LIMIT_WINDOW_MSРезервная переменная окружения для --rate-limit-window-ms.

Переопределения переменных окружения для каждого дескриптора сделаны намеренно: два демона, работающие в одном процессе, не создают гонок за process.env. defaultSpawnChannelFactory делает снимок переменных окружения во время создания.

5. Также читается settings.json

При запуске loadSettings(boundWorkspace) вызывается один раз:

КлючТипПоведение
policy.permissionStrategy'first-responder' | 'designated' | 'consensus' | 'local-only'Устанавливает BridgeOptions.permissionPolicy. При запуске проверяется с помощью validatePolicyConfig; неизвестные значения вызывают InvalidPolicyConfigError вместо тихого отката.
policy.consensusQuorumpositive integerN для политики consensus. По умолчанию floor(M/2)+1. Если установлено для политики, отличной от consensus, игнорируется, и при запуске в stderr выводится предупреждение.
context.fileNamestringПереопределяет getCurrentGeminiMdFilename() и контролирует, в какой файл пишет POST /workspace/init.
tools.disabledstring[]Нормализуется через normalizeDisabledToolList() (обрезка, удаление пустых записей, дедупликация) перед влиянием на следующее создание ACP-потомка.
tools.approvalModestringРежим одобрения сессии по умолчанию.
telemetryobjectКонфигурация OTel: enabled, otlpEndpoint, otlpProtocol, эндпоинты для каждого сигнала и другое. См. 17-configuration.md.

При сбое ввода-вывода настроек, например, из-за некорректного JSON, используются значения по умолчанию. Исключением является InvalidPolicyConfigError: неправильная конфигурация политики явно прерывает запуск.

6. Сценарии отказа при запуске (явные ошибки)

run-qwen-serve.ts намеренно вызывает ошибку вместо отката в следующих случаях:

СценарийПрефикс ошибки
Привязка не к loopback без токенаRefusing to bind ... without a bearer token
--require-auth без токенаRefusing to start with --require-auth set but no bearer token
--workspace не существует, не является директорией или не является абсолютным путемInvalid --workspace ...
Отказано в доступе к stat для --workspaceInvalid --workspace ...: permission denied
--mcp-client-budget не является положительным целым числомMust be a positive integer
--mcp-budget-mode=enforce без указания бюджетаrequires a positive mcpClientBudget
--hostname записан как localhost:4170looks like a "host:port" combination. Use --port
--hostname [::1]:8080Invalid --hostname ... brackets indicate an IPv6 literal but the value is not a clean [addr] form
--max-connections равен NaN или отрицательному числуMust be >= 0
--event-ring-size > 1_000_000Вызывается при создании bridge
--allow-origin '*' без токенаRefusing to start with --allow-origin '*' but no bearer token configured
--prompt-deadline-ms / --writer-idle-timeout-ms не является положительным целым числомMust be a positive integer
--initialize-timeout-ms не является положительным целым числом или превышает 2^31-1Must be a positive integer / Exceeds maximum JS timer delay
Неизвестный policy.permissionStrategy или неположительный policy.consensusQuorumInvalidPolicyConfigError

7. Чек-лист проверки через Curl

# 1. Проверка доступности curl http://127.0.0.1:4170/health # -> {"status":"ok"} # 1.1 Глубокая проверка (Deep health) curl -s 'http://127.0.0.1:4170/health?deep=1' | jq # 2. Возможности (Capabilities) curl -s http://127.0.0.1:4170/capabilities | jq # 3. Готовность к предварительной проверке (Preflight) curl -s http://127.0.0.1:4170/workspace/preflight | jq # 4. Снимок окружения (секреты сообщают только о своем наличии) curl -s http://127.0.0.1:4170/workspace/env | jq # 5. Снимок пула / бюджета MCP curl -s http://127.0.0.1:4170/workspace/mcp | jq # 6. Создание сессии curl -s -X POST http://127.0.0.1:4170/session \ -H 'Content-Type: application/json' \ -H 'X-Qwen-Client-Id: curl-debug' \ -d '{}' | jq # 7. Чтение SSE в реальном времени (замените <sid>) curl -N \ -H 'Accept: text/event-stream' \ -H 'X-Qwen-Client-Id: curl-debug' \ -H 'Last-Event-ID: 0' \ 'http://127.0.0.1:4170/session/<sid>/events' # 8. Web Shell UI open http://127.0.0.1:4170/

Если включена bearer-аутентификация, добавьте -H "Authorization: Bearer $QWEN_SERVER_TOKEN" к каждому запросу.

8. Есть ли браузерный UI?

Да — Web Shell. resolveWebShellDir() находит собранные ассеты (bundled рядом с CLI-бандлом в релизе, packages/web-shell/dist в репозитории), и mountWebShellAssets() раздаёт их по путям /, /assets и навигациям по документам /session/:id (deep links браузера — обычный curl /session/<id> получает 401/404 API, а не shell). Если ассеты отсутствуют, демон деградирует до API-only вместо падения; --no-web явно отключает UI.

Статический shell монтируется до bearerAuth во всех режимах запуска — браузер не может добавить заголовок Authorization к навигации в адресной строке или под ресурсу <script src>, поэтому проверка только сломала бы UI. Каждый API-маршрут, который он вызывает, остаётся защищённым токеном, и фронтенд сам прикрепляет bearer. При привязке не к loopback shell доступен только для чтения, если не передан --allow-origin <origin> — одноисточниковые POST-запросы несут заголовок Origin, который CORS-стена отклоняет (403) — поэтому передавайте --allow-origin для любой привязки за пределами loopback.

CSP собирается через buildWebShellCsp() и намеренно мягче, чем для статической страницы ('unsafe-inline' для встроенного патча performance.measure, eval/wasm/blob-воркеры для shiki и mermaid, data: для шрифтов katex, connect-src 'self' для SSE). frame-ancestors 'none' плюс X-Frame-Options: DENY блокируют clickjacking, кроме случая, когда origin расширения явно разрешён через --allow-origin, чтобы UI можно было размещать в боковой панели Chrome (#5626).

Для прямой инспекции протокола подпишитесь на SSE-поток напрямую (routes/sse-events.ts) — см. рецепты curl в разделе 7.

9. Цепочка вызовов от qwen serve до прослушивающего сервера

qwen serve | v (process) packages/cli/index.ts main() | v gemini.tsx main() - parseArguments() | v (yargs assembly) config/config.ts import { serveCommand } ... config/config.ts .command(serveCommand) config/config.ts await yargsInstance.parse() | v (handler) commands/serve.ts handler(argv) - boot pre-checks commands/serve.ts const { runQwenServe } = await import('../serve/index.js') # ленивая загрузка commands/serve.ts await runQwenServe({...}) | v serve/run-qwen-serve.ts runQwenServe(opts, deps) | |- trim token | |- hostname mismatch fallback | |- auth preflight | |- workspace validation + canonicalization | |- MCP budget validation + childEnvOverrides | |- loadSettings + validatePolicyConfig | |- PermissionAuditRing + publisher | |- resolveBridgeFsFactory | `- createHttpAcpBridge({...}) | v serve/run-qwen-serve.ts const app = createServeApp(opts, () => actualPort, {...}) | v serve/server.ts createServeApp() - builds Express app (**does not listen**) | |- middleware chain (Host allowlist / CORS / bearerAuth / mutation gate / rate limit) | |- route mounting (health / web-shell static / capabilities / workspace / session / SSE / ACP HTTP) | `- return app | v serve/run-qwen-serve.ts server = createServer(app) / https.createServer(..., app) | |- lifecycle.bindServer(server, { startupReady, drainHost }) | |- server.listen(port, hostname) | |- server.maxConnections = cap | |- actualPort = server.address().port | |- write "qwen serve listening on ..." | |- register SIGINT / SIGTERM (onSignal) | `- resolve(handle: RunHandle) | v commands/serve.ts await blockForever() // блокировка навсегда до получения сигнала

Ключевые факты:

  • createServeApp только собирает приложение; он не начинает прослушивание. Он возвращает экземпляр express() с подключенными middleware и маршрутами. Встраивающие модули только для обычных маршрутов могут продолжать управлять app.listen() самостоятельно. Встраивающие модули, использующие Live/Conversations, должны привязать actual Node-сервер к экспортируемому жизненному циклу приложения перед прослушиванием и ожидать этот жизненный цикл при завершении работы.
  • () => actualPort — это ленивое замыкание. actualPort присваивается в колбэке server.listen. Middleware hostAllowlist читает его по требованию, поэтому эфемерные порты (--port 0) по-прежнему корректно проверяют заголовок Host.
  • await blockForever() используется намеренно. Если yargs.parse() завершается, верхний уровень CLI переходит к точке входа интерактивного TUI (gemini.tsx). SIGINT / SIGTERM завершают работу через путь onSignal в runQwenServe.

10. Разделение файлов HTTP-маршрутов

Основная сборка происходит в createServeApp() в server.ts, где подключаются middleware и монтируются специализированные модули маршрутов:

МаршрутыФайлТочка монтирования
/healthpackages/cli/src/serve/routes/health.tshealthRoutes.register()
/daemon/statuspackages/cli/src/serve/routes/daemon-status.tsregisterDaemonStatusRoutes()
/capabilities, маршруты инициализации workspace/инструментов/мутации MCP, HTTP-мост ACPpackages/cli/src/serve/server.tsРегистрируются напрямую внутри createServeApp()
Статус workspace, окружение, preflight, сводки MCP/инструментов/провайдеров/навыковpackages/cli/src/serve/routes/workspace-status.tsregisterWorkspaceStatusRoutes(), registerWorkspaceDiagnosticStatusRoutes()
Расширения workspace и операции с нимиpackages/cli/src/serve/routes/workspace-extensions.tsregisterWorkspaceExtensionRoutes()
/workspace/memory (GET/POST)packages/cli/src/serve/workspace-memory.tsmountWorkspaceMemoryRoutes()
Все CRUD-маршруты /workspace/agentspackages/cli/src/serve/workspace-agents.tsmountWorkspaceAgentsRoutes()
GET /file, /file/bytes, /list, /glob, /statpackages/cli/src/serve/routes/workspace-file-read.tsregisterWorkspaceFileReadRoutes()
POST /file/write, /file/editpackages/cli/src/serve/routes/workspace-file-write.tsregisterWorkspaceFileWriteRoutes()
Маршруты настройки workspace, доверия, настроек, разрешений и голосаpackages/cli/src/serve/routes/workspace-*.tsregisterWorkspaceSetupGithubRoutes(), registerWorkspaceTrustRoutes() и т.д.
Маршруты провайдера аутентификации workspace и device-flowpackages/cli/src/serve/routes/workspace-auth.tsregisterWorkspaceAuthRoutes()
Маршруты жизненного цикла сессии, промпта, метаданных, языка, оболочки, резюме, отката, ветвления и спискаpackages/cli/src/serve/routes/session.tsregisterSessionRoutes()
GET /session/:id/events SSE-потокpackages/cli/src/serve/routes/sse-events.tsregisterSseEventsRoutes()
Маршруты ответов на запросы разрешенийpackages/cli/src/serve/routes/permission.tsregisterPermissionRoutes()

Полную справку по маршрутам и wire-протоколу см. в ../qwen-serve-protocol.md. Об архитектуре см. в 01-architecture.md.

11. Корректное (graceful) и жесткое (hard) завершение работы

  • Первый SIGINT / SIGTERM -> onSignal в runQwenServe -> двухфазное корректное завершение:
    1. bridge.shutdown(): каждый канал получает KILL_HARD_DEADLINE_MS (10 с), затем вызывается channel.kill().
    2. server.close(): обработка текущих запросов завершается, SHUTDOWN_FORCE_CLOSE_MS (5 с) вызывает closeAllConnections(), затем применяется второй дедлайн в 2 с.
  • Второй SIGINT / SIGTERM во время уже идущего завершения -> bridge.killAllSync() синхронно отправляет SIGKILL всем дочерним процессам ACP и вызывает process.exit(1), чтобы избежать появления процессов-сирот.

Метод RunHandle.close(), возвращаемый runQwenServe, является программным эквивалентом для встраиваемых сценариев и тестов.

12. Встраиваемый вызов (в обход CLI)

import { runQwenServe } from '@qwen-code/qwen-code/serve'; const handle = await runQwenServe({ port: 0, // ephemeral hostname: '127.0.0.1', mode: 'http-bridge', maxSessions: 20, workspace: '/abs/path/to/repo', }); console.log(`Daemon at ${handle.url}`); // ... call handle.bridge directly or access handle.server await handle.close(); // programmatic shutdown

Или получите приложение Express напрямую и привяжите жизненный цикл слушателя самостоятельно. Эта форма необходима, когда встраивающий модуль использует Live/Conversations:

import { createServer } from 'node:http'; import type { AddressInfo } from 'node:net'; import { createServeApp, getServeAppLifecycle, } from '@qwen-code/qwen-code/serve'; let actualPort = 0; const app = createServeApp( { port: 0, hostname: '127.0.0.1', mode: 'http-bridge', maxSessions: 20, }, () => actualPort, { /* deps: bridge, fsFactory, ... */ }, ); const lifecycle = getServeAppLifecycle(app); const server = createServer(app); lifecycle.bindServer(server); await new Promise<void>((resolve, reject) => { server.once('error', reject); server.listen(0, '127.0.0.1', () => resolve()); }); actualPort = (server.address() as AddressInfo).port; console.log('listening on', server.address()); // Остановка допуска, дренирование работы приложения, закрытие слушателя и освобождение владения. await lifecycle.close();

Вызов обычного server.close() также запускает ту же очистку на основе событий, но она выполняется по мере возможности (best-effort), если процесс не остаётся активным; всегда ожидайте lifecycle.close(), чтобы получить ошибки завершения работы. Если сервер не привязан, запросы Live/Conversations завершаются с ошибкой (fail closed), а обычное поведение приложения остаётся неизменным.

Примечание: при прямом вызове createServeApp значение по умолчанию для fsFactory.trusted равно false. ACP-метод writeTextFile на стороне агента отклоняется с ошибкой untrusted_workspace, и в stderr выводится предупреждение (один раз). Либо внедрите deps.fsFactory с явным указанием доверия, либо внедрите deps.bridge, либо примите поведение по умолчанию, ограниченное проверкой доверия.

13. Рецепты отладки

См. раздел об отладке в 19-observability.md. Основные команды:

# Демон работает? curl http://127.0.0.1:4170/health # Какие возможности объявлены? curl -s http://127.0.0.1:4170/capabilities | jq # Готовность хоста демона curl -s http://127.0.0.1:4170/workspace/preflight | jq # Чтение live SSE в реальном времени curl -N -H 'Accept: text/event-stream' \ -H 'Last-Event-ID: 0' \ 'http://127.0.0.1:4170/session/<sid>/events' # Подробные логи QWEN_SERVE_DEBUG=1 qwen serve

Ссылки

  • Точка входа CLI: packages/cli/src/commands/serve.ts
  • Инициализация (Bootstrap): packages/cli/src/serve/run-qwen-serve.ts
  • Фабрика Express: packages/cli/src/serve/server.ts
  • Middleware: packages/cli/src/serve/auth.ts
  • Фабрика моста (Bridge): packages/acp-bridge/src/bridge.ts
  • Статический монтаж Web Shell: packages/cli/src/serve/web-shell-static.ts
  • Документация для пользователей: ../../users/qwen-serve.md
  • Wire-протокол: ../qwen-serve-protocol.md
Last updated on