"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const events = require("./fetch-virtual-server-J6SUa9HS.cjs");
const REPLAY_AUTH_PATH = "/home/user/.replay/auth.json";
function ensureDir(vfs, filePath) {
  const dir = events.dirname(filePath);
  if (!vfs.existsSync(dir)) {
    vfs.mkdirSync(dir, { recursive: true });
  }
}
function readReplayAuth(vfs) {
  if (!vfs.existsSync(REPLAY_AUTH_PATH)) return null;
  try {
    const content = vfs.readFileSync(REPLAY_AUTH_PATH, "utf8");
    const parsed = JSON.parse(content);
    if (parsed && typeof parsed.accessToken === "string" && typeof parsed.refreshToken === "string") {
      return parsed;
    }
    return null;
  } catch {
    return null;
  }
}
function writeReplayAuth(vfs, config) {
  ensureDir(vfs, REPLAY_AUTH_PATH);
  vfs.writeFileSync(REPLAY_AUTH_PATH, JSON.stringify(config, null, 2));
}
function deleteReplayAuth(vfs) {
  if (!vfs.existsSync(REPLAY_AUTH_PATH)) return false;
  try {
    vfs.unlinkSync(REPLAY_AUTH_PATH);
  } catch {
  }
  return true;
}
function ok(stdout) {
  return { stdout, stderr: "", exitCode: 0 };
}
function err(stderr) {
  return { stdout: "", stderr, exitCode: 1 };
}
const MAX_CACHE = 25;
const recordingCache = [];
let nextRecordingId = 1;
const CORS_PROXY = "https://almostnode-cors-proxy.langtail.workers.dev/?url=";
const REPLAY_BASE = "https://dispatch.replay.io/nut";
const AUTH0_CLIENT_ID = "4FvFnJJW4XlnUyrXQF8zOLw6vNAH1MAo";
const AUTH0_TOKEN_URL = "https://webreplay.us.auth0.com/oauth/token";
const REPLAY_API = "https://api.replay.io";
const REPLAY_APP = "https://app.replay.io";
typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}-${Math.random().toString(36).slice(2)}`;
const CLIENT_SESSION_ID = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}-${Math.random().toString(36).slice(2)}`;
function clientHeaders() {
  return {
    "X-Client-Info": "almostnode-webide/1.0.0",
    "X-Client-Session-Id": CLIENT_SESSION_ID
  };
}
async function replayFetch(path, options = {}, authenticated = false, vfs) {
  const headers = {
    ...clientHeaders(),
    ...options.headers
  };
  if (authenticated && vfs) {
    const token = await getValidAccessToken(vfs);
    if (token) {
      headers["Authorization"] = `Bearer ${token}`;
    }
  }
  const opts = { ...options, headers };
  const url = `${REPLAY_BASE}${path}`;
  try {
    const res = await fetch(url, opts);
    if (res.ok || res.status < 500) return res;
    throw new Error(`status ${res.status}`);
  } catch {
    const proxiedUrl = `${CORS_PROXY}${encodeURIComponent(url)}`;
    return fetch(proxiedUrl, opts);
  }
}
async function fetchViaProxy(url, opts = {}) {
  const proxiedUrl = `${CORS_PROXY}${encodeURIComponent(url)}`;
  return fetch(proxiedUrl, opts);
}
async function generateAuthKey() {
  const value = String(globalThis.performance.now());
  const encoded = new TextEncoder().encode(value);
  const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
  const hashArray = new Uint8Array(hashBuffer);
  return Array.from(hashArray).map((b) => b.toString(16).padStart(2, "0")).join("");
}
function parseJwtPayload(jwt) {
  try {
    const parts = jwt.split(".");
    if (parts.length !== 3) return null;
    const payload = atob(parts[1].replace(/-/g, "+").replace(/_/g, "/"));
    return JSON.parse(payload);
  } catch {
    return null;
  }
}
function parseJwtExpiry(jwt) {
  const payload = parseJwtPayload(jwt);
  if (payload && typeof payload.exp === "number") {
    return payload.exp * 1e3;
  }
  return Date.now() + 36e5;
}
function parseJwtEmail(jwt) {
  const payload = parseJwtPayload(jwt);
  if (!payload) return null;
  if (typeof payload.email === "string") return payload.email;
  if (typeof payload.sub === "string") return payload.sub;
  return null;
}
async function pollForReplayToken(key) {
  var _a, _b;
  const mutation = `
    mutation CloseAuthRequest($key: String!) {
      closeAuthRequest(input: { key: $key }) {
        success
        token
      }
    }
  `;
  const maxAttempts = 120;
  for (let i = 0; i < maxAttempts; i++) {
    try {
      const res = await fetchViaProxy(`${REPLAY_API}/v1/graphql`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          query: mutation,
          name: "CloseAuthRequest",
          variables: { key }
        })
      });
      if (!res.ok) {
        await new Promise((resolve) => setTimeout(resolve, 2500));
        continue;
      }
      const json = await res.json();
      if (json.errors) {
        const isMissing = json.errors.length === 1 && json.errors[0].message === "Authentication request does not exist";
        if (isMissing) {
          await new Promise((resolve) => setTimeout(resolve, 2500));
          continue;
        }
        throw new Error(json.errors.map((e) => e.message).filter(Boolean).join(", "));
      }
      const token = (_b = (_a = json.data) == null ? void 0 : _a.closeAuthRequest) == null ? void 0 : _b.token;
      if (token) return token;
      await new Promise((resolve) => setTimeout(resolve, 2500));
    } catch (e) {
      if (e instanceof Error) throw e;
      throw new Error(String(e));
    }
  }
  throw new Error("Login timed out after 5 minutes. Please try again.");
}
async function exchangeRefreshToken(refreshToken) {
  const res = await fetchViaProxy(AUTH0_TOKEN_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      audience: "https://api.replay.io",
      scope: "openid profile",
      grant_type: "refresh_token",
      client_id: AUTH0_CLIENT_ID,
      refresh_token: refreshToken
    })
  });
  if (!res.ok) {
    const body = await res.text();
    throw new Error(`Auth0 token exchange failed (${res.status}): ${body}`);
  }
  const data = await res.json();
  if (data.error) throw new Error(`Auth0 error: ${data.error}`);
  if (!data.access_token || !data.refresh_token) {
    throw new Error("Auth0 returned no access or refresh token");
  }
  return { accessToken: data.access_token, refreshToken: data.refresh_token };
}
async function getValidAccessToken(vfs) {
  const auth = readReplayAuth(vfs);
  if (!auth) return null;
  if (auth.expiresAt > Date.now() + 6e4) {
    return auth.accessToken;
  }
  try {
    const { accessToken, refreshToken } = await exchangeRefreshToken(auth.refreshToken);
    const updated = {
      ...auth,
      accessToken,
      refreshToken,
      expiresAt: parseJwtExpiry(accessToken),
      userInfo: parseJwtEmail(accessToken) || auth.userInfo
    };
    writeReplayAuth(vfs, updated);
    return accessToken;
  } catch {
    return null;
  }
}
function getPreviewIframe() {
  if (typeof document === "undefined") return null;
  return document.getElementById("webidePreview");
}
async function extractRecording() {
  const iframe = getPreviewIframe();
  if (!iframe) throw new Error("No preview iframe found. Run your dev server first.");
  const win = iframe.contentWindow;
  if (!win) throw new Error("Preview iframe has no window. Wait for the page to load.");
  const messageId = `replay-capture-${Date.now()}-${Math.random().toString(36).slice(2)}`;
  const iframeSrc = iframe.src || "(unknown)";
  return new Promise((resolve, reject) => {
    const timeout = setTimeout(() => {
      window.removeEventListener("message", handler);
      reject(
        new Error(
          "Timed out waiting for recording data (10s). Make sure the preview has loaded."
        )
      );
    }, 1e4);
    function handler(event) {
      if (!event.data || event.data.source !== "@@replay-nut" || event.data.id !== messageId) return;
      window.removeEventListener("message", handler);
      clearTimeout(timeout);
      if (event.data.error) {
        reject(new Error(event.data.error));
        return;
      }
      const response = event.data.response;
      if (response instanceof ArrayBuffer) {
        resolve({ data: response, url: iframeSrc });
      } else if (response && typeof response === "object" && response.buffer instanceof ArrayBuffer) {
        resolve({ data: response.buffer, url: iframeSrc });
      } else if (typeof response === "string") {
        const encoded = new TextEncoder().encode(response);
        resolve({ data: encoded.buffer, url: iframeSrc });
      } else {
        reject(new Error("Unexpected recording data format"));
      }
    }
    window.addEventListener("message", handler);
    win.postMessage(
      {
        id: messageId,
        request: { request: "recording-data" },
        source: "@@replay-nut"
      },
      "*"
    );
  });
}
function estimateEventCount(data) {
  try {
    const text = new TextDecoder().decode(data.slice(0, Math.min(data.byteLength, 1024 * 1024)));
    const parsed = JSON.parse(text);
    if (Array.isArray(parsed)) return parsed.length;
    if (parsed && Array.isArray(parsed.events)) return parsed.events.length;
    if (parsed && Array.isArray(parsed.simulationData)) return parsed.simulationData.length;
  } catch {
  }
  return Math.max(1, Math.floor(data.byteLength / 200));
}
async function cmdCapture() {
  try {
    const { data, url } = await extractRecording();
    const id = nextRecordingId++;
    const eventCount = estimateEventCount(data);
    const recording = {
      id,
      timestamp: (/* @__PURE__ */ new Date()).toISOString(),
      url,
      eventCount,
      sizeBytes: data.byteLength,
      data
    };
    recordingCache.push(recording);
    if (recordingCache.length > MAX_CACHE) {
      recordingCache.shift();
    }
    const sizeKB = (data.byteLength / 1024).toFixed(1);
    return ok(
      `Captured recording #${id} (${eventCount} events, ${sizeKB} KB)
Use 'replayio upload ${id}' to upload to Replay for analysis.
`
    );
  } catch (e) {
    return err(`capture failed: ${e instanceof Error ? e.message : String(e)}
`);
  }
}
async function cmdLs() {
  if (recordingCache.length === 0) {
    const iframe = getPreviewIframe();
    if (iframe && iframe.contentWindow) {
      const captureResult = await cmdCapture();
      if (captureResult.exitCode !== 0) {
        return ok("(no recordings cached)\n");
      }
    } else {
      return ok("(no recordings cached)\n");
    }
  }
  const lines = ["ID  Timestamp                  Events  Size      Uploaded"];
  lines.push("──  ─────────────────────────  ──────  ────────  ────────");
  for (const rec of recordingCache) {
    const ts = rec.timestamp.replace("T", " ").slice(0, 19);
    const sizeKB = (rec.sizeBytes / 1024).toFixed(1) + " KB";
    const uploaded = rec.visitDataId ? "yes" : "no";
    lines.push(
      `${String(rec.id).padEnd(4)}${ts.padEnd(27)}${String(rec.eventCount).padEnd(8)}${sizeKB.padEnd(10)}${uploaded}`
    );
  }
  return ok(lines.join("\n") + "\n");
}
async function cmdUpload(args, vfs) {
  var _a, _b;
  const idStr = args[0];
  if (!idStr) return err("Usage: replayio upload <id>\n");
  const id = parseInt(idStr, 10);
  const recording = recordingCache.find((r) => r.id === id);
  if (!recording) return err(`Recording #${id} not found. Run 'replayio ls' to see available recordings.
`);
  if (recording.visitDataId) {
    return ok(
      `Recording #${id} already uploaded.
  Visit Data ID: ${recording.visitDataId}
` + (recording.recordingId ? `  Recording ID: ${recording.recordingId}
` : "") + `
Use 'replayio chat ${recording.visitDataId} "your question"' to ask about this recording.
`
    );
  }
  try {
    const jsonText = new TextDecoder().decode(recording.data);
    let simulationDataArray;
    try {
      const parsed = JSON.parse(jsonText);
      simulationDataArray = Array.isArray(parsed) ? parsed : parsed.simulationData ?? [parsed];
    } catch {
      return err("Recording data is not valid JSON. Try capturing again.\n");
    }
    const visitData = { simulationData: simulationDataArray };
    let compressed;
    if (typeof CompressionStream !== "undefined") {
      const jsonString = JSON.stringify(visitData);
      const blob = new Blob([jsonString]);
      const compressedStream = blob.stream().pipeThrough(
        new CompressionStream("gzip")
      );
      compressed = await new Response(compressedStream).blob().then((b) => b.arrayBuffer());
    } else {
      const pako = await import("pako");
      compressed = pako.gzip(new TextEncoder().encode(JSON.stringify(visitData))).buffer;
    }
    const isLoggedIn = readReplayAuth(vfs) !== null;
    const uploadRes = await replayFetch("/create-visit-data", {
      method: "POST",
      headers: {
        "Content-Type": "application/octet-stream",
        "Content-Length": String(compressed.byteLength)
      },
      body: compressed
    }, isLoggedIn, vfs);
    if (!uploadRes.ok) {
      const body = await uploadRes.text();
      return err(`Upload failed (${uploadRes.status}): ${body}
`);
    }
    const uploadJson = await uploadRes.json();
    if (typeof uploadJson.error === "string" && uploadJson.error.trim()) {
      return err(`Upload backend error: ${uploadJson.error.trim()}
`);
    }
    const visitDataId = uploadJson.visitDataId;
    if (!visitDataId) return err("Upload succeeded but no visitDataId returned.\n");
    recording.visitDataId = visitDataId;
    let recordingId;
    let replayUrl;
    try {
      const ensureRes = await replayFetch("/ensure-visit-recording", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ visitDataId })
      }, isLoggedIn, vfs);
      if (ensureRes.ok) {
        const ensureData = await ensureRes.json();
        const rid = ensureData.recordingId ?? ensureData.id ?? ((_a = ensureData.recording) == null ? void 0 : _a.id);
        if (rid) {
          recordingId = rid;
          replayUrl = ensureData.url ?? ((_b = ensureData.recording) == null ? void 0 : _b.url) ?? `https://app.replay.io/recording/${rid}`;
          recording.recordingId = recordingId;
          recording.replayUrl = replayUrl;
        }
      }
    } catch {
    }
    if (recordingId) {
      return ok(
        `Recording uploaded successfully!
  Recording ID: ${recordingId}
  URL: ${replayUrl}

Use 'replayio chat ${recordingId} "your question"' to ask about this recording.
`
      );
    }
    const loginHint = !isLoggedIn ? `
Tip: Run 'replayio login' to get a full recording URL on upload.
` : "";
    return ok(
      `Recording uploaded successfully!
  Visit Data ID: ${visitDataId}

Use 'replayio chat ${visitDataId} "your question"' to ask about this recording.
` + loginHint
    );
  } catch (e) {
    return err(`upload failed: ${e instanceof Error ? e.message : String(e)}
`);
  }
}
async function resolveRecordingId(id, vfs) {
  var _a;
  const cachedByRecId = recordingCache.find((r) => r.recordingId === id);
  if (cachedByRecId) return id;
  const cachedByVisit = recordingCache.find((r) => r.visitDataId === id);
  if (cachedByVisit == null ? void 0 : cachedByVisit.recordingId) return cachedByVisit.recordingId;
  const visitDataId = cachedByVisit ? id : null;
  if (visitDataId) {
    for (let attempt = 0; attempt < 6; attempt++) {
      if (attempt > 0) {
        await new Promise((resolve) => setTimeout(resolve, 2500));
      }
      try {
        const ensureRes = await replayFetch("/ensure-visit-recording", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ visitDataId })
        }, true, vfs);
        if (ensureRes.ok) {
          const data = await ensureRes.json();
          const rid = data.recordingId ?? data.id ?? ((_a = data.recording) == null ? void 0 : _a.id);
          if (rid) {
            if (cachedByVisit) {
              cachedByVisit.recordingId = rid;
              cachedByVisit.replayUrl = `https://app.replay.io/recording/${rid}`;
            }
            return rid;
          }
        }
      } catch {
      }
    }
    return null;
  }
  return id;
}
async function cmdChat(args, vfs) {
  const id = args[0];
  const message = args.slice(1).join(" ");
  if (!id || !message) {
    return err('Usage: replayio chat <recordingId|visitDataId> "message"\n');
  }
  try {
    const recordingId = await resolveRecordingId(id, vfs);
    if (!recordingId) {
      return err(
        `Could not resolve a recording ID from "${id}".
The recording may still be processing. Wait a moment and try again.
`
      );
    }
    const res = await replayFetch(`/recording/${recordingId}/chat`, {
      method: "POST",
      headers: { "Content-Type": "application/x-ndjson" },
      body: JSON.stringify({ role: "user", content: message })
    });
    if (!res.ok) {
      const body = await res.text();
      return err(`Chat failed (${res.status}): ${body}
`);
    }
    const text = await res.text();
    const lines = text.split("\n").filter((line) => line.trim()).map((line) => {
      try {
        const parsed = JSON.parse(line);
        return parsed.text || parsed.content || parsed.message || JSON.stringify(parsed);
      } catch {
        return line;
      }
    });
    return ok(lines.join("") + "\n");
  } catch (e) {
    return err(`chat failed: ${e instanceof Error ? e.message : String(e)}
`);
  }
}
async function cmdAnalyze(args, vfs) {
  const id = args[0];
  const goal = args.slice(1).join(" ") || "Analyze this recording for issues and provide a summary.";
  if (!id) {
    return err("Usage: replayio analyze <recordingId|visitDataId> [goal]\n");
  }
  try {
    const recordingId = await resolveRecordingId(id, vfs);
    if (!recordingId) {
      return err(
        `Could not resolve a recording ID from "${id}".
The recording may still be processing. Wait a moment and try again.
`
      );
    }
    const res = await replayFetch(`/recording/${recordingId}/analyze`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ goal })
    }, true, vfs);
    if (!res.ok) {
      const body = await res.text();
      return err(`Analysis failed (${res.status}): ${body}
`);
    }
    const data = await res.json();
    if (typeof data.error === "string" && data.error.trim()) {
      return err(`Analysis error: ${data.error.trim()}
`);
    }
    const explanation = data.explanation || "No explanation returned.";
    return ok(explanation + "\n");
  } catch (e) {
    return err(`analyze failed: ${e instanceof Error ? e.message : String(e)}
`);
  }
}
async function cmdLogin(vfs, keychain) {
  const existing = readReplayAuth(vfs);
  if (existing) {
    const info = existing.userInfo || "(unknown user)";
    return ok(`Already logged in as ${info}. Run 'replayio logout' first to switch accounts.
`);
  }
  if (typeof window === "undefined") {
    return err("Login requires a browser environment.\n");
  }
  try {
    const key = await generateAuthKey();
    const authUrl = `${REPLAY_APP}/api/browser/auth?key=${key}&source=cli`;
    const authWindow = window.open(authUrl, "_blank");
    if (!authWindow) {
      return err(
        `Could not open auth page. Please visit this URL manually:
  ${authUrl}
`
      );
    }
    const refreshToken = await pollForReplayToken(key);
    const exchanged = await exchangeRefreshToken(refreshToken);
    const expiresAt = parseJwtExpiry(exchanged.accessToken);
    const userInfo = parseJwtEmail(exchanged.accessToken);
    const config = {
      accessToken: exchanged.accessToken,
      refreshToken: exchanged.refreshToken,
      expiresAt,
      userInfo: userInfo || null
    };
    writeReplayAuth(vfs, config);
    if (keychain) {
      await keychain.persistCurrentState();
    }
    const displayUser = userInfo || "(authenticated)";
    return ok(`Logged in as ${displayUser}
`);
  } catch (e) {
    return err(`login failed: ${e instanceof Error ? e.message : String(e)}
`);
  }
}
async function cmdLogout(vfs, keychain) {
  const deleted = deleteReplayAuth(vfs);
  if (!deleted) {
    return ok("Not logged in.\n");
  }
  if (keychain) {
    await keychain.persistCurrentState();
  }
  return ok("Logged out of Replay.\n");
}
function cmdWhoami(vfs) {
  const auth = readReplayAuth(vfs);
  if (!auth) {
    return ok("Not logged in. Run 'replayio login' to authenticate.\n");
  }
  const user = auth.userInfo || "(unknown user)";
  const expired = auth.expiresAt < Date.now();
  const expiryStr = new Date(auth.expiresAt).toISOString().replace("T", " ").slice(0, 19);
  if (expired) {
    return ok(`Logged in as ${user} (token expired at ${expiryStr}, will auto-refresh on next use)
`);
  }
  return ok(`Logged in as ${user} (token expires ${expiryStr})
`);
}
function cmdHelp() {
  return ok(
    `replayio — capture and analyze app recordings with Replay

Commands:
  login                        Authenticate with Replay via browser OAuth
  logout                       Clear saved Replay credentials
  whoami                       Show current auth status
  capture                      Extract recording from preview iframe
  ls                           List cached recordings (auto-captures if empty)
  upload <id>                  Upload recording to Replay for analysis
  chat <visitDataId> "msg"     Chat with Replay AI about a recording
  analyze <visitDataId> [goal] Full analysis of a recording
  help                         Show this help message

Workflow:
  1. replayio login                — authenticate with Replay
  2. replayio capture              — grab recording from live preview
  3. replayio upload 1             — upload to Replay platform
  4. replayio chat <id> "why?"     — ask questions about the recording
  5. replayio analyze <id>         — get full root-cause analysis

The recording captures rrweb DOM snapshots, user interactions, network
activity, and errors — giving Replay AI full context for debugging.
`
  );
}
async function runReplayioCommand(args, _ctx, vfs, keychain) {
  const subcommand = args[0];
  if (!subcommand || subcommand === "help" || subcommand === "--help") {
    return cmdHelp();
  }
  switch (subcommand) {
    case "login":
      return cmdLogin(vfs, keychain);
    case "logout":
      return cmdLogout(vfs, keychain);
    case "whoami":
      return cmdWhoami(vfs);
    case "capture":
      return cmdCapture();
    case "ls":
    case "list":
      return cmdLs();
    case "upload":
      return cmdUpload(args.slice(1), vfs);
    case "chat":
      return cmdChat(args.slice(1), vfs);
    case "analyze":
    case "analysis":
      return cmdAnalyze(args.slice(1), vfs);
    default:
      return err(`unknown command: ${subcommand}. Run 'replayio help' for usage.
`);
  }
}
exports.runReplayioCommand = runReplayioCommand;
//# sourceMappingURL=replayio-command-CITzyPGt.cjs.map
