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.6.0
11 files changed
+479−58
README.md+77−17@@ -57,11 +57,11 @@ source for the gateway: [`adom/service-oauth`](https://wiki.adom.inc/adom/servic --- -## Getting your own auth — three ways+## Getting your own auth — pick your path 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. Pick your path:+client_id) is the only account-specific piece. Four ways to get one: ### 1. You're at Adom → just use it ```bash@@ -72,17 +72,29 @@ adom-google auth # one Allow click — done (`adom-google-adom` is a tiny private, Adom-only package that writes the Adom provider; the public `adom-google` package contains none of Adom's infrastructure.) -### 2. Any other company / team → let Adom host your OAuth (easiest) 💵-Email **[email protected]**. We spin up a dedicated OAuth gateway container for your org-(~**$5/month** for the service container), wire it to a Google client for your domain, and-your whole team gets the **full adom-google capability set — exactly what the Adom team-uses every day**. You install the CLI, click Allow once, and you're driving your Workspace.-No Cloud-console wrestling, no secret management.+### 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+your own browser through it**, auto-filling every field, while you watch and click the couple+of buttons Google reserves for a human: -### 3. Self-host it → fully DIY (free, more setup)-Fork this repo and stand up your own:+```bash+adompkg install adom-google # the open-source CLI+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+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 💵+Email **[email protected]**. We stand up a dedicated OAuth gateway container for your org+(~**$5/month**) wired to a Google client for your domain; your whole team gets the full+capability set with zero console wrestling and zero secret management on your side.++### 4. Self-host the gateway too → fully DIY (free, most control)+Fork this repo and run the open-source gateway yourself: - A Google Cloud project + OAuth client (Internal to your Workspace for long-lived tokens),- with the Gmail/Drive/Sheets/Docs/Slides/Calendar/Tasks/People/Chat APIs enabled.+ with all nine APIs enabled (the onboarding flow's one-click enable URL does this for you). - The open-source OAuth gateway ([`adom/service-oauth`](https://wiki.adom.inc/adom/service-oauth)) on a small container, with your client's secret in `creds/<app>.json`. - Point the CLI at **your** gateway + client (writes `~/.config/adom-google/provider.json`, 0600):@@ -91,14 +103,62 @@ Fork this repo and stand up your own: adom-google provider show # verify what's resolved (env > provider.json > bundled) ``` -**Enabling the APIs** — in the Cloud Console go to **APIs & Services → Library**, search each-one, and click **Enable**. You want all nine:+---++## 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+> 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.++**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+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+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+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.)++### Step by step++```bash+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).+The AI reads `adom-google onboard --json` for the machine-readable version it types into the console.++++**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.++++**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.++++**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`), asks you safe-vs-full,+then runs `adom-google auth` — you click **Allow** once and you're live. -+ -The Chat API also needs a one-time "Chat app" configuration (name/avatar/description) in the-Cloud console's **Chat API → Configuration** tab — otherwise `spaces.*` calls 404 with "Chat-app not found." See the [SKILL.md](/blob/app/adom-google/SKILL.md) for the exact steps.+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. ---
bin/adom-google+180−3@@ -66,6 +66,82 @@ SCOPES_FULL = [ def _scopes_for(mode): return SCOPES_FULL if mode == "full" else SCOPES_SAFE ++# ---------------------------------------------------------------- org onboarding+# Adom's shared OAuth gateway. Its /callback is a PUBLIC, MULTI-TENANT relay: any org can+# register a one-time `state` over the WebSocket and have its auth code relayed back — zero+# per-org config on the gateway, near-zero load (a callback is one tiny request). A new org+# points its OWN Google OAuth client's redirect_uri at this callback and keeps its OWN client+# secret on its OWN container (RELAY mode, broker:false) — Adom never sees the org's secret.+# (Adom employees use BROKER mode instead, where the secret lives on the gateway; see provider.)+ADOM_SHARED_GATEWAY = "https://oauth-9mycwxij7tif.adom.cloud"++# The nine Google APIs adom-google drives — (console "enable" id, human label).+ONBOARD_APIS = [+ ("gmail.googleapis.com", "Gmail API"),+ ("people.googleapis.com", "People API (Contacts)"),+ ("drive.googleapis.com", "Google Drive API"),+ ("sheets.googleapis.com", "Google Sheets API"),+ ("docs.googleapis.com", "Google Docs API"),+ ("slides.googleapis.com", "Google Slides API"),+ ("calendar-json.googleapis.com", "Google Calendar API"),+ ("tasks.googleapis.com", "Google Tasks API"),+ ("chat.googleapis.com", "Google Chat API"),+]++def _enable_apis_url(project=None):+ # One URL that enables ALL nine APIs in one click (the GCP "enable multiple APIs" flow).+ u = "https://console.cloud.google.com/flows/enableapi?apiid=" + ",".join(a for a, _ in ONBOARD_APIS)+ return u + ("&project=" + project if project else "")++def _onboard_config(org=None):+ """The exact, canonical values an org needs to wire up their own Google OAuth client.+ Consumed by the onboarding skill (via `onboard --json`) to auto-fill the Cloud Console."""+ gw = (_provider().get("gateway_url") or ADOM_SHARED_GATEWAY).rstrip("/")+ label = "Adom Workspace CLI" + (f" — {org}" if org else "")+ return {+ "org": org,+ "gateway_url": gw,+ "redirect_uri": gw + "/callback", # paste verbatim into the OAuth client+ "javascript_origins": [], # none needed (no browser JS client)+ "enable_apis_url": _enable_apis_url(),+ "apis": [{"id": a, "label": l} for a, l in ONBOARD_APIS],+ "scopes_safe": SCOPES_SAFE,+ "scopes_full": SCOPES_FULL,+ "consent": { # OAuth consent screen settings+ "user_type": "Internal", # Internal → only the org's Workspace, long-lived tokens+ "app_name": label,+ "client_type": "Web application", # the OAuth client TYPE to create+ "client_name": label,+ },+ "console": { # deep links the skill drives the browser to+ "home": "https://console.cloud.google.com/",+ "project_create": "https://console.cloud.google.com/projectcreate",+ "consent_screen": "https://console.cloud.google.com/auth/overview",+ "credentials": "https://console.cloud.google.com/apis/credentials",+ "create_client": "https://console.cloud.google.com/apis/credentials/oauthclient",+ "chat_config": "https://console.cloud.google.com/apis/api/chat.googleapis.com/hangouts-chat",+ },+ }++ONBOARD_HINT = """Hint: ORG ONBOARDING — drive the user's OWN browser through Google Cloud Console.+Hint: This is the hand-held setup that gives a brand-new org their own Google OAuth client so+Hint: adom-google works for their whole team. Use the `adom-google-onboarding` skill, which+Hint: drives the user's REAL logged-in browser (adom-desktop `nbrowser_*`; pup native-mode as+Hint: fallback) and AUTO-FILLS the Cloud Console fields. Get the exact values to type with:+Hint: adom-google onboard --json # redirect_uri, the 9 APIs, scopes, consent settings+Hint: Steps the skill performs (screenshotting + pausing for the user's approval clicks):+Hint: 1. Create/select a Google Cloud project.+Hint: 2. Enable all nine APIs in one click (the `enable_apis_url`).+Hint: 3. Configure the OAuth consent screen (User type = Internal; app name from config).+Hint: 4. Create an OAuth client (type = Web application); AUTO-FILL Authorized redirect URI+Hint: with the gateway `redirect_uri` (the error-prone field — never make the user type it).+Hint: 5. Capture the client_id + client_secret from the final dialog.+Hint: 6. Save them: adom-google onboard finish --org <slug> --client-id <id> --client-secret <secret>+Hint: 7. Ask the user safe vs full mode, then: adom-google auth+Hint: The secret stays on THE USER'S container (0600) and rides Adom's shared callback in RELAY+Hint: mode — Adom never holds it."""+ TOKEN_URL = "https://oauth2.googleapis.com/token" AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth" GMAIL_API = "https://gmail.googleapis.com/gmail/v1/users/me"@@ -94,7 +170,7 @@ def _provider(): if _PROVIDER is not None: return _PROVIDER p = {"app": DEFAULT_APP, "gateway_url": None, "gateway_ws": None,- "client_id": None, "client_secret": None}+ "client_id": None, "client_secret": None, "broker": None} def merge(path, keymap): if not os.path.exists(path): return@@ -117,6 +193,17 @@ def _provider(): p["client_id"] = e.get("ADOM_GOOGLE_CLIENT_ID", p["client_id"]) p["client_secret"] = e.get("ADOM_GOOGLE_CLIENT_SECRET", p["client_secret"]) p["app"] = e.get("ADOM_GOOGLE_APP", p["app"])+ # broker is a bool — merge() skips falsy values, so read it explicitly (later wins).+ for path in (BUNDLED_PROVIDER, PROVIDER_PATH):+ if os.path.exists(path):+ try:+ d = json.load(open(path))+ if "broker" in d:+ p["broker"] = bool(d["broker"])+ except Exception:+ pass+ if "ADOM_GOOGLE_BROKER" in e:+ p["broker"] = e["ADOM_GOOGLE_BROKER"].strip().lower() in ("1", "true", "yes", "on") if p["gateway_url"]: p["gateway_url"] = p["gateway_url"].rstrip("/") p["gateway_ws"] = (p["gateway_ws"] or "wss://" + p["gateway_url"].split("://", 1)[-1]).rstrip("/")@@ -770,6 +857,9 @@ def cmd_auth(a): "scope": " ".join(scopes), "access_type": "offline", "prompt": "consent", "state": state, }+ # broker=False → RELAY mode: the gateway just relays the code back; THIS container exchanges+ # it locally with the org's own secret. (None/True → broker mode: gateway exchanges.)+ want_exchange = pr.get("broker") is not False auth_link = AUTH_URL + "?" + urllib.parse.urlencode(params) print(f"OAuth Gateway flow — {mode.upper()} mode" + (" (full Drive/Sheets/Docs/Slides + delete)" if mode == "full"@@ -783,7 +873,7 @@ def cmd_auth(a): msg = _minimal_ws_roundtrip( pr["gateway_ws"], send_msgs=[{"type": "register", "state": state, "provider": "google",- "app": pr["app"], "exchange": True}],+ "app": pr["app"], "exchange": want_exchange}], want_types=("tokens", "callback"), ) except Exception as e:@@ -878,6 +968,73 @@ def cmd_provider_show(a): print(f"source: {src}") +def cmd_onboard(a):+ """Guided org onboarding. Prints the exact plan + canonical values (or --json for the+ skill that drives the browser). The actual Cloud-Console clicking is done by the+ `adom-google-onboarding` skill via the native browser; this command supplies the truth."""+ org = getattr(a, "org", None)+ cfg = _onboard_config(org)+ if getattr(a, "json", False):+ print(json.dumps(cfg, indent=2))+ return+ R = cfg["redirect_uri"]+ print("┌─ adom-google · onboard your org to Google Workspace ─────────────────────────┐")+ print("│ One-time setup: your team gets its OWN Google OAuth client so the AI can │")+ print("│ read mail, post Chat on your behalf, build Slides/Sheets/Docs, search Drive. │")+ print("│ Your client SECRET stays on YOUR machine (0600). Adom only lends its public │")+ print("│ callback URL — it never sees your secret. │")+ print("└──────────────────────────────────────────────────────────────────────────────┘\n")+ print("The AI can drive your real browser through every step below and auto-fill the")+ print("fields (recommended) — or you can do it by hand. Either way, the exact values:\n")+ print("1) Create / pick a Google Cloud project:")+ print(" " + cfg["console"]["project_create"])+ print("\n2) Enable all nine APIs in ONE click:")+ print(" " + cfg["enable_apis_url"])+ for ap in cfg["apis"]:+ print(f" • {ap['label']}")+ print("\n3) OAuth consent screen → User type = Internal")+ print(" " + cfg["console"]["consent_screen"])+ print(f" App name: {cfg['consent']['app_name']}")+ print("\n4) Create an OAuth client → Application type = Web application")+ print(" " + cfg["console"]["create_client"])+ print(" ⚠ Authorized redirect URI — paste EXACTLY (this is the gateway callback):")+ print(" " + R)+ print("\n5) Copy the Client ID + Client secret it shows, then run:")+ print(" adom-google onboard finish --org <your-org-slug> \\")+ print(" --client-id <client-id> --client-secret <client-secret>")+ print("\n6) Pick safe vs full mode, then authorize:")+ print(" adom-google auth # one Allow click with your Workspace account")+ print("\n(Chat also needs a one-time Chat-app config: " + cfg["console"]["chat_config"] + ")")+ print("\n" + ONBOARD_HINT)+++def cmd_onboard_finish(a):+ """Record the org's freshly-created OAuth client and wire RELAY mode (secret stays local)."""+ gw = (a.gateway or ADOM_SHARED_GATEWAY).rstrip("/")+ prov = {}+ if os.path.exists(PROVIDER_PATH):+ try:+ with open(PROVIDER_PATH) as f:+ prov = json.load(f)+ except Exception:+ prov = {}+ prov["gateway_url"] = gw+ prov["client_id"] = a.client_id+ prov["client_secret"] = a.client_secret # the org's OWN secret, on the org's OWN box+ prov["app"] = a.org+ prov["broker"] = False # relay mode: gateway relays, we exchange locally+ os.makedirs(CFG_DIR, exist_ok=True)+ fd = os.open(PROVIDER_PATH, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)+ with os.fdopen(fd, "w") as f:+ json.dump(prov, f, indent=2)+ os.chmod(PROVIDER_PATH, 0o600)+ print(f"✅ Saved provider → {PROVIDER_PATH} (0600, RELAY mode).")+ print(f" org/app: {a.org}")+ print(f" gateway: {gw} (shared callback; your secret never leaves this box)")+ print(f" redirect_uri: {gw}/callback (must match your OAuth client)")+ print("\nNext: ask the user safe vs full, then `adom-google auth`.")++ def cmd_setup(a): # Canonical first-run entry point. Always prints the ask-the-user hint (even mid-config), # so the installing AI is reminded to ask the safe/full question before authorizing.@@ -897,7 +1054,8 @@ def cmd_status(a): print(f"OAuth client: {(cid[:18] + '…') if cid != '(none)' else cid} (from {src})") print(f"Refresh token: {'present' if cfg.get('refresh_token') else 'MISSING — run auth'}") print(f"Mode: {cfg.get('mode', '(unset)')} (safe = no 'delete all'; full = broad)")- print(f"Auth: {'broker (secret on gateway, none here)' if cfg.get('broker') else 'local (client secret in config)'}")+ relay = pr.get("broker") is False+ print(f"Auth: {'broker (secret on gateway, none here)' if cfg.get('broker') else ('relay (your secret 0600 here; Adom lends only its callback)' if relay else 'local (client secret in config)')}") print(f"Scopes: {', '.join(s.split('/')[-1] for s in (cfg.get('scopes') or [])) or '(none)'}") if cfg.get("_migrated_from"): print(f"Migrated from: {cfg['_migrated_from']} (gmail.readonly only — run `auth` to add contacts)")@@ -917,6 +1075,15 @@ AUTH (one time; one consent → long-lived refresh token, stored 0600): adom-google init <id> <secret> set an explicit desktop OAuth client (for --manual) adom-google status show mode + what's authorised (never prints secrets) +ONBOARD A NEW ORG (give your team its OWN Google OAuth client — the AI hand-holds you):+ adom-google onboard [--org <slug>] print the step-by-step plan + exact values to use+ adom-google onboard --json same, machine-readable (the onboarding skill reads this)+ adom-google onboard finish --org <slug> --client-id <id> --client-secret <secret>+ record your new client (RELAY mode: secret stays 0600+ on THIS box; rides Adom's shared callback URL)+ → The `adom-google-onboarding` skill drives your real browser through Google Cloud Console+ and auto-fills the fields. Then: pick safe/full, `adom-google auth`, done.+ GMAIL (read-only; migrated from adom-gmail): adom-google gmail attachments <messageId> list attachments + ids adom-google gmail read <messageId> <attId> [-o f] dump attachment bytes@@ -966,6 +1133,16 @@ def main(): s = sub.add_parser("status"); s.set_defaults(fn=cmd_status) s = sub.add_parser("setup"); s.set_defaults(fn=cmd_setup) + ob = sub.add_parser("onboard"); ob.add_argument("--org"); ob.add_argument("--json", action="store_true")+ ob.set_defaults(fn=cmd_onboard)+ obx = ob.add_subparsers(dest="sub")+ y = obx.add_parser("finish")+ y.add_argument("--org", required=True)+ y.add_argument("--client-id", dest="client_id", required=True)+ y.add_argument("--client-secret", dest="client_secret", required=True)+ y.add_argument("--gateway", help="override the shared callback gateway (advanced)")+ y.set_defaults(fn=cmd_onboard_finish)+ g = sub.add_parser("gmail"); gs = g.add_subparsers(dest="sub") x = gs.add_parser("attachments"); x.add_argument("message_id"); x.set_defaults(fn=cmd_gmail_attachments) x = gs.add_parser("read"); x.add_argument("message_id"); x.add_argument("attachment_id"); x.add_argument("-o", "--out"); x.set_defaults(fn=cmd_gmail_read)
docs/onboard-1-plan.pngadded⋯ 1 unchanged line ⋯
docs/onboard-2-enable-apis.pngadded⋯ 1 unchanged line ⋯
docs/onboard-3-redirect-uri.pngadded⋯ 1 unchanged line ⋯
docs/onboard-4-authorized.pngadded⋯ 1 unchanged line ⋯
install.sh+5−3@@ -17,13 +17,15 @@ source "$(adompkg sh-helpers)" chmod +x bin/adom-google bin/adom-gmail adompkg-link-bin adom-google # ~/.local/bin/adom-google -> $PWD/bin/adom-google adompkg-link-bin adom-gmail # deprecation shim -> adom-google-adompkg-link-skill adom-google # ~/.claude/skills/adom-google -> $PWD/skills/adom-google+adompkg-link-skill adom-google # ~/.claude/skills/adom-google -> $PWD/skills/adom-google+adompkg-link-skill adom-google-onboarding # the guided org-onboarding skill (drives the browser) case ":$PATH:" in *":$HOME/.local/bin:"*) ;; *) echo "[adom-google] NOTE: $HOME/.local/bin is not on PATH — add it to your shell rc." ;; esac echo "✅ adom-google installed. It ships with NO OAuth provider — set one up:"-echo " • self-host: adom-google provider set --gateway https://your-gateway --client-id <id>"-echo " • Adom staff: adompkg install adom-google-adom (drops the Adom provider)"+echo " • new org (self-serve): adom-google onboard (the AI drives your browser through Google setup)"+echo " • self-host (manual): adom-google provider set --gateway https://your-gateway --client-id <id>"+echo " • Adom staff: adompkg install adom-google-adom (drops the Adom provider)" echo " Then: 'adom-google setup' (asks safe vs full) and 'adom-google auth'."
package.json+6−1@@ -1,6 +1,6 @@ { "slug": "adom-google",- "version": "0.5.1",+ "version": "0.6.0", "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. 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).",@@ -38,6 +38,7 @@ "bin/adom-google", "bin/adom-gmail", "skills/adom-google/SKILL.md",+ "skills/adom-google-onboarding/SKILL.md", "README.md", "LICENSE", "MIGRATION.md",@@ -45,6 +46,10 @@ "docs/hero.png", "docs/enable-apis.png", "docs/chat-reads-email.png",+ "docs/onboard-1-plan.png",+ "docs/onboard-2-enable-apis.png",+ "docs/onboard-3-redirect-uri.png",+ "docs/onboard-4-authorized.png", "screenshots/hero.png", "install.sh", "uninstall.sh",
page.json+51−33@@ -1,22 +1,22 @@ {- "schema_version": 1,- "type": "app", "slug": "adom-google",- "title": "adom-google",- "brief": "Drive your whole Google Workspace from the terminal — Gmail, Drive, Sheets, Docs, Slides, Calendar, Tasks, Chat + any Google API.",- "version": "0.5.1",- "visibility": "public",- "install": {- "binary_name": "adom-google",- "install_dir": "",- "install_hint": "",- "version_cmd": ""- },- "author": {- "id": "6738b00a4dc87b8adbb0a5a3",- "name": "John Lauer",- "email": "[email protected]"- },+ "version": "0.6.0",+ "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. 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).",+ "keywords": [+ "adom-google",+ "google-workspace",+ "oauth",+ "gmail",+ "google-sheets",+ "google-docs",+ "google-slides",+ "google-calendar",+ "google-chat",+ "cli",+ "ai-agent"+ ], "tags": [ "google-workspace", "oauth",@@ -26,21 +26,39 @@ "open-source", "mit" ],- "license": "MIT",- "hero": {- "path": "docs/hero.png",- "type": "image"+ "install": {+ "binary_name": "adom-google" },- "sample_prompts": [],- "discovery_triggers": [],- "discovery_pitch": null,- "metadata": {- "hero_path": "docs/hero.png",- "hero_type": "image"+ "dependencies": {},+ "scripts": {+ "install": "./install.sh",+ "uninstall": "./uninstall.sh" },- "created_at": "2026-06-16T22:28:58.933Z",- "updated_at": "2026-06-17T00:57:47.322Z",- "skills": [],- "hero_path": "docs/hero.png",- "hero_type": "image"-}+ "files": [+ "bin/adom-google",+ "bin/adom-gmail",+ "skills/adom-google/SKILL.md",+ "skills/adom-google-onboarding/SKILL.md",+ "README.md",+ "LICENSE",+ "MIGRATION.md",+ "provider.example.json",+ "docs/hero.png",+ "docs/enable-apis.png",+ "docs/chat-reads-email.png",+ "docs/onboard-1-plan.png",+ "docs/onboard-2-enable-apis.png",+ "docs/onboard-3-redirect-uri.png",+ "docs/onboard-4-authorized.png",+ "screenshots/hero.png",+ "install.sh",+ "uninstall.sh",+ "package.json"+ ],+ "visibility": "public",+ "org": "adom",+ "author": {+ "name": "John Lauer",+ "email": "[email protected]"+ }+}
skills/adom-google-onboarding/SKILL.mdadded+149@@ -0,0 +1,149 @@+---+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 <company>, get my company on adom-google, self-serve google setup, "we want to use adom-google at <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.)+---++# adom-google — Org Onboarding (drive the browser, auto-configure Google)++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.++## 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.)++## Get the exact values FIRST (single source of truth)++```bash+adom-google onboard --org <slug> --json+```++Returns `redirect_uri`, `enable_apis_url` (one click → all 9 APIs), `apis[]`, `scopes_safe/full`,+`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.++## 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.++```bash+adom-desktop --target AdomLapper 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"}'`.++> ⚠ **Confirm the signed-in account first.** Drive to `https://console.cloud.google.com`, screenshot,+> and verify the top-right account is the user's **Workspace admin** (not a personal @gmail). Creating+> the project under the wrong account is the #1 way this goes sideways. If wrong, have them switch+> accounts in Chrome before continuing.++## Drive loop (repeat for every step)++1. `nbrowser_navigate` to the deep link from the config.+2. `nbrowser_screenshot` → **read it** (don't assume the DOM; Google Console mutates constantly).+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.+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.++### Always pause for the human on these (Google requires a real click; never fake it)+- Account chooser / re-auth prompts.+- The final **Create** on the OAuth client.+- The OAuth consent screen **Publish/Save** if a confirm dialog appears.+Show a screenshot, say exactly what to click, wait, then continue. Auto-fill everything *up to* these.++## 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).++**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."++**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.)*++**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**.++**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**:++```bash+adom-google onboard finish --org <slug> --client-id "<id>" --client-secret "<secret>"+```++This writes `provider.json` (`0600`, `broker:false`). The secret lives only on the user's box.++**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.++## 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:++```bash+adom-google auth # safe (or: adom-google auth --full)+```++Print the consent URL; drive their browser to it (`nbrowser_navigate`), screenshot the consent, pause+for **Allow**. The gateway relays the code; the CLI exchanges it locally. Then prove it works — small,+real, delightful:++```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 …"+```++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.++## 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.+- **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.++## 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.++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).
skills/adom-google/SKILL.md+11−1@@ -1,6 +1,6 @@ --- 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). On FIRST-RUN SETUP you MUST ask the user to pick safe mode (no delete) vs full mode (incl. delete) before authorizing. 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. 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, safe mode vs full mode, google oauth gateway broker, google api passthrough, self-host oauth, managed oauth container.+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). On FIRST-RUN SETUP you MUST ask the user to pick safe mode (no delete) vs full mode (incl. delete) before authorizing. 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, safe mode vs full mode, 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@@ -38,6 +38,16 @@ Gemini CLI, and `gcloud`. Conclusion: **call the REST APIs directly.** 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. +## 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`**+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`.+ ## Install `install.mjs` symlinks the CLI onto PATH automatically. Manual: