Skip to Content
EntwicklerhandbuchDaemonQuickstart & Betrieb

Quickstart & Betrieb

Diese Seite konzentriert sich darauf, wie qwen serve gestartet wird, wie die Funktionsfähigkeit überprüft werden kann und wie die interne Aufrufkette von qwen serve zum Listening-Server aussieht. Details zu Architektur, Komponenten und dem Wire-Protokoll finden sich in den anderen Deep-Dive-Seiten zum Daemon.

1. Kürzester Weg

qwen serve

Output:

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.

Öffne http://127.0.0.1:4170/ in einem Browser, um die Web-Shell-UI zu erhalten: Chat, Session-Liste und Workspace-Inspektion. createServeApp() mountet die gebündelten Web-Shell-Assets (packages/cli/src/serve/web-shell-static.ts) vor bearerAuth, sodass die Shell selbst ohne Token geladen wird; ihre eigenen API-Aufrufe tragen das Bearer-Token, wenn eines konfiguriert ist — starte den Daemon mit --open (das das Token im URL-Fragment platziert, das niemals an den Server gesendet wird) oder hänge #token=… manuell an, wenn Auth aktiviert ist. --no-web meldet ab und lässt den Daemon API-only.

2. Start-Rezepte

# 1. Local-Dev-Standard (Loopback, kein Token) qwen serve # 2. Expliziter Workspace + ephemeraler Port qwen serve --workspace /path/to/repo --port 0 # 3. Abgesicherte Loopback-Entwicklung (Bearer auch auf Loopback erzwingen) QWEN_SERVER_TOKEN=$(openssl rand -hex 32) qwen serve --require-auth # 4. Für LAN freigeben (Non-Loopback erfordert ein Token) QWEN_SERVER_TOKEN=$(openssl rand -hex 32) \ qwen serve --hostname 0.0.0.0 --port 4170 # 5. Optimierung für viele Sessions und einen größeren Replay-Ring qwen serve --max-sessions 0 --event-ring-size 32000 # 6. Multi-Client-Kollaboration + striktes MCP-Budget QWEN_SERVER_TOKEN=secret \ qwen serve --require-auth \ --mcp-client-budget 10 \ --mcp-budget-mode enforce # 7. Start mit einer in settings.json konfigurierten Consensus-Policy # settings.json: { "policy": { "permissionStrategy": "consensus", "consensusQuorum": 2 } } qwen serve # 8. Debug-Logging QWEN_SERVE_DEBUG=1 qwen serve # 9. F2-Pool deaktivieren (Fallback auf session-spezifische MCP-Clients) QWEN_SERVE_NO_MCP_POOL=1 qwen serve # 10. Cross-Origin-Zugriff für Browser-Web-UI erlauben QWEN_SERVER_TOKEN=secret \ qwen serve --allow-origin 'http://localhost:3000' # 11. Prompt-Deadline + SSE-Idle-Timeout qwen serve --prompt-deadline-ms 300000 --writer-idle-timeout-ms 600000 # 12. ACP-Child nach Schließen der letzten Session warmhalten qwen serve --channel-idle-timeout-ms 60000 # 13. HTTP-Rate-Limiting aktivieren QWEN_SERVE_RATE_LIMIT=1 qwen serve

Beim abgesicherten Loopback-Rezept (3) wird /health nach bearerAuth registriert, sodass Proben das Token wie jede andere API-Route mitführen müssen (die statische Web-Shell-Oberfläche bleibt standardmäßig pre-auth; verwende --no-web für einen API-only-Daemon).

3. Alle Start-Flags

Die CLI ist in packages/cli/src/commands/serve.ts definiert:

FlagTypStandardErforderlich wennEffekt
--port <n>number4170-TCP-Port; 0 bedeutet ein vom OS zugewiesener ephemeraler Port.
--hostname <host>string127.0.0.1Non-Loopback erfordert TokenBind-Adresse. Loopback-Werte: 127.0.0.1, localhost, ::1, [::1]. [::1]-Klammern werden automatisch entfernt; host:port-Eingaben werden mit einem Hinweis auf --port abgelehnt.
--token <s>stringenv / noneNon-Loopback und --require-authBearer-Token; wird einmal getrimmt. Es erscheint in /proc/<pid>/cmdline, bevorzuge daher QWEN_SERVER_TOKEN. Boot-Stderr warnt ebenfalls davor.
--max-sessions <n>number32-Obergrenze für aktive Sessions pro Workspace. Überschüssige Spawns geben 503 zurück. 0 bedeutet unbegrenzt. NaN / negative Werte werfen einen Fehler.
--max-total-sessions <n>numberabgeleitet für mehrere Start-/wiederhergestellte Workspaces-Daemon-weite Obergrenze für aktive Sessions. Wenn weggelassen, wird einmalig ein endlicher Standardwert aus der Obergrenze pro Workspace und der Anzahl der Start-/wiederhergestellten Workspaces abgeleitet; dynamische Registrierung berechnet ihn nicht neu. 0 bedeutet unbegrenzt.
--memory-budget-mb <n>integer in [1024, 1048576]50% des Cgroup/Host-Speichers-Gesamtspeicherbudget für den Daemon-Prozessbaum, gedeckelt beim aufgelösten verfügbaren Speicher. Kein Kindprozess wird daraus dimensioniert; der einzige Consumer heute ist der adaptive Live-Journal-Wachstumspool (siehe --max-journal-bytes). Berichtet unter limits.memory, einschließlich einer modellierten pro-Child-Partition.
--max-journal-events <n>positive safe integer10000-Basis-Obergrenze pro Session für laufende liveJournal-Replay-Einträge. Adaptives Wachstum kann sie erhöhen (siehe --max-journal-bytes); das Fixieren eines der beiden Journal-Flags deaktiviert das Wachstum.
--max-journal-bytes <n>positive safe integer8388608-Basis-Byte-Obergrenze pro Session für das laufende liveJournal. Überschreitende Turns vergrößern die Obergrenzen bei Bedarf (auf das Doppelte, begrenzt durch den verbleibenden Pool-Spielraum) innerhalb eines daemon-weiten Pools von 5% des effektiven --memory-budget-mb (gedeckelt bei 1024 MB; 0 — Wachstum deaktiviert — wenn das effektive Budget unter das 1024-MB-Minimum fällt), niemals über eine 256-MiB-pro-Session-Hardcap hinaus; das Fixieren eines der beiden Journal-Flags deaktiviert das Wachstum.
--memory-pressure-mode <mode>off | observeobserveNur BeobachtungBerichtet runtime.memory.pressure in beiden Modi; nur observe löst das daemon_memory_pressure-Issue aus. Nur Root-Prozess.
--child-heap-mode <mode>off | observeobserveNur BeobachtungUnter observe wird die modellierte Partition unter limits.memory.childHeap berichtet; nichts wird angewendet und nichts abgelehnt. Unter off sind die beiden Werte dieses Blocks null.
--max-pending-prompts-per-session <n>number5-Obergrenze für akzeptierte, aber ausstehende/laufende Prompts pro Session. Überschüssige Prompts geben 503 zurück. 0 / Infinity bedeutet unbegrenzt. Negative oder nicht-ganzzahlige Werte werfen einen Fehler.
--workspace <dir>string / wiederholbarprocess.cwd()-Start-Workspace-Runtime; wiederholen, um zusätzliche isolierte Runtimes zu registrieren. Der erste ist primär. Jeder Wert muss ein absoluter Pfad sein, muss existieren und muss ein Verzeichnis sein. Boot kanonisiert jeden Wert über canonicalizeWorkspace. POST /session mit einem nicht übereinstimmenden cwd gibt 400 workspace_mismatch zurück.
--max-connections <n>number256-server.maxConnections auf Listener-Ebene. 0 / Infinity bedeutet unbegrenzt. NaN / negative Werte schlagen beim Boot fehl, um Fail-Open-Verhalten zu vermeiden.
--require-authbooleanfalseToken erforderlichErweitert die Bearer-Auth auf Loopback und /health. Boot verweigert den Start ohne Token.
--enable-session-shellbooleanfalseToken erforderlichAktiviert die direkte POST /session/:id/shell-Ausführung. Caller müssen zusätzlich eine session-gebundene X-Qwen-Client-Id senden.
--event-ring-size <n>number8000-Tiefe des SSE-Replay-Rings pro Session. Soft-Cap ist MAX_EVENT_RING_SIZE = 1_000_000; Werte außerhalb des Bereichs werfen während der Bridge-Konstruktion einen Fehler.
--http-bridgebooleantrue-Bridge-Modus: Production versucht, einen primären qwen --acp-Child vorzuwärmen und retryt bei erstem Gebrauch nach einem Fehler; vertrauenswürdige Secondaries starten einen bei Bedarf, während nicht vertrauenswürdige Secondaries kein ACP starten können. Stage-2-In-Process-Modus ist noch nicht implementiert; --no-http-bridge fällt zurück und gibt eine Meldung auf stderr aus.
--mcp-client-budget <n>numbernoneErforderlich für mcp-budget-mode=enforceObergrenze für Workspace-MCP-Clients. Muss eine positive Ganzzahl sein.
--mcp-budget-mode <m>'enforce' | 'warn' | 'off'warn wenn ein Budget gesetzt ist, sonst offenforce erfordert --mcp-client-budgetenforce lehnt ab, warn warnt nur bei 75%, off dient nur der Beobachtung.
--allow-origin <pattern>repeatable stringnone-CORS-Allowlist, die die Standard-Origin-Verweigerung ersetzt. * erfordert ein Token.
--allow-private-auth-base-urlbooleanfalse-Erlaubt die Installation von baseUrl für localhost / Private-Network-Auth-Provider. Nur für vertrauenswürdige lokale Entwicklung verwenden.
--prompt-deadline-ms <n>numbernone-Serverseitiges Prompt-Wallclock-Limit in ms; Timeout bricht den Prompt ab.
--writer-idle-timeout-ms <n>numbernone-Idle-Timeout pro SSE-Verbindung in ms.
--channel-idle-timeout-ms <n>number0-Hält das ACP-Child am Leben, nachdem die letzte Session geschlossen wird. 0 bedeutet sofortige Freigabe.
--initialize-timeout-ms <n>number10000-ACP-Child-Request-Timeout, einschließlich des Initialize-Handshakes (ms).
--session-reap-interval-ms <n>number60000-Scan-Intervall des Session-Reapers. 0 deaktiviert ihn.
--session-idle-timeout-ms <n>number1800000-Idle-Timeout für getrennte Sessions. 0 deaktiviert es.
--rate-limit / --no-rate-limitbooleanenv / off-Aktiviert oder deaktiviert das HTTP-Rate-Limiting pro Stufe.
--rate-limit-prompt <n>number10--rate-limitPrompt-Requests pro Zeitfenster.
--rate-limit-mutation <n>number30--rate-limitMutations-Requests pro Zeitfenster.
--rate-limit-read <n>number120--rate-limitRead-Requests pro Zeitfenster.
--rate-limit-window-ms <n>number60000--rate-limitLänge des Rate-Limit-Zeitfensters; muss >= 1000 sein.

4. Umgebungsvariablen

EnvÄquivalentes Flag / Effekt
QWEN_SERVER_TOKENÄquivalent zu --token; --token hat Vorrang. Wird beim Boot einmal getrimmt, um einen abschließenden Zeilenumbruch von cat token.txt zu vermeiden.
QWEN_SERVE_DEBUG1 / true / on / yes (Groß-/Kleinschreibung ignorieren) aktiviert ausführliche Stderr-Logs.
QWEN_SERVE_NO_MCP_POOL1 deaktiviert den Workspace-MCP-Pool vollständig und fällt auf den session-spezifischen McpClientManager zurück. Capabilities bewerben nicht mehr mcp_workspace_pool / mcp_pool_restart.
QWEN_SERVE_MCP_CLIENT_BUDGETInterner Budget-Input des ACP-Childs. Die CLI generiert ihn aus --mcp-client-budget über childEnvOverrides; er ist kein Env-Fallback des Elternprozesses.
QWEN_SERVE_MCP_BUDGET_MODEInterner Budget-Modus des ACP-Childs. Die CLI generiert ihn aus --mcp-budget-mode über childEnvOverrides; er ist kein Env-Fallback des Elternprozesses.
QWEN_SERVE_PROMPT_DEADLINE_MSEnv-Fallback für --prompt-deadline-ms.
QWEN_SERVE_WRITER_IDLE_TIMEOUT_MSEnv-Fallback für --writer-idle-timeout-ms.
QWEN_SERVE_MCP_POOL_TRANSPORTSWird vom ACP-Child gelesen. Kommagetrennte Allowlist für gepoolte Transports; Standard ist stdio,websocket.
QWEN_SERVE_MCP_POOL_DRAIN_MSWird vom ACP-Child gelesen. Idle-Drain-Verzögerung für Pool-Einträge; Standard ist 30000, begrenzt auf 1000..600000 ms.
QWEN_SERVE_RATE_LIMIT1 / true aktiviert das Rate-Limiting; CLI-Flag hat Vorrang.
QWEN_SERVE_RATE_LIMIT_PROMPTEnv-Fallback für --rate-limit-prompt.
QWEN_SERVE_RATE_LIMIT_MUTATIONEnv-Fallback für --rate-limit-mutation.
QWEN_SERVE_RATE_LIMIT_READEnv-Fallback für --rate-limit-read.
QWEN_SERVE_RATE_LIMIT_WINDOW_MSEnv-Fallback für --rate-limit-window-ms.

Pro-Handle-Env-Overrides sind beabsichtigt: zwei Daemons, die im selben Prozess laufen, konkurrieren nicht um process.env. defaultSpawnChannelFactory erstellt beim Spawnen einen Snapshot der Env.

5. settings.json wird ebenfalls gelesen

Boot ruft loadSettings(boundWorkspace) einmal auf:

KeyTypVerhalten
policy.permissionStrategy'first-responder' | 'designated' | 'consensus' | 'local-only'Setzt BridgeOptions.permissionPolicy. Boot validiert mit validatePolicyConfig; unbekannte Werte werfen InvalidPolicyConfigError, anstatt stillschweigend zurückzufallen.
policy.consensusQuorumpositive integerN für die consensus-Policy. Standard ist floor(M/2)+1. Wenn es unter einer Non-Consensus-Policy gesetzt wird, wird es ignoriert und Boot gibt eine Stderr-Warnung aus.
context.fileNamestringÜberschreibt getCurrentGeminiMdFilename() und steuert, welche Datei POST /workspace/init schreibt.
tools.disabledstring[]Wird durch normalizeDisabledToolList() normalisiert (trimmen, leere Einträge entfernen, Deduplizierung), bevor es den nächsten ACP-Child-Spawn beeinflusst.
tools.approvalModestringStandard-Session-Approval-Modus.
telemetryobjectOTel-Konfiguration: enabled, otlpEndpoint, otlpProtocol, Endpunkte pro Signal und mehr. Siehe 17-configuration.md.

Fehler bei den Settings-I/O, wie z. B. fehlerhaftes JSON, fallen auf die Standardwerte zurück. InvalidPolicyConfigError ist die Ausnahme: Eine falsche Policy-Konfiguration lässt den Boot explizit fehlschlagen.

6. Boot-Verweigerungsszenarien (explizite Fehler)

run-qwen-serve.ts wirft in diesen Fällen absichtlich einen Fehler, anstatt zurückzufallen:

SzenarioFehler-Präfix
Non-Loopback-Bind ohne TokenRefusing to bind ... without a bearer token
--require-auth ohne TokenRefusing to start with --require-auth set but no bearer token
--workspace existiert nicht, ist kein Verzeichnis oder ist nicht absolutInvalid --workspace ...
--workspace stat permission deniedInvalid --workspace ...: permission denied
--mcp-client-budget ist keine positive GanzzahlMust be a positive integer
--mcp-budget-mode=enforce ohne Budgetrequires a positive mcpClientBudget
--hostname ist als localhost:4170 geschriebenlooks 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 ist NaN oder negativMust be >= 0
--event-ring-size > 1_000_000Wird während der Bridge-Konstruktion geworfen
--allow-origin '*' ohne TokenRefusing to start with --allow-origin '*' but no bearer token configured
--prompt-deadline-ms / --writer-idle-timeout-ms ist keine positive GanzzahlMust be a positive integer
--initialize-timeout-ms ist keine positive Ganzzahl oder überschreitet 2^31-1Must be a positive integer / Exceeds maximum JS timer delay
Unbekannte policy.permissionStrategy oder nicht-positive policy.consensusQuorumInvalidPolicyConfigError

7. Curl-Verifizierungscheckliste

# 1. Liveness 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-Bereitschaft curl -s http://127.0.0.1:4170/workspace/preflight | jq # 4. Env-Snapshot (Secrets melden nur ihre Präsenz) curl -s http://127.0.0.1:4170/workspace/env | jq # 5. MCP-Pool / Budget-Snapshot curl -s http://127.0.0.1:4170/workspace/mcp | jq # 6. Session erstellen 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 tailen (<sid> ersetzen) 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/

Wenn die Bearer-Authentifizierung aktiviert ist, füge -H "Authorization: Bearer $QWEN_SERVER_TOKEN" zu jeder Anfrage hinzu.

8. Gibt es eine Browser-UI?

Ja — die Web Shell. resolveWebShellDir() findet die gebauten Assets (gebündelt neben dem CLI-Bundle in einem Release, packages/web-shell/dist in einem Checkout) und mountWebShellAssets() serviert sie unter /, /assets und /session/:id Dokument-Navigationen (Browser-Deep-Links — ein einfaches curl /session/<id> bekommt die 401/404 der API, nicht die Shell). Wenn die Assets fehlen, degradiert der Daemon zu API-only, anstatt abzustürzen; --no-web meldet explizit ab.

Die statische Shell wird in jedem Startmodus vor bearerAuth gemountet — ein Browser kann bei einer Adressleisten-Navigation oder einer <script src>-Subressource keinen Authorization-Header anhängen, daher würde eine Zugangskontrolle die UI einfach brechen. Jede API-Route, die sie aufruft, bleibt token-geschützt, und das Frontend hängt das Bearer-Token selbst an. Bei einem Non-Loopback-Bind ist die Shell schreibgeschützt, außer --allow-origin <origin> wird übergeben — gleichoriginige POSTs tragen einen Origin-Header, den die CORS-Wand ablehnt (403) — daher verwende --allow-origin für jeden Bind über Loopback hinaus.

Die CSP wird von buildWebShellCsp() erstellt und ist absichtlich lockerer als die einer statischen Seite ('unsafe-inline' für den inline performance.measure-Patch, eval/wasm/blob Worker für shiki und mermaid, data: für katex-Fonts, connect-src 'self' für SSE). frame-ancestors 'none' plus X-Frame-Options: DENY blockieren Clickjacking, außer wenn ein Extension-Origin explizit über --allow-origin erlaubt wird, damit die UI in einem Chrome-Side-Panel gehostet werden kann (#5626).

Für rohe Protokollinspektion abonniere den SSE-Stream direkt (routes/sse-events.ts) — siehe die Curl-Rezepte in Abschnitt 7.

9. Aufrufkette von qwen serve bis zum Server, der auf Anfragen wartet

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') # lazy load 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() // block forever until signal

Wichtige Fakten:

  • createServeApp baut nur auf; es lauscht nicht. Es gibt eine express()-Instanz mit gemounteter Middleware und Routen zurück. Ordinary-only-Embedder können weiterhin app.listen() selbst besitzen. Embedder, die Live/Conversations verwenden, müssen den tatsächlichen Node-Server an den exportierten App-Lifecycle binden, bevor sie lauschen, und diesen Lifecycle während des Shutdowns awaiten.
  • () => actualPort ist eine Lazy Closure. actualPort wird im app.listen-Callback zugewiesen. Die hostAllowlist-Middleware liest ihn bei Bedarf aus, sodass ephemere Ports (--port 0) den Host-Header weiterhin korrekt prüfen.
  • await blockForever() ist beabsichtigt. Wenn yargs.parse() auflöst, fällt die CLI-Top-Level-Ebene in den interaktiven TUI-Entrypoint (gemini.tsx) durch. SIGINT / SIGTERM werden über den onSignal-Pfad von runQwenServe beendet.

10. Aufteilung der HTTP-Routendateien

Die Hauptzusammenstellung erfolgt in createServeApp() in server.ts, wo die Middleware verdrahtet und fokussierte Routenmodule gemountet werden:

RoutenDateiMount-Eintrag
/healthpackages/cli/src/serve/routes/health.tshealthRoutes.register()
/daemon/statuspackages/cli/src/serve/routes/daemon-status.tsregisterDaemonStatusRoutes()
/capabilities, Workspace-Init/Tool/MCP-Mutationsrouten, ACP-HTTP-Bridgepackages/cli/src/serve/server.tsDirekt innerhalb von createServeApp() registriert
Workspace-Status, Env, Preflight, MCP/Tool/Provider/Skill-Zusammenfassungenpackages/cli/src/serve/routes/workspace-status.tsregisterWorkspaceStatusRoutes(), registerWorkspaceDiagnosticStatusRoutes()
Workspace-Erweiterungen und Erweiterungsoperationenpackages/cli/src/serve/routes/workspace-extensions.tsregisterWorkspaceExtensionRoutes()
/workspace/memory (GET/POST)packages/cli/src/serve/workspace-memory.tsmountWorkspaceMemoryRoutes()
Alle /workspace/agents CRUD-Routenpackages/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-Setup, Trust, Einstellungen, Berechtigungen und Voice-Routenpackages/cli/src/serve/routes/workspace-*.tsregisterWorkspaceSetupGithubRoutes(), registerWorkspaceTrustRoutes(), etc.
Workspace-Auth-Provider und Device-Flow-Routenpackages/cli/src/serve/routes/workspace-auth.tsregisterWorkspaceAuthRoutes()
Session-Lifecycle, Prompt, Metadaten, Sprache, Shell, Recap, Rewind, Branch und Listen-Routenpackages/cli/src/serve/routes/session.tsregisterSessionRoutes()
GET /session/:id/events SSE-Streampackages/cli/src/serve/routes/sse-events.tsregisterSseEventsRoutes()
Permission-Response-Routenpackages/cli/src/serve/routes/permission.tsregisterPermissionRoutes()

Die vollständige Referenz für Routen und Wire-Protokolle findest du unter ../qwen-serve-protocol.md. Informationen zur Architektur findest du unter 01-architecture.md.

11. Graceful vs. Hard Shutdown

  • Erstes SIGINT / SIGTERM -> runQwenServe onSignal -> zweiphasiger Graceful Shutdown:
    1. bridge.shutdown(): Jeder Channel erhält KILL_HARD_DEADLINE_MS (10s), dann channel.kill().
    2. server.close(): In-flight Requests werden abgearbeitet, SHUTDOWN_FORCE_CLOSE_MS (5s) löst closeAllConnections() aus, dann greift eine zweite 2s-Frist.
  • Zweites SIGINT / SIGTERM während des bereits laufenden Exits -> bridge.killAllSync() beendet alle ACP-Children synchron per SIGKILL und ruft process.exit(1) auf, um orphan processes zu vermeiden.

Das von runQwenServe zurückgegebene RunHandle.close() ist das programmatische Äquivalent für Embedder und Tests.

12. Eingebetteter Aufruf (CLI umgehen)

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}`); // ... handle.bridge direkt aufrufen oder auf handle.server zugreifen await handle.close(); // programmatischer Shutdown

Oder hole die Express-App direkt und binde den Listener-Lifecycle selbst. Diese Form ist erforderlich, wenn der Embed Live/Conversations verwendet:

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()); // Admission stoppen, App-Arbeit drainen, Listener schließen und Ownership freigeben. await lifecycle.close();

Der Aufruf von server.close() startet ebenfalls dieselbe ereignisgesteuerte Aufräumaktion, aber sie ist nur Best Effort, außer der Prozess bleibt am Leben; awaiten Sie immer lifecycle.close(), um Shutdown-Fehler zu erhalten. Wenn kein Server gebunden ist, schlagen Live/Conversations-Anfragen fail-closed fehl, während das Ordinary-only-App-Verhalten unverändert bleibt.

Hinweis: Beim direkten Aufruf von createServeApp ist der Standardwert fsFactory.trusted = false. Der agentenseitige ACP writeTextFile wird als untrusted_workspace abgelehnt und eine Stderr-Warnung wird einmalig ausgegeben. Injiziere entweder deps.fsFactory mit explizitem Trust, injiziere deps.bridge oder akzeptiere das standardmäßige, durch Trust gesteuerte Verhalten.

13. Debugging-Rezepte

Siehe den Debugging-Abschnitt in 19-observability.md. Die gängigsten Befehle sind:

# Ist der Daemon erreichbar? curl http://127.0.0.1:4170/health # Welche Capabilities werden angeboten? curl -s http://127.0.0.1:4170/capabilities | jq # Daemon-Host-Bereitschaft curl -s http://127.0.0.1:4170/workspace/preflight | jq # Live-SSE tailen curl -N -H 'Accept: text/event-stream' \ -H 'Last-Event-ID: 0' \ 'http://127.0.0.1:4170/session/<sid>/events' # Ausführliche Logs QWEN_SERVE_DEBUG=1 qwen serve

Referenzen

  • CLI-Entrypoint: packages/cli/src/commands/serve.ts
  • Bootstrap: packages/cli/src/serve/run-qwen-serve.ts
  • Express-Factory: packages/cli/src/serve/server.ts
  • Middleware: packages/cli/src/serve/auth.ts
  • Bridge-Factory: packages/acp-bridge/src/bridge.ts
  • Web-Shell-Statisches Mount: packages/cli/src/serve/web-shell-static.ts
  • User-Docs: ../../users/qwen-serve.md
  • Wire-Protokoll: ../qwen-serve-protocol.md
Last updated on