mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-03 04:16:22 +00:00
* refactor: share talk event metric extraction * refactor: reuse shared coercion helpers * refactor: reuse shared primitive guards * refactor: reuse shared record guard * refactor: reuse shared primitive helpers * refactor: reuse shared string guards * refactor: reuse shared non-empty string guard * refactor: share plugin primitive coercion helpers * refactor: reuse plugin coercion helpers * refactor: reuse plugin coercion helpers in more plugins * refactor: reuse channel coercion helpers * refactor: reuse monitor coercion helpers * refactor: reuse provider coercion helpers * refactor: reuse core coercion helpers * refactor: reuse runtime coercion helpers * refactor: reuse helper coercion in codex paths * refactor: reuse helper coercion in runtime paths * refactor: reuse codex app-server coercion helpers * refactor: reuse codex record helpers * refactor: reuse migration and qa record helpers * refactor: reuse feishu and core helper guards * refactor: reuse browser and policy coercion helpers * refactor: reuse memory wiki record helper * refactor: share boolean coercion helpers * refactor: reuse finite number coercion * refactor: reuse trimmed string list helpers * refactor: reuse string list normalization * refactor: reuse remaining string list helpers * refactor: reuse string entry normalizer * refactor: share sorted string helpers * refactor: share string list normalization * test: preserve command registry browser imports * refactor: reuse trimmed list helpers * refactor: reuse string dedupe helpers * refactor: reuse local dedupe helpers * refactor: reuse more string dedupe helpers * refactor: reuse command string dedupe helpers * refactor: dedupe memory path lists with helper * refactor: expose string dedupe helpers to plugins * refactor: reuse core string dedupe helpers * refactor: reuse shared unique value helpers * refactor: reuse unique helpers in agent utilities * refactor: reuse unique helpers in config plumbing * refactor: reuse unique helpers in extensions * refactor: reuse unique helpers in core utilities * refactor: reuse unique helpers in qa plugins * refactor: reuse unique helpers in memory plugins * refactor: reuse unique helpers in channel plugins * refactor: reuse unique helpers in core tails * refactor: reuse unique helper in comfy workflow * refactor: reuse unique helpers in test utilities * refactor: expose unique value helper to plugins * refactor: reuse unique helpers for numeric lists * refactor: replace index dedupe filters * refactor: reuse string entry normalization * refactor: reuse string normalization in plugin helpers * refactor: reuse string normalization in extension helpers * refactor: reuse string normalization in channel parsers * refactor: reuse string normalization in memory search * refactor: reuse string normalization in provider parsers * refactor: reuse string normalization in qa helpers * refactor: reuse string normalization in infra parsers * refactor: reuse string normalization in messaging parsers * refactor: reuse string normalization in core parsers * refactor: reuse string normalization in extension parsers * refactor: reuse string normalization in remaining parsers * refactor: reuse string normalization in final parser spots * refactor: reuse string normalization in qa media helpers * refactor: reuse normalization in provider and media lists * refactor: reuse normalization for remaining set filters * refactor: reuse normalization in policy allowlists * refactor: reuse normalization in session and owner lists * refactor: centralize primitive string lists * refactor: reuse lowercase entry helpers * refactor: reuse sorted string helpers * refactor: reuse unique trimmed helpers * refactor: reuse string normalization helpers * refactor: reuse catalog string helpers * refactor: reuse remaining string helpers * refactor: simplify remaining list normalization * refactor: reuse codex auth order normalization * chore: refresh plugin sdk api baseline * fix: make shared string sorting deterministic * chore: refresh plugin sdk api baseline * fix: align host env security ordering
102 lines
3.2 KiB
TypeScript
102 lines
3.2 KiB
TypeScript
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import type { PluginHookInboundClaimEvent } from "openclaw/plugin-sdk/plugin-entry";
|
|
import { normalizeSingleOrTrimmedStringList } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
import type { CodexUserInput } from "./app-server/protocol.js";
|
|
|
|
type InboundMedia = {
|
|
path?: string;
|
|
url?: string;
|
|
mimeType?: string;
|
|
};
|
|
|
|
const IMAGE_EXTENSIONS = new Set([".avif", ".gif", ".jpeg", ".jpg", ".png", ".webp"]);
|
|
|
|
export function buildCodexConversationTurnInput(params: {
|
|
prompt: string;
|
|
event: PluginHookInboundClaimEvent;
|
|
}): CodexUserInput[] {
|
|
return [
|
|
{ type: "text", text: params.prompt, text_elements: [] },
|
|
...extractInboundMedia(params.event)
|
|
.map(toCodexImageInput)
|
|
.filter((item): item is CodexUserInput => item !== undefined),
|
|
];
|
|
}
|
|
|
|
function extractInboundMedia(event: PluginHookInboundClaimEvent): InboundMedia[] {
|
|
const metadata = event.metadata ?? {};
|
|
// OpenClaw channels expose either local staged files or remote URLs. Keep
|
|
// them separate so Codex can receive the cheaper localImage input when a file
|
|
// is already present, while still supporting remote-only transports.
|
|
const paths = normalizeSingleOrTrimmedStringList(metadata.mediaPaths).concat(
|
|
normalizeSingleOrTrimmedStringList(metadata.mediaPath),
|
|
);
|
|
const urls = normalizeSingleOrTrimmedStringList(metadata.mediaUrls).concat(
|
|
normalizeSingleOrTrimmedStringList(metadata.mediaUrl),
|
|
);
|
|
const mimeTypes = normalizeSingleOrTrimmedStringList(metadata.mediaTypes).concat(
|
|
normalizeSingleOrTrimmedStringList(metadata.mediaType),
|
|
);
|
|
const count = Math.max(paths.length, urls.length, mimeTypes.length);
|
|
const media: InboundMedia[] = [];
|
|
for (let index = 0; index < count; index += 1) {
|
|
media.push({
|
|
path: paths[index],
|
|
url: urls[index],
|
|
mimeType: mimeTypes[index] ?? mimeTypes[0],
|
|
});
|
|
}
|
|
return media;
|
|
}
|
|
|
|
function toCodexImageInput(media: InboundMedia): CodexUserInput | undefined {
|
|
if (!isImageMedia(media)) {
|
|
return undefined;
|
|
}
|
|
const localPath = media.path ?? readLocalMediaPath(media.url);
|
|
if (localPath) {
|
|
const normalized = normalizeFileUrl(localPath);
|
|
return normalized ? { type: "localImage", path: normalized } : undefined;
|
|
}
|
|
return media.url ? { type: "image", url: media.url } : undefined;
|
|
}
|
|
|
|
function isImageMedia(media: InboundMedia): boolean {
|
|
if (media.mimeType?.toLowerCase().startsWith("image/")) {
|
|
return true;
|
|
}
|
|
const candidate = media.path ?? media.url;
|
|
if (!candidate) {
|
|
return false;
|
|
}
|
|
return IMAGE_EXTENSIONS.has(path.extname(candidate.split(/[?#]/, 1)[0] ?? "").toLowerCase());
|
|
}
|
|
|
|
function normalizeFileUrl(value: string): string | undefined {
|
|
if (!value.startsWith("file://")) {
|
|
return value;
|
|
}
|
|
try {
|
|
return fileURLToPath(value);
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
function readLocalMediaPath(value: string | undefined): string | undefined {
|
|
if (!value) {
|
|
return undefined;
|
|
}
|
|
if (value.startsWith("file://")) {
|
|
return value;
|
|
}
|
|
if (value.startsWith("//")) {
|
|
return undefined;
|
|
}
|
|
if (path.isAbsolute(value) || path.win32.isAbsolute(value)) {
|
|
return value;
|
|
}
|
|
return /^[a-z][a-z0-9+.-]*:/i.test(value) ? undefined : value;
|
|
}
|