Skip to content

Commit b2a70d8

Browse files
committed
cli: prep 0.4.0 stable release
1 parent 64c626e commit b2a70d8

11 files changed

Lines changed: 276 additions & 158 deletions

File tree

crates-cli/yaak-cli/README.md

Lines changed: 53 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,66 @@
1-
# yaak-cli
1+
# Yaak CLI
22

3-
Command-line interface for Yaak.
3+
The `yaak` CLI for publishing plugins and creating/updating/sending requests.
44

5-
## Command Overview
5+
## Installation
66

7-
Current top-level commands:
7+
```sh
8+
npm install @yaakapp/cli
9+
```
10+
11+
## Agentic Workflows
12+
13+
The `yaak` CLI is primarily meant to be used by AI agents, and has the following features:
14+
15+
- `schema` subcommands to get the JSON Schema for any model (eg. `yaak request schema http`)
16+
- `--json '{...}'` input format to create and update data
17+
- `--verbose` mode for extracting debug info while sending requests
18+
- The ability to send entire workspaces and folders (Supports `--parallel` and `--fail-fast`)
19+
20+
### Example Prompts
21+
22+
Use the `yaak` CLI with agents like Claude or Codex to do useful things for you.
23+
24+
Here are some example prompts:
825

926
```text
10-
yaakcli send <request_id>
11-
yaakcli agent-help
12-
yaakcli workspace list
13-
yaakcli workspace schema [--pretty]
14-
yaakcli workspace show <workspace_id>
15-
yaakcli workspace create --name <name>
16-
yaakcli workspace create --json '{"name":"My Workspace"}'
17-
yaakcli workspace create '{"name":"My Workspace"}'
18-
yaakcli workspace update --json '{"id":"wk_abc","description":"Updated"}'
19-
yaakcli workspace delete <workspace_id> [--yes]
20-
yaakcli request list <workspace_id>
21-
yaakcli request show <request_id>
22-
yaakcli request send <request_id>
23-
yaakcli request create <workspace_id> --name <name> --url <url> [--method GET]
24-
yaakcli request create --json '{"workspaceId":"wk_abc","name":"Users","url":"https://api.example.com/users"}'
25-
yaakcli request create '{"workspaceId":"wk_abc","name":"Users","url":"https://api.example.com/users"}'
26-
yaakcli request update --json '{"id":"rq_abc","name":"Users v2"}'
27-
yaakcli request delete <request_id> [--yes]
28-
yaakcli folder list <workspace_id>
29-
yaakcli folder show <folder_id>
30-
yaakcli folder create <workspace_id> --name <name>
31-
yaakcli folder create --json '{"workspaceId":"wk_abc","name":"Auth"}'
32-
yaakcli folder create '{"workspaceId":"wk_abc","name":"Auth"}'
33-
yaakcli folder update --json '{"id":"fl_abc","name":"Auth v2"}'
34-
yaakcli folder delete <folder_id> [--yes]
35-
yaakcli environment list <workspace_id>
36-
yaakcli environment schema [--pretty]
37-
yaakcli environment show <environment_id>
38-
yaakcli environment create <workspace_id> --name <name>
39-
yaakcli environment create --json '{"workspaceId":"wk_abc","name":"Production"}'
40-
yaakcli environment create '{"workspaceId":"wk_abc","name":"Production"}'
41-
yaakcli environment update --json '{"id":"ev_abc","color":"#00ff00"}'
42-
yaakcli environment delete <environment_id> [--yes]
27+
Scan my API routes and create a workspace (using yaak cli) with
28+
all the requests needed for me to do manual testing?
4329
```
4430

45-
Global options:
46-
47-
- `--data-dir <path>`: use a custom data directory
48-
- `-e, --environment <id>`: environment to use during request rendering/sending
49-
- `-v, --verbose`: verbose send output (events and streamed response body)
50-
- `--log [level]`: enable CLI logging; optional level is `error|warn|info|debug|trace`
51-
52-
Notes:
53-
54-
- `send` is currently a shortcut for sending an HTTP request ID.
55-
- `delete` commands prompt for confirmation unless `--yes` is provided.
56-
- In non-interactive mode, `delete` commands require `--yes`.
57-
- `create` and `update` commands support `--json` and positional JSON shorthand.
58-
- For `create` commands, use one input mode at a time. Example: do not combine `<workspace_id>` with `--json`.
59-
- Template tags use `${[ ... ]}` syntax (for example `${[API_BASE_URL]}`), not `{{ ... }}`.
60-
- `update` uses JSON Merge Patch semantics (RFC 7386) for partial updates.
61-
62-
## Examples
63-
64-
```bash
65-
yaakcli workspace list
66-
yaakcli workspace create --name "My Workspace"
67-
yaakcli workspace show wk_abc
68-
yaakcli workspace update --json '{"id":"wk_abc","description":"Team workspace"}'
69-
yaakcli request list wk_abc
70-
yaakcli request show rq_abc
71-
yaakcli request create wk_abc --name "Users" --url "https://api.example.com/users"
72-
yaakcli request update --json '{"id":"rq_abc","name":"Users v2"}'
73-
yaakcli request send rq_abc -e ev_abc
74-
yaakcli request delete rq_abc --yes
75-
yaakcli folder create wk_abc --name "Auth"
76-
yaakcli folder update --json '{"id":"fl_abc","name":"Auth v2"}'
77-
yaakcli environment create wk_abc --name "Production"
78-
yaakcli environment update --json '{"id":"ev_abc","color":"#00ff00"}'
31+
```text
32+
Send all the GraphQL requests in my workspace
7933
```
8034

81-
## Roadmap
35+
## Description
36+
37+
Here's the current print of `yaak --help`
38+
39+
```text
40+
Yaak CLI - API client from the command line
41+
42+
Usage: yaak [OPTIONS] <COMMAND>
8243
83-
Planned command expansion (request schema and polymorphic send) is tracked in `PLAN.md`.
44+
Commands:
45+
auth Authentication commands
46+
plugin Plugin development and publishing commands
47+
send Send a request, folder, or workspace by ID
48+
workspace Workspace commands
49+
request Request commands
50+
folder Folder commands
51+
environment Environment commands
8452
85-
When command behavior changes, update this README and verify with:
53+
Options:
54+
--data-dir <DATA_DIR> Use a custom data directory
55+
-e, --environment <ENVIRONMENT> Environment ID to use for variable substitution
56+
-v, --verbose Enable verbose send output (events and streamed response body)
57+
--log [<LEVEL>] Enable CLI logging; optionally set level (error|warn|info|debug|trace) [possible values: error, warn, info, debug, trace]
58+
-h, --help Print help
59+
-V, --version Print version
8660
87-
```bash
88-
cargo run -q -p yaak-cli -- --help
89-
cargo run -q -p yaak-cli -- request --help
90-
cargo run -q -p yaak-cli -- workspace --help
91-
cargo run -q -p yaak-cli -- folder --help
92-
cargo run -q -p yaak-cli -- environment --help
61+
Agent Hints:
62+
- Template variable syntax is ${[ my_var ]}, not {{ ... }}
63+
- Template function syntax is ${[ namespace.my_func(a='aaa',b='bbb') ]}
64+
- View JSONSchema for models before creating or updating (eg. `yaak request schema http`)
65+
- Deletion requires confirmation (--yes for non-interactive environments)
9366
```

crates-cli/yaak-cli/src/cli.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,8 @@ pub struct SendArgs {
6868
/// Request, folder, or workspace ID
6969
pub id: String,
7070

71-
/// Execute requests sequentially (default)
72-
#[arg(long, conflicts_with = "parallel")]
73-
pub sequential: bool,
74-
7571
/// Execute requests in parallel
76-
#[arg(long, conflicts_with = "sequential")]
72+
#[arg(long)]
7773
pub parallel: bool,
7874

7975
/// Stop on first request failure when sending folders/workspaces
@@ -342,8 +338,8 @@ pub enum EnvironmentCommands {
342338
1) yaak environment create <workspace_id> --name <name>
343339
2) yaak environment create --json '{"workspaceId":"wk_abc","name":"Production"}'
344340
3) yaak environment create '{"workspaceId":"wk_abc","name":"Production"}'
345-
346-
Do not combine <workspace_id> with --json."#)]
341+
4) yaak environment create <workspace_id> --json '{"name":"Production"}'
342+
"#)]
347343
Create {
348344
/// Workspace ID for flag-based mode, or positional JSON payload shorthand
349345
#[arg(value_name = "WORKSPACE_ID_OR_JSON")]

crates-cli/yaak-cli/src/commands/environment.rs

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use crate::cli::{EnvironmentArgs, EnvironmentCommands};
22
use crate::context::CliContext;
33
use crate::utils::confirm::confirm_delete;
44
use crate::utils::json::{
5-
apply_merge_patch, is_json_shorthand, parse_optional_json, parse_required_json, require_id,
6-
validate_create_id,
5+
apply_merge_patch, is_json_shorthand, merge_workspace_id_arg, parse_optional_json,
6+
parse_required_json, require_id, validate_create_id,
77
};
88
use crate::utils::schema::append_agent_hints;
99
use schemars::schema_for;
@@ -34,18 +34,13 @@ pub fn run(ctx: &CliContext, args: EnvironmentArgs) -> i32 {
3434
}
3535

3636
fn schema(pretty: bool) -> CommandResult {
37-
let mut schema =
38-
serde_json::to_value(schema_for!(Environment)).map_err(|e| format!(
39-
"Failed to serialize environment schema: {e}"
40-
))?;
37+
let mut schema = serde_json::to_value(schema_for!(Environment))
38+
.map_err(|e| format!("Failed to serialize environment schema: {e}"))?;
4139
append_agent_hints(&mut schema);
4240

43-
let output = if pretty {
44-
serde_json::to_string_pretty(&schema)
45-
} else {
46-
serde_json::to_string(&schema)
47-
}
48-
.map_err(|e| format!("Failed to format environment schema JSON: {e}"))?;
41+
let output =
42+
if pretty { serde_json::to_string_pretty(&schema) } else { serde_json::to_string(&schema) }
43+
.map_err(|e| format!("Failed to format environment schema JSON: {e}"))?;
4944
println!("{output}");
5045
Ok(())
5146
}
@@ -83,17 +78,11 @@ fn create(
8378
name: Option<String>,
8479
json: Option<String>,
8580
) -> CommandResult {
86-
if json.is_some() && workspace_id.as_deref().is_some_and(|v| !is_json_shorthand(v)) {
87-
return Err(
88-
"environment create cannot combine workspace_id with --json payload".to_string()
89-
);
90-
}
81+
let json_shorthand =
82+
workspace_id.as_deref().filter(|v| is_json_shorthand(v)).map(str::to_owned);
83+
let workspace_id_arg = workspace_id.filter(|v| !is_json_shorthand(v));
9184

92-
let payload = parse_optional_json(
93-
json,
94-
workspace_id.clone().filter(|v| is_json_shorthand(v)),
95-
"environment create",
96-
)?;
85+
let payload = parse_optional_json(json, json_shorthand, "environment create")?;
9786

9887
if let Some(payload) = payload {
9988
if name.is_some() {
@@ -103,10 +92,11 @@ fn create(
10392
validate_create_id(&payload, "environment")?;
10493
let mut environment: Environment = serde_json::from_value(payload)
10594
.map_err(|e| format!("Failed to parse environment create JSON: {e}"))?;
106-
107-
if environment.workspace_id.is_empty() {
108-
return Err("environment create JSON requires non-empty \"workspaceId\"".to_string());
109-
}
95+
merge_workspace_id_arg(
96+
workspace_id_arg.as_deref(),
97+
&mut environment.workspace_id,
98+
"environment create",
99+
)?;
110100

111101
if environment.parent_model.is_empty() {
112102
environment.parent_model = "environment".to_string();
@@ -121,7 +111,7 @@ fn create(
121111
return Ok(());
122112
}
123113

124-
let workspace_id = workspace_id.ok_or_else(|| {
114+
let workspace_id = workspace_id_arg.ok_or_else(|| {
125115
"environment create requires workspace_id unless JSON payload is provided".to_string()
126116
})?;
127117
let name = name.ok_or_else(|| {

crates-cli/yaak-cli/src/commands/folder.rs

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use crate::cli::{FolderArgs, FolderCommands};
22
use crate::context::CliContext;
33
use crate::utils::confirm::confirm_delete;
44
use crate::utils::json::{
5-
apply_merge_patch, is_json_shorthand, parse_optional_json, parse_required_json, require_id,
6-
validate_create_id,
5+
apply_merge_patch, is_json_shorthand, merge_workspace_id_arg, parse_optional_json,
6+
parse_required_json, require_id, validate_create_id,
77
};
88
use yaak_models::models::Folder;
99
use yaak_models::util::UpdateSource;
@@ -58,28 +58,25 @@ fn create(
5858
name: Option<String>,
5959
json: Option<String>,
6060
) -> CommandResult {
61-
if json.is_some() && workspace_id.as_deref().is_some_and(|v| !is_json_shorthand(v)) {
62-
return Err("folder create cannot combine workspace_id with --json payload".to_string());
63-
}
61+
let json_shorthand =
62+
workspace_id.as_deref().filter(|v| is_json_shorthand(v)).map(str::to_owned);
63+
let workspace_id_arg = workspace_id.filter(|v| !is_json_shorthand(v));
6464

65-
let payload = parse_optional_json(
66-
json,
67-
workspace_id.clone().filter(|v| is_json_shorthand(v)),
68-
"folder create",
69-
)?;
65+
let payload = parse_optional_json(json, json_shorthand, "folder create")?;
7066

7167
if let Some(payload) = payload {
7268
if name.is_some() {
7369
return Err("folder create cannot combine --name with JSON payload".to_string());
7470
}
7571

7672
validate_create_id(&payload, "folder")?;
77-
let folder: Folder = serde_json::from_value(payload)
73+
let mut folder: Folder = serde_json::from_value(payload)
7874
.map_err(|e| format!("Failed to parse folder create JSON: {e}"))?;
79-
80-
if folder.workspace_id.is_empty() {
81-
return Err("folder create JSON requires non-empty \"workspaceId\"".to_string());
82-
}
75+
merge_workspace_id_arg(
76+
workspace_id_arg.as_deref(),
77+
&mut folder.workspace_id,
78+
"folder create",
79+
)?;
8380

8481
let created = ctx
8582
.db()
@@ -90,7 +87,7 @@ fn create(
9087
return Ok(());
9188
}
9289

93-
let workspace_id = workspace_id.ok_or_else(|| {
90+
let workspace_id = workspace_id_arg.ok_or_else(|| {
9491
"folder create requires workspace_id unless JSON payload is provided".to_string()
9592
})?;
9693
let name = name.ok_or_else(|| {

crates-cli/yaak-cli/src/commands/request.rs

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@ use crate::cli::{RequestArgs, RequestCommands, RequestSchemaType};
22
use crate::context::CliContext;
33
use crate::utils::confirm::confirm_delete;
44
use crate::utils::json::{
5-
apply_merge_patch, is_json_shorthand, parse_optional_json, parse_required_json, require_id,
6-
validate_create_id,
5+
apply_merge_patch, is_json_shorthand, merge_workspace_id_arg, parse_optional_json,
6+
parse_required_json, require_id, validate_create_id,
77
};
88
use crate::utils::schema::append_agent_hints;
99
use schemars::schema_for;
1010
use serde_json::{Map, Value, json};
1111
use std::collections::HashMap;
1212
use std::io::Write;
1313
use tokio::sync::mpsc;
14-
use yaak_http::sender::HttpResponseEvent as SenderHttpResponseEvent;
1514
use yaak::send::{SendHttpRequestByIdWithPluginsParams, send_http_request_by_id_with_plugins};
15+
use yaak_http::sender::HttpResponseEvent as SenderHttpResponseEvent;
1616
use yaak_models::models::{GrpcRequest, HttpRequest, WebsocketRequest};
1717
use yaak_models::queries::any_request::AnyRequest;
1818
use yaak_models::util::UpdateSource;
@@ -336,28 +336,25 @@ fn create(
336336
url: Option<String>,
337337
json: Option<String>,
338338
) -> CommandResult {
339-
if json.is_some() && workspace_id.as_deref().is_some_and(|v| !is_json_shorthand(v)) {
340-
return Err("request create cannot combine workspace_id with --json payload".to_string());
341-
}
339+
let json_shorthand =
340+
workspace_id.as_deref().filter(|v| is_json_shorthand(v)).map(str::to_owned);
341+
let workspace_id_arg = workspace_id.filter(|v| !is_json_shorthand(v));
342342

343-
let payload = parse_optional_json(
344-
json,
345-
workspace_id.clone().filter(|v| is_json_shorthand(v)),
346-
"request create",
347-
)?;
343+
let payload = parse_optional_json(json, json_shorthand, "request create")?;
348344

349345
if let Some(payload) = payload {
350346
if name.is_some() || method.is_some() || url.is_some() {
351347
return Err("request create cannot combine simple flags with JSON payload".to_string());
352348
}
353349

354350
validate_create_id(&payload, "request")?;
355-
let request: HttpRequest = serde_json::from_value(payload)
351+
let mut request: HttpRequest = serde_json::from_value(payload)
356352
.map_err(|e| format!("Failed to parse request create JSON: {e}"))?;
357-
358-
if request.workspace_id.is_empty() {
359-
return Err("request create JSON requires non-empty \"workspaceId\"".to_string());
360-
}
353+
merge_workspace_id_arg(
354+
workspace_id_arg.as_deref(),
355+
&mut request.workspace_id,
356+
"request create",
357+
)?;
361358

362359
let created = ctx
363360
.db()
@@ -368,7 +365,7 @@ fn create(
368365
return Ok(());
369366
}
370367

371-
let workspace_id = workspace_id.ok_or_else(|| {
368+
let workspace_id = workspace_id_arg.ok_or_else(|| {
372369
"request create requires workspace_id unless JSON payload is provided".to_string()
373370
})?;
374371
let name = name.unwrap_or_default();

0 commit comments

Comments
 (0)