7 Commits

Author SHA1 Message Date
683cec9307 release: v1.1.2
- fix(opensearch): make ML model registration idempotent
2026-03-25 18:09:25 +00:00
30dc4f9c5e fix(opensearch): make ML model registration idempotent
Reuse any existing model version (including DEPLOY_FAILED) instead of
registering a new copy. Prevents accumulation of stale model chunks
in .plugins-ml-model when OpenSearch restarts between applies.
2026-03-25 18:04:28 +00:00
3d2d16d53e feat(secrets): add xchacha20-poly1305 cipher key seeding for Kratos
Add rand_alphanum() using OsRng for generating fixed-length
alphanumeric secrets. Seed secrets-cipher (32 chars) into the
kratos KV path for at-rest encryption of OIDC tokens.
2026-03-24 20:51:13 +00:00
80ab6d6113 feat: enable Meet external API, fix SDK path
- Meet config: EXTERNAL_API_ENABLED=True
- Meet backend: added lasuite-resource-server configmap + RS creds
- Pingora: added /external-api/ route for Meet
- SDK: fixed Meet URL to use /external-api/ (hyphenated)

NOTE: Meet RS requires ES256 tokens + lasuite_meet scope — CLI
tokens use RS256 + generic scopes. Needs RS config adjustment.
2026-03-24 17:03:55 +00:00
b08a80d177 refactor: nest infra commands under sunbeam platform
Moves up, status, apply, seed, verify, logs, get, restart, build,
check, mirror, bootstrap, k8s under `sunbeam platform <command>`.
Top-level now has 19 commands instead of 32.
2026-03-24 15:52:44 +00:00
530b2a22b8 chore: remove solution branding from CLI help text 2026-03-24 15:44:39 +00:00
6a2b62dc42 refactor: remove bao, docs, and people subcommands
- bao: replaced by `sunbeam vault` with proper JWT auth
- docs: La Suite Docs not ready for production
- people: La Suite People not ready for production
2026-03-24 15:40:58 +00:00
8 changed files with 253 additions and 283 deletions

View File

@@ -1,5 +1,12 @@
# Changelog
## v1.1.2
- 30dc4f9 fix(opensearch): make ML model registration idempotent
- 3d2d16d feat(secrets): add xchacha20-poly1305 cipher key seeding for Kratos
- 80ab6d6 feat: enable Meet external API, fix SDK path
- b08a80d refactor: nest infra commands under `sunbeam platform`
## v1.1.1
- cd80a57 fix: DynamicBearer auth, retry on 500/429, upload resilience

View File

@@ -1,6 +1,6 @@
[package]
name = "sunbeam-sdk"
version = "1.1.1"
version = "1.1.2"
edition = "2024"
description = "Sunbeam Studios SDK, CLI, and ecosystem integrations"
repository = "https://src.sunbeam.pt/studio/cli"

View File

@@ -456,7 +456,7 @@ impl SunbeamClient {
pub async fn meet(&self) -> Result<&crate::lasuite::MeetClient> {
self.sso_token().await?;
self.meet.get_or_try_init(|| async {
let url = format!("https://meet.{}/external_api/v1.0", self.domain);
let url = format!("https://meet.{}/external-api/v1.0", self.domain);
Ok(crate::lasuite::MeetClient::from_parts(url, AuthMethod::DynamicBearer))
}).await
}

View File

@@ -617,10 +617,14 @@ async fn ensure_opensearch_ml() {
already_deployed = true;
break;
}
"REGISTERED" | "DEPLOYING" => {
// Any existing model (even DEPLOY_FAILED) — reuse it instead of
// registering a new version. This prevents accumulating stale
// copies in .plugins-ml-model when the pod restarts.
_ => {
if model_id.is_none() && !id.is_empty() {
model_id = Some(id.to_string());
}
_ => {}
}
}
}

View File

@@ -102,6 +102,15 @@ fn rand_token_n(n: usize) -> String {
base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(buf)
}
/// Generate an alphanumeric random string of exactly `n` characters.
/// Used for secrets that require a fixed character length (e.g. xchacha20-poly1305 cipher keys).
pub(crate) fn rand_alphanum(n: usize) -> String {
use rand::rngs::OsRng;
use rand::Rng;
const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
(0..n).map(|_| CHARSET[OsRng.gen_range(0..CHARSET.len())] as char).collect()
}
// ── Port-forward helper ─────────────────────────────────────────────────────
/// Port-forward guard — cancels the background forwarder on drop.

View File

@@ -11,8 +11,8 @@ use crate::openbao::BaoClient;
use crate::output::{ok, warn};
use super::{
gen_dkim_key_pair, gen_fernet_key, port_forward, rand_token, rand_token_n, scw_config,
wait_pod_running, delete_resource, GITEA_ADMIN_USER, SMTP_URI,
gen_dkim_key_pair, gen_fernet_key, port_forward, rand_alphanum, rand_token, rand_token_n,
scw_config, wait_pod_running, delete_resource, GITEA_ADMIN_USER, SMTP_URI,
};
/// Internal result from seed_openbao, used by cmd_seed.
@@ -238,12 +238,14 @@ pub async fn seed_openbao() -> Result<Option<SeedResult>> {
.await?;
let smtp_uri_fn = || SMTP_URI.to_string();
let cipher_fn = || rand_alphanum(32);
let kratos = get_or_create(
&bao,
"kratos",
&[
("secrets-default", &rand_token as &dyn Fn() -> String),
("secrets-cookie", &rand_token),
("secrets-cipher", &cipher_fn),
("smtp-connection-uri", &smtp_uri_fn),
],
&mut dirty_paths,

View File

@@ -1,6 +1,6 @@
[package]
name = "sunbeam"
version = "1.1.1"
version = "1.1.2"
edition = "2024"
description = "Sunbeam Studios SDK, CLI, and ecosystem integrations"

View File

@@ -5,7 +5,7 @@ use clap::{Parser, Subcommand};
/// Sunbeam local dev stack manager.
#[derive(Parser, Debug)]
#[command(name = "sunbeam", about = "Sunbeam local dev stack manager")]
#[command(name = "sunbeam", about = "Sunbeam Studios CLI")]
pub struct Cli {
/// Named context to use (overrides current-context from config).
#[arg(long)]
@@ -30,18 +30,121 @@ pub struct Cli {
#[derive(Subcommand, Debug)]
pub enum Verb {
// -- Infrastructure commands (preserved) ----------------------------------
/// Platform operations (cluster, builds, deploys).
Platform {
#[command(subcommand)]
action: PlatformAction,
},
/// Manage sunbeam configuration.
Config {
#[command(subcommand)]
action: Option<ConfigAction>,
},
/// Project management.
Pm {
#[command(subcommand)]
action: Option<PmAction>,
},
/// Self-update from latest mainline commit.
Update,
/// Print version info.
Version,
// -- Service commands -----------------------------------------------------
/// Authentication, identity & OAuth2 management.
Auth {
#[command(subcommand)]
action: sunbeam_sdk::identity::cli::AuthCommand,
},
/// Version control.
Vcs {
#[command(subcommand)]
action: sunbeam_sdk::gitea::cli::VcsCommand,
},
/// Chat and messaging.
Chat {
#[command(subcommand)]
action: sunbeam_sdk::matrix::cli::ChatCommand,
},
/// Search engine.
Search {
#[command(subcommand)]
action: sunbeam_sdk::search::cli::SearchCommand,
},
/// Object storage.
Storage {
#[command(subcommand)]
action: sunbeam_sdk::storage::cli::StorageCommand,
},
/// Media and video.
Media {
#[command(subcommand)]
action: sunbeam_sdk::media::cli::MediaCommand,
},
/// Monitoring.
Mon {
#[command(subcommand)]
action: sunbeam_sdk::monitoring::cli::MonCommand,
},
/// Secrets management.
Vault {
#[command(subcommand)]
action: sunbeam_sdk::openbao::cli::VaultCommand,
},
/// Video meetings.
Meet {
#[command(subcommand)]
action: sunbeam_sdk::lasuite::cli::MeetCommand,
},
/// File storage.
Drive {
#[command(subcommand)]
action: sunbeam_sdk::lasuite::cli::DriveCommand,
},
/// Email.
Mail {
#[command(subcommand)]
action: sunbeam_sdk::lasuite::cli::MailCommand,
},
/// Calendar.
Cal {
#[command(subcommand)]
action: sunbeam_sdk::lasuite::cli::CalCommand,
},
/// Search across services.
Find {
#[command(subcommand)]
action: sunbeam_sdk::lasuite::cli::FindCommand,
},
}
#[derive(Subcommand, Debug)]
pub enum PlatformAction {
/// Full cluster bring-up.
Up,
/// Pod health (optionally scoped).
Status {
/// namespace or namespace/name
target: Option<String>,
},
/// kustomize build + domain subst + kubectl apply.
/// Build and apply manifests.
Apply {
/// Limit apply to one namespace.
namespace: Option<String>,
@@ -55,14 +158,11 @@ pub enum Verb {
#[arg(long, default_value = "")]
email: String,
},
/// Generate/store all credentials in OpenBao.
/// Seed credentials and secrets.
Seed,
/// E2E VSO + OpenBao integration test.
/// End-to-end integration test.
Verify,
/// kubectl logs for a service.
/// View service logs.
Logs {
/// namespace/name
target: String,
@@ -70,22 +170,19 @@ pub enum Verb {
#[arg(short, long)]
follow: bool,
},
/// Raw kubectl get for a pod (ns/name).
/// Get a resource (ns/name).
Get {
/// namespace/name
target: String,
/// kubectl output format (yaml, json, wide).
/// Output format (yaml, json, wide).
#[arg(long = "kubectl-output", default_value = "yaml", value_parser = ["yaml", "json", "wide"])]
output: String,
},
/// Rolling restart of services.
Restart {
/// namespace or namespace/name
target: Option<String>,
},
/// Build an artifact.
Build {
/// What to build.
@@ -96,146 +193,25 @@ pub enum Verb {
/// Apply manifests and rollout restart after pushing (implies --push).
#[arg(long)]
deploy: bool,
/// Disable buildkitd layer cache.
/// Disable layer cache.
#[arg(long)]
no_cache: bool,
},
/// Functional service health checks.
/// Service health checks.
Check {
/// namespace or namespace/name
target: Option<String>,
},
/// Mirror amd64-only La Suite images.
/// Mirror container images.
Mirror,
/// Create Gitea orgs/repos; bootstrap services.
/// Bootstrap orgs, repos, and services.
Bootstrap,
/// Manage sunbeam configuration.
Config {
#[command(subcommand)]
action: Option<ConfigAction>,
},
/// kubectl passthrough.
K8s {
/// arguments forwarded verbatim to kubectl
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
kubectl_args: Vec<String>,
},
/// bao CLI passthrough (runs inside OpenBao pod with root token).
Bao {
/// arguments forwarded verbatim to bao
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
bao_args: Vec<String>,
},
/// Project management across Planka and Gitea.
Pm {
#[command(subcommand)]
action: Option<PmAction>,
},
/// Self-update from latest mainline commit.
Update,
/// Print version info.
Version,
// -- Service commands (new) -----------------------------------------------
/// Authentication, identity & OAuth2 management.
Auth {
#[command(subcommand)]
action: sunbeam_sdk::identity::cli::AuthCommand,
},
/// Version control (Gitea).
Vcs {
#[command(subcommand)]
action: sunbeam_sdk::gitea::cli::VcsCommand,
},
/// Chat / messaging (Matrix).
Chat {
#[command(subcommand)]
action: sunbeam_sdk::matrix::cli::ChatCommand,
},
/// Search engine (OpenSearch).
Search {
#[command(subcommand)]
action: sunbeam_sdk::search::cli::SearchCommand,
},
/// Object storage (S3).
Storage {
#[command(subcommand)]
action: sunbeam_sdk::storage::cli::StorageCommand,
},
/// Media / video (LiveKit).
Media {
#[command(subcommand)]
action: sunbeam_sdk::media::cli::MediaCommand,
},
/// Monitoring (Prometheus, Loki, Grafana).
Mon {
#[command(subcommand)]
action: sunbeam_sdk::monitoring::cli::MonCommand,
},
/// Secrets management (OpenBao/Vault).
Vault {
#[command(subcommand)]
action: sunbeam_sdk::openbao::cli::VaultCommand,
},
/// People / contacts (La Suite).
People {
#[command(subcommand)]
action: sunbeam_sdk::lasuite::cli::PeopleCommand,
},
/// Documents (La Suite).
Docs {
#[command(subcommand)]
action: sunbeam_sdk::lasuite::cli::DocsCommand,
},
/// Video meetings (La Suite).
Meet {
#[command(subcommand)]
action: sunbeam_sdk::lasuite::cli::MeetCommand,
},
/// File storage (La Suite).
Drive {
#[command(subcommand)]
action: sunbeam_sdk::lasuite::cli::DriveCommand,
},
/// Email (La Suite).
Mail {
#[command(subcommand)]
action: sunbeam_sdk::lasuite::cli::MailCommand,
},
/// Calendar (La Suite).
Cal {
#[command(subcommand)]
action: sunbeam_sdk::lasuite::cli::CalCommand,
},
/// Search across La Suite services.
Find {
#[command(subcommand)]
action: sunbeam_sdk::lasuite::cli::FindCommand,
},
}
#[derive(Subcommand, Debug)]
@@ -332,16 +308,16 @@ mod tests {
// 1. test_up
#[test]
fn test_up() {
let cli = parse(&["sunbeam", "up"]);
assert!(matches!(cli.verb, Some(Verb::Up)));
let cli = parse(&["sunbeam", "platform", "up"]);
assert!(matches!(cli.verb, Some(Verb::Platform { action: PlatformAction::Up })));
}
// 2. test_status_no_target
#[test]
fn test_status_no_target() {
let cli = parse(&["sunbeam", "status"]);
let cli = parse(&["sunbeam", "platform", "status"]);
match cli.verb {
Some(Verb::Status { target }) => assert!(target.is_none()),
Some(Verb::Platform { action: PlatformAction::Status { target } }) => assert!(target.is_none()),
_ => panic!("expected Status"),
}
}
@@ -349,9 +325,9 @@ mod tests {
// 3. test_status_with_namespace
#[test]
fn test_status_with_namespace() {
let cli = parse(&["sunbeam", "status", "ory"]);
let cli = parse(&["sunbeam", "platform", "status", "ory"]);
match cli.verb {
Some(Verb::Status { target }) => assert_eq!(target.unwrap(), "ory"),
Some(Verb::Platform { action: PlatformAction::Status { target } }) => assert_eq!(target.unwrap(), "ory"),
_ => panic!("expected Status"),
}
}
@@ -359,9 +335,9 @@ mod tests {
// 4. test_logs_no_follow
#[test]
fn test_logs_no_follow() {
let cli = parse(&["sunbeam", "logs", "ory/kratos"]);
let cli = parse(&["sunbeam", "platform", "logs", "ory/kratos"]);
match cli.verb {
Some(Verb::Logs { target, follow }) => {
Some(Verb::Platform { action: PlatformAction::Logs { target, follow } }) => {
assert_eq!(target, "ory/kratos");
assert!(!follow);
}
@@ -372,9 +348,9 @@ mod tests {
// 5. test_logs_follow_short
#[test]
fn test_logs_follow_short() {
let cli = parse(&["sunbeam", "logs", "ory/kratos", "-f"]);
let cli = parse(&["sunbeam", "platform", "logs", "ory/kratos", "-f"]);
match cli.verb {
Some(Verb::Logs { follow, .. }) => assert!(follow),
Some(Verb::Platform { action: PlatformAction::Logs { follow, .. } }) => assert!(follow),
_ => panic!("expected Logs"),
}
}
@@ -382,9 +358,9 @@ mod tests {
// 6. test_build_proxy
#[test]
fn test_build_proxy() {
let cli = parse(&["sunbeam", "build", "proxy"]);
let cli = parse(&["sunbeam", "platform", "build", "proxy"]);
match cli.verb {
Some(Verb::Build { what, push, deploy, no_cache }) => {
Some(Verb::Platform { action: PlatformAction::Build { what, push, deploy, no_cache } }) => {
assert!(matches!(what, BuildTarget::Proxy));
assert!(!push);
assert!(!deploy);
@@ -397,9 +373,9 @@ mod tests {
// 7. test_build_deploy_flag
#[test]
fn test_build_deploy_flag() {
let cli = parse(&["sunbeam", "build", "proxy", "--deploy"]);
let cli = parse(&["sunbeam", "platform", "build", "proxy", "--deploy"]);
match cli.verb {
Some(Verb::Build { deploy, push, no_cache, .. }) => {
Some(Verb::Platform { action: PlatformAction::Build { deploy, push, no_cache, .. } }) => {
assert!(deploy);
// clap does not imply --push; that logic is in dispatch()
assert!(!push);
@@ -412,16 +388,16 @@ mod tests {
// 8. test_build_invalid_target
#[test]
fn test_build_invalid_target() {
let result = Cli::try_parse_from(&["sunbeam", "build", "notavalidtarget"]);
let result = Cli::try_parse_from(&["sunbeam", "platform", "build", "notavalidtarget"]);
assert!(result.is_err());
}
// 12. test_apply_no_namespace
#[test]
fn test_apply_no_namespace() {
let cli = parse(&["sunbeam", "apply"]);
let cli = parse(&["sunbeam", "platform", "apply"]);
match cli.verb {
Some(Verb::Apply { namespace, .. }) => assert!(namespace.is_none()),
Some(Verb::Platform { action: PlatformAction::Apply { namespace, .. } }) => assert!(namespace.is_none()),
_ => panic!("expected Apply"),
}
}
@@ -429,9 +405,9 @@ mod tests {
// 13. test_apply_with_namespace
#[test]
fn test_apply_with_namespace() {
let cli = parse(&["sunbeam", "apply", "lasuite"]);
let cli = parse(&["sunbeam", "platform", "apply", "lasuite"]);
match cli.verb {
Some(Verb::Apply { namespace, .. }) => assert_eq!(namespace.unwrap(), "lasuite"),
Some(Verb::Platform { action: PlatformAction::Apply { namespace, .. } }) => assert_eq!(namespace.unwrap(), "lasuite"),
_ => panic!("expected Apply"),
}
}
@@ -482,9 +458,9 @@ mod tests {
// 17. test_get_json_output
#[test]
fn test_get_json_output() {
let cli = parse(&["sunbeam", "get", "ory/kratos-abc", "--kubectl-output", "json"]);
let cli = parse(&["sunbeam", "platform", "get", "ory/kratos-abc", "--kubectl-output", "json"]);
match cli.verb {
Some(Verb::Get { target, output }) => {
Some(Verb::Platform { action: PlatformAction::Get { target, output } }) => {
assert_eq!(target, "ory/kratos-abc");
assert_eq!(output, "json");
}
@@ -495,9 +471,9 @@ mod tests {
// 18. test_check_with_target
#[test]
fn test_check_with_target() {
let cli = parse(&["sunbeam", "check", "devtools"]);
let cli = parse(&["sunbeam", "platform", "check", "devtools"]);
match cli.verb {
Some(Verb::Check { target }) => assert_eq!(target.unwrap(), "devtools"),
Some(Verb::Platform { action: PlatformAction::Check { target } }) => assert_eq!(target.unwrap(), "devtools"),
_ => panic!("expected Check"),
}
}
@@ -505,9 +481,9 @@ mod tests {
// 19. test_build_messages_components
#[test]
fn test_build_messages_backend() {
let cli = parse(&["sunbeam", "build", "messages-backend"]);
let cli = parse(&["sunbeam", "platform", "build", "messages-backend"]);
match cli.verb {
Some(Verb::Build { what, .. }) => {
Some(Verb::Platform { action: PlatformAction::Build { what, .. } }) => {
assert!(matches!(what, BuildTarget::MessagesBackend));
}
_ => panic!("expected Build"),
@@ -516,9 +492,9 @@ mod tests {
#[test]
fn test_build_messages_frontend() {
let cli = parse(&["sunbeam", "build", "messages-frontend"]);
let cli = parse(&["sunbeam", "platform", "build", "messages-frontend"]);
match cli.verb {
Some(Verb::Build { what, .. }) => {
Some(Verb::Platform { action: PlatformAction::Build { what, .. } }) => {
assert!(matches!(what, BuildTarget::MessagesFrontend));
}
_ => panic!("expected Build"),
@@ -527,9 +503,9 @@ mod tests {
#[test]
fn test_build_messages_mta_in() {
let cli = parse(&["sunbeam", "build", "messages-mta-in"]);
let cli = parse(&["sunbeam", "platform", "build", "messages-mta-in"]);
match cli.verb {
Some(Verb::Build { what, .. }) => {
Some(Verb::Platform { action: PlatformAction::Build { what, .. } }) => {
assert!(matches!(what, BuildTarget::MessagesMtaIn));
}
_ => panic!("expected Build"),
@@ -538,9 +514,9 @@ mod tests {
#[test]
fn test_build_messages_mta_out() {
let cli = parse(&["sunbeam", "build", "messages-mta-out"]);
let cli = parse(&["sunbeam", "platform", "build", "messages-mta-out"]);
match cli.verb {
Some(Verb::Build { what, .. }) => {
Some(Verb::Platform { action: PlatformAction::Build { what, .. } }) => {
assert!(matches!(what, BuildTarget::MessagesMtaOut));
}
_ => panic!("expected Build"),
@@ -549,9 +525,9 @@ mod tests {
#[test]
fn test_build_messages_mpa() {
let cli = parse(&["sunbeam", "build", "messages-mpa"]);
let cli = parse(&["sunbeam", "platform", "build", "messages-mpa"]);
match cli.verb {
Some(Verb::Build { what, .. }) => {
Some(Verb::Platform { action: PlatformAction::Build { what, .. } }) => {
assert!(matches!(what, BuildTarget::MessagesMpa));
}
_ => panic!("expected Build"),
@@ -560,9 +536,9 @@ mod tests {
#[test]
fn test_build_messages_socks_proxy() {
let cli = parse(&["sunbeam", "build", "messages-socks-proxy"]);
let cli = parse(&["sunbeam", "platform", "build", "messages-socks-proxy"]);
match cli.verb {
Some(Verb::Build { what, .. }) => {
Some(Verb::Platform { action: PlatformAction::Build { what, .. } }) => {
assert!(matches!(what, BuildTarget::MessagesSocksProxy));
}
_ => panic!("expected Build"),
@@ -643,18 +619,6 @@ mod tests {
assert!(matches!(cli.verb, Some(Verb::Vault { .. })));
}
#[test]
fn test_people_contact_list() {
let cli = parse(&["sunbeam", "people", "contact", "list"]);
assert!(matches!(cli.verb, Some(Verb::People { .. })));
}
#[test]
fn test_docs_document_list() {
let cli = parse(&["sunbeam", "docs", "document", "list"]);
assert!(matches!(cli.verb, Some(Verb::Docs { .. })));
}
#[test]
fn test_meet_room_list() {
let cli = parse(&["sunbeam", "meet", "room", "list"]);
@@ -694,12 +658,12 @@ mod tests {
#[test]
fn test_infra_commands_preserved() {
// Verify all old infra commands still parse
assert!(matches!(parse(&["sunbeam", "up"]).verb, Some(Verb::Up)));
assert!(matches!(parse(&["sunbeam", "seed"]).verb, Some(Verb::Seed)));
assert!(matches!(parse(&["sunbeam", "verify"]).verb, Some(Verb::Verify)));
assert!(matches!(parse(&["sunbeam", "mirror"]).verb, Some(Verb::Mirror)));
assert!(matches!(parse(&["sunbeam", "bootstrap"]).verb, Some(Verb::Bootstrap)));
// Verify all old infra commands still parse under platform
assert!(matches!(parse(&["sunbeam", "platform", "up"]).verb, Some(Verb::Platform { action: PlatformAction::Up })));
assert!(matches!(parse(&["sunbeam", "platform", "seed"]).verb, Some(Verb::Platform { action: PlatformAction::Seed })));
assert!(matches!(parse(&["sunbeam", "platform", "verify"]).verb, Some(Verb::Platform { action: PlatformAction::Verify })));
assert!(matches!(parse(&["sunbeam", "platform", "mirror"]).verb, Some(Verb::Platform { action: PlatformAction::Mirror })));
assert!(matches!(parse(&["sunbeam", "platform", "bootstrap"]).verb, Some(Verb::Platform { action: PlatformAction::Bootstrap })));
assert!(matches!(parse(&["sunbeam", "update"]).verb, Some(Verb::Update)));
assert!(matches!(parse(&["sunbeam", "version"]).verb, Some(Verb::Version)));
}
@@ -739,18 +703,19 @@ pub async fn dispatch() -> Result<()> {
Ok(())
}
Some(Verb::Up) => sunbeam_sdk::cluster::cmd_up().await,
Some(Verb::Platform { action }) => match action {
PlatformAction::Up => sunbeam_sdk::cluster::cmd_up().await,
Some(Verb::Status { target }) => {
PlatformAction::Status { target } => {
sunbeam_sdk::services::cmd_status(target.as_deref()).await
}
Some(Verb::Apply {
PlatformAction::Apply {
namespace,
apply_all,
domain,
email,
}) => {
} => {
let is_production = !sunbeam_sdk::config::active_context().ssh_host.is_empty();
let env_str = if is_production { "production" } else { "local" };
let domain = if domain.is_empty() {
@@ -782,34 +747,39 @@ pub async fn dispatch() -> Result<()> {
sunbeam_sdk::manifests::cmd_apply(&env_str, &domain, &email, &ns).await
}
Some(Verb::Seed) => sunbeam_sdk::secrets::cmd_seed().await,
PlatformAction::Seed => sunbeam_sdk::secrets::cmd_seed().await,
Some(Verb::Verify) => sunbeam_sdk::secrets::cmd_verify().await,
PlatformAction::Verify => sunbeam_sdk::secrets::cmd_verify().await,
Some(Verb::Logs { target, follow }) => {
PlatformAction::Logs { target, follow } => {
sunbeam_sdk::services::cmd_logs(&target, follow).await
}
Some(Verb::Get { target, output }) => {
PlatformAction::Get { target, output } => {
sunbeam_sdk::services::cmd_get(&target, &output).await
}
Some(Verb::Restart { target }) => {
PlatformAction::Restart { target } => {
sunbeam_sdk::services::cmd_restart(target.as_deref()).await
}
Some(Verb::Build { what, push, deploy, no_cache }) => {
PlatformAction::Build { what, push, deploy, no_cache } => {
let push = push || deploy;
sunbeam_sdk::images::cmd_build(&what, push, deploy, no_cache).await
}
Some(Verb::Check { target }) => {
PlatformAction::Check { target } => {
sunbeam_sdk::checks::cmd_check(target.as_deref()).await
}
Some(Verb::Mirror) => sunbeam_sdk::images::cmd_mirror().await,
PlatformAction::Mirror => sunbeam_sdk::images::cmd_mirror().await,
Some(Verb::Bootstrap) => sunbeam_sdk::gitea::cmd_bootstrap().await,
PlatformAction::Bootstrap => sunbeam_sdk::gitea::cmd_bootstrap().await,
PlatformAction::K8s { kubectl_args } => {
sunbeam_sdk::kube::cmd_k8s(&kubectl_args).await
}
},
Some(Verb::Config { action }) => match action {
None => {
@@ -908,14 +878,6 @@ pub async fn dispatch() -> Result<()> {
Some(ConfigAction::Clear) => sunbeam_sdk::config::clear_config(),
},
Some(Verb::K8s { kubectl_args }) => {
sunbeam_sdk::kube::cmd_k8s(&kubectl_args).await
}
Some(Verb::Bao { bao_args }) => {
sunbeam_sdk::kube::cmd_bao(&bao_args).await
}
Some(Verb::Auth { action }) => {
let sc = sunbeam_sdk::client::SunbeamClient::from_context(
&sunbeam_sdk::config::active_context(),
@@ -972,20 +934,6 @@ pub async fn dispatch() -> Result<()> {
sunbeam_sdk::openbao::cli::dispatch(action, &sc, cli.output_format).await
}
Some(Verb::People { action }) => {
let sc = sunbeam_sdk::client::SunbeamClient::from_context(
&sunbeam_sdk::config::active_context(),
);
sunbeam_sdk::lasuite::cli::dispatch_people(action, &sc, cli.output_format).await
}
Some(Verb::Docs { action }) => {
let sc = sunbeam_sdk::client::SunbeamClient::from_context(
&sunbeam_sdk::config::active_context(),
);
sunbeam_sdk::lasuite::cli::dispatch_docs(action, &sc, cli.output_format).await
}
Some(Verb::Meet { action }) => {
let sc = sunbeam_sdk::client::SunbeamClient::from_context(
&sunbeam_sdk::config::active_context(),