---
name: oblien-files
description: >-
  Oblien workspace file operations - list, read, write, stream, stat, mkdir, and delete files via the Runtime API on port 9990
license: MIT
compatibility:
  - claude-code
  - cursor
  - openclaw
  - goose
  - jetbrains-ai
metadata:
  author: oblien
  version: 1.0.0
  source: https://oblien.com/docs
---

# Files

The file system endpoints let you list, read, write, and delete files inside the workspace VM. All paths are absolute filesystem paths (e.g. `/app/src/main.go`).

> **Note:** Requires the internal server to be [enabled](/docs/runtime-api/server-setup#enable-the-server). All requests require a valid token - see [Connection & Auth](/docs/runtime-api/server-setup).


## List directory

List files and directories in a given path. Supports recursive traversal, content inclusion, hash computation, and filtering.


**SDK:**

```typescript
const rt = await client.workspaces.runtime('ws_a1b2c3d4');

const result = await rt.files.list({
  dirPath: '/app/src',
  nested: true,
  flatten: true,
  includeContent: true,
  codeFilesOnly: true,
  maxDepth: 5,
});

console.log(result.entries);       // fileEntry[]
console.log(result.count);         // number of entries
```


**REST API:**

```http
GET https://workspace.oblien.com/files?path=/app/src&nested=true&flatten=true&include_content=true&code_files_only=true&max_depth=5
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```


**cURL:**

```bash
curl "https://workspace.oblien.com/files?path=/app/src&nested=true&flatten=true&include_content=true&code_files_only=true&max_depth=5" \
  -H "Authorization: Bearer $GATEWAY_JWT"
```


### Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `path` | `string` | No | Directory path to list. Defaults to `/` |
| `nested` | `boolean` | No | Recurse into subdirectories. Default `false` |
| `flatten` | `boolean` | No | Return flat list instead of tree. Default `false` |
| `light` | `boolean` | No | Omit size and modified time for faster response. Default `false` |
| `include_hash` | `boolean` | No | Include SHA-256 hash for each file. Default `false` |
| `include_content` | `boolean` | No | Include file content inline. Default `false` |
| `include_extensions` | `boolean` | No | Include file extension field. Default `false` |
| `code_files_only` | `boolean` | No | Only return code/config files. Default `false` |
| `use_gitignore` | `boolean` | No | Respect `.gitignore` rules. Default `true` |
| `max_depth` | `integer` | No | Maximum recursion depth. Default `20` |
| `path_filter` | `string` | No | Case-insensitive substring filter on path |
| `include_ext` | `string` | No | Comma-separated extensions to include (e.g. `js,ts,go`) |
| `ignore_patterns` | `string` | No | Comma-separated glob patterns to ignore |
| `max_content_budget` | `integer` | No | Max total bytes for inline content. Default ~50 MiB |

### Response

```json
{
  "success": true,
  "path": "/app/src",
  "entries": [
    {
      "name": "main.go",
      "path": "/app/src/main.go",
      "type": "file",
      "size": 1234,
      "modified": "2025-01-15T10:30:00Z",
      "extension": ".go",
      "content": "package main\n...",
      "hash": "a1b2c3..."
    },
    {
      "name": "utils",
      "path": "/app/src/utils",
      "type": "directory",
      "children": [...]
    }
  ],
  "count": 42
}
```

> **Warning:** The list endpoint is capped at **50,000 entries**. For large directories, use `path_filter`, `include_ext`, or `code_files_only` to narrow results, or use the [stream endpoint](#stream-directory) for NDJSON streaming.


---

## Stream directory

Stream directory entries as **NDJSON** (newline-delimited JSON). Ideal for large directories - entries flow to the client as they're discovered, without accumulating in memory.


**SDK:**

```typescript
for await (const entry of rt.files.stream({
  dirPath: '/app',
  includeContent: true,
  codeFilesOnly: true,
})) {
  console.log(entry.name, entry.path);
}
```


**REST API:**

```http
GET https://workspace.oblien.com/files/stream?path=/app&include_content=true&code_files_only=true
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```

Response: `Content-Type: application/x-ndjson`


**cURL:**

```bash
curl -N "https://workspace.oblien.com/files/stream?path=/app&include_content=true&code_files_only=true" \
  -H "Authorization: Bearer $GATEWAY_JWT"
```


### Parameters

Same as [List directory](#list-directory). The `nested` and `flatten` options are always enabled for streaming.

### Response format

Each line is a JSON object. The stream starts with a `start` event and ends with a `done` event:

```jsonl
{"event":"start","path":"/app"}
{"name":"main.go","path":"/app/main.go","type":"file","size":1234}
{"name":"utils.go","path":"/app/utils.go","type":"file","size":567}
{"event":"done","count":2}
```

> **Note:** The stream endpoint uses **batched directory reads** for memory efficiency. Entries are not sorted - they arrive in filesystem order. Use the list endpoint if you need sorted output.


---

## Read file

Read the content of a file. Supports line ranges for partial reads.


**SDK:**

```typescript
const file = await rt.files.read({
  filePath: '/app/src/main.go',
});

console.log(file.content);    // file content as string
console.log(file.lines);      // number of lines returned
console.log(file.size);       // file size in bytes

// Read specific line range
const partial = await rt.files.read({
  filePath: '/app/src/main.go',
  startLine: 10,
  endLine: 25,
  withLineNumbers: true,
});
```


**REST API:**

```http
GET https://workspace.oblien.com/files/read?path=/app/src/main.go&start_line=10&end_line=25&with_line_numbers=true
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```


**cURL:**

```bash
curl "https://workspace.oblien.com/files/read?path=/app/src/main.go&start_line=10&end_line=25&with_line_numbers=true" \
  -H "Authorization: Bearer $GATEWAY_JWT"
```


### Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `path` | `string` | Yes | Absolute path to the file |
| `start_line` | `integer` | No | First line to read (1-based) |
| `end_line` | `integer` | No | Last line to read (1-based, inclusive) |
| `with_line_numbers` | `boolean` | No | Prefix each line with its line number |

### Response

```json
{
  "success": true,
  "path": "/app/src/main.go",
  "content": "package main\n\nfunc main() {\n\tfmt.Println(\"hello\")\n}",
  "size": 1234,
  "lines": 5,
  "extension": ".go",
  "start_line": 10,
  "end_line": 25
}
```

`start_line` and `end_line` are only included when a line range was requested.

---

## Write file

Create or overwrite a file. Uses atomic write (temp file + rename) by default. Accepts both `POST` and `PUT`.


**SDK:**

```typescript
const result = await rt.files.write({
  fullPath: '/app/src/hello.txt',
  content: 'Hello, world!',
  createDirs: true,
});

console.log(result.path);  // "/app/src/hello.txt"
console.log(result.size);  // 13

// Append to an existing file
await rt.files.write({
  fullPath: '/app/logs/output.log',
  content: 'New log entry\n',
  append: true,
  createDirs: true,
});
```


**REST API:**

```http
POST https://workspace.oblien.com/files/write
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json

{
  "path": "/app/src/hello.txt",
  "content": "Hello, world!",
  "create_dirs": true
}
```


**cURL:**

```bash
curl -X POST "https://workspace.oblien.com/files/write" \
  -H "Authorization: Bearer $GATEWAY_JWT" \
  -H "Content-Type: application/json" \
  -d '{"path":"/app/src/hello.txt","content":"Hello, world!","create_dirs":true}'
```


### Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `path` | `string` | Yes | Absolute path for the file |
| `content` | `string` | Yes | File content |
| `create_dirs` | `boolean` | No | Create parent directories if they don't exist. Default `false` |
| `append` | `boolean` | No | Append to existing file instead of overwriting. Default `false` |
| `mode` | `string` | No | File permissions in octal (e.g. `"0644"`). Default `"0644"` |

### Response

```json
{
  "success": true,
  "path": "/app/src/hello.txt",
  "size": 13
}
```

HTTP status: `201 Created`

---

## Create directory

Create a directory and any necessary parent directories.


**SDK:**

```typescript
await rt.files.mkdir({
  path: '/app/src/utils/helpers',
});
```


**REST API:**

```http
POST https://workspace.oblien.com/files/mkdir
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json

{
  "path": "/app/src/utils/helpers"
}
```


**cURL:**

```bash
curl -X POST "https://workspace.oblien.com/files/mkdir" \
  -H "Authorization: Bearer $GATEWAY_JWT" \
  -H "Content-Type: application/json" \
  -d '{"path":"/app/src/utils/helpers"}'
```


### Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `path` | `string` | Yes | Directory path to create |
| `mode` | `string` | No | Directory permissions in octal (e.g. `"0755"`). Default `"0755"` |

### Response

```json
{
  "success": true,
  "path": "/app/src/utils/helpers"
}
```

HTTP status: `201 Created`

---

## Stat

Get detailed information about a file or directory.


**SDK:**

```typescript
const info = await rt.files.stat({
  path: '/app/src/main.go',
});

console.log(info.type);        // "file"
console.log(info.size);        // 1234
console.log(info.permissions); // "0644"
console.log(info.is_code);     // true
```


**REST API:**

```http
GET https://workspace.oblien.com/files/stat?path=/app/src/main.go
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```


**cURL:**

```bash
curl "https://workspace.oblien.com/files/stat?path=/app/src/main.go" \
  -H "Authorization: Bearer $GATEWAY_JWT"
```


### Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `path` | `string` | Yes | Path to the file or directory |

### Response

```json
{
  "success": true,
  "path": "/app/src/main.go",
  "name": "main.go",
  "type": "file",
  "size": 1234,
  "modified": "2025-01-15T10:30:00Z",
  "permissions": "0644",
  "is_code": true,
  "extension": ".go"
}
```

For symlinks:

```json
{
  "success": true,
  "path": "/app/link",
  "name": "link",
  "type": "symlink",
  "size": 0,
  "modified": "2025-01-15T10:30:00Z",
  "permissions": "0777",
  "is_code": false,
  "symlink_target": "/app/src/main.go"
}
```

---

## Delete

Delete a file or directory. Directories are removed recursively.


**SDK:**

```typescript
await rt.files.delete({
  path: '/app/src/old-file.txt',
});
```


**REST API:**

```http
DELETE https://workspace.oblien.com/files/delete?path=/app/src/old-file.txt
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```


**cURL:**

```bash
curl -X DELETE "https://workspace.oblien.com/files/delete?path=/app/src/old-file.txt" \
  -H "Authorization: Bearer $GATEWAY_JWT"
```


### Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `path` | `string` | Yes | Path to the file or directory to delete |

The path can also be provided in the request body as `{"path": "..."}`.

### Response

```json
{
  "success": true,
  "path": "/app/src/old-file.txt"
}
```

> **Warning:** System paths (`/`, `/bin`, `/sbin`, `/usr`, `/lib`, `/lib64`, `/etc`, `/dev`, `/proc`, `/sys`, `/boot`, `/run`) are protected and cannot be deleted.


---

## Error responses

All file endpoints return errors in a consistent format:

```json
{
  "error": "file not found: /app/missing.txt"
}
```

| Status | Meaning |
|--------|---------|
| `400` | Invalid parameters (missing path, path is a directory when file expected, etc.) |
| `401` | Missing or invalid token |
| `403` | Attempted to delete a protected system path |
| `404` | File or directory not found |
| `413` | File too large to read or content too large to write |
| `500` | Internal server error |

---

## File Transfer

Stream files to and from the workspace using tar.gz archives. Supports multiple paths and exclude patterns — ideal for bulk download/upload.

### Download

Download files and directories as a streaming tar.gz archive.


**SDK:**

```ts
const rt = await client.workspaces.runtime('ws_a1b2c3d4');

const response = await rt.transfer.download({
  paths: ['/app/src', '/app/package.json'],
  excludePatterns: ['node_modules', '.git'],
});

// Save the tar.gz stream to a file (Node.js)


const dest = createWriteStream('backup.tar.gz');
Readable.fromWeb(response.body).pipe(dest);
```


**REST API:**

```http
POST https://workspace.oblien.com/files/transfer/download
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json

{
  "paths": ["/app/src", "/app/package.json"],
  "exclude_patterns": ["node_modules", ".git"]
}
```


**cURL:**

```bash
curl -X POST "https://workspace.oblien.com/files/transfer/download" \
  -H "Authorization: Bearer $GATEWAY_JWT" \
  -H "Content-Type: application/json" \
  -d '{ "paths": ["/app/src"], "exclude_patterns": ["node_modules"] }' \
  -o backup.tar.gz
```


#### Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `paths` | `string[]` | **Yes** | Absolute paths to include in the archive |
| `exclude_patterns` | `string[]` | No | Glob patterns for filenames to exclude |

#### Response

The response body is a streaming `application/gzip` tar archive (`Content-Type: application/gzip`).

---

### Upload

Upload a tar.gz archive and extract it into the workspace.


**SDK:**

```ts

const rt = await client.workspaces.runtime('ws_a1b2c3d4');

const tarData = readFileSync('project.tar.gz');
const result = await rt.transfer.upload({
  body: tarData,
  dest: '/app',
});
console.log(result.files_extracted); // number of files extracted
```


**REST API:**

```http
POST https://workspace.oblien.com/files/transfer/upload?dest=/app
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/gzip

<raw tar.gz binary data>
```


**cURL:**

```bash
curl -X POST "https://workspace.oblien.com/files/transfer/upload?dest=/app" \
  -H "Authorization: Bearer $GATEWAY_JWT" \
  -H "Content-Type: application/gzip" \
  --data-binary @project.tar.gz
```


#### Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `dest` | `string` (query) | No | Destination directory for extraction. Default: `/` |

#### Response

```json
{
  "success": true,
  "dest": "/app",
  "files_extracted": 42
}
```

---

### CLI Push / Pull

The CLI provides `push` and `pull` shortcuts for file transfer:

```bash
# Push local files into the workspace
oblien files push ws_abc123 ./src ./package.json --dest /app

# Pull workspace files to local machine
oblien files pull ws_abc123 /app/src /app/config.json --dest ./backup

# Exclude patterns on pull
oblien files pull ws_abc123 /app --dest ./backup --exclude node_modules --exclude .git
```
