app
Adom Google
Public Made by Adomby adom
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.
← Commit history
Publish 0.8.26
6 files changed
+131−103
SKILL.md+1−1@@ -44,7 +44,7 @@ If a **non-Adom** org wants to get going on their own, they need their own Googl 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 (the bridge's `nbrowser_*` verbs) 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).+it with `adom-google org register`: Adom's gateway verifies the caller is an org member, checks the client with Google and holds the secret, and the non-secret half goes on `<org>/org-settings`, so every other member just runs `auth`. Kick it off with `adom-google onboard --org <slug>` (or `--json` for the exact values). Adom employees skip all this: adom-google finds Adom's provider in `adom/org-settings`, so they just run `auth`.
VERSION+1−1@@ -1 +1 @@-0.8.25+0.8.26
install.shpackage.json+1−1@@ -1,6 +1,6 @@ { "slug": "adom-google",- "version": "0.8.25",+ "version": "0.8.26", "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.",
page.json+4−5@@ -4,7 +4,7 @@ "slug": "adom-google", "title": "Adom Google", "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.25",+ "version": "0.8.26", "visibility": "public", "install": { "binary_name": "adom-google"@@ -94,7 +94,7 @@ "skills": [], "hero_path": "screenshots/hero-google-v5.png", "hero_type": "image",- "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. Open source, ships with NO OAuth provider baked in: point it at your own token-broker gateway (`provider set`), buy a managed one, or: Adom staff: install the private adom-google-adom provider. One-click OAuth, no client secret on your machine. First run asks safe mode (no delete) vs full mode (incl. delete).",+ "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.", "keywords": [ "adom-google", "google-workspace",@@ -118,8 +118,6 @@ "uninstall": "./uninstall.sh" }, "files": [- "bin/adom-google",- "bin/adom-gmail", "SKILL.md", "skills/adom-google-onboarding/SKILL.md", "README.md",@@ -128,7 +126,8 @@ "provider.example.json", "install.sh", "uninstall.sh",- "package.json"+ "package.json",+ "VERSION" ], "org": "adom" }
skills/adom-google-onboarding/SKILL.md+124−95@@ -1,78 +1,93 @@ --- name: adom-google-onboarding-description: "Hand-hold a brand-new ORG through standing up its OWN Google Workspace OAuth client so adom-google works for their whole team — read mail, post Google Chat on their behalf, build Slides/Sheets/Docs, search Drive. You drive the user's REAL logged-in browser (adom-desktop nbrowser_* extension; pup native-mode fallback) to Google Cloud Console, AUTO-FILL the fields (especially the error-prone redirect URI), capture the new client_id + client_secret, and wire it in RELAY mode (their secret stays 0600 on their box; Adom only lends its shared callback URL — it never sees the secret). Use when a NON-Adom org wants to get going themselves. Triggers — onboard my org/company to adom-google, set up google workspace for my team, configure google oauth for my company, get my company on adom-google, self-serve google setup, we want to use adom-google at our org, set up the google cloud project, create our oauth client. (For an Adom EMPLOYEE, skip all this — they install adom-google-adom and just run auth. This skill is for everyone else.)"+description: "Hand-hold the FIRST person in a new org through standing up that org's OWN Google Workspace OAuth client, ONCE, so adom-google works for the WHOLE team: read mail, post Google Chat on their behalf, build Slides/Sheets/Docs, search Drive. You drive the user's real logged-in browser (adom-bridge nbrowser_* extension; pup native-mode fallback) through Google Cloud Console, auto-fill the fields (especially the error-prone redirect URI), capture the new client_id + client_secret, and register it with `adom-google org register`: Adom's OAuth gateway verifies org membership (their wiki login), checks the client with Google, and holds the secret; the non-secret half goes on the org's members-only wiki page <org>/org-settings, so every other member just runs `adom-google auth` with nothing to set up. Use when a NON-Adom org wants to get going. Triggers: onboard my org/company to adom-google, set up google workspace for my team, configure google oauth for my company, get my company on adom-google, self-serve google setup, we want to use adom-google at our org, set up the google cloud project, create our oauth client, set up adom-google for everyone at my company. (Adom employees skip all this: Adom is already set up in adom/org-settings, so they install adom-google and run auth.)" --- -# adom-google — Org Onboarding (drive the browser, auto-configure Google)+# adom-google: Org Onboarding (set it up once, the whole org benefits) -Goal: take a new org from *nothing* to *"the AI is reading our mail, posting in Chat, and building-decks"* in one guided sitting. The hard part — creating a Google Cloud OAuth client — is normally a-20-field console slog. You do it FOR them: drive their real browser, auto-fill every field, pause only-for the clicks Google forces a human to make. Pristine, billboard-grade setup.+Goal: take a new org from *nothing* to *"everyone's AI is reading our mail, posting in Chat, and+building decks"* with ONE person doing ONE guided sitting. The hard part, creating a Google Cloud+OAuth client, is normally a 20-field console slog. You do it FOR them: drive their real browser,+auto-fill every field, and pause only for the clicks Google forces a human to make.++## 0 · Is this org already set up? (check before anything else)++```bash+adom-wiki whoami # which wiki orgs is this user in? note the org handle+adom-google org status --org <org> # gateway: registered? org settings: present?+adom-google provider discover # if the org is set up, this wires this box and you are DONE+```++If `org status` shows **registered** and **org settings: present**, someone already did this for the+org. Do not create a second client. Run `adom-google auth` and stop. ## The architecture you're wiring (know this cold) -- The org creates **their own** Google Cloud project + OAuth client (Internal to their Workspace).-- Their OAuth client's **Authorized redirect URI = Adom's shared gateway `/callback`** — a public,- multi-tenant **relay**. Any org can register a one-time `state` over its WebSocket and get the auth- code relayed back. Near-zero load; no per-org config on the gateway.-- The org's **client secret stays on the org's own container** (`~/.config/adom-google/provider.json`,- `0600`) in **RELAY mode** (`broker:false`). Adom's gateway only relays the `code`; the org's CLI- exchanges it and refreshes locally with their own secret. **Adom never holds their secret.**-- Result: the org owns everything; Adom just lends a callback URL. (Adom *employees* use BROKER mode- via `adom-google-adom`, where the secret lives on the gateway — different package, not this flow.)+- The org creates **its own** Google Cloud project + OAuth client, **Internal** to its Workspace.+ Internal means no Google verification and no CASA assessment, and full Gmail/Drive access works+ immediately. That is the whole reason to do this instead of using Adom's hosted Connector.+- The client's **Authorized redirect URI = Adom's shared gateway `/callback`** (`redirect_uri` from+ `onboard --json`).+- **`adom-google org register`** hands the client to the gateway ONCE, for the whole org:+ 1. the gateway asks the wiki who the caller is (their own wiki token) and **refuses anyone who is+ not a member of that org**;+ 2. it asks Google that the client_id + secret pair is real, and that `/callback` is an authorized+ redirect URI on it, and refuses otherwise with the exact fix;+ 3. it stores the secret (`creds/org-<org>.json`, `0600`) and from then on does every token exchange+ and refresh for that org (**broker mode**). No member's box ever holds the secret.+- The **non-secret half** (`client_id`, gateway, app `org-<org>`, mode, Workspace domain, who set it+ up) is written to the org's members-only wiki page **`<org>/org-settings`**+ (`adom-wiki settings get <org> google-oauth`). adom-google reads it on first run, so **every other+ member just installs adom-google and runs `adom-google auth`**.+- Opt-out: an org that will not let Adom's gateway hold its secret can use+ `adom-google onboard finish --relay ...`. The secret then stays `0600` on THIS box only, and+ teammates cannot use it (each would need the secret themselves). Say that trade-off plainly. ## Get the exact values FIRST (single source of truth) ```bash-adom-google onboard --org <slug> --json+adom-google onboard --org <org> --json ``` -Returns `redirect_uri`, `enable_apis_url` (one click → all 9 APIs), `apis[]`, `scopes_safe/full`,+Returns `redirect_uri`, `enable_apis_url` (one click enables all 9 APIs), `apis[]`, scopes, `consent` (User type = **Internal**, app name), and `console{}` deep links. **Every field you type-comes from here** — never hand-author a redirect URI or API id.+comes from here.** Never hand-author a redirect URI or API id. ## Which browser surface -You need the user's **real, logged-in Google admin session**. Use the **extension** (`nbrowser_*`) —-it IS their everyday Chrome/Edge with their Workspace login + device enrollment already active. Pup-Chrome-for-Testing has no logins; pup *native-mode* is the fallback if the extension is unavailable.+You need the user's **real, logged-in Google admin session**. Use the **extension** (`nbrowser_*`):+it IS their everyday Chrome/Edge with their Workspace login and device enrollment already active. A+plain pup window has no logins; pup *native-mode* is the fallback if the extension is unavailable. ```bash-adom-desktop --target AdomLapper nbrowser_status # connected? which browser?+adom-bridge nbrowser_status # connected? which browser? ``` -If "not connected": the target profile has no open Chrome window — ask the user to open Chrome (any-window) in the profile that's signed into their Workspace, then `nbrowser_rescan`. Fallback to pup:-`adom-desktop browser_open_window '{"sessionId":"gcp","nativeBrowser":"chrome","nativeProfile":"Default","url":"https://console.cloud.google.com"}'`.+If "not connected": the target profile has no open Chrome window. Ask the user to open Chrome (any+window) in the profile signed into their Workspace, then `nbrowser_rescan`. Fallback to pup:+`adom-bridge pup_open_window '{"sessionId":"gcp","nativeBrowser":"chrome","nativeProfile":"Default","url":"https://console.cloud.google.com"}'`. -> ⛔ **MANDATORY PREFLIGHT — profiles first, then YOUR OWN window. Never the user's tabs.**-> 1. `nbrowser_profiles` (read-only) → every connected `browser:email` profile across Chrome AND Edge.-> 2. Pick the profile whose **email matches the Google account this setup is FOR** (org onboarding →-> the Workspace admin's profile; a personal-account setup → that personal profile, e.g.-> `chrome:[email protected]`). The user almost certainly has a browser profile tied to that account-> — find it, never guess, and never use "whatever profile is active."+> ⛔ **MANDATORY PREFLIGHT: profiles first, then YOUR OWN window. Never the user's tabs.**+> 1. `nbrowser_profiles` (read-only) lists every connected `browser:email` profile across Chrome and Edge.+> 2. Pick the profile whose **email is the Workspace admin this setup is FOR**. Find it, never guess,+> never use "whatever profile is active." > 3. `nbrowser_open_window {profile:"<key>", url:"https://console.cloud.google.com", sessionId:"gcp"}`-> → a BACKGROUND window pinned to that profile. **ALL console driving happens in this window via-> its sessionId.** A bare `nbrowser_navigate` (no sessionId) targets the user's ACTIVE tab — the-> extension (v0.0.21+) refuses it (`no_target`/`not_owned`), and it once destroyed a user's other-> AI threads. There is no override.-> 4. Screenshot the new window and verify the top-right account matches. Wrong account = wrong-> project owner, the #1 way this goes sideways.+> opens a BACKGROUND window pinned to that profile. **All console driving happens in this window+> via its sessionId.** A bare `nbrowser_navigate` (no sessionId) targets the user's ACTIVE tab; the+> extension refuses it (`no_target`/`not_owned`), and it once destroyed a user's other AI threads.+> 4. Screenshot the new window and verify the top-right account. Wrong account = wrong project owner,+> the #1 way this goes sideways. ## Drive loop (repeat for every step) -1. `nbrowser_navigate {sessionId:"gcp", url:"<deep link from the config>"}` — always in YOUR window.-2. `nbrowser_screenshot` → **read it** (don't assume the DOM; Google Console mutates constantly).+1. `nbrowser_navigate {sessionId:"gcp", url:"<deep link from the config>"}`, always in YOUR window.+2. `nbrowser_screenshot` and **read it** (Google Console mutates constantly; do not assume the DOM). 3. Act with **trusted** input: `nbrowser_type {selector,text}` to fill, `nbrowser_click {selector}` to- click. Selectors are modal-aware; prefer visible text / `aria-label` (`[aria-label="Create"]`,- `button:has-text("Enable")`). If a selector misses (`elementsMatched:0`), screenshot again and try a- coordinate click (`nbrowser_input_dispatch {type:"click",x,y}`) off the screenshot.+ click. Prefer visible text / `aria-label`. If a selector misses (`elementsMatched:0`), screenshot+ again and use a coordinate click (`nbrowser_input_dispatch {type:"click",x,y}`). 4. `nbrowser_screenshot` again to confirm the result before moving on.-5. Narrate with `nbrowser_caption {text:"Step 3/6 — enabling APIs…"}` so the user follows along, and- `desktop_bring_to_front {titleContains:"Google Cloud"}` so they can watch.-6. **Shotlog every screenshot** with a description and hand the user the phone URL (per the all-screenshots- rule) so they can audit each step from their phone.+5. Narrate with `nbrowser_caption {text:"Step 3/6: enabling APIs"}` so the user can follow along.+6. **Shotlog every screenshot** with a description and hand the user the phone URL. ### Always pause for the human on these (Google requires a real click; never fake it) - Account chooser / re-auth prompts.@@ -82,79 +97,93 @@ Show a screenshot, say exactly what to click, wait, then continue. Auto-fill eve ## The six steps -**1 · Project.** Navigate `console.project_create`. Auto-fill the project name-(`type` into the Project name field) e.g. `adom-workspace`. Pause for the user to click **Create**.-Wait for the create to finish (poll screenshots / the notifications bell), then make sure the new-project is the **active** project (project picker, top bar).+**1 · Project.** Navigate `console.project_create`. Auto-fill the project name (e.g.+`adom-workspace`). Pause for the user to click **Create**. Wait for it to finish, then make sure the+new project is the **active** project (project picker, top bar). -**2 · Enable all nine APIs in one click.** Navigate to `enable_apis_url` (it pre-selects all 9). Screenshot.-Click **Enable**. This is the single biggest time-saver — one screen instead of nine. Confirm "APIs enabled."+**2 · Enable all nine APIs in one click.** Navigate to `enable_apis_url` (it pre-selects all 9).+Click **Enable**. Confirm "APIs enabled." -**3 · OAuth consent screen → Internal.** Navigate `console.consent_screen`. Choose **User type = Internal**-(critical — Internal = only their Workspace can sign in, and gives long-lived refresh tokens). Auto-fill-App name (`consent.app_name`), user support email (their admin), developer contact (their admin). Save.-*(Internal apps need no verification and skip the scary "unverified app" screen.)*+**3 · OAuth consent screen: Internal.** Navigate `console.consent_screen`. Choose **User type =+Internal** (only their Workspace can sign in; long-lived refresh tokens; no verification). Auto-fill+App name (`consent.app_name`), user support email and developer contact (their admin). Save. -**4 · Create the OAuth client.** Navigate `console.create_client`. Application type = **Web application**.-Auto-fill Name (`consent.client_name`). Then the load-bearing move: **AUTO-FILL the Authorized redirect-URI** with `redirect_uri` from the config — paste it verbatim, never make the user type it (one wrong-char = `redirect_uri_mismatch` later). Screenshot to prove it matches. Pause for the user to click **Create**.+**4 · Create the OAuth client.** Navigate `console.create_client`. Application type = **Web+application**. Auto-fill Name (`consent.client_name`). Then the load-bearing move: **auto-fill the+Authorized redirect URI** with `redirect_uri` from the config, verbatim. Screenshot to prove it+matches. Pause for the user to click **Create**. -**5 · Capture the credentials.** The post-create dialog shows **Client ID** + **Client secret**. Read them:-`nbrowser_eval` to scrape the dialog text, and ALSO screenshot as a backup. Verify the client_id ends in-`.apps.googleusercontent.com` and the secret looks like `GOCSPX-…`. Then store them — **secret to the CLI,-never echoed into chat**:+**5 · Register it for the whole org.** The post-create dialog shows **Client ID** and **Client+secret**. Scrape them with `nbrowser_eval` (screenshot as a backup). Verify the client_id ends in+`.apps.googleusercontent.com` and the secret looks like `GOCSPX-…`. Pass the secret on **stdin**,+never on the command line and never into chat: ```bash-adom-google onboard finish --org <slug> --client-id "<id>" --client-secret "<secret>"+printf '%s\n' "$SECRET" | adom-google org register --org <org> --client-id "<id>" --domain <workspace-domain> ``` -This writes `provider.json` (`0600`, `broker:false`). The secret lives only on the user's box.+It prints three OKs: the gateway accepted it (membership, client and redirect all verified), the+non-secret half was written to `<org>/org-settings`, and this box is wired. If the gateway refuses,+the message names the fix:+- `not_a_member`: the user's wiki account is not in that org. Fix org membership first.+- `client_rejected_by_google (invalid_client)`: the id/secret pair is wrong. Re-copy both.+- `redirect_not_registered`: add the printed `redirect_uri` to the client's Authorized redirect URIs.+- `SETTINGS_NOT_FOUND` warning on step 2: the org has no settings page yet. The warning prints the+ one-time `adom-wiki page create org-settings --org <org> ...` command; run it, then re-run+ `org register` (it is safe to repeat; the gateway reports `replaced`). -**6 · Chat app config (once).** Navigate `console.chat_config` (Chat API → Configuration). Set an app-name + avatar + "Make available to … your domain." Without this, `chat spaces` 404s "Chat app not found."-This is the one step pure-data tools skip; do it so `adom-google chat` works out of the box.+**6 · Chat app config (once).** Navigate `console.chat_config` (Chat API > Configuration). Set an+app name and avatar, and "Make available to … your domain." Without this, `chat spaces` 404s with+"Chat app not found." ## Authorize + verify (the payoff) -ASK the user **safe vs full** (use AskUserQuestion — never pick for them; safe = no "delete all" on the-consent screen, the recommended default). Then:+No scope question to ask: full Workspace access is the default. ```bash-adom-google auth # safe (or: adom-google auth --full)+adom-google auth ``` -Print the consent URL; open it in a NEW background window in the matching profile-(`nbrowser_open_window {profile:"<key>", url:"<consent url>", sessionId:"consent"}` — never a bare-`nbrowser_navigate` at their tabs), screenshot the consent, pause-for **Allow**. The gateway relays the code; the CLI exchanges it locally. Then prove it works — small,-real, delightful:+Get the consent URL in front of the user the way the `auth` output describes (a clickable link is+always safe). The gateway brokers the exchange. Then prove it works, small and real:++```bash+adom-google status # "Auth: broker (secret on gateway, none here)"+adom-google chat spaces # lists their spaces+adom-google api https://www.googleapis.com/drive/v3/about?fields=user # signed in as …+```++End with ONE magic moment, e.g. compose a Google Doc or post a Chat message.++## Tell them how teammates join (the whole point)++Every other member of the org, on their own box: ```bash-adom-google status # mode + "Auth: relay (your secret 0600 here…)"-adom-google chat spaces # lists their spaces-adom-google api https://www.googleapis.com/drive/v3/about?fields=user # "you're signed in as …"+adom-wiki pkg install adom/adom-google+adom-google auth ``` -End by showing them ONE magic moment — e.g. compose a Google Doc or post a Chat message — so the-"oh, this is a game-changer" lands immediately.+adom-google finds the org's provider in `<org>/org-settings` by itself. They must be members of the+org on the wiki (that is what gates the settings page). Someone in several set-up orgs picks one with+`adom-google provider discover --org <org>`. ## Gotchas-- **Wrong Google account** under the project → everything downstream is wrong. Verify in step 0.-- **redirect_uri mismatch** at `auth` time → the client's Authorized redirect URI ≠ `redirect_uri`. Re-open- the OAuth client, fix the field (auto-fill again), save, retry `auth`. This is why step 4 auto-fills.-- **Internal vs External**: always Internal for an org. External forces app verification + a scary screen.+- **Wrong Google account** under the project: everything downstream is wrong. Verify in the preflight.+- **redirect_uri mismatch**: `org register` catches it before anything is stored. Fix the client's+ Authorized redirect URI (auto-fill again), save, re-run.+- **Internal vs External**: always Internal for an org. External forces verification and CASA. - **Extension "not connected"** = no open Chrome window in the signed-in profile. Open one, `nbrowser_rescan`.-- **Secrets**: never print the client_secret or refresh token to the transcript/logs. Pass the secret- straight into `onboard finish`. Screenshots of the secret dialog → shotlog them privately; don't paste- the secret text into chat.-- **Don't auto-click Create/Allow/Publish** — Google wants a human there, and the user *should* see what- they're approving. Auto-fill up to the button; let them press it.+- **Secrets**: never print the client_secret or refresh tokens into the transcript or logs. Screenshots+ of the secret dialog go to the shotlog privately; never paste the secret text into chat.+- **Rotating the secret**: create a new secret in the console, then re-run `org register` with it.+ `adom-google org unregister --org <org>` removes the gateway's copy (members' tokens stop refreshing).+- **Don't auto-click Create/Allow/Publish.** Auto-fill up to the button; let them press it. ## Why this matters (say it to the user)-Once they're authed, their AI can read their email, post Google Chat **on their behalf**, compose Google-Docs, build Google Sheets and Slides, and search Google Drive — all from the terminal. That's the-workflow unlock teams fall in love with. You just made the painful part painless.+One person spends one sitting, and from then on everyone in the org has an AI that can read their+email, post Google Chat on their behalf, compose Google Docs, build Sheets and Slides, and search+Drive, with nothing to set up. You just made the painful part painless, once, for the whole team. Related: [adom-google](/adom/adom-google) (the CLI + full command set) · the `driving-the-extension`-skill (nbrowser_* deep reference) · [adom-gchat](/adom/adom-gchat) (attributed bot posts).+skill (nbrowser_* deep reference) · `adom-wiki settings` (the org settings page).