Sovereign backup of all Hermes Agent configuration and data. Excludes: secrets, auth tokens, sessions, caches, code (separate repo). Tracked: - config.yaml (model, fallback chain, toolsets, display prefs) - SOUL.md (Timmy personality charter) - memories/ (persistent MEMORY.md + USER.md) - skills/ (371 files — full skill library) - cron/jobs.json (scheduled tasks) - channel_directory.json (platform channels) - hooks/ (custom hooks)
241 lines
7.7 KiB
Markdown
241 lines
7.7 KiB
Markdown
---
|
|
name: google-workspace
|
|
description: Gmail, Calendar, Drive, Contacts, Sheets, and Docs integration via Python. Uses OAuth2 with automatic token refresh. No external binaries needed — runs entirely with Google's Python client libraries in the Hermes venv.
|
|
version: 1.0.0
|
|
author: Nous Research
|
|
license: MIT
|
|
metadata:
|
|
hermes:
|
|
tags: [Google, Gmail, Calendar, Drive, Sheets, Docs, Contacts, Email, OAuth]
|
|
homepage: https://github.com/NousResearch/hermes-agent
|
|
related_skills: [himalaya]
|
|
---
|
|
|
|
# Google Workspace
|
|
|
|
Gmail, Calendar, Drive, Contacts, Sheets, and Docs — all through Python scripts in this skill. No external binaries to install.
|
|
|
|
## References
|
|
|
|
- `references/gmail-search-syntax.md` — Gmail search operators (is:unread, from:, newer_than:, etc.)
|
|
|
|
## Scripts
|
|
|
|
- `scripts/setup.py` — OAuth2 setup (run once to authorize)
|
|
- `scripts/google_api.py` — API wrapper CLI (agent uses this for all operations)
|
|
|
|
## First-Time Setup
|
|
|
|
The setup is fully non-interactive — you drive it step by step so it works
|
|
on CLI, Telegram, Discord, or any platform.
|
|
|
|
Define a shorthand first:
|
|
|
|
```bash
|
|
GSETUP="python ~/.hermes/skills/productivity/google-workspace/scripts/setup.py"
|
|
```
|
|
|
|
### Step 0: Check if already set up
|
|
|
|
```bash
|
|
$GSETUP --check
|
|
```
|
|
|
|
If it prints `AUTHENTICATED`, skip to Usage — setup is already done.
|
|
|
|
### Step 1: Triage — ask the user what they need
|
|
|
|
Before starting OAuth setup, ask the user TWO questions:
|
|
|
|
**Question 1: "What Google services do you need? Just email, or also
|
|
Calendar/Drive/Sheets/Docs?"**
|
|
|
|
- **Email only** → They don't need this skill at all. Use the `himalaya` skill
|
|
instead — it works with a Gmail App Password (Settings → Security → App
|
|
Passwords) and takes 2 minutes to set up. No Google Cloud project needed.
|
|
Load the himalaya skill and follow its setup instructions.
|
|
|
|
- **Calendar, Drive, Sheets, Docs (or email + these)** → Continue with this
|
|
skill's OAuth setup below.
|
|
|
|
**Question 2: "Does your Google account use Advanced Protection (hardware
|
|
security keys required to sign in)? If you're not sure, you probably don't
|
|
— it's something you would have explicitly enrolled in."**
|
|
|
|
- **No / Not sure** → Normal setup. Continue below.
|
|
- **Yes** → Their Workspace admin must add the OAuth client ID to the org's
|
|
allowed apps list before Step 4 will work. Let them know upfront.
|
|
|
|
### Step 2: Create OAuth credentials (one-time, ~5 minutes)
|
|
|
|
Tell the user:
|
|
|
|
> You need a Google Cloud OAuth client. This is a one-time setup:
|
|
>
|
|
> 1. Go to https://console.cloud.google.com/apis/credentials
|
|
> 2. Create a project (or use an existing one)
|
|
> 3. Click "Enable APIs" and enable: Gmail API, Google Calendar API,
|
|
> Google Drive API, Google Sheets API, Google Docs API, People API
|
|
> 4. Go to Credentials → Create Credentials → OAuth 2.0 Client ID
|
|
> 5. Application type: "Desktop app" → Create
|
|
> 6. Click "Download JSON" and tell me the file path
|
|
|
|
Once they provide the path:
|
|
|
|
```bash
|
|
$GSETUP --client-secret /path/to/client_secret.json
|
|
```
|
|
|
|
### Step 3: Get authorization URL
|
|
|
|
```bash
|
|
$GSETUP --auth-url
|
|
```
|
|
|
|
This prints a URL. **Send the URL to the user** and tell them:
|
|
|
|
> Open this link in your browser, sign in with your Google account, and
|
|
> authorize access. After authorizing, you'll be redirected to a page that
|
|
> may show an error — that's expected. Copy the ENTIRE URL from your
|
|
> browser's address bar and paste it back to me.
|
|
|
|
### Step 4: Exchange the code
|
|
|
|
The user will paste back either a URL like `http://localhost:1/?code=4/0A...&scope=...`
|
|
or just the code string. Either works:
|
|
|
|
```bash
|
|
$GSETUP --auth-code "THE_URL_OR_CODE_THE_USER_PASTED"
|
|
```
|
|
|
|
### Step 5: Verify
|
|
|
|
```bash
|
|
$GSETUP --check
|
|
```
|
|
|
|
Should print `AUTHENTICATED`. Setup is complete — token refreshes automatically from now on.
|
|
|
|
### Notes
|
|
|
|
- Token is stored at `~/.hermes/google_token.json` and auto-refreshes.
|
|
- To revoke: `$GSETUP --revoke`
|
|
|
|
## Usage
|
|
|
|
All commands go through the API script. Set `GAPI` as a shorthand:
|
|
|
|
```bash
|
|
GAPI="python ~/.hermes/skills/productivity/google-workspace/scripts/google_api.py"
|
|
```
|
|
|
|
### Gmail
|
|
|
|
```bash
|
|
# Search (returns JSON array with id, from, subject, date, snippet)
|
|
$GAPI gmail search "is:unread" --max 10
|
|
$GAPI gmail search "from:boss@company.com newer_than:1d"
|
|
$GAPI gmail search "has:attachment filename:pdf newer_than:7d"
|
|
|
|
# Read full message (returns JSON with body text)
|
|
$GAPI gmail get MESSAGE_ID
|
|
|
|
# Send
|
|
$GAPI gmail send --to user@example.com --subject "Hello" --body "Message text"
|
|
$GAPI gmail send --to user@example.com --subject "Report" --body "<h1>Q4</h1><p>Details...</p>" --html
|
|
|
|
# Reply (automatically threads and sets In-Reply-To)
|
|
$GAPI gmail reply MESSAGE_ID --body "Thanks, that works for me."
|
|
|
|
# Labels
|
|
$GAPI gmail labels
|
|
$GAPI gmail modify MESSAGE_ID --add-labels LABEL_ID
|
|
$GAPI gmail modify MESSAGE_ID --remove-labels UNREAD
|
|
```
|
|
|
|
### Calendar
|
|
|
|
```bash
|
|
# List events (defaults to next 7 days)
|
|
$GAPI calendar list
|
|
$GAPI calendar list --start 2026-03-01T00:00:00Z --end 2026-03-07T23:59:59Z
|
|
|
|
# Create event (ISO 8601 with timezone required)
|
|
$GAPI calendar create --summary "Team Standup" --start 2026-03-01T10:00:00-06:00 --end 2026-03-01T10:30:00-06:00
|
|
$GAPI calendar create --summary "Lunch" --start 2026-03-01T12:00:00Z --end 2026-03-01T13:00:00Z --location "Cafe"
|
|
$GAPI calendar create --summary "Review" --start 2026-03-01T14:00:00Z --end 2026-03-01T15:00:00Z --attendees "alice@co.com,bob@co.com"
|
|
|
|
# Delete event
|
|
$GAPI calendar delete EVENT_ID
|
|
```
|
|
|
|
### Drive
|
|
|
|
```bash
|
|
$GAPI drive search "quarterly report" --max 10
|
|
$GAPI drive search "mimeType='application/pdf'" --raw-query --max 5
|
|
```
|
|
|
|
### Contacts
|
|
|
|
```bash
|
|
$GAPI contacts list --max 20
|
|
```
|
|
|
|
### Sheets
|
|
|
|
```bash
|
|
# Read
|
|
$GAPI sheets get SHEET_ID "Sheet1!A1:D10"
|
|
|
|
# Write
|
|
$GAPI sheets update SHEET_ID "Sheet1!A1:B2" --values '[["Name","Score"],["Alice","95"]]'
|
|
|
|
# Append rows
|
|
$GAPI sheets append SHEET_ID "Sheet1!A:C" --values '[["new","row","data"]]'
|
|
```
|
|
|
|
### Docs
|
|
|
|
```bash
|
|
$GAPI docs get DOC_ID
|
|
```
|
|
|
|
## Output Format
|
|
|
|
All commands return JSON. Parse with `jq` or read directly. Key fields:
|
|
|
|
- **Gmail search**: `[{id, threadId, from, to, subject, date, snippet, labels}]`
|
|
- **Gmail get**: `{id, threadId, from, to, subject, date, labels, body}`
|
|
- **Gmail send/reply**: `{status: "sent", id, threadId}`
|
|
- **Calendar list**: `[{id, summary, start, end, location, description, htmlLink}]`
|
|
- **Calendar create**: `{status: "created", id, summary, htmlLink}`
|
|
- **Drive search**: `[{id, name, mimeType, modifiedTime, webViewLink}]`
|
|
- **Contacts list**: `[{name, emails: [...], phones: [...]}]`
|
|
- **Sheets get**: `[[cell, cell, ...], ...]`
|
|
|
|
## Rules
|
|
|
|
1. **Never send email or create/delete events without confirming with the user first.** Show the draft content and ask for approval.
|
|
2. **Check auth before first use** — run `setup.py --check`. If it fails, guide the user through setup.
|
|
3. **Use the Gmail search syntax reference** for complex queries — load it with `skill_view("google-workspace", file_path="references/gmail-search-syntax.md")`.
|
|
4. **Calendar times must include timezone** — always use ISO 8601 with offset (e.g., `2026-03-01T10:00:00-06:00`) or UTC (`Z`).
|
|
5. **Respect rate limits** — avoid rapid-fire sequential API calls. Batch reads when possible.
|
|
|
|
## Troubleshooting
|
|
|
|
| Problem | Fix |
|
|
|---------|-----|
|
|
| `NOT_AUTHENTICATED` | Run setup Steps 2-5 above |
|
|
| `REFRESH_FAILED` | Token revoked or expired — redo Steps 3-5 |
|
|
| `HttpError 403: Insufficient Permission` | Missing API scope — `$GSETUP --revoke` then redo Steps 3-5 |
|
|
| `HttpError 403: Access Not Configured` | API not enabled — user needs to enable it in Google Cloud Console |
|
|
| `ModuleNotFoundError` | Run `$GSETUP --install-deps` |
|
|
| Advanced Protection blocks auth | Workspace admin must allowlist the OAuth client ID |
|
|
|
|
## Revoking Access
|
|
|
|
```bash
|
|
$GSETUP --revoke
|
|
```
|