Files
orca/src/preload/api/filesystem-api.ts
Kelvin Amoaba 215197f46d fix(editor): live-update tabs reached through a symlinked folder (#41075)
The workspace watch never follows a symlinked folder, so a tab opened
through one (target outside the root) got no change events. Watch each
such tab's folder directly; main checks for the link with an lstat walk
and already rewrites the folder watch's events to the link spelling.
2026-11-11 08:12:54 +00:00

275 lines
10 KiB
TypeScript

import type { PathExistenceResult } from '../../shared/file-access/path-existence-batch'
import type { RuntimeFileReadChunkResult } from '../../shared/file-content/runtime-file-read'
import type { SearchOptions, SearchResult } from '../../shared/file-search/code-search-types'
import type { PdfExportResult } from '../../shared/pdf-export-failure-copy'
import type { DirEntry } from '../../shared/file-listing/directory-entry'
import type { FsChangedPayload } from '../../shared/file-watch/filesystem-change-event'
import type { MarkdownDocument } from '../../shared/file-listing/file-document'
import type {
ImportItemResult,
ResolveDroppedPathsResult
} from '../../shared/file-import/filesystem-import-result'
import type {
StagedRuntimeUploadSource as StagedExternalImportSource,
RuntimeUploadFileStreamRequest
} from '../../shared/file-import/runtime-upload-staging-contract'
import type {
LocalLogTailChangedPayload,
LocalLogTailReadArgs,
LocalLogTailReadResult,
LocalLogTailWatchArgs
} from '../../shared/local-log-tail-types'
import type { SshMutationExpectation } from '../../shared/ssh-types'
import type { LocalFileAccess } from '../../shared/file-access/local-file-access'
import type { RemoteFilePreviewAddress } from '../../shared/file-preview/remote-file-preview-address'
import type { FileBytesWritePrecondition } from '../../shared/file-write/file-bytes-write'
import type {
FileWriteDiskPrecondition,
FileWriteResult
} from '../../shared/file-write/file-write-disk-precondition'
import type {
CreateVenvResult,
KernelFrameEvent,
KernelStartResult,
PythonEnvironment,
PythonEnvironments
} from '../../shared/notebook-kernel-types'
import type {
AgentSessionAttachmentPathUploadResult,
AgentSessionAttachmentUploadTarget
} from '../../shared/agent-session/agent-session-attachments'
import type {
PrepareDroppedPathsRequest,
PreparedDroppedPaths
} from '../../shared/file-import/native-file-drop-preparation'
export type ExportApi = {
htmlToPdf: (args: { html: string; title: string }) => Promise<PdfExportResult>
}
export type FilesystemApi = {
fs: {
getPathForFile?: (file: File) => string
prepareDroppedPaths: (args: PrepareDroppedPathsRequest) => Promise<PreparedDroppedPaths>
readFileChunk: (args: {
filePath: string
connectionId?: string
access?: LocalFileAccess
offset: number
length: number
wakeWslDistro?: boolean
}) => Promise<RuntimeFileReadChunkResult>
readDir: (args: {
dirPath: string
connectionId?: string
followSymlinks?: boolean
/** A user act (open or Retry); may start a stopped WSL distro. */
wakeWslDistro?: boolean
access?: LocalFileAccess
}) => Promise<DirEntry[]>
readFile: (args: {
filePath: string
connectionId?: string
includeLocalLogMetadata?: boolean
access?: LocalFileAccess
/** A user act (open, expand, Retry); may start a stopped WSL distro. */
wakeWslDistro?: boolean
/** A viewer that reads images and PDFs by URL; other readers keep their base64. */
rangePreview?: boolean
}) => Promise<{
content: string
isBinary: boolean
mediaUrl?: string
size?: number
isImage?: boolean
mimeType?: string
fileIdentity?: string
}>
/** A PDF preview's bytes, for the renderer that minted its orca-media URL only. */
readPreviewRange: (args: { url: string; offset: number; length: number }) => Promise<Uint8Array>
grantRuntimeFilePreview: (args: RemoteFilePreviewAddress) => Promise<{
content: string
isBinary: true
mimeType: string
mediaUrl: string
isImage?: true
size?: number
} | null>
readLocalLogTail: (args: LocalLogTailReadArgs) => Promise<LocalLogTailReadResult>
startLocalLogTail: (args: LocalLogTailWatchArgs) => Promise<void>
stopLocalLogTail: (args: { subscriptionId: string }) => Promise<void>
onLocalLogTailChanged: (callback: (payload: LocalLogTailChangedPayload) => void) => () => void
downloadFile: (args: {
filePath: string
connectionId: string
}) => Promise<{ canceled: true } | { canceled: false; destinationPath: string }>
downloadFolder: (args: {
dirPath: string
connectionId: string
}) => Promise<{ canceled: true } | { canceled: false; destinationPath: string }>
saveDownloadedFile: (args: {
suggestedName: string
content: string
encoding: 'utf8' | 'base64'
}) => Promise<{ canceled: true } | { canceled: false; destinationPath: string }>
startDownloadedFile: (args: {
suggestedName: string
}) => Promise<
{ canceled: true } | { canceled: false; transferId: string; destinationPath: string }
>
appendDownloadedFileChunk: (args: {
transferId: string
contentBase64: string
}) => Promise<{ ok: true }>
finishDownloadedFile: (args: {
transferId: string
}) => Promise<{ canceled: false; destinationPath: string }>
cancelDownloadedFile: (args: { transferId: string }) => Promise<{ ok: true }>
listMarkdownDocuments: (args: {
rootPath: string
connectionId?: string
}) => Promise<MarkdownDocument[]>
writeFile: (
args: {
filePath: string
content: string
connectionId?: string
access?: LocalFileAccess
diskPrecondition?: FileWriteDiskPrecondition
} & SshMutationExpectation
) => Promise<FileWriteResult>
/** Atomic replacement of a binary file; absent where only text saves are served. */
writeFileBytes?: (
args: {
filePath: string
bytes: Uint8Array
connectionId?: string
access?: LocalFileAccess
diskPrecondition: FileBytesWritePrecondition
} & SshMutationExpectation
) => Promise<FileWriteResult>
createFile: (
args: {
filePath: string
connectionId?: string
} & SshMutationExpectation
) => Promise<void>
createDir: (
args: {
dirPath: string
connectionId?: string
followSymlinks?: boolean
} & SshMutationExpectation
) => Promise<void>
rename: (
args: {
oldPath: string
newPath: string
connectionId?: string
access?: LocalFileAccess
} & SshMutationExpectation
) => Promise<void>
copy: (
args: {
sourcePath: string
destinationPath: string
connectionId?: string
} & SshMutationExpectation
) => Promise<void>
deletePath: (
args: {
targetPath: string
connectionId?: string
recursive?: boolean
permanent?: boolean
} & SshMutationExpectation
) => Promise<void>
stat: (args: {
filePath: string
connectionId?: string
access?: LocalFileAccess
wakeWslDistro?: boolean
}) => Promise<{ size: number; isDirectory: boolean; mtime: number }>
/** Local only: whether a symlink below `rootPath` leads to `path` (the root watch can't see past it). */
hasSymlinkBelowRoot: (args: { rootPath: string; path: string }) => Promise<boolean>
pathsExist?: (args: {
filePaths: string[]
connectionId?: string
}) => Promise<PathExistenceResult[]>
pathExists: (args: {
filePath: string
connectionId?: string
access?: LocalFileAccess
}) => Promise<boolean>
listFiles: (args: {
rootPath: string
connectionId?: string
excludePaths?: string[]
requestToken?: string
maxResults?: number
searchQuery?: string
candidatePaths?: string[]
includeIgnored?: boolean
allowLegacyIncludeIgnored?: boolean
followSymlinks?: boolean
nameFilter?: string
}) => Promise<string[]>
cancelListFiles: (args: { requestToken: string }) => Promise<void>
cancelSearch: (args: { requestToken: string }) => Promise<void>
search: (
args: SearchOptions & { connectionId?: string; requestToken?: string }
) => Promise<SearchResult>
importExternalPaths: (
args: {
sourcePaths: string[]
destDir: string
connectionId?: string
ensureDir?: boolean
access?: LocalFileAccess
} & SshMutationExpectation
) => Promise<{ results: ImportItemResult[] }>
stageExternalPathsForRuntimeUpload: (args: {
sourcePaths: string[]
nameExtensionlessImages?: boolean
}) => Promise<{ sources: StagedExternalImportSource[] }>
uploadExternalFileToRuntime: (
args: RuntimeUploadFileStreamRequest
) => Promise<{ byteLength: number }>
resolveDroppedPathsForAgent: (
args: {
paths: string[]
worktreePath: string
connectionId?: string
} & SshMutationExpectation
) => Promise<ResolveDroppedPathsResult>
/** Uploads client-local files to the paired server owning the chat. */
uploadPathsToAgentSessionAttachments: (
args: AgentSessionAttachmentUploadTarget & { paths: string[] }
) => Promise<AgentSessionAttachmentPathUploadResult>
watchWorktree: (args: { worktreePath: string; connectionId?: string }) => Promise<void>
unwatchWorktree: (args: { worktreePath: string; connectionId?: string }) => Promise<void>
onFsChanged: (callback: (payload: FsChangedPayload) => void) => () => void
}
notebook: {
listPythonEnvironments: (args: {
filePath: string
rootPath: string | null
/** False until the notebook is trusted: workspace envs are then listed without running them. */
runWorkspaceInterpreters: boolean
}) => Promise<PythonEnvironments>
describePython: (args: { path: string }) => Promise<PythonEnvironment | null>
startKernel: (args: { filePath: string; python: string }) => Promise<KernelStartResult>
installIpykernel: (args: { python: string }) => Promise<{ ok: boolean; detail: string }>
createVenv: (args: {
filePath: string
rootPath: string | null
python: string
}) => Promise<CreateVenvResult>
execute: (args: { filePath: string; code: string }) => Promise<void>
interrupt: (args: { filePath: string }) => Promise<void>
shutdownKernel: (args: { filePath: string }) => Promise<void>
onKernelFrame: (callback: (event: KernelFrameEvent) => void) => () => void
}
export: ExportApi
}