mirror of https://github.com/openclaw/openclaw.git
313 lines
9.2 KiB
TypeScript
313 lines
9.2 KiB
TypeScript
import type { ChannelStatusAdapter } from "../channels/plugins/types.adapters.js";
|
|
import type { ChannelAccountSnapshot } from "../channels/plugins/types.core.js";
|
|
import type { ChannelStatusIssue } from "../channels/plugins/types.js";
|
|
import type { OpenClawConfig } from "../config/config.js";
|
|
export { isRecord } from "../channels/plugins/status-issues/shared.js";
|
|
export {
|
|
appendMatchMetadata,
|
|
asString,
|
|
collectIssuesForEnabledAccounts,
|
|
formatMatchMetadata,
|
|
resolveEnabledConfiguredAccountId,
|
|
} from "../channels/plugins/status-issues/shared.js";
|
|
|
|
type RuntimeLifecycleSnapshot = {
|
|
running?: boolean | null;
|
|
lastStartAt?: number | null;
|
|
lastStopAt?: number | null;
|
|
lastError?: string | null;
|
|
lastInboundAt?: number | null;
|
|
lastOutboundAt?: number | null;
|
|
};
|
|
|
|
type StatusSnapshotExtra = Record<string, unknown>;
|
|
|
|
type ComputedAccountStatusBase = {
|
|
accountId: string;
|
|
name?: string;
|
|
enabled?: boolean;
|
|
configured?: boolean;
|
|
};
|
|
|
|
type ComputedAccountStatusAdapterParams<ResolvedAccount, Probe, Audit> = {
|
|
account: ResolvedAccount;
|
|
cfg: OpenClawConfig;
|
|
runtime?: ChannelAccountSnapshot;
|
|
probe?: Probe;
|
|
audit?: Audit;
|
|
};
|
|
|
|
type ComputedAccountStatusSnapshot<TExtra extends StatusSnapshotExtra = StatusSnapshotExtra> =
|
|
ComputedAccountStatusBase & { extra?: TExtra };
|
|
|
|
/** Create the baseline runtime snapshot shape used by channel/account status stores. */
|
|
export function createDefaultChannelRuntimeState<T extends Record<string, unknown>>(
|
|
accountId: string,
|
|
extra?: T,
|
|
): {
|
|
accountId: string;
|
|
running: false;
|
|
lastStartAt: null;
|
|
lastStopAt: null;
|
|
lastError: null;
|
|
} & T {
|
|
return {
|
|
accountId,
|
|
running: false,
|
|
lastStartAt: null,
|
|
lastStopAt: null,
|
|
lastError: null,
|
|
...(extra ?? ({} as T)),
|
|
};
|
|
}
|
|
|
|
/** Normalize a channel-level status summary so missing lifecycle fields become explicit nulls. */
|
|
export function buildBaseChannelStatusSummary<TExtra extends StatusSnapshotExtra>(
|
|
snapshot: {
|
|
configured?: boolean | null;
|
|
running?: boolean | null;
|
|
lastStartAt?: number | null;
|
|
lastStopAt?: number | null;
|
|
lastError?: string | null;
|
|
},
|
|
extra?: TExtra,
|
|
) {
|
|
return {
|
|
configured: snapshot.configured ?? false,
|
|
...(extra ?? ({} as TExtra)),
|
|
running: snapshot.running ?? false,
|
|
lastStartAt: snapshot.lastStartAt ?? null,
|
|
lastStopAt: snapshot.lastStopAt ?? null,
|
|
lastError: snapshot.lastError ?? null,
|
|
};
|
|
}
|
|
|
|
/** Extend the base summary with probe fields while preserving stable null defaults. */
|
|
export function buildProbeChannelStatusSummary<TExtra extends Record<string, unknown>>(
|
|
snapshot: {
|
|
configured?: boolean | null;
|
|
running?: boolean | null;
|
|
lastStartAt?: number | null;
|
|
lastStopAt?: number | null;
|
|
lastError?: string | null;
|
|
probe?: unknown;
|
|
lastProbeAt?: number | null;
|
|
},
|
|
extra?: TExtra,
|
|
) {
|
|
return {
|
|
...buildBaseChannelStatusSummary(snapshot, extra),
|
|
probe: snapshot.probe,
|
|
lastProbeAt: snapshot.lastProbeAt ?? null,
|
|
};
|
|
}
|
|
|
|
/** Build the standard per-account status payload from config metadata plus runtime state. */
|
|
export function buildBaseAccountStatusSnapshot<TExtra extends StatusSnapshotExtra>(
|
|
params: {
|
|
account: {
|
|
accountId: string;
|
|
name?: string;
|
|
enabled?: boolean;
|
|
configured?: boolean;
|
|
};
|
|
runtime?: RuntimeLifecycleSnapshot | null;
|
|
probe?: unknown;
|
|
},
|
|
extra?: TExtra,
|
|
) {
|
|
const { account, runtime, probe } = params;
|
|
return {
|
|
accountId: account.accountId,
|
|
name: account.name,
|
|
enabled: account.enabled,
|
|
configured: account.configured,
|
|
...buildRuntimeAccountStatusSnapshot({ runtime, probe }),
|
|
lastInboundAt: runtime?.lastInboundAt ?? null,
|
|
lastOutboundAt: runtime?.lastOutboundAt ?? null,
|
|
...(extra ?? ({} as TExtra)),
|
|
};
|
|
}
|
|
|
|
/** Convenience wrapper when the caller already has flattened account fields instead of an account object. */
|
|
export function buildComputedAccountStatusSnapshot<TExtra extends StatusSnapshotExtra>(
|
|
params: {
|
|
accountId: string;
|
|
name?: string;
|
|
enabled?: boolean;
|
|
configured?: boolean;
|
|
runtime?: RuntimeLifecycleSnapshot | null;
|
|
probe?: unknown;
|
|
},
|
|
extra?: TExtra,
|
|
) {
|
|
const { accountId, name, enabled, configured, runtime, probe } = params;
|
|
return buildBaseAccountStatusSnapshot(
|
|
{
|
|
account: {
|
|
accountId,
|
|
name,
|
|
enabled,
|
|
configured,
|
|
},
|
|
runtime,
|
|
probe,
|
|
},
|
|
extra,
|
|
);
|
|
}
|
|
|
|
/** Build a full status adapter when only configured/extras vary per account. */
|
|
export function createComputedAccountStatusAdapter<
|
|
ResolvedAccount,
|
|
Probe = unknown,
|
|
Audit = unknown,
|
|
TExtra extends StatusSnapshotExtra = StatusSnapshotExtra,
|
|
>(
|
|
options: Omit<ChannelStatusAdapter<ResolvedAccount, Probe, Audit>, "buildAccountSnapshot"> & {
|
|
resolveAccountSnapshot: (
|
|
params: ComputedAccountStatusAdapterParams<ResolvedAccount, Probe, Audit>,
|
|
) => ComputedAccountStatusSnapshot<TExtra>;
|
|
},
|
|
): ChannelStatusAdapter<ResolvedAccount, Probe, Audit> {
|
|
return {
|
|
defaultRuntime: options.defaultRuntime,
|
|
buildChannelSummary: options.buildChannelSummary,
|
|
probeAccount: options.probeAccount,
|
|
formatCapabilitiesProbe: options.formatCapabilitiesProbe,
|
|
auditAccount: options.auditAccount,
|
|
buildCapabilitiesDiagnostics: options.buildCapabilitiesDiagnostics,
|
|
logSelfId: options.logSelfId,
|
|
resolveAccountState: options.resolveAccountState,
|
|
collectStatusIssues: options.collectStatusIssues,
|
|
buildAccountSnapshot: (params) => {
|
|
const typedParams = params as ComputedAccountStatusAdapterParams<
|
|
ResolvedAccount,
|
|
Probe,
|
|
Audit
|
|
>;
|
|
const { extra, ...snapshot } = options.resolveAccountSnapshot(typedParams);
|
|
return buildComputedAccountStatusSnapshot(
|
|
{
|
|
...snapshot,
|
|
runtime: typedParams.runtime,
|
|
probe: typedParams.probe,
|
|
},
|
|
extra,
|
|
);
|
|
},
|
|
};
|
|
}
|
|
|
|
/** Async variant for channels that compute configured state or snapshot extras from I/O. */
|
|
export function createAsyncComputedAccountStatusAdapter<
|
|
ResolvedAccount,
|
|
Probe = unknown,
|
|
Audit = unknown,
|
|
TExtra extends StatusSnapshotExtra = StatusSnapshotExtra,
|
|
>(
|
|
options: Omit<ChannelStatusAdapter<ResolvedAccount, Probe, Audit>, "buildAccountSnapshot"> & {
|
|
resolveAccountSnapshot: (
|
|
params: ComputedAccountStatusAdapterParams<ResolvedAccount, Probe, Audit>,
|
|
) => Promise<ComputedAccountStatusSnapshot<TExtra>>;
|
|
},
|
|
): ChannelStatusAdapter<ResolvedAccount, Probe, Audit> {
|
|
return {
|
|
defaultRuntime: options.defaultRuntime,
|
|
buildChannelSummary: options.buildChannelSummary,
|
|
probeAccount: options.probeAccount,
|
|
formatCapabilitiesProbe: options.formatCapabilitiesProbe,
|
|
auditAccount: options.auditAccount,
|
|
buildCapabilitiesDiagnostics: options.buildCapabilitiesDiagnostics,
|
|
logSelfId: options.logSelfId,
|
|
resolveAccountState: options.resolveAccountState,
|
|
collectStatusIssues: options.collectStatusIssues,
|
|
buildAccountSnapshot: async (params) => {
|
|
const typedParams = params as ComputedAccountStatusAdapterParams<
|
|
ResolvedAccount,
|
|
Probe,
|
|
Audit
|
|
>;
|
|
const { extra, ...snapshot } = await options.resolveAccountSnapshot(typedParams);
|
|
return buildComputedAccountStatusSnapshot(
|
|
{
|
|
...snapshot,
|
|
runtime: typedParams.runtime,
|
|
probe: typedParams.probe,
|
|
},
|
|
extra,
|
|
);
|
|
},
|
|
};
|
|
}
|
|
|
|
/** Normalize runtime-only account state into the shared status snapshot fields. */
|
|
export function buildRuntimeAccountStatusSnapshot<TExtra extends StatusSnapshotExtra>(
|
|
params: {
|
|
runtime?: RuntimeLifecycleSnapshot | null;
|
|
probe?: unknown;
|
|
},
|
|
extra?: TExtra,
|
|
) {
|
|
const { runtime, probe } = params;
|
|
return {
|
|
running: runtime?.running ?? false,
|
|
lastStartAt: runtime?.lastStartAt ?? null,
|
|
lastStopAt: runtime?.lastStopAt ?? null,
|
|
lastError: runtime?.lastError ?? null,
|
|
probe,
|
|
...(extra ?? ({} as TExtra)),
|
|
};
|
|
}
|
|
|
|
/** Build token-based channel status summaries with optional mode reporting. */
|
|
export function buildTokenChannelStatusSummary(
|
|
snapshot: {
|
|
configured?: boolean | null;
|
|
tokenSource?: string | null;
|
|
running?: boolean | null;
|
|
mode?: string | null;
|
|
lastStartAt?: number | null;
|
|
lastStopAt?: number | null;
|
|
lastError?: string | null;
|
|
probe?: unknown;
|
|
lastProbeAt?: number | null;
|
|
},
|
|
opts?: { includeMode?: boolean },
|
|
) {
|
|
const base = {
|
|
...buildBaseChannelStatusSummary(snapshot),
|
|
tokenSource: snapshot.tokenSource ?? "none",
|
|
probe: snapshot.probe,
|
|
lastProbeAt: snapshot.lastProbeAt ?? null,
|
|
};
|
|
if (opts?.includeMode === false) {
|
|
return base;
|
|
}
|
|
return {
|
|
...base,
|
|
mode: snapshot.mode ?? null,
|
|
};
|
|
}
|
|
|
|
/** Convert account runtime errors into the generic channel status issue format. */
|
|
export function collectStatusIssuesFromLastError(
|
|
channel: string,
|
|
accounts: Array<{ accountId: string; lastError?: unknown }>,
|
|
): ChannelStatusIssue[] {
|
|
return accounts.flatMap((account) => {
|
|
const lastError = typeof account.lastError === "string" ? account.lastError.trim() : "";
|
|
if (!lastError) {
|
|
return [];
|
|
}
|
|
return [
|
|
{
|
|
channel,
|
|
accountId: account.accountId,
|
|
kind: "runtime",
|
|
message: `Channel error: ${lastError}`,
|
|
},
|
|
];
|
|
});
|
|
}
|