Skip to main content

objectiveai_cli/filesystem/tools/
install.rs

1//! Tool GitHub-install — the tool side of the shared install engine
2//! ([`crate::filesystem::install`]). Tools are simpler than plugins: a
3//! `cli_zip` bundle plus the manifest, no viewer. These methods are
4//! thin wrappers driving the generic engine with `tools::Manifest`.
5
6use super::super::Client;
7use super::Manifest;
8use crate::filesystem::install::{
9    ExtraAsset, InstallKind, InstallManifest, platform_cli_zip,
10    validate_install_inputs,
11};
12
13impl Client {
14    /// Install a tool from a GitHub repository: fetch `objectiveai.json`,
15    /// download the current platform's `cli_zip`, extract it into the
16    /// tool's `cli/` dir, and persist the manifest verbatim. Thin
17    /// wrapper over [`Client::install_from_github_at`]. The `bool` is
18    /// always `true` on success (retained for symmetry with plugins).
19    pub async fn install_tool(
20        &self,
21        owner: &str,
22        repository: &str,
23        commit_sha: Option<&str>,
24        headers: Option<&indexmap::IndexMap<String, String>>,
25        upgrade: bool,
26    ) -> Result<bool, super::super::Error> {
27        self.install_from_github_at::<Manifest>(
28            "https://raw.githubusercontent.com",
29            "https://github.com",
30            owner,
31            repository,
32            commit_sha,
33            headers,
34            upgrade,
35        )
36        .await
37    }
38
39    /// Fetch + parse `<owner>/<repo>/<ref>/objectiveai.json` as a
40    /// tool manifest. Exposed so callers can inspect it before
41    /// committing to an install (e.g. for whitelist checks).
42    pub async fn fetch_tool_manifest(
43        &self,
44        owner: &str,
45        repository: &str,
46        commit_sha: Option<&str>,
47        headers: Option<&indexmap::IndexMap<String, String>>,
48    ) -> Result<Manifest, super::super::Error> {
49        self.fetch_manifest_at::<Manifest>(
50            "https://raw.githubusercontent.com",
51            owner,
52            repository,
53            commit_sha,
54            headers,
55        )
56        .await
57    }
58
59    /// Given an already-parsed tool manifest, download its `cli_zip` and
60    /// persist it.
61    pub async fn install_tool_from_manifest(
62        &self,
63        owner: &str,
64        repository: &str,
65        manifest: &Manifest,
66        headers: Option<&indexmap::IndexMap<String, String>>,
67        upgrade: bool,
68    ) -> Result<bool, super::super::Error> {
69        // Public entry — callers may hand us a manifest with no fetch
70        // ever happening, so validate inputs here (cheap + idempotent).
71        validate_install_inputs(InstallKind::Tool, owner, repository, None)?;
72        self.install_parsed_at::<Manifest>(
73            "https://github.com",
74            owner,
75            repository,
76            manifest,
77            headers,
78            upgrade,
79        )
80        .await
81    }
82
83    /// Test-only entry threading mock URL bases through the engine.
84    #[cfg(test)]
85    pub(super) async fn install_tool_at(
86        &self,
87        raw_base: &str,
88        releases_base: &str,
89        owner: &str,
90        repository: &str,
91        commit_sha: Option<&str>,
92        headers: Option<&indexmap::IndexMap<String, String>>,
93        upgrade: bool,
94    ) -> Result<bool, super::super::Error> {
95        self.install_from_github_at::<Manifest>(
96            raw_base,
97            releases_base,
98            owner,
99            repository,
100            commit_sha,
101            headers,
102            upgrade,
103        )
104        .await
105    }
106
107    /// Test-only fetch-only entry, mirrors `install_tool_at`.
108    #[cfg(test)]
109    pub(super) async fn fetch_tool_manifest_at(
110        &self,
111        raw_base: &str,
112        owner: &str,
113        repository: &str,
114        commit_sha: Option<&str>,
115        headers: Option<&indexmap::IndexMap<String, String>>,
116    ) -> Result<Manifest, super::super::Error> {
117        self.fetch_manifest_at::<Manifest>(
118            raw_base, owner, repository, commit_sha, headers,
119        )
120        .await
121    }
122}
123
124/// Tools install via the shared engine: just the cli bundle, under the
125/// `tools/` dir tree — no viewer, no reserved-name check.
126impl InstallManifest for Manifest {
127    const KIND: InstallKind = InstallKind::Tool;
128    fn validate_manifest(&self) -> Result<(), &'static str> {
129        self.validate()
130    }
131    fn version(&self) -> &str {
132        &self.version
133    }
134    fn cli_zip_for_platform(&self) -> Option<&str> {
135        platform_cli_zip(&self.cli_zip)
136    }
137    fn extra_assets(&self) -> Vec<ExtraAsset> {
138        Vec::new()
139    }
140}