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

# Google Drive

> Mount Google Drive folders and files, including Docs, Sheets, and Slides, as a Mirage virtual filesystem.

The Google Drive resource exposes a Google Drive account as a virtual
filesystem mounted at some prefix such as `/gdrive/`.

For Google OAuth setup, see [Google Workspace Setup](/python/setup/google).

## Config

```python theme={null}
import os

from mirage import MountMode, Workspace
from mirage.resource.gdrive import GoogleDriveConfig, GoogleDriveResource

config = GoogleDriveConfig(
    client_id=os.environ["GOOGLE_CLIENT_ID"],
    client_secret=os.environ["GOOGLE_CLIENT_SECRET"],
    refresh_token=os.environ["GOOGLE_REFRESH_TOKEN"],
)
resource = GoogleDriveResource(config=config)
ws = Workspace({"/gdrive": resource}, mode=MountMode.WRITE)
```

To mount a subfolder instead of the whole Drive, set `folder_id`.
The mount root becomes that folder. The folder may live in My Drive,
be shared with you, sit inside a Shared Drive, or be a Shared Drive
id itself; the drive scope is resolved automatically. Scoped mounts
do not surface other shared drives:

```python theme={null}
config = GoogleDriveConfig(
    client_id=os.environ["GOOGLE_CLIENT_ID"],
    client_secret=os.environ["GOOGLE_CLIENT_SECRET"],
    refresh_token=os.environ["GOOGLE_REFRESH_TOKEN"],
    folder_id="1AbCdEfFolderId",
)
```

## Filesystem Layout

```text theme={null}
/gdrive/
  <folder>/
    <file>
    <subfolder>/
      ...
    <name>.gdoc.json
    <name>.gsheet.json
    <name>.gslide.json
  <shared-drive>/
    <file>
    <folder>/
```

Example:

```text theme={null}
/gdrive/
  Projects/
    spec.pdf
    roadmap.gsheet.json
    Budget/
      Q1.gsheet.json
      Q2.gsheet.json
  Notes/
    meeting.gdoc.json
  Presentations/
    quarterly_review.gslide.json
  Team Drive/
    shared-spec.pdf
  data.csv
  report.pdf
```

The mount mirrors the actual Google Drive folder hierarchy. The root
contains the Drive API `root` folder plus each Shared Drive visible to
the user. Shared Drives appear as top-level directories. Duplicate names
receive a `[Shared Drive]` suffix and, when needed, a numeric suffix.
Subfolders appear as directories, and regular files keep their original names.

### Synthetic Extensions

Google Workspace files cannot be downloaded as raw bytes, so they
are exposed with synthetic extensions and read via their respective
APIs:

| Extension      | Type          | Read via   |
| -------------- | ------------- | ---------- |
| `.gdoc.json`   | Google Docs   | Docs API   |
| `.gsheet.json` | Google Sheets | Sheets API |
| `.gslide.json` | Google Slides | Slides API |

Regular files (PDFs, images, CSVs, etc.) are downloaded directly
from Drive. Large regular files use streaming.

## Write Support

Under `MountMode.WRITE` the standard write commands work on regular
files and folders: `tee`, `cp`, `mv`, `rm`, `mkdir`, `rmdir`,
`touch`, `truncate`, and in-place editors such as `sed -i`. Semantics
follow the other object-store mounts (GNU check-then-act: `EEXIST`
on mkdir over an existing name, `mv` onto a non-empty directory
fails with `ENOTEMPTY`, `cp -r` merges).

Google-native files (`.gdoc.json`, `.gsheet.json`, `.gslide.json`)
are read-only as bytes; writing to them fails with `EACCES`. Mutate
them through the `gws` commands below instead.

Drive access is per-item (shared-drive roles, folder-level grants), so
a write mount can still hold items you may not edit. A mutation the
API denies fails with `EACCES` (Permission denied) on that operand,
like a real filesystem; the rest of the mount keeps working.

### Writing inside a Shared Drive

A Shared Drive is not a read-only corner of the mount. Every Drive call
mirage makes carries `supportsAllDrives`, so create, write, rename, copy
and delete work the same inside a Shared Drive as in My Drive, and the
commands above behave identically there.

What you may actually do is decided by Drive, not by mirage: a Shared
Drive has its own role model (`viewer`, `commenter`, `contributor`,
`content manager`, `manager`), and some organizations restrict deletion
or moving content out of the drive. mirage does not attempt to predict
those rules. It issues the operation and reports the answer, so a role
that forbids the change fails with `EACCES` on that operand, exactly like
an unwritable item in My Drive.

The practical consequence is that a write mount spanning Shared Drives is
partly writable, and the boundary follows your roles rather than the
mount. If you want a mount that cannot write at all, use
`MountMode.READ`; if you want to scope one to a single drive, set
`folder_id` to the Shared Drive id.

## Snapshots

The resource supports workspace snapshots. Recorded reads capture the
file's Drive revision (`headRevisionId`), and a loaded snapshot pins
reads to that revision via the Drive Revisions API, so replay serves
the exact bytes the agent saw.

## Cache

The Google Drive resource uses `IndexCacheStore` (same as Slack,
Gmail, and other resources). Index entries store folder IDs, file
IDs, and file metadata. There is no separate content cache -- file
content caching is handled by the workspace `IOResult` mechanism.

## Example

```python theme={null}
import asyncio
import os

from dotenv import load_dotenv

from mirage import MountMode, Workspace
from mirage.commands.cli.builtin.gws import GWS
from mirage.resource.gdrive import GoogleDriveConfig, GoogleDriveResource

load_dotenv(".env.development")

config = GoogleDriveConfig(
    client_id=os.environ["GOOGLE_CLIENT_ID"],
    client_secret=os.environ["GOOGLE_CLIENT_SECRET"],
    refresh_token=os.environ["GOOGLE_REFRESH_TOKEN"],
)
resource = GoogleDriveResource(config=config)


async def main():
    ws = Workspace({"/gdrive": resource}, mode=MountMode.WRITE)
    ws.register_cli("gws", GWS, config.model_dump())

    # List root
    r = await ws.execute("ls /gdrive/ | head -n 10")
    print(await r.stdout_str())

    # Browse a subfolder
    r = await ws.execute("ls /gdrive/Projects/")
    print(await r.stdout_str())

    # Read a regular file
    r = await ws.execute("cat /gdrive/Projects/spec.pdf | head -c 200")
    print(await r.stdout_str())

    # Read a Google Doc title
    r = await ws.execute('jq ".title" /gdrive/Notes/meeting.gdoc.json')
    print(await r.stdout_str())

    # Read a Google Sheet title
    r = await ws.execute(
        'jq ".properties.title" /gdrive/Projects/roadmap.gsheet.json')
    print(await r.stdout_str())

    # Read a Google Slides deck length
    r = await ws.execute(
        'jq ".slides | length"'
        " /gdrive/Presentations/quarterly_review.gslide.json")
    print(await r.stdout_str())

    # Search across all files
    r = await ws.execute('rg "quarterly" /gdrive/Projects/')
    print(await r.stdout_str())

    # Tree view
    r = await ws.execute("tree -L 2 /gdrive/")
    print(await r.stdout_str())

    # Create a new Google Doc
    r = await ws.execute(
        "gws docs documents create"
        ' --json \'{"title": "New Doc from MIRAGE"}\'')
    print(await r.stdout_str())


if __name__ == "__main__":
    asyncio.run(main())
```

See `examples/google/gdrive.py` for the full working example.

## Shell Commands

Standard commands available on the mounted Google Drive tree:

| Command         | Notes                                      |
| --------------- | ------------------------------------------ |
| `ls`            | List folders and files                     |
| `cat`           | Read file content (regular or Workspace)   |
| `head` / `tail` | First/last N lines or bytes                |
| `grep` / `rg`   | Pattern search (file or directory level)   |
| `jq`            | Query JSON fields on Workspace files       |
| `wc`            | Line/word/byte counts                      |
| `stat`          | File metadata (name, size, type)           |
| `find`          | Recursive search with `-name`, `-maxdepth` |
| `tree`          | Directory tree view                        |
| `basename`      | Extract filename from path                 |
| `dirname`       | Extract directory from path                |
| `realpath`      | Resolve path to absolute form              |
| `nl`            | Number lines of output                     |
| `sort`          | Sort lines                                 |
| `uniq`          | Deduplicate adjacent lines                 |
| `cut`           | Extract fields/columns                     |
| `awk`           | Pattern-directed scanning                  |
| `sed`           | Stream editor                              |
| `tr`            | Translate/delete characters                |
| `diff`          | Compare two files                          |
| `rev`           | Reverse lines                              |
| `tac`           | Reverse file line order                    |
| `paste`         | Merge lines of files                       |
| `join`          | Join lines on a common field               |
| `column`        | Columnate output                           |
| `comm`          | Compare sorted files line by line          |
| `fold`          | Wrap lines to a given width                |
| `fmt`           | Reformat paragraph text                    |
| `expand`        | Convert tabs to spaces                     |
| `unexpand`      | Convert spaces to tabs                     |
| `du`            | Estimate file space usage                  |
| `shuf`          | Randomly permute lines                     |
| `look`          | Display lines beginning with a prefix      |
| `strings`       | Extract printable strings from binary      |
| `base64`        | Base64 encode/decode                       |
| `md5`           | MD5 checksum                               |
| `sha256sum`     | SHA-256 checksum                           |
| `xxd`           | Hex dump                                   |
| `zcat`          | Read compressed files                      |
| `zgrep`         | Search compressed files                    |
| `readlink`      | Print resolved symbolic links              |
| `cmp`           | Compare two files byte by byte             |
| `tsort`         | Topological sort                           |
| `file`          | Detect file type                           |

## Data Format Support

Google Drive may contain data files in binary columnar formats.
These are auto-converted to CSV on read. Specialized variants
of common commands handle them natively:

| Format  | Extension  | Specialized commands                                                                                                                     |
| ------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| Parquet | `.parquet` | `cat-parquet`, `head-parquet`, `tail-parquet`, `wc-parquet`, `stat-parquet`, `grep-parquet`, `cut-parquet`, `ls-parquet`, `file-parquet` |
| Feather | `.feather` | `cat-feather`, `head-feather`, `tail-feather`, `wc-feather`, `stat-feather`, `grep-feather`, `cut-feather`, `ls-feather`, `file-feather` |
| HDF5    | `.hdf5`    | `cat-hdf5`, `head-hdf5`, `tail-hdf5`, `wc-hdf5`, `stat-hdf5`, `grep-hdf5`, `cut-hdf5`, `ls-hdf5`, `file-hdf5`                            |
| ORC     | `.orc`     | `cat-orc`, `head-orc`, `tail-orc`, `wc-orc`, `stat-orc`, `grep-orc`, `cut-orc`, `ls-orc`, `file-orc`                                     |

Example:

```bash theme={null}
cat-parquet /gdrive/data/sales.parquet
head-parquet -n 5 /gdrive/data/sales.parquet
grep-parquet "revenue" /gdrive/data/sales.parquet
wc-parquet /gdrive/data/sales.parquet
```

## Acting on Drive

Acting on Drive files by id (create, update, copy, delete, share,
export, plus the Docs/Sheets/Slides helpers) goes through the
[gws CLI](/python/cli/gws) when installed.
