> ## Documentation Index
> Fetch the complete documentation index at: https://developers.arg.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Workspace files

> The fs namespace: read in any encoding, write with revision conditions, list, glob and search, watch for changes, and resolve stable file ids.

`fs` is the same object on every host. Paths are workspace paths: relative ones resolve beside the app or script, and `/` is the workspace root. On a folder served from disk with `arg serve`, or one the desktop app opened directly, `/` is that folder instead.

```ts theme={null}
import { fs } from "@arg-ai/sdk";

const text = await fs.read("./notes.md");
const rows = await fs.readJSON<Row[]>("./data.json");
const bytes = await fs.read("./logo.png", { encoding: "bytes" });
const file = await fs.readFile("./notes.md"); // { path, content, encoding, mimeType, size, revision, ... }
```

## Reading

| Method                                  | Returns                                                                                                         |
| --------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `read(path, options?)`                  | A string by default; `encoding: "bytes"`, `"arrayBuffer"`, `"base64"`, `"dataUrl"` or `"file"` change the shape |
| `readJSON<T>(path, options?)`           | The parsed document                                                                                             |
| `readBytes(path, options?)`             | A `Uint8Array`                                                                                                  |
| `readFile(path, options?)`              | The file with its metadata and current `revision`                                                               |
| `dataUrl(path)`                         | A `data:` URL with the bytes inline, for small images                                                           |
| `assetUrl(path)`                        | `{ url, expiresAt, size, contentType }`: a signed URL for media tags. It expires; it is not a share link        |
| `info(path, options?)` / `exists(path)` | Metadata (`null` when absent), or a boolean                                                                     |

Every read method has a `ById` twin (`readById`, `readJSONById`, `infoById`, `assetUrlById`, ...). Hosts cache one-shot reads for the life of a page run; pass `{ fresh: true }` to bypass the cache after you know a file changed.

## Writing

```ts theme={null}
await fs.write("./notes.md", "# Notes\n");
await fs.write("./logo.png", base64, { encoding: "base64" });
const { revision } = await fs.writeJSON("./state.json", { count: 1 });
```

Write, move, copy, `mkdir` and `remove` need write authority: **Read and write** in an app's grant, a non-read-only turn in an agent script, or an API key that can write. A refused write rejects with `read_only`.

### Conditional writes

Over HTTP - servers, CI, `arg serve` in account mode and agent scripts - a write can carry a condition, so two writers never silently overwrite each other:

```ts theme={null}
const current = await fs.readFile("/state.json");
await fs.writeJSON("/state.json", next, { baseRevision: current.revision! });
await fs.writeJSON("/lock.json", { owner: me.id }, { createOnly: true });
```

`baseRevision` writes only if the file still has that revision; `createOnly` writes only if the path does not exist. A failed condition rejects, and nothing is written. The in-Arg preview bridge does not implement conditions: it rejects them explicitly with `unsupported_capability` rather than dropping the condition and writing anyway.

## Listing, globbing, searching

```ts theme={null}
const entries = await fs.list("./data"); // ArgFsEntry[]: name, path, type, size, revision, timestamps, ids
const csvs = await fs.glob("**/*.csv", { cwd: "./data" });
const hits = await fs.search("TODO", { path: "./src", include: "*.ts" }); // [{ path, line, text }]
```

Listings are bounded by the transport: a server client stops after one storage page and rejects a truncated result rather than returning a partial list as if it were whole; `glob` walks at most 64 directories or 10,000 entries; a folder served from disk lists at most 1,000 entries. Under a folder-scoped grant, listings never leave the folder.

## Moving, copying, removing

```ts theme={null}
await fs.mkdir("./archive");
await fs.move("./notes.md", "./archive/notes.md");
await fs.copy("./archive/notes.md", "./notes-copy.md");
await fs.remove("./notes-copy.md"); // `delete` is an alias
```

Targets are literal file paths, not folders. In a cloud workspace a move keeps the file's id, its comments and its share links; that is why the SDK exposes a move rather than a read-write-remove sequence.

## Sharing with Arg users

`permissions` grants a user access through the workspace's own permission system. Pick a user id from `users.list()` and choose `read`, `write` or `manage`; `admin` is a workspace-only level and cannot be granted on a file or folder.

```ts theme={null}
import { permissions, users } from "@arg/sdk";

const teammate = (await users.list()).find(
  (member) => member.kind === "user" && member.name === "Ada",
);
if (!teammate) throw new Error("Ada is not in this workspace");

await permissions.shareFile("/reports/q3.md", {
  userId: teammate.id,
  permission: "write",
});
await permissions.shareFolder("/reports/archive", {
  userId: teammate.id,
  permission: "read",
});
```

The caller must be a user with `manage` access to the path, and the recipient must belong to the workspace organization. Folder grants flow to descendants. These methods use cloud workspace paths under `arg serve`, and are unavailable in an embedded app or a disk-only session.

## Stable ids

Cloud workspaces give every file an id that survives renames. `fs.getId(path)` returns it and `fs.resolveId(id)` returns the current path, or `null` once the file is gone. Store the id in anything that must outlive a rename - a link between two documents, a bookmark in app state - and read through the `ById` methods. Disk-only folders have no ids, so these methods report `unsupported_capability` there.

## Watching for changes

```ts theme={null}
const stop = fs.watch("./data.json", (event) => console.log(event.type, event.path), {
  emitInitial: true,
  intervalMs: 1000,
});
// Call stop() when the subscription is no longer needed.
```

`watch` returns a synchronous stop function. Inside Arg the host pushes changes; over HTTP the SDK polls metadata sequentially, never faster than every 250 ms, for as long as the process is alive. A watcher that throws does not stop the subscription. Dispose watchers on unmount; a client's `dispose()` stops every watcher it created.

## Opening in Arg

`fs.open(path)` and `fs.openById(id)` ask the Arg host to open the file in its own editor (the same call is exported as `host.open`). Only an Arg application host can do this; a server or an agent script reports `unsupported_capability`.

## Limits worth knowing

* File content read or written over HTTP is capped at 32 MiB per request; a folder served from disk caps content at 8 MiB. Use the advanced `files` resource of the [server client](/guides/sdk/server#advanced-file-transfers) for large or streamed transfers.
* A disk-served folder excludes hidden paths, symlinks and anything that is not a regular file; `copy` needs an unused destination, `move` may replace one, and `remove` refuses a non-empty directory.
* Backslashes and `#` are never valid in a workspace path.
