diff --git a/docs/cli/browser.md b/docs/cli/browser.md index ffdc93cfa4f..d1de7e44421 100644 --- a/docs/cli/browser.md +++ b/docs/cli/browser.md @@ -205,6 +205,7 @@ File + dialog helpers: ```bash openclaw browser upload /tmp/openclaw/uploads/file.pdf --ref +openclaw browser upload media://inbound/file.pdf --ref openclaw browser waitfordownload openclaw browser download report.pdf openclaw browser dialog --accept @@ -215,6 +216,10 @@ Managed Chrome profiles save ordinary click-triggered downloads into the OpenCla downloads directory (`/tmp/openclaw/downloads` by default, or the configured temp root). Use `waitfordownload` or `download` when the agent needs to wait for a specific file and return its path; those explicit waiters own the next download. +Uploads accept files from the OpenClaw temp uploads root and OpenClaw-managed +inbound media, including `media://inbound/` and sandbox-relative +`media/inbound/` references. Nested media refs, traversal, and arbitrary +local paths remain rejected. When an action opens a modal dialog, the action response returns `blockedByDialog` with `browserState.dialogs.pending`; pass `--dialog-id` to answer it directly. Dialogs handled outside OpenClaw appear under diff --git a/docs/tools/browser-control.md b/docs/tools/browser-control.md index 0bb611278a3..b5e583163fb 100644 --- a/docs/tools/browser-control.md +++ b/docs/tools/browser-control.md @@ -191,6 +191,7 @@ openclaw browser select 9 OptionA OptionB openclaw browser download e12 report.pdf openclaw browser waitfordownload report.pdf openclaw browser upload /tmp/openclaw/uploads/file.pdf +openclaw browser upload media://inbound/file.pdf openclaw browser fill --fields '[{"ref":"1","type":"text","value":"Ada"}]' openclaw browser dialog --accept openclaw browser dialog --dismiss --dialog-id d1 @@ -232,7 +233,12 @@ Notes: - `upload` and `dialog` are **arming** calls; run them before the click/press that triggers the chooser/dialog. If an action opens a modal, the action response includes `blockedByDialog` and `browserState.dialogs.pending`; pass that `dialogId` to respond directly. Dialogs handled outside OpenClaw appear under `browserState.dialogs.recent`. - `click`/`type`/etc require a `ref` from `snapshot` (numeric `12`, role ref `e12`, or actionable ARIA ref `ax12`). CSS selectors are intentionally not supported for actions. Use `click-coords` when the visible viewport position is the only reliable target. -- Download, trace, and upload paths are constrained to OpenClaw temp roots: `/tmp/openclaw{,/downloads,/uploads}` (fallback: `${os.tmpdir()}/openclaw/...`). +- Download and trace paths are constrained to OpenClaw temp roots: `/tmp/openclaw{,/downloads}` (fallback: `${os.tmpdir()}/openclaw/...`). +- `upload` accepts files from the OpenClaw temp uploads root and + OpenClaw-managed inbound media. Managed inbound media can be referenced as + `media://inbound/`, sandbox-relative `media/inbound/`, or a resolved + path inside the managed inbound media directory. Nested media refs, + traversal, symlinks, hardlinks, and arbitrary local paths are still rejected. - `upload` can also set file inputs directly via `--input-ref` or `--element`. Stable tab ids and labels survive Chromium raw-target replacement when OpenClaw diff --git a/extensions/browser/src/browser-runtime.ts b/extensions/browser/src/browser-runtime.ts index 5264fca998b..83642449105 100644 --- a/extensions/browser/src/browser-runtime.ts +++ b/extensions/browser/src/browser-runtime.ts @@ -53,7 +53,11 @@ export { resolveGoogleChromeExecutableForPlatform, } from "./browser/chrome.executables.js"; export { redactCdpUrl } from "./browser/cdp.helpers.js"; -export { DEFAULT_UPLOAD_DIR, resolveExistingPathsWithinRoot } from "./browser/paths.js"; +export { + DEFAULT_UPLOAD_DIR, + resolveExistingPathsWithinRoot, + resolveExistingUploadPaths, +} from "./browser/paths.js"; export { getBrowserProfileCapabilities } from "./browser/profile-capabilities.js"; export { applyBrowserProxyPaths, persistBrowserProxyFiles } from "./browser/proxy-files.js"; export { diff --git a/extensions/browser/src/cli/browser-cli-actions-input/register.files-downloads.ts b/extensions/browser/src/cli/browser-cli-actions-input/register.files-downloads.ts index 3d4c96abdef..84cf814386e 100644 --- a/extensions/browser/src/cli/browser-cli-actions-input/register.files-downloads.ts +++ b/extensions/browser/src/cli/browser-cli-actions-input/register.files-downloads.ts @@ -3,9 +3,8 @@ import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runti import { callBrowserRequest, type BrowserParentOpts } from "../browser-cli-shared.js"; import { danger, - DEFAULT_UPLOAD_DIR, defaultRuntime, - resolveExistingPathsWithinRoot, + resolveExistingUploadPaths, shortenHomePath, } from "../core-api.js"; import { resolveBrowserActionContext, withBrowserActionTimeoutSlack } from "./shared.js"; @@ -13,11 +12,7 @@ import { resolveBrowserActionContext, withBrowserActionTimeoutSlack } from "./sh const DEFAULT_BROWSER_HOOK_TIMEOUT_MS = 120000; async function normalizeUploadPaths(paths: string[]): Promise { - const result = await resolveExistingPathsWithinRoot({ - rootDir: DEFAULT_UPLOAD_DIR, - requestedPaths: paths, - scopeLabel: `uploads directory (${DEFAULT_UPLOAD_DIR})`, - }); + const result = await resolveExistingUploadPaths({ requestedPaths: paths }); if (!result.ok) { throw new Error(result.error); } @@ -91,7 +86,7 @@ export function registerBrowserFilesAndDownloadsCommands( .description("Arm file upload for the next file chooser") .argument( "", - "File paths to upload (must be within OpenClaw temp uploads dir, e.g. /tmp/openclaw/uploads/file.pdf)", + "File paths to upload from OpenClaw temp uploads or managed inbound media (e.g. /tmp/openclaw/uploads/file.pdf or media://inbound/)", ) .option("--ref ", "Ref id from snapshot to click after arming") .option("--input-ref ", "Ref id for to set directly") diff --git a/extensions/browser/src/cli/browser-cli-examples.ts b/extensions/browser/src/cli/browser-cli-examples.ts index 084f94041d7..d4ac122a452 100644 --- a/extensions/browser/src/cli/browser-cli-examples.ts +++ b/extensions/browser/src/cli/browser-cli-examples.ts @@ -27,6 +27,7 @@ export const browserActionExamples = [ "openclaw browser drag 10 11", "openclaw browser select 9 OptionA OptionB", "openclaw browser upload /tmp/openclaw/uploads/file.pdf", + "openclaw browser upload media://inbound/file.pdf", 'openclaw browser fill --fields \'[{"ref":"1","value":"Ada"}]\'', "openclaw browser dialog --accept", 'openclaw browser wait --text "Done"', diff --git a/extensions/browser/src/core-api.ts b/extensions/browser/src/core-api.ts index c11c6e6c6c3..e685e9f031f 100644 --- a/extensions/browser/src/core-api.ts +++ b/extensions/browser/src/core-api.ts @@ -42,6 +42,7 @@ export { resolveBrowserConfig, resolveBrowserControlAuth, resolveExistingPathsWithinRoot, + resolveExistingUploadPaths, resolveProfile, resolveRequestedBrowserProfile, startBrowserControlServiceFromConfig,