import { t as CONFIG_PATH, u as resolveGatewayPort } from "./paths-BDd7_JUB.js"; import "./agent-scope-CrgUOY3f.js"; import { E as danger, R as theme, c as defaultRuntime } from "./subsystem-46MXi6Ip.js"; import { a as displayPath } from "./utils-Dg0Xbl6w.js"; import { t as runCommandWithTimeout } from "./exec-CTo4hK94.js"; import "./model-selection-Cp1maz7B.js"; import "./github-copilot-token-VZsS4013.js"; import { t as formatCliCommand } from "./command-format-BQK1OIvH.js"; import "./boolean-CE7i9tBR.js"; import "./env-DOcCob95.js"; import { a as readConfigFileSnapshot, l as validateConfigObjectWithPlugins, r as loadConfig, s as writeConfigFile } from "./config-BAN8QfW5.js"; import "./manifest-registry-BFpLJJDB.js"; import "./skills-C4b1FA1e.js"; import { t as formatDocsLink } from "./links-C9fyAH-V.js"; import { C as normalizeServePath, S as normalizeHooksPath, T as resolveGmailHookRuntimeConfig, _ as buildGogWatchServeArgs, a as ensureTopic, b as generateHookToken, c as DEFAULT_GMAIL_LABEL, d as DEFAULT_GMAIL_SERVE_BIND, f as DEFAULT_GMAIL_SERVE_PATH, g as buildDefaultHookUrl, h as DEFAULT_GMAIL_TOPIC, i as ensureTailscaleEndpoint, l as DEFAULT_GMAIL_MAX_BYTES, m as DEFAULT_GMAIL_SUBSCRIPTION, n as ensureGcloudAuth, o as resolveProjectIdFromGogCredentials, p as DEFAULT_GMAIL_SERVE_PORT, r as ensureSubscription, s as runGcloud, t as ensureDependency, u as DEFAULT_GMAIL_RENEW_MINUTES, v as buildGogWatchStartArgs, w as parseTopicPath, x as mergeHookPresets, y as buildTopicPath } from "./gmail-setup-utils-B1CTmT2V.js"; import { spawn } from "node:child_process"; //#region src/hooks/gmail-ops.ts const DEFAULT_GMAIL_TOPIC_IAM_MEMBER = "serviceAccount:gmail-api-push@system.gserviceaccount.com"; async function runGmailSetup(opts) { await ensureDependency("gcloud", ["--cask", "gcloud-cli"]); await ensureDependency("gog", ["gogcli"]); if (opts.tailscale !== "off" && !opts.pushEndpoint) await ensureDependency("tailscale", ["tailscale"]); await ensureGcloudAuth(); const configSnapshot = await readConfigFileSnapshot(); if (!configSnapshot.valid) throw new Error(`Config invalid: ${CONFIG_PATH}`); const baseConfig = configSnapshot.config; const hooksPath = normalizeHooksPath(baseConfig.hooks?.path); const hookToken = opts.hookToken ?? baseConfig.hooks?.token ?? generateHookToken(); const pushToken = opts.pushToken ?? baseConfig.hooks?.gmail?.pushToken ?? generateHookToken(); const topicInput = opts.topic ?? baseConfig.hooks?.gmail?.topic ?? DEFAULT_GMAIL_TOPIC; const parsedTopic = parseTopicPath(topicInput); const topicName = parsedTopic?.topicName ?? topicInput; const projectId = opts.project ?? parsedTopic?.projectId ?? await resolveProjectIdFromGogCredentials(); if (!projectId) throw new Error("GCP project id required (use --project or ensure gog credentials are available)"); const topicPath = buildTopicPath(projectId, topicName); const subscription = opts.subscription ?? DEFAULT_GMAIL_SUBSCRIPTION; const label = opts.label ?? DEFAULT_GMAIL_LABEL; const hookUrl = opts.hookUrl ?? baseConfig.hooks?.gmail?.hookUrl ?? buildDefaultHookUrl(hooksPath, resolveGatewayPort(baseConfig)); const serveBind = opts.bind ?? DEFAULT_GMAIL_SERVE_BIND; const servePort = opts.port ?? DEFAULT_GMAIL_SERVE_PORT; const configuredServePath = opts.path ?? baseConfig.hooks?.gmail?.serve?.path; const configuredTailscaleTarget = opts.tailscaleTarget ?? baseConfig.hooks?.gmail?.tailscale?.target; const normalizedServePath = typeof configuredServePath === "string" && configuredServePath.trim().length > 0 ? normalizeServePath(configuredServePath) : DEFAULT_GMAIL_SERVE_PATH; const normalizedTailscaleTarget = typeof configuredTailscaleTarget === "string" && configuredTailscaleTarget.trim().length > 0 ? configuredTailscaleTarget.trim() : void 0; const includeBody = opts.includeBody ?? true; const maxBytes = opts.maxBytes ?? DEFAULT_GMAIL_MAX_BYTES; const renewEveryMinutes = opts.renewEveryMinutes ?? DEFAULT_GMAIL_RENEW_MINUTES; const tailscaleMode = opts.tailscale ?? "funnel"; const servePath = normalizeServePath(tailscaleMode !== "off" && !normalizedTailscaleTarget ? "/" : normalizedServePath); const tailscalePath = normalizeServePath(opts.tailscalePath ?? baseConfig.hooks?.gmail?.tailscale?.path ?? (tailscaleMode !== "off" ? normalizedServePath : servePath)); await runGcloud([ "config", "set", "project", projectId, "--quiet" ]); await runGcloud([ "services", "enable", "gmail.googleapis.com", "pubsub.googleapis.com", "--project", projectId, "--quiet" ]); await ensureTopic(projectId, topicName); await runGcloud([ "pubsub", "topics", "add-iam-policy-binding", topicName, "--project", projectId, "--member", DEFAULT_GMAIL_TOPIC_IAM_MEMBER, "--role", "roles/pubsub.publisher", "--quiet" ]); const pushEndpoint = opts.pushEndpoint ? opts.pushEndpoint : await ensureTailscaleEndpoint({ mode: tailscaleMode, path: tailscalePath, port: servePort, target: normalizedTailscaleTarget, token: pushToken }); if (!pushEndpoint) throw new Error("push endpoint required (set --push-endpoint)"); await ensureSubscription(projectId, subscription, topicName, pushEndpoint); await startGmailWatch({ account: opts.account, label, topic: topicPath }, true); const validated = validateConfigObjectWithPlugins({ ...baseConfig, hooks: { ...baseConfig.hooks, enabled: true, path: hooksPath, token: hookToken, presets: mergeHookPresets(baseConfig.hooks?.presets, "gmail"), gmail: { ...baseConfig.hooks?.gmail, account: opts.account, label, topic: topicPath, subscription, pushToken, hookUrl, includeBody, maxBytes, renewEveryMinutes, serve: { ...baseConfig.hooks?.gmail?.serve, bind: serveBind, port: servePort, path: servePath }, tailscale: { ...baseConfig.hooks?.gmail?.tailscale, mode: tailscaleMode, path: tailscalePath, target: normalizedTailscaleTarget } } } }); if (!validated.ok) throw new Error(`Config validation failed: ${validated.issues[0]?.message ?? "invalid"}`); await writeConfigFile(validated.config); const summary = { projectId, topic: topicPath, subscription, pushEndpoint, hookUrl, hookToken, pushToken, serve: { bind: serveBind, port: servePort, path: servePath } }; if (opts.json) { defaultRuntime.log(JSON.stringify(summary, null, 2)); return; } defaultRuntime.log("Gmail hooks configured:"); defaultRuntime.log(`- project: ${projectId}`); defaultRuntime.log(`- topic: ${topicPath}`); defaultRuntime.log(`- subscription: ${subscription}`); defaultRuntime.log(`- push endpoint: ${pushEndpoint}`); defaultRuntime.log(`- hook url: ${hookUrl}`); defaultRuntime.log(`- config: ${displayPath(CONFIG_PATH)}`); defaultRuntime.log(`Next: ${formatCliCommand("openclaw webhooks gmail run")}`); } async function runGmailService(opts) { await ensureDependency("gog", ["gogcli"]); const resolved = resolveGmailHookRuntimeConfig(loadConfig(), { account: opts.account, topic: opts.topic, subscription: opts.subscription, label: opts.label, hookToken: opts.hookToken, pushToken: opts.pushToken, hookUrl: opts.hookUrl, serveBind: opts.bind, servePort: opts.port, servePath: opts.path, includeBody: opts.includeBody, maxBytes: opts.maxBytes, renewEveryMinutes: opts.renewEveryMinutes, tailscaleMode: opts.tailscale, tailscalePath: opts.tailscalePath, tailscaleTarget: opts.tailscaleTarget }); if (!resolved.ok) throw new Error(resolved.error); const runtimeConfig = resolved.value; if (runtimeConfig.tailscale.mode !== "off") { await ensureDependency("tailscale", ["tailscale"]); await ensureTailscaleEndpoint({ mode: runtimeConfig.tailscale.mode, path: runtimeConfig.tailscale.path, port: runtimeConfig.serve.port, target: runtimeConfig.tailscale.target }); } await startGmailWatch(runtimeConfig); let shuttingDown = false; let child = spawnGogServe(runtimeConfig); const renewMs = runtimeConfig.renewEveryMinutes * 6e4; const renewTimer = setInterval(() => { startGmailWatch(runtimeConfig); }, renewMs); const shutdown = () => { if (shuttingDown) return; shuttingDown = true; clearInterval(renewTimer); child.kill("SIGTERM"); }; process.on("SIGINT", shutdown); process.on("SIGTERM", shutdown); child.on("exit", () => { if (shuttingDown) return; defaultRuntime.log("gog watch serve exited; restarting in 2s"); setTimeout(() => { if (shuttingDown) return; child = spawnGogServe(runtimeConfig); }, 2e3); }); } function spawnGogServe(cfg) { const args = buildGogWatchServeArgs(cfg); defaultRuntime.log(`Starting gog ${args.join(" ")}`); return spawn("gog", args, { stdio: "inherit" }); } async function startGmailWatch(cfg, fatal = false) { const result = await runCommandWithTimeout(["gog", ...buildGogWatchStartArgs(cfg)], { timeoutMs: 12e4 }); if (result.code !== 0) { const message = result.stderr || result.stdout || "gog watch start failed"; if (fatal) throw new Error(message); defaultRuntime.error(message); } } //#endregion //#region src/cli/webhooks-cli.ts function registerWebhooksCli(program) { const gmail = program.command("webhooks").description("Webhook helpers and integrations").addHelpText("after", () => `\n${theme.muted("Docs:")} ${formatDocsLink("/cli/webhooks", "docs.openclaw.ai/cli/webhooks")}\n`).command("gmail").description("Gmail Pub/Sub hooks (via gogcli)"); gmail.command("setup").description("Configure Gmail watch + Pub/Sub + OpenClaw hooks").requiredOption("--account ", "Gmail account to watch").option("--project ", "GCP project id (OAuth client owner)").option("--topic ", "Pub/Sub topic name", DEFAULT_GMAIL_TOPIC).option("--subscription ", "Pub/Sub subscription name", DEFAULT_GMAIL_SUBSCRIPTION).option("--label