Public release release-ff7f847f143d

Source commit: ff7f847f143dc67864bed68a86cf259114384bd5

Public tree identity: sha256:bb7dcd2ac78bdbad2f4eba0f49be649d446d67f96f4eb2796941c026412fc032
This commit is contained in:
Clusterflux release 2026-07-17 09:33:47 +02:00
commit 70319cde15
210 changed files with 78958 additions and 0 deletions

View file

@ -0,0 +1,689 @@
#[cfg(test)]
use std::io::{BufRead, BufReader, Write};
#[cfg(test)]
use std::net::TcpListener;
#[cfg(test)]
use std::path::Path;
use std::path::PathBuf;
use clap::{Args, Parser, Subcommand};
#[cfg(test)]
use clusterflux_core::{Capability, Digest, ProjectModel, SourceProviderKind};
#[cfg(test)]
use serde_json::json;
use serde_json::Value;
mod admin;
mod agent;
mod artifact;
mod auth;
mod auth_scope;
mod build;
mod bundle;
mod client;
mod config;
mod confirm;
mod debug;
mod dispatch;
mod doctor;
mod errors;
mod key;
mod logout;
mod logs;
mod node;
mod output;
mod process;
mod process_events;
mod project;
mod quota;
mod run;
mod task;
mod tools;
use admin::{admin_bootstrap_report, admin_status_report, admin_suspend_tenant_report};
use agent::agent_enrollment_plan;
#[cfg(test)]
use artifact::{
artifact_download_report, artifact_export_report, artifact_list_report,
artifact_stream_summary, DEFAULT_ARTIFACT_EXPORT_MAX_BYTES,
};
use artifact::{
artifact_download_report_with_session, artifact_export_report_with_session,
artifact_list_report_with_session,
};
use auth::{
auth_status_report, connect_self_hosted_report, execute_interactive_browser_login, login_plan,
non_interactive_browser_login_report, print_browser_login_success,
read_session_secret_from_stdin,
};
#[cfg(test)]
use auth::{contains_provider_token_field, stored_cli_session_from_login_response, LoginFlowPlan};
#[cfg(test)]
use auth_scope::login_args_for_project;
use build::build_report;
#[cfg(test)]
use bundle::task_abi_digest;
pub(crate) use bundle::{bundle_inspection, discovered_environment_names};
#[cfg(test)]
use client::control_endpoint_identity;
use config::{default_hosted_coordinator_endpoint, read_cli_session};
#[cfg(test)]
use config::{
read_project_config, session_config_file, write_cli_session, write_project_config,
ProjectConfig, StoredCliSession, DEFAULT_HOSTED_COORDINATOR_ENDPOINT,
};
pub(crate) use confirm::confirmation_required_report;
#[cfg(test)]
use debug::debug_attach_report_with_dap;
use debug::{dap_plan, debug_attach_report_with_dap_and_session, exec_dap};
use doctor::doctor_report;
use errors::cli_error_summary;
#[cfg(test)]
use errors::cli_error_summary_for_category;
#[cfg(test)]
use key::{key_add_report, key_list_report, key_revoke_report};
use key::{
key_add_report_with_session, key_list_report_with_session, key_revoke_report_with_session,
};
use logout::{auth_logout_report, logout_report};
#[cfg(test)]
use logs::logs_report;
use logs::logs_report_with_session;
use node::{
attach_plan, execute_node_attach, node_enroll_report, node_list_report, node_revoke_report,
node_status_report,
};
use output::emit_report;
#[cfg(test)]
use output::{apply_command_report_exit_code, human_report};
use process::{
process_abort_report_with_session, process_cancel_report, process_cancel_report_with_session,
process_list_report_with_session, process_restart_report_with_session,
process_status_report_with_session,
};
#[cfg(test)]
use process::{process_restart_report, process_status_report};
#[cfg(test)]
use process_events::task_summaries;
#[cfg(test)]
use process_events::{
artifact_download_session_summary, artifact_export_plan_summary, log_entries,
};
use project::{
project_init_report, project_list_report, project_select_report, project_status_report,
};
use quota::quota_status_report;
#[cfg(test)]
use run::{
agent_session_from_keys, run_plan, run_start_summary, should_execute_local_node, CliSession,
CoordinatorSelection,
};
use run::{run_report, session_from_sources};
#[cfg(test)]
use task::{task_list_report, task_restart_report};
use task::{task_list_report_with_session, task_restart_report_with_session};
use tools::dap_binary_path;
#[derive(Clone, Debug, Parser)]
#[command(
name = "clusterflux",
version,
arg_required_else_help = true,
about = "Clusterflux distributed Wasm runtime CLI.",
after_help = "Primary workflow:
1. clusterflux login --browser
2. clusterflux project init
3. clusterflux node enroll; clusterflux node attach; clusterflux-node --worker
4. clusterflux run [entry] --project <path>
5. Debug with VS Code \"Clusterflux: Launch Virtual Process\" or clusterflux dap
6. Inspect with clusterflux process list, clusterflux process status, task list, logs, and artifact list
7. Request cooperative shutdown with process cancel; force termination with process abort
Use --json on primary commands for scriptable output. Hosted account creation happens in the browser login flow."
)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Clone, Debug, Subcommand)]
enum Commands {
Doctor(DoctorArgs),
Login(LoginArgs),
Logout(AuthLogoutArgs),
Auth {
#[command(subcommand)]
command: AuthCommands,
},
Agent {
#[command(subcommand)]
command: AgentCommands,
},
Key {
#[command(subcommand)]
command: KeyCommands,
},
Project {
#[command(subcommand)]
command: ProjectCommands,
},
Inspect(BundleInspectArgs),
Build(BuildArgs),
Bundle {
#[command(subcommand)]
command: BundleCommands,
},
Run(RunArgs),
Node {
#[command(subcommand)]
command: NodeCommands,
},
Process {
#[command(subcommand)]
command: ProcessCommands,
},
Task {
#[command(subcommand)]
command: TaskCommands,
},
Logs(LogsArgs),
Artifact {
#[command(subcommand)]
command: ArtifactCommands,
},
Dap(DapArgs),
Debug {
#[command(subcommand)]
command: DebugCommands,
},
Quota {
#[command(subcommand)]
command: QuotaCommands,
},
Admin {
#[command(subcommand)]
command: AdminCommands,
},
}
#[derive(Clone, Debug, Parser)]
struct DoctorArgs {
#[command(flatten)]
scope: CliScopeArgs,
}
#[derive(Clone, Debug, Parser)]
struct LoginArgs {
#[arg(long = "browser")]
_browser: bool,
#[arg(long)]
non_interactive: bool,
#[arg(long)]
plan: bool,
#[arg(long)]
json: bool,
#[arg(long, default_value_t = default_hosted_coordinator_endpoint())]
coordinator: String,
#[arg(long = "project-id", default_value = "project")]
project: String,
}
#[derive(Clone, Debug, Subcommand)]
enum AuthCommands {
Status(AuthStatusArgs),
ConnectSelfHosted(ConnectSelfHostedArgs),
Logout(AuthLogoutArgs),
}
#[derive(Clone, Debug, Parser)]
struct AuthStatusArgs {
#[command(flatten)]
scope: CliScopeArgs,
}
#[derive(Clone, Debug, Parser)]
struct ConnectSelfHostedArgs {
#[command(flatten)]
scope: CliScopeArgs,
#[arg(long, required = true)]
session_secret_stdin: bool,
}
#[derive(Clone, Debug, Parser)]
struct AuthLogoutArgs {
#[arg(long)]
yes: bool,
#[command(flatten)]
scope: CliScopeArgs,
}
#[derive(Clone, Debug, Subcommand)]
enum AgentCommands {
Enroll(AgentEnrollArgs),
}
#[derive(Clone, Debug, Subcommand)]
enum KeyCommands {
Add(KeyAddArgs),
List(KeyListArgs),
Revoke(KeyRevokeArgs),
}
#[derive(Clone, Debug, Subcommand)]
enum BundleCommands {
Inspect(BundleInspectArgs),
}
#[derive(Clone, Debug, Parser)]
struct AgentEnrollArgs {
#[arg(long)]
json: bool,
#[arg(long = "public-key")]
public_key: String,
}
#[derive(Clone, Debug, Parser)]
struct KeyAddArgs {
#[command(flatten)]
scope: CliScopeArgs,
#[arg(long, default_value = "agent")]
agent: String,
#[arg(long = "public-key")]
public_key: String,
}
#[derive(Clone, Debug, Parser)]
struct KeyListArgs {
#[command(flatten)]
scope: CliScopeArgs,
}
#[derive(Clone, Debug, Parser)]
struct KeyRevokeArgs {
#[command(flatten)]
scope: CliScopeArgs,
#[arg(long, default_value = "agent")]
agent: String,
#[arg(long)]
yes: bool,
}
#[derive(Clone, Debug, Subcommand)]
enum ProjectCommands {
Init(ProjectInitArgs),
Status(ProjectStatusArgs),
List(ProjectListArgs),
Select(ProjectSelectArgs),
}
#[derive(Clone, Debug, Parser)]
struct ProjectInitArgs {
#[command(flatten)]
scope: CliScopeArgs,
#[arg(long, default_value = "project")]
new_project: String,
#[arg(long, default_value = "Clusterflux Project")]
name: String,
#[arg(long)]
yes: bool,
}
#[derive(Clone, Debug, Parser)]
struct ProjectStatusArgs {
#[command(flatten)]
scope: CliScopeArgs,
}
#[derive(Clone, Debug, Parser)]
struct ProjectListArgs {
#[command(flatten)]
scope: CliScopeArgs,
}
#[derive(Clone, Debug, Parser)]
struct ProjectSelectArgs {
#[command(flatten)]
scope: CliScopeArgs,
#[arg(value_name = "PROJECT")]
selected_project: String,
}
#[derive(Clone, Debug, Parser)]
struct BundleInspectArgs {
#[arg(long)]
project: Option<PathBuf>,
#[arg(long = "source-provider")]
source_provider: Option<String>,
#[arg(long = "disable-source-provider")]
disabled_source_providers: Vec<String>,
#[arg(long)]
json: bool,
}
#[derive(Clone, Debug, Parser)]
struct BuildArgs {
#[arg(long)]
project: Option<PathBuf>,
#[arg(long = "source-provider")]
source_provider: Option<String>,
#[arg(long = "disable-source-provider")]
disabled_source_providers: Vec<String>,
#[arg(long)]
output: Option<PathBuf>,
#[arg(long)]
json: bool,
}
#[derive(Clone, Debug, Parser)]
struct RunArgs {
entry: Option<String>,
#[arg(long)]
project: Option<PathBuf>,
#[arg(long)]
coordinator: Option<String>,
#[arg(long)]
local: bool,
#[arg(long)]
non_interactive: bool,
#[arg(long)]
json: bool,
}
#[derive(Clone, Debug, Subcommand)]
enum NodeCommands {
Attach(AttachArgs),
Enroll(NodeEnrollArgs),
List(NodeListArgs),
Status(NodeStatusArgs),
Revoke(NodeRevokeArgs),
}
#[derive(Clone, Debug, Parser)]
struct AttachArgs {
#[arg(long)]
coordinator: Option<String>,
#[arg(long, default_value = "tenant")]
tenant: String,
#[arg(long = "project-id", default_value = "project")]
project: String,
#[arg(long)]
node: Option<String>,
#[arg(long = "cap")]
caps: Vec<String>,
#[arg(long = "enrollment-grant")]
enrollment_grant: Option<String>,
#[arg(long = "public-key")]
public_key: Option<String>,
#[arg(long)]
json: bool,
}
#[derive(Clone, Debug, Parser)]
struct NodeEnrollArgs {
#[command(flatten)]
scope: CliScopeArgs,
#[arg(long, default_value_t = 900)]
ttl_seconds: u64,
}
#[derive(Clone, Debug, Parser)]
struct NodeListArgs {
#[command(flatten)]
scope: CliScopeArgs,
}
#[derive(Clone, Debug, Parser)]
struct NodeStatusArgs {
#[command(flatten)]
scope: CliScopeArgs,
#[arg(long)]
node: Option<String>,
}
#[derive(Clone, Debug, Parser)]
struct NodeRevokeArgs {
#[command(flatten)]
scope: CliScopeArgs,
#[arg(long)]
node: String,
#[arg(long)]
yes: bool,
}
#[derive(Clone, Debug, Subcommand)]
enum ProcessCommands {
List(ProcessListArgs),
Status(ProcessStatusArgs),
Restart(ProcessRestartArgs),
Cancel(ProcessCancelArgs),
Abort(ProcessAbortArgs),
}
#[derive(Clone, Debug, Parser)]
struct ProcessListArgs {
#[command(flatten)]
scope: CliScopeArgs,
}
#[derive(Clone, Debug, Parser)]
struct ProcessStatusArgs {
#[command(flatten)]
scope: CliScopeArgs,
#[arg(long, default_value = "vp-current")]
process: String,
}
#[derive(Clone, Debug, Parser)]
struct ProcessRestartArgs {
#[command(flatten)]
scope: CliScopeArgs,
#[arg(long, default_value = "vp-current")]
process: String,
#[arg(long)]
yes: bool,
}
#[derive(Clone, Debug, Parser)]
struct ProcessCancelArgs {
#[command(flatten)]
scope: CliScopeArgs,
#[arg(long, default_value = "vp-current")]
process: String,
#[arg(long)]
node: Option<String>,
#[arg(long)]
task: Option<String>,
#[arg(long)]
yes: bool,
}
#[derive(Clone, Debug, Parser)]
struct ProcessAbortArgs {
#[command(flatten)]
scope: CliScopeArgs,
#[arg(long, default_value = "vp-current")]
process: String,
#[arg(long)]
yes: bool,
}
#[derive(Clone, Debug, Subcommand)]
enum TaskCommands {
List(TaskListArgs),
Restart(TaskRestartArgs),
}
#[derive(Clone, Debug, Parser)]
struct TaskListArgs {
#[command(flatten)]
scope: CliScopeArgs,
#[arg(long)]
process: Option<String>,
}
#[derive(Clone, Debug, Parser)]
struct TaskRestartArgs {
#[command(flatten)]
scope: CliScopeArgs,
task: String,
#[arg(long, default_value = "vp-current")]
process: String,
#[arg(long)]
yes: bool,
}
#[derive(Clone, Debug, Parser)]
struct LogsArgs {
#[command(flatten)]
scope: CliScopeArgs,
#[arg(long)]
process: Option<String>,
#[arg(long)]
task: Option<String>,
}
#[derive(Clone, Debug, Subcommand)]
enum ArtifactCommands {
List(ArtifactListArgs),
Download(ArtifactDownloadArgs),
Export(ArtifactExportArgs),
}
#[derive(Clone, Debug, Parser)]
struct ArtifactListArgs {
#[command(flatten)]
scope: CliScopeArgs,
#[arg(long)]
process: Option<String>,
}
#[derive(Clone, Debug, Parser)]
struct ArtifactDownloadArgs {
#[command(flatten)]
scope: CliScopeArgs,
artifact: String,
/// Write the verified artifact bytes to this local path.
#[arg(long)]
to: Option<PathBuf>,
#[arg(long, default_value_t = 64 * 1024 * 1024)]
max_bytes: u64,
}
#[derive(Clone, Debug, Parser)]
struct ArtifactExportArgs {
#[command(flatten)]
scope: CliScopeArgs,
artifact: String,
#[arg(long)]
to: PathBuf,
#[arg(long, default_value = "node-local")]
receiver_node: String,
}
#[derive(Clone, Debug, Parser)]
struct DapArgs {
#[arg(long)]
plan: bool,
#[arg(long)]
json: bool,
#[arg(last = true)]
args: Vec<String>,
}
#[derive(Clone, Debug, Subcommand)]
enum DebugCommands {
Attach(DebugAttachArgs),
}
#[derive(Clone, Debug, Parser)]
struct DebugAttachArgs {
#[command(flatten)]
scope: CliScopeArgs,
#[arg(long, default_value = "vp-current")]
process: String,
}
#[derive(Clone, Debug, Subcommand)]
enum QuotaCommands {
Status(QuotaStatusArgs),
}
#[derive(Clone, Debug, Parser)]
struct QuotaStatusArgs {
#[command(flatten)]
scope: CliScopeArgs,
}
#[derive(Clone, Debug, Subcommand)]
enum AdminCommands {
Status(AdminStatusArgs),
Bootstrap(AdminBootstrapArgs),
RevokeNode(NodeRevokeArgs),
StopProcess(ProcessCancelArgs),
SuspendTenant(AdminSuspendTenantArgs),
}
#[derive(Clone, Debug, Parser)]
struct AdminStatusArgs {
#[command(flatten)]
scope: CliScopeArgs,
#[arg(long)]
admin_token: Option<String>,
}
#[derive(Clone, Debug, Parser)]
struct AdminBootstrapArgs {
#[command(flatten)]
scope: CliScopeArgs,
#[arg(long, default_value = "Clusterflux Project")]
name: String,
#[arg(long)]
yes: bool,
}
#[derive(Clone, Debug, Parser)]
struct AdminSuspendTenantArgs {
#[command(flatten)]
scope: CliScopeArgs,
#[arg(long = "target-tenant")]
target_tenant: Option<String>,
#[arg(long)]
admin_token: Option<String>,
#[arg(long)]
yes: bool,
}
#[derive(Clone, Debug, Args)]
struct CliScopeArgs {
#[arg(long)]
coordinator: Option<String>,
#[arg(long, default_value = "tenant")]
tenant: String,
#[arg(long = "project-id", default_value = "project")]
project: String,
#[arg(long, default_value = "user")]
user: String,
#[arg(long)]
json: bool,
}
fn main() {
if let Err(error) = dispatch::run_cli() {
let message = error.to_string();
let machine_error = cli_error_summary(&message);
let category = machine_error
.get("category")
.and_then(Value::as_str)
.unwrap_or("unknown");
let exit_code = machine_error
.get("stable_exit_code")
.and_then(Value::as_i64)
.and_then(|code| i32::try_from(code).ok())
.unwrap_or(1);
eprintln!("Error ({category}, exit {exit_code}): {error:#}");
std::process::exit(exit_code);
}
}
#[cfg(test)]
mod tests;