openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
195 lines (194 loc) • 7.8 kB
JavaScript
import { l as normalizeOptionalString } from "./string-coerce-6kR-86XC.mjs";
import { s as asFiniteNumber } from "./number-coercion-Dq1l5muL.mjs";
import { c as isRecord } from "./record-coerce-TIsquoVR.mjs";
import { d as normalizeStringEntries } from "./string-normalization-YMwDjufr.mjs";
import { t as asBoolean } from "./boolean-cOv0ta_w.mjs";
import { o as redactSensitiveUrlLikeString } from "./redact-sensitive-url-BZmo2ydO.mjs";
import "./utils-DUAwk2p5.mjs";
//#region packages/net-policy/src/url-userinfo.ts
/** Strip username/password credentials from a URL string when it parses. */
function stripUrlUserInfo(value) {
try {
const parsed = new URL(value);
if (!parsed.username && !parsed.password) return value;
parsed.username = "";
parsed.password = "";
return parsed.toString();
} catch {
return value;
}
}
//#endregion
//#region src/channels/account-snapshot-fields.ts
const CREDENTIAL_STATUS_KEYS = [
"tokenStatus",
"botTokenStatus",
"appTokenStatus",
"signingSecretStatus",
"userTokenStatus"
];
const CREDENTIAL_SOURCE_KEYS = [
"tokenSource",
"botTokenSource",
"appTokenSource",
"signingSecretSource",
"userTokenSource",
"credentialSource",
"secretSource"
];
/** Redacts a plugin-provided base URL after status hooks have produced their final record. */
function redactChannelStatusSummaryBaseUrl(summary) {
if (!isRecord(summary) || typeof summary.baseUrl !== "string" || !summary.baseUrl) return summary;
const redactedBaseUrl = stripUrlUserInfo(redactSensitiveUrlLikeString(summary.baseUrl));
return redactedBaseUrl === summary.baseUrl ? summary : {
...summary,
baseUrl: redactedBaseUrl
};
}
/** Redacts a plugin-provided base URL at the public account-snapshot boundary. */
function redactChannelAccountSnapshotBaseUrl(snapshot) {
return redactChannelStatusSummaryBaseUrl(snapshot);
}
function readNullableNumber(record, key) {
return record[key] === null ? null : asFiniteNumber(record[key]);
}
function setSnapshotField(snapshot, key, value) {
if (value !== void 0) snapshot[key] = value;
}
function readStringArray(record, key) {
const value = record[key];
if (!Array.isArray(value)) return;
const normalized = normalizeStringEntries(value.map((entry) => typeof entry === "string" || typeof entry === "number" ? entry : ""));
return normalized.length > 0 ? normalized : void 0;
}
function readCredentialStatus(record, key) {
const value = record[key];
return value === "available" || value === "configured_unavailable" || value === "missing" ? value : void 0;
}
/** Status inspection must not redeem credentials to detect configured-but-unavailable accounts. */
function resolveConfiguredFromCredentialStatuses(account) {
const record = isRecord(account) ? account : null;
if (!record) return;
let sawCredentialStatus = false;
for (const key of CREDENTIAL_STATUS_KEYS) {
const status = readCredentialStatus(record, key);
if (!status) continue;
sawCredentialStatus = true;
if (status !== "missing") return true;
}
return sawCredentialStatus ? false : void 0;
}
/** Infers configured state only from the credential status keys required by a channel. */
function resolveConfiguredFromRequiredCredentialStatuses(account, requiredKeys) {
const record = isRecord(account) ? account : null;
if (!record) return;
let sawCredentialStatus = false;
for (const key of requiredKeys) {
const status = readCredentialStatus(record, key);
if (!status) continue;
sawCredentialStatus = true;
if (status === "missing") return false;
}
return sawCredentialStatus ? true : void 0;
}
/** Returns true when a credential exists but cannot be resolved at status-render time. */
function hasConfiguredUnavailableCredentialStatus(account) {
const record = isRecord(account) ? account : null;
if (!record) return false;
return CREDENTIAL_STATUS_KEYS.some((key) => readCredentialStatus(record, key) === "configured_unavailable");
}
/** Reads typed, redacted credential diagnostics from a resolved channel account. */
function getCredentialUnavailableDiagnostics(account) {
const record = isRecord(account) ? account : null;
if (!record || !Array.isArray(record.credentialDiagnostics)) return [];
const diagnostics = [];
for (const value of record.credentialDiagnostics) {
if (!isRecord(value) || value.code !== "CREDENTIAL_FILE_UNAVAILABLE") continue;
const path = normalizeOptionalString(value.path);
const reason = normalizeOptionalString(value.reason);
if (path && reason) diagnostics.push({
code: value.code,
path,
reason
});
}
return diagnostics;
}
/** Returns true when account data contains a resolved credential value or available status. */
function hasResolvedCredentialValue(account) {
const record = isRecord(account) ? account : null;
if (!record) return false;
return [
"token",
"botToken",
"appToken",
"signingSecret",
"userToken"
].some((key) => {
return normalizeOptionalString(record[key]) !== void 0;
}) || CREDENTIAL_STATUS_KEYS.some((key) => readCredentialStatus(record, key) === "available");
}
/** Projects credential source/status metadata while omitting raw credential values. */
function projectCredentialSnapshotFields(account) {
const record = isRecord(account) ? account : null;
if (!record) return {};
const snapshot = {};
for (const key of CREDENTIAL_SOURCE_KEYS) setSnapshotField(snapshot, key, normalizeOptionalString(record[key]));
for (const key of CREDENTIAL_STATUS_KEYS) setSnapshotField(snapshot, key, readCredentialStatus(record, key));
return snapshot;
}
/** Explicitly allowlist status fields so new account fields cannot expose credentials. */
function projectSafeChannelAccountSnapshotFields(account) {
const record = isRecord(account) ? account : null;
if (!record) return {};
const snapshot = {};
for (const key of [
"name",
"statusState",
"healthState",
"mode",
"dmPolicy",
"identity",
"audienceType",
"webhookPath",
"cliPath",
"dbPath"
]) setSnapshotField(snapshot, key, normalizeOptionalString(record[key]));
for (const key of [
"linked",
"running",
"connected",
"restartPending",
"terminalDisconnect",
"busy",
"allowUnmentionedGroups"
]) setSnapshotField(snapshot, key, asBoolean(record[key]));
for (const key of [
"reconnectAttempts",
"lastInboundAt",
"lastTransportActivityAt",
"activeRuns",
"port"
]) setSnapshotField(snapshot, key, asFiniteNumber(record[key]));
for (const key of [
"lastConnectedAt",
"lastOutboundAt",
"lastMessageAt",
"lastEventAt",
"lastRunActivityAt",
"activeRunStartedAt"
]) setSnapshotField(snapshot, key, readNullableNumber(record, key));
const lifecycle = record.lifecycle;
if (lifecycle === "starting" || lifecycle === "ready" || lifecycle === "recovering" || lifecycle === "blocked" || lifecycle === "stopped") snapshot.lifecycle = lifecycle;
if (asBoolean(record.ingressUnavailable) === true) snapshot.ingressUnavailable = true;
setSnapshotField(snapshot, "allowFrom", readStringArray(record, "allowFrom"));
Object.assign(snapshot, projectCredentialSnapshotFields(account));
setSnapshotField(snapshot, "apiCredentialStatus", readCredentialStatus(record, "apiCredentialStatus"));
for (const key of ["baseUrl", "audience"]) {
const value = normalizeOptionalString(record[key]);
if (value) snapshot[key] = stripUrlUserInfo(redactSensitiveUrlLikeString(value));
}
return snapshot;
}
//#endregion
export { projectCredentialSnapshotFields as a, redactChannelStatusSummaryBaseUrl as c, hasResolvedCredentialValue as i, resolveConfiguredFromCredentialStatuses as l, getCredentialUnavailableDiagnostics as n, projectSafeChannelAccountSnapshotFields as o, hasConfiguredUnavailableCredentialStatus as r, redactChannelAccountSnapshotBaseUrl as s, CREDENTIAL_STATUS_KEYS as t, resolveConfiguredFromRequiredCredentialStatuses as u };