// ../../packages/inventory/dist/index.js
import { join as join4 } from "path";

// ../../packages/core/dist/index.js
import { createHash, randomUUID } from "crypto";
import { homedir } from "os";
import { join } from "path";
function newId(prefix = "ap") {
  return `${prefix}_${randomUUID()}`;
}
function stableId(prefix, value) {
  return `${prefix}_${createHash("sha256").update(JSON.stringify(value)).digest("hex").slice(0, 16)}`;
}
function nowIso() {
  return (/* @__PURE__ */ new Date()).toISOString();
}
function getDataDir() {
  return process.env.AGENT_PULSE_DATA_DIR || join(homedir(), ".agent-pulse");
}
function getDefaultDatabasePath() {
  return join(getDataDir(), "agent-pulse.db");
}
function eventTypeToTaskStatus(eventType) {
  if (eventType === "permission.request") return "waiting";
  if (eventType === "session.end" || eventType === "message.output" || eventType === "tool.result") return "completed";
  if (eventType === "error") return "failed";
  return "running";
}
function maxRisk(left = "none", right = "none") {
  const order = ["none", "low", "medium", "high", "critical"];
  return order[Math.max(order.indexOf(left), order.indexOf(right))] ?? "none";
}
function summarizeUnknown(value, maxLength = 2048) {
  const redacted = redactSecrets(value);
  const text = JSON.stringify(redacted);
  if (text.length <= maxLength) return { value: redacted, truncated: false };
  return { value: `${text.slice(0, maxLength)}\u2026`, truncated: true };
}
function redactSecrets(value) {
  if (Array.isArray(value)) return value.map((item) => redactSecrets(item));
  if (!value || typeof value !== "object") {
    if (typeof value === "string") return redactSecretString(value);
    return value;
  }
  const output = {};
  for (const [key, item] of Object.entries(value)) {
    if (isSensitiveKey(key)) {
      output[key] = "<redacted>";
    } else {
      output[key] = redactSecrets(item);
    }
  }
  return output;
}
function isSensitiveKey(key) {
  return /(api[_-]?key|token|secret|password|passwd|authorization|cookie|session)/i.test(key);
}
function redactSecretString(value) {
  return value.replace(
    /(sk-[a-zA-Z0-9_-]{16,}|xox[baprs]-[a-zA-Z0-9-]{16,}|gh[pousr]_[a-zA-Z0-9_]{16,}|Bearer\s+[a-zA-Z0-9._-]{16,})/g,
    "<redacted>"
  );
}

// ../../packages/integrations/dist/index.js
import { resolve as resolve2 } from "path";
import { homedir as homedir3 } from "os";
import { join as join3 } from "path";

// ../../packages/probes/dist/index.js
import { access, readdir, readFile, stat } from "fs/promises";
import { constants } from "fs";
import { homedir as homedir2, platform, release, type } from "os";
import { delimiter, dirname, extname, join as join2, resolve } from "path";
import { spawn } from "child_process";
function createSystemProbe(workspaceDir = process.cwd()) {
  return {
    getPlatform: () => ({ platform: platform(), release: release(), type: type(), arch: process.arch }),
    getHomeDir: () => homedir2(),
    getWorkspaceDir: () => resolve(workspaceDir),
    getConfigDirs: () => [
      { scope: "workspace", path: resolve(workspaceDir) },
      { scope: "user", path: homedir2() }
    ]
  };
}
function createFileProbe() {
  return {
    async exists(path) {
      try {
        await access(path, constants.F_OK);
        return true;
      } catch {
        return false;
      }
    },
    async stat(path) {
      try {
        const result = await stat(path);
        return {
          path,
          size: result.size,
          isDirectory: result.isDirectory(),
          isFile: result.isFile(),
          lastModifiedAt: result.mtime.toISOString()
        };
      } catch {
        return null;
      }
    },
    async listDir(path) {
      try {
        const entries = await readdir(path, { withFileTypes: true });
        return entries.map((entry) => ({
          path: join2(path, entry.name),
          name: entry.name,
          isDirectory: entry.isDirectory(),
          isFile: entry.isFile()
        }));
      } catch {
        return [];
      }
    },
    async readText(path, maxBytes = 256 * 1024) {
      const info = await stat(path);
      if (info.size > maxBytes) throw new Error(`Refusing to read large file: ${path}`);
      return readFile(path, "utf8");
    }
  };
}
function createCommandProbe() {
  return {
    async which(command) {
      const names = process.platform === "win32" && !/\.(exe|cmd|bat|ps1)$/i.test(command) ? [command, `${command}.exe`, `${command}.cmd`, `${command}.bat`, `${command}.ps1`] : [command];
      for (const dir of (process.env.PATH || "").split(delimiter)) {
        for (const name of names) {
          const candidate = join2(dir, name);
          try {
            await access(candidate, constants.X_OK);
            return candidate;
          } catch {
            try {
              await access(candidate, constants.F_OK);
              return candidate;
            } catch {
            }
          }
        }
      }
      return null;
    },
    async getVersion(command, args = ["--version"]) {
      return new Promise((resolveResult) => {
        const child = spawn(command, args, { shell: true, windowsHide: true });
        let output = "";
        let error = "";
        child.stdout.on("data", (chunk) => output += String(chunk));
        child.stderr.on("data", (chunk) => error += String(chunk));
        child.on("error", (err) => resolveResult({ ok: false, error: err.message }));
        child.on("close", (code) => resolveResult({ ok: code === 0, output: output.trim() || error.trim(), error: code === 0 ? void 0 : error.trim() }));
      });
    },
    async isExecutable(path) {
      try {
        await access(path, constants.X_OK);
        return true;
      } catch {
        return false;
      }
    }
  };
}
function createConfigProbe() {
  return {
    async parseJson(path) {
      return parseConfig(path, (text) => JSON.parse(text));
    },
    async parseJsonc(path) {
      return parseConfig(path, (text) => JSON.parse(stripJsonComments(text)));
    },
    async parseToml(path) {
      return parseConfig(path, parseSimpleToml);
    },
    async parseYaml(path) {
      return parseConfig(path, parseSimpleYaml);
    }
  };
}
function createSkillProbe() {
  const fileProbe2 = createFileProbe();
  return {
    async isSkillDirectory(path) {
      const entryFile = join2(path, "SKILL.md");
      if (!await fileProbe2.exists(entryFile)) {
        return { isSkill: false, confidence: "low", reasons: ["SKILL.md not found"] };
      }
      const metadata = await this.readSkillMetadata(path);
      return {
        isSkill: true,
        confidence: "high",
        reasons: ["SKILL.md found"],
        entryFile,
        name: metadata.value?.name,
        description: metadata.value?.description
      };
    },
    async readSkillMetadata(path) {
      const entryFile = join2(path, "SKILL.md");
      try {
        const text = await fileProbe2.readText(entryFile, 128 * 1024);
        const heading = text.match(/^#\s+(.+)$/m)?.[1]?.trim();
        const description = text.split(/\r?\n/).map((line) => line.trim()).find((line) => line && !line.startsWith("#") && !line.startsWith("---"));
        return {
          ok: true,
          confidence: "high",
          value: { name: heading || dirname(path).split(/[\\/]/).pop() || path, description, entryFile },
          reasons: ["Read SKILL.md metadata"]
        };
      } catch (error) {
        return { ok: false, errors: [{ code: "skill.read_failed", message: String(error), path: entryFile }] };
      }
    }
  };
}
function createMcpProbe() {
  return {
    isMcpConfig(value) {
      const servers = this.extractMcpServers(value);
      return { ok: true, value: servers.length > 0, confidence: servers.length > 0 ? "high" : "low" };
    },
    extractMcpServers(config, sourcePath) {
      const root = config && typeof config === "object" ? config : {};
      const serverMap = findServerMap(root);
      return Object.entries(serverMap).map(([name, raw]) => {
        const item = raw && typeof raw === "object" ? raw : {};
        const url = stringValue(item.url) || stringValue(item.endpoint);
        const command = stringValue(item.command);
        const env = item.env && typeof item.env === "object" ? item.env : {};
        const envKeys = Object.keys(env);
        return {
          name,
          transport: detectTransport(item, url, command),
          command,
          args: Array.isArray(item.args) ? item.args.map(String) : void 0,
          url,
          envKeys,
          sourcePath,
          confidence: command || url ? "high" : "medium",
          riskLevel: envKeys.some(isSensitiveKey) ? "medium" : "none"
        };
      });
    }
  };
}
function createSecretProbe() {
  return {
    isSensitiveKey,
    containsSecret(value) {
      const findings = [];
      if (/(sk-[a-zA-Z0-9_-]{16,}|gh[pousr]_[a-zA-Z0-9_]{16,}|Bearer\s+[a-zA-Z0-9._-]{16,})/.test(value)) {
        findings.push({ code: "secret.pattern", message: "Sensitive token-like value detected" });
      }
      return findings;
    },
    redactValue(value) {
      return redactSecrets(value);
    },
    redactObject(value) {
      return redactSecrets(value);
    }
  };
}
function createProcessProbe() {
  return {
    async isAlive(pid) {
      try {
        process.kill(pid, 0);
        return true;
      } catch {
        return false;
      }
    },
    async describe(pid) {
      const alive = await this.isAlive(pid);
      return alive ? { pid, alive } : null;
    }
  };
}
var probes = {
  system: createSystemProbe(),
  file: createFileProbe(),
  command: createCommandProbe(),
  config: createConfigProbe(),
  skill: createSkillProbe(),
  mcp: createMcpProbe(),
  secret: createSecretProbe(),
  process: createProcessProbe()
};
async function parseConfig(path, parser) {
  try {
    const text = await createFileProbe().readText(path);
    return { ok: true, value: parser(text), confidence: "high", reasons: [`Parsed ${extname(path) || "config"} file`] };
  } catch (error) {
    return { ok: false, errors: [{ code: "config.parse_failed", message: String(error), path }] };
  }
}
function stripJsonComments(text) {
  return text.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|\s)\/\/.*$/gm, "$1");
}
function parseSimpleToml(text) {
  const root = {};
  let current = root;
  for (const rawLine of text.split(/\r?\n/)) {
    const line = rawLine.trim();
    if (!line || line.startsWith("#")) continue;
    const section = line.match(/^\[(.+)]$/)?.[1];
    if (section) {
      current = root;
      for (const part of section.split(".").map((item) => item.replace(/^["']|["']$/g, ""))) {
        current[part] = current[part] && typeof current[part] === "object" ? current[part] : {};
        current = current[part];
      }
      continue;
    }
    const eq = line.indexOf("=");
    if (eq === -1) continue;
    current[line.slice(0, eq).trim()] = parseScalar(line.slice(eq + 1).trim());
  }
  return root;
}
function parseSimpleYaml(text) {
  const root = {};
  for (const rawLine of text.split(/\r?\n/)) {
    const line = rawLine.trim();
    if (!line || line.startsWith("#") || !line.includes(":")) continue;
    const [key, ...rest] = line.split(":");
    if (key) root[key.trim()] = parseScalar(rest.join(":").trim());
  }
  return root;
}
function parseScalar(value) {
  const trimmed = value.trim();
  if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
    return trimmed.slice(1, -1);
  }
  if (trimmed === "true") return true;
  if (trimmed === "false") return false;
  if (/^\d+(\.\d+)?$/.test(trimmed)) return Number(trimmed);
  if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
    return trimmed.slice(1, -1).split(",").map((item) => parseScalar(item.trim()));
  }
  return trimmed;
}
function findServerMap(root) {
  const candidates = [
    root.mcp_servers,
    root.mcpServers,
    root.servers,
    root.mcp && typeof root.mcp === "object" ? root.mcp.servers : void 0
  ];
  for (const candidate of candidates) {
    if (candidate && typeof candidate === "object" && !Array.isArray(candidate)) {
      return candidate;
    }
  }
  return {};
}
function stringValue(value) {
  return typeof value === "string" && value.trim() ? value : void 0;
}
function detectTransport(item, url, command) {
  const explicit = stringValue(item.transport);
  if (explicit === "stdio" || explicit === "http" || explicit === "sse" || explicit === "websocket") return explicit;
  if (command) return "stdio";
  if (url?.startsWith("ws")) return "websocket";
  if (url?.includes("/sse")) return "sse";
  if (url) return "http";
  return "unknown";
}
function sourceId(integration, path) {
  return stableId("src", { integration, path });
}

// ../../packages/integrations/dist/index.js
import { readFile as readFile2 } from "fs/promises";
import { homedir as homedir22 } from "os";
import { join as join22 } from "path";
import { homedir as homedir32 } from "os";
import { join as join32 } from "path";
var fileProbe = createFileProbe();
async function buildSources(integration, specs) {
  return Promise.all(
    specs.map(async ([scope, kind, path]) => {
      const info = await fileProbe.stat(path);
      return {
        id: sourceId(integration, path),
        integration,
        scope,
        kind,
        path,
        exists: Boolean(info),
        lastModifiedAt: info?.lastModifiedAt
      };
    })
  );
}
async function readJsonLikeConfig(path) {
  try {
    const text = await readFile2(path, "utf8");
    return JSON.parse(stripJsonComments2(text));
  } catch {
    return null;
  }
}
async function readTomlConfig(path) {
  try {
    const text = await readFile2(path, "utf8");
    return parseSimpleToml2(text);
  } catch {
    return null;
  }
}
function summarizeConfig(value) {
  const redacted = redactSecrets(value);
  if (!redacted || typeof redacted !== "object" || Array.isArray(redacted)) return { value: redacted };
  return redacted;
}
function getPathValue(root, path) {
  let current = root;
  for (const part of path) {
    if (!current || typeof current !== "object") return void 0;
    current = Array.isArray(current) ? current[Number(part)] : current[part];
  }
  return current;
}
function setPathValue(root, path, value) {
  const output = cloneObject(root);
  let current = output;
  path.forEach((part, index) => {
    if (index === path.length - 1) {
      if (Array.isArray(current)) current[Number(part)] = value;
      else current[part] = value;
      return;
    }
    const next = Array.isArray(current) ? current[Number(part)] : current[part];
    const replacement = next && typeof next === "object" ? cloneObject(next) : /^\d+$/.test(path[index + 1] || "") ? [] : {};
    if (Array.isArray(current)) current[Number(part)] = replacement;
    else current[part] = replacement;
    current = replacement;
  });
  return output;
}
function targetBackupId(planId, filePath) {
  return stableId("backup", { planId, filePath });
}
function cloneObject(value) {
  return JSON.parse(JSON.stringify(value));
}
function stripJsonComments2(text) {
  return text.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|\s)\/\/.*$/gm, "$1");
}
function parseSimpleToml2(text) {
  const root = {};
  let current = root;
  for (const rawLine of text.split(/\r?\n/)) {
    const line = rawLine.trim();
    if (!line || line.startsWith("#")) continue;
    const section = line.match(/^\[(.+)]$/)?.[1];
    if (section) {
      current = root;
      for (const part of section.split(".").map((item) => item.replace(/^["']|["']$/g, ""))) {
        current[part] = current[part] && typeof current[part] === "object" ? current[part] : {};
        current = current[part];
      }
      continue;
    }
    const eq = line.indexOf("=");
    if (eq === -1) continue;
    current[line.slice(0, eq).trim()] = parseScalar2(line.slice(eq + 1).trim());
  }
  return root;
}
function parseScalar2(value) {
  const trimmed = value.trim();
  if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
    return trimmed.slice(1, -1);
  }
  if (trimmed === "true") return true;
  if (trimmed === "false") return false;
  if (/^\d+(\.\d+)?$/.test(trimmed)) return Number(trimmed);
  if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
    return trimmed.slice(1, -1).split(",").map((item) => parseScalar2(item.trim()));
  }
  return trimmed;
}
var sensitiveHeaders = ["authorization", "cookie", "x-api-key", "api-key", "anthropic-api-key", "session"];
function normalizeProxyBaseUrl(proxyBaseUrl, route) {
  return `${proxyBaseUrl.replace(/\/+$/, "")}${route.startsWith("/") ? route : `/${route}`}`;
}
function openAiRouteProfile(provider, localRoute) {
  return {
    provider,
    localRoute,
    upstreamEnv: "AGENT_PULSE_OPENAI_UPSTREAM",
    defaultUpstream: "https://api.openai.com",
    streamingMode: "passthrough",
    sensitiveHeaders,
    pathMode: "preserve"
  };
}
function anthropicRouteProfile(provider, localRoute) {
  return {
    provider,
    localRoute,
    upstreamEnv: "AGENT_PULSE_ANTHROPIC_UPSTREAM",
    defaultUpstream: "https://api.anthropic.com",
    streamingMode: "passthrough",
    sensitiveHeaders,
    pathMode: "preserve"
  };
}
var commandProbe = createCommandProbe();
var knownFields = ["ANTHROPIC_BASE_URL", "apiBaseUrl", "anthropicBaseUrl"];
var officialClaudeBaseUrl = "https://api.anthropic.com";
var claudeCodeAdapter = {
  name: "claude-code",
  sourceType: "ai-coding",
  async detect(input) {
    const sources = await this.getInventorySources(input);
    const command = await commandProbe.which("claude") || await commandProbe.which("claude-code");
    const routeState = await this.readConfigState(input);
    const detected = Boolean(command) || sources.some((source) => source.exists);
    return {
      integration: this.name,
      detected,
      sourceType: this.sourceType,
      configSources: sources,
      capabilities: { hook: true, proxy: true, transcript: true, configInstall: true, rollback: true },
      reasons: [command ? `Claude command found at ${command}` : "Claude command not found", routeState.routed ? "Claude Code already routes through AgentPulse" : "Claude Code route state inspected"],
      warnings: detected ? routeState.warnings : ["Claude Code not detected on this machine", ...routeState.warnings],
      routeState
    };
  },
  async getInventorySources(input) {
    return buildSources("claude-code", [
      ["workspace", "tool-config", join3(input.workspaceDir, ".claude", "settings.json")],
      ["workspace", "skill-dir", join3(input.workspaceDir, ".claude", "skills")],
      ["workspace", "plugin-dir", join3(input.workspaceDir, ".claude", "plugins")],
      ["user", "tool-config", join3(homedir3(), ".claude", "settings.json")],
      ["user", "skill-dir", join3(homedir3(), ".claude", "skills")],
      ["user", "plugin-dir", join3(homedir3(), ".claude", "plugins")]
    ]);
  },
  async readConfigState(input) {
    return readClaudeState(input);
  },
  async planProxyInstall(input) {
    const state = await readClaudeState(input);
    const planId = newId("plan");
    const proxyUrl = normalizeProxyBaseUrl(input.proxyBaseUrl, "/proxy/claude-code");
    const target = targetPath(input);
    const current = await readJsonLikeConfig(target) || {};
    const baseUrlPath = findClaudeBaseUrlPath(current) || ["env", "ANTHROPIC_BASE_URL"];
    const after = setPathValue(current, baseUrlPath, proxyUrl);
    return {
      id: planId,
      integration: this.name,
      createdAt: nowIso(),
      scope: input.scope,
      summary: "Route Claude Code API-key mode through AgentPulse proxy.",
      preflight: state,
      proxyRoute: this.getProxyRouteProfile(),
      actions: [
        {
          type: "file.patch",
          filePath: target,
          description: "Route Claude Code Anthropic-compatible traffic through AgentPulse proxy.",
          before: summarizeConfig(current),
          after,
          backupRequired: true,
          scope: input.scope,
          format: "json",
          writeMode: "merge",
          preserveFormatting: false
        }
      ],
      risks: [{ level: "low", message: "Claude Code settings schema can vary; unknown fields are preserved and rollback is available." }],
      rollback: { backupId: stableId("backup", { planId }), files: [{ filePath: target, backupPath: targetBackupId(planId, target) }] },
      verification: { ok: false, checkedFiles: [target], expectedProxyBaseUrl: proxyUrl, warnings: ["Plan has not been applied yet."] }
    };
  },
  async planInstall(input) {
    return this.planProxyInstall(input);
  },
  async verifyInstall(plan) {
    const expected = plan.verification?.expectedProxyBaseUrl;
    const checkedFiles = plan.actions.filter((action) => action.type === "file.patch").map((action) => action.filePath);
    const warnings = [];
    let ok = checkedFiles.length > 0;
    for (const filePath of checkedFiles) {
      const config = await readJsonLikeConfig(filePath);
      const actual = config ? findClaudeBaseUrl(config) : void 0;
      if (!expected || actual !== expected) {
        ok = false;
        warnings.push(`Claude Code proxy URL was not verified in ${filePath}`);
      }
    }
    return { ok, checkedFiles, expectedProxyBaseUrl: expected, warnings };
  },
  getProxyRouteProfile() {
    return anthropicRouteProfile("claude-code", "/proxy/claude-code");
  }
};
async function readClaudeState(input) {
  const target = targetPath({ workspaceDir: input.workspaceDir, scope: input.scope || "workspace", proxyBaseUrl: input.proxyBaseUrl || "http://127.0.0.1:8080" });
  const config = await readJsonLikeConfig(target);
  const currentBaseUrl = config ? findClaudeBaseUrl(config) || officialClaudeBaseUrl : void 0;
  const expected = normalizeProxyBaseUrl(input.proxyBaseUrl || "http://127.0.0.1:8080", "/proxy/claude-code");
  const warnings = config ? [] : [`Claude Code config not found or not parseable: ${target}`];
  return {
    integration: "claude-code",
    routed: currentBaseUrl === expected || Boolean(currentBaseUrl?.includes("/proxy/claude-code")),
    configPath: target,
    currentBaseUrl,
    originalUpstream: currentBaseUrl?.includes("/proxy/claude-code") ? void 0 : currentBaseUrl,
    confidence: config ? currentBaseUrl ? "high" : "medium" : "low",
    warnings,
    summary: config ? summarizeConfig(config) : void 0
  };
}
function targetPath(input) {
  return input.scope === "workspace" ? join3(input.workspaceDir, ".claude", "settings.json") : join3(homedir3(), ".claude", "settings.json");
}
function findClaudeBaseUrl(config) {
  const envValue = getPathValue(config, ["env", "ANTHROPIC_BASE_URL"]);
  if (typeof envValue === "string") return envValue;
  for (const field of knownFields) {
    if (typeof config[field] === "string") return config[field];
  }
  return void 0;
}
function findClaudeBaseUrlPath(config) {
  if (typeof getPathValue(config, ["env", "ANTHROPIC_BASE_URL"]) === "string") return ["env", "ANTHROPIC_BASE_URL"];
  for (const field of knownFields) {
    if (typeof config[field] === "string") return [field];
  }
  return void 0;
}
var commandProbe2 = createCommandProbe();
var codexAdapter = {
  name: "codex",
  sourceType: "ai-coding",
  async detect(input) {
    const sources = await this.getInventorySources(input);
    const command = await commandProbe2.which("codex");
    const routeState = await this.readConfigState(input);
    const detected = Boolean(command) || sources.some((source) => source.exists);
    return {
      integration: this.name,
      detected,
      sourceType: this.sourceType,
      configSources: sources,
      capabilities: { hook: false, proxy: true, transcript: true, configInstall: true, rollback: true },
      reasons: [command ? `codex command found at ${command}` : "codex command not found", routeState.routed ? "Codex already routes through AgentPulse" : "Codex route state inspected"],
      warnings: detected ? routeState.warnings : ["Codex not detected on this machine", ...routeState.warnings],
      routeState
    };
  },
  async getInventorySources(input) {
    return buildSources("codex", [
      ["workspace", "tool-config", join22(input.workspaceDir, ".codex", "config.toml")],
      ["workspace", "skill-dir", join22(input.workspaceDir, ".codex", "skills")],
      ["workspace", "plugin-dir", join22(input.workspaceDir, ".codex", "plugins")],
      ["user", "tool-config", join22(homedir22(), ".codex", "config.toml")],
      ["user", "skill-dir", join22(homedir22(), ".codex", "skills")],
      ["user", "plugin-dir", join22(homedir22(), ".codex", "plugins")]
    ]);
  },
  async readConfigState(input) {
    return readCodexState(input);
  },
  async planProxyInstall(input) {
    const state = await readCodexState(input);
    const planId = newId("plan");
    const proxyUrl = normalizeProxyBaseUrl(input.proxyBaseUrl, "/proxy/codex");
    const target = targetPath2(input);
    const current = await readTomlConfig(target) || {};
    const writablePath = findCodexBaseUrlPath(current);
    const actions = [];
    const risks = [];
    const after = writablePath ? setPathValue(current, writablePath, proxyUrl) : current;
    if (writablePath) {
      actions.push({
        type: "file.patch",
        filePath: target,
        description: "Route Codex API-key mode traffic through AgentPulse proxy.",
        before: summarizeConfig(current),
        after,
        backupRequired: true,
        scope: input.scope,
        format: "toml",
        writeMode: "structured-patch",
        preserveFormatting: false
      });
    } else {
      actions.push({
        type: "command.suggestion",
        command: "agent-pulse scan",
        reason: "Codex config schema does not expose a known base_url field; inspect config and configure proxy manually."
      });
      risks.push({ level: "medium", message: "Codex config schema was not recognized; no file patch will be applied." });
    }
    actions.push({ type: "command.suggestion", command: "agent-pulse start", reason: "Start local AgentPulse before routing Codex traffic through the proxy." });
    return {
      id: planId,
      integration: this.name,
      createdAt: nowIso(),
      scope: input.scope,
      summary: "Route Codex API-key mode through AgentPulse proxy.",
      preflight: state,
      proxyRoute: this.getProxyRouteProfile(),
      actions,
      risks,
      rollback: { backupId: stableId("backup", { planId }), files: writablePath ? [{ filePath: target, backupPath: targetBackupId(planId, target) }] : [] },
      verification: { ok: false, checkedFiles: [target], expectedProxyBaseUrl: proxyUrl, warnings: ["Plan has not been applied yet."] }
    };
  },
  async planInstall(input) {
    return this.planProxyInstall(input);
  },
  async verifyInstall(plan) {
    const expected = plan.verification?.expectedProxyBaseUrl || plan.proxyRoute?.localRoute;
    const checkedFiles = plan.actions.filter((action) => action.type === "file.patch").map((action) => action.filePath);
    const warnings = [];
    let ok = checkedFiles.length > 0;
    for (const filePath of checkedFiles) {
      const config = await readTomlConfig(filePath);
      const actual = config ? findCodexBaseUrl(config) : void 0;
      if (!expected || actual !== expected) {
        ok = false;
        warnings.push(`Codex proxy URL was not verified in ${filePath}`);
      }
    }
    return { ok, checkedFiles, expectedProxyBaseUrl: expected, warnings };
  },
  getProxyRouteProfile() {
    return openAiRouteProfile("codex", "/proxy/codex");
  }
};
async function readCodexState(input) {
  const target = targetPath2({ workspaceDir: input.workspaceDir, scope: input.scope || "workspace", proxyBaseUrl: input.proxyBaseUrl || "http://127.0.0.1:8080" });
  const config = await readTomlConfig(target);
  const currentBaseUrl = config ? findCodexBaseUrl(config) : void 0;
  const expected = normalizeProxyBaseUrl(input.proxyBaseUrl || "http://127.0.0.1:8080", "/proxy/codex");
  const warnings = config ? [] : [`Codex config not found or not parseable: ${target}`];
  return {
    integration: "codex",
    routed: currentBaseUrl === expected || Boolean(currentBaseUrl?.includes("/proxy/codex")),
    configPath: target,
    currentBaseUrl,
    originalUpstream: currentBaseUrl?.includes("/proxy/codex") ? void 0 : currentBaseUrl,
    confidence: config ? currentBaseUrl ? "high" : "medium" : "low",
    warnings,
    summary: config ? summarizeConfig(config) : void 0
  };
}
function targetPath2(input) {
  return input.scope === "workspace" ? join22(input.workspaceDir, ".codex", "config.toml") : join22(homedir22(), ".codex", "config.toml");
}
function findCodexBaseUrl(config) {
  const path = findCodexBaseUrlPath(config);
  const value = path ? getPathValue(config, path) : void 0;
  return typeof value === "string" ? value : void 0;
}
function findCodexBaseUrlPath(config) {
  if (typeof config.base_url === "string") return ["base_url"];
  const providerName = typeof config.model_provider === "string" ? config.model_provider : void 0;
  for (const groupName of ["model_providers", "providers"]) {
    const group = config[groupName];
    if (!group || typeof group !== "object" || Array.isArray(group)) continue;
    if (providerName) {
      const provider = group[providerName];
      if (provider && typeof provider === "object" && !Array.isArray(provider) && typeof provider.base_url === "string") {
        return [groupName, providerName, "base_url"];
      }
    }
    for (const [name, raw] of Object.entries(group)) {
      if (raw && typeof raw === "object" && !Array.isArray(raw) && typeof raw.base_url === "string") {
        return [groupName, name, "base_url"];
      }
    }
  }
  return void 0;
}
var commandProbe3 = createCommandProbe();
var endpointFields = ["baseURL", "baseUrl", "apiBase", "endpoint"];
var openCodeAdapter = {
  name: "opencode",
  sourceType: "ai-coding",
  async detect(input) {
    const sources = await this.getInventorySources(input);
    const command = await commandProbe3.which("opencode");
    const routeState = await this.readConfigState(input);
    const detected = Boolean(command) || sources.some((source) => source.exists);
    return {
      integration: this.name,
      detected,
      sourceType: this.sourceType,
      configSources: sources,
      capabilities: { hook: false, proxy: true, transcript: false, configInstall: true, rollback: true },
      reasons: [command ? `opencode command found at ${command}` : "opencode command not found", routeState.routed ? "OpenCode already routes through AgentPulse" : "OpenCode route state inspected"],
      warnings: detected ? routeState.warnings : ["OpenCode not detected", ...routeState.warnings],
      routeState
    };
  },
  async getInventorySources(input) {
    return buildSources("opencode", [
      ["workspace", "tool-config", join32(input.workspaceDir, "opencode.json")],
      ["user", "tool-config", join32(homedir32(), ".config", "opencode", "config.json")]
    ]);
  },
  async readConfigState(input) {
    return readOpenCodeState(input);
  },
  async planProxyInstall(input) {
    const state = await readOpenCodeState(input);
    const planId = newId("plan");
    const proxyUrl = normalizeProxyBaseUrl(input.proxyBaseUrl, "/proxy/opencode");
    const target = targetPath3(input);
    const current = await readJsonLikeConfig(target) || {};
    const endpointPath = findOpenCodeEndpointPath(current);
    const actions = [];
    const risks = [];
    if (endpointPath) {
      actions.push({
        type: "file.patch",
        filePath: target,
        description: "Route OpenCode default provider traffic through AgentPulse proxy.",
        before: summarizeConfig(current),
        after: setPathValue(current, endpointPath, proxyUrl),
        backupRequired: true,
        scope: input.scope,
        format: "json",
        writeMode: "merge",
        preserveFormatting: false
      });
    } else {
      actions.push({
        type: "command.suggestion",
        command: "agent-pulse scan",
        reason: "OpenCode config has multiple or unknown providers; configure the default provider proxy manually."
      });
      risks.push({ level: "medium", message: "OpenCode default provider endpoint was not recognized; no file patch will be applied." });
    }
    return {
      id: planId,
      integration: this.name,
      createdAt: nowIso(),
      scope: input.scope,
      summary: "Route OpenCode API-key mode through AgentPulse proxy.",
      preflight: state,
      proxyRoute: this.getProxyRouteProfile(),
      actions,
      risks,
      rollback: { backupId: stableId("backup", { planId }), files: endpointPath ? [{ filePath: target, backupPath: targetBackupId(planId, target) }] : [] },
      verification: { ok: false, checkedFiles: [target], expectedProxyBaseUrl: proxyUrl, warnings: ["Plan has not been applied yet."] }
    };
  },
  async planInstall(input) {
    return this.planProxyInstall(input);
  },
  async verifyInstall(plan) {
    const expected = plan.verification?.expectedProxyBaseUrl;
    const checkedFiles = plan.actions.filter((action) => action.type === "file.patch").map((action) => action.filePath);
    const warnings = [];
    let ok = checkedFiles.length > 0;
    for (const filePath of checkedFiles) {
      const config = await readJsonLikeConfig(filePath);
      const actual = config ? findOpenCodeEndpoint(config) : void 0;
      if (!expected || actual !== expected) {
        ok = false;
        warnings.push(`OpenCode proxy URL was not verified in ${filePath}`);
      }
    }
    return { ok, checkedFiles, expectedProxyBaseUrl: expected, warnings };
  },
  getProxyRouteProfile() {
    return openAiRouteProfile("opencode", "/proxy/opencode");
  }
};
async function readOpenCodeState(input) {
  const target = targetPath3({ workspaceDir: input.workspaceDir, scope: input.scope || "workspace", proxyBaseUrl: input.proxyBaseUrl || "http://127.0.0.1:8080" });
  const config = await readJsonLikeConfig(target);
  const currentBaseUrl = config ? findOpenCodeEndpoint(config) : void 0;
  const expected = normalizeProxyBaseUrl(input.proxyBaseUrl || "http://127.0.0.1:8080", "/proxy/opencode");
  const warnings = config ? [] : [`OpenCode config not found or not parseable: ${target}`];
  return {
    integration: "opencode",
    routed: currentBaseUrl === expected || Boolean(currentBaseUrl?.includes("/proxy/opencode")),
    configPath: target,
    currentBaseUrl,
    originalUpstream: currentBaseUrl?.includes("/proxy/opencode") ? void 0 : currentBaseUrl,
    confidence: config ? currentBaseUrl ? "high" : "medium" : "low",
    warnings,
    summary: config ? summarizeConfig(config) : void 0
  };
}
function targetPath3(input) {
  return input.scope === "workspace" ? join32(input.workspaceDir, "opencode.json") : join32(homedir32(), ".config", "opencode", "config.json");
}
function findOpenCodeEndpoint(config) {
  const path = findOpenCodeEndpointPath(config);
  const value = path ? getPathValue(config, path) : void 0;
  return typeof value === "string" ? value : void 0;
}
function findOpenCodeEndpointPath(config) {
  for (const field of endpointFields) {
    if (typeof config[field] === "string") return [field];
  }
  const providers = config.providers;
  const defaultProvider = typeof config.provider === "string" ? config.provider : typeof config.defaultProvider === "string" ? config.defaultProvider : void 0;
  if (providers && typeof providers === "object" && !Array.isArray(providers)) {
    const entries = Object.entries(providers);
    const candidate = defaultProvider ? [[defaultProvider, providers[defaultProvider]]] : entries.length === 1 ? entries : [];
    for (const [name, raw] of candidate) {
      if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
      for (const field of endpointFields) {
        if (typeof raw[field] === "string") return ["providers", name, field];
      }
    }
  }
  if (Array.isArray(providers)) {
    if (providers.length !== 1 && !defaultProvider) return void 0;
    const index = defaultProvider ? providers.findIndex((item) => item && typeof item === "object" && item.name === defaultProvider) : 0;
    const provider = providers[index];
    if (index >= 0 && provider && typeof provider === "object") {
      for (const field of endpointFields) {
        if (typeof provider[field] === "string") return ["providers", String(index), field];
      }
    }
  }
  return void 0;
}
var adapters = [codexAdapter, claudeCodeAdapter, openCodeAdapter];
function getAdapterByName(name) {
  return adapters.find((adapter) => adapter.name === name);
}
async function scanIntegrations(workspaceDir = process.cwd()) {
  return Promise.all(adapters.map((adapter) => adapter.detect({ workspaceDir: resolve2(workspaceDir) })));
}
async function collectIntegrationSources(workspaceDir = process.cwd()) {
  const groups = await Promise.all(adapters.map((adapter) => adapter.getInventorySources({ workspaceDir: resolve2(workspaceDir) })));
  return groups.flat();
}
async function createInstallPlans(input) {
  return Promise.all(adapters.map((adapter) => adapter.planProxyInstall({ ...input, workspaceDir: resolve2(input.workspaceDir) })));
}
async function verifyInstallPlan(plan) {
  const adapter = getAdapterByName(plan.integration);
  if (!adapter) {
    return { ok: false, checkedFiles: [], expectedProxyBaseUrl: plan.verification?.expectedProxyBaseUrl, warnings: [`Unknown integration: ${plan.integration}`] };
  }
  return adapter.verifyInstall(plan);
}

// ../../packages/inventory/dist/index.js
async function scanInventory(options = {}) {
  const workspaceDir = options.workspaceDir || process.cwd();
  const sources = await collectIntegrationSources(workspaceDir);
  const fileProbe2 = createFileProbe();
  const skillProbe = createSkillProbe();
  const configProbe = createConfigProbe();
  const mcpProbe = createMcpProbe();
  const skills = [];
  const mcpServers = [];
  const plugins = [];
  for (const source of sources) {
    if (!source.exists) continue;
    if (source.kind === "skill-dir") {
      const entries = await fileProbe2.listDir(source.path);
      for (const entry of entries.filter((item) => item.isDirectory)) {
        const probe = await skillProbe.isSkillDirectory(entry.path);
        if (!probe.isSkill) continue;
        const info = await fileProbe2.stat(entry.path);
        skills.push({
          id: stableId("skill", { source: source.path, dir: entry.path }),
          name: probe.name || entry.name,
          description: probe.description,
          integration: source.integration,
          scope: source.scope,
          directory: entry.path,
          entryFile: probe.entryFile,
          enabled: true,
          sourcePath: source.path,
          lastModifiedAt: info?.lastModifiedAt,
          status: "discovered"
        });
      }
    }
    if (source.kind === "plugin-dir") {
      const entries = await fileProbe2.listDir(source.path);
      for (const entry of entries.filter((item) => item.isDirectory)) {
        const manifestPath = await fileProbe2.exists(join4(entry.path, "package.json")) ? join4(entry.path, "package.json") : await fileProbe2.exists(join4(entry.path, "manifest.json")) ? join4(entry.path, "manifest.json") : void 0;
        const info = await fileProbe2.stat(entry.path);
        plugins.push({
          id: stableId("plugin", { source: source.path, dir: entry.path }),
          name: entry.name,
          integration: source.integration,
          scope: source.scope,
          directory: entry.path,
          manifestPath,
          enabled: true,
          sourcePath: source.path,
          lastModifiedAt: info?.lastModifiedAt,
          status: manifestPath ? "discovered" : "configured"
        });
      }
    }
    if (source.kind === "tool-config") {
      const parsed = source.path.endsWith(".toml") ? await configProbe.parseToml(source.path) : source.path.endsWith(".json") || source.path.endsWith(".jsonc") ? await configProbe.parseJsonc(source.path) : { ok: false };
      if (!parsed.ok) continue;
      for (const candidate of mcpProbe.extractMcpServers(parsed.value, source.path)) {
        mcpServers.push({
          id: stableId("mcp", { source: source.path, name: candidate.name }),
          name: candidate.name,
          integration: source.integration,
          scope: source.scope,
          transport: candidate.transport,
          command: candidate.command,
          args: candidate.args,
          url: candidate.url,
          envKeys: candidate.envKeys,
          enabled: true,
          sourcePath: source.path,
          lastModifiedAt: source.lastModifiedAt,
          riskLevel: candidate.riskLevel,
          status: candidate.command || candidate.url ? "configured" : "discovered"
        });
      }
    }
  }
  const snapshot = { sources, skills, mcpServers, plugins, scannedAt: nowIso() };
  options.storage?.upsertInventory(snapshot);
  return snapshot;
}
function diffInventory(snapshot) {
  const findings = [];
  for (const source of snapshot.sources) {
    if (!source.exists) findings.push({ type: "missing-source", message: `${source.integration} ${source.kind} missing`, sourcePath: source.path });
  }
  for (const server of snapshot.mcpServers) {
    if (!server.command && !server.url) {
      findings.push({ type: "configured-but-missing", message: `MCP server ${server.name} has no command or url`, sourcePath: server.sourcePath });
    }
  }
  return findings;
}

export {
  newId,
  stableId,
  nowIso,
  getDataDir,
  getDefaultDatabasePath,
  eventTypeToTaskStatus,
  maxRisk,
  summarizeUnknown,
  redactSecrets,
  createSecretProbe,
  getAdapterByName,
  scanIntegrations,
  createInstallPlans,
  verifyInstallPlan,
  scanInventory,
  diffInventory
};
