From d4cab5c56779bb68f91d502d552e19a4d75c7d92 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sun, 25 Oct 2026 13:40:31 -0700 Subject: [PATCH] refactor: group local filesystem guards and search owners (#33624) * refactor: group local filesystem guards and search owners * refactor: use process-host handle contract for listing cleanup * test: preserve concrete WSL module exports in search fixtures * test: remove unnecessary WSL owner mocks from search fixtures --- config/flaky-test-pattern-baseline.txt | 2 +- .../process-host-direct-import-baseline.txt | 1 - config/reliability-gates.jsonc | 8 +-- config/scripts/ci-shard-timings.json | 26 +++---- src/main/ai-vault/session-delete.ts | 2 +- .../ipc/agent-session-attachment-upload.ts | 2 +- .../filesystem-allowed-roots.test.ts | 43 ++++++------ .../filesystem-allowed-roots.ts | 22 +++--- .../{ => filesystem}/filesystem-auth.test.ts | 24 +++---- .../ipc/{ => filesystem}/filesystem-auth.ts | 6 +- .../filesystem-canonical-owner.test.ts | 8 ++- ...system-content-search-cancellation.test.ts | 6 +- .../filesystem-create-path-guards.ts | 0 .../filesystem-download-promotion.ts | 2 +- .../filesystem-git-diff-handlers.ts | 5 +- .../filesystem-git-index-handlers.ts | 2 +- .../filesystem-git-status-handlers.ts | 2 +- .../filesystem/filesystem-git-url-handlers.ts | 2 +- .../filesystem-import-local-rollback.test.ts | 0 .../filesystem-import-local-tree-copy.ts | 0 .../filesystem-import-local.ts | 4 +- .../filesystem-import-source-stat.ts | 6 +- .../filesystem-import.test.ts | 2 +- ...tem-list-files-handler-name-filter.test.ts | 47 ++++++------- .../filesystem-list-files-name-filter.test.ts | 8 +-- .../filesystem-list-files.test.ts | 10 +-- .../{ => filesystem}/filesystem-list-files.ts | 68 +++++++++---------- .../filesystem-listing-process-cleanup.ts | 23 +++++++ .../ipc/filesystem/filesystem-mutations.ts | 6 +- ...tem-path-containment-remote-enoent.test.ts | 0 ...-containment-unicode-normalization.test.ts | 4 +- .../filesystem-path-containment.ts | 2 +- ...tem-quick-open-options.integration.test.ts | 4 +- .../filesystem/filesystem-read-handlers.ts | 4 +- .../filesystem-runtime-upload-staging.test.ts | 4 +- .../filesystem-runtime-upload-staging.ts | 6 +- .../filesystem-search-file-paths.test.ts | 16 ++--- .../filesystem-search-file-paths.ts | 29 ++++---- .../filesystem/filesystem-search-handlers.ts | 2 +- .../filesystem-search-rg-timeout.test.ts | 18 ++--- ...lesystem-source-control-ai-targets.test.ts | 2 +- .../filesystem-source-control-ai-targets.ts | 2 +- ...lesystem-symlink-directory-entries.test.ts | 0 .../filesystem-symlink-directory-entries.ts | 4 +- .../filesystem/filesystem-write-handlers.ts | 2 +- .../filesystem/git-remote/compare-handlers.ts | 2 +- .../ipc/filesystem/local-regular-file-read.ts | 2 +- ...gister-worktree-hook-inspection-handler.ts | 2 +- .../ipc/local-file-access-resolution.test.ts | 2 +- src/main/ipc/local-file-access-resolution.ts | 4 +- .../ipc/registered-worktree-root-match.ts | 14 ++++ ...registered-worktree-root-ownership.test.ts | 5 +- .../registered-worktree-root-refresh.test.ts | 5 +- ...gistered-worktree-root-wsl-runtime.test.ts | 2 +- .../ipc/registered-worktree-roots-cache.ts | 18 ++--- .../ipc/runtime-upload-file-stream.test.ts | 2 +- .../runtime-upload-slice-boundaries.test.ts | 3 +- .../native-chat-visual-file-read.ts | 2 +- .../native-chat/transcript-path-admission.ts | 2 +- src/main/native-chat/transcript-reader.ts | 2 +- .../native-chat/transcript-tail-reader.ts | 2 +- .../providers/local-filesystem-provider.ts | 11 +-- .../runtime/files/chat-scratch-file-root.ts | 7 +- src/main/runtime/files/file-access.ts | 2 +- src/main/runtime/files/file-path-existence.ts | 2 +- src/main/runtime/files/search/file-search.ts | 4 +- src/main/runtime/files/watch/file-watches.ts | 2 +- .../orca-runtime-files-chunk-reads.test.ts | 2 +- ...untime-files-mobile-explorer-reads.test.ts | 2 +- .../orca-runtime-files-mock-registry.ts | 4 +- .../orca-runtime-files-preview-budget.test.ts | 2 +- ...rca-runtime-files-rename-authority.test.ts | 2 +- .../runtime/orca-runtime-files-search.test.ts | 2 +- ...ime-files-terminal-artifact-grants.test.ts | 2 +- ...runtime-files-terminal-artifact-io.test.ts | 2 +- ...s-terminal-artifact-swap-detection.test.ts | 2 +- ...les-terminal-link-host-translation.test.ts | 2 +- ...ime-files-terminal-path-resolution.test.ts | 2 +- ...rca-runtime-files-watch-host-scope.test.ts | 2 +- .../runtime/orca-runtime-files-watch.test.ts | 6 +- ...ca-runtime-quick-open-capabilities.test.ts | 2 +- .../orca-runtime-test-mocks/setup.spec.ts | 8 +-- ...time-file-listing-producer-budgets.test.ts | 2 +- .../runtime-file-upload-decode.test.ts | 2 +- .../runtime-repository-hooks-commands.ts | 2 +- ...time-repository-registration-controller.ts | 2 +- .../runtime-search-line-fragments.test.ts | 2 +- .../runtime/runtime-worktree-filesystem.ts | 2 +- src/main/runtime/terminal-kitty-file-read.ts | 2 +- .../terminal/terminal-path-resolver.ts | 2 +- .../window/clipboard-ipc-handlers.test.ts | 2 +- src/main/window/clipboard-ipc-handlers.ts | 4 +- .../clipboard-runtime-owned-ssh-paste.test.ts | 2 +- src/main/worktree-prunable-git-file.ts | 2 +- .../remote-host-file-open-repro.test.ts | 2 +- 95 files changed, 317 insertions(+), 286 deletions(-) rename src/main/ipc/{ => filesystem}/filesystem-allowed-roots.test.ts (90%) rename src/main/ipc/{ => filesystem}/filesystem-allowed-roots.ts (88%) rename src/main/ipc/{ => filesystem}/filesystem-auth.test.ts (95%) rename src/main/ipc/{ => filesystem}/filesystem-auth.ts (97%) rename src/main/ipc/{ => filesystem}/filesystem-create-path-guards.ts (100%) rename src/main/ipc/{ => filesystem}/filesystem-import-local-rollback.test.ts (100%) rename src/main/ipc/{ => filesystem}/filesystem-import-local-tree-copy.ts (100%) rename src/main/ipc/{ => filesystem}/filesystem-import-local.ts (92%) rename src/main/ipc/{ => filesystem}/filesystem-import-source-stat.ts (86%) rename src/main/ipc/{ => filesystem}/filesystem-import.test.ts (99%) rename src/main/ipc/{ => filesystem}/filesystem-list-files-handler-name-filter.test.ts (72%) rename src/main/ipc/{ => filesystem}/filesystem-list-files-name-filter.test.ts (94%) rename src/main/ipc/{ => filesystem}/filesystem-list-files.test.ts (98%) rename src/main/ipc/{ => filesystem}/filesystem-list-files.ts (86%) create mode 100644 src/main/ipc/filesystem/filesystem-listing-process-cleanup.ts rename src/main/ipc/{ => filesystem}/filesystem-path-containment-remote-enoent.test.ts (100%) rename src/main/ipc/{ => filesystem}/filesystem-path-containment-unicode-normalization.test.ts (98%) rename src/main/ipc/{ => filesystem}/filesystem-path-containment.ts (98%) rename src/main/ipc/{ => filesystem}/filesystem-runtime-upload-staging.test.ts (98%) rename src/main/ipc/{ => filesystem}/filesystem-runtime-upload-staging.ts (98%) rename src/main/ipc/{ => filesystem}/filesystem-search-file-paths.test.ts (96%) rename src/main/ipc/{ => filesystem}/filesystem-search-file-paths.ts (89%) rename src/main/ipc/{ => filesystem}/filesystem-search-rg-timeout.test.ts (96%) rename src/main/ipc/{ => filesystem}/filesystem-symlink-directory-entries.test.ts (100%) rename src/main/ipc/{ => filesystem}/filesystem-symlink-directory-entries.ts (84%) create mode 100644 src/main/ipc/registered-worktree-root-match.ts diff --git a/config/flaky-test-pattern-baseline.txt b/config/flaky-test-pattern-baseline.txt index a85534aa961..365bd12004b 100644 --- a/config/flaky-test-pattern-baseline.txt +++ b/config/flaky-test-pattern-baseline.txt @@ -267,7 +267,7 @@ 1 test-no-real-time-wait:src/main/gitlab/work-item-details.test.ts 1 test-no-real-time-wait:src/main/grok/grok-hook-stdin-no-eof.test.ts 1 test-no-real-time-wait:src/main/ipc/created-worktree-reconciliation.test.ts -1 test-no-real-time-wait:src/main/ipc/filesystem-auth.test.ts +1 test-no-real-time-wait:src/main/ipc/filesystem/filesystem-auth.test.ts 1 test-no-real-time-wait:src/main/ipc/filesystem/filesystem-content-search-cancellation.test.ts 5 test-no-real-time-wait:src/main/ipc/filesystem/local-disk-write-precondition.test.ts 2 test-no-real-time-wait:src/main/ipc/folder-repo-git-upgrade.test.ts diff --git a/config/process-host-direct-import-baseline.txt b/config/process-host-direct-import-baseline.txt index 05d175dfbd0..25af0ba176b 100644 --- a/config/process-host-direct-import-baseline.txt +++ b/config/process-host-direct-import-baseline.txt @@ -54,7 +54,6 @@ src/main/git/wsl-git-read-environment.ts src/main/github/gh-utils.ts src/main/hooks.ts src/main/ipc/developer-permissions.ts -src/main/ipc/filesystem-list-files.ts src/main/ipc/filesystem/filesystem-handler-context.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-cancellation-restart.ts src/main/ipc/filesystem/watcher-process/parcel-watcher-cancellation-tracker.ts diff --git a/config/reliability-gates.jsonc b/config/reliability-gates.jsonc index 29db5fa3f3b..dfe4db1bcde 100644 --- a/config/reliability-gates.jsonc +++ b/config/reliability-gates.jsonc @@ -20053,11 +20053,11 @@ "invariant": "Quick Open queries execute on the worktree host, find exact ignored-path matches beyond the legacy inventory prefix, return deterministic bounded results, and keep the paired runtime usable without overflowing its outbound reply buffer.", "oracle": "Create 88,763 paths with 77,792 ignored entries and a nominal 22 GiB sparse file, query exact late gitignored and .orcaignored targets through headed and headless paired runtimes, and require one deterministic match, a payload below 4 KiB, and a healthy follow-up worktree call.", "commands": [ - "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem-search-file-paths.test.ts src/main/runtime/orca-runtime-files-search.test.ts src/main/runtime/rpc/methods/files-path-search.test.ts src/renderer/src/components/quick-open-file-list.react.test.tsx src/renderer/src/runtime/runtime-file-client-search-listing.test.ts src/shared/quick-open-transport-budget.test.ts --reporter=dot", + "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/filesystem-search-file-paths.test.ts src/main/runtime/orca-runtime-files-search.test.ts src/main/runtime/rpc/methods/files-path-search.test.ts src/renderer/src/components/quick-open-file-list.react.test.tsx src/renderer/src/runtime/runtime-file-client-search-listing.test.ts src/shared/quick-open-transport-budget.test.ts --reporter=dot", "pnpm exec playwright test tests/e2e/paired-quick-open-large-tree.spec.ts --config tests/playwright.config.ts --workers=1" ], "testFiles": [ - "src/main/ipc/filesystem-search-file-paths.test.ts", + "src/main/ipc/filesystem/filesystem-search-file-paths.test.ts", "src/main/runtime/orca-runtime-files-search.test.ts", "src/main/runtime/rpc/methods/files-path-search.test.ts", "src/renderer/src/components/quick-open-file-list.react.test.tsx", @@ -20067,7 +20067,7 @@ ], "assertionRefs": [ { - "file": "src/main/ipc/filesystem-search-file-paths.test.ts", + "file": "src/main/ipc/filesystem/filesystem-search-file-paths.test.ts", "assertions": [ "host search stays bounded and respects gitignore plus .orcaignore traversal" ] @@ -20087,7 +20087,7 @@ "runner": "local", "platform": "macos", "result": "passed", - "command": "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem-search-file-paths.test.ts src/main/runtime/orca-runtime-files-search.test.ts src/main/runtime/rpc/methods/files-path-search.test.ts src/renderer/src/components/quick-open-file-list.react.test.tsx src/renderer/src/runtime/runtime-file-client-search-listing.test.ts src/shared/quick-open-transport-budget.test.ts --reporter=dot", + "command": "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/filesystem/filesystem-search-file-paths.test.ts src/main/runtime/orca-runtime-files-search.test.ts src/main/runtime/rpc/methods/files-path-search.test.ts src/renderer/src/components/quick-open-file-list.react.test.tsx src/renderer/src/runtime/runtime-file-client-search-listing.test.ts src/shared/quick-open-transport-budget.test.ts --reporter=dot", "durationSeconds": 5, "summary": "Six focused files passed 52 tests across host search, runtime RPC, renderer query ownership, and transport bounds; existing React act warnings remained non-fatal." } diff --git a/config/scripts/ci-shard-timings.json b/config/scripts/ci-shard-timings.json index dd7526aed52..4393fe9ff48 100644 --- a/config/scripts/ci-shard-timings.json +++ b/config/scripts/ci-shard-timings.json @@ -2538,8 +2538,8 @@ "src/main/ipc/feedback-image-attachments.test.ts": 52, "src/main/ipc/feedback-request-ownership.test.ts": 56, "src/main/ipc/feedback.test.ts": 142, - "src/main/ipc/filesystem-allowed-roots.test.ts": 730, - "src/main/ipc/filesystem-auth.test.ts": 599, + "src/main/ipc/filesystem/filesystem-allowed-roots.test.ts": 730, + "src/main/ipc/filesystem/filesystem-auth.test.ts": 599, "src/main/ipc/filesystem/filesystem-branch-compare-diff.test.ts": 1385, "src/main/ipc/filesystem/filesystem-canonical-owner.test.ts": 340, "src/main/ipc/filesystem/filesystem-commit-message-generation.test.ts": 1421, @@ -2551,22 +2551,22 @@ "src/main/ipc/filesystem/filesystem-download-transfers.test.ts": 1450, "src/main/ipc/filesystem/filesystem-git-commit-dispatch.test.ts": 1454, "src/main/ipc/filesystem/filesystem-git-status-staging.test.ts": 1645, - "src/main/ipc/filesystem-import-local-rollback.test.ts": 86, - "src/main/ipc/filesystem-import.test.ts": 1213, - "src/main/ipc/filesystem-list-files-handler-name-filter.test.ts": 1554, - "src/main/ipc/filesystem-list-files-name-filter.test.ts": 564, - "src/main/ipc/filesystem-list-files.test.ts": 742, + "src/main/ipc/filesystem/filesystem-import-local-rollback.test.ts": 86, + "src/main/ipc/filesystem/filesystem-import.test.ts": 1213, + "src/main/ipc/filesystem/filesystem-list-files-handler-name-filter.test.ts": 1554, + "src/main/ipc/filesystem/filesystem-list-files-name-filter.test.ts": 564, + "src/main/ipc/filesystem/filesystem-list-files.test.ts": 742, "src/main/ipc/filesystem/filesystem-markdown-document-listing.test.ts": 1217, "src/main/ipc/filesystem/filesystem-mutations-runtime-upload.test.ts": 605, "src/main/ipc/filesystem/filesystem-mutations.test.ts": 1536, - "src/main/ipc/filesystem-path-containment-remote-enoent.test.ts": 40, - "src/main/ipc/filesystem-path-containment-unicode-normalization.test.ts": 616, + "src/main/ipc/filesystem/filesystem-path-containment-remote-enoent.test.ts": 40, + "src/main/ipc/filesystem/filesystem-path-containment-unicode-normalization.test.ts": 616, "src/main/ipc/filesystem/filesystem-pull-request-linked-issue-lifetime.test.ts": 2341, "src/main/ipc/filesystem/filesystem-quick-open-options.integration.test.ts": 1280, - "src/main/ipc/filesystem-runtime-upload-staging.test.ts": 62, - "src/main/ipc/filesystem-search-file-paths.test.ts": 361, - "src/main/ipc/filesystem-search-rg-timeout.test.ts": 1957, - "src/main/ipc/filesystem-symlink-directory-entries.test.ts": 33, + "src/main/ipc/filesystem/filesystem-runtime-upload-staging.test.ts": 62, + "src/main/ipc/filesystem/filesystem-search-file-paths.test.ts": 361, + "src/main/ipc/filesystem/filesystem-search-rg-timeout.test.ts": 1957, + "src/main/ipc/filesystem/filesystem-symlink-directory-entries.test.ts": 33, "src/main/ipc/filesystem-watcher-canonical-root-paths.test.ts": 456, "src/main/ipc/filesystem/watchers/filesystem-watcher-document-lifetime.test.ts": 388, "src/main/ipc/filesystem/watchers/filesystem-watcher-event-batch.test.ts": 34, diff --git a/src/main/ai-vault/session-delete.ts b/src/main/ai-vault/session-delete.ts index 5c70ef82454..ec8263f580b 100644 --- a/src/main/ai-vault/session-delete.ts +++ b/src/main/ai-vault/session-delete.ts @@ -10,7 +10,7 @@ import { type ValidateAiVaultSessionDeleteTargetArgs } from './session-delete-target' import { tryDeleteWslUncPath, WslDeleteValidationError } from '../wsl-unc-delete' -import { isENOENT } from '../ipc/filesystem-path-containment' +import { isENOENT } from '../ipc/filesystem/filesystem-path-containment' // Remove companions first so partial failure leaves the transcript visible for retry. export async function deleteAiVaultSessionFile( diff --git a/src/main/ipc/agent-session-attachment-upload.ts b/src/main/ipc/agent-session-attachment-upload.ts index 54451acd6c8..5061595cdde 100644 --- a/src/main/ipc/agent-session-attachment-upload.ts +++ b/src/main/ipc/agent-session-attachment-upload.ts @@ -19,7 +19,7 @@ import { isRuntimeEnvironmentManuallyDisconnected, RUNTIME_MANUALLY_DISCONNECTED_MESSAGE } from './runtime-environments/runtime-environment-manual-disconnect' -import { stageOneSourceForRuntimeUpload } from './filesystem-runtime-upload-staging' +import { stageOneSourceForRuntimeUpload } from './filesystem/filesystem-runtime-upload-staging' import { streamExternalFileSlices } from './runtime-upload-file-stream' import { formatByteCeiling } from './runtime-import-limits' diff --git a/src/main/ipc/filesystem-allowed-roots.test.ts b/src/main/ipc/filesystem/filesystem-allowed-roots.test.ts similarity index 90% rename from src/main/ipc/filesystem-allowed-roots.test.ts rename to src/main/ipc/filesystem/filesystem-allowed-roots.test.ts index 9ae8aeee3d3..b19af1aae49 100644 --- a/src/main/ipc/filesystem-allowed-roots.test.ts +++ b/src/main/ipc/filesystem/filesystem-allowed-roots.test.ts @@ -2,30 +2,33 @@ import { mkdir, mkdtemp, realpath, rm, symlink, writeFile } from 'node:fs/promis import { tmpdir } from 'node:os' import { join, resolve } from 'node:path' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { Store } from '../persistence' -import type * as RepoWorktrees from '../repo-worktrees' -import { listRepoWorktreeGraph } from '../repo-worktrees' -import type * as ProjectGroupsModule from '../../shared/project-groups' -import { buildProjectGroupChildIndex, getProjectGroupSubtreeIds } from '../../shared/project-groups' -import { isPathInsideOrEqual } from '../../shared/cross-platform-path' -import type * as CrossPlatformPathModule from '../../shared/cross-platform-path' -import { getWorktreeMirrorDistro } from '../project-runtime-git-options' -import type { FolderWorkspace } from '../../shared/folder-workspace-types' -import type { ProjectGroup } from '../../shared/project-group-types' -import type { Project } from '../../shared/project-types' -import type { Repo } from '../../shared/repo-types' +import type { Store } from '../../persistence/loading-store/store' +import type * as RepoWorktrees from '../../repo-worktrees' +import { listRepoWorktreeGraph } from '../../repo-worktrees' +import type * as ProjectGroupsModule from '../../../shared/project-groups' +import { + buildProjectGroupChildIndex, + getProjectGroupSubtreeIds +} from '../../../shared/project-groups' +import { isPathInsideOrEqual } from '../../../shared/cross-platform-path' +import type * as CrossPlatformPathModule from '../../../shared/cross-platform-path' +import { getWorktreeMirrorDistro } from '../../project-runtime-git-options' +import type { FolderWorkspace } from '../../../shared/folder-workspace-types' +import type { ProjectGroup } from '../../../shared/project-group-types' +import type { Project } from '../../../shared/project-types' +import type { Repo } from '../../../shared/repo-types' import { getAllowedRoots } from './filesystem-allowed-roots' import { resolveAuthorizedPath } from './filesystem-auth' -import { invalidateAuthorizedRootsCache } from './registered-worktree-roots-cache' -import { computeWorkspaceRoot, getWorktreePathSettings } from './worktree-logic' +import { invalidateAuthorizedRootsCache } from '../registered-worktree-roots-cache' +import { computeWorkspaceRoot, getWorktreePathSettings } from '../worktree-logic' -vi.mock('../repo-worktrees', async () => { - const actual = await vi.importActual('../repo-worktrees') +vi.mock('../../repo-worktrees', async () => { + const actual = await vi.importActual('../../repo-worktrees') return { ...actual, listRepoWorktreeGraph: vi.fn(async () => []) } }) -vi.mock('../../shared/project-groups', async () => { - const actual = await vi.importActual('../../shared/project-groups') +vi.mock('../../../shared/project-groups', async () => { + const actual = await vi.importActual('../../../shared/project-groups') return { ...actual, buildProjectGroupChildIndex: vi.fn(actual.buildProjectGroupChildIndex), @@ -33,9 +36,9 @@ vi.mock('../../shared/project-groups', async () => { } }) -vi.mock('../../shared/cross-platform-path', async () => { +vi.mock('../../../shared/cross-platform-path', async () => { const actual = await vi.importActual( - '../../shared/cross-platform-path' + '../../../shared/cross-platform-path' ) return { ...actual, isPathInsideOrEqual: vi.fn(actual.isPathInsideOrEqual) } }) diff --git a/src/main/ipc/filesystem-allowed-roots.ts b/src/main/ipc/filesystem/filesystem-allowed-roots.ts similarity index 88% rename from src/main/ipc/filesystem-allowed-roots.ts rename to src/main/ipc/filesystem/filesystem-allowed-roots.ts index ddbd6f18d58..d5df5db5dbd 100644 --- a/src/main/ipc/filesystem-allowed-roots.ts +++ b/src/main/ipc/filesystem/filesystem-allowed-roots.ts @@ -1,20 +1,18 @@ import { isAbsolute, resolve } from 'node:path' -import type { RuntimeStore } from '../runtime/runtime-store-contract' -import { computeWorkspaceRootWithin, getWorktreePathSettings } from './worktree-logic' -import { - getWorktreeMirrorDistroForRuntime, - resolveLocalProjectRuntimesForRepos -} from '../project-runtime-git-options' -import { isPathInsideOrEqual } from '../../shared/cross-platform-path' +import type { RuntimeStore } from '../../runtime/runtime-store-contract' +import { computeWorkspaceRootWithin, getWorktreePathSettings } from '../worktree-logic' +import { getWorktreeMirrorDistroForRuntime } from '../../project-runtime-git-options' +import { resolveLocalProjectRuntimesForRepos } from '../../local-project-runtime-resolution' +import { isPathInsideOrEqual } from '../../../shared/cross-platform-path' import { buildProjectGroupChildIndex, collectProjectGroupSubtreeIds, type ProjectGroupChildIndex -} from '../../shared/project-groups' -import type { FolderWorkspace } from '../../shared/folder-workspace-types' -import type { ProjectGroup } from '../../shared/project-group-types' -import type { Repo } from '../../shared/repo-types' -import { hasRemoteFilesystemOwner } from './remote-filesystem-owner' +} from '../../../shared/project-groups' +import type { FolderWorkspace } from '../../../shared/folder-workspace-types' +import type { ProjectGroup } from '../../../shared/project-group-types' +import type { Repo } from '../../../shared/repo-types' +import { hasRemoteFilesystemOwner } from '../remote-filesystem-owner' type FolderScopeStore = Pick & Partial> diff --git a/src/main/ipc/filesystem-auth.test.ts b/src/main/ipc/filesystem/filesystem-auth.test.ts similarity index 95% rename from src/main/ipc/filesystem-auth.test.ts rename to src/main/ipc/filesystem/filesystem-auth.test.ts index e6728a05828..29a06f7127c 100644 --- a/src/main/ipc/filesystem-auth.test.ts +++ b/src/main/ipc/filesystem/filesystem-auth.test.ts @@ -3,13 +3,13 @@ import { mkdir, mkdtemp, realpath, rm, symlink } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join, resolve } from 'node:path' import { beforeEach, describe, expect, it, vi } from 'vitest' -import type { Store } from '../persistence' -import type * as RepoWorktrees from '../repo-worktrees' -import { listRepoWorktreeGraph } from '../repo-worktrees' -import type { FolderWorkspace } from '../../shared/folder-workspace-types' -import type { ProjectGroup } from '../../shared/project-group-types' -import type { Repo } from '../../shared/repo-types' -import type { GitWorktreeInfo } from '../../shared/worktree/types' +import type { Store } from '../../persistence/loading-store/store' +import type * as RepoWorktrees from '../../repo-worktrees' +import { listRepoWorktreeGraph } from '../../repo-worktrees' +import type { FolderWorkspace } from '../../../shared/folder-workspace-types' +import type { ProjectGroup } from '../../../shared/project-group-types' +import type { Repo } from '../../../shared/repo-types' +import type { GitWorktreeInfo } from '../../../shared/worktree/types' import { resolveAuthorizedPath } from './filesystem-auth' import { isDescendantOrEqual, validateGitRelativeFilePath } from './filesystem-path-containment' import { @@ -18,10 +18,10 @@ import { rebuildAuthorizedRootsCache, registerCreatedWorktreeRoot, resolveRegisteredWorktreePath -} from './registered-worktree-roots-cache' +} from '../registered-worktree-roots-cache' -vi.mock('../repo-worktrees', async () => { - const actual = await vi.importActual('../repo-worktrees') +vi.mock('../../repo-worktrees', async () => { + const actual = await vi.importActual('../../repo-worktrees') return { ...actual, listRepoWorktreeGraph: vi.fn() @@ -390,7 +390,7 @@ describe('filesystem-auth path containment', () => { it('accepts Windows descendants when drive and root casing differ', async () => { vi.resetModules() - vi.doMock('../repo-worktrees', () => ({ + vi.doMock('../../repo-worktrees', () => ({ isRepoRoot: vi.fn(), listRepoWorktreeGraph: vi.fn() })) @@ -414,7 +414,7 @@ describe('filesystem-auth path containment', () => { ).toBe(false) } finally { vi.doUnmock('path') - vi.doUnmock('../repo-worktrees') + vi.doUnmock('../../repo-worktrees') vi.resetModules() } }) diff --git a/src/main/ipc/filesystem-auth.ts b/src/main/ipc/filesystem/filesystem-auth.ts similarity index 97% rename from src/main/ipc/filesystem-auth.ts rename to src/main/ipc/filesystem/filesystem-auth.ts index 67d72f95aef..35fb9711f6b 100644 --- a/src/main/ipc/filesystem-auth.ts +++ b/src/main/ipc/filesystem/filesystem-auth.ts @@ -1,14 +1,14 @@ import { resolve, dirname, basename } from 'node:path' -import type { RuntimeStore } from '../runtime/runtime-store-contract' +import type { RuntimeStore } from '../../runtime/runtime-store-contract' import { lstat, realpath } from 'node:fs/promises' -import { PATH_OUTSIDE_ALLOWED_DIRECTORIES } from '../../shared/local-file-access' +import { PATH_OUTSIDE_ALLOWED_DIRECTORIES } from '../../../shared/local-file-access' import { getAllowedRoots } from './filesystem-allowed-roots' import { isDescendantOrEqual, isENOENT, normalizeExistingPath } from './filesystem-path-containment' import { ensureAuthorizedRootsCache, isPathAllowedByCanonicalRegisteredRoot, isRegisteredWorktreePath -} from './registered-worktree-roots-cache' +} from '../registered-worktree-roots-cache' export const PATH_ACCESS_DENIED_MESSAGE = `${PATH_OUTSIDE_ALLOWED_DIRECTORIES}. If this blocks a legitimate workflow, please file a GitHub issue.` /** Lazily share allowed roots across every check in one authorization. */ diff --git a/src/main/ipc/filesystem/filesystem-canonical-owner.test.ts b/src/main/ipc/filesystem/filesystem-canonical-owner.test.ts index 54e597cae5b..a7f314e6fbc 100644 --- a/src/main/ipc/filesystem/filesystem-canonical-owner.test.ts +++ b/src/main/ipc/filesystem/filesystem-canonical-owner.test.ts @@ -9,8 +9,8 @@ import { getAllowedRoots, getLocalRepos, resolveUnanchoredWorkspaceRoot -} from '../filesystem-allowed-roots' -import { isPathAllowed } from '../filesystem-auth' +} from './filesystem-allowed-roots' +import { isPathAllowed } from './filesystem-auth' import { __resetCreatedWorktreeRootsForTests, invalidateAuthorizedRootsCache, @@ -35,7 +35,9 @@ vi.mock('../worktree-logic', () => ({ getWorktreePathSettings: mocks.pathSettings })) vi.mock('../../project-runtime-git-options', () => ({ - getWorktreeMirrorDistroForRuntime: vi.fn(), + getWorktreeMirrorDistroForRuntime: vi.fn() +})) +vi.mock('../../local-project-runtime-resolution', () => ({ resolveLocalProjectRuntimesForRepos: mocks.projectRuntimes })) diff --git a/src/main/ipc/filesystem/filesystem-content-search-cancellation.test.ts b/src/main/ipc/filesystem/filesystem-content-search-cancellation.test.ts index 6499140ddfd..06948897765 100644 --- a/src/main/ipc/filesystem/filesystem-content-search-cancellation.test.ts +++ b/src/main/ipc/filesystem/filesystem-content-search-cancellation.test.ts @@ -43,7 +43,7 @@ vi.mock('../../wsl', () => ({ toWindowsWslPath: toWindowsWslPathMock })) -vi.mock('../filesystem-auth', () => ({ +vi.mock('./filesystem-auth', () => ({ authorizeExternalPath: vi.fn(async (value: string) => value), resolveAuthorizedPath: resolveAuthorizedPathMock })) @@ -53,7 +53,7 @@ vi.mock('../local-file-access-resolution', () => ({ resolveLocalFileRequestPath: resolveAuthorizedPathMock })) -vi.mock('../filesystem-path-containment', () => ({ +vi.mock('./filesystem-path-containment', () => ({ isENOENT: vi.fn(() => false), validateGitRelativeFilePath: vi.fn((value: string) => value) })) @@ -62,7 +62,7 @@ vi.mock('../registered-worktree-roots-cache', () => ({ resolveRegisteredWorktreePath: vi.fn(async (value: string) => value) })) -vi.mock('../filesystem-list-files', () => ({ +vi.mock('./filesystem-list-files', () => ({ listQuickOpenFiles: vi.fn() })) diff --git a/src/main/ipc/filesystem-create-path-guards.ts b/src/main/ipc/filesystem/filesystem-create-path-guards.ts similarity index 100% rename from src/main/ipc/filesystem-create-path-guards.ts rename to src/main/ipc/filesystem/filesystem-create-path-guards.ts diff --git a/src/main/ipc/filesystem/filesystem-download-promotion.ts b/src/main/ipc/filesystem/filesystem-download-promotion.ts index 6995ad3bb6e..2b0b74d5bac 100644 --- a/src/main/ipc/filesystem/filesystem-download-promotion.ts +++ b/src/main/ipc/filesystem/filesystem-download-promotion.ts @@ -1,7 +1,7 @@ import { randomUUID } from 'node:crypto' import { rename, rm, stat } from 'node:fs/promises' import { dirname, join } from 'node:path' -import { isENOENT } from '../filesystem-path-containment' +import { isENOENT } from './filesystem-path-containment' export function decodeDownloadedFileContent(content: string, encoding: 'utf8' | 'base64'): Buffer { return encoding === 'base64' ? Buffer.from(content, 'base64') : Buffer.from(content, 'utf8') diff --git a/src/main/ipc/filesystem/filesystem-git-diff-handlers.ts b/src/main/ipc/filesystem/filesystem-git-diff-handlers.ts index 38145fe8eca..5c4464ccf7b 100644 --- a/src/main/ipc/filesystem/filesystem-git-diff-handlers.ts +++ b/src/main/ipc/filesystem/filesystem-git-diff-handlers.ts @@ -3,10 +3,7 @@ import type { GitDiffResult } from '../../../shared/git-diff-compare-types' import { getBranchDiff, getCommitDiff } from '../../git/status' import { resolveRegisteredWorktreePath } from '../registered-worktree-roots-cache' import { getLocalGitOptionsForRegisteredWorktree } from '../local-worktree-runtime-options' -import { - validateFullGitObjectId, - validateGitRelativeFilePath -} from '../filesystem-path-containment' +import { validateFullGitObjectId, validateGitRelativeFilePath } from './filesystem-path-containment' import type { FilesystemHandlerContext } from './filesystem-handler-context' import { requireReachableGitRoute } from '../../providers/execution-host-provider-dispatch' import { getConnectionExecutionHostId } from '../../../shared/execution-host' diff --git a/src/main/ipc/filesystem/filesystem-git-index-handlers.ts b/src/main/ipc/filesystem/filesystem-git-index-handlers.ts index f2eb20f89ea..5a1a66028b7 100644 --- a/src/main/ipc/filesystem/filesystem-git-index-handlers.ts +++ b/src/main/ipc/filesystem/filesystem-git-index-handlers.ts @@ -14,7 +14,7 @@ import { getLocalRepoForRegisteredWorktree } from '../local-worktree-runtime-options' import { getWorktreeSharedLinkPaths } from '../../git/worktree-shared-directories' -import { validateGitRelativeFilePath } from '../filesystem-path-containment' +import { validateGitRelativeFilePath } from './filesystem-path-containment' import type { FilesystemHandlerContext } from './filesystem-handler-context' import { parseGitStageWorktreeScope } from '../../../shared/git-stage-worktree-scope' import { requireReachableGitRoute } from '../../providers/execution-host-provider-dispatch' diff --git a/src/main/ipc/filesystem/filesystem-git-status-handlers.ts b/src/main/ipc/filesystem/filesystem-git-status-handlers.ts index ec4c0508de9..1e768ab3d0e 100644 --- a/src/main/ipc/filesystem/filesystem-git-status-handlers.ts +++ b/src/main/ipc/filesystem/filesystem-git-status-handlers.ts @@ -25,7 +25,7 @@ import { findKnownHugeFolderPathsToIgnore } from '../../git/huge-folder-ignore' import { resolveRegisteredWorktreePath } from '../registered-worktree-roots-cache' -import { validateGitRelativeFilePath } from '../filesystem-path-containment' +import { validateGitRelativeFilePath } from './filesystem-path-containment' import { getLocalGitOptionsForRegisteredWorktree, getLocalGitOptionsForRepo, diff --git a/src/main/ipc/filesystem/filesystem-git-url-handlers.ts b/src/main/ipc/filesystem/filesystem-git-url-handlers.ts index 1b658d7ab6c..61a0486ec61 100644 --- a/src/main/ipc/filesystem/filesystem-git-url-handlers.ts +++ b/src/main/ipc/filesystem/filesystem-git-url-handlers.ts @@ -2,7 +2,7 @@ import { ipcMain } from 'electron' import { awaitWindowsHostGitEnvironmentReady } from '../../git/runner' import { getRemoteCommitUrl, getRemoteFileUrl } from '../../git/repo' import { resolveRegisteredWorktreePath } from '../registered-worktree-roots-cache' -import { validateFullGitObjectId } from '../filesystem-path-containment' +import { validateFullGitObjectId } from './filesystem-path-containment' import type { FilesystemHandlerContext } from './filesystem-handler-context' import { requireReachableGitRoute } from '../../providers/execution-host-provider-dispatch' import { getConnectionExecutionHostId } from '../../../shared/execution-host' diff --git a/src/main/ipc/filesystem-import-local-rollback.test.ts b/src/main/ipc/filesystem/filesystem-import-local-rollback.test.ts similarity index 100% rename from src/main/ipc/filesystem-import-local-rollback.test.ts rename to src/main/ipc/filesystem/filesystem-import-local-rollback.test.ts diff --git a/src/main/ipc/filesystem-import-local-tree-copy.ts b/src/main/ipc/filesystem/filesystem-import-local-tree-copy.ts similarity index 100% rename from src/main/ipc/filesystem-import-local-tree-copy.ts rename to src/main/ipc/filesystem/filesystem-import-local-tree-copy.ts diff --git a/src/main/ipc/filesystem-import-local.ts b/src/main/ipc/filesystem/filesystem-import-local.ts similarity index 92% rename from src/main/ipc/filesystem-import-local.ts rename to src/main/ipc/filesystem/filesystem-import-local.ts index 08605eb1ee0..afa6978f63e 100644 --- a/src/main/ipc/filesystem-import-local.ts +++ b/src/main/ipc/filesystem/filesystem-import-local.ts @@ -2,8 +2,8 @@ import { lstat } from 'node:fs/promises' import { basename, join, resolve } from 'node:path' import { isENOENT } from './filesystem-path-containment' import { classifyImportSource } from './filesystem-import-source-stat' -import { deconflictCopyName } from '../../shared/import-copy-name' -import type { ImportItemResult } from '../../shared/filesystem-import-result-types' +import { deconflictCopyName } from '../../../shared/import-copy-name' +import type { ImportItemResult } from '../../../shared/filesystem-import-result-types' import { copyLocalFileNoFollow, preScanForSymlinks, diff --git a/src/main/ipc/filesystem-import-source-stat.ts b/src/main/ipc/filesystem/filesystem-import-source-stat.ts similarity index 86% rename from src/main/ipc/filesystem-import-source-stat.ts rename to src/main/ipc/filesystem/filesystem-import-source-stat.ts index 4a0707bbaea..2d079c61b8e 100644 --- a/src/main/ipc/filesystem-import-source-stat.ts +++ b/src/main/ipc/filesystem/filesystem-import-source-stat.ts @@ -1,8 +1,8 @@ import type { Stats } from 'node:fs' import { lstat } from 'node:fs/promises' -import type { ImportSkipReason } from '../../shared/filesystem-import-result-types' -import { errorMessage } from '../../shared/error-message' -import { isPermissionError } from '../win32-utils' +import type { ImportSkipReason } from '../../../shared/filesystem-import-result-types' +import { errorMessage } from '../../../shared/error-message' +import { isPermissionError } from '../../win32-utils' import { isENOENT } from './filesystem-path-containment' export type ImportSourceRejection = diff --git a/src/main/ipc/filesystem-import.test.ts b/src/main/ipc/filesystem/filesystem-import.test.ts similarity index 99% rename from src/main/ipc/filesystem-import.test.ts rename to src/main/ipc/filesystem/filesystem-import.test.ts index 79da8e38e43..b54a9466369 100644 --- a/src/main/ipc/filesystem-import.test.ts +++ b/src/main/ipc/filesystem/filesystem-import.test.ts @@ -47,7 +47,7 @@ vi.mock('fs/promises', () => ({ unlink: unlinkMock })) -import { registerFilesystemMutationHandlers } from './filesystem/filesystem-mutations' +import { registerFilesystemMutationHandlers } from './filesystem-mutations' const REPO_PATH = path.resolve('/workspace/repo') const WORKSPACE_DIR = path.resolve('/workspace') diff --git a/src/main/ipc/filesystem-list-files-handler-name-filter.test.ts b/src/main/ipc/filesystem/filesystem-list-files-handler-name-filter.test.ts similarity index 72% rename from src/main/ipc/filesystem-list-files-handler-name-filter.test.ts rename to src/main/ipc/filesystem/filesystem-list-files-handler-name-filter.test.ts index eaa416a1dca..d3a1fff7e3e 100644 --- a/src/main/ipc/filesystem-list-files-handler-name-filter.test.ts +++ b/src/main/ipc/filesystem/filesystem-list-files-handler-name-filter.test.ts @@ -1,53 +1,50 @@ import { EventEmitter } from 'node:events' import { beforeEach, describe, expect, it, vi } from 'vitest' -import { handlers, store, resetFilesystemIpcMocks } from './filesystem/filesystem-test-harness' +import { handlers, store, resetFilesystemIpcMocks } from './filesystem-test-harness' const { listQuickOpenFilesMock } = vi.hoisted(() => ({ listQuickOpenFilesMock: vi.fn() })) -vi.mock('electron', async () => (await import('./filesystem/filesystem-test-harness')).electronMock) +vi.mock('electron', async () => (await import('./filesystem-test-harness')).electronMock) +vi.mock('fs/promises', async () => (await import('./filesystem-test-harness')).fsPromisesMock) vi.mock( - 'fs/promises', - async () => (await import('./filesystem/filesystem-test-harness')).fsPromisesMock + '../../wsl-unc-delete', + async () => (await import('./filesystem-test-harness')).wslUncDeleteMock ) vi.mock( - '../wsl-unc-delete', - async () => (await import('./filesystem/filesystem-test-harness')).wslUncDeleteMock + '../../crash-reporting/crash-breadcrumb-store', + async () => (await import('./filesystem-test-harness')).crashBreadcrumbMock ) vi.mock( - '../crash-reporting/crash-breadcrumb-store', - async () => (await import('./filesystem/filesystem-test-harness')).crashBreadcrumbMock + '../../git/status', + async () => (await import('./filesystem-test-harness')).gitStatusModuleMock ) vi.mock( - '../git/status', - async () => (await import('./filesystem/filesystem-test-harness')).gitStatusModuleMock + '../../git/check-ignored-paths', + async () => (await import('./filesystem-test-harness')).gitIgnoredPathsMock ) vi.mock( - '../git/check-ignored-paths', - async () => (await import('./filesystem/filesystem-test-harness')).gitIgnoredPathsMock + '../../git/worktree', + async () => (await import('./filesystem-test-harness')).gitWorktreeMock ) vi.mock( - '../git/worktree', - async () => (await import('./filesystem/filesystem-test-harness')).gitWorktreeMock + '../../text-generation/commit-message-text-generation', + async () => (await import('./filesystem-test-harness')).textGenerationModuleMock ) vi.mock( - '../text-generation/commit-message-text-generation', - async () => (await import('./filesystem/filesystem-test-harness')).textGenerationModuleMock + '../../text-generation/pull-request-context', + async () => (await import('./filesystem-test-harness')).pullRequestContextMock ) vi.mock( - '../text-generation/pull-request-context', - async () => (await import('./filesystem/filesystem-test-harness')).pullRequestContextMock + '../../source-control/pull-request-template', + async () => (await import('./filesystem-test-harness')).pullRequestTemplateMock ) vi.mock( - '../source-control/pull-request-template', - async () => (await import('./filesystem/filesystem-test-harness')).pullRequestTemplateMock -) -vi.mock( - '../source-control/pull-request-linked-issue', - async () => (await import('./filesystem/filesystem-test-harness')).pullRequestLinkedIssueMock + '../../source-control/pull-request-linked-issue', + async () => (await import('./filesystem-test-harness')).pullRequestLinkedIssueMock ) vi.mock('./filesystem-list-files', () => ({ listQuickOpenFiles: listQuickOpenFilesMock })) -import { registerFilesystemHandlers } from './filesystem/filesystem' +import { registerFilesystemHandlers } from './filesystem' function registerHandlers(): void { // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: handlers under test only read repos and settings from the harness store. diff --git a/src/main/ipc/filesystem-list-files-name-filter.test.ts b/src/main/ipc/filesystem/filesystem-list-files-name-filter.test.ts similarity index 94% rename from src/main/ipc/filesystem-list-files-name-filter.test.ts rename to src/main/ipc/filesystem/filesystem-list-files-name-filter.test.ts index e22043a4d69..f728e1b8d3e 100644 --- a/src/main/ipc/filesystem-list-files-name-filter.test.ts +++ b/src/main/ipc/filesystem/filesystem-list-files-name-filter.test.ts @@ -1,17 +1,17 @@ import { EventEmitter } from 'node:events' import { afterEach, describe, expect, it, vi } from 'vitest' -import type { Store } from '../persistence' -import type * as GitRunner from '../git/runner' +import type { Store } from '../../persistence/loading-store/store' +import type * as GitRunner from '../../git/runner' import { pathMatchesFileNameFilterTokens, splitFileNameFilterTokens -} from '../../shared/file-name-filter-tokens' +} from '../../../shared/file-name-filter-tokens' const { wslAwareSpawnMock } = vi.hoisted(() => ({ wslAwareSpawnMock: vi.fn() })) -vi.mock('../git/runner', async (importOriginal) => ({ +vi.mock('../../git/runner', async (importOriginal) => ({ ...(await importOriginal()), wslAwareSpawn: wslAwareSpawnMock })) diff --git a/src/main/ipc/filesystem-list-files.test.ts b/src/main/ipc/filesystem/filesystem-list-files.test.ts similarity index 98% rename from src/main/ipc/filesystem-list-files.test.ts rename to src/main/ipc/filesystem/filesystem-list-files.test.ts index 90c0eeae43c..7d98dbe1d43 100644 --- a/src/main/ipc/filesystem-list-files.test.ts +++ b/src/main/ipc/filesystem/filesystem-list-files.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it, vi, beforeEach } from 'vitest' -import type * as BundledRipgrepPath from '../ripgrep/bundled-ripgrep-path' +import type * as BundledRipgrepPath from '../../ripgrep/bundled-ripgrep-path' const { spawnMock, @@ -24,20 +24,20 @@ vi.mock('./filesystem-auth', () => ({ resolveAuthorizedPath: resolveAuthorizedPathMock })) -vi.mock('../ripgrep/bundled-ripgrep-path', async (importOriginal) => ({ +vi.mock('../../ripgrep/bundled-ripgrep-path', async (importOriginal) => ({ ...(await importOriginal()), bundledRipgrepCommand: bundledRipgrepCommandMock })) -vi.mock('./local-worktree-runtime-options', () => ({ +vi.mock('../local-worktree-runtime-options', () => ({ getLocalGitOptionsForRegisteredWorktree: getLocalGitOptionsForRegisteredWorktreeMock })) import { listQuickOpenFiles } from './filesystem-list-files' import { EventEmitter } from 'node:events' -import type { Store } from '../persistence' +import type { Store } from '../../persistence/loading-store/store' import type { ChildProcess } from 'node:child_process' -import { FileListingCancelledError } from '../../shared/file-listing-cancellation' +import { FileListingCancelledError } from '../../../shared/file-listing-cancellation' const BUNDLED_RG = '/bundled/rg' const BUNDLED_ERROR = "Orca's bundled search tool (ripgrep) could not start" diff --git a/src/main/ipc/filesystem-list-files.ts b/src/main/ipc/filesystem/filesystem-list-files.ts similarity index 86% rename from src/main/ipc/filesystem-list-files.ts rename to src/main/ipc/filesystem/filesystem-list-files.ts index 26bbc8d1a7d..197a7ba5698 100644 --- a/src/main/ipc/filesystem-list-files.ts +++ b/src/main/ipc/filesystem/filesystem-list-files.ts @@ -1,25 +1,30 @@ -import { FileInventoryBudget, FileInventoryCapacityError } from '../../shared/file-inventory-budget' -import { quickOpenListingPathFilter } from '../../shared/quick-open-listing-path-filter' -import { getQuickOpenRgOutputMode } from '../../shared/quick-open-ripgrep-output-mode' -import { RipgrepFilenameDecoder, RipgrepFilenameError } from '../../shared/ripgrep-filename-decoder' +import { + FileInventoryBudget, + FileInventoryCapacityError +} from '../../../shared/file-inventory-budget' +import { quickOpenListingPathFilter } from '../../../shared/quick-open-listing-path-filter' +import { getQuickOpenRgOutputMode } from '../../../shared/quick-open-ripgrep-output-mode' +import { + RipgrepFilenameDecoder, + RipgrepFilenameError +} from '../../../shared/ripgrep-filename-decoder' import { sep } from 'node:path' -import type { ChildProcess } from 'node:child_process' -import type { RuntimeStore as Store } from '../runtime/runtime-store-contract' +import type { RuntimeStore as Store } from '../../runtime/runtime-store-contract' import { resolveAuthorizedPath } from './filesystem-auth' -import { toWindowsWslPath } from '../wsl' -import { resolveBundledRipgrepWslRouting } from '../ripgrep/bundled-ripgrep-wsl-routing' -import { getLocalGitOptionsForRegisteredWorktree } from './local-worktree-runtime-options' +import { toWindowsWslPath } from '../../../shared/wsl-paths' +import { resolveBundledRipgrepWslRouting } from '../../ripgrep/bundled-ripgrep-wsl-routing' +import { getLocalGitOptionsForRegisteredWorktree } from '../local-worktree-runtime-options' import { buildExcludePathPrefixes, buildRgArgsForQuickOpen, normalizeQuickOpenRgLine -} from '../../shared/quick-open-filter' +} from '../../../shared/quick-open-filter' import { limitQuickOpenFilesBySerializedBytes, serializedQuickOpenPathBytes -} from '../../shared/quick-open-transport-budget' -import { bundledRipgrepUnavailableError } from '../ripgrep/bundled-ripgrep-path' -import { spawnBundledRipgrep } from '../ripgrep/bundled-ripgrep-spawn' +} from '../../../shared/quick-open-transport-budget' +import { bundledRipgrepUnavailableError } from '../../ripgrep/bundled-ripgrep-path' +import { spawnBundledRipgrep } from '../../ripgrep/bundled-ripgrep-spawn' import { absorbPendingRipgrepSpawnError, isRipgrepUnavailableExit, @@ -30,8 +35,12 @@ import { killSpawnedRipgrepProcess, ripgrepMissingCwdError, RipgrepUnavailableError -} from '../../shared/ripgrep-process-availability' -import { fileListingCancellationError } from '../../shared/file-listing-cancellation' +} from '../../../shared/ripgrep-process-availability' +import { fileListingCancellationError } from '../../../shared/file-listing-cancellation' +import { + finishFilesystemListingProcesses, + type FilesystemListingProcess +} from './filesystem-listing-process-cleanup' export async function listQuickOpenFiles( rootPath: string, @@ -64,11 +73,7 @@ export async function listQuickOpenFiles( maxResults === undefined && maxSerializedBytes === undefined ? new FileInventoryBudget() : null const files = new Set() let serializedBytes = 2 - const children: { - child: ChildProcess - isDone: () => boolean - finish: (error?: Error) => void - }[] = [] + const children: FilesystemListingProcess[] = [] // WSL rg may emit absolute Linux paths; distro identity comes from UNC paths or project runtime. const { primary, ignoredPass } = buildRgArgsForQuickOpen({ // Use a relative search root so root-relative exclusion globs prune traversal instead of only filtering output. @@ -125,7 +130,10 @@ export async function listQuickOpenFiles( } catch (error) { buf = '' files.clear() - killSurvivors(error instanceof Error ? error : new FileInventoryCapacityError()) + finishFilesystemListingProcesses( + children, + error instanceof Error ? error : new FileInventoryCapacityError() + ) return true } files.add(relPath) @@ -157,7 +165,7 @@ export async function listQuickOpenFiles( while (delimiterIdx !== -1) { if (processLine(buf.substring(start, delimiterIdx))) { buf = '' - killSurvivors() + finishFilesystemListingProcesses(children) return } start = delimiterIdx + 1 @@ -220,7 +228,7 @@ export async function listQuickOpenFiles( } if (buf && processLine(buf)) { buf = '' - killSurvivors() + finishFilesystemListingProcesses(children) return } if (code === 0 || code === 1 || (code === 2 && parseablePathCount > 0)) { @@ -277,18 +285,6 @@ export async function listQuickOpenFiles( } }) - const killSurvivors = (error?: Error): void => { - for (const entry of children) { - if (entry.isDone()) { - continue - } - entry.finish(error) - if (entry.child.exitCode === null && entry.child.signalCode === null) { - killSpawnedRipgrepProcess(entry.child) - } - } - } - try { if (options.includeIgnored === false) { await runRg(primary) @@ -320,7 +316,7 @@ export async function listQuickOpenFiles( } } } catch (err) { - killSurvivors() + finishFilesystemListingProcesses(children) throw err instanceof RipgrepUnavailableError ? bundledRipgrepUnavailableError() : err } const result = Array.from(files).slice(0, maxResults) diff --git a/src/main/ipc/filesystem/filesystem-listing-process-cleanup.ts b/src/main/ipc/filesystem/filesystem-listing-process-cleanup.ts new file mode 100644 index 00000000000..34ca37bbd7c --- /dev/null +++ b/src/main/ipc/filesystem/filesystem-listing-process-cleanup.ts @@ -0,0 +1,23 @@ +import type { ChildProcessHandle } from '@orca/process-host/process-spec' +import { killSpawnedRipgrepProcess } from '../../../shared/ripgrep-process-availability' + +export type FilesystemListingProcess = { + child: ChildProcessHandle + isDone: () => boolean + finish: (error?: Error) => void +} + +export function finishFilesystemListingProcesses( + children: readonly FilesystemListingProcess[], + error?: Error +): void { + for (const entry of children) { + if (entry.isDone()) { + continue + } + entry.finish(error) + if (entry.child.exitCode === null && entry.child.signalCode === null) { + killSpawnedRipgrepProcess(entry.child) + } + } +} diff --git a/src/main/ipc/filesystem/filesystem-mutations.ts b/src/main/ipc/filesystem/filesystem-mutations.ts index e183ce51b2c..287e9d82612 100644 --- a/src/main/ipc/filesystem/filesystem-mutations.ts +++ b/src/main/ipc/filesystem/filesystem-mutations.ts @@ -14,17 +14,17 @@ import { resolveLocalDroppedPathsForAgent } from '../dropped-path-resolution' import type { SshMutationExpectation } from '../../../shared/ssh-types' import { assertSshMutationExpectation } from '../../ssh/connection/ssh-connection-generation' import { renameLocalPathSerializedByDestination } from '../../destination-serialized-local-rename' -import { assertNotExists, rethrowWithUserMessage } from '../filesystem-create-path-guards' +import { assertNotExists, rethrowWithUserMessage } from './filesystem-create-path-guards' import type { ImportItemResult, ResolveDroppedPathsResult, StagedExternalImportSource } from '../../../shared/filesystem-import-result-types' -import { importOneSource } from '../filesystem-import-local' +import { importOneSource } from './filesystem-import-local' import { stagedRuntimeUploadByteLength, stageOneSourceForRuntimeUpload -} from '../filesystem-runtime-upload-staging' +} from './filesystem-runtime-upload-staging' import { streamExternalFileToRuntime } from '../runtime-upload-file-stream' import { abortWhenRendererGone } from '../renderer-lifetime-abort' import { sweepAbandonedRuntimeUploadTempPath } from '../runtime-upload-temp-sweep' diff --git a/src/main/ipc/filesystem-path-containment-remote-enoent.test.ts b/src/main/ipc/filesystem/filesystem-path-containment-remote-enoent.test.ts similarity index 100% rename from src/main/ipc/filesystem-path-containment-remote-enoent.test.ts rename to src/main/ipc/filesystem/filesystem-path-containment-remote-enoent.test.ts diff --git a/src/main/ipc/filesystem-path-containment-unicode-normalization.test.ts b/src/main/ipc/filesystem/filesystem-path-containment-unicode-normalization.test.ts similarity index 98% rename from src/main/ipc/filesystem-path-containment-unicode-normalization.test.ts rename to src/main/ipc/filesystem/filesystem-path-containment-unicode-normalization.test.ts index 09159ab925f..a5d069d952a 100644 --- a/src/main/ipc/filesystem-path-containment-unicode-normalization.test.ts +++ b/src/main/ipc/filesystem/filesystem-path-containment-unicode-normalization.test.ts @@ -3,8 +3,8 @@ import { tmpdir } from 'node:os' import { join, resolve } from 'node:path' import { afterEach, describe, expect, it } from 'vitest' -import type { Store } from '../persistence' -import type { Repo } from '../../shared/repo-types' +import type { Store } from '../../persistence/loading-store/store' +import type { Repo } from '../../../shared/repo-types' import { PATH_ACCESS_DENIED_MESSAGE, resolveAuthorizedPath } from './filesystem-auth' import { isDescendantOrEqual } from './filesystem-path-containment' diff --git a/src/main/ipc/filesystem-path-containment.ts b/src/main/ipc/filesystem/filesystem-path-containment.ts similarity index 98% rename from src/main/ipc/filesystem-path-containment.ts rename to src/main/ipc/filesystem/filesystem-path-containment.ts index ad3a722ce14..2726315e44a 100644 --- a/src/main/ipc/filesystem-path-containment.ts +++ b/src/main/ipc/filesystem/filesystem-path-containment.ts @@ -1,7 +1,7 @@ import { resolve, relative, isAbsolute, sep, dirname } from 'node:path' import { statSync } from 'node:fs' import { realpath } from 'node:fs/promises' -import { isDefinitiveAbsence } from '../../shared/definitive-filesystem-absence' +import { isDefinitiveAbsence } from '../../../shared/definitive-filesystem-absence' /** Check native-path containment; accept Unicode-equivalent spellings only after proving filesystem identity. */ export function isDescendantOrEqual(resolvedTarget: string, resolvedBase: string): boolean { diff --git a/src/main/ipc/filesystem/filesystem-quick-open-options.integration.test.ts b/src/main/ipc/filesystem/filesystem-quick-open-options.integration.test.ts index aa3a5e4ff82..686ce678ca4 100644 --- a/src/main/ipc/filesystem/filesystem-quick-open-options.integration.test.ts +++ b/src/main/ipc/filesystem/filesystem-quick-open-options.integration.test.ts @@ -4,8 +4,8 @@ import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, expect, it } from 'vitest' import type { Store } from '../../persistence/loading-store/store' -import { listQuickOpenFiles } from '../filesystem-list-files' -import { searchQuickOpenFilePaths } from '../filesystem-search-file-paths' +import { listQuickOpenFiles } from './filesystem-list-files' +import { searchQuickOpenFilePaths } from './filesystem-search-file-paths' const fixtures: string[] = [] afterEach(async () => { diff --git a/src/main/ipc/filesystem/filesystem-read-handlers.ts b/src/main/ipc/filesystem/filesystem-read-handlers.ts index ffc354460ad..02c3f5f2a07 100644 --- a/src/main/ipc/filesystem/filesystem-read-handlers.ts +++ b/src/main/ipc/filesystem/filesystem-read-handlers.ts @@ -1,4 +1,4 @@ -import { classifyFilesystemDirectoryEntries } from '../filesystem-symlink-directory-entries' +import { classifyFilesystemDirectoryEntries } from './filesystem-symlink-directory-entries' import { markdownDocumentsFromRelativePaths } from '../../../shared/markdown-document-paths' import { capturePathExistence, @@ -16,7 +16,7 @@ import { resolveDesktopAuthorizedPath, resolveLocalFileRequestPath } from '../local-file-access-resolution' -import { isPathAbsentError } from '../filesystem-path-containment' +import { isPathAbsentError } from './filesystem-path-containment' import { listMarkdownDocuments } from '../markdown-documents' import { getLocalGitOptionsForRegisteredWorktree } from '../local-worktree-runtime-options' import { recordCrashBreadcrumb } from '../../crash-reporting/crash-breadcrumb-store' diff --git a/src/main/ipc/filesystem-runtime-upload-staging.test.ts b/src/main/ipc/filesystem/filesystem-runtime-upload-staging.test.ts similarity index 98% rename from src/main/ipc/filesystem-runtime-upload-staging.test.ts rename to src/main/ipc/filesystem/filesystem-runtime-upload-staging.test.ts index 6f3adf2692d..a98e19656ee 100644 --- a/src/main/ipc/filesystem-runtime-upload-staging.test.ts +++ b/src/main/ipc/filesystem/filesystem-runtime-upload-staging.test.ts @@ -2,12 +2,12 @@ import { lstat, mkdtemp, mkdir, rm, symlink, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type * as RuntimeImportLimits from './runtime-import-limits' +import type * as RuntimeImportLimits from '../runtime-import-limits' type RuntimeImportLimitsModule = typeof RuntimeImportLimits // Scale down gigabyte ceilings: NTFS truncate may allocate the entire oversized fixture. -vi.mock('./runtime-import-limits', async (importOriginal) => ({ +vi.mock('../runtime-import-limits', async (importOriginal) => ({ ...(await importOriginal()), REMOTE_IMPORT_MAX_FILE_BYTES: 4 * 1024, REMOTE_IMPORT_MAX_TOTAL_BYTES: 16 * 1024 diff --git a/src/main/ipc/filesystem-runtime-upload-staging.ts b/src/main/ipc/filesystem/filesystem-runtime-upload-staging.ts similarity index 98% rename from src/main/ipc/filesystem-runtime-upload-staging.ts rename to src/main/ipc/filesystem/filesystem-runtime-upload-staging.ts index 8e1a1f1081d..552f2ebf46e 100644 --- a/src/main/ipc/filesystem-runtime-upload-staging.ts +++ b/src/main/ipc/filesystem/filesystem-runtime-upload-staging.ts @@ -2,16 +2,16 @@ import { formatByteCeiling, REMOTE_IMPORT_MAX_FILE_BYTES, REMOTE_IMPORT_MAX_TOTAL_BYTES -} from './runtime-import-limits' +} from '../runtime-import-limits' import { constants } from 'node:fs' import { lstat, open, readdir, realpath, type FileHandle } from 'node:fs/promises' import { basename, extname, isAbsolute, join, relative, resolve, sep } from 'node:path' import { classifyImportSource } from './filesystem-import-source-stat' -import { detectImageFileSignature, IMAGE_SIGNATURE_BYTES } from '../image-file-signature' +import { detectImageFileSignature, IMAGE_SIGNATURE_BYTES } from '../../image-file-signature' import type { StagedExternalImportEntry, StagedExternalImportSource -} from '../../shared/filesystem-import-result-types' +} from '../../../shared/filesystem-import-result-types' class RuntimeUploadSymlinkError extends Error {} diff --git a/src/main/ipc/filesystem-search-file-paths.test.ts b/src/main/ipc/filesystem/filesystem-search-file-paths.test.ts similarity index 96% rename from src/main/ipc/filesystem-search-file-paths.test.ts rename to src/main/ipc/filesystem/filesystem-search-file-paths.test.ts index e7b665cfae1..15959e4eda8 100644 --- a/src/main/ipc/filesystem-search-file-paths.test.ts +++ b/src/main/ipc/filesystem/filesystem-search-file-paths.test.ts @@ -1,11 +1,11 @@ import { EventEmitter } from 'node:events' import type { ChildProcess } from 'node:child_process' import { beforeEach, describe, expect, it, vi } from 'vitest' -import type { Store } from '../persistence' -import type * as GitRunner from '../git/runner' -import type * as BundledRipgrepPath from '../ripgrep/bundled-ripgrep-path' -import { isFileListingCancellation } from '../../shared/file-listing-cancellation' -import { RipgrepUnavailableError } from '../../shared/ripgrep-process-availability' +import type { Store } from '../../persistence/loading-store/store' +import type * as GitRunner from '../../git/runner' +import type * as BundledRipgrepPath from '../../ripgrep/bundled-ripgrep-path' +import { isFileListingCancellation } from '../../../shared/file-listing-cancellation' +import { RipgrepUnavailableError } from '../../../shared/ripgrep-process-availability' const { bundledRipgrepCommandMock, @@ -19,7 +19,7 @@ const { wslAwareSpawnMock: vi.fn() })) -vi.mock('../git/runner', async (importOriginal) => ({ +vi.mock('../../git/runner', async (importOriginal) => ({ ...(await importOriginal()), wslAwareSpawn: wslAwareSpawnMock })) @@ -28,12 +28,12 @@ vi.mock('./filesystem-auth', () => ({ resolveAuthorizedPath: resolveAuthorizedPathMock })) -vi.mock('../ripgrep/bundled-ripgrep-path', async (importOriginal) => ({ +vi.mock('../../ripgrep/bundled-ripgrep-path', async (importOriginal) => ({ ...(await importOriginal()), bundledRipgrepCommand: bundledRipgrepCommandMock })) -vi.mock('./local-worktree-runtime-options', () => ({ +vi.mock('../local-worktree-runtime-options', () => ({ getLocalGitOptionsForRegisteredWorktree: getLocalGitOptionsForRegisteredWorktreeMock })) diff --git a/src/main/ipc/filesystem-search-file-paths.ts b/src/main/ipc/filesystem/filesystem-search-file-paths.ts similarity index 89% rename from src/main/ipc/filesystem-search-file-paths.ts rename to src/main/ipc/filesystem/filesystem-search-file-paths.ts index 59a29c81066..70058308b08 100644 --- a/src/main/ipc/filesystem-search-file-paths.ts +++ b/src/main/ipc/filesystem/filesystem-search-file-paths.ts @@ -1,16 +1,19 @@ -import { getQuickOpenRgOutputMode } from '../../shared/quick-open-ripgrep-output-mode' -import { RipgrepFilenameDecoder } from '../../shared/ripgrep-filename-decoder' +import { getQuickOpenRgOutputMode } from '../../../shared/quick-open-ripgrep-output-mode' +import { RipgrepFilenameDecoder } from '../../../shared/ripgrep-filename-decoder' import { sep } from 'node:path' -import type { RuntimeStore } from '../runtime/runtime-store-contract' -import { fileListingCancellationError } from '../../shared/file-listing-cancellation' +import type { RuntimeStore } from '../../runtime/runtime-store-contract' +import { fileListingCancellationError } from '../../../shared/file-listing-cancellation' import { buildExcludePathPrefixes, buildRgArgsForQuickOpen, normalizeQuickOpenRgLine, shouldExcludeQuickOpenRelPath, shouldIncludeQuickOpenPath -} from '../../shared/quick-open-filter' -import { isQuickOpenQueryTooLarge, QuickOpenPathRanker } from '../../shared/quick-open-path-search' +} from '../../../shared/quick-open-filter' +import { + isQuickOpenQueryTooLarge, + QuickOpenPathRanker +} from '../../../shared/quick-open-path-search' import { absorbPendingRipgrepSpawnError, isRipgrepUnavailableExit, @@ -22,14 +25,14 @@ import { ripgrepMissingCwdError, RipgrepLaunchFailureError, RipgrepUnavailableError -} from '../../shared/ripgrep-process-availability' -import { toWindowsWslPath } from '../wsl' -import { resolveBundledRipgrepWslRouting } from '../ripgrep/bundled-ripgrep-wsl-routing' +} from '../../../shared/ripgrep-process-availability' +import { toWindowsWslPath } from '../../../shared/wsl-paths' +import { resolveBundledRipgrepWslRouting } from '../../ripgrep/bundled-ripgrep-wsl-routing' import { resolveAuthorizedPath } from './filesystem-auth' -import { getLocalGitOptionsForRegisteredWorktree } from './local-worktree-runtime-options' -import { QuickOpenSubprocessPathAccumulator } from '../../shared/quick-open-subprocess-path-accumulator' -import { bundledRipgrepUnavailableError } from '../ripgrep/bundled-ripgrep-path' -import { spawnBundledRipgrep } from '../ripgrep/bundled-ripgrep-spawn' +import { getLocalGitOptionsForRegisteredWorktree } from '../local-worktree-runtime-options' +import { QuickOpenSubprocessPathAccumulator } from '../../../shared/quick-open-subprocess-path-accumulator' +import { bundledRipgrepUnavailableError } from '../../ripgrep/bundled-ripgrep-path' +import { spawnBundledRipgrep } from '../../ripgrep/bundled-ripgrep-spawn' export type QuickOpenFilePathSearchResult = { paths: string[] diff --git a/src/main/ipc/filesystem/filesystem-search-handlers.ts b/src/main/ipc/filesystem/filesystem-search-handlers.ts index 216c25b3457..56d552b851e 100644 --- a/src/main/ipc/filesystem/filesystem-search-handlers.ts +++ b/src/main/ipc/filesystem/filesystem-search-handlers.ts @@ -1,5 +1,5 @@ import { ipcMain } from 'electron' -import { listQuickOpenFiles } from '../filesystem-list-files' +import { listQuickOpenFiles } from './filesystem-list-files' import { isFileNameFilterQueryTooLarge, pathMatchesFileNameFilterTokens, diff --git a/src/main/ipc/filesystem-search-rg-timeout.test.ts b/src/main/ipc/filesystem/filesystem-search-rg-timeout.test.ts similarity index 96% rename from src/main/ipc/filesystem-search-rg-timeout.test.ts rename to src/main/ipc/filesystem/filesystem-search-rg-timeout.test.ts index aac5169a08f..666211f755d 100644 --- a/src/main/ipc/filesystem-search-rg-timeout.test.ts +++ b/src/main/ipc/filesystem/filesystem-search-rg-timeout.test.ts @@ -1,7 +1,7 @@ import { EventEmitter } from 'node:events' import type { ChildProcess } from 'node:child_process' import { beforeEach, describe, expect, it, vi } from 'vitest' -import type * as BundledRipgrepPath from '../ripgrep/bundled-ripgrep-path' +import type * as BundledRipgrepPath from '../../ripgrep/bundled-ripgrep-path' const { handleMock, @@ -33,12 +33,12 @@ vi.mock('electron', () => ({ } })) -vi.mock('../git/runner', () => ({ +vi.mock('../../git/runner', () => ({ gitExecFileAsync: vi.fn(), wslAwareSpawn: wslAwareSpawnMock })) -vi.mock('../wsl', () => ({ +vi.mock('../../wsl', () => ({ parseWslPath: parseWslPathMock, toWindowsWslPath: toWindowsWslPathMock })) @@ -52,7 +52,7 @@ vi.mock('./filesystem-path-containment', () => ({ validateGitRelativeFilePath: vi.fn((value: string) => value) })) -vi.mock('./registered-worktree-roots-cache', () => ({ +vi.mock('../registered-worktree-roots-cache', () => ({ resolveRegisteredWorktreePath: vi.fn(async (value: string) => value) })) @@ -60,24 +60,24 @@ vi.mock('./filesystem-list-files', () => ({ listQuickOpenFiles: vi.fn() })) -vi.mock('./filesystem/filesystem-mutations', () => ({ +vi.mock('./filesystem-mutations', () => ({ registerFilesystemMutationHandlers: vi.fn() })) -vi.mock('../ripgrep/bundled-ripgrep-path', async (importOriginal) => ({ +vi.mock('../../ripgrep/bundled-ripgrep-path', async (importOriginal) => ({ ...(await importOriginal()), bundledRipgrepCommand: bundledRipgrepCommandMock })) -vi.mock('./local-worktree-runtime-options', () => ({ +vi.mock('../local-worktree-runtime-options', () => ({ getLocalGitOptionsForRegisteredWorktree: getLocalGitOptionsForRegisteredWorktreeMock })) -vi.mock('./markdown-documents', () => ({ +vi.mock('../markdown-documents', () => ({ listMarkdownDocuments: vi.fn() })) -import { registerFilesystemHandlers } from './filesystem/filesystem' +import { registerFilesystemHandlers } from './filesystem' function createMockProcess(): ChildProcess { const p = new EventEmitter() as unknown as ChildProcess diff --git a/src/main/ipc/filesystem/filesystem-source-control-ai-targets.test.ts b/src/main/ipc/filesystem/filesystem-source-control-ai-targets.test.ts index 5e1c9666cd8..0351d4dc7e0 100644 --- a/src/main/ipc/filesystem/filesystem-source-control-ai-targets.test.ts +++ b/src/main/ipc/filesystem/filesystem-source-control-ai-targets.test.ts @@ -11,7 +11,7 @@ vi.mock('../../project-runtime-git-options', () => ({ vi.mock('../registered-worktree-roots-cache', () => ({ resolveRegisteredWorktreePath: async (path: string) => path })) -vi.mock('../filesystem-auth', () => ({ resolveAuthorizedPath: vi.fn() })) +vi.mock('./filesystem-auth', () => ({ resolveAuthorizedPath: vi.fn() })) import { getRepoForSourceControlAi } from './filesystem-source-control-ai-targets' const repo: Repo = { diff --git a/src/main/ipc/filesystem/filesystem-source-control-ai-targets.ts b/src/main/ipc/filesystem/filesystem-source-control-ai-targets.ts index c6a57b5b313..8b65ffbd24b 100644 --- a/src/main/ipc/filesystem/filesystem-source-control-ai-targets.ts +++ b/src/main/ipc/filesystem/filesystem-source-control-ai-targets.ts @@ -8,7 +8,7 @@ import type { CommitMessageAgentRuntimeTarget } from '../../text-generation/comm import type { CommitMessageGenerationTarget } from '../../text-generation/commit-message-text-generation' import { resolve } from 'node:path' import { listRepoWorktreeGraph } from '../../repo-worktrees' -import { resolveAuthorizedPath } from '../filesystem-auth' +import { resolveAuthorizedPath } from './filesystem-auth' import { resolveRegisteredWorktreePath } from '../registered-worktree-roots-cache' import { splitWorktreeId } from '../../../shared/worktree/id' diff --git a/src/main/ipc/filesystem-symlink-directory-entries.test.ts b/src/main/ipc/filesystem/filesystem-symlink-directory-entries.test.ts similarity index 100% rename from src/main/ipc/filesystem-symlink-directory-entries.test.ts rename to src/main/ipc/filesystem/filesystem-symlink-directory-entries.test.ts diff --git a/src/main/ipc/filesystem-symlink-directory-entries.ts b/src/main/ipc/filesystem/filesystem-symlink-directory-entries.ts similarity index 84% rename from src/main/ipc/filesystem-symlink-directory-entries.ts rename to src/main/ipc/filesystem/filesystem-symlink-directory-entries.ts index e0e5689de01..a9f4350e7a9 100644 --- a/src/main/ipc/filesystem-symlink-directory-entries.ts +++ b/src/main/ipc/filesystem/filesystem-symlink-directory-entries.ts @@ -1,8 +1,8 @@ import type { Dirent } from 'node:fs' import { stat } from 'node:fs/promises' import { join } from 'node:path' -import type { DirEntry } from '../../shared/filesystem-entry-types' -import { mapWithConcurrency } from '../../shared/map-with-concurrency' +import type { DirEntry } from '../../../shared/filesystem-entry-types' +import { mapWithConcurrency } from '../../../shared/map-with-concurrency' export function classifyFilesystemDirectoryEntries( dirPath: string, diff --git a/src/main/ipc/filesystem/filesystem-write-handlers.ts b/src/main/ipc/filesystem/filesystem-write-handlers.ts index 36d6964e04e..447c9bec571 100644 --- a/src/main/ipc/filesystem/filesystem-write-handlers.ts +++ b/src/main/ipc/filesystem/filesystem-write-handlers.ts @@ -9,7 +9,7 @@ import { resolveDesktopAuthorizedPath, resolveLocalWriteRequestPath } from '../local-file-access-resolution' -import { isENOENT } from '../filesystem-path-containment' +import { isENOENT } from './filesystem-path-containment' import { registerFilesystemMutationHandlers } from './filesystem-mutations' import { registerAgentSessionAttachmentUploadHandlers } from '../agent-session-attachment-upload-ipc' import type { FilesystemHandlerContext } from './filesystem-handler-context' diff --git a/src/main/ipc/filesystem/git-remote/compare-handlers.ts b/src/main/ipc/filesystem/git-remote/compare-handlers.ts index 83e3bcf0164..aec2b0ad3ac 100644 --- a/src/main/ipc/filesystem/git-remote/compare-handlers.ts +++ b/src/main/ipc/filesystem/git-remote/compare-handlers.ts @@ -6,7 +6,7 @@ import type { import { getBranchCompare, getCommitCompare } from '../../../git/status' import { resolveRegisteredWorktreePath } from '../../registered-worktree-roots-cache' import { getLocalGitOptionsForRegisteredWorktree } from '../../local-worktree-runtime-options' -import { validateFullGitObjectId } from '../../filesystem-path-containment' +import { validateFullGitObjectId } from '../filesystem-path-containment' import type { FilesystemHandlerContext } from '../filesystem-handler-context' import type { GitAdmissionTier } from '../../../git/command-runner/git-exec-options' import { requireReachableGitRoute } from '../../../providers/execution-host-provider-dispatch' diff --git a/src/main/ipc/filesystem/local-regular-file-read.ts b/src/main/ipc/filesystem/local-regular-file-read.ts index 600ac8b026a..8f1985888d3 100644 --- a/src/main/ipc/filesystem/local-regular-file-read.ts +++ b/src/main/ipc/filesystem/local-regular-file-read.ts @@ -1,6 +1,6 @@ import { constants, type Stats } from 'node:fs' import { open, type FileHandle } from 'node:fs/promises' -import { isENOENT } from '../filesystem-path-containment' +import { isENOENT } from './filesystem-path-containment' export const NOT_A_REGULAR_FILE_MESSAGE = 'Not a regular file' diff --git a/src/main/ipc/hooks/register-worktree-hook-inspection-handler.ts b/src/main/ipc/hooks/register-worktree-hook-inspection-handler.ts index c0abeb95230..699d022136f 100644 --- a/src/main/ipc/hooks/register-worktree-hook-inspection-handler.ts +++ b/src/main/ipc/hooks/register-worktree-hook-inspection-handler.ts @@ -4,7 +4,7 @@ import { isFolderRepo } from '../../../shared/repo-kind' import { inspectSetupScriptImportCandidates } from '../../../shared/setup-script-imports' import { joinWorktreeRelativePath } from '../../runtime/runtime-relative-paths' import { readFile, stat } from 'node:fs/promises' -import { isENOENT } from '../filesystem-path-containment' +import { isENOENT } from '../filesystem/filesystem-path-containment' import { resolveRepoForExecutionHost } from '../worktrees/repo-host-ownership' import type { WorktreeIpcContext } from '../worktrees/worktree-ipc-context' diff --git a/src/main/ipc/local-file-access-resolution.test.ts b/src/main/ipc/local-file-access-resolution.test.ts index 4b338079d66..23879fe6b96 100644 --- a/src/main/ipc/local-file-access-resolution.test.ts +++ b/src/main/ipc/local-file-access-resolution.test.ts @@ -5,7 +5,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { installFakeAppEnvironment } from '../../../config/scripts/vitest-host-ports-setup' import type { Store } from '../persistence' import type * as RepoWorktrees from '../repo-worktrees' -import { resolveAuthorizedPath } from './filesystem-auth' +import { resolveAuthorizedPath } from './filesystem/filesystem-auth' import { resolveDesktopAuthorizedPath, resolveLocalFileRequestPath, diff --git a/src/main/ipc/local-file-access-resolution.ts b/src/main/ipc/local-file-access-resolution.ts index db9c56f768c..f153eb6bfc9 100644 --- a/src/main/ipc/local-file-access-resolution.ts +++ b/src/main/ipc/local-file-access-resolution.ts @@ -6,8 +6,8 @@ import { PATH_ACCESS_DENIED_MESSAGE, resolveAuthorizedPath, type ResolveAuthorizedPathOptions -} from './filesystem-auth' -import { isDescendantOrEqual } from './filesystem-path-containment' +} from './filesystem/filesystem-auth' +import { isDescendantOrEqual } from './filesystem/filesystem-path-containment' import { PREVIEWABLE_BINARY_MIME_TYPES } from './filesystem/filesystem-file-content-inspection' import { getDefaultFloatingWorkspacePath } from './floating-workspace-directory' import { diff --git a/src/main/ipc/registered-worktree-root-match.ts b/src/main/ipc/registered-worktree-root-match.ts new file mode 100644 index 00000000000..2e37c47ef4f --- /dev/null +++ b/src/main/ipc/registered-worktree-root-match.ts @@ -0,0 +1,14 @@ +import { isDescendantOrEqual } from './filesystem/filesystem-path-containment' + +export function findRegisteredWorktreeRoot( + targetPath: string, + registeredWorktreeRoots: Iterable +): string | null { + let bestRoot: string | null = null + for (const root of registeredWorktreeRoots) { + if (isDescendantOrEqual(targetPath, root) && (!bestRoot || root.length > bestRoot.length)) { + bestRoot = root + } + } + return bestRoot +} diff --git a/src/main/ipc/registered-worktree-root-ownership.test.ts b/src/main/ipc/registered-worktree-root-ownership.test.ts index c03f923d7ec..2643ab05f5c 100644 --- a/src/main/ipc/registered-worktree-root-ownership.test.ts +++ b/src/main/ipc/registered-worktree-root-ownership.test.ts @@ -15,10 +15,9 @@ vi.mock('./worktree-logic', () => ({ getWorktreePathSettings: vi.fn() })) vi.mock('../project-runtime-git-options', () => ({ - getWorktreeMirrorDistroForRuntime: vi.fn(), - resolveLocalProjectRuntimesForRepos: vi.fn() + getWorktreeMirrorDistroForRuntime: vi.fn() })) -import { resolveAuthorizedPath } from './filesystem-auth' +import { resolveAuthorizedPath } from './filesystem/filesystem-auth' import { __resetCreatedWorktreeRootsForTests, invalidateAuthorizedRootsCache, diff --git a/src/main/ipc/registered-worktree-root-refresh.test.ts b/src/main/ipc/registered-worktree-root-refresh.test.ts index f33221adb05..f5274c70643 100644 --- a/src/main/ipc/registered-worktree-root-refresh.test.ts +++ b/src/main/ipc/registered-worktree-root-refresh.test.ts @@ -15,10 +15,9 @@ vi.mock('./worktree-logic', () => ({ getWorktreePathSettings: vi.fn() })) vi.mock('../project-runtime-git-options', () => ({ - getWorktreeMirrorDistroForRuntime: vi.fn(), - resolveLocalProjectRuntimesForRepos: vi.fn() + getWorktreeMirrorDistroForRuntime: vi.fn() })) -import { resolveAuthorizedPath } from './filesystem-auth' +import { resolveAuthorizedPath } from './filesystem/filesystem-auth' import { __resetCreatedWorktreeRootsForTests, invalidateAuthorizedRootsCache, diff --git a/src/main/ipc/registered-worktree-root-wsl-runtime.test.ts b/src/main/ipc/registered-worktree-root-wsl-runtime.test.ts index b3bcde48243..571fe4183cd 100644 --- a/src/main/ipc/registered-worktree-root-wsl-runtime.test.ts +++ b/src/main/ipc/registered-worktree-root-wsl-runtime.test.ts @@ -14,7 +14,7 @@ vi.mock('../repo-worktrees', () => ({ vi.mock('../local-project-runtime-resolution', () => ({ resolveLocalProjectRuntimesForRepos: mocks.runtimes })) -import { PATH_ACCESS_DENIED_MESSAGE, resolveAuthorizedPath } from './filesystem-auth' +import { PATH_ACCESS_DENIED_MESSAGE, resolveAuthorizedPath } from './filesystem/filesystem-auth' import { __resetCreatedWorktreeRootsForTests, invalidateAuthorizedRootsCache, diff --git a/src/main/ipc/registered-worktree-roots-cache.ts b/src/main/ipc/registered-worktree-roots-cache.ts index a93001a9f5f..68a5f4767e5 100644 --- a/src/main/ipc/registered-worktree-roots-cache.ts +++ b/src/main/ipc/registered-worktree-roots-cache.ts @@ -1,11 +1,15 @@ import { resolve } from 'node:path' +import { findRegisteredWorktreeRoot } from './registered-worktree-root-match' import type { Repo } from '../../shared/repo-types' import type { RegisteredWorktreeRootStore as Store } from './registered-worktree-root-store' import { listWorktreeRootsWithConcurrency, pruneCreatedWorktreeRoots } from './registered-worktree-root-probes' -import { isDescendantOrEqual, normalizeExistingPath } from './filesystem-path-containment' +import { + isDescendantOrEqual, + normalizeExistingPath +} from './filesystem/filesystem-path-containment' import { findOwnersListedThroughAnotherGit, shouldRelistOwner @@ -283,7 +287,7 @@ export async function isPathAllowedByCanonicalRegisteredRoot( return false } synchronizeOwners(store) - const textualRoot = findRegisteredWorktreeRoot(sourcePath) + const textualRoot = findRegisteredWorktreeRoot(sourcePath, registeredWorktreeRoots) if (!textualRoot) { return false } @@ -314,13 +318,3 @@ export async function isPathAllowedByCanonicalRegisteredRoot( } return allowed } - -function findRegisteredWorktreeRoot(targetPath: string): string | null { - let bestRoot: string | null = null - for (const root of registeredWorktreeRoots) { - if (isDescendantOrEqual(targetPath, root) && (!bestRoot || root.length > bestRoot.length)) { - bestRoot = root - } - } - return bestRoot -} diff --git a/src/main/ipc/runtime-upload-file-stream.test.ts b/src/main/ipc/runtime-upload-file-stream.test.ts index 96924dc7a12..483b5e50268 100644 --- a/src/main/ipc/runtime-upload-file-stream.test.ts +++ b/src/main/ipc/runtime-upload-file-stream.test.ts @@ -35,7 +35,7 @@ vi.mock('./runtime-environments/runtime-environment-transport-routing', () => ({ callRuntimeEnvironment: (...args: Parameters) => callRuntimeEnvironment(...args) })) -// Lower test caps avoid allocating gigabytes on Windows; see filesystem-runtime-upload-staging.test.ts. +// Lower test caps avoid allocating gigabytes on Windows; see filesystem/filesystem-runtime-upload-staging.test.ts. vi.mock('./runtime-import-limits', async (importOriginal) => ({ ...(await importOriginal()), REMOTE_IMPORT_MAX_FILE_BYTES: 2 * 1024 * 1024 diff --git a/src/main/ipc/runtime-upload-slice-boundaries.test.ts b/src/main/ipc/runtime-upload-slice-boundaries.test.ts index 9a6c3c1091f..ebe2e8e8e3d 100644 --- a/src/main/ipc/runtime-upload-slice-boundaries.test.ts +++ b/src/main/ipc/runtime-upload-slice-boundaries.test.ts @@ -39,7 +39,8 @@ vi.mock('./runtime-environments/runtime-environment-transport-routing', () => ({ const { RUNTIME_UPLOAD_SLICE_BYTES, streamExternalFileToRuntime } = await import('./runtime-upload-file-stream') -const { stageOneSourceForRuntimeUpload } = await import('./filesystem-runtime-upload-staging') +const { stageOneSourceForRuntimeUpload } = + await import('./filesystem/filesystem-runtime-upload-staging') const { REMOTE_IMPORT_MAX_FILE_BYTES, REMOTE_IMPORT_MAX_TOTAL_BYTES, formatByteCeiling } = await import('./runtime-import-limits') diff --git a/src/main/native-chat/native-chat-visual-file-read.ts b/src/main/native-chat/native-chat-visual-file-read.ts index cdf6313fac3..64e1e623bc9 100644 --- a/src/main/native-chat/native-chat-visual-file-read.ts +++ b/src/main/native-chat/native-chat-visual-file-read.ts @@ -12,7 +12,7 @@ import type { AgentSessionReadVisualResult, AgentSessionVisualReadError } from '../../shared/rpc-contract/agent-session-visual-params' -import { isENOENT } from '../ipc/filesystem-path-containment' +import { isENOENT } from '../ipc/filesystem/filesystem-path-containment' import { readLocalFileBounded } from '../ipc/filesystem/local-regular-file-read' // O_NOFOLLOW prevents final-component swaps; Windows relies on lstat and post-open identity checks. diff --git a/src/main/native-chat/transcript-path-admission.ts b/src/main/native-chat/transcript-path-admission.ts index 9b970b818f2..5c305988a83 100644 --- a/src/main/native-chat/transcript-path-admission.ts +++ b/src/main/native-chat/transcript-path-admission.ts @@ -1,6 +1,6 @@ import { lstat, realpath } from 'node:fs/promises' import { basename, dirname, isAbsolute, relative, resolve } from 'node:path' -import { isDescendantOrEqual, isENOENT } from '../ipc/filesystem-path-containment' +import { isDescendantOrEqual, isENOENT } from '../ipc/filesystem/filesystem-path-containment' import { isWslUncPath } from '../../shared/wsl-paths' import { needsWslHostResolution } from './host-readable-transcript-path' diff --git a/src/main/native-chat/transcript-reader.ts b/src/main/native-chat/transcript-reader.ts index 047fdf3fcae..f5c61709e97 100644 --- a/src/main/native-chat/transcript-reader.ts +++ b/src/main/native-chat/transcript-reader.ts @@ -1,5 +1,5 @@ import { readOpenCodeNativeChatTranscriptFull } from './transcript-opencode' -import { isENOENT } from '../ipc/filesystem-path-containment' +import { isENOENT } from '../ipc/filesystem/filesystem-path-containment' import type { AgentType, NativeChatMessage, diff --git a/src/main/native-chat/transcript-tail-reader.ts b/src/main/native-chat/transcript-tail-reader.ts index 6d74bef4878..75c91657696 100644 --- a/src/main/native-chat/transcript-tail-reader.ts +++ b/src/main/native-chat/transcript-tail-reader.ts @@ -1,5 +1,5 @@ import { readOpenCodeNativeChatTranscriptTail } from './transcript-opencode' -import { isENOENT } from '../ipc/filesystem-path-containment' +import { isENOENT } from '../ipc/filesystem/filesystem-path-containment' import type { AgentType, NativeChatMessage, diff --git a/src/main/providers/local-filesystem-provider.ts b/src/main/providers/local-filesystem-provider.ts index 8046d28263c..c3bad7fad7c 100644 --- a/src/main/providers/local-filesystem-provider.ts +++ b/src/main/providers/local-filesystem-provider.ts @@ -3,10 +3,13 @@ import { dirname } from 'node:path' import { throwIfSignalAborted, waitForPromiseWithSignal } from '../../shared/abort-signal-reason' import { readAuthorizedDocPreviewFile } from '../../shared/doc-preview-file-access' import { renameLocalPathSerializedByDestination } from '../destination-serialized-local-rename' -import type { resolveAuthorizedPath as ResolveAuthorizedPath } from '../ipc/filesystem-auth' -import { isENOENT } from '../ipc/filesystem-path-containment' -import { assertNotExists, rethrowWithUserMessage } from '../ipc/filesystem-create-path-guards' -import { classifyFilesystemDirectoryEntries } from '../ipc/filesystem-symlink-directory-entries' +import type { resolveAuthorizedPath as ResolveAuthorizedPath } from '../ipc/filesystem/filesystem-auth' +import { isENOENT } from '../ipc/filesystem/filesystem-path-containment' +import { + assertNotExists, + rethrowWithUserMessage +} from '../ipc/filesystem/filesystem-create-path-guards' +import { classifyFilesystemDirectoryEntries } from '../ipc/filesystem/filesystem-symlink-directory-entries' import { getLocalGitOptionsForRegisteredWorktree } from '../ipc/local-worktree-runtime-options' import { listMarkdownDocuments } from '../ipc/markdown-documents' import type { RuntimeStore } from '../runtime/runtime-store-contract' diff --git a/src/main/runtime/files/chat-scratch-file-root.ts b/src/main/runtime/files/chat-scratch-file-root.ts index 84197c610bd..2bf1d730ee9 100644 --- a/src/main/runtime/files/chat-scratch-file-root.ts +++ b/src/main/runtime/files/chat-scratch-file-root.ts @@ -1,8 +1,11 @@ import { lstat } from 'node:fs/promises' import { basename, dirname } from 'node:path' import type { RuntimeStore } from '../runtime-store-contract' -import { resolveAuthorizedPath, type ResolveAuthorizedPathOptions } from '../../ipc/filesystem-auth' -import { isENOENT } from '../../ipc/filesystem-path-containment' +import { + resolveAuthorizedPath, + type ResolveAuthorizedPathOptions +} from '../../ipc/filesystem/filesystem-auth' +import { isENOENT } from '../../ipc/filesystem/filesystem-path-containment' import { nativeChatScratchRootFor } from '../../native-chat/native-chat-scratch-folder' import { getProfileUserDataPath } from '../../orca-profiles/profile-storage-paths' diff --git a/src/main/runtime/files/file-access.ts b/src/main/runtime/files/file-access.ts index 47ff1e72625..9733e1b8830 100644 --- a/src/main/runtime/files/file-access.ts +++ b/src/main/runtime/files/file-access.ts @@ -16,7 +16,7 @@ import type { RuntimeFileCommandHost } from './file-command-host' import type { RuntimeFileOpenResult } from '../../../shared/runtime-types' import type { RuntimeNavigationTarget } from '../../../shared/runtime-navigation' import { joinWorktreeRelativePath } from '../runtime-relative-paths' -import { isENOENT } from '../../ipc/filesystem-path-containment' +import { isENOENT } from '../../ipc/filesystem/filesystem-path-containment' import { requireRuntimeFileProvider, resolveFileExplorerPath, diff --git a/src/main/runtime/files/file-path-existence.ts b/src/main/runtime/files/file-path-existence.ts index 41042053e44..fab793b2b6b 100644 --- a/src/main/runtime/files/file-path-existence.ts +++ b/src/main/runtime/files/file-path-existence.ts @@ -2,7 +2,7 @@ import { capturePathExistence, type PathExistenceResult } from '../../../shared/path-existence-batch' -import { isPathAbsentError } from '../../ipc/filesystem-path-containment' +import { isPathAbsentError } from '../../ipc/filesystem/filesystem-path-containment' import { requireRuntimeFileProvider, type RuntimeFileExplorerPath, diff --git a/src/main/runtime/files/search/file-search.ts b/src/main/runtime/files/search/file-search.ts index f39c8d41d72..ee4bf0613d0 100644 --- a/src/main/runtime/files/search/file-search.ts +++ b/src/main/runtime/files/search/file-search.ts @@ -8,8 +8,8 @@ import { throwIfSignalAborted, waitForPromiseWithSignal } from '../../../../shared/abort-signal-reason' -import { listQuickOpenFiles } from '../../../ipc/filesystem-list-files' -import { searchQuickOpenFilePaths as searchHostQuickOpenFilePaths } from '../../../ipc/filesystem-search-file-paths' +import { listQuickOpenFiles } from '../../../ipc/filesystem/filesystem-list-files' +import { searchQuickOpenFilePaths as searchHostQuickOpenFilePaths } from '../../../ipc/filesystem/filesystem-search-file-paths' import { isMobileBinaryPath, isSafeMobileRelativePath } from '../mobile-file-kind' import { requireRuntimeFileProvider, diff --git a/src/main/runtime/files/watch/file-watches.ts b/src/main/runtime/files/watch/file-watches.ts index 809aded3071..f551cbfa219 100644 --- a/src/main/runtime/files/watch/file-watches.ts +++ b/src/main/runtime/files/watch/file-watches.ts @@ -12,7 +12,7 @@ import type { RuntimeFileCommandHost } from '../file-command-host' import { stat } from 'node:fs/promises' import type { DirEntry, FsChangeEvent } from '../../../../shared/filesystem-entry-types' import { sortDirEntries } from '../../../../shared/file-name-sort' -import { resolveAuthorizedPath } from '../../../ipc/filesystem-auth' +import { resolveAuthorizedPath } from '../../../ipc/filesystem/filesystem-auth' import { resolveRuntimeAuthorizedPath } from '../chat-scratch-file-root' import { watchWindowsRuntimeFileExplorer } from './windows-file-watch' import { beginWatcherInstall } from '../../../ipc/watcher-removal-gate' diff --git a/src/main/runtime/orca-runtime-files-chunk-reads.test.ts b/src/main/runtime/orca-runtime-files-chunk-reads.test.ts index dc3b87019dc..1d9ae050726 100644 --- a/src/main/runtime/orca-runtime-files-chunk-reads.test.ts +++ b/src/main/runtime/orca-runtime-files-chunk-reads.test.ts @@ -14,7 +14,7 @@ vi.mock( './file-watcher-host', async () => (await import('./orca-runtime-files-mock-registry')).fileWatcherHostMock ) -vi.mock('../ipc/filesystem-auth', async () => +vi.mock('../ipc/filesystem/filesystem-auth', async () => (await import('./orca-runtime-files-mock-registry')).filesystemAuthModuleMock() ) vi.mock('../git/runner', async () => diff --git a/src/main/runtime/orca-runtime-files-mobile-explorer-reads.test.ts b/src/main/runtime/orca-runtime-files-mobile-explorer-reads.test.ts index 683d56cc81f..f34ce9a9fcb 100644 --- a/src/main/runtime/orca-runtime-files-mobile-explorer-reads.test.ts +++ b/src/main/runtime/orca-runtime-files-mobile-explorer-reads.test.ts @@ -21,7 +21,7 @@ vi.mock( './file-watcher-host', async () => (await import('./orca-runtime-files-mock-registry')).fileWatcherHostMock ) -vi.mock('../ipc/filesystem-auth', async () => +vi.mock('../ipc/filesystem/filesystem-auth', async () => (await import('./orca-runtime-files-mock-registry')).filesystemAuthModuleMock() ) vi.mock('../git/runner', async () => diff --git a/src/main/runtime/orca-runtime-files-mock-registry.ts b/src/main/runtime/orca-runtime-files-mock-registry.ts index 498c36f9207..3d29c48c974 100644 --- a/src/main/runtime/orca-runtime-files-mock-registry.ts +++ b/src/main/runtime/orca-runtime-files-mock-registry.ts @@ -3,7 +3,7 @@ import { vi } from 'vitest' import type { Mock } from 'vitest' import type * as Fs from 'node:fs' import type * as FsPromises from 'node:fs/promises' -import type * as FilesystemAuth from '../ipc/filesystem-auth' +import type * as FilesystemAuth from '../ipc/filesystem/filesystem-auth' import type * as GitRunner from '../git/runner' import type * as BundledRipgrepPath from '../ripgrep/bundled-ripgrep-path' @@ -75,7 +75,7 @@ export const fileWatcherHostMock = { } export async function filesystemAuthModuleMock() { - const actual = await vi.importActual('../ipc/filesystem-auth') + const actual = await vi.importActual('../ipc/filesystem/filesystem-auth') return { ...actual, resolveAuthorizedPath: resolveAuthorizedPathMock diff --git a/src/main/runtime/orca-runtime-files-preview-budget.test.ts b/src/main/runtime/orca-runtime-files-preview-budget.test.ts index d66aeab5a38..439ea55a919 100644 --- a/src/main/runtime/orca-runtime-files-preview-budget.test.ts +++ b/src/main/runtime/orca-runtime-files-preview-budget.test.ts @@ -18,7 +18,7 @@ vi.mock( './file-watcher-host', async () => (await import('./orca-runtime-files-mock-registry')).fileWatcherHostMock ) -vi.mock('../ipc/filesystem-auth', async () => +vi.mock('../ipc/filesystem/filesystem-auth', async () => (await import('./orca-runtime-files-mock-registry')).filesystemAuthModuleMock() ) vi.mock('../git/runner', async () => diff --git a/src/main/runtime/orca-runtime-files-rename-authority.test.ts b/src/main/runtime/orca-runtime-files-rename-authority.test.ts index 651668173ac..d7dda1c12a1 100644 --- a/src/main/runtime/orca-runtime-files-rename-authority.test.ts +++ b/src/main/runtime/orca-runtime-files-rename-authority.test.ts @@ -20,7 +20,7 @@ vi.mock( './file-watcher-host', async () => (await import('./orca-runtime-files-mock-registry')).fileWatcherHostMock ) -vi.mock('../ipc/filesystem-auth', async () => +vi.mock('../ipc/filesystem/filesystem-auth', async () => (await import('./orca-runtime-files-mock-registry')).filesystemAuthModuleMock() ) vi.mock('../git/runner', async () => diff --git a/src/main/runtime/orca-runtime-files-search.test.ts b/src/main/runtime/orca-runtime-files-search.test.ts index 65d9baeec26..04542005ff7 100644 --- a/src/main/runtime/orca-runtime-files-search.test.ts +++ b/src/main/runtime/orca-runtime-files-search.test.ts @@ -20,7 +20,7 @@ vi.mock( './file-watcher-host', async () => (await import('./orca-runtime-files-mock-registry')).fileWatcherHostMock ) -vi.mock('../ipc/filesystem-auth', async () => +vi.mock('../ipc/filesystem/filesystem-auth', async () => (await import('./orca-runtime-files-mock-registry')).filesystemAuthModuleMock() ) vi.mock('../git/runner', async () => diff --git a/src/main/runtime/orca-runtime-files-terminal-artifact-grants.test.ts b/src/main/runtime/orca-runtime-files-terminal-artifact-grants.test.ts index 1495cb1329b..a2e271bd018 100644 --- a/src/main/runtime/orca-runtime-files-terminal-artifact-grants.test.ts +++ b/src/main/runtime/orca-runtime-files-terminal-artifact-grants.test.ts @@ -21,7 +21,7 @@ vi.mock( './file-watcher-host', async () => (await import('./orca-runtime-files-mock-registry')).fileWatcherHostMock ) -vi.mock('../ipc/filesystem-auth', async () => +vi.mock('../ipc/filesystem/filesystem-auth', async () => (await import('./orca-runtime-files-mock-registry')).filesystemAuthModuleMock() ) vi.mock('../git/runner', async () => diff --git a/src/main/runtime/orca-runtime-files-terminal-artifact-io.test.ts b/src/main/runtime/orca-runtime-files-terminal-artifact-io.test.ts index 3ca3e67d70b..028bcb59b03 100644 --- a/src/main/runtime/orca-runtime-files-terminal-artifact-io.test.ts +++ b/src/main/runtime/orca-runtime-files-terminal-artifact-io.test.ts @@ -23,7 +23,7 @@ vi.mock( './file-watcher-host', async () => (await import('./orca-runtime-files-mock-registry')).fileWatcherHostMock ) -vi.mock('../ipc/filesystem-auth', async () => +vi.mock('../ipc/filesystem/filesystem-auth', async () => (await import('./orca-runtime-files-mock-registry')).filesystemAuthModuleMock() ) vi.mock('../git/runner', async () => diff --git a/src/main/runtime/orca-runtime-files-terminal-artifact-swap-detection.test.ts b/src/main/runtime/orca-runtime-files-terminal-artifact-swap-detection.test.ts index 8356254c563..1c70ceb7a78 100644 --- a/src/main/runtime/orca-runtime-files-terminal-artifact-swap-detection.test.ts +++ b/src/main/runtime/orca-runtime-files-terminal-artifact-swap-detection.test.ts @@ -20,7 +20,7 @@ vi.mock( './file-watcher-host', async () => (await import('./orca-runtime-files-mock-registry')).fileWatcherHostMock ) -vi.mock('../ipc/filesystem-auth', async () => +vi.mock('../ipc/filesystem/filesystem-auth', async () => (await import('./orca-runtime-files-mock-registry')).filesystemAuthModuleMock() ) vi.mock('../git/runner', async () => diff --git a/src/main/runtime/orca-runtime-files-terminal-link-host-translation.test.ts b/src/main/runtime/orca-runtime-files-terminal-link-host-translation.test.ts index 96d98f4442d..5de2c57b8d0 100644 --- a/src/main/runtime/orca-runtime-files-terminal-link-host-translation.test.ts +++ b/src/main/runtime/orca-runtime-files-terminal-link-host-translation.test.ts @@ -20,7 +20,7 @@ vi.mock( './file-watcher-host', async () => (await import('./orca-runtime-files-mock-registry')).fileWatcherHostMock ) -vi.mock('../ipc/filesystem-auth', async () => +vi.mock('../ipc/filesystem/filesystem-auth', async () => (await import('./orca-runtime-files-mock-registry')).filesystemAuthModuleMock() ) vi.mock('../git/runner', async () => diff --git a/src/main/runtime/orca-runtime-files-terminal-path-resolution.test.ts b/src/main/runtime/orca-runtime-files-terminal-path-resolution.test.ts index 01c34bbf9b9..8ea4425224b 100644 --- a/src/main/runtime/orca-runtime-files-terminal-path-resolution.test.ts +++ b/src/main/runtime/orca-runtime-files-terminal-path-resolution.test.ts @@ -18,7 +18,7 @@ vi.mock( './file-watcher-host', async () => (await import('./orca-runtime-files-mock-registry')).fileWatcherHostMock ) -vi.mock('../ipc/filesystem-auth', async () => +vi.mock('../ipc/filesystem/filesystem-auth', async () => (await import('./orca-runtime-files-mock-registry')).filesystemAuthModuleMock() ) vi.mock('../git/runner', async () => diff --git a/src/main/runtime/orca-runtime-files-watch-host-scope.test.ts b/src/main/runtime/orca-runtime-files-watch-host-scope.test.ts index f1c0fbdac47..980bbf106fa 100644 --- a/src/main/runtime/orca-runtime-files-watch-host-scope.test.ts +++ b/src/main/runtime/orca-runtime-files-watch-host-scope.test.ts @@ -14,7 +14,7 @@ vi.mock( './file-watcher-host', async () => (await import('./orca-runtime-files-mock-registry')).fileWatcherHostMock ) -vi.mock('../ipc/filesystem-auth', async () => +vi.mock('../ipc/filesystem/filesystem-auth', async () => (await import('./orca-runtime-files-mock-registry')).filesystemAuthModuleMock() ) vi.mock('../git/runner', async () => diff --git a/src/main/runtime/orca-runtime-files-watch.test.ts b/src/main/runtime/orca-runtime-files-watch.test.ts index 6d41fc7fb1a..6c87723e57a 100644 --- a/src/main/runtime/orca-runtime-files-watch.test.ts +++ b/src/main/runtime/orca-runtime-files-watch.test.ts @@ -2,7 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { EventEmitter } from 'node:events' import type * as Fs from 'node:fs' import type * as FsPromises from 'node:fs/promises' -import type * as FilesystemAuth from '../ipc/filesystem-auth' +import type * as FilesystemAuth from '../ipc/filesystem/filesystem-auth' import type { FsChangeEvent } from '../../shared/filesystem-entry-types' import { WatcherProcessFailure } from '../ipc/filesystem/watcher-process/parcel-watcher-process-failure' import { acquireWatcherRemovalGate } from '../ipc/watcher-removal-gate' @@ -42,8 +42,8 @@ vi.mock('./file-watcher-host', () => ({ watchFileExplorerInWatcherProcess: watchInWatcherProcessMock })) -vi.mock('../ipc/filesystem-auth', async () => { - const actual = await vi.importActual('../ipc/filesystem-auth') +vi.mock('../ipc/filesystem/filesystem-auth', async () => { + const actual = await vi.importActual('../ipc/filesystem/filesystem-auth') return { ...actual, resolveAuthorizedPath: resolveAuthorizedPathMock diff --git a/src/main/runtime/orca-runtime-quick-open-capabilities.test.ts b/src/main/runtime/orca-runtime-quick-open-capabilities.test.ts index f109d771044..fd635d51edc 100644 --- a/src/main/runtime/orca-runtime-quick-open-capabilities.test.ts +++ b/src/main/runtime/orca-runtime-quick-open-capabilities.test.ts @@ -13,7 +13,7 @@ vi.mock( './file-watcher-host', async () => (await import('./orca-runtime-files-mock-registry')).fileWatcherHostMock ) -vi.mock('../ipc/filesystem-auth', async () => +vi.mock('../ipc/filesystem/filesystem-auth', async () => (await import('./orca-runtime-files-mock-registry')).filesystemAuthModuleMock() ) vi.mock('../git/runner', async () => diff --git a/src/main/runtime/orca-runtime-test-mocks/setup.spec.ts b/src/main/runtime/orca-runtime-test-mocks/setup.spec.ts index b3567916748..5f4e54ae5ce 100644 --- a/src/main/runtime/orca-runtime-test-mocks/setup.spec.ts +++ b/src/main/runtime/orca-runtime-test-mocks/setup.spec.ts @@ -1,7 +1,7 @@ import { expect, vi } from 'vitest' import type { Mock } from 'vitest' import type * as GitUsernameModule from '../../git/git-username' -import type * as FilesystemPathContainmentModule from '../../ipc/filesystem-path-containment' +import type * as FilesystemPathContainmentModule from '../../ipc/filesystem/filesystem-path-containment' import { reviewHeadRemoteRefComponent } from '../../../shared/review-head-tracking-ref' export const ORIGIN_REMOTE_URL = 'git@example.com:group/repo.git' @@ -349,7 +349,7 @@ vi.mock('../../ipc/worktree-logic', async (importOriginal) => { } }) -vi.mock('../../ipc/filesystem-auth', () => ({ +vi.mock('../../ipc/filesystem/filesystem-auth', () => ({ resolveAuthorizedPath: vi.fn(async (pathValue: string) => pathValue) })) @@ -362,9 +362,9 @@ vi.mock('../../ipc/registered-worktree-roots-scoped-invalidation', () => ({ })) // Relay-rebuilt ENOENT errors may retain only the message. -vi.mock('../../ipc/filesystem-path-containment', async () => { +vi.mock('../../ipc/filesystem/filesystem-path-containment', async () => { const actual = await vi.importActual( - '../../ipc/filesystem-path-containment' + '../../ipc/filesystem/filesystem-path-containment' ) return { isENOENT: actual.isENOENT } }) diff --git a/src/main/runtime/runtime-file-listing-producer-budgets.test.ts b/src/main/runtime/runtime-file-listing-producer-budgets.test.ts index 4d275e9ba2d..5858fb5728f 100644 --- a/src/main/runtime/runtime-file-listing-producer-budgets.test.ts +++ b/src/main/runtime/runtime-file-listing-producer-budgets.test.ts @@ -5,7 +5,7 @@ import { useRuntimeFileCommandsLifecycle } from './orca-runtime-files-test-harness' const { localList } = vi.hoisted(() => ({ localList: vi.fn() })) -vi.mock('../ipc/filesystem-list-files', () => ({ listQuickOpenFiles: localList })) +vi.mock('../ipc/filesystem/filesystem-list-files', () => ({ listQuickOpenFiles: localList })) describe('runtime producer listing budgets', () => { useRuntimeFileCommandsLifecycle() diff --git a/src/main/runtime/runtime-file-upload-decode.test.ts b/src/main/runtime/runtime-file-upload-decode.test.ts index bf2e5a8a206..f63961af485 100644 --- a/src/main/runtime/runtime-file-upload-decode.test.ts +++ b/src/main/runtime/runtime-file-upload-decode.test.ts @@ -20,7 +20,7 @@ vi.mock('./files/file-target', () => ({ resolveFileExplorerPath: mocks.resolvePath, requireRuntimeFileProvider: mocks.provider })) -vi.mock('../ipc/filesystem-auth', () => ({ resolveAuthorizedPath: mocks.authorize })) +vi.mock('../ipc/filesystem/filesystem-auth', () => ({ resolveAuthorizedPath: mocks.authorize })) vi.mock('node:fs/promises', async (importOriginal) => ({ ...(await importOriginal()), mkdir: mocks.mkdir, diff --git a/src/main/runtime/runtime-repository-hooks-commands.ts b/src/main/runtime/runtime-repository-hooks-commands.ts index 417c900c1ae..702d7b65938 100644 --- a/src/main/runtime/runtime-repository-hooks-commands.ts +++ b/src/main/runtime/runtime-repository-hooks-commands.ts @@ -7,7 +7,7 @@ import { getDefaultTabCommandTrustContent, getEffectiveSetupRunPolicy } from '../effective-hook-config' -import { isENOENT } from '../ipc/filesystem-path-containment' +import { isENOENT } from '../ipc/filesystem/filesystem-path-containment' import { isFolderRepo } from '../../shared/repo-kind' import { inspectSetupScriptImportCandidates } from '../../shared/setup-script-imports' import { joinWorktreeRelativePath } from './runtime-relative-paths' diff --git a/src/main/runtime/runtime-repository-registration-controller.ts b/src/main/runtime/runtime-repository-registration-controller.ts index 7ef630970ee..dab5b01ef93 100644 --- a/src/main/runtime/runtime-repository-registration-controller.ts +++ b/src/main/runtime/runtime-repository-registration-controller.ts @@ -11,7 +11,7 @@ import type { Repo } from '../../shared/repo-types' import { gitExecFileAsync, awaitWindowsHostGitEnvironmentReady } from '../git/runner' import { getRepoName, inspectGitRepoForRegistrationAsync } from '../git/repo' import { invalidateAuthorizedRootsCache } from '../ipc/registered-worktree-roots-cache' -import { isENOENT } from '../ipc/filesystem-path-containment' +import { isENOENT } from '../ipc/filesystem/filesystem-path-containment' import { findProjectAtSameRealPath } from '../project-realpath-identity' import { detectRepoIconAndUpstream } from '../repo-icon-autodetect' import { prepareLocalWorktreeRootForRepo } from '../worktree-root-preparation' diff --git a/src/main/runtime/runtime-search-line-fragments.test.ts b/src/main/runtime/runtime-search-line-fragments.test.ts index 4451a1e9960..230d94d7208 100644 --- a/src/main/runtime/runtime-search-line-fragments.test.ts +++ b/src/main/runtime/runtime-search-line-fragments.test.ts @@ -14,7 +14,7 @@ vi.mock( './file-watcher-host', async () => (await import('./orca-runtime-files-mock-registry')).fileWatcherHostMock ) -vi.mock('../ipc/filesystem-auth', async () => +vi.mock('../ipc/filesystem/filesystem-auth', async () => (await import('./orca-runtime-files-mock-registry')).filesystemAuthModuleMock() ) vi.mock('../git/runner', async () => diff --git a/src/main/runtime/runtime-worktree-filesystem.ts b/src/main/runtime/runtime-worktree-filesystem.ts index 423c8401081..c59640577c3 100644 --- a/src/main/runtime/runtime-worktree-filesystem.ts +++ b/src/main/runtime/runtime-worktree-filesystem.ts @@ -3,7 +3,7 @@ import { stat } from 'node:fs/promises' import { FOLDER_WORKSPACE_INSTANCE_SEPARATOR } from '../../shared/worktree/id' import type { Repo } from '../../shared/repo-types' import type { Worktree } from '../../shared/worktree/types' -import { isENOENT } from '../ipc/filesystem-path-containment' +import { isENOENT } from '../ipc/filesystem/filesystem-path-containment' import { getLocalWorktreePathAccess, toLocalWorktreeRuntimePath diff --git a/src/main/runtime/terminal-kitty-file-read.ts b/src/main/runtime/terminal-kitty-file-read.ts index 0fc9cc1f91f..05aed6502e6 100644 --- a/src/main/runtime/terminal-kitty-file-read.ts +++ b/src/main/runtime/terminal-kitty-file-read.ts @@ -1,7 +1,7 @@ import { constants } from 'node:fs' import { lstat, open, realpath } from 'node:fs/promises' import { isAbsolute, win32 } from 'node:path' -import { isDescendantOrEqual } from '../ipc/filesystem-path-containment' +import { isDescendantOrEqual } from '../ipc/filesystem/filesystem-path-containment' import { parseWslUncPath, toLinuxPath, diff --git a/src/main/runtime/terminal/terminal-path-resolver.ts b/src/main/runtime/terminal/terminal-path-resolver.ts index 5fb72745093..3944eb77aea 100644 --- a/src/main/runtime/terminal/terminal-path-resolver.ts +++ b/src/main/runtime/terminal/terminal-path-resolver.ts @@ -8,7 +8,7 @@ import { homedir } from 'node:os' import { provenancePathCandidate, resolveTerminalAbsolutePath } from './terminal-file-path' import { requireRuntimeFileProvider, runtimeFileRouteForTarget } from '../files/file-target' import { isSafeMobileRelativePath } from '../files/mobile-file-kind' -import { isENOENT } from '../../ipc/filesystem-path-containment' +import { isENOENT } from '../../ipc/filesystem/filesystem-path-containment' import type { RuntimeFileCommandHost } from '../files/file-command-host' import type { RuntimeTerminalArtifacts } from './artifacts/terminal-artifacts' diff --git a/src/main/window/clipboard-ipc-handlers.test.ts b/src/main/window/clipboard-ipc-handlers.test.ts index fcdcbd7d911..44df26a2433 100644 --- a/src/main/window/clipboard-ipc-handlers.test.ts +++ b/src/main/window/clipboard-ipc-handlers.test.ts @@ -73,7 +73,7 @@ vi.mock('node:fs/promises', () => ({ default: { writeFile: fsWriteFileMock, mkdir: fsMkdirMock } })) -vi.mock('../ipc/filesystem-auth', () => ({ +vi.mock('../ipc/filesystem/filesystem-auth', () => ({ PATH_ACCESS_DENIED_MESSAGE: 'Access denied: path resolves outside allowed directories. If this blocks a legitimate workflow, please file a GitHub issue.', resolveAuthorizedPath: resolveAuthorizedPathMock diff --git a/src/main/window/clipboard-ipc-handlers.ts b/src/main/window/clipboard-ipc-handlers.ts index 75be1ec1989..79ced264cf5 100644 --- a/src/main/window/clipboard-ipc-handlers.ts +++ b/src/main/window/clipboard-ipc-handlers.ts @@ -10,9 +10,9 @@ import { randomUUID } from 'node:crypto' import { open, stat } from 'node:fs/promises' import { runProcess } from '@orca/process-host' import type { Store } from '../persistence' -import { PATH_ACCESS_DENIED_MESSAGE } from '../ipc/filesystem-auth' +import { PATH_ACCESS_DENIED_MESSAGE } from '../ipc/filesystem/filesystem-auth' import { resolveDesktopAuthorizedPath } from '../ipc/local-file-access-resolution' -import { isENOENT } from '../ipc/filesystem-path-containment' +import { isENOENT } from '../ipc/filesystem/filesystem-path-containment' import { assertClipboardTextWriteWithinLimitWithYield, assertClipboardTextWithinLimitWithYield, diff --git a/src/main/window/clipboard-runtime-owned-ssh-paste.test.ts b/src/main/window/clipboard-runtime-owned-ssh-paste.test.ts index bfaaa627c1d..acf544ac1f6 100644 --- a/src/main/window/clipboard-runtime-owned-ssh-paste.test.ts +++ b/src/main/window/clipboard-runtime-owned-ssh-paste.test.ts @@ -40,7 +40,7 @@ vi.mock('node:fs/promises', () => ({ writeFile: fsWriteFileMock, default: { writeFile: fsWriteFileMock, mkdir: vi.fn() } })) -vi.mock('../ipc/filesystem-auth', () => ({ +vi.mock('../ipc/filesystem/filesystem-auth', () => ({ PATH_ACCESS_DENIED_MESSAGE: 'denied', resolveAuthorizedPath: vi.fn() })) diff --git a/src/main/worktree-prunable-git-file.ts b/src/main/worktree-prunable-git-file.ts index 74a487b2053..2db09ed1716 100644 --- a/src/main/worktree-prunable-git-file.ts +++ b/src/main/worktree-prunable-git-file.ts @@ -1,4 +1,4 @@ -import { isENOENT } from './ipc/filesystem-path-containment' +import { isENOENT } from './ipc/filesystem/filesystem-path-containment' import type { GitWorktreeInfo } from '../shared/worktree/types' import type { LocalWorktreeFilesystemOptions } from './local-worktree-filesystem' import { getLocalWorktreePathAccess, toLocalWorktreeRuntimePath } from './local-worktree-filesystem' diff --git a/src/renderer/src/runtime/remote-host-file-open-repro.test.ts b/src/renderer/src/runtime/remote-host-file-open-repro.test.ts index 78779f45824..b9756d7e076 100644 --- a/src/renderer/src/runtime/remote-host-file-open-repro.test.ts +++ b/src/renderer/src/runtime/remote-host-file-open-repro.test.ts @@ -12,7 +12,7 @@ import { shouldRetryFileLoadError } from '@/components/editor/useEditorPanelFile import { readRuntimeFileContent } from './runtime-file-read-client' import type { RuntimeClientTarget } from '../../../shared/runtime-client-target' -// Mirrors src/main/ipc/filesystem-auth.ts PATH_ACCESS_DENIED_MESSAGE. +// Mirrors src/main/ipc/filesystem/filesystem-auth.ts PATH_ACCESS_DENIED_MESSAGE. const PATH_ACCESS_DENIED_MESSAGE = 'Access denied: path resolves outside allowed directories. If this blocks a legitimate workflow, please file a GitHub issue.'