Skip to content

Commit 975c12a

Browse files
authored
feat(poll): add persisted Drive and Docs polling (#751)
* feat(poll): add persisted Drive and Docs polling * docs(poll): document persisted polling contract
1 parent 3f653c7 commit 975c12a

17 files changed

Lines changed: 1430 additions & 32 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
- Docs: prevent multi-paragraph Markdown range replacements from inheriting the matched paragraph's heading or list style. (#756) — thanks @sebsnyk.
1212
- Docs: preserve nested Markdown list levels as native bullets inside imported and updated table cells. (#749) — thanks @sebsnyk.
1313
- Gmail: add explicit `gmail archive --thread` semantics so IDs from thread search can archive every message in each thread. (#752) — thanks @sebsnyk.
14+
- Drive/Docs: add persisted polling for Drive changes and Docs comments, with bounded runs, filters, retry-safe cursors, and sequential JSON hooks. (#690, #751)
1415

1516
## 0.24.0 - 2026-06-11
1617

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,8 @@ gog calendar appointments
183183

184184
### Drive
185185

186-
Docs: [Drive audits](docs/drive-audits.md), [raw API dumps](docs/raw-api.md),
186+
Docs: [Drive audits](docs/drive-audits.md), [polling](docs/polling.md),
187+
[raw API dumps](docs/raw-api.md),
187188
[`gog drive`](docs/commands/gog-drive.md),
188189
[`drive changes`](docs/commands/gog-drive-changes.md),
189190
[`drive revisions`](docs/commands/gog-drive-revisions.md),
@@ -209,6 +210,7 @@ gog drive get <fileId> --fields 'id,name,mimeType,size,owners,emailAddress' --js
209210
# Track changes and audit activity.
210211
gog drive changes start-token
211212
gog drive changes list --token <token> --json
213+
gog drive changes poll --state-file ~/.local/state/gog/drive-changes.json --json
212214
gog drive revisions list <fileId> --all --json
213215
gog drive revisions get <fileId> <revisionId> --json
214216
gog drive activity query --file <fileId> --actions edit,share --from 2026-01-01T00:00:00Z --json
@@ -285,6 +287,7 @@ gog contacts dedupe --match email,phone,name --dry-run
285287

286288
Docs: [Google Docs editing](docs/docs-editing.md),
287289
[atomic Docs request batches](docs/docs-batch.md),
290+
[polling](docs/polling.md),
288291
[sed-style document edits](docs/sedmat.md),
289292
[`gog docs`](docs/commands/gog-docs.md).
290293

@@ -297,6 +300,7 @@ gog docs named-range create <docId> --name Status --at "Ready"
297300
gog docs insert-image <docId> --url https://example.com/chart.png --at end
298301
gog docs add-tab <docId> --title "Notes"
299302
gog docs tabs add <docId> --title "Notes"
303+
gog docs comments poll <docId> --state-file ~/.local/state/gog/doc-comments.json --json
300304
gog docs find-replace <docId> old new --tab "Notes" --dry-run
301305
gog docs raw <docId> --pretty
302306
```

docs/commands.generated.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ Generated from `gog schema --json`.
234234
- [`gog docs (doc) comments get (info,show) <docId> <commentId>`](commands/gog-docs-comments-get.md) - Get a comment by ID
235235
- [`gog docs (doc) comments list (ls) <docId> [flags]`](commands/gog-docs-comments-list.md) - List comments on a Google Doc
236236
- [`gog docs (doc) comments locate <docId> <commentId> [flags]`](commands/gog-docs-comments-locate.md) - Resolve a comment quote to Docs API index ranges
237+
- [`gog docs (doc) comments poll --state-file=STRING <docId> [flags]`](commands/gog-docs-comments-poll.md) - Poll new and modified comments with persisted state
237238
- [`gog docs (doc) comments reopen <docId> <commentId> [flags]`](commands/gog-docs-comments-reopen.md) - Reopen a previously resolved comment
238239
- [`gog docs (doc) comments reply (respond) <docId> <commentId> <content> [flags]`](commands/gog-docs-comments-reply.md) - Reply to a comment
239240
- [`gog docs (doc) comments resolve <docId> <commentId> [flags]`](commands/gog-docs-comments-resolve.md) - Resolve a comment (mark as done)
@@ -301,6 +302,7 @@ Generated from `gog schema --json`.
301302
- [`gog drive (drv) bulk update-role [flags]`](commands/gog-drive-bulk-update-role.md) - Change matching Drive permission roles across files
302303
- [`gog drive (drv) changes <command>`](commands/gog-drive-changes.md) - Track Drive changes for sync and automation
303304
- [`gog drive (drv) changes list (ls) --token=STRING [flags]`](commands/gog-drive-changes-list.md) - List Drive changes since a page token
305+
- [`gog drive (drv) changes poll --state-file=STRING [flags]`](commands/gog-drive-changes-poll.md) - Poll Drive changes with a persisted page token
304306
- [`gog drive (drv) changes start-token (token) [flags]`](commands/gog-drive-changes-start-token.md) - Get a Drive changes start page token
305307
- [`gog drive (drv) changes stop <channelId> <resourceId>`](commands/gog-drive-changes-stop.md) - Stop a Drive changes webhook channel
306308
- [`gog drive (drv) changes watch --token=STRING --webhook-url=STRING [flags]`](commands/gog-drive-changes-watch.md) - Watch Drive changes with a webhook channel

docs/commands/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Every `gog` command has a generated docs page. The source of truth is the live CLI schema; run `make docs-commands` after changing command names, flags, help text, aliases, or arguments.
44

5-
Generated pages: 629.
5+
Generated pages: 631.
66

77
## Top-level Commands
88

@@ -285,6 +285,7 @@ Generated pages: 629.
285285
- [gog docs comments get](gog-docs-comments-get.md) - Get a comment by ID
286286
- [gog docs comments list](gog-docs-comments-list.md) - List comments on a Google Doc
287287
- [gog docs comments locate](gog-docs-comments-locate.md) - Resolve a comment quote to Docs API index ranges
288+
- [gog docs comments poll](gog-docs-comments-poll.md) - Poll new and modified comments with persisted state
288289
- [gog docs comments reopen](gog-docs-comments-reopen.md) - Reopen a previously resolved comment
289290
- [gog docs comments reply](gog-docs-comments-reply.md) - Reply to a comment
290291
- [gog docs comments resolve](gog-docs-comments-resolve.md) - Resolve a comment (mark as done)
@@ -352,6 +353,7 @@ Generated pages: 629.
352353
- [gog drive bulk update-role](gog-drive-bulk-update-role.md) - Change matching Drive permission roles across files
353354
- [gog drive changes](gog-drive-changes.md) - Track Drive changes for sync and automation
354355
- [gog drive changes list](gog-drive-changes-list.md) - List Drive changes since a page token
356+
- [gog drive changes poll](gog-drive-changes-poll.md) - Poll Drive changes with a persisted page token
355357
- [gog drive changes start-token](gog-drive-changes-start-token.md) - Get a Drive changes start page token
356358
- [gog drive changes stop](gog-drive-changes-stop.md) - Stop a Drive changes webhook channel
357359
- [gog drive changes watch](gog-drive-changes-watch.md) - Watch Drive changes with a webhook channel
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# `gog docs comments poll`
2+
3+
> Generated from `gog schema --json`. Do not edit this page by hand; run `make docs-commands`.
4+
5+
Poll new and modified comments with persisted state
6+
7+
## Usage
8+
9+
```bash
10+
gog docs (doc) comments poll --state-file=STRING <docId> [flags]
11+
```
12+
13+
## Parent
14+
15+
- [gog docs comments](gog-docs-comments.md)
16+
17+
## Flags
18+
19+
| Flag | Type | Default | Help |
20+
| --- | --- | --- | --- |
21+
| `--access-token` | `string` | | Use provided access token directly (bypasses stored refresh tokens; token expires in ~1h) |
22+
| `-a`<br>`--account`<br>`--acct` | `string` | | Account email for API commands (gmail/calendar/chat/classroom/drive/drivelabels/docs/slides/contacts/tasks/people/sheets/forms/sites/appscript/analytics/searchconsole/youtube/photos) |
23+
| `--client` | `string` | | OAuth client name (selects stored credentials + token bucket) |
24+
| `--color` | `string` | auto | Color output: auto\|always\|never |
25+
| `--disable-commands` | `string` | | Comma-separated list of disabled commands; dot paths allowed |
26+
| `-n`<br>`--dry-run`<br>`--dryrun`<br>`--noop`<br>`--preview` | `bool` | | Do not make changes; print intended actions and exit successfully |
27+
| `--enable-commands` | `string` | | Comma-separated list of enabled command prefixes; dot paths allowed (restricts CLI) |
28+
| `--enable-commands-exact` | `string` | | Comma-separated list of exact enabled commands; dot paths allowed and parent commands do not enable children |
29+
| `-y`<br>`--force`<br>`--assume-yes`<br>`--yes` | `bool` | | Skip confirmations for destructive commands |
30+
| `--gmail-no-send` | `bool` | false | Block Gmail send operations (agent safety) |
31+
| `-h`<br>`--help` | `kong.helpFlag` | | Show context-sensitive help. |
32+
| `--home` | `string` | | Override gogcli config/data/state/cache root (equivalent to GOG_HOME) |
33+
| `--include-resolved`<br>`--resolved` | `bool` | | Include resolved comments |
34+
| `--interval` | `time.Duration` | 60s | Delay between polls |
35+
| `-j`<br>`--json`<br>`--machine` | `bool` | false | Output JSON to stdout (best for scripting) |
36+
| `--max`<br>`--limit` | `int64` | 100 | Max comments per API page |
37+
| `--max-iterations` | `int` | 0 | Stop after N polls; 0 runs until interrupted |
38+
| `--no-input`<br>`--non-interactive`<br>`--noninteractive` | `bool` | | Never prompt; fail instead (useful for CI) |
39+
| `--on-new` | `string` | | Trusted local shell command run for each comment; comment event JSON is provided on stdin |
40+
| `-p`<br>`--plain`<br>`--tsv` | `bool` | false | Output stable, parseable text to stdout (TSV; no colors) |
41+
| `--results-only` | `bool` | | In JSON mode, emit only the primary result (drops envelope fields like nextPageToken) |
42+
| `--select`<br>`--pick`<br>`--project` | `string` | | In JSON mode, select comma-separated fields (best-effort; supports dot paths). Desire path: use --fields for most commands. |
43+
| `--state-file` | `string` | | JSON file that stores the comment time watermark |
44+
| `-v`<br>`--verbose` | `bool` | | Enable verbose logging |
45+
| `--version` | `kong.VersionFlag` | | Print version and exit |
46+
| `--wrap-untrusted` | `bool` | false | In JSON/raw output, wrap fetched text fields in external untrusted-content markers |
47+
48+
## See Also
49+
50+
- [gog docs comments](gog-docs-comments.md)
51+
- [Command index](README.md)

docs/commands/gog-docs-comments.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ gog docs (doc) comments <command>
2121
- [gog docs comments get](gog-docs-comments-get.md) - Get a comment by ID
2222
- [gog docs comments list](gog-docs-comments-list.md) - List comments on a Google Doc
2323
- [gog docs comments locate](gog-docs-comments-locate.md) - Resolve a comment quote to Docs API index ranges
24+
- [gog docs comments poll](gog-docs-comments-poll.md) - Poll new and modified comments with persisted state
2425
- [gog docs comments reopen](gog-docs-comments-reopen.md) - Reopen a previously resolved comment
2526
- [gog docs comments reply](gog-docs-comments-reply.md) - Reply to a comment
2627
- [gog docs comments resolve](gog-docs-comments-resolve.md) - Resolve a comment (mark as done)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# `gog drive changes poll`
2+
3+
> Generated from `gog schema --json`. Do not edit this page by hand; run `make docs-commands`.
4+
5+
Poll Drive changes with a persisted page token
6+
7+
## Usage
8+
9+
```bash
10+
gog drive (drv) changes poll --state-file=STRING [flags]
11+
```
12+
13+
## Parent
14+
15+
- [gog drive changes](gog-drive-changes.md)
16+
17+
## Flags
18+
19+
| Flag | Type | Default | Help |
20+
| --- | --- | --- | --- |
21+
| `--access-token` | `string` | | Use provided access token directly (bypasses stored refresh tokens; token expires in ~1h) |
22+
| `-a`<br>`--account`<br>`--acct` | `string` | | Account email for API commands (gmail/calendar/chat/classroom/drive/drivelabels/docs/slides/contacts/tasks/people/sheets/forms/sites/appscript/analytics/searchconsole/youtube/photos) |
23+
| `--client` | `string` | | OAuth client name (selects stored credentials + token bucket) |
24+
| `--color` | `string` | auto | Color output: auto\|always\|never |
25+
| `--disable-commands` | `string` | | Comma-separated list of disabled commands; dot paths allowed |
26+
| `--drive`<br>`--drive-id` | `string` | | Shared drive ID for a shared-drive change log |
27+
| `-n`<br>`--dry-run`<br>`--dryrun`<br>`--noop`<br>`--preview` | `bool` | | Do not make changes; print intended actions and exit successfully |
28+
| `--enable-commands` | `string` | | Comma-separated list of enabled command prefixes; dot paths allowed (restricts CLI) |
29+
| `--enable-commands-exact` | `string` | | Comma-separated list of exact enabled commands; dot paths allowed and parent commands do not enable children |
30+
| `--filter-file` | `string` | | Only emit and invoke the hook for changes to this file ID |
31+
| `-y`<br>`--force`<br>`--assume-yes`<br>`--yes` | `bool` | | Skip confirmations for destructive commands |
32+
| `--gmail-no-send` | `bool` | false | Block Gmail send operations (agent safety) |
33+
| `-h`<br>`--help` | `kong.helpFlag` | | Show context-sensitive help. |
34+
| `--home` | `string` | | Override gogcli config/data/state/cache root (equivalent to GOG_HOME) |
35+
| `--include-removed` | `bool` | true | Include removed changes |
36+
| `--interval` | `time.Duration` | 60s | Delay between polls |
37+
| `-j`<br>`--json`<br>`--machine` | `bool` | false | Output JSON to stdout (best for scripting) |
38+
| `--max`<br>`--limit` | `int64` | 100 | Max changes per API page |
39+
| `--max-iterations` | `int` | 0 | Stop after N polls; 0 runs until interrupted |
40+
| `--no-input`<br>`--non-interactive`<br>`--noninteractive` | `bool` | | Never prompt; fail instead (useful for CI) |
41+
| `--on-change` | `string` | | Trusted local shell command run for each non-empty batch; batch JSON is provided on stdin |
42+
| `-p`<br>`--plain`<br>`--tsv` | `bool` | false | Output stable, parseable text to stdout (TSV; no colors) |
43+
| `--results-only` | `bool` | | In JSON mode, emit only the primary result (drops envelope fields like nextPageToken) |
44+
| `--select`<br>`--pick`<br>`--project` | `string` | | In JSON mode, select comma-separated fields (best-effort; supports dot paths). Desire path: use --fields for most commands. |
45+
| `--state-file` | `string` | | JSON file that stores the current Drive page token |
46+
| `-v`<br>`--verbose` | `bool` | | Enable verbose logging |
47+
| `--version` | `kong.VersionFlag` | | Print version and exit |
48+
| `--wrap-untrusted` | `bool` | false | In JSON/raw output, wrap fetched text fields in external untrusted-content markers |
49+
50+
## See Also
51+
52+
- [gog drive changes](gog-drive-changes.md)
53+
- [Command index](README.md)

docs/commands/gog-drive-changes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ gog drive (drv) changes <command>
1717
## Subcommands
1818

1919
- [gog drive changes list](gog-drive-changes-list.md) - List Drive changes since a page token
20+
- [gog drive changes poll](gog-drive-changes-poll.md) - Poll Drive changes with a persisted page token
2021
- [gog drive changes start-token](gog-drive-changes-start-token.md) - Get a Drive changes start page token
2122
- [gog drive changes stop](gog-drive-changes-stop.md) - Stop a Drive changes webhook channel
2223
- [gog drive changes watch](gog-drive-changes-watch.md) - Watch Drive changes with a webhook channel

docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ gog slides create-from-markdown "Weekly update" --content-file slides.md
4949
- **Wiring up automation.** [Automation](automation.md), [Safety Profiles](safety-profiles.md), and the bundled [`gog` skill](https://github.com/openclaw/gogcli/blob/main/.agents/skills/gog/SKILL.md). Discover the active contract and lock the binary down before handing it to an untrusted caller.
5050
- **Serving MCP tools.** [MCP server](mcp.md) exposes typed, allowlisted tools for agent clients without a generic command bridge.
5151
- **Discovering runtime contracts.** [Automation](automation.md) explains root help, schema metadata, safety controls, and stable exit codes.
52+
- **Polling local events.** [Drive and Docs polling](polling.md) persists cursors and optionally invokes trusted shell hooks.
5253
- **Persisting auth and state.** [Paths and State](paths.md) covers `GOG_HOME`, per-kind directories, XDG paths, and legacy compatibility.
5354
- **Running Workspace at scale.** [Auth Clients](auth-clients.md) for service accounts, named OAuth clients, and domain-wide delegation.
5455
- **Managing Workspace.** [Workspace Admin](workspace-admin.md) covers user creation, cleanup, organizational units, and group administration.

docs/polling.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
---
2+
summary: "Persisted Drive changes and Docs comment polling"
3+
read_when:
4+
- Running gog as a local Drive or Docs event poller
5+
- Wiring Drive changes or Docs comments to shell hooks
6+
---
7+
8+
# Drive and Docs polling
9+
10+
`gog` can poll Drive changes or Google Docs comments while persisting the API
11+
cursor in a local JSON file:
12+
13+
```bash
14+
gog drive changes poll \
15+
--state-file ~/.local/state/gog/drive-changes.json \
16+
--interval 30s \
17+
--json
18+
19+
gog docs comments poll <docId> \
20+
--state-file ~/.local/state/gog/doc-comments.json \
21+
--interval 30s \
22+
--json
23+
```
24+
25+
Both commands poll immediately, then wait for `--interval`. Use
26+
`--max-iterations N` for bounded jobs and tests. `SIGINT` and `SIGTERM` stop the
27+
poller; completed iterations have already persisted their cursor.
28+
29+
## State
30+
31+
State files are written atomically with mode `0600`. Missing and empty state
32+
files initialize without replaying existing history:
33+
34+
- Drive stores a fresh start page token before its first changes request.
35+
- Docs stores the current time as its initial comment watermark.
36+
37+
Drive state is scoped to `--drive`. Docs state is scoped to the document and
38+
the `--include-resolved` setting. Delete the state file or choose a new path to
39+
start a new stream. Run only one poller against a state file; concurrent writers
40+
can overwrite each other's cursor.
41+
42+
The Drive comments API time filter is inclusive. State therefore records both
43+
the latest timestamp and comment IDs already delivered at that timestamp.
44+
Comments that share a modified time are delivered once without moving the
45+
watermark past an unseen peer.
46+
47+
## Output
48+
49+
With `--json`, stdout is newline-delimited JSON: one object per non-empty Drive
50+
batch or Docs comment. Plain and human modes emit one tab-separated line per
51+
change or comment. Empty polls produce no stdout.
52+
53+
`drive changes poll --filter-file <fileId>` filters emitted changes and hook
54+
payloads, while the underlying Drive page token still advances.
55+
56+
## Shell hooks
57+
58+
Hooks are explicit trusted local shell commands:
59+
60+
```bash
61+
gog drive changes poll \
62+
--state-file drive.json \
63+
--on-change './handle-drive-batch'
64+
65+
gog docs comments poll <docId> \
66+
--state-file comments.json \
67+
--on-new './handle-comment'
68+
```
69+
70+
Payload JSON is passed on stdin. Google-provided text is never interpolated
71+
into the command string. Hook stdout and stderr go to `gog` stderr so event
72+
stdout remains parseable.
73+
74+
Hooks run through the platform shell and are not sandboxed. Use only fixed,
75+
operator-controlled commands; do not build the hook string from Google content.
76+
77+
Drive invokes `--on-change` once per non-empty filtered batch. Docs invokes
78+
`--on-new` once per comment, in modified-time and comment-ID order. Hooks run
79+
sequentially.
80+
81+
State advances only after output and all hooks succeed. Output or hook failure
82+
returns an error and retains the previous cursor, so the event is retried on
83+
the next run. Consumers must tolerate duplicate delivery.
84+
85+
Command pages:
86+
87+
- [`gog drive changes poll`](commands/gog-drive-changes-poll.md)
88+
- [`gog docs comments poll`](commands/gog-docs-comments-poll.md)

0 commit comments

Comments
 (0)