objectiveai_cli/filesystem/tools/manifest.rs
1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4/// Per-OS exec command. Reused from the SDK so the exec shape stays
5/// identical across the on-disk manifest and the `tools get` wire
6/// response.
7pub use objectiveai_sdk::cli::command::tools::get::Exec;
8
9/// Per-OS cli bundle: the GitHub-release `.zip` asset filename for each
10/// platform. At install time the current platform's entry (when
11/// present) is downloaded and extracted into the `cli/` working
12/// directory. Local to the CLI's filesystem layer (the on-disk shape);
13/// shared by both the tool and plugin manifests. The `cli_zip` field
14/// itself is required; each per-OS entry is optional — absent means
15/// nothing to fetch for that platform (e.g. a hand-placed source-run
16/// tool, or a viewer-only plugin).
17#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
18#[schemars(rename = "filesystem.tools.CliZip")]
19pub struct CliZip {
20 #[serde(default, skip_serializing_if = "Option::is_none")]
21 #[schemars(extend("omitempty" = true))]
22 pub windows: Option<String>,
23 #[serde(default, skip_serializing_if = "Option::is_none")]
24 #[schemars(extend("omitempty" = true))]
25 pub linux: Option<String>,
26 #[serde(default, skip_serializing_if = "Option::is_none")]
27 #[schemars(extend("omitempty" = true))]
28 pub macos: Option<String>,
29}
30
31/// Declarative metadata a local tool ships with — the on-disk
32/// `objectiveai.json` at
33/// `<base_dir>/tools/<owner>/<name>/<version>/objectiveai.json`. The
34/// CLI reads and writes this shape verbatim; the `tools get` wire
35/// response is a lean projection of it (it drops `cli_zip`).
36#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
37#[schemars(rename = "filesystem.tools.Manifest")]
38pub struct Manifest {
39 /// GitHub-style owner (user or org) of the tool's source repo.
40 pub owner: String,
41 /// The tool's identifier (matches the `<name>` directory segment).
42 pub name: String,
43 /// Version string. Free-form; semver recommended, not enforced.
44 pub version: String,
45 /// One-line description of what the tool does. Surfaced to agents
46 /// that consume the tool.
47 pub description: String,
48 /// Per-OS exec command. The current platform's vector is the
49 /// program plus its leading arguments; the caller's `--args` are
50 /// appended and the whole thing runs with CWD = this version
51 /// folder's `cli/` subdir.
52 pub exec: Exec,
53 /// Per-OS cli bundle zip filenames (required field; each per-OS
54 /// entry optional). Hand-placed tools leave every entry absent.
55 pub cli_zip: CliZip,
56}
57
58impl Manifest {
59 /// Validate fields that serde alone can't enforce. Tools have no
60 /// cross-field invariants (unlike plugins' viewer rules), so this is
61 /// currently a no-op — it exists so the shared install/discovery
62 /// paths can call `validate()` uniformly across manifest kinds.
63 pub fn validate(&self) -> Result<(), &'static str> {
64 Ok(())
65 }
66}
67
68// Projection to the SDK's lean `tools get` wire response — drops the
69// on-disk-only `cli_zip`. `exec` is the same SDK `Exec` type, so it
70// moves across unchanged.
71impl From<Manifest> for objectiveai_sdk::cli::command::tools::get::ResponseManifest {
72 fn from(m: Manifest) -> Self {
73 Self {
74 owner: m.owner,
75 name: m.name,
76 version: m.version,
77 description: m.description,
78 exec: m.exec,
79 }
80 }
81}