Skip to content

Google operator CLIs: dapier gmail, gdrive, calendar - #36

Merged
alexeygrigorev merged 4 commits into
mainfrom
dapier-wt-gmail-gdrive-1104
Nov 4, 2026
Merged

alexeygrigorev merged 4 commits into
mainfrom
dapier-wt-gmail-gdrive-1104

Conversation

@alexeygrigorev

Copy link
Copy Markdown
Member

What

Operator CLI nouns dapier gmail, dapier gdrive, dapier calendar over new operator-gated /api/agent/gmail|gdrive|calendar/* routes, following the existing dapier slack agent-proxy pattern (connection tokens stay server-side; every route operator-gated + audited).

Account selection (all three nouns): --account <email-or-connection-id> on every operation — matched by verified email, connection id, or unique fragment. With several connected Google accounts the choice is required; ambiguous or missing selections are rejected naming the candidates. No silent defaults, no wrong-account writes.

Gmail: search/list/read messages, threads, labels, attachments (save by id or bulk), mark read/unread/archive/trash/label, drafts create/update/delete with attachments, direct send with attachments, replies as direct send or --draft reply draft (In-Reply-To/References, Re: subject, original threadId; reply-all excludes self), send existing draft.

Drive: list/search/meta, download/export (Google-native → PDF/CSV/PNG, --export-mime), upload (file/stdin), update (rename/content), copy, move, share (permissions), trash (recoverable, --permanent destroys), restore, mkdir.

Calendar: calendar list, events list/search in bounded windows (--time-min/--time-max required, ≤ 366 days, occurrences expanded), free/busy, create/update/delete with attendees, RRULE recurrence, timezones, reminder overrides, attachment links, all-day form, or reviewed JSON via --event-file. Guest email is explicit: writes carry Google's sendUpdates, defaulting to none.

Previews: write verbs accept --preview — the exact JSON request body prints (payloads shown as sizes, not bytes), nothing is sent.

Safety

  • No live sends/mutations in tests: fakes at the transport seam only.
  • Secrets: tokens never leave the server; errors relay provider detail without token material (tested).
  • Granted scopes honored as-is — insufficient grants fail with the provider's 403 plus a dapier connections edit --scopes hint.

Tests

  • tests/test_google_account_selection.py — listing, id/email/fragment resolution, ambiguity rejection, disconnected-duplicate handling, no-account errors.
  • tests/test_gmail_gdrive_ops_cli.py — wrong-account isolation (per-connection bearers), provider errors (403 hint, 404), MIME construction (Unicode subject/body, binary attachment round-trip), reply threading (In-Reply-To/References/reply-all), draft lifecycle, modify label mapping, drive multipart upload, export/download, share/trash/restore/move semantics, route gating/audit/error mapping.
  • tests/test_gmail_gdrive_cli.py, tests/test_calendar_agent_cli.py — thin-client path/body shapes, --preview sends nothing, calendar window bounds and sendUpdates discipline.
  • Full suite: make test green (4792 passed).

Parity

No console surface added; the console manages the underlying Google connections via /api/admin/* as before. These nouns follow the dapier slack operator-agent category; parity audit re-run (no console call to the new routes, no console-only action introduced).

/api/agent/gmail/* and /api/agent/gdrive/* operator routes over the stored
Google connections, mirroring the slack agent-proxy pattern: token stays
server-side, every route is operator-gated and audited. Account selection
is the single gate (google_accounts): --account matches a verified email,
connection id, or unique fragment; multiple connected accounts require the
explicit choice and ambiguity is rejected with the candidates — never a
silent default.

Gmail: search/list/read messages, threads, labels, attachments, drafts
create/update/delete/send with attachments, direct send, replies as direct
send or reply draft (In-Reply-To/References/Re:/threadId preserved,
reply-all minus self), mark read/unread/archive/trash/label. MIME rides
the engine's raw builder. Drive: list/search/meta, download/export
(native docs to PDF/CSV/PNG), multipart upload, update, copy, move,
share, trash (recoverable or --permanent), restore, mkdir; files move as
base64 through the JSON API. Granted scopes are honored as-is — provider
403s relay with a connections-edit --scopes hint.
Thin JSON clients over the new agent routes (the slack pattern): every
command carries --account, bodies ride stdin/--file, attachments are read
from disk as base64 (PATH[;MIME]), downloads land on disk, pagination is
bounded with --page-token passthrough, and write verbs accept --preview —
the exact request body prints with payloads shown as sizes, nothing is
sent. gdrive covers list/search/meta/download/upload/update/copy/move/
share/trash/restore/mkdir; gmail covers accounts/search/list/read/
attachment/threads/labels/mark/send/reply/draft.
/api/agent/calendar/* and dapier calendar: accounts, the account's
calendar list, events list/search in bounded windows (time_min/time_max
required, inverted ranges rejected, span capped at 366 days, occurrences
expanded in start order), one-event reads, free/busy, and event
create/update/delete — structured fields (summary, ISO start/end,
timezone, attendees, RRULE recurrence, reminder overrides, attachment
links, all-day) or a reviewed JSON event resource via --event-file, never
both. Guest email is explicit: every write carries Google's sendUpdates
and defaults to none, so invitations never go out by accident; sendUpdates
is validated against all/externalOnly/none. Same account-selection gate,
operator gating, audit, and provider-403 scope hint as gmail/gdrive.
Document the new nouns, the explicit --account rules, scope-honoring
behavior, sendUpdates discipline, --preview, and drive restore; register
the new command groups in the dapier-cli skill with recipes.
@alexeygrigorev
alexeygrigorev merged commit 5a41d22 into main Nov 4, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant