1use harn_vm::VmDictExt;
39
40use harn_vm::VmValue;
41
42use crate::error::HostlibError;
43use crate::registry::{BuiltinRegistry, HostlibCapability};
44
45pub(crate) mod args;
46mod cancel_handle;
47mod diagnostics;
48mod file_io;
49mod git;
50pub(crate) mod inspect_test_results;
51mod lang;
52mod list_handles;
53pub mod long_running;
54mod manage_packages;
55mod outline;
56pub(crate) mod payload;
57pub mod permissions;
58mod proc;
59mod read_command_output;
60mod response;
61mod run_build_command;
62mod run_command;
63pub(crate) use run_command::policy_blocked_response as policy_blocked_run_command_response;
64pub(crate) use run_command::request_is_background as run_command_request_is_background;
65pub(crate) mod run_test;
66mod search;
67mod test_parsers;
68mod toolchain_facts;
69mod wait_command;
70mod wait_command_output;
71
72pub use permissions::{FEATURE_TERMINAL_SESSION, FEATURE_TOOLS_DETERMINISTIC};
73
74#[derive(Default)]
76pub struct ToolsCapability;
77
78impl HostlibCapability for ToolsCapability {
79 fn module_name(&self) -> &'static str {
80 "tools"
81 }
82
83 fn register_builtins(&self, registry: &mut BuiltinRegistry) {
84 long_running::register_cleanup_hook();
87
88 registry.register_gated_fn("tools", "hostlib_tools_search", "search", search::run);
89 registry.register_gated_fn(
90 "tools",
91 "hostlib_tools_read_file",
92 "read_file",
93 file_io::read_file,
94 );
95 registry.register_gated_fn(
96 "tools",
97 "hostlib_tools_write_file",
98 "write_file",
99 file_io::write_file,
100 );
101 registry.register_gated_fn(
102 "tools",
103 "hostlib_tools_delete_file",
104 "delete_file",
105 file_io::delete_file,
106 );
107 registry.register_gated_fn(
108 "tools",
109 "hostlib_tools_list_directory",
110 "list_directory",
111 file_io::list_directory,
112 );
113 registry.register_gated_fn(
114 "tools",
115 "hostlib_tools_get_file_outline",
116 "get_file_outline",
117 outline::run,
118 );
119 registry.register_gated_fn("tools", "hostlib_tools_git", "git", git::run);
120
121 registry.register_gated_command_fn(
122 "tools",
123 "hostlib_tools_run_command",
124 "run_command",
125 run_command::handle,
126 );
127 registry.register_gated_fn(
128 "tools",
129 read_command_output::NAME,
130 "read_command_output",
131 read_command_output::handle,
132 );
133 registry.register_gated_fn(
134 "tools",
135 wait_command::NAME,
136 "wait_command",
137 wait_command::handle,
138 );
139 registry.register_gated_async_fn(
140 "tools",
141 wait_command_output::NAME,
142 "wait_command_output",
143 wait_command_output::handle,
144 );
145 registry.register_gated_fn(
146 "tools",
147 "hostlib_tools_run_test",
148 "run_test",
149 run_test::handle,
150 );
151 registry.register_gated_fn(
152 "tools",
153 "hostlib_tools_run_build_command",
154 "run_build_command",
155 run_build_command::handle,
156 );
157 registry.register_gated_fn(
158 "tools",
159 "hostlib_tools_inspect_test_results",
160 "inspect_test_results",
161 inspect_test_results::handle,
162 );
163 registry.register_gated_fn(
164 "tools",
165 "hostlib_tools_manage_packages",
166 "manage_packages",
167 manage_packages::handle,
168 );
169 registry.register_gated_fn(
170 "tools",
171 cancel_handle::NAME,
172 "cancel_handle",
173 cancel_handle::handle,
174 );
175 registry.register_gated_fn(
176 "tools",
177 list_handles::NAME,
178 "list_handles",
179 list_handles::handle,
180 );
181 registry.register_gated_fn(
182 "tools",
183 toolchain_facts::NAME,
184 "toolchain_facts",
185 toolchain_facts::handle,
186 );
187
188 registry.register_fn("tools", "hostlib_enable", "enable", handle_enable);
191 }
192}
193
194fn handle_enable(args: &[VmValue]) -> Result<VmValue, HostlibError> {
199 let feature = match args.first() {
200 Some(VmValue::String(s)) => s.to_string(),
201 Some(VmValue::Dict(dict)) => match dict.get("feature") {
202 Some(VmValue::String(s)) => s.to_string(),
203 _ => {
204 return Err(HostlibError::MissingParameter {
205 builtin: "hostlib_enable",
206 param: "feature",
207 });
208 }
209 },
210 _ => {
211 return Err(HostlibError::MissingParameter {
212 builtin: "hostlib_enable",
213 param: "feature",
214 });
215 }
216 };
217
218 let supported = feature == permissions::FEATURE_TOOLS_DETERMINISTIC
219 || cfg!(feature = "terminal-session") && feature == permissions::FEATURE_TERMINAL_SESSION;
220 if !supported {
221 return Err(HostlibError::InvalidParameter {
222 builtin: "hostlib_enable",
223 param: "feature",
224 message: format!(
225 "unknown feature `{feature}`; supported: [`tools:deterministic`{}]",
226 if cfg!(feature = "terminal-session") {
227 ", `terminal:session`"
228 } else {
229 ""
230 }
231 ),
232 });
233 }
234
235 let newly_enabled = permissions::enable(&feature);
236 let mut map: harn_vm::value::DictMap = harn_vm::value::DictMap::new();
237 map.put_str("feature", feature);
238 map.insert(harn_vm::value::intern_key("enabled"), VmValue::Bool(true));
239 map.insert(
240 harn_vm::value::intern_key("newly_enabled"),
241 VmValue::Bool(newly_enabled),
242 );
243 Ok(VmValue::dict(map))
244}