@opencode-cloud/core 4.2.0 → 4.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "opencode-cloud-core"
3
- version = "4.2.0"
3
+ version = "4.2.1"
4
4
  edition = "2024"
5
5
  rust-version = "1.88"
6
6
  license = "MIT"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencode-cloud/core",
3
- "version": "4.2.0",
3
+ "version": "4.2.1",
4
4
  "description": "Core NAPI bindings for opencode-cloud (internal package)",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
package/src/config/mod.rs CHANGED
@@ -15,7 +15,7 @@ use anyhow::{Context, Result};
15
15
  use jsonc_parser::parse_to_serde_value;
16
16
 
17
17
  pub use paths::{get_config_dir, get_config_path, get_data_dir, get_hosts_path, get_pid_path};
18
- pub use schema::{Config, validate_bind_address};
18
+ pub use schema::{Config, default_mounts, validate_bind_address};
19
19
  pub use validation::{
20
20
  ValidationError, ValidationWarning, display_validation_error, display_validation_warning,
21
21
  validate_config,
@@ -75,6 +75,7 @@ pub fn load_config() -> Result<Config> {
75
75
  config_path.display()
76
76
  );
77
77
  let config = Config::default();
78
+ ensure_default_mount_dirs(&config)?;
78
79
  save_config(&config)?;
79
80
  return Ok(config);
80
81
  }
@@ -105,6 +106,7 @@ pub fn load_config() -> Result<Config> {
105
106
  )
106
107
  })?;
107
108
 
109
+ ensure_default_mount_dirs(&config)?;
108
110
  Ok(config)
109
111
  }
110
112
 
@@ -141,6 +143,36 @@ pub fn save_config(config: &Config) -> Result<()> {
141
143
  Ok(())
142
144
  }
143
145
 
146
+ fn ensure_default_mount_dirs(config: &Config) -> Result<()> {
147
+ let defaults = default_mounts();
148
+ if defaults.is_empty() {
149
+ return Ok(());
150
+ }
151
+
152
+ for mount_str in &config.mounts {
153
+ if !defaults.contains(mount_str) {
154
+ continue;
155
+ }
156
+ let parsed = crate::docker::mount::ParsedMount::parse(mount_str)
157
+ .with_context(|| format!("Invalid default mount configured: {mount_str}"))?;
158
+ let path = parsed.host_path.as_path();
159
+ if path.exists() {
160
+ if !path.is_dir() {
161
+ return Err(anyhow::anyhow!(
162
+ "Default mount path is not a directory: {}",
163
+ path.display()
164
+ ));
165
+ }
166
+ continue;
167
+ }
168
+ fs::create_dir_all(path)
169
+ .with_context(|| format!("Failed to create mount directory: {}", path.display()))?;
170
+ tracing::info!("Created mount directory: {}", path.display());
171
+ }
172
+
173
+ Ok(())
174
+ }
175
+
144
176
  #[cfg(test)]
145
177
  mod tests {
146
178
  use super::*;
@@ -2,6 +2,8 @@
2
2
  //!
3
3
  //! Defines the structure and defaults for the config.json file.
4
4
 
5
+ use super::paths::{get_config_dir, get_data_dir};
6
+ use crate::docker::volume::{MOUNT_CONFIG, MOUNT_PROJECTS, MOUNT_SESSION};
5
7
  use serde::{Deserialize, Serialize};
6
8
  use std::net::{IpAddr, Ipv4Addr};
7
9
  /// Main configuration structure for opencode-cloud
@@ -117,7 +119,7 @@ pub struct Config {
117
119
 
118
120
  /// Bind mounts to apply when starting the container
119
121
  /// Format: ["/host/path:/container/path", "/host:/mnt:ro"]
120
- #[serde(default)]
122
+ #[serde(default = "default_mounts")]
121
123
  pub mounts: Vec<String>,
122
124
  }
123
125
 
@@ -173,6 +175,24 @@ fn default_update_check() -> String {
173
175
  "always".to_string()
174
176
  }
175
177
 
178
+ pub fn default_mounts() -> Vec<String> {
179
+ let maybe_data_dir = get_data_dir();
180
+ let maybe_config_dir = get_config_dir();
181
+ let (Some(data_dir), Some(config_dir)) = (maybe_data_dir, maybe_config_dir) else {
182
+ return Vec::new();
183
+ };
184
+
185
+ let session_dir = data_dir.join("data");
186
+ let workspace_dir = data_dir.join("workspace");
187
+ let config_dir = config_dir.join("container");
188
+
189
+ vec![
190
+ format!("{}:{MOUNT_SESSION}", session_dir.display()),
191
+ format!("{}:{MOUNT_PROJECTS}", workspace_dir.display()),
192
+ format!("{}:{MOUNT_CONFIG}", config_dir.display()),
193
+ ]
194
+ }
195
+
176
196
  /// Validate and parse a bind address string
177
197
  ///
178
198
  /// Accepts:
@@ -225,7 +245,7 @@ impl Default for Config {
225
245
  cockpit_enabled: default_cockpit_enabled(),
226
246
  image_source: default_image_source(),
227
247
  update_check: default_update_check(),
228
- mounts: Vec::new(),
248
+ mounts: default_mounts(),
229
249
  }
230
250
  }
231
251
  }
@@ -309,7 +329,7 @@ mod tests {
309
329
  assert_eq!(config.rate_limit_attempts, 5);
310
330
  assert_eq!(config.rate_limit_window_seconds, 60);
311
331
  assert!(config.users.is_empty());
312
- assert!(config.mounts.is_empty());
332
+ assert_eq!(config.mounts, default_mounts());
313
333
  }
314
334
 
315
335
  #[test]
@@ -693,7 +713,7 @@ mod tests {
693
713
  #[test]
694
714
  fn test_default_config_mounts_field() {
695
715
  let config = Config::default();
696
- assert!(config.mounts.is_empty());
716
+ assert_eq!(config.mounts, default_mounts());
697
717
  }
698
718
 
699
719
  #[test]
@@ -717,9 +737,9 @@ mod tests {
717
737
 
718
738
  #[test]
719
739
  fn test_mounts_field_default_on_missing() {
720
- // Old configs without mounts field should get empty vec
740
+ // Old configs without mounts field should get default mounts
721
741
  let json = r#"{"version": 1}"#;
722
742
  let config: Config = serde_json::from_str(json).unwrap();
723
- assert!(config.mounts.is_empty());
743
+ assert_eq!(config.mounts, default_mounts());
724
744
  }
725
745
  }