@@ -2,6 +2,7 @@ mod agent_packages;
22mod browser_adapter;
33mod federation_operator;
44mod http_api;
5+ mod supply_chain;
56
67use agent_packages:: load_agent_package;
78use browser_adapter:: serve_local_browser_adapter;
@@ -56,6 +57,9 @@ enum Command {
5657 WasmAbiVerify {
5758 wasm_paths : Vec < PathBuf > ,
5859 } ,
60+ ArtifactVerify {
61+ artifact_path : PathBuf ,
62+ } ,
5963 FederationPeers {
6064 manifest_path : PathBuf ,
6165 } ,
@@ -202,6 +206,7 @@ fn run_command(command: Command) -> Result<String, CliError> {
202206 request_path,
203207 } => execute_agent ( & manifest_path, & request_path) ,
204208 Command :: WasmAbiVerify { wasm_paths } => verify_wasm_abi_imports ( & wasm_paths) ,
209+ Command :: ArtifactVerify { artifact_path } => verify_supply_chain_artifact ( & artifact_path) ,
205210 Command :: FederationPeers { manifest_path } => {
206211 render_federation_peers ( & manifest_path) . map_err ( CliError :: IoError )
207212 }
@@ -263,6 +268,7 @@ fn parse_command(args: &[String]) -> Result<Command, String> {
263268 ( Some ( "serve" ) , _) => parse_serve_command ( args) ,
264269 ( Some ( "federation" ) , Some ( _) ) => parse_federation_command ( args) ,
265270 ( Some ( "agent" ) , Some ( "execute" ) ) => parse_agent_execute_command ( args) ,
271+ ( Some ( "artifact" ) , Some ( "verify" ) ) => parse_artifact_verify_command ( args) ,
266272 ( Some ( "wasm" ) , Some ( "abi" ) ) => parse_wasm_abi_command ( args) ,
267273 ( Some ( "expedition" ) , Some ( "execute" ) ) => parse_expedition_execute_command ( args) ,
268274 ( Some ( "capability" ) , Some ( "discover" ) ) => parse_capability_discover_command ( args) ,
@@ -279,6 +285,8 @@ fn subcommand_help(family: Option<&str>, subcommand: Option<&str>) -> String {
279285 ( Some ( "agent" ) , Some ( "inspect" ) ) => help_agent_inspect ( ) ,
280286 ( Some ( "agent" ) , Some ( "execute" ) ) => help_agent_execute ( ) ,
281287 ( Some ( "agent" ) , _) => help_agent ( ) ,
288+ ( Some ( "artifact" ) , Some ( "verify" ) ) => help_artifact_verify ( ) ,
289+ ( Some ( "artifact" ) , _) => help_artifact ( ) ,
282290 ( Some ( "wasm" ) , Some ( "abi" ) ) => help_wasm_abi ( ) ,
283291 ( Some ( "wasm" ) , _) => help_wasm ( ) ,
284292 ( Some ( "workflow" ) , Some ( "register" ) ) => help_workflow_register ( ) ,
@@ -402,6 +410,36 @@ fn help_agent() -> String {
402410 . to_string ( )
403411}
404412
413+ fn help_artifact_verify ( ) -> String {
414+ "traverse-cli artifact verify <artifact-or-manifest-path>
415+
416+ Purpose:
417+ Verify one governed artifact's supply-chain evidence. The command reads
418+ either a manifest JSON path or an artifact path with sidecars named
419+ <artifact>.manifest.json and <artifact>.provenance.json, then emits a
420+ structured JSON report for checksum, signature, and provenance checks.
421+
422+ Required arguments:
423+ <artifact-or-manifest-path> Artifact file or artifact manifest JSON path.
424+
425+ Optional flags:
426+ --help Print this help text.
427+
428+ Example:
429+ traverse-cli artifact verify target/release/traverse-cli"
430+ . to_string ( )
431+ }
432+
433+ fn help_artifact ( ) -> String {
434+ "traverse-cli artifact <subcommand> [options]
435+
436+ Subcommands:
437+ verify <artifact-or-manifest-path> Verify checksum, signature, and provenance evidence.
438+
439+ Run `traverse-cli artifact verify --help` for subcommand-specific help."
440+ . to_string ( )
441+ }
442+
405443fn help_wasm_abi ( ) -> String {
406444 "traverse-cli wasm abi verify <wasm-path>...
407445
@@ -824,6 +862,15 @@ fn parse_fixed_arity_command(args: &[String]) -> Result<Command, String> {
824862 }
825863}
826864
865+ fn parse_artifact_verify_command ( args : & [ String ] ) -> Result < Command , String > {
866+ match args {
867+ [ _, _, _, artifact_path] => Ok ( Command :: ArtifactVerify {
868+ artifact_path : PathBuf :: from ( artifact_path) ,
869+ } ) ,
870+ _ => Err ( usage ( ) ) ,
871+ }
872+ }
873+
827874fn parse_agent_execute_command ( args : & [ String ] ) -> Result < Command , String > {
828875 match args {
829876 [ _, _, _, manifest_path, request_path] => Ok ( Command :: AgentExecute {
@@ -1064,6 +1111,17 @@ fn verify_wasm_abi_imports(wasm_paths: &[PathBuf]) -> Result<String, CliError> {
10641111 Ok ( lines. join ( "\n " ) )
10651112}
10661113
1114+ fn verify_supply_chain_artifact ( artifact_path : & Path ) -> Result < String , CliError > {
1115+ let report = supply_chain:: verify_artifact ( artifact_path) ;
1116+ let json = serde_json:: to_string_pretty ( & report)
1117+ . map_err ( |e| CliError :: IoError ( format ! ( "failed to serialize artifact report: {e}" ) ) ) ?;
1118+ if report. passed ( ) {
1119+ Ok ( json)
1120+ } else {
1121+ Err ( CliError :: ValidationFailed ( json) )
1122+ }
1123+ }
1124+
10671125fn execute_expedition (
10681126 request_path : & Path ,
10691127 trace_output_path : Option < & Path > ,
@@ -2428,6 +2486,12 @@ mod tests {
24282486 "verify" . to_string( ) ,
24292487 "examples/hello-world/say-hello-agent/artifacts/say-hello-agent.wasm" . to_string( ) ,
24302488 ] ;
2489+ let artifact_verify = vec ! [
2490+ "traverse-cli" . to_string( ) ,
2491+ "artifact" . to_string( ) ,
2492+ "verify" . to_string( ) ,
2493+ "target/release/traverse-cli" . to_string( ) ,
2494+ ] ;
24312495 let expedition_execute = vec ! [
24322496 "traverse-cli" . to_string( ) ,
24332497 "expedition" . to_string( ) ,
@@ -2467,6 +2531,7 @@ mod tests {
24672531 assert ! ( parse_command( & agent_inspect) . is_ok( ) ) ;
24682532 assert ! ( parse_command( & agent_execute) . is_ok( ) ) ;
24692533 assert ! ( parse_command( & wasm_abi_verify) . is_ok( ) ) ;
2534+ assert ! ( parse_command( & artifact_verify) . is_ok( ) ) ;
24702535 assert ! ( parse_command( & expedition_execute) . is_ok( ) ) ;
24712536 assert ! ( parse_command( & expedition_execute_with_trace) . is_ok( ) ) ;
24722537 assert ! ( parse_command( & event) . is_ok( ) ) ;
@@ -2678,6 +2743,22 @@ mod tests {
26782743 assert ! ( text. contains( "Example:" ) ) ;
26792744 }
26802745
2746+ #[ test]
2747+ fn parse_command_returns_artifact_verify_help_on_help_flag ( ) {
2748+ let args = vec ! [
2749+ "traverse-cli" . to_string( ) ,
2750+ "artifact" . to_string( ) ,
2751+ "verify" . to_string( ) ,
2752+ "--help" . to_string( ) ,
2753+ ] ;
2754+ let result = parse_command ( & args) ;
2755+ assert ! ( result. is_err( ) , "expected Err for --help" ) ;
2756+ let text = result. err ( ) . unwrap_or_default ( ) ;
2757+ assert ! ( text. contains( "artifact verify" ) ) ;
2758+ assert ! ( text. contains( "<artifact-or-manifest-path>" ) ) ;
2759+ assert ! ( text. contains( "Example:" ) ) ;
2760+ }
2761+
26812762 #[ test]
26822763 fn parse_command_returns_workflow_inspect_help_on_help_flag ( ) {
26832764 let args = vec ! [
0 commit comments