google-apis-cli
v0.1.0
Published
One CLI (gapi) for Google Ads, GTM, GA4 (Admin+Data), Search Console, and Indexing. One login, all scopes, read+write.
Downloads
174
Maintainers
Readme
google-apis-cli (gapi)
gapi is one CLI for Google Ads, GTM, GA4 (Admin + Data), Search Console, and Indexing.
- One login. One refresh token. All scopes. Read and write.
- One runtime dependency:
google-auth-library(Google). Nothing third-party. - All network calls use native
fetch. - TypeScript 7 (strict), Node 24. Node runs
.tsdirectly (type-stripping) — no build step in dev. Biome for lint + format. pnpm 12 (corepack-pinned) for package management. - It replaces the 4 fragmented Google MCPs in vmCODE.
Command shape: gapi <api> <resource> <verb> [--json|--raw]
Install
npm i -g google-apis-cli # installs the `gapi` binNeeds Node ≥ 24 — gapi ships as TypeScript and runs on Node's native type-stripping, so there is no build step and no bundle.
Start here
gapi auth setup # store your own OAuth Desktop client (id + secret)
gapi auth login # one consent, all 13 scopes
gapi auth status # account, refresh token, granted scopesFirst run, or a console step fails? docs/SETUP.md walks the five steps and
the traps in each. gapi is multi-tenant: every operator brings their own Cloud project.
OAuth App Verification
gapi asks for sensitive scopes (analytics.manage.users, tagmanager.manage.*, adwords, webmasters).
- Testing mode (default) works for all commands, but the refresh token expires every ~7 days — re-run
gapi auth loginwhen it does. - Production removes that expiry, but Google requires OAuth App Verification first: a privacy-policy URL, a homepage, and a reviewed consent screen. Review timeline is not guaranteed.
Testing mode is enough to use the CLI; Production is the no-expiry target. Until verification passes, stay in Testing and re-auth on the ~7-day cadence.
Flow
The project has 3 parts. A call moves through them in order.
| Part | File | Function |
|---|---|---|
| auth | src/auth.ts | gapi auth login gets one OAuth consent. It stores the refresh token in ~/.config/gapi/credentials.json (mode 600). getAccessToken() refreshes the access token. Only part with real handlers. |
| manifest | src/apis/*.ts | Each API is data, not code: resource → verb → { httpMethod, pathTemplate, scopes, requiredHeaders?, decoder?, listKey?, query? }. One file per surface. A glob finds them. No central registry. Row/seam types live in src/types.ts. |
| executor seam | src/rest.ts | execute(manifestRow, params, { tokenProvider, fetch }) builds the URL, adds headers, follows pagination, applies the decoder, and formats --json/--raw. All network calls pass here. It is the one test seam. |
bin/gapi.ts parses argv. The dispatch, --help, and URL resolution all come from the manifests.
Where to look
| For | Read |
|---|---|
| Onboarding a Google account: consent, Ads access level, customer IDs, runtime errors | docs/SETUP.md |
| What the CLI does, decisions, scopes, endpoints, risks | docs/SPEC.md — behavior source of truth |
| How work is tracked (issues, blocking edges) | docs/agents/issue-tracker.md |
| Triage labels | docs/agents/triage-labels.md |
| Domain vocabulary (CONTEXT.md, ADRs) | docs/agents/domain.md |
| Real commands and scripts | package.json, gapi --help |
Add an API or a resource
- New resource = one manifest row.
- New API = one new
src/apis/<api>.tsfile (default-exportsatisfies Manifest). The glob finds it. No shared file changes. - Accept rule — coverage-oracle: the self-check fails if the manifest does not cover 100% of the discovery-doc resources.
- Test through the executor seam. Inject a fake
tokenProviderandfetch. Use no real network. - Before commit:
pnpm typecheck(tsc --noEmit) ·pnpm lint(biome) ·pnpm test. - Fix lint by fixing the code it flags — never
biome --unsafe, never disable a rule to clear it.
Agent skills
| Topic | Rule | Detail |
|---|---|---|
| Issue tracker | GitHub issues via gh | docs/agents/issue-tracker.md |
| Triage labels | 5 canonical labels (string == role) | docs/agents/triage-labels.md |
| Domain docs | Single-context (CONTEXT.md + docs/adr/) | docs/agents/domain.md |
