import { v as validateAuth } from './auth-DeCdZ83n.js';
import * as crypto from 'crypto';
import * as fs from 'fs';
import * as path from 'path';
import Database from 'better-sqlite3';
import { j as json } from './shared-server-sSGG17Df.js';

const SKIP_EVENT_TYPES = /* @__PURE__ */ new Set([
  "config",
  "heartbeat",
  "metadata",
  "result",
  "summary",
  "system"
]);
function parseAssistantBlock(block) {
  if (!block || typeof block !== "object") {
    return null;
  }
  switch (block.type) {
    case "text":
      return { content: block.text || "", type: "text" };
    case "thinking":
      return { content: block.thinking || "", type: "thinking" };
    case "tool_use":
      return {
        id: block.id || "",
        input: block.input || {},
        toolName: block.name || "Unknown",
        type: "tool_use"
      };
    default:
      return null;
  }
}
function parseJsonlLine(line, assistantTurns, messageIndex) {
  const messages = [];
  const assistantCompleted = [];
  const entry = JSON.parse(line);
  const entryType = entry.type;
  if (SKIP_EVENT_TYPES.has(entryType)) {
    return { assistantCompleted, messages };
  }
  if (entryType === "user") {
    const msg = entry.message;
    if (!msg?.content) {
      return { assistantCompleted, messages };
    }
    const parts = [];
    const content = Array.isArray(msg.content) ? msg.content : [msg.content];
    for (const block of content) {
      if (typeof block === "string") {
        parts.push({ content: block, type: "text" });
      } else if (block.type === "text") {
        parts.push({ content: block.text || "", type: "text" });
      } else if (block.type === "tool_result") {
        let output = "";
        if (typeof block.content === "string") {
          output = block.content;
        } else if (Array.isArray(block.content)) {
          output = block.content.filter((c) => c.type === "text").map((c) => c.text).join("\n");
        }
        if (entry.toolUseResult?.content) {
          const trc = entry.toolUseResult.content;
          if (typeof trc === "string") {
            output = trc;
          } else if (Array.isArray(trc)) {
            output = trc.filter((c) => c.type === "text").map((c) => c.text).join("\n");
          }
        }
        parts.push({
          isError: block.is_error || false,
          output: output.slice(0, 2e3),
          toolUseId: block.tool_use_id || "",
          type: "tool_result"
        });
      }
    }
    if (parts.length > 0 && parts.some((p) => p.type === "text")) {
      messages.push({
        id: entry.uuid || `user-${messageIndex}`,
        parts: parts.filter((p) => p.type === "text"),
        role: "user",
        timestamp: entry.timestamp || ""
      });
    }
    const toolResults = parts.filter((p) => p.type === "tool_result");
    if (toolResults.length > 0) {
      messages.push({
        id: `tool-result-${entry.uuid || messageIndex}`,
        parts: toolResults,
        role: "system",
        timestamp: entry.timestamp || ""
      });
    }
  } else if (entryType === "assistant") {
    const msg = entry.message;
    if (!msg?.content) {
      return { assistantCompleted, messages };
    }
    const content = Array.isArray(msg.content) ? msg.content : [msg.content];
    const msgId = msg.id || entry.uuid;
    for (const block of content) {
      const part = parseAssistantBlock(block);
      if (!part) {
        continue;
      }
      if (!assistantTurns.has(msgId)) {
        assistantTurns.set(msgId, { parts: [], timestamp: entry.timestamp || "" });
      }
      assistantTurns.get(msgId).parts.push(part);
    }
    if (msg.stop_reason) {
      const turn = assistantTurns.get(msgId);
      if (turn && turn.parts.length > 0) {
        assistantCompleted.push({
          id: msgId,
          parts: turn.parts,
          role: "assistant",
          timestamp: turn.timestamp
        });
        assistantTurns.delete(msgId);
      }
    }
  }
  return { assistantCompleted, messages };
}
function parseJsonlText(text, assistantTurns, startIndex) {
  const lines = text.split("\n").filter((line) => line.trim());
  const allMessages = [];
  let idx = startIndex;
  for (const line of lines) {
    try {
      const { assistantCompleted, messages } = parseJsonlLine(line, assistantTurns, idx);
      allMessages.push(...messages, ...assistantCompleted);
      idx++;
    } catch {
    }
  }
  return allMessages;
}
function shortHash$1(input) {
  return crypto.createHash("sha256").update(input).digest("hex").slice(0, 8);
}
const CLAUDE_PROJECTS_DIR = path.join(process.env.HOME || "", ".claude", "projects");
function getSessionConversation(sessionId, offset = 0, limit = 100, projectId) {
  const projectDir = projectId ? path.join(CLAUDE_PROJECTS_DIR, projectId) : getProjectDir();
  const jsonlPath = path.join(projectDir, `${sessionId}.jsonl`);
  if (!fs.existsSync(jsonlPath)) {
    return [];
  }
  try {
    const raw = fs.readFileSync(jsonlPath, "utf-8");
    const assistantTurns = /* @__PURE__ */ new Map();
    const messages = parseJsonlText(raw, assistantTurns, 0);
    for (const [msgId, turn] of assistantTurns) {
      if (turn.parts.length > 0) {
        messages.push({
          id: msgId,
          parts: turn.parts,
          role: "assistant",
          timestamp: turn.timestamp
        });
      }
    }
    return messages.slice(offset, offset + limit);
  } catch (error) {
    console.error("[sessions] Failed to parse session JSONL:", error);
    return [];
  }
}
function listProjectsWithSessions() {
  if (!fs.existsSync(CLAUDE_PROJECTS_DIR)) {
    return [];
  }
  let projectDirs;
  try {
    projectDirs = fs.readdirSync(CLAUDE_PROJECTS_DIR).filter((d) => {
      const fullPath = path.join(CLAUDE_PROJECTS_DIR, d);
      try {
        return fs.statSync(fullPath).isDirectory();
      } catch {
        return false;
      }
    });
  } catch {
    return [];
  }
  const projects = [];
  for (const dir of projectDirs) {
    const projectDir = path.join(CLAUDE_PROJECTS_DIR, dir);
    const sessions = listSessionsForProject(projectDir);
    if (sessions.length === 0) {
      continue;
    }
    sessions.sort((a, b) => new Date(b.modified).getTime() - new Date(a.modified).getTime());
    const realPath = sessions.find((s) => s.projectPath && !s.projectPath.includes(".claude/projects"))?.projectPath || readCwdFromProjectDir(projectDir, sessions) || (dir.startsWith("-") ? dir.replace(/-/g, "/") : `/${dir.replace(/-/g, "/")}`);
    const pathSegments = realPath.split("/").filter(Boolean);
    const projectName = pathSegments.slice(-2).join("/");
    projects.push({
      fullPath: realPath,
      id: shortHash$1(realPath),
      lastModified: sessions[0]?.modified || "",
      name: projectName,
      sessionCount: sessions.length,
      sessions
    });
  }
  return projects.sort(
    (a, b) => new Date(b.lastModified).getTime() - new Date(a.lastModified).getTime()
  );
}
function cleanTitle(prompt) {
  let cleaned = prompt.replace(/<command-message>.*?<\/command-message>/gs, "").replace(/<command-name>.*?<\/command-name>/gs, "").replace(/<command-args>.*?<\/command-args>/gs, "").replace(/<local-command-caveat>.*?<\/local-command-caveat>/gs, "").replace(/<local-command-stdout>.*?<\/local-command-stdout>/gs, "").replace(/<system-reminder>.*?<\/system-reminder>/gs, "").replace(/<task-notification>.*?<\/task-notification>/gs, "");
  let prev = "";
  while (prev !== cleaned) {
    prev = cleaned;
    cleaned = cleaned.replace(/<[^>]*>/g, "").replace(/</g, "");
  }
  cleaned = cleaned.trim();
  if (!cleaned) {
    return "Untitled Session";
  }
  const firstLine = cleaned.split("\n")[0].trim();
  if (!firstLine) {
    return "Untitled Session";
  }
  if (firstLine.length > 80) {
    return `${firstLine.slice(0, 77)}...`;
  }
  return firstLine;
}
function getProjectDir() {
  const projectPath = process.cwd();
  const encoded = projectPath.replace(/\//g, "-");
  return path.join(CLAUDE_PROJECTS_DIR, encoded);
}
function listSessionsForProject(projectDir) {
  const indexPath = path.join(projectDir, "sessions-index.json");
  if (!fs.existsSync(indexPath)) {
    return [];
  }
  try {
    const raw = fs.readFileSync(indexPath, "utf-8");
    const index = JSON.parse(raw);
    const indexedIds = /* @__PURE__ */ new Set();
    const sessions = index.entries.filter((e) => !e.isSidechain).filter((e) => {
      const jsonlFile = path.join(projectDir, `${e.sessionId}.jsonl`);
      return fs.existsSync(jsonlFile);
    }).map((entry) => {
      indexedIds.add(entry.sessionId);
      return {
        created: entry.created || "",
        gitBranch: entry.gitBranch || "",
        id: entry.sessionId,
        messageCount: entry.messageCount || 0,
        modified: entry.modified || "",
        projectPath: entry.projectPath || "",
        source: "claude-code",
        summary: entry.summary || "",
        title: cleanTitle(entry.firstPrompt || entry.summary || "Untitled Session")
      };
    });
    try {
      const files = fs.readdirSync(projectDir);
      for (const file of files) {
        if (!file.endsWith(".jsonl")) {
          continue;
        }
        const sessionId = file.replace(".jsonl", "");
        if (indexedIds.has(sessionId)) {
          continue;
        }
        const filePath = path.join(projectDir, file);
        const stat = fs.statSync(filePath);
        let firstPrompt = "Active Session";
        let messageCount = 0;
        try {
          const content = fs.readFileSync(filePath, "utf-8");
          const lines = content.split("\n");
          for (const line of lines) {
            if (!line.trim()) {
              continue;
            }
            try {
              const entry = JSON.parse(line);
              if (entry.type === "user" || entry.type === "assistant") {
                messageCount++;
              }
            } catch {
            }
          }
          for (const line of lines) {
            if (!line.trim()) {
              continue;
            }
            try {
              const entry = JSON.parse(line);
              if (entry.type !== "user") {
                continue;
              }
              const msg = entry.message;
              if (!msg?.content) {
                continue;
              }
              let text = "";
              if (typeof msg.content === "string") {
                text = msg.content.trim();
              } else if (Array.isArray(msg.content)) {
                for (const b of msg.content) {
                  if (typeof b === "string" && b.trim()) {
                    text = b.trim();
                    break;
                  }
                  if (typeof b === "object" && b !== null && b.type === "text" && b.text?.trim()) {
                    text = b.text.trim();
                    break;
                  }
                }
              }
              if (!text) {
                continue;
              }
              if (text.startsWith("<local-command") || text.startsWith("<command-name>") || text.startsWith("<command-message>") || text.startsWith("<command-args>") || text.startsWith("<local-command-stdout>") || text.startsWith("<local-command-caveat>") || text.startsWith("<system-reminder>") || text.startsWith("<task-notification>")) {
                continue;
              }
              firstPrompt = text;
              break;
            } catch {
              continue;
            }
          }
        } catch {
        }
        sessions.push({
          created: stat.birthtime.toISOString(),
          gitBranch: "",
          id: sessionId,
          messageCount,
          modified: stat.mtime.toISOString(),
          projectPath: "",
          source: "claude-code",
          summary: "",
          title: cleanTitle(firstPrompt)
        });
      }
    } catch {
    }
    return sessions.sort((a, b) => new Date(b.modified).getTime() - new Date(a.modified).getTime());
  } catch (error) {
    console.error("[sessions] Failed to read sessions index:", error);
    return [];
  }
}
function readCwdFromProjectDir(projectDir, sessions) {
  for (const session of sessions) {
    try {
      const jsonlPath = path.join(projectDir, `${session.id}.jsonl`);
      if (!fs.existsSync(jsonlPath)) {
        continue;
      }
      const content = fs.readFileSync(jsonlPath, "utf-8");
      for (const line of content.split("\n")) {
        if (!line.trim()) {
          continue;
        }
        try {
          const entry = JSON.parse(line);
          if (typeof entry.cwd === "string" && entry.cwd) {
            return entry.cwd;
          }
        } catch {
        }
      }
    } catch {
    }
  }
  return "";
}
function shortHash(input) {
  return crypto.createHash("sha256").update(input).digest("hex").slice(0, 8);
}
const OPENCODE_DB_PATH = path.join(
  process.env.HOME || "",
  ".local",
  "share",
  "opencode",
  "opencode.db"
);
function getOpenCodeConversation(sessionId, offset = 0, limit = 100) {
  const db = getDb();
  if (!db) {
    return [];
  }
  try {
    const messages = db.prepare(
      `
      SELECT id, session_id, time_created, data
      FROM message
      WHERE session_id = ?
      ORDER BY time_created ASC
      LIMIT ? OFFSET ?
    `
    ).all(sessionId, limit, offset);
    const messageIds = messages.map((m) => m.id);
    if (messageIds.length === 0) {
      return [];
    }
    const placeholders = messageIds.map(() => "?").join(",");
    const parts = db.prepare(
      `
      SELECT id, message_id, time_created, data
      FROM part
      WHERE message_id IN (${placeholders})
      ORDER BY time_created ASC
    `
    ).all(...messageIds);
    const partsByMessage = /* @__PURE__ */ new Map();
    for (const part of parts) {
      if (!partsByMessage.has(part.message_id)) {
        partsByMessage.set(part.message_id, []);
      }
      const bucket = partsByMessage.get(part.message_id);
      if (bucket) {
        bucket.push(part);
      }
    }
    const result = [];
    for (const msg of messages) {
      let msgData = {};
      try {
        msgData = JSON.parse(msg.data);
      } catch {
      }
      const role = msgData.role === "user" ? "user" : "assistant";
      const msgParts = partsByMessage.get(msg.id) || [];
      const convertedParts = [];
      for (const part of msgParts) {
        let partData = {};
        try {
          partData = JSON.parse(part.data);
        } catch {
          continue;
        }
        const converted = convertOpenCodePart(partData);
        if (converted) {
          convertedParts.push(converted);
        }
      }
      if (convertedParts.length > 0) {
        result.push({
          id: msg.id,
          parts: convertedParts,
          role,
          timestamp: new Date(msg.time_created).toISOString()
        });
      }
    }
    return result;
  } catch (error) {
    console.error("[opencode] Failed to read conversation:", error);
    return [];
  } finally {
    db.close();
  }
}
function listOpenCodeProjects() {
  const db = getDb();
  if (!db) {
    return [];
  }
  try {
    const sessions = db.prepare(
      `
      SELECT id, directory, title, slug, time_created, time_updated, parent_id
      FROM session
      WHERE time_archived IS NULL OR time_archived = 0
      ORDER BY time_updated DESC
    `
    ).all();
    const msgCounts = db.prepare(`SELECT session_id, COUNT(*) as count FROM message GROUP BY session_id`).all();
    const msgCountMap = new Map(msgCounts.map((r) => [r.session_id, r.count]));
    const projectMap = /* @__PURE__ */ new Map();
    for (const row of sessions) {
      const dir = row.directory || "unknown";
      if (!projectMap.has(dir)) {
        projectMap.set(dir, []);
      }
      const sessionInfo = {
        created: new Date(row.time_created).toISOString(),
        gitBranch: "",
        id: row.id,
        messageCount: msgCountMap.get(row.id) || 0,
        modified: new Date(row.time_updated).toISOString(),
        projectPath: dir,
        source: "opencode",
        summary: "",
        title: row.title || row.slug || "Untitled Session"
      };
      const bucket = projectMap.get(dir);
      if (bucket) {
        bucket.push(sessionInfo);
      }
    }
    const projects = [];
    for (const [dir, dirSessions] of projectMap) {
      const pathSegments = dir.split("/").filter(Boolean);
      const projectName = pathSegments.slice(-2).join("/");
      dirSessions.sort((a, b) => new Date(b.modified).getTime() - new Date(a.modified).getTime());
      projects.push({
        fullPath: dir,
        id: shortHash(dir),
        lastModified: dirSessions[0]?.modified || "",
        name: `${projectName} (OpenCode)`,
        sessionCount: dirSessions.length,
        sessions: dirSessions
      });
    }
    return projects.sort(
      (a, b) => new Date(b.lastModified).getTime() - new Date(a.lastModified).getTime()
    );
  } catch (error) {
    console.error("[opencode] Failed to read sessions:", error);
    return [];
  } finally {
    db.close();
  }
}
function convertOpenCodePart(data) {
  const type = data.type;
  switch (type) {
    case "reasoning":
      return {
        content: data.text || "",
        type: "thinking"
      };
    case "text":
      return {
        content: data.text || "",
        type: "text"
      };
    case "tool": {
      const state = data.state;
      return {
        id: data.callID || data.id || "",
        input: state?.input || {},
        toolName: data.tool || "Unknown",
        type: "tool_use"
      };
    }
    default:
      return null;
  }
}
function getDb() {
  if (!fs.existsSync(OPENCODE_DB_PATH)) {
    return null;
  }
  try {
    return new Database(OPENCODE_DB_PATH, { readonly: true });
  } catch {
    return null;
  }
}
let cachedProjects = null;
let cacheTimestamp = 0;
const CACHE_TTL_MS = 3e4;
function getMergedProjects() {
  const now = Date.now();
  if (cachedProjects && now - cacheTimestamp < CACHE_TTL_MS) {
    return cachedProjects;
  }
  const claudeProjects = listProjectsWithSessions();
  const openCodeProjects = listOpenCodeProjects();
  const projectsByPath = /* @__PURE__ */ new Map();
  for (const p of claudeProjects) {
    projectsByPath.set(p.fullPath, { ...p });
  }
  for (const op of openCodeProjects) {
    const existing = projectsByPath.get(op.fullPath);
    if (existing) {
      existing.sessions.push(...op.sessions);
      existing.sessions.sort(
        (a, b) => new Date(b.modified).getTime() - new Date(a.modified).getTime()
      );
      existing.sessionCount = existing.sessions.length;
      existing.lastModified = existing.sessions[0]?.modified || existing.lastModified;
      existing.name = existing.name.replace(" (OpenCode)", "");
    } else {
      op.name = op.name.replace(" (OpenCode)", "");
      projectsByPath.set(op.fullPath, { ...op });
    }
  }
  cachedProjects = [...projectsByPath.values()].sort(
    (a, b) => new Date(b.lastModified).getTime() - new Date(a.lastModified).getTime()
  );
  cacheTimestamp = now;
  return cachedProjects;
}
const GET = ({ request, url }) => {
  const authError = validateAuth(request);
  if (authError) {
    return authError;
  }
  const sessionId = url.searchParams.get("id");
  if (sessionId) {
    const projectId = url.searchParams.get("project") || "";
    const offset2 = parseInt(url.searchParams.get("offset") || "0");
    const limit2 = parseInt(url.searchParams.get("limit") || "100");
    const allProjects2 = getMergedProjects();
    const matchedProject = projectId ? allProjects2.find((p) => p.id === projectId) : void 0;
    const claudeProjectDir = matchedProject ? matchedProject.fullPath.replace(/\//g, "-") : void 0;
    let messages = getSessionConversation(sessionId, offset2, limit2, claudeProjectDir);
    if (messages.length === 0) {
      messages = getOpenCodeConversation(sessionId, offset2, limit2);
    }
    let sessionInfo = null;
    for (const project of allProjects2) {
      if (projectId && project.id !== projectId) {
        continue;
      }
      const found = project.sessions.find((s) => s.id === sessionId);
      if (found) {
        sessionInfo = found;
        break;
      }
    }
    if (!sessionInfo) {
      return json({ error: "Session not found" }, { status: 404 });
    }
    return json({
      messages,
      session: sessionInfo,
      timestamp: (/* @__PURE__ */ new Date()).toISOString()
    });
  }
  const allProjects = getMergedProjects();
  const limit = parseInt(url.searchParams.get("limit") || "0");
  const offset = parseInt(url.searchParams.get("offset") || "0");
  const total = allProjects.length;
  const paginatedProjects = limit > 0 ? allProjects.slice(offset, offset + limit) : allProjects;
  return json({
    count: allProjects.reduce((sum, p) => sum + p.sessionCount, 0),
    projects: paginatedProjects,
    timestamp: (/* @__PURE__ */ new Date()).toISOString(),
    total
  });
};

export { GET };
//# sourceMappingURL=_server.ts-DpEVfp8W.js.map
