"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 __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
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
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

// src/riddle-client.ts
var riddle_client_exports = {};
__export(riddle_client_exports, {
  DEFAULT_RIDDLE_API_BASE_URL: () => DEFAULT_RIDDLE_API_BASE_URL,
  DEFAULT_RIDDLE_API_KEY_FILE: () => DEFAULT_RIDDLE_API_KEY_FILE,
  RIDDLE_UNSUBMITTED_WAKE_HINT: () => RIDDLE_UNSUBMITTED_WAKE_HINT,
  RiddleApiError: () => RiddleApiError,
  collectRiddlePreviewDeployWarnings: () => collectRiddlePreviewDeployWarnings,
  createRiddleApiClient: () => createRiddleApiClient,
  deployRiddlePreview: () => deployRiddlePreview,
  deployRiddleStaticPreview: () => deployRiddleStaticPreview,
  getRiddleBalance: () => getRiddleBalance,
  isTerminalRiddleJobStatus: () => isTerminalRiddleJobStatus,
  parseRiddleViewport: () => parseRiddleViewport,
  pollRiddleJob: () => pollRiddleJob,
  resolveRiddleApiKey: () => resolveRiddleApiKey,
  resolveRiddleApiKeySource: () => resolveRiddleApiKeySource,
  riddleRequestJson: () => riddleRequestJson,
  runRiddleScript: () => runRiddleScript,
  runRiddleServerPreview: () => runRiddleServerPreview
});
module.exports = __toCommonJS(riddle_client_exports);
var import_node_child_process = require("child_process");
var import_node_fs = require("fs");
var import_node_os = require("os");
var import_node_path = __toESM(require("path"), 1);
var DEFAULT_RIDDLE_API_BASE_URL = "https://api.riddledc.com";
var DEFAULT_RIDDLE_API_KEY_FILE = "/tmp/riddle-api-key";
var RIDDLE_UNSUBMITTED_WAKE_HINT = "hosted worker may still be waking or waiting for a lease";
var RiddleApiError = class extends Error {
  constructor(pathname, status, body) {
    super(`Riddle API ${pathname} failed HTTP ${status}: ${body}`);
    this.name = "RiddleApiError";
    this.status = status;
    this.body = body;
    this.path = pathname;
  }
};
var PREVIEW_PUBLISH_RECOVERY_STATUSES = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
var PREVIEW_PUBLISH_RECOVERY_ATTEMPTS = 30;
var PREVIEW_PUBLISH_RECOVERY_INTERVAL_MS = 2e3;
function normalizeBaseUrl(value) {
  return (value || DEFAULT_RIDDLE_API_BASE_URL).replace(/\/$/, "");
}
function fetchFor(config = {}) {
  return config.fetchImpl || fetch;
}
function resolveRiddleApiKeyWithSource(config = {}) {
  if (config.apiKey?.trim()) return { apiKey: config.apiKey.trim(), source: "option" };
  if (process.env.RIDDLE_API_KEY?.trim()) return { apiKey: process.env.RIDDLE_API_KEY.trim(), source: "env" };
  const keyFile = config.apiKeyFile || process.env.RIDDLE_API_KEY_FILE || DEFAULT_RIDDLE_API_KEY_FILE;
  if ((0, import_node_fs.existsSync)(keyFile)) {
    const key = (0, import_node_fs.readFileSync)(keyFile, "utf8").trim();
    if (key) return { apiKey: key, source: "file", file: keyFile };
  }
  throw new Error(`Riddle API key missing. Set RIDDLE_API_KEY or write ${DEFAULT_RIDDLE_API_KEY_FILE}.`);
}
function resolveRiddleApiKeySource(config = {}) {
  const { apiKey: _apiKey, ...source } = resolveRiddleApiKeyWithSource(config);
  return source;
}
function resolveRiddleApiKey(config = {}) {
  return resolveRiddleApiKeyWithSource(config).apiKey;
}
async function riddleRequestJson(config, pathname, init = {}) {
  const response = await fetchFor(config)(`${normalizeBaseUrl(config.apiBaseUrl)}${pathname}`, {
    ...init,
    headers: {
      Authorization: `Bearer ${resolveRiddleApiKey(config)}`,
      "Content-Type": "application/json",
      ...init.headers || {}
    }
  });
  const text = await response.text();
  let json = null;
  try {
    json = JSON.parse(text);
  } catch {
  }
  if (!response.ok) throw new RiddleApiError(pathname, response.status, text);
  return json ?? text;
}
async function getRiddleBalance(config = {}) {
  return riddleRequestJson(config, "/v1/balance", { method: "GET" });
}
function previewDeployResultFromRecord(input) {
  const { record, id, label, framework, expiresAt, publishRecovered, publishError } = input;
  return {
    ok: true,
    id: String(record.id || record.preview_id || id),
    label,
    framework,
    preview_url: String(record.preview_url || ""),
    file_count: typeof record.file_count === "number" ? record.file_count : void 0,
    total_bytes: typeof record.total_bytes === "number" ? record.total_bytes : void 0,
    expires_at: expiresAt,
    publish_recovered: publishRecovered || void 0,
    publish_error: publishError,
    warnings: input.warnings?.length ? input.warnings : void 0,
    raw: record
  };
}
function canRecoverPreviewPublish(error) {
  return error instanceof RiddleApiError && PREVIEW_PUBLISH_RECOVERY_STATUSES.has(error.status);
}
function summarizePreviewDirectory(directory) {
  const stack = [directory];
  let fileCount = 0;
  let totalBytes = 0;
  while (stack.length) {
    const current = stack.pop();
    for (const entry of (0, import_node_fs.readdirSync)(current, { withFileTypes: true })) {
      const fullPath = import_node_path.default.join(current, entry.name);
      if (entry.isDirectory()) {
        stack.push(fullPath);
        continue;
      }
      if (!entry.isFile()) continue;
      const stat = (0, import_node_fs.statSync)(fullPath);
      fileCount += 1;
      totalBytes += stat.size;
    }
  }
  return { file_count: fileCount, total_bytes: totalBytes };
}
function previewProgressEmitter(config, base) {
  return async (snapshot) => {
    if (!config.onPreviewProgress) return;
    await config.onPreviewProgress({
      label: base.label,
      framework: base.framework,
      directory: base.directory,
      elapsed_ms: Math.max(0, Date.now() - base.startedAt),
      warnings: base.warnings?.length ? base.warnings : void 0,
      ...snapshot
    });
  };
}
async function waitForPublishedPreview(config, input) {
  await input.emitProgress?.({
    stage: "publish_recovering",
    id: input.id,
    file_count: input.localSummary?.file_count,
    total_bytes: input.localSummary?.total_bytes,
    publish_error: input.publishError.message,
    message: `publish returned ${input.publishError.status}; polling preview status`
  });
  for (let attempt = 1; attempt <= PREVIEW_PUBLISH_RECOVERY_ATTEMPTS; attempt += 1) {
    const status = await riddleRequestJson(config, `/v1/preview/${input.id}`);
    await input.emitProgress?.({
      stage: "checking_status",
      id: input.id,
      attempt,
      attempts: PREVIEW_PUBLISH_RECOVERY_ATTEMPTS,
      status: typeof status.status === "string" ? status.status : null,
      preview_url: typeof status.preview_url === "string" ? status.preview_url : void 0,
      file_count: typeof status.file_count === "number" ? status.file_count : input.localSummary?.file_count,
      total_bytes: typeof status.total_bytes === "number" ? status.total_bytes : input.localSummary?.total_bytes
    });
    if (String(status.status || "") === "ready" && String(status.preview_url || "").trim()) {
      await input.emitProgress?.({
        stage: "ready",
        id: input.id,
        preview_url: String(status.preview_url),
        file_count: typeof status.file_count === "number" ? status.file_count : input.localSummary?.file_count,
        total_bytes: typeof status.total_bytes === "number" ? status.total_bytes : input.localSummary?.total_bytes
      });
      return previewDeployResultFromRecord({
        record: status,
        id: input.id,
        label: input.label,
        framework: input.framework,
        expiresAt: input.expiresAt,
        publishRecovered: true,
        publishError: input.publishError.message,
        warnings: input.warnings
      });
    }
    if (attempt < PREVIEW_PUBLISH_RECOVERY_ATTEMPTS) {
      await new Promise((resolve) => setTimeout(resolve, PREVIEW_PUBLISH_RECOVERY_INTERVAL_MS));
    }
  }
  throw input.publishError;
}
async function deployRiddlePreview(config, directory, label, framework = "static") {
  if (!directory?.trim()) throw new Error("directory is required");
  if (!label?.trim()) throw new Error("label is required");
  if (framework !== "spa" && framework !== "static") throw new Error("framework must be spa or static");
  const startedAt = Date.now();
  const warnings = collectRiddlePreviewDeployWarnings(directory, framework);
  const emitProgress = previewProgressEmitter(config, { label, framework, directory, startedAt, warnings });
  await emitProgress({ stage: "validating", message: "checking preview input directory" });
  const localSummary = summarizePreviewDirectory(directory);
  await emitProgress({
    stage: "creating",
    file_count: localSummary.file_count,
    total_bytes: localSummary.total_bytes,
    message: "creating preview upload target"
  });
  const created = await riddleRequestJson(config, "/v1/preview", {
    method: "POST",
    body: JSON.stringify({ framework, label })
  });
  const id = String(created.id || "");
  const uploadUrl = String(created.upload_url || "");
  if (!id || !uploadUrl) throw new Error("Riddle preview create response was missing id or upload_url.");
  await emitProgress({
    stage: "created",
    id,
    file_count: localSummary.file_count,
    total_bytes: localSummary.total_bytes,
    message: "preview upload target created"
  });
  const scratch = (0, import_node_fs.mkdtempSync)(import_node_path.default.join((0, import_node_os.tmpdir)(), "riddle-preview-upload-"));
  const tarball = import_node_path.default.join(scratch, `${id}.tar.gz`);
  let tarballBytes = 0;
  try {
    await emitProgress({
      stage: "archiving",
      id,
      file_count: localSummary.file_count,
      total_bytes: localSummary.total_bytes,
      message: "creating preview archive"
    });
    (0, import_node_child_process.execFileSync)("tar", ["czf", tarball, "-C", directory, "."], { stdio: "pipe" });
    tarballBytes = (0, import_node_fs.statSync)(tarball).size;
    await emitProgress({
      stage: "archived",
      id,
      file_count: localSummary.file_count,
      total_bytes: localSummary.total_bytes,
      tarball_bytes: tarballBytes,
      message: "preview archive created"
    });
    await emitProgress({
      stage: "uploading",
      id,
      file_count: localSummary.file_count,
      total_bytes: localSummary.total_bytes,
      tarball_bytes: tarballBytes,
      message: "uploading preview archive"
    });
    const upload = await fetchFor(config)(uploadUrl, {
      method: "PUT",
      headers: { "Content-Type": "application/gzip" },
      body: (0, import_node_fs.readFileSync)(tarball)
    });
    if (!upload.ok) {
      throw new RiddleApiError(uploadUrl, upload.status, await upload.text());
    }
    await emitProgress({
      stage: "uploaded",
      id,
      file_count: localSummary.file_count,
      total_bytes: localSummary.total_bytes,
      tarball_bytes: tarballBytes,
      message: "preview archive uploaded"
    });
  } finally {
    (0, import_node_fs.rmSync)(scratch, { recursive: true, force: true });
  }
  const expiresAt = typeof created.expires_at === "string" ? created.expires_at : void 0;
  try {
    await emitProgress({
      stage: "publishing",
      id,
      file_count: localSummary.file_count,
      total_bytes: localSummary.total_bytes,
      tarball_bytes: tarballBytes || void 0,
      message: "publishing preview"
    });
    const published = await riddleRequestJson(config, `/v1/preview/${id}/publish`, {
      method: "POST"
    });
    await emitProgress({
      stage: "ready",
      id,
      preview_url: String(published.preview_url || ""),
      file_count: typeof published.file_count === "number" ? published.file_count : localSummary.file_count,
      total_bytes: typeof published.total_bytes === "number" ? published.total_bytes : localSummary.total_bytes
    });
    return previewDeployResultFromRecord({ record: published, id, label, framework, expiresAt, warnings });
  } catch (error) {
    if (!canRecoverPreviewPublish(error)) throw error;
    return waitForPublishedPreview(config, {
      id,
      label,
      framework,
      expiresAt,
      publishError: error,
      warnings,
      localSummary,
      emitProgress
    });
  }
}
function latestMatchingMtimeMs(directory, predicate) {
  let latest = 0;
  const stack = [directory];
  let visited = 0;
  while (stack.length) {
    const current = stack.pop();
    for (const entry of (0, import_node_fs.readdirSync)(current, { withFileTypes: true })) {
      const fullPath = import_node_path.default.join(current, entry.name);
      if (entry.isDirectory()) {
        stack.push(fullPath);
        continue;
      }
      if (!entry.isFile() || !predicate(fullPath)) continue;
      const stat = (0, import_node_fs.statSync)(fullPath);
      latest = Math.max(latest, stat.mtimeMs);
      visited += 1;
      if (visited >= 1e4) return latest;
    }
  }
  return latest;
}
function collectRiddlePreviewDeployWarnings(directory, framework = "static") {
  try {
    if (framework !== "static") return [];
    const resolvedDirectory = import_node_path.default.resolve(directory);
    if (import_node_path.default.basename(resolvedDirectory) !== "out") return [];
    const repoRoot = import_node_path.default.dirname(resolvedDirectory);
    const nextAppDir = import_node_path.default.join(repoRoot, ".next", "server", "app");
    if (!(0, import_node_fs.existsSync)(nextAppDir)) return [];
    const nextRenderedMtimeMs = latestMatchingMtimeMs(nextAppDir, (filePath) => /\.(?:html|rsc)$/i.test(filePath));
    if (!nextRenderedMtimeMs) return [];
    const outRenderedMtimeMs = (0, import_node_fs.existsSync)(resolvedDirectory) ? latestMatchingMtimeMs(resolvedDirectory, (filePath) => /\.(?:html|txt|rsc)$/i.test(filePath)) : 0;
    if (!outRenderedMtimeMs) {
      return [
        "Riddle Preview static deploy target is an out/ directory with newer Next render output in .next/server/app, but no rendered HTML/RSC files were found in out/. Run the project static bundle/export step before deploying."
      ];
    }
    if (nextRenderedMtimeMs > outRenderedMtimeMs + 1e3) {
      return [
        "Riddle Preview static deploy target out/ appears older than the Next render output in .next/server/app. Run the project static bundle/export step, such as npm run build:static-deploy or next export, before deploying to avoid a stale Preview."
      ];
    }
    return [];
  } catch {
    return [];
  }
}
async function deployRiddleStaticPreview(config, directory, label) {
  return deployRiddlePreview(config, directory, label, "static");
}
function createTarball(directory, label, exclude = []) {
  const scratch = (0, import_node_fs.mkdtempSync)(import_node_path.default.join((0, import_node_os.tmpdir)(), "riddle-upload-"));
  const tarball = import_node_path.default.join(scratch, `${label}.tar.gz`);
  const excludeArgs = exclude.flatMap((item) => ["--exclude", item]);
  try {
    (0, import_node_child_process.execFileSync)("tar", ["czf", tarball, ...excludeArgs, "-C", directory, "."], { stdio: "pipe" });
    return { scratch, tarball };
  } catch (error) {
    (0, import_node_fs.rmSync)(scratch, { recursive: true, force: true });
    throw error;
  }
}
function parseRiddleViewport(value) {
  if (!value) return void 0;
  const match = /^(\d+)x(\d+)$/.exec(value.trim());
  if (!match) throw new Error("viewport must look like 1280x720");
  return { width: Number(match[1]), height: Number(match[2]) };
}
function scriptErrorFrom(job) {
  const nested = job?.proof_of_execution && typeof job.proof_of_execution === "object" && !Array.isArray(job.proof_of_execution) ? job.proof_of_execution : null;
  return typeof job?.script_error === "string" && job.script_error.trim() ? job.script_error : typeof nested?.script_error === "string" && nested.script_error.trim() ? nested.script_error : null;
}
async function runRiddleServerPreview(config, input) {
  if (!input.directory?.trim()) throw new Error("directory is required");
  if (!input.script?.trim()) throw new Error("script is required");
  const port = input.port || 3e3;
  const routePath = input.path || "/";
  const timeoutSec = input.timeoutSec || 180;
  const created = await riddleRequestJson(config, "/v1/server-preview", {
    method: "POST",
    body: JSON.stringify({
      image: input.image || "node:22-slim",
      command: input.command || "node scripts/riddleSpaPreviewServer.mjs build 3000",
      port,
      path: routePath,
      readiness_path: input.readinessPath || routePath,
      readiness_timeout: input.readinessTimeoutSec || 90,
      wait_until: input.waitUntil || "domcontentloaded",
      wait_for_selector: input.waitForSelector || "body",
      navigation_timeout: input.navigationTimeoutSec || 60,
      viewport: input.viewport,
      timeout: timeoutSec,
      script: input.script
    })
  });
  const jobId = String(created.job_id || "");
  const uploadUrl = String(created.upload_url || "");
  if (!jobId || !uploadUrl) {
    throw new Error(`Riddle server preview create response was missing job_id or upload_url.`);
  }
  const { scratch, tarball } = createTarball(input.directory, jobId, [
    ".git",
    "node_modules",
    "test-results",
    ...input.exclude || []
  ]);
  try {
    const upload = await fetchFor(config)(uploadUrl, {
      method: "PUT",
      headers: { "Content-Type": "application/gzip" },
      body: (0, import_node_fs.readFileSync)(tarball)
    });
    if (!upload.ok) throw new RiddleApiError(uploadUrl, upload.status, await upload.text());
  } finally {
    (0, import_node_fs.rmSync)(scratch, { recursive: true, force: true });
  }
  await riddleRequestJson(config, `/v1/server-preview/${jobId}/start`, {
    method: "POST"
  });
  let job = null;
  const attempts = input.pollAttempts || Math.ceil((timeoutSec + 90) / 2);
  const intervalMs = input.pollIntervalMs || 2e3;
  for (let index = 0; index < attempts; index += 1) {
    job = await riddleRequestJson(config, `/v1/server-preview/${jobId}`);
    if (isTerminalRiddleJobStatus(job.status)) break;
    if (index + 1 < attempts) await new Promise((resolve) => setTimeout(resolve, intervalMs));
  }
  const status = job?.status ? String(job.status) : null;
  const scriptError = scriptErrorFrom(job);
  return {
    ok: (status === "completed" || status === "complete") && !scriptError,
    job_id: jobId,
    status,
    terminal: isTerminalRiddleJobStatus(status),
    script_error: scriptError,
    job
  };
}
async function runRiddleScript(config, input) {
  if (!input.url?.trim()) throw new Error("url is required");
  if (!input.script?.trim()) throw new Error("script is required");
  const payload = {
    url: input.url,
    script: input.script,
    sync: input.sync ?? false,
    timeout_sec: input.timeoutSec || 120
  };
  if (input.viewport) payload.viewport = input.viewport;
  if (input.include) payload.include = input.include;
  if (typeof input.strict === "boolean") payload.strict = input.strict;
  if (input.options) payload.options = input.options;
  return riddleRequestJson(config, "/v1/run", {
    method: "POST",
    body: JSON.stringify(payload)
  });
}
function isTerminalRiddleJobStatus(status) {
  return ["completed", "complete", "completed_error", "completed_timeout", "failed"].includes(String(status || ""));
}
function stringField(record, key) {
  const value = record?.[key];
  return typeof value === "string" && value.trim() ? value.trim() : null;
}
function parseTimestampMs(value) {
  if (!value) return null;
  const parsed = Date.parse(value);
  return Number.isFinite(parsed) ? parsed : null;
}
function buildPollSnapshot(jobId, job, input) {
  const status = job?.status ? String(job.status) : null;
  const terminal = isTerminalRiddleJobStatus(status);
  const createdAt = stringField(job, "created_at");
  const submittedAt = stringField(job, "submitted_at");
  const completedAt = stringField(job, "completed_at");
  const createdMs = parseTimestampMs(createdAt);
  const submittedMs = parseTimestampMs(submittedAt);
  let queueElapsedMs = null;
  if (createdMs !== null && submittedMs !== null) {
    queueElapsedMs = Math.max(0, submittedMs - createdMs);
  } else if (createdMs !== null && !submittedAt && !terminal) {
    queueElapsedMs = Math.max(0, input.observedAt - createdMs);
  }
  return {
    job_id: jobId,
    status,
    terminal,
    attempt: input.attempt,
    attempts: input.attempts,
    elapsed_ms: Math.max(0, input.observedAt - input.startedAt),
    created_at: createdAt,
    submitted_at: submittedAt,
    completed_at: completedAt,
    queue_elapsed_ms: queueElapsedMs,
    pre_submission_elapsed_ms: Math.max(0, Math.floor(input.preSubmissionElapsedMs ?? 0)),
    running_without_submission: Boolean(status && !terminal && !submittedAt)
  };
}
function pollMessage(snapshot, timedOut) {
  if (!timedOut) return void 0;
  const submitted = snapshot.submitted_at || "not submitted";
  const wakeHint = snapshot.running_without_submission && !snapshot.created_at && !snapshot.submitted_at ? ` ${RIDDLE_UNSUBMITTED_WAKE_HINT}.` : "";
  const queue = snapshot.queue_elapsed_ms !== null ? ` queue_elapsed_ms=${snapshot.queue_elapsed_ms}` : "";
  const preSubmit = snapshot.pre_submission_elapsed_ms > 0 ? ` pre_submission_elapsed_ms=${snapshot.pre_submission_elapsed_ms}` : "";
  return `Riddle job ${snapshot.job_id} did not reach a terminal status after ${snapshot.attempt} poll attempts; status=${snapshot.status || "unknown"} submitted_at=${submitted}.${wakeHint}${queue}${preSubmit}`;
}
async function pollRiddleJob(config, jobId, options = {}) {
  if (!jobId?.trim()) throw new Error("jobId is required");
  const attempts = Math.max(1, Math.floor(options.attempts ?? (options.wait ? 300 : 1)));
  const intervalMs = Math.max(0, Math.floor(options.intervalMs ?? 2e3));
  const progressEveryMs = Math.max(0, Math.floor(options.progressEveryMs ?? 1e4));
  const unsubmittedTimeoutMs = Math.max(0, Math.floor(options.unsubmittedTimeoutMs ?? 0));
  const startedAt = Date.now();
  let job = null;
  let lastSnapshot = null;
  let lastProgressAt = 0;
  let lastProgressKey = "";
  let preSubmissionElapsedMs = 0;
  let unsubmittedTimedOut = false;
  for (let index = 0; index < attempts; index += 1) {
    job = await riddleRequestJson(config, `/v1/jobs/${jobId}`);
    const observedAt = Date.now();
    const nextSnapshot = buildPollSnapshot(jobId, job, {
      attempt: index + 1,
      attempts,
      startedAt,
      observedAt,
      preSubmissionElapsedMs
    });
    if (nextSnapshot.running_without_submission) {
      preSubmissionElapsedMs = Math.max(preSubmissionElapsedMs, nextSnapshot.elapsed_ms);
    }
    lastSnapshot = {
      ...nextSnapshot,
      pre_submission_elapsed_ms: preSubmissionElapsedMs
    };
    const progressKey = [
      lastSnapshot.status || "unknown",
      lastSnapshot.terminal ? "terminal" : "nonterminal",
      lastSnapshot.submitted_at ? "submitted" : "unsubmitted"
    ].join(":");
    if (options.onProgress) {
      const shouldReport = index === 0 || lastSnapshot.terminal || progressKey !== lastProgressKey || observedAt - lastProgressAt >= progressEveryMs;
      if (shouldReport) {
        lastProgressAt = observedAt;
        lastProgressKey = progressKey;
        await options.onProgress(lastSnapshot);
      }
    }
    unsubmittedTimedOut = Boolean(
      options.wait && unsubmittedTimeoutMs > 0 && lastSnapshot.running_without_submission && !lastSnapshot.created_at && !lastSnapshot.submitted_at && preSubmissionElapsedMs >= unsubmittedTimeoutMs
    );
    if (lastSnapshot.terminal) break;
    if (unsubmittedTimedOut) break;
    if (index + 1 < attempts) {
      await new Promise((resolve) => setTimeout(resolve, intervalMs));
    }
  }
  const status = job?.status ? String(job.status) : null;
  const fallbackObservedAt = Date.now();
  const snapshot = lastSnapshot || buildPollSnapshot(jobId, job, {
    attempt: 0,
    attempts,
    startedAt,
    observedAt: fallbackObservedAt
  });
  if (!isTerminalRiddleJobStatus(status)) {
    const timedOut = Boolean(options.wait);
    return {
      ok: !timedOut,
      job_id: jobId,
      status,
      terminal: false,
      job,
      poll: {
        ...snapshot,
        timed_out: timedOut,
        interval_ms: intervalMs,
        unsubmitted_timeout: unsubmittedTimedOut || void 0,
        unsubmitted_timeout_ms: unsubmittedTimedOut ? unsubmittedTimeoutMs : void 0,
        message: pollMessage(snapshot, timedOut)
      }
    };
  }
  const artifacts = await riddleRequestJson(config, `/v1/jobs/${jobId}/artifacts`);
  return {
    ok: status === "completed" || status === "complete",
    job_id: jobId,
    status,
    terminal: true,
    job,
    artifacts,
    poll: {
      ...snapshot,
      timed_out: false,
      interval_ms: intervalMs
    }
  };
}
function createRiddleApiClient(config = {}) {
  return {
    apiKeySource: () => resolveRiddleApiKeySource(config),
    requestJson: (pathname, init) => riddleRequestJson(config, pathname, init),
    getBalance: () => getRiddleBalance(config),
    deployPreview: (directory, label, framework = "static") => deployRiddlePreview(config, directory, label, framework),
    deployStaticPreview: (directory, label) => deployRiddleStaticPreview(config, directory, label),
    runScript: (input) => runRiddleScript(config, input),
    runServerPreview: (input) => runRiddleServerPreview(config, input),
    pollJob: (jobId, options) => pollRiddleJob(config, jobId, options)
  };
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  DEFAULT_RIDDLE_API_BASE_URL,
  DEFAULT_RIDDLE_API_KEY_FILE,
  RIDDLE_UNSUBMITTED_WAKE_HINT,
  RiddleApiError,
  collectRiddlePreviewDeployWarnings,
  createRiddleApiClient,
  deployRiddlePreview,
  deployRiddleStaticPreview,
  getRiddleBalance,
  isTerminalRiddleJobStatus,
  parseRiddleViewport,
  pollRiddleJob,
  resolveRiddleApiKey,
  resolveRiddleApiKeySource,
  riddleRequestJson,
  runRiddleScript,
  runRiddleServerPreview
});
