Motivation
Today the CLI is great for interactive/ergonomic use, but scripts that integrate with gws end up needing the raw Google API JSON shape (Gmail API, Chat API, People API payloads as documented by Google). Two friction points hit programmatic consumers:
- Output is flattened/transformed — fields are renamed, base64 bodies are decoded, header arrays are collapsed, etc. Great for humans, lossy for machines that already speak the API.
- Not every API param is exposed as a flag — e.g. Chat API's
filter parameter supports expressions like createTime > "2025-01-01T00:00:00Z" or spaceType = "DIRECT_MESSAGE" that aren't accessible from the current flag surface.
Adding a small programmatic mode would unblock scripting/automation use cases without disturbing the existing ergonomic UX.
Proposed changes
1. Global --raw flag
When --raw is set on a resource command, return the unmodified Google API response JSON instead of the flattened/pretty shape. Implementation-wise: skip the transform layer between SDK response and stdout.
2. --params <json> escape hatch on resource commands
Accept a JSON object that maps to the underlying API request's parameters. Merges with / overrides any flag-derived params. Example:
gws chat messages list --params '{\"parent\":\"spaces/AAA\",\"pageSize\":100,\"filter\":\"createTime > \\\"2025-01-01T00:00:00Z\\\"\"}' --raw
This avoids having to flag-ify every API parameter and unblocks features like Chat filters immediately.
3. Pagination under --raw
--all should aggregate across pages while preserving API shape:
- Concatenate top-level list fields (
messages, spaces, members, ...) across pages
- Drop
nextPageToken from the final aggregated output
NDJSON (one page object per line) is an acceptable alternative — easy for callers to merge.
Endpoints that matter most for programmatic use
| Command |
Underlying API |
Notes |
gmail list |
users.messages.list |
needs q, maxResults, pagination |
gmail thread <id> |
users.threads.get |
needs full payload tree: headers, parts[*].body.data (base64), internalDate, labelIds |
chat spaces list |
spaces.list |
needs filter (DM filter especially) |
chat members list |
spaces.members.list |
needs parent, pagination |
chat messages list |
spaces.messages.list |
needs parent, filter (createTime), pagination |
people get |
people.get |
needs resourceName, personFields |
These cover the highest-value programmatic use cases. Other resources can follow the same pattern incrementally.
Backwards compatibility
--raw is opt-in; default behavior unchanged.
--params is additive; existing flags continue to work and take precedence (or merge — designer's call).
- No change to auth, config, or command structure.
Why this matters
Right now, programmatic consumers either (a) reach past the CLI directly to the Google SDK, or (b) reverse-engineer the flattened shape and write fragile decoders. Both defeat the value of having a unified CLI. --raw + --params makes the CLI a first-class building block for scripts and pipelines without compromising the human-friendly default.
Motivation
Today the CLI is great for interactive/ergonomic use, but scripts that integrate with
gwsend up needing the raw Google API JSON shape (Gmail API, Chat API, People API payloads as documented by Google). Two friction points hit programmatic consumers:filterparameter supports expressions likecreateTime > "2025-01-01T00:00:00Z"orspaceType = "DIRECT_MESSAGE"that aren't accessible from the current flag surface.Adding a small programmatic mode would unblock scripting/automation use cases without disturbing the existing ergonomic UX.
Proposed changes
1. Global
--rawflagWhen
--rawis set on a resource command, return the unmodified Google API response JSON instead of the flattened/pretty shape. Implementation-wise: skip the transform layer between SDK response and stdout.2.
--params <json>escape hatch on resource commandsAccept a JSON object that maps to the underlying API request's parameters. Merges with / overrides any flag-derived params. Example:
This avoids having to flag-ify every API parameter and unblocks features like Chat filters immediately.
3. Pagination under
--raw--allshould aggregate across pages while preserving API shape:messages,spaces,members, ...) across pagesnextPageTokenfrom the final aggregated outputNDJSON (one page object per line) is an acceptable alternative — easy for callers to merge.
Endpoints that matter most for programmatic use
gmail listusers.messages.listq,maxResults, paginationgmail thread <id>users.threads.getheaders,parts[*].body.data(base64),internalDate,labelIdschat spaces listspaces.listfilter(DM filter especially)chat members listspaces.members.listparent, paginationchat messages listspaces.messages.listparent,filter(createTime), paginationpeople getpeople.getresourceName,personFieldsThese cover the highest-value programmatic use cases. Other resources can follow the same pattern incrementally.
Backwards compatibility
--rawis opt-in; default behavior unchanged.--paramsis additive; existing flags continue to work and take precedence (or merge — designer's call).Why this matters
Right now, programmatic consumers either (a) reach past the CLI directly to the Google SDK, or (b) reverse-engineer the flattened shape and write fragile decoders. Both defeat the value of having a unified CLI.
--raw+--paramsmakes the CLI a first-class building block for scripts and pipelines without compromising the human-friendly default.