act-cli 0.8.1__tar.gz → 0.8.2__tar.gz

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.
Files changed (40) hide show
  1. {act_cli-0.8.1 → act_cli-0.8.2}/Cargo.lock +9 -2
  2. {act_cli-0.8.1 → act_cli-0.8.2}/Cargo.toml +1 -1
  3. {act_cli-0.8.1 → act_cli-0.8.2}/PKG-INFO +1 -1
  4. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/Cargo.toml +1 -0
  5. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/src/main.rs +42 -1
  6. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/src/runtime/mod.rs +19 -3
  7. {act_cli-0.8.1 → act_cli-0.8.2}/README.md +0 -0
  8. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/README.md +0 -0
  9. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/build.rs +0 -0
  10. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/src/config.rs +0 -0
  11. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/src/format.rs +0 -0
  12. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/src/http.rs +0 -0
  13. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/src/resolve.rs +0 -0
  14. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/src/rmcp_bridge.rs +0 -0
  15. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/src/runtime/bindings/mod.rs +0 -0
  16. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/src/runtime/effective.rs +0 -0
  17. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/src/runtime/fs_matcher.rs +0 -0
  18. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/src/runtime/fs_policy.rs +0 -0
  19. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/src/runtime/http_client.rs +0 -0
  20. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/src/runtime/http_policy.rs +0 -0
  21. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/src/runtime/network.rs +0 -0
  22. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/src/runtime/sessions.rs +0 -0
  23. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/src/runtime/sockets_policy.rs +0 -0
  24. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/wit/deps/act-core/act-core.wit +0 -0
  25. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/wit/deps/act-tools/act-tools.wit +0 -0
  26. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/wit/deps.lock +0 -0
  27. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/wit/deps.toml +0 -0
  28. {act_cli-0.8.1 → act_cli-0.8.2}/act-cli/wit/world.wit +0 -0
  29. {act_cli-0.8.1 → act_cli-0.8.2}/crates/act-store/Cargo.toml +0 -0
  30. {act_cli-0.8.1 → act_cli-0.8.2}/crates/act-store/README.md +0 -0
  31. {act_cli-0.8.1 → act_cli-0.8.2}/crates/act-store/src/fetch.rs +0 -0
  32. {act_cli-0.8.1 → act_cli-0.8.2}/crates/act-store/src/index.rs +0 -0
  33. {act_cli-0.8.1 → act_cli-0.8.2}/crates/act-store/src/layout.rs +0 -0
  34. {act_cli-0.8.1 → act_cli-0.8.2}/crates/act-store/src/lib.rs +0 -0
  35. {act_cli-0.8.1 → act_cli-0.8.2}/crates/act-store/src/lock.rs +0 -0
  36. {act_cli-0.8.1 → act_cli-0.8.2}/crates/act-store/src/provenance.rs +0 -0
  37. {act_cli-0.8.1 → act_cli-0.8.2}/crates/act-store/src/reference.rs +0 -0
  38. {act_cli-0.8.1 → act_cli-0.8.2}/crates/act-store/src/referrer.rs +0 -0
  39. {act_cli-0.8.1 → act_cli-0.8.2}/crates/act-store/src/store.rs +0 -0
  40. {act_cli-0.8.1 → act_cli-0.8.2}/pyproject.toml +0 -0
@@ -4,7 +4,7 @@ version = 4
4
4
 
5
5
  [[package]]
6
6
  name = "act-build"
7
- version = "0.8.1"
7
+ version = "0.8.2"
8
8
  dependencies = [
9
9
  "act-types",
10
10
  "anyhow",
@@ -37,7 +37,7 @@ dependencies = [
37
37
 
38
38
  [[package]]
39
39
  name = "act-cli"
40
- version = "0.8.1"
40
+ version = "0.8.2"
41
41
  dependencies = [
42
42
  "act-store",
43
43
  "act-types",
@@ -45,6 +45,7 @@ dependencies = [
45
45
  "axum",
46
46
  "base64",
47
47
  "bytes",
48
+ "bytesize",
48
49
  "ciborium",
49
50
  "cidr",
50
51
  "clap",
@@ -384,6 +385,12 @@ version = "1.11.1"
384
385
  source = "registry+https://github.com/rust-lang/crates.io-index"
385
386
  checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
386
387
 
388
+ [[package]]
389
+ name = "bytesize"
390
+ version = "2.3.1"
391
+ source = "registry+https://github.com/rust-lang/crates.io-index"
392
+ checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3"
393
+
387
394
  [[package]]
388
395
  name = "cap-fs-ext"
389
396
  version = "3.4.5"
@@ -3,7 +3,7 @@ members = ["act-cli", "crates/act-store"]
3
3
  resolver = "3"
4
4
 
5
5
  [workspace.package]
6
- version = "0.8.1"
6
+ version = "0.8.2"
7
7
  edition = "2024"
8
8
  license = "MIT OR Apache-2.0"
9
9
  repository = "https://github.com/actcore/act-cli"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: act-cli
3
- Version: 0.8.1
3
+ Version: 0.8.2
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -57,6 +57,7 @@ globset = "0.4"
57
57
  path-clean = "1"
58
58
  owo-colors = { version = "4.3.0", features = ["supports-colors"] }
59
59
  act-store = { version = "0.1.0", path = "../crates/act-store" }
60
+ bytesize = "2.3.1"
60
61
 
61
62
  [dev-dependencies]
62
63
  tempfile = "3"
@@ -55,6 +55,12 @@ struct CommonOpts {
55
55
  #[arg(long = "deny-socket")]
56
56
  sockets_deny: Vec<String>,
57
57
 
58
+ /// Cap the component's wasm linear memory. Accepts a byte count or a size
59
+ /// with a unit — binary (`512MiB`) or decimal (`512MB`). Growth past the cap
60
+ /// fails inside the guest instead of ballooning the host process.
61
+ #[arg(long = "max-memory", value_parser = parse_max_memory)]
62
+ max_memory: Option<usize>,
63
+
58
64
  /// Use a named profile from the config file
59
65
  #[arg(long)]
60
66
  profile: Option<String>,
@@ -318,6 +324,22 @@ fn parse_cli_metadata(
318
324
  }
319
325
  }
320
326
 
327
+ /// Parse a `--max-memory` value via the `bytesize` crate: a byte count or a
328
+ /// size with a unit, decimal (`512MB` = 512·10⁶) or binary (`512MiB` = 512·2²⁰).
329
+ fn parse_max_memory(s: &str) -> Result<usize, String> {
330
+ let bytes = s
331
+ .trim()
332
+ .parse::<bytesize::ByteSize>()
333
+ .map_err(|e| format!("invalid --max-memory value '{s}': {e}"))?
334
+ .as_u64();
335
+ let bytes =
336
+ usize::try_from(bytes).map_err(|_| format!("--max-memory value too large: '{s}'"))?;
337
+ if bytes == 0 {
338
+ return Err(format!("--max-memory must be greater than 0: '{s}'"));
339
+ }
340
+ Ok(bytes)
341
+ }
342
+
321
343
  struct ResolvedOpts {
322
344
  #[allow(dead_code)]
323
345
  config_file: config::ConfigFile,
@@ -325,6 +347,7 @@ struct ResolvedOpts {
325
347
  http: config::HttpConfig,
326
348
  sockets: config::SocketsConfig,
327
349
  metadata: Option<serde_json::Value>,
350
+ max_memory: Option<usize>,
328
351
  }
329
352
 
330
353
  fn resolve_opts(opts: &CommonOpts) -> Result<ResolvedOpts> {
@@ -360,6 +383,7 @@ fn resolve_opts(opts: &CommonOpts) -> Result<ResolvedOpts> {
360
383
  http,
361
384
  sockets,
362
385
  metadata,
386
+ max_memory: opts.max_memory,
363
387
  })
364
388
  }
365
389
 
@@ -388,6 +412,7 @@ async fn prepare_component(
388
412
  let fs = resolved.fs;
389
413
  let http = resolved.http;
390
414
  let sockets = resolved.sockets;
415
+ let max_memory = resolved.max_memory;
391
416
 
392
417
  let mut preopens = runtime::fs_policy::derive_preopens(&fs);
393
418
  let mount_root = info.std.capabilities.fs_mount_root().unwrap_or("/");
@@ -410,7 +435,7 @@ async fn prepare_component(
410
435
  let wasm = runtime::load_component(&engine, &component_path)?;
411
436
  let linker = runtime::create_linker(&engine)?;
412
437
  let (instance, session_provider, store) = runtime::instantiate_component(
413
- &engine, &wasm, &linker, &preopens, &http, &fs, &sockets, &info,
438
+ &engine, &wasm, &linker, &preopens, &http, &fs, &sockets, &info, max_memory,
414
439
  )
415
440
  .await?;
416
441
  let has_sessions = session_provider.is_some();
@@ -969,6 +994,22 @@ mod tests {
969
994
  assert_eq!(result, Some(serde_json::json!({"key": "value"})));
970
995
  }
971
996
 
997
+ #[test]
998
+ fn parse_max_memory_units_and_bytes() {
999
+ assert_eq!(parse_max_memory("268435456").unwrap(), 256 << 20); // bare = bytes
1000
+ assert_eq!(parse_max_memory("256MiB").unwrap(), 256 << 20); // binary
1001
+ assert_eq!(parse_max_memory("1GiB").unwrap(), 1 << 30);
1002
+ assert_eq!(parse_max_memory("512KiB").unwrap(), 512 << 10);
1003
+ assert_eq!(parse_max_memory("256MB").unwrap(), 256_000_000); // decimal
1004
+ }
1005
+
1006
+ #[test]
1007
+ fn parse_max_memory_rejects_garbage_and_zero() {
1008
+ assert!(parse_max_memory("12xyz").is_err());
1009
+ assert!(parse_max_memory("").is_err());
1010
+ assert!(parse_max_memory("0").is_err());
1011
+ }
1012
+
972
1013
  #[test]
973
1014
  fn parse_cli_metadata_from_file() {
974
1015
  let mut file = NamedTempFile::new().unwrap();
@@ -5,7 +5,9 @@ use std::pin::Pin;
5
5
  use std::task::{Context, Poll};
6
6
  use tokio::sync::{mpsc, oneshot};
7
7
  use wasmtime::component::{Component, Linker, ResourceTable, Source, StreamConsumer, StreamResult};
8
- use wasmtime::{AsContextMut, Config, Engine, Store, StoreContextMut};
8
+ use wasmtime::{
9
+ AsContextMut, Config, Engine, Store, StoreContextMut, StoreLimits, StoreLimitsBuilder,
10
+ };
9
11
  use wasmtime_wasi::{WasiCtx, WasiCtxBuilder, WasiCtxView, WasiView};
10
12
  use wasmtime_wasi_http::WasiHttpCtx;
11
13
  use wasmtime_wasi_http::p3::WasiHttpCtxView;
@@ -36,6 +38,9 @@ pub struct HostState {
36
38
  fs_matcher: crate::runtime::fs_matcher::FsMatcher,
37
39
  fs_mode: crate::config::PolicyMode,
38
40
  fd_paths: crate::runtime::fs_policy::FdPathMap,
41
+ /// Caps the component's wasm linear memory growth (via `store.limiter`).
42
+ /// Default `StoreLimits` is unlimited.
43
+ limits: StoreLimits,
39
44
  }
40
45
 
41
46
  impl HostState {
@@ -153,6 +158,7 @@ pub async fn create_store(
153
158
  fs: &crate::config::FsConfig,
154
159
  sockets: &crate::config::SocketsConfig,
155
160
  info: &ComponentInfo,
161
+ max_memory: Option<usize>,
156
162
  ) -> Result<Store<HostState>> {
157
163
  // Intersect user policy with the component's declared capabilities.
158
164
  let effective_fs = crate::runtime::effective::effective_fs(fs, &info.std.capabilities).config;
@@ -208,8 +214,17 @@ pub async fn create_store(
208
214
  preopens: preopen_pairs,
209
215
  by_rep: Default::default(),
210
216
  },
217
+ limits: match max_memory {
218
+ Some(bytes) => StoreLimitsBuilder::new().memory_size(bytes).build(),
219
+ None => StoreLimits::default(),
220
+ },
211
221
  };
212
- Ok(Store::new(engine, state))
222
+ let mut store = Store::new(engine, state);
223
+ // Enforce the linear-memory cap: when the guest grows memory past the limit,
224
+ // `memory.grow` fails (the guest typically traps OOM) instead of letting the
225
+ // host process balloon. No-op when `max_memory` is None (default limits).
226
+ store.limiter(|state| &mut state.limits);
227
+ Ok(store)
213
228
  }
214
229
 
215
230
  // ── Component info from custom section ──
@@ -346,12 +361,13 @@ pub async fn instantiate_component(
346
361
  fs: &crate::config::FsConfig,
347
362
  sockets: &crate::config::SocketsConfig,
348
363
  info: &ComponentInfo,
364
+ max_memory: Option<usize>,
349
365
  ) -> Result<(
350
366
  ActWorld,
351
367
  Option<sessions::SessionProvider>,
352
368
  Store<HostState>,
353
369
  )> {
354
- let mut store = create_store(engine, preopens, http, fs, sockets, info).await?;
370
+ let mut store = create_store(engine, preopens, http, fs, sockets, info, max_memory).await?;
355
371
 
356
372
  // Manual instantiation flow (replicates ActWorld::instantiate_async)
357
373
  // so we keep access to the raw `Instance` for session-provider lookup.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes