diff --git a/src/agents/clawdis-tools.ts b/src/agents/clawdis-tools.ts index 8ff19fcb4..c3fcc5f02 100644 --- a/src/agents/clawdis-tools.ts +++ b/src/agents/clawdis-tools.ts @@ -77,6 +77,16 @@ type StringParamOptions = { label?: string; }; +function readStringParam( + params: Record, + key: string, + options: StringParamOptions & { required: true }, +): string; +function readStringParam( + params: Record, + key: string, + options?: StringParamOptions, +): string | undefined; function readStringParam( params: Record, key: string, @@ -783,7 +793,12 @@ function createCanvasTool(): AnyAgentTool { payload?: { result?: string }; }; const result = raw?.payload?.result; - if (result) return { content: [{ type: "text", text: result }] }; + if (result) { + return { + content: [{ type: "text", text: result }], + details: { result }, + }; + } return jsonResult({ ok: true }); } case "snapshot": { diff --git a/src/agents/models-config.ts b/src/agents/models-config.ts index 63c1ebcbe..1a0995d26 100644 --- a/src/agents/models-config.ts +++ b/src/agents/models-config.ts @@ -15,7 +15,7 @@ function isRecord(value: unknown): value is Record { return Boolean(value && typeof value === "object" && !Array.isArray(value)); } -async function readJson(pathname: string): Promise { +async function readJson(pathname: string): Promise { try { const raw = await fs.readFile(pathname, "utf8"); return JSON.parse(raw) as unknown; diff --git a/src/browser/routes/agent.ts b/src/browser/routes/agent.ts index 66a1ff883..c88775067 100644 --- a/src/browser/routes/agent.ts +++ b/src/browser/routes/agent.ts @@ -251,9 +251,11 @@ export function registerBrowserAgentRoutes( typeof rec.value === "boolean" ? rec.value : undefined; - return { ref, type, value }; + const parsed: BrowserFormField = + value === undefined ? { ref, type } : { ref, type, value }; + return parsed; }) - .filter((field): field is BrowserFormField => Boolean(field)); + .filter((field): field is BrowserFormField => field !== null); if (!fields.length) return jsonError(res, 400, "fields are required"); await pw.fillFormViaPlaywright({ cdpPort, diff --git a/src/cli/browser-cli-actions-input.ts b/src/cli/browser-cli-actions-input.ts index 59fc57479..fedd73b2e 100644 --- a/src/cli/browser-cli-actions-input.ts +++ b/src/cli/browser-cli-actions-input.ts @@ -6,6 +6,7 @@ import { browserArmFileChooser, browserNavigate, } from "../browser/client-actions.js"; +import type { BrowserFormField } from "../browser/client-actions-core.js"; import { danger } from "../globals.js"; import { defaultRuntime } from "../runtime.js"; import type { BrowserParentOpts } from "./browser-cli-shared.js"; @@ -18,14 +19,37 @@ async function readFile(path: string): Promise { async function readFields(opts: { fields?: string; fieldsFile?: string; -}): Promise>> { +}): Promise { const payload = opts.fieldsFile ? await readFile(opts.fieldsFile) : (opts.fields ?? ""); if (!payload.trim()) throw new Error("fields are required"); const parsed = JSON.parse(payload) as unknown; if (!Array.isArray(parsed)) throw new Error("fields must be an array"); - return parsed as Array>; + return parsed.map((entry, index) => { + if (!entry || typeof entry !== "object") { + throw new Error(`fields[${index}] must be an object`); + } + const rec = entry as Record; + const ref = typeof rec.ref === "string" ? rec.ref.trim() : ""; + const type = typeof rec.type === "string" ? rec.type.trim() : ""; + if (!ref || !type) { + throw new Error(`fields[${index}] must include ref and type`); + } + if ( + typeof rec.value === "string" || + typeof rec.value === "number" || + typeof rec.value === "boolean" + ) { + return { ref, type, value: rec.value }; + } + if (rec.value === undefined || rec.value === null) { + return { ref, type }; + } + throw new Error( + `fields[${index}].value must be string, number, boolean, or null`, + ); + }); } export function registerBrowserActionInputCommands( diff --git a/src/infra/ws.ts b/src/infra/ws.ts index a5986c269..99d753780 100644 --- a/src/infra/ws.ts +++ b/src/infra/ws.ts @@ -9,5 +9,8 @@ export function rawDataToString( if (typeof data === "string") return data; if (Buffer.isBuffer(data)) return data.toString(encoding); if (Array.isArray(data)) return Buffer.concat(data).toString(encoding); - return Buffer.from(data as ArrayBuffer | ArrayBufferView).toString(encoding); + if (data instanceof ArrayBuffer) { + return Buffer.from(data).toString(encoding); + } + return Buffer.from(String(data)).toString(encoding); }