From 5e3e7e1330ef13edc611fea36e29998dbf3e33c5 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Sat, 31 Oct 2026 06:19:11 +0000 Subject: [PATCH] Fix CLI internal path filtering on Windows Split logical Vault paths on '/' on every platform so that internal database files and hidden path segments remain excluded when the database and Vault share a directory. Cover POSIX and Windows host separators through the real CLI bootstrap filter, preserving the omitted-Vault-path E2E scenario. --- src/apps/cli/main.bootstrap.unit.spec.ts | 36 ++++++++++++++++++++++++ src/apps/cli/main.ts | 2 +- updates.md | 2 ++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/apps/cli/main.bootstrap.unit.spec.ts b/src/apps/cli/main.bootstrap.unit.spec.ts index 033771a1..14bbf804 100644 --- a/src/apps/cli/main.bootstrap.unit.spec.ts +++ b/src/apps/cli/main.bootstrap.unit.spec.ts @@ -3,6 +3,7 @@ import { appendFile, mkdtemp, mkdir, readFile, rm, stat, writeFile } from "node: import { tmpdir } from "node:os"; import { join } from "node:path"; import * as chokidar from "chokidar"; +import { path } from "@vrtmrz/livesync-commonlib/node"; import * as offlineScanner from "@vrtmrz/livesync-commonlib/compat/serviceFeatures/offlineScanner"; import { ControlService } from "@vrtmrz/livesync-commonlib/compat/services/base/ControlService"; import { @@ -20,6 +21,11 @@ import { main, type CliCommandRunner } from "./main"; import { cloneSettings } from "./settingsPersistence"; import type { CLICommand } from "./commands/types"; +vi.mock("@vrtmrz/livesync-commonlib/node", async (importOriginal) => { + const actual = await importOriginal(); + return { ...actual, path: { ...actual.path } }; +}); + vi.mock("chokidar", { spy: true }); vi.mock("@vrtmrz/livesync-commonlib/compat/serviceFeatures/offlineScanner", { spy: true }); @@ -88,6 +94,36 @@ describe("CLI database preparation", () => { await expect(main(standardIo, runner)).rejects.toThrow(`__EXIT__:${exitCode}`); } + it.each([ + { separator: "/", platform: "POSIX" }, + { separator: "\\", platform: "Windows" }, + ])("excludes internal Vault paths with the $platform host separator", async ({ separator }) => { + const targets = [ + "headless-vault-livesync-v2/LOCK", + ".livesync/settings.json", + "notes/.private/secret.md", + "notes/local.md", + ]; + const observed: boolean[] = []; + const runner = vi.fn(async (_options, { core }) => { + const originalSeparator = path.sep; + try { + Object.defineProperty(path, "sep", { value: separator }); + for (const target of targets) { + observed.push(await core.services.vault.isTargetFile(target as FilePathWithPrefix)); + } + } finally { + Object.defineProperty(path, "sep", { value: originalSeparator }); + } + return false; + }); + + await start("ls", runner); + + expect(runner).toHaveBeenCalledOnce(); + expect(observed).toEqual([false, false, false, true]); + }); + it("loads legacy device values and persists configuration through the service-owned store", async () => { const localStoragePath = join(directory, ".livesync", "runtime", "local-storage.json"); await mkdir(join(directory, ".livesync", "runtime"), { recursive: true }); diff --git a/src/apps/cli/main.ts b/src/apps/cli/main.ts index cea0ec35..9522ba21 100644 --- a/src/apps/cli/main.ts +++ b/src/apps/cli/main.ts @@ -550,7 +550,7 @@ export async function main( // Add target filter to prevent internal files are handled core.services.vault.isTargetFile.addHandler(async (target) => { const targetPath = stripAllPrefixes(getPathFromUXFileInfo(target)); - const parts = targetPath.split(path.sep); + const parts = targetPath.split("/"); // if some part of the path starts with dot, treat it as internal file and ignore. if (parts.some((part) => part.startsWith("."))) { return await Promise.resolve(false); diff --git a/updates.md b/updates.md index e4441dd6..3d01a759 100644 --- a/updates.md +++ b/updates.md @@ -12,6 +12,8 @@ Earlier releases remain available in the 1.0 release history, the 1.0 preview hi ## Unreleased +- The CLI mirror now starts correctly on Windows when the Vault path is omitted and the database directory is also used as the Vault. It checks logical Vault paths using `/` separators, correctly excluding internal database files and nested hidden directories even when the host uses Windows path separators. + ## 1.0.43 30th October, 2026