diff --git a/Cargo.lock b/Cargo.lock
index da08ad6363..ab51b79c5a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -520,7 +520,7 @@ dependencies = [
"polling 3.3.2",
"rustix 0.38.32",
"slab",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"windows-sys 0.52.0",
]
@@ -861,7 +861,7 @@ dependencies = [
"ring 0.17.7",
"time",
"tokio",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"zeroize",
]
@@ -897,7 +897,7 @@ dependencies = [
"http-body",
"percent-encoding",
"pin-project-lite",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid",
]
@@ -926,7 +926,7 @@ dependencies = [
"once_cell",
"percent-encoding",
"regex-lite",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"url",
]
@@ -949,7 +949,7 @@ dependencies = [
"http 0.2.9",
"once_cell",
"regex-lite",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -971,7 +971,7 @@ dependencies = [
"http 0.2.9",
"once_cell",
"regex-lite",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -994,7 +994,7 @@ dependencies = [
"http 0.2.9",
"once_cell",
"regex-lite",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1022,7 +1022,7 @@ dependencies = [
"sha2 0.10.7",
"subtle",
"time",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"zeroize",
]
@@ -1055,7 +1055,7 @@ dependencies = [
"pin-project-lite",
"sha1",
"sha2 0.10.7",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1087,7 +1087,7 @@ dependencies = [
"percent-encoding",
"pin-project-lite",
"pin-utils",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1131,7 +1131,7 @@ dependencies = [
"pin-utils",
"rustls",
"tokio",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1146,7 +1146,7 @@ dependencies = [
"http 0.2.9",
"pin-project-lite",
"tokio",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"zeroize",
]
@@ -1194,7 +1194,7 @@ dependencies = [
"aws-smithy-types",
"http 0.2.9",
"rustc_version",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1517,7 +1517,7 @@ dependencies = [
"futures-io",
"futures-lite 2.2.0",
"piper",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1877,6 +1877,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+[[package]]
+name = "cfg_aliases"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
+
[[package]]
name = "channel"
version = "0.1.0"
@@ -2238,6 +2244,7 @@ dependencies = [
"git",
"google_ai",
"gpui",
+ "headless",
"hex",
"indoc",
"language",
@@ -2279,7 +2286,7 @@ dependencies = [
"toml 0.8.10",
"tower",
"tower-http 0.4.4",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"tracing-subscriber",
"unindent",
"util",
@@ -2302,6 +2309,7 @@ dependencies = [
"editor",
"emojis",
"extensions_ui",
+ "feature_flags",
"futures 0.3.28",
"fuzzy",
"gpui",
@@ -2966,11 +2974,11 @@ dependencies = [
[[package]]
name = "ctrlc"
-version = "3.4.2"
+version = "3.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b"
+checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345"
dependencies = [
- "nix 0.27.1",
+ "nix 0.28.0",
"windows-sys 0.52.0",
]
@@ -4550,7 +4558,7 @@ dependencies = [
"slab",
"tokio",
"tokio-util",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -4624,6 +4632,26 @@ dependencies = [
"http 0.2.9",
]
+[[package]]
+name = "headless"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "client",
+ "ctrlc",
+ "fs",
+ "futures 0.3.28",
+ "gpui",
+ "language",
+ "log",
+ "node_runtime",
+ "postage",
+ "project",
+ "rpc",
+ "settings",
+ "util",
+]
+
[[package]]
name = "heck"
version = "0.3.3"
@@ -4806,7 +4834,7 @@ dependencies = [
"socket2 0.4.9",
"tokio",
"tower-service",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"want",
]
@@ -5121,7 +5149,7 @@ dependencies = [
"polling 2.8.0",
"slab",
"sluice",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"tracing-futures",
"url",
"waker-fn",
@@ -6100,6 +6128,18 @@ dependencies = [
"memoffset",
]
+[[package]]
+name = "nix"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
+dependencies = [
+ "bitflags 2.4.2",
+ "cfg-if",
+ "cfg_aliases",
+ "libc",
+]
+
[[package]]
name = "node_runtime"
version = "0.1.0"
@@ -7036,7 +7076,7 @@ dependencies = [
"concurrent-queue",
"pin-project-lite",
"rustix 0.38.32",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"windows-sys 0.52.0",
]
@@ -7941,7 +7981,7 @@ dependencies = [
"serde",
"serde_json",
"strum",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"util",
"zstd",
]
@@ -8308,7 +8348,7 @@ dependencies = [
"strum",
"thiserror",
"time",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"url",
"uuid",
]
@@ -9059,7 +9099,7 @@ dependencies = [
"time",
"tokio",
"tokio-stream",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"url",
"uuid",
"webpki-roots",
@@ -9146,7 +9186,7 @@ dependencies = [
"stringprep",
"thiserror",
"time",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid",
"whoami",
]
@@ -9191,7 +9231,7 @@ dependencies = [
"stringprep",
"thiserror",
"time",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid",
"whoami",
]
@@ -9216,7 +9256,7 @@ dependencies = [
"serde",
"sqlx-core",
"time",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"url",
"uuid",
]
@@ -10039,7 +10079,7 @@ dependencies = [
"futures-sink",
"pin-project-lite",
"tokio",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -10134,7 +10174,7 @@ dependencies = [
"tokio",
"tower-layer",
"tower-service",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -10171,7 +10211,7 @@ dependencies = [
"pin-project-lite",
"tower-layer",
"tower-service",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -10188,22 +10228,30 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
[[package]]
name = "tracing"
-version = "0.1.37"
+version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
dependencies = [
- "cfg-if",
"log",
"pin-project-lite",
"tracing-attributes",
- "tracing-core",
+ "tracing-core 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "tracing"
+version = "0.1.40"
+source = "git+https://github.com/tokio-rs/tracing?rev=tracing-subscriber-0.3.18#8b7a1dde69797b33ecfa20da71e72eb5e61f0b25"
+dependencies = [
+ "pin-project-lite",
+ "tracing-core 0.1.32 (git+https://github.com/tokio-rs/tracing?rev=tracing-subscriber-0.3.18)",
]
[[package]]
name = "tracing-attributes"
-version = "0.1.26"
+version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
+checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
@@ -10212,9 +10260,17 @@ dependencies = [
[[package]]
name = "tracing-core"
-version = "0.1.31"
+version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "git+https://github.com/tokio-rs/tracing?rev=tracing-subscriber-0.3.18#8b7a1dde69797b33ecfa20da71e72eb5e61f0b25"
dependencies = [
"once_cell",
"valuable",
@@ -10227,35 +10283,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2"
dependencies = [
"pin-project",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tracing-log"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
+version = "0.2.0"
+source = "git+https://github.com/tokio-rs/tracing?rev=tracing-subscriber-0.3.18#8b7a1dde69797b33ecfa20da71e72eb5e61f0b25"
dependencies = [
- "lazy_static",
"log",
- "tracing-core",
+ "once_cell",
+ "tracing-core 0.1.32 (git+https://github.com/tokio-rs/tracing?rev=tracing-subscriber-0.3.18)",
]
[[package]]
name = "tracing-serde"
version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1"
+source = "git+https://github.com/tokio-rs/tracing?rev=tracing-subscriber-0.3.18#8b7a1dde69797b33ecfa20da71e72eb5e61f0b25"
dependencies = [
"serde",
- "tracing-core",
+ "tracing-core 0.1.32 (git+https://github.com/tokio-rs/tracing?rev=tracing-subscriber-0.3.18)",
]
[[package]]
name = "tracing-subscriber"
-version = "0.3.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77"
+version = "0.3.18"
+source = "git+https://github.com/tokio-rs/tracing?rev=tracing-subscriber-0.3.18#8b7a1dde69797b33ecfa20da71e72eb5e61f0b25"
dependencies = [
"matchers",
"nu-ansi-term",
@@ -10266,8 +10319,8 @@ dependencies = [
"sharded-slab",
"smallvec",
"thread_local",
- "tracing",
- "tracing-core",
+ "tracing 0.1.40 (git+https://github.com/tokio-rs/tracing?rev=tracing-subscriber-0.3.18)",
+ "tracing-core 0.1.32 (git+https://github.com/tokio-rs/tracing?rev=tracing-subscriber-0.3.18)",
"tracing-log",
"tracing-serde",
]
@@ -11157,7 +11210,7 @@ dependencies = [
"anyhow",
"log",
"once_cell",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmtime",
"wasmtime-c-api-macros",
]
@@ -11369,7 +11422,7 @@ dependencies = [
"system-interface",
"thiserror",
"tokio",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"url",
"wasmtime",
"wiggle",
@@ -11606,7 +11659,7 @@ dependencies = [
"async-trait",
"bitflags 2.4.2",
"thiserror",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmtime",
"wiggle-macro",
]
@@ -12362,7 +12415,7 @@ dependencies = [
"serde_repr",
"sha1",
"static_assertions",
- "tracing",
+ "tracing 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"uds_windows",
"windows-sys 0.52.0",
"xdg-home",
@@ -12434,6 +12487,7 @@ dependencies = [
"futures 0.3.28",
"go_to_line",
"gpui",
+ "headless",
"image_viewer",
"install_cli",
"isahc",
diff --git a/Cargo.toml b/Cargo.toml
index e8b03c0d96..5802072805 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -38,6 +38,7 @@ members = [
"crates/google_ai",
"crates/gpui",
"crates/gpui_macros",
+ "crates/headless",
"crates/image_viewer",
"crates/install_cli",
"crates/journal",
@@ -164,6 +165,7 @@ go_to_line = { path = "crates/go_to_line" }
google_ai = { path = "crates/google_ai" }
gpui = { path = "crates/gpui" }
gpui_macros = { path = "crates/gpui_macros" }
+headless = { path = "crates/headless" }
install_cli = { path = "crates/install_cli" }
image_viewer = { path = "crates/image_viewer" }
journal = { path = "crates/journal" }
@@ -242,6 +244,7 @@ chrono = { version = "0.4", features = ["serde"] }
clap = { version = "4.4", features = ["derive"] }
clickhouse = { version = "0.11.6" }
ctor = "0.2.6"
+ctrlc = "3.4.4"
core-foundation = { version = "0.9.3" }
core-foundation-sys = "0.8.6"
derive_more = "0.99.17"
diff --git a/assets/icons/server.svg b/assets/icons/server.svg
new file mode 100644
index 0000000000..10fbdcbff4
--- /dev/null
+++ b/assets/icons/server.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/trash.svg b/assets/icons/trash.svg
new file mode 100644
index 0000000000..94d7971f9b
--- /dev/null
+++ b/assets/icons/trash.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs
index 94c055e6b8..7ec80334e4 100644
--- a/crates/call/src/room.rs
+++ b/crates/call/src/room.rs
@@ -1182,7 +1182,7 @@ impl Room {
cx.emit(Event::RemoteProjectJoined { project_id: id });
cx.spawn(move |this, mut cx| async move {
let project =
- Project::remote(id, client, user_store, language_registry, fs, cx.clone()).await?;
+ Project::in_room(id, client, user_store, language_registry, fs, cx.clone()).await?;
this.update(&mut cx, |this, cx| {
this.joined_projects.retain(|project| {
diff --git a/crates/channel/src/channel.rs b/crates/channel/src/channel.rs
index aee92d0f6c..f592c1f8e7 100644
--- a/crates/channel/src/channel.rs
+++ b/crates/channel/src/channel.rs
@@ -11,7 +11,9 @@ pub use channel_chat::{
mentions_to_proto, ChannelChat, ChannelChatEvent, ChannelMessage, ChannelMessageId,
MessageParams,
};
-pub use channel_store::{Channel, ChannelEvent, ChannelMembership, ChannelStore};
+pub use channel_store::{
+ Channel, ChannelEvent, ChannelMembership, ChannelStore, DevServer, RemoteProject,
+};
#[cfg(test)]
mod channel_store_tests;
diff --git a/crates/channel/src/channel_store.rs b/crates/channel/src/channel_store.rs
index 28f9150143..0d323a2fa0 100644
--- a/crates/channel/src/channel_store.rs
+++ b/crates/channel/src/channel_store.rs
@@ -3,7 +3,10 @@ mod channel_index;
use crate::{channel_buffer::ChannelBuffer, channel_chat::ChannelChat, ChannelMessage};
use anyhow::{anyhow, Result};
use channel_index::ChannelIndex;
-use client::{ChannelId, Client, ClientSettings, ProjectId, Subscription, User, UserId, UserStore};
+use client::{
+ ChannelId, Client, ClientSettings, DevServerId, ProjectId, RemoteProjectId, Subscription, User,
+ UserId, UserStore,
+};
use collections::{hash_map, HashMap, HashSet};
use futures::{channel::mpsc, future::Shared, Future, FutureExt, StreamExt};
use gpui::{
@@ -12,7 +15,7 @@ use gpui::{
};
use language::Capability;
use rpc::{
- proto::{self, ChannelRole, ChannelVisibility},
+ proto::{self, ChannelRole, ChannelVisibility, DevServerStatus},
TypedEnvelope,
};
use settings::Settings;
@@ -40,7 +43,6 @@ pub struct HostedProject {
name: SharedString,
_visibility: proto::ChannelVisibility,
}
-
impl From for HostedProject {
fn from(project: proto::HostedProject) -> Self {
Self {
@@ -52,12 +54,56 @@ impl From for HostedProject {
}
}
+#[derive(Debug, Clone)]
+pub struct RemoteProject {
+ pub id: RemoteProjectId,
+ pub project_id: Option,
+ pub channel_id: ChannelId,
+ pub name: SharedString,
+ pub path: SharedString,
+ pub dev_server_id: DevServerId,
+}
+
+impl From for RemoteProject {
+ fn from(project: proto::RemoteProject) -> Self {
+ Self {
+ id: RemoteProjectId(project.id),
+ project_id: project.project_id.map(|id| ProjectId(id)),
+ channel_id: ChannelId(project.channel_id),
+ name: project.name.into(),
+ path: project.path.into(),
+ dev_server_id: DevServerId(project.dev_server_id),
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct DevServer {
+ pub id: DevServerId,
+ pub channel_id: ChannelId,
+ pub name: SharedString,
+ pub status: DevServerStatus,
+}
+
+impl From for DevServer {
+ fn from(dev_server: proto::DevServer) -> Self {
+ Self {
+ id: DevServerId(dev_server.dev_server_id),
+ channel_id: ChannelId(dev_server.channel_id),
+ status: dev_server.status(),
+ name: dev_server.name.into(),
+ }
+ }
+}
+
pub struct ChannelStore {
pub channel_index: ChannelIndex,
channel_invitations: Vec>,
channel_participants: HashMap>>,
channel_states: HashMap,
hosted_projects: HashMap,
+ remote_projects: HashMap,
+ dev_servers: HashMap,
outgoing_invites: HashSet<(ChannelId, UserId)>,
update_channels_tx: mpsc::UnboundedSender,
@@ -87,6 +133,8 @@ pub struct ChannelState {
observed_chat_message: Option,
role: Option,
projects: HashSet,
+ dev_servers: HashSet,
+ remote_projects: HashSet,
}
impl Channel {
@@ -217,6 +265,8 @@ impl ChannelStore {
channel_index: ChannelIndex::default(),
channel_participants: Default::default(),
hosted_projects: Default::default(),
+ remote_projects: Default::default(),
+ dev_servers: Default::default(),
outgoing_invites: Default::default(),
opened_buffers: Default::default(),
opened_chats: Default::default(),
@@ -316,6 +366,40 @@ impl ChannelStore {
projects
}
+ pub fn dev_servers_for_id(&self, channel_id: ChannelId) -> Vec {
+ let mut dev_servers: Vec = self
+ .channel_states
+ .get(&channel_id)
+ .map(|state| state.dev_servers.clone())
+ .unwrap_or_default()
+ .into_iter()
+ .flat_map(|id| self.dev_servers.get(&id).cloned())
+ .collect();
+ dev_servers.sort_by_key(|s| (s.name.clone(), s.id));
+ dev_servers
+ }
+
+ pub fn find_dev_server_by_id(&self, id: DevServerId) -> Option<&DevServer> {
+ self.dev_servers.get(&id)
+ }
+
+ pub fn find_remote_project_by_id(&self, id: RemoteProjectId) -> Option<&RemoteProject> {
+ self.remote_projects.get(&id)
+ }
+
+ pub fn remote_projects_for_id(&self, channel_id: ChannelId) -> Vec {
+ let mut remote_projects: Vec = self
+ .channel_states
+ .get(&channel_id)
+ .map(|state| state.remote_projects.clone())
+ .unwrap_or_default()
+ .into_iter()
+ .flat_map(|id| self.remote_projects.get(&id).cloned())
+ .collect();
+ remote_projects.sort_by_key(|p| (p.name.clone(), p.id));
+ remote_projects
+ }
+
pub fn has_open_channel_buffer(&self, channel_id: ChannelId, _cx: &AppContext) -> bool {
if let Some(buffer) = self.opened_buffers.get(&channel_id) {
if let OpenedModelHandle::Open(buffer) = buffer {
@@ -818,6 +902,45 @@ impl ChannelStore {
})
}
+ pub fn create_remote_project(
+ &mut self,
+ channel_id: ChannelId,
+ dev_server_id: DevServerId,
+ name: String,
+ path: String,
+ cx: &mut ModelContext,
+ ) -> Task> {
+ let client = self.client.clone();
+ cx.background_executor().spawn(async move {
+ client
+ .request(proto::CreateRemoteProject {
+ channel_id: channel_id.0,
+ dev_server_id: dev_server_id.0,
+ name,
+ path,
+ })
+ .await
+ })
+ }
+
+ pub fn create_dev_server(
+ &mut self,
+ channel_id: ChannelId,
+ name: String,
+ cx: &mut ModelContext,
+ ) -> Task> {
+ let client = self.client.clone();
+ cx.background_executor().spawn(async move {
+ let result = client
+ .request(proto::CreateDevServer {
+ channel_id: channel_id.0,
+ name,
+ })
+ .await?;
+ Ok(result)
+ })
+ }
+
pub fn get_channel_member_details(
&self,
channel_id: ChannelId,
@@ -1098,7 +1221,11 @@ impl ChannelStore {
|| !payload.latest_channel_message_ids.is_empty()
|| !payload.latest_channel_buffer_versions.is_empty()
|| !payload.hosted_projects.is_empty()
- || !payload.deleted_hosted_projects.is_empty();
+ || !payload.deleted_hosted_projects.is_empty()
+ || !payload.dev_servers.is_empty()
+ || !payload.deleted_dev_servers.is_empty()
+ || !payload.remote_projects.is_empty()
+ || !payload.deleted_remote_projects.is_empty();
if channels_changed {
if !payload.delete_channels.is_empty() {
@@ -1186,6 +1313,60 @@ impl ChannelStore {
.remove_hosted_project(old_project.project_id);
}
}
+
+ for remote_project in payload.remote_projects {
+ let remote_project: RemoteProject = remote_project.into();
+ if let Some(old_remote_project) = self
+ .remote_projects
+ .insert(remote_project.id, remote_project.clone())
+ {
+ self.channel_states
+ .entry(old_remote_project.channel_id)
+ .or_default()
+ .remove_remote_project(old_remote_project.id);
+ }
+ self.channel_states
+ .entry(remote_project.channel_id)
+ .or_default()
+ .add_remote_project(remote_project.id);
+ }
+
+ for remote_project_id in payload.deleted_remote_projects {
+ let remote_project_id = RemoteProjectId(remote_project_id);
+
+ if let Some(old_project) = self.remote_projects.remove(&remote_project_id) {
+ self.channel_states
+ .entry(old_project.channel_id)
+ .or_default()
+ .remove_remote_project(old_project.id);
+ }
+ }
+
+ for dev_server in payload.dev_servers {
+ let dev_server: DevServer = dev_server.into();
+ if let Some(old_server) = self.dev_servers.insert(dev_server.id, dev_server.clone())
+ {
+ self.channel_states
+ .entry(old_server.channel_id)
+ .or_default()
+ .remove_dev_server(old_server.id);
+ }
+ self.channel_states
+ .entry(dev_server.channel_id)
+ .or_default()
+ .add_dev_server(dev_server.id);
+ }
+
+ for dev_server_id in payload.deleted_dev_servers {
+ let dev_server_id = DevServerId(dev_server_id);
+
+ if let Some(old_server) = self.dev_servers.remove(&dev_server_id) {
+ self.channel_states
+ .entry(old_server.channel_id)
+ .or_default()
+ .remove_dev_server(old_server.id);
+ }
+ }
}
cx.notify();
@@ -1300,4 +1481,20 @@ impl ChannelState {
fn remove_hosted_project(&mut self, project_id: ProjectId) {
self.projects.remove(&project_id);
}
+
+ fn add_remote_project(&mut self, remote_project_id: RemoteProjectId) {
+ self.remote_projects.insert(remote_project_id);
+ }
+
+ fn remove_remote_project(&mut self, remote_project_id: RemoteProjectId) {
+ self.remote_projects.remove(&remote_project_id);
+ }
+
+ fn add_dev_server(&mut self, dev_server_id: DevServerId) {
+ self.dev_servers.insert(dev_server_id);
+ }
+
+ fn remove_dev_server(&mut self, dev_server_id: DevServerId) {
+ self.dev_servers.remove(&dev_server_id);
+ }
}
diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs
index 7b696d1eaa..db88afb038 100644
--- a/crates/client/src/client.rs
+++ b/crates/client/src/client.rs
@@ -759,8 +759,9 @@ impl Client {
read_credentials_from_keychain(cx).await.is_some()
}
- pub fn set_dev_server_token(&self, token: DevServerToken) {
+ pub fn set_dev_server_token(&self, token: DevServerToken) -> &Self {
self.state.write().credentials = Some(Credentials::DevServer { token });
+ self
}
#[async_recursion(?Send)]
diff --git a/crates/client/src/user.rs b/crates/client/src/user.rs
index e8be09dd64..2c5632593d 100644
--- a/crates/client/src/user.rs
+++ b/crates/client/src/user.rs
@@ -27,6 +27,12 @@ impl std::fmt::Display for ChannelId {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub struct ProjectId(pub u64);
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
+pub struct DevServerId(pub u64);
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
+pub struct RemoteProjectId(pub u64);
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ParticipantIndex(pub u32);
diff --git a/crates/collab/.env.toml b/crates/collab/.env.toml
index ee01d75782..9bfdf294e4 100644
--- a/crates/collab/.env.toml
+++ b/crates/collab/.env.toml
@@ -1,5 +1,5 @@
DATABASE_URL = "postgres://postgres@localhost/zed"
-# DATABASE_URL = "sqlite:////home/zed/.config/zed/db.sqlite3?mode=rwc"
+# DATABASE_URL = "sqlite:////root/0/zed/db.sqlite3?mode=rwc"
DATABASE_MAX_CONNECTIONS = 5
HTTP_PORT = 8080
API_TOKEN = "secret"
diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml
index 7fbc4bfd03..f83403c705 100644
--- a/crates/collab/Cargo.toml
+++ b/crates/collab/Cargo.toml
@@ -63,8 +63,8 @@ tokio.workspace = true
toml.workspace = true
tower = "0.4"
tower-http = { workspace = true, features = ["trace"] }
-tracing = "0.1.34"
-tracing-subscriber = { version = "0.3.11", features = ["env-filter", "json", "registry", "tracing-log"] }
+tracing = "0.1.40"
+tracing-subscriber = { git = "https://github.com/tokio-rs/tracing", rev = "tracing-subscriber-0.3.18", features = ["env-filter", "json", "registry", "tracing-log"] } # workaround for https://github.com/tokio-rs/tracing/issues/2927
util.workspace = true
uuid.workspace = true
@@ -102,3 +102,4 @@ theme.workspace = true
unindent.workspace = true
util.workspace = true
workspace = { workspace = true, features = ["test-support"] }
+headless.workspace = true
diff --git a/crates/collab/migrations.sqlite/20221109000000_test_schema.sql b/crates/collab/migrations.sqlite/20221109000000_test_schema.sql
index 9ad045e56d..bc14721e21 100644
--- a/crates/collab/migrations.sqlite/20221109000000_test_schema.sql
+++ b/crates/collab/migrations.sqlite/20221109000000_test_schema.sql
@@ -45,12 +45,13 @@ CREATE UNIQUE INDEX "index_rooms_on_channel_id" ON "rooms" ("channel_id");
CREATE TABLE "projects" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
- "room_id" INTEGER REFERENCES rooms (id) ON DELETE CASCADE NOT NULL,
+ "room_id" INTEGER REFERENCES rooms (id) ON DELETE CASCADE,
"host_user_id" INTEGER REFERENCES users (id),
"host_connection_id" INTEGER,
"host_connection_server_id" INTEGER REFERENCES servers (id) ON DELETE CASCADE,
"unregistered" BOOLEAN NOT NULL DEFAULT FALSE,
- "hosted_project_id" INTEGER REFERENCES hosted_projects (id)
+ "hosted_project_id" INTEGER REFERENCES hosted_projects (id),
+ "remote_project_id" INTEGER REFERENCES remote_projects(id)
);
CREATE INDEX "index_projects_on_host_connection_server_id" ON "projects" ("host_connection_server_id");
CREATE INDEX "index_projects_on_host_connection_id_and_host_connection_server_id" ON "projects" ("host_connection_id", "host_connection_server_id");
@@ -397,7 +398,9 @@ CREATE TABLE hosted_projects (
channel_id INTEGER NOT NULL REFERENCES channels(id),
name TEXT NOT NULL,
visibility TEXT NOT NULL,
- deleted_at TIMESTAMP NULL
+ deleted_at TIMESTAMP NULL,
+ dev_server_id INTEGER REFERENCES dev_servers(id),
+ dev_server_path TEXT
);
CREATE INDEX idx_hosted_projects_on_channel_id ON hosted_projects (channel_id);
CREATE UNIQUE INDEX uix_hosted_projects_on_channel_id_and_name ON hosted_projects (channel_id, name) WHERE (deleted_at IS NULL);
@@ -409,3 +412,13 @@ CREATE TABLE dev_servers (
hashed_token TEXT NOT NULL
);
CREATE INDEX idx_dev_servers_on_channel_id ON dev_servers (channel_id);
+
+CREATE TABLE remote_projects (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ channel_id INTEGER NOT NULL REFERENCES channels(id),
+ dev_server_id INTEGER NOT NULL REFERENCES dev_servers(id),
+ name TEXT NOT NULL,
+ path TEXT NOT NULL
+);
+
+ALTER TABLE hosted_projects ADD COLUMN remote_project_id INTEGER REFERENCES remote_projects(id);
diff --git a/crates/collab/migrations/20240402155003_add_dev_server_projects.sql b/crates/collab/migrations/20240402155003_add_dev_server_projects.sql
new file mode 100644
index 0000000000..003c43f4e2
--- /dev/null
+++ b/crates/collab/migrations/20240402155003_add_dev_server_projects.sql
@@ -0,0 +1,9 @@
+CREATE TABLE remote_projects (
+ id INT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
+ channel_id INT NOT NULL REFERENCES channels(id),
+ dev_server_id INT NOT NULL REFERENCES dev_servers(id),
+ name TEXT NOT NULL,
+ path TEXT NOT NULL
+);
+
+ALTER TABLE projects ADD COLUMN remote_project_id INTEGER REFERENCES remote_projects(id);
diff --git a/crates/collab/src/auth.rs b/crates/collab/src/auth.rs
index 5daf6e6186..915563d6b4 100644
--- a/crates/collab/src/auth.rs
+++ b/crates/collab/src/auth.rs
@@ -10,6 +10,7 @@ use axum::{
response::IntoResponse,
};
use prometheus::{exponential_buckets, register_histogram, Histogram};
+pub use rpc::auth::random_token;
use scrypt::{
password_hash::{PasswordHash, PasswordVerifier},
Scrypt,
@@ -152,7 +153,7 @@ pub async fn create_access_token(
/// Hashing prevents anyone with access to the database being able to login.
/// As the token is randomly generated, we don't need to worry about scrypt-style
/// protection.
-fn hash_access_token(token: &str) -> String {
+pub fn hash_access_token(token: &str) -> String {
let digest = sha2::Sha256::digest(token);
format!(
"$sha256${}",
@@ -230,18 +231,15 @@ pub async fn verify_access_token(
})
}
-// a dev_server_token has the format .. This is to make them
-// relatively easy to copy/paste around.
+pub fn generate_dev_server_token(id: usize, access_token: String) -> String {
+ format!("{}.{}", id, access_token)
+}
+
pub async fn verify_dev_server_token(
dev_server_token: &str,
db: &Arc,
) -> anyhow::Result {
- let mut parts = dev_server_token.splitn(2, '.');
- let id = DevServerId(parts.next().unwrap_or_default().parse()?);
- let token = parts
- .next()
- .ok_or_else(|| anyhow!("invalid dev server token format"))?;
-
+ let (id, token) = split_dev_server_token(dev_server_token)?;
let token_hash = hash_access_token(&token);
let server = db.get_dev_server(id).await?;
@@ -257,6 +255,17 @@ pub async fn verify_dev_server_token(
}
}
+// a dev_server_token has the format .. This is to make them
+// relatively easy to copy/paste around.
+pub fn split_dev_server_token(dev_server_token: &str) -> anyhow::Result<(DevServerId, &str)> {
+ let mut parts = dev_server_token.splitn(2, '.');
+ let id = DevServerId(parts.next().unwrap_or_default().parse()?);
+ let token = parts
+ .next()
+ .ok_or_else(|| anyhow!("invalid dev server token format"))?;
+ Ok((id, token))
+}
+
#[cfg(test)]
mod test {
use rand::thread_rng;
diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs
index e9249edcb1..24bae3fba7 100644
--- a/crates/collab/src/db.rs
+++ b/crates/collab/src/db.rs
@@ -56,6 +56,7 @@ pub struct Database {
options: ConnectOptions,
pool: DatabaseConnection,
rooms: DashMap>>,
+ projects: DashMap>>,
rng: Mutex,
executor: Executor,
notification_kinds_by_id: HashMap,
@@ -74,6 +75,7 @@ impl Database {
options: options.clone(),
pool: sea_orm::Database::connect(options).await?,
rooms: DashMap::with_capacity(16384),
+ projects: DashMap::with_capacity(16384),
rng: Mutex::new(StdRng::seed_from_u64(0)),
notification_kinds_by_id: HashMap::default(),
notification_kinds_by_name: HashMap::default(),
@@ -86,6 +88,7 @@ impl Database {
#[cfg(test)]
pub fn reset(&self) {
self.rooms.clear();
+ self.projects.clear();
}
/// Runs the database migrations.
@@ -190,7 +193,10 @@ impl Database {
}
/// The same as room_transaction, but if you need to only optionally return a Room.
- async fn optional_room_transaction(&self, f: F) -> Result