Skip to main content
Mirage’s built-in Google Workspace API client follows the command surface of the upstream googleworkspace/cli project, with Discovery passthroughs plus ergonomic helpers. The Mirage command tree is discoverable with gws --help. Licenses: The upstream project uses Apache-2.0; Mirage’s independent implementation uses Apache-2.0.

Install

Two installs under different names are two accounts. In YAML, the same install rides the clis: section; see the CLI overview.

Verbs

The syntax mirrors the upstream Google Workspace CLI: one passthrough leaf per Discovery method, plus hand-written helpers under each service.
Services: drive, sheets, docs, slides, gmail. The whole surface is discoverable from the shell:

API passthrough

Passthrough commands call the corresponding API method directly: --params fills URL path and query parameters, --json is the request body, and the output is the compact API response JSON. List methods follow nextPageToken to the end by default (pages print as NDJSON); --page-limit N stops early.

Folder scope

A config carrying folder_id scopes what the CLI creates, so a file it makes lands in the same folder a GDriveVFS sharing that config mounts, and the agent’s own ls shows what it just made:
Three things worth knowing:
  • Two divergences from the official CLI’s passthrough. drive files create and copy get parents defaulted into the request body, and the Docs/Sheets/Slides create methods have no parents field at all, so mirage issues a second Drive call to move the new file. Both also send supportsAllDrives, which is what lets a scope name a Shared Drive folder.
  • An explicit parents array always wins, and then nothing is injected into the query either: you typed the call, you own it. The key being present is what counts, so "parents": [] is honored too rather than read as absent.
  • Reads are not scoped. gws drive files list still sees the whole account. The scope is about where new files go, not a fence.
  • Helpers are spelled bare, not +-prefixed. Upstream marks its hand-written commands with a + (gws docs +write); mirage’s tree does not need the marker, because a helper and a Discovery method cannot collide inside a typed spec. So an upstream line carrying + has to drop it here. The flag names are upstream’s own and match it (--document --text, gmail send’s --to --subject --body, sheets read’s --spreadsheet --range); --tab is the single addition, because upstream’s +write takes no tab argument and upstream has no tab support at all.

Document tabs

A Google Doc holds one or more tabs, and the Docs API reports them only when asked. documents.get fills the legacy top-level body from the first tab and leaves tabs empty unless includeTabsContent is set; with it, the content moves under tabs[].documentTab and body is left empty. The two shapes are exclusive, so it is a shape switch rather than a verbosity knob, and tabs nest through childTabs. A .gdoc.json on a mount is always the tab-aware shape, because that is the only one that can represent a document with more than one tab. Through the passthrough you choose:
Writes pick a tab the same way. gws docs write without --tab appends to the first tab, which is the API’s own default for a request that names no tab; read the ids out of a mounted file with jq '[.. | .tabProperties? // empty | .tabId]'. Note that replaceAllText is one of three requests that instead default to every tab, so a bare batchUpdate replacement is document-wide.

Helpers