qwen-code/packages/chrome-extension
AlexHuang 06cd7ce13f
feat(cli): add --project and --global flags to /model for per-project model persistence (#6060)
* feat(cli): add --project and --global flags to /model for per-project model persistence

Add scope control to the /model command so users can persist model
selections to either project-level or user-level settings independently.

- /model --project: persist to workspace .qwen/settings.json
- /model --global: persist to user ~/.qwen/settings.json
- /model (no flag): unchanged behavior (backward compatible)
- Model dialog title shows scope: 'Select Model (this project)' / 'Select Model (global)'
- Completion and argumentHint updated with new flags
- Full i18n support for zh/en

Closes #6052

Signed-off-by: Alex <alex.tech.lab@outlook.com>

* fix(cli): add missing zh-TW translations for /model scope flags

Signed-off-by: Alex <alex.tech.lab@outlook.com>

* fix(cli): address PR review — scope flags, subcommand persistScope, titles, tests

- parseScopeFlags: use (?:^|\s) instead of \b for --flag matching
  (\b fails because - is not a word character)
- Completion: strip all flags to isolate model prefix, supports any order
- Subcommand dialogs (fast/voice/vision) now propagate persistScope
- slashCommandProcessor forwards persistScope for all subcommand cases
- ModelDialog title combines subcommand mode + scope label
  e.g. 'Select Fast Model (this project)'
- Subcommand confirmations show scope suffix (project/global)
- Extract persistScopeSpread() helper to reduce duplication
- Add 9 tests covering scope flags, dialog returns, confirmations
- Add i18n keys for scope suffix labels in zh/en/zh-TW

Signed-off-by: Alex <alex.tech.lab@outlook.com>

* fix(cli): use Partial<Config> & {[key:string]:unknown} to fix index signature TS error

Replace Record<string,unknown> with Partial<Config> & {[key:string]:unknown}
to satisfy TS4111 index signature access rule in the CI build.

Signed-off-by: Alex <alex.tech.lab@outlook.com>

* fix(cli): add scope suffix to ModelDialog history items

Address review comment: historyManager.addItem for voice/fast/vision/main
model selections now shows scope indicator like ' (this project)' or
' (global)', consistent with CLI direct-set confirmations.

Affected: handleModelSwitchSuccess (main), handleSelect (voice/fast/vision)
Signed-off-by: Alex <alex.tech.lab@outlook.com>

* fix(cli): wrap scopeSuffix in t() and unify wording with ModelDialog

- scopeSuffix in modelCommand.ts now uses t(' (this project)') / t(' (global)')
  instead of hardcoded English strings, matching ModelDialog.tsx wording
- Main model confirmation uses shared scopeSuffix instead of separate
  i18n keys, eliminating 'Model: {{model}} (project)' duplication
- Remove unused i18n keys from en/zh/zh-TW locales
- Update tests to expect '(this project)' wording

Signed-off-by: Alex <alex.tech.lab@outlook.com>

* fix(cli): address code review feedback — scope validation, i18n, tests

- Reject inline prompt + scope flag combination with clear error (#1)
- Add mutual exclusivity check for --project and --global (#5)
- Verify setValue scope parameter in tests + add --global test (#2)
- Extract scopeSuffix to shared variable, remove duplication (#3)
- Remove dead i18n keys 'Select Model (this project)' / '(global)' (#4)
- Fix scopeSuffix placement on model line not API key line (#8)
- Add fr.js / ja.js translations for scope keys (#10)
- Remove unused export ModelDialogPersistScope (#6)
- Wrap non-interactive help text in t() with new flags (#7)
- Fix argumentHint grouping to show mode vs scope flags (#11)

Signed-off-by: Alex <alex.tech.lab@outlook.com>

* fix(cli): reject --project when workspace is untrusted

Reject --project scope flag before direct persistence or opening ModelDialog
when settings.isTrusted is false. Workspace settings are ignored on merge in
that state, so the save would silently not take effect.

Also mirrors the guard in ModelDialog.tsx resolvePersistScope() to fall back
to user scope when the dialog is opened with --project on an untrusted folder.

Default mock settings now includes isTrusted: true.

Signed-off-by: Alex <alex.tech.lab@outlook.com>

---------

Signed-off-by: Alex <alex.tech.lab@outlook.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
2026-07-07 04:49:36 +00:00
..
config feat(browser-ext): revive Chrome extension via daemon-direct architecture (#5777) 2026-06-28 15:57:31 +00:00
docs feat(browser-ext): revive Chrome extension via daemon-direct architecture (#5777) 2026-06-28 15:57:31 +00:00
public feat(browser-ext): revive Chrome extension via daemon-direct architecture (#5777) 2026-06-28 15:57:31 +00:00
scripts feat(browser-ext): revive Chrome extension via daemon-direct architecture (#5777) 2026-06-28 15:57:31 +00:00
src feat(cli): add --project and --global flags to /model for per-project model persistence (#6060) 2026-07-07 04:49:36 +00:00
.gitignore feat(browser-ext): revive Chrome extension via daemon-direct architecture (#5777) 2026-06-28 15:57:31 +00:00
package.json chore(release): v0.19.6 (#6280) 2026-07-03 16:37:54 +00:00
README.md feat(browser-ext): revive Chrome extension via daemon-direct architecture (#5777) 2026-06-28 15:57:31 +00:00
tsconfig.json feat(browser-ext): revive Chrome extension via daemon-direct architecture (#5777) 2026-06-28 15:57:31 +00:00
vitest.config.ts feat(browser-ext): revive Chrome extension via daemon-direct architecture (#5777) 2026-06-28 15:57:31 +00:00

@qwen-code/chrome-bridge

A Chrome extension that brings Qwen Code into the browser as a thin client of a local qwen serve daemon — no Native Messaging host to install.

It does two things:

  • Side panel — frames the daemon's Web Shell (chat + tools), the same UI the daemon serves to the browser. The panel has no UI of its own.
  • Service worker — a CDP-tunnel pipe. It connects to the daemon's /acp WebSocket and bridges cdp_* frames into chrome.debugger, so the agent can drive the real browser (read page, screenshot, click, …) via chrome-devtools-mcp over the tunnel.

Build

npm run build        # -> dist/extension (static assets + bundled service worker)

Then load it: chrome://extensions → enable Developer mode → Load unpacked → pick dist/extension.

Run

The extension is a client; the daemon does the work and must be started separately (an extension cannot spawn a local process). Open the side panel and it will tell you exactly what to run — it generates the command with this extension's own id:

qwen serve --allow-origin chrome-extension://<this-extension-id>

--allow-origin chrome-extension://<id> is required: it lets the daemon's Web Shell be framed by the extension (the frame-ancestors CSP) and accepts the extension's requests. The side panel reads the id at runtime via chrome.runtime.id, so you never have to look it up.

Once the daemon is reachable and permits framing, the side panel swaps the welcome screen for the chat UI automatically.

Onboarding states

The side panel probes GET /health and GET /capabilities and shows one of:

State Meaning Shown
down no daemon reachable "Start qwen serve" + command
needs-allow-origin daemon up but --allow-origin not set "Allow this extension" + command
ready daemon up and framing permitted the Web Shell (chat)

Packaging for the Chrome Web Store

npm run package      # -> chrome-extension.zip (manifest at the zip root)

Upload the zip to the Chrome Web Store Developer Dashboard. Note that the debugger and <all_urls> permissions will draw manual review — justify them in the store listing.