Bascule shell runtime workspace — governed shell access layer for Substrate/Guildhouse FFC deployments. Crates: - bascule-agent: node agent with SSH server + command filtering - bascule-core: audit, grant engine, ceremony types, session - bascule-filter-core: log line filtering (stdio protocol) - bascule-gateway: OIDC auth, session management, SAT validation - bascule-node-agent: k8s DaemonSet agent (pod watcher, BPF manager) - bascule-proto: protobuf definitions - bascule-shell: governed SSH shell (commands, elevation, REPL) - bascule-tail: chronicle log tail + fanout - ceremony-engine: ceremony lifecycle (6 types + request/resolution) 172 tests passing. Implements SBS-SPEC-0001 shell model. Reference impl for SPEC-SHELLOPS-0001 Layer 1 (root shell).
69 lines
2.5 KiB
Rust
69 lines
2.5 KiB
Rust
use async_trait::async_trait;
|
|
|
|
use crate::governed::command_trait::*;
|
|
use crate::governed::session::{GovernedSession, Governed};
|
|
|
|
pub struct PipelineCommand;
|
|
|
|
#[async_trait]
|
|
impl ShellCommand for PipelineCommand {
|
|
fn name(&self) -> &str { "pipeline" }
|
|
fn aliases(&self) -> Vec<&str> { vec!["pl"] }
|
|
fn tier(&self) -> CommandTier { CommandTier::Engineer }
|
|
fn required_scope(&self) -> RequiredScope {
|
|
// Base command is read-only; subcommands like "trigger" require elevation.
|
|
RequiredScope::ReadOnly
|
|
}
|
|
fn description(&self) -> &str { "Pipeline management (list, show, trigger)" }
|
|
fn usage(&self) -> &str { "pipeline <list|show|trigger> [args...]" }
|
|
|
|
async fn execute(
|
|
&self,
|
|
args: &[String],
|
|
session: &mut GovernedSession,
|
|
) -> Result<CommandOutput, CommandError> {
|
|
let subcommand = args.first().map(|s| s.as_str()).unwrap_or("list");
|
|
|
|
match subcommand {
|
|
"list" | "ls" => {
|
|
let lines = vec![
|
|
OutputLine::Table {
|
|
headers: vec![
|
|
"RUN ID".to_string(),
|
|
"PIPELINE".to_string(),
|
|
"STATUS".to_string(),
|
|
"BRANCH".to_string(),
|
|
],
|
|
rows: vec![
|
|
vec!["(no runs loaded)".to_string(), "-".to_string(), "-".to_string(), "-".to_string()],
|
|
],
|
|
},
|
|
];
|
|
Ok(CommandOutput { lines })
|
|
}
|
|
"show" => {
|
|
let run_id = args.get(1).ok_or_else(|| {
|
|
CommandError::InvalidArgs("Usage: pipeline show <run_id>".to_string())
|
|
})?;
|
|
Ok(CommandOutput::text(format!(
|
|
"Pipeline run: {} (stub: not connected to runner-controller)",
|
|
run_id
|
|
)))
|
|
}
|
|
"trigger" => {
|
|
if session.tenant_context().is_none() {
|
|
return Err(CommandError::NoTenantContext);
|
|
}
|
|
// Trigger requires elevation.
|
|
Err(CommandError::ElevationRequired {
|
|
registry: "pipeline".to_string(),
|
|
verb: "trigger".to_string(),
|
|
})
|
|
}
|
|
_ => Err(CommandError::InvalidArgs(format!(
|
|
"Unknown subcommand: {}. Use: list, show, trigger",
|
|
subcommand
|
|
))),
|
|
}
|
|
}
|
|
}
|