mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-10 03:03:37 +02:00
cli: implement plugin host render/send context and cookie jar support
This commit is contained in:
@@ -21,6 +21,10 @@ pub struct Cli {
|
||||
#[arg(long, short, global = true)]
|
||||
pub environment: Option<String>,
|
||||
|
||||
/// Cookie jar ID to use when sending requests
|
||||
#[arg(long = "cookie-jar", global = true, value_name = "COOKIE_JAR_ID")]
|
||||
pub cookie_jar: Option<String>,
|
||||
|
||||
/// Enable verbose send output (events and streamed response body)
|
||||
#[arg(long, short, global = true)]
|
||||
pub verbose: bool,
|
||||
@@ -58,6 +62,9 @@ pub enum Commands {
|
||||
/// Send a request, folder, or workspace by ID
|
||||
Send(SendArgs),
|
||||
|
||||
/// Cookie jar commands
|
||||
CookieJar(CookieJarArgs),
|
||||
|
||||
/// Workspace commands
|
||||
Workspace(WorkspaceArgs),
|
||||
|
||||
@@ -85,6 +92,22 @@ pub struct SendArgs {
|
||||
pub fail_fast: bool,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
#[command(disable_help_subcommand = true)]
|
||||
pub struct CookieJarArgs {
|
||||
#[command(subcommand)]
|
||||
pub command: CookieJarCommands,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum CookieJarCommands {
|
||||
/// List cookie jars in a workspace
|
||||
List {
|
||||
/// Workspace ID (optional when exactly one workspace exists)
|
||||
workspace_id: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
#[command(disable_help_subcommand = true)]
|
||||
pub struct WorkspaceArgs {
|
||||
@@ -158,8 +181,8 @@ pub struct RequestArgs {
|
||||
pub enum RequestCommands {
|
||||
/// List requests in a workspace
|
||||
List {
|
||||
/// Workspace ID
|
||||
workspace_id: String,
|
||||
/// Workspace ID (optional when exactly one workspace exists)
|
||||
workspace_id: Option<String>,
|
||||
},
|
||||
|
||||
/// Show a request as JSON
|
||||
@@ -267,8 +290,8 @@ pub struct FolderArgs {
|
||||
pub enum FolderCommands {
|
||||
/// List folders in a workspace
|
||||
List {
|
||||
/// Workspace ID
|
||||
workspace_id: String,
|
||||
/// Workspace ID (optional when exactly one workspace exists)
|
||||
workspace_id: Option<String>,
|
||||
},
|
||||
|
||||
/// Show a folder as JSON
|
||||
@@ -324,8 +347,8 @@ pub struct EnvironmentArgs {
|
||||
pub enum EnvironmentCommands {
|
||||
/// List environments in a workspace
|
||||
List {
|
||||
/// Workspace ID
|
||||
workspace_id: String,
|
||||
/// Workspace ID (optional when exactly one workspace exists)
|
||||
workspace_id: Option<String>,
|
||||
},
|
||||
|
||||
/// Output JSON schema for environment create/update payloads
|
||||
|
||||
42
crates-cli/yaak-cli/src/commands/cookie_jar.rs
Normal file
42
crates-cli/yaak-cli/src/commands/cookie_jar.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use crate::cli::{CookieJarArgs, CookieJarCommands};
|
||||
use crate::context::CliContext;
|
||||
use crate::utils::workspace::resolve_workspace_id;
|
||||
|
||||
type CommandResult<T = ()> = std::result::Result<T, String>;
|
||||
|
||||
pub fn run(ctx: &CliContext, args: CookieJarArgs) -> i32 {
|
||||
let result = match args.command {
|
||||
CookieJarCommands::List { workspace_id } => list(ctx, workspace_id.as_deref()),
|
||||
};
|
||||
|
||||
match result {
|
||||
Ok(()) => 0,
|
||||
Err(error) => {
|
||||
eprintln!("Error: {error}");
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn list(ctx: &CliContext, workspace_id: Option<&str>) -> CommandResult {
|
||||
let workspace_id = resolve_workspace_id(ctx, workspace_id, "cookie-jar list")?;
|
||||
let cookie_jars = ctx
|
||||
.db()
|
||||
.list_cookie_jars(&workspace_id)
|
||||
.map_err(|e| format!("Failed to list cookie jars: {e}"))?;
|
||||
|
||||
if cookie_jars.is_empty() {
|
||||
println!("No cookie jars found in workspace {}", workspace_id);
|
||||
} else {
|
||||
for cookie_jar in cookie_jars {
|
||||
println!(
|
||||
"{} - {} ({} cookies)",
|
||||
cookie_jar.id,
|
||||
cookie_jar.name,
|
||||
cookie_jar.cookies.len()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -6,6 +6,7 @@ use crate::utils::json::{
|
||||
parse_required_json, require_id, validate_create_id,
|
||||
};
|
||||
use crate::utils::schema::append_agent_hints;
|
||||
use crate::utils::workspace::resolve_workspace_id;
|
||||
use schemars::schema_for;
|
||||
use yaak_models::models::Environment;
|
||||
use yaak_models::util::UpdateSource;
|
||||
@@ -14,7 +15,7 @@ type CommandResult<T = ()> = std::result::Result<T, String>;
|
||||
|
||||
pub fn run(ctx: &CliContext, args: EnvironmentArgs) -> i32 {
|
||||
let result = match args.command {
|
||||
EnvironmentCommands::List { workspace_id } => list(ctx, &workspace_id),
|
||||
EnvironmentCommands::List { workspace_id } => list(ctx, workspace_id.as_deref()),
|
||||
EnvironmentCommands::Schema { pretty } => schema(pretty),
|
||||
EnvironmentCommands::Show { environment_id } => show(ctx, &environment_id),
|
||||
EnvironmentCommands::Create { workspace_id, name, json } => {
|
||||
@@ -45,10 +46,11 @@ fn schema(pretty: bool) -> CommandResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn list(ctx: &CliContext, workspace_id: &str) -> CommandResult {
|
||||
fn list(ctx: &CliContext, workspace_id: Option<&str>) -> CommandResult {
|
||||
let workspace_id = resolve_workspace_id(ctx, workspace_id, "environment list")?;
|
||||
let environments = ctx
|
||||
.db()
|
||||
.list_environments_ensure_base(workspace_id)
|
||||
.list_environments_ensure_base(&workspace_id)
|
||||
.map_err(|e| format!("Failed to list environments: {e}"))?;
|
||||
|
||||
if environments.is_empty() {
|
||||
@@ -92,8 +94,14 @@ fn create(
|
||||
validate_create_id(&payload, "environment")?;
|
||||
let mut environment: Environment = serde_json::from_value(payload)
|
||||
.map_err(|e| format!("Failed to parse environment create JSON: {e}"))?;
|
||||
let fallback_workspace_id =
|
||||
if workspace_id_arg.is_none() && environment.workspace_id.is_empty() {
|
||||
Some(resolve_workspace_id(ctx, None, "environment create")?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
merge_workspace_id_arg(
|
||||
workspace_id_arg.as_deref(),
|
||||
workspace_id_arg.as_deref().or(fallback_workspace_id.as_deref()),
|
||||
&mut environment.workspace_id,
|
||||
"environment create",
|
||||
)?;
|
||||
@@ -111,9 +119,8 @@ fn create(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let workspace_id = workspace_id_arg.ok_or_else(|| {
|
||||
"environment create requires workspace_id unless JSON payload is provided".to_string()
|
||||
})?;
|
||||
let workspace_id =
|
||||
resolve_workspace_id(ctx, workspace_id_arg.as_deref(), "environment create")?;
|
||||
let name = name.ok_or_else(|| {
|
||||
"environment create requires --name unless JSON payload is provided".to_string()
|
||||
})?;
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::utils::json::{
|
||||
apply_merge_patch, is_json_shorthand, merge_workspace_id_arg, parse_optional_json,
|
||||
parse_required_json, require_id, validate_create_id,
|
||||
};
|
||||
use crate::utils::workspace::resolve_workspace_id;
|
||||
use yaak_models::models::Folder;
|
||||
use yaak_models::util::UpdateSource;
|
||||
|
||||
@@ -12,7 +13,7 @@ type CommandResult<T = ()> = std::result::Result<T, String>;
|
||||
|
||||
pub fn run(ctx: &CliContext, args: FolderArgs) -> i32 {
|
||||
let result = match args.command {
|
||||
FolderCommands::List { workspace_id } => list(ctx, &workspace_id),
|
||||
FolderCommands::List { workspace_id } => list(ctx, workspace_id.as_deref()),
|
||||
FolderCommands::Show { folder_id } => show(ctx, &folder_id),
|
||||
FolderCommands::Create { workspace_id, name, json } => {
|
||||
create(ctx, workspace_id, name, json)
|
||||
@@ -30,9 +31,10 @@ pub fn run(ctx: &CliContext, args: FolderArgs) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
fn list(ctx: &CliContext, workspace_id: &str) -> CommandResult {
|
||||
fn list(ctx: &CliContext, workspace_id: Option<&str>) -> CommandResult {
|
||||
let workspace_id = resolve_workspace_id(ctx, workspace_id, "folder list")?;
|
||||
let folders =
|
||||
ctx.db().list_folders(workspace_id).map_err(|e| format!("Failed to list folders: {e}"))?;
|
||||
ctx.db().list_folders(&workspace_id).map_err(|e| format!("Failed to list folders: {e}"))?;
|
||||
if folders.is_empty() {
|
||||
println!("No folders found in workspace {}", workspace_id);
|
||||
} else {
|
||||
@@ -72,8 +74,14 @@ fn create(
|
||||
validate_create_id(&payload, "folder")?;
|
||||
let mut folder: Folder = serde_json::from_value(payload)
|
||||
.map_err(|e| format!("Failed to parse folder create JSON: {e}"))?;
|
||||
let fallback_workspace_id = if workspace_id_arg.is_none() && folder.workspace_id.is_empty()
|
||||
{
|
||||
Some(resolve_workspace_id(ctx, None, "folder create")?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
merge_workspace_id_arg(
|
||||
workspace_id_arg.as_deref(),
|
||||
workspace_id_arg.as_deref().or(fallback_workspace_id.as_deref()),
|
||||
&mut folder.workspace_id,
|
||||
"folder create",
|
||||
)?;
|
||||
@@ -87,9 +95,7 @@ fn create(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let workspace_id = workspace_id_arg.ok_or_else(|| {
|
||||
"folder create requires workspace_id unless JSON payload is provided".to_string()
|
||||
})?;
|
||||
let workspace_id = resolve_workspace_id(ctx, workspace_id_arg.as_deref(), "folder create")?;
|
||||
let name = name.ok_or_else(|| {
|
||||
"folder create requires --name unless JSON payload is provided".to_string()
|
||||
})?;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod auth;
|
||||
pub mod cookie_jar;
|
||||
pub mod environment;
|
||||
pub mod folder;
|
||||
pub mod plugin;
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::utils::json::{
|
||||
parse_required_json, require_id, validate_create_id,
|
||||
};
|
||||
use crate::utils::schema::append_agent_hints;
|
||||
use crate::utils::workspace::resolve_workspace_id;
|
||||
use schemars::schema_for;
|
||||
use serde_json::{Map, Value, json};
|
||||
use std::collections::HashMap;
|
||||
@@ -24,13 +25,16 @@ pub async fn run(
|
||||
ctx: &CliContext,
|
||||
args: RequestArgs,
|
||||
environment: Option<&str>,
|
||||
cookie_jar_id: Option<&str>,
|
||||
verbose: bool,
|
||||
) -> i32 {
|
||||
let result = match args.command {
|
||||
RequestCommands::List { workspace_id } => list(ctx, &workspace_id),
|
||||
RequestCommands::List { workspace_id } => list(ctx, workspace_id.as_deref()),
|
||||
RequestCommands::Show { request_id } => show(ctx, &request_id),
|
||||
RequestCommands::Send { request_id } => {
|
||||
return match send_request_by_id(ctx, &request_id, environment, verbose).await {
|
||||
return match send_request_by_id(ctx, &request_id, environment, cookie_jar_id, verbose)
|
||||
.await
|
||||
{
|
||||
Ok(()) => 0,
|
||||
Err(error) => {
|
||||
eprintln!("Error: {error}");
|
||||
@@ -63,10 +67,11 @@ pub async fn run(
|
||||
}
|
||||
}
|
||||
|
||||
fn list(ctx: &CliContext, workspace_id: &str) -> CommandResult {
|
||||
fn list(ctx: &CliContext, workspace_id: Option<&str>) -> CommandResult {
|
||||
let workspace_id = resolve_workspace_id(ctx, workspace_id, "request list")?;
|
||||
let requests = ctx
|
||||
.db()
|
||||
.list_http_requests(workspace_id)
|
||||
.list_http_requests(&workspace_id)
|
||||
.map_err(|e| format!("Failed to list requests: {e}"))?;
|
||||
if requests.is_empty() {
|
||||
println!("No requests found in workspace {}", workspace_id);
|
||||
@@ -350,8 +355,14 @@ fn create(
|
||||
validate_create_id(&payload, "request")?;
|
||||
let mut request: HttpRequest = serde_json::from_value(payload)
|
||||
.map_err(|e| format!("Failed to parse request create JSON: {e}"))?;
|
||||
let fallback_workspace_id = if workspace_id_arg.is_none() && request.workspace_id.is_empty()
|
||||
{
|
||||
Some(resolve_workspace_id(ctx, None, "request create")?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
merge_workspace_id_arg(
|
||||
workspace_id_arg.as_deref(),
|
||||
workspace_id_arg.as_deref().or(fallback_workspace_id.as_deref()),
|
||||
&mut request.workspace_id,
|
||||
"request create",
|
||||
)?;
|
||||
@@ -365,9 +376,7 @@ fn create(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let workspace_id = workspace_id_arg.ok_or_else(|| {
|
||||
"request create requires workspace_id unless JSON payload is provided".to_string()
|
||||
})?;
|
||||
let workspace_id = resolve_workspace_id(ctx, workspace_id_arg.as_deref(), "request create")?;
|
||||
let name = name.unwrap_or_default();
|
||||
let url = url.unwrap_or_default();
|
||||
let method = method.unwrap_or_else(|| "GET".to_string());
|
||||
@@ -436,6 +445,7 @@ pub async fn send_request_by_id(
|
||||
ctx: &CliContext,
|
||||
request_id: &str,
|
||||
environment: Option<&str>,
|
||||
cookie_jar_id: Option<&str>,
|
||||
verbose: bool,
|
||||
) -> Result<(), String> {
|
||||
let request =
|
||||
@@ -447,6 +457,7 @@ pub async fn send_request_by_id(
|
||||
&http_request.id,
|
||||
&http_request.workspace_id,
|
||||
environment,
|
||||
cookie_jar_id,
|
||||
verbose,
|
||||
)
|
||||
.await
|
||||
@@ -465,8 +476,11 @@ async fn send_http_request_by_id(
|
||||
request_id: &str,
|
||||
workspace_id: &str,
|
||||
environment: Option<&str>,
|
||||
cookie_jar_id: Option<&str>,
|
||||
verbose: bool,
|
||||
) -> Result<(), String> {
|
||||
let cookie_jar_id = resolve_cookie_jar_id(ctx, workspace_id, cookie_jar_id)?;
|
||||
|
||||
let plugin_context = PluginContext::new(None, Some(workspace_id.to_string()));
|
||||
|
||||
let (event_tx, mut event_rx) = mpsc::channel::<SenderHttpResponseEvent>(100);
|
||||
@@ -495,7 +509,7 @@ async fn send_http_request_by_id(
|
||||
request_id,
|
||||
environment_id: environment,
|
||||
update_source: UpdateSource::Sync,
|
||||
cookie_jar_id: None,
|
||||
cookie_jar_id,
|
||||
response_dir: &response_dir,
|
||||
emit_events_to: Some(event_tx),
|
||||
emit_response_body_chunks_to: Some(body_chunk_tx),
|
||||
@@ -512,3 +526,22 @@ async fn send_http_request_by_id(
|
||||
result.map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_cookie_jar_id(
|
||||
ctx: &CliContext,
|
||||
workspace_id: &str,
|
||||
explicit_cookie_jar_id: Option<&str>,
|
||||
) -> Result<Option<String>, String> {
|
||||
if let Some(cookie_jar_id) = explicit_cookie_jar_id {
|
||||
return Ok(Some(cookie_jar_id.to_string()));
|
||||
}
|
||||
|
||||
let default_cookie_jar = ctx
|
||||
.db()
|
||||
.list_cookie_jars(workspace_id)
|
||||
.map_err(|e| format!("Failed to list cookie jars: {e}"))?
|
||||
.into_iter()
|
||||
.min_by_key(|jar| jar.created_at)
|
||||
.map(|jar| jar.id);
|
||||
Ok(default_cookie_jar)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::cli::SendArgs;
|
||||
use crate::commands::request;
|
||||
use crate::context::CliContext;
|
||||
use futures::future::join_all;
|
||||
use yaak_models::queries::any_request::AnyRequest;
|
||||
|
||||
enum ExecutionMode {
|
||||
Sequential,
|
||||
@@ -12,9 +13,10 @@ pub async fn run(
|
||||
ctx: &CliContext,
|
||||
args: SendArgs,
|
||||
environment: Option<&str>,
|
||||
cookie_jar_id: Option<&str>,
|
||||
verbose: bool,
|
||||
) -> i32 {
|
||||
match send_target(ctx, args, environment, verbose).await {
|
||||
match send_target(ctx, args, environment, cookie_jar_id, verbose).await {
|
||||
Ok(()) => 0,
|
||||
Err(error) => {
|
||||
eprintln!("Error: {error}");
|
||||
@@ -27,30 +29,70 @@ async fn send_target(
|
||||
ctx: &CliContext,
|
||||
args: SendArgs,
|
||||
environment: Option<&str>,
|
||||
cookie_jar_id: Option<&str>,
|
||||
verbose: bool,
|
||||
) -> Result<(), String> {
|
||||
let mode = if args.parallel { ExecutionMode::Parallel } else { ExecutionMode::Sequential };
|
||||
|
||||
if ctx.db().get_any_request(&args.id).is_ok() {
|
||||
return request::send_request_by_id(ctx, &args.id, environment, verbose).await;
|
||||
if let Ok(request) = ctx.db().get_any_request(&args.id) {
|
||||
let workspace_id = match &request {
|
||||
AnyRequest::HttpRequest(r) => r.workspace_id.clone(),
|
||||
AnyRequest::GrpcRequest(r) => r.workspace_id.clone(),
|
||||
AnyRequest::WebsocketRequest(r) => r.workspace_id.clone(),
|
||||
};
|
||||
let resolved_cookie_jar_id =
|
||||
request::resolve_cookie_jar_id(ctx, &workspace_id, cookie_jar_id)?;
|
||||
|
||||
return request::send_request_by_id(
|
||||
ctx,
|
||||
&args.id,
|
||||
environment,
|
||||
resolved_cookie_jar_id.as_deref(),
|
||||
verbose,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
if ctx.db().get_folder(&args.id).is_ok() {
|
||||
if let Ok(folder) = ctx.db().get_folder(&args.id) {
|
||||
let resolved_cookie_jar_id =
|
||||
request::resolve_cookie_jar_id(ctx, &folder.workspace_id, cookie_jar_id)?;
|
||||
|
||||
let request_ids = collect_folder_request_ids(ctx, &args.id)?;
|
||||
if request_ids.is_empty() {
|
||||
println!("No requests found in folder {}", args.id);
|
||||
return Ok(());
|
||||
}
|
||||
return send_many(ctx, request_ids, mode, args.fail_fast, environment, verbose).await;
|
||||
return send_many(
|
||||
ctx,
|
||||
request_ids,
|
||||
mode,
|
||||
args.fail_fast,
|
||||
environment,
|
||||
resolved_cookie_jar_id.as_deref(),
|
||||
verbose,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
if ctx.db().get_workspace(&args.id).is_ok() {
|
||||
if let Ok(workspace) = ctx.db().get_workspace(&args.id) {
|
||||
let resolved_cookie_jar_id =
|
||||
request::resolve_cookie_jar_id(ctx, &workspace.id, cookie_jar_id)?;
|
||||
|
||||
let request_ids = collect_workspace_request_ids(ctx, &args.id)?;
|
||||
if request_ids.is_empty() {
|
||||
println!("No requests found in workspace {}", args.id);
|
||||
return Ok(());
|
||||
}
|
||||
return send_many(ctx, request_ids, mode, args.fail_fast, environment, verbose).await;
|
||||
return send_many(
|
||||
ctx,
|
||||
request_ids,
|
||||
mode,
|
||||
args.fail_fast,
|
||||
environment,
|
||||
resolved_cookie_jar_id.as_deref(),
|
||||
verbose,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
Err(format!("Could not resolve ID '{}' as request, folder, or workspace", args.id))
|
||||
@@ -131,6 +173,7 @@ async fn send_many(
|
||||
mode: ExecutionMode,
|
||||
fail_fast: bool,
|
||||
environment: Option<&str>,
|
||||
cookie_jar_id: Option<&str>,
|
||||
verbose: bool,
|
||||
) -> Result<(), String> {
|
||||
let mut success_count = 0usize;
|
||||
@@ -139,7 +182,15 @@ async fn send_many(
|
||||
match mode {
|
||||
ExecutionMode::Sequential => {
|
||||
for request_id in request_ids {
|
||||
match request::send_request_by_id(ctx, &request_id, environment, verbose).await {
|
||||
match request::send_request_by_id(
|
||||
ctx,
|
||||
&request_id,
|
||||
environment,
|
||||
cookie_jar_id,
|
||||
verbose,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(()) => success_count += 1,
|
||||
Err(error) => {
|
||||
failures.push((request_id, error));
|
||||
@@ -156,7 +207,14 @@ async fn send_many(
|
||||
.map(|request_id| async move {
|
||||
(
|
||||
request_id.clone(),
|
||||
request::send_request_by_id(ctx, request_id, environment, verbose).await,
|
||||
request::send_request_by_id(
|
||||
ctx,
|
||||
request_id,
|
||||
environment,
|
||||
cookie_jar_id,
|
||||
verbose,
|
||||
)
|
||||
.await,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@@ -18,6 +18,14 @@ const EMBEDDED_PLUGIN_RUNTIME: &str = include_str!(concat!(
|
||||
static EMBEDDED_VENDORED_PLUGINS: Dir<'_> =
|
||||
include_dir!("$CARGO_MANIFEST_DIR/../../crates-tauri/yaak-app/vendored/plugins");
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct CliExecutionContext {
|
||||
pub request_id: Option<String>,
|
||||
pub workspace_id: Option<String>,
|
||||
pub environment_id: Option<String>,
|
||||
pub cookie_jar_id: Option<String>,
|
||||
}
|
||||
|
||||
pub struct CliContext {
|
||||
data_dir: PathBuf,
|
||||
query_manager: QueryManager,
|
||||
@@ -28,15 +36,14 @@ pub struct CliContext {
|
||||
}
|
||||
|
||||
impl CliContext {
|
||||
pub async fn initialize(data_dir: PathBuf, app_id: &str, with_plugins: bool) -> Self {
|
||||
let db_path = data_dir.join("db.sqlite");
|
||||
let blob_path = data_dir.join("blobs.sqlite");
|
||||
|
||||
let (query_manager, blob_manager, _rx) = yaak_models::init_standalone(&db_path, &blob_path)
|
||||
.expect("Failed to initialize database");
|
||||
|
||||
let encryption_manager = Arc::new(EncryptionManager::new(query_manager.clone(), app_id));
|
||||
|
||||
pub async fn new(
|
||||
data_dir: PathBuf,
|
||||
query_manager: QueryManager,
|
||||
blob_manager: BlobManager,
|
||||
encryption_manager: Arc<EncryptionManager>,
|
||||
with_plugins: bool,
|
||||
execution_context: CliExecutionContext,
|
||||
) -> Self {
|
||||
let plugin_manager = if with_plugins {
|
||||
let vendored_plugin_dir = data_dir.join("vendored-plugins");
|
||||
let installed_plugin_dir = data_dir.join("installed-plugins");
|
||||
@@ -80,6 +87,7 @@ impl CliContext {
|
||||
blob_manager.clone(),
|
||||
encryption_manager.clone(),
|
||||
data_dir.clone(),
|
||||
execution_context.clone(),
|
||||
)
|
||||
.await,
|
||||
)
|
||||
|
||||
@@ -9,11 +9,15 @@ mod version_check;
|
||||
|
||||
use clap::Parser;
|
||||
use cli::{Cli, Commands, RequestCommands};
|
||||
use context::CliContext;
|
||||
use context::{CliContext, CliExecutionContext};
|
||||
use std::sync::Arc;
|
||||
use yaak_crypto::manager::EncryptionManager;
|
||||
use yaak_models::queries::any_request::AnyRequest;
|
||||
use yaak_models::query_manager::QueryManager;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let Cli { data_dir, environment, verbose, log, command } = Cli::parse();
|
||||
let Cli { data_dir, environment, cookie_jar, verbose, log, command } = Cli::parse();
|
||||
|
||||
if let Some(log_level) = log {
|
||||
match log_level {
|
||||
@@ -35,28 +39,16 @@ async fn main() {
|
||||
|
||||
version_check::maybe_check_for_updates().await;
|
||||
|
||||
let needs_context = matches!(
|
||||
&command,
|
||||
Commands::Send(_)
|
||||
| Commands::Workspace(_)
|
||||
| Commands::Request(_)
|
||||
| Commands::Folder(_)
|
||||
| Commands::Environment(_)
|
||||
);
|
||||
|
||||
let needs_plugins = matches!(
|
||||
&command,
|
||||
Commands::Send(_)
|
||||
| Commands::Request(cli::RequestArgs {
|
||||
command: RequestCommands::Send { .. } | RequestCommands::Schema { .. },
|
||||
})
|
||||
);
|
||||
|
||||
let context = if needs_context {
|
||||
Some(CliContext::initialize(data_dir, app_id, needs_plugins).await)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let db_path = data_dir.join("db.sqlite");
|
||||
let blob_path = data_dir.join("blobs.sqlite");
|
||||
let (query_manager, blob_manager, _rx) =
|
||||
match yaak_models::init_standalone(&db_path, &blob_path) {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
eprintln!("Error: Failed to initialize database: {err}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let exit_code = match command {
|
||||
Commands::Auth(args) => commands::auth::run(args).await,
|
||||
@@ -66,41 +58,258 @@ async fn main() {
|
||||
Commands::Generate(args) => commands::plugin::run_generate(args).await,
|
||||
Commands::Publish(args) => commands::plugin::run_publish(args).await,
|
||||
Commands::Send(args) => {
|
||||
commands::send::run(
|
||||
context.as_ref().expect("context initialized for send"),
|
||||
args,
|
||||
let query_manager = query_manager.clone();
|
||||
let blob_manager = blob_manager.clone();
|
||||
|
||||
let execution_context_result = resolve_send_execution_context(
|
||||
&query_manager,
|
||||
&args.id,
|
||||
environment.as_deref(),
|
||||
verbose,
|
||||
)
|
||||
.await
|
||||
cookie_jar.as_deref(),
|
||||
);
|
||||
match execution_context_result {
|
||||
Ok(execution_context) => {
|
||||
let context = CliContext::new(
|
||||
data_dir.clone(),
|
||||
query_manager.clone(),
|
||||
blob_manager,
|
||||
Arc::new(EncryptionManager::new(query_manager, app_id)),
|
||||
true,
|
||||
execution_context,
|
||||
)
|
||||
.await;
|
||||
|
||||
let exit_code = commands::send::run(
|
||||
&context,
|
||||
args,
|
||||
environment.as_deref(),
|
||||
cookie_jar.as_deref(),
|
||||
verbose,
|
||||
)
|
||||
.await;
|
||||
context.shutdown().await;
|
||||
exit_code
|
||||
}
|
||||
Err(error) => {
|
||||
eprintln!("Error: {error}");
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
Commands::Workspace(args) => commands::workspace::run(
|
||||
context.as_ref().expect("context initialized for workspace"),
|
||||
args,
|
||||
),
|
||||
Commands::Request(args) => {
|
||||
commands::request::run(
|
||||
context.as_ref().expect("context initialized for request"),
|
||||
args,
|
||||
environment.as_deref(),
|
||||
verbose,
|
||||
Commands::CookieJar(args) => {
|
||||
let query_manager = query_manager.clone();
|
||||
let blob_manager = blob_manager.clone();
|
||||
let execution_context = CliExecutionContext::default();
|
||||
|
||||
let context = CliContext::new(
|
||||
data_dir.clone(),
|
||||
query_manager.clone(),
|
||||
blob_manager,
|
||||
Arc::new(EncryptionManager::new(query_manager, app_id)),
|
||||
false,
|
||||
execution_context,
|
||||
)
|
||||
.await
|
||||
.await;
|
||||
let exit_code = commands::cookie_jar::run(&context, args);
|
||||
context.shutdown().await;
|
||||
exit_code
|
||||
}
|
||||
Commands::Workspace(args) => {
|
||||
let query_manager = query_manager.clone();
|
||||
let blob_manager = blob_manager.clone();
|
||||
let execution_context = CliExecutionContext::default();
|
||||
|
||||
let context = CliContext::new(
|
||||
data_dir.clone(),
|
||||
query_manager.clone(),
|
||||
blob_manager,
|
||||
Arc::new(EncryptionManager::new(query_manager, app_id)),
|
||||
false,
|
||||
execution_context,
|
||||
)
|
||||
.await;
|
||||
let exit_code = commands::workspace::run(&context, args);
|
||||
context.shutdown().await;
|
||||
exit_code
|
||||
}
|
||||
Commands::Request(args) => {
|
||||
let query_manager = query_manager.clone();
|
||||
let blob_manager = blob_manager.clone();
|
||||
|
||||
let execution_context_result = match &args.command {
|
||||
RequestCommands::Send { request_id } => resolve_request_execution_context(
|
||||
&query_manager,
|
||||
request_id,
|
||||
environment.as_deref(),
|
||||
cookie_jar.as_deref(),
|
||||
),
|
||||
_ => Ok(CliExecutionContext::default()),
|
||||
};
|
||||
match execution_context_result {
|
||||
Ok(execution_context) => {
|
||||
let with_plugins = matches!(
|
||||
&args.command,
|
||||
RequestCommands::Send { .. } | RequestCommands::Schema { .. }
|
||||
);
|
||||
let context = CliContext::new(
|
||||
data_dir.clone(),
|
||||
query_manager.clone(),
|
||||
blob_manager,
|
||||
Arc::new(EncryptionManager::new(query_manager, app_id)),
|
||||
with_plugins,
|
||||
execution_context,
|
||||
)
|
||||
.await;
|
||||
|
||||
let exit_code = commands::request::run(
|
||||
&context,
|
||||
args,
|
||||
environment.as_deref(),
|
||||
cookie_jar.as_deref(),
|
||||
verbose,
|
||||
)
|
||||
.await;
|
||||
context.shutdown().await;
|
||||
exit_code
|
||||
}
|
||||
Err(error) => {
|
||||
eprintln!("Error: {error}");
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
Commands::Folder(args) => {
|
||||
commands::folder::run(context.as_ref().expect("context initialized for folder"), args)
|
||||
}
|
||||
Commands::Environment(args) => commands::environment::run(
|
||||
context.as_ref().expect("context initialized for environment"),
|
||||
args,
|
||||
),
|
||||
};
|
||||
let query_manager = query_manager.clone();
|
||||
let blob_manager = blob_manager.clone();
|
||||
let execution_context = CliExecutionContext::default();
|
||||
|
||||
if let Some(context) = &context {
|
||||
context.shutdown().await;
|
||||
}
|
||||
let context = CliContext::new(
|
||||
data_dir.clone(),
|
||||
query_manager.clone(),
|
||||
blob_manager,
|
||||
Arc::new(EncryptionManager::new(query_manager, app_id)),
|
||||
false,
|
||||
execution_context,
|
||||
)
|
||||
.await;
|
||||
let exit_code = commands::folder::run(&context, args);
|
||||
context.shutdown().await;
|
||||
exit_code
|
||||
}
|
||||
Commands::Environment(args) => {
|
||||
let query_manager = query_manager.clone();
|
||||
let blob_manager = blob_manager.clone();
|
||||
let execution_context = CliExecutionContext::default();
|
||||
|
||||
let context = CliContext::new(
|
||||
data_dir.clone(),
|
||||
query_manager.clone(),
|
||||
blob_manager,
|
||||
Arc::new(EncryptionManager::new(query_manager, app_id)),
|
||||
false,
|
||||
execution_context,
|
||||
)
|
||||
.await;
|
||||
let exit_code = commands::environment::run(&context, args);
|
||||
context.shutdown().await;
|
||||
exit_code
|
||||
}
|
||||
};
|
||||
|
||||
if exit_code != 0 {
|
||||
std::process::exit(exit_code);
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_send_execution_context(
|
||||
query_manager: &QueryManager,
|
||||
id: &str,
|
||||
environment: Option<&str>,
|
||||
explicit_cookie_jar_id: Option<&str>,
|
||||
) -> Result<CliExecutionContext, String> {
|
||||
if let Ok(request) = query_manager.connect().get_any_request(id) {
|
||||
let (request_id, workspace_id) = match request {
|
||||
AnyRequest::HttpRequest(r) => (Some(r.id), r.workspace_id),
|
||||
AnyRequest::GrpcRequest(r) => (Some(r.id), r.workspace_id),
|
||||
AnyRequest::WebsocketRequest(r) => (Some(r.id), r.workspace_id),
|
||||
};
|
||||
let cookie_jar_id =
|
||||
resolve_cookie_jar_id(query_manager, &workspace_id, explicit_cookie_jar_id)?;
|
||||
return Ok(CliExecutionContext {
|
||||
request_id,
|
||||
workspace_id: Some(workspace_id),
|
||||
environment_id: environment.map(str::to_string),
|
||||
cookie_jar_id,
|
||||
});
|
||||
}
|
||||
|
||||
if let Ok(folder) = query_manager.connect().get_folder(id) {
|
||||
let cookie_jar_id =
|
||||
resolve_cookie_jar_id(query_manager, &folder.workspace_id, explicit_cookie_jar_id)?;
|
||||
return Ok(CliExecutionContext {
|
||||
request_id: None,
|
||||
workspace_id: Some(folder.workspace_id),
|
||||
environment_id: environment.map(str::to_string),
|
||||
cookie_jar_id,
|
||||
});
|
||||
}
|
||||
|
||||
if let Ok(workspace) = query_manager.connect().get_workspace(id) {
|
||||
let cookie_jar_id =
|
||||
resolve_cookie_jar_id(query_manager, &workspace.id, explicit_cookie_jar_id)?;
|
||||
return Ok(CliExecutionContext {
|
||||
request_id: None,
|
||||
workspace_id: Some(workspace.id),
|
||||
environment_id: environment.map(str::to_string),
|
||||
cookie_jar_id,
|
||||
});
|
||||
}
|
||||
|
||||
Err(format!("Could not resolve ID '{}' as request, folder, or workspace", id))
|
||||
}
|
||||
|
||||
fn resolve_request_execution_context(
|
||||
query_manager: &QueryManager,
|
||||
request_id: &str,
|
||||
environment: Option<&str>,
|
||||
explicit_cookie_jar_id: Option<&str>,
|
||||
) -> Result<CliExecutionContext, String> {
|
||||
let request = query_manager
|
||||
.connect()
|
||||
.get_any_request(request_id)
|
||||
.map_err(|e| format!("Failed to get request: {e}"))?;
|
||||
|
||||
let workspace_id = match request {
|
||||
AnyRequest::HttpRequest(r) => r.workspace_id,
|
||||
AnyRequest::GrpcRequest(r) => r.workspace_id,
|
||||
AnyRequest::WebsocketRequest(r) => r.workspace_id,
|
||||
};
|
||||
let cookie_jar_id =
|
||||
resolve_cookie_jar_id(query_manager, &workspace_id, explicit_cookie_jar_id)?;
|
||||
|
||||
Ok(CliExecutionContext {
|
||||
request_id: Some(request_id.to_string()),
|
||||
workspace_id: Some(workspace_id),
|
||||
environment_id: environment.map(str::to_string),
|
||||
cookie_jar_id,
|
||||
})
|
||||
}
|
||||
|
||||
fn resolve_cookie_jar_id(
|
||||
query_manager: &QueryManager,
|
||||
workspace_id: &str,
|
||||
explicit_cookie_jar_id: Option<&str>,
|
||||
) -> Result<Option<String>, String> {
|
||||
if let Some(cookie_jar_id) = explicit_cookie_jar_id {
|
||||
return Ok(Some(cookie_jar_id.to_string()));
|
||||
}
|
||||
|
||||
let default_cookie_jar = query_manager
|
||||
.connect()
|
||||
.list_cookie_jars(workspace_id)
|
||||
.map_err(|e| format!("Failed to list cookie jars: {e}"))?
|
||||
.into_iter()
|
||||
.min_by_key(|jar| jar.created_at)
|
||||
.map(|jar| jar.id);
|
||||
Ok(default_cookie_jar)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::context::CliExecutionContext;
|
||||
use serde_json::Value;
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::PathBuf;
|
||||
@@ -15,9 +16,10 @@ use yaak_models::query_manager::QueryManager;
|
||||
use yaak_models::render::make_vars_hashmap;
|
||||
use yaak_models::util::UpdateSource;
|
||||
use yaak_plugins::events::{
|
||||
EmptyPayload, ErrorResponse, InternalEvent, InternalEventPayload, ListOpenWorkspacesResponse,
|
||||
RenderGrpcRequestResponse, RenderHttpRequestResponse, SendHttpRequestResponse,
|
||||
TemplateRenderResponse, WorkspaceInfo,
|
||||
EmptyPayload, ErrorResponse, GetCookieValueResponse, InternalEvent, InternalEventPayload,
|
||||
ListCookieNamesResponse, ListOpenWorkspacesResponse, PluginContext, RenderGrpcRequestResponse,
|
||||
RenderHttpRequestResponse, SendHttpRequestResponse, TemplateRenderResponse, WindowInfoResponse,
|
||||
WorkspaceInfo,
|
||||
};
|
||||
use yaak_plugins::manager::PluginManager;
|
||||
use yaak_plugins::template_callback::PluginTemplateCallback;
|
||||
@@ -34,6 +36,7 @@ struct CliHostContext {
|
||||
plugin_manager: Arc<PluginManager>,
|
||||
encryption_manager: Arc<EncryptionManager>,
|
||||
response_dir: PathBuf,
|
||||
execution_context: CliExecutionContext,
|
||||
}
|
||||
|
||||
impl CliPluginEventBridge {
|
||||
@@ -43,6 +46,7 @@ impl CliPluginEventBridge {
|
||||
blob_manager: BlobManager,
|
||||
encryption_manager: Arc<EncryptionManager>,
|
||||
data_dir: PathBuf,
|
||||
execution_context: CliExecutionContext,
|
||||
) -> Self {
|
||||
let (rx_id, mut rx) = plugin_manager.subscribe("cli").await;
|
||||
let rx_id_for_task = rx_id.clone();
|
||||
@@ -53,6 +57,7 @@ impl CliPluginEventBridge {
|
||||
plugin_manager,
|
||||
encryption_manager,
|
||||
response_dir: data_dir.join("responses"),
|
||||
execution_context,
|
||||
});
|
||||
|
||||
let task = tokio::spawn(async move {
|
||||
@@ -109,13 +114,14 @@ async fn build_plugin_reply(
|
||||
event: &InternalEvent,
|
||||
plugin_name: &str,
|
||||
) -> Option<InternalEventPayload> {
|
||||
let execution_context = &host_context.execution_context;
|
||||
let shared_workspace_id =
|
||||
event.context.workspace_id.as_deref().or(execution_context.workspace_id.as_deref());
|
||||
|
||||
match handle_shared_plugin_event(
|
||||
&host_context.query_manager,
|
||||
&event.payload,
|
||||
SharedPluginEventContext {
|
||||
plugin_name,
|
||||
workspace_id: event.context.workspace_id.as_deref(),
|
||||
},
|
||||
SharedPluginEventContext { plugin_name, workspace_id: shared_workspace_id },
|
||||
) {
|
||||
GroupedPluginEvent::Handled(payload) => payload,
|
||||
GroupedPluginEvent::ToHandle(host_request) => match host_request {
|
||||
@@ -147,7 +153,12 @@ async fn build_plugin_reply(
|
||||
HostRequest::SendHttpRequest(send_http_request_request) => {
|
||||
let mut http_request = send_http_request_request.http_request.clone();
|
||||
if http_request.workspace_id.is_empty() {
|
||||
let Some(workspace_id) = event.context.workspace_id.clone() else {
|
||||
let Some(workspace_id) = event
|
||||
.context
|
||||
.workspace_id
|
||||
.clone()
|
||||
.or_else(|| execution_context.workspace_id.clone())
|
||||
else {
|
||||
return Some(InternalEventPayload::ErrorResponse(ErrorResponse {
|
||||
error: "workspace_id is required to send HTTP requests in CLI"
|
||||
.to_string(),
|
||||
@@ -156,18 +167,38 @@ async fn build_plugin_reply(
|
||||
http_request.workspace_id = workspace_id;
|
||||
}
|
||||
|
||||
let mut plugin_context = event.context.clone();
|
||||
if plugin_context.workspace_id.is_none() {
|
||||
plugin_context.workspace_id = Some(http_request.workspace_id.clone());
|
||||
}
|
||||
let cookie_jar_id =
|
||||
if let Some(cookie_jar_id) = execution_context.cookie_jar_id.clone() {
|
||||
Some(cookie_jar_id)
|
||||
} else {
|
||||
match host_context
|
||||
.query_manager
|
||||
.connect()
|
||||
.list_cookie_jars(http_request.workspace_id.as_str())
|
||||
{
|
||||
Ok(cookie_jars) => cookie_jars
|
||||
.into_iter()
|
||||
.min_by_key(|jar| jar.created_at)
|
||||
.map(|jar| jar.id),
|
||||
Err(err) => {
|
||||
return Some(InternalEventPayload::ErrorResponse(ErrorResponse {
|
||||
error: format!("Failed to list cookie jars in CLI: {err}"),
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
||||
let plugin_context = PluginContext {
|
||||
workspace_id: Some(http_request.workspace_id.clone()),
|
||||
..event.context.clone()
|
||||
};
|
||||
|
||||
match send_http_request_with_plugins(SendHttpRequestWithPluginsParams {
|
||||
query_manager: &host_context.query_manager,
|
||||
blob_manager: &host_context.blob_manager,
|
||||
request: http_request,
|
||||
environment_id: None,
|
||||
environment_id: execution_context.environment_id.as_deref(),
|
||||
update_source: UpdateSource::Plugin,
|
||||
cookie_jar_id: None,
|
||||
cookie_jar_id,
|
||||
response_dir: &host_context.response_dir,
|
||||
emit_events_to: None,
|
||||
emit_response_body_chunks_to: None,
|
||||
@@ -191,7 +222,12 @@ async fn build_plugin_reply(
|
||||
HostRequest::RenderGrpcRequest(render_grpc_request_request) => {
|
||||
let mut grpc_request = render_grpc_request_request.grpc_request.clone();
|
||||
if grpc_request.workspace_id.is_empty() {
|
||||
let Some(workspace_id) = event.context.workspace_id.clone() else {
|
||||
let Some(workspace_id) = event
|
||||
.context
|
||||
.workspace_id
|
||||
.clone()
|
||||
.or_else(|| execution_context.workspace_id.clone())
|
||||
else {
|
||||
return Some(InternalEventPayload::ErrorResponse(ErrorResponse {
|
||||
error: "workspace_id is required to render gRPC requests in CLI"
|
||||
.to_string(),
|
||||
@@ -200,16 +236,16 @@ async fn build_plugin_reply(
|
||||
grpc_request.workspace_id = workspace_id;
|
||||
}
|
||||
|
||||
let mut plugin_context = event.context.clone();
|
||||
if plugin_context.workspace_id.is_none() {
|
||||
plugin_context.workspace_id = Some(grpc_request.workspace_id.clone());
|
||||
}
|
||||
let plugin_context = PluginContext {
|
||||
workspace_id: Some(grpc_request.workspace_id.clone()),
|
||||
..event.context.clone()
|
||||
};
|
||||
|
||||
let environment_chain =
|
||||
match host_context.query_manager.connect().resolve_environments(
|
||||
&grpc_request.workspace_id,
|
||||
grpc_request.folder_id.as_deref(),
|
||||
None,
|
||||
execution_context.environment_id.as_deref(),
|
||||
) {
|
||||
Ok(chain) => chain,
|
||||
Err(err) => {
|
||||
@@ -246,7 +282,12 @@ async fn build_plugin_reply(
|
||||
HostRequest::RenderHttpRequest(render_http_request_request) => {
|
||||
let mut http_request = render_http_request_request.http_request.clone();
|
||||
if http_request.workspace_id.is_empty() {
|
||||
let Some(workspace_id) = event.context.workspace_id.clone() else {
|
||||
let Some(workspace_id) = event
|
||||
.context
|
||||
.workspace_id
|
||||
.clone()
|
||||
.or_else(|| execution_context.workspace_id.clone())
|
||||
else {
|
||||
return Some(InternalEventPayload::ErrorResponse(ErrorResponse {
|
||||
error: "workspace_id is required to render HTTP requests in CLI"
|
||||
.to_string(),
|
||||
@@ -255,16 +296,16 @@ async fn build_plugin_reply(
|
||||
http_request.workspace_id = workspace_id;
|
||||
}
|
||||
|
||||
let mut plugin_context = event.context.clone();
|
||||
if plugin_context.workspace_id.is_none() {
|
||||
plugin_context.workspace_id = Some(http_request.workspace_id.clone());
|
||||
}
|
||||
let plugin_context = PluginContext {
|
||||
workspace_id: Some(http_request.workspace_id.clone()),
|
||||
..event.context.clone()
|
||||
};
|
||||
|
||||
let environment_chain =
|
||||
match host_context.query_manager.connect().resolve_environments(
|
||||
&http_request.workspace_id,
|
||||
http_request.folder_id.as_deref(),
|
||||
None,
|
||||
execution_context.environment_id.as_deref(),
|
||||
) {
|
||||
Ok(chain) => chain,
|
||||
Err(err) => {
|
||||
@@ -299,30 +340,36 @@ async fn build_plugin_reply(
|
||||
}
|
||||
}
|
||||
HostRequest::TemplateRender(template_render_request) => {
|
||||
let Some(workspace_id) = event.context.workspace_id.clone() else {
|
||||
let Some(workspace_id) = event
|
||||
.context
|
||||
.workspace_id
|
||||
.clone()
|
||||
.or_else(|| execution_context.workspace_id.clone())
|
||||
else {
|
||||
return Some(InternalEventPayload::ErrorResponse(ErrorResponse {
|
||||
error: "workspace_id is required to render templates in CLI".to_string(),
|
||||
}));
|
||||
};
|
||||
|
||||
let mut plugin_context = event.context.clone();
|
||||
if plugin_context.workspace_id.is_none() {
|
||||
plugin_context.workspace_id = Some(workspace_id.clone());
|
||||
}
|
||||
|
||||
let environment_chain = match host_context
|
||||
.query_manager
|
||||
.connect()
|
||||
.resolve_environments(&workspace_id, None, None)
|
||||
{
|
||||
Ok(chain) => chain,
|
||||
Err(err) => {
|
||||
return Some(InternalEventPayload::ErrorResponse(ErrorResponse {
|
||||
error: format!("Failed to resolve environments in CLI: {err}"),
|
||||
}));
|
||||
}
|
||||
let plugin_context = PluginContext {
|
||||
workspace_id: Some(workspace_id.clone()),
|
||||
..event.context.clone()
|
||||
};
|
||||
|
||||
let environment_chain =
|
||||
match host_context.query_manager.connect().resolve_environments(
|
||||
&workspace_id,
|
||||
None,
|
||||
execution_context.environment_id.as_deref(),
|
||||
) {
|
||||
Ok(chain) => chain,
|
||||
Err(err) => {
|
||||
return Some(InternalEventPayload::ErrorResponse(ErrorResponse {
|
||||
error: format!("Failed to resolve environments in CLI: {err}"),
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
let template_callback = PluginTemplateCallback::new(
|
||||
host_context.plugin_manager.clone(),
|
||||
host_context.encryption_manager.clone(),
|
||||
@@ -381,20 +428,64 @@ async fn build_plugin_reply(
|
||||
}))
|
||||
}
|
||||
HostRequest::ListCookieNames(_) => {
|
||||
Some(InternalEventPayload::ErrorResponse(ErrorResponse {
|
||||
error: "Unsupported plugin request in CLI: list_cookie_names_request"
|
||||
.to_string(),
|
||||
let Some(cookie_jar_id) = execution_context.cookie_jar_id.as_deref() else {
|
||||
return Some(InternalEventPayload::ListCookieNamesResponse(
|
||||
ListCookieNamesResponse { names: Vec::new() },
|
||||
));
|
||||
};
|
||||
|
||||
let cookie_jar =
|
||||
match host_context.query_manager.connect().get_cookie_jar(cookie_jar_id) {
|
||||
Ok(cookie_jar) => cookie_jar,
|
||||
Err(err) => {
|
||||
return Some(InternalEventPayload::ErrorResponse(ErrorResponse {
|
||||
error: format!("Failed to load cookie jar in CLI: {err}"),
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
let names = cookie_jar
|
||||
.cookies
|
||||
.into_iter()
|
||||
.filter_map(|c| parse_cookie_name_value(&c.raw_cookie).map(|(name, _)| name))
|
||||
.collect();
|
||||
|
||||
Some(InternalEventPayload::ListCookieNamesResponse(ListCookieNamesResponse {
|
||||
names,
|
||||
}))
|
||||
}
|
||||
HostRequest::GetCookieValue(_) => {
|
||||
Some(InternalEventPayload::ErrorResponse(ErrorResponse {
|
||||
error: "Unsupported plugin request in CLI: get_cookie_value_request"
|
||||
.to_string(),
|
||||
}))
|
||||
HostRequest::GetCookieValue(req) => {
|
||||
let Some(cookie_jar_id) = execution_context.cookie_jar_id.as_deref() else {
|
||||
return Some(InternalEventPayload::GetCookieValueResponse(
|
||||
GetCookieValueResponse { value: None },
|
||||
));
|
||||
};
|
||||
|
||||
let cookie_jar =
|
||||
match host_context.query_manager.connect().get_cookie_jar(cookie_jar_id) {
|
||||
Ok(cookie_jar) => cookie_jar,
|
||||
Err(err) => {
|
||||
return Some(InternalEventPayload::ErrorResponse(ErrorResponse {
|
||||
error: format!("Failed to load cookie jar in CLI: {err}"),
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
let value = cookie_jar.cookies.into_iter().find_map(|c| {
|
||||
let (name, value) = parse_cookie_name_value(&c.raw_cookie)?;
|
||||
if name == req.name { Some(value) } else { None }
|
||||
});
|
||||
Some(InternalEventPayload::GetCookieValueResponse(GetCookieValueResponse { value }))
|
||||
}
|
||||
HostRequest::WindowInfo(_) => {
|
||||
Some(InternalEventPayload::ErrorResponse(ErrorResponse {
|
||||
error: "Unsupported plugin request in CLI: window_info_request".to_string(),
|
||||
HostRequest::WindowInfo(req) => {
|
||||
Some(InternalEventPayload::WindowInfoResponse(WindowInfoResponse {
|
||||
label: req.label.clone(),
|
||||
request_id: execution_context.request_id.clone(),
|
||||
workspace_id: execution_context
|
||||
.workspace_id
|
||||
.clone()
|
||||
.or_else(|| event.context.workspace_id.clone()),
|
||||
environment_id: execution_context.environment_id.clone(),
|
||||
}))
|
||||
}
|
||||
HostRequest::OtherRequest(payload) => {
|
||||
@@ -470,3 +561,9 @@ async fn render_grpc_request_for_cli<T: TemplateCallback>(
|
||||
|
||||
Ok(GrpcRequest { url, metadata, authentication, ..grpc_request.to_owned() })
|
||||
}
|
||||
|
||||
fn parse_cookie_name_value(raw_cookie: &str) -> Option<(String, String)> {
|
||||
let first_part = raw_cookie.split(';').next()?.trim();
|
||||
let (name, value) = first_part.split_once('=')?;
|
||||
Some((name.trim().to_string(), value.to_string()))
|
||||
}
|
||||
|
||||
@@ -2,3 +2,4 @@ pub mod confirm;
|
||||
pub mod http;
|
||||
pub mod json;
|
||||
pub mod schema;
|
||||
pub mod workspace;
|
||||
|
||||
19
crates-cli/yaak-cli/src/utils/workspace.rs
Normal file
19
crates-cli/yaak-cli/src/utils/workspace.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use crate::context::CliContext;
|
||||
|
||||
pub fn resolve_workspace_id(
|
||||
ctx: &CliContext,
|
||||
workspace_id: Option<&str>,
|
||||
command_name: &str,
|
||||
) -> Result<String, String> {
|
||||
if let Some(workspace_id) = workspace_id {
|
||||
return Ok(workspace_id.to_string());
|
||||
}
|
||||
|
||||
let workspaces =
|
||||
ctx.db().list_workspaces().map_err(|e| format!("Failed to list workspaces: {e}"))?;
|
||||
match workspaces.as_slice() {
|
||||
[] => Err(format!("No workspaces found. {command_name} requires a workspace ID.")),
|
||||
[workspace] => Ok(workspace.id.clone()),
|
||||
_ => Err(format!("Multiple workspaces found. {command_name} requires a workspace ID.")),
|
||||
}
|
||||
}
|
||||
160
crates/yaak-plugins/bindings/gen_events.ts
generated
160
crates/yaak-plugins/bindings/gen_events.ts
generated
@@ -18,12 +18,12 @@ export type CallHttpAuthenticationActionRequest = { index: number, pluginRefId:
|
||||
|
||||
export type CallHttpAuthenticationRequest = { contextId: string, values: { [key in string]?: JsonPrimitive }, method: string, url: string, headers: Array<HttpHeader>, };
|
||||
|
||||
export type CallHttpAuthenticationResponse = {
|
||||
export type CallHttpAuthenticationResponse = {
|
||||
/**
|
||||
* HTTP headers to add to the request. Existing headers will be replaced, while
|
||||
* new headers will be added.
|
||||
*/
|
||||
setHeaders?: Array<HttpHeader>,
|
||||
setHeaders?: Array<HttpHeader>,
|
||||
/**
|
||||
* Query parameters to add to the request. Existing params will be replaced, while
|
||||
* new params will be added.
|
||||
@@ -78,7 +78,7 @@ export type ExportHttpRequestRequest = { httpRequest: HttpRequest, };
|
||||
|
||||
export type ExportHttpRequestResponse = { content: string, };
|
||||
|
||||
export type FileFilter = { name: string,
|
||||
export type FileFilter = { name: string,
|
||||
/**
|
||||
* File extensions to require
|
||||
*/
|
||||
@@ -100,149 +100,149 @@ export type FormInputAccordion = { label: string, inputs?: Array<FormInput>, hid
|
||||
|
||||
export type FormInputBanner = { inputs?: Array<FormInput>, hidden?: boolean, color?: Color, };
|
||||
|
||||
export type FormInputBase = {
|
||||
export type FormInputBase = {
|
||||
/**
|
||||
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||
*/
|
||||
name: string,
|
||||
name: string,
|
||||
/**
|
||||
* Whether this input is visible for the given configuration. Use this to
|
||||
* make branching forms.
|
||||
*/
|
||||
hidden?: boolean,
|
||||
hidden?: boolean,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
label?: string,
|
||||
/**
|
||||
* Visually hide the label of the input
|
||||
*/
|
||||
hideLabel?: boolean,
|
||||
hideLabel?: boolean,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, disabled?: boolean,
|
||||
defaultValue?: string, disabled?: boolean,
|
||||
/**
|
||||
* Longer description of the input, likely shown in a tooltip
|
||||
*/
|
||||
description?: string, };
|
||||
|
||||
export type FormInputCheckbox = {
|
||||
export type FormInputCheckbox = {
|
||||
/**
|
||||
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||
*/
|
||||
name: string,
|
||||
name: string,
|
||||
/**
|
||||
* Whether this input is visible for the given configuration. Use this to
|
||||
* make branching forms.
|
||||
*/
|
||||
hidden?: boolean,
|
||||
hidden?: boolean,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
label?: string,
|
||||
/**
|
||||
* Visually hide the label of the input
|
||||
*/
|
||||
hideLabel?: boolean,
|
||||
hideLabel?: boolean,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, disabled?: boolean,
|
||||
defaultValue?: string, disabled?: boolean,
|
||||
/**
|
||||
* Longer description of the input, likely shown in a tooltip
|
||||
*/
|
||||
description?: string, };
|
||||
|
||||
export type FormInputEditor = {
|
||||
export type FormInputEditor = {
|
||||
/**
|
||||
* Placeholder for the text input
|
||||
*/
|
||||
placeholder?: string | null,
|
||||
placeholder?: string | null,
|
||||
/**
|
||||
* Don't show the editor gutter (line numbers, folds, etc.)
|
||||
*/
|
||||
hideGutter?: boolean,
|
||||
hideGutter?: boolean,
|
||||
/**
|
||||
* Language for syntax highlighting
|
||||
*/
|
||||
language?: EditorLanguage, readOnly?: boolean,
|
||||
language?: EditorLanguage, readOnly?: boolean,
|
||||
/**
|
||||
* Fixed number of visible rows
|
||||
*/
|
||||
rows?: number, completionOptions?: Array<GenericCompletionOption>,
|
||||
rows?: number, completionOptions?: Array<GenericCompletionOption>,
|
||||
/**
|
||||
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||
*/
|
||||
name: string,
|
||||
name: string,
|
||||
/**
|
||||
* Whether this input is visible for the given configuration. Use this to
|
||||
* make branching forms.
|
||||
*/
|
||||
hidden?: boolean,
|
||||
hidden?: boolean,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
label?: string,
|
||||
/**
|
||||
* Visually hide the label of the input
|
||||
*/
|
||||
hideLabel?: boolean,
|
||||
hideLabel?: boolean,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, disabled?: boolean,
|
||||
defaultValue?: string, disabled?: boolean,
|
||||
/**
|
||||
* Longer description of the input, likely shown in a tooltip
|
||||
*/
|
||||
description?: string, };
|
||||
|
||||
export type FormInputFile = {
|
||||
export type FormInputFile = {
|
||||
/**
|
||||
* The title of the file selection window
|
||||
*/
|
||||
title: string,
|
||||
title: string,
|
||||
/**
|
||||
* Allow selecting multiple files
|
||||
*/
|
||||
multiple?: boolean, directory?: boolean, defaultPath?: string, filters?: Array<FileFilter>,
|
||||
multiple?: boolean, directory?: boolean, defaultPath?: string, filters?: Array<FileFilter>,
|
||||
/**
|
||||
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||
*/
|
||||
name: string,
|
||||
name: string,
|
||||
/**
|
||||
* Whether this input is visible for the given configuration. Use this to
|
||||
* make branching forms.
|
||||
*/
|
||||
hidden?: boolean,
|
||||
hidden?: boolean,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
label?: string,
|
||||
/**
|
||||
* Visually hide the label of the input
|
||||
*/
|
||||
hideLabel?: boolean,
|
||||
hideLabel?: boolean,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, disabled?: boolean,
|
||||
defaultValue?: string, disabled?: boolean,
|
||||
/**
|
||||
* Longer description of the input, likely shown in a tooltip
|
||||
*/
|
||||
@@ -250,63 +250,63 @@ description?: string, };
|
||||
|
||||
export type FormInputHStack = { inputs?: Array<FormInput>, hidden?: boolean, };
|
||||
|
||||
export type FormInputHttpRequest = {
|
||||
export type FormInputHttpRequest = {
|
||||
/**
|
||||
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||
*/
|
||||
name: string,
|
||||
name: string,
|
||||
/**
|
||||
* Whether this input is visible for the given configuration. Use this to
|
||||
* make branching forms.
|
||||
*/
|
||||
hidden?: boolean,
|
||||
hidden?: boolean,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
label?: string,
|
||||
/**
|
||||
* Visually hide the label of the input
|
||||
*/
|
||||
hideLabel?: boolean,
|
||||
hideLabel?: boolean,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, disabled?: boolean,
|
||||
defaultValue?: string, disabled?: boolean,
|
||||
/**
|
||||
* Longer description of the input, likely shown in a tooltip
|
||||
*/
|
||||
description?: string, };
|
||||
|
||||
export type FormInputKeyValue = {
|
||||
export type FormInputKeyValue = {
|
||||
/**
|
||||
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||
*/
|
||||
name: string,
|
||||
name: string,
|
||||
/**
|
||||
* Whether this input is visible for the given configuration. Use this to
|
||||
* make branching forms.
|
||||
*/
|
||||
hidden?: boolean,
|
||||
hidden?: boolean,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
label?: string,
|
||||
/**
|
||||
* Visually hide the label of the input
|
||||
*/
|
||||
hideLabel?: boolean,
|
||||
hideLabel?: boolean,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, disabled?: boolean,
|
||||
defaultValue?: string, disabled?: boolean,
|
||||
/**
|
||||
* Longer description of the input, likely shown in a tooltip
|
||||
*/
|
||||
@@ -314,36 +314,36 @@ description?: string, };
|
||||
|
||||
export type FormInputMarkdown = { content: string, hidden?: boolean, };
|
||||
|
||||
export type FormInputSelect = {
|
||||
export type FormInputSelect = {
|
||||
/**
|
||||
* The options that will be available in the select input
|
||||
*/
|
||||
options: Array<FormInputSelectOption>,
|
||||
options: Array<FormInputSelectOption>,
|
||||
/**
|
||||
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||
*/
|
||||
name: string,
|
||||
name: string,
|
||||
/**
|
||||
* Whether this input is visible for the given configuration. Use this to
|
||||
* make branching forms.
|
||||
*/
|
||||
hidden?: boolean,
|
||||
hidden?: boolean,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
label?: string,
|
||||
/**
|
||||
* Visually hide the label of the input
|
||||
*/
|
||||
hideLabel?: boolean,
|
||||
hideLabel?: boolean,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, disabled?: boolean,
|
||||
defaultValue?: string, disabled?: boolean,
|
||||
/**
|
||||
* Longer description of the input, likely shown in a tooltip
|
||||
*/
|
||||
@@ -351,44 +351,44 @@ description?: string, };
|
||||
|
||||
export type FormInputSelectOption = { label: string, value: string, };
|
||||
|
||||
export type FormInputText = {
|
||||
export type FormInputText = {
|
||||
/**
|
||||
* Placeholder for the text input
|
||||
*/
|
||||
placeholder?: string | null,
|
||||
placeholder?: string | null,
|
||||
/**
|
||||
* Placeholder for the text input
|
||||
*/
|
||||
password?: boolean,
|
||||
password?: boolean,
|
||||
/**
|
||||
* Whether to allow newlines in the input, like a <textarea/>
|
||||
*/
|
||||
multiLine?: boolean, completionOptions?: Array<GenericCompletionOption>,
|
||||
multiLine?: boolean, completionOptions?: Array<GenericCompletionOption>,
|
||||
/**
|
||||
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||
*/
|
||||
name: string,
|
||||
name: string,
|
||||
/**
|
||||
* Whether this input is visible for the given configuration. Use this to
|
||||
* make branching forms.
|
||||
*/
|
||||
hidden?: boolean,
|
||||
hidden?: boolean,
|
||||
/**
|
||||
* Whether the user must fill in the argument
|
||||
*/
|
||||
optional?: boolean,
|
||||
optional?: boolean,
|
||||
/**
|
||||
* The label of the input
|
||||
*/
|
||||
label?: string,
|
||||
label?: string,
|
||||
/**
|
||||
* Visually hide the label of the input
|
||||
*/
|
||||
hideLabel?: boolean,
|
||||
hideLabel?: boolean,
|
||||
/**
|
||||
* The default value
|
||||
*/
|
||||
defaultValue?: string, disabled?: boolean,
|
||||
defaultValue?: string, disabled?: boolean,
|
||||
/**
|
||||
* Longer description of the input, likely shown in a tooltip
|
||||
*/
|
||||
@@ -474,7 +474,7 @@ export type ListOpenWorkspacesResponse = { workspaces: Array<WorkspaceInfo>, };
|
||||
|
||||
export type OpenExternalUrlRequest = { url: string, };
|
||||
|
||||
export type OpenWindowRequest = { url: string,
|
||||
export type OpenWindowRequest = { url: string,
|
||||
/**
|
||||
* Label for the window. If not provided, a random one will be generated.
|
||||
*/
|
||||
@@ -486,15 +486,15 @@ export type PromptFormRequest = { id: string, title: string, description?: strin
|
||||
|
||||
export type PromptFormResponse = { values: { [key in string]?: JsonPrimitive } | null, done?: boolean, };
|
||||
|
||||
export type PromptTextRequest = { id: string, title: string, label: string, description?: string, defaultValue?: string, placeholder?: string,
|
||||
export type PromptTextRequest = { id: string, title: string, label: string, description?: string, defaultValue?: string, placeholder?: string,
|
||||
/**
|
||||
* Text to add to the confirmation button
|
||||
*/
|
||||
confirmText?: string, password?: boolean,
|
||||
confirmText?: string, password?: boolean,
|
||||
/**
|
||||
* Text to add to the cancel button
|
||||
*/
|
||||
cancelText?: string,
|
||||
cancelText?: string,
|
||||
/**
|
||||
* Require the user to enter a non-empty value
|
||||
*/
|
||||
@@ -524,12 +524,12 @@ export type SetKeyValueResponse = {};
|
||||
|
||||
export type ShowToastRequest = { message: string, color?: Color, icon?: Icon, timeout?: number, };
|
||||
|
||||
export type TemplateFunction = { name: string, previewType?: TemplateFunctionPreviewType, description?: string,
|
||||
export type TemplateFunction = { name: string, previewType?: TemplateFunctionPreviewType, description?: string,
|
||||
/**
|
||||
* Also support alternative names. This is useful for not breaking existing
|
||||
* tags when changing the `name` property
|
||||
*/
|
||||
aliases?: Array<string>, args: Array<TemplateFunctionArg>,
|
||||
aliases?: Array<string>, args: Array<TemplateFunctionArg>,
|
||||
/**
|
||||
* A list of arg names to show in the inline preview. If not provided, none will be shown (for privacy reasons).
|
||||
*/
|
||||
@@ -546,23 +546,23 @@ export type TemplateRenderRequest = { data: JsonValue, purpose: RenderPurpose, }
|
||||
|
||||
export type TemplateRenderResponse = { data: JsonValue, };
|
||||
|
||||
export type Theme = {
|
||||
export type Theme = {
|
||||
/**
|
||||
* How the theme is identified. This should never be changed
|
||||
*/
|
||||
id: string,
|
||||
id: string,
|
||||
/**
|
||||
* The friendly name of the theme to be displayed to the user
|
||||
*/
|
||||
label: string,
|
||||
label: string,
|
||||
/**
|
||||
* Whether the theme will be used for dark or light appearance
|
||||
*/
|
||||
dark: boolean,
|
||||
dark: boolean,
|
||||
/**
|
||||
* The default top-level colors for the theme
|
||||
*/
|
||||
base: ThemeComponentColors,
|
||||
base: ThemeComponentColors,
|
||||
/**
|
||||
* Optionally override theme for individual UI components for more control
|
||||
*/
|
||||
|
||||
36
crates/yaak-plugins/bindings/gen_models.ts
generated
36
crates/yaak-plugins/bindings/gen_models.ts
generated
@@ -18,7 +18,12 @@ export type EditorKeymap = "default" | "vim" | "vscode" | "emacs";
|
||||
|
||||
export type EncryptedKey = { encryptedKey: string, };
|
||||
|
||||
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, parentModel: string, parentId: string | null, variables: Array<EnvironmentVariable>, color: string | null, sortPriority: number, };
|
||||
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, parentModel: string, parentId: string | null,
|
||||
/**
|
||||
* Variables defined in this environment scope.
|
||||
* Child environments override parent variables by name.
|
||||
*/
|
||||
variables: Array<EnvironmentVariable>, color: string | null, sortPriority: number, };
|
||||
|
||||
export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||
|
||||
@@ -34,9 +39,17 @@ export type GrpcEvent = { model: "grpc_event", id: string, createdAt: string, up
|
||||
|
||||
export type GrpcEventType = "info" | "error" | "client_message" | "server_message" | "connection_start" | "connection_end";
|
||||
|
||||
export type GrpcRequest = { model: "grpc_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authenticationType: string | null, authentication: Record<string, any>, description: string, message: string, metadata: Array<HttpRequestHeader>, method: string | null, name: string, service: string | null, sortPriority: number, url: string, };
|
||||
export type GrpcRequest = { model: "grpc_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authenticationType: string | null, authentication: Record<string, any>, description: string, message: string, metadata: Array<HttpRequestHeader>, method: string | null, name: string, service: string | null, sortPriority: number,
|
||||
/**
|
||||
* Server URL (http for plaintext or https for secure)
|
||||
*/
|
||||
url: string, };
|
||||
|
||||
export type HttpRequest = { model: "http_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record<string, any>, authenticationType: string | null, body: Record<string, any>, bodyType: string | null, description: string, headers: Array<HttpRequestHeader>, method: string, name: string, sortPriority: number, url: string, urlParameters: Array<HttpUrlParameter>, };
|
||||
export type HttpRequest = { model: "http_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record<string, any>, authenticationType: string | null, body: Record<string, any>, bodyType: string | null, description: string, headers: Array<HttpRequestHeader>, method: string, name: string, sortPriority: number, url: string,
|
||||
/**
|
||||
* URL parameters used for both path placeholders (`:id`) and query string entries.
|
||||
*/
|
||||
urlParameters: Array<HttpUrlParameter>, };
|
||||
|
||||
export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||
|
||||
@@ -55,11 +68,18 @@ export type HttpResponseHeader = { name: string, value: string, };
|
||||
|
||||
export type HttpResponseState = "initialized" | "connected" | "closed";
|
||||
|
||||
export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||
export type HttpUrlParameter = { enabled?: boolean,
|
||||
/**
|
||||
* Colon-prefixed parameters are treated as path parameters if they match, like `/users/:id`
|
||||
* Other entries are appended as query parameters
|
||||
*/
|
||||
name: string, value: string, id?: string, };
|
||||
|
||||
export type KeyValue = { model: "key_value", id: string, createdAt: string, updatedAt: string, key: string, namespace: string, value: string, };
|
||||
|
||||
export type Plugin = { model: "plugin", id: string, createdAt: string, updatedAt: string, checkedAt: string | null, directory: string, enabled: boolean, url: string | null, };
|
||||
export type Plugin = { model: "plugin", id: string, createdAt: string, updatedAt: string, checkedAt: string | null, directory: string, enabled: boolean, url: string | null, source: PluginSource, };
|
||||
|
||||
export type PluginSource = "bundled" | "filesystem" | "registry";
|
||||
|
||||
export type ProxySetting = { "type": "enabled", http: string, https: string, auth: ProxySettingAuth | null, bypass: string, disabled: boolean, } | { "type": "disabled" };
|
||||
|
||||
@@ -77,7 +97,11 @@ export type WebsocketEvent = { model: "websocket_event", id: string, createdAt:
|
||||
|
||||
export type WebsocketEventType = "binary" | "close" | "frame" | "open" | "ping" | "pong" | "text";
|
||||
|
||||
export type WebsocketRequest = { model: "websocket_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record<string, any>, authenticationType: string | null, description: string, headers: Array<HttpRequestHeader>, message: string, name: string, sortPriority: number, url: string, urlParameters: Array<HttpUrlParameter>, };
|
||||
export type WebsocketRequest = { model: "websocket_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record<string, any>, authenticationType: string | null, description: string, headers: Array<HttpRequestHeader>, message: string, name: string, sortPriority: number, url: string,
|
||||
/**
|
||||
* URL parameters used for both path placeholders (`:id`) and query string entries.
|
||||
*/
|
||||
urlParameters: Array<HttpUrlParameter>, };
|
||||
|
||||
export type Workspace = { model: "workspace", id: string, createdAt: string, updatedAt: string, authentication: Record<string, any>, authenticationType: string | null, description: string, headers: Array<HttpRequestHeader>, name: string, encryptionKeyChallenge: string | null, settingValidateCertificates: boolean, settingFollowRedirects: boolean, settingRequestTimeout: number, settingDnsOverrides: Array<DnsOverride>, };
|
||||
|
||||
|
||||
Reference in New Issue
Block a user