Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/Java/Openclaw/src/hooks/   (KI Agentensystem Version 22©)  Datei vom 26.3.2026 mit Größe 10 kB image not shown  

Quelle  gmail-ops.ts

  Sprache: JAVA
 

Spracherkennung für: .ts vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

import { spawn } from "node:child_process";
import { formatCliCommand } from "../cli/command-format.js";
import {
  type OpenClawConfig,
  CONFIG_PATH,
  loadConfig,
  readConfigFileSnapshot,
  resolveGatewayPort,
  validateConfigObjectWithPlugins,
  writeConfigFile,
} from "../config/config.js";
import { runCommandWithTimeout } from "../process/exec.js";
import { defaultRuntime } from "../runtime.js";
import { displayPath } from "../utils.js";
import {
  ensureDependency,
  ensureGcloudAuth,
  ensureSubscription,
  ensureTailscaleEndpoint,
  ensureTopic,
  resolveProjectIdFromGogCredentials,
  runGcloud,
} from "./gmail-setup-utils.js";
import {
  buildDefaultHookUrl,
  buildGogWatchServeLogArgs,
  buildGogWatchServeArgs,
  buildGogWatchStartArgs,
  buildTopicPath,
  DEFAULT_GMAIL_LABEL,
  DEFAULT_GMAIL_MAX_BYTES,
  DEFAULT_GMAIL_RENEW_MINUTES,
  DEFAULT_GMAIL_SERVE_BIND,
  DEFAULT_GMAIL_SERVE_PATH,
  DEFAULT_GMAIL_SERVE_PORT,
  DEFAULT_GMAIL_SUBSCRIPTION,
  DEFAULT_GMAIL_TOPIC,
  type GmailHookOverrides,
  type GmailHookRuntimeConfig,
  generateHookToken,
  mergeHookPresets,
  normalizeHooksPath,
  normalizeServePath,
  parseTopicPath,
  resolveGmailHookRuntimeConfig,
} from "./gmail.js";

type GmailCommonOptions = {
  topic?: string;
  subscription?: string;
  label?: string;
  hookToken?: string;
  pushToken?: string;
  hookUrl?: string;
  bind?: string;
  port?: number;
  path?: string;
  includeBody?: boolean;
  maxBytes?: number;
  renewEveryMinutes?: number;
  tailscale?: "off" | "serve" | "funnel";
  tailscalePath?: string;
  tailscaleTarget?: string;
};

export type GmailSetupOptions = GmailCommonOptions & {
  account: string;
  project?: string;
  pushEndpoint?: string;
  json?: boolean;
};

export type GmailRunOptions = GmailCommonOptions & {
  account?: string;
};

const DEFAULT_GMAIL_TOPIC_IAM_MEMBER = "serviceAccount:gmail-api-push@system.gserviceaccount.com";

export async function runGmailSetup(opts: GmailSetupOptions) {
  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());
  // Gmail watch requires the Pub/Sub topic to live in the OAuth client project.
  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()
      : undefined;

  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";
  // Tailscale strips the path before proxying; keep a public path while gog
  // listens on "/" whenever Tailscale is enabled.
  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 nextConfig: OpenClawConfig = {
    ...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,
        },
      },
    },
  };

  const validated = validateConfigObjectWithPlugins(nextConfig);
  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.writeJson(summary);
    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")}`);
}

export async function runGmailService(opts: GmailRunOptions) {
  await ensureDependency("gog", ["gogcli"]);
  const config = loadConfig();

  const overrides: GmailHookOverrides = {
    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,
  };

  const resolved = resolveGmailHookRuntimeConfig(config, overrides);
  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 * 60_000;
  const renewTimer = setInterval(() => {
    void startGmailWatch(runtimeConfig);
  }, renewMs);

  const detachSignals = () => {
    process.off("SIGINT", shutdown);
    process.off("SIGTERM", shutdown);
  };

  const shutdown = () => {
    if (shuttingDown) {
      return;
    }
    shuttingDown = true;
    detachSignals();
    clearInterval(renewTimer);
    child.kill("SIGTERM");
  };

  process.on("SIGINT", shutdown);
  process.on("SIGTERM", shutdown);

  child.on("exit", () => {
    if (shuttingDown) {
      detachSignals();
      return;
    }
    defaultRuntime.log("gog watch serve exited; restarting in 2s");
    setTimeout(() => {
      if (shuttingDown) {
        return;
      }
      child = spawnGogServe(runtimeConfig);
    }, 2000);
  });
}

function spawnGogServe(cfg: GmailHookRuntimeConfig) {
  const args = buildGogWatchServeArgs(cfg);
  defaultRuntime.log(`Starting gog ${buildGogWatchServeLogArgs(cfg).join(" ")}`);
  return spawn("gog", args, { stdio: "inherit" });
}

async function startGmailWatch(
  cfg: Pick<GmailHookRuntimeConfig, "account" | "label" | "topic">,
  fatal = false,
) {
  const args = ["gog", ...buildGogWatchStartArgs(cfg)];
  const result = await runCommandWithTimeout(args, { timeoutMs: 120_000 });
  if (result.code !== 0) {
    const message = result.stderr || result.stdout || "gog watch start failed";
    if (fatal) {
      throw new Error(message);
    }
    defaultRuntime.error(message);
  }
}

¤ Dauer der Verarbeitung: 0.25 Sekunden  (vorverarbeitet am  2026-04-27) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.