cli: spawn pods through insomnia pod
This commit is contained in:
parent
de3f64f41d
commit
4f622b8e32
6
Cargo.lock
generated
6
Cargo.lock
generated
|
|
@ -333,6 +333,7 @@ name = "client"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"manifest",
|
"manifest",
|
||||||
|
"pod-command",
|
||||||
"protocol",
|
"protocol",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|
@ -2338,6 +2339,7 @@ dependencies = [
|
||||||
"manifest",
|
"manifest",
|
||||||
"memory",
|
"memory",
|
||||||
"minijinja",
|
"minijinja",
|
||||||
|
"pod-command",
|
||||||
"pod-registry",
|
"pod-registry",
|
||||||
"pod-store",
|
"pod-store",
|
||||||
"protocol",
|
"protocol",
|
||||||
|
|
@ -2357,6 +2359,10 @@ dependencies = [
|
||||||
"workflow",
|
"workflow",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pod-command"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pod-registry"
|
name = "pod-registry"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ members = [
|
||||||
"crates/session-store",
|
"crates/session-store",
|
||||||
"crates/manifest",
|
"crates/manifest",
|
||||||
"crates/pod",
|
"crates/pod",
|
||||||
|
"crates/pod-command",
|
||||||
"crates/pod-store",
|
"crates/pod-store",
|
||||||
"crates/protocol",
|
"crates/protocol",
|
||||||
"crates/provider",
|
"crates/provider",
|
||||||
|
|
@ -33,6 +34,7 @@ manifest = { path = "crates/manifest" }
|
||||||
lint-common = { path = "crates/lint-common" }
|
lint-common = { path = "crates/lint-common" }
|
||||||
memory = { path = "crates/memory" }
|
memory = { path = "crates/memory" }
|
||||||
pod = { path = "crates/pod" }
|
pod = { path = "crates/pod" }
|
||||||
|
pod-command = { path = "crates/pod-command" }
|
||||||
pod-registry = { path = "crates/pod-registry" }
|
pod-registry = { path = "crates/pod-registry" }
|
||||||
pod-store = { path = "crates/pod-store" }
|
pod-store = { path = "crates/pod-store" }
|
||||||
protocol = { path = "crates/protocol" }
|
protocol = { path = "crates/protocol" }
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ license.workspace = true
|
||||||
[dependencies]
|
[dependencies]
|
||||||
protocol = { workspace = true }
|
protocol = { workspace = true }
|
||||||
manifest = { workspace = true }
|
manifest = { workspace = true }
|
||||||
|
pod-command = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
tokio = { workspace = true, features = ["rt", "macros", "net", "io-util", "sync", "time", "process", "fs"] }
|
tokio = { workspace = true, features = ["rt", "macros", "net", "io-util", "sync", "time", "process", "fs"] }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
//! `insomnia-pod` バイナリをサブプロセスとして立ち上げ、`INSOMNIA-READY` を待つ
|
//! Pod runtime command をサブプロセスとして立ち上げ、`INSOMNIA-READY` を待つ
|
||||||
//! ハンドシェイク。
|
//! ハンドシェイク。
|
||||||
//!
|
//!
|
||||||
//! - 親プロセス (TUI / GUI / E2E) は profile/default/typed restore flags を
|
//! - 親プロセス (TUI / GUI / E2E) は profile/default/typed restore flags を
|
||||||
|
|
@ -15,6 +15,7 @@ use std::path::{Path, PathBuf};
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use pod_command::PodRuntimeCommand;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
|
@ -99,7 +100,7 @@ pub async fn spawn_pod<F>(config: SpawnConfig, mut progress: F) -> Result<SpawnR
|
||||||
where
|
where
|
||||||
F: FnMut(&str),
|
F: FnMut(&str),
|
||||||
{
|
{
|
||||||
let pod_bin = resolve_pod_command();
|
let pod_command = PodRuntimeCommand::resolve().map_err(SpawnError::Io)?;
|
||||||
|
|
||||||
let pod_runtime_dir = manifest::paths::pod_runtime_dir(&config.pod_name)
|
let pod_runtime_dir = manifest::paths::pod_runtime_dir(&config.pod_name)
|
||||||
.ok_or(SpawnError::RuntimeDirUnavailable)?;
|
.ok_or(SpawnError::RuntimeDirUnavailable)?;
|
||||||
|
|
@ -107,8 +108,9 @@ where
|
||||||
let stderr_path = pod_runtime_dir.join("stderr.log");
|
let stderr_path = pod_runtime_dir.join("stderr.log");
|
||||||
let stderr_file = std::fs::File::create(&stderr_path).map_err(SpawnError::Io)?;
|
let stderr_file = std::fs::File::create(&stderr_path).map_err(SpawnError::Io)?;
|
||||||
|
|
||||||
let mut command = Command::new(&pod_bin);
|
let mut command = Command::new(pod_command.program());
|
||||||
command
|
command
|
||||||
|
.args(pod_command.prefix_args())
|
||||||
.current_dir(&config.cwd)
|
.current_dir(&config.cwd)
|
||||||
.stdin(Stdio::null())
|
.stdin(Stdio::null())
|
||||||
.stdout(Stdio::null())
|
.stdout(Stdio::null())
|
||||||
|
|
@ -268,23 +270,6 @@ async fn drain_stderr_into_tail(stderr_path: &Path, tail: &mut StderrTail, offse
|
||||||
*offset = content.len();
|
*offset = content.len();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves the binary used to launch a child Pod. Must point at a
|
|
||||||
/// `insomnia-pod`-compatible executable — the parent reads the child's stderr
|
|
||||||
/// directly looking for `INSOMNIA-READY`, so any wrapper that emits
|
|
||||||
/// extra lines on stderr will pollute that handshake.
|
|
||||||
///
|
|
||||||
/// `INSOMNIA_POD_COMMAND` overrides the lookup (used by tests to inject
|
|
||||||
/// a mock binary). Otherwise we defer to `PATH` — missing binary
|
|
||||||
/// surfaces as the spawn `io::Error`.
|
|
||||||
fn resolve_pod_command() -> PathBuf {
|
|
||||||
if let Ok(cmd) = std::env::var("INSOMNIA_POD_COMMAND")
|
|
||||||
&& !cmd.is_empty()
|
|
||||||
{
|
|
||||||
return PathBuf::from(cmd);
|
|
||||||
}
|
|
||||||
PathBuf::from("insomnia-pod")
|
|
||||||
}
|
|
||||||
|
|
||||||
struct StderrTail {
|
struct StderrTail {
|
||||||
lines: std::collections::VecDeque<String>,
|
lines: std::collections::VecDeque<String>,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
7
crates/pod-command/Cargo.toml
Normal file
7
crates/pod-command/Cargo.toml
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
[package]
|
||||||
|
name = "pod-command"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
170
crates/pod-command/src/lib.rs
Normal file
170
crates/pod-command/src/lib.rs
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
use std::ffi::{OsStr, OsString};
|
||||||
|
use std::fmt;
|
||||||
|
use std::io;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
pub const POD_COMMAND_OVERRIDE_ENV: &str = "INSOMNIA_POD_COMMAND";
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct PodRuntimeCommand {
|
||||||
|
pub program: PathBuf,
|
||||||
|
pub prefix_args: Vec<OsString>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PodRuntimeCommand {
|
||||||
|
pub fn new(program: impl Into<PathBuf>, prefix_args: Vec<OsString>) -> Self {
|
||||||
|
Self {
|
||||||
|
program: program.into(),
|
||||||
|
prefix_args,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn executable_only(program: impl Into<PathBuf>) -> Self {
|
||||||
|
Self::new(program, Vec::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn for_current_exe() -> io::Result<Self> {
|
||||||
|
Ok(Self::for_executable(std::env::current_exe()?))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn for_executable(program: impl Into<PathBuf>) -> Self {
|
||||||
|
let program = program.into();
|
||||||
|
let prefix_args = if is_legacy_pod_binary(&program) {
|
||||||
|
Vec::new()
|
||||||
|
} else {
|
||||||
|
vec![OsString::from("pod")]
|
||||||
|
};
|
||||||
|
Self::new(program, prefix_args)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve the Pod runtime command used for subprocess launches.
|
||||||
|
///
|
||||||
|
/// `INSOMNIA_POD_COMMAND` is intentionally executable-only: its value is
|
||||||
|
/// used as the program path without shell parsing and without the unified
|
||||||
|
/// `pod` prefix arg. That keeps existing development/test overrides safe
|
||||||
|
/// while the default path moves to `current_exe() + ["pod"]`.
|
||||||
|
pub fn resolve() -> io::Result<Self> {
|
||||||
|
if let Some(command) = Self::from_override_env() {
|
||||||
|
return Ok(command);
|
||||||
|
}
|
||||||
|
Self::for_current_exe()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_override_env() -> Option<Self> {
|
||||||
|
let raw = std::env::var_os(POD_COMMAND_OVERRIDE_ENV)?;
|
||||||
|
if raw.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(Self::executable_only(raw))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn program(&self) -> &Path {
|
||||||
|
&self.program
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prefix_args(&self) -> &[OsString] {
|
||||||
|
&self.prefix_args
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn argv_with<I, S>(&self, args: I) -> Vec<OsString>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = S>,
|
||||||
|
S: Into<OsString>,
|
||||||
|
{
|
||||||
|
let mut argv = self.prefix_args.clone();
|
||||||
|
argv.extend(args.into_iter().map(Into::into));
|
||||||
|
argv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for PodRuntimeCommand {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.program.display())?;
|
||||||
|
for arg in &self.prefix_args {
|
||||||
|
write!(f, " {}", arg.to_string_lossy())?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_legacy_pod_binary(program: &Path) -> bool {
|
||||||
|
let Some(file_name) = program.file_name().and_then(OsStr::to_str) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let stem = file_name.strip_suffix(".exe").unwrap_or(file_name);
|
||||||
|
stem == "insomnia-pod"
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
static ENV_LOCK: Mutex<()> = Mutex::new(());
|
||||||
|
|
||||||
|
struct EnvRestore(Option<OsString>);
|
||||||
|
|
||||||
|
impl EnvRestore {
|
||||||
|
fn capture() -> Self {
|
||||||
|
Self(std::env::var_os(POD_COMMAND_OVERRIDE_ENV))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for EnvRestore {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
match &self.0 {
|
||||||
|
Some(value) => std::env::set_var(POD_COMMAND_OVERRIDE_ENV, value),
|
||||||
|
None => std::env::remove_var(POD_COMMAND_OVERRIDE_ENV),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn insomnia_binary_defaults_to_pod_prefix() {
|
||||||
|
let command = PodRuntimeCommand::for_executable("/opt/insomnia/bin/insomnia");
|
||||||
|
|
||||||
|
assert_eq!(command.program(), Path::new("/opt/insomnia/bin/insomnia"));
|
||||||
|
assert_eq!(command.prefix_args(), [OsString::from("pod")]);
|
||||||
|
assert_eq!(
|
||||||
|
command.argv_with(["--pod", "agent"]),
|
||||||
|
vec!["pod", "--pod", "agent"]
|
||||||
|
.into_iter()
|
||||||
|
.map(OsString::from)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn legacy_wrapper_keeps_executable_only_command() {
|
||||||
|
let command = PodRuntimeCommand::for_executable("/opt/insomnia/bin/insomnia-pod");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
command.program(),
|
||||||
|
Path::new("/opt/insomnia/bin/insomnia-pod")
|
||||||
|
);
|
||||||
|
assert!(command.prefix_args().is_empty());
|
||||||
|
assert_eq!(
|
||||||
|
command.argv_with(["--pod", "agent"]),
|
||||||
|
vec!["--pod", "agent"]
|
||||||
|
.into_iter()
|
||||||
|
.map(OsString::from)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn env_override_is_executable_only_and_not_shell_parsed() {
|
||||||
|
let _guard = ENV_LOCK.lock().unwrap();
|
||||||
|
let _restore = EnvRestore::capture();
|
||||||
|
unsafe {
|
||||||
|
std::env::set_var(POD_COMMAND_OVERRIDE_ENV, "/tmp/mock pod --flag");
|
||||||
|
}
|
||||||
|
|
||||||
|
let command = PodRuntimeCommand::resolve().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(command.program(), Path::new("/tmp/mock pod --flag"));
|
||||||
|
assert!(command.prefix_args().is_empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,7 @@ pod-store = { workspace = true }
|
||||||
manifest = { workspace = true }
|
manifest = { workspace = true }
|
||||||
protocol = { workspace = true }
|
protocol = { workspace = true }
|
||||||
provider = { workspace = true }
|
provider = { workspace = true }
|
||||||
|
pod-command = { workspace = true }
|
||||||
pod-registry = { workspace = true }
|
pod-registry = { workspace = true }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ use std::time::Duration;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use llm_worker::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput};
|
use llm_worker::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput};
|
||||||
use manifest::{Permission, ScopeRule};
|
use manifest::{Permission, ScopeRule};
|
||||||
|
use pod_command::PodRuntimeCommand;
|
||||||
use pod_store::{PodActiveSegmentRef, PodMetadata, PodMetadataStore};
|
use pod_store::{PodActiveSegmentRef, PodMetadata, PodMetadataStore};
|
||||||
use protocol::stream::JsonLineReader;
|
use protocol::stream::JsonLineReader;
|
||||||
use protocol::{Event, PodStatus};
|
use protocol::{Event, PodStatus};
|
||||||
|
|
@ -328,8 +329,10 @@ where
|
||||||
pod_name: &str,
|
pod_name: &str,
|
||||||
socket_path: &Path,
|
socket_path: &Path,
|
||||||
) -> Result<(), PodDiscoveryError> {
|
) -> Result<(), PodDiscoveryError> {
|
||||||
let mut command = Command::new(resolve_pod_command());
|
let pod_command = PodRuntimeCommand::resolve().map_err(PodDiscoveryError::RestoreSpawn)?;
|
||||||
|
let mut command = Command::new(pod_command.program());
|
||||||
command
|
command
|
||||||
|
.args(pod_command.prefix_args())
|
||||||
.arg("--pod")
|
.arg("--pod")
|
||||||
.arg(pod_name)
|
.arg(pod_name)
|
||||||
.arg("--require-pod-state")
|
.arg("--require-pod-state")
|
||||||
|
|
@ -661,15 +664,6 @@ fn lookup_segment_lock(
|
||||||
pod_registry::lookup_segment(segment_id)
|
pod_registry::lookup_segment(segment_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_pod_command() -> PathBuf {
|
|
||||||
if let Ok(cmd) = std::env::var("INSOMNIA_POD_COMMAND")
|
|
||||||
&& !cmd.is_empty()
|
|
||||||
{
|
|
||||||
return PathBuf::from(cmd);
|
|
||||||
}
|
|
||||||
PathBuf::from("insomnia-pod")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, JsonSchema)]
|
#[derive(Debug, Deserialize, JsonSchema)]
|
||||||
struct PodNameInput {
|
struct PodNameInput {
|
||||||
/// Pod name to restore.
|
/// Pod name to restore.
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
//!
|
//!
|
||||||
//! Wires pod-registry delegation, child manifest-config construction, subprocess
|
//! Wires pod-registry delegation, child manifest-config construction, subprocess
|
||||||
//! launch, and socket handoff into a single `Tool` implementation. When
|
//! launch, and socket handoff into a single `Tool` implementation. When
|
||||||
//! the LLM calls `SpawnPod`, a fresh `insomnia-pod` binary is exec'd in its own
|
//! the LLM calls `SpawnPod`, a fresh Pod runtime command is exec'd in its own
|
||||||
//! process group, the pod-registry is updated atomically, and the child's
|
//! process group, the pod-registry is updated atomically, and the child's
|
||||||
//! first turn is kicked off by handing its socket a `Method::Run`.
|
//! first turn is kicked off by handing its socket a `Method::Run`.
|
||||||
|
|
||||||
|
|
@ -19,6 +19,7 @@ use manifest::{
|
||||||
ProfileRegistrySource, ProfileResolveOptions, ProfileResolver, ProfileSelector, ScopeConfig,
|
ProfileRegistrySource, ProfileResolveOptions, ProfileResolver, ProfileSelector, ScopeConfig,
|
||||||
ScopeRule, SessionConfigPartial, SharedScope, ToolOutputLimitsPartial, WorkerManifestConfig,
|
ScopeRule, SessionConfigPartial, SharedScope, ToolOutputLimitsPartial, WorkerManifestConfig,
|
||||||
};
|
};
|
||||||
|
use pod_command::PodRuntimeCommand;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tokio::net::UnixStream;
|
use tokio::net::UnixStream;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
@ -408,8 +409,9 @@ impl SpawnPodTool {
|
||||||
spawn_config_json: &str,
|
spawn_config_json: &str,
|
||||||
predicted_socket: &Path,
|
predicted_socket: &Path,
|
||||||
) -> Result<(), ToolError> {
|
) -> Result<(), ToolError> {
|
||||||
let pod_command =
|
let pod_command = PodRuntimeCommand::resolve().map_err(|error| {
|
||||||
std::env::var("INSOMNIA_POD_COMMAND").unwrap_or_else(|_| "insomnia-pod".into());
|
ToolError::ExecutionFailed(format!("failed to resolve Pod runtime command: {error}"))
|
||||||
|
})?;
|
||||||
|
|
||||||
// Pre-create the child's runtime dir so we have a stable place to
|
// Pre-create the child's runtime dir so we have a stable place to
|
||||||
// capture its stderr before it has had a chance to bind anything.
|
// capture its stderr before it has had a chance to bind anything.
|
||||||
|
|
@ -430,8 +432,9 @@ impl SpawnPodTool {
|
||||||
ToolError::ExecutionFailed(format!("open {}: {e}", stderr_path.display()))
|
ToolError::ExecutionFailed(format!("open {}: {e}", stderr_path.display()))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let mut cmd = Command::new(&pod_command);
|
let mut cmd = Command::new(pod_command.program());
|
||||||
cmd.arg("--adopt")
|
cmd.args(pod_command.prefix_args())
|
||||||
|
.arg("--adopt")
|
||||||
.arg("--callback")
|
.arg("--callback")
|
||||||
.arg(&self.callback_socket)
|
.arg(&self.callback_socket)
|
||||||
.arg("--spawn-config-json")
|
.arg("--spawn-config-json")
|
||||||
|
|
@ -488,7 +491,7 @@ fn parse_scope(rules: &[ScopeRuleInput]) -> Result<Vec<ScopeRule>, ToolError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serialise the internal manifest config that gets handed to the child
|
/// Serialise the internal manifest config that gets handed to the child
|
||||||
/// `insomnia-pod` binary via the hidden `--spawn-config-json` flag.
|
/// Pod runtime process via the hidden `--spawn-config-json` flag.
|
||||||
/// `PodManifestConfig`'s `Serialize` impl is the single source of truth for the
|
/// `PodManifestConfig`'s `Serialize` impl is the single source of truth for the
|
||||||
/// internal handoff shape.
|
/// internal handoff shape.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ enum Mode {
|
||||||
profile: Option<String>,
|
profile: Option<String>,
|
||||||
},
|
},
|
||||||
/// `insomnia <name>` / `insomnia --pod <name>`: attach to a live Pod by name if
|
/// `insomnia <name>` / `insomnia --pod <name>`: attach to a live Pod by name if
|
||||||
/// possible; otherwise launch `insomnia-pod --pod <name>` so the pod process
|
/// possible; otherwise launch the Pod runtime command with `--pod <name>` so it
|
||||||
/// resumes from name-keyed state or creates a fresh same-name Pod.
|
/// resumes from name-keyed state or creates a fresh same-name Pod.
|
||||||
PodName {
|
PodName {
|
||||||
pod_name: String,
|
pod_name: String,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
//!
|
//!
|
||||||
//! Reads live Pod allocations from the runtime registry and stopped Pod state
|
//! Reads live Pod allocations from the runtime registry and stopped Pod state
|
||||||
//! from the pod-store name-keyed metadata. Picking a live row attaches to
|
//! from the pod-store name-keyed metadata. Picking a live row attaches to
|
||||||
//! its socket; picking a stopped row restores via `insomnia-pod --pod <name>`.
|
//! its socket; picking a stopped row restores via the Pod runtime command.
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
@ -65,7 +65,7 @@ impl From<session_store::StoreError> for PickerError {
|
||||||
pub enum PickerOutcome {
|
pub enum PickerOutcome {
|
||||||
/// User picked a Pod. `socket_override` is set for live rows when the
|
/// User picked a Pod. `socket_override` is set for live rows when the
|
||||||
/// runtime registry knows the exact socket path; stopped rows leave it
|
/// runtime registry knows the exact socket path; stopped rows leave it
|
||||||
/// empty so the caller restores with `insomnia-pod --pod <name>`.
|
/// empty so the caller restores by spawning the Pod runtime command.
|
||||||
Picked {
|
Picked {
|
||||||
pod_name: String,
|
pod_name: String,
|
||||||
socket_override: Option<PathBuf>,
|
socket_override: Option<PathBuf>,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
//! Rendered at the user's current cursor position when `insomnia` is invoked
|
//! Rendered at the user's current cursor position when `insomnia` is invoked
|
||||||
//! with no positional argument. Discovers `.insomnia/profiles.toml` profile
|
//! with no positional argument. Discovers `.insomnia/profiles.toml` profile
|
||||||
//! choices plus bundled profiles, defaults to the builtin profile, prompts for
|
//! choices plus bundled profiles, defaults to the builtin profile, prompts for
|
||||||
//! the Pod's name, and on confirmation launches the `insomnia-pod` binary as an
|
//! the Pod's name, and on confirmation launches the Pod runtime command as an
|
||||||
//! independent process. Once the process reports its socket via the
|
//! independent process. Once the process reports its socket via the
|
||||||
//! `INSOMNIA-READY` stderr line, the dialog hands control back so main can
|
//! `INSOMNIA-READY` stderr line, the dialog hands control back so main can
|
||||||
//! switch the terminal to alternate-screen mode.
|
//! switch the terminal to alternate-screen mode.
|
||||||
|
|
@ -72,7 +72,7 @@ type InlineTerminal = Terminal<CrosstermBackend<io::Stdout>>;
|
||||||
|
|
||||||
/// Source session for a resume run. `None` = fresh spawn (current
|
/// Source session for a resume run. `None` = fresh spawn (current
|
||||||
/// behaviour); `Some(id)` swaps the dialog into "Resume Pod" mode and
|
/// behaviour); `Some(id)` swaps the dialog into "Resume Pod" mode and
|
||||||
/// passes `--session <id>` to the spawned `insomnia-pod` child.
|
/// passes `--session <id>` to the spawned Pod runtime child.
|
||||||
pub async fn run(
|
pub async fn run(
|
||||||
resume_from: Option<SegmentId>,
|
resume_from: Option<SegmentId>,
|
||||||
profile: Option<String>,
|
profile: Option<String>,
|
||||||
|
|
@ -162,7 +162,7 @@ pub async fn run(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Launch `insomnia-pod --pod <name>` without opening the name dialog. The child Pod
|
/// Launch a Pod runtime command with `--pod <name>` without opening the name dialog. The child Pod
|
||||||
/// resolves persisted Pod metadata if present, or creates a fresh same-name Pod
|
/// resolves persisted Pod metadata if present, or creates a fresh same-name Pod
|
||||||
/// from the default profile.
|
/// from the default profile.
|
||||||
pub async fn run_pod_name(pod_name: String) -> Result<SpawnOutcome, SpawnError> {
|
pub async fn run_pod_name(pod_name: String) -> Result<SpawnOutcome, SpawnError> {
|
||||||
|
|
@ -415,7 +415,7 @@ struct Form {
|
||||||
/// When true, launch the child with `--pod <name>` so the pod process
|
/// When true, launch the child with `--pod <name>` so the pod process
|
||||||
/// resolves name-keyed state before falling back to fresh creation.
|
/// resolves name-keyed state before falling back to fresh creation.
|
||||||
resume_by_pod_name: bool,
|
resume_by_pod_name: bool,
|
||||||
/// Optional profile choices passed to `insomnia-pod --profile` for
|
/// Optional profile choices passed with `--profile` for
|
||||||
/// fresh spawns. This is not used for resume/attach flows because those must
|
/// fresh spawns. This is not used for resume/attach flows because those must
|
||||||
/// restore Pod state rather than re-evaluate a profile source.
|
/// restore Pod state rather than re-evaluate a profile source.
|
||||||
profile_choices: Vec<ProfileChoice>,
|
profile_choices: Vec<ProfileChoice>,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user