From 2ae52c97ad48195f3f9f568378823a514d002417 Mon Sep 17 00:00:00 2001 From: Andy Gayton Date: Wed, 21 Oct 2026 13:21:59 +0000 Subject: [PATCH 1/2] feat: add nushell's plugin management commands (plugin list, stop, use, ...) --- Cargo.lock | 14 ++++++++++++++ Cargo.toml | 1 + README.md | 8 ++++++++ src/engine.rs | 1 + tests/eval_test.rs | 16 ++++++++++++++++ tests/server_test.rs | 26 ++++++++++++++++++++++++++ 6 files changed, 66 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 13e156f..c4f5b66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2784,6 +2784,7 @@ dependencies = [ "nu-cli", "nu-cmd-extra", "nu-cmd-lang", + "nu-cmd-plugin", "nu-command", "nu-engine", "nu-parser", @@ -4719,6 +4720,19 @@ dependencies = [ "shadow-rs", ] +[[package]] +name = "nu-cmd-plugin" +version = "0.116.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a3455969656165b9b0dd2ad4c668d63c11b8498d6fbc594b99f5bb1a59878c7" +dependencies = [ + "itertools 0.15.0", + "nu-engine", + "nu-path", + "nu-plugin-engine", + "nu-protocol", +] + [[package]] name = "nu-color-config" version = "0.116.0" diff --git a/Cargo.toml b/Cargo.toml index 239aeef..b692e72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ url = "2.5.0" nu-cli = "0.116.0" nu-cmd-lang = "0.116.0" +nu-cmd-plugin = "0.116.0" nu-cmd-extra = "0.116.0" nu-command = { version = "0.116.0", features = ["sqlite"] } nu-engine = "0.116.0" diff --git a/README.md b/README.md index 07f5d0e..ec34f72 100644 --- a/README.md +++ b/README.md @@ -921,6 +921,14 @@ $ http-nu --plugin ~/.cargo/bin/nu_plugin_inc eval -c '1 | inc' 2 ``` +Nushell's plugin management commands (`plugin list`, `plugin stop`, `plugin use`, +`plugin add`, `plugin rm`) are available. `plugin list --engine` shows the +plugins loaded with `--plugin`, including the process id when running: + +```bash +$ http-nu --plugin ~/.cargo/bin/nu_plugin_inc eval -c 'plugin list --engine | select name status pid' +``` + ### Module Paths Make module paths available with `-I` / `--include-path`: diff --git a/src/engine.rs b/src/engine.rs index e08b0e0..e295a8c 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -59,6 +59,7 @@ impl Engine { engine_state = add_shell_command_context(engine_state); engine_state = add_cli_context(engine_state); engine_state = nu_cmd_extra::extra::add_extra_command_context(engine_state); + engine_state = nu_cmd_plugin::add_plugin_command_context(engine_state); load_http_nu_stdlib(&mut engine_state)?; nu_std::load_standard_library(&mut engine_state)?; diff --git a/tests/eval_test.rs b/tests/eval_test.rs index 33a65ff..4055ae0 100644 --- a/tests/eval_test.rs +++ b/tests/eval_test.rs @@ -143,6 +143,22 @@ fn test_eval_with_plugin() { .stdout("PLUGIN_WORKS\n"); } +#[test] +fn test_eval_plugin_stop_respawns() { + let plugin_path = workspace_bin("nu_plugin_test"); + Command::new(assert_cmd::cargo::cargo_bin!("http-nu")) + .args([ + "eval", + "--plugin", + plugin_path.to_str().unwrap(), + "-c", + "let a = test-plugin-pid; plugin stop test; let b = test-plugin-pid; [($a != $b) ($b == (plugin list --engine | get pid.0))] | to nuon", + ]) + .assert() + .success() + .stdout("[true, true]\n"); +} + #[test] fn test_eval_include_path() { let dir = TempDir::new().unwrap(); diff --git a/tests/server_test.rs b/tests/server_test.rs index 1eb58fc..ba47aba 100644 --- a/tests/server_test.rs +++ b/tests/server_test.rs @@ -1367,6 +1367,32 @@ async fn test_plugin_process_shared_across_requests() { ); } +/// Tests that `plugin list --engine` is available to served scripts and reports +/// the running plugin's pid, matching the pid the plugin reports for itself. +#[tokio::test] +async fn test_plugin_list_engine() { + let plugin_path = workspace_bin("nu_plugin_test"); + let server = TestServer::new_with_plugins( + "127.0.0.1:0", + r#"{|req| + let own = test-plugin-pid + let listed = plugin list --engine | where name == test | get pid.0 + {own: $own, listed: $listed, status: (plugin list --engine | get status.0)} | to json -r + }"#, + false, + &[plugin_path], + ) + .await; + + let output = server.curl("/").await; + assert!(output.status.success()); + let stdout = String::from_utf8_lossy(&output.stdout); + let v: serde_json::Value = serde_json::from_str(stdout.trim()).expect("json response"); + assert_eq!(v["status"], "running"); + assert!(v["own"].as_i64().is_some(), "missing plugin pid: {v}"); + assert_eq!(v["own"], v["listed"]); +} + // ============================================================================ // Watch mode tests (-w/--watch) // ============================================================================ From 8b2833820cb2f9615f860cd45c822022fc8c7454 Mon Sep 17 00:00:00 2001 From: Andy Gayton Date: Wed, 21 Oct 2026 13:32:11 +0000 Subject: [PATCH 2/2] style: deno fmt README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ec34f72..855bc0b 100644 --- a/README.md +++ b/README.md @@ -921,9 +921,9 @@ $ http-nu --plugin ~/.cargo/bin/nu_plugin_inc eval -c '1 | inc' 2 ``` -Nushell's plugin management commands (`plugin list`, `plugin stop`, `plugin use`, -`plugin add`, `plugin rm`) are available. `plugin list --engine` shows the -plugins loaded with `--plugin`, including the process id when running: +Nushell's plugin management commands (`plugin list`, `plugin stop`, +`plugin use`, `plugin add`, `plugin rm`) are available. `plugin list --engine` +shows the plugins loaded with `--plugin`, including the process id when running: ```bash $ http-nu --plugin ~/.cargo/bin/nu_plugin_inc eval -c 'plugin list --engine | select name status pid'