modelstudioai / modelstudioai/openwork
Migration tracker: re-found OpenWork on the Qwen Code Web Shell + Tauri architecture
Personne n'a encore pris cette issue.
- Langage dominant
- TypeScript
- Étoiles
- 27
- Forks
- 6
- Métriques de merge des PR
- Aucune PR mergée en 30 j
Description
Summary
Re-found OpenWork on the Qwen Code stack: fork QwenLM/qwen-code into a standalone OpenWork repository, adopt the Tauri 2 desktop shell (packages/desktop-shell) and Web Shell UI (packages/web-shell), and port OpenWork's existing product surface onto that architecture.
This issue is the master tracker. Workstreams below will be broken out into sub-issues.
1. Context: what OpenWork actually is today
Establishing this correctly matters, because it determines what "sync from upstream" can and cannot mean.
OpenWork is not a fork of Qwen Code. Its root commit 7b57e21 (2026-05-01) is a subtree extraction from eb45067f "Make backend Qwen-only", whose tree is the root-level Craft layout (apps/{cli,electron,viewer,webui}, Dockerfile.server). Qwen Code did not gain packages/desktop until 62e45c567 (#3778) on 2026-06-11 — six weeks later. eb45067f is not an ancestor of qwen-code@main.
The real topology is two siblings from a common Craft-derived ancestor:
craft-ai-agents/craft-agents-oss (craft-agent, Electron)
│
▼
eb45067f "Make backend Qwen-only" (2026-05-01)
│
┌───────┴────────┐
▼ ▼
OpenWork qwen-code/packages/desktop
228 commits 50 commits
v0.1.4 v0.0.5
Current coupling to Qwen Code is narrow: qwenCodeRuntime.version: 0.15.11 in package.json, vendored from the npm tarball by scripts/vendor-qwen-code.ts, spawned as a subprocess by packages/shared/src/agent/qwen-agent.ts with ['--acp', '--channel=desktop']. Upstream CLI is now at 0.21.6 — six minors ahead.
Consequence of this migration: tracking craft-agents-oss ends. Adopting Web Shell replaces the Craft-derived UI and server layers. That is an accepted, deliberate trade — noted here so it is never a surprise.
2. Target architecture
Mirrors upstream's packages/desktop-shell:
- Tauri 2 shell (~1,537 lines of Rust:
main.rs678,runtime.rs547,desktop_state.rs312) replaces Electron. - Shell starts the daemon on an ephemeral loopback port with a per-launch bearer token, polls
/health, then opens the daemon-served Web Shell in a native window. scripts/prepare-runtime.jsstagesruntime/qwen-code/with a platform Node.js, the built CLI, and Web Shell underlib/web-shell/.
All artifacts build from source. prepare-runtime.js resolves sourceRoot from QWEN_CODE_ROOT or the repo root and runs npm run build -- --cli-only, --workspace=packages/webui, --workspace=packages/web-shell. No @qwen-code/* package is fetched from npm. This is deliberate — @qwen-code/web-shell is unpublished, and @qwen-code/webui on npm is 0.1.0-beta.4 against a monorepo at 0.21.6. Only third-party deps come from the registry.
3. Open questions — resolve before Phase 2
These are genuine unknowns, not rhetorical. Each can change scope materially.
- Browser panes / CDP.
apps/electron/src/main/browser-cdp.tsandbrowser-pane-manager.tsdepend on ElectronBrowserView+ Chrome DevTools Protocol, backing the agent browser tools inpackages/shared/src/agent/browser-tools.ts. Tauri can create multiple and child webviews, but it has no portable equivalent for the ElectronBrowserView+ CDP contract; WebKitGTK exposes no CDP. The current browser-agent contract may therefore be architecturally blocked. Spike before committing. - Web Shell variant seam.
client/customization.tsxcovers only presentation (markdown components, code-block/chart renderers,WelcomeHeader, theme);client/extensions/contains one module. There is no seam for pages, navigation, or settings sections — which is most of what OpenWork adds. Decide whether to build that seam upstream first (strongly preferred) or patch Web Shell source directly. - Session/credential data migration. OpenWork stores product data under
~/.craft-agent/, includingconfig.json,preferences.json, workspace/session data, and an AES-256-GCM credential file at~/.craft-agent/credentials.enc. Its key is derived from the machine UUID; this is not a macOS Keychain re-binding problem. Session formats still differ from daemon transcripts. Decide whether to migrate each data class or explicitly document a one-way break. - Feature freeze policy — the parity target is currently moving. OpenWork is still shipping actively (12 commits in W30; 17 UI features in the last ~30 commits, driven by the autonomous feature loop bootstrapped in #38). If that continues through a multi-month migration, every new Electron feature adds to Phase 4's port backlog and parity recedes as it is approached. Decide one of: (a) freeze feature work on the Electron app at a named commit, (b) require new features to land on the new stack only once Phase 3 completes, or (c) accept dual-maintenance and staff for it explicitly. This must be answered before Phase 4 begins — it is the difference between a finite port and an open-ended one.
4. Phase plan
Phase 0 — De-risk (no migration work)
- Bump
qwenCodeRuntime.version0.15.11→0.21.6; re-vendor; validate ACP. Note: OpenWork is on@agentclientprotocol/sdk ^0.21.0while qwen-code0.21.6CLI is on^0.14.1— verifyPROTOCOL_VERSIONnegotiation rather than assuming. Confirm--acp(not deprecated--experimental-acp) and--channel. Valuable independently of migration. - Browser-pane / CDP spike (see §3).
- Run
bun run server:prodand exercise the app in a plain browser. Whatever breaks is the true Electron-coupling inventory — 95 of 509 renderer files importwindow.electron/electrondirectly. - Build
desktop-shellunmodified on macOS/Windows/Linux; measure real bundle size and memory before promising numbers (runtime/qwen-code/ships Node + CLI, so the Chromium saving is partly offset).
Phase 1 — Repository foundation
- Freeze current tree as
modelstudioai/openwork-legacy(reference during porting — do not disturb the shipping product). - Create new OpenWork repo from
QwenLM/qwen-code. - First commit: neutralize CI. 43 workflows, 13 release/publish.
release.ymlruns on daily cron;release-vscode-companion.ymlfires onrelease: published. Alsorelease-sdk*.yml,build-and-publish-image.yml,sync-release-to-oss.yml. Scheduled workflows are auto-disabled only in true GitHub forks, not plain repos. - Establish branch model:
vendor/upstream(pristine mirror, never edited) →main. -
.gitattributes:package-lock.json merge=ours, regenerate post-merge. - Keep all upstream packages (~18 + 10 channels). Do not prune — deleting files conflicts on every future merge.
- Preserve upstream
LICENSE/NOTICE; append attribution rather than replacing (Apache-2.0 requirement). Clean the stale Craft-era Anthropic Commercial Terms clause, which no longer applies post-eb45067f.
Phase 2 — Shell and branding
- Fork
desktop-shell→ OpenWork shell with own identifier, icons, signing identity, updater keypair, endpoint. - Overlay, do not edit
tauri.conf.json(currentlycom.alibaba.qwen-code, pubkey, and aQwenLM/qwen-codeupdater endpoint). Usetauri build --config tauri.openwork.conf.jsondeep-merge; verify merge semantics for the pinned Tauri version. - Release-time assertion that the shipped updater endpoint and identifier are OpenWork's. Shipping with Qwen's endpoint would push users into a different application — unrecoverable, so it must be enforced mechanically.
- Add
desktop-release.ymlas a new file, not an edit to upstream's.
Phase 3 — Variant seam
- Land page/route registry, settings-section registry, slot-based navigation in Web Shell (upstream where possible).
- Establish
packages/web-shell/client/openwork/andpackages/openwork-*for additive product code. - Start
PATCHES.mdledger recording every intentional divergence in shared files, with rationale.
Phase 4 — Feature port
Port against the parity matrix (§5), one sub-issue per cluster.
Phase 5 — Data migration
- Settings, workspace/session history, and
credentials.encmigration or in-place reuse, validated with upgrade fixtures. - Electron → Tauri update bridge. Upstream's
electron_bridgehandles update delivery only — data migration is separate and ours to build.
Phase 6 — Release
- Platform QA, signing/notarization, staged rollout, rollback plan.
5. Preservation inventory — what must not be lost
Web Shell already covers parts of Skills, MCP, Agents, Extensions/Plugins, Channels, Scheduled Tasks, themes, permissions, voice, raw-Markdown copy, prompt history, and jump-to-latest. The matrix must classify every item below as reuse / adapt / rebuild / consciously drop; do not assume either full absence or full parity.
OpenWork-owned overlay (separate release blocker):
- Branding/distribution:
openworkidentity,com.alibaba.openwork, OpenWork assets/copy/co-author text, themodelstudioai/openworkupdater and homepage, attribution, and an explicit decision on inheritedcraftagents://deep links and theagents.craft.doviewer URL. - Curated skill marketplace: Bailian CLI, Bailian Docs LLM Wiki, and Spark Video Episode, including icons, examples, install sources, and descriptions.
- Repository skills:
.agents/skills/{desktop-brand-builder,desktop-develop,desktop-pet}. Port the brand builder to Tauri; rewrite or retire Electron/CDP-specific instructions; retain the pet skill only if pets remain. - Agent/product behavior: preserve the OpenWork contracts currently assembled in
packages/shared/src/prompts/system.ts—source workflows/auth, skill discovery and precedence, product docs/preferences, permission and plan flow, MCP naming, OAuth/credential operations, rich content blocks,call_llm, browser tools, labels/status/session automation, document tools, and tool display metadata. - Custom agents: no repo-owned OpenWork custom-agent definitions were found. Preserve user-created Qwen agents and add a migration fixture, but do not invent a custom-agent porting scope unless one is identified.
- Skill storage: preserve
~/.qwen/skills,~/.agents/skills, project.qwen/skills/.agents/skills, and the OpenWork workspace-skill layer, with an explicit precedence and migration rule.
Settings surface (16 logical page IDs + searchable navigator): General, Appearance, App, AI, Input, Shortcuts, Memory, MCP Servers, Hooks, Extensions, Permissions, Labels, Messaging, Server, Workspace, and Preferences. QwenSettingsPage backs General/MCP/Hooks/Extensions; it is not an additional route. Hidden and feature-flagged pages still need explicit parity decisions.
Pages: ChatPage, DraftChatPage, PreferencesPage, ShortcutsPage, SkillInfoPage, SourceInfoPage.
Feature domains (per apps/electron/resources/docs/): automations, browser-tools, data-tables, html-preview, image-preview, labels, llm-tool, mermaid, pdf-preview, permissions, skills, sources, statuses, themes, tool-icons.
Doc tools (Python, stack-independent — needs new sidecar packaging): pdf_tool, xlsx_tool, docx_tool, pptx_tool, img_tool, ical_tool, doc_diff, markitdown_cli.
Packages: messaging-gateway + messaging-whatsapp-worker (upstream has 10 channels but no WhatsApp — consider rebuilding against packages/channels/base plugin API and upstreaming), session-tools-core, session-mcp-server, apps/viewer (transcript sharing).
Recent UI work — 17 features shipped in the last ~30 commits, highest regression risk because it is newest and least documented. Against current Web Shell, raw-Markdown copy, jump-to-latest, and prompt history are reusable; thinking selection and reduced motion are partial; the other 12 are absent: starter prompt suggestions (#73), interface zoom ⌘+/⌘-/⌘0 (#69), increase contrast (#67), chat text size (#65), conversation width (#63), live word/char count (#61), shortcuts search (#59), command palette recents (#57), thinking-menu shortcut (#55), prompt-history recall (#53), composer expand/collapse (#49), jump-to-latest (#47), copy-as-Markdown (#71), reduce motion (#51), thinking-level picker (#45), command palette ⌘K (#42), settings navigator search (#40).
Cross-cutting: i18n catalogs + lint:i18n:parity tooling (OpenWork ships 7 locales; Web Shell currently has English and zh-CN), Sentry wiring, voice, network-proxy.ts (Electron session proxy API — no Tauri equivalent), pets registry.
Retired by design: apps/electron main/preload/renderer (~119k LOC), packages/ui (30k), packages/server-core (35k), most of packages/shared's agent loop (95k).
6. Risks
| # | Risk | Severity | Mitigation |
|---|---|---|---|
| 1 | Product-shape divergence. Web Shell is coding-tool shaped (GitBranchIndicator, GitModePopover, BranchPickerPopover, artifacts/, agents/); OpenWork is knowledge-work shaped (Sources, Skills, Labels, Memory, Messaging). Pressure to add if (product === …) into shared code. |
High | Phase 3 seam before Phase 4 |
| 2 | Browser panes may be unportable (§3). | High | Spike in Phase 0 |
| 3 | Data/credential loss on upgrade. credentials.enc key derivation and incompatible session formats need fixture-based migration tests. |
High | Phase 5; treat as release blocker |
| 4 | Updater cross-contamination. | High (low likelihood, unrecoverable impact) | Generated config + release assertion |
| 4b | Inherited upstream defaults. The fork carries qwen-code's endpoint and telemetry plumbing: dashscope.aliyuncs.com / -intl / -us, coding.dashscope.aliyuncs.com/v1, gateway.alibaba-inc.com/dashscope/v1, a CLEARCUT path, and privacy.usageStatisticsEnabled handling. Some ModelStudio/DashScope routes may be intentional for OpenWork; the risk is shipping any endpoint, telemetry, auth, or privacy default without explicit ownership and disclosure. |
High | Full audit in Phase 2; release assertion against an approved endpoint/telemetry allowlist |
| 4c | Moving parity target (§3). Feature work continuing on the Electron app during migration grows the Phase 4 backlog continuously. | High | Freeze policy decided before Phase 4 |
| 5 | Toolchain collision. OpenWork is bun; qwen-code is npm, and prepare-runtime.js hard-requires it (npm_execpath is unavailable. Run through npm.). |
Medium | Decide convergence explicitly |
| 6 | Engine differences. WebView2 / WebKitGTK vs bundled Chromium. Web Shell leans on Shiki, KaTeX, mermaid, echarts, CodeMirror, and shadowDom.ts. |
Medium | Budget Linux QA or scope out of v1 |
| 7 | Release-train coupling. Web Shell is versioned in lockstep with the monorepo (0.21.6). | Medium | Document branching/cherry-pick policy |
| 8 | Merge debt accumulation. Upstream ships ~2 minors/month; conflict cost scales worse than linearly with drift. | Medium | Monthly cadence, never skip |
| 9 | Attribution. Craft-derived code moving between trees carries Apache-2.0 notice obligations; Craft ships TRADEMARK.md. |
Low | Legal pass in Phase 1 |
| 10 | Perf/size expectations set too high. | Low | Measure in Phase 0 before committing publicly |
7. Areas requiring special attention
- Never reformat or reorganize upstream files. A single prettier run across
packages/web-shellwould poison every future merge. - Additive files never conflict. Prefer new files over in-place edits; when editing upstream files, insert a call into our module rather than inlining logic.
- Upstream everything generic. Since the same team maintains both sides, a fix landed upstream permanently leaves our patch set — the single most effective conflict reducer available.
- The 95 renderer files touching
window.electronare the concrete Electron-coupling inventory;web-api.ts(277 lines) and the preload (421 lines) define the contract to re-implement. - Web Shell is 182,393 LOC — larger than OpenWork's current renderer (103,203). Porting to it is the bigger codebase absorbing the smaller.
- Audit every inherited default, not just visible branding. Model/API endpoints, auth flows, telemetry targets, crash reporting, update endpoints, and analytics all arrive preconfigured for Qwen Code. Branding is what you see; endpoints are what ships. Treat this as a checklist with an automated assertion, not a review pass.
- Security posture is inherited too. The daemon binds loopback with a per-launch bearer token, and Web Shell receives that token via URL fragment (
#token=, never sent to the server).tauri.conf.jsonships a restrictive CSP. Any OpenWork addition that widens the network surface — notably the messaging gateways and the WhatsApp worker — must be reviewed against that model rather than assuming it still holds. - Crash reporting needs rebuilding, not porting.
@sentry/electronhas no meaning post-migration; only@sentry/reactcarries over, and main-process crash capture needs a Tauri-native equivalent.
8. Upstream synchronization strategy
QwenLM/qwen-code main
│ fetch (never edit)
▼
vendor/upstream pristine mirror branch
│ merge at release tags only
▼
main OpenWork product branch
git remote add upstream https://github.com/QwenLM/qwen-code
git fetch upstream --tags
git checkout -b vendor/upstream upstream/main
# each sync:
git checkout vendor/upstream && git merge --ff-only v0.22.0
git checkout main && git merge vendor/upstream
npm install && git add package-lock.json
- The pristine branch provides a stable three-way merge base and separates "what changed upstream" from "what we changed" — the question actually asked during conflict resolution.
- Merge release tags, not
main. Bounded, tested diffs. - Monthly cadence. Three minors behind costs far more than three times one minor.
PATCHES.mdledger so that, months later, a resolver knows whether a divergent line was deliberate.
9. Validation & acceptance criteria
Feature parity (blocking):
- Parity matrix complete: every item in §5 marked ported / rebuilt / consciously dropped, with sign-off on each drop.
- All 16 logical settings pages and searchable navigation reachable with equivalent function.
- OpenWork-owned overlay in §5 verified: branding/distribution, all 3 curated marketplace entries, agent/product behavior, repository skills, and skill/agent storage.
- All 17 recent UI features (§5) verified by hand — newest code, weakest coverage.
- Doc tools pass existing smoke suites (
test:doc-tools). - i18n parity lint passes across merged catalogs; no untranslated regressions.
Data integrity (blocking):
- Upgrade from latest Electron build preserves settings, session history, and credentials with zero re-authentication.
- Rollback path validated.
Release safety (blocking):
- Automated assertion: shipped identifier + updater endpoint are OpenWork's, on every platform artifact.
- Automated assertion: only explicitly approved and disclosed OpenWork/ModelStudio endpoints are reachable by default; no unintended upstream telemetry, CLEARCUT, updater, or auth endpoint ships; telemetry and usage-statistics defaults match OpenWork's stated privacy policy.
- Crash reporting verified end-to-end from both the Rust shell and the web layer.
- Signing and notarization green on macOS and Windows.
Platform:
- macOS, Windows verified. Linux verified or explicitly descoped with a written decision.
Performance (measured, not assumed):
- Bundle size and cold-start vs current Electron build, recorded before/after.
Sync process:
- One full practice merge from an upstream release tag completed, conflicts resolved, time recorded as the cadence baseline.
10. Sub-issue breakdown
- Runtime bump
0.15.11→0.21.6+ ACP validation (Phase 0, independent value) - Browser-pane / CDP portability spike (Phase 0, potential blocker)
- Browser-context audit via
server:prod(Phase 0) - Baseline bundle/perf measurement (Phase 0)
- Repo foundation + CI neutralization (Phase 1)
- Shell fork, branding, updater safety (Phase 2)
- Web Shell variant seam (Phase 3 — gates Phase 4)
- Feature port: settings surface (Phase 4)
- Feature port: Sources / Skills / Labels / Memory (Phase 4)
- Feature port: doc tools + sidecar packaging (Phase 4)
- Feature port: messaging + WhatsApp as channel plugin (Phase 4)
- Data migration: settings, sessions, credentials (Phase 5)
- Inherited-defaults audit: endpoints, telemetry, auth, crash reporting (Phase 2, release blocker)
- Platform QA + release pipeline (Phase 6)
- OpenWork-owned overlay: curated marketplace, agent/product behavior, repository skills, and skill/agent storage (Phases 2–5, release blocker)
Guide de contribution
Ouvrir le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Piste de recherche
Commencez par la checklist de la Phase 0 et inspectez apps/electron/src/main/browser-cdp.ts, browser-pane-manager.ts, packages/shared/src/agent/browser-tools.ts et scripts/vendor-qwen-code.ts. Exécutez bun run server:prod et construisez desktop-shell sans modification afin d’établir le couplage avec Electron et la base de référence de la plateforme. Pour considérer le travail comme terminé, il faut résoudre les questions d’architecture listées et diviser la migration en sous-issues ciblées.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- electron, github-actions, node.js, python, rust, typescript
- Domaine
- ai, build-system, desktop, devtools, release
- Type d'issue
- Fonctionnalité
- Difficulté
- 5/5
- Temps estimé
- Plus d'une semaine
- Activité
- Calme
- Clarté
- À clarifier
- Accessibilité débutants
- 15/100