google-workspace
# Google Workspace Skill
Operate all Google Workspace services through the `gws` CLI from OpenClaw.
## Prerequisites
- Node.js 18+
- A Google Cloud project with OAuth credentials
- `gws` CLI installed: `npm install -g @googleworkspace/cli`
## Authentication
### First-Time Setup & Authentication Workflow (For the Agent)
When a user wants to start using this skill or if credentials are missing, **you (the AI agent) MUST follow this specific authentication workflow**:
1. **Ask for `client_secret.json`:** Prompt the user to provide their Google Cloud OAuth `client_secret.json`. They can either upload the file or paste its contents into the chat. Once they do, save it as `credentials.json` in the workspace.
2. **Explain the Flow:** Once you have the file, explain to the user exactly how the auth will work:
> *"I have saved your credentials. I am now going to start the authentication process. I'll provide you with a Google login link. You'll need to click it, authorize your account, and then your browser will redirect to a blank `http://localhost...` page. Copy that full localhost URL and paste it back to me here!"*
3. **Run Authentication:** Run `gws auth login` in the background (using your `exec` tool). Extract the generated Google OAuth URL from the output and send it to the user.
4. **Complete Handshake:** When the user replies with the `http://localhost...` callback URL, use `curl` to fetch that exact URL. This completes the local webserver handshake for `gws`. Confirm successful authentication with the user.
---
### Alternative: First-time setup (machine with browser)
```bash
gws auth setup # creates GCP project, enables APIs, logs in
gws auth login -s drive,gmail,sheets,calendar # pick services you need
```
### Alternative: Headless server (no browser) - PREFERRED MANUAL METHOD
Complete auth on a machine with a browser, then export:
```bash
gws auth export --unmasked > credentials.json
```
> **Security notes:**
> - Set file permissions: `chmod 600 credentials.json`
> - Do not commit credential files to git — add `credentials.json` to your `.gitignore`
> - For production environments, prefer using a service account instead of user credentials
On the headless server:
```bash
export GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE=/path/to/credentials.json
```
### Service account
```bash
export GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE=/path/to/service-account.json
```
### Pre-obtained token
```bash
export GOOGLE_WORKSPACE_CLI_TOKEN=$(gcloud auth print-access-token)
```
**Priority:** Token env > Credentials file env > `gws auth login` store > plaintext file.
## Command Pattern
```bash
gws <service> <resource> <method> [--params '{}'] [--json '{}'] [flags]
```
All responses are **structured JSON**. Use `jq` for extraction.
### Global Flags
| Flag | Purpose |
|------|---------|
| `--dry-run` | Preview request without executing |
| `--page-all` | Stream all pages as NDJSON |
| `--fields 'a,b'` | Select response fields |
| `--output table` | Table output for humans |
### Discover commands
```bash
gws --help # list all services
gws drive --help # list resources in a service
gws drive files --help # list methods on a resource
gws schema drive.files.list # full request/response schema
```
## Common Operations
### Drive
```bash
# List recent files
gws drive files list --params '{"pageSize": 10}'
# Search files
gws drive files list --params '{"q": "name contains '\''report'\''", "pageSize": 20}'
# Upload a file
gws drive +upload ./report.pdf
# Download a file
gws drive files get --params '{"fileId": "FILE_ID", "alt": "media"}' > output.pdf
# Create a folder
gws drive files create --json '{"name": "Project", "mimeType": "application/vnd.google-apps.folder"}'
# Share a file
gws drive permissions create \
--params '{"fileId": "FILE_ID"}' \
--json '{"role": "reader", "type": "user", "emailAddress": "user@example.com"}'
# List all pages
gws drive files list --params '{"pageSize": 100}' --page-all | jq -r '.files[].name'
```
### Gmail
```bash
# List inbox messages
gws gmail users-messages list --params '{"userId": "me", "maxResults": 10}'
# Read a message
gws gmail users-messages get --params '{"userId": "me", "id": "MSG_ID"}'
# Send an email
gws gmail users-messages send \
--params '{"userId": "me"}' \
--json '{"raw": "BASE64_ENCODED_EMAIL"}'
# Search messages
gws gmail users-messages list --params '{"userId": "me", "q": "from:boss@company.com is:unread"}'
# List labels
gws gmail users-labels list --params '{"userId": "me"}'
# Create a filter
gws gmail users-settings-filters create \
--params '{"userId": "me"}' \
--json '{"criteria": {"from": "noreply@example.com"}, "action": {"addLabelIds": ["LABEL_ID"], "removeLabelIds": ["INBOX"]}}'
```
### Calendar
```bash
# List upcoming events
gws calendar events list --params '{"calendarId": "primary", "timeMin": "2026-01-01T00:00:00Z", "maxResults": 10, "orderBy": "startTime", "singleEvents": true}'
# Create an event
gws calendar events insert \
--params '{"calendarId": "primary"}' \
--json '{"summary": "Team Sync", "start": {"dateTime": "2026-03-07T10:00:00+08:00"}, "end": {"dateTime": "2026-03-07T11:00:00+08:00"}, "attendees": [{"email": "user@example.com"}]}'
# Delete an event
gws calendar events delete --params '{"calendarId": "primary", "eventId": "EVENT_ID"}'
# Find free/busy slots
gws calendar freebusy query \
--json '{"timeMin": "2026-03-07T00:00:00Z", "timeMax": "2026-03-07T23:59:59Z", "items": [{"id": "user@example.com"}]}'
```
### Sheets
```bash
# Create a spreadsheet
gws sheets spreadsheets create --json '{"properties": {"title": "Q1 Budget"}}'
# Read cell values
gws sheets spreadsheets-values get --params '{"spreadsheetId": "SHEET_ID", "range": "Sheet1!A1:D10"}'
# Write values
gws sheets spreadsheets-values update \
--params '{"spreadsheetId": "SHEET_ID", "range": "Sheet1!A1", "valueInputOption": "USER_ENTERED"}' \
--json '{"values": [["Name", "Amount"], ["Rent", "2000"]]}'
# Append a row
gws sheets spreadsheets-values append \
--params '{"spreadsheetId": "SHEET_ID", "range": "Sheet1!A1", "valueInputOption": "USER_ENTERED"}' \
--json '{"values": [["New Item", "500"]]}'
```
### Docs
```bash
# Create a document
gws docs documents create --json '{"title": "Meeting Notes"}'
# Get document content
gws docs documents get --params '{"documentId": "DOC_ID"}'
# Insert text (batchUpdate)
gws docs documents batchUpdate \
--params '{"documentId": "DOC_ID"}' \
--json '{"requests": [{"insertText": {"location": {"index": 1}, "text": "Hello World\n"}}]}'
```
### Chat
```bash
# List spaces
gws chat spaces list
# Send a message
gws chat spaces messages create \
--params '{"parent": "spaces/SPACE_ID"}' \
--json '{"text": "Deploy complete ✅"}'
```
### Tasks
```bash
# List task lists
gws tasks tasklists list
# List tasks
gws tasks tasks list --params '{"tasklist": "TASKLIST_ID"}'
# Create a task
gws tasks tasks insert \
--params '{"tasklist": "TASKLIST_ID"}' \
--json '{"title": "Review PR", "due": "2026-03-10T00:00:00Z"}'
```
### Admin (Directory)
```bash
# List users
gws admin users list --params '{"domain": "example.com"}'
# Get user details
gws admin users get --params '{"userKey": "user@example.com"}'
```
## Workflow Patterns
### Pipeline: Find → Process → Act
```bash
# Find unread emails from boss, extract subjects
gws gmail users-messages list --params '{"userId": "me", "q": "from:boss is:unread"}' \
| jq -r '.messages[].id' \
| while read id; do
gws gmail users-messages get --params "{\"userId\": \"me\", \"id\": \"$id\"}" \
| jq -r '.payload.headers[] | select(.name=="Subject") | .value'
done
```
### Dry-run first
Always use `--dry-run` before destructive operations:
```bash
gws drive files delete --params '{"fileId": "FILE_ID"}' --dry-run
```
## Tips
- Use `gws schema <method>` to discover exact parameter names and types.
- All commands accept `--params` for URL/query parameters and `--json` for request body.
- Pipe through `jq` for field extraction in agent pipelines.
- Use `--page-all` for full result sets with automatic pagination.
- Credentials are encrypted at rest (AES-256-GCM) with OS keyring.
## Recipes
For 50+ ready-made workflow recipes (label & archive emails, organize Drive folders, schedule meetings, etc.), see the [official recipe library](https://github.com/googleworkspace/cli/tree/main/skills).
## Disclaimer
The `gws` CLI is **not an officially supported Google product**. It is a community/experimental tool. Use at your own discretion, and refer to the [upstream repository](https://github.com/googleworkspace/cli) for license and support details.
## Troubleshooting
| Issue | Fix |
|-------|-----|
| `gws: command not found` | `npm install -g @googleworkspace/cli` |
| Auth fails / scope error | `gws auth login -s drive,gmail` (pick specific services) |
| "Access blocked" on login | Add yourself as test user in GCP OAuth consent screen |
| Headless server | Export creds from a desktop, set `GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE` |
| Rate limited | Add delays between calls, reduce `pageSize` |
标签
skill
ai