// src/trace/raw-provider-sink.ts
import { promises as fs } from "fs";
import * as path from "path";
var REDACTED_HEADER_NAMES = /* @__PURE__ */ new Set([
  "authorization",
  "x-api-key",
  "x-auth-token",
  "cookie",
  "set-cookie",
  "proxy-authorization"
]);
var REDACTED_BODY_KEY = /^(api[_-]?key|bearer|password|secret|token|access[_-]?token|refresh[_-]?token)$/i;
function defaultProviderRedactor(event) {
  const redactedFields = [...event.redactedFields ?? []];
  const requestHeaders = redactHeaders(event.requestHeaders, "request", redactedFields);
  const responseHeaders = redactHeaders(event.responseHeaders, "response", redactedFields);
  const requestBody = redactBody(event.requestBody, "requestBody", redactedFields);
  const responseBody = redactBody(event.responseBody, "responseBody", redactedFields);
  return { ...event, requestHeaders, responseHeaders, requestBody, responseBody, redactedFields };
}
function redactHeaders(headers, prefix, redactedFields) {
  if (!headers) return headers;
  const out = {};
  for (const [k, v] of Object.entries(headers)) {
    if (REDACTED_HEADER_NAMES.has(k.toLowerCase())) {
      redactedFields.push(`${prefix}Headers.${k}`);
      continue;
    }
    out[k] = v;
  }
  return out;
}
function redactBody(value, pathStr, redactedFields) {
  if (value == null) return value;
  if (Array.isArray(value))
    return value.map((v, i) => redactBody(v, `${pathStr}[${i}]`, redactedFields));
  if (typeof value === "object") {
    const out = {};
    for (const [k, v] of Object.entries(value)) {
      if (REDACTED_BODY_KEY.test(k)) {
        redactedFields.push(`${pathStr}.${k}`);
        continue;
      }
      out[k] = redactBody(v, `${pathStr}.${k}`, redactedFields);
    }
    return out;
  }
  return value;
}
var InMemoryRawProviderSink = class {
  events = [];
  redactor;
  constructor(opts = {}) {
    this.redactor = opts.redactor ?? defaultProviderRedactor;
  }
  async record(event) {
    this.events.push(this.redactor({ ...event, redactedFields: event.redactedFields ?? [] }));
  }
  async list(filter = {}) {
    return this.events.filter(
      (e) => (filter.runId === void 0 || e.runId === filter.runId) && (filter.spanId === void 0 || e.spanId === filter.spanId) && (filter.direction === void 0 || e.direction === filter.direction) && (filter.attemptIndex === void 0 || e.attemptIndex === filter.attemptIndex)
    );
  }
  size() {
    return this.events.length;
  }
};
var NoopRawProviderSink = class {
  async record() {
  }
  /**
   * Returns an empty array. Implemented so `assertRunCaptured` does not
   * trip the `no_raw_sink` issue when a caller explicitly opts out of
   * capture by passing this sink — opt-out is a deliberate choice, not a
   * misconfiguration.
   */
  async list() {
    return [];
  }
};
var FileSystemRawProviderSink = class {
  dir;
  fileName;
  rollAtBytes;
  redactor;
  bytesWritten = 0;
  rollIndex = 0;
  initPromise = null;
  constructor(opts) {
    this.dir = opts.dir;
    this.fileName = opts.fileName ?? "raw-provider-events.ndjson";
    this.rollAtBytes = opts.rollAtBytes ?? 32 * 1024 * 1024;
    this.redactor = opts.redactor ?? defaultProviderRedactor;
  }
  async ensureInit() {
    if (!this.initPromise) {
      this.initPromise = fs.mkdir(this.dir, { recursive: true }).then(() => void 0);
    }
    await this.initPromise;
  }
  currentPath() {
    if (this.rollIndex === 0) return path.join(this.dir, this.fileName);
    return path.join(this.dir, `${this.fileName}.${this.rollIndex}`);
  }
  async record(event) {
    await this.ensureInit();
    const redacted = this.redactor({ ...event, redactedFields: event.redactedFields ?? [] });
    const line = `${JSON.stringify(redacted)}
`;
    if (this.bytesWritten + line.length > this.rollAtBytes && this.bytesWritten > 0) {
      this.rollIndex += 1;
      this.bytesWritten = 0;
    }
    await fs.appendFile(this.currentPath(), line, "utf8");
    this.bytesWritten += line.length;
  }
  async list(filter = {}) {
    await this.ensureInit();
    const out = [];
    for (let i = 0; i <= this.rollIndex; i++) {
      const file = i === 0 ? path.join(this.dir, this.fileName) : path.join(this.dir, `${this.fileName}.${i}`);
      let body;
      try {
        body = await fs.readFile(file, "utf8");
      } catch (err) {
        if (err.code === "ENOENT") continue;
        throw err;
      }
      for (const line of body.split("\n")) {
        if (!line) continue;
        const event = JSON.parse(line);
        if (filter.runId !== void 0 && event.runId !== filter.runId) continue;
        if (filter.spanId !== void 0 && event.spanId !== filter.spanId) continue;
        if (filter.direction !== void 0 && event.direction !== filter.direction) continue;
        if (filter.attemptIndex !== void 0 && event.attemptIndex !== filter.attemptIndex)
          continue;
        out.push(event);
      }
    }
    return out;
  }
};
function providerFromBaseUrl(baseUrl) {
  const lower = baseUrl.toLowerCase();
  if (lower.includes("api.openai.com")) return "openai";
  if (lower.includes("api.anthropic.com")) return "anthropic";
  if (lower.includes("generativelanguage.googleapis.com")) return "google";
  if (lower.includes("api.together.ai") || lower.includes("api.together.xyz")) return "together";
  if (lower.includes("api.deepseek.com")) return "deepseek";
  if (lower.includes("router.tangle.tools")) return "tangle-router";
  if (lower.includes("api.litellm") || lower.includes("litellm")) return "litellm";
  try {
    return new URL(baseUrl).host;
  } catch {
    return baseUrl;
  }
}

export {
  defaultProviderRedactor,
  InMemoryRawProviderSink,
  NoopRawProviderSink,
  FileSystemRawProviderSink,
  providerFromBaseUrl
};
//# sourceMappingURL=chunk-PC4UYEBM.js.map