---
name: oblien-search
description: >-
  Oblien workspace search - content search with ripgrep and filename search 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
---

# Search

Two search modes are available inside the workspace:

- **Content search** - powered by [ripgrep](https://github.com/BurntSushi/ripgrep), searches file contents with regex, whole-word, and context line support
- **Filename search** - built-in filesystem search that matches against file paths

> **Note:** Requires the internal server to be [enabled](/docs/runtime-api/server-setup#enable-the-server). Content search requires ripgrep - use the [install endpoint](#install-ripgrep) to set it up.


---

## Content search

Search file contents using ripgrep. Results are grouped by file with line numbers and match context.


**SDK:**

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

const results = await rt.search.content({
  query: 'handleRequest',
  path: '/app/src',
  contextLines: 2,
  maxResults: 100,
});

for (const file of results.results) {
  console.log(file.path);
  for (const match of file.matches) {
    console.log(`  Line ${match.line}: ${match.text}`);
  }
}
```


**REST API:**

```http
GET https://workspace.oblien.com/files/search?q=handleRequest&path=/app/src&context_lines=2&max_results=100
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```


**cURL:**

```bash
curl "https://workspace.oblien.com/files/search?q=handleRequest&path=/app/src&context_lines=2&max_results=100" \
  -H "Authorization: Bearer $GATEWAY_JWT"
```


### Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `q` | `string` | Yes | Search query (max 1000 characters) |
| `path` | `string` | No | Directory or file to search in. Default `/` |
| `case_sensitive` | `boolean` | No | Case-sensitive matching. Default `false` |
| `regex` | `boolean` | No | Treat query as a regex pattern. Default `false` |
| `whole_word` | `boolean` | No | Match whole words only. Default `false` |
| `max_results` | `integer` | No | Maximum number of matches. Default `100` |
| `timeout` | `integer` | No | Timeout in seconds (max 60). Default `10` |
| `context_lines` | `integer` | No | Lines of context around matches (0–10). Default `0` |
| `file_types` | `string` | No | Comma-separated file extension filters (e.g. `go,js,py`). Converted to glob patterns internally |
| `include_hidden` | `boolean` | No | Include hidden files/directories. Default `false` |
| `no_gitignore` | `boolean` | No | Ignore `.gitignore` rules. Default `false` |
| `ignore_patterns` | `string` | No | Comma-separated glob patterns to skip |

### Response

Results are returned as an **object keyed by file path**, with each value being an array of matches:

```json
{
  "success": true,
  "query": "handleRequest",
  "path": "/app/src",
  "results": {
    "src/server.go": [
      {
        "line": 42,
        "column": 6,
        "text": "func handleRequest(w http.ResponseWriter, r *http.Request) {"
      },
      {
        "line": 105,
        "column": 2,
        "text": "\thandleRequest(w, r)"
      }
    ],
    "src/router.go": [
      {
        "line": 18,
        "column": 12,
        "text": "\trouter.HandleFunc(\"/\", handleRequest)"
      }
    ]
  },
  "total_matches": 3,
  "total_files": 2,
  "capped": false
}
```

| Field | Description |
|-------|-------------|
| `results` | Object mapping file paths to arrays of matches |
| `total_matches` | Total number of matching lines across all files |
| `total_files` | Number of files with at least one match |
| `capped` | `true` if `max_results` was reached before all matches were found |

---

## Filename search

Search for files by name. Uses a fast filesystem walk with substring matching on relative paths.


**SDK:**

```typescript
const results = await rt.search.files({
  query: 'controller',
  path: '/app/src',
  maxResults: 50,
});

console.log(results.files);        // ["auth/controller.go", "user/controller.go"]
console.log(results.total_files);  // 2
```


**REST API:**

```http
GET https://workspace.oblien.com/files/search/files?q=controller&path=/app/src&max_results=50
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```


**cURL:**

```bash
curl "https://workspace.oblien.com/files/search/files?q=controller&path=/app/src&max_results=50" \
  -H "Authorization: Bearer $GATEWAY_JWT"
```


### Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `q` | `string` | Yes | Search query - matches against relative file paths (max 1000 characters) |
| `path` | `string` | No | Directory to search in. Default `/` |
| `case_sensitive` | `boolean` | No | Case-sensitive matching. Default `false` |
| `include_hidden` | `boolean` | No | Include hidden files/directories. Default `false` |
| `max_results` | `integer` | No | Maximum number of results. Default `200` |
| `ignore_patterns` | `string` | No | Comma-separated glob patterns to skip |

### Response

```json
{
  "success": true,
  "query": "controller",
  "path": "/app/src",
  "files": [
    "auth/controller.go",
    "user/controller.go"
  ],
  "total_files": 2
}
```

> **Note:** Filename search does **not** require ripgrep - it works out of the box with no additional setup.


---

## Ripgrep status

Check if ripgrep is installed and available for content search.


**SDK:**

```typescript
const status = await rt.search.status();
console.log(status.installed); // true or false
console.log(status.version);   // "ripgrep 14.1.0" or null
```


**REST API:**

```http
GET https://workspace.oblien.com/files/search/init
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```


**cURL:**

```bash
curl "https://workspace.oblien.com/files/search/init" \
  -H "Authorization: Bearer $GATEWAY_JWT"
```


### Response

When ripgrep is installed:

```json
{
  "success": true,
  "installed": true,
  "path": "/usr/local/bin/rg",
  "version": "ripgrep 14.1.0"
}
```

When not installed:

```json
{
  "success": true,
  "installed": false,
  "message": "ripgrep is not installed - use POST /files/search/init to install"
}
```

---

## Install ripgrep

Download and install ripgrep from GitHub releases. This is a one-time setup per workspace.


**SDK:**

```typescript
const result = await rt.search.init();
console.log(result.installed); // true
console.log(result.version);   // "14.1.1"
```


**REST API:**

```http
POST https://workspace.oblien.com/files/search/init
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```


**cURL:**

```bash
curl -X POST "https://workspace.oblien.com/files/search/init" \
  -H "Authorization: Bearer $GATEWAY_JWT"
```


### Response

```json
{
  "success": true,
  "version": "ripgrep 14.1.0",
  "path": "/usr/local/bin/rg"
}
```

If ripgrep is already installed, the endpoint returns the current installation info without re-downloading.

> **Warning:** Installation requires internet access from inside the workspace to download from GitHub releases. The binary is installed to `/usr/local/bin/rg`.


---

## Error responses

| Status | Meaning |
|--------|---------|
| `400` | Missing `q` parameter, query too long, or path is not a directory |
| `401` | Missing or invalid token |
| `404` | Search path not found |
| `409` | Ripgrep installation already in progress |
| `503` | Content search requested but ripgrep is not installed |
| `504` | Search timed out |
