diff --git a/docs/channels/googlechat.md b/docs/channels/googlechat.md index c6419bef8..2b73e4092 100644 --- a/docs/channels/googlechat.md +++ b/docs/channels/googlechat.md @@ -77,16 +77,17 @@ If you already use `gog` for Google Workspace, you can reuse its OAuth client + - For headless systems, switch to file keyring + password (see `gog` docs). citeturn6view0 4) Verify `gog` is visible to the gateway user: ```bash - gog auth tokens --json + gog auth tokens list --json ``` If this fails, install `gog` on the gateway host and ensure the keyring is accessible. + For non-interactive services, set `GOG_KEYRING_PASSWORD` in the gateway environment so `gog` can unlock the keyring. Clawdbot reads `gog` OAuth client files from: - `~/.config/gogcli/credentials.json` - `~/.config/gogcli/credentials-.json` - `~/.config/gogcli/credentials-.json` (or macOS equivalent) citeturn9view0 -Clawdbot queries `gog auth tokens --json` to reuse the stored refresh token. If this fails, set `oauthRefreshToken` manually. +Clawdbot queries `gog auth tokens list --json` (and falls back to `gog auth tokens export --json`) to reuse the stored refresh token. If this fails, set `oauthRefreshToken` manually. ### Option B: Manual OAuth 1) Configure OAuth consent + create OAuth client credentials in your Google Cloud project (desktop app recommended). citeturn6view0 diff --git a/extensions/googlechat/src/channel.ts b/extensions/googlechat/src/channel.ts index 3a45b6901..28c40cf81 100644 --- a/extensions/googlechat/src/channel.ts +++ b/extensions/googlechat/src/channel.ts @@ -543,6 +543,16 @@ export const googlechatPlugin: ChannelPlugin = { const configured = entry.configured === true; if (!enabled || !configured) return []; const issues = []; + if (entry.oauthFromGog && entry.userCredentialSource === "none") { + issues.push({ + channel: "googlechat", + accountId, + kind: "auth", + message: + "Google Chat OAuth is set to reuse gog, but no gog OAuth credentials were detected.", + fix: "Ensure gog is installed and the keyring is unlocked (set GOG_KEYRING_PASSWORD), or set oauthRefreshToken/oauthClientFile manually.", + }); + } if (!entry.audience) { issues.push({ channel: "googlechat", @@ -584,6 +594,8 @@ export const googlechatPlugin: ChannelPlugin = { enabled: account.enabled, configured: account.credentialSource !== "none", credentialSource: account.credentialSource, + oauthFromGog: account.config.oauthFromGog ?? false, + userCredentialSource: account.userCredentialSource, audienceType: account.config.audienceType, audience: account.config.audience, webhookPath: account.config.webhookPath, diff --git a/extensions/googlechat/src/gog.ts b/extensions/googlechat/src/gog.ts index 67851d03a..d2d720333 100644 --- a/extensions/googlechat/src/gog.ts +++ b/extensions/googlechat/src/gog.ts @@ -109,25 +109,37 @@ export function readGogRefreshTokenSync(params: { const cached = tokenCache.get(cacheKey); if (cached) return cached; - let stdout = ""; - try { - stdout = execFileSync("gog", ["auth", "tokens", "--json"], { - encoding: "utf8", - stdio: ["ignore", "pipe", "pipe"], - }); - } catch { - return null; - } + const env = { + ...process.env, + ...(params.gogAccount?.trim() ? { GOG_ACCOUNT: params.gogAccount.trim() } : {}), + ...(params.gogClient?.trim() ? { GOG_CLIENT: params.gogClient.trim() } : {}), + }; - let parsed: unknown; - try { - parsed = JSON.parse(stdout); - } catch { - return null; - } + const runGogJson = (args: string[]): unknown | null => { + try { + const stdout = execFileSync("gog", ["--no-input", ...args], { + encoding: "utf8", + stdio: ["ignore", "pipe", "pipe"], + timeout: 3000, + env, + }); + return JSON.parse(stdout); + } catch { + return null; + } + }; + const parsed = runGogJson(["auth", "tokens", "list", "--json"]); const tokens: GogTokenEntry[] = []; - collectTokens(parsed, tokens); + if (parsed) { + collectTokens(parsed, tokens); + } + if (tokens.length === 0) { + const exported = runGogJson(["auth", "tokens", "export", "--json"]); + if (exported) { + collectTokens(exported, tokens); + } + } if (tokens.length === 0) return null; const target = params.gogAccount?.trim().toLowerCase();