use std::time::Duration;
use serde_json::{Value, json};
use crate::agents::{DEFAULT_PROMPT_TIMEOUT, DEFAULT_SIZE, PromptOptions, bedrock};
use crate::control::artifact::WrapOptions;
use crate::control::ensure::EnsureImageRequest;
use crate::control::{CreateImageRequest, DEFAULT_AGENT_PORT};
use crate::cost::{DEFAULT_REPORT_CYCLES, DEFAULT_RESIDENCY_CYCLES};
use crate::sandbox::{
DEFAULT_LIFECYCLE_TIMEOUT, LIFECYCLE_POLL_INTERVAL, RunRequest, TeardownOpts,
};
use crate::session::{DEFAULT_CLIENT_GRACE, DEFAULT_EXEC_WAIT, StreamOptions};
use crate::sizing::SizeClass;
pub fn client_defaults() -> Value {
let launch = RunRequest::default();
let start = protocol::exec::StartRequest::new(String::new(), Vec::new());
let stream = StreamOptions::default();
let teardown = TeardownOpts::default();
let prompt = PromptOptions::default();
let plan = crate::cost::PlanUsage::default();
let usage = crate::cost::RunUsage::default();
let image = CreateImageRequest::new(String::new(), Vec::new(), String::new(), String::new());
let ensured = EnsureImageRequest::new(
String::new(),
Vec::new(),
String::new(),
String::new(),
String::new(),
);
json!({
"sizeMib": SizeClass::DEFAULT.baseline_mib(),
"agentSizeMib": DEFAULT_SIZE.baseline_mib(),
"agentPort": DEFAULT_AGENT_PORT,
"listener": {
"bindAddress": crate::session::DEFAULT_BIND.to_string(),
"bindIp": crate::session::DEFAULT_BIND.ip().to_string(),
},
"execWaitSeconds": seconds(DEFAULT_EXEC_WAIT),
"readyTimeoutSeconds": seconds(crate::session::DEFAULT_BOOTSTRAP_TIMEOUT),
"launchTimeoutSeconds": seconds(crate::sandbox::DEFAULT_RUNNING_TIMEOUT),
"lifecycleTimeoutSeconds": seconds(DEFAULT_LIFECYCLE_TIMEOUT),
"lifecyclePollIntervalSeconds": seconds(LIFECYCLE_POLL_INTERVAL),
"promptTimeoutSeconds": seconds(DEFAULT_PROMPT_TIMEOUT),
"clientGraceSeconds": seconds(DEFAULT_CLIENT_GRACE),
"bedrockTokenTtlHours": bedrock::MAX_LIFETIME.as_secs() / 3600,
"residencyCycles": DEFAULT_RESIDENCY_CYCLES,
"reportCycles": DEFAULT_REPORT_CYCLES,
"keepAwakeToleratedErrors": crate::session::keepalive::DEFAULT_TOLERATED_ERRORS,
"syncDeleteTimeoutSeconds": seconds(crate::workspace::DEFAULT_SYNC_DELETE_TIMEOUT),
"launch": {
"maxIdleSeconds": launch.max_idle_sec,
"suspendedSeconds": launch.suspended_sec,
"maxDurationSeconds": launch.max_duration_sec,
"autoResume": launch.auto_resume,
"egress": launch.egress,
"denyEgress": launch.deny_egress,
"identity": launch.identity,
"shell": launch.shell,
"wait": launch.wait,
"loggingDisabled": matches!(
launch.logging,
Some(crate::control::ops::Logging::Disabled { .. })
),
},
"exec": {
"cwd": start.cwd,
"user": start.user,
"group": start.group,
"timeoutSeconds": start.timeout_sec,
"shell": matches!(start.shell, protocol::exec::Shell::Flag(true)),
"stdin": start.stdin,
"reapGroupOnExit": start.reap_group_on_exit,
"poweroffOnExit": start.poweroff_on_exit,
"inheritImageEnv": start.inherit_image_env,
},
"stream": {
"offset": stream.offset,
"reconnect": stream.reconnect,
"errorOnGap": stream.error_on_gap,
},
"teardown": {
"deleteImage": teardown.delete_image,
"deleteLogGroup": teardown.delete_log_group,
"waitForTerminated": teardown.wait_for_terminated.is_some(),
},
"prompt": {
"permissionMode": prompt.permission_mode.as_str(),
"execId": prompt.exec_id,
"timeoutSeconds": prompt.timeout.map(seconds),
"reapGroupOnExit": prompt.reap_group_on_exit,
},
"cost": {
"runningSeconds": plan.running_seconds,
"suspendedSeconds": plan.suspended_seconds,
"running": usage.running.map(crate::cost::DurationP::seconds_f64),
"suspended": usage.suspended.map(crate::cost::DurationP::seconds_f64),
"imageBuild": usage.image_build.map(crate::cost::DurationP::seconds_f64),
"imageGb": usage.image_gb,
"snapshotGb": usage.snapshot_gb,
},
"image": {
"workingDirectory": crate::control::artifact::BaseImage::al2023().working_dir,
"workdir": WrapOptions::default().workdir,
"inheritWorkdir": WrapOptions::default().inherit_workdir,
"repairGuestIdentity": image.repair_guest_identity,
"force": ensured.force,
},
})
}
fn seconds(duration: Duration) -> f64 {
duration.as_secs_f64()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_client_defaults_are_cores_own_values() {
let defaults = client_defaults();
assert_eq!(
defaults["execWaitSeconds"],
DEFAULT_EXEC_WAIT.as_secs_f64(),
"execWaitSeconds"
);
assert_eq!(defaults["agentPort"], DEFAULT_AGENT_PORT);
assert_eq!(defaults["sizeMib"], SizeClass::DEFAULT.baseline_mib());
let launch = RunRequest::default();
assert_eq!(defaults["launch"]["maxIdleSeconds"], launch.max_idle_sec);
assert_eq!(defaults["launch"]["wait"], launch.wait);
let start = protocol::exec::StartRequest::new("x", vec!["true".into()]);
assert_eq!(defaults["exec"]["cwd"], json!(start.cwd));
assert_eq!(defaults["exec"]["user"], json!(start.user));
assert_eq!(defaults["exec"]["group"], json!(start.group));
assert_eq!(defaults["exec"]["timeoutSeconds"], json!(start.timeout_sec));
assert_eq!(defaults["exec"]["stdin"], start.stdin);
assert_eq!(defaults["exec"]["shell"], false);
assert_eq!(
defaults["stream"]["offset"],
StreamOptions::default().offset
);
assert_eq!(
defaults["prompt"]["permissionMode"],
PromptOptions::default().permission_mode.as_str()
);
assert_eq!(
defaults["image"]["workingDirectory"],
crate::control::artifact::BaseImage::al2023().working_dir
);
assert_eq!(
defaults["syncDeleteTimeoutSeconds"],
crate::workspace::DEFAULT_SYNC_DELETE_TIMEOUT.as_secs_f64()
);
}
}