mirror of
https://github.com/Tencent/BrowserSkill.git
synced 2026-09-10 13:01:23 +08:00
72 lines
2.7 KiB
TypeScript
72 lines
2.7 KiB
TypeScript
// Download orchestration: validate session/tab ownership, then delegate one
|
|
// browser-global chrome.downloads transaction to download-capture.ts.
|
|
|
|
import type { SessionManager } from "@/session-manager/manager";
|
|
import type { ClickParams, DownloadParams, DownloadResult, RpcError } from "@/transport/types";
|
|
import { captureBrowserDownload, chromeDownloadsApi, type DownloadsApi } from "./download-capture";
|
|
import { handleClick, type InteractionDeps, resolveBackendNode } from "./interaction";
|
|
import { enforceAgentWindow, isRpcError, lookupSession, resolveTargetTab } from "./shared";
|
|
|
|
let downloadActive = false;
|
|
|
|
export type { DownloadsApi } from "./download-capture";
|
|
|
|
export interface DownloadDeps extends InteractionDeps {
|
|
downloads?: DownloadsApi;
|
|
}
|
|
|
|
export async function handleDownload(
|
|
manager: SessionManager,
|
|
params: DownloadParams,
|
|
deps: DownloadDeps,
|
|
): Promise<DownloadResult | RpcError> {
|
|
if (downloadActive) return { code: "invalid_params", message: "another bsk download is active" };
|
|
downloadActive = true;
|
|
try {
|
|
const ctx = lookupSession(manager, params, "download");
|
|
if (isRpcError(ctx)) return ctx;
|
|
const target = await resolveTargetTab(manager, ctx, params.tab_id, deps.tabsApi);
|
|
if (isRpcError(target)) return target;
|
|
const denied = enforceAgentWindow(ctx, target, "download");
|
|
if (denied) return denied;
|
|
if (!params.browser_relative_dir) {
|
|
return { code: "invalid_params", message: "download requires a daemon capability directory" };
|
|
}
|
|
const address = await resolveBackendNode(deps.cdp, ctx, target, params, "download");
|
|
if (isRpcError(address)) return address;
|
|
|
|
const capture = await captureBrowserDownload({
|
|
cdp: deps.cdp,
|
|
target: address.cdpTarget,
|
|
downloads: deps.downloads ?? chromeDownloadsApi,
|
|
browserRelativeDir: params.browser_relative_dir,
|
|
timeoutMs: params.timeout_ms ?? 120_000,
|
|
signal: deps.signal,
|
|
trigger: () => {
|
|
const clickParams: ClickParams = {
|
|
session_id: params.session_id,
|
|
ref: params.ref,
|
|
selector: params.selector,
|
|
tab_id: params.tab_id,
|
|
timeout_ms: params.timeout_ms,
|
|
};
|
|
return handleClick(manager, clickParams, deps);
|
|
},
|
|
});
|
|
if (isRpcError(capture)) return capture;
|
|
const { click, item } = capture;
|
|
return {
|
|
tab_id: target.tabId,
|
|
used_ref: click.used_ref,
|
|
used_selector: click.used_selector,
|
|
suggested_filename: item.filename.split(/[\\/]/).pop() ?? "download",
|
|
byte_size: item.fileSize >= 0 ? item.fileSize : item.totalBytes,
|
|
mime: item.mime || undefined,
|
|
danger: item.danger,
|
|
browser_path: item.filename,
|
|
};
|
|
} finally {
|
|
downloadActive = false;
|
|
}
|
|
}
|