mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 13:17:39 +00:00
refactor(core): dedupe schema and command parsing helpers
This commit is contained in:
@@ -207,6 +207,36 @@ function simplifyUnionVariants(params: { obj: Record<string, unknown>; variants:
|
|||||||
return { variants: stripped ? nonNullVariants : variants };
|
return { variants: stripped ? nonNullVariants : variants };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function flattenUnionFallback(
|
||||||
|
obj: Record<string, unknown>,
|
||||||
|
value: unknown,
|
||||||
|
): Record<string, unknown> | undefined {
|
||||||
|
if (!Array.isArray(value)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const variants = (value as Record<string, unknown>[]).filter((v) => v && typeof v === "object");
|
||||||
|
const types = new Set(variants.map((v) => v.type).filter(Boolean));
|
||||||
|
if (variants.length === 1) {
|
||||||
|
const merged: Record<string, unknown> = { ...variants[0] };
|
||||||
|
copySchemaMeta(obj, merged);
|
||||||
|
return merged;
|
||||||
|
}
|
||||||
|
if (types.size === 1) {
|
||||||
|
const merged: Record<string, unknown> = { type: Array.from(types)[0] };
|
||||||
|
copySchemaMeta(obj, merged);
|
||||||
|
return merged;
|
||||||
|
}
|
||||||
|
const first = variants[0];
|
||||||
|
if (first?.type) {
|
||||||
|
const merged: Record<string, unknown> = { type: first.type };
|
||||||
|
copySchemaMeta(obj, merged);
|
||||||
|
return merged;
|
||||||
|
}
|
||||||
|
const merged: Record<string, unknown> = {};
|
||||||
|
copySchemaMeta(obj, merged);
|
||||||
|
return merged;
|
||||||
|
}
|
||||||
|
|
||||||
function cleanSchemaForGeminiWithDefs(
|
function cleanSchemaForGeminiWithDefs(
|
||||||
schema: unknown,
|
schema: unknown,
|
||||||
defs: SchemaDefs | undefined,
|
defs: SchemaDefs | undefined,
|
||||||
@@ -343,58 +373,14 @@ function cleanSchemaForGeminiWithDefs(
|
|||||||
// If simplifyUnionVariants couldn't reduce the union above, flatten it
|
// If simplifyUnionVariants couldn't reduce the union above, flatten it
|
||||||
// here as a fallback: pick the first variant's type or use a permissive
|
// here as a fallback: pick the first variant's type or use a permissive
|
||||||
// schema so the tool declaration is accepted.
|
// schema so the tool declaration is accepted.
|
||||||
if (cleaned.anyOf && Array.isArray(cleaned.anyOf)) {
|
const flattenedAnyOf = flattenUnionFallback(cleaned, cleaned.anyOf);
|
||||||
const variants = (cleaned.anyOf as Record<string, unknown>[]).filter(
|
if (flattenedAnyOf) {
|
||||||
(v) => v && typeof v === "object",
|
return flattenedAnyOf;
|
||||||
);
|
|
||||||
const types = new Set(variants.map((v) => v.type).filter(Boolean));
|
|
||||||
if (variants.length === 1) {
|
|
||||||
const merged: Record<string, unknown> = { ...variants[0] };
|
|
||||||
copySchemaMeta(cleaned, merged);
|
|
||||||
return merged;
|
|
||||||
}
|
|
||||||
if (types.size === 1) {
|
|
||||||
const merged: Record<string, unknown> = { type: Array.from(types)[0] };
|
|
||||||
copySchemaMeta(cleaned, merged);
|
|
||||||
return merged;
|
|
||||||
}
|
|
||||||
// Mixed types (e.g. string | array<string>): use first variant's type.
|
|
||||||
// The execute function already handles type coercion at runtime.
|
|
||||||
const first = variants[0];
|
|
||||||
if (first?.type) {
|
|
||||||
const merged: Record<string, unknown> = { type: first.type };
|
|
||||||
copySchemaMeta(cleaned, merged);
|
|
||||||
return merged;
|
|
||||||
}
|
|
||||||
const merged: Record<string, unknown> = {};
|
|
||||||
copySchemaMeta(cleaned, merged);
|
|
||||||
return merged;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cleaned.oneOf && Array.isArray(cleaned.oneOf)) {
|
const flattenedOneOf = flattenUnionFallback(cleaned, cleaned.oneOf);
|
||||||
const variants = (cleaned.oneOf as Record<string, unknown>[]).filter(
|
if (flattenedOneOf) {
|
||||||
(v) => v && typeof v === "object",
|
return flattenedOneOf;
|
||||||
);
|
|
||||||
const types = new Set(variants.map((v) => v.type).filter(Boolean));
|
|
||||||
if (variants.length === 1) {
|
|
||||||
const merged: Record<string, unknown> = { ...variants[0] };
|
|
||||||
copySchemaMeta(cleaned, merged);
|
|
||||||
return merged;
|
|
||||||
}
|
|
||||||
if (types.size === 1) {
|
|
||||||
const merged: Record<string, unknown> = { type: Array.from(types)[0] };
|
|
||||||
copySchemaMeta(cleaned, merged);
|
|
||||||
return merged;
|
|
||||||
}
|
|
||||||
const first = variants[0];
|
|
||||||
if (first?.type) {
|
|
||||||
const merged: Record<string, unknown> = { type: first.type };
|
|
||||||
copySchemaMeta(cleaned, merged);
|
|
||||||
return merged;
|
|
||||||
}
|
|
||||||
const merged: Record<string, unknown> = {};
|
|
||||||
copySchemaMeta(cleaned, merged);
|
|
||||||
return merged;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return cleaned;
|
return cleaned;
|
||||||
|
|||||||
@@ -483,7 +483,10 @@ function unwrapShellWrapper(command: string): string {
|
|||||||
return inner ? (stripOuterQuotes(inner) ?? command) : command;
|
return inner ? (stripOuterQuotes(inner) ?? command) : command;
|
||||||
}
|
}
|
||||||
|
|
||||||
function firstTopLevelStage(command: string): string {
|
function scanTopLevelChars(
|
||||||
|
command: string,
|
||||||
|
visit: (char: string, index: number) => boolean | void,
|
||||||
|
): void {
|
||||||
let quote: '"' | "'" | undefined;
|
let quote: '"' | "'" | undefined;
|
||||||
let escaped = false;
|
let escaped = false;
|
||||||
|
|
||||||
@@ -511,52 +514,39 @@ function firstTopLevelStage(command: string): string {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (char === ";") {
|
if (visit(char, i) === false) {
|
||||||
return command.slice(0, i);
|
return;
|
||||||
}
|
|
||||||
if ((char === "&" || char === "|") && command[i + 1] === char) {
|
|
||||||
return command.slice(0, i);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return command;
|
function firstTopLevelStage(command: string): string {
|
||||||
|
let splitIndex = -1;
|
||||||
|
scanTopLevelChars(command, (char, index) => {
|
||||||
|
if (char === ";") {
|
||||||
|
splitIndex = index;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((char === "&" || char === "|") && command[index + 1] === char) {
|
||||||
|
splitIndex = index;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return splitIndex >= 0 ? command.slice(0, splitIndex) : command;
|
||||||
}
|
}
|
||||||
|
|
||||||
function splitTopLevelPipes(command: string): string[] {
|
function splitTopLevelPipes(command: string): string[] {
|
||||||
const parts: string[] = [];
|
const parts: string[] = [];
|
||||||
let quote: '"' | "'" | undefined;
|
|
||||||
let escaped = false;
|
|
||||||
let start = 0;
|
let start = 0;
|
||||||
|
|
||||||
for (let i = 0; i < command.length; i += 1) {
|
scanTopLevelChars(command, (char, index) => {
|
||||||
const char = command[i];
|
if (char === "|" && command[index - 1] !== "|" && command[index + 1] !== "|") {
|
||||||
|
parts.push(command.slice(start, index));
|
||||||
if (escaped) {
|
start = index + 1;
|
||||||
escaped = false;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
if (char === "\\") {
|
return true;
|
||||||
escaped = true;
|
});
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (quote) {
|
|
||||||
if (char === quote) {
|
|
||||||
quote = undefined;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (char === '"' || char === "'") {
|
|
||||||
quote = char;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (char === "|" && command[i - 1] !== "|" && command[i + 1] !== "|") {
|
|
||||||
parts.push(command.slice(start, i));
|
|
||||||
start = i + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parts.push(command.slice(start));
|
parts.push(command.slice(start));
|
||||||
return parts.map((part) => part.trim()).filter((part) => part.length > 0);
|
return parts.map((part) => part.trim()).filter((part) => part.length > 0);
|
||||||
|
|||||||
@@ -18,11 +18,9 @@ type SelectOption = {
|
|||||||
text?: { text?: string };
|
text?: { text?: string };
|
||||||
};
|
};
|
||||||
|
|
||||||
type InteractionSummary = {
|
type InteractionSelectionFields = {
|
||||||
interactionType?: "block_action" | "view_submission" | "view_closed";
|
|
||||||
actionId: string;
|
|
||||||
blockId?: string;
|
|
||||||
actionType?: string;
|
actionType?: string;
|
||||||
|
blockId?: string;
|
||||||
inputKind?: "text" | "number" | "email" | "url" | "rich_text";
|
inputKind?: "text" | "number" | "email" | "url" | "rich_text";
|
||||||
value?: string;
|
value?: string;
|
||||||
selectedValues?: string[];
|
selectedValues?: string[];
|
||||||
@@ -39,6 +37,11 @@ type InteractionSummary = {
|
|||||||
inputUrl?: string;
|
inputUrl?: string;
|
||||||
richTextValue?: unknown;
|
richTextValue?: unknown;
|
||||||
richTextPreview?: string;
|
richTextPreview?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type InteractionSummary = InteractionSelectionFields & {
|
||||||
|
interactionType?: "block_action" | "view_submission" | "view_closed";
|
||||||
|
actionId: string;
|
||||||
userId?: string;
|
userId?: string;
|
||||||
teamId?: string;
|
teamId?: string;
|
||||||
triggerId?: string;
|
triggerId?: string;
|
||||||
@@ -50,26 +53,9 @@ type InteractionSummary = {
|
|||||||
threadTs?: string;
|
threadTs?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ModalInputSummary = {
|
type ModalInputSummary = InteractionSelectionFields & {
|
||||||
blockId: string;
|
blockId: string;
|
||||||
actionId: string;
|
actionId: string;
|
||||||
actionType?: string;
|
|
||||||
inputKind?: "text" | "number" | "email" | "url" | "rich_text";
|
|
||||||
value?: string;
|
|
||||||
selectedValues?: string[];
|
|
||||||
selectedUsers?: string[];
|
|
||||||
selectedChannels?: string[];
|
|
||||||
selectedConversations?: string[];
|
|
||||||
selectedLabels?: string[];
|
|
||||||
selectedDate?: string;
|
|
||||||
selectedTime?: string;
|
|
||||||
selectedDateTime?: number;
|
|
||||||
inputValue?: string;
|
|
||||||
inputNumber?: number;
|
|
||||||
inputEmail?: string;
|
|
||||||
inputUrl?: string;
|
|
||||||
richTextValue?: unknown;
|
|
||||||
richTextPreview?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function readOptionValues(options: unknown): string[] | undefined {
|
function readOptionValues(options: unknown): string[] | undefined {
|
||||||
|
|||||||
Reference in New Issue
Block a user