diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 13b205444ac..7b8cc616c03 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2325,10 +2325,17 @@ jobs: # classifier makes above. The two-tree `git diff BASE HEAD` this replaced # is not the PR's diff: it reports everything the base gained since the # branch point as a change on the PR's side. #8132 added - # packages/desktop-shell, so every branch older than it saw 78 phantom - # desktop-shell paths, ran this job, and failed in a checkout of - # refs/pull/N/head that has no such directory. - - name: 'Detect desktop-shell changes' + # packages/desktop-shell (since renamed to packages/desktop), so every + # branch older than it saw 78 phantom desktop paths, ran this job, and + # failed in a checkout of refs/pull/N/head that has no such directory. + # The rename cuts the other way and is not caught by the guard below: a + # pre-rename head's files sit under packages/desktop-shell, which the + # path filter no longer matches, so the job skips and reports green + # having compiled nothing. Such a branch needs a rebase to be gated + # again. The Cargo.toml guard covers the remaining case -- a head the + # filter matched because ci.yml itself changed, with no crate at the + # new path. + - name: 'Detect desktop changes' id: 'filter' env: GH_TOKEN: '${{ github.token }}' @@ -2339,7 +2346,7 @@ jobs: # `previous_filename` too: renaming a file out of the crate changes # it, and only the old path says so. if files="$(gh api --paginate "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/files" --jq '.[] | .filename, (.previous_filename // empty)')"; then - if grep -Eq '^(packages/desktop-shell/|\.github/scripts/create-desktop-update-manifest\.mjs|\.github/workflows/ci\.yml|\.github/workflows/desktop-release\.yml)' <<<"${files}"; then + if grep -Eq '^(packages/desktop/|\.github/scripts/create-desktop-update-manifest\.mjs|\.github/workflows/ci\.yml|\.github/workflows/desktop-release\.yml)' <<<"${files}"; then changed=true else changed=false @@ -2351,12 +2358,12 @@ jobs: # Whatever the filter decided, a tree without the crate cannot have # regressed it, and cargo has no manifest to run against — the job # would report a missing working directory as a failure of the PR. - if [[ "${changed}" == "true" && ! -f packages/desktop-shell/src-tauri/Cargo.toml ]]; then - echo "::notice::packages/desktop-shell/src-tauri is absent from this head; nothing to compile." + if [[ "${changed}" == "true" && ! -f packages/desktop/src-tauri/Cargo.toml ]]; then + echo "::notice::packages/desktop/src-tauri is absent from this head; nothing to compile." changed=false fi echo "changed=${changed}" >> "${GITHUB_OUTPUT}" - echo "desktop-shell changed: ${changed}" + echo "desktop changed: ${changed}" # cargo test links the Tauri/wry webview, so the WebKit/GTK dev headers # must be present (mirrors the Linux build job in desktop-release.yml). @@ -2377,14 +2384,14 @@ jobs: - uses: 'Swatinem/rust-cache@42dc69e1aa15d09112580998cf2ef0119e2e91ae' # v2 if: "${{ steps.filter.outputs.changed == 'true' }}" with: - workspaces: 'packages/desktop-shell/src-tauri -> target' + workspaces: 'packages/desktop/src-tauri -> target' - name: 'Compile and test the desktop crate' if: "${{ steps.filter.outputs.changed == 'true' }}" - working-directory: 'packages/desktop-shell' + working-directory: 'packages/desktop' run: 'cargo test --manifest-path src-tauri/Cargo.toml' - name: 'Run desktop release tests' if: "${{ steps.filter.outputs.changed == 'true' && runner.os == 'Linux' }}" - working-directory: 'packages/desktop-shell' + working-directory: 'packages/desktop' run: 'node scripts/test-release.js' diff --git a/.github/workflows/desktop-release.yml b/.github/workflows/desktop-release.yml index 31a7e5f383d..e262eb1e863 100644 --- a/.github/workflows/desktop-release.yml +++ b/.github/workflows/desktop-release.yml @@ -277,7 +277,7 @@ jobs: with: node-version: '${{ env.NODE_VERSION }}' cache: 'npm' - cache-dependency-path: 'packages/desktop-shell/package-lock.json' + cache-dependency-path: 'packages/desktop/package-lock.json' - name: 'Configure bundled Node.js cache' id: 'node-cache-path' @@ -299,7 +299,7 @@ jobs: - uses: 'Swatinem/rust-cache@42dc69e1aa15d09112580998cf2ef0119e2e91ae' # v2 with: - workspaces: 'packages/desktop-shell/src-tauri -> target' + workspaces: 'packages/desktop/src-tauri -> target' - name: 'Install Linux dependencies' if: "runner.os == 'Linux'" @@ -320,11 +320,11 @@ jobs: fi - name: 'Install desktop dependencies' - working-directory: 'packages/desktop-shell' + working-directory: 'packages/desktop' run: 'npm ci' - name: 'Set desktop version' - working-directory: 'packages/desktop-shell' + working-directory: 'packages/desktop' run: 'node scripts/version.js "${{ needs.prepare.outputs.version }}"' - name: 'Require updater signing key for publishing' @@ -444,7 +444,7 @@ jobs: } - name: 'Prepare bundled runtime' - working-directory: 'packages/desktop-shell' + working-directory: 'packages/desktop' run: 'npm run build:runtime' - uses: 'actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830' # v4 @@ -454,16 +454,16 @@ jobs: key: 'desktop-node-v2-${{ matrix.rust_target }}-${{ env.NODE_VERSION }}-${{ inputs.dry_run }}' - name: 'Run desktop tests' - working-directory: 'packages/desktop-shell' + working-directory: 'packages/desktop' run: 'npm test' - name: 'Run desktop release tests' - working-directory: 'packages/desktop-shell' + working-directory: 'packages/desktop' run: 'npm run test:release' - name: 'Sign bundled vendor binaries (macOS)' if: "runner.os == 'macOS' && (github.event_name == 'release' || inputs.dry_run == false)" - working-directory: 'packages/desktop-shell' + working-directory: 'packages/desktop' shell: 'bash' env: APPLE_SIGNING_IDENTITY: '${{ env.APPLE_SIGNING_IDENTITY }}' @@ -516,15 +516,15 @@ jobs: - name: 'Refresh bundled runtime checksums after signing (macOS)' if: "runner.os == 'macOS' && (github.event_name == 'release' || inputs.dry_run == false)" - working-directory: 'packages/desktop-shell' + working-directory: 'packages/desktop' run: 'node scripts/prepare-runtime.js --refresh-checksums' - name: 'Verify bundled runtime' - working-directory: 'packages/desktop-shell' + working-directory: 'packages/desktop' run: 'npm run smoke:runtime' - name: 'Build desktop installers' - working-directory: 'packages/desktop-shell' + working-directory: 'packages/desktop' shell: 'bash' env: DRY_RUN: "${{ github.event_name == 'release' && 'false' || inputs.dry_run }}" @@ -546,7 +546,7 @@ jobs: shell: 'bash' run: | set -euo pipefail - app="$(find packages/desktop-shell/src-tauri/target/${{ matrix.rust_target }}/release/bundle/macos -maxdepth 1 -name '*.app' -print -quit)" + app="$(find packages/desktop/src-tauri/target/${{ matrix.rust_target }}/release/bundle/macos -maxdepth 1 -name '*.app' -print -quit)" codesign --verify --deep --strict --verbose=2 "$app" spctl --assess --type execute --verbose=2 "$app" entitlements="$(mktemp)" @@ -560,7 +560,7 @@ jobs: env: WINDOWS_CONFIG: '${{ env.WINDOWS_CONFIG }}' run: | - $installer = Get-ChildItem packages/desktop-shell/src-tauri/target/${{ matrix.rust_target }}/release/bundle/nsis/*.exe | Select-Object -First 1 + $installer = Get-ChildItem packages/desktop/src-tauri/target/${{ matrix.rust_target }}/release/bundle/nsis/*.exe | Select-Object -First 1 $signature = Get-AuthenticodeSignature $installer.FullName if ($signature.Status -eq 'Valid') { Write-Output "Windows installer is Authenticode-signed." @@ -577,18 +577,18 @@ jobs: LEGACY_ARCH: '${{ matrix.legacy_arch }}' run: | set -euo pipefail - app="$(find packages/desktop-shell/src-tauri/target/${{ matrix.rust_target }}/release/bundle/macos -maxdepth 1 -name 'Qwen Code Desktop.app' -print -quit)" + app="$(find packages/desktop/src-tauri/target/${{ matrix.rust_target }}/release/bundle/macos -maxdepth 1 -name 'Qwen Code Desktop.app' -print -quit)" if [ -z "$app" ]; then echo '::error::The legacy-named macOS app bundle was not produced.' exit 1 fi - destination="packages/desktop-shell/src-tauri/target/${{ matrix.rust_target }}/release/bundle/electron-bridge" + destination="packages/desktop/src-tauri/target/${{ matrix.rust_target }}/release/bundle/electron-bridge" mkdir -p "$destination" ditto -c -k --sequesterRsrc --keepParent "$app" "$destination/Qwen-Code-Desktop-$LEGACY_ARCH.zip" - name: 'Smoke packaged application' if: "runner.os == 'macOS'" - working-directory: 'packages/desktop-shell' + working-directory: 'packages/desktop' shell: 'bash' run: | set -euo pipefail @@ -599,7 +599,7 @@ jobs: - name: 'Smoke packaged application' if: "runner.os == 'Windows'" - working-directory: 'packages/desktop-shell' + working-directory: 'packages/desktop' shell: 'pwsh' run: | $executable = Get-ChildItem src-tauri/target/${{ matrix.rust_target }}/release/qwen-code-desktop.exe | Select-Object -First 1 @@ -607,7 +607,7 @@ jobs: - name: 'Smoke packaged application' if: "runner.os == 'Linux'" - working-directory: 'packages/desktop-shell' + working-directory: 'packages/desktop' shell: 'bash' run: | set -euo pipefail @@ -621,7 +621,7 @@ jobs: set -euo pipefail destination="$RUNNER_TEMP/desktop-artifacts" mkdir -p "$destination" - bundle_root="packages/desktop-shell/src-tauri/target/${{ matrix.rust_target }}/release/bundle" + bundle_root="packages/desktop/src-tauri/target/${{ matrix.rust_target }}/release/bundle" while IFS= read -r -d '' artifact; do name="$(basename "$artifact")" if [ "$RUNNER_OS" = 'macOS' ]; then diff --git a/.github/workflows/pnpm-worktree-smoke.yml b/.github/workflows/pnpm-worktree-smoke.yml index d654a585988..2128b9f9d48 100644 --- a/.github/workflows/pnpm-worktree-smoke.yml +++ b/.github/workflows/pnpm-worktree-smoke.yml @@ -11,7 +11,7 @@ on: - '.pnpmfile.mjs' - 'package.json' - 'packages/*/package.json' - - '!packages/desktop-shell/package.json' + - '!packages/desktop/package.json' - '!packages/live-host/package.json' - '!packages/mobile-shell/package.json' - 'packages/channels/*/package.json' @@ -35,7 +35,7 @@ on: - '.pnpmfile.mjs' - 'package.json' - 'packages/*/package.json' - - '!packages/desktop-shell/package.json' + - '!packages/desktop/package.json' - '!packages/live-host/package.json' - '!packages/mobile-shell/package.json' - 'packages/channels/*/package.json' diff --git a/.prettierignore b/.prettierignore index 5cb9d8d9ed6..5cd9cad0590 100644 --- a/.prettierignore +++ b/.prettierignore @@ -33,7 +33,7 @@ packages/cua-driver/ # house style would corrupt the very data the codemod tests assert on. scripts/codemod/fixtures/ packages/live-host/ -packages/desktop-shell/runtime/ +packages/desktop/runtime/ packages/core/src/skills/bundled/browser-use/runtime/ # Vendored from mobile-next/mobile-mcp and carries its own eslint config, so # reformatting it to house style only widens the diff against upstream on the diff --git a/.qwen/skills/find-simplifications/SKILL.md b/.qwen/skills/find-simplifications/SKILL.md index 38033aab5d6..7e271514f2f 100644 --- a/.qwen/skills/find-simplifications/SKILL.md +++ b/.qwen/skills/find-simplifications/SKILL.md @@ -97,7 +97,7 @@ Landable. | `.github/` — workflows, actions, CODEOWNERS | **Report-only** — consumed GitHub-side: triggers, required checks, cross-repo `uses:` | | `package.json` dependencies | **Report-only** — bundlers and postinstall hide consumers | | Comments, JSDoc, commented-out code | **Out of scope** — `AGENTS.md` says do not delete existing comments as cleanup | -| `packages/desktop-shell`, `packages/cua-driver`, `packages/mobile-mcp`, `**/generated/**`, `**/*.test.ts(x)`, `**/*.spec.ts(x)`, `**/__snapshots__/**` | Never a target; always searched as consumers — vitest discovers tests by filename glob, nothing imports them, so an import-based orphan detector matches every live test vacuously | +| `packages/desktop`, `packages/cua-driver`, `packages/mobile-mcp`, `**/generated/**`, `**/*.test.ts(x)`, `**/*.spec.ts(x)`, `**/__snapshots__/**` | Never a target; always searched as consumers — vitest discovers tests by filename glob, nothing imports them, so an import-based orphan detector matches every live test vacuously | A settings key with zero read sites is still not cleanup. The key is likely documented under `docs/users/` and completed from the generated schema; diff --git a/.qwen/skills/find-simplifications/references/land.md b/.qwen/skills/find-simplifications/references/land.md index 1d2da4591a7..adc9c1a1c60 100644 --- a/.qwen/skills/find-simplifications/references/land.md +++ b/.qwen/skills/find-simplifications/references/land.md @@ -121,7 +121,7 @@ Then the gate matching what you touched: | user-facing strings, `i18n/locales/*` | `npm run check-i18n` | | `package.json` or dependencies | `npm run check:lockfile` | | `scripts/` | `npm run test:scripts` — plain `npm test` does not cover it | -| `packages/desktop-shell` | `npm run check:desktop-isolation` | +| `packages/desktop` | `npm run check:desktop-isolation` | | bundling, the serve fast path, `esbuild.config.js` | `npm run check:serve-fast-path-bundle` | | CLI behavior | the matching `npm run test:integration:*` script | diff --git a/.qwen/skills/find-simplifications/references/survey.md b/.qwen/skills/find-simplifications/references/survey.md index 2e0102a511d..cb9f3f20229 100644 --- a/.qwen/skills/find-simplifications/references/survey.md +++ b/.qwen/skills/find-simplifications/references/survey.md @@ -231,7 +231,7 @@ beat the doc. `--no-ignore`: it surfaces `.qwen/tmp/` scratch copies of this repo, the same phantom-consumer class as `.claude/worktrees/` — **never name that one**, every hit there is a phantom consumer. -- `packages/desktop-shell` is negated out of the root `workspaces` list but +- `packages/desktop` is negated out of the root `workspaces` list but is still shipped code; `packages/mobile-mcp` and `packages/cua-driver` are vendored. None is a target; all three are consumers. - Then classify every hit: production / test / snapshot / docs / lint diff --git a/.qwen/skills/repo-hygiene/references/scan.md b/.qwen/skills/repo-hygiene/references/scan.md index 56815b9719d..2d6ae47aee2 100644 --- a/.qwen/skills/repo-hygiene/references/scan.md +++ b/.qwen/skills/repo-hygiene/references/scan.md @@ -108,9 +108,9 @@ hygiene findings. - Correct: multi-SDK behavior is consistent, protocol fields match the TS SDK, bridge error mapping preserves the original error class. -- **ui-apps** — the two UI apps (`packages/desktop-shell/` and +- **ui-apps** — the two UI apps (`packages/desktop/` and `packages/web-shell/`). - - `desktop-shell`: a thin Tauri shell around Web Shell (window + - `desktop`: a thin Tauri shell around Web Shell (window management, process lifecycle, signing, updates). - `web-shell`: a client React app (`client/`), a Vite build, and a daemon proxy; ships as an embeddable component. diff --git a/docs/design/telemetry-runtime-client-attribution-design.md b/docs/design/telemetry-runtime-client-attribution-design.md index 72256d55244..0a78658e143 100644 --- a/docs/design/telemetry-runtime-client-attribution-design.md +++ b/docs/design/telemetry-runtime-client-attribution-design.md @@ -12,7 +12,7 @@ - TS/Python/Java SDK 的主入口(`query()`)直接 spawn CLI(stream-json 模式),自带 `--channel=SDK` - `--acp` 未显式指定 channel 时回退为 `ACP`(`packages/cli/src/config/config.ts` 的 ACP fallback) -但 `qwen serve`(daemon)的 spawn 工厂启动的是不带 channel 的 `qwen --acp` 子进程(`packages/acp-bridge/src/spawnChannel.ts`),因此**经 daemon 承载的会话——SDK 的 daemon 客户端入口(如 TS SDK 的 `DaemonClient`/`DaemonSessionClient`)、Web Shell、Tauri 桌面 shell——全部上报为 `ACP`**,无法区分。Tauri shell 启动 daemon 时设置了 `QWEN_CODE_DESKTOP=1`(`packages/desktop-shell/src-tauri/src/runtime.rs`),但 telemetry 从未读取该变量。 +但 `qwen serve`(daemon)的 spawn 工厂启动的是不带 channel 的 `qwen --acp` 子进程(`packages/acp-bridge/src/spawnChannel.ts`),因此**经 daemon 承载的会话——SDK 的 daemon 客户端入口(如 TS SDK 的 `DaemonClient`/`DaemonSessionClient`)、Web Shell、Tauri 桌面 shell——全部上报为 `ACP`**,无法区分。Tauri shell 启动 daemon 时设置了 `QWEN_CODE_DESKTOP=1`(`packages/desktop/src-tauri/src/runtime.rs`),但 telemetry 从未读取该变量。 `app.channel` 是来自 `~/.qwen/source.json` 的**安装来源**,与入口归因是不同概念,不应被重载。 diff --git a/docs/design/web-shell-user-message-links.md b/docs/design/web-shell-user-message-links.md index eb79b35e1d5..eaee62a1dab 100644 --- a/docs/design/web-shell-user-message-links.md +++ b/docs/design/web-shell-user-message-links.md @@ -61,7 +61,7 @@ the composer is not clickable in the transcript, forcing copy-paste to open it. | Explicit `https?://` scheme only; no bare `www.`/domains/emails | Minimum surface, near-zero false positives; pasted URLs almost always carry a scheme. | | ASCII allowlist, not a denylist of prose characters | A denylist is wrong in both directions: unlisted scripts (emoji, Thai) get absorbed into the href, and excluded ASCII quotes truncate URLs containing them (`/wiki/L'Aquila`). The allowlist fails closed to plain text; its boundary behavior is inspired by remark-gfm (which assistant messages use) but not identical to it — known deltas: no left boundary is required before an ASCII alphanumeric (`xhttps://…` still links), a trailing HTML character reference is not resolved (`…/page&` keeps `&`), the tokenizer is stricter than GFM on a trailing backtick and backslash (they terminate the match), and a balanced `a[0]` keeps its `]`, and raw `[`, `]`, `{`, `}` stay verbatim in the href where the assistant pipeline's `normalizeUri` percent-encodes them. | | Raw IRIs are not linkified whole | `https://zh.wikipedia.org/wiki/中文` linkifies only its ASCII prefix. Accepted tradeoff: absorbing CJK would swallow the following prose (`详情见https://example.com即可使用`); percent-encoded URLs are unaffected. Measured wrong-link shapes beyond this: markdown link syntax pasted as plain text (`[https://a.example](https://b.example)`) yields an unusable href, and a bare `%` is percent-encoded on the assistant path (`normalizeUri` in mdast-util-to-hast) — `LinkifiedText` matches that byte-for-byte (`%` not followed by two ASCII alphanumerics → `%25`, e.g. `/100%` → `/100%25`, while `50%off` is kept) while keeping the visible text verbatim. | -| Reuse `isSafeHref` + `useExternalLinkOpener` | Same safety check and desktop-shell routing as assistant-message links; no second policy to maintain. | +| Reuse `isSafeHref` + `useExternalLinkOpener` | Same safety check and desktop-host routing as assistant-message links; no second policy to maintain. | | Separate tiny util + component instead of routing user text through `Markdown` | User text is intentionally not markdown (composer tags, `white-space: pre-wrap` layout); a regex tokenizer avoids changing that contract. | | No linkification inside host-provided `renderUserMessageContent` output | That output belongs to the embedding host; overriding it would break the customization contract. | | Scheduled-task-run prompt is linkified too | Only the header lines are machine-generated; the prompt body is user-authored task instructions, so it gets the same treatment. | @@ -93,7 +93,7 @@ the composer is not clickable in the transcript, forcing copy-paste to open it. (every render path: default, annotated segments, host-parser parts, parse-failure fallback, scheduled-task prompt): URL renders as an anchor with `target="_blank"` / `rel="noopener noreferrer"`; surrounding text and - composer-tag chips unchanged; desktop-shell clicks route through + composer-tag chips unchanged; desktop-host clicks route through `useExternalLinkOpener`. - Playwright `@smoke` spec replaying a user message with a URL through the mock daemon. diff --git a/docs/design/web-shell/web-shell-browser-notification-details.md b/docs/design/web-shell/web-shell-browser-notification-details.md index d172ced712f..b15f8580075 100644 --- a/docs/design/web-shell/web-shell-browser-notification-details.md +++ b/docs/design/web-shell/web-shell-browser-notification-details.md @@ -14,7 +14,7 @@ The body also includes this turn's user prompt, with localized Prompt and Reply The session-title component retains at most 60 Unicode code points (the application prefix is separate), prompt excerpts at most 80, and reply excerpts at most 120, ending in an ellipsis when truncated. Excerpt cleanup reads at most the first 4096 UTF-16 code units, removes common Markdown formatting and simple inline link destinations, strips control and bidi-formatting characters, and collapses whitespace. Angle-bracket text, including HTML and JSX, is preserved literally because a plain-text notification must not guess whether it is code or markup. Fenced code keeps its content. Truncation avoids splitting Unicode code points; grapheme-cluster truncation is out of scope. No extra configuration or model call is introduced. Browser/OS layout may truncate the combined body further. -Notifications include a package-local copy of the project's existing 128×128 PNG mark from `packages/desktop-shell/src-tauri/icons/128x128.png`, with Vite resolving its URL. The published library inlines the PNG as a data URL so it needs no separately published asset. Business text does not append IP addresses, ports, or navigation URLs. Chrome's site attribution and its own macOS logo are controlled by the browser and OS and cannot be replaced by the business icon. Content may appear in the notification center or on the lock screen; the setting description explains that titles, prompts, and reply excerpts are shown. Shared storage continues to record only deduplication hashes, never bodies, titles, or navigation targets. +Notifications include a package-local copy of the project's existing 128×128 PNG mark from `packages/desktop/src-tauri/icons/128x128.png`, with Vite resolving its URL. The published library inlines the PNG as a data URL so it needs no separately published asset. Business text does not append IP addresses, ports, or navigation URLs. Chrome's site attribution and its own macOS logo are controlled by the browser and OS and cannot be replaced by the business icon. Content may appear in the notification center or on the lock screen; the setting description explains that titles, prompts, and reply excerpts are shown. Shared storage continues to record only deduplication hashes, never bodies, titles, or navigation targets. ## Clicking to open a session diff --git a/docs/design/web-shell/web-shell-browser-notification-details.zh-CN.md b/docs/design/web-shell/web-shell-browser-notification-details.zh-CN.md index 19edfec573c..7057a8224c6 100644 --- a/docs/design/web-shell/web-shell-browser-notification-details.zh-CN.md +++ b/docs/design/web-shell/web-shell-browser-notification-details.zh-CN.md @@ -14,7 +14,7 @@ 标题中的会话部分最多保留 60 个 Unicode 码点(应用名称前缀另计),提问摘录最多 80 个,回复摘录最多 120 个,超长时以省略号结尾。摘录清理最多读取前 4096 个 UTF-16 代码单元,移除常见 Markdown 格式、简单内联链接地址、控制字符和双向格式字符,并合并空白。尖括号内容(含 HTML 和 JSX)按字面保留,纯文本通知不猜测它是代码还是标记;代码围栏保留内容。截断不拆开 Unicode 码点,按字素簇截断不在范围内。不增加配置或模型调用。浏览器或系统布局可能进一步截短整体正文。 -通知携带项目现有 `packages/desktop-shell/src-tauri/icons/128x128.png` 标志的本包副本,由 Vite 解析图片 URL。发布的库将 PNG 内联为 data URL,无需另行发布图片资源。标题与正文不额外拼接 IP、端口或跳转地址;Chrome 自带的站点来源和 macOS 上的 Chrome 标志由浏览器及系统控制,业务图标不能替换它们。通知内容可能显示在通知中心或锁屏上,设置说明告知会展示标题、提问和回复摘录。共享存储仍仅记录去重哈希,不保存正文、标题或导航目标。 +通知携带项目现有 `packages/desktop/src-tauri/icons/128x128.png` 标志的本包副本,由 Vite 解析图片 URL。发布的库将 PNG 内联为 data URL,无需另行发布图片资源。标题与正文不额外拼接 IP、端口或跳转地址;Chrome 自带的站点来源和 macOS 上的 Chrome 标志由浏览器及系统控制,业务图标不能替换它们。通知内容可能显示在通知中心或锁屏上,设置说明告知会展示标题、提问和回复摘录。共享存储仍仅记录去重哈希,不保存正文、标题或导航目标。 ## 点击打开会话 diff --git a/docs/developers/architecture.md b/docs/developers/architecture.md index 7c6a2c88786..a5b6ca335f7 100644 --- a/docs/developers/architecture.md +++ b/docs/developers/architecture.md @@ -77,22 +77,22 @@ an HTTP daemon. See the ## Repository layout -| Path | Responsibility | -| ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `packages/cli` | The `qwen` executable, argument parsing, configuration assembly, Ink TUI, headless output, ACP entry point, `qwen serve`, and command-specific adapters. | -| `packages/core` | UI-independent agent orchestration, model-provider integration, prompt and context construction, tool registration and execution, permissions, sessions, memory, telemetry, and shared services. | -| `packages/acp-bridge` | ACP channel lifecycle, session multiplexing, event delivery, permission mediation, process spawning, and the filesystem seam shared by daemon and adapter hosts. | -| `packages/sdk-typescript` | Programmatic process execution through `query()` plus HTTP/SSE clients and transcript projection for `qwen serve`. | -| `packages/web-shell` | The browser UI and daemon React adapter built on the TypeScript SDK. | -| `packages/web-templates` | Web templates packaged as embeddable JavaScript and CSS strings. | -| `packages/audio-capture` | Native microphone capture for voice input. | -| `packages/channels` | The shared channel runtime and platform adapters for messaging services. | -| `packages/desktop-shell`, `packages/vscode-ide-companion`, `packages/chrome-extension`, `packages/zed-extension` | Product and editor surfaces that adapt Qwen Code to their host environments. | -| `packages/sdk-java`, `packages/sdk-python` | Language-specific programmatic clients. | -| `packages/cua-driver`, `packages/mobile-mcp` | Computer-use and mobile-device integrations exposed through MCP-compatible boundaries. | -| `integration-tests` | End-to-end coverage for CLI, interactive, SDK, sandbox, hook, and terminal behavior. | -| `docs` and `docs-site` | User, developer, protocol, and design documentation plus the documentation site. | -| `scripts` | Build, packaging, release, validation, and repository-maintenance automation. | +| Path | Responsibility | +| ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `packages/cli` | The `qwen` executable, argument parsing, configuration assembly, Ink TUI, headless output, ACP entry point, `qwen serve`, and command-specific adapters. | +| `packages/core` | UI-independent agent orchestration, model-provider integration, prompt and context construction, tool registration and execution, permissions, sessions, memory, telemetry, and shared services. | +| `packages/acp-bridge` | ACP channel lifecycle, session multiplexing, event delivery, permission mediation, process spawning, and the filesystem seam shared by daemon and adapter hosts. | +| `packages/sdk-typescript` | Programmatic process execution through `query()` plus HTTP/SSE clients and transcript projection for `qwen serve`. | +| `packages/web-shell` | The browser UI and daemon React adapter built on the TypeScript SDK. | +| `packages/web-templates` | Web templates packaged as embeddable JavaScript and CSS strings. | +| `packages/audio-capture` | Native microphone capture for voice input. | +| `packages/channels` | The shared channel runtime and platform adapters for messaging services. | +| `packages/desktop`, `packages/vscode-ide-companion`, `packages/chrome-extension`, `packages/zed-extension` | Product and editor surfaces that adapt Qwen Code to their host environments. | +| `packages/sdk-java`, `packages/sdk-python` | Language-specific programmatic clients. | +| `packages/cua-driver`, `packages/mobile-mcp` | Computer-use and mobile-device integrations exposed through MCP-compatible boundaries. | +| `integration-tests` | End-to-end coverage for CLI, interactive, SDK, sandbox, hook, and terminal behavior. | +| `docs` and `docs-site` | User, developer, protocol, and design documentation plus the documentation site. | +| `scripts` | Build, packaging, release, validation, and repository-maintenance automation. | Most code lives in npm workspaces under `packages/`. A package should depend on another package through its declared public exports rather than through a diff --git a/eslint.config.js b/eslint.config.js index 9d332257501..0465aa26e7e 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -75,9 +75,9 @@ export default tseslint.config( 'docs-site/out/**', '.qwen/**', 'scripts/codemod/fixtures/**', // codemod test data; intentionally non-idiomatic ink input/output - 'packages/desktop-shell/runtime/**', + 'packages/desktop/runtime/**', 'packages/core/src/skills/bundled/browser-use/runtime/**', - 'packages/desktop-shell/src-tauri/target/**', + 'packages/desktop/src-tauri/target/**', 'packages/live-host/**', // standalone Electron app with its own Node test conventions 'packages/cua-driver/**', // vendored trycua/cua driver (Rust + scripts); not qwen-code TS 'packages/mobile-mcp/**', // vendored mobile-next/mobile-mcp; has own eslint config @@ -512,8 +512,8 @@ export default tseslint.config( 'docs/**/*.mjs', // Plan C CDP-tunnel acceptance harness (issue #5626) runs with `node`. 'packages/cli/src/serve/cdp-tunnel/acceptance/**/*.mjs', - // Desktop-shell skill helper scripts also run with `node`. - 'packages/desktop-shell/.agents/skills/**/scripts/**/*.mjs', + // Desktop skill helper scripts also run with `node`. + 'packages/desktop/.agents/skills/**/scripts/**/*.mjs', ], languageOptions: { globals: { @@ -558,7 +558,7 @@ export default tseslint.config( }, }, { - files: ['packages/desktop-shell/bootstrap/**/*.js'], + files: ['packages/desktop/bootstrap/**/*.js'], languageOptions: { globals: { ...globals.browser, diff --git a/package.json b/package.json index 36ca5bebf61..c276517c448 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "packages/channels/plugin-example", "integrations/external-context", "integrations/external-context-mem0", - "!packages/desktop-shell", + "!packages/desktop", "!packages/live-host", "!packages/mobile-shell" ], diff --git a/packages/cli/src/commands/review/build-test.test.ts b/packages/cli/src/commands/review/build-test.test.ts index 27cfadbf4b1..5e539cb4674 100644 --- a/packages/cli/src/commands/review/build-test.test.ts +++ b/packages/cli/src/commands/review/build-test.test.ts @@ -2111,7 +2111,7 @@ describe('runBuildTest', () => { }); it('discloses a diff inside a negated member — softly, never as an incomplete scope', () => { - // packages/desktop-shell is a separate toolchain (its own lockfile); a diff + // packages/desktop is a separate toolchain (its own lockfile); a diff // inside it cannot fail any npm workspace's suite, so "nothing to run" // stays the answer — disclosed softly (its own suite did not run), never // as an incomplete scope. @@ -2119,7 +2119,7 @@ describe('runBuildTest', () => { join(root, 'package.json'), JSON.stringify({ name: 'r', - workspaces: ['packages/*', '!packages/desktop-shell'], + workspaces: ['packages/*', '!packages/desktop'], scripts: { test: 'exit 0' }, }), ); @@ -2127,11 +2127,11 @@ describe('runBuildTest', () => { name: '@x/core', scripts: { build: 'exit 0', test: 'exit 0' }, }); - pkg('packages/desktop-shell', { + pkg('packages/desktop', { name: '@x/desktop', scripts: { build: 'exit 0', test: 'exit 0' }, }); - writePlan(['packages/desktop-shell/src/main.rs']); + writePlan(['packages/desktop/src/main.rs']); const rep = runBuildTest({ plan: planPath, @@ -2143,9 +2143,7 @@ describe('runBuildTest', () => { expect(rep.build).toEqual([]); expect(rep.test).toEqual([]); expect(rep.testScope?.workspaces).toEqual([]); - expect(rep.testScope?.caveat).toContain( - 'packages/desktop-shell/src/main.rs', - ); + expect(rep.testScope?.caveat).toContain('packages/desktop/src/main.rs'); expect(rep.testScope?.caveat).toContain('were not run'); expect(rep.note).toContain('were not run'); }); @@ -2392,7 +2390,7 @@ describe('runBuildTest', () => { it('excludes a negated workspace from the build set (integration)', () => { // `!packages/excluded` must keep that package out — building it could fail on a - // repo where it is a separate toolchain (e.g. packages/desktop-shell, its own lockfile). + // repo where it is a separate toolchain (e.g. packages/desktop, its own lockfile). writeFileSync( join(root, 'package.json'), JSON.stringify({ diff --git a/packages/cli/src/commands/review/lib/diff-plan.test.ts b/packages/cli/src/commands/review/lib/diff-plan.test.ts index ba55cef8f99..e55c3ab8b1d 100644 --- a/packages/cli/src/commands/review/lib/diff-plan.test.ts +++ b/packages/cli/src/commands/review/lib/diff-plan.test.ts @@ -56,7 +56,7 @@ describe('classifyPath', () => { it('recognises generated and vendored files', () => { const paths = [ 'package-lock.json', - 'packages/desktop-shell/bun.lock', + 'packages/desktop/bun.lock', 'packages/vscode-ide-companion/NOTICES.txt', 'dist/bundle.min.js', 'vendor/lib.go', diff --git a/packages/cli/src/commands/review/lib/workspace-scope.test.ts b/packages/cli/src/commands/review/lib/workspace-scope.test.ts index f0c06a17ad0..3db9c278f36 100644 --- a/packages/cli/src/commands/review/lib/workspace-scope.test.ts +++ b/packages/cli/src/commands/review/lib/workspace-scope.test.ts @@ -136,18 +136,18 @@ describe('resolveTestScope', () => { }); it('discloses — softly, not as incompleteness — a member a negation excludes', () => { - // !packages/desktop-shell is a separate toolchain with its own lockfile; a diff + // !packages/desktop is a separate toolchain with its own lockfile; a diff // inside it cannot fail any included workspace's suite, so it earns no // incomplete-scope caveat. But its own suite was not run either, and // "nothing is silent" covers that: a softer line says what did not run. const scope = resolveTestScope({ - changed: ['packages/desktop-shell/src/main.rs'], - globs: ['packages/*', '!packages/desktop-shell'], + changed: ['packages/desktop/src/main.rs'], + globs: ['packages/*', '!packages/desktop'], packages: PKGS, skipped: [], }); expect(scope.workspaces).toEqual([]); - expect(scope.caveat).toContain('packages/desktop-shell/src/main.rs'); + expect(scope.caveat).toContain('packages/desktop/src/main.rs'); expect(scope.caveat).toContain('were not run'); expect(scope.caveat).not.toContain('outside every workspace'); }); diff --git a/packages/cli/src/commands/review/lib/workspace-scope.ts b/packages/cli/src/commands/review/lib/workspace-scope.ts index 40bc5e8e651..d277ef88536 100644 --- a/packages/cli/src/commands/review/lib/workspace-scope.ts +++ b/packages/cli/src/commands/review/lib/workspace-scope.ts @@ -196,7 +196,7 @@ export function resolveTestScope(input: { const unmapped = affected.filter( (d) => d !== '.' && !scriptsOf.has(d) && !skipped.includes(d), ); - // Files a negation excludes (!packages/desktop-shell — a separate toolchain with + // Files a negation excludes (!packages/desktop — a separate toolchain with // its own lockfile) cannot affect any included workspace's tests, so they // earn no incomplete-scope caveat — but their own suites were not run // either, and "nothing is silent" covers that too: disclose it as the diff --git a/packages/cli/src/commands/review/lib/workspaces.test.ts b/packages/cli/src/commands/review/lib/workspaces.test.ts index f60624b834e..1f7f3323e6d 100644 --- a/packages/cli/src/commands/review/lib/workspaces.test.ts +++ b/packages/cli/src/commands/review/lib/workspaces.test.ts @@ -35,7 +35,7 @@ const GLOBS = [ 'packages/channels/base', 'packages/channels/telegram', 'packages/channels/qqbot', - '!packages/desktop-shell', + '!packages/desktop', ]; describe('workspaceDirFor', () => { @@ -55,47 +55,36 @@ describe('workspaceDirFor', () => { }); it('honours a negation, so a separate toolchain is not a member', () => { - // packages/desktop-shell has its own lockfile and is not part of this npm workspace. + // packages/desktop has its own lockfile and is not part of this npm workspace. // Building it from the root fails. expect( - workspaceDirFor( - 'packages/desktop-shell/apps/electron/src/main.ts', - GLOBS, - ), + workspaceDirFor('packages/desktop/apps/electron/src/main.ts', GLOBS), ).toBeNull(); - expect( - isWorkspaceMember('packages/desktop-shell/src/a.test.ts', GLOBS), - ).toBe(false); + expect(isWorkspaceMember('packages/desktop/src/a.test.ts', GLOBS)).toBe( + false, + ); }); it('re-includes what a negation excluded when a later glob matches again', () => { // npm's own rule: last match wins, whichever direction it points. - const globs = [ - 'packages/*', - '!packages/desktop-shell', - 'packages/desktop-shell', - ]; - expect(workspaceDirFor('packages/desktop-shell/src/a.ts', globs)).toBe( - 'packages/desktop-shell', + const globs = ['packages/*', '!packages/desktop', 'packages/desktop']; + expect(workspaceDirFor('packages/desktop/src/a.ts', globs)).toBe( + 'packages/desktop', ); }); it('falls back to the surviving OUTER member when a negation excludes a nested one', () => { - // npm keeps packages/desktop-shell in the graph — only src is excluded — and + // npm keeps packages/desktop in the graph — only src is excluded — and // desktop's test runner collects src/**, so the file is felt by the outer // member's suite. Declaring it felt by NOTHING would certify "a complete // answer" over a suite that can fail. - const globs = [ - 'packages/*', - 'packages/desktop-shell/*', - '!packages/desktop-shell/src', - ]; - expect(workspaceDirFor('packages/desktop-shell/src/x.test.ts', globs)).toBe( - 'packages/desktop-shell', + const globs = ['packages/*', 'packages/desktop/*', '!packages/desktop/src']; + expect(workspaceDirFor('packages/desktop/src/x.test.ts', globs)).toBe( + 'packages/desktop', + ); + expect(isNegationExcluded('packages/desktop/src/x.test.ts', globs)).toBe( + false, ); - expect( - isNegationExcluded('packages/desktop-shell/src/x.test.ts', globs), - ).toBe(false); }); it('treats a ./-prefixed glob like its bare form', () => { @@ -212,9 +201,9 @@ describe('readRootPackage', () => { describe('isNegationExcluded', () => { it('is true when a positive glob claims the file but a negation excludes it', () => { - expect( - isNegationExcluded('packages/desktop-shell/src/main.rs', GLOBS), - ).toBe(true); + expect(isNegationExcluded('packages/desktop/src/main.rs', GLOBS)).toBe( + true, + ); }); it('is false for a file inside an included workspace', () => { @@ -227,27 +216,21 @@ describe('isNegationExcluded', () => { }); it('is false when a later glob re-includes what the negation excluded', () => { - const globs = [ - 'packages/*', - '!packages/desktop-shell', - 'packages/desktop-shell', - ]; - expect(isNegationExcluded('packages/desktop-shell/src/a.ts', globs)).toBe( - false, - ); + const globs = ['packages/*', '!packages/desktop', 'packages/desktop']; + expect(isNegationExcluded('packages/desktop/src/a.ts', globs)).toBe(false); }); - it('keeps a member owned under a partial negation (`!packages/desktop-shell/*`)', () => { - // npm keeps packages/desktop-shell itself a member — a glob with a subpath + it('keeps a member owned under a partial negation (`!packages/desktop/*`)', () => { + // npm keeps packages/desktop itself a member — a glob with a subpath // cannot match the dir itself — so a file under it is still owned and its // suite can feel a change there; it is NOT negation-excluded. - const globs = ['packages/*', '!packages/desktop-shell/*']; - expect(workspaceDirFor('packages/desktop-shell/src/main.ts', globs)).toBe( - 'packages/desktop-shell', + const globs = ['packages/*', '!packages/desktop/*']; + expect(workspaceDirFor('packages/desktop/src/main.ts', globs)).toBe( + 'packages/desktop', + ); + expect(isNegationExcluded('packages/desktop/src/main.ts', globs)).toBe( + false, ); - expect( - isNegationExcluded('packages/desktop-shell/src/main.ts', globs), - ).toBe(false); }); }); @@ -351,9 +334,9 @@ describe('readWorkspacePackages', () => { }); it('ignores a broken manifest in a NEGATED dir — not a workspace, not our graph', () => { - setup(['packages/*', '!packages/desktop-shell']); + setup(['packages/*', '!packages/desktop']); write('packages/good', { name: '@x/good' }); - write('packages/desktop-shell', '{ not json'); + write('packages/desktop', '{ not json'); const { packages, skipped } = readWorkspacePackages(root); expect(packages.map((p) => p.dir)).toEqual(['packages/good']); expect(skipped).toEqual([]); @@ -459,7 +442,7 @@ describe('hasUnmodeledWorkspaceGlob', () => { it('is false for the shapes the walker models — literals and a trailing /*', () => { expect(hasUnmodeledWorkspaceGlob(GLOBS)).toBe(false); expect(hasUnmodeledWorkspaceGlob(['packages/*', 'apps/web'])).toBe(false); - expect(hasUnmodeledWorkspaceGlob(['!packages/desktop-shell'])).toBe(false); + expect(hasUnmodeledWorkspaceGlob(['!packages/desktop'])).toBe(false); }); it('is true for `**`, an inner `*`, or a `foo-*` prefix the walker cannot model', () => { diff --git a/packages/cli/src/commands/review/lib/workspaces.ts b/packages/cli/src/commands/review/lib/workspaces.ts index 49b2c9224bf..ec1eec00173 100644 --- a/packages/cli/src/commands/review/lib/workspaces.ts +++ b/packages/cli/src/commands/review/lib/workspaces.ts @@ -79,10 +79,10 @@ export function workspaceDirFor( const norm = filePath.replace(/^\.\//, ''); let owner: string | null = null; // The owners that came before the current one — where a negation falls back - // TO. When a negation excludes a NESTED member (`packages/desktop-shell/*` claimed - // `packages/desktop-shell/src`, then `!packages/desktop-shell/src` excluded it), the + // TO. When a negation excludes a NESTED member (`packages/desktop/*` claimed + // `packages/desktop/src`, then `!packages/desktop/src` excluded it), the // still-included outer member keeps owning the file: npm keeps - // `packages/desktop-shell` in the graph, and its test runner collects `src/**`. + // `packages/desktop` in the graph, and its test runner collects `src/**`. // Falling back to the previous owner is what lets that suite feel the // change instead of the file being declared felt by nothing. const previous: Array = []; @@ -111,15 +111,15 @@ export function workspaceDirFor( } } else if (dir === owner) { // A negation only excludes the file when it excludes the member that - // currently owns it. `!packages/desktop-shell/*` matches a deeper pseudo-dir + // currently owns it. `!packages/desktop/*` matches a deeper pseudo-dir // than `packages/*` does, and npm keeps the member itself in the graph // (a glob with a subpath cannot match a dir with no subpath), so the // member's suite can still feel a change there. When the negation DOES // exclude the owner, ownership falls back to the previous, outer member // — only a negation of THAT one leaves the file owned by nothing. (The // pop does not re-check the popped owner against negations already - // walked past: a contrived ordering like `!packages/desktop-shell` BEFORE - // `!packages/desktop-shell/src` can resurrect an excluded owner. Realistic + // walked past: a contrived ordering like `!packages/desktop` BEFORE + // `!packages/desktop/src` can resurrect an excluded owner. Realistic // orderings — the outer negation written last — are exact.) owner = previous.pop() ?? null; } @@ -132,11 +132,11 @@ export function workspaceDirFor( * every member. * * Such a file belongs to a workspace the npm graph does not contain — this - * repo's `!packages/desktop-shell` is a separate toolchain with its own + * repo's `!packages/desktop` is a separate toolchain with its own * lockfile — so no included workspace's tests can feel a change to it, and it * must not earn the incomplete-scope caveat a genuinely outside file does. A * file whose nested member is negated while an OUTER member survives - * (`!packages/desktop-shell/src` under `packages/desktop-shell`) is NOT excluded here: + * (`!packages/desktop/src` under `packages/desktop`) is NOT excluded here: * `workspaceDirFor` falls back to the outer member, whose suite collects it. */ export function isNegationExcluded( @@ -351,7 +351,7 @@ export function readWorkspacePackages(root: string): WorkspaceGraph { if (!existsSync(manifest)) continue; if (workspaceDirFor(`${dir}/package.json`, globs) !== dir) { // A directory a negation excludes is not a workspace, and its own - // `package.json` says nothing about that — `packages/desktop-shell` is a + // `package.json` says nothing about that — `packages/desktop` is a // separate toolchain with its own lockfile, and building it from // here fails. The tell: the POSITIVE globs alone still make the dir its // own owner, so a negation is what took the ownership away. diff --git a/packages/cli/src/commands/review/test-efficacy.test.ts b/packages/cli/src/commands/review/test-efficacy.test.ts index 0abb6efe941..9decd7dca6d 100644 --- a/packages/cli/src/commands/review/test-efficacy.test.ts +++ b/packages/cli/src/commands/review/test-efficacy.test.ts @@ -71,7 +71,7 @@ const GLOBS = [ 'packages/*', 'packages/channels/base', 'packages/channels/telegram', - '!packages/desktop-shell', + '!packages/desktop', ]; // Skipped on win32, and not for convenience: `mountRootFor` refuses every @@ -104,30 +104,22 @@ describe('isWorkspaceMember', () => { }); it('honours a negated glob', () => { - expect( - isWorkspaceMember('packages/desktop-shell/src/a.test.ts', GLOBS), - ).toBe(false); + expect(isWorkspaceMember('packages/desktop/src/a.test.ts', GLOBS)).toBe( + false, + ); }); it('honours workspace-glob ORDER — a positive after a negation re-includes', () => { // npm evaluates the list in order. Filtering all negations first let a // negation win wherever it sat, which would file a false `unreachable`. - const globs = [ - 'packages/*', - '!packages/desktop-shell', - 'packages/desktop-shell', - ]; - expect( - isWorkspaceMember('packages/desktop-shell/src/a.test.ts', globs), - ).toBe(true); - const reordered = [ - 'packages/*', - 'packages/desktop-shell', - '!packages/desktop-shell', - ]; - expect( - isWorkspaceMember('packages/desktop-shell/src/a.test.ts', reordered), - ).toBe(false); + const globs = ['packages/*', '!packages/desktop', 'packages/desktop']; + expect(isWorkspaceMember('packages/desktop/src/a.test.ts', globs)).toBe( + true, + ); + const reordered = ['packages/*', 'packages/desktop', '!packages/desktop']; + expect(isWorkspaceMember('packages/desktop/src/a.test.ts', reordered)).toBe( + false, + ); }); it('does not match a sibling directory by prefix', () => { diff --git a/packages/cli/src/config/acp-channel-fallback.ts b/packages/cli/src/config/acp-channel-fallback.ts index 9b0b0a0d1fe..a86352aac84 100644 --- a/packages/cli/src/config/acp-channel-fallback.ts +++ b/packages/cli/src/config/acp-channel-fallback.ts @@ -12,7 +12,7 @@ * `cli/src/serve/channel-worker-supervisor.ts`), so a daemon-spawned * `qwen --acp` child reports `channel=daemon` instead of the generic `ACP`. * The Tauri desktop shell launches `qwen serve` with `QWEN_CODE_DESKTOP=1` - * (`packages/desktop-shell/src-tauri/src/runtime.rs`) and daemon children + * (`packages/desktop/src-tauri/src/runtime.rs`) and daemon children * inherit the marker; those sessions report `channel=desktop`, the same * client identity the Electron desktop passes explicitly. * diff --git a/packages/desktop-shell/.agents/skills/desktop-brand-builder/SKILL.md b/packages/desktop/.agents/skills/desktop-brand-builder/SKILL.md similarity index 89% rename from packages/desktop-shell/.agents/skills/desktop-brand-builder/SKILL.md rename to packages/desktop/.agents/skills/desktop-brand-builder/SKILL.md index e1eb1ac587b..d6d2fda82ec 100644 --- a/packages/desktop-shell/.agents/skills/desktop-brand-builder/SKILL.md +++ b/packages/desktop/.agents/skills/desktop-brand-builder/SKILL.md @@ -1,13 +1,13 @@ --- name: desktop-brand-builder -description: Generate a branded Qwen Code desktop package from the Tauri desktop shell using a minimal brandId and logo. Use when the user wants a custom, white-label, or rebranded desktop client, installer, DMG/EXE/AppImage/deb, or one-click brand build on top of packages/desktop-shell. +description: Generate a branded Qwen Code desktop package from the Tauri desktop shell using a minimal brandId and logo. Use when the user wants a custom, white-label, or rebranded desktop client, installer, DMG/EXE/AppImage/deb, or one-click brand build on top of packages/desktop. --- # Desktop Brand Builder (Tauri shell) ## Goal -Create a branded desktop package from `packages/desktop-shell` with the least +Create a branded desktop package from `packages/desktop` with the least user input possible. The user should usually provide only: ```text @@ -20,10 +20,11 @@ website: https://acme.ai copyright, or updater endpoints unless the user explicitly asks to override them. -This skill replaces the Electron-era brand builder that lived in the removed -`packages/desktop`. The Tauri shell is the only desktop implementation now; -branding hooks are `src-tauri/tauri.conf.json`, `src-tauri/icons/`, and the -`bootstrap/` startup UI. +This skill replaces the Electron-era brand builder that lived in the older, +since-deleted Electron `packages/desktop` (removed in PR 9085, before this +Tauri shell took over that path). The Tauri shell is the only desktop +implementation now; branding hooks are `src-tauri/tauri.conf.json`, +`src-tauri/icons/`, and the `bootstrap/` startup UI. ## Input Rules @@ -102,7 +103,7 @@ Create a temporary `brand.json` in the build directory: } ``` -Install dependencies. The brand script itself only needs desktop-shell's +Install dependencies. The brand script itself only needs the desktop package's own `node_modules`, but `npm run build:runtime` shells out to the repo root (which uses `cross-env` and other root devDependencies), so the root install is also required before packaging: @@ -111,8 +112,8 @@ root install is also required before packaging: # Root dependencies (needed by build:runtime → cross-env, esbuild, etc.) npm install -# Desktop-shell dependencies -cd packages/desktop-shell +# Desktop package dependencies +cd packages/desktop npm install --workspaces=false cd ../.. ``` @@ -121,8 +122,8 @@ Then run this skill's bundled brand creation script with plain Node (the script has no dependencies beyond Node >= 18): ```bash -node packages/desktop-shell/.agents/skills/desktop-brand-builder/scripts/brand-create.mjs \ - --shell-root /absolute/path/to/qwen-code/packages/desktop-shell \ +node packages/desktop/.agents/skills/desktop-brand-builder/scripts/brand-create.mjs \ + --shell-root /absolute/path/to/qwen-code/packages/desktop \ --config /absolute/path/to/brand.json ``` @@ -149,7 +150,7 @@ What the script does: Package with the current host target unless the user requested a target: ```bash -cd packages/desktop-shell +cd packages/desktop npm run build:runtime --workspaces=false npx tauri build # current platform ``` @@ -169,7 +170,7 @@ npx tauri build --target aarch64-apple-darwin # explicit macOS arm64 For `target: all`, iterate `build:runtime` → `tauri build` per target; run only targets supported by the current machine or CI environment. Do not claim cross-platform artifacts were produced unless the files exist. -Artifacts land under `packages/desktop-shell/src-tauri/target/release/bundle/` +Artifacts land under `packages/desktop/src-tauri/target/release/bundle/` for the host target, or `src-tauri/target//release/bundle/` when `--target ` is used. @@ -195,7 +196,7 @@ npx @tauri-apps/cli signer generate -w ~/.tauri/my-brand.key After packaging: 1. Confirm the expected artifact exists under - `packages/desktop-shell/src-tauri/target/release/bundle/` + `packages/desktop/src-tauri/target/release/bundle/` (or `src-tauri/target//release/bundle/` for cross-compile targets) (`dmg/`, `nsis/`, `appimage/`, or `deb/`). 2. Compute `sha256sum` or `shasum -a 256` for each artifact. @@ -207,7 +208,7 @@ After packaging: - Invalid `brandId`: show the regex and ask for a corrected value. - Missing `logo`: ask for a valid local path. - Missing bundled script: report that - `packages/desktop-shell/.agents/skills/desktop-brand-builder/scripts/brand-create.mjs` + `packages/desktop/.agents/skills/desktop-brand-builder/scripts/brand-create.mjs` is missing, and include the expected command. - Already-branded shell-root: the script refuses to run when `productName` is no longer the default (`Qwen Code Desktop`). Start from diff --git a/packages/desktop-shell/.agents/skills/desktop-brand-builder/scripts/brand-create.mjs b/packages/desktop/.agents/skills/desktop-brand-builder/scripts/brand-create.mjs similarity index 97% rename from packages/desktop-shell/.agents/skills/desktop-brand-builder/scripts/brand-create.mjs rename to packages/desktop/.agents/skills/desktop-brand-builder/scripts/brand-create.mjs index 3019cbcfd33..11ded4b3c8d 100644 --- a/packages/desktop-shell/.agents/skills/desktop-brand-builder/scripts/brand-create.mjs +++ b/packages/desktop/.agents/skills/desktop-brand-builder/scripts/brand-create.mjs @@ -2,12 +2,13 @@ /** * Brand creation script for the Tauri desktop shell. * - * Patches packages/desktop-shell so a branded desktop app can be built from - * a minimal brand.json. Replaces the Electron-era brand-create.ts that was - * removed together with packages/desktop. + * Patches packages/desktop so a branded desktop app can be built from + * a minimal brand.json. Replaces the Electron-era brand-create.ts, removed + * together with the former Electron packages/desktop (PR 9085); this Tauri + * shell has since taken over that path. * * Usage: - * node brand-create.mjs --shell-root /path/to/packages/desktop-shell \ + * node brand-create.mjs --shell-root /path/to/packages/desktop \ * --config /path/to/brand.json * * Requires Node >= 18. No external dependencies. @@ -26,7 +27,7 @@ import { extname, join, resolve } from 'node:path'; const BRAND_ID_RE = /^[a-z][a-z0-9-]*$/; const USAGE = - 'Usage: node brand-create.mjs --shell-root /path/to/packages/desktop-shell --config /path/to/brand.json'; + 'Usage: node brand-create.mjs --shell-root /path/to/packages/desktop --config /path/to/brand.json'; function argValue(name) { const index = process.argv.indexOf(name); @@ -43,7 +44,7 @@ function shellRootFromArgs() { if (!value) fail(USAGE); const shellRoot = resolve(value); if (!existsSync(join(shellRoot, 'src-tauri', 'tauri.conf.json'))) { - fail(`desktop-shell package not found: ${shellRoot}`); + fail(`desktop package not found: ${shellRoot}`); } return shellRoot; } diff --git a/packages/desktop-shell/.gitignore b/packages/desktop/.gitignore similarity index 100% rename from packages/desktop-shell/.gitignore rename to packages/desktop/.gitignore diff --git a/packages/desktop-shell/.npmrc b/packages/desktop/.npmrc similarity index 100% rename from packages/desktop-shell/.npmrc rename to packages/desktop/.npmrc diff --git a/packages/desktop-shell/NOTICE b/packages/desktop/NOTICE similarity index 100% rename from packages/desktop-shell/NOTICE rename to packages/desktop/NOTICE diff --git a/packages/desktop-shell/README.md b/packages/desktop/README.md similarity index 100% rename from packages/desktop-shell/README.md rename to packages/desktop/README.md diff --git a/packages/desktop-shell/bootstrap/bootstrap.js b/packages/desktop/bootstrap/bootstrap.js similarity index 100% rename from packages/desktop-shell/bootstrap/bootstrap.js rename to packages/desktop/bootstrap/bootstrap.js diff --git a/packages/desktop-shell/bootstrap/index.html b/packages/desktop/bootstrap/index.html similarity index 100% rename from packages/desktop-shell/bootstrap/index.html rename to packages/desktop/bootstrap/index.html diff --git a/packages/desktop-shell/bootstrap/qwen-code-logo.svg b/packages/desktop/bootstrap/qwen-code-logo.svg similarity index 100% rename from packages/desktop-shell/bootstrap/qwen-code-logo.svg rename to packages/desktop/bootstrap/qwen-code-logo.svg diff --git a/packages/desktop-shell/package-lock.json b/packages/desktop/package-lock.json similarity index 98% rename from packages/desktop-shell/package-lock.json rename to packages/desktop/package-lock.json index ccf0961d12f..48ac298ad6a 100644 --- a/packages/desktop-shell/package-lock.json +++ b/packages/desktop/package-lock.json @@ -1,11 +1,11 @@ { - "name": "@qwen-code/desktop-shell", + "name": "@qwen-code/desktop", "version": "0.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "@qwen-code/desktop-shell", + "name": "@qwen-code/desktop", "version": "0.0.1", "devDependencies": { "@tauri-apps/cli": "^2.8.5" diff --git a/packages/desktop-shell/package.json b/packages/desktop/package.json similarity index 93% rename from packages/desktop-shell/package.json rename to packages/desktop/package.json index 7b59fe3d0a0..2f653a65f53 100644 --- a/packages/desktop-shell/package.json +++ b/packages/desktop/package.json @@ -1,5 +1,5 @@ { - "name": "@qwen-code/desktop-shell", + "name": "@qwen-code/desktop", "version": "0.0.1", "private": true, "type": "module", diff --git a/packages/desktop-shell/runtime/qwen-code/.gitkeep b/packages/desktop/runtime/qwen-code/.gitkeep similarity index 100% rename from packages/desktop-shell/runtime/qwen-code/.gitkeep rename to packages/desktop/runtime/qwen-code/.gitkeep diff --git a/packages/desktop-shell/scripts/prepare-runtime.js b/packages/desktop/scripts/prepare-runtime.js similarity index 100% rename from packages/desktop-shell/scripts/prepare-runtime.js rename to packages/desktop/scripts/prepare-runtime.js diff --git a/packages/desktop-shell/scripts/resolve-log-root.js b/packages/desktop/scripts/resolve-log-root.js similarity index 100% rename from packages/desktop-shell/scripts/resolve-log-root.js rename to packages/desktop/scripts/resolve-log-root.js diff --git a/packages/desktop-shell/scripts/smoke-packaged.js b/packages/desktop/scripts/smoke-packaged.js similarity index 100% rename from packages/desktop-shell/scripts/smoke-packaged.js rename to packages/desktop/scripts/smoke-packaged.js diff --git a/packages/desktop-shell/scripts/smoke-runtime.js b/packages/desktop/scripts/smoke-runtime.js similarity index 100% rename from packages/desktop-shell/scripts/smoke-runtime.js rename to packages/desktop/scripts/smoke-runtime.js diff --git a/packages/desktop-shell/scripts/test-release.js b/packages/desktop/scripts/test-release.js similarity index 99% rename from packages/desktop-shell/scripts/test-release.js rename to packages/desktop/scripts/test-release.js index 3814a825962..8e03527d284 100644 --- a/packages/desktop-shell/scripts/test-release.js +++ b/packages/desktop/scripts/test-release.js @@ -527,7 +527,7 @@ function testRuntimeNodePtyTargetMapping() { } function testRuntimePreparation(directory) { - const testPackageDir = path.join(directory, 'packages', 'desktop-shell'); + const testPackageDir = path.join(directory, 'packages', 'desktop'); const testScript = path.join(testPackageDir, 'scripts', 'prepare-runtime.js'); const sourceRoot = path.join(directory, 'source'); const runtimeDir = path.join(testPackageDir, 'runtime'); diff --git a/packages/desktop-shell/scripts/version.js b/packages/desktop/scripts/version.js similarity index 100% rename from packages/desktop-shell/scripts/version.js rename to packages/desktop/scripts/version.js diff --git a/packages/desktop-shell/src-tauri/.gitignore b/packages/desktop/src-tauri/.gitignore similarity index 100% rename from packages/desktop-shell/src-tauri/.gitignore rename to packages/desktop/src-tauri/.gitignore diff --git a/packages/desktop-shell/src-tauri/Cargo.lock b/packages/desktop/src-tauri/Cargo.lock similarity index 100% rename from packages/desktop-shell/src-tauri/Cargo.lock rename to packages/desktop/src-tauri/Cargo.lock diff --git a/packages/desktop-shell/src-tauri/Cargo.toml b/packages/desktop/src-tauri/Cargo.toml similarity index 100% rename from packages/desktop-shell/src-tauri/Cargo.toml rename to packages/desktop/src-tauri/Cargo.toml diff --git a/packages/desktop-shell/src-tauri/Entitlements.plist b/packages/desktop/src-tauri/Entitlements.plist similarity index 100% rename from packages/desktop-shell/src-tauri/Entitlements.plist rename to packages/desktop/src-tauri/Entitlements.plist diff --git a/packages/desktop-shell/src-tauri/Info.plist b/packages/desktop/src-tauri/Info.plist similarity index 100% rename from packages/desktop-shell/src-tauri/Info.plist rename to packages/desktop/src-tauri/Info.plist diff --git a/packages/desktop-shell/src-tauri/NodeEntitlements.plist b/packages/desktop/src-tauri/NodeEntitlements.plist similarity index 100% rename from packages/desktop-shell/src-tauri/NodeEntitlements.plist rename to packages/desktop/src-tauri/NodeEntitlements.plist diff --git a/packages/desktop-shell/src-tauri/build.rs b/packages/desktop/src-tauri/build.rs similarity index 100% rename from packages/desktop-shell/src-tauri/build.rs rename to packages/desktop/src-tauri/build.rs diff --git a/packages/desktop-shell/src-tauri/capabilities/.gitkeep b/packages/desktop/src-tauri/capabilities/.gitkeep similarity index 100% rename from packages/desktop-shell/src-tauri/capabilities/.gitkeep rename to packages/desktop/src-tauri/capabilities/.gitkeep diff --git a/packages/desktop-shell/src-tauri/capabilities/bootstrap.json b/packages/desktop/src-tauri/capabilities/bootstrap.json similarity index 100% rename from packages/desktop-shell/src-tauri/capabilities/bootstrap.json rename to packages/desktop/src-tauri/capabilities/bootstrap.json diff --git a/packages/desktop-shell/src-tauri/capabilities/web-shell-external-url.json b/packages/desktop/src-tauri/capabilities/web-shell-external-url.json similarity index 100% rename from packages/desktop-shell/src-tauri/capabilities/web-shell-external-url.json rename to packages/desktop/src-tauri/capabilities/web-shell-external-url.json diff --git a/packages/desktop-shell/src-tauri/icons/128x128.png b/packages/desktop/src-tauri/icons/128x128.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/128x128.png rename to packages/desktop/src-tauri/icons/128x128.png diff --git a/packages/desktop-shell/src-tauri/icons/128x128@2x.png b/packages/desktop/src-tauri/icons/128x128@2x.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/128x128@2x.png rename to packages/desktop/src-tauri/icons/128x128@2x.png diff --git a/packages/desktop-shell/src-tauri/icons/32x32.png b/packages/desktop/src-tauri/icons/32x32.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/32x32.png rename to packages/desktop/src-tauri/icons/32x32.png diff --git a/packages/desktop-shell/src-tauri/icons/64x64.png b/packages/desktop/src-tauri/icons/64x64.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/64x64.png rename to packages/desktop/src-tauri/icons/64x64.png diff --git a/packages/desktop-shell/src-tauri/icons/Square107x107Logo.png b/packages/desktop/src-tauri/icons/Square107x107Logo.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/Square107x107Logo.png rename to packages/desktop/src-tauri/icons/Square107x107Logo.png diff --git a/packages/desktop-shell/src-tauri/icons/Square142x142Logo.png b/packages/desktop/src-tauri/icons/Square142x142Logo.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/Square142x142Logo.png rename to packages/desktop/src-tauri/icons/Square142x142Logo.png diff --git a/packages/desktop-shell/src-tauri/icons/Square150x150Logo.png b/packages/desktop/src-tauri/icons/Square150x150Logo.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/Square150x150Logo.png rename to packages/desktop/src-tauri/icons/Square150x150Logo.png diff --git a/packages/desktop-shell/src-tauri/icons/Square284x284Logo.png b/packages/desktop/src-tauri/icons/Square284x284Logo.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/Square284x284Logo.png rename to packages/desktop/src-tauri/icons/Square284x284Logo.png diff --git a/packages/desktop-shell/src-tauri/icons/Square30x30Logo.png b/packages/desktop/src-tauri/icons/Square30x30Logo.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/Square30x30Logo.png rename to packages/desktop/src-tauri/icons/Square30x30Logo.png diff --git a/packages/desktop-shell/src-tauri/icons/Square310x310Logo.png b/packages/desktop/src-tauri/icons/Square310x310Logo.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/Square310x310Logo.png rename to packages/desktop/src-tauri/icons/Square310x310Logo.png diff --git a/packages/desktop-shell/src-tauri/icons/Square44x44Logo.png b/packages/desktop/src-tauri/icons/Square44x44Logo.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/Square44x44Logo.png rename to packages/desktop/src-tauri/icons/Square44x44Logo.png diff --git a/packages/desktop-shell/src-tauri/icons/Square71x71Logo.png b/packages/desktop/src-tauri/icons/Square71x71Logo.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/Square71x71Logo.png rename to packages/desktop/src-tauri/icons/Square71x71Logo.png diff --git a/packages/desktop-shell/src-tauri/icons/Square89x89Logo.png b/packages/desktop/src-tauri/icons/Square89x89Logo.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/Square89x89Logo.png rename to packages/desktop/src-tauri/icons/Square89x89Logo.png diff --git a/packages/desktop-shell/src-tauri/icons/StoreLogo.png b/packages/desktop/src-tauri/icons/StoreLogo.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/StoreLogo.png rename to packages/desktop/src-tauri/icons/StoreLogo.png diff --git a/packages/desktop-shell/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml b/packages/desktop/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml similarity index 100% rename from packages/desktop-shell/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml rename to packages/desktop/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml diff --git a/packages/desktop-shell/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png b/packages/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png rename to packages/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png diff --git a/packages/desktop-shell/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png b/packages/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png rename to packages/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png diff --git a/packages/desktop-shell/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png b/packages/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png rename to packages/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png diff --git a/packages/desktop-shell/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png b/packages/desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png rename to packages/desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png diff --git a/packages/desktop-shell/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png b/packages/desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png rename to packages/desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png diff --git a/packages/desktop-shell/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png b/packages/desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png rename to packages/desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png diff --git a/packages/desktop-shell/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png b/packages/desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png rename to packages/desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png diff --git a/packages/desktop-shell/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png b/packages/desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png rename to packages/desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png diff --git a/packages/desktop-shell/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png b/packages/desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png rename to packages/desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png diff --git a/packages/desktop-shell/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png b/packages/desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png rename to packages/desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png diff --git a/packages/desktop-shell/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png b/packages/desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png rename to packages/desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png diff --git a/packages/desktop-shell/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png b/packages/desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png rename to packages/desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png diff --git a/packages/desktop-shell/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png b/packages/desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png rename to packages/desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png diff --git a/packages/desktop-shell/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png b/packages/desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png rename to packages/desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png diff --git a/packages/desktop-shell/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png b/packages/desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png rename to packages/desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/packages/desktop-shell/src-tauri/icons/android/values/ic_launcher_background.xml b/packages/desktop/src-tauri/icons/android/values/ic_launcher_background.xml similarity index 100% rename from packages/desktop-shell/src-tauri/icons/android/values/ic_launcher_background.xml rename to packages/desktop/src-tauri/icons/android/values/ic_launcher_background.xml diff --git a/packages/desktop-shell/src-tauri/icons/icon.icns b/packages/desktop/src-tauri/icons/icon.icns similarity index 100% rename from packages/desktop-shell/src-tauri/icons/icon.icns rename to packages/desktop/src-tauri/icons/icon.icns diff --git a/packages/desktop-shell/src-tauri/icons/icon.ico b/packages/desktop/src-tauri/icons/icon.ico similarity index 100% rename from packages/desktop-shell/src-tauri/icons/icon.ico rename to packages/desktop/src-tauri/icons/icon.ico diff --git a/packages/desktop-shell/src-tauri/icons/icon.png b/packages/desktop/src-tauri/icons/icon.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/icon.png rename to packages/desktop/src-tauri/icons/icon.png diff --git a/packages/desktop-shell/src-tauri/icons/ios/AppIcon-20x20@1x.png b/packages/desktop/src-tauri/icons/ios/AppIcon-20x20@1x.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/ios/AppIcon-20x20@1x.png rename to packages/desktop/src-tauri/icons/ios/AppIcon-20x20@1x.png diff --git a/packages/desktop-shell/src-tauri/icons/ios/AppIcon-20x20@2x-1.png b/packages/desktop/src-tauri/icons/ios/AppIcon-20x20@2x-1.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/ios/AppIcon-20x20@2x-1.png rename to packages/desktop/src-tauri/icons/ios/AppIcon-20x20@2x-1.png diff --git a/packages/desktop-shell/src-tauri/icons/ios/AppIcon-20x20@2x.png b/packages/desktop/src-tauri/icons/ios/AppIcon-20x20@2x.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/ios/AppIcon-20x20@2x.png rename to packages/desktop/src-tauri/icons/ios/AppIcon-20x20@2x.png diff --git a/packages/desktop-shell/src-tauri/icons/ios/AppIcon-20x20@3x.png b/packages/desktop/src-tauri/icons/ios/AppIcon-20x20@3x.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/ios/AppIcon-20x20@3x.png rename to packages/desktop/src-tauri/icons/ios/AppIcon-20x20@3x.png diff --git a/packages/desktop-shell/src-tauri/icons/ios/AppIcon-29x29@1x.png b/packages/desktop/src-tauri/icons/ios/AppIcon-29x29@1x.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/ios/AppIcon-29x29@1x.png rename to packages/desktop/src-tauri/icons/ios/AppIcon-29x29@1x.png diff --git a/packages/desktop-shell/src-tauri/icons/ios/AppIcon-29x29@2x-1.png b/packages/desktop/src-tauri/icons/ios/AppIcon-29x29@2x-1.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/ios/AppIcon-29x29@2x-1.png rename to packages/desktop/src-tauri/icons/ios/AppIcon-29x29@2x-1.png diff --git a/packages/desktop-shell/src-tauri/icons/ios/AppIcon-29x29@2x.png b/packages/desktop/src-tauri/icons/ios/AppIcon-29x29@2x.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/ios/AppIcon-29x29@2x.png rename to packages/desktop/src-tauri/icons/ios/AppIcon-29x29@2x.png diff --git a/packages/desktop-shell/src-tauri/icons/ios/AppIcon-29x29@3x.png b/packages/desktop/src-tauri/icons/ios/AppIcon-29x29@3x.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/ios/AppIcon-29x29@3x.png rename to packages/desktop/src-tauri/icons/ios/AppIcon-29x29@3x.png diff --git a/packages/desktop-shell/src-tauri/icons/ios/AppIcon-40x40@1x.png b/packages/desktop/src-tauri/icons/ios/AppIcon-40x40@1x.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/ios/AppIcon-40x40@1x.png rename to packages/desktop/src-tauri/icons/ios/AppIcon-40x40@1x.png diff --git a/packages/desktop-shell/src-tauri/icons/ios/AppIcon-40x40@2x-1.png b/packages/desktop/src-tauri/icons/ios/AppIcon-40x40@2x-1.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/ios/AppIcon-40x40@2x-1.png rename to packages/desktop/src-tauri/icons/ios/AppIcon-40x40@2x-1.png diff --git a/packages/desktop-shell/src-tauri/icons/ios/AppIcon-40x40@2x.png b/packages/desktop/src-tauri/icons/ios/AppIcon-40x40@2x.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/ios/AppIcon-40x40@2x.png rename to packages/desktop/src-tauri/icons/ios/AppIcon-40x40@2x.png diff --git a/packages/desktop-shell/src-tauri/icons/ios/AppIcon-40x40@3x.png b/packages/desktop/src-tauri/icons/ios/AppIcon-40x40@3x.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/ios/AppIcon-40x40@3x.png rename to packages/desktop/src-tauri/icons/ios/AppIcon-40x40@3x.png diff --git a/packages/desktop-shell/src-tauri/icons/ios/AppIcon-512@2x.png b/packages/desktop/src-tauri/icons/ios/AppIcon-512@2x.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/ios/AppIcon-512@2x.png rename to packages/desktop/src-tauri/icons/ios/AppIcon-512@2x.png diff --git a/packages/desktop-shell/src-tauri/icons/ios/AppIcon-60x60@2x.png b/packages/desktop/src-tauri/icons/ios/AppIcon-60x60@2x.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/ios/AppIcon-60x60@2x.png rename to packages/desktop/src-tauri/icons/ios/AppIcon-60x60@2x.png diff --git a/packages/desktop-shell/src-tauri/icons/ios/AppIcon-60x60@3x.png b/packages/desktop/src-tauri/icons/ios/AppIcon-60x60@3x.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/ios/AppIcon-60x60@3x.png rename to packages/desktop/src-tauri/icons/ios/AppIcon-60x60@3x.png diff --git a/packages/desktop-shell/src-tauri/icons/ios/AppIcon-76x76@1x.png b/packages/desktop/src-tauri/icons/ios/AppIcon-76x76@1x.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/ios/AppIcon-76x76@1x.png rename to packages/desktop/src-tauri/icons/ios/AppIcon-76x76@1x.png diff --git a/packages/desktop-shell/src-tauri/icons/ios/AppIcon-76x76@2x.png b/packages/desktop/src-tauri/icons/ios/AppIcon-76x76@2x.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/ios/AppIcon-76x76@2x.png rename to packages/desktop/src-tauri/icons/ios/AppIcon-76x76@2x.png diff --git a/packages/desktop-shell/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png b/packages/desktop/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png similarity index 100% rename from packages/desktop-shell/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png rename to packages/desktop/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png diff --git a/packages/desktop-shell/src-tauri/src/desktop_state.rs b/packages/desktop/src-tauri/src/desktop_state.rs similarity index 100% rename from packages/desktop-shell/src-tauri/src/desktop_state.rs rename to packages/desktop/src-tauri/src/desktop_state.rs diff --git a/packages/desktop-shell/src-tauri/src/main.rs b/packages/desktop/src-tauri/src/main.rs similarity index 99% rename from packages/desktop-shell/src-tauri/src/main.rs rename to packages/desktop/src-tauri/src/main.rs index cff7b7028c5..c80cae388be 100755 --- a/packages/desktop-shell/src-tauri/src/main.rs +++ b/packages/desktop/src-tauri/src/main.rs @@ -31,8 +31,8 @@ static FULLSCREEN_HIDE_PENDING: AtomicBool = AtomicBool::new(false); #[cfg(target_os = "macos")] static FULLSCREEN_HIDE_GENERATION: AtomicU64 = AtomicU64::new(0); // Keep the default first-launch workspace aligned with the Electron shell's -// getDefaultConversationWorkspacePath() in -// packages/desktop/packages/shared/src/config/storage.ts: ~/Documents/Qwen, +// getDefaultConversationWorkspacePath(), in the removed Electron package's +// packages/shared/src/config/storage.ts (PR 9085): ~/Documents/Qwen, // relocatable through QWEN_DEFAULT_WORKSPACE_DIR (see default_workspace). const DEFAULT_WORKSPACE_DIRECTORY: &str = "Qwen"; const UPDATE_CHECK_TIMEOUT: Duration = Duration::from_secs(3); diff --git a/packages/desktop-shell/src-tauri/src/runtime.rs b/packages/desktop/src-tauri/src/runtime.rs similarity index 100% rename from packages/desktop-shell/src-tauri/src/runtime.rs rename to packages/desktop/src-tauri/src/runtime.rs diff --git a/packages/desktop-shell/src-tauri/tauri.conf.json b/packages/desktop/src-tauri/tauri.conf.json similarity index 100% rename from packages/desktop-shell/src-tauri/tauri.conf.json rename to packages/desktop/src-tauri/tauri.conf.json diff --git a/packages/desktop-shell/src-tauri/windows-app-manifest.xml b/packages/desktop/src-tauri/windows-app-manifest.xml similarity index 100% rename from packages/desktop-shell/src-tauri/windows-app-manifest.xml rename to packages/desktop/src-tauri/windows-app-manifest.xml diff --git a/packages/desktop-shell/src-tauri/windows/electron-migration.nsh b/packages/desktop/src-tauri/windows/electron-migration.nsh similarity index 100% rename from packages/desktop-shell/src-tauri/windows/electron-migration.nsh rename to packages/desktop/src-tauri/windows/electron-migration.nsh diff --git a/packages/web-shell/client/components/messages/LinkifiedText.tsx b/packages/web-shell/client/components/messages/LinkifiedText.tsx index 5786019917d..c74bc0064a9 100644 --- a/packages/web-shell/client/components/messages/LinkifiedText.tsx +++ b/packages/web-shell/client/components/messages/LinkifiedText.tsx @@ -6,7 +6,7 @@ import styles from './Markdown.module.css'; /** * Plain-text renderer that turns explicit http(s) URLs into anchors. Mirrors - * MarkdownLink's safety (`isSafeHref`) and desktop-shell routing + * MarkdownLink's safety (`isSafeHref`) and desktop-host routing * (`useExternalLinkOpener`); text without URLs passes through untouched. */ export const LinkifiedText = memo(function LinkifiedText({ diff --git a/packages/web-shell/client/index.html b/packages/web-shell/client/index.html index 116e2676612..ab0d5712128 100644 --- a/packages/web-shell/client/index.html +++ b/packages/web-shell/client/index.html @@ -87,7 +87,7 @@ would never be reachable. The data: URI is allowed by the shell CSP (img-src 'self' data: blob:). The encodeURIComponent(svg) encoding technique mirrors packages/web-templates/src/export-html/build.mjs; - the artwork is from packages/desktop-shell/bootstrap/qwen-code-logo.svg + the artwork is from packages/desktop/bootstrap/qwen-code-logo.svg (the purple #6D44E8 Qwen mark, legible on both light and dark browser tab bars). --> diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 9da154cc99e..968149c5cb3 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -16,7 +16,7 @@ packages: - 'packages/channels/weixin' - 'integrations/external-context' - 'integrations/external-context-mem0' - - '!packages/desktop-shell' + - '!packages/desktop' - '!packages/live-host' - '!packages/mobile-shell' diff --git a/scripts/check-desktop-isolation.js b/scripts/check-desktop-isolation.js index 2a2d3ceefb5..e63fc169c1f 100644 --- a/scripts/check-desktop-isolation.js +++ b/scripts/check-desktop-isolation.js @@ -15,6 +15,9 @@ import { parse as parseYaml } from 'yaml'; const __dirname = dirname(fileURLToPath(import.meta.url)); const root = join(__dirname, '..'); const nativePrefixes = [ + 'packages/desktop', + // The pre-rename location: nothing should be created under it again, and it + // must never enter a workspace set either. 'packages/desktop-shell', 'packages/live-host', 'packages/mobile-shell', diff --git a/scripts/tests/brand-create-safety.test.js b/scripts/tests/brand-create-safety.test.js index a02e002af0b..862a088eca1 100644 --- a/scripts/tests/brand-create-safety.test.js +++ b/scripts/tests/brand-create-safety.test.js @@ -22,7 +22,7 @@ const SCRIPT = join( '..', '..', 'packages', - 'desktop-shell', + 'desktop', '.agents', 'skills', 'desktop-brand-builder', @@ -58,7 +58,7 @@ function makeShellRoot({ withUpdater = true } = {}) { ); writeFileSync( join(root, 'package.json'), - JSON.stringify({ name: 'desktop-shell', type: 'module' }), + JSON.stringify({ name: 'desktop', type: 'module' }), ); return root; } diff --git a/scripts/tests/desktop-isolation.test.js b/scripts/tests/desktop-isolation.test.js new file mode 100644 index 00000000000..3b447483b4f --- /dev/null +++ b/scripts/tests/desktop-isolation.test.js @@ -0,0 +1,213 @@ +/** + * @license + * Copyright 2026 Qwen Team + * SPDX-License-Identifier: Apache-2.0 + */ + +// Two agreements nothing else in the suite reads, both of which fail green. +// +// 1. `scripts/check-desktop-isolation.js` keeps a `nativePrefixes` list, and +// the root workspace manifests keep a `!packages/*` negation per native +// package. Those two lists are maintained by hand on opposite sides of the +// same invariant, so a rename that moves one and not the other leaves the +// guard matching nothing while it still prints "passed". +// `packages/desktop-shell` -> `packages/desktop` is exactly that rename, and +// `'packages/desktop'` had to be added to `nativePrefixes` by hand for the +// guard to keep covering the package. Deleting the added line changes +// neither the guard's output nor its exit code, so only this file notices. +// +// 2. The `desktop_shell` job in `ci.yml` names the crate directory in five +// places -- the changed-files filter, the `Cargo.toml` existence guard and +// its `::notice::` text, the rust-cache `workspaces:`, and two +// `working-directory:` values. They have to name the same directory as each +// other and a directory that exists, or the job skips and reports success +// having compiled nothing. +// +// The npm/pnpm mirror of the negation list is already pinned by +// `package-scripts.test.js` ("mirrors the npm workspace boundaries in +// pnpm-workspace.yaml"), so these tests read `package.json` only. + +import { existsSync, readFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { describe, expect, it } from 'vitest'; +import { parse as parseYaml } from 'yaml'; + +const root = join(dirname(fileURLToPath(import.meta.url)), '..', '..'); +const read = (...parts) => readFileSync(join(root, ...parts), 'utf8'); + +// The script runs `npm query` and calls `process.exit` at import time, so it +// cannot be imported for its list; parse the literal instead. Line comments are +// stripped first so prose inside the array cannot contribute an entry. +function nativePrefixesFromSource(source) { + const array = /const nativePrefixes = \[([\s\S]*?)\n\];/u.exec(source); + if (!array) { + throw new Error( + 'scripts/check-desktop-isolation.js no longer declares `const nativePrefixes = [...]`; update this parser.', + ); + } + return [...array[1].replace(/\/\/[^\n]*/gu, '').matchAll(/'([^']+)'/gu)].map( + (match) => match[1], + ); +} + +// A hand-copied duplicate of the rule `isNativeLocation` implements in +// `scripts/check-desktop-isolation.js`, used below to check the prefix *data*. +// Only the array literal is read back from the script, not the rule, so a +// change to the script's rule does not fail here -- update this copy by hand. +const isNativeLocation = (location, prefixes) => + prefixes.some( + (prefix) => location === prefix || location.startsWith(`${prefix}/`), + ); + +const nativePrefixes = nativePrefixesFromSource( + read('scripts', 'check-desktop-isolation.js'), +); +const npmNegations = JSON.parse(read('package.json')) + .workspaces.filter( + (entry) => typeof entry === 'string' && entry.startsWith('!'), + ) + .map((entry) => entry.slice(1)); + +const ci = parseYaml(read('.github', 'workflows', 'ci.yml')); +const desktopJob = ci.jobs.desktop_shell; +const stepNamed = (name) => + desktopJob.steps.find((step) => step.name === name) ?? + desktopJob.steps.find((step) => String(step.uses ?? '').includes(name)); + +describe('desktop isolation guard — nativePrefixes coverage', () => { + it('matches every negated native workspace entry', () => { + expect(npmNegations.length).toBeGreaterThan(0); + for (const location of npmNegations) { + expect( + isNativeLocation(location, nativePrefixes), + `package.json negates !${location}, but no nativePrefixes entry matches it under the script's rule, so check-desktop-isolation.js would not recognise the package if it re-entered the workspace set. nativePrefixes: ${JSON.stringify(nativePrefixes)}`, + ).toBe(true); + } + }); + + it('negates every native prefix that exists on disk', () => { + // Keyed on the manifest, not the directory: `packages/*` only makes a + // directory a workspace member if it holds a package.json, while ignored + // build residue (node_modules, src-tauri/target) survives a branch switch + // under a path that is no longer a package at all. + const onDisk = nativePrefixes.filter((prefix) => + existsSync(join(root, prefix, 'package.json')), + ); + // Without this the test would also pass on a tree where the package moved + // and no prefix resolved at all. + expect(onDisk).toContain('packages/desktop'); + for (const prefix of onDisk) { + expect( + npmNegations, + `${prefix} holds a package.json but the root manifest does not negate it, so \`packages/*\` pulls it into the root npm workspace set.`, + ).toContain(prefix); + } + }); + + it('keeps the pre-rename tripwire, which has no negation to mirror', () => { + // Recorded intent: docs/design/9152-architecture-invariant-classification.md + // ("fails if `packages/desktop` or `packages/desktop-shell` re-enters the + // root npm workspace set"). The coverage assertions above are deliberately + // superset-shaped so this entry, which nothing negates, stays legal. + expect(nativePrefixes).toContain('packages/desktop-shell'); + // The manifest, not the directory: ignored build residue under the old + // path survives a branch switch and is not the re-entry this pins. + expect( + existsSync(join(root, 'packages', 'desktop-shell', 'package.json')), + 'packages/desktop-shell holds a package.json again, so `packages/*` pulls the pre-rename path back into the root npm workspace set.', + ).toBe(false); + }); +}); + +describe('desktop_shell CI job — the crate path agrees with itself', () => { + const filterStep = desktopJob.steps.find((step) => step.id === 'filter'); + const filterRun = String(filterStep?.run ?? ''); + + it('still carries the Cargo.toml existence guard, and it clears changed', () => { + // Not redundant with the filter: the filter also matches on ci.yml and + // desktop-release.yml changing, which a head with no crate at all can do. + // That is the #8132 failure the job's header comment describes. + // The condition is only half the guard: without the assignment inside its + // own body, every gated step still runs against a crate that is not there. + const guardBody = + /! -f \S+\/src-tauri\/Cargo\.toml \]\]; then\n([\s\S]*?)\n[ \t]*fi/u.exec( + filterRun, + )?.[1]; + expect( + guardBody, + 'the Cargo.toml guard lost its `if ...; then ... fi` body', + ).toBeDefined(); + expect( + guardBody, + 'the Cargo.toml guard no longer sets changed=false inside its own body, so a head without the crate runs every gated step and reports a missing working directory as a failure of the PR', + ).toMatch(/^\s*changed=false\s*$/mu); + }); + + it('names one crate directory in all five places, and it exists', () => { + const filterAlternative = /\^\(([^|]+)\|/u.exec(filterRun)?.[1]; + expect( + filterAlternative, + 'changed-files filter lost its path alternative', + ).toBeDefined(); + + const guardPath = /! -f (\S+)\/src-tauri\/Cargo\.toml/u.exec( + filterRun, + )?.[1]; + // The notice names the crate's src-tauri, not the package directory. + const noticePath = /::notice::(\S+) is absent from this head/u + .exec(filterRun)?.[1] + .replace(/\/src-tauri$/u, ''); + + const rustCache = stepNamed('Swatinem/rust-cache'); + const cacheRoot = /^(.+?)\/src-tauri -> /u.exec( + String(rustCache?.with?.workspaces ?? ''), + )?.[1]; + + const workingDirs = desktopJob.steps + .map((step) => step['working-directory']) + .filter((value) => value !== undefined) + .map(String); + + expect(filterAlternative).toBeDefined(); + const sites = { + 'changed-files filter': filterAlternative.replace(/\/$/u, ''), + 'Cargo.toml guard': guardPath, + 'guard ::notice::': noticePath, + 'rust-cache workspaces': cacheRoot, + ...Object.fromEntries( + workingDirs.map((dir, index) => [`working-directory[${index}]`, dir]), + ), + }; + + // Every site must resolve; an undefined one means the parser fell behind a + // rewrite of ci.yml, which must not read as agreement. + for (const [site, value] of Object.entries(sites)) { + expect( + value, + `could not read the crate directory out of ${site}`, + ).toBeDefined(); + } + expect( + new Set(Object.values(sites)).size, + JSON.stringify(sites, null, 2), + ).toBe(1); + + const crateDir = sites['changed-files filter']; + expect( + existsSync(join(root, crateDir, 'src-tauri', 'Cargo.toml')), + `${crateDir}/src-tauri/Cargo.toml does not exist, so every gated step in desktop_shell skips and the job reports success having compiled nothing.`, + ).toBe(true); + // The filter is `grep -Eq '^(|...)'` over the PR's changed + // file names, so the alternative is a bare path prefix and has to end in + // `/`: without it, `packages/desktop` also matches a sibling such as + // `packages/desktop-extra/`, and the job runs on heads that never touch + // the crate. Building a path out of `crateDir` -- this same alternative + // with the slash stripped -- and comparing it back proves nothing. + expect( + filterAlternative.endsWith('/'), + `the filter alternative ${filterAlternative} has no trailing slash, so the changed-files filter also matches sibling paths that merely start with it`, + ).toBe(true); + }); +}); diff --git a/scripts/tests/desktop-oss-workflow.test.js b/scripts/tests/desktop-oss-workflow.test.js index a995ac00073..449655939a4 100644 --- a/scripts/tests/desktop-oss-workflow.test.js +++ b/scripts/tests/desktop-oss-workflow.test.js @@ -29,7 +29,7 @@ const syncWorkflow = readFileSync( 'utf8', ); const tauriConfig = JSON.parse( - readFileSync('packages/desktop-shell/src-tauri/tauri.conf.json', 'utf8'), + readFileSync('packages/desktop/src-tauri/tauri.conf.json', 'utf8'), ); const hasJq = (() => { try { diff --git a/scripts/tests/desktop-smoke-teardown.test.js b/scripts/tests/desktop-smoke-teardown.test.js index bd672429b68..725c935befb 100644 --- a/scripts/tests/desktop-smoke-teardown.test.js +++ b/scripts/tests/desktop-smoke-teardown.test.js @@ -18,7 +18,7 @@ const repoRoot = path.resolve( const smokeScript = path.join( repoRoot, 'packages', - 'desktop-shell', + 'desktop', 'scripts', 'smoke-packaged.js', ); diff --git a/scripts/tests/package-scripts.test.js b/scripts/tests/package-scripts.test.js index d7e17f1b12f..29a95fbdf38 100644 --- a/scripts/tests/package-scripts.test.js +++ b/scripts/tests/package-scripts.test.js @@ -923,7 +923,7 @@ describe('package scripts', () => { '.pnpmfile.mjs', 'package.json', 'packages/*/package.json', - '!packages/desktop-shell/package.json', + '!packages/desktop/package.json', '!packages/live-host/package.json', '!packages/mobile-shell/package.json', 'packages/channels/*/package.json', diff --git a/scripts/tests/qwen-autofix-workflow.test.js b/scripts/tests/qwen-autofix-workflow.test.js index 675048c360a..f6cefa83331 100644 --- a/scripts/tests/qwen-autofix-workflow.test.js +++ b/scripts/tests/qwen-autofix-workflow.test.js @@ -13287,7 +13287,7 @@ exit 1 workspaces: [ 'packages/*', 'packages/channels/*', - '!packages/desktop-shell', + '!packages/desktop', ], }), ); @@ -13296,7 +13296,7 @@ exit 1 'packages/brandnew', // a new top-level workspace the branch adds 'packages/channels/base', 'packages/channels/newchannel', // a new nested workspace the branch adds - 'packages/desktop-shell', // excluded by the ! glob + 'packages/desktop', // excluded by the ! glob 'packages/cli/src/commands/examples/starter', // fixture, NOT a workspace ]) { mkdirSync(join(dir, pkg), { recursive: true }); @@ -13308,7 +13308,7 @@ exit 1 'packages/cli/src/commands/examples/starter/src/index.ts', // -> packages/cli 'packages/brandnew/src/z.ts', // -> packages/brandnew (branch-added) 'packages/channels/newchannel/src/y.ts', // -> newchannel (branch-added nested) - 'packages/desktop-shell/src/d.ts', // excluded workspace -> dropped + 'packages/desktop/src/d.ts', // excluded workspace -> dropped 'packages/sdk-python/foo.py', // no manifest -> dropped 'README.md', // outside packages/ -> dropped ].join('\n') + '\n'; @@ -13324,7 +13324,7 @@ exit 1 ]); expect(out).not.toContain('examples/starter'); // fixture never owns expect(out).not.toContain('sdk-python'); - expect(out).not.toContain('packages/desktop-shell'); // ! negation honoured + expect(out).not.toContain('packages/desktop'); // ! negation honoured } finally { rmSync(dir, { recursive: true, force: true }); } @@ -13776,7 +13776,7 @@ exit 1 'patches/ink+7.0.3.patch', '.gitattributes', 'packages/core/.gitattributes', - 'packages/desktop-shell/.npmrc', + 'packages/desktop/.npmrc', 'eslint.legacy-filenames.mjs', 'eslint.legacy-core-barrel-imports.mjs', '.github/workflows/qwen-pr-safety-precheck.yml', @@ -13805,7 +13805,7 @@ exit 1 expect(classes).toContain( 'packages/core/.gitattributes=measurement-config', ); - expect(classes).toContain('packages/desktop-shell/.npmrc=toolchain-config'); + expect(classes).toContain('packages/desktop/.npmrc=toolchain-config'); expect(classes).toContain('eslint.legacy-filenames.mjs=lint-config'); expect(classes).toContain( 'eslint.legacy-core-barrel-imports.mjs=lint-config', diff --git a/scripts/tests/security-checks-audit-retry.test.js b/scripts/tests/security-checks-audit-retry.test.js index 96ebc3388f1..9d01c8da613 100644 --- a/scripts/tests/security-checks-audit-retry.test.js +++ b/scripts/tests/security-checks-audit-retry.test.js @@ -57,7 +57,7 @@ describe('security-checks audit step endpoint-error retry', () => { try { // mobile-mcp is the one lockfile the loop skips. Including it means // deleting that skip line changes the counts below instead of passing. - for (const pkg of ['desktop-shell', 'live-host', 'mobile-mcp']) { + for (const pkg of ['desktop', 'live-host', 'mobile-mcp']) { mkdirSync(join(dir, 'packages', pkg), { recursive: true }); writeFileSync(join(dir, 'packages', pkg, 'package-lock.json'), '{}'); } diff --git a/scripts/tests/workspaces.test.js b/scripts/tests/workspaces.test.js index 0568cf0f694..5a055533d5c 100644 --- a/scripts/tests/workspaces.test.js +++ b/scripts/tests/workspaces.test.js @@ -29,7 +29,7 @@ describe('workspace helpers', () => { for (const packagePath of [ 'packages/cli/package.json', 'packages/core/package.json', - 'packages/desktop-shell/package.json', + 'packages/desktop/package.json', 'packages/channels/base/package.json', ]) { writeFile(root, packagePath, '{}\n'); @@ -39,7 +39,7 @@ describe('workspace helpers', () => { getWorkspacePackageJsonPaths(root, [ 'packages/*', 'packages/channels/base', - '!packages/desktop-shell', + '!packages/desktop', ]), ).toEqual([ 'packages/channels/base/package.json', @@ -55,7 +55,7 @@ describe('workspace helpers', () => { for (const packagePath of [ 'packages/cli/package.json', 'packages/core/package.json', - 'packages/desktop-shell/package.json', + 'packages/desktop/package.json', 'packages/channels/base/package.json', ]) { writeFile(root, packagePath, '{}\n'); @@ -65,7 +65,7 @@ describe('workspace helpers', () => { getWorkspacePackageJsonPaths(root, [ 'packages\\*', 'packages\\channels\\base', - '!packages\\desktop-shell', + '!packages\\desktop', ]), ).toEqual([ 'packages/channels/base/package.json',