Skip to content

Latest commit

 

History

History
265 lines (198 loc) · 4.57 KB

File metadata and controls

265 lines (198 loc) · 4.57 KB

Remote Sandbox Shim Surface

This is the final state I would target for the remote filesystem shim.

Summary:

  • listDirectory for directory listings
  • readFile for text and byte reads, including paged reads
  • getMetadata for path metadata and revision tokens
  • writeFile for plain and conflict-aware writes
  • createDirectory, deletePath, movePath, and copyPath for path mutations
  • searchFiles and searchContent for path and content search
  • watchPath for filesystem change events

Core API

listDirectory

Returns the direct children of a directory.

listDirectory({ absolutePath })

Returns:

{
  entries: Array<{
    absolutePath: string
    name: string
    kind: "file" | "directory" | "symlink" | "other"
  }>
}

readFile

Reads a file as text or bytes. offset and maxBytes support paged reads. Should return an opaque revision token representing the version of the file that was read.

readFile({ absolutePath, offset, maxBytes, encoding })

Returns:

{
  kind: "text" | "bytes"
  content: string | Uint8Array
  byteLength: number
  exceededLimit: boolean
  revision: string
}

If exceededLimit is true, more data is available after the returned chunk and the client can continue reading by calling readFile again with a larger offset.

getMetadata

Returns file metadata, or null if the path does not exist. Should return an opaque revision token representing the current version of the path.

getMetadata({ absolutePath })

Returns:

null | {
  absolutePath: string
  kind: "file" | "directory" | "symlink" | "other"
  size: number | null
  createdAt: string | null
  modifiedAt: string | null
  accessedAt: string | null
  mode?: number | null
  permissions?: string | null
  owner?: string | null
  group?: string | null
  symlinkTarget?: string | null
  revision: string
}

writeFile

Writes file contents. ifMatch is the recommended conflict-aware write mechanism. Use an opaque revision token from readFile or getMetadata. A revision is a freshness token for the version of the file the client last observed. Without a precondition, this should act as a plain overwrite-or-create write.

writeFile({
  absolutePath,
  content,
  encoding,
  precondition: {
    ifMatch: revision,
  },
})

Returns:

| {
    ok: true
    revision: string
  }
| {
    ok: false
    reason: "conflict"
    currentRevision: string
  }

createDirectory

Creates a directory. File creation should happen through writeFile.

createDirectory({ absolutePath })

Returns:

{
  absolutePath: string
  kind: "directory"
}

deletePath

Deletes a path. permanent controls trash vs hard delete behavior.

deletePath({ absolutePath, permanent })

Returns:

{
  absolutePath: string
}

movePath

Moves a path. Rename is just a same-parent move.

movePath({ sourceAbsolutePath, destinationAbsolutePath })

Returns:

{
  fromAbsolutePath: string
  toAbsolutePath: string
}

copyPath

Copies a path.

copyPath({ sourceAbsolutePath, destinationAbsolutePath })

Returns:

{
  fromAbsolutePath: string
  toAbsolutePath: string
}

searchFiles

Searches file names and paths.

searchFiles({ query, includeHidden, includePattern, excludePattern, limit })

Returns:

{
  matches: Array<{
    absolutePath: string
    relativePath: string
    name: string
    kind: "file" | "directory" | "symlink" | "other"
    score: number
  }>
}

searchContent

Searches file contents and should return line/column-oriented matches.

searchContent({ query, includeHidden, includePattern, excludePattern, limit })

Returns:

{
  matches: Array<{
    absolutePath: string
    relativePath: string
    line: number
    column: number
    preview: string
  }>
}

watchPath

Subscribes to path changes.

watchPath({ absolutePath, recursive })

Yields:

{
  events: Array<{
    kind: "create" | "update" | "delete" | "rename" | "overflow"
    absolutePath: string
    oldAbsolutePath?: string
  }>
}

Search

Keep search as two distinct primitives:

  • searchFiles
  • searchContent

Do not collapse them into a single overloaded search(...).

They have different semantics, different cost profiles, and different result shapes.

Notes

  • The shim should be pure path-based
  • Workspace scoping should live in client logic, not in the remote filesystem interface
  • higher-level helpers like readWorkspaceDirectory or searchFilesMulti should stay above the shim layer