refactor(service): unify submit bootstrap target resolution
Use page context, deterministic plans, and direct-skill metadata as the service-owned bootstrap target precedence so callback-host startup no longer relies on line-loss text matching or the old request-url helper. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
use reqwest::Url;
|
||||
use serde::Deserialize;
|
||||
use serde_json::{Map, Value};
|
||||
use zeroclaw::skills::{load_skills_from_directory, SkillTool};
|
||||
|
||||
@@ -12,6 +14,12 @@ use crate::compat::runtime::CompatTaskContext;
|
||||
use crate::config::SgClawSettings;
|
||||
use crate::pipe::{BrowserPipeTool, PipeError, Transport};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct DirectSubmitBootstrapMetadata {
|
||||
pub bootstrap_url: String,
|
||||
pub expected_domain: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct DirectSubmitOutcome {
|
||||
pub success: bool,
|
||||
@@ -111,6 +119,32 @@ pub fn execute_browser_script_skill_raw_output_with_browser_backend(
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_direct_submit_bootstrap_metadata(
|
||||
configured_tool: &str,
|
||||
workspace_root: &Path,
|
||||
settings: &SgClawSettings,
|
||||
) -> Result<Option<DirectSubmitBootstrapMetadata>, PipeError> {
|
||||
let (tool, skill_root) = resolve_browser_script_skill(configured_tool, workspace_root, settings)?;
|
||||
let manifest_path = skill_root.join("SKILL.toml");
|
||||
let Ok(manifest) = fs::read_to_string(&manifest_path) else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Ok(parsed) = toml::from_str::<DirectSubmitSkillManifest>(&manifest) else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let metadata = parsed
|
||||
.tools
|
||||
.into_iter()
|
||||
.find(|candidate| candidate.name == tool.name)
|
||||
.and_then(|candidate| candidate.metadata)
|
||||
.and_then(|metadata| {
|
||||
normalize_bootstrap_metadata(metadata.bootstrap_url, metadata.expected_domain)
|
||||
});
|
||||
|
||||
Ok(metadata)
|
||||
}
|
||||
|
||||
fn resolve_browser_script_skill(
|
||||
configured_tool: &str,
|
||||
workspace_root: &Path,
|
||||
@@ -306,6 +340,50 @@ fn count_summary_rows(counts: Option<&Value>, sections: Option<&Value>) -> usize
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct DirectSubmitSkillManifest {
|
||||
#[serde(default)]
|
||||
tools: Vec<DirectSubmitSkillManifestTool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct DirectSubmitSkillManifestTool {
|
||||
name: String,
|
||||
#[serde(default)]
|
||||
metadata: Option<DirectSubmitToolMetadata>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct DirectSubmitToolMetadata {
|
||||
#[serde(default)]
|
||||
bootstrap_url: Option<String>,
|
||||
#[serde(default)]
|
||||
expected_domain: Option<String>,
|
||||
}
|
||||
|
||||
fn normalize_bootstrap_metadata(
|
||||
bootstrap_url: Option<String>,
|
||||
expected_domain: Option<String>,
|
||||
) -> Option<DirectSubmitBootstrapMetadata> {
|
||||
let bootstrap_url = bootstrap_url
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())?;
|
||||
let parsed = Url::parse(bootstrap_url).ok()?;
|
||||
if parsed.scheme().is_empty() || parsed.host_str().is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(DirectSubmitBootstrapMetadata {
|
||||
bootstrap_url: parsed.to_string(),
|
||||
expected_domain: expected_domain
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(ToString::to_string),
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_configured_tool_name(configured_tool: &str) -> Result<(&str, &str), PipeError> {
|
||||
let (skill_name, tool_name) = configured_tool.split_once('.').ok_or_else(|| {
|
||||
PipeError::Protocol(format!(
|
||||
|
||||
Reference in New Issue
Block a user