report installed version from update install

This commit is contained in:
Will Wang
2026-06-23 12:45:53 -07:00
parent f0e0092d80
commit bd4bb1c8a7
8 changed files with 106 additions and 12 deletions
@@ -20251,6 +20251,18 @@
},
"UpdateInstallResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"installedVersion": {
"type": "string"
},
"success": {
"type": "boolean"
}
},
"required": [
"installedVersion",
"success"
],
"title": "UpdateInstallResponse",
"type": "object"
},
@@ -18030,6 +18030,18 @@
},
"UpdateInstallResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"installedVersion": {
"type": "string"
},
"success": {
"type": "boolean"
}
},
"required": [
"installedVersion",
"success"
],
"title": "UpdateInstallResponse",
"type": "object"
},
@@ -1,5 +1,17 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"installedVersion": {
"type": "string"
},
"success": {
"type": "boolean"
}
},
"required": [
"installedVersion",
"success"
],
"title": "UpdateInstallResponse",
"type": "object"
}
@@ -2,4 +2,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type UpdateInstallResponse = Record<string, never>;
export type UpdateInstallResponse = { installedVersion: string, success: boolean, };
@@ -28,4 +28,7 @@ pub struct UpdateCheckResponse {
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct UpdateInstallResponse {}
pub struct UpdateInstallResponse {
pub installed_version: String,
pub success: bool,
}
+9 -4
View File
@@ -202,7 +202,7 @@ Example with notification opt-out:
- `model/list` — list available models (set `includeHidden: true` to include entries with `hidden: true`), with model-advertised string reasoning effort options in the catalog's intended progression order, `additionalSpeedTiers`, `serviceTiers`, optional `defaultServiceTier`, optional legacy `upgrade` model ids, optional `upgradeInfo` metadata (`model`, `upgradeCopy`, `modelLink`, `migrationMarkdown`), and optional `availabilityNux` metadata. Clients should preserve the `supportedReasoningEfforts` array order rather than deriving order from the effort names.
- `modelProvider/capabilities/read` — read provider-level capabilities for the currently configured model provider.
- `update/check` — force a fresh update check and return the current version, latest version available through the detected distribution channel, installation method, and whether app-server can apply the update automatically.
- `update/install` — run the updater associated with the detected installation method; returns `{}` after the updater exits successfully.
- `update/install` — run the updater associated with the detected installation method; returns the installed version and whether installation succeeded.
- `experimentalFeature/list` — list feature flags with stage metadata (`beta`, `underDevelopment`, `stable`, etc.), enabled/default-enabled state, and cursor pagination. Pass `threadId` when showing feature state for an existing loaded thread so `enabled` is computed from that thread's refreshed config, including project-local config for the thread's cwd; if omitted, the server uses its default config resolution context. For non-beta flags, `displayName`/`description`/`announcement` are `null`.
- `permissionProfile/list` — beta; list available permission profile ids with optional display `description` text and an `allowed` flag reflecting effective requirements, using cursor pagination. Pass `cwd` when the caller needs project-local `[permissions.<id>]` entries to be included in the current catalog view.
- `experimentalFeature/enablement/set` — patch the in-memory process-wide runtime feature enablement for currently supported feature keys. For each feature, precedence is: cloud requirements > --enable <feature_name> > config.toml > experimentalFeature/enablement/set (new) > code default. Invalid keys will be ignored.
@@ -1920,7 +1920,8 @@ request has no params:
`installationMethod` is one of `npm`, `bun`, `brew`, `standaloneUnix`,
`standaloneWindows`, or `other`. npm and Bun checks require the latest GitHub
release to be ready under npm's `latest` tag. Homebrew checks the cask version;
standalone and other installations check the latest GitHub release.
standalone and other installations check the latest GitHub release. The
`latestVersion` field is the version that an available update would install.
When `canAutoUpdate` is `true`, call `update/install` to run the
corresponding npm, Bun, Homebrew, or standalone installer. The updater's output
@@ -1930,11 +1931,15 @@ installed version.
```json
{ "method": "update/install", "id": 51 }
{ "id": 51, "result": {} }
{ "id": 51, "result": {
"installedVersion": "1.3.0",
"success": true
} }
```
For `installationMethod: "other"`, `update/install` returns an invalid-request
error because app-server cannot determine a safe updater command.
error because app-server cannot determine a safe updater command. Installer or
post-install version-check failures return a JSON-RPC error instead of a result.
## Auth endpoints
@@ -79,7 +79,44 @@ impl UpdateRequestProcessor {
)));
}
Ok(Some(UpdateInstallResponse {}.into()))
let version_output = Command::new("codex")
.arg("--version")
.output()
.await
.map_err(|err| {
internal_error(format!(
"Codex update succeeded, but the installed version could not be read: {err}"
))
})?;
if !version_output.status.success() {
return Err(internal_error(format!(
"Codex update succeeded, but `codex --version` failed with status {}",
version_output.status
)));
}
let version_stdout = String::from_utf8(version_output.stdout).map_err(|err| {
internal_error(format!(
"Codex update succeeded, but `codex --version` returned invalid UTF-8: {err}"
))
})?;
let installed_version = version_stdout
.trim()
.strip_prefix("codex-cli ")
.filter(|version| !version.is_empty())
.ok_or_else(|| {
internal_error(format!(
"Codex update succeeded, but `codex --version` returned an unexpected value: {version_stdout:?}"
))
})?
.to_string();
Ok(Some(
UpdateInstallResponse {
installed_version,
success: true,
}
.into(),
))
}
}
+18 -5
View File
@@ -15,20 +15,27 @@ use tokio::time::timeout;
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10);
fn write_executable(path: &std::path::Path, contents: &str) -> Result<()> {
fs::write(path, contents)?;
let mut permissions = fs::metadata(path)?.permissions();
permissions.set_mode(0o755);
fs::set_permissions(path, permissions)?;
Ok(())
}
#[tokio::test]
async fn update_install_runs_the_detected_npm_updater() -> Result<()> {
let codex_home = TempDir::new()?;
let bin_dir = codex_home.path().join("bin");
fs::create_dir_all(&bin_dir)?;
let npm_path = bin_dir.join("npm");
let codex_path = bin_dir.join("codex");
let invocation_path = codex_home.path().join("npm-invocation.txt");
fs::write(
write_executable(
&npm_path,
"#!/bin/sh\nprintf '%s\\n' \"$@\" > \"$CODEX_TEST_UPDATE_INVOCATION\"\n",
)?;
let mut permissions = fs::metadata(&npm_path)?.permissions();
permissions.set_mode(0o755);
fs::set_permissions(&npm_path, permissions)?;
write_executable(&codex_path, "#!/bin/sh\nprintf 'codex-cli 1.2.3\\n'\n")?;
let path = format!(
"{}:{}",
@@ -55,7 +62,13 @@ async fn update_install_runs_the_detected_npm_updater() -> Result<()> {
.await??;
let response = to_response::<UpdateInstallResponse>(response)?;
assert_eq!(response, UpdateInstallResponse {});
assert_eq!(
response,
UpdateInstallResponse {
installed_version: "1.2.3".to_string(),
success: true,
}
);
assert_eq!(
fs::read_to_string(invocation_path)?,
"install\n-g\n@openai/codex\n"