mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 02:00:43 +00:00
* feat(msteams): add federated authentication support (certificate + managed identity + workload identity) * msteams: fix vitest 4.1.2 compat, type errors, and regenerate config baseline * msteams: fix lint errors, update fetch allowlist, regenerate protocol Swift * fix(msteams): gate secret-only delegated auth flows * fix(ci): unblock gateway watch and install smoke * fix(ci): restore mergeability for pr 53615 * fix(ci): restore channel registry helper typing * fix(ci): refresh raw fetch guard allowlist --------- Co-authored-by: Chudi Huang <Chudi.Huang@microsoft.com> Co-authored-by: Brad Groux <3053586+BradGroux@users.noreply.github.com>
200 lines
6.1 KiB
TypeScript
200 lines
6.1 KiB
TypeScript
import process from "node:process";
|
|
import { resolveDebugProxySettings, type DebugProxySettings } from "./env.js";
|
|
import type { CaptureProtocol } from "./types.js";
|
|
|
|
export type DebugProxyCoverageStatus = "captured" | "proxy-only" | "uncovered";
|
|
|
|
export type DebugProxyCoverageEntry = {
|
|
id: string;
|
|
label: string;
|
|
modulePath: string;
|
|
protocols: CaptureProtocol[];
|
|
status: DebugProxyCoverageStatus;
|
|
notes: string;
|
|
};
|
|
|
|
export type DebugProxyCoverageSummary = {
|
|
total: number;
|
|
captured: number;
|
|
proxyOnly: number;
|
|
uncovered: number;
|
|
};
|
|
|
|
const DEBUG_PROXY_COVERAGE_ENTRIES: readonly DebugProxyCoverageEntry[] = [
|
|
{
|
|
id: "provider-transport-fetch",
|
|
label: "Provider HTTP transport",
|
|
modulePath: "src/agents/provider-transport-fetch.ts",
|
|
protocols: ["http", "https", "sse"],
|
|
status: "captured",
|
|
notes:
|
|
"Central provider fetch seam routes through explicit proxy overrides and records request/response payloads.",
|
|
},
|
|
{
|
|
id: "openai-ws-manager",
|
|
label: "OpenAI websocket manager",
|
|
modulePath: "src/agents/openai-ws-connection.ts",
|
|
protocols: ["ws", "wss"],
|
|
status: "captured",
|
|
notes:
|
|
"Central OpenAI websocket path records open/frame/close/error events with proxy agent support.",
|
|
},
|
|
{
|
|
id: "discord-rest",
|
|
label: "Discord REST monitor fetch",
|
|
modulePath: "extensions/discord/monitor/rest-fetch.ts",
|
|
protocols: ["http", "https"],
|
|
status: "captured",
|
|
notes: "Discord monitor REST calls inherit the debug proxy and record HTTP exchanges.",
|
|
},
|
|
{
|
|
id: "discord-gateway",
|
|
label: "Discord gateway monitor",
|
|
modulePath: "extensions/discord/monitor/gateway-plugin.ts",
|
|
protocols: ["https", "wss"],
|
|
status: "captured",
|
|
notes:
|
|
"Gateway metadata fetches and websocket lifecycle events are captured for monitor traffic.",
|
|
},
|
|
{
|
|
id: "telegram-fetch",
|
|
label: "Telegram fetch resolver",
|
|
modulePath: "extensions/telegram/fetch.ts",
|
|
protocols: ["http", "https"],
|
|
status: "captured",
|
|
notes:
|
|
"Telegram API fetch fallback picks up debug proxy env and records outbound/inbound exchanges.",
|
|
},
|
|
{
|
|
id: "mattermost-ws",
|
|
label: "Mattermost monitor websocket",
|
|
modulePath: "extensions/mattermost/mattermost/monitor-websocket.ts",
|
|
protocols: ["ws", "wss"],
|
|
status: "captured",
|
|
notes: "Mattermost websocket monitor uses the debug proxy agent and records frame activity.",
|
|
},
|
|
{
|
|
id: "openai-realtime-voice",
|
|
label: "OpenAI realtime voice bridge",
|
|
modulePath: "extensions/openai/realtime-voice-provider.ts",
|
|
protocols: ["wss"],
|
|
status: "captured",
|
|
notes:
|
|
"Realtime voice bridge now routes through the debug proxy agent and records websocket frames.",
|
|
},
|
|
{
|
|
id: "openai-realtime-transcription",
|
|
label: "OpenAI realtime transcription",
|
|
modulePath: "extensions/openai/realtime-transcription-provider.ts",
|
|
protocols: ["wss"],
|
|
status: "captured",
|
|
notes:
|
|
"Realtime transcription sessions now route through the debug proxy agent and record websocket frames.",
|
|
},
|
|
{
|
|
id: "openai-tts",
|
|
label: "OpenAI text-to-speech",
|
|
modulePath: "extensions/openai/tts.ts",
|
|
protocols: ["https"],
|
|
status: "captured",
|
|
notes:
|
|
"Direct OpenAI TTS fetches record request/response payloads while inheriting proxy env routing.",
|
|
},
|
|
{
|
|
id: "microsoft-voices",
|
|
label: "Microsoft voice discovery",
|
|
modulePath: "extensions/microsoft/speech-provider.ts",
|
|
protocols: ["https"],
|
|
status: "captured",
|
|
notes:
|
|
"Microsoft voice listing fetches record HTTP exchanges and follow process-wide proxy routing.",
|
|
},
|
|
{
|
|
id: "feishu-client-http",
|
|
label: "Feishu SDK HTTP client",
|
|
modulePath: "extensions/feishu/client.ts",
|
|
protocols: ["https"],
|
|
status: "proxy-only",
|
|
notes:
|
|
"Feishu SDK traffic can inherit ambient proxying, but decrypted request/response capture is not yet wired at the SDK seam.",
|
|
},
|
|
{
|
|
id: "feishu-client-ws",
|
|
label: "Feishu SDK websocket client",
|
|
modulePath: "extensions/feishu/client.ts",
|
|
protocols: ["wss"],
|
|
status: "proxy-only",
|
|
notes:
|
|
"Feishu websocket creation can inherit ambient proxying, but websocket frame capture is not yet wired.",
|
|
},
|
|
];
|
|
|
|
let warnedCoverageSessionKey: string | null = null;
|
|
|
|
export function listDebugProxyCoverageEntries(): DebugProxyCoverageEntry[] {
|
|
return DEBUG_PROXY_COVERAGE_ENTRIES.map((entry) => ({
|
|
...entry,
|
|
protocols: [...entry.protocols],
|
|
}));
|
|
}
|
|
|
|
export function summarizeDebugProxyCoverage(
|
|
entries: readonly DebugProxyCoverageEntry[] = DEBUG_PROXY_COVERAGE_ENTRIES,
|
|
): DebugProxyCoverageSummary {
|
|
let captured = 0;
|
|
let proxyOnly = 0;
|
|
let uncovered = 0;
|
|
for (const entry of entries) {
|
|
if (entry.status === "captured") {
|
|
captured += 1;
|
|
continue;
|
|
}
|
|
if (entry.status === "proxy-only") {
|
|
proxyOnly += 1;
|
|
continue;
|
|
}
|
|
uncovered += 1;
|
|
}
|
|
return {
|
|
total: entries.length,
|
|
captured,
|
|
proxyOnly,
|
|
uncovered,
|
|
};
|
|
}
|
|
|
|
export function buildDebugProxyCoverageReport() {
|
|
const entries = listDebugProxyCoverageEntries();
|
|
return {
|
|
summary: summarizeDebugProxyCoverage(entries),
|
|
entries,
|
|
};
|
|
}
|
|
|
|
export function maybeWarnAboutDebugProxyCoverage(
|
|
settings: DebugProxySettings = resolveDebugProxySettings(),
|
|
warn: (message: string) => void = (message) => process.stderr.write(`${message}\n`),
|
|
): void {
|
|
if (!settings.enabled || !settings.required) {
|
|
return;
|
|
}
|
|
const sessionKey = `${settings.sessionId}:${settings.proxyUrl ?? ""}`;
|
|
if (warnedCoverageSessionKey === sessionKey) {
|
|
return;
|
|
}
|
|
warnedCoverageSessionKey = sessionKey;
|
|
|
|
const report = buildDebugProxyCoverageReport();
|
|
const { summary } = report;
|
|
const partial = report.entries.filter((entry) => entry.status !== "captured");
|
|
if (partial.length === 0) {
|
|
return;
|
|
}
|
|
warn(
|
|
`[openclaw proxy] debug proxy coverage: ${summary.captured}/${summary.total} captured, ${summary.proxyOnly} proxy-only, ${summary.uncovered} uncovered.`,
|
|
);
|
|
warn(
|
|
`[openclaw proxy] remaining gaps: ${partial.map((entry) => entry.id).join(", ")}. Run \`openclaw proxy coverage\` for details.`,
|
|
);
|
|
}
|