Files
pikasTech-HWLAB/web/hwlab-cloud-web/app-helpers.ts
T
2026-05-31 11:45:10 +08:00

634 lines
26 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
function traceEventMeta(event) {
return [
event.toolName ? `tool=${event.toolName}` : null,
event.waitingFor ? `waiting=${event.waitingFor}` : null,
typeof event.timeoutMs === "number" ? `timeoutMs=${event.timeoutMs}` : null,
typeof event.hardTimeoutMs === "number" ? `hardTimeoutMs=${event.hardTimeoutMs}` : null,
typeof event.idleMs === "number" ? `idleMs=${event.idleMs}` : null,
event.lastActivityAt ? `lastActivityAt=${event.lastActivityAt}` : null,
typeof event.elapsedMs === "number" ? `${event.elapsedMs}ms` : null,
event.errorCode ? `error=${event.errorCode}` : null,
event.outputSummary,
event.message
].filter(Boolean).join(" / ");
}
function toolCallsSummary(toolCalls) {
const summary = compactToolCallsSummary(toolCalls);
return summary === "none" ? null : summary;
}
function skillsSummary(skills) {
const summary = compactSkillsSummary(skills);
return summary === "none" ? null : summary;
}
function runnerTraceSummary(runnerTrace) {
const summary = compactRunnerTraceSummary(runnerTrace);
return summary === "none" ? null : summary;
}
function conversationFactsSummary(conversationFacts) {
if (!conversationFacts || typeof conversationFacts !== "object") return "none";
return [
recordField("turns", conversationFacts.turnCount),
recordField("sessionId", conversationFacts.sessionId),
recordField("status", conversationFacts.sessionStatus),
recordField("workspace", conversationFacts.workspace),
recordField("mode", conversationFacts.sessionMode),
conversationFacts.valuesRedacted === true ? "valuesRedacted=true" : null
].filter(Boolean).join(" / ") || "none";
}
function conversationFactTracesSummary(conversationFacts) {
if (!conversationFacts || typeof conversationFacts !== "object") return "none";
const traces = Array.isArray(conversationFacts.traceIds) ? conversationFacts.traceIds.filter(Boolean) : [];
return traces.length ? traces.slice(-4).join(",") : conversationFacts.latestTraceId ?? "none";
}
function conversationFactSkillsSummary(conversationFacts) {
const skills = conversationFacts?.latestSkills;
if (!skills || typeof skills !== "object") return "none";
const names = Array.isArray(skills.names) ? skills.names.filter(Boolean).slice(0, 6).join(",") : "";
return [
skills.status ?? "unknown",
recordField("count", skills.totalCount ?? skills.count),
names ? `items=${names}` : null
].filter(Boolean).join(":") || "none";
}
function conversationFactToolsSummary(conversationFacts) {
const toolCalls = Array.isArray(conversationFacts?.recentToolCalls) ? conversationFacts.recentToolCalls : [];
if (toolCalls.length === 0) return "none";
return toolCalls
.map((toolCall) => [toolCall.name ?? "tool", toolCall.status ? `(${toolCall.status})` : null].filter(Boolean).join(""))
.slice(-5)
.join(",");
}
function sessionSummary(session) {
if (!session || typeof session !== "object") return null;
const parts = [
session.sessionId,
session.status,
typeof session.turn === "number" ? `turn${session.turn}` : null,
session.threadId ? `thread=${session.threadId}` : null,
session.lastTraceId ? `lastTrace=${session.lastTraceId}` : null
].filter(Boolean);
return parts.join(":");
}
function statusCard(item) {
const article = document.createElement("article");
article.className = "status-card";
article.append(textSpan(item.title, "metric-label"));
article.append(textSpan(String(item.value), `status-value tone-${toneClass(item.tone)}`));
article.append(textSpan(item.meta, "status-meta"));
return article;
}
function infoCard(item) {
const article = document.createElement("article");
article.className = "info-card";
article.append(badge(statusLabel(item.tone), item.tone));
const body = document.createElement("div");
body.append(textSpan(item.title, "info-title"));
body.append(textSpan(item.detail, "info-detail"));
article.append(body);
return article;
}
function badge(text, tone = text) {
const span = document.createElement("span");
span.className = `badge tone-${toneClass(tone)}`;
span.textContent = text ?? "无";
return span;
}
function copyButton(value, label = "value") {
const button = document.createElement("button");
button.className = "copy-chip";
button.type = "button";
button.textContent = "复制";
button.title = `复制 ${label}`;
button.addEventListener("click", async () => {
await copyTextToClipboard(value);
button.textContent = "已复制";
window.setTimeout(() => {
button.textContent = "复制";
}, 1200);
});
return button;
}
async function copyTextToClipboard(value) {
const text = String(value ?? "");
if (navigator.clipboard?.writeText) {
await navigator.clipboard.writeText(text);
return;
}
const input = document.createElement("textarea");
input.value = text;
input.setAttribute("readonly", "");
input.style.position = "fixed";
input.style.left = "-9999px";
document.body.append(input);
input.select();
document.execCommand("copy");
input.remove();
}
function safeLink(href, text, className) {
const anchor = document.createElement("a");
anchor.href = href;
anchor.textContent = text ?? href;
anchor.rel = "noreferrer";
if (className) anchor.className = className;
return anchor;
}
function textSpan(text, className) {
const span = document.createElement("span");
span.textContent = text ?? "无";
if (className) span.className = className;
return span;
}
function cell(text, className) {
const td = document.createElement("td");
td.textContent = text ?? "无";
if (className) td.className = className;
return td;
}
function formatTimestamp(value) {
const date = new Date(value);
if (Number.isNaN(date.getTime())) return "未知";
return shortTime(date.toISOString());
}
function tableCell(text, className, colspan = 1) {
const td = cell(text, className);
if (colspan > 1) td.colSpan = colspan;
return td;
}
function runtimeSummaryFrom(live) {
return live.health.data?.runtime ?? live.healthLive.data?.runtime ?? live.adapter.data?.runtime ?? live.restIndex.data?.runtime ?? null;
}
function messageFromPayload(payload) {
if (!payload || typeof payload !== "object") return "非 JSON 或空响应";
if (payload.error && typeof payload.error === "object") {
return payload.error.message || payload.error.code || payload.reason || payload.message || JSON.stringify(payload.error).slice(0, 160);
}
if (typeof payload.error === "string") {
return payload.message ? `${payload.error}${payload.message}` : payload.error;
}
return payload.code || payload.message || payload.reason || JSON.stringify(payload).slice(0, 160);
}
function normalizeBlockedAgentResult(payload, httpStatus, traceId, fallbackMessage) {
const { reply, ...safePayload } = payload;
const payloadError = payload.error && typeof payload.error === "object" ? payload.error : {};
const payloadErrorCode = payloadError.code ?? (typeof payload.error === "string" ? payload.error : null);
const providerStatus = payloadError.providerStatus ?? httpStatus;
const error = {
...payloadError,
code: payloadErrorCode ?? "provider_unavailable",
message: payloadError.message ?? fallbackMessage ?? `Code Agent provider HTTP ${httpStatus}`,
providerStatus
};
return {
...safePayload,
status: "failed",
traceId: payload.traceId || traceId,
error,
availability: payload.availability ?? codeAgentAvailabilityFromResult({ error })
};
}
function isBlockedAgentResponse(payload, httpStatus) {
return Boolean(
payload &&
typeof payload === "object" &&
(
isHttpNon2xxStatus(httpStatus) ||
isHttpNon2xxStatus(payload.error?.providerStatus) ||
payload.status === "blocked" ||
payload.error?.code === "provider_unavailable" ||
payload.error?.code === "security_blocked" ||
payload.error?.code === "tool_unavailable" ||
payload.error?.code === "skills_unavailable" ||
payload.availability?.status === "blocked"
)
);
}
function isHttpNon2xxStatus(value) {
return typeof value === "number" && Number.isInteger(value) && (value < 200 || value > 299);
}
function codeAgentAvailabilityFrom(live) {
return sanitizeCodeAgentAvailability(codeAgentAvailabilityFromLive(live));
}
function codeAgentAvailabilityFromResult(result) {
const missingEnv = Array.isArray(result?.error?.missingEnv) ? result.error.missingEnv : [];
const providerStatus = result?.error?.providerStatus;
const credentialBlocked = missingEnv.includes("OPENAI_API_KEY");
return {
status: "blocked",
blocker: credentialBlocked ? "凭证缺口" : "provider_unavailable",
reason: result?.error?.code ?? "provider_unavailable",
summary: providerStatus
? `真实后端已接入,但当前 provider 上游返回 HTTP ${providerStatus};本次保持 BLOCKED/provider_unavailable,不会冒充真实 Code Agent 回复。`
: "真实后端已接入,但当前 DEV provider Secret hwlab-code-agent-provider/openai-api-key 未注入,因此不能返回真实回复。",
missingEnv,
secretRefs: credentialBlocked ? [
{
env: "OPENAI_API_KEY",
secretName: "hwlab-code-agent-provider",
secretKey: "openai-api-key",
redacted: true
}
] : []
};
}
function sanitizeCodeAgentAvailability(availability) {
if (!availability || typeof availability !== "object") return availability;
if (availability.status !== "blocked") return availability;
return {
...availability,
blocker: "服务受阻",
summary: codeAgentBlockedSummary(availability)
};
}
function codeAgentStatusMessage(availability) {
if (availability?.longLivedSessionGate?.status === "pass" && availability?.codexStdioFeasibility?.canStartLongLivedCodexStdio === true) {
return {
role: "system",
title: "Code Agent 状态:Codex stdio 长会话可用",
text: "当前通道可创建和复用 repo-owned Codex stdio session,并暴露 cancel、reap、idle timeout 与 trace capture;硬件控制仍只走 cloud-api/HWLAB API/skill CLI。",
status: "source"
};
}
if (availability?.runner?.ready === true) {
return {
role: "system",
title: "Code Agent 状态:Codex stdio gate 未通过",
text: `同源服务可响应,但完整 Codex stdio 长会话仍未通过 gate;不会用本地技能发现、shell/file 快捷命令或文本 fallback 冒充回复。${codeAgentBlockerDetail(availability)}`,
status: "blocked"
};
}
if (availability?.status === "blocked") {
return {
role: "system",
title: "Code Agent 状态:服务受阻",
text: codeAgentBlockedSummary(availability),
status: "blocked"
};
}
if (String(availability?.sourceKind ?? availability?.evidenceLevel ?? "").toUpperCase() === "SOURCE") {
return {
role: "system",
title: "Code Agent 状态:本地验证记录",
text: "当前只用于本地浏览器验证:可以检查发送、处理中、回复渲染和证据展示,但不会标记为实况完成。",
status: "source"
};
}
if (availability?.status === "available") {
return {
role: "system",
title: "Code Agent 状态:真实通道已接入",
text: "受控对话通道已接入;发送后只有真实完成的回复才会标记为已完成。",
status: "source"
};
}
return {
role: "system",
title: "Code Agent 状态:等待 Codex stdio 探测",
text: "正在确认服务状态;首屏不会宣称 Code Agent 可用,也不会把失败或静态内容当成真实回复。",
status: "source"
};
}
function codeAgentPromptText(availability) {
if (availability?.status === "blocked") {
return "可以继续输入并保留草稿;发送会走受控对话通道。服务恢复前会显示结构化失败,不会冒充真实 Codex 回复。";
}
return "请在底部输入中文消息并点击发送;完成态只来自真实完成的后端回复。";
}
function codeAgentControlSummary(availability) {
if (latestCompletedAgentMessage()) {
return state.conversationId
? `当前会话 ${state.conversationId};最近一次真实完成回复来自受控 Code Agent 接口,并带有可核验记录。`
: "最近一次真实完成回复来自受控 Code Agent 接口,并带有可核验记录。";
}
if (isSourceFixtureCompletedChatMessage(latestChatResult())) {
return "最近一次为本地验证回复;它只证明浏览器对话体验,不作为实况完成证据。";
}
if (availability?.status === "blocked") {
return codeAgentBlockedSummary(availability);
}
if (availability?.partialReady === true || availability?.runner?.ready === true) {
return `输入区会调用受控接口;完整 Codex stdio 长会话尚未可用时只返回结构化失败,不能用本地 shortcut 或 text fallback 冒充回复。${codeAgentBlockerDetail(availability)}`;
}
return "输入区会调用受控 Code Agent 接口;只有真实完成回复才显示为完成,不能因为只有会话编号就当成实况完成。";
}
function codeAgentBlockedSummary(availability) {
const structured = availability?.error?.userMessage ?? availability?.blocker?.userMessage;
if (structured) return structured;
const providerStatus = availability?.error?.providerStatus ?? availability?.providerStatus;
if (providerStatus) {
return `真实后端已接入,但当前上游响应 HTTP ${providerStatus};工作台保持只读和可重试,不会冒充真实 Code Agent 回复。`;
}
const blocker = codeAgentBlockerDetail(availability);
return `真实后端已接入,但 Code Agent 服务暂不可用;完整 Codex stdio Code Agent 仍受阻,工作台保持只读和可重试,不会冒充真实回复。${blocker}`;
}
function codeAgentBlockerDetail(availability) {
const blockers = codeAgentBlockerCodes(availability);
if (blockers.length === 0 && availability?.reason) blockers.push(availability.reason);
if (blockers.length === 0) return "";
const labels = blockers.slice(0, 3).map(codeAgentBlockerChineseLabel);
return ` 阻塞:${labels.join("")}。证据 code=${blockers.slice(0, 3).join(",")}。`;
}
function codeAgentBlockerCodes(availability) {
const values = [
...(Array.isArray(availability?.blockers) ? availability.blockers.map((blocker) => blocker?.code) : []),
...(Array.isArray(availability?.blockerCodes) ? availability.blockerCodes : []),
...(Array.isArray(availability?.longLivedSessionGate?.blockers) ? availability.longLivedSessionGate.blockers.map((blocker) => blocker?.code) : []),
...(Array.isArray(availability?.codexStdioFeasibility?.blockers) ? availability.codexStdioFeasibility.blockers.map((blocker) => blocker?.code) : []),
...(Array.isArray(availability?.runtimeContract?.stdioProtocol?.missingTools) && availability.runtimeContract.stdioProtocol.missingTools.length > 0 ? ["stdio_protocol_not_wired"] : []),
availability?.runtimeContract?.binary?.status === "missing" ? "codex_cli_binary_missing" : null,
availability?.runtimeContract?.lifecycleSupervisor?.status === "blocked" ? "runner_lifecycle_missing" : null
].filter(Boolean).map((code) => String(code));
return [...new Set(values)];
}
function codeAgentBlockerChineseLabel(code) {
return {
codex_cli_binary_missing: "未找到受控 Codex CLI binary",
codex_cli_not_executable: "Codex CLI 无法执行 --version",
codex_cli_native_dependency_missing: "Codex CLI native 依赖缺失",
runner_lifecycle_missing: "缺少 repo-owned lifecycle supervisor",
stdio_protocol_not_wired: "Codex stdio 协议尚未接入",
codex_stdio_supervisor_disabled: "Codex stdio supervisor 未启用",
codex_stdio_blocked_readonly_session_available: "旧 partial runner 不能作为 Codex stdio",
controlled_readonly_not_long_lived_stdio: "非 Codex stdio 长会话",
openai_responses_fallback_not_session: "文本 fallback 不是长会话",
one_shot_runner_not_long_lived: "一次性执行不是可复用 session",
provider_token_boundary: "token 边界未配置",
codex_home_missing: "CODEX_HOME 不存在或不可读",
codex_home_write_blocked: "CODEX_HOME 不可写",
workspace_mount_missing: "工作区挂载不可读",
workspace_write_boundary_blocked: "workspace-write 沙箱不可写",
codex_stdio_egress_boundary: "DEV egress 边界不合规"
}[code] ?? code;
}
function untrustedCompletionMessage(result) {
const provider = result?.provider ?? "unknown";
return `Code Agent 返回 completed,但缺少真实 provider/model/trace/conversation 证据,或 provider=${provider} 属于 echo/mock/stub;本次不会标记为真实完成。SOURCE fixture 只可显示为 SOURCE 回复,不能冒充 DEV-LIVE。`;
}
function codeAgentAvailabilitySummary() {
if (state.codeAgentAvailability?.longLivedSessionGate?.status === "pass" && state.codeAgentAvailability?.codexStdioFeasibility?.canStartLongLivedCodexStdio === true) {
return "同源 API 可响应;Codex stdio 长会话 gate 已通过,支持真实 session 复用和 trace capture。";
}
if (state.codeAgentAvailability?.runner?.ready === true) {
return `同源 API 可响应;完整 Codex stdio 长会话仍受阻,本地 shortcut 与文本 fallback 不会冒充真实 Code Agent 回复。${codeAgentBlockerDetail(state.codeAgentAvailability)}`;
}
if (state.codeAgentAvailability?.status === "blocked") {
return "同源 API 可响应;Code Agent 服务暂不可用,工作台保持只读和可重试。";
}
if (String(state.codeAgentAvailability?.sourceKind ?? state.codeAgentAvailability?.evidenceLevel ?? "").toUpperCase() === "SOURCE") {
return "同源 API 可响应;当前 Code Agent 回复来源是本地验证记录,仅用于浏览器验证,不是实况完成。";
}
if (state.codeAgentAvailability?.status === "available") {
return "同源 API 可响应;Code Agent 真实通道已接入,只有真实完成回复才显示为完成。";
}
return "同源接口可响应。技术复核详情已放在二级页面。";
}
function latestCompletedAgentMessage() {
return [...state.chatMessages].reverse().find((message) =>
message.role === "agent" &&
isRealCompletedChatMessage(message)
) ?? null;
}
function isRealCompletedChatResult(result) {
const assistantReply = typeof result?.reply?.content === "string" ? result.reply.content.trim() : "";
return result?.status === "completed" && assistantReply.length > 0 && !isSourceFixtureCompletion(result) && hasRealCodeAgentEvidence(result);
}
function isSourceFixtureChatResult(result) {
const assistantReply = typeof result?.reply?.content === "string" ? result.reply.content.trim() : "";
return result?.status === "completed" && assistantReply.length > 0 && isSourceFixtureCompletion(result);
}
function isTextFallbackChatResult(result) {
const assistantReply = typeof result?.reply?.content === "string" ? result.reply.content.trim() : "";
return result?.status === "completed" &&
assistantReply.length > 0 &&
result?.provider === "openai-responses" &&
(result?.capabilityLevel === "text-chat-only" || result?.runner?.kind === "openai-responses-fallback") &&
result?.longLivedSessionGate?.status !== "pass";
}
function isRealCompletedChatMessage(message) {
return message?.status === "completed" && !message.error && hasRealCodeAgentEvidence(message);
}
function isSourceFixtureCompletedChatMessage(message) {
return message?.status === "source" && !message.error && message.sourceKind === "SOURCE";
}
function hasRealCodeAgentEvidence(value) {
if (isCodexRunnerCapableEvidence(value)) {
return true;
}
if (CODEX_RUNNER_CAPABLE_PROVIDERS.includes(String(value?.provider ?? "").trim().toLowerCase())) {
return false;
}
if (isTextFallbackChatResult(value)) {
return false;
}
return (
isTrustedCodeAgentProvider(value?.provider) &&
Boolean(value?.model) &&
Boolean(value?.backend) &&
Boolean(value?.traceId) &&
Boolean(value?.conversationId || value?.sessionId) &&
hasProviderTrace(value) &&
Boolean(sessionIdFrom(value)) &&
(!requiresCodexThreadEvidence(value) || Boolean(threadIdFrom(value))) &&
!UNTRUSTED_CODE_AGENT_PROVIDER_PATTERN.test(`${value?.provider ?? ""} ${value?.backend ?? ""}`)
);
}
function isCodexRunnerCapableEvidence(value) {
return (
CODEX_RUNNER_CAPABLE_PROVIDERS.includes(String(value?.provider ?? "").trim().toLowerCase()) &&
value?.runner?.kind === CODEX_APP_SERVER_RUNNER_KIND &&
value?.capabilityLevel === "long-lived-codex-stdio-session" &&
value?.session?.status === "idle" &&
typeof value?.session?.idleTimeoutMs === "number" &&
Boolean(value?.session?.lastTraceId) &&
value?.sessionMode === CODEX_APP_SERVER_SESSION_MODE &&
value?.implementationType === CODEX_APP_SERVER_IMPLEMENTATION_TYPE &&
Boolean(value?.sessionReuse) &&
hasProviderTrace(value) &&
Boolean(sessionIdFrom(value)) &&
Boolean(threadIdFrom(value)) &&
value?.session?.longLivedSession === true &&
value?.session?.codexStdio === true &&
value?.runner?.codexStdio === true &&
value?.runner?.writeCapable === true &&
value?.runner?.durableSession === true &&
Boolean(value?.workspace || value?.runner?.workspace) &&
["workspace-write", "danger-full-access"].includes(value?.sandbox || value?.runner?.sandbox) &&
Array.isArray(value?.toolCalls) &&
value.toolCalls.some((tool) => tool?.status === "completed") &&
value?.longLivedSessionGate?.status === "pass" &&
value?.providerTrace?.protocol === CODEX_APP_SERVER_PROTOCOL &&
/\bcodex\s+app-server\s+--listen\s+stdio:\/\//u.test(String(value?.providerTrace?.command ?? "")) &&
["completed", "succeeded"].includes(String(value?.providerTrace?.terminalStatus ?? "").toLowerCase()) &&
Boolean(value?.runnerTrace) &&
Boolean(value?.skills)
);
}
function isTrustedCodeAgentProvider(provider) {
const normalized = String(provider ?? "").trim().toLowerCase();
return TRUSTED_CODE_AGENT_PROVIDERS.includes(normalized);
}
function isSourceFixtureCompletion(value) {
const sourceKind = String(value?.sourceKind ?? value?.evidenceLevel ?? value?.providerTrace?.sourceKind ?? "").trim().toUpperCase();
const traceSource = String(value?.providerTrace?.source ?? value?.providerTrace?.mode ?? "").trim().toUpperCase();
return sourceKind === "SOURCE" || traceSource.startsWith("SOURCE");
}
function latestChatResult() {
return [...state.chatMessages].reverse().find((message) => message.role === "agent") ?? null;
}
function deriveAgentChatStatus() {
if (state.chatPending) return "running";
const latest = latestChatResult();
if (latest?.status === "completed") return "completed";
if (latest?.status === "source") return "source";
if (latest?.status === "canceled") return "canceled";
if (latest?.status === "timeout") return "timeout";
if (latest?.status === "error") return "error";
if (latest?.status === "failed") return latest.error?.code === "provider_unavailable" ? "blocked" : "failed";
if (state.codeAgentAvailability?.status === "blocked") return "blocked";
return "idle";
}
function currentConversationTone() {
if (latestCompletedAgentMessage()) return "dev-live";
if (isSourceFixtureCompletedChatMessage(latestChatResult())) return "source";
if (state.codeAgentAvailability?.status === "blocked" || ["failed", "timeout", "canceled", "error"].includes(latestChatResult()?.status)) return "blocked";
return "source";
}
function statusLabel(value) {
const key = String(value ?? "unknown").trim();
const normalized = key.toLowerCase();
return STATUS_LABELS[normalized] ?? key;
}
function gateStatusLabel(value, statusKey) {
const explicit = String(value ?? "").trim();
const labels = {
pass: "通过",
blocked: "阻塞",
failed: "失败",
pending: "待验证",
info: "信息"
};
return labels[statusKey] ?? (["通过", "阻塞", "失败", "待验证", "信息"].includes(explicit) ? explicit : "阻塞");
}
function normalizeGateStatusKey(value) {
const text = String(value ?? "").trim().toLowerCase();
if (["通过", "pass", "passed", "ok", "ready"].includes(text)) return "pass";
if (["失败", "failed", "failure", "error"].includes(text)) return "failed";
if (["待验证", "pending", "loading", "unknown"].includes(text)) return "pending";
if (["信息", "info", "information"].includes(text)) return "info";
return "blocked";
}
function normalizeEvidenceList(value) {
const items = Array.isArray(value) ? value : [value];
const normalized = items.map(oneLine).filter(Boolean);
return normalized.length > 0 ? normalized : ["live 后端未返回 evidence 字段"];
}
function oneLine(value) {
return String(value ?? "").replace(/\s+/gu, " ").trim().slice(0, 240);
}
function roleLabel(role) {
return (
{
agent: "Agent",
blocker: "阻塞",
system: "系统",
user: "用户"
}[String(role ?? "").toLowerCase()] ?? statusLabel(role)
);
}
function recordKindLabel(kind) {
return (
{
audit: "审计",
evidence: "证据",
operation: "操作",
record: "记录",
report: "报告",
trace: "轨迹"
}[String(kind ?? "").toLowerCase()] ?? statusLabel(kind)
);
}
function internalGatePathnames() {
return new Set(["/gate", "/diagnostics/gate"]);
}
function helpPathnames() {
return new Set(["/help"]);
}
function skillsPathnames() {
return new Set(["/skills"]);
}
function valueOrUnavailable(liveValue, sourceValue) {
if (liveValue !== undefined && liveValue !== null) return liveValue;
if (sourceValue !== undefined && sourceValue !== null) return `${sourceValue} 来源 SOURCE`;
return "不可用";
}
function shortTime(value) {
return new Date(value).toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "Z");
}
function toneClass(tone) {
return String(tone ?? "unknown").replace(/[^a-z0-9_-]/gi, "-").toLowerCase();
}
function replaceChildren(parent, ...children) {
parent.replaceChildren(...children.filter((child) => child && typeof child !== "string"));
}