#!/usr/bin/env node
import {
  createInstallPlans,
  createSecretProbe,
  diffInventory,
  eventTypeToTaskStatus,
  getAdapterByName,
  getDataDir,
  getDefaultDatabasePath,
  maxRisk,
  newId,
  nowIso,
  redactSecrets,
  scanIntegrations,
  scanInventory,
  stableId,
  summarizeUnknown,
  verifyInstallPlan
} from "./chunk-XEO4LVJ6.js";

// src/index.ts
import { existsSync as existsSync4 } from "fs";
import { dirname as dirname4, join as join3 } from "path";
import { fileURLToPath as fileURLToPath2 } from "url";
import { Command } from "commander";

// ../server/dist/chunk-GYHXPPH4.js
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
import { dirname as dirname3, join as join2 } from "path";
import { pathToFileURL } from "url";
import { fileURLToPath } from "url";
import cors from "@fastify/cors";
import fastifyStatic from "@fastify/static";
import Fastify from "fastify";

// ../../packages/analyzers/dist/index.js
function analyzeEvent(event) {
  const findings = [];
  const text = JSON.stringify({ raw: event.raw, normalized: event.normalized });
  const secretProbe = createSecretProbe();
  for (const finding of secretProbe.containsSecret(text)) {
    findings.push(finding);
  }
  if (/\b(rm\s+-rf|Remove-Item\s+-Recurse|format\s+c:|del\s+\/s)\b/i.test(text)) {
    findings.push({ code: "command.dangerous", message: "Dangerous shell command pattern detected" });
  }
  if (text.length > 2e4) {
    findings.push({ code: "content.large", message: "Large event payload detected" });
  }
  if (event.eventType === "error") {
    findings.push({ code: "event.error", message: "Agent error event received" });
  }
  const riskLevel = findings.reduce((level, finding) => {
    if (finding.code.includes("secret") || finding.code.includes("dangerous")) return maxRisk(level, "high");
    if (finding.code.includes("error")) return maxRisk(level, "medium");
    return maxRisk(level, "low");
  }, event.riskLevel || "none");
  return {
    id: newId("analysis"),
    eventId: event.id,
    analyzer: "basic",
    riskLevel,
    findings,
    createdAt: nowIso()
  };
}

// ../../packages/channels/dist/index.js
import { spawn } from "child_process";
async function sendNotification(input) {
  if (input.channel === "webhook") return sendWebhook(input);
  if (input.channel === "windows") return sendWindows(input);
  return { success: false, channel: input.channel, error: "Unsupported channel" };
}
async function sendWebhook(input) {
  const url = String(input.config?.url || process.env.AGENT_PULSE_WEBHOOK_URL || "");
  if (!url) return { success: false, channel: "webhook", error: "Webhook URL is not configured" };
  try {
    const response = await fetch(url, {
      method: String(input.config?.method || "POST"),
      headers: { "content-type": "application/json", ...input.config?.headers },
      body: JSON.stringify({ title: input.title, message: input.message, event: input.event })
    });
    return { success: response.ok, channel: "webhook", messageId: newId("msg"), error: response.ok ? void 0 : `HTTP ${response.status}` };
  } catch (error) {
    return { success: false, channel: "webhook", error: String(error) };
  }
}
async function sendWindows(input) {
  if (process.platform !== "win32") {
    return { success: false, channel: "windows", error: "Windows notifications are only available on win32" };
  }
  const title = escapePowerShell(input.title);
  const message = escapePowerShell(input.message);
  const script = `[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null;$template=[Windows.UI.Notifications.ToastTemplateType]::ToastText02;$xml=[Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent($template);$texts=$xml.GetElementsByTagName('text');$texts.Item(0).AppendChild($xml.CreateTextNode('${title}'))|Out-Null;$texts.Item(1).AppendChild($xml.CreateTextNode('${message}'))|Out-Null;$toast=[Windows.UI.Notifications.ToastNotification]::new($xml);[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('AgentPulse').Show($toast);`;
  return new Promise((resolve) => {
    const child = spawn("powershell.exe", ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", script], { windowsHide: true });
    let error = "";
    child.stderr.on("data", (chunk) => error += String(chunk));
    child.on("error", (err) => resolve({ success: false, channel: "windows", error: err.message }));
    child.on("close", (code) => resolve({ success: code === 0, channel: "windows", messageId: newId("msg"), error: code === 0 ? void 0 : error || `Exit ${code}` }));
  });
}
function escapePowerShell(value) {
  return value.replace(/'/g, "''").slice(0, 200);
}

// ../../packages/installer/dist/index.js
import { copyFileSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
import { dirname, join } from "path";
async function scan(workspaceDir = process.cwd()) {
  return scanIntegrations(workspaceDir);
}
async function planInstall(options) {
  const plans = await createInstallPlans({
    workspaceDir: options.workspaceDir || process.cwd(),
    scope: options.scope || "workspace",
    proxyBaseUrl: options.proxyBaseUrl || "http://127.0.0.1:8080"
  });
  plans.forEach((plan) => options.storage?.saveInstallPlan(plan));
  return plans;
}
async function applyInstall(plan, options = {}) {
  const scope = options.scope || plan.scope;
  if (scope !== "workspace" && !options.yes) {
    return { ok: false, planId: plan.id, appliedActions: 0, warnings: ["Refusing to modify non-workspace scope without --yes"] };
  }
  let appliedActions = 0;
  const warnings = [];
  for (const action of plan.actions) {
    if (action.type !== "file.patch") continue;
    if ((action.scope || plan.scope) !== "workspace" && !options.yes) {
      warnings.push(`Skipped non-workspace file ${action.filePath}`);
      continue;
    }
    mkdirSync(dirname(action.filePath), { recursive: true });
    const backupId = newId("backup");
    const backupPath = join(getDataDir(), "backups", `${backupId}-${encodeURIComponent(action.filePath).replace(/%/g, "_")}`);
    const existedBefore = existsSync(action.filePath);
    mkdirSync(dirname(backupPath), { recursive: true });
    if (existedBefore) {
      copyFileSync(action.filePath, backupPath);
    } else {
      writeFileSync(backupPath, "");
    }
    writeFileSync(`${backupPath}.meta.json`, `${JSON.stringify({ existedBefore, filePath: action.filePath, planId: plan.id, createdAt: nowIso() }, null, 2)}
`, "utf8");
    options.storage?.insertBackup({ id: backupId, planId: plan.id, filePath: action.filePath, backupPath });
    writeFileSync(action.filePath, renderPatchContent(action), "utf8");
    appliedActions += 1;
  }
  const verification = await verifyInstallPlan(plan);
  warnings.push(...verification.warnings);
  if (verification.ok) persistProxyRouteMapping(plan, options.storage);
  options.storage?.markPlanApplied(plan.id);
  return { ok: warnings.length === 0 && verification.ok, planId: plan.id, appliedActions, warnings, verification };
}
function rollbackLatest(options) {
  return rollbackBackups(options.storage.getLatestBackups(), options.storage);
}
function rollbackIntegration(integration, options) {
  return rollbackBackups(options.storage.getLatestBackupsForIntegration(integration), options.storage);
}
function rollbackBackups(backups, storage) {
  const restoredFiles = [];
  const deletedFiles = [];
  const warnings = [];
  for (const backup of backups) {
    try {
      const meta = readBackupMeta(backup.backupPath);
      if (meta?.existedBefore === false) {
        if (existsSync(backup.filePath)) {
          unlinkSync(backup.filePath);
          deletedFiles.push(backup.filePath);
        }
      } else {
        mkdirSync(dirname(backup.filePath), { recursive: true });
        copyFileSync(backup.backupPath, backup.filePath);
        restoredFiles.push(backup.filePath);
      }
      storage.markBackupRestored(backup.id);
    } catch (error) {
      warnings.push(`Failed to restore ${backup.filePath}: ${String(error)}`);
    }
  }
  if (warnings.length === 0 && (restoredFiles.length > 0 || deletedFiles.length > 0)) {
    deleteProxyRouteMappingsForBackups(backups, storage);
  }
  return { ok: warnings.length === 0 && (restoredFiles.length > 0 || deletedFiles.length > 0), backupId: backups[0]?.id, restoredFiles, deletedFiles, warnings };
}
function persistProxyRouteMapping(plan, storage) {
  if (!storage || !plan.proxyRoute || !plan.verification?.expectedProxyBaseUrl) return;
  const localRoute = plan.proxyRoute.localRoute;
  const current = plan.preflight?.currentBaseUrl;
  const upstreamBaseUrl = plan.preflight?.originalUpstream || (current && !current.includes(localRoute) ? current : void 0) || plan.proxyRoute.defaultUpstream;
  storage.upsertProxyRouteMapping({
    integration: plan.integration,
    provider: plan.proxyRoute.provider,
    proxyKey: plan.integration,
    apiProtocol: inferApiProtocol(String(plan.proxyRoute.provider)),
    localRoute,
    proxyBaseUrl: plan.verification.expectedProxyBaseUrl,
    upstreamBaseUrl,
    sourceConfigPath: plan.preflight?.configPath
  });
}
function inferApiProtocol(provider) {
  return provider === "anthropic" || provider === "claude-code" ? "anthropic-compatible" : "openai-compatible";
}
function deleteProxyRouteMappingsForBackups(backups, storage) {
  const records = storage.listInstallPlanRecords();
  const integrations = /* @__PURE__ */ new Set();
  for (const backup of backups) {
    const integration = records.find((record) => record.plan.id === backup.planId)?.plan.integration;
    if (integration) integrations.add(integration);
  }
  integrations.forEach((integration) => storage.deleteProxyRouteMapping(integration));
}
function renderPatchContent(action) {
  if (action.format === "toml") return toToml(action.after && typeof action.after === "object" ? action.after : {});
  if (action.format === "text") return String(action.after ?? "");
  return `${JSON.stringify(action.after, null, 2)}
`;
}
function readBackupMeta(backupPath) {
  try {
    return JSON.parse(readFileSync(`${backupPath}.meta.json`, "utf8"));
  } catch {
    return null;
  }
}
function toToml(value) {
  const lines = [];
  writeTomlObject(value, [], lines);
  return `${lines.join("\n")}
`;
}
function writeTomlObject(value, prefix, lines) {
  const scalars = [];
  const nested = [];
  for (const [key, item] of Object.entries(value)) {
    if (item && typeof item === "object" && !Array.isArray(item)) nested.push([key, item]);
    else scalars.push([key, item]);
  }
  if (prefix.length) lines.push(`[${prefix.join(".")}]`);
  scalars.forEach(([key, item]) => lines.push(`${key} = ${formatTomlScalar(item)}`));
  for (const [key, item] of nested) {
    if (lines.length && lines[lines.length - 1] !== "") lines.push("");
    writeTomlObject(item, [...prefix, key], lines);
  }
}
function formatTomlScalar(value) {
  if (typeof value === "string") return JSON.stringify(value);
  if (typeof value === "number" || typeof value === "boolean") return String(value);
  if (Array.isArray(value)) return `[${value.map(formatTomlScalar).join(", ")}]`;
  if (value == null) return '""';
  return JSON.stringify(String(value));
}

// ../../packages/proxy/dist/index.js
import { Readable } from "stream";
function registerProxyRoutes(app, options) {
  refreshProxyRouteMappings(options.storage);
  app.all("/proxy/:proxyKey/*", (request, reply) => handleProxy(request, reply, options.storage));
}
var routeMappings = /* @__PURE__ */ new Map();
function refreshProxyRouteMappings(storage) {
  routeMappings.clear();
  for (const mapping of storage.listProxyRouteMappings()) {
    routeMappings.set(mapping.proxyKey, mapping);
    routeMappings.set(mapping.integration, mapping);
  }
}
async function handleProxy(request, reply, storage) {
  const started = Date.now();
  refreshProxyRouteMappings(storage);
  const target = resolveProxyTarget(request.url);
  if (!target) {
    reply.status(404).send({ error: "proxy_mapping_not_found" });
    return;
  }
  const mapping = routeMappings.get(target.proxyKey);
  if (!mapping) {
    reply.status(404).send({ error: "proxy_mapping_not_found", proxyKey: target.proxyKey });
    return;
  }
  const upstreamUrl = `${mapping.upstreamBaseUrl.replace(/\/$/, "")}${target.suffix}`;
  const id = newId("proxy");
  const headers = normalizeHeaders(request.headers);
  delete headers.host;
  delete headers["content-length"];
  const body = serializeRequestBody(request);
  try {
    const response = await fetch(upstreamUrl, {
      method: request.method,
      headers,
      body: request.method === "GET" || request.method === "HEAD" ? void 0 : body
    });
    response.headers.forEach((value, key) => reply.header(key, value));
    reply.status(response.status);
    const contentType = response.headers.get("content-type") || "";
    if (shouldPassthroughResponse(response, contentType)) {
      storage.insertProxyRequest(baseRecord(id, mapping, request, upstreamUrl, started, response.status, { passthrough: true, streaming: contentType.includes("text/event-stream") }));
      return reply.send(response.body ? Readable.fromWeb(response.body) : void 0);
    }
    const text = await response.text();
    storage.insertProxyRequest(baseRecord(id, mapping, request, upstreamUrl, started, response.status, summarizeUnknown(safeJson(text) ?? text)));
    return reply.send(text);
  } catch (error) {
    storage.insertProxyRequest({
      ...baseRecord(id, mapping, request, upstreamUrl, started),
      error: String(error)
    });
    reply.status(502).send({ error: "proxy_failed", message: String(error) });
  }
}
function baseRecord(id, mapping, request, upstreamUrl, started, statusCode, responseSummary) {
  return {
    id,
    provider: mapping.provider,
    proxyKey: mapping.proxyKey,
    apiProtocol: mapping.apiProtocol,
    method: request.method,
    path: request.url,
    upstreamUrl,
    statusCode,
    durationMs: Date.now() - started,
    requestSummary: summarizeUnknown(redactSecrets({ headers: request.headers, body: request.body ?? {} })),
    responseSummary,
    createdAt: nowIso()
  };
}
function resolveProxyTarget(url) {
  const match = url.match(/^\/proxy\/([^/?#]+)(\/[^?#]*)?(\?[^#]*)?/);
  if (!match) return null;
  const proxyKey = decodeURIComponent(match[1] || "");
  if (!proxyKey) return null;
  const suffix = `${match[2] || "/"}${match[3] || ""}`;
  return { proxyKey, suffix };
}
function normalizeHeaders(headers) {
  const output = {};
  for (const [key, value] of Object.entries(headers)) {
    if (Array.isArray(value)) output[key] = value.join(", ");
    else if (value !== void 0) output[key] = String(value);
  }
  return output;
}
function serializeRequestBody(request) {
  if (request.method === "GET" || request.method === "HEAD") return void 0;
  const body = request.body;
  if (body === void 0) return void 0;
  if (typeof body === "string") return body;
  if (body instanceof Uint8Array) return body;
  return JSON.stringify(body);
}
function shouldPassthroughResponse(response, contentType) {
  if (!response.body) return false;
  if (contentType.includes("text/event-stream")) return true;
  if (contentType.includes("application/json") || contentType.includes("+json") || contentType.startsWith("text/")) return false;
  return true;
}
function safeJson(text) {
  try {
    return JSON.parse(text);
  } catch {
    return void 0;
  }
}

// ../../packages/storage/dist/index.js
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
import { createRequire } from "module";
import { dirname as dirname2 } from "path";
import initSqlJs from "sql.js";
var require2 = createRequire(import.meta.url);
var SQL = await initSqlJs({ locateFile: (file) => require2.resolve(`sql.js/dist/${file}`) });
var SqlJsCompatDatabase = class {
  constructor(filePath) {
    this.filePath = filePath;
    const bytes = existsSync2(filePath) ? readFileSync2(filePath) : void 0;
    this.database = new SQL.Database(bytes);
  }
  filePath;
  database;
  pragma(_value) {
  }
  exec(sql) {
    this.database.exec(sql);
    this.persist();
  }
  prepare(sql) {
    return new SqlJsCompatStatement(this, sql, this.database.prepare(sql));
  }
  transaction(fn) {
    return ((...args) => {
      const result = fn(...args);
      this.persist();
      return result;
    });
  }
  getRowsModified() {
    return this.database.getRowsModified();
  }
  persist() {
    writeFileSync2(this.filePath, Buffer.from(this.database.export()));
  }
  close() {
    this.persist();
    this.database.close();
  }
};
var SqlJsCompatStatement = class {
  constructor(db, sql, statement) {
    this.db = db;
    this.sql = sql;
    this.statement = statement;
  }
  db;
  sql;
  statement;
  run(...params) {
    try {
      this.bind(params);
      this.statement.step();
      const changes = this.db.getRowsModified();
      this.db.persist();
      return { changes };
    } finally {
      this.statement.free();
    }
  }
  all(...params) {
    try {
      this.bind(params);
      const rows = [];
      while (this.statement.step()) rows.push(this.statement.getAsObject());
      return rows;
    } finally {
      this.statement.free();
    }
  }
  get(...params) {
    try {
      this.bind(params);
      return this.statement.step() ? this.statement.getAsObject() : void 0;
    } finally {
      this.statement.free();
    }
  }
  bind(params) {
    if (params.length === 0) return;
    if (params.length === 1 && params[0] && typeof params[0] === "object" && !Array.isArray(params[0])) {
      this.statement.bind(normalizeNamedParams(this.sql, params[0]));
      return;
    }
    this.statement.bind(params);
  }
};
function normalizeNamedParams(sql, params) {
  const normalized = {};
  for (const match of sql.matchAll(/[@:$][A-Za-z_][A-Za-z0-9_]*/g)) {
    const placeholder = match[0];
    const bare = placeholder.slice(1);
    if (Object.prototype.hasOwnProperty.call(params, placeholder)) normalized[placeholder] = params[placeholder];
    else if (Object.prototype.hasOwnProperty.call(params, bare)) normalized[placeholder] = params[bare];
  }
  return normalized;
}
var SqliteAgentPulseStorage = class {
  db;
  constructor(options = {}) {
    const databasePath = options.databasePath || getDefaultDatabasePath();
    mkdirSync2(dirname2(databasePath), { recursive: true });
    this.db = new SqlJsCompatDatabase(databasePath);
    this.db.pragma("journal_mode = WAL");
    this.migrate();
  }
  close() {
    this.db.close();
  }
  migrate() {
    this.db.exec(`
      CREATE TABLE IF NOT EXISTS events (
        id TEXT PRIMARY KEY,
        source TEXT NOT NULL,
        source_type TEXT NOT NULL,
        event_type TEXT NOT NULL,
        timestamp TEXT NOT NULL,
        project_id TEXT,
        workspace TEXT,
        session_id TEXT,
        correlation_id TEXT,
        raw_json TEXT NOT NULL,
        normalized_json TEXT NOT NULL,
        risk_level TEXT DEFAULT 'none',
        tags_json TEXT NOT NULL DEFAULT '[]'
      );
      CREATE TABLE IF NOT EXISTS sessions (
        id TEXT PRIMARY KEY,
        source TEXT NOT NULL,
        workspace TEXT,
        status TEXT NOT NULL,
        started_at TEXT NOT NULL,
        updated_at TEXT NOT NULL,
        ended_at TEXT
      );
      CREATE TABLE IF NOT EXISTS tasks (
        id TEXT PRIMARY KEY,
        session_id TEXT,
        source TEXT NOT NULL,
        workspace TEXT,
        status TEXT NOT NULL,
        title TEXT,
        pid INTEGER,
        started_at TEXT NOT NULL,
        updated_at TEXT NOT NULL,
        ended_at TEXT,
        last_event_id TEXT
      );
      CREATE TABLE IF NOT EXISTS proxy_requests (
        id TEXT PRIMARY KEY,
        provider TEXT NOT NULL,
        method TEXT NOT NULL,
        path TEXT NOT NULL,
        upstream_url TEXT NOT NULL,
        status_code INTEGER,
        duration_ms INTEGER,
        request_summary_json TEXT,
        response_summary_json TEXT,
        error TEXT,
        created_at TEXT NOT NULL
      );
      CREATE TABLE IF NOT EXISTS analysis_results (
        id TEXT PRIMARY KEY,
        event_id TEXT,
        proxy_request_id TEXT,
        analyzer TEXT NOT NULL,
        risk_level TEXT NOT NULL,
        findings_json TEXT NOT NULL,
        created_at TEXT NOT NULL
      );
      CREATE TABLE IF NOT EXISTS notifications (
        id TEXT PRIMARY KEY,
        channel TEXT NOT NULL,
        success INTEGER NOT NULL,
        title TEXT NOT NULL,
        message TEXT NOT NULL,
        error TEXT,
        created_at TEXT NOT NULL
      );
      CREATE TABLE IF NOT EXISTS install_plans (
        id TEXT PRIMARY KEY,
        integration TEXT NOT NULL,
        scope TEXT NOT NULL,
        plan_json TEXT NOT NULL,
        applied INTEGER NOT NULL DEFAULT 0,
        created_at TEXT NOT NULL,
        applied_at TEXT
      );
      CREATE TABLE IF NOT EXISTS backups (
        id TEXT PRIMARY KEY,
        plan_id TEXT,
        file_path TEXT NOT NULL,
        backup_path TEXT NOT NULL,
        created_at TEXT NOT NULL,
        restored_at TEXT
      );
      CREATE TABLE IF NOT EXISTS inventory_sources (
        id TEXT PRIMARY KEY,
        integration TEXT NOT NULL,
        scope TEXT NOT NULL,
        kind TEXT NOT NULL,
        path TEXT NOT NULL,
        exists_flag INTEGER NOT NULL,
        last_modified_at TEXT
      );
      CREATE TABLE IF NOT EXISTS skills (
        id TEXT PRIMARY KEY,
        item_json TEXT NOT NULL
      );
      CREATE TABLE IF NOT EXISTS mcp_servers (
        id TEXT PRIMARY KEY,
        item_json TEXT NOT NULL
      );
      CREATE TABLE IF NOT EXISTS plugins (
        id TEXT PRIMARY KEY,
        item_json TEXT NOT NULL
      );
      CREATE TABLE IF NOT EXISTS settings (
        key TEXT PRIMARY KEY,
        value_json TEXT NOT NULL
      );
    `);
  }
  insertEvent(event) {
    const result = this.db.prepare(
      `INSERT OR IGNORE INTO events
        (id, source, source_type, event_type, timestamp, project_id, workspace, session_id, correlation_id, raw_json, normalized_json, risk_level, tags_json)
        VALUES (@id, @source, @sourceType, @eventType, @timestamp, @projectId, @workspace, @sessionId, @correlationId, @rawJson, @normalizedJson, @riskLevel, @tagsJson)`
    ).run({
      id: event.id,
      source: event.source,
      sourceType: event.sourceType,
      eventType: event.eventType,
      timestamp: event.timestamp,
      projectId: event.projectId ?? null,
      workspace: event.workspace ?? null,
      sessionId: event.sessionId ?? null,
      correlationId: event.correlationId ?? null,
      rawJson: JSON.stringify(event.raw),
      normalizedJson: JSON.stringify(event.normalized),
      riskLevel: event.riskLevel ?? "none",
      tagsJson: JSON.stringify(event.tags ?? [])
    });
    if (result.changes > 0) this.upsertTaskFromEvent(event);
    return { inserted: result.changes > 0, event };
  }
  listEvents(limit = 100) {
    return this.db.prepare("SELECT * FROM events ORDER BY timestamp DESC LIMIT ?").all(limit).map(rowToEvent);
  }
  updateEventRisk(eventId, riskLevel) {
    this.db.prepare("UPDATE events SET risk_level = ? WHERE id = ?").run(riskLevel, eventId);
  }
  upsertTaskFromEvent(event) {
    const now = event.timestamp || nowIso();
    const sessionId = event.sessionId || event.correlationId || `${event.source}:default`;
    const status = eventTypeToTaskStatus(event.eventType);
    this.db.prepare(
      `INSERT INTO sessions (id, source, workspace, status, started_at, updated_at, ended_at)
         VALUES (?, ?, ?, ?, ?, ?, ?)
         ON CONFLICT(id) DO UPDATE SET status = excluded.status, updated_at = excluded.updated_at, ended_at = excluded.ended_at`
    ).run(sessionId, event.source, event.workspace ?? null, status, now, now, isTerminal(status) ? now : null);
    const taskId = event.correlationId || sessionId;
    this.db.prepare(
      `INSERT INTO tasks (id, session_id, source, workspace, status, title, pid, started_at, updated_at, ended_at, last_event_id)
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
         ON CONFLICT(id) DO UPDATE SET status = excluded.status, updated_at = excluded.updated_at, ended_at = excluded.ended_at, last_event_id = excluded.last_event_id`
    ).run(
      taskId,
      sessionId,
      event.source,
      event.workspace ?? null,
      status,
      String(event.normalized.title ?? event.eventType),
      toNumber(event.normalized.pid),
      now,
      now,
      isTerminal(status) ? now : null,
      event.id
    );
  }
  listSessions(limit = 100) {
    return this.db.prepare("SELECT * FROM sessions ORDER BY updated_at DESC LIMIT ?").all(limit).map((row) => ({
      id: row.id,
      source: row.source,
      workspace: row.workspace ?? void 0,
      status: row.status,
      startedAt: row.started_at,
      updatedAt: row.updated_at,
      endedAt: row.ended_at ?? void 0
    }));
  }
  listTasks(limit = 100) {
    return this.db.prepare("SELECT * FROM tasks ORDER BY updated_at DESC LIMIT ?").all(limit).map((row) => ({
      id: row.id,
      sessionId: row.session_id ?? void 0,
      source: row.source,
      workspace: row.workspace ?? void 0,
      status: row.status,
      title: row.title ?? void 0,
      pid: row.pid ?? void 0,
      startedAt: row.started_at,
      updatedAt: row.updated_at,
      endedAt: row.ended_at ?? void 0,
      lastEventId: row.last_event_id ?? void 0
    }));
  }
  markStaleTasks(staleAfterMs) {
    const cutoff = new Date(Date.now() - staleAfterMs).toISOString();
    return this.db.prepare("UPDATE tasks SET status = 'stalled', updated_at = ? WHERE status IN ('running', 'waiting') AND updated_at < ?").run(nowIso(), cutoff).changes;
  }
  upsertInventory(snapshot) {
    const tx = this.db.transaction(() => {
      this.db.prepare("DELETE FROM inventory_sources").run();
      this.db.prepare("DELETE FROM skills").run();
      this.db.prepare("DELETE FROM mcp_servers").run();
      this.db.prepare("DELETE FROM plugins").run();
      const sourceStmt = this.db.prepare(
        `INSERT INTO inventory_sources (id, integration, scope, kind, path, exists_flag, last_modified_at)
         VALUES (@id, @integration, @scope, @kind, @path, @existsFlag, @lastModifiedAt)`
      );
      for (const source of snapshot.sources) {
        sourceStmt.run({ ...source, existsFlag: source.exists ? 1 : 0, lastModifiedAt: source.lastModifiedAt ?? null });
      }
      const insertJson = (table, item) => this.db.prepare(`INSERT INTO ${table} (id, item_json) VALUES (?, ?)`).run(item.id, JSON.stringify(item));
      snapshot.skills.forEach((item) => insertJson("skills", item));
      snapshot.mcpServers.forEach((item) => insertJson("mcp_servers", item));
      snapshot.plugins.forEach((item) => insertJson("plugins", item));
      this.setSetting("inventory.lastScan", snapshot.scannedAt);
    });
    tx();
  }
  getInventory() {
    return {
      sources: this.db.prepare("SELECT * FROM inventory_sources ORDER BY integration, kind, path").all().map((row) => ({
        id: row.id,
        integration: row.integration,
        scope: row.scope,
        kind: row.kind,
        path: row.path,
        exists: row.exists_flag === 1,
        lastModifiedAt: row.last_modified_at ?? void 0
      })),
      skills: this.readJsonTable("skills"),
      mcpServers: this.readJsonTable("mcp_servers"),
      plugins: this.readJsonTable("plugins"),
      scannedAt: String(this.getSetting("inventory.lastScan") || nowIso())
    };
  }
  insertProxyRequest(record) {
    const requestSummary = {
      ...record.requestSummary || {},
      ...record.proxyKey ? { proxyKey: record.proxyKey } : {},
      ...record.apiProtocol ? { apiProtocol: record.apiProtocol } : {}
    };
    this.db.prepare(
      `INSERT INTO proxy_requests (id, provider, method, path, upstream_url, status_code, duration_ms, request_summary_json, response_summary_json, error, created_at)
         VALUES (@id, @provider, @method, @path, @upstreamUrl, @statusCode, @durationMs, @requestSummaryJson, @responseSummaryJson, @error, @createdAt)`
    ).run({
      ...record,
      statusCode: record.statusCode ?? null,
      durationMs: record.durationMs ?? null,
      requestSummaryJson: Object.keys(requestSummary).length ? JSON.stringify(requestSummary) : null,
      responseSummaryJson: record.responseSummary ? JSON.stringify(record.responseSummary) : null,
      error: record.error ?? null
    });
  }
  listProxyRequests(limit = 100) {
    return this.db.prepare("SELECT * FROM proxy_requests ORDER BY created_at DESC LIMIT ?").all(limit).map((row) => {
      const requestSummary = parseJson(row.request_summary_json);
      return {
        id: row.id,
        provider: row.provider,
        proxyKey: typeof requestSummary?.proxyKey === "string" ? requestSummary.proxyKey : void 0,
        apiProtocol: typeof requestSummary?.apiProtocol === "string" ? requestSummary.apiProtocol : void 0,
        method: row.method,
        path: row.path,
        upstreamUrl: row.upstream_url,
        statusCode: row.status_code ?? void 0,
        durationMs: row.duration_ms ?? void 0,
        requestSummary,
        responseSummary: parseJson(row.response_summary_json),
        error: row.error ?? void 0,
        createdAt: row.created_at
      };
    });
  }
  insertAnalysis(result) {
    this.db.prepare(
      `INSERT INTO analysis_results (id, event_id, proxy_request_id, analyzer, risk_level, findings_json, created_at)
         VALUES (?, ?, ?, ?, ?, ?, ?)`
    ).run(
      result.id,
      result.eventId ?? null,
      result.proxyRequestId ?? null,
      result.analyzer,
      result.riskLevel,
      JSON.stringify(result.findings),
      result.createdAt
    );
  }
  insertNotification(input) {
    this.db.prepare("INSERT INTO notifications (id, channel, success, title, message, error, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)").run(input.id, input.channel, input.success ? 1 : 0, input.title, input.message, input.error ?? null, nowIso());
  }
  saveInstallPlan(plan, applied = false) {
    this.db.prepare(
      `INSERT OR REPLACE INTO install_plans (id, integration, scope, plan_json, applied, created_at, applied_at)
         VALUES (?, ?, ?, ?, ?, ?, COALESCE((SELECT applied_at FROM install_plans WHERE id = ?), NULL))`
    ).run(plan.id, plan.integration, plan.scope, JSON.stringify(plan), applied ? 1 : 0, plan.createdAt, plan.id);
  }
  listInstallPlans(limit = 100) {
    return this.db.prepare("SELECT plan_json FROM install_plans ORDER BY created_at DESC LIMIT ?").all(limit).map((row) => JSON.parse(row.plan_json));
  }
  listInstallPlanRecords(limit = 100) {
    return this.db.prepare("SELECT plan_json, applied, applied_at FROM install_plans ORDER BY created_at DESC LIMIT ?").all(limit).map((row) => ({
      plan: JSON.parse(row.plan_json),
      applied: row.applied === 1,
      appliedAt: row.applied_at ?? void 0
    }));
  }
  markPlanApplied(planId) {
    this.db.prepare("UPDATE install_plans SET applied = 1, applied_at = ? WHERE id = ?").run(nowIso(), planId);
  }
  insertBackup(input) {
    this.db.prepare("INSERT INTO backups (id, plan_id, file_path, backup_path, created_at) VALUES (?, ?, ?, ?, ?)").run(input.id, input.planId, input.filePath, input.backupPath, nowIso());
  }
  getLatestBackups() {
    const latest = this.db.prepare("SELECT plan_id FROM backups ORDER BY created_at DESC LIMIT 1").get();
    if (!latest?.plan_id) return [];
    return this.db.prepare("SELECT * FROM backups WHERE plan_id = ? ORDER BY created_at DESC").all(latest.plan_id).map((row) => ({ id: row.id, planId: row.plan_id, filePath: row.file_path, backupPath: row.backup_path }));
  }
  getLatestBackupsForIntegration(integration) {
    const latest = this.db.prepare(
      `SELECT b.plan_id
         FROM backups b
         JOIN install_plans p ON p.id = b.plan_id
         WHERE p.integration = ? AND p.applied = 1
         ORDER BY b.created_at DESC
         LIMIT 1`
    ).get(integration);
    if (!latest?.plan_id) return [];
    return this.db.prepare("SELECT * FROM backups WHERE plan_id = ? ORDER BY created_at DESC").all(latest.plan_id).map((row) => ({ id: row.id, planId: row.plan_id, filePath: row.file_path, backupPath: row.backup_path }));
  }
  markBackupRestored(backupId) {
    this.db.prepare("UPDATE backups SET restored_at = ? WHERE id = ?").run(nowIso(), backupId);
  }
  listProxyRouteMappings() {
    const value = this.getSetting("proxy.routeMappings");
    return Array.isArray(value) ? value.map(normalizeProxyRouteMapping).filter(Boolean) : [];
  }
  getProxyRouteMapping(integration) {
    return this.listProxyRouteMappings().find((mapping) => mapping.integration === integration);
  }
  upsertProxyRouteMapping(input) {
    const mappings = this.listProxyRouteMappings();
    const now = nowIso();
    const existing = mappings.find((mapping) => mapping.integration === input.integration);
    const next = {
      ...input,
      createdAt: existing?.createdAt || now,
      updatedAt: now
    };
    this.setSetting("proxy.routeMappings", [next, ...mappings.filter((mapping) => mapping.integration !== input.integration)]);
    return next;
  }
  deleteProxyRouteMapping(integration) {
    this.setSetting("proxy.routeMappings", this.listProxyRouteMappings().filter((mapping) => mapping.integration !== integration));
  }
  setSetting(key, value) {
    this.db.prepare("INSERT OR REPLACE INTO settings (key, value_json) VALUES (?, ?)").run(key, JSON.stringify(value));
  }
  getSetting(key) {
    const row = this.db.prepare("SELECT value_json FROM settings WHERE key = ?").get(key);
    return parseJson(row?.value_json);
  }
  readJsonTable(table) {
    return this.db.prepare(`SELECT item_json FROM ${table} ORDER BY id`).all().map((row) => JSON.parse(row.item_json));
  }
};
function createStorage(options) {
  return new SqliteAgentPulseStorage(options);
}
function rowToEvent(row) {
  return {
    id: row.id,
    source: row.source,
    sourceType: row.source_type,
    eventType: row.event_type,
    timestamp: row.timestamp,
    projectId: row.project_id ?? void 0,
    workspace: row.workspace ?? void 0,
    sessionId: row.session_id ?? void 0,
    correlationId: row.correlation_id ?? void 0,
    raw: parseJson(row.raw_json),
    normalized: parseJson(row.normalized_json) ?? {},
    riskLevel: row.risk_level,
    tags: parseJson(row.tags_json) ?? []
  };
}
function parseJson(value) {
  if (!value) return void 0;
  try {
    return JSON.parse(value);
  } catch {
    return void 0;
  }
}
function normalizeProxyRouteMapping(value) {
  if (!value || typeof value !== "object") return null;
  const mapping = value;
  if (!mapping.integration || !mapping.provider || !mapping.localRoute || !mapping.proxyBaseUrl || !mapping.upstreamBaseUrl) return null;
  return {
    ...mapping,
    integration: mapping.integration,
    provider: mapping.provider,
    proxyKey: mapping.proxyKey || mapping.integration,
    apiProtocol: mapping.apiProtocol || inferApiProtocol2(String(mapping.provider || mapping.integration)),
    localRoute: mapping.localRoute,
    proxyBaseUrl: mapping.proxyBaseUrl,
    upstreamBaseUrl: mapping.upstreamBaseUrl,
    createdAt: mapping.createdAt || nowIso(),
    updatedAt: mapping.updatedAt || mapping.createdAt || nowIso()
  };
}
function inferApiProtocol2(provider) {
  return provider === "anthropic" || provider === "claude-code" ? "anthropic-compatible" : "openai-compatible";
}
function isTerminal(status) {
  return status === "completed" || status === "failed" || status === "unknown";
}
function toNumber(value) {
  if (typeof value === "number") return value;
  if (typeof value === "string" && value.trim()) {
    const parsed = Number(value);
    return Number.isFinite(parsed) ? parsed : null;
  }
  return null;
}

// ../server/dist/chunk-GYHXPPH4.js
async function buildApp(options = {}) {
  const storage = options.storage || createStorage();
  const workspaceDir = options.workspaceDir || process.cwd();
  const app = Fastify({ logger: true, bodyLimit: 10 * 1024 * 1024 });
  await app.register(cors, { origin: true });
  app.get("/health", async () => ({ ok: true, service: "agent-pulse", timestamp: nowIso() }));
  app.get("/api/health", async () => ({ ok: true, service: "agent-pulse", timestamp: nowIso() }));
  app.post("/ingest/hook/:integration/:event", async (request, reply) => {
    const params = request.params;
    const body = request.body || {};
    const event = normalizeHookEvent(params.integration, params.event, body);
    const analysis = analyzeEvent(event);
    event.riskLevel = analysis.riskLevel;
    const result = storage.insertEvent(event);
    storage.insertAnalysis(analysis);
    storage.updateEventRisk(event.id, analysis.riskLevel);
    if (result.inserted && shouldNotify(event.eventType, analysis.riskLevel)) {
      const notification = await sendNotification({
        channel: process.platform === "win32" ? "windows" : "webhook",
        title: `AgentPulse: ${event.eventType}`,
        message: `${event.source} ${event.eventType} ${analysis.riskLevel}`,
        event
      });
      storage.insertNotification({
        id: newId("notification"),
        channel: notification.channel,
        success: notification.success,
        title: `AgentPulse: ${event.eventType}`,
        message: `${event.source} ${event.eventType}`,
        error: notification.error
      });
    }
    reply.send({ inserted: result.inserted, event, analysis });
  });
  app.get("/api/events", async (request) => {
    const query = request.query;
    return storage.listEvents(Number(query.limit || 100));
  });
  app.get("/api/sessions", async () => storage.listSessions());
  app.get("/api/tasks", async () => storage.listTasks());
  app.get("/api/proxy/requests", async () => storage.listProxyRequests());
  app.get("/api/install/plans", async () => storage.listInstallPlans());
  app.get("/api/inventory", async () => storage.getInventory());
  app.get("/api/inventory/sources", async () => storage.getInventory().sources);
  app.get("/api/inventory/skills", async () => storage.getInventory().skills);
  app.get("/api/inventory/mcp-servers", async () => storage.getInventory().mcpServers);
  app.get("/api/inventory/plugins", async () => storage.getInventory().plugins);
  app.get("/api/inventory/diff", async () => diffInventory(storage.getInventory()));
  app.post("/api/inventory/scan", async () => scanInventory({ workspaceDir, storage }));
  app.get("/api/scan", async () => scan(workspaceDir));
  app.post("/api/agents/scan", async (request) => {
    const body = request.body || {};
    return buildAgentRows(storage, await scan(workspaceDir), {
      workspaceDir,
      scope: body.scope || "user",
      proxyBaseUrl: body.proxyBaseUrl || "http://127.0.0.1:8080"
    });
  });
  app.post("/api/agents/:integration/replace", async (request) => {
    const params = request.params;
    const body = request.body || {};
    const scope = body.scope || "user";
    const plans = await planInstall({ workspaceDir, scope, proxyBaseUrl: body.proxyBaseUrl, storage });
    const plan = plans.find((item) => item.integration === params.integration);
    if (!plan) return { ok: false, error: "integration_not_found", integration: params.integration };
    const result = await applyInstall(plan, { scope, yes: true, storage });
    return { ok: result.ok, integration: params.integration, plan, result };
  });
  app.post("/api/agents/:integration/rollback", async (request) => {
    const params = request.params;
    const result = rollbackIntegration(params.integration, { storage });
    if (!result.ok && result.restoredFiles.length === 0 && (result.deletedFiles || []).length === 0) {
      return { ok: false, integration: params.integration, error: "backup_not_found", result };
    }
    return { ok: result.ok, integration: params.integration, result };
  });
  app.post("/api/install/plan", async (request) => {
    const body = request.body || {};
    return planInstall({ workspaceDir, scope: body.scope || "workspace", proxyBaseUrl: body.proxyBaseUrl, storage });
  });
  app.post("/api/install/apply", async (request) => {
    const body = request.body || {};
    const plan = storage.listInstallPlans().find((item) => item.id === body.planId);
    if (!plan) return { ok: false, error: "plan_not_found" };
    return applyInstall(plan, { yes: body.yes, storage });
  });
  app.post("/api/install/rollback", async () => rollbackLatest({ storage }));
  app.post("/api/notifications/test", async (request) => {
    const body = request.body || {};
    const result = await sendNotification({
      channel: body.channel || "webhook",
      title: "AgentPulse test",
      message: "Notification channel test from AgentPulse",
      config: body.config
    });
    storage.insertNotification({
      id: newId("notification"),
      channel: result.channel,
      success: result.success,
      title: "AgentPulse test",
      message: "Notification channel test from AgentPulse",
      error: result.error
    });
    return result;
  });
  registerProxyRoutes(app, { storage });
  registerStaticWeb(app);
  app.addHook("onClose", async () => storage.close());
  return app;
}
async function buildAgentRows(storage, results, options) {
  const records = storage.listInstallPlanRecords();
  return Promise.all(results.map(async (result) => {
    const latest = records.find((record) => record.plan.integration === result.integration);
    const latestApplied = records.find((record) => record.plan.integration === result.integration && record.applied);
    const adapter = getAdapterByName(result.integration);
    const routeState = adapter ? await adapter.readConfigState({ workspaceDir: options.workspaceDir, scope: options.scope, proxyBaseUrl: options.proxyBaseUrl }) : result.routeState;
    const proxyRoute = adapter?.getProxyRouteProfile({ proxyBaseUrl: options.proxyBaseUrl });
    const targetSource = result.configSources.find((source) => source.kind === "tool-config" && source.scope === options.scope);
    const targetConfigPath = routeState?.configPath || targetSource?.path;
    const proxyUrl = proxyRoute ? joinProxyUrl(options.proxyBaseUrl, proxyRoute.localRoute) : void 0;
    const originalUpstream = routeState?.originalUpstream || routeState?.currentBaseUrl || proxyRoute?.defaultUpstream;
    const willCreateConfig = Boolean(targetConfigPath && targetSource && !targetSource.exists);
    const canReplace = result.capabilities.configInstall && Boolean(targetConfigPath);
    return {
      integration: result.integration,
      detected: result.detected,
      sourceType: result.sourceType,
      routeState,
      configSources: result.configSources,
      capabilities: result.capabilities,
      reasons: result.reasons,
      warnings: [...result.warnings, ...routeState?.warnings || []],
      latestPlan: latest ? { id: latest.plan.id, applied: latest.applied, appliedAt: latest.appliedAt, scope: latest.plan.scope, summary: latest.plan.summary } : void 0,
      scope: options.scope,
      targetConfigPath,
      originalUpstream,
      proxyBaseUrl: proxyUrl,
      willCreateConfig,
      backupRequired: true,
      canReplace,
      canRollback: Boolean(latestApplied)
    };
  }));
}
function joinProxyUrl(proxyBaseUrl, route) {
  return `${proxyBaseUrl.replace(/\/+$/, "")}${route.startsWith("/") ? route : `/${route}`}`;
}
function normalizeHookEvent(integration, eventType, body) {
  const raw = redactSecrets(body);
  const normalized = {
    ...body.normalized && typeof body.normalized === "object" ? body.normalized : {},
    pid: body.pid,
    title: body.title
  };
  const id = String(body.eventId || body.id || stableId("event", { integration, eventType, body: raw }));
  return {
    id,
    source: integration,
    sourceType: sourceTypeFor(integration),
    eventType,
    timestamp: typeof body.timestamp === "string" ? body.timestamp : nowIso(),
    projectId: typeof body.projectId === "string" ? body.projectId : void 0,
    workspace: typeof body.workspace === "string" ? body.workspace : void 0,
    sessionId: typeof body.sessionId === "string" ? body.sessionId : void 0,
    correlationId: typeof body.correlationId === "string" ? body.correlationId : void 0,
    raw,
    normalized,
    riskLevel: "none",
    tags: Array.isArray(body.tags) ? body.tags.map(String) : []
  };
}
function sourceTypeFor(integration) {
  if (["codex", "claude-code", "opencode"].includes(integration)) return "ai-coding";
  return "custom";
}
function shouldNotify(eventType, riskLevel) {
  return eventType === "permission.request" || eventType === "error" || eventType === "session.end" || ["high", "critical"].includes(riskLevel);
}
function registerStaticWeb(app) {
  const current = dirname3(fileURLToPath(import.meta.url));
  const webDist = process.env.AGENT_PULSE_WEB_DIST || join2(current, "..", "..", "web", "dist");
  const docsDist = process.env.AGENT_PULSE_DOCS_DIST || join2(current, "..", "..", "docs", ".vitepress", "dist");
  const clientDist = join2(webDist, "client");
  if (existsSync3(docsDist)) {
    app.register(fastifyStatic, { root: docsDist, prefix: "/docs/", decorateReply: false });
  }
  if (!existsSync3(clientDist)) return;
  app.register(fastifyStatic, { root: join2(clientDist, "assets"), prefix: "/assets/", decorateReply: false });
  app.setNotFoundHandler(async (request, reply) => {
    if (request.raw.url?.startsWith("/api") || request.raw.url?.startsWith("/ingest") || request.raw.url?.startsWith("/proxy")) {
      reply.status(404).send({ error: "not_found" });
      return;
    }
    if (request.raw.url === "/docs") {
      reply.redirect("/docs/");
      return;
    }
    if (process.env.AGENT_PULSE_DISABLE_SSR === "1") {
      reply.type("text/html").send(readFileSync3(join2(clientDist, "index.html"), "utf8"));
      return;
    }
    const rendered = await renderSsrPage(webDist, request.raw.url || "/");
    if (rendered) {
      reply.type("text/html").send(rendered);
      return;
    }
    reply.type("text/html").send(readFileSync3(join2(clientDist, "index.html"), "utf8"));
  });
}
async function renderSsrPage(webDist, url) {
  const templatePath = join2(webDist, "client", "index.html");
  const serverEntry = join2(webDist, "server", "entry-server.js");
  if (!existsSync3(templatePath) || !existsSync3(serverEntry)) return null;
  const template = readFileSync3(templatePath, "utf8");
  const mod = await import(`${pathToFileURL(serverEntry).href}?t=${Date.now()}`);
  const html = await mod.render?.(url);
  if (!html) return null;
  return template.replace("<!--app-html-->", html);
}

// src/index.ts
var program = new Command();
program.name("agent-pulse").description("Local event center for AI agent tools").version("0.1.0");
program.command("scan").description("Scan AI agent integrations").action(async () => {
  const results = await scan(process.cwd());
  printJson(results);
});
program.command("inventory").description("Scan local skills, MCP servers, plugins and config sources").option("--skills", "print only skills").option("--mcp", "print only MCP servers").option("--diff", "print diff findings").action(async (options) => {
  const storage = createStorage();
  const snapshot = await scanInventory({ workspaceDir: process.cwd(), storage });
  storage.close();
  if (options.skills) return printJson(snapshot.skills);
  if (options.mcp) return printJson(snapshot.mcpServers);
  if (options.diff) {
    const { diffInventory: diffInventory2 } = await import("./dist-MAUFQ26Y.js");
    return printJson(diffInventory2(snapshot));
  }
  printJson(snapshot);
});
program.command("plan").description("Generate install plans without changing files").option("--scope <scope>", "workspace, user, or global", "workspace").option("--proxy-base-url <url>", "AgentPulse local base URL", "http://127.0.0.1:8080").action(async (options) => {
  const storage = createStorage();
  const plans = await planInstall({ workspaceDir: process.cwd(), scope: options.scope, proxyBaseUrl: options.proxyBaseUrl, storage });
  storage.close();
  printJson(plans);
});
program.command("install").description("Apply the latest generated install plan").option("--scope <scope>", "workspace, user, or global", "workspace").option("--yes", "allow non-workspace writes").action(async (options) => {
  const storage = createStorage();
  let plans = storage.listInstallPlans();
  if (plans.length === 0) {
    plans = await planInstall({ workspaceDir: process.cwd(), scope: options.scope, storage });
  }
  const results = await Promise.all(plans.map((plan) => applyInstall(plan, { scope: options.scope, yes: options.yes, storage })));
  storage.close();
  printJson(results);
});
program.command("rollback").description("Rollback the latest applied plan backup").action(() => {
  const storage = createStorage();
  const result = rollbackLatest({ storage });
  storage.close();
  printJson(result);
});
program.command("start").description("Start local AgentPulse server").option("--host <host>", "host", process.env.AGENT_PULSE_HOST || "127.0.0.1").option("--port <port>", "port", process.env.AGENT_PULSE_PORT || "8080").action(async (options) => {
  configureStaticAssetPaths();
  const app = await buildApp({ workspaceDir: process.cwd() });
  await app.listen({ host: options.host, port: Number(options.port) });
  console.log(`AgentPulse listening at http://${options.host}:${options.port}`);
  console.log(`AgentPulse docs available at http://${options.host}:${options.port}/docs/`);
});
program.command("doctor").description("Check local AgentPulse health").action(async () => {
  const storage = createStorage();
  const integrations = await scan(process.cwd());
  const inventory = storage.getInventory();
  const tasks = storage.listTasks(5);
  storage.close();
  printJson({
    ok: true,
    node: process.version,
    platform: process.platform,
    integrations,
    inventory: {
      sources: inventory.sources.length,
      skills: inventory.skills.length,
      mcpServers: inventory.mcpServers.length,
      plugins: inventory.plugins.length
    },
    recentTasks: tasks
  });
});
program.parseAsync(process.argv);
function printJson(value) {
  console.log(JSON.stringify(value, null, 2));
}
function configureStaticAssetPaths() {
  const currentDir = dirname4(fileURLToPath2(import.meta.url));
  const candidates = [
    {
      web: join3(currentDir, "..", "web", "dist"),
      docs: join3(currentDir, "..", "docs")
    },
    {
      web: join3(currentDir, "..", "..", "web", "dist"),
      docs: join3(currentDir, "..", "..", "docs", ".vitepress", "dist")
    },
    {
      web: join3(process.cwd(), "apps", "web", "dist"),
      docs: join3(process.cwd(), "apps", "docs", ".vitepress", "dist")
    }
  ];
  const match = candidates.find((candidate) => existsSync4(join3(candidate.web, "client")) || existsSync4(candidate.docs));
  if (!match) return;
  process.env.AGENT_PULSE_WEB_DIST ||= match.web;
  process.env.AGENT_PULSE_DOCS_DIST ||= match.docs;
  process.env.AGENT_PULSE_DISABLE_SSR ||= "1";
}
