@@ -122,8 +122,7 @@ fn decode_args_impl<T: DeserializeOwned>(args: &[u8]) -> Result<T> {
122122 . context ( "decode action args from cbor" ) ?;
123123 let value = revive_undefined_envelopes ( value) ;
124124 let mut normalized = Vec :: new ( ) ;
125- ciborium:: into_writer ( & value, & mut normalized)
126- . context ( "re-encode revived action args" ) ?;
125+ ciborium:: into_writer ( & value, & mut normalized) . context ( "re-encode revived action args" ) ?;
127126 abi:: codec:: decode_positional ( & normalized)
128127}
129128
@@ -664,6 +663,93 @@ pub mod contract {
664663 . collect ( )
665664 }
666665
666+ pub fn encoded_invalid_client_arg_variants ( name : & str ) -> Result < Vec < ( & ' static str , Vec < u8 > ) > > {
667+ let variants = match name {
668+ "readFile"
669+ | "stat"
670+ | "mkdir"
671+ | "readdir"
672+ | "readdirEntries"
673+ | "exists"
674+ | "readdirRecursive"
675+ | "closeShell"
676+ | "waitShell"
677+ | "cancelCronJob"
678+ | "closeSession"
679+ | "getSessionEvents"
680+ | "expireSignedPreviewUrl" => {
681+ vec ! [ ( "path/id must be a string" , json!( [ 42 ] ) ) ]
682+ }
683+ "writeFile" | "writeShell" => {
684+ vec ! [ (
685+ "content must be string or Uint8Array" ,
686+ json!( [ "/workspace/file.txt" , { "bad" : true } ] ) ,
687+ ) ]
688+ }
689+ "move" => vec ! [ (
690+ "destination must be a string" ,
691+ json!( [ "/workspace/a.txt" , 42 ] ) ,
692+ ) ] ,
693+ "deleteFile" => vec ! [ (
694+ "recursive option must be boolean" ,
695+ json!( [ "/workspace/file.txt" , { "recursive" : "yes" } ] ) ,
696+ ) ] ,
697+ "writeFiles" => vec ! [ (
698+ "entry path must be a string" ,
699+ json!( [ [ { "path" : 42 , "content" : "a" } ] ] ) ,
700+ ) ] ,
701+ "readFiles" => vec ! [ ( "paths must be strings" , json!( [ [ 42 ] ] ) ) ] ,
702+ "exec" => vec ! [ (
703+ "env option values must be strings" ,
704+ json!( [ "echo hello" , { "env" : { "A" : 42 } } ] ) ,
705+ ) ] ,
706+ "spawn" => vec ! [ ( "args must be strings" , json!( [ "node" , [ 42 ] ] ) ) ] ,
707+ "waitProcess" | "killProcess" | "stopProcess" | "getProcess" | "closeProcessStdin" => {
708+ vec ! [ ( "pid must be a number" , json!( [ "42" ] ) ) ]
709+ }
710+ "listProcesses"
711+ | "allProcesses"
712+ | "processTree"
713+ | "listCronJobs"
714+ | "listPersistedSessions"
715+ | "listMounts"
716+ | "listSoftware" => vec ! [ (
717+ "zero-arg action must not accept extras" ,
718+ json!( [ "unexpected" ] ) ,
719+ ) ] ,
720+ "writeProcessStdin" => {
721+ vec ! [ (
722+ "stdin content must be string or Uint8Array" ,
723+ json!( [ 42 , { "bad" : true } ] ) ,
724+ ) ]
725+ }
726+ "openShell" => vec ! [ ( "cols option must be numeric" , json!( [ { "cols" : "wide" } ] ) ) ] ,
727+ "resizeShell" => vec ! [ ( "cols must be numeric" , json!( [ "shell-1" , "80" , 24 ] ) ) ] ,
728+ "vmFetch" => vec ! [ ( "port must be numeric" , json!( [ "3000" , "http://127.0.0.1/" ] ) ) ] ,
729+ "scheduleCron" => vec ! [ (
730+ "cron job requires a schedule/action shape" ,
731+ json!( [ { "id" : "job-1" } ] ) ,
732+ ) ] ,
733+ "createSession" => vec ! [ ( "agent type must be a string" , json!( [ 42 ] ) ) ] ,
734+ "sendPrompt" => vec ! [ (
735+ "prompt must be a string" ,
736+ json!( [ "session-1" , { "text" : "hello" } ] ) ,
737+ ) ] ,
738+ "respondPermission" => vec ! [ (
739+ "permission response must be a string" ,
740+ json!( [ "session-1" , "permission-1" , 42 ] ) ,
741+ ) ] ,
742+ "createSignedPreviewUrl" => vec ! [ ( "ttl must be numeric" , json!( [ 3000 , "60" ] ) ) ] ,
743+ other => return Err ( anyhow ! ( "unknown action {other}" ) ) ,
744+ } ;
745+ variants
746+ . into_iter ( )
747+ . map ( |( case, value) | {
748+ abi:: codec:: encode_positional ( & value) . map ( |encoded| ( case, encoded) )
749+ } )
750+ . collect ( )
751+ }
752+
667753 pub fn encode_sample_reply ( name : & str ) -> Result < Vec < u8 > > {
668754 match name {
669755 "readFile" => encode ( & serde_bytes:: ByteBuf :: from ( vec ! [ 1 , 2 , 3 ] ) ) ,
0 commit comments