Skip to content

feat(hooks): support gws as alternative to gog for Gmail Pub/Sub#35506

Closed
JoshuaLelon wants to merge 5 commits intoopenclaw:mainfrom
JoshuaLelon:gws/06-gmail-hooks-gws
Closed

feat(hooks): support gws as alternative to gog for Gmail Pub/Sub#35506
JoshuaLelon wants to merge 5 commits intoopenclaw:mainfrom
JoshuaLelon:gws/06-gmail-hooks-gws

Conversation

@JoshuaLelon
Copy link
Copy Markdown
Contributor

Summary

  • Adds hooks.gmail.cli: "gog" | "gws" config option so users can choose between gog (push-based, Homebrew/macOS) and gws (pull-based, npm, cross-platform) for Gmail Pub/Sub hooks
  • gws mode (--cli gws) uses gws gmail +watch which polls Pub/Sub and outputs NDJSON — no Tailscale, no push endpoint, no renewal interval needed
  • New gmail-gws-bridge.ts module transforms Gmail API messages from gws stdout into the hook payload format expected by the gmail preset ({ messages: [{ id, from, subject, snippet, body }] })
  • ensureDependency now supports npm install method alongside brew

Changes

  • Config: GmailCliMode type, cli/project fields in HooksGmailConfig + Zod schema
  • Runtime: resolveGmailHookRuntimeConfig relaxes pushToken/topic requirements for gws; added buildGwsWatchArgs
  • Bridge: gmail-gws-bridge.ts — NDJSON parsing, Gmail API message transform, hook POST
  • Setup utils: npm install path, gws credential paths, unified resolveProjectIdFromCredentials(cli)
  • Watcher: spawnGwsWatch with NDJSON line buffering; startGmailWatcher branches by cli mode
  • Ops: runGmailSetup/runGmailService branch for gws (skip Tailscale/push subscription)
  • CLI: --cli <gog|gws> option on both setup and run commands
  • Docs: gws mode comparison table, prerequisites, config example, wizard/daemon instructions

Test plan

  • pnpm check passes (lint + format + type check)
  • pnpm test -- src/hooks/gmail — 41 tests pass across 4 files
  • Manual test with gws installed: openclaw webhooks gmail setup --cli gws --account X --project P

🤖 Generated with Claude Code

@openclaw-barnacle openclaw-barnacle bot added docs Improvements or additions to documentation cli CLI command changes size: XL labels Mar 5, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 5, 2026

Greptile Summary

This PR adds gws (@googleworkspace/cli) as a pull-based alternative backend to gog for Gmail Pub/Sub hooks. The core feature logic is solid: the gmail-gws-bridge.ts module transforms Gmail API messages correctly, config schema changes are clean, CLI option plumbing is well-structured, and tests provide good coverage.

However, two critical bugs exist in the process-management layer that make the gws service daemon unreliable:

  1. runGwsService restart loop (gmail-ops.ts, lines 416-428): The exit handler is only attached to the initial child process. After an unattended crash-and-restart, the new child has no exit listener, so auto-recovery stops. The service only survives one crash.

  2. spawnGwsWatch module-level state conflict (gmail-watcher.ts, lines 175-192): The exported function has a built-in restart handler that references uninitialized module-level state (currentConfig, shuttingDown, watcherProcess) when called from the ops context. This causes spurious logs and silent restart failures after the first crash. Combined with bug fix: add @lid format support and allowFrom wildcard handling #1, the service dies after the second crash.

The gws feature cannot ship in its current form — the daemon is unreliable and will silently stop working in production after crashing twice.

Confidence Score: 2/5

  • Not safe to merge as-is. The gws service daemon has two interconnected bugs that cause silent death after the second crash.
  • The core feature logic (bridge transform, config resolution, CLI plumbing, schema) is solid and well tested. However, the gws process-management layer is broken: (1) runGwsService's exit handler is not re-attached after restart, (2) spawnGwsWatch's internal restart handler uses uninitialized module-level state when called from ops context. After the first crash, runGwsService creates a new child, but the new child only has spawnGwsWatch's broken handler. If this second child crashes, the service fails to recover. This makes the daemon unreliable for production use.
  • src/hooks/gmail-ops.ts (restart loop), src/hooks/gmail-watcher.ts (module-level state leak)

Last reviewed commit: ae737c9

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ae737c98b7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 238c48a120

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 22a9e696c0

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 61ea14c02a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

// Buffer partial lines from stdout (NDJSON may arrive in chunks)
let buffer = "";
child.stdout?.on("data", (data: Buffer) => {
buffer += data.toString();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Decode gws stdout with a streaming UTF-8 decoder

spawnGwsWatch appends each chunk using data.toString() before line-splitting, which corrupts multi-byte UTF-8 characters when gws splits a character across chunk boundaries. In multilingual inboxes this can turn subjects/snippets into replacement characters () before JSON parsing/forwarding, so hook payload content is silently altered even though the message is otherwise valid; use a streaming decoder (for example StringDecoder) or buffer bytes until line boundaries before decoding.

Useful? React with 👍 / 👎.

@JoshuaLelon JoshuaLelon force-pushed the gws/06-gmail-hooks-gws branch from 61ea14c to 2bea21f Compare March 9, 2026 14:04
@openclaw-barnacle openclaw-barnacle bot removed the app: macos App: macos label Mar 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli CLI command changes docs Improvements or additions to documentation size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant