mirror of
https://github.com/openai/codex.git
synced 2026-10-30 19:18:49 +00:00
Propagate exec host through app-server items
This commit is contained in:
@@ -2865,6 +2865,14 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"hostId": {
|
||||
"default": null,
|
||||
"description": "The configured exec host, when the command ran outside the local host.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -13127,6 +13127,14 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"hostId": {
|
||||
"default": null,
|
||||
"description": "The configured exec host, when the command ran outside the local host.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -10982,6 +10982,14 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"hostId": {
|
||||
"default": null,
|
||||
"description": "The configured exec host, when the command ran outside the local host.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -684,6 +684,14 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"hostId": {
|
||||
"default": null,
|
||||
"description": "The configured exec host, when the command ran outside the local host.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -684,6 +684,14 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"hostId": {
|
||||
"default": null,
|
||||
"description": "The configured exec host, when the command ran outside the local host.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -827,6 +827,14 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"hostId": {
|
||||
"default": null,
|
||||
"description": "The configured exec host, when the command ran outside the local host.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -1341,6 +1341,14 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"hostId": {
|
||||
"default": null,
|
||||
"description": "The configured exec host, when the command ran outside the local host.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -1099,6 +1099,14 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"hostId": {
|
||||
"default": null,
|
||||
"description": "The configured exec host, when the command ran outside the local host.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -1099,6 +1099,14 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"hostId": {
|
||||
"default": null,
|
||||
"description": "The configured exec host, when the command ran outside the local host.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -1099,6 +1099,14 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"hostId": {
|
||||
"default": null,
|
||||
"description": "The configured exec host, when the command ran outside the local host.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -1341,6 +1341,14 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"hostId": {
|
||||
"default": null,
|
||||
"description": "The configured exec host, when the command ran outside the local host.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -1099,6 +1099,14 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"hostId": {
|
||||
"default": null,
|
||||
"description": "The configured exec host, when the command ran outside the local host.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -1341,6 +1341,14 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"hostId": {
|
||||
"default": null,
|
||||
"description": "The configured exec host, when the command ran outside the local host.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -1099,6 +1099,14 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"hostId": {
|
||||
"default": null,
|
||||
"description": "The configured exec host, when the command ran outside the local host.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -1099,6 +1099,14 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"hostId": {
|
||||
"default": null,
|
||||
"description": "The configured exec host, when the command ran outside the local host.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -827,6 +827,14 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"hostId": {
|
||||
"default": null,
|
||||
"description": "The configured exec host, when the command ran outside the local host.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -827,6 +827,14 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"hostId": {
|
||||
"default": null,
|
||||
"description": "The configured exec host, when the command ran outside the local host.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -827,6 +827,14 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"hostId": {
|
||||
"default": null,
|
||||
"description": "The configured exec host, when the command ran outside the local host.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -34,7 +34,11 @@ cwd: string,
|
||||
/**
|
||||
* Identifier for the underlying PTY process (when available).
|
||||
*/
|
||||
processId: string | null, source: CommandExecutionSource, status: CommandExecutionStatus,
|
||||
processId: string | null,
|
||||
/**
|
||||
* The configured exec host, when the command ran outside the local host.
|
||||
*/
|
||||
hostId: string | null, source: CommandExecutionSource, status: CommandExecutionStatus,
|
||||
/**
|
||||
* A best-effort parsing of the command to understand the action(s) it will perform.
|
||||
* This returns a list of CommandAction objects because a single shell command may
|
||||
|
||||
@@ -71,6 +71,7 @@ pub fn build_command_execution_approval_request_item(
|
||||
command: shlex_join(&payload.command),
|
||||
cwd: payload.cwd.clone(),
|
||||
process_id: None,
|
||||
host_id: None,
|
||||
source: CommandExecutionSource::Agent,
|
||||
status: CommandExecutionStatus::InProgress,
|
||||
command_actions: payload
|
||||
@@ -91,6 +92,7 @@ pub fn build_command_execution_begin_item(payload: &ExecCommandBeginEvent) -> Th
|
||||
command: shlex_join(&payload.command),
|
||||
cwd: payload.cwd.clone(),
|
||||
process_id: payload.process_id.clone(),
|
||||
host_id: payload.host_id.clone(),
|
||||
source: payload.source.into(),
|
||||
status: CommandExecutionStatus::InProgress,
|
||||
command_actions: payload
|
||||
@@ -118,6 +120,7 @@ pub fn build_command_execution_end_item(payload: &ExecCommandEndEvent) -> Thread
|
||||
command: shlex_join(&payload.command),
|
||||
cwd: payload.cwd.clone(),
|
||||
process_id: payload.process_id.clone(),
|
||||
host_id: payload.host_id.clone(),
|
||||
source: payload.source.into(),
|
||||
status: (&payload.status).into(),
|
||||
command_actions: payload
|
||||
@@ -151,6 +154,7 @@ pub fn build_item_from_guardian_event(
|
||||
command,
|
||||
cwd: cwd.clone(),
|
||||
process_id: None,
|
||||
host_id: None,
|
||||
source: CommandExecutionSource::Agent,
|
||||
status,
|
||||
command_actions,
|
||||
@@ -183,6 +187,7 @@ pub fn build_item_from_guardian_event(
|
||||
command,
|
||||
cwd: cwd.clone(),
|
||||
process_id: None,
|
||||
host_id: None,
|
||||
source: CommandExecutionSource::Agent,
|
||||
status,
|
||||
command_actions,
|
||||
|
||||
@@ -1837,6 +1837,7 @@ mod tests {
|
||||
command: "echo 'hello world'".into(),
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
process_id: Some("pid-1".into()),
|
||||
host_id: None,
|
||||
source: CommandExecutionSource::Agent,
|
||||
status: CommandExecutionStatus::Completed,
|
||||
command_actions: vec![CommandAction::Unknown {
|
||||
@@ -2050,6 +2051,7 @@ mod tests {
|
||||
command: "ls".into(),
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
process_id: Some("pid-2".into()),
|
||||
host_id: None,
|
||||
source: CommandExecutionSource::Agent,
|
||||
status: CommandExecutionStatus::Declined,
|
||||
command_actions: vec![CommandAction::Unknown {
|
||||
@@ -2135,6 +2137,7 @@ mod tests {
|
||||
command: "rm -rf /tmp/guardian".into(),
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
process_id: None,
|
||||
host_id: None,
|
||||
source: CommandExecutionSource::Agent,
|
||||
status: CommandExecutionStatus::Declined,
|
||||
command_actions: vec![CommandAction::Unknown {
|
||||
@@ -2194,6 +2197,7 @@ mod tests {
|
||||
command: "/bin/rm -f /tmp/file.sqlite".into(),
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
process_id: None,
|
||||
host_id: None,
|
||||
source: CommandExecutionSource::Agent,
|
||||
status: CommandExecutionStatus::InProgress,
|
||||
command_actions: vec![CommandAction::Unknown {
|
||||
@@ -2284,6 +2288,7 @@ mod tests {
|
||||
command: "echo done".into(),
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
process_id: Some("pid-42".into()),
|
||||
host_id: None,
|
||||
source: CommandExecutionSource::Agent,
|
||||
status: CommandExecutionStatus::Completed,
|
||||
command_actions: vec![CommandAction::Unknown {
|
||||
|
||||
@@ -4409,6 +4409,9 @@ pub enum ThreadItem {
|
||||
cwd: PathBuf,
|
||||
/// Identifier for the underlying PTY process (when available).
|
||||
process_id: Option<String>,
|
||||
/// The configured exec host, when the command ran outside the local host.
|
||||
#[serde(default)]
|
||||
host_id: Option<String>,
|
||||
#[serde(default)]
|
||||
source: CommandExecutionSource,
|
||||
status: CommandExecutionStatus,
|
||||
|
||||
@@ -133,7 +133,7 @@ fn read_file_bytes(path: &Path) -> Result<Vec<u8>> {
|
||||
// fixture test is platform-independent.
|
||||
let text = String::from_utf8(bytes)
|
||||
.with_context(|| format!("expected UTF-8 TypeScript in {}", path.display()))?;
|
||||
let text = text.replace("\r\n", "\n").replace('\r', "\n");
|
||||
let text = normalize_typescript_fixture_text(&text);
|
||||
// Fixture comparisons care about schema content, not whether the generator
|
||||
// re-prepended the standard banner to every TypeScript file.
|
||||
let text = text
|
||||
@@ -276,10 +276,7 @@ fn collect_typescript_fixture_file<T: TS + 'static + ?Sized>(
|
||||
|
||||
let contents = T::export_to_string().context("export TypeScript fixture content")?;
|
||||
let output_path = normalize_relative_fixture_path(&output_path);
|
||||
files.insert(
|
||||
output_path,
|
||||
contents.replace("\r\n", "\n").replace('\r', "\n"),
|
||||
);
|
||||
files.insert(output_path, normalize_typescript_fixture_text(&contents));
|
||||
|
||||
let mut visitor = TypeScriptFixtureCollector {
|
||||
files,
|
||||
@@ -294,6 +291,20 @@ fn collect_typescript_fixture_file<T: TS + 'static + ?Sized>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn normalize_typescript_fixture_text(text: &str) -> String {
|
||||
let text = text.replace("\r\n", "\n").replace('\r', "\n");
|
||||
let mut normalized = String::with_capacity(text.len());
|
||||
for line in text.split_inclusive('\n') {
|
||||
if let Some(line) = line.strip_suffix('\n') {
|
||||
normalized.push_str(line.trim_end_matches(|ch| ch == ' ' || ch == '\t'));
|
||||
normalized.push('\n');
|
||||
} else {
|
||||
normalized.push_str(line.trim_end_matches(|ch| ch == ' ' || ch == '\t'));
|
||||
}
|
||||
}
|
||||
normalized
|
||||
}
|
||||
|
||||
fn normalize_relative_fixture_path(path: &Path) -> PathBuf {
|
||||
path.components().collect()
|
||||
}
|
||||
|
||||
@@ -1618,6 +1618,7 @@ pub(crate) async fn apply_bespoke_event_handling(
|
||||
let command = shlex_join(&exec_command_begin_event.command);
|
||||
let cwd = exec_command_begin_event.cwd;
|
||||
let process_id = exec_command_begin_event.process_id;
|
||||
let host_id = exec_command_begin_event.host_id;
|
||||
let first_start = {
|
||||
let mut state = thread_state.lock().await;
|
||||
state
|
||||
@@ -1631,6 +1632,7 @@ pub(crate) async fn apply_bespoke_event_handling(
|
||||
command,
|
||||
cwd,
|
||||
process_id,
|
||||
host_id,
|
||||
source: exec_command_begin_event.source.into(),
|
||||
status: CommandExecutionStatus::InProgress,
|
||||
command_actions,
|
||||
@@ -2002,6 +2004,7 @@ async fn start_command_execution_item(
|
||||
command,
|
||||
cwd,
|
||||
process_id: None,
|
||||
host_id: None,
|
||||
source,
|
||||
status: CommandExecutionStatus::InProgress,
|
||||
command_actions,
|
||||
@@ -2046,6 +2049,7 @@ async fn complete_command_execution_item(
|
||||
command,
|
||||
cwd,
|
||||
process_id,
|
||||
host_id: None,
|
||||
source,
|
||||
status,
|
||||
command_actions,
|
||||
@@ -3228,6 +3232,7 @@ mod tests {
|
||||
command: completion_item.command.clone(),
|
||||
cwd: completion_item.cwd.clone(),
|
||||
process_id: None,
|
||||
host_id: None,
|
||||
source: CommandExecutionSource::Agent,
|
||||
status: CommandExecutionStatus::InProgress,
|
||||
command_actions: completion_item.command_actions.clone(),
|
||||
|
||||
@@ -900,6 +900,7 @@ fn command_execution_started_event(turn_id: &str, item: &ThreadItem) -> Option<V
|
||||
command,
|
||||
cwd,
|
||||
process_id,
|
||||
host_id,
|
||||
source,
|
||||
command_actions,
|
||||
..
|
||||
@@ -922,7 +923,7 @@ fn command_execution_started_event(turn_id: &str, item: &ThreadItem) -> Option<V
|
||||
.map(codex_app_server_protocol::CommandAction::into_core)
|
||||
.collect(),
|
||||
source: source.to_core(),
|
||||
host_id: None,
|
||||
host_id: host_id.clone(),
|
||||
interaction_input: None,
|
||||
}),
|
||||
}])
|
||||
@@ -935,6 +936,7 @@ fn command_execution_completed_event(turn_id: &str, item: &ThreadItem) -> Option
|
||||
command,
|
||||
cwd,
|
||||
process_id,
|
||||
host_id,
|
||||
source,
|
||||
status,
|
||||
command_actions,
|
||||
@@ -983,7 +985,7 @@ fn command_execution_completed_event(turn_id: &str, item: &ThreadItem) -> Option
|
||||
.map(codex_app_server_protocol::CommandAction::into_core)
|
||||
.collect(),
|
||||
source: source.to_core(),
|
||||
host_id: None,
|
||||
host_id: host_id.clone(),
|
||||
interaction_input: None,
|
||||
stdout: String::new(),
|
||||
stderr: String::new(),
|
||||
@@ -1183,6 +1185,7 @@ mod tests {
|
||||
command: "printf 'hello world\\n'".to_string(),
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
process_id: None,
|
||||
host_id: None,
|
||||
source: CommandExecutionSource::UserShell,
|
||||
status: CommandExecutionStatus::InProgress,
|
||||
command_actions: vec![CommandAction::Unknown {
|
||||
@@ -1239,6 +1242,7 @@ mod tests {
|
||||
command: "printf 'hello world\\n'".to_string(),
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
process_id: None,
|
||||
host_id: None,
|
||||
source: CommandExecutionSource::UserShell,
|
||||
status: CommandExecutionStatus::Completed,
|
||||
command_actions: vec![CommandAction::Unknown {
|
||||
@@ -1276,6 +1280,7 @@ mod tests {
|
||||
command: r#"C:\Program Files\Git\bin\bash.exe -lc "echo hi""#.to_string(),
|
||||
cwd: PathBuf::from("C:\\repo"),
|
||||
process_id: None,
|
||||
host_id: None,
|
||||
source: CommandExecutionSource::UserShell,
|
||||
status: CommandExecutionStatus::InProgress,
|
||||
command_actions: vec![],
|
||||
@@ -1324,6 +1329,7 @@ mod tests {
|
||||
command: "printf 'hello world\\n'".to_string(),
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
process_id: None,
|
||||
host_id: None,
|
||||
source: CommandExecutionSource::UserShell,
|
||||
status: CommandExecutionStatus::Completed,
|
||||
command_actions: vec![CommandAction::Unknown {
|
||||
|
||||
@@ -6195,6 +6195,7 @@ impl ChatWidget {
|
||||
command,
|
||||
cwd,
|
||||
process_id,
|
||||
host_id,
|
||||
source,
|
||||
status,
|
||||
command_actions,
|
||||
@@ -6217,7 +6218,7 @@ impl ChatWidget {
|
||||
.map(codex_app_server_protocol::CommandAction::into_core)
|
||||
.collect(),
|
||||
source: source.to_core(),
|
||||
host_id: None,
|
||||
host_id,
|
||||
interaction_input: None,
|
||||
});
|
||||
} else {
|
||||
@@ -6233,7 +6234,7 @@ impl ChatWidget {
|
||||
.map(codex_app_server_protocol::CommandAction::into_core)
|
||||
.collect(),
|
||||
source: source.to_core(),
|
||||
host_id: None,
|
||||
host_id,
|
||||
interaction_input: None,
|
||||
stdout: String::new(),
|
||||
stderr: String::new(),
|
||||
@@ -6790,6 +6791,7 @@ impl ChatWidget {
|
||||
command,
|
||||
cwd,
|
||||
process_id,
|
||||
host_id,
|
||||
source,
|
||||
command_actions,
|
||||
..
|
||||
@@ -6805,7 +6807,7 @@ impl ChatWidget {
|
||||
.map(codex_app_server_protocol::CommandAction::into_core)
|
||||
.collect(),
|
||||
source: source.to_core(),
|
||||
host_id: None,
|
||||
host_id,
|
||||
interaction_input: None,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -193,6 +193,7 @@ async fn live_app_server_command_execution_strips_shell_wrapper() {
|
||||
command: command.clone(),
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
process_id: None,
|
||||
host_id: None,
|
||||
source: AppServerCommandExecutionSource::UserShell,
|
||||
status: AppServerCommandExecutionStatus::InProgress,
|
||||
command_actions: vec![AppServerCommandAction::Unknown {
|
||||
@@ -214,6 +215,7 @@ async fn live_app_server_command_execution_strips_shell_wrapper() {
|
||||
command,
|
||||
cwd: PathBuf::from("/tmp"),
|
||||
process_id: None,
|
||||
host_id: None,
|
||||
source: AppServerCommandExecutionSource::UserShell,
|
||||
status: AppServerCommandExecutionStatus::Completed,
|
||||
command_actions: vec![AppServerCommandAction::Unknown {
|
||||
@@ -240,6 +242,51 @@ async fn live_app_server_command_execution_strips_shell_wrapper() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn live_app_server_command_execution_shows_remote_host() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
chat.on_task_started();
|
||||
|
||||
let script = "echo remote";
|
||||
let command =
|
||||
shlex::try_join(["/bin/zsh", "-lc", script]).expect("round-trippable shell wrapper");
|
||||
|
||||
chat.handle_server_notification(
|
||||
ServerNotification::ItemCompleted(ItemCompletedNotification {
|
||||
thread_id: "thread-1".to_string(),
|
||||
turn_id: "turn-1".to_string(),
|
||||
item: AppServerThreadItem::CommandExecution {
|
||||
id: "cmd-remote".to_string(),
|
||||
command,
|
||||
cwd: PathBuf::from("/Users/nlieb"),
|
||||
process_id: Some("proc-remote".to_string()),
|
||||
host_id: Some("mac".to_string()),
|
||||
source: AppServerCommandExecutionSource::UnifiedExecStartup,
|
||||
status: AppServerCommandExecutionStatus::Completed,
|
||||
command_actions: vec![AppServerCommandAction::Unknown {
|
||||
command: script.to_string(),
|
||||
}],
|
||||
aggregated_output: Some("remote\n".to_string()),
|
||||
exit_code: Some(0),
|
||||
duration_ms: Some(5),
|
||||
},
|
||||
}),
|
||||
/*replay_kind*/ None,
|
||||
);
|
||||
|
||||
let cells = drain_insert_history(&mut rx);
|
||||
assert_eq!(
|
||||
cells.len(),
|
||||
1,
|
||||
"expected one completed command history cell"
|
||||
);
|
||||
let blob = lines_to_single_string(cells.first().expect("command cell"));
|
||||
assert!(
|
||||
blob.contains("• Ran on mac echo remote"),
|
||||
"expected remote host in app-server command row: {blob:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn app_server_patch_changes_to_core_preserves_diffs() {
|
||||
let changes = app_server_patch_changes_to_core(vec![FileUpdateChange {
|
||||
|
||||
Reference in New Issue
Block a user