diff --git a/src/commands/sandbox.rs b/src/commands/sandbox.rs index c6818fe8d..57b9be5fc 100644 --- a/src/commands/sandbox.rs +++ b/src/commands/sandbox.rs @@ -1,3 +1,5 @@ +use std::collections::BTreeMap; +use std::str::FromStr; use std::time::Duration; use anyhow::{Result, anyhow, bail}; @@ -5,10 +7,11 @@ use clap::Parser; use is_terminal::IsTerminal; use crate::client::{GQLClient, post_graphql}; -use crate::commands::ssh::{ensure_ssh_key, run_native_ssh}; -use crate::config::{Configs, StoredSandbox}; +use crate::commands::ssh::{ensure_ssh_key, run_native_ssh, tel}; +use crate::config::{Configs, StoredSandbox, StoredSandboxTemplate}; use crate::controllers::environment::get_matched_environment; use crate::controllers::project::get_project; +use crate::controllers::variables::Variable; use crate::gql::{mutations, queries}; use crate::util::progress::{create_shimmer_spinner, fail_spinner}; use crate::util::prompt::{prompt_options, prompt_options_skippable}; @@ -16,7 +19,7 @@ use crate::util::prompt::{prompt_options, prompt_options_skippable}; /// Manage ephemeral sandboxes #[derive(Parser)] #[clap( - after_help = "Examples:\n\n railway sandbox create # create + remember it as active\n railway sandbox list # list sandboxes in the environment\n railway sandbox ssh # connect to the active (last) sandbox\n railway sandbox ssh --id # connect to a specific sandbox\n railway sandbox exec --id -- ls -la\n railway sandbox destroy --id \n\nNote: requires the PROJECT_SANDBOXES feature to be enabled." + after_help = "Examples:\n\n railway sandbox create # create + remember it as active\n railway sandbox create --variable FOO=bar,DB_URL=postgres.DATABASE_URL\n railway sandbox create --env-file .env\n railway sandbox template build --name dev -c 'npm i -g pnpm' --wait\n railway sandbox create --template dev # boot from the pre-built snapshot\n railway sandbox list # list sandboxes in the environment\n railway sandbox ssh # connect to the active (last) sandbox\n railway sandbox ssh --id # connect to a specific sandbox\n railway sandbox exec --id -- ls -la\n railway sandbox fork # fork the active sandbox; the fork becomes active\n railway sandbox fork --variable FOO=bar\n railway sandbox destroy --id \n\nNote: requires the PROJECT_SANDBOXES feature to be enabled." )] pub struct Args { #[clap(subcommand)] @@ -37,6 +40,12 @@ enum Commands { #[clap(visible_alias = "new")] Create(CreateArgs), + /// Fork an existing sandbox into a new one and make it active + Fork(ForkArgs), + + /// Manage sandbox templates (pre-built filesystem snapshots) + Template(TemplateArgs), + /// List sandboxes in the environment #[clap(visible_alias = "ls")] List(ListArgs), @@ -59,11 +68,147 @@ struct CreateArgs { #[clap(long)] idle_timeout_minutes: Option, + /// Set a variable on the sandbox (repeatable, comma-separable). Values may + /// reference other variables — `DB_URL=postgres.DATABASE_URL` or the full + /// `${{postgres.DATABASE_URL}}` form — resolved server-side at create time + #[clap(long = "variable", value_name = "KEY=VALUE[,KEY=VALUE...]")] + variables: Vec, + + /// Load variables from a .env file (repeatable). `--variable` flags + /// override file entries with the same key + #[clap(long = "env-file", value_name = "PATH")] + env_files: Vec, + + /// Create from a built template, by local name or template id (see + /// `railway sandbox template build`) + #[clap(long, value_name = "NAME_OR_ID")] + template: Option, + + /// Join the environment's private network (default: isolated, public + /// egress only). Needed to reach internal hosts like + /// `postgres.railway.internal` + #[clap(long)] + private_network: bool, + + /// Output the created sandbox as JSON + #[clap(long)] + json: bool, +} + +#[derive(Parser)] +struct TemplateArgs { + #[clap(subcommand)] + command: TemplateCommands, +} + +#[derive(Parser)] +enum TemplateCommands { + /// Build a template from shell instructions. Templates are + /// content-addressed and cached server-side (~7 days), so re-running the + /// same build is an instant cache hit + #[clap(visible_alias = "create", visible_alias = "new")] + Build(TemplateBuildArgs), + + /// Show the build status of a template + Status(TemplateStatusArgs), + + /// List templates this CLI has built + #[clap(visible_alias = "ls")] + List(TemplateListArgs), +} + +#[derive(Parser)] +struct TemplateBuildArgs { + /// Shell instruction to run while building (repeatable, runs in order; + /// each step must exit 0 within 10 minutes) + #[clap( + short = 'c', + long = "command", + value_name = "SHELL_COMMAND", + required = true + )] + commands: Vec, + + /// Local name for the template, usable with `railway sandbox create + /// --template ` + #[clap(long)] + name: Option, + + /// Base image digest to build on (defaults to the standard sandbox image) + #[clap(long, value_name = "DIGEST")] + base_image_digest: Option, + + /// Wait for the build to finish (polls until READY or FAILED) + #[clap(long)] + wait: bool, + + /// Output as JSON + #[clap(long)] + json: bool, +} + +#[derive(Parser)] +struct TemplateStatusArgs { + /// Template id or local name + #[clap(value_name = "ID_OR_NAME")] + template: String, + + /// Output as JSON + #[clap(long)] + json: bool, +} + +#[derive(Parser)] +struct TemplateListArgs { + /// Output as JSON + #[clap(long)] + json: bool, +} + +/// Fork has no trailing command, so a positional id is unambiguous; `--id` is +/// also accepted. Omitted → the active sandbox is the fork source. +#[derive(Parser)] +struct ForkArgs { + /// Source sandbox ID to fork (defaults to the active sandbox) + #[clap(value_name = "ID")] + id_positional: Option, + + /// Source sandbox ID (alternative to the positional argument) + #[clap(long = "id", value_name = "ID")] + id: Option, + + /// Minutes the new sandbox may sit idle before it is auto-destroyed + #[clap(long)] + idle_timeout_minutes: Option, + + /// Set a variable on the fork (repeatable, comma-separable). The fork does + /// not inherit the source's variables; values may reference other + /// variables — `DB_URL=postgres.DATABASE_URL` or the full + /// `${{postgres.DATABASE_URL}}` form — resolved server-side at fork time + #[clap(long = "variable", value_name = "KEY=VALUE[,KEY=VALUE...]")] + variables: Vec, + + /// Load variables from a .env file (repeatable). `--variable` flags + /// override file entries with the same key + #[clap(long = "env-file", value_name = "PATH")] + env_files: Vec, + + /// Join the environment's private network (default: isolated, public + /// egress only). The fork does not inherit the source's network mode + #[clap(long)] + private_network: bool, + /// Output the created sandbox as JSON #[clap(long)] json: bool, } +impl ForkArgs { + fn explicit_id(&self) -> Option { + self.id.clone().or_else(|| self.id_positional.clone()) + } +} + #[derive(Parser)] struct ListArgs { /// Output as JSON @@ -124,6 +269,13 @@ impl DestroyArgs { } pub async fn command(args: Args) -> Result<()> { + use colored::Colorize; + eprintln!( + "{}", + "Warning: Railway sandboxes are experimental and APIs may change or break during testing." + .yellow() + ); + let mut configs = Configs::new()?; let client = GQLClient::new_authorized(&configs)?; let project = args.project; @@ -131,6 +283,8 @@ pub async fn command(args: Args) -> Result<()> { match args.command { Commands::Create(sub) => create(&mut configs, &client, project, environment, sub).await, + Commands::Fork(sub) => fork(&mut configs, &client, project, environment, sub).await, + Commands::Template(sub) => template(&mut configs, &client, project, environment, sub).await, Commands::List(sub) => list(&mut configs, &client, project, environment, sub).await, Commands::Ssh(sub) => ssh(&mut configs, &client, project, environment, sub).await, Commands::Exec(sub) => exec(&mut configs, &client, project, environment, sub).await, @@ -330,33 +484,155 @@ async fn resolve_target( } } -async fn create( +/// Parse repeatable `--variable` values into key/value pairs. Each argument is +/// a single `KEY=VALUE` or a comma-separated list of them (`A=1,B=2`). A comma +/// only splits when every segment carries its own `=` — `ALLOWED=a.com,b.com` +/// stays one variable whose value contains the comma. Repeating the flag is +/// the unambiguous form for values that mix commas and `=`. +fn parse_variable_args(args: &[String]) -> Result> { + let mut vars = Vec::new(); + for arg in args { + let segments: Vec<&str> = arg.split(',').collect(); + if segments.len() > 1 && segments.iter().all(|s| s.contains('=')) { + for segment in segments { + vars.push(Variable::from_str(segment)?); + } + } else { + vars.push(Variable::from_str(arg)?); + } + } + Ok(vars) +} + +/// Wrap a bare Railway reference (`name.VAR`) in `${{...}}` so users can write +/// `--variable DB_URL=postgres.DATABASE_URL` without shell-quoting the full +/// `${{postgres.DATABASE_URL}}` form. Only an exact `.` value is +/// wrapped — `name` alphanumeric/`_`/`-` starting with a letter, `VAR` in +/// UPPER_SNAKE starting with an uppercase letter — so plain values like `1.5`, +/// `example.com`, or `file.txt` pass through untouched, as does anything +/// already containing `${{`. The `shared.` namespace is unmistakable, so its +/// var segment may be any case (`shared.char`), not just UPPER_SNAKE. +fn auto_wrap_reference(value: &str) -> String { + if value.contains("${{") { + return value.to_string(); + } + let Some((name, var)) = value.split_once('.') else { + return value.to_string(); + }; + let name_ok = name.chars().next().is_some_and(|c| c.is_ascii_alphabetic()) + && name + .chars() + .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-'); + let var_ok = if name == "shared" { + var.chars().next().is_some_and(|c| c.is_ascii_alphabetic()) + && var.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') + } else { + var.chars().next().is_some_and(|c| c.is_ascii_uppercase()) + && var + .chars() + .all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_') + }; + if name_ok && var_ok { + format!("${{{{{name}.{var}}}}}") + } else { + value.to_string() + } +} + +/// Parse a dotenv-style file into key/value pairs. Supports `KEY=VALUE` lines, +/// blank lines, `#` comments, an optional `export ` prefix, single/double +/// quoted values (kept verbatim inside the quotes), and trailing ` #` comments +/// on unquoted values. Multiline values are not supported. +fn parse_env_file(path: &std::path::Path) -> Result> { + let contents = std::fs::read_to_string(path) + .map_err(|e| anyhow!("Failed to read env file {}: {e}", path.display()))?; + let mut vars = Vec::new(); + for (i, raw_line) in contents.lines().enumerate() { + let line = raw_line.trim(); + if line.is_empty() || line.starts_with('#') { + continue; + } + let line = line.strip_prefix("export ").unwrap_or(line).trim_start(); + let Some((key, value)) = line.split_once('=') else { + bail!( + "{}:{}: expected KEY=VALUE, got `{raw_line}`", + path.display(), + i + 1 + ); + }; + let key = key.trim(); + if key.is_empty() { + bail!("{}:{}: empty variable name", path.display(), i + 1); + } + let value = value.trim(); + let value = if (value.starts_with('"') && value.ends_with('"') && value.len() >= 2) + || (value.starts_with('\'') && value.ends_with('\'') && value.len() >= 2) + { + &value[1..value.len() - 1] + } else { + // Unquoted: strip a trailing ` # comment`. + value.split(" #").next().unwrap_or(value).trim_end() + }; + vars.push(Variable { + key: key.to_string(), + value: value.to_string(), + }); + } + Ok(vars) +} + +/// Convert `--env-file` and `--variable` args into the `EnvironmentVariables` +/// scalar, wrapping bare references. Files load first (in order), then flags — +/// so a `--variable` overrides a file entry with the same key. `None` when +/// empty so `skip_serializing_none` omits the field from the mutation input. +fn variables_to_input( + env_files: &[std::path::PathBuf], + args: &[String], +) -> Result>> { + let mut vars = Vec::new(); + for path in env_files { + vars.extend(parse_env_file(path)?); + } + vars.extend(parse_variable_args(args)?); + if vars.is_empty() { + return Ok(None); + } + Ok(Some( + vars.into_iter() + .map(|v| (v.key, auto_wrap_reference(&v.value))) + .collect(), + )) +} + +/// Run `sandboxCreate` with the given input, persist the result as the active +/// sandbox (create and fork both retarget `ssh`/`exec` at the new sandbox), +/// and print create-style output. +async fn create_and_store( configs: &mut Configs, client: &reqwest::Client, - project: Option, - environment: Option, - args: CreateArgs, + project_id: String, + environment_id: String, + input: mutations::sandbox_create::SandboxCreateInput, + json: bool, + forked: bool, ) -> Result<()> { - let (project_id, environment_id) = - resolve_project_and_env(configs, client, project, environment).await?; + let (doing, did, failed) = if forked { + ("Forking sandbox", "Forked", "Failed to fork sandbox") + } else { + ("Creating sandbox", "Created", "Failed to create sandbox") + }; - let mut spinner = create_shimmer_spinner("Creating sandbox"); + let mut spinner = create_shimmer_spinner(doing); let sandbox = match post_graphql::( client, configs.get_backboard(), - mutations::sandbox_create::Variables { - input: mutations::sandbox_create::SandboxCreateInput { - environment_id: environment_id.clone(), - idle_timeout_minutes: args.idle_timeout_minutes, - template: None, - }, - }, + mutations::sandbox_create::Variables { input }, ) .await { Ok(res) => res.sandbox_create, Err(e) => { - fail_spinner(&mut spinner, "Failed to create sandbox".to_string()); + fail_spinner(&mut spinner, failed.to_string()); return Err(e.into()); } }; @@ -373,10 +649,10 @@ async fn create( ); configs.write()?; - if args.json { + if json { println!("{}", serde_json::to_string_pretty(&sandbox)?); } else { - println!("✓ Created sandbox {} (now active)", sandbox.id); + println!("✓ {did} sandbox {} (now active)", sandbox.id); println!(" status: {:?}", sandbox.status); println!(" region: {}", sandbox.region); if let Some(idle) = sandbox.idle_timeout_minutes { @@ -387,6 +663,355 @@ async fn create( Ok(()) } +async fn create( + configs: &mut Configs, + client: &reqwest::Client, + project: Option, + environment: Option, + args: CreateArgs, +) -> Result<()> { + let (project_id, environment_id) = + resolve_project_and_env(configs, client, project, environment).await?; + + // Templates are content-addressed server-side: sandboxCreate needs the + // full recipe, not just the id, so resolve it from the local store. + let template = match &args.template { + Some(handle) => { + let stored = configs + .find_sandbox_template(handle, Some(&environment_id)) + .ok_or_else(|| { + anyhow!( + "Unknown template `{handle}` for this environment. Build it first:\n railway sandbox template build --name {handle} -c '' --wait" + ) + })?; + Some(mutations::sandbox_create::SandboxTemplateInput { + instructions: stored.instructions, + base_image_digest: stored.base_image_digest, + }) + } + None => None, + }; + + let input = mutations::sandbox_create::SandboxCreateInput { + environment_id: environment_id.clone(), + idle_timeout_minutes: args.idle_timeout_minutes, + template, + source_sandbox_id: None, + network_isolation: args + .private_network + .then_some(mutations::sandbox_create::SandboxNetworkIsolation::PRIVATE), + variables: variables_to_input(&args.env_files, &args.variables)?, + }; + create_and_store( + configs, + client, + project_id, + environment_id, + input, + args.json, + false, + ) + .await +} + +async fn template( + configs: &mut Configs, + client: &reqwest::Client, + project: Option, + environment: Option, + args: TemplateArgs, +) -> Result<()> { + match args.command { + TemplateCommands::Build(sub) => { + template_build(configs, client, project, environment, sub).await + } + TemplateCommands::Status(sub) => { + template_status(configs, client, project, environment, sub).await + } + TemplateCommands::List(sub) => { + template_list(configs, client, project, environment, sub).await + } + } +} + +async fn template_build( + configs: &mut Configs, + client: &reqwest::Client, + project: Option, + environment: Option, + args: TemplateBuildArgs, +) -> Result<()> { + let (_, environment_id) = + resolve_project_and_env(configs, client, project, environment).await?; + + let res = post_graphql::( + client, + configs.get_backboard(), + mutations::sandbox_template_build::Variables { + environment_id: environment_id.clone(), + input: mutations::sandbox_template_build::SandboxTemplateInput { + instructions: args.commands.clone(), + base_image_digest: args.base_image_digest.clone(), + }, + }, + ) + .await?; + let built = res.sandbox_template_build; + + // Keep the recipe locally: `sandbox create --template` must resend the + // instructions, since the server only caches by hash. + configs.upsert_sandbox_template(StoredSandboxTemplate { + id: built.id.clone(), + name: args.name.clone(), + environment_id: environment_id.clone(), + instructions: args.commands, + base_image_digest: args.base_image_digest, + created_at: Some(chrono::Utc::now().to_rfc3339()), + }); + configs.write()?; + + let already_ready = matches!( + built.status, + mutations::sandbox_template_build::SandboxTemplateStatus::READY + ); + let status = if args.wait && !already_ready { + wait_for_template(client, configs, &environment_id, &built.id).await? + } else { + format!("{:?}", built.status) + }; + + let handle = args.name.unwrap_or_else(|| built.id.clone()); + if args.json { + let out = serde_json::json!({ + "id": built.id, + "status": status, + "environmentId": environment_id, + "name": handle, + }); + println!("{}", serde_json::to_string_pretty(&out)?); + return Ok(()); + } + + if already_ready { + println!("✓ Template {handle} ready (cached)"); + } else if status == "READY" { + println!("✓ Template {handle} built"); + } else { + println!("Template {handle} status: {status}"); + println!("\nCheck progress with:\n railway sandbox template status {handle}"); + } + if status == "READY" { + println!("\nCreate a sandbox from it with:\n railway sandbox create --template {handle}"); + } + Ok(()) +} + +async fn template_status( + configs: &mut Configs, + client: &reqwest::Client, + project: Option, + environment: Option, + args: TemplateStatusArgs, +) -> Result<()> { + // A locally stored template knows its environment; a raw id falls back to + // flags / the linked environment. + let stored = configs.find_sandbox_template(&args.template, None); + let (id, environment_id) = match &stored { + Some(t) => (t.id.clone(), t.environment_id.clone()), + None => { + let (_, environment_id) = + resolve_project_and_env(configs, client, project, environment).await?; + (args.template.clone(), environment_id) + } + }; + + let res = post_graphql::( + client, + configs.get_backboard(), + queries::sandbox_template::Variables { environment_id, id }, + ) + .await?; + let tpl = res.sandbox_template; + + if args.json { + println!("{}", serde_json::to_string_pretty(&tpl)?); + return Ok(()); + } + if let Some(name) = stored.and_then(|t| t.name) { + println!("Template {name} ({})", tpl.id); + } else { + println!("Template {}", tpl.id); + } + println!(" status: {:?}", tpl.status); + Ok(()) +} + +async fn template_list( + configs: &mut Configs, + client: &reqwest::Client, + project: Option, + environment: Option, + args: TemplateListArgs, +) -> Result<()> { + let (_, environment_id) = + resolve_project_and_env(configs, client, project, environment).await?; + let templates = configs.list_sandbox_templates(Some(&environment_id)); + + if templates.is_empty() { + if args.json { + println!("[]"); + } else { + println!( + "No templates built from this CLI for this environment.\nBuild one with:\n railway sandbox template build --name -c '' --wait" + ); + } + return Ok(()); + } + + let mut rows = Vec::new(); + for t in &templates { + let status = post_graphql::( + client, + configs.get_backboard(), + queries::sandbox_template::Variables { + environment_id: environment_id.clone(), + id: t.id.clone(), + }, + ) + .await + .map(|r| format!("{:?}", r.sandbox_template.status)) + .unwrap_or_else(|_| "UNKNOWN".to_string()); + rows.push((t, status)); + } + + if args.json { + let out: Vec<_> = rows + .iter() + .map(|(t, status)| { + serde_json::json!({ + "id": t.id, + "name": t.name, + "status": status, + "instructions": t.instructions, + "baseImageDigest": t.base_image_digest, + "createdAt": t.created_at, + }) + }) + .collect(); + println!("{}", serde_json::to_string_pretty(&out)?); + return Ok(()); + } + + println!( + "{:<20} {:<16} {:<10} {:<6}", + "NAME", "ID", "STATUS", "STEPS" + ); + for (t, status) in rows { + println!( + "{:<20} {:<16} {:<10} {:<6}", + t.name.as_deref().unwrap_or("-"), + &t.id[..t.id.len().min(16)], + status, + t.instructions.len() + ); + } + Ok(()) +} + +/// Poll the template status until READY (or fail on FAILED/timeout). Build +/// steps run server-side in a transient sandbox; the workflow caps out at 40m, +/// so poll a little past that. +async fn wait_for_template( + client: &reqwest::Client, + configs: &Configs, + environment_id: &str, + id: &str, +) -> Result { + let mut spinner = create_shimmer_spinner("Building template"); + let deadline = std::time::Instant::now() + Duration::from_secs(45 * 60); + loop { + tokio::time::sleep(Duration::from_secs(5)).await; + let res = post_graphql::( + client, + configs.get_backboard(), + queries::sandbox_template::Variables { + environment_id: environment_id.to_string(), + id: id.to_string(), + }, + ) + .await?; + match res.sandbox_template.status { + queries::sandbox_template::SandboxTemplateStatus::READY => { + spinner.finish_and_clear(); + return Ok("READY".to_string()); + } + queries::sandbox_template::SandboxTemplateStatus::FAILED => { + fail_spinner(&mut spinner, "Template build failed".to_string()); + bail!( + "Template build failed. Each instruction must exit 0 within 10 minutes; fix the failing step and rebuild." + ); + } + _ => {} + } + if std::time::Instant::now() > deadline { + fail_spinner(&mut spinner, "Timed out waiting for template".to_string()); + bail!("Timed out waiting for the template build."); + } + } +} + +async fn fork( + configs: &mut Configs, + client: &reqwest::Client, + project: Option, + environment: Option, + args: ForkArgs, +) -> Result<()> { + let (source_sandbox_id, environment_id) = resolve_target( + configs, + client, + args.explicit_id(), + project.clone(), + environment.clone(), + ) + .await?; + + // For the stored ref: prefer the source's cached project_id, else resolve + // from flags / the linked project. + let project_id = match configs + .get_sandbox(&source_sandbox_id) + .and_then(|s| s.project_id) + { + Some(id) => id, + None => { + resolve_project_and_env(configs, client, project, environment) + .await? + .0 + } + }; + + let input = mutations::sandbox_create::SandboxCreateInput { + environment_id: environment_id.clone(), + idle_timeout_minutes: args.idle_timeout_minutes, + template: None, + source_sandbox_id: Some(source_sandbox_id), + network_isolation: args + .private_network + .then_some(mutations::sandbox_create::SandboxNetworkIsolation::PRIVATE), + variables: variables_to_input(&args.env_files, &args.variables)?, + }; + create_and_store( + configs, + client, + project_id, + environment_id, + input, + args.json, + true, + ) + .await +} + async fn list( configs: &mut Configs, client: &reqwest::Client, @@ -534,15 +1159,27 @@ async fn ssh( environment: Option, args: SshArgs, ) -> Result<()> { - let (sandbox_id, environment_id) = - resolve_target(configs, client, args.id.clone(), project, environment).await?; + // Stage-tagged failure telemetry, mirroring `railway ssh` (tel.rs) so + // sandbox SSH sessions land in the same stage-failure dashboards under + // command = "sandbox". + let (sandbox_id, environment_id) = tel::track_for( + "sandbox", + "ssh_resolve_target", + resolve_target(configs, client, args.id.clone(), project, environment).await, + ) + .await?; // Reuse the native-SSH key registration flow from `railway ssh`. When the // user didn't pass `-i`, use the registered key it resolves so a // non-default-named key (e.g. ~/.ssh/raildesk_railway_ed25519) is actually // offered to the relay instead of just ssh's default identities. let auto_identity = if args.identity_file.is_none() { - ensure_ssh_key(client, configs).await? + tel::track_for( + "sandbox", + "ssh_key_setup", + ensure_ssh_key(client, configs).await, + ) + .await? } else { None }; @@ -551,7 +1188,7 @@ async fn ssh( configs.write()?; // Relay target format (per backboard): sbx::. - // `run_native_ssh` appends `@ssh.railway.com` internally. + // `run_native_ssh` appends the environment's relay host internally. let target = format!("sbx:{environment_id}:{sandbox_id}"); // Keep the sandbox alive for the duration of the session. @@ -571,14 +1208,23 @@ async fn ssh( // `run_native_ssh` is blocking (inherits the terminal); run it off the // async runtime so the heartbeat task keeps ticking. - let exit_code = tokio::task::spawn_blocking(move || { + let session = tokio::task::spawn_blocking(move || { run_native_ssh(&target, command.as_deref(), identity.as_deref()) }) - .await??; + .await + .map_err(anyhow::Error::from) + .and_then(|r| r); + let exit_code = tel::track_for("sandbox", "ssh_session", session).await?; heartbeat.abort(); if exit_code != 0 { + tel::report_failure_for( + "sandbox", + "ssh_exit_nonzero", + &format!("ssh exited with code {exit_code}"), + ) + .await; std::process::exit(exit_code); } Ok(()) @@ -608,3 +1254,193 @@ fn spawn_heartbeat( } }) } + +#[cfg(test)] +mod tests { + use super::*; + + fn args(list: &[&str]) -> Vec { + list.iter().map(|s| s.to_string()).collect() + } + + #[test] + fn parse_single_pair() { + let vars = parse_variable_args(&args(&["FOO=bar"])).unwrap(); + assert_eq!(vars.len(), 1); + assert_eq!(vars[0].key, "FOO"); + assert_eq!(vars[0].value, "bar"); + } + + #[test] + fn parse_comma_separated_pairs() { + let vars = parse_variable_args(&args(&["FOO=bar,BAZ=qux,N=1"])).unwrap(); + assert_eq!( + vars.iter() + .map(|v| (v.key.as_str(), v.value.as_str())) + .collect::>(), + vec![("FOO", "bar"), ("BAZ", "qux"), ("N", "1")] + ); + } + + #[test] + fn comma_in_value_stays_single_pair() { + // "b.com" has no '=', so the comma is part of the value, not a separator. + let vars = parse_variable_args(&args(&["ALLOWED=a.com,b.com"])).unwrap(); + assert_eq!(vars.len(), 1); + assert_eq!(vars[0].key, "ALLOWED"); + assert_eq!(vars[0].value, "a.com,b.com"); + } + + #[test] + fn repeated_flags_accumulate() { + let vars = parse_variable_args(&args(&["A=1", "B=2,C=3"])).unwrap(); + assert_eq!(vars.len(), 3); + } + + #[test] + fn invalid_pair_errors() { + assert!(parse_variable_args(&args(&["NOVALUE"])).is_err()); + assert!(parse_variable_args(&args(&["FOO=bar,NOVALUE=,BAZ=qux"])).is_err()); + } + + #[test] + fn wraps_bare_references() { + assert_eq!( + auto_wrap_reference("postgres.DATABASE_URL"), + "${{postgres.DATABASE_URL}}" + ); + assert_eq!(auto_wrap_reference("shared.FOO"), "${{shared.FOO}}"); + assert_eq!( + auto_wrap_reference("my-api_2.PORT_8080"), + "${{my-api_2.PORT_8080}}" + ); + } + + #[test] + fn leaves_plain_values_alone() { + for v in ["bar", "1.5", "example.com", "file.txt", "a.b.C", "2.0.1"] { + assert_eq!(auto_wrap_reference(v), v); + } + } + + #[test] + fn leaves_existing_references_alone() { + let full = "${{postgres.DATABASE_URL}}"; + assert_eq!(auto_wrap_reference(full), full); + let embedded = "postgres://${{postgres.PGUSER}}@host"; + assert_eq!(auto_wrap_reference(embedded), embedded); + } + + #[test] + fn variables_to_input_wraps_and_collects() { + let input = variables_to_input(&[], &args(&["DB=postgres.DATABASE_URL,FOO=bar"])) + .unwrap() + .unwrap(); + assert_eq!( + input.get("DB").map(String::as_str), + Some("${{postgres.DATABASE_URL}}") + ); + assert_eq!(input.get("FOO").map(String::as_str), Some("bar")); + } + + #[test] + fn variables_to_input_empty_is_none() { + assert!(variables_to_input(&[], &[]).unwrap().is_none()); + } + + #[test] + fn manually_wrapped_pairs_split_and_pass_verbatim() { + // Users may pre-wrap references themselves; comma-splitting still + // applies and the wrapped values are sent untouched. + let input = variables_to_input( + &[], + &args(&["FOO=${{serviceName.FOO}},BAR=${{serviceName.BAR}}"]), + ) + .unwrap() + .unwrap(); + assert_eq!( + input.get("FOO").map(String::as_str), + Some("${{serviceName.FOO}}") + ); + assert_eq!( + input.get("BAR").map(String::as_str), + Some("${{serviceName.BAR}}") + ); + // Embedded references inside larger values also pass through. + let input = variables_to_input(&[], &args(&["URL=http://${{svc.HOST}}:8080"])) + .unwrap() + .unwrap(); + assert_eq!( + input.get("URL").map(String::as_str), + Some("http://${{svc.HOST}}:8080") + ); + } + + #[test] + fn wraps_shared_refs_any_case() { + assert_eq!(auto_wrap_reference("shared.char"), "${{shared.char}}"); + assert_eq!(auto_wrap_reference("shared.FOO"), "${{shared.FOO}}"); + // Other namespaces still require UPPER_SNAKE vars. + assert_eq!(auto_wrap_reference("postgres.char"), "postgres.char"); + } + + fn write_temp_env(name: &str, contents: &str) -> std::path::PathBuf { + let path = std::env::temp_dir().join(format!("railway-test-{}-{name}", std::process::id())); + std::fs::write(&path, contents).unwrap(); + path + } + + #[test] + fn env_file_parses_dotenv_format() { + let path = write_temp_env( + "basic.env", + "# comment\n\nFOO=bar\nexport BAZ=qux\nQUOTED=\"hello world\"\nSINGLE='a # not comment'\nTRAIL=value # comment\nREF=postgres.DATABASE_URL\n", + ); + let vars = parse_env_file(&path).unwrap(); + std::fs::remove_file(&path).ok(); + let map: BTreeMap<_, _> = vars.into_iter().map(|v| (v.key, v.value)).collect(); + assert_eq!(map.get("FOO").map(String::as_str), Some("bar")); + assert_eq!(map.get("BAZ").map(String::as_str), Some("qux")); + assert_eq!(map.get("QUOTED").map(String::as_str), Some("hello world")); + assert_eq!( + map.get("SINGLE").map(String::as_str), + Some("a # not comment") + ); + assert_eq!(map.get("TRAIL").map(String::as_str), Some("value")); + assert_eq!( + map.get("REF").map(String::as_str), + Some("postgres.DATABASE_URL") + ); + } + + #[test] + fn env_file_invalid_line_errors_with_location() { + let path = write_temp_env("bad.env", "FOO=bar\nNOT A PAIR\n"); + let err = parse_env_file(&path).unwrap_err().to_string(); + std::fs::remove_file(&path).ok(); + assert!(err.contains(":2:"), "error should cite line 2: {err}"); + } + + #[test] + fn env_file_missing_errors() { + assert!(parse_env_file(std::path::Path::new("/nonexistent/x.env")).is_err()); + } + + #[test] + fn flags_override_env_file_entries() { + let path = write_temp_env("override.env", "FOO=from-file\nKEEP=file-value\n"); + let input = variables_to_input( + std::slice::from_ref(&path), + &args(&["FOO=from-flag,REF=shared.char"]), + ) + .unwrap() + .unwrap(); + std::fs::remove_file(&path).ok(); + assert_eq!(input.get("FOO").map(String::as_str), Some("from-flag")); + assert_eq!(input.get("KEEP").map(String::as_str), Some("file-value")); + assert_eq!( + input.get("REF").map(String::as_str), + Some("${{shared.char}}") + ); + } +} diff --git a/src/commands/ssh/config.rs b/src/commands/ssh/config.rs index 0855bfeaa..4acefeb7f 100644 --- a/src/commands/ssh/config.rs +++ b/src/commands/ssh/config.rs @@ -272,8 +272,12 @@ fn render_config_block( writeln!(block, "# BEGIN railway:{rendered_marker}").expect("writing to String cannot fail"); writeln!(block, "# Railway service: {rendered_service_name}") .expect("writing to String cannot fail"); + let (relay_host, relay_port) = native::ssh_relay(); writeln!(block, "Host {alias}").expect("writing to String cannot fail"); - writeln!(block, " HostName {}", native::SSH_HOST).expect("writing to String cannot fail"); + writeln!(block, " HostName {relay_host}").expect("writing to String cannot fail"); + if let Some(port) = relay_port { + writeln!(block, " Port {port}").expect("writing to String cannot fail"); + } writeln!(block, " User {service_instance_id}").expect("writing to String cannot fail"); if let Some(identity_file) = identity_file { diff --git a/src/commands/ssh/mod.rs b/src/commands/ssh/mod.rs index a7f24d952..ad4e3d0cf 100644 --- a/src/commands/ssh/mod.rs +++ b/src/commands/ssh/mod.rs @@ -9,12 +9,13 @@ mod common; mod config; mod keys; mod native; -mod tel; +// `pub(crate)` so `sandbox ssh` can emit the same stage-failure telemetry. +pub(crate) mod tel; use common::*; // Re-exported for the `sandbox` command, which reuses the same native SSH -// transport (key registration + `ssh @ssh.railway.com`). +// transport (key registration + `ssh @`). pub use native::{ensure_ssh_key, run_native_ssh}; /// Connect to a service via SSH or manage SSH keys diff --git a/src/commands/ssh/native.rs b/src/commands/ssh/native.rs index df4115e92..5f684cc42 100644 --- a/src/commands/ssh/native.rs +++ b/src/commands/ssh/native.rs @@ -12,7 +12,22 @@ use crate::controllers::ssh::keys::{SshKeySource, find_local_ssh_keys, register_ use crate::gql::queries::{ServiceInstance, service_instance}; use crate::util::prompt::{prompt_confirm_with_default, prompt_select}; -pub(super) const SSH_HOST: &str = "ssh.railway.com"; +/// SSH relay endpoint (host, non-default port) for the current environment — +/// must track `Configs::get_backboard()`'s environment, or key registration +/// is checked against one backboard while the relay authenticates against +/// another (dev-mode CLIs used to dial the prod relay and get publickey +/// denials for keys that were registered fine). +pub(super) fn ssh_relay() -> (&'static str, Option) { + Configs::get_ssh_relay() +} + +/// Append `-p ` when the relay listens on a non-default port (the +/// develop relay uses 2222). +fn apply_relay_port(cmd: &mut Command, port: Option) { + if let Some(port) = port { + cmd.args(["-p", &port.to_string()]); + } +} /// Get the service instance ID for a service in an environment pub async fn get_service_instance_id( @@ -149,10 +164,12 @@ fn identity_for(key: &crate::controllers::ssh::keys::LocalSshKey) -> Option) -> Result<()> { - let target = format!("{}@{}", ssh_target, SSH_HOST); + let (host, port) = ssh_relay(); + let target = format!("{ssh_target}@{host}"); eprintln!("Ensuring tmux is installed..."); let mut install_cmd = Command::new("ssh"); + apply_relay_port(&mut install_cmd, port); if let Some(key) = identity_file { install_cmd.arg("-i").arg(key); } @@ -183,7 +200,8 @@ pub fn run_tmux_session( session_name: &str, identity_file: Option<&Path>, ) -> Result<()> { - let target = format!("{}@{}", ssh_target, SSH_HOST); + let (host, port) = ssh_relay(); + let target = format!("{ssh_target}@{host}"); let tmux_cmd = format!( "exec tmux new-session -A -s {} \\; set -g mouse on", session_name @@ -191,6 +209,7 @@ pub fn run_tmux_session( loop { let mut session_cmd = Command::new("ssh"); + apply_relay_port(&mut session_cmd, port); if let Some(key) = identity_file { session_cmd.arg("-i").arg(key); } @@ -228,11 +247,13 @@ pub fn run_native_ssh( command: Option<&[String]>, identity_file: Option<&Path>, ) -> Result { - let target = format!("{}@{}", service_instance_id, SSH_HOST); + let (host, port) = ssh_relay(); + let target = format!("{service_instance_id}@{host}"); let stdin_tty = std::io::stdin().is_terminal(); let stdout_tty = std::io::stdout().is_terminal(); let mut ssh_cmd = Command::new("ssh"); + apply_relay_port(&mut ssh_cmd, port); if let Some(key) = identity_file { ssh_cmd.arg("-i").arg(key); diff --git a/src/commands/ssh/tel.rs b/src/commands/ssh/tel.rs index 46bbe18a1..94376771c 100644 --- a/src/commands/ssh/tel.rs +++ b/src/commands/ssh/tel.rs @@ -8,13 +8,20 @@ use crate::telemetry::{self, CliTrackEvent}; /// *which stage* failed. Use lowercase_snake_case stage names; they appear /// in telemetry as `sub_command = "stage__failed"`. pub async fn report_failure(stage: &str, message: &str) { + report_failure_for("ssh", stage, message).await; +} + +/// Like [`report_failure`] but under an arbitrary command namespace, so other +/// SSH-backed commands (e.g. `sandbox ssh`) land in the same stage-failure +/// dashboards with their own command tag. +pub async fn report_failure_for(command: &str, stage: &str, message: &str) { let mut truncated = message.to_string(); if truncated.len() > 256 { truncated.truncate(256); } telemetry::send(CliTrackEvent { - command: "ssh".to_string(), + command: command.to_string(), sub_command: Some(format!("stage_{stage}_failed")), success: false, error_message: Some(truncated), @@ -31,8 +38,17 @@ pub async fn report_failure(stage: &str, message: &str) { /// unchanged. Intended to wrap each step of an SSH flow so failures are /// categorized without replacing the existing `?`-propagation. pub async fn track(stage: &str, result: anyhow::Result) -> anyhow::Result { + track_for("ssh", stage, result).await +} + +/// [`track`] under an arbitrary command namespace. +pub async fn track_for( + command: &str, + stage: &str, + result: anyhow::Result, +) -> anyhow::Result { if let Err(ref e) = result { - report_failure(stage, &format!("{e}")).await; + report_failure_for(command, stage, &format!("{e}")).await; } result } diff --git a/src/commands/volume/sftp.rs b/src/commands/volume/sftp.rs index 5d8753613..b565998ee 100644 --- a/src/commands/volume/sftp.rs +++ b/src/commands/volume/sftp.rs @@ -138,7 +138,13 @@ impl VolumeSftpHandler { } } -const ADDR: &str = "ssh.railway.com"; +/// SSH relay endpoint for the current environment (host, port). Tracks the +/// same env switching as `Configs::get_backboard()`; the develop relay +/// listens on 2222. +fn relay_addr() -> (&'static str, u16) { + let (host, port) = crate::config::Configs::get_ssh_relay(); + (host, port.unwrap_or(22)) +} pub(crate) const DEFAULT_TRANSFER_CONCURRENCY: usize = 32; const DOWNLOAD_TRANSFER_BUFFER_SIZE: usize = 2 * 1024 * 1024; const DIRECTORY_UPLOAD_TRANSFER_BUFFER_SIZE: usize = 2 * 1024 * 1024; @@ -197,13 +203,14 @@ impl VolumeSftp { if self.session.is_none() || self.is_disconnected() { self.disconnected.store(false, Ordering::SeqCst); + let (relay_host, relay_port) = relay_addr(); let mut session = russh::client::connect( Arc::new(russh::client::Config::default()), - (ADDR, 22), + (relay_host, relay_port), VolumeSftpHandler::new(Arc::clone(&self.disconnected)), ) .await - .with_context(|| format!("Failed to connect to Railway SFTP at {ADDR}"))?; + .with_context(|| format!("Failed to connect to Railway SFTP at {relay_host}"))?; crate::controllers::ssh::authenticate(&mut session, &self.service_instance_id).await?; diff --git a/src/config.rs b/src/config.rs index 7e7c60a26..e8a3ecd10 100644 --- a/src/config.rs +++ b/src/config.rs @@ -64,6 +64,25 @@ pub struct StoredSandbox { pub created_at: Option, } +/// A sandbox template recipe the CLI has built. Templates are +/// content-addressed server-side (the id is a hash of the recipe) and +/// `sandboxCreate` needs the full recipe — not just the id — so the CLI keeps +/// the instructions locally to make `railway sandbox create --template ` +/// possible. +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde_with::skip_serializing_none] +#[serde(rename_all = "camelCase")] +pub struct StoredSandboxTemplate { + /// Server-side template id (sha256 of the recipe). + pub id: String, + /// Optional local-only name for friendlier lookup. + pub name: Option, + pub environment_id: String, + pub instructions: Vec, + pub base_image_digest: Option, + pub created_at: Option, +} + #[derive(Serialize, Deserialize, Debug, Default)] #[serde_with::skip_serializing_none] #[serde(rename_all = "camelCase")] @@ -78,6 +97,9 @@ pub struct RailwayConfig { /// The most recently created/used sandbox; the default target for /// `railway sandbox ssh` when no id is given. pub active_sandbox: Option, + /// Sandbox template recipes the CLI has built (id is server-side hash; + /// instructions kept locally because sandboxCreate needs the full recipe). + pub sandbox_templates: Option>, } #[derive(Debug)] @@ -255,6 +277,17 @@ impl Configs { format!("https://backboard.{}/graphql/v2", self.get_host()) } + /// SSH relay host and non-default port for the current environment. + /// Mirrors backboard's `controllers/ssh` mapping: only the develop relay + /// is separate (and listens on 2222); staging falls through to the + /// production relay, same as backboard's IS_DEV-only branch. + pub fn get_ssh_relay() -> (&'static str, Option) { + match Self::get_environment_id() { + Environment::Dev => ("ssh.railway-develop.com", Some(2222)), + Environment::Production | Environment::Staging => ("ssh.railway.com", None), + } + } + pub fn get_current_directory(&self) -> Result { let current_dir = std::env::current_dir()?; let path = current_dir @@ -446,6 +479,76 @@ impl Configs { } } + /// Record a sandbox template recipe (upsert by template id within the same + /// environment). When a name is given, any other template in the + /// environment holding that name loses it — names are unique handles. + /// Caller persists with `write()`. + pub fn upsert_sandbox_template(&mut self, template: StoredSandboxTemplate) { + let templates = self + .root_config + .sandbox_templates + .get_or_insert_with(Vec::new); + if let Some(name) = &template.name { + for other in templates.iter_mut() { + if other.environment_id == template.environment_id + && other.id != template.id + && other.name.as_deref() == Some(name) + { + other.name = None; + } + } + } + match templates + .iter_mut() + .find(|t| t.id == template.id && t.environment_id == template.environment_id) + { + Some(existing) => *existing = template, + None => templates.push(template), + } + } + + /// Look up a stored template by local name or id (exact or unambiguous id + /// prefix), optionally scoped to an environment. + pub fn find_sandbox_template( + &self, + name_or_id: &str, + environment_id: Option<&str>, + ) -> Option { + let templates = self.root_config.sandbox_templates.as_ref()?; + let in_env = + |t: &&StoredSandboxTemplate| environment_id.is_none_or(|env| t.environment_id == env); + if let Some(t) = templates + .iter() + .filter(in_env) + .find(|t| t.name.as_deref() == Some(name_or_id)) + { + return Some(t.clone()); + } + let mut matches = templates + .iter() + .filter(in_env) + .filter(|t| t.id.starts_with(name_or_id)); + match (matches.next(), matches.next()) { + (Some(t), None) => Some(t.clone()), + _ => None, + } + } + + /// All stored templates, optionally scoped to an environment. + pub fn list_sandbox_templates( + &self, + environment_id: Option<&str>, + ) -> Vec { + self.root_config + .sandbox_templates + .as_deref() + .unwrap_or_default() + .iter() + .filter(|t| environment_id.is_none_or(|env| t.environment_id == env)) + .cloned() + .collect() + } + pub fn link_service(&mut self, service_id: String) -> Result<()> { let linked_project = self.get_linked_project_mut()?; linked_project.service = Some(service_id); diff --git a/src/controllers/variables.rs b/src/controllers/variables.rs index f540feb78..056729f5d 100644 --- a/src/controllers/variables.rs +++ b/src/controllers/variables.rs @@ -41,7 +41,7 @@ pub async fn get_service_variables( Ok(variables) } -#[derive(Clone, Default)] +#[derive(Clone, Debug, Default)] pub struct Variable { pub key: String, pub value: String, diff --git a/src/gql/mutations/mod.rs b/src/gql/mutations/mod.rs index 7844b3117..189b4ded7 100644 --- a/src/gql/mutations/mod.rs +++ b/src/gql/mutations/mod.rs @@ -341,6 +341,15 @@ pub struct SshPublicKeyDelete; )] pub struct SandboxCreate; +#[derive(GraphQLQuery)] +#[graphql( + schema_path = "src/gql/schema.json", + query_path = "src/gql/mutations/strings/SandboxTemplateBuild.graphql", + response_derives = "Debug, Serialize, Clone", + skip_serializing_none +)] +pub struct SandboxTemplateBuild; + #[derive(GraphQLQuery)] #[graphql( schema_path = "src/gql/schema.json", diff --git a/src/gql/mutations/strings/SandboxTemplateBuild.graphql b/src/gql/mutations/strings/SandboxTemplateBuild.graphql new file mode 100644 index 000000000..73b877a7a --- /dev/null +++ b/src/gql/mutations/strings/SandboxTemplateBuild.graphql @@ -0,0 +1,10 @@ +mutation SandboxTemplateBuild( + $environmentId: String! + $input: SandboxTemplateInput! +) { + sandboxTemplateBuild(environmentId: $environmentId, input: $input) { + id + status + environmentId + } +} diff --git a/src/gql/queries/mod.rs b/src/gql/queries/mod.rs index f8dd673ad..5094ed59a 100644 --- a/src/gql/queries/mod.rs +++ b/src/gql/queries/mod.rs @@ -270,6 +270,15 @@ pub struct SshPublicKeys; )] pub struct Sandboxes; +#[derive(GraphQLQuery)] +#[graphql( + schema_path = "src/gql/schema.json", + query_path = "src/gql/queries/strings/SandboxTemplate.graphql", + response_derives = "Debug, Serialize, Clone", + skip_serializing_none +)] +pub struct SandboxTemplate; + #[derive(GraphQLQuery)] #[graphql( schema_path = "src/gql/schema.json", diff --git a/src/gql/queries/strings/SandboxTemplate.graphql b/src/gql/queries/strings/SandboxTemplate.graphql new file mode 100644 index 000000000..9065e502b --- /dev/null +++ b/src/gql/queries/strings/SandboxTemplate.graphql @@ -0,0 +1,7 @@ +query SandboxTemplate($environmentId: String!, $id: ID!) { + sandboxTemplate(environmentId: $environmentId, id: $id) { + id + status + environmentId + } +} diff --git a/src/gql/schema.json b/src/gql/schema.json index 0813a39cb..44c7ca679 100644 --- a/src/gql/schema.json +++ b/src/gql/schema.json @@ -146,25 +146,19 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "AUDIT_LOGS" + "name": "CHAT_SANDBOX" }, { "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "BUCKET_FILE_BROWSER" + "name": "DEBUG_SMART_DIAGNOSIS" }, { "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "CDN_CACHING" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "DEBUG_SMART_DIAGNOSIS" + "name": "EDGE_CONFIG" }, { "deprecationReason": null, @@ -182,13 +176,13 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "POSTGRES_HA" + "name": "POSTGRES_PGBOUNCER" }, { "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "POSTGRES_PGBOUNCER" + "name": "POSTGRES_PITR" }, { "deprecationReason": null, @@ -200,13 +194,13 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "SMART_DIAGNOSIS" + "name": "PROJECT_SANDBOXES" }, { "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "UNLIMITED_SMART_DIAGNOSIS" + "name": "TCPIP_DATABASE_CLIENT" } ], "fields": null, @@ -223,49 +217,13 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "AGENT_USAGE_BILLING" + "name": "BAN_APPEAL_FORM" }, { "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "ALLOW_REPLICA_METRICS" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "ARCHIVER_V2_ROLLOUT" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "BUILDER_V3_ROLLOUT_EXISTING_SERVICES" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "BUILDER_V3_ROLLOUT_EXISTING_SERVICES_PRO" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "BUILDER_V3_ROLLOUT_NEW_SERVICES" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "BUILDER_V3_ROLLOUT_NEW_SERVICES_PRO" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "COMPARE_CLICKHOUSE_METRICS" + "name": "CHAT_SANDBOX" }, { "deprecationReason": null, @@ -283,19 +241,7 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "DISABLE_OAUTH_ACCESS_TOKENS" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "FOCUSED_PR_ENVIRONMENTS" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "GITHUB_PERMISSION_NOTIFICATION_ROLLOUT" + "name": "HA_STATIC_EGRESS_SELF_SERVICE" }, { "deprecationReason": null, @@ -327,12 +273,6 @@ "isDeprecated": false, "name": "OAUTH_DCR_KILLSWITCH" }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "OAUTH_DEVICE_FLOW_KILLSWITCH" - }, { "deprecationReason": null, "description": null, @@ -367,37 +307,13 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "UNIFIED_SNAPSHOT_AND_BUILD" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "UNIFIED_SNAPSHOT_AND_BUILD_HOBBY" + "name": "TCPIP_DATABASE_CLIENT" }, { "deprecationReason": null, "description": null, "isDeprecated": false, "name": "UPDATED_VM_QUERIES" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "USE_CLICKHOUSE_METRICS" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "USE_GH_WEBHOOKS_FOR_CHANGE_DETECTION" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "VM_TIME_RANGE_QUERY" } ], "fields": null, @@ -455,13 +371,13 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "USE_BUILDER_V3_FOR_CLI_DEPLOYS" + "name": "USE_EXPRESS_DEPLOY" }, { "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "USE_GH_WEBHOOKS_FOR_CHANGE_DETECTION" + "name": "USE_HA_STATIC_EGRESS" }, { "deprecationReason": null, @@ -2401,11 +2317,266 @@ "name": "CertificateStatusDetailed", "possibleTypes": null }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "kind", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "outputs", + "type": { + "kind": "SCALAR", + "name": "JSON", + "ofType": null + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "path", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "status", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "summary", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "ChangeOperationResult", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "changes", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "ChangeOperationResult", + "ofType": null + } + } + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "deploymentId", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "diagnostics", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "JSON", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "id", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "stagedPatchId", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "status", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "ChangeSetApplyResult", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "changeSet", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "JSON", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "diagnostics", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "JSON", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "effects", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "JSON", + "ofType": null + } + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "ChangeSetPreview", + "possibleTypes": null + }, { "description": null, "enumValues": null, "fields": null, "inputFields": [ + { + "defaultValue": null, + "description": null, + "name": "agentSessionId", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, { "defaultValue": null, "description": null, @@ -2420,6 +2591,16 @@ } } }, + { + "defaultValue": null, + "description": null, + "name": "caller", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, { "defaultValue": null, "description": null, @@ -2462,6 +2643,26 @@ } } }, + { + "defaultValue": null, + "description": null, + "name": "environmentId", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "errorClass", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, { "defaultValue": null, "description": null, @@ -2472,6 +2673,16 @@ "ofType": null } }, + { + "defaultValue": null, + "description": null, + "name": "installRequestId", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, { "defaultValue": null, "description": null, @@ -2500,6 +2711,36 @@ } } }, + { + "defaultValue": null, + "description": null, + "name": "projectId", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "serviceId", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "sessionId", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, { "defaultValue": null, "description": null, @@ -2523,6 +2764,16 @@ "ofType": null } } + }, + { + "defaultValue": null, + "description": null, + "name": "workspaceId", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } } ], "interfaces": null, @@ -3091,9 +3342,9 @@ "fields": [ { "args": [], - "deprecationReason": null, + "deprecationReason": "Removed; always null.", "description": null, - "isDeprecated": false, + "isDeprecated": true, "name": "cdnMode", "type": { "kind": "SCALAR", @@ -6686,6 +6937,45 @@ "name": "DeploymentTriggerUpdateInput", "possibleTypes": null }, + { + "description": null, + "enumValues": null, + "fields": null, + "inputFields": [ + { + "defaultValue": null, + "description": null, + "name": "environmentId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "serviceId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "interfaces": null, + "kind": "INPUT_OBJECT", + "name": "DisableServiceCdnInput", + "possibleTypes": null + }, { "description": null, "enumValues": null, @@ -6749,9 +7039,9 @@ "fields": [ { "args": [], - "deprecationReason": null, + "deprecationReason": "Removed; always null.", "description": null, - "isDeprecated": false, + "isDeprecated": true, "name": "cdnMode", "type": { "kind": "SCALAR", @@ -7108,7 +7398,23 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "ipv4", + "name": "defaultTtlSeconds", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "htmlCaching", "type": { "kind": "NON_NULL", "name": null, @@ -7124,7 +7430,7 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "region", + "name": "mode", "type": { "kind": "NON_NULL", "name": null, @@ -7134,12 +7440,44 @@ "ofType": null } } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "purgeOnDeploy", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "PurgeOnDeploy", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "staleWhileRevalidate", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "StaleWhileRevalidateConfig", + "ofType": null + } + } } ], "inputFields": null, "interfaces": [], "kind": "OBJECT", - "name": "EgressGateway", + "name": "EdgeCachingConfig", "possibleTypes": null }, { @@ -7150,7 +7488,97 @@ { "defaultValue": null, "description": null, - "name": "environmentId", + "name": "defaultTtlSeconds", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "htmlCaching", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "mode", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "purgeOnDeploy", + "type": { + "kind": "ENUM", + "name": "PurgeOnDeploy", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "staleWhileRevalidate", + "type": { + "kind": "INPUT_OBJECT", + "name": "StaleWhileRevalidateInput", + "ofType": null + } + } + ], + "interfaces": null, + "kind": "INPUT_OBJECT", + "name": "EdgeCachingConfigInput", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "caching", + "type": { + "kind": "OBJECT", + "name": "EdgeCachingConfig", + "ofType": null + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "enabled", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "id", "type": { "kind": "NON_NULL", "name": null, @@ -7161,20 +7589,92 @@ } } }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "purgeEpoch", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "purgeEpochByKind", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "JSON", + "ofType": null + } + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "EdgeConfig", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": null, + "inputFields": [ { "defaultValue": null, "description": null, - "name": "region", + "name": "caching", "type": { - "kind": "SCALAR", - "name": "String", + "kind": "INPUT_OBJECT", + "name": "EdgeCachingConfigInput", "ofType": null } + } + ], + "interfaces": null, + "kind": "INPUT_OBJECT", + "name": "EdgeConfigInput", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "ipv4", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } }, { - "defaultValue": null, + "args": [], + "deprecationReason": null, "description": null, - "name": "serviceId", + "isDeprecated": false, + "name": "region", "type": { "kind": "NON_NULL", "name": null, @@ -7184,11 +7684,73 @@ "ofType": null } } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "zone", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } } ], - "interfaces": null, - "kind": "INPUT_OBJECT", - "name": "EgressGatewayCreateInput", + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "EgressGateway", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": null, + "inputFields": [ + { + "defaultValue": null, + "description": null, + "name": "environmentId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "region", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "serviceId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "interfaces": null, + "kind": "INPUT_OBJECT", + "name": "EgressGatewayCreateInput", "possibleTypes": null }, { @@ -7230,6 +7792,55 @@ "name": "EgressGatewayServiceTargetInput", "possibleTypes": null }, + { + "description": null, + "enumValues": null, + "fields": null, + "inputFields": [ + { + "defaultValue": null, + "description": null, + "name": "config", + "type": { + "kind": "INPUT_OBJECT", + "name": "EdgeConfigInput", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "environmentId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "serviceId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "interfaces": null, + "kind": "INPUT_OBJECT", + "name": "EnableServiceCdnInput", + "possibleTypes": null + }, { "description": null, "enumValues": null, @@ -7720,7 +8331,7 @@ "possibleTypes": null }, { - "description": "\nEnvironmentConfig is a custom scalar type that represents the serializedConfig for an environment.\nJSON Schema: https://backboard.railway-develop.com/schema/environment.schema.json\n", + "description": "\nEnvironmentConfig is a custom scalar type that represents the serializedConfig for an environment.\nJSON Schema: https://backboard.railway.com/schema/environment.schema.json\n", "enumValues": null, "fields": null, "inputFields": null, @@ -8779,6 +9390,18 @@ } } }, + { + "args": [], + "deprecationReason": null, + "description": "Minimal event payload for activity feed list rendering. Avoids returning large deployment payload fields like commit messages.", + "isDeprecated": false, + "name": "activityPayload", + "type": { + "kind": "SCALAR", + "name": "JSON", + "ofType": null + } + }, { "args": [], "deprecationReason": null, @@ -9084,9 +9707,9 @@ "fields": [ { "args": [], - "deprecationReason": null, + "deprecationReason": "Deprecated regions are no longer supported.", "description": null, - "isDeprecated": false, + "isDeprecated": true, "name": "allowDeprecatedRegions", "type": { "kind": "SCALAR", @@ -11002,6 +11625,18 @@ "description": null, "enumValues": null, "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "createdAt", + "type": { + "kind": "SCALAR", + "name": "DateTime", + "ofType": null + } + }, { "args": [], "deprecationReason": null, @@ -12036,6 +12671,73 @@ "name": "LoginSessionAuthInput", "possibleTypes": null }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "avatar", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "id", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "name", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "partnerProfile", + "type": { + "kind": "OBJECT", + "name": "PartnerProfile", + "ofType": null + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "MaintainerWorkspace", + "possibleTypes": null + }, { "description": null, "enumValues": null, @@ -13571,6 +14273,37 @@ } } }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "input", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "DisableServiceCdnInput", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Disables CDN for a service, soft-deleting the edge config.", + "isDeprecated": false, + "name": "disableServiceCdn", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, { "args": [ { @@ -13710,6 +14443,84 @@ } } }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "input", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "EgressGatewayServiceTargetInput", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Rollback from HA static IPs to legacy. Creates legacy association, clears HA, and redeploys.", + "isDeprecated": false, + "name": "egressGatewayRollbackFromHA", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "EgressGateway", + "ofType": null + } + } + } + } + }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "input", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "EgressGatewayServiceTargetInput", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Upgrade static IPs from legacy to HA. Creates HA associations, clears legacy, and redeploys.", + "isDeprecated": false, + "name": "egressGatewayUpgradeToHA", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "EgressGateway", + "ofType": null + } + } + } + } + }, { "args": [ { @@ -13772,6 +14583,92 @@ } } }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "input", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "EnableServiceCdnInput", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Enables CDN for a service, creating an edge config and attaching all live domains.", + "isDeprecated": false, + "name": "enableServiceCdn", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "EdgeConfig", + "ofType": null + } + } + }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "commitMessage", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "environmentId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "input", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "JSON", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Experimental: applies an intent-level RailwayChangeSet and returns operation results.", + "isDeprecated": false, + "name": "environmentApplyChangeSet", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "ChangeSetApplyResult", + "ofType": null + } + } + }, { "args": [ { @@ -13936,6 +14833,51 @@ } } }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "environmentId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "input", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "JSON", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Experimental: previews an intent-level RailwayChangeSet without side effects.", + "isDeprecated": false, + "name": "environmentPreviewChangeSet", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "ChangeSetPreview", + "ofType": null + } + } + }, { "args": [ { @@ -14216,16 +15158,16 @@ "name": null, "ofType": { "kind": "INPUT_OBJECT", - "name": "GitHubRepoDeployInput", + "name": "ShellTokenInput", "ofType": null } } } ], "deprecationReason": null, - "description": "Deploys a GitHub repo", + "description": "Mints a 5-minute JWT for opening a browser WS session against tcp-proxy.", "isDeprecated": false, - "name": "githubRepoDeploy", + "name": "generateShellToken", "type": { "kind": "NON_NULL", "name": null, @@ -14247,22 +15189,22 @@ "name": null, "ofType": { "kind": "INPUT_OBJECT", - "name": "GitHubRepoUpdateInput", + "name": "GitHubRepoDeployInput", "ofType": null } } } ], "deprecationReason": null, - "description": "Updates a GitHub repo through the linked template", + "description": "Deploys a GitHub repo", "isDeprecated": false, - "name": "githubRepoUpdate", + "name": "githubRepoDeploy", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "Boolean", + "name": "String", "ofType": null } } @@ -14278,22 +15220,22 @@ "name": null, "ofType": { "kind": "INPUT_OBJECT", - "name": "HerokuImportVariablesInput", + "name": "GitHubRepoUpdateInput", "ofType": null } } } ], "deprecationReason": null, - "description": "Import variables from a Heroku app into a Railway service. Returns the number of variables imports", + "description": "Updates a GitHub repo through the linked template", "isDeprecated": false, - "name": "herokuImportVariables", + "name": "githubRepoUpdate", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "Int", + "name": "Boolean", "ofType": null } } @@ -14309,73 +15251,28 @@ "name": null, "ofType": { "kind": "INPUT_OBJECT", - "name": "IntegrationCreateInput", - "ofType": null - } - } - } - ], - "deprecationReason": null, - "description": "Create an integration for a project", - "isDeprecated": false, - "name": "integrationCreate", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "Integration", - "ofType": null - } - } - }, - { - "args": [ - { - "defaultValue": null, - "description": null, - "name": "id", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", + "name": "HerokuImportVariablesInput", "ofType": null } } } ], "deprecationReason": null, - "description": "Delete an integration for a project", + "description": "Import variables from a Heroku app into a Railway service. Returns the number of variables imports", "isDeprecated": false, - "name": "integrationDelete", + "name": "herokuImportVariables", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "Boolean", + "name": "Int", "ofType": null } } }, { "args": [ - { - "defaultValue": null, - "description": null, - "name": "id", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - }, { "defaultValue": null, "description": null, @@ -14385,16 +15282,16 @@ "name": null, "ofType": { "kind": "INPUT_OBJECT", - "name": "IntegrationUpdateInput", + "name": "IntegrationCreateInput", "ofType": null } } } ], "deprecationReason": null, - "description": "Update an integration for a project", + "description": "Create an integration for a project", "isDeprecated": false, - "name": "integrationUpdate", + "name": "integrationCreate", "type": { "kind": "NON_NULL", "name": null, @@ -14410,7 +15307,7 @@ { "defaultValue": null, "description": null, - "name": "code", + "name": "id", "type": { "kind": "NON_NULL", "name": null, @@ -14423,85 +15320,9 @@ } ], "deprecationReason": null, - "description": "Join a project using an invite code", - "isDeprecated": false, - "name": "inviteCodeUse", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "Project", - "ofType": null - } - } - }, - { - "args": [ - { - "defaultValue": null, - "description": null, - "name": "input", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "JobApplicationCreateInput", - "ofType": null - } - } - }, - { - "defaultValue": null, - "description": null, - "name": "resume", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Upload", - "ofType": null - } - } - } - ], - "deprecationReason": null, - "description": "Creates a new job application.", - "isDeprecated": false, - "name": "jobApplicationCreate", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - } - }, - { - "args": [ - { - "defaultValue": null, - "description": null, - "name": "input", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "LoginSessionAuthInput", - "ofType": null - } - } - } - ], - "deprecationReason": null, - "description": "Auth a login session for a user", + "description": "Delete an integration for a project", "isDeprecated": false, - "name": "loginSessionAuth", + "name": "integrationDelete", "type": { "kind": "NON_NULL", "name": null, @@ -14517,7 +15338,7 @@ { "defaultValue": null, "description": null, - "name": "code", + "name": "id", "type": { "kind": "NON_NULL", "name": null, @@ -14527,61 +15348,213 @@ "ofType": null } } - } - ], - "deprecationReason": null, - "description": "Cancel a login session", - "isDeprecated": false, - "name": "loginSessionCancel", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - } - }, - { - "args": [ + }, { "defaultValue": null, "description": null, - "name": "code", + "name": "input", "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "SCALAR", - "name": "String", + "kind": "INPUT_OBJECT", + "name": "IntegrationUpdateInput", "ofType": null } } } ], "deprecationReason": null, - "description": "Get a token for a login session if it exists", - "isDeprecated": false, - "name": "loginSessionConsume", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - { - "args": [], - "deprecationReason": null, - "description": "Start a CLI login session", + "description": "Update an integration for a project", "isDeprecated": false, - "name": "loginSessionCreate", + "name": "integrationUpdate", "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "SCALAR", - "name": "String", + "kind": "OBJECT", + "name": "Integration", + "ofType": null + } + } + }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "code", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Join a project using an invite code", + "isDeprecated": false, + "name": "inviteCodeUse", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Project", + "ofType": null + } + } + }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "input", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "JobApplicationCreateInput", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "resume", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Upload", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Creates a new job application.", + "isDeprecated": false, + "name": "jobApplicationCreate", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "input", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "LoginSessionAuthInput", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Auth a login session for a user", + "isDeprecated": false, + "name": "loginSessionAuth", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "code", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Cancel a login session", + "isDeprecated": false, + "name": "loginSessionCancel", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "code", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Get a token for a login session if it exists", + "isDeprecated": false, + "name": "loginSessionConsume", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "args": [], + "deprecationReason": null, + "description": "Start a CLI login session", + "isDeprecated": false, + "name": "loginSessionCreate", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", "ofType": null } } @@ -16287,6 +17260,37 @@ } } }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "input", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "PurgeServiceCacheInput", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Purges the CDN cache for a service. Bumps the edge config's purge epoch so every edge node treats prior cached entries as stale on next request. Idempotent; returns true even if CDN is disabled for the service.", + "isDeprecated": false, + "name": "purgeServiceCache", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, { "args": [], "deprecationReason": null, @@ -16367,20 +17371,6 @@ }, { "args": [ - { - "defaultValue": null, - "description": null, - "name": "id", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - }, { "defaultValue": null, "description": null, @@ -16390,22 +17380,22 @@ "name": null, "ofType": { "kind": "INPUT_OBJECT", - "name": "ServiceConnectInput", + "name": "SandboxCreateInput", "ofType": null } } } ], "deprecationReason": null, - "description": "Connect a service to a source", + "description": "Create a sandbox in an environment.", "isDeprecated": false, - "name": "serviceConnect", + "name": "sandboxCreate", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", - "name": "Service", + "name": "Sandbox", "ofType": null } } @@ -16415,43 +17405,16 @@ { "defaultValue": null, "description": null, - "name": "input", + "name": "environmentId", "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "INPUT_OBJECT", - "name": "ServiceCreateInput", + "kind": "SCALAR", + "name": "String", "ofType": null } } - } - ], - "deprecationReason": null, - "description": "Creates a new service.", - "isDeprecated": false, - "name": "serviceCreate", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "Service", - "ofType": null - } - } - }, - { - "args": [ - { - "defaultValue": null, - "description": "[Experimental] Environment ID. If the environment is a forked environment, the service will only be deleted in the specified environment, otherwise it will deleted in all environments that are not forks of other environments", - "name": "environmentId", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } }, { "defaultValue": null, @@ -16469,17 +17432,13 @@ } ], "deprecationReason": null, - "description": "Deletes a service.", + "description": "Destroy a sandbox.", "isDeprecated": false, - "name": "serviceDelete", + "name": "sandboxDestroy", "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } + "kind": "OBJECT", + "name": "Sandbox", + "ofType": null } }, { @@ -16487,7 +17446,7 @@ { "defaultValue": null, "description": null, - "name": "id", + "name": "command", "type": { "kind": "NON_NULL", "name": null, @@ -16497,55 +17456,21 @@ "ofType": null } } - } - ], - "deprecationReason": null, - "description": "Disconnect a service from a repo", - "isDeprecated": false, - "name": "serviceDisconnect", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "Service", - "ofType": null - } - } - }, - { - "args": [ + }, { "defaultValue": null, "description": null, - "name": "input", + "name": "environmentId", "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "INPUT_OBJECT", - "name": "ServiceDomainCreateInput", + "kind": "SCALAR", + "name": "String", "ofType": null } } - } - ], - "deprecationReason": null, - "description": "Creates a new service domain.", - "isDeprecated": false, - "name": "serviceDomainCreate", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "ServiceDomain", - "ofType": null - } - } - }, - { - "args": [ + }, { "defaultValue": null, "description": null, @@ -16559,49 +17484,355 @@ "ofType": null } } - } - ], - "deprecationReason": null, - "description": "Deletes a service domain.", - "isDeprecated": false, - "name": "serviceDomainDelete", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - } - }, - { - "args": [ + }, { "defaultValue": null, "description": null, - "name": "input", + "name": "timeoutSec", "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "ServiceDomainUpdateInput", - "ofType": null - } + "kind": "SCALAR", + "name": "Int", + "ofType": null } } ], "deprecationReason": null, - "description": "Updates a service domain.", + "description": "Execute a command inside a running sandbox.", "isDeprecated": false, - "name": "serviceDomainUpdate", + "name": "sandboxExec", "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "SCALAR", - "name": "Boolean", + "kind": "OBJECT", + "name": "SandboxExecResult", + "ofType": null + } + } + }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "environmentId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "id", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Extend a sandbox's lifetime.", + "isDeprecated": false, + "name": "sandboxHeartbeat", + "type": { + "kind": "OBJECT", + "name": "Sandbox", + "ofType": null + } + }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "environmentId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "input", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "SandboxTemplateInput", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Build a sandbox template.", + "isDeprecated": false, + "name": "sandboxTemplateBuild", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "SandboxTemplate", + "ofType": null + } + } + }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "id", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "input", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "ServiceConnectInput", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Connect a service to a source", + "isDeprecated": false, + "name": "serviceConnect", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Service", + "ofType": null + } + } + }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "input", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "ServiceCreateInput", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Creates a new service.", + "isDeprecated": false, + "name": "serviceCreate", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Service", + "ofType": null + } + } + }, + { + "args": [ + { + "defaultValue": null, + "description": "[Experimental] Environment ID. If the environment is a forked environment, the service will only be deleted in the specified environment, otherwise it will deleted in all environments that are not forks of other environments", + "name": "environmentId", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "id", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Deletes a service.", + "isDeprecated": false, + "name": "serviceDelete", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "id", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Disconnect a service from a repo", + "isDeprecated": false, + "name": "serviceDisconnect", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Service", + "ofType": null + } + } + }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "input", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "ServiceDomainCreateInput", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Creates a new service domain.", + "isDeprecated": false, + "name": "serviceDomainCreate", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "ServiceDomain", + "ofType": null + } + } + }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "id", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Deletes a service domain.", + "isDeprecated": false, + "name": "serviceDomainDelete", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "input", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "ServiceDomainUpdateInput", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Updates a service domain.", + "isDeprecated": false, + "name": "serviceDomainUpdate", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", "ofType": null } } @@ -16713,6 +17944,37 @@ } } }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "input", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "ServiceInstanceAutoDeployUpdateInput", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Enables or disables auto-deploy for a service instance.", + "isDeprecated": false, + "name": "serviceInstanceAutoDeployUpdate", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "ServiceInstanceAutoDeployUpdateResult", + "ofType": null + } + } + }, { "args": [ { @@ -17071,6 +18333,37 @@ } } }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "input", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "SetupAgentEventTrackInput", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Track setup agent lifecycle events from the Railway CLI", + "isDeprecated": false, + "name": "setupAgentEventTrack", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, { "args": [ { @@ -17120,7 +18413,7 @@ } ], "deprecationReason": null, - "description": "Creates a new SSH public key. When workspaceId is provided, the key is owned by the workspace and can be used by anyone authenticating as that workspace via native SSH; requires workspace ADMIN access. Otherwise the key is owned by the authenticated user.", + "description": "Creates a new SSH public key. When workspaceId is provided (or omitted under a workspace-scoped API token, in which case it defaults to the token's workspace), the key is owned by the workspace and can be used by anyone authenticating as that workspace via native SSH; requires workspace ADMIN access. Otherwise the key is owned by the authenticated user.", "isDeprecated": false, "name": "sshPublicKeyCreate", "type": { @@ -17685,6 +18978,37 @@ } } }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "input", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "UpdateServiceEdgeConfigInput", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Updates the edge config (caching settings) for a service.", + "isDeprecated": false, + "name": "updateServiceEdgeConfig", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "EdgeConfig", + "ofType": null + } + } + }, { "args": [ { @@ -18319,8 +19643,8 @@ "args": [ { "defaultValue": null, - "description": "The environment of the volume instance to update. If null, all instances for the volume will be updated", - "name": "environmentId", + "description": "Optional name for the new restored service. Defaults to '-restored-YYYYMMDD-HHMM'.", + "name": "newServiceName", "type": { "kind": "SCALAR", "name": "String", @@ -18329,67 +19653,32 @@ }, { "defaultValue": null, - "description": null, - "name": "input", + "description": "Caller-supplied source archive sub-prefix (e.g. `pgbackrest/cluster-`). Provided by the multi-history picker when the user selects a specific cluster lifetime out of multiple sub-prefixes in the source bucket. Null falls through to the workflow's SSH probe of the live source's marker file, which always resolves to the current cluster's history.", + "name": "sourceRepoPath", "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "VolumeInstanceUpdateInput", - "ofType": null - } + "kind": "SCALAR", + "name": "String", + "ofType": null } }, { "defaultValue": null, - "description": "The id of the volume to update", - "name": "volumeId", + "description": "Point-in-time target. Must be within the available restore window.", + "name": "targetTimestamp", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - } - ], - "deprecationReason": null, - "description": "Update a volume instance. If no environmentId is provided, all volume instances for the volume will be updated.", - "isDeprecated": false, - "name": "volumeInstanceUpdate", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - } - }, - { - "args": [ - { - "defaultValue": null, - "description": null, - "name": "input", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "VolumeUpdateInput", + "name": "DateTime", "ofType": null } } }, { "defaultValue": null, - "description": null, - "name": "volumeId", + "description": "The id of the volume instance to restore from", + "name": "volumeInstanceId", "type": { "kind": "NON_NULL", "name": null, @@ -18402,15 +19691,15 @@ } ], "deprecationReason": null, - "description": "Update a persistent volume in a project", + "description": "Point-in-time restore. Creates a brand-new Postgres service in the project. The image populates the new service's volume from the source bucket via `pgbackrest restore --type=time --target=` on first boot, replays WAL forward, and promotes. Source service stays online and untouched.", "isDeprecated": false, - "name": "volumeUpdate", + "name": "volumeInstancePITRRestore", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", - "name": "Volume", + "name": "WorkflowId", "ofType": null } } @@ -18419,53 +19708,32 @@ "args": [ { "defaultValue": null, - "description": null, - "name": "payload", + "description": "The environment of the volume instance to update. If null, all instances for the volume will be updated", + "name": "environmentId", "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } + "kind": "SCALAR", + "name": "String", + "ofType": null } }, { "defaultValue": null, "description": null, - "name": "url", + "name": "input", "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "SCALAR", - "name": "String", + "kind": "INPUT_OBJECT", + "name": "VolumeInstanceUpdateInput", "ofType": null } } - } - ], - "deprecationReason": null, - "description": "Test a webhook URL by sending a sample payload. Returns the HTTP status code.", - "isDeprecated": false, - "name": "webhookTest", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - } - } - }, - { - "args": [ + }, { "defaultValue": null, - "description": null, - "name": "id", + "description": "The id of the volume to update", + "name": "volumeId", "type": { "kind": "NON_NULL", "name": null, @@ -18478,9 +19746,9 @@ } ], "deprecationReason": null, - "description": "Delete a workspace and all data associated with it", + "description": "Update a volume instance. If no environmentId is provided, all volume instances for the volume will be updated.", "isDeprecated": false, - "name": "workspaceDelete", + "name": "volumeInstanceUpdate", "type": { "kind": "NON_NULL", "name": null, @@ -18502,7 +19770,7 @@ "name": null, "ofType": { "kind": "INPUT_OBJECT", - "name": "WorkspaceInviteCodeCreateInput", + "name": "VolumeUpdateInput", "ofType": null } } @@ -18510,7 +19778,7 @@ { "defaultValue": null, "description": null, - "name": "workspaceId", + "name": "volumeId", "type": { "kind": "NON_NULL", "name": null, @@ -18523,15 +19791,15 @@ } ], "deprecationReason": null, - "description": "Get an invite code for a workspace and role", + "description": "Update a persistent volume in a project", "isDeprecated": false, - "name": "workspaceInviteCodeCreate", + "name": "volumeUpdate", "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "SCALAR", - "name": "String", + "kind": "OBJECT", + "name": "Volume", "ofType": null } } @@ -18541,7 +19809,7 @@ { "defaultValue": null, "description": null, - "name": "code", + "name": "payload", "type": { "kind": "NON_NULL", "name": null, @@ -18551,28 +19819,11 @@ "ofType": null } } - } - ], - "deprecationReason": null, - "description": "Use an invite code to join a workspace", - "isDeprecated": false, - "name": "workspaceInviteCodeUse", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "Workspace", - "ofType": null - } - } - }, - { - "args": [ + }, { "defaultValue": null, "description": null, - "name": "id", + "name": "url", "type": { "kind": "NON_NULL", "name": null, @@ -18585,46 +19836,15 @@ } ], "deprecationReason": null, - "description": "Leave a workspace", - "isDeprecated": false, - "name": "workspaceLeave", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - } - }, - { - "args": [ - { - "defaultValue": null, - "description": null, - "name": "input", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "WorkspacePermissionChangeInput", - "ofType": null - } - } - } - ], - "deprecationReason": null, - "description": "Changes a user workspace permissions.", + "description": "Test a webhook URL by sending a sample payload. Returns the HTTP status code.", "isDeprecated": false, - "name": "workspacePermissionChange", + "name": "webhookTest", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "Boolean", + "name": "Int", "ofType": null } } @@ -18634,37 +19854,7 @@ { "defaultValue": null, "description": null, - "name": "enabled", - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, - { - "defaultValue": null, - "description": null, - "name": "input", - "type": { - "kind": "INPUT_OBJECT", - "name": "WorkspacePolicyItemUpdateInput", - "ofType": null - } - }, - { - "defaultValue": null, - "description": null, - "name": "policy", - "type": { - "kind": "ENUM", - "name": "WorkspacePolicyName", - "ofType": null - } - }, - { - "defaultValue": null, - "description": null, - "name": "workspaceId", + "name": "id", "type": { "kind": "NON_NULL", "name": null, @@ -18677,9 +19867,9 @@ } ], "deprecationReason": null, - "description": "Enable or disable a workspace policy. Enterprise workspaces only.", + "description": "Delete a workspace and all data associated with it", "isDeprecated": false, - "name": "workspacePolicyItemUpdate", + "name": "workspaceDelete", "type": { "kind": "NON_NULL", "name": null, @@ -18695,13 +19885,13 @@ { "defaultValue": null, "description": null, - "name": "enabled", + "name": "input", "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "SCALAR", - "name": "Boolean", + "kind": "INPUT_OBJECT", + "name": "WorkspaceInviteCodeCreateInput", "ofType": null } } @@ -18722,15 +19912,15 @@ } ], "deprecationReason": null, - "description": "Enable or disable 2FA enforcement for a workspace", + "description": "Get an invite code for a workspace and role", "isDeprecated": false, - "name": "workspaceTwoFactorEnforcementUpdate", + "name": "workspaceInviteCodeCreate", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "Boolean", + "name": "String", "ofType": null } } @@ -18740,7 +19930,7 @@ { "defaultValue": null, "description": null, - "name": "id", + "name": "code", "type": { "kind": "NON_NULL", "name": null, @@ -18750,32 +19940,18 @@ "ofType": null } } - }, - { - "defaultValue": null, - "description": null, - "name": "input", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "WorkspaceUpdateInput", - "ofType": null - } - } } ], "deprecationReason": null, - "description": "Update a workspace by id", + "description": "Use an invite code to join a workspace", "isDeprecated": false, - "name": "workspaceUpdate", + "name": "workspaceInviteCodeUse", "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "SCALAR", - "name": "Boolean", + "kind": "OBJECT", + "name": "Workspace", "ofType": null } } @@ -18798,9 +19974,9 @@ } ], "deprecationReason": null, - "description": "Generate a Slack channel for a workspace", + "description": "Leave a workspace", "isDeprecated": false, - "name": "workspaceUpsertSlackChannel", + "name": "workspaceLeave", "type": { "kind": "NON_NULL", "name": null, @@ -18822,7 +19998,52 @@ "name": null, "ofType": { "kind": "INPUT_OBJECT", - "name": "WorkspaceUserInviteInput", + "name": "WorkspacePermissionChangeInput", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Changes a user workspace permissions.", + "isDeprecated": false, + "name": "workspacePermissionChange", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "sourceId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "sourceType", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "WorkspacePolicyDeploySourceType", "ofType": null } } @@ -18843,15 +20064,15 @@ } ], "deprecationReason": null, - "description": "Invite a user by email to a workspace", + "description": "Add a deploy source to a workspace policy allowlist.", "isDeprecated": false, - "name": "workspaceUserInvite", + "name": "workspacePolicyDeploySourceAllowlistAdd", "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "SCALAR", - "name": "Boolean", + "kind": "OBJECT", + "name": "WorkspacePolicyDeploySourceAllowlist", "ofType": null } } @@ -18861,16 +20082,63 @@ { "defaultValue": null, "description": null, - "name": "input", + "name": "id", "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "INPUT_OBJECT", - "name": "WorkspaceUserRemoveInput", + "kind": "SCALAR", + "name": "String", "ofType": null } } + } + ], + "deprecationReason": null, + "description": "Remove a deploy source from a workspace policy allowlist.", + "isDeprecated": false, + "name": "workspacePolicyDeploySourceAllowlistRemove", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "enabled", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "input", + "type": { + "kind": "INPUT_OBJECT", + "name": "WorkspacePolicyItemUpdateInput", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "policy", + "type": { + "kind": "ENUM", + "name": "WorkspacePolicyName", + "ofType": null + } }, { "defaultValue": null, @@ -18888,9 +20156,9 @@ } ], "deprecationReason": null, - "description": "Remove a user from a workspace", + "description": "Enable or disable a workspace policy. Enterprise workspaces only.", "isDeprecated": false, - "name": "workspaceUserRemove", + "name": "workspacePolicyItemUpdate", "type": { "kind": "NON_NULL", "name": null, @@ -18902,43 +20170,56 @@ } }, { - "name": "sandboxCreate", - "description": "Create a sandbox in an environment.", "args": [ { - "name": "input", + "defaultValue": null, "description": null, + "name": "enabled", "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "INPUT_OBJECT", - "name": "SandboxCreateInput", + "kind": "SCALAR", + "name": "Boolean", "ofType": null } - }, - "defaultValue": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "workspaceId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } } ], + "deprecationReason": null, + "description": "Enable or disable 2FA enforcement for a workspace", + "isDeprecated": false, + "name": "workspaceTwoFactorEnforcementUpdate", "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "OBJECT", - "name": "Sandbox", + "kind": "SCALAR", + "name": "Boolean", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null + } }, { - "name": "sandboxExec", - "description": "Execute a command inside a running sandbox.", "args": [ { - "name": "command", + "defaultValue": null, "description": null, + "name": "id", "type": { "kind": "NON_NULL", "name": null, @@ -18947,26 +20228,43 @@ "name": "String", "ofType": null } - }, - "defaultValue": null + } }, { - "name": "environmentId", + "defaultValue": null, "description": null, + "name": "input", "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "SCALAR", - "name": "String", + "kind": "INPUT_OBJECT", + "name": "WorkspaceUpdateInput", "ofType": null } - }, - "defaultValue": null - }, + } + } + ], + "deprecationReason": null, + "description": "Update a workspace by id", + "isDeprecated": false, + "name": "workspaceUpdate", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, + { + "args": [ { - "name": "id", + "defaultValue": null, "description": null, + "name": "id", "type": { "kind": "NON_NULL", "name": null, @@ -18975,53 +20273,43 @@ "name": "String", "ofType": null } - }, - "defaultValue": null - }, - { - "name": "timeoutSec", - "description": null, - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "defaultValue": null + } } ], + "deprecationReason": null, + "description": "Generate a Slack channel for a workspace", + "isDeprecated": false, + "name": "workspaceUpsertSlackChannel", "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "OBJECT", - "name": "SandboxExecResult", + "kind": "SCALAR", + "name": "Boolean", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null + } }, { - "name": "sandboxDestroy", - "description": "Destroy a sandbox.", "args": [ { - "name": "environmentId", + "defaultValue": null, "description": null, + "name": "input", "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "SCALAR", - "name": "String", + "kind": "INPUT_OBJECT", + "name": "WorkspaceUserInviteInput", "ofType": null } - }, - "defaultValue": null + } }, { - "name": "id", + "defaultValue": null, "description": null, + "name": "workspaceId", "type": { "kind": "NON_NULL", "name": null, @@ -19030,39 +20318,43 @@ "name": "String", "ofType": null } - }, - "defaultValue": null + } } ], - "type": { - "kind": "OBJECT", - "name": "Sandbox", - "ofType": null - }, + "deprecationReason": null, + "description": "Invite a user by email to a workspace", "isDeprecated": false, - "deprecationReason": null + "name": "workspaceUserInvite", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } }, { - "name": "sandboxHeartbeat", - "description": "Extend a sandbox's lifetime.", "args": [ { - "name": "environmentId", + "defaultValue": null, "description": null, + "name": "input", "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "SCALAR", - "name": "String", + "kind": "INPUT_OBJECT", + "name": "WorkspaceUserRemoveInput", "ofType": null } - }, - "defaultValue": null + } }, { - "name": "id", + "defaultValue": null, "description": null, + "name": "workspaceId", "type": { "kind": "NON_NULL", "name": null, @@ -19071,17 +20363,22 @@ "name": "String", "ofType": null } - }, - "defaultValue": null + } } ], - "type": { - "kind": "OBJECT", - "name": "Sandbox", - "ofType": null - }, + "deprecationReason": null, + "description": "Remove a user from a workspace", "isDeprecated": false, - "deprecationReason": null + "name": "workspaceUserRemove", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } } ], "inputFields": null, @@ -19385,6 +20682,11 @@ "kind": "OBJECT", "name": "WorkspacePolicy", "ofType": null + }, + { + "kind": "OBJECT", + "name": "WorkspacePolicyDeploySourceAllowlist", + "ofType": null } ] }, @@ -21821,49 +23123,13 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "AGENT_USAGE_BILLING" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "ALLOW_REPLICA_METRICS" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "ARCHIVER_V2_ROLLOUT" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "BUILDER_V3_ROLLOUT_EXISTING_SERVICES" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "BUILDER_V3_ROLLOUT_EXISTING_SERVICES_PRO" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "BUILDER_V3_ROLLOUT_NEW_SERVICES" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "BUILDER_V3_ROLLOUT_NEW_SERVICES_PRO" + "name": "BAN_APPEAL_FORM" }, { "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "COMPARE_CLICKHOUSE_METRICS" + "name": "CHAT_SANDBOX" }, { "deprecationReason": null, @@ -21881,19 +23147,7 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "DISABLE_OAUTH_ACCESS_TOKENS" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "FOCUSED_PR_ENVIRONMENTS" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "GITHUB_PERMISSION_NOTIFICATION_ROLLOUT" + "name": "HA_STATIC_EGRESS_SELF_SERVICE" }, { "deprecationReason": null, @@ -21925,12 +23179,6 @@ "isDeprecated": false, "name": "OAUTH_DCR_KILLSWITCH" }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "OAUTH_DEVICE_FLOW_KILLSWITCH" - }, { "deprecationReason": null, "description": null, @@ -21965,37 +23213,13 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "UNIFIED_SNAPSHOT_AND_BUILD" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "UNIFIED_SNAPSHOT_AND_BUILD_HOBBY" + "name": "TCPIP_DATABASE_CLIENT" }, { "deprecationReason": null, "description": null, "isDeprecated": false, "name": "UPDATED_VM_QUERIES" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "USE_CLICKHOUSE_METRICS" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "USE_GH_WEBHOOKS_FOR_CHANGE_DETECTION" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "VM_TIME_RANGE_QUERY" } ], "fields": null, @@ -24217,6 +25441,18 @@ } } }, + { + "args": [], + "deprecationReason": null, + "description": "The id of the oldest non-ephemeral environment for this project (typically production). Used by the dashboard to render project cards without fetching the full environments connection.", + "isDeprecated": false, + "name": "primaryEnvironmentId", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, { "args": [ { @@ -26195,6 +27431,22 @@ "ofType": null } } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "sandbox", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "AccessRule", + "ofType": null + } + } } ], "inputFields": null, @@ -27052,6 +28304,35 @@ "name": "ProjectWorkspaceMembersResponse", "possibleTypes": null }, + { + "description": null, + "enumValues": [ + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "CREATED_AT_DESC" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "NAME_ASC" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "UPDATED_AT_DESC" + } + ], + "fields": null, + "inputFields": null, + "interfaces": null, + "kind": "ENUM", + "name": "ProjectsOrderBy", + "possibleTypes": null + }, { "description": null, "enumValues": null, @@ -27365,6 +28646,111 @@ "name": "PublicStats", "possibleTypes": null }, + { + "description": null, + "enumValues": [ + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "ALL" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "HTML" + } + ], + "fields": null, + "inputFields": null, + "interfaces": null, + "kind": "ENUM", + "name": "PurgeCacheScope", + "possibleTypes": null + }, + { + "description": null, + "enumValues": [ + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "ALL" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "HTML" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "OFF" + } + ], + "fields": null, + "inputFields": null, + "interfaces": null, + "kind": "ENUM", + "name": "PurgeOnDeploy", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": null, + "inputFields": [ + { + "defaultValue": null, + "description": null, + "name": "environmentId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "scope", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "PurgeCacheScope", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "serviceId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "interfaces": null, + "kind": "INPUT_OBJECT", + "name": "PurgeServiceCacheInput", + "possibleTypes": null + }, { "description": null, "enumValues": null, @@ -28588,6 +29974,112 @@ } } }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "environmentId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "serviceId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Preview HA static egress IPs that would be assigned without persisting", + "isDeprecated": false, + "name": "egressGatewayHAPreview", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "EgressGateway", + "ofType": null + } + } + } + } + }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "environmentId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "serviceId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Preview legacy static egress IP that would be assigned without persisting", + "isDeprecated": false, + "name": "egressGatewayLegacyPreview", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "EgressGateway", + "ofType": null + } + } + } + } + }, { "args": [ { @@ -30468,6 +31960,33 @@ } } }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "id", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Gets a notification delivery by ID for the authenticated user", + "isDeprecated": false, + "name": "notificationDelivery", + "type": { + "kind": "OBJECT", + "name": "NotificationDelivery", + "ofType": null + } + }, { "args": [ { @@ -31382,6 +32901,16 @@ "ofType": null } }, + { + "defaultValue": null, + "description": null, + "name": "orderBy", + "type": { + "kind": "ENUM", + "name": "ProjectsOrderBy", + "ofType": null + } + }, { "defaultValue": null, "description": null, @@ -31417,6 +32946,53 @@ } } }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "ids", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + } + } + ], + "deprecationReason": null, + "description": "Fetch multiple projects by id. Skips ids the caller cannot access (does not throw on partial denial). Intended for batched dashboard hydration of a small viewport-sized set of cards.", + "isDeprecated": false, + "name": "projectsByIds", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Project", + "ofType": null + } + } + } + } + }, { "args": [], "deprecationReason": null, @@ -31530,6 +33106,163 @@ } } }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "environmentId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "id", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Get a sandbox by id.", + "isDeprecated": false, + "name": "sandbox", + "type": { + "kind": "OBJECT", + "name": "Sandbox", + "ofType": null + } + }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "environmentId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "id", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Get the status of a sandbox template.", + "isDeprecated": false, + "name": "sandboxTemplate", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "SandboxTemplate", + "ofType": null + } + } + }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "after", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "before", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "environmentId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "first", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "last", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + } + ], + "deprecationReason": null, + "description": "List sandboxes in an environment.", + "isDeprecated": false, + "name": "sandboxes", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "QuerySandboxesConnection", + "ofType": null + } + } + }, { "args": [ { @@ -31653,6 +33386,20 @@ } } }, + { + "defaultValue": null, + "description": null, + "name": "projectId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, { "defaultValue": null, "description": null, @@ -31669,15 +33416,15 @@ } ], "deprecationReason": null, - "description": "Check if the upstream repo for a service has an update available", + "description": "Returns the auto-deploy status for a service instance, including whether it can be enabled.", "isDeprecated": false, - "name": "serviceInstanceIsUpdatable", + "name": "serviceInstanceAutoDeployStatus", "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "SCALAR", - "name": "Boolean", + "kind": "OBJECT", + "name": "ServiceInstanceAutoDeployStatus", "ofType": null } } @@ -31714,13 +33461,58 @@ } ], "deprecationReason": null, - "description": "Get the service instance resource limit overrides (null if no overrides set)", + "description": "Check if the upstream repo for a service has an update available", "isDeprecated": false, - "name": "serviceInstanceLimitOverride", + "name": "serviceInstanceIsUpdatable", "type": { - "kind": "SCALAR", - "name": "ServiceInstanceLimit", - "ofType": null + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "environmentId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "serviceId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "deprecationReason": null, + "description": "Get the service instance resource limit overrides (null if no overrides set)", + "isDeprecated": false, + "name": "serviceInstanceLimitOverride", + "type": { + "kind": "SCALAR", + "name": "ServiceInstanceLimit", + "ofType": null } }, { @@ -31879,7 +33671,7 @@ } ], "deprecationReason": null, - "description": "Gets SSH public keys. If workspaceId is provided, returns the keys owned by that workspace (requires workspace MEMBER access); otherwise returns the authenticated user's personal keys.", + "description": "Gets SSH public keys. If workspaceId is provided, returns the keys owned by that workspace (requires workspace MEMBER access). Under a workspace-scoped API token, workspaceId defaults to the token's workspace when omitted; otherwise returns the authenticated user's personal keys.", "isDeprecated": false, "name": "sshPublicKeys", "type": { @@ -33112,41 +34904,15 @@ { "defaultValue": null, "description": null, - "name": "after", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - { - "defaultValue": null, - "description": null, - "name": "before", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - { - "defaultValue": null, - "description": null, - "name": "first", + "name": "sourceType", "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - } - }, - { - "defaultValue": null, - "description": null, - "name": "last", - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "WorkspacePolicyDeploySourceType", + "ofType": null + } } }, { @@ -33165,89 +34931,97 @@ } ], "deprecationReason": null, - "description": "Get all templates for a workspace.", + "description": "List deploy sources that can be added to a workspace policy.", "isDeprecated": false, - "name": "workspaceTemplates", + "name": "workspacePolicySelectableDeploySources", "type": { "kind": "NON_NULL", "name": null, "ofType": { - "kind": "OBJECT", - "name": "QueryWorkspaceTemplatesConnection", - "ofType": null + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "WorkspacePolicySelectableDeploySource", + "ofType": null + } + } } } }, { - "name": "sandboxes", - "description": "List sandboxes in an environment.", "args": [ { - "name": "after", + "defaultValue": null, "description": null, + "name": "after", "type": { "kind": "SCALAR", "name": "String", "ofType": null - }, - "defaultValue": null + } }, { - "name": "before", + "defaultValue": null, "description": null, + "name": "before", "type": { "kind": "SCALAR", "name": "String", "ofType": null - }, - "defaultValue": null + } }, { - "name": "environmentId", + "defaultValue": null, "description": null, - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "defaultValue": null - }, - { "name": "first", - "description": null, "type": { "kind": "SCALAR", "name": "Int", "ofType": null - }, - "defaultValue": null + } }, { - "name": "last", + "defaultValue": null, "description": null, + "name": "last", "type": { "kind": "SCALAR", "name": "Int", "ofType": null - }, - "defaultValue": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "workspaceId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } } ], + "deprecationReason": null, + "description": "Get all templates for a workspace.", + "isDeprecated": false, + "name": "workspaceTemplates", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", - "name": "QuerySandboxesConnection", + "name": "QueryWorkspaceTemplatesConnection", "ofType": null } - }, - "isDeprecated": false, - "deprecationReason": null + } } ], "inputFields": null, @@ -34760,6 +36534,100 @@ "name": "QueryProjectsConnectionEdge", "possibleTypes": null }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "edges", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "QuerySandboxesConnectionEdge", + "ofType": null + } + } + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "pageInfo", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "PageInfo", + "ofType": null + } + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "QuerySandboxesConnection", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "cursor", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "node", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Sandbox", + "ofType": null + } + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "QuerySandboxesConnectionEdge", + "possibleTypes": null + }, { "description": null, "enumValues": null, @@ -35063,7 +36931,7 @@ "name": null, "ofType": { "kind": "OBJECT", - "name": "QueryTemplatesConnectionEdge", + "name": "QueryTemplateSearchConnectionEdge", "ofType": null } } @@ -35090,7 +36958,7 @@ "inputFields": null, "interfaces": [], "kind": "OBJECT", - "name": "QueryTemplatesConnection", + "name": "QueryTemplateSearchConnection", "possibleTypes": null }, { @@ -35124,7 +36992,7 @@ "name": null, "ofType": { "kind": "OBJECT", - "name": "Template", + "name": "TemplateSearchResult", "ofType": null } } @@ -35133,7 +37001,7 @@ "inputFields": null, "interfaces": [], "kind": "OBJECT", - "name": "QueryTemplatesConnectionEdge", + "name": "QueryTemplateSearchConnectionEdge", "possibleTypes": null }, { @@ -35157,7 +37025,7 @@ "name": null, "ofType": { "kind": "OBJECT", - "name": "QueryTrustedDomainsConnectionEdge", + "name": "QueryTemplatesConnectionEdge", "ofType": null } } @@ -35184,7 +37052,7 @@ "inputFields": null, "interfaces": [], "kind": "OBJECT", - "name": "QueryTrustedDomainsConnection", + "name": "QueryTemplatesConnection", "possibleTypes": null }, { @@ -35218,7 +37086,7 @@ "name": null, "ofType": { "kind": "OBJECT", - "name": "TrustedDomain", + "name": "Template", "ofType": null } } @@ -35227,7 +37095,7 @@ "inputFields": null, "interfaces": [], "kind": "OBJECT", - "name": "QueryTrustedDomainsConnectionEdge", + "name": "QueryTemplatesConnectionEdge", "possibleTypes": null }, { @@ -35251,7 +37119,101 @@ "name": null, "ofType": { "kind": "OBJECT", - "name": "QueryUserTemplatesConnectionEdge", + "name": "QueryTrustedDomainsConnectionEdge", + "ofType": null + } + } + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "pageInfo", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "PageInfo", + "ofType": null + } + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "QueryTrustedDomainsConnection", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "cursor", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "node", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "TrustedDomain", + "ofType": null + } + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "QueryTrustedDomainsConnectionEdge", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "edges", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "QueryUserTemplatesConnectionEdge", "ofType": null } } @@ -35875,6 +37837,18 @@ "ofType": null } }, + { + "args": [], + "deprecationReason": null, + "description": "Region ID (airport code)", + "isDeprecated": false, + "name": "id", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, { "args": [], "deprecationReason": null, @@ -35942,18 +37916,6 @@ "description": null, "enumValues": null, "fields": [ - { - "args": [], - "deprecationReason": null, - "description": "Admin only region", - "isDeprecated": false, - "name": "adminOnly", - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, { "args": [], "deprecationReason": null, @@ -35965,38 +37927,6 @@ "name": "RegionDeprecationInfo", "ofType": null } - }, - { - "args": [], - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "runtimeExclusivity", - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - } - }, - { - "args": [], - "deprecationReason": null, - "description": "Staging only region", - "isDeprecated": false, - "name": "stagingOnly", - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } } ], "inputFields": null, @@ -36338,7 +38268,493 @@ "possibleTypes": null }, { - "description": "\nSerializedTemplateConfig is a custom scalar type that represents the serializedConfig for a template.\nJSON Schema: https://backboard.railway-develop.com/schema/template.schema.json\n", + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "createdAt", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "DateTime", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "environmentId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "id", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "idleTimeoutMinutes", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "networkIsolation", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "SandboxNetworkIsolation", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "region", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "status", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "SandboxStatus", + "ofType": null + } + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "Sandbox", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": null, + "inputFields": [ + { + "defaultValue": null, + "description": null, + "name": "environmentId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "idleTimeoutMinutes", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": null, + "description": "Network access for the sandbox. Defaults to ISOLATED (no private network access).", + "name": "networkIsolation", + "type": { + "kind": "ENUM", + "name": "SandboxNetworkIsolation", + "ofType": null + } + }, + { + "defaultValue": null, + "description": "Fork an existing running sandbox in this environment. Mutually exclusive with template.", + "name": "sourceSandboxId", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "template", + "type": { + "kind": "INPUT_OBJECT", + "name": "SandboxTemplateInput", + "ofType": null + } + }, + { + "defaultValue": null, + "description": "Environment variables baked into the sandbox, available to every command. Values may contain Railway variable references (e.g. ${{shared.FOO}}, ${{ServiceName.BAR}}), resolved at create time.", + "name": "variables", + "type": { + "kind": "SCALAR", + "name": "EnvironmentVariables", + "ofType": null + } + } + ], + "interfaces": null, + "kind": "INPUT_OBJECT", + "name": "SandboxCreateInput", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "exitCode", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "stderr", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "stdout", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "timedOut", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "truncated", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "SandboxExecResult", + "possibleTypes": null + }, + { + "description": "Controls a sandbox's access to the environment's private network.", + "enumValues": [ + { + "deprecationReason": null, + "description": "Peer-isolated with public NAT egress only; no access to the environment's private network. Default.", + "isDeprecated": false, + "name": "ISOLATED" + }, + { + "deprecationReason": null, + "description": "Joins the environment's private network.", + "isDeprecated": false, + "name": "PRIVATE" + } + ], + "fields": null, + "inputFields": null, + "interfaces": null, + "kind": "ENUM", + "name": "SandboxNetworkIsolation", + "possibleTypes": null + }, + { + "description": null, + "enumValues": [ + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "CREATING" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "DESTROYED" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "DESTROYING" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "FAILED" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "RUNNING" + } + ], + "fields": null, + "inputFields": null, + "interfaces": null, + "kind": "ENUM", + "name": "SandboxStatus", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "environmentId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "id", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "status", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "SandboxTemplateStatus", + "ofType": null + } + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "SandboxTemplate", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": null, + "inputFields": [ + { + "defaultValue": null, + "description": null, + "name": "baseImageDigest", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "instructions", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + } + } + ], + "interfaces": null, + "kind": "INPUT_OBJECT", + "name": "SandboxTemplateInput", + "possibleTypes": null + }, + { + "description": null, + "enumValues": [ + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "BUILDING" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "FAILED" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "PENDING" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "READY" + } + ], + "fields": null, + "inputFields": null, + "interfaces": null, + "kind": "ENUM", + "name": "SandboxTemplateStatus", + "possibleTypes": null + }, + { + "description": "\nSerializedTemplateConfig is a custom scalar type that represents the serializedConfig for a template.\nJSON Schema: https://backboard.railway.com/schema/template.schema.json\n", "enumValues": null, "fields": null, "inputFields": null, @@ -37054,9 +39470,9 @@ "fields": [ { "args": [], - "deprecationReason": null, + "deprecationReason": "Removed; always null.", "description": null, - "isDeprecated": false, + "isDeprecated": true, "name": "cdnMode", "type": { "kind": "SCALAR", @@ -37613,6 +40029,18 @@ "ofType": null } }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "edgeConfig", + "type": { + "kind": "OBJECT", + "name": "EdgeConfig", + "ofType": null + } + }, { "args": [], "deprecationReason": null, @@ -37998,6 +40426,155 @@ "name": "ServiceInstance", "possibleTypes": null }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "canEnable", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "enabled", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "reason", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "ServiceInstanceAutoDeployStatus", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": null, + "inputFields": [ + { + "defaultValue": null, + "description": null, + "name": "enabled", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "environmentId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "projectId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "serviceId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "interfaces": null, + "kind": "INPUT_OBJECT", + "name": "ServiceInstanceAutoDeployUpdateInput", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "enabled", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "ServiceInstanceAutoDeployUpdateResult", + "possibleTypes": null + }, { "description": null, "enumValues": null, @@ -38767,6 +41344,149 @@ "name": "SessionType", "possibleTypes": null }, + { + "description": null, + "enumValues": null, + "fields": null, + "inputFields": [ + { + "defaultValue": null, + "description": null, + "name": "agentSessionId", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "arch", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "caller", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "cliVersion", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "configuredClients", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "errorMessage", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "installRequestId", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "isCi", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "os", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "phase", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "sessionId", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "success", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + ], + "interfaces": null, + "kind": "INPUT_OBJECT", + "name": "SetupAgentEventTrackInput", + "possibleTypes": null + }, { "description": null, "enumValues": null, @@ -38864,6 +41584,89 @@ "name": "SharedVariableConfigureInput", "possibleTypes": null }, + { + "description": null, + "enumValues": null, + "fields": null, + "inputFields": [ + { + "defaultValue": null, + "description": null, + "name": "environmentId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "instanceId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "kind", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "port", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "scope", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "serviceId", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + ], + "interfaces": null, + "kind": "INPUT_OBJECT", + "name": "ShellTokenInput", + "possibleTypes": null + }, { "description": null, "enumValues": null, @@ -39018,106 +41821,253 @@ } ], "inputFields": null, - "interfaces": [], - "kind": "OBJECT", - "name": "SimilarTemplate", - "possibleTypes": null - }, - { - "description": null, - "enumValues": null, - "fields": null, - "inputFields": null, - "interfaces": null, - "kind": "SCALAR", - "name": "SkippedResourceIds", - "possibleTypes": null - }, - { - "description": null, - "enumValues": [ - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "asc" - }, - { - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "desc" - } - ], - "fields": null, - "inputFields": null, - "interfaces": null, - "kind": "ENUM", - "name": "SortOrder", - "possibleTypes": null - }, - { - "description": null, - "enumValues": null, - "fields": [ - { - "args": [], - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "features", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "SpendCommitmentFeatureId", - "ofType": null - } - } - } - } - }, - { - "args": [], - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "id", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - } - }, - { - "args": [], - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "minSpendAmountCents", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - } - } - } - ], - "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "SimilarTemplate", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": null, + "inputFields": null, + "interfaces": null, + "kind": "SCALAR", + "name": "SkippedResourceIds", + "possibleTypes": null + }, + { + "description": null, + "enumValues": [ + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "asc" + }, + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "desc" + } + ], + "fields": null, + "inputFields": null, + "interfaces": null, + "kind": "ENUM", + "name": "SortOrder", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "features", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "SpendCommitmentFeatureId", + "ofType": null + } + } + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "id", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "minSpendAmountCents", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + } + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Node", + "ofType": null + } + ], + "kind": "OBJECT", + "name": "SpendCommitment", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": null, + "inputFields": null, + "interfaces": null, + "kind": "SCALAR", + "name": "SpendCommitmentFeatureId", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "createdAt", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "DateTime", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "fingerprint", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "id", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "name", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "publicKey", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "updatedAt", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "DateTime", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "userId", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "workspaceId", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + ], + "inputFields": null, "interfaces": [ { "kind": "INTERFACE", @@ -39126,45 +42076,18 @@ } ], "kind": "OBJECT", - "name": "SpendCommitment", + "name": "SshPublicKey", "possibleTypes": null }, { "description": null, "enumValues": null, "fields": null, - "inputFields": null, - "interfaces": null, - "kind": "SCALAR", - "name": "SpendCommitmentFeatureId", - "possibleTypes": null - }, - { - "description": null, - "enumValues": null, - "fields": [ - { - "args": [], - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "createdAt", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "DateTime", - "ofType": null - } - } - }, + "inputFields": [ { - "args": [], - "deprecationReason": null, + "defaultValue": null, "description": null, - "isDeprecated": false, - "name": "fingerprint", + "name": "name", "type": { "kind": "NON_NULL", "name": null, @@ -39176,27 +42099,9 @@ } }, { - "args": [], - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "id", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - } - }, - { - "args": [], - "deprecationReason": null, + "defaultValue": null, "description": null, - "isDeprecated": false, - "name": "name", + "name": "publicKey", "type": { "kind": "NON_NULL", "name": null, @@ -39208,72 +42113,46 @@ } }, { - "args": [], - "deprecationReason": null, + "defaultValue": null, "description": null, - "isDeprecated": false, - "name": "publicKey", + "name": "workspaceId", "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } + "kind": "SCALAR", + "name": "String", + "ofType": null } - }, + } + ], + "interfaces": null, + "kind": "INPUT_OBJECT", + "name": "SshPublicKeyCreateInput", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": [ { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "updatedAt", + "name": "enabled", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "DateTime", + "name": "Boolean", "ofType": null } } - }, - { - "args": [], - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "userId", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - { - "args": [], - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "workspaceId", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } } ], "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Node", - "ofType": null - } - ], + "interfaces": [], "kind": "OBJECT", - "name": "SshPublicKey", + "name": "StaleWhileRevalidateConfig", "possibleTypes": null }, { @@ -39284,45 +42163,21 @@ { "defaultValue": null, "description": null, - "name": "name", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - }, - { - "defaultValue": null, - "description": null, - "name": "publicKey", + "name": "enabled", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "String", + "name": "Boolean", "ofType": null } } - }, - { - "defaultValue": null, - "description": null, - "name": "workspaceId", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } } ], "interfaces": null, "kind": "INPUT_OBJECT", - "name": "SshPublicKeyCreateInput", + "name": "StaleWhileRevalidateInput", "possibleTypes": null }, { @@ -41356,6 +44211,18 @@ } } }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "maintainer", + "type": { + "kind": "OBJECT", + "name": "MaintainerWorkspace", + "ofType": null + } + }, { "args": [], "deprecationReason": "Deprecated in favor of listing the fields individually.", @@ -41601,6 +44468,22 @@ } } }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "updatedAt", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "DateTime", + "ofType": null + } + } + }, { "args": [], "deprecationReason": null, @@ -42453,100 +45336,6 @@ "name": "TemplatePublishInput", "possibleTypes": null }, - { - "description": null, - "enumValues": null, - "fields": [ - { - "args": [], - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "edges", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "QueryTemplateSearchConnectionEdge", - "ofType": null - } - } - } - } - }, - { - "args": [], - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "pageInfo", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "PageInfo", - "ofType": null - } - } - } - ], - "inputFields": null, - "interfaces": [], - "kind": "OBJECT", - "name": "QueryTemplateSearchConnection", - "possibleTypes": null - }, - { - "description": null, - "enumValues": null, - "fields": [ - { - "args": [], - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "cursor", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - }, - { - "args": [], - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "node", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "TemplateSearchResult", - "ofType": null - } - } - } - ], - "inputFields": null, - "interfaces": [], - "kind": "OBJECT", - "name": "QueryTemplateSearchConnectionEdge", - "possibleTypes": null - }, { "description": null, "enumValues": null, @@ -43493,6 +46282,59 @@ "name": "UpdateNotificationRuleInput", "possibleTypes": null }, + { + "description": null, + "enumValues": null, + "fields": null, + "inputFields": [ + { + "defaultValue": null, + "description": null, + "name": "config", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "EdgeConfigInput", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "environmentId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": null, + "name": "serviceId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + ], + "interfaces": null, + "kind": "INPUT_OBJECT", + "name": "UpdateServiceEdgeConfigInput", + "possibleTypes": null + }, { "description": "The `Upload` scalar type represents a file upload.", "enumValues": null, @@ -43740,9 +46582,9 @@ }, { "args": [], - "deprecationReason": null, + "deprecationReason": "Resolve via the user's workspaces and check for an active BAN restriction. This field will be removed after the WorkspaceRestriction migration completes.", "description": null, - "isDeprecated": false, + "isDeprecated": true, "name": "banReason", "type": { "kind": "SCALAR", @@ -45629,13 +48471,236 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "id", + "name": "id", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "name", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "project", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Project", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "projectId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "after", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "before", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "first", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "last", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + } + ], + "deprecationReason": "Use environment.volumeInstances for properly scoped access control", + "description": null, + "isDeprecated": true, + "name": "volumeInstances", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "VolumeVolumeInstancesConnection", + "ofType": null + } + } + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Node", + "ofType": null + } + ], + "kind": "OBJECT", + "name": "Volume", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": null, + "inputFields": [ + { + "defaultValue": null, + "description": "The environment to deploy the volume instances into. If `null`, the volume will not be deployed to any environment. `undefined` will deploy to all environments.", + "name": "environmentId", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": "The path in the container to mount the volume to", + "name": "mountPath", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": "The project to create the volume in", + "name": "projectId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "defaultValue": null, + "description": "The region to create the volume instances in. If not provided, the default region will be used.", + "name": "region", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": "The service to attach the volume to. If not provided, the volume will be disconnected.", + "name": "serviceId", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + ], + "interfaces": null, + "kind": "INPUT_OBJECT", + "name": "VolumeCreateInput", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "createdAt", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "DateTime", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "currentSizeMB", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "ID", + "name": "Float", "ofType": null } } @@ -45645,15 +48710,11 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "name", + "name": "deletedAt", "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } + "kind": "SCALAR", + "name": "DateTime", + "ofType": null } }, { @@ -45661,13 +48722,13 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "project", + "name": "environment", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "OBJECT", - "name": "Project", + "name": "Environment", "ofType": null } } @@ -45677,106 +48738,7 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "projectId", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - }, - { - "args": [ - { - "defaultValue": null, - "description": null, - "name": "after", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - { - "defaultValue": null, - "description": null, - "name": "before", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - { - "defaultValue": null, - "description": null, - "name": "first", - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - } - }, - { - "defaultValue": null, - "description": null, - "name": "last", - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - } - } - ], - "deprecationReason": "Use environment.volumeInstances for properly scoped access control", - "description": null, - "isDeprecated": true, - "name": "volumeInstances", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "VolumeVolumeInstancesConnection", - "ofType": null - } - } - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Node", - "ofType": null - } - ], - "kind": "OBJECT", - "name": "Volume", - "possibleTypes": null - }, - { - "description": null, - "enumValues": null, - "fields": null, - "inputFields": [ - { - "defaultValue": null, - "description": "The environment to deploy the volume instances into. If `null`, the volume will not be deployed to any environment. `undefined` will deploy to all environments.", "name": "environmentId", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - { - "defaultValue": null, - "description": "The path in the container to mount the volume to", - "name": "mountPath", "type": { "kind": "NON_NULL", "name": null, @@ -45787,91 +48749,15 @@ } } }, - { - "defaultValue": null, - "description": "The project to create the volume in", - "name": "projectId", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - }, - { - "defaultValue": null, - "description": "The region to create the volume instances in. If not provided, the default region will be used.", - "name": "region", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - { - "defaultValue": null, - "description": "The service to attach the volume to. If not provided, the volume will be disconnected.", - "name": "serviceId", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - ], - "interfaces": null, - "kind": "INPUT_OBJECT", - "name": "VolumeCreateInput", - "possibleTypes": null - }, - { - "description": null, - "enumValues": null, - "fields": [ - { - "args": [], - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "createdAt", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "DateTime", - "ofType": null - } - } - }, - { - "args": [], - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "currentSizeMB", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Float", - "ofType": null - } - } - }, { "args": [], "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "deletedAt", + "name": "externalId", "type": { "kind": "SCALAR", - "name": "DateTime", + "name": "String", "ofType": null } }, @@ -45880,45 +48766,13 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "isPendingDeletion", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - } - }, - { - "args": [], - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "environment", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "Environment", - "ofType": null - } - } - }, - { - "args": [], - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "environmentId", + "name": "id", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "String", + "name": "ID", "ofType": null } } @@ -45928,25 +48782,13 @@ "deprecationReason": null, "description": null, "isDeprecated": false, - "name": "externalId", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - { - "args": [], - "deprecationReason": null, - "description": null, - "isDeprecated": false, - "name": "id", + "name": "isPendingDeletion", "type": { "kind": "NON_NULL", "name": null, "ofType": { "kind": "SCALAR", - "name": "ID", + "name": "Boolean", "ofType": null } } @@ -47311,9 +50153,9 @@ }, { "args": [], - "deprecationReason": null, + "deprecationReason": "Deprecated regions are no longer supported.", "description": null, - "isDeprecated": false, + "isDeprecated": true, "name": "allowDeprecatedRegions", "type": { "kind": "SCALAR", @@ -47347,9 +50189,9 @@ }, { "args": [], - "deprecationReason": null, + "deprecationReason": "Use `workspace.restriction` and check for an active BAN restriction. This field will be removed after the WorkspaceRestriction migration completes.", "description": null, - "isDeprecated": false, + "isDeprecated": true, "name": "banReason", "type": { "kind": "SCALAR", @@ -47618,6 +50460,33 @@ "ofType": null } }, + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "includeDeleted", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + ], + "deprecationReason": null, + "description": "Total number of projects in this workspace. Used by the dashboard to show an exact count without paginating through every project.", + "isDeprecated": false, + "name": "projectCount", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + } + }, { "args": [ { @@ -48352,6 +51221,63 @@ "description": null, "enumValues": null, "fields": [ + { + "args": [ + { + "defaultValue": null, + "description": null, + "name": "after", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "before", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "first", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + { + "defaultValue": null, + "description": null, + "name": "last", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + } + ], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "deploySourceAllowlist", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "WorkspacePolicyDeploySourceAllowlistConnection", + "ofType": null + } + } + }, { "args": [], "deprecationReason": null, @@ -48368,6 +51294,22 @@ } } }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "restrictDeploysToAllowedSources", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + } + }, { "args": [], "deprecationReason": null, @@ -48413,6 +51355,238 @@ "name": "WorkspacePolicy", "possibleTypes": null }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "addedBy", + "type": { + "kind": "OBJECT", + "name": "User", + "ofType": null + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "createdAt", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "DateTime", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "id", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "sourceIcon", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "sourceId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "sourceName", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "sourceType", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "WorkspacePolicyDeploySourceType", + "ofType": null + } + } + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Node", + "ofType": null + } + ], + "kind": "OBJECT", + "name": "WorkspacePolicyDeploySourceAllowlist", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "edges", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "WorkspacePolicyDeploySourceAllowlistConnectionEdge", + "ofType": null + } + } + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "pageInfo", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "PageInfo", + "ofType": null + } + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "WorkspacePolicyDeploySourceAllowlistConnection", + "possibleTypes": null + }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "cursor", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "node", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "WorkspacePolicyDeploySourceAllowlist", + "ofType": null + } + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "WorkspacePolicyDeploySourceAllowlistConnectionEdge", + "possibleTypes": null + }, + { + "description": null, + "enumValues": [ + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "GITHUB_ORG" + } + ], + "fields": null, + "inputFields": null, + "interfaces": null, + "kind": "ENUM", + "name": "WorkspacePolicyDeploySourceType", + "possibleTypes": null + }, { "description": null, "enumValues": null, @@ -48455,6 +51629,12 @@ { "description": null, "enumValues": [ + { + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "RESTRICT_DEPLOYS_TO_ALLOWED_SOURCES" + }, { "deprecationReason": null, "description": null, @@ -48475,6 +51655,77 @@ "name": "WorkspacePolicyName", "possibleTypes": null }, + { + "description": null, + "enumValues": null, + "fields": [ + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "sourceIcon", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "sourceId", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "sourceName", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + { + "args": [], + "deprecationReason": null, + "description": null, + "isDeprecated": false, + "name": "sourceType", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "WorkspacePolicyDeploySourceType", + "ofType": null + } + } + } + ], + "inputFields": null, + "interfaces": [], + "kind": "OBJECT", + "name": "WorkspacePolicySelectableDeploySource", + "possibleTypes": null + }, { "description": null, "enumValues": null, @@ -49660,423 +52911,6 @@ "kind": "INPUT_OBJECT", "name": "customerTogglePayoutsToCreditsInput", "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "Sandbox", - "description": null, - "fields": [ - { - "name": "createdAt", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "DateTime", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "environmentId", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "id", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "idleTimeoutMinutes", - "description": null, - "args": [], - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "region", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "status", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "ENUM", - "name": "SandboxStatus", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "SandboxStatus", - "description": null, - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { - "name": "CREATING", - "description": null, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "DESTROYED", - "description": null, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "DESTROYING", - "description": null, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "FAILED", - "description": null, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "RUNNING", - "description": null, - "isDeprecated": false, - "deprecationReason": null - } - ], - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "SandboxExecResult", - "description": null, - "fields": [ - { - "name": "exitCode", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "stderr", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "stdout", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "timedOut", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "truncated", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "SandboxCreateInput", - "description": null, - "fields": null, - "inputFields": [ - { - "name": "environmentId", - "description": null, - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "idleTimeoutMinutes", - "description": null, - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "template", - "description": null, - "type": { - "kind": "INPUT_OBJECT", - "name": "SandboxTemplateInput", - "ofType": null - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "SandboxTemplateInput", - "description": null, - "fields": null, - "inputFields": [ - { - "name": "baseImageDigest", - "description": null, - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "instructions", - "description": null, - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - } - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "QuerySandboxesConnection", - "description": null, - "fields": [ - { - "name": "edges", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "QuerySandboxesConnectionEdge", - "ofType": null - } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "pageInfo", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "PageInfo", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "QuerySandboxesConnectionEdge", - "description": null, - "fields": [ - { - "name": "cursor", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "node", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "Sandbox", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null } ] } diff --git a/src/macros.rs b/src/macros.rs index b5ace1722..cf1d12877 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -63,7 +63,18 @@ macro_rules! commands { match matches.subcommand() { $( Some((stringify!([<$module:snake>]), sub_matches)) => { - let subcommand_name = sub_matches.subcommand_name().map(|s| s.to_string()); + // Walk nested subcommand levels so telemetry can + // distinguish e.g. `sandbox template build` from + // `sandbox template status` ("template:build"). + let subcommand_name = { + let mut parts: Vec<&str> = Vec::new(); + let mut current = sub_matches; + while let Some((name, next)) = current.subcommand() { + parts.push(name); + current = next; + } + if parts.is_empty() { None } else { Some(parts.join(":")) } + }; let args = <$module::Args as ::clap::FromArgMatches>::from_arg_matches(sub_matches) .map_err(anyhow::Error::from)?; let start = ::std::time::Instant::now();