1use crate::wasm_host::wit::since_v0_7_0::{
2 dap::{
3 AttachRequest, BuildTaskDefinition, BuildTaskDefinitionTemplatePayload, LaunchRequest,
4 StartDebuggingRequestArguments, TcpArguments, TcpArgumentsTemplate,
5 },
6 lsp::{CompletionKind, CompletionLabelDetails, InsertTextFormat, SymbolKind},
7 slash_command::SlashCommandOutputSection,
8};
9use crate::wasm_host::{WasmState, wit::ToWasmtimeResult};
10use ::http_client::{AsyncBody, HttpRequestExt};
11use ::settings::{Settings, WorktreeId};
12use anyhow::{Context as _, Result, bail};
13use async_compression::futures::bufread::GzipDecoder;
14use async_tar::Archive;
15use async_trait::async_trait;
16use credentials_provider::CredentialsProvider;
17use extension::{
18 ExtensionLanguageServerProxy, KeyValueStoreDelegate, ProjectDelegate, WorktreeDelegate,
19};
20use futures::{AsyncReadExt, lock::Mutex};
21use futures::{FutureExt as _, io::BufReader};
22use gpui::{BackgroundExecutor, SharedString};
23use language::{BinaryStatus, LanguageName, language_settings::AllLanguageSettings};
24use project::project_settings::ProjectSettings;
25use semver::Version;
26use std::{
27 env,
28 net::Ipv4Addr,
29 path::{Path, PathBuf},
30 str::FromStr,
31 sync::{Arc, OnceLock},
32};
33use task::{SpawnInTerminal, ZedDebugConfig};
34use url::Url;
35use util::{
36 archive::extract_zip, fs::make_file_executable, maybe, paths::PathStyle, rel_path::RelPath,
37};
38use wasmtime::component::{Linker, Resource};
39
40pub const MIN_VERSION: Version = Version::new(0, 7, 0);
41#[allow(dead_code)]
42pub const MAX_VERSION: Version = Version::new(0, 8, 0);
43
44wasmtime::component::bindgen!({
45 async: true,
46 trappable_imports: true,
47 path: "../extension_api/wit/since_v0.7.0",
48 with: {
49 "worktree": ExtensionWorktree,
50 "project": ExtensionProject,
51 "key-value-store": ExtensionKeyValueStore,
52 "zed:extension/http-client/http-response-stream": ExtensionHttpResponseStream,
53 },
54});
55
56// This is the latest version, so we pub use to make types available to parent module.
57// Note: The parent wit.rs module re-exports specific types from here as the "latest" types.
58pub use self::zed::extension::*;
59
60mod settings {
61 #![allow(dead_code)]
62 include!(concat!(env!("OUT_DIR"), "/since_v0.7.0/settings.rs"));
63}
64
65pub type ExtensionWorktree = Arc<dyn WorktreeDelegate>;
66pub type ExtensionProject = Arc<dyn ProjectDelegate>;
67pub type ExtensionKeyValueStore = Arc<dyn KeyValueStoreDelegate>;
68pub type ExtensionHttpResponseStream = Arc<Mutex<::http_client::Response<AsyncBody>>>;
69
70pub fn linker(executor: &BackgroundExecutor) -> &'static Linker<WasmState> {
71 static LINKER: OnceLock<Linker<WasmState>> = OnceLock::new();
72 LINKER.get_or_init(|| super::new_linker(executor, Extension::add_to_linker))
73}
74
75impl From<Range> for std::ops::Range<usize> {
76 fn from(range: Range) -> Self {
77 let start = range.start as usize;
78 let end = range.end as usize;
79 start..end
80 }
81}
82
83impl From<Command> for extension::Command {
84 fn from(value: Command) -> Self {
85 Self {
86 command: value.command.into(),
87 args: value.args,
88 env: value.env,
89 }
90 }
91}
92
93impl From<StartDebuggingRequestArgumentsRequest>
94 for extension::StartDebuggingRequestArgumentsRequest
95{
96 fn from(value: StartDebuggingRequestArgumentsRequest) -> Self {
97 match value {
98 StartDebuggingRequestArgumentsRequest::Launch => Self::Launch,
99 StartDebuggingRequestArgumentsRequest::Attach => Self::Attach,
100 }
101 }
102}
103impl TryFrom<StartDebuggingRequestArguments> for extension::StartDebuggingRequestArguments {
104 type Error = anyhow::Error;
105
106 fn try_from(value: StartDebuggingRequestArguments) -> Result<Self, Self::Error> {
107 Ok(Self {
108 configuration: serde_json::from_str(&value.configuration)?,
109 request: value.request.into(),
110 })
111 }
112}
113impl From<TcpArguments> for extension::TcpArguments {
114 fn from(value: TcpArguments) -> Self {
115 Self {
116 host: value.host.into(),
117 port: value.port,
118 timeout: value.timeout,
119 }
120 }
121}
122
123impl From<extension::TcpArgumentsTemplate> for TcpArgumentsTemplate {
124 fn from(value: extension::TcpArgumentsTemplate) -> Self {
125 Self {
126 host: value.host.map(Ipv4Addr::to_bits),
127 port: value.port,
128 timeout: value.timeout,
129 }
130 }
131}
132
133impl From<TcpArgumentsTemplate> for extension::TcpArgumentsTemplate {
134 fn from(value: TcpArgumentsTemplate) -> Self {
135 Self {
136 host: value.host.map(Ipv4Addr::from_bits),
137 port: value.port,
138 timeout: value.timeout,
139 }
140 }
141}
142
143impl TryFrom<extension::DebugTaskDefinition> for DebugTaskDefinition {
144 type Error = anyhow::Error;
145 fn try_from(value: extension::DebugTaskDefinition) -> Result<Self, Self::Error> {
146 Ok(Self {
147 label: value.label.to_string(),
148 adapter: value.adapter.to_string(),
149 config: value.config.to_string(),
150 tcp_connection: value.tcp_connection.map(Into::into),
151 })
152 }
153}
154
155impl From<task::DebugRequest> for DebugRequest {
156 fn from(value: task::DebugRequest) -> Self {
157 match value {
158 task::DebugRequest::Launch(launch_request) => Self::Launch(launch_request.into()),
159 task::DebugRequest::Attach(attach_request) => Self::Attach(attach_request.into()),
160 }
161 }
162}
163
164impl From<DebugRequest> for task::DebugRequest {
165 fn from(value: DebugRequest) -> Self {
166 match value {
167 DebugRequest::Launch(launch_request) => Self::Launch(launch_request.into()),
168 DebugRequest::Attach(attach_request) => Self::Attach(attach_request.into()),
169 }
170 }
171}
172
173impl From<task::LaunchRequest> for LaunchRequest {
174 fn from(value: task::LaunchRequest) -> Self {
175 Self {
176 program: value.program,
177 cwd: value.cwd.map(|p| p.to_string_lossy().into_owned()),
178 args: value.args,
179 envs: value.env.into_iter().collect(),
180 }
181 }
182}
183
184impl From<task::AttachRequest> for AttachRequest {
185 fn from(value: task::AttachRequest) -> Self {
186 Self {
187 process_id: value.process_id,
188 }
189 }
190}
191
192impl From<LaunchRequest> for task::LaunchRequest {
193 fn from(value: LaunchRequest) -> Self {
194 Self {
195 program: value.program,
196 cwd: value.cwd.map(|p| p.into()),
197 args: value.args,
198 env: value.envs.into_iter().collect(),
199 }
200 }
201}
202impl From<AttachRequest> for task::AttachRequest {
203 fn from(value: AttachRequest) -> Self {
204 Self {
205 process_id: value.process_id,
206 }
207 }
208}
209
210impl From<ZedDebugConfig> for DebugConfig {
211 fn from(value: ZedDebugConfig) -> Self {
212 Self {
213 label: value.label.into(),
214 adapter: value.adapter.into(),
215 request: value.request.into(),
216 stop_on_entry: value.stop_on_entry,
217 }
218 }
219}
220impl TryFrom<DebugAdapterBinary> for extension::DebugAdapterBinary {
221 type Error = anyhow::Error;
222 fn try_from(value: DebugAdapterBinary) -> Result<Self, Self::Error> {
223 Ok(Self {
224 command: value.command,
225 arguments: value.arguments,
226 envs: value.envs.into_iter().collect(),
227 cwd: value.cwd.map(|s| s.into()),
228 connection: value.connection.map(Into::into),
229 request_args: value.request_args.try_into()?,
230 })
231 }
232}
233
234impl From<BuildTaskDefinition> for extension::BuildTaskDefinition {
235 fn from(value: BuildTaskDefinition) -> Self {
236 match value {
237 BuildTaskDefinition::ByName(name) => Self::ByName(name.into()),
238 BuildTaskDefinition::Template(build_task_template) => Self::Template {
239 task_template: build_task_template.template.into(),
240 locator_name: build_task_template.locator_name.map(SharedString::from),
241 },
242 }
243 }
244}
245
246impl From<extension::BuildTaskDefinition> for BuildTaskDefinition {
247 fn from(value: extension::BuildTaskDefinition) -> Self {
248 match value {
249 extension::BuildTaskDefinition::ByName(name) => Self::ByName(name.into()),
250 extension::BuildTaskDefinition::Template {
251 task_template,
252 locator_name,
253 } => Self::Template(BuildTaskDefinitionTemplatePayload {
254 template: task_template.into(),
255 locator_name: locator_name.map(String::from),
256 }),
257 }
258 }
259}
260impl From<BuildTaskTemplate> for extension::BuildTaskTemplate {
261 fn from(value: BuildTaskTemplate) -> Self {
262 Self {
263 label: value.label,
264 command: value.command,
265 args: value.args,
266 env: value.env.into_iter().collect(),
267 cwd: value.cwd,
268 ..Default::default()
269 }
270 }
271}
272impl From<extension::BuildTaskTemplate> for BuildTaskTemplate {
273 fn from(value: extension::BuildTaskTemplate) -> Self {
274 Self {
275 label: value.label,
276 command: value.command,
277 args: value.args,
278 env: value.env.into_iter().collect(),
279 cwd: value.cwd,
280 }
281 }
282}
283
284impl TryFrom<DebugScenario> for extension::DebugScenario {
285 type Error = anyhow::Error;
286
287 fn try_from(value: DebugScenario) -> std::result::Result<Self, Self::Error> {
288 Ok(Self {
289 adapter: value.adapter.into(),
290 label: value.label.into(),
291 build: value.build.map(Into::into),
292 config: serde_json::Value::from_str(&value.config)?,
293 tcp_connection: value.tcp_connection.map(Into::into),
294 })
295 }
296}
297
298impl From<extension::DebugScenario> for DebugScenario {
299 fn from(value: extension::DebugScenario) -> Self {
300 Self {
301 adapter: value.adapter.into(),
302 label: value.label.into(),
303 build: value.build.map(Into::into),
304 config: value.config.to_string(),
305 tcp_connection: value.tcp_connection.map(Into::into),
306 }
307 }
308}
309
310impl TryFrom<SpawnInTerminal> for ResolvedTask {
311 type Error = anyhow::Error;
312
313 fn try_from(value: SpawnInTerminal) -> Result<Self, Self::Error> {
314 Ok(Self {
315 label: value.label,
316 command: value.command.context("missing command")?,
317 args: value.args,
318 env: value.env.into_iter().collect(),
319 cwd: value.cwd.map(|s| {
320 let s = s.to_string_lossy();
321 if cfg!(target_os = "windows") {
322 s.replace('\\', "/")
323 } else {
324 s.into_owned()
325 }
326 }),
327 })
328 }
329}
330
331impl From<CodeLabel> for extension::CodeLabel {
332 fn from(value: CodeLabel) -> Self {
333 Self {
334 code: value.code,
335 spans: value.spans.into_iter().map(Into::into).collect(),
336 filter_range: value.filter_range.into(),
337 }
338 }
339}
340
341impl From<CodeLabelSpan> for extension::CodeLabelSpan {
342 fn from(value: CodeLabelSpan) -> Self {
343 match value {
344 CodeLabelSpan::CodeRange(range) => Self::CodeRange(range.into()),
345 CodeLabelSpan::Literal(literal) => Self::Literal(literal.into()),
346 }
347 }
348}
349
350impl From<CodeLabelSpanLiteral> for extension::CodeLabelSpanLiteral {
351 fn from(value: CodeLabelSpanLiteral) -> Self {
352 Self {
353 text: value.text,
354 highlight_name: value.highlight_name,
355 }
356 }
357}
358
359impl From<extension::Completion> for Completion {
360 fn from(value: extension::Completion) -> Self {
361 Self {
362 label: value.label,
363 label_details: value.label_details.map(Into::into),
364 detail: value.detail,
365 kind: value.kind.map(Into::into),
366 insert_text_format: value.insert_text_format.map(Into::into),
367 }
368 }
369}
370
371impl From<extension::CompletionLabelDetails> for CompletionLabelDetails {
372 fn from(value: extension::CompletionLabelDetails) -> Self {
373 Self {
374 detail: value.detail,
375 description: value.description,
376 }
377 }
378}
379
380impl From<extension::CompletionKind> for CompletionKind {
381 fn from(value: extension::CompletionKind) -> Self {
382 match value {
383 extension::CompletionKind::Text => Self::Text,
384 extension::CompletionKind::Method => Self::Method,
385 extension::CompletionKind::Function => Self::Function,
386 extension::CompletionKind::Constructor => Self::Constructor,
387 extension::CompletionKind::Field => Self::Field,
388 extension::CompletionKind::Variable => Self::Variable,
389 extension::CompletionKind::Class => Self::Class,
390 extension::CompletionKind::Interface => Self::Interface,
391 extension::CompletionKind::Module => Self::Module,
392 extension::CompletionKind::Property => Self::Property,
393 extension::CompletionKind::Unit => Self::Unit,
394 extension::CompletionKind::Value => Self::Value,
395 extension::CompletionKind::Enum => Self::Enum,
396 extension::CompletionKind::Keyword => Self::Keyword,
397 extension::CompletionKind::Snippet => Self::Snippet,
398 extension::CompletionKind::Color => Self::Color,
399 extension::CompletionKind::File => Self::File,
400 extension::CompletionKind::Reference => Self::Reference,
401 extension::CompletionKind::Folder => Self::Folder,
402 extension::CompletionKind::EnumMember => Self::EnumMember,
403 extension::CompletionKind::Constant => Self::Constant,
404 extension::CompletionKind::Struct => Self::Struct,
405 extension::CompletionKind::Event => Self::Event,
406 extension::CompletionKind::Operator => Self::Operator,
407 extension::CompletionKind::TypeParameter => Self::TypeParameter,
408 extension::CompletionKind::Other(value) => Self::Other(value),
409 }
410 }
411}
412
413impl From<extension::InsertTextFormat> for InsertTextFormat {
414 fn from(value: extension::InsertTextFormat) -> Self {
415 match value {
416 extension::InsertTextFormat::PlainText => Self::PlainText,
417 extension::InsertTextFormat::Snippet => Self::Snippet,
418 extension::InsertTextFormat::Other(value) => Self::Other(value),
419 }
420 }
421}
422
423impl From<extension::Symbol> for Symbol {
424 fn from(value: extension::Symbol) -> Self {
425 Self {
426 kind: value.kind.into(),
427 name: value.name,
428 }
429 }
430}
431
432impl From<extension::SymbolKind> for SymbolKind {
433 fn from(value: extension::SymbolKind) -> Self {
434 match value {
435 extension::SymbolKind::File => Self::File,
436 extension::SymbolKind::Module => Self::Module,
437 extension::SymbolKind::Namespace => Self::Namespace,
438 extension::SymbolKind::Package => Self::Package,
439 extension::SymbolKind::Class => Self::Class,
440 extension::SymbolKind::Method => Self::Method,
441 extension::SymbolKind::Property => Self::Property,
442 extension::SymbolKind::Field => Self::Field,
443 extension::SymbolKind::Constructor => Self::Constructor,
444 extension::SymbolKind::Enum => Self::Enum,
445 extension::SymbolKind::Interface => Self::Interface,
446 extension::SymbolKind::Function => Self::Function,
447 extension::SymbolKind::Variable => Self::Variable,
448 extension::SymbolKind::Constant => Self::Constant,
449 extension::SymbolKind::String => Self::String,
450 extension::SymbolKind::Number => Self::Number,
451 extension::SymbolKind::Boolean => Self::Boolean,
452 extension::SymbolKind::Array => Self::Array,
453 extension::SymbolKind::Object => Self::Object,
454 extension::SymbolKind::Key => Self::Key,
455 extension::SymbolKind::Null => Self::Null,
456 extension::SymbolKind::EnumMember => Self::EnumMember,
457 extension::SymbolKind::Struct => Self::Struct,
458 extension::SymbolKind::Event => Self::Event,
459 extension::SymbolKind::Operator => Self::Operator,
460 extension::SymbolKind::TypeParameter => Self::TypeParameter,
461 extension::SymbolKind::Other(value) => Self::Other(value),
462 }
463 }
464}
465
466impl From<extension::SlashCommand> for SlashCommand {
467 fn from(value: extension::SlashCommand) -> Self {
468 Self {
469 name: value.name,
470 description: value.description,
471 tooltip_text: value.tooltip_text,
472 requires_argument: value.requires_argument,
473 }
474 }
475}
476
477impl From<SlashCommandOutput> for extension::SlashCommandOutput {
478 fn from(value: SlashCommandOutput) -> Self {
479 Self {
480 text: value.text,
481 sections: value.sections.into_iter().map(Into::into).collect(),
482 }
483 }
484}
485
486impl From<SlashCommandOutputSection> for extension::SlashCommandOutputSection {
487 fn from(value: SlashCommandOutputSection) -> Self {
488 Self {
489 range: value.range.start as usize..value.range.end as usize,
490 label: value.label,
491 }
492 }
493}
494
495impl From<SlashCommandArgumentCompletion> for extension::SlashCommandArgumentCompletion {
496 fn from(value: SlashCommandArgumentCompletion) -> Self {
497 Self {
498 label: value.label,
499 new_text: value.new_text,
500 run_command: value.run_command,
501 }
502 }
503}
504
505impl TryFrom<ContextServerConfiguration> for extension::ContextServerConfiguration {
506 type Error = anyhow::Error;
507
508 fn try_from(value: ContextServerConfiguration) -> Result<Self, Self::Error> {
509 let settings_schema: serde_json::Value = serde_json::from_str(&value.settings_schema)
510 .context("Failed to parse settings_schema")?;
511
512 Ok(Self {
513 installation_instructions: value.installation_instructions,
514 default_settings: value.default_settings,
515 settings_schema,
516 })
517 }
518}
519
520impl HostKeyValueStore for WasmState {
521 async fn insert(
522 &mut self,
523 kv_store: Resource<ExtensionKeyValueStore>,
524 key: String,
525 value: String,
526 ) -> wasmtime::Result<Result<(), String>> {
527 let kv_store = self.table.get(&kv_store)?;
528 kv_store.insert(key, value).await.to_wasmtime_result()
529 }
530
531 async fn drop(&mut self, _worktree: Resource<ExtensionKeyValueStore>) -> Result<()> {
532 // We only ever hand out borrows of key-value stores.
533 Ok(())
534 }
535}
536
537impl HostProject for WasmState {
538 async fn worktree_ids(
539 &mut self,
540 project: Resource<ExtensionProject>,
541 ) -> wasmtime::Result<Vec<u64>> {
542 let project = self.table.get(&project)?;
543 Ok(project.worktree_ids())
544 }
545
546 async fn drop(&mut self, _project: Resource<Project>) -> Result<()> {
547 // We only ever hand out borrows of projects.
548 Ok(())
549 }
550}
551
552impl HostWorktree for WasmState {
553 async fn id(&mut self, delegate: Resource<Arc<dyn WorktreeDelegate>>) -> wasmtime::Result<u64> {
554 let delegate = self.table.get(&delegate)?;
555 Ok(delegate.id())
556 }
557
558 async fn root_path(
559 &mut self,
560 delegate: Resource<Arc<dyn WorktreeDelegate>>,
561 ) -> wasmtime::Result<String> {
562 let delegate = self.table.get(&delegate)?;
563 Ok(delegate.root_path())
564 }
565
566 async fn read_text_file(
567 &mut self,
568 delegate: Resource<Arc<dyn WorktreeDelegate>>,
569 path: String,
570 ) -> wasmtime::Result<Result<String, String>> {
571 let delegate = self.table.get(&delegate)?;
572 Ok(delegate
573 .read_text_file(&RelPath::new(Path::new(&path), PathStyle::Posix)?)
574 .await
575 .map_err(|error| error.to_string()))
576 }
577
578 async fn shell_env(
579 &mut self,
580 delegate: Resource<Arc<dyn WorktreeDelegate>>,
581 ) -> wasmtime::Result<EnvVars> {
582 let delegate = self.table.get(&delegate)?;
583 Ok(delegate.shell_env().await.into_iter().collect())
584 }
585
586 async fn which(
587 &mut self,
588 delegate: Resource<Arc<dyn WorktreeDelegate>>,
589 binary_name: String,
590 ) -> wasmtime::Result<Option<String>> {
591 let delegate = self.table.get(&delegate)?;
592 Ok(delegate.which(binary_name).await)
593 }
594
595 async fn drop(&mut self, _worktree: Resource<Worktree>) -> Result<()> {
596 // We only ever hand out borrows of worktrees.
597 Ok(())
598 }
599}
600
601impl common::Host for WasmState {}
602
603impl http_client::Host for WasmState {
604 async fn fetch(
605 &mut self,
606 request: http_client::HttpRequest,
607 ) -> wasmtime::Result<Result<http_client::HttpResponse, String>> {
608 maybe!(async {
609 let url = &request.url;
610 let request = convert_request(&request)?;
611 let mut response = self.host.http_client.send(request).await?;
612
613 if response.status().is_client_error() || response.status().is_server_error() {
614 bail!("failed to fetch '{url}': status code {}", response.status())
615 }
616 convert_response(&mut response).await
617 })
618 .await
619 .to_wasmtime_result()
620 }
621
622 async fn fetch_stream(
623 &mut self,
624 request: http_client::HttpRequest,
625 ) -> wasmtime::Result<Result<Resource<ExtensionHttpResponseStream>, String>> {
626 let request = convert_request(&request)?;
627 let response = self.host.http_client.send(request);
628 maybe!(async {
629 let response = response.await?;
630 let stream = Arc::new(Mutex::new(response));
631 let resource = self.table.push(stream)?;
632 Ok(resource)
633 })
634 .await
635 .to_wasmtime_result()
636 }
637}
638
639impl http_client::HostHttpResponseStream for WasmState {
640 async fn next_chunk(
641 &mut self,
642 resource: Resource<ExtensionHttpResponseStream>,
643 ) -> wasmtime::Result<Result<Option<Vec<u8>>, String>> {
644 let stream = self.table.get(&resource)?.clone();
645 maybe!(async move {
646 let mut response = stream.lock().await;
647 let mut buffer = vec![0; 8192]; // 8KB buffer
648 let bytes_read = response.body_mut().read(&mut buffer).await?;
649 if bytes_read == 0 {
650 Ok(None)
651 } else {
652 buffer.truncate(bytes_read);
653 Ok(Some(buffer))
654 }
655 })
656 .await
657 .to_wasmtime_result()
658 }
659
660 async fn drop(&mut self, _resource: Resource<ExtensionHttpResponseStream>) -> Result<()> {
661 Ok(())
662 }
663}
664
665impl From<http_client::HttpMethod> for ::http_client::Method {
666 fn from(value: http_client::HttpMethod) -> Self {
667 match value {
668 http_client::HttpMethod::Get => Self::GET,
669 http_client::HttpMethod::Post => Self::POST,
670 http_client::HttpMethod::Put => Self::PUT,
671 http_client::HttpMethod::Delete => Self::DELETE,
672 http_client::HttpMethod::Head => Self::HEAD,
673 http_client::HttpMethod::Options => Self::OPTIONS,
674 http_client::HttpMethod::Patch => Self::PATCH,
675 }
676 }
677}
678
679fn convert_request(
680 extension_request: &http_client::HttpRequest,
681) -> anyhow::Result<::http_client::Request<AsyncBody>> {
682 let mut request = ::http_client::Request::builder()
683 .method(::http_client::Method::from(extension_request.method))
684 .uri(&extension_request.url)
685 .follow_redirects(match extension_request.redirect_policy {
686 http_client::RedirectPolicy::NoFollow => ::http_client::RedirectPolicy::NoFollow,
687 http_client::RedirectPolicy::FollowLimit(limit) => {
688 ::http_client::RedirectPolicy::FollowLimit(limit)
689 }
690 http_client::RedirectPolicy::FollowAll => ::http_client::RedirectPolicy::FollowAll,
691 });
692 for (key, value) in &extension_request.headers {
693 request = request.header(key, value);
694 }
695 let body = extension_request
696 .body
697 .clone()
698 .map(AsyncBody::from)
699 .unwrap_or_default();
700 request.body(body).map_err(anyhow::Error::from)
701}
702
703async fn convert_response(
704 response: &mut ::http_client::Response<AsyncBody>,
705) -> anyhow::Result<http_client::HttpResponse> {
706 let mut extension_response = http_client::HttpResponse {
707 body: Vec::new(),
708 headers: Vec::new(),
709 };
710
711 for (key, value) in response.headers() {
712 extension_response
713 .headers
714 .push((key.to_string(), value.to_str().unwrap_or("").to_string()));
715 }
716
717 response
718 .body_mut()
719 .read_to_end(&mut extension_response.body)
720 .await?;
721
722 Ok(extension_response)
723}
724
725impl nodejs::Host for WasmState {
726 async fn node_binary_path(&mut self) -> wasmtime::Result<Result<String, String>> {
727 self.host
728 .node_runtime
729 .binary_path()
730 .await
731 .map(|path| path.to_string_lossy().into_owned())
732 .to_wasmtime_result()
733 }
734
735 async fn npm_package_latest_version(
736 &mut self,
737 package_name: String,
738 ) -> wasmtime::Result<Result<String, String>> {
739 self.host
740 .node_runtime
741 .npm_package_latest_version(&package_name)
742 .await
743 .to_wasmtime_result()
744 }
745
746 async fn npm_package_installed_version(
747 &mut self,
748 package_name: String,
749 ) -> wasmtime::Result<Result<Option<String>, String>> {
750 self.host
751 .node_runtime
752 .npm_package_installed_version(&self.work_dir(), &package_name)
753 .await
754 .to_wasmtime_result()
755 }
756
757 async fn npm_install_package(
758 &mut self,
759 package_name: String,
760 version: String,
761 ) -> wasmtime::Result<Result<(), String>> {
762 self.capability_granter
763 .grant_npm_install_package(&package_name)?;
764
765 self.host
766 .node_runtime
767 .npm_install_packages(&self.work_dir(), &[(&package_name, &version)])
768 .await
769 .to_wasmtime_result()
770 }
771}
772
773#[async_trait]
774impl lsp::Host for WasmState {}
775
776impl From<::http_client::github::GithubRelease> for github::GithubRelease {
777 fn from(value: ::http_client::github::GithubRelease) -> Self {
778 Self {
779 version: value.tag_name,
780 assets: value.assets.into_iter().map(Into::into).collect(),
781 }
782 }
783}
784
785impl From<::http_client::github::GithubReleaseAsset> for github::GithubReleaseAsset {
786 fn from(value: ::http_client::github::GithubReleaseAsset) -> Self {
787 Self {
788 name: value.name,
789 download_url: value.browser_download_url,
790 }
791 }
792}
793
794impl github::Host for WasmState {
795 async fn latest_github_release(
796 &mut self,
797 repo: String,
798 options: github::GithubReleaseOptions,
799 ) -> wasmtime::Result<Result<github::GithubRelease, String>> {
800 maybe!(async {
801 let release = ::http_client::github::latest_github_release(
802 &repo,
803 options.require_assets,
804 options.pre_release,
805 self.host.http_client.clone(),
806 )
807 .await?;
808 Ok(release.into())
809 })
810 .await
811 .to_wasmtime_result()
812 }
813
814 async fn github_release_by_tag_name(
815 &mut self,
816 repo: String,
817 tag: String,
818 ) -> wasmtime::Result<Result<github::GithubRelease, String>> {
819 maybe!(async {
820 let release = ::http_client::github::get_release_by_tag_name(
821 &repo,
822 &tag,
823 self.host.http_client.clone(),
824 )
825 .await?;
826 Ok(release.into())
827 })
828 .await
829 .to_wasmtime_result()
830 }
831}
832
833impl platform::Host for WasmState {
834 async fn current_platform(&mut self) -> Result<(platform::Os, platform::Architecture)> {
835 Ok((
836 match env::consts::OS {
837 "macos" => platform::Os::Mac,
838 "linux" => platform::Os::Linux,
839 "windows" => platform::Os::Windows,
840 _ => panic!("unsupported os"),
841 },
842 match env::consts::ARCH {
843 "aarch64" => platform::Architecture::Aarch64,
844 "x86" => platform::Architecture::X86,
845 "x86_64" => platform::Architecture::X8664,
846 _ => panic!("unsupported architecture"),
847 },
848 ))
849 }
850}
851
852impl From<std::process::Output> for process::Output {
853 fn from(output: std::process::Output) -> Self {
854 Self {
855 status: output.status.code(),
856 stdout: output.stdout,
857 stderr: output.stderr,
858 }
859 }
860}
861
862impl process::Host for WasmState {
863 async fn run_command(
864 &mut self,
865 command: process::Command,
866 ) -> wasmtime::Result<Result<process::Output, String>> {
867 maybe!(async {
868 self.capability_granter
869 .grant_exec(&command.command, &command.args)?;
870
871 let output = util::command::new_smol_command(command.command.as_str())
872 .args(&command.args)
873 .envs(command.env)
874 .output()
875 .await?;
876
877 Ok(output.into())
878 })
879 .await
880 .to_wasmtime_result()
881 }
882}
883
884#[async_trait]
885impl slash_command::Host for WasmState {}
886
887#[async_trait]
888impl context_server::Host for WasmState {}
889
890impl dap::Host for WasmState {
891 async fn resolve_tcp_template(
892 &mut self,
893 template: TcpArgumentsTemplate,
894 ) -> wasmtime::Result<Result<TcpArguments, String>> {
895 maybe!(async {
896 let (host, port, timeout) =
897 ::dap::configure_tcp_connection(task::TcpArgumentsTemplate {
898 port: template.port,
899 host: template.host.map(Ipv4Addr::from_bits),
900 timeout: template.timeout,
901 })
902 .await?;
903 Ok(TcpArguments {
904 port,
905 host: host.to_bits(),
906 timeout,
907 })
908 })
909 .await
910 .to_wasmtime_result()
911 }
912}
913
914impl ExtensionImports for WasmState {
915 async fn get_settings(
916 &mut self,
917 location: Option<self::SettingsLocation>,
918 category: String,
919 key: Option<String>,
920 ) -> wasmtime::Result<Result<String, String>> {
921 self.on_main_thread(|cx| {
922 async move {
923 let path = location.as_ref().and_then(|location| {
924 RelPath::new(Path::new(&location.path), PathStyle::Posix).ok()
925 });
926 let location = path
927 .as_ref()
928 .zip(location.as_ref())
929 .map(|(path, location)| ::settings::SettingsLocation {
930 worktree_id: WorktreeId::from_proto(location.worktree_id),
931 path,
932 });
933
934 cx.update(|cx| match category.as_str() {
935 "language" => {
936 let key = key.map(|k| LanguageName::new(&k));
937 let settings = AllLanguageSettings::get(location, cx).language(
938 location,
939 key.as_ref(),
940 cx,
941 );
942 Ok(serde_json::to_string(&settings::LanguageSettings {
943 tab_size: settings.tab_size,
944 })?)
945 }
946 "lsp" => {
947 let settings = key
948 .and_then(|key| {
949 ProjectSettings::get(location, cx)
950 .lsp
951 .get(&::lsp::LanguageServerName::from_proto(key))
952 })
953 .cloned()
954 .unwrap_or_default();
955 Ok(serde_json::to_string(&settings::LspSettings {
956 binary: settings.binary.map(|binary| settings::CommandSettings {
957 path: binary.path,
958 arguments: binary.arguments,
959 env: binary.env.map(|env| env.into_iter().collect()),
960 }),
961 settings: settings.settings,
962 initialization_options: settings.initialization_options,
963 })?)
964 }
965 "context_servers" => {
966 let settings = key
967 .and_then(|key| {
968 ProjectSettings::get(location, cx)
969 .context_servers
970 .get(key.as_str())
971 })
972 .cloned()
973 .unwrap_or_else(|| {
974 project::project_settings::ContextServerSettings::default_extension(
975 )
976 });
977
978 match settings {
979 project::project_settings::ContextServerSettings::Stdio {
980 enabled: _,
981 command,
982 } => Ok(serde_json::to_string(&settings::ContextServerSettings {
983 command: Some(settings::CommandSettings {
984 path: command.path.to_str().map(|path| path.to_string()),
985 arguments: Some(command.args),
986 env: command.env.map(|env| env.into_iter().collect()),
987 }),
988 settings: None,
989 })?),
990 project::project_settings::ContextServerSettings::Extension {
991 enabled: _,
992 settings,
993 } => Ok(serde_json::to_string(&settings::ContextServerSettings {
994 command: None,
995 settings: Some(settings),
996 })?),
997 project::project_settings::ContextServerSettings::Http { .. } => {
998 bail!("remote context server settings not supported in 0.6.0")
999 }
1000 }
1001 }
1002 _ => {
1003 bail!("Unknown settings category: {}", category);
1004 }
1005 })
1006 }
1007 .boxed_local()
1008 })
1009 .await?
1010 .to_wasmtime_result()
1011 }
1012
1013 async fn set_language_server_installation_status(
1014 &mut self,
1015 server_name: String,
1016 status: LanguageServerInstallationStatus,
1017 ) -> wasmtime::Result<()> {
1018 let status = match status {
1019 LanguageServerInstallationStatus::CheckingForUpdate => BinaryStatus::CheckingForUpdate,
1020 LanguageServerInstallationStatus::Downloading => BinaryStatus::Downloading,
1021 LanguageServerInstallationStatus::None => BinaryStatus::None,
1022 LanguageServerInstallationStatus::Failed(error) => BinaryStatus::Failed { error },
1023 };
1024
1025 self.host
1026 .proxy
1027 .update_language_server_status(::lsp::LanguageServerName(server_name.into()), status);
1028
1029 Ok(())
1030 }
1031
1032 async fn download_file(
1033 &mut self,
1034 url: String,
1035 path: String,
1036 file_type: DownloadedFileType,
1037 ) -> wasmtime::Result<Result<(), String>> {
1038 maybe!(async {
1039 let parsed_url = Url::parse(&url)?;
1040 self.capability_granter.grant_download_file(&parsed_url)?;
1041
1042 let path = PathBuf::from(path);
1043 let extension_work_dir = self.host.work_dir.join(self.manifest.id.as_ref());
1044
1045 self.host.fs.create_dir(&extension_work_dir).await?;
1046
1047 let destination_path = self
1048 .host
1049 .writeable_path_from_extension(&self.manifest.id, &path)?;
1050
1051 let mut response = self
1052 .host
1053 .http_client
1054 .get(&url, Default::default(), true)
1055 .await
1056 .context("downloading release")?;
1057
1058 anyhow::ensure!(
1059 response.status().is_success(),
1060 "download failed with status {}",
1061 response.status()
1062 );
1063 let body = BufReader::new(response.body_mut());
1064
1065 match file_type {
1066 DownloadedFileType::Uncompressed => {
1067 futures::pin_mut!(body);
1068 self.host
1069 .fs
1070 .create_file_with(&destination_path, body)
1071 .await?;
1072 }
1073 DownloadedFileType::Gzip => {
1074 let body = GzipDecoder::new(body);
1075 futures::pin_mut!(body);
1076 self.host
1077 .fs
1078 .create_file_with(&destination_path, body)
1079 .await?;
1080 }
1081 DownloadedFileType::GzipTar => {
1082 let body = GzipDecoder::new(body);
1083 futures::pin_mut!(body);
1084 self.host
1085 .fs
1086 .extract_tar_file(&destination_path, Archive::new(body))
1087 .await?;
1088 }
1089 DownloadedFileType::Zip => {
1090 futures::pin_mut!(body);
1091 extract_zip(&destination_path, body)
1092 .await
1093 .with_context(|| format!("unzipping {path:?} archive"))?;
1094 }
1095 }
1096
1097 Ok(())
1098 })
1099 .await
1100 .to_wasmtime_result()
1101 }
1102
1103 async fn make_file_executable(&mut self, path: String) -> wasmtime::Result<Result<(), String>> {
1104 let path = self
1105 .host
1106 .writeable_path_from_extension(&self.manifest.id, Path::new(&path))?;
1107
1108 make_file_executable(&path)
1109 .await
1110 .with_context(|| format!("setting permissions for path {path:?}"))
1111 .to_wasmtime_result()
1112 }
1113
1114 // =========================================================================
1115 // LLM Provider Import Implementations
1116 // =========================================================================
1117
1118 async fn llm_request_credential(
1119 &mut self,
1120 _provider_id: String,
1121 _credential_type: llm_provider::CredentialType,
1122 _label: String,
1123 _placeholder: String,
1124 ) -> wasmtime::Result<Result<bool, String>> {
1125 // For now, credential requests return false (not provided)
1126 // Extensions should use llm_get_env_var to check for env vars first,
1127 // then llm_store_credential/llm_get_credential for manual storage
1128 // Full UI credential prompting will be added in a future phase
1129 Ok(Ok(false))
1130 }
1131
1132 async fn llm_get_credential(
1133 &mut self,
1134 provider_id: String,
1135 ) -> wasmtime::Result<Option<String>> {
1136 let extension_id = self.manifest.id.clone();
1137 let credential_key = format!("extension-llm-{}:{}", extension_id, provider_id);
1138
1139 self.on_main_thread(move |cx| {
1140 async move {
1141 let credentials_provider = cx.update(|cx| <dyn CredentialsProvider>::global(cx))?;
1142 let result = credentials_provider
1143 .read_credentials(&credential_key, cx)
1144 .await
1145 .ok()
1146 .flatten();
1147 Ok(result.map(|(_, password)| String::from_utf8_lossy(&password).to_string()))
1148 }
1149 .boxed_local()
1150 })
1151 .await
1152 }
1153
1154 async fn llm_store_credential(
1155 &mut self,
1156 provider_id: String,
1157 value: String,
1158 ) -> wasmtime::Result<Result<(), String>> {
1159 let extension_id = self.manifest.id.clone();
1160 let credential_key = format!("extension-llm-{}:{}", extension_id, provider_id);
1161
1162 self.on_main_thread(move |cx| {
1163 async move {
1164 let credentials_provider = cx.update(|cx| <dyn CredentialsProvider>::global(cx))?;
1165 credentials_provider
1166 .write_credentials(&credential_key, "api_key", value.as_bytes(), cx)
1167 .await
1168 .map_err(|e| anyhow::anyhow!("{}", e))
1169 }
1170 .boxed_local()
1171 })
1172 .await
1173 .to_wasmtime_result()
1174 }
1175
1176 async fn llm_delete_credential(
1177 &mut self,
1178 provider_id: String,
1179 ) -> wasmtime::Result<Result<(), String>> {
1180 let extension_id = self.manifest.id.clone();
1181 let credential_key = format!("extension-llm-{}:{}", extension_id, provider_id);
1182
1183 self.on_main_thread(move |cx| {
1184 async move {
1185 let credentials_provider = cx.update(|cx| <dyn CredentialsProvider>::global(cx))?;
1186 credentials_provider
1187 .delete_credentials(&credential_key, cx)
1188 .await
1189 .map_err(|e| anyhow::anyhow!("{}", e))
1190 }
1191 .boxed_local()
1192 })
1193 .await
1194 .to_wasmtime_result()
1195 }
1196
1197 async fn llm_get_env_var(&mut self, name: String) -> wasmtime::Result<Option<String>> {
1198 Ok(env::var(&name).ok())
1199 }
1200}
1201
1202// =============================================================================
1203// LLM Provider Host Implementations
1204// =============================================================================
1205
1206impl llm_provider::Host for WasmState {}
1207
1208// =============================================================================
1209// LLM Provider Type Conversions (v0.7.0 -> latest/v0.8.0)
1210// =============================================================================
1211
1212use super::since_v0_8_0 as latest;
1213
1214impl From<llm_provider::ProviderInfo> for latest::llm_provider::ProviderInfo {
1215 fn from(value: llm_provider::ProviderInfo) -> Self {
1216 Self {
1217 id: value.id,
1218 name: value.name,
1219 icon: value.icon,
1220 }
1221 }
1222}
1223
1224impl From<llm_provider::ModelInfo> for latest::llm_provider::ModelInfo {
1225 fn from(value: llm_provider::ModelInfo) -> Self {
1226 Self {
1227 id: value.id,
1228 name: value.name,
1229 max_token_count: value.max_token_count,
1230 max_output_tokens: value.max_output_tokens,
1231 capabilities: value.capabilities.into(),
1232 is_default: value.is_default,
1233 is_default_fast: value.is_default_fast,
1234 }
1235 }
1236}
1237
1238impl From<llm_provider::ModelCapabilities> for latest::llm_provider::ModelCapabilities {
1239 fn from(value: llm_provider::ModelCapabilities) -> Self {
1240 Self {
1241 supports_images: value.supports_images,
1242 supports_tools: value.supports_tools,
1243 supports_tool_choice_auto: value.supports_tool_choice_auto,
1244 supports_tool_choice_any: value.supports_tool_choice_any,
1245 supports_tool_choice_none: value.supports_tool_choice_none,
1246 supports_thinking: value.supports_thinking,
1247 tool_input_format: value.tool_input_format.into(),
1248 }
1249 }
1250}
1251
1252impl From<llm_provider::ToolInputFormat> for latest::llm_provider::ToolInputFormat {
1253 fn from(value: llm_provider::ToolInputFormat) -> Self {
1254 match value {
1255 llm_provider::ToolInputFormat::JsonSchema => Self::JsonSchema,
1256 llm_provider::ToolInputFormat::Simplified => Self::Simplified,
1257 }
1258 }
1259}
1260
1261impl From<llm_provider::CompletionEvent> for latest::llm_provider::CompletionEvent {
1262 fn from(value: llm_provider::CompletionEvent) -> Self {
1263 match value {
1264 llm_provider::CompletionEvent::Started => Self::Started,
1265 llm_provider::CompletionEvent::Text(s) => Self::Text(s),
1266 llm_provider::CompletionEvent::Thinking(t) => Self::Thinking(t.into()),
1267 llm_provider::CompletionEvent::RedactedThinking(s) => Self::RedactedThinking(s),
1268 llm_provider::CompletionEvent::ToolUse(t) => Self::ToolUse(t.into()),
1269 llm_provider::CompletionEvent::ToolUseJsonParseError(e) => {
1270 Self::ToolUseJsonParseError(e.into())
1271 }
1272 llm_provider::CompletionEvent::Stop(r) => Self::Stop(r.into()),
1273 llm_provider::CompletionEvent::Usage(u) => Self::Usage(u.into()),
1274 llm_provider::CompletionEvent::ReasoningDetails(s) => Self::ReasoningDetails(s),
1275 }
1276 }
1277}
1278
1279impl From<llm_provider::ThinkingContent> for latest::llm_provider::ThinkingContent {
1280 fn from(value: llm_provider::ThinkingContent) -> Self {
1281 Self {
1282 text: value.text,
1283 signature: value.signature,
1284 }
1285 }
1286}
1287
1288impl From<llm_provider::ToolUse> for latest::llm_provider::ToolUse {
1289 fn from(value: llm_provider::ToolUse) -> Self {
1290 Self {
1291 id: value.id,
1292 name: value.name,
1293 input: value.input,
1294 thought_signature: value.thought_signature,
1295 }
1296 }
1297}
1298
1299impl From<llm_provider::ToolUseJsonParseError> for latest::llm_provider::ToolUseJsonParseError {
1300 fn from(value: llm_provider::ToolUseJsonParseError) -> Self {
1301 Self {
1302 id: value.id,
1303 tool_name: value.tool_name,
1304 raw_input: value.raw_input,
1305 error: value.error,
1306 }
1307 }
1308}
1309
1310impl From<llm_provider::StopReason> for latest::llm_provider::StopReason {
1311 fn from(value: llm_provider::StopReason) -> Self {
1312 match value {
1313 llm_provider::StopReason::EndTurn => Self::EndTurn,
1314 llm_provider::StopReason::MaxTokens => Self::MaxTokens,
1315 llm_provider::StopReason::ToolUse => Self::ToolUse,
1316 llm_provider::StopReason::Refusal => Self::Refusal,
1317 }
1318 }
1319}
1320
1321impl From<llm_provider::TokenUsage> for latest::llm_provider::TokenUsage {
1322 fn from(value: llm_provider::TokenUsage) -> Self {
1323 Self {
1324 input_tokens: value.input_tokens,
1325 output_tokens: value.output_tokens,
1326 cache_creation_input_tokens: value.cache_creation_input_tokens,
1327 cache_read_input_tokens: value.cache_read_input_tokens,
1328 }
1329 }
1330}
1331
1332impl From<llm_provider::CacheConfiguration> for latest::llm_provider::CacheConfiguration {
1333 fn from(value: llm_provider::CacheConfiguration) -> Self {
1334 Self {
1335 max_cache_anchors: value.max_cache_anchors,
1336 should_cache_tool_definitions: value.should_cache_tool_definitions,
1337 min_total_token_count: value.min_total_token_count,
1338 }
1339 }
1340}
1341
1342// Conversions from latest (v0.8.0) -> v0.7.0 for requests
1343
1344impl From<latest::llm_provider::CompletionRequest> for llm_provider::CompletionRequest {
1345 fn from(value: latest::llm_provider::CompletionRequest) -> Self {
1346 Self {
1347 messages: value.messages.into_iter().map(Into::into).collect(),
1348 tools: value.tools.into_iter().map(Into::into).collect(),
1349 tool_choice: value.tool_choice.map(Into::into),
1350 stop_sequences: value.stop_sequences,
1351 temperature: value.temperature,
1352 thinking_allowed: value.thinking_allowed,
1353 max_tokens: value.max_tokens,
1354 }
1355 }
1356}
1357
1358impl From<latest::llm_provider::RequestMessage> for llm_provider::RequestMessage {
1359 fn from(value: latest::llm_provider::RequestMessage) -> Self {
1360 Self {
1361 role: value.role.into(),
1362 content: value.content.into_iter().map(Into::into).collect(),
1363 cache: value.cache,
1364 }
1365 }
1366}
1367
1368impl From<latest::llm_provider::MessageRole> for llm_provider::MessageRole {
1369 fn from(value: latest::llm_provider::MessageRole) -> Self {
1370 match value {
1371 latest::llm_provider::MessageRole::User => Self::User,
1372 latest::llm_provider::MessageRole::Assistant => Self::Assistant,
1373 latest::llm_provider::MessageRole::System => Self::System,
1374 }
1375 }
1376}
1377
1378impl From<latest::llm_provider::MessageContent> for llm_provider::MessageContent {
1379 fn from(value: latest::llm_provider::MessageContent) -> Self {
1380 match value {
1381 latest::llm_provider::MessageContent::Text(s) => Self::Text(s),
1382 latest::llm_provider::MessageContent::Image(i) => Self::Image(i.into()),
1383 latest::llm_provider::MessageContent::ToolUse(t) => Self::ToolUse(t.into()),
1384 latest::llm_provider::MessageContent::ToolResult(t) => Self::ToolResult(t.into()),
1385 latest::llm_provider::MessageContent::Thinking(t) => Self::Thinking(t.into()),
1386 latest::llm_provider::MessageContent::RedactedThinking(s) => Self::RedactedThinking(s),
1387 }
1388 }
1389}
1390
1391impl From<latest::llm_provider::ImageData> for llm_provider::ImageData {
1392 fn from(value: latest::llm_provider::ImageData) -> Self {
1393 Self {
1394 source: value.source,
1395 width: value.width,
1396 height: value.height,
1397 }
1398 }
1399}
1400
1401impl From<latest::llm_provider::ToolUse> for llm_provider::ToolUse {
1402 fn from(value: latest::llm_provider::ToolUse) -> Self {
1403 Self {
1404 id: value.id,
1405 name: value.name,
1406 input: value.input,
1407 thought_signature: value.thought_signature,
1408 }
1409 }
1410}
1411
1412impl From<latest::llm_provider::ToolResult> for llm_provider::ToolResult {
1413 fn from(value: latest::llm_provider::ToolResult) -> Self {
1414 Self {
1415 tool_use_id: value.tool_use_id,
1416 tool_name: value.tool_name,
1417 is_error: value.is_error,
1418 content: value.content.into(),
1419 }
1420 }
1421}
1422
1423impl From<latest::llm_provider::ToolResultContent> for llm_provider::ToolResultContent {
1424 fn from(value: latest::llm_provider::ToolResultContent) -> Self {
1425 match value {
1426 latest::llm_provider::ToolResultContent::Text(s) => Self::Text(s),
1427 latest::llm_provider::ToolResultContent::Image(i) => Self::Image(i.into()),
1428 }
1429 }
1430}
1431
1432impl From<latest::llm_provider::ThinkingContent> for llm_provider::ThinkingContent {
1433 fn from(value: latest::llm_provider::ThinkingContent) -> Self {
1434 Self {
1435 text: value.text,
1436 signature: value.signature,
1437 }
1438 }
1439}
1440
1441impl From<latest::llm_provider::ToolDefinition> for llm_provider::ToolDefinition {
1442 fn from(value: latest::llm_provider::ToolDefinition) -> Self {
1443 Self {
1444 name: value.name,
1445 description: value.description,
1446 input_schema: value.input_schema,
1447 }
1448 }
1449}
1450
1451impl From<latest::llm_provider::ToolChoice> for llm_provider::ToolChoice {
1452 fn from(value: latest::llm_provider::ToolChoice) -> Self {
1453 match value {
1454 latest::llm_provider::ToolChoice::Auto => Self::Auto,
1455 latest::llm_provider::ToolChoice::Any => Self::Any,
1456 latest::llm_provider::ToolChoice::None => Self::None,
1457 }
1458 }
1459}
1460
1461// =============================================================================
1462// Command Type Conversions (v0.7.0 -> latest/v0.8.0)
1463// =============================================================================
1464
1465impl From<Command> for latest::Command {
1466 fn from(value: Command) -> Self {
1467 Self {
1468 command: value.command,
1469 args: value.args,
1470 env: value.env,
1471 }
1472 }
1473}
1474
1475// =============================================================================
1476// LSP Type Conversions (latest/v0.8.0 -> v0.7.0)
1477// =============================================================================
1478
1479impl From<latest::lsp::Completion> for lsp::Completion {
1480 fn from(value: latest::lsp::Completion) -> Self {
1481 Self {
1482 label: value.label,
1483 label_details: value.label_details.map(Into::into),
1484 detail: value.detail,
1485 kind: value.kind.map(Into::into),
1486 insert_text_format: value.insert_text_format.map(Into::into),
1487 }
1488 }
1489}
1490
1491impl From<latest::lsp::CompletionLabelDetails> for lsp::CompletionLabelDetails {
1492 fn from(value: latest::lsp::CompletionLabelDetails) -> Self {
1493 Self {
1494 detail: value.detail,
1495 description: value.description,
1496 }
1497 }
1498}
1499
1500impl From<latest::lsp::CompletionKind> for lsp::CompletionKind {
1501 fn from(value: latest::lsp::CompletionKind) -> Self {
1502 match value {
1503 latest::lsp::CompletionKind::Text => Self::Text,
1504 latest::lsp::CompletionKind::Method => Self::Method,
1505 latest::lsp::CompletionKind::Function => Self::Function,
1506 latest::lsp::CompletionKind::Constructor => Self::Constructor,
1507 latest::lsp::CompletionKind::Field => Self::Field,
1508 latest::lsp::CompletionKind::Variable => Self::Variable,
1509 latest::lsp::CompletionKind::Class => Self::Class,
1510 latest::lsp::CompletionKind::Interface => Self::Interface,
1511 latest::lsp::CompletionKind::Module => Self::Module,
1512 latest::lsp::CompletionKind::Property => Self::Property,
1513 latest::lsp::CompletionKind::Unit => Self::Unit,
1514 latest::lsp::CompletionKind::Value => Self::Value,
1515 latest::lsp::CompletionKind::Enum => Self::Enum,
1516 latest::lsp::CompletionKind::Keyword => Self::Keyword,
1517 latest::lsp::CompletionKind::Snippet => Self::Snippet,
1518 latest::lsp::CompletionKind::Color => Self::Color,
1519 latest::lsp::CompletionKind::File => Self::File,
1520 latest::lsp::CompletionKind::Reference => Self::Reference,
1521 latest::lsp::CompletionKind::Folder => Self::Folder,
1522 latest::lsp::CompletionKind::EnumMember => Self::EnumMember,
1523 latest::lsp::CompletionKind::Constant => Self::Constant,
1524 latest::lsp::CompletionKind::Struct => Self::Struct,
1525 latest::lsp::CompletionKind::Event => Self::Event,
1526 latest::lsp::CompletionKind::Operator => Self::Operator,
1527 latest::lsp::CompletionKind::TypeParameter => Self::TypeParameter,
1528 latest::lsp::CompletionKind::Other(n) => Self::Other(n),
1529 }
1530 }
1531}
1532
1533impl From<latest::lsp::InsertTextFormat> for lsp::InsertTextFormat {
1534 fn from(value: latest::lsp::InsertTextFormat) -> Self {
1535 match value {
1536 latest::lsp::InsertTextFormat::PlainText => Self::PlainText,
1537 latest::lsp::InsertTextFormat::Snippet => Self::Snippet,
1538 latest::lsp::InsertTextFormat::Other(n) => Self::Other(n),
1539 }
1540 }
1541}
1542
1543impl From<latest::lsp::Symbol> for lsp::Symbol {
1544 fn from(value: latest::lsp::Symbol) -> Self {
1545 Self {
1546 kind: value.kind.into(),
1547 name: value.name,
1548 }
1549 }
1550}
1551
1552impl From<latest::lsp::SymbolKind> for lsp::SymbolKind {
1553 fn from(value: latest::lsp::SymbolKind) -> Self {
1554 match value {
1555 latest::lsp::SymbolKind::File => Self::File,
1556 latest::lsp::SymbolKind::Module => Self::Module,
1557 latest::lsp::SymbolKind::Namespace => Self::Namespace,
1558 latest::lsp::SymbolKind::Package => Self::Package,
1559 latest::lsp::SymbolKind::Class => Self::Class,
1560 latest::lsp::SymbolKind::Method => Self::Method,
1561 latest::lsp::SymbolKind::Property => Self::Property,
1562 latest::lsp::SymbolKind::Field => Self::Field,
1563 latest::lsp::SymbolKind::Constructor => Self::Constructor,
1564 latest::lsp::SymbolKind::Enum => Self::Enum,
1565 latest::lsp::SymbolKind::Interface => Self::Interface,
1566 latest::lsp::SymbolKind::Function => Self::Function,
1567 latest::lsp::SymbolKind::Variable => Self::Variable,
1568 latest::lsp::SymbolKind::Constant => Self::Constant,
1569 latest::lsp::SymbolKind::String => Self::String,
1570 latest::lsp::SymbolKind::Number => Self::Number,
1571 latest::lsp::SymbolKind::Boolean => Self::Boolean,
1572 latest::lsp::SymbolKind::Array => Self::Array,
1573 latest::lsp::SymbolKind::Object => Self::Object,
1574 latest::lsp::SymbolKind::Key => Self::Key,
1575 latest::lsp::SymbolKind::Null => Self::Null,
1576 latest::lsp::SymbolKind::EnumMember => Self::EnumMember,
1577 latest::lsp::SymbolKind::Struct => Self::Struct,
1578 latest::lsp::SymbolKind::Event => Self::Event,
1579 latest::lsp::SymbolKind::Operator => Self::Operator,
1580 latest::lsp::SymbolKind::TypeParameter => Self::TypeParameter,
1581 latest::lsp::SymbolKind::Other(n) => Self::Other(n),
1582 }
1583 }
1584}
1585
1586// =============================================================================
1587// CodeLabel Type Conversions (v0.7.0 -> latest/v0.8.0)
1588// =============================================================================
1589
1590impl From<CodeLabel> for latest::CodeLabel {
1591 fn from(value: CodeLabel) -> Self {
1592 Self {
1593 code: value.code,
1594 spans: value.spans.into_iter().map(Into::into).collect(),
1595 filter_range: value.filter_range.into(),
1596 }
1597 }
1598}
1599
1600impl From<CodeLabelSpan> for latest::CodeLabelSpan {
1601 fn from(value: CodeLabelSpan) -> Self {
1602 match value {
1603 CodeLabelSpan::CodeRange(r) => Self::CodeRange(r.into()),
1604 CodeLabelSpan::Literal(l) => Self::Literal(l.into()),
1605 }
1606 }
1607}
1608
1609impl From<CodeLabelSpanLiteral> for latest::CodeLabelSpanLiteral {
1610 fn from(value: CodeLabelSpanLiteral) -> Self {
1611 Self {
1612 text: value.text,
1613 highlight_name: value.highlight_name,
1614 }
1615 }
1616}
1617
1618impl From<Range> for latest::Range {
1619 fn from(value: Range) -> Self {
1620 Self {
1621 start: value.start,
1622 end: value.end,
1623 }
1624 }
1625}
1626
1627// =============================================================================
1628// SlashCommand Type Conversions (latest/v0.8.0 -> v0.7.0)
1629// =============================================================================
1630
1631impl From<&latest::SlashCommand> for slash_command::SlashCommand {
1632 fn from(value: &latest::SlashCommand) -> Self {
1633 Self {
1634 name: value.name.clone(),
1635 description: value.description.clone(),
1636 tooltip_text: value.tooltip_text.clone(),
1637 requires_argument: value.requires_argument,
1638 }
1639 }
1640}
1641
1642// =============================================================================
1643// SlashCommand Type Conversions (v0.7.0 -> latest/v0.8.0)
1644// =============================================================================
1645
1646impl From<slash_command::SlashCommandArgumentCompletion>
1647 for latest::SlashCommandArgumentCompletion
1648{
1649 fn from(value: slash_command::SlashCommandArgumentCompletion) -> Self {
1650 Self {
1651 label: value.label,
1652 new_text: value.new_text,
1653 run_command: value.run_command,
1654 }
1655 }
1656}
1657
1658impl From<slash_command::SlashCommandOutput> for latest::SlashCommandOutput {
1659 fn from(value: slash_command::SlashCommandOutput) -> Self {
1660 Self {
1661 sections: value.sections.into_iter().map(Into::into).collect(),
1662 text: value.text,
1663 }
1664 }
1665}
1666
1667impl From<SlashCommandOutputSection> for latest::slash_command::SlashCommandOutputSection {
1668 fn from(value: SlashCommandOutputSection) -> Self {
1669 Self {
1670 range: value.range.into(),
1671 label: value.label,
1672 }
1673 }
1674}
1675
1676// =============================================================================
1677// ContextServer Type Conversions (v0.7.0 -> latest/v0.8.0)
1678// =============================================================================
1679
1680impl From<context_server::ContextServerConfiguration>
1681 for latest::context_server::ContextServerConfiguration
1682{
1683 fn from(value: context_server::ContextServerConfiguration) -> Self {
1684 Self {
1685 installation_instructions: value.installation_instructions,
1686 settings_schema: value.settings_schema,
1687 default_settings: value.default_settings,
1688 }
1689 }
1690}
1691
1692// =============================================================================
1693// DAP Type Conversions (v0.7.0 -> latest/v0.8.0)
1694// =============================================================================
1695
1696impl From<dap::DebugAdapterBinary> for latest::dap::DebugAdapterBinary {
1697 fn from(value: dap::DebugAdapterBinary) -> Self {
1698 Self {
1699 command: value.command,
1700 arguments: value.arguments,
1701 envs: value.envs,
1702 cwd: value.cwd,
1703 connection: value.connection.map(|c| latest::dap::TcpArguments {
1704 host: c.host,
1705 port: c.port,
1706 timeout: c.timeout,
1707 }),
1708 request_args: latest::dap::StartDebuggingRequestArguments {
1709 configuration: value.request_args.configuration,
1710 request: match value.request_args.request {
1711 dap::StartDebuggingRequestArgumentsRequest::Launch => {
1712 latest::dap::StartDebuggingRequestArgumentsRequest::Launch
1713 }
1714 dap::StartDebuggingRequestArgumentsRequest::Attach => {
1715 latest::dap::StartDebuggingRequestArgumentsRequest::Attach
1716 }
1717 },
1718 },
1719 }
1720 }
1721}
1722
1723impl From<dap::StartDebuggingRequestArgumentsRequest>
1724 for latest::dap::StartDebuggingRequestArgumentsRequest
1725{
1726 fn from(value: dap::StartDebuggingRequestArgumentsRequest) -> Self {
1727 match value {
1728 dap::StartDebuggingRequestArgumentsRequest::Launch => Self::Launch,
1729 dap::StartDebuggingRequestArgumentsRequest::Attach => Self::Attach,
1730 }
1731 }
1732}
1733
1734impl From<dap::DebugScenario> for latest::dap::DebugScenario {
1735 fn from(value: dap::DebugScenario) -> Self {
1736 Self {
1737 adapter: value.adapter,
1738 label: value.label,
1739 build: value.build.map(|b| match b {
1740 dap::BuildTaskDefinition::ByName(name) => {
1741 latest::dap::BuildTaskDefinition::ByName(name)
1742 }
1743 dap::BuildTaskDefinition::Template(t) => {
1744 latest::dap::BuildTaskDefinition::Template(
1745 latest::dap::BuildTaskDefinitionTemplatePayload {
1746 locator_name: t.locator_name,
1747 template: latest::dap::BuildTaskTemplate {
1748 label: t.template.label,
1749 command: t.template.command,
1750 args: t.template.args,
1751 env: t.template.env,
1752 cwd: t.template.cwd,
1753 },
1754 },
1755 )
1756 }
1757 }),
1758 config: value.config,
1759 tcp_connection: value
1760 .tcp_connection
1761 .map(|t| latest::dap::TcpArgumentsTemplate {
1762 host: t.host,
1763 port: t.port,
1764 timeout: t.timeout,
1765 }),
1766 }
1767 }
1768}
1769
1770impl From<dap::DebugRequest> for latest::dap::DebugRequest {
1771 fn from(value: dap::DebugRequest) -> Self {
1772 match value {
1773 dap::DebugRequest::Attach(a) => Self::Attach(latest::dap::AttachRequest {
1774 process_id: a.process_id,
1775 }),
1776 dap::DebugRequest::Launch(l) => Self::Launch(latest::dap::LaunchRequest {
1777 program: l.program,
1778 cwd: l.cwd,
1779 args: l.args,
1780 envs: l.envs,
1781 }),
1782 }
1783 }
1784}