mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-07-09 17:19:02 +00:00
7 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
489bea9771
|
fix(release): reduce npm package scan triggers (#6164)
* fix(release): reduce npm package scan triggers * fix(release): remove browser MCP dev dependencies * test(serve): stabilize CDP tunnel acceptance startup * fix(serve): remove unused chrome devtools MCP helper * fix(serve): remove unused path import * fix(serve): address CDP MCP review comments |
||
|
|
98dec4fa71
|
fix(release): use relative postinstall patch dir (#5973) | ||
|
|
5581424b6b
|
feat(browser-ext): revive Chrome extension via daemon-direct architecture (#5777)
* feat(chrome-qwen-bridge): 🔥 init chrome qwen code bridge * chore(chrome-qwen-bridge): connect * chore(chrome-qwen-bridge): connect & them * chore(chrome-qwen-bridge): connect & them * chore(chrome-qwen-bridge): wip use chat ui * chore(chrome-qwen-bridge): wip use chat ui * wip * refactor(chrome-extension): rename chrome-qwen-bridge package to chrome-extension * feat(chrome-extension): enhance network monitoring with webRequest API Replace the existing network monitoring implementation with a more comprehensive solution that combines both webRequest and debugger APIs for broader coverage. The new implementation: - Uses chrome.webRequest.onBeforeRequest to capture all outgoing requests - Uses chrome.webRequest.onCompleted to capture completed responses - Uses chrome.webRequest.onErrorOccurred to capture failed requests - Retains debugger API integration for detailed network information - Implements memory management with maximum 1000 logs per tab - Adds proper initialization and cleanup for each tab - Ensures graceful handling of debugger attachment failures - Provides more reliable network activity capture across all tabs This enhancement significantly improves the reliability and coverage of network monitoring functionality in the Chrome extension. * refactor(chrome-extension): reorganize directory structure for better maintainability This commit reorganizes the entire Chrome extension package structure for improved maintainability and clarity: - Move all source files to `src/` directory (background, content, sidepanel) - Move build configurations to `config/` directory - Move documentation to `docs/` directory with proper categorization - Move all script files to `scripts/` directory - Move native-host specific files to appropriate subdirectories (`src`, `scripts`, `config`) - Update package.json scripts to reflect new file locations - Add comprehensive documentation files (debugging, development, architecture, API reference) - Maintain all functionality while improving project organization The reorganization separates source code from build output, centralizes documentation, and creates a clear separation of concerns making the project more maintainable and easier for developers to navigate. * feat(chrome-extension): enhance native host communication and network logging - Add troubleshooting documentation for native host setup issues - Improve native host logging to home directory with fallback to tmp - Enhance network logging in service worker with response body capture - Update scripts to properly reference host.js from correct path - Increase timeout for MCP session creation and long prompts from 3 to 5 minutes - Add getConsoleLogs functionality to sidepanel for content script capture - Improve browser-mcp-server network logs aggregation by request ID - Update icon assets and improve manifest configuration refactor(chrome-extension): consolidate host.js entry point and improve path resolution - Create unified host.js entry point that delegates to src/host.js - Improve path resolution for host scripts in installer and runner scripts - Add proper path existence checks for browser-mcp-server.js - Support running from different directory structures style(chrome-extension): improve TypeScript type safety and error handling - Add proper type definitions for message handling in side panel - Add null checks and error handling for message parsing - Improve React component callback implementations * refactor(chrome-extension): redesign build workflow * fix(chrome-extension): resolve ESLint errors in native host, service worker, and content script - Fix 'Unexpected lexical declaration in case block' by wrapping switch cases in blocks - Fix 'Unexpected constant truthiness on the left-hand side of a || expression' by using conditional patterns - Fix unused variable errors by properly using catch error parameters or adding logging - Fix 'document' and 'window' not defined errors in service worker with proper global declarations - Add eslint-disable comments where appropriate for globals used in specific contexts * feat(chrome-extension): enhance native host with browser MCP tools and event streaming - Add new browser MCP tools: browser_click, browser_click_text, browser_run_js, browser_fill_form_auto - Implement SSE (Server-Sent Events) for improved event streaming instead of long-polling - Add daemon script for running the bridge host in background - Enhance documentation with MCP notes and updated README - Add multiple executable binaries to package.json: chrome-browser-mcp, qwen-bridge-host - Improve error handling and event processing in the native host - Add debouncing mechanism for stream end events in service worker - Update timeout for MCP discovery to accommodate slower startup Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> * fix(chrome-extension): use trusted cwd for MCP tool discovery The root cause of MCP tools not being recognized by Qwen CLI was that the cwd (current working directory) was defaulting to '/' (root directory). In Qwen CLI's MCP discovery logic, there's a security check: if (!cliConfig.isTrustedFolder()) { return; // Skip MCP tool discovery } The root directory '/' is not a trusted folder, so MCP tools were silently not being discovered at all. Changes: - host.js: Default to $HOME instead of process.cwd() for start_qwen - service-worker.js: Remove '/' fallback, let host.js handle default This ensures browser MCP tools (browser_read_page, browser_click, etc.) are properly discovered and available to the model. * fix(core): validate MCP entry script existence before connection Add pre-flight check to verify that stdio-based MCP server entry scripts exist on disk before attempting connection. This prevents silent failures and provides clear error messages for misconfigured MCP servers. * fix(chrome-extension): enhance native host path resolution and port config - Add QWEN_BROWSER_MCP_SERVER_PATH env override for custom installations - Expand candidate search paths for browser-mcp-server.js discovery - Add detailed logging when MCP server script is not found - Support BRIDGE_PORT env variable for HTTP API server configuration * fix(chrome-extension): improve browser MCP server reliability and debugging - Add comprehensive debug logging for bridge health checks and host spawn - Support BROWSER_MCP_NO_SPAWN env to disable automatic host.js spawning - Handle bridge unavailability gracefully with clear error messages - Add raw JSON mode fallback for clients without Content-Length framing - Capture and log host.js stdout/stderr instead of inheriting stdio - Add pre-flight bridge check at startup for better diagnostics - Handle each bridge call failure with proper error responses * chore(chrome-extension): add debug wrapper script for MCP server Add cbmcp-wrapper.sh to help diagnose MCP server invocation issues. The wrapper logs invocation details and stderr to /tmp/cbmcp.log, making it easier to debug when Qwen CLI spawns the MCP server. * docs(chrome-extension): add MCP/Bridge troubleshooting guide Document common failure scenarios when MCP bridge shows as Disconnected: - EPERM errors when spawning host.js cannot bind to port - Content-Length framing issues during MCP handshake - Step-by-step troubleshooting with flow diagrams - Manual bridge setup with BROWSER_MCP_NO_SPAWN workaround * build(chrome-extension): 浏览器插件 mcp 构建优化 * docs(chrome-extension): update docs * fix(chrome-extension): 解决CDP响应体获取和权限请求处理问题 * feat(mcp-chrome-integration): add MCP Chrome browser extension integration Add complete Chrome extension with native messaging host for MCP integration: - Chrome extension with sidepanel UI, service worker, and content script - Native server with agent engines (Claude/Codex), session management, and tool bridge - Shared packages for types, tools, and node specifications - Documentation and build scripts Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(mcp-chrome-integration): refactor * feat(chrome-extension): 切换到HTTP后端代理并更新构建配置 * refactor(mcp-chrome-integration): build * wip * wip chrome extension * chore: ignore .worktrees * docs: plan sidepanel component removal * refactor(sidepanel): remove local components * feat(chrome-extension): add native messaging ACP client and protocol support - Add ACP client for native messaging communication - Add file handler for local file access via native host - Add protocol definitions for ACP communication - Archive old documentation files - Update integration status and protocol documentation * fix(chrome-extension): use GenericToolCall from @qwen-code/webui - Replace non-existent local ToolCallCard import with GenericToolCall - Import from @qwen-code/webui package instead of local path - Fix component props to match GenericToolCall interface - Remove unused ToolCallData import * refactor(mcp-chrome-integration): split large files and fix ESLint errors - Split tools.ts (1554 lines) into 8 schema files by functionality - Split native-messaging.ts (1533 lines) into 6 modules - Split doctor.ts (1099 lines) into 8 modules - Split content-script.ts (1055 lines) into 5 modules - Add Qwen Team license headers to all new files - Remove unused tool names (SEARCH_TABS_CONTENT, SEND_COMMAND_TO_INJECT_SCRIPT, USERSCRIPT, RECORD_REPLAY) - Fix ESLint errors: no-require-imports, no-explicit-any, no-unused-vars, prefer-const - Add archive/ directory to eslint ignore patterns * 调试 MCP 工具成功 * refactor: revert unnecessary formatting changes and clean up MCP chrome-integration - Revert Node version requirement from >=22 to >=20 - Revert code formatting changes (import statements and indentation) - Archive obsolete chrome-extension implementation - Clean up outdated documentation and scripts - Reorganize MCP chrome-integration docs * docs(mcp-chrome): 新增核心文档和更新 README - 新增 02-features-and-architecture.md (27个工具完整参考) - 新增 03-design-and-implementation.md (与 hangwin/mcp-chrome 对比) - 新增 04-test-cases.md (35个测试用例) - 更新 01-installation-guide.md (Extension ID 固定方案) - 重写 README.md (对齐新文档结构) * refactor(chrome-extension): 简化 sidepanel 并移除未使用代码 - 删除未使用的 Onboarding 组件 - 删除冗余样式文件 (App.css, timeline.css) - 删除未使用的工具函数 (diffStats, diffUtils, sessionGrouping, tempFileManager, webviewUtils) - 新增 MCP 工具状态横幅显示 - 隐藏不需要的 UI 按钮 (slash command, attach, edit mode) - 移除未使用的变量 (clearToolCalls) - 更新 manifest.json 和 native-messaging-host * chore: regenerate lockfile for mcp-chrome-integration workspace npm install reconciles the lock with the merged package.json: adds the new packages/mcp-chrome-integration/app/* workspace subtree (371 deps). No existing main dependencies were removed. * chore(chrome-integration): drop dead logger + unused pino deps native-server/src/util/logger.ts was 100% commented-out dead code (even a hardcoded /Users/hang/... path) with zero importers; pino/pino-pretty were declared but never imported (logging is console.error to stderr, correct for a native-messaging host). Remove the file and both deps. * feat(chrome-integration): WIP serve-backed agent client (Phase 1 backbone) Replace the hand-rolled ACP client by driving `qwen serve` (main's maintained HTTP daemon) through the SDK's DaemonClient. New serve-agent/client.ts spawns `qwen serve --no-web` where the host used to spawn `qwen --acp`, then uses DaemonClient (REST + SSE) for session create / prompt / cancel / permission / streaming. Same public surface as AcpClient so the native host can swap in place. Compiles + typechecks against the real SDK API. NOT yet wired into native-messaging-host.ts — wiring is gated on two items that need runtime validation in a real Chrome + qwen env: - OAuth: host's onAuthenticateUpdate (in-extension authUri) maps to serve's server-side device-flow events — needs design. - permission requestId becomes string (was number) — host's permissionRequests map + handler need to follow. acp/ kept in place until the serve path is validated. Packaging follow-up: @qwen-code/sdk (→ core) as a dep of the standalone host is fine in the monorepo but needs a publish-time story (bundle or resolve from the co-installed qwen). * feat(browser-ext): daemon-direct connection foundation (Phase 1, #5626) First brick of the daemon-direct architecture from #5626: the extension talks straight to a local `qwen serve` HTTP daemon instead of a native messaging host. - daemon/config.ts: resolve { baseUrl, token } — default loopback http://127.0.0.1:4170 (auth-free), overridable via chrome.storage.local. - daemon/discovery.ts: GET /health probe so the side panel can show a "start qwen serve" hint instead of a broken chat when no daemon is up. Both typecheck clean. (Pre-existing tsc errors live only in the orphaned legacy sidepanel hooks — useWebViewMessages/useToolCalls etc. — which the DaemonSessionProvider migration removes next.) * docs(browser-ext): daemon-direct architecture spec (Phase 1+2, #5626) Concrete implementation spec: Phase 1 (side panel as daemon client, no daemon changes) and Phase 2 (browser tools as a client-hosted MCP server over the daemon WS). Phase 2 reuses the existing SdkControlClientTransport / SdkControlServerTransport pattern but moves the wire from the SDK subprocess control plane onto qwen serve's WebSocket — a new public daemon-contract surface, gated behind a capability flag (the open question in #5626). Includes the daemon-lifecycle options for #5626 Q3. * feat(browser-ext): Phase 1 — side panel as a daemon-direct client (#5626) Side panel chat now talks straight to a local `qwen serve` HTTP daemon via @qwen-code/webui's DaemonSessionProvider, replacing the native-messaging relay (background/ + content/ untouched; nativeMessaging perm kept for now). - SidePanelRoot.tsx: health gate — checkDaemonHealth(getDaemonConfig()); shows a "run qwen serve" hint + Retry when unreachable, else mounts DaemonSessionProvider around App. - App.tsx: rewritten daemon-driven — transcript/streaming/permissions/ lifecycle from the webui daemon hooks (useTranscriptBlocks, useStreamingState, usePromptStatus, usePendingPermissions, useConnection, useActions); reuses the existing webui presentational components + ChromePlatformProvider. - sidepanel/daemon/{transcriptItems,permission}.ts: adapters from daemon transcript/permission shapes to the existing UI components. - Deleted the dead legacy native-messaging chat hooks/types. Verified: sidepanel/daemon tsc clean; `npm run build` (esbuild) passes, emits the side-panel bundle with the daemon wiring. (Pre-existing tsc errors remain only in background/ + content/, out of scope until Phase 2.) Daemon contract accepted live against the worktree `qwen serve`: /health, /capabilities (advertises session_create/prompt/events/workspace_mcp), POST /session runs end-to-end to the model-auth gate. * feat(browser-ext): Phase 2 — browser tools over the daemon WS (#5626) Reverse tool channel: a WS client (the extension) hosts an MCP server (its browser tools) that the daemon's agent can call, carrying mcp_message JSON-RPC frames over the daemon WS — reusing the SdkControlClientTransport / SDK-MCP-server control-plane pattern. Gated behind capability flag `client_mcp_over_ws` (opt-in; the public-contract piece flagged in #5626). Daemon (core + cli/serve): - core/tools/client-mcp-registrar.ts: ClientMcpRegistrar — id-correlation, pending/timeout, notifications fire-and-forget, exposes the sendSdkMcpMessage(server,msg) callback McpClientManager consumes. - cli/serve/acp-http/client-mcp-ws.ts: ClientMcpWsConnection — handles mcp_register/mcp_message/mcp_unregister frames; pushes mcp_message down the WS; disposes on close. Hookup to the live agent McpClientManager is a ClientMcpServerProvider injection point (returns structured `not_wired` until the child↔parent reverse-IPC lands — see below). - capability `client_mcp_over_ws` threaded through serve options/capabilities. Extension (chrome-extension/background): - browser-tools-server.ts: minimal hand-rolled MCP JSON-RPC (initialize/tools/list/tools/call) reusing the existing tool-catalog + router + executors (MVP 6 read-first tools); WS client to the daemon /acp with mcp_register + reconnect. Wired into the service worker behind a health probe; native messaging left intact. Self-accepted (no LLM needed): 10/10 tests pass — a headless ws client registers + answers the MCP handshake over mcp_message and the daemon lists+CALLS the client-hosted chrome_read_page tool end-to-end over a real socket. Builds: core + cli + extension esbuild all green; tsc clean. NOT yet wired (out of scope here; needs acp-bridge + acpAgent reverse IPC): the parent-process WS ↔ ACP-child McpClientManager hookup — the daemon's WS lives in the parent serve process but sendSdkMcpMessage binds in the ACP child. Single injection at the mountAcpHttp call site once that IPC exists. * feat(serve): wire client-MCP-over-WS to the ACP child agent (#5626) Closes the Phase 2 gap: a client-hosted (extension) MCP server's tool calls now reach the agent's McpClientManager in the ACP CHILD, routed back to the parent's ClientMcpRegistrar and out over the daemon WS. Opt-in via QWEN_SERVE_CLIENT_MCP_OVER_WS=1 (the contract is still settling — dormant by default; this is the public-daemon-contract piece flagged in #5626). Contract additions: - ACP ext-method `qwen/control/client_mcp/message` (child→parent, called UP): {server,payload} → {payload}; notifications resolve with a synthetic ack. - runtime-MCP config flag `__clientMcpOverWs`: parent stamps it on the SDK-type add config; child KEEPS type:'sdk' (instead of stripping) so it binds an SdkControlClientTransport instead of the SDK subprocess control plane. - BridgeOptions.clientMcpSender seam + ServeAppDeps.clientMcpSenderRegistry. Round-trip: mcp_register(WS) → serve registers the connection's ClientMcpRegistrar.sendSdkMcpMessage in a process ClientMcpSenderRegistry + bridge.addRuntimeMcpServer(type:'sdk',__clientMcpOverWs) → child adds the SDK server + runs initialize/tools/list, each frame child→parent via client_mcp/message → BridgeClient looks up the sender → registrar pushes mcp_message down the WS → extension answers → child discovers the tools. Self-accepted LIVE (no LLM): integration-tests/cli/qwen-serve-client-mcp.test.ts spawns a REAL qwen serve + REAL qwen --acp child under a mock OpenAI server, a headless ws client registers + answers the MCP handshake, and the child discovers the client-hosted chrome_read_page tool over the genuine child→parent→WS channel (GET /workspace/mcp/chrome-tools/tools lists it). Verified passing locally (24.6s). +5 bridge round-trip tests; 324 acp-bridge, the 10 prior Phase-2, acpAgent 133, server+acp-http 675 all pass; builds clean. Not exercised here: a real LLM turn driving tools/call (needs creds), and a real Chrome extension as the WS client (headless ws stands in). * fix(serve): session-scope runtime MCP servers so client tool calls resolve (#5626) The reverse tool channel registered the client-hosted MCP server only on the bootstrap/workspace Config, so discovery worked but a prompt — which runs against an independent per-session Config from newSessionConfig→loadCliConfig — couldn't resolve the tool ("not found in registry"), and the reverse WS channel was never reached. Spec (docs/05) intends per-session scope. Fix (minimal, additive, guarded; normal settings-based MCP servers unaffected): - core/config.ts: add Config.getRuntimeMcpServers() (shallow copy of the private runtimeMcpServers map). - acpAgent.ts newSessionConfig: copy the bootstrap Config's runtime MCP servers into a newly-created session Config before initialize() so its discovery binds that session's sendSdkMcpMessage (register-before-session). - acpAgent.ts workspaceMcpRuntimeAdd/Remove: fan the add/remove out to every active session's McpClientManager (register-after-session), best-effort. Test: the fake model now emits the fully-qualified registered name mcp__chrome-tools__chrome_read_page (what a real model is handed); the reverse channel still forwards the bare tool name to the client server. Verified LIVE (no LLM, no creds): integration test now drives the FULL loop — model→agent→session-registry resolution→reverse client_mcp/message over the daemon WS→headless ws client returns CallToolResult→agent consumes it (tool completed)→turn_complete. 2/2 integration tests pass (confirmed locally). Regression: config 248, acpAgent 133, mcp-client-manager 101, client-mcp-ws 5 pass; builds clean. (server.test.ts has 2 pre-existing Web-Shell flakes, identical on the unmodified baseline.) Still needs a real Chrome extension (vs the headless ws stand-in) + a real model turn for true browser behavior; the protocol round-trip is proven. * chore(browser-ext): remove the dead Native Messaging stack (#5626) Daemon-direct made Native Messaging obsolete — the extension talks to `qwen serve` directly (chat over HTTP+SSE, browser tools over the daemon WS), so the entire native-host stack is dead weight. Deletes ~15.5k lines: - packages/mcp-chrome-integration/app/native-server/ — the whole native host (MCP servers, ACP client, Fastify server, doctor/register/report/postinstall, the superseded serve-agent backbone). - extension background transport: native-messaging.ts, native-connection.ts, native-message-handler.ts, native-messaging-types.ts, ui-request-router.ts (+ its test). The still-used executor types (BrowserToolArgs / RawNetworkRequest / WebSocketSession / NetworkCaptureState) move to background/browser-tool-types.ts. - service-worker rewired to daemon-direct only (drops the NativeMessaging init + the onMessage→routeUiRequest relay; keeps the browser-tools server start). - esbuild.background.config.js drops the deleted native-messaging entry point. - manifest.json drops the `nativeMessaging` permission. - package.json scripts drop every native-server/native-host reference; dev-watch no longer spawns the native host. - obsolete native-messaging docs (01-04) + scripts (diagnose/install/update) removed; README rewritten for daemon-direct. Extension esbuild build green; tsc errors dropped (84 -> 69, all pre-existing node:test/content typings). Daemon-side (cli/serve/core) untouched. * chore(browser-ext): drop orphaned content-fetch-patch.ts (#5626) * fix(serve): let browser extensions open the daemon WS reverse channel The daemon-direct Chrome extension (#5626) connects to qwen serve's /acp WebSocket to register its browser tools as a client-hosted MCP server. Three gaps blocked the real-browser path. A node WS client in the integration tests carries no browser Origin and completes the ACP handshake, so none of these surfaced until a real Chrome connected: - The WS CSRF check hard-coded loopback origins, so the extension's chrome-extension://<id> Origin was rejected with 403. Wire the existing --allow-origin allowlist into the WS upgrade check (acp-http/index.ts, server.ts) with the same match semantics as the REST allowOriginCors. - parseAllowOriginPatterns rejected chrome-extension:// because its URL.origin is the opaque "null". Rebuild the canonical origin from scheme+host for opaque-origin schemes (auth.ts), with tests. - The extension skipped the ACP initialize handshake and sent mcp_register directly, tripping the daemon's 30s initialize timeout. Send ACP initialize first and register only after the ack (browser-tools-server.ts). Also allow console.* in the extension package (no stdio in the MV3 runtime) via the eslint no-console allowlist. Verified end-to-end against a real Chrome: the daemon agent calls mcp__chrome-tools__chrome_read_page and reads the live active tab. * docs(browser-ext): Plan C (CDP tunnel) feasibility + implementation design Assess routing chrome-devtools-mcp's ready-made DevTools toolset through the extension's chrome.debugger to drive the user's real browser, instead of re-implementing each tool in the extension (Plan A). Records, with source verification against chrome-devtools-mcp@1.4.0 + puppeteer-core@25.2.0: - the createCDPSession wall (why zero-change reuse fails — Target.attachToTarget is "Not allowed" for chrome.debugger; cdp-mcp throws in McpContext.from); - the patch-package fork shape (pin 1.4.0 + a ~2-site patch, not vendor/submodule); - the daemon /cdp browser-level CDP emulation + sessionId routing design; - minimal browser-level command set, reusable prior art (playwright-mcp --extension), phased steps, risks, and the Plan A fallback. Refs #5626. * feat(serve): add CDP browser-level emulator for Plan C tunnel (#5626) First component of the Plan C "CDP tunnel": a synthesis layer that fakes the browser-level CDP topology so an external puppeteer client (chrome-devtools-mcp) can connect over a future /cdp endpoint while page-domain commands are forwarded to the one real tab via the extension's chrome.debugger. Implements the exact contract a Phase 0 spike proved necessary: a tab->page two-level target tree + recursive Target.setAutoAttach (browser attaches the tab session; the tab session attaches the page session), with page-session commands routed to forwardToTab and tab events re-tagged with the page session id. The spike connected real puppeteer to a pure synthesis layer and ran page.evaluate(() => 1 + 1) === 2. 7 unit tests cover the handshake + routing. Refs #5626. * feat(serve): add CDP tunnel reverse-link, /cdp glue, and bridge registry (#5626) Plan C Phase 1 daemon core. The reverse-link forwards page-domain CDP commands to the extension over cdp_command/cdp_result frames (id-correlated, timeout) and re-tags cdp_event onto the page session; cdp-ws wires a per- puppeteer-connection emulator to the reverse-link bound to the single active extension bridge in a process-scoped registry. The emulator gains setTabInfo so the synthetic targetInfo reflects the real tab after cdp_attach. * feat(serve): wire /cdp upgrade branch and cdpTunnelOverWs flag (#5626) Adds the /cdp WebSocket upgrade branch to acp-http (reusing the loopback / host-allowlist / auth / CSRF checks) and routes inbound cdp_* frames on the extension's /acp socket to the bound reverse-link. The extension connection registers as the active CDP bridge eagerly at ACP initialize so a /cdp puppeteer client can bind immediately (avoids the attach chicken-and-egg). Feature flag cdpTunnelOverWs is wired exactly like clientMcpOverWs (env QWEN_SERVE_CDP_TUNNEL_OVER_WS=1, capability cdp_tunnel_over_ws), DEFAULT OFF — existing behaviour is unchanged when off. * feat(browser-ext): add CDP bridge over the reverse /acp socket (#5626) The extension answers cdp_attach by attaching chrome.debugger to the active tab and cdp_command via chrome.debugger.sendCommand, replying cdp_result; chrome.debugger.onEvent -> cdp_event, onDetach -> cdp_detach. Reuses the existing browser-tools-server /acp socket (routes cdp_* frames; tears the bridge down on socket close) and mutually excludes with the chrome_network_debugger_* tools (one debugger per tab). * build(deps): pin chrome-devtools-mcp 1.4.0 + puppeteer-core 25.2.0, patch McpContext (#5626) Pins the two CDP-tunnel client deps (exact, to keep the version-specific patch and puppeteer's hardcoded ExtensionTransport topology stable) and adds patches/chrome-devtools-mcp+1.4.0.patch. The patch wraps McpContext.#init's devtoolsUniverseManager.init / serviceWorkerConsoleCollector.init in try/catch so the createCDPSession wall (Target.attachToTarget -> -32000 over chrome.debugger) no longer crashes the server on startup; only performance_* / service-worker console degrade. Applied via the existing postinstall patch-package hook (same form as patches/ink+7.0.3.patch). * test(serve): add Plan C /cdp end-to-end acceptance harness (#5626) Node script that starts the real daemon with the flag on, connects a mock extension over /acp (ACP initialize + mcp_register, answering cdp_command with page-domain CDP), then puppeteer.connect to /cdp and asserts page.evaluate(() => 1 + 1) === 2 through the real daemon + emulator + reverse-link. Allowlists the harness dir in eslint's node-script globals. * fix(serve): gate CDP page commands behind attach completion (#5626) Real-Chrome testing surfaced an ordering race the mock acceptance missed: the extension's chrome.debugger.attach is async (it pops the debugger banner), but forwardToTab forwarded page-domain commands immediately, so a fast puppeteer Network.enable reached the extension before attachedTabId was set and failed "CDP tunnel not attached to a tab". The mock extension acked attach synchronously, which hid the race. Add an attach gate in CdpReverseLink: forwardToTab awaits the in-flight cdp_attach to settle (success or failure) before forwarding. Verified end-to-end against REAL Chrome — puppeteer read the live active tab (a GitHub PR page) through the tunnel: pages=1, real url/title/body returned. Refs #5626. * refactor(chrome-extension): delete Plan A side panel, content scripts, and reverse tool channel Tears out the superseded Plan A surface so the extension can become a pure CDP-tunnel pipe (chat moves to the daemon web UI, browser tooling runs as chrome-devtools-mcp over the /cdp tunnel): - src/sidepanel/ (side-panel chat UI) - src/content/ (content scripts; the CDP tunnel drives DOM/Input via chrome.debugger, no injection needed) - background reverse tool channel: browser-tools-server, browser-network-tools, network-capture-utils, browser-tool-executors, tool-catalog, tool-router, mcp-tool-result, browser-tool-types, and their tests - public/sidepanel/sidepanel.html static asset Refs #5626 * refactor(chrome-extension): rewrite service worker as minimal daemon CDP client The service worker is now the entire extension logic: probe the daemon /health, open the /acp WebSocket, send the ACP initialize handshake (the daemon closes the socket on a 30s init timeout otherwise and binds this connection as the CDP bridge at that point), then route cdp_* frames into the CDP bridge with capped backoff reconnect. No more reverse MCP tool server (chrome-tools is gone). cdp-bridge: drop the browser-network-tools import and the network-capture mutual-exclusion branch in handleAttach (the network tools are deleted, nothing to exclude); remove the now-unused isCdpTunnelAttached export. Refs #5626 * build(chrome-extension): trim manifest, build config, and deps to the CDP pipe manifest: drop content_scripts, side_panel, and the sidePanel/webRequest/cookies/ scripting/webNavigation permissions; keep only debugger/tabs/activeTab/storage plus background, key, host_permissions, icons, and action. build: background esbuild now has a single service-worker entry point (content script gone); delete the UI esbuild/postcss/tailwind configs and drop the UI build step + build:ui scripts; sync-extension no longer special-cases the gone sidepanel assets; dev-watch no longer spawns the UI watcher. deps: remove the side-panel-only deps (@qwen-code/webui, react, react-dom, markdown-it, and the @types + the postcss/tailwind/autoprefixer CSS toolchain). Refs #5626 * chore(chrome-extension): drop dead externally_connectable hook (#5626) * test(serve): add real-Chrome /cdp local verification script (#5626) * test(serve): add cdp-mcp-over-tunnel layer-C smoke check * fix(extension): keep the CDP tunnel alive with chrome.alarms MV3 service workers idle out after ~30s, so the tunnel silently dropped whenever no puppeteer client was driving it and the user had to keep the Service Worker DevTools open to hold the worker awake. Register a 30s chrome.alarms keepalive: the recurring onAlarm dispatch holds the idle timer off, and each wake of a terminated worker re-runs the top level to reconnect. * feat(serve): auto-register chrome-devtools-mcp over the CDP tunnel Plan C (#5626) last mile: when `qwen serve` runs with the CDP-tunnel flag, the agent should be able to drive the user's real browser. Rather than hand-writing browser tools, auto-register the (patched) chrome-devtools-mcp as a session MCP server pointed at this daemon's /cdp endpoint, so its 29 ready-made DevTools tools flow through the tunnel. - run-qwen-serve: forward QWEN_SERVE_CDP_TUNNEL_OVER_WS + _PORT into the spawned ACP child via childEnvOverrides (same path as the MCP budget env). - acpAgent: buildCdpTunnelMcpServer() injects the server into the top-precedence sessionMcpServers tier when the flag + port are present and the package resolves; trust left unset so tools default to 'ask' (no silent auto-approval of browser control); best-effort skip otherwise. No settings.json edit and no hand-written tools required. * fix(serve): gate CDP bridge registration to the extension (#5626) Auto-registering every /acp initialize as the CDP bridge was last-writer- wins. Once an ACP agent connects over the same /acp endpoint (web UI, Zed), it would capture the bridge and receive cdp_* frames it can't answer, stealing the tunnel from the extension. Gate registration on clientInfo.name === 'qwen-cdp-bridge'; the extension (and the acceptance mock) now identify themselves that way, while agent clients are left alone. * feat(extension): open the web UI when the toolbar icon is clicked The extension has no UI of its own (pure CDP-tunnel pipe; chat lives in the daemon web UI), so clicking the toolbar icon did nothing after the side panel was removed. Wire action.onClicked to open the daemon baseUrl in a new tab so the icon is a useful entry point instead of a dead click. * feat: host the web UI in a Chrome side panel (#5626) The extension is a pure CDP-tunnel pipe with no UI of its own, so after the side panel chat was removed the toolbar icon did nothing. Bring the side panel back as a thin host that iframes the daemon web UI (chat + tools), so the sidebar is the everyday entry point and reuses the web UI's pages/components instead of shipping a second UI in the extension. - extension: side_panel + sidePanel permission; sidepanel.html/js frames the daemon baseUrl; toolbar icon opens the panel (openPanelOnActionClick). - daemon: the Web Shell sent frame-ancestors 'none' + X-Frame-Options: DENY, which blocked the iframe. Allow framing only for chrome-extension origins explicitly passed via --allow-origin; everything else still gets DENY. * fix: prefer chrome-devtools over computer-use under the CDP tunnel + keep MV3 worker alive during attach (#5626) Two issues surfaced driving the real agent: 1. The agent picked the OS-level computer-use tool (cua-driver) for browser tasks instead of the injected chrome-devtools-mcp — heavyweight screenshot/ click loop that pegged a CPU and stalled turns. Disable computerUse when the CDP tunnel flag is on so browser automation goes through the tunnel. 2. The extension's MV3 service worker idled out *between* CDP commands (the agent pauses to think), detaching chrome.debugger and hanging the next command. Add a sub-30s keepalive while attached; the 30s alarm only covered idle reconnects, not in-flight attachments. * chore(extension): stop tracking .extension-key.pem (signing private key) The extension signing private key was committed and pushed — anyone with repo access could impersonate the extension. Stop tracking it and gitignore *.pem. Load-unpacked debugging needs only the public "key" in manifest.json, so this doesn't affect dev on any machine; the .pem is kept locally for packaging. * refactor(chrome-extension): flatten to packages/chrome-extension Drop the mcp-chrome-integration wrapper + dead native-server (daemon-direct no longer uses native messaging). The extension is now a top-level workspace at packages/chrome-extension. Updated root workspaces, eslint globs, doc-path comments, and the two node scripts' global directives. cli + extension builds and eslint verified green. * test(serve): assert cdp_tunnel_over_ws in the capability registry The cdp_tunnel_over_ws capability was added to SERVE_CAPABILITY_REGISTRY + CONDITIONAL_SERVE_FEATURES but the test's EXPECTED_REGISTERED_FEATURES and the conditional drift-insurance branch weren't updated, failing 3 registry tests. Add it to the expected list (after client_mcp_over_ws, matching registry order) and add its assertion branch (predicate accepts/rejects the cdpTunnelOverWsEnabled toggle). * fix(serve): address review comments on CDP tunnel + client-MCP wiring (#5777) - guard post-async ws.send() with readyState OPEN (daemon crash on extension disconnect, sendClientMcpAck + cdp endpoint sends) - make client-mcp-sender-registry delete() ownership-aware (cross-connection server-name collision) - type the __clientMcpOverWs runtime config flag carrier - document the CDP tunnel trust model (loopback + bridge-gated dumb pipe) * chore(cdp-tunnel): set copyright year to 2026 on files created this year The Plan C / #5626 files were authored in 2026 but carried a 2025 header. service-worker.ts (created last year under #1432) keeps 2025. * feat(chrome-ext): add side-panel onboarding gate + fix packaging The side panel framed the daemon Web Shell unconditionally and showed a static "Connecting…" line. When no daemon was reachable, or the daemon wasn't started with --allow-origin (so frame-ancestors blocks the iframe), the user was stuck on "Connecting…" with no guidance, and discovery.ts's health check was never wired in. Wire a health/capabilities gate into the panel: - probe GET /health, then GET /capabilities - down → "Start qwen serve" + the exact command - up but no `allow_origin` feat → "Allow this extension" + the command - ready → frame the Web Shell The command is built from chrome.runtime.id at runtime, so it always names this extension's real origin (dev-unpacked or published) — no need to know or hardcode the id, and no publish-first chicken-and-egg. The pure decision helpers live in onboarding-logic.js. Also: - fix `npm run package` so manifest.json sits at the zip root (the old `zip ... extension/` nested it under extension/, which the Chrome Web Store rejects) - gitignore that packaging zip; add a package README Part of #5626. PR #5777. * docs(cdp-tunnel): trim over-long Plan C comments (ponytail) Comment-only: compress multi-paragraph design rationale to intent, keep the non-obvious why (trust model, bridge gate, attach ordering) + ponytail ceilings. ~-91 lines, no code touched, build + tests green. * fix(serve): address second-round review comments on the CDP tunnel (#5777) - close the bound /cdp puppeteer socket on extension disconnect (was hanging ~170s on CDP timeout) via an onExtensionGone hook - reject a 2nd concurrent /cdp client instead of silently clobbering routeInbound - narrow extension host_permissions from <all_urls> to localhost (chrome.debugger needs no host perm; only the /health fetch needs localhost) - log safeWsSend drops under serve debug mode so a dead tunnel is diagnosable * feat(chrome-ext): polish side-panel welcome into a terminal console Replace the bare welcome with a console that matches the product (a CLI daemon): the command is the hero, typed at a `$` prompt with a blinking cursor inside a titled terminal card. Warm-charcoal / electric-lime, light + dark aware (prefers-color-scheme), staggered load-in, a pulsing "listening" status, and a reduced-motion guard. No web fonts / no inline JS (the extension CSP allows neither). No behavior change: same /health + /capabilities gate and the chrome.runtime.id-derived command. Mechanics tidied alongside the markup: visibility toggles a .hidden class (CSS owns the flex layout), the copy button updates a label span, and the command is prefilled synchronously so first paint isn't an empty prompt. PR #5777. * feat(chrome-ext): click-to-copy command + centered copy button Make the onboarding command easier to grab: the whole command row is now click-to-copy (keyboard-reachable, Enter/Space), and a centered "Copy command" button sits at the foot of the terminal card. Both flash a check-mark "Copied" confirmation; the small top-bar button is gone. No gate-logic change. PR #5777. * test(serve): cover CDP-tunnel + client-MCP regression guards Add the four focused unit tests flagged in review for load-bearing reverse-channel paths that had no coverage: - ClientMcpSenderRegistry: ownership-scoped delete — a disconnecting connection must not remove an entry a peer re-registered under the same name. - CdpTunnelRegistry: register/supersede/unregister lifecycle, inbound routing delegation, onExtensionGone-on-disconnect, and the stale-unregister guard that must not evict a newer active bridge. - CdpReverseLink: a forwarded command rejects when its per-command timer expires (not just on bulk dispose). - safeWsSend: drops (no send, no throw) on a CLOSED/CLOSING socket. safeWsSend is extracted from acp-http/index.ts into its own module so it's unit-testable in isolation; behavior unchanged. PR #5777. * fix(serve): address bot code-review findings on the CDP tunnel (#5777) - cdp-bridge: tear down listeners before re-attach (was double-registering → duplicate cdp_event frames corrupting puppeteer) - emulator: return a CDP error for an unknown session instead of fake success - registry: notify the superseded bridge so the old /cdp closes (single-puppeteer) - deps: move chrome-devtools-mcp + puppeteer-core to optionalDependencies (~26MB) - tests: entry-script validation, deliverClientMcpMessage error branches, registry supersede * fix(chrome-ext): revert side panel to welcome when the daemon stops Once the panel framed the Web Shell it stopped probing, so if the daemon later went away the iframe was left showing Chrome's localhost connection-refused page with no way back. Keep probing after framing and, after a short tolerance (2 misses, ~5s, so a transient blip doesn't nuke a live chat), clear the iframe src and show the welcome screen again. PR #5777. * fix(chrome-ext,serve): address review comments on the CDP tunnel - service-worker: redact the bearer token from the connect log, and guard connect() against a still-CONNECTING socket so a rapid reconnect can't orphan an in-flight handshake. - acpAgent: don't clobber a user-configured `chrome-devtools` MCP server with the tunnel auto-wire. - cdp-reverse-link: log dropped/unexpected inbound frames via an optional diagnostic sink instead of swallowing them silently. - name the cross-package `qwen-cdp-bridge` client-name constant on both sides instead of repeating the bare string. PR #5777. * refactor(chrome-ext): inline onboarding helpers, drop dead pollTimer onboarding-logic.js was a 65-line file (mostly JSDoc) for three trivial helpers and a constant, split out "for testability" that was never used. Fold them into sidepanel.js (decideState collapses into probeState's return; resolveBaseUrl/allowOriginCommand become a one-liner each) and delete the file + its import. Also drop `pollTimer`: the welcome-fallback change made it write-only (the only clearInterval was removed), which trips no-unused-vars. PR #5777. * fix(serve,chrome-ext): more CDP-tunnel review fixes - sidepanel: pass the bearer token through the Web Shell URL fragment so a token-gated daemon doesn't 401 every framed request. - server: throw if deps.bridge is injected without deps.clientMcpSenderRegistry (the bridge is already wired to its own sender; a fresh one would be orphaned). - cdp-browser-emulator: surface unhandled browser-level CDP commands via an optional log sink (keep the empty-result ack, with a TODO). - cdp-bridge: only treat "already attached" as ours when attachedTabId === tabId (a foreign DevTools owner now errors), and detach the previous tab on switch so Chrome drops its debug banner. - build.js: add packages/chrome-extension to the build order so root build exercises the extension bundle. PR #5777. * fix(serve,chrome-ext): harden CDP-tunnel + client-MCP reverse channel - client-mcp-ws: cap registered servers per connection (max 10) and re-check `disposed` after the provider round-trip so a WS close mid-register can't leave a zombie server; add registrar serverCount(). - acp-http: rate-limit client-MCP frames (mcp_register/unregister at the mutation tier, mcp_message at read) and cap concurrent fire-and-forget register/unregister dispatch (max 8) to stop DoS amplification. - cdp tunnel: on /cdp puppeteer disconnect send a `cdp_release` frame so the extension detaches chrome.debugger instead of leaving the tab's debug banner up until /acp dies. - acpAgent: skip chrome-devtools auto-registration (with a diagnostic) when the /cdp tunnel requires bearer auth — the ACP child can't authenticate to it. PR #5777. * fix(chrome-ext,serve): address second-round CDP-tunnel review - service-worker: only the *active* socket's close tears down the bridge — a stale daemon-forced close must not detach the new connection's debugger. - daemon config + sidepanel: fail closed on a non-loopback baseUrl so a tampered chrome.storage value can't exfiltrate the bearer token off-host (background fetch/WS bypass host_permissions). - sidepanel: reentrancy guard on tick() so overlapping slow probes don't burn the framed-miss tolerance and flash the welcome screen mid-chat. - cdp-bridge: reentrancy guard on handleAttach so overlapping cdp_attach frames can't interleave teardown and corrupt attachedTabId. - add cdp-ws.test.ts: regression cover for no-bridge reject, second-client reject, onExtensionGone fail-fast, cdp_release on dispose, and the superseded-bridge cleanup guard. PR #5777. * fix(serve,webui): address third-round CDP-tunnel review - safe-ws-send: wrap the debug-mode writeStderrLine in try/catch so a broken stderr (EPIPE on a piped/closed log) can't break the "never throw on a dead socket" contract production callers rely on. - ChromeToolCall: index-access rawInput['name'] to satisfy noPropertyAccessFromIndexSignature. PR #5777. * fix(serve,chrome-ext): address #5777 review round 4 - acp-http: send mcp_error back on an unexpected client-MCP handler rejection (register/unregister callers otherwise hang); log when the inflight cap rejects. - cdp-ws: log the happy-path cdp_release dispatch for oncall tracing. - run-qwen-serve/acpAgent: don't pass a bogus "0" CDP-tunnel port for ephemeral --port 0, and emit a stderr diagnostic when the tunnel is disabled for a missing/invalid port instead of failing silently. - cdp-bridge: handle a cdp_release that races an in-flight handleAttach so a late attach can't leave a debugger attachment with no live /cdp client. - service-worker: close the WS on an ACP initialize error so the daemon doesn't keep holding a non-functional CDP bridge. * fix(serve,chrome-ext): address pr-review findings (#5777) - client-mcp-sender-registry: gate the child-side runtime-server teardown on ownership too (Config.removeRuntimeMcpServer is not owner-scoped), so a disconnecting connection can't kill a server a later connection re-registered under the same name. (P2) - service-worker: carry the bearer token via the `qwen-bearer.*` WS subprotocol (matching the web-shell + daemon decoder) instead of a `?token=` query the daemon never reads, so a token-gated daemon no longer 401-reconnect-loops. (P3) - run-qwen-serve: advertise client_mcp_over_ws / cdp_tunnel_over_ws in the bootstrap /capabilities too, matching the runtime path. (P3) * chore(webui): remove unused ChromeToolCall component (#5777) ChromeToolCall was added in a debugging commit but is never wired into the tool-call routing (getToolCallComponent) — there's no `chrome` tool kind and the only references were barrel re-exports. Dead code; remove it. * fix(serve,chrome-ext): address #5777 review round 5 (diagnostics) - service-worker: log the WS close code/reason so failure modes aren't indistinguishable (e.g. the daemon's 1011 "no extension connected"). - acpAgent: containment-check the resolved chrome-devtools-mcp bin path so a malformed `bin` field can't escape the package dir. - cdp-tunnel-registry: log when a new extension bridge supersedes a stale one. * fix(chrome-ext,serve): address #5777 review round 6 - cdp-bridge: ack the attach (as an error) before tearing down on a release-during-attach, so the daemon's reverse link doesn't hang ~170s waiting for a cdp_attached that never arrives. - acp-http: rename isFireAndForget -> dispatchOffQueue + clarify the comment; register/unregister are dispatched off-queue but still expect a response ack, so the name no longer reads as "no response." * fix(serve,cli): address #5777 review round 7 - config: warn (stderr) when QWEN_SERVE_CDP_TUNNEL_OVER_WS overrides an explicit tools.computerUse.enabled=true, so the effective config isn't a silent surprise. - client-mcp-ws: document the intentional idempotency of handleUnregister. - cdp-reverse-link: add tests for the attach gate — forwardToTab parks behind an in-flight attach, the cdp_attach timer rejects, and the gate opens on timeout so commands don't hang. * fix(serve,core): address #5777 review round 8 - mcp-client: only treat the first stdio arg as a local entry script when it is clearly a filesystem path. A bare includes('/') also matched scoped npm package names (npx @scope/pkg), wrongly resolving them under the workspace and throwing before the runner ran. Adds a regression test. - client-mcp-sender-registry: reject shadowedSettings so a browser-hosted WS client cannot shadow a user-configured MCP server name; roll back the child-side add. - cdp-ws: close the puppeteer socket when /cdp attach fails so dispose() clears cdpBound/routeInbound, instead of a stuck tunnel until restart. * fix(serve,chrome-ext): address #5777 review follow-ups * fix(serve): satisfy CDP inbound frame guard typing * fix(serve): address CDP tunnel review feedback --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
a4203da49d
|
fix(packaging): bundle audio capture for mirror installs (#5747)
* fix(packaging): bundle audio capture for mirror installs * fix(packaging): include audio prebuilds before packaging * fix(cli): narrow native audio load error wrapping * test(packaging): cover native audio fallback paths * fix(packaging): require native audio artifacts * fix(packaging): allow fork release without audio prebuilds * fix(packaging): keep fork audio dependency fallback * test(packaging): harden audio bundle coverage * fix(packaging): validate native audio artifacts * fix(packaging): harden native audio fallback paths * fix(packaging): tighten audio bundle copy |
||
|
|
b9c5e3566b
|
feat(voice): voice dictation with native capture, streaming, and biasing (#5502)
* feat(voice): voice dictation with native capture, streaming, and biasing
Add voice dictation for the prompt input:
- /voice [hold|tap|off|status] command + general.voice.{enabled,mode,language,protocol} settings; push-to-talk via Space, /model --voice to pick the model
- Native microphone capture (@qwen-code/audio-capture, miniaudio N-API) with arecord/SoX fallback, silence auto-stop, cold-start warm-up, and macOS permission query
- Batch transcription via DashScope Qwen-ASR (OpenAI-compatible chat/completions + input_audio) with language + keyterm biasing and an echo guard
- Live streaming over the DashScope realtime WebSocket (fun-asr-realtime / paraformer-realtime-v2) with interim text and an input-level waveform, behind voice.protocol=dashscope-realtime
- Rich VoiceIndicator UI (state, level meter, live partial transcript)
- Cross-platform prebuilds via prebuildify + node-gyp-build and a CI matrix
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* feat(cli): route voice ASR by model
* feat(cli): polish voice realtime parity
* ci: fix voice workflow checks
* fix(cli): address voice review blockers
* fix(cli): harden voice transcription failures
* fix(cli): address voice PR review blockers
* fix(cli): harden voice review follow-ups
* fix(voice): handle realtime review blockers
* fix(cli): add voice command i18n keys
* fix(cli): address voice review blockers
* fix(cli): address voice review follow-ups
* fix(voice): harden review edge cases
* fix(voice): address release and realtime review blockers
* fix(voice): address realtime suggestion followups
* fix(voice): handle realtime review followups
* fix(voice): address realtime review blockers
* fix(voice): address review feedback
* fix(voice): address recorder review feedback
* docs(voice): document ssrf guard boundary
* fix(voice): address review feedback
* fix(voice): preserve warm recorder session safety
* fix(voice): address stream review suggestions
* fix(voice): address multi-round review findings
Realtime/streaming:
- Salvage an already-committed transcript when the WebSocket closes right
after finish() instead of rejecting the whole dictation
(qwenAsrRealtimeSession, voiceStreamSession) + regression tests.
useVoiceInput state machine:
- Single-shot finalize guard so a tap-stop racing the silence auto-stop can't
double-stop the recorder and surface a spurious failure.
- Reset mountedRef on (re)mount so StrictMode (DEBUG) can't freeze the voice UI.
- Widen the hold-mode first-press release window above common key-repeat delays.
Model selection:
- Reject ids with no ASR transport at /model --voice and in the model dialog via
a new isSelectableVoiceModel; move resolveVoiceTransport into voiceModel so the
record-time config resolver stays transport-agnostic (+ tests).
macOS mic permission:
- Surface the not-determined state in voice warmup so the first dictation isn't
silently lost behind the TCC dialog.
Native packaging:
- Make the audio-capture native install non-fatal (falls back to SoX/arecord) so
a voice-only build failure can't break npm ci.
- Download audio-capture prebuilds before building standalone release archives.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(voice): address review blockers
* fix(voice): update batch recording audio level
* fix(voice): tap-mode transcript loss, stream leaks, and dead keyterm cleanup
- Tap-mode dictation submitted a stale empty buffer.text, wiping the just-
inserted transcript and sending nothing: thread the resulting prompt text
through onSubmit(text) instead of reading buffer.text back synchronously
after the async insert (useVoiceInput, InputPrompt).
- Streaming finalize leaked the WebSocket session when recorder.drain() threw:
abort the session before propagating the error.
- voiceStreamSession: reject the connect promise when 'task-finished' arrives
before 'task-started' instead of hanging forever in 'transcribing'.
- Remove dead keyterm enrichment (project/branch/recent-file paths were
unreachable after the privacy fix) and the now-inert CJK echo guard, plus the
tests that asserted that removed behavior; fix the misleading "OpenAI prompt
field" comment.
- Add the missing 'Voice Model' and macOS mic-permission i18n keys to
en/zh/zh-TW (they were falling back to English; check-i18n doesn't flag keys
absent from en).
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(voice): reject partial stream transcripts
* fix(cli): let dialogs consume voice keys first
* fix(voice): reject incomplete qwen realtime transcripts
* fix(voice): handle stream review blockers
* fix(voice): salvage qwen realtime transcript on close
* fix(voice): sanitize streamed transcript text
* test(audio): run audio capture tests in CI
* fix(voice): address review blockers
* fix(voice): report stream close while recording
* fix(voice): reduce keyterm echo false positives
* fix(voice): handle realtime close diagnostics
* fix(voice): address realtime review blockers
* fix(lint): allow legacy voice filenames
* chore(cli): rename voice files to kebab-case
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
||
|
|
715ef938f5
|
feat(cli): serve the Web Shell UI from qwen serve (#5392)
* feat(cli): serve the Web Shell UI from `qwen serve` `qwen serve` now serves the built Web Shell SPA at its root on the same origin as the API, so a released binary exposes the browser terminal without the dev-only Vite server (the `npm run dev:daemon` two-process setup is unchanged for development). - New `webShellStatic.ts` mounts `/`, `/assets/*` and an SPA deep-link fallback. The fallback uses the same document-navigation discriminator as the Vite dev proxy so it never shadows API JSON 404s. - The static shell is registered BEFORE bearerAuth (a browser can't attach a token to a `<script>` subresource or an address-bar navigation; the shell carries no secrets and every API route stays token-gated). HTML responses set CSP + X-Frame-Options + Referrer-Policy + no-cache. - `--open` launches the browser at the daemon URL (with `?token=` when set) once the listener is up, guarded by `shouldLaunchBrowser()`. - `--no-web` opts out for an API-only daemon. - Bundle / npm publish / standalone packaging now ship `dist/web-shell/`. Missing assets degrade to API-only with a breadcrumb, never a hard fail. Tests: +6 cases in server.test.ts (root shell, assets, SPA fallback, non-navigation 404 passthrough, security headers, --no-web off). * fix(cli): address review on Web Shell serving Review fixes for #5392 (qwen-code-ci-bot): - [Critical] SPA fallback no longer shadows /health or /demo on non-loopback binds — those paths fall through to their own routes / bearerAuth instead of receiving index.html. - [Critical] --open trims the bearer token before putting it in the browser URL, matching runQwenServe's own trimming, so a trailing newline from `$(cat token.txt)` no longer makes every API call 401. - --open is wrapped in its own try/catch so a failed browser launch can't take down the already-listening daemon; it normalizes wildcard binds (0.0.0.0 / ::) to loopback, and only fires when the UI is actually mounted (new RunHandle.webShellMounted). - resolveWebShellDir() now requires BOTH index.html and assets/, so a partial build degrades to API-only instead of serving a shell whose chunks 404. - runQwenServe logs a positive "Web Shell UI served from <dir>" breadcrumb, and warns that on a non-loopback bind without --allow-origin the shell is read-only (same-origin POSTs are blocked by the CORS wall). - Document the --open token-in-process-list exposure in help text + a stderr note when a token is forwarded. - Tests: POST method guard, sec-fetch navigation signal, /health not shadowed, sendFile 500 path, plus isDocumentNavigation and resolveWebShellDir units. * fix(cli): harden Web Shell asset resolution and send-error logging Second-round review (claude /qreview on the initial commit): - resolveWebShellDir() now walks up from this module to find a sibling packages/web-shell/dist, covering the transpiled layouts the previous fixed `..` depth missed — per-package `tsc` output and the integration daemon harness (packages/cli/dist/index.js), which would otherwise resolve to nonexistent paths and silently run API-only. - sendFile failures are no longer silent: log the error (matching the /demo handler — previously the only 5xx path that emitted nothing) and res.end() a half-streamed response instead of leaving the client on a 200 with a partial body. The remaining comment (open-browser inside the boot try) was already fixed in |
||
|
|
45efb1d3aa
|
fix(cli): bundle extension examples (#4719) |