import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
import {
  getPluginVersion
} from "./chunk-CK24O5YQ.js";
import {
  AgentRegistry
} from "./chunk-RNWALAFP.js";
import {
  DAEMON_CLIENT_TIMEOUT_MS,
  DAEMON_HEALTH_CHECK_TIMEOUT_MS,
  DAEMON_HEALTH_RETRY_DELAYS,
  DAEMON_STALE_GRACE_PERIOD_MS
} from "./chunk-WBLTISAK.js";

// src/hooks/client.ts
import fs from "fs";
import path from "path";
import { spawn } from "child_process";
var DaemonClient = class {
  vaultDir;
  constructor(vaultDir) {
    this.vaultDir = vaultDir;
  }
  async post(endpoint, body) {
    try {
      const info = this.readDaemonJson();
      if (!info) return { ok: false };
      const res = await fetch(`http://127.0.0.1:${info.port}${endpoint}`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(body),
        signal: AbortSignal.timeout(DAEMON_CLIENT_TIMEOUT_MS)
      });
      if (!res.ok) return { ok: false };
      const data = await res.json();
      return { ok: true, data };
    } catch {
      return { ok: false };
    }
  }
  async get(endpoint) {
    try {
      const info = this.readDaemonJson();
      if (!info) return { ok: false };
      const res = await fetch(`http://127.0.0.1:${info.port}${endpoint}`, {
        signal: AbortSignal.timeout(DAEMON_CLIENT_TIMEOUT_MS)
      });
      if (!res.ok) return { ok: false };
      const data = await res.json();
      return { ok: true, data };
    } catch {
      return { ok: false };
    }
  }
  async isHealthy() {
    try {
      const info = this.readDaemonJson();
      if (!info) return false;
      const res = await fetch(`http://127.0.0.1:${info.port}/health`, {
        signal: AbortSignal.timeout(DAEMON_HEALTH_CHECK_TIMEOUT_MS)
      });
      if (!res.ok) return false;
      const data = await res.json();
      return data.myco === true;
    } catch {
      return false;
    }
  }
  /**
   * Check if the daemon is running a stale version.
   * Returns true if the daemon's version doesn't match the current plugin version.
   * Skips the check if daemon.json was written recently (grace period) to prevent
   * rapid restart loops from concurrent hooks or session reloads.
   */
  async isStale() {
    try {
      const jsonPath = path.join(this.vaultDir, "daemon.json");
      const stat = fs.statSync(jsonPath);
      if (Date.now() - stat.mtimeMs < DAEMON_STALE_GRACE_PERIOD_MS) {
        return false;
      }
      const info = this.readDaemonJson();
      if (!info) return false;
      const res = await fetch(`http://127.0.0.1:${info.port}/health`, {
        signal: AbortSignal.timeout(DAEMON_HEALTH_CHECK_TIMEOUT_MS)
      });
      if (!res.ok) return false;
      const data = await res.json();
      if (!data.myco) return false;
      if (!data.version) return true;
      return data.version !== getPluginVersion();
    } catch {
      return false;
    }
  }
  /**
   * Kill the running daemon process.
   */
  killDaemon() {
    try {
      const info = this.readDaemonJson();
      if (!info) return;
      process.kill(info.pid, "SIGTERM");
    } catch {
    }
    try {
      fs.unlinkSync(path.join(this.vaultDir, "daemon.json"));
    } catch {
    }
  }
  /**
   * Ensure the daemon is running. Spawns it if unhealthy.
   * When checkStale is true (default), also restarts a healthy daemon if its
   * version doesn't match the current plugin version. Use checkStale: false
   * for hooks that just need the daemon alive (e.g., stop) without triggering
   * version-driven restarts.
   */
  async ensureRunning(opts) {
    const checkStale = opts?.checkStale ?? true;
    if (checkStale && await this.isStale()) {
      this.killDaemon();
      await new Promise((r) => setTimeout(r, 200));
    } else if (await this.isHealthy()) {
      return true;
    }
    this.spawnDaemon();
    for (const delay of DAEMON_HEALTH_RETRY_DELAYS) {
      await new Promise((r) => setTimeout(r, delay));
      if (await this.isHealthy()) return true;
    }
    return false;
  }
  spawnDaemon() {
    const daemonScript = this.resolveDaemonScript();
    if (!daemonScript || !fs.existsSync(daemonScript)) return;
    const child = spawn("node", [daemonScript, "--vault", this.vaultDir], {
      detached: true,
      stdio: "ignore"
    });
    child.unref();
  }
  /**
   * Resolve the daemon entry script path.
   * Priority:
   * 1. Plugin root env var (set by the agent host) → dist/src/daemon/main.js
   * 2. Walk up from the current file to find the dist/ directory containing
   *    the daemon entry. This handles both chunk files (dist/chunk-*.js) and
   *    thin entry points (dist/src/hooks/*.js) after bundling.
   */
  resolveDaemonScript() {
    const pluginRoot = new AgentRegistry().resolvePluginRoot();
    if (pluginRoot) {
      return path.join(pluginRoot, "dist", "src", "daemon", "main.js");
    }
    let dir = import.meta.dirname;
    for (let i = 0; i < 5; i++) {
      const candidate = path.join(dir, "dist", "src", "daemon", "main.js");
      if (fs.existsSync(candidate)) return candidate;
      const inDist = path.join(dir, "src", "daemon", "main.js");
      if (fs.existsSync(inDist)) return inDist;
      dir = path.dirname(dir);
    }
    return void 0;
  }
  readDaemonJson() {
    try {
      const jsonPath = path.join(this.vaultDir, "daemon.json");
      const content = fs.readFileSync(jsonPath, "utf-8");
      const info = JSON.parse(content);
      if (typeof info.port !== "number") return null;
      return info;
    } catch {
      return null;
    }
  }
};

export {
  DaemonClient
};
//# sourceMappingURL=chunk-CPVXNRGW.js.map