Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ browser = [

[dependencies]
# CLI (optional — not needed for browser WASM library)
clap = { version = "4", features = ["derive"], optional = true }
clap = { version = "4", features = ["derive", "string"], optional = true }
clap_complete = { version = "4", optional = true }

# Async runtime (features selected by native/wasi feature flags)
Expand Down
1 change: 1 addition & 0 deletions docs/COMMANDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ pup <domain> <subgroup> <action> [options] # Nested commands
| fleet | agents (list, get, versions, tracers), deployments (list, get, configure, upgrade, cancel), schedules (list, get, create, update, delete, trigger), tracers (list), clusters (list), instrumented-pods (list) | src/commands/fleet.rs | ✅ |
| skills | list, install, path (positional `<platform>`: claude/cursor/codex/opencode/windsurf/gemini/pi/devin/all; `--name`, `--type`, `--project` for project-local scope; bundled skills include supplementary references) | src/commands/skills.rs | ✅ |
| runbooks | list, describe, run, import, validate | src/commands/runbooks.rs | ✅ |
| setup | (all arguments passed to AI Setup, run from npm with npx; needs Node.js 22+) | src/commands/setup.rs | ✅ |
| workflows | get, create, update, diff, delete, run, instances (list, get, cancel), connections (get, create, update, delete) | src/commands/workflows.rs | ✅ |
| investigations | list, get, trigger | src/commands/investigations.rs | ✅ |
| change-requests | create, get, update, create-branch, decisions (update, delete) | src/commands/change_management.rs | ✅ |
Expand Down
13 changes: 13 additions & 0 deletions src/commands/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,23 @@ pub fn returns_for(full_path: &str) -> serde_json::Value {
"traces search" => traces_search_returns(),
"metrics query" | "metrics search" => metrics_query_returns(),
"security findings schema" => findings_schema_returns(),
"setup" => setup_returns(),
_ => generic_returns(),
}
}

fn setup_returns() -> serde_json::Value {
serde_json::json!({
"documented": false,
"data": {"description": "No envelope and no API body: AI Setup's output is passed through to stdout and stderr."},
"notes": [
"Headless runs (agent mode, or no terminal) print AI_SETUP_PROGRESS lines and end with one AI_SETUP_RESULT line: `status=success product=<product> code=0 url=<next step>` or `status=error product=<product> code=<n> reason=\"<message>\"`.",
"The exit code is AI Setup's: 0 on success, non-zero on failure.",
"Needs Node.js 22+ and npx; without them pup prints how to install them and exits 1."
]
})
}

fn generic_returns() -> serde_json::Value {
serde_json::json!({
"documented": false,
Expand Down
6 changes: 5 additions & 1 deletion src/commands/alias.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,11 @@ pub fn delete(names: Vec<String>) -> Result<()> {
/// Core expansion logic, operating on an already-loaded alias map.
/// Exposed separately so unit tests can exercise it without touching the
/// filesystem.
fn apply_expansion(args: &[String], name: &str, aliases: &BTreeMap<String, String>) -> Vec<String> {
pub(crate) fn apply_expansion(
args: &[String],
name: &str,
aliases: &BTreeMap<String, String>,
) -> Vec<String> {
// Built-in commands cannot be shadowed by aliases.
#[cfg(not(target_arch = "wasm32"))]
if crate::extensions::is_builtin_command(name) {
Expand Down
9 changes: 5 additions & 4 deletions src/commands/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub async fn login(
scopes: Vec<String>,
callback_port: Option<u16>,
org_uuid: Option<&str>,
) -> Result<()> {
) -> Result<storage::SessionEntry> {
use crate::auth::{dcr, pkce};

let site = &cfg.site;
Expand Down Expand Up @@ -238,11 +238,12 @@ pub async fn login(
let saved_org_uuid = validated_dd_oid
.map(String::from)
.or_else(|| org_uuid.map(String::from));
storage::save_session(&storage::SessionEntry {
let session = storage::SessionEntry {
site: effective_site.to_string(),
org: saved_org.map(String::from),
org_uuid: saved_org_uuid,
})?;
};
storage::save_session(&session)?;

// Enforce the single-slot invariant: a bare login (no org) replaces any
// previous no-org session on a different site so that
Expand All @@ -259,7 +260,7 @@ pub async fn login(
eprintln!(" Access token expires: {expires_at}");
eprintln!(" Token stored in: {location}");

Ok(())
Ok(session)
}

/// Pick the site to key the exchanged token, session, and DCR credential under.
Expand Down
4 changes: 4 additions & 0 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ pub mod seats;
pub mod security;
pub mod service_catalog;
#[cfg(not(target_arch = "wasm32"))]
pub mod setup;
#[cfg(not(target_arch = "wasm32"))]
pub mod setup_help;
#[cfg(not(target_arch = "wasm32"))]
pub mod skills;
#[cfg(not(target_arch = "wasm32"))]
pub mod skills_remote;
Expand Down
Loading
Loading