mirror of
https://github.com/Tencent/BrowserSkill.git
synced 2026-09-10 13:01:23 +08:00
198 lines
6.6 KiB
TypeScript
198 lines
6.6 KiB
TypeScript
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
import {
|
|
appendTabId,
|
|
appendTarget,
|
|
type PhaseOneRuntime,
|
|
requireNonEmpty,
|
|
requirePositive,
|
|
runnerTimeout,
|
|
type ToolRegistrar,
|
|
} from "./phase-one-runtime";
|
|
import { SESSION_PARAM, TAB_ID_PARAM, TIMEOUT_MS_PARAM } from "./tool-params";
|
|
import type { ToolDeps } from "./tools";
|
|
|
|
const MODIFIERS = ["alt", "ctrl", "meta", "shift"] as const;
|
|
|
|
/** Add hover and select without bypassing session ownership or observation. */
|
|
export function registerPhaseOneInteractionTools(
|
|
deps: ToolDeps,
|
|
register: ToolRegistrar,
|
|
runtime: PhaseOneRuntime,
|
|
): void {
|
|
const { registry } = deps;
|
|
|
|
register(
|
|
defineTool({
|
|
name: "interact.hover",
|
|
description:
|
|
"Move the mouse over a snapshot ref or CSS selector to reveal hover-triggered UI. " +
|
|
"Run browser_inspect action=observe or snapshot afterwards to discover newly visible refs.",
|
|
parameters: {
|
|
target: {
|
|
type: "string",
|
|
required: true,
|
|
description: "Snapshot ref (@e3 / e3) or CSS selector of the element to hover.",
|
|
},
|
|
session: SESSION_PARAM,
|
|
tabId: TAB_ID_PARAM,
|
|
modifiers: {
|
|
type: "array",
|
|
items: { type: "string", enum: MODIFIERS },
|
|
description: "Keyboard modifiers held during the mouse move.",
|
|
},
|
|
settleMs: {
|
|
type: "integer",
|
|
description: "Milliseconds to wait for hover-triggered UI to settle (default: 200).",
|
|
},
|
|
timeoutMs: TIMEOUT_MS_PARAM,
|
|
},
|
|
output: {
|
|
schema: {
|
|
type: "object",
|
|
additionalProperties: false,
|
|
properties: {
|
|
session: { type: "string", required: true },
|
|
tabId: { type: "integer", required: true },
|
|
x: { type: "number", required: true },
|
|
y: { type: "number", required: true },
|
|
},
|
|
},
|
|
render: (_args, value) => [
|
|
{
|
|
type: "text",
|
|
text: `[session ${value.session}] hovered at (${value.x}, ${value.y}) on tab ${value.tabId}`,
|
|
},
|
|
],
|
|
},
|
|
async execute(args, exec) {
|
|
requireNonEmpty(args.target, "target");
|
|
requirePositive(args.timeoutMs, "timeoutMs");
|
|
requirePositive(args.settleMs, "settleMs");
|
|
const sessionId = registry.resolve(args.session, "browser_interact(action=hover)");
|
|
const cmdArgs = ["hover", "--session", sessionId];
|
|
appendTabId(cmdArgs, args.tabId);
|
|
if (args.modifiers !== undefined && args.modifiers.length > 0) {
|
|
cmdArgs.push("--modifiers", args.modifiers.join(","));
|
|
}
|
|
if (args.settleMs !== undefined) cmdArgs.push("--settle", `${args.settleMs}ms`);
|
|
if (args.timeoutMs !== undefined) cmdArgs.push("--timeout", `${args.timeoutMs}ms`);
|
|
appendTarget(cmdArgs, args.target);
|
|
const reply = (await runtime.run(
|
|
exec,
|
|
cmdArgs,
|
|
"hover",
|
|
sessionId,
|
|
runnerTimeout(deps, args.timeoutMs),
|
|
)) as { tab_id: number; x: number; y: number };
|
|
return { session: sessionId, tabId: reply.tab_id, x: reply.x, y: reply.y };
|
|
},
|
|
presentCall: (args) => ({
|
|
card: "terminal",
|
|
title: runtime.commandLine([
|
|
"hover",
|
|
args.target,
|
|
"--session",
|
|
args.session ?? "(current)",
|
|
]),
|
|
description: "Hover an element",
|
|
}),
|
|
presentResult: runtime.presentTerminalResult,
|
|
}),
|
|
);
|
|
|
|
register(
|
|
defineTool({
|
|
name: "interact.select",
|
|
description:
|
|
"Set a select element's option values. Pass one value for a normal select or multiple " +
|
|
"values for a multi-select; values replace the current selection.",
|
|
parameters: {
|
|
target: {
|
|
type: "string",
|
|
required: true,
|
|
description: "Snapshot ref (@e3 / e3) or CSS selector of the select element.",
|
|
},
|
|
values: {
|
|
type: "array",
|
|
required: true,
|
|
items: { type: "string" },
|
|
description: "Option value attributes to select; at least one value is required.",
|
|
},
|
|
session: SESSION_PARAM,
|
|
tabId: TAB_ID_PARAM,
|
|
timeoutMs: TIMEOUT_MS_PARAM,
|
|
},
|
|
output: {
|
|
schema: {
|
|
type: "object",
|
|
additionalProperties: false,
|
|
properties: {
|
|
session: { type: "string", required: true },
|
|
tabId: { type: "integer", required: true },
|
|
multiple: { type: "boolean", required: true },
|
|
selectedValues: {
|
|
type: "array",
|
|
required: true,
|
|
items: { type: "string" },
|
|
},
|
|
selectedLabels: {
|
|
type: "array",
|
|
required: true,
|
|
items: { type: "string" },
|
|
},
|
|
},
|
|
},
|
|
render: (_args, value) => [
|
|
{
|
|
type: "text",
|
|
text:
|
|
`[session ${value.session}] selected ` +
|
|
`${value.selectedValues.join(", ") || "(none)"} on tab ${value.tabId}`,
|
|
},
|
|
],
|
|
},
|
|
async execute(args, exec) {
|
|
requireNonEmpty(args.target, "target");
|
|
requirePositive(args.timeoutMs, "timeoutMs");
|
|
if (args.values.length === 0) throw new Error("values must contain at least one option");
|
|
const sessionId = registry.resolve(args.session, "browser_interact(action=select)");
|
|
const cmdArgs = ["select", "--session", sessionId];
|
|
appendTabId(cmdArgs, args.tabId);
|
|
for (const value of args.values) cmdArgs.push("--value", value);
|
|
if (args.timeoutMs !== undefined) cmdArgs.push("--timeout", `${args.timeoutMs}ms`);
|
|
appendTarget(cmdArgs, args.target);
|
|
const reply = (await runtime.run(
|
|
exec,
|
|
cmdArgs,
|
|
"select",
|
|
sessionId,
|
|
runnerTimeout(deps, args.timeoutMs),
|
|
)) as {
|
|
tab_id: number;
|
|
multiple: boolean;
|
|
selected_values: string[];
|
|
selected_labels: string[];
|
|
};
|
|
return {
|
|
session: sessionId,
|
|
tabId: reply.tab_id,
|
|
multiple: reply.multiple,
|
|
selectedValues: reply.selected_values,
|
|
selectedLabels: reply.selected_labels,
|
|
};
|
|
},
|
|
presentCall: (args) => ({
|
|
card: "terminal",
|
|
title: runtime.commandLine([
|
|
"select",
|
|
args.target,
|
|
"--session",
|
|
args.session ?? "(current)",
|
|
]),
|
|
description: `Select ${args.values.length} option${args.values.length === 1 ? "" : "s"}`,
|
|
}),
|
|
presentResult: runtime.presentTerminalResult,
|
|
}),
|
|
);
|
|
}
|