From 3af6d1cd4d9735e7cd2f3e87b0557e75d6916197 Mon Sep 17 00:00:00 2001 From: GCWing Date: Tue, 8 Sep 2026 14:15:08 +0800 Subject: [PATCH 1/2] feat: add standalone data migrator and workspace resources panel --- .github/workflows/cli-package.yml | 1 + .github/workflows/data-migrator-package.yml | 112 + .github/workflows/desktop-package.yml | 1 + Cargo.lock | 48 +- Cargo.toml | 2 + README.md | 6 + README.zh-CN.md | 6 + docs/architecture/product-architecture.md | 16 + .../product-customization-blueprint.md | 21 +- .../rust-build-dependency-boundaries.md | 16 + docs/architecture/theme-token-optimization.md | 7 + docs/interactive-capabilities/README.md | 16 +- .../capabilities.json | 439 +-- .../capabilities/setting.data.migration.md | 63 - .../technical/product-control-open-audit.json | 93 +- .../technical/tauri-command-map.json | 88 +- package.json | 3 + products/fixtures/acme/locales/en-US.json | 1 - products/fixtures/acme/locales/zh-CN.json | 1 - products/fixtures/acme/locales/zh-TW.json | 1 - products/fixtures/acme/product.jsonc | 5 - products/openbitfun/locales/en-US.json | 1 - products/openbitfun/locales/zh-CN.json | 1 - products/openbitfun/locales/zh-TW.json | 1 - products/openbitfun/product.jsonc | 5 - .../schemas/product-definition.schema.json | 3 +- scripts/check-core-boundaries.test.mjs | 7 +- .../cargo-dependency-boundaries.mjs | 2 +- .../core-boundaries/rules/crate-layout.mjs | 2 + .../core-boundaries/rules/feature-rules.mjs | 28 +- scripts/core-boundaries/self-test.mjs | 1 + scripts/data-migrator-release.mjs | 64 + scripts/data-migrator-tauri-build.mjs | 53 +- scripts/data-migrator-tauri-build.test.mjs | 119 +- scripts/desktop-dev-migration.mjs | 65 - scripts/desktop-dev-migration.test.mjs | 60 - scripts/desktop-tauri-build.mjs | 98 +- scripts/desktop-tauri-build.test.mjs | 54 - scripts/dev.cjs | 98 +- scripts/frontend-color-surface-registry.json | 25 + scripts/generate-data-migrator-theme.mjs | 48 + scripts/product-customization/projections.mjs | 3 - .../projections.test.mjs | 5 - scripts/product-customization/resolver.mjs | 14 +- .../product-customization/resolver.test.mjs | 8 - scripts/product-identity-audit.mjs | 5 +- scripts/product-identity-audit.test.mjs | 4 - src/apps/data-migrator/AGENTS.md | 72 +- src/apps/data-migrator/Cargo.toml | 7 +- src/apps/data-migrator/README.md | 130 + src/apps/data-migrator/README.zh-CN.md | 100 + src/apps/data-migrator/RELEASE.md | 16 + .../icons/openbitfun-app-icon.icns | Bin 0 -> 210447 bytes .../icons/openbitfun-app-icon.ico | Bin 0 -> 23274 bytes .../icons/openbitfun-app-icon.png | Bin 0 -> 104027 bytes src/apps/data-migrator/src/app_state.rs | 701 ++-- src/apps/data-migrator/src/commands.rs | 43 +- src/apps/data-migrator/src/lib.rs | 16 +- src/apps/data-migrator/src/locations.rs | 89 + src/apps/data-migrator/src/main.rs | 13 +- src/apps/data-migrator/tauri.conf.json | 9 +- src/apps/data-migrator/ui/app.js | 223 +- .../ui/generated/design-system.css | 1026 +++++ src/apps/data-migrator/ui/index.html | 39 +- src/apps/data-migrator/ui/locales.json | 185 + src/apps/data-migrator/ui/styles.css | 469 ++- src/apps/data-migrator/ui/theme.js | 13 + src/apps/data-migrator/ui/transport.js | 4 + src/apps/desktop/AGENTS.md | 7 +- src/apps/desktop/Cargo.toml | 3 +- src/apps/desktop/src/api/commands.rs | 1 + .../desktop/src/api/legacy_migration_api.rs | 694 ---- src/apps/desktop/src/api/mod.rs | 1 - src/apps/desktop/src/api/terminal_api.rs | 6 + src/apps/desktop/src/lib.rs | 19 - src/crates/assembly/core/Cargo.toml | 37 +- .../infrastructure/app_paths/path_manager.rs | 25 +- .../assembly/core/src/legacy_migration/mod.rs | 123 +- .../core/src/plugin_host_http_routes.rs | 1 + .../core/src/service/config/manager.rs | 128 +- .../assembly/core/src/service/config/types.rs | 3349 +--------------- .../src/service/coordination_persistence.rs | 173 +- src/crates/assembly/core/src/service/mod.rs | 7 +- .../src/service/session_projection_format.rs | 122 +- .../core/src/service/workspace/manager.rs | 72 +- .../core/src/service/workspace/mod.rs | 8 +- .../core/src/service/workspace/persistence.rs | 226 +- .../core/src/service/workspace/provider.rs | 1 + .../core/src/service/workspace/service.rs | 1 + .../core/src/service/workspace/types.rs | 176 +- src/crates/assembly/core/src/util/errors.rs | 13 + .../assembly/core/src/util/types/config.rs | 337 +- src/crates/contracts/AGENTS.md | 1 + .../contracts/config-contracts/AGENTS.md | 13 + .../contracts/config-contracts/Cargo.toml | 23 + .../config-contracts/src/conversion.rs | 336 ++ .../contracts/config-contracts/src/lib.rs | 4 + .../contracts/config-contracts/src/types.rs | 3477 +++++++++++++++++ .../generated/product-control-catalog.json | 439 +-- .../generated/remote-surface-registry.json | 62 +- .../src/remote_surface/table.rs | 5 - src/crates/services/AGENTS.md | 3 +- .../legacy-migration-adapters/AGENTS.md | 15 + .../legacy-migration-adapters/Cargo.toml | 30 + .../src}/agent_coordination.rs | 18 +- .../legacy-migration-adapters/src}/common.rs | 0 .../src}/extensions.rs | 2 +- .../legacy-migration-adapters/src/lib.rs | 137 + .../legacy-migration-adapters/src}/memory.rs | 2 +- .../src}/remote_connect.rs | 2 +- .../src}/remote_ssh.rs | 2 +- .../src}/settings.rs | 6 +- .../src}/workspace_sessions.rs | 44 +- .../services/legacy-migration/AGENTS.md | 20 +- .../services/legacy-migration/src/error.rs | 2 + .../services/legacy-migration/src/handoff.rs | 26 +- .../services/legacy-migration/src/lib.rs | 2 + .../services/legacy-migration/src/paths.rs | 11 +- .../services/legacy-migration/src/tasks.rs | 291 ++ src/crates/services/services-core/AGENTS.md | 13 + src/crates/services/services-core/Cargo.toml | 3 + .../src/coordination_persistence.rs | 177 + src/crates/services/services-core/src/lib.rs | 9 + .../src/session_projection_format.rs | 121 + .../services-core/src/storage_error.rs | 32 + .../services-core/src/workspace_identity.rs | 26 + .../src/workspace_persistence.rs | 230 ++ .../services-core/src/workspace_records.rs | 169 + src/crates/services/terminal/AGENTS.md | 1 + src/crates/services/terminal/src/api.rs | 43 +- .../interactive-capabilities/catalog.json | 137 - src/web-ui/src/app/App.tsx | 8 - .../sections/workspaces/WorkspaceItem.tsx | 4 +- .../src/app/components/panels/FilesPanel.tsx | 78 +- .../panels/TerminalEditModal.appearance.ts | 2 +- .../components/panels/TerminalEditModal.scss | 8 + .../components/panels/TerminalEditModal.tsx | 24 +- .../content-canvas/hooks/useTabLifecycle.ts | 3 + .../generated/interactive-capabilities.json | 439 +-- .../file-viewer/FileViewerNav.appearance.ts | 10 +- .../FileViewerNav.presentation.test.ts | 2 +- .../app/scenes/file-viewer/FileViewerNav.scss | 179 +- .../app/scenes/file-viewer/FileViewerNav.tsx | 117 +- src/web-ui/src/app/scenes/nav-registry.ts | 2 +- .../scenes/settings/settingsRegistry.test.ts | 9 +- .../app/scenes/settings/settingsRegistry.ts | 15 - .../src/app/scenes/settings/settingsTypes.ts | 1 - .../app/scenes/shell/hooks/shellEntryTypes.ts | 17 +- .../shell/hooks/useManualTerminalProfiles.ts | 18 +- .../app/scenes/shell/hooks/useShellEntries.ts | 107 +- .../scenes/shell/hooks/useTerminalSessions.ts | 329 +- .../scenes/terminal/TerminalActionBridge.tsx | 36 +- .../src/app/scenes/terminal/TerminalScene.tsx | 6 +- .../app/scenes/workspace-resources/README.md | 87 + .../WorkspaceResourcePanel.tsx | 207 + .../WorkspaceTerminals.tsx | 105 + .../workspace-resources/resourceMenus.ts | 16 + .../terminalViewLifecycle.test.tsx | 38 + .../workspace-resources/useResourceSplit.ts | 47 + .../workspaceResourceState.ts | 51 + .../workspaceResources.test.ts | 58 + ...legacyMigrationStartupNotification.test.ts | 90 - .../legacyMigrationStartupNotification.ts | 75 - src/web-ui/src/app/stores/sceneStore.ts | 6 +- .../api/generated/productControl.ts | 6 +- .../api/generated/remoteSurface.ts | 7 +- .../service-api/LegacyMigrationAPI.test.ts | 30 - .../api/service-api/LegacyMigrationAPI.ts | 184 - .../LegacyMigrationSettingsPage.scss | 20 - .../LegacyMigrationSettingsPage.tsx | 443 --- .../i18n/presets/namespaceRegistry.ts | 1 - src/web-ui/src/locales/en-US/common.json | 29 + .../src/locales/en-US/panels/terminal.json | 1 + src/web-ui/src/locales/en-US/settings.json | 1 - .../en-US/settings/legacy-migration.json | 152 - src/web-ui/src/locales/zh-CN/common.json | 29 + .../src/locales/zh-CN/panels/terminal.json | 1 + src/web-ui/src/locales/zh-CN/settings.json | 1 - .../zh-CN/settings/legacy-migration.json | 140 - src/web-ui/src/locales/zh-TW/common.json | 29 + .../src/locales/zh-TW/panels/terminal.json | 1 + src/web-ui/src/locales/zh-TW/settings.json | 1 - .../zh-TW/settings/legacy-migration.json | 140 - .../providers/FileExplorerMenuProvider.ts | 18 +- .../services/createManualTerminalSession.ts | 16 +- .../shared/services/openShellSessionTarget.ts | 10 - src/web-ui/src/shared/utils/tabUtils.ts | 1 + .../file-explorer/search/useExplorerSearch.ts | 15 +- .../file-system/components/FileExplorer.tsx | 2 + .../components/VirtualFileTree.tsx | 19 +- .../src/tools/file-system/types/index.ts | 7 + .../file-system/utils/fileTreeReveal.test.ts | 43 + .../tools/file-system/utils/fileTreeReveal.ts | 38 + .../terminal/components/ConnectedTerminal.tsx | 23 +- .../terminal/services/TerminalService.ts | 16 +- .../manualTerminalProfileService.test.ts | 48 + .../services/manualTerminalProfileService.ts | 40 +- .../services/terminalWorkspaceScope.test.ts | 54 + .../services/terminalWorkspaceScope.ts | 57 + .../src/tools/terminal/types/session.ts | 2 + 200 files changed, 9918 insertions(+), 10240 deletions(-) create mode 100644 .github/workflows/data-migrator-package.yml delete mode 100644 docs/interactive-capabilities/capabilities/setting.data.migration.md create mode 100644 scripts/data-migrator-release.mjs delete mode 100644 scripts/desktop-dev-migration.mjs delete mode 100644 scripts/desktop-dev-migration.test.mjs create mode 100644 scripts/generate-data-migrator-theme.mjs create mode 100644 src/apps/data-migrator/README.md create mode 100644 src/apps/data-migrator/README.zh-CN.md create mode 100644 src/apps/data-migrator/RELEASE.md create mode 100644 src/apps/data-migrator/icons/openbitfun-app-icon.icns create mode 100644 src/apps/data-migrator/icons/openbitfun-app-icon.ico create mode 100644 src/apps/data-migrator/icons/openbitfun-app-icon.png create mode 100644 src/apps/data-migrator/src/locations.rs create mode 100644 src/apps/data-migrator/ui/generated/design-system.css create mode 100644 src/apps/data-migrator/ui/locales.json create mode 100644 src/apps/data-migrator/ui/theme.js create mode 100644 src/apps/data-migrator/ui/transport.js delete mode 100644 src/apps/desktop/src/api/legacy_migration_api.rs create mode 100644 src/crates/contracts/config-contracts/AGENTS.md create mode 100644 src/crates/contracts/config-contracts/Cargo.toml create mode 100644 src/crates/contracts/config-contracts/src/conversion.rs create mode 100644 src/crates/contracts/config-contracts/src/lib.rs create mode 100644 src/crates/contracts/config-contracts/src/types.rs create mode 100644 src/crates/services/legacy-migration-adapters/AGENTS.md create mode 100644 src/crates/services/legacy-migration-adapters/Cargo.toml rename src/crates/{assembly/core/src/legacy_migration => services/legacy-migration-adapters/src}/agent_coordination.rs (99%) rename src/crates/{assembly/core/src/legacy_migration => services/legacy-migration-adapters/src}/common.rs (100%) rename src/crates/{assembly/core/src/legacy_migration => services/legacy-migration-adapters/src}/extensions.rs (99%) create mode 100644 src/crates/services/legacy-migration-adapters/src/lib.rs rename src/crates/{assembly/core/src/legacy_migration => services/legacy-migration-adapters/src}/memory.rs (99%) rename src/crates/{assembly/core/src/legacy_migration => services/legacy-migration-adapters/src}/remote_connect.rs (99%) rename src/crates/{assembly/core/src/legacy_migration => services/legacy-migration-adapters/src}/remote_ssh.rs (99%) rename src/crates/{assembly/core/src/legacy_migration => services/legacy-migration-adapters/src}/settings.rs (99%) rename src/crates/{assembly/core/src/legacy_migration => services/legacy-migration-adapters/src}/workspace_sessions.rs (98%) create mode 100644 src/crates/services/legacy-migration/src/tasks.rs create mode 100644 src/crates/services/services-core/src/coordination_persistence.rs create mode 100644 src/crates/services/services-core/src/session_projection_format.rs create mode 100644 src/crates/services/services-core/src/storage_error.rs create mode 100644 src/crates/services/services-core/src/workspace_persistence.rs create mode 100644 src/crates/services/services-core/src/workspace_records.rs create mode 100644 src/web-ui/src/app/scenes/workspace-resources/README.md create mode 100644 src/web-ui/src/app/scenes/workspace-resources/WorkspaceResourcePanel.tsx create mode 100644 src/web-ui/src/app/scenes/workspace-resources/WorkspaceTerminals.tsx create mode 100644 src/web-ui/src/app/scenes/workspace-resources/resourceMenus.ts create mode 100644 src/web-ui/src/app/scenes/workspace-resources/terminalViewLifecycle.test.tsx create mode 100644 src/web-ui/src/app/scenes/workspace-resources/useResourceSplit.ts create mode 100644 src/web-ui/src/app/scenes/workspace-resources/workspaceResourceState.ts create mode 100644 src/web-ui/src/app/scenes/workspace-resources/workspaceResources.test.ts delete mode 100644 src/web-ui/src/app/startup/legacyMigrationStartupNotification.test.ts delete mode 100644 src/web-ui/src/app/startup/legacyMigrationStartupNotification.ts delete mode 100644 src/web-ui/src/infrastructure/api/service-api/LegacyMigrationAPI.test.ts delete mode 100644 src/web-ui/src/infrastructure/api/service-api/LegacyMigrationAPI.ts delete mode 100644 src/web-ui/src/infrastructure/config/components/LegacyMigrationSettingsPage.scss delete mode 100644 src/web-ui/src/infrastructure/config/components/LegacyMigrationSettingsPage.tsx delete mode 100644 src/web-ui/src/locales/en-US/settings/legacy-migration.json delete mode 100644 src/web-ui/src/locales/zh-CN/settings/legacy-migration.json delete mode 100644 src/web-ui/src/locales/zh-TW/settings/legacy-migration.json create mode 100644 src/web-ui/src/tools/file-system/utils/fileTreeReveal.test.ts create mode 100644 src/web-ui/src/tools/file-system/utils/fileTreeReveal.ts create mode 100644 src/web-ui/src/tools/terminal/services/manualTerminalProfileService.test.ts create mode 100644 src/web-ui/src/tools/terminal/services/terminalWorkspaceScope.test.ts create mode 100644 src/web-ui/src/tools/terminal/services/terminalWorkspaceScope.ts diff --git a/.github/workflows/cli-package.yml b/.github/workflows/cli-package.yml index c662c80ba1..5494a1ce18 100644 --- a/.github/workflows/cli-package.yml +++ b/.github/workflows/cli-package.yml @@ -26,6 +26,7 @@ concurrency: jobs: # ── Resolve version info (mirrors desktop-package.yml) ───────────── prepare: + if: github.event_name != 'release' || !startsWith(github.event.release.tag_name, 'data-migrator-v') name: Prepare runs-on: ubuntu-latest outputs: diff --git a/.github/workflows/data-migrator-package.yml b/.github/workflows/data-migrator-package.yml new file mode 100644 index 0000000000..be681e5930 --- /dev/null +++ b/.github/workflows/data-migrator-package.yml @@ -0,0 +1,112 @@ +name: Data Migrator Package + +on: + push: + tags: ['data-migrator-v*'] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: data-migrator-package-${{ github.ref }} + cancel-in-progress: false + +jobs: + build: + name: Migrator (${{ matrix.platform }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + include: + - platform: windows-x64 + os: windows-latest + target: x86_64-pc-windows-msvc + bundles: --no-bundle + - platform: macos-arm64 + os: macos-15 + target: aarch64-apple-darwin + bundles: --bundles dmg + - platform: macos-x64 + os: macos-15-intel + target: x86_64-apple-darwin + bundles: --bundles dmg + - platform: linux-x64 + os: ubuntu-22.04 + target: x86_64-unknown-linux-gnu + bundles: --bundles appimage + steps: + - uses: actions/checkout@v5 + - uses: pnpm/action-setup@v5 + - uses: actions/setup-node@v5 + with: + node-version: '22.18.0' + cache: pnpm + - uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + - uses: swatinem/rust-cache@v2 + with: + shared-key: data-migrator-${{ matrix.platform }} + cache-bin: false + - name: Install Linux WebView build dependencies + if: runner.os == 'Linux' + run: sudo apt-get update && sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf + - run: pnpm install --frozen-lockfile --ignore-scripts + - name: Verify independent version and packaging contract + run: | + node scripts/data-migrator-release.mjs --check-version + node --test scripts/data-migrator-tauri-build.test.mjs + - name: Check offline data compatibility + run: cargo test --locked -p openbitfun-data-migrator -p openbitfun-legacy-migration-adapters -p openbitfun-legacy-migration -p openbitfun-config-contracts --lib --test migration_engine_contracts + - name: Build independent package + run: pnpm run data-migrator:build -- --target ${{ matrix.target }} ${{ matrix.bundles }} + - name: Stage package and checksum + run: node scripts/data-migrator-release.mjs ${{ matrix.platform }} + - uses: actions/upload-artifact@v6 + with: + name: data-migrator-${{ matrix.platform }} + path: target/data-migrator-release/${{ matrix.platform }}/* + if-no-files-found: error + + draft-release: + if: startsWith(github.ref, 'refs/tags/data-migrator-v') + needs: build + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 + with: + node-version: '22.18.0' + - uses: actions/download-artifact@v7 + with: + pattern: data-migrator-* + path: migrator-release-assets + merge-multiple: true + - name: Sign and verify with the independent migrator release key + shell: bash + env: + OPENBITFUN_SIGNING_KEY: ${{ secrets.DATA_MIGRATOR_SIGNING_PRIVATE_KEY }} + OPENBITFUN_SIGNING_PASSWORD: ${{ secrets.DATA_MIGRATOR_SIGNING_PRIVATE_KEY_PASSWORD }} + OPENBITFUN_SIGNING_PUBKEY: ${{ secrets.DATA_MIGRATOR_SIGNING_PUBKEY }} + run: | + set -euo pipefail + test -n "$OPENBITFUN_SIGNING_KEY" + test -n "$OPENBITFUN_SIGNING_PUBKEY" + cat migrator-release-assets/*.sha256 | sort -k2 > migrator-release-assets/SHA256SUMS + (cd migrator-release-assets && sha256sum --check SHA256SUMS) + bash scripts/sign-release-assets.sh migrator-release-assets/* + node scripts/write-minisign-public-key.mjs --out migrator-release-assets/data-migrator.minisign.pub + - name: Create a separately reviewed release + uses: softprops/action-gh-release@v3 + with: + tag_name: ${{ github.ref_name }} + name: OpenBitFun Data Migrator ${{ github.ref_name }} + draft: true + make_latest: false + files: migrator-release-assets/* + body_path: src/apps/data-migrator/RELEASE.md diff --git a/.github/workflows/desktop-package.yml b/.github/workflows/desktop-package.yml index b5f2480557..44e90b0601 100644 --- a/.github/workflows/desktop-package.yml +++ b/.github/workflows/desktop-package.yml @@ -44,6 +44,7 @@ concurrency: jobs: # ── Resolve version info ─────────────────────────────────────────── prepare: + if: github.event_name != 'release' || !startsWith(github.event.release.tag_name, 'data-migrator-v') name: Prepare runs-on: ubuntu-latest outputs: diff --git a/Cargo.lock b/Cargo.lock index 7d9fa167ab..5b599783f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5863,6 +5863,21 @@ dependencies = [ "url", ] +[[package]] +name = "openbitfun-config-contracts" +version = "1.0.0" +dependencies = [ + "chrono", + "hex", + "log", + "openbitfun-core-types", + "openbitfun-product-domains", + "serde", + "serde_json", + "sha2", + "ts-rs", +] + [[package]] name = "openbitfun-core" version = "1.0.0" @@ -5897,11 +5912,12 @@ dependencies = [ "openbitfun-ai-adapters", "openbitfun-claude-code-adapter", "openbitfun-codex-adapter", + "openbitfun-config-contracts", "openbitfun-core-types", "openbitfun-dsh-adapter", "openbitfun-events", "openbitfun-external-sources", - "openbitfun-legacy-migration", + "openbitfun-legacy-migration-adapters", "openbitfun-opencode-adapter", "openbitfun-opencode-plugin-host", "openbitfun-plugin-runtime-client", @@ -5945,12 +5961,11 @@ dependencies = [ [[package]] name = "openbitfun-data-migrator" -version = "1.0.0" +version = "0.1.0" dependencies = [ - "openbitfun-core", "openbitfun-core-types", "openbitfun-legacy-migration", - "openbitfun-product-capabilities", + "openbitfun-legacy-migration-adapters", "openbitfun-product-domains", "serde", "serde_json", @@ -6003,7 +6018,6 @@ dependencies = [ "openbitfun-core", "openbitfun-core-types", "openbitfun-events", - "openbitfun-legacy-migration", "openbitfun-product-domains", "openbitfun-relay-service", "openbitfun-runtime-ports", @@ -6111,6 +6125,30 @@ dependencies = [ "windows 0.61.3", ] +[[package]] +name = "openbitfun-legacy-migration-adapters" +version = "1.0.0" +dependencies = [ + "anyhow", + "chrono", + "dunce", + "hex", + "openbitfun-agent-runtime", + "openbitfun-config-contracts", + "openbitfun-core-types", + "openbitfun-legacy-migration", + "openbitfun-product-domains", + "openbitfun-services-core", + "openbitfun-services-integrations", + "rusqlite", + "serde", + "serde_json", + "sha2", + "tempfile", + "tokio", + "uuid", +] + [[package]] name = "openbitfun-miniapp-market-server" version = "1.0.0" diff --git a/Cargo.toml b/Cargo.toml index b4f13d7fe5..7460f7acf5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ members = [ "src/crates/services/services-core", "src/crates/services/services-integrations", "src/crates/services/legacy-migration", + "src/crates/services/legacy-migration-adapters", "src/crates/services/miniapp-market-service", "src/crates/services/skin-market-service", "src/crates/services/relay-service", @@ -46,6 +47,7 @@ members = [ "src/crates/execution/tool-provider-groups", "src/crates/execution/tool-execution", "src/crates/contracts/core-types", + "src/crates/contracts/config-contracts", "src/crates/contracts/events", "src/crates/contracts/runtime-ports", ] diff --git a/README.md b/README.md index cbd9d9d93b..524d80a94d 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,12 @@ OpenBitFun is still evolving. Here are three directions we want to explore: Getting there means making long-running tasks more reliable, the runtime more efficient, and the desktop experience smoother. +## Optional legacy data migration + +Data migration is optional and uses a **separately downloaded OpenBitFun Data +Migrator**. It runs independently and is not bundled with or launched by the main +application. See the [download, compatibility and recovery guide](src/apps/data-migrator/README.md). + ## Build with it. Help shape it. **Star OpenBitFun to follow along. Share what you build, offer feedback, or contribute code to help shape what comes next.** diff --git a/README.zh-CN.md b/README.zh-CN.md index af66b1db9e..7c500d8543 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -105,6 +105,12 @@ OpenBitFun 仍在演进,我们希望继续探索三个方向: 更可靠的长任务、更高效的 Runtime、更从容的桌面体验,是这些探索共同的基础。 +## 按需迁移旧版数据 + +数据迁移是可选操作,请按需**单独下载 OpenBitFun 数据迁移器**。工具独立运行, +不随主应用打包,也不会由主应用自动启动。下载、兼容范围、操作步骤与中断恢复见 +[迁移器使用说明](src/apps/data-migrator/README.zh-CN.md)。 + ## 用它创造,也一起创造它 **欢迎用 Star 关注 OpenBitFun,用作品、反馈和代码参与它的未来。** diff --git a/docs/architecture/product-architecture.md b/docs/architecture/product-architecture.md index b4bbea2ad4..b8a0bd1be3 100644 --- a/docs/architecture/product-architecture.md +++ b/docs/architecture/product-architecture.md @@ -895,3 +895,19 @@ Shared Agent Runtime 是第一方多实例的目标部署,不是上表新增 启停顺序和失败回滚。这项宿主接入不构成 CLI、Server、ACP 或 HarmonyOS 本地产品支持。 - HarmonyOS PC 的完整目标同时包含本地 CLI/TUI 与 GUI,当前均不能标记可用;两种宿主分别验收,具体支持证据和禁止替代项以平台规约及各自专题为准。 - 文档、边界脚本和 focused 测试能说明本次变更保护了哪个稳定接口边界,或删除/降级了哪个过宽接口。 + +## 独立数据迁移工具的依赖边界 + +Data Migrator 是独立发布的本地离线工具,不依赖 Core、Product Assembly、Desktop 或 Web UI。 +主应用不检测、启动或捆绑迁移器。两者在同一源码工作区复用稳定的数据格式与存储实现: + +- contracts/config-contracts:配置 DTO、默认值、版本校验及到共享模型 DTO 的纯转换;Core 原路径保留转发,ConfigProvider 仍在 Core。 +- services-core 的 workspace-persistence、coordination-store、session-event-format:工作区记录、注册表校验、SQLite 物理 schema 和会话日志格式。 +- services/legacy-migration-adapters:旧版读取、转换、引用修复;只调用共享存储 owner。 +- services/legacy-migration:快照、锁、暂存、备份、原子写入、日志恢复和无时效交接依赖的任务存储。 + +本次只移动数据/存储 owner,不移动 WorkspaceManager、会话生命周期、权限、事件或远程执行。 +WorkspaceInfo/WorkspaceIdentity 的运行操作由 Core 的 runtime extension traits 保留,稳定记录无需导入这些能力。 +原 Core 存储入口保留错误映射;可选 legacy-migration facade 保留旧导入路径,但不再由 product-full 启用。 +远程四种场景不提供迁移工具的执行入口;仅转换本机保存的连接记录,不连接远端。 +使用与发行契约以 [独立迁移器说明](../../src/apps/data-migrator/README.zh-CN.md) 为准。 diff --git a/docs/architecture/product-customization-blueprint.md b/docs/architecture/product-customization-blueprint.md index 22ff0d80bd..4e864d4a26 100644 --- a/docs/architecture/product-customization-blueprint.md +++ b/docs/architecture/product-customization-blueprint.md @@ -17,15 +17,13 @@ C0a 实现一个构建期 JSONC 产品定义、严格解析器和确定性解析 当前真实消费者只有: - Desktop build adapter:从解析结果覆盖 Tauri `productName`、`mainBinaryName` 与 bundle identifier; -- Data Migrator build adapter:从解析结果覆盖独立 Tauri 身份,并把 Desktop 与 Migrator 的 sibling binary name - 编译进交接边界; - CLI dev/build wrapper:从同一解析结果设置命令名、隔离定制构建缓存,并按成员 `binaryName` 暂存构建产物; -- First-party Rust artifacts:Desktop/Data Migrator/CLI build adapter 通过编译期环境注入 `productId`、 +- First-party Rust artifacts:Desktop/CLI build adapter 通过编译期环境注入 `productId`、 `dataNamespace` 与由其派生的隐藏目录名,`openbitfun-core-types::product_identity` 作为最小事实 owner,供数据路径、Runtime ownership、Remote Connect 与 Detached Dispatch 复用; `product:check` / `product:explain` 只是构建作者的校验与解释工具,不计作产品字段的生产消费者。C0a 不生成无人读取的 -通用产品 manifest 或 locale projection;三个 build adapter 直接消费同一次内存解析结果,Rust consumer +通用产品 manifest 或 locale projection;两个 build adapter 直接消费同一次内存解析结果,Rust consumer 只读取随对应产品 artifact 编译进去的不可变事实,不在运行时重新选产品。 产品定义 v1 仅包含已被这些消费者读取的字段,未知字段一律拒绝。localized 名称独立于技术 ID,并按共享 locale contract @@ -37,7 +35,7 @@ GUI/TUI 布局、插件/内置扩展选择、Installer/Store target、更新与 ### 0.1 当前定义与解析契约 -产品定义描述一个 family,其中 Desktop、Data Migrator 与 CLI 是分别命名、分别消费的成员;Installer 与 Store 是可能的 Desktop +产品定义描述一个 family,其中 Desktop 与 CLI 是分别命名、分别消费的成员;Installer 与 Store 是可能的 Desktop 交付目标,不是独立成员,当前也没有对应实现。schema v1 只接受以下已消费字段: ```jsonc @@ -51,11 +49,6 @@ GUI/TUI 布局、插件/内置扩展选择、Installer/Store target、更新与 "binaryName": "acme-desktop", "bundleId": "com.acme.desktop" }, - "dataMigrator": { - "displayNameKey": "product.dataMigrator.name", - "binaryName": "acme-data-migrator", - "bundleId": "com.acme.data-migrator" - }, "cli": { "displayNameKey": "product.cli.name", "binaryName": "acme" @@ -65,7 +58,7 @@ GUI/TUI 布局、插件/内置扩展选择、Installer/Store target、更新与 ``` 解析器先校验完整 family、双方 locale key、owned path 与技术 ID,再选择命令对应成员;digest-bearing `assembly` -只携带 schema/source digest、成员、display-name key、binary/bundle identity、交接所需 sibling binary names、 +只携带 schema/source digest、成员、display-name key、binary/bundle identity、 locale contract facts 与 assembly digest。 构建 adapter 所需的源路径、localized 名称、输出目录和 default-product 标记保留在外围 build context,不扩展成通用 manifest。相同输入必须产生相同摘要;非默认产品使用 digest-scoped Cargo target 目录,避免复用其他产品的编译期身份。 @@ -79,9 +72,13 @@ i18n locale 集合和 key parity。 - 只修改默认产品定义或资源引用时运行 `pnpm run product:check`;非默认定义运行 `pnpm run product:check -- --product-config `,确保校验实际改动的产品; -- 修改 schema、resolver 或 Desktop/Data Migrator/CLI build adapter 行为时,再运行 `pnpm run product:test`; +- 修改 schema、resolver 或 Desktop/CLI build adapter 行为时,再运行 `pnpm run product:test`; - 打包和平台矩阵只在变更触及对应交付路径时运行,不作为产品定义的默认本地预检。 +Data Migrator 已从产品 family 与 sibling binary 投影中移除,使用自己的版本、Tauri 身份、构建与发布工作流。 +它固定面向 OpenBitFun 数据格式;工具身份与目标数据身份分开。旧定制产品定义应移除 `members.dataMigrator`。 +详见 [迁移器 README](../../src/apps/data-migrator/README.zh-CN.md)。 + ## 1. 设计结论 产品定制只需要四类对象: diff --git a/docs/architecture/rust-build-dependency-boundaries.md b/docs/architecture/rust-build-dependency-boundaries.md index c0a7ddc46c..1873ee621a 100644 --- a/docs/architecture/rust-build-dependency-boundaries.md +++ b/docs/architecture/rust-build-dependency-boundaries.md @@ -246,3 +246,19 @@ cargo check -p --timings 当前硬边界由 `scripts/check-core-boundaries.mjs` 统一执行。不要为同一 Cargo 架构事实增加第二个 checker;新增规则先证明当前树满足、fixture 能捕获回归,并保持错误消息可直接定位到 owner manifest。 检查器必须保持工作树只读;读取独立 manifest 的声明事实时不得生成新的 lockfile、target artifact 或格式化改动。 + +## 独立数据迁移工具的依赖边界 + +Data Migrator 是独立发布的本地离线工具,不依赖 Core、Product Assembly、Desktop 或 Web UI。 +主应用不检测、启动或捆绑迁移器。两者在同一源码工作区复用稳定的数据格式与存储实现: + +- contracts/config-contracts:配置 DTO、默认值、版本校验及到共享模型 DTO 的纯转换;Core 原路径保留转发,ConfigProvider 仍在 Core。 +- services-core 的 workspace-persistence、coordination-store、session-event-format:工作区记录、注册表校验、SQLite 物理 schema 和会话日志格式。 +- services/legacy-migration-adapters:旧版读取、转换、引用修复;只调用共享存储 owner。 +- services/legacy-migration:快照、锁、暂存、备份、原子写入、日志恢复和无时效交接依赖的任务存储。 + +本次只移动数据/存储 owner,不移动 WorkspaceManager、会话生命周期、权限、事件或远程执行。 +WorkspaceInfo/WorkspaceIdentity 的运行操作由 Core 的 runtime extension traits 保留,稳定记录无需导入这些能力。 +原 Core 存储入口保留错误映射;可选 legacy-migration facade 保留旧导入路径,但不再由 product-full 启用。 +远程四种场景不提供迁移工具的执行入口;仅转换本机保存的连接记录,不连接远端。 +使用与发行契约以 [独立迁移器说明](../../src/apps/data-migrator/README.zh-CN.md) 为准。 diff --git a/docs/architecture/theme-token-optimization.md b/docs/architecture/theme-token-optimization.md index 2008504d70..fb00602c13 100644 --- a/docs/architecture/theme-token-optimization.md +++ b/docs/architecture/theme-token-optimization.md @@ -200,6 +200,13 @@ Mobile Web 直接消费 `@openbitfun/theme-openbitfun`。`ThemeProvider` 与首 ### Desktop bootstrap 与 Native Mobile 预览 +Data Migrator 的独立静态界面直接消费设计系统公开的字体、间距、控件和语义颜色 Token。 +`generate-data-migrator-theme.mjs` 构建设计系统的 Token/主题包,并将公开 CSS 入口及其依赖打包为 +`src/apps/data-migrator/ui/generated/design-system.css`,随迁移程序离线交付。原生控件只使用 canonical +Token;`theme.js` 在首屏绘制前选择系统浅色/深色和高对比模式,并监听系统设置变化。 +仅独立迁移器打包刷新该资源,开发者也可显式运行生成命令;Desktop 开发和打包不依赖迁移器。 +统一颜色审计同时检查源码和生成物漂移。 + Desktop 的更新确认页和启动页只消费 `src/apps/desktop/src/generated/bootstrap_theme.css` 发布的 canonical `--openbitfun-*`,不得内联另一套启动色。该 CSS 和两个 Appearance manifest 一起由 `generate-startup-appearance-bootstrap.mjs` 从正式主题/Appearance 源生成;统一颜色审计执行 `--check`, diff --git a/docs/interactive-capabilities/README.md b/docs/interactive-capabilities/README.md index 3bcdd2c880..09eac55a30 100644 --- a/docs/interactive-capabilities/README.md +++ b/docs/interactive-capabilities/README.md @@ -1,9 +1,9 @@ # OpenBitFun 功能与设置目录 / OpenBitFun Features & Settings -OpenBitFun Playbook 当前包含 **22 个功能**和 **22 个设置页**,共 **44 个**用户可理解的条目、**326 项**有源码证据的子能力。每个条目有独立 Markdown,并直接服务于说明书网站、OpenBitFun 全局搜索和 `OpenBitFunControl` Agent 工具。 +OpenBitFun Playbook 当前包含 **22 个功能**和 **21 个设置页**,共 **43 个**用户可理解的条目、**321 项**有源码证据的子能力。每个条目有独立 Markdown,并直接服务于说明书网站、OpenBitFun 全局搜索和 `OpenBitFunControl` Agent 工具。 -OpenBitFun Playbook currently contains **22 features**, **22 settings pages**, and **326** source-backed sub-capabilities across **44** user-facing entries. Every entry has its own Markdown page and directly powers the website, in-app global search, and the `OpenBitFunControl` agent tool. +OpenBitFun Playbook currently contains **22 features**, **21 settings pages**, and **321** source-backed sub-capabilities across **43** user-facing entries. Every entry has its own Markdown page and directly powers the website, in-app global search, and the `OpenBitFunControl` agent tool. ## 唯一事实源 / Single source of truth @@ -27,20 +27,20 @@ OpenBitFun Playbook currently contains **22 features**, **22 settings pages**, a - Generated per-item interaction audit: `docs/interactive-capabilities/technical/product-control-open-audit.json` - Generated low-level audit map: `docs/interactive-capabilities/technical/tauri-command-map.json` -说明书、网站、搜索和 Agent 只看“功能 + 设置 + 子能力”。每项子能力都必须引用已注册 Tauri Command 或可解析的源码标记;这些证据不会进入公开目录。当前 **671** 个 Tauri 命令只用于实现覆盖审计。产品 UI 交互源码会在生成和检查时扫描并校验,但不会保存成随普通 UI 改动频繁变化的版本化快照。 +说明书、网站、搜索和 Agent 只看“功能 + 设置 + 子能力”。每项子能力都必须引用已注册 Tauri Command 或可解析的源码标记;这些证据不会进入公开目录。当前 **666** 个 Tauri 命令只用于实现覆盖审计。产品 UI 交互源码会在生成和检查时扫描并校验,但不会保存成随普通 UI 改动频繁变化的版本化快照。 -Docs, website, search, and agents see only features, settings, and documented sub-capabilities. Every sub-capability must reference a registered Tauri command or a resolvable source marker; evidence is stripped from public projections. The **671** Tauri commands remain implementation-audit evidence only. Product UI interaction sources are scanned and validated during generation and checks, but are not stored as a versioned snapshot that churns with ordinary UI changes. +Docs, website, search, and agents see only features, settings, and documented sub-capabilities. Every sub-capability must reference a registered Tauri command or a resolvable source marker; evidence is stripped from public projections. The **666** Tauri commands remain implementation-audit evidence only. Product UI interaction sources are scanned and validated during generation and checks, but are not stored as a versioned snapshot that churns with ordinary UI changes. ## 控制边界 / Control boundary -- 每个子能力都明确标记为直接控制、委托给专用 Agent 工具、需交互打开或不支持;“打开页面”不会再被统计成“Agent 已控制”。当前覆盖:直接 **48**、委托 **61**、需交互 **217**、不支持 **0**。 +- 每个子能力都明确标记为直接控制、委托给专用 Agent 工具、需交互打开或不支持;“打开页面”不会再被统计成“Agent 已控制”。当前覆盖:直接 **48**、委托 **61**、需交互 **212**、不支持 **0**。 - 稳定行为声明为带 JSON 输入契约的 `operations` 或 `options`,并绑定原生产品控制 Provider;Agent 不接触原始 Tauri Command。 -- `OpenBitFunControl list` 和 `search` 都返回带 `nextCursor` 的精简分页结果;目录可持续增长,不靠固定总量上限。完整目录和 326 项子能力都不会写入 system prompt。 +- `OpenBitFunControl list` 和 `search` 都返回带 `nextCursor` 的精简分页结果;目录可持续增长,不靠固定总量上限。完整目录和 321 项子能力都不会写入 system prompt。 - 目录发现与契约读取不依赖 React 或可见窗口。普通配置型 option 统一由 Product Assembly 的共享 ConfigService 执行器读、写并回读,因此 Desktop、CLI 与 Headless 表面走同一份实现;只有宿主原生 operation/provider option 和界面导航按表面注册适配器,缺失时必须明确返回不可用,禁止静默回退本机。只读 Agent 只能发现和读取目录。 -- Every documented item is classified as direct control, delegated Agent control, interactive opening, or unsupported; opening a page is never counted as direct control. Current coverage is **48 direct**, **61 delegated**, **217 interactive**, and **0 unsupported**. +- Every documented item is classified as direct control, delegated Agent control, interactive opening, or unsupported; opening a page is never counted as direct control. Current coverage is **48 direct**, **61 delegated**, **212 interactive**, and **0 unsupported**. - Stable behavior becomes a typed `operation` or `option` with a JSON input contract and a native product-control provider. Agents never receive raw Tauri commands. -- `OpenBitFunControl list` and `search` return compact pages with a `nextCursor`; the catalog can grow without a fixed total-size ceiling. Neither the full catalog nor its 326 documented items enters the system prompt. +- `OpenBitFunControl list` and `search` return compact pages with a `nextCursor`; the catalog can grow without a fixed total-size ceiling. Neither the full catalog nor its 321 documented items enters the system prompt. - Discovery and contract lookup do not depend on React or a visible window. Ordinary config-backed options are read, written, and read back by one Product Assembly ConfigService executor shared by Desktop, CLI, and headless surfaces. Only host-native operations/provider options and presentation routes install surface adapters; missing adapters return explicit unavailability without local fallback. Read-only agents may only discover and inspect entries. ## 防腐化门禁 / Anti-drift gates diff --git a/docs/interactive-capabilities/capabilities.json b/docs/interactive-capabilities/capabilities.json index 653efc45a6..a8c4149de3 100644 --- a/docs/interactive-capabilities/capabilities.json +++ b/docs/interactive-capabilities/capabilities.json @@ -4,7 +4,7 @@ "title": "OpenBitFun Playbook", "origin": "https://playbook.openbitfun.com", "source": "src/shared/interactive-capabilities/catalog.json", - "digest": "c77c16c414ce9fe50929fe249d900489fdceedb8e646aaaca03893aa33d4fbc8", + "digest": "d7a7419ddd673eb733ae8bd33dbb3dcd3b4d1ce0acfc953067a3403fe26d6699", "ownerDigest": "c0e5c187cf62bc6ed06196ce8520b3eb427bf268cf24659b72d2552fb1d99c54", "searchAcceptance": [ { @@ -136,13 +136,13 @@ ], "counts": { "features": 22, - "settings": 22, - "userFacing": 44, - "documentedItems": 326, + "settings": 21, + "userFacing": 43, + "documentedItems": 321, "controlCoverage": { "direct": 48, "delegated": 61, - "interactive": 217, + "interactive": 212, "unsupported": 0 } }, @@ -18765,285 +18765,6 @@ "pageId": "data.archived" } }, - { - "id": "setting.data.migration:query", - "capabilityId": "setting.data.migration", - "itemIds": [ - "scan", - "scope", - "launch", - "report", - "reminder" - ], - "kind": "query", - "risk": "read", - "executionHost": "productHost", - "availability": { - "desktop": { - "available": true - }, - "cli": { - "available": true - }, - "peer": { - "available": true, - "requiredCapabilities": [ - "product_control_v1" - ] - }, - "remoteControl": { - "available": true - }, - "detachedDispatch": { - "available": true - } - }, - "inputSchema": { - "type": "object", - "additionalProperties": false - }, - "outputSchema": { - "type": "object", - "additionalProperties": true - }, - "valueSource": { - "kind": "static" - }, - "presentationTarget": { - "kind": "settings", - "pageId": "data.migration" - } - }, - { - "id": "setting.data.migration:open:scan", - "capabilityId": "setting.data.migration", - "itemIds": [ - "scan" - ], - "kind": "open", - "risk": "ui", - "executionHost": "presentationSurface", - "availability": { - "desktop": { - "available": true - }, - "cli": { - "available": false, - "reason": "This delivery profile has no live presentation surface" - }, - "peer": { - "available": true, - "requiredCapabilities": [ - "product_control_v1", - "product_control_presentation_v1" - ] - }, - "remoteControl": { - "available": true - }, - "detachedDispatch": { - "available": false, - "reason": "This delivery profile has no live presentation surface" - } - }, - "inputSchema": { - "type": "object", - "additionalProperties": false - }, - "outputSchema": { - "type": "object", - "additionalProperties": true - }, - "openReason": "unstructuredInteraction", - "presentationTarget": { - "kind": "settings", - "pageId": "data.migration" - } - }, - { - "id": "setting.data.migration:open:scope", - "capabilityId": "setting.data.migration", - "itemIds": [ - "scope" - ], - "kind": "open", - "risk": "ui", - "executionHost": "presentationSurface", - "availability": { - "desktop": { - "available": true - }, - "cli": { - "available": false, - "reason": "This delivery profile has no live presentation surface" - }, - "peer": { - "available": true, - "requiredCapabilities": [ - "product_control_v1", - "product_control_presentation_v1" - ] - }, - "remoteControl": { - "available": true - }, - "detachedDispatch": { - "available": false, - "reason": "This delivery profile has no live presentation surface" - } - }, - "inputSchema": { - "type": "object", - "additionalProperties": false - }, - "outputSchema": { - "type": "object", - "additionalProperties": true - }, - "openReason": "visualSelection", - "presentationTarget": { - "kind": "settings", - "pageId": "data.migration" - } - }, - { - "id": "setting.data.migration:open:launch", - "capabilityId": "setting.data.migration", - "itemIds": [ - "launch" - ], - "kind": "open", - "risk": "ui", - "executionHost": "presentationSurface", - "availability": { - "desktop": { - "available": true - }, - "cli": { - "available": false, - "reason": "This delivery profile has no live presentation surface" - }, - "peer": { - "available": true, - "requiredCapabilities": [ - "product_control_v1", - "product_control_presentation_v1" - ] - }, - "remoteControl": { - "available": true - }, - "detachedDispatch": { - "available": false, - "reason": "This delivery profile has no live presentation surface" - } - }, - "inputSchema": { - "type": "object", - "additionalProperties": false - }, - "outputSchema": { - "type": "object", - "additionalProperties": true - }, - "openReason": "unstructuredInteraction", - "presentationTarget": { - "kind": "settings", - "pageId": "data.migration" - } - }, - { - "id": "setting.data.migration:open:report", - "capabilityId": "setting.data.migration", - "itemIds": [ - "report" - ], - "kind": "open", - "risk": "ui", - "executionHost": "presentationSurface", - "availability": { - "desktop": { - "available": true - }, - "cli": { - "available": false, - "reason": "This delivery profile has no live presentation surface" - }, - "peer": { - "available": true, - "requiredCapabilities": [ - "product_control_v1", - "product_control_presentation_v1" - ] - }, - "remoteControl": { - "available": true - }, - "detachedDispatch": { - "available": false, - "reason": "This delivery profile has no live presentation surface" - } - }, - "inputSchema": { - "type": "object", - "additionalProperties": false - }, - "outputSchema": { - "type": "object", - "additionalProperties": true - }, - "openReason": "unstructuredInteraction", - "presentationTarget": { - "kind": "settings", - "pageId": "data.migration" - } - }, - { - "id": "setting.data.migration:open:reminder", - "capabilityId": "setting.data.migration", - "itemIds": [ - "reminder" - ], - "kind": "open", - "risk": "ui", - "executionHost": "presentationSurface", - "availability": { - "desktop": { - "available": true - }, - "cli": { - "available": false, - "reason": "This delivery profile has no live presentation surface" - }, - "peer": { - "available": true, - "requiredCapabilities": [ - "product_control_v1", - "product_control_presentation_v1" - ] - }, - "remoteControl": { - "available": true - }, - "detachedDispatch": { - "available": false, - "reason": "This delivery profile has no live presentation surface" - } - }, - "inputSchema": { - "type": "object", - "additionalProperties": false - }, - "outputSchema": { - "type": "object", - "additionalProperties": true - }, - "openReason": "unstructuredInteraction", - "presentationTarget": { - "kind": "settings", - "pageId": "data.migration" - } - }, { "id": "setting.data.diagnostics:query", "capabilityId": "setting.data.diagnostics", @@ -29844,156 +29565,6 @@ ], "docsUrl": "https://playbook.openbitfun.com/capabilities/setting.data.archived/" }, - { - "id": "setting.data.migration", - "kind": "setting", - "categoryId": "data", - "titleZh": "旧版数据迁移", - "titleEn": "Legacy data migration", - "summaryZh": "从本机旧版 安装扫描并导入受支持的数据,查看去敏报告,同时保持旧来源不变。", - "summaryEn": "Scan and import supported data from a local legacy installation, inspect redacted reports, and leave the legacy source unchanged.", - "keywordsZh": [ - "旧版数据迁移", - "旧版数据", - "迁移报告", - "导入旧数据", - "Data Migrator" - ], - "keywordsEn": [ - "legacy data migration", - "legacy data", - "migration report", - "import old data", - "Data Migrator" - ], - "highlightsZh": [ - "只读扫描本机旧版数据", - "按五个高层数据组选择迁移范围", - "通过独立 Data Migrator 导入并查看去敏报告" - ], - "highlightsEn": [ - "Read-only scan of local legacy data", - "Choose migration scope across five high-level data groups", - "Import through the standalone Data Migrator and inspect redacted reports" - ], - "items": [ - { - "id": "scan", - "titleZh": "只读扫描本机旧版数据来源及所选数据组", - "titleEn": "Read-only scan the local legacy source and selected data groups", - "control": { - "kind": "open", - "reasonCode": "unstructuredInteraction", - "reasonZh": "“只读扫描本机旧版数据来源及所选数据组”:扫描依赖当前设备上的旧版数据、实时范围选择和结果状态;Agent 会打开精确入口,并把选择与扫描保留在用户可见界面。", - "reasonEn": "Read-only scan the local legacy source and selected data groups: Scanning depends on legacy data on the current device, live scope selection, and result state; the Agent opens the exact entry and keeps selection and scanning visible to the user." - } - }, - { - "id": "scope", - "titleZh": "选择设置、扩展、会话、记忆和远程连接迁移范围", - "titleEn": "Choose settings, extensions, sessions, memory, and remote-connection migration scope", - "control": { - "kind": "open", - "reasonCode": "visualSelection", - "reasonZh": "迁移范围是影响本机持久数据的五组可见选择;Agent 会打开精确入口,由用户确认所需范围。", - "reasonEn": "Migration scope is a visible five-group selection affecting local persisted data; the Agent opens the exact entry so the user can confirm the intended scope." - } - }, - { - "id": "launch", - "titleZh": "确认关闭影响后启动独立 Data Migrator", - "titleEn": "Launch the standalone Data Migrator after confirming shutdown impact", - "control": { - "kind": "open", - "reasonCode": "unstructuredInteraction", - "reasonZh": "“确认关闭影响后启动独立 Data Migrator”:启动迁移器会停止正在运行的 Agent 和终端任务、关闭 Desktop,并交接到独立本机进程;Agent 只打开入口,确认和启动保留在用户可见界面。", - "reasonEn": "Launch the standalone Data Migrator after confirming shutdown impact: Launching the migrator can stop running agents and terminal tasks, close Desktop, and hand off to a separate local process; the Agent only opens the entry while confirmation and launch remain visible to the user." - } - }, - { - "id": "report", - "titleZh": "查看最近运行结果和各领域去敏状态", - "titleEn": "Inspect the latest run result and redacted per-domain status", - "control": { - "kind": "open", - "reasonCode": "unstructuredInteraction", - "reasonZh": "“查看最近运行结果和各领域去敏状态”:报告取决于本机最近一次迁移运行和各领域实时状态;Agent 会打开精确入口,并把报告查看与失败组重试保留在用户可见界面。", - "reasonEn": "Inspect the latest run result and redacted per-domain status: Reports depend on the most recent local migration run and live per-domain state; the Agent opens the exact entry and keeps report review and failed-group retry visible to the user." - } - }, - { - "id": "reminder", - "titleZh": "恢复已关闭的首次启动迁移提醒", - "titleEn": "Restore the first-start migration reminder after it was disabled", - "control": { - "kind": "open", - "reasonCode": "unstructuredInteraction", - "reasonZh": "“恢复已关闭的首次启动迁移提醒”:提醒偏好与当前本机旧数据来源绑定;Agent 会打开精确入口,由用户在可见界面决定是否恢复提醒。", - "reasonEn": "Restore the first-start migration reminder after it was disabled: The reminder preference is bound to the current local legacy source; the Agent opens the exact entry so the user can decide visibly whether to restore it." - } - } - ], - "stepsZh": [ - "打开设置", - "进入“数据 > 旧版数据迁移”", - "扫描来源、选择范围并在确认关闭影响后启动迁移器" - ], - "stepsEn": [ - "Open Settings", - "Go to Data > Legacy data migration", - "Scan the source, choose scope, and launch the migrator after confirming shutdown impact" - ], - "agentExamplesZh": [ - "打开旧版数据迁移", - "带我查看数据迁移报告" - ], - "agentExamplesEn": [ - "Open legacy data migration", - "Show me the data migration report" - ], - "destination": { - "kind": "settings", - "pageId": "data.migration" - }, - "operations": [], - "options": [], - "searchTerms": [ - "setting.data.migration", - "旧版数据迁移", - "Legacy data migration", - "数据与诊断", - "Data & diagnostics", - "旧版数据", - "迁移报告", - "导入旧数据", - "Data Migrator", - "legacy data migration", - "legacy data", - "migration report", - "import old data", - "只读扫描本机旧版数据", - "按五个高层数据组选择迁移范围", - "通过独立 Data Migrator 导入并查看去敏报告", - "Read-only scan of local legacy data", - "Choose migration scope across five high-level data groups", - "Import through the standalone Data Migrator and inspect redacted reports", - "只读扫描本机旧版数据来源及所选数据组", - "Read-only scan the local legacy source and selected data groups", - "选择设置、扩展、会话、记忆和远程连接迁移范围", - "Choose settings, extensions, sessions, memory, and remote-connection migration scope", - "确认关闭影响后启动独立 Data Migrator", - "Launch the standalone Data Migrator after confirming shutdown impact", - "查看最近运行结果和各领域去敏状态", - "Inspect the latest run result and redacted per-domain status", - "恢复已关闭的首次启动迁移提醒", - "Restore the first-start migration reminder after it was disabled", - "打开旧版数据迁移", - "带我查看数据迁移报告", - "Open legacy data migration", - "Show me the data migration report" - ], - "docsUrl": "https://playbook.openbitfun.com/capabilities/setting.data.migration/" - }, { "id": "setting.data.diagnostics", "kind": "setting", diff --git a/docs/interactive-capabilities/capabilities/setting.data.migration.md b/docs/interactive-capabilities/capabilities/setting.data.migration.md deleted file mode 100644 index a8039fde4c..0000000000 --- a/docs/interactive-capabilities/capabilities/setting.data.migration.md +++ /dev/null @@ -1,63 +0,0 @@ - ---- -id: setting.data.migration -kind: setting -category: data -title_zh: "旧版数据迁移" -title_en: "Legacy data migration" ---- - -# 旧版数据迁移 / Legacy data migration - -> 设置 / Setting - -从本机旧版 安装扫描并导入受支持的数据,查看去敏报告,同时保持旧来源不变。 - -Scan and import supported data from a local legacy installation, inspect redacted reports, and leave the legacy source unchanged. - -## 完整功能清单 / Everything included - -- **Agent 可定位入口,需交互完成 / Agent opens; interaction required** · 只读扫描本机旧版数据来源及所选数据组 - - Read-only scan the local legacy source and selected data groups -- **Agent 可定位入口,需交互完成 / Agent opens; interaction required** · 选择设置、扩展、会话、记忆和远程连接迁移范围 - - Choose settings, extensions, sessions, memory, and remote-connection migration scope -- **Agent 可定位入口,需交互完成 / Agent opens; interaction required** · 确认关闭影响后启动独立 Data Migrator - - Launch the standalone Data Migrator after confirming shutdown impact -- **Agent 可定位入口,需交互完成 / Agent opens; interaction required** · 查看最近运行结果和各领域去敏状态 - - Inspect the latest run result and redacted per-domain status -- **Agent 可定位入口,需交互完成 / Agent opens; interaction required** · 恢复已关闭的首次启动迁移提醒 - - Restore the first-start migration reminder after it was disabled - -## 怎么用 / How to use it - -1. 打开设置 - Open Settings -2. 进入“数据 > 旧版数据迁移” - Go to Data > Legacy data migration -3. 扫描来源、选择范围并在确认关闭影响后启动迁移器 - Scan the source, choose scope, and launch the migrator after confirming shutdown impact - -入口 / Entry: OpenBitFun 设置 - -## Agent 可替你做什么 / What an agent can do for you - -| 操作 / Action | 中文说明 | English description | -| --- | --- | --- | -| 打开对应界面 / Open the UI | 进入 OpenBitFun 中对应的功能界面。 | Open the matching feature in OpenBitFun. | - -## 可配置选项 / Configurable options - -| 选项 / Option | 可用值 / Values | 中文说明 | English description | -| --- | --- | --- | --- | -| 在界面中配置 / Configure in the UI | — | 此页面的设置在对应界面中完成。 | Configure this page in its matching UI. | - -## 可以直接对 Agent 说 / Try saying - -- “打开旧版数据迁移” - - “Open legacy data migration” -- “带我查看数据迁移报告” - - “Show me the data migration report” - -Agent 会先查找相关功能或设置,确认目标后再替你打开、执行或修改。完整能力目录不会预先塞进对话上下文。 - -The agent first finds the relevant feature or setting, confirms the target, and then opens, runs, or changes it for you. The full catalog is never embedded in the conversation context. diff --git a/docs/interactive-capabilities/technical/product-control-open-audit.json b/docs/interactive-capabilities/technical/product-control-open-audit.json index 1839909c20..b2f434a198 100644 --- a/docs/interactive-capabilities/technical/product-control-open-audit.json +++ b/docs/interactive-capabilities/technical/product-control-open-audit.json @@ -1,13 +1,13 @@ { "schemaVersion": 1, "generatedFrom": "src/shared/interactive-capabilities/catalog.json", - "catalogDigest": "c77c16c414ce9fe50929fe249d900489fdceedb8e646aaaca03893aa33d4fbc8", - "count": 217, + "catalogDigest": "d7a7419ddd673eb733ae8bd33dbb3dcd3b4d1ce0acfc953067a3403fe26d6699", + "count": 212, "reasonCounts": { "externalAuth": 4, "secretEntry": 5, - "unstructuredInteraction": 189, - "visualSelection": 19 + "unstructuredInteraction": 185, + "visualSelection": 18 }, "entries": [ { @@ -3749,91 +3749,6 @@ "command:delete_all_archived_sessions" ] }, - { - "capabilityId": "setting.data.migration", - "itemId": "scan", - "titleZh": "只读扫描本机旧版数据来源及所选数据组", - "titleEn": "Read-only scan the local legacy source and selected data groups", - "reasonCode": "unstructuredInteraction", - "reasonZh": "“只读扫描本机旧版数据来源及所选数据组”:扫描依赖当前设备上的旧版数据、实时范围选择和结果状态;Agent 会打开精确入口,并把选择与扫描保留在用户可见界面。", - "reasonEn": "Read-only scan the local legacy source and selected data groups: Scanning depends on legacy data on the current device, live scope selection, and result state; the Agent opens the exact entry and keeps selection and scanning visible to the user.", - "presentationTarget": { - "kind": "settings", - "pageId": "data.migration" - }, - "evidence": [ - "command:get_legacy_migration_status", - "command:scan_legacy_migration", - "source:src/web-ui/src/locales/zh-CN/settings/legacy-migration.json#actions.scan" - ] - }, - { - "capabilityId": "setting.data.migration", - "itemId": "scope", - "titleZh": "选择设置、扩展、会话、记忆和远程连接迁移范围", - "titleEn": "Choose settings, extensions, sessions, memory, and remote-connection migration scope", - "reasonCode": "visualSelection", - "reasonZh": "迁移范围是影响本机持久数据的五组可见选择;Agent 会打开精确入口,由用户确认所需范围。", - "reasonEn": "Migration scope is a visible five-group selection affecting local persisted data; the Agent opens the exact entry so the user can confirm the intended scope.", - "presentationTarget": { - "kind": "settings", - "pageId": "data.migration" - }, - "evidence": [ - "source:src/web-ui/src/locales/zh-CN/settings/legacy-migration.json#sections.scope.title" - ] - }, - { - "capabilityId": "setting.data.migration", - "itemId": "launch", - "titleZh": "确认关闭影响后启动独立 Data Migrator", - "titleEn": "Launch the standalone Data Migrator after confirming shutdown impact", - "reasonCode": "unstructuredInteraction", - "reasonZh": "“确认关闭影响后启动独立 Data Migrator”:启动迁移器会停止正在运行的 Agent 和终端任务、关闭 Desktop,并交接到独立本机进程;Agent 只打开入口,确认和启动保留在用户可见界面。", - "reasonEn": "Launch the standalone Data Migrator after confirming shutdown impact: Launching the migrator can stop running agents and terminal tasks, close Desktop, and hand off to a separate local process; the Agent only opens the entry while confirmation and launch remain visible to the user.", - "presentationTarget": { - "kind": "settings", - "pageId": "data.migration" - }, - "evidence": [ - "command:prepare_legacy_migration", - "source:src/web-ui/src/locales/zh-CN/settings/legacy-migration.json#confirm.title" - ] - }, - { - "capabilityId": "setting.data.migration", - "itemId": "report", - "titleZh": "查看最近运行结果和各领域去敏状态", - "titleEn": "Inspect the latest run result and redacted per-domain status", - "reasonCode": "unstructuredInteraction", - "reasonZh": "“查看最近运行结果和各领域去敏状态”:报告取决于本机最近一次迁移运行和各领域实时状态;Agent 会打开精确入口,并把报告查看与失败组重试保留在用户可见界面。", - "reasonEn": "Inspect the latest run result and redacted per-domain status: Reports depend on the most recent local migration run and live per-domain state; the Agent opens the exact entry and keeps report review and failed-group retry visible to the user.", - "presentationTarget": { - "kind": "settings", - "pageId": "data.migration" - }, - "evidence": [ - "command:get_legacy_migration_report", - "source:src/web-ui/src/locales/zh-CN/settings/legacy-migration.json#sections.report.title" - ] - }, - { - "capabilityId": "setting.data.migration", - "itemId": "reminder", - "titleZh": "恢复已关闭的首次启动迁移提醒", - "titleEn": "Restore the first-start migration reminder after it was disabled", - "reasonCode": "unstructuredInteraction", - "reasonZh": "“恢复已关闭的首次启动迁移提醒”:提醒偏好与当前本机旧数据来源绑定;Agent 会打开精确入口,由用户在可见界面决定是否恢复提醒。", - "reasonEn": "Restore the first-start migration reminder after it was disabled: The reminder preference is bound to the current local legacy source; the Agent opens the exact entry so the user can decide visibly whether to restore it.", - "presentationTarget": { - "kind": "settings", - "pageId": "data.migration" - }, - "evidence": [ - "command:set_legacy_migration_prompt_preference", - "source:src/web-ui/src/locales/zh-CN/settings/legacy-migration.json#actions.restoreReminder" - ] - }, { "capabilityId": "setting.data.diagnostics", "itemId": "log-path", diff --git a/docs/interactive-capabilities/technical/tauri-command-map.json b/docs/interactive-capabilities/technical/tauri-command-map.json index c33322462f..04b6121d30 100644 --- a/docs/interactive-capabilities/technical/tauri-command-map.json +++ b/docs/interactive-capabilities/technical/tauri-command-map.json @@ -1,11 +1,11 @@ { "schemaVersion": 2, "generatedFrom": "src/shared/interactive-capabilities/catalog.json", - "catalogDigest": "c77c16c414ce9fe50929fe249d900489fdceedb8e646aaaca03893aa33d4fbc8", - "commandCount": 671, + "catalogDigest": "d7a7419ddd673eb733ae8bd33dbb3dcd3b4d1ce0acfc953067a3403fe26d6699", + "commandCount": 666, "coverage": { - "commandCount": 671, - "documentedCommandCount": 638, + "commandCount": 666, + "documentedCommandCount": 633, "implementationCommandCount": 33, "implementationDigest": "35539d9c1510287cb47f4a68fe35859b78f93bb06cd66b86e58d878a48d8c509" }, @@ -3230,38 +3230,6 @@ "signature": "fn get_latest_insights() -> Result, String>", "remoteWorkspacePolicy": "LocalOnly" }, - { - "id": "get_legacy_migration_report", - "moduleId": "legacy_migration", - "capabilityId": "setting.data.migration", - "capabilityIds": [ - "setting.data.migration" - ], - "documentedItemIds": [ - "setting.data.migration:report" - ], - "visibility": "documented", - "rustPath": "api::legacy_migration_api::get_legacy_migration_report", - "sourceFile": "src/apps/desktop/src/api/legacy_migration_api.rs", - "signature": "fn get_legacy_migration_report( request: GetLegacyMigrationReportRequest, ) -> Result, LegacyMigrationCommandError>", - "remoteWorkspacePolicy": "LocalOnly" - }, - { - "id": "get_legacy_migration_status", - "moduleId": "legacy_migration", - "capabilityId": "setting.data.migration", - "capabilityIds": [ - "setting.data.migration" - ], - "documentedItemIds": [ - "setting.data.migration:scan" - ], - "visibility": "documented", - "rustPath": "api::legacy_migration_api::get_legacy_migration_status", - "sourceFile": "src/apps/desktop/src/api/legacy_migration_api.rs", - "signature": "fn get_legacy_migration_status( request: EmptyLegacyMigrationRequest, ) -> Result", - "remoteWorkspacePolicy": "LocalOnly" - }, { "id": "get_mcp_prompt", "moduleId": "mcp", @@ -6646,22 +6614,6 @@ "signature": "fn predownload_acp_client_adapter( state: State<'_, AppState>, request: AcpClientIdRequest, ) -> Result<(), String>", "remoteWorkspacePolicy": "LegacyUnaudited" }, - { - "id": "prepare_legacy_migration", - "moduleId": "legacy_migration", - "capabilityId": "setting.data.migration", - "capabilityIds": [ - "setting.data.migration" - ], - "documentedItemIds": [ - "setting.data.migration:launch" - ], - "visibility": "documented", - "rustPath": "api::legacy_migration_api::prepare_legacy_migration", - "sourceFile": "src/apps/desktop/src/api/legacy_migration_api.rs", - "signature": "fn prepare_legacy_migration( app: AppHandle, request: PrepareLegacyMigrationRequest, ) -> Result", - "remoteWorkspacePolicy": "LocalOnly" - }, { "id": "preview_commit_message", "moduleId": "git_agent", @@ -8417,22 +8369,6 @@ "signature": "fn save_web_search_credential( _state: State<'_, AppState>, request: openbitfun_core::service::web_search::SaveWebSearchCredentialRequest, ) -> Result", "remoteWorkspacePolicy": "WorkspaceAgnostic" }, - { - "id": "scan_legacy_migration", - "moduleId": "legacy_migration", - "capabilityId": "setting.data.migration", - "capabilityIds": [ - "setting.data.migration" - ], - "documentedItemIds": [ - "setting.data.migration:scan" - ], - "visibility": "documented", - "rustPath": "api::legacy_migration_api::scan_legacy_migration", - "sourceFile": "src/apps/desktop/src/api/legacy_migration_api.rs", - "signature": "fn scan_legacy_migration( request: ScanLegacyMigrationRequest, ) -> Result", - "remoteWorkspacePolicy": "LocalOnly" - }, { "id": "scan_workspace_info", "moduleId": "commands", @@ -8901,22 +8837,6 @@ "signature": "fn set_global_skill_disabled( request: SetGlobalSkillDisabledRequest, ) -> Result", "remoteWorkspacePolicy": "WorkspaceAgnostic" }, - { - "id": "set_legacy_migration_prompt_preference", - "moduleId": "legacy_migration", - "capabilityId": "setting.data.migration", - "capabilityIds": [ - "setting.data.migration" - ], - "documentedItemIds": [ - "setting.data.migration:reminder" - ], - "visibility": "documented", - "rustPath": "api::legacy_migration_api::set_legacy_migration_prompt_preference", - "sourceFile": "src/apps/desktop/src/api/legacy_migration_api.rs", - "signature": "fn set_legacy_migration_prompt_preference( request: SetLegacyMigrationPromptPreferenceRequest, ) -> Result", - "remoteWorkspacePolicy": "LocalOnly" - }, { "id": "set_macos_edit_menu_mode", "moduleId": "system", diff --git a/package.json b/package.json index 2748bf7658..111c68a395 100644 --- a/package.json +++ b/package.json @@ -115,7 +115,10 @@ "product:check": "node scripts/product-customization/cli.mjs check", "product:explain": "node scripts/product-customization/cli.mjs explain", "product:test": "node --test --test-concurrency=1 scripts/product-customization/*.test.mjs scripts/cli-product.test.mjs scripts/desktop-tauri-build.test.mjs scripts/data-migrator-tauri-build.test.mjs", + "data-migrator:dev": "cargo run -p openbitfun-data-migrator --bin openbitfun-data-migrator", "data-migrator:check": "cargo check -p openbitfun-data-migrator", + "data-migrator:theme:generate": "node scripts/generate-data-migrator-theme.mjs", + "data-migrator:theme:check": "node scripts/generate-data-migrator-theme.mjs --check", "data-migrator:build": "node scripts/data-migrator-tauri-build.mjs", "desktop:build": "node scripts/desktop-tauri-build.mjs", "desktop:build:fast": "node scripts/desktop-tauri-build.mjs --debug --no-bundle", diff --git a/products/fixtures/acme/locales/en-US.json b/products/fixtures/acme/locales/en-US.json index fa286b519c..a8d066b01c 100644 --- a/products/fixtures/acme/locales/en-US.json +++ b/products/fixtures/acme/locales/en-US.json @@ -1,5 +1,4 @@ { "product.cli.name": "Acme CLI", - "product.dataMigrator.name": "Acme Data Migrator", "product.desktop.name": "Acme Desktop" } diff --git a/products/fixtures/acme/locales/zh-CN.json b/products/fixtures/acme/locales/zh-CN.json index ed492ddd53..140f3111ef 100644 --- a/products/fixtures/acme/locales/zh-CN.json +++ b/products/fixtures/acme/locales/zh-CN.json @@ -1,5 +1,4 @@ { "product.cli.name": "Acme 命令行", - "product.dataMigrator.name": "Acme 数据迁移器", "product.desktop.name": "Acme 桌面版" } diff --git a/products/fixtures/acme/locales/zh-TW.json b/products/fixtures/acme/locales/zh-TW.json index a9e7eb4ac9..770e9abd60 100644 --- a/products/fixtures/acme/locales/zh-TW.json +++ b/products/fixtures/acme/locales/zh-TW.json @@ -1,5 +1,4 @@ { "product.cli.name": "Acme 命令列", - "product.dataMigrator.name": "Acme 資料遷移器", "product.desktop.name": "Acme 桌面版" } diff --git a/products/fixtures/acme/product.jsonc b/products/fixtures/acme/product.jsonc index 6b2ab8af27..2af9a0dfa4 100644 --- a/products/fixtures/acme/product.jsonc +++ b/products/fixtures/acme/product.jsonc @@ -10,11 +10,6 @@ "binaryName": "acme-desktop", "bundleId": "com.acme.desktop" }, - "dataMigrator": { - "displayNameKey": "product.dataMigrator.name", - "binaryName": "acme-data-migrator", - "bundleId": "com.acme.data-migrator" - }, "cli": { "displayNameKey": "product.cli.name", "binaryName": "acme" diff --git a/products/openbitfun/locales/en-US.json b/products/openbitfun/locales/en-US.json index e2ef5aa960..92463c4799 100644 --- a/products/openbitfun/locales/en-US.json +++ b/products/openbitfun/locales/en-US.json @@ -1,5 +1,4 @@ { "product.cli.name": "OpenBitFun CLI", - "product.dataMigrator.name": "OpenBitFun Data Migrator", "product.desktop.name": "OpenBitFun" } diff --git a/products/openbitfun/locales/zh-CN.json b/products/openbitfun/locales/zh-CN.json index 223b6b308e..92463c4799 100644 --- a/products/openbitfun/locales/zh-CN.json +++ b/products/openbitfun/locales/zh-CN.json @@ -1,5 +1,4 @@ { "product.cli.name": "OpenBitFun CLI", - "product.dataMigrator.name": "OpenBitFun 数据迁移器", "product.desktop.name": "OpenBitFun" } diff --git a/products/openbitfun/locales/zh-TW.json b/products/openbitfun/locales/zh-TW.json index 61b90a2bfb..92463c4799 100644 --- a/products/openbitfun/locales/zh-TW.json +++ b/products/openbitfun/locales/zh-TW.json @@ -1,5 +1,4 @@ { "product.cli.name": "OpenBitFun CLI", - "product.dataMigrator.name": "OpenBitFun 資料遷移器", "product.desktop.name": "OpenBitFun" } diff --git a/products/openbitfun/product.jsonc b/products/openbitfun/product.jsonc index 29c10f9d8a..b6f199e067 100644 --- a/products/openbitfun/product.jsonc +++ b/products/openbitfun/product.jsonc @@ -10,11 +10,6 @@ "binaryName": "openbitfun-desktop", "bundleId": "com.openbitfun.desktop" }, - "dataMigrator": { - "displayNameKey": "product.dataMigrator.name", - "binaryName": "openbitfun-data-migrator", - "bundleId": "com.openbitfun.data-migrator" - }, "cli": { "displayNameKey": "product.cli.name", "binaryName": "openbitfun" diff --git a/products/schemas/product-definition.schema.json b/products/schemas/product-definition.schema.json index 8ec650671e..a5e530f87e 100644 --- a/products/schemas/product-definition.schema.json +++ b/products/schemas/product-definition.schema.json @@ -14,10 +14,9 @@ "members": { "type": "object", "additionalProperties": false, - "required": ["desktop", "dataMigrator", "cli"], + "required": ["desktop", "cli"], "properties": { "desktop": { "$ref": "#/$defs/desktopMember" }, - "dataMigrator": { "$ref": "#/$defs/desktopMember" }, "cli": { "$ref": "#/$defs/commonMember" } } } diff --git a/scripts/check-core-boundaries.test.mjs b/scripts/check-core-boundaries.test.mjs index bdb2450a10..7057a221e8 100644 --- a/scripts/check-core-boundaries.test.mjs +++ b/scripts/check-core-boundaries.test.mjs @@ -198,7 +198,8 @@ test('Agent Runtime leaf capabilities have one managed feature and source contra 'native-hook-runtime', 'native-hook-settings', ]); - assert.equal(rule.consumers.size, 10); + assert.equal(rule.consumers.size, 11); + assert.ok(rule.consumers.has('openbitfun-legacy-migration-adapters')); assert.ok( guardedEmptyInternalDefaultManifestPaths.includes( 'src/crates/execution/agent-runtime/Cargo.toml', @@ -4156,7 +4157,7 @@ test('Core Tokio capabilities cannot hide behind an unreviewed owner feature', ( ], features: { 'agent-runtime': ['tokio/io-util', 'tokio/macros', 'tokio/rt', 'tokio/time'], - 'legacy-migration': ['tokio/rt'], + 'legacy-migration': [], 'mcp-runtime': ['agent-runtime', 'tokio/rt-multi-thread'], 'browser-control': ['tokio/net', 'tokio/rt', 'tokio/time'], sneaky: ['agent-runtime', 'browser-control'], @@ -4178,7 +4179,7 @@ test('reviewed Tokio aggregates cannot declare runtime capabilities directly', ( dependencies: [{ name: 'tokio', kind: null, optional: false, features: ['fs', 'sync'] }], features: { 'agent-runtime': ['tokio/io-util', 'tokio/macros', 'tokio/rt', 'tokio/time'], - 'legacy-migration': ['tokio/rt'], + 'legacy-migration': [], 'mcp-runtime': ['agent-runtime', 'tokio/rt-multi-thread'], 'browser-control': ['tokio/net', 'tokio/rt', 'tokio/time'], 'product-full': ['agent-runtime', 'tokio/net'], diff --git a/scripts/core-boundaries/cargo-dependency-boundaries.mjs b/scripts/core-boundaries/cargo-dependency-boundaries.mjs index 57e9fdebb0..dd3ca537d7 100644 --- a/scripts/core-boundaries/cargo-dependency-boundaries.mjs +++ b/scripts/core-boundaries/cargo-dependency-boundaries.mjs @@ -174,7 +174,7 @@ const SERVICES_INTEGRATIONS_TOKIO_AGGREGATES = new Set(['product-full']); const SERVICES_CORE_TOKIO_AGGREGATES = new Set(['session-git', 'token-usage-statistics']); const CORE_TOKIO_FEATURES = new Map([ ['agent-runtime', ['io-util', 'macros', 'rt', 'time']], - ['legacy-migration', ['rt']], + ['legacy-migration', []], ['mcp-runtime', ['io-util', 'macros', 'rt', 'rt-multi-thread', 'time']], ['browser-control', ['net', 'rt', 'time']], ]); diff --git a/scripts/core-boundaries/rules/crate-layout.mjs b/scripts/core-boundaries/rules/crate-layout.mjs index 1f550325e3..5642062585 100644 --- a/scripts/core-boundaries/rules/crate-layout.mjs +++ b/scripts/core-boundaries/rules/crate-layout.mjs @@ -2,6 +2,7 @@ // owns where workspace crates live under src/crates. export const crateLayoutRules = [ + { crateName: 'config-contracts', layer: 'contracts', path: 'src/crates/contracts/config-contracts' }, { crateName: 'core-types', layer: 'contracts', path: 'src/crates/contracts/core-types' }, { crateName: 'events', layer: 'contracts', path: 'src/crates/contracts/events' }, { crateName: 'product-domains', layer: 'contracts', path: 'src/crates/contracts/product-domains' }, @@ -23,6 +24,7 @@ export const crateLayoutRules = [ { crateName: 'services-core', layer: 'services', path: 'src/crates/services/services-core' }, { crateName: 'services-integrations', layer: 'services', path: 'src/crates/services/services-integrations' }, + { crateName: 'legacy-migration-adapters', layer: 'services', path: 'src/crates/services/legacy-migration-adapters' }, { crateName: 'legacy-migration', layer: 'services', path: 'src/crates/services/legacy-migration' }, { crateName: 'miniapp-market-service', layer: 'services', path: 'src/crates/services/miniapp-market-service' }, { crateName: 'skin-market-service', layer: 'services', path: 'src/crates/services/skin-market-service' }, diff --git a/scripts/core-boundaries/rules/feature-rules.mjs b/scripts/core-boundaries/rules/feature-rules.mjs index c576e7ead8..0b2c575fff 100644 --- a/scripts/core-boundaries/rules/feature-rules.mjs +++ b/scripts/core-boundaries/rules/feature-rules.mjs @@ -38,6 +38,7 @@ export const optionalDependencyFeatureOwnerRules = [ }, { crateName: 'services-core', + reviewedAggregateFeatures: ['workspace-persistence'], reason: 'services-core optional implementation dependencies must stay behind their exact owner capability', dependencies: [ @@ -47,11 +48,11 @@ export const optionalDependencyFeatureOwnerRules = [ { depName: 'base64', ownerFeatures: ['credential-vault', 'filesystem'] }, { depName: 'openbitfun-core-types', - ownerFeatures: ['filesystem', 'local-storage', 'product-identity'], + ownerFeatures: ['filesystem', 'local-storage', 'product-identity', 'workspace-persistence'], }, - { depName: 'openbitfun-events', ownerFeatures: ['local-storage'] }, - { depName: 'openbitfun-runtime-ports', ownerFeatures: ['permission', 'workspace-runtime'] }, - { depName: 'chrono', ownerFeatures: ['filesystem', 'local-storage'] }, + { depName: 'openbitfun-events', ownerFeatures: ['local-storage', 'session-event-format'] }, + { depName: 'openbitfun-runtime-ports', ownerFeatures: ['permission', 'workspace-runtime', 'workspace-persistence'] }, + { depName: 'chrono', ownerFeatures: ['filesystem', 'local-storage', 'workspace-persistence'] }, { depName: 'chrono-tz', ownerFeatures: ['token-usage-statistics'] }, { depName: 'dunce', ownerFeatures: ['runtime-ownership', 'workspace-identity', 'workspace-runtime'] }, { depName: 'fs2', ownerFeatures: ['credential-vault', 'json-io', 'local-storage', 'runtime-ownership'] }, @@ -70,7 +71,7 @@ export const optionalDependencyFeatureOwnerRules = [ 'workspace-instructions', ], }, - { depName: 'rusqlite', ownerFeatures: ['memory-store', 'permission', 'session-search'] }, + { depName: 'rusqlite', ownerFeatures: ['memory-store', 'permission', 'session-search', 'coordination-store'] }, { depName: 'rustls', ownerFeatures: ['tls-provider'] }, { depName: 'serde_yaml', ownerFeatures: ['markdown', 'workspace-instructions'] }, { depName: 'similar', ownerFeatures: ['diff', 'local-storage'] }, @@ -157,14 +158,14 @@ export const optionalDependencyFeatureOwnerRules = [ depName: 'openbitfun-ai-adapters', ownerFeatures: ['ai-adapter-runtime', 'subscription-auth'], }, - { depName: 'openbitfun-agent-runtime', ownerFeatures: ['agent-runtime', 'legacy-migration'] }, + { depName: 'openbitfun-agent-runtime', ownerFeatures: ['agent-runtime'] }, { depName: 'openbitfun-agent-workflows', ownerFeatures: ['deep-research'] }, { depName: 'openbitfun-agent-stream', ownerFeatures: ['agent-runtime'] }, { depName: 'openbitfun-agent-tools', ownerFeatures: ['agent-runtime', 'local-storage', 'mcp-runtime'] }, { depName: 'openbitfun-claude-code-adapter', ownerFeatures: ['external-sources'] }, { depName: 'openbitfun-codex-adapter', ownerFeatures: ['external-sources'] }, { depName: 'openbitfun-external-sources', ownerFeatures: ['external-sources'] }, - { depName: 'openbitfun-legacy-migration', ownerFeatures: ['legacy-migration'] }, + { depName: 'openbitfun-legacy-migration-adapters', ownerFeatures: ['legacy-migration'] }, { depName: 'openbitfun-opencode-adapter', ownerFeatures: ['external-sources'] }, { depName: 'openbitfun-dsh-adapter', ownerFeatures: ['external-sources'] }, { depName: 'openbitfun-plugin-runtime-client', ownerFeatures: ['plugin-runtime'] }, @@ -175,7 +176,6 @@ export const optionalDependencyFeatureOwnerRules = [ 'agent-runtime', 'canvas-runtime', 'function-agents', - 'legacy-migration', 'plugin-source', 'product-search', 'tools-miniapp', @@ -193,7 +193,6 @@ export const optionalDependencyFeatureOwnerRules = [ 'external-sources', 'file-watch', 'function-agents', - 'legacy-migration', 'git', 'mcp-runtime', 'model-catalog', @@ -239,7 +238,7 @@ export const optionalDependencyFeatureOwnerRules = [ { depName: 'md5', ownerFeatures: ['agent-runtime'] }, { depName: 'reqwest', ownerFeatures: ['mcp-runtime', 'tools-miniapp'] }, { depName: 'regex', ownerFeatures: ['agent-runtime'] }, - { depName: 'rusqlite', ownerFeatures: ['agent-runtime', 'legacy-migration'] }, + { depName: 'rusqlite', ownerFeatures: ['agent-runtime'] }, { depName: 'semver', ownerFeatures: ['tools-miniapp'] }, { depName: 'serde_yaml', ownerFeatures: ['workspace-runtime'] }, { depName: 'similar', ownerFeatures: ['agent-runtime'] }, @@ -498,7 +497,7 @@ export const capabilityContractDependencyRules = [ capabilityForwarder('workspace-runtime', 'runtime-event-port'), capabilityForwarder('workspace-runtime', 'workspace-ports'), ], - ['permission', 'workspace-runtime'], + ['permission', 'workspace-runtime', 'workspace-persistence'], )], ['openbitfun-services-integrations', capabilityConsumer( [capabilityEdge([], { optional: true })], @@ -639,6 +638,7 @@ export const capabilityContractDependencyRules = [ ], }, consumers: new Map([ + ['openbitfun-legacy-migration-adapters', capabilityConsumer([capabilityEdge(['definition-contracts'])])], ['openbitfun-acp', capabilityConsumer( [capabilityEdge([], { optional: true })], [capabilityForwarder('server', 'agent-runtime')], @@ -655,9 +655,8 @@ export const capabilityContractDependencyRules = [ [capabilityEdge([], { optional: true })], [ capabilityForwarder('agent-runtime', 'agent-runtime'), - capabilityForwarder('legacy-migration', 'definition-contracts'), ], - ['agent-runtime', 'legacy-migration'], + ['agent-runtime'], ['external-sources', 'mcp-runtime', 'opencode-plugin-host', 'plugin-runtime', 'product-search', 'product-full', 'remote-connect', 'tools-mcp'], )], ['openbitfun-desktop', capabilityConsumer([ @@ -883,6 +882,8 @@ export const coreClosedFeatureProfileRules = [ // Complete ExecCommand constraint syntax facts live in the tool owner. 'tool-runtime/shell-analysis', 'openbitfun-services-core/memory-store', + 'openbitfun-services-core/coordination-store', + 'openbitfun-services-core/session-event-format', 'openbitfun-services-core/permission', 'openbitfun-services-core/runtime-ownership', 'openbitfun-services-core/session-git', @@ -1633,6 +1634,7 @@ export const coreClosedFeatureProfileRules = [ 'openbitfun-services-core/workspace-identity', 'openbitfun-services-core/workspace-instructions', 'openbitfun-services-core/workspace-runtime', + 'openbitfun-services-core/workspace-persistence', ], exact: true, reason: 'openbitfun-core workspace-runtime must select only local workspace and runtime layout owners', diff --git a/scripts/core-boundaries/self-test.mjs b/scripts/core-boundaries/self-test.mjs index 0667091eae..a6d5c193f5 100644 --- a/scripts/core-boundaries/self-test.mjs +++ b/scripts/core-boundaries/self-test.mjs @@ -544,6 +544,7 @@ export function runManifestParserSelfTest({ 'openbitfun-services-core/workspace-identity', 'openbitfun-services-core/workspace-instructions', 'openbitfun-services-core/workspace-runtime', + 'openbitfun-services-core/workspace-persistence', ], ], [coreManifest, 'workspace-watch', ['workspace-runtime', 'dep:notify']], diff --git a/scripts/data-migrator-release.mjs b/scripts/data-migrator-release.mjs new file mode 100644 index 0000000000..ae45a91e1e --- /dev/null +++ b/scripts/data-migrator-release.mjs @@ -0,0 +1,64 @@ +#!/usr/bin/env node +import { createHash } from 'node:crypto'; +import { copyFileSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs'; +import { join, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { spawnSync } from 'node:child_process'; + +const ROOT = resolve(import.meta.dirname, '..'); +export const PLATFORMS = { + 'windows-x64': { target: 'x86_64-pc-windows-msvc', extension: 'zip' }, + 'macos-arm64': { target: 'aarch64-apple-darwin', extension: 'dmg', folder: 'dmg' }, + 'macos-x64': { target: 'x86_64-apple-darwin', extension: 'dmg', folder: 'dmg' }, + 'linux-x64': { target: 'x86_64-unknown-linux-gnu', extension: 'AppImage', folder: 'appimage' }, +}; +export function releaseVersion(root = ROOT) { + const manifest = readFileSync(join(root, 'src/apps/data-migrator/Cargo.toml'), 'utf8'); + const version = manifest.match(/^version = "([^"]+)"/m)?.[1]; + if (!version || !/^\d+\.\d+\.\d+(?:-[a-zA-Z0-9.-]+)?$/.test(version)) throw new Error('Invalid independent migrator version'); + const config = JSON.parse(readFileSync(join(root, 'src/apps/data-migrator/tauri.conf.json'), 'utf8')); + if (config.version !== version) throw new Error('Migrator version differs between Cargo and Tauri'); + return version; +} + +export function validateReleaseTag(tag, version) { + if (tag !== `data-migrator-v${version}`) throw new Error('Tag must match the independent migrator version'); +} + +function stage(platform) { + const metadata = PLATFORMS[platform]; + if (!metadata) throw new Error('Unsupported migrator release platform'); + const version = releaseVersion(); + if (process.env.GITHUB_REF_TYPE === 'tag') validateReleaseTag(process.env.GITHUB_REF_NAME, version); + const target = resolve(ROOT, process.env.CARGO_TARGET_DIR || 'target', metadata.target, 'release'); + const output = join(ROOT, 'target/data-migrator-release', platform); + mkdirSync(output, { recursive: true }); + const asset = `openbitfun-data-migrator-v${version}-${platform}.${metadata.extension}`; + const destination = join(output, asset); + if (platform === 'windows-x64') { + const portable = join(ROOT, 'target/data-migrator-portable', version); + mkdirSync(portable, { recursive: true }); + copyFileSync(join(target, 'openbitfun-data-migrator.exe'), join(portable, 'openbitfun-data-migrator.exe')); + for (const file of ['README.md', 'README.zh-CN.md']) copyFileSync(join(ROOT, 'src/apps/data-migrator', file), join(portable, file)); + copyFileSync(join(ROOT, 'THIRD_PARTY_NOTICES.md'), join(portable, 'THIRD_PARTY_NOTICES.md')); + const result = spawnSync('tar', ['-a', '-cf', destination, '-C', portable, 'openbitfun-data-migrator.exe', 'README.md', 'README.zh-CN.md', 'THIRD_PARTY_NOTICES.md'], { stdio: 'inherit', windowsHide: true }); + if (result.error) throw result.error; + if (result.status !== 0) throw new Error('Could not archive the portable migrator'); + } else { + const folder = join(target, 'bundle', metadata.folder); + const files = readdirSync(folder).filter((name) => name.startsWith(`OpenBitFun Data Migrator_${version}_`) && name.endsWith(`.${metadata.extension}`)); + if (files.length !== 1) throw new Error('Expected exactly one migrator package in the bundle output'); + copyFileSync(join(folder, files[0]), destination); + } + const sha256 = createHash('sha256').update(readFileSync(destination)).digest('hex'); + writeFileSync(`${destination}.sha256`, `${sha256} ${asset}\n`); + console.log(destination); +} + +if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + if (process.argv[2] === '--check-version') { + const version = releaseVersion(); + if (process.env.GITHUB_REF_TYPE === 'tag') validateReleaseTag(process.env.GITHUB_REF_NAME, version); + console.log(version); + } else stage(process.argv[2]); +} diff --git a/scripts/data-migrator-tauri-build.mjs b/scripts/data-migrator-tauri-build.mjs index e52219ac7f..9c2a7a4cff 100644 --- a/scripts/data-migrator-tauri-build.mjs +++ b/scripts/data-migrator-tauri-build.mjs @@ -5,27 +5,19 @@ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'; import { dirname, join, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; -import { extractProductConfigArg } from './product-customization/cli.mjs'; -import { productBuildEnvironment } from './product-customization/projections.mjs'; -import { resolveProductDefinition } from './product-customization/resolver.mjs'; +import { generateDataMigratorTheme } from './generate-data-migrator-theme.mjs'; const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..'); const APP_DIR = join(ROOT, 'src', 'apps', 'data-migrator'); export function prepareDataMigratorTauriConfig( baseConfigPath, - resolution, outputDirectory = join(APP_DIR, 'gen'), ) { - if (resolution.assembly.member !== 'dataMigrator') { - throw new Error('Data Migrator packaging requires the dataMigrator product member.'); - } const config = JSON.parse(readFileSync(baseConfigPath, 'utf8')); - const productName = resolution.productNames[resolution.assembly.fallbackLocale] - ?? resolution.productNames[resolution.assembly.defaultLocale]; - config.productName = productName; - config.mainBinaryName = resolution.assembly.binaryName; - config.identifier = resolution.assembly.bundleId; + const manifest = readFileSync(join(APP_DIR, 'Cargo.toml'), 'utf8'); + const version = manifest.match(/^version = "([^"]+)"/m)?.[1]; + if (!version || config.version !== version) throw new Error('Migrator Cargo and Tauri versions must match.'); config.build = { frontendDist: config.build?.frontendDist || 'ui', }; @@ -38,12 +30,28 @@ export function prepareDataMigratorTauriConfig( mkdirSync(outputDirectory, { recursive: true }); const output = join( outputDirectory, - `tauri.${resolution.assembly.assemblyDigest}.generated.conf.json`, + 'tauri.generated.conf.json', ); writeFileSync(output, `${JSON.stringify(config, null, 2)}\n`, 'utf8'); return output; } +// The tool identity and the format destination are separate. Never inherit a +// branded Desktop build's data namespace or sibling-executable projections. +export function dataMigratorEnvironment(environment = process.env) { + const result = { ...environment }; + for (const key of ['OPENBITFUN_DESKTOP_BINARY_NAME', 'OPENBITFUN_DATA_MIGRATOR_BINARY_NAME']) delete result[key]; + return { + ...result, + CI: 'true', + OPENBITFUN_PRODUCT_ID: 'openbitfun', + OPENBITFUN_DATA_NAMESPACE: 'openbitfun', + OPENBITFUN_HIDDEN_DATA_DIRECTORY: '.openbitfun', + OPENBITFUN_PRODUCT_BINARY_NAME: 'openbitfun-data-migrator', + OPENBITFUN_PRODUCT_DISPLAY_NAME: 'OpenBitFun Data Migrator', + }; +} + function tauriArguments(raw) { let offset = 0; while (raw[offset] === '--') offset += 1; @@ -51,26 +59,19 @@ function tauriArguments(raw) { } async function main() { - const { productConfig, forwardArgs } = extractProductConfigArg( - tauriArguments(process.argv.slice(2)), - ); - const resolution = resolveProductDefinition({ - rootDir: ROOT, - productConfig, - member: 'dataMigrator', - }); - Object.assign(process.env, productBuildEnvironment(resolution)); - process.env.CI = 'true'; + await generateDataMigratorTheme(); + const forwardArgs = tauriArguments(process.argv.slice(2)); + if (forwardArgs.some((arg) => arg.startsWith('--product-config'))) { + throw new Error('The standalone migrator has its own identity and supports OpenBitFun data only.'); + } const generated = prepareDataMigratorTauriConfig( join(APP_DIR, 'tauri.conf.json'), - resolution, ); - console.log(`[product] dataMigrator ${resolution.assembly.assemblyDigest}`); const tauriBin = join(ROOT, 'node_modules', '.bin', 'tauri'); const result = spawnSync(tauriBin, ['build', '--config', generated, ...forwardArgs], { cwd: APP_DIR, - env: process.env, + env: dataMigratorEnvironment(), stdio: 'inherit', shell: true, windowsHide: true, diff --git a/scripts/data-migrator-tauri-build.test.mjs b/scripts/data-migrator-tauri-build.test.mjs index 8d2cad1615..b045752d87 100644 --- a/scripts/data-migrator-tauri-build.test.mjs +++ b/scripts/data-migrator-tauri-build.test.mjs @@ -1,92 +1,67 @@ import assert from 'node:assert/strict'; -import { mkdtempSync, readFileSync } from 'node:fs'; +import { mkdtempSync, readFileSync, existsSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join, resolve } from 'node:path'; import test from 'node:test'; - -import { prepareDataMigratorTauriConfig } from './data-migrator-tauri-build.mjs'; -import { productBuildEnvironment } from './product-customization/projections.mjs'; -import { resolveProductDefinition } from './product-customization/resolver.mjs'; - +import { prepareDataMigratorTauriConfig, dataMigratorEnvironment } from './data-migrator-tauri-build.mjs'; +import { releaseVersion, validateReleaseTag } from './data-migrator-release.mjs'; +import { prepareTauriConfig } from './desktop-tauri-build.mjs'; const ROOT = resolve(import.meta.dirname, '..'); -const APP = join(ROOT, 'src', 'apps', 'data-migrator'); -const ACME = join(ROOT, 'products', 'fixtures', 'acme', 'product.jsonc'); +const APP = join(ROOT, 'src/apps/data-migrator'); -test('Data Migrator has an independent product and non-updating bundle identity', () => { - const resolution = resolveProductDefinition({ - rootDir: ROOT, - productConfig: ACME, - member: 'dataMigrator', - }); - const output = prepareDataMigratorTauriConfig( - join(APP, 'tauri.conf.json'), - resolution, - mkdtempSync(join(tmpdir(), 'openbitfun-data-migrator-config-')), - ); +test('independent bundle owns version, offline assets, icons and identity', () => { + const output = prepareDataMigratorTauriConfig(join(APP, 'tauri.conf.json'), mkdtempSync(join(tmpdir(), 'migrator-config-'))); const config = JSON.parse(readFileSync(output, 'utf8')); - - assert.equal(config.productName, 'Acme Data Migrator'); - assert.equal(config.mainBinaryName, 'acme-data-migrator'); - assert.equal(config.identifier, 'com.acme.data-migrator'); - assert.notEqual(config.identifier, 'com.acme.desktop'); + assert.equal(config.productName, 'OpenBitFun Data Migrator'); + assert.equal(config.identifier, 'com.openbitfun.data-migrator'); + assert.equal(config.version, releaseVersion()); assert.deepEqual(config.build, { frontendDist: 'ui' }); assert.equal(config.plugins?.updater, undefined); - assert.equal(config.bundle.createUpdaterArtifacts, undefined); - assert.deepEqual(Object.values(config.bundle.resources), ['THIRD_PARTY_NOTICES.md']); - assert.deepEqual(config.app.windows.map(({ label }) => label), ['migrator']); + assert.equal(config.bundle.externalBin, undefined); + assert.ok(config.bundle.icon.every((icon) => !icon.includes('desktop') && existsSync(join(APP, icon)))); + assert.ok(config.app.security.csp.includes("connect-src 'self'")); }); -test('Data Migrator projection provides both trusted sibling binary names', () => { - const resolution = resolveProductDefinition({ - rootDir: ROOT, - productConfig: ACME, - member: 'dataMigrator', - }); - const environment = productBuildEnvironment(resolution); - - assert.equal(environment.OPENBITFUN_PRODUCT_BINARY_NAME, 'acme-data-migrator'); - assert.equal(environment.OPENBITFUN_DATA_MIGRATOR_BINARY_NAME, 'acme-data-migrator'); - assert.equal(environment.OPENBITFUN_DESKTOP_BINARY_NAME, 'acme-desktop'); +test('a Desktop product environment cannot change the migration destination identity', () => { + const env = dataMigratorEnvironment({ OPENBITFUN_PRODUCT_ID: 'acme', OPENBITFUN_DATA_NAMESPACE: 'acme', OPENBITFUN_DESKTOP_BINARY_NAME: 'acme', CI: '1' }); + assert.equal(env.OPENBITFUN_PRODUCT_ID, 'openbitfun'); + assert.equal(env.OPENBITFUN_DATA_NAMESPACE, 'openbitfun'); + assert.equal(env.OPENBITFUN_DESKTOP_BINARY_NAME, undefined); + assert.equal(env.CI, 'true'); }); -test('Data Migrator dependency and command closure stays migration-only', () => { - const manifest = readFileSync(join(APP, 'Cargo.toml'), 'utf8'); - const source = readFileSync(join(APP, 'src', 'app_state.rs'), 'utf8'); - const registration = readFileSync(join(APP, 'src', 'lib.rs'), 'utf8'); - const capability = readFileSync(join(APP, 'capabilities', 'migrator.json'), 'utf8'); - - assert.match(manifest, /openbitfun-core[^\n]+features = \["legacy-migration"\]/); - for (const forbidden of ['product-full', 'openbitfun-agent-runtime', 'plugin-runtime']) { - assert.equal(manifest.includes(forbidden), false, `manifest must not include ${forbidden}`); - } - assert.match(source, /product_assembly_plan_for_profile\(DeliveryProfile::DataMigrator\)/); - assert.match(registration, /tauri::generate_handler!/); - assert.match(registration, /commands::export_migration_diagnostics/); - assert.match( - readFileSync(join(ROOT, 'scripts', 'data-migrator-tauri-build.mjs'), 'utf8'), - /windowsHide:\s*true/, - ); - for (const forbidden of ['fs:', 'shell:', 'updater:', 'dialog:']) { - assert.equal(capability.includes(forbidden), false, `capability must not include ${forbidden}`); +test('Desktop package generation has no migrator payload or build hook', () => { + const desktop = join(ROOT, 'src/apps/desktop'); + const output = prepareTauriConfig(join(desktop, 'tauri.conf.json'), { desktopDir: desktop }); + const config = JSON.parse(readFileSync(output, 'utf8')); + assert.ok(!(config.bundle.externalBin || []).some((file) => file.includes('migrator'))); + for (const file of ['scripts/dev.cjs', 'scripts/desktop-tauri-build.mjs', 'src/apps/desktop/src/lib.rs']) { + assert.doesNotMatch(readFileSync(join(ROOT, file), 'utf8'), /data-migrator|legacy_migration_api/); } }); -test('Data Migrator gates onboarding actions on authenticated bootstrap', () => { - const html = readFileSync(join(APP, 'ui', 'index.html'), 'utf8'); - const source = readFileSync(join(APP, 'ui', 'app.js'), 'utf8'); - - assert.match(html, /
]+hidden>/); - assert.match(source, /function requireBootstrap\(\)/); - assert.match(source, /if \(!requireBootstrap\(\)\) return;/); - assert.match(source, /catch \(error\) \{\s+notice\(/); +test('migrator dependency and command closure exclude the main app and restart handshake', () => { + const manifest = readFileSync(join(APP, 'Cargo.toml'), 'utf8'); + assert.doesNotMatch(manifest, /^openbitfun-core\s*=|product-full|product-capabilities|plugin-runtime/m); + const source = readFileSync(join(APP, 'src/app_state.rs'), 'utf8'); + assert.doesNotMatch(source, /HandoffStore|MigrationOnboardingStore|restart_desktop|TrustedInstallationResolver/); + const capability = readFileSync(join(APP, 'capabilities/migrator.json'), 'utf8'); + assert.doesNotMatch(capability, /fs:|shell:|updater:|dialog:/); }); -test('Data Migrator labels unverified report counts as staged', () => { - const source = readFileSync(join(APP, 'ui', 'app.js'), 'utf8'); +test('independent release tags cannot be confused with main app versions', () => { + const version = releaseVersion(); + assert.doesNotThrow(() => validateReleaseTag('data-migrator-v' + version, version)); + assert.throws(() => validateReleaseTag('v' + version, version)); + assert.throws(() => validateReleaseTag('data-migrator-v9.0.0', version)); +}); - assert.match( - source, - /result\.state === 'verified' \? text\.imported : text\.staged/, - ); - assert.match(source, /\$\{result\.imported\} \$\{transferLabel\(result\)\}/); +test('all static UI labels and locale keys have complete translations', () => { + const html = readFileSync(join(APP, 'ui/index.html'), 'utf8'); + const locales = JSON.parse(readFileSync(join(APP, 'ui/locales.json'), 'utf8')); + const keys = Object.keys(locales.en).sort(); + for (const labels of Object.values(locales)) { + assert.deepEqual(Object.keys(labels).sort(), keys); + for (const match of html.matchAll(/data-i18n="([^"]+)"/g)) assert.ok(labels[match[1]], match[1]); + } }); diff --git a/scripts/desktop-dev-migration.mjs b/scripts/desktop-dev-migration.mjs deleted file mode 100644 index 99aa2455ae..0000000000 --- a/scripts/desktop-dev-migration.mjs +++ /dev/null @@ -1,65 +0,0 @@ -import { mkdtemp, readFile, rm } from 'node:fs/promises'; -import os from 'node:os'; -import path from 'node:path'; -import { setTimeout as delay } from 'node:timers/promises'; - -const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; - -async function readOptionalJson(file) { - try { - return JSON.parse(await readFile(file, 'utf8')); - } catch (error) { - if (error.code === 'ENOENT') return null; - throw error; - } -} - -function isProcessAlive(pid) { - try { - process.kill(pid, 0); - return true; - } catch (error) { - if (error.code === 'ESRCH') return false; - throw error; - } -} - -// Tauri stops its frontend server when Desktop hands off to Data Migrator. -// Keep the development supervisor alive and re-enter Tauri after the migrator -// requests a restart, restoring both Vite and the Rust watcher. -export async function runDesktopWithMigrationRestart(run, { - info = () => {}, - isAlive = isProcessAlive, - wait = () => delay(250), -} = {}) { - let restartArgs = []; - for (;;) { - const directory = await mkdtemp(path.join(os.tmpdir(), 'openbitfun-dev-migration-')); - try { - let failure; - try { - await run(restartArgs, { OPENBITFUN_DEV_MIGRATION_DIR: directory }); - } catch (error) { - failure = error; - } - const handoff = await readOptionalJson(path.join(directory, 'handoff.json')); - if (!handoff) { - if (failure) throw failure; - return; - } - if (!UUID.test(handoff.runId) || !Number.isSafeInteger(handoff.pid) || handoff.pid <= 0) { - throw new Error('Invalid development migration handoff'); - } - info('Waiting for Data Migrator; Desktop will reopen automatically when it finishes'); - while (isAlive(handoff.pid)) await wait(); - const restart = await readOptionalJson(path.join(directory, 'restart.json')); - if (restart?.runId !== handoff.runId) { - throw new Error('Data Migrator exited without completing its restart handoff; run desktop:dev to retry'); - } - restartArgs = ['--legacy-migration-run-id', handoff.runId]; - info('Restarting Desktop through the development launcher'); - } finally { - await rm(directory, { recursive: true, force: true }); - } - } -} diff --git a/scripts/desktop-dev-migration.test.mjs b/scripts/desktop-dev-migration.test.mjs deleted file mode 100644 index 6f064f228c..0000000000 --- a/scripts/desktop-dev-migration.test.mjs +++ /dev/null @@ -1,60 +0,0 @@ -import assert from 'node:assert/strict'; -import { access, writeFile } from 'node:fs/promises'; -import path from 'node:path'; -import test from 'node:test'; -import { runDesktopWithMigrationRestart } from './desktop-dev-migration.mjs'; - -const runId = '01234567-89ab-4cde-8fab-0123456789ab'; -const write = (directory, file, value) => writeFile(path.join(directory, file), JSON.stringify(value)); - -test('normal exit does not restart and removes its temporary channel', async () => { - let directory; - let calls = 0; - await runDesktopWithMigrationRestart(async (args, env) => { - calls++; - assert.deepEqual(args, []); - directory = env.OPENBITFUN_DEV_MIGRATION_DIR; - }); - assert.equal(calls, 1); - await assert.rejects(access(directory), { code: 'ENOENT' }); -}); - -test('build failures remain failures when no migration was launched', async () => { - const failure = new Error('build failed'); - await assert.rejects(runDesktopWithMigrationRestart(async () => { throw failure; }), failure); -}); - -test('migration completion waits for child exit then restores the development host with the run id', async () => { - let calls = 0; - let directory; - let running = true; - await runDesktopWithMigrationRestart(async (args, env) => { - calls++; - if (calls === 1) { - directory = env.OPENBITFUN_DEV_MIGRATION_DIR; - await write(directory, 'handoff.json', { runId, pid: 123 }); - // A handoff may also make Tauri report its stopped frontend as a failure. - throw new Error('frontend stopped'); - } - assert.equal(running, false); - assert.deepEqual(args, ['--legacy-migration-run-id', runId]); - assert.notEqual(env.OPENBITFUN_DEV_MIGRATION_DIR, directory); - }, { - isAlive: (pid) => { assert.equal(pid, 123); return running; }, - wait: async () => { - await write(directory, 'restart.json', { runId }); - running = false; - }, - }); - assert.equal(calls, 2); - await assert.rejects(access(directory), { code: 'ENOENT' }); -}); - -test('crashed or mismatched migrators cannot silently restart Desktop', async () => { - for (const restart of [null, { runId: 'wrong-run' }]) { - await assert.rejects(runDesktopWithMigrationRestart(async (_, env) => { - await write(env.OPENBITFUN_DEV_MIGRATION_DIR, 'handoff.json', { runId, pid: 123 }); - if (restart) await write(env.OPENBITFUN_DEV_MIGRATION_DIR, 'restart.json', restart); - }, { isAlive: () => false }), /without completing its restart handoff/); - } -}); diff --git a/scripts/desktop-tauri-build.mjs b/scripts/desktop-tauri-build.mjs index e362c16503..34ca961ce6 100644 --- a/scripts/desktop-tauri-build.mjs +++ b/scripts/desktop-tauri-build.mjs @@ -30,7 +30,6 @@ const LINUX_FLASHGREP_BINARIES = [ 'flashgrep-aarch64-unknown-linux-musl', 'flashgrep-aarch64-unknown-linux-gnu', ]; -const DATA_MIGRATOR_CARGO_BINARY = 'openbitfun-data-migrator'; function tauriBuildArgsFromArgv() { const args = process.argv.slice(2); @@ -54,7 +53,6 @@ async function main() { const desktopDir = join(ROOT, 'src', 'apps', 'desktop'); preparePluginHost(); - const dataMigratorSidecar = prepareDataMigratorSidecar(forward, resolution, desktopDir); // Flashgrep distribution is temporarily suspended. const flashgrepBinary = null; // Tauri CLI reads CI and rejects numeric "1" (common in CI providers). @@ -68,7 +66,6 @@ async function main() { const tauriConfig = prepareTauriConfig(join(desktopDir, 'tauri.conf.json'), { desktopDir, flashgrepBinary, - dataMigratorSidecar, resolution, releaseChannel, }); @@ -99,10 +96,6 @@ async function main() { process.exit(1); } - if (r.status === 0 && forward.includes('--no-bundle')) { - stageNoBundleDataMigrator(dataMigratorSidecar); - } - // Keep only the latest useful Cargo caches for this build profile after tauri build ends. try { const { profileFromTauriBuildArgs, runGcBestEffort, targetFromTauriBuildArgs } = await import( @@ -142,84 +135,6 @@ function rustHostTargetTriple() { return host; } -export function planDataMigratorSidecar( - args, - resolution, - desktopDir, - runtime = {}, -) { - const explicitTarget = optionValue(args, '--target'); - const targetTriple = explicitTarget || runtime.hostTarget || rustHostTargetTriple(); - const profile = args.includes('--debug') ? 'debug' : optionValue(args, '--profile') || 'release'; - const targetDirValue = runtime.cargoTargetDir ?? process.env.CARGO_TARGET_DIR; - const targetDir = targetDirValue - ? isAbsolute(targetDirValue) - ? targetDirValue - : resolve(ROOT, targetDirValue) - : join(ROOT, 'target'); - const windowsTarget = targetTriple.includes('windows'); - const suffix = windowsTarget ? '.exe' : ''; - const artifactDirectory = join(targetDir, ...(explicitTarget ? [explicitTarget] : []), profile); - const cargoArgs = ['build', '-p', 'openbitfun-data-migrator', '--bin', DATA_MIGRATOR_CARGO_BINARY]; - if (explicitTarget) cargoArgs.push('--target', explicitTarget); - if (args.includes('--debug')) { - // Cargo's default profile is the Tauri CLI's debug profile. - } else if (optionValue(args, '--profile')) { - cargoArgs.push('--profile', profile); - } else { - cargoArgs.push('--release'); - } - - const siblingBinaryName = resolution.assembly.memberBinaryNames.dataMigrator; - const externalBinBase = join(desktopDir, 'gen', 'sidecars', siblingBinaryName); - return { - artifactDirectory, - cargoArgs, - externalBinBase, - externalBinInput: `${externalBinBase}-${targetTriple}${suffix}`, - sourceArtifact: join(artifactDirectory, `${DATA_MIGRATOR_CARGO_BINARY}${suffix}`), - siblingArtifact: join(artifactDirectory, `${siblingBinaryName}${suffix}`), - siblingBinaryName, - targetTriple, - }; -} - -function prepareDataMigratorSidecar(args, resolution, desktopDir) { - const plan = planDataMigratorSidecar(args, resolution, desktopDir); - console.log( - `[tauri-build] Building Data Migrator sidecar (${plan.targetTriple}, ${plan.artifactDirectory})` - ); - const result = spawnSync('cargo', plan.cargoArgs, { - cwd: ROOT, - env: process.env, - stdio: 'inherit', - shell: false, - windowsHide: true, - }); - if (result.error) throw result.error; - if (result.status !== 0) { - throw new Error(`Data Migrator sidecar build failed with exit code ${result.status}`); - } - if (!existsSync(plan.sourceArtifact)) { - throw new Error(`Data Migrator build did not produce ${plan.sourceArtifact}`); - } - mkdirSync(dirname(plan.externalBinInput), { recursive: true }); - copyFileSync(plan.sourceArtifact, plan.externalBinInput); - if (!plan.externalBinInput.endsWith('.exe')) { - chmodSync(plan.externalBinInput, statSync(plan.externalBinInput).mode | 0o111); - } - return plan; -} - -export function stageNoBundleDataMigrator(plan) { - if (resolve(plan.sourceArtifact) === resolve(plan.siblingArtifact)) return plan.siblingArtifact; - copyFileSync(plan.sourceArtifact, plan.siblingArtifact); - if (!plan.siblingArtifact.endsWith('.exe')) { - chmodSync(plan.siblingArtifact, statSync(plan.siblingArtifact).mode | 0o111); - } - return plan.siblingArtifact; -} - function preparePluginHost() { const result = spawnSync('pnpm', ['run', 'plugin-host:prepare'], { cwd: ROOT, @@ -378,7 +293,7 @@ export function prepareMacOSFlashgrepForSigning( export function prepareTauriConfig( baseConfigPath, - { desktopDir, flashgrepBinary, dataMigratorSidecar, resolution, releaseChannel } + { desktopDir, flashgrepBinary, resolution, releaseChannel } ) { const config = JSON.parse(readFileSync(baseConfigPath, 'utf8')); if (resolution) { @@ -390,7 +305,6 @@ export function prepareTauriConfig( config.identifier = resolution.assembly.bundleId; } injectTargetFlashgrepResource(config, desktopDir, flashgrepBinary); - injectDataMigratorSidecar(config, desktopDir, dataMigratorSidecar); // The DeepSeek bridge is not a compile-time resource: cargo check and // desktop:dev must not require packages/dsh-acp/dist-profile. Official // packaging injects it here; frontend:build-all (beforeBuildCommand) @@ -457,16 +371,6 @@ export function prepareTauriConfig( return generatedConfig; } -function injectDataMigratorSidecar(config, desktopDir, sidecar) { - if (!sidecar) return; - const externalBin = new Set(config.bundle?.externalBin || []); - externalBin.add(toTauriPath(relative(desktopDir, sidecar.externalBinBase))); - config.bundle = { - ...(config.bundle || {}), - externalBin: [...externalBin], - }; -} - const DSH_PROFILE_RESOURCE_SOURCE = '../../../packages/dsh-acp/dist-profile'; const DSH_PROFILE_RESOURCE_TARGET = 'resources/dsh-profile'; const EXTERNAL_FRONTEND_RESOURCE_SOURCE = '../../../dist'; diff --git a/scripts/desktop-tauri-build.test.mjs b/scripts/desktop-tauri-build.test.mjs index 5c27bcda53..2399ecc306 100644 --- a/scripts/desktop-tauri-build.test.mjs +++ b/scripts/desktop-tauri-build.test.mjs @@ -5,10 +5,8 @@ import { join } from 'node:path'; import test from 'node:test'; import { configureDesktopWebFontProfile, - planDataMigratorSidecar, prepareMacOSFlashgrepForSigning, prepareTauriConfig, - stageNoBundleDataMigrator, shouldRetryMacDmgBuild, } from './desktop-tauri-build.mjs'; import { resolveProductDefinition } from './product-customization/resolver.mjs'; @@ -345,58 +343,6 @@ test('Desktop packaging works without the suspended Flashgrep resource', () => { } }); -test('Desktop packaging builds and projects the matching Data Migrator sidecar', () => { - const fixture = join(tmpdir(), `openbitfun-migrator-sidecar-${process.pid}-${Date.now()}`); - const desktopDir = join(fixture, 'src', 'apps', 'desktop'); - const targetDir = join(fixture, 'target'); - mkdirSync(desktopDir, { recursive: true }); - const baseConfig = join(fixture, 'tauri.conf.json'); - writeFileSync(baseConfig, JSON.stringify({ bundle: { resources: {} } })); - try { - const resolution = resolveProductDefinition({ - rootDir: ROOT, - productConfig: join(ROOT, 'products', 'fixtures', 'acme', 'product.jsonc'), - member: 'desktop', - }); - const plan = planDataMigratorSidecar( - ['--target', 'x86_64-pc-windows-msvc', '--profile', 'release-fast'], - resolution, - desktopDir, - { cargoTargetDir: targetDir }, - ); - assert.deepEqual(plan.cargoArgs, [ - 'build', - '-p', - 'openbitfun-data-migrator', - '--bin', - 'openbitfun-data-migrator', - '--target', - 'x86_64-pc-windows-msvc', - '--profile', - 'release-fast', - ]); - assert.equal( - plan.externalBinInput, - join(desktopDir, 'gen', 'sidecars', 'acme-data-migrator-x86_64-pc-windows-msvc.exe'), - ); - const generated = prepareTauriConfig(baseConfig, { - desktopDir, - flashgrepBinary: join(fixture, 'flashgrep'), - dataMigratorSidecar: plan, - resolution, - }); - const config = JSON.parse(readFileSync(generated, 'utf8')); - assert.deepEqual(config.bundle.externalBin, ['gen/sidecars/acme-data-migrator']); - - mkdirSync(plan.artifactDirectory, { recursive: true }); - writeFileSync(plan.sourceArtifact, 'migrator'); - assert.equal(stageNoBundleDataMigrator(plan), plan.siblingArtifact); - assert.equal(readFileSync(plan.siblingArtifact, 'utf8'), 'migrator'); - } finally { - rmSync(fixture, { force: true, recursive: true }); - } -}); - test('Windows updater installs NSIS packages without showing its progress window', () => { const fixture = join(tmpdir(), `openbitfun-tauri-updater-${process.pid}-${Date.now()}`); const baseConfig = join(fixture, 'tauri.conf.json'); diff --git a/scripts/dev.cjs b/scripts/dev.cjs index e3672f494c..bcfd15567b 100644 --- a/scripts/dev.cjs +++ b/scripts/dev.cjs @@ -557,31 +557,30 @@ async function startDesktopPreview() { printInfo(`Launching debug desktop binary: ${desktopBinary}`); - const { runDesktopWithMigrationRestart } = await import( - pathToFileURL(path.join(__dirname, 'desktop-dev-migration.mjs')).href - ); - try { - await runDesktopWithMigrationRestart((restartArgs, migrationEnv) => new Promise((resolve, reject) => { - appProcess = spawnBackgroundCommand(desktopBinary, restartArgs, ROOT_DIR, { - ...process.env, - ...migrationEnv, - // Upload the current workspace mobile bundle instead of the staged copy. - OPENBITFUN_MOBILE_WEB_DIR: path.join(ROOT_DIR, 'src/mobile-web/dist'), - }); - appProcess.on('error', reject); - appProcess.on('close', (code, signal) => { - appProcess = null; - if (code === 0) resolve(); - else reject(new Error(`Desktop preview exited (code=${code}, signal=${signal})`)); - }); - printSuccess('Desktop preview is running'); - printInfo('Front-end edits continue to use Vite HMR; rebuild Rust only when desktop-side code changes'); - }), { info: printInfo }); - await shutdown(0); - } catch (error) { - printError(error.message || String(error)); - await shutdown(1); - } + appProcess = spawnBackgroundCommand(desktopBinary, [], ROOT_DIR, { + ...process.env, + // Debug previews must upload the current workspace build. The adjacent + // target/debug resource tree is only a build-time copy and can lag behind + // mobile-web edits made while the desktop binary is being reused. + OPENBITFUN_MOBILE_WEB_DIR: path.join(ROOT_DIR, 'src/mobile-web/dist'), + }); + + appProcess.on('error', (error) => { + printError(`Desktop preview failed to start: ${error.message || String(error)}`); + void shutdown(1); + }); + + appProcess.on('exit', (code, signal) => { + if (!shuttingDown) { + printInfo(`Desktop preview exited (code=${code ?? 'null'}, signal=${signal ?? 'null'})`); + } + void shutdown(code ?? 0); + }); + + printSuccess('Desktop preview is running'); + printInfo('Front-end edits continue to use Vite HMR; rebuild Rust only when desktop-side code changes'); + + await new Promise(() => {}); } /** @@ -677,22 +676,6 @@ async function main() { process.exit(1); } - // Build after version generation and before Desktop starts. Cargo checks - // freshness so an existing but stale Migrator is rebuilt as well. - if (desktopMode) { - printInfo('Preparing Data Migrator (incremental Debug build)'); - const migratorBuild = await runCommandPrefixed( - 'data-migrator', - 'cargo', - ['build', '-p', 'openbitfun-data-migrator', '--bin', 'openbitfun-data-migrator'], - ); - if (!migratorBuild.ok) { - printError('Data Migrator build failed; Desktop was not started'); - if (migratorBuild.error?.message) printError(migratorBuild.error.message); - process.exit(1); - } - } - if (desktopMode) { const baselineHelperUrl = pathToFileURL( path.join(__dirname, 'frontend-workbench-dev-baseline.mjs') @@ -751,26 +734,19 @@ async function main() { OPENBITFUN_MOBILE_WEB_DIR: path.join(ROOT_DIR, 'src/mobile-web/dist'), }; try { - const { runDesktopWithMigrationRestart } = await import( - pathToFileURL(path.join(__dirname, 'desktop-dev-migration.mjs')).href - ); - await runDesktopWithMigrationRestart(async (restartArgs, migrationEnv) => { - const args = ['dev', '--config', tauriConfig, ...(restartArgs.length ? ['--', '--', ...restartArgs] : [])]; - if (process.platform === 'win32') { - // Running the generated .cmd shim directly via spawn is flaky on Windows. - // Use cmd.exe with an explicit args array so the desktop app directory - // stays the Tauri project root without pnpm workspace path rewriting. - const tauriBin = path.join(ROOT_DIR, 'node_modules', '.bin', 'tauri.cmd'); - await runWindowsCommandArgs(tauriBin, args, desktopDir, { ...tauriDevEnv, ...migrationEnv }); - } else { - const tauriBin = path.join(ROOT_DIR, 'node_modules', '.bin', 'tauri'); - await spawnCommand(tauriBin, args, desktopDir, { - CARGO_PROFILE_DEV_CODEGEN_UNITS: tauriDevEnv.CARGO_PROFILE_DEV_CODEGEN_UNITS, - OPENBITFUN_MOBILE_WEB_DIR: tauriDevEnv.OPENBITFUN_MOBILE_WEB_DIR, - ...migrationEnv, - }); - } - }, { info: printInfo }); + if (process.platform === 'win32') { + // Running the generated .cmd shim directly via spawn is flaky on Windows. + // Use cmd.exe with an explicit args array so the desktop app directory + // stays the Tauri project root without pnpm workspace path rewriting. + const tauriBin = path.join(ROOT_DIR, 'node_modules', '.bin', 'tauri.cmd'); + await runWindowsCommandArgs(tauriBin, ['dev', '--config', tauriConfig], desktopDir, tauriDevEnv); + } else { + const tauriBin = path.join(ROOT_DIR, 'node_modules', '.bin', 'tauri'); + await spawnCommand(tauriBin, ['dev', '--config', tauriConfig], desktopDir, { + CARGO_PROFILE_DEV_CODEGEN_UNITS: tauriDevEnv.CARGO_PROFILE_DEV_CODEGEN_UNITS, + OPENBITFUN_MOBILE_WEB_DIR: tauriDevEnv.OPENBITFUN_MOBILE_WEB_DIR, + }); + } } finally { // Option B: prune only when the desktop:dev session ends, not on each rebuild. await runDesktopTargetGc('debug'); diff --git a/scripts/frontend-color-surface-registry.json b/scripts/frontend-color-surface-registry.json index 5693d6497e..dd7bf656a6 100644 --- a/scripts/frontend-color-surface-registry.json +++ b/scripts/frontend-color-surface-registry.json @@ -114,6 +114,19 @@ "baseline": "scripts/theme-color-governance-baseline.installer.json" } }, + { + "id": "data-migrator", + "label": "Offline Data Migrator", + "kind": "canonical-web", + "owner": "@openbitfun/design-tokens and @openbitfun/theme-openbitfun public CSS exports", + "root": "src/apps/data-migrator/ui", + "audit": { + "engine": "theme", + "policy": "canonical-ui-zero", + "packageContracts": ["@openbitfun/design-tokens", "@openbitfun/theme-openbitfun"], + "excludePaths": ["generated"] + } + }, { "id": "desktop-bootstrap", "label": "Desktop pre-JavaScript bootstrap pages", @@ -362,6 +375,11 @@ } ], "generatedChecks": [ + { + "id": "data-migrator-design-system", + "surfaceIds": ["data-migrator"], + "command": ["node", "scripts/generate-data-migrator-theme.mjs", "--check"] + }, { "id": "desktop-appearance-projection", "surfaceIds": ["web-ui", "desktop-bootstrap"], @@ -463,6 +481,13 @@ "owner": "scripts/mobile-ui-design-system.mjs", "reason": "Preview data is generated from the native mobile token contract and checked for drift." }, + { + "id": "data-migrator-generated", + "path": "src/apps/data-migrator/ui/generated", + "kind": "generated-output", + "owner": "scripts/generate-data-migrator-theme.mjs", + "reason": "Offline CSS is bundled from public design-system exports and checked for drift." + }, { "id": "desktop-bootstrap-generated", "path": "src/apps/desktop/src/generated", diff --git a/scripts/generate-data-migrator-theme.mjs b/scripts/generate-data-migrator-theme.mjs new file mode 100644 index 0000000000..1631e45c24 --- /dev/null +++ b/scripts/generate-data-migrator-theme.mjs @@ -0,0 +1,48 @@ +#!/usr/bin/env node +/** Bundle the public design-system CSS for the offline, static migrator UI. */ +import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; +import { createRequire } from 'node:module'; +import { dirname, join, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const ROOT = resolve(import.meta.dirname, '..'); +const OUTPUT = join(ROOT, 'src/apps/data-migrator/ui/generated/design-system.css'); +const themeRequire = createRequire(join(ROOT, 'design-system/packages/theme-openbitfun/package.json')); + +function bundleStylesheet(file, ancestors = new Set()) { + if (ancestors.has(file)) throw new Error(`Circular design-system stylesheet import: ${file}`); + const imports = new Set([...ancestors, file]); + const requireFromFile = createRequire(file); + return readFileSync(file, 'utf8').replace(/\r\n?/g, '\n').replace( + /@import\s+["']([^"']+)["'];/g, + (_, specifier) => bundleStylesheet(requireFromFile.resolve(specifier), imports).trimEnd(), + ); +} + +export async function generateDataMigratorTheme({ check = false } = {}) { + // Build the owning packages before reading their exports; an older dist must + // never make a source change appear current in the generated-output check. + await import('../design-system/packages/design-tokens/scripts/build.mjs'); + await import('../design-system/packages/theme-openbitfun/scripts/build.mjs'); + const css = bundleStylesheet(themeRequire.resolve('@openbitfun/theme-openbitfun/default.css')); + if (/@import\b|url\(/i.test(css)) { + throw new Error('Data Migrator design-system CSS must have no external asset dependencies.'); + } + const content = '/* Generated by scripts/generate-data-migrator-theme.mjs. Do not edit. */\n' + + `${css.trimEnd()}\n`; + const current = existsSync(OUTPUT) ? readFileSync(OUTPUT, 'utf8').replace(/\r\n?/g, '\n') : null; + if (current === content) return; + if (check) { + throw new Error('Data Migrator theme is stale. Run `pnpm run data-migrator:theme:generate`.'); + } + mkdirSync(dirname(OUTPUT), { recursive: true }); + writeFileSync(OUTPUT, content, 'utf8'); + console.log('[data-migrator] Generated offline design-system CSS'); +} + +if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + generateDataMigratorTheme({ check: process.argv.includes('--check') }).catch((error) => { + console.error(error.message); + process.exitCode = 1; + }); +} diff --git a/scripts/product-customization/projections.mjs b/scripts/product-customization/projections.mjs index 8918e16777..8bf06c09a9 100644 --- a/scripts/product-customization/projections.mjs +++ b/scripts/product-customization/projections.mjs @@ -46,9 +46,6 @@ export function productBuildEnvironment(resolution) { OPENBITFUN_HIDDEN_DATA_DIRECTORY: `.${resolution.assembly.dataNamespace}`, OPENBITFUN_PRODUCT_BINARY_NAME: resolution.assembly.binaryName, OPENBITFUN_PRODUCT_DISPLAY_NAME: fallbackName, - OPENBITFUN_DESKTOP_BINARY_NAME: resolution.assembly.memberBinaryNames.desktop, - OPENBITFUN_DATA_MIGRATOR_BINARY_NAME: - resolution.assembly.memberBinaryNames.dataMigrator, }; if (!resolution.isDefaultProduct) { const cargoTargetRoot = process.env.CARGO_TARGET_DIR diff --git a/scripts/product-customization/projections.test.mjs b/scripts/product-customization/projections.test.mjs index 25334218db..6f6ffeb472 100644 --- a/scripts/product-customization/projections.test.mjs +++ b/scripts/product-customization/projections.test.mjs @@ -32,9 +32,4 @@ test('build environment isolates custom Cargo output without overriding the defa assert.equal(customEnvironment.OPENBITFUN_HIDDEN_DATA_DIRECTORY, '.acme'); assert.equal(customEnvironment.OPENBITFUN_PRODUCT_BINARY_NAME, 'acme'); assert.equal(customEnvironment.OPENBITFUN_PRODUCT_DISPLAY_NAME, 'Acme CLI'); - assert.equal(customEnvironment.OPENBITFUN_DESKTOP_BINARY_NAME, 'acme-desktop'); - assert.equal( - customEnvironment.OPENBITFUN_DATA_MIGRATOR_BINARY_NAME, - 'acme-data-migrator', - ); }); diff --git a/scripts/product-customization/resolver.mjs b/scripts/product-customization/resolver.mjs index c522f00ad4..8a6487ec9b 100644 --- a/scripts/product-customization/resolver.mjs +++ b/scripts/product-customization/resolver.mjs @@ -15,7 +15,7 @@ const ROOT_FIELDS = new Set([ 'localeRoot', 'members', ]); -const MEMBERS_FIELDS = new Set(['desktop', 'dataMigrator', 'cli']); +const MEMBERS_FIELDS = new Set(['desktop', 'cli']); const COMMON_MEMBER_FIELDS = new Set(['displayNameKey', 'binaryName']); const BUNDLED_MEMBER_FIELDS = new Set([...COMMON_MEMBER_FIELDS, 'bundleId']); @@ -192,7 +192,7 @@ function ownedLocaleFile(localeRoot, locale) { function validateMember(raw, member) { const owner = `members.${member}`; const value = requireObject(raw, owner); - const bundled = member === 'desktop' || member === 'dataMigrator'; + const bundled = member === 'desktop'; rejectUnknownFields(value, bundled ? BUNDLED_MEMBER_FIELDS : COMMON_MEMBER_FIELDS, owner); const result = { displayNameKey: requiredString(value.displayNameKey, `${owner}.displayNameKey`), @@ -235,8 +235,8 @@ function loadProductNames(rootDir, localeRoot, displayNameKeys) { } export function resolveProductDefinition({ rootDir, productConfig, member }) { - if (!['desktop', 'dataMigrator', 'cli'].includes(member)) { - fail('invalid_member', `Unsupported product member: ${member}`, 'Use desktop, dataMigrator, or cli.'); + if (!['desktop', 'cli'].includes(member)) { + fail('invalid_member', `Unsupported product member: ${member}`, 'Use desktop or cli.'); } const canonicalRoot = realpathSync.native(resolve(rootDir)); const defaultPath = realpathSync.native(join(canonicalRoot, 'products', 'openbitfun', 'product.jsonc')); @@ -262,7 +262,6 @@ export function resolveProductDefinition({ rootDir, productConfig, member }) { rejectUnknownFields(members, MEMBERS_FIELDS, 'members'); const normalizedMembers = { desktop: validateMember(members.desktop, 'desktop'), - dataMigrator: validateMember(members.dataMigrator, 'dataMigrator'), cli: validateMember(members.cli, 'cli'), }; const locales = loadProductNames( @@ -270,7 +269,6 @@ export function resolveProductDefinition({ rootDir, productConfig, member }) { localeRoot, [ normalizedMembers.desktop.displayNameKey, - normalizedMembers.dataMigrator.displayNameKey, normalizedMembers.cli.displayNameKey, ], ); @@ -281,10 +279,6 @@ export function resolveProductDefinition({ rootDir, productConfig, member }) { productId, dataNamespace, member, - memberBinaryNames: { - desktop: normalizedMembers.desktop.binaryName, - dataMigrator: normalizedMembers.dataMigrator.binaryName, - }, displayNameKey: selected.displayNameKey, binaryName: selected.binaryName, localeDigest: locales.digest, diff --git a/scripts/product-customization/resolver.test.mjs b/scripts/product-customization/resolver.test.mjs index 79eae41705..4c2283601c 100644 --- a/scripts/product-customization/resolver.test.mjs +++ b/scripts/product-customization/resolver.test.mjs @@ -12,7 +12,6 @@ const ACME = join(ROOT, 'products', 'fixtures', 'acme', 'product.jsonc'); test('default and custom members resolve through one deterministic contract', () => { const openbitfun = resolveProductDefinition({ rootDir: ROOT, member: 'desktop' }); const desktop = resolveProductDefinition({ rootDir: ROOT, productConfig: ACME, member: 'desktop' }); - const dataMigrator = resolveProductDefinition({ rootDir: ROOT, productConfig: ACME, member: 'dataMigrator' }); const cli = resolveProductDefinition({ rootDir: ROOT, productConfig: ACME, member: 'cli' }); assert.equal(openbitfun.assembly.productId, 'openbitfun'); @@ -20,16 +19,9 @@ test('default and custom members resolve through one deterministic contract', () assert.equal(openbitfun.assembly.binaryName, 'openbitfun-desktop'); assert.equal(openbitfun.assembly.bundleId, 'com.openbitfun.desktop'); assert.equal(desktop.assembly.bundleId, 'com.acme.desktop'); - assert.equal(dataMigrator.assembly.binaryName, 'acme-data-migrator'); - assert.equal(dataMigrator.assembly.bundleId, 'com.acme.data-migrator'); - assert.deepEqual(dataMigrator.assembly.memberBinaryNames, { - desktop: 'acme-desktop', - dataMigrator: 'acme-data-migrator', - }); assert.equal(cli.assembly.binaryName, 'acme'); assert.equal(cli.assembly.bundleId, undefined); assert.notEqual(desktop.assembly.assemblyDigest, cli.assembly.assemblyDigest); - assert.notEqual(desktop.assembly.assemblyDigest, dataMigrator.assembly.assemblyDigest); assert.equal( resolveProductDefinition({ rootDir: ROOT, productConfig: ACME, member: 'desktop' }) .assembly.assemblyDigest, diff --git a/scripts/product-identity-audit.mjs b/scripts/product-identity-audit.mjs index 84c2253364..ec8d0873aa 100644 --- a/scripts/product-identity-audit.mjs +++ b/scripts/product-identity-audit.mjs @@ -21,10 +21,6 @@ const productIdentityOwner = 'src/crates/contracts/core-types/src/product_identi const retiredIdentityDataBoundaryFiles = new Set([ 'OPENBITFUN_LEGACY_DATA_MIGRATION_IMPLEMENTATION_PLAN.md', 'OPENBITFUN_LEGACY_DATA_MIGRATION_INVENTORY.md', - 'src/apps/desktop/src/api/legacy_migration_api.rs', - 'src/web-ui/src/locales/en-US/settings/legacy-migration.json', - 'src/web-ui/src/locales/zh-CN/settings/legacy-migration.json', - 'src/web-ui/src/locales/zh-TW/settings/legacy-migration.json', 'deploy/openbitfun-host/README.md', 'deploy/openbitfun-host/migrate-market-data-v1.py', 'src/apps/relay-server/README.md', @@ -39,6 +35,7 @@ const retiredIdentityDataBoundaryPrefixes = Object.freeze([ 'src/apps/data-migrator/', 'src/crates/assembly/core/src/legacy_migration/', 'src/crates/services/legacy-migration/', + 'src/crates/services/legacy-migration-adapters/', ]); const noncanonicalIdentityDataBoundaryFiles = new Set([ 'OPENBITFUN_LEGACY_DATA_MIGRATION_INVENTORY.md', diff --git a/scripts/product-identity-audit.test.mjs b/scripts/product-identity-audit.test.mjs index 6d11658599..0a32a89540 100644 --- a/scripts/product-identity-audit.test.mjs +++ b/scripts/product-identity-audit.test.mjs @@ -106,10 +106,6 @@ test('allows only the exact legacy data-directory ignore entry', () => { test('limits retired identity data to the one-time production migration boundary', () => { for (const file of [ - 'src/apps/desktop/src/api/legacy_migration_api.rs', - 'src/web-ui/src/locales/en-US/settings/legacy-migration.json', - 'src/web-ui/src/locales/zh-CN/settings/legacy-migration.json', - 'src/web-ui/src/locales/zh-TW/settings/legacy-migration.json', ]) { assert.deepEqual(violationsFor(retiredName, file), []); } diff --git a/src/apps/data-migrator/AGENTS.md b/src/apps/data-migrator/AGENTS.md index 55f856171d..2128eca3de 100644 --- a/src/apps/data-migrator/AGENTS.md +++ b/src/apps/data-migrator/AGENTS.md @@ -1,44 +1,42 @@ # Data Migrator Agent Guide -Scope: this guide applies to `src/apps/data-migrator`. - -This app is the offline, local-only host for importing legacy BitFun data. It -must remain a separate executable and WebView identity from Desktop. - -## Guardrails - -- Select `DeliveryProfile::DataMigrator` and only the Core - `legacy-migration` feature. Do not add `product-full`, Agent Runtime, - plugin runtime, normal session startup, updater, shell, or frontend - filesystem capabilities. -- Accept only the handoff `run_id` on the command line. Derive the request path - from `MigrationRoots`; never accept a request or executable path from UI or - command-line input. -- Keep all filesystem, process, credential, and restart work in Rust. The UI - may call only the typed commands registered in `src/lib.rs`. -- Report domain, phase, and counts. Do not invent progress percentages or emit - secrets, user content, credential values, or absolute paths in errors. -- Cancellation is advisory and may be honored only at engine-declared safe - boundaries. Closing during execution requests cancellation and keeps the - window open until a safe boundary. -- The migrator never updates itself. Resolve Desktop as a fixed-name sibling - binary using product-definition projections and the trusted installation - resolver; do not accept executable paths from handoff input. - -## Verification +Scope: src/apps/data-migrator. This is a separately versioned, offline local tool. +Read README.md for its user-facing contract. + +## Boundaries + +- Launch with no arguments. Own directory selection, discovery, durable task + recovery and completion; never depend on a Desktop request or restart Desktop. +- Keep preferences under the tool's Tauri app-config identity. Migration run + journals/backups remain under the destination data directory for recovery. + Do not write Desktop onboarding/reminder state. +- Consume config-contracts, shared storage services and legacy-migration-adapters. + Do not depend on Core, product assembly, Agent execution, Web UI, updater, + plugin lifecycle or a main-application sibling executable. +- Use typed Rust commands for filesystem/process operations. Cancellation is + advisory and is honored only at engine-declared safe boundaries. +- Reject unsupported data formats and unsafe directory overlaps. Preserve source + data, old plans/reports, snapshots, journals, backups and owner conflict policies. +- The UI uses public @openbitfun/design-tokens and @openbitfun/theme-openbitfun + exports bundled in ui/generated/design-system.css. Regenerate through + pnpm run data-migrator:theme:generate. Direct Cargo builds are offline; + Desktop dev/build must never generate or build migrator assets. +- ui/theme.js selects system scheme/contrast before paint. Workflow translations + are app-owned; do not import Web UI catalogs. No mocks or browser automation + for visual verification. +- Tool version lives in Cargo.toml and tauri.conf.json. Packaging/signing uses + the independent Data Migrator workflow and data-migrator-v tags. + +## Focused verification ```bash -cargo test -p openbitfun-data-migrator +cargo test -p openbitfun-data-migrator -p openbitfun-legacy-migration-adapters -p openbitfun-legacy-migration --lib +cargo test -p openbitfun-legacy-migration --test migration_engine_contracts node --test scripts/data-migrator-tauri-build.test.mjs -node --test scripts/desktop-dev-migration.test.mjs +node --check src/apps/data-migrator/ui/app.js +pnpm run theme:color-audit:all ``` -Completion always returns to Desktop. Debug builds launched through `desktop:dev` -or `desktop:preview:debug` ask that launcher to restart via its private temporary -handoff directory, preserving the frontend server and development lifecycle. -Builds without that channel restart the trusted sibling Desktop executable. -Keep this developer-only channel out of persisted migration and remote protocols. - -Run `pnpm run check:core-boundaries` when dependencies or delivery-profile -selection change. Packaging, signing, and UI interaction are separate explicit -verification steps. +Run pnpm run check:core-boundaries for dependency ownership changes and +pnpm run check:github-config for release workflow changes. Platform packaging +and native visual checks are separate from these contract checks. diff --git a/src/apps/data-migrator/Cargo.toml b/src/apps/data-migrator/Cargo.toml index 8c112cad8d..52b1c3fd20 100644 --- a/src/apps/data-migrator/Cargo.toml +++ b/src/apps/data-migrator/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openbitfun-data-migrator" -version.workspace = true +version = "0.1.0" authors.workspace = true edition.workspace = true description = "OpenBitFun offline legacy data migrator" @@ -17,18 +17,17 @@ path = "src/main.rs" tauri-build = { workspace = true } [dependencies] -openbitfun-core = { path = "../../crates/assembly/core", features = ["legacy-migration"] } +openbitfun-legacy-migration-adapters = { path = "../../crates/services/legacy-migration-adapters" } openbitfun-core-types = { path = "../../crates/contracts/core-types" } openbitfun-legacy-migration = { path = "../../crates/services/legacy-migration" } -openbitfun-product-capabilities = { path = "../../crates/assembly/product-capabilities" } openbitfun-product-domains = { path = "../../crates/contracts/product-domains", features = ["legacy-migration"] } serde = { workspace = true } serde_json = { workspace = true } tauri = { workspace = true } +uuid = { workspace = true } [dev-dependencies] tempfile = { workspace = true } -uuid = { workspace = true } [lints] workspace = true diff --git a/src/apps/data-migrator/README.md b/src/apps/data-migrator/README.md new file mode 100644 index 0000000000..b821573866 --- /dev/null +++ b/src/apps/data-migrator/README.md @@ -0,0 +1,130 @@ +# OpenBitFun Data Migrator + +[中文](README.zh-CN.md) + +A separate, optional desktop utility for importing old **BitFun** data into +**OpenBitFun**. It runs without installing or opening the main application, has +its own window and settings, and never starts or restarts Desktop. OpenBitFun +does not bundle, download, or launch it automatically. + +## Download and run + +Look for **OpenBitFun Data Migrator** releases with a `data-migrator-v*` tag on +the [release page](https://github.com/GCWing/OpenBitFun/releases?q=data-migrator-v&expanded=true). +These releases have their own version and assets; the main application's +installer does not contain the tool. If no migrator release is listed, build +from source using the commands below. + +| Platform | Download | Launch | +| --- | --- | --- | +| Windows x64 | `openbitfun-data-migrator-v-windows-x64.zip` | Extract, then double-click `openbitfun-data-migrator.exe` | +| macOS Apple Silicon | `openbitfun-data-migrator-v-macos-arm64.dmg` | Open the DMG and its Data Migrator app | +| macOS Intel | `openbitfun-data-migrator-v-macos-x64.dmg` | Open the DMG and its Data Migrator app | +| Linux x64 | `openbitfun-data-migrator-v-linux-x64.AppImage` | Make executable and launch in a desktop session | + +Windows needs the Microsoft Edge WebView2 runtime. macOS uses the system WebView; +Linux packages are built on Ubuntu 22.04. No login or network connection is +needed for migration. ARM Windows/Linux packages are not currently produced. + +1. Close BitFun, OpenBitFun, their CLI instances, and background data writers. +2. Open Data Migrator. Check the **source and destination** directories. All + four locations on each side can be edited; apply changes before scanning. +3. Select the data groups, scan, then run the preflight plan. +4. Review the destination and conflicts, then start migration. If known writers + remain open, the tool waits for them to stop; it does not terminate them. +5. Read the report. Sign in again or repair paths where indicated, close the + tool, and open OpenBitFun yourself. + +The UI uses the shared design-system tokens bundled offline, follows the system +light/dark/high-contrast setting, and offers English, Simplified Chinese, and +Traditional Chinese. + +## Data and compatibility + +The declared source range is BitFun `>=0.2.0,<1.0.0`; the archived integration +fixture is **0.2.19**. This is format-based support, not a claim that every old +release has been tested. The source must pass the probe and selected domain +validators. Unsupported or corrupt data is kept and reported. + +The destination is OpenBitFun: configuration schema **1**, workspace registry +format **1**, coordination database schema **2**, and the session, memory, +extension, and connection formats accepted by the shared storage owners in +this source revision. Unknown product/configuration schemas and newer SQLite +or session schemas fail validation. A future storage format requires a new +migrator release; matching application and tool version numbers is unnecessary. +Custom branded products are not supported by this tool. + +Migration covers settings and credentials; user Agents, Skills and MiniApps; +workspaces, sessions and task records; memories; and local connection/device +records. Existing destination values take priority or conflicts are preserved +under a new identity according to the domain policy. Runtime caches, locks, +process discovery files, built-in executable content and request traces are +excluded. Credentials that cannot be decrypted on the destination require +sign-in again. + +Source data is never automatically deleted. Writes use consistent snapshots, +staging, validation, backups, a migration lock and atomic replacement. Keep +both applications closed until the run finishes. Cancellation and window close +requests wait for an engine-declared safe boundary; already verified domains +may remain imported. + +## Resume and diagnose + +Plans, journals, reports, backups and staging live under: + +```text +/data/migrations/bitfun-to-openbitfun/runs// +``` + +Reopen the tool, select the original directories, and use **Saved migration +tasks → Review / resume task**. Recovery requires a valid plan and unchanged +source fingerprint and resumes through the journal; it does not expire after +ten minutes. Completed reports can be reopened. New scans create new tasks and +never replace earlier journals. Old handoff-based plans remain readable even +if their `request.json` has expired; select the original locations before +resuming them. Unreadable files are not deleted or reset. + +The tool remembers selected locations in its own `com.openbitfun.data-migrator` +application configuration directory. It does not write main-app onboarding or +reminder preferences. **Export failure diagnostics** writes a sanitized file +containing result codes and journal phases; full local reports and backups +can contain sensitive data and should stay private. + +This tool only operates on files accessible on the computer where it runs. +Remote workspace execution, remote control, Peer Device Mode and Detached +Dispatch are not execution surfaces for it. Run it on the data-owning computer; +importing stored connection records does not connect to or migrate a remote host. + +## Build and release + +From the repository root with Rust, Node, pnpm and the platform's Tauri build prerequisites: + +```bash +pnpm install +pnpm run data-migrator:dev # independent window; no Desktop or dev server +pnpm run data-migrator:build # independent release bundle +cargo build -p openbitfun-data-migrator --bin openbitfun-data-migrator +``` + +Direct Cargo builds embed the committed UI and design-system CSS. After changing +the token/theme owners run `pnpm run data-migrator:theme:generate`; the packaging +entry does this automatically. Desktop development/build commands do not build +the migrator. Shared Rust crates remain in the same source workspace to preserve +storage compatibility; there is no dependency on the main application's Core, +runtime assembly, Web UI, installer or updater. + +The tool version is maintained in its own `Cargo.toml` and `tauri.conf.json`. +The **Data Migrator Package** workflow builds four platform artifacts manually +or on `data-migrator-v` tags. Tag builds require the separate +`DATA_MIGRATOR_SIGNING_PRIVATE_KEY`, `DATA_MIGRATOR_SIGNING_PRIVATE_KEY_PASSWORD` +and `DATA_MIGRATOR_SIGNING_PUBKEY` secrets, verify checksums/signatures, and create +a **draft** release for review. Manual workflow runs only upload CI artifacts. +Publishing migrator releases does not start main-app packaging or update feeds. + +Each asset has a SHA-256 sidecar and a base64-encoded minisign `.sig`; the +release also carries `SHA256SUMS` and `data-migrator.minisign.pub`. Verify the key +against the maintainer's trusted key before checking signatures. Detached +signatures are distinct from Apple/Authenticode platform signing; the workflow +does not currently configure those certificates or macOS notarization. + +Focused checks and architecture rules are in [AGENTS.md](AGENTS.md). diff --git a/src/apps/data-migrator/README.zh-CN.md b/src/apps/data-migrator/README.zh-CN.md new file mode 100644 index 0000000000..4ba91993b9 --- /dev/null +++ b/src/apps/data-migrator/README.zh-CN.md @@ -0,0 +1,100 @@ +# OpenBitFun 独立数据迁移器 + +[English](README.md) + +这是一个可选的独立桌面工具,用于将旧版 **BitFun** 数据导入 **OpenBitFun**。 +无需安装或启动主应用;工具有自己的窗口、版本和配置,完成后只关闭自身。 +主应用不会捆绑、自动下载、自动启动迁移器,也不会因为旧版数据而阻止正常启动。 + +## 下载和使用 + +在 [GitHub Releases](https://github.com/GCWing/OpenBitFun/releases?q=data-migrator-v&expanded=true) +寻找 **OpenBitFun Data Migrator**、标签以 `data-migrator-v` 开头的独立发布。 +迁移器不在主应用安装包内;如果尚未列出独立发布,请按下文从源码构建。 + +| 系统 | 下载文件 | 启动方式 | +| --- | --- | --- | +| Windows x64 | `openbitfun-data-migrator-v<版本>-windows-x64.zip` | 解压后双击 `openbitfun-data-migrator.exe` | +| macOS Apple Silicon | `openbitfun-data-migrator-v<版本>-macos-arm64.dmg` | 打开 DMG 中的迁移器 | +| macOS Intel | `openbitfun-data-migrator-v<版本>-macos-x64.dmg` | 打开 DMG 中的迁移器 | +| Linux x64 | `openbitfun-data-migrator-v<版本>-linux-x64.AppImage` | 添加可执行权限,在桌面会话中启动 | + +Windows 需要 Microsoft Edge WebView2;macOS 使用系统 WebView,Linux 包以 Ubuntu 22.04 +为构建基线。迁移不需要登录或联网,目前不产出 Windows/Linux ARM 安装包。 + +1. 关闭 BitFun、OpenBitFun、CLI 实例及其后台数据写入进程。 +2. 启动迁移器,检查**来源和目标目录**。两侧各有设置与数据、主目录数据、Skills、SSH + 四个位置;如需修改,先点击“使用这些目录”。 +3. 选择迁移范围,扫描数据,再运行预检。 +4. 确认目标、范围和冲突后开始迁移。发现已知写入进程时会等待其退出,不会强制终止进程。 +5. 查看结果,根据提示重新登录或修复路径,关闭迁移器,再自行打开 OpenBitFun。 + +界面使用离线打包的共享设计系统,跟随系统深浅色和高对比度设置,并提供中英文切换。 + +## 支持范围与数据保护 + +声明支持的来源是 BitFun `>=0.2.0,<1.0.0`,仓库保存的集成验证样本是 **0.2.19**。 +支持以实际数据格式和各领域校验为准,不代表已验证范围内的每个旧版本。 +未知格式、损坏数据会明确报告并保留。 + +目标是 OpenBitFun:配置 schema **1**、工作区格式 **1**、任务协调数据库 schema **2**, +以及此源码版本共享存储模块支持的会话、记忆、扩展和连接格式。未知产品/配置格式以及 +超出支持范围的数据库、会话版本会被拒绝。未来数据格式变更需要发布新的迁移器, +工具版本无需与主应用版本相同;此工具不支持定制品牌产品的数据。 + +可迁移设置与凭据、用户 Agents/Skills/MiniApps、工作区与会话及任务记录、记忆、 +本机保存的远程连接与设备记录。已有目标值优先,部分冲突会按领域规则保留双方。 +缓存、锁、进程发现文件、内置可执行内容和请求追踪不迁移;不可解密的凭据需重新登录。 + +来源不会被自动删除。写入使用一致性快照、暂存、校验、备份、迁移锁和原子替换。 +迁移期间请保持相关应用关闭;取消或关闭窗口会等到安全边界,已验证完成的领域可能已导入。 + +## 中断恢复与诊断 + +任务保存在: + +```text +<目标设置与数据目录>/data/migrations/bitfun-to-openbitfun/runs/<任务 ID>/ +``` + +重新打开迁移器,选择原来的目录,在“历史迁移任务”中查看或恢复。恢复会校验计划、 +来源指纹和原目录,并沿用日志,不受原来十分钟交接请求有效期限制。 +已完成任务仍可查看报告;新扫描会创建新任务,不会覆盖旧日志。 +旧版交接流程留下的计划与日志仍可读取,即使 `request.json` 已过期;恢复前须选择原目录。 +无法读取的任务文件不会被删除或重置。 + +迁移器只在自己的 `com.openbitfun.data-migrator` 配置目录中记住所选位置,不写主应用的 +引导或提醒状态。“导出失败诊断”生成包含结果码和执行阶段的去敏文件。 +完整本地报告与备份可能含敏感信息,请保留在本机。 + +迁移器只操作运行电脑可访问的文件,不接入远程工作区执行、远程控制、Peer Device Mode +或 Detached Dispatch。请在数据所在电脑上运行;迁移连接记录并不连接或迁移远端主机。 + +## 开发与独立发布 + +安装 Rust、Node、pnpm 和对应系统的 Tauri 构建依赖后,在仓库根目录执行: + +```bash +pnpm install +pnpm run data-migrator:dev # 独立窗口,无需主应用或开发服务器 +pnpm run data-migrator:build # 独立发行包 +cargo build -p openbitfun-data-migrator --bin openbitfun-data-migrator +``` + +直接 Cargo 构建使用已提交的静态 UI 和设计系统 CSS。修改主题源后运行 +`pnpm run data-migrator:theme:generate`;独立打包入口会自动生成。 +主应用开发和构建不再构建迁移器。两者仍在同一源码工作区共享稳定的数据契约和存储模块, +以保证格式一致,但迁移器不依赖主应用 Core、运行时组装、Web UI、安装器或更新器。 + +版本由迁移器自己的 `Cargo.toml` 和 `tauri.conf.json` 维护。**Data Migrator Package** +工作流支持手动构建,或通过 `data-migrator-v<版本>` 标签生成独立发布草稿。 +标签发布使用专用的 `DATA_MIGRATOR_SIGNING_PRIVATE_KEY`、 +`DATA_MIGRATOR_SIGNING_PRIVATE_KEY_PASSWORD`、`DATA_MIGRATOR_SIGNING_PUBKEY`, +完成签名和校验后创建草稿,审核后再发布。手动运行只生成 CI 构建产物。 +迁移器发布不会触发主应用打包或更新源。 + +每个产物带 SHA-256 校验文件和 base64 编码的 minisign `.sig`,同时提供 +`SHA256SUMS` 与 `data-migrator.minisign.pub`。验证签名前应通过可信渠道确认公钥。 +独立文件签名不等于 Apple/Authenticode 系统代码签名;当前工作流尚未配置这些证书及 macOS 公证。 + +开发约束和针对性检查见 [AGENTS.md](AGENTS.md)。 diff --git a/src/apps/data-migrator/RELEASE.md b/src/apps/data-migrator/RELEASE.md new file mode 100644 index 0000000000..68f4fc1fc1 --- /dev/null +++ b/src/apps/data-migrator/RELEASE.md @@ -0,0 +1,16 @@ +Optional, independently downloaded BitFun → OpenBitFun data migrator. + +Extract the Windows ZIP and double-click the migrator, or open the macOS/Linux +package. Close all BitFun/OpenBitFun data writers first. Review source, +destination and the preflight plan before importing. Source data is retained; +interrupted runs can be resumed from Saved migration tasks. + +Declared source range: BitFun >=0.2.0,<1.0.0. Archived integration fixture: 0.2.19. +The tool uses explicit storage-format validation and rejects unsupported data. +No main application installation, login, network or restart handoff is required. + +See the README files at this release tag for compatibility, recovery, build and +signature verification details. Every package carries a SHA-256 sidecar and a +verified detached minisign signature. Platform code signing/notarization is not +configured by this workflow. This draft requires maintainer review and platform +launch checks before publication. diff --git a/src/apps/data-migrator/icons/openbitfun-app-icon.icns b/src/apps/data-migrator/icons/openbitfun-app-icon.icns new file mode 100644 index 0000000000000000000000000000000000000000..976eec4c27c55cb0fb9b4f98e26f55dd61f09c83 GIT binary patch literal 210447 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

C}#zO=LQ#>q+{lH&k^?cMOVgOWGk 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 z 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 literal 0 HcmV?d00001 diff --git a/src/apps/data-migrator/icons/openbitfun-app-icon.ico b/src/apps/data-migrator/icons/openbitfun-app-icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..2d88a5a67784d104d7092fb438327d36647c31b1 GIT binary patch literal 23274 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 literal 0 HcmV?d00001 diff --git a/src/apps/data-migrator/icons/openbitfun-app-icon.png b/src/apps/data-migrator/icons/openbitfun-app-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..2cfdd17e9453d91c999c9c7d046dbc650abf89e8 GIT binary patch literal 104027 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�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 literal 0 HcmV?d00001 diff --git a/src/apps/data-migrator/src/app_state.rs b/src/apps/data-migrator/src/app_state.rs index c4d6a04d3f..1b2eb172b0 100644 --- a/src/apps/data-migrator/src/app_state.rs +++ b/src/apps/data-migrator/src/app_state.rs @@ -1,36 +1,23 @@ -use openbitfun_core::legacy_migration::adapters_for_groups; -use openbitfun_core_types::product_identity::product_id; use openbitfun_legacy_migration::{ - blocking_writer_processes_for_product, export_failure_diagnostics, launch_trusted_executable, - probe_legacy_source, CancellationToken, HandoffDisposition, HandoffStore, LegacyMigrationError, - LegacyMigrationResult, MigrationEngine, MigrationLayout, MigrationOnboardingStore, - MigrationRoots, NoCrashInjection, ProbeLimits, TrustedInstallationResolver, WriterProcess, + atomic_write_json, blocking_writer_processes, export_failure_diagnostics, list_tasks, + load_task, probe_legacy_source, save_task, CancellationToken, LegacyMigrationError, + LegacyMigrationResult, MigrationEngine, MigrationLayout, MigrationRoots, NoCrashInjection, + ProbeLimits, SavedMigrationTask, WriterProcess, }; -use openbitfun_product_capabilities::{product_assembly_plan_for_profile, DeliveryProfile}; +use openbitfun_legacy_migration_adapters::adapters_for_groups; use openbitfun_product_domains::legacy_migration::{ FindingSeverity, LegacySourceDescriptor, MigrationPhase, MigrationPlan, MigrationProgressEvent, - MigrationPromptChoice, MigrationRunReport, MigrationRunStatus, MigrationSelection, - MigratorHandoffRequest, MigratorProtocolCapabilities, MigratorRequestMode, ScanFinding, - CURRENT_MIGRATION_FORMAT_VERSION, + MigrationRunReport, MigrationRunStatus, MigrationSelection, ScanFinding, }; use serde::Serialize; -use std::ffi::OsStr; -use std::path::Path; +use std::path::PathBuf; use std::sync::{Arc, Mutex, MutexGuard}; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use std::time::Duration; -const RELEASE_CHANNEL: &str = match option_env!("OPENBITFUN_RELEASE_CHANNEL") { - Some(value) => value, - None => "stable", -}; -const DESKTOP_BINARY_NAME: &str = match option_env!("OPENBITFUN_DESKTOP_BINARY_NAME") { - Some(value) => value, - None => "openbitfun-desktop", -}; -const DATA_MIGRATOR_BINARY_NAME: &str = match option_env!("OPENBITFUN_DATA_MIGRATOR_BINARY_NAME") { - Some(value) => value, - None => "openbitfun-data-migrator", -}; +#[derive(Debug, Clone)] +struct TaskRequest { + run_id: String, +} #[derive(Debug, Clone, PartialEq, Eq, Serialize)] #[serde(rename_all = "camelCase")] @@ -82,9 +69,14 @@ impl CommandError { "This legacy BitFun data format is not supported by this migrator.", false, ), + LegacyMigrationError::UnsupportedTarget(_) => Self::new( + "unsupported_target", + "The destination data format is not supported. Use a compatible migrator or an empty destination.", + true, + ), LegacyMigrationError::InvalidRequest(_) => Self::new( - "invalid_handoff", - "The migration handoff could not be authenticated or has expired.", + "invalid_task", + "The selected migration task or data locations are invalid.", false, ), LegacyMigrationError::InvalidPlan(_) => Self::new( @@ -167,10 +159,9 @@ impl CommandError { #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] pub(crate) struct MigratorView { - pub delivery_profile: String, - pub restart_desktop_on_finish: bool, - pub protocol: MigratorProtocolCapabilities, - pub mode: MigratorRequestMode, + pub tool_version: String, + pub locations: MigrationRoots, + pub saved_tasks: Vec, pub source: Option, pub selection: MigrationSelection, pub findings: Vec, @@ -194,8 +185,10 @@ pub(crate) struct DiagnosticsExportView { #[derive(Debug)] struct MigratorSession { roots: MigrationRoots, - request: MigratorHandoffRequest, - disposition: HandoffDisposition, + request: TaskRequest, + recovery: bool, + saved_tasks: Vec, + settings_path: PathBuf, source: Option, selection: MigrationSelection, findings: Vec, @@ -215,80 +208,111 @@ pub(crate) struct MigratorCoordinator { } impl MigratorCoordinator { - pub(crate) fn bootstrap(run_id: &str) -> LegacyMigrationResult { - Self::bootstrap_with( - run_id, - MigrationRoots::resolve_current_user()?, - product_id(), - RELEASE_CHANNEL, - ) - } - - fn bootstrap_with( - run_id: &str, - roots: MigrationRoots, - expected_product_id: &str, - expected_release_channel: &str, - ) -> LegacyMigrationResult { - let product_plan = product_assembly_plan_for_profile(DeliveryProfile::DataMigrator); - if !product_plan.capability_set().ids().is_empty() - || !product_plan.capability_assembly().agent_ids().is_empty() - || !product_plan.feature_groups().is_empty() - { - return Err(LegacyMigrationError::InvalidRequest( - "data migrator delivery profile unexpectedly selected runtime capabilities" - .to_string(), - )); + pub(crate) fn bootstrap(settings_path: PathBuf) -> LegacyMigrationResult { + let defaults = MigrationRoots::current_user_locations()?; + let layout = MigrationLayout::new(&defaults, "preferences"); + let saved = layout.read_json::(&settings_path); + let (roots, error) = match saved { + Ok(Some(roots)) => (roots, None), + Ok(None) => (defaults, None), + Err(error) => (defaults, Some(CommandError::from_legacy(&error))), + }; + let coordinator = Self::bootstrap_with(roots, settings_path); + if let Some(error) = error { + coordinator.lock().error = Some(error); } + Ok(coordinator) + } - let store = HandoffStore::new(roots.clone(), expected_product_id, expected_release_channel); - let handoff = store.load_request(run_id, now_ms())?; - let request = handoff.request().clone(); - let source = probe_bound_source(&roots, &request)?; - let plan = store.load_authorized_plan(&handoff)?; - let report = handoff - .layout() - .read_json::(&handoff.layout().report_path())?; - let selection = plan - .as_ref() - .map(|plan| plan.selection.clone()) - .unwrap_or_else(|| request.selection.clone()); - let findings = plan - .as_ref() - .map(|plan| plan.findings.clone()) - .unwrap_or_default(); - let status = report - .as_ref() - .map(|report| report.status) - .unwrap_or_else(|| { - if plan.is_some() { - MigrationRunStatus::Planned - } else if source.is_some() { + fn bootstrap_with(roots: MigrationRoots, settings_path: PathBuf) -> Self { + let probe = crate::locations::validate(&roots) + .and_then(|_| probe_legacy_source(&roots, ProbeLimits::default())); + let (source, mut error) = match probe { + Ok(source) => (source, None), + Err(error) => (None, Some(CommandError::from_legacy(&error))), + }; + let saved_tasks = match list_tasks(&roots) { + Ok(tasks) => tasks, + Err(failure) => { + error = Some(CommandError::from_legacy(&failure)); + Vec::new() + } + }; + Self { + session: Arc::new(Mutex::new(MigratorSession { + roots, + settings_path, + saved_tasks, + request: TaskRequest { + run_id: uuid::Uuid::new_v4().to_string(), + }, + recovery: false, + status: if source.is_some() { MigrationRunStatus::Discovered } else { MigrationRunStatus::default() - } - }); - let blockers = writer_processes(request.caller_process_id)?; - - Ok(Self { - session: Arc::new(Mutex::new(MigratorSession { - roots, - request, - disposition: handoff.disposition(), + }, source, - selection, - findings, - plan, - report, + error, + selection: MigrationSelection::all(), + findings: Vec::new(), + plan: None, + report: None, progress: None, - blockers, - status, + blockers: Vec::new(), running: false, - error: None, cancellation: CancellationToken::default(), })), - }) + } + } + + pub(crate) fn set_locations( + &self, + roots: MigrationRoots, + ) -> Result { + let mut session = self.lock(); + if session.running { + return Err(CommandError::operation_in_progress()); + } + crate::locations::validate(&roots).map_err(|error| CommandError::from_legacy(&error))?; + atomic_write_json(&session.settings_path, &roots) + .map_err(|error| CommandError::from_legacy(&error))?; + let replacement = Self::bootstrap_with(roots, session.settings_path.clone()); + std::mem::swap(&mut *session, &mut *replacement.lock()); + Ok(snapshot(&session)) + } + + pub(crate) fn new_task(&self) -> Result { + let mut session = self.lock(); + if session.running { + return Err(CommandError::operation_in_progress()); + } + let replacement = + Self::bootstrap_with(session.roots.clone(), session.settings_path.clone()); + std::mem::swap(&mut *session, &mut *replacement.lock()); + Ok(snapshot(&session)) + } + + pub(crate) fn resume_task(&self, run_id: &str) -> Result { + let mut session = self.lock(); + if session.running { + return Err(CommandError::operation_in_progress()); + } + let (plan, report) = + load_task(&session.roots, run_id).map_err(|error| CommandError::from_legacy(&error))?; + session.request.run_id = run_id.to_string(); + session.selection = plan.selection.clone(); + session.findings = plan.findings.clone(); + session.status = report + .as_ref() + .map(|report| report.status) + .unwrap_or(MigrationRunStatus::Planned); + session.plan = Some(plan); + session.report = report; + session.progress = None; + session.recovery = true; + session.error = None; + Ok(snapshot(&session)) } pub(crate) fn snapshot(&self) -> MigratorView { @@ -333,12 +357,12 @@ impl MigratorCoordinator { fn scan_background( &self, roots: MigrationRoots, - request: MigratorHandoffRequest, + _request: TaskRequest, selection: MigrationSelection, cancellation: CancellationToken, ) { let result = (|| { - let source = probe_bound_source(&roots, &request)?.ok_or_else(|| { + let source = probe_legacy_source(&roots, ProbeLimits::default())?.ok_or_else(|| { LegacyMigrationError::UnsupportedSource( "no supported legacy BitFun data was discovered".to_string(), ) @@ -395,24 +419,25 @@ impl MigratorCoordinator { fn prepare_background( &self, roots: MigrationRoots, - request: MigratorHandoffRequest, + request: TaskRequest, selection: MigrationSelection, cancellation: CancellationToken, ) { let result = (|| { - let source = probe_bound_source(&roots, &request)?.ok_or_else(|| { + let source = probe_legacy_source(&roots, ProbeLimits::default())?.ok_or_else(|| { LegacyMigrationError::UnsupportedSource( "no supported legacy BitFun data was discovered".to_string(), ) })?; - let engine = migration_engine(roots, &selection)?; + let engine = migration_engine(roots.clone(), &selection)?; let plan = engine.plan_with_run_id( &source, selection.clone(), request.run_id.clone(), &cancellation, )?; - let blockers = writer_processes(request.caller_process_id)?; + save_task(&roots, &plan)?; + let blockers = writer_processes()?; Ok::<_, LegacyMigrationError>((source, plan, blockers)) })(); @@ -424,6 +449,8 @@ impl MigratorCoordinator { session.selection = selection; session.findings = plan.findings.clone(); session.plan = Some(plan); + session.saved_tasks = + list_tasks(&session.roots).unwrap_or_else(|_| session.saved_tasks.clone()); session.report = None; session.blockers = blockers; session.status = MigrationRunStatus::Planned; @@ -445,8 +472,10 @@ impl MigratorCoordinator { } pub(crate) fn refresh_blockers(&self) -> Result { - let caller_process_id = self.lock().request.caller_process_id; - match writer_processes(caller_process_id) { + if self.is_running() { + return Err(CommandError::operation_in_progress()); + } + match writer_processes() { Ok(blockers) => { let mut session = self.lock(); session.blockers = blockers; @@ -480,13 +509,25 @@ impl MigratorCoordinator { true, )); } - let store = HandoffStore::new(session.roots.clone(), product_id(), RELEASE_CHANNEL); - let handoff = store - .load_request(&session.request.run_id, now_ms()) - .map_err(|error| CommandError::from_legacy(&error))?; - store - .authorize_plan(&handoff, &plan, now_ms()) + let (saved, _) = load_task(&session.roots, &session.request.run_id) .map_err(|error| CommandError::from_legacy(&error))?; + if saved != plan { + return Err(CommandError::new( + "stale_plan", + "Review the saved plan again before continuing.", + true, + )); + } + if matches!( + session.status, + MigrationRunStatus::Completed | MigrationRunStatus::CompletedWithWarnings + ) { + return Err(CommandError::new( + "task_completed", + "This task is already complete. Start a new scan to import other data.", + true, + )); + } session.cancellation = CancellationToken::default(); session.running = true; @@ -532,66 +573,29 @@ impl MigratorCoordinator { self.lock().running } - pub(crate) fn finish_and_restart( - &self, - choice: MigrationPromptChoice, - ) -> Result<(), CommandError> { - self.finish_with_restart(choice, || self.restart_desktop()) - } - - fn finish_with_restart( - &self, - choice: MigrationPromptChoice, - restart: impl FnOnce() -> LegacyMigrationResult<()>, - ) -> Result<(), CommandError> { + pub(crate) fn finish(&self) -> Result<(), CommandError> { if self.is_running() { return Err(CommandError::operation_in_progress()); } - if choice == MigrationPromptChoice::Unset { - return Err(CommandError::new( - "invalid_prompt_choice", - "Choose whether to migrate now, be reminded later, or stop reminders.", - true, - )); - } - if choice == MigrationPromptChoice::MigrateNow && self.lock().report.is_none() { - return Err(CommandError::new( - "migration_result_required", - "A completed or recoverable migration report is required before finishing.", - true, - )); - } - - let result = (|| { - self.persist_prompt_choice(choice)?; - restart() - })(); - if let Err(error) = result { - let mut session = self.lock(); - let command_error = self.finish_error_locked(&mut session, &error); - return Err(command_error); - } Ok(()) } - pub(crate) fn close_and_restart(&self) -> Result<(), CommandError> { - let choice = if self.lock().report.is_some() { - MigrationPromptChoice::MigrateNow - } else { - MigrationPromptChoice::RemindLater - }; - self.finish_and_restart(choice) - } - fn begin_operation( &self, selection: &MigrationSelection, - ) -> Result<(MigrationRoots, MigratorHandoffRequest, CancellationToken), CommandError> { + ) -> Result<(MigrationRoots, TaskRequest, CancellationToken), CommandError> { let mut session = self.lock(); if session.running { return Err(CommandError::operation_in_progress()); } - validate_selection(&session.request, selection)?; + validate_selection(selection)?; + // A changed scan/selection is a new task. Never overwrite an old journal. + session.request.run_id = uuid::Uuid::new_v4().to_string(); + session.recovery = false; + session.selection = selection.clone(); + session.plan = None; + session.report = None; + session.findings.clear(); session.cancellation = CancellationToken::default(); session.running = true; session.error = None; @@ -634,7 +638,7 @@ impl MigratorCoordinator { fn execute_background( &self, roots: MigrationRoots, - request: MigratorHandoffRequest, + _request: TaskRequest, plan: MigrationPlan, cancellation: CancellationToken, ) { @@ -643,7 +647,7 @@ impl MigratorCoordinator { self.finish_cancelled_before_execution(&plan); return; } - match writer_processes(request.caller_process_id) { + match writer_processes() { Ok(blockers) => { let done = blockers.is_empty(); let mut session = self.lock(); @@ -694,8 +698,8 @@ impl MigratorCoordinator { session.status = report.status; session.report = Some(report); session.error = None; - drop(session); - let _ = self.persist_prompt_choice(MigrationPromptChoice::MigrateNow); + session.saved_tasks = + list_tasks(&session.roots).unwrap_or_else(|_| session.saved_tasks.clone()); } Err(error) => { let layout = MigrationLayout::new(&roots, &plan.run_id); @@ -734,49 +738,6 @@ impl MigratorCoordinator { session.progress = Some(progress); } - fn persist_prompt_choice(&self, choice: MigrationPromptChoice) -> LegacyMigrationResult<()> { - let session = self.lock(); - let store = MigrationOnboardingStore::new(session.roots.clone()); - let request = &session.request; - let source = session.source.as_ref(); - let has_report = session.report.is_some(); - store.update(|state| { - state.format_version = CURRENT_MIGRATION_FORMAT_VERSION; - if let Some(source) = source { - state.source_fingerprint = source.source_fingerprint.clone(); - state.detected_at_ms.get_or_insert_with(now_ms); - } - state.choice = choice; - state.last_prompted_version = Some(env!("CARGO_PKG_VERSION").to_string()); - state.run_id = Some(request.run_id.clone()); - state.handled_run_id = Some(request.run_id.clone()); - if has_report { - state.last_report_run_id = Some(request.run_id.clone()); - } - })?; - Ok(()) - } - - fn restart_desktop(&self) -> LegacyMigrationResult<()> { - let run_id = self.lock().request.run_id.clone(); - #[cfg(debug_assertions)] - if let Some(directory) = std::env::var_os("OPENBITFUN_DEV_MIGRATION_DIR") { - return request_dev_restart(Path::new(&directory), &run_id); - } - let current = std::env::current_exe().map_err(|error| LegacyMigrationError::Io { - path: Path::new(DATA_MIGRATOR_BINARY_NAME).to_path_buf(), - source: error, - })?; - let executable = TrustedInstallationResolver::resolve_sibling( - ¤t, - DATA_MIGRATOR_BINARY_NAME, - DESKTOP_BINARY_NAME, - )?; - let arguments = [OsStr::new("--legacy-migration-run-id"), OsStr::new(&run_id)]; - launch_trusted_executable(&executable, &arguments)?; - Ok(()) - } - fn finish_error_locked( &self, session: &mut MigratorSession, @@ -806,46 +767,20 @@ impl MigratorCoordinator { } } -#[cfg(debug_assertions)] -fn request_dev_restart(directory: &Path, run_id: &str) -> LegacyMigrationResult<()> { - // Only the development supervisor supplies this private, per-launch channel. - // The migration request never supplies paths or executable names. - let handoff_path = directory.join("handoff.json"); - let bytes = std::fs::read(&handoff_path).map_err(|source| LegacyMigrationError::Io { - path: handoff_path, - source, - })?; - let handoff: serde_json::Value = serde_json::from_slice(&bytes).map_err(|_| { - LegacyMigrationError::InvalidRequest("invalid development restart handoff".to_string()) - })?; - if handoff["runId"].as_str() != Some(run_id) - || handoff["pid"].as_u64() != Some(u64::from(std::process::id())) - { - return Err(LegacyMigrationError::InvalidRequest( - "development restart handoff does not match this migrator".to_string(), - )); - } - openbitfun_legacy_migration::atomic_write_json( - &directory.join("restart.json"), - &serde_json::json!({ "runId": run_id }), - ) -} - fn migration_engine( roots: MigrationRoots, selection: &MigrationSelection, ) -> LegacyMigrationResult { + crate::locations::validate(&roots)?; + openbitfun_legacy_migration_adapters::validate_target(&roots)?; MigrationEngine::new(roots, adapters_for_groups(selection)) } -fn writer_processes(caller_process_id: u32) -> LegacyMigrationResult> { - blocking_writer_processes_for_product(caller_process_id, &[DESKTOP_BINARY_NAME]) +fn writer_processes() -> LegacyMigrationResult> { + blocking_writer_processes(0) } -fn validate_selection( - request: &MigratorHandoffRequest, - selection: &MigrationSelection, -) -> Result<(), CommandError> { +fn validate_selection(selection: &MigrationSelection) -> Result<(), CommandError> { if selection.groups.is_empty() { return Err(CommandError::new( "empty_selection", @@ -853,51 +788,16 @@ fn validate_selection( true, )); } - if request.mode == MigratorRequestMode::Execute && request.selection != *selection { - return Err(CommandError::new( - "selection_mismatch", - "The selected groups differ from the scope confirmed in OpenBitFun.", - false, - )); - } Ok(()) } -fn probe_bound_source( - roots: &MigrationRoots, - request: &MigratorHandoffRequest, -) -> LegacyMigrationResult> { - let source = probe_legacy_source(roots, ProbeLimits::default())?; - if let Some(source) = &source { - if request - .source_id - .as_deref() - .is_some_and(|source_id| source_id != source.source_id) - || request - .source_fingerprint - .as_deref() - .is_some_and(|fingerprint| fingerprint != source.source_fingerprint) - { - return Err(LegacyMigrationError::InvalidRequest( - "discovered source does not match the authenticated handoff".to_string(), - )); - } - } else if request.source_id.is_some() || request.source_fingerprint.is_some() { - return Err(LegacyMigrationError::InvalidRequest( - "authenticated handoff source is no longer present".to_string(), - )); - } - Ok(source) -} - fn snapshot(session: &MigratorSession) -> MigratorView { let plan = session.plan.as_ref().map(redact_plan_for_ui); let report = session.report.as_ref().map(redact_report_for_ui); MigratorView { - delivery_profile: DeliveryProfile::DataMigrator.id().to_string(), - restart_desktop_on_finish: true, - protocol: MigratorProtocolCapabilities::current(), - mode: session.request.mode, + tool_version: env!("CARGO_PKG_VERSION").to_string(), + locations: session.roots.clone(), + saved_tasks: session.saved_tasks.clone(), source: session.source.clone(), selection: session.selection.clone(), findings: session @@ -911,14 +811,18 @@ fn snapshot(session: &MigratorSession) -> MigratorView { .source .as_ref() .is_some_and(|source| source.supported) - && !session.running, + && !session.running + && !matches!( + session.status, + MigrationRunStatus::Completed | MigrationRunStatus::CompletedWithWarnings + ), plan, report, progress: session.progress.clone(), blockers: session.blockers.clone(), status: session.status, running: session.running, - recovery: session.disposition == HandoffDisposition::Recovery, + recovery: session.recovery, error: session.error.clone(), } } @@ -973,24 +877,11 @@ fn status_for_phase(phase: MigrationPhase) -> MigrationRunStatus { } } -fn now_ms() -> i64 { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap_or_default() - .as_millis() - .try_into() - .unwrap_or(i64::MAX) -} - #[cfg(test)] mod tests { use super::*; - use openbitfun_product_domains::legacy_migration::{ - MigratorProtocolCapability, MigratorRequestOrigin, CURRENT_MIGRATOR_PROTOCOL_VERSION, - }; - use std::collections::BTreeSet; use std::fs; - + use std::path::Path; fn fixture_roots(root: &Path) -> MigrationRoots { MigrationRoots { legacy_user_root: root.join("legacy/user"), @@ -1004,103 +895,78 @@ mod tests { } } - fn handoff_request() -> MigratorHandoffRequest { - let current = now_ms(); - MigratorHandoffRequest { - protocol_version: CURRENT_MIGRATOR_PROTOCOL_VERSION, - mode: MigratorRequestMode::Onboarding, - origin: MigratorRequestOrigin::FirstLaunch, - run_id: uuid::Uuid::new_v4().to_string(), - nonce: uuid::Uuid::new_v4().to_string(), - selection: MigrationSelection::all(), - caller_process_id: u32::MAX, - product_id: "openbitfun".to_string(), - release_channel: "stable".to_string(), - created_at_ms: current, - expires_at_ms: current + 60_000, - required_capabilities: BTreeSet::from([ - MigratorProtocolCapability::ReadOnlyScan, - MigratorProtocolCapability::JournalRecovery, - ]), - ..MigratorHandoffRequest::default() - } - } - - fn write_probe_fixture(roots: &MigrationRoots) { - let config = roots.legacy_user_root.join("config"); - fs::create_dir_all(&config).unwrap(); - fs::write(config.join("app.json"), br#"{"version":"0.2.19"}"#).unwrap(); - } - #[test] - fn bootstrap_consumes_the_real_non_agent_delivery_profile() { - let temporary = tempfile::tempdir().unwrap(); - let roots = fixture_roots(temporary.path()); - write_probe_fixture(&roots); - let request = handoff_request(); - HandoffStore::new(roots.clone(), "openbitfun", "stable") - .write_request(&request, now_ms()) - .unwrap(); - - let coordinator = - MigratorCoordinator::bootstrap_with(&request.run_id, roots, "openbitfun", "stable") - .unwrap(); + fn starts_without_desktop_request_or_data() { + let temp = tempfile::tempdir().unwrap(); + let roots = fixture_roots(temp.path()); + let coordinator = MigratorCoordinator::bootstrap_with( + roots.clone(), + temp.path().join("tool/locations.json"), + ); let view = coordinator.snapshot(); - - assert_eq!(view.delivery_profile, "data-migrator"); - assert!(view.restart_desktop_on_finish); - assert_eq!(view.mode, MigratorRequestMode::Onboarding); - assert!(view.source.is_some()); - assert!(!view.recovery); - } - - #[test] - fn execute_handoff_rejects_a_scope_change() { - let mut request = handoff_request(); - request.mode = MigratorRequestMode::Execute; - let mut changed = request.selection.clone(); - changed - .groups - .remove(&openbitfun_product_domains::legacy_migration::MigrationGroupId::Memory); - - let error = validate_selection(&request, &changed).unwrap_err(); - assert_eq!(error.code, "selection_mismatch"); + assert!(view.source.is_none()); + assert!(view.error.is_none()); + assert!(!view.running); + coordinator.finish().unwrap(); + assert!(!roots.target_user_root.exists()); } #[test] - fn cancelled_scan_finishes_in_an_explicit_cancelled_state() { - let temporary = tempfile::tempdir().unwrap(); - let roots = fixture_roots(temporary.path()); - write_probe_fixture(&roots); - let request = handoff_request(); - HandoffStore::new(roots.clone(), "openbitfun", "stable") - .write_request(&request, now_ms()) - .unwrap(); - let coordinator = MigratorCoordinator::bootstrap_with( - &request.run_id, - roots.clone(), - "openbitfun", - "stable", + fn cancelled_scan_preserves_safe_boundary() { + let temp = tempfile::tempdir().unwrap(); + let roots = fixture_roots(temp.path()); + fs::create_dir_all(roots.legacy_user_root.join("config")).unwrap(); + fs::write( + roots.legacy_user_root.join("config/app.json"), + br#"{"version":"0.2.19"}"#, ) .unwrap(); + let coordinator = + MigratorCoordinator::bootstrap_with(roots, temp.path().join("tool/locations.json")); let selection = MigrationSelection::all(); let (roots, request, cancellation) = coordinator.begin_operation(&selection).unwrap(); + assert!(coordinator.finish().is_err()); + assert!(coordinator.new_task().is_err()); cancellation.cancel(); coordinator.scan_background(roots, request, selection, cancellation); + assert_eq!(coordinator.snapshot().status, MigrationRunStatus::Cancelled); + assert!(!coordinator.is_running()); + } - let view = coordinator.snapshot(); - assert!(!view.running); - assert_eq!(view.status, MigrationRunStatus::Cancelled); - assert_eq!( - view.error.map(|error| error.code).as_deref(), - Some("cancelled") - ); - assert_eq!( - view.progress.map(|progress| progress.code).as_deref(), - Some("migration_cancelled") - ); + #[test] + fn rejects_nested_locations_without_writing_preferences() { + let temp = tempfile::tempdir().unwrap(); + let mut roots = fixture_roots(temp.path()); + let settings = temp.path().join("tool/locations.json"); + let coordinator = MigratorCoordinator::bootstrap_with(roots.clone(), settings.clone()); + roots.target_home_root = roots.legacy_user_root.join("nested"); + assert!(coordinator.set_locations(roots).is_err()); + assert!(!settings.exists()); } + #[test] + fn unsupported_target_is_rejected_before_scan_or_any_writes() { + let temp = tempfile::tempdir().unwrap(); + let roots = fixture_roots(temp.path()); + assert!(migration_engine(roots.clone(), &MigrationSelection::all()).is_ok()); + assert!(!roots.target_user_root.exists()); + let target = roots.target_user_root.join("config/app.json"); + for value in [ + serde_json::json!({"product_id":"openbitfun", "schema_version":999, "version":"9.0.0"}), + serde_json::json!({"product_id":"other-product", "schema_version":1, "version":"1.0.0"}), + serde_json::json!({"version":"0.2.19"}), + ] { + atomic_write_json(&target, &value).unwrap(); + let before = fs::read(&target).unwrap(); + assert!(matches!( + migration_engine(roots.clone(), &MigrationSelection::all()), + Err(LegacyMigrationError::UnsupportedTarget(_)) + )); + assert_eq!(fs::read(&target).unwrap(), before); + assert!(!roots.migration_root().exists()); + assert!(!roots.legacy_user_root.exists()); + } + } #[test] fn command_errors_do_not_expose_storage_paths() { let error = LegacyMigrationError::Io { @@ -1141,93 +1007,4 @@ mod tests { assert!(!serialized.contains("private")); assert!(!serialized.contains("session-state")); } - - #[test] - fn dismissing_onboarding_persists_choice_and_restarts_desktop() { - for choice in [ - MigrationPromptChoice::DoNotRemind, - MigrationPromptChoice::RemindLater, - ] { - let temporary = tempfile::tempdir().unwrap(); - let roots = fixture_roots(temporary.path()); - write_probe_fixture(&roots); - let request = handoff_request(); - HandoffStore::new(roots.clone(), "openbitfun", "stable") - .write_request(&request, now_ms()) - .unwrap(); - let coordinator = MigratorCoordinator::bootstrap_with( - &request.run_id, - roots.clone(), - "openbitfun", - "stable", - ) - .unwrap(); - let mut restarted = false; - coordinator - .finish_with_restart(choice, || { - // Restart must observe the saved choice and one-time restart receipt. - let state = MigrationOnboardingStore::new(roots.clone()).load().unwrap(); - assert_eq!(state.choice, choice); - assert_eq!( - state.handled_run_id.as_deref(), - Some(request.run_id.as_str()) - ); - restarted = true; - Ok(()) - }) - .unwrap(); - assert!(restarted); - assert!(coordinator.snapshot().restart_desktop_on_finish); - } - } - - #[test] - fn failed_restart_stays_visible_and_keeps_the_saved_choice() { - let temporary = tempfile::tempdir().unwrap(); - let roots = fixture_roots(temporary.path()); - write_probe_fixture(&roots); - let request = handoff_request(); - HandoffStore::new(roots.clone(), "openbitfun", "stable") - .write_request(&request, now_ms()) - .unwrap(); - let coordinator = MigratorCoordinator::bootstrap_with( - &request.run_id, - roots.clone(), - "openbitfun", - "stable", - ) - .unwrap(); - assert!(coordinator - .finish_with_restart(MigrationPromptChoice::DoNotRemind, || { - Err(LegacyMigrationError::TrustedInstallationUnavailable( - "missing desktop".into(), - )) - }) - .is_err()); - assert!(coordinator.snapshot().error.is_some()); - assert_eq!( - MigrationOnboardingStore::new(roots).load().unwrap().choice, - MigrationPromptChoice::DoNotRemind - ); - } - - #[test] - #[cfg(debug_assertions)] - fn development_restart_requires_matching_handoff() { - let temporary = tempfile::tempdir().unwrap(); - let run_id = uuid::Uuid::new_v4().to_string(); - let handoff = temporary.path().join("handoff.json"); - openbitfun_legacy_migration::atomic_write_json( - &handoff, - &serde_json::json!({ "runId": run_id, "pid": std::process::id() }), - ) - .unwrap(); - assert!(request_dev_restart(temporary.path(), "wrong-run").is_err()); - assert!(!temporary.path().join("restart.json").exists()); - request_dev_restart(temporary.path(), &run_id).unwrap(); - let restart: serde_json::Value = - serde_json::from_slice(&std::fs::read(temporary.path().join("restart.json")).unwrap()) - .unwrap(); - assert_eq!(restart["runId"], run_id); - } } diff --git a/src/apps/data-migrator/src/commands.rs b/src/apps/data-migrator/src/commands.rs index be2dd043da..e9bf9de2e9 100644 --- a/src/apps/data-migrator/src/commands.rs +++ b/src/apps/data-migrator/src/commands.rs @@ -1,5 +1,6 @@ use crate::app_state::{CommandError, DiagnosticsExportView, MigratorCoordinator, MigratorView}; -use openbitfun_product_domains::legacy_migration::{MigrationPromptChoice, MigrationSelection}; +use openbitfun_legacy_migration::MigrationRoots; +use openbitfun_product_domains::legacy_migration::MigrationSelection; use serde::Deserialize; use tauri::{AppHandle, State}; @@ -21,8 +22,39 @@ pub(crate) struct ExecuteRequest { #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields, rename_all = "camelCase")] -pub(crate) struct PromptChoiceRequest { - pub choice: MigrationPromptChoice, +pub(crate) struct LocationsRequest { + pub locations: MigrationRoots, +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields, rename_all = "camelCase")] +pub(crate) struct ResumeRequest { + pub run_id: String, +} + +#[tauri::command] +pub(crate) fn set_migration_locations( + state: State<'_, MigratorCoordinator>, + request: LocationsRequest, +) -> Result { + state.set_locations(request.locations) +} + +#[tauri::command] +pub(crate) fn new_migration_task( + state: State<'_, MigratorCoordinator>, + request: EmptyRequest, +) -> Result { + let _ = request; + state.new_task() +} + +#[tauri::command] +pub(crate) fn resume_migration_task( + state: State<'_, MigratorCoordinator>, + request: ResumeRequest, +) -> Result { + state.resume_task(&request.run_id) } #[tauri::command] @@ -89,9 +121,10 @@ pub(crate) fn export_migration_diagnostics( pub(crate) fn finish_legacy_migration( app: AppHandle, state: State<'_, MigratorCoordinator>, - request: PromptChoiceRequest, + request: EmptyRequest, ) -> Result<(), CommandError> { - state.finish_and_restart(request.choice)?; + let _ = request; + state.finish()?; app.exit(0); Ok(()) } diff --git a/src/apps/data-migrator/src/lib.rs b/src/apps/data-migrator/src/lib.rs index 64894deb98..b50be4419b 100644 --- a/src/apps/data-migrator/src/lib.rs +++ b/src/apps/data-migrator/src/lib.rs @@ -1,5 +1,6 @@ mod app_state; mod commands; +mod locations; use app_state::MigratorCoordinator; use std::fmt; @@ -22,10 +23,14 @@ impl fmt::Display for RunError { impl std::error::Error for RunError {} -pub fn run(run_id: &str) -> Result<(), RunError> { - let coordinator = MigratorCoordinator::bootstrap(run_id).map_err(|_| RunError::Bootstrap)?; +pub fn run() -> Result<(), RunError> { tauri::Builder::default() - .manage(coordinator) + .setup(|app| { + let settings = app.path().app_config_dir()?.join("locations.json"); + let coordinator = MigratorCoordinator::bootstrap(settings)?; + app.manage(coordinator); + Ok(()) + }) .on_window_event(|window, event| { if let tauri::WindowEvent::CloseRequested { api, .. } = event { let coordinator = window.app_handle().state::(); @@ -34,13 +39,16 @@ pub fn run(run_id: &str) -> Result<(), RunError> { coordinator.cancel(); return; } - if coordinator.close_and_restart().is_ok() { + if coordinator.finish().is_ok() { window.app_handle().exit(0); } } }) .invoke_handler(tauri::generate_handler![ commands::get_migrator_bootstrap, + commands::set_migration_locations, + commands::new_migration_task, + commands::resume_migration_task, commands::scan_legacy_migration, commands::prepare_legacy_migration, commands::retry_writer_check, diff --git a/src/apps/data-migrator/src/locations.rs b/src/apps/data-migrator/src/locations.rs new file mode 100644 index 0000000000..d2f0569d5f --- /dev/null +++ b/src/apps/data-migrator/src/locations.rs @@ -0,0 +1,89 @@ +//! Local user-selected locations, independent of Desktop configuration. +use openbitfun_legacy_migration::{LegacyMigrationError, LegacyMigrationResult, MigrationRoots}; +use std::path::{Component, Path, PathBuf}; + +pub(crate) fn validate(roots: &MigrationRoots) -> LegacyMigrationResult<()> { + let sources = [ + &roots.legacy_user_root, + &roots.legacy_home_root, + &roots.legacy_skills_root, + &roots.legacy_ssh_root, + ]; + let targets = [ + &roots.target_user_root, + &roots.target_home_root, + &roots.target_skills_root, + &roots.target_ssh_root, + ]; + let sources = sources + .into_iter() + .map(normalized) + .collect::>>()?; + let targets = targets + .into_iter() + .map(normalized) + .collect::>>()?; + for source in &sources { + for target in &targets { + if source.starts_with(target) || target.starts_with(source) { + return Err(LegacyMigrationError::SourceEqualsTarget(source.clone())); + } + } + } + Ok(()) +} + +fn normalized(path: &PathBuf) -> LegacyMigrationResult { + if !path.is_absolute() + || path.parent().is_none() + || path + .components() + .any(|part| matches!(part, Component::ParentDir)) + { + return Err(LegacyMigrationError::PathUnavailable( + "choose an absolute local data directory".into(), + )); + } + // Resolve existing parents too: destinations often do not exist yet. + let mut parent: &Path = path; + let mut suffix = Vec::new(); + while !parent.exists() { + suffix.push( + parent + .file_name() + .ok_or_else(|| LegacyMigrationError::PathUnavailable("data directory".into()))?, + ); + parent = parent + .parent() + .ok_or_else(|| LegacyMigrationError::PathUnavailable("data directory".into()))?; + } + if !parent.is_dir() { + return Err(LegacyMigrationError::PathUnavailable( + "data directory".into(), + )); + } + let mut resolved = + std::fs::canonicalize(parent).map_err(|source| LegacyMigrationError::Io { + path: parent.to_path_buf(), + source, + })?; + for part in suffix.into_iter().rev() { + resolved.push(part); + } + if cfg!(windows) { + resolved = PathBuf::from(resolved.to_string_lossy().to_lowercase()); + } + Ok(resolved) +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn normalizes_missing_children_and_rejects_relative_paths() { + let temp = tempfile::tempdir().unwrap(); + assert!(normalized(&temp.path().join("missing/child")).is_ok()); + assert!(normalized(&PathBuf::from("relative/data")).is_err()); + assert!(normalized(&temp.path().join("../escape")).is_err()); + } +} diff --git a/src/apps/data-migrator/src/main.rs b/src/apps/data-migrator/src/main.rs index d73be4545e..27588eb2a5 100644 --- a/src/apps/data-migrator/src/main.rs +++ b/src/apps/data-migrator/src/main.rs @@ -3,18 +3,7 @@ use std::process::ExitCode; fn main() -> ExitCode { - let mut arguments = std::env::args_os().skip(1); - let Some(run_id) = arguments.next() else { - return ExitCode::from(2); - }; - if arguments.next().is_some() { - return ExitCode::from(2); - } - let Some(run_id) = run_id.to_str() else { - return ExitCode::from(2); - }; - - match openbitfun_data_migrator_lib::run(run_id) { + match openbitfun_data_migrator_lib::run() { Ok(()) => ExitCode::SUCCESS, Err(_) => ExitCode::from(1), } diff --git a/src/apps/data-migrator/tauri.conf.json b/src/apps/data-migrator/tauri.conf.json index c7b678c1c6..5383a891e9 100644 --- a/src/apps/data-migrator/tauri.conf.json +++ b/src/apps/data-migrator/tauri.conf.json @@ -1,6 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "OpenBitFun Data Migrator", + "version": "0.1.0", "mainBinaryName": "openbitfun-data-migrator", "identifier": "com.openbitfun.data-migrator", "build": { @@ -11,9 +12,9 @@ "targets": "all", "publisher": "OpenBitFun Team", "icon": [ - "../desktop/icons/openbitfun-app-icon.icns", - "../desktop/icons/openbitfun-app-icon.ico", - "../desktop/icons/openbitfun-app-icon.png" + "icons/openbitfun-app-icon.icns", + "icons/openbitfun-app-icon.ico", + "icons/openbitfun-app-icon.png" ], "resources": { "../../../THIRD_PARTY_NOTICES.md": "THIRD_PARTY_NOTICES.md" @@ -34,7 +35,7 @@ } ], "security": { - "csp": "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src ipc: http://ipc.localhost" + "csp": "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self' ipc: http://ipc.localhost" }, "withGlobalTauri": true } diff --git a/src/apps/data-migrator/ui/app.js b/src/apps/data-migrator/ui/app.js index df1ca5b99b..ec21b9f52a 100644 --- a/src/apps/data-migrator/ui/app.js +++ b/src/apps/data-migrator/ui/app.js @@ -1,90 +1,27 @@ -const invoke = window.__TAURI__.core.invoke; +import { invoke } from './transport.js'; + +async function loadTranslations() { + try { + const response = await fetch('locales.json'); + if (!response.ok) throw new Error('Locale asset unavailable'); + const catalogs = await response.json(); + if (!catalogs.en) throw new Error('Default locale unavailable'); + return catalogs; + } catch (error) { + const output = document.getElementById('notice'); + output.textContent = 'Data Migrator could not load its language files. Close it and download or rebuild the complete package.'; + output.hidden = false; + document.querySelectorAll('button, select').forEach((node) => { node.disabled = true; }); + throw error; + } +} +const translations = await loadTranslations(); -const translations = { - en: { - eyebrow: 'OpenBitFun maintenance', title: 'Import data from BitFun', - intro: 'Choose what to bring forward. Your original BitFun data will not be deleted.', - stepSource: 'Step 1', sourceTitle: 'Legacy source', firstLaunch: 'First launch', - choiceTitle: 'What would you like to do?', - choiceHelp: 'Migration runs only after OpenBitFun and other data writers have stopped.', - migrateNow: 'Migrate now', remindLater: 'Remind me later', doNotRemind: 'Do not remind me', - stepScope: 'Step 2', scopeTitle: 'Choose migration scope', scan: 'Scan selected data', - stepReview: 'Step 3', reviewTitle: 'Review scan', prepare: 'Run preflight plan', - stepConfirm: 'Step 4', planTitle: 'Confirm migration', retryWriters: 'Check processes again', - start: 'Start migration', stepProgress: 'Step 5', progressTitle: 'Migration progress', - phase: 'Phase', domain: 'Domain', count: 'Completed steps', - cancel: 'Cancel', stepDone: 'Result', reportTitle: 'Migration report', - reportPrivacy: 'This summary contains counts and result codes, not credentials or user content.', - exportDiagnostics: 'Export failure diagnostics', - diagnosticsExported: 'Sanitized diagnostics saved to {path}', - openDesktop: 'Open OpenBitFun', ready: 'Ready', unsupported: 'Unsupported', missing: 'Not found', - closeMigrator: 'Close Data Migrator', - devRestartHelp: 'Development build: close Data Migrator, then run pnpm run desktop:dev again.', - bootstrapPending: 'Data Migrator is still loading. Please try again.', - bootstrapFailed: 'Data Migrator could not load its authenticated migration request.', - sourceFound: 'BitFun {version} was found. The source stays read-only.', - recovery: 'A previous migration journal was found and can be resumed.', - blockers: '{count} data-writing process(es) must stop before migration can continue.', - noBlockers: 'No data-writing processes are blocking migration.', - steps: '{count} migration step(s)', conflicts: '{count} conflict(s)', - imported: 'imported', staged: 'staged', skipped: 'skipped', warnings: 'warnings', - }, - 'zh-CN': { - eyebrow: 'OpenBitFun 数据维护', title: '从 BitFun 导入数据', - intro: '选择要迁移的内容。原始 BitFun 数据不会被删除。', stepSource: '第 1 步', - sourceTitle: '旧版数据来源', firstLaunch: '首次启动', choiceTitle: '你希望如何处理?', - choiceHelp: '迁移只会在 OpenBitFun 和其他数据写入进程停止后运行。', migrateNow: '立即迁移', - remindLater: '稍后提醒', doNotRemind: '不再提醒', stepScope: '第 2 步', - scopeTitle: '选择迁移范围', scan: '扫描所选数据', stepReview: '第 3 步', - reviewTitle: '检查扫描结果', prepare: '运行迁移预检', stepConfirm: '第 4 步', - planTitle: '确认迁移', retryWriters: '重新检查进程', start: '开始迁移', - stepProgress: '第 5 步', progressTitle: '迁移进度', phase: '阶段', domain: '领域', - count: '已完成步骤', cancel: '取消', stepDone: '结果', reportTitle: '迁移报告', - reportPrivacy: '此摘要仅包含计数和结果码,不包含凭据或用户正文。', - exportDiagnostics: '导出失败诊断', diagnosticsExported: '去敏诊断已保存到 {path}', - openDesktop: '打开 OpenBitFun', - closeMigrator: '关闭数据迁移器', - devRestartHelp: '开发版本:关闭数据迁移器,然后重新运行 pnpm run desktop:dev。', - bootstrapPending: '数据迁移器仍在加载,请稍后重试。', - bootstrapFailed: '数据迁移器无法加载已认证的迁移请求。', - ready: '可迁移', unsupported: '不受支持', missing: '未发现', - sourceFound: '已发现 BitFun {version}。迁移期间来源保持只读。', - recovery: '发现上次迁移日志,可以从安全状态继续。', - blockers: '迁移前还需停止 {count} 个数据写入进程。', noBlockers: '没有进程阻止迁移。', - steps: '{count} 个迁移步骤', conflicts: '{count} 个冲突', - imported: '已导入', staged: '已暂存', skipped: '已跳过', warnings: '警告', - }, - 'zh-TW': { - eyebrow: 'OpenBitFun 資料維護', title: '從 BitFun 匯入資料', - intro: '選擇要遷移的內容。原始 BitFun 資料不會被刪除。', stepSource: '第 1 步', - sourceTitle: '舊版資料來源', firstLaunch: '首次啟動', choiceTitle: '你希望如何處理?', - choiceHelp: '遷移只會在 OpenBitFun 和其他資料寫入程序停止後執行。', migrateNow: '立即遷移', - remindLater: '稍後提醒', doNotRemind: '不再提醒', stepScope: '第 2 步', - scopeTitle: '選擇遷移範圍', scan: '掃描所選資料', stepReview: '第 3 步', - reviewTitle: '檢查掃描結果', prepare: '執行遷移預檢', stepConfirm: '第 4 步', - planTitle: '確認遷移', retryWriters: '重新檢查程序', start: '開始遷移', - stepProgress: '第 5 步', progressTitle: '遷移進度', phase: '階段', domain: '領域', - count: '已完成步驟', cancel: '取消', stepDone: '結果', reportTitle: '遷移報告', - reportPrivacy: '此摘要僅包含計數和結果碼,不包含憑據或使用者正文。', - exportDiagnostics: '匯出失敗診斷', diagnosticsExported: '去敏診斷已儲存至 {path}', - openDesktop: '開啟 OpenBitFun', - closeMigrator: '關閉資料遷移器', - devRestartHelp: '開發版本:關閉資料遷移器,然後重新執行 pnpm run desktop:dev。', - bootstrapPending: '資料遷移器仍在載入,請稍後重試。', - bootstrapFailed: '資料遷移器無法載入已驗證的遷移請求。', - ready: '可遷移', unsupported: '不支援', missing: '未發現', - sourceFound: '已發現 BitFun {version}。遷移期間來源保持唯讀。', - recovery: '發現上次遷移日誌,可以從安全狀態繼續。', - blockers: '遷移前還需停止 {count} 個資料寫入程序。', noBlockers: '沒有程序阻止遷移。', - steps: '{count} 個遷移步驟', conflicts: '{count} 個衝突', - imported: '已匯入', staged: '已暫存', skipped: '已略過', warnings: '警告', - }, -}; -const locale = navigator.language.startsWith('zh-TW') || navigator.language.startsWith('zh-HK') +const locale = localStorage.getItem('migrator.language') || (navigator.language.startsWith('zh-TW') || navigator.language.startsWith('zh-HK') ? 'zh-TW' - : navigator.language.startsWith('zh') ? 'zh-CN' : 'en'; -const text = translations[locale]; + : navigator.language.startsWith('zh') ? 'zh-CN' : 'en'); +const text = translations[locale] || translations.en; document.documentElement.lang = locale; document.querySelectorAll('[data-i18n]').forEach((node) => { node.textContent = text[node.dataset.i18n] || translations.en[node.dataset.i18n]; @@ -120,6 +57,9 @@ const groups = [ let current; let pollTimer; +let locationsDirty = false; +let locationSnapshot; +let scopeSnapshot; function format(template, values) { return Object.entries(values).reduce((value, [key, replacement]) => @@ -134,9 +74,10 @@ function setBusy(busy) { document.querySelectorAll('button').forEach((button) => { button.disabled = busy; }); } -function notice(message) { +function notice(message, tone = 'danger') { const node = document.getElementById('notice'); node.textContent = message || ''; + node.dataset.tone = tone; node.hidden = !message; } @@ -161,8 +102,16 @@ function transferLabel(result) { return result.state === 'verified' ? text.imported : text.staged; } +function groupLabel(id) { + const labels = groups.find(([group]) => group === id)?.[1]; + return (labels?.[locale] || labels?.en || [id])[0]; +} + function renderScopes(selection) { const list = document.getElementById('scope-list'); + const signature = JSON.stringify([selection, current?.recovery, current?.running]); + if (signature === scopeSnapshot) return; + scopeSnapshot = signature; list.replaceChildren(); const selected = new Set(selection?.groups?.length ? selection.groups : groups.map(([id]) => id)); groups.forEach(([id, labels]) => { @@ -173,7 +122,7 @@ function renderScopes(selection) { checkbox.id = `scope-${id}`; checkbox.value = id; checkbox.checked = selected.has(id); - if (current?.mode === 'execute') checkbox.disabled = true; + checkbox.disabled = Boolean(current?.running || current?.recovery); const label = document.createElement('label'); label.htmlFor = checkbox.id; const strong = document.createElement('strong'); @@ -196,13 +145,28 @@ function render(view) { const source = view.source; document.getElementById('source-badge').textContent = !source ? text.missing : source.supported ? text.ready : text.unsupported; + document.getElementById('source-badge').dataset.tone = !source || !source.supported + ? 'warning' : 'success'; document.getElementById('source-summary').textContent = source ? format(text.sourceFound, { version: source.productVersion }) : text.missing; - document.getElementById('source-path').textContent = source?.roots?.[0]?.displayPath || ''; - notice(view.error?.message || (view.recovery ? text.recovery : '')); + if (!source) document.getElementById('source-summary').textContent = text.noSource; + renderLocations(view.locations); + document.getElementById('tool-version').textContent = 'v' + view.toolVersion; + const tasks = document.getElementById('saved-task'); + const previousTask = tasks.value; + tasks.replaceChildren(...view.savedTasks.map((task) => { + const option = document.createElement('option'); + option.value = task.runId; + option.textContent = task.runId + ' · ' + (task.readable ? task.status : text.unreadableTask); + option.disabled = !task.readable; + return option; + })); + if ([...tasks.options].some((option) => option.value === previousTask && !option.disabled)) tasks.value = previousTask; + show('history-card', view.savedTasks.length > 0 && !view.running); + document.getElementById('resume-task').disabled = !tasks.selectedOptions[0] || tasks.selectedOptions[0].disabled || view.running || locationsDirty; + notice(view.error?.message || (view.recovery ? text.recovery : ''), view.error ? 'danger' : 'info'); - show('choice-card', view.mode === 'onboarding' && !view.findings.length && !view.plan && !view.running); - show('scope-card', Boolean(source) && (view.mode === 'execute' || view.findings.length || view.plan)); + show('scope-card', !view.recovery && !view.running && !view.plan); renderScopes(view.selection); const findings = document.getElementById('findings'); @@ -213,11 +177,12 @@ function render(view) { const planSummary = document.getElementById('plan-summary'); if (view.plan) { planSummary.replaceChildren( - row(text.steps.replace('{count}', view.plan.steps.length), view.plan.planHash), - row(text.conflicts.replace('{count}', view.plan.conflicts.length), `${view.plan.estimatedWriteBytes} byte(s)`), + row(text.steps.replace('{count}', view.plan.steps.length), view.plan.selection.groups.map(groupLabel).join(' · ')), + row(text.conflicts.replace('{count}', view.plan.conflicts.length), text.confirmHelp), + ...view.plan.conflicts.map((conflict) => row(conflict.domain, conflict.code || conflict.resolution || '')), ); } - show('plan-card', Boolean(view.plan) && !view.running && !view.report); + show('plan-card', Boolean(view.plan) && !view.running && !['completed', 'completed_with_warnings'].includes(view.status)); const blocker = document.getElementById('blockers'); blocker.textContent = view.blockers.length ? format(text.blockers, { count: view.blockers.length }) : text.noBlockers; @@ -239,9 +204,6 @@ function render(view) { reportSummary.replaceChildren(...view.report.domainResults.map((result) => row(result.domain, `${result.imported} ${transferLabel(result)}, ${result.skipped} ${text.skipped}, ${result.warnings.filter((item) => item.severity !== 'info').length} ${text.warnings}`))); } - show('dev-restart-help', !view.restartDesktopOnFinish); - document.getElementById('open-desktop').textContent = view.restartDesktopOnFinish - ? text.openDesktop : text.closeMigrator; show('report-card', !view.running && (Boolean(view.report) || view.status === 'cancelled')); const canExportDiagnostics = ['failed_recoverable', 'failed_manual_action_required'].includes(view.status); show('export-diagnostics', canExportDiagnostics); @@ -250,7 +212,12 @@ function render(view) { output.textContent = ''; output.hidden = true; } - document.getElementById('start').disabled = !view.canExecute; + document.querySelectorAll('button, #locations input, #language, #saved-task').forEach((node) => { node.disabled = view.running; }); + document.getElementById('cancel').disabled = !view.running; + document.getElementById('start').disabled = !view.canExecute || locationsDirty; + document.getElementById('resume-task').disabled = !tasks.selectedOptions[0] || tasks.selectedOptions[0].disabled || view.running || locationsDirty; + for (const id of ['scan', 'prepare']) document.getElementById(id).disabled = view.running || locationsDirty; + if (locationsDirty) notice(text.dirtyLocations, 'info'); if (view.running && !pollTimer) { pollTimer = window.setInterval(refresh, 500); @@ -262,16 +229,18 @@ function render(view) { async function call(command, request = {}) { setBusy(true); + let failure; try { const result = await invoke(command, { request }); if (result) render(result); return result; } catch (error) { - notice(error?.message || String(error)); + failure = error?.message || String(error); return undefined; } finally { setBusy(false); if (current) render(current); + if (failure) notice(failure); } } @@ -286,16 +255,47 @@ async function refresh() { } } -document.getElementById('migrate-now').addEventListener('click', () => { +function renderLocations(locations) { + const signature = JSON.stringify(locations); + if (signature === locationSnapshot || locationsDirty) return; + locationSnapshot = signature; + const host = document.getElementById('locations'); + host.replaceChildren(); + for (const [prefix, title] of [['legacy', text.source], ['target', text.target]]) { + const group = document.createElement('fieldset'); + const legend = document.createElement('legend'); + legend.textContent = title; + group.append(legend); + for (const suffix of ['UserRoot', 'HomeRoot', 'SkillsRoot', 'SshRoot']) { + const key = prefix + suffix; + const label = document.createElement('label'); + const input = document.createElement('input'); + label.htmlFor = key; + label.textContent = text[suffix[0].toLowerCase() + suffix.slice(1)]; + input.type = 'text'; input.id = key; input.value = locations[key]; + input.spellcheck = false; input.autocomplete = 'off'; + input.addEventListener('input', () => { locationsDirty = true; if (current) render(current); }); + group.append(label, input); + } + host.append(group); + } +} + +document.getElementById('language').value = locale; +document.getElementById('language').addEventListener('change', (event) => { + localStorage.setItem('migrator.language', event.target.value); + window.location.reload(); +}); +document.getElementById('apply-locations').addEventListener('click', async () => { if (!requireBootstrap()) return; - show('choice-card', false); - show('scope-card'); - renderScopes(current.selection); + const locations = Object.fromEntries([...document.querySelectorAll('#locations input')].map((input) => [input.id, input.value.trim()])); + const result = await call('set_migration_locations', { locations }); + if (result) { locationsDirty = false; locationSnapshot = undefined; render(result); } }); -document.getElementById('remind-later').addEventListener('click', () => - call('finish_legacy_migration', { choice: 'remind_later' })); -document.getElementById('do-not-remind').addEventListener('click', () => - call('finish_legacy_migration', { choice: 'do_not_remind' })); +document.getElementById('resume-task').addEventListener('click', () => call('resume_migration_task', { runId: document.getElementById('saved-task').value })); +document.getElementById('new-task').addEventListener('click', () => call('new_migration_task')); +for (const id of ['close', 'finish']) document.getElementById(id).addEventListener('click', () => call('finish_legacy_migration')); + document.getElementById('scan').addEventListener('click', () => call('scan_legacy_migration', { selection: selection() })); document.getElementById('prepare').addEventListener('click', () => @@ -308,21 +308,18 @@ document.getElementById('cancel').addEventListener('click', () => call('cancel_legacy_migration')); document.getElementById('export-diagnostics').addEventListener('click', async () => { setBusy(true); + let failure; try { const result = await invoke('export_migration_diagnostics', { request: {} }); const output = document.getElementById('diagnostics-path'); output.textContent = format(text.diagnosticsExported, { path: result.filePath }); output.hidden = false; } catch (error) { - notice(error?.message || String(error)); + failure = error?.message || String(error); } finally { setBusy(false); if (current) render(current); + if (failure) notice(failure); } }); -document.getElementById('open-desktop').addEventListener('click', () => - call('finish_legacy_migration', { choice: current.report ? 'migrate_now' : 'remind_later' })); - -refresh().then(() => { - if (current?.mode === 'execute') show('scope-card'); -}); +refresh(); diff --git a/src/apps/data-migrator/ui/generated/design-system.css b/src/apps/data-migrator/ui/generated/design-system.css new file mode 100644 index 0000000000..70e3125ba3 --- /dev/null +++ b/src/apps/data-migrator/ui/generated/design-system.css @@ -0,0 +1,1026 @@ +/* Generated by scripts/generate-data-migrator-theme.mjs. Do not edit. */ +@layer openbitfun.tokens.system, openbitfun.tokens.theme, openbitfun.reset, openbitfun.base, openbitfun.components, openbitfun.overrides; + +@layer openbitfun.tokens.system { + :where([data-openbitfun-design-system-root]) { + --openbitfun-border-width-default: 1px; + --openbitfun-border-width-strong: 2px; + --openbitfun-control-action-card-actions-gap: 0px; + --openbitfun-control-action-card-actions-padding-inline-end: var(--openbitfun-space-2); + --openbitfun-control-action-card-content-gap: var(--openbitfun-space-1); + --openbitfun-control-action-card-gap: var(--openbitfun-space-2); + --openbitfun-control-action-card-icon-size: 16px; + --openbitfun-control-action-card-leading-size: 30px; + --openbitfun-control-action-card-md-min-block-size: 62px; + --openbitfun-control-action-card-padding-block: var(--openbitfun-space-2); + --openbitfun-control-action-card-padding-inline: var(--openbitfun-space-3); + --openbitfun-control-action-card-radius: var(--openbitfun-radius-base); + --openbitfun-control-action-card-sm-min-block-size: 54px; + --openbitfun-control-activity-item-content-gap: var(--openbitfun-space-1); + --openbitfun-control-activity-item-divider-block-size: 16px; + --openbitfun-control-activity-item-inline-gap: var(--openbitfun-space-1); + --openbitfun-control-activity-item-inline-icon-size: 12px; + --openbitfun-control-activity-item-surface-gap: var(--openbitfun-space-2); + --openbitfun-control-activity-item-surface-height: 30px; + --openbitfun-control-activity-item-surface-icon-size: 14px; + --openbitfun-control-activity-item-surface-padding-block: var(--openbitfun-space-1); + --openbitfun-control-activity-item-surface-padding-inline-end: 3px; + --openbitfun-control-activity-item-surface-padding-inline-start: var(--openbitfun-space-2); + --openbitfun-control-activity-item-surface-radius: var(--openbitfun-radius-base); + --openbitfun-control-ask-user-body-gap: var(--openbitfun-space-3); + --openbitfun-control-ask-user-body-padding: var(--openbitfun-space-4); + --openbitfun-control-ask-user-description-max-width: 500px; + --openbitfun-control-ask-user-header-height: 30px; + --openbitfun-control-ask-user-icon-size: 14px; + --openbitfun-control-ask-user-option-content-gap: var(--openbitfun-space-2); + --openbitfun-control-ask-user-option-gap: var(--openbitfun-space-1); + --openbitfun-control-ask-user-option-padding-block: 7px; + --openbitfun-control-ask-user-option-padding-inline: var(--openbitfun-space-2); + --openbitfun-control-ask-user-question-options-gap: var(--openbitfun-space-3); + --openbitfun-control-ask-user-question-padding-inline: var(--openbitfun-space-1); + --openbitfun-control-ask-user-summary-action-size: 22px; + --openbitfun-control-ask-user-summary-padding-block: var(--openbitfun-space-1); + --openbitfun-control-ask-user-summary-padding-inline-end: var(--openbitfun-space-1); + --openbitfun-control-ask-user-summary-padding-inline-start: var(--openbitfun-space-2); + --openbitfun-control-button-xs-height: 24px; + --openbitfun-control-button-xs-leading-icon-size: 12px; + --openbitfun-control-button-xs-padding-inline: 6px; + --openbitfun-control-button-xs-trailing-icon-size: 10px; + --openbitfun-control-change-count-gap: var(--openbitfun-space-1); + --openbitfun-control-change-count-padding-block: 2px; + --openbitfun-control-change-count-padding-inline: var(--openbitfun-space-1); + --openbitfun-control-change-count-radius: var(--openbitfun-radius-xs); + --openbitfun-control-chat-composer-action-icon-size: 14px; + --openbitfun-control-chat-composer-compact-gap: 9px; + --openbitfun-control-chat-composer-compact-height: 45px; + --openbitfun-control-chat-composer-compact-padding-block: 9px; + --openbitfun-control-chat-composer-compact-padding-inline: 9px; + --openbitfun-control-chat-composer-compact-track-height: 25px; + --openbitfun-control-chat-composer-control-height: 25px; + --openbitfun-control-composer-context-gap: var(--openbitfun-space-2); + --openbitfun-control-composer-context-offset: 32px; + --openbitfun-control-composer-context-padding-block: 6px; + --openbitfun-control-composer-context-padding-inline: var(--openbitfun-space-2); + --openbitfun-control-composer-divider-block-size: 16px; + --openbitfun-control-composer-editor-padding: var(--openbitfun-space-1); + --openbitfun-control-composer-min-block-size: 120px; + --openbitfun-control-composer-surface-gap: var(--openbitfun-space-3); + --openbitfun-control-composer-surface-padding: var(--openbitfun-space-2); + --openbitfun-control-composer-surface-radius: var(--openbitfun-radius-xl); + --openbitfun-control-composer-toolbar-gap: var(--openbitfun-space-2); + --openbitfun-control-flow-chat-card-expanded-padding-block: 0.5rem; + --openbitfun-control-flow-chat-card-expanded-padding-inline: 0.625rem; + --openbitfun-control-flow-chat-card-gap: 0.42rem; + --openbitfun-control-flow-chat-card-padding-block: 0.625rem; + --openbitfun-control-flow-chat-card-padding-inline: 0.75rem; + --openbitfun-control-flow-chat-card-radius: var(--openbitfun-radius-base); + --openbitfun-control-flow-chat-code-block-padding-block: 0.55rem; + --openbitfun-control-flow-chat-code-block-padding-inline: 0.75rem; + --openbitfun-control-flow-chat-content-padding-inline: 3rem; + --openbitfun-control-flow-chat-content-padding-inline-mobile: 1.5rem; + --openbitfun-control-flow-chat-control-gap: 0.5rem; + --openbitfun-control-flow-chat-control-padding-block: 0.35rem; + --openbitfun-control-flow-chat-control-padding-inline: 0.75rem; + --openbitfun-control-flow-chat-flow-item-gap: 0.42rem; + --openbitfun-control-flow-chat-inline-gap: 0.35rem; + --openbitfun-control-flow-chat-turn-gap: var(--openbitfun-space-4); + --openbitfun-control-height-lg: 48px; + --openbitfun-control-height-md: 40px; + --openbitfun-control-height-sm: 32px; + --openbitfun-control-hit-target: 40px; + --openbitfun-control-icon-size2xs: 8px; + --openbitfun-control-icon-size-lg: 24px; + --openbitfun-control-icon-size-md: 16px; + --openbitfun-control-icon-size-sm: 14px; + --openbitfun-control-icon-size-xs: 12px; + --openbitfun-control-icon-button-xs-icon-size: 14px; + --openbitfun-control-icon-button-xs-size: 22px; + --openbitfun-control-launcher-button-block-size: var(--openbitfun-control-height-md); + --openbitfun-control-launcher-button-gap: var(--openbitfun-space-2); + --openbitfun-control-launcher-button-icon-size: 16px; + --openbitfun-control-launcher-button-min-inline-size: 104px; + --openbitfun-control-launcher-button-padding-inline: 14px; + --openbitfun-control-launcher-button-radius: var(--openbitfun-radius-lg); + --openbitfun-control-segmented-control-gap: 2px; + --openbitfun-control-segmented-control-icon-size: 12px; + --openbitfun-control-segmented-control-icon-size-md: 14px; + --openbitfun-control-segmented-control-padding: 2px; + --openbitfun-control-segmented-control-padding-md: 3px; + --openbitfun-control-segmented-control-pill-segment-height: 24px; + --openbitfun-control-segmented-control-pill-segment-radius: var(--openbitfun-radius-sm); + --openbitfun-control-segmented-control-radius: var(--openbitfun-radius-pill); + --openbitfun-control-segmented-control-segment-gap: var(--openbitfun-space-1); + --openbitfun-control-segmented-control-segment-height: 22px; + --openbitfun-control-segmented-control-segment-height-md: 28px; + --openbitfun-control-segmented-control-segment-padding-inline: var(--openbitfun-space-2); + --openbitfun-control-segmented-control-segment-radius: var(--openbitfun-radius-pill); + --openbitfun-control-select-content-gap: var(--openbitfun-space-2); + --openbitfun-control-select-indicator-size: 14px; + --openbitfun-control-select-leading-inset: var(--openbitfun-space-3); + --openbitfun-control-select-padding-inline: var(--openbitfun-space-3); + --openbitfun-control-select-radius: var(--openbitfun-radius-base); + --openbitfun-control-select-trailing-inset: var(--openbitfun-space-3); + --openbitfun-control-status-pill-gap: 2px; + --openbitfun-control-status-pill-icon-size: 14px; + --openbitfun-control-status-pill-padding-block: 3px; + --openbitfun-control-status-pill-padding-inline: 6px; + --openbitfun-control-status-pill-radius: var(--openbitfun-radius-pill); + --openbitfun-control-switch-thumb-inset: 2px; + --openbitfun-control-switch-thumb-size: 12px; + --openbitfun-control-switch-thumb-travel: 12px; + --openbitfun-control-switch-thumb-travel-reverse: -12px; + --openbitfun-control-switch-track-height: 16px; + --openbitfun-control-switch-track-width: 28px; + --openbitfun-control-tab-group-gap: var(--openbitfun-space-2); + --openbitfun-control-tab-group-item-action-inset: var(--openbitfun-space-2); + --openbitfun-control-tab-group-item-action-size: var(--openbitfun-space-5); + --openbitfun-control-tab-group-item-gap: 6px; + --openbitfun-control-tab-group-item-height: var(--openbitfun-control-height-md); + --openbitfun-control-tab-group-item-height-sm: 30px; + --openbitfun-control-tab-group-item-icon-size: 16px; + --openbitfun-control-tab-group-item-padding-block-sm: 7px; + --openbitfun-control-tab-group-item-padding-inline: var(--openbitfun-space-4); + --openbitfun-control-tab-group-item-padding-inline-sm: var(--openbitfun-space-3); + --openbitfun-control-tab-group-item-radius: var(--openbitfun-radius-pill); + --openbitfun-control-tool-card-ambient-row-min-block-size: 22px; + --openbitfun-control-tool-card-expanded-padding-inline: 0.625rem; + --openbitfun-control-tool-card-header-icon-slot: 34px; + --openbitfun-control-tool-card-header-padding-block: 0.44rem; + --openbitfun-control-tool-card-header-padding-inline-end: 0.625rem; + --openbitfun-focus-offset: 2px; + --openbitfun-focus-width: 2px; + --openbitfun-font-family-control: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI Variable Text', 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif; + --openbitfun-font-family-mono: 'JetBrains Mono', 'Fira Code', ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Monaco, 'Cascadia Mono', 'Cascadia Code', Consolas, 'Liberation Mono', 'Courier New', monospace; + --openbitfun-font-family-sans: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif; + --openbitfun-font-size-2xl: 18px; + --openbitfun-font-size-2xl-plus: 20px; + --openbitfun-font-size-2xs: 9px; + --openbitfun-font-size-3xl: 22px; + --openbitfun-font-size-3xl-plus: 24px; + --openbitfun-font-size-3xs: 8px; + --openbitfun-font-size-4xl: 26px; + --openbitfun-font-size-4xs: 7px; + --openbitfun-font-size-5xl: 32px; + --openbitfun-font-size-6xl: 40px; + --openbitfun-font-size-7xl: 48px; + --openbitfun-font-size-8xl: 56px; + --openbitfun-font-size-9xl: 64px; + --openbitfun-font-size-base: 14px; + --openbitfun-font-size-lg: 15px; + --openbitfun-font-size-meta: 11px; + --openbitfun-font-size-micro: 10px; + --openbitfun-font-size-sm: 13px; + --openbitfun-font-size-xl: 16px; + --openbitfun-font-size-xl-plus: 17px; + --openbitfun-font-size-xs: 12px; + --openbitfun-font-weight-bold: 700; + --openbitfun-font-weight-medium: 500; + --openbitfun-font-weight-regular: 400; + --openbitfun-font-weight-semibold: 600; + --openbitfun-layer-base: 0; + --openbitfun-layer-content: 2; + --openbitfun-layer-context-menu: 500; + --openbitfun-layer-decoration: 1; + --openbitfun-layer-dropdown: 60; + --openbitfun-layer-floating: 50; + --openbitfun-layer-fullscreen: 280; + --openbitfun-layer-modal: 200; + --openbitfun-layer-modal-active: 250; + --openbitfun-layer-notification: 400; + --openbitfun-layer-overlay: 100; + --openbitfun-layer-overlay-host: 300; + --openbitfun-layer-popover: 360; + --openbitfun-layer-sticky: 15; + --openbitfun-layer-toast: 400; + --openbitfun-layer-tooltip: 350; + --openbitfun-layout-card-footer-gap: var(--openbitfun-space-2); + --openbitfun-layout-card-gap-lg: 30px; + --openbitfun-layout-card-gap-md: var(--openbitfun-space-3); + --openbitfun-layout-card-gap-sm: var(--openbitfun-space-2); + --openbitfun-layout-card-header-gap: var(--openbitfun-space-2); + --openbitfun-layout-card-media-min-block-size: 120px; + --openbitfun-layout-card-padding-md: var(--openbitfun-space-5); + --openbitfun-layout-card-padding-sm: var(--openbitfun-space-3); + --openbitfun-layout-card-radius-lg: 28px; + --openbitfun-layout-card-radius-md: var(--openbitfun-radius-lg); + --openbitfun-layout-card-radius-sm: var(--openbitfun-radius-base); + --openbitfun-layout-card-title-description-gap: var(--openbitfun-space-1); + --openbitfun-layout-confirm-dialog-content-gap: var(--openbitfun-space-4); + --openbitfun-layout-confirm-dialog-icon-glyph-size: var(--openbitfun-control-icon-size-md); + --openbitfun-layout-confirm-dialog-icon-size: 32px; + --openbitfun-layout-confirm-dialog-message-gap: var(--openbitfun-space-2); + --openbitfun-layout-confirm-dialog-preview-max-block-size: 240px; + --openbitfun-layout-confirm-dialog-preview-padding-block: var(--openbitfun-space-3); + --openbitfun-layout-confirm-dialog-preview-padding-inline: var(--openbitfun-space-4); + --openbitfun-layout-confirm-dialog-preview-radius: var(--openbitfun-radius-base); + --openbitfun-layout-disclosure-actions-gap: var(--openbitfun-space-1); + --openbitfun-layout-disclosure-content-padding-block: var(--openbitfun-space-3); + --openbitfun-layout-disclosure-content-padding-inline: var(--openbitfun-space-8); + --openbitfun-layout-disclosure-indicator-size: 14px; + --openbitfun-layout-disclosure-trigger-gap: var(--openbitfun-space-2); + --openbitfun-layout-disclosure-trigger-min-block-size: 30px; + --openbitfun-layout-disclosure-trigger-padding-block: var(--openbitfun-space-1); + --openbitfun-layout-disclosure-trigger-padding-inline: var(--openbitfun-space-2); + --openbitfun-layout-disclosure-trigger-radius: var(--openbitfun-radius-base); + --openbitfun-layout-field-content-gap: var(--openbitfun-space-1); + --openbitfun-layout-field-control-gap: var(--openbitfun-space-2); + --openbitfun-layout-field-horizontal-gap: var(--openbitfun-space-5); + --openbitfun-layout-field-horizontal-gap-wide: var(--openbitfun-space-10); + --openbitfun-layout-field-label-action-gap: var(--openbitfun-space-2); + --openbitfun-layout-field-label-gap: 2px; + --openbitfun-layout-field-label-width-lg: 400px; + --openbitfun-layout-field-label-width-md: 200px; + --openbitfun-layout-field-label-width-sm: 150px; + --openbitfun-layout-field-root-gap: var(--openbitfun-space-2); + --openbitfun-layout-field-group-radius: var(--openbitfun-radius-lg); + --openbitfun-layout-field-group-row-padding-block: var(--openbitfun-space-4); + --openbitfun-layout-field-group-row-padding-inline: var(--openbitfun-space-5); + --openbitfun-layout-form-section-gap: var(--openbitfun-space-4); + --openbitfun-layout-form-section-header-gap: var(--openbitfun-space-5); + --openbitfun-layout-form-section-title-description-gap: var(--openbitfun-space-1); + --openbitfun-layout-navigation-panel-content-gap: var(--openbitfun-space-4); + --openbitfun-layout-navigation-panel-footer-height: 40px; + --openbitfun-layout-navigation-panel-footer-padding: var(--openbitfun-space-2); + --openbitfun-layout-navigation-panel-heading-action-size: 22px; + --openbitfun-layout-navigation-panel-heading-gap: var(--openbitfun-space-5); + --openbitfun-layout-navigation-panel-heading-height: 22px; + --openbitfun-layout-navigation-panel-heading-padding-inline: var(--openbitfun-space-2); + --openbitfun-layout-navigation-panel-inline-size: 216px; + --openbitfun-layout-navigation-panel-item-gap: var(--openbitfun-space-2); + --openbitfun-layout-navigation-panel-item-height: 30px; + --openbitfun-layout-navigation-panel-item-icon-size: 14px; + --openbitfun-layout-navigation-panel-item-padding-inline: var(--openbitfun-space-2); + --openbitfun-layout-navigation-panel-item-radius: var(--openbitfun-radius-base); + --openbitfun-layout-navigation-panel-scrollbar-gap: 2px; + --openbitfun-layout-navigation-panel-section-gap: var(--openbitfun-space-1); + --openbitfun-layout-navigation-panel-surface-padding: var(--openbitfun-space-2); + --openbitfun-layout-overflow-text-fade-extent: var(--openbitfun-space-4); + --openbitfun-layout-spinner-matrix-cell-lg: 8px; + --openbitfun-layout-spinner-matrix-cell-md: 6px; + --openbitfun-layout-spinner-matrix-cell-sm: 4px; + --openbitfun-layout-spinner-matrix-cell-xs: 3px; + --openbitfun-layout-spinner-matrix-gap-md: 2px; + --openbitfun-layout-spinner-matrix-gap-sm: 1px; + --openbitfun-layout-spinner-matrix-radius: 1px; + --openbitfun-layout-split-view-content-panel-radius: var(--openbitfun-radius-3xl); + --openbitfun-layout-toolbar-badge-size: 24px; + --openbitfun-layout-toolbar-content-gap: 40px; + --openbitfun-layout-toolbar-group-gap-md: var(--openbitfun-space-2); + --openbitfun-layout-toolbar-group-gap-sm: var(--openbitfun-space-1); + --openbitfun-layout-toolbar-md-height: 45px; + --openbitfun-layout-toolbar-md-padding-block-end: 6px; + --openbitfun-layout-toolbar-md-padding-block-start: var(--openbitfun-space-2); + --openbitfun-layout-toolbar-md-padding-inline-end: 11px; + --openbitfun-layout-toolbar-md-padding-inline-start: var(--openbitfun-space-2); + --openbitfun-layout-toolbar-overflow-fade-extent: 16px; + --openbitfun-layout-toolbar-separator-block-size: 16px; + --openbitfun-layout-toolbar-sm-height: 33px; + --openbitfun-layout-toolbar-sm-padding-block: var(--openbitfun-space-1); + --openbitfun-layout-toolbar-sm-padding-inline: var(--openbitfun-space-2); + --openbitfun-letter-spacing-caps: 0.1em; + --openbitfun-letter-spacing-expanded: 0.26em; + --openbitfun-letter-spacing-normal: 0em; + --openbitfun-letter-spacing-snug: -0.01em; + --openbitfun-letter-spacing-subtle: 0.01em; + --openbitfun-letter-spacing-tight: -0.02em; + --openbitfun-letter-spacing-tighter: -0.04em; + --openbitfun-letter-spacing-wide: 0.02em; + --openbitfun-letter-spacing-wider: 0.04em; + --openbitfun-letter-spacing-widest: 0.08em; + --openbitfun-line-height-balanced: 1.35; + --openbitfun-line-height-base: 1.5; + --openbitfun-line-height-code: 1.52; + --openbitfun-line-height-comfortable: 1.55; + --openbitfun-line-height-compact: 1.32; + --openbitfun-line-height-dense: 1.25; + --openbitfun-line-height-display: 1.1; + --openbitfun-line-height-loose: 1.8; + --openbitfun-line-height-none: 1; + --openbitfun-line-height-reading: 1.58; + --openbitfun-line-height-relaxed: 1.6; + --openbitfun-line-height-snug: 1.3; + --openbitfun-line-height-spacious: 1.7; + --openbitfun-line-height-support: 1.45; + --openbitfun-line-height-tight: 1.2; + --openbitfun-line-height-ui: 1.4; + --openbitfun-motion-distance-sm: var(--openbitfun-space-1); + --openbitfun-motion-duration-base: 220ms; + --openbitfun-motion-duration-fast: 140ms; + --openbitfun-motion-duration-instant: 80ms; + --openbitfun-motion-duration-lazy: 1s; + --openbitfun-motion-duration-loop: 720ms; + --openbitfun-motion-duration-normal: var(--openbitfun-motion-duration-base); + --openbitfun-motion-duration-slow: 420ms; + --openbitfun-motion-easing-accelerate: cubic-bezier(0.4, 0, 1, 1); + --openbitfun-motion-easing-decelerate: var(--openbitfun-motion-easing-standard); + --openbitfun-motion-easing-enter: var(--openbitfun-motion-easing-standard); + --openbitfun-motion-easing-exit: cubic-bezier(0.3, 0, 1, 1); + --openbitfun-motion-easing-smooth: cubic-bezier(0.77, 0, 0.175, 1); + --openbitfun-motion-easing-standard: cubic-bezier(0.23, 1, 0.32, 1); + --openbitfun-overlay-dialog-backdrop-blur: blur(20px); + --openbitfun-overlay-dialog-content-padding-lg: var(--openbitfun-space-6); + --openbitfun-overlay-dialog-content-padding-md: var(--openbitfun-space-4); + --openbitfun-overlay-dialog-content-padding-sm: var(--openbitfun-space-3); + --openbitfun-overlay-dialog-content-padding-xl: var(--openbitfun-space-8); + --openbitfun-overlay-dialog-description-gap: var(--openbitfun-space-2); + --openbitfun-overlay-dialog-edge-gutter: 8px; + --openbitfun-overlay-dialog-footer-action-min-width: 100px; + --openbitfun-overlay-dialog-footer-blur: blur(10px); + --openbitfun-overlay-dialog-footer-content-inset: 104px; + --openbitfun-overlay-dialog-footer-fade-extent: var(--openbitfun-space-6); + --openbitfun-overlay-dialog-footer-gap: var(--openbitfun-space-2); + --openbitfun-overlay-dialog-footer-height: 68px; + --openbitfun-overlay-dialog-footer-padding-block-end: var(--openbitfun-space-5); + --openbitfun-overlay-dialog-footer-padding-block-start: var(--openbitfun-space-2); + --openbitfun-overlay-dialog-footer-padding-inline: var(--openbitfun-space-6); + --openbitfun-overlay-dialog-header-actions-gap: var(--openbitfun-space-2); + --openbitfun-overlay-dialog-header-gap: var(--openbitfun-space-5); + --openbitfun-overlay-dialog-header-padding-block-end: var(--openbitfun-space-5); + --openbitfun-overlay-dialog-header-padding-block-start: var(--openbitfun-space-6); + --openbitfun-overlay-dialog-header-padding-inline: var(--openbitfun-space-6); + --openbitfun-overlay-dialog-max-inline-size-large: 600px; + --openbitfun-overlay-dialog-max-inline-size-medium: 560px; + --openbitfun-overlay-dialog-max-inline-size-small: 420px; + --openbitfun-overlay-dialog-max-inline-size-wide: 1200px; + --openbitfun-overlay-dialog-max-inline-size-xlarge: 720px; + --openbitfun-overlay-dialog-max-inline-size-xxlarge: 960px; + --openbitfun-overlay-dialog-scrollbar-width: var(--openbitfun-scrollbar-width); + --openbitfun-overlay-dialog-surface-radius: 28px; + --openbitfun-overlay-dialog-viewport-gutter: 24px; + --openbitfun-overlay-menu-heading-action-size: 22px; + --openbitfun-overlay-menu-heading-gap: var(--openbitfun-space-5); + --openbitfun-overlay-menu-heading-height: 24px; + --openbitfun-overlay-menu-heading-padding-inline: var(--openbitfun-space-2); + --openbitfun-overlay-menu-inline-size: 220px; + --openbitfun-overlay-menu-item-gap: var(--openbitfun-space-2); + --openbitfun-overlay-menu-item-height: 30px; + --openbitfun-overlay-menu-item-icon-size: 14px; + --openbitfun-overlay-menu-item-padding-inline: var(--openbitfun-space-2); + --openbitfun-overlay-menu-item-radius: var(--openbitfun-radius-base); + --openbitfun-overlay-menu-max-block-size: 480px; + --openbitfun-overlay-menu-scrollbar-gap: 2px; + --openbitfun-overlay-menu-section-gap: var(--openbitfun-space-2); + --openbitfun-overlay-menu-surface-padding: var(--openbitfun-space-2); + --openbitfun-overlay-menu-surface-radius: var(--openbitfun-radius-xl); + --openbitfun-overlay-tooltip-arrow-size: 8px; + --openbitfun-overlay-tooltip-gap: var(--openbitfun-space-2); + --openbitfun-overlay-tooltip-max-block-size: 320px; + --openbitfun-overlay-tooltip-max-inline-size: 280px; + --openbitfun-overlay-tooltip-padding-block: 6px; + --openbitfun-overlay-tooltip-padding-inline: 10px; + --openbitfun-overlay-tooltip-surface-radius: var(--openbitfun-radius-sm); + --openbitfun-radius-2xl: 20px; + --openbitfun-radius-3xl: 24px; + --openbitfun-radius-4xl: 32px; + --openbitfun-radius-base: 8px; + --openbitfun-radius-lg: 12px; + --openbitfun-radius-md: var(--openbitfun-radius-base); + --openbitfun-radius-pill: 9999px; + --openbitfun-radius-sm: 6px; + --openbitfun-radius-xl: 16px; + --openbitfun-radius-xs: 4px; + --openbitfun-scrollbar-radius: var(--openbitfun-radius-pill); + --openbitfun-scrollbar-width: 6px; + --openbitfun-space-0: 0px; + --openbitfun-space-1: 4px; + --openbitfun-space-10: 40px; + --openbitfun-space-12: 48px; + --openbitfun-space-16: 64px; + --openbitfun-space-2: 8px; + --openbitfun-space-3: 12px; + --openbitfun-space-4: 16px; + --openbitfun-space-5: 20px; + --openbitfun-space-6: 24px; + --openbitfun-space-8: 32px; + --openbitfun-space-component-block: 8px; + --openbitfun-space-component-inline: 12px; + --openbitfun-type-body-lg-font-family: var(--openbitfun-font-family-sans); + --openbitfun-type-body-lg-font-size: var(--openbitfun-font-size-lg); + --openbitfun-type-body-lg-font-weight: var(--openbitfun-font-weight-regular); + --openbitfun-type-body-lg-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-body-lg-line-height: var(--openbitfun-line-height-relaxed); + --openbitfun-type-body-md-font-family: var(--openbitfun-font-family-sans); + --openbitfun-type-body-md-font-size: var(--openbitfun-font-size-base); + --openbitfun-type-body-md-font-weight: var(--openbitfun-font-weight-regular); + --openbitfun-type-body-md-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-body-md-line-height: var(--openbitfun-line-height-base); + --openbitfun-type-body-sm-font-family: var(--openbitfun-font-family-sans); + --openbitfun-type-body-sm-font-size: var(--openbitfun-font-size-sm); + --openbitfun-type-body-sm-font-weight: var(--openbitfun-font-weight-regular); + --openbitfun-type-body-sm-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-body-sm-line-height: var(--openbitfun-line-height-base); + --openbitfun-type-body-xs-font-family: var(--openbitfun-font-family-sans); + --openbitfun-type-body-xs-font-size: var(--openbitfun-font-size-xs); + --openbitfun-type-body-xs-font-weight: var(--openbitfun-font-weight-regular); + --openbitfun-type-body-xs-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-body-xs-line-height: var(--openbitfun-line-height-base); + --openbitfun-type-code-md-font-family: var(--openbitfun-font-family-mono); + --openbitfun-type-code-md-font-size: var(--openbitfun-font-size-sm); + --openbitfun-type-code-md-font-weight: var(--openbitfun-font-weight-regular); + --openbitfun-type-code-md-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-code-md-line-height: var(--openbitfun-line-height-code); + --openbitfun-type-code-sm-font-family: var(--openbitfun-font-family-mono); + --openbitfun-type-code-sm-font-size: var(--openbitfun-font-size-xs); + --openbitfun-type-code-sm-font-weight: var(--openbitfun-font-weight-regular); + --openbitfun-type-code-sm-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-code-sm-line-height: var(--openbitfun-line-height-code); + --openbitfun-type-display-fluid-hero-font-family: var(--openbitfun-font-family-sans); + --openbitfun-type-display-fluid-hero-font-size: clamp(var(--openbitfun-font-size-6xl), 4vw, var(--openbitfun-font-size-8xl)); + --openbitfun-type-display-fluid-hero-font-weight: var(--openbitfun-font-weight-semibold); + --openbitfun-type-display-fluid-hero-letter-spacing: var(--openbitfun-letter-spacing-tight); + --openbitfun-type-display-fluid-hero-line-height: var(--openbitfun-line-height-display); + --openbitfun-type-display-fluid-title-font-family: var(--openbitfun-font-family-sans); + --openbitfun-type-display-fluid-title-font-size: clamp(var(--openbitfun-font-size-5xl), 3.2vw, var(--openbitfun-font-size-7xl)); + --openbitfun-type-display-fluid-title-font-weight: var(--openbitfun-font-weight-semibold); + --openbitfun-type-display-fluid-title-letter-spacing: var(--openbitfun-letter-spacing-tight); + --openbitfun-type-display-fluid-title-line-height: var(--openbitfun-line-height-display); + --openbitfun-type-display-lg-font-family: var(--openbitfun-font-family-sans); + --openbitfun-type-display-lg-font-size: var(--openbitfun-font-size-7xl); + --openbitfun-type-display-lg-font-weight: var(--openbitfun-font-weight-semibold); + --openbitfun-type-display-lg-letter-spacing: var(--openbitfun-letter-spacing-tight); + --openbitfun-type-display-lg-line-height: var(--openbitfun-line-height-display); + --openbitfun-type-display-md-font-family: var(--openbitfun-font-family-sans); + --openbitfun-type-display-md-font-size: var(--openbitfun-font-size-6xl); + --openbitfun-type-display-md-font-weight: var(--openbitfun-font-weight-semibold); + --openbitfun-type-display-md-letter-spacing: var(--openbitfun-letter-spacing-tight); + --openbitfun-type-display-md-line-height: var(--openbitfun-line-height-display); + --openbitfun-type-display-sm-font-family: var(--openbitfun-font-family-sans); + --openbitfun-type-display-sm-font-size: var(--openbitfun-font-size-5xl); + --openbitfun-type-display-sm-font-weight: var(--openbitfun-font-weight-semibold); + --openbitfun-type-display-sm-letter-spacing: var(--openbitfun-letter-spacing-tight); + --openbitfun-type-display-sm-line-height: var(--openbitfun-line-height-display); + --openbitfun-type-display-xl-font-family: var(--openbitfun-font-family-sans); + --openbitfun-type-display-xl-font-size: var(--openbitfun-font-size-8xl); + --openbitfun-type-display-xl-font-weight: var(--openbitfun-font-weight-semibold); + --openbitfun-type-display-xl-letter-spacing: var(--openbitfun-letter-spacing-tight); + --openbitfun-type-display-xl-line-height: var(--openbitfun-line-height-display); + --openbitfun-type-display-xxl-font-family: var(--openbitfun-font-family-sans); + --openbitfun-type-display-xxl-font-size: var(--openbitfun-font-size-9xl); + --openbitfun-type-display-xxl-font-weight: var(--openbitfun-font-weight-semibold); + --openbitfun-type-display-xxl-letter-spacing: var(--openbitfun-letter-spacing-tight); + --openbitfun-type-display-xxl-line-height: var(--openbitfun-line-height-display); + --openbitfun-type-flow-body-font-family: var(--openbitfun-font-family-sans); + --openbitfun-type-flow-body-font-size: var(--openbitfun-font-size-base); + --openbitfun-type-flow-body-font-weight: var(--openbitfun-font-weight-regular); + --openbitfun-type-flow-body-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-flow-body-line-height: var(--openbitfun-line-height-reading); + --openbitfun-type-flow-code-font-family: var(--openbitfun-font-family-mono); + --openbitfun-type-flow-code-font-size: var(--openbitfun-font-size-sm); + --openbitfun-type-flow-code-font-weight: var(--openbitfun-font-weight-regular); + --openbitfun-type-flow-code-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-flow-code-line-height: var(--openbitfun-line-height-code); + --openbitfun-type-flow-control-font-family: var(--openbitfun-font-family-control); + --openbitfun-type-flow-control-font-size: var(--openbitfun-font-size-sm); + --openbitfun-type-flow-control-font-weight: var(--openbitfun-font-weight-regular); + --openbitfun-type-flow-control-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-flow-control-line-height: var(--openbitfun-line-height-support); + --openbitfun-type-flow-lead-font-family: var(--openbitfun-font-family-sans); + --openbitfun-type-flow-lead-font-size: var(--openbitfun-font-size-lg); + --openbitfun-type-flow-lead-font-weight: var(--openbitfun-font-weight-regular); + --openbitfun-type-flow-lead-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-flow-lead-line-height: var(--openbitfun-line-height-relaxed); + --openbitfun-type-flow-meta-font-family: var(--openbitfun-font-family-control); + --openbitfun-type-flow-meta-font-size: var(--openbitfun-font-size-meta); + --openbitfun-type-flow-meta-font-weight: var(--openbitfun-font-weight-regular); + --openbitfun-type-flow-meta-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-flow-meta-line-height: var(--openbitfun-line-height-compact); + --openbitfun-type-flow-micro-font-family: var(--openbitfun-font-family-control); + --openbitfun-type-flow-micro-font-size: var(--openbitfun-font-size-micro); + --openbitfun-type-flow-micro-font-weight: var(--openbitfun-font-weight-regular); + --openbitfun-type-flow-micro-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-flow-micro-line-height: var(--openbitfun-line-height-compact); + --openbitfun-type-flow-page-title-font-family: var(--openbitfun-font-family-sans); + --openbitfun-type-flow-page-title-font-size: var(--openbitfun-font-size-3xl); + --openbitfun-type-flow-page-title-font-weight: var(--openbitfun-font-weight-semibold); + --openbitfun-type-flow-page-title-letter-spacing: var(--openbitfun-letter-spacing-tight); + --openbitfun-type-flow-page-title-line-height: var(--openbitfun-line-height-tight); + --openbitfun-type-flow-section-title-font-family: var(--openbitfun-font-family-sans); + --openbitfun-type-flow-section-title-font-size: var(--openbitfun-font-size-2xl); + --openbitfun-type-flow-section-title-font-weight: var(--openbitfun-font-weight-semibold); + --openbitfun-type-flow-section-title-letter-spacing: var(--openbitfun-letter-spacing-snug); + --openbitfun-type-flow-section-title-line-height: var(--openbitfun-line-height-tight); + --openbitfun-type-flow-support-font-family: var(--openbitfun-font-family-control); + --openbitfun-type-flow-support-font-size: var(--openbitfun-font-size-xs); + --openbitfun-type-flow-support-font-weight: var(--openbitfun-font-weight-regular); + --openbitfun-type-flow-support-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-flow-support-line-height: var(--openbitfun-line-height-support); + --openbitfun-type-flow-title-font-family: var(--openbitfun-font-family-sans); + --openbitfun-type-flow-title-font-size: var(--openbitfun-font-size-xl); + --openbitfun-type-flow-title-font-weight: var(--openbitfun-font-weight-semibold); + --openbitfun-type-flow-title-letter-spacing: var(--openbitfun-letter-spacing-snug); + --openbitfun-type-flow-title-line-height: var(--openbitfun-line-height-tight); + --openbitfun-type-heading-card-font-family: var(--openbitfun-font-family-control); + --openbitfun-type-heading-card-font-size: var(--openbitfun-font-size-sm); + --openbitfun-type-heading-card-font-weight: var(--openbitfun-font-weight-semibold); + --openbitfun-type-heading-card-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-heading-card-line-height: var(--openbitfun-line-height-tight); + --openbitfun-type-heading-compact-page-font-family: var(--openbitfun-font-family-control); + --openbitfun-type-heading-compact-page-font-size: var(--openbitfun-font-size-2xl-plus); + --openbitfun-type-heading-compact-page-font-weight: var(--openbitfun-font-weight-semibold); + --openbitfun-type-heading-compact-page-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-heading-compact-page-line-height: var(--openbitfun-line-height-tight); + --openbitfun-type-heading-dialog-font-family: var(--openbitfun-font-family-sans); + --openbitfun-type-heading-dialog-font-size: var(--openbitfun-font-size-3xl); + --openbitfun-type-heading-dialog-font-weight: var(--openbitfun-font-weight-semibold); + --openbitfun-type-heading-dialog-letter-spacing: var(--openbitfun-letter-spacing-tight); + --openbitfun-type-heading-dialog-line-height: var(--openbitfun-line-height-tight); + --openbitfun-type-heading-display-font-family: var(--openbitfun-font-family-sans); + --openbitfun-type-heading-display-font-size: var(--openbitfun-font-size-4xl); + --openbitfun-type-heading-display-font-weight: var(--openbitfun-font-weight-semibold); + --openbitfun-type-heading-display-letter-spacing: var(--openbitfun-letter-spacing-tight); + --openbitfun-type-heading-display-line-height: var(--openbitfun-line-height-display); + --openbitfun-type-heading-navigation-font-family: var(--openbitfun-font-family-control); + --openbitfun-type-heading-navigation-font-size: var(--openbitfun-font-size-xl-plus); + --openbitfun-type-heading-navigation-font-weight: var(--openbitfun-font-weight-semibold); + --openbitfun-type-heading-navigation-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-heading-navigation-line-height: var(--openbitfun-line-height-tight); + --openbitfun-type-heading-page-font-family: var(--openbitfun-font-family-control); + --openbitfun-type-heading-page-font-size: var(--openbitfun-font-size-3xl-plus); + --openbitfun-type-heading-page-font-weight: var(--openbitfun-font-weight-bold); + --openbitfun-type-heading-page-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-heading-page-line-height: var(--openbitfun-line-height-tight); + --openbitfun-type-heading-section-font-family: var(--openbitfun-font-family-control); + --openbitfun-type-heading-section-font-size: var(--openbitfun-font-size-lg); + --openbitfun-type-heading-section-font-weight: var(--openbitfun-font-weight-semibold); + --openbitfun-type-heading-section-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-heading-section-line-height: var(--openbitfun-line-height-tight); + --openbitfun-type-label-lg-font-family: var(--openbitfun-font-family-control); + --openbitfun-type-label-lg-font-size: var(--openbitfun-font-size-base); + --openbitfun-type-label-lg-font-weight: var(--openbitfun-font-weight-medium); + --openbitfun-type-label-lg-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-label-lg-line-height: var(--openbitfun-line-height-tight); + --openbitfun-type-label-md-font-family: var(--openbitfun-font-family-control); + --openbitfun-type-label-md-font-size: var(--openbitfun-font-size-sm); + --openbitfun-type-label-md-font-weight: var(--openbitfun-font-weight-regular); + --openbitfun-type-label-md-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-label-md-line-height: var(--openbitfun-line-height-tight); + --openbitfun-type-label-selected-font-family: var(--openbitfun-font-family-control); + --openbitfun-type-label-selected-font-size: var(--openbitfun-font-size-sm); + --openbitfun-type-label-selected-font-weight: var(--openbitfun-font-weight-semibold); + --openbitfun-type-label-selected-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-label-selected-line-height: var(--openbitfun-line-height-tight); + --openbitfun-type-label-sm-font-family: var(--openbitfun-font-family-control); + --openbitfun-type-label-sm-font-size: var(--openbitfun-font-size-xs); + --openbitfun-type-label-sm-font-weight: var(--openbitfun-font-weight-medium); + --openbitfun-type-label-sm-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-label-sm-line-height: var(--openbitfun-line-height-tight); + --openbitfun-type-label-xs-font-family: var(--openbitfun-font-family-control); + --openbitfun-type-label-xs-font-size: var(--openbitfun-font-size-meta); + --openbitfun-type-label-xs-font-weight: var(--openbitfun-font-weight-medium); + --openbitfun-type-label-xs-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-label-xs-line-height: var(--openbitfun-line-height-tight); + --openbitfun-type-meta-font-family: var(--openbitfun-font-family-control); + --openbitfun-type-meta-font-size: var(--openbitfun-font-size-meta); + --openbitfun-type-meta-font-weight: var(--openbitfun-font-weight-regular); + --openbitfun-type-meta-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-meta-line-height: var(--openbitfun-line-height-compact); + --openbitfun-type-micro-font-family: var(--openbitfun-font-family-control); + --openbitfun-type-micro-font-size: var(--openbitfun-font-size-micro); + --openbitfun-type-micro-font-weight: var(--openbitfun-font-weight-regular); + --openbitfun-type-micro-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-micro-line-height: var(--openbitfun-line-height-compact); + --openbitfun-type-modifier-leading-balanced-line-height: var(--openbitfun-line-height-balanced); + --openbitfun-type-modifier-leading-dense-line-height: var(--openbitfun-line-height-dense); + --openbitfun-type-modifier-leading-loose-line-height: var(--openbitfun-line-height-loose); + --openbitfun-type-modifier-leading-none-line-height: var(--openbitfun-line-height-none); + --openbitfun-type-modifier-leading-snug-line-height: var(--openbitfun-line-height-snug); + --openbitfun-type-modifier-leading-spacious-line-height: var(--openbitfun-line-height-spacious); + --openbitfun-type-modifier-leading-support-line-height: var(--openbitfun-line-height-support); + --openbitfun-type-modifier-leading-ui-line-height: var(--openbitfun-line-height-ui); + --openbitfun-type-modifier-tracking-caps-letter-spacing: var(--openbitfun-letter-spacing-caps); + --openbitfun-type-modifier-tracking-expanded-letter-spacing: var(--openbitfun-letter-spacing-expanded); + --openbitfun-type-modifier-tracking-subtle-letter-spacing: var(--openbitfun-letter-spacing-subtle); + --openbitfun-type-modifier-tracking-tighter-letter-spacing: var(--openbitfun-letter-spacing-tighter); + --openbitfun-type-modifier-tracking-wide-letter-spacing: var(--openbitfun-letter-spacing-wide); + --openbitfun-type-modifier-tracking-wider-letter-spacing: var(--openbitfun-letter-spacing-wider); + --openbitfun-type-modifier-tracking-widest-letter-spacing: var(--openbitfun-letter-spacing-widest); + --openbitfun-type-overline-micro-font-family: var(--openbitfun-font-family-control); + --openbitfun-type-overline-micro-font-size: var(--openbitfun-font-size-4xs); + --openbitfun-type-overline-micro-font-weight: var(--openbitfun-font-weight-semibold); + --openbitfun-type-overline-micro-letter-spacing: var(--openbitfun-letter-spacing-widest); + --openbitfun-type-overline-micro-line-height: var(--openbitfun-line-height-none); + --openbitfun-type-overline-sm-font-family: var(--openbitfun-font-family-control); + --openbitfun-type-overline-sm-font-size: var(--openbitfun-font-size-2xs); + --openbitfun-type-overline-sm-font-weight: var(--openbitfun-font-weight-semibold); + --openbitfun-type-overline-sm-letter-spacing: var(--openbitfun-letter-spacing-wider); + --openbitfun-type-overline-sm-line-height: var(--openbitfun-line-height-none); + --openbitfun-type-overline-xs-font-family: var(--openbitfun-font-family-control); + --openbitfun-type-overline-xs-font-size: var(--openbitfun-font-size-3xs); + --openbitfun-type-overline-xs-font-weight: var(--openbitfun-font-weight-semibold); + --openbitfun-type-overline-xs-letter-spacing: var(--openbitfun-letter-spacing-widest); + --openbitfun-type-overline-xs-line-height: var(--openbitfun-line-height-none); + --openbitfun-type-support-font-family: var(--openbitfun-font-family-control); + --openbitfun-type-support-font-size: var(--openbitfun-font-size-meta); + --openbitfun-type-support-font-weight: var(--openbitfun-font-weight-regular); + --openbitfun-type-support-letter-spacing: var(--openbitfun-letter-spacing-normal); + --openbitfun-type-support-line-height: var(--openbitfun-line-height-comfortable); + } +} + +@layer openbitfun.tokens.system { + :where([data-openbitfun-design-system-root][data-density="compact"]) { + --openbitfun-control-height-lg: 44px; + --openbitfun-control-height-md: 36px; + --openbitfun-control-height-sm: 28px; + --openbitfun-control-hit-target: 36px; + --openbitfun-control-launcher-button-block-size: var(--openbitfun-control-height-md); + --openbitfun-control-tab-group-item-height: var(--openbitfun-control-height-md); + --openbitfun-space-component-block: 6px; + --openbitfun-space-component-inline: 10px; + } +} + +@layer openbitfun.tokens.system { + :where([data-openbitfun-design-system-root][data-density="touch"]) { + --openbitfun-control-height-lg: 56px; + --openbitfun-control-height-md: 48px; + --openbitfun-control-height-sm: 40px; + --openbitfun-control-hit-target: 48px; + --openbitfun-control-launcher-button-block-size: var(--openbitfun-control-height-md); + --openbitfun-control-tab-group-item-height: var(--openbitfun-control-height-md); + --openbitfun-space-component-block: 12px; + --openbitfun-space-component-inline: 16px; + } +} + +@media (prefers-reduced-motion: reduce) { + + :where([data-openbitfun-design-system-root]) { + + --openbitfun-motion-duration-fast: 0ms; + + --openbitfun-motion-duration-normal: 0ms; + + --openbitfun-motion-duration-slow: 0ms; + + --openbitfun-motion-duration-loop: 0ms; + + } + +} +@layer openbitfun.tokens.theme { + :where([data-openbitfun-design-system-root]), :where([data-openbitfun-design-system-root][data-color-scheme="light"]) { + --openbitfun-color-accent-border: color-mix(in srgb, #059cb0 40%, transparent); + --openbitfun-color-accent-border-subtle: color-mix(in srgb, #059cb0 25%, transparent); + --openbitfun-color-accent-default: #059cb0; + --openbitfun-color-accent-disabled: rgba(5, 156, 176, 0.30); + --openbitfun-color-accent-hover: #059cb0; + --openbitfun-color-accent-secondary: #7c6b99; + --openbitfun-color-accent-secondary-border: color-mix(in srgb, #7c6b99 25%, transparent); + --openbitfun-color-accent-secondary-hover: #655680; + --openbitfun-color-accent-secondary-surface: color-mix(in srgb, #7c6b99 9%, transparent); + --openbitfun-color-accent-secondary-surface-strong: color-mix(in srgb, #7c6b99 15%, transparent); + --openbitfun-color-accent-surface: color-mix(in srgb, #059cb0 9%, transparent); + --openbitfun-color-accent-surface-strong: color-mix(in srgb, #059cb0 15%, transparent); + --openbitfun-color-accent-surface-subtle: color-mix(in srgb, #059cb0 5%, transparent); + --openbitfun-color-action-neutral-border: rgba(0, 0, 0, 0.05); + --openbitfun-color-action-neutral-content: rgba(0, 0, 0, 0.80); + --openbitfun-color-action-neutral-content-disabled: rgba(0, 0, 0, 0.30); + --openbitfun-color-action-neutral-fill-border: rgba(0, 0, 0, 0.05); + --openbitfun-color-action-neutral-surface: rgba(0, 0, 0, 0.05); + --openbitfun-color-action-neutral-surface-hover: rgba(0, 0, 0, 0.08); + --openbitfun-color-action-neutral-surface-pressed: rgba(0, 0, 0, 0.10); + --openbitfun-color-action-primary-background: #101a27; + --openbitfun-color-action-primary-content: #ffffff; + --openbitfun-color-action-primary-hover: #1c1c1f; + --openbitfun-color-action-primary-pressed: #000000; + --openbitfun-color-action-quiet-content: rgba(0, 0, 0, 0.60); + --openbitfun-color-action-quiet-hover: #f3f3f5; + --openbitfun-color-action-quiet-pressed: rgba(16, 26, 39, 0.09); + --openbitfun-color-action-secondary-background: #f3f3f5; + --openbitfun-color-action-secondary-content: rgba(0, 0, 0, 0.80); + --openbitfun-color-action-secondary-hover: rgba(16, 26, 39, 0.09); + --openbitfun-color-action-secondary-pressed: rgba(16, 26, 39, 0.13); + --openbitfun-color-border-default: rgba(16, 26, 39, 0.15); + --openbitfun-color-border-strong: rgba(16, 26, 39, 0.34); + --openbitfun-color-border-subtle: rgba(16, 26, 39, 0.08); + --openbitfun-color-code-change-added: #1aa73e; + --openbitfun-color-code-change-removed: #ec221f; + --openbitfun-color-content-disabled: rgba(0, 0, 0, 0.30); + --openbitfun-color-content-inverse: #ffffff; + --openbitfun-color-content-muted: #6a6a6a; + --openbitfun-color-content-on-dark: #ffffff; + --openbitfun-color-content-on-light: #000000; + --openbitfun-color-content-primary: rgba(0, 0, 0, 0.80); + --openbitfun-color-content-required-indicator: #059cb0; + --openbitfun-color-content-secondary: rgba(0, 0, 0, 0.60); + --openbitfun-color-control-highlight-background: #059cb0; + --openbitfun-color-control-highlight-content: #000000; + --openbitfun-color-control-launcher-background: rgba(0, 0, 0, 0.10); + --openbitfun-color-control-launcher-background-hover: color-mix(in srgb, #059cb0 20%, transparent); + --openbitfun-color-control-launcher-background-pressed: color-mix(in srgb, #059cb0 30%, transparent); + --openbitfun-color-control-launcher-content: rgba(0, 0, 0, 0.80); + --openbitfun-color-control-launcher-content-disabled: rgba(0, 0, 0, 0.30); + --openbitfun-color-control-launcher-content-hover: #059cb0; + --openbitfun-color-control-launcher-content-pressed: #059cb0; + --openbitfun-color-control-switch-thumb: #ffffff; + --openbitfun-color-control-switch-track: #dddddd; + --openbitfun-color-control-switch-track-checked: #059cb0; + --openbitfun-color-field-background: #ffffff; + --openbitfun-color-field-background-hover: #ffffff; + --openbitfun-color-field-border: rgba(16, 26, 39, 0.15); + --openbitfun-color-field-border-focus: #858585; + --openbitfun-color-field-border-hover: rgba(16, 26, 39, 0.24); + --openbitfun-color-focus-ring: #6a6a6a; + --openbitfun-color-identity-assistant-border: color-mix(in srgb, #db2777 15%, transparent); + --openbitfun-color-identity-assistant-content: #db2777; + --openbitfun-color-identity-assistant-content-hover: #be185d; + --openbitfun-color-identity-assistant-surface: color-mix(in srgb, #db2777 9%, transparent); + --openbitfun-color-identity-assistant-surface-subtle: color-mix(in srgb, #db2777 5%, transparent); + --openbitfun-color-identity-global-search-new-project: #3271d7; + --openbitfun-color-identity-global-search-new-session: #ec221f; + --openbitfun-color-identity-global-search-open-browser: #ff8c00; + --openbitfun-color-identity-global-search-open-files: #9e54ff; + --openbitfun-color-identity-global-search-open-project: #059cb0; + --openbitfun-color-identity-global-search-open-terminal: rgba(0, 0, 0, 0.80); + --openbitfun-color-identity-harness-creative: #2e7eff; + --openbitfun-color-identity-harness-minimal: #b434ef; + --openbitfun-color-identity-harness-standard: #1aa73e; + --openbitfun-color-identity-harness-ultimate: #ff8c00; + --openbitfun-color-key-hint-background: rgba(0, 0, 0, 0.05); + --openbitfun-color-link-default: #2563eb; + --openbitfun-color-link-hover: #005fcc; + --openbitfun-color-overlay-scrim: rgba(16, 26, 39, 0.20); + --openbitfun-color-scrollbar-thumb: rgba(0, 0, 0, 0.08); + --openbitfun-color-scrollbar-thumb-hover: rgba(0, 0, 0, 0.10); + --openbitfun-color-selection-surface: rgba(0, 0, 0, 0.08); + --openbitfun-color-status-danger-border: rgba(236, 34, 31, 0.3); + --openbitfun-color-status-danger-content: #bd1b19; + --openbitfun-color-status-danger-emphasis: #ec221f; + --openbitfun-color-status-danger-surface: rgba(236, 34, 31, 0.1); + --openbitfun-color-status-info-border: rgba(46, 126, 255, 0.3); + --openbitfun-color-status-info-content: #2360c2; + --openbitfun-color-status-info-emphasis: #2e7eff; + --openbitfun-color-status-info-surface: rgba(46, 126, 255, 0.1); + --openbitfun-color-status-success-border: rgba(26, 167, 62, 0.3); + --openbitfun-color-status-success-content: #12722a; + --openbitfun-color-status-success-emphasis: #1aa73e; + --openbitfun-color-status-success-surface: rgba(26, 167, 62, 0.1); + --openbitfun-color-status-warning-border: rgba(255, 140, 0, 0.3); + --openbitfun-color-status-warning-content: #8a4c00; + --openbitfun-color-status-warning-emphasis: #ff8c00; + --openbitfun-color-status-warning-surface: rgba(255, 140, 0, 0.1); + --openbitfun-color-surface-canvas: #fdfdfd; + --openbitfun-color-surface-chrome: #f8f8f9; + --openbitfun-color-surface-panel: #ffffff; + --openbitfun-color-surface-raised: #ffffff; + --openbitfun-color-surface-scene: #ffffff; + --openbitfun-color-surface-subtle: rgba(16, 26, 39, 0.03); + --openbitfun-color-surface-tertiary: #f7f7f7; + --openbitfun-color-surface-workbench: #f3f3f5; + --openbitfun-effect-blur-base: blur(8px) saturate(1.05); + --openbitfun-effect-blur-medium: blur(12px) saturate(1.2); + --openbitfun-effect-blur-subtle: blur(4px) saturate(1.02); + --openbitfun-opacity-disabled: 0.55; + --openbitfun-opacity-focus: 0.9; + --openbitfun-opacity-hover: 0.75; + --openbitfun-opacity-muted: 0.75; + --openbitfun-shadow-accent-glow: 0 12px 32px color-mix(in srgb, #059cb0 25%, transparent), 0 6px 16px color-mix(in srgb, #059cb0 18%, transparent), 0 3px 8px rgba(0, 0, 0, 0.12); + --openbitfun-shadow-base: 0 4px 8px rgba(16, 26, 39, 0.07); + --openbitfun-shadow-composer: 0 2px 6px rgba(0, 0, 0, 0.08); + --openbitfun-shadow-inner-highlight: inset 0 1px 0 rgba(255, 255, 255, 0.08); + --openbitfun-shadow-inner-highlight-hover: inset 0 1px 0 rgba(255, 255, 255, 0.24); + --openbitfun-shadow-lg: 0 8px 16px rgba(16, 26, 39, 0.09); + --openbitfun-shadow-menu: 0 4px 10px rgba(0, 0, 0, 0.12); + --openbitfun-shadow-overlay: 0 4px 20px rgba(0, 0, 0, 0.12); + --openbitfun-shadow-raised: 0 2px 4px rgba(16, 26, 39, 0.055); + --openbitfun-shadow-sm: 0 2px 4px rgba(16, 26, 39, 0.055); + --openbitfun-shadow-xl: 0 12px 24px rgba(16, 26, 39, 0.11); + --openbitfun-shadow-xs: 0 1px 2px rgba(16, 26, 39, 0.04); + } +} + +@layer openbitfun.tokens.theme { + :where([data-openbitfun-design-system-root][data-color-scheme="dark"]) { + --openbitfun-color-accent-border: color-mix(in srgb, #60a5fa 40%, transparent); + --openbitfun-color-accent-border-subtle: color-mix(in srgb, #60a5fa 25%, transparent); + --openbitfun-color-accent-default: #60a5fa; + --openbitfun-color-accent-disabled: rgba(96, 165, 250, 0.30); + --openbitfun-color-accent-hover: #3b82f6; + --openbitfun-color-accent-secondary: #8b5cf6; + --openbitfun-color-accent-secondary-border: color-mix(in srgb, #8b5cf6 25%, transparent); + --openbitfun-color-accent-secondary-hover: #7c6b99; + --openbitfun-color-accent-secondary-surface: color-mix(in srgb, #8b5cf6 9%, transparent); + --openbitfun-color-accent-secondary-surface-strong: color-mix(in srgb, #8b5cf6 15%, transparent); + --openbitfun-color-accent-surface: color-mix(in srgb, #60a5fa 9%, transparent); + --openbitfun-color-accent-surface-strong: color-mix(in srgb, #60a5fa 15%, transparent); + --openbitfun-color-accent-surface-subtle: color-mix(in srgb, #60a5fa 5%, transparent); + --openbitfun-color-action-neutral-border: rgba(255, 255, 255, 0.18); + --openbitfun-color-action-neutral-content: #b0b0b0; + --openbitfun-color-action-neutral-content-disabled: #555555; + --openbitfun-color-action-neutral-fill-border: rgba(255, 255, 255, 0.1); + --openbitfun-color-action-neutral-surface: rgba(255, 255, 255, 0.1); + --openbitfun-color-action-neutral-surface-hover: rgba(255, 255, 255, 0.12); + --openbitfun-color-action-neutral-surface-pressed: rgba(255, 255, 255, 0.15); + --openbitfun-color-action-primary-background: rgba(255, 255, 255, 0.16); + --openbitfun-color-action-primary-content: #f3f3f5; + --openbitfun-color-action-primary-hover: rgba(255, 255, 255, 0.24); + --openbitfun-color-action-primary-pressed: rgba(255, 255, 255, 0.2); + --openbitfun-color-action-quiet-content: #b0b0b0; + --openbitfun-color-action-quiet-hover: rgba(255, 255, 255, 0.06); + --openbitfun-color-action-quiet-pressed: rgba(255, 255, 255, 0.1); + --openbitfun-color-action-secondary-background: rgba(255, 255, 255, 0.06); + --openbitfun-color-action-secondary-content: #e8e8e8; + --openbitfun-color-action-secondary-hover: rgba(255, 255, 255, 0.1); + --openbitfun-color-action-secondary-pressed: rgba(255, 255, 255, 0.12); + --openbitfun-color-border-default: rgba(255, 255, 255, 0.18); + --openbitfun-color-border-strong: rgba(255, 255, 255, 0.3); + --openbitfun-color-border-subtle: rgba(255, 255, 255, 0.12); + --openbitfun-color-code-change-added: #1aa73e; + --openbitfun-color-code-change-removed: #ec221f; + --openbitfun-color-content-disabled: #555555; + --openbitfun-color-content-inverse: #0e0e10; + --openbitfun-color-content-muted: #858585; + --openbitfun-color-content-on-dark: #ffffff; + --openbitfun-color-content-on-light: #000000; + --openbitfun-color-content-primary: #e8e8e8; + --openbitfun-color-content-required-indicator: #059cb0; + --openbitfun-color-content-secondary: #b0b0b0; + --openbitfun-color-control-highlight-background: #059cb0; + --openbitfun-color-control-highlight-content: #000000; + --openbitfun-color-control-launcher-background: rgba(255, 255, 255, 0.15); + --openbitfun-color-control-launcher-background-hover: color-mix(in srgb, #059cb0 20%, transparent); + --openbitfun-color-control-launcher-background-pressed: color-mix(in srgb, #059cb0 30%, transparent); + --openbitfun-color-control-launcher-content: #b0b0b0; + --openbitfun-color-control-launcher-content-disabled: #555555; + --openbitfun-color-control-launcher-content-hover: #059cb0; + --openbitfun-color-control-launcher-content-pressed: #059cb0; + --openbitfun-color-control-switch-thumb: #ffffff; + --openbitfun-color-control-switch-track: #555555; + --openbitfun-color-control-switch-track-checked: #059cb0; + --openbitfun-color-field-background: #1c1c1f; + --openbitfun-color-field-background-hover: rgba(255, 255, 255, 0.06); + --openbitfun-color-field-border: rgba(255, 255, 255, 0.18); + --openbitfun-color-field-border-focus: #858585; + --openbitfun-color-field-border-hover: rgba(255, 255, 255, 0.24); + --openbitfun-color-focus-ring: #60a5fa; + --openbitfun-color-identity-assistant-border: color-mix(in srgb, #ec4899 15%, transparent); + --openbitfun-color-identity-assistant-content: #ec4899; + --openbitfun-color-identity-assistant-content-hover: #f472b6; + --openbitfun-color-identity-assistant-surface: color-mix(in srgb, #ec4899 9%, transparent); + --openbitfun-color-identity-assistant-surface-subtle: color-mix(in srgb, #ec4899 5%, transparent); + --openbitfun-color-identity-global-search-new-project: #3271d7; + --openbitfun-color-identity-global-search-new-session: #ec221f; + --openbitfun-color-identity-global-search-open-browser: #ff8c00; + --openbitfun-color-identity-global-search-open-files: #9e54ff; + --openbitfun-color-identity-global-search-open-project: #059cb0; + --openbitfun-color-identity-global-search-open-terminal: #b0b0b0; + --openbitfun-color-identity-harness-creative: #2e7eff; + --openbitfun-color-identity-harness-minimal: #b434ef; + --openbitfun-color-identity-harness-standard: #1aa73e; + --openbitfun-color-identity-harness-ultimate: #ff8c00; + --openbitfun-color-key-hint-background: rgba(255, 255, 255, 0.1); + --openbitfun-color-link-default: #60a5fa; + --openbitfun-color-link-hover: #93c5fd; + --openbitfun-color-overlay-scrim: rgba(0, 0, 0, 0.56); + --openbitfun-color-scrollbar-thumb: rgba(255, 255, 255, 0.12); + --openbitfun-color-scrollbar-thumb-hover: rgba(255, 255, 255, 0.15); + --openbitfun-color-selection-surface: rgba(255, 255, 255, 0.12); + --openbitfun-color-status-danger-border: rgba(236, 34, 31, 0.3); + --openbitfun-color-status-danger-content: #f47f7d; + --openbitfun-color-status-danger-emphasis: #ec221f; + --openbitfun-color-status-danger-surface: rgba(236, 34, 31, 0.1); + --openbitfun-color-status-info-border: rgba(46, 126, 255, 0.3); + --openbitfun-color-status-info-content: #7dafff; + --openbitfun-color-status-info-emphasis: #2e7eff; + --openbitfun-color-status-info-surface: rgba(46, 126, 255, 0.1); + --openbitfun-color-status-success-border: rgba(26, 167, 62, 0.3); + --openbitfun-color-status-success-content: #5fc178; + --openbitfun-color-status-success-emphasis: #1aa73e; + --openbitfun-color-status-success-surface: rgba(26, 167, 62, 0.1); + --openbitfun-color-status-warning-border: rgba(255, 140, 0, 0.3); + --openbitfun-color-status-warning-content: #ff930f; + --openbitfun-color-status-warning-emphasis: #ff8c00; + --openbitfun-color-status-warning-surface: rgba(255, 140, 0, 0.1); + --openbitfun-color-surface-canvas: #0e0e10; + --openbitfun-color-surface-chrome: #0e0e10; + --openbitfun-color-surface-panel: #1c1c1f; + --openbitfun-color-surface-raised: #1c1c1f; + --openbitfun-color-surface-scene: #1c1c1f; + --openbitfun-color-surface-subtle: rgba(255, 255, 255, 0.06); + --openbitfun-color-surface-tertiary: #0e0e10; + --openbitfun-color-surface-workbench: #0e0e10; + --openbitfun-effect-blur-base: blur(8px) saturate(1.1); + --openbitfun-effect-blur-medium: blur(12px) saturate(1.2); + --openbitfun-effect-blur-subtle: blur(4px) saturate(1.05); + --openbitfun-opacity-disabled: 0.6; + --openbitfun-opacity-focus: 0.9; + --openbitfun-opacity-hover: 0.8; + --openbitfun-opacity-muted: 0.8; + --openbitfun-shadow-accent-glow: 0 12px 32px color-mix(in srgb, #3b82f6 25%, transparent), 0 6px 16px color-mix(in srgb, #60a5fa 18%, transparent), 0 3px 8px rgba(0, 0, 0, 0.12); + --openbitfun-shadow-base: 0 4px 8px rgba(0, 0, 0, 0.7); + --openbitfun-shadow-composer: 0 2px 6px rgba(0, 0, 0, 0.32); + --openbitfun-shadow-inner-highlight: inset 0 1px 0 rgba(255, 255, 255, 0.08); + --openbitfun-shadow-inner-highlight-hover: inset 0 1px 0 rgba(255, 255, 255, 0.24); + --openbitfun-shadow-lg: 0 8px 16px rgba(0, 0, 0, 0.6); + --openbitfun-shadow-menu: 0 4px 10px rgba(0, 0, 0, 0.48); + --openbitfun-shadow-overlay: 0 4px 20px rgba(0, 0, 0, 0.48); + --openbitfun-shadow-raised: 0 2px 4px rgba(0, 0, 0, 0.8); + --openbitfun-shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.8); + --openbitfun-shadow-xl: 0 12px 24px rgba(0, 0, 0, 0.5); + --openbitfun-shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.9); + } +} + +@layer openbitfun.tokens.theme { + :where([data-openbitfun-design-system-root][data-color-scheme="light"][data-contrast="high"]) { + --openbitfun-color-action-neutral-border: #000000; + --openbitfun-color-action-neutral-content: #000000; + --openbitfun-color-action-neutral-content-disabled: #343434; + --openbitfun-color-action-neutral-fill-border: #000000; + --openbitfun-color-action-neutral-surface: #e6e6e6; + --openbitfun-color-action-neutral-surface-hover: #cccccc; + --openbitfun-color-action-neutral-surface-pressed: #b3b3b3; + --openbitfun-color-action-primary-background: #000000; + --openbitfun-color-action-primary-hover: #262626; + --openbitfun-color-action-quiet-content: #161616; + --openbitfun-color-action-secondary-content: #000000; + --openbitfun-color-border-default: #3b3b3b; + --openbitfun-color-border-strong: #000000; + --openbitfun-color-border-subtle: #707070; + --openbitfun-color-content-muted: #343434; + --openbitfun-color-content-primary: #000000; + --openbitfun-color-content-required-indicator: #005fcc; + --openbitfun-color-content-secondary: #161616; + --openbitfun-color-control-highlight-background: #005fcc; + --openbitfun-color-control-highlight-content: #ffffff; + --openbitfun-color-control-launcher-background: #b3b3b3; + --openbitfun-color-control-launcher-background-hover: color-mix(in srgb, #005fcc 20%, transparent); + --openbitfun-color-control-launcher-background-pressed: color-mix(in srgb, #005fcc 30%, transparent); + --openbitfun-color-control-launcher-content: #000000; + --openbitfun-color-control-launcher-content-disabled: #343434; + --openbitfun-color-control-launcher-content-hover: #005fcc; + --openbitfun-color-control-launcher-content-pressed: #005fcc; + --openbitfun-color-control-switch-track: #3b3b3b; + --openbitfun-color-control-switch-track-checked: #005fcc; + --openbitfun-color-field-border: #3b3b3b; + --openbitfun-color-field-border-focus: #005fcc; + --openbitfun-color-field-border-hover: #000000; + --openbitfun-color-focus-ring: #005fcc; + --openbitfun-color-identity-global-search-open-terminal: #000000; + --openbitfun-color-key-hint-background: #e6e6e6; + --openbitfun-color-scrollbar-thumb: #cccccc; + --openbitfun-color-scrollbar-thumb-hover: #b3b3b3; + --openbitfun-color-selection-surface: #cccccc; + --openbitfun-color-status-danger-content: #8e1413; + --openbitfun-color-status-info-content: #1c4c99; + --openbitfun-color-status-success-content: #0d541f; + --openbitfun-color-status-warning-content: #663800; + --openbitfun-color-status-warning-surface: rgba(255, 140, 0, 0.04); + --openbitfun-color-surface-canvas: #ffffff; + } +} + +@layer openbitfun.tokens.theme { + :where([data-openbitfun-design-system-root][data-color-scheme="dark"][data-contrast="high"]) { + --openbitfun-color-action-neutral-border: #ffffff; + --openbitfun-color-action-neutral-content: #ffffff; + --openbitfun-color-action-neutral-content-disabled: #d0d0d0; + --openbitfun-color-action-neutral-fill-border: #ffffff; + --openbitfun-color-action-neutral-surface: #343434; + --openbitfun-color-action-neutral-surface-hover: #505050; + --openbitfun-color-action-neutral-surface-pressed: #6b6b6b; + --openbitfun-color-action-primary-background: #ffffff; + --openbitfun-color-action-primary-content: #000000; + --openbitfun-color-action-primary-hover: #e6e6e6; + --openbitfun-color-action-primary-pressed: #ffffff; + --openbitfun-color-border-default: #b5b5b5; + --openbitfun-color-border-strong: #ffffff; + --openbitfun-color-border-subtle: #8a8a8a; + --openbitfun-color-content-muted: #d0d0d0; + --openbitfun-color-content-primary: #ffffff; + --openbitfun-color-content-required-indicator: #ffcc00; + --openbitfun-color-content-secondary: #f0f0f0; + --openbitfun-color-control-highlight-background: #ffcc00; + --openbitfun-color-control-launcher-background: #6b6b6b; + --openbitfun-color-control-launcher-background-hover: color-mix(in srgb, #ffcc00 20%, transparent); + --openbitfun-color-control-launcher-background-pressed: color-mix(in srgb, #ffcc00 30%, transparent); + --openbitfun-color-control-launcher-content: #ffffff; + --openbitfun-color-control-launcher-content-disabled: #d0d0d0; + --openbitfun-color-control-launcher-content-hover: #ffcc00; + --openbitfun-color-control-launcher-content-pressed: #ffcc00; + --openbitfun-color-control-switch-thumb: #000000; + --openbitfun-color-control-switch-track: #b5b5b5; + --openbitfun-color-control-switch-track-checked: #ffcc00; + --openbitfun-color-field-border: #b5b5b5; + --openbitfun-color-field-border-focus: #ffcc00; + --openbitfun-color-field-border-hover: #ffffff; + --openbitfun-color-focus-ring: #ffcc00; + --openbitfun-color-identity-global-search-open-terminal: #ffffff; + --openbitfun-color-key-hint-background: #343434; + --openbitfun-color-scrollbar-thumb: #505050; + --openbitfun-color-scrollbar-thumb-hover: #6b6b6b; + --openbitfun-color-selection-surface: #505050; + --openbitfun-color-status-danger-content: #f7a7a5; + --openbitfun-color-status-info-content: #abcbff; + --openbitfun-color-status-success-content: #afe0bb; + --openbitfun-color-status-warning-content: #ffa940; + --openbitfun-color-status-warning-surface: rgba(255, 140, 0, 0.04); + --openbitfun-color-surface-canvas: #000000; + --openbitfun-color-surface-chrome: #000000; + --openbitfun-color-surface-panel: #090909; + --openbitfun-color-surface-scene: #090909; + --openbitfun-color-surface-tertiary: #000000; + --openbitfun-color-surface-workbench: #000000; + } +} diff --git a/src/apps/data-migrator/ui/index.html b/src/apps/data-migrator/ui/index.html index 38dd720570..5efdedb074 100644 --- a/src/apps/data-migrator/ui/index.html +++ b/src/apps/data-migrator/ui/index.html @@ -1,11 +1,13 @@ - + OpenBitFun Data Migrator + + - +

@@ -15,6 +17,11 @@

Import data from BitFun

Choose what to bring forward. Your original BitFun data will not be deleted.

+
+ + + +
@@ -25,23 +32,19 @@

Import data from BitFun

Step 1

Legacy source

- Checking + Checking

-

+

+
+
-