import { o as hasConfiguredSecretInput } from "./types.secrets-CwOir82x.js";
import { c as isProviderApiKeyConfigured } from "./provider-auth-xmozx9bh.js";
import { r as fetchWithTimeout } from "./fetch-timeout-CboMtRsl.js";
import { r as assertOkOrThrowHttpError } from "./provider-http-errors-C5jbpUhT.js";
import { a as postJsonRequest, i as pollProviderOperationJson, n as createProviderOperationDeadline, u as resolveProviderHttpRequestConfig } from "./shared-DRHp86wl.js";
import { a as resolveApiKeyForProvider } from "./provider-auth-runtime-C6NVEhjh.js";
import "./provider-http-DDi_PB2_.js";
//#region extensions/sealgw/media-gateway.ts
const SEALGW_PROVIDER_ID = "sealgw";
/**
* 同账号体系的 sealseek LLM provider id。sealgw 媒体网关与 sealseek LLM 共用
* 同一个网关账号、同一个 host、同一个 API key，因此 sealgw 未单独配置时复用 sealseek 的鉴权与 baseUrl。
*/
const SEALSEEK_TEXT_PROVIDER_ID = "sealseek";
/** Local default; override with models.providers.sealgw.baseUrl. */
const DEFAULT_SEALGW_BASE_URL = "http://localhost:9010";
/** Submit/poll budget. Media tasks (esp. video) are slow; default 10 min. */
const DEFAULT_SEALGW_TIMEOUT_MS = 600 * 1e3;
const DEFAULT_POLL_INTERVAL_MS = 3e3;
const DEFAULT_MAX_POLL_ATTEMPTS = 200;
/** Terminal task states as defined by MediaTaskStatusEnum on the gateway. */
const TERMINAL_OK = new Set(["SUCCESS"]);
const TERMINAL_FAIL = new Set([
	"FAILED",
	"TIMEOUT",
	"REFUNDED"
]);
function resolveSealgwProviderConfig(cfg) {
	return cfg?.models?.providers?.[SEALGW_PROVIDER_ID];
}
function stripTrailingSlash(value) {
	return value.replace(/\/+$/u, "");
}
function resolveSealgwBaseUrl(cfg) {
	const direct = resolveSealgwProviderConfig(cfg)?.baseUrl?.trim();
	if (direct) return stripTrailingSlash(direct);
	const llmBaseUrl = cfg?.models?.providers?.[SEALSEEK_TEXT_PROVIDER_ID]?.baseUrl?.trim();
	if (llmBaseUrl) return stripTrailingSlash(llmBaseUrl).replace(/\/v1$/u, "");
	return DEFAULT_SEALGW_BASE_URL;
}
/**
* 是否允许访问私有/内网 IP。sealgw 是自托管/内网媒体网关（默认 localhost，
* 自建部署常解析到内网 IP），baseUrl 由运营方配置（可信、非请求注入），因此默认放行，
* 否则 SSRF guard 会拦截 submit 请求（Blocked: resolves to private/internal IP）。
* 运营方可显式 models.providers.sealgw.allowPrivateNetwork=false 收紧。
*/
function resolveSealgwAllowPrivateNetwork(cfg) {
	return resolveSealgwProviderConfig(cfg)?.allowPrivateNetwork === false ? false : true;
}
/**
* 解析单个 provider 的鉴权，但“拿不到 key 不抛错”。
*
* 关键点：核心的 resolveApiKeyForProvider 在所有来源（config / env / auth profile）
* 都拿不到 key 时是 **抛异常**（src/agents/model-auth.ts 末尾 throw "No API key found..."），
* 并非返回 { apiKey: undefined }。两级 fallback 不能直接 await 第一个再判空，
* 否则第一步（sealgw 通常未单独配置）一抛错就中断，永远 fallback 不到 sealseek。
* 因此这里把“无 key”归一化成返回 undefined，由调用方决定是否继续 fallback。
*/
async function resolveProviderApiKeyOrUndefined(provider, params) {
	try {
		return await resolveApiKeyForProvider({
			provider,
			cfg: params.cfg,
			agentDir: params.agentDir,
			store: params.store
		});
	} catch {
		return;
	}
}
/**
* 两级鉴权解析：sealgw 媒体网关与 sealseek LLM 共用同一账号体系/同一个 key。
* 优先用 sealgw 自身配置的 key；若 sealgw 未单独配置，则复用 sealseek 的 key。
* 这样用户只配置一次 LLM 鉴权，媒体生成即可零配置可用。
*
* 两步都用“不抛错”的解析：任一步拿到 key 立即返回；两步都没有时返回一个
* apiKey 为 undefined 的结果（不抛错），把“缺 key”的友好提示交给调用方
* （image/video provider 与 list-models 工具都按 !auth.apiKey 给出统一文案）。
*/
async function resolveSealgwAuth(params) {
	const direct = await resolveProviderApiKeyOrUndefined(SEALGW_PROVIDER_ID, params);
	if (direct?.apiKey) return direct;
	const fallback = await resolveProviderApiKeyOrUndefined(SEALSEEK_TEXT_PROVIDER_ID, params);
	if (fallback?.apiKey) return fallback;
	return {
		apiKey: void 0,
		source: "sealgw:unconfigured",
		mode: "api-key"
	};
}
/** config 里 models.providers.<id>.apiKey 是否已配置（兼容明文 string 与 SecretRef）。 */
function hasConfigProviderApiKey(cfg, providerId) {
	return hasConfiguredSecretInput(cfg?.models?.providers?.[providerId]?.apiKey);
}
/**
* 媒体生成是否已具备鉴权：sealgw 自身或 sealseek（同账号体系 LLM）任一配置了 key 即可。
* 三个来源都算：config 的 models.providers.<id>.apiKey、环境变量、auth profile。
*
* 注意：执行时的模型自动解析（resolveCapabilityModelCandidatesForTool）依赖本函数，
* 用户的 key 通常写在 config，因此这里必须检查 config，否则 provider 不会被选为候选模型，
* 工具虽点亮但调用时报 "No image-generation model configured"。
*/
function isSealgwAuthConfigured(cfg, agentDir) {
	if (hasConfigProviderApiKey(cfg, "sealgw") || hasConfigProviderApiKey(cfg, "sealseek")) return true;
	return isProviderApiKeyConfigured({
		provider: "sealgw",
		agentDir
	}) || isProviderApiKeyConfigured({
		provider: "sealseek",
		agentDir
	});
}
/**
* Default model when the agent omits one. The gateway also supports its own
* defaulting, but a provider must surface a defaultModel for tool display, so
* we read it from config and fall back to undefined (caller decides).
*/
function resolveSealgwDefaultModel(cfg) {
	const raw = resolveSealgwProviderConfig(cfg);
	return (typeof raw?.defaultModel === "string" ? raw.defaultModel.trim() : "") || void 0;
}
/**
* 工具展示与零配置提交用的默认模型占位符。模型的自动选择交给网关后端：
* 插件原样把 "auto" 发给网关，由后端解析成真实启用的模型（并处理计费/可用性）。
*/
const SEALGW_DISPLAY_DEFAULT_MODEL = "auto";
/**
* 解析提交给网关的 model：
* - 显式真实模型 → 原样透传；
* - "auto" 占位或空 → 优先用配置的 defaultModel；未配置则发 "auto"，由网关后端自动选模型。
* 这样零配置（用户不指定模型）时，模型选择责任落在网关后端，而非插件去拉列表挨个试。
*/
function resolveSealgwRequestModel(requestModel, cfg) {
	const requested = requestModel?.trim();
	if (requested && requested !== "auto") return requested;
	return resolveSealgwDefaultModel(cfg) || "auto";
}
/**
* Submit a generation task and poll until terminal. Returns the resolved
* result URLs. Throws a descriptive Error on upstream failure / timeout.
*/
async function runSealgwMediaTask(params) {
	const { baseUrl, allowPrivateNetwork, headers, dispatcherPolicy } = resolveProviderHttpRequestConfig({
		baseUrl: params.baseUrl,
		defaultBaseUrl: DEFAULT_SEALGW_BASE_URL,
		defaultHeaders: {
			Authorization: `Bearer ${params.apiKey}`,
			"Content-Type": "application/json"
		},
		provider: SEALGW_PROVIDER_ID,
		capability: params.capability,
		transport: "http",
		allowPrivateNetwork: params.allowPrivateNetwork
	});
	const { response: submitResponse, release } = await postJsonRequest({
		url: `${baseUrl}/v1/${params.pathSegment}/generations`,
		headers,
		body: params.body,
		timeoutMs: params.timeoutMs,
		fetchFn: params.fetchFn,
		allowPrivateNetwork,
		dispatcherPolicy,
		auditContext: `${params.providerLabel} submit`
	});
	let taskId;
	let syncResult;
	try {
		await assertOkOrThrowHttpError(submitResponse, `${params.providerLabel} submit failed`);
		const submitPayload = await submitResponse.json();
		const resolvedTaskId = submitPayload.task_id?.trim();
		if (!resolvedTaskId) throw new Error(`${params.providerLabel}: gateway did not return a task_id`);
		taskId = resolvedTaskId;
		if (submitPayload.status && TERMINAL_OK.has(submitPayload.status) && (submitPayload.result_urls?.length ?? 0) > 0) syncResult = {
			resultUrls: submitPayload.result_urls ?? [],
			actualModel: submitPayload.actual_model ?? submitPayload.model
		};
		else assertNotFailed(submitPayload, params.providerLabel);
	} finally {
		await release();
	}
	if (syncResult) return syncResult;
	const deadline = createProviderOperationDeadline({
		timeoutMs: params.timeoutMs,
		label: params.providerLabel
	});
	const finalPayload = await pollProviderOperationJson({
		url: `${baseUrl}/v1/tasks/${encodeURIComponent(taskId)}`,
		headers,
		deadline,
		defaultTimeoutMs: params.timeoutMs,
		fetchFn: params.fetchFn,
		maxAttempts: DEFAULT_MAX_POLL_ATTEMPTS,
		pollIntervalMs: DEFAULT_POLL_INTERVAL_MS,
		requestFailedMessage: `${params.providerLabel}: task status request failed`,
		timeoutMessage: `${params.providerLabel}: task ${taskId} did not complete before timeout`,
		isComplete: (payload) => !!payload.status && TERMINAL_OK.has(payload.status),
		getFailureMessage: (payload) => payload.status && TERMINAL_FAIL.has(payload.status) ? formatFailure(params.providerLabel, payload) : void 0
	});
	const resultUrls = finalPayload.result_urls ?? [];
	if (resultUrls.length === 0) throw new Error(`${params.providerLabel}: task ${taskId} succeeded but returned no result URLs`);
	return {
		resultUrls,
		actualModel: finalPayload.actual_model ?? finalPayload.model
	};
}
function assertNotFailed(payload, providerLabel) {
	if (payload.status && TERMINAL_FAIL.has(payload.status)) throw new Error(formatFailure(providerLabel, payload));
}
function formatFailure(providerLabel, payload) {
	const code = payload.error?.code ? ` [${payload.error.code}]` : "";
	const message = payload.error?.message ? `: ${payload.error.message}` : "";
	return `${providerLabel}: task ${payload.status}${code}${message}`;
}
/**
* List the gateway's currently-enabled media models (image and/or video).
* `id` is the display_name an agent should pass back as `model`. Requires a
* valid API key (the /v1/** routes are all auth-gated).
*/
async function fetchSealgwModels(params) {
	const { baseUrl, allowPrivateNetwork, headers, dispatcherPolicy } = resolveProviderHttpRequestConfig({
		baseUrl: params.baseUrl,
		defaultBaseUrl: DEFAULT_SEALGW_BASE_URL,
		defaultHeaders: {
			Authorization: `Bearer ${params.apiKey}`,
			"Content-Type": "application/json"
		},
		provider: SEALGW_PROVIDER_ID,
		capability: params.modality === "video" ? "video" : "image",
		transport: "http"
	});
	const response = await fetchWithTimeout(`${baseUrl}/v1/media/models${params.modality ? `?modality=${params.modality}` : ""}`, {
		method: "GET",
		headers
	}, params.timeoutMs, params.fetchFn);
	await assertOkOrThrowHttpError(response, "SealGW: media models request failed");
	return ((await response.json()).data ?? []).filter((entry) => typeof entry.id === "string" && entry.id.length > 0).map((entry) => {
		const result = {
			id: entry.id,
			modality: entry.modality ?? ""
		};
		if (entry.extra_config) result.extraConfig = entry.extra_config;
		return result;
	});
}
const sealgwModelCache = /* @__PURE__ */ new Map();
const SEALGW_MODEL_CACHE_TTL_MS = 300 * 1e3;
/** 仅供测试：清空模型缓存，避免用例间相互污染。 */
function _resetSealgwModelCacheForTesting() {
	sealgwModelCache.clear();
}
/**
* 读取媒体模型清单：命中且未过期走内存缓存，否则打网关拉取并回填缓存。
* 参数与 fetchSealgwModels 一致，便于 provider / 工具 / 启动预热共用同一条获取路径。
*/
async function getSealgwModelsCached(params) {
	const cacheKey = params.modality ?? "all";
	const cached = sealgwModelCache.get(cacheKey);
	if (cached && Date.now() - cached.fetchedAtMs < SEALGW_MODEL_CACHE_TTL_MS) return cached.models;
	const models = await fetchSealgwModels(params);
	sealgwModelCache.set(cacheKey, {
		fetchedAtMs: Date.now(),
		models
	});
	return models;
}
/** 预热拉取超时：元数据请求，给 30s 上限即可。 */
const PREWARM_TIMEOUT_MS = 3e4;
/**
* 启动/激活时的模型清单预热：解析鉴权（含 sealseek fallback），有 key 才拉取，
* 失败吞掉（best-effort，缓存留空，运行时再拉）。返回 Promise 便于测试与可选 await，
* service 中以 fire-and-forget 方式调用，不阻塞启动。
*/
async function prewarmSealgwModels(params) {
	const auth = await resolveSealgwAuth({
		cfg: params.cfg,
		agentDir: params.agentDir
	}).catch(() => void 0);
	if (!auth?.apiKey) return;
	await getSealgwModelsCached({
		apiKey: auth.apiKey,
		baseUrl: resolveSealgwBaseUrl(params.cfg),
		timeoutMs: PREWARM_TIMEOUT_MS,
		fetchFn: params.fetchFn ?? fetch
	}).catch(() => void 0);
}
/** Download a result URL into bytes (used by the image provider). */
async function downloadSealgwAsset(params) {
	const response = await fetchWithTimeout(params.url, { method: "GET" }, params.timeoutMs, params.fetchFn);
	await assertOkOrThrowHttpError(response, `${params.providerLabel}: result download failed`);
	const arrayBuffer = await response.arrayBuffer();
	const mimeType = response.headers.get("content-type")?.split(";")[0]?.trim() || "image/png";
	return {
		buffer: Buffer.from(arrayBuffer),
		mimeType
	};
}
//#endregion
export { runSealgwMediaTask as _, SEALSEEK_TEXT_PROVIDER_ID as a, fetchSealgwModels as c, prewarmSealgwModels as d, resolveSealgwAllowPrivateNetwork as f, resolveSealgwRequestModel as g, resolveSealgwDefaultModel as h, SEALGW_PROVIDER_ID as i, getSealgwModelsCached as l, resolveSealgwBaseUrl as m, DEFAULT_SEALGW_TIMEOUT_MS as n, _resetSealgwModelCacheForTesting as o, resolveSealgwAuth as p, SEALGW_DISPLAY_DEFAULT_MODEL as r, downloadSealgwAsset as s, DEFAULT_SEALGW_BASE_URL as t, isSealgwAuthConfigured as u };
