mirror of
https://github.com/Tencent/BrowserSkill.git
synced 2026-09-10 13:01:23 +08:00
feat(bsk): support drop to upload
This commit is contained in:
@@ -182,7 +182,7 @@ pub enum Command {
|
||||
/// Set `<select>` option values by `value` attribute.
|
||||
Select(SelectArgs),
|
||||
|
||||
/// Upload one or more local files through a page file chooser.
|
||||
/// Upload files through a page file input or explicit drop target.
|
||||
Upload(UploadArgs),
|
||||
|
||||
/// Capture one browser download and write it to a local path.
|
||||
|
||||
@@ -52,6 +52,9 @@ pub mod reason {
|
||||
pub const FILE_INPUT_PROBE_FAILED: &str = "file_input_probe_failed";
|
||||
pub const FILE_INPUT_NOT_ACTIVATED: &str = "file_input_not_activated";
|
||||
pub const SET_FILE_INPUT_FAILED: &str = "set_file_input_failed";
|
||||
pub const UPLOAD_MECHANISM_UNSUPPORTED: &str = "upload_mechanism_unsupported";
|
||||
pub const FILE_DROP_TARGET_UNAVAILABLE: &str = "file_drop_target_unavailable";
|
||||
pub const FILE_DROP_FAILED: &str = "file_drop_failed";
|
||||
pub const DOWNLOAD_CAPTURE_FAILED: &str = "download_capture_failed";
|
||||
pub const TRANSFER_OUTCOME_UNKNOWN: &str = "transfer_outcome_unknown";
|
||||
pub const TRANSFER_TIMEOUT: &str = "transfer_timeout";
|
||||
@@ -323,6 +326,30 @@ pub fn info_for_error(code: ErrorCode, data: Option<&serde_json::Value>) -> Rend
|
||||
),
|
||||
exit_code: base.exit_code,
|
||||
},
|
||||
(ErrorCode::Unsupported, reason::UPLOAD_MECHANISM_UNSUPPORTED) => RenderInfo {
|
||||
summary: "the browser does not support native file-drop upload",
|
||||
hint: Some(
|
||||
"do not retry the same drop; use a standard file-input target or `bsk request-help`",
|
||||
),
|
||||
exit_code: base.exit_code,
|
||||
},
|
||||
(
|
||||
ErrorCode::PermissionDenied | ErrorCode::Timeout | ErrorCode::CdpFailed,
|
||||
reason::FILE_DROP_TARGET_UNAVAILABLE,
|
||||
) => RenderInfo {
|
||||
summary: "the file-drop target could not be safely resolved",
|
||||
hint: Some(
|
||||
"rerun observe and select the visible drop zone itself; no file was delivered when effect_state is none",
|
||||
),
|
||||
exit_code: base.exit_code,
|
||||
},
|
||||
(ErrorCode::Timeout | ErrorCode::CdpFailed, reason::FILE_DROP_FAILED) => RenderInfo {
|
||||
summary: "the browser could not complete the native file drop",
|
||||
hint: Some(
|
||||
"do not retry when effect_state is unknown; observe the page first, otherwise use `bsk request-help`",
|
||||
),
|
||||
exit_code: base.exit_code,
|
||||
},
|
||||
(ErrorCode::CdpFailed, reason::DOWNLOAD_CAPTURE_FAILED) => RenderInfo {
|
||||
summary: "the browser download could not be attributed or completed",
|
||||
hint: Some(
|
||||
@@ -487,6 +514,29 @@ mod tests {
|
||||
);
|
||||
assert!(info.hint.unwrap().contains("do not retry"));
|
||||
|
||||
let unsupported_drop =
|
||||
serde_json::json!({ "reason": reason::UPLOAD_MECHANISM_UNSUPPORTED });
|
||||
let info = info_for_error(ErrorCode::Unsupported, Some(&unsupported_drop));
|
||||
assert_eq!(
|
||||
info.summary,
|
||||
"the browser does not support native file-drop upload"
|
||||
);
|
||||
assert!(info.hint.unwrap().contains("standard file-input"));
|
||||
|
||||
let unavailable_target =
|
||||
serde_json::json!({ "reason": reason::FILE_DROP_TARGET_UNAVAILABLE });
|
||||
let info = info_for_error(ErrorCode::PermissionDenied, Some(&unavailable_target));
|
||||
assert_eq!(
|
||||
info.summary,
|
||||
"the file-drop target could not be safely resolved"
|
||||
);
|
||||
assert!(info.hint.unwrap().contains("effect_state is none"));
|
||||
|
||||
let failed_drop = serde_json::json!({ "reason": reason::FILE_DROP_FAILED });
|
||||
let info = info_for_error(ErrorCode::Timeout, Some(&failed_drop));
|
||||
assert!(info.summary.contains("native file drop"));
|
||||
assert!(info.hint.unwrap().contains("effect_state is unknown"));
|
||||
|
||||
let download = serde_json::json!({ "reason": reason::DOWNLOAD_CAPTURE_FAILED });
|
||||
let info = info_for_error(ErrorCode::CdpFailed, Some(&download));
|
||||
assert!(info.summary.contains("download could not be attributed"));
|
||||
|
||||
@@ -10,19 +10,35 @@ use base64::Engine;
|
||||
use bsk_protocol::Method;
|
||||
use bsk_protocol::tools::{
|
||||
TransferBeginParams, TransferBeginResult, TransferChunkParams, TransferChunkResult,
|
||||
TransferIdParams, TransferReadyResult, TransferReleaseResult, UploadFile, UploadParams,
|
||||
UploadResult,
|
||||
TransferIdParams, TransferReadyResult, TransferReleaseResult, UploadFile, UploadMode,
|
||||
UploadParams, UploadResult,
|
||||
};
|
||||
use clap::Args;
|
||||
use clap::{Args, ValueEnum};
|
||||
|
||||
use crate::cli::ensure_daemon::ensure_daemon;
|
||||
use crate::cli::error::{CliError, Format};
|
||||
use crate::cli::interaction::split_target;
|
||||
use crate::cli::navigate::parse_timeout_ms;
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, ValueEnum)]
|
||||
pub enum UploadModeArg {
|
||||
#[default]
|
||||
Input,
|
||||
Drop,
|
||||
}
|
||||
|
||||
impl From<UploadModeArg> for UploadMode {
|
||||
fn from(value: UploadModeArg) -> Self {
|
||||
match value {
|
||||
UploadModeArg::Input => Self::Input,
|
||||
UploadModeArg::Drop => Self::Drop,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Args)]
|
||||
pub struct UploadArgs {
|
||||
/// Snapshot ref (`@e3`) or CSS selector for the file input / chooser trigger.
|
||||
/// Snapshot ref (`@e3`) or CSS selector for the chooser trigger / drop target.
|
||||
pub target: Option<String>,
|
||||
#[arg(long = "ref")]
|
||||
pub ref_: Option<String>,
|
||||
@@ -31,6 +47,9 @@ pub struct UploadArgs {
|
||||
/// Local file to upload. Repeat for a multiple-file input.
|
||||
#[arg(long = "file", required = true)]
|
||||
pub files: Vec<PathBuf>,
|
||||
/// Browser mechanism used to deliver the staged files.
|
||||
#[arg(long, value_enum, default_value_t)]
|
||||
pub mode: UploadModeArg,
|
||||
#[arg(long)]
|
||||
pub session: String,
|
||||
#[arg(long = "tab-id")]
|
||||
@@ -73,6 +92,7 @@ pub fn dispatch(args: UploadArgs, format: Format) -> Result<(), CliError> {
|
||||
staged_path: None,
|
||||
})
|
||||
.collect(),
|
||||
mode: args.mode.into(),
|
||||
timeout_ms: Some(args.timeout),
|
||||
};
|
||||
let result = crate::cli::business_rpc::call::<_, UploadResult>(
|
||||
|
||||
Reference in New Issue
Block a user