mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 01:31:23 +00:00
fix(tool-display): satisfy format/lint and address review feedback
- extract web_search/web_fetch detail resolvers into common module\n- fix node -c classification so file path remains positional\n- remove dead git subcommands set\n- keep exec summary refinements (heredoc/node check/git -C/preamble strip)\n- make tests cover node -c syntax-check path\n- run format:check, tsgo, lint, and focused e2e tests
This commit is contained in:
committed by
Peter Steinberger
parent
24f213e7ed
commit
facfa410a7
@@ -193,7 +193,8 @@ export function resolveWriteDetail(toolKey: string, args: unknown): string | und
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const path = resolvePathArg(record) ?? (typeof record.url === "string" ? record.url.trim() : undefined);
|
const path =
|
||||||
|
resolvePathArg(record) ?? (typeof record.url === "string" ? record.url.trim() : undefined);
|
||||||
if (!path) {
|
if (!path) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -219,6 +220,52 @@ export function resolveWriteDetail(toolKey: string, args: unknown): string | und
|
|||||||
return `${destinationPrefix} ${path}`;
|
return `${destinationPrefix} ${path}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resolveWebSearchDetail(args: unknown): string | undefined {
|
||||||
|
const record = asRecord(args);
|
||||||
|
if (!record) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = typeof record.query === "string" ? record.query.trim() : undefined;
|
||||||
|
const count =
|
||||||
|
typeof record.count === "number" && Number.isFinite(record.count) && record.count > 0
|
||||||
|
? Math.floor(record.count)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
if (!query) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count !== undefined ? `for "${query}" (top ${count})` : `for "${query}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveWebFetchDetail(args: unknown): string | undefined {
|
||||||
|
const record = asRecord(args);
|
||||||
|
if (!record) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = typeof record.url === "string" ? record.url.trim() : undefined;
|
||||||
|
if (!url) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mode = typeof record.extractMode === "string" ? record.extractMode.trim() : undefined;
|
||||||
|
const maxChars =
|
||||||
|
typeof record.maxChars === "number" && Number.isFinite(record.maxChars) && record.maxChars > 0
|
||||||
|
? Math.floor(record.maxChars)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const suffix = [
|
||||||
|
mode ? `mode ${mode}` : undefined,
|
||||||
|
maxChars !== undefined ? `max ${maxChars} chars` : undefined,
|
||||||
|
]
|
||||||
|
.filter((value): value is string => Boolean(value))
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
|
return suffix ? `from ${url} (${suffix})` : `from ${url}`;
|
||||||
|
}
|
||||||
|
|
||||||
function stripOuterQuotes(value: string | undefined): string | undefined {
|
function stripOuterQuotes(value: string | undefined): string | undefined {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return value;
|
return value;
|
||||||
@@ -297,7 +344,7 @@ function binaryName(token: string | undefined): string | undefined {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const cleaned = stripOuterQuotes(token) ?? token;
|
const cleaned = stripOuterQuotes(token) ?? token;
|
||||||
const segment = cleaned.split(/[\/]/).at(-1) ?? cleaned;
|
const segment = cleaned.split(/[/]/).at(-1) ?? cleaned;
|
||||||
return segment.trim().toLowerCase();
|
return segment.trim().toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,7 +418,11 @@ function positionalArgs(words: string[], from = 1, optionsWithValue: string[] =
|
|||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
function firstPositional(words: string[], from = 1, optionsWithValue: string[] = []): string | undefined {
|
function firstPositional(
|
||||||
|
words: string[],
|
||||||
|
from = 1,
|
||||||
|
optionsWithValue: string[] = [],
|
||||||
|
): string | undefined {
|
||||||
return positionalArgs(words, from, optionsWithValue)[0];
|
return positionalArgs(words, from, optionsWithValue)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,7 +476,10 @@ function unwrapShellWrapper(command: string): string {
|
|||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
const inner = words.slice(flagIndex + 1).join(" ").trim();
|
const inner = words
|
||||||
|
.slice(flagIndex + 1)
|
||||||
|
.join(" ")
|
||||||
|
.trim();
|
||||||
return inner ? (stripOuterQuotes(inner) ?? command) : command;
|
return inner ? (stripOuterQuotes(inner) ?? command) : command;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,7 +576,7 @@ function stripShellPreamble(command: string): string {
|
|||||||
{ index: newlineIndex, length: 1 },
|
{ index: newlineIndex, length: 1 },
|
||||||
]
|
]
|
||||||
.filter((candidate) => candidate.index >= 0)
|
.filter((candidate) => candidate.index >= 0)
|
||||||
.sort((a, b) => a.index - b.index);
|
.toSorted((a, b) => a.index - b.index);
|
||||||
|
|
||||||
const first = candidates[0];
|
const first = candidates[0];
|
||||||
const head = (first ? rest.slice(0, first.index) : rest).trim();
|
const head = (first ? rest.slice(0, first.index) : rest).trim();
|
||||||
@@ -550,25 +604,6 @@ function summarizeKnownExec(words: string[]): string {
|
|||||||
const bin = binaryName(words[0]) ?? "command";
|
const bin = binaryName(words[0]) ?? "command";
|
||||||
|
|
||||||
if (bin === "git") {
|
if (bin === "git") {
|
||||||
const subcommands = new Set([
|
|
||||||
"status",
|
|
||||||
"diff",
|
|
||||||
"log",
|
|
||||||
"show",
|
|
||||||
"branch",
|
|
||||||
"checkout",
|
|
||||||
"switch",
|
|
||||||
"commit",
|
|
||||||
"pull",
|
|
||||||
"push",
|
|
||||||
"fetch",
|
|
||||||
"merge",
|
|
||||||
"rebase",
|
|
||||||
"add",
|
|
||||||
"restore",
|
|
||||||
"reset",
|
|
||||||
"stash",
|
|
||||||
]);
|
|
||||||
const globalWithValue = new Set([
|
const globalWithValue = new Set([
|
||||||
"-C",
|
"-C",
|
||||||
"-c",
|
"-c",
|
||||||
@@ -675,7 +710,10 @@ function summarizeKnownExec(words: string[]): string {
|
|||||||
if (bin === "head" || bin === "tail") {
|
if (bin === "head" || bin === "tail") {
|
||||||
const lines =
|
const lines =
|
||||||
optionValue(words, ["-n", "--lines"]) ??
|
optionValue(words, ["-n", "--lines"]) ??
|
||||||
words.slice(1).find((token) => /^-\d+$/.test(token))?.slice(1);
|
words
|
||||||
|
.slice(1)
|
||||||
|
.find((token) => /^-\d+$/.test(token))
|
||||||
|
?.slice(1);
|
||||||
const positional = positionalArgs(words, 1, ["-n", "--lines"]);
|
const positional = positionalArgs(words, 1, ["-n", "--lines"]);
|
||||||
let target = positional.at(-1);
|
let target = positional.at(-1);
|
||||||
if (target && /^\d+$/.test(target) && positional.length === 1) {
|
if (target && /^\d+$/.test(target) && positional.length === 1) {
|
||||||
@@ -791,15 +829,22 @@ function summarizeKnownExec(words: string[]): string {
|
|||||||
return `run ${bin} inline script`;
|
return `run ${bin} inline script`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const script = firstPositional(words, 1, ["-c", "-e", "--eval", "-m"]);
|
const nodeOptsWithValue = ["-e", "--eval", "-m"];
|
||||||
|
const otherOptsWithValue = ["-c", "-e", "--eval", "-m"];
|
||||||
|
const script = firstPositional(
|
||||||
|
words,
|
||||||
|
1,
|
||||||
|
bin === "node" ? nodeOptsWithValue : otherOptsWithValue,
|
||||||
|
);
|
||||||
if (!script) {
|
if (!script) {
|
||||||
return `run ${bin}`;
|
return `run ${bin}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bin === "node") {
|
if (bin === "node") {
|
||||||
const mode = words.includes("--check") || words.includes("-c")
|
const mode =
|
||||||
? "check js syntax for"
|
words.includes("--check") || words.includes("-c")
|
||||||
: "run node script";
|
? "check js syntax for"
|
||||||
|
: "run node script";
|
||||||
return `${mode} ${script}`;
|
return `${mode} ${script}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -815,7 +860,7 @@ function summarizeKnownExec(words: string[]): string {
|
|||||||
if (!arg || arg.length > 48) {
|
if (!arg || arg.length > 48) {
|
||||||
return `run ${bin}`;
|
return `run ${bin}`;
|
||||||
}
|
}
|
||||||
return /^[A-Za-z0-9._\/-]+$/.test(arg) ? `run ${bin} ${arg}` : `run ${bin}`;
|
return /^[A-Za-z0-9._/-]+$/.test(arg) ? `run ${bin} ${arg}` : `run ${bin}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function summarizeExecCommand(command: string): string | undefined {
|
function summarizeExecCommand(command: string): string | undefined {
|
||||||
|
|||||||
@@ -123,8 +123,18 @@ describe("tool display details", () => {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
const nodeShortCheckDetail = formatToolDetail(
|
||||||
|
resolveToolDisplay({
|
||||||
|
name: "exec",
|
||||||
|
args: {
|
||||||
|
command: "node -c /tmp/test.js",
|
||||||
|
workdir: "/Users/adityasingh/.openclaw/workspace",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
expect(pyDetail).toContain("run python3 inline script (heredoc)");
|
expect(pyDetail).toContain("run python3 inline script (heredoc)");
|
||||||
expect(nodeCheckDetail).toContain("check js syntax for /tmp/test.js");
|
expect(nodeCheckDetail).toContain("check js syntax for /tmp/test.js");
|
||||||
|
expect(nodeShortCheckDetail).toContain("check js syntax for /tmp/test.js");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import {
|
|||||||
resolveDetailFromKeys,
|
resolveDetailFromKeys,
|
||||||
resolveExecDetail,
|
resolveExecDetail,
|
||||||
resolveReadDetail,
|
resolveReadDetail,
|
||||||
|
resolveWebFetchDetail,
|
||||||
|
resolveWebSearchDetail,
|
||||||
resolveWriteDetail,
|
resolveWriteDetail,
|
||||||
type ToolDisplaySpec as ToolDisplaySpecBase,
|
type ToolDisplaySpec as ToolDisplaySpecBase,
|
||||||
} from "./tool-display-common.js";
|
} from "./tool-display-common.js";
|
||||||
@@ -92,36 +94,12 @@ export function resolveToolDisplay(params: {
|
|||||||
detail = resolveWriteDetail(key, params.args);
|
detail = resolveWriteDetail(key, params.args);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!detail && key === "web_search" && params.args && typeof params.args === "object") {
|
if (!detail && key === "web_search") {
|
||||||
const record = params.args as Record<string, unknown>;
|
detail = resolveWebSearchDetail(params.args);
|
||||||
const query = typeof record.query === "string" ? record.query.trim() : undefined;
|
|
||||||
const count =
|
|
||||||
typeof record.count === "number" && Number.isFinite(record.count) && record.count > 0
|
|
||||||
? Math.floor(record.count)
|
|
||||||
: undefined;
|
|
||||||
if (query) {
|
|
||||||
detail = count !== undefined ? `for "${query}" (top ${count})` : `for "${query}"`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!detail && key === "web_fetch" && params.args && typeof params.args === "object") {
|
if (!detail && key === "web_fetch") {
|
||||||
const record = params.args as Record<string, unknown>;
|
detail = resolveWebFetchDetail(params.args);
|
||||||
const url = typeof record.url === "string" ? record.url.trim() : undefined;
|
|
||||||
const mode =
|
|
||||||
typeof record.extractMode === "string" ? record.extractMode.trim() : undefined;
|
|
||||||
const maxChars =
|
|
||||||
typeof record.maxChars === "number" && Number.isFinite(record.maxChars) && record.maxChars > 0
|
|
||||||
? Math.floor(record.maxChars)
|
|
||||||
: undefined;
|
|
||||||
if (url) {
|
|
||||||
const suffix = [
|
|
||||||
mode ? `mode ${mode}` : undefined,
|
|
||||||
maxChars !== undefined ? `max ${maxChars} chars` : undefined,
|
|
||||||
]
|
|
||||||
.filter((value): value is string => Boolean(value))
|
|
||||||
.join(", ");
|
|
||||||
detail = suffix ? `from ${url} (${suffix})` : `from ${url}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const detailKeys = actionSpec?.detailKeys ?? spec?.detailKeys ?? FALLBACK.detailKeys ?? [];
|
const detailKeys = actionSpec?.detailKeys ?? spec?.detailKeys ?? FALLBACK.detailKeys ?? [];
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import {
|
|||||||
resolveDetailFromKeys,
|
resolveDetailFromKeys,
|
||||||
resolveExecDetail,
|
resolveExecDetail,
|
||||||
resolveReadDetail,
|
resolveReadDetail,
|
||||||
|
resolveWebFetchDetail,
|
||||||
|
resolveWebSearchDetail,
|
||||||
resolveWriteDetail,
|
resolveWriteDetail,
|
||||||
type ToolDisplaySpec as ToolDisplaySpecBase,
|
type ToolDisplaySpec as ToolDisplaySpecBase,
|
||||||
} from "../../../src/agents/tool-display-common.js";
|
} from "../../../src/agents/tool-display-common.js";
|
||||||
@@ -92,36 +94,12 @@ export function resolveToolDisplay(params: {
|
|||||||
detail = resolveWriteDetail(key, params.args);
|
detail = resolveWriteDetail(key, params.args);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!detail && key === "web_search" && params.args && typeof params.args === "object") {
|
if (!detail && key === "web_search") {
|
||||||
const record = params.args as Record<string, unknown>;
|
detail = resolveWebSearchDetail(params.args);
|
||||||
const query = typeof record.query === "string" ? record.query.trim() : undefined;
|
|
||||||
const count =
|
|
||||||
typeof record.count === "number" && Number.isFinite(record.count) && record.count > 0
|
|
||||||
? Math.floor(record.count)
|
|
||||||
: undefined;
|
|
||||||
if (query) {
|
|
||||||
detail = count !== undefined ? `for "${query}" (top ${count})` : `for "${query}"`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!detail && key === "web_fetch" && params.args && typeof params.args === "object") {
|
if (!detail && key === "web_fetch") {
|
||||||
const record = params.args as Record<string, unknown>;
|
detail = resolveWebFetchDetail(params.args);
|
||||||
const url = typeof record.url === "string" ? record.url.trim() : undefined;
|
|
||||||
const mode =
|
|
||||||
typeof record.extractMode === "string" ? record.extractMode.trim() : undefined;
|
|
||||||
const maxChars =
|
|
||||||
typeof record.maxChars === "number" && Number.isFinite(record.maxChars) && record.maxChars > 0
|
|
||||||
? Math.floor(record.maxChars)
|
|
||||||
: undefined;
|
|
||||||
if (url) {
|
|
||||||
const suffix = [
|
|
||||||
mode ? `mode ${mode}` : undefined,
|
|
||||||
maxChars !== undefined ? `max ${maxChars} chars` : undefined,
|
|
||||||
]
|
|
||||||
.filter((value): value is string => Boolean(value))
|
|
||||||
.join(", ");
|
|
||||||
detail = suffix ? `from ${url} (${suffix})` : `from ${url}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const detailKeys = actionSpec?.detailKeys ?? spec?.detailKeys ?? FALLBACK.detailKeys ?? [];
|
const detailKeys = actionSpec?.detailKeys ?? spec?.detailKeys ?? FALLBACK.detailKeys ?? [];
|
||||||
|
|||||||
Reference in New Issue
Block a user