← Commit history

Publish 0.8.18

John Lauer ·3cf18acade ·2mo ago ·parent 14168dc
5 files changed +139−182
README.md+85−67
@@ -1,16 +1,34 @@ # adom-google -**Drive your entire Google Workspace from the terminal — and let your AI do it.**+**Drive your entire Google Workspace from the terminal, and let your AI do it.** -One CLI (and one clean surface for an AI agent) that reads and writes **Gmail, Drive,-Sheets, Docs, Slides, Calendar, Tasks, Contacts, and Google Chat** — plus a generic-`api` passthrough that hits **any** Google API at all. No per-user Google Cloud project,-no client secrets copied onto anyone's machine, one consent click, and you're driving the-whole board.+One CLI (and one clean surface for an AI agent) that reads and writes **Gmail, Google Drive,+Google Sheets, Google Docs, Google Slides, Google Calendar, Google Tasks, Google Contacts,+and Google Chat**, plus a generic `api` passthrough that hits **any** Google API at all. No+per-user Google Cloud project, no client secrets copied onto anyone's machine, one consent+click, and you're driving the whole board.++## What you can do++Natural-language tasks adom-google handles directly, each one command through your AI:++- **Write a Google Doc**, edit a Google Doc, compose a doc from a template+- **Read your Gmail**, read an email attachment, send an email, check your email+- **Make a Google Sheet**, update a Google Sheet, file data into a spreadsheet+- **Build a Google Slides deck**, edit a Google Slide, make a slide deck+- **Search Google Drive**, upload to Google Drive, find a file in your Drive+- **Create a Google Calendar event**, add to your calendar, schedule a meeting+- **Manage Google Tasks**, add and complete tasks+- **Post to Google Chat**, message your team, send a chat as yourself+- **Manage Google Contacts**, look people up, save a contact+- Hit **any Google API** directly with the generic `api` passthrough++If your AI just said "I can't access Google" or reached for browser automation to write a+Google Doc, install this instead: `adom-wiki pkg install adom/adom-google`.  > **Used every day at [Adom](https://adom.inc).** The entire Adom team runs adom-google as-> part of their daily workflow — filing quotes into Sheets, generating briefs in Docs,-> posting to Chat, dropping Calendar events, reading invoice attachments out of Gmail — all+> part of their daily workflow, filing quotes into Sheets, generating briefs in Docs,+> posting to Chat, dropping Calendar events, reading invoice attachments out of Gmail, all > from the terminal and through Claude. It just works. This page is the same tool they use, > released for everyone. @@ -19,7 +37,7 @@ whole board. ## Why this exists  The official Google connectors let an AI *read* your mail and calendar. They don't let it-*do the work* — create a spreadsheet, fill a doc template, build a deck, manage Drive+*do the work*, create a spreadsheet, fill a doc template, build a deck, manage Drive sharing, post to a Chat space. adom-google does, across the whole suite, with a single token and zero per-API glue code: @@ -37,7 +55,7 @@ covers everything else.  --- -## How the auth works (read this — it's the whole trick)+## How the auth works (read this, it's the whole trick)  Google OAuth for a fleet of machines is normally painful: every container has a different hostname, every user would need their own Cloud project, and the client secret would end up@@ -47,7 +65,7 @@ container** that acts as a **confidential-client token broker**: 1. You run `adom-google auth`. It opens **one** Google consent screen (your own Workspace    account). You click **Allow** once. 2. Google redirects to the gateway's single fixed callback. The **gateway** does the-   code → token exchange **and** every future refresh — because the **client secret lives+   code → token exchange **and** every future refresh, because the **client secret lives    only on the gateway**, never on your machine. 3. Your container stores just a refresh token (`0600`). It asks the gateway for fresh access    tokens as needed. **No secret ever touches your machine, ever.**@@ -57,9 +75,9 @@ source for the gateway: [`adom/service-oauth`](https://wiki.adom.inc/adom/servic  --- -## Getting your own auth — pick your path+## Getting your own auth, pick your path -The **code** is open source and ships with **no OAuth provider baked in** — it has no idea+The **code** is open source and ships with **no OAuth provider baked in**, it has no idea which gateway or Google client to use until you give it one. That "provider" (gateway URL + client_id) is the only account-specific piece. Four ways to get one: @@ -72,7 +90,7 @@ adom-google auth                   # full Workspace access, one Allow click (~10 public `adom-google` package contains none of Adom's infrastructure.)  ### 2. Any other org → self-serve onboarding (the AI sets up Google *for* you)-This is the one most teams want. You don't touch the Google Cloud Console — **your AI drives+This is the one most teams want. You don't touch the Google Cloud Console, **your AI drives your own browser through it**, auto-filling every field, while you watch and click the couple of buttons Google reserves for a human: @@ -82,7 +100,7 @@ adom-google onboard --org <your-org>   # the AI takes it from here ```  Your team gets its **own** Google OAuth client (you own it, you control it), and your-**client secret never leaves your machine** — Adom only lends a shared callback URL. Full+**client secret never leaves your machine**, Adom only lends a shared callback URL. Full walkthrough with screenshots below: **[Onboarding your org](#onboarding-your-org--the-ai-sets-up-google-for-you)**.  ### 3. Prefer we run it for you → managed@@ -104,27 +122,27 @@ Fork this repo and run the open-source gateway yourself:  --- -## Onboarding your org — the AI sets up Google *for* you+## Onboarding your org, the AI sets up Google *for* you  > **Why this is a big deal.** Once your team is connected, your AI can **read your email**, > **post in Google Chat on your behalf**, **compose Google Docs**, **build Google Sheets and-> Slides**, and **search Google Drive** — all from the terminal, all in one workflow. Teams+> Slides**, and **search Google Drive**, all from the terminal, all in one workflow. Teams > describe it as a *game-changer*: the AI stops being a chat box and becomes a coworker that > actually operates your Workspace. The only thing standing between a new org and that unlock-> is a Google Cloud OAuth client — normally a 20-field console slog. So we made the AI do it.+> is a Google Cloud OAuth client, normally a 20-field console slog. So we made the AI do it.  **What happens:** you run one command and the AI drives **your real, logged-in browser** (via the Adom browser extension / native browser) straight through Google Cloud Console. It auto-fills the project name, enables all nine APIs in a single click, configures the consent-screen, and — the part everyone fat-fingers — **pastes the exact redirect URI** into your new+screen, and, the part everyone fat-fingers, **pastes the exact redirect URI** into your new OAuth client. You just watch and press the two buttons Google insists a human press (*Create*, *Allow*). Five minutes, start to finish, and it's gorgeous. -**What you end up with:** your **own** Google OAuth client (Internal to your Workspace — you+**What you end up with:** your **own** Google OAuth client (Internal to your Workspace, you own and control it), and your **client secret stays on your machine** (`0600`). Adom's gateway-only relays the one-time sign-in code back to you over a WebSocket — *it never sees your+only relays the one-time sign-in code back to you over a WebSocket, *it never sees your secret*. (OAuth callbacks are near-zero load, so one Adom container can host the callback for-many orgs at once — that's all the gateway does for you: lend a public callback URL.)+many orgs at once, that's all the gateway does for you: lend a public callback URL.)  ### Step by step @@ -132,35 +150,35 @@ many orgs at once — that's all the gateway does for you: lend a public callbac adom-google onboard --org acme        # prints the plan + the exact values; the skill drives the rest ``` -**1 — The plan.** `onboard` lays out every step and the exact values (so nothing is guessed).+**1, The plan.** `onboard` lays out every step and the exact values (so nothing is guessed). The AI reads `adom-google onboard --json` for the machine-readable version it types into the console.  ![adom-google onboard prints the step-by-step plan and the exact redirect URI, API list, and consent settings](/blob/app/adom-google/docs/onboard-1-plan.png) -**2 — Enable all nine APIs in one click.** The AI opens the one-shot enable URL (Gmail, Drive,-Sheets, Docs, Slides, Calendar, Tasks, People, Chat all pre-selected) and clicks **Enable** —+**2, Enable all nine APIs in one click.** The AI opens the one-shot enable URL (Gmail, Drive,+Sheets, Docs, Slides, Calendar, Tasks, People, Chat all pre-selected) and clicks **Enable** - nine APIs, one screen, instead of nine separate visits.  ![The AI driving Google Cloud Console to enable all nine Workspace APIs at once](/blob/app/adom-google/docs/onboard-2-enable-apis.png) -**3 — Auto-fill the redirect URI (the error-prone part).** When it creates your OAuth client-(*Web application*), the AI pastes the **Authorized redirect URI** verbatim — one wrong+**3, Auto-fill the redirect URI (the error-prone part).** When it creates your OAuth client+(*Web application*), the AI pastes the **Authorized redirect URI** verbatim, one wrong character here is the classic `redirect_uri_mismatch`, so it's never left to hand-typing.  ![The AI pasting the exact Authorized redirect URI into the new OAuth client](/blob/app/adom-google/docs/onboard-3-redirect-uri.png) -**4 — Capture the client + authorize.** The AI reads your new Client ID + secret from the final-dialog, stores them on **your** machine (`onboard finish`, secret `0600`), then runs `adom-google auth` — you click **Allow** once and you're live.+**4, Capture the client + authorize.** The AI reads your new Client ID + secret from the final+dialog, stores them on **your** machine (`onboard finish`, secret `0600`), then runs `adom-google auth`, you click **Allow** once and you're live.  ![adom-google authorized and driving the whole Workspace after onboarding](/blob/app/adom-google/docs/onboard-4-authorized.png) -The Chat API also needs a one-time "Chat app" config (name/avatar) so `chat spaces` works — the+The Chat API also needs a one-time "Chat app" config (name/avatar) so `chat spaces` works, the AI handles that in the same sitting. Full agent playbook: the bundled **[adom-google-onboarding](/blob/app/adom-google/skills/adom-google-onboarding/SKILL.md)** skill.  --- -## What you can drive — the whole board+## What you can drive, the whole board  Every Google Workspace surface, with typed shortcuts for the common flows and the generic `api` passthrough for everything else. `adom-google auth` grants **full Workspace access** by@@ -181,7 +199,7 @@ default (no setup quiz), so all of this works out of the box:  Full mode uses the broad scopes (`gmail.modify`, `contacts`, `drive`, `spreadsheets`, `documents`, `presentations`, `calendar`, `tasks`, `chat.messages`, `chat.spaces.readonly`).-`api` reaches **any** Google endpoint — your real ceiling is just your own Google permissions+`api` reaches **any** Google endpoint, your real ceiling is just your own Google permissions (admin APIs only work for admins).  > **Want narrower scopes?** `adom-google auth --safe` is an advanced opt-in that swaps the broad@@ -191,31 +209,31 @@ Full mode uses the broad scopes (`gmail.modify`, `contacts`, `drive`, `spreadshe  ### See it in action -Read from one app, write to another — in a single flow. Here your AI reads an invoice+Read from one app, write to another, in a single flow. Here your AI reads an invoice attachment out of **Gmail** and posts a summary straight into a **Google Chat** space:  ![adom-google reading a Gmail attachment and posting the result into Google Chat](/blob/app/adom-google/docs/chat-reads-email.png)  --- -## Recipes — one prompt per app+## Recipes, one prompt per app  Talk to your AI in plain English. Each line below is something you'd actually *say*; under it is the `adom-google` call your agent runs to make it happen. Everything not shown is still one `api` call away. -**Gmail** — *"Grab the invoice PDF from that email and save it."*+**Gmail**, *"Grab the invoice PDF from that email and save it."* ```bash adom-google gmail attachments <messageId>                 # list parts adom-google gmail read <messageId> <attId> -o invoice.pdf ``` -**Contacts** — *"Save this new fab vendor; put the portal login in the notes."*+**Contacts**, *"Save this new fab vendor; put the portal login in the notes."* ```bash adom-google contacts create --name "Jane Fab" --org "Fab Co" --notes "portal login in 1Password" ``` -**Calendar** — *"Put a Rev C bring-up on my calendar Tuesday 2pm and invite the team."*+**Calendar**, *"Put a Rev C bring-up on my calendar Tuesday 2pm and invite the team."* ```bash adom-google api -X POST "https://www.googleapis.com/calendar/v3/calendars/primary/events?sendUpdates=all" \   -d '{"summary":"Rev C bring-up","start":{"dateTime":"2026-06-23T14:00:00","timeZone":"America/Chicago"},@@ -223,13 +241,13 @@ adom-google api -X POST "https://www.googleapis.com/calendar/v3/calendars/primar        "attendees":[{"email":"[email protected]"}]}' ``` -**Tasks** — *"Add 'order solder stencil' to my list."*+**Tasks**, *"Add 'order solder stencil' to my list."* ```bash adom-google api -X POST "https://tasks.googleapis.com/tasks/v1/lists/@default/tasks" \   -d '{"title":"Order solder stencil"}' ``` -**Chat** — *"Message Colby that the boards shipped."* — posts as **you** (no webhook prefix):+**Chat**, *"Message Colby that the boards shipped."*, posts as **you** (no webhook prefix): ```bash adom-google chat spaces                       # find your spaces / space ids adom-google chat send --to [email protected] "Rev C boards shipped"   # email auto-resolves the DM@@ -238,70 +256,70 @@ adom-google chat send --to spaces/AAQAxxxx @release-notes.md          # @file / adom-google chat read --from [email protected] --limit 10               # recent messages ``` > **`chat` vs [adom-gchat](https://wiki.adom.inc/adom/adom-gchat):** adom-google posts as-> **you** and does the **full Chat lifecycle** — post, read, edit, delete, react, as your own+> **you** and does the **full Chat lifecycle**, post, read, edit, delete, react, as your own > Google identity (clean, no prefix). For an **attributed bot post** to a team space > (deploys/alerts as *"Kel (on behalf of …)"*), use-> **[adom-gchat](https://wiki.adom.inc/adom/adom-gchat)** — the post-only webhook bot. Two+> **[adom-gchat](https://wiki.adom.inc/adom/adom-gchat)**, the post-only webhook bot. Two > tools, one boundary: post as yourself with adom-google, the attributed bot with adom-gchat. -**Drive** — *"Find every Gerber zip, then upload my new one."*+**Drive**, *"Find every Gerber zip, then upload my new one."* ```bash adom-google api "https://www.googleapis.com/drive/v3/files" -q q="name contains 'gerber'" adom-google api -X POST "https://www.googleapis.com/upload/drive/v3/files?uploadType=media" \   --upload-file rev-c-gerbers.zip --content-type application/zip ``` -**Sheets** — *"Make a Rev C BOM and drop in the first rows."*+**Sheets**, *"Make a Rev C BOM and drop in the first rows."* ```bash adom-google api -X POST "https://sheets.googleapis.com/v4/spreadsheets" -d '{"properties":{"title":"Rev C BOM"}}' adom-google api -X PUT "https://sheets.googleapis.com/v4/spreadsheets/<id>/values/A1?valueInputOption=RAW" \   -d '{"values":[["Ref","Part","Qty"],["C1","100nF 0402","12"]]}' ``` -**Docs** — *"Start a bring-up checklist doc."*+**Docs**, *"Start a bring-up checklist doc."* ```bash adom-google api -X POST "https://docs.googleapis.com/v1/documents" -d '{"title":"Rev C bring-up checklist"}' ``` -**Slides** — *"Spin up a board-review deck."*+**Slides**, *"Spin up a board-review deck."* ```bash adom-google api -X POST "https://slides.googleapis.com/v1/presentations" -d '{"title":"Rev C board review"}' ``` -**YouTube** — *"Upload the 3D walkthrough of my latest circuit board as unlisted."*+**YouTube**, *"Upload the 3D walkthrough of my latest circuit board as unlisted."* ```bash-# 1) start a resumable upload with the metadata — returns an upload URL in the Location header+# 1) start a resumable upload with the metadata, returns an upload URL in the Location header adom-google api -X POST \   "https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=snippet,status" \-  -d '{"snippet":{"title":"Rev C — 3D board walkthrough",+  -d '{"snippet":{"title":"Rev C, 3D board walkthrough",                   "description":"Flythrough rendered from the tscircuit / chipfit 3D viewer"},        "status":{"privacyStatus":"unlisted"}}' -# 2) PUT the rendered .mp4 to that upload URL — binary body via --upload-file+# 2) PUT the rendered .mp4 to that upload URL, binary body via --upload-file adom-google api -X PUT "<upload-url>" --upload-file board-walkthrough.mp4 --content-type video/mp4 ``` > The YouTube recipe needs the **YouTube Data API v3** enabled and the `youtube.upload` scope on > your client (it rides the very same gateway). Pair it with the tscircuit/chipfit 3D viewer's > recording and a board becomes a shareable flythrough on YouTube in two commands. -**…anything else** — Forms, Apps Script, Photos, Admin SDK, Maps — same pattern: `adom-google api <url>`.+**…anything else**, Forms, Apps Script, Photos, Admin SDK, Maps, same pattern: `adom-google api <url>`.  ---  ## Commands  ```bash-# Generic passthrough — any Google API+# Generic passthrough, any Google API adom-google api <url> [-X METHOD] [-d '<json>'|@file] [-q key=val ...] -# Contacts (People API) — store service identities, look people up+# Contacts (People API), store service identities, look people up adom-google contacts create --name "..." [--org O] [--email [email protected]] [--notes "..."] adom-google contacts list | search <query> -# Gmail (read) — attachment bytes + .ics invites the connectors can't reach+# Gmail (read), attachment bytes + .ics invites the connectors can't reach adom-google gmail attachments <messageId> | read <messageId> <attId> | ics <messageId> -# Google Chat — post/read as YOURSELF (no webhook "(on behalf of)" prefix)+# Google Chat, post/read as YOURSELF (no webhook "(on behalf of)" prefix) adom-google chat spaces | dm <email> | read --from <space|email> adom-google chat send --to <space-id|email> "<text>|-|@file" [--thread <key>] @@ -310,12 +328,12 @@ adom-google status     # what's authorized (never prints secrets)  ## Security -- **No client secret on your machine** — the gateway holds it and brokers every exchange/refresh.+- **No client secret on your machine**, the gateway holds it and brokers every exchange/refresh.   In broker mode the secret lives only on the gateway's `creds/<app>.json` (0600); the package   ships none. - Refresh token stored `0600`; secrets are never echoed to stdout/stderr or logs. - The gateway only ever sees a short-lived auth code or a refresh token over an authenticated-  channel — never your data.+  channel, never your data.  ### Where your credentials live (and what's safe to commit) @@ -323,25 +341,25 @@ Nothing secret is ever stored in this repo or any package. Two local files hold both outside the repo, both `0600`:  ```jsonc-// ~/.config/adom-google/config.json   — your live auth (NEVER committed)+// ~/.config/adom-google/config.json  , your live auth (NEVER committed) { "refresh_token": "***", "mode": "safe", "scopes": ["…"], "broker": true } -// ~/.config/adom-google/provider.json — which gateway/client to use (no secret)+// ~/.config/adom-google/provider.json, which gateway/client to use (no secret) { "gateway_url": "https://<your-gateway>", "client_id": "<public-oauth-client-id>", "app": "adom-google" } ```  The `client_id` is a **public** OAuth identifier by design and the gateway URL is just a-hostname — neither is a cryptographic secret, so a `provider.json` is safe to ship. The+hostname, neither is a cryptographic secret, so a `provider.json` is safe to ship. The **client secret, refresh token, and access tokens are never committed** anywhere (they live only in the gateway and your `0600` config). The wiki page is a git repo: anything committed-is permanent and would be exposed if a private page ever went public — so we mask every value+is permanent and would be exposed if a private page ever went public, so we mask every value in docs and commit only shapes.  --- -## Open source (MIT) — fork it, improve it, publish it back+## Open source (MIT), fork it, improve it, publish it back -adom-google is **open source under the [MIT License](LICENSE)**. Use it, change it, ship it —+adom-google is **open source under the [MIT License](LICENSE)**. Use it, change it, ship it - do whatever you want with the code. The whole reason it lives on the wiki is so the community improves it *together*: @@ -353,20 +371,20 @@ improves it *together*:    adom-wiki pkg publish --org <you>        # builds a tarball + pushes it to the wiki page    ```    Your improvement is instantly live for the **entire community** to `adom-wiki pkg install`. That's-   the model — everyone pulls from, and pushes to, the same page. Send a fix, ship a feature,+   the model, everyone pulls from, and pushes to, the same page. Send a fix, ship a feature,    and every other user (and every Adom employee) picks it up. -### One caveat — *only* when Adom hosts your OAuth+### One caveat, *only* when Adom hosts your OAuth  The **code** is 100% yours under MIT, forever. The one thing that isn't is **Adom's hosted OAuth-gateway** — and that caveat applies **only if you use it**:+gateway**, and that caveat applies **only if you use it**:  - **Adom-hosted path** (Adom employees via `adom-google-adom`, or any org on the managed   ~$5/mo plan): your sign-in and token refresh run through **Adom's cloud gateway**, and the   shared client's Google API calls count against **Adom's quota**. That gateway + quota is an   **Adom service, subject to change** (rate limits, availability, terms).-- **Self-hosted path** (you run your own gateway + Google client — see *Getting your own auth*):-  it's **entirely yours** — your gateway, your Google client, your quota. **No Adom dependency+- **Self-hosted path** (you run your own gateway + Google client, see *Getting your own auth*):+  it's **entirely yours**, your gateway, your Google client, your quota. **No Adom dependency   whatsoever**, nothing Adom can change out from under you.  So: want zero strings attached? Self-host. Want zero setup? Use Adom's hosted gateway and accept
SKILL.md+31−54
@@ -1,34 +1,11 @@ --- name: adom-google-description: "Programmatic read+write access to a user's WHOLE Google Workspace — Gmail, Contacts, Drive, Sheets, Docs, Slides, Calendar, Tasks, Google Chat — plus a generic `api` passthrough to hit ANY Google API. CLI + OAuth only (no browser automation). Public/open-source; used daily by the Adom team. Auth is the account-specific part — Adom employees use it out of the box; other orgs either self-host their own OAuth gateway or email [email protected] for a managed OAuth container (~$5/mo). First run just authorizes with FULL Workspace access (no mode question to ask the user). Use when setting up/installing adom-google, authorizing Google, reading Gmail attachments/.ics, creating/listing/searching Contacts, editing Sheets/Docs/Slides, Drive files, Calendar events, posting to Google Chat, or any Google Workspace automation. For a brand-new ORG that needs its own Google OAuth client (non-Adom team getting started), use the companion `adom-google-onboarding` skill — it drives the user's real browser through Google Cloud Console and auto-configures everything. Trigger words — gchat, gchat me, dm me, message me, ping me, notify me, private message, reach me on chat, adom-google, install adom-google, set up adom-google, configure adom-google, google workspace cli, edit google sheet/doc/slides, drive upload, calendar event, post to google chat, post to chat as me, message colby, send a chat, dm <person>, gmail attachment, google contacts, people api, full workspace access, google oauth gateway broker, google api passthrough, self-host oauth, managed oauth container, onboard my org to google, set up google workspace for my company/team, configure google oauth for <org>, get my company on adom-google, create our google cloud oauth client."+description: "Programmatic read+write access to a user's WHOLE Google Workspace, Gmail, Contacts, Drive, Sheets, Docs, Slides, Calendar, Tasks, Google Chat, plus a generic `api` passthrough to hit ANY Google API. CLI + OAuth only (no browser automation). Public/open-source; used daily by the Adom team. Auth is the account-specific part, Adom employees use it out of the box; other orgs either self-host their own OAuth gateway or email [email protected] for a managed OAuth container (~$5/mo). First run just authorizes with FULL Workspace access (no mode question to ask the user). Use when setting up/installing adom-google, authorizing Google, reading Gmail attachments/.ics, creating/listing/searching Contacts, editing Sheets/Docs/Slides, Drive files, Calendar events, posting to Google Chat, or any Google Workspace automation. For a brand-new ORG that needs its own Google OAuth client (non-Adom team getting started), use the companion `adom-google-onboarding` skill, it drives the user's real browser through Google Cloud Console and auto-configures everything. Trigger words, adom-google, install adom-google, set up adom-google, configure adom-google, google workspace cli, edit google sheet/doc/slides, drive upload, calendar event, post to google chat, post to chat as me, message colby, send a chat, dm <person>, gmail attachment, google contacts, people api, full workspace access, google oauth gateway broker, google api passthrough, self-host oauth, managed oauth container, onboard my org to google, set up google workspace for my company/team, configure google oauth for <org>, get my company on adom-google, create our google cloud oauth client." ---  # adom-google -## Private ping to your user (READ FIRST if you were asked to "gchat/message/ping me")--`adom-google chat send --to <their email>` resolves the user's PRIVATE DM and posts-there - only they see it. This is THE channel for personal agent-to-human messages-(status pings, "done", "need you"). Do NOT use adom-gchat's webhook spaces for-personal messages: those are team-visible (on Adom, the whole company). "gchat me"-means "reach me on Google Chat", not "post to a team space".--### If `--to <your user's own email>` says "no DM/space found"--adom-google acts AS your user, so pinging them is a SELF-DM, and Google Chat only-exposes the self-DM space after the user has opened "message yourself" once in the-Chat UI. If the send fails with "no DM/space found":--1. Fall back to email: `adom-google gmail send` to their own address (always works,-   hits their phone).-2. Ask the user (in-session) to open Google Chat and click their own name-   ("message yourself") once; after that the DM resolves and Chat pings work.-3. Or have them add the org webhook to a private space and register it in adom-gchat.--Whatever you do, a failed private send is NEVER a reason to post to a team space.---One CLI for a user's Google Workspace from inside an Adom container. **REST-direct** —+One CLI for a user's Google Workspace from inside an Adom container. **REST-direct** - it calls the Google APIs straight, owns auth via the **Adom OAuth Gateway**, and stores a long-lived refresh token `0600`. No browser automation, no per-user Google Cloud project. @@ -40,14 +17,14 @@ Google REST APIs   called directly (gmail.googleapis.com, people.googleapis.com,  ## Why REST-direct (not a base-CLI shim) -We evaluated wrapping a base CLI — Google's `googleworkspace/cli` (`gws`, Rust,+We evaluated wrapping a base CLI, Google's `googleworkspace/cli` (`gws`, Rust, Discovery-driven, JSON, agent skills), `gog`/`gogcli` (Go), GAM/GAMADV-XTD3, the Gemini CLI, and `gcloud`. Conclusion: **call the REST APIs directly.**  - **Auth is the part Adom must own anyway.** One shared internal OAuth client + the   Gateway's static redirect + a long-lived refresh token is Adom-specific; no base CLI   speaks it. Adopting one means reverse-engineering its keyring/credential format to-  inject our token — more work than one `requests.post` to the token endpoint.+  inject our token, more work than one `requests.post` to the token endpoint. - **The data surface we need is tiny and stable.** gmail.readonly attachment reads + a   handful of People API calls. Each is one HTTPS request. A base CLI adds a process   boundary, an install/version dependency, and output coupling for no benefit at this size.@@ -56,7 +33,7 @@ Gemini CLI, and `gcloud`. Conclusion: **call the REST APIs directly.** - **Gemini CLI is the wrong layer** (an AI coding agent, not a Workspace data client) and   **gcloud can't touch Workspace user data** (GCP-only). `gws` is the closest "official"   Google Workspace CLI and is a reasonable **future accelerator** if we ever need broad-  Sheets/Docs/Chat coverage — but for Gmail+Contacts it's overkill.+  Sheets/Docs/Chat coverage, but for Gmail+Contacts it's overkill.  The code is structured so a `gws` backend *could* be slotted behind the same subcommands later; today the win is zero new runtime dependencies and a clean migration from adom-gmail.@@ -64,12 +41,12 @@ later; today the win is zero new runtime dependencies and a clean migration from ## Onboarding a brand-new org (give a team its OWN Google client)  If a **non-Adom** org wants to get going on their own, they need their own Google OAuth client.-Don't make them slog through Google Cloud Console by hand — use the **`adom-google-onboarding`**+Don't make them slog through Google Cloud Console by hand, use the **`adom-google-onboarding`** skill, which drives the user's real logged-in browser (adom-desktop `nbrowser_*`) through the console, auto-fills the fields (especially the redirect URI), captures the new client, and wires it in RELAY mode (their secret stays `0600` on their box; Adom only lends a shared callback URL). Kick it off with `adom-google onboard --org <slug>` (or `--json` for the exact values). Adom-employees skip all this — they install `adom-google-adom` and just run `auth`.+employees skip all this, they install `adom-google-adom` and just run `auth`.  ## Install @@ -81,9 +58,9 @@ adom-wiki pkg install adom/adom-google     # copies the CLI to ~/.local/bin + sk skill in `~/.claude/skills/<name>/`. Runtime: Python 3 + `requests` (already in the container). The Gateway WebSocket handshake is implemented in the stdlib (no `ws`/`websockets` package). -## First-run setup — just run `adom-google auth` (no mode quiz)+## First-run setup, just run `adom-google auth` (no mode quiz) -When setting up adom-google for the first time (it's unconfigured — `adom-google setup` or any+When setting up adom-google for the first time (it's unconfigured, `adom-google setup` or any command prints a `Hint:` block), **do NOT ask the user any scope/mode question.** Full Workspace access is the default and the tool "just works." Simply run: @@ -97,16 +74,16 @@ chat.delete, chat.customemojis, chat.users.*`) plus the generic `adom-google api <url>` passthrough for **any** Google API. It prints a Google consent URL; the user clicks **Allow** once. -> ⚠ **Open the consent URL as ONE value — never unquoted in a shell.** It contains `&`; if you+> ⚠ **Open the consent URL as ONE value, never unquoted in a shell.** It contains `&`; if you > drop it into an unquoted shell/open command the shell truncates it at the first `&`, Google-> receives only `client_id`, and shows *"Error 400: invalid_request — missing response_type"*+> receives only `client_id`, and shows *"Error 400: invalid_request, missing response_type"* > (this bit real users). Safe: hand the URL to a browser tool with the URL **inside JSON** > (`adom-desktop nbrowser_open_window '{"sessionId":"gauth","url":"<URL>"}'`), or single-quote the > whole URL. The `adom-google auth` output prints the ready-to-paste safe command for you.  > 🖐 **Consent is the sanctioned exception to background-only browsing.** abe/`nbrowser_*` opens > windows in the BACKGROUND and [foreground-etiquette](/adom/foreground-etiquette) says never steal-> focus — correct almost everywhere, but a consent page is worthless until a human physically+> focus, correct almost everywhere, but a consent page is worthless until a human physically > clicks Allow, and a silent background window is easy to miss. So you MAY raise the consent > window, on one condition: **announce it first, never raise silently.** >@@ -119,21 +96,21 @@ user clicks **Allow** once. > taskbar (`browser_alert_window`), letting them come to it. Sessions survive dropped sockets and > last ~60 minutes, so there is genuinely no rush. Never foreground anything else in the flow. -> ⏱ **The user has ~60 minutes to click Allow — do NOT rush them.** The gateway keeps the+> ⏱ **The user has ~60 minutes to click Allow, do NOT rush them.** The gateway keeps the > sign-in session alive for the full hour, and since v0.8.6 the session also SURVIVES a dropped > WebSocket: the CLI reconnects and claims the result, so a wifi blip, a sleeping laptop, or the > ingress killing an idle socket no longer costs the user their sign-in. (Before that fix a > disconnect silently destroyed the session and Allow showed "Session Expired" minutes in.) If a > "Session Expired" page ever does appear, just re-run `adom-google auth`. -*(Advanced/rare: `adom-google auth --safe` grants narrower create-only scopes — only the+*(Advanced/rare: `adom-google auth --safe` grants narrower create-only scopes, only the Drive/Sheets/Docs/Slides files this app makes, no "delete all". Do NOT prompt for it; use it only if the user explicitly asks to minimize scopes.)* -## Auth — one consent, long-lived refresh token+## Auth, one consent, long-lived refresh token  ```bash-adom-google auth            # authorize (FULL Workspace access — the default; ~60 min to approve)+adom-google auth            # authorize (FULL Workspace access, the default; ~60 min to approve) adom-google auth --safe     # advanced: narrower create-only scopes (no delete-all) adom-google auth --manual   # fallback: desktop-client paste flow adom-google auth-code '<code>'   # finish --manual@@ -152,17 +129,17 @@ confidential-client **token broker**, so the client secret never reaches user co 2. The user approves in their browser (their own Google Workspace account). 3. Google redirects to the Gateway `/callback`; the Gateway **does the code→token exchange    itself** (using the secret it alone holds) and pushes `{type:"tokens", …}` over the WS.-4. The container stores only the **refresh token** `0600` — no client secret. Later refreshes+4. The container stores only the **refresh token** `0600`, no client secret. Later refreshes    go through the gateway's `POST /refresh`, so the secret stays on the gateway forever. -**The package ships NO provider** — no gateway URL, no client_id, nothing Adom-specific. The+**The package ships NO provider**, no gateway URL, no client_id, nothing Adom-specific. The provider (`{gateway_url, client_id, app}`) is resolved at runtime, later wins: bundled `provider.json` → legacy `credentials.json` → `~/.config/adom-google/provider.json` → env (`OAUTH_GATEWAY_URL`, `ADOM_GOOGLE_CLIENT_ID/SECRET/APP`). With none configured, every command prints the NO_PROVIDER hint. Adom employees install the private **`adom-google-adom`** package, which writes the Adom provider (gateway + Internal client_id) to `~/.config/adom-google/provider.json`; self-hosters run `adom-google provider set --gateway <url> --client-id <id>`. The client secret is-never in any package — in broker mode it lives only on the gateway's `creds/<app>.json` (0600).+never in any package, in broker mode it lives only on the gateway's `creds/<app>.json` (0600).  If the Gateway is down, `auth --manual` uses a desktop-client loopback paste flow (that one exchanges locally and does need a client secret via `init`).@@ -170,10 +147,10 @@ exchanges locally and does need a client secret via `init`). ### Getting auth as a NON-Adom user (this tool is public)  The code is open source; the OAuth app + gateway are account-specific. If a user is **not**-on Adom's Workspace, the bundled Internal client won't let them sign in — Google blocks+on Adom's Workspace, the bundled Internal client won't let them sign in, Google blocks non-adom.inc accounts at consent. Two options to surface to them: -1. **Managed by Adom (easiest):** email **[email protected]** — Adom stands up a dedicated+1. **Managed by Adom (easiest):** email **[email protected]**, Adom stands up a dedicated    OAuth gateway container for their org (~$5/mo) wired to a Google client for their domain;    they then get the full adom-google capability set, same as the Adom team. 2. **Self-host (DIY):** fork the repo; create their own Google Cloud project + OAuth client@@ -187,7 +164,7 @@ non-adom.inc accounts at consent. Two options to surface to them: ## Commands  ```bash-# Generic passthrough — ANY Google API, signed with the user's token. This is the workhorse:+# Generic passthrough, ANY Google API, signed with the user's token. This is the workhorse: # Sheets/Docs/Slides/Drive/Calendar/Chat/Tasks/YouTube all go through here, zero per-API code. adom-google api <url> [-X METHOD] [-d '<json>'|@file] [-q key=val ...] [--raw] #   e.g. create a sheet:   adom-google api -X POST https://sheets.googleapis.com/v4/spreadsheets -d '{"properties":{"title":"BOM"}}'@@ -205,7 +182,7 @@ adom-google contacts create --name "First Last" [--org O] [--title T] \ adom-google contacts list [--limit N] [--json] adom-google contacts search <query> [-v] [--json] -# Google Chat — posts as the SIGNED-IN USER (their own identity, NO attribution prefix).+# Google Chat, posts as the SIGNED-IN USER (their own identity, NO attribution prefix). # This is distinct from `adom-gchat`, which posts via an org webhook with "(on behalf of …)". adom-google chat spaces [--json]                          # list your spaces (id · type · name) adom-google chat dm <email> [--json]                      # resolve the 1:1 DM space with a person@@ -235,24 +212,24 @@ adom-google accounts   # list all configured accounts (work + personal, isolated ```  **Requested ≠ granted.** `status` prints what auth ASKED for; `scopes` asks Google's tokeninfo-what was actually GRANTED — always gate on `scopes --require <scope>` before building against an+what was actually GRANTED, always gate on `scopes --require <scope>` before building against an API. And tokeninfo can't tell you whether the API is *enabled* in the OAuth client's GCP project: to probe real capability **without creating anything**, send a deliberately invalid body to the-target method — a missing scope fails 403 `ACCESS_TOKEN_SCOPE_INSUFFICIENT` *before* body+target method, a missing scope fails 403 `ACCESS_TOKEN_SCOPE_INSUFFICIENT` *before* body validation, while a granted scope fails 400 `INVALID_ARGUMENT` (proof the call would work). -## Multiple accounts (v0.8.0+) — e.g. work + personal, fully isolated+## Multiple accounts (v0.8.0+), e.g. work + personal, fully isolated  `adom-google --account <name> <cmd>` (or `ADOM_GOOGLE_ACCOUNT=<name>`) runs ANY command against a named account stored at `~/.config/adom-google/accounts/<name>/` with its OWN refresh token, mode,-and provider.json (inherits the default account's provider unless overridden — a Workspace-Internal+and provider.json (inherits the default account's provider unless overridden, a Workspace-Internal OAuth client can't sign in a gmail.com user, so a personal account typically needs its own External-client via `--account <name> provider set`). The default (unnamed) account keeps the original paths —+client via `--account <name> provider set`). The default (unnamed) account keeps the original paths - existing setups are untouched. First-run auth for a new account follows the SAME ask-the-user each account authorizes independently: `adom-google --account personal auth`.  **`chat` vs [adom-gchat](/adom/adom-gchat):** adom-google posts as **you** and does the-**full Chat lifecycle** — post, read, edit, delete, react — as your own Google identity+**full Chat lifecycle**, post, read, edit, delete, react, as your own Google identity (clean, no prefix). For an **attributed bot post** to a team space (deploys/alerts as "Kel (on behalf of john)"), use **[adom-gchat](/adom/adom-gchat)**, the post-only webhook bot. Rule of thumb: posting *as yourself* ⇒ `adom-google chat`; an *attributed bot announcement* ⇒@@ -265,7 +242,7 @@ requested at any tier. Tokens issued before v0.8.8 need one re-auth to pick the  We use Google Contacts to store **service identities** (portal/account metadata for an API vendor), e.g. the JPMorgan Payments developer portal. Keep secrets (client secrets, portal-passwords) in a password manager — **never** in a contact's notes.+passwords) in a password manager, **never** in a contact's notes.  ## Adding Drive / Calendar @@ -283,4 +260,4 @@ and the `_request()` helper are already generic.  See [MIGRATION.md](MIGRATION.md). Short version: `adom-gmail` is now a forwarding shim; its old `~/.config/adom-gmail/config.json` is auto-imported on first `adom-google` run-(gmail.readonly only — run `adom-google auth` once to add the `contacts` scope).+(gmail.readonly only, run `adom-google auth` once to add the `contacts` scope).
VERSION+1−1
@@ -1 +1 @@-0.8.17+0.8.18
package.json+8−17
@@ -1,6 +1,6 @@ {   "slug": "adom-google",-  "version": "0.8.17",+  "version": "0.8.18",   "type": "app",   "license": "MIT",   "description": "Drive your whole Google Workspace from the terminal: Gmail, Contacts, Drive, Sheets, Docs, Slides, Calendar, Tasks, Google Chat, plus a generic `api` passthrough to hit ANY Google API. Connect a personal @gmail (or another org's Google account) with ZERO setup via Adom's hosted Connector (`adom-google connect-personal`), or bring your own token-broker gateway (`provider set`): Adom staff use the private adom-google-adom provider. One-click OAuth, no client secret on your machine.",@@ -12,8 +12,12 @@     "google-sheets",     "google-docs",     "google-slides",+    "google-drive",     "google-calendar",+    "google-tasks",+    "google-contacts",     "google-chat",+    "google-api",     "cli",     "ai-agent"   ],@@ -32,7 +36,7 @@   "hero": {     "path": "screenshots/hero-google-v5.png"   },-  "discovery_pitch": "Drive your whole Google Workspace from the terminal through your AI: read Gmail (and its attachments), search Drive, build Sheets/Docs/Slides, manage Calendar & Tasks, and post to Google Chat as yourself. One OAuth, any Google API.",+  "discovery_pitch": "Write a Google Doc, make a Google Sheet, build a Google Slides deck, read and send Gmail, search Google Drive, create a Google Calendar event, manage Google Tasks, and post to Google Chat, all from your terminal through your AI. One OAuth, any Google API, plus a zero-setup personal Gmail.",   "discovery_triggers": [     "read my gmail",     "read my email",@@ -62,20 +66,7 @@     "connect my personal gmail",     "use adom-google with my personal gmail",     "connect my gmail account",-    "adom-google",-    "gchat",-    "gchat me",-    "google chat",-    "dm me",-    "DM the user",-    "message me",-    "ping me",-    "notify me",-    "send me a message",-    "private message",-    "message the user privately",-    "reach me on chat",-    "chat me"+    "adom-google"   ],   "dependencies": {},   "scripts": {@@ -120,5 +111,5 @@       "prompt": "post to Google Chat that the boards shipped"     }   ],-  "brief": "Drive your whole Google Workspace from the terminal, run by your AI. Google Chat DMs (THE private channel for pinging your user: chat send --to <email>), Gmail, Drive, Sheets, Docs, Slides, Calendar, Contacts, plus a raw API passthrough."+  "brief": "Drive your whole Google Workspace from the terminal, run by your AI. Read Gmail, write Google Docs, build Google Sheets and Google Slides, search Google Drive, manage Google Calendar and Google Tasks, and post to Google Chat, plus any Google API and a zero-setup personal Gmail." }\ No newline at end of file
page.json+14−43
@@ -3,8 +3,8 @@   "type": "app",   "slug": "adom-google",   "title": "Adom Google",-  "brief": "Drive your whole Google Workspace from the terminal, run by your AI. Google Chat DMs (THE private channel for pinging your user: chat send --to <email>), Gmail, Drive, Sheets, Docs, Slides, Calendar, Contacts, plus a raw API passthrough.",-  "version": "0.8.17",+  "brief": "Drive your whole Google Workspace from the terminal, run by your AI. Read Gmail, write Google Docs, build Google Sheets and Google Slides, search Google Drive, manage Google Calendar and Google Tasks, and post to Google Chat, plus any Google API and a zero-setup personal Gmail.",+  "version": "0.8.18",   "visibility": "public",   "install": {     "binary_name": "adom-google"@@ -20,10 +20,7 @@     "cli",     "ai-agent",     "open-source",-    "mit",-    "google-chat",-    "gchat",-    "dm"+    "mit"   ],   "license": "MIT",   "hero": {@@ -59,7 +56,10 @@   "discovery_triggers": [     "read my gmail",     "read my email",+    "read my emails",+    "read my gmails",     "check my email",+    "check my gmail",     "read an email attachment",     "gmail attachment",     "search my google drive",@@ -80,44 +80,11 @@     "set up google workspace",     "connect my google account",     "connect my personal gmail",-    "adom-google",-    "gchat",-    "read gchat",-    "check gchat",-    "read a gchat thread",-    "look at the gchat thread",-    "read my google chat",-    "read a chat thread",-    "catch up on a chat thread",-    "what did people say in chat",-    "summarize a chat thread",-    "who said what in the space",-    "send a calendar invite",-    "send an invite",-    "invite the whole company",-    "invite my team to an event",-    "put it on everyone's calendar",-    "schedule a meeting with the team",-    "company roster",-    "list everyone at the company",-    "who works here",-    "look up a coworker's email",-    "google workspace directory",-    "everyone in the org",-    "gchat me",-    "google chat",-    "dm me",-    "DM the user",-    "message me",-    "ping me",-    "notify me",-    "send me a message",-    "private message",-    "message the user privately",-    "reach me on chat",-    "chat me"+    "use adom-google with my personal gmail",+    "connect my gmail account",+    "adom-google"   ],-  "discovery_pitch": "Drive your whole Google Workspace from the terminal through your AI: read Gmail (and its attachments), search Drive, build Sheets/Docs/Slides, manage Calendar & Tasks, and post to Google Chat as yourself. One OAuth, any Google API.",+  "discovery_pitch": "Write a Google Doc, make a Google Sheet, build a Google Slides deck, read and send Gmail, search Google Drive, create a Google Calendar event, manage Google Tasks, and post to Google Chat, all from your terminal through your AI. One OAuth, any Google API, plus a zero-setup personal Gmail.",   "metadata": {     "hero_path": "screenshots/hero-google-v5.png",     "hero_type": "image"@@ -136,8 +103,12 @@     "google-sheets",     "google-docs",     "google-slides",+    "google-drive",     "google-calendar",+    "google-tasks",+    "google-contacts",     "google-chat",+    "google-api",     "cli",     "ai-agent"   ],