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
This commit is contained in:
Neil
2026-10-25 13:40:31 -07:00
committed by GitHub
parent 843e502a46
commit d4cab5c567
95 changed files with 317 additions and 286 deletions
+1 -1
View File
@@ -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
@@ -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
+4 -4
View File
@@ -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."
}
+13 -13
View File
@@ -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,
+1 -1
View File
@@ -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(
@@ -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'
@@ -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<typeof RepoWorktrees>('../repo-worktrees')
vi.mock('../../repo-worktrees', async () => {
const actual = await vi.importActual<typeof RepoWorktrees>('../../repo-worktrees')
return { ...actual, listRepoWorktreeGraph: vi.fn(async () => []) }
})
vi.mock('../../shared/project-groups', async () => {
const actual = await vi.importActual<typeof ProjectGroupsModule>('../../shared/project-groups')
vi.mock('../../../shared/project-groups', async () => {
const actual = await vi.importActual<typeof ProjectGroupsModule>('../../../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<typeof CrossPlatformPathModule>(
'../../shared/cross-platform-path'
'../../../shared/cross-platform-path'
)
return { ...actual, isPathInsideOrEqual: vi.fn(actual.isPathInsideOrEqual) }
})
@@ -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<RuntimeStore, 'getRepos'> &
Partial<Pick<RuntimeStore, 'getProjectGroups' | 'getFolderWorkspaces'>>
@@ -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<typeof RepoWorktrees>('../repo-worktrees')
vi.mock('../../repo-worktrees', async () => {
const actual = await vi.importActual<typeof RepoWorktrees>('../../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()
}
})
@@ -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. */
@@ -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
}))
@@ -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()
}))
@@ -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')
@@ -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'
@@ -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'
@@ -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,
@@ -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'
@@ -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,
@@ -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 =
@@ -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')
@@ -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.
@@ -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<typeof GitRunner>()),
wslAwareSpawn: wslAwareSpawnMock
}))
@@ -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<typeof BundledRipgrepPath>()),
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"
@@ -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<string>()
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)
@@ -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)
}
}
}
@@ -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'
@@ -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'
@@ -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 {
@@ -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 () => {
@@ -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'
@@ -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<RuntimeImportLimitsModule>()),
REMOTE_IMPORT_MAX_FILE_BYTES: 4 * 1024,
REMOTE_IMPORT_MAX_TOTAL_BYTES: 16 * 1024
@@ -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 {}
@@ -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<typeof GitRunner>()),
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<typeof BundledRipgrepPath>()),
bundledRipgrepCommand: bundledRipgrepCommandMock
}))
vi.mock('./local-worktree-runtime-options', () => ({
vi.mock('../local-worktree-runtime-options', () => ({
getLocalGitOptionsForRegisteredWorktree: getLocalGitOptionsForRegisteredWorktreeMock
}))
@@ -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[]
@@ -1,5 +1,5 @@
import { ipcMain } from 'electron'
import { listQuickOpenFiles } from '../filesystem-list-files'
import { listQuickOpenFiles } from './filesystem-list-files'
import {
isFileNameFilterQueryTooLarge,
pathMatchesFileNameFilterTokens,
@@ -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<typeof BundledRipgrepPath>()),
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
@@ -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 = {
@@ -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'
@@ -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,
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -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,
+2 -2
View File
@@ -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 {
@@ -0,0 +1,14 @@
import { isDescendantOrEqual } from './filesystem/filesystem-path-containment'
export function findRegisteredWorktreeRoot(
targetPath: string,
registeredWorktreeRoots: Iterable<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
}
@@ -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,
@@ -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,
@@ -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,
@@ -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
}
@@ -35,7 +35,7 @@ vi.mock('./runtime-environments/runtime-environment-transport-routing', () => ({
callRuntimeEnvironment: (...args: Parameters<typeof callRuntimeEnvironment>) =>
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<RuntimeImportLimitsModule>()),
REMOTE_IMPORT_MAX_FILE_BYTES: 2 * 1024 * 1024
@@ -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')
@@ -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.
@@ -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'
+1 -1
View File
@@ -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,
@@ -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,
@@ -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'
@@ -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'
+1 -1
View File
@@ -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,
@@ -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,
+2 -2
View File
@@ -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,
+1 -1
View File
@@ -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'
@@ -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 () =>
@@ -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 () =>
@@ -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<typeof FilesystemAuth>('../ipc/filesystem-auth')
const actual = await vi.importActual<typeof FilesystemAuth>('../ipc/filesystem/filesystem-auth')
return {
...actual,
resolveAuthorizedPath: resolveAuthorizedPathMock
@@ -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 () =>
@@ -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 () =>
@@ -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 () =>
@@ -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 () =>
@@ -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 () =>
@@ -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 () =>
@@ -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 () =>
@@ -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 () =>
@@ -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 () =>
@@ -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<typeof FilesystemAuth>('../ipc/filesystem-auth')
vi.mock('../ipc/filesystem/filesystem-auth', async () => {
const actual = await vi.importActual<typeof FilesystemAuth>('../ipc/filesystem/filesystem-auth')
return {
...actual,
resolveAuthorizedPath: resolveAuthorizedPathMock
@@ -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 () =>
@@ -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<typeof FilesystemPathContainmentModule>(
'../../ipc/filesystem-path-containment'
'../../ipc/filesystem/filesystem-path-containment'
)
return { isENOENT: actual.isENOENT }
})
@@ -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()
@@ -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,
@@ -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'
@@ -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'
@@ -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 () =>
@@ -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
+1 -1
View File
@@ -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,
@@ -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'
@@ -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
+2 -2
View File
@@ -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,
@@ -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()
}))
+1 -1
View File
@@ -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'
@@ -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.'