From da28ef0991763caa447d183107bd70773b55658e Mon Sep 17 00:00:00 2001 From: qnbs <155236708+qnbs@users.noreply.github.com> Date: Fri, 14 Aug 2026 06:16:32 +0200 Subject: [PATCH 1/2] =?UTF-8?q?release:=20v1.27.1=20=E2=80=94=20desktop=20?= =?UTF-8?q?persistence/security=20stabilization=20(#363)=20+=20nanoid=20pa?= =?UTF-8?q?tch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cuts everything unreleased on main since v1.27.0: PR #363 (atomic writes across all Tauri fs-backed stores, fail-closed desktop key routing, factory-reset UI consolidation, packaged-build capability-scope fix, rust-tauri/e2e/vrt promoted to required CI gates) and PR #362 (nanoid security-advisory patch). Co-Authored-By: Claude Sonnet 5 --- AUDIT.md | 6 ++-- CHANGELOG.md | 63 +++++++++++++++++++++++++++++++++++++++ README.md | 2 +- package.json | 2 +- public/sw.js | 2 +- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/tauri.conf.json | 2 +- 8 files changed, 73 insertions(+), 8 deletions(-) diff --git a/AUDIT.md b/AUDIT.md index 612d1a57..ed534b75 100644 --- a/AUDIT.md +++ b/AUDIT.md @@ -1,8 +1,10 @@ # WorldScript Studio — Codebase Audit Report -**Date:** 2026-04-17 (baseline); **follow-up chain:** … → 2026-05-28 (v1.19.0 — Security/Voice/RTL/Logger B-1..B-8) → **2026-05-30 (B-1 passphrase UX + CI unblock)** → **2026-05-31 (i18n audit + settings features + CI stabilization)** → **2026-05-31 (Edge-AI Perfection Cycle — Phases 0-7 complete)** → **2026-06-01 (Post-crash session: CI stabilisation + 14 CodeAnt AI fixes + E2E hardening)** → **2026-06-02 (Perf Phase 2.3 — pipeline-LRU unification + PR #69 CodeAnt fixes)** → **2026-06-03 (WorkerBus v2 Phase 3 — Rust TaskSupervisor + Tauri-build unblock)** → **2026-06-06 (Phase 3 i18n Expansion — ja/zh/pt/el + Intl APIs)** → **2026-06-09 (v1.21 Deep Audit Correction — Whisper WASM download UI + 3 CodeAnt fixes + CloudSync LWW)** → **2026-06-09 (feat/deep-audit-v1.21 — CSP hardening, zh locale ≤5% EN, coverage Batches A/B/C, VoiceActivityCoordinator B-2 bridge)** → **2026-06-11 (Ultimate Copilot v2 Phase 2+3 — markdown, sidebar, Apply-to-chapter, InlineAnnotation, ProForge chip; PR #110+#111)** → **2026-06-11 (v1.22.0 release — OpenRouter Cloud 5 provider, AI Execution Modes hybrid/cloud/local/eco, AiModeIndicator, SW cache-invalidation fix)** → **2026-06-13/14 (v1.23 perfection batch — OpenRouter + AI-Execution-Mode settings sections localized/modernized, i18n single-brace interpolation bug-class fix + `i18nPlaceholders` regression guard, bundle split + budget tightening PR #130)** → **2026-06-16 (v1.23.0 release — rebrand StoryCraft → WorldScript Studio, local-first data foundation ADR-0008, Tauri blank-screen + asset-URL fixes, AI error taxonomy + retry hardening, command-palette & local-AI settings localization, WorldScript W monogram icons)** → **2026-06-16 (post-release documentation perfection pass — corpus sync, metric reconciliation, history archival, dependabot hardening)** → **2026-06-17 (Language expansion — +6 locales fi/sv/hu/is/eu/fa (RTL); PR #174 merged; `LanguageSelector` exonym localization via `portal.language.names.*`; portal chrome 100 % for the 6 new langs; README/AUDIT/CHANGELOG docs sync)** → **2026-07-28 (v1.24.1 — Local-AI reliability fixes #266, Dependabot backlog triage, Issue #60 audit closeout, security/build hardening)** → **2026-07-29 (v1.24.2, PR #284 — CSP functional-truth, desktop crypto, and doc-truth hardening; the local-inference stack had been silently non-functional in production for two months; merged and tagged)** → **2026-07-30 (v1.24.3, WorkerBus v2 worker-generation consolidation — 5 stacked PRs #286–288/#290–291 + independent #289 recovered after an interrupted merge queue; ADR-0015; merged and tagged)** → **2026-08-12 (encryption lifecycle + desktop reliability + recovery journal — stacked PRs #335/#336/#337 merged into `main`; fail-closed IDB encryption lifecycle, desktop AI/Python provider hardening for #332/#333, durable resumable migration journal with secondary-store adapters; PR #339 closes the write-vs-migration TOCTOU race CodeRabbit and Qodo independently found on #337; PR #310 closed as superseded; Phase-4 production wiring for disable/rotate tracked as issue #338)** → **2026-08-13 (v1.27.0 — Phase 4 encryption disable/rotate production wiring + i18n (#342/#343 close issue #338), Tauri desktop boot-hydration + flush-on-quit + Settings re-render perf fixes (#332, PR #345), AI Writing Studio unreadable-text fix (#341, PR #344), download-progress bytes/speed for voice + WebLLM models (#333 item 1, PR #346); all 5 PRs' correction loops ran to full quiescence — codebase-wide QNBS-v3/i18n fixes, a real data-integrity bug found and fixed in the rekey-recovery try/catch scoping, and sequential main-reconciliation merges across the whole 5-PR stack)** +**Date:** 2026-04-17 (baseline); **follow-up chain:** … → 2026-05-28 (v1.19.0 — Security/Voice/RTL/Logger B-1..B-8) → **2026-05-30 (B-1 passphrase UX + CI unblock)** → **2026-05-31 (i18n audit + settings features + CI stabilization)** → **2026-05-31 (Edge-AI Perfection Cycle — Phases 0-7 complete)** → **2026-06-01 (Post-crash session: CI stabilisation + 14 CodeAnt AI fixes + E2E hardening)** → **2026-06-02 (Perf Phase 2.3 — pipeline-LRU unification + PR #69 CodeAnt fixes)** → **2026-06-03 (WorkerBus v2 Phase 3 — Rust TaskSupervisor + Tauri-build unblock)** → **2026-06-06 (Phase 3 i18n Expansion — ja/zh/pt/el + Intl APIs)** → **2026-06-09 (v1.21 Deep Audit Correction — Whisper WASM download UI + 3 CodeAnt fixes + CloudSync LWW)** → **2026-06-09 (feat/deep-audit-v1.21 — CSP hardening, zh locale ≤5% EN, coverage Batches A/B/C, VoiceActivityCoordinator B-2 bridge)** → **2026-06-11 (Ultimate Copilot v2 Phase 2+3 — markdown, sidebar, Apply-to-chapter, InlineAnnotation, ProForge chip; PR #110+#111)** → **2026-06-11 (v1.22.0 release — OpenRouter Cloud 5 provider, AI Execution Modes hybrid/cloud/local/eco, AiModeIndicator, SW cache-invalidation fix)** → **2026-06-13/14 (v1.23 perfection batch — OpenRouter + AI-Execution-Mode settings sections localized/modernized, i18n single-brace interpolation bug-class fix + `i18nPlaceholders` regression guard, bundle split + budget tightening PR #130)** → **2026-06-16 (v1.23.0 release — rebrand StoryCraft → WorldScript Studio, local-first data foundation ADR-0008, Tauri blank-screen + asset-URL fixes, AI error taxonomy + retry hardening, command-palette & local-AI settings localization, WorldScript W monogram icons)** → **2026-06-16 (post-release documentation perfection pass — corpus sync, metric reconciliation, history archival, dependabot hardening)** → **2026-06-17 (Language expansion — +6 locales fi/sv/hu/is/eu/fa (RTL); PR #174 merged; `LanguageSelector` exonym localization via `portal.language.names.*`; portal chrome 100 % for the 6 new langs; README/AUDIT/CHANGELOG docs sync)** → **2026-07-28 (v1.24.1 — Local-AI reliability fixes #266, Dependabot backlog triage, Issue #60 audit closeout, security/build hardening)** → **2026-07-29 (v1.24.2, PR #284 — CSP functional-truth, desktop crypto, and doc-truth hardening; the local-inference stack had been silently non-functional in production for two months; merged and tagged)** → **2026-07-30 (v1.24.3, WorkerBus v2 worker-generation consolidation — 5 stacked PRs #286–288/#290–291 + independent #289 recovered after an interrupted merge queue; ADR-0015; merged and tagged)** → **2026-08-12 (encryption lifecycle + desktop reliability + recovery journal — stacked PRs #335/#336/#337 merged into `main`; fail-closed IDB encryption lifecycle, desktop AI/Python provider hardening for #332/#333, durable resumable migration journal with secondary-store adapters; PR #339 closes the write-vs-migration TOCTOU race CodeRabbit and Qodo independently found on #337; PR #310 closed as superseded; Phase-4 production wiring for disable/rotate tracked as issue #338)** → **2026-08-13 (v1.27.0 — Phase 4 encryption disable/rotate production wiring + i18n (#342/#343 close issue #338), Tauri desktop boot-hydration + flush-on-quit + Settings re-render perf fixes (#332, PR #345), AI Writing Studio unreadable-text fix (#341, PR #344), download-progress bytes/speed for voice + WebLLM models (#333 item 1, PR #346); all 5 PRs' correction loops ran to full quiescence — codebase-wide QNBS-v3/i18n fixes, a real data-integrity bug found and fixed in the rekey-recovery try/catch scoping, and sequential main-reconciliation merges across the whole 5-PR stack)** → **2026-08-14 (v1.27.1 — desktop persistence/security stabilization; #363: atomic writes across every Tauri fs-backed store, unified fail-closed desktop API-key routing, factory-reset UI/logic consolidated into `useFactoryReset` + `FactoryResetDangerZone`, packaged-build factory-reset capability-scope fix, `#[cfg(desktop)]`-gated menu-event handler + re-wired native menu-action bridge, `rust-tauri`/`e2e`/`vrt` promoted to required CI gates; plus the nanoid security-advisory patch, PR #362)** **Scope:** Full application, repository configuration, CI/CD, documentation, release validation -**Current version:** **v1.27.0** — Phase 4 closes out the encryption lifecycle feature (issue #338: production disable/rotate wiring, now user-facing in Settings › Privacy), alongside three independently-shipped reliability/UX fixes for #332, #333, and #341. **2919 keys × 19 locales**. Historical release sections below remain historical. +**Current version:** **v1.27.1** — Desktop persistence/security stabilization (#363: atomic writes, fail-closed key routing, factory-reset consolidation, packaged-build capability fix, Rust/E2E/VRT promoted to required CI gates) plus the nanoid security-advisory patch (#362). **2919 keys × 19 locales**. Historical release sections below remain historical. + +**Quality gate (2026-08-14 — v1.27.1):** lint ✅ · typecheck ✅ (tsgo) · i18n:check ✅ (**2919 keys × 19 locales**) — verified locally per the CI-cloud-first workflow; full CI suite (Quality Gate Node 22+24, rust-tauri, Build, E2E, E2E Deep Coverage, Storybook, Lighthouse, Visual Regression) tracked on the `release/v1.27.1` → `main` PR before merge/tag. **Quality gate (2026-08-13 — v1.27.0, 5-PR merge):** lint ✅ · typecheck ✅ (tsgo) · i18n:check ✅ (**2919 keys × 19 locales**) · targeted unit tests ✅ across all 5 PRs' correction loops (storageEncryptionService 75 tests, useSettingsView 38, VoiceModelDownloadModal/voiceCommandService/downloadProgressFormat, desktopTray/desktopMenu, ContextPanel/Textarea, and the full post-merge reconciliation suite) · CI Quality Gate (Node 22 + 24), E2E, E2E Deep Coverage, Build, Storybook, Lighthouse, and Visual Regression all green on every one of #342/#343/#344/#345/#346's final commits. #342 (disable/rotate production wiring), #343 (its stacked i18n), #344 (#341 fix), #345 (#332 fix), #346 (#333 item 1) all merged into `main`. Phase-4 (issue #338) is now fully closed — encryption disable and passphrase rotation are live in the Settings UI. diff --git a/CHANGELOG.md b/CHANGELOG.md index e943880c..dbb6e745 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,69 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.27.1] — 2026-08-14 + +> Desktop persistence/security stabilization (#363) — atomic writes and fail-closed key routing +> across every Tauri filesystem-backed store, a factory-reset UI/logic consolidation, and three +> new required CI gates (Rust compile/lint/test, E2E, Visual Regression) that previously let +> native-code and UI-regression risk merge unchecked — plus a nanoid security-advisory patch. + +### Fixed + +- **Atomic writes across all Tauri filesystem-backed stores** (`services/fs/{assetFsStore, + codexFsStore,fsCore,projectFsStore,settingsFsStore,snapshotFsStore}.ts`) — writes are now + serialized, and an orphaned temp file left behind by a failed write is retried on cleanup (with a + warning logged, not silently dropped) instead of risking corruption of concurrently-written + project data. +- **Unified, fail-closed desktop API-key routing.** Desktop key storage now routes unconditionally + through the same encrypted IndexedDB path used on the web, with recovery-reset hardened to fail + closed rather than leaving a partially-wiped state; `docs/SECURITY-THREAT-MODEL.md` corrected to + describe the unconditional routing and the best-effort (not guaranteed) nature of legacy-key-file + cleanup. +- **Factory reset / danger-zone UI consolidated.** New `hooks/useFactoryReset.ts` + + `components/settings/FactoryResetDangerZone.tsx` replace three near-duplicate confirm/wipe/error/ + busy implementations across `ApiKeySection`, `IdbUnlockModal`, and `EncryptionRecoveryModal`; also + fixes a bug where the stuck "recovery-required" branch had no error paragraph to render a failed + reset's error state. +- **Packaged-build factory reset was broken.** The Tauri fs capability glob for `fs:allow-exists` + and `fs:allow-read-dir` only covered `$APPDATA/**`, rejecting `exists()`/`readDir()` on the + `$APPDATA` root itself; the capability now grants the exact root path too + (`src-tauri/capabilities/default.json`). +- **Corrupt binder-asset detection fixed.** A binary/metadata byte-size mismatch is now treated as + corruption instead of silently pairing two independent atomic writes from different save + generations as if they were one transaction. +- **Legacy API-key file cleanup no longer provider-list-bound.** Scans the config directory by the + `*_key.enc.json` naming convention instead of a hardcoded 5-provider list, so a future or + unlisted provider's legacy key file is no longer left behind after migration. +- **Rust: `Builder::on_menu_event` gated `#[cfg(desktop)]`.** The call was unconditional despite + the crate itself restricting that method to desktop, which would have failed to compile for the + mobile targets `src-tauri/src/lib.rs` already anticipates (`#[cfg_attr(mobile, ...)]`). +- **Re-wired the dead Rust menu-action event bridge.** `registerTauriMenuHandler` is back as a + pre-paint-window fallback, self-unregistering once the JS-owned menu (`installDesktopMenu`) takes + over — the native fallback menu's items were previously silently inert, and the two menus can no + longer double-dispatch a single click. + +### Security + +- **nanoid security advisory patched** (PR #362) — `nanoid@3.3.17` → `3.3.18`, added to + `minimumReleaseAgeExclude` in `pnpm-workspace.yaml` so the fix isn't held back by the + 10,080-minute (7-day) `minimumReleaseAge` policy. + +### Changed + +- **CI: Rust compile/lint/test, E2E, and Visual Regression Testing are now required merge gates** + (`rust-tauri`, `e2e`, `vrt` join the `ci-success` aggregator's `needs`). Previously native Rust + code and UI regressions could merge into `main` with no automated check beyond an advisory OSV + vulnerability scan. + +### Tests + +- Added coverage for the new `clearTauriAppData` branch and the factory-reset danger-zone UI + (`tests/unit/factoryResetService.test.ts`, `tests/unit/settings/{EncryptionRecoveryModal, + IdbUnlockModal}.test.tsx`, `tests/unit/services/fs/{fsCore,fsStores}.test.ts`, + `tests/unit/tauriServices.test.ts`, `tests/unit/ApiKeySection.test.tsx`) — `codecov/patch` had + dropped to 52.75% before this pass. + ## [1.27.0] — 2026-08-13 ### Added diff --git a/README.md b/README.md index 93786834..54233af7 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ TypeScript 7 (tsgo) Gemini · OpenAI · OpenRouter · Ollama · WebLLM WebGPU · ONNX · Transformers.js - v1.27.0 + v1.27.1 IndexedDB v8 PWA v3.0 i18n 19 locales — 2919 keys diff --git a/package.json b/package.json index a3ae75e4..ccd63fc5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "worldscript-studio", "private": true, - "version": "1.27.0", + "version": "1.27.1", "description": "WorldScript Studio — offline-first, AI-powered creative writing application.", "author": "QNBS", "keywords": [ diff --git a/public/sw.js b/public/sw.js index f1be5c9d..f6d47ff8 100644 --- a/public/sw.js +++ b/public/sw.js @@ -6,7 +6,7 @@ // ============================================================ // QNBS-v3 (CodeRabbit): version bump auto-synced from package.json by scripts/sync-sw-version.mjs — every CACHE_STATIC/CACHE_DYNAMIC name changes too, so this alone invalidates all prior caches on next activate. -const APP_VERSION = '1.27.0'; +const APP_VERSION = '1.27.1'; const CACHE_STATIC = `worldscript-static-v${APP_VERSION}`; const CACHE_DYNAMIC = `worldscript-dynamic-v${APP_VERSION}`; const CACHE_IMAGES = `worldscript-images-v${APP_VERSION}`; diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 83083f08..9017018a 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -6677,7 +6677,7 @@ dependencies = [ [[package]] name = "worldscript-studio" -version = "1.27.0" +version = "1.27.1" dependencies = [ "base64 0.23.1", "candle-core", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 1435ba94..5fed8d12 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "worldscript-studio" -version = "1.27.0" +version = "1.27.1" description = "AI-powered creative writing and world-building application" authors = ["QNBS"] license = "MIT" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index ee736c88..b1a8ba5c 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", "productName": "WorldScript Studio", - "version": "1.27.0", + "version": "1.27.1", "identifier": "com.worldscript.studio", "build": { "frontendDist": "../dist", From fc0f0a25e8ce3101e4d3795ce45ccea6ef9ce1d5 Mon Sep 17 00:00:00 2001 From: qnbs <155236708+qnbs@users.noreply.github.com> Date: Fri, 14 Aug 2026 06:41:38 +0200 Subject: [PATCH 2/2] fix(docs): sync AGENTS.md Version field, prevent recurrence in sync-tauri-version.mjs AGENTS.md still declared 1.27.0 after the version bump (qodo-code-review finding on PR #364). Fixes the immediate drift and wires AGENTS.md's Version field plus Cargo.lock's own locked package-version entry into scripts/sync-tauri-version.mjs so future releases can't reintroduce either drift. Co-Authored-By: Claude Sonnet 5 --- AGENTS.md | 2 +- scripts/sync-tauri-version.mjs | 43 ++++++++++++++++++++-------------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 76fc1a6b..bd657369 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,7 +11,7 @@ - **Primary deploy target:** Static SPA on GitHub Pages (`/WorldScript-Studio/` base path) - **Secondary targets:** Vercel (root base) and Cloudflare Pages via edge builds (`pnpm run build:edge`) - **Desktop:** Tauri 2 bundles for Linux (AppImage), macOS (DMG), and Windows (MSI); auto-updater enabled via `latest.json` -- **Version:** `1.27.0` +- **Version:** `1.27.1` - **License:** MIT The app supports a multi-provider AI stack (Gemini, OpenAI, Claude, Grok, OpenRouter, Ollama, WebLLM, ONNX Runtime Web, Transformers.js), four AI execution modes (Hybrid / Cloud / Local / Eco), real-time collaboration with E2E encryption, a Plot Board v2 with swimlane/canvas/timeline modes, character/world management, manuscript export, voice dictation, and a 19-locale i18n layer. diff --git a/scripts/sync-tauri-version.mjs b/scripts/sync-tauri-version.mjs index 406701db..f034f5ec 100755 --- a/scripts/sync-tauri-version.mjs +++ b/scripts/sync-tauri-version.mjs @@ -1,7 +1,10 @@ #!/usr/bin/env node /** - * Sync Tauri version from package.json → src-tauri/Cargo.toml + src-tauri/tauri.conf.json - * QNBS-v3: Prevents version drift between web (package.json) and desktop builds. + * Sync version from package.json → src-tauri/Cargo.toml, src-tauri/tauri.conf.json, + * src-tauri/Cargo.lock (the workspace package's own locked entry), and AGENTS.md. + * QNBS-v3: Prevents version drift between web (package.json) and desktop builds/docs — a stale + * Cargo.lock entry fails `cargo check --locked` in CI's rust-check gate, and a stale AGENTS.md + * `Version:` field is agent-facing documentation drift (found by review-loop bots on PR #364). * Run via predev / prebuild hooks or manually: * node scripts/sync-tauri-version.mjs */ @@ -15,6 +18,8 @@ const root = path.join(__dirname, '..'); const pkgPath = path.join(root, 'package.json'); const cargoPath = path.join(root, 'src-tauri', 'Cargo.toml'); const tauriConfPath = path.join(root, 'src-tauri', 'tauri.conf.json'); +const cargoLockPath = path.join(root, 'src-tauri', 'Cargo.lock'); +const agentsPath = path.join(root, 'AGENTS.md'); const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); const version = pkg.version; @@ -25,25 +30,27 @@ if (!version || typeof version !== 'string') { let changed = false; -// --- Cargo.toml --- -const cargo = fs.readFileSync(cargoPath, 'utf8'); -const cargoRe = /^version = "[^"]+"/m; -const cargoNew = cargo.replace(cargoRe, `version = "${version}"`); -if (cargoNew !== cargo) { - fs.writeFileSync(cargoPath, cargoNew); - console.log(`[sync-tauri-version] ${cargoPath} → ${version}`); - changed = true; +function syncFile(filePath, pattern, replacement) { + const content = fs.readFileSync(filePath, 'utf8'); + const next = content.replace(pattern, replacement); + if (next !== content) { + fs.writeFileSync(filePath, next); + console.log(`[sync-tauri-version] ${filePath} → ${version}`); + changed = true; + } } +// --- Cargo.toml --- +syncFile(cargoPath, /^version = "[^"]+"/m, `version = "${version}"`); + // --- tauri.conf.json --- -const tauriConf = fs.readFileSync(tauriConfPath, 'utf8'); -const tauriRe = /"version":\s*"[^"]+"/; -const tauriNew = tauriConf.replace(tauriRe, `"version": "${version}"`); -if (tauriNew !== tauriConf) { - fs.writeFileSync(tauriConfPath, tauriNew); - console.log(`[sync-tauri-version] ${tauriConfPath} → ${version}`); - changed = true; -} +syncFile(tauriConfPath, /"version":\s*"[^"]+"/, `"version": "${version}"`); + +// --- Cargo.lock (the workspace package's own locked entry, not registry deps) --- +syncFile(cargoLockPath, /(name = "worldscript-studio"\nversion = )"[^"]+"/, `$1"${version}"`); + +// --- AGENTS.md --- +syncFile(agentsPath, /(\*\*Version:\*\* `)[^`]+(`)/, `$1${version}$2`); if (!changed) { console.log('[sync-tauri-version] Already in sync.');