fix(ci): restore runtime-api guardrails

This commit is contained in:
Peter Steinberger
2026-03-27 15:56:17 +00:00
parent df5b9ef0c6
commit 351a931a62
18 changed files with 91 additions and 66 deletions

View File

@@ -1,7 +1,7 @@
import { describe, expect, it } from "vitest";
import { discordMonitorTesting } from "../../../../extensions/discord/runtime-api.js";
import { imessageMonitorTesting } from "../../../../extensions/imessage/runtime-api.js";
import { slackMonitorTesting } from "../../../../extensions/slack/runtime-api.js";
import { __testing as discordMonitorTesting } from "../../../../extensions/discord/src/monitor/provider.js";
import { __testing as imessageMonitorTesting } from "../../../../extensions/imessage/src/monitor/monitor-provider.js";
import { __testing as slackMonitorTesting } from "../../../../extensions/slack/src/monitor/provider.js";
import { resolveTelegramRuntimeGroupPolicy } from "../../../../extensions/telegram/runtime-api.js";
import { whatsappAccessControlTesting } from "../../../../extensions/whatsapp/api.js";
import {

View File

@@ -2,7 +2,7 @@ import { beforeEach, describe } from "vitest";
import { __testing as discordThreadBindingTesting } from "../../../../extensions/discord/runtime-api.js";
import { feishuThreadBindingTesting } from "../../../../extensions/feishu/api.js";
import { resetMatrixThreadBindingsForTests } from "../../../../extensions/matrix/api.js";
import { telegramThreadBindingTesting } from "../../../../extensions/telegram/runtime-api.js";
import { __testing as telegramThreadBindingTesting } from "../../../../extensions/telegram/src/thread-bindings.js";
import { __testing as sessionBindingTesting } from "../../../infra/outbound/session-binding-service.js";
import {
actionContractRegistry,

View File

@@ -1,8 +1,6 @@
import { describe, expect, it } from "vitest";
import { discordSessionBindingAdapterChannels } from "../../../../extensions/discord/runtime-api.js";
import { feishuSessionBindingAdapterChannels } from "../../../../extensions/feishu/api.js";
import { matrixSessionBindingAdapterChannels } from "../../../../extensions/matrix/api.js";
import { telegramSessionBindingAdapterChannels } from "../../../../extensions/telegram/runtime-api.js";
import { sessionBindingContractChannelIds } from "./manifest.js";
function discoverSessionBindingChannels() {
@@ -11,11 +9,13 @@ function discoverSessionBindingChannels() {
...discordSessionBindingAdapterChannels,
...feishuSessionBindingAdapterChannels,
...matrixSessionBindingAdapterChannels,
...telegramSessionBindingAdapterChannels,
"telegram",
]),
].toSorted();
}
const discordSessionBindingAdapterChannels = ["discord"] as const;
describe("channel contract registry", () => {
it("keeps session binding coverage aligned with registered session binding adapters", () => {
expect([...sessionBindingContractChannelIds]).toEqual(discoverSessionBindingChannels());

View File

@@ -83,7 +83,7 @@ export {
export {
setMatrixThreadBindingIdleTimeoutBySessionKey,
setMatrixThreadBindingMaxAgeBySessionKey,
} from "../../extensions/matrix/runtime-api.js";
} from "../../extensions/matrix/thread-bindings-runtime.js";
export { createTypingCallbacks } from "../channels/typing.js";
export { createChannelReplyPipeline } from "./channel-reply-pipeline.js";
export type { OpenClawConfig } from "../config/config.js";
@@ -163,7 +163,7 @@ export {
resolveMatrixCredentialsPath,
resolveMatrixLegacyFlatStoragePaths,
} from "../../extensions/matrix/helper-api.js";
export { resolveMatrixAccountStringValues } from "../../extensions/matrix/runtime-api.js";
export { resolveMatrixAccountStringValues } from "../../extensions/matrix/src/auth-precedence.js";
export { getMatrixScopedEnvVarNames } from "../../extensions/matrix/helper-api.js";
export {
requiresExplicitMatrixDefaultAccount,
@@ -173,7 +173,7 @@ export {
createMatrixThreadBindingManager,
resetMatrixThreadBindingsForTests,
} from "../../extensions/matrix/api.js";
export { setMatrixRuntime } from "../../extensions/matrix/runtime-api.js";
export { setMatrixRuntime } from "../../extensions/matrix/src/runtime.js";
const matrixSetup = createOptionalChannelSetupSurface({
channel: "matrix",

View File

@@ -42,6 +42,7 @@ const RUNTIME_API_EXPORT_GUARDS: Record<string, readonly string[]> = {
'export * from "./helper-api.js";',
'export { assertHttpUrlTargetsPrivateNetwork, closeDispatcher, createPinnedDispatcher, resolvePinnedHostnameWithPolicy, ssrfPolicyFromAllowPrivateNetwork, type LookupFn, type SsrFPolicy } from "openclaw/plugin-sdk/ssrf-runtime";',
'export { setMatrixThreadBindingIdleTimeoutBySessionKey, setMatrixThreadBindingMaxAgeBySessionKey } from "./thread-bindings-runtime.js";',
'export { setMatrixRuntime } from "./src/runtime.js";',
'export { writeJsonFileAtomically } from "openclaw/plugin-sdk/json-store";',
'export type { ChannelDirectoryEntry, ChannelMessageActionContext, OpenClawConfig, PluginRuntime, RuntimeLogger, RuntimeEnv, WizardPrompter } from "openclaw/plugin-sdk/matrix";',
'export { formatZonedTimestamp } from "openclaw/plugin-sdk/matrix";',
@@ -67,10 +68,13 @@ const RUNTIME_API_EXPORT_GUARDS: Record<string, readonly string[]> = {
'export { buildChannelConfigSchema, getChatChannelMeta, jsonResult, readNumberParam, readReactionParams, readStringArrayParam, readStringOrNumberParam, readStringParam, resolvePollMaxSelections, TelegramConfigSchema } from "openclaw/plugin-sdk/telegram-core";',
'export type { TelegramProbe } from "./src/probe.js";',
'export { auditTelegramGroupMembership, collectTelegramUnmentionedGroupIds } from "./src/audit.js";',
'export { resolveTelegramRuntimeGroupPolicy } from "./src/group-access.js";',
'export { buildTelegramExecApprovalPendingPayload, shouldSuppressTelegramExecApprovalForwardingFallback } from "./src/exec-approval-forwarding.js";',
'export { telegramMessageActions } from "./src/channel-actions.js";',
'export { monitorTelegramProvider } from "./src/monitor.js";',
'export { probeTelegram } from "./src/probe.js";',
'export { resolveTelegramFetch, resolveTelegramTransport, shouldRetryTelegramTransportFallback } from "./src/fetch.js";',
'export { makeProxyFetch } from "./src/proxy.js";',
'export { createForumTopicTelegram, deleteMessageTelegram, editForumTopicTelegram, editMessageReplyMarkupTelegram, editMessageTelegram, pinMessageTelegram, reactMessageTelegram, renameForumTopicTelegram, sendMessageTelegram, sendPollTelegram, sendStickerTelegram, sendTypingTelegram, unpinMessageTelegram } from "./src/send.js";',
'export { createTelegramThreadBindingManager, getTelegramThreadBindingManager, setTelegramThreadBindingIdleTimeoutBySessionKey, setTelegramThreadBindingMaxAgeBySessionKey } from "./src/thread-bindings.js";',
'export { resolveTelegramToken } from "./src/token.js";',
@@ -83,10 +87,11 @@ const RUNTIME_API_EXPORT_GUARDS: Record<string, readonly string[]> = {
'export * from "./src/auto-reply.js";',
'export * from "./src/inbound.js";',
'export * from "./src/login.js";',
'export * from "./src/login-qr.js";',
'export * from "./src/media.js";',
'export * from "./src/send.js";',
'export * from "./src/session.js";',
"export async function startWebLoginWithQr( ...args: Parameters<StartWebLoginWithQr> ): ReturnType<StartWebLoginWithQr> { const { startWebLoginWithQr } = await loadLoginQrModule(); return await startWebLoginWithQr(...args); }",
"export async function waitForWebLogin( ...args: Parameters<WaitForWebLogin> ): ReturnType<WaitForWebLogin> { const { waitForWebLogin } = await loadLoginQrModule(); return await waitForWebLogin(...args); }",
],
} as const;

View File

@@ -0,0 +1,7 @@
import { createSubsystemLogger } from "../../logging/subsystem.js";
const log = createSubsystemLogger("process/supervisor");
export function warnProcessSupervisorSpawnFailure(message: string) {
log.warn(message);
}

View File

@@ -1,6 +1,5 @@
import crypto from "node:crypto";
import { getShellConfig } from "../../agents/shell-utils.js";
import { createSubsystemLogger } from "../../logging/subsystem.js";
import { createChildAdapter } from "./adapters/child.js";
import { createPtyAdapter } from "./adapters/pty.js";
import { createRunRegistry } from "./registry.js";
@@ -13,8 +12,6 @@ import type {
TerminationReason,
} from "./types.js";
const log = createSubsystemLogger("process/supervisor");
type ActiveRun = {
run: ManagedRun;
scopeKey?: string;
@@ -264,7 +261,8 @@ export function createProcessSupervisor(): ProcessSupervisor {
exitCode: null,
exitSignal: null,
});
log.warn(`spawn failed: runId=${runId} reason=${String(err)}`);
const { warnProcessSupervisorSpawnFailure } = await import("./supervisor-log.runtime.js");
warnProcessSupervisorSpawnFailure(`spawn failed: runId=${runId} reason=${String(err)}`);
throw err;
}
};

View File

@@ -1,4 +1,4 @@
import { formatTerminalLink } from "../utils.js";
import { formatTerminalLink } from "./terminal-link.js";
export function resolveDocsRoot(): string {
return "https://docs.openclaw.ai";

View File

@@ -0,0 +1,15 @@
export function formatTerminalLink(
label: string,
url: string,
opts?: { fallback?: string; force?: boolean },
): string {
const esc = "\u001b";
const safeLabel = label.replaceAll(esc, "");
const safeUrl = url.replaceAll(esc, "");
const allow =
opts?.force === true ? true : opts?.force === false ? false : Boolean(process.stdout.isTTY);
if (!allow) {
return opts?.fallback ?? `${safeLabel} (${safeUrl})`;
}
return `\u001b]8;;${safeUrl}\u0007${safeLabel}\u001b]8;;\u0007`;
}

View File

@@ -9,6 +9,7 @@ import {
resolveRequiredHomeDir,
} from "./infra/home-dir.js";
import { isPlainObject } from "./infra/plain-object.js";
import { formatTerminalLink } from "./terminal/terminal-link.js";
export async function ensureDir(dir: string) {
await fs.promises.mkdir(dir, { recursive: true });
@@ -55,7 +56,7 @@ export function safeParseJson<T>(raw: string): T | null {
}
}
export { isPlainObject };
export { formatTerminalLink, isPlainObject };
/**
* Type guard for Record<string, unknown> (less strict than isPlainObject).
@@ -355,21 +356,5 @@ export function displayString(input: string): string {
return shortenHomeInString(input);
}
export function formatTerminalLink(
label: string,
url: string,
opts?: { fallback?: string; force?: boolean },
): string {
const esc = "\u001b";
const safeLabel = label.replaceAll(esc, "");
const safeUrl = url.replaceAll(esc, "");
const allow =
opts?.force === true ? true : opts?.force === false ? false : Boolean(process.stdout.isTTY);
if (!allow) {
return opts?.fallback ?? `${safeLabel} (${safeUrl})`;
}
return `\u001b]8;;${safeUrl}\u0007${safeLabel}\u001b]8;;\u0007`;
}
// Configuration root; can be overridden via OPENCLAW_STATE_DIR.
export const CONFIG_DIR = resolveConfigDir();